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/SkChecksum.h"
13 #include "include/private/SkMutex.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/GrSurfaceFillContext.h"
40 #include "src/gpu/effects/GrMatrixEffect.h"
41 #include "src/gpu/effects/GrSkSLFP.h"
42 #include "src/image/SkImage_Gpu.h"
43 #endif
44
45 #include <algorithm>
46
47 namespace SkSL {
48 class SharedCompiler {
49 public:
SharedCompiler()50 SharedCompiler() : fLock(compiler_mutex()) {
51 if (!gImpl) {
52 gImpl = new Impl();
53 }
54 }
55
operator ->() const56 SkSL::Compiler* operator->() const { return gImpl->fCompiler; }
57
58 private:
59 SkAutoMutexExclusive fLock;
60
compiler_mutex()61 static SkMutex& compiler_mutex() {
62 static SkMutex& mutex = *(new SkMutex);
63 return mutex;
64 }
65
66 struct Impl {
ImplSkSL::SharedCompiler::Impl67 Impl() {
68 // These caps are configured to apply *no* workarounds. This avoids changes that are
69 // unnecessary (GLSL intrinsic rewrites), or possibly incorrect (adding do-while loops).
70 // We may apply other "neutral" transformations to the user's SkSL, including inlining.
71 // Anything determined by the device caps is deferred to the GPU backend. The processor
72 // set produces the final program (including our re-emitted SkSL), and the backend's
73 // compiler resolves any necessary workarounds.
74 fCaps = ShaderCapsFactory::Standalone();
75 fCaps->fBuiltinFMASupport = true;
76 fCaps->fBuiltinDeterminantSupport = true;
77 // Don't inline if it would require a do loop, some devices don't support them.
78 fCaps->fCanUseDoLoops = false;
79
80 fCompiler = new SkSL::Compiler(fCaps.get());
81 }
82
83 SkSL::ShaderCapsPointer fCaps;
84 SkSL::Compiler* fCompiler;
85 };
86
87 static Impl* gImpl;
88 };
89
90 SharedCompiler::Impl* SharedCompiler::gImpl = nullptr;
91
92 } // namespace SkSL
93
init_uniform_type(const SkSL::Context & ctx,const SkSL::Type * type,SkRuntimeEffect::Uniform * v)94 static bool init_uniform_type(const SkSL::Context& ctx,
95 const SkSL::Type* type,
96 SkRuntimeEffect::Uniform* v) {
97 using Type = SkRuntimeEffect::Uniform::Type;
98 if (*type == *ctx.fTypes.fFloat) { v->type = Type::kFloat; return true; }
99 if (*type == *ctx.fTypes.fHalf) { v->type = Type::kFloat; return true; }
100 if (*type == *ctx.fTypes.fFloat2) { v->type = Type::kFloat2; return true; }
101 if (*type == *ctx.fTypes.fHalf2) { v->type = Type::kFloat2; return true; }
102 if (*type == *ctx.fTypes.fFloat3) { v->type = Type::kFloat3; return true; }
103 if (*type == *ctx.fTypes.fHalf3) { v->type = Type::kFloat3; return true; }
104 if (*type == *ctx.fTypes.fFloat4) { v->type = Type::kFloat4; return true; }
105 if (*type == *ctx.fTypes.fHalf4) { v->type = Type::kFloat4; return true; }
106 if (*type == *ctx.fTypes.fFloat2x2) { v->type = Type::kFloat2x2; return true; }
107 if (*type == *ctx.fTypes.fHalf2x2) { v->type = Type::kFloat2x2; return true; }
108 if (*type == *ctx.fTypes.fFloat3x3) { v->type = Type::kFloat3x3; return true; }
109 if (*type == *ctx.fTypes.fHalf3x3) { v->type = Type::kFloat3x3; return true; }
110 if (*type == *ctx.fTypes.fFloat4x4) { v->type = Type::kFloat4x4; return true; }
111 if (*type == *ctx.fTypes.fHalf4x4) { v->type = Type::kFloat4x4; return true; }
112
113 if (*type == *ctx.fTypes.fInt) { v->type = Type::kInt; return true; }
114 if (*type == *ctx.fTypes.fInt2) { v->type = Type::kInt2; return true; }
115 if (*type == *ctx.fTypes.fInt3) { v->type = Type::kInt3; return true; }
116 if (*type == *ctx.fTypes.fInt4) { v->type = Type::kInt4; return true; }
117
118 return false;
119 }
120
child_type(const SkSL::Type & type)121 static SkRuntimeEffect::Child::Type child_type(const SkSL::Type& type) {
122 switch (type.typeKind()) {
123 case SkSL::Type::TypeKind::kColorFilter: return SkRuntimeEffect::Child::Type::kColorFilter;
124 case SkSL::Type::TypeKind::kShader: return SkRuntimeEffect::Child::Type::kShader;
125 default: SkUNREACHABLE;
126 }
127 }
128
129 // TODO: Many errors aren't caught until we process the generated Program here. Catching those
130 // in the IR generator would provide better errors messages (with locations).
131 #define RETURN_FAILURE(...) return Result{nullptr, SkStringPrintf(__VA_ARGS__)}
132
Make(SkString sksl,const Options & options,SkSL::ProgramKind kind)133 SkRuntimeEffect::Result SkRuntimeEffect::Make(SkString sksl, const Options& options,
134 SkSL::ProgramKind kind) {
135 std::unique_ptr<SkSL::Program> program;
136 {
137 // We keep this SharedCompiler in a separate scope to make sure it's destroyed before
138 // calling the Make overload at the end, which creates its own (non-reentrant)
139 // SharedCompiler instance
140 SkSL::SharedCompiler compiler;
141 SkSL::Program::Settings settings;
142 settings.fInlineThreshold = 0;
143 settings.fForceNoInline = options.forceNoInline;
144 #if GR_TEST_UTILS
145 settings.fEnforceES2Restrictions = options.enforceES2Restrictions;
146 #endif
147 settings.fAllowNarrowingConversions = true;
148 program = compiler->convertProgram(kind, SkSL::String(sksl.c_str(), sksl.size()), settings);
149
150 if (!program) {
151 RETURN_FAILURE("%s", compiler->errorText().c_str());
152 }
153 }
154 return Make(std::move(sksl), std::move(program), options, kind);
155 }
156
Make(std::unique_ptr<SkSL::Program> program,SkSL::ProgramKind kind)157 SkRuntimeEffect::Result SkRuntimeEffect::Make(std::unique_ptr<SkSL::Program> program,
158 SkSL::ProgramKind kind) {
159 SkString source(program->description().c_str());
160 return Make(std::move(source), std::move(program), Options{}, kind);
161 }
162
Make(SkString sksl,std::unique_ptr<SkSL::Program> program,const Options & options,SkSL::ProgramKind kind)163 SkRuntimeEffect::Result SkRuntimeEffect::Make(SkString sksl,
164 std::unique_ptr<SkSL::Program> program,
165 const Options& options,
166 SkSL::ProgramKind kind) {
167 SkSL::SharedCompiler compiler;
168 SkSL::Program::Settings settings;
169 settings.fInlineThreshold = 0;
170 settings.fForceNoInline = options.forceNoInline;
171 settings.fAllowNarrowingConversions = true;
172
173 // Find 'main', then locate the sample coords parameter. (It might not be present.)
174 const SkSL::FunctionDefinition* main = SkSL::Program_GetFunction(*program, "main");
175 if (!main) {
176 RETURN_FAILURE("missing 'main' function");
177 }
178 const auto& mainParams = main->declaration().parameters();
179 auto iter = std::find_if(mainParams.begin(), mainParams.end(), [](const SkSL::Variable* p) {
180 return p->modifiers().fLayout.fBuiltin == SK_MAIN_COORDS_BUILTIN;
181 });
182 const SkSL::ProgramUsage::VariableCounts sampleCoordsUsage =
183 iter != mainParams.end() ? program->usage()->get(**iter)
184 : SkSL::ProgramUsage::VariableCounts{};
185
186 uint32_t flags = 0;
187 switch (kind) {
188 case SkSL::ProgramKind::kRuntimeColorFilter: flags |= kAllowColorFilter_Flag; break;
189 case SkSL::ProgramKind::kRuntimeShader: flags |= kAllowShader_Flag; break;
190 default: SkUNREACHABLE;
191 }
192
193
194 if (sampleCoordsUsage.fRead || sampleCoordsUsage.fWrite) {
195 flags |= kUsesSampleCoords_Flag;
196 }
197
198 // Color filters are not allowed to depend on position (local or device) in any way.
199 // The signature of main, and the declarations in sksl_rt_colorfilter should guarantee this.
200 if (flags & kAllowColorFilter_Flag) {
201 SkASSERT(!(flags & kUsesSampleCoords_Flag));
202 SkASSERT(!SkSL::Analysis::ReferencesFragCoords(*program));
203 }
204
205 size_t offset = 0;
206 std::vector<Uniform> uniforms;
207 std::vector<Child> children;
208 std::vector<SkSL::SampleUsage> sampleUsages;
209 const SkSL::Context& ctx(compiler->context());
210
211 // Go through program elements, pulling out information that we need
212 for (const SkSL::ProgramElement* elem : program->elements()) {
213 // Variables (uniform, etc.)
214 if (elem->is<SkSL::GlobalVarDeclaration>()) {
215 const SkSL::GlobalVarDeclaration& global = elem->as<SkSL::GlobalVarDeclaration>();
216 const SkSL::VarDeclaration& varDecl = global.declaration()->as<SkSL::VarDeclaration>();
217
218 const SkSL::Variable& var = varDecl.var();
219 const SkSL::Type& varType = var.type();
220
221 // Child effects that can be sampled ('shader' or 'colorFilter')
222 if (varType.isEffectChild()) {
223 Child c;
224 c.name = var.name();
225 c.type = child_type(varType);
226 c.index = children.size();
227 children.push_back(c);
228 sampleUsages.push_back(SkSL::Analysis::GetSampleUsage(
229 *program, var, sampleCoordsUsage.fWrite != 0));
230 }
231 // 'uniform' variables
232 else if (var.modifiers().fFlags & SkSL::Modifiers::kUniform_Flag) {
233 Uniform uni;
234 uni.name = var.name();
235 uni.flags = 0;
236 uni.count = 1;
237
238 const SkSL::Type* type = &var.type();
239 if (type->isArray()) {
240 uni.flags |= Uniform::kArray_Flag;
241 uni.count = type->columns();
242 type = &type->componentType();
243 }
244
245 if (!init_uniform_type(ctx, type, &uni)) {
246 RETURN_FAILURE("Invalid uniform type: '%s'", type->displayName().c_str());
247 }
248
249 if (var.modifiers().fLayout.fFlags & SkSL::Layout::Flag::kSRGBUnpremul_Flag) {
250 uni.flags |= Uniform::kSRGBUnpremul_Flag;
251 }
252
253 uni.offset = offset;
254 offset += uni.sizeInBytes();
255 SkASSERT(SkIsAlign4(offset));
256
257 uniforms.push_back(uni);
258 }
259 }
260 }
261
262 #undef RETURN_FAILURE
263
264 sk_sp<SkRuntimeEffect> effect(new SkRuntimeEffect(std::move(sksl),
265 std::move(program),
266 options,
267 *main,
268 std::move(uniforms),
269 std::move(children),
270 std::move(sampleUsages),
271 flags));
272 return Result{std::move(effect), SkString()};
273 }
274
MakeForColorFilter(SkString sksl,const Options & options)275 SkRuntimeEffect::Result SkRuntimeEffect::MakeForColorFilter(SkString sksl, const Options& options) {
276 auto result = Make(std::move(sksl), options, SkSL::ProgramKind::kRuntimeColorFilter);
277 SkASSERT(!result.effect || result.effect->allowColorFilter());
278 return result;
279 }
280
MakeForShader(SkString sksl,const Options & options)281 SkRuntimeEffect::Result SkRuntimeEffect::MakeForShader(SkString sksl, const Options& options) {
282 auto result = Make(std::move(sksl), options, SkSL::ProgramKind::kRuntimeShader);
283 SkASSERT(!result.effect || result.effect->allowShader());
284 return result;
285 }
286
MakeForColorFilter(std::unique_ptr<SkSL::Program> program)287 SkRuntimeEffect::Result SkRuntimeEffect::MakeForColorFilter(std::unique_ptr<SkSL::Program> program) {
288 auto result = Make(std::move(program), SkSL::ProgramKind::kRuntimeColorFilter);
289 SkASSERT(!result.effect || result.effect->allowColorFilter());
290 return result;
291 }
292
MakeForShader(std::unique_ptr<SkSL::Program> program)293 SkRuntimeEffect::Result SkRuntimeEffect::MakeForShader(std::unique_ptr<SkSL::Program> program) {
294 auto result = Make(std::move(program), SkSL::ProgramKind::kRuntimeShader);
295 SkASSERT(!result.effect || result.effect->allowShader());
296 return result;
297 }
298
SkMakeCachedRuntimeEffect(SkRuntimeEffect::Result (* make)(SkString sksl),SkString sksl)299 sk_sp<SkRuntimeEffect> SkMakeCachedRuntimeEffect(SkRuntimeEffect::Result (*make)(SkString sksl),
300 SkString sksl) {
301 SK_BEGIN_REQUIRE_DENSE
302 struct Key {
303 uint32_t skslHashA;
304 uint32_t skslHashB;
305
306 bool operator==(const Key& that) const {
307 return this->skslHashA == that.skslHashA
308 && this->skslHashB == that.skslHashB;
309 }
310
311 explicit Key(const SkString& sksl)
312 : skslHashA(SkOpts::hash(sksl.c_str(), sksl.size(), 0))
313 , skslHashB(SkOpts::hash(sksl.c_str(), sksl.size(), 1)) {}
314 };
315 SK_END_REQUIRE_DENSE
316
317 static auto* mutex = new SkMutex;
318 static auto* cache = new SkLRUCache<Key, sk_sp<SkRuntimeEffect>>(11/*totally arbitrary*/);
319
320 Key key(sksl);
321 {
322 SkAutoMutexExclusive _(*mutex);
323 if (sk_sp<SkRuntimeEffect>* found = cache->find(key)) {
324 return *found;
325 }
326 }
327
328 auto [effect, err] = make(std::move(sksl));
329 if (!effect) {
330 return nullptr;
331 }
332 SkASSERT(err.isEmpty());
333
334 {
335 SkAutoMutexExclusive _(*mutex);
336 cache->insert_or_update(key, effect);
337 }
338 return effect;
339 }
340
sizeInBytes() const341 size_t SkRuntimeEffect::Uniform::sizeInBytes() const {
342 static_assert(sizeof(int) == sizeof(float));
343 auto element_size = [](Type type) -> size_t {
344 switch (type) {
345 case Type::kFloat: return sizeof(float);
346 case Type::kFloat2: return sizeof(float) * 2;
347 case Type::kFloat3: return sizeof(float) * 3;
348 case Type::kFloat4: return sizeof(float) * 4;
349
350 case Type::kFloat2x2: return sizeof(float) * 4;
351 case Type::kFloat3x3: return sizeof(float) * 9;
352 case Type::kFloat4x4: return sizeof(float) * 16;
353
354 case Type::kInt: return sizeof(int);
355 case Type::kInt2: return sizeof(int) * 2;
356 case Type::kInt3: return sizeof(int) * 3;
357 case Type::kInt4: return sizeof(int) * 4;
358 default: SkUNREACHABLE;
359 }
360 };
361 return element_size(this->type) * this->count;
362 }
363
SkRuntimeEffect(SkString sksl,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)364 SkRuntimeEffect::SkRuntimeEffect(SkString sksl,
365 std::unique_ptr<SkSL::Program> baseProgram,
366 const Options& options,
367 const SkSL::FunctionDefinition& main,
368 std::vector<Uniform>&& uniforms,
369 std::vector<Child>&& children,
370 std::vector<SkSL::SampleUsage>&& sampleUsages,
371 uint32_t flags)
372 : fHash(SkGoodHash()(sksl))
373 , fSkSL(std::move(sksl))
374 , fBaseProgram(std::move(baseProgram))
375 , fMain(main)
376 , fUniforms(std::move(uniforms))
377 , fChildren(std::move(children))
378 , fSampleUsages(std::move(sampleUsages))
379 , fFlags(flags) {
380 SkASSERT(fBaseProgram);
381 SkASSERT(fChildren.size() == fSampleUsages.size());
382
383 // Everything from SkRuntimeEffect::Options which could influence the compiled result needs to
384 // be accounted for in `fHash`. If you've added a new field to Options and caused the static-
385 // assert below to trigger, please incorporate your field into `fHash` and update KnownOptions
386 // to match the layout of Options.
387 struct KnownOptions { bool a, b; };
388 static_assert(sizeof(Options) == sizeof(KnownOptions));
389 fHash = SkOpts::hash_fn(&options.forceNoInline,
390 sizeof(options.forceNoInline), fHash);
391 fHash = SkOpts::hash_fn(&options.enforceES2Restrictions,
392 sizeof(options.enforceES2Restrictions), fHash);
393
394 this->initFilterColorInfo();
395 }
396
397 SkRuntimeEffect::~SkRuntimeEffect() = default;
398
uniformSize() const399 size_t SkRuntimeEffect::uniformSize() const {
400 return fUniforms.empty() ? 0
401 : SkAlign4(fUniforms.back().offset + fUniforms.back().sizeInBytes());
402 }
403
findUniform(const char * name) const404 const SkRuntimeEffect::Uniform* SkRuntimeEffect::findUniform(const char* name) const {
405 auto iter = std::find_if(fUniforms.begin(), fUniforms.end(),
406 [name](const Uniform& u) { return u.name.equals(name); });
407 return iter == fUniforms.end() ? nullptr : &(*iter);
408 }
409
findChild(const char * name) const410 const SkRuntimeEffect::Child* SkRuntimeEffect::findChild(const char* name) const {
411 auto iter = std::find_if(fChildren.begin(), fChildren.end(),
412 [name](const Child& c) { return c.name.equals(name); });
413 return iter == fChildren.end() ? nullptr : &(*iter);
414 }
415
initFilterColorInfo()416 void SkRuntimeEffect::initFilterColorInfo() {
417 // Runtime effects are often long lived & cached. So: build and save a program that can
418 // filter a single color, without baking in anything tied to a particular instance
419 // (uniforms or children). This isn't possible (or needed) for shaders.
420 if (!this->allowColorFilter()) {
421 return;
422 }
423
424 // We allocate a uniform color for the input color, and for each child in the SkSL. When we run
425 // this program later, these uniform values are replaced with either the results of the child,
426 // or the input color (if the child is nullptr). These Uniform ids are loads from the *first*
427 // arg ptr.
428 //
429 // This scheme only works if every child is sampled using the original input color. If we detect
430 // a sampleChild call where a different color is being supplied, we bail out, and the returned
431 // info will have a null program. (Callers will need to fall back to another implementation.)
432 skvm::Builder p;
433 skvm::Uniforms childColorUniforms{p.uniform(), 0};
434 skvm::Color inputColor = p.uniformColor(/*placeholder*/ SkColors::kWhite, &childColorUniforms);
435 std::vector<skvm::Color> childColors;
436 for (size_t i = 0; i < fChildren.size(); ++i) {
437 childColors.push_back(
438 p.uniformColor(/*placeholder*/ SkColors::kWhite, &childColorUniforms));
439 }
440 bool allSampleCallsPassInputColor = true;
441 auto sampleChild = [&](int ix, skvm::Coord, skvm::Color color) {
442 if (color.r.id != inputColor.r.id ||
443 color.g.id != inputColor.g.id ||
444 color.b.id != inputColor.b.id ||
445 color.a.id != inputColor.a.id) {
446 allSampleCallsPassInputColor = false;
447 }
448 return childColors[ix];
449 };
450
451 // For SkSL uniforms, we reserve space and allocate skvm Uniform ids for each one. When we run
452 // the program, these ids will be loads from the *second* arg ptr, the uniform data of the
453 // specific color filter instance.
454 skvm::Uniforms skslUniforms{p.uniform(), 0};
455 const size_t uniformCount = this->uniformSize() / 4;
456 std::vector<skvm::Val> uniform;
457 uniform.reserve(uniformCount);
458 for (size_t i = 0; i < uniformCount; i++) {
459 uniform.push_back(p.uniform32(skslUniforms.push(/*placeholder*/ 0)).id);
460 }
461
462 // Emit the skvm instructions for the SkSL
463 skvm::Coord zeroCoord = {p.splat(0.0f), p.splat(0.0f)};
464 skvm::Color result = SkSL::ProgramToSkVM(*fBaseProgram,
465 fMain,
466 &p,
467 SkMakeSpan(uniform),
468 /*device=*/zeroCoord,
469 /*local=*/zeroCoord,
470 inputColor,
471 sampleChild);
472
473 // Then store the result to the *third* arg ptr
474 p.store({skvm::PixelFormat::FLOAT, 32, 32, 32, 32, 0, 32, 64, 96}, p.arg(16), result);
475
476 // This is conservative. If a filter gets the input color by sampling a null child, we'll
477 // return an (acceptable) false negative. All internal runtime color filters should work.
478 fColorFilterProgramLeavesAlphaUnchanged = (inputColor.a.id == result.a.id);
479
480 // We'll use this program to filter one color at a time, don't bother with jit
481 fColorFilterProgram = allSampleCallsPassInputColor
482 ? std::make_unique<skvm::Program>(
483 p.done(/*debug_name=*/nullptr, /*allow_jit=*/false))
484 : nullptr;
485 }
486
getFilterColorInfo()487 SkRuntimeEffect::FilterColorInfo SkRuntimeEffect::getFilterColorInfo() {
488 return {fColorFilterProgram.get(), fColorFilterProgramLeavesAlphaUnchanged};
489 }
490
491 ///////////////////////////////////////////////////////////////////////////////////////////////////
492
get_xformed_uniforms(const SkRuntimeEffect * effect,sk_sp<SkData> baseUniforms,const SkColorSpace * dstCS)493 static sk_sp<SkData> get_xformed_uniforms(const SkRuntimeEffect* effect,
494 sk_sp<SkData> baseUniforms,
495 const SkColorSpace* dstCS) {
496 using Flags = SkRuntimeEffect::Uniform::Flags;
497 using Type = SkRuntimeEffect::Uniform::Type;
498 SkColorSpaceXformSteps steps(sk_srgb_singleton(), kUnpremul_SkAlphaType,
499 dstCS, kUnpremul_SkAlphaType);
500
501 sk_sp<SkData> uniforms = nullptr;
502 auto writableData = [&]() {
503 if (!uniforms) {
504 uniforms = SkData::MakeWithCopy(baseUniforms->data(), baseUniforms->size());
505 }
506 return uniforms->writable_data();
507 };
508
509 for (const auto& v : effect->uniforms()) {
510 if (v.flags & Flags::kSRGBUnpremul_Flag) {
511 SkASSERT(v.type == Type::kFloat3 || v.type == Type::kFloat4);
512 if (steps.flags.mask()) {
513 float* color = SkTAddOffset<float>(writableData(), v.offset);
514 if (v.type == Type::kFloat4) {
515 // RGBA, easy case
516 for (int i = 0; i < v.count; ++i) {
517 steps.apply(color);
518 color += 4;
519 }
520 } else {
521 // RGB, need to pad out to include alpha. Technically, this isn't necessary,
522 // because steps shouldn't include unpremul or premul, and thus shouldn't
523 // read or write the fourth element. But let's be safe.
524 float rgba[4];
525 for (int i = 0; i < v.count; ++i) {
526 memcpy(rgba, color, 3 * sizeof(float));
527 rgba[3] = 1.0f;
528 steps.apply(rgba);
529 memcpy(color, rgba, 3 * sizeof(float));
530 color += 3;
531 }
532 }
533 }
534 }
535 }
536 return uniforms ? uniforms : baseUniforms;
537 }
538
539 class SkRuntimeColorFilter : public SkColorFilterBase {
540 public:
SkRuntimeColorFilter(sk_sp<SkRuntimeEffect> effect,sk_sp<SkData> uniforms,sk_sp<SkColorFilter> children[],size_t childCount)541 SkRuntimeColorFilter(sk_sp<SkRuntimeEffect> effect,
542 sk_sp<SkData> uniforms,
543 sk_sp<SkColorFilter> children[],
544 size_t childCount)
545 : fEffect(std::move(effect))
546 , fUniforms(std::move(uniforms))
547 , fChildren(children, children + childCount) {}
548
549 #if SK_SUPPORT_GPU
asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,GrRecordingContext * context,const GrColorInfo & colorInfo) const550 GrFPResult asFragmentProcessor(std::unique_ptr<GrFragmentProcessor> inputFP,
551 GrRecordingContext* context,
552 const GrColorInfo& colorInfo) const override {
553 sk_sp<SkData> uniforms =
554 get_xformed_uniforms(fEffect.get(), fUniforms, colorInfo.colorSpace());
555 SkASSERT(uniforms);
556
557 auto fp = GrSkSLFP::Make(fEffect, "Runtime_Color_Filter", std::move(uniforms));
558 for (const auto& child : fChildren) {
559 std::unique_ptr<GrFragmentProcessor> childFP;
560 if (child) {
561 bool success;
562 std::tie(success, childFP) = as_CFB(child)->asFragmentProcessor(
563 /*inputFP=*/nullptr, context, colorInfo);
564 if (!success) {
565 return GrFPFailure(std::move(inputFP));
566 }
567 }
568 fp->addChild(std::move(childFP));
569 }
570
571 // Runtime effect scripts are written to take an input color, not a fragment processor.
572 // We need to pass the input to the runtime filter using Compose. This ensures that it will
573 // be invoked exactly once, and the result will be returned when null children are sampled,
574 // or as the (default) input color for non-null children.
575 return GrFPSuccess(GrFragmentProcessor::Compose(std::move(fp), std::move(inputFP)));
576 }
577 #endif
578
onAppendStages(const SkStageRec & rec,bool shaderIsOpaque) const579 bool onAppendStages(const SkStageRec& rec, bool shaderIsOpaque) const override {
580 return false;
581 }
582
onProgram(skvm::Builder * p,skvm::Color c,SkColorSpace * dstCS,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const583 skvm::Color onProgram(skvm::Builder* p, skvm::Color c,
584 SkColorSpace* dstCS,
585 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
586 sk_sp<SkData> inputs = get_xformed_uniforms(fEffect.get(), fUniforms, dstCS);
587 SkASSERT(inputs);
588
589 // There should be no way for the color filter to use device coords, but we need to supply
590 // something. (Uninitialized values can trigger asserts in skvm::Builder).
591 skvm::Coord zeroCoord = { p->splat(0.0f), p->splat(0.0f) };
592
593 auto sampleChild = [&](int ix, skvm::Coord /*coord*/, skvm::Color color) {
594 if (fChildren[ix]) {
595 return as_CFB(fChildren[ix])->program(p, color, dstCS, uniforms, alloc);
596 } else {
597 return color;
598 }
599 };
600
601 const size_t uniformCount = fEffect->uniformSize() / 4;
602 std::vector<skvm::Val> uniform;
603 uniform.reserve(uniformCount);
604 for (size_t i = 0; i < uniformCount; i++) {
605 int bits;
606 memcpy(&bits, (const char*)inputs->data() + 4*i, 4);
607 uniform.push_back(p->uniform32(uniforms->push(bits)).id);
608 }
609
610 return SkSL::ProgramToSkVM(*fEffect->fBaseProgram, fEffect->fMain, p, SkMakeSpan(uniform),
611 /*device=*/zeroCoord, /*local=*/zeroCoord, c, sampleChild);
612 }
613
onFilterColor4f(const SkPMColor4f & color,SkColorSpace * dstCS) const614 SkPMColor4f onFilterColor4f(const SkPMColor4f& color, SkColorSpace* dstCS) const override {
615 // Get the generic program for filtering a single color
616 const skvm::Program* program = fEffect->getFilterColorInfo().program;
617 if (!program) {
618 // We were unable to build a cached (per-effect) program. Use the base-class fallback,
619 // which builds a program for the specific filter instance.
620 return SkColorFilterBase::onFilterColor4f(color, dstCS);
621 }
622
623 // Get our specific uniform values
624 sk_sp<SkData> inputs = get_xformed_uniforms(fEffect.get(), fUniforms, dstCS);
625 SkASSERT(inputs);
626
627 // 'program' defines sampling any child as returning a uniform color. Assemble a buffer
628 // containing those colors. The first entry is always the input color. Subsequent entries
629 // are for children. For any null children, the sample result is just the input color.
630 // For non-null children, it's the result of that child filtering the input color.
631 SkSTArray<1, SkPMColor4f, true> inputColors;
632 inputColors.push_back(color);
633 for (const auto &child : fChildren) {
634 inputColors.push_back(child ? as_CFB(child)->onFilterColor4f(color, dstCS) : color);
635 }
636
637 SkPMColor4f result;
638 program->eval(1, inputColors.begin(), inputs->data(), result.vec());
639 return result;
640 }
641
onIsAlphaUnchanged() const642 bool onIsAlphaUnchanged() const override {
643 return fEffect->getFilterColorInfo().alphaUnchanged;
644 }
645
flatten(SkWriteBuffer & buffer) const646 void flatten(SkWriteBuffer& buffer) const override {
647 buffer.writeString(fEffect->source().c_str());
648 if (fUniforms) {
649 buffer.writeDataAsByteArray(fUniforms.get());
650 } else {
651 buffer.writeByteArray(nullptr, 0);
652 }
653 buffer.write32(fChildren.size());
654 for (const auto& child : fChildren) {
655 buffer.writeFlattenable(child.get());
656 }
657 }
658
659 SK_FLATTENABLE_HOOKS(SkRuntimeColorFilter)
660
661 private:
662 sk_sp<SkRuntimeEffect> fEffect;
663 sk_sp<SkData> fUniforms;
664 std::vector<sk_sp<SkColorFilter>> fChildren;
665 };
666
CreateProc(SkReadBuffer & buffer)667 sk_sp<SkFlattenable> SkRuntimeColorFilter::CreateProc(SkReadBuffer& buffer) {
668 SkString sksl;
669 buffer.readString(&sksl);
670 sk_sp<SkData> uniforms = buffer.readByteArrayAsData();
671
672 auto effect = SkMakeCachedRuntimeEffect(SkRuntimeEffect::MakeForColorFilter, std::move(sksl));
673 if (!buffer.validate(effect != nullptr)) {
674 return nullptr;
675 }
676
677 size_t childCount = buffer.read32();
678 if (!buffer.validate(childCount == effect->children().count())) {
679 return nullptr;
680 }
681
682 std::vector<sk_sp<SkColorFilter>> children(childCount);
683 for (size_t i = 0; i < children.size(); ++i) {
684 children[i] = buffer.readColorFilter();
685 }
686
687 return effect->makeColorFilter(std::move(uniforms), children.data(), children.size());
688 }
689
690 ///////////////////////////////////////////////////////////////////////////////////////////////////
691
692 class SkRTShader : public SkShaderBase {
693 public:
SkRTShader(sk_sp<SkRuntimeEffect> effect,sk_sp<SkData> uniforms,const SkMatrix * localMatrix,sk_sp<SkShader> * children,size_t childCount,bool isOpaque)694 SkRTShader(sk_sp<SkRuntimeEffect> effect, sk_sp<SkData> uniforms, const SkMatrix* localMatrix,
695 sk_sp<SkShader>* children, size_t childCount, bool isOpaque)
696 : SkShaderBase(localMatrix)
697 , fEffect(std::move(effect))
698 , fIsOpaque(isOpaque)
699 , fUniforms(std::move(uniforms))
700 , fChildren(children, children + childCount) {}
701
isOpaque() const702 bool isOpaque() const override { return fIsOpaque; }
703
704 #if SK_SUPPORT_GPU
asFragmentProcessor(const GrFPArgs & args) const705 std::unique_ptr<GrFragmentProcessor> asFragmentProcessor(const GrFPArgs& args) const override {
706 SkMatrix matrix;
707 if (!this->totalLocalMatrix(args.fPreLocalMatrix)->invert(&matrix)) {
708 return nullptr;
709 }
710
711 sk_sp<SkData> uniforms =
712 get_xformed_uniforms(fEffect.get(), fUniforms, args.fDstColorInfo->colorSpace());
713 SkASSERT(uniforms);
714
715 // If we sample children with explicit colors, this may not be true.
716 // TODO: Determine this via analysis?
717 GrFPArgs childArgs = args;
718 childArgs.fInputColorIsOpaque = false;
719
720 auto fp = GrSkSLFP::Make(fEffect, "runtime_shader", std::move(uniforms));
721 for (const auto& child : fChildren) {
722 auto childFP = child ? as_SB(child)->asFragmentProcessor(childArgs) : nullptr;
723 fp->addChild(std::move(childFP));
724 }
725 std::unique_ptr<GrFragmentProcessor> result = std::move(fp);
726 // If the shader was created with isOpaque = true, we *force* that result here.
727 // CPU does the same thing (in SkShaderBase::program).
728 if (fIsOpaque) {
729 result = GrFragmentProcessor::SwizzleOutput(std::move(result), GrSwizzle::RGB1());
730 }
731 result = GrMatrixEffect::Make(matrix, std::move(result));
732 // Three cases of GrClampType to think about:
733 // kAuto - Normalized fixed-point. If fIsOpaque, then A is 1 (above), and the format's
734 // range ensures RGB must be no larger. If !fIsOpaque, we clamp here.
735 // kManual - Normalized floating point. Whether or not we set A above, the format's range
736 // means we need to clamp RGB.
737 // kNone - Unclamped floating point. No clamping is done, ever.
738 GrClampType clampType = GrColorTypeClampType(args.fDstColorInfo->colorType());
739 if (clampType == GrClampType::kManual || (clampType == GrClampType::kAuto && !fIsOpaque)) {
740 return GrFragmentProcessor::ClampPremulOutput(std::move(result));
741 } else {
742 return result;
743 }
744 }
745 #endif
746
onAppendStages(const SkStageRec & rec) const747 bool onAppendStages(const SkStageRec& rec) const override {
748 return false;
749 }
750
onProgram(skvm::Builder * p,skvm::Coord device,skvm::Coord local,skvm::Color paint,const SkMatrixProvider & matrices,const SkMatrix * localM,const SkColorInfo & dst,skvm::Uniforms * uniforms,SkArenaAlloc * alloc) const751 skvm::Color onProgram(skvm::Builder* p,
752 skvm::Coord device, skvm::Coord local, skvm::Color paint,
753 const SkMatrixProvider& matrices, const SkMatrix* localM,
754 const SkColorInfo& dst,
755 skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
756 sk_sp<SkData> inputs = get_xformed_uniforms(fEffect.get(), fUniforms, dst.colorSpace());
757 SkASSERT(inputs);
758
759 SkMatrix inv;
760 if (!this->computeTotalInverse(matrices.localToDevice(), localM, &inv)) {
761 return {};
762 }
763 local = SkShaderBase::ApplyMatrix(p,inv,local,uniforms);
764
765 auto sampleChild = [&](int ix, skvm::Coord coord, skvm::Color color) {
766 if (fChildren[ix]) {
767 SkOverrideDeviceMatrixProvider mats{matrices, SkMatrix::I()};
768 return as_SB(fChildren[ix])->program(p, device, coord, color,
769 mats, nullptr, dst,
770 uniforms, alloc);
771 } else {
772 return color;
773 }
774 };
775
776 const size_t uniformCount = fEffect->uniformSize() / 4;
777 std::vector<skvm::Val> uniform;
778 uniform.reserve(uniformCount);
779 for (size_t i = 0; i < uniformCount; i++) {
780 int bits;
781 memcpy(&bits, (const char*)inputs->data() + 4*i, 4);
782 uniform.push_back(p->uniform32(uniforms->push(bits)).id);
783 }
784
785 return SkSL::ProgramToSkVM(*fEffect->fBaseProgram, fEffect->fMain, p, SkMakeSpan(uniform),
786 device, local, paint, sampleChild);
787 }
788
flatten(SkWriteBuffer & buffer) const789 void flatten(SkWriteBuffer& buffer) const override {
790 uint32_t flags = 0;
791 if (fIsOpaque) {
792 flags |= kIsOpaque_Flag;
793 }
794 if (!this->getLocalMatrix().isIdentity()) {
795 flags |= kHasLocalMatrix_Flag;
796 }
797
798 buffer.writeString(fEffect->source().c_str());
799 if (fUniforms) {
800 buffer.writeDataAsByteArray(fUniforms.get());
801 } else {
802 buffer.writeByteArray(nullptr, 0);
803 }
804 buffer.write32(flags);
805 if (flags & kHasLocalMatrix_Flag) {
806 buffer.writeMatrix(this->getLocalMatrix());
807 }
808 buffer.write32(fChildren.size());
809 for (const auto& child : fChildren) {
810 buffer.writeFlattenable(child.get());
811 }
812 }
813
asRuntimeEffect() const814 SkRuntimeEffect* asRuntimeEffect() const override { return fEffect.get(); }
815
816 SK_FLATTENABLE_HOOKS(SkRTShader)
817
818 private:
819 enum Flags {
820 kIsOpaque_Flag = 1 << 0,
821 kHasLocalMatrix_Flag = 1 << 1,
822 };
823
824 sk_sp<SkRuntimeEffect> fEffect;
825 bool fIsOpaque;
826
827 sk_sp<SkData> fUniforms;
828 std::vector<sk_sp<SkShader>> fChildren;
829 };
830
CreateProc(SkReadBuffer & buffer)831 sk_sp<SkFlattenable> SkRTShader::CreateProc(SkReadBuffer& buffer) {
832 SkString sksl;
833 buffer.readString(&sksl);
834 sk_sp<SkData> uniforms = buffer.readByteArrayAsData();
835 uint32_t flags = buffer.read32();
836
837 bool isOpaque = SkToBool(flags & kIsOpaque_Flag);
838 SkMatrix localM, *localMPtr = nullptr;
839 if (flags & kHasLocalMatrix_Flag) {
840 buffer.readMatrix(&localM);
841 localMPtr = &localM;
842 }
843
844 auto effect = SkMakeCachedRuntimeEffect(SkRuntimeEffect::MakeForShader, std::move(sksl));
845 if (!buffer.validate(effect != nullptr)) {
846 return nullptr;
847 }
848
849 size_t childCount = buffer.read32();
850 if (!buffer.validate(childCount == effect->children().count())) {
851 return nullptr;
852 }
853
854 std::vector<sk_sp<SkShader>> children(childCount);
855 for (size_t i = 0; i < children.size(); ++i) {
856 children[i] = buffer.readShader();
857 }
858
859 return effect->makeShader(std::move(uniforms), children.data(), children.size(), localMPtr,
860 isOpaque);
861 }
862
863 ///////////////////////////////////////////////////////////////////////////////////////////////////
864
865 #if SK_SUPPORT_GPU
makeFP(sk_sp<SkData> uniforms,std::unique_ptr<GrFragmentProcessor> children[],size_t childCount) const866 std::unique_ptr<GrFragmentProcessor> SkRuntimeEffect::makeFP(
867 sk_sp<SkData> uniforms,
868 std::unique_ptr<GrFragmentProcessor> children[],
869 size_t childCount) const {
870 if (!uniforms) {
871 uniforms = SkData::MakeEmpty();
872 }
873 auto fp = GrSkSLFP::Make(sk_ref_sp(this), "make_fp", std::move(uniforms));
874 for (size_t i = 0; i < childCount; ++i) {
875 fp->addChild(std::move(children[i]));
876 }
877 return std::move(fp);
878 }
879 #endif
880
makeShader(sk_sp<SkData> uniforms,sk_sp<SkShader> children[],size_t childCount,const SkMatrix * localMatrix,bool isOpaque) const881 sk_sp<SkShader> SkRuntimeEffect::makeShader(sk_sp<SkData> uniforms,
882 sk_sp<SkShader> children[],
883 size_t childCount,
884 const SkMatrix* localMatrix,
885 bool isOpaque) const {
886 if (!this->allowShader()) {
887 return nullptr;
888 }
889 if (!uniforms) {
890 uniforms = SkData::MakeEmpty();
891 }
892 // Verify that all child objects are shaders (to match the C++ types here).
893 // TODO(skia:11813) When we support shader and colorFilter children (with different samplng
894 // semantics), the 'children' parameter will contain both types, so this will be more complex.
895 if (!std::all_of(fChildren.begin(), fChildren.end(), [](const Child& c) {
896 return c.type == Child::Type::kShader;
897 })) {
898 return nullptr;
899 }
900 return uniforms->size() == this->uniformSize() && childCount == fChildren.size()
901 ? sk_sp<SkShader>(new SkRTShader(sk_ref_sp(this),
902 std::move(uniforms),
903 localMatrix,
904 children,
905 childCount,
906 isOpaque))
907 : nullptr;
908 }
909
makeImage(GrRecordingContext * recordingContext,sk_sp<SkData> uniforms,sk_sp<SkShader> children[],size_t childCount,const SkMatrix * localMatrix,SkImageInfo resultInfo,bool mipmapped) const910 sk_sp<SkImage> SkRuntimeEffect::makeImage(GrRecordingContext* recordingContext,
911 sk_sp<SkData> uniforms,
912 sk_sp<SkShader> children[],
913 size_t childCount,
914 const SkMatrix* localMatrix,
915 SkImageInfo resultInfo,
916 bool mipmapped) const {
917 if (recordingContext) {
918 #if SK_SUPPORT_GPU
919 if (!recordingContext->priv().caps()->mipmapSupport()) {
920 mipmapped = false;
921 }
922 auto fillContext = GrSurfaceFillContext::Make(recordingContext,
923 resultInfo,
924 SkBackingFit::kExact,
925 /*sample count*/ 1,
926 GrMipmapped(mipmapped));
927 if (!fillContext) {
928 return nullptr;
929 }
930 uniforms = get_xformed_uniforms(this, std::move(uniforms), resultInfo.colorSpace());
931 SkASSERT(uniforms);
932
933 auto fp = GrSkSLFP::Make(sk_ref_sp(this),
934 "runtime_image",
935 std::move(uniforms));
936 SkSimpleMatrixProvider matrixProvider(SkMatrix::I());
937 GrColorInfo colorInfo(resultInfo.colorInfo());
938 GrFPArgs args(recordingContext, matrixProvider, &colorInfo);
939 for (size_t i = 0; i < childCount; ++i) {
940 if (!children[i]) {
941 return nullptr;
942 }
943 auto childFP = as_SB(children[i])->asFragmentProcessor(args);
944 fp->addChild(std::move(childFP));
945 }
946 if (localMatrix) {
947 SkMatrix invLM;
948 if (!localMatrix->invert(&invLM)) {
949 return nullptr;
950 }
951 fillContext->fillWithFP(invLM, std::move(fp));
952 } else {
953 fillContext->fillWithFP(std::move(fp));
954 }
955 return sk_sp<SkImage>(new SkImage_Gpu(sk_ref_sp(recordingContext),
956 kNeedNewImageUniqueID,
957 fillContext->readSurfaceView(),
958 resultInfo.colorInfo()));
959 #else
960 return nullptr;
961 #endif
962 }
963 if (resultInfo.alphaType() == kUnpremul_SkAlphaType) {
964 // We don't have a good way of supporting this right now. In this case the runtime effect
965 // will produce a unpremul value. The shader generated from it is assumed to produce
966 // premul and RGB get pinned to A. Moreover, after the blend in premul the new dst is
967 // unpremul'ed, producing a double unpremul result.
968 return nullptr;
969 }
970 auto surf = SkSurface::MakeRaster(resultInfo);
971 if (!surf) {
972 return nullptr;
973 }
974 SkCanvas* canvas = surf->getCanvas();
975 SkTLazy<SkCanvas> tempCanvas;
976 auto shader = this->makeShader(std::move(uniforms), children, childCount, localMatrix, false);
977 if (!shader) {
978 return nullptr;
979 }
980 SkPaint paint;
981 paint.setShader(std::move(shader));
982 paint.setBlendMode(SkBlendMode::kSrc);
983 canvas->drawPaint(paint);
984 // TODO: Specify snapshot should have mip levels if mipmapped is true.
985 return surf->makeImageSnapshot();
986 }
987
makeColorFilter(sk_sp<SkData> uniforms,sk_sp<SkColorFilter> children[],size_t childCount) const988 sk_sp<SkColorFilter> SkRuntimeEffect::makeColorFilter(sk_sp<SkData> uniforms,
989 sk_sp<SkColorFilter> children[],
990 size_t childCount) const {
991 if (!this->allowColorFilter()) {
992 return nullptr;
993 }
994 if (!uniforms) {
995 uniforms = SkData::MakeEmpty();
996 }
997 // Verify that all child objects are color filters (to match the C++ types here).
998 // TODO(skia:11813) When we support shader and colorFilter children (with different samplng
999 // semantics), the 'children' parameter will contain both types, so this will be more complex.
1000 if (!std::all_of(fChildren.begin(), fChildren.end(), [](const Child& c) {
1001 return c.type == Child::Type::kColorFilter;
1002 })) {
1003 return nullptr;
1004 }
1005 return uniforms->size() == this->uniformSize() && childCount == fChildren.size()
1006 ? sk_sp<SkColorFilter>(new SkRuntimeColorFilter(
1007 sk_ref_sp(this), std::move(uniforms), children, childCount))
1008 : nullptr;
1009 }
1010
makeColorFilter(sk_sp<SkData> uniforms) const1011 sk_sp<SkColorFilter> SkRuntimeEffect::makeColorFilter(sk_sp<SkData> uniforms) const {
1012 return this->makeColorFilter(std::move(uniforms), nullptr, 0);
1013 }
1014
1015 ///////////////////////////////////////////////////////////////////////////////////////////////////
1016
RegisterFlattenables()1017 void SkRuntimeEffect::RegisterFlattenables() {
1018 SK_REGISTER_FLATTENABLE(SkRuntimeColorFilter);
1019 SK_REGISTER_FLATTENABLE(SkRTShader);
1020 }
1021
SkRuntimeShaderBuilder(sk_sp<SkRuntimeEffect> effect)1022 SkRuntimeShaderBuilder::SkRuntimeShaderBuilder(sk_sp<SkRuntimeEffect> effect)
1023 : INHERITED(std::move(effect)) {}
1024
1025 SkRuntimeShaderBuilder::~SkRuntimeShaderBuilder() = default;
1026
makeImage(GrRecordingContext * recordingContext,const SkMatrix * localMatrix,SkImageInfo resultInfo,bool mipmapped)1027 sk_sp<SkImage> SkRuntimeShaderBuilder::makeImage(GrRecordingContext* recordingContext,
1028 const SkMatrix* localMatrix,
1029 SkImageInfo resultInfo,
1030 bool mipmapped) {
1031 return this->effect()->makeImage(recordingContext,
1032 this->uniforms(),
1033 this->children(),
1034 this->numChildren(),
1035 localMatrix,
1036 resultInfo,
1037 mipmapped);
1038 }
1039
makeShader(const SkMatrix * localMatrix,bool isOpaque)1040 sk_sp<SkShader> SkRuntimeShaderBuilder::makeShader(const SkMatrix* localMatrix, bool isOpaque) {
1041 return this->effect()->makeShader(this->uniforms(),
1042 this->children(),
1043 this->numChildren(),
1044 localMatrix,
1045 isOpaque);
1046 }
1047