Skip to content

Commit

Permalink
[graphite] Add PrecompileContext::precompile option
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
rphilli authored and SkCQ committed Feb 4, 2025
1 parent 9212eed commit c9cb75c
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 18 deletions.
4 changes: 2 additions & 2 deletions dm/DMSrcSink.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2302,9 +2302,9 @@ Result GraphitePrecompileTestingSink::resetAndRecreatePipelines(

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
Expand Down
11 changes: 11 additions & 0 deletions include/gpu/graphite/PrecompileContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#include <chrono>
#include <memory>

class SkData;

namespace skgpu::graphite {

class SharedContext;
Expand All @@ -32,6 +34,15 @@ class SK_API PrecompileContext {
*/
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)
Expand Down
1 change: 1 addition & 0 deletions relnotes/serializedkeyprecompilation.md
Original file line number Diff line number Diff line change
@@ -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.
2 changes: 2 additions & 0 deletions src/gpu/graphite/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ PRECOMPILE_FILES = [
"PrecompileInternal.h",
"PublicPrecompile.cpp",
"AndroidSpecificPrecompile.h",
"SerializationUtils.cpp",
"SerializationUtils.h",
]

split_srcs_and_hdrs(
Expand Down
36 changes: 35 additions & 1 deletion src/gpu/graphite/GlobalCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand Down Expand Up @@ -52,12 +59,39 @@ GlobalCache::~GlobalCache() {
}

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 };

Expand Down
3 changes: 3 additions & 0 deletions src/gpu/graphite/GlobalCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@ class GlobalCache {
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(); }
Expand Down
39 changes: 39 additions & 0 deletions src/gpu/graphite/PrecompileContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -37,4 +45,35 @@ void PrecompileContext::purgePipelinesNotUsedInMs(std::chrono::milliseconds msNo
}


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
1 change: 1 addition & 0 deletions src/gpu/graphite/ResourceProvider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ sk_sp<GraphicsPipeline> ResourceProvider::findOrCreateGraphicsPipeline(
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));
Expand Down
37 changes: 35 additions & 2 deletions src/gpu/graphite/precompile/SerializationUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,6 @@ static const char kMagic[] = { 's', 'k', 'i', 'a', 'p', 'i', 'p', 'e' };
return true;
}

} // anonymous namespace

#define SK_BLOB_END_TAG SkSetFourByteTag('e', 'n', 'd', ' ')

bool SerializePipelineDesc(ShaderCodeDictionary* shaderCodeDictionary,
Expand Down Expand Up @@ -285,4 +283,39 @@ bool DeserializePipelineDesc(const Caps* caps,
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
24 changes: 11 additions & 13 deletions src/gpu/graphite/precompile/SerializationUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit c9cb75c

Please sign in to comment.