• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "include/core/SkCanvas.h"
9 #include "src/core/SkPathPriv.h"
10 #include "tools/viewer/ClickHandlerSlide.h"
11 
12 #if defined(SK_GANESH)
13 
14 #include "src/core/SkCanvasPriv.h"
15 #include "src/gpu/ganesh/GrCaps.h"
16 #include "src/gpu/ganesh/GrOpFlushState.h"
17 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
18 #include "src/gpu/ganesh/SurfaceDrawContext.h"
19 #include "src/gpu/ganesh/ops/GrDrawOp.h"
20 #include "src/gpu/ganesh/ops/GrSimpleMeshDrawOpHelper.h"
21 #include "src/gpu/ganesh/ops/TessellationPathRenderer.h"
22 #include "src/gpu/ganesh/tessellate/GrPathTessellationShader.h"
23 #include "src/gpu/ganesh/tessellate/PathTessellator.h"
24 #include "src/gpu/tessellate/AffineMatrix.h"
25 #include "src/gpu/tessellate/MiddleOutPolygonTriangulator.h"
26 
27 namespace skgpu::v1 {
28 
29 namespace {
30 
31 enum class Mode {
32     kWedgeMiddleOut,
33     kCurveMiddleOut
34 };
35 
ModeName(Mode mode)36 static const char* ModeName(Mode mode) {
37     switch (mode) {
38         case Mode::kWedgeMiddleOut:
39             return "MiddleOutShader (kWedges)";
40         case Mode::kCurveMiddleOut:
41             return "MiddleOutShader (kCurves)";
42     }
43     SkUNREACHABLE;
44 }
45 
46 // Draws a path directly to the screen using a specific tessellator.
47 class SamplePathTessellatorOp : public GrDrawOp {
48 private:
49     DEFINE_OP_CLASS_ID
50 
SamplePathTessellatorOp(const SkRect & drawBounds,const SkPath & path,const SkMatrix & m,GrPipeline::InputFlags pipelineFlags,Mode mode)51     SamplePathTessellatorOp(const SkRect& drawBounds, const SkPath& path, const SkMatrix& m,
52                             GrPipeline::InputFlags pipelineFlags, Mode mode)
53             : GrDrawOp(ClassID())
54             , fPath(path)
55             , fMatrix(m)
56             , fPipelineFlags(pipelineFlags)
57             , fMode(mode) {
58         this->setBounds(drawBounds, HasAABloat::kNo, IsHairline::kNo);
59     }
name() const60     const char* name() const override { return "SamplePathTessellatorOp"; }
visitProxies(const GrVisitProxyFunc &) const61     void visitProxies(const GrVisitProxyFunc&) const override {}
fixedFunctionFlags() const62     FixedFunctionFlags fixedFunctionFlags() const override {
63         return FixedFunctionFlags::kUsesHWAA;
64     }
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrClampType clampType)65     GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
66                                       GrClampType clampType) override {
67         SkPMColor4f color;
68         return fProcessors.finalize(SK_PMColor4fWHITE, GrProcessorAnalysisCoverage::kNone, clip,
69                                     nullptr, caps, clampType, &color);
70     }
onPrePrepare(GrRecordingContext *,const GrSurfaceProxyView &,GrAppliedClip *,const GrDstProxyView &,GrXferBarrierFlags,GrLoadOp colorLoadOp)71     void onPrePrepare(GrRecordingContext*, const GrSurfaceProxyView&, GrAppliedClip*,
72                       const GrDstProxyView&, GrXferBarrierFlags, GrLoadOp colorLoadOp) override {}
onPrepare(GrOpFlushState * flushState)73     void onPrepare(GrOpFlushState* flushState) override {
74         constexpr static SkPMColor4f kCyan = {0,1,1,1};
75         auto alloc = flushState->allocator();
76         const SkMatrix& shaderMatrix = SkMatrix::I();
77         const SkMatrix& pathMatrix = fMatrix;
78         const GrCaps& caps = flushState->caps();
79         const GrShaderCaps& shaderCaps = *caps.shaderCaps();
80 
81         PathTessellator::PathDrawList pathList{pathMatrix, fPath, kCyan};
82         if (fMode == Mode::kCurveMiddleOut) {
83 #if !defined(SK_ENABLE_OPTIMIZE_SIZE)
84             // This emulates what PathStencilCoverOp does when using curves, except we include the
85             // middle-out triangles directly in the written patches for convenience (normally they
86             // use a simple triangle pipeline). But PathCurveTessellator only knows how to read
87             // extra triangles from BreadcrumbTriangleList, so build on from the middle-out stack.
88             SkArenaAlloc storage{256};
89             GrInnerFanTriangulator::BreadcrumbTriangleList triangles;
90             for (tess::PathMiddleOutFanIter it(fPath); !it.done();) {
91                 for (auto [p0, p1, p2] : it.nextStack()) {
92                     triangles.append(&storage,
93                                      pathMatrix.mapPoint(p0),
94                                      pathMatrix.mapPoint(p1),
95                                      pathMatrix.mapPoint(p2),
96                                      /*winding=*/1);
97                 }
98             }
99 
100             auto* tess = PathCurveTessellator::Make(alloc, shaderCaps.fInfinitySupport);
101             tess->prepareWithTriangles(flushState, shaderMatrix, &triangles, pathList,
102                                        fPath.countVerbs());
103             fTessellator = tess;
104 #else
105             auto* tess = PathCurveTessellator::Make(alloc, shaderCaps.fInfinitySupport);
106             tess->prepareWithTriangles(flushState, shaderMatrix, nullptr, pathList,
107                                        fPath.countVerbs());
108             fTessellator = tess;
109 #endif
110         } else {
111             // This emulates what PathStencilCoverOp does when using wedges.
112             fTessellator = PathWedgeTessellator::Make(alloc, shaderCaps.fInfinitySupport);
113             fTessellator->prepare(flushState, shaderMatrix, pathList, fPath.countVerbs());
114         }
115 
116         auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(flushState, std::move(fProcessors),
117                                                                  fPipelineFlags);
118         auto* tessShader = GrPathTessellationShader::Make(*caps.shaderCaps(),
119                                                           alloc,
120                                                           shaderMatrix,
121                                                           kCyan,
122                                                           fTessellator->patchAttribs());
123         fProgram = GrTessellationShader::MakeProgram({alloc, flushState->writeView(),
124                                                      flushState->usesMSAASurface(),
125                                                      &flushState->dstProxyView(),
126                                                      flushState->renderPassBarriers(),
127                                                      GrLoadOp::kClear, &flushState->caps()},
128                                                      tessShader,
129                                                      pipeline,
130                                                      &GrUserStencilSettings::kUnused);
131     }
132 
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)133     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
134         flushState->bindPipeline(*fProgram, chainBounds);
135         fTessellator->draw(flushState);
136     }
137 
138     const SkPath fPath;
139     const SkMatrix fMatrix;
140     const GrPipeline::InputFlags fPipelineFlags;
141     const Mode fMode;
142     PathTessellator* fTessellator = nullptr;
143     GrProgramInfo* fProgram;
144     GrProcessorSet fProcessors{SkBlendMode::kSrcOver};
145 
146     friend class GrOp;  // For ctor.
147 };
148 
149 }  // namespace
150 
151 // This slide enables wireframe and visualizes the triangles generated by path tessellators.
152 class PathTessellatorsSlide : public ClickHandlerSlide {
153 public:
PathTessellatorsSlide()154     PathTessellatorsSlide() {
155 #if 0
156         // For viewing middle-out triangulations of the inner fan.
157         fPath.moveTo(1, 0);
158         int numSides = 32 * 3;
159         for (int i = 1; i < numSides; ++i) {
160             float theta = 2*3.1415926535897932384626433832785 * i / numSides;
161             fPath.lineTo(std::cos(theta), std::sin(theta));
162         }
163         fPath.transform(SkMatrix::Scale(200, 200));
164         fPath.transform(SkMatrix::Translate(300, 300));
165 #else
166         fPath.moveTo(100, 500);
167         fPath.cubicTo(300, 400, -100, 300, 100, 200);
168         fPath.quadTo(250, 0, 400, 200);
169         fPath.conicTo(600, 350, 400, 500, fConicWeight);
170         fPath.close();
171 #endif
172         fName = "PathTessellators";
173     }
174 
175     bool onChar(SkUnichar) override;
176 
177     void draw(SkCanvas*) override;
178 
179 protected:
180     Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override;
181     bool onClick(Click*) override;
182 
183     SkPath fPath;
184     GrPipeline::InputFlags fPipelineFlags = GrPipeline::InputFlags::kWireframe;
185     Mode fMode = Mode::kWedgeMiddleOut;
186 
187     float fConicWeight = .5;
188 
189     class Click;
190 };
191 
draw(SkCanvas * canvas)192 void PathTessellatorsSlide::draw(SkCanvas* canvas) {
193     canvas->clear(SK_ColorBLACK);
194 
195     auto ctx = canvas->recordingContext();
196     auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas);
197 
198     SkString error;
199     if (!sdc || !ctx) {
200         error = "GPU Only.";
201     } else if (!skgpu::v1::TessellationPathRenderer::IsSupported(*ctx->priv().caps())) {
202         error = "TessellationPathRenderer not supported.";
203     }
204     if (!error.isEmpty()) {
205         canvas->clear(SK_ColorRED);
206         SkFont font(nullptr, 20);
207         SkPaint captionPaint;
208         captionPaint.setColor(SK_ColorWHITE);
209         canvas->drawString(error.c_str(), 10, 30, font, captionPaint);
210         return;
211     }
212 
213     sdc->addDrawOp(GrOp::Make<SamplePathTessellatorOp>(ctx,
214                                                        sdc->asRenderTargetProxy()->getBoundsRect(),
215                                                        fPath, canvas->getTotalMatrix(),
216                                                        fPipelineFlags, fMode));
217 
218     // Draw the path points.
219     SkPaint pointsPaint;
220     pointsPaint.setColor(SK_ColorBLUE);
221     pointsPaint.setStrokeWidth(8);
222     SkPath devPath = fPath;
223     devPath.transform(canvas->getTotalMatrix());
224     {
225         SkAutoCanvasRestore acr(canvas, true);
226         canvas->setMatrix(SkMatrix::I());
227         SkString caption(ModeName(fMode));
228         caption.appendf(" (w=%g)", fConicWeight);
229         SkFont font(nullptr, 20);
230         SkPaint captionPaint;
231         captionPaint.setColor(SK_ColorWHITE);
232         canvas->drawString(caption, 10, 30, font, captionPaint);
233         canvas->drawPoints(SkCanvas::kPoints_PointMode, devPath.countPoints(),
234                            SkPathPriv::PointData(devPath), pointsPaint);
235     }
236 }
237 
238 class PathTessellatorsSlide::Click : public ClickHandlerSlide::Click {
239 public:
Click(int ptIdx)240     Click(int ptIdx) : fPtIdx(ptIdx) {}
241 
doClick(SkPath * path)242     void doClick(SkPath* path) {
243         SkPoint pt = path->getPoint(fPtIdx);
244         SkPathPriv::UpdatePathPoint(path, fPtIdx, pt + fCurr - fPrev);
245     }
246 
247 private:
248     int fPtIdx;
249 };
250 
onFindClickHandler(SkScalar x,SkScalar y,skui::ModifierKey)251 ClickHandlerSlide::Click* PathTessellatorsSlide::onFindClickHandler(SkScalar x, SkScalar y,
252                                                                     skui::ModifierKey) {
253     const SkPoint* pts = SkPathPriv::PointData(fPath);
254     float fuzz = 30;
255     for (int i = 0; i < fPath.countPoints(); ++i) {
256         if (fabs(x - pts[i].x()) < fuzz && fabsf(y - pts[i].y()) < fuzz) {
257             return new Click(i);
258         }
259     }
260     return nullptr;
261 }
262 
onClick(ClickHandlerSlide::Click * click)263 bool PathTessellatorsSlide::onClick(ClickHandlerSlide::Click* click) {
264     Click* myClick = (Click*)click;
265     myClick->doClick(&fPath);
266     return true;
267 }
268 
update_weight(const SkPath & path,float w)269 static SkPath update_weight(const SkPath& path, float w) {
270     SkPath path_;
271     for (auto [verb, pts, _] : SkPathPriv::Iterate(path)) {
272         switch (verb) {
273             case SkPathVerb::kMove:
274                 path_.moveTo(pts[0]);
275                 break;
276             case SkPathVerb::kLine:
277                 path_.lineTo(pts[1]);
278                 break;
279             case SkPathVerb::kQuad:
280                 path_.quadTo(pts[1], pts[2]);
281                 break;
282             case SkPathVerb::kCubic:
283                 path_.cubicTo(pts[1], pts[2], pts[3]);
284                 break;
285             case SkPathVerb::kConic:
286                 path_.conicTo(pts[1], pts[2], (w != 1) ? w : .99f);
287                 break;
288             case SkPathVerb::kClose:
289                 break;
290         }
291     }
292     return path_;
293 }
294 
onChar(SkUnichar unichar)295 bool PathTessellatorsSlide::onChar(SkUnichar unichar) {
296     switch (unichar) {
297         case 'w':
298             fPipelineFlags = (GrPipeline::InputFlags)(
299                     (int)fPipelineFlags ^ (int)GrPipeline::InputFlags::kWireframe);
300             return true;
301         case 'D': {
302             fPath.dump();
303             return true;
304         }
305         case '+':
306             fConicWeight *= 2;
307             fPath = update_weight(fPath, fConicWeight);
308             return true;
309         case '=':
310             fConicWeight *= 5/4.f;
311             fPath = update_weight(fPath, fConicWeight);
312             return true;
313         case '_':
314             fConicWeight *= .5f;
315             fPath = update_weight(fPath, fConicWeight);
316             return true;
317         case '-':
318             fConicWeight *= 4/5.f;
319             fPath = update_weight(fPath, fConicWeight);
320             return true;
321         case '1':
322         case '2':
323             fMode = (Mode)(unichar - '1');
324             return true;
325     }
326     return false;
327 }
328 
329 DEF_SLIDE( return new PathTessellatorsSlide; )
330 
331 }  // namespace skgpu::v1
332 
333 #endif  // defined(SK_GANESH)
334