• 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,const NonMSAAClip & nonMSAAClip,sk_sp<SkShader> clipShader,bool dstReadRequired,bool skipColorXform)53 PaintParams::PaintParams(const SkPaint& paint,
54                          sk_sp<SkBlender> primitiveBlender,
55                          const NonMSAAClip& nonMSAAClip,
56                          sk_sp<SkShader> clipShader,
57                          bool dstReadRequired,
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         , fNonMSAAClip(nonMSAAClip)
65         , fClipShader(std::move(clipShader))
66         , fDstReadRequired(dstReadRequired)
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 
AddFixedBlendMode(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,SkBlendMode bm)97 void AddFixedBlendMode(const KeyContext& keyContext,
98                        PaintParamsKeyBuilder* builder,
99                        PipelineDataGatherer* gatherer,
100                        SkBlendMode bm) {
101     SkASSERT(bm <= SkBlendMode::kLastMode);
102     BuiltInCodeSnippetID id = static_cast<BuiltInCodeSnippetID>(kFixedBlendIDOffset +
103                                                                 static_cast<int>(bm));
104     builder->addBlock(id);
105 }
106 
AddBlendMode(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,SkBlendMode bm)107 void AddBlendMode(const KeyContext& keyContext,
108                   PaintParamsKeyBuilder* builder,
109                   PipelineDataGatherer* gatherer,
110                   SkBlendMode bm) {
111     // For non-fixed blends, coefficient blend modes are combined into the same shader snippet.
112     // The same goes for the HSLC advanced blends. The remaining advanced blends are fairly unique
113     // in their implementations. To avoid having to compile all of their SkSL, they are treated as
114     // fixed blend modes.
115     SkSpan<const float> coeffs = skgpu::GetPorterDuffBlendConstants(bm);
116     if (!coeffs.empty()) {
117         PorterDuffBlenderBlock::AddBlock(keyContext, builder, gatherer, coeffs);
118     } else if (bm >= SkBlendMode::kHue) {
119         ReducedBlendModeInfo blendInfo = GetReducedBlendModeInfo(bm);
120         HSLCBlenderBlock::AddBlock(keyContext, builder, gatherer, blendInfo.fUniformData);
121     } else {
122         AddFixedBlendMode(keyContext, builder, gatherer, bm);
123     }
124 }
125 
AddDitherBlock(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer,SkColorType ct)126 void AddDitherBlock(const KeyContext& keyContext,
127                     PaintParamsKeyBuilder* builder,
128                     PipelineDataGatherer* gatherer,
129                     SkColorType ct) {
130     static const SkBitmap gLUT = skgpu::MakeDitherLUT();
131 
132     sk_sp<TextureProxy> proxy = RecorderPriv::CreateCachedProxy(keyContext.recorder(), gLUT,
133                                                                 "DitherLUT");
134     if (keyContext.recorder() && !proxy) {
135         SKGPU_LOG_W("Couldn't create dither shader's LUT");
136         builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
137         return;
138     }
139 
140     DitherShaderBlock::DitherData data(skgpu::DitherRangeForConfig(ct), std::move(proxy));
141 
142     DitherShaderBlock::AddBlock(keyContext, builder, gatherer, data);
143 }
144 
addPaintColorToKey(const KeyContext & keyContext,PaintParamsKeyBuilder * keyBuilder,PipelineDataGatherer * gatherer) const145 void PaintParams::addPaintColorToKey(const KeyContext& keyContext,
146                                      PaintParamsKeyBuilder* keyBuilder,
147                                      PipelineDataGatherer* gatherer) const {
148     if (fShader) {
149         AddToKey(keyContext, keyBuilder, gatherer, fShader.get());
150     } else {
151         RGBPaintColorBlock::AddBlock(keyContext, keyBuilder, gatherer);
152     }
153 }
154 
155 /**
156  * Primitive blend blocks are used to blend either the paint color or the output of another shader
157  * with a primitive color emitted by certain draw geometry calls (drawVertices, drawAtlas, etc.).
158  * Dst: primitiveColor Src: Paint color/shader output
159  */
handlePrimitiveColor(const KeyContext & keyContext,PaintParamsKeyBuilder * keyBuilder,PipelineDataGatherer * gatherer) const160 void PaintParams::handlePrimitiveColor(const KeyContext& keyContext,
161                                        PaintParamsKeyBuilder* keyBuilder,
162                                        PipelineDataGatherer* gatherer) const {
163     if (fPrimitiveBlender) {
164         Blend(keyContext, keyBuilder, gatherer,
165               /* addBlendToKey= */ [&] () -> void {
166                   AddToKey(keyContext, keyBuilder, gatherer, fPrimitiveBlender.get());
167               },
168               /* addSrcToKey= */ [&]() -> void {
169                   this->addPaintColorToKey(keyContext, keyBuilder, gatherer);
170               },
171               /* addDstToKey= */ [&]() -> void {
172                   // When fSkipColorXform is true, it's assumed that the primitive color is
173                   // already in the dst color space. We could change the paint key to not have
174                   // any colorspace block wrapping the primitive color block, but for now just
175                   // use the dst color space as the src color space to produce an identity CS
176                   // transform.
177                   //
178                   // When fSkipColorXform is false (most cases), it's assumed to be in sRGB.
179                   const SkColorSpace* primitiveCS =
180                         fSkipColorXform ? keyContext.dstColorInfo().colorSpace()
181                                         : sk_srgb_singleton();
182                   AddPrimitiveColor(keyContext, keyBuilder, gatherer, primitiveCS);
183               });
184     } else {
185         this->addPaintColorToKey(keyContext, keyBuilder, gatherer);
186     }
187 }
188 
189 // Apply the paint's alpha value.
handlePaintAlpha(const KeyContext & keyContext,PaintParamsKeyBuilder * keyBuilder,PipelineDataGatherer * gatherer) const190 void PaintParams::handlePaintAlpha(const KeyContext& keyContext,
191                                    PaintParamsKeyBuilder* keyBuilder,
192                                    PipelineDataGatherer* gatherer) const {
193 
194     if (!fShader && !fPrimitiveBlender) {
195         // If there is no shader and no primitive blending the input to the colorFilter stage
196         // is just the premultiplied paint color.
197         SkPMColor4f paintColor = PaintParams::Color4fPrepForDst(fColor,
198                                                                 keyContext.dstColorInfo()).premul();
199         SolidColorShaderBlock::AddBlock(keyContext, keyBuilder, gatherer, paintColor);
200         return;
201     }
202 
203     if (fColor.fA != 1.0f) {
204         Blend(keyContext, keyBuilder, gatherer,
205               /* addBlendToKey= */ [&] () -> void {
206                   AddFixedBlendMode(keyContext, keyBuilder, gatherer, SkBlendMode::kSrcIn);
207               },
208               /* addSrcToKey= */ [&]() -> void {
209                   this->handlePrimitiveColor(keyContext, keyBuilder, gatherer);
210               },
211               /* addDstToKey= */ [&]() -> void {
212                   AlphaOnlyPaintColorBlock::AddBlock(keyContext, keyBuilder, gatherer);
213               });
214     } else {
215         this->handlePrimitiveColor(keyContext, keyBuilder, gatherer);
216     }
217 }
218 
handleColorFilter(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer) const219 void PaintParams::handleColorFilter(const KeyContext& keyContext,
220                                     PaintParamsKeyBuilder* builder,
221                                     PipelineDataGatherer* gatherer) const {
222     if (fColorFilter) {
223         Compose(keyContext, builder, gatherer,
224                 /* addInnerToKey= */ [&]() -> void {
225                     this->handlePaintAlpha(keyContext, builder, gatherer);
226                 },
227                 /* addOuterToKey= */ [&]() -> void {
228                     AddToKey(keyContext, builder, gatherer, fColorFilter.get());
229                 });
230     } else {
231         this->handlePaintAlpha(keyContext, builder, gatherer);
232     }
233 }
234 
handleDithering(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer) const235 void PaintParams::handleDithering(const KeyContext& keyContext,
236                                   PaintParamsKeyBuilder* builder,
237                                   PipelineDataGatherer* gatherer) const {
238 
239 #ifndef SK_IGNORE_GPU_DITHER
240     SkColorType ct = keyContext.dstColorInfo().colorType();
241     if (should_dither(*this, ct)) {
242         Compose(keyContext, builder, gatherer,
243                 /* addInnerToKey= */ [&]() -> void {
244                     this->handleColorFilter(keyContext, builder, gatherer);
245                 },
246                 /* addOuterToKey= */ [&]() -> void {
247                     AddDitherBlock(keyContext, builder, gatherer, ct);
248                 });
249     } else
250 #endif
251     {
252         this->handleColorFilter(keyContext, builder, gatherer);
253     }
254 }
255 
handleClipping(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer) const256 void PaintParams::handleClipping(const KeyContext& keyContext,
257                                  PaintParamsKeyBuilder* builder,
258                                  PipelineDataGatherer* gatherer) const {
259     if (!fNonMSAAClip.isEmpty()) {
260         const AnalyticClip& analyticClip = fNonMSAAClip.fAnalyticClip;
261         SkPoint radiusPair;
262         SkRect analyticBounds;
263         if (!analyticClip.isEmpty()) {
264             float radius = analyticClip.fRadius + 0.5f;
265             // N.B.: Because the clip data is normally used with depth-based clipping,
266             // the shape is inverted from its usual state. We re-invert here to
267             // match what the shader snippet expects.
268             radiusPair = {(analyticClip.fInverted) ? radius : -radius, 1.0f/radius};
269             analyticBounds = analyticClip.fBounds.makeOutset(0.5f).asSkRect();
270         } else {
271             // This will generate no analytic clip.
272             radiusPair = { -0.5f, 1.f };
273             analyticBounds = { 0, 0, 0, 0 };
274         }
275 
276         const AtlasClip& atlasClip = fNonMSAAClip.fAtlasClip;
277         SkISize maskSize = atlasClip.fMaskBounds.size();
278         SkRect texMaskBounds = SkRect::MakeXYWH(atlasClip.fOutPos.x(), atlasClip.fOutPos.y(),
279                                                 maskSize.width(), maskSize.height());
280         // Outset bounds to capture some of the padding (necessary for inverse clip)
281         texMaskBounds.outset(0.5f, 0.5f);
282         SkPoint texCoordOffset = SkPoint::Make(atlasClip.fOutPos.x() - atlasClip.fMaskBounds.left(),
283                                                atlasClip.fOutPos.y() - atlasClip.fMaskBounds.top());
284 
285         NonMSAAClipBlock::NonMSAAClipData data(
286                 analyticBounds,
287                 radiusPair,
288                 analyticClip.edgeSelectRect(),
289                 texCoordOffset,
290                 texMaskBounds,
291                 atlasClip.fAtlasTexture);
292         if (fClipShader) {
293             // For both an analytic clip and clip shader, we need to compose them together into
294             // a single clipping root node.
295             Blend(keyContext, builder, gatherer,
296                   /* addBlendToKey= */ [&]() -> void {
297                       AddFixedBlendMode(keyContext, builder, gatherer, SkBlendMode::kModulate);
298                   },
299                   /* addSrcToKey= */ [&]() -> void {
300                       NonMSAAClipBlock::AddBlock(keyContext, builder, gatherer, data);
301                   },
302                   /* addDstToKey= */ [&]() -> void {
303                       AddToKey(keyContext, builder, gatherer, fClipShader.get());
304                   });
305         } else {
306             // Without a clip shader, the analytic clip can be the clipping root node.
307             NonMSAAClipBlock::AddBlock(keyContext, builder, gatherer, data);
308         }
309     } else if (fClipShader) {
310         // Since there's no analytic clip, the clipping root node can be fClipShader directly.
311         AddToKey(keyContext, builder, gatherer, fClipShader.get());
312     }
313 }
314 
toKey(const KeyContext & keyContext,PaintParamsKeyBuilder * builder,PipelineDataGatherer * gatherer) const315 void PaintParams::toKey(const KeyContext& keyContext,
316                         PaintParamsKeyBuilder* builder,
317                         PipelineDataGatherer* gatherer) const {
318     // Root Node 0 is the source color, which is the output of all effects post dithering
319     this->handleDithering(keyContext, builder, gatherer);
320 
321     // Root Node 1 is the final blender
322     std::optional<SkBlendMode> finalBlendMode = this->asFinalBlendMode();
323     if (finalBlendMode) {
324         if (!fDstReadRequired) {
325             // With no shader blending, be as explicit as possible about the final blend
326             AddFixedBlendMode(keyContext, builder, gatherer, *finalBlendMode);
327         } else {
328             // With shader blending, use AddBlendMode() to select the more universal blend functions
329             // when possible. Technically we could always use a fixed blend mode but would then
330             // over-generate when encountering certain classes of blends. This is most problematic
331             // on devices that wouldn't support dual-source blending, so help them out by at least
332             // not requiring lots of pipelines.
333             AddBlendMode(keyContext, builder, gatherer, *finalBlendMode);
334         }
335     } else {
336         AddToKey(keyContext, builder, gatherer, fFinalBlender.get());
337     }
338 
339     // Optional Root Node 2 is the clip
340     this->handleClipping(keyContext, builder, gatherer);
341 }
342 
343 // TODO(b/330864257): Can be deleted once keys are determined by the Device draw.
notifyImagesInUse(Recorder * recorder,DrawContext * drawContext) const344 void PaintParams::notifyImagesInUse(Recorder* recorder,
345                                     DrawContext* drawContext) const {
346     if (fShader) {
347         NotifyImagesInUse(recorder, drawContext, fShader.get());
348     }
349     if (fPrimitiveBlender) {
350         NotifyImagesInUse(recorder, drawContext, fPrimitiveBlender.get());
351     }
352     if (fColorFilter) {
353         NotifyImagesInUse(recorder, drawContext, fColorFilter.get());
354     }
355     if (fFinalBlender) {
356         NotifyImagesInUse(recorder, drawContext, fFinalBlender.get());
357     }
358     if (fClipShader) {
359         NotifyImagesInUse(recorder, drawContext, fClipShader.get());
360     }
361 }
362 
363 } // namespace skgpu::graphite
364