1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2017 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 <new>
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include "include/core/SkPoint.h"
11cb93a386Sopenharmony_ci#include "include/core/SkPoint3.h"
12cb93a386Sopenharmony_ci#include "include/gpu/GrRecordingContext.h"
13cb93a386Sopenharmony_ci#include "include/private/SkFloatingPoint.h"
14cb93a386Sopenharmony_ci#include "include/private/SkTo.h"
15cb93a386Sopenharmony_ci#include "src/core/SkMathPriv.h"
16cb93a386Sopenharmony_ci#include "src/core/SkMatrixPriv.h"
17cb93a386Sopenharmony_ci#include "src/core/SkRectPriv.h"
18cb93a386Sopenharmony_ci#include "src/gpu/GrAppliedClip.h"
19cb93a386Sopenharmony_ci#include "src/gpu/GrCaps.h"
20cb93a386Sopenharmony_ci#include "src/gpu/GrDrawOpTest.h"
21cb93a386Sopenharmony_ci#include "src/gpu/GrGeometryProcessor.h"
22cb93a386Sopenharmony_ci#include "src/gpu/GrGpu.h"
23cb93a386Sopenharmony_ci#include "src/gpu/GrMemoryPool.h"
24cb93a386Sopenharmony_ci#include "src/gpu/GrOpFlushState.h"
25cb93a386Sopenharmony_ci#include "src/gpu/GrOpsTypes.h"
26cb93a386Sopenharmony_ci#include "src/gpu/GrRecordingContextPriv.h"
27cb93a386Sopenharmony_ci#include "src/gpu/GrResourceProvider.h"
28cb93a386Sopenharmony_ci#include "src/gpu/GrResourceProviderPriv.h"
29cb93a386Sopenharmony_ci#include "src/gpu/GrShaderCaps.h"
30cb93a386Sopenharmony_ci#include "src/gpu/GrTexture.h"
31cb93a386Sopenharmony_ci#include "src/gpu/GrTextureProxy.h"
32cb93a386Sopenharmony_ci#include "src/gpu/SkGr.h"
33cb93a386Sopenharmony_ci#include "src/gpu/effects/GrBlendFragmentProcessor.h"
34cb93a386Sopenharmony_ci#include "src/gpu/effects/GrTextureEffect.h"
35cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrQuad.h"
36cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrQuadBuffer.h"
37cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrQuadUtils.h"
38cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrRect.h"
39cb93a386Sopenharmony_ci#include "src/gpu/glsl/GrGLSLVarying.h"
40cb93a386Sopenharmony_ci#include "src/gpu/ops/FillRectOp.h"
41cb93a386Sopenharmony_ci#include "src/gpu/ops/GrMeshDrawOp.h"
42cb93a386Sopenharmony_ci#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
43cb93a386Sopenharmony_ci#include "src/gpu/ops/QuadPerEdgeAA.h"
44cb93a386Sopenharmony_ci#include "src/gpu/ops/TextureOp.h"
45cb93a386Sopenharmony_ci#include "src/gpu/v1/SurfaceDrawContext_v1.h"
46cb93a386Sopenharmony_ci
47cb93a386Sopenharmony_cinamespace {
48cb93a386Sopenharmony_ci
49cb93a386Sopenharmony_ciusing Subset = skgpu::v1::QuadPerEdgeAA::Subset;
50cb93a386Sopenharmony_ciusing VertexSpec = skgpu::v1::QuadPerEdgeAA::VertexSpec;
51cb93a386Sopenharmony_ciusing ColorType = skgpu::v1::QuadPerEdgeAA::ColorType;
52cb93a386Sopenharmony_ci
53cb93a386Sopenharmony_ci// Extracts lengths of vertical and horizontal edges of axis-aligned quad. "width" is the edge
54cb93a386Sopenharmony_ci// between v0 and v2 (or v1 and v3), "height" is the edge between v0 and v1 (or v2 and v3).
55cb93a386Sopenharmony_ciSkSize axis_aligned_quad_size(const GrQuad& quad) {
56cb93a386Sopenharmony_ci    SkASSERT(quad.quadType() == GrQuad::Type::kAxisAligned);
57cb93a386Sopenharmony_ci    // Simplification of regular edge length equation, since it's axis aligned and can avoid sqrt
58cb93a386Sopenharmony_ci    float dw = sk_float_abs(quad.x(2) - quad.x(0)) + sk_float_abs(quad.y(2) - quad.y(0));
59cb93a386Sopenharmony_ci    float dh = sk_float_abs(quad.x(1) - quad.x(0)) + sk_float_abs(quad.y(1) - quad.y(0));
60cb93a386Sopenharmony_ci    return {dw, dh};
61cb93a386Sopenharmony_ci}
62cb93a386Sopenharmony_ci
63cb93a386Sopenharmony_cistd::tuple<bool /* filter */,
64cb93a386Sopenharmony_ci           bool /* mipmap */>
65cb93a386Sopenharmony_cifilter_and_mm_have_effect(const GrQuad& srcQuad, const GrQuad& dstQuad) {
66cb93a386Sopenharmony_ci    // If not axis-aligned in src or dst, then always say it has an effect
67cb93a386Sopenharmony_ci    if (srcQuad.quadType() != GrQuad::Type::kAxisAligned ||
68cb93a386Sopenharmony_ci        dstQuad.quadType() != GrQuad::Type::kAxisAligned) {
69cb93a386Sopenharmony_ci        return {true, true};
70cb93a386Sopenharmony_ci    }
71cb93a386Sopenharmony_ci
72cb93a386Sopenharmony_ci    SkRect srcRect;
73cb93a386Sopenharmony_ci    SkRect dstRect;
74cb93a386Sopenharmony_ci    if (srcQuad.asRect(&srcRect) && dstQuad.asRect(&dstRect)) {
75cb93a386Sopenharmony_ci        // Disable filtering when there is no scaling (width and height are the same), and the
76cb93a386Sopenharmony_ci        // top-left corners have the same fraction (so src and dst snap to the pixel grid
77cb93a386Sopenharmony_ci        // identically).
78cb93a386Sopenharmony_ci        SkASSERT(srcRect.isSorted());
79cb93a386Sopenharmony_ci        bool filter = srcRect.width() != dstRect.width() || srcRect.height() != dstRect.height() ||
80cb93a386Sopenharmony_ci                      SkScalarFraction(srcRect.fLeft) != SkScalarFraction(dstRect.fLeft) ||
81cb93a386Sopenharmony_ci                      SkScalarFraction(srcRect.fTop)  != SkScalarFraction(dstRect.fTop);
82cb93a386Sopenharmony_ci        bool mm = srcRect.width() > dstRect.width() || srcRect.height() > dstRect.height();
83cb93a386Sopenharmony_ci        return {filter, mm};
84cb93a386Sopenharmony_ci    }
85cb93a386Sopenharmony_ci    // Extract edge lengths
86cb93a386Sopenharmony_ci    SkSize srcSize = axis_aligned_quad_size(srcQuad);
87cb93a386Sopenharmony_ci    SkSize dstSize = axis_aligned_quad_size(dstQuad);
88cb93a386Sopenharmony_ci    // Although the quads are axis-aligned, the local coordinate system is transformed such
89cb93a386Sopenharmony_ci    // that fractionally-aligned sample centers will not align with the device coordinate system
90cb93a386Sopenharmony_ci    // So disable filtering when edges are the same length and both srcQuad and dstQuad
91cb93a386Sopenharmony_ci    // 0th vertex is integer aligned.
92cb93a386Sopenharmony_ci    bool filter = srcSize != dstSize ||
93cb93a386Sopenharmony_ci                  !SkScalarIsInt(srcQuad.x(0)) ||
94cb93a386Sopenharmony_ci                  !SkScalarIsInt(srcQuad.y(0)) ||
95cb93a386Sopenharmony_ci                  !SkScalarIsInt(dstQuad.x(0)) ||
96cb93a386Sopenharmony_ci                  !SkScalarIsInt(dstQuad.y(0));
97cb93a386Sopenharmony_ci    bool mm = srcSize.fWidth > dstSize.fWidth || srcSize.fHeight > dstSize.fHeight;
98cb93a386Sopenharmony_ci    return {filter, mm};
99cb93a386Sopenharmony_ci}
100cb93a386Sopenharmony_ci
101cb93a386Sopenharmony_ci// Describes function for normalizing src coords: [x * iw, y * ih + yOffset] can represent
102cb93a386Sopenharmony_ci// regular and rectangular textures, w/ or w/o origin correction.
103cb93a386Sopenharmony_cistruct NormalizationParams {
104cb93a386Sopenharmony_ci    float fIW; // 1 / width of texture, or 1.0 for texture rectangles
105cb93a386Sopenharmony_ci    float fInvH; // 1 / height of texture, or 1.0 for tex rects, X -1 if bottom-left origin
106cb93a386Sopenharmony_ci    float fYOffset; // 0 for top-left origin, height of [normalized] tex if bottom-left
107cb93a386Sopenharmony_ci};
108cb93a386Sopenharmony_ciNormalizationParams proxy_normalization_params(const GrSurfaceProxy* proxy,
109cb93a386Sopenharmony_ci                                               GrSurfaceOrigin origin) {
110cb93a386Sopenharmony_ci    // Whether or not the proxy is instantiated, this is the size its texture will be, so we can
111cb93a386Sopenharmony_ci    // normalize the src coordinates up front.
112cb93a386Sopenharmony_ci    SkISize dimensions = proxy->backingStoreDimensions();
113cb93a386Sopenharmony_ci    float iw, ih, h;
114cb93a386Sopenharmony_ci    if (proxy->backendFormat().textureType() == GrTextureType::kRectangle) {
115cb93a386Sopenharmony_ci        iw = ih = 1.f;
116cb93a386Sopenharmony_ci        h = dimensions.height();
117cb93a386Sopenharmony_ci    } else {
118cb93a386Sopenharmony_ci        iw = 1.f / dimensions.width();
119cb93a386Sopenharmony_ci        ih = 1.f / dimensions.height();
120cb93a386Sopenharmony_ci        h = 1.f;
121cb93a386Sopenharmony_ci    }
122cb93a386Sopenharmony_ci
123cb93a386Sopenharmony_ci    if (origin == kBottomLeft_GrSurfaceOrigin) {
124cb93a386Sopenharmony_ci        return {iw, -ih, h};
125cb93a386Sopenharmony_ci    } else {
126cb93a386Sopenharmony_ci        return {iw, ih, 0.0f};
127cb93a386Sopenharmony_ci    }
128cb93a386Sopenharmony_ci}
129cb93a386Sopenharmony_ci
130cb93a386Sopenharmony_ci// Normalize the subset. If 'subsetRect' is null, it is assumed no subset constraint is desired,
131cb93a386Sopenharmony_ci// so a sufficiently large rect is returned even if the quad ends up batched with an op that uses
132cb93a386Sopenharmony_ci// subsets overall. When there is a subset it will be inset based on the filter mode. Normalization
133cb93a386Sopenharmony_ci// and y-flipping are applied as indicated by NormalizationParams.
134cb93a386Sopenharmony_ciSkRect normalize_and_inset_subset(GrSamplerState::Filter filter,
135cb93a386Sopenharmony_ci                                  const NormalizationParams& params,
136cb93a386Sopenharmony_ci                                  const SkRect* subsetRect) {
137cb93a386Sopenharmony_ci    static constexpr SkRect kLargeRect = {-100000, -100000, 1000000, 1000000};
138cb93a386Sopenharmony_ci    if (!subsetRect) {
139cb93a386Sopenharmony_ci        // Either the quad has no subset constraint and is batched with a subset constrained op
140cb93a386Sopenharmony_ci        // (in which case we want a subset that doesn't restrict normalized tex coords), or the
141cb93a386Sopenharmony_ci        // entire op doesn't use the subset, in which case the returned value is ignored.
142cb93a386Sopenharmony_ci        return kLargeRect;
143cb93a386Sopenharmony_ci    }
144cb93a386Sopenharmony_ci
145cb93a386Sopenharmony_ci    auto ltrb = skvx::Vec<4, float>::Load(subsetRect);
146cb93a386Sopenharmony_ci    auto flipHi = skvx::Vec<4, float>({1.f, 1.f, -1.f, -1.f});
147cb93a386Sopenharmony_ci    if (filter == GrSamplerState::Filter::kNearest) {
148cb93a386Sopenharmony_ci        // Make sure our insetting puts us at pixel centers.
149cb93a386Sopenharmony_ci        ltrb = skvx::floor(ltrb*flipHi)*flipHi;
150cb93a386Sopenharmony_ci    }
151cb93a386Sopenharmony_ci    // Inset with pin to the rect center.
152cb93a386Sopenharmony_ci    ltrb += skvx::Vec<4, float>({.5f, .5f, -.5f, -.5f});
153cb93a386Sopenharmony_ci    auto mid = (skvx::shuffle<2, 3, 0, 1>(ltrb) + ltrb)*0.5f;
154cb93a386Sopenharmony_ci    ltrb = skvx::min(ltrb*flipHi, mid*flipHi)*flipHi;
155cb93a386Sopenharmony_ci
156cb93a386Sopenharmony_ci    // Normalize and offset
157cb93a386Sopenharmony_ci    ltrb = ltrb * skvx::Vec<4, float>{params.fIW, params.fInvH, params.fIW, params.fInvH} +
158cb93a386Sopenharmony_ci               skvx::Vec<4, float>{0.f, params.fYOffset, 0.f, params.fYOffset};
159cb93a386Sopenharmony_ci    if (params.fInvH < 0.f) {
160cb93a386Sopenharmony_ci        // Flip top and bottom to keep the rect sorted when loaded back to SkRect.
161cb93a386Sopenharmony_ci        ltrb = skvx::shuffle<0, 3, 2, 1>(ltrb);
162cb93a386Sopenharmony_ci    }
163cb93a386Sopenharmony_ci
164cb93a386Sopenharmony_ci    SkRect out;
165cb93a386Sopenharmony_ci    ltrb.store(&out);
166cb93a386Sopenharmony_ci    return out;
167cb93a386Sopenharmony_ci}
168cb93a386Sopenharmony_ci
169cb93a386Sopenharmony_ci// Normalizes logical src coords and corrects for origin
170cb93a386Sopenharmony_civoid normalize_src_quad(const NormalizationParams& params,
171cb93a386Sopenharmony_ci                        GrQuad* srcQuad) {
172cb93a386Sopenharmony_ci    // The src quad should not have any perspective
173cb93a386Sopenharmony_ci    SkASSERT(!srcQuad->hasPerspective());
174cb93a386Sopenharmony_ci    skvx::Vec<4, float> xs = srcQuad->x4f() * params.fIW;
175cb93a386Sopenharmony_ci    skvx::Vec<4, float> ys = srcQuad->y4f() * params.fInvH + params.fYOffset;
176cb93a386Sopenharmony_ci    xs.store(srcQuad->xs());
177cb93a386Sopenharmony_ci    ys.store(srcQuad->ys());
178cb93a386Sopenharmony_ci}
179cb93a386Sopenharmony_ci
180cb93a386Sopenharmony_ci// Count the number of proxy runs in the entry set. This usually is already computed by
181cb93a386Sopenharmony_ci// SkGpuDevice, but when the BatchLengthLimiter chops the set up it must determine a new proxy count
182cb93a386Sopenharmony_ci// for each split.
183cb93a386Sopenharmony_ciint proxy_run_count(const GrTextureSetEntry set[], int count) {
184cb93a386Sopenharmony_ci    int actualProxyRunCount = 0;
185cb93a386Sopenharmony_ci    const GrSurfaceProxy* lastProxy = nullptr;
186cb93a386Sopenharmony_ci    for (int i = 0; i < count; ++i) {
187cb93a386Sopenharmony_ci        if (set[i].fProxyView.proxy() != lastProxy) {
188cb93a386Sopenharmony_ci            actualProxyRunCount++;
189cb93a386Sopenharmony_ci            lastProxy = set[i].fProxyView.proxy();
190cb93a386Sopenharmony_ci        }
191cb93a386Sopenharmony_ci    }
192cb93a386Sopenharmony_ci    return actualProxyRunCount;
193cb93a386Sopenharmony_ci}
194cb93a386Sopenharmony_ci
195cb93a386Sopenharmony_cibool safe_to_ignore_subset_rect(GrAAType aaType, GrSamplerState::Filter filter,
196cb93a386Sopenharmony_ci                                const DrawQuad& quad, const SkRect& subsetRect) {
197cb93a386Sopenharmony_ci    // If both the device and local quad are both axis-aligned, and filtering is off, the local quad
198cb93a386Sopenharmony_ci    // can push all the way up to the edges of the the subset rect and the sampler shouldn't
199cb93a386Sopenharmony_ci    // overshoot. Unfortunately, antialiasing adds enough jitter that we can only rely on this in
200cb93a386Sopenharmony_ci    // the non-antialiased case.
201cb93a386Sopenharmony_ci    SkRect localBounds = quad.fLocal.bounds();
202cb93a386Sopenharmony_ci    if (aaType == GrAAType::kNone &&
203cb93a386Sopenharmony_ci        filter == GrSamplerState::Filter::kNearest &&
204cb93a386Sopenharmony_ci        quad.fDevice.quadType() == GrQuad::Type::kAxisAligned &&
205cb93a386Sopenharmony_ci        quad.fLocal.quadType() == GrQuad::Type::kAxisAligned &&
206cb93a386Sopenharmony_ci        subsetRect.contains(localBounds)) {
207cb93a386Sopenharmony_ci
208cb93a386Sopenharmony_ci        return true;
209cb93a386Sopenharmony_ci    }
210cb93a386Sopenharmony_ci
211cb93a386Sopenharmony_ci    // If the local quad is inset by at least 0.5 pixels into the subset rect's bounds, the
212cb93a386Sopenharmony_ci    // sampler shouldn't overshoot, even when antialiasing and filtering is taken into account.
213cb93a386Sopenharmony_ci    if (subsetRect.makeInset(0.5f, 0.5f).contains(localBounds)) {
214cb93a386Sopenharmony_ci        return true;
215cb93a386Sopenharmony_ci    }
216cb93a386Sopenharmony_ci
217cb93a386Sopenharmony_ci    // The subset rect cannot be ignored safely.
218cb93a386Sopenharmony_ci    return false;
219cb93a386Sopenharmony_ci}
220cb93a386Sopenharmony_ci
221cb93a386Sopenharmony_ci/**
222cb93a386Sopenharmony_ci * Op that implements TextureOp::Make. It draws textured quads. Each quad can modulate against a
223cb93a386Sopenharmony_ci * the texture by color. The blend with the destination is always src-over. The edges are non-AA.
224cb93a386Sopenharmony_ci */
225cb93a386Sopenharmony_ciclass TextureOpImpl final : public GrMeshDrawOp {
226cb93a386Sopenharmony_cipublic:
227cb93a386Sopenharmony_ci    using Saturate = skgpu::v1::TextureOp::Saturate;
228cb93a386Sopenharmony_ci
229cb93a386Sopenharmony_ci    static GrOp::Owner Make(GrRecordingContext* context,
230cb93a386Sopenharmony_ci                            GrSurfaceProxyView proxyView,
231cb93a386Sopenharmony_ci                            sk_sp<GrColorSpaceXform> textureXform,
232cb93a386Sopenharmony_ci                            GrSamplerState::Filter filter,
233cb93a386Sopenharmony_ci                            GrSamplerState::MipmapMode mm,
234cb93a386Sopenharmony_ci                            const SkPMColor4f& color,
235cb93a386Sopenharmony_ci                            Saturate saturate,
236cb93a386Sopenharmony_ci                            GrAAType aaType,
237cb93a386Sopenharmony_ci                            DrawQuad* quad,
238cb93a386Sopenharmony_ci                            const SkRect* subset) {
239cb93a386Sopenharmony_ci
240cb93a386Sopenharmony_ci        return GrOp::Make<TextureOpImpl>(context, std::move(proxyView), std::move(textureXform),
241cb93a386Sopenharmony_ci                                         filter, mm, color, saturate, aaType, quad, subset);
242cb93a386Sopenharmony_ci    }
243cb93a386Sopenharmony_ci
244cb93a386Sopenharmony_ci    static GrOp::Owner Make(GrRecordingContext* context,
245cb93a386Sopenharmony_ci                            GrTextureSetEntry set[],
246cb93a386Sopenharmony_ci                            int cnt,
247cb93a386Sopenharmony_ci                            int proxyRunCnt,
248cb93a386Sopenharmony_ci                            GrSamplerState::Filter filter,
249cb93a386Sopenharmony_ci                            GrSamplerState::MipmapMode mm,
250cb93a386Sopenharmony_ci                            Saturate saturate,
251cb93a386Sopenharmony_ci                            GrAAType aaType,
252cb93a386Sopenharmony_ci                            SkCanvas::SrcRectConstraint constraint,
253cb93a386Sopenharmony_ci                            const SkMatrix& viewMatrix,
254cb93a386Sopenharmony_ci                            sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
255cb93a386Sopenharmony_ci        // Allocate size based on proxyRunCnt, since that determines number of ViewCountPairs.
256cb93a386Sopenharmony_ci        SkASSERT(proxyRunCnt <= cnt);
257cb93a386Sopenharmony_ci        return GrOp::MakeWithExtraMemory<TextureOpImpl>(
258cb93a386Sopenharmony_ci                context, sizeof(ViewCountPair) * (proxyRunCnt - 1),
259cb93a386Sopenharmony_ci                set, cnt, proxyRunCnt, filter, mm, saturate, aaType, constraint,
260cb93a386Sopenharmony_ci                viewMatrix, std::move(textureColorSpaceXform));
261cb93a386Sopenharmony_ci    }
262cb93a386Sopenharmony_ci
263cb93a386Sopenharmony_ci    ~TextureOpImpl() override {
264cb93a386Sopenharmony_ci        for (unsigned p = 1; p < fMetadata.fProxyCount; ++p) {
265cb93a386Sopenharmony_ci            fViewCountPairs[p].~ViewCountPair();
266cb93a386Sopenharmony_ci        }
267cb93a386Sopenharmony_ci    }
268cb93a386Sopenharmony_ci
269cb93a386Sopenharmony_ci    const char* name() const override { return "TextureOp"; }
270cb93a386Sopenharmony_ci
271cb93a386Sopenharmony_ci    void visitProxies(const GrVisitProxyFunc& func) const override {
272cb93a386Sopenharmony_ci        bool mipped = (fMetadata.mipmapMode() != GrSamplerState::MipmapMode::kNone);
273cb93a386Sopenharmony_ci        for (unsigned p = 0; p <  fMetadata.fProxyCount; ++p) {
274cb93a386Sopenharmony_ci            func(fViewCountPairs[p].fProxy.get(), GrMipmapped(mipped));
275cb93a386Sopenharmony_ci        }
276cb93a386Sopenharmony_ci        if (fDesc && fDesc->fProgramInfo) {
277cb93a386Sopenharmony_ci            fDesc->fProgramInfo->visitFPProxies(func);
278cb93a386Sopenharmony_ci        }
279cb93a386Sopenharmony_ci    }
280cb93a386Sopenharmony_ci
281cb93a386Sopenharmony_ci#ifdef SK_DEBUG
282cb93a386Sopenharmony_ci    static void ValidateResourceLimits() {
283cb93a386Sopenharmony_ci        // The op implementation has an upper bound on the number of quads that it can represent.
284cb93a386Sopenharmony_ci        // However, the resource manager imposes its own limit on the number of quads, which should
285cb93a386Sopenharmony_ci        // always be lower than the numerical limit this op can hold.
286cb93a386Sopenharmony_ci        using CountStorage = decltype(Metadata::fTotalQuadCount);
287cb93a386Sopenharmony_ci        CountStorage maxQuadCount = std::numeric_limits<CountStorage>::max();
288cb93a386Sopenharmony_ci        // GrResourceProvider::Max...() is typed as int, so don't compare across signed/unsigned.
289cb93a386Sopenharmony_ci        int resourceLimit = SkTo<int>(maxQuadCount);
290cb93a386Sopenharmony_ci        SkASSERT(GrResourceProvider::MaxNumAAQuads() <= resourceLimit &&
291cb93a386Sopenharmony_ci                 GrResourceProvider::MaxNumNonAAQuads() <= resourceLimit);
292cb93a386Sopenharmony_ci    }
293cb93a386Sopenharmony_ci#endif
294cb93a386Sopenharmony_ci
295cb93a386Sopenharmony_ci    GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip*,
296cb93a386Sopenharmony_ci                                      GrClampType clampType) override {
297cb93a386Sopenharmony_ci        SkASSERT(fMetadata.colorType() == ColorType::kNone);
298cb93a386Sopenharmony_ci        auto iter = fQuads.metadata();
299cb93a386Sopenharmony_ci        while(iter.next()) {
300cb93a386Sopenharmony_ci            auto colorType = skgpu::v1::QuadPerEdgeAA::MinColorType(iter->fColor);
301cb93a386Sopenharmony_ci            colorType = std::max(static_cast<ColorType>(fMetadata.fColorType),
302cb93a386Sopenharmony_ci                                 colorType);
303cb93a386Sopenharmony_ci            if (caps.reducedShaderMode()) {
304cb93a386Sopenharmony_ci                colorType = std::max(colorType, ColorType::kByte);
305cb93a386Sopenharmony_ci            }
306cb93a386Sopenharmony_ci            fMetadata.fColorType = static_cast<uint16_t>(colorType);
307cb93a386Sopenharmony_ci        }
308cb93a386Sopenharmony_ci        return GrProcessorSet::EmptySetAnalysis();
309cb93a386Sopenharmony_ci    }
310cb93a386Sopenharmony_ci
311cb93a386Sopenharmony_ci    FixedFunctionFlags fixedFunctionFlags() const override {
312cb93a386Sopenharmony_ci        return fMetadata.aaType() == GrAAType::kMSAA ? FixedFunctionFlags::kUsesHWAA
313cb93a386Sopenharmony_ci                                                     : FixedFunctionFlags::kNone;
314cb93a386Sopenharmony_ci    }
315cb93a386Sopenharmony_ci
316cb93a386Sopenharmony_ci    DEFINE_OP_CLASS_ID
317cb93a386Sopenharmony_ci
318cb93a386Sopenharmony_ciprivate:
319cb93a386Sopenharmony_ci    friend class ::GrOp;
320cb93a386Sopenharmony_ci
321cb93a386Sopenharmony_ci    struct ColorSubsetAndAA {
322cb93a386Sopenharmony_ci        ColorSubsetAndAA(const SkPMColor4f& color, const SkRect& subsetRect, GrQuadAAFlags aaFlags)
323cb93a386Sopenharmony_ci                : fColor(color)
324cb93a386Sopenharmony_ci                , fSubsetRect(subsetRect)
325cb93a386Sopenharmony_ci                , fAAFlags(static_cast<uint16_t>(aaFlags)) {
326cb93a386Sopenharmony_ci            SkASSERT(fAAFlags == static_cast<uint16_t>(aaFlags));
327cb93a386Sopenharmony_ci        }
328cb93a386Sopenharmony_ci
329cb93a386Sopenharmony_ci        SkPMColor4f fColor;
330cb93a386Sopenharmony_ci        // If the op doesn't use subsets, this is ignored. If the op uses subsets and the specific
331cb93a386Sopenharmony_ci        // entry does not, this rect will equal kLargeRect, so it automatically has no effect.
332cb93a386Sopenharmony_ci        SkRect fSubsetRect;
333cb93a386Sopenharmony_ci        unsigned fAAFlags : 4;
334cb93a386Sopenharmony_ci
335cb93a386Sopenharmony_ci        GrQuadAAFlags aaFlags() const { return static_cast<GrQuadAAFlags>(fAAFlags); }
336cb93a386Sopenharmony_ci    };
337cb93a386Sopenharmony_ci
338cb93a386Sopenharmony_ci    struct ViewCountPair {
339cb93a386Sopenharmony_ci        // Normally this would be a GrSurfaceProxyView, but TextureOp applies the GrOrigin right
340cb93a386Sopenharmony_ci        // away so it doesn't need to be stored, and all ViewCountPairs in an op have the same
341cb93a386Sopenharmony_ci        // swizzle so that is stored in the op metadata.
342cb93a386Sopenharmony_ci        sk_sp<GrSurfaceProxy> fProxy;
343cb93a386Sopenharmony_ci        int fQuadCnt;
344cb93a386Sopenharmony_ci    };
345cb93a386Sopenharmony_ci
346cb93a386Sopenharmony_ci    // TextureOp and ViewCountPair are 8 byte aligned. This is packed into 8 bytes to minimally
347cb93a386Sopenharmony_ci    // increase the size of the op; increasing the op size can have a surprising impact on
348cb93a386Sopenharmony_ci    // performance (since texture ops are one of the most commonly used in an app).
349cb93a386Sopenharmony_ci    struct Metadata {
350cb93a386Sopenharmony_ci        // AAType must be filled after initialization; ColorType is determined in finalize()
351cb93a386Sopenharmony_ci        Metadata(const GrSwizzle& swizzle,
352cb93a386Sopenharmony_ci                 GrSamplerState::Filter filter,
353cb93a386Sopenharmony_ci                 GrSamplerState::MipmapMode mm,
354cb93a386Sopenharmony_ci                 Subset subset,
355cb93a386Sopenharmony_ci                 Saturate saturate)
356cb93a386Sopenharmony_ci            : fSwizzle(swizzle)
357cb93a386Sopenharmony_ci            , fProxyCount(1)
358cb93a386Sopenharmony_ci            , fTotalQuadCount(1)
359cb93a386Sopenharmony_ci            , fFilter(static_cast<uint16_t>(filter))
360cb93a386Sopenharmony_ci            , fMipmapMode(static_cast<uint16_t>(mm))
361cb93a386Sopenharmony_ci            , fAAType(static_cast<uint16_t>(GrAAType::kNone))
362cb93a386Sopenharmony_ci            , fColorType(static_cast<uint16_t>(ColorType::kNone))
363cb93a386Sopenharmony_ci            , fSubset(static_cast<uint16_t>(subset))
364cb93a386Sopenharmony_ci            , fSaturate(static_cast<uint16_t>(saturate)) {}
365cb93a386Sopenharmony_ci
366cb93a386Sopenharmony_ci        GrSwizzle fSwizzle; // sizeof(GrSwizzle) == uint16_t
367cb93a386Sopenharmony_ci        uint16_t  fProxyCount;
368cb93a386Sopenharmony_ci        // This will be >= fProxyCount, since a proxy may be drawn multiple times
369cb93a386Sopenharmony_ci        uint16_t  fTotalQuadCount;
370cb93a386Sopenharmony_ci
371cb93a386Sopenharmony_ci        // These must be based on uint16_t to help MSVC's pack bitfields optimally
372cb93a386Sopenharmony_ci        uint16_t  fFilter     : 2; // GrSamplerState::Filter
373cb93a386Sopenharmony_ci        uint16_t  fMipmapMode : 2; // GrSamplerState::MipmapMode
374cb93a386Sopenharmony_ci        uint16_t  fAAType     : 2; // GrAAType
375cb93a386Sopenharmony_ci        uint16_t  fColorType  : 2; // GrQuadPerEdgeAA::ColorType
376cb93a386Sopenharmony_ci        uint16_t  fSubset     : 1; // bool
377cb93a386Sopenharmony_ci        uint16_t  fSaturate   : 1; // bool
378cb93a386Sopenharmony_ci        uint16_t  fUnused     : 6; // # of bits left before Metadata exceeds 8 bytes
379cb93a386Sopenharmony_ci
380cb93a386Sopenharmony_ci        GrSamplerState::Filter filter() const {
381cb93a386Sopenharmony_ci            return static_cast<GrSamplerState::Filter>(fFilter);
382cb93a386Sopenharmony_ci        }
383cb93a386Sopenharmony_ci        GrSamplerState::MipmapMode mipmapMode() const {
384cb93a386Sopenharmony_ci            return static_cast<GrSamplerState::MipmapMode>(fMipmapMode);
385cb93a386Sopenharmony_ci        }
386cb93a386Sopenharmony_ci        GrAAType aaType() const { return static_cast<GrAAType>(fAAType); }
387cb93a386Sopenharmony_ci        ColorType colorType() const { return static_cast<ColorType>(fColorType); }
388cb93a386Sopenharmony_ci        Subset subset() const { return static_cast<Subset>(fSubset); }
389cb93a386Sopenharmony_ci        Saturate saturate() const { return static_cast<Saturate>(fSaturate); }
390cb93a386Sopenharmony_ci
391cb93a386Sopenharmony_ci        static_assert(GrSamplerState::kFilterCount <= 4);
392cb93a386Sopenharmony_ci        static_assert(kGrAATypeCount <= 4);
393cb93a386Sopenharmony_ci        static_assert(skgpu::v1::QuadPerEdgeAA::kColorTypeCount <= 4);
394cb93a386Sopenharmony_ci    };
395cb93a386Sopenharmony_ci    static_assert(sizeof(Metadata) == 8);
396cb93a386Sopenharmony_ci
397cb93a386Sopenharmony_ci    // This descriptor is used to store the draw info we decide on during on(Pre)PrepareDraws. We
398cb93a386Sopenharmony_ci    // store the data in a separate struct in order to minimize the size of the TextureOp.
399cb93a386Sopenharmony_ci    // Historically, increasing the TextureOp's size has caused surprising perf regressions, but we
400cb93a386Sopenharmony_ci    // may want to re-evaluate whether this is still necessary.
401cb93a386Sopenharmony_ci    //
402cb93a386Sopenharmony_ci    // In the onPrePrepareDraws case it is allocated in the creation-time opData arena, and
403cb93a386Sopenharmony_ci    // allocatePrePreparedVertices is also called.
404cb93a386Sopenharmony_ci    //
405cb93a386Sopenharmony_ci    // In the onPrepareDraws case this descriptor is allocated in the flush-time arena (i.e., as
406cb93a386Sopenharmony_ci    // part of the flushState).
407cb93a386Sopenharmony_ci    struct Desc {
408cb93a386Sopenharmony_ci        VertexSpec fVertexSpec;
409cb93a386Sopenharmony_ci        int fNumProxies = 0;
410cb93a386Sopenharmony_ci        int fNumTotalQuads = 0;
411cb93a386Sopenharmony_ci
412cb93a386Sopenharmony_ci        // This member variable is only used by 'onPrePrepareDraws'.
413cb93a386Sopenharmony_ci        char* fPrePreparedVertices = nullptr;
414cb93a386Sopenharmony_ci
415cb93a386Sopenharmony_ci        GrProgramInfo* fProgramInfo = nullptr;
416cb93a386Sopenharmony_ci
417cb93a386Sopenharmony_ci        sk_sp<const GrBuffer> fIndexBuffer;
418cb93a386Sopenharmony_ci        sk_sp<const GrBuffer> fVertexBuffer;
419cb93a386Sopenharmony_ci        int fBaseVertex;
420cb93a386Sopenharmony_ci
421cb93a386Sopenharmony_ci        // How big should 'fVertices' be to hold all the vertex data?
422cb93a386Sopenharmony_ci        size_t totalSizeInBytes() const {
423cb93a386Sopenharmony_ci            return this->totalNumVertices() * fVertexSpec.vertexSize();
424cb93a386Sopenharmony_ci        }
425cb93a386Sopenharmony_ci
426cb93a386Sopenharmony_ci        int totalNumVertices() const {
427cb93a386Sopenharmony_ci            return fNumTotalQuads * fVertexSpec.verticesPerQuad();
428cb93a386Sopenharmony_ci        }
429cb93a386Sopenharmony_ci
430cb93a386Sopenharmony_ci        void allocatePrePreparedVertices(SkArenaAlloc* arena) {
431cb93a386Sopenharmony_ci            fPrePreparedVertices = arena->makeArrayDefault<char>(this->totalSizeInBytes());
432cb93a386Sopenharmony_ci        }
433cb93a386Sopenharmony_ci    };
434cb93a386Sopenharmony_ci    // If subsetRect is not null it will be used to apply a strict src rect-style constraint.
435cb93a386Sopenharmony_ci    TextureOpImpl(GrSurfaceProxyView proxyView,
436cb93a386Sopenharmony_ci                  sk_sp<GrColorSpaceXform> textureColorSpaceXform,
437cb93a386Sopenharmony_ci                  GrSamplerState::Filter filter,
438cb93a386Sopenharmony_ci                  GrSamplerState::MipmapMode mm,
439cb93a386Sopenharmony_ci                  const SkPMColor4f& color,
440cb93a386Sopenharmony_ci                  Saturate saturate,
441cb93a386Sopenharmony_ci                  GrAAType aaType,
442cb93a386Sopenharmony_ci                  DrawQuad* quad,
443cb93a386Sopenharmony_ci                  const SkRect* subsetRect)
444cb93a386Sopenharmony_ci            : INHERITED(ClassID())
445cb93a386Sopenharmony_ci            , fQuads(1, true /* includes locals */)
446cb93a386Sopenharmony_ci            , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
447cb93a386Sopenharmony_ci            , fDesc(nullptr)
448cb93a386Sopenharmony_ci            , fMetadata(proxyView.swizzle(), filter, mm, Subset(!!subsetRect), saturate) {
449cb93a386Sopenharmony_ci        // Clean up disparities between the overall aa type and edge configuration and apply
450cb93a386Sopenharmony_ci        // optimizations based on the rect and matrix when appropriate
451cb93a386Sopenharmony_ci        GrQuadUtils::ResolveAAType(aaType, quad->fEdgeFlags, quad->fDevice,
452cb93a386Sopenharmony_ci                                   &aaType, &quad->fEdgeFlags);
453cb93a386Sopenharmony_ci        fMetadata.fAAType = static_cast<uint16_t>(aaType);
454cb93a386Sopenharmony_ci
455cb93a386Sopenharmony_ci        // We expect our caller to have already caught this optimization.
456cb93a386Sopenharmony_ci        SkASSERT(!subsetRect ||
457cb93a386Sopenharmony_ci                 !subsetRect->contains(proxyView.proxy()->backingStoreBoundsRect()));
458cb93a386Sopenharmony_ci
459cb93a386Sopenharmony_ci        // We may have had a strict constraint with nearest filter solely due to possible AA bloat.
460cb93a386Sopenharmony_ci        // Try to identify cases where the subsetting isn't actually necessary, and skip it.
461cb93a386Sopenharmony_ci        if (subsetRect) {
462cb93a386Sopenharmony_ci            if (safe_to_ignore_subset_rect(aaType, filter, *quad, *subsetRect)) {
463cb93a386Sopenharmony_ci                subsetRect = nullptr;
464cb93a386Sopenharmony_ci                fMetadata.fSubset = static_cast<uint16_t>(Subset::kNo);
465cb93a386Sopenharmony_ci            }
466cb93a386Sopenharmony_ci        }
467cb93a386Sopenharmony_ci
468cb93a386Sopenharmony_ci        // Normalize src coordinates and the subset (if set)
469cb93a386Sopenharmony_ci        NormalizationParams params = proxy_normalization_params(proxyView.proxy(),
470cb93a386Sopenharmony_ci                                                                proxyView.origin());
471cb93a386Sopenharmony_ci        normalize_src_quad(params, &quad->fLocal);
472cb93a386Sopenharmony_ci        SkRect subset = normalize_and_inset_subset(filter, params, subsetRect);
473cb93a386Sopenharmony_ci
474cb93a386Sopenharmony_ci        // Set bounds before clipping so we don't have to worry about unioning the bounds of
475cb93a386Sopenharmony_ci        // the two potential quads (GrQuad::bounds() is perspective-safe).
476cb93a386Sopenharmony_ci        bool hairline = GrQuadUtils::WillUseHairline(quad->fDevice, aaType, quad->fEdgeFlags);
477cb93a386Sopenharmony_ci        this->setBounds(quad->fDevice.bounds(), HasAABloat(aaType == GrAAType::kCoverage),
478cb93a386Sopenharmony_ci                        hairline ? IsHairline::kYes : IsHairline::kNo);
479cb93a386Sopenharmony_ci        int quadCount = this->appendQuad(quad, color, subset);
480cb93a386Sopenharmony_ci        fViewCountPairs[0] = {proxyView.detachProxy(), quadCount};
481cb93a386Sopenharmony_ci    }
482cb93a386Sopenharmony_ci
483cb93a386Sopenharmony_ci    TextureOpImpl(GrTextureSetEntry set[],
484cb93a386Sopenharmony_ci                  int cnt,
485cb93a386Sopenharmony_ci                  int proxyRunCnt,
486cb93a386Sopenharmony_ci                  const GrSamplerState::Filter filter,
487cb93a386Sopenharmony_ci                  const GrSamplerState::MipmapMode mm,
488cb93a386Sopenharmony_ci                  const Saturate saturate,
489cb93a386Sopenharmony_ci                  const GrAAType aaType,
490cb93a386Sopenharmony_ci                  const SkCanvas::SrcRectConstraint constraint,
491cb93a386Sopenharmony_ci                  const SkMatrix& viewMatrix,
492cb93a386Sopenharmony_ci                  sk_sp<GrColorSpaceXform> textureColorSpaceXform)
493cb93a386Sopenharmony_ci            : INHERITED(ClassID())
494cb93a386Sopenharmony_ci            , fQuads(cnt, true /* includes locals */)
495cb93a386Sopenharmony_ci            , fTextureColorSpaceXform(std::move(textureColorSpaceXform))
496cb93a386Sopenharmony_ci            , fDesc(nullptr)
497cb93a386Sopenharmony_ci            , fMetadata(set[0].fProxyView.swizzle(),
498cb93a386Sopenharmony_ci                        GrSamplerState::Filter::kNearest,
499cb93a386Sopenharmony_ci                        GrSamplerState::MipmapMode::kNone,
500cb93a386Sopenharmony_ci                        Subset::kNo,
501cb93a386Sopenharmony_ci                        saturate) {
502cb93a386Sopenharmony_ci        // Update counts to reflect the batch op
503cb93a386Sopenharmony_ci        fMetadata.fProxyCount = SkToUInt(proxyRunCnt);
504cb93a386Sopenharmony_ci        fMetadata.fTotalQuadCount = SkToUInt(cnt);
505cb93a386Sopenharmony_ci
506cb93a386Sopenharmony_ci        SkRect bounds = SkRectPriv::MakeLargestInverted();
507cb93a386Sopenharmony_ci
508cb93a386Sopenharmony_ci        GrAAType netAAType = GrAAType::kNone; // aa type maximally compatible with all dst rects
509cb93a386Sopenharmony_ci        Subset netSubset = Subset::kNo;
510cb93a386Sopenharmony_ci        GrSamplerState::Filter netFilter = GrSamplerState::Filter::kNearest;
511cb93a386Sopenharmony_ci        GrSamplerState::MipmapMode netMM = GrSamplerState::MipmapMode::kNone;
512cb93a386Sopenharmony_ci        bool hasSubpixel = false;
513cb93a386Sopenharmony_ci
514cb93a386Sopenharmony_ci        const GrSurfaceProxy* curProxy = nullptr;
515cb93a386Sopenharmony_ci
516cb93a386Sopenharmony_ci        // 'q' is the index in 'set' and fQuadBuffer; 'p' is the index in fViewCountPairs and only
517cb93a386Sopenharmony_ci        // increases when set[q]'s proxy changes.
518cb93a386Sopenharmony_ci        int p = 0;
519cb93a386Sopenharmony_ci        for (int q = 0; q < cnt; ++q) {
520cb93a386Sopenharmony_ci            SkASSERT(mm == GrSamplerState::MipmapMode::kNone ||
521cb93a386Sopenharmony_ci                     (set[0].fProxyView.proxy()->asTextureProxy()->mipmapped() ==
522cb93a386Sopenharmony_ci                      GrMipmapped::kYes));
523cb93a386Sopenharmony_ci            if (q == 0) {
524cb93a386Sopenharmony_ci                // We do not placement new the first ViewCountPair since that one is allocated and
525cb93a386Sopenharmony_ci                // initialized as part of the TextureOp creation.
526cb93a386Sopenharmony_ci                fViewCountPairs[0].fProxy = set[0].fProxyView.detachProxy();
527cb93a386Sopenharmony_ci                fViewCountPairs[0].fQuadCnt = 0;
528cb93a386Sopenharmony_ci                curProxy = fViewCountPairs[0].fProxy.get();
529cb93a386Sopenharmony_ci            } else if (set[q].fProxyView.proxy() != curProxy) {
530cb93a386Sopenharmony_ci                // We must placement new the ViewCountPairs here so that the sk_sps in the
531cb93a386Sopenharmony_ci                // GrSurfaceProxyView get initialized properly.
532cb93a386Sopenharmony_ci                new(&fViewCountPairs[++p])ViewCountPair({set[q].fProxyView.detachProxy(), 0});
533cb93a386Sopenharmony_ci
534cb93a386Sopenharmony_ci                curProxy = fViewCountPairs[p].fProxy.get();
535cb93a386Sopenharmony_ci                SkASSERT(GrTextureProxy::ProxiesAreCompatibleAsDynamicState(
536cb93a386Sopenharmony_ci                        curProxy, fViewCountPairs[0].fProxy.get()));
537cb93a386Sopenharmony_ci                SkASSERT(fMetadata.fSwizzle == set[q].fProxyView.swizzle());
538cb93a386Sopenharmony_ci            } // else another quad referencing the same proxy
539cb93a386Sopenharmony_ci
540cb93a386Sopenharmony_ci            SkMatrix ctm = viewMatrix;
541cb93a386Sopenharmony_ci            if (set[q].fPreViewMatrix) {
542cb93a386Sopenharmony_ci                ctm.preConcat(*set[q].fPreViewMatrix);
543cb93a386Sopenharmony_ci            }
544cb93a386Sopenharmony_ci
545cb93a386Sopenharmony_ci            // Use dstRect/srcRect unless dstClip is provided, in which case derive new source
546cb93a386Sopenharmony_ci            // coordinates by mapping dstClipQuad by the dstRect to srcRect transform.
547cb93a386Sopenharmony_ci            DrawQuad quad;
548cb93a386Sopenharmony_ci            if (set[q].fDstClipQuad) {
549cb93a386Sopenharmony_ci                quad.fDevice = GrQuad::MakeFromSkQuad(set[q].fDstClipQuad, ctm);
550cb93a386Sopenharmony_ci
551cb93a386Sopenharmony_ci                SkPoint srcPts[4];
552cb93a386Sopenharmony_ci                GrMapRectPoints(set[q].fDstRect, set[q].fSrcRect, set[q].fDstClipQuad, srcPts, 4);
553cb93a386Sopenharmony_ci                quad.fLocal = GrQuad::MakeFromSkQuad(srcPts, SkMatrix::I());
554cb93a386Sopenharmony_ci            } else {
555cb93a386Sopenharmony_ci                quad.fDevice = GrQuad::MakeFromRect(set[q].fDstRect, ctm);
556cb93a386Sopenharmony_ci                quad.fLocal = GrQuad(set[q].fSrcRect);
557cb93a386Sopenharmony_ci            }
558cb93a386Sopenharmony_ci
559cb93a386Sopenharmony_ci            // This may be reduced per-quad from the requested aggregate filtering level, and used
560cb93a386Sopenharmony_ci            // to determine if the subset is needed for the entry as well.
561cb93a386Sopenharmony_ci            GrSamplerState::Filter filterForQuad = filter;
562cb93a386Sopenharmony_ci            if (netFilter != filter || netMM != mm) {
563cb93a386Sopenharmony_ci                // The only way netFilter != filter is if linear is requested and we haven't yet
564cb93a386Sopenharmony_ci                // found a quad that requires linear (so net is still nearest). Similar for mip
565cb93a386Sopenharmony_ci                // mapping.
566cb93a386Sopenharmony_ci                SkASSERT(filter == netFilter ||
567cb93a386Sopenharmony_ci                         (netFilter == GrSamplerState::Filter::kNearest && filter > netFilter));
568cb93a386Sopenharmony_ci                SkASSERT(mm == netMM ||
569cb93a386Sopenharmony_ci                         (netMM == GrSamplerState::MipmapMode::kNone && mm > netMM));
570cb93a386Sopenharmony_ci                auto [mustFilter, mustMM] = filter_and_mm_have_effect(quad.fLocal, quad.fDevice);
571cb93a386Sopenharmony_ci                if (filter != GrSamplerState::Filter::kNearest) {
572cb93a386Sopenharmony_ci                    if (mustFilter) {
573cb93a386Sopenharmony_ci                        netFilter = filter; // upgrade batch to higher filter level
574cb93a386Sopenharmony_ci                    } else {
575cb93a386Sopenharmony_ci                        filterForQuad = GrSamplerState::Filter::kNearest; // downgrade entry
576cb93a386Sopenharmony_ci                    }
577cb93a386Sopenharmony_ci                }
578cb93a386Sopenharmony_ci                if (mustMM && mm != GrSamplerState::MipmapMode::kNone) {
579cb93a386Sopenharmony_ci                    netMM = mm;
580cb93a386Sopenharmony_ci                }
581cb93a386Sopenharmony_ci            }
582cb93a386Sopenharmony_ci
583cb93a386Sopenharmony_ci            // Determine the AA type for the quad, then merge with net AA type
584cb93a386Sopenharmony_ci            GrAAType aaForQuad;
585cb93a386Sopenharmony_ci            GrQuadUtils::ResolveAAType(aaType, set[q].fAAFlags, quad.fDevice,
586cb93a386Sopenharmony_ci                                       &aaForQuad, &quad.fEdgeFlags);
587cb93a386Sopenharmony_ci            // Update overall bounds of the op as the union of all quads
588cb93a386Sopenharmony_ci            bounds.joinPossiblyEmptyRect(quad.fDevice.bounds());
589cb93a386Sopenharmony_ci            hasSubpixel |= GrQuadUtils::WillUseHairline(quad.fDevice, aaForQuad, quad.fEdgeFlags);
590cb93a386Sopenharmony_ci
591cb93a386Sopenharmony_ci            // Resolve sets aaForQuad to aaType or None, there is never a change between aa methods
592cb93a386Sopenharmony_ci            SkASSERT(aaForQuad == GrAAType::kNone || aaForQuad == aaType);
593cb93a386Sopenharmony_ci            if (netAAType == GrAAType::kNone && aaForQuad != GrAAType::kNone) {
594cb93a386Sopenharmony_ci                netAAType = aaType;
595cb93a386Sopenharmony_ci            }
596cb93a386Sopenharmony_ci
597cb93a386Sopenharmony_ci            // Calculate metadata for the entry
598cb93a386Sopenharmony_ci            const SkRect* subsetForQuad = nullptr;
599cb93a386Sopenharmony_ci            if (constraint == SkCanvas::kStrict_SrcRectConstraint) {
600cb93a386Sopenharmony_ci                // Check (briefly) if the subset rect is actually needed for this set entry.
601cb93a386Sopenharmony_ci                SkRect* subsetRect = &set[q].fSrcRect;
602cb93a386Sopenharmony_ci                if (!subsetRect->contains(curProxy->backingStoreBoundsRect())) {
603cb93a386Sopenharmony_ci                    if (!safe_to_ignore_subset_rect(aaForQuad, filterForQuad, quad, *subsetRect)) {
604cb93a386Sopenharmony_ci                        netSubset = Subset::kYes;
605cb93a386Sopenharmony_ci                        subsetForQuad = subsetRect;
606cb93a386Sopenharmony_ci                    }
607cb93a386Sopenharmony_ci                }
608cb93a386Sopenharmony_ci            }
609cb93a386Sopenharmony_ci
610cb93a386Sopenharmony_ci            // Normalize the src quads and apply origin
611cb93a386Sopenharmony_ci            NormalizationParams proxyParams = proxy_normalization_params(
612cb93a386Sopenharmony_ci                    curProxy, set[q].fProxyView.origin());
613cb93a386Sopenharmony_ci            normalize_src_quad(proxyParams, &quad.fLocal);
614cb93a386Sopenharmony_ci
615cb93a386Sopenharmony_ci            // This subset may represent a no-op, otherwise it will have the origin and dimensions
616cb93a386Sopenharmony_ci            // of the texture applied to it.
617cb93a386Sopenharmony_ci            SkRect subset = normalize_and_inset_subset(filter, proxyParams, subsetForQuad);
618cb93a386Sopenharmony_ci
619cb93a386Sopenharmony_ci            // Always append a quad (or 2 if perspective clipped), it just may refer back to a prior
620cb93a386Sopenharmony_ci            // ViewCountPair (this frequently happens when Chrome draws 9-patches).
621cb93a386Sopenharmony_ci            fViewCountPairs[p].fQuadCnt += this->appendQuad(&quad, set[q].fColor, subset);
622cb93a386Sopenharmony_ci        }
623cb93a386Sopenharmony_ci        // The # of proxy switches should match what was provided (+1 because we incremented p
624cb93a386Sopenharmony_ci        // when a new proxy was encountered).
625cb93a386Sopenharmony_ci        SkASSERT((p + 1) == fMetadata.fProxyCount);
626cb93a386Sopenharmony_ci        SkASSERT(fQuads.count() == fMetadata.fTotalQuadCount);
627cb93a386Sopenharmony_ci
628cb93a386Sopenharmony_ci        fMetadata.fAAType = static_cast<uint16_t>(netAAType);
629cb93a386Sopenharmony_ci        fMetadata.fFilter = static_cast<uint16_t>(netFilter);
630cb93a386Sopenharmony_ci        fMetadata.fSubset = static_cast<uint16_t>(netSubset);
631cb93a386Sopenharmony_ci
632cb93a386Sopenharmony_ci        this->setBounds(bounds, HasAABloat(netAAType == GrAAType::kCoverage),
633cb93a386Sopenharmony_ci                        hasSubpixel ? IsHairline::kYes : IsHairline::kNo);
634cb93a386Sopenharmony_ci    }
635cb93a386Sopenharmony_ci
636cb93a386Sopenharmony_ci    int appendQuad(DrawQuad* quad, const SkPMColor4f& color, const SkRect& subset) {
637cb93a386Sopenharmony_ci        DrawQuad extra;
638cb93a386Sopenharmony_ci        // Always clip to W0 to stay consistent with GrQuad::bounds
639cb93a386Sopenharmony_ci        int quadCount = GrQuadUtils::ClipToW0(quad, &extra);
640cb93a386Sopenharmony_ci        if (quadCount == 0) {
641cb93a386Sopenharmony_ci            // We can't discard the op at this point, but disable AA flags so it won't go through
642cb93a386Sopenharmony_ci            // inset/outset processing
643cb93a386Sopenharmony_ci            quad->fEdgeFlags = GrQuadAAFlags::kNone;
644cb93a386Sopenharmony_ci            quadCount = 1;
645cb93a386Sopenharmony_ci        }
646cb93a386Sopenharmony_ci        fQuads.append(quad->fDevice, {color, subset, quad->fEdgeFlags},  &quad->fLocal);
647cb93a386Sopenharmony_ci        if (quadCount > 1) {
648cb93a386Sopenharmony_ci            fQuads.append(extra.fDevice, {color, subset, extra.fEdgeFlags}, &extra.fLocal);
649cb93a386Sopenharmony_ci            fMetadata.fTotalQuadCount++;
650cb93a386Sopenharmony_ci        }
651cb93a386Sopenharmony_ci        return quadCount;
652cb93a386Sopenharmony_ci    }
653cb93a386Sopenharmony_ci
654cb93a386Sopenharmony_ci    GrProgramInfo* programInfo() override {
655cb93a386Sopenharmony_ci        // Although this Op implements its own onPrePrepareDraws it calls GrMeshDrawOps' version so
656cb93a386Sopenharmony_ci        // this entry point will be called.
657cb93a386Sopenharmony_ci        return (fDesc) ? fDesc->fProgramInfo : nullptr;
658cb93a386Sopenharmony_ci    }
659cb93a386Sopenharmony_ci
660cb93a386Sopenharmony_ci    void onCreateProgramInfo(const GrCaps* caps,
661cb93a386Sopenharmony_ci                             SkArenaAlloc* arena,
662cb93a386Sopenharmony_ci                             const GrSurfaceProxyView& writeView,
663cb93a386Sopenharmony_ci                             bool usesMSAASurface,
664cb93a386Sopenharmony_ci                             GrAppliedClip&& appliedClip,
665cb93a386Sopenharmony_ci                             const GrDstProxyView& dstProxyView,
666cb93a386Sopenharmony_ci                             GrXferBarrierFlags renderPassXferBarriers,
667cb93a386Sopenharmony_ci                             GrLoadOp colorLoadOp) override {
668cb93a386Sopenharmony_ci        SkASSERT(fDesc);
669cb93a386Sopenharmony_ci
670cb93a386Sopenharmony_ci        GrGeometryProcessor* gp;
671cb93a386Sopenharmony_ci
672cb93a386Sopenharmony_ci        {
673cb93a386Sopenharmony_ci            const GrBackendFormat& backendFormat =
674cb93a386Sopenharmony_ci                    fViewCountPairs[0].fProxy->backendFormat();
675cb93a386Sopenharmony_ci
676cb93a386Sopenharmony_ci            GrSamplerState samplerState = GrSamplerState(GrSamplerState::WrapMode::kClamp,
677cb93a386Sopenharmony_ci                                                         fMetadata.filter());
678cb93a386Sopenharmony_ci
679cb93a386Sopenharmony_ci            gp = skgpu::v1::QuadPerEdgeAA::MakeTexturedProcessor(
680cb93a386Sopenharmony_ci                    arena, fDesc->fVertexSpec, *caps->shaderCaps(), backendFormat, samplerState,
681cb93a386Sopenharmony_ci                    fMetadata.fSwizzle, std::move(fTextureColorSpaceXform), fMetadata.saturate());
682cb93a386Sopenharmony_ci
683cb93a386Sopenharmony_ci            SkASSERT(fDesc->fVertexSpec.vertexSize() == gp->vertexStride());
684cb93a386Sopenharmony_ci        }
685cb93a386Sopenharmony_ci
686cb93a386Sopenharmony_ci        fDesc->fProgramInfo = GrSimpleMeshDrawOpHelper::CreateProgramInfo(
687cb93a386Sopenharmony_ci                caps, arena, writeView, usesMSAASurface, std::move(appliedClip), dstProxyView, gp,
688cb93a386Sopenharmony_ci                GrProcessorSet::MakeEmptySet(), fDesc->fVertexSpec.primitiveType(),
689cb93a386Sopenharmony_ci                renderPassXferBarriers, colorLoadOp, GrPipeline::InputFlags::kNone);
690cb93a386Sopenharmony_ci    }
691cb93a386Sopenharmony_ci
692cb93a386Sopenharmony_ci    void onPrePrepareDraws(GrRecordingContext* context,
693cb93a386Sopenharmony_ci                           const GrSurfaceProxyView& writeView,
694cb93a386Sopenharmony_ci                           GrAppliedClip* clip,
695cb93a386Sopenharmony_ci                           const GrDstProxyView& dstProxyView,
696cb93a386Sopenharmony_ci                           GrXferBarrierFlags renderPassXferBarriers,
697cb93a386Sopenharmony_ci                           GrLoadOp colorLoadOp) override {
698cb93a386Sopenharmony_ci        TRACE_EVENT0("skia.gpu", TRACE_FUNC);
699cb93a386Sopenharmony_ci
700cb93a386Sopenharmony_ci        SkDEBUGCODE(this->validate();)
701cb93a386Sopenharmony_ci        SkASSERT(!fDesc);
702cb93a386Sopenharmony_ci
703cb93a386Sopenharmony_ci        SkArenaAlloc* arena = context->priv().recordTimeAllocator();
704cb93a386Sopenharmony_ci
705cb93a386Sopenharmony_ci        fDesc = arena->make<Desc>();
706cb93a386Sopenharmony_ci        this->characterize(fDesc);
707cb93a386Sopenharmony_ci        fDesc->allocatePrePreparedVertices(arena);
708cb93a386Sopenharmony_ci        FillInVertices(*context->priv().caps(), this, fDesc, fDesc->fPrePreparedVertices);
709cb93a386Sopenharmony_ci
710cb93a386Sopenharmony_ci        // This will call onCreateProgramInfo and register the created program with the DDL.
711cb93a386Sopenharmony_ci        this->INHERITED::onPrePrepareDraws(context, writeView, clip, dstProxyView,
712cb93a386Sopenharmony_ci                                           renderPassXferBarriers, colorLoadOp);
713cb93a386Sopenharmony_ci    }
714cb93a386Sopenharmony_ci
715cb93a386Sopenharmony_ci    static void FillInVertices(const GrCaps& caps,
716cb93a386Sopenharmony_ci                               TextureOpImpl* texOp,
717cb93a386Sopenharmony_ci                               Desc* desc,
718cb93a386Sopenharmony_ci                               char* vertexData) {
719cb93a386Sopenharmony_ci        SkASSERT(vertexData);
720cb93a386Sopenharmony_ci
721cb93a386Sopenharmony_ci        SkDEBUGCODE(int totQuadsSeen = 0;)
722cb93a386Sopenharmony_ci        SkDEBUGCODE(int totVerticesSeen = 0;)
723cb93a386Sopenharmony_ci        SkDEBUGCODE(const size_t vertexSize = desc->fVertexSpec.vertexSize());
724cb93a386Sopenharmony_ci
725cb93a386Sopenharmony_ci        skgpu::v1::QuadPerEdgeAA::Tessellator tessellator(desc->fVertexSpec, vertexData);
726cb93a386Sopenharmony_ci        for (const auto& op : ChainRange<TextureOpImpl>(texOp)) {
727cb93a386Sopenharmony_ci            auto iter = op.fQuads.iterator();
728cb93a386Sopenharmony_ci            for (unsigned p = 0; p < op.fMetadata.fProxyCount; ++p) {
729cb93a386Sopenharmony_ci                const int quadCnt = op.fViewCountPairs[p].fQuadCnt;
730cb93a386Sopenharmony_ci                SkDEBUGCODE(int meshVertexCnt = quadCnt * desc->fVertexSpec.verticesPerQuad());
731cb93a386Sopenharmony_ci
732cb93a386Sopenharmony_ci                for (int i = 0; i < quadCnt && iter.next(); ++i) {
733cb93a386Sopenharmony_ci                    SkASSERT(iter.isLocalValid());
734cb93a386Sopenharmony_ci                    const ColorSubsetAndAA& info = iter.metadata();
735cb93a386Sopenharmony_ci
736cb93a386Sopenharmony_ci                    tessellator.append(iter.deviceQuad(), iter.localQuad(), info.fColor,
737cb93a386Sopenharmony_ci                                       info.fSubsetRect, info.aaFlags());
738cb93a386Sopenharmony_ci                }
739cb93a386Sopenharmony_ci
740cb93a386Sopenharmony_ci                SkASSERT((totVerticesSeen + meshVertexCnt) * vertexSize
741cb93a386Sopenharmony_ci                         == (size_t)(tessellator.vertices() - vertexData));
742cb93a386Sopenharmony_ci
743cb93a386Sopenharmony_ci                SkDEBUGCODE(totQuadsSeen += quadCnt;)
744cb93a386Sopenharmony_ci                SkDEBUGCODE(totVerticesSeen += meshVertexCnt);
745cb93a386Sopenharmony_ci                SkASSERT(totQuadsSeen * desc->fVertexSpec.verticesPerQuad() == totVerticesSeen);
746cb93a386Sopenharmony_ci            }
747cb93a386Sopenharmony_ci
748cb93a386Sopenharmony_ci            // If quad counts per proxy were calculated correctly, the entire iterator
749cb93a386Sopenharmony_ci            // should have been consumed.
750cb93a386Sopenharmony_ci            SkASSERT(!iter.next());
751cb93a386Sopenharmony_ci        }
752cb93a386Sopenharmony_ci
753cb93a386Sopenharmony_ci        SkASSERT(desc->totalSizeInBytes() == (size_t)(tessellator.vertices() - vertexData));
754cb93a386Sopenharmony_ci        SkASSERT(totQuadsSeen == desc->fNumTotalQuads);
755cb93a386Sopenharmony_ci        SkASSERT(totVerticesSeen == desc->totalNumVertices());
756cb93a386Sopenharmony_ci    }
757cb93a386Sopenharmony_ci
758cb93a386Sopenharmony_ci#ifdef SK_DEBUG
759cb93a386Sopenharmony_ci    static int validate_op(GrTextureType textureType,
760cb93a386Sopenharmony_ci                           GrAAType aaType,
761cb93a386Sopenharmony_ci                           GrSwizzle swizzle,
762cb93a386Sopenharmony_ci                           const TextureOpImpl* op) {
763cb93a386Sopenharmony_ci        SkASSERT(op->fMetadata.fSwizzle == swizzle);
764cb93a386Sopenharmony_ci
765cb93a386Sopenharmony_ci        int quadCount = 0;
766cb93a386Sopenharmony_ci        for (unsigned p = 0; p < op->fMetadata.fProxyCount; ++p) {
767cb93a386Sopenharmony_ci            auto* proxy = op->fViewCountPairs[p].fProxy->asTextureProxy();
768cb93a386Sopenharmony_ci            quadCount += op->fViewCountPairs[p].fQuadCnt;
769cb93a386Sopenharmony_ci            SkASSERT(proxy);
770cb93a386Sopenharmony_ci            SkASSERT(proxy->textureType() == textureType);
771cb93a386Sopenharmony_ci        }
772cb93a386Sopenharmony_ci
773cb93a386Sopenharmony_ci        SkASSERT(aaType == op->fMetadata.aaType());
774cb93a386Sopenharmony_ci        return quadCount;
775cb93a386Sopenharmony_ci    }
776cb93a386Sopenharmony_ci
777cb93a386Sopenharmony_ci    void validate() const override {
778cb93a386Sopenharmony_ci        // NOTE: Since this is debug-only code, we use the virtual asTextureProxy()
779cb93a386Sopenharmony_ci        auto textureType = fViewCountPairs[0].fProxy->asTextureProxy()->textureType();
780cb93a386Sopenharmony_ci        GrAAType aaType = fMetadata.aaType();
781cb93a386Sopenharmony_ci        GrSwizzle swizzle = fMetadata.fSwizzle;
782cb93a386Sopenharmony_ci
783cb93a386Sopenharmony_ci        int quadCount = validate_op(textureType, aaType, swizzle, this);
784cb93a386Sopenharmony_ci
785cb93a386Sopenharmony_ci        for (const GrOp* tmp = this->prevInChain(); tmp; tmp = tmp->prevInChain()) {
786cb93a386Sopenharmony_ci            quadCount += validate_op(textureType, aaType, swizzle,
787cb93a386Sopenharmony_ci                                     static_cast<const TextureOpImpl*>(tmp));
788cb93a386Sopenharmony_ci        }
789cb93a386Sopenharmony_ci
790cb93a386Sopenharmony_ci        for (const GrOp* tmp = this->nextInChain(); tmp; tmp = tmp->nextInChain()) {
791cb93a386Sopenharmony_ci            quadCount += validate_op(textureType, aaType, swizzle,
792cb93a386Sopenharmony_ci                                     static_cast<const TextureOpImpl*>(tmp));
793cb93a386Sopenharmony_ci        }
794cb93a386Sopenharmony_ci
795cb93a386Sopenharmony_ci        SkASSERT(quadCount == this->numChainedQuads());
796cb93a386Sopenharmony_ci    }
797cb93a386Sopenharmony_ci
798cb93a386Sopenharmony_ci#endif
799cb93a386Sopenharmony_ci
800cb93a386Sopenharmony_ci#if GR_TEST_UTILS
801cb93a386Sopenharmony_ci    int numQuads() const final { return this->totNumQuads(); }
802cb93a386Sopenharmony_ci#endif
803cb93a386Sopenharmony_ci
804cb93a386Sopenharmony_ci    void characterize(Desc* desc) const {
805cb93a386Sopenharmony_ci        SkDEBUGCODE(this->validate();)
806cb93a386Sopenharmony_ci
807cb93a386Sopenharmony_ci        GrQuad::Type quadType = GrQuad::Type::kAxisAligned;
808cb93a386Sopenharmony_ci        ColorType colorType = ColorType::kNone;
809cb93a386Sopenharmony_ci        GrQuad::Type srcQuadType = GrQuad::Type::kAxisAligned;
810cb93a386Sopenharmony_ci        Subset subset = Subset::kNo;
811cb93a386Sopenharmony_ci        GrAAType overallAAType = fMetadata.aaType();
812cb93a386Sopenharmony_ci
813cb93a386Sopenharmony_ci        desc->fNumProxies = 0;
814cb93a386Sopenharmony_ci        desc->fNumTotalQuads = 0;
815cb93a386Sopenharmony_ci        int maxQuadsPerMesh = 0;
816cb93a386Sopenharmony_ci
817cb93a386Sopenharmony_ci        for (const auto& op : ChainRange<TextureOpImpl>(this)) {
818cb93a386Sopenharmony_ci            if (op.fQuads.deviceQuadType() > quadType) {
819cb93a386Sopenharmony_ci                quadType = op.fQuads.deviceQuadType();
820cb93a386Sopenharmony_ci            }
821cb93a386Sopenharmony_ci            if (op.fQuads.localQuadType() > srcQuadType) {
822cb93a386Sopenharmony_ci                srcQuadType = op.fQuads.localQuadType();
823cb93a386Sopenharmony_ci            }
824cb93a386Sopenharmony_ci            if (op.fMetadata.subset() == Subset::kYes) {
825cb93a386Sopenharmony_ci                subset = Subset::kYes;
826cb93a386Sopenharmony_ci            }
827cb93a386Sopenharmony_ci            colorType = std::max(colorType, op.fMetadata.colorType());
828cb93a386Sopenharmony_ci            desc->fNumProxies += op.fMetadata.fProxyCount;
829cb93a386Sopenharmony_ci
830cb93a386Sopenharmony_ci            for (unsigned p = 0; p < op.fMetadata.fProxyCount; ++p) {
831cb93a386Sopenharmony_ci                maxQuadsPerMesh = std::max(op.fViewCountPairs[p].fQuadCnt, maxQuadsPerMesh);
832cb93a386Sopenharmony_ci            }
833cb93a386Sopenharmony_ci            desc->fNumTotalQuads += op.totNumQuads();
834cb93a386Sopenharmony_ci
835cb93a386Sopenharmony_ci            if (op.fMetadata.aaType() == GrAAType::kCoverage) {
836cb93a386Sopenharmony_ci                overallAAType = GrAAType::kCoverage;
837cb93a386Sopenharmony_ci            }
838cb93a386Sopenharmony_ci        }
839cb93a386Sopenharmony_ci
840cb93a386Sopenharmony_ci        SkASSERT(desc->fNumTotalQuads == this->numChainedQuads());
841cb93a386Sopenharmony_ci
842cb93a386Sopenharmony_ci        SkASSERT(!CombinedQuadCountWillOverflow(overallAAType, false, desc->fNumTotalQuads));
843cb93a386Sopenharmony_ci
844cb93a386Sopenharmony_ci        auto indexBufferOption = skgpu::v1::QuadPerEdgeAA::CalcIndexBufferOption(overallAAType,
845cb93a386Sopenharmony_ci                                                                                 maxQuadsPerMesh);
846cb93a386Sopenharmony_ci
847cb93a386Sopenharmony_ci        desc->fVertexSpec = VertexSpec(quadType, colorType, srcQuadType, /* hasLocal */ true,
848cb93a386Sopenharmony_ci                                       subset, overallAAType, /* alpha as coverage */ true,
849cb93a386Sopenharmony_ci                                       indexBufferOption);
850cb93a386Sopenharmony_ci
851cb93a386Sopenharmony_ci        SkASSERT(desc->fNumTotalQuads <= skgpu::v1::QuadPerEdgeAA::QuadLimit(indexBufferOption));
852cb93a386Sopenharmony_ci    }
853cb93a386Sopenharmony_ci
854cb93a386Sopenharmony_ci    int totNumQuads() const {
855cb93a386Sopenharmony_ci#ifdef SK_DEBUG
856cb93a386Sopenharmony_ci        int tmp = 0;
857cb93a386Sopenharmony_ci        for (unsigned p = 0; p < fMetadata.fProxyCount; ++p) {
858cb93a386Sopenharmony_ci            tmp += fViewCountPairs[p].fQuadCnt;
859cb93a386Sopenharmony_ci        }
860cb93a386Sopenharmony_ci        SkASSERT(tmp == fMetadata.fTotalQuadCount);
861cb93a386Sopenharmony_ci#endif
862cb93a386Sopenharmony_ci
863cb93a386Sopenharmony_ci        return fMetadata.fTotalQuadCount;
864cb93a386Sopenharmony_ci    }
865cb93a386Sopenharmony_ci
866cb93a386Sopenharmony_ci    int numChainedQuads() const {
867cb93a386Sopenharmony_ci        int numChainedQuads = this->totNumQuads();
868cb93a386Sopenharmony_ci
869cb93a386Sopenharmony_ci        for (const GrOp* tmp = this->prevInChain(); tmp; tmp = tmp->prevInChain()) {
870cb93a386Sopenharmony_ci            numChainedQuads += ((const TextureOpImpl*)tmp)->totNumQuads();
871cb93a386Sopenharmony_ci        }
872cb93a386Sopenharmony_ci
873cb93a386Sopenharmony_ci        for (const GrOp* tmp = this->nextInChain(); tmp; tmp = tmp->nextInChain()) {
874cb93a386Sopenharmony_ci            numChainedQuads += ((const TextureOpImpl*)tmp)->totNumQuads();
875cb93a386Sopenharmony_ci        }
876cb93a386Sopenharmony_ci
877cb93a386Sopenharmony_ci        return numChainedQuads;
878cb93a386Sopenharmony_ci    }
879cb93a386Sopenharmony_ci
880cb93a386Sopenharmony_ci    // onPrePrepareDraws may or may not have been called at this point
881cb93a386Sopenharmony_ci    void onPrepareDraws(GrMeshDrawTarget* target) override {
882cb93a386Sopenharmony_ci        TRACE_EVENT0("skia.gpu", TRACE_FUNC);
883cb93a386Sopenharmony_ci
884cb93a386Sopenharmony_ci        SkDEBUGCODE(this->validate();)
885cb93a386Sopenharmony_ci
886cb93a386Sopenharmony_ci        SkASSERT(!fDesc || fDesc->fPrePreparedVertices);
887cb93a386Sopenharmony_ci
888cb93a386Sopenharmony_ci        if (!fDesc) {
889cb93a386Sopenharmony_ci            SkArenaAlloc* arena = target->allocator();
890cb93a386Sopenharmony_ci            fDesc = arena->make<Desc>();
891cb93a386Sopenharmony_ci            this->characterize(fDesc);
892cb93a386Sopenharmony_ci            SkASSERT(!fDesc->fPrePreparedVertices);
893cb93a386Sopenharmony_ci        }
894cb93a386Sopenharmony_ci
895cb93a386Sopenharmony_ci        size_t vertexSize = fDesc->fVertexSpec.vertexSize();
896cb93a386Sopenharmony_ci
897cb93a386Sopenharmony_ci        void* vdata = target->makeVertexSpace(vertexSize, fDesc->totalNumVertices(),
898cb93a386Sopenharmony_ci                                              &fDesc->fVertexBuffer, &fDesc->fBaseVertex);
899cb93a386Sopenharmony_ci        if (!vdata) {
900cb93a386Sopenharmony_ci            SkDebugf("Could not allocate vertices\n");
901cb93a386Sopenharmony_ci            return;
902cb93a386Sopenharmony_ci        }
903cb93a386Sopenharmony_ci
904cb93a386Sopenharmony_ci        if (fDesc->fVertexSpec.needsIndexBuffer()) {
905cb93a386Sopenharmony_ci            fDesc->fIndexBuffer = skgpu::v1::QuadPerEdgeAA::GetIndexBuffer(
906cb93a386Sopenharmony_ci                    target, fDesc->fVertexSpec.indexBufferOption());
907cb93a386Sopenharmony_ci            if (!fDesc->fIndexBuffer) {
908cb93a386Sopenharmony_ci                SkDebugf("Could not allocate indices\n");
909cb93a386Sopenharmony_ci                return;
910cb93a386Sopenharmony_ci            }
911cb93a386Sopenharmony_ci        }
912cb93a386Sopenharmony_ci
913cb93a386Sopenharmony_ci        if (fDesc->fPrePreparedVertices) {
914cb93a386Sopenharmony_ci            memcpy(vdata, fDesc->fPrePreparedVertices, fDesc->totalSizeInBytes());
915cb93a386Sopenharmony_ci        } else {
916cb93a386Sopenharmony_ci            FillInVertices(target->caps(), this, fDesc, (char*) vdata);
917cb93a386Sopenharmony_ci        }
918cb93a386Sopenharmony_ci    }
919cb93a386Sopenharmony_ci
920cb93a386Sopenharmony_ci    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
921cb93a386Sopenharmony_ci        if (!fDesc->fVertexBuffer) {
922cb93a386Sopenharmony_ci            return;
923cb93a386Sopenharmony_ci        }
924cb93a386Sopenharmony_ci
925cb93a386Sopenharmony_ci        if (fDesc->fVertexSpec.needsIndexBuffer() && !fDesc->fIndexBuffer) {
926cb93a386Sopenharmony_ci            return;
927cb93a386Sopenharmony_ci        }
928cb93a386Sopenharmony_ci
929cb93a386Sopenharmony_ci        if (!fDesc->fProgramInfo) {
930cb93a386Sopenharmony_ci            this->createProgramInfo(flushState);
931cb93a386Sopenharmony_ci            SkASSERT(fDesc->fProgramInfo);
932cb93a386Sopenharmony_ci        }
933cb93a386Sopenharmony_ci
934cb93a386Sopenharmony_ci        flushState->bindPipelineAndScissorClip(*fDesc->fProgramInfo, chainBounds);
935cb93a386Sopenharmony_ci        flushState->bindBuffers(std::move(fDesc->fIndexBuffer), nullptr,
936cb93a386Sopenharmony_ci                                std::move(fDesc->fVertexBuffer));
937cb93a386Sopenharmony_ci
938cb93a386Sopenharmony_ci        int totQuadsSeen = 0;
939cb93a386Sopenharmony_ci        SkDEBUGCODE(int numDraws = 0;)
940cb93a386Sopenharmony_ci        for (const auto& op : ChainRange<TextureOpImpl>(this)) {
941cb93a386Sopenharmony_ci            for (unsigned p = 0; p < op.fMetadata.fProxyCount; ++p) {
942cb93a386Sopenharmony_ci                const int quadCnt = op.fViewCountPairs[p].fQuadCnt;
943cb93a386Sopenharmony_ci                SkASSERT(numDraws < fDesc->fNumProxies);
944cb93a386Sopenharmony_ci                flushState->bindTextures(fDesc->fProgramInfo->geomProc(),
945cb93a386Sopenharmony_ci                                         *op.fViewCountPairs[p].fProxy,
946cb93a386Sopenharmony_ci                                         fDesc->fProgramInfo->pipeline());
947cb93a386Sopenharmony_ci                skgpu::v1::QuadPerEdgeAA::IssueDraw(flushState->caps(), flushState->opsRenderPass(),
948cb93a386Sopenharmony_ci                                                    fDesc->fVertexSpec, totQuadsSeen, quadCnt,
949cb93a386Sopenharmony_ci                                                    fDesc->totalNumVertices(), fDesc->fBaseVertex);
950cb93a386Sopenharmony_ci                totQuadsSeen += quadCnt;
951cb93a386Sopenharmony_ci                SkDEBUGCODE(++numDraws;)
952cb93a386Sopenharmony_ci            }
953cb93a386Sopenharmony_ci        }
954cb93a386Sopenharmony_ci
955cb93a386Sopenharmony_ci        SkASSERT(totQuadsSeen == fDesc->fNumTotalQuads);
956cb93a386Sopenharmony_ci        SkASSERT(numDraws == fDesc->fNumProxies);
957cb93a386Sopenharmony_ci    }
958cb93a386Sopenharmony_ci
959cb93a386Sopenharmony_ci    void propagateCoverageAAThroughoutChain() {
960cb93a386Sopenharmony_ci        fMetadata.fAAType = static_cast<uint16_t>(GrAAType::kCoverage);
961cb93a386Sopenharmony_ci
962cb93a386Sopenharmony_ci        for (GrOp* tmp = this->prevInChain(); tmp; tmp = tmp->prevInChain()) {
963cb93a386Sopenharmony_ci            auto tex = static_cast<TextureOpImpl*>(tmp);
964cb93a386Sopenharmony_ci            SkASSERT(tex->fMetadata.aaType() == GrAAType::kCoverage ||
965cb93a386Sopenharmony_ci                     tex->fMetadata.aaType() == GrAAType::kNone);
966cb93a386Sopenharmony_ci            tex->fMetadata.fAAType = static_cast<uint16_t>(GrAAType::kCoverage);
967cb93a386Sopenharmony_ci        }
968cb93a386Sopenharmony_ci
969cb93a386Sopenharmony_ci        for (GrOp* tmp = this->nextInChain(); tmp; tmp = tmp->nextInChain()) {
970cb93a386Sopenharmony_ci            auto tex = static_cast<TextureOpImpl*>(tmp);
971cb93a386Sopenharmony_ci            SkASSERT(tex->fMetadata.aaType() == GrAAType::kCoverage ||
972cb93a386Sopenharmony_ci                     tex->fMetadata.aaType() == GrAAType::kNone);
973cb93a386Sopenharmony_ci            tex->fMetadata.fAAType = static_cast<uint16_t>(GrAAType::kCoverage);
974cb93a386Sopenharmony_ci        }
975cb93a386Sopenharmony_ci    }
976cb93a386Sopenharmony_ci
977cb93a386Sopenharmony_ci    CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
978cb93a386Sopenharmony_ci        TRACE_EVENT0("skia.gpu", TRACE_FUNC);
979cb93a386Sopenharmony_ci        auto that = t->cast<TextureOpImpl>();
980cb93a386Sopenharmony_ci
981cb93a386Sopenharmony_ci        SkDEBUGCODE(this->validate();)
982cb93a386Sopenharmony_ci        SkDEBUGCODE(that->validate();)
983cb93a386Sopenharmony_ci
984cb93a386Sopenharmony_ci        if (fDesc || that->fDesc) {
985cb93a386Sopenharmony_ci            // This should never happen (since only DDL recorded ops should be prePrepared)
986cb93a386Sopenharmony_ci            // but, in any case, we should never combine ops that that been prePrepared
987cb93a386Sopenharmony_ci            return CombineResult::kCannotCombine;
988cb93a386Sopenharmony_ci        }
989cb93a386Sopenharmony_ci
990cb93a386Sopenharmony_ci        if (fMetadata.subset() != that->fMetadata.subset()) {
991cb93a386Sopenharmony_ci            // It is technically possible to combine operations across subset modes, but performance
992cb93a386Sopenharmony_ci            // testing suggests it's better to make more draw calls where some take advantage of
993cb93a386Sopenharmony_ci            // the more optimal shader path without coordinate clamping.
994cb93a386Sopenharmony_ci            return CombineResult::kCannotCombine;
995cb93a386Sopenharmony_ci        }
996cb93a386Sopenharmony_ci        if (!GrColorSpaceXform::Equals(fTextureColorSpaceXform.get(),
997cb93a386Sopenharmony_ci                                       that->fTextureColorSpaceXform.get())) {
998cb93a386Sopenharmony_ci            return CombineResult::kCannotCombine;
999cb93a386Sopenharmony_ci        }
1000cb93a386Sopenharmony_ci
1001cb93a386Sopenharmony_ci        bool upgradeToCoverageAAOnMerge = false;
1002cb93a386Sopenharmony_ci        if (fMetadata.aaType() != that->fMetadata.aaType()) {
1003cb93a386Sopenharmony_ci            if (!CanUpgradeAAOnMerge(fMetadata.aaType(), that->fMetadata.aaType())) {
1004cb93a386Sopenharmony_ci                return CombineResult::kCannotCombine;
1005cb93a386Sopenharmony_ci            }
1006cb93a386Sopenharmony_ci            upgradeToCoverageAAOnMerge = true;
1007cb93a386Sopenharmony_ci        }
1008cb93a386Sopenharmony_ci
1009cb93a386Sopenharmony_ci        if (CombinedQuadCountWillOverflow(fMetadata.aaType(), upgradeToCoverageAAOnMerge,
1010cb93a386Sopenharmony_ci                                          this->numChainedQuads() + that->numChainedQuads())) {
1011cb93a386Sopenharmony_ci            return CombineResult::kCannotCombine;
1012cb93a386Sopenharmony_ci        }
1013cb93a386Sopenharmony_ci
1014cb93a386Sopenharmony_ci        if (fMetadata.saturate() != that->fMetadata.saturate()) {
1015cb93a386Sopenharmony_ci            return CombineResult::kCannotCombine;
1016cb93a386Sopenharmony_ci        }
1017cb93a386Sopenharmony_ci        if (fMetadata.filter() != that->fMetadata.filter()) {
1018cb93a386Sopenharmony_ci            return CombineResult::kCannotCombine;
1019cb93a386Sopenharmony_ci        }
1020cb93a386Sopenharmony_ci        if (fMetadata.mipmapMode() != that->fMetadata.mipmapMode()) {
1021cb93a386Sopenharmony_ci            return CombineResult::kCannotCombine;
1022cb93a386Sopenharmony_ci        }
1023cb93a386Sopenharmony_ci        if (fMetadata.fSwizzle != that->fMetadata.fSwizzle) {
1024cb93a386Sopenharmony_ci            return CombineResult::kCannotCombine;
1025cb93a386Sopenharmony_ci        }
1026cb93a386Sopenharmony_ci        const auto* thisProxy = fViewCountPairs[0].fProxy.get();
1027cb93a386Sopenharmony_ci        const auto* thatProxy = that->fViewCountPairs[0].fProxy.get();
1028cb93a386Sopenharmony_ci        if (fMetadata.fProxyCount > 1 || that->fMetadata.fProxyCount > 1 ||
1029cb93a386Sopenharmony_ci            thisProxy != thatProxy) {
1030cb93a386Sopenharmony_ci            // We can't merge across different proxies. Check if 'this' can be chained with 'that'.
1031cb93a386Sopenharmony_ci            if (GrTextureProxy::ProxiesAreCompatibleAsDynamicState(thisProxy, thatProxy) &&
1032cb93a386Sopenharmony_ci                caps.dynamicStateArrayGeometryProcessorTextureSupport() &&
1033cb93a386Sopenharmony_ci                fMetadata.aaType() == that->fMetadata.aaType()) {
1034cb93a386Sopenharmony_ci                // We only allow chaining when the aaTypes match bc otherwise the AA type
1035cb93a386Sopenharmony_ci                // reported by the chain can be inconsistent. That is, since chaining doesn't
1036cb93a386Sopenharmony_ci                // propagate revised AA information throughout the chain, the head of the chain
1037cb93a386Sopenharmony_ci                // could have an AA setting of kNone while the chain as a whole could have a
1038cb93a386Sopenharmony_ci                // setting of kCoverage. This inconsistency would then interfere with the validity
1039cb93a386Sopenharmony_ci                // of the CombinedQuadCountWillOverflow calls.
1040cb93a386Sopenharmony_ci                // This problem doesn't occur w/ merging bc we do propagate the AA information
1041cb93a386Sopenharmony_ci                // (in propagateCoverageAAThroughoutChain) below.
1042cb93a386Sopenharmony_ci                return CombineResult::kMayChain;
1043cb93a386Sopenharmony_ci            }
1044cb93a386Sopenharmony_ci            return CombineResult::kCannotCombine;
1045cb93a386Sopenharmony_ci        }
1046cb93a386Sopenharmony_ci
1047cb93a386Sopenharmony_ci        fMetadata.fSubset |= that->fMetadata.fSubset;
1048cb93a386Sopenharmony_ci        fMetadata.fColorType = std::max(fMetadata.fColorType, that->fMetadata.fColorType);
1049cb93a386Sopenharmony_ci
1050cb93a386Sopenharmony_ci        // Concatenate quad lists together
1051cb93a386Sopenharmony_ci        fQuads.concat(that->fQuads);
1052cb93a386Sopenharmony_ci        fViewCountPairs[0].fQuadCnt += that->fQuads.count();
1053cb93a386Sopenharmony_ci        fMetadata.fTotalQuadCount += that->fQuads.count();
1054cb93a386Sopenharmony_ci
1055cb93a386Sopenharmony_ci        if (upgradeToCoverageAAOnMerge) {
1056cb93a386Sopenharmony_ci            // This merger may be the start of a concatenation of two chains. When one
1057cb93a386Sopenharmony_ci            // of the chains mutates its AA the other must follow suit or else the above AA
1058cb93a386Sopenharmony_ci            // check may prevent later ops from chaining together. A specific example of this is
1059cb93a386Sopenharmony_ci            // when chain2 is prepended onto chain1:
1060cb93a386Sopenharmony_ci            //  chain1 (that): opA (non-AA/mergeable) opB (non-AA/non-mergeable)
1061cb93a386Sopenharmony_ci            //  chain2 (this): opC (cov-AA/non-mergeable) opD (cov-AA/mergeable)
1062cb93a386Sopenharmony_ci            // W/o this propagation, after opD & opA merge, opB and opC would say they couldn't
1063cb93a386Sopenharmony_ci            // chain - which would stop the concatenation process.
1064cb93a386Sopenharmony_ci            this->propagateCoverageAAThroughoutChain();
1065cb93a386Sopenharmony_ci            that->propagateCoverageAAThroughoutChain();
1066cb93a386Sopenharmony_ci        }
1067cb93a386Sopenharmony_ci
1068cb93a386Sopenharmony_ci        SkDEBUGCODE(this->validate();)
1069cb93a386Sopenharmony_ci
1070cb93a386Sopenharmony_ci        return CombineResult::kMerged;
1071cb93a386Sopenharmony_ci    }
1072cb93a386Sopenharmony_ci
1073cb93a386Sopenharmony_ci#if GR_TEST_UTILS
1074cb93a386Sopenharmony_ci    SkString onDumpInfo() const override {
1075cb93a386Sopenharmony_ci        SkString str = SkStringPrintf("# draws: %d\n", fQuads.count());
1076cb93a386Sopenharmony_ci        auto iter = fQuads.iterator();
1077cb93a386Sopenharmony_ci        for (unsigned p = 0; p < fMetadata.fProxyCount; ++p) {
1078cb93a386Sopenharmony_ci            SkString proxyStr = fViewCountPairs[p].fProxy->dump();
1079cb93a386Sopenharmony_ci            str.append(proxyStr);
1080cb93a386Sopenharmony_ci            str.appendf(", Filter: %d, MM: %d\n",
1081cb93a386Sopenharmony_ci                        static_cast<int>(fMetadata.fFilter),
1082cb93a386Sopenharmony_ci                        static_cast<int>(fMetadata.fMipmapMode));
1083cb93a386Sopenharmony_ci            for (int i = 0; i < fViewCountPairs[p].fQuadCnt && iter.next(); ++i) {
1084cb93a386Sopenharmony_ci                const GrQuad* quad = iter.deviceQuad();
1085cb93a386Sopenharmony_ci                GrQuad uv = iter.isLocalValid() ? *(iter.localQuad()) : GrQuad();
1086cb93a386Sopenharmony_ci                const ColorSubsetAndAA& info = iter.metadata();
1087cb93a386Sopenharmony_ci                str.appendf(
1088cb93a386Sopenharmony_ci                        "%d: Color: 0x%08x, Subset(%d): [L: %.2f, T: %.2f, R: %.2f, B: %.2f]\n"
1089cb93a386Sopenharmony_ci                        "  UVs  [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n"
1090cb93a386Sopenharmony_ci                        "  Quad [(%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f), (%.2f, %.2f)]\n",
1091cb93a386Sopenharmony_ci                        i, info.fColor.toBytes_RGBA(), fMetadata.fSubset, info.fSubsetRect.fLeft,
1092cb93a386Sopenharmony_ci                        info.fSubsetRect.fTop, info.fSubsetRect.fRight, info.fSubsetRect.fBottom,
1093cb93a386Sopenharmony_ci                        quad->point(0).fX, quad->point(0).fY, quad->point(1).fX, quad->point(1).fY,
1094cb93a386Sopenharmony_ci                        quad->point(2).fX, quad->point(2).fY, quad->point(3).fX, quad->point(3).fY,
1095cb93a386Sopenharmony_ci                        uv.point(0).fX, uv.point(0).fY, uv.point(1).fX, uv.point(1).fY,
1096cb93a386Sopenharmony_ci                        uv.point(2).fX, uv.point(2).fY, uv.point(3).fX, uv.point(3).fY);
1097cb93a386Sopenharmony_ci            }
1098cb93a386Sopenharmony_ci        }
1099cb93a386Sopenharmony_ci        return str;
1100cb93a386Sopenharmony_ci    }
1101cb93a386Sopenharmony_ci#endif
1102cb93a386Sopenharmony_ci
1103cb93a386Sopenharmony_ci    GrQuadBuffer<ColorSubsetAndAA> fQuads;
1104cb93a386Sopenharmony_ci    sk_sp<GrColorSpaceXform> fTextureColorSpaceXform;
1105cb93a386Sopenharmony_ci    // Most state of TextureOp is packed into these two field to minimize the op's size.
1106cb93a386Sopenharmony_ci    // Historically, increasing the size of TextureOp has caused surprising perf regressions, so
1107cb93a386Sopenharmony_ci    // consider/measure changes with care.
1108cb93a386Sopenharmony_ci    Desc* fDesc;
1109cb93a386Sopenharmony_ci    Metadata fMetadata;
1110cb93a386Sopenharmony_ci
1111cb93a386Sopenharmony_ci    // This field must go last. When allocating this op, we will allocate extra space to hold
1112cb93a386Sopenharmony_ci    // additional ViewCountPairs immediately after the op's allocation so we can treat this
1113cb93a386Sopenharmony_ci    // as an fProxyCnt-length array.
1114cb93a386Sopenharmony_ci    ViewCountPair fViewCountPairs[1];
1115cb93a386Sopenharmony_ci
1116cb93a386Sopenharmony_ci    using INHERITED = GrMeshDrawOp;
1117cb93a386Sopenharmony_ci};
1118cb93a386Sopenharmony_ci
1119cb93a386Sopenharmony_ci}  // anonymous namespace
1120cb93a386Sopenharmony_ci
1121cb93a386Sopenharmony_cinamespace skgpu::v1 {
1122cb93a386Sopenharmony_ci
1123cb93a386Sopenharmony_ci#if GR_TEST_UTILS
1124cb93a386Sopenharmony_ciuint32_t TextureOp::ClassID() {
1125cb93a386Sopenharmony_ci    return TextureOpImpl::ClassID();
1126cb93a386Sopenharmony_ci}
1127cb93a386Sopenharmony_ci#endif
1128cb93a386Sopenharmony_ci
1129cb93a386Sopenharmony_ciGrOp::Owner TextureOp::Make(GrRecordingContext* context,
1130cb93a386Sopenharmony_ci                            GrSurfaceProxyView proxyView,
1131cb93a386Sopenharmony_ci                            SkAlphaType alphaType,
1132cb93a386Sopenharmony_ci                            sk_sp<GrColorSpaceXform> textureXform,
1133cb93a386Sopenharmony_ci                            GrSamplerState::Filter filter,
1134cb93a386Sopenharmony_ci                            GrSamplerState::MipmapMode mm,
1135cb93a386Sopenharmony_ci                            const SkPMColor4f& color,
1136cb93a386Sopenharmony_ci                            Saturate saturate,
1137cb93a386Sopenharmony_ci                            SkBlendMode blendMode,
1138cb93a386Sopenharmony_ci                            GrAAType aaType,
1139cb93a386Sopenharmony_ci                            DrawQuad* quad,
1140cb93a386Sopenharmony_ci                            const SkRect* subset) {
1141cb93a386Sopenharmony_ci    // Apply optimizations that are valid whether or not using TextureOp or FillRectOp
1142cb93a386Sopenharmony_ci    if (subset && subset->contains(proxyView.proxy()->backingStoreBoundsRect())) {
1143cb93a386Sopenharmony_ci        // No need for a shader-based subset if hardware clamping achieves the same effect
1144cb93a386Sopenharmony_ci        subset = nullptr;
1145cb93a386Sopenharmony_ci    }
1146cb93a386Sopenharmony_ci
1147cb93a386Sopenharmony_ci    if (filter != GrSamplerState::Filter::kNearest || mm != GrSamplerState::MipmapMode::kNone) {
1148cb93a386Sopenharmony_ci        auto [mustFilter, mustMM] = filter_and_mm_have_effect(quad->fLocal, quad->fDevice);
1149cb93a386Sopenharmony_ci        if (!mustFilter) {
1150cb93a386Sopenharmony_ci            filter = GrSamplerState::Filter::kNearest;
1151cb93a386Sopenharmony_ci        }
1152cb93a386Sopenharmony_ci        if (!mustMM) {
1153cb93a386Sopenharmony_ci            mm = GrSamplerState::MipmapMode::kNone;
1154cb93a386Sopenharmony_ci        }
1155cb93a386Sopenharmony_ci    }
1156cb93a386Sopenharmony_ci
1157cb93a386Sopenharmony_ci    if (blendMode == SkBlendMode::kSrcOver) {
1158cb93a386Sopenharmony_ci        return TextureOpImpl::Make(context, std::move(proxyView), std::move(textureXform), filter,
1159cb93a386Sopenharmony_ci                                   mm, color, saturate, aaType, std::move(quad), subset);
1160cb93a386Sopenharmony_ci    } else {
1161cb93a386Sopenharmony_ci        // Emulate complex blending using FillRectOp
1162cb93a386Sopenharmony_ci        GrSamplerState samplerState(GrSamplerState::WrapMode::kClamp, filter, mm);
1163cb93a386Sopenharmony_ci        GrPaint paint;
1164cb93a386Sopenharmony_ci        paint.setColor4f(color);
1165cb93a386Sopenharmony_ci        paint.setXPFactory(SkBlendMode_AsXPFactory(blendMode));
1166cb93a386Sopenharmony_ci
1167cb93a386Sopenharmony_ci        std::unique_ptr<GrFragmentProcessor> fp;
1168cb93a386Sopenharmony_ci        const auto& caps = *context->priv().caps();
1169cb93a386Sopenharmony_ci        if (subset) {
1170cb93a386Sopenharmony_ci            SkRect localRect;
1171cb93a386Sopenharmony_ci            if (quad->fLocal.asRect(&localRect)) {
1172cb93a386Sopenharmony_ci                fp = GrTextureEffect::MakeSubset(std::move(proxyView), alphaType, SkMatrix::I(),
1173cb93a386Sopenharmony_ci                                                 samplerState, *subset, localRect, caps);
1174cb93a386Sopenharmony_ci            } else {
1175cb93a386Sopenharmony_ci                fp = GrTextureEffect::MakeSubset(std::move(proxyView), alphaType, SkMatrix::I(),
1176cb93a386Sopenharmony_ci                                                 samplerState, *subset, caps);
1177cb93a386Sopenharmony_ci            }
1178cb93a386Sopenharmony_ci        } else {
1179cb93a386Sopenharmony_ci            fp = GrTextureEffect::Make(std::move(proxyView), alphaType, SkMatrix::I(), samplerState,
1180cb93a386Sopenharmony_ci                                       caps);
1181cb93a386Sopenharmony_ci        }
1182cb93a386Sopenharmony_ci        fp = GrColorSpaceXformEffect::Make(std::move(fp), std::move(textureXform));
1183cb93a386Sopenharmony_ci        fp = GrBlendFragmentProcessor::Make(std::move(fp), nullptr, SkBlendMode::kModulate);
1184cb93a386Sopenharmony_ci        if (saturate == Saturate::kYes) {
1185cb93a386Sopenharmony_ci            fp = GrFragmentProcessor::ClampOutput(std::move(fp));
1186cb93a386Sopenharmony_ci        }
1187cb93a386Sopenharmony_ci        paint.setColorFragmentProcessor(std::move(fp));
1188cb93a386Sopenharmony_ci        return FillRectOp::Make(context, std::move(paint), aaType, quad);
1189cb93a386Sopenharmony_ci    }
1190cb93a386Sopenharmony_ci}
1191cb93a386Sopenharmony_ci
1192cb93a386Sopenharmony_ci// A helper class that assists in breaking up bulk API quad draws into manageable chunks.
1193cb93a386Sopenharmony_ciclass TextureOp::BatchSizeLimiter {
1194cb93a386Sopenharmony_cipublic:
1195cb93a386Sopenharmony_ci    BatchSizeLimiter(SurfaceDrawContext* sdc,
1196cb93a386Sopenharmony_ci                     const GrClip* clip,
1197cb93a386Sopenharmony_ci                     GrRecordingContext* rContext,
1198cb93a386Sopenharmony_ci                     int numEntries,
1199cb93a386Sopenharmony_ci                     GrSamplerState::Filter filter,
1200cb93a386Sopenharmony_ci                     GrSamplerState::MipmapMode mm,
1201cb93a386Sopenharmony_ci                     Saturate saturate,
1202cb93a386Sopenharmony_ci                     SkCanvas::SrcRectConstraint constraint,
1203cb93a386Sopenharmony_ci                     const SkMatrix& viewMatrix,
1204cb93a386Sopenharmony_ci                     sk_sp<GrColorSpaceXform> textureColorSpaceXform)
1205cb93a386Sopenharmony_ci            : fSDC(sdc)
1206cb93a386Sopenharmony_ci            , fClip(clip)
1207cb93a386Sopenharmony_ci            , fContext(rContext)
1208cb93a386Sopenharmony_ci            , fFilter(filter)
1209cb93a386Sopenharmony_ci            , fMipmapMode(mm)
1210cb93a386Sopenharmony_ci            , fSaturate(saturate)
1211cb93a386Sopenharmony_ci            , fConstraint(constraint)
1212cb93a386Sopenharmony_ci            , fViewMatrix(viewMatrix)
1213cb93a386Sopenharmony_ci            , fTextureColorSpaceXform(textureColorSpaceXform)
1214cb93a386Sopenharmony_ci            , fNumLeft(numEntries) {}
1215cb93a386Sopenharmony_ci
1216cb93a386Sopenharmony_ci    void createOp(GrTextureSetEntry set[], int clumpSize, GrAAType aaType) {
1217cb93a386Sopenharmony_ci
1218cb93a386Sopenharmony_ci        int clumpProxyCount = proxy_run_count(&set[fNumClumped], clumpSize);
1219cb93a386Sopenharmony_ci        GrOp::Owner op = TextureOpImpl::Make(fContext,
1220cb93a386Sopenharmony_ci                                             &set[fNumClumped],
1221cb93a386Sopenharmony_ci                                             clumpSize,
1222cb93a386Sopenharmony_ci                                             clumpProxyCount,
1223cb93a386Sopenharmony_ci                                             fFilter,
1224cb93a386Sopenharmony_ci                                             fMipmapMode,
1225cb93a386Sopenharmony_ci                                             fSaturate,
1226cb93a386Sopenharmony_ci                                             aaType,
1227cb93a386Sopenharmony_ci                                             fConstraint,
1228cb93a386Sopenharmony_ci                                             fViewMatrix,
1229cb93a386Sopenharmony_ci                                             fTextureColorSpaceXform);
1230cb93a386Sopenharmony_ci        fSDC->addDrawOp(fClip, std::move(op));
1231cb93a386Sopenharmony_ci
1232cb93a386Sopenharmony_ci        fNumLeft -= clumpSize;
1233cb93a386Sopenharmony_ci        fNumClumped += clumpSize;
1234cb93a386Sopenharmony_ci    }
1235cb93a386Sopenharmony_ci
1236cb93a386Sopenharmony_ci    int numLeft() const { return fNumLeft;  }
1237cb93a386Sopenharmony_ci    int baseIndex() const { return fNumClumped; }
1238cb93a386Sopenharmony_ci
1239cb93a386Sopenharmony_ciprivate:
1240cb93a386Sopenharmony_ci    SurfaceDrawContext*         fSDC;
1241cb93a386Sopenharmony_ci    const GrClip*               fClip;
1242cb93a386Sopenharmony_ci    GrRecordingContext*         fContext;
1243cb93a386Sopenharmony_ci    GrSamplerState::Filter      fFilter;
1244cb93a386Sopenharmony_ci    GrSamplerState::MipmapMode  fMipmapMode;
1245cb93a386Sopenharmony_ci    Saturate                    fSaturate;
1246cb93a386Sopenharmony_ci    SkCanvas::SrcRectConstraint fConstraint;
1247cb93a386Sopenharmony_ci    const SkMatrix&             fViewMatrix;
1248cb93a386Sopenharmony_ci    sk_sp<GrColorSpaceXform>    fTextureColorSpaceXform;
1249cb93a386Sopenharmony_ci
1250cb93a386Sopenharmony_ci    int                         fNumLeft;
1251cb93a386Sopenharmony_ci    int                         fNumClumped = 0; // also the offset for the start of the next clump
1252cb93a386Sopenharmony_ci};
1253cb93a386Sopenharmony_ci
1254cb93a386Sopenharmony_ci// Greedily clump quad draws together until the index buffer limit is exceeded.
1255cb93a386Sopenharmony_civoid TextureOp::AddTextureSetOps(SurfaceDrawContext* sdc,
1256cb93a386Sopenharmony_ci                                 const GrClip* clip,
1257cb93a386Sopenharmony_ci                                 GrRecordingContext* context,
1258cb93a386Sopenharmony_ci                                 GrTextureSetEntry set[],
1259cb93a386Sopenharmony_ci                                 int cnt,
1260cb93a386Sopenharmony_ci                                 int proxyRunCnt,
1261cb93a386Sopenharmony_ci                                 GrSamplerState::Filter filter,
1262cb93a386Sopenharmony_ci                                 GrSamplerState::MipmapMode mm,
1263cb93a386Sopenharmony_ci                                 Saturate saturate,
1264cb93a386Sopenharmony_ci                                 SkBlendMode blendMode,
1265cb93a386Sopenharmony_ci                                 GrAAType aaType,
1266cb93a386Sopenharmony_ci                                 SkCanvas::SrcRectConstraint constraint,
1267cb93a386Sopenharmony_ci                                 const SkMatrix& viewMatrix,
1268cb93a386Sopenharmony_ci                                 sk_sp<GrColorSpaceXform> textureColorSpaceXform) {
1269cb93a386Sopenharmony_ci    // Ensure that the index buffer limits are lower than the proxy and quad count limits of
1270cb93a386Sopenharmony_ci    // the op's metadata so we don't need to worry about overflow.
1271cb93a386Sopenharmony_ci    SkDEBUGCODE(TextureOpImpl::ValidateResourceLimits();)
1272cb93a386Sopenharmony_ci    SkASSERT(proxy_run_count(set, cnt) == proxyRunCnt);
1273cb93a386Sopenharmony_ci
1274cb93a386Sopenharmony_ci    // First check if we can support batches as a single op
1275cb93a386Sopenharmony_ci    if (blendMode != SkBlendMode::kSrcOver ||
1276cb93a386Sopenharmony_ci        !context->priv().caps()->dynamicStateArrayGeometryProcessorTextureSupport()) {
1277cb93a386Sopenharmony_ci        // Append each entry as its own op; these may still be GrTextureOps if the blend mode is
1278cb93a386Sopenharmony_ci        // src-over but the backend doesn't support dynamic state changes. Otherwise Make()
1279cb93a386Sopenharmony_ci        // automatically creates the appropriate FillRectOp to emulate TextureOp.
1280cb93a386Sopenharmony_ci        SkMatrix ctm;
1281cb93a386Sopenharmony_ci        for (int i = 0; i < cnt; ++i) {
1282cb93a386Sopenharmony_ci            ctm = viewMatrix;
1283cb93a386Sopenharmony_ci            if (set[i].fPreViewMatrix) {
1284cb93a386Sopenharmony_ci                ctm.preConcat(*set[i].fPreViewMatrix);
1285cb93a386Sopenharmony_ci            }
1286cb93a386Sopenharmony_ci
1287cb93a386Sopenharmony_ci            DrawQuad quad;
1288cb93a386Sopenharmony_ci            quad.fEdgeFlags = set[i].fAAFlags;
1289cb93a386Sopenharmony_ci            if (set[i].fDstClipQuad) {
1290cb93a386Sopenharmony_ci                quad.fDevice = GrQuad::MakeFromSkQuad(set[i].fDstClipQuad, ctm);
1291cb93a386Sopenharmony_ci
1292cb93a386Sopenharmony_ci                SkPoint srcPts[4];
1293cb93a386Sopenharmony_ci                GrMapRectPoints(set[i].fDstRect, set[i].fSrcRect, set[i].fDstClipQuad, srcPts, 4);
1294cb93a386Sopenharmony_ci                quad.fLocal = GrQuad::MakeFromSkQuad(srcPts, SkMatrix::I());
1295cb93a386Sopenharmony_ci            } else {
1296cb93a386Sopenharmony_ci                quad.fDevice = GrQuad::MakeFromRect(set[i].fDstRect, ctm);
1297cb93a386Sopenharmony_ci                quad.fLocal = GrQuad(set[i].fSrcRect);
1298cb93a386Sopenharmony_ci            }
1299cb93a386Sopenharmony_ci
1300cb93a386Sopenharmony_ci            const SkRect* subset = constraint == SkCanvas::kStrict_SrcRectConstraint
1301cb93a386Sopenharmony_ci                    ? &set[i].fSrcRect : nullptr;
1302cb93a386Sopenharmony_ci
1303cb93a386Sopenharmony_ci            auto op = Make(context, set[i].fProxyView, set[i].fSrcAlphaType, textureColorSpaceXform,
1304cb93a386Sopenharmony_ci                           filter, mm, set[i].fColor, saturate, blendMode, aaType, &quad, subset);
1305cb93a386Sopenharmony_ci            sdc->addDrawOp(clip, std::move(op));
1306cb93a386Sopenharmony_ci        }
1307cb93a386Sopenharmony_ci        return;
1308cb93a386Sopenharmony_ci    }
1309cb93a386Sopenharmony_ci
1310cb93a386Sopenharmony_ci    // Second check if we can always just make a single op and avoid the extra iteration
1311cb93a386Sopenharmony_ci    // needed to clump things together.
1312cb93a386Sopenharmony_ci    if (cnt <= std::min(GrResourceProvider::MaxNumNonAAQuads(),
1313cb93a386Sopenharmony_ci                      GrResourceProvider::MaxNumAAQuads())) {
1314cb93a386Sopenharmony_ci        auto op = TextureOpImpl::Make(context, set, cnt, proxyRunCnt, filter, mm, saturate, aaType,
1315cb93a386Sopenharmony_ci                                      constraint, viewMatrix, std::move(textureColorSpaceXform));
1316cb93a386Sopenharmony_ci        sdc->addDrawOp(clip, std::move(op));
1317cb93a386Sopenharmony_ci        return;
1318cb93a386Sopenharmony_ci    }
1319cb93a386Sopenharmony_ci
1320cb93a386Sopenharmony_ci    BatchSizeLimiter state(sdc, clip, context, cnt, filter, mm, saturate, constraint, viewMatrix,
1321cb93a386Sopenharmony_ci                           std::move(textureColorSpaceXform));
1322cb93a386Sopenharmony_ci
1323cb93a386Sopenharmony_ci    // kNone and kMSAA never get altered
1324cb93a386Sopenharmony_ci    if (aaType == GrAAType::kNone || aaType == GrAAType::kMSAA) {
1325cb93a386Sopenharmony_ci        // Clump these into series of MaxNumNonAAQuads-sized GrTextureOps
1326cb93a386Sopenharmony_ci        while (state.numLeft() > 0) {
1327cb93a386Sopenharmony_ci            int clumpSize = std::min(state.numLeft(), GrResourceProvider::MaxNumNonAAQuads());
1328cb93a386Sopenharmony_ci
1329cb93a386Sopenharmony_ci            state.createOp(set, clumpSize, aaType);
1330cb93a386Sopenharmony_ci        }
1331cb93a386Sopenharmony_ci    } else {
1332cb93a386Sopenharmony_ci        // kCoverage can be downgraded to kNone. Note that the following is conservative. kCoverage
1333cb93a386Sopenharmony_ci        // can also get downgraded to kNone if all the quads are on integer coordinates and
1334cb93a386Sopenharmony_ci        // axis-aligned.
1335cb93a386Sopenharmony_ci        SkASSERT(aaType == GrAAType::kCoverage);
1336cb93a386Sopenharmony_ci
1337cb93a386Sopenharmony_ci        while (state.numLeft() > 0) {
1338cb93a386Sopenharmony_ci            GrAAType runningAA = GrAAType::kNone;
1339cb93a386Sopenharmony_ci            bool clumped = false;
1340cb93a386Sopenharmony_ci
1341cb93a386Sopenharmony_ci            for (int i = 0; i < state.numLeft(); ++i) {
1342cb93a386Sopenharmony_ci                int absIndex = state.baseIndex() + i;
1343cb93a386Sopenharmony_ci
1344cb93a386Sopenharmony_ci                if (set[absIndex].fAAFlags != GrQuadAAFlags::kNone ||
1345cb93a386Sopenharmony_ci                    runningAA == GrAAType::kCoverage) {
1346cb93a386Sopenharmony_ci
1347cb93a386Sopenharmony_ci                    if (i >= GrResourceProvider::MaxNumAAQuads()) {
1348cb93a386Sopenharmony_ci                        // Here we either need to boost the AA type to kCoverage, but doing so with
1349cb93a386Sopenharmony_ci                        // all the accumulated quads would overflow, or we have a set of AA quads
1350cb93a386Sopenharmony_ci                        // that has just gotten too large. In either case, calve off the existing
1351cb93a386Sopenharmony_ci                        // quads as their own TextureOp.
1352cb93a386Sopenharmony_ci                        state.createOp(
1353cb93a386Sopenharmony_ci                            set,
1354cb93a386Sopenharmony_ci                            runningAA == GrAAType::kNone ? i : GrResourceProvider::MaxNumAAQuads(),
1355cb93a386Sopenharmony_ci                            runningAA); // maybe downgrading AA here
1356cb93a386Sopenharmony_ci                        clumped = true;
1357cb93a386Sopenharmony_ci                        break;
1358cb93a386Sopenharmony_ci                    }
1359cb93a386Sopenharmony_ci
1360cb93a386Sopenharmony_ci                    runningAA = GrAAType::kCoverage;
1361cb93a386Sopenharmony_ci                } else if (runningAA == GrAAType::kNone) {
1362cb93a386Sopenharmony_ci
1363cb93a386Sopenharmony_ci                    if (i >= GrResourceProvider::MaxNumNonAAQuads()) {
1364cb93a386Sopenharmony_ci                        // Here we've found a consistent batch of non-AA quads that has gotten too
1365cb93a386Sopenharmony_ci                        // large. Calve it off as its own TextureOp.
1366cb93a386Sopenharmony_ci                        state.createOp(set, GrResourceProvider::MaxNumNonAAQuads(),
1367cb93a386Sopenharmony_ci                                       GrAAType::kNone); // definitely downgrading AA here
1368cb93a386Sopenharmony_ci                        clumped = true;
1369cb93a386Sopenharmony_ci                        break;
1370cb93a386Sopenharmony_ci                    }
1371cb93a386Sopenharmony_ci                }
1372cb93a386Sopenharmony_ci            }
1373cb93a386Sopenharmony_ci
1374cb93a386Sopenharmony_ci            if (!clumped) {
1375cb93a386Sopenharmony_ci                // We ran through the above loop w/o hitting a limit. Spit out this last clump of
1376cb93a386Sopenharmony_ci                // quads and call it a day.
1377cb93a386Sopenharmony_ci                state.createOp(set, state.numLeft(), runningAA); // maybe downgrading AA here
1378cb93a386Sopenharmony_ci            }
1379cb93a386Sopenharmony_ci        }
1380cb93a386Sopenharmony_ci    }
1381cb93a386Sopenharmony_ci}
1382cb93a386Sopenharmony_ci
1383cb93a386Sopenharmony_ci} // namespace skgpu::v1
1384cb93a386Sopenharmony_ci
1385cb93a386Sopenharmony_ci#if GR_TEST_UTILS
1386cb93a386Sopenharmony_ci#include "include/gpu/GrRecordingContext.h"
1387cb93a386Sopenharmony_ci#include "src/gpu/GrProxyProvider.h"
1388cb93a386Sopenharmony_ci#include "src/gpu/GrRecordingContextPriv.h"
1389cb93a386Sopenharmony_ci
1390cb93a386Sopenharmony_ciGR_DRAW_OP_TEST_DEFINE(TextureOpImpl) {
1391cb93a386Sopenharmony_ci    SkISize dims;
1392cb93a386Sopenharmony_ci    dims.fHeight = random->nextULessThan(90) + 10;
1393cb93a386Sopenharmony_ci    dims.fWidth = random->nextULessThan(90) + 10;
1394cb93a386Sopenharmony_ci    auto origin = random->nextBool() ? kTopLeft_GrSurfaceOrigin : kBottomLeft_GrSurfaceOrigin;
1395cb93a386Sopenharmony_ci    GrMipmapped mipMapped = random->nextBool() ? GrMipmapped::kYes : GrMipmapped::kNo;
1396cb93a386Sopenharmony_ci    SkBackingFit fit = SkBackingFit::kExact;
1397cb93a386Sopenharmony_ci    if (mipMapped == GrMipmapped::kNo) {
1398cb93a386Sopenharmony_ci        fit = random->nextBool() ? SkBackingFit::kApprox : SkBackingFit::kExact;
1399cb93a386Sopenharmony_ci    }
1400cb93a386Sopenharmony_ci    const GrBackendFormat format =
1401cb93a386Sopenharmony_ci            context->priv().caps()->getDefaultBackendFormat(GrColorType::kRGBA_8888,
1402cb93a386Sopenharmony_ci                                                            GrRenderable::kNo);
1403cb93a386Sopenharmony_ci    GrProxyProvider* proxyProvider = context->priv().proxyProvider();
1404cb93a386Sopenharmony_ci    sk_sp<GrTextureProxy> proxy = proxyProvider->createProxy(
1405cb93a386Sopenharmony_ci            format, dims, GrRenderable::kNo, 1, mipMapped, fit, SkBudgeted::kNo, GrProtected::kNo,
1406cb93a386Sopenharmony_ci            GrInternalSurfaceFlags::kNone);
1407cb93a386Sopenharmony_ci
1408cb93a386Sopenharmony_ci    SkRect rect = GrTest::TestRect(random);
1409cb93a386Sopenharmony_ci    SkRect srcRect;
1410cb93a386Sopenharmony_ci    srcRect.fLeft = random->nextRangeScalar(0.f, proxy->width() / 2.f);
1411cb93a386Sopenharmony_ci    srcRect.fRight = random->nextRangeScalar(0.f, proxy->width()) + proxy->width() / 2.f;
1412cb93a386Sopenharmony_ci    srcRect.fTop = random->nextRangeScalar(0.f, proxy->height() / 2.f);
1413cb93a386Sopenharmony_ci    srcRect.fBottom = random->nextRangeScalar(0.f, proxy->height()) + proxy->height() / 2.f;
1414cb93a386Sopenharmony_ci    SkMatrix viewMatrix = GrTest::TestMatrixPreservesRightAngles(random);
1415cb93a386Sopenharmony_ci    SkPMColor4f color = SkPMColor4f::FromBytes_RGBA(SkColorToPremulGrColor(random->nextU()));
1416cb93a386Sopenharmony_ci    GrSamplerState::Filter filter = (GrSamplerState::Filter)random->nextULessThan(
1417cb93a386Sopenharmony_ci            static_cast<uint32_t>(GrSamplerState::Filter::kLast) + 1);
1418cb93a386Sopenharmony_ci    GrSamplerState::MipmapMode mm = GrSamplerState::MipmapMode::kNone;
1419cb93a386Sopenharmony_ci    if (mipMapped == GrMipmapped::kYes) {
1420cb93a386Sopenharmony_ci        mm = (GrSamplerState::MipmapMode)random->nextULessThan(
1421cb93a386Sopenharmony_ci                static_cast<uint32_t>(GrSamplerState::MipmapMode::kLast) + 1);
1422cb93a386Sopenharmony_ci    }
1423cb93a386Sopenharmony_ci
1424cb93a386Sopenharmony_ci    auto texXform = GrTest::TestColorXform(random);
1425cb93a386Sopenharmony_ci    GrAAType aaType = GrAAType::kNone;
1426cb93a386Sopenharmony_ci    if (random->nextBool()) {
1427cb93a386Sopenharmony_ci        aaType = (numSamples > 1) ? GrAAType::kMSAA : GrAAType::kCoverage;
1428cb93a386Sopenharmony_ci    }
1429cb93a386Sopenharmony_ci    GrQuadAAFlags aaFlags = GrQuadAAFlags::kNone;
1430cb93a386Sopenharmony_ci    aaFlags |= random->nextBool() ? GrQuadAAFlags::kLeft : GrQuadAAFlags::kNone;
1431cb93a386Sopenharmony_ci    aaFlags |= random->nextBool() ? GrQuadAAFlags::kTop : GrQuadAAFlags::kNone;
1432cb93a386Sopenharmony_ci    aaFlags |= random->nextBool() ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone;
1433cb93a386Sopenharmony_ci    aaFlags |= random->nextBool() ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone;
1434cb93a386Sopenharmony_ci    bool useSubset = random->nextBool();
1435cb93a386Sopenharmony_ci    auto saturate = random->nextBool() ? skgpu::v1::TextureOp::Saturate::kYes
1436cb93a386Sopenharmony_ci                                       : skgpu::v1::TextureOp::Saturate::kNo;
1437cb93a386Sopenharmony_ci    GrSurfaceProxyView proxyView(
1438cb93a386Sopenharmony_ci            std::move(proxy), origin,
1439cb93a386Sopenharmony_ci            context->priv().caps()->getReadSwizzle(format, GrColorType::kRGBA_8888));
1440cb93a386Sopenharmony_ci    auto alphaType = static_cast<SkAlphaType>(
1441cb93a386Sopenharmony_ci            random->nextRangeU(kUnknown_SkAlphaType + 1, kLastEnum_SkAlphaType));
1442cb93a386Sopenharmony_ci
1443cb93a386Sopenharmony_ci    DrawQuad quad = {GrQuad::MakeFromRect(rect, viewMatrix), GrQuad(srcRect), aaFlags};
1444cb93a386Sopenharmony_ci    return skgpu::v1::TextureOp::Make(context, std::move(proxyView), alphaType,
1445cb93a386Sopenharmony_ci                                      std::move(texXform), filter, mm, color, saturate,
1446cb93a386Sopenharmony_ci                                      SkBlendMode::kSrcOver, aaType, &quad,
1447cb93a386Sopenharmony_ci                                      useSubset ? &srcRect : nullptr);
1448cb93a386Sopenharmony_ci}
1449cb93a386Sopenharmony_ci
1450cb93a386Sopenharmony_ci#endif // GR_TEST_UTILS
1451