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 if (!RuntimeEffectBlock::BeginBlock(keyContext, builder, gatherer, { fEffect })) {
125 RuntimeEffectBlock::AddNoOpEffect(keyContext, builder, gatherer, fEffect.get());
126 return;
127 }
128
129 KeyContextWithScope childContext(keyContext, KeyContext::Scope::kRuntimeEffect);
130
131 int remainingCombinations = desiredCombination;
132
133 for (size_t rowIndex = 0; rowIndex < fChildOptions.size(); ++rowIndex) {
134 const std::vector<sk_sp<PrecompileBase>>& slotOptions = fChildOptions[rowIndex];
135 int numSlotCombinations = fNumSlotCombinations[rowIndex];
136
137 const int slotOption = remainingCombinations % numSlotCombinations;
138 remainingCombinations /= numSlotCombinations;
139
140 auto [option, childOptions] = PrecompileBase::SelectOption(
141 SkSpan<const sk_sp<PrecompileBase>>(slotOptions),
142 slotOption);
143
144 SkASSERT(precompilebase_is_valid_as_child(option.get()));
145 if (option) {
146 option->priv().addToKey(keyContext, builder, gatherer, childOptions);
147 } else {
148 SkASSERT(childOptions == 0);
149
150 // We don't have a child effect. Substitute in a no-op effect.
151 switch (childInfo[rowIndex].type) {
152 case SkRuntimeEffect::ChildType::kShader:
153 // A missing shader returns transparent black
154 SolidColorShaderBlock::AddBlock(childContext, builder, gatherer,
155 SK_PMColor4fTRANSPARENT);
156 break;
157
158 case SkRuntimeEffect::ChildType::kColorFilter:
159 // A "passthrough" shader returns the input color as-is.
160 builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
161 break;
162
163 case SkRuntimeEffect::ChildType::kBlender:
164 // A "passthrough" blender performs `blend_src_over(src, dest)`.
165 AddFixedBlendMode(childContext, builder, gatherer, SkBlendMode::kSrcOver);
166 break;
167 }
168 }
169 }
170
171 builder->endBlock();
172 }
173
174 sk_sp<SkRuntimeEffect> fEffect;
175 std::vector<std::vector<sk_sp<PrecompileBase>>> fChildOptions;
176 skia_private::TArray<int> fNumSlotCombinations;
177 int fNumChildCombinations;
178 };
179
MakePrecompileShader(sk_sp<SkRuntimeEffect> effect,SkSpan<const PrecompileChildOptions> childOptions)180 sk_sp<PrecompileShader> PrecompileRuntimeEffects::MakePrecompileShader(
181 sk_sp<SkRuntimeEffect> effect,
182 SkSpan<const PrecompileChildOptions> childOptions) {
183 if (!effect || !effect->allowShader()) {
184 return nullptr;
185 }
186
187 if (!children_are_valid(effect.get(), childOptions)) {
188 return nullptr;
189 }
190
191 return sk_make_sp<PrecompileRTEffect<PrecompileShader>>(std::move(effect), childOptions);
192 }
193
MakePrecompileColorFilter(sk_sp<SkRuntimeEffect> effect,SkSpan<const PrecompileChildOptions> childOptions)194 sk_sp<PrecompileColorFilter> PrecompileRuntimeEffects::MakePrecompileColorFilter(
195 sk_sp<SkRuntimeEffect> effect,
196 SkSpan<const PrecompileChildOptions> childOptions) {
197 if (!effect || !effect->allowColorFilter()) {
198 return nullptr;
199 }
200
201 if (!children_are_valid(effect.get(), childOptions)) {
202 return nullptr;
203 }
204
205 return sk_make_sp<PrecompileRTEffect<PrecompileColorFilter>>(std::move(effect), childOptions);
206 }
207
MakePrecompileBlender(sk_sp<SkRuntimeEffect> effect,SkSpan<const PrecompileChildOptions> childOptions)208 sk_sp<PrecompileBlender> PrecompileRuntimeEffects::MakePrecompileBlender(
209 sk_sp<SkRuntimeEffect> effect,
210 SkSpan<const PrecompileChildOptions> childOptions) {
211 if (!effect || !effect->allowBlender()) {
212 return nullptr;
213 }
214
215 if (!children_are_valid(effect.get(), childOptions)) {
216 return nullptr;
217 }
218
219 return sk_make_sp<PrecompileRTEffect<PrecompileBlender>>(std::move(effect), childOptions);
220 }
221
222 } // namespace skgpu::graphite
223