Update gpu caps for valid sample counts.

Instead of query and maxSampleCount and using that to cap, we now have
each config store its supported values and when requested returns either
the next highest or equal supported value, or if non the max config supported.

Bug: skia:
Change-Id: I8802d44c13b3b1703ee54a7e69b82102d4b8dc2d
Reviewed-on: https://skia-review.googlesource.com/24302
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 0c227e3..ecf4398 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -636,7 +636,8 @@
             chosenSampleCount = 16;
         }
     }
-    return chosenSampleCount <= fGpu->caps()->maxSampleCount() ? chosenSampleCount : 0;
+    int supportedSampleCount = fGpu->caps()->getSampleCount(chosenSampleCount, config);
+    return chosenSampleCount <= supportedSampleCount ? supportedSampleCount : 0;
 }
 
 sk_sp<GrSurfaceContext> GrContextPriv::makeWrappedSurfaceContext(sk_sp<GrSurfaceProxy> proxy,
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index cab5d15..038ffef 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -143,7 +143,7 @@
         return nullptr;
     }
 
-    desc.fSampleCnt = SkTMin(desc.fSampleCnt, caps->maxSampleCount());
+    desc.fSampleCnt = caps->getSampleCount(desc.fSampleCnt, desc.fConfig);
     // Attempt to catch un- or wrongly initialized sample counts.
     SkASSERT(desc.fSampleCnt >= 0 && desc.fSampleCnt <= 64);
 
diff --git a/src/gpu/GrSurfaceProxy.cpp b/src/gpu/GrSurfaceProxy.cpp
index b073662..f300770 100644
--- a/src/gpu/GrSurfaceProxy.cpp
+++ b/src/gpu/GrSurfaceProxy.cpp
@@ -185,7 +185,7 @@
     }
 
     GrSurfaceDesc copyDesc = desc;
