1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2019 Google Inc.
3cb93a386Sopenharmony_ci *
4cb93a386Sopenharmony_ci * Use of this source code is governed by a BSD-style license that can be
5cb93a386Sopenharmony_ci * found in the LICENSE file.
6cb93a386Sopenharmony_ci */
7cb93a386Sopenharmony_ci
8cb93a386Sopenharmony_ci#include "include/private/SkImageInfoPriv.h"
9cb93a386Sopenharmony_ci#include "include/private/SkMacros.h"
10cb93a386Sopenharmony_ci#include "src/core/SkArenaAlloc.h"
11cb93a386Sopenharmony_ci#include "src/core/SkBlendModePriv.h"
12cb93a386Sopenharmony_ci#include "src/core/SkBlenderBase.h"
13cb93a386Sopenharmony_ci#include "src/core/SkColorFilterBase.h"
14cb93a386Sopenharmony_ci#include "src/core/SkColorSpacePriv.h"
15cb93a386Sopenharmony_ci#include "src/core/SkColorSpaceXformSteps.h"
16cb93a386Sopenharmony_ci#include "src/core/SkCoreBlitters.h"
17cb93a386Sopenharmony_ci#include "src/core/SkLRUCache.h"
18cb93a386Sopenharmony_ci#include "src/core/SkMatrixProvider.h"
19cb93a386Sopenharmony_ci#include "src/core/SkOpts.h"
20cb93a386Sopenharmony_ci#include "src/core/SkPaintPriv.h"
21cb93a386Sopenharmony_ci#include "src/core/SkVM.h"
22cb93a386Sopenharmony_ci#include "src/core/SkVMBlitter.h"
23cb93a386Sopenharmony_ci#include "src/shaders/SkColorFilterShader.h"
24cb93a386Sopenharmony_ci
25cb93a386Sopenharmony_ci#include <cinttypes>
26cb93a386Sopenharmony_ci
27cb93a386Sopenharmony_cinamespace {
28cb93a386Sopenharmony_ci
29cb93a386Sopenharmony_ci    // Uniforms set by the Blitter itself,
30cb93a386Sopenharmony_ci    // rather than by the Shader, which follow this struct in the skvm::Uniforms buffer.
31cb93a386Sopenharmony_ci    struct BlitterUniforms {
32cb93a386Sopenharmony_ci        int       right;  // First device x + blit run length n, used to get device x coordinate.
33cb93a386Sopenharmony_ci        int       y;      // Device y coordinate.
34cb93a386Sopenharmony_ci    };
35cb93a386Sopenharmony_ci    static_assert(SkIsAlign4(sizeof(BlitterUniforms)), "");
36cb93a386Sopenharmony_ci    inline static constexpr int kBlitterUniformsCount = sizeof(BlitterUniforms) / 4;
37cb93a386Sopenharmony_ci
38cb93a386Sopenharmony_ci    static skvm::Coord device_coord(skvm::Builder* p, skvm::Uniforms* uniforms) {
39cb93a386Sopenharmony_ci        skvm::I32 dx = p->uniform32(uniforms->base, offsetof(BlitterUniforms, right))
40cb93a386Sopenharmony_ci                     - p->index(),
41cb93a386Sopenharmony_ci                  dy = p->uniform32(uniforms->base, offsetof(BlitterUniforms, y));
42cb93a386Sopenharmony_ci        return {
43cb93a386Sopenharmony_ci            to_F32(dx) + 0.5f,
44cb93a386Sopenharmony_ci            to_F32(dy) + 0.5f,
45cb93a386Sopenharmony_ci        };
46cb93a386Sopenharmony_ci    }
47cb93a386Sopenharmony_ci
48cb93a386Sopenharmony_ci    struct NoopColorFilter : public SkColorFilterBase {
49cb93a386Sopenharmony_ci        skvm::Color onProgram(skvm::Builder*, skvm::Color c,
50cb93a386Sopenharmony_ci                              const SkColorInfo&, skvm::Uniforms*, SkArenaAlloc*) const override {
51cb93a386Sopenharmony_ci            return c;
52cb93a386Sopenharmony_ci        }
53cb93a386Sopenharmony_ci
54cb93a386Sopenharmony_ci        bool onAppendStages(const SkStageRec&, bool) const override { return true; }
55cb93a386Sopenharmony_ci
56cb93a386Sopenharmony_ci        // Only created here, should never be flattened / unflattened.
57cb93a386Sopenharmony_ci        Factory getFactory() const override { return nullptr; }
58cb93a386Sopenharmony_ci        const char* getTypeName() const override { return "NoopColorFilter"; }
59cb93a386Sopenharmony_ci    };
60cb93a386Sopenharmony_ci
61cb93a386Sopenharmony_ci    struct SpriteShader : public SkShaderBase {
62cb93a386Sopenharmony_ci        explicit SpriteShader(SkPixmap sprite) : fSprite(sprite) {}
63cb93a386Sopenharmony_ci
64cb93a386Sopenharmony_ci        SkPixmap fSprite;
65cb93a386Sopenharmony_ci
66cb93a386Sopenharmony_ci        // Only created here temporarily... never serialized.
67cb93a386Sopenharmony_ci        Factory      getFactory() const override { return nullptr; }
68cb93a386Sopenharmony_ci        const char* getTypeName() const override { return "SpriteShader"; }
69cb93a386Sopenharmony_ci
70cb93a386Sopenharmony_ci        bool isOpaque() const override { return fSprite.isOpaque(); }
71cb93a386Sopenharmony_ci
72cb93a386Sopenharmony_ci        skvm::Color onProgram(skvm::Builder* p,
73cb93a386Sopenharmony_ci                              skvm::Coord /*device*/, skvm::Coord /*local*/, skvm::Color /*paint*/,
74cb93a386Sopenharmony_ci                              const SkMatrixProvider&, const SkMatrix* /*localM*/,
75cb93a386Sopenharmony_ci                              const SkColorInfo& dst,
76cb93a386Sopenharmony_ci                              skvm::Uniforms* uniforms, SkArenaAlloc*) const override {
77cb93a386Sopenharmony_ci            const SkColorType ct = fSprite.colorType();
78cb93a386Sopenharmony_ci
79cb93a386Sopenharmony_ci            skvm::PixelFormat fmt = skvm::SkColorType_to_PixelFormat(ct);
80cb93a386Sopenharmony_ci
81cb93a386Sopenharmony_ci            skvm::Color c = p->load(fmt, p->varying(SkColorTypeBytesPerPixel(ct)));
82cb93a386Sopenharmony_ci
83cb93a386Sopenharmony_ci            return SkColorSpaceXformSteps{fSprite, dst}.program(p, uniforms, c);
84cb93a386Sopenharmony_ci        }
85cb93a386Sopenharmony_ci    };
86cb93a386Sopenharmony_ci
87cb93a386Sopenharmony_ci    struct DitherShader : public SkShaderBase {
88cb93a386Sopenharmony_ci        explicit DitherShader(sk_sp<SkShader> shader) : fShader(std::move(shader)) {}
89cb93a386Sopenharmony_ci
90cb93a386Sopenharmony_ci        sk_sp<SkShader> fShader;
91cb93a386Sopenharmony_ci
92cb93a386Sopenharmony_ci        // Only created here temporarily... never serialized.
93cb93a386Sopenharmony_ci        Factory      getFactory() const override { return nullptr; }
94cb93a386Sopenharmony_ci        const char* getTypeName() const override { return "DitherShader"; }
95cb93a386Sopenharmony_ci
96cb93a386Sopenharmony_ci        bool isOpaque() const override { return fShader->isOpaque(); }
97cb93a386Sopenharmony_ci
98cb93a386Sopenharmony_ci        skvm::Color onProgram(skvm::Builder* p,
99cb93a386Sopenharmony_ci                              skvm::Coord device, skvm::Coord local, skvm::Color paint,
100cb93a386Sopenharmony_ci                              const SkMatrixProvider& matrices, const SkMatrix* localM,
101cb93a386Sopenharmony_ci                              const SkColorInfo& dst,
102cb93a386Sopenharmony_ci                              skvm::Uniforms* uniforms, SkArenaAlloc* alloc) const override {
103cb93a386Sopenharmony_ci            // Run our wrapped shader.
104cb93a386Sopenharmony_ci            skvm::Color c = as_SB(fShader)->program(p, device,local, paint,
105cb93a386Sopenharmony_ci                                                    matrices,localM, dst, uniforms,alloc);
106cb93a386Sopenharmony_ci            if (!c) {
107cb93a386Sopenharmony_ci                return {};
108cb93a386Sopenharmony_ci            }
109cb93a386Sopenharmony_ci
110cb93a386Sopenharmony_ci            float rate = 0.0f;
111cb93a386Sopenharmony_ci            switch (dst.colorType()) {
112cb93a386Sopenharmony_ci                case kARGB_4444_SkColorType:    rate =   1/15.0f; break;
113cb93a386Sopenharmony_ci                case   kRGB_565_SkColorType:    rate =   1/63.0f; break;
114cb93a386Sopenharmony_ci                case    kGray_8_SkColorType:
115cb93a386Sopenharmony_ci                case  kRGB_888x_SkColorType:
116cb93a386Sopenharmony_ci                case kRGBA_8888_SkColorType:
117cb93a386Sopenharmony_ci                case kBGRA_8888_SkColorType:
118cb93a386Sopenharmony_ci                case kSRGBA_8888_SkColorType:   rate =  1/255.0f; break;
119cb93a386Sopenharmony_ci                case kRGB_101010x_SkColorType:
120cb93a386Sopenharmony_ci                case kRGBA_1010102_SkColorType:
121cb93a386Sopenharmony_ci                case kBGR_101010x_SkColorType:
122cb93a386Sopenharmony_ci                case kBGRA_1010102_SkColorType: rate = 1/1023.0f; break;
123cb93a386Sopenharmony_ci
124cb93a386Sopenharmony_ci                case kUnknown_SkColorType:
125cb93a386Sopenharmony_ci                case kAlpha_8_SkColorType:
126cb93a386Sopenharmony_ci                case kRGBA_F16_SkColorType:
127cb93a386Sopenharmony_ci                case kRGBA_F16Norm_SkColorType:
128cb93a386Sopenharmony_ci                case kRGBA_F32_SkColorType:
129cb93a386Sopenharmony_ci                case kR8G8_unorm_SkColorType:
130cb93a386Sopenharmony_ci                case kA16_float_SkColorType:
131cb93a386Sopenharmony_ci                case kA16_unorm_SkColorType:
132cb93a386Sopenharmony_ci                case kR16G16_float_SkColorType:
133cb93a386Sopenharmony_ci                case kR16G16_unorm_SkColorType:
134cb93a386Sopenharmony_ci                case kR16G16B16A16_unorm_SkColorType: return c;
135cb93a386Sopenharmony_ci            }
136cb93a386Sopenharmony_ci
137cb93a386Sopenharmony_ci            // See SkRasterPipeline dither stage.
138cb93a386Sopenharmony_ci            // This is 8x8 ordered dithering.  From here we'll only need dx and dx^dy.
139cb93a386Sopenharmony_ci            SkASSERT(local.x.id == device.x.id);
140cb93a386Sopenharmony_ci            SkASSERT(local.y.id == device.y.id);
141cb93a386Sopenharmony_ci            skvm::I32 X =     trunc(device.x - 0.5f),
142cb93a386Sopenharmony_ci                      Y = X ^ trunc(device.y - 0.5f);
143cb93a386Sopenharmony_ci
144cb93a386Sopenharmony_ci            // If X's low bits are abc and Y's def, M is fcebda,
145cb93a386Sopenharmony_ci            // 6 bits producing all values [0,63] shuffled over an 8x8 grid.
146cb93a386Sopenharmony_ci            skvm::I32 M = shl(Y & 1, 5)
147cb93a386Sopenharmony_ci                        | shl(X & 1, 4)
148cb93a386Sopenharmony_ci                        | shl(Y & 2, 2)
149cb93a386Sopenharmony_ci                        | shl(X & 2, 1)
150cb93a386Sopenharmony_ci                        | shr(Y & 4, 1)
151cb93a386Sopenharmony_ci                        | shr(X & 4, 2);
152cb93a386Sopenharmony_ci
153cb93a386Sopenharmony_ci            // Scale to [0,1) by /64, then to (-0.5,0.5) using 63/128 (~0.492) as 0.5-ε,
154cb93a386Sopenharmony_ci            // and finally scale all that by rate.  We keep dither strength strictly
155cb93a386Sopenharmony_ci            // within ±0.5 to not change exact values like 0 or 1.
156cb93a386Sopenharmony_ci
157cb93a386Sopenharmony_ci            // rate could be a uniform, but since it's based on the destination SkColorType,
158cb93a386Sopenharmony_ci            // we can bake it in without hurting the cache hit rate.
159cb93a386Sopenharmony_ci            float scale = rate * (  2/128.0f),
160cb93a386Sopenharmony_ci                  bias  = rate * (-63/128.0f);
161cb93a386Sopenharmony_ci            skvm::F32 dither = to_F32(M) * scale + bias;
162cb93a386Sopenharmony_ci            c.r += dither;
163cb93a386Sopenharmony_ci            c.g += dither;
164cb93a386Sopenharmony_ci            c.b += dither;
165cb93a386Sopenharmony_ci
166cb93a386Sopenharmony_ci            c.r = clamp(c.r, 0.0f, c.a);
167cb93a386Sopenharmony_ci            c.g = clamp(c.g, 0.0f, c.a);
168cb93a386Sopenharmony_ci            c.b = clamp(c.b, 0.0f, c.a);
169cb93a386Sopenharmony_ci            return c;
170cb93a386Sopenharmony_ci        }
171cb93a386Sopenharmony_ci    };
172cb93a386Sopenharmony_ci
173cb93a386Sopenharmony_ci    // This is similar to using SkShaders::Color(paint.getColor4f(), nullptr),
174cb93a386Sopenharmony_ci    // but uses the blitter-provided paint color uniforms instead of pushing its own.
175cb93a386Sopenharmony_ci    struct PaintColorShader : public SkShaderBase {
176cb93a386Sopenharmony_ci        explicit PaintColorShader(bool isOpaque) : fIsOpaque(isOpaque) {}
177cb93a386Sopenharmony_ci
178cb93a386Sopenharmony_ci        const bool fIsOpaque;
179cb93a386Sopenharmony_ci
180cb93a386Sopenharmony_ci        // Only created here temporarily... never serialized.
181cb93a386Sopenharmony_ci        Factory      getFactory() const override { return nullptr; }
182cb93a386Sopenharmony_ci        const char* getTypeName() const override { return "PaintColorShader"; }
183cb93a386Sopenharmony_ci
184cb93a386Sopenharmony_ci        bool isOpaque() const override { return fIsOpaque; }
185cb93a386Sopenharmony_ci
186cb93a386Sopenharmony_ci        skvm::Color onProgram(skvm::Builder*,
187cb93a386Sopenharmony_ci                              skvm::Coord, skvm::Coord, skvm::Color paint,
188cb93a386Sopenharmony_ci                              const SkMatrixProvider&, const SkMatrix*, const SkColorInfo&,
189cb93a386Sopenharmony_ci                              skvm::Uniforms*, SkArenaAlloc*) const override {
190cb93a386Sopenharmony_ci            // Incoming `paint` is unpremul in the destination color space,
191cb93a386Sopenharmony_ci            // so we just need to premul it.
192cb93a386Sopenharmony_ci            return premul(paint);
193cb93a386Sopenharmony_ci        }
194cb93a386Sopenharmony_ci    };
195cb93a386Sopenharmony_ci}  // namespace
196cb93a386Sopenharmony_ci
197cb93a386Sopenharmony_cibool SkVMBlitter::Key::operator==(const Key& that) const {
198cb93a386Sopenharmony_ci    return this->shader      == that.shader
199cb93a386Sopenharmony_ci        && this->clip        == that.clip
200cb93a386Sopenharmony_ci        && this->blender     == that.blender
201cb93a386Sopenharmony_ci        && this->colorSpace  == that.colorSpace
202cb93a386Sopenharmony_ci        && this->colorType   == that.colorType
203cb93a386Sopenharmony_ci        && this->alphaType   == that.alphaType
204cb93a386Sopenharmony_ci        && this->coverage    == that.coverage;
205cb93a386Sopenharmony_ci}
206cb93a386Sopenharmony_ci
207cb93a386Sopenharmony_ciSkVMBlitter::Key SkVMBlitter::Key::withCoverage(Coverage c) const {
208cb93a386Sopenharmony_ci    Key k = *this;
209cb93a386Sopenharmony_ci    k.coverage = SkToU8(c);
210cb93a386Sopenharmony_ci    return k;
211cb93a386Sopenharmony_ci}
212cb93a386Sopenharmony_ci
213cb93a386Sopenharmony_ciSkVMBlitter::Params SkVMBlitter::Params::withCoverage(Coverage c) const {
214cb93a386Sopenharmony_ci    Params p = *this;
215cb93a386Sopenharmony_ci    p.coverage = c;
216cb93a386Sopenharmony_ci    return p;
217cb93a386Sopenharmony_ci}
218cb93a386Sopenharmony_ci
219cb93a386Sopenharmony_ciSkVMBlitter::Params SkVMBlitter::EffectiveParams(const SkPixmap& device,
220cb93a386Sopenharmony_ci                                                 const SkPixmap* sprite,
221cb93a386Sopenharmony_ci                                                 SkPaint paint,
222cb93a386Sopenharmony_ci                                                 const SkMatrixProvider& matrices,
223cb93a386Sopenharmony_ci                                                 sk_sp<SkShader> clip) {
224cb93a386Sopenharmony_ci    // Sprites take priority over any shader.  (There's rarely one set, and it's meaningless.)
225cb93a386Sopenharmony_ci    if (sprite) {
226cb93a386Sopenharmony_ci        paint.setShader(sk_make_sp<SpriteShader>(*sprite));
227cb93a386Sopenharmony_ci    }
228cb93a386Sopenharmony_ci
229cb93a386Sopenharmony_ci    // Normal blitters will have already folded color filters into their shader,
230cb93a386Sopenharmony_ci    // but we may still need to do that here for SpriteShaders.
231cb93a386Sopenharmony_ci    if (paint.getColorFilter()) {
232cb93a386Sopenharmony_ci        SkPaintPriv::RemoveColorFilter(&paint, device.colorSpace());
233cb93a386Sopenharmony_ci    }
234cb93a386Sopenharmony_ci    SkASSERT(!paint.getColorFilter());
235cb93a386Sopenharmony_ci
236cb93a386Sopenharmony_ci    // If there's no explicit shader, the paint color is the shader,
237cb93a386Sopenharmony_ci    // but if there is a shader, it's modulated by the paint alpha.
238cb93a386Sopenharmony_ci    sk_sp<SkShader> shader = paint.refShader();
239cb93a386Sopenharmony_ci    if (!shader) {
240cb93a386Sopenharmony_ci        shader = sk_make_sp<PaintColorShader>(paint.getColor4f().isOpaque());
241cb93a386Sopenharmony_ci    } else if (paint.getAlphaf() < 1.0f) {
242cb93a386Sopenharmony_ci        shader = sk_make_sp<SkColorFilterShader>(std::move(shader),
243cb93a386Sopenharmony_ci                                                 paint.getAlphaf(),
244cb93a386Sopenharmony_ci                                                 sk_make_sp<NoopColorFilter>());
245cb93a386Sopenharmony_ci    }
246cb93a386Sopenharmony_ci
247cb93a386Sopenharmony_ci    // Add dither to the end of the shader pipeline if requested and needed.
248cb93a386Sopenharmony_ci    if (paint.isDither() && !as_SB(shader)->isConstant()) {
249cb93a386Sopenharmony_ci        shader = sk_make_sp<DitherShader>(std::move(shader));
250cb93a386Sopenharmony_ci    }
251cb93a386Sopenharmony_ci
252cb93a386Sopenharmony_ci    // Add the blender.
253cb93a386Sopenharmony_ci    sk_sp<SkBlender> blender = paint.refBlender();
254cb93a386Sopenharmony_ci    if (!blender) {
255cb93a386Sopenharmony_ci        blender = SkBlender::Mode(SkBlendMode::kSrcOver);
256cb93a386Sopenharmony_ci    }
257cb93a386Sopenharmony_ci
258cb93a386Sopenharmony_ci    // The most common blend mode is SrcOver, and it can be strength-reduced
259cb93a386Sopenharmony_ci    // _greatly_ to Src mode when the shader is opaque.
260cb93a386Sopenharmony_ci    //
261cb93a386Sopenharmony_ci    // In general all the information we use to make decisions here need to
262cb93a386Sopenharmony_ci    // be reflected in Params and Key to make program caching sound, and it
263cb93a386Sopenharmony_ci    // might appear that shader->isOpaque() is a property of the shader's
264cb93a386Sopenharmony_ci    // uniforms than its fundamental program structure and so unsafe to use.
265cb93a386Sopenharmony_ci    //
266cb93a386Sopenharmony_ci    // Opacity is such a powerful property that SkShaderBase::program()
267cb93a386Sopenharmony_ci    // forces opacity for any shader subclass that claims isOpaque(), so
268cb93a386Sopenharmony_ci    // the opaque bit is strongly guaranteed to be part of the program and
269cb93a386Sopenharmony_ci    // not just a property of the uniforms.  The shader program hash includes
270cb93a386Sopenharmony_ci    // this information, making it safe to use anywhere in the blitter codegen.
271cb93a386Sopenharmony_ci    if (as_BB(blender)->asBlendMode() == SkBlendMode::kSrcOver && shader->isOpaque()) {
272cb93a386Sopenharmony_ci        blender = SkBlender::Mode(SkBlendMode::kSrc);
273cb93a386Sopenharmony_ci    }
274cb93a386Sopenharmony_ci
275cb93a386Sopenharmony_ci    SkColor4f paintColor = paint.getColor4f();
276cb93a386Sopenharmony_ci    SkColorSpaceXformSteps{sk_srgb_singleton(), kUnpremul_SkAlphaType,
277cb93a386Sopenharmony_ci                           device.colorSpace(), kUnpremul_SkAlphaType}
278cb93a386Sopenharmony_ci            .apply(paintColor.vec());
279cb93a386Sopenharmony_ci
280cb93a386Sopenharmony_ci    return {
281cb93a386Sopenharmony_ci        std::move(shader),
282cb93a386Sopenharmony_ci        std::move(clip),
283cb93a386Sopenharmony_ci        std::move(blender),
284cb93a386Sopenharmony_ci        { device.colorType(), device.alphaType(), device.refColorSpace() },
285cb93a386Sopenharmony_ci        Coverage::Full,  // Placeholder... withCoverage() will change as needed.
286cb93a386Sopenharmony_ci        paintColor,
287cb93a386Sopenharmony_ci        matrices,
288cb93a386Sopenharmony_ci    };
289cb93a386Sopenharmony_ci}
290cb93a386Sopenharmony_ci
291cb93a386Sopenharmony_ciskvm::Color SkVMBlitter::DstColor(skvm::Builder* p, const Params& params) {
292cb93a386Sopenharmony_ci    skvm::PixelFormat dstFormat = skvm::SkColorType_to_PixelFormat(params.dst.colorType());
293cb93a386Sopenharmony_ci    skvm::Ptr dst_ptr = p->varying(SkColorTypeBytesPerPixel(params.dst.colorType()));
294cb93a386Sopenharmony_ci    return p->load(dstFormat, dst_ptr);
295cb93a386Sopenharmony_ci}
296cb93a386Sopenharmony_ci
297cb93a386Sopenharmony_civoid SkVMBlitter::BuildProgram(skvm::Builder* p, const Params& params,
298cb93a386Sopenharmony_ci                               skvm::Uniforms* uniforms, SkArenaAlloc* alloc) {
299cb93a386Sopenharmony_ci    // First two arguments are always uniforms and the destination buffer.
300cb93a386Sopenharmony_ci    uniforms->base    = p->uniform();
301cb93a386Sopenharmony_ci    skvm::Ptr dst_ptr = p->varying(SkColorTypeBytesPerPixel(params.dst.colorType()));
302cb93a386Sopenharmony_ci    // A SpriteShader (in this file) may next use one argument as its varying source.
303cb93a386Sopenharmony_ci    // Subsequent arguments depend on params.coverage:
304cb93a386Sopenharmony_ci    //    - Full:      (no more arguments)
305cb93a386Sopenharmony_ci    //    - Mask3D:    mul varying, add varying, 8-bit coverage varying
306cb93a386Sopenharmony_ci    //    - MaskA8:    8-bit coverage varying
307cb93a386Sopenharmony_ci    //    - MaskLCD16: 565 coverage varying
308cb93a386Sopenharmony_ci    //    - UniformF:  float coverage uniform
309cb93a386Sopenharmony_ci
310cb93a386Sopenharmony_ci    skvm::Coord device = device_coord(p, uniforms);
311cb93a386Sopenharmony_ci    skvm::Color paint = p->uniformColor(params.paint, uniforms);
312cb93a386Sopenharmony_ci
313cb93a386Sopenharmony_ci    // See note about arguments above: a SpriteShader will call p->arg() once during program().
314cb93a386Sopenharmony_ci    skvm::Color src = as_SB(params.shader)->program(p, device, /*local=*/device, paint,
315cb93a386Sopenharmony_ci                                                    params.matrices, /*localM=*/nullptr,
316cb93a386Sopenharmony_ci                                                    params.dst, uniforms, alloc);
317cb93a386Sopenharmony_ci    SkASSERT(src);
318cb93a386Sopenharmony_ci    if (params.coverage == Coverage::Mask3D) {
319cb93a386Sopenharmony_ci        skvm::F32 M = from_unorm(8, p->load8(p->varying<uint8_t>())),
320cb93a386Sopenharmony_ci                  A = from_unorm(8, p->load8(p->varying<uint8_t>()));
321cb93a386Sopenharmony_ci
322cb93a386Sopenharmony_ci        src.r = min(src.r * M + A, src.a);
323cb93a386Sopenharmony_ci        src.g = min(src.g * M + A, src.a);
324cb93a386Sopenharmony_ci        src.b = min(src.b * M + A, src.a);
325cb93a386Sopenharmony_ci    }
326cb93a386Sopenharmony_ci
327cb93a386Sopenharmony_ci    // GL clamps all its color channels to limits of the format just before the blend step (~here).
328cb93a386Sopenharmony_ci    // TODO: Below, we also clamp after the blend step. If we can prove that none of the work here
329cb93a386Sopenharmony_ci    // (especially blending, for built-in blend modes) will produce colors outside [0, 1] we may be
330cb93a386Sopenharmony_ci    // able to skip the second clamp. For now, we clamp twice.
331cb93a386Sopenharmony_ci    if (SkColorTypeIsNormalized(params.dst.colorType())) {
332cb93a386Sopenharmony_ci        src = clamp01(src);
333cb93a386Sopenharmony_ci    }
334cb93a386Sopenharmony_ci
335cb93a386Sopenharmony_ci    // Load the destination color.
336cb93a386Sopenharmony_ci    skvm::PixelFormat dstFormat = skvm::SkColorType_to_PixelFormat(params.dst.colorType());
337cb93a386Sopenharmony_ci    skvm::Color dst = p->load(dstFormat, dst_ptr);
338cb93a386Sopenharmony_ci    if (params.dst.isOpaque()) {
339cb93a386Sopenharmony_ci        // When a destination is known opaque, we may assume it both starts and stays fully
340cb93a386Sopenharmony_ci        // opaque, ignoring any math that disagrees.  This sometimes trims a little work.
341cb93a386Sopenharmony_ci        dst.a = p->splat(1.0f);
342cb93a386Sopenharmony_ci    } else if (params.dst.alphaType() == kUnpremul_SkAlphaType) {
343cb93a386Sopenharmony_ci        // All our blending works in terms of premul.
344cb93a386Sopenharmony_ci        dst = premul(dst);
345cb93a386Sopenharmony_ci    }
346cb93a386Sopenharmony_ci
347cb93a386Sopenharmony_ci    // Load coverage.
348cb93a386Sopenharmony_ci    skvm::Color cov;
349cb93a386Sopenharmony_ci    switch (params.coverage) {
350cb93a386Sopenharmony_ci        case Coverage::Full:
351cb93a386Sopenharmony_ci            cov.r = cov.g = cov.b = cov.a = p->splat(1.0f);
352cb93a386Sopenharmony_ci            break;
353cb93a386Sopenharmony_ci
354cb93a386Sopenharmony_ci        case Coverage::UniformF:
355cb93a386Sopenharmony_ci            cov.r = cov.g = cov.b = cov.a = p->uniformF(p->uniform(), 0);
356cb93a386Sopenharmony_ci            break;
357cb93a386Sopenharmony_ci
358cb93a386Sopenharmony_ci        case Coverage::Mask3D:
359cb93a386Sopenharmony_ci        case Coverage::MaskA8:
360cb93a386Sopenharmony_ci            cov.r = cov.g = cov.b = cov.a = from_unorm(8, p->load8(p->varying<uint8_t>()));
361cb93a386Sopenharmony_ci            break;
362cb93a386Sopenharmony_ci
363cb93a386Sopenharmony_ci        case Coverage::MaskLCD16: {
364cb93a386Sopenharmony_ci            skvm::PixelFormat fmt = skvm::SkColorType_to_PixelFormat(kRGB_565_SkColorType);
365cb93a386Sopenharmony_ci            cov = p->load(fmt, p->varying<uint16_t>());
366cb93a386Sopenharmony_ci            cov.a = select(src.a < dst.a, min(cov.r, min(cov.g, cov.b)),
367cb93a386Sopenharmony_ci                           max(cov.r, max(cov.g, cov.b)));
368cb93a386Sopenharmony_ci        } break;
369cb93a386Sopenharmony_ci    }
370cb93a386Sopenharmony_ci    if (params.clip) {
371cb93a386Sopenharmony_ci        skvm::Color clip = as_SB(params.clip)->program(p, device, /*local=*/device, paint,
372cb93a386Sopenharmony_ci                                                       params.matrices, /*localM=*/nullptr,
373cb93a386Sopenharmony_ci                                                       params.dst, uniforms, alloc);
374cb93a386Sopenharmony_ci        SkAssertResult(clip);
375cb93a386Sopenharmony_ci        cov.r *= clip.a;  // We use the alpha channel of clip for all four.
376cb93a386Sopenharmony_ci        cov.g *= clip.a;
377cb93a386Sopenharmony_ci        cov.b *= clip.a;
378cb93a386Sopenharmony_ci        cov.a *= clip.a;
379cb93a386Sopenharmony_ci    }
380cb93a386Sopenharmony_ci
381cb93a386Sopenharmony_ci    const SkBlenderBase* blender = as_BB(params.blender);
382cb93a386Sopenharmony_ci    const auto as_blendmode = blender->asBlendMode();
383cb93a386Sopenharmony_ci
384cb93a386Sopenharmony_ci    // The math for some blend modes lets us fold coverage into src before the blend, which is
385cb93a386Sopenharmony_ci    // simpler than the canonical post-blend lerp().
386cb93a386Sopenharmony_ci    bool applyPostBlendCoverage = true;
387cb93a386Sopenharmony_ci    if (as_blendmode &&
388cb93a386Sopenharmony_ci        SkBlendMode_ShouldPreScaleCoverage(as_blendmode.value(),
389cb93a386Sopenharmony_ci                                           params.coverage == Coverage::MaskLCD16)) {
390cb93a386Sopenharmony_ci        applyPostBlendCoverage = false;
391cb93a386Sopenharmony_ci        src.r *= cov.r;
392cb93a386Sopenharmony_ci        src.g *= cov.g;
393cb93a386Sopenharmony_ci        src.b *= cov.b;
394cb93a386Sopenharmony_ci        src.a *= cov.a;
395cb93a386Sopenharmony_ci    }
396cb93a386Sopenharmony_ci
397cb93a386Sopenharmony_ci    // Apply our blend function to the computed color.
398cb93a386Sopenharmony_ci    src = blender->program(p, src, dst, params.dst, uniforms, alloc);
399cb93a386Sopenharmony_ci
400cb93a386Sopenharmony_ci    if (applyPostBlendCoverage) {
401cb93a386Sopenharmony_ci        src.r = lerp(dst.r, src.r, cov.r);
402cb93a386Sopenharmony_ci        src.g = lerp(dst.g, src.g, cov.g);
403cb93a386Sopenharmony_ci        src.b = lerp(dst.b, src.b, cov.b);
404cb93a386Sopenharmony_ci        src.a = lerp(dst.a, src.a, cov.a);
405cb93a386Sopenharmony_ci    }
406cb93a386Sopenharmony_ci
407cb93a386Sopenharmony_ci    if (params.dst.isOpaque()) {
408cb93a386Sopenharmony_ci        // (See the note above when loading the destination color.)
409cb93a386Sopenharmony_ci        src.a = p->splat(1.0f);
410cb93a386Sopenharmony_ci    } else if (params.dst.alphaType() == kUnpremul_SkAlphaType) {
411cb93a386Sopenharmony_ci        src = unpremul(src);
412cb93a386Sopenharmony_ci    }
413cb93a386Sopenharmony_ci
414cb93a386Sopenharmony_ci    // Clamp to fit destination color format if needed.
415cb93a386Sopenharmony_ci    if (SkColorTypeIsNormalized(params.dst.colorType())) {
416cb93a386Sopenharmony_ci        src = clamp01(src);
417cb93a386Sopenharmony_ci    }
418cb93a386Sopenharmony_ci
419cb93a386Sopenharmony_ci    // Write it out!
420cb93a386Sopenharmony_ci    store(dstFormat, dst_ptr, src);
421cb93a386Sopenharmony_ci}
422cb93a386Sopenharmony_ci
423cb93a386Sopenharmony_ci// If BuildProgram() can't build this program, CacheKey() sets *ok to false.
424cb93a386Sopenharmony_ciSkVMBlitter::Key SkVMBlitter::CacheKey(
425cb93a386Sopenharmony_ci        const Params& params, skvm::Uniforms* uniforms, SkArenaAlloc* alloc, bool* ok) {
426cb93a386Sopenharmony_ci    // Take care to match buildProgram()'s reuse of the paint color uniforms.
427cb93a386Sopenharmony_ci    skvm::Uniform r = uniforms->pushF(params.paint.fR),
428cb93a386Sopenharmony_ci                  g = uniforms->pushF(params.paint.fG),
429cb93a386Sopenharmony_ci                  b = uniforms->pushF(params.paint.fB),
430cb93a386Sopenharmony_ci                  a = uniforms->pushF(params.paint.fA);
431cb93a386Sopenharmony_ci
432cb93a386Sopenharmony_ci    auto hash_shader = [&](skvm::Builder& p, const sk_sp<SkShader>& shader,
433cb93a386Sopenharmony_ci                           skvm::Color* outColor) {
434cb93a386Sopenharmony_ci        const SkShaderBase* sb = as_SB(shader);
435cb93a386Sopenharmony_ci
436cb93a386Sopenharmony_ci        skvm::Coord device = device_coord(&p, uniforms);
437cb93a386Sopenharmony_ci        skvm::Color paint = {
438cb93a386Sopenharmony_ci            p.uniformF(r),
439cb93a386Sopenharmony_ci            p.uniformF(g),
440cb93a386Sopenharmony_ci            p.uniformF(b),
441cb93a386Sopenharmony_ci            p.uniformF(a),
442cb93a386Sopenharmony_ci        };
443cb93a386Sopenharmony_ci
444cb93a386Sopenharmony_ci        uint64_t hash = 0;
445cb93a386Sopenharmony_ci        *outColor = sb->program(&p, device, /*local=*/device, paint, params.matrices,
446cb93a386Sopenharmony_ci                /*localM=*/nullptr, params.dst, uniforms, alloc);
447cb93a386Sopenharmony_ci        if (*outColor) {
448cb93a386Sopenharmony_ci            hash = p.hash();
449cb93a386Sopenharmony_ci            // p.hash() folds in all instructions to produce r,g,b,a but does not know
450cb93a386Sopenharmony_ci            // precisely which value we'll treat as which channel.  Imagine the shader
451cb93a386Sopenharmony_ci            // called std::swap(*r,*b)... it draws differently, but p.hash() is unchanged.
452cb93a386Sopenharmony_ci            // We'll fold the hash of their IDs in order to disambiguate.
453cb93a386Sopenharmony_ci            const skvm::Val outputs[] = {
454cb93a386Sopenharmony_ci                outColor->r.id,
455cb93a386Sopenharmony_ci                outColor->g.id,
456cb93a386Sopenharmony_ci                outColor->b.id,
457cb93a386Sopenharmony_ci                outColor->a.id
458cb93a386Sopenharmony_ci            };
459cb93a386Sopenharmony_ci            hash ^= SkOpts::hash(outputs, sizeof(outputs));
460cb93a386Sopenharmony_ci        } else {
461cb93a386Sopenharmony_ci            *ok = false;
462cb93a386Sopenharmony_ci        }
463cb93a386Sopenharmony_ci        return hash;
464cb93a386Sopenharmony_ci    };
465cb93a386Sopenharmony_ci
466cb93a386Sopenharmony_ci    // Use this builder for shader, clip and blender, so that color objects that pass
467cb93a386Sopenharmony_ci    // from one to the other all 'make sense' -- i.e. have the same builder and/or have
468cb93a386Sopenharmony_ci    // meaningful values for the hash.
469cb93a386Sopenharmony_ci    //
470cb93a386Sopenharmony_ci    // Question: better if we just pass in mock uniform colors, so we don't need to
471cb93a386Sopenharmony_ci    //           explicitly use the output color from one stage as input to another?
472cb93a386Sopenharmony_ci    //
473cb93a386Sopenharmony_ci    skvm::Builder p;
474cb93a386Sopenharmony_ci
475cb93a386Sopenharmony_ci    // Calculate a hash for the color shader.
476cb93a386Sopenharmony_ci    SkASSERT(params.shader);
477cb93a386Sopenharmony_ci    skvm::Color src;
478cb93a386Sopenharmony_ci    uint64_t shaderHash = hash_shader(p, params.shader, &src);
479cb93a386Sopenharmony_ci
480cb93a386Sopenharmony_ci    // Calculate a hash for the clip shader, if one exists.
481cb93a386Sopenharmony_ci    uint64_t clipHash = 0;
482cb93a386Sopenharmony_ci    if (params.clip) {
483cb93a386Sopenharmony_ci        skvm::Color cov;
484cb93a386Sopenharmony_ci        clipHash = hash_shader(p, params.clip, &cov);
485cb93a386Sopenharmony_ci        if (clipHash == 0) {
486cb93a386Sopenharmony_ci            clipHash = 1;
487cb93a386Sopenharmony_ci        }
488cb93a386Sopenharmony_ci    }
489cb93a386Sopenharmony_ci
490cb93a386Sopenharmony_ci    // Calculate a hash for the blender.
491cb93a386Sopenharmony_ci    uint64_t blendHash = 0;
492cb93a386Sopenharmony_ci    if (auto bm = as_BB(params.blender)->asBlendMode()) {
493cb93a386Sopenharmony_ci        blendHash = static_cast<uint8_t>(bm.value());
494cb93a386Sopenharmony_ci    } else if (*ok) {
495cb93a386Sopenharmony_ci        const SkBlenderBase* blender = as_BB(params.blender);
496cb93a386Sopenharmony_ci
497cb93a386Sopenharmony_ci        skvm::Color dst = DstColor(&p, params);
498cb93a386Sopenharmony_ci        skvm::Color outColor = blender->program(&p, src, dst, params.dst, uniforms, alloc);
499cb93a386Sopenharmony_ci        if (outColor) {
500cb93a386Sopenharmony_ci            blendHash = p.hash();
501cb93a386Sopenharmony_ci            // Like in `hash_shader` above, we must fold the color component IDs into our hash.
502cb93a386Sopenharmony_ci            const skvm::Val outputs[] = {
503cb93a386Sopenharmony_ci                outColor.r.id,
504cb93a386Sopenharmony_ci                outColor.g.id,
505cb93a386Sopenharmony_ci                outColor.b.id,
506cb93a386Sopenharmony_ci                outColor.a.id
507cb93a386Sopenharmony_ci            };
508cb93a386Sopenharmony_ci            blendHash ^= SkOpts::hash(outputs, sizeof(outputs));
509cb93a386Sopenharmony_ci        } else {
510cb93a386Sopenharmony_ci            *ok = false;
511cb93a386Sopenharmony_ci        }
512cb93a386Sopenharmony_ci        if (blendHash == 0) {
513cb93a386Sopenharmony_ci            blendHash = 1;
514cb93a386Sopenharmony_ci        }
515cb93a386Sopenharmony_ci    }
516cb93a386Sopenharmony_ci
517cb93a386Sopenharmony_ci    return {
518cb93a386Sopenharmony_ci        shaderHash,
519cb93a386Sopenharmony_ci        clipHash,
520cb93a386Sopenharmony_ci        blendHash,
521cb93a386Sopenharmony_ci        params.dst.colorSpace() ? params.dst.colorSpace()->hash() : 0,
522cb93a386Sopenharmony_ci        SkToU8(params.dst.colorType()),
523cb93a386Sopenharmony_ci        SkToU8(params.dst.alphaType()),
524cb93a386Sopenharmony_ci        SkToU8(params.coverage),
525cb93a386Sopenharmony_ci    };
526cb93a386Sopenharmony_ci}
527cb93a386Sopenharmony_ci
528cb93a386Sopenharmony_ciSkVMBlitter::SkVMBlitter(const SkPixmap& device,
529cb93a386Sopenharmony_ci                         const SkPaint& paint,
530cb93a386Sopenharmony_ci                         const SkPixmap* sprite,
531cb93a386Sopenharmony_ci                         SkIPoint spriteOffset,
532cb93a386Sopenharmony_ci                         const SkMatrixProvider& matrices,
533cb93a386Sopenharmony_ci                         sk_sp<SkShader> clip,
534cb93a386Sopenharmony_ci                         bool* ok)
535cb93a386Sopenharmony_ci        : fDevice(device)
536cb93a386Sopenharmony_ci        , fSprite(sprite ? *sprite : SkPixmap{})
537cb93a386Sopenharmony_ci        , fSpriteOffset(spriteOffset)
538cb93a386Sopenharmony_ci        , fUniforms(skvm::UPtr{0}, kBlitterUniformsCount)
539cb93a386Sopenharmony_ci        , fParams(EffectiveParams(device, sprite, paint, matrices, std::move(clip)))
540cb93a386Sopenharmony_ci        , fKey(CacheKey(fParams, &fUniforms, &fAlloc, ok)) {}
541cb93a386Sopenharmony_ci
542cb93a386Sopenharmony_ciSkVMBlitter::~SkVMBlitter() {
543cb93a386Sopenharmony_ci    if (SkLRUCache<Key, skvm::Program>* cache = TryAcquireProgramCache()) {
544cb93a386Sopenharmony_ci        auto cache_program = [&](skvm::Program&& program, Coverage coverage) {
545cb93a386Sopenharmony_ci            if (!program.empty()) {
546cb93a386Sopenharmony_ci                cache->insert_or_update(fKey.withCoverage(coverage), std::move(program));
547cb93a386Sopenharmony_ci            }
548cb93a386Sopenharmony_ci        };
549cb93a386Sopenharmony_ci        cache_program(std::move(fBlitH),         Coverage::Full);
550cb93a386Sopenharmony_ci        cache_program(std::move(fBlitAntiH),     Coverage::UniformF);
551cb93a386Sopenharmony_ci        cache_program(std::move(fBlitMaskA8),    Coverage::MaskA8);
552cb93a386Sopenharmony_ci        cache_program(std::move(fBlitMask3D),    Coverage::Mask3D);
553cb93a386Sopenharmony_ci        cache_program(std::move(fBlitMaskLCD16), Coverage::MaskLCD16);
554cb93a386Sopenharmony_ci
555cb93a386Sopenharmony_ci        ReleaseProgramCache();
556cb93a386Sopenharmony_ci    }
557cb93a386Sopenharmony_ci}
558cb93a386Sopenharmony_ci
559cb93a386Sopenharmony_ciSkLRUCache<SkVMBlitter::Key, skvm::Program>* SkVMBlitter::TryAcquireProgramCache() {
560cb93a386Sopenharmony_ci#if defined(SKVM_JIT)
561cb93a386Sopenharmony_ci    thread_local static SkLRUCache<Key, skvm::Program> cache{64};
562cb93a386Sopenharmony_ci    return &cache;
563cb93a386Sopenharmony_ci#else
564cb93a386Sopenharmony_ci    // iOS now supports thread_local since iOS 9.
565cb93a386Sopenharmony_ci    // On the other hand, we'll never be able to JIT there anyway.
566cb93a386Sopenharmony_ci    // It's probably fine to not cache any interpreted programs, anywhere.
567cb93a386Sopenharmony_ci    return nullptr;
568cb93a386Sopenharmony_ci#endif
569cb93a386Sopenharmony_ci}
570cb93a386Sopenharmony_ci
571cb93a386Sopenharmony_ciSkString SkVMBlitter::DebugName(const Key& key) {
572cb93a386Sopenharmony_ci    return SkStringPrintf("Shader-%" PRIx64 "_Clip-%" PRIx64 "_Blender-%" PRIx64
573cb93a386Sopenharmony_ci                          "_CS-%" PRIx64 "_CT-%d_AT-%d_Cov-%d",
574cb93a386Sopenharmony_ci                          key.shader,
575cb93a386Sopenharmony_ci                          key.clip,
576cb93a386Sopenharmony_ci                          key.blender,
577cb93a386Sopenharmony_ci                          key.colorSpace,
578cb93a386Sopenharmony_ci                          key.colorType,
579cb93a386Sopenharmony_ci                          key.alphaType,
580cb93a386Sopenharmony_ci                          key.coverage);
581cb93a386Sopenharmony_ci}
582cb93a386Sopenharmony_ci
583cb93a386Sopenharmony_civoid SkVMBlitter::ReleaseProgramCache() {}
584cb93a386Sopenharmony_ci
585cb93a386Sopenharmony_ciskvm::Program SkVMBlitter::buildProgram(Coverage coverage) {
586cb93a386Sopenharmony_ci    Key key = fKey.withCoverage(coverage);
587cb93a386Sopenharmony_ci    {
588cb93a386Sopenharmony_ci        skvm::Program p;
589cb93a386Sopenharmony_ci        if (SkLRUCache<Key, skvm::Program>* cache = TryAcquireProgramCache()) {
590cb93a386Sopenharmony_ci            if (skvm::Program* found = cache->find(key)) {
591cb93a386Sopenharmony_ci                p = std::move(*found);
592cb93a386Sopenharmony_ci            }
593cb93a386Sopenharmony_ci            ReleaseProgramCache();
594cb93a386Sopenharmony_ci        }
595cb93a386Sopenharmony_ci        if (!p.empty()) {
596cb93a386Sopenharmony_ci            return p;
597cb93a386Sopenharmony_ci        }
598cb93a386Sopenharmony_ci    }
599cb93a386Sopenharmony_ci    // We don't really _need_ to rebuild fUniforms here.
600cb93a386Sopenharmony_ci    // It's just more natural to have effects unconditionally emit them,
601cb93a386Sopenharmony_ci    // and more natural to rebuild fUniforms than to emit them into a temporary buffer.
602cb93a386Sopenharmony_ci    // fUniforms should reuse the exact same memory, so this is very cheap.
603cb93a386Sopenharmony_ci    SkDEBUGCODE(size_t prev = fUniforms.buf.size();)
604cb93a386Sopenharmony_ci    fUniforms.buf.resize(kBlitterUniformsCount);
605cb93a386Sopenharmony_ci    skvm::Builder builder;
606cb93a386Sopenharmony_ci    BuildProgram(&builder, fParams.withCoverage(coverage), &fUniforms, &fAlloc);
607cb93a386Sopenharmony_ci    SkASSERTF(fUniforms.buf.size() == prev,
608cb93a386Sopenharmony_ci              "%zu, prev was %zu", fUniforms.buf.size(), prev);
609cb93a386Sopenharmony_ci
610cb93a386Sopenharmony_ci    skvm::Program program = builder.done(DebugName(key).c_str());
611cb93a386Sopenharmony_ci    if (false) {
612cb93a386Sopenharmony_ci        static std::atomic<int> missed{0},
613cb93a386Sopenharmony_ci                total{0};
614cb93a386Sopenharmony_ci        if (!program.hasJIT()) {
615cb93a386Sopenharmony_ci            SkDebugf("\ncouldn't JIT %s\n", DebugName(key).c_str());
616cb93a386Sopenharmony_ci            builder.dump();
617cb93a386Sopenharmony_ci            program.dump();
618cb93a386Sopenharmony_ci
619cb93a386Sopenharmony_ci            missed++;
620cb93a386Sopenharmony_ci        }
621cb93a386Sopenharmony_ci        if (0 == total++) {
622cb93a386Sopenharmony_ci            atexit([]{ SkDebugf("SkVMBlitter compiled %d programs, %d without JIT.\n",
623cb93a386Sopenharmony_ci                                total.load(), missed.load()); });
624cb93a386Sopenharmony_ci        }
625cb93a386Sopenharmony_ci    }
626cb93a386Sopenharmony_ci    return program;
627cb93a386Sopenharmony_ci}
628cb93a386Sopenharmony_ci
629cb93a386Sopenharmony_civoid SkVMBlitter::updateUniforms(int right, int y) {
630cb93a386Sopenharmony_ci    BlitterUniforms uniforms{right, y};
631cb93a386Sopenharmony_ci    memcpy(fUniforms.buf.data(), &uniforms, sizeof(BlitterUniforms));
632cb93a386Sopenharmony_ci}
633cb93a386Sopenharmony_ci
634cb93a386Sopenharmony_ciconst void* SkVMBlitter::isSprite(int x, int y) const {
635cb93a386Sopenharmony_ci    if (fSprite.colorType() != kUnknown_SkColorType) {
636cb93a386Sopenharmony_ci        return fSprite.addr(x - fSpriteOffset.x(),
637cb93a386Sopenharmony_ci                            y - fSpriteOffset.y());
638cb93a386Sopenharmony_ci    }
639cb93a386Sopenharmony_ci    return nullptr;
640cb93a386Sopenharmony_ci}
641cb93a386Sopenharmony_ci
642cb93a386Sopenharmony_civoid SkVMBlitter::blitH(int x, int y, int w) {
643cb93a386Sopenharmony_ci    if (fBlitH.empty()) {
644cb93a386Sopenharmony_ci        fBlitH = this->buildProgram(Coverage::Full);
645cb93a386Sopenharmony_ci    }
646cb93a386Sopenharmony_ci    this->updateUniforms(x+w, y);
647cb93a386Sopenharmony_ci    if (const void* sprite = this->isSprite(x,y)) {
648cb93a386Sopenharmony_ci        fBlitH.eval(w, fUniforms.buf.data(), fDevice.addr(x,y), sprite);
649cb93a386Sopenharmony_ci    } else {
650cb93a386Sopenharmony_ci        fBlitH.eval(w, fUniforms.buf.data(), fDevice.addr(x,y));
651cb93a386Sopenharmony_ci    }
652cb93a386Sopenharmony_ci}
653cb93a386Sopenharmony_ci
654cb93a386Sopenharmony_civoid SkVMBlitter::blitAntiH(int x, int y, const SkAlpha cov[], const int16_t runs[]) {
655cb93a386Sopenharmony_ci    if (fBlitAntiH.empty()) {
656cb93a386Sopenharmony_ci        fBlitAntiH = this->buildProgram(Coverage::UniformF);
657cb93a386Sopenharmony_ci    }
658cb93a386Sopenharmony_ci    if (fBlitH.empty()) {
659cb93a386Sopenharmony_ci        fBlitH = this->buildProgram(Coverage::Full);
660cb93a386Sopenharmony_ci    }
661cb93a386Sopenharmony_ci    for (int16_t run = *runs; run > 0; run = *runs) {
662cb93a386Sopenharmony_ci        const SkAlpha coverage = *cov;
663cb93a386Sopenharmony_ci        if (coverage != 0x00) {
664cb93a386Sopenharmony_ci            this->updateUniforms(x+run, y);
665cb93a386Sopenharmony_ci            const void* sprite = this->isSprite(x,y);
666cb93a386Sopenharmony_ci            if (coverage == 0xFF) {
667cb93a386Sopenharmony_ci                if (sprite) {
668cb93a386Sopenharmony_ci                    fBlitH.eval(run, fUniforms.buf.data(), fDevice.addr(x,y), sprite);
669cb93a386Sopenharmony_ci                } else {
670cb93a386Sopenharmony_ci                    fBlitH.eval(run, fUniforms.buf.data(), fDevice.addr(x,y));
671cb93a386Sopenharmony_ci                }
672cb93a386Sopenharmony_ci            } else {
673cb93a386Sopenharmony_ci                const float covF = *cov * (1/255.0f);
674cb93a386Sopenharmony_ci                if (sprite) {
675cb93a386Sopenharmony_ci                    fBlitAntiH.eval(run, fUniforms.buf.data(), fDevice.addr(x,y), sprite, &covF);
676cb93a386Sopenharmony_ci                } else {
677cb93a386Sopenharmony_ci                    fBlitAntiH.eval(run, fUniforms.buf.data(), fDevice.addr(x,y), &covF);
678cb93a386Sopenharmony_ci                }
679cb93a386Sopenharmony_ci            }
680cb93a386Sopenharmony_ci        }
681cb93a386Sopenharmony_ci        x    += run;
682cb93a386Sopenharmony_ci        runs += run;
683cb93a386Sopenharmony_ci        cov  += run;
684cb93a386Sopenharmony_ci    }
685cb93a386Sopenharmony_ci}
686cb93a386Sopenharmony_ci
687cb93a386Sopenharmony_civoid SkVMBlitter::blitMask(const SkMask& mask, const SkIRect& clip) {
688cb93a386Sopenharmony_ci    if (mask.fFormat == SkMask::kBW_Format) {
689cb93a386Sopenharmony_ci        return SkBlitter::blitMask(mask, clip);
690cb93a386Sopenharmony_ci    }
691cb93a386Sopenharmony_ci
692cb93a386Sopenharmony_ci    const skvm::Program* program = nullptr;
693cb93a386Sopenharmony_ci    switch (mask.fFormat) {
694cb93a386Sopenharmony_ci        default: SkUNREACHABLE;     // ARGB and SDF masks shouldn't make it here.
695cb93a386Sopenharmony_ci
696cb93a386Sopenharmony_ci        case SkMask::k3D_Format:
697cb93a386Sopenharmony_ci            if (fBlitMask3D.empty()) {
698cb93a386Sopenharmony_ci                fBlitMask3D = this->buildProgram(Coverage::Mask3D);
699cb93a386Sopenharmony_ci            }
700cb93a386Sopenharmony_ci            program = &fBlitMask3D;
701cb93a386Sopenharmony_ci            break;
702cb93a386Sopenharmony_ci
703cb93a386Sopenharmony_ci        case SkMask::kA8_Format:
704cb93a386Sopenharmony_ci            if (fBlitMaskA8.empty()) {
705cb93a386Sopenharmony_ci                fBlitMaskA8 = this->buildProgram(Coverage::MaskA8);
706cb93a386Sopenharmony_ci            }
707cb93a386Sopenharmony_ci            program = &fBlitMaskA8;
708cb93a386Sopenharmony_ci            break;
709cb93a386Sopenharmony_ci
710cb93a386Sopenharmony_ci        case SkMask::kLCD16_Format:
711cb93a386Sopenharmony_ci            if (fBlitMaskLCD16.empty()) {
712cb93a386Sopenharmony_ci                fBlitMaskLCD16 = this->buildProgram(Coverage::MaskLCD16);
713cb93a386Sopenharmony_ci            }
714cb93a386Sopenharmony_ci            program = &fBlitMaskLCD16;
715cb93a386Sopenharmony_ci            break;
716cb93a386Sopenharmony_ci    }
717cb93a386Sopenharmony_ci
718cb93a386Sopenharmony_ci    SkASSERT(program);
719cb93a386Sopenharmony_ci    if (program) {
720cb93a386Sopenharmony_ci        for (int y = clip.top(); y < clip.bottom(); y++) {
721cb93a386Sopenharmony_ci             int x = clip.left(),
722cb93a386Sopenharmony_ci                 w = clip.width();
723cb93a386Sopenharmony_ci            void* dptr =        fDevice.writable_addr(x,y);
724cb93a386Sopenharmony_ci            auto  mptr = (const uint8_t*)mask.getAddr(x,y);
725cb93a386Sopenharmony_ci            this->updateUniforms(x+w,y);
726cb93a386Sopenharmony_ci
727cb93a386Sopenharmony_ci            if (program == &fBlitMask3D) {
728cb93a386Sopenharmony_ci                size_t plane = mask.computeImageSize();
729cb93a386Sopenharmony_ci                if (const void* sprite = this->isSprite(x,y)) {
730cb93a386Sopenharmony_ci                    program->eval(w, fUniforms.buf.data(), dptr, sprite, mptr + 1*plane
731cb93a386Sopenharmony_ci                                                                       , mptr + 2*plane
732cb93a386Sopenharmony_ci                                                                       , mptr + 0*plane);
733cb93a386Sopenharmony_ci                } else {
734cb93a386Sopenharmony_ci                    program->eval(w, fUniforms.buf.data(), dptr, mptr + 1*plane
735cb93a386Sopenharmony_ci                                                               , mptr + 2*plane
736cb93a386Sopenharmony_ci                                                               , mptr + 0*plane);
737cb93a386Sopenharmony_ci                }
738cb93a386Sopenharmony_ci            } else {
739cb93a386Sopenharmony_ci                if (const void* sprite = this->isSprite(x,y)) {
740cb93a386Sopenharmony_ci                    program->eval(w, fUniforms.buf.data(), dptr, sprite, mptr);
741cb93a386Sopenharmony_ci                } else {
742cb93a386Sopenharmony_ci                    program->eval(w, fUniforms.buf.data(), dptr, mptr);
743cb93a386Sopenharmony_ci                }
744cb93a386Sopenharmony_ci            }
745cb93a386Sopenharmony_ci        }
746cb93a386Sopenharmony_ci    }
747cb93a386Sopenharmony_ci}
748cb93a386Sopenharmony_ci
749cb93a386Sopenharmony_ciSkVMBlitter* SkVMBlitter::Make(const SkPixmap& device,
750cb93a386Sopenharmony_ci                               const SkPaint& paint,
751cb93a386Sopenharmony_ci                               const SkMatrixProvider& matrices,
752cb93a386Sopenharmony_ci                               SkArenaAlloc* alloc,
753cb93a386Sopenharmony_ci                               sk_sp<SkShader> clip) {
754cb93a386Sopenharmony_ci    bool ok = true;
755cb93a386Sopenharmony_ci    SkVMBlitter* blitter = alloc->make<SkVMBlitter>(
756cb93a386Sopenharmony_ci            device, paint, /*sprite=*/nullptr, SkIPoint{0,0}, matrices, std::move(clip), &ok);
757cb93a386Sopenharmony_ci    return ok ? blitter : nullptr;
758cb93a386Sopenharmony_ci}
759cb93a386Sopenharmony_ci
760cb93a386Sopenharmony_ciSkVMBlitter* SkVMBlitter::Make(const SkPixmap& device,
761cb93a386Sopenharmony_ci                               const SkPaint& paint,
762cb93a386Sopenharmony_ci                               const SkPixmap& sprite,
763cb93a386Sopenharmony_ci                               int left, int top,
764cb93a386Sopenharmony_ci                               SkArenaAlloc* alloc,
765cb93a386Sopenharmony_ci                               sk_sp<SkShader> clip) {
766cb93a386Sopenharmony_ci    if (paint.getMaskFilter()) {
767cb93a386Sopenharmony_ci        // TODO: SkVM support for mask filters?  definitely possible!
768cb93a386Sopenharmony_ci        return nullptr;
769cb93a386Sopenharmony_ci    }
770cb93a386Sopenharmony_ci    bool ok = true;
771cb93a386Sopenharmony_ci    auto blitter = alloc->make<SkVMBlitter>(
772cb93a386Sopenharmony_ci            device, paint, &sprite, SkIPoint{left,top},
773cb93a386Sopenharmony_ci            SkSimpleMatrixProvider{SkMatrix{}}, std::move(clip), &ok);
774cb93a386Sopenharmony_ci    return ok ? blitter : nullptr;
775cb93a386Sopenharmony_ci}
776