Direct backend object allocation API

Change-Id: Ie8733a4db8061c67678a41cf239ce4407e967df7
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/212965
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index a0b3e17..ce2fcdd 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1643,6 +1643,9 @@
   import("gn/tests.gni")
   test_lib("tests") {
     sources = tests_sources + pathops_tests_sources
+    if (skia_use_metal) {
+      sources += metal_tests_sources
+    }
     if (!skia_enable_fontmgr_android) {
       sources -= [ "//tests/FontMgrAndroidParserTest.cpp" ]
     }
diff --git a/gn/tests.gni b/gn/tests.gni
index e0073f2..160a016 100644
--- a/gn/tests.gni
+++ b/gn/tests.gni
@@ -15,6 +15,7 @@
   "$_tests/ApplyGammaTest.cpp",
   "$_tests/ArenaAllocTest.cpp",
   "$_tests/AsADashTest.cpp",
+  "$_tests/BackendAllocationTest.cpp",
   "$_tests/BadIcoTest.cpp",
   "$_tests/BitSetTest.cpp",
   "$_tests/BitmapCopyTest.cpp",
@@ -305,6 +306,8 @@
   "$_tests/YUVTest.cpp",
 ]
 
+metal_tests_sources = [ "$_tests/MtlBackendAllocationTest.mm" ]
+
 pathops_tests_sources = [
   "$_tests/PathOpsAngleIdeas.cpp",
   "$_tests/PathOpsAngleTest.cpp",
diff --git a/src/gpu/GrContextPriv.cpp b/src/gpu/GrContextPriv.cpp
index 09b5327..816a7dc 100644
--- a/src/gpu/GrContextPriv.cpp
+++ b/src/gpu/GrContextPriv.cpp
@@ -764,3 +764,58 @@
     fContext->drawingManager()->testingOnly_removeOnFlushCallbackObject(cb);
 }
 #endif
+
+//////////////////////////////////////////////////////////////////////////////
+GrBackendTexture GrContextPriv::createBackendTexture(int width, int height,
+                                                     GrBackendFormat backendFormat,
+                                                     GrMipMapped mipMapped,
+                                                     GrRenderable renderable) {
+    if (!fContext->asDirectContext()) {
+        return GrBackendTexture();
+    }
+
+    if (this->abandoned()) {
+        return GrBackendTexture();
+    }
+
+    if (!backendFormat.isValid()) {
+        return GrBackendTexture();
+    }
+
+    GrGpu* gpu = fContext->fGpu.get();
+
+    return gpu->createTestingOnlyBackendTexture(width, height, backendFormat,
+                                                mipMapped, renderable,
+                                                nullptr, 0);
+}
+
+GrBackendTexture GrContextPriv::createBackendTexture(int width, int height,
+                                                     SkColorType colorType,
+                                                     GrMipMapped mipMapped,
+                                                     GrRenderable renderable) {
+    if (!fContext->asDirectContext()) {
+        return GrBackendTexture();
+    }
+
+    if (this->abandoned()) {
+        return GrBackendTexture();
+    }
+
+    GrBackendFormat format = fContext->caps()->getBackendFormatFromColorType(colorType);
+    if (!format.isValid()) {
+        return GrBackendTexture();
+    }
+
+    return this->createBackendTexture(width, height, format, mipMapped, renderable);
+}
+
+void GrContextPriv::deleteBackendTexture(GrBackendTexture backendTex) {
+    if (this->abandoned() || !backendTex.isValid()) {
+        return;
+    }
+
+    GrGpu* gpu = fContext->fGpu.get();
+
+    gpu->deleteTestingOnlyBackendTexture(backendTex);
+}
+
diff --git a/src/gpu/GrContextPriv.h b/src/gpu/GrContextPriv.h
index 86af1f3..9543ffc 100644
--- a/src/gpu/GrContextPriv.h
+++ b/src/gpu/GrContextPriv.h
@@ -292,6 +292,34 @@
     void testingOnly_flushAndRemoveOnFlushCallbackObject(GrOnFlushCallbackObject*);
 #endif
 
+   /*
+     * The explicitly allocated backend texture API allows clients to use Skia to create backend
+     * objects outside of Skia proper (i.e., Skia's caching system will not know about them.)
+     *
+     * It is the client's responsibility to delete all these objects (using deleteBackendTexture)
+     * before deleting the GrContext used to create them. Additionally, clients should only
+     * delete these objects on the thread for which that GrContext is active.
+     *
+     * Additionally, the client is responsible for ensuring synchronization between different uses
+     * of the backend object.
+     */
+
+    // If possible, create an uninitialized backend texture. The client should ensure that the
+    // returned backend texture is valid.
+    GrBackendTexture createBackendTexture(int width, int height,
+                                          GrBackendFormat,
+                                          GrMipMapped, GrRenderable);
+
+    // If possible, create an uninitialized backend texture. The client should ensure that the
+    // returned backend texture is valid.
+    // If successful, the created backend texture will be compatible with the provided
+    // SkColorType.
+    GrBackendTexture createBackendTexture(int width, int height,
+                                          SkColorType,
+                                          GrMipMapped, GrRenderable);
+
+    void deleteBackendTexture(GrBackendTexture);
+
 private:
     explicit GrContextPriv(GrContext* context) : fContext(context) {}
     GrContextPriv(const GrContextPriv&); // unimpl
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index d235c77..e0f768c 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -3198,7 +3198,7 @@
             }
             break;
     }
-    SkDebugf("Unknown pixel config %d\n", format);
+    SkDebugf("Unknown pixel config 0x%x\n", format);
     return kUnknown_GrPixelConfig;
 }
 
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index c52da42..f177d18 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -1535,11 +1535,11 @@
         return false;
     }
 
-    if (texturable && !fVkCaps->isConfigTexturable(config)) {
+    if (texturable && !fVkCaps->isFormatTexturable(vkFormat)) {
         return false;
     }
 
-    if (renderable && !fVkCaps->isConfigRenderable(config)) {
+    if (renderable && !fVkCaps->isFormatRenderable(vkFormat)) {
         return false;
     }
 
diff --git a/tests/BackendAllocationTest.cpp b/tests/BackendAllocationTest.cpp
new file mode 100644
index 0000000..16b3d85
--- /dev/null
+++ b/tests/BackendAllocationTest.cpp
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/gpu/GrContext.h"
+#include "src/gpu/GrContextPriv.h"
+#include "include/core/SkSurface.h"
+#include "tests/Test.h"
+
+// Test wrapping of GrBackendObjects in SkSurfaces and SkImages
+void test_wrapping(GrContext* context, skiatest::Reporter* reporter,
+                   std::function<GrBackendTexture (GrContext*, GrRenderable)> createMtd,
+                   SkColorType colorType, GrRenderable renderable) {
+    GrResourceCache* cache = context->priv().getResourceCache();
+
+    const int initialCount = cache->getResourceCount();
+
+    GrBackendTexture t = createMtd(context, renderable);
+    if (!t.isValid()) {
+        ERRORF(reporter, "Couldn't create backendTexture for colorType %d renderable %s\n",
+               colorType,
+               GrRenderable::kYes == renderable ? "yes" : "no");
+        return;
+    }
+    // Skia proper should know nothing about the new backend object
+    REPORTER_ASSERT(reporter, initialCount == cache->getResourceCount());
+
+    if (kUnknown_SkColorType != colorType) {
+        if (GrRenderable::kYes == renderable) {
+            sk_sp<SkSurface> s = SkSurface::MakeFromBackendTexture(context,
+                                                                   t,
+                                                                   kTopLeft_GrSurfaceOrigin,
+                                                                   0,
+                                                                   colorType,
+                                                                   nullptr, nullptr);
+            if (!s) {
+                ERRORF(reporter, "Couldn't make surface from backendTexture for colorType %d\n",
+                       colorType);
+            } else {
+                REPORTER_ASSERT(reporter, initialCount+1 == cache->getResourceCount());
+            }
+        }
+
+        {
+            sk_sp<SkImage> i = SkImage::MakeFromTexture(context,
+                                                        t,
+                                                        kTopLeft_GrSurfaceOrigin,
+                                                        colorType,
+                                                        kPremul_SkAlphaType,
+                                                        nullptr);
+            if (!i) {
+                ERRORF(reporter, "Couldn't make image from backendTexture for colorType %d\n",
+                       colorType);
+            } else {
+                REPORTER_ASSERT(reporter, initialCount+1 == cache->getResourceCount());
+            }
+        }
+    }
+
+    REPORTER_ASSERT(reporter, initialCount == cache->getResourceCount());
+
+    context->priv().deleteBackendTexture(t);
+}
+
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(ColorTypeBackendAllocationTest, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    const GrCaps* caps = context->priv().caps();
+
+    struct {
+        SkColorType   fColorType;
+        GrPixelConfig fConfig;
+    } combinations[] = {
+        { kAlpha_8_SkColorType,      kAlpha_8_GrPixelConfig           },
+        { kRGB_565_SkColorType,      kRGB_565_GrPixelConfig           },
+        { kARGB_4444_SkColorType,    kRGBA_4444_GrPixelConfig         },
+        { kRGBA_8888_SkColorType,    kRGBA_8888_GrPixelConfig         },
+        { kRGB_888x_SkColorType,     kRGB_888_GrPixelConfig           },
+        { kBGRA_8888_SkColorType,    kBGRA_8888_GrPixelConfig         },
+        { kRGBA_1010102_SkColorType, kRGBA_1010102_GrPixelConfig      },
+        { kRGB_101010x_SkColorType,  kUnknown_GrPixelConfig           },    // No Ganesh correlate
+        { kGray_8_SkColorType,       kGray_8_GrPixelConfig            },
+        { kRGBA_F16Norm_SkColorType, kRGBA_half_Clamped_GrPixelConfig },
+        { kRGBA_F16_SkColorType,     kRGBA_half_GrPixelConfig         },
+        { kRGBA_F32_SkColorType,     kRGBA_float_GrPixelConfig        },
+    };
+
+    SkASSERT(kLastEnum_SkColorType == SK_ARRAY_COUNT(combinations));
+
+    for (auto combo : combinations) {
+        SkColorType colorType = combo.fColorType;
+
+        if (!caps->isConfigTexturable(combo.fConfig)) {
+            continue;
+        }
+
+        if (GrBackendApi::kMetal == context->backend()) {
+            // skbug.com/9086 (Metal caps may not be handling RGBA32 correctly)
+            if (kRGBA_F32_SkColorType == combo.fColorType) {
+                continue;
+            }
+        }
+
+        for (auto renderable : { GrRenderable::kNo, GrRenderable::kYes }) {
+            if (GrRenderable::kYes == renderable) {
+                if (kRGB_888x_SkColorType == combo.fColorType) {
+                    // Ganesh can't perform the blends correctly when rendering this format
+                    continue;
+                }
+                if (!caps->isConfigRenderable(combo.fConfig)) {
+                    continue;
+                }
+            }
+
+            auto createMtd = [colorType](GrContext* context, GrRenderable renderable) {
+                return context->priv().createBackendTexture(32, 32, colorType,
+                                                            GrMipMapped::kNo, renderable);
+            };
+
+            test_wrapping(context, reporter, createMtd, colorType, renderable);
+        }
+    }
+
+}
+
+#include "src/gpu/gl/GrGLDefines.h"
+
+DEF_GPUTEST_FOR_ALL_GL_CONTEXTS(GLBackendAllocationTest, reporter, ctxInfo) {
+    sk_gpu_test::GLTestContext* glCtx = ctxInfo.glContext();
+    GrGLStandard standard = glCtx->gl()->fStandard;
+    GrContext* context = ctxInfo.grContext();
+    const GrCaps* caps = context->priv().caps();
+
+    struct {
+        SkColorType   fColorType;
+        GrGLenum      fFormat;
+        // TODO: remove 'fConfig' and directly use 'fFormat' in GrGLCaps::isFormatTexturable
+        GrPixelConfig fConfig;
+    } combinations[] = {
+        { kRGBA_8888_SkColorType,    GR_GL_RGBA8,                kRGBA_8888_GrPixelConfig         },
+        { kRGBA_8888_SkColorType,    GR_GL_SRGB8_ALPHA8,         kSRGBA_8888_GrPixelConfig        },
+
+        { kRGB_888x_SkColorType,     GR_GL_RGBA8,                kRGBA_8888_GrPixelConfig         },
+        { kRGB_888x_SkColorType,     GR_GL_RGB8,                 kRGB_888_GrPixelConfig           },
+
+        { kBGRA_8888_SkColorType,    GR_GL_RGBA8,                kRGBA_8888_GrPixelConfig         },
+        { kBGRA_8888_SkColorType,    GR_GL_BGRA8,                kBGRA_8888_GrPixelConfig         },
+        { kBGRA_8888_SkColorType,    GR_GL_SRGB8_ALPHA8,         kSBGRA_8888_GrPixelConfig        },
+
+        { kRGBA_1010102_SkColorType, GR_GL_RGB10_A2,             kRGBA_1010102_GrPixelConfig      },
+        { kRGB_565_SkColorType,      GR_GL_RGB565,               kRGB_565_GrPixelConfig           },
+        { kARGB_4444_SkColorType,    GR_GL_RGBA4,                kRGBA_4444_GrPixelConfig         },
+
+        { kAlpha_8_SkColorType,      GR_GL_ALPHA8,               kAlpha_8_as_Alpha_GrPixelConfig  },
+        { kAlpha_8_SkColorType,      GR_GL_R8,                   kAlpha_8_as_Red_GrPixelConfig    },
+
+        { kGray_8_SkColorType,       GR_GL_LUMINANCE8,           kGray_8_as_Lum_GrPixelConfig     },
+        { kGray_8_SkColorType,       GR_GL_R8,                   kGray_8_as_Red_GrPixelConfig     },
+
+        { kRGBA_F32_SkColorType,     GR_GL_RGBA32F,              kRGBA_float_GrPixelConfig        },
+
+        { kRGBA_F16Norm_SkColorType, GR_GL_RGBA16F,              kRGBA_half_Clamped_GrPixelConfig },
+        { kRGBA_F16_SkColorType,     GR_GL_RGBA16F,              kRGBA_half_GrPixelConfig         },
+
+        // These backend formats don't have SkColorType equivalents
+        { kUnknown_SkColorType,      GR_GL_RG32F,                kRG_float_GrPixelConfig          },
+        { kUnknown_SkColorType,      GR_GL_RG8,                  kRG_88_GrPixelConfig             },
+        { kUnknown_SkColorType,      GR_GL_R16F,                 kAlpha_half_as_Red_GrPixelConfig },
+        { kUnknown_SkColorType,      GR_GL_COMPRESSED_RGB8_ETC2, kRGB_ETC1_GrPixelConfig          },
+    };
+
+    for (auto combo : combinations) {
+        GrBackendFormat format = GrBackendFormat::MakeGL(combo.fFormat, GR_GL_TEXTURE_2D);
+
+        if (GR_GL_COMPRESSED_RGB8_ETC2 == combo.fFormat) {
+            // We current disallow uninitialized ETC1 textures in the GL backend
+            continue;
+        }
+        if (!caps->isConfigTexturable(combo.fConfig)) {
+            continue;
+        }
+
+        if (kBGRA_8888_SkColorType == combo.fColorType) {
+            if (GR_GL_RGBA8 == combo.fFormat && kGL_GrGLStandard != standard) {
+                continue;
+            }
+            if (GR_GL_BGRA8 == combo.fFormat && kGL_GrGLStandard == standard) {
+                continue;
+            }
+        }
+
+        for (auto renderable : { GrRenderable::kNo, GrRenderable::kYes }) {
+
+            if (GrRenderable::kYes == renderable) {
+                if (kRGB_888x_SkColorType == combo.fColorType) {
+                    // Ganesh can't perform the blends correctly when rendering this format
+                    continue;
+                }
+                if (!caps->isConfigRenderable(combo.fConfig)) {
+                    continue;
+                }
+            }
+
+            auto createMtd = [format](GrContext* context, GrRenderable renderable) {
+                return context->priv().createBackendTexture(32, 32, format,
+                                                            GrMipMapped::kNo, renderable);
+            };
+
+            test_wrapping(context, reporter, createMtd, combo.fColorType, renderable);
+        }
+    }
+}
+
+
+#ifdef SK_VULKAN
+
+#include "src/gpu/vk/GrVkCaps.h"
+
+DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkBackendAllocationTest, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    const GrVkCaps* vkCaps = static_cast<const GrVkCaps*>(context->priv().caps());
+
+    struct {
+        SkColorType fColorType;
+        VkFormat    fFormat;
+    } combinations[] = {
+        { kRGBA_8888_SkColorType,    VK_FORMAT_R8G8B8A8_UNORM           },
+        { kRGBA_8888_SkColorType,    VK_FORMAT_R8G8B8A8_SRGB            },
+
+        { kRGB_888x_SkColorType,     VK_FORMAT_R8G8B8_UNORM             },
+        { kRGB_888x_SkColorType,     VK_FORMAT_R8G8B8A8_UNORM           },
+
+        { kBGRA_8888_SkColorType,    VK_FORMAT_B8G8R8A8_UNORM           },
+        { kBGRA_8888_SkColorType,    VK_FORMAT_B8G8R8A8_SRGB            },
+
+        { kRGBA_1010102_SkColorType, VK_FORMAT_A2B10G10R10_UNORM_PACK32 },
+        { kRGB_565_SkColorType,      VK_FORMAT_R5G6B5_UNORM_PACK16      },
+        { kARGB_4444_SkColorType,    VK_FORMAT_B4G4R4A4_UNORM_PACK16    },
+        { kAlpha_8_SkColorType,      VK_FORMAT_R8_UNORM                 },
+        { kGray_8_SkColorType,       VK_FORMAT_R8_UNORM                 },
+        { kRGBA_F32_SkColorType,     VK_FORMAT_R32G32B32A32_SFLOAT      },
+
+        { kRGBA_F16Norm_SkColorType, VK_FORMAT_R16G16B16A16_SFLOAT      },
+        { kRGBA_F16_SkColorType,     VK_FORMAT_R16G16B16A16_SFLOAT      },
+
+        // These backend formats don't have SkColorType equivalents
+        { kUnknown_SkColorType,      VK_FORMAT_R32G32_SFLOAT            },
+        { kUnknown_SkColorType,      VK_FORMAT_R8G8_UNORM               },
+        { kUnknown_SkColorType,      VK_FORMAT_R16_SFLOAT               },
+        { kUnknown_SkColorType,      VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK  },
+    };
+
+    for (auto combo : combinations) {
+        if (!vkCaps->isFormatTexturable(combo.fFormat)) {
+            continue;
+        }
+
+        GrBackendFormat format = GrBackendFormat::MakeVk(combo.fFormat);
+
+        for (auto renderable : { GrRenderable::kNo, GrRenderable::kYes }) {
+
+            if (GrRenderable::kYes == renderable) {
+                if (kRGB_888x_SkColorType == combo.fColorType) {
+                    // Ganesh can't perform the blends correctly when rendering this format
+                    continue;
+                }
+                if (!vkCaps->isFormatRenderable(combo.fFormat)) {
+                    continue;
+                }
+            }
+
+            auto createMtd = [format](GrContext* context, GrRenderable renderable) {
+                return context->priv().createBackendTexture(32, 32, format,
+                                                            GrMipMapped::kNo, renderable);
+            };
+
+            test_wrapping(context, reporter, createMtd, combo.fColorType, renderable);
+        }
+    }
+}
+
+#endif
diff --git a/tests/MtlBackendAllocationTest.mm b/tests/MtlBackendAllocationTest.mm
new file mode 100644
index 0000000..191e191
--- /dev/null
+++ b/tests/MtlBackendAllocationTest.mm
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/gpu/GrContext.h"
+#include "src/gpu/GrContextPriv.h"
+#include "tests/Test.h"
+
+#import <metal/metal.h>
+
+// In BackendAllocationTest.cpp
+void test_wrapping(GrContext* context, skiatest::Reporter* reporter,
+                   std::function<GrBackendTexture (GrContext*, GrRenderable)> createMtd,
+                   SkColorType colorType, GrRenderable renderable);
+
+DEF_GPUTEST_FOR_METAL_CONTEXT(MtlBackendAllocationTest, reporter, ctxInfo) {
+    GrContext* context = ctxInfo.grContext();
+    const GrCaps* caps = context->priv().caps();
+
+    struct {
+        SkColorType      fColorType;
+        GrMTLPixelFormat fFormat;
+        // TODO: remove 'fConfig' and directly use 'fFormat' in GrMtlCaps::isFormatTexturable
+        GrPixelConfig    fConfig;
+    } combinations[] = {
+        { kRGBA_8888_SkColorType,    MTLPixelFormatRGBA8Unorm,      kRGBA_8888_GrPixelConfig      },
+        { kRGBA_8888_SkColorType,    MTLPixelFormatRGBA8Unorm_sRGB, kSRGBA_8888_GrPixelConfig     },
+
+        { kRGB_888x_SkColorType,     MTLPixelFormatRGBA8Unorm,      kRGBA_8888_GrPixelConfig      },
+
+        { kBGRA_8888_SkColorType,    MTLPixelFormatBGRA8Unorm,      kBGRA_8888_GrPixelConfig      },
+        { kBGRA_8888_SkColorType,    MTLPixelFormatBGRA8Unorm_sRGB, kSBGRA_8888_GrPixelConfig     },
+
+        { kRGBA_1010102_SkColorType, MTLPixelFormatRGB10A2Unorm,    kRGBA_1010102_GrPixelConfig    },
+#ifdef SK_BUILD_FOR_IOS
+        { kRGB_565_SkColorType,      MTLPixelFormatB5G6R5Unorm,     kRGB_565_GrPixelConfig        },
+        { kARGB_4444_SkColorType,    MTLPixelFormatABGR4Unorm,      kRGBA_4444_GrPixelConfig      },
+#endif
+
+        { kAlpha_8_SkColorType,      MTLPixelFormatA8Unorm,         kAlpha_8_as_Alpha_GrPixelConfig },
+        { kAlpha_8_SkColorType,      MTLPixelFormatR8Unorm,         kAlpha_8_as_Red_GrPixelConfig },
+
+        { kGray_8_SkColorType,       MTLPixelFormatR8Unorm,         kGray_8_as_Red_GrPixelConfig  },
+
+        { kRGBA_F32_SkColorType,     MTLPixelFormatRGBA32Float,     kRGBA_float_GrPixelConfig     },
+
+        { kRGBA_F16Norm_SkColorType, MTLPixelFormatRGBA16Float,     kRGBA_half_Clamped_GrPixelConfig },
+        { kRGBA_F16_SkColorType,     MTLPixelFormatRGBA16Float,     kRGBA_half_GrPixelConfig      },
+
+        // These backend formats don't have SkColorType equivalents
+        { kUnknown_SkColorType,     MTLPixelFormatRG32Float,        kRG_float_GrPixelConfig       },
+        { kUnknown_SkColorType,     MTLPixelFormatRG8Unorm,         kRG_88_GrPixelConfig          },
+        { kUnknown_SkColorType,     MTLPixelFormatR16Float,         kAlpha_half_as_Red_GrPixelConfig },
+#ifdef SK_BUILD_FOR_IOS
+        { kUnknown_SkColorType,     MTLPixelFormatETC2_RGB8,        kRGB_ETC1_GrPixelConfig       }
+#endif
+    };
+
+    for (auto combo : combinations) {
+        GrBackendFormat format = GrBackendFormat::MakeMtl(combo.fFormat);
+
+        if (!caps->isConfigTexturable(combo.fConfig)) {
+            continue;
+        }
+
+        // skbug.com/9086 (Metal caps may not be handling RGBA32 correctly)
+        if (kRGBA_F32_SkColorType == combo.fColorType) {
+            continue;
+        }
+
+        for (auto renderable : { GrRenderable::kNo, GrRenderable::kYes }) {
+
+            if (GrRenderable::kYes == renderable) {
+                if (kRGB_888x_SkColorType == combo.fColorType) {
+                    // Ganesh can't perform the blends correctly when rendering this format
+                    continue;
+                }
+                if (!caps->isConfigRenderable(combo.fConfig)) {
+                    continue;
+                }
+            }
+
+            auto createMtd = [format](GrContext* context, GrRenderable renderable) {
+                return context->priv().createBackendTexture(32, 32, format,
+                                                            GrMipMapped::kNo, renderable);
+            };
+
+            test_wrapping(context, reporter, createMtd, combo.fColorType, renderable);
+        }
+    }
+}