-    copyDesc.fSampleCnt = SkTMin(desc.fSampleCnt, caps->maxSampleCount());
+    copyDesc.fSampleCnt = caps->getSampleCount(desc.fSampleCnt, desc.fConfig);
 
     if (willBeRT) {
         // We know anything we instantiate later from this deferred path will be
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index 6d8e46d..af0a55a 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -308,11 +308,11 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-GrPixelConfig SkImageInfo2GrPixelConfig(const SkImageInfo& info, const GrCaps& caps) {
+GrPixelConfig SkImageInfo2GrPixelConfig(const SkColorType type, SkColorSpace* cs,
+                                        const GrCaps& caps) {
     // We intentionally ignore profile type for non-8888 formats. Anything we can't support
     // in hardware will be expanded to sRGB 8888 in GrUploadPixmapToTexture.
-    SkColorSpace* cs = info.colorSpace();
-    switch (info.colorType()) {
+    switch (type) {
         case kUnknown_SkColorType:
             return kUnknown_GrPixelConfig;
         case kAlpha_8_SkColorType:
@@ -336,6 +336,10 @@
     return kUnknown_GrPixelConfig;
 }
 
+GrPixelConfig SkImageInfo2GrPixelConfig(const SkImageInfo& info, const GrCaps& caps) {
+    return SkImageInfo2GrPixelConfig(info.colorType(), info.colorSpace(), caps);
+}
+
 bool GrPixelConfigToColorType(GrPixelConfig config, SkColorType* ctOut) {
     SkColorType ct;
     switch (config) {
diff --git a/src/gpu/SkGr.h b/src/gpu/SkGr.h
index 3ed82a8..1f7f2ad 100644
--- a/src/gpu/SkGr.h
+++ b/src/gpu/SkGr.h
@@ -151,6 +151,7 @@
 // Misc Sk to Gr type conversions
 
 GrSurfaceDesc GrImageInfoToSurfaceDesc(const SkImageInfo&, const GrCaps&);
+GrPixelConfig SkImageInfo2GrPixelConfig(const SkColorType, SkColorSpace*, const GrCaps& caps);
 GrPixelConfig SkImageInfo2GrPixelConfig(const SkImageInfo& info, const GrCaps& caps);
 
 bool GrPixelConfigToColorType(GrPixelConfig, SkColorType*);
diff --git a/src/gpu/gl/GrGLAssembleInterface.cpp b/src/gpu/gl/GrGLAssembleInterface.cpp
index d61b38b..64bed32 100644
--- a/src/gpu/gl/GrGLAssembleInterface.cpp
+++ b/src/gpu/gl/GrGLAssembleInterface.cpp
@@ -529,6 +529,11 @@
         GET_PROC(MemoryBarrierByRegion);
     }
 
+
+    if (glVer >= GR_GL_VER(4,2) || extensions.has("GL_ARB_internalformat_query")) {
+        GET_PROC(GetInternalformativ);
+    }
+
     interface->fStandard = kGL_GrGLStandard;
     interface->fExtensions.swap(&extensions);
 
@@ -951,6 +956,10 @@
         GET_PROC(MemoryBarrierByRegion);
     }
 
+    if (version >= GR_GL_VER(3,0)) {
+        GET_PROC(GetInternalformativ);
+    }
+
     interface->fStandard = kGLES_GrGLStandard;
     interface->fExtensions.swap(&extensions);
 
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index a7ec88e..766b438 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -2109,6 +2109,46 @@
         }
     }
 
+    bool hasInternalformatFunction = gli->fFunctions.fGetInternalformativ != nullptr;
+    for (int i = 0; i < kGrPixelConfigCnt; ++i) {
+        if (ConfigInfo::kRenderableWithMSAA_Flag & fConfigTable[i].fFlags) {
+            if (hasInternalformatFunction && // This check is temporary until chrome is updated
+                ((kGL_GrGLStandard == ctxInfo.standard() &&
+                 (ctxInfo.version() >= GR_GL_VER(4,2) ||
+                  ctxInfo.hasExtension("GL_ARB_internalformat_query"))) ||
+                (kGLES_GrGLStandard == ctxInfo.standard() && ctxInfo.version() >= GR_GL_VER(3,0)))) {
+                int count;
+                GrGLenum format = fConfigTable[i].fFormats.fInternalFormatRenderbuffer;
+                GR_GL_GetInternalformativ(gli, GR_GL_RENDERBUFFER, format, GR_GL_NUM_SAMPLE_COUNTS,
+                                          1, &count);
+                if (count) {
+                    int* temp = new int[count];
+                    GR_GL_GetInternalformativ(gli, GR_GL_RENDERBUFFER, format, GR_GL_SAMPLES, count,
+                                              temp);
+                    fConfigTable[i].fColorSampleCounts.setCount(count+1);
+                    // We initialize our supported values with 0 (no msaa) and reverse the order
+                    // returned by GL so that the array is ascending.
+                    fConfigTable[i].fColorSampleCounts[0] = 0;
+                    for (int j = 0; j < count; ++j) {
+                        fConfigTable[i].fColorSampleCounts[j+1] = temp[count - j - 1];
+                    }
+                    delete[] temp;
+                }
+            } else {
+                static const int kDefaultSamples[] = {0,1,2,4,8};
+                int count = SK_ARRAY_COUNT(kDefaultSamples);
+                for (; count > 0; --count) {
+                    if (kDefaultSamples[count-i] <= fMaxColorSampleCount) {
+                        break;
+                    }
+                }
+                if (count > 0) {
+                    fConfigTable[i].fColorSampleCounts.append(count, kDefaultSamples);
+                }
+            }
+        }
+    }
+
 #ifdef SK_DEBUG
     // Make sure we initialized everything.
     ConfigInfo defaultEntry;
@@ -2231,3 +2271,18 @@
         fUseDrawInsteadOfAllRenderTargetWrites = true;
     }
 }
+
+int GrGLCaps::getSampleCount(int requestedCount, GrPixelConfig config) const {
+    int count = fConfigTable[config].fColorSampleCounts.count();
+    if (!count || !this->isConfigRenderable(config, true)) {
+        return 0;
+    }
+
+    for (int i = 0; i < count; ++i) {
+        if (fConfigTable[config].fColorSampleCounts[i] >= requestedCount) {
+            return fConfigTable[config].fColorSampleCounts[i];
+        }
+    }
+    return fConfigTable[config].fColorSampleCounts[count-1];
+}
+
diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h
index 43da5f8..707c65e 100644
--- a/src/gpu/gl/GrGLCaps.h
+++ b/src/gpu/gl/GrGLCaps.h
@@ -112,6 +112,8 @@
     GrGLCaps(const GrContextOptions& contextOptions, const GrGLContextInfo& ctxInfo,
              const GrGLInterface* glInterface);
 
