Add SkShaderCodeDictionary::findOrCreateRuntimeEffectSnippet.

`findOrCreateRuntimeEffectSnippet` takes a pointer to the runtime effect
and returns a code-snippet ID associated with the runtime effect, with
matching uniform layout, etc. The SkShaderCodeDictionary will maintain a
persistent map from {sksl-hash, uniform set} to code-snippet ID.

http://go/runtime-effects-in-graphite

Change-Id: I9c51667aad96f850184899f3df00a8206a1fe354
Bug: skia:13405
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/552686
Auto-Submit: John Stiles <johnstiles@google.com>
Commit-Queue: John Stiles <johnstiles@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
John Stiles 2022-06-27 10:45:45 -04:00 committed by SkCQ
parent 3824e1db80
commit a10e6c1025
3 changed files with 103 additions and 12 deletions

View File

@ -11,6 +11,7 @@
#include "include/effects/SkRuntimeEffect.h"
#include "include/private/SkSLString.h"
#include "src/core/SkOpts.h"
#include "src/core/SkRuntimeEffectPriv.h"
#include "src/sksl/SkSLUtil.h"
#ifdef SK_GRAPHITE_ENABLED
@ -212,6 +213,10 @@ size_t SkShaderCodeDictionary::SkPaintParamsKeyPtr::Hash::operator()(SkPaintPara
return SkOpts::hash_fn(p.fKey->data(), p.fKey->sizeInBytes(), 0);
}
size_t SkShaderCodeDictionary::RuntimeEffectKey::Hash::operator()(RuntimeEffectKey k) const {
return SkOpts::hash_fn(&k, sizeof(k), 0);
}
const SkShaderCodeDictionary::Entry* SkShaderCodeDictionary::findOrCreate(
SkPaintParamsKeyBuilder* builder) {
const SkPaintParamsKey& key = builder->lockAsKey();
@ -696,6 +701,46 @@ SkBlenderID SkShaderCodeDictionary::addUserDefinedBlender(sk_sp<SkRuntimeEffect>
return SkBlenderID(codeSnippetID);
}
int SkShaderCodeDictionary::findOrCreateRuntimeEffectSnippet(const SkRuntimeEffect* effect) {
// Use the combination of {SkSL program hash, uniform size} as our key.
// In the unfortunate event of a hash collision, at least we'll have the right amount of
// uniform data available.
RuntimeEffectKey key;
key.fHash = SkRuntimeEffectPriv::Hash(*effect);
key.fUniformSize = effect->uniformSize();
SkAutoSpinlock lock{fSpinLock};
int32_t* existingCodeSnippetID = fRuntimeEffectMap.find(key);
if (existingCodeSnippetID) {
return *existingCodeSnippetID;
}
// TODO(skia:13457): Convert effect uniforms from Runtime Effect format to Graphite.
// For now, just hard-code the required local-matrix field.
static constexpr SkUniform kUniforms[] = {
{"localMatrix", SkSLType::kFloat4x4},
};
// TODO(skia:13405): consider removing these data fields, they don't seem to add value anymore
static constexpr DataPayloadField kRuntimeShaderDataPayload[] = {
{"runtime effect hash", DataPayloadType::kInt, 1},
{"uniform data size (bytes)", DataPayloadType::kInt, 1},
};
// TODO(skia:13405): arguments to `addUserDefinedSnippet` here are placeholder
int newCodeSnippetID = this->addUserDefinedSnippet("RuntimeEffect",
SkSpan(kUniforms),
SnippetRequirementFlags::kLocalCoords,
/*texturesAndSamplers=*/{},
"sk_runtime_placeholder",
GenerateDefaultGlueCode,
/*numChildren=*/0,
SkSpan(kRuntimeShaderDataPayload));
fRuntimeEffectMap.set(key, newCodeSnippetID);
return newCodeSnippetID;
}
SkShaderCodeDictionary::SkShaderCodeDictionary() {
// The 0th index is reserved as invalid
fEntryVector.push_back(nullptr);

View File

@ -199,17 +199,7 @@ public:
void getShaderInfo(SkUniquePaintParamsID, SkShaderInfo*);
// TODO: this is still experimental but, most likely, it will need to be made thread-safe
// It returns the code snippet ID to use to identify the supplied user-defined code
int addUserDefinedSnippet(
const char* name,
SkSpan<const SkUniform> uniforms,
SnippetRequirementFlags snippetRequirementFlags,
SkSpan<const SkTextureAndSampler> texturesAndSamplers,
const char* functionName,
SkShaderSnippet::GenerateGlueCodeForEntry glueCodeGenerator,
int numChildren,
SkSpan<const SkPaintParamsKey::DataPayloadField> dataPayloadExpectations);
int findOrCreateRuntimeEffectSnippet(const SkRuntimeEffect* effect);
int addUserDefinedSnippet(const char* name,
SkSpan<const SkPaintParamsKey::DataPayloadField> expectations);
@ -223,6 +213,18 @@ private:
Entry* makeEntry(const SkPaintParamsKey&);
#endif
// TODO: this is still experimental but, most likely, it will need to be made thread-safe
// It returns the code snippet ID to use to identify the supplied user-defined code
int addUserDefinedSnippet(
const char* name,
SkSpan<const SkUniform> uniforms,
SnippetRequirementFlags snippetRequirementFlags,
SkSpan<const SkTextureAndSampler> texturesAndSamplers,
const char* functionName,
SkShaderSnippet::GenerateGlueCodeForEntry glueCodeGenerator,
int numChildren,
SkSpan<const SkPaintParamsKey::DataPayloadField> dataPayloadExpectations);
std::array<SkShaderSnippet, kBuiltInCodeSnippetIDCount> fBuiltInCodeSnippets;
// The value returned from 'getEntry' must be stable so, hold the user-defined code snippet
@ -248,6 +250,29 @@ private:
PaintHashMap fHash SK_GUARDED_BY(fSpinLock);
std::vector<Entry*> fEntryVector SK_GUARDED_BY(fSpinLock);
SK_BEGIN_REQUIRE_DENSE
struct RuntimeEffectKey {
uint32_t fHash;
uint32_t fUniformSize;
bool operator==(RuntimeEffectKey rhs) const {
return fHash == rhs.fHash && fUniformSize == rhs.fUniformSize;
}
struct Hash {
size_t operator()(RuntimeEffectKey) const;
};
};
SK_END_REQUIRE_DENSE
// A map from RuntimeEffectKeys (hash plus uniforms) to code-snippet IDs. RuntimeEffectKeys
// don't track the lifetime of a runtime effect at all; they live forever, and a newly-
// instantiated runtime effect with the same program as a previously-discarded effect will reuse
// an existing ID. Entries in the runtime-effect map are never removed; they only disappear when
// the context is discarded, which takes the ShaderCodeDictionary along with it. However, they
// are extremely small (< 20 bytes) so the memory footprint should be unnoticeable.
using RuntimeEffectMap = SkTHashMap<RuntimeEffectKey, int32_t>;
RuntimeEffectMap fRuntimeEffectMap SK_GUARDED_BY(fSpinLock);
// This arena holds:
// the Entries held in 'fHash' and 'fEntryVector' - thus, guarded by 'fSpinLock'
SkArenaAlloc fArena{256};

View File

@ -10,6 +10,7 @@
#include "include/core/SkCombinationBuilder.h"
#include "include/effects/SkRuntimeEffect.h"
#include "include/gpu/graphite/Context.h"
#include "src/core/SkKeyHelpers.h"
#include "src/core/SkShaderCodeDictionary.h"
#include "src/gpu/graphite/ContextPriv.h"
@ -92,7 +93,7 @@ static sk_sp<SkBlender> get_blender(sk_sp<SkRuntimeEffect> comboEffect,
} // anonymous namespace
DEF_GRAPHITE_TEST_FOR_CONTEXTS(RTEffectTest, reporter, context) {
auto dict = context->priv().shaderCodeDictionary();
SkShaderCodeDictionary* dict = context->priv().shaderCodeDictionary();
sk_sp<SkRuntimeEffect> comboEffect = get_combo_effect();
sk_sp<SkRuntimeEffect> redEffect = get_red_effect();
@ -125,3 +126,23 @@ DEF_GRAPHITE_TEST_FOR_CONTEXTS(RTEffectTest, reporter, context) {
// TODO:
// check that the uniforms can be extracted from 'blender' correctly
}
DEF_GRAPHITE_TEST_FOR_CONTEXTS(FindOrCreateSnippetForRuntimeEffectWorks, reporter, context) {
SkShaderCodeDictionary* dict = context->priv().shaderCodeDictionary();
const SkRuntimeEffect* effect = TestingOnly_GetCommonRuntimeEffect();
// Create a new runtime-effect snippet.
int snippetID = dict->findOrCreateRuntimeEffectSnippet(effect);
REPORTER_ASSERT(reporter, snippetID >= kBuiltInCodeSnippetIDCount);
// Verify that it can be looked up and its name is 'RuntimeEffect'. (The name isn't meaningful,
// but this is an easy way to verify that we didn't get an unrelated snippet.)
const SkShaderSnippet* snippet = dict->getEntry(snippetID);
REPORTER_ASSERT(reporter, snippet);
REPORTER_ASSERT(reporter, std::string_view(snippet->fName) == "RuntimeEffect");
// If we pass the same effect again, we should get the same snippet ID as before.
int foundSnippetID = dict->findOrCreateRuntimeEffectSnippet(effect);
REPORTER_ASSERT(reporter, foundSnippetID == snippetID);
}