blob: 8ec922c9f0c72e5acbaea281feb425f0cdf31c3d [file] [log] [blame]
Chris Daltona9f759d2021-05-18 12:37:08 -06001/*
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"
Robert Phillips06273bc2021-08-11 15:43:50 -040015#include "src/gpu/GrOpFlushState.h"
Chris Daltona9f759d2021-05-18 12:37:08 -060016#include "src/gpu/GrRecordingContextPriv.h"
Robert Phillips06273bc2021-08-11 15:43:50 -040017#include "src/gpu/ops/GrDrawOp.h"
18#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
Robert Phillipse453fa02021-08-19 14:57:05 -040019#include "src/gpu/ops/TessellationPathRenderer.h"
Chris Dalton09ece932021-11-10 20:36:03 -070020#include "src/gpu/tessellate/AffineMatrix.h"
Chris Dalton0de8a962021-10-13 10:09:50 -060021#include "src/gpu/tessellate/PathCurveTessellator.h"
22#include "src/gpu/tessellate/PathWedgeTessellator.h"
Chris Dalton3b412782021-06-01 13:40:03 -060023#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
Robert Phillips4dca8312021-07-28 15:13:20 -040024#include "src/gpu/v1/SurfaceDrawContext_v1.h"
Chris Daltona9f759d2021-05-18 12:37:08 -060025
Chris Dalton49c76672021-10-20 09:45:03 -060026namespace skgpu {
27
Chris Dalton09ece932021-11-10 20:36:03 -070028using TrianglePatch = PatchWriter::TrianglePatch;
29
Chris Daltona9f759d2021-05-18 12:37:08 -060030namespace {
31
32enum class Mode {
Chris Daltond2b8ba32021-06-09 00:12:59 -060033 kWedgeMiddleOut,
Chris Daltona9f759d2021-05-18 12:37:08 -060034 kCurveMiddleOut,
35 kWedgeTessellate,
36 kCurveTessellate
37};
38
39static const char* ModeName(Mode mode) {
40 switch (mode) {
Chris Daltond2b8ba32021-06-09 00:12:59 -060041 case Mode::kWedgeMiddleOut:
42 return "MiddleOutShader (kWedges)";
Chris Daltona9f759d2021-05-18 12:37:08 -060043 case Mode::kCurveMiddleOut:
Chris Daltond2b8ba32021-06-09 00:12:59 -060044 return "MiddleOutShader (kCurves)";
Chris Daltona9f759d2021-05-18 12:37:08 -060045 case Mode::kWedgeTessellate:
Chris Daltond2b8ba32021-06-09 00:12:59 -060046 return "HardwareWedgeShader";
Chris Daltona9f759d2021-05-18 12:37:08 -060047 case Mode::kCurveTessellate:
Chris Daltond2b8ba32021-06-09 00:12:59 -060048 return "HardwareCurveShader";
Chris Daltona9f759d2021-05-18 12:37:08 -060049 }
50 SkUNREACHABLE;
51}
52
53// Draws a path directly to the screen using a specific tessellator.
54class SamplePathTessellatorOp : public GrDrawOp {
55private:
56 DEFINE_OP_CLASS_ID
57
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 }
67 const char* name() const override { return "SamplePathTessellatorOp"; }
Robert Phillips294723d2021-06-17 09:23:58 -040068 void visitProxies(const GrVisitProxyFunc&) const override {}
Chris Daltona9f759d2021-05-18 12:37:08 -060069 FixedFunctionFlags fixedFunctionFlags() const override {
70 return FixedFunctionFlags::kUsesHWAA;
71 }
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 }
78 void onPrePrepare(GrRecordingContext*, const GrSurfaceProxyView&, GrAppliedClip*,
John Stiles52cb1d02021-06-02 11:58:05 -040079 const GrDstProxyView&, GrXferBarrierFlags, GrLoadOp colorLoadOp) override {}
Chris Daltona9f759d2021-05-18 12:37:08 -060080 void onPrepare(GrOpFlushState* flushState) override {
Chris Dalton2f733ec2021-06-01 12:11:57 -060081 constexpr static SkPMColor4f kCyan = {0,1,1,1};
Chris Daltona9f759d2021-05-18 12:37:08 -060082 auto alloc = flushState->allocator();
Chris Dalton69669812021-07-27 10:00:12 -060083 const SkMatrix& shaderMatrix = SkMatrix::I();
84 const SkMatrix& pathMatrix = fMatrix;
Chris Daltond2b8ba32021-06-09 00:12:59 -060085 const GrCaps& caps = flushState->caps();
Chris Daltonf0f447c2021-11-10 17:39:56 -070086 const GrShaderCaps& shaderCaps = *caps.shaderCaps();
Chris Daltond2b8ba32021-06-09 00:12:59 -060087 int numVerbsToGetMiddleOut = 0;
88 int numVerbsToGetTessellation = caps.minPathVerbsForHwTessellation();
Chris Dalton198ac152021-06-09 13:49:43 -060089 auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(flushState, std::move(fProcessors),
90 fPipelineFlags);
Chris Daltonf0f447c2021-11-10 17:39:56 -070091 int numVerbs;
Chris Dalton09ece932021-11-10 20:36:03 -070092 bool needsInnerFan;
Chris Daltona9f759d2021-05-18 12:37:08 -060093 switch (fMode) {
Chris Daltond2b8ba32021-06-09 00:12:59 -060094 case Mode::kWedgeMiddleOut:
Chris Daltonf0f447c2021-11-10 17:39:56 -070095 fTessellator = PathWedgeTessellator::Make(alloc, shaderCaps.infinitySupport());
96 numVerbs = numVerbsToGetMiddleOut;
Chris Dalton09ece932021-11-10 20:36:03 -070097 needsInnerFan = false;
Chris Daltond2b8ba32021-06-09 00:12:59 -060098 break;
Chris Daltona9f759d2021-05-18 12:37:08 -060099 case Mode::kCurveMiddleOut:
Chris Dalton49c76672021-10-20 09:45:03 -0600100 fTessellator = PathCurveTessellator::Make(alloc,
Chris Daltonf0f447c2021-11-10 17:39:56 -0700101 shaderCaps.infinitySupport());
102 numVerbs = numVerbsToGetMiddleOut;
Chris Dalton09ece932021-11-10 20:36:03 -0700103 needsInnerFan = true;
Chris Daltona9f759d2021-05-18 12:37:08 -0600104 break;
105 case Mode::kWedgeTessellate:
Chris Daltonf0f447c2021-11-10 17:39:56 -0700106 fTessellator = PathWedgeTessellator::Make(alloc, shaderCaps.infinitySupport());
107 numVerbs = numVerbsToGetTessellation;
Chris Dalton09ece932021-11-10 20:36:03 -0700108 needsInnerFan = false;
Chris Daltona9f759d2021-05-18 12:37:08 -0600109 break;
110 case Mode::kCurveTessellate:
Chris Dalton49c76672021-10-20 09:45:03 -0600111 fTessellator = PathCurveTessellator::Make(alloc,
Chris Daltonf0f447c2021-11-10 17:39:56 -0700112 shaderCaps.infinitySupport());
113 numVerbs = numVerbsToGetTessellation;
Chris Dalton09ece932021-11-10 20:36:03 -0700114 needsInnerFan = true;
Chris Daltona9f759d2021-05-18 12:37:08 -0600115 break;
116 }
Chris Daltonf0f447c2021-11-10 17:39:56 -0700117 auto* tessShader = GrPathTessellationShader::Make(alloc,
118 shaderMatrix,
119 kCyan,
120 numVerbs,
121 *pipeline,
122 fTessellator->patchAttribs(),
123 caps);
Chris Dalton2f733ec2021-06-01 12:11:57 -0600124 fProgram = GrTessellationShader::MakeProgram({alloc, flushState->writeView(),
Chris Dalton2a26c502021-08-26 10:05:11 -0600125 flushState->usesMSAASurface(),
Chris Dalton2f733ec2021-06-01 12:11:57 -0600126 &flushState->dstProxyView(),
127 flushState->renderPassBarriers(),
128 GrLoadOp::kClear, &flushState->caps()},
Chris Daltonf0f447c2021-11-10 17:39:56 -0700129 tessShader,
130 pipeline,
Chris Dalton2f733ec2021-06-01 12:11:57 -0600131 &GrUserStencilSettings::kUnused);
Chris Dalton09ece932021-11-10 20:36:03 -0700132
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
Chris Daltona9f759d2021-05-18 12:37:08 -0600160 }
161 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
162 flushState->bindPipeline(*fProgram, chainBounds);
Chris Daltonf0f447c2021-11-10 17:39:56 -0700163 fTessellator->draw(flushState, fProgram->geomProc().willUseTessellationShaders());
Chris Daltona9f759d2021-05-18 12:37:08 -0600164 }
165
166 const SkPath fPath;
167 const SkMatrix fMatrix;
168 const GrPipeline::InputFlags fPipelineFlags;
169 const Mode fMode;
Chris Dalton49c76672021-10-20 09:45:03 -0600170 PathTessellator* fTessellator = nullptr;
Chris Daltona9f759d2021-05-18 12:37:08 -0600171 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.
180class SamplePathTessellators : public Sample {
181public:
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
202private:
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
208 SkString name() override { return SkString("PathTessellators"); }
209
210 SkPath fPath;
Chris Daltoneb0195e2021-08-18 21:39:02 -0600211 GrPipeline::InputFlags fPipelineFlags = GrPipeline::InputFlags::kWireframe;
Chris Daltond2b8ba32021-06-09 00:12:59 -0600212 Mode fMode = Mode::kWedgeMiddleOut;
Chris Daltona9f759d2021-05-18 12:37:08 -0600213
214 float fConicWeight = .5;
215
216 class Click;
217};
218
219void SamplePathTessellators::onDrawContent(SkCanvas* canvas) {
220 canvas->clear(SK_ColorBLACK);
221
222 auto ctx = canvas->recordingContext();
Robert Phillips4dca8312021-07-28 15:13:20 -0400223 auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas);
Chris Daltona9f759d2021-05-18 12:37:08 -0600224
225 SkString error;
226 if (!sdc || !ctx) {
227 error = "GPU Only.";
Robert Phillips24d622d2021-08-19 17:04:05 -0400228 } else if (!skgpu::v1::TessellationPathRenderer::IsSupported(*ctx->priv().caps())) {
229 error = "TessellationPathRenderer not supported.";
Chris Daltona9f759d2021-05-18 12:37:08 -0600230 } 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
268class SamplePathTessellators::Click : public Sample::Click {
269public:
270 Click(int ptIdx) : fPtIdx(ptIdx) {}
271
272 void doClick(SkPath* path) {
273 SkPoint pt = path->getPoint(fPtIdx);
274 SkPathPriv::UpdatePathPoint(path, fPtIdx, pt + fCurr - fPrev);
275 }
276
277private:
278 int fPtIdx;
279};
280
281Sample::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
293bool SamplePathTessellators::onClick(Sample::Click* click) {
294 Click* myClick = (Click*)click;
295 myClick->doClick(&fPath);
296 return true;
297}
298
299static 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
325bool 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':
Chris Daltond2b8ba32021-06-09 00:12:59 -0600354 case '4':
Chris Daltona9f759d2021-05-18 12:37:08 -0600355 fMode = (Mode)(unichar - '1');
356 return true;
357 }
358 return false;
359}
360
361Sample* MakeTessellatedPathSample() { return new SamplePathTessellators; }
362static SampleRegistry gTessellatedPathSample(MakeTessellatedPathSample);
363
Chris Dalton49c76672021-10-20 09:45:03 -0600364} // namespace skgpu
365
Chris Daltona9f759d2021-05-18 12:37:08 -0600366#endif // SK_SUPPORT_GPU