1cb93a386Sopenharmony_ci/*
2cb93a386Sopenharmony_ci * Copyright 2019 Google LLC.
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/TessellationPathRenderer.h"
9cb93a386Sopenharmony_ci
10cb93a386Sopenharmony_ci#include "include/private/SkVx.h"
11cb93a386Sopenharmony_ci#include "src/core/SkPathPriv.h"
12cb93a386Sopenharmony_ci#include "src/gpu/GrClip.h"
13cb93a386Sopenharmony_ci#include "src/gpu/GrMemoryPool.h"
14cb93a386Sopenharmony_ci#include "src/gpu/GrRecordingContextPriv.h"
15cb93a386Sopenharmony_ci#include "src/gpu/GrVx.h"
16cb93a386Sopenharmony_ci#include "src/gpu/effects/GrDisableColorXP.h"
17cb93a386Sopenharmony_ci#include "src/gpu/geometry/GrStyledShape.h"
18cb93a386Sopenharmony_ci#include "src/gpu/ops/PathInnerTriangulateOp.h"
19cb93a386Sopenharmony_ci#include "src/gpu/ops/PathStencilCoverOp.h"
20cb93a386Sopenharmony_ci#include "src/gpu/ops/PathTessellateOp.h"
21cb93a386Sopenharmony_ci#include "src/gpu/ops/StrokeTessellateOp.h"
22cb93a386Sopenharmony_ci#include "src/gpu/tessellate/Tessellation.h"
23cb93a386Sopenharmony_ci#include "src/gpu/tessellate/WangsFormula.h"
24cb93a386Sopenharmony_ci#include "src/gpu/v1/SurfaceDrawContext_v1.h"
25cb93a386Sopenharmony_ci
26cb93a386Sopenharmony_cinamespace {
27cb93a386Sopenharmony_ci
28cb93a386Sopenharmony_ciGrOp::Owner make_non_convex_fill_op(GrRecordingContext* rContext,
29cb93a386Sopenharmony_ci                                    SkArenaAlloc* arena,
30cb93a386Sopenharmony_ci                                    skgpu::v1::FillPathFlags fillPathFlags,
31cb93a386Sopenharmony_ci                                    GrAAType aaType,
32cb93a386Sopenharmony_ci                                    const SkRect& drawBounds,
33cb93a386Sopenharmony_ci                                    const SkMatrix& viewMatrix,
34cb93a386Sopenharmony_ci                                    const SkPath& path,
35cb93a386Sopenharmony_ci                                    GrPaint&& paint) {
36cb93a386Sopenharmony_ci    SkASSERT(!path.isConvex() || path.isInverseFillType());
37cb93a386Sopenharmony_ci    int numVerbs = path.countVerbs();
38cb93a386Sopenharmony_ci    if (numVerbs > 0 && !path.isInverseFillType()) {
39cb93a386Sopenharmony_ci        // Check if the path is large and/or simple enough that we can triangulate the inner fan
40cb93a386Sopenharmony_ci        // on the CPU. This is our fastest approach. It allows us to stencil only the curves,
41cb93a386Sopenharmony_ci        // and then fill the inner fan directly to the final render target, thus drawing the
42cb93a386Sopenharmony_ci        // majority of pixels in a single render pass.
43cb93a386Sopenharmony_ci        float gpuFragmentWork = drawBounds.height() * drawBounds.width();
44cb93a386Sopenharmony_ci        float cpuTessellationWork = numVerbs * SkNextLog2(numVerbs);  // N log N.
45cb93a386Sopenharmony_ci        constexpr static float kCpuWeight = 512;
46cb93a386Sopenharmony_ci        constexpr static float kMinNumPixelsToTriangulate = 256 * 256;
47cb93a386Sopenharmony_ci        if (cpuTessellationWork * kCpuWeight + kMinNumPixelsToTriangulate < gpuFragmentWork) {
48cb93a386Sopenharmony_ci            return GrOp::Make<skgpu::v1::PathInnerTriangulateOp>(rContext,
49cb93a386Sopenharmony_ci                                                                 viewMatrix,
50cb93a386Sopenharmony_ci                                                                 path,
51cb93a386Sopenharmony_ci                                                                 std::move(paint),
52cb93a386Sopenharmony_ci                                                                 aaType,
53cb93a386Sopenharmony_ci                                                                 fillPathFlags,
54cb93a386Sopenharmony_ci                                                                 drawBounds);
55cb93a386Sopenharmony_ci        }
56cb93a386Sopenharmony_ci    }
57cb93a386Sopenharmony_ci    return GrOp::Make<skgpu::v1::PathStencilCoverOp>(rContext,
58cb93a386Sopenharmony_ci                                                     arena,
59cb93a386Sopenharmony_ci                                                     viewMatrix,
60cb93a386Sopenharmony_ci                                                     path,
61cb93a386Sopenharmony_ci                                                     std::move(paint),
62cb93a386Sopenharmony_ci                                                     aaType,
63cb93a386Sopenharmony_ci                                                     fillPathFlags,
64cb93a386Sopenharmony_ci                                                     drawBounds);
65cb93a386Sopenharmony_ci}
66cb93a386Sopenharmony_ci
67cb93a386Sopenharmony_ci} // anonymous namespace
68cb93a386Sopenharmony_ci
69cb93a386Sopenharmony_cinamespace skgpu::v1 {
70cb93a386Sopenharmony_ci
71cb93a386Sopenharmony_cibool TessellationPathRenderer::IsSupported(const GrCaps& caps) {
72cb93a386Sopenharmony_ci    return !caps.avoidStencilBuffers() &&
73cb93a386Sopenharmony_ci           caps.drawInstancedSupport() &&
74cb93a386Sopenharmony_ci           !caps.disableTessellationPathRenderer();
75cb93a386Sopenharmony_ci}
76cb93a386Sopenharmony_ci
77cb93a386Sopenharmony_ciPathRenderer::StencilSupport TessellationPathRenderer::onGetStencilSupport(
78cb93a386Sopenharmony_ci        const GrStyledShape& shape) const {
79cb93a386Sopenharmony_ci    if (!shape.style().isSimpleFill() || shape.inverseFilled()) {
80cb93a386Sopenharmony_ci        // Don't bother with stroke stencilling or inverse fills yet. The Skia API doesn't support
81cb93a386Sopenharmony_ci        // clipping by a stroke, and the stencilling code already knows how to invert a fill.
82cb93a386Sopenharmony_ci        return kNoSupport_StencilSupport;
83cb93a386Sopenharmony_ci    }
84cb93a386Sopenharmony_ci    return shape.knownToBeConvex() ? kNoRestriction_StencilSupport : kStencilOnly_StencilSupport;
85cb93a386Sopenharmony_ci}
86cb93a386Sopenharmony_ci
87cb93a386Sopenharmony_ciPathRenderer::CanDrawPath TessellationPathRenderer::onCanDrawPath(
88cb93a386Sopenharmony_ci        const CanDrawPathArgs& args) const {
89cb93a386Sopenharmony_ci    const GrStyledShape& shape = *args.fShape;
90cb93a386Sopenharmony_ci    if (args.fAAType == GrAAType::kCoverage ||
91cb93a386Sopenharmony_ci        shape.style().hasPathEffect() ||
92cb93a386Sopenharmony_ci        args.fViewMatrix->hasPerspective() ||
93cb93a386Sopenharmony_ci        shape.style().strokeRec().getStyle() == SkStrokeRec::kStrokeAndFill_Style ||
94cb93a386Sopenharmony_ci        !args.fProxy->canUseStencil(*args.fCaps)) {
95cb93a386Sopenharmony_ci        return CanDrawPath::kNo;
96cb93a386Sopenharmony_ci    }
97cb93a386Sopenharmony_ci    if (!shape.style().isSimpleFill()) {
98cb93a386Sopenharmony_ci        if (shape.inverseFilled()) {
99cb93a386Sopenharmony_ci            return CanDrawPath::kNo;
100cb93a386Sopenharmony_ci        }
101cb93a386Sopenharmony_ci    }
102cb93a386Sopenharmony_ci    if (args.fHasUserStencilSettings) {
103cb93a386Sopenharmony_ci        // Non-convex paths and strokes use the stencil buffer internally, so they can't support
104cb93a386Sopenharmony_ci        // draws with stencil settings.
105cb93a386Sopenharmony_ci        if (!shape.style().isSimpleFill() || !shape.knownToBeConvex() || shape.inverseFilled()) {
106cb93a386Sopenharmony_ci            return CanDrawPath::kNo;
107cb93a386Sopenharmony_ci        }
108cb93a386Sopenharmony_ci    }
109cb93a386Sopenharmony_ci    return CanDrawPath::kYes;
110cb93a386Sopenharmony_ci}
111cb93a386Sopenharmony_ci
112cb93a386Sopenharmony_cibool TessellationPathRenderer::onDrawPath(const DrawPathArgs& args) {
113cb93a386Sopenharmony_ci    auto sdc = args.fSurfaceDrawContext;
114cb93a386Sopenharmony_ci
115cb93a386Sopenharmony_ci    SkPath path;
116cb93a386Sopenharmony_ci    args.fShape->asPath(&path);
117cb93a386Sopenharmony_ci
118cb93a386Sopenharmony_ci    const SkRect pathDevBounds = args.fViewMatrix->mapRect(args.fShape->bounds());
119cb93a386Sopenharmony_ci    float n = wangs_formula::worst_case_cubic_pow4(kTessellationPrecision,
120cb93a386Sopenharmony_ci                                                   pathDevBounds.width(),
121cb93a386Sopenharmony_ci                                                   pathDevBounds.height());
122cb93a386Sopenharmony_ci    if (n > pow4(kMaxTessellationSegmentsPerCurve)) {
123cb93a386Sopenharmony_ci        // The path is extremely large. Pre-chop its curves to keep the number of tessellation
124cb93a386Sopenharmony_ci        // segments tractable. This will also flatten curves that fall completely outside the
125cb93a386Sopenharmony_ci        // viewport.
126cb93a386Sopenharmony_ci        SkRect viewport = SkRect::Make(*args.fClipConservativeBounds);
127cb93a386Sopenharmony_ci        if (!args.fShape->style().isSimpleFill()) {
128cb93a386Sopenharmony_ci            // Outset the viewport to pad for the stroke width.
129cb93a386Sopenharmony_ci            const SkStrokeRec& stroke = args.fShape->style().strokeRec();
130cb93a386Sopenharmony_ci            float inflationRadius;
131cb93a386Sopenharmony_ci            if (stroke.isHairlineStyle()) {
132cb93a386Sopenharmony_ci                // SkStrokeRec::getInflationRadius() doesn't handle hairlines robustly. Instead
133cb93a386Sopenharmony_ci                // find the inflation of an equivalent stroke in device space with a width of 1.
134cb93a386Sopenharmony_ci                inflationRadius = SkStrokeRec::GetInflationRadius(stroke.getJoin(),
135cb93a386Sopenharmony_ci                                                                  stroke.getMiter(),
136cb93a386Sopenharmony_ci                                                                  stroke.getCap(), 1);
137cb93a386Sopenharmony_ci            } else {
138cb93a386Sopenharmony_ci                inflationRadius = stroke.getInflationRadius() * args.fViewMatrix->getMaxScale();
139cb93a386Sopenharmony_ci            }
140cb93a386Sopenharmony_ci            viewport.outset(inflationRadius, inflationRadius);
141cb93a386Sopenharmony_ci        }
142cb93a386Sopenharmony_ci        path = PreChopPathCurves(path, *args.fViewMatrix, viewport);
143cb93a386Sopenharmony_ci    }
144cb93a386Sopenharmony_ci
145cb93a386Sopenharmony_ci    // Handle strokes first.
146cb93a386Sopenharmony_ci    if (!args.fShape->style().isSimpleFill()) {
147cb93a386Sopenharmony_ci        SkASSERT(!path.isInverseFillType());  // See onGetStencilSupport().
148cb93a386Sopenharmony_ci        SkASSERT(args.fUserStencilSettings->isUnused());
149cb93a386Sopenharmony_ci        const SkStrokeRec& stroke = args.fShape->style().strokeRec();
150cb93a386Sopenharmony_ci        SkASSERT(stroke.getStyle() != SkStrokeRec::kStrokeAndFill_Style);
151cb93a386Sopenharmony_ci        auto op = GrOp::Make<StrokeTessellateOp>(args.fContext, args.fAAType, *args.fViewMatrix,
152cb93a386Sopenharmony_ci                                                 path, stroke, std::move(args.fPaint));
153cb93a386Sopenharmony_ci        sdc->addDrawOp(args.fClip, std::move(op));
154cb93a386Sopenharmony_ci        return true;
155cb93a386Sopenharmony_ci    }
156cb93a386Sopenharmony_ci
157cb93a386Sopenharmony_ci    // Handle empty paths.
158cb93a386Sopenharmony_ci    if (pathDevBounds.isEmpty()) {
159cb93a386Sopenharmony_ci        if (path.isInverseFillType()) {
160cb93a386Sopenharmony_ci            args.fSurfaceDrawContext->drawPaint(args.fClip, std::move(args.fPaint),
161cb93a386Sopenharmony_ci                                                *args.fViewMatrix);
162cb93a386Sopenharmony_ci        }
163cb93a386Sopenharmony_ci        return true;
164cb93a386Sopenharmony_ci    }
165cb93a386Sopenharmony_ci
166cb93a386Sopenharmony_ci    // Handle convex paths.
167cb93a386Sopenharmony_ci    if (args.fShape->knownToBeConvex() && !path.isInverseFillType()) {
168cb93a386Sopenharmony_ci        auto op = GrOp::Make<PathTessellateOp>(args.fContext,
169cb93a386Sopenharmony_ci                                               args.fSurfaceDrawContext->arenaAlloc(),
170cb93a386Sopenharmony_ci                                               args.fAAType,
171cb93a386Sopenharmony_ci                                               args.fUserStencilSettings,
172cb93a386Sopenharmony_ci                                               *args.fViewMatrix,
173cb93a386Sopenharmony_ci                                               path,
174cb93a386Sopenharmony_ci                                               std::move(args.fPaint),
175cb93a386Sopenharmony_ci                                               pathDevBounds);
176cb93a386Sopenharmony_ci        sdc->addDrawOp(args.fClip, std::move(op));
177cb93a386Sopenharmony_ci        return true;
178cb93a386Sopenharmony_ci    }
179cb93a386Sopenharmony_ci
180cb93a386Sopenharmony_ci    SkASSERT(args.fUserStencilSettings->isUnused());  // See onGetStencilSupport().
181cb93a386Sopenharmony_ci    const SkRect& drawBounds = path.isInverseFillType()
182cb93a386Sopenharmony_ci            ? args.fSurfaceDrawContext->asSurfaceProxy()->backingStoreBoundsRect()
183cb93a386Sopenharmony_ci            : pathDevBounds;
184cb93a386Sopenharmony_ci    auto op = make_non_convex_fill_op(args.fContext,
185cb93a386Sopenharmony_ci                                      args.fSurfaceDrawContext->arenaAlloc(),
186cb93a386Sopenharmony_ci                                      FillPathFlags::kNone,
187cb93a386Sopenharmony_ci                                      args.fAAType,
188cb93a386Sopenharmony_ci                                      drawBounds,
189cb93a386Sopenharmony_ci                                      *args.fViewMatrix,
190cb93a386Sopenharmony_ci                                      path,
191cb93a386Sopenharmony_ci                                      std::move(args.fPaint));
192cb93a386Sopenharmony_ci    sdc->addDrawOp(args.fClip, std::move(op));
193cb93a386Sopenharmony_ci    return true;
194cb93a386Sopenharmony_ci}
195cb93a386Sopenharmony_ci
196cb93a386Sopenharmony_civoid TessellationPathRenderer::onStencilPath(const StencilPathArgs& args) {
197cb93a386Sopenharmony_ci    SkASSERT(args.fShape->style().isSimpleFill());  // See onGetStencilSupport().
198cb93a386Sopenharmony_ci    SkASSERT(!args.fShape->inverseFilled());  // See onGetStencilSupport().
199cb93a386Sopenharmony_ci
200cb93a386Sopenharmony_ci    auto sdc = args.fSurfaceDrawContext;
201cb93a386Sopenharmony_ci    GrAAType aaType = (GrAA::kYes == args.fDoStencilMSAA) ? GrAAType::kMSAA : GrAAType::kNone;
202cb93a386Sopenharmony_ci
203cb93a386Sopenharmony_ci    SkRect pathDevBounds;
204cb93a386Sopenharmony_ci    args.fViewMatrix->mapRect(&pathDevBounds, args.fShape->bounds());
205cb93a386Sopenharmony_ci
206cb93a386Sopenharmony_ci    SkPath path;
207cb93a386Sopenharmony_ci    args.fShape->asPath(&path);
208cb93a386Sopenharmony_ci
209cb93a386Sopenharmony_ci    float n = wangs_formula::worst_case_cubic_pow4(kTessellationPrecision,
210cb93a386Sopenharmony_ci                                                   pathDevBounds.width(),
211cb93a386Sopenharmony_ci                                                   pathDevBounds.height());
212cb93a386Sopenharmony_ci    if (n > pow4(kMaxTessellationSegmentsPerCurve)) {
213cb93a386Sopenharmony_ci        SkRect viewport = SkRect::Make(*args.fClipConservativeBounds);
214cb93a386Sopenharmony_ci        path = PreChopPathCurves(path, *args.fViewMatrix, viewport);
215cb93a386Sopenharmony_ci    }
216cb93a386Sopenharmony_ci
217cb93a386Sopenharmony_ci    if (args.fShape->knownToBeConvex()) {
218cb93a386Sopenharmony_ci        constexpr static GrUserStencilSettings kMarkStencil(
219cb93a386Sopenharmony_ci            GrUserStencilSettings::StaticInit<
220cb93a386Sopenharmony_ci                0x0001,
221cb93a386Sopenharmony_ci                GrUserStencilTest::kAlways,
222cb93a386Sopenharmony_ci                0xffff,
223cb93a386Sopenharmony_ci                GrUserStencilOp::kReplace,
224cb93a386Sopenharmony_ci                GrUserStencilOp::kKeep,
225cb93a386Sopenharmony_ci                0xffff>());
226cb93a386Sopenharmony_ci
227cb93a386Sopenharmony_ci        GrPaint stencilPaint;
228cb93a386Sopenharmony_ci        stencilPaint.setXPFactory(GrDisableColorXPFactory::Get());
229cb93a386Sopenharmony_ci        auto op = GrOp::Make<PathTessellateOp>(args.fContext,
230cb93a386Sopenharmony_ci                                               args.fSurfaceDrawContext->arenaAlloc(),
231cb93a386Sopenharmony_ci                                               aaType,
232cb93a386Sopenharmony_ci                                               &kMarkStencil,
233cb93a386Sopenharmony_ci                                               *args.fViewMatrix,
234cb93a386Sopenharmony_ci                                               path,
235cb93a386Sopenharmony_ci                                               std::move(stencilPaint),
236cb93a386Sopenharmony_ci                                               pathDevBounds);
237cb93a386Sopenharmony_ci        sdc->addDrawOp(args.fClip, std::move(op));
238cb93a386Sopenharmony_ci        return;
239cb93a386Sopenharmony_ci    }
240cb93a386Sopenharmony_ci
241cb93a386Sopenharmony_ci    auto op = make_non_convex_fill_op(args.fContext,
242cb93a386Sopenharmony_ci                                      args.fSurfaceDrawContext->arenaAlloc(),
243cb93a386Sopenharmony_ci                                      FillPathFlags::kStencilOnly,
244cb93a386Sopenharmony_ci                                      aaType,
245cb93a386Sopenharmony_ci                                      pathDevBounds,
246cb93a386Sopenharmony_ci                                      *args.fViewMatrix,
247cb93a386Sopenharmony_ci                                      path,
248cb93a386Sopenharmony_ci                                      GrPaint());
249cb93a386Sopenharmony_ci    sdc->addDrawOp(args.fClip, std::move(op));
250cb93a386Sopenharmony_ci}
251cb93a386Sopenharmony_ci
252cb93a386Sopenharmony_ci} // namespace skgpu::v1
253