1 /*
2 * Copyright 2024 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/gpu/graphite/precompile/PrecompileRuntimeEffect.h"
9
10 #include "include/effects/SkRuntimeEffect.h"
11 #include "include/gpu/graphite/precompile/PrecompileBase.h"
12 #include "include/gpu/graphite/precompile/PrecompileBlender.h"
13 #include "include/gpu/graphite/precompile/PrecompileColorFilter.h"
14 #include "include/gpu/graphite/precompile/PrecompileShader.h"
15 #include "include/private/base/SkTArray.h"
16 #include "src/gpu/graphite/KeyContext.h"
17 #include "src/gpu/graphite/KeyHelpers.h"
18 #include "src/gpu/graphite/PaintParams.h"
19 #include "src/gpu/graphite/PaintParamsKey.h"
20 #include "src/gpu/graphite/precompile/PrecompileBaseComplete.h"
21 #include "src/gpu/graphite/precompile/PrecompileBasePriv.h"
22
23 namespace skgpu::graphite {
24
25 namespace {
26
27 #ifdef SK_DEBUG
28
precompilebase_is_valid_as_child(const PrecompileBase * child)29 bool precompilebase_is_valid_as_child(const PrecompileBase *child) {
30 if (!child) {
31 return true;
32 }
33
34 switch (child->type()) {
35 case PrecompileBase::Type::kShader:
36 case PrecompileBase::Type::kColorFilter:
37 case PrecompileBase::Type::kBlender:
38 return true;
39 default:
40 return false;
41 }
42 }
43
44 #endif // SK_DEBUG
45
num_options_in_set(const SkSpan<const sk_sp<PrecompileBase>> & optionSet)46 int num_options_in_set(const SkSpan<const sk_sp<PrecompileBase>>& optionSet) {
47 int numOptions = 0;
48 for (const sk_sp<PrecompileBase>& childOption : optionSet) {
49 // A missing child will fall back to a passthrough object
50 if (childOption) {
51 numOptions += childOption->priv().numCombinations();
52 } else {
53 ++numOptions;
54 }
55 }
56
57 return numOptions;
58 }
59
60 // Convert from runtime effect type to precompile type
to_precompile_type(SkRuntimeEffect::ChildType childType)61 PrecompileBase::Type to_precompile_type(SkRuntimeEffect::ChildType childType) {
62 switch(childType) {
63 case SkRuntimeEffect::ChildType::kShader: return PrecompileBase::Type::kShader;
64 case SkRuntimeEffect::ChildType::kColorFilter: return PrecompileBase::Type::kColorFilter;
65 case SkRuntimeEffect::ChildType::kBlender: return PrecompileBase::Type::kBlender;
66 }
67 SkUNREACHABLE;
68 }
69
children_are_valid(SkRuntimeEffect * effect,SkSpan<const PrecompileChildOptions> childOptions)70 bool children_are_valid(SkRuntimeEffect* effect,
71 SkSpan<const PrecompileChildOptions> childOptions) {
72 SkSpan<const SkRuntimeEffect::Child> childInfo = effect->children();
73 if (childOptions.size() != childInfo.size()) {
74 return false;
75 }
76
77 for (size_t i = 0; i < childInfo.size(); ++i) {
78 const PrecompileBase::Type expectedType = to_precompile_type(childInfo[i].type);
79 for (const sk_sp<PrecompileBase>& childOption : childOptions[i]) {
80 if (childOption && expectedType != childOption->type()) {
81 return false;
82 }
83 }
84 }
85
86 return true;
87 }
88
89 } // anonymous namespace
90
91 template<typename T>
92 class PrecompileRTEffect : public T {
93 public:
PrecompileRTEffect(sk_sp<SkRuntimeEffect> effect,SkSpan<const PrecompileChildOptions> childOptions)94 PrecompileRTEffect(sk_sp<SkRuntimeEffect> effect,
95 SkSpan<const PrecompileChildOptions> childOptions)
96 : fEffect(std::move(effect)) {
97 fChildOptions.reserve(childOptions.size());
98 for (PrecompileChildOptions c : childOptions) {
99 fChildOptions.push_back({ c.begin(), c.end() });
100 }
101
102 fNumSlotCombinations.reserve(childOptions.size());
103 fNumChildCombinations = 1;
104 for (const std::vector<sk_sp<PrecompileBase>>& optionSet : fChildOptions) {
105 fNumSlotCombinations.push_back(num_options_in_set(optionSet));
106 fNumChildCombinations *= fNumSlotCombinations.back();
107 }
108
109 SkASSERT(fChildOptions.size() == fEffect->children().size());
110 }
111
112 private:
numChildCombinations() const113 int numChildCombinations() const override { return fNumChildCombinations; }
114
addToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,int desiredCombination) const115 void addToKey(const KeyContext& keyContext,
116 PaintParamsKeyBuilder* builder,
117 PipelineDataGatherer* gatherer,
118 int desiredCombination) const override {
119
120 SkASSERT(desiredCombination < this->numCombinations());
121
122 SkSpan<const SkRuntimeEffect::Child> childInfo = fEffect->children();
123
124 RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer, { fEffect });
125
126 KeyContextWithScope childContext(keyContext, KeyContext::Scope::kRuntimeEffect);
127
128 int remainingCombinations = desiredCombination;
129
130 for (size_t rowIndex = 0; rowIndex < fChildOptions.size(); ++rowIndex) {
131 const std::vector<sk_sp<PrecompileBase>>& slotOptions = fChildOptions[rowIndex];
132 int numSlotCombinations = fNumSlotCombinations[rowIndex];
133
134 const int slotOption = remainingCombinations % numSlotCombinations;
135 remainingCombinations /= numSlotCombinations;
136
137 auto [option, childOptions] = PrecompileBase::SelectOption(
138 SkSpan<const sk_sp<PrecompileBase>>(slotOptions),
139 slotOption);
140
141 SkASSERT(precompilebase_is_valid_as_child(option.get()));
142 if (option) {
143 option->priv().addToKey(keyContext, builder, gatherer, childOptions);
144 } else {
145 SkASSERT(childOptions == 0);
146
147 // We don't have a child effect. Substitute in a no-op effect.
148 switch (childInfo[rowIndex].type) {
149 case SkRuntimeEffect::ChildType::kShader:
150 // A missing shader returns transparent black
151 SolidColorShaderBlock::AddBlock(childContext, builder, gatherer,
152 SK_PMColor4fTRANSPARENT);
153 break;
154
155 case SkRuntimeEffect::ChildType::kColorFilter:
156 // A "passthrough" shader returns the input color as-is.
157 builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
158 break;
159
160 case SkRuntimeEffect::ChildType::kBlender:
161 // A "passthrough" blender performs `blend_src_over(src, dest)`.
162 AddFixedBlendMode(childContext, builder, gatherer, SkBlendMode::kSrcOver);
163 break;
164 }
165 }
166 }
167
168 builder->endBlock();
169 }
170
171 sk_sp<SkRuntimeEffect> fEffect;
172 std::vector<std::vector<sk_sp<PrecompileBase>>> fChildOptions;
173 skia_private::TArray<int> fNumSlotCombinations;
174 int fNumChildCombinations;
175 };
176
MakePrecompileShader(sk_sp<SkRuntimeEffect> effect,SkSpan<const PrecompileChildOptions> childOptions)177 sk_sp<PrecompileShader> PrecompileRuntimeEffects::MakePrecompileShader(
178 sk_sp<SkRuntimeEffect> effect,
179 SkSpan<const PrecompileChildOptions> childOptions) {
180 if (!effect || !effect->allowShader()) {
181 return nullptr;
182 }
183
184 if (!children_are_valid(effect.get(), childOptions)) {
185 return nullptr;
186 }
187
188 return sk_make_sp<PrecompileRTEffect<PrecompileShader>>(std::move(effect), childOptions);
189 }
190
MakePrecompileColorFilter(sk_sp<SkRuntimeEffect> effect,SkSpan<const PrecompileChildOptions> childOptions)191 sk_sp<PrecompileColorFilter> PrecompileRuntimeEffects::MakePrecompileColorFilter(
192 sk_sp<SkRuntimeEffect> effect,
193 SkSpan<const PrecompileChildOptions> childOptions) {
194 if (!effect || !effect->allowColorFilter()) {
195 return nullptr;
196 }
197
198 if (!children_are_valid(effect.get(), childOptions)) {
199 return nullptr;
200 }
201
202 return sk_make_sp<PrecompileRTEffect<PrecompileColorFilter>>(std::move(effect), childOptions);
203 }
204
MakePrecompileBlender(sk_sp<SkRuntimeEffect> effect,SkSpan<const PrecompileChildOptions> childOptions)205 sk_sp<PrecompileBlender> PrecompileRuntimeEffects::MakePrecompileBlender(
206 sk_sp<SkRuntimeEffect> effect,
207 SkSpan<const PrecompileChildOptions> childOptions) {
208 if (!effect || !effect->allowBlender()) {
209 return nullptr;
210 }
211
212 if (!children_are_valid(effect.get(), childOptions)) {
213 return nullptr;
214 }
215
216 return sk_make_sp<PrecompileRTEffect<PrecompileBlender>>(std::move(effect), childOptions);
217 }
218
219 } // namespace skgpu::graphite
220