Add SkSurfaceCharacterization::isCompatible

Although the main change in this CL is the addition of GrCaps::areColorTypeAndFormatCompatible.

This is split out of:

https://skia-review.googlesource.com/c/skia/+/222781 (Add bridge between GrContext::createBackendTexture and SkSurface::MakeFromBackendTexture)

Change-Id: I2e50fff91eb07fb1358840e1a4a76dc138a2f195
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/223932
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/include/core/SkSurfaceCharacterization.h b/include/core/SkSurfaceCharacterization.h
index 774b0b8..97b72ab 100644
--- a/include/core/SkSurfaceCharacterization.h
+++ b/include/core/SkSurfaceCharacterization.h
@@ -82,6 +82,9 @@
     sk_sp<SkColorSpace> refColorSpace() const { return fImageInfo.refColorSpace(); }
     const SkSurfaceProps& surfaceProps()const { return fSurfaceProps; }
 
+    // Is the provided backend texture compatible with this surface characterization?
+    bool isCompatible(const GrBackendTexture&) const;
+
 private:
     friend class SkSurface_Gpu; // for 'set' & 'config'
     friend class GrVkSecondaryCBDrawContext; // for 'set' & 'config'
diff --git a/include/private/GrTypesPriv.h b/include/private/GrTypesPriv.h
index bcf6c24..6a0902f 100644
--- a/include/private/GrTypesPriv.h
+++ b/include/private/GrTypesPriv.h
@@ -1392,9 +1392,9 @@
     return GrPixelConfigToColorTypeAndEncoding(config, &bogusEncoded);
 }
 
-static constexpr GrPixelConfig GrColorTypeToPixelConfig(GrColorType config,
+static constexpr GrPixelConfig GrColorTypeToPixelConfig(GrColorType colorType,
                                                         GrSRGBEncoded srgbEncoded) {
-    switch (config) {
+    switch (colorType) {
         case GrColorType::kUnknown:
             return kUnknown_GrPixelConfig;
         case GrColorType::kAlpha_8:
diff --git a/src/core/SkSurfaceCharacterization.cpp b/src/core/SkSurfaceCharacterization.cpp
index a73c6e7..a051547 100644
--- a/src/core/SkSurfaceCharacterization.cpp
+++ b/src/core/SkSurfaceCharacterization.cpp
@@ -49,4 +49,58 @@
                                      fVulkanSecondaryCBCompatible, fSurfaceProps);
 }
 
+bool SkSurfaceCharacterization::isCompatible(const GrBackendTexture& backendTex) const {
+    if (!this->isValid() || !backendTex.isValid()) {
+        return false;
+    }
+
+    const GrCaps* caps = fContextInfo->priv().caps();
+
+    // TODO: remove this block involving the pixel config
+    {
+        GrPixelConfig config = caps->getConfigFromBackendFormat(backendTex.getBackendFormat(),
+                                                                this->colorType());
+        if (GrPixelConfig::kUnknown_GrPixelConfig == config) {
+            return false;
+        }
+
+        if (this->config() != config) {
+            return false;
+        }
+    }
+
+    if (this->usesGLFBO0()) {
+        // It is a backend texture so can't be wrapping FBO0
+        return false;
+    }
+
+    if (this->vulkanSecondaryCBCompatible()) {
+        return false;
+    }
+
+    int maxColorSamples = caps->maxRenderTargetSampleCount(this->colorType(),
+                                                           backendTex.getBackendFormat());
+    if (0 == maxColorSamples) {
+        return false;  // backendTex isn't renderable
+    }
+
+    if (this->isMipMapped() && !backendTex.hasMipMaps()) {
+        // backend texture is allowed to have mipmaps even if the characterization doesn't require
+        // them.
+        return false;
+    }
+
+    if (!caps->areColorTypeAndFormatCompatible(this->colorType(), backendTex.getBackendFormat())) {
+        return false;
+    }
+
+    if (this->width() != backendTex.width() || this->height() != backendTex.height()) {
+        return false;
+    }
+
+    // TODO: need to check protected status here
+    return true;
+}
+
+
 #endif
diff --git a/src/gpu/GrCaps.h b/src/gpu/GrCaps.h
index 67e2209..275fc79 100644
--- a/src/gpu/GrCaps.h
+++ b/src/gpu/GrCaps.h
@@ -349,6 +349,8 @@
     virtual GrPixelConfig validateBackendRenderTarget(const GrBackendRenderTarget&,
                                                       SkColorType) const = 0;
 
+    virtual bool areColorTypeAndFormatCompatible(SkColorType ct, const GrBackendFormat&) const = 0;
+
     // TODO: replace validateBackendRenderTarget with calls to getConfigFromBackendFormat?
     // TODO: it seems like we could pass the full SkImageInfo and validate its colorSpace too
     // Returns kUnknown if a valid config could not be determined.
diff --git a/src/gpu/GrContextThreadSafeProxy.cpp b/src/gpu/GrContextThreadSafeProxy.cpp
index 8da3afe..8bf5afb 100644
--- a/src/gpu/GrContextThreadSafeProxy.cpp
+++ b/src/gpu/GrContextThreadSafeProxy.cpp
@@ -38,6 +38,8 @@
         return SkSurfaceCharacterization(); // return an invalid characterization
     }
 
+    SkASSERT(isTextureable || !isMipMapped);
+
     if (GrBackendApi::kOpenGL != backendFormat.backend() && willUseGLFBO0) {
         // The willUseGLFBO0 flags can only be used for a GL backend.
         return SkSurfaceCharacterization(); // return an invalid characterization
diff --git a/src/gpu/GrProxyProvider.cpp b/src/gpu/GrProxyProvider.cpp
index cb2cefa..c7d4de9 100644
--- a/src/gpu/GrProxyProvider.cpp
+++ b/src/gpu/GrProxyProvider.cpp
@@ -422,8 +422,8 @@
         // SkColorType are correct.
         return true;
     }
-    GrPixelConfig testConfig = caps->getConfigFromBackendFormat(format, colorType);
-    return testConfig != kUnknown_GrPixelConfig;
+
+    return caps->areColorTypeAndFormatCompatible(colorType, format);
 }
 #endif
 
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index a1dfca6..cba9277 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -3621,6 +3621,16 @@
     return validate_sized_format(fbInfo.fFormat, ct, fStandard);
 }
 
