Flutter Engine
The Flutter Engine
SkRuntimeEffect.cpp
Go to the documentation of this file.
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
9
15#include "include/core/SkData.h"
25#include "src/core/SkChecksum.h"
29#include "src/core/SkLRUCache.h"
62
63#include <algorithm>
64
65using namespace skia_private;
66
67class SkColorSpace;
68struct SkIPoint;
69
70constexpr bool kRPEnableLiveTrace = false;
71
73
74static bool init_uniform_type(const SkSL::Context& ctx,
75 const SkSL::Type* type,
78 if (type->matches(*ctx.fTypes.fFloat)) { v->type = Type::kFloat; return true; }
79 if (type->matches(*ctx.fTypes.fHalf)) { v->type = Type::kFloat; return true; }
80 if (type->matches(*ctx.fTypes.fFloat2)) { v->type = Type::kFloat2; return true; }
81 if (type->matches(*ctx.fTypes.fHalf2)) { v->type = Type::kFloat2; return true; }
82 if (type->matches(*ctx.fTypes.fFloat3)) { v->type = Type::kFloat3; return true; }
83 if (type->matches(*ctx.fTypes.fHalf3)) { v->type = Type::kFloat3; return true; }
84 if (type->matches(*ctx.fTypes.fFloat4)) { v->type = Type::kFloat4; return true; }
85 if (type->matches(*ctx.fTypes.fHalf4)) { v->type = Type::kFloat4; return true; }
86 if (type->matches(*ctx.fTypes.fFloat2x2)) { v->type = Type::kFloat2x2; return true; }
87 if (type->matches(*ctx.fTypes.fHalf2x2)) { v->type = Type::kFloat2x2; return true; }
88 if (type->matches(*ctx.fTypes.fFloat3x3)) { v->type = Type::kFloat3x3; return true; }
89 if (type->matches(*ctx.fTypes.fHalf3x3)) { v->type = Type::kFloat3x3; return true; }
90 if (type->matches(*ctx.fTypes.fFloat4x4)) { v->type = Type::kFloat4x4; return true; }
91 if (type->matches(*ctx.fTypes.fHalf4x4)) { v->type = Type::kFloat4x4; return true; }
92
93 if (type->matches(*ctx.fTypes.fInt)) { v->type = Type::kInt; return true; }
94 if (type->matches(*ctx.fTypes.fInt2)) { v->type = Type::kInt2; return true; }
95 if (type->matches(*ctx.fTypes.fInt3)) { v->type = Type::kInt3; return true; }
96 if (type->matches(*ctx.fTypes.fInt4)) { v->type = Type::kInt4; return true; }
97
98 return false;
99}
100
102 const SkSL::Context& context,
103 size_t* offset) {
106 Uniform uni;
107 uni.name = var.name();
108 uni.flags = 0;
109 uni.count = 1;
110
111 const SkSL::Type* type = &var.type();
112 if (type->isArray()) {
114 uni.count = type->columns();
115 type = &type->componentType();
116 }
117
118 if (type->hasPrecision() && !type->highPrecision()) {
120 }
121
122 SkAssertResult(init_uniform_type(context, type, &uni));
125 }
126
127 uni.offset = *offset;
128 *offset += uni.sizeInBytes();
130 return uni;
131}
132
134 switch (type.typeKind()) {
138 default: SkUNREACHABLE;
139 }
140}
141
143 switch (type) {
144 case ChildType::kBlender: return "blender";
145 case ChildType::kColorFilter: return "color filter";
146 case ChildType::kShader: return "shader";
147 default: SkUNREACHABLE;
148 }
149}
150
153 c.name = var.name();
154 c.type = child_type(var.type());
155 c.index = index;
156 return c;
157}
158
161 sk_sp<const SkData> originalData,
162 const SkColorSpace* dstCS) {
163 if (!dstCS) {
164 // There's no destination color-space; we can early-out immediately.
165 return originalData;
166 }
168 dstCS, kUnpremul_SkAlphaType);
169 return TransformUniforms(uniforms, std::move(originalData), steps);
170}
171
174 sk_sp<const SkData> originalData,
175 const SkColorSpaceXformSteps& steps) {
178
179 sk_sp<SkData> data = nullptr;
180 auto writableData = [&]() {
181 if (!data) {
182 data = SkData::MakeWithCopy(originalData->data(), originalData->size());
183 }
184 return data->writable_data();
185 };
186
187 for (const auto& u : uniforms) {
188 if (u.flags & Flags::kColor_Flag) {
189 SkASSERT(u.type == Type::kFloat3 || u.type == Type::kFloat4);
190 if (steps.flags.mask()) {
191 float* color = SkTAddOffset<float>(writableData(), u.offset);
192 if (u.type == Type::kFloat4) {
193 // RGBA, easy case
194 for (int i = 0; i < u.count; ++i) {
195 steps.apply(color);
196 color += 4;
197 }
198 } else {
199 // RGB, need to pad out to include alpha. Technically, this isn't necessary,
200 // because steps shouldn't include unpremul or premul, and thus shouldn't
201 // read or write the fourth element. But let's be safe.
202 float rgba[4];
203 for (int i = 0; i < u.count; ++i) {
204 memcpy(rgba, color, 3 * sizeof(float));
205 rgba[3] = 1.0f;
206 steps.apply(rgba);
207 memcpy(color, rgba, 3 * sizeof(float));
208 color += 3;
209 }
210 }
211 }
212 }
213 }
214 return data ? data : originalData;
215}
216
217const SkSL::RP::Program* SkRuntimeEffect::getRPProgram(SkSL::DebugTracePriv* debugTrace) const {
218 // Lazily compile the program the first time `getRPProgram` is called.
219 // By using an SkOnce, we avoid thread hazards and behave in a conceptually const way, but we
220 // can avoid the cost of invoking the RP code generator until it's actually needed.
221 fCompileRPProgramOnce([&] {
222 // We generally do not run the inliner when an SkRuntimeEffect program is initially created,
223 // because the final compile to native shader code will do this. However, in SkRP, there's
224 // no additional compilation occurring, so we need to manually inline here if we want the
225 // performance boost of inlining.
226 if (!(fFlags & kDisableOptimization_Flag)) {
228 fBaseProgram->fConfig->fSettings.fInlineThreshold = SkSL::kDefaultInlineThreshold;
229 compiler.runInliner(*fBaseProgram);
230 }
231
232 SkSL::DebugTracePriv tempDebugTrace;
233 if (debugTrace) {
234 const_cast<SkRuntimeEffect*>(this)->fRPProgram = MakeRasterPipelineProgram(
235 *fBaseProgram, fMain, debugTrace, /*writeTraceOps=*/true);
236 } else if (kRPEnableLiveTrace) {
237 debugTrace = &tempDebugTrace;
238 const_cast<SkRuntimeEffect*>(this)->fRPProgram = MakeRasterPipelineProgram(
239 *fBaseProgram, fMain, debugTrace, /*writeTraceOps=*/false);
240 } else {
241 const_cast<SkRuntimeEffect*>(this)->fRPProgram = MakeRasterPipelineProgram(
242 *fBaseProgram, fMain, /*debugTrace=*/nullptr, /*writeTraceOps=*/false);
243 }
244
245 if (kRPEnableLiveTrace) {
246 if (fRPProgram) {
247 SkDebugf("-----\n\n");
249 fRPProgram->dump(&stream, /*writeInstructionCount=*/true);
250 SkDebugf("\n-----\n\n");
251 } else {
252 SkDebugf("----- RP unsupported -----\n\n");
253 }
254 }
255 });
256
257 return fRPProgram.get();
258}
259
262 sk_sp<const SkData> originalData,
263 bool alwaysCopyIntoAlloc,
264 const SkColorSpace* destColorSpace,
265 SkArenaAlloc* alloc) {
266 // Transform the uniforms into the destination colorspace.
268 originalData,
269 destColorSpace);
270 if (alwaysCopyIntoAlloc || originalData != transformedData) {
271 // The transformed uniform data's lifetime is not long enough to reuse; instead, we copy the
272 // uniform data directly into the alloc.
273 int numBytes = transformedData->size();
274 int numFloats = numBytes / sizeof(float);
275 float* uniformsInAlloc = alloc->makeArrayDefault<float>(numFloats);
276 memcpy(uniformsInAlloc, transformedData->data(), numBytes);
277 return SkSpan{uniformsInAlloc, numFloats};
278 }
279 // It's safe to return a pointer into existing data.
280 return SkSpan{static_cast<const float*>(originalData->data()),
281 originalData->size() / sizeof(float)};
282}
283
285 if (SkShader* shader = fChildren[index].shader()) {
286 if (fSampleUsages[index].isPassThrough()) {
287 // Given a passthrough sample, the total-matrix is still as valid as before.
288 return as_SB(shader)->appendStages(fStage, fMatrix);
289 }
290 // For a non-passthrough sample, we need to explicitly mark the total-matrix as invalid.
291 SkShaders::MatrixRec nonPassthroughMatrix = fMatrix;
292 nonPassthroughMatrix.markTotalMatrixInvalid();
293 return as_SB(shader)->appendStages(fStage, nonPassthroughMatrix);
294 }
295 // Return transparent black when a null shader is evaluated.
296 fStage.fPipeline->appendConstantColor(fStage.fAlloc, SkColors::kTransparent);
297 return true;
298}
300 if (SkColorFilter* colorFilter = fChildren[index].colorFilter()) {
301 return as_CFB(colorFilter)->appendStages(fStage, /*shaderIsOpaque=*/false);
302 }
303 // Return the original color as-is when a null child color filter is evaluated.
304 return true;
305}
307 if (SkBlender* blender = fChildren[index].blender()) {
308 return as_BB(blender)->appendStages(fStage);
309 }
310 // Return a source-over blend when a null blender is evaluated.
311 fStage.fPipeline->append(SkRasterPipelineOp::srcover);
312 return true;
313}
314
315// TODO: If an effect calls these intrinsics more than once, we could cache and re-use the steps
316// object(s), rather than re-creating them in the arena repeatedly.
318 if (fStage.fDstCS) {
321 if (xform.flags.mask()) {
322 // We have a non-identity colorspace transform; apply it.
323 this->applyColorSpaceXform(xform, color);
324 }
325 }
326}
327
329 if (fStage.fDstCS) {
332 if (xform.flags.mask()) {
333 // We have a non-identity colorspace transform; apply it.
334 this->applyColorSpaceXform(xform, color);
335 }
336 }
337}
338
339void RuntimeEffectRPCallbacks::applyColorSpaceXform(const SkColorSpaceXformSteps& tempXform,
340 const void* color) {
341 // Copy the transform steps into our alloc.
342 SkColorSpaceXformSteps* xform = fStage.fAlloc->make<SkColorSpaceXformSteps>(tempXform);
343
344 // Put the color into src.rgba (and temporarily stash the execution mask there instead).
345 fStage.fPipeline->append(SkRasterPipelineOp::exchange_src, color);
346 // Add the color space transform to our raster pipeline.
347 xform->apply(fStage.fPipeline);
348 // Restore the execution mask, and move the color back into program data.
349 fStage.fPipeline->append(SkRasterPipelineOp::exchange_src, color);
350}
351
353 SkASSERT(caps && program);
354 SkASSERT(program->fConfig->enforcesSkSLVersion());
355 return program->fConfig->fRequiredSkSLVersion <= caps->skslVersion();
356}
357
359 SkASSERT(effect);
360 return CanDraw(caps, effect->fBaseProgram.get());
361}
362
363//////////////////////////////////////////////////////////////////////////////
364
366 if (!f) { return true; }
367 switch (f->getFlattenableType()) {
371 return true;
372 default:
373 return false;
374 }
375}
376
379}
380
381static bool verify_child_effects(const std::vector<SkRuntimeEffect::Child>& reflected,
383 // Verify that the number of passed-in child-effect pointers matches the SkSL code.
384 if (reflected.size() != effectPtrs.size()) {
385 return false;
386 }
387
388 // Verify that each child object's type matches its declared type in the SkSL.
389 for (size_t i = 0; i < effectPtrs.size(); ++i) {
390 std::optional<ChildType> effectType = effectPtrs[i].type();
391 if (effectType && effectType != reflected[i].type) {
392 return false;
393 }
394 }
395 return true;
396}
397
398/**
399 * If `effect` is specified, then the number and type of child objects are validated against the
400 * children() of `effect`. If it's nullptr, this is skipped, allowing deserialization of children,
401 * even when the effect could not be constructed (ie, due to malformed SkSL).
402 */
404 const SkRuntimeEffect* effect,
406 size_t childCount = buffer.read32();
407 if (effect && !buffer.validate(childCount == effect->children().size())) {
408 return false;
409 }
410
411 children->clear();
412 children->reserve_exact(childCount);
413
414 for (size_t i = 0; i < childCount; i++) {
415 sk_sp<SkFlattenable> obj(buffer.readRawFlattenable());
417 buffer.validate(false);
418 return false;
419 }
420 children->push_back(std::move(obj));
421 }
422
423 // If we are validating against an effect, make sure any (non-null) children are the right type
424 if (effect) {
425 auto childInfo = effect->children();
426 SkASSERT(childInfo.size() == SkToSizeT(children->size()));
427 for (size_t i = 0; i < childCount; i++) {
428 std::optional<ChildType> ct = (*children)[i].type();
429 if (ct.has_value() && (*ct) != childInfo[i].type) {
430 buffer.validate(false);
431 }
432 }
433 }
434
435 return buffer.isValid();
436}
437
440 buffer.write32(children.size());
441 for (const auto& child : children) {
442 buffer.writeFlattenable(child.flattenable());
443 }
444}
445
446SkSL::ProgramSettings SkRuntimeEffect::MakeSettings(const Options& options) {
448 settings.fInlineThreshold = 0;
449 settings.fForceNoInline = options.forceUnoptimized;
450 settings.fOptimize = !options.forceUnoptimized;
451 settings.fMaxVersionAllowed = options.maxVersionAllowed;
452
453 // SkSL created by the GPU backend is typically parsed, converted to a backend format,
454 // and the IR is immediately discarded. In that situation, it makes sense to use node
455 // pools to accelerate the IR allocations. Here, SkRuntimeEffect instances are often
456 // long-lived (especially those created internally for runtime FPs). In this situation,
457 // we're willing to pay for a slightly longer compile so that we don't waste huge
458 // amounts of memory.
459 settings.fUseMemoryPool = false;
460 return settings;
461}
462
463// TODO: Many errors aren't caught until we process the generated Program here. Catching those
464// in the IR generator would provide better errors messages (with locations).
465#define RETURN_FAILURE(...) return Result{nullptr, SkStringPrintf(__VA_ARGS__)}
466
467SkRuntimeEffect::Result SkRuntimeEffect::MakeFromSource(SkString sksl,
468 const Options& options,
469 SkSL::ProgramKind kind) {
471 SkSL::ProgramSettings settings = MakeSettings(options);
472 std::unique_ptr<SkSL::Program> program =
473 compiler.convertProgram(kind, std::string(sksl.c_str(), sksl.size()), settings);
474
475 if (!program) {
476 RETURN_FAILURE("%s", compiler.errorText().c_str());
477 }
478
479 return MakeInternal(std::move(program), options, kind);
480}
481
482SkRuntimeEffect::Result SkRuntimeEffect::MakeInternal(std::unique_ptr<SkSL::Program> program,
483 const Options& options,
484 SkSL::ProgramKind kind) {
486
487 uint32_t flags = 0;
488 switch (kind) {
491 // TODO(skia:11209): Figure out a way to run ES3+ color filters on the CPU. This doesn't
492 // need to be fast - it could just be direct IR evaluation. But without it, there's no
493 // way for us to fully implement the SkColorFilter API (eg, `filterColor4f`)
495 program.get())) {
496 RETURN_FAILURE("SkSL color filters must target #version 100");
497 }
498 flags |= kAllowColorFilter_Flag;
499 break;
502 flags |= kAllowShader_Flag;
503 break;
506 flags |= kAllowBlender_Flag;
507 break;
508 default:
510 }
511
512 if (options.forceUnoptimized) {
513 flags |= kDisableOptimization_Flag;
514 }
515
516 // Find 'main', then locate the sample coords parameter. (It might not be present.)
517 const SkSL::FunctionDeclaration* main = program->getFunction("main");
518 if (!main) {
519 RETURN_FAILURE("missing 'main' function");
520 }
521 const SkSL::Variable* coordsParam = main->getMainCoordsParameter();
522
523 const SkSL::ProgramUsage::VariableCounts sampleCoordsUsage =
524 coordsParam ? program->usage()->get(*coordsParam)
526
527 if (sampleCoordsUsage.fRead || sampleCoordsUsage.fWrite) {
528 flags |= kUsesSampleCoords_Flag;
529 }
530
531 // Color filters and blends are not allowed to depend on position (local or device) in any way.
532 // The signature of main, and the declarations in sksl_rt_colorfilter/sksl_rt_blend should
533 // guarantee this.
534 if (flags & (kAllowColorFilter_Flag | kAllowBlender_Flag)) {
535 SkASSERT(!(flags & kUsesSampleCoords_Flag));
537 }
538
540 flags |= kSamplesOutsideMain_Flag;
541 }
542
543 // Look for color filters that preserve the input alpha. This analysis is very conservative, and
544 // only returns true when the input alpha is returned as-is from main() with no intervening
545 // copies or arithmetic.
546 if (flags & kAllowColorFilter_Flag) {
547 if (SkSL::Analysis::ReturnsInputAlpha(*main->definition(), *program->usage())) {
548 flags |= kAlphaUnchanged_Flag;
549 }
550 }
551
552 // Determine if this effect uses of the color transform intrinsics. Effects need to know this
553 // so they can allocate color transform objects, etc.
555 flags |= kUsesColorTransform_Flag;
556 }
557
558 // Shaders are the only thing that cares about this, but it's inexpensive (and safe) to call.
559 if (SkSL::Analysis::ReturnsOpaqueColor(*main->definition())) {
560 flags |= kAlwaysOpaque_Flag;
561 }
562
563 // Go through program elements, pulling out information that we need
564 size_t offset = 0;
565 std::vector<Uniform> uniforms;
566 std::vector<Child> children;
567 std::vector<SkSL::SampleUsage> sampleUsages;
568 int elidedSampleCoords = 0;
569 const SkSL::Context& ctx(compiler.context());
570
571 for (const SkSL::ProgramElement* elem : program->elements()) {
572 // Variables (uniform, etc.)
573 if (elem->is<SkSL::GlobalVarDeclaration>()) {
575 const SkSL::VarDeclaration& varDecl = global.declaration()->as<SkSL::VarDeclaration>();
576 const SkSL::Variable& var = *varDecl.var();
577
578 // Child effects that can be sampled ('shader', 'colorFilter', 'blender')
579 if (var.type().isEffectChild()) {
580 children.push_back(SkRuntimeEffectPriv::VarAsChild(var, children.size()));
582 *program, var, sampleCoordsUsage.fWrite != 0, &elidedSampleCoords);
583 // If the child is never sampled, we pretend that it's actually in PassThrough mode.
584 // Otherwise, the GP code for collecting transforms and emitting transform code gets
585 // very confused, leading to asserts and bad (backend) shaders. There's an implicit
586 // assumption that every FP is used by its parent. (skbug.com/12429)
587 sampleUsages.push_back(usage.isSampled() ? usage
589 }
590 // 'uniform' variables
591 else if (var.modifierFlags().isUniform()) {
593 }
594 }
595 }
596
597 // If the sample coords are never written to, then we will have converted sample calls that use
598 // them unmodified into "passthrough" sampling. If all references to the sample coords were of
599 // that form, then we don't actually "use" sample coords. We unset the flag to prevent creating
600 // an extra (unused) varying holding the coords.
601 if (elidedSampleCoords == sampleCoordsUsage.fRead && sampleCoordsUsage.fWrite == 0) {
602 flags &= ~kUsesSampleCoords_Flag;
603 }
604
605#undef RETURN_FAILURE
606
607 sk_sp<SkRuntimeEffect> effect(new SkRuntimeEffect(std::move(program),
608 options,
609 *main->definition(),
610 std::move(uniforms),
611 std::move(children),
612 std::move(sampleUsages),
613 flags));
614 return Result{std::move(effect), SkString()};
615}
616
617sk_sp<SkRuntimeEffect> SkRuntimeEffect::makeUnoptimizedClone() {
618 // Compile with maximally-permissive options; any restrictions we need to enforce were already
619 // handled when the original SkRuntimeEffect was made. We don't keep around the Options struct
620 // from when it was initially made so we don't know what was originally requested.
621 Options options;
622 options.forceUnoptimized = true;
623 options.maxVersionAllowed = SkSL::Version::k300;
624 options.allowPrivateAccess = true;
625
626 // We do know the original ProgramKind, so we don't need to re-derive it.
627 SkSL::ProgramKind kind = fBaseProgram->fConfig->fKind;
628
629 // Attempt to recompile the program's source with optimizations off. This ensures that the
630 // Debugger shows results on every line, even for things that could be optimized away (static
631 // branches, unused variables, etc). If recompilation fails, we fall back to the original code.
633 SkSL::ProgramSettings settings = MakeSettings(options);
634 std::unique_ptr<SkSL::Program> program =
635 compiler.convertProgram(kind, *fBaseProgram->fSource, settings);
636
637 if (!program) {
638 // Turning off compiler optimizations can theoretically expose a program error that
639 // had been optimized away (e.g. "all control paths return a value" might be found on a path
640 // that is completely eliminated in the optimized program).
641 // If this happens, the debugger will just have to show the optimized code.
642 return sk_ref_sp(this);
643 }
644
645 SkRuntimeEffect::Result result = MakeInternal(std::move(program), options, kind);
646 if (!result.effect) {
647 // Nothing in MakeInternal should change as a result of optimizations being toggled.
648 SkDEBUGFAILF("makeUnoptimizedClone: MakeInternal failed\n%s",
649 result.errorText.c_str());
650 return sk_ref_sp(this);
651 }
652
653 return result.effect;
654}
655
659 auto result = MakeFromSource(std::move(sksl), options, programKind);
660 SkASSERT(!result.effect || result.effect->allowColorFilter());
661 return result;
662}
663
667 auto result = MakeFromSource(std::move(sksl), options, programKind);
668 SkASSERT(!result.effect || result.effect->allowShader());
669 return result;
670}
671
675 auto result = MakeFromSource(std::move(sksl), options, programKind);
676 SkASSERT(!result.effect || result.effect->allowBlender());
677 return result;
678}
679
682 SkString sksl) {
683 static SkNoDestructor<SkMutex> mutex;
685
686 uint64_t key = SkChecksum::Hash64(sksl.c_str(), sksl.size());
687 {
688 SkAutoMutexExclusive _(*mutex);
689 if (sk_sp<SkRuntimeEffect>* found = cache->find(key)) {
690 return *found;
691 }
692 }
693
696
697 auto [effect, err] = make(std::move(sksl), options);
698 if (!effect) {
699 SkDEBUGFAILF("%s", err.c_str());
700 return nullptr;
701 }
702 SkASSERT(err.isEmpty());
703
704 {
705 SkAutoMutexExclusive _(*mutex);
706 cache->insert_or_update(key, effect);
707 }
708 return effect;
709}
710
712 switch (type) {
714 case SkRuntimeEffect::Uniform::Type::kFloat2: return sizeof(float) * 2;
715 case SkRuntimeEffect::Uniform::Type::kFloat3: return sizeof(float) * 3;
716 case SkRuntimeEffect::Uniform::Type::kFloat4: return sizeof(float) * 4;
717
718 case SkRuntimeEffect::Uniform::Type::kFloat2x2: return sizeof(float) * 4;
719 case SkRuntimeEffect::Uniform::Type::kFloat3x3: return sizeof(float) * 9;
720 case SkRuntimeEffect::Uniform::Type::kFloat4x4: return sizeof(float) * 16;
721
722 case SkRuntimeEffect::Uniform::Type::kInt: return sizeof(int);
723 case SkRuntimeEffect::Uniform::Type::kInt2: return sizeof(int) * 2;
724 case SkRuntimeEffect::Uniform::Type::kInt3: return sizeof(int) * 3;
725 case SkRuntimeEffect::Uniform::Type::kInt4: return sizeof(int) * 4;
726 default: SkUNREACHABLE;
727 }
728}
729
731 static_assert(sizeof(int) == sizeof(float));
732 return uniform_element_size(this->type) * this->count;
733}
734
735SkRuntimeEffect::SkRuntimeEffect(std::unique_ptr<SkSL::Program> baseProgram,
736 const Options& options,
738 std::vector<Uniform>&& uniforms,
739 std::vector<Child>&& children,
740 std::vector<SkSL::SampleUsage>&& sampleUsages,
741 uint32_t flags)
742 : fHash(SkChecksum::Hash32(baseProgram->fSource->c_str(), baseProgram->fSource->size()))
743 , fStableKey(options.fStableKey)
744 , fBaseProgram(std::move(baseProgram))
745 , fMain(main)
746 , fUniforms(std::move(uniforms))
747 , fChildren(std::move(children))
748 , fSampleUsages(std::move(sampleUsages))
749 , fFlags(flags) {
750 SkASSERT(fBaseProgram);
751 SkASSERT(fChildren.size() == fSampleUsages.size());
752
753 // Everything from SkRuntimeEffect::Options which could influence the compiled result needs to
754 // be accounted for in `fHash`. If you've added a new field to Options and caused the static-
755 // assert below to trigger, please incorporate your field into `fHash` and update KnownOptions
756 // to match the layout of Options.
757 struct KnownOptions {
758 bool forceUnoptimized, allowPrivateAccess;
759 uint32_t fStableKey;
760 SkSL::Version maxVersionAllowed;
761 };
762 static_assert(sizeof(Options) == sizeof(KnownOptions));
763 fHash = SkChecksum::Hash32(&options.forceUnoptimized,
764 sizeof(options.forceUnoptimized), fHash);
765 fHash = SkChecksum::Hash32(&options.allowPrivateAccess,
766 sizeof(options.allowPrivateAccess), fHash);
767 fHash = SkChecksum::Hash32(&options.fStableKey,
768 sizeof(options.fStableKey), fHash);
769 fHash = SkChecksum::Hash32(&options.maxVersionAllowed,
770 sizeof(options.maxVersionAllowed), fHash);
771}
772
774
775const std::string& SkRuntimeEffect::source() const {
776 return *fBaseProgram->fSource;
777}
778
780 return fUniforms.empty() ? 0
781 : SkAlign4(fUniforms.back().offset + fUniforms.back().sizeInBytes());
782}
783
785 auto iter = std::find_if(fUniforms.begin(), fUniforms.end(), [name](const Uniform& u) {
786 return u.name == name;
787 });
788 return iter == fUniforms.end() ? nullptr : &(*iter);
789}
790
792 auto iter = std::find_if(fChildren.begin(), fChildren.end(), [name](const Child& c) {
793 return c.name == name;
794 });
795 return iter == fChildren.end() ? nullptr : &(*iter);
796}
797
798///////////////////////////////////////////////////////////////////////////////////////////////////
799
801 const SkRuntimeEffect* effect,
802 UniformsCallback uniformsCallback,
804 const SkMatrix* localMatrix) {
805 if (!effect->allowShader()) {
806 return nullptr;
807 }
808 if (!verify_child_effects(effect->fChildren, children)) {
809 return nullptr;
810 }
811 if (!uniformsCallback) {
812 return nullptr;
813 }
814 return SkLocalMatrixShader::MakeWrapped<SkRuntimeShader>(localMatrix,
815 sk_ref_sp(effect),
816 /*debugTrace=*/nullptr,
817 std::move(uniformsCallback),
818 children);
819}
820
822 sk_sp<SkShader> childShaders[],
823 size_t childCount,
824 const SkMatrix* localMatrix) const {
825 STArray<4, ChildPtr> children(childCount);
826 for (size_t i = 0; i < childCount; ++i) {
827 children.emplace_back(childShaders[i]);
828 }
829 return this->makeShader(std::move(uniforms), SkSpan(children), localMatrix);
830}
831
834 const SkMatrix* localMatrix) const {
835 if (!this->allowShader()) {
836 return nullptr;
837 }
838 if (!verify_child_effects(fChildren, children)) {
839 return nullptr;
840 }
841 if (!uniforms) {
843 }
844 if (uniforms->size() != this->uniformSize()) {
845 return nullptr;
846 }
847 return SkLocalMatrixShader::MakeWrapped<SkRuntimeShader>(localMatrix,
848 sk_ref_sp(this),
849 /*debugTrace=*/nullptr,
850 std::move(uniforms),
851 children);
852}
853
855 sk_sp<SkColorFilter> childColorFilters[],
856 size_t childCount) const {
857 STArray<4, ChildPtr> children(childCount);
858 for (size_t i = 0; i < childCount; ++i) {
859 children.emplace_back(childColorFilters[i]);
860 }
861 return this->makeColorFilter(std::move(uniforms), SkSpan(children));
862}
863
866 if (!this->allowColorFilter()) {
867 return nullptr;
868 }
869 if (!verify_child_effects(fChildren, children)) {
870 return nullptr;
871 }
872 if (!uniforms) {
874 }
875 if (uniforms->size() != this->uniformSize()) {
876 return nullptr;
877 }
878 return sk_make_sp<SkRuntimeColorFilter>(sk_ref_sp(this), std::move(uniforms), children);
879}
880
882 return this->makeColorFilter(std::move(uniforms), /*children=*/{});
883}
884
887 if (!this->allowBlender()) {
888 return nullptr;
889 }
890 if (!verify_child_effects(fChildren, children)) {
891 return nullptr;
892 }
893 if (!uniforms) {
895 }
896 if (uniforms->size() != this->uniformSize()) {
897 return nullptr;
898 }
899 return sk_make_sp<SkRuntimeBlender>(sk_ref_sp(this), std::move(uniforms), children);
900}
901
902///////////////////////////////////////////////////////////////////////////////////////////////////
903
905 const SkIPoint& traceCoord) {
906 SkRuntimeEffect* effect = as_SB(shader)->asRuntimeEffect();
907 if (!effect) {
908 return TracedShader{nullptr, nullptr};
909 }
910 // An SkShader with an attached SkRuntimeEffect must be an SkRuntimeShader.
911 SkRuntimeShader* rtShader = static_cast<SkRuntimeShader*>(shader.get());
912 return rtShader->makeTracedClone(traceCoord);
913}
914
915///////////////////////////////////////////////////////////////////////////////////////////////////
916
917std::optional<ChildType> SkRuntimeEffect::ChildPtr::type() const {
918 if (fChild) {
919 switch (fChild->getFlattenableType()) {
921 return ChildType::kShader;
925 return ChildType::kBlender;
926 default:
927 break;
928 }
929 }
930 return std::nullopt;
931}
932
934 return (fChild && fChild->getFlattenableType() == SkFlattenable::kSkShader_Type)
935 ? static_cast<SkShader*>(fChild.get())
936 : nullptr;
937}
938
940 return (fChild && fChild->getFlattenableType() == SkFlattenable::kSkColorFilter_Type)
941 ? static_cast<SkColorFilter*>(fChild.get())
942 : nullptr;
943}
944
946 return (fChild && fChild->getFlattenableType() == SkFlattenable::kSkBlender_Type)
947 ? static_cast<SkBlender*>(fChild.get())
948 : nullptr;
949}
950
951///////////////////////////////////////////////////////////////////////////////////////////////////
952
957
958 // Previous name
959 SkFlattenable::Register("SkRTShader", SkRuntimeShader::CreateProc);
960}
961
963 : SkRuntimeEffectBuilder(std::move(effect)) {}
964
966
968 return this->effect()->makeShader(this->uniforms(), this->children(), localMatrix);
969}
970
972 : SkRuntimeEffectBuilder(std::move(effect)) {}
973
975
977 return this->effect()->makeBlender(this->uniforms(), this->children());
978}
979
981 : SkRuntimeEffectBuilder(std::move(effect)) {}
982
984
986 return this->effect()->makeColorFilter(this->uniforms(), this->children());
987}
enum skgpu::ganesh::@8120::DegenerateTestData::@384 fStage
const char * options
SkMatrix fMatrix
Definition: FillRRectOp.cpp:74
SkAssertResult(font.textToGlyphs("Hello", 5, SkTextEncoding::kUTF8, glyphs, std::size(glyphs))==count)
int count
Definition: FontMgrTest.cpp:50
kUnpremul_SkAlphaType
static const uint32_t rgba[kNumPixels]
uint16_t fFlags
Definition: ShapeLayer.cpp:106
static constexpr bool SkIsAlign4(T x)
Definition: SkAlign.h:20
static constexpr T SkAlign4(T x)
Definition: SkAlign.h:16
#define SkUNREACHABLE
Definition: SkAssert.h:135
#define SkDEBUGFAILF(fmt,...)
Definition: SkAssert.h:119
#define SkASSERT(cond)
Definition: SkAssert.h:116
SkBlenderBase * as_BB(SkBlender *blend)
Definition: SkBlenderBase.h:69
static SkColorFilterBase * as_CFB(SkColorFilter *filter)
SkColorSpace * sk_srgb_linear_singleton()
SkColorSpace * sk_srgb_singleton()
void SK_SPI SkDebugf(const char format[],...) SK_PRINTF_LIKE(1
#define SK_REGISTER_FLATTENABLE(type)
SkMeshSpecification::Uniform Uniform
Definition: SkMesh.cpp:66
sk_sp< T > sk_ref_sp(T *obj)
Definition: SkRefCnt.h:381
static bool verify_child_effects(const std::vector< SkRuntimeEffect::Child > &reflected, SkSpan< const SkRuntimeEffect::ChildPtr > effectPtrs)
static size_t uniform_element_size(SkRuntimeEffect::Uniform::Type type)
static bool init_uniform_type(const SkSL::Context &ctx, const SkSL::Type *type, SkRuntimeEffect::Uniform *v)
static bool flattenable_is_valid_as_child(const SkFlattenable *f)
constexpr bool kRPEnableLiveTrace
static ChildType child_type(const SkSL::Type &type)
#define RETURN_FAILURE(...)
sk_sp< SkRuntimeEffect > SkMakeCachedRuntimeEffect(SkRuntimeEffect::Result(*make)(SkString sksl, const SkRuntimeEffect::Options &), SkString sksl)
SkShaderBase * as_SB(SkShader *shader)
Definition: SkShaderBase.h:412
SkSpan(Container &&) -> SkSpan< std::remove_pointer_t< decltype(std::data(std::declval< Container >()))> >
constexpr size_t SkToSizeT(S x)
Definition: SkTo.h:31
GLenum type
void toLinearSrgb(const void *color) override
bool appendColorFilter(int index) override
bool appendBlender(int index) override
void fromLinearSrgb(const void *color) override
bool appendShader(int index) override
T * makeArrayDefault(size_t count)
Definition: SkArenaAlloc.h:171
bool appendStages(const SkStageRec &rec) const
Definition: SkBlenderBase.h:49
SkSL::Version skslVersion() const
static sk_sp< const SkCapabilities > RasterBackend()
virtual bool appendStages(const SkStageRec &rec, bool shaderIsOpaque) const =0
const void * data() const
Definition: SkData.h:37
static sk_sp< SkData > MakeWithCopy(const void *data, size_t length)
Definition: SkData.cpp:111
static sk_sp< SkData > MakeEmpty()
Definition: SkData.cpp:94
size_t size() const
Definition: SkData.h:30
static void Register(const char name[], Factory)
sk_sp< SkBlender > makeBlender() const
SkRuntimeBlendBuilder(sk_sp< SkRuntimeEffect >)
SkRuntimeColorFilterBuilder(sk_sp< SkRuntimeEffect >)
sk_sp< SkColorFilter > makeColorFilter() const
sk_sp< const SkData > uniforms() const
const SkRuntimeEffect * effect() const
SkSpan< const SkRuntimeEffect::ChildPtr > children() const
static bool ReadChildEffects(SkReadBuffer &buffer, const SkRuntimeEffect *effect, skia_private::TArray< SkRuntimeEffect::ChildPtr > *children)
static sk_sp< SkShader > MakeDeferredShader(const SkRuntimeEffect *effect, UniformsCallback uniformsCallback, SkSpan< const SkRuntimeEffect::ChildPtr > children, const SkMatrix *localMatrix=nullptr)
static const char * ChildTypeToStr(SkRuntimeEffect::ChildType type)
std::function< sk_sp< const SkData >(const UniformsCallbackContext &)> UniformsCallback
static SkSpan< const float > UniformsAsSpan(SkSpan< const SkRuntimeEffect::Uniform > uniforms, sk_sp< const SkData > originalData, bool alwaysCopyIntoAlloc, const SkColorSpace *destColorSpace, SkArenaAlloc *alloc)
static void AllowPrivateAccess(SkRuntimeEffect::Options *options)
static SkRuntimeEffect::Uniform VarAsUniform(const SkSL::Variable &, const SkSL::Context &, size_t *offset)
static sk_sp< const SkData > TransformUniforms(SkSpan< const SkRuntimeEffect::Uniform > uniforms, sk_sp< const SkData > originalData, const SkColorSpaceXformSteps &)
static bool CanDraw(const SkCapabilities *, const SkSL::Program *)
static void WriteChildEffects(SkWriteBuffer &buffer, SkSpan< const SkRuntimeEffect::ChildPtr > children)
static SkRuntimeEffect::Child VarAsChild(const SkSL::Variable &var, int index)
SkBlender * blender() const
std::optional< ChildType > type() const
SkColorFilter * colorFilter() const
size_t uniformSize() const
const Child * findChild(std::string_view name) const
static void RegisterFlattenables()
SkSpan< const Child > children() const
sk_sp< SkShader > makeShader(sk_sp< const SkData > uniforms, sk_sp< SkShader > children[], size_t childCount, const SkMatrix *localMatrix=nullptr) const
bool allowColorFilter() const
~SkRuntimeEffect() override
static Result MakeForColorFilter(SkString sksl, const Options &)
bool allowBlender() const
sk_sp< SkBlender > makeBlender(sk_sp< const SkData > uniforms, SkSpan< const ChildPtr > children={}) const
SkSpan< const Uniform > uniforms() const
static Result MakeForBlender(SkString sksl, const Options &)
static Result MakeForShader(SkString sksl, const Options &)
static TracedShader MakeTraced(sk_sp< SkShader > shader, const SkIPoint &traceCoord)
bool allowShader() const
sk_sp< SkColorFilter > makeColorFilter(sk_sp< const SkData > uniforms) const
const Uniform * findUniform(std::string_view name) const
const std::string & source() const
sk_sp< SkShader > makeShader(const SkMatrix *localMatrix=nullptr) const
SkRuntimeShaderBuilder(sk_sp< SkRuntimeEffect >)
SkRuntimeEffect::TracedShader makeTracedClone(const SkIPoint &coord)
const std::unique_ptr< Type > fFloat2
const std::unique_ptr< Type > fHalf4
const std::unique_ptr< Type > fInt4
const std::unique_ptr< Type > fHalf2x2
const std::unique_ptr< Type > fInt2
const std::unique_ptr< Type > fInt
const std::unique_ptr< Type > fFloat2x2
const std::unique_ptr< Type > fFloat4x4
const std::unique_ptr< Type > fInt3
const std::unique_ptr< Type > fFloat4
const std::unique_ptr< Type > fHalf2
const std::unique_ptr< Type > fHalf3x3
const std::unique_ptr< Type > fFloat3x3
const std::unique_ptr< Type > fHalf3
const std::unique_ptr< Type > fFloat
const std::unique_ptr< Type > fFloat3
const std::unique_ptr< Type > fHalf
const std::unique_ptr< Type > fHalf4x4
const BuiltinTypes & fTypes
Definition: SkSLContext.h:30
std::unique_ptr< Statement > & declaration()
const T & as() const
Definition: SkSLIRNode.h:133
static SampleUsage PassThrough()
std::string_view name() const
Definition: SkSLSymbol.h:51
const Type & type() const
Definition: SkSLSymbol.h:42
ModifierFlags modifierFlags() const
Definition: SkSLVariable.h:89
virtual const Layout & layout() const
virtual bool appendStages(const SkStageRec &, const SkShaders::MatrixRec &) const =0
virtual SkRuntimeEffect * asRuntimeEffect() const
Definition: SkShaderBase.h:366
constexpr size_t size() const
Definition: SkSpan_impl.h:95
size_t size() const
Definition: SkString.h:131
const char * c_str() const
Definition: SkString.h:133
T * get() const
Definition: SkRefCnt.h:303
DlColor color
FlutterSemanticsFlag flags
GAsyncResult * result
uint64_t Hash64(const void *data, size_t bytes, uint64_t seed)
Definition: SkChecksum.cpp:117
uint32_t Hash32(const void *data, size_t bytes, uint32_t seed)
Definition: SkChecksum.cpp:113
constexpr SkColor4f kTransparent
Definition: SkColor.h:434
bool ReturnsOpaqueColor(const FunctionDefinition &function)
bool ReferencesFragCoords(const Program &program)
SampleUsage GetSampleUsage(const Program &program, const Variable &child, bool writesToSampleCoords=true, int *elidedSampleCoordCount=nullptr)
bool ReturnsInputAlpha(const FunctionDefinition &function, const ProgramUsage &usage)
bool CallsColorTransformIntrinsics(const Program &program)
bool CallsSampleOutsideMain(const Program &program)
static constexpr int kDefaultInlineThreshold
Definition: SkSLDefines.h:38
std::unique_ptr< RP::Program > MakeRasterPipelineProgram(const SkSL::Program &program, const FunctionDefinition &function, DebugTracePriv *debugTrace, bool writeTraceOps)
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace Enable an endless trace buffer The default is a ring buffer This is useful when very old events need to viewed For during application launch Memory usage will continue to grow indefinitely however Start app with an specific route defined on the framework flutter assets Path to the Flutter assets directory enable service port Allow the VM service to fallback to automatic port selection if binding to a specified port fails trace Trace early application lifecycle Automatically switches to an endless trace buffer trace skia Filters out all Skia trace event categories except those that are specified in this comma separated list dump skp on shader Automatically dump the skp that triggers new shader compilations This is useful for writing custom ShaderWarmUp to reduce jank By this is not enabled to reduce the overhead purge persistent cache
Definition: switches.h:191
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
string programKind
Definition: minify_sksl.py:40
Definition: main.py:1
compiler
Definition: malisc.py:17
const myers::Point & get(const myers::Segment &)
static void make(SkBitmap *bitmap, SkColorType colorType, SkAlphaType alphaType, sk_sp< SkColorSpace > colorSpace)
Definition: encode_srgb.cpp:35
Definition: ref_ptr.h:256
static void usage(char *argv0)
SeparatedVector2 offset
constexpr uint32_t mask() const
void apply(float rgba[4]) const
std::string_view name
LayoutFlags fFlags
Definition: SkSLLayout.h:112
std::unique_ptr< ProgramConfig > fConfig
Definition: SkSLProgram.h:153
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63