Add mipmap support to SkPixmap flavor of createBackendTexture

Change-Id: I7a2dea9b0b8e6b985f2d56e587939266022c19e1
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/234664
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/src/core/SkPixmap.cpp b/src/core/SkPixmap.cpp
index 353c252af..3da7c1f 100644
--- a/src/core/SkPixmap.cpp
+++ b/src/core/SkPixmap.cpp
@@ -341,9 +341,9 @@
                   b = ((value >> 20) & 0x3ff) * (1/1023.0f),
                   a = ((value >> 30) & 0x3  ) * (1/   3.0f);
             if (a != 0 && needsUnpremul) {
-                r *= (1.0f/a);
-                g *= (1.0f/a);
-                b *= (1.0f/a);
+                r = SkTPin(r/a, 0.0f, 1.0f);
+                g = SkTPin(g/a, 0.0f, 1.0f);
+                b = SkTPin(b/a, 0.0f, 1.0f);
             }
             return (uint32_t)( r * 255.0f ) << 16
                  | (uint32_t)( g * 255.0f ) <<  8
diff --git a/src/gpu/GrContextPriv.h b/src/gpu/GrContextPriv.h
index ede9ea0..0fbec0f 100644
--- a/src/gpu/GrContextPriv.h
+++ b/src/gpu/GrContextPriv.h
@@ -267,6 +267,9 @@
     // pixmap(s).
     // If numLevels is 1 a non-mipMapped texture will result. If a mipMapped texture is desired
     // the data for all the mipmap levels must be provided.
+    // For the Vulkan backend the layout of the created VkImage will be:
+    //      VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL
+    // regardless of the renderability setting
     GrBackendTexture createBackendTexture(const SkPixmap srcData[], int numLevels,
                                           GrRenderable, GrProtected);
 
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 632f82f..c982a0f 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -3553,11 +3553,6 @@
         return GrBackendTexture();  // invalid
     }
 
-    // Currently we don't support uploading pixel data when mipped.
-    if (srcData && GrMipMapped::kYes == mipMapped) {
-        return GrBackendTexture();  // invalid
-    }
-
     GrGLTextureInfo info;
     GrGLTextureParameters::SamplerOverriddenState initialState;
 
@@ -3596,6 +3591,7 @@
             texels[i] = {&(tmpPixels[offset]), currentWidth * bytesPerPixel};
         }
     }
+
     GrSurfaceDesc desc;
     desc.fWidth = w;
     desc.fHeight = h;
diff --git a/src/gpu/mtl/GrMtlCppUtil.h b/src/gpu/mtl/GrMtlCppUtil.h
index 9e55204..47c6eb5 100644
--- a/src/gpu/mtl/GrMtlCppUtil.h
+++ b/src/gpu/mtl/GrMtlCppUtil.h
@@ -16,6 +16,7 @@
 
 #if GR_TEST_UTILS
 const char* GrMtlFormatToStr(GrMTLPixelFormat mtlFormat);
+bool GrMtlFormatIsBGRA(GrMTLPixelFormat mtlFormat);
 #endif
 
 #endif
diff --git a/src/gpu/mtl/GrMtlGpu.mm b/src/gpu/mtl/GrMtlGpu.mm
index 10666a4..16d0d42 100644
--- a/src/gpu/mtl/GrMtlGpu.mm
+++ b/src/gpu/mtl/GrMtlGpu.mm
@@ -712,6 +712,20 @@
     SkUNREACHABLE;
 }
 
+void copy_src_data(char* dst, size_t bytesPerPixel, const SkTArray<size_t>& individualMipOffsets,
+                   const SkPixmap srcData[], int numMipLevels, size_t bufferSize) {
+    SkASSERT(srcData && numMipLevels);
+    SkASSERT(individualMipOffsets.count() == numMipLevels);
+
+    for (int level = 0; level < numMipLevels; ++level) {
+        const size_t trimRB = srcData[level].width() * bytesPerPixel;
+        SkASSERT(individualMipOffsets[level] + trimRB * srcData[level].height() <= bufferSize);
+        SkRectMemcpy(dst + individualMipOffsets[level], trimRB,
+                     srcData[level].addr(), srcData[level].rowBytes(),
+                     trimRB, srcData[level].height());
+    }
+}
+
 bool GrMtlGpu::createMtlTextureForBackendSurface(MTLPixelFormat format,
                                                  int w, int h, bool texturable,
                                                  bool renderable, GrMipMapped mipMapped,
@@ -734,11 +748,8 @@
     if (renderable && !fMtlCaps->isFormatRenderable(format, 1)) {
         return false;
     }
-    // Currently we don't support uploading pixel data when mipped.
-    if (srcData && GrMipMapped::kYes == mipMapped) {
-        return false;
-    }
-    if(!check_max_blit_width(w)) {
+
+    if (!check_max_blit_width(w)) {
         return false;
     }
 
@@ -793,14 +804,8 @@
 
     // Fill buffer with data
     if (srcData) {
-        const size_t trimRowBytes = w * bytesPerPixel;
-
-        // TODO: support mipmapping
-        SkASSERT(1 == mipLevelCount);
-
-        // copy data into the buffer, skipping the trailing bytes
-        const char* src = (const char*) srcData->addr();
-        SkRectMemcpy(buffer, trimRowBytes, src, srcData->rowBytes(), trimRowBytes, h);
+        copy_src_data(buffer, bytesPerPixel, individualMipOffsets,
+                      srcData, numMipLevels, combinedBufferSize);
     } else if (color) {
         GrPixelConfig config = mtl_format_to_pixelconfig(format);
         SkASSERT(kUnknown_GrPixelConfig != config);
diff --git a/src/gpu/mtl/GrMtlUtil.mm b/src/gpu/mtl/GrMtlUtil.mm
index 045bf02..e0de7d5 100644
--- a/src/gpu/mtl/GrMtlUtil.mm
+++ b/src/gpu/mtl/GrMtlUtil.mm
@@ -353,6 +353,10 @@
 }
 
 #if GR_TEST_UTILS
+bool GrMtlFormatIsBGRA(GrMTLPixelFormat mtlFormat) {
+    return mtlFormat == MTLPixelFormatBGRA8Unorm;
+}
+
 const char* GrMtlFormatToStr(GrMTLPixelFormat mtlFormat) {
     switch (mtlFormat) {
         case MTLPixelFormatInvalid:         return "Invalid";
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index a5577a8..899360e 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -1448,23 +1448,26 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 bool copy_src_data(GrVkGpu* gpu, const GrVkAlloc& alloc, VkFormat vkFormat,
-                   int width, int height,
-                   const void* srcData, size_t srcRowBytes) {
-    SkASSERT(srcData);
+                   const SkTArray<size_t>& individualMipOffsets,
+                   const SkPixmap srcData[], int numMipLevels) {
+    SkASSERT(srcData && numMipLevels);
     SkASSERT(!GrVkFormatIsCompressed(vkFormat));
+    SkASSERT(individualMipOffsets.count() == numMipLevels);
 
-    void* mapPtr = GrVkMemory::MapAlloc(gpu, alloc);
+    char* mapPtr = (char*) GrVkMemory::MapAlloc(gpu, alloc);
     if (!mapPtr) {
         return false;
     }
     size_t bytesPerPixel = GrVkBytesPerFormat(vkFormat);
-    const size_t trimRowBytes = width * bytesPerPixel;
-    if (!srcRowBytes) {
-        srcRowBytes = trimRowBytes;
-    }
-    SkASSERT(trimRowBytes * height <= alloc.fSize);
 
-    SkRectMemcpy(mapPtr, trimRowBytes, srcData, srcRowBytes, trimRowBytes, height);
+    for (int level = 0; level < numMipLevels; ++level) {
+        const size_t trimRB = srcData[level].width() * bytesPerPixel;
+        SkASSERT(individualMipOffsets[level] + trimRB * srcData[level].height() <= alloc.fSize);
+
+        SkRectMemcpy(mapPtr + individualMipOffsets[level], trimRB,
+                     srcData[level].addr(), srcData[level].rowBytes(),
+                     trimRB, srcData[level].height());
+    }
 
     GrVkMemory::FlushMappedAlloc(gpu, alloc, 0, alloc.fSize);
     GrVkMemory::UnmapAlloc(gpu, alloc);
@@ -1527,11 +1530,6 @@
         return false;
     }
 
-    // Currently we don't support uploading pixel data when mipped.
-    if (srcData && GrMipMapped::kYes == mipMapped) {
-        return false;
-    }
-
     VkImageUsageFlags usageFlags = 0;
     usageFlags |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
     usageFlags |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
@@ -1644,9 +1642,8 @@
             return false;
         }
 
-        SkASSERT(1 == mipLevelCount);
-        bool result = copy_src_data(this, bufferAlloc, vkFormat, w, h,
-                                    srcData->addr(), srcData->rowBytes());
+        bool result = copy_src_data(this, bufferAlloc, vkFormat, individualMipOffsets,
+                                    srcData, numMipLevels);
         if (!result) {
             GrVkImage::DestroyImageInfo(this, info);
             GrVkMemory::FreeBufferMemory(this, GrVkBuffer::kCopyRead_Type, bufferAlloc);
diff --git a/tests/BackendAllocationTest.cpp b/tests/BackendAllocationTest.cpp
index acd2507..9d61535 100644
--- a/tests/BackendAllocationTest.cpp
+++ b/tests/BackendAllocationTest.cpp
@@ -20,6 +20,11 @@
 #include "src/gpu/gl/GrGLUtil.h"
 #endif
 
+#ifdef SK_METAL
+#include "include/gpu/mtl/GrMtlTypes.h"
+#include "src/gpu/mtl/GrMtlCppUtil.h"
+#endif
+
 // Test wrapping of GrBackendObjects in SkSurfaces and SkImages (non-static since used in Mtl test)
 void test_wrapping(GrContext* context, skiatest::Reporter* reporter,
                    std::function<GrBackendTexture (GrContext*,
@@ -93,60 +98,113 @@
     context->deleteBackendTexture(backendTex);
 }
 
+static bool isBGRA(const GrBackendFormat& format) {
+    switch (format.backend()) {
+        case GrBackendApi::kMetal:
+#ifdef SK_METAL
+            return GrMtlFormatIsBGRA(format.asMtlFormat());
+#else
+            return false;
+#endif
+        case GrBackendApi::kDawn:
+            return false;
+        case GrBackendApi::kOpenGL:
+#ifdef SK_GL
+            return format.asGLFormat() == GrGLFormat::kBGRA8;
+#else
+            return false;
+#endif
+        case GrBackendApi::kVulkan: {
+#ifdef SK_VULKAN
+            VkFormat vkFormat;
+            format.asVkFormat(&vkFormat);
+            return vkFormat == VK_FORMAT_B8G8R8A8_UNORM;
+#else
+            return false;
+#endif
+        }
+        case GrBackendApi::kMock:
+            return format.asMockColorType() == GrColorType::kBGRA_8888;
+    }
+    SkUNREACHABLE;
+}
+
+static bool isRGB(const GrBackendFormat& format) {
+    switch (format.backend()) {
+        case GrBackendApi::kMetal:
+            return false;  // Metal doesn't even pretend to support this
+        case GrBackendApi::kDawn:
+            return false;
+        case GrBackendApi::kOpenGL:
+#ifdef SK_GL
+            return format.asGLFormat() == GrGLFormat::kRGB8;
+#else
+            return false;
+#endif
+        case GrBackendApi::kVulkan: {
+#ifdef SK_VULKAN
+            VkFormat vkFormat;
+            format.asVkFormat(&vkFormat);
+            return vkFormat == VK_FORMAT_R8G8B8_UNORM;
+#else
+            return false;
+#endif
+        }
+        case GrBackendApi::kMock:
+            return false;  // No GrColorType::kRGB_888
+    }
+    SkUNREACHABLE;
+}
+
 static void check_solid_pixmap(skiatest::Reporter* reporter,
                                const SkColor4f& expected, const SkPixmap& actual,
-                               SkColorType ct, const char* label) {
+                               SkColorType ct, const char* label1, const char* label2) {
     // we need 0.001f across the board just for noise
     // we need 0.01f across the board for 1010102
-    const float tols[4] = {0.01f, 0.01f, 0.01f, 0.01f};
+    const float tols[4] = { 0.01f, 0.01f, 0.01f, 0.01f };
 
     auto error = std::function<ComparePixmapsErrorReporter>(
-        [reporter, ct, label](int x, int y, const float diffs[4]) {
+        [reporter, ct, label1, label2](int x, int y, const float diffs[4]) {
             SkASSERT(x >= 0 && y >= 0);
-            ERRORF(reporter, "%s %s - mismatch at %d, %d (%f, %f, %f %f)",
-                   ToolUtils::colortype_name(ct), label, x, y,
+            ERRORF(reporter, "%s %s %s - mismatch at %d, %d (%f, %f, %f %f)",
+                   ToolUtils::colortype_name(ct), label1, label2, x, y,
                    diffs[0], diffs[1], diffs[2], diffs[3]);
         });
 
     check_solid_pixels(expected, actual, tols, error);
 }
 
+// What would raster do?
 static SkColor4f get_expected_color(SkColor4f orig, SkColorType ct) {
-
-    uint32_t components = SkColorTypeComponentFlags(ct);
-
-    if (components & kGray_SkColorTypeComponentFlag) {
-        // For the GPU backends, gray implies a single channel which is opaque.
-        return { orig.fA, orig.fA, orig.fA, 1 };
+    if (ct == kGray_8_SkColorType) {
+        // Prevent the premul raster graciously applies in this case
+        orig.fA = 1.0;
     }
 
-    float r = orig.fR, g = orig.fG, b = orig.fB, a = orig.fA;
+    SkAlphaType at = SkColorTypeIsAlwaysOpaque(ct) ? kOpaque_SkAlphaType
+                                                   : kPremul_SkAlphaType;
 
-    // Missing channels are set to 0
-    if (!(components & kRed_SkColorTypeComponentFlag)) {
-        r = 0;
-    }
-    if (!(components & kGreen_SkColorTypeComponentFlag)) {
-        g = 0;
-    }
-    if (!(components & kBlue_SkColorTypeComponentFlag)) {
-        b = 0;
-    }
-    // except for missing alpha - which gets set to 1
-    if (!(components & kAlpha_SkColorTypeComponentFlag)) {
-        a = 1;
-    }
-
-    return { r, g, b, a };
+    SkImageInfo ii = SkImageInfo::Make(2, 2, ct, at);
+    SkAutoPixmapStorage pm;
+    pm.alloc(ii);
+    pm.erase(orig);
+    SkColor tmp = pm.getColor(0, 0);
+    return SkColor4f::FromColor(tmp);
 }
 
 static void check_mipmaps(GrContext* context, const GrBackendTexture& backendTex,
                           SkColorType skColorType, const SkColor4f expectedColors[6],
-                          skiatest::Reporter* reporter);
+                          skiatest::Reporter* reporter, const char* label);
 
 static void check_base_readbacks(GrContext* context, const GrBackendTexture& backendTex,
                                  SkColorType skColorType, GrRenderable renderable,
-                                 const SkColor4f& color, skiatest::Reporter* reporter) {
+                                 const SkColor4f& color, skiatest::Reporter* reporter,
+                                 const char* label) {
+    if (isRGB(backendTex.getBackendFormat())) {
+        // readPixels is busted for the RGB backend format (skbug.com/8862)
+        // TODO: add a GrColorType::kRGB_888 to fix the situation
+        return;
+    }
 
     SkAlphaType at = SkColorTypeIsAlwaysOpaque(skColorType) ? kOpaque_SkAlphaType
                                                             : kPremul_SkAlphaType;
@@ -157,28 +215,11 @@
 
     {
         SkImageInfo readBackII = SkImageInfo::Make(32, 32, kRGBA_8888_SkColorType,
-                                                   kPremul_SkAlphaType);
+                                                   kUnpremul_SkAlphaType);
 
         SkAssertResult(actual.tryAlloc(readBackII));
     }
 
-    if (GrRenderable::kYes == renderable && context->colorTypeSupportedAsSurface(skColorType)) {
-        sk_sp<SkSurface> surf = SkSurface::MakeFromBackendTexture(context,
-                                                                  backendTex,
-                                                                  kTopLeft_GrSurfaceOrigin,
-                                                                  0,
-                                                                  skColorType,
-                                                                  nullptr, nullptr);
-        if (surf) {
-            actual.erase(SkColors::kTransparent);
-            bool result = surf->readPixels(actual, 0, 0);
-            REPORTER_ASSERT(reporter, result);
-
-            check_solid_pixmap(reporter, expectedColor, actual, skColorType,
-                               "SkSurface::readPixels");
-        }
-    }
-
     {
         sk_sp<SkImage> img = SkImage::MakeFromTexture(context,
                                                       backendTex,
@@ -197,10 +238,29 @@
 #endif
             } else {
                 check_solid_pixmap(reporter, expectedColor, actual, skColorType,
-                                   "SkImage::readPixels");
+                                   label, "SkImage::readPixels");
             }
         }
     }
+
+    // This will mark any mipmaps as dirty (bc that is what we do when we wrap a renderable
+    // backend texture) so it must be done last!
+    if (GrRenderable::kYes == renderable && context->colorTypeSupportedAsSurface(skColorType)) {
+        sk_sp<SkSurface> surf = SkSurface::MakeFromBackendTexture(context,
+                                                                  backendTex,
+                                                                  kTopLeft_GrSurfaceOrigin,
+                                                                  0,
+                                                                  skColorType,
+                                                                  nullptr, nullptr);
+        if (surf) {
+            actual.erase(SkColors::kTransparent);
+            bool result = surf->readPixels(actual, 0, 0);
+            REPORTER_ASSERT(reporter, result);
+
+            check_solid_pixmap(reporter, expectedColor, actual, skColorType,
+                               label, "SkSurface::readPixels");
+        }
+    }
 }
 
 // Test initialization of GrBackendObjects to a specific color (non-static since used in Mtl test)
@@ -226,15 +286,16 @@
         return;
     }
 
-    check_base_readbacks(context, backendTex, skColorType, renderable, color, reporter);
-
     if (mipMapped == GrMipMapped::kYes) {
         SkColor4f expectedColor = get_expected_color(color, skColorType);
         SkColor4f expectedColors[6] = { expectedColor, expectedColor, expectedColor,
                                         expectedColor, expectedColor, expectedColor };
-        check_mipmaps(context, backendTex, skColorType, expectedColors, reporter);
+        check_mipmaps(context, backendTex, skColorType, expectedColors, reporter, "colorinit");
     }
 
+    // The last step in this test will dirty the mipmaps so do it last
+    check_base_readbacks(context, backendTex, skColorType, renderable, color,
+                         reporter, "colorinit");
     context->deleteBackendTexture(backendTex);
 }
 
@@ -242,7 +303,7 @@
 // all the mipMap levels.
 static void check_mipmaps(GrContext* context, const GrBackendTexture& backendTex,
                           SkColorType skColorType, const SkColor4f expectedColors[6],
-                          skiatest::Reporter* reporter) {
+                          skiatest::Reporter* reporter, const char* label) {
 
 #ifdef SK_GL
     // skbug.com/9141 (RGBA_F32 mipmaps appear to be broken on some Mali devices)
@@ -256,8 +317,15 @@
     }
 #endif
 
+    if (isRGB(backendTex.getBackendFormat())) {
+        // readPixels is busted for the RGB backend format (skbug.com/8862)
+        // TODO: add a GrColorType::kRGB_888 to fix the situation
+        return;
+    }
+
     SkAlphaType at = SkColorTypeIsAlwaysOpaque(skColorType) ? kOpaque_SkAlphaType
                                                             : kPremul_SkAlphaType;
+
     sk_sp<SkImage> img = SkImage::MakeFromTexture(context,
                                                   backendTex,
                                                   kTopLeft_GrSurfaceOrigin,
@@ -268,11 +336,12 @@
         return;
     }
 
-    SkImageInfo newII = SkImageInfo::Make(32, 32, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
+    SkImageInfo readbackSurfaceII = SkImageInfo::Make(32, 32, kRGBA_8888_SkColorType,
+                                                      kPremul_SkAlphaType);
 
     sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(context,
                                                         SkBudgeted::kNo,
-                                                        newII, 1,
+                                                        readbackSurfaceII, 1,
                                                         kTopLeft_GrSurfaceOrigin,
                                                         nullptr);
     if (!surf) {
@@ -295,7 +364,7 @@
 
         SkImageInfo readbackII = SkImageInfo::Make(rectSize, rectSize,
                                                    kRGBA_8888_SkColorType,
-                                                   kPremul_SkAlphaType);
+                                                   kUnpremul_SkAlphaType);
         SkAutoPixmapStorage actual2;
         SkAssertResult(actual2.tryAlloc(readbackII));
         actual2.erase(SkColors::kTransparent);
@@ -303,10 +372,83 @@
         bool result = surf->readPixels(actual2, 0, 0);
         REPORTER_ASSERT(reporter, result);
 
-        check_solid_pixmap(reporter, expectedColors[i], actual2, skColorType, "mip-level failure");
+        check_solid_pixmap(reporter, expectedColors[i], actual2, skColorType,
+                           label, "mip-level failure");
     }
 }
 
+static int make_pixmaps(SkColorType skColorType, GrMipMapped mipMapped,
+                        const SkColor4f colors[6], SkAutoPixmapStorage pixmaps[6]) {
+    int levelSize = 32;
+    int numMipLevels = mipMapped == GrMipMapped::kYes ? 6 : 1;
+    SkAlphaType at = SkColorTypeIsAlwaysOpaque(skColorType) ? kOpaque_SkAlphaType
+                                                            : kPremul_SkAlphaType;
+    for (int level = 0; level < numMipLevels; ++level) {
+        SkImageInfo ii = SkImageInfo::Make(levelSize, levelSize, skColorType, at);
+        pixmaps[level].alloc(ii);
+        pixmaps[level].erase(colors[level]);
+        levelSize /= 2;
+    }
+    return numMipLevels;
+}
+
+// Test initialization of GrBackendObjects using SkPixmaps
+static void test_pixmap_init(GrContext* context, skiatest::Reporter* reporter,
+                             std::function<GrBackendTexture (GrContext*,
+                                                             const SkPixmap srcData[],
+                                                             int numLevels,
+                                                             GrRenderable)> create,
+                             SkColorType skColorType, GrMipMapped mipMapped,
+                             GrRenderable renderable) {
+    SkAutoPixmapStorage pixmapMem[6];
+    SkColor4f colors[6] = {
+        { 1.0f, 0.0f, 0.0f, 1.0f }, // R
+        { 0.0f, 1.0f, 0.0f, 0.9f }, // G
+        { 0.0f, 0.0f, 1.0f, 0.7f }, // B
+        { 0.0f, 1.0f, 1.0f, 0.5f }, // C
+        { 1.0f, 0.0f, 1.0f, 0.3f }, // M
+        { 1.0f, 1.0f, 0.0f, 0.2f }, // Y
+    };
+
+    int numMipLevels = make_pixmaps(skColorType, mipMapped, colors, pixmapMem);
+    SkASSERT(numMipLevels);
+
+    // TODO: this is tedious. Should we pass in an array of SkBitmaps instead?
+    SkPixmap pixmaps[6];
+    for (int i = 0; i < numMipLevels; ++i) {
+        pixmaps[i].reset(pixmapMem[i].info(), pixmapMem[i].addr(), pixmapMem[i].rowBytes());
+    }
+
+    GrBackendTexture backendTex = create(context, pixmaps, numMipLevels, renderable);
+    if (!backendTex.isValid()) {
+        // errors here should be reported by the test_wrapping test
+        return;
+    }
+
+    if (skColorType == kBGRA_8888_SkColorType && !isBGRA(backendTex.getBackendFormat())) {
+        // When kBGRA is backed by an RGBA something goes wrong in the swizzling
+        return;
+    }
+
+    if (mipMapped == GrMipMapped::kYes) {
+        SkColor4f expectedColors[6] = {
+            get_expected_color(colors[0], skColorType),
+            get_expected_color(colors[1], skColorType),
+            get_expected_color(colors[2], skColorType),
+            get_expected_color(colors[3], skColorType),
+            get_expected_color(colors[4], skColorType),
+            get_expected_color(colors[5], skColorType),
+        };
+
+        check_mipmaps(context, backendTex, skColorType, expectedColors, reporter, "pixmap");
+    }
+
+    // The last step in this test will dirty the mipmaps so do it last
+    check_base_readbacks(context, backendTex, skColorType, renderable, colors[0],
+                         reporter, "pixmap");
+    context->deleteBackendTexture(backendTex);
+}
+
 enum class VkLayout {
     kUndefined,
     kReadOnlyOptimal,
@@ -452,11 +594,11 @@
         { kRGBA_F16Norm_SkColorType,      SkColors::kLtGray        },
         { kRGBA_F16_SkColorType,          SkColors::kYellow        },
         { kRGBA_F32_SkColorType,          SkColors::kGray          },
-        { kR8G8_unorm_SkColorType,        { .25f, .75f, 0, 0 }     },
+        { kR8G8_unorm_SkColorType,        { .25f, .75f, 0, 1 }     },
         { kR16G16_unorm_SkColorType,      SkColors::kGreen         },
         { kA16_unorm_SkColorType,         kTransCol                },
         { kA16_float_SkColorType,         kTransCol                },
-        { kR16G16_float_SkColorType,      { .25f, .75f, 0, 0 }     },
+        { kR16G16_float_SkColorType,      { .25f, .75f, 0, 1 }     },
         { kR16G16B16A16_unorm_SkColorType,{ .25f, .5f, .75f, 1 }   },
     };
 
@@ -482,6 +624,7 @@
                                                    renderable).isValid()) {
                     continue;
                 }
+
                 if (GrRenderable::kYes == renderable) {
                     if (kRGB_888x_SkColorType == combo.fColorType) {
                         // Ganesh can't perform the blends correctly when rendering this format
@@ -549,10 +692,41 @@
                                     SkColorTypeToGrColorType(colorType),
                                     combo.fColor, mipMapped, renderable);
                 }
+
+                // Gray_8 is problematic. In the colorInit tests there is ambiguity when
+                // mapping from format to colorType (since R8 or A8 could be either Alpha_8
+                // or Gray_8). To compensate for this ambiguity we feed in colors with
+                // R==G==B==A. If we actually do know the colorType (as is the case
+                // in the SkPixmap case, there is no ambiguity but the two test expectations
+                // now collide.
+                // For now, skip the SkPixmap tests. The real answer is to plumb the
+                // SkColorType down further in the color-init case.
+                if (colorType != kGray_8_SkColorType) {
+                    auto createWithSrcDataMtd = [](GrContext* context,
+                                                   const SkPixmap srcData[],
+                                                   int numLevels,
+                                                   GrRenderable renderable) {
+                        SkASSERT(srcData && numLevels);
+                        auto result = context->priv().createBackendTexture(srcData, numLevels,
+                                                                           renderable,
+                                                                           GrProtected::kNo);
+                        check_vk_layout(result, VkLayout::kReadOnlyOptimal);
+#ifdef SK_DEBUG
+                        {
+                            auto format = context->defaultBackendFormat(srcData[0].colorType(),
+                                                                        renderable);
+                            SkASSERT(format == result.getBackendFormat());
+                        }
+#endif
+                        return result;
+                    };
+
+                    test_pixmap_init(context, reporter, createWithSrcDataMtd, colorType,
+                                     mipMapped, renderable);
+                }
             }
         }
     }
-
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -692,7 +866,7 @@
     const GrVkCaps* vkCaps = static_cast<const GrVkCaps*>(context->priv().caps());
 
     constexpr SkColor4f kTransCol { 0, 0.25f, 0.75f, 0.5f };
-    constexpr SkColor4f kGrayCol { 0.75f, 0.75f, 0.75f, 0.75f };
+    constexpr SkColor4f kGrayCol { 0.75f, 0.75f, 0.75f, 1 };
 
     struct {
         GrColorType fColorType;
diff --git a/tests/MtlBackendAllocationTest.mm b/tests/MtlBackendAllocationTest.mm
index a9384cb..d3bfc79 100644
--- a/tests/MtlBackendAllocationTest.mm
+++ b/tests/MtlBackendAllocationTest.mm
@@ -32,10 +32,11 @@
     const GrMtlCaps* mtlCaps = static_cast<const GrMtlCaps*>(context->priv().caps());
 
     constexpr SkColor4f kTransCol { 0, 0.25f, 0.75f, 0.5f };
+    constexpr SkColor4f kGrayCol { 0.75f, 0.75f, 0.75f, 0.75f };
 
     struct {
         GrColorType      fColorType;
-        MTLPixelFormat fFormat;
+        MTLPixelFormat   fFormat;
         SkColor4f        fColor;
     } combinations[] = {
         { GrColorType::kRGBA_8888,        MTLPixelFormatRGBA8Unorm,      SkColors::kRed       },
@@ -56,7 +57,7 @@
 
         { GrColorType::kAlpha_8,          MTLPixelFormatA8Unorm,         kTransCol            },
         { GrColorType::kAlpha_8,          MTLPixelFormatR8Unorm,         kTransCol            },
-        { GrColorType::kGray_8,           MTLPixelFormatR8Unorm,         SkColors::kDkGray    },
+        { GrColorType::kGray_8,           MTLPixelFormatR8Unorm,         kGrayCol             },
 
         { GrColorType::kRGBA_F32,         MTLPixelFormatRGBA32Float,     SkColors::kRed       },
 
@@ -114,7 +115,6 @@
                                   combo.fColorType, mipMapped, renderable);
                 }
 
-                // Not implemented for Metal yet
                 {
                     auto createWithColorMtd = [format](GrContext* context,
                                                        const SkColor4f& color,