• 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 "samplecode/Sample.h"
10 #include "src/core/SkPathPriv.h"
11 
12 #if SK_SUPPORT_GPU
13 
14 #include "src/core/SkCanvasPriv.h"
15 #include "src/gpu/GrOpFlushState.h"
16 #include "src/gpu/GrRecordingContextPriv.h"
17 #include "src/gpu/ops/GrDrawOp.h"
18 #include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
19 #include "src/gpu/ops/TessellationPathRenderer.h"
20 #include "src/gpu/tessellate/AffineMatrix.h"
21 #include "src/gpu/tessellate/MiddleOutPolygonTriangulator.h"
22 #include "src/gpu/tessellate/PathCurveTessellator.h"
23 #include "src/gpu/tessellate/PathWedgeTessellator.h"
24 #include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
25 #include "src/gpu/v1/SurfaceDrawContext_v1.h"
26 
27 namespace skgpu {
28 
29 namespace {
30 
31 enum class Mode {
32     kWedgeMiddleOut,
33     kCurveMiddleOut,
34     kWedgeTessellate,
35     kCurveTessellate
36 };
37 
ModeName(Mode mode)38 static const char* ModeName(Mode mode) {
39     switch (mode) {
40         case Mode::kWedgeMiddleOut:
41             return "MiddleOutShader (kWedges)";
42         case Mode::kCurveMiddleOut:
43             return "MiddleOutShader (kCurves)";
44         case Mode::kWedgeTessellate:
45             return "HardwareWedgeShader";
46         case Mode::kCurveTessellate:
47             return "HardwareCurveShader";
48     }
49     SkUNREACHABLE;
50 }
51 
52 // Draws a path directly to the screen using a specific tessellator.
53 class SamplePathTessellatorOp : public GrDrawOp {
54 private:
55     DEFINE_OP_CLASS_ID
56 
SamplePathTessellatorOp(const SkRect & drawBounds,const SkPath & path,const SkMatrix & m,GrPipeline::InputFlags pipelineFlags,Mode mode)57     SamplePathTessellatorOp(const SkRect& drawBounds, const SkPath& path, const SkMatrix& m,
58                             GrPipeline::InputFlags pipelineFlags, Mode mode)
59             : GrDrawOp(ClassID())
60             , fPath(path)
61             , fMatrix(m)
62             , fPipelineFlags(pipelineFlags)
63             , fMode(mode) {
64         this->setBounds(drawBounds, HasAABloat::kNo, IsHairline::kNo);
65     }
name() const66     const char* name() const override { return "SamplePathTessellatorOp"; }
visitProxies(const GrVisitProxyFunc &) const67     void visitProxies(const GrVisitProxyFunc&) const override {}
fixedFunctionFlags() const68     FixedFunctionFlags fixedFunctionFlags() const override {
69         return FixedFunctionFlags::kUsesHWAA;
70     }
finalize(const GrCaps & caps,const GrAppliedClip * clip,GrClampType clampType)71     GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
72                                       GrClampType clampType) override {
73         SkPMColor4f color;
74         return fProcessors.finalize(SK_PMColor4fWHITE, GrProcessorAnalysisCoverage::kNone, clip,
75                                     nullptr, caps, clampType, &color);
76     }
onPrePrepare(GrRecordingContext *,const GrSurfaceProxyView &,GrAppliedClip *,const GrDstProxyView &,GrXferBarrierFlags,GrLoadOp colorLoadOp)77     void onPrePrepare(GrRecordingContext*, const GrSurfaceProxyView&, GrAppliedClip*,
78                       const GrDstProxyView&, GrXferBarrierFlags, GrLoadOp colorLoadOp) override {}
onPrepare(GrOpFlushState * flushState)79     void onPrepare(GrOpFlushState* flushState) override {
80         constexpr static SkPMColor4f kCyan = {0,1,1,1};
81         auto alloc = flushState->allocator();
82         const SkMatrix& shaderMatrix = SkMatrix::I();
83         const SkMatrix& pathMatrix = fMatrix;
84         const GrCaps& caps = flushState->caps();
85         const GrShaderCaps& shaderCaps = *caps.shaderCaps();
86         int numVerbsToGetMiddleOut = 0;
87         int numVerbsToGetTessellation = caps.minPathVerbsForHwTessellation();
88         auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(flushState, std::move(fProcessors),
89                                                                  fPipelineFlags);
90         int numVerbs;
91         bool needsInnerFan;
92         switch (fMode) {
93             case Mode::kWedgeMiddleOut:
94                 fTessellator = PathWedgeTessellator::Make(alloc, shaderCaps.infinitySupport());
95                 numVerbs = numVerbsToGetMiddleOut;
96                 needsInnerFan = false;
97                 break;
98             case Mode::kCurveMiddleOut:
99                 fTessellator = PathCurveTessellator::Make(alloc,
100                                                           shaderCaps.infinitySupport());
101                 numVerbs = numVerbsToGetMiddleOut;
102                 needsInnerFan = true;
103                 break;
104             case Mode::kWedgeTessellate:
105                 fTessellator = PathWedgeTessellator::Make(alloc, shaderCaps.infinitySupport());
106                 numVerbs = numVerbsToGetTessellation;
107                 needsInnerFan = false;
108                 break;
109             case Mode::kCurveTessellate:
110                 fTessellator = PathCurveTessellator::Make(alloc,
111                                                           shaderCaps.infinitySupport());
112                 numVerbs = numVerbsToGetTessellation;
113                 needsInnerFan = true;
114                 break;
115         }
116         auto* tessShader = GrPathTessellationShader::Make(alloc,
117                                                           shaderMatrix,
118                                                           kCyan,
119                                                           numVerbs,
120                                                           *pipeline,
121                                                           fTessellator->patchAttribs(),
122                                                           caps);
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 
133         int patchPreallocCount = fTessellator->patchPreallocCount(fPath.countVerbs());
134         if (needsInnerFan) {
135             patchPreallocCount += fPath.countVerbs() - 1;
136         }
137         PatchWriter patchWriter(flushState,
138                                 fTessellator,
139                                 tessShader->maxTessellationSegments(*caps.shaderCaps()),
140                                 patchPreallocCount);
141 
142         if (needsInnerFan) {
143             // Write out inner fan triangles.
144             AffineMatrix m(pathMatrix);
145             for (PathMiddleOutFanIter it(fPath); !it.done();) {
146                 for (auto [p0, p1, p2] : it.nextStack()) {
147                     auto [mp0, mp1] = m.map2Points(p0, p1);
148                     auto mp2 = m.map1Point(&p2);
149                     patchWriter.writeTriangle(mp0, mp1, mp2);
150                 }
151             }
152         }
153 
154         // Write out the curves.
155         fTessellator->writePatches(patchWriter, shaderMatrix, {pathMatrix, fPath, kCyan});
156 
157         if (!tessShader->willUseTessellationShaders()) {
158             fTessellator->prepareFixedCountBuffers(flushState);
159         }
160 
161     }
onExecute(GrOpFlushState * flushState,const SkRect & chainBounds)162     void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
163         flushState->bindPipeline(*fProgram, chainBounds);
164         fTessellator->draw(flushState, fProgram->geomProc().willUseTessellationShaders());
165     }
166 
167     const SkPath fPath;
168     const SkMatrix fMatrix;
169     const GrPipeline::InputFlags fPipelineFlags;
170     const Mode fMode;
171     PathTessellator* fTessellator = nullptr;
172     GrProgramInfo* fProgram;
173     GrProcessorSet fProcessors{SkBlendMode::kSrcOver};
174 
175     friend class GrOp;  // For ctor.
176 };
177 
178 }  // namespace
179 
180 // This sample enables wireframe and visualizes the triangles generated by path tessellators.
181 class SamplePathTessellators : public Sample {
182 public:
SamplePathTessellators()183     SamplePathTessellators() {
184 #if 0
185         // For viewing middle-out triangulations of the inner fan.
186         fPath.moveTo(1, 0);
187         int numSides = 32 * 3;
188         for (int i = 1; i < numSides; ++i) {
189             float theta = 2*3.1415926535897932384626433832785 * i / numSides;
190             fPath.lineTo(std::cos(theta), std::sin(theta));
191         }
192         fPath.transform(SkMatrix::Scale(200, 200));
193         fPath.transform(SkMatrix::Translate(300, 300));
194 #else
195         fPath.moveTo(100, 500);
196         fPath.cubicTo(300, 400, -100, 300, 100, 200);
197         fPath.quadTo(250, 0, 400, 200);
198         fPath.conicTo(600, 350, 400, 500, fConicWeight);
199         fPath.close();
200 #endif
201     }
202 
203 private:
204     void onDrawContent(SkCanvas*) override;
205     Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override;
206     bool onClick(Sample::Click*) override;
207     bool onChar(SkUnichar) override;
208 
name()209     SkString name() override { return SkString("PathTessellators"); }
210 
211     SkPath fPath;
212     GrPipeline::InputFlags fPipelineFlags = GrPipeline::InputFlags::kWireframe;
213     Mode fMode = Mode::kWedgeMiddleOut;
214 
215     float fConicWeight = .5;
216 
217     class Click;
218 };
219 
onDrawContent(SkCanvas * canvas)220 void SamplePathTessellators::onDrawContent(SkCanvas* canvas) {
221     canvas->clear(SK_ColorBLACK);
222 
223     auto ctx = canvas->recordingContext();
224     auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas);
225 
226     SkString error;
227     if (!sdc || !ctx) {
228         error = "GPU Only.";
229     } else if (!skgpu::v1::TessellationPathRenderer::IsSupported(*ctx->priv().caps())) {
230         error = "TessellationPathRenderer not supported.";
231     } else if (fMode >= Mode::kWedgeTessellate &&
232                !ctx->priv().caps()->shaderCaps()->tessellationSupport()) {
233         error.printf("%s requires hardware tessellation support.", ModeName(fMode));
234     }
235     if (!error.isEmpty()) {
236         canvas->clear(SK_ColorRED);
237         SkFont font(nullptr, 20);
238         SkPaint captionPaint;
239         captionPaint.setColor(SK_ColorWHITE);
240         canvas->drawString(error.c_str(), 10, 30, font, captionPaint);
241         return;
242     }
243 
244     sdc->addDrawOp(GrOp::Make<SamplePathTessellatorOp>(ctx,
245                                                        sdc->asRenderTargetProxy()->getBoundsRect(),
246                                                        fPath, canvas->getTotalMatrix(),
247                                                        fPipelineFlags, fMode));
248 
249     // Draw the path points.
250     SkPaint pointsPaint;
251     pointsPaint.setColor(SK_ColorBLUE);
252     pointsPaint.setStrokeWidth(8);
253     SkPath devPath = fPath;
254     devPath.transform(canvas->getTotalMatrix());
255     {
256         SkAutoCanvasRestore acr(canvas, true);
257         canvas->setMatrix(SkMatrix::I());
258         SkString caption(ModeName(fMode));
259         caption.appendf(" (w=%g)", fConicWeight);
260         SkFont font(nullptr, 20);
261         SkPaint captionPaint;
262         captionPaint.setColor(SK_ColorWHITE);
263         canvas->drawString(caption, 10, 30, font, captionPaint);
264         canvas->drawPoints(SkCanvas::kPoints_PointMode, devPath.countPoints(),
265                            SkPathPriv::PointData(devPath), pointsPaint);
266     }
267 }
268 
269 class SamplePathTessellators::Click : public Sample::Click {
270 public:
Click(int ptIdx)271     Click(int ptIdx) : fPtIdx(ptIdx) {}
272 
doClick(SkPath * path)273     void doClick(SkPath* path) {
274         SkPoint pt = path->getPoint(fPtIdx);
275         SkPathPriv::UpdatePathPoint(path, fPtIdx, pt + fCurr - fPrev);
276     }
277 
278 private:
279     int fPtIdx;
280 };
281 
onFindClickHandler(SkScalar x,SkScalar y,skui::ModifierKey)282 Sample::Click* SamplePathTessellators::onFindClickHandler(SkScalar x, SkScalar y,
283                                                           skui::ModifierKey) {
284     const SkPoint* pts = SkPathPriv::PointData(fPath);
285     float fuzz = 30;
286     for (int i = 0; i < fPath.countPoints(); ++i) {
287         if (fabs(x - pts[i].x()) < fuzz && fabsf(y - pts[i].y()) < fuzz) {
288             return new Click(i);
289         }
290     }
291     return nullptr;
292 }
293 
onClick(Sample::Click * click)294 bool SamplePathTessellators::onClick(Sample::Click* click) {
295     Click* myClick = (Click*)click;
296     myClick->doClick(&fPath);
297     return true;
298 }
299 
update_weight(const SkPath & path,float w)300 static SkPath update_weight(const SkPath& path, float w) {
301     SkPath path_;
302     for (auto [verb, pts, _] : SkPathPriv::Iterate(path)) {
303         switch (verb) {
304             case SkPathVerb::kMove:
305                 path_.moveTo(pts[0]);
306                 break;
307             case SkPathVerb::kLine:
308                 path_.lineTo(pts[1]);
309                 break;
310             case SkPathVerb::kQuad:
311                 path_.quadTo(pts[1], pts[2]);
312                 break;
313             case SkPathVerb::kCubic:
314                 path_.cubicTo(pts[1], pts[2], pts[3]);
315                 break;
316             case SkPathVerb::kConic:
317                 path_.conicTo(pts[1], pts[2], (w != 1) ? w : .99f);
318                 break;
319             case SkPathVerb::kClose:
320                 break;
321         }
322     }
323     return path_;
324 }
325 
onChar(SkUnichar unichar)326 bool SamplePathTessellators::onChar(SkUnichar unichar) {
327     switch (unichar) {
328         case 'w':
329             fPipelineFlags = (GrPipeline::InputFlags)(
330                     (int)fPipelineFlags ^ (int)GrPipeline::InputFlags::kWireframe);
331             return true;
332         case 'D': {
333             fPath.dump();
334             return true;
335         }
336         case '+':
337             fConicWeight *= 2;
338             fPath = update_weight(fPath, fConicWeight);
339             return true;
340         case '=':
341             fConicWeight *= 5/4.f;
342             fPath = update_weight(fPath, fConicWeight);
343             return true;
344         case '_':
345             fConicWeight *= .5f;
346             fPath = update_weight(fPath, fConicWeight);
347             return true;
348         case '-':
349             fConicWeight *= 4/5.f;
350             fPath = update_weight(fPath, fConicWeight);
351             return true;
352         case '1':
353         case '2':
354         case '3':
355         case '4':
356             fMode = (Mode)(unichar - '1');
357             return true;
358     }
359     return false;
360 }
361 
MakeTessellatedPathSample()362 Sample* MakeTessellatedPathSample() { return new SamplePathTessellators; }
363 static SampleRegistry gTessellatedPathSample(MakeTessellatedPathSample);
364 
365 }  // namespace skgpu
366 
367 #endif  // SK_SUPPORT_GPU
368