blob: 25a2a416ce92ff38f42726eb5f90ca890cc1aeb8 [file] [log] [blame]
* Copyright 2022 Google LLC
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
#ifndef SkShaderCodeDictionary_DEFINED
#define SkShaderCodeDictionary_DEFINED
#include <array>
#include <unordered_map>
#include <vector>
#include "include/core/SkSpan.h"
#include "include/private/SkSpinlock.h"
#include "include/private/SkUniquePaintParamsID.h"
#include "src/core/SkArenaAlloc.h"
#include "src/core/SkEnumBitMask.h"
#include "src/core/SkPaintParamsKey.h"
#include "src/core/SkPipelineData.h"
#include "src/core/SkUniform.h"
namespace SkSL {
struct ShaderCaps;
// TODO: How to represent the type (e.g., 2D) of texture being sampled?
class SkTextureAndSampler {
constexpr SkTextureAndSampler(const char* name) : fName(name) {}
const char* name() const { return fName; }
const char* fName;
enum class SnippetRequirementFlags : uint32_t {
kNone = 0x0,
kLocalCoords = 0x1,
struct SkShaderSnippet {
using GenerateGlueCodeForEntry = std::string (*)(const std::string& resultName,
int entryIndex, // for uniform name mangling
const SkPaintParamsKey::BlockReader&,
const std::string& priorStageOutputName,
const std::vector<std::string>& childNames,
int indent);
SkShaderSnippet() = default;
SkShaderSnippet(const char* name,
SkSpan<const SkUniform> uniforms,
SnippetRequirementFlags snippetRequirementFlags,
SkSpan<const SkTextureAndSampler> texturesAndSamplers,
const char* functionName,
GenerateGlueCodeForEntry glueCodeGenerator,
int numChildren,
SkSpan<const SkPaintParamsKey::DataPayloadField> dataPayloadExpectations)
: fName(name)
, fUniforms(uniforms)
, fSnippetRequirementFlags(snippetRequirementFlags)
, fTexturesAndSamplers(texturesAndSamplers)
, fStaticFunctionName(functionName)
, fGlueCodeGenerator(glueCodeGenerator)
, fNumChildren(numChildren)
, fDataPayloadExpectations(dataPayloadExpectations) {
std::string getMangledUniformName(int uniformIndex, int mangleId) const;
bool needsLocalCoords() const {
return fSnippetRequirementFlags & SnippetRequirementFlags::kLocalCoords;
int numExpectedChildren() const { return fNumChildren; }
const char* fName = nullptr;
SkSpan<const SkUniform> fUniforms;
SnippetRequirementFlags fSnippetRequirementFlags;
SkSpan<const SkTextureAndSampler> fTexturesAndSamplers;
const char* fStaticFunctionName = nullptr;
GenerateGlueCodeForEntry fGlueCodeGenerator = nullptr;
int fNumChildren = 0;
SkSpan<const SkPaintParamsKey::DataPayloadField> fDataPayloadExpectations;
// This is just a simple collection object that gathers together all the information needed
// for program creation and its invocation.
class SkShaderInfo {
void add(const SkPaintParamsKey::BlockReader& reader) {
void addFlags(SnippetRequirementFlags flags) {
fSnippetRequirementFlags |= flags;
bool needsLocalCoords() const {
return fSnippetRequirementFlags & SnippetRequirementFlags::kLocalCoords;
void setBlendInfo(const SkPipelineDataGatherer::BlendInfo& blendInfo) {
fBlendInfo = blendInfo;
const SkPipelineDataGatherer::BlendInfo& blendInfo() const { return fBlendInfo; }
std::string toSkSL() const;
std::string emitGlueCodeForEntry(int* entryIndex,
const std::string& priorStageOutputName,
const std::string& parentPreLocalName,
std::string* result,
int indent) const;
std::vector<SkPaintParamsKey::BlockReader> fBlockReaders;
SkEnumBitMask<SnippetRequirementFlags> fSnippetRequirementFlags =SnippetRequirementFlags::kNone;
// The blendInfo doesn't actually contribute to the program's creation but, it contains the
// matching fixed-function settings that the program's caller needs to set up.
SkPipelineDataGatherer::BlendInfo fBlendInfo;
class SkShaderCodeDictionary {
struct Entry {
SkUniquePaintParamsID uniqueID() const {
return fUniqueID;
const SkPaintParamsKey& paintParamsKey() const { return fKey; }
const SkPipelineDataGatherer::BlendInfo& blendInfo() const { return fBlendInfo; }
friend class SkShaderCodeDictionary;
Entry(const SkPaintParamsKey& key, const SkPipelineDataGatherer::BlendInfo& blendInfo)
: fKey(key.asSpan())
, fBlendInfo(blendInfo) {
Entry(const SkPaintParamsKey& key) : fKey(key.asSpan()) {}
void setUniqueID(uint32_t newID) {
fUniqueID = SkUniquePaintParamsID(newID);
SkUniquePaintParamsID fUniqueID; // fixed-size (uint32_t) unique ID assigned to a key
SkPaintParamsKey fKey; // variable-length paint key descriptor
// The BlendInfo isn't used in the hash (that is the key's job) but it does directly vary
// with the key. It could, theoretically, be recreated from the key but that would add
// extra complexity.
SkPipelineDataGatherer::BlendInfo fBlendInfo;
const Entry* findOrCreate(const SkPaintParamsKey&,
const SkPipelineDataGatherer::BlendInfo&) SK_EXCLUDES(fSpinLock);
const Entry* findOrCreate(const SkPaintParamsKey&) SK_EXCLUDES(fSpinLock);
const Entry* lookup(SkUniquePaintParamsID) const SK_EXCLUDES(fSpinLock);
SkSpan<const SkUniform> getUniforms(SkBuiltInCodeSnippetID) const;
SnippetRequirementFlags getSnippetRequirementFlags(SkBuiltInCodeSnippetID id) const {
return fBuiltInCodeSnippets[(int) id].fSnippetRequirementFlags;
SkSpan<const SkPaintParamsKey::DataPayloadField> dataPayloadExpectations(int snippetID) const;
// This method can return nullptr
const SkShaderSnippet* getEntry(int codeSnippetID) const;
const SkShaderSnippet* getEntry(SkBuiltInCodeSnippetID codeSnippetID) const {
return this->getEntry(SkTo<int>(codeSnippetID));
void getShaderInfo(SkUniquePaintParamsID, SkShaderInfo*);
int maxCodeSnippetID() const {
return static_cast<int>(SkBuiltInCodeSnippetID::kLast) + fUserDefinedCodeSnippets.size();
// 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
// TODO: add hooks for user to actually provide code.
int addUserDefinedSnippet(const char* name,
SkSpan<const SkPaintParamsKey::DataPayloadField> expectations);
Entry* makeEntry(const SkPaintParamsKey&, const SkPipelineDataGatherer::BlendInfo&);
Entry* makeEntry(const SkPaintParamsKey&);
struct Hash {
size_t operator()(const SkPaintParamsKey*) const;
struct KeyEqual {
bool operator()(const SkPaintParamsKey* k1, const SkPaintParamsKey* k2) const {
return k1->operator==(*k2);
std::array<SkShaderSnippet, kBuiltInCodeSnippetIDCount> fBuiltInCodeSnippets;
// The value returned from 'getEntry' must be stable so, hold the user-defined code snippet
// entries as pointers.
std::vector<std::unique_ptr<SkShaderSnippet>> fUserDefinedCodeSnippets;
// TODO: can we do something better given this should have write-seldom/read-often behavior?
mutable SkSpinlock fSpinLock;
using PaintHashMap = std::unordered_map<const SkPaintParamsKey*, Entry*, Hash, KeyEqual>;
PaintHashMap fHash SK_GUARDED_BY(fSpinLock);
std::vector<Entry*> fEntryVector SK_GUARDED_BY(fSpinLock);
// This arena holds:
// the Entries held in 'fHash' and 'fEntryVector' - thus, guarded by 'fSpinLock'
SkArenaAlloc fArena{256};
#endif // SkShaderCodeDictionary_DEFINED