Coverage Report

Created: 2024-09-14 07:19

/src/skia/src/gpu/graphite/PaintParams.cpp
Line
Count
Source (jump to first uncovered line)
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
32
0
bool should_dither(const PaintParams& p, SkColorType dstCT) {
33
    // The paint dither flag can veto.
34
0
    if (!p.dither()) {
35
0
        return false;
36
0
    }
37
38
0
    if (dstCT == kUnknown_SkColorType) {
39
0
        return false;
40
0
    }
41
42
    // We always dither 565 or 4444 when requested.
43
0
    if (dstCT == kRGB_565_SkColorType || dstCT == kARGB_4444_SkColorType) {
44
0
        return true;
45
0
    }
46
47
    // Otherwise, dither is only needed for non-const paints.
48
0
    return p.shader() && !as_SB(p.shader())->isConstant();
49
0
}
50
51
} // anonymous namespace
52
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
0
        , fDither(paint.isDither()) {}
69
70
0
PaintParams::PaintParams(const PaintParams& other) = default;
71
0
PaintParams::~PaintParams() = default;
72
0
PaintParams& PaintParams::operator=(const PaintParams& other) = default;
73
74
0
std::optional<SkBlendMode> PaintParams::asFinalBlendMode() const {
75
0
    return fFinalBlender ? as_BB(fFinalBlender)->asBlendMode()
76
0
                         : SkBlendMode::kSrcOver;
77
0
}
78
79
0
sk_sp<SkBlender> PaintParams::refFinalBlender() const { return fFinalBlender; }
80
81
0
sk_sp<SkShader> PaintParams::refShader() const { return fShader; }
82
83
0
sk_sp<SkColorFilter> PaintParams::refColorFilter() const { return fColorFilter; }
84
85
0
sk_sp<SkBlender> PaintParams::refPrimitiveBlender() const { return fPrimitiveBlender; }
86
87
0
SkColor4f PaintParams::Color4fPrepForDst(SkColor4f srcColor, const SkColorInfo& dstColorInfo) {
88
    // xform from sRGB to the destination colorspace
89
0
    SkColorSpaceXformSteps steps(sk_srgb_singleton(),       kUnpremul_SkAlphaType,
90
0
                                 dstColorInfo.colorSpace(), kUnpremul_SkAlphaType);
91
92
0
    SkColor4f result = srcColor;
93
0
    steps.apply(result.vec());
94
0
    return result;
95
0
}
96
97
void Blend(const KeyContext& keyContext,
98
           PaintParamsKeyBuilder* keyBuilder,
99
           PipelineDataGatherer* gatherer,
100
           AddToKeyFn addBlendToKey,
101
           AddToKeyFn addSrcToKey,
102
0
           AddToKeyFn addDstToKey) {
103
0
    BlendComposeBlock::BeginBlock(keyContext, keyBuilder, gatherer);
104
105
0
        addSrcToKey();
106
107
0
        addDstToKey();
108
109
0
        addBlendToKey();
110
111
0
    keyBuilder->endBlock();  // BlendComposeBlock
112
0
}
113
114
void Compose(const KeyContext& keyContext,
115
             PaintParamsKeyBuilder* keyBuilder,
116
             PipelineDataGatherer* gatherer,
117
             AddToKeyFn addInnerToKey,
118
0
             AddToKeyFn addOuterToKey) {
119
0
    ComposeBlock::BeginBlock(keyContext, keyBuilder, gatherer);
120
121
0
        addInnerToKey();
122
123
0
        addOuterToKey();
124
125
0
    keyBuilder->endBlock();  // ComposeBlock
126
0
}
127
128
void AddFixedBlendMode(const KeyContext& keyContext,
129
                       PaintParamsKeyBuilder* builder,
130
                       PipelineDataGatherer* gatherer,
131
0
                       SkBlendMode bm) {
132
0
    SkASSERT(bm <= SkBlendMode::kLastMode);
133
0
    BuiltInCodeSnippetID id = static_cast<BuiltInCodeSnippetID>(kFixedBlendIDOffset +
134
0
                                                                static_cast<int>(bm));
135
0
    builder->addBlock(id);
136
0
}
Unexecuted instantiation: skgpu::graphite::AddFixedBlendMode(skgpu::graphite::KeyContext const&, skgpu::graphite::PaintParamsKeyBuilder*, skgpu::graphite::PipelineDataGatherer*, SkBlendMode)
Unexecuted instantiation: skgpu::graphite::AddFixedBlendMode(skgpu::graphite::KeyContext const&, skgpu::graphite::PaintParamsKeyBuilder*, skgpu::graphite::PipelineDataGatherer*, SkBlendMode)
137
138
void AddBlendMode(const KeyContext& keyContext,
139
                  PaintParamsKeyBuilder* builder,
140
                  PipelineDataGatherer* gatherer,
141
0
                  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
0
    SkSpan<const float> coeffs = skgpu::GetPorterDuffBlendConstants(bm);
147
0
    if (!coeffs.empty()) {
148
0
        PorterDuffBlenderBlock::AddBlock(keyContext, builder, gatherer, coeffs);
149
0
    } else if (bm >= SkBlendMode::kHue) {
150
0
        ReducedBlendModeInfo blendInfo = GetReducedBlendModeInfo(bm);
151
0
        HSLCBlenderBlock::AddBlock(keyContext, builder, gatherer, blendInfo.fUniformData);
152
0
    } else {
153
0
        AddFixedBlendMode(keyContext, builder, gatherer, bm);
154
0
    }
155
0
}
156
157
void AddDstReadBlock(const KeyContext& keyContext,
158
                     PaintParamsKeyBuilder* builder,
159
                     PipelineDataGatherer* gatherer,
160
0
                     DstReadRequirement dstReadReq) {
161
0
    switch(dstReadReq) {
162
0
        case DstReadRequirement::kNone:
163
0
            SkASSERT(false);            // This should never be reached
164
0
            return;
165
0
        case DstReadRequirement::kTextureCopy:
166
0
            [[fallthrough]];
167
0
        case DstReadRequirement::kTextureSample:
168
0
            DstReadSampleBlock::AddBlock(keyContext, builder, gatherer, keyContext.dstTexture(),
169
0
                                         keyContext.dstOffset());
170
0
            break;
171
0
        case DstReadRequirement::kFramebufferFetch:
172
0
            builder->addBlock(BuiltInCodeSnippetID::kDstReadFetch);
173
0
            break;
174
0
    }
175
0
}
Unexecuted instantiation: skgpu::graphite::AddDstReadBlock(skgpu::graphite::KeyContext const&, skgpu::graphite::PaintParamsKeyBuilder*, skgpu::graphite::PipelineDataGatherer*, skgpu::graphite::DstReadRequirement)
Unexecuted instantiation: skgpu::graphite::AddDstReadBlock(skgpu::graphite::KeyContext const&, skgpu::graphite::PaintParamsKeyBuilder*, skgpu::graphite::PipelineDataGatherer*, skgpu::graphite::DstReadRequirement)
176
177
void AddDitherBlock(const KeyContext& keyContext,
178
                    PaintParamsKeyBuilder* builder,
179
                    PipelineDataGatherer* gatherer,
180
0
                    SkColorType ct) {
181
0
    static const SkBitmap gLUT = skgpu::MakeDitherLUT();
182
183
0
    sk_sp<TextureProxy> proxy = RecorderPriv::CreateCachedProxy(keyContext.recorder(), gLUT,
184
0
                                                                "DitherLUT");
185
0
    if (keyContext.recorder() && !proxy) {
186
0
        SKGPU_LOG_W("Couldn't create dither shader's LUT");
187
0
        builder->addBlock(BuiltInCodeSnippetID::kPriorOutput);
188
0
        return;
189
0
    }
190
191
0
    DitherShaderBlock::DitherData data(skgpu::DitherRangeForConfig(ct), std::move(proxy));
192
193
0
    DitherShaderBlock::AddBlock(keyContext, builder, gatherer, data);
194
0
}
195
196
void PaintParams::addPaintColorToKey(const KeyContext& keyContext,
197
                                     PaintParamsKeyBuilder* keyBuilder,
198
0
                                     PipelineDataGatherer* gatherer) const {
199
0
    if (fShader) {
200
0
        AddToKey(keyContext, keyBuilder, gatherer, fShader.get());
201
0
    } else {
202
0
        RGBPaintColorBlock::AddBlock(keyContext, keyBuilder, gatherer);
203
0
    }
204
0
}
205
206
/**
207
 * Primitive blend blocks are used to blend either the paint color or the output of another shader
208
 * with a primitive color emitted by certain draw geometry calls (drawVertices, drawAtlas, etc.).
209
 * Dst: primitiveColor Src: Paint color/shader output
210
 */
211
void PaintParams::handlePrimitiveColor(const KeyContext& keyContext,
212
                                       PaintParamsKeyBuilder* keyBuilder,
213
0
                                       PipelineDataGatherer* gatherer) const {
214
0
    if (fPrimitiveBlender) {
215
0
        Blend(keyContext, keyBuilder, gatherer,
216
0
              /* addBlendToKey= */ [&] () -> void {
217
0
                  AddToKey(keyContext, keyBuilder, gatherer, fPrimitiveBlender.get());
218
0
              },
219
0
              /* addSrcToKey= */ [&]() -> void {
220
0
                  this->addPaintColorToKey(keyContext, keyBuilder, gatherer);
221
0
              },
222
0
              /* addDstToKey= */ [&]() -> void {
223
0
                  PrimitiveColorBlock::AddBlock(keyContext, keyBuilder, gatherer);
224
0
              });
225
0
    } else {
226
0
        this->addPaintColorToKey(keyContext, keyBuilder, gatherer);
227
0
    }
228
0
}
229
230
// Apply the paint's alpha value.
231
void PaintParams::handlePaintAlpha(const KeyContext& keyContext,
232
                                   PaintParamsKeyBuilder* keyBuilder,
233
0
                                   PipelineDataGatherer* gatherer) const {
234
235
0
    if (!fShader && !fPrimitiveBlender) {
236
        // If there is no shader and no primitive blending the input to the colorFilter stage
237
        // is just the premultiplied paint color.
238
0
        SkPMColor4f paintColor = PaintParams::Color4fPrepForDst(fColor,
239
0
                                                                keyContext.dstColorInfo()).premul();
240
0
        SolidColorShaderBlock::AddBlock(keyContext, keyBuilder, gatherer, paintColor);
241
0
        return;
242
0
    }
243
244
0
    if (fColor.fA != 1.0f) {
245
0
        Blend(keyContext, keyBuilder, gatherer,
246
0
              /* addBlendToKey= */ [&] () -> void {
247
0
                  AddFixedBlendMode(keyContext, keyBuilder, gatherer, SkBlendMode::kSrcIn);
248
0
              },
249
0
              /* addSrcToKey= */ [&]() -> void {
250
0
                  this->handlePrimitiveColor(keyContext, keyBuilder, gatherer);
251
0
              },
252
0
              /* addDstToKey= */ [&]() -> void {
253
0
                  AlphaOnlyPaintColorBlock::AddBlock(keyContext, keyBuilder, gatherer);
254
0
              });
255
0
    } else {
256
0
        this->handlePrimitiveColor(keyContext, keyBuilder, gatherer);
257
0
    }
258
0
}
259
260
void PaintParams::handleColorFilter(const KeyContext& keyContext,
261
                                    PaintParamsKeyBuilder* builder,
262
0
                                    PipelineDataGatherer* gatherer) const {
263
0
    if (fColorFilter) {
264
0
        Compose(keyContext, builder, gatherer,
265
0
                /* addInnerToKey= */ [&]() -> void {
266
0
                    this->handlePaintAlpha(keyContext, builder, gatherer);
267
0
                },
268
0
                /* addOuterToKey= */ [&]() -> void {
269
0
                    AddToKey(keyContext, builder, gatherer, fColorFilter.get());
270
0
                });
271
0
    } else {
272
0
        this->handlePaintAlpha(keyContext, builder, gatherer);
273
0
    }
274
0
}
275
276
void PaintParams::handleDithering(const KeyContext& keyContext,
277
                                  PaintParamsKeyBuilder* builder,
278
0
                                  PipelineDataGatherer* gatherer) const {
279
280
0
#ifndef SK_IGNORE_GPU_DITHER
281
0
    SkColorType ct = keyContext.dstColorInfo().colorType();
282
0
    if (should_dither(*this, ct)) {
283
0
        Compose(keyContext, builder, gatherer,
284
0
                /* addInnerToKey= */ [&]() -> void {
285
0
                    this->handleColorFilter(keyContext, builder, gatherer);
286
0
                },
287
0
                /* addOuterToKey= */ [&]() -> void {
288
0
                    AddDitherBlock(keyContext, builder, gatherer, ct);
289
0
                });
290
0
    } else
291
0
#endif
292
0
    {
293
0
        this->handleColorFilter(keyContext, builder, gatherer);
294
0
    }
295
0
}
296
297
void PaintParams::handleDstRead(const KeyContext& keyContext,
298
                                PaintParamsKeyBuilder* builder,
299
0
                                PipelineDataGatherer* gatherer) const {
300
0
    if (fDstReadReq != DstReadRequirement::kNone) {
301
0
        Blend(keyContext, builder, gatherer,
302
0
              /* addBlendToKey= */ [&] () -> void {
303
0
                  if (fFinalBlender) {
304
0
                      AddToKey(keyContext, builder, gatherer, fFinalBlender.get());
305
0
                  } else {
306
0
                      AddFixedBlendMode(keyContext, builder, gatherer, SkBlendMode::kSrcOver);
307
0
                  }
308
0
              },
309
0
              /* addSrcToKey= */ [&]() -> void {
310
0
                  this->handleDithering(keyContext, builder, gatherer);
311
0
              },
312
0
              /* addDstToKey= */ [&]() -> void {
313
0
                  AddDstReadBlock(keyContext, builder, gatherer, fDstReadReq);
314
0
              });
315
0
    } else {
316
0
        this->handleDithering(keyContext, builder, gatherer);
317
0
    }
318
0
}
319
320
void PaintParams::handleClipping(const KeyContext& keyContext,
321
                                 PaintParamsKeyBuilder* builder,
322
0
                                 PipelineDataGatherer* gatherer) const {
323
0
    ClipBlock::BeginBlock(keyContext, builder, gatherer);
324
325
0
    if (!fAnalyticClip.isEmpty()) {
326
0
        float radius = fAnalyticClip.fRadius + 0.5f;
327
        // N.B.: Because the clip data is normally used with depth-based clipping,
328
        // the shape is inverted from its usual state. We re-invert here to
329
        // match what the shader snippet expects.
330
0
        SkPoint radiusPair = {(fAnalyticClip.fInverted) ? radius : -radius, 1.0f/radius};
331
0
        CircularRRectClipBlock::CircularRRectClipData data(
332
0
                fAnalyticClip.fBounds.makeOutset(0.5f).asSkRect(),
333
0
                radiusPair,
334
0
                fAnalyticClip.edgeSelectRect());
335
0
        if (fClipShader) {
336
0
            Blend(keyContext, builder, gatherer,
337
0
                  /* addBlendToKey= */ [&]() -> void {
338
0
                      AddFixedBlendMode(keyContext, builder, gatherer, SkBlendMode::kModulate);
339
0
                  },
340
0
                  /* addSrcToKey= */ [&]() -> void {
341
0
                      CircularRRectClipBlock::AddBlock(keyContext, builder, gatherer, data);
342
0
                  },
343
0
                  /* addDstToKey= */ [&]() -> void {
344
0
                      AddToKey(keyContext, builder, gatherer, fClipShader.get());
345
0
                  });
346
0
        } else {
347
0
            CircularRRectClipBlock::AddBlock(keyContext, builder, gatherer, data);
348
0
        }
349
0
    } else {
350
0
        SkASSERT(fClipShader);
351
0
        AddToKey(keyContext, builder, gatherer, fClipShader.get());
352
0
    }
353
354
0
    builder->endBlock();
355
0
}
Unexecuted instantiation: skgpu::graphite::PaintParams::handleClipping(skgpu::graphite::KeyContext const&, skgpu::graphite::PaintParamsKeyBuilder*, skgpu::graphite::PipelineDataGatherer*) const
Unexecuted instantiation: skgpu::graphite::PaintParams::handleClipping(skgpu::graphite::KeyContext const&, skgpu::graphite::PaintParamsKeyBuilder*, skgpu::graphite::PipelineDataGatherer*) const
356
357
void PaintParams::toKey(const KeyContext& keyContext,
358
                        PaintParamsKeyBuilder* builder,
359
0
                        PipelineDataGatherer* gatherer) const {
360
0
    this->handleDstRead(keyContext, builder, gatherer);
361
362
0
    std::optional<SkBlendMode> finalBlendMode = this->asFinalBlendMode();
363
0
    if (fDstReadReq != DstReadRequirement::kNone) {
364
        // In this case the blend will have been handled by shader-based blending with the dstRead.
365
0
        finalBlendMode = SkBlendMode::kSrc;
366
0
    }
367
368
0
    if (!fAnalyticClip.isEmpty() || fClipShader) {
369
0
        this->handleClipping(keyContext, builder, gatherer);
370
0
    }
371
372
    // Set the hardware blend mode.
373
0
    SkASSERT(finalBlendMode);
374
0
    AddFixedBlendMode(keyContext, builder, gatherer, *finalBlendMode);
375
0
}
Unexecuted instantiation: skgpu::graphite::PaintParams::toKey(skgpu::graphite::KeyContext const&, skgpu::graphite::PaintParamsKeyBuilder*, skgpu::graphite::PipelineDataGatherer*) const
Unexecuted instantiation: skgpu::graphite::PaintParams::toKey(skgpu::graphite::KeyContext const&, skgpu::graphite::PaintParamsKeyBuilder*, skgpu::graphite::PipelineDataGatherer*) const
376
377
// TODO(b/330864257): Can be deleted once keys are determined by the Device draw.
378
void PaintParams::notifyImagesInUse(Recorder* recorder,
379
0
                                    DrawContext* drawContext) const {
380
0
    if (fShader) {
381
0
        NotifyImagesInUse(recorder, drawContext, fShader.get());
382
0
    }
383
0
    if (fPrimitiveBlender) {
384
0
        NotifyImagesInUse(recorder, drawContext, fPrimitiveBlender.get());
385
0
    }
386
0
    if (fColorFilter) {
387
0
        NotifyImagesInUse(recorder, drawContext, fColorFilter.get());
388
0
    }
389
0
    if (fFinalBlender) {
390
0
        NotifyImagesInUse(recorder, drawContext, fFinalBlender.get());
391
0
    }
392
0
    if (fClipShader) {
393
0
        NotifyImagesInUse(recorder, drawContext, fClipShader.get());
394
0
    }
395
0
}
396
397
} // namespace skgpu::graphite