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