Flutter Engine
The Flutter Engine
GrFragmentProcessor.cpp
Go to the documentation of this file.
1/*
2* Copyright 2015 Google Inc.
3*
4* Use of this source code is governed by a BSD-style license that can be
5* found in the LICENSE file.
6*/
7
9
10#include "include/core/SkM44.h"
11#include "src/base/SkVx.h"
13#include "src/gpu/KeyBuilder.h"
24
26 if (this->classID() != that.classID()) {
27 return false;
28 }
29 if (this->sampleUsage() != that.sampleUsage()) {
30 return false;
31 }
32 if (!this->onIsEqual(that)) {
33 return false;
34 }
35 if (this->numChildProcessors() != that.numChildProcessors()) {
36 return false;
37 }
38 for (int i = 0; i < this->numChildProcessors(); ++i) {
39 auto thisChild = this->childProcessor(i),
40 thatChild = that .childProcessor(i);
41 if (SkToBool(thisChild) != SkToBool(thatChild)) {
42 return false;
43 }
44 if (thisChild && !thisChild->isEqual(*thatChild)) {
45 return false;
46 }
47 }
48 return true;
49}
50
52 this->visitTextureEffects([&func](const GrTextureEffect& te) {
53 func(te.view().proxy(), te.samplerState().mipmapped());
54 });
55}
56
58 const std::function<void(const GrTextureEffect&)>& func) const {
59 if (auto* te = this->asTextureEffect()) {
60 func(*te);
61 }
62 for (auto& child : fChildProcessors) {
63 if (child) {
64 child->visitTextureEffects(func);
65 }
66 }
67}
68
70 const std::function<void(const GrFragmentProcessor&, ProgramImpl&)>& f,
71 ProgramImpl& impl) const {
72 f(*this, impl);
73 SkASSERT(impl.numChildProcessors() == this->numChildProcessors());
74 for (int i = 0; i < this->numChildProcessors(); ++i) {
75 if (const auto* child = this->childProcessor(i)) {
76 child->visitWithImpls(f, *impl.childProcessor(i));
77 }
78 }
79}
80
82 if (this->classID() == kGrTextureEffect_ClassID) {
83 return static_cast<GrTextureEffect*>(this);
84 }
85 return nullptr;
86}
87
89 if (this->classID() == kGrTextureEffect_ClassID) {
90 return static_cast<const GrTextureEffect*>(this);
91 }
92 return nullptr;
93}
94
95#if defined(GR_TEST_UTILS)
96static void recursive_dump_tree_info(const GrFragmentProcessor& fp,
97 SkString indent,
98 SkString* text) {
99 for (int index = 0; index < fp.numChildProcessors(); ++index) {
100 text->appendf("\n%s(#%d) -> ", indent.c_str(), index);
101 if (const GrFragmentProcessor* childFP = fp.childProcessor(index)) {
102 text->append(childFP->dumpInfo());
103 indent.append("\t");
104 recursive_dump_tree_info(*childFP, indent, text);
105 } else {
106 text->append("null");
107 }
108 }
109}
110
111SkString GrFragmentProcessor::dumpTreeInfo() const {
112 SkString text = this->dumpInfo();
113 recursive_dump_tree_info(*this, SkString("\t"), &text);
114 text.append("\n");
115 return text;
116}
117#endif
118
119std::unique_ptr<GrFragmentProcessor::ProgramImpl> GrFragmentProcessor::makeProgramImpl() const {
120 std::unique_ptr<ProgramImpl> impl = this->onMakeProgramImpl();
121 impl->fChildProcessors.push_back_n(fChildProcessors.size());
122 for (int i = 0; i < fChildProcessors.size(); ++i) {
123 impl->fChildProcessors[i] = fChildProcessors[i] ? fChildProcessors[i]->makeProgramImpl()
124 : nullptr;
125 }
126 return impl;
127}
128
130 return std::count_if(fChildProcessors.begin(), fChildProcessors.end(),
131 [](const auto& c) { return c != nullptr; });
132}
133
134#ifdef SK_DEBUG
135bool GrFragmentProcessor::isInstantiated() const {
136 bool result = true;
137 this->visitTextureEffects([&result](const GrTextureEffect& te) {
138 if (!te.texture()) {
139 result = false;
140 }
141 });
142 return result;
143}
144#endif
145
146void GrFragmentProcessor::registerChild(std::unique_ptr<GrFragmentProcessor> child,
147 SkSL::SampleUsage sampleUsage) {
149
150 if (!child) {
151 fChildProcessors.push_back(nullptr);
152 return;
153 }
154
155 // The child should not have been attached to another FP already and not had any sampling
156 // strategy set on it.
157 SkASSERT(!child->fParent && !child->sampleUsage().isSampled());
158
159 // Configure child's sampling state first
160 child->fUsage = sampleUsage;
161
162 // Propagate the "will read dest-color" flag up to parent FPs.
163 if (child->willReadDstColor()) {
164 this->setWillReadDstColor();
165 }
166
167 // If this child receives passthrough or matrix transformed coords from its parent then note
168 // that the parent's coords are used indirectly to ensure that they aren't omitted.
169 if ((sampleUsage.isPassThrough() || sampleUsage.isUniformMatrix()) &&
170 child->usesSampleCoords()) {
171 fFlags |= kUsesSampleCoordsIndirectly_Flag;
172 }
173
174 // Record that the child is attached to us; this FP is the source of any uniform data needed
175 // to evaluate the child sample matrix.
176 child->fParent = this;
177 fChildProcessors.push_back(std::move(child));
178
179 // Validate: our sample strategy comes from a parent we shouldn't have yet.
180 SkASSERT(!fUsage.isSampled() && !fParent);
181}
182
184 for (int i = 0; i < src.numChildProcessors(); ++i) {
185 if (auto fp = src.childProcessor(i)) {
186 this->registerChild(fp->clone(), fp->sampleUsage());
187 } else {
188 this->registerChild(nullptr);
189 }
190 }
191}
192
193std::unique_ptr<GrFragmentProcessor> GrFragmentProcessor::MakeColor(SkPMColor4f color) {
194 // Use ColorFilter signature/factory to get the constant output for constant input optimization
196 "uniform half4 color;"
197 "half4 main(half4 inColor) { return color; }"
198 );
200 return GrSkSLFP::Make(effect, "color_fp", /*inputFP=*/nullptr,
203 "color", color);
204}
205
206std::unique_ptr<GrFragmentProcessor> GrFragmentProcessor::MulInputByChildAlpha(
207 std::unique_ptr<GrFragmentProcessor> fp) {
208 if (!fp) {
209 return nullptr;
210 }
211 return GrBlendFragmentProcessor::Make<SkBlendMode::kSrcIn>(/*src=*/nullptr, std::move(fp));
212}
213
214std::unique_ptr<GrFragmentProcessor> GrFragmentProcessor::ApplyPaintAlpha(
215 std::unique_ptr<GrFragmentProcessor> child) {
216 SkASSERT(child);
218 "uniform colorFilter fp;"
219 "half4 main(half4 inColor) {"
220 "return fp.eval(inColor.rgb1) * inColor.a;"
221 "}"
222 );
223 return GrSkSLFP::Make(effect, "ApplyPaintAlpha", /*inputFP=*/nullptr,
226 "fp", std::move(child));
227}
228
229std::unique_ptr<GrFragmentProcessor> GrFragmentProcessor::ModulateRGBA(
230 std::unique_ptr<GrFragmentProcessor> inputFP, const SkPMColor4f& color) {
231 auto colorFP = MakeColor(color);
232 return GrBlendFragmentProcessor::Make<SkBlendMode::kModulate>(std::move(colorFP),
233 std::move(inputFP));
234}
235
236std::unique_ptr<GrFragmentProcessor> GrFragmentProcessor::ClampOutput(
237 std::unique_ptr<GrFragmentProcessor> fp) {
238 SkASSERT(fp);
240 "half4 main(half4 inColor) {"
241 "return saturate(inColor);"
242 "}"
243 );
245 return GrSkSLFP::Make(effect, "Clamp", std::move(fp),
247}
248
249std::unique_ptr<GrFragmentProcessor> GrFragmentProcessor::SwizzleOutput(
250 std::unique_ptr<GrFragmentProcessor> fp, const skgpu::Swizzle& swizzle) {
251 class SwizzleFragmentProcessor : public GrFragmentProcessor {
252 public:
253 static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> fp,
254 const skgpu::Swizzle& swizzle) {
255 return std::unique_ptr<GrFragmentProcessor>(
256 new SwizzleFragmentProcessor(std::move(fp), swizzle));
257 }
258
259 const char* name() const override { return "Swizzle"; }
260
261 std::unique_ptr<GrFragmentProcessor> clone() const override {
262 return Make(this->childProcessor(0)->clone(), fSwizzle);
263 }
264
265 private:
266 SwizzleFragmentProcessor(std::unique_ptr<GrFragmentProcessor> fp,
267 const skgpu::Swizzle& swizzle)
269 , fSwizzle(swizzle) {
270 this->registerChild(std::move(fp));
271 }
272
273 std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override {
274 class Impl : public ProgramImpl {
275 public:
276 void emitCode(EmitArgs& args) override {
277 SkString childColor = this->invokeChild(0, args);
278
279 const SwizzleFragmentProcessor& sfp = args.fFp.cast<SwizzleFragmentProcessor>();
280 const skgpu::Swizzle& swizzle = sfp.fSwizzle;
281 GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
282
283 fragBuilder->codeAppendf("return %s.%s;",
284 childColor.c_str(), swizzle.asString().c_str());
285 }
286 };
287 return std::make_unique<Impl>();
288 }
289
290 void onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder* b) const override {
291 b->add32(fSwizzle.asKey());
292 }
293
294 bool onIsEqual(const GrFragmentProcessor& other) const override {
295 const SwizzleFragmentProcessor& sfp = other.cast<SwizzleFragmentProcessor>();
296 return fSwizzle == sfp.fSwizzle;
297 }
298
299 SkPMColor4f constantOutputForConstantInput(const SkPMColor4f& input) const override {
300 return fSwizzle.applyTo(ConstantOutputForConstantInput(this->childProcessor(0), input));
301 }
302
303 skgpu::Swizzle fSwizzle;
304
306 };
307
308 if (!fp) {
309 return nullptr;
310 }
311 if (skgpu::Swizzle::RGBA() == swizzle) {
312 return fp;
313 }
314 return SwizzleFragmentProcessor::Make(std::move(fp), swizzle);
315}
316
317//////////////////////////////////////////////////////////////////////////////
318
319std::unique_ptr<GrFragmentProcessor> GrFragmentProcessor::OverrideInput(
320 std::unique_ptr<GrFragmentProcessor> fp, const SkPMColor4f& color) {
321 if (!fp) {
322 return nullptr;
323 }
325 "uniform colorFilter fp;" // Declared as colorFilter so we can pass a color
326 "uniform half4 color;"
327 "half4 main(half4 inColor) {"
328 "return fp.eval(color);"
329 "}"
330 );
331 return GrSkSLFP::Make(effect, "OverrideInput", /*inputFP=*/nullptr,
334 "fp", std::move(fp),
335 "color", color);
336}
337
338//////////////////////////////////////////////////////////////////////////////
339
340std::unique_ptr<GrFragmentProcessor> GrFragmentProcessor::DisableCoverageAsAlpha(
341 std::unique_ptr<GrFragmentProcessor> fp) {
342 if (!fp || !fp->compatibleWithCoverageAsAlpha()) {
343 return fp;
344 }
346 "half4 main(half4 inColor) { return inColor; }"
347 );
349 return GrSkSLFP::Make(effect, "DisableCoverageAsAlpha", std::move(fp),
351}
352
353//////////////////////////////////////////////////////////////////////////////
354
355std::unique_ptr<GrFragmentProcessor> GrFragmentProcessor::DestColor() {
357 "half4 main(half4 src, half4 dst) {"
358 "return dst;"
359 "}"
360 );
361 return GrSkSLFP::Make(effect, "DestColor", /*inputFP=*/nullptr, GrSkSLFP::OptFlags::kNone);
362}
363
364//////////////////////////////////////////////////////////////////////////////
365
366std::unique_ptr<GrFragmentProcessor> GrFragmentProcessor::Compose(
367 std::unique_ptr<GrFragmentProcessor> f, std::unique_ptr<GrFragmentProcessor> g) {
368 class ComposeProcessor : public GrFragmentProcessor {
369 public:
370 static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> f,
371 std::unique_ptr<GrFragmentProcessor> g) {
372 return std::unique_ptr<GrFragmentProcessor>(new ComposeProcessor(std::move(f),
373 std::move(g)));
374 }
375
376 const char* name() const override { return "Compose"; }
377
378 std::unique_ptr<GrFragmentProcessor> clone() const override {
379 return std::unique_ptr<GrFragmentProcessor>(new ComposeProcessor(*this));
380 }
381
382 private:
383 std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override {
384 class Impl : public ProgramImpl {
385 public:
386 void emitCode(EmitArgs& args) override {
387 SkString result = this->invokeChild(1, args); // g(x)
388 result = this->invokeChild(0, result.c_str(), args); // f(g(x))
389 args.fFragBuilder->codeAppendf("return %s;", result.c_str());
390 }
391 };
392 return std::make_unique<Impl>();
393 }
394
395 ComposeProcessor(std::unique_ptr<GrFragmentProcessor> f,
396 std::unique_ptr<GrFragmentProcessor> g)
398 f->optimizationFlags() & g->optimizationFlags()) {
399 this->registerChild(std::move(f));
400 this->registerChild(std::move(g));
401 }
402
403 ComposeProcessor(const ComposeProcessor& that) : INHERITED(that) {}
404
405 void onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const override {}
406
407 bool onIsEqual(const GrFragmentProcessor&) const override { return true; }
408
409 SkPMColor4f constantOutputForConstantInput(const SkPMColor4f& inColor) const override {
410 SkPMColor4f color = inColor;
413 return color;
414 }
415
417 };
418
419 // Allow either of the composed functions to be null.
420 if (f == nullptr) {
421 return g;
422 }
423 if (g == nullptr) {
424 return f;
425 }
426
427 // Run an optimization pass on this composition.
428 GrProcessorAnalysisColor inputColor;
429 inputColor.setToUnknown();
430
431 std::unique_ptr<GrFragmentProcessor> series[2] = {std::move(g), std::move(f)};
432 GrColorFragmentProcessorAnalysis info(inputColor, series, std::size(series));
433
434 SkPMColor4f knownColor;
435 int leadingFPsToEliminate = info.initialProcessorsToEliminate(&knownColor);
436 switch (leadingFPsToEliminate) {
437 default:
438 // We shouldn't eliminate more than we started with.
439 SkASSERT(leadingFPsToEliminate <= 2);
440 [[fallthrough]];
441 case 0:
442 // Compose the two processors as requested.
443 return ComposeProcessor::Make(/*f=*/std::move(series[1]), /*g=*/std::move(series[0]));
444 case 1:
445 // Replace the first processor with a constant color.
446 return ComposeProcessor::Make(/*f=*/std::move(series[1]),
447 /*g=*/MakeColor(knownColor));
448 case 2:
449 // Replace the entire composition with a constant color.
450 return MakeColor(knownColor);
451 }
452}
453
454//////////////////////////////////////////////////////////////////////////////
455
456std::unique_ptr<GrFragmentProcessor> GrFragmentProcessor::ColorMatrix(
457 std::unique_ptr<GrFragmentProcessor> child,
458 const float matrix[20],
459 bool unpremulInput,
460 bool clampRGBOutput,
461 bool premulOutput) {
463 "uniform half4x4 m;"
464 "uniform half4 v;"
465 "uniform int unpremulInput;" // always specialized
466 "uniform int clampRGBOutput;" // always specialized
467 "uniform int premulOutput;" // always specialized
468 "half4 main(half4 color) {"
469 "if (bool(unpremulInput)) {"
470 "color = unpremul(color);"
471 "}"
472 "color = m * color + v;"
473 "if (bool(clampRGBOutput)) {"
474 "color = saturate(color);"
475 "} else {"
476 "color.a = saturate(color.a);"
477 "}"
478 "if (bool(premulOutput)) {"
479 "color.rgb *= color.a;"
480 "}"
481 "return color;"
482 "}"
483 );
485
486 SkM44 m44(matrix[ 0], matrix[ 1], matrix[ 2], matrix[ 3],
487 matrix[ 5], matrix[ 6], matrix[ 7], matrix[ 8],
488 matrix[10], matrix[11], matrix[12], matrix[13],
489 matrix[15], matrix[16], matrix[17], matrix[18]);
490 SkV4 v4 = {matrix[4], matrix[9], matrix[14], matrix[19]};
491 return GrSkSLFP::Make(effect, "ColorMatrix", std::move(child), GrSkSLFP::OptFlags::kNone,
492 "m", m44,
493 "v", v4,
494 "unpremulInput", GrSkSLFP::Specialize(unpremulInput ? 1 : 0),
495 "clampRGBOutput", GrSkSLFP::Specialize(clampRGBOutput ? 1 : 0),
496 "premulOutput", GrSkSLFP::Specialize(premulOutput ? 1 : 0));
497}
498
499//////////////////////////////////////////////////////////////////////////////
500
501std::unique_ptr<GrFragmentProcessor> GrFragmentProcessor::SurfaceColor() {
502 class SurfaceColorProcessor : public GrFragmentProcessor {
503 public:
504 static std::unique_ptr<GrFragmentProcessor> Make() {
505 return std::unique_ptr<GrFragmentProcessor>(new SurfaceColorProcessor());
506 }
507
508 std::unique_ptr<GrFragmentProcessor> clone() const override { return Make(); }
509
510 const char* name() const override { return "SurfaceColor"; }
511
512 private:
513 std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override {
514 class Impl : public ProgramImpl {
515 public:
516 void emitCode(EmitArgs& args) override {
517 const char* dstColor = args.fFragBuilder->dstColor();
518 args.fFragBuilder->codeAppendf("return %s;", dstColor);
519 }
520 };
521 return std::make_unique<Impl>();
522 }
523
524 SurfaceColorProcessor()
526 this->setWillReadDstColor();
527 }
528
529 void onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const override {}
530
531 bool onIsEqual(const GrFragmentProcessor&) const override { return true; }
532
534 };
535
537}
538
539//////////////////////////////////////////////////////////////////////////////
540
541std::unique_ptr<GrFragmentProcessor> GrFragmentProcessor::DeviceSpace(
542 std::unique_ptr<GrFragmentProcessor> fp) {
543 if (!fp) {
544 return nullptr;
545 }
546
548 public:
549 static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> fp) {
550 return std::unique_ptr<GrFragmentProcessor>(new DeviceSpace(std::move(fp)));
551 }
552
553 private:
554 DeviceSpace(std::unique_ptr<GrFragmentProcessor> fp)
555 : GrFragmentProcessor(kDeviceSpace_ClassID, fp->optimizationFlags()) {
556 // Passing FragCoord here is the reason this is a subclass and not a runtime-FP.
557 this->registerChild(std::move(fp), SkSL::SampleUsage::FragCoord());
558 }
559
560 std::unique_ptr<GrFragmentProcessor> clone() const override {
561 auto child = this->childProcessor(0)->clone();
562 return std::unique_ptr<GrFragmentProcessor>(new DeviceSpace(std::move(child)));
563 }
564
567 }
568
569 std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override {
570 class Impl : public ProgramImpl {
571 public:
572 Impl() = default;
573 void emitCode(ProgramImpl::EmitArgs& args) override {
574 auto child = this->invokeChild(0, args.fInputColor, args, "sk_FragCoord.xy");
575 args.fFragBuilder->codeAppendf("return %s;", child.c_str());
576 }
577 };
578 return std::make_unique<Impl>();
579 }
580
581 void onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const override {}
582
583 bool onIsEqual(const GrFragmentProcessor& processor) const override { return true; }
584
585 const char* name() const override { return "DeviceSpace"; }
586 };
587
588 return DeviceSpace::Make(std::move(fp));
589}
590
591//////////////////////////////////////////////////////////////////////////////
592
593#define CLIP_EDGE_SKSL \
594 "const int kFillBW = 0;" \
595 "const int kFillAA = 1;" \
596 "const int kInverseFillBW = 2;" \
597 "const int kInverseFillAA = 3;"
598
599static_assert(static_cast<int>(GrClipEdgeType::kFillBW) == 0);
600static_assert(static_cast<int>(GrClipEdgeType::kFillAA) == 1);
601static_assert(static_cast<int>(GrClipEdgeType::kInverseFillBW) == 2);
602static_assert(static_cast<int>(GrClipEdgeType::kInverseFillAA) == 3);
603
604std::unique_ptr<GrFragmentProcessor> GrFragmentProcessor::Rect(
605 std::unique_ptr<GrFragmentProcessor> inputFP, GrClipEdgeType edgeType, SkRect rect) {
608 "uniform int edgeType;" // GrClipEdgeType, specialized
609 "uniform float4 rectUniform;"
610
611 "half4 main(float2 xy) {"
612 "half coverage;"
613 "if (edgeType == kFillBW || edgeType == kInverseFillBW) {"
614 // non-AA
615 "coverage = half(all(greaterThan(float4(sk_FragCoord.xy, rectUniform.zw),"
616 "float4(rectUniform.xy, sk_FragCoord.xy))));"
617 "} else {"
618 // compute coverage relative to left and right edges, add, then subtract 1 to
619 // account for double counting. And similar for top/bottom.
620 "half4 dists4 = saturate(half4(1, 1, -1, -1) *"
621 "half4(sk_FragCoord.xyxy - rectUniform));"
622 "half2 dists2 = dists4.xy + dists4.zw - 1;"
623 "coverage = dists2.x * dists2.y;"
624 "}"
625
626 "if (edgeType == kInverseFillBW || edgeType == kInverseFillAA) {"
627 "coverage = 1.0 - coverage;"
628 "}"
629
630 "return half4(coverage);"
631 "}"
632 );
633
634 SkASSERT(rect.isSorted());
635 // The AA math in the shader evaluates to 0 at the uploaded coordinates, so outset by 0.5
636 // to interpolate from 0 at a half pixel inset and 1 at a half pixel outset of rect.
637 SkRect rectUniform = GrClipEdgeTypeIsAA(edgeType) ? rect.makeOutset(.5f, .5f) : rect;
638
639 auto rectFP = GrSkSLFP::Make(effect, "Rect", /*inputFP=*/nullptr,
641 "edgeType", GrSkSLFP::Specialize(static_cast<int>(edgeType)),
642 "rectUniform", rectUniform);
643 return GrBlendFragmentProcessor::Make<SkBlendMode::kModulate>(std::move(rectFP),
644 std::move(inputFP));
645}
646
647GrFPResult GrFragmentProcessor::Circle(std::unique_ptr<GrFragmentProcessor> inputFP,
648 GrClipEdgeType edgeType,
650 float radius) {
651 // A radius below half causes the implicit insetting done by this processor to become
652 // inverted. We could handle this case by making the processor code more complicated.
653 if (radius < .5f && GrClipEdgeTypeIsInverseFill(edgeType)) {
654 return GrFPFailure(std::move(inputFP));
655 }
656
659 "uniform int edgeType;" // GrClipEdgeType, specialized
660 // The circle uniform is (center.x, center.y, radius + 0.5, 1 / (radius + 0.5)) for regular
661 // fills and (..., radius - 0.5, 1 / (radius - 0.5)) for inverse fills.
662 "uniform float4 circle;"
663
664 "half4 main(float2 xy) {"
665 // TODO: Right now the distance to circle calculation is performed in a space normalized
666 // to the radius and then denormalized. This is to mitigate overflow on devices that
667 // don't have full float.
668 "half d;"
669 "if (edgeType == kInverseFillBW || edgeType == kInverseFillAA) {"
670 "d = half((length((circle.xy - sk_FragCoord.xy) * circle.w) - 1.0) * circle.z);"
671 "} else {"
672 "d = half((1.0 - length((circle.xy - sk_FragCoord.xy) * circle.w)) * circle.z);"
673 "}"
674 "return half4((edgeType == kFillAA || edgeType == kInverseFillAA)"
675 "? saturate(d)"
676 ": (d > 0.5 ? 1 : 0));"
677 "}"
678 );
679
680 SkScalar effectiveRadius = radius;
681 if (GrClipEdgeTypeIsInverseFill(edgeType)) {
682 effectiveRadius -= 0.5f;
683 // When the radius is 0.5 effectiveRadius is 0 which causes an inf * 0 in the shader.
684 effectiveRadius = std::max(0.001f, effectiveRadius);
685 } else {
686 effectiveRadius += 0.5f;
687 }
688 SkV4 circle = {center.fX, center.fY, effectiveRadius, SkScalarInvert(effectiveRadius)};
689
690 auto circleFP = GrSkSLFP::Make(effect, "Circle", /*inputFP=*/nullptr,
692 "edgeType", GrSkSLFP::Specialize(static_cast<int>(edgeType)),
693 "circle", circle);
694 return GrFPSuccess(GrBlendFragmentProcessor::Make<SkBlendMode::kModulate>(std::move(inputFP),
695 std::move(circleFP)));
696}
697
698GrFPResult GrFragmentProcessor::Ellipse(std::unique_ptr<GrFragmentProcessor> inputFP,
699 GrClipEdgeType edgeType,
701 SkPoint radii,
702 const GrShaderCaps& caps) {
703 const bool medPrecision = !caps.fFloatIs32Bits;
704
705 // Small radii produce bad results on devices without full float.
706 if (medPrecision && (radii.fX < 0.5f || radii.fY < 0.5f)) {
707 return GrFPFailure(std::move(inputFP));
708 }
709 // Very narrow ellipses produce bad results on devices without full float
710 if (medPrecision && (radii.fX > 255*radii.fY || radii.fY > 255*radii.fX)) {
711 return GrFPFailure(std::move(inputFP));
712 }
713 // Very large ellipses produce bad results on devices without full float
714 if (medPrecision && (radii.fX > 16384 || radii.fY > 16384)) {
715 return GrFPFailure(std::move(inputFP));
716 }
717
720 "uniform int edgeType;" // GrClipEdgeType, specialized
721 "uniform int medPrecision;" // !sk_Caps.floatIs32Bits, specialized
722
723 "uniform float4 ellipse;"
724 "uniform float2 scale;" // only for medPrecision
725
726 "half4 main(float2 xy) {"
727 // d is the offset to the ellipse center
728 "float2 d = sk_FragCoord.xy - ellipse.xy;"
729 // If we're on a device with a "real" mediump then we'll do the distance computation in
730 // a space that is normalized by the larger radius or 128, whichever is smaller. The
731 // scale uniform will be scale, 1/scale. The inverse squared radii uniform values are
732 // already in this normalized space. The center is not.
733 "if (bool(medPrecision)) {"
734 "d *= scale.y;"
735 "}"
736 "float2 Z = d * ellipse.zw;"
737 // implicit is the evaluation of (x/rx)^2 + (y/ry)^2 - 1.
738 "float implicit = dot(Z, d) - 1;"
739 // grad_dot is the squared length of the gradient of the implicit.
740 "float grad_dot = 4 * dot(Z, Z);"
741 // Avoid calling inversesqrt on zero.
742 "if (bool(medPrecision)) {"
743 "grad_dot = max(grad_dot, 6.1036e-5);"
744 "} else {"
745 "grad_dot = max(grad_dot, 1.1755e-38);"
746 "}"
747 "float approx_dist = implicit * inversesqrt(grad_dot);"
748 "if (bool(medPrecision)) {"
749 "approx_dist *= scale.x;"
750 "}"
751
752 "half alpha;"
753 "if (edgeType == kFillBW) {"
754 "alpha = approx_dist > 0.0 ? 0.0 : 1.0;"
755 "} else if (edgeType == kFillAA) {"
756 "alpha = saturate(0.5 - half(approx_dist));"
757 "} else if (edgeType == kInverseFillBW) {"
758 "alpha = approx_dist > 0.0 ? 1.0 : 0.0;"
759 "} else {" // edgeType == kInverseFillAA
760 "alpha = saturate(0.5 + half(approx_dist));"
761 "}"
762 "return half4(alpha);"
763 "}"
764 );
765
766 float invRXSqd;
767 float invRYSqd;
768 SkV2 scale = {1, 1};
769 // If we're using a scale factor to work around precision issues, choose the larger radius as
770 // the scale factor. The inv radii need to be pre-adjusted by the scale factor.
771 if (medPrecision) {
772 if (radii.fX > radii.fY) {
773 invRXSqd = 1.f;
774 invRYSqd = (radii.fX * radii.fX) / (radii.fY * radii.fY);
775 scale = {radii.fX, 1.f / radii.fX};
776 } else {
777 invRXSqd = (radii.fY * radii.fY) / (radii.fX * radii.fX);
778 invRYSqd = 1.f;
779 scale = {radii.fY, 1.f / radii.fY};
780 }
781 } else {
782 invRXSqd = 1.f / (radii.fX * radii.fX);
783 invRYSqd = 1.f / (radii.fY * radii.fY);
784 }
785 SkV4 ellipse = {center.fX, center.fY, invRXSqd, invRYSqd};
786
787 auto ellipseFP = GrSkSLFP::Make(effect, "Ellipse", /*inputFP=*/nullptr,
789 "edgeType", GrSkSLFP::Specialize(static_cast<int>(edgeType)),
790 "medPrecision", GrSkSLFP::Specialize<int>(medPrecision),
791 "ellipse", ellipse,
792 "scale", scale);
793 return GrFPSuccess(GrBlendFragmentProcessor::Make<SkBlendMode::kModulate>(std::move(ellipseFP),
794 std::move(inputFP)));
795}
796
797//////////////////////////////////////////////////////////////////////////////
798
799std::unique_ptr<GrFragmentProcessor> GrFragmentProcessor::HighPrecision(
800 std::unique_ptr<GrFragmentProcessor> fp) {
801 class HighPrecisionFragmentProcessor : public GrFragmentProcessor {
802 public:
803 static std::unique_ptr<GrFragmentProcessor> Make(std::unique_ptr<GrFragmentProcessor> fp) {
804 return std::unique_ptr<GrFragmentProcessor>(
805 new HighPrecisionFragmentProcessor(std::move(fp)));
806 }
807
808 const char* name() const override { return "HighPrecision"; }
809
810 std::unique_ptr<GrFragmentProcessor> clone() const override {
811 return Make(this->childProcessor(0)->clone());
812 }
813
814 private:
815 HighPrecisionFragmentProcessor(std::unique_ptr<GrFragmentProcessor> fp)
818 this->registerChild(std::move(fp));
819 }
820
821 std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override {
822 class Impl : public ProgramImpl {
823 public:
824 void emitCode(EmitArgs& args) override {
825 SkString childColor = this->invokeChild(0, args);
826
827 args.fFragBuilder->forceHighPrecision();
828 args.fFragBuilder->codeAppendf("return %s;", childColor.c_str());
829 }
830 };
831 return std::make_unique<Impl>();
832 }
833
834 void onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const override {}
835 bool onIsEqual(const GrFragmentProcessor& other) const override { return true; }
836
837 SkPMColor4f constantOutputForConstantInput(const SkPMColor4f& input) const override {
838 return ConstantOutputForConstantInput(this->childProcessor(0), input);
839 }
840
842 };
843
844 return HighPrecisionFragmentProcessor::Make(std::move(fp));
845}
846
847//////////////////////////////////////////////////////////////////////////////
848
850
852 const GrFragmentProcessor& processor) {
853 this->onSetData(pdman, processor);
854}
855
857 const char* inputColor,
858 const char* destColor,
859 EmitArgs& args,
860 std::string_view skslCoords) {
861 SkASSERT(childIndex >= 0);
862
863 if (!inputColor) {
864 inputColor = args.fInputColor;
865 }
866
867 const GrFragmentProcessor* childProc = args.fFp.childProcessor(childIndex);
868 if (!childProc) {
869 // If no child processor is provided, return the input color as-is.
870 return SkString(inputColor);
871 }
872
873 auto invocation = SkStringPrintf("%s(%s", this->childProcessor(childIndex)->functionName(),
874 inputColor);
875
876 if (childProc->isBlendFunction()) {
877 if (!destColor) {
878 destColor = args.fFp.isBlendFunction() ? args.fDestColor : "half4(1)";
879 }
880 invocation.appendf(", %s", destColor);
881 }
882
883 // Assert that the child has no sample matrix. A uniform matrix sample call would go through
884 // invokeChildWithMatrix, not here.
885 SkASSERT(!childProc->sampleUsage().isUniformMatrix());
886
887 if (args.fFragBuilder->getProgramBuilder()->fragmentProcessorHasCoordsParam(childProc)) {
888 SkASSERT(!childProc->sampleUsage().isFragCoord() || skslCoords == "sk_FragCoord.xy");
889 // The child's function takes a half4 color and a float2 coordinate
890 if (!skslCoords.empty()) {
891 invocation.appendf(", %.*s", (int)skslCoords.size(), skslCoords.data());
892 } else {
893 invocation.appendf(", %s", args.fSampleCoord);
894 }
895 }
896
897 invocation.append(")");
898 return invocation;
899}
900
902 const char* inputColor,
903 const char* destColor,
904 EmitArgs& args) {
905 SkASSERT(childIndex >= 0);
906
907 if (!inputColor) {
908 inputColor = args.fInputColor;
909 }
910
911 const GrFragmentProcessor* childProc = args.fFp.childProcessor(childIndex);
912 if (!childProc) {
913 // If no child processor is provided, return the input color as-is.
914 return SkString(inputColor);
915 }
916
917 SkASSERT(childProc->sampleUsage().isUniformMatrix());
918
919 // Every uniform matrix has the same (initial) name. Resolve that into the mangled name:
920 GrShaderVar uniform = args.fUniformHandler->getUniformMapping(
923 const SkString& matrixName(uniform.getName());
924
925 auto invocation = SkStringPrintf("%s(%s", this->childProcessor(childIndex)->functionName(),
926 inputColor);
927
928 if (childProc->isBlendFunction()) {
929 if (!destColor) {
930 destColor = args.fFp.isBlendFunction() ? args.fDestColor : "half4(1)";
931 }
932 invocation.appendf(", %s", destColor);
933 }
934
935 // Produce a string containing the call to the helper function. We have a uniform variable
936 // containing our transform (matrixName). If the parent coords were produced by uniform
937 // transforms, then the entire expression (matrixName * coords) is lifted to a vertex shader
938 // and is stored in a varying. In that case, childProc will not be sampled explicitly, so its
939 // function signature will not take in coords.
940 //
941 // In all other cases, we need to insert sksl to compute matrix * parent coords and then invoke
942 // the function.
943 if (args.fFragBuilder->getProgramBuilder()->fragmentProcessorHasCoordsParam(childProc)) {
944 // Only check perspective for this specific matrix transform, not the aggregate FP property.
945 // Any parent perspective will have already been applied when evaluated in the FS.
946 if (childProc->sampleUsage().hasPerspective()) {
947 invocation.appendf(", proj((%s) * %s.xy1)", matrixName.c_str(), args.fSampleCoord);
948 } else if (args.fShaderCaps->fNonsquareMatrixSupport) {
949 invocation.appendf(", float3x2(%s) * %s.xy1", matrixName.c_str(), args.fSampleCoord);
950 } else {
951 invocation.appendf(", ((%s) * %s.xy1).xy", matrixName.c_str(), args.fSampleCoord);
952 }
953 }
954
955 invocation.append(")");
956 return invocation;
957}
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
#define CLIP_EDGE_SKSL
GrFragmentProcessor::ProgramImpl ProgramImpl
static GrFPResult GrFPSuccess(std::unique_ptr< GrFragmentProcessor > fp)
std::tuple< bool, std::unique_ptr< GrFragmentProcessor > > GrFPResult
static GrFPResult GrFPFailure(std::unique_ptr< GrFragmentProcessor > fp)
std::function< void(GrSurfaceProxy *, skgpu::Mipmapped)> GrVisitProxyFunc
Definition: GrTypesPriv.h:943
static constexpr bool GrClipEdgeTypeIsInverseFill(const GrClipEdgeType edgeType)
Definition: GrTypesPriv.h:375
GrClipEdgeType
Definition: GrTypesPriv.h:361
static constexpr bool GrClipEdgeTypeIsAA(const GrClipEdgeType edgeType)
Definition: GrTypesPriv.h:380
#define SkASSERT(cond)
Definition: SkAssert.h:116
static SkV4 v4(SkV3 v, SkScalar w)
Definition: SkM44.cpp:329
SkRuntimeEffect * SkMakeRuntimeEffect(SkRuntimeEffect::Result(*make)(SkString, const SkRuntimeEffect::Options &), const char *sksl, SkRuntimeEffect::Options options=SkRuntimeEffect::Options{})
#define SkScalarInvert(x)
Definition: SkScalar.h:73
SK_API SkString SkStringPrintf(const char *format,...) SK_PRINTF_LIKE(1
Creates a new string and writes into it using a printf()-style format.
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
static SkScalar center(float pos0, float pos1)
SkString invokeChildWithMatrix(int childIndex, EmitArgs &parentArgs)
void setData(const GrGLSLProgramDataManager &pdman, const GrFragmentProcessor &processor)
virtual void onSetData(const GrGLSLProgramDataManager &, const GrFragmentProcessor &)
SkString invokeChild(int childIndex, EmitArgs &parentArgs, std::string_view skslCoords={})
ProgramImpl * childProcessor(int index) const
void cloneAndRegisterAllChildProcessors(const GrFragmentProcessor &src)
virtual SkPMColor4f constantOutputForConstantInput(const SkPMColor4f &) const
static std::unique_ptr< GrFragmentProcessor > SwizzleOutput(std::unique_ptr< GrFragmentProcessor >, const skgpu::Swizzle &)
static std::unique_ptr< GrFragmentProcessor > ClampOutput(std::unique_ptr< GrFragmentProcessor >)
static GrFPResult Circle(std::unique_ptr< GrFragmentProcessor >, GrClipEdgeType, SkPoint center, float radius)
GrFragmentProcessor(ClassID classID, OptimizationFlags optimizationFlags)
virtual std::unique_ptr< GrFragmentProcessor > clone() const =0
int numNonNullChildProcessors() const
static OptimizationFlags ProcessorOptimizationFlags(const GrFragmentProcessor *fp)
static std::unique_ptr< GrFragmentProcessor > MakeColor(SkPMColor4f color)
std::unique_ptr< ProgramImpl > makeProgramImpl() const
static std::unique_ptr< GrFragmentProcessor > ModulateRGBA(std::unique_ptr< GrFragmentProcessor > child, const SkPMColor4f &color)
static std::unique_ptr< GrFragmentProcessor > DeviceSpace(std::unique_ptr< GrFragmentProcessor >)
static std::unique_ptr< GrFragmentProcessor > OverrideInput(std::unique_ptr< GrFragmentProcessor >, const SkPMColor4f &)
static std::unique_ptr< GrFragmentProcessor > SurfaceColor()
GrTextureEffect * asTextureEffect()
bool isEqual(const GrFragmentProcessor &that) const
GrFragmentProcessor * childProcessor(int index)
void visitProxies(const GrVisitProxyFunc &) const
void visitWithImpls(const std::function< void(const GrFragmentProcessor &, ProgramImpl &)> &, ProgramImpl &) const
void registerChild(std::unique_ptr< GrFragmentProcessor > child, SkSL::SampleUsage sampleUsage=SkSL::SampleUsage::PassThrough())
static std::unique_ptr< GrFragmentProcessor > Rect(std::unique_ptr< GrFragmentProcessor >, GrClipEdgeType, SkRect)
static std::unique_ptr< GrFragmentProcessor > ApplyPaintAlpha(std::unique_ptr< GrFragmentProcessor > child)
static std::unique_ptr< GrFragmentProcessor > MulInputByChildAlpha(std::unique_ptr< GrFragmentProcessor > child)
const SkSL::SampleUsage & sampleUsage() const
virtual void onAddToKey(const GrShaderCaps &, skgpu::KeyBuilder *) const =0
static std::unique_ptr< GrFragmentProcessor > HighPrecision(std::unique_ptr< GrFragmentProcessor >)
static std::unique_ptr< GrFragmentProcessor > DestColor()
static std::unique_ptr< GrFragmentProcessor > ColorMatrix(std::unique_ptr< GrFragmentProcessor > child, const float matrix[20], bool unpremulInput, bool clampRGBOutput, bool premulOutput)
static std::unique_ptr< GrFragmentProcessor > DisableCoverageAsAlpha(std::unique_ptr< GrFragmentProcessor >)
static std::unique_ptr< GrFragmentProcessor > Compose(std::unique_ptr< GrFragmentProcessor > f, std::unique_ptr< GrFragmentProcessor > g)
static SkPMColor4f ConstantOutputForConstantInput(const GrFragmentProcessor *fp, const SkPMColor4f &input)
virtual bool onIsEqual(const GrFragmentProcessor &) const =0
static GrFPResult Ellipse(std::unique_ptr< GrFragmentProcessor >, GrClipEdgeType, SkPoint center, SkPoint radii, const GrShaderCaps &)
void visitTextureEffects(const std::function< void(const GrTextureEffect &)> &) const
virtual std::unique_ptr< ProgramImpl > onMakeProgramImpl() const =0
void codeAppendf(const char format[],...) SK_PRINTF_LIKE(2
ClassID classID() const
Definition: GrProcessor.h:129
const T & cast() const
Definition: GrProcessor.h:127
virtual const char * name() const =0
@ kDeviceSpace_ClassID
Definition: GrProcessor.h:41
@ kSurfaceColorProcessor_ClassID
Definition: GrProcessor.h:83
@ kSeriesFragmentProcessor_ClassID
Definition: GrProcessor.h:81
@ kHighPrecisionFragmentProcessor_ClassID
Definition: GrProcessor.h:74
@ kGrTextureEffect_ClassID
Definition: GrProcessor.h:71
@ kSwizzleFragmentProcessor_ClassID
Definition: GrProcessor.h:84
constexpr skgpu::Mipmapped mipmapped() const
SkSLType getType() const
Definition: GrShaderVar.h:97
const SkString & getName() const
Definition: GrShaderVar.h:91
static std::unique_ptr< GrSkSLFP > Make(const SkRuntimeEffect *effect, const char *name, std::unique_ptr< GrFragmentProcessor > inputFP, OptFlags optFlags, Args &&... args)
Definition: GrSkSLFP.h:159
static GrSpecializedUniform< T > Specialize(const T &value)
Definition: GrSkSLFP.h:77
GrSurfaceProxy * proxy() const
const GrSurfaceProxyView & view() const
GrTexture * texture() const
GrSamplerState samplerState() const
Definition: SkM44.h:150
static bool SupportsConstantOutputForConstantInput(const SkRuntimeEffect *effect)
static Result MakeForColorFilter(SkString sksl, const Options &)
static Result MakeForBlender(SkString sksl, const Options &)
static Result MakeForShader(SkString sksl, const Options &)
bool isPassThrough() const
bool isSampled() const
bool isUniformMatrix() const
static const char * MatrixUniformName()
bool isFragCoord() const
bool hasPerspective() const
static SampleUsage FragCoord()
void append(const char text[])
Definition: SkString.h:203
const char * c_str() const
Definition: SkString.h:133
static constexpr Swizzle RGBA()
Definition: Swizzle.h:66
int size() const
Definition: SkTArray.h:421
DlColor color
float SkScalar
Definition: extension.cpp:12
static bool b
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
GAsyncResult * result
Dart_NativeFunction function
Definition: fuchsia.cc:51
static float max(float r, float g, float b)
Definition: hsl.cpp:49
std::u16string text
SK_API sk_sp< SkDocument > Make(SkWStream *dst, const SkSerialProcs *=nullptr, std::function< void(const SkPicture *)> onEndPage=nullptr)
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
const uint32_t fp
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
const Scalar scale
float fX
x-axis value
Definition: SkPoint_impl.h:164
float fY
y-axis value
Definition: SkPoint_impl.h:165
bool fFloatIs32Bits
Definition: SkSLUtil.h:100
Definition: SkM44.h:19
Definition: SkM44.h:98