+bool GrGLCaps::areColorTypeAndFormatCompatible(SkColorType ct,
+                                               const GrBackendFormat& format) const {
+    const GrGLenum* glFormat = format.getGLFormat();
+    if (!glFormat) {
+        return false;
+    }
+
+    return kUnknown_GrPixelConfig != validate_sized_format(*glFormat, ct, fStandard);
+}
+
 GrPixelConfig GrGLCaps::getConfigFromBackendFormat(const GrBackendFormat& format,
                                                    SkColorType ct) const {
     const GrGLenum* glFormat = format.getGLFormat();
diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h
index d9cb6e6..c3e8918 100644
--- a/src/gpu/gl/GrGLCaps.h
+++ b/src/gpu/gl/GrGLCaps.h
@@ -427,6 +427,8 @@
     GrPixelConfig validateBackendRenderTarget(const GrBackendRenderTarget&,
                                               SkColorType) const override;
 
+    bool areColorTypeAndFormatCompatible(SkColorType, const GrBackendFormat&) const override;
+
     GrPixelConfig getConfigFromBackendFormat(const GrBackendFormat&, SkColorType) const override;
     GrPixelConfig getYUVAConfigFromBackendFormat(const GrBackendFormat&) const override;
 
diff --git a/src/gpu/mock/GrMockCaps.h b/src/gpu/mock/GrMockCaps.h
index ff39757..b4b005f 100644
--- a/src/gpu/mock/GrMockCaps.h
+++ b/src/gpu/mock/GrMockCaps.h
@@ -124,6 +124,84 @@
         return kUnknown_GrPixelConfig;
     }
 
