• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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,sk_sp<SkShader> clipShader,DstReadRequirement dstReadReq,bool skipColorXform)53 PaintParams::PaintParams(const SkPaint& paint,
54                          sk_sp<SkBlender> primitiveBlender,
55                          sk_sp<SkShader> clipShader,
56                          DstReadRequirement dstReadReq,
57                          bool skipColorXform)
58         : fColor(paint.getColor4f())
59         , fFinalBlender(paint.refBlender())
60         , fShader(paint.refShader())
61         , fColorFilter(paint.refColorFilter())
62         , fPrimitiveBlender(std::move(primitiveBlender))
63         , fClipShader(std::move(clipShader))
64         , fDstReadReq(dstReadReq)
65         , fSkipColorXform(skipColorXform)
66         , fDither(paint.isDither()) {}
67 
68 PaintParams::PaintParams(const PaintParams& other) = default;
69 PaintParams::~PaintParams() = default;
70 PaintParams& PaintParams::operator=(const PaintParams& other) = default;
71 
asFinalBlendMode() const72 std::optional<SkBlendMode> PaintParams::asFinalBlendMode() const {
73     return fFinalBlender ? as_BB(fFinalBlender)->asBlendMode()
74                          : SkBlendMode::kSrcOver;
75 }
76 
refFinalBlender() const77 sk_sp<SkBlender> PaintParams::refFinalBlender() const { return fFinalBlender; }
78 
refShader() const79 sk_sp<SkShader> PaintParams::refShader() const { return fShader; }
80 
refColorFilter() const81 sk_sp<SkColorFilter> PaintParams::refColorFilter() const { return fColorFilter; }
82 
refPrimitiveBlender() const83 sk_sp<SkBlender> PaintParams::refPrimitiveBlender() const { return fPrimitiveBlender; }
84 
Color4fPrepForDst(SkColor4f srcColor,const SkColorInfo & dstColorInfo)85 SkColor4f PaintParams::Color4fPrepForDst(SkColor4f srcColor, const SkColorInfo& dstColorInfo) {
86     // xform from sRGB to the destination colorspace
87     SkColorSpaceXformSteps steps(sk_srgb_singleton(),       kUnpremul_SkAlphaType,
88                                  dstColorInfo.colorSpace(), kUnpremul_SkAlphaType);
89 
90     SkColor4f result = srcColor;
91     steps.apply(result.vec());
92     return result;
93 }
94 
Blend(const KeyContext & keyContext,PaintParamsKeyBuilder * keyBuilder,PipelineDataGatherer * gatherer,AddToKeyFn addBlendToKey,AddToKeyFn addSrcToKey,AddToKeyFn addDstToKey)95 void Blend(const KeyContext& keyContext,
96            PaintParamsKeyBuilder* keyBuilder,
97            PipelineDataGatherer* gatherer,
98            AddToKeyFn addBlendToKey,
99            AddToKeyFn addSrcToKey,
100            AddToKeyFn addDstToKey) {
101     BlendShaderBlock::BeginBlock(keyContext, keyBuilder, gatherer);
102 
103         addSrcToKey();
104 
105         addDstToKey();
106 
107         addBlendToKey();
108 
109     keyBuilder->endBlock();  // BlendShaderBlock
110 }
111 
Compose(const KeyContext & keyContext,PaintParamsKeyBuilder * keyBuilder,PipelineDataGatherer * gatherer,AddToKeyFn addInnerToKey,AddToKeyFn addOuterToKey)112 void Compose(const KeyContext& keyContext,
113              PaintParamsKeyBuilder* keyBuilder,
114              PipelineDataGatherer* gatherer,
115              AddToKeyFn addInnerToKey,
116              AddToKeyFn addOuterToKey) {
117     ComposeBlock::BeginBlock(keyContext, keyBuilder, gatherer);
118 
119         addInnerToKey();
120 
121         addOuterToKey();
122 
123     keyBuilder->endBlock();  // ComposeBlock
124 }
125 
AddKnownModeBlend(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,SkBlendMode bm)126 void AddKnownModeBlend(const KeyContext& keyContext,
127                        PaintParamsKeyBuilder* builder,
128                        PipelineDataGatherer* gatherer,
129                        SkBlendMode bm) {
130     SkASSERT(bm <= SkBlendMode::kLastCoeffMode);
131     BuiltInCodeSnippetID id = static_cast<BuiltInCodeSnippetID>(kFixedFunctionBlendModeIDOffset +
132                                                                 static_cast<int>(bm));
133     builder->addBlock(id);
134 }
135 
AddModeBlend(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,SkBlendMode bm)136 void AddModeBlend(const KeyContext& keyContext,
137                   PaintParamsKeyBuilder* builder,
138                   PipelineDataGatherer* gatherer,
139                   SkBlendMode bm) {
140     SkSpan<const float> coeffs = skgpu::GetPorterDuffBlendConstants(bm);
141     if (!coeffs.empty()) {
142         CoeffBlenderBlock::AddBlock(keyContext, builder, gatherer, coeffs);
143     } else {
144         BlendModeBlenderBlock::AddBlock(keyContext, builder, gatherer, bm);
145     }
146 }
147 
AddDstReadBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,DstReadRequirement dstReadReq)148 void AddDstReadBlock(const KeyContext& keyContext,
149                      PaintParamsKeyBuilder* builder,
150                      PipelineDataGatherer* gatherer,
151                      DstReadRequirement dstReadReq) {
152     switch(dstReadReq) {
153         case DstReadRequirement::kNone:
154             SkASSERT(false);            // This should never be reached
155             return;
156         case DstReadRequirement::kTextureCopy:
157             [[fallthrough]];
158         case DstReadRequirement::kTextureSample:
159             DstReadSampleBlock::AddBlock(keyContext, builder, gatherer, keyContext.dstTexture(),
160                                          keyContext.dstOffset());
161             break;
162         case DstReadRequirement::kFramebufferFetch:
163             builder->addBlock(BuiltInCodeSnippetID::kDstReadFetch);
164             break;
165     }
166 }
167 
AddDitherBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,SkColorType ct)168 void AddDitherBlock(const KeyContext& keyContext,
169                     PaintParamsKeyBuilder* builder,
170                     PipelineDataGatherer* gatherer,
171                     SkColorType ct) {
172     static const SkBitmap gLUT = skgpu::MakeDitherLUT();
173 
174     sk_sp<TextureProxy> proxy = RecorderPriv::CreateCachedProxy(keyContext.recorder(), gLUT,
175                                                                 "DitherLUT");
176     if (keyContext.recorder() && !proxy) {
177         SKGPU_LOG_W("Couldn't create dither shader's LUT");
178         builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
179         return;
180     }
181 
182     DitherShaderBlock::DitherData data(skgpu::DitherRangeForConfig(ct), std::move(proxy));
183 
184     DitherShaderBlock::AddBlock(keyContext, builder, gatherer, data);
185 }
186 
addPaintColorToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * keyBuilder,PipelineDataGatherer * gatherer) const187 void PaintParams::addPaintColorToKey(const KeyContext& keyContext,
188                                      PaintParamsKeyBuilder* keyBuilder,
189                                      PipelineDataGatherer* gatherer) const {
190     if (fShader) {
191         AddToKey(keyContext, keyBuilder, gatherer, fShader.get());
192     } else {
193         RGBPaintColorBlock::AddBlock(keyContext, keyBuilder, gatherer);
194     }
195 }
196 
197 /**
198  * Primitive blend blocks are used to blend either the paint color or the output of another shader
199  * with a primitive color emitted by certain draw geometry calls (drawVertices, drawAtlas, etc.).
200  * Dst: primitiveColor Src: Paint color/shader output
201  */
handlePrimitiveColor(const KeyContext & keyContext,PaintParamsKeyBuilder * keyBuilder,PipelineDataGatherer * gatherer) const202 void PaintParams::handlePrimitiveColor(const KeyContext& keyContext,
203                                        PaintParamsKeyBuilder* keyBuilder,
204                                        PipelineDataGatherer* gatherer) const {
205     if (fPrimitiveBlender) {
206         Blend(keyContext, keyBuilder, gatherer,
207               /* addBlendToKey= */ [&] () -> void {
208                   AddToKey(keyContext, keyBuilder, gatherer, fPrimitiveBlender.get());
209               },
210               /* addSrcToKey= */ [&]() -> void {
211                   this->addPaintColorToKey(keyContext, keyBuilder, gatherer);
212               },
213               /* addDstToKey= */ [&]() -> void {
214                   keyBuilder->addBlock(BuiltInCodeSnippetID::kPrimitiveColor);
215               });
216     } else {
217         this->addPaintColorToKey(keyContext, keyBuilder, gatherer);
218     }
219 }
220 
221 // Apply the paint's alpha value.
handlePaintAlpha(const KeyContext & keyContext,PaintParamsKeyBuilder * keyBuilder,PipelineDataGatherer * gatherer) const222 void PaintParams::handlePaintAlpha(const KeyContext& keyContext,
223                                    PaintParamsKeyBuilder* keyBuilder,
224                                    PipelineDataGatherer* gatherer) const {
225 
226     if (!fShader && !fPrimitiveBlender) {
227         // If there is no shader and no primitive blending the input to the colorFilter stage
228         // is just the premultiplied paint color.
229         SkPMColor4f paintColor = PaintParams::Color4fPrepForDst(fColor,
230                                                                 keyContext.dstColorInfo()).premul();
231         SolidColorShaderBlock::AddBlock(keyContext, keyBuilder, gatherer, paintColor);
232         return;
233     }
234 
235     if (fColor.fA != 1.0f) {
236         Blend(keyContext, keyBuilder, gatherer,
237               /* addBlendToKey= */ [&] () -> void {
238                   AddKnownModeBlend(keyContext, keyBuilder, gatherer, SkBlendMode::kSrcIn);
239               },
240               /* addSrcToKey= */ [&]() -> void {
241                   this->handlePrimitiveColor(keyContext, keyBuilder, gatherer);
242               },
243               /* addDstToKey= */ [&]() -> void {
244                   AlphaOnlyPaintColorBlock::AddBlock(keyContext, keyBuilder, gatherer);
245               });
246     } else {
247         this->handlePrimitiveColor(keyContext, keyBuilder, gatherer);
248     }
249 }
250 
handleColorFilter(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer) const251 void PaintParams::handleColorFilter(const KeyContext& keyContext,
252                                     PaintParamsKeyBuilder* builder,
253                                     PipelineDataGatherer* gatherer) const {
254     if (fColorFilter) {
255         Compose(keyContext, builder, gatherer,
256                 /* addInnerToKey= */ [&]() -> void {
257                     this->handlePaintAlpha(keyContext, builder, gatherer);
258                 },
259                 /* addOuterToKey= */ [&]() -> void {
260                     AddToKey(keyContext, builder, gatherer, fColorFilter.get());
261                 });
262     } else {
263         this->handlePaintAlpha(keyContext, builder, gatherer);
264     }
265 }
266 
handleDithering(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer) const267 void PaintParams::handleDithering(const KeyContext& keyContext,
268                                   PaintParamsKeyBuilder* builder,
269                                   PipelineDataGatherer* gatherer) const {
270 
271 #ifndef SK_IGNORE_GPU_DITHER
272     SkColorType ct = keyContext.dstColorInfo().colorType();
273     if (should_dither(*this, ct)) {
274         Compose(keyContext, builder, gatherer,
275                 /* addInnerToKey= */ [&]() -> void {
276                     this->handleColorFilter(keyContext, builder, gatherer);
277                 },
278                 /* addOuterToKey= */ [&]() -> void {
279                     AddDitherBlock(keyContext, builder, gatherer, ct);
280                 });
281     } else
282 #endif
283     {
284         this->handleColorFilter(keyContext, builder, gatherer);
285     }
286 }
287 
handleDstRead(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer) const288 void PaintParams::handleDstRead(const KeyContext& keyContext,
289                                 PaintParamsKeyBuilder* builder,
290                                 PipelineDataGatherer* gatherer) const {
291     if (fDstReadReq != DstReadRequirement::kNone) {
292         Blend(keyContext, builder, gatherer,
293               /* addBlendToKey= */ [&] () -> void {
294                   if (fFinalBlender) {
295                       AddToKey(keyContext, builder, gatherer, fFinalBlender.get());
296                   } else {
297                       AddKnownModeBlend(keyContext, builder, gatherer, SkBlendMode::kSrcOver);
298                   }
299               },
300               /* addSrcToKey= */ [&]() -> void {
301                   this->handleDithering(keyContext, builder, gatherer);
302               },
303               /* addDstToKey= */ [&]() -> void {
304                   AddDstReadBlock(keyContext, builder, gatherer, fDstReadReq);
305               });
306     } else {
307         this->handleDithering(keyContext, builder, gatherer);
308     }
309 }
310 
toKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer) const311 void PaintParams::toKey(const KeyContext& keyContext,
312                         PaintParamsKeyBuilder* builder,
313                         PipelineDataGatherer* gatherer) const {
314     this->handleDstRead(keyContext, builder, gatherer);
315 
316     std::optional<SkBlendMode> finalBlendMode = this->asFinalBlendMode();
317     if (fDstReadReq != DstReadRequirement::kNone) {
318         // In this case the blend will have been handled by shader-based blending with the dstRead.
319         finalBlendMode = SkBlendMode::kSrc;
320     }
321 
322     if (fClipShader) {
323         ClipShaderBlock::BeginBlock(keyContext, builder, gatherer);
324 
325             AddToKey(keyContext, builder, gatherer, fClipShader.get());
326 
327         builder->endBlock();
328     }
329 
330     // Set the hardware blend mode.
331     SkASSERT(finalBlendMode);
332     BuiltInCodeSnippetID fixedFuncBlendModeID = static_cast<BuiltInCodeSnippetID>(
333             kFixedFunctionBlendModeIDOffset + static_cast<int>(*finalBlendMode));
334 
335     builder->addBlock(fixedFuncBlendModeID);
336 }
337 
338 // TODO(b/330864257): Can be deleted once keys are determined by the Device draw.
notifyImagesInUse(Recorder * recorder,DrawContext * drawContext) const339 void PaintParams::notifyImagesInUse(Recorder* recorder,
340                                     DrawContext* drawContext) const {
341     if (fShader) {
342         NotifyImagesInUse(recorder, drawContext, fShader.get());
343     }
344     if (fPrimitiveBlender) {
345         NotifyImagesInUse(recorder, drawContext, fPrimitiveBlender.get());
346     }
347     if (fColorFilter) {
348         NotifyImagesInUse(recorder, drawContext, fColorFilter.get());
349     }
350     if (fFinalBlender) {
351         NotifyImagesInUse(recorder, drawContext, fFinalBlender.get());
352     }
353     if (fClipShader) {
354         NotifyImagesInUse(recorder, drawContext, fClipShader.get());
355     }
356 }
357 
358 } // namespace skgpu::graphite
359