[graphite] Add ContextFactory

Bug: skia:12466
Change-Id: I3299940af72cffde3904cf5f6262955807d6d1bc
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/453637
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 8ffdc24..cf6c559 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -1800,6 +1800,8 @@
       sources -= [ "tools/gpu/TestOps.h" ]
     }
     if (skia_enable_graphite) {
+      sources += [ "tools/graphite/ContextFactory.h" ]
+      sources += [ "tools/graphite/ContextFactory.cpp" ]
       sources += [ "tools/graphite/GraphiteTestContext.h" ]
       sources += [ "tools/graphite/GraphiteTestContext.cpp" ]
       if (skia_use_metal) {
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index 885aaf5..495a6aa 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -80,7 +80,10 @@
 #endif
 
 #ifdef SK_GRAPHITE_ENABLED
+#include "experimental/graphite/include/Context.h"
 #include "experimental/graphite/include/SkStuff.h"
+#include "tools/graphite/ContextFactory.h"
+#include "tools/graphite/GraphiteTestContext.h"
 #endif
 
 #if defined(SK_ENABLE_ANDROID_UTILS)
@@ -2122,9 +2125,14 @@
                           SkBitmap* dst,
                           SkWStream* dstStream,
                           SkString* log) const {
+    using ContextType = sk_graphite_test::ContextFactory::ContextType;
+
     SkImageInfo ii = SkImageInfo::Make(src.size(), kRGBA_8888_SkColorType, kPremul_SkAlphaType);
 
-    sk_sp<SkSurface> surface = MakeGraphite(ii);
+    sk_graphite_test::ContextFactory factory;
+    auto [_, context] = factory.getContextInfo(ContextType::kMetal);
+
+    sk_sp<SkSurface> surface = MakeGraphite(std::move(context), ii);
     if (!surface) {
         return Result::Fatal("Could not create a surface.");
     }
diff --git a/experimental/graphite/include/SkStuff.h b/experimental/graphite/include/SkStuff.h
index 00efaed..1cb2403 100644
--- a/experimental/graphite/include/SkStuff.h
+++ b/experimental/graphite/include/SkStuff.h
@@ -13,7 +13,11 @@
 struct SkImageInfo;
 class SkSurface;
 
-// TODO: Should be SkSurface.h
-sk_sp<SkSurface> MakeGraphite(const SkImageInfo&);
+namespace skgpu {
+    class Context;
+}
+
+// TODO: Should be in SkSurface.h
+sk_sp<SkSurface> MakeGraphite(sk_sp<skgpu::Context>, const SkImageInfo&);
 
 #endif // SkStuff_DEFINED
diff --git a/experimental/graphite/src/Device.cpp b/experimental/graphite/src/Device.cpp
index f6dc330..c3afb02 100644
--- a/experimental/graphite/src/Device.cpp
+++ b/experimental/graphite/src/Device.cpp
@@ -7,6 +7,7 @@
 
 #include "experimental/graphite/src/Device.h"
 
+#include "experimental/graphite/include/Context.h"
 #include "experimental/graphite/include/SkStuff.h"
 #include "experimental/graphite/src/DrawList.h"
 #include "experimental/graphite/src/SurfaceDrawContext.h"
@@ -18,20 +19,22 @@
 
 #include "src/core/SkMatrixPriv.h"
 #include "src/core/SkPaintPriv.h"
+#include "src/core/SkSpecialImage.h"
 
 namespace skgpu {
 
-sk_sp<Device> Device::Make(const SkImageInfo& ii) {
+sk_sp<Device> Device::Make(sk_sp<Context> context, const SkImageInfo& ii) {
     sk_sp<SurfaceDrawContext> sdc = SurfaceDrawContext::Make(ii);
     if (!sdc) {
         return nullptr;
     }
 
-    return sk_sp<Device>(new Device(std::move(sdc)));
+    return sk_sp<Device>(new Device(std::move(context), std::move(sdc)));
 }
 
-Device::Device(sk_sp<SurfaceDrawContext> sdc)
+Device::Device(sk_sp<Context> context, sk_sp<SurfaceDrawContext> sdc)
         : SkBaseDevice(sdc->imageInfo(), SkSurfaceProps())
+        , fContext(std::move(context))
         , fSDC(std::move(sdc)) {
     SkASSERT(SkToBool(fSDC));
 }
@@ -40,11 +43,11 @@
     // TODO: Inspect the paint and create info to determine if there's anything that has to be
     // modified to support inline subpasses.
     // TODO: onCreateDevice really should return sk_sp<SkBaseDevice>...
-    return Make(info.fInfo).release();
+    return Make(fContext, info.fInfo).release();
 }
 
 sk_sp<SkSurface> Device::makeSurface(const SkImageInfo& ii, const SkSurfaceProps& /* props */) {
-    return MakeGraphite(ii);
+    return MakeGraphite(fContext, ii);
 }
 
 bool Device::onReadPixels(const SkPixmap& pm, int x, int y) {
@@ -202,4 +205,16 @@
     }
 }
 
+sk_sp<SkSpecialImage> Device::makeSpecial(const SkBitmap&) {
+    return nullptr;
+}
+
+sk_sp<SkSpecialImage> Device::makeSpecial(const SkImage*) {
+    return nullptr;
+}
+
+sk_sp<SkSpecialImage> Device::snapSpecial(const SkIRect& subset, bool forceCopy) {
+    return nullptr;
+}
+
 } // namespace skgpu
diff --git a/experimental/graphite/src/Device.h b/experimental/graphite/src/Device.h
index d09535c..50d2726 100644
--- a/experimental/graphite/src/Device.h
+++ b/experimental/graphite/src/Device.h
@@ -12,11 +12,14 @@
 
 namespace skgpu {
 
+class Context;
 class SurfaceDrawContext;
 
 class Device final : public SkBaseDevice  {
 public:
-    static sk_sp<Device> Make(const SkImageInfo&);
+    static sk_sp<Device> Make(sk_sp<Context>, const SkImageInfo&);
+
+    sk_sp<Context> refContext() { return fContext; }
 
 protected:
     // Clipping
@@ -93,15 +96,14 @@
     void drawSpecial(SkSpecialImage*, const SkMatrix& localToDevice,
                      const SkSamplingOptions&, const SkPaint&) override {}
 
-    sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override { return nullptr; }
-    sk_sp<SkSpecialImage> makeSpecial(const SkImage*) override { return nullptr; }
-    sk_sp<SkSpecialImage> snapSpecial(const SkIRect& subset, bool forceCopy = false) override {
-        return nullptr;
-    }
+    sk_sp<SkSpecialImage> makeSpecial(const SkBitmap&) override;
+    sk_sp<SkSpecialImage> makeSpecial(const SkImage*) override;
+    sk_sp<SkSpecialImage> snapSpecial(const SkIRect& subset, bool forceCopy = false) override;
 
 private:
-    Device(sk_sp<SurfaceDrawContext>);
+    Device(sk_sp<Context>, sk_sp<SurfaceDrawContext>);
 
+    sk_sp<Context> fContext;
     sk_sp<SurfaceDrawContext> fSDC;
 };
 
diff --git a/experimental/graphite/src/ResourceProvider.cpp b/experimental/graphite/src/ResourceProvider.cpp
index 3b1122d..92e8ecf 100644
--- a/experimental/graphite/src/ResourceProvider.cpp
+++ b/experimental/graphite/src/ResourceProvider.cpp
@@ -34,4 +34,8 @@
     return pso;
 }
 
+std::unique_ptr<CommandBuffer> onCreateCommandBuffer() {
+    return nullptr;
+}
+
 } // namespace skgpu
diff --git a/experimental/graphite/src/ResourceProvider.h b/experimental/graphite/src/ResourceProvider.h
index 4ea1a19..d0f75ff 100644
--- a/experimental/graphite/src/ResourceProvider.h
+++ b/experimental/graphite/src/ResourceProvider.h
@@ -26,7 +26,7 @@
 protected:
     ResourceProvider();
 
-    virtual std::unique_ptr<CommandBuffer> onCreateCommandBuffer() { return nullptr; }
+    virtual std::unique_ptr<CommandBuffer> onCreateCommandBuffer();
     virtual Pipeline* onCreatePipeline() { return nullptr; }
 
 private:
diff --git a/experimental/graphite/src/SkStuff.cpp b/experimental/graphite/src/SkStuff.cpp
index ac1fb6b..d26bedf 100644
--- a/experimental/graphite/src/SkStuff.cpp
+++ b/experimental/graphite/src/SkStuff.cpp
@@ -7,12 +7,13 @@
 
 #include "experimental/graphite/include/SkStuff.h"
 
+#include "experimental/graphite/include/Context.h"
 #include "experimental/graphite/src/Device.h"
 #include "experimental/graphite/src/Surface_Graphite.h"
 
-sk_sp<SkSurface> MakeGraphite(const SkImageInfo& ii) {
+sk_sp<SkSurface> MakeGraphite(sk_sp<skgpu::Context> context, const SkImageInfo& ii) {
 
-    sk_sp<skgpu::Device> device = skgpu::Device::Make(ii);
+    sk_sp<skgpu::Device> device = skgpu::Device::Make(std::move(context), ii);
     if (!device) {
         return nullptr;
     }
diff --git a/experimental/graphite/src/Surface_Graphite.cpp b/experimental/graphite/src/Surface_Graphite.cpp
index 11b50ba..a43280d 100644
--- a/experimental/graphite/src/Surface_Graphite.cpp
+++ b/experimental/graphite/src/Surface_Graphite.cpp
@@ -7,6 +7,7 @@
 
 #include "experimental/graphite/src/Surface_Graphite.h"
 
+#include "experimental/graphite/include/Context.h"
 #include "experimental/graphite/include/SkStuff.h"
 #include "experimental/graphite/src/Device.h"
 #include "experimental/graphite/src/Image_Graphite.h"
@@ -23,7 +24,7 @@
 SkCanvas* Surface_Graphite::onNewCanvas() { return new SkCanvas(fDevice); }
 
 sk_sp<SkSurface> Surface_Graphite::onNewSurface(const SkImageInfo& ii) {
-    return MakeGraphite(ii);
+    return MakeGraphite(fDevice->refContext(), ii);
 }
 
 sk_sp<SkImage> Surface_Graphite::onNewImageSnapshot(const SkIRect* subset) {
diff --git a/tools/graphite/ContextFactory.cpp b/tools/graphite/ContextFactory.cpp
new file mode 100644
index 0000000..0bc2c54
--- /dev/null
+++ b/tools/graphite/ContextFactory.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "tools/graphite/ContextFactory.h"
+
+#include "experimental/graphite/include/Context.h"
+
+#ifdef SK_METAL
+#include "tools/graphite/mtl/GraphiteMtlTestContext.h"
+#endif
+
+namespace sk_graphite_test {
+
+ std::tuple<GraphiteTestContext*, sk_sp<skgpu::Context>> ContextFactory::getContextInfo(
+        ContextType type) {
+
+    for (ContextInfo& c : fContexts) {
+        if (c.type() == type) {
+            return { c.testContext(), c.refContext() };
+        }
+    }
+
+    std::unique_ptr<GraphiteTestContext> testCtx;
+
+    switch (type) {
+        case ContextType::kMetal: {
+#ifdef SK_METAL
+            testCtx = mtl::TestContext::Make();
+#endif
+        } break;
+
+        default:
+            break;
+    }
+
+    if (!testCtx) {
+        return {};
+    }
+
+    sk_sp<skgpu::Context> context = testCtx->makeContext();
+    if (!context) {
+        return {};
+    }
+
+    fContexts.push_back({ type, std::move(testCtx), std::move(context) });
+
+    return { fContexts.back().testContext(), fContexts.back().refContext() };
+}
+
+} // namespace sk_graphite_test
diff --git a/tools/graphite/ContextFactory.h b/tools/graphite/ContextFactory.h
new file mode 100644
index 0000000..46f0121
--- /dev/null
+++ b/tools/graphite/ContextFactory.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef sk_graphite_test_ContextFactory_DEFINED
+#define sk_graphite_test_ContextFactory_DEFINED
+
+#include <vector>
+#include "experimental/graphite/include/GraphiteTypes.h"
+#include "include/core/SkRefCnt.h"
+#include "tools/graphite/GraphiteTestContext.h"
+
+namespace skgpu {
+    class Context;
+};
+
+namespace sk_graphite_test {
+
+class ContextFactory {
+public:
+    enum class ContextType {
+        kMetal,
+        kMock,
+    };
+
+    class ContextInfo {
+    public:
+        ContextInfo() = default;
+        ContextInfo(ContextInfo&& other)
+           : fType(other.fType)
+           , fTestContext(std::move(other.fTestContext))
+           , fContext(std::move(other.fContext)) {
+        }
+
+        ~ContextInfo() = default;
+
+        ContextFactory::ContextType type() const { return fType; }
+
+        skgpu::Context* context() const { return fContext.get(); }
+        sk_sp<skgpu::Context> refContext() const { return fContext; }
+        GraphiteTestContext* testContext() const { return fTestContext.get(); }
+
+    private:
+        friend class ContextFactory; // for ctor
+
+        ContextInfo(ContextFactory::ContextType type,
+                    std::unique_ptr<GraphiteTestContext> testContext,
+                    sk_sp<skgpu::Context> context)
+            : fType(type)
+            , fTestContext(std::move(testContext))
+            , fContext(std::move(context)) {
+        }
+
+        ContextType                          fType = ContextType::kMock;
+        std::unique_ptr<GraphiteTestContext> fTestContext;
+        sk_sp<skgpu::Context>                fContext;
+    };
+
+    ContextFactory() = default;
+    ContextFactory(const ContextFactory&) = delete;
+    ContextFactory& operator=(const ContextFactory&) = delete;
+
+    ~ContextFactory() = default;
+
+    std::tuple<GraphiteTestContext*, sk_sp<skgpu::Context>> getContextInfo(ContextType);
+
+private:
+    std::vector<ContextInfo> fContexts;
+};
+
+} // namespace sk_graphite_test
+
+#endif // sk_graphite_test_ContextFactory_DEFINED
diff --git a/tools/graphite/GraphiteTestContext.h b/tools/graphite/GraphiteTestContext.h
index 789e30f..0d97fb7 100644
--- a/tools/graphite/GraphiteTestContext.h
+++ b/tools/graphite/GraphiteTestContext.h
@@ -5,8 +5,8 @@
  * found in the LICENSE file.
  */
 
-#ifndef skgpu_GraphiteTestContext_DEFINED
-#define skgpu_GraphiteTestContext_DEFINED
+#ifndef sk_graphite_test_GraphiteTestContext_DEFINED
+#define sk_graphite_test_GraphiteTestContext_DEFINED
 
 #include "experimental/graphite/include/GraphiteTypes.h"
 #include "include/core/SkRefCnt.h"
@@ -37,4 +37,4 @@
 
 }  // namespace sk_graphite_test
 
-#endif // skgpu_GraphiteTestContext_DEFINED
+#endif // sk_graphite_test_GraphiteTestContext_DEFINED
diff --git a/tools/graphite/mtl/GraphiteMtlTestContext.h b/tools/graphite/mtl/GraphiteMtlTestContext.h
index f58a470..ea06ad9 100644
--- a/tools/graphite/mtl/GraphiteMtlTestContext.h
+++ b/tools/graphite/mtl/GraphiteMtlTestContext.h
@@ -20,7 +20,7 @@
 public:
     ~TestContext() override {}
 
-    static GraphiteTestContext* Make();
+    static std::unique_ptr<GraphiteTestContext> Make();
 
     skgpu::BackendApi backend() override { return skgpu::BackendApi::kMetal; }
 
diff --git a/tools/graphite/mtl/MtlTestContext.mm b/tools/graphite/mtl/MtlTestContext.mm
index 9ec00b3..ea8f263 100644
--- a/tools/graphite/mtl/MtlTestContext.mm
+++ b/tools/graphite/mtl/MtlTestContext.mm
@@ -16,7 +16,7 @@
 
 namespace sk_graphite_test::mtl {
 
-GraphiteTestContext* TestContext::Make() {
+std::unique_ptr<GraphiteTestContext> TestContext::Make() {
     sk_cfp<id<MTLDevice>> device;
 #ifdef SK_BUILD_FOR_MAC
     sk_cfp<NSArray<id <MTLDevice>>*> availableDevices(MTLCopyAllDevices());
@@ -44,7 +44,7 @@
     backendContext.fDevice.retain(device.get());
     backendContext.fQueue.retain([*device newCommandQueue]);
 
-    return new TestContext(backendContext);
+    return std::unique_ptr<GraphiteTestContext>(new TestContext(backendContext));
 }
 
 sk_sp<skgpu::Context> TestContext::makeContext() {