[graphite] Add PrecompileContext::precompile option
This connects all the innards of the Android-style precompilation system.
Bug: b/390186667
Change-Id: I3e37a0f1cf0fbe1cab8d6cc25984631314aa498e
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/946057
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 bc04209..67ccb70 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -2302,9 +2302,9 @@
SkASSERT(globalCache->numGraphicsPipelines() == 0);
-#if 0
+#if 1
for (sk_sp<SkData>& d : androidStyleKeys) {
- bool result = precompileContext->androidSpecificPrecompile(d);
+ bool result = precompileContext->precompile(d);
SkAssertResult(result);
}
#else
diff --git a/include/gpu/graphite/PrecompileContext.h b/include/gpu/graphite/PrecompileContext.h
index 2ba475c..acff0ee 100644
--- a/include/gpu/graphite/PrecompileContext.h
+++ b/include/gpu/graphite/PrecompileContext.h
@@ -14,6 +14,8 @@
#include <chrono>
#include <memory>
+class SkData;
+
namespace skgpu::graphite {
class SharedContext;
@@ -32,6 +34,15 @@
*/
void purgePipelinesNotUsedInMs(std::chrono::milliseconds msNotUsed);
+ /**
+ * Precompile one specific Pipeline that has been previously serialized. Serialized pipeline
+ * keys can be acquired via the ContextOptions::PipelineCallback.
+ *
+ * @param serializedPipelineKey serialized Pipeline key.
+ * @return true if a Pipeline was created from the key; false otherwise
+ */
+ bool precompile(sk_sp<SkData> serializedPipelineKey);
+
// Provides access to functions that aren't part of the public API.
PrecompileContextPriv priv();
const PrecompileContextPriv priv() const; // NOLINT(readability-const-return-type)
diff --git a/relnotes/serializedkeyprecompilation.md b/relnotes/serializedkeyprecompilation.md
new file mode 100644
index 0000000..9627484
--- /dev/null
+++ b/relnotes/serializedkeyprecompilation.md
@@ -0,0 +1 @@
+The `PrecompileContext` now allows clients to precompile previously serialized Pipelines via the `PrecompileContext::precompile` entry point. Serialized keys can be obtained by implementing a `ContextOptions::PipelineCallback` handler.
diff --git a/src/gpu/graphite/BUILD.bazel b/src/gpu/graphite/BUILD.bazel
index a590482..007c395 100644
--- a/src/gpu/graphite/BUILD.bazel
+++ b/src/gpu/graphite/BUILD.bazel
@@ -160,6 +160,8 @@
"PrecompileInternal.h",
"PublicPrecompile.cpp",
"AndroidSpecificPrecompile.h",
+ "SerializationUtils.cpp",
+ "SerializationUtils.h",
]
split_srcs_and_hdrs(
diff --git a/src/gpu/graphite/GlobalCache.cpp b/src/gpu/graphite/GlobalCache.cpp
index 63115b4..6e05ec4 100644
--- a/src/gpu/graphite/GlobalCache.cpp
+++ b/src/gpu/graphite/GlobalCache.cpp
@@ -12,7 +12,14 @@
#include "src/gpu/graphite/ComputePipeline.h"
#include "src/gpu/graphite/ContextUtils.h"
#include "src/gpu/graphite/GraphicsPipeline.h"
+#include "src/gpu/graphite/GraphicsPipelineDesc.h"
+#include "src/gpu/graphite/RenderPassDesc.h"
#include "src/gpu/graphite/Resource.h"
+#include "src/gpu/graphite/SharedContext.h"
+
+#if defined(SK_ENABLE_PRECOMPILE)
+#include "src/gpu/graphite/precompile/SerializationUtils.h"
+#endif
namespace {
@@ -52,12 +59,39 @@
}
void GlobalCache::setPipelineCallback(PipelineCallback callback, PipelineCallbackContext context) {
- SkAutoSpinlock lock{ fSpinLock };
+ SkAutoSpinlock lock{fSpinLock};
fPipelineCallback = callback;
fPipelineCallbackContext = context;
}
+void GlobalCache::invokePipelineCallback(SharedContext* sharedContext,
+ const GraphicsPipelineDesc& pipelineDesc,
+ const RenderPassDesc& renderPassDesc) {
+#if defined(SK_ENABLE_PRECOMPILE)
+ PipelineCallback tmpCB = nullptr;
+ PipelineCallbackContext tmpContext = nullptr;
+
+ {
+ // We want to get a consistent callback/context pair but not invoke the callback
+ // w/in our lock.
+ SkAutoSpinlock lock{fSpinLock};
+
+ tmpCB = fPipelineCallback;
+ tmpContext = fPipelineCallbackContext;
+ }
+
+ if (tmpCB) {
+ sk_sp<SkData> data = PipelineDescToData(sharedContext->shaderCodeDictionary(),
+ pipelineDesc,
+ renderPassDesc);
+ if (data) {
+ tmpCB(tmpContext, std::move(data));
+ }
+ }
+#endif
+}
+
void GlobalCache::deleteResources() {
SkAutoSpinlock lock{ fSpinLock };
diff --git a/src/gpu/graphite/GlobalCache.h b/src/gpu/graphite/GlobalCache.h
index a4aa3353..e1a97b2 100644
--- a/src/gpu/graphite/GlobalCache.h
+++ b/src/gpu/graphite/GlobalCache.h
@@ -91,6 +91,9 @@
using PipelineCallback = void (*)(PipelineCallbackContext context, sk_sp<SkData> pipelineData);
void setPipelineCallback(PipelineCallback, PipelineCallbackContext) SK_EXCLUDES(fSpinLock);
+ void invokePipelineCallback(SharedContext*,
+ const GraphicsPipelineDesc&,
+ const RenderPassDesc&);
private:
struct KeyHash {
uint32_t operator()(const UniqueKey& key) const { return key.hash(); }
diff --git a/src/gpu/graphite/PrecompileContext.cpp b/src/gpu/graphite/PrecompileContext.cpp
index 6b5d1c1..1e2d840 100644
--- a/src/gpu/graphite/PrecompileContext.cpp
+++ b/src/gpu/graphite/PrecompileContext.cpp
@@ -7,9 +7,17 @@
#include "include/gpu/graphite/PrecompileContext.h"
+#include "src/gpu/graphite/GraphicsPipelineDesc.h"
+#include "src/gpu/graphite/Log.h"
+#include "src/gpu/graphite/RenderPassDesc.h"
#include "src/gpu/graphite/ResourceProvider.h"
+#include "src/gpu/graphite/RuntimeEffectDictionary.h"
#include "src/gpu/graphite/SharedContext.h"
+#if defined(SK_ENABLE_PRECOMPILE)
+#include "src/gpu/graphite/precompile/SerializationUtils.h"
+#endif
+
namespace skgpu::graphite {
#define ASSERT_SINGLE_OWNER SKGPU_ASSERT_SINGLE_OWNER(&fSingleOwner)
@@ -37,4 +45,35 @@
}
+bool PrecompileContext::precompile(sk_sp<SkData> serializedPipelineKey) {
+#if defined(SK_ENABLE_PRECOMPILE)
+ auto rtEffectDict = std::make_unique<RuntimeEffectDictionary>();
+
+ GraphicsPipelineDesc pipelineDesc;
+ RenderPassDesc renderPassDesc;
+
+ if (!DataToPipelineDesc(fSharedContext->caps(),
+ fSharedContext->shaderCodeDictionary(),
+ serializedPipelineKey.get(),
+ &pipelineDesc,
+ &renderPassDesc)) {
+ return false;
+ }
+
+ sk_sp<GraphicsPipeline> pipeline = fResourceProvider->findOrCreateGraphicsPipeline(
+ rtEffectDict.get(),
+ pipelineDesc,
+ renderPassDesc,
+ PipelineCreationFlags::kForPrecompilation);
+ if (!pipeline) {
+ SKGPU_LOG_W("Failed to create GraphicsPipeline in precompile!");
+ return false;
+ }
+
+ return true;
+#else
+ return false;
+#endif
+}
+
} // namespace skgpu::graphite
diff --git a/src/gpu/graphite/ResourceProvider.cpp b/src/gpu/graphite/ResourceProvider.cpp
index 627fee7..a194262 100644
--- a/src/gpu/graphite/ResourceProvider.cpp
+++ b/src/gpu/graphite/ResourceProvider.cpp
@@ -94,6 +94,7 @@
pipelineCreationFlags,
compilationID);
if (pipeline) {
+ globalCache->invokePipelineCallback(fSharedContext, pipelineDesc, renderPassDesc);
// TODO: Should we store a null pipeline if we failed to create one so that subsequent
// usage immediately sees that the pipeline cannot be created, vs. retrying every time?
pipeline = globalCache->addGraphicsPipeline(pipelineKey, std::move(pipeline));
diff --git a/src/gpu/graphite/precompile/SerializationUtils.cpp b/src/gpu/graphite/precompile/SerializationUtils.cpp
index a6b1bb6..5be46e6 100644
--- a/src/gpu/graphite/precompile/SerializationUtils.cpp
+++ b/src/gpu/graphite/precompile/SerializationUtils.cpp
@@ -230,8 +230,6 @@
return true;
}
-} // anonymous namespace
-
#define SK_BLOB_END_TAG SkSetFourByteTag('e', 'n', 'd', ' ')
bool SerializePipelineDesc(ShaderCodeDictionary* shaderCodeDictionary,
@@ -285,4 +283,39 @@
return true;
}
+} // anonymous namespace
+
+sk_sp<SkData> PipelineDescToData(ShaderCodeDictionary* shaderCodeDictionary,
+ const GraphicsPipelineDesc& pipelineDesc,
+ const RenderPassDesc& renderPassDesc) {
+ SkDynamicMemoryWStream stream;
+
+ if (!SerializePipelineDesc(shaderCodeDictionary,
+ &stream,
+ pipelineDesc, renderPassDesc)) {
+ return nullptr;
+ }
+
+ return stream.detachAsData();
+}
+
+bool DataToPipelineDesc(const Caps* caps,
+ ShaderCodeDictionary* shaderCodeDictionary,
+ const SkData* data,
+ GraphicsPipelineDesc* pipelineDesc,
+ RenderPassDesc* renderPassDesc) {
+ if (!data) {
+ return false;
+ }
+ SkMemoryStream stream(data->data(), data->size());
+
+ if (!DeserializePipelineDesc(caps, shaderCodeDictionary, &stream,
+ pipelineDesc,
+ renderPassDesc)) {
+ return false;
+ }
+
+ return true;
+}
+
} // namespace skgpu::graphite
diff --git a/src/gpu/graphite/precompile/SerializationUtils.h b/src/gpu/graphite/precompile/SerializationUtils.h
index 2e9ebbb..65ce616 100644
--- a/src/gpu/graphite/precompile/SerializationUtils.h
+++ b/src/gpu/graphite/precompile/SerializationUtils.h
@@ -8,30 +8,28 @@
#ifndef skgpu_graphite_precompile_SerializationUtils_DEFINED
#define skgpu_graphite_precompile_SerializationUtils_DEFINED
-#include "include/core/SkTypes.h"
+#include "include/core/SkRefCnt.h"
-class SkStream;
-class SkWStream;
+class SkData;
namespace skgpu::graphite {
class Caps;
class GraphicsPipelineDesc;
-class RendererProvider;
struct RenderPassDesc;
class ShaderCodeDictionary;
// These are the top-level entry points to serialize Pipeline data for the Android-style
// Precompilation API
-[[nodiscard]] bool SerializePipelineDesc(ShaderCodeDictionary*,
- SkWStream*,
- const GraphicsPipelineDesc&,
- const RenderPassDesc&);
-[[nodiscard]] bool DeserializePipelineDesc(const Caps*,
- ShaderCodeDictionary*,
- SkStream*,
- GraphicsPipelineDesc*,
- RenderPassDesc*);
+[[nodiscard]] sk_sp<SkData> PipelineDescToData(ShaderCodeDictionary*,
+ const GraphicsPipelineDesc&,
+ const RenderPassDesc&);
+
+[[nodiscard]] bool DataToPipelineDesc(const Caps*,
+ ShaderCodeDictionary*,
+ const SkData*,
+ GraphicsPipelineDesc* pipelineDesc,
+ RenderPassDesc* renderPassDesc);
} // skgpu::graphite