+    int getSampleCount(int requestedCount, GrPixelConfig config) const override;
+
     bool isConfigTexturable(GrPixelConfig config) const override {
         return SkToBool(fConfigTable[config].fFlags & ConfigInfo::kTextureable_Flag);
     }
@@ -529,7 +531,9 @@
         };
 
         // Index fStencilFormats.
-        int      fStencilFormatIndex;
+        int fStencilFormatIndex;
+
+        SkTDArray<int> fColorSampleCounts;
 
         enum {
             kVerifiedColorAttachment_Flag = 0x1,
diff --git a/src/gpu/gl/GrGLDefines.h b/src/gpu/gl/GrGLDefines.h
index a986ce8..b62c26b 100644
--- a/src/gpu/gl/GrGLDefines.h
+++ b/src/gpu/gl/GrGLDefines.h
@@ -1065,6 +1065,9 @@
 /* GL_EXT_geometry_shader */
 #define GR_GL_LINES_ADJACENCY                               0x000A
 
+/* GL_ARB_internalformat_query */
+#define GR_GL_NUM_SAMPLE_COUNTS                             0x9380
+
 /* EGL Defines */
 #define GR_EGL_NO_DISPLAY                                   ((GrEGLDisplay)0)
 #define GR_EGL_EXTENSIONS                                   0x3055
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index a73f97c..b310df4 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -572,7 +572,7 @@
     surfDesc.fWidth = backendTex.width();
     surfDesc.fHeight = backendTex.height();
     surfDesc.fConfig = backendTex.config();
-    surfDesc.fSampleCnt = SkTMin(sampleCnt, this->caps()->maxSampleCount());
+    surfDesc.fSampleCnt = this->caps()->getSampleCount(sampleCnt, backendTex.config());
     // FIXME:  this should be calling resolve_origin(), but Chrome code is currently
     // assuming the old behaviour, which is that backend textures are always
     // BottomLeft, even for non-RT's.  Once Chrome is fixed, change this to:
@@ -616,7 +616,7 @@
     desc.fFlags = kRenderTarget_GrSurfaceFlag;
     desc.fWidth = backendRT.width();
     desc.fHeight = backendRT.height();
-    desc.fSampleCnt = SkTMin(backendRT.sampleCnt(), this->caps()->maxSampleCount());
+    desc.fSampleCnt = this->caps()->getSampleCount(backendRT.sampleCnt(), backendRT.config());
     SkASSERT(kDefault_GrSurfaceOrigin != origin);
     desc.fOrigin = origin;
 
@@ -647,7 +647,7 @@
     surfDesc.fWidth = tex.width();
     surfDesc.fHeight = tex.height();
     surfDesc.fConfig = tex.config();
-    surfDesc.fSampleCnt = SkTMin(sampleCnt, this->caps()->maxSampleCount());
+    surfDesc.fSampleCnt = this->caps()->getSampleCount(sampleCnt, tex.config());
     // FIXME:  this should be calling resolve_origin(), but Chrome code is currently
     // assuming the old behaviour, which is that backend textures are always
     // BottomLeft, even for non-RT's.  Once Chrome is fixed, change this to:
diff --git a/src/gpu/gl/GrGLInterface.cpp b/src/gpu/gl/GrGLInterface.cpp
index a71127f..09d7a4f 100644
--- a/src/gpu/gl/GrGLInterface.cpp
+++ b/src/gpu/gl/GrGLInterface.cpp
@@ -808,5 +808,14 @@
         }
     }
 
+    // getInternalformativ was added in GL 4.2, ES 3.0, and with extension ARB_internalformat_query
+    if ((kGL_GrGLStandard == fStandard &&
+         (glVer >= GR_GL_VER(4,2) || fExtensions.has("GL_ARB_internalformat_query"))) ||
+        (kGLES_GrGLStandard == fStandard && glVer >= GR_GL_VER(3,0))) {
+        if (nullptr == fFunctions.fGetInternalformativ) {
+        //    RETURN_FALSE_INTERFACE;
+        }
+    }
+
     return true;
 }
diff --git a/src/gpu/gl/GrGLTestInterface.cpp b/src/gpu/gl/GrGLTestInterface.cpp
index 25d66a9..ef86ea7 100644
--- a/src/gpu/gl/GrGLTestInterface.cpp
+++ b/src/gpu/gl/GrGLTestInterface.cpp
@@ -324,4 +324,5 @@
     fFunctions.fPushDebugGroup = bind_to_member(this, &GrGLTestInterface::pushDebugGroup);
     fFunctions.fPopDebugGroup = bind_to_member(this, &GrGLTestInterface::popDebugGroup);
     fFunctions.fObjectLabel = bind_to_member(this, &GrGLTestInterface::objectLabel);
+    fFunctions.fGetInternalformativ = bind_to_member(this, &GrGLTestInterface::getInternalformativ);
 }
diff --git a/src/gpu/gl/GrGLTestInterface.h b/src/gpu/gl/GrGLTestInterface.h
index 636f493..69427f8 100644
--- a/src/gpu/gl/GrGLTestInterface.h
+++ b/src/gpu/gl/GrGLTestInterface.h
@@ -329,6 +329,7 @@
     virtual GrGLvoid pushDebugGroup(GrGLenum source, GrGLuint id, GrGLsizei length,  const GrGLchar * message) {}
     virtual GrGLvoid popDebugGroup() {}
     virtual GrGLvoid objectLabel(GrGLenum identifier, GrGLuint name, GrGLsizei length, const GrGLchar *label) {}
+    virtual GrGLvoid getInternalformativ(GrGLenum target, GrGLenum internalformat, GrGLenum pname, GrGLsizei bufSize, GrGLint *params) {}
 
 protected:
     // This must be called by leaf class
diff --git a/src/gpu/gl/GrGLUtil.h b/src/gpu/gl/GrGLUtil.h
index b798385..834b682 100644
--- a/src/gpu/gl/GrGLUtil.h
+++ b/src/gpu/gl/GrGLUtil.h
@@ -90,6 +90,12 @@
         GR_GL_CALL(gl, GetFramebufferAttachmentParameteriv(t, a, pname, p));   \
     } while (0)
 
+#define GR_GL_GetInternalformativ(gl, t, f, n, s, p)                           \
+    do {                                                                       \
+        *(p) = GR_GL_INIT_ZERO;                                                \
+        GR_GL_CALL(gl, GetInternalformativ(t, f, n, s, p));                    \
+    } while (0)
+
 #define GR_GL_GetNamedFramebufferAttachmentParameteriv(gl, fb, a, pname, p)          \
     do {                                                                             \
         *(p) = GR_GL_INIT_ZERO;                                                      \
diff --git a/src/gpu/mock/GrMockCaps.h b/src/gpu/mock/GrMockCaps.h
index 721ac83..b1d9a84 100644
--- a/src/gpu/mock/GrMockCaps.h
+++ b/src/gpu/mock/GrMockCaps.h
@@ -22,6 +22,9 @@
         fShaderCaps.reset(new GrShaderCaps(contextOptions));
         this->applyOptionsOverrides(contextOptions);
     }
+    int getSampleCount(int /*requestCount*/, GrPixelConfig /*config*/) const override {
+        return 0;
+    }
     bool isConfigTexturable(GrPixelConfig config) const override {
         return fOptions.fConfigOptions[config].fTexturable;
     }
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index db92617..909b4fb 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -340,6 +340,47 @@
     }
 }
 