+    bool areColorTypeAndFormatCompatible(SkColorType ct,
+                                         const GrBackendFormat& format) const override {
+        const GrPixelConfig* mockFormat = format.getMockFormat();
+        if (!mockFormat) {
+            return kUnknown_GrPixelConfig;
+        }
+
+        switch (ct) {
+            case kUnknown_SkColorType:
+                return false;
+            case kAlpha_8_SkColorType:
+                if (kAlpha_8_GrPixelConfig == *mockFormat ||
+                    kAlpha_8_as_Alpha_GrPixelConfig == *mockFormat ||
+                    kAlpha_8_as_Red_GrPixelConfig == *mockFormat) {
+                    return true;
+                }
+                break;
+            case kRGB_565_SkColorType:
+                if (kRGB_565_GrPixelConfig == *mockFormat) {
+                    return true;
+                }
+                break;
+            case kARGB_4444_SkColorType:
+                if (kRGBA_4444_GrPixelConfig == *mockFormat) {
+                    return true;
+                }
+                break;
+            case kRGBA_8888_SkColorType:
+                if (kRGBA_8888_GrPixelConfig == *mockFormat ||
+                    kSRGBA_8888_GrPixelConfig == *mockFormat) {
+                    return true;
+                }
+                break;
+            case kRGB_888x_SkColorType:
+                if (kRGB_888X_GrPixelConfig == *mockFormat ||
+                    kRGB_888_GrPixelConfig == *mockFormat) {
+                    return true;
+                }
+                break;
+            case kBGRA_8888_SkColorType:
+                if (kBGRA_8888_GrPixelConfig == *mockFormat) {
+                    return true;
+                }
+                break;
+            case kRGBA_1010102_SkColorType:
+                if (kRGBA_1010102_GrPixelConfig == *mockFormat) {
+                    return true;
+                }
+                break;
+            case kRGB_101010x_SkColorType:
+                return false;
+            case kGray_8_SkColorType:
+                if (kGray_8_GrPixelConfig == *mockFormat ||
+                    kGray_8_as_Lum_GrPixelConfig == *mockFormat ||
+                    kGray_8_as_Red_GrPixelConfig == *mockFormat) {
+                    return true;
+                }
+                break;
+            case kRGBA_F16Norm_SkColorType:
+                if (kRGBA_half_Clamped_GrPixelConfig == *mockFormat) {
+                    return true;
+                }
+                break;
+            case kRGBA_F16_SkColorType:
+                if (kRGBA_half_GrPixelConfig == *mockFormat) {
+                    return true;
+                }
+                break;
+            case kRGBA_F32_SkColorType:
+                if (kRGBA_float_GrPixelConfig == *mockFormat) {
+                    return true;
+                }
+                break;
+        }
+
+        return false;
+    }
+
     GrPixelConfig getConfigFromBackendFormat(const GrBackendFormat& format,
                                              SkColorType ct) const override {
         const GrPixelConfig* mockFormat = format.getMockFormat();
diff --git a/src/gpu/mtl/GrMtlCaps.h b/src/gpu/mtl/GrMtlCaps.h
index adebbce..8de71ce 100644
--- a/src/gpu/mtl/GrMtlCaps.h
+++ b/src/gpu/mtl/GrMtlCaps.h
@@ -67,6 +67,8 @@
     GrPixelConfig validateBackendRenderTarget(const GrBackendRenderTarget&,
                                               SkColorType) const override;
 
+    bool areColorTypeAndFormatCompatible(SkColorType, const GrBackendFormat&) const override;
+
     GrPixelConfig getConfigFromBackendFormat(const GrBackendFormat&, SkColorType) const override;
 
     GrPixelConfig getYUVAConfigFromBackendFormat(const GrBackendFormat&) const override;
diff --git a/src/gpu/mtl/GrMtlCaps.mm b/src/gpu/mtl/GrMtlCaps.mm
index dc3469a..b662578 100644
--- a/src/gpu/mtl/GrMtlCaps.mm
+++ b/src/gpu/mtl/GrMtlCaps.mm
@@ -566,6 +566,17 @@
     return validate_sized_format(texture.pixelFormat, ct);
 }
 
+bool GrMtlCaps::areColorTypeAndFormatCompatible(SkColorType ct,
+                                                const GrBackendFormat& format) const {
+    const GrMTLPixelFormat* mtlFormat = format.getMtlFormat();
+    if (!mtlFormat) {
+        return false;
+    }
+
+    return kUnknown_GrPixelConfig != validate_sized_format(*mtlFormat, ct);
+}
+
+
 GrPixelConfig GrMtlCaps::getConfigFromBackendFormat(const GrBackendFormat& format,
                                                     SkColorType ct) const {
     const GrMTLPixelFormat* mtlFormat = format.getMtlFormat();
diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp
index 7d28cd8..aa4bac3 100644
--- a/src/gpu/vk/GrVkCaps.cpp
+++ b/src/gpu/vk/GrVkCaps.cpp
@@ -1026,6 +1026,18 @@
     return validate_image_info(imageInfo.fFormat, ct, imageInfo.fYcbcrConversionInfo.isValid());
 }
 
+bool GrVkCaps::areColorTypeAndFormatCompatible(SkColorType ct,
+                                               const GrBackendFormat& format) const {
+    const VkFormat* vkFormat = format.getVkFormat();
+    const GrVkYcbcrConversionInfo* ycbcrInfo = format.getVkYcbcrConversionInfo();
+    if (!vkFormat || !ycbcrInfo) {
+        return false;
+    }
+
+    return kUnknown_GrPixelConfig != validate_image_info(*vkFormat, ct, ycbcrInfo->isValid());
+}
+
+
 GrPixelConfig GrVkCaps::getConfigFromBackendFormat(const GrBackendFormat& format,
                                                    SkColorType ct) const {
     const VkFormat* vkFormat = format.getVkFormat();
diff --git a/src/gpu/vk/GrVkCaps.h b/src/gpu/vk/GrVkCaps.h
index c859121..771d8e9 100644
--- a/src/gpu/vk/GrVkCaps.h
+++ b/src/gpu/vk/GrVkCaps.h
@@ -164,6 +164,8 @@
     GrPixelConfig validateBackendRenderTarget(const GrBackendRenderTarget&,
                                               SkColorType) const override;
 
+    bool areColorTypeAndFormatCompatible(SkColorType, const GrBackendFormat&) const override;
+
     GrPixelConfig getConfigFromBackendFormat(const GrBackendFormat&, SkColorType) const override;
     GrPixelConfig getYUVAConfigFromBackendFormat(const GrBackendFormat&) const override;
 
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
index cc1b978..5a6e388 100644
--- a/src/image/SkSurface_Gpu.cpp
+++ b/src/image/SkSurface_Gpu.cpp
@@ -351,6 +351,10 @@
         return nullptr;
     }
 
+    if (c.vulkanSecondaryCBCompatible()) {
+        return nullptr;
+    }
+
     const GrBackendFormat format = caps->getBackendFormatFromColorType(c.colorType());
     if (!format.isValid()) {
         return nullptr;
diff --git a/tests/DeferredDisplayListTest.cpp b/tests/DeferredDisplayListTest.cpp
index 6a3bdbf..e5c1f84 100644
--- a/tests/DeferredDisplayListTest.cpp
+++ b/tests/DeferredDisplayListTest.cpp
@@ -113,10 +113,12 @@
         case 9:
             if (GrBackendApi::kOpenGL == fBackend) {
                 fUsesGLFBO0 = true;
+                fShouldCreateMipMaps = false; // needs to changed in tandem w/ textureability
                 fIsTextureable = false;
             }
             break;
         case 10:
+            fShouldCreateMipMaps = false; // needs to changed in tandem w/ textureability
             fIsTextureable = false;
             break;
         }
@@ -199,6 +201,10 @@
             return nullptr;
         }
 
+        // Even if a characterization couldn't be constructed we want to soldier on to make
+        // sure that surface creation will/would've also failed
+        SkASSERT(!c.isValid() || c.isCompatible(*backend));
+
         sk_sp<SkSurface> surface;
         if (!fIsTextureable) {
             // Create a surface w/ the current parameters but make it non-textureable
@@ -212,10 +218,12 @@
         }
 
         if (!surface) {
+            SkASSERT(!c.isValid());
             this->cleanUpBackEnd(context, *backend);
             return nullptr;
         }
 
+        SkASSERT(c.isValid());
         SkASSERT(surface->isCompatible(c));
         return surface;
     }
@@ -409,6 +417,7 @@
     {
         GrBackendTexture backend;
         SurfaceParameters params(context->backend());
+        params.setShouldCreateMipMaps(false);
         params.setTextureable(false);
 
         sk_sp<SkSurface> s = params.make(context, &backend);
@@ -480,10 +489,11 @@
     for (bool textureability : { true, false }) {
         std::unique_ptr<SkDeferredDisplayList> ddl;
 
-        // First, create a DDL w/o textureability. TODO: once we have reusable DDLs, move this
-        // outside of the loop.
+        // First, create a DDL w/o textureability (and thus no mipmaps). TODO: once we have
+        // reusable DDLs, move this outside of the loop.
         {
             SurfaceParameters params(context->backend());
+            params.setShouldCreateMipMaps(false);
             params.setTextureable(false);
 
             ddl = params.createDDL(context);
@@ -492,6 +502,7 @@
 
         // Then verify it can draw into either flavor of destination
         SurfaceParameters params(context->backend());
+        params.setShouldCreateMipMaps(textureability);
         params.setTextureable(textureability);
 
         GrBackendTexture backend;
@@ -543,6 +554,7 @@
         }
 
         REPORTER_ASSERT(reporter, c.isValid());
+        REPORTER_ASSERT(reporter, c.isCompatible(backend));
         REPORTER_ASSERT(reporter, s->isCompatible(c));
         // Note that we're leaving 'backend' live here
     }