[graphite] Add PrecompileContext object

This CL introduces an object that can be created on the main thread and then moved to a precompilation thread. This is substantially safer than passing the Context to a precompilation thread.

It also holds a ResourceProvider so eliminates the need to allocate a new one for each Precompile call.

Bug: b/358074434
Bug: b/373849852
Change-Id: I4ba4530655d65224dcd68e1b1a3e2a0d477bd5fd
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/919738
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index 115ecb9..b53988d 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -113,11 +113,12 @@
 #include "tools/graphite/GraphiteTestContext.h"
 
 #if defined(SK_ENABLE_PRECOMPILE)
+#include "src/gpu/graphite/AndroidSpecificPrecompile.h"
 #include "src/gpu/graphite/Caps.h"
 #include "src/gpu/graphite/ContextPriv.h"
 #include "src/gpu/graphite/GraphicsPipeline.h"
 #include "src/gpu/graphite/GraphicsPipelineDesc.h"
-#include "src/gpu/graphite/PublicPrecompile.h"
+#include "src/gpu/graphite/PrecompileContextPriv.h"
 #include "src/gpu/graphite/RecorderPriv.h"
 #include "src/gpu/graphite/RenderPassDesc.h"
 #include "src/gpu/graphite/RendererProvider.h"
@@ -2253,6 +2254,10 @@
         }
     }
 
+    if (!fPrecompileContext) {
+        fPrecompileContext = context->makePrecompileContext();
+    }
+
     sk_sp<SkSurface> surface = this->makeSurface(fRecorder.get(), src.size());
     if (!surface) {
         return Result::Fatal("Could not create a surface.");
@@ -2279,24 +2284,25 @@
     return Result::Ok();
 }
 
-Result GraphitePrecompileTestingSink::resetAndRecreatePipelines(
-        skgpu::graphite::Context* context) const {
+Result GraphitePrecompileTestingSink::resetAndRecreatePipelines() const {
     using namespace skgpu::graphite;
 
-    SkASSERT(fRecorder);
+    SkASSERT(fRecorder && fPrecompileContext);
+
+    GlobalCache* globalCache = fPrecompileContext->priv().globalCache();
 
     RuntimeEffectDictionary* rteDict = fRecorder->priv().runtimeEffectDictionary();
 
     std::vector<skgpu::UniqueKey> origKeys;
 
-    UniqueKeyUtils::FetchUniqueKeys(context->priv().globalCache(), &origKeys);
+    UniqueKeyUtils::FetchUniqueKeys(fPrecompileContext.get(), &origKeys);
 
-    SkDEBUGCODE(int numBeforeReset = context->priv().globalCache()->numGraphicsPipelines();)
+    SkDEBUGCODE(int numBeforeReset = globalCache->numGraphicsPipelines();)
     SkASSERT(numBeforeReset == (int) origKeys.size());
 
-    context->priv().globalCache()->resetGraphicsPipelines();
+    fPrecompileContext->priv().globalCache()->resetGraphicsPipelines();
 
-    SkASSERT(context->priv().globalCache()->numGraphicsPipelines() == 0);
+    SkASSERT(globalCache->numGraphicsPipelines() == 0);
 
     for (const skgpu::UniqueKey& k : origKeys) {
         // TODO: add a separate path that decomposes the keys into PaintOptions
@@ -2304,43 +2310,41 @@
         GraphicsPipelineDesc pipelineDesc;
         RenderPassDesc renderPassDesc;
 
-        if (!UniqueKeyUtils::ExtractKeyDescs(context, k, &pipelineDesc, &renderPassDesc)) {
+        if (!UniqueKeyUtils::ExtractKeyDescs(fPrecompileContext.get(), k,
+                                             &pipelineDesc, &renderPassDesc)) {
             continue;
         }
 
-        Precompile(context, rteDict, pipelineDesc, renderPassDesc);
+        AndroidSpecificPrecompile(fPrecompileContext.get(), rteDict,
+                                  pipelineDesc, renderPassDesc);
     }
 
-    SkDEBUGCODE(int postRecreate = context->priv().globalCache()->numGraphicsPipelines();)
+    SkDEBUGCODE(int postRecreate = globalCache->numGraphicsPipelines();)
 
     SkASSERT(numBeforeReset == postRecreate);
 
     {
         std::vector<skgpu::UniqueKey> recreatedKeys;
 
-        UniqueKeyUtils::FetchUniqueKeys(context->priv().globalCache(), &recreatedKeys);
+        UniqueKeyUtils::FetchUniqueKeys(fPrecompileContext.get(), &recreatedKeys);
 
         for (const skgpu::UniqueKey& origKey : origKeys) {
             if(std::find(recreatedKeys.begin(), recreatedKeys.end(), origKey) ==
                          recreatedKeys.end()) {
-                sk_sp<GraphicsPipeline> pipeline =
-                        context->priv().globalCache()->findGraphicsPipeline(origKey);
+                sk_sp<GraphicsPipeline> pipeline = globalCache->findGraphicsPipeline(origKey);
                 SkASSERT(!pipeline);
 
 #ifdef SK_DEBUG
-                const RendererProvider* rendererProvider = context->priv().rendererProvider();
-                const ShaderCodeDictionary* dict = context->priv().shaderCodeDictionary();
-
                 {
                     GraphicsPipelineDesc originalPipelineDesc;
                     RenderPassDesc originalRenderPassDesc;
-                    UniqueKeyUtils::ExtractKeyDescs(context, origKey,
+                    UniqueKeyUtils::ExtractKeyDescs(fPrecompileContext.get(), origKey,
                                                     &originalPipelineDesc,
                                                     &originalRenderPassDesc);
 
                     SkDebugf("------- Missing key from rebuilt keys:\n");
                     origKey.dump("original key:");
-                    UniqueKeyUtils::DumpDescs(rendererProvider, dict,
+                    UniqueKeyUtils::DumpDescs(fPrecompileContext.get(),
                                               originalPipelineDesc,
                                               originalRenderPassDesc);
                 }
@@ -2351,13 +2355,13 @@
 
                     GraphicsPipelineDesc recreatedPipelineDesc;
                     RenderPassDesc recreatedRenderPassDesc;
-                    UniqueKeyUtils::ExtractKeyDescs(context, recreatedKey,
+                    UniqueKeyUtils::ExtractKeyDescs(fPrecompileContext.get(), recreatedKey,
                                                     &recreatedPipelineDesc,
                                                     &recreatedRenderPassDesc);
 
                     SkDebugf("%d ----\n", count++);
                     recreatedKey.dump("recreated key:");
-                    UniqueKeyUtils::DumpDescs(rendererProvider, dict,
+                    UniqueKeyUtils::DumpDescs(fPrecompileContext.get(),
                                               recreatedPipelineDesc,
                                               recreatedRenderPassDesc);
                 }
@@ -2403,7 +2407,7 @@
 
     // Call resetAndRecreatePipelines to clear out all the Pipelines in the global cache and then
     // regenerate them using the Precompilation system.
-    result = this->resetAndRecreatePipelines(context);
+    result = this->resetAndRecreatePipelines();
     if (!result.isOk()) {
         fRecorder.reset();
         return result;
diff --git a/dm/DMSrcSink.h b/dm/DMSrcSink.h
index 0f0bfa7..4881afb 100644
--- a/dm/DMSrcSink.h
+++ b/dm/DMSrcSink.h
@@ -13,6 +13,7 @@
 #include "include/core/SkCanvas.h"
 #include "include/core/SkPicture.h"
 #include "include/docs/SkMultiPictureDocument.h"
+#include "include/gpu/graphite/PrecompileContext.h"
 #include "tools/flags/CommonFlagsConfig.h"
 #include "tools/gpu/MemoryCache.h"
 
@@ -616,9 +617,10 @@
     Result drawSrc(const Src&,
                    skgpu::graphite::Context*,
                    skiatest::graphite::GraphiteTestContext*) const;
-    Result resetAndRecreatePipelines(skgpu::graphite::Context*) const;
+    Result resetAndRecreatePipelines() const;
 
     mutable std::unique_ptr<skgpu::graphite::Recorder> fRecorder;
+    mutable std::unique_ptr<skgpu::graphite::PrecompileContext> fPrecompileContext;
 };
 #endif // SK_ENABLE_PRECOMPILE
 #endif // SK_GRAPHITE
diff --git a/gn/graphite.gni b/gn/graphite.gni
index 665d077..e8de6980 100644
--- a/gn/graphite.gni
+++ b/gn/graphite.gni
@@ -16,6 +16,7 @@
   "$_include/GraphiteTypes.h",
   "$_include/Image.h",
   "$_include/ImageProvider.h",
+  "$_include/PrecompileContext.h",
   "$_include/Recorder.h",
   "$_include/Recording.h",
   "$_include/Surface.h",
@@ -101,6 +102,8 @@
   "$_src/PathAtlas.cpp",
   "$_src/PathAtlas.h",
   "$_src/PipelineData.h",
+  "$_src/PrecompileContext.cpp",
+  "$_src/PrecompileContextPriv.h",
   "$_src/ProxyCache.cpp",
   "$_src/ProxyCache.h",
   "$_src/QueueManager.cpp",
@@ -378,9 +381,9 @@
 ]
 
 skia_graphite_precompile_sources = [
+  "$_src/AndroidSpecificPrecompile.h",
   "$_src/PrecompileInternal.h",
   "$_src/PublicPrecompile.cpp",
-  "$_src/PublicPrecompile.h",
   "$_src/precompile/PaintOption.cpp",
   "$_src/precompile/PaintOption.h",
   "$_src/precompile/PaintOptions.cpp",
diff --git a/include/gpu/graphite/BUILD.bazel b/include/gpu/graphite/BUILD.bazel
index e5bbbf1..8369dd0 100644
--- a/include/gpu/graphite/BUILD.bazel
+++ b/include/gpu/graphite/BUILD.bazel
@@ -16,6 +16,7 @@
         "GraphiteTypes.h",
         "Image.h",
         "ImageProvider.h",
+        "PrecompileContext.h",
         "Recorder.h",
         "Recording.h",
         "Surface.h",
diff --git a/include/gpu/graphite/Context.h b/include/gpu/graphite/Context.h
index 2112412..d751c70 100644
--- a/include/gpu/graphite/Context.h
+++ b/include/gpu/graphite/Context.h
@@ -33,7 +33,7 @@
 class ContextPriv;
 class GlobalCache;
 class PaintOptions;
-class PlotUploadTracker;
+class PrecompileContext;
 class QueueManager;
 class Recording;
 class ResourceProvider;
@@ -53,6 +53,11 @@
 
     std::unique_ptr<Recorder> makeRecorder(const RecorderOptions& = {});
 
+    /** Creates a helper object that can be moved to a different thread and used
+     *  for precompilation.
+     */
+    std::unique_ptr<PrecompileContext> makePrecompileContext();
+
     bool insertRecording(const InsertRecordingInfo&);
     bool submit(SyncToCpu = SyncToCpu::kNo);
 
diff --git a/include/gpu/graphite/PrecompileContext.h b/include/gpu/graphite/PrecompileContext.h
new file mode 100644
index 0000000..47810e9
--- /dev/null
+++ b/include/gpu/graphite/PrecompileContext.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef skgpu_graphite_PrecompileContext_DEFINED
+#define skgpu_graphite_PrecompileContext_DEFINED
+
+#include "include/core/SkRefCnt.h"
+#include "include/private/base/SingleOwner.h"
+
+#include <memory>
+
+namespace skgpu::graphite {
+
+class SharedContext;
+class PrecompileContextPriv;
+class ResourceProvider;
+
+class SK_API PrecompileContext {
+public:
+    ~PrecompileContext();
+
+    // Provides access to functions that aren't part of the public API.
+    PrecompileContextPriv priv();
+    const PrecompileContextPriv priv() const;  // NOLINT(readability-const-return-type)
+
+private:
+    friend class PrecompileContextPriv;
+    friend class Context; // for ctor
+
+    PrecompileContext(sk_sp<SharedContext>);
+
+    mutable SingleOwner fSingleOwner;
+    sk_sp<SharedContext> fSharedContext;
+    std::unique_ptr<ResourceProvider> fResourceProvider;
+};
+
+}  // namespace skgpu::graphite
+
+#endif // skgpu_graphite_PrecompileContext_DEFINED
diff --git a/include/gpu/graphite/precompile/Precompile.h b/include/gpu/graphite/precompile/Precompile.h
index a26c73e..f138539 100644
--- a/include/gpu/graphite/precompile/Precompile.h
+++ b/include/gpu/graphite/precompile/Precompile.h
@@ -15,6 +15,7 @@
 
 class Context;
 class PaintOptions;
+class PrecompileContext;
 
 /**
  *  Describes the required properties of a RenderPass that will be combined with the
@@ -33,11 +34,17 @@
  * drawing. Graphite will always be able to perform an inline compilation if some SkPaint
  * combination was omitted from precompilation.
  *
- *   @param context              the Context to which the actual draws will be submitted
+ *   @param precompileContext    thread-safe helper holding required portions of the Context
  *   @param paintOptions         captures a set of SkPaints that will be drawn
  *   @param drawTypes            communicates which primitives those paints will be drawn with
  *   @param renderPassProperties describes the RenderPasses needed for the desired Pipelines
  */
+void SK_API Precompile(PrecompileContext* precompileContext,
+                       const PaintOptions& paintOptions,
+                       DrawTypeFlags drawTypes,
+                       SkSpan<const RenderPassProperties> renderPassProperties);
+
+// Deprecated. Please use PrecompileContext version.
 void SK_API Precompile(Context* context,
                        const PaintOptions& paintOptions,
                        DrawTypeFlags drawTypes,
diff --git a/relnotes/precompilecontext.md b/relnotes/precompilecontext.md
new file mode 100644
index 0000000..c6c64f5
--- /dev/null
+++ b/relnotes/precompilecontext.md
@@ -0,0 +1,21 @@
+
+A new PrecompileContext object has been added to assist Precompilation. The old API of the form:\
+    bool Precompile(Context*, ...);\
+has been deprecated and replaced with the API:\
+    bool Precompile(PrecompileContext*, ...)\
+The new PrecompileContext object can be obtained via the Context::makePrecompileContext call.
+
+As an example of a possible Compilation/Precompilation threading model, one could employ 4 threads:
+
+2 for creating Recordings (\<r1\> and \<r2\>) \
+1 for precompiling (\<p1\>) \
+and the main thread - which owns the Context and submits Recordings. 
+
+Start up for this scenario would look like:
+
+  the main thread moves a PrecompileContext to <p1> and begins precompiling there\
+  the main thread creates two Recorders and moves them to <r1> and <r2> to create Recordings\
+  the main thread continues on - calling Context::insertRecording on the posted Recordings.
+
+The PrecompileContext can safely outlive the Context that created it, but it will 
+effectively be shut down at that point.
diff --git a/src/gpu/graphite/PublicPrecompile.h b/src/gpu/graphite/AndroidSpecificPrecompile.h
similarity index 68%
rename from src/gpu/graphite/PublicPrecompile.h
rename to src/gpu/graphite/AndroidSpecificPrecompile.h
index 142cd6d..8b4beee 100644
--- a/src/gpu/graphite/PublicPrecompile.h
+++ b/src/gpu/graphite/AndroidSpecificPrecompile.h
@@ -10,8 +10,8 @@
 
 #include "include/gpu/graphite/GraphiteTypes.h"
 
-// TODO: this header should be moved to include/gpu/graphite once the precompilation API
-// is made public
+// TODO: this header should be moved to include/gpu/graphite once the Android-specific
+//  precompilation API is made public
 namespace skgpu::graphite {
 
 class Context;
@@ -19,16 +19,16 @@
 class PaintOptions;
 struct RenderPassDesc;
 class RuntimeEffectDictionary;
-
+class PrecompileContext;
 
 /*
  * TODO: Rather than passing in a pipelineDesc and renderPassDesc we need to add an
  * opaque serializable object that contains the same information.
  */
-bool Precompile(Context*,
-                RuntimeEffectDictionary* rteDict,
-                const GraphicsPipelineDesc& pipelineDesc,
-                const RenderPassDesc& renderPassDesc);
+bool AndroidSpecificPrecompile(PrecompileContext*,
+                               RuntimeEffectDictionary* rteDict,
+                               const GraphicsPipelineDesc& pipelineDesc,
+                               const RenderPassDesc& renderPassDesc);
 
 } // namespace skgpu::graphite
 
diff --git a/src/gpu/graphite/BUILD.bazel b/src/gpu/graphite/BUILD.bazel
index 5efc831..b1db7f6 100644
--- a/src/gpu/graphite/BUILD.bazel
+++ b/src/gpu/graphite/BUILD.bazel
@@ -136,6 +136,8 @@
     "TextureProxyView.h",
     "TextureUtils.cpp",
     "TextureUtils.h",
+    "PrecompileContext.cpp",
+    "PrecompileContextPriv.h",
     "Uniform.h",
     "UniformManager.cpp",
     "UniformManager.h",
@@ -155,7 +157,7 @@
     "FactoryFunctions.h",
     "PrecompileInternal.h",
     "PublicPrecompile.cpp",
-    "PublicPrecompile.h",
+    "AndroidSpecificPrecompile.h",
 ]
 
 split_srcs_and_hdrs(
diff --git a/src/gpu/graphite/Context.cpp b/src/gpu/graphite/Context.cpp
index c1fe543..cdd792d 100644
--- a/src/gpu/graphite/Context.cpp
+++ b/src/gpu/graphite/Context.cpp
@@ -12,6 +12,7 @@
 #include "include/core/SkTraceMemoryDump.h"
 #include "include/effects/SkRuntimeEffect.h"
 #include "include/gpu/graphite/BackendTexture.h"
+#include "include/gpu/graphite/PrecompileContext.h"
 #include "include/gpu/graphite/Recorder.h"
 #include "include/gpu/graphite/Recording.h"
 #include "include/gpu/graphite/Surface.h"
@@ -146,6 +147,12 @@
     return recorder;
 }
 
+std::unique_ptr<PrecompileContext> Context::makePrecompileContext() {
+    ASSERT_SINGLE_OWNER
+
+    return std::unique_ptr<PrecompileContext>(new PrecompileContext(fSharedContext));
+}
+
 std::unique_ptr<Recorder> Context::makeInternalRecorder() const {
     ASSERT_SINGLE_OWNER
 
diff --git a/src/gpu/graphite/ContextPriv.h b/src/gpu/graphite/ContextPriv.h
index 24cd25a..d95d972 100644
--- a/src/gpu/graphite/ContextPriv.h
+++ b/src/gpu/graphite/ContextPriv.h
@@ -37,12 +37,14 @@
     ShaderCodeDictionary* shaderCodeDictionary() {
         return fContext->fSharedContext->shaderCodeDictionary();
     }
+#if defined(GPU_TEST_UTILS)
     const GlobalCache* globalCache() const {
         return fContext->fSharedContext->globalCache();
     }
     GlobalCache* globalCache() {
         return fContext->fSharedContext->globalCache();
     }
+#endif
     const RendererProvider* rendererProvider() const {
         return fContext->fSharedContext->rendererProvider();
     }
diff --git a/src/gpu/graphite/PrecompileContext.cpp b/src/gpu/graphite/PrecompileContext.cpp
new file mode 100644
index 0000000..e355279
--- /dev/null
+++ b/src/gpu/graphite/PrecompileContext.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/gpu/graphite/PrecompileContext.h"
+
+#include "src/gpu/graphite/ResourceProvider.h"
+#include "src/gpu/graphite/SharedContext.h"
+
+namespace skgpu::graphite {
+
+#define ASSERT_SINGLE_OWNER SKGPU_ASSERT_SINGLE_OWNER(&fSingleOwner)
+
+PrecompileContext::~PrecompileContext() {
+    ASSERT_SINGLE_OWNER
+}
+
+PrecompileContext::PrecompileContext(sk_sp<SharedContext> sharedContext)
+    : fSharedContext(sharedContext) {
+
+    // ResourceProviders are not thread-safe. Here we create a ResourceProvider
+    // specifically for the thread on which precompilation will occur.
+    static constexpr size_t kEmptyBudget = 0;
+    fResourceProvider = fSharedContext->makeResourceProvider(
+            &fSingleOwner,
+            SK_InvalidGenID,
+            kEmptyBudget,
+            /* avoidBufferAlloc= */ true);
+}
+
+} // namespace skgpu::graphite
diff --git a/src/gpu/graphite/PrecompileContextPriv.h b/src/gpu/graphite/PrecompileContextPriv.h
new file mode 100644
index 0000000..93c24b3
--- /dev/null
+++ b/src/gpu/graphite/PrecompileContextPriv.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2024 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef skgpu_graphite_PrecompileContextPriv_DEFINED
+#define skgpu_graphite_PrecompileContextPriv_DEFINED
+
+#include "include/gpu/graphite/PrecompileContext.h"
+
+class Caps;
+
+namespace skgpu::graphite {
+
+/**
+ * Class that adds methods to PrecompileContext that are only intended for use internal to Skia.
+ * This class is purely a privileged window into PrecompileContext. It should never have additional
+ * data members or virtual methods.
+ */
+class PrecompileContextPriv {
+public:
+    const Caps* caps() const { return fPrecompileContext->fSharedContext->caps(); }
+    const ShaderCodeDictionary* shaderCodeDictionary() const {
+        return fPrecompileContext->fSharedContext->shaderCodeDictionary();
+    }
+    ShaderCodeDictionary* shaderCodeDictionary() {
+        return fPrecompileContext->fSharedContext->shaderCodeDictionary();
+    }
+    const RendererProvider* rendererProvider() const {
+        return fPrecompileContext->fSharedContext->rendererProvider();
+    }
+    SharedContext* sharedContext() {
+        return fPrecompileContext->fSharedContext.get();
+    }
+    ResourceProvider* resourceProvider() {
+        return fPrecompileContext->fResourceProvider.get();
+    }
+#if defined(GPU_TEST_UTILS)
+    GlobalCache* globalCache() {
+        return fPrecompileContext->fSharedContext->globalCache();
+    }
+#endif
+
+private:
+    friend class PrecompileContext; // to construct/copy this type.
+
+    explicit PrecompileContextPriv(PrecompileContext* precompileContext)
+            : fPrecompileContext(precompileContext) {}
+
+    PrecompileContextPriv& operator=(const PrecompileContextPriv&) = delete;
+
+    // No taking addresses of this type.
+    const PrecompileContextPriv* operator&() const;
+    PrecompileContextPriv *operator&();
+
+    PrecompileContext* fPrecompileContext;
+};
+
+inline PrecompileContextPriv PrecompileContext::priv() { return PrecompileContextPriv(this); }
+
+// NOLINTNEXTLINE(readability-const-return-type)
+inline const PrecompileContextPriv PrecompileContext::priv() const {
+    return PrecompileContextPriv(const_cast<PrecompileContext *>(this));
+}
+
+} // namespace skgpu::graphite
+
+#endif // skgpu_graphite_PrecompileContextPriv_DEFINED
diff --git a/src/gpu/graphite/PublicPrecompile.cpp b/src/gpu/graphite/PublicPrecompile.cpp
index 46788e5..776a97e 100644
--- a/src/gpu/graphite/PublicPrecompile.cpp
+++ b/src/gpu/graphite/PublicPrecompile.cpp
@@ -5,12 +5,12 @@
  * found in the LICENSE file.
  */
 
-#include "src/gpu/graphite/PublicPrecompile.h"
-
 #include "include/core/SkColorSpace.h"
 #include "include/core/SkColorType.h"
+#include "include/gpu/graphite/PrecompileContext.h"
 #include "include/gpu/graphite/precompile/Precompile.h"
 #include "include/gpu/graphite/precompile/PrecompileColorFilter.h"
+#include "src/gpu/graphite/AndroidSpecificPrecompile.h"
 #include "src/gpu/graphite/Caps.h"
 #include "src/gpu/graphite/ContextPriv.h"
 #include "src/gpu/graphite/ContextUtils.h"
@@ -18,6 +18,7 @@
 #include "src/gpu/graphite/GraphicsPipelineDesc.h"
 #include "src/gpu/graphite/KeyContext.h"
 #include "src/gpu/graphite/Log.h"
+#include "src/gpu/graphite/PrecompileContextPriv.h"
 #include "src/gpu/graphite/PrecompileInternal.h"
 #include "src/gpu/graphite/RenderPassDesc.h"
 #include "src/gpu/graphite/Renderer.h"
@@ -80,11 +81,11 @@
 
 namespace skgpu::graphite {
 
-bool Precompile(Context* context,
-                RuntimeEffectDictionary* rteDict,
-                const GraphicsPipelineDesc& pipelineDesc,
-                const RenderPassDesc& renderPassDesc) {
-    ResourceProvider* resourceProvider = context->priv().resourceProvider();
+bool AndroidSpecificPrecompile(PrecompileContext* precompileContext,
+                               RuntimeEffectDictionary* rteDict,
+                               const GraphicsPipelineDesc& pipelineDesc,
+                               const RenderPassDesc& renderPassDesc) {
+    ResourceProvider* resourceProvider = precompileContext->priv().resourceProvider();
 
     sk_sp<GraphicsPipeline> pipeline = resourceProvider->findOrCreateGraphicsPipeline(
             rteDict,
@@ -99,28 +100,26 @@
     return true;
 }
 
-void Precompile(Context* context, const PaintOptions& options, DrawTypeFlags drawTypes,
+/* Deprecated */
+void Precompile(Context* context,
+                const PaintOptions& options,
+                DrawTypeFlags drawTypes,
+                SkSpan<const RenderPassProperties> renderPassProperties) {
+    std::unique_ptr<PrecompileContext> precompileContext = context->makePrecompileContext();
+
+    Precompile(precompileContext.get(), options, drawTypes, renderPassProperties);
+}
+
+void Precompile(PrecompileContext* precompileContext,
+                const PaintOptions& options,
+                DrawTypeFlags drawTypes,
                 SkSpan<const RenderPassProperties> renderPassProperties) {
 
-    ShaderCodeDictionary* dict = context->priv().shaderCodeDictionary();
-    const Caps* caps = context->priv().caps();
+    ShaderCodeDictionary* dict = precompileContext->priv().shaderCodeDictionary();
+    const Caps* caps = precompileContext->priv().caps();
 
     auto rtEffectDict = std::make_unique<RuntimeEffectDictionary>();
 
-    // Here we are creating a ResourceProvider for each call to 'Precompile'. The issue is that
-    // 'Precompile' can be called from any number of threads but the ResourceProvider and
-    // its nested ResourceCache were never intended to be thread-safe. This allocation fixes
-    // the thread-safety issue but at the cost of excessive (re)allocations.
-    // TODO(b/373849852): implement a better solution to the Precompile thread-safety problem.
-    SharedContext* sharedContext = context->priv().sharedContext();
-    SingleOwner singleOwner;
-    static constexpr size_t kDefaultBudgetInBytes = 0;
-    std::unique_ptr<ResourceProvider> tmpResourceProvider = sharedContext->makeResourceProvider(
-            &singleOwner,
-            SK_InvalidGenID,
-            kDefaultBudgetInBytes,
-            /* avoidBufferAlloc= */ true);
-
     for (const RenderPassProperties& rpp : renderPassProperties) {
         // TODO: Allow the client to pass in mipmapping and protection too?
         TextureInfo info = caps->getDefaultSampledTextureInfo(rpp.fDstCT,
@@ -162,8 +161,9 @@
 
             for (Coverage coverage : { Coverage::kNone, Coverage::kSingleChannel }) {
                 PrecompileCombinations(
-                        context->priv().rendererProvider(),
-                        tmpResourceProvider.get(), options, keyContext,
+                        precompileContext->priv().rendererProvider(),
+                        precompileContext->priv().resourceProvider(),
+                        options, keyContext,
                         static_cast<DrawTypeFlags>(drawTypes & ~(DrawTypeFlags::kBitmapText_Color |
                                                                  DrawTypeFlags::kBitmapText_LCD |
                                                                  DrawTypeFlags::kSDFText_LCD |
@@ -179,8 +179,8 @@
                 tmp.setShaders({});
 
                 // ARGB text doesn't emit coverage and always has a primitive blender
-                PrecompileCombinations(context->priv().rendererProvider(),
-                                       tmpResourceProvider.get(),
+                PrecompileCombinations(precompileContext->priv().rendererProvider(),
+                                       precompileContext->priv().resourceProvider(),
                                        tmp,
                                        keyContext,
                                        DrawTypeFlags::kBitmapText_Color,
@@ -192,8 +192,9 @@
             if (drawTypes & (DrawTypeFlags::kBitmapText_LCD | DrawTypeFlags::kSDFText_LCD)) {
                 // LCD-based text always emits LCD coverage but never has primitiveBlenders
                 PrecompileCombinations(
-                        context->priv().rendererProvider(),
-                        tmpResourceProvider.get(), options, keyContext,
+                        precompileContext->priv().rendererProvider(),
+                        precompileContext->priv().resourceProvider(),
+                        options, keyContext,
                         static_cast<DrawTypeFlags>(drawTypes & (DrawTypeFlags::kBitmapText_LCD |
                                                                 DrawTypeFlags::kSDFText_LCD)),
                         /* withPrimitiveBlender= */ false,
@@ -205,8 +206,9 @@
                 // drawVertices w/ colors use a primitiveBlender while those w/o don't. It never
                 // emits coverage.
                 for (bool withPrimitiveBlender : { true, false }) {
-                    PrecompileCombinations(context->priv().rendererProvider(),
-                                           tmpResourceProvider.get(), options, keyContext,
+                    PrecompileCombinations(precompileContext->priv().rendererProvider(),
+                                           precompileContext->priv().resourceProvider(),
+                                           options, keyContext,
                                            DrawTypeFlags::kDrawVertices,
                                            withPrimitiveBlender,
                                            Coverage::kNone,
diff --git a/tests/graphite/precompile/ChromePrecompileTest.cpp b/tests/graphite/precompile/ChromePrecompileTest.cpp
index 7e3b2db..f626e0e 100644
--- a/tests/graphite/precompile/ChromePrecompileTest.cpp
+++ b/tests/graphite/precompile/ChromePrecompileTest.cpp
@@ -10,6 +10,7 @@
 #if defined(SK_GRAPHITE)
 
 #include "include/gpu/graphite/Context.h"
+#include "include/gpu/graphite/PrecompileContext.h"
 #include "include/gpu/graphite/precompile/PaintOptions.h"
 #include "include/gpu/graphite/precompile/Precompile.h"
 #include "include/gpu/graphite/precompile/PrecompileColorFilter.h"
@@ -17,6 +18,7 @@
 #include "src/gpu/graphite/ContextPriv.h"
 #include "src/gpu/graphite/ContextUtils.h"
 #include "src/gpu/graphite/GraphicsPipelineDesc.h"
+#include "src/gpu/graphite/PrecompileContextPriv.h"
 #include "src/gpu/graphite/RenderPassDesc.h"
 #include "src/gpu/graphite/RendererProvider.h"
 #include "tools/graphite/UniqueKeyUtils.h"
@@ -102,7 +104,7 @@
 // the expected string is in the generated set.
 // Additionally, verify that overgeneration is within expected tolerances.
 // If you add an additional RenderStep you may need to increase the tolerance values.
-void run_test(Context* context,
+void run_test(PrecompileContext* precompileContext,
               skiatest::Reporter* reporter,
               const char* expectedString, size_t caseID,
               const PaintOptions& paintOptions,
@@ -110,24 +112,24 @@
               const RenderPassProperties& renderPassSettings,
               unsigned int allowedOvergeneration) {
 
-    context->priv().globalCache()->resetGraphicsPipelines();
+    precompileContext->priv().globalCache()->resetGraphicsPipelines();
 
-    Precompile(context, paintOptions, drawType, { &renderPassSettings, 1 });
+    Precompile(precompileContext, paintOptions, drawType, { &renderPassSettings, 1 });
 
     std::vector<std::string> generated;
 
     {
-        const RendererProvider* rendererProvider = context->priv().rendererProvider();
-        const ShaderCodeDictionary* dict = context->priv().shaderCodeDictionary();
+        const RendererProvider* rendererProvider = precompileContext->priv().rendererProvider();
+        const ShaderCodeDictionary* dict = precompileContext->priv().shaderCodeDictionary();
 
         std::vector<skgpu::UniqueKey> generatedKeys;
 
-        UniqueKeyUtils::FetchUniqueKeys(context->priv().globalCache(), &generatedKeys);
+        UniqueKeyUtils::FetchUniqueKeys(precompileContext, &generatedKeys);
 
         for (const skgpu::UniqueKey& key : generatedKeys) {
             GraphicsPipelineDesc pipelineDesc;
             RenderPassDesc renderPassDesc;
-            UniqueKeyUtils::ExtractKeyDescs(context, key, &pipelineDesc, &renderPassDesc);
+            UniqueKeyUtils::ExtractKeyDescs(precompileContext, key, &pipelineDesc, &renderPassDesc);
 
             const RenderStep* renderStep = rendererProvider->lookup(pipelineDesc.renderStepID());
             generated.push_back(GetPipelineLabel(dict, renderPassDesc, renderStep,
@@ -181,7 +183,8 @@
 DEF_GRAPHITE_TEST_FOR_CONTEXTS(ChromePrecompileTest, is_dawn_metal_context_type,
                                reporter, context, /* testContext */, CtsEnforcement::kNever) {
 
-    const Caps* caps = context->priv().caps();
+    std::unique_ptr<PrecompileContext> precompileContext = context->makePrecompileContext();
+    const skgpu::graphite::Caps* caps = precompileContext->priv().caps();
 
     TextureInfo textureInfo = caps->getDefaultSampledTextureInfo(kBGRA_8888_SkColorType,
                                                                  skgpu::Mipmapped::kNo,
@@ -382,7 +385,7 @@
             allowedOvergeneration *= 2; // due to ExpandResolveTextureLoadOp
         }
 
-        run_test(context, reporter,
+        run_test(precompileContext.get(), reporter,
                  kCases[i], i,
                  paintOptions, drawTypeFlags, renderPassSettings, allowedOvergeneration);
     }
diff --git a/tests/graphite/precompile/PaintParamsKeyTest.cpp b/tests/graphite/precompile/PaintParamsKeyTest.cpp
index b3b54ff..fce421b 100644
--- a/tests/graphite/precompile/PaintParamsKeyTest.cpp
+++ b/tests/graphite/precompile/PaintParamsKeyTest.cpp
@@ -35,6 +35,7 @@
 #include "include/gpu/graphite/Image.h"
 #include "include/gpu/graphite/Recorder.h"
 #include "include/gpu/graphite/Surface.h"
+#include "include/gpu/graphite/PrecompileContext.h"
 #include "include/gpu/graphite/precompile/Precompile.h"
 #include "include/gpu/graphite/precompile/PrecompileBlender.h"
 #include "include/gpu/graphite/precompile/PrecompileColorFilter.h"
@@ -53,6 +54,7 @@
 #include "src/gpu/graphite/KeyHelpers.h"
 #include "src/gpu/graphite/PaintParams.h"
 #include "src/gpu/graphite/PipelineData.h"
+#include "src/gpu/graphite/PrecompileContextPriv.h"
 #include "src/gpu/graphite/RecorderPriv.h"
 #include "src/gpu/graphite/RenderPassDesc.h"
 #include "src/gpu/graphite/Renderer.h"
@@ -1814,13 +1816,11 @@
 }
 
 #ifdef SK_DEBUG
-void dump_keys(Context* context,
+void dump_keys(PrecompileContext* precompileContext,
                const std::vector<skgpu::UniqueKey>& needleKeys,
                const std::vector<skgpu::UniqueKey>& hayStackKeys,
                const char* needleName,
                const char* haystackName) {
-    const RendererProvider* rendererProvider = context->priv().rendererProvider();
-    const ShaderCodeDictionary* dict = context->priv().shaderCodeDictionary();
 
     SkDebugf("-------------------------- %zu %s pipelines\n", needleKeys.size(), needleName);
 
@@ -1830,7 +1830,7 @@
 
         GraphicsPipelineDesc originalPipelineDesc;
         RenderPassDesc originalRenderPassDesc;
-        UniqueKeyUtils::ExtractKeyDescs(context, k,
+        UniqueKeyUtils::ExtractKeyDescs(precompileContext, k,
                                         &originalPipelineDesc,
                                         &originalRenderPassDesc);
 
@@ -1838,7 +1838,7 @@
         label.appendf("--- %s key %d (%s in %s):\n",
                       needleName, count++, found ? "found" : "not-found", haystackName);
         k.dump(label.c_str());
-        UniqueKeyUtils::DumpDescs(rendererProvider, dict,
+        UniqueKeyUtils::DumpDescs(precompileContext,
                                   originalPipelineDesc,
                                   originalRenderPassDesc);
     }
@@ -1847,6 +1847,7 @@
 
 void check_draw(skiatest::Reporter* reporter,
                 Context* context,
+                PrecompileContext* precompileContext,
                 skiatest::graphite::GraphiteTestContext* testContext,
                 Recorder* recorder,
                 const SkPaint& paint,
@@ -1857,9 +1858,9 @@
 
     std::vector<skgpu::UniqueKey> precompileKeys, drawKeys;
 
-    UniqueKeyUtils::FetchUniqueKeys(context->priv().globalCache(), &precompileKeys);
+    UniqueKeyUtils::FetchUniqueKeys(precompileContext, &precompileKeys);
 
-    context->priv().globalCache()->resetGraphicsPipelines();
+    precompileContext->priv().globalCache()->resetGraphicsPipelines();
 
     {
         // TODO: vary the colorType of the target surface too
@@ -1939,7 +1940,7 @@
         testContext->syncedSubmit(context);
     }
 
-    UniqueKeyUtils::FetchUniqueKeys(context->priv().globalCache(), &drawKeys);
+    UniqueKeyUtils::FetchUniqueKeys(precompileContext, &drawKeys);
 
     // Actually using the SkPaint with the specified type of draw shouldn't have added
     // any additional pipelines
@@ -1957,8 +1958,8 @@
                     precompileKeys.size(), drawKeys.size(), missingPipelines);
 #ifdef SK_DEBUG
     if (missingPipelines) {
-        dump_keys(context, drawKeys, precompileKeys, "draw", "precompile");
-        dump_keys(context, precompileKeys, drawKeys, "precompile", "draw");
+        dump_keys(precompileContext, drawKeys, precompileKeys, "draw", "precompile");
+        dump_keys(precompileContext, precompileKeys, drawKeys, "precompile", "draw");
     }
 #endif // SK_DEBUG
 
@@ -2112,6 +2113,7 @@
 // Precompile system will, at least, generate all the pipelines a real draw would generate.
 void precompile_vs_real_draws_subtest(skiatest::Reporter* reporter,
                                       Context* context,
+                                      PrecompileContext* precompileContext,
                                       skiatest::graphite::GraphiteTestContext* testContext,
                                       Recorder* recorder,
                                       const SkPaint& paint,
@@ -2120,9 +2122,11 @@
                                       sk_sp<SkShader> clipShader,
                                       DrawTypeFlags dt,
                                       bool /* verbose */) {
-    context->priv().globalCache()->resetGraphicsPipelines();
+    GlobalCache* globalCache = precompileContext->priv().globalCache();
 
-    const Caps* caps = context->priv().caps();
+    globalCache->resetGraphicsPipelines();
+
+    const skgpu::graphite::Caps* caps = context->priv().caps();
 
     const SkColorType kColorType = kBGRA_8888_SkColorType;
 
@@ -2151,21 +2155,23 @@
                                                                  ? &kDepth_Stencil_4
                                                                  : &kDepth_1;
 
-    int before = context->priv().globalCache()->numGraphicsPipelines();
-    Precompile(context, paintOptions, dt,
+    int before = globalCache->numGraphicsPipelines();
+    Precompile(precompileContext, paintOptions, dt,
                dt == kNonSimpleShape ? SkSpan(pathProperties, 1) : SkSpan(&kDepth_1, 1));
     if (gNeedSKPPaintOption) {
         // The skp draws a rect w/ a default SkPaint
         PaintOptions skpPaintOptions;
-        Precompile(context, skpPaintOptions, DrawTypeFlags::kSimpleShape, { kDepth_1 });
+        Precompile(precompileContext, skpPaintOptions, DrawTypeFlags::kSimpleShape,
+                   { kDepth_1 });
     }
-    int after = context->priv().globalCache()->numGraphicsPipelines();
+    int after = globalCache->numGraphicsPipelines();
 
     REPORTER_ASSERT(reporter, before == 0);
     REPORTER_ASSERT(reporter, after > before);
 
     check_draw(reporter,
                context,
+               precompileContext,
                testContext,
                recorder,
                paint,
@@ -2176,6 +2182,7 @@
 
 void run_test(skiatest::Reporter* reporter,
               Context* context,
+              PrecompileContext* precompileContext,
               skiatest::graphite::GraphiteTestContext* testContext,
               const KeyContext& precompileKeyContext,
               ShaderType s,
@@ -2210,7 +2217,8 @@
     extract_vs_build_subtest(reporter, context, testContext, precompileKeyContext, recorder.get(),
                              paint, paintOptions, s, bm, cf, mf, imageFilter, clip, clipShader, dt,
                              seed, &rand, verbose);
-    precompile_vs_real_draws_subtest(reporter, context, testContext, recorder.get(),
+    precompile_vs_real_draws_subtest(reporter, context, precompileContext,
+                                     testContext, recorder.get(),
                                      paint, paintOptions, clip, clipShader, dt, verbose);
 }
 
@@ -2222,6 +2230,7 @@
                                                testContext,
                                                true,
                                                CtsEnforcement::kNever) {
+    std::unique_ptr<PrecompileContext> precompileContext = context->makePrecompileContext();
     std::unique_ptr<RuntimeEffectDictionary> rtDict = std::make_unique<RuntimeEffectDictionary>();
 
 #if 1
@@ -2257,6 +2266,7 @@
 
     run_test(reporter,
              context,
+             precompileContext.get(),
              testContext,
              create_key_context(context, rtDict.get()),
              shaderType,
@@ -2283,6 +2293,7 @@
                                                testContext,
                                                true,
                                                CtsEnforcement::kNever) {
+    std::unique_ptr<PrecompileContext> precompileContext = context->makePrecompileContext();
     std::unique_ptr<RuntimeEffectDictionary> rtDict = std::make_unique<RuntimeEffectDictionary>();
 
     KeyContext precompileKeyContext(create_key_context(context, rtDict.get()));
@@ -2400,7 +2411,8 @@
                                 ++current;
 #endif
 
-                                run_test(reporter, context, testContext, precompileKeyContext,
+                                run_test(reporter, context, precompileContext.get(),
+                                         testContext, precompileKeyContext,
                                          shader, blender, cf, mf, imageFilter, clip, dt,
                                          kDefaultSeed, /* verbose= */ false);
                             }
diff --git a/tests/graphite/precompile/ThreadedPrecompileTest.cpp b/tests/graphite/precompile/ThreadedPrecompileTest.cpp
index d8c0900..bd3eb37 100644
--- a/tests/graphite/precompile/ThreadedPrecompileTest.cpp
+++ b/tests/graphite/precompile/ThreadedPrecompileTest.cpp
@@ -9,6 +9,7 @@
 
 #if defined(SK_GRAPHITE)
 
+#include "include/gpu/graphite/PrecompileContext.h"
 #include "include/gpu/graphite/precompile/PaintOptions.h"
 #include "include/gpu/graphite/precompile/Precompile.h"
 #include "include/gpu/graphite/precompile/PrecompileShader.h"
@@ -49,7 +50,7 @@
     return paintOptions;
 }
 
-void precompile_gradients(Context* context,
+void precompile_gradients(std::unique_ptr<PrecompileContext> precompileContext,
                           skiatest::Reporter* reporter,
                           int threadID) {
     constexpr RenderPassProperties kProps = { DepthStencilFlags::kDepth,
@@ -58,11 +59,13 @@
 
     for (auto createOptionsMtd : { linear, radial, sweep, conical }) {
         PaintOptions paintOptions = createOptionsMtd();
-        Precompile(context,
+        Precompile(precompileContext.get(),
                    paintOptions,
                    DrawTypeFlags::kBitmapText_Mask,
                    { &kProps, 1 });
     }
+
+    precompileContext.reset();
 }
 
 } // anonymous namespace
@@ -75,11 +78,12 @@
                                    CtsEnforcement::kNever) {
     constexpr int kNumThreads = 4;
 
+
     std::thread threads[kNumThreads];
     for (int i = 0; i < kNumThreads; ++i) {
-        threads[i] = std::thread([context, reporter, i]() {
-            precompile_gradients(context, reporter, i);
-        });
+        std::unique_ptr<PrecompileContext> precompileContext = context->makePrecompileContext();
+
+        threads[i] = std::thread(precompile_gradients, std::move(precompileContext), reporter, i);
     }
 
     for (auto& thread : threads) {
diff --git a/tools/graphite/UniqueKeyUtils.cpp b/tools/graphite/UniqueKeyUtils.cpp
index 1581a4c..79900e8 100644
--- a/tools/graphite/UniqueKeyUtils.cpp
+++ b/tools/graphite/UniqueKeyUtils.cpp
@@ -11,6 +11,7 @@
 #include "src/gpu/graphite/Caps.h"
 #include "src/gpu/graphite/ContextPriv.h"
 #include "src/gpu/graphite/GraphicsPipelineDesc.h"
+#include "src/gpu/graphite/PrecompileContextPriv.h"
 #include "src/gpu/graphite/RenderPassDesc.h"
 #include "src/gpu/graphite/RendererProvider.h"
 
@@ -20,8 +21,10 @@
 
 namespace UniqueKeyUtils {
 
-void FetchUniqueKeys(GlobalCache* globalCache,
+void FetchUniqueKeys(PrecompileContext* precompileContext,
                      std::vector<UniqueKey>* keys) {
+    GlobalCache* globalCache = precompileContext->priv().globalCache();
+
     keys->reserve(globalCache->numGraphicsPipelines());
     globalCache->forEachGraphicsPipeline([keys](const UniqueKey& key,
                                                 const GraphicsPipeline* pipeline) {
@@ -30,10 +33,12 @@
 }
 
 #ifdef SK_DEBUG
-void DumpDescs(const RendererProvider* rendererProvider,
-               const ShaderCodeDictionary* dict,
+void DumpDescs(PrecompileContext* precompileContext,
                const GraphicsPipelineDesc& pipelineDesc,
                const RenderPassDesc& rpd) {
+    const RendererProvider* rendererProvider = precompileContext->priv().rendererProvider();
+    const ShaderCodeDictionary* dict = precompileContext->priv().shaderCodeDictionary();
+
     const RenderStep* rs = rendererProvider->lookup(pipelineDesc.renderStepID());
     SkDebugf("GraphicsPipelineDesc: %u %s\n", pipelineDesc.paintParamsID().asUInt(), rs->name());
 
@@ -57,12 +62,12 @@
 }
 #endif // SK_DEBUG
 
-bool ExtractKeyDescs(Context* context,
+bool ExtractKeyDescs(PrecompileContext* precompileContext,
                      const UniqueKey& origKey,
                      GraphicsPipelineDesc* pipelineDesc,
                      RenderPassDesc* renderPassDesc) {
-    const Caps* caps = context->priv().caps();
-    const RendererProvider* rendererProvider = context->priv().rendererProvider();
+    const skgpu::graphite::Caps* caps = precompileContext->priv().caps();
+    const RendererProvider* rendererProvider = precompileContext->priv().rendererProvider();
 
     bool extracted = caps->extractGraphicsDescs(origKey, pipelineDesc, renderPassDesc,
                                                 rendererProvider);
@@ -72,14 +77,12 @@
     }
 
 #ifdef SK_DEBUG
-    const ShaderCodeDictionary* dict = context->priv().shaderCodeDictionary();
-
     UniqueKey newKey = caps->makeGraphicsPipelineKey(*pipelineDesc, *renderPassDesc);
     if (origKey != newKey) {
         SkDebugf("------- The UniqueKey didn't round trip!\n");
         origKey.dump("original key:");
         newKey.dump("reassembled key:");
-        DumpDescs(rendererProvider, dict, *pipelineDesc, *renderPassDesc);
+        DumpDescs(precompileContext, *pipelineDesc, *renderPassDesc);
         SkDebugf("------------------------\n");
     }
     SkASSERT(origKey == newKey);
diff --git a/tools/graphite/UniqueKeyUtils.h b/tools/graphite/UniqueKeyUtils.h
index 2bef2e2..227d2a0 100644
--- a/tools/graphite/UniqueKeyUtils.h
+++ b/tools/graphite/UniqueKeyUtils.h
@@ -20,6 +20,7 @@
     class Context;
     class GlobalCache;
     class GraphicsPipelineDesc;
+    class PrecompileContext;
     struct RenderPassDesc;
     class RendererProvider;
     class ShaderCodeDictionary;
@@ -27,12 +28,11 @@
 
 namespace UniqueKeyUtils {
 
-void FetchUniqueKeys(skgpu::graphite::GlobalCache* globalCache,
+void FetchUniqueKeys(skgpu::graphite::PrecompileContext*,
                      std::vector<skgpu::UniqueKey>* keys);
 
 #ifdef SK_DEBUG
-void DumpDescs(const skgpu::graphite::RendererProvider*,
-               const skgpu::graphite::ShaderCodeDictionary*,
+void DumpDescs(skgpu::graphite::PrecompileContext*,
                const skgpu::graphite::GraphicsPipelineDesc&,
                const skgpu::graphite::RenderPassDesc&);
 #endif
@@ -40,7 +40,7 @@
 // This helper breaks a UniqueKey down into its GraphicsPipelineDesc
 // and a RenderPassDesc and checks that the reassembled pieces match the
 // original.
-bool ExtractKeyDescs(skgpu::graphite::Context*,
+bool ExtractKeyDescs(skgpu::graphite::PrecompileContext*,
                      const skgpu::UniqueKey&,
                      skgpu::graphite::GraphicsPipelineDesc*,
                      skgpu::graphite::RenderPassDesc*);