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