1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2018 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 "src/gpu/ops/FillRectOp.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include "include/core/SkMatrix.h"
11cb93a386Sopenharmony_ci#include "include/core/SkRect.h"
12cb93a386Sopenharmony_ci#include "src/gpu/GrCaps.h"
13cb93a386Sopenharmony_ci#include "src/gpu/GrGeometryProcessor.h"
14cb93a386Sopenharmony_ci#include "src/gpu/GrOpsTypes.h"
15cb93a386Sopenharmony_ci#include "src/gpu/GrPaint.h"
16cb93a386Sopenharmony_ci#include "src/gpu/GrProgramInfo.h"
17cb93a386Sopenharmony_ci#include "src/gpu/SkGr.h"
18cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrQuad.h"
19cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrQuadBuffer.h"
20cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrQuadUtils.h"
21cb93a386Sopenharmony_ci#include "src/gpu/glsl/GrGLSLColorSpaceXformHelper.h"
22cb93a386Sopenharmony_ci#include "src/gpu/glsl/GrGLSLVarying.h"
23cb93a386Sopenharmony_ci#include "src/gpu/ops/GrMeshDrawOp.h"
24cb93a386Sopenharmony_ci#include "src/gpu/ops/GrSimpleMeshDrawOpHelperWithStencil.h"
25cb93a386Sopenharmony_ci#include "src/gpu/ops/QuadPerEdgeAA.h"
26cb93a386Sopenharmony_ci#include "src/gpu/v1/SurfaceDrawContext_v1.h"
27cb93a386Sopenharmony_ci
28cb93a386Sopenharmony_cinamespace {
29cb93a386Sopenharmony_ci
30cb93a386Sopenharmony_ciusing VertexSpec = skgpu::v1::QuadPerEdgeAA::VertexSpec;
31cb93a386Sopenharmony_ciusing ColorType = skgpu::v1::QuadPerEdgeAA::ColorType;
32cb93a386Sopenharmony_ciusing Subset = skgpu::v1::QuadPerEdgeAA::Subset;
33cb93a386Sopenharmony_ci
34cb93a386Sopenharmony_ci#if GR_TEST_UTILS
35cb93a386Sopenharmony_ciSkString dump_quad_info(int index, const GrQuad* deviceQuad,
36cb93a386Sopenharmony_ci                        const GrQuad* localQuad, const SkPMColor4f& color,
37cb93a386Sopenharmony_ci                        GrQuadAAFlags aaFlags) {
38cb93a386Sopenharmony_ci    GrQuad safeLocal = localQuad ? *localQuad : GrQuad();
39cb93a386Sopenharmony_ci    SkString str;
40cb93a386Sopenharmony_ci    str.appendf("%d: Color: [%.2f, %.2f, %.2f, %.2f], Edge AA: l%u_t%u_r%u_b%u, \n"
41cb93a386Sopenharmony_ci                "  device quad: [(%.2f, %2.f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), "
42cb93a386Sopenharmony_ci                "(%.2f, %.2f, %.2f)],\n"
43cb93a386Sopenharmony_ci                "  local quad: [(%.2f, %2.f, %.2f), (%.2f, %.2f, %.2f), (%.2f, %.2f, %.2f), "
44cb93a386Sopenharmony_ci                "(%.2f, %.2f, %.2f)]\n",
45cb93a386Sopenharmony_ci                index, color.fR, color.fG, color.fB, color.fA,
46cb93a386Sopenharmony_ci                (uint32_t) (aaFlags & GrQuadAAFlags::kLeft),
47cb93a386Sopenharmony_ci                (uint32_t) (aaFlags & GrQuadAAFlags::kTop),
48cb93a386Sopenharmony_ci                (uint32_t) (aaFlags & GrQuadAAFlags::kRight),
49cb93a386Sopenharmony_ci                (uint32_t) (aaFlags & GrQuadAAFlags::kBottom),
50cb93a386Sopenharmony_ci                deviceQuad->x(0), deviceQuad->y(0), deviceQuad->w(0),
51cb93a386Sopenharmony_ci                deviceQuad->x(1), deviceQuad->y(1), deviceQuad->w(1),
52cb93a386Sopenharmony_ci                deviceQuad->x(2), deviceQuad->y(2), deviceQuad->w(2),
53cb93a386Sopenharmony_ci                deviceQuad->x(3), deviceQuad->y(3), deviceQuad->w(3),
54cb93a386Sopenharmony_ci                safeLocal.x(0), safeLocal.y(0), safeLocal.w(0),
55cb93a386Sopenharmony_ci                safeLocal.x(1), safeLocal.y(1), safeLocal.w(1),
56cb93a386Sopenharmony_ci                safeLocal.x(2), safeLocal.y(2), safeLocal.w(2),
57cb93a386Sopenharmony_ci                safeLocal.x(3), safeLocal.y(3), safeLocal.w(3));
58cb93a386Sopenharmony_ci    return str;
59cb93a386Sopenharmony_ci}
60cb93a386Sopenharmony_ci#endif
61cb93a386Sopenharmony_ci
62cb93a386Sopenharmony_ciclass FillRectOpImpl final : public GrMeshDrawOp {
63cb93a386Sopenharmony_ciprivate:
64cb93a386Sopenharmony_ci    using Helper = GrSimpleMeshDrawOpHelperWithStencil;
65cb93a386Sopenharmony_ci
66cb93a386Sopenharmony_cipublic:
67cb93a386Sopenharmony_ci    static GrOp::Owner Make(GrRecordingContext* context,
68cb93a386Sopenharmony_ci                            GrPaint&& paint,
69cb93a386Sopenharmony_ci                            GrAAType aaType,
70cb93a386Sopenharmony_ci                            DrawQuad* quad,
71cb93a386Sopenharmony_ci                            const GrUserStencilSettings* stencilSettings,
72cb93a386Sopenharmony_ci                            Helper::InputFlags inputFlags) {
73cb93a386Sopenharmony_ci        // Clean up deviations between aaType and edgeAA
74cb93a386Sopenharmony_ci        GrQuadUtils::ResolveAAType(aaType, quad->fEdgeFlags, quad->fDevice,
75cb93a386Sopenharmony_ci                                   &aaType, &quad->fEdgeFlags);
76cb93a386Sopenharmony_ci        return Helper::FactoryHelper<FillRectOpImpl>(context, std::move(paint), aaType, quad,
77cb93a386Sopenharmony_ci                                                     stencilSettings, inputFlags);
78cb93a386Sopenharmony_ci    }
79cb93a386Sopenharmony_ci
80cb93a386Sopenharmony_ci    // aaType is passed to Helper in the initializer list, so incongruities between aaType and
81cb93a386Sopenharmony_ci    // edgeFlags must be resolved prior to calling this constructor.
82cb93a386Sopenharmony_ci    FillRectOpImpl(GrProcessorSet* processorSet, SkPMColor4f paintColor, GrAAType aaType,
83cb93a386Sopenharmony_ci                   DrawQuad* quad, const GrUserStencilSettings* stencil,
84cb93a386Sopenharmony_ci                   Helper::InputFlags inputFlags)
85cb93a386Sopenharmony_ci            : INHERITED(ClassID())
86cb93a386Sopenharmony_ci            , fHelper(processorSet, aaType, stencil, inputFlags)
87cb93a386Sopenharmony_ci            , fQuads(1, !fHelper.isTrivial()) {
88cb93a386Sopenharmony_ci        // Set bounds before clipping so we don't have to worry about unioning the bounds of
89cb93a386Sopenharmony_ci        // the two potential quads (GrQuad::bounds() is perspective-safe).
90cb93a386Sopenharmony_ci        bool hairline = GrQuadUtils::WillUseHairline(quad->fDevice, aaType, quad->fEdgeFlags);
91cb93a386Sopenharmony_ci        this->setBounds(quad->fDevice.bounds(), HasAABloat(aaType == GrAAType::kCoverage),
92cb93a386Sopenharmony_ci                        hairline ? IsHairline::kYes : IsHairline::kNo);
93cb93a386Sopenharmony_ci        DrawQuad extra;
94cb93a386Sopenharmony_ci        // Always crop to W>0 to remain consistent with GrQuad::bounds()
95cb93a386Sopenharmony_ci        int count = GrQuadUtils::ClipToW0(quad, &extra);
96cb93a386Sopenharmony_ci        if (count == 0) {
97cb93a386Sopenharmony_ci            // We can't discard the op at this point, but disable AA flags so it won't go through
98cb93a386Sopenharmony_ci            // inset/outset processing
99cb93a386Sopenharmony_ci            quad->fEdgeFlags = GrQuadAAFlags::kNone;
100cb93a386Sopenharmony_ci            count = 1;
101cb93a386Sopenharmony_ci        }
102cb93a386Sopenharmony_ci
103cb93a386Sopenharmony_ci        // Conservatively keep track of the local coordinates; it may be that the paint doesn't
104cb93a386Sopenharmony_ci        // need them after analysis is finished. If the paint is known to be solid up front they
105cb93a386Sopenharmony_ci        // can be skipped entirely.
106cb93a386Sopenharmony_ci        fQuads.append(quad->fDevice, {paintColor, quad->fEdgeFlags},
107cb93a386Sopenharmony_ci                      fHelper.isTrivial() ? nullptr : &quad->fLocal);
108cb93a386Sopenharmony_ci        if (count > 1) {
109cb93a386Sopenharmony_ci            fQuads.append(extra.fDevice, { paintColor, extra.fEdgeFlags },
110cb93a386Sopenharmony_ci                          fHelper.isTrivial() ? nullptr : &extra.fLocal);
111cb93a386Sopenharmony_ci        }
112cb93a386Sopenharmony_ci    }
113cb93a386Sopenharmony_ci
114cb93a386Sopenharmony_ci    const char* name() const override { return "FillRectOp"; }
115cb93a386Sopenharmony_ci
116cb93a386Sopenharmony_ci    void visitProxies(const GrVisitProxyFunc& func) const override {
117cb93a386Sopenharmony_ci        if (fProgramInfo) {
118cb93a386Sopenharmony_ci            fProgramInfo->visitFPProxies(func);
119cb93a386Sopenharmony_ci        } else {
120cb93a386Sopenharmony_ci            return fHelper.visitProxies(func);
121cb93a386Sopenharmony_ci        }
122cb93a386Sopenharmony_ci    }
123cb93a386Sopenharmony_ci
124cb93a386Sopenharmony_ci    GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
125cb93a386Sopenharmony_ci                                      GrClampType clampType) override {
126cb93a386Sopenharmony_ci        // Initialize aggregate color analysis with the first quad's color (which always exists)
127cb93a386Sopenharmony_ci        auto iter = fQuads.metadata();
128cb93a386Sopenharmony_ci        SkAssertResult(iter.next());
129cb93a386Sopenharmony_ci        GrProcessorAnalysisColor quadColors(iter->fColor);
130cb93a386Sopenharmony_ci        // Then combine the colors of any additional quads (e.g. from MakeSet)
131cb93a386Sopenharmony_ci        while(iter.next()) {
132cb93a386Sopenharmony_ci            quadColors = GrProcessorAnalysisColor::Combine(quadColors, iter->fColor);
133cb93a386Sopenharmony_ci            if (quadColors.isUnknown()) {
134cb93a386Sopenharmony_ci                // No point in accumulating additional starting colors, combining cannot make it
135cb93a386Sopenharmony_ci                // less unknown.
136cb93a386Sopenharmony_ci                break;
137cb93a386Sopenharmony_ci            }
138cb93a386Sopenharmony_ci        }
139cb93a386Sopenharmony_ci
140cb93a386Sopenharmony_ci        // If the AA type is coverage, it will be a single value per pixel; if it's not coverage AA
141cb93a386Sopenharmony_ci        // then the coverage is always 1.0, so specify kNone for more optimal blending.
142cb93a386Sopenharmony_ci        auto coverage = fHelper.aaType() == GrAAType::kCoverage
143cb93a386Sopenharmony_ci                                                    ? GrProcessorAnalysisCoverage::kSingleChannel
144cb93a386Sopenharmony_ci                                                    : GrProcessorAnalysisCoverage::kNone;
145cb93a386Sopenharmony_ci        auto result = fHelper.finalizeProcessors(caps, clip, clampType, coverage, &quadColors);
146cb93a386Sopenharmony_ci        // If there is a constant color after analysis, that means all of the quads should be set
147cb93a386Sopenharmony_ci        // to the same color (even if they started out with different colors).
148cb93a386Sopenharmony_ci        iter = fQuads.metadata();
149cb93a386Sopenharmony_ci        SkPMColor4f colorOverride;
150cb93a386Sopenharmony_ci        if (quadColors.isConstant(&colorOverride)) {
151cb93a386Sopenharmony_ci            fColorType = skgpu::v1::QuadPerEdgeAA::MinColorType(colorOverride);
152cb93a386Sopenharmony_ci            while(iter.next()) {
153cb93a386Sopenharmony_ci                iter->fColor = colorOverride;
154cb93a386Sopenharmony_ci            }
155cb93a386Sopenharmony_ci        } else {
156cb93a386Sopenharmony_ci            // Otherwise compute the color type needed as the max over all quads.
157cb93a386Sopenharmony_ci            fColorType = ColorType::kNone;
158cb93a386Sopenharmony_ci            while(iter.next()) {
159cb93a386Sopenharmony_ci                fColorType = std::max(fColorType,
160cb93a386Sopenharmony_ci                                      skgpu::v1::QuadPerEdgeAA::MinColorType(iter->fColor));
161cb93a386Sopenharmony_ci            }
162cb93a386Sopenharmony_ci        }
163cb93a386Sopenharmony_ci        // Most SkShaders' FPs multiply their calculated color by the paint color or alpha. We want
164cb93a386Sopenharmony_ci        // to use ColorType::kNone to optimize out that multiply. However, if there are no color
165cb93a386Sopenharmony_ci        // FPs then were really writing a special shader for white rectangles and not saving any
166cb93a386Sopenharmony_ci        // multiples. So in that case use bytes to avoid the extra shader (and possibly work around
167cb93a386Sopenharmony_ci        // an ANGLE issue: crbug.com/942565).
168cb93a386Sopenharmony_ci        if (fColorType == ColorType::kNone && !result.hasColorFragmentProcessor()) {
169cb93a386Sopenharmony_ci            fColorType = ColorType::kByte;
170cb93a386Sopenharmony_ci        }
171cb93a386Sopenharmony_ci
172cb93a386Sopenharmony_ci        return result;
173cb93a386Sopenharmony_ci    }
174cb93a386Sopenharmony_ci
175cb93a386Sopenharmony_ci    FixedFunctionFlags fixedFunctionFlags() const override {
176cb93a386Sopenharmony_ci        // Since the AA type of the whole primitive is kept consistent with the per edge AA flags
177cb93a386Sopenharmony_ci        // the helper's fixed function flags are appropriate.
178cb93a386Sopenharmony_ci        return fHelper.fixedFunctionFlags();
179cb93a386Sopenharmony_ci    }
180cb93a386Sopenharmony_ci
181cb93a386Sopenharmony_ci    DEFINE_OP_CLASS_ID
182cb93a386Sopenharmony_ci
183cb93a386Sopenharmony_ciprivate:
184cb93a386Sopenharmony_ci    friend class skgpu::v1::FillRectOp; // for access to addQuad
185cb93a386Sopenharmony_ci
186cb93a386Sopenharmony_ci#if GR_TEST_UTILS
187cb93a386Sopenharmony_ci    int numQuads() const final { return fQuads.count(); }
188cb93a386Sopenharmony_ci#endif
189cb93a386Sopenharmony_ci
190cb93a386Sopenharmony_ci    VertexSpec vertexSpec() const {
191cb93a386Sopenharmony_ci        auto indexBufferOption = skgpu::v1::QuadPerEdgeAA::CalcIndexBufferOption(fHelper.aaType(),
192cb93a386Sopenharmony_ci                                                                                 fQuads.count());
193cb93a386Sopenharmony_ci
194cb93a386Sopenharmony_ci        return VertexSpec(fQuads.deviceQuadType(), fColorType, fQuads.localQuadType(),
195cb93a386Sopenharmony_ci                          fHelper.usesLocalCoords(), Subset::kNo, fHelper.aaType(),
196cb93a386Sopenharmony_ci                          fHelper.compatibleWithCoverageAsAlpha(), indexBufferOption);
197cb93a386Sopenharmony_ci    }
198cb93a386Sopenharmony_ci
199cb93a386Sopenharmony_ci    GrProgramInfo* programInfo() override {
200cb93a386Sopenharmony_ci        return fProgramInfo;
201cb93a386Sopenharmony_ci    }
202cb93a386Sopenharmony_ci
203cb93a386Sopenharmony_ci    void onCreateProgramInfo(const GrCaps* caps,
204cb93a386Sopenharmony_ci                             SkArenaAlloc* arena,
205cb93a386Sopenharmony_ci                             const GrSurfaceProxyView& writeView,
206cb93a386Sopenharmony_ci                             bool usesMSAASurface,
207cb93a386Sopenharmony_ci                             GrAppliedClip&& appliedClip,
208cb93a386Sopenharmony_ci                             const GrDstProxyView& dstProxyView,
209cb93a386Sopenharmony_ci                             GrXferBarrierFlags renderPassXferBarriers,
210cb93a386Sopenharmony_ci                             GrLoadOp colorLoadOp) override {
211cb93a386Sopenharmony_ci        const VertexSpec vertexSpec = this->vertexSpec();
212cb93a386Sopenharmony_ci
213cb93a386Sopenharmony_ci        GrGeometryProcessor* gp = skgpu::v1::QuadPerEdgeAA::MakeProcessor(arena, vertexSpec);
214cb93a386Sopenharmony_ci        SkASSERT(gp->vertexStride() == vertexSpec.vertexSize());
215cb93a386Sopenharmony_ci
216cb93a386Sopenharmony_ci        fProgramInfo = fHelper.createProgramInfoWithStencil(caps, arena, writeView, usesMSAASurface,
217cb93a386Sopenharmony_ci                                                            std::move(appliedClip),
218cb93a386Sopenharmony_ci                                                            dstProxyView, gp,
219cb93a386Sopenharmony_ci                                                            vertexSpec.primitiveType(),
220cb93a386Sopenharmony_ci                                                            renderPassXferBarriers, colorLoadOp);
221cb93a386Sopenharmony_ci    }
222cb93a386Sopenharmony_ci
223cb93a386Sopenharmony_ci    void onPrePrepareDraws(GrRecordingContext* rContext,
224cb93a386Sopenharmony_ci                           const GrSurfaceProxyView& writeView,
225cb93a386Sopenharmony_ci                           GrAppliedClip* clip,
226cb93a386Sopenharmony_ci                           const GrDstProxyView& dstProxyView,
227cb93a386Sopenharmony_ci                           GrXferBarrierFlags renderPassXferBarriers,
228cb93a386Sopenharmony_ci                           GrLoadOp colorLoadOp) override {
229cb93a386Sopenharmony_ci        TRACE_EVENT0("skia.gpu", TRACE_FUNC);
230cb93a386Sopenharmony_ci
231cb93a386Sopenharmony_ci        SkASSERT(!fPrePreparedVertices);
232cb93a386Sopenharmony_ci
233cb93a386Sopenharmony_ci        INHERITED::onPrePrepareDraws(rContext, writeView, clip, dstProxyView,
234cb93a386Sopenharmony_ci                                     renderPassXferBarriers, colorLoadOp);
235cb93a386Sopenharmony_ci
236cb93a386Sopenharmony_ci        SkArenaAlloc* arena = rContext->priv().recordTimeAllocator();
237cb93a386Sopenharmony_ci
238cb93a386Sopenharmony_ci        const VertexSpec vertexSpec = this->vertexSpec();
239cb93a386Sopenharmony_ci
240cb93a386Sopenharmony_ci        const int totalNumVertices = fQuads.count() * vertexSpec.verticesPerQuad();
241cb93a386Sopenharmony_ci        const size_t totalVertexSizeInBytes = vertexSpec.vertexSize() * totalNumVertices;
242cb93a386Sopenharmony_ci
243cb93a386Sopenharmony_ci        fPrePreparedVertices = arena->makeArrayDefault<char>(totalVertexSizeInBytes);
244cb93a386Sopenharmony_ci
245cb93a386Sopenharmony_ci        this->tessellate(vertexSpec, fPrePreparedVertices);
246cb93a386Sopenharmony_ci    }
247cb93a386Sopenharmony_ci
248cb93a386Sopenharmony_ci    void tessellate(const VertexSpec& vertexSpec, char* dst) const {
249cb93a386Sopenharmony_ci        static constexpr SkRect kEmptyDomain = SkRect::MakeEmpty();
250cb93a386Sopenharmony_ci
251cb93a386Sopenharmony_ci        skgpu::v1::QuadPerEdgeAA::Tessellator tessellator(vertexSpec, dst);
252cb93a386Sopenharmony_ci        auto iter = fQuads.iterator();
253cb93a386Sopenharmony_ci        while (iter.next()) {
254cb93a386Sopenharmony_ci            // All entries should have local coords, or no entries should have local coords,
255cb93a386Sopenharmony_ci            // matching !helper.isTrivial() (which is more conservative than helper.usesLocalCoords)
256cb93a386Sopenharmony_ci            SkASSERT(iter.isLocalValid() != fHelper.isTrivial());
257cb93a386Sopenharmony_ci            auto info = iter.metadata();
258cb93a386Sopenharmony_ci            tessellator.append(iter.deviceQuad(), iter.localQuad(),
259cb93a386Sopenharmony_ci                               info.fColor, kEmptyDomain, info.fAAFlags);
260cb93a386Sopenharmony_ci        }
261cb93a386Sopenharmony_ci    }
262cb93a386Sopenharmony_ci
263cb93a386Sopenharmony_ci    void onPrepareDraws(GrMeshDrawTarget* target) override {
264cb93a386Sopenharmony_ci        TRACE_EVENT0("skia.gpu", TRACE_FUNC);
265cb93a386Sopenharmony_ci
266cb93a386Sopenharmony_ci        const VertexSpec vertexSpec = this->vertexSpec();
267cb93a386Sopenharmony_ci
268cb93a386Sopenharmony_ci        // Make sure that if the op thought it was a solid color, the vertex spec does not use
269cb93a386Sopenharmony_ci        // local coords.
270cb93a386Sopenharmony_ci        SkASSERT(!fHelper.isTrivial() || !fHelper.usesLocalCoords());
271cb93a386Sopenharmony_ci
272cb93a386Sopenharmony_ci        const int totalNumVertices = fQuads.count() * vertexSpec.verticesPerQuad();
273cb93a386Sopenharmony_ci
274cb93a386Sopenharmony_ci        // Fill the allocated vertex data
275cb93a386Sopenharmony_ci        void* vdata = target->makeVertexSpace(vertexSpec.vertexSize(), totalNumVertices,
276cb93a386Sopenharmony_ci                                              &fVertexBuffer, &fBaseVertex);
277cb93a386Sopenharmony_ci        if (!vdata) {
278cb93a386Sopenharmony_ci            SkDebugf("Could not allocate vertices\n");
279cb93a386Sopenharmony_ci            return;
280cb93a386Sopenharmony_ci        }
281cb93a386Sopenharmony_ci
282cb93a386Sopenharmony_ci        if (fPrePreparedVertices) {
283cb93a386Sopenharmony_ci            const size_t totalVertexSizeInBytes = vertexSpec.vertexSize() * totalNumVertices;
284cb93a386Sopenharmony_ci
285cb93a386Sopenharmony_ci            memcpy(vdata, fPrePreparedVertices, totalVertexSizeInBytes);
286cb93a386Sopenharmony_ci        } else {
287cb93a386Sopenharmony_ci            this->tessellate(vertexSpec, (char*) vdata);
288cb93a386Sopenharmony_ci        }
289cb93a386Sopenharmony_ci
290cb93a386Sopenharmony_ci        if (vertexSpec.needsIndexBuffer()) {
291cb93a386Sopenharmony_ci            fIndexBuffer = skgpu::v1::QuadPerEdgeAA::GetIndexBuffer(target,
292cb93a386Sopenharmony_ci                                                                    vertexSpec.indexBufferOption());
293cb93a386Sopenharmony_ci            if (!fIndexBuffer) {
294cb93a386Sopenharmony_ci                SkDebugf("Could not allocate indices\n");
295cb93a386Sopenharmony_ci                return;
296cb93a386Sopenharmony_ci            }
297cb93a386Sopenharmony_ci        }
298cb93a386Sopenharmony_ci    }
299cb93a386Sopenharmony_ci
300cb93a386Sopenharmony_ci    void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
301cb93a386Sopenharmony_ci        if (!fVertexBuffer) {
302cb93a386Sopenharmony_ci            return;
303cb93a386Sopenharmony_ci        }
304cb93a386Sopenharmony_ci
305cb93a386Sopenharmony_ci        const VertexSpec vertexSpec = this->vertexSpec();
306cb93a386Sopenharmony_ci
307cb93a386Sopenharmony_ci        if (vertexSpec.needsIndexBuffer() && !fIndexBuffer) {
308cb93a386Sopenharmony_ci            return;
309cb93a386Sopenharmony_ci        }
310cb93a386Sopenharmony_ci
311cb93a386Sopenharmony_ci        if (!fProgramInfo) {
312cb93a386Sopenharmony_ci            this->createProgramInfo(flushState);
313cb93a386Sopenharmony_ci        }
314cb93a386Sopenharmony_ci
315cb93a386Sopenharmony_ci        const int totalNumVertices = fQuads.count() * vertexSpec.verticesPerQuad();
316cb93a386Sopenharmony_ci
317cb93a386Sopenharmony_ci        flushState->bindPipelineAndScissorClip(*fProgramInfo, chainBounds);
318cb93a386Sopenharmony_ci        flushState->bindBuffers(std::move(fIndexBuffer), nullptr, std::move(fVertexBuffer));
319cb93a386Sopenharmony_ci        flushState->bindTextures(fProgramInfo->geomProc(), nullptr, fProgramInfo->pipeline());
320cb93a386Sopenharmony_ci        skgpu::v1::QuadPerEdgeAA::IssueDraw(flushState->caps(), flushState->opsRenderPass(),
321cb93a386Sopenharmony_ci                                            vertexSpec, 0, fQuads.count(), totalNumVertices,
322cb93a386Sopenharmony_ci                                            fBaseVertex);
323cb93a386Sopenharmony_ci    }
324cb93a386Sopenharmony_ci
325cb93a386Sopenharmony_ci    CombineResult onCombineIfPossible(GrOp* t, SkArenaAlloc*, const GrCaps& caps) override {
326cb93a386Sopenharmony_ci        TRACE_EVENT0("skia.gpu", TRACE_FUNC);
327cb93a386Sopenharmony_ci        auto that = t->cast<FillRectOpImpl>();
328cb93a386Sopenharmony_ci
329cb93a386Sopenharmony_ci        bool upgradeToCoverageAAOnMerge = false;
330cb93a386Sopenharmony_ci        if (fHelper.aaType() != that->fHelper.aaType()) {
331cb93a386Sopenharmony_ci            if (!CanUpgradeAAOnMerge(fHelper.aaType(), that->fHelper.aaType())) {
332cb93a386Sopenharmony_ci                return CombineResult::kCannotCombine;
333cb93a386Sopenharmony_ci            }
334cb93a386Sopenharmony_ci            upgradeToCoverageAAOnMerge = true;
335cb93a386Sopenharmony_ci        }
336cb93a386Sopenharmony_ci
337cb93a386Sopenharmony_ci        if (CombinedQuadCountWillOverflow(fHelper.aaType(), upgradeToCoverageAAOnMerge,
338cb93a386Sopenharmony_ci                                          fQuads.count() + that->fQuads.count())) {
339cb93a386Sopenharmony_ci            return CombineResult::kCannotCombine;
340cb93a386Sopenharmony_ci        }
341cb93a386Sopenharmony_ci
342cb93a386Sopenharmony_ci        // Unlike most users of the draw op helper, this op can merge none-aa and coverage-aa draw
343cb93a386Sopenharmony_ci        // ops together, so pass true as the last argument.
344cb93a386Sopenharmony_ci        if (!fHelper.isCompatible(that->fHelper, caps, this->bounds(), that->bounds(), true)) {
345cb93a386Sopenharmony_ci            return CombineResult::kCannotCombine;
346cb93a386Sopenharmony_ci        }
347cb93a386Sopenharmony_ci
348cb93a386Sopenharmony_ci        // If the paints were compatible, the trivial/solid-color state should be the same
349cb93a386Sopenharmony_ci        SkASSERT(fHelper.isTrivial() == that->fHelper.isTrivial());
350cb93a386Sopenharmony_ci
351cb93a386Sopenharmony_ci        // If the processor sets are compatible, the two ops are always compatible; it just needs to
352cb93a386Sopenharmony_ci        // adjust the state of the op to be the more general quad and aa types of the two ops and
353cb93a386Sopenharmony_ci        // then concatenate the per-quad data.
354cb93a386Sopenharmony_ci        fColorType = std::max(fColorType, that->fColorType);
355cb93a386Sopenharmony_ci
356cb93a386Sopenharmony_ci        // The helper stores the aa type, but isCompatible(with true arg) allows the two ops' aa
357cb93a386Sopenharmony_ci        // types to be none and coverage, in which case this op's aa type must be lifted to coverage
358cb93a386Sopenharmony_ci        // so that quads with no aa edges can be batched with quads that have some/all edges aa'ed.
359cb93a386Sopenharmony_ci        if (upgradeToCoverageAAOnMerge) {
360cb93a386Sopenharmony_ci            fHelper.setAAType(GrAAType::kCoverage);
361cb93a386Sopenharmony_ci        }
362cb93a386Sopenharmony_ci
363cb93a386Sopenharmony_ci        fQuads.concat(that->fQuads);
364cb93a386Sopenharmony_ci        return CombineResult::kMerged;
365cb93a386Sopenharmony_ci    }
366cb93a386Sopenharmony_ci
367cb93a386Sopenharmony_ci#if GR_TEST_UTILS
368cb93a386Sopenharmony_ci    SkString onDumpInfo() const override {
369cb93a386Sopenharmony_ci        SkString str = SkStringPrintf("# draws: %u\n", fQuads.count());
370cb93a386Sopenharmony_ci        str.appendf("Device quad type: %u, local quad type: %u\n",
371cb93a386Sopenharmony_ci                    (uint32_t) fQuads.deviceQuadType(), (uint32_t) fQuads.localQuadType());
372cb93a386Sopenharmony_ci        str += fHelper.dumpInfo();
373cb93a386Sopenharmony_ci        int i = 0;
374cb93a386Sopenharmony_ci        auto iter = fQuads.iterator();
375cb93a386Sopenharmony_ci        while(iter.next()) {
376cb93a386Sopenharmony_ci            const ColorAndAA& info = iter.metadata();
377cb93a386Sopenharmony_ci            str += dump_quad_info(i, iter.deviceQuad(), iter.localQuad(),
378cb93a386Sopenharmony_ci                                  info.fColor, info.fAAFlags);
379cb93a386Sopenharmony_ci            i++;
380cb93a386Sopenharmony_ci        }
381cb93a386Sopenharmony_ci        return str;
382cb93a386Sopenharmony_ci    }
383cb93a386Sopenharmony_ci#endif
384cb93a386Sopenharmony_ci
385cb93a386Sopenharmony_ci    bool canAddQuads(int numQuads, GrAAType aaType) {
386cb93a386Sopenharmony_ci        // The new quad's aa type should be the same as the first quad's or none, except when the
387cb93a386Sopenharmony_ci        // first quad's aa type was already downgraded to none, in which case the stored type must
388cb93a386Sopenharmony_ci        // be lifted to back to the requested type.
389cb93a386Sopenharmony_ci        int quadCount = fQuads.count() + numQuads;
390cb93a386Sopenharmony_ci        if (aaType != fHelper.aaType() && aaType != GrAAType::kNone) {
391cb93a386Sopenharmony_ci            auto indexBufferOption = skgpu::v1::QuadPerEdgeAA::CalcIndexBufferOption(aaType,
392cb93a386Sopenharmony_ci                                                                                     quadCount);
393cb93a386Sopenharmony_ci            if (quadCount > skgpu::v1::QuadPerEdgeAA::QuadLimit(indexBufferOption)) {
394cb93a386Sopenharmony_ci                // Promoting to the new aaType would've caused an overflow of the indexBuffer
395cb93a386Sopenharmony_ci                // limit
396cb93a386Sopenharmony_ci                return false;
397cb93a386Sopenharmony_ci            }
398cb93a386Sopenharmony_ci
399cb93a386Sopenharmony_ci            // Original quad was downgraded to non-aa, lift back up to this quad's required type
400cb93a386Sopenharmony_ci            SkASSERT(fHelper.aaType() == GrAAType::kNone);
401cb93a386Sopenharmony_ci            fHelper.setAAType(aaType);
402cb93a386Sopenharmony_ci        } else {
403cb93a386Sopenharmony_ci            auto indexBufferOption = skgpu::v1::QuadPerEdgeAA::CalcIndexBufferOption(
404cb93a386Sopenharmony_ci                    fHelper.aaType(), quadCount);
405cb93a386Sopenharmony_ci            if (quadCount > skgpu::v1::QuadPerEdgeAA::QuadLimit(indexBufferOption)) {
406cb93a386Sopenharmony_ci                return false; // This op can't grow any more
407cb93a386Sopenharmony_ci            }
408cb93a386Sopenharmony_ci        }
409cb93a386Sopenharmony_ci
410cb93a386Sopenharmony_ci        return true;
411cb93a386Sopenharmony_ci    }
412cb93a386Sopenharmony_ci
413cb93a386Sopenharmony_ci    // Similar to onCombineIfPossible, but adds a quad assuming its op would have been compatible.
414cb93a386Sopenharmony_ci    // But since it's avoiding the op list management, it must update the op's bounds.
415cb93a386Sopenharmony_ci    bool addQuad(DrawQuad* quad, const SkPMColor4f& color, GrAAType aaType) {
416cb93a386Sopenharmony_ci        SkRect newBounds = this->bounds();
417cb93a386Sopenharmony_ci        newBounds.joinPossiblyEmptyRect(quad->fDevice.bounds());
418cb93a386Sopenharmony_ci
419cb93a386Sopenharmony_ci        DrawQuad extra;
420cb93a386Sopenharmony_ci        int count = quad->fEdgeFlags != GrQuadAAFlags::kNone ? GrQuadUtils::ClipToW0(quad, &extra)
421cb93a386Sopenharmony_ci                                                             : 1;
422cb93a386Sopenharmony_ci        if (count == 0 ) {
423cb93a386Sopenharmony_ci            // Just skip the append (trivial success)
424cb93a386Sopenharmony_ci            return true;
425cb93a386Sopenharmony_ci        } else if (!this->canAddQuads(count, aaType)) {
426cb93a386Sopenharmony_ci            // Not enough room in the index buffer for the AA type
427cb93a386Sopenharmony_ci            return false;
428cb93a386Sopenharmony_ci        } else {
429cb93a386Sopenharmony_ci            // Can actually add the 1 or 2 quads representing the draw
430cb93a386Sopenharmony_ci            fQuads.append(quad->fDevice, { color, quad->fEdgeFlags },
431cb93a386Sopenharmony_ci                          fHelper.isTrivial() ? nullptr : &quad->fLocal);
432cb93a386Sopenharmony_ci            if (count > 1) {
433cb93a386Sopenharmony_ci                fQuads.append(extra.fDevice, { color, extra.fEdgeFlags },
434cb93a386Sopenharmony_ci                              fHelper.isTrivial() ? nullptr : &extra.fLocal);
435cb93a386Sopenharmony_ci            }
436cb93a386Sopenharmony_ci            // Update the bounds
437cb93a386Sopenharmony_ci            this->setBounds(newBounds, HasAABloat(fHelper.aaType() == GrAAType::kCoverage),
438cb93a386Sopenharmony_ci                            IsHairline::kNo);
439cb93a386Sopenharmony_ci            return true;
440cb93a386Sopenharmony_ci        }
441cb93a386Sopenharmony_ci    }
442cb93a386Sopenharmony_ci
443cb93a386Sopenharmony_ci    struct ColorAndAA {
444cb93a386Sopenharmony_ci        SkPMColor4f fColor;
445cb93a386Sopenharmony_ci        GrQuadAAFlags fAAFlags;
446cb93a386Sopenharmony_ci    };
447cb93a386Sopenharmony_ci
448cb93a386Sopenharmony_ci    Helper fHelper;
449cb93a386Sopenharmony_ci    GrQuadBuffer<ColorAndAA> fQuads;
450cb93a386Sopenharmony_ci    char* fPrePreparedVertices = nullptr;
451cb93a386Sopenharmony_ci
452cb93a386Sopenharmony_ci    GrProgramInfo* fProgramInfo = nullptr;
453cb93a386Sopenharmony_ci    ColorType      fColorType;
454cb93a386Sopenharmony_ci
455cb93a386Sopenharmony_ci    sk_sp<const GrBuffer> fVertexBuffer;
456cb93a386Sopenharmony_ci    sk_sp<const GrBuffer> fIndexBuffer;
457cb93a386Sopenharmony_ci    int fBaseVertex;
458cb93a386Sopenharmony_ci
459cb93a386Sopenharmony_ci    using INHERITED = GrMeshDrawOp;
460cb93a386Sopenharmony_ci};
461cb93a386Sopenharmony_ci
462cb93a386Sopenharmony_ci} // anonymous namespace
463cb93a386Sopenharmony_ci
464cb93a386Sopenharmony_cinamespace skgpu::v1 {
465cb93a386Sopenharmony_ci
466cb93a386Sopenharmony_ciGrOp::Owner FillRectOp::Make(GrRecordingContext* context,
467cb93a386Sopenharmony_ci                             GrPaint&& paint,
468cb93a386Sopenharmony_ci                             GrAAType aaType,
469cb93a386Sopenharmony_ci                             DrawQuad* quad,
470cb93a386Sopenharmony_ci                             const GrUserStencilSettings* stencil,
471cb93a386Sopenharmony_ci                             InputFlags inputFlags) {
472cb93a386Sopenharmony_ci    return FillRectOpImpl::Make(context, std::move(paint), aaType, std::move(quad), stencil,
473cb93a386Sopenharmony_ci                                inputFlags);
474cb93a386Sopenharmony_ci}
475cb93a386Sopenharmony_ci
476cb93a386Sopenharmony_ciGrOp::Owner FillRectOp::MakeNonAARect(GrRecordingContext* context,
477cb93a386Sopenharmony_ci                                      GrPaint&& paint,
478cb93a386Sopenharmony_ci                                      const SkMatrix& view,
479cb93a386Sopenharmony_ci                                      const SkRect& rect,
480cb93a386Sopenharmony_ci                                      const GrUserStencilSettings* stencil) {
481cb93a386Sopenharmony_ci    DrawQuad quad{GrQuad::MakeFromRect(rect, view), GrQuad(rect), GrQuadAAFlags::kNone};
482cb93a386Sopenharmony_ci    return FillRectOpImpl::Make(context, std::move(paint), GrAAType::kNone, &quad, stencil,
483cb93a386Sopenharmony_ci                                InputFlags::kNone);
484cb93a386Sopenharmony_ci}
485cb93a386Sopenharmony_ci
486cb93a386Sopenharmony_ciGrOp::Owner FillRectOp::MakeOp(GrRecordingContext* context,
487cb93a386Sopenharmony_ci                               GrPaint&& paint,
488cb93a386Sopenharmony_ci                               GrAAType aaType,
489cb93a386Sopenharmony_ci                               const SkMatrix& viewMatrix,
490cb93a386Sopenharmony_ci                               const GrQuadSetEntry quads[],
491cb93a386Sopenharmony_ci                               int cnt,
492cb93a386Sopenharmony_ci                               const GrUserStencilSettings* stencilSettings,
493cb93a386Sopenharmony_ci                               int* numConsumed) {
494cb93a386Sopenharmony_ci    // First make a draw op for the first quad in the set
495cb93a386Sopenharmony_ci    SkASSERT(cnt > 0);
496cb93a386Sopenharmony_ci
497cb93a386Sopenharmony_ci    DrawQuad quad{GrQuad::MakeFromRect(quads[0].fRect, viewMatrix),
498cb93a386Sopenharmony_ci                  GrQuad::MakeFromRect(quads[0].fRect, quads[0].fLocalMatrix),
499cb93a386Sopenharmony_ci                  quads[0].fAAFlags};
500cb93a386Sopenharmony_ci    paint.setColor4f(quads[0].fColor);
501cb93a386Sopenharmony_ci    GrOp::Owner op = FillRectOp::Make(context, std::move(paint), aaType,
502cb93a386Sopenharmony_ci                                      &quad, stencilSettings, InputFlags::kNone);
503cb93a386Sopenharmony_ci    auto fillRects = op->cast<FillRectOpImpl>();
504cb93a386Sopenharmony_ci
505cb93a386Sopenharmony_ci    *numConsumed = 1;
506cb93a386Sopenharmony_ci    // Accumulate remaining quads similar to onCombineIfPossible() without creating an op
507cb93a386Sopenharmony_ci    for (int i = 1; i < cnt; ++i) {
508cb93a386Sopenharmony_ci        quad = {GrQuad::MakeFromRect(quads[i].fRect, viewMatrix),
509cb93a386Sopenharmony_ci                GrQuad::MakeFromRect(quads[i].fRect, quads[i].fLocalMatrix),
510cb93a386Sopenharmony_ci                quads[i].fAAFlags};
511cb93a386Sopenharmony_ci
512cb93a386Sopenharmony_ci        GrAAType resolvedAA;
513cb93a386Sopenharmony_ci        GrQuadUtils::ResolveAAType(aaType, quads[i].fAAFlags, quad.fDevice,
514cb93a386Sopenharmony_ci                                   &resolvedAA, &quad.fEdgeFlags);
515cb93a386Sopenharmony_ci
516cb93a386Sopenharmony_ci        if (!fillRects->addQuad(&quad, quads[i].fColor, resolvedAA)) {
517cb93a386Sopenharmony_ci            break;
518cb93a386Sopenharmony_ci        }
519cb93a386Sopenharmony_ci
520cb93a386Sopenharmony_ci        (*numConsumed)++;
521cb93a386Sopenharmony_ci    }
522cb93a386Sopenharmony_ci
523cb93a386Sopenharmony_ci    return op;
524cb93a386Sopenharmony_ci}
525cb93a386Sopenharmony_ci
526cb93a386Sopenharmony_civoid FillRectOp::AddFillRectOps(skgpu::v1::SurfaceDrawContext* sdc,
527cb93a386Sopenharmony_ci                                const GrClip* clip,
528cb93a386Sopenharmony_ci                                GrRecordingContext* context,
529cb93a386Sopenharmony_ci                                GrPaint&& paint,
530cb93a386Sopenharmony_ci                                GrAAType aaType,
531cb93a386Sopenharmony_ci                                const SkMatrix& viewMatrix,
532cb93a386Sopenharmony_ci                                const GrQuadSetEntry quads[],
533cb93a386Sopenharmony_ci                                int cnt,
534cb93a386Sopenharmony_ci                                const GrUserStencilSettings* stencilSettings) {
535cb93a386Sopenharmony_ci
536cb93a386Sopenharmony_ci    int offset = 0;
537cb93a386Sopenharmony_ci    int numLeft = cnt;
538cb93a386Sopenharmony_ci    while (numLeft) {
539cb93a386Sopenharmony_ci        int numConsumed = 0;
540cb93a386Sopenharmony_ci
541cb93a386Sopenharmony_ci        GrOp::Owner op = MakeOp(context, GrPaint::Clone(paint), aaType, viewMatrix,
542cb93a386Sopenharmony_ci                                &quads[offset], numLeft, stencilSettings,
543cb93a386Sopenharmony_ci                                &numConsumed);
544cb93a386Sopenharmony_ci
545cb93a386Sopenharmony_ci        offset += numConsumed;
546cb93a386Sopenharmony_ci        numLeft -= numConsumed;
547cb93a386Sopenharmony_ci
548cb93a386Sopenharmony_ci        sdc->addDrawOp(clip, std::move(op));
549cb93a386Sopenharmony_ci    }
550cb93a386Sopenharmony_ci
551cb93a386Sopenharmony_ci    SkASSERT(offset == cnt);
552cb93a386Sopenharmony_ci}
553cb93a386Sopenharmony_ci
554cb93a386Sopenharmony_ci} // namespace skgpu::v1
555cb93a386Sopenharmony_ci
556cb93a386Sopenharmony_ci#if GR_TEST_UTILS
557cb93a386Sopenharmony_ci
558cb93a386Sopenharmony_ciuint32_t skgpu::v1::FillRectOp::ClassID() {
559cb93a386Sopenharmony_ci    return FillRectOpImpl::ClassID();
560cb93a386Sopenharmony_ci}
561cb93a386Sopenharmony_ci
562cb93a386Sopenharmony_ci#include "src/gpu/GrDrawOpTest.h"
563cb93a386Sopenharmony_ci#include "src/gpu/SkGr.h"
564cb93a386Sopenharmony_ci
565cb93a386Sopenharmony_ciGR_DRAW_OP_TEST_DEFINE(FillRectOp) {
566cb93a386Sopenharmony_ci    SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random);
567cb93a386Sopenharmony_ci    SkRect rect = GrTest::TestRect(random);
568cb93a386Sopenharmony_ci
569cb93a386Sopenharmony_ci    GrAAType aaType = GrAAType::kNone;
570cb93a386Sopenharmony_ci    if (random->nextBool()) {
571cb93a386Sopenharmony_ci        aaType = (numSamples > 1) ? GrAAType::kMSAA : GrAAType::kCoverage;
572cb93a386Sopenharmony_ci    }
573cb93a386Sopenharmony_ci    const GrUserStencilSettings* stencil = random->nextBool() ? nullptr
574cb93a386Sopenharmony_ci                                                              : GrGetRandomStencil(random, context);
575cb93a386Sopenharmony_ci
576cb93a386Sopenharmony_ci    GrQuadAAFlags aaFlags = GrQuadAAFlags::kNone;
577cb93a386Sopenharmony_ci    aaFlags |= random->nextBool() ? GrQuadAAFlags::kLeft : GrQuadAAFlags::kNone;
578cb93a386Sopenharmony_ci    aaFlags |= random->nextBool() ? GrQuadAAFlags::kTop : GrQuadAAFlags::kNone;
579cb93a386Sopenharmony_ci    aaFlags |= random->nextBool() ? GrQuadAAFlags::kRight : GrQuadAAFlags::kNone;
580cb93a386Sopenharmony_ci    aaFlags |= random->nextBool() ? GrQuadAAFlags::kBottom : GrQuadAAFlags::kNone;
581cb93a386Sopenharmony_ci
582cb93a386Sopenharmony_ci    if (random->nextBool()) {
583cb93a386Sopenharmony_ci        if (random->nextBool()) {
584cb93a386Sopenharmony_ci            // Single local matrix
585cb93a386Sopenharmony_ci            SkMatrix localMatrix = GrTest::TestMatrixInvertible(random);
586cb93a386Sopenharmony_ci            DrawQuad quad = {GrQuad::MakeFromRect(rect, viewMatrix),
587cb93a386Sopenharmony_ci                             GrQuad::MakeFromRect(rect, localMatrix), aaFlags};
588cb93a386Sopenharmony_ci            return skgpu::v1::FillRectOp::Make(context, std::move(paint), aaType, &quad, stencil);
589cb93a386Sopenharmony_ci        } else {
590cb93a386Sopenharmony_ci            // Pass local rect directly
591cb93a386Sopenharmony_ci            SkRect localRect = GrTest::TestRect(random);
592cb93a386Sopenharmony_ci            DrawQuad quad = {GrQuad::MakeFromRect(rect, viewMatrix),
593cb93a386Sopenharmony_ci                             GrQuad(localRect), aaFlags};
594cb93a386Sopenharmony_ci            return skgpu::v1::FillRectOp::Make(context, std::move(paint), aaType, &quad, stencil);
595cb93a386Sopenharmony_ci        }
596cb93a386Sopenharmony_ci    } else {
597cb93a386Sopenharmony_ci        // The simplest constructor
598cb93a386Sopenharmony_ci        DrawQuad quad = {GrQuad::MakeFromRect(rect, viewMatrix), GrQuad(rect), aaFlags};
599cb93a386Sopenharmony_ci        return skgpu::v1::FillRectOp::Make(context, std::move(paint), aaType, &quad, stencil);
600cb93a386Sopenharmony_ci    }
601cb93a386Sopenharmony_ci}
602cb93a386Sopenharmony_ci
603cb93a386Sopenharmony_ci#endif
604