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:
parent
3824e1db80
commit
a10e6c1025
@ -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);
|
||||
|
@ -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};
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user