Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkRuntimeImageFilter.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2021 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
9
10#include "include/core/SkData.h"
13#include "include/core/SkRect.h"
17#include "include/core/SkSize.h"
18#include "include/core/SkSpan.h"
23#include "src/base/SkSpinlock.h"
28#include "src/core/SkRectPriv.h"
31
32#include <cstddef>
33#include <optional>
34#include <string>
35#include <string_view>
36#include <utility>
37
38using namespace skia_private;
39
40// NOTE: Not in an anonymous namespace so that SkRuntimeShaderBuilder can friend it.
42public:
44 float maxSampleRadius,
45 std::string_view childShaderNames[],
46 const sk_sp<SkImageFilter> inputs[],
47 int inputCount)
48 : SkImageFilter_Base(inputs, inputCount)
49 , fRuntimeEffectBuilder(builder)
50 , fMaxSampleRadius(maxSampleRadius) {
51 SkASSERT(maxSampleRadius >= 0.f);
52 fChildShaderNames.reserve_exact(inputCount);
53 for (int i = 0; i < inputCount; i++) {
54 fChildShaderNames.push_back(SkString(childShaderNames[i]));
55 }
56 }
57
58 SkRect computeFastBounds(const SkRect& src) const override;
59
60protected:
61 void flatten(SkWriteBuffer&) const override;
62
63private:
64 friend void ::SkRegisterRuntimeImageFilterFlattenable();
66
67 bool onAffectsTransparentBlack() const override { return true; }
68 // Currently there is no way for a client to specify the semantics of geometric uniforms that
69 // should respond to the canvas matrix. Forcing translate-only is a hammer that lets the output
70 // be correct at the expense of resolution when there's a lot of scaling. See skbug.com/13416.
71 MatrixCapability onGetCTMCapability() const override { return MatrixCapability::kTranslate; }
72
73 skif::FilterResult onFilterImage(const skif::Context&) const override;
74
76 const skif::Mapping& mapping,
77 const skif::LayerSpace<SkIRect>& desiredOutput,
78 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override;
79
80 std::optional<skif::LayerSpace<SkIRect>> onGetOutputLayerBounds(
81 const skif::Mapping& mapping,
82 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const override;
83
84 skif::LayerSpace<SkIRect> applyMaxSampleRadius(
85 const skif::Mapping& mapping,
86 skif::LayerSpace<SkIRect> bounds) const {
87 skif::LayerSpace<SkISize> maxSampleRadius = mapping.paramToLayer(
88 skif::ParameterSpace<SkSize>({fMaxSampleRadius, fMaxSampleRadius})).ceil();
89 bounds.outset(maxSampleRadius);
90 return bounds;
91 }
92
93 mutable SkSpinlock fRuntimeEffectLock;
94 mutable SkRuntimeShaderBuilder fRuntimeEffectBuilder;
95 STArray<1, SkString> fChildShaderNames;
96 float fMaxSampleRadius;
97};
98
100 SkScalar sampleRadius,
101 std::string_view childShaderName,
102 sk_sp<SkImageFilter> input) {
103 // If no childShaderName is provided, check to see if we can implicitly assign it to the only
104 // child in the effect.
105 if (childShaderName.empty()) {
106 auto children = builder.effect()->children();
107 if (children.size() != 1) {
108 return nullptr;
109 }
110 childShaderName = children.front().name;
111 }
112
113 return SkImageFilters::RuntimeShader(builder, sampleRadius, &childShaderName, &input, 1);
114}
115
117 SkScalar maxSampleRadius,
118 std::string_view childShaderNames[],
119 const sk_sp<SkImageFilter> inputs[],
120 int inputCount) {
121 if (maxSampleRadius < 0.f) {
122 return nullptr; // invalid sample radius
123 }
124
125 auto child_is_shader = [](const SkRuntimeEffect::Child* child) {
126 return child && child->type == SkRuntimeEffect::ChildType::kShader;
127 };
128
129 for (int i = 0; i < inputCount; i++) {
130 std::string_view name = childShaderNames[i];
131 // All names must be non-empty, and present as a child shader in the effect:
132 if (name.empty() || !child_is_shader(builder.effect()->findChild(name))) {
133 return nullptr;
134 }
135
136 // We don't allow duplicates, either:
137 for (int j = 0; j < i; j++) {
138 if (name == childShaderNames[j]) {
139 return nullptr;
140 }
141 }
142 }
143
144 return sk_sp<SkImageFilter>(new SkRuntimeImageFilter(builder, maxSampleRadius, childShaderNames,
145 inputs, inputCount));
146}
147
151
152sk_sp<SkFlattenable> SkRuntimeImageFilter::CreateProc(SkReadBuffer& buffer) {
153 // We don't know how many inputs to expect yet. Passing -1 allows any number of children.
155 if (common.cropRect()) {
156 return nullptr;
157 }
158
159 // Read the SkSL string and convert it into a runtime effect
160 SkString sksl;
161 buffer.readString(&sksl);
162 auto effect = SkMakeCachedRuntimeEffect(SkRuntimeEffect::MakeForShader, std::move(sksl));
163 if (!buffer.validate(effect != nullptr)) {
164 return nullptr;
165 }
166
167 // Read the uniform data and make sure it matches the size from the runtime effect
168 sk_sp<SkData> uniforms = buffer.readByteArrayAsData();
169 if (!buffer.validate(uniforms->size() == effect->uniformSize())) {
170 return nullptr;
171 }
172
173 // Read the child shader names
174 STArray<4, std::string_view> childShaderNames;
175 STArray<4, SkString> childShaderNameStrings;
176 childShaderNames.resize(common.inputCount());
177 childShaderNameStrings.resize(common.inputCount());
178 for (int i = 0; i < common.inputCount(); i++) {
179 buffer.readString(&childShaderNameStrings[i]);
180 childShaderNames[i] = childShaderNameStrings[i].c_str();
181 }
182
183 SkRuntimeShaderBuilder builder(std::move(effect), std::move(uniforms));
184
185 // Populate the builder with the corresponding children
186 for (const SkRuntimeEffect::Child& child : builder.effect()->children()) {
187 std::string_view name = child.name;
188 switch (child.type) {
189 case SkRuntimeEffect::ChildType::kBlender: {
190 builder.child(name) = buffer.readBlender();
191 break;
192 }
193 case SkRuntimeEffect::ChildType::kColorFilter: {
194 builder.child(name) = buffer.readColorFilter();
195 break;
196 }
197 case SkRuntimeEffect::ChildType::kShader: {
198 builder.child(name) = buffer.readShader();
199 break;
200 }
201 }
202 }
203
204 float maxSampleRadius = 0.f; // default before sampleRadius was exposed in the factory
206 maxSampleRadius = buffer.readScalar();
207 }
208
209 if (!buffer.isValid()) {
210 return nullptr;
211 }
212
213 return SkImageFilters::RuntimeShader(builder, maxSampleRadius, childShaderNames.data(),
214 common.inputs(), common.inputCount());
215}
216
218 this->SkImageFilter_Base::flatten(buffer);
219 fRuntimeEffectLock.acquire();
220 buffer.writeString(fRuntimeEffectBuilder.effect()->source().c_str());
221 buffer.writeDataAsByteArray(fRuntimeEffectBuilder.uniforms().get());
222 for (const SkString& name : fChildShaderNames) {
223 buffer.writeString(name.c_str());
224 }
225 for (size_t x = 0; x < fRuntimeEffectBuilder.children().size(); x++) {
226 buffer.writeFlattenable(fRuntimeEffectBuilder.children()[x].flattenable());
227 }
228 fRuntimeEffectLock.release();
229
230 buffer.writeScalar(fMaxSampleRadius);
231}
232
233///////////////////////////////////////////////////////////////////////////////////////////////////
234
236 using ShaderFlags = skif::FilterResult::ShaderFlags;
237
238 const int inputCount = this->countInputs();
239 SkASSERT(inputCount == fChildShaderNames.size());
240
241 skif::Context inputCtx = ctx.withNewDesiredOutput(
242 this->applyMaxSampleRadius(ctx.mapping(), ctx.desiredOutput()));
243 skif::FilterResult::Builder builder{ctx};
244 for (int i = 0; i < inputCount; ++i) {
245 // Record the input context's desired output as the sample bounds for the child shaders
246 // since the runtime shader can go up to max sample radius away from its desired output
247 // (which is the default sample bounds if we didn't override it here).
248 builder.add(this->getChildOutput(i, inputCtx),
249 inputCtx.desiredOutput(),
250 ShaderFlags::kNonTrivialSampling);
251 }
252 return builder.eval([&](SkSpan<sk_sp<SkShader>> inputs) {
253 // lock the mutation of the builder and creation of the shader so that the builder's state
254 // is const and is safe for multi-threaded access.
255 fRuntimeEffectLock.acquire();
256 for (int i = 0; i < inputCount; i++) {
257 fRuntimeEffectBuilder.child(fChildShaderNames[i].c_str()) = inputs[i];
258 }
259 sk_sp<SkShader> shader = fRuntimeEffectBuilder.makeShader();
260
261 // Remove the inputs from the builder to avoid unnecessarily prolonging the input shaders'
262 // lifetimes.
263 for (int i = 0; i < inputCount; i++) {
264 fRuntimeEffectBuilder.child(fChildShaderNames[i].c_str()) = nullptr;
265 }
266 fRuntimeEffectLock.release();
267
268 return shader;
269 }, {}, /*evaluateInParameterSpace=*/true);
270}
271
273 const skif::Mapping& mapping,
274 const skif::LayerSpace<SkIRect>& desiredOutput,
275 std::optional<skif::LayerSpace<SkIRect>> contentBounds) const {
276 const int inputCount = this->countInputs();
277 if (inputCount <= 0) {
279 } else {
280 // Provide 'maxSampleRadius' pixels (in layer space) to the child shaders.
281 skif::LayerSpace<SkIRect> requiredInput =
282 this->applyMaxSampleRadius(mapping, desiredOutput);
283
284 // Union of all child input bounds so that one source image can provide for all of them.
286 inputCount,
287 [&](int i) {
288 return this->getChildInputLayerBounds(i, mapping, requiredInput, contentBounds);
289 });
290 }
291}
292
293std::optional<skif::LayerSpace<SkIRect>> SkRuntimeImageFilter::onGetOutputLayerBounds(
294 const skif::Mapping& /*mapping*/,
295 std::optional<skif::LayerSpace<SkIRect>> /*contentBounds*/) const {
296 // Pessimistically assume it can cover anything
298}
299
301 // Can't predict what the RT Shader will generate (see onGetOutputLayerBounds)
303}
#define SkASSERT(cond)
Definition SkAssert.h:116
#define SK_FLATTENABLE_HOOKS(type)
#define SK_REGISTER_FLATTENABLE(type)
#define SK_IMAGEFILTER_UNFLATTEN_COMMON(localVar, expectedCount)
sk_sp< SkRuntimeEffect > SkMakeCachedRuntimeEffect(SkRuntimeEffect::Result(*make)(SkString sksl, const SkRuntimeEffect::Options &), SkString sksl)
void SkRegisterRuntimeImageFilterFlattenable()
skif::LayerSpace< SkIRect > getChildInputLayerBounds(int index, const skif::Mapping &mapping, const skif::LayerSpace< SkIRect > &desiredOutput, std::optional< skif::LayerSpace< SkIRect > > contentBounds) const
void flatten(SkWriteBuffer &) const override
skif::FilterResult getChildOutput(int index, const skif::Context &ctx) const
int countInputs() const
static sk_sp< SkImageFilter > RuntimeShader(const SkRuntimeShaderBuilder &builder, std::string_view childShaderName, sk_sp< SkImageFilter > input)
@ kRuntimeImageFilterSampleRadius
static SkRect MakeLargeS32()
Definition SkRectPriv.h:33
BuilderChild child(std::string_view name)
sk_sp< const SkData > uniforms() const
const SkRuntimeEffect * effect() const
SkSpan< const SkRuntimeEffect::ChildPtr > children() const
static Result MakeForShader(SkString sksl, const Options &)
const std::string & source() const
void flatten(SkWriteBuffer &) const override
skif::FilterResult onFilterImage(const skif::Context &) const override
std::optional< skif::LayerSpace< SkIRect > > onGetOutputLayerBounds(const skif::Mapping &mapping, std::optional< skif::LayerSpace< SkIRect > > contentBounds) const override
MatrixCapability onGetCTMCapability() const override
skif::LayerSpace< SkIRect > onGetInputLayerBounds(const skif::Mapping &mapping, const skif::LayerSpace< SkIRect > &desiredOutput, std::optional< skif::LayerSpace< SkIRect > > contentBounds) const override
bool onAffectsTransparentBlack() const override
SkRuntimeImageFilter(const SkRuntimeShaderBuilder &builder, float maxSampleRadius, std::string_view childShaderNames[], const sk_sp< SkImageFilter > inputs[], int inputCount)
SkRect computeFastBounds(const SkRect &src) const override
sk_sp< SkShader > makeShader(const SkMatrix *localMatrix=nullptr) const
constexpr size_t size() const
Definition SkSpan_impl.h:95
T * get() const
Definition SkRefCnt.h:303
void resize(size_t count)
Definition SkTArray.h:418
int size() const
Definition SkTArray.h:416
void reserve_exact(int n)
Definition SkTArray.h:176
const LayerSpace< SkIRect > & desiredOutput() const
Context withNewDesiredOutput(const LayerSpace< SkIRect > &desiredOutput) const
const Mapping & mapping() const
LayerSpace< T > paramToLayer(const ParameterSpace< T > &paramGeometry) const
float SkScalar
Definition extension.cpp:12
static const uint8_t buffer[]
const char * name
Definition fuchsia.cc:50
double x
Optional< SkRect > bounds
Definition SkRecords.h:189