blob: a1478878d7db26eb7d23af03983b9318f630baca [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 Dalton0de8a962021-10-13 10:09:50 -060020#include "src/gpu/tessellate/PathCurveTessellator.h"
21#include "src/gpu/tessellate/PathWedgeTessellator.h"
Chris Dalton3b412782021-06-01 13:40:03 -060022#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
Robert Phillips4dca8312021-07-28 15:13:20 -040023#include "src/gpu/v1/SurfaceDrawContext_v1.h"
Chris Daltona9f759d2021-05-18 12:37:08 -060024
25namespace {
26
27enum class Mode {
Chris Daltond2b8ba32021-06-09 00:12:59 -060028 kWedgeMiddleOut,
Chris Daltona9f759d2021-05-18 12:37:08 -060029 kCurveMiddleOut,
30 kWedgeTessellate,
31 kCurveTessellate
32};
33
34static const char* ModeName(Mode mode) {
35 switch (mode) {
Chris Daltond2b8ba32021-06-09 00:12:59 -060036 case Mode::kWedgeMiddleOut:
37 return "MiddleOutShader (kWedges)";
Chris Daltona9f759d2021-05-18 12:37:08 -060038 case Mode::kCurveMiddleOut:
Chris Daltond2b8ba32021-06-09 00:12:59 -060039 return "MiddleOutShader (kCurves)";
Chris Daltona9f759d2021-05-18 12:37:08 -060040 case Mode::kWedgeTessellate:
Chris Daltond2b8ba32021-06-09 00:12:59 -060041 return "HardwareWedgeShader";
Chris Daltona9f759d2021-05-18 12:37:08 -060042 case Mode::kCurveTessellate:
Chris Daltond2b8ba32021-06-09 00:12:59 -060043 return "HardwareCurveShader";
Chris Daltona9f759d2021-05-18 12:37:08 -060044 }
45 SkUNREACHABLE;
46}
47
48// Draws a path directly to the screen using a specific tessellator.
49class SamplePathTessellatorOp : public GrDrawOp {
50private:
51 DEFINE_OP_CLASS_ID
52
53 SamplePathTessellatorOp(const SkRect& drawBounds, const SkPath& path, const SkMatrix& m,
54 GrPipeline::InputFlags pipelineFlags, Mode mode)
55 : GrDrawOp(ClassID())
56 , fPath(path)
57 , fMatrix(m)
58 , fPipelineFlags(pipelineFlags)
59 , fMode(mode) {
60 this->setBounds(drawBounds, HasAABloat::kNo, IsHairline::kNo);
61 }
62 const char* name() const override { return "SamplePathTessellatorOp"; }
Robert Phillips294723d2021-06-17 09:23:58 -040063 void visitProxies(const GrVisitProxyFunc&) const override {}
Chris Daltona9f759d2021-05-18 12:37:08 -060064 FixedFunctionFlags fixedFunctionFlags() const override {
65 return FixedFunctionFlags::kUsesHWAA;
66 }
67 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
68 GrClampType clampType) override {
69 SkPMColor4f color;
70 return fProcessors.finalize(SK_PMColor4fWHITE, GrProcessorAnalysisCoverage::kNone, clip,
71 nullptr, caps, clampType, &color);
72 }
73 void onPrePrepare(GrRecordingContext*, const GrSurfaceProxyView&, GrAppliedClip*,
John Stiles52cb1d02021-06-02 11:58:05 -040074 const GrDstProxyView&, GrXferBarrierFlags, GrLoadOp colorLoadOp) override {}
Chris Daltona9f759d2021-05-18 12:37:08 -060075 void onPrepare(GrOpFlushState* flushState) override {
Chris Dalton2f733ec2021-06-01 12:11:57 -060076 constexpr static SkPMColor4f kCyan = {0,1,1,1};
Chris Daltona9f759d2021-05-18 12:37:08 -060077 auto alloc = flushState->allocator();
Chris Dalton69669812021-07-27 10:00:12 -060078 const SkMatrix& shaderMatrix = SkMatrix::I();
79 const SkMatrix& pathMatrix = fMatrix;
Chris Daltond2b8ba32021-06-09 00:12:59 -060080 const GrCaps& caps = flushState->caps();
81 int numVerbsToGetMiddleOut = 0;
82 int numVerbsToGetTessellation = caps.minPathVerbsForHwTessellation();
Chris Dalton198ac152021-06-09 13:49:43 -060083 auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(flushState, std::move(fProcessors),
84 fPipelineFlags);
Chris Daltona9f759d2021-05-18 12:37:08 -060085 switch (fMode) {
Chris Dalton0de8a962021-10-13 10:09:50 -060086 using DrawInnerFan = skgpu::tess::PathCurveTessellator::DrawInnerFan;
Chris Daltond2b8ba32021-06-09 00:12:59 -060087 case Mode::kWedgeMiddleOut:
Chris Dalton0de8a962021-10-13 10:09:50 -060088 fTessellator = skgpu::tess::PathWedgeTessellator::Make(alloc,
89 shaderMatrix,
90 kCyan,
91 numVerbsToGetMiddleOut,
92 *pipeline,
93
94 caps);
Chris Daltond2b8ba32021-06-09 00:12:59 -060095 break;
Chris Daltona9f759d2021-05-18 12:37:08 -060096 case Mode::kCurveMiddleOut:
Chris Dalton0de8a962021-10-13 10:09:50 -060097 fTessellator = skgpu::tess::PathCurveTessellator::Make(alloc,
98 shaderMatrix,
99 kCyan,
100 DrawInnerFan::kYes,
101 numVerbsToGetMiddleOut,
102 *pipeline,
103 caps);
Chris Daltona9f759d2021-05-18 12:37:08 -0600104 break;
105 case Mode::kWedgeTessellate:
Chris Dalton0de8a962021-10-13 10:09:50 -0600106 fTessellator = skgpu::tess::PathWedgeTessellator::Make(alloc,
107 shaderMatrix,
108 kCyan,
109 numVerbsToGetTessellation,
110 *pipeline,
111 caps);
Chris Daltona9f759d2021-05-18 12:37:08 -0600112 break;
113 case Mode::kCurveTessellate:
Chris Dalton0de8a962021-10-13 10:09:50 -0600114 fTessellator = skgpu::tess::PathCurveTessellator::Make(alloc,
115 shaderMatrix,
116 kCyan,
117 DrawInnerFan::kYes,
118 numVerbsToGetTessellation,
119 *pipeline,
120 caps);
Chris Daltona9f759d2021-05-18 12:37:08 -0600121 break;
122 }
Chris Dalton17eaf622021-07-28 09:43:52 -0600123 fTessellator->prepare(flushState, this->bounds(), {pathMatrix, fPath}, fPath.countVerbs());
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()},
129 fTessellator->shader(), pipeline,
130 &GrUserStencilSettings::kUnused);
Chris Daltona9f759d2021-05-18 12:37:08 -0600131 }
132 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
133 flushState->bindPipeline(*fProgram, chainBounds);
134 fTessellator->draw(flushState);
135 }
136
137 const SkPath fPath;
138 const SkMatrix fMatrix;
139 const GrPipeline::InputFlags fPipelineFlags;
140 const Mode fMode;
Chris Dalton0de8a962021-10-13 10:09:50 -0600141 skgpu::tess::PathTessellator* fTessellator = nullptr;
Chris Daltona9f759d2021-05-18 12:37:08 -0600142 GrProgramInfo* fProgram;
143 GrProcessorSet fProcessors{SkBlendMode::kSrcOver};
144
145 friend class GrOp; // For ctor.
146};
147
148} // namespace
149
150// This sample enables wireframe and visualizes the triangles generated by path tessellators.
151class SamplePathTessellators : public Sample {
152public:
153 SamplePathTessellators() {
154#if 0
155 // For viewing middle-out triangulations of the inner fan.
156 fPath.moveTo(1, 0);
157 int numSides = 32 * 3;
158 for (int i = 1; i < numSides; ++i) {
159 float theta = 2*3.1415926535897932384626433832785 * i / numSides;
160 fPath.lineTo(std::cos(theta), std::sin(theta));
161 }
162 fPath.transform(SkMatrix::Scale(200, 200));
163 fPath.transform(SkMatrix::Translate(300, 300));
164#else
165 fPath.moveTo(100, 500);
166 fPath.cubicTo(300, 400, -100, 300, 100, 200);
167 fPath.quadTo(250, 0, 400, 200);
168 fPath.conicTo(600, 350, 400, 500, fConicWeight);
169 fPath.close();
170#endif
171 }
172
173private:
174 void onDrawContent(SkCanvas*) override;
175 Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override;
176 bool onClick(Sample::Click*) override;
177 bool onChar(SkUnichar) override;
178
179 SkString name() override { return SkString("PathTessellators"); }
180
181 SkPath fPath;
Chris Daltoneb0195e2021-08-18 21:39:02 -0600182 GrPipeline::InputFlags fPipelineFlags = GrPipeline::InputFlags::kWireframe;
Chris Daltond2b8ba32021-06-09 00:12:59 -0600183 Mode fMode = Mode::kWedgeMiddleOut;
Chris Daltona9f759d2021-05-18 12:37:08 -0600184
185 float fConicWeight = .5;
186
187 class Click;
188};
189
190void SamplePathTessellators::onDrawContent(SkCanvas* canvas) {
191 canvas->clear(SK_ColorBLACK);
192
193 auto ctx = canvas->recordingContext();
Robert Phillips4dca8312021-07-28 15:13:20 -0400194 auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas);
Chris Daltona9f759d2021-05-18 12:37:08 -0600195
196 SkString error;
197 if (!sdc || !ctx) {
198 error = "GPU Only.";
Robert Phillips24d622d2021-08-19 17:04:05 -0400199 } else if (!skgpu::v1::TessellationPathRenderer::IsSupported(*ctx->priv().caps())) {
200 error = "TessellationPathRenderer not supported.";
Chris Daltona9f759d2021-05-18 12:37:08 -0600201 } else if (fMode >= Mode::kWedgeTessellate &&
202 !ctx->priv().caps()->shaderCaps()->tessellationSupport()) {
203 error.printf("%s requires hardware tessellation support.", ModeName(fMode));
204 }
205 if (!error.isEmpty()) {
206 canvas->clear(SK_ColorRED);
207 SkFont font(nullptr, 20);
208 SkPaint captionPaint;
209 captionPaint.setColor(SK_ColorWHITE);
210 canvas->drawString(error.c_str(), 10, 30, font, captionPaint);
211 return;
212 }
213
214 sdc->addDrawOp(GrOp::Make<SamplePathTessellatorOp>(ctx,
215 sdc->asRenderTargetProxy()->getBoundsRect(),
216 fPath, canvas->getTotalMatrix(),
217 fPipelineFlags, fMode));
218
219 // Draw the path points.
220 SkPaint pointsPaint;
221 pointsPaint.setColor(SK_ColorBLUE);
222 pointsPaint.setStrokeWidth(8);
223 SkPath devPath = fPath;
224 devPath.transform(canvas->getTotalMatrix());
225 {
226 SkAutoCanvasRestore acr(canvas, true);
227 canvas->setMatrix(SkMatrix::I());
228 SkString caption(ModeName(fMode));
229 caption.appendf(" (w=%g)", fConicWeight);
230 SkFont font(nullptr, 20);
231 SkPaint captionPaint;
232 captionPaint.setColor(SK_ColorWHITE);
233 canvas->drawString(caption, 10, 30, font, captionPaint);
234 canvas->drawPoints(SkCanvas::kPoints_PointMode, devPath.countPoints(),
235 SkPathPriv::PointData(devPath), pointsPaint);
236 }
237}
238
239class SamplePathTessellators::Click : public Sample::Click {
240public:
241 Click(int ptIdx) : fPtIdx(ptIdx) {}
242
243 void doClick(SkPath* path) {
244 SkPoint pt = path->getPoint(fPtIdx);
245 SkPathPriv::UpdatePathPoint(path, fPtIdx, pt + fCurr - fPrev);
246 }
247
248private:
249 int fPtIdx;
250};
251
252Sample::Click* SamplePathTessellators::onFindClickHandler(SkScalar x, SkScalar y,
253 skui::ModifierKey) {
254 const SkPoint* pts = SkPathPriv::PointData(fPath);
255 float fuzz = 30;
256 for (int i = 0; i < fPath.countPoints(); ++i) {
257 if (fabs(x - pts[i].x()) < fuzz && fabsf(y - pts[i].y()) < fuzz) {
258 return new Click(i);
259 }
260 }
261 return nullptr;
262}
263
264bool SamplePathTessellators::onClick(Sample::Click* click) {
265 Click* myClick = (Click*)click;
266 myClick->doClick(&fPath);
267 return true;
268}
269
270static SkPath update_weight(const SkPath& path, float w) {
271 SkPath path_;
272 for (auto [verb, pts, _] : SkPathPriv::Iterate(path)) {
273 switch (verb) {
274 case SkPathVerb::kMove:
275 path_.moveTo(pts[0]);
276 break;
277 case SkPathVerb::kLine:
278 path_.lineTo(pts[1]);
279 break;
280 case SkPathVerb::kQuad:
281 path_.quadTo(pts[1], pts[2]);
282 break;
283 case SkPathVerb::kCubic:
284 path_.cubicTo(pts[1], pts[2], pts[3]);
285 break;
286 case SkPathVerb::kConic:
287 path_.conicTo(pts[1], pts[2], (w != 1) ? w : .99f);
288 break;
289 case SkPathVerb::kClose:
290 break;
291 }
292 }
293 return path_;
294}
295
296bool SamplePathTessellators::onChar(SkUnichar unichar) {
297 switch (unichar) {
298 case 'w':
299 fPipelineFlags = (GrPipeline::InputFlags)(
300 (int)fPipelineFlags ^ (int)GrPipeline::InputFlags::kWireframe);
301 return true;
302 case 'D': {
303 fPath.dump();
304 return true;
305 }
306 case '+':
307 fConicWeight *= 2;
308 fPath = update_weight(fPath, fConicWeight);
309 return true;
310 case '=':
311 fConicWeight *= 5/4.f;
312 fPath = update_weight(fPath, fConicWeight);
313 return true;
314 case '_':
315 fConicWeight *= .5f;
316 fPath = update_weight(fPath, fConicWeight);
317 return true;
318 case '-':
319 fConicWeight *= 4/5.f;
320 fPath = update_weight(fPath, fConicWeight);
321 return true;
322 case '1':
323 case '2':
324 case '3':
Chris Daltond2b8ba32021-06-09 00:12:59 -0600325 case '4':
Chris Daltona9f759d2021-05-18 12:37:08 -0600326 fMode = (Mode)(unichar - '1');
327 return true;
328 }
329 return false;
330}
331
332Sample* MakeTessellatedPathSample() { return new SamplePathTessellators; }
333static SampleRegistry gTessellatedPathSample(MakeTessellatedPathSample);
334
335#endif // SK_SUPPORT_GPU