• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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