+void GrVkCaps::ConfigInfo::initSampleCounts(const GrVkInterface* interface,
+                                            VkPhysicalDevice physDev,
+                                            VkFormat format) {
+    VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
+                              VK_IMAGE_USAGE_TRANSFER_DST_BIT |
+                              VK_IMAGE_USAGE_SAMPLED_BIT |
+                              VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
+    VkImageCreateFlags createFlags = GrVkFormatIsSRGB(format, nullptr)
+        ? VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT : 0;
+    VkImageFormatProperties properties;
+    GR_VK_CALL(interface, GetPhysicalDeviceImageFormatProperties(physDev,
+                                                                 format,
+                                                                 VK_IMAGE_TYPE_2D,
+                                                                 VK_IMAGE_TILING_OPTIMAL,
+                                                                 usage,
+                                                                 createFlags,
+                                                                 &properties));
+    VkSampleCountFlags flags = properties.sampleCounts;
+    if (flags & VK_SAMPLE_COUNT_1_BIT) {
+        fColorSampleCounts.push(0);
+    }
+    if (flags & VK_SAMPLE_COUNT_2_BIT) {
+        fColorSampleCounts.push(2);
+    }
+    if (flags & VK_SAMPLE_COUNT_4_BIT) {
+        fColorSampleCounts.push(4);
+    }
+    if (flags & VK_SAMPLE_COUNT_8_BIT) {
+        fColorSampleCounts.push(8);
+    }
+    if (flags & VK_SAMPLE_COUNT_16_BIT) {
+        fColorSampleCounts.push(16);
+    }
+    if (flags & VK_SAMPLE_COUNT_32_BIT) {
+        fColorSampleCounts.push(32);
+    }
+    if (flags & VK_SAMPLE_COUNT_64_BIT) {
+        fColorSampleCounts.push(64);
+    }
+}
+
 void GrVkCaps::ConfigInfo::init(const GrVkInterface* interface,
                                 VkPhysicalDevice physDev,
                                 VkFormat format) {
@@ -348,4 +389,22 @@
     GR_VK_CALL(interface, GetPhysicalDeviceFormatProperties(physDev, format, &props));
     InitConfigFlags(props.linearTilingFeatures, &fLinearFlags);
     InitConfigFlags(props.optimalTilingFeatures, &fOptimalFlags);
+    if (fOptimalFlags & kRenderable_Flag) {
+        this->initSampleCounts(interface, physDev, format);
+    }
 }
+
+int GrVkCaps::getSampleCount(int requestedCount, GrPixelConfig config) const {
+    int count = fConfigTable[config].fColorSampleCounts.count();
+    if (!count || !this->isConfigRenderable(config, true)) {
+        return 0;
+    }
+
+    for (int i = 0; i < count; ++i) {
+        if (fConfigTable[config].fColorSampleCounts[i] >= requestedCount) {
+            return fConfigTable[config].fColorSampleCounts[i];
+        }
+    }
+    return fConfigTable[config].fColorSampleCounts[count-1];
+}
+
diff --git a/src/gpu/vk/GrVkCaps.h b/src/gpu/vk/GrVkCaps.h
index f599d22..b085486 100644
--- a/src/gpu/vk/GrVkCaps.h
+++ b/src/gpu/vk/GrVkCaps.h
@@ -29,6 +29,8 @@
     GrVkCaps(const GrContextOptions& contextOptions, const GrVkInterface* vkInterface,
              VkPhysicalDevice device, uint32_t featureFlags, uint32_t extensionFlags);
 
+    int getSampleCount(int requestedCount, GrPixelConfig config) const override;
+
     bool isConfigTexturable(GrPixelConfig config) const override {
         return SkToBool(ConfigInfo::kTextureable_Flag & fConfigTable[config].fOptimalFlags);
     }
@@ -132,6 +134,7 @@
 
         void init(const GrVkInterface*, VkPhysicalDevice, VkFormat);
         static void InitConfigFlags(VkFormatFeatureFlags, uint16_t* flags);
+        void initSampleCounts(const GrVkInterface*, VkPhysicalDevice, VkFormat);
 
         enum {
             kTextureable_Flag = 0x1,
@@ -142,6 +145,8 @@
 
         uint16_t fOptimalFlags;
         uint16_t fLinearFlags;
+
+        SkTDArray<int> fColorSampleCounts;
     };
     ConfigInfo fConfigTable[kGrPixelConfigCnt];
 
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 17fccfb..cd0fc99 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -882,7 +882,7 @@
     surfDesc.fWidth = backendTex.width();
     surfDesc.fHeight = backendTex.height();
     surfDesc.fConfig = backendTex.config();
-    surfDesc.fSampleCnt = SkTMin(sampleCnt, this->caps()->maxSampleCount());
+    surfDesc.fSampleCnt = this->caps()->getSampleCount(sampleCnt, backendTex.config());
     bool renderTarget = SkToBool(flags & kRenderTarget_GrBackendTextureFlag);
     // In GL, Chrome assumes all textures are BottomLeft
     // In VK, we don't have this restriction
@@ -948,7 +948,7 @@
     desc.fConfig = tex.config();
     desc.fWidth = tex.width();
     desc.fHeight = tex.height();
-    desc.fSampleCnt = SkTMin(sampleCnt, this->caps()->maxSampleCount());
+    desc.fSampleCnt = this->caps()->getSampleCount(sampleCnt, tex.config());
 
     desc.fOrigin = resolve_origin(origin);