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/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: