Add support for creating a GrContext backed by Metal.

Also adds the support code to allow our TestContext to create a Metal
backend.

Bug: skia:
Change-Id: Ia850687019d79b897bb16e2c151f4f8526721ad9
Reviewed-on: https://skia-review.googlesource.com/22644
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Greg Daniel <egdaniel@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 26eeb95..4c34a1e 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -994,6 +994,9 @@
           libs += [ "vulkan" ]
         }
       }
+      if (skia_use_metal) {
+        sources += [ "tools/gpu/mtl/MtlTestContext.mm" ]
+      }
     }
   }
 
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 64c1746..c277ba7 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -52,6 +52,16 @@
     static GrContext* Create(GrBackend, GrBackendContext, const GrContextOptions& options);
     static GrContext* Create(GrBackend, GrBackendContext);
 
+#ifdef SK_METAL
+    /**
+     * Makes a GrContext which uses Metal as the backend. The device parameter is an MTLDevice
+     * and queue is an MTLCommandQueue which should be used by the backend. These objects must
+     * have a ref on them which can be transferred to Ganesh which will release the ref when the
+     * GrContext is destroyed.
+     */
+    static sk_sp<GrContext> MakeMetal(void* device, void* queue, const GrContextOptions& options);
+#endif
+
     virtual ~GrContext();
 
     sk_sp<GrContextThreadSafeProxy> threadSafeProxy();
@@ -328,6 +338,7 @@
 
     GrContext(); // init must be called after the constructor.
     bool init(GrBackend, GrBackendContext, const GrContextOptions& options);
+    bool init(const GrContextOptions& options);
 
     /**
      * These functions create premul <-> unpremul effects. If the second argument is 'true', they
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 8cad6be..2f3c30d 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -28,6 +28,10 @@
 #include "effects/GrConfigConversionEffect.h"
 #include "text/GrTextBlobCache.h"
 
+#ifdef SK_METAL
+#include "mtl/GrMtlTrampoline.h"
+#endif
+
 #define ASSERT_OWNED_PROXY(P) \
 SkASSERT(!(P) || !((P)->priv().peekTexture()) || (P)->priv().peekTexture()->getContext() == this)
 #define ASSERT_OWNED_PROXY_PRIV(P) \
@@ -61,6 +65,21 @@
     return context.release();
 }
 
+#ifdef SK_METAL
+sk_sp<GrContext> GrContext::MakeMetal(void* device, void* queue, const GrContextOptions& options) {
+    sk_sp<GrContext> context(new GrContext);
+    context->fGpu = GrMtlTrampoline::CreateGpu(context.get(), options, device, queue);
+    if (!context->fGpu) {
+        return nullptr;
+    }
+    context->fBackend = kMetal_GrBackend;
+    if (!context->init(options)) {
+        return nullptr;
+    }
+    return context;
+}
+#endif
+
 static int32_t gNextID = 1;
 static int32_t next_id() {
     int32_t id;
@@ -89,7 +108,11 @@
     if (!fGpu) {
         return false;
     }
+    return this->init(options);
+}
 
+bool GrContext::init(const GrContextOptions& options) {
+    ASSERT_SINGLE_OWNER
     fCaps = SkRef(fGpu->caps());
     fResourceCache = new GrResourceCache(fCaps, fUniqueID);
     fResourceProvider = new GrResourceProvider(fGpu, fResourceCache, &fSingleOwner);
diff --git a/src/gpu/GrGpuFactory.cpp b/src/gpu/GrGpuFactory.cpp
index 7cfec4a..1fc19c1 100644
--- a/src/gpu/GrGpuFactory.cpp
+++ b/src/gpu/GrGpuFactory.cpp
@@ -12,9 +12,6 @@
 #ifdef SK_VULKAN
 #include "vk/GrVkGpu.h"
 #endif
-#ifdef SK_METAL
-#include "mtl/GrMtlTrampoline.h"
-#endif
 
 GrGpu* GrGpu::Create(GrBackend backend,
                      GrBackendContext backendContext,
@@ -27,10 +24,6 @@
         case kVulkan_GrBackend:
             return GrVkGpu::Create(backendContext, options, context);
 #endif
-#ifdef SK_METAL
-        case kMetal_GrBackend:
-            return GrMtlTrampoline::CreateGpu(backendContext, options, context);
-#endif
         case kMock_GrBackend:
             return GrMockGpu::Create(backendContext, options, context);
         default:
diff --git a/src/gpu/mtl/GrMtlGpu.h b/src/gpu/mtl/GrMtlGpu.h
index 7ec9907..b63f908 100644
--- a/src/gpu/mtl/GrMtlGpu.h
+++ b/src/gpu/mtl/GrMtlGpu.h
@@ -16,11 +16,12 @@
 #import <Metal/Metal.h>
 
 class GrSemaphore;
+struct GrMtlBackendContext;
 
 class GrMtlGpu : public GrGpu {
 public:
-    static GrGpu* Create(GrBackendContext backendContext, const GrContextOptions& options,
-                         GrContext* context);
+    static GrGpu* Create(GrContext* context, const GrContextOptions& options,
+                         id<MTLDevice> device, id<MTLCommandQueue> queue);
 
     ~GrMtlGpu() override {}
 
@@ -59,19 +60,8 @@
     sk_sp<GrSemaphore> prepareTextureForCrossContextUsage(GrTexture*) override { return nullptr; }
 
 private:
-    GrMtlGpu(GrContext* context, const GrContextOptions& options) : INHERITED(context) {
-        id<MTLDevice> device = MTLCreateSystemDefaultDevice();
-
-        MTLTextureDescriptor* txDesc = [[MTLTextureDescriptor alloc] init];
-        txDesc.textureType = MTLTextureType3D;
-        txDesc.height = 64;
-        txDesc.width = 64;
-        txDesc.depth = 64;
-        txDesc.pixelFormat = MTLPixelFormatBGRA8Unorm;
-        txDesc.arrayLength = 1;
-        txDesc.mipmapLevelCount = 1;
-        fTestHeaderTexture = [device newTextureWithDescriptor:txDesc];
-    }
+    GrMtlGpu(GrContext* context, const GrContextOptions& options,
+             id<MTLDevice> device, id<MTLCommandQueue> queue);
 
     void onResetContext(uint32_t resetBits) override {}
 
@@ -146,7 +136,9 @@
     bool isTestingOnlyBackendTexture(GrBackendObject ) const override { return false; }
     void deleteTestingOnlyBackendTexture(GrBackendObject, bool abandonTexture) override {}
 
-    id<MTLTexture> fTestHeaderTexture;
+    id<MTLDevice> fDevice;
+    id<MTLCommandQueue> fQueue;
+
 
     typedef GrGpu INHERITED;
 };
diff --git a/src/gpu/mtl/GrMtlGpu.mm b/src/gpu/mtl/GrMtlGpu.mm
index 7d9339c..986bb8a 100644
--- a/src/gpu/mtl/GrMtlGpu.mm
+++ b/src/gpu/mtl/GrMtlGpu.mm
@@ -11,8 +11,31 @@
 #error This file must be compiled with Arc. Use -fobjc-arc flag
 #endif
 
-GrGpu* GrMtlGpu::Create(GrBackendContext backendContext, const GrContextOptions& options,
-                        GrContext* context) {
-    return nullptr;
+GrGpu* GrMtlGpu::Create(GrContext* context, const GrContextOptions& options,
+                        id<MTLDevice> device, id<MTLCommandQueue> queue) {
+    if (!device || !queue) {
+        return nullptr;
+    }
+    return new GrMtlGpu(context, options, device, queue);
 }
 
+GrMtlGpu::GrMtlGpu(GrContext* context, const GrContextOptions& options,
+                   id<MTLDevice> device, id<MTLCommandQueue> queue)
+        : INHERITED(context)
+        , fDevice(device)
+        , fQueue(queue) {
+    MTLTextureDescriptor* txDesc = [[MTLTextureDescriptor alloc] init];
+    txDesc.textureType = MTLTextureType3D;
+    txDesc.height = 64;
+    txDesc.width = 64;
+    txDesc.depth = 64;
+    txDesc.pixelFormat = MTLPixelFormatRGBA8Unorm;
+    txDesc.arrayLength = 1;
+    txDesc.mipmapLevelCount = 1;
+    id<MTLTexture> testTexture = [fDevice newTextureWithDescriptor:txDesc];
+    // To get ride of unused var warning
+    int width = [testTexture width];
+    SkDebugf("width: %d\n", width);
+    // Unused queue warning fix
+    SkDebugf("ptr to queue: %p\n", fQueue);
+}
diff --git a/src/gpu/mtl/GrMtlTrampoline.h b/src/gpu/mtl/GrMtlTrampoline.h
index acce98c..531a4ce 100644
--- a/src/gpu/mtl/GrMtlTrampoline.h
+++ b/src/gpu/mtl/GrMtlTrampoline.h
@@ -20,8 +20,10 @@
  */
 class GrMtlTrampoline {
 public:
-    static GrGpu* CreateGpu(GrBackendContext backendContext, const GrContextOptions& options,
-                            GrContext* context);
+    static GrGpu* CreateGpu(GrContext* context,
+                            const GrContextOptions& options,
+                            void* device,
+                            void* queue);
 };
 
 #endif
diff --git a/src/gpu/mtl/GrMtlTrampoline.mm b/src/gpu/mtl/GrMtlTrampoline.mm
index ea91ebb..0f112e4 100644
--- a/src/gpu/mtl/GrMtlTrampoline.mm
+++ b/src/gpu/mtl/GrMtlTrampoline.mm
@@ -9,9 +9,13 @@
 
 #include "GrMtlGpu.h"
 
-GrGpu* GrMtlTrampoline::CreateGpu(GrBackendContext backendContext,
+GrGpu* GrMtlTrampoline::CreateGpu(GrContext* context,
                                   const GrContextOptions& options,
-                                  GrContext* context) {
-    return GrMtlGpu::Create(backendContext, options, context);
+                                  void* device,
+                                  void* queue) {
+    return GrMtlGpu::Create(context,
+                            options,
+                            (__bridge_transfer id<MTLDevice>)device,
+                            (__bridge_transfer id<MTLCommandQueue>)queue);
 }
 
diff --git a/tools/gpu/GrContextFactory.cpp b/tools/gpu/GrContextFactory.cpp
index d614b06..0686b79 100644
--- a/tools/gpu/GrContextFactory.cpp
+++ b/tools/gpu/GrContextFactory.cpp
@@ -20,6 +20,9 @@
 #ifdef SK_VULKAN
 #include "vk/VkTestContext.h"
 #endif
+#ifdef SK_METAL
+#include "mtl/MtlTestContext.h"
+#endif
 #include "gl/null/NullGLTestContext.h"
 #include "gl/GrGLGpu.h"
 #include "mock/MockTestContext.h"
@@ -221,6 +224,16 @@
             break;
         }
 #endif
+#ifdef SK_METAL
+        case kMetal_GrBackend: {
+            SkASSERT(!masterContext);
+            testCtx.reset(CreatePlatformMtlTestContext(nullptr));
+            if (!testCtx) {
+                return ContextInfo();
+            }
+            break;
+        }
+#endif
         case kMock_GrBackend: {
             TestContext* sharedContext = masterContext ? masterContext->fTestContext : nullptr;
             SkASSERT(kMock_ContextType == type);
@@ -252,7 +265,10 @@
     if (ContextOverrides::kAvoidStencilBuffers & overrides) {
         grOptions.fAvoidStencilBuffers = true;
     }
-    sk_sp<GrContext> grCtx(GrContext::Create(backend, backendContext, grOptions));
+    sk_sp<GrContext> grCtx = testCtx->makeGrContext(grOptions);
+    if (!grCtx.get() && kMetal_GrBackend != backend) {
+        grCtx.reset(GrContext::Create(backend, backendContext, grOptions));
+    }
     if (!grCtx.get()) {
         return ContextInfo();
     }
diff --git a/tools/gpu/TestContext.cpp b/tools/gpu/TestContext.cpp
index 90aba43..c80c4ea 100644
--- a/tools/gpu/TestContext.cpp
+++ b/tools/gpu/TestContext.cpp
@@ -10,6 +10,8 @@
 
 #include "GpuTimer.h"
 
+#include "GrContext.h"
+
 namespace sk_gpu_test {
 TestContext::TestContext()
     : fFenceSync(nullptr)
@@ -29,6 +31,10 @@
     SkASSERT(!fGpuTimer);
 }
 
+sk_sp<GrContext> TestContext::makeGrContext(const GrContextOptions&) {
+    return nullptr;
+}
+
 void TestContext::makeCurrent() const { this->onPlatformMakeCurrent(); }
 
 void TestContext::swapBuffers() { this->onPlatformSwapBuffers(); }
diff --git a/tools/gpu/TestContext.h b/tools/gpu/TestContext.h
index ecb61e3..84794f3 100644
--- a/tools/gpu/TestContext.h
+++ b/tools/gpu/TestContext.h
@@ -11,8 +11,12 @@
 
 #include "FenceSync.h"
 #include "GrTypes.h"
+#include "SkRefCnt.h"
 #include "../private/SkTemplates.h"
 
+class GrContext;
+struct GrContextOptions;
+
 namespace sk_gpu_test {
 
 class GpuTimer;
@@ -25,8 +29,6 @@
 public:
     virtual ~TestContext();
 
-    virtual bool isValid() const = 0;
-
     bool fenceSyncSupport() const { return fFenceSync != nullptr; }
     FenceSync* fenceSync() { SkASSERT(fFenceSync); return fFenceSync.get(); }
 
@@ -46,6 +48,8 @@
     virtual GrBackend backend() = 0;
     virtual GrBackendContext backendContext() = 0;
 
+    virtual sk_sp<GrContext> makeGrContext(const GrContextOptions&);
+
     /** Swaps front and back buffer (if the context has such buffers) */
     void swapBuffers();
 
diff --git a/tools/gpu/gl/GLTestContext.h b/tools/gpu/gl/GLTestContext.h
index 9bd1039..f5afa31 100644
--- a/tools/gpu/gl/GLTestContext.h
+++ b/tools/gpu/gl/GLTestContext.h
@@ -25,7 +25,7 @@
         return reinterpret_cast<GrBackendContext>(fGL.get());
     }
 
-    bool isValid() const override { return SkToBool(this->gl()); }
+    bool isValid() const { return SkToBool(this->gl()); }
 
     const GrGLInterface *gl() const { return fGL.get(); }
 
diff --git a/tools/gpu/mock/MockTestContext.cpp b/tools/gpu/mock/MockTestContext.cpp
index c47fff5..56cd68c 100644
--- a/tools/gpu/mock/MockTestContext.cpp
+++ b/tools/gpu/mock/MockTestContext.cpp
@@ -21,7 +21,6 @@
     virtual GrBackendContext backendContext() override {
         return reinterpret_cast<GrBackendContext>(nullptr);
     }
-    bool isValid() const override { return true; }
     void testAbandon() override {}
     void submit() override {}
     void finish() override {}
diff --git a/tools/gpu/mtl/MtlTestContext.h b/tools/gpu/mtl/MtlTestContext.h
new file mode 100644
index 0000000..f703238
--- /dev/null
+++ b/tools/gpu/mtl/MtlTestContext.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef MtlTestContext_h
+#define MtlTestContext_h
+
+#include "TestContext.h"
+
+#ifdef SK_METAL
+
+namespace sk_gpu_test {
+TestContext* CreatePlatformMtlTestContext(TestContext*);
+}  // namespace sk_gpu_test
+
+#endif
+
+
+#endif /* MtlTestContext_h */
diff --git a/tools/gpu/mtl/MtlTestContext.mm b/tools/gpu/mtl/MtlTestContext.mm
new file mode 100644
index 0000000..4014e2b
--- /dev/null
+++ b/tools/gpu/mtl/MtlTestContext.mm
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "MtlTestContext.h"
+
+#include "GrContext.h"
+#include "GrContextOptions.h"
+
+#import <Metal/Metal.h>
+
+#ifdef SK_METAL
+
+namespace {
+/**
+ * Implements sk_gpu_test::FenceSync for Metal.
+ */
+
+// TODO
+#if 0
+class MtlFenceSync : public sk_gpu_test::FenceSync {
+public:
+    MtlFenceSync(sk_sp<const GrVkInterface> vk, VkDevice device, VkQueue queue,
+                uint32_t queueFamilyIndex)
+            : fVk(std::move(vk))
+            , fDevice(device)
+            , fQueue(queue) {
+        SkDEBUGCODE(fUnfinishedSyncs = 0;)
+        VkCommandPoolCreateInfo createInfo;
+        createInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+        createInfo.pNext = nullptr;
+        createInfo.flags = 0;
+        createInfo.queueFamilyIndex = queueFamilyIndex;
+        GR_VK_CALL_ERRCHECK(fVk, CreateCommandPool(fDevice, &createInfo, nullptr, &fCommandPool));
+
+        VkCommandBufferAllocateInfo allocateInfo;
+        allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+        allocateInfo.pNext = nullptr;
+        allocateInfo.commandBufferCount = 1;
+        allocateInfo.commandPool = fCommandPool;
+        allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+        GR_VK_CALL_ERRCHECK(fVk, AllocateCommandBuffers(fDevice, &allocateInfo, &fCommandBuffer));
+
+        VkCommandBufferBeginInfo beginInfo;
+        beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+        beginInfo.pNext = nullptr;
+        beginInfo.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
+        beginInfo.pInheritanceInfo = nullptr;
+        GR_VK_CALL_ERRCHECK(fVk, BeginCommandBuffer(fCommandBuffer, &beginInfo));
+        GR_VK_CALL_ERRCHECK(fVk, EndCommandBuffer(fCommandBuffer));
+    }
+
+    ~VkFenceSync() override {
+        SkASSERT(!fUnfinishedSyncs);
+        // If the above assertion is true then the command buffer should not be in flight.
+        GR_VK_CALL(fVk, FreeCommandBuffers(fDevice, fCommandPool, 1, &fCommandBuffer));
+        GR_VK_CALL(fVk, DestroyCommandPool(fDevice, fCommandPool, nullptr));
+    }
+
+    sk_gpu_test::PlatformFence SK_WARN_UNUSED_RESULT insertFence() const override {
+        VkFence fence;
+        VkFenceCreateInfo info;
+        info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+        info.pNext = nullptr;
+        info.flags = 0;
+        GR_VK_CALL_ERRCHECK(fVk, CreateFence(fDevice, &info, nullptr, &fence));
+        VkSubmitInfo submitInfo;
+        submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+        submitInfo.pNext = nullptr;
+        submitInfo.waitSemaphoreCount = 0;
+        submitInfo.pWaitSemaphores = nullptr;
+        submitInfo.pWaitDstStageMask = nullptr;
+        submitInfo.commandBufferCount = 1;
+        submitInfo.pCommandBuffers = &fCommandBuffer;
+        submitInfo.signalSemaphoreCount = 0;
+        submitInfo.pSignalSemaphores = nullptr;
+        GR_VK_CALL_ERRCHECK(fVk, QueueSubmit(fQueue, 1, &submitInfo, fence));
+        SkDEBUGCODE(++fUnfinishedSyncs;)
+        return (sk_gpu_test::PlatformFence)fence;
+    }
+
+    bool waitFence(sk_gpu_test::PlatformFence opaqueFence) const override {
+        VkFence fence = (VkFence)opaqueFence;
+        static constexpr uint64_t kForever = ~((uint64_t)0);
+        auto result = GR_VK_CALL(fVk, WaitForFences(fDevice, 1, &fence, true, kForever));
+        return result != VK_TIMEOUT;
+    }
+
+    void deleteFence(sk_gpu_test::PlatformFence opaqueFence) const override {
+        VkFence fence = (VkFence)opaqueFence;
+        GR_VK_CALL(fVk, DestroyFence(fDevice, fence, nullptr));
+        SkDEBUGCODE(--fUnfinishedSyncs;)
+    }
+
+private:
+    sk_sp<const GrVkInterface>  fVk;
+    VkDevice                    fDevice;
+    VkQueue                     fQueue;
+    VkCommandPool               fCommandPool;
+    VkCommandBuffer             fCommandBuffer;
+    SkDEBUGCODE(mutable int     fUnfinishedSyncs;)
+    typedef sk_gpu_test::FenceSync INHERITED;
+};
+
+GR_STATIC_ASSERT(sizeof(VkFence) <= sizeof(sk_gpu_test::PlatformFence));
+#endif
+
+class MtlTestContext : public sk_gpu_test::TestContext {
+public:
+    static MtlTestContext* Create(TestContext* sharedContext) {
+        SkASSERT(!sharedContext);
+        id<MTLDevice> device = MTLCreateSystemDefaultDevice();
+        id<MTLCommandQueue> queue = [device newCommandQueue];
+
+        return new MtlTestContext(device, queue);
+    }
+
+    ~MtlTestContext() override { this->teardown(); }
+
+    GrBackend backend() override { return kMetal_GrBackend; }
+
+    GrBackendContext backendContext() override { return 0; }
+
+    void testAbandon() override {}
+
+    // There is really nothing to here since we don't own any unqueued command buffers here.
+    void submit() override {}
+
+    void finish() override {}
+
+    sk_sp<GrContext> makeGrContext(const GrContextOptions& options) override {
+        return GrContext::MakeMetal((__bridge_retained void*)fDevice,
+                                    (__bridge_retained void*)fQueue,
+                                    options);
+    }
+
+private:
+    MtlTestContext(id<MTLDevice> device, id<MTLCommandQueue> queue)
+            : fDevice(device), fQueue(queue) {
+        fFenceSync.reset(nullptr);
+    }
+
+    void onPlatformMakeCurrent() const override {}
+    void onPlatformSwapBuffers() const override {}
+
+    id<MTLDevice> fDevice;
+    id<MTLCommandQueue>  fQueue;
+
+    typedef sk_gpu_test::TestContext INHERITED;
+};
+
+}  // anonymous namespace
+
+namespace sk_gpu_test {
+
+TestContext* CreatePlatformMtlTestContext(TestContext* sharedContext) {
+    return MtlTestContext::Create(sharedContext);
+}
+}  // namespace sk_gpu_test
+
+
+#endif
diff --git a/tools/gpu/vk/VkTestContext.h b/tools/gpu/vk/VkTestContext.h
index 85acb0e..5090f55 100644
--- a/tools/gpu/vk/VkTestContext.h
+++ b/tools/gpu/vk/VkTestContext.h
@@ -26,8 +26,6 @@
         return fVk;
     }
 
-    bool isValid() const override { return NULL != this->vk(); }
-
     const GrVkInterface* vk() const { return fVk->fInterface.get(); }
 
 protected: