1 /*
2 * Copyright 2019 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/core/SkColorFilter.h"
9 #include "include/core/SkData.h"
10 #include "include/core/SkSurface.h"
11 #include "include/effects/SkRuntimeEffect.h"
12 #include "include/private/SkMutex.h"
13 #include "src/core/SkBlenderBase.h"
14 #include "src/core/SkCanvasPriv.h"
15 #include "src/core/SkColorFilterBase.h"
16 #include "src/core/SkColorSpacePriv.h"
17 #include "src/core/SkColorSpaceXformSteps.h"
18 #include "src/core/SkLRUCache.h"
19 #include "src/core/SkMatrixProvider.h"
20 #include "src/core/SkOpts.h"
21 #include "src/core/SkRasterPipeline.h"
22 #include "src/core/SkReadBuffer.h"
23 #include "src/core/SkRuntimeEffectPriv.h"
24 #include "src/core/SkUtils.h"
25 #include "src/core/SkVM.h"
26 #include "src/core/SkWriteBuffer.h"
27 #include "src/sksl/SkSLAnalysis.h"
28 #include "src/sksl/SkSLCompiler.h"
29 #include "src/sksl/SkSLUtil.h"
30 #include "src/sksl/codegen/SkSLVMCodeGenerator.h"
31 #include "src/sksl/ir/SkSLFunctionDefinition.h"
32 #include "src/sksl/ir/SkSLVarDeclarations.h"
33
34 #if SK_SUPPORT_GPU
35 #include "include/gpu/GrRecordingContext.h"
36 #include "src/gpu/GrColorInfo.h"
37 #include "src/gpu/GrFPArgs.h"
38 #include "src/gpu/GrImageInfo.h"
39 #include "src/gpu/GrRecordingContextPriv.h"
40 #include "src/gpu/SurfaceFillContext.h"
41 #include "src/gpu/effects/GrMatrixEffect.h"
42 #include "src/gpu/effects/GrSkSLFP.h"
43 #include "src/image/SkImage_Gpu.h"
44 #endif
45
46 #include <algorithm>
47
48 using ChildType = SkRuntimeEffect::ChildType;
49
50 #ifdef SK_ENABLE_SKSL
51
52 namespace SkSL {
53 class SharedCompiler {
54 public:
SharedCompiler()55 SharedCompiler() : fLock(compiler_mutex()) {
56 if (!gImpl) {
57 gImpl = new Impl();
58 }
59 }
60
operator ->() const61 SkSL::Compiler* operator->() const { return gImpl->fCompiler; }
62
63 private:
64 SkAutoMutexExclusive fLock;
65
compiler_mutex()66 static SkMutex& compiler_mutex() {
67 static SkMutex& mutex = *(new SkMutex);
68 return mutex;
69 }
70
71 struct Impl {
ImplSkSL::SharedCompiler::Impl72 Impl() {
73 // These caps are configured to apply *no* workarounds. This avoids changes that are
74 // unnecessary (GLSL intrinsic rewrites), or possibly incorrect (adding do-while loops).
75 // We may apply other "neutral" transformations to the user's SkSL, including inlining.
76 // Anything determined by the device caps is deferred to the GPU backend. The processor
77 // set produces the final program (including our re-emitted SkSL), and the backend's
78 // compiler resolves any necessary workarounds.
79 fCaps = ShaderCapsFactory::Standalone();
80 fCaps->fBuiltinFMASupport = true;
81 fCaps->fBuiltinDeterminantSupport = true;
82 // Don't inline if it would require a do loop, some devices don't support them.
83 fCaps->fCanUseDoLoops = false;
84
85 // SkSL created by the GPU backend is typically parsed, converted to a backend format,
86 // and the IR is immediately discarded. In that situation, it makes sense to use node
87 // pools to accelerate the IR allocations. Here, SkRuntimeEffect instances are often
88 // long-lived (especially those created internally for runtime FPs). In this situation,
89 // we're willing to pay for a slightly longer compile so that we don't waste huge
90 // amounts of memory.
91 fCaps->fUseNodePools = false;
92
93 fCompiler = new SkSL::Compiler(fCaps.get());
94 }
95
96 SkSL::ShaderCapsPointer fCaps;
97 SkSL::Compiler* fCompiler;
98 };
99
100 static Impl* gImpl;
101 };
102
103 SharedCompiler::Impl* SharedCompiler::gImpl = nullptr;
104
105 } // namespace SkSL
106
init_uniform_type(const SkSL::Context & ctx,const SkSL::Type * type,SkRuntimeEffect::Uniform * v)107 static bool init_uniform_type(const SkSL::Context& ctx,
108 const SkSL::Type* type,
109 SkRuntimeEffect::Uniform* v) {
110 using Type = SkRuntimeEffect::Uniform::Type;
111 if (*type == *ctx.fTypes.fFloat) { v->type = Type::kFloat; return true; }
112 if (*type == *ctx.fTypes.fHalf) { v->type = Type::kFloat; return true; }
113 if (*type == *ctx.fTypes.fFloat2) { v->type = Type::kFloat2; return true; }
114 if (*type == *ctx.fTypes.fHalf2) { v->type = Type::kFloat2; return true; }
115 if (*type == *ctx.fTypes.fFloat3) { v->type = Type::kFloat3; return true; }
116 if (*type == *ctx.fTypes.fHalf3) { v->type = Type::kFloat3; return true; }
117 if (*type == *ctx.fTypes.fFloat4) { v->type = Type::kFloat4; return true; }
118 if (*type == *ctx.fTypes.fHalf4) { v->type = Type::kFloat4; return true; }
119 if (*type == *ctx.fTypes.fFloat2x2) { v->type = Type::kFloat2x2; return true; }
120 if (*type == *ctx.fTypes.fHalf2x2) { v->type = Type::kFloat2x2; return true; }
121 if (*type == *ctx.fTypes.fFloat3x3) { v->type = Type::kFloat3x3; return true; }
122 if (*type == *ctx.fTypes.fHalf3x3) { v->type = Type::kFloat3x3; return true; }
123 if (*type == *ctx.fTypes.fFloat4x4) { v->type = Type::kFloat4x4; return true; }
124 if (*type == *ctx.fTypes.fHalf4x4) { v->type = Type::kFloat4x4; return true; }
125
126 if (*type == *ctx.fTypes.fInt) { v->type = Type::kInt; return true; }
127 if (*type == *ctx.fTypes.fInt2) { v->type = Type::kInt2; return true; }
128 if (*type == *ctx.fTypes.fInt3) { v->type = Type::kInt3; return true; }
129 if (*type == *ctx.fTypes.fInt4) { v->type = Type::kInt4; return true; }
130
131 return false;
132 }
133
child_type(const SkSL::Type & type)134 static ChildType child_type(const SkSL::Type& type) {
135 switch (type.typeKind()) {
136 case SkSL::Type::TypeKind::kBlender: return ChildType::kBlender;
137 case SkSL::Type::TypeKind::kColorFilter: return ChildType::kColorFilter;
138 case SkSL::Type::TypeKind::kShader: return ChildType::kShader;
139 default: SkUNREACHABLE;
140 }
141 }
142
verify_child_effects(const std::vector<SkRuntimeEffect::Child> & reflected,SkSpan<SkRuntimeEffect::ChildPtr> effectPtrs)143 static bool verify_child_effects(const std::vector<SkRuntimeEffect::Child>& reflected,
144 SkSpan<SkRuntimeEffect::ChildPtr> effectPtrs) {
145 // Verify that the number of passed-in child-effect pointers matches the SkSL code.
146 if (reflected.size() != effectPtrs.size()) {
147 return false;
148 }
149
150 // Verify that each child object's type matches its declared type in the SkSL.
151 for (size_t i = 0; i < effectPtrs.size(); ++i) {
152 skstd::optional<ChildType> effectType = effectPtrs[i].type();
153 if (effectType && effectType != reflected[i].type) {
154 return false;
155 }
156 }
157 return true;
158 }
159
read_child_effects(SkReadBuffer & buffer,const SkRuntimeEffect * effect,SkTArray<SkRuntimeEffect::ChildPtr> * children)160 static bool read_child_effects(SkReadBuffer& buffer,
161 const SkRuntimeEffect* effect,
162 SkTArray<SkRuntimeEffect::ChildPtr>* children) {
163 size_t childCount = buffer.read32();
164 if (!buffer.validate(childCount == effect->children().size())) {
165 return false;
166 }
167
168 children->reset();
169 children->reserve_back(childCount);
170
171 for (const auto& child : effect->children()) {
172 if (child.type == ChildType::kShader) {
173 children->emplace_back(buffer.readShader());
174 } else if (child.type == ChildType::kColorFilter) {
175 children->emplace_back(buffer.readColorFilter());
176 } else if (child.type == ChildType::kBlender) {
177 children->emplace_back(buffer.readBlender());
178 } else {
179 return false;
180 }
181 }
182
183 return buffer.isValid();
184 }
185
write_child_effects(SkWriteBuffer & buffer,const std::vector<SkRuntimeEffect::ChildPtr> & children)186 static void write_child_effects(SkWriteBuffer& buffer,
187 const std::vector<SkRuntimeEffect::ChildPtr>& children) {
188 buffer.write32(children.size());
189 for (const auto& child : children) {
190 buffer.writeFlattenable(child.flattenable());
191 }
192 }
193
make_skvm_uniforms(skvm::Builder * p,skvm::Uniforms * uniforms,size_t inputSize,const SkData & inputs)194 static std::vector<skvm::Val> make_skvm_uniforms(skvm::Builder* p,
195 skvm::Uniforms* uniforms,
196 size_t inputSize,
197 const SkData& inputs) {
198 SkASSERTF(!(inputSize & 3), "inputSize was %zu, expected a multiple of 4", inputSize);
199
200 const int32_t* data = reinterpret_cast<const int32_t*>(inputs.data());
201 const size_t uniformCount = inputSize / sizeof(int32_t);
202 std::vector<skvm::Val> uniform;
203 uniform.reserve(uniformCount);
204 for (size_t index = 0; index < uniformCount; ++index) {
205 int32_t bits;
206 memcpy(&bits, data + index, sizeof(int32_t));
207 uniform.push_back(p->uniform32(uniforms->push(bits)).id);
208 }
209
210 return uniform;
211 }
212
213 // TODO: Many errors aren't caught until we process the generated Program here. Catching those
214 // in the IR generator would provide better errors messages (with locations).
215 #define RETURN_FAILURE(...) return Result{nullptr, SkStringPrintf(__VA_ARGS__)}
216
MakeFromSource(SkString sksl,const Options & options,SkSL::ProgramKind kind)217 SkRuntimeEffect::Result SkRuntimeEffect::MakeFromSource(SkString sksl,
218 const Options& options,
219 SkSL::ProgramKind kind) {
220 std::unique_ptr<SkSL::Program> program;
221 {
222 // We keep this SharedCompiler in a separate scope to make sure it's destroyed before
223 // calling the Make overload at the end, which creates its own (non-reentrant)
224 // SharedCompiler instance
225 SkSL::SharedCompiler compiler;
226 SkSL::Program::Settings settings;
227 settings.fInlineThreshold = 0;
228 settings.fForceNoInline = options.forceNoInline;
229 settings.fEnforceES2Restrictions = options.enforceES2Restrictions;
230 program = compiler->convertProgram(kind, SkSL::String(sksl.c_str(), sksl.size()), settings);
231
232 if (!program) {
233 RETURN_FAILURE("%s", compiler->errorText().c_str());
234 }
235 }
236 return MakeInternal(std::move(program), options, kind);
237 }
238
MakeFromDSL(std::unique_ptr<SkSL::Program> program,const Options & options,SkSL::ProgramKind kind)239 SkRuntimeEffect::Result SkRuntimeEffect::MakeFromDSL(std::unique_ptr<SkSL::Program> program,
240 const Options& options,
241 SkSL::ProgramKind kind) {
242 // This factory is used for all DSL runtime effects, which don't have anything stored in the
243 // program's source. Populate it so that we can compute fHash, and serialize these effects.
244 program->fSource = std::make_unique<SkSL::String>(program->description());
245 return MakeInternal(std::move(program), options, kind);
246 }
247
MakeInternal(std::unique_ptr<SkSL::Program> program,const Options & options,SkSL::ProgramKind kind)248 SkRuntimeEffect::Result SkRuntimeEffect::MakeInternal(std::unique_ptr<SkSL::Program> program,
249 const Options& options,
250 SkSL::ProgramKind kind) {
251 SkSL::SharedCompiler compiler;
252
253 // Find 'main', then locate the sample coords parameter. (It might not be present.)
254 const SkSL::FunctionDefinition* main = SkSL::Program_GetFunction(*program, "main");
255 if (!main) {
256 RETURN_FAILURE("missing 'main' function");
257 }
258 const auto& mainParams = main->declaration().parameters();
259 auto iter = std::find_if(mainParams.begin(), mainParams.end(), [](const SkSL::Variable* p) {
260 return p->modifiers().fLayout.fBuiltin == SK_MAIN_COORDS_BUILTIN;
261 });
262 const SkSL::ProgramUsage::VariableCounts sampleCoordsUsage =
263 iter != mainParams.end() ? program->usage()->get(**iter)
264 : SkSL::ProgramUsage::VariableCounts{};
265
266 uint32_t flags = 0;
267 switch (kind) {
268 case SkSL::ProgramKind::kRuntimeColorFilter: flags |= kAllowColorFilter_Flag; break;
269 case SkSL::ProgramKind::kRuntimeShader: flags |= kAllowShader_Flag; break;
270 case SkSL::ProgramKind::kRuntimeBlender: flags |= kAllowBlender_Flag; break;
271 default: SkUNREACHABLE;
272 }
273
274 if (sampleCoordsUsage.fRead || sampleCoordsUsage.fWrite) {
275 flags |= kUsesSampleCoords_Flag;
276 }
277
278 // TODO(skia:12202): When we can layer modules, implement this restriction by moving the
279 // declaration of sk_FragCoord to a private module.
280 if (!options.allowFragCoord && SkSL::Analysis::ReferencesFragCoords(*program)) {
281 RETURN_FAILURE("unknown identifier 'sk_FragCoord'");
282 }
283
284 // Color filters and blends are not allowed to depend on position (local or device) in any way.
285 // The signature of main, and the declarations in sksl_rt_colorfilter/sksl_rt_blend should
286 // guarantee this.
287 if (flags & (kAllowColorFilter_Flag | kAllowBlender_Flag)) {
288 SkASSERT(!(flags & kUsesSampleCoords_Flag));
289 SkASSERT(!SkSL::Analysis::ReferencesFragCoords(*program));
290 }
291
292 if (SkSL::Analysis::CallsSampleOutsideMain(*program)) {
293 flags |= kSamplesOutsideMain_Flag;
294 }
295
296 size_t offset = 0;
297 std::vector<Uniform> uniforms;
298 std::vector<Child> children;
299 std::vector<SkSL::SampleUsage> sampleUsages;
300 int elidedSampleCoords = 0;
301 const SkSL::Context& ctx(compiler->context());
302
303 // Go through program elements, pulling out information that we need
304 for (const SkSL::ProgramElement* elem : program->elements()) {
305 // Variables (uniform, etc.)
306 if (elem->is<SkSL::GlobalVarDeclaration>()) {
307 const SkSL::GlobalVarDeclaration& global = elem->as<SkSL::GlobalVarDeclaration>();
308 const SkSL::VarDeclaration& varDecl = global.declaration()->as<SkSL::VarDeclaration>();
309
310 const SkSL::Variable& var = varDecl.var();
311 const SkSL::Type& varType = var.type();
312
313 // Child effects that can be sampled ('shader', 'colorFilter', 'blender')
314 if (varType.isEffectChild()) {
315 Child c;
316 c.name = SkString(var.name());
317 c.type = child_type(varType);
318 c.index = children.size();
319 children.push_back(c);
320 auto usage = SkSL::Analysis::GetSampleUsage(
321 *program, var, sampleCoordsUsage.fWrite != 0, &elidedSampleCoords);
322 // If the child is never sampled, we pretend that it's actually in PassThrough mode.
323 // Otherwise, the GP code for collecting transforms and emitting transform code gets
324 // very confused, leading to asserts and bad (backend) shaders. There's an implicit
325 // assumption that every FP is used by its parent. (skbug.com/12429)
326 sampleUsages.push_back(usage.isSampled() ? usage
327 : SkSL::SampleUsage::PassThrough());
328 }
329 // 'uniform' variables
330 else if (var.modifiers().fFlags & SkSL::Modifiers::kUniform_Flag) {
331 Uniform uni;
332 uni.name = SkString(var.name());
333 uni.flags = 0;
334 uni.count = 1;
335
336 const SkSL::Type* type = &var.type();
337 if (type->isArray()) {
338 uni.flags |= Uniform::kArray_Flag;
339 uni.count = type->columns();
340 type = &type->componentType();
341 }
342
343 if (!init_uniform_type(ctx, type, &uni)) {
344 RETURN_FAILURE("Invalid uniform type: '%s'", type->displayName().c_str());
345 }
346
347 if (var.modifiers().fLayout.fFlags & SkSL::Layout::Flag::kSRGBUnpremul_Flag) {
348 uni.flags |= Uniform::kSRGBUnpremul_Flag;
349 }
350
351 uni.offset = offset;
352 offset += uni.sizeInBytes();
353 SkASSERT(SkIsAlign4(offset));
354
355 uniforms.push_back(uni);
356 }
357 }
358 }
359
360 // If the sample coords are never written to, then we will have converted sample calls that use
361 // them unmodified into "passthrough" sampling. If all references to the sample coords were of
362 // that form, then we don't actually "use" sample coords. We unset the flag to prevent creating
363 // an extra (unused) varying holding the coords.
364 if (elidedSampleCoords == sampleCoordsUsage.fRead && sampleCoordsUsage.fWrite == 0) {
365 flags &= ~kUsesSampleCoords_Flag;
366 }
367
368 #undef RETURN_FAILURE
369
370 sk_sp<SkRuntimeEffect> effect(new SkRuntimeEffect(std::move(program),
371 options,
372 *main,
373 std::move(uniforms),
374 std::move(children),
375 std::move(sampleUsages),
376 flags));
377 return Result{std::move(effect), SkString()};
378 }
379
MakeForColorFilter(SkString sksl,const Options & options)380 SkRuntimeEffect::Result SkRuntimeEffect::MakeForColorFilter(SkString sksl, const Options& options) {
381 auto result = MakeFromSource(std::move(sksl), options, SkSL::ProgramKind::kRuntimeColorFilter);
382 SkASSERT(!result.effect || result.effect->allowColorFilter());
383 return result;
384 }
385
MakeForShader(SkString sksl,const Options & options)386 SkRuntimeEffect::Result SkRuntimeEffect::MakeForShader(SkString sksl, const Options& options) {
387 auto result = MakeFromSource(std::move(sksl), options, SkSL::ProgramKind::kRuntimeShader);
388 SkASSERT(!result.effect || result.effect->allowShader());
389 return result;
390 }
391
MakeForBlender(SkString sksl,const Options & options)392 SkRuntimeEffect::Result SkRuntimeEffect::MakeForBlender(SkString sksl, const Options& options) {
393 auto result = MakeFromSource(std::move(sksl), options, SkSL::ProgramKind::kRuntimeBlender);
394 SkASSERT(!result.effect || result.effect->allowBlender());
395 return result;
396 }
397
MakeForColorFilter(std::unique_ptr<SkSL::Program> program,const Options & options)398 SkRuntimeEffect::Result SkRuntimeEffect::MakeForColorFilter(std::unique_ptr<SkSL::Program> program,
399 const Options& options) {
400 auto result = MakeFromDSL(std::move(program), options, SkSL::ProgramKind::kRuntimeColorFilter);
401 SkASSERT(!result.effect || result.effect->allowColorFilter());
402 return result;
403 }
404
MakeForShader(std::unique_ptr<SkSL::Program> program,const Options & options)405 SkRuntimeEffect::Result SkRuntimeEffect::MakeForShader(std::unique_ptr<SkSL::Program> program,
406 const Options& options) {
407 auto result = MakeFromDSL(std::move(program), options, SkSL::ProgramKind::kRuntimeShader);
408 SkASSERT(!result.effect || result.effect->allowShader());
409 return result;
410 }
411
MakeForBlender(std::unique_ptr<SkSL::Program> program,const Options & options)412 SkRuntimeEffect::Result SkRuntimeEffect::MakeForBlender(std::unique_ptr<SkSL::Program> program,
413 const Options& options) {
414 auto result = MakeFromDSL(std::move(program), options, SkSL::ProgramKind::kRuntimeBlender);
415 SkASSERT(!result.effect || result.effect->allowBlender());
416 return result;
417 }
418
MakeForColorFilter(std::unique_ptr<SkSL::Program> program)419 SkRuntimeEffect::Result SkRuntimeEffect::MakeForColorFilter(
420 std::unique_ptr<SkSL::Program> program) {
421 return MakeForColorFilter(std::move(program), Options{});
422 }
423
MakeForShader(std::unique_ptr<SkSL::Program> program)424 SkRuntimeEffect::Result SkRuntimeEffect::MakeForShader(std::unique_ptr<SkSL::Program> program) {
425 return MakeForShader(std::move(program), Options{});
426 }
427
MakeForBlender(std::unique_ptr<SkSL::Program> program)428 SkRuntimeEffect::Result SkRuntimeEffect::MakeForBlender(std::unique_ptr<SkSL::Program> program) {
429 return MakeForBlender(std::move(program), Options{});
430 }
431
SkMakeCachedRuntimeEffect(SkRuntimeEffect::Result (* make)(SkString sksl),SkString sksl)432 sk_sp<SkRuntimeEffect> SkMakeCachedRuntimeEffect(SkRuntimeEffect::Result (*make)(SkString sksl),
433 SkString sksl) {
434 SK_BEGIN_REQUIRE_DENSE
435 struct Key {
436 uint32_t skslHashA;
437 uint32_t skslHashB;
438
439 bool operator==(const Key& that) const {
440 return this->skslHashA == that.skslHashA
441 && this->skslHashB == that.skslHashB;
442 }
443
444 explicit Key(const SkString& sksl)
445 : skslHashA(SkOpts::hash(sksl.c_str(), sksl.size(), 0))
446 , skslHashB(SkOpts::hash(sksl.c_str(), sksl.size(), 1)) {}
447 };
448 SK_END_REQUIRE_DENSE
449
450 static auto* mutex = new SkMutex;
451 static auto* cache = new SkLRUCache<Key, sk_sp<SkRuntimeEffect>>(11/*totally arbitrary*/);
452
453 Key key(sksl);
454 {
455 SkAutoMutexExclusive _(*mutex);
456 if (sk_sp<SkRuntimeEffect>* found = cache->find(key)) {
457 return *found;
458 }
459 }
460
461 auto [effect, err] = make(std::move(sksl));
462 if (!effect) {
463 return nullptr;
464 }
465 SkASSERT(err.isEmpty());
466
467 {
468 SkAutoMutexExclusive _(*mutex);
469 cache->insert_or_update(key, effect);
470 }
471 return effect;
472 }
473
uniform_element_size(SkRuntimeEffect::Uniform::Type type)474 static size_t uniform_element_size(SkRuntimeEffect::Uniform::Type type) {
475 switch (type) {
476 case SkRuntimeEffect::Uniform::Type::kFloat: return sizeof(float);
477 case SkRuntimeEffect::Uniform::Type::kFloat2: return sizeof(float) * 2;
478 case SkRuntimeEffect::Uniform::Type::kFloat3: return sizeof(float) * 3;
479 case SkRuntimeEffect::Uniform::Type::kFloat4: return sizeof(float) * 4;
480
481 case SkRuntimeEffect::Uniform::Type::kFloat2x2: return sizeof(float) * 4;
482 case SkRuntimeEffect::Uniform::Type::kFloat3x3: return sizeof(float) * 9;
483 case SkRuntimeEffect::Uniform::Type::kFloat4x4: return sizeof(float) * 16;
484
485 case SkRuntimeEffect::Uniform::Type::kInt: return sizeof(int);
486 case SkRuntimeEffect::Uniform::Type::kInt2: return sizeof(int) * 2;
487 case SkRuntimeEffect::Uniform::Type::kInt3: return sizeof(int) * 3;
488 case SkRuntimeEffect::Uniform::Type::kInt4: return sizeof(int) * 4;
489 default: SkUNREACHABLE;
490 }
491 }
492
sizeInBytes() const493 size_t SkRuntimeEffect::Uniform::sizeInBytes() const {
494 static_assert(sizeof(int) == sizeof(float));
495 return uniform_element_size(this->type) * this->count;
496 }
497
SkRuntimeEffect(std::unique_ptr<SkSL::Program> baseProgram,const Options & options,const SkSL::FunctionDefinition & main,std::vector<Uniform> && uniforms,std::vector<Child> && children,std::vector<SkSL::SampleUsage> && sampleUsages,uint32_t flags)498 SkRuntimeEffect::SkRuntimeEffect(std::unique_ptr<SkSL::Program> baseProgram,
499 const Options& options,
500 const SkSL::FunctionDefinition& main,
501 std::vector<Uniform>&& uniforms,
502 std::vector<Child>&& children,
503 std::vector<SkSL::SampleUsage>&& sampleUsages,
504 uint32_t flags)
505 : fHash(SkOpts::hash_fn(baseProgram->fSource->c_str(), baseProgram->fSource->size(), 0))
506 , fBaseProgram(std::move(baseProgram))
507 , fMain(main)
508 , fUniforms(std::move(uniforms))
509 , fChildren(std::move(children))
510 , fSampleUsages(std::move(sampleUsages))
511 , fFlags(flags) {
512 SkASSERT(fBaseProgram);
513 SkASSERT(fChildren.size() == fSampleUsages.size());
514
515 // Everything from SkRuntimeEffect::Options which could influence the compiled result needs to
516 // be accounted for in `fHash`. If you've added a new field to Options and caused the static-
517 // assert below to trigger, please incorporate your field into `fHash` and update KnownOptions
518 // to match the layout of Options.
519 struct KnownOptions { bool forceNoInline, enforceES2Restrictions, allowFragCoord; };
520 static_assert(sizeof(Options) == sizeof(KnownOptions));
521 fHash = SkOpts::hash_fn(&options.forceNoInline,
522 sizeof(options.forceNoInline), fHash);
523 fHash = SkOpts::hash_fn(&options.enforceES2Restrictions,
524 sizeof(options.enforceES2Restrictions), fHash);
525 fHash = SkOpts::hash_fn(&options.allowFragCoord,
526 sizeof(options.allowFragCoord), fHash);
527
528 fFilterColorProgram = SkFilterColorProgram::Make(this);
529 }
530
531 SkRuntimeEffect::~SkRuntimeEffect() = default;
532
source() const533 const std::string& SkRuntimeEffect::source() const {
534 return *fBaseProgram->fSource;
535 }
536
uniformSize() const537 size_t SkRuntimeEffect::uniformSize() const {
538 return fUniforms.empty() ? 0
539 : SkAlign4(fUniforms.back().offset + fUniforms.back().sizeInBytes());
540 }
541
findUniform(const char * name) const542 const SkRuntimeEffect::Uniform* SkRuntimeEffect::findUniform(const char* name) const {
543 SkASSERT(name);
544 size_t len = strlen(name);
545 auto iter = std::find_if(fUniforms.begin(), fUniforms.end(), [name, len](const Uniform& u) {
546 return u.name.equals(name, len);
547 });
548 return iter == fUniforms.end() ? nullptr : &(*iter);
549 }
550
findChild(const char * name) const551 const SkRuntimeEffect::Child* SkRuntimeEffect::findChild(const char* name) const {
552 SkASSERT(name);
553 size_t len = strlen(name);
554 auto iter = std::find_if(fChildren.begin(), fChildren.end(), [name, len](const Child& c) {
555 return c.name.equals(name, len);
556 });
557 return iter == fChildren.end() ? nullptr : &(*iter);
558 }
559
Make(const SkRuntimeEffect * effect)560 std::unique_ptr<SkFilterColorProgram> SkFilterColorProgram::Make(const SkRuntimeEffect* effect) {
561 // Our per-effect program technique is only possible (and necessary) for color filters
562 if (!effect->allowColorFilter()) {
563 return nullptr;
564 }
565
566 // We require that any children are color filters (not shaders or blenders). In theory, we could
567 // detect the coords being passed to shader children, and replicate those calls, but that's very
568 // complicated, and has diminishing returns. (eg, for table lookup color filters).
569 if (!std::all_of(effect->fChildren.begin(),
570 effect->fChildren.end(),
571 [](const SkRuntimeEffect::Child& c) {
572 return c.type == ChildType::kColorFilter;
573 })) {
574 return nullptr;
575 }
576
577 skvm::Builder p;
578
579 // For SkSL uniforms, we reserve space and allocate skvm Uniform ids for each one. When we run
580 // the program, these ids will be loads from the *first* arg ptr, the uniform data of the
581 // specific color filter instance.
582 skvm::Uniforms skslUniforms{p.uniform(), 0};
583 const size_t uniformCount = effect->uniformSize() / 4;
584 std::vector<skvm::Val> uniform;
585 uniform.reserve(uniformCount);
586 for (size_t i = 0; i < uniformCount; i++) {
587 uniform.push_back(p.uniform32(skslUniforms.push(/*placeholder*/ 0)).id);
588 }
589
590 auto is_simple_uniform = [&](skvm::Color c, int* baseOffset) {
591 skvm::Uniform ur, ug, ub, ua;
592 if (!p.allUniform(c.r.id, &ur, c.g.id, &ug, c.b.id, &ub, c.a.id, &ua)) {
593 return false;
594 }
595 skvm::Ptr uniPtr = skslUniforms.base;
596 if (ur.ptr != uniPtr || ug.ptr != uniPtr || ub.ptr != uniPtr || ua.ptr != uniPtr) {
597 return false;
598 }
599 *baseOffset = ur.offset;
600 return ug.offset == ur.offset + 4 &&
601 ub.offset == ur.offset + 8 &&
602 ua.offset == ur.offset + 12;
603 };
604
605 // We reserve a uniform color for each child invocation. While processing the SkSL, we record
606 // the index of the child, and the color being filtered (in a SampleCall struct).
607 // When we run this program later, we use the SampleCall to evaluate the correct child, and
608 // populate these uniform values. These Uniform ids are loads from the *second* arg ptr.
609 // If the color being passed is too complex for us to describe and re-create using SampleCall,
610 // we are unable to use this per-effect program, and callers will need to fall back to another
611 // (slower) implementation.
612 skvm::Uniforms childColorUniforms{p.uniform(), 0};
613 skvm::Color inputColor = p.uniformColor(/*placeholder*/ SkColors::kWhite, &childColorUniforms);
614 std::vector<SkFilterColorProgram::SampleCall> sampleCalls;
615 std::vector<skvm::Color> childColors;
616 auto ids_equal = [](skvm::Color x, skvm::Color y) {
617 return x.r.id == y.r.id && x.g.id == y.g.id && x.b.id == y.b.id && x.a.id == y.a.id;
618 };
619 bool allSampleCallsSupported = true;
620 auto sampleColorFilter = [&](int ix, skvm::Color c) {
621 skvm::Color result = p.uniformColor(/*placeholder*/ SkColors::kWhite, &childColorUniforms);
622 SkFilterColorProgram::SampleCall call;
623 call.fChild = ix;
624 if (ids_equal(c, inputColor)) {
625 call.fKind = SkFilterColorProgram::SampleCall::Kind::kInputColor;
626 } else if (p.allImm(c.r.id, &call.fImm.fR,
627 c.g.id, &call.fImm.fG,
628 c.b.id, &call.fImm.fB,
629 c.a.id, &call.fImm.fA)) {
630 call.fKind = SkFilterColorProgram::SampleCall::Kind::kImmediate;
631 } else if (auto it = std::find_if(childColors.begin(),
632 childColors.end(),
633 [&](skvm::Color x) { return ids_equal(x, c); });
634 it != childColors.end()) {
635 call.fKind = SkFilterColorProgram::SampleCall::Kind::kPrevious;
636 call.fPrevious = SkTo<int>(it - childColors.begin());
637 } else if (is_simple_uniform(c, &call.fOffset)) {
638 call.fKind = SkFilterColorProgram::SampleCall::Kind::kUniform;
639 } else {
640 allSampleCallsSupported = false;
641 }
642 sampleCalls.push_back(call);
643 childColors.push_back(result);
644 return result;
645 };
646
647 // Emit the skvm instructions for the SkSL
648 skvm::Coord zeroCoord = {p.splat(0.0f), p.splat(0.0f)};
649 skvm::Color result = SkSL::ProgramToSkVM(*effect->fBaseProgram,
650 effect->fMain,
651 &p,
652 /*debugInfo=*/nullptr,
653 SkMakeSpan(uniform),
654 /*device=*/zeroCoord,
655 /*local=*/zeroCoord,
656 inputColor,
657 inputColor,
658 /*sampleShader=*/nullptr,
659 sampleColorFilter,
660 /*sampleBlender=*/nullptr);
661
662 // Then store the result to the *third* arg ptr
663 p.store({skvm::PixelFormat::FLOAT, 32, 32, 32, 32, 0, 32, 64, 96},
664 p.varying<skvm::F32>(), result);
665
666 if (!allSampleCallsSupported) {
667 return nullptr;
668 }
669
670 // This is conservative. If a filter gets the input color by sampling a null child, we'll
671 // return an (acceptable) false negative. All internal runtime color filters should work.
672 bool alphaUnchanged = (inputColor.a.id == result.a.id);
673
674 // We'll use this program to filter one color at a time, don't bother with jit
675 return std::unique_ptr<SkFilterColorProgram>(
676 new SkFilterColorProgram(p.done(/*debug_name=*/nullptr, /*allow_jit=*/false),
677 std::move(sampleCalls),
678 alphaUnchanged));
679 }
680
SkFilterColorProgram(skvm::Program program,std::vector<SampleCall> sampleCalls,bool alphaUnchanged)681 SkFilterColorProgram::SkFilterColorProgram(skvm::Program program,
682 std::vector<SampleCall> sampleCalls,
683 bool alphaUnchanged)
684 : fProgram(std::move(program))
685 , fSampleCalls(std::move(sampleCalls))
686 , fAlphaUnchanged(alphaUnchanged) {}
687
eval(const SkPMColor4f & inColor,const void * uniformData,std::function<SkPMColor4f (int,SkPMColor4f)> evalChild) const688 SkPMColor4f SkFilterColorProgram::eval(
689 const SkPMColor4f& inColor,
690 const void* uniformData,
691 std::function<SkPMColor4f(int, SkPMColor4f)> evalChild) const {
692 // Our program defines sampling any child as returning a uniform color. Assemble a buffer
693 // containing those colors. The first entry is always the input color. Subsequent entries
694 // are for each sample call, based on the information in fSampleCalls. For any null children,
695 // the sample result is just the passed-in color.
696 SkSTArray<4, SkPMColor4f, true> childColors;
697 childColors.push_back(inColor);
698 for (const auto& s : fSampleCalls) {
699 SkPMColor4f passedColor = inColor;
700 switch (s.fKind) {
701 case SampleCall::Kind::kInputColor: break;
702 case SampleCall::Kind::kImmediate: passedColor = s.fImm; break;
703 case SampleCall::Kind::kPrevious: passedColor = childColors[s.fPrevious + 1]; break;
704 case SampleCall::Kind::kUniform:
705 passedColor = *SkTAddOffset<const SkPMColor4f>(uniformData, s.fOffset);
706 break;
707 }
708 childColors.push_back(evalChild(s.fChild, passedColor));
709 }
710
711 SkPMColor4f result;
712 fProgram.eval(1, uniformData, childColors.begin(), result.vec());
713 return result;
714 }
715
getFilterColorProgram()716 const SkFilterColorProgram* SkRuntimeEffect::getFilterColorProgram() {
717 return fFilterColorProgram.get();
718 }
719
720 ///////////////////////////////////////////////////////////////////////////////////////////////////
721
get_xformed_uniforms(const SkRuntimeEffect * effect,sk_sp<SkData> baseUniforms,const SkColorSpace * dstCS)722 static sk_sp<SkData> get_xformed_uniforms(const SkRuntimeEffect* effect,
723 sk_sp<SkData> baseUniforms,
724 const SkColorSpace* dstCS) {
725 using Flags = SkRuntimeEffect::Uniform::Flags;
726 using Type = SkRuntimeEffect::Uniform::Type;
727 SkColorSpaceXformSteps steps(sk_srgb_singleton(), kUnpremul_SkAlphaType,
728 dstCS, kUnpremul_SkAlphaType);
729
730 sk_sp<SkData> uniforms = nullptr;
731 auto writableData = [&]() {
732 if (!uniforms) {
733 uniforms = SkData::MakeWithCopy(baseUniforms->data(), baseUniforms->size());
734 }
735 return uniforms->writable_data();
736 };
737
738 for (const auto& v : effect->uniforms()) {
739 if (v.flags & Flags::kSRGBUnpremul_Flag) {
740 SkASSERT(v.type == Type::kFloat3 || v.type == Type::kFloat4);
741 if (steps.flags.mask()) {
742 float* color = SkTAddOffset<float>(writableData(), v.offset);
743 if (v.type == Type::kFloat4) {
744 // RGBA, easy case
745 for (int i = 0; i < v.count; ++i) {
746 steps.apply(color);
747 color += 4;
748 }
749 } else {
750 // RGB, need to pad out to include alpha. Technically, this isn't necessary,
751 // because steps shouldn't include unpremul or premul, and thus shouldn't
752 // read or write the fourth element. But let's be safe.
753 float rgba[4];
754 for (int i = 0; i < v.count; ++i) {
755 memcpy(rgba, color, 3 * sizeof(float));
756 rgba[3] = 1.0f;
757 steps.apply(rgba);
758 memcpy(color, rgba, 3 * sizeof(float));
759 color += 3;
760 }
761 }
762 }
763 }
764 }
765 return uniforms ? uniforms : baseUniforms;
766 }
767
768 #if SK_SUPPORT_GPU
make_effect_fp(sk_sp<SkRuntimeEffect> effect,const char * name,sk_sp<SkData> uniforms,std::unique_ptr<GrFragmentProcessor> inputFP,std::unique_ptr<GrFragmentProcessor> destColorFP,SkSpan<const SkRuntimeEffect::ChildPtr> children,const GrFPArgs & childArgs)769 static GrFPResult make_effect_fp(sk_sp<SkRuntimeEffect> effect,
770 const char* name,
771 sk_sp<SkData> uniforms,
772 std::unique_ptr<GrFragmentProcessor> inputFP,
773 std::unique_ptr<GrFragmentProcessor> destColorFP,
774 SkSpan<const SkRuntimeEffect::ChildPtr> children,
775 const GrFPArgs& childArgs) {
776 SkSTArray<8, std::unique_ptr<GrFragmentProcessor>> childFPs;
777 for (const auto& child : children) {
778 skstd::optional<ChildType> type = child.type();
779 if (type == ChildType::kShader) {
780 // Convert a SkShader into a child FP.
781 auto childFP = as_SB(child.shader())->asFragmentProcessor(childArgs);
782 if (!childFP) {
783 return GrFPFailure(std::move(inputFP));
784 }
785 childFPs.push_back(std::move(childFP));
786 } else if (type == ChildType::kColorFilter) {
787 // Convert a SkColorFilter into a child FP.
788 auto [success, childFP] = as_CFB(child.colorFilter())
789 ->asFragmentProcessor(/*inputFP=*/nullptr,
790 childArgs.fContext,
791 *childArgs.fDstColorInfo);
792 if (!success) {
793 return GrFPFailure(std::move(inputFP));
794 }
795 childFPs.push_back(std::move(childFP));
796 } else if (type == ChildType::kBlender) {
797 // Convert a SkBlender into a child FP.
798 auto childFP = as_BB(child.blender())->asFragmentProcessor(/*srcFP=*/nullptr,
799 /*dstFP=*/nullptr,
800 childArgs);
801 if (!childFP) {
802 return GrFPFailure(std::move(inputFP));
803 }
804 childFPs.push_back(std::move(childFP));
805 } else {
806 // We have a null child effect.
807 childFPs.push_back(nullptr);
808 }
809 }
810 auto fp = GrSkSLFP::MakeWithData(std::move(effect),
811 name,
812 std::move(inputFP),
813 std::move(destColorFP),
814 std::move(uniforms),
815 SkMakeSpan(childFPs));
816 SkASSERT(fp);
817 return GrFPSuccess(std::move(fp));
818 }
819 #endif
820
821 class SkRuntimeColorFilter : public SkColorFilterBase {
822 public:
SkRuntimeColorFilter(sk_sp<SkRuntimeEffect> effect,sk_sp<SkData> uniforms,SkSpan<SkRuntimeEffect::ChildPtr> children)823 SkRuntimeColorFilter(sk_sp<SkRuntimeEffect> effect,
824 sk_sp<SkData> uniforms,
825 SkSpan<SkRuntimeEffect::ChildPtr> children)
826 : fEffect(std::move(effect))
827 , fUniforms(std::move(uniforms))
828 , fChildren(children.begin(), children.end()) {}
829
830 #if SK_SUPPORT_GPU
asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,GrRecordingContext * context,const GrColorInfo & colorInfo) const831 GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
832 GrRecordingContext* context,
833 const GrColorInfo& colorInfo) const override {
834 sk_sp<SkData> uniforms =
835 get_xformed_uniforms(fEffect.get(), fUniforms, colorInfo.colorSpace());
836 SkASSERT(uniforms);
837
838 GrFPArgs childArgs(context, SkSimpleMatrixProvider(SkMatrix::I()), &colorInfo);
839 return make_effect_fp(fEffect,
840 "runtime_color_filter",
841 std::move(uniforms),
842 std::move(inputFP),
843 /*destColorFP=*/nullptr,
844 SkMakeSpan(fChildren),
845 childArgs);
846 }
847 #endif
848
onAppendStages(const SkStageRec & rec,bool shaderIsOpaque) const849 bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
850 return false;
851 }
852
onProgram(skvm::Builder * p,skvm::Color c,const SkColorInfo & colorInfo,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const853 skvm::Color onProgram(skvm::Builder* p, skvm::Color c,
854 const SkColorInfo& colorInfo,
855 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
856 sk_sp<SkData> inputs =
857 get_xformed_uniforms(fEffect.get(), fUniforms, colorInfo.colorSpace());
858 SkASSERT(inputs);
859
860 auto sampleShader = [&](int ix, skvm::Coord coord) {
861 if (SkShader* shader = fChildren[ix].shader()) {
862 SkSimpleMatrixProvider mats{SkMatrix::I()};
863 return as_SB(shader)->program(p, coord, coord, c, mats, /*localM=*/nullptr,
864 colorInfo, uniforms, alloc);
865 }
866 return c;
867 };
868 auto sampleColorFilter = [&](int ix, skvm::Color color) {
869 if (SkColorFilter* colorFilter = fChildren[ix].colorFilter()) {
870 return as_CFB(colorFilter)->program(p, color, colorInfo, uniforms, alloc);
871 }
872 return color;
873 };
874 auto sampleBlender = [&](int ix, skvm::Color src, skvm::Color dst) {
875 if (SkBlender* blender = fChildren[ix].blender()) {
876 return as_BB(blender)->program(p, src, dst, colorInfo, uniforms, alloc);
877 }
878 return blend(SkBlendMode::kSrcOver, src, dst);
879 };
880
881 std::vector<skvm::Val> uniform = make_skvm_uniforms(p, uniforms, fEffect->uniformSize(),
882 *inputs);
883
884 // There should be no way for the color filter to use device coords, but we need to supply
885 // something. (Uninitialized values can trigger asserts in skvm::Builder).
886 skvm::Coord zeroCoord = { p->splat(0.0f), p->splat(0.0f) };
887 return SkSL::ProgramToSkVM(*fEffect->fBaseProgram, fEffect->fMain, p, /*debugInfo=*/nullptr,
888 SkMakeSpan(uniform), /*device=*/zeroCoord, /*local=*/zeroCoord,
889 c, c, sampleShader, sampleColorFilter, sampleBlender);
890 }
891
onFilterColor4f(const SkPMColor4f & color,SkColorSpace * dstCS) const892 SkPMColor4f onFilterColor4f(const SkPMColor4f& color, SkColorSpace* dstCS) const override {
893 // Get the generic program for filtering a single color
894 const SkFilterColorProgram* program = fEffect->getFilterColorProgram();
895 if (!program) {
896 // We were unable to build a cached (per-effect) program. Use the base-class fallback,
897 // which builds a program for the specific filter instance.
898 return SkColorFilterBase::onFilterColor4f(color, dstCS);
899 }
900
901 // Get our specific uniform values
902 sk_sp<SkData> inputs = get_xformed_uniforms(fEffect.get(), fUniforms, dstCS);
903 SkASSERT(inputs);
904
905 auto evalChild = [&](int index, SkPMColor4f inColor) {
906 const auto& child = fChildren[index];
907
908 // SkFilterColorProgram::Make has guaranteed that any children will be color filters.
909 SkASSERT(!child.shader());
910 SkASSERT(!child.blender());
911 if (SkColorFilter* colorFilter = child.colorFilter()) {
912 return as_CFB(colorFilter)->onFilterColor4f(inColor, dstCS);
913 }
914 return inColor;
915 };
916
917 return program->eval(color, inputs->data(), evalChild);
918 }
919
onIsAlphaUnchanged() const920 bool onIsAlphaUnchanged() const override {
921 return fEffect->getFilterColorProgram() &&
922 fEffect->getFilterColorProgram()->isAlphaUnchanged();
923 }
924
flatten(SkWriteBuffer & buffer) const925 void flatten(SkWriteBuffer& buffer) const override {
926 buffer.writeString(fEffect->source().c_str());
927 buffer.writeDataAsByteArray(fUniforms.get());
928 write_child_effects(buffer, fChildren);
929 }
930
931 SK_FLATTENABLE_HOOKS(SkRuntimeColorFilter)
932
933 private:
934 sk_sp<SkRuntimeEffect> fEffect;
935 sk_sp<SkData> fUniforms;
936 std::vector<SkRuntimeEffect::ChildPtr> fChildren;
937 };
938
CreateProc(SkReadBuffer & buffer)939 sk_sp<SkFlattenable> SkRuntimeColorFilter::CreateProc(SkReadBuffer& buffer) {
940 SkString sksl;
941 buffer.readString(&sksl);
942 sk_sp<SkData> uniforms = buffer.readByteArrayAsData();
943
944 auto effect = SkMakeCachedRuntimeEffect(SkRuntimeEffect::MakeForColorFilter, std::move(sksl));
945 if (!buffer.validate(effect != nullptr)) {
946 return nullptr;
947 }
948
949 SkSTArray<4, SkRuntimeEffect::ChildPtr> children;
950 if (!read_child_effects(buffer, effect.get(), &children)) {
951 return nullptr;
952 }
953
954 return effect->makeColorFilter(std::move(uniforms), SkMakeSpan(children));
955 }
956
957 ///////////////////////////////////////////////////////////////////////////////////////////////////
958
959 class SkRTShader : public SkShaderBase {
960 public:
SkRTShader(sk_sp<SkRuntimeEffect> effect,sk_sp<SkData> uniforms,const SkMatrix * localMatrix,SkSpan<SkRuntimeEffect::ChildPtr> children,bool isOpaque)961 SkRTShader(sk_sp<SkRuntimeEffect> effect,
962 sk_sp<SkData> uniforms,
963 const SkMatrix* localMatrix,
964 SkSpan<SkRuntimeEffect::ChildPtr> children,
965 bool isOpaque)
966 : SkShaderBase(localMatrix)
967 , fEffect(std::move(effect))
968 , fIsOpaque(isOpaque)
969 , fUniforms(std::move(uniforms))
970 , fChildren(children.begin(), children.end()) {}
971
isOpaque() const972 bool isOpaque() const override { return fIsOpaque; }
973
974 #if SK_SUPPORT_GPU
asFragmentProcessor(const GrFPArgs & args) const975 std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs& args) const override {
976 SkMatrix matrix;
977 if (!this->totalLocalMatrix(args.fPreLocalMatrix)->invert(&matrix)) {
978 return nullptr;
979 }
980
981 sk_sp<SkData> uniforms =
982 get_xformed_uniforms(fEffect.get(), fUniforms, args.fDstColorInfo->colorSpace());
983 SkASSERT(uniforms);
984
985 auto [success, fp] = make_effect_fp(fEffect,
986 "runtime_shader",
987 std::move(uniforms),
988 /*inputFP=*/nullptr,
989 /*destColorFP=*/nullptr,
990 SkMakeSpan(fChildren),
991 args);
992 if (!success) {
993 return nullptr;
994 }
995
996 // If the shader was created with isOpaque = true, we *force* that result here.
997 // CPU does the same thing (in SkShaderBase::program).
998 if (fIsOpaque) {
999 fp = GrFragmentProcessor::SwizzleOutput(std::move(fp), GrSwizzle::RGB1());
1000 }
1001 return GrMatrixEffect::Make(matrix, std::move(fp));
1002 }
1003 #endif
1004
onAppendStages(const SkStageRec & rec) const1005 bool onAppendStages(const SkStageRec& rec) const override {
1006 return false;
1007 }
1008
onProgram(skvm::Builder * p,skvm::Coord device,skvm::Coord local,skvm::Color paint,const SkMatrixProvider & matrices,const SkMatrix * localM,const SkColorInfo & colorInfo,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const1009 skvm::Color onProgram(skvm::Builder* p,
1010 skvm::Coord device, skvm::Coord local, skvm::Color paint,
1011 const SkMatrixProvider& matrices, const SkMatrix* localM,
1012 const SkColorInfo& colorInfo,
1013 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
1014 sk_sp<SkData> inputs =
1015 get_xformed_uniforms(fEffect.get(), fUniforms, colorInfo.colorSpace());
1016 SkASSERT(inputs);
1017
1018 SkMatrix inv;
1019 if (!this->computeTotalInverse(matrices.localToDevice(), localM, &inv)) {
1020 return {};
1021 }
1022 local = SkShaderBase::ApplyMatrix(p,inv,local,uniforms);
1023
1024 auto sampleShader = [&](int ix, skvm::Coord coord) {
1025 if (SkShader* shader = fChildren[ix].shader()) {
1026 SkOverrideDeviceMatrixProvider mats{matrices, SkMatrix::I()};
1027 return as_SB(shader)->program(p, device, coord, paint, mats, /*localM=*/nullptr,
1028 colorInfo, uniforms, alloc);
1029 }
1030 return paint;
1031 };
1032 auto sampleColorFilter = [&](int ix, skvm::Color color) {
1033 if (SkColorFilter* colorFilter = fChildren[ix].colorFilter()) {
1034 return as_CFB(colorFilter)->program(p, color, colorInfo, uniforms, alloc);
1035 }
1036 return color;
1037 };
1038 auto sampleBlender = [&](int ix, skvm::Color src, skvm::Color dst) {
1039 if (SkBlender* blender = fChildren[ix].blender()) {
1040 return as_BB(blender)->program(p, src, dst, colorInfo, uniforms, alloc);
1041 }
1042 return blend(SkBlendMode::kSrcOver, src, dst);
1043 };
1044
1045 std::vector<skvm::Val> uniform = make_skvm_uniforms(p, uniforms, fEffect->uniformSize(),
1046 *inputs);
1047
1048 return SkSL::ProgramToSkVM(*fEffect->fBaseProgram, fEffect->fMain, p, /*debugInfo=*/nullptr,
1049 SkMakeSpan(uniform), device, local, paint, paint, sampleShader,
1050 sampleColorFilter, sampleBlender);
1051 }
1052
flatten(SkWriteBuffer & buffer) const1053 void flatten(SkWriteBuffer& buffer) const override {
1054 uint32_t flags = 0;
1055 if (fIsOpaque) {
1056 flags |= kIsOpaque_Flag;
1057 }
1058 if (!this->getLocalMatrix().isIdentity()) {
1059 flags |= kHasLocalMatrix_Flag;
1060 }
1061
1062 buffer.writeString(fEffect->source().c_str());
1063 buffer.writeDataAsByteArray(fUniforms.get());
1064 buffer.write32(flags);
1065 if (flags & kHasLocalMatrix_Flag) {
1066 buffer.writeMatrix(this->getLocalMatrix());
1067 }
1068 write_child_effects(buffer, fChildren);
1069 }
1070
asRuntimeEffect() const1071 SkRuntimeEffect* asRuntimeEffect() const override { return fEffect.get(); }
1072
1073 SK_FLATTENABLE_HOOKS(SkRTShader)
1074
1075 private:
1076 enum Flags {
1077 kIsOpaque_Flag = 1 << 0,
1078 kHasLocalMatrix_Flag = 1 << 1,
1079 };
1080
1081 sk_sp<SkRuntimeEffect> fEffect;
1082 bool fIsOpaque;
1083
1084 sk_sp<SkData> fUniforms;
1085 std::vector<SkRuntimeEffect::ChildPtr> fChildren;
1086 };
1087
CreateProc(SkReadBuffer & buffer)1088 sk_sp<SkFlattenable> SkRTShader::CreateProc(SkReadBuffer& buffer) {
1089 SkString sksl;
1090 buffer.readString(&sksl);
1091 sk_sp<SkData> uniforms = buffer.readByteArrayAsData();
1092 uint32_t flags = buffer.read32();
1093
1094 bool isOpaque = SkToBool(flags & kIsOpaque_Flag);
1095 SkMatrix localM, *localMPtr = nullptr;
1096 if (flags & kHasLocalMatrix_Flag) {
1097 buffer.readMatrix(&localM);
1098 localMPtr = &localM;
1099 }
1100
1101 auto effect = SkMakeCachedRuntimeEffect(SkRuntimeEffect::MakeForShader, std::move(sksl));
1102 if (!buffer.validate(effect != nullptr)) {
1103 return nullptr;
1104 }
1105
1106 SkSTArray<4, SkRuntimeEffect::ChildPtr> children;
1107 if (!read_child_effects(buffer, effect.get(), &children)) {
1108 return nullptr;
1109 }
1110
1111 return effect->makeShader(std::move(uniforms), SkMakeSpan(children), localMPtr, isOpaque);
1112 }
1113
1114 ///////////////////////////////////////////////////////////////////////////////////////////////////
1115
1116 class SkRuntimeBlender : public SkBlenderBase {
1117 public:
SkRuntimeBlender(sk_sp<SkRuntimeEffect> effect,sk_sp<SkData> uniforms,SkSpan<SkRuntimeEffect::ChildPtr> children)1118 SkRuntimeBlender(sk_sp<SkRuntimeEffect> effect,
1119 sk_sp<SkData> uniforms,
1120 SkSpan<SkRuntimeEffect::ChildPtr> children)
1121 : fEffect(std::move(effect))
1122 , fUniforms(std::move(uniforms))
1123 , fChildren(children.begin(), children.end()) {}
1124
asRuntimeEffect() const1125 SkRuntimeEffect* asRuntimeEffect() const override { return fEffect.get(); }
1126
onProgram(skvm::Builder * p,skvm::Color src,skvm::Color dst,const SkColorInfo & colorInfo,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const1127 skvm::Color onProgram(skvm::Builder* p, skvm::Color src, skvm::Color dst,
1128 const SkColorInfo& colorInfo, skvm::Uniforms* uniforms,
1129 SkArenaAlloc* alloc) const override {
1130 sk_sp<SkData> inputs = get_xformed_uniforms(fEffect.get(), fUniforms,
1131 colorInfo.colorSpace());
1132 SkASSERT(inputs);
1133
1134 auto sampleShader = [&](int ix, skvm::Coord coord) {
1135 if (SkShader* shader = fChildren[ix].shader()) {
1136 SkSimpleMatrixProvider mats{SkMatrix::I()};
1137 return as_SB(shader)->program(p, coord, coord, src, mats, /*localM=*/nullptr,
1138 colorInfo, uniforms, alloc);
1139 }
1140 return src;
1141 };
1142 auto sampleColorFilter = [&](int ix, skvm::Color color) {
1143 if (SkColorFilter* colorFilter = fChildren[ix].colorFilter()) {
1144 return as_CFB(colorFilter)->program(p, color, colorInfo, uniforms, alloc);
1145 }
1146 return color;
1147 };
1148 auto sampleBlender = [&](int ix, skvm::Color src, skvm::Color dst) {
1149 if (SkBlender* blender = fChildren[ix].blender()) {
1150 return as_BB(blender)->program(p, src, dst, colorInfo, uniforms, alloc);
1151 }
1152 return blend(SkBlendMode::kSrcOver, src, dst);
1153 };
1154
1155 std::vector<skvm::Val> uniform = make_skvm_uniforms(p, uniforms, fEffect->uniformSize(),
1156 *inputs);
1157
1158 // Emit the blend function as an SkVM program.
1159 skvm::Coord zeroCoord = {p->splat(0.0f), p->splat(0.0f)};
1160 return SkSL::ProgramToSkVM(*fEffect->fBaseProgram, fEffect->fMain, p, /*debugInfo=*/nullptr,
1161 SkMakeSpan(uniform), /*device=*/zeroCoord, /*local=*/zeroCoord,
1162 src, dst, sampleShader, sampleColorFilter, sampleBlender);
1163 }
1164
1165 #if SK_SUPPORT_GPU
asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> srcFP,std::unique_ptr<GrFragmentProcessor> dstFP,const GrFPArgs & args) const1166 std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(
1167 std::unique_ptr<GrFragmentProcessor> srcFP,
1168 std::unique_ptr<GrFragmentProcessor> dstFP,
1169 const GrFPArgs& args) const override {
1170 sk_sp<SkData> uniforms = get_xformed_uniforms(fEffect.get(), fUniforms,
1171 args.fDstColorInfo->colorSpace());
1172 SkASSERT(uniforms);
1173 auto [success, fp] = make_effect_fp(fEffect,
1174 "runtime_blender",
1175 std::move(uniforms),
1176 std::move(srcFP),
1177 std::move(dstFP),
1178 SkMakeSpan(fChildren),
1179 args);
1180
1181 return success ? std::move(fp) : nullptr;
1182 }
1183 #endif
1184
flatten(SkWriteBuffer & buffer) const1185 void flatten(SkWriteBuffer& buffer) const override {
1186 buffer.writeString(fEffect->source().c_str());
1187 buffer.writeDataAsByteArray(fUniforms.get());
1188 write_child_effects(buffer, fChildren);
1189 }
1190
1191 SK_FLATTENABLE_HOOKS(SkRuntimeBlender)
1192
1193 private:
1194 using INHERITED = SkBlenderBase;
1195
1196 sk_sp<SkRuntimeEffect> fEffect;
1197 sk_sp<SkData> fUniforms;
1198 std::vector<SkRuntimeEffect::ChildPtr> fChildren;
1199 };
1200
CreateProc(SkReadBuffer & buffer)1201 sk_sp<SkFlattenable> SkRuntimeBlender::CreateProc(SkReadBuffer& buffer) {
1202 SkString sksl;
1203 buffer.readString(&sksl);
1204 sk_sp<SkData> uniforms = buffer.readByteArrayAsData();
1205
1206 auto effect = SkMakeCachedRuntimeEffect(SkRuntimeEffect::MakeForBlender, std::move(sksl));
1207 if (!buffer.validate(effect != nullptr)) {
1208 return nullptr;
1209 }
1210
1211 SkSTArray<4, SkRuntimeEffect::ChildPtr> children;
1212 if (!read_child_effects(buffer, effect.get(), &children)) {
1213 return nullptr;
1214 }
1215
1216 return effect->makeBlender(std::move(uniforms), SkMakeSpan(children));
1217 }
1218
1219 ///////////////////////////////////////////////////////////////////////////////////////////////////
1220
makeShader(sk_sp<SkData> uniforms,sk_sp<SkShader> childShaders[],size_t childCount,const SkMatrix * localMatrix,bool isOpaque) const1221 sk_sp<SkShader> SkRuntimeEffect::makeShader(sk_sp<SkData> uniforms,
1222 sk_sp<SkShader> childShaders[],
1223 size_t childCount,
1224 const SkMatrix* localMatrix,
1225 bool isOpaque) const {
1226 SkSTArray<4, ChildPtr> children(childCount);
1227 for (size_t i = 0; i < childCount; ++i) {
1228 children.emplace_back(childShaders[i]);
1229 }
1230 return this->makeShader(std::move(uniforms), SkMakeSpan(children), localMatrix, isOpaque);
1231 }
1232
makeShader(sk_sp<SkData> uniforms,SkSpan<ChildPtr> children,const SkMatrix * localMatrix,bool isOpaque) const1233 sk_sp<SkShader> SkRuntimeEffect::makeShader(sk_sp<SkData> uniforms,
1234 SkSpan<ChildPtr> children,
1235 const SkMatrix* localMatrix,
1236 bool isOpaque) const {
1237 if (!this->allowShader()) {
1238 return nullptr;
1239 }
1240 if (!verify_child_effects(fChildren, children)) {
1241 return nullptr;
1242 }
1243 if (!uniforms) {
1244 uniforms = SkData::MakeEmpty();
1245 }
1246 if (uniforms->size() != this->uniformSize()) {
1247 return nullptr;
1248 }
1249 return sk_sp<SkShader>(new SkRTShader(sk_ref_sp(this), std::move(uniforms), localMatrix,
1250 children, isOpaque));
1251 }
1252
makeImage(GrRecordingContext * rContext,sk_sp<SkData> uniforms,SkSpan<ChildPtr> children,const SkMatrix * localMatrix,SkImageInfo resultInfo,bool mipmapped) const1253 sk_sp<SkImage> SkRuntimeEffect::makeImage(GrRecordingContext* rContext,
1254 sk_sp<SkData> uniforms,
1255 SkSpan<ChildPtr> children,
1256 const SkMatrix* localMatrix,
1257 SkImageInfo resultInfo,
1258 bool mipmapped) const {
1259 if (rContext) {
1260 #if SK_SUPPORT_GPU
1261 if (!rContext->priv().caps()->mipmapSupport()) {
1262 mipmapped = false;
1263 }
1264 auto fillContext = rContext->priv().makeSFC(resultInfo,
1265 SkBackingFit::kExact,
1266 /*sample count*/ 1,
1267 GrMipmapped(mipmapped));
1268 if (!fillContext) {
1269 return nullptr;
1270 }
1271 uniforms = get_xformed_uniforms(this, std::move(uniforms), resultInfo.colorSpace());
1272 SkASSERT(uniforms);
1273
1274 SkSimpleMatrixProvider matrixProvider(SkMatrix::I());
1275 GrColorInfo colorInfo(resultInfo.colorInfo());
1276 GrFPArgs args(rContext, matrixProvider, &colorInfo);
1277 SkSTArray<8, std::unique_ptr<GrFragmentProcessor>> childFPs;
1278 for (size_t i = 0; i < children.size(); ++i) {
1279 // TODO: add support for other types of child effects
1280 if (SkShader* shader = children[i].shader()) {
1281 childFPs.push_back(as_SB(shader)->asFragmentProcessor(args));
1282 } else {
1283 return nullptr;
1284 }
1285 }
1286 auto fp = GrSkSLFP::MakeWithData(sk_ref_sp(this),
1287 "runtime_image",
1288 /*inputFP=*/nullptr,
1289 /*destColorFP=*/nullptr,
1290 std::move(uniforms),
1291 SkMakeSpan(childFPs));
1292
1293 if (localMatrix) {
1294 SkMatrix invLM;
1295 if (!localMatrix->invert(&invLM)) {
1296 return nullptr;
1297 }
1298 fillContext->fillWithFP(invLM, std::move(fp));
1299 } else {
1300 fillContext->fillWithFP(std::move(fp));
1301 }
1302 return sk_sp<SkImage>(new SkImage_Gpu(sk_ref_sp(rContext),
1303 kNeedNewImageUniqueID,
1304 fillContext->readSurfaceView(),
1305 resultInfo.colorInfo()));
1306 #else
1307 return nullptr;
1308 #endif
1309 }
1310 if (resultInfo.alphaType() == kUnpremul_SkAlphaType) {
1311 // We don't have a good way of supporting this right now. In this case the runtime effect
1312 // will produce a unpremul value. The shader generated from it is assumed to produce
1313 // premul and RGB get pinned to A. Moreover, after the blend in premul the new dst is
1314 // unpremul'ed, producing a double unpremul result.
1315 return nullptr;
1316 }
1317 auto surf = SkSurface::MakeRaster(resultInfo);
1318 if (!surf) {
1319 return nullptr;
1320 }
1321 SkCanvas* canvas = surf->getCanvas();
1322 SkTLazy<SkCanvas> tempCanvas;
1323 auto shader = this->makeShader(std::move(uniforms), children, localMatrix, false);
1324 if (!shader) {
1325 return nullptr;
1326 }
1327 SkPaint paint;
1328 paint.setShader(std::move(shader));
1329 paint.setBlendMode(SkBlendMode::kSrc);
1330 canvas->drawPaint(paint);
1331 // TODO: Specify snapshot should have mip levels if mipmapped is true.
1332 return surf->makeImageSnapshot();
1333 }
1334
makeColorFilter(sk_sp<SkData> uniforms,sk_sp<SkColorFilter> childColorFilters[],size_t childCount) const1335 sk_sp<SkColorFilter> SkRuntimeEffect::makeColorFilter(sk_sp<SkData> uniforms,
1336 sk_sp<SkColorFilter> childColorFilters[],
1337 size_t childCount) const {
1338 SkSTArray<4, ChildPtr> children(childCount);
1339 for (size_t i = 0; i < childCount; ++i) {
1340 children.emplace_back(childColorFilters[i]);
1341 }
1342 return this->makeColorFilter(std::move(uniforms), SkMakeSpan(children));
1343 }
1344
makeColorFilter(sk_sp<SkData> uniforms,SkSpan<ChildPtr> children) const1345 sk_sp<SkColorFilter> SkRuntimeEffect::makeColorFilter(sk_sp<SkData> uniforms,
1346 SkSpan<ChildPtr> children) const {
1347 if (!this->allowColorFilter()) {
1348 return nullptr;
1349 }
1350 if (!verify_child_effects(fChildren, children)) {
1351 return nullptr;
1352 }
1353 if (!uniforms) {
1354 uniforms = SkData::MakeEmpty();
1355 }
1356 if (uniforms->size() != this->uniformSize()) {
1357 return nullptr;
1358 }
1359 return sk_sp<SkColorFilter>(new SkRuntimeColorFilter(sk_ref_sp(this), std::move(uniforms),
1360 children));
1361 }
1362
makeColorFilter(sk_sp<SkData> uniforms) const1363 sk_sp<SkColorFilter> SkRuntimeEffect::makeColorFilter(sk_sp<SkData> uniforms) const {
1364 return this->makeColorFilter(std::move(uniforms), /*children=*/{});
1365 }
1366
makeBlender(sk_sp<SkData> uniforms,SkSpan<ChildPtr> children) const1367 sk_sp<SkBlender> SkRuntimeEffect::makeBlender(sk_sp<SkData> uniforms,
1368 SkSpan<ChildPtr> children) const {
1369 if (!this->allowBlender()) {
1370 return nullptr;
1371 }
1372 if (!verify_child_effects(fChildren, children)) {
1373 return nullptr;
1374 }
1375 if (!uniforms) {
1376 uniforms = SkData::MakeEmpty();
1377 }
1378 if (uniforms->size() != this->uniformSize()) {
1379 return nullptr;
1380 }
1381 return sk_sp<SkBlender>(new SkRuntimeBlender(sk_ref_sp(this), std::move(uniforms), children));
1382 }
1383
1384 ///////////////////////////////////////////////////////////////////////////////////////////////////
1385
type() const1386 skstd::optional<ChildType> SkRuntimeEffect::ChildPtr::type() const {
1387 if (fChild) {
1388 switch (fChild->getFlattenableType()) {
1389 case SkFlattenable::kSkShader_Type:
1390 return ChildType::kShader;
1391 case SkFlattenable::kSkColorFilter_Type:
1392 return ChildType::kColorFilter;
1393 case SkFlattenable::kSkBlender_Type:
1394 return ChildType::kBlender;
1395 default:
1396 break;
1397 }
1398 }
1399 return skstd::nullopt;
1400 }
1401
shader() const1402 SkShader* SkRuntimeEffect::ChildPtr::shader() const {
1403 return (fChild && fChild->getFlattenableType() == SkFlattenable::kSkShader_Type)
1404 ? static_cast<SkShader*>(fChild.get())
1405 : nullptr;
1406 }
1407
colorFilter() const1408 SkColorFilter* SkRuntimeEffect::ChildPtr::colorFilter() const {
1409 return (fChild && fChild->getFlattenableType() == SkFlattenable::kSkColorFilter_Type)
1410 ? static_cast<SkColorFilter*>(fChild.get())
1411 : nullptr;
1412 }
1413
blender() const1414 SkBlender* SkRuntimeEffect::ChildPtr::blender() const {
1415 return (fChild && fChild->getFlattenableType() == SkFlattenable::kSkBlender_Type)
1416 ? static_cast<SkBlender*>(fChild.get())
1417 : nullptr;
1418 }
1419
1420 ///////////////////////////////////////////////////////////////////////////////////////////////////
1421
RegisterFlattenables()1422 void SkRuntimeEffect::RegisterFlattenables() {
1423 SK_REGISTER_FLATTENABLE(SkRuntimeColorFilter);
1424 SK_REGISTER_FLATTENABLE(SkRTShader);
1425 SK_REGISTER_FLATTENABLE(SkRuntimeBlender);
1426 }
1427
SkRuntimeShaderBuilder(sk_sp<SkRuntimeEffect> effect)1428 SkRuntimeShaderBuilder::SkRuntimeShaderBuilder(sk_sp<SkRuntimeEffect> effect)
1429 : INHERITED(std::move(effect)) {}
1430
1431 SkRuntimeShaderBuilder::~SkRuntimeShaderBuilder() = default;
1432
makeImage(GrRecordingContext * recordingContext,const SkMatrix * localMatrix,SkImageInfo resultInfo,bool mipmapped)1433 sk_sp<SkImage> SkRuntimeShaderBuilder::makeImage(GrRecordingContext* recordingContext,
1434 const SkMatrix* localMatrix,
1435 SkImageInfo resultInfo,
1436 bool mipmapped) {
1437 return this->effect()->makeImage(recordingContext,
1438 this->uniforms(),
1439 SkMakeSpan(this->children(), this->numChildren()),
1440 localMatrix,
1441 resultInfo,
1442 mipmapped);
1443 }
1444
makeShader(const SkMatrix * localMatrix,bool isOpaque)1445 sk_sp<SkShader> SkRuntimeShaderBuilder::makeShader(const SkMatrix* localMatrix, bool isOpaque) {
1446 return this->effect()->makeShader(this->uniforms(),
1447 SkMakeSpan(this->children(), this->numChildren()),
1448 localMatrix,
1449 isOpaque);
1450 }
1451
SkRuntimeBlendBuilder(sk_sp<SkRuntimeEffect> effect)1452 SkRuntimeBlendBuilder::SkRuntimeBlendBuilder(sk_sp<SkRuntimeEffect> effect)
1453 : INHERITED(std::move(effect)) {}
1454
1455 SkRuntimeBlendBuilder::~SkRuntimeBlendBuilder() = default;
1456
makeBlender()1457 sk_sp<SkBlender> SkRuntimeBlendBuilder::makeBlender() {
1458 return this->effect()->makeBlender(this->uniforms(),
1459 SkMakeSpan(this->children(), this->numChildren()));
1460 }
1461
1462 #endif // SK_ENABLE_SKSL
1463