Add an (optional) SkTaskGroup to GrContext

GrContextOptions has an SkExecutor field, allowing clients to supply a
thread pool. If present, the GrContext will create an SkTaskGroup that
can be used for internal threading work.

Bug: skia:
Change-Id: I8b01245515a21a83f9fe838caf0a01c9a26c0003
Reviewed-on: https://skia-review.googlesource.com/37580
Reviewed-by: Greg Daniel <egdaniel@google.com>
Reviewed-by: Mike Klein <mtklein@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 6144687..7510a73 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -46,6 +46,7 @@
 
 class SkImage;
 class SkSurfaceProps;
+class SkTaskGroup;
 
 class SK_API GrContext : public SkRefCnt {
 public:
@@ -359,6 +360,8 @@
     // GrRenderTargetContexts.  It is also passed to the GrResourceProvider and SkGpuDevice.
     mutable GrSingleOwner                   fSingleOwner;
 
+    std::unique_ptr<SkTaskGroup>            fTaskGroup;
+
     struct CleanUpData {
         PFCleanUpFunc fFunc;
         void*         fInfo;
diff --git a/include/gpu/GrContextOptions.h b/include/gpu/GrContextOptions.h
index f560b31..d01d072 100644
--- a/include/gpu/GrContextOptions.h
+++ b/include/gpu/GrContextOptions.h
@@ -11,6 +11,8 @@
 #include "SkTypes.h"
 #include "GrTypes.h"
 
+class SkExecutor;
+
 struct GrContextOptions {
     GrContextOptions() {}
 
@@ -33,6 +35,14 @@
         deduce the optimal value for this platform. */
     int  fBufferMapThreshold = -1;
 
+    /**
+     * Executor to handle threaded work within Ganesh. If this is nullptr, then all work will be
+     * done serially on the main thread. To have worker threads assist with various tasks, set this
+     * to a valid SkExecutor instance. Currently, used for software path rendering, but may be used
+     * for other tasks.
+     */
+    SkExecutor* fExecutor = nullptr;
+
     /** some gpus have problems with partial writes of the rendertarget */
     bool fUseDrawInsteadOfPartialRenderTargetWrite = false;
 
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index d5c82c6..915700c 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -27,6 +27,8 @@
 #include "SkConvertPixels.h"
 #include "SkGr.h"
 #include "SkJSONWriter.h"
+#include "SkMakeUnique.h"
+#include "SkTaskGroup.h"
 #include "SkUnPreMultiplyPriv.h"
 #include "effects/GrConfigConversionEffect.h"
 #include "text/GrTextBlobCache.h"
@@ -201,6 +203,10 @@
 
     fTextBlobCache.reset(new GrTextBlobCache(TextBlobCacheOverBudgetCB, this));
 
+    if (options.fExecutor) {
+        fTaskGroup = skstd::make_unique<SkTaskGroup>(*options.fExecutor);
+    }
+
     return true;
 }
 
diff --git a/src/gpu/GrContextPriv.h b/src/gpu/GrContextPriv.h
index be67d0e..e1773c8 100644
--- a/src/gpu/GrContextPriv.h
+++ b/src/gpu/GrContextPriv.h
@@ -159,6 +159,8 @@
 
     GrBackend getBackend() const { return fContext->fBackend; }
 
+    SkTaskGroup* getTaskGroup() { return fContext->fTaskGroup.get(); }
+
 private:
     explicit GrContextPriv(GrContext* context) : fContext(context) {}
     GrContextPriv(const GrContextPriv&); // unimpl
diff --git a/tests/GrContextFactoryTest.cpp b/tests/GrContextFactoryTest.cpp
index 098b2d6..a6291cf 100644
--- a/tests/GrContextFactoryTest.cpp
+++ b/tests/GrContextFactoryTest.cpp
@@ -10,7 +10,9 @@
 #if SK_SUPPORT_GPU
 
 #include "GrContextFactory.h"
+#include "GrContextPriv.h"
 #include "GrCaps.h"
+#include "SkExecutor.h"
 #include "Test.h"
 
 using namespace sk_gpu_test;
@@ -139,6 +141,30 @@
     }
 }
 
+DEF_GPUTEST(GrContextFactory_executorAndTaskGroup, reporter, /*factory*/) {
+    // Verify that contexts have a task group iff we supply an executor with context options
+    GrContextOptions contextOptions;
+    contextOptions.fExecutor = nullptr;
+    GrContextFactory serialFactory(contextOptions);
+
+    std::unique_ptr<SkExecutor> threadPool = SkExecutor::MakeThreadPool(1);
+    contextOptions.fExecutor = threadPool.get();
+    GrContextFactory threadedFactory(contextOptions);
+
+    for (int i = 0; i < GrContextFactory::kContextTypeCnt; ++i) {
+        GrContextFactory::ContextType ctxType = static_cast<GrContextFactory::ContextType>(i);
+        ContextInfo serialInfo = serialFactory.getContextInfo(ctxType);
+        if (GrContext* serialContext = serialInfo.grContext()) {
+            REPORTER_ASSERT(reporter, nullptr == serialContext->contextPriv().getTaskGroup());
+        }
+
+        ContextInfo threadedInfo = threadedFactory.getContextInfo(ctxType);
+        if (GrContext* threadedContext = threadedInfo.grContext()) {
+            REPORTER_ASSERT(reporter, nullptr != threadedContext->contextPriv().getTaskGroup());
+        }
+    }
+}
+
 DEF_GPUTEST_FOR_ALL_CONTEXTS(GrContextDump, reporter, ctxInfo) {
     // Ensure that GrContext::dump doesn't assert (which is possible, if the JSON code is wrong)
     SkString result = ctxInfo.grContext()->dump();