1 /*
2  * Copyright 2019 Google LLC.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "src/gpu/tessellate/GrTessellationPathRenderer.h"
9 
10 #include "src/core/SkPathPriv.h"
11 #include "src/gpu/GrClip.h"
12 #include "src/gpu/GrMemoryPool.h"
13 #include "src/gpu/GrRecordingContextPriv.h"
14 #include "src/gpu/GrRenderTargetContext.h"
15 #include "src/gpu/GrSurfaceContextPriv.h"
16 #include "src/gpu/geometry/GrShape.h"
17 #include "src/gpu/ops/GrFillRectOp.h"
18 #include "src/gpu/tessellate/GrDrawAtlasPathOp.h"
19 #include "src/gpu/tessellate/GrTessellatePathOp.h"
20 
21 constexpr static SkISize kAtlasInitialSize{512, 512};
22 constexpr static int kMaxAtlasSize = 2048;
23 
GrTessellationPathRenderer(const GrCaps & caps)24 GrTessellationPathRenderer::GrTessellationPathRenderer(const GrCaps& caps) : fAtlas(
25         GrColorType::kAlpha_8, GrDynamicAtlas::InternalMultisample::kYes, kAtlasInitialSize,
26         std::min(kMaxAtlasSize, caps.maxPreferredRenderTargetSize()), caps) {
27 }
28 
onCanDrawPath(const CanDrawPathArgs & args) const29 GrPathRenderer::CanDrawPath GrTessellationPathRenderer::onCanDrawPath(
30         const CanDrawPathArgs& args) const {
31     // This class should not have been added to the chain without tessellation support.
32     SkASSERT(args.fCaps->shaderCaps()->tessellationSupport());
33     if (!args.fShape->style().isSimpleFill() || args.fShape->inverseFilled() ||
34         args.fViewMatrix->hasPerspective()) {
35         return CanDrawPath::kNo;
36     }
37     if (GrAAType::kCoverage == args.fAAType) {
38         SkASSERT(1 == args.fProxy->numSamples());
39         if (!args.fProxy->canUseMixedSamples(*args.fCaps)) {
40             return CanDrawPath::kNo;
41         }
42     }
43     SkPath path;
44     args.fShape->asPath(&path);
45     if (SkPathPriv::ConicWeightCnt(path)) {
46         return CanDrawPath::kNo;
47     }
48     return CanDrawPath::kYes;
49 }
50 
onDrawPath(const DrawPathArgs & args)51 bool GrTessellationPathRenderer::onDrawPath(const DrawPathArgs& args) {
52     GrRenderTargetContext* renderTargetContext = args.fRenderTargetContext;
53     GrOpMemoryPool* pool = args.fContext->priv().opMemoryPool();
54     SkPath path;
55     args.fShape->asPath(&path);
56 
57     // See if the path is small and simple enough to atlas instead of drawing directly.
58     //
59     // NOTE: The atlas uses alpha8 coverage even for msaa render targets. We could theoretically
60     // render the sample mask to an integer texture, but such a scheme would probably require
61     // GL_EXT_post_depth_coverage, which appears to have low adoption.
62     SkIRect devIBounds;
63     SkIVector devToAtlasOffset;
64     if (this->tryAddPathToAtlas(*args.fContext->priv().caps(), *args.fViewMatrix, path,
65                                 args.fAAType, &devIBounds, &devToAtlasOffset)) {
66         auto op = pool->allocate<GrDrawAtlasPathOp>(
67                 renderTargetContext->numSamples(), sk_ref_sp(fAtlas.textureProxy()),
68                 devIBounds, devToAtlasOffset, *args.fViewMatrix, std::move(args.fPaint));
69         renderTargetContext->addDrawOp(*args.fClip, std::move(op));
70         return true;
71     }
72 
73     auto op = pool->allocate<GrTessellatePathOp>(
74             *args.fViewMatrix, path, std::move(args.fPaint), args.fAAType);
75     renderTargetContext->addDrawOp(*args.fClip, std::move(op));
76     return true;
77 }
78 
tryAddPathToAtlas(const GrCaps & caps,const SkMatrix & viewMatrix,const SkPath & path,GrAAType aaType,SkIRect * devIBounds,SkIVector * devToAtlasOffset)79 bool GrTessellationPathRenderer::tryAddPathToAtlas(
80         const GrCaps& caps, const SkMatrix& viewMatrix, const SkPath& path, GrAAType aaType,
81         SkIRect* devIBounds, SkIVector* devToAtlasOffset) {
82     if (!caps.multisampleDisableSupport() && GrAAType::kNone == aaType) {
83         return false;
84     }
85 
86     // Atlas paths require their points to be transformed on CPU. Check if the path has too many
87     // points to justify this CPU transformation.
88     if (path.countPoints() > 150) {
89         return false;
90     }
91 
92     // Check if the path is too large for an atlas.
93     SkRect devBounds;
94     viewMatrix.mapRect(&devBounds, path.getBounds());
95     if (devBounds.height() * devBounds.width() > 100 * 100 ||
96         std::max(devBounds.height(), devBounds.width()) > kMaxAtlasSize / 2) {
97         return false;
98     }
99 
100     devBounds.roundOut(devIBounds);
101     if (!fAtlas.addRect(*devIBounds, devToAtlasOffset)) {
102         return false;
103     }
104 
105     SkMatrix atlasMatrix = viewMatrix;
106     atlasMatrix.postTranslate(devToAtlasOffset->x(), devToAtlasOffset->y());
107 
108     // Concatenate this path onto our uber path that matches its fill and AA types.
109     SkPath* uberPath = this->getAtlasUberPath(path.getFillType(), GrAAType::kNone != aaType);
110     uberPath->moveTo(devToAtlasOffset->x(), devToAtlasOffset->y());  // Implicit moveTo(0,0).
111     uberPath->addPath(path, atlasMatrix);
112     return true;
113 }
114 
onStencilPath(const StencilPathArgs & args)115 void GrTessellationPathRenderer::onStencilPath(const StencilPathArgs& args) {
116     SkPath path;
117     args.fShape->asPath(&path);
118 
119     GrAAType aaType = (GrAA::kYes == args.fDoStencilMSAA) ? GrAAType::kMSAA : GrAAType::kNone;
120 
121     auto op = args.fContext->priv().opMemoryPool()->allocate<GrTessellatePathOp>(
122             *args.fViewMatrix, path, GrPaint(), aaType, GrTessellatePathOp::Flags::kStencilOnly);
123     args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op));
124 }
125 
preFlush(GrOnFlushResourceProvider * onFlushRP,const uint32_t * opsTaskIDs,int numOpsTaskIDs)126 void GrTessellationPathRenderer::preFlush(GrOnFlushResourceProvider* onFlushRP,
127                                           const uint32_t* opsTaskIDs, int numOpsTaskIDs) {
128     if (!fAtlas.drawBounds().isEmpty()) {
129         this->renderAtlas(onFlushRP);
130         fAtlas.reset(kAtlasInitialSize, *onFlushRP->caps());
131     }
132     for (SkPath& path : fAtlasUberPaths) {
133         path.reset();
134     }
135 }
136 
137 constexpr static GrUserStencilSettings kTestStencil(
138     GrUserStencilSettings::StaticInit<
139         0x0000,
140         GrUserStencilTest::kNotEqual,
141         0xffff,
142         GrUserStencilOp::kKeep,
143         GrUserStencilOp::kKeep,
144         0xffff>());
145 
146 constexpr static GrUserStencilSettings kTestAndResetStencil(
147     GrUserStencilSettings::StaticInit<
148         0x0000,
149         GrUserStencilTest::kNotEqual,
150         0xffff,
151         GrUserStencilOp::kZero,
152         GrUserStencilOp::kKeep,
153         0xffff>());
154 
renderAtlas(GrOnFlushResourceProvider * onFlushRP)155 void GrTessellationPathRenderer::renderAtlas(GrOnFlushResourceProvider* onFlushRP) {
156     auto rtc = fAtlas.instantiate(onFlushRP);
157     if (!rtc) {
158         return;
159     }
160 
161     // Add ops to stencil the atlas paths.
162     for (auto antialias : {false, true}) {
163         for (auto fillType : {SkPathFillType::kWinding, SkPathFillType::kEvenOdd}) {
164             SkPath* uberPath = this->getAtlasUberPath(fillType, antialias);
165             if (uberPath->isEmpty()) {
166                 continue;
167             }
168             uberPath->setFillType(fillType);
169             GrAAType aaType = (antialias) ? GrAAType::kMSAA : GrAAType::kNone;
170             auto op = onFlushRP->opMemoryPool()->allocate<GrTessellatePathOp>(
171                     SkMatrix::I(), *uberPath, GrPaint(), aaType,
172                     GrTessellatePathOp::Flags::kStencilOnly);
173             rtc->addDrawOp(GrNoClip(), std::move(op));
174         }
175     }
176 
177     // Finally, draw a fullscreen rect to convert our stencilled paths into alpha coverage masks.
178     auto fillRectFlags = GrFillRectOp::InputFlags::kNone;
179 
180     // This will be the final op in the renderTargetContext. So if Ganesh is planning to discard the
181     // stencil values anyway, then we might not actually need to reset the stencil values back to 0.
182     bool mustResetStencil = !onFlushRP->caps()->discardStencilValuesAfterRenderPass();
183 
184     if (rtc->numSamples() <= 1) {
185         // We are mixed sampled. We need to enable conservative raster and ensure stencil values get
186         // reset in order to avoid artifacts along the diagonal of the atlas.
187         fillRectFlags |= GrFillRectOp::InputFlags::kConservativeRaster;
188         mustResetStencil = true;
189     }
190 
191     SkRect coverRect = SkRect::MakeIWH(fAtlas.drawBounds().width(), fAtlas.drawBounds().height());
192     const GrUserStencilSettings* stencil;
193     if (mustResetStencil) {
194         // Outset the cover rect in case there are T-junctions in the path bounds.
195         coverRect.outset(1, 1);
196         stencil = &kTestAndResetStencil;
197     } else {
198         stencil = &kTestStencil;
199     }
200 
201     GrQuad coverQuad(coverRect);
202     DrawQuad drawQuad{coverQuad, coverQuad, GrQuadAAFlags::kAll};
203 
204     GrPaint paint;
205     paint.setColor4f(SK_PMColor4fWHITE);
206 
207     auto coverOp = GrFillRectOp::Make(rtc->surfPriv().getContext(), std::move(paint),
208                                       GrAAType::kMSAA, &drawQuad, stencil, fillRectFlags);
209     rtc->addDrawOp(GrNoClip(), std::move(coverOp));
210 
211     if (rtc->asSurfaceProxy()->requiresManualMSAAResolve()) {
212         onFlushRP->addTextureResolveTask(sk_ref_sp(rtc->asTextureProxy()),
213                                          GrSurfaceProxy::ResolveFlags::kMSAA);
214     }
215 }
216