xref: /aosp_15_r20/external/skia/src/gpu/graphite/PaintParams.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2022 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 "src/gpu/graphite/PaintParams.h"
9 
10 #include "include/core/SkColorSpace.h"
11 #include "include/core/SkShader.h"
12 #include "src/core/SkBlendModeBlender.h"
13 #include "src/core/SkBlenderBase.h"
14 #include "src/core/SkColorSpacePriv.h"
15 #include "src/effects/colorfilters/SkColorFilterBase.h"
16 #include "src/gpu/Blend.h"
17 #include "src/gpu/DitherUtils.h"
18 #include "src/gpu/graphite/KeyContext.h"
19 #include "src/gpu/graphite/KeyHelpers.h"
20 #include "src/gpu/graphite/Log.h"
21 #include "src/gpu/graphite/PaintParamsKey.h"
22 #include "src/gpu/graphite/PipelineData.h"
23 #include "src/gpu/graphite/RecorderPriv.h"
24 #include "src/gpu/graphite/Uniform.h"
25 #include "src/shaders/SkShaderBase.h"
26 
27 namespace skgpu::graphite {
28 
29 namespace {
30 
31 // This should be kept in sync w/ SkPaintPriv::ShouldDither and PaintOption::shouldDither
should_dither(const PaintParams & p,SkColorType dstCT)32 bool should_dither(const PaintParams& p, SkColorType dstCT) {
33     // The paint dither flag can veto.
34     if (!p.dither()) {
35         return false;
36     }
37 
38     if (dstCT == kUnknown_SkColorType) {
39         return false;
40     }
41 
42     // We always dither 565 or 4444 when requested.
43     if (dstCT == kRGB_565_SkColorType || dstCT == kARGB_4444_SkColorType) {
44         return true;
45     }
46 
47     // Otherwise, dither is only needed for non-const paints.
48     return p.shader() && !as_SB(p.shader())->isConstant();
49 }
50 
51 } // anonymous namespace
52 
PaintParams(const SkPaint & paint,sk_sp<SkBlender> primitiveBlender,const CircularRRectClip & analyticClip,sk_sp<SkShader> clipShader,DstReadRequirement dstReadReq,bool skipColorXform)53 PaintParams::PaintParams(const SkPaint& paint,
54                          sk_sp<SkBlender> primitiveBlender,
55                          const CircularRRectClip& analyticClip,
56                          sk_sp<SkShader> clipShader,
57                          DstReadRequirement dstReadReq,
58                          bool skipColorXform)
59         : fColor(paint.getColor4f())
60         , fFinalBlender(paint.refBlender())
61         , fShader(paint.refShader())
62         , fColorFilter(paint.refColorFilter())
63         , fPrimitiveBlender(std::move(primitiveBlender))
64         , fAnalyticClip(analyticClip)
65         , fClipShader(std::move(clipShader))
66         , fDstReadReq(dstReadReq)
67         , fSkipColorXform(skipColorXform)
68         , fDither(paint.isDither()) {}
69 
70 PaintParams::PaintParams(const PaintParams& other) = default;
71 PaintParams::~PaintParams() = default;
72 PaintParams& PaintParams::operator=(const PaintParams& other) = default;
73 
asFinalBlendMode() const74 std::optional<SkBlendMode> PaintParams::asFinalBlendMode() const {
75     return fFinalBlender ? as_BB(fFinalBlender)->asBlendMode()
76                          : SkBlendMode::kSrcOver;
77 }
78 
refFinalBlender() const79 sk_sp<SkBlender> PaintParams::refFinalBlender() const { return fFinalBlender; }
80 
refShader() const81 sk_sp<SkShader> PaintParams::refShader() const { return fShader; }
82 
refColorFilter() const83 sk_sp<SkColorFilter> PaintParams::refColorFilter() const { return fColorFilter; }
84 
refPrimitiveBlender() const85 sk_sp<SkBlender> PaintParams::refPrimitiveBlender() const { return fPrimitiveBlender; }
86 
Color4fPrepForDst(SkColor4f srcColor,const SkColorInfo & dstColorInfo)87 SkColor4f PaintParams::Color4fPrepForDst(SkColor4f srcColor, const SkColorInfo& dstColorInfo) {
88     // xform from sRGB to the destination colorspace
89     SkColorSpaceXformSteps steps(sk_srgb_singleton(),       kUnpremul_SkAlphaType,
90                                  dstColorInfo.colorSpace(), kUnpremul_SkAlphaType);
91 
92     SkColor4f result = srcColor;
93     steps.apply(result.vec());
94     return result;
95 }
96 
Blend(const KeyContext & keyContext,PaintParamsKeyBuilder * keyBuilder,PipelineDataGatherer * gatherer,AddToKeyFn addBlendToKey,AddToKeyFn addSrcToKey,AddToKeyFn addDstToKey)97 void Blend(const KeyContext& keyContext,
98            PaintParamsKeyBuilder* keyBuilder,
99            PipelineDataGatherer* gatherer,
100            AddToKeyFn addBlendToKey,
101            AddToKeyFn addSrcToKey,
102            AddToKeyFn addDstToKey) {
103     BlendComposeBlock::BeginBlock(keyContext, keyBuilder, gatherer);
104 
105         addSrcToKey();
106 
107         addDstToKey();
108 
109         addBlendToKey();
110 
111     keyBuilder->endBlock();  // BlendComposeBlock
112 }
113 
Compose(const KeyContext & keyContext,PaintParamsKeyBuilder * keyBuilder,PipelineDataGatherer * gatherer,AddToKeyFn addInnerToKey,AddToKeyFn addOuterToKey)114 void Compose(const KeyContext& keyContext,
115              PaintParamsKeyBuilder* keyBuilder,
116              PipelineDataGatherer* gatherer,
117              AddToKeyFn addInnerToKey,
118              AddToKeyFn addOuterToKey) {
119     ComposeBlock::BeginBlock(keyContext, keyBuilder, gatherer);
120 
121         addInnerToKey();
122 
123         addOuterToKey();
124 
125     keyBuilder->endBlock();  // ComposeBlock
126 }
127 
AddFixedBlendMode(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,SkBlendMode bm)128 void AddFixedBlendMode(const KeyContext& keyContext,
129                        PaintParamsKeyBuilder* builder,
130                        PipelineDataGatherer* gatherer,
131                        SkBlendMode bm) {
132     SkASSERT(bm <= SkBlendMode::kLastMode);
133     BuiltInCodeSnippetID id = static_cast<BuiltInCodeSnippetID>(kFixedBlendIDOffset +
134                                                                 static_cast<int>(bm));
135     builder->addBlock(id);
136 }
137 
AddBlendMode(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,SkBlendMode bm)138 void AddBlendMode(const KeyContext& keyContext,
139                   PaintParamsKeyBuilder* builder,
140                   PipelineDataGatherer* gatherer,
141                   SkBlendMode bm) {
142     // For non-fixed blends, coefficient blend modes are combined into the same shader snippet.
143     // The same goes for the HSLC advanced blends. The remaining advanced blends are fairly unique
144     // in their implementations. To avoid having to compile all of their SkSL, they are treated as
145     // fixed blend modes.
146     SkSpan<const float> coeffs = skgpu::GetPorterDuffBlendConstants(bm);
147     if (!coeffs.empty()) {
148         PorterDuffBlenderBlock::AddBlock(keyContext, builder, gatherer, coeffs);
149     } else if (bm >= SkBlendMode::kHue) {
150         ReducedBlendModeInfo blendInfo = GetReducedBlendModeInfo(bm);
151         HSLCBlenderBlock::AddBlock(keyContext, builder, gatherer, blendInfo.fUniformData);
152     } else {
153         AddFixedBlendMode(keyContext, builder, gatherer, bm);
154     }
155 }
156 
AddDitherBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,SkColorType ct)157 void AddDitherBlock(const KeyContext& keyContext,
158                     PaintParamsKeyBuilder* builder,
159                     PipelineDataGatherer* gatherer,
160                     SkColorType ct) {
161     static const SkBitmap gLUT = skgpu::MakeDitherLUT();
162 
163     sk_sp<TextureProxy> proxy = RecorderPriv::CreateCachedProxy(keyContext.recorder(), gLUT,
164                                                                 "DitherLUT");
165     if (keyContext.recorder() && !proxy) {
166         SKGPU_LOG_W("Couldn't create dither shader's LUT");
167         builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
168         return;
169     }
170 
171     DitherShaderBlock::DitherData data(skgpu::DitherRangeForConfig(ct), std::move(proxy));
172 
173     DitherShaderBlock::AddBlock(keyContext, builder, gatherer, data);
174 }
175 
addPaintColorToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * keyBuilder,PipelineDataGatherer * gatherer) const176 void PaintParams::addPaintColorToKey(const KeyContext& keyContext,
177                                      PaintParamsKeyBuilder* keyBuilder,
178                                      PipelineDataGatherer* gatherer) const {
179     if (fShader) {
180         AddToKey(keyContext, keyBuilder, gatherer, fShader.get());
181     } else {
182         RGBPaintColorBlock::AddBlock(keyContext, keyBuilder, gatherer);
183     }
184 }
185 
186 /**
187  * Primitive blend blocks are used to blend either the paint color or the output of another shader
188  * with a primitive color emitted by certain draw geometry calls (drawVertices, drawAtlas, etc.).
189  * Dst: primitiveColor Src: Paint color/shader output
190  */
handlePrimitiveColor(const KeyContext & keyContext,PaintParamsKeyBuilder * keyBuilder,PipelineDataGatherer * gatherer) const191 void PaintParams::handlePrimitiveColor(const KeyContext& keyContext,
192                                        PaintParamsKeyBuilder* keyBuilder,
193                                        PipelineDataGatherer* gatherer) const {
194     if (fPrimitiveBlender) {
195         Blend(keyContext, keyBuilder, gatherer,
196               /* addBlendToKey= */ [&] () -> void {
197                   AddToKey(keyContext, keyBuilder, gatherer, fPrimitiveBlender.get());
198               },
199               /* addSrcToKey= */ [&]() -> void {
200                   this->addPaintColorToKey(keyContext, keyBuilder, gatherer);
201               },
202               /* addDstToKey= */ [&]() -> void {
203                   PrimitiveColorBlock::AddBlock(keyContext, keyBuilder, gatherer);
204               });
205     } else {
206         this->addPaintColorToKey(keyContext, keyBuilder, gatherer);
207     }
208 }
209 
210 // Apply the paint's alpha value.
handlePaintAlpha(const KeyContext & keyContext,PaintParamsKeyBuilder * keyBuilder,PipelineDataGatherer * gatherer) const211 void PaintParams::handlePaintAlpha(const KeyContext& keyContext,
212                                    PaintParamsKeyBuilder* keyBuilder,
213                                    PipelineDataGatherer* gatherer) const {
214 
215     if (!fShader && !fPrimitiveBlender) {
216         // If there is no shader and no primitive blending the input to the colorFilter stage
217         // is just the premultiplied paint color.
218         SkPMColor4f paintColor = PaintParams::Color4fPrepForDst(fColor,
219                                                                 keyContext.dstColorInfo()).premul();
220         SolidColorShaderBlock::AddBlock(keyContext, keyBuilder, gatherer, paintColor);
221         return;
222     }
223 
224     if (fColor.fA != 1.0f) {
225         Blend(keyContext, keyBuilder, gatherer,
226               /* addBlendToKey= */ [&] () -> void {
227                   AddFixedBlendMode(keyContext, keyBuilder, gatherer, SkBlendMode::kSrcIn);
228               },
229               /* addSrcToKey= */ [&]() -> void {
230                   this->handlePrimitiveColor(keyContext, keyBuilder, gatherer);
231               },
232               /* addDstToKey= */ [&]() -> void {
233                   AlphaOnlyPaintColorBlock::AddBlock(keyContext, keyBuilder, gatherer);
234               });
235     } else {
236         this->handlePrimitiveColor(keyContext, keyBuilder, gatherer);
237     }
238 }
239 
handleColorFilter(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer) const240 void PaintParams::handleColorFilter(const KeyContext& keyContext,
241                                     PaintParamsKeyBuilder* builder,
242                                     PipelineDataGatherer* gatherer) const {
243     if (fColorFilter) {
244         Compose(keyContext, builder, gatherer,
245                 /* addInnerToKey= */ [&]() -> void {
246                     this->handlePaintAlpha(keyContext, builder, gatherer);
247                 },
248                 /* addOuterToKey= */ [&]() -> void {
249                     AddToKey(keyContext, builder, gatherer, fColorFilter.get());
250                 });
251     } else {
252         this->handlePaintAlpha(keyContext, builder, gatherer);
253     }
254 }
255 
handleDithering(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer) const256 void PaintParams::handleDithering(const KeyContext& keyContext,
257                                   PaintParamsKeyBuilder* builder,
258                                   PipelineDataGatherer* gatherer) const {
259 
260 #ifndef SK_IGNORE_GPU_DITHER
261     SkColorType ct = keyContext.dstColorInfo().colorType();
262     if (should_dither(*this, ct)) {
263         Compose(keyContext, builder, gatherer,
264                 /* addInnerToKey= */ [&]() -> void {
265                     this->handleColorFilter(keyContext, builder, gatherer);
266                 },
267                 /* addOuterToKey= */ [&]() -> void {
268                     AddDitherBlock(keyContext, builder, gatherer, ct);
269                 });
270     } else
271 #endif
272     {
273         this->handleColorFilter(keyContext, builder, gatherer);
274     }
275 }
276 
handleClipping(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer) const277 void PaintParams::handleClipping(const KeyContext& keyContext,
278                                  PaintParamsKeyBuilder* builder,
279                                  PipelineDataGatherer* gatherer) const {
280     if (!fAnalyticClip.isEmpty()) {
281         float radius = fAnalyticClip.fRadius + 0.5f;
282         // N.B.: Because the clip data is normally used with depth-based clipping,
283         // the shape is inverted from its usual state. We re-invert here to
284         // match what the shader snippet expects.
285         SkPoint radiusPair = {(fAnalyticClip.fInverted) ? radius : -radius, 1.0f/radius};
286         CircularRRectClipBlock::CircularRRectClipData data(
287                 fAnalyticClip.fBounds.makeOutset(0.5f).asSkRect(),
288                 radiusPair,
289                 fAnalyticClip.edgeSelectRect());
290         if (fClipShader) {
291             // For both an analytic clip and clip shader, we need to compose them together into
292             // a single clipping root node.
293             Blend(keyContext, builder, gatherer,
294                   /* addBlendToKey= */ [&]() -> void {
295                       AddFixedBlendMode(keyContext, builder, gatherer, SkBlendMode::kModulate);
296                   },
297                   /* addSrcToKey= */ [&]() -> void {
298                       CircularRRectClipBlock::AddBlock(keyContext, builder, gatherer, data);
299                   },
300                   /* addDstToKey= */ [&]() -> void {
301                       AddToKey(keyContext, builder, gatherer, fClipShader.get());
302                   });
303         } else {
304             // Without a clip shader, the analytic clip can be the clipping root node.
305             CircularRRectClipBlock::AddBlock(keyContext, builder, gatherer, data);
306         }
307     } else if (fClipShader) {
308         // Since there's no analytic clip, the clipping root node can be fClipShader directly.
309         AddToKey(keyContext, builder, gatherer, fClipShader.get());
310     }
311 }
312 
toKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer) const313 void PaintParams::toKey(const KeyContext& keyContext,
314                         PaintParamsKeyBuilder* builder,
315                         PipelineDataGatherer* gatherer) const {
316     // Root Node 0 is the source color, which is the output of all effects post dithering
317     this->handleDithering(keyContext, builder, gatherer);
318 
319     // Root Node 1 is the final blender
320     std::optional<SkBlendMode> finalBlendMode = this->asFinalBlendMode();
321     if (finalBlendMode) {
322         if (fDstReadReq == DstReadRequirement::kNone) {
323             // With no shader blending, be as explicit as possible about the final blend
324             AddFixedBlendMode(keyContext, builder, gatherer, *finalBlendMode);
325         } else {
326             // With shader blending, use AddBlendMode() to select the more universal blend functions
327             // when possible. Technically we could always use a fixed blend mode but would then
328             // over-generate when encountering certain classes of blends. This is most problematic
329             // on devices that wouldn't support dual-source blending, so help them out by at least
330             // not requiring lots of pipelines.
331             AddBlendMode(keyContext, builder, gatherer, *finalBlendMode);
332         }
333     } else {
334         AddToKey(keyContext, builder, gatherer, fFinalBlender.get());
335     }
336 
337     // Optional Root Node 2 is the clip
338     this->handleClipping(keyContext, builder, gatherer);
339 }
340 
341 // TODO(b/330864257): Can be deleted once keys are determined by the Device draw.
notifyImagesInUse(Recorder * recorder,DrawContext * drawContext) const342 void PaintParams::notifyImagesInUse(Recorder* recorder,
343                                     DrawContext* drawContext) const {
344     if (fShader) {
345         NotifyImagesInUse(recorder, drawContext, fShader.get());
346     }
347     if (fPrimitiveBlender) {
348         NotifyImagesInUse(recorder, drawContext, fPrimitiveBlender.get());
349     }
350     if (fColorFilter) {
351         NotifyImagesInUse(recorder, drawContext, fColorFilter.get());
352     }
353     if (fFinalBlender) {
354         NotifyImagesInUse(recorder, drawContext, fFinalBlender.get());
355     }
356     if (fClipShader) {
357         NotifyImagesInUse(recorder, drawContext, fClipShader.get());
358     }
359 }
360 
361 } // namespace skgpu::graphite
362