blob: bba84f108976833034faffaa614b8d1fc2f19e9c [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
Chris Dalton49c76672021-10-20 09:45:03 -060025namespace skgpu {
26
Chris Daltona9f759d2021-05-18 12:37:08 -060027namespace {
28
29enum class Mode {
Chris Daltond2b8ba32021-06-09 00:12:59 -060030 kWedgeMiddleOut,
Chris Daltona9f759d2021-05-18 12:37:08 -060031 kCurveMiddleOut,
32 kWedgeTessellate,
33 kCurveTessellate
34};
35
36static const char* ModeName(Mode mode) {
37 switch (mode) {
Chris Daltond2b8ba32021-06-09 00:12:59 -060038 case Mode::kWedgeMiddleOut:
39 return "MiddleOutShader (kWedges)";
Chris Daltona9f759d2021-05-18 12:37:08 -060040 case Mode::kCurveMiddleOut:
Chris Daltond2b8ba32021-06-09 00:12:59 -060041 return "MiddleOutShader (kCurves)";
Chris Daltona9f759d2021-05-18 12:37:08 -060042 case Mode::kWedgeTessellate:
Chris Daltond2b8ba32021-06-09 00:12:59 -060043 return "HardwareWedgeShader";
Chris Daltona9f759d2021-05-18 12:37:08 -060044 case Mode::kCurveTessellate:
Chris Daltond2b8ba32021-06-09 00:12:59 -060045 return "HardwareCurveShader";
Chris Daltona9f759d2021-05-18 12:37:08 -060046 }
47 SkUNREACHABLE;
48}
49
50// Draws a path directly to the screen using a specific tessellator.
51class SamplePathTessellatorOp : public GrDrawOp {
52private:
53 DEFINE_OP_CLASS_ID
54
55 SamplePathTessellatorOp(const SkRect& drawBounds, const SkPath& path, const SkMatrix& m,
56 GrPipeline::InputFlags pipelineFlags, Mode mode)
57 : GrDrawOp(ClassID())
58 , fPath(path)
59 , fMatrix(m)
60 , fPipelineFlags(pipelineFlags)
61 , fMode(mode) {
62 this->setBounds(drawBounds, HasAABloat::kNo, IsHairline::kNo);
63 }
64 const char* name() const override { return "SamplePathTessellatorOp"; }
Robert Phillips294723d2021-06-17 09:23:58 -040065 void visitProxies(const GrVisitProxyFunc&) const override {}
Chris Daltona9f759d2021-05-18 12:37:08 -060066 FixedFunctionFlags fixedFunctionFlags() const override {
67 return FixedFunctionFlags::kUsesHWAA;
68 }
69 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
70 GrClampType clampType) override {
71 SkPMColor4f color;
72 return fProcessors.finalize(SK_PMColor4fWHITE, GrProcessorAnalysisCoverage::kNone, clip,
73 nullptr, caps, clampType, &color);
74 }
75 void onPrePrepare(GrRecordingContext*, const GrSurfaceProxyView&, GrAppliedClip*,
John Stiles52cb1d02021-06-02 11:58:05 -040076 const GrDstProxyView&, GrXferBarrierFlags, GrLoadOp colorLoadOp) override {}
Chris Daltona9f759d2021-05-18 12:37:08 -060077 void onPrepare(GrOpFlushState* flushState) override {
Chris Dalton2f733ec2021-06-01 12:11:57 -060078 constexpr static SkPMColor4f kCyan = {0,1,1,1};
Chris Daltona9f759d2021-05-18 12:37:08 -060079 auto alloc = flushState->allocator();
Chris Dalton69669812021-07-27 10:00:12 -060080 const SkMatrix& shaderMatrix = SkMatrix::I();
81 const SkMatrix& pathMatrix = fMatrix;
Chris Daltond2b8ba32021-06-09 00:12:59 -060082 const GrCaps& caps = flushState->caps();
Chris Daltonf0f447c2021-11-10 17:39:56 -070083 const GrShaderCaps& shaderCaps = *caps.shaderCaps();
Chris Daltond2b8ba32021-06-09 00:12:59 -060084 int numVerbsToGetMiddleOut = 0;
85 int numVerbsToGetTessellation = caps.minPathVerbsForHwTessellation();
Chris Dalton198ac152021-06-09 13:49:43 -060086 auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(flushState, std::move(fProcessors),
87 fPipelineFlags);
Chris Daltonf0f447c2021-11-10 17:39:56 -070088 int numVerbs;
Chris Daltona9f759d2021-05-18 12:37:08 -060089 switch (fMode) {
Chris Dalton49c76672021-10-20 09:45:03 -060090 using DrawInnerFan = PathCurveTessellator::DrawInnerFan;
Chris Daltond2b8ba32021-06-09 00:12:59 -060091 case Mode::kWedgeMiddleOut:
Chris Daltonf0f447c2021-11-10 17:39:56 -070092 fTessellator = PathWedgeTessellator::Make(alloc, shaderCaps.infinitySupport());
93 numVerbs = numVerbsToGetMiddleOut;
Chris Daltond2b8ba32021-06-09 00:12:59 -060094 break;
Chris Daltona9f759d2021-05-18 12:37:08 -060095 case Mode::kCurveMiddleOut:
Chris Dalton49c76672021-10-20 09:45:03 -060096 fTessellator = PathCurveTessellator::Make(alloc,
Chris Dalton49c76672021-10-20 09:45:03 -060097 DrawInnerFan::kYes,
Chris Daltonf0f447c2021-11-10 17:39:56 -070098 shaderCaps.infinitySupport());
99 numVerbs = numVerbsToGetMiddleOut;
Chris Daltona9f759d2021-05-18 12:37:08 -0600100 break;
101 case Mode::kWedgeTessellate:
Chris Daltonf0f447c2021-11-10 17:39:56 -0700102 fTessellator = PathWedgeTessellator::Make(alloc, shaderCaps.infinitySupport());
103 numVerbs = numVerbsToGetTessellation;
Chris Daltona9f759d2021-05-18 12:37:08 -0600104 break;
105 case Mode::kCurveTessellate:
Chris Dalton49c76672021-10-20 09:45:03 -0600106 fTessellator = PathCurveTessellator::Make(alloc,
Chris Dalton49c76672021-10-20 09:45:03 -0600107 DrawInnerFan::kYes,
Chris Daltonf0f447c2021-11-10 17:39:56 -0700108 shaderCaps.infinitySupport());
109 numVerbs = numVerbsToGetTessellation;
Chris Daltona9f759d2021-05-18 12:37:08 -0600110 break;
111 }
Chris Daltonf0f447c2021-11-10 17:39:56 -0700112 auto* tessShader = GrPathTessellationShader::Make(alloc,
113 shaderMatrix,
114 kCyan,
115 numVerbs,
116 *pipeline,
117 fTessellator->patchAttribs(),
118 caps);
119 fTessellator->prepare(flushState,
120 tessShader->maxTessellationSegments(*caps.shaderCaps()),
121 shaderMatrix,
122 {pathMatrix, fPath, kCyan},
123 fPath.countVerbs());
124 if (!tessShader->willUseTessellationShaders()) {
125 fTessellator->prepareFixedCountBuffers(flushState->resourceProvider());
126 }
Chris Dalton2f733ec2021-06-01 12:11:57 -0600127 fProgram = GrTessellationShader::MakeProgram({alloc, flushState->writeView(),
Chris Dalton2a26c502021-08-26 10:05:11 -0600128 flushState->usesMSAASurface(),
Chris Dalton2f733ec2021-06-01 12:11:57 -0600129 &flushState->dstProxyView(),
130 flushState->renderPassBarriers(),
131 GrLoadOp::kClear, &flushState->caps()},
Chris Daltonf0f447c2021-11-10 17:39:56 -0700132 tessShader,
133 pipeline,
Chris Dalton2f733ec2021-06-01 12:11:57 -0600134 &GrUserStencilSettings::kUnused);
Chris Daltona9f759d2021-05-18 12:37:08 -0600135 }
136 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
137 flushState->bindPipeline(*fProgram, chainBounds);
Chris Daltonf0f447c2021-11-10 17:39:56 -0700138 fTessellator->draw(flushState, fProgram->geomProc().willUseTessellationShaders());
Chris Daltona9f759d2021-05-18 12:37:08 -0600139 }
140
141 const SkPath fPath;
142 const SkMatrix fMatrix;
143 const GrPipeline::InputFlags fPipelineFlags;
144 const Mode fMode;
Chris Dalton49c76672021-10-20 09:45:03 -0600145 PathTessellator* fTessellator = nullptr;
Chris Daltona9f759d2021-05-18 12:37:08 -0600146 GrProgramInfo* fProgram;
147 GrProcessorSet fProcessors{SkBlendMode::kSrcOver};
148
149 friend class GrOp; // For ctor.
150};
151
152} // namespace
153
154// This sample enables wireframe and visualizes the triangles generated by path tessellators.
155class SamplePathTessellators : public Sample {
156public:
157 SamplePathTessellators() {
158#if 0
159 // For viewing middle-out triangulations of the inner fan.
160 fPath.moveTo(1, 0);
161 int numSides = 32 * 3;
162 for (int i = 1; i < numSides; ++i) {
163 float theta = 2*3.1415926535897932384626433832785 * i / numSides;
164 fPath.lineTo(std::cos(theta), std::sin(theta));
165 }
166 fPath.transform(SkMatrix::Scale(200, 200));
167 fPath.transform(SkMatrix::Translate(300, 300));
168#else
169 fPath.moveTo(100, 500);
170 fPath.cubicTo(300, 400, -100, 300, 100, 200);
171 fPath.quadTo(250, 0, 400, 200);
172 fPath.conicTo(600, 350, 400, 500, fConicWeight);
173 fPath.close();
174#endif
175 }
176
177private:
178 void onDrawContent(SkCanvas*) override;
179 Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override;
180 bool onClick(Sample::Click*) override;
181 bool onChar(SkUnichar) override;
182
183 SkString name() override { return SkString("PathTessellators"); }
184
185 SkPath fPath;
Chris Daltoneb0195e2021-08-18 21:39:02 -0600186 GrPipeline::InputFlags fPipelineFlags = GrPipeline::InputFlags::kWireframe;
Chris Daltond2b8ba32021-06-09 00:12:59 -0600187 Mode fMode = Mode::kWedgeMiddleOut;
Chris Daltona9f759d2021-05-18 12:37:08 -0600188
189 float fConicWeight = .5;
190
191 class Click;
192};
193
194void SamplePathTessellators::onDrawContent(SkCanvas* canvas) {
195 canvas->clear(SK_ColorBLACK);
196
197 auto ctx = canvas->recordingContext();
Robert Phillips4dca8312021-07-28 15:13:20 -0400198 auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas);
Chris Daltona9f759d2021-05-18 12:37:08 -0600199
200 SkString error;
201 if (!sdc || !ctx) {
202 error = "GPU Only.";
Robert Phillips24d622d2021-08-19 17:04:05 -0400203 } else if (!skgpu::v1::TessellationPathRenderer::IsSupported(*ctx->priv().caps())) {
204 error = "TessellationPathRenderer not supported.";
Chris Daltona9f759d2021-05-18 12:37:08 -0600205 } else if (fMode >= Mode::kWedgeTessellate &&
206 !ctx->priv().caps()->shaderCaps()->tessellationSupport()) {
207 error.printf("%s requires hardware tessellation support.", ModeName(fMode));
208 }
209 if (!error.isEmpty()) {
210 canvas->clear(SK_ColorRED);
211 SkFont font(nullptr, 20);
212 SkPaint captionPaint;
213 captionPaint.setColor(SK_ColorWHITE);
214 canvas->drawString(error.c_str(), 10, 30, font, captionPaint);
215 return;
216 }
217
218 sdc->addDrawOp(GrOp::Make<SamplePathTessellatorOp>(ctx,
219 sdc->asRenderTargetProxy()->getBoundsRect(),
220 fPath, canvas->getTotalMatrix(),
221 fPipelineFlags, fMode));
222
223 // Draw the path points.
224 SkPaint pointsPaint;
225 pointsPaint.setColor(SK_ColorBLUE);
226 pointsPaint.setStrokeWidth(8);
227 SkPath devPath = fPath;
228 devPath.transform(canvas->getTotalMatrix());
229 {
230 SkAutoCanvasRestore acr(canvas, true);
231 canvas->setMatrix(SkMatrix::I());
232 SkString caption(ModeName(fMode));
233 caption.appendf(" (w=%g)", fConicWeight);
234 SkFont font(nullptr, 20);
235 SkPaint captionPaint;
236 captionPaint.setColor(SK_ColorWHITE);
237 canvas->drawString(caption, 10, 30, font, captionPaint);
238 canvas->drawPoints(SkCanvas::kPoints_PointMode, devPath.countPoints(),
239 SkPathPriv::PointData(devPath), pointsPaint);
240 }
241}
242
243class SamplePathTessellators::Click : public Sample::Click {
244public:
245 Click(int ptIdx) : fPtIdx(ptIdx) {}
246
247 void doClick(SkPath* path) {
248 SkPoint pt = path->getPoint(fPtIdx);
249 SkPathPriv::UpdatePathPoint(path, fPtIdx, pt + fCurr - fPrev);
250 }
251
252private:
253 int fPtIdx;
254};
255
256Sample::Click* SamplePathTessellators::onFindClickHandler(SkScalar x, SkScalar y,
257 skui::ModifierKey) {
258 const SkPoint* pts = SkPathPriv::PointData(fPath);
259 float fuzz = 30;
260 for (int i = 0; i < fPath.countPoints(); ++i) {
261 if (fabs(x - pts[i].x()) < fuzz && fabsf(y - pts[i].y()) < fuzz) {
262 return new Click(i);
263 }
264 }
265 return nullptr;
266}
267
268bool SamplePathTessellators::onClick(Sample::Click* click) {
269 Click* myClick = (Click*)click;
270 myClick->doClick(&fPath);
271 return true;
272}
273
274static SkPath update_weight(const SkPath& path, float w) {
275 SkPath path_;
276 for (auto [verb, pts, _] : SkPathPriv::Iterate(path)) {
277 switch (verb) {
278 case SkPathVerb::kMove:
279 path_.moveTo(pts[0]);
280 break;
281 case SkPathVerb::kLine:
282 path_.lineTo(pts[1]);
283 break;
284 case SkPathVerb::kQuad:
285 path_.quadTo(pts[1], pts[2]);
286 break;
287 case SkPathVerb::kCubic:
288 path_.cubicTo(pts[1], pts[2], pts[3]);
289 break;
290 case SkPathVerb::kConic:
291 path_.conicTo(pts[1], pts[2], (w != 1) ? w : .99f);
292 break;
293 case SkPathVerb::kClose:
294 break;
295 }
296 }
297 return path_;
298}
299
300bool SamplePathTessellators::onChar(SkUnichar unichar) {
301 switch (unichar) {
302 case 'w':
303 fPipelineFlags = (GrPipeline::InputFlags)(
304 (int)fPipelineFlags ^ (int)GrPipeline::InputFlags::kWireframe);
305 return true;
306 case 'D': {
307 fPath.dump();
308 return true;
309 }
310 case '+':
311 fConicWeight *= 2;
312 fPath = update_weight(fPath, fConicWeight);
313 return true;
314 case '=':
315 fConicWeight *= 5/4.f;
316 fPath = update_weight(fPath, fConicWeight);
317 return true;
318 case '_':
319 fConicWeight *= .5f;
320 fPath = update_weight(fPath, fConicWeight);
321 return true;
322 case '-':
323 fConicWeight *= 4/5.f;
324 fPath = update_weight(fPath, fConicWeight);
325 return true;
326 case '1':
327 case '2':
328 case '3':
Chris Daltond2b8ba32021-06-09 00:12:59 -0600329 case '4':
Chris Daltona9f759d2021-05-18 12:37:08 -0600330 fMode = (Mode)(unichar - '1');
331 return true;
332 }
333 return false;
334}
335
336Sample* MakeTessellatedPathSample() { return new SamplePathTessellators; }
337static SampleRegistry gTessellatedPathSample(MakeTessellatedPathSample);
338
Chris Dalton49c76672021-10-20 09:45:03 -0600339} // namespace skgpu
340
Chris Daltona9f759d2021-05-18 12:37:08 -0600341#endif // SK_SUPPORT_GPU