xref: /aosp_15_r20/external/skia/src/gpu/graphite/precompile/PrecompileRuntimeEffect.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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