blob: 5379f71fae089a6c3e6b11170513cb5e2e20aa8f [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"
Chris Daltond9bdc322021-06-01 19:22:05 -060019#include "src/gpu/tessellate/GrPathCurveTessellator.h"
Chris Daltond9bdc322021-06-01 19:22:05 -060020#include "src/gpu/tessellate/GrPathWedgeTessellator.h"
Chris Dalton3b412782021-06-01 13:40:03 -060021#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
Robert Phillips4dca8312021-07-28 15:13:20 -040022#include "src/gpu/v1/SurfaceDrawContext_v1.h"
Chris Daltona9f759d2021-05-18 12:37:08 -060023
24namespace {
25
26enum class Mode {
Chris Daltond2b8ba32021-06-09 00:12:59 -060027 kWedgeMiddleOut,
Chris Daltona9f759d2021-05-18 12:37:08 -060028 kCurveMiddleOut,
29 kWedgeTessellate,
30 kCurveTessellate
31};
32
33static const char* ModeName(Mode mode) {
34 switch (mode) {
Chris Daltond2b8ba32021-06-09 00:12:59 -060035 case Mode::kWedgeMiddleOut:
36 return "MiddleOutShader (kWedges)";
Chris Daltona9f759d2021-05-18 12:37:08 -060037 case Mode::kCurveMiddleOut:
Chris Daltond2b8ba32021-06-09 00:12:59 -060038 return "MiddleOutShader (kCurves)";
Chris Daltona9f759d2021-05-18 12:37:08 -060039 case Mode::kWedgeTessellate:
Chris Daltond2b8ba32021-06-09 00:12:59 -060040 return "HardwareWedgeShader";
Chris Daltona9f759d2021-05-18 12:37:08 -060041 case Mode::kCurveTessellate:
Chris Daltond2b8ba32021-06-09 00:12:59 -060042 return "HardwareCurveShader";
Chris Daltona9f759d2021-05-18 12:37:08 -060043 }
44 SkUNREACHABLE;
45}
46
47// Draws a path directly to the screen using a specific tessellator.
48class SamplePathTessellatorOp : public GrDrawOp {
49private:
50 DEFINE_OP_CLASS_ID
51
52 SamplePathTessellatorOp(const SkRect& drawBounds, const SkPath& path, const SkMatrix& m,
53 GrPipeline::InputFlags pipelineFlags, Mode mode)
54 : GrDrawOp(ClassID())
55 , fPath(path)
56 , fMatrix(m)
57 , fPipelineFlags(pipelineFlags)
58 , fMode(mode) {
59 this->setBounds(drawBounds, HasAABloat::kNo, IsHairline::kNo);
60 }
61 const char* name() const override { return "SamplePathTessellatorOp"; }
Robert Phillips294723d2021-06-17 09:23:58 -040062 void visitProxies(const GrVisitProxyFunc&) const override {}
Chris Daltona9f759d2021-05-18 12:37:08 -060063 FixedFunctionFlags fixedFunctionFlags() const override {
64 return FixedFunctionFlags::kUsesHWAA;
65 }
66 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
67 GrClampType clampType) override {
68 SkPMColor4f color;
69 return fProcessors.finalize(SK_PMColor4fWHITE, GrProcessorAnalysisCoverage::kNone, clip,
70 nullptr, caps, clampType, &color);
71 }
72 void onPrePrepare(GrRecordingContext*, const GrSurfaceProxyView&, GrAppliedClip*,
John Stiles52cb1d02021-06-02 11:58:05 -040073 const GrDstProxyView&, GrXferBarrierFlags, GrLoadOp colorLoadOp) override {}
Chris Daltona9f759d2021-05-18 12:37:08 -060074 void onPrepare(GrOpFlushState* flushState) override {
Chris Dalton2f733ec2021-06-01 12:11:57 -060075 constexpr static SkPMColor4f kCyan = {0,1,1,1};
Chris Daltona9f759d2021-05-18 12:37:08 -060076 auto alloc = flushState->allocator();
Chris Dalton69669812021-07-27 10:00:12 -060077 const SkMatrix& shaderMatrix = SkMatrix::I();
78 const SkMatrix& pathMatrix = fMatrix;
Chris Daltond2b8ba32021-06-09 00:12:59 -060079 const GrCaps& caps = flushState->caps();
80 int numVerbsToGetMiddleOut = 0;
81 int numVerbsToGetTessellation = caps.minPathVerbsForHwTessellation();
Chris Dalton198ac152021-06-09 13:49:43 -060082 auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(flushState, std::move(fProcessors),
83 fPipelineFlags);
Chris Daltona9f759d2021-05-18 12:37:08 -060084 switch (fMode) {
Chris Daltond2b8ba32021-06-09 00:12:59 -060085 using DrawInnerFan = GrPathCurveTessellator::DrawInnerFan;
86 case Mode::kWedgeMiddleOut:
Chris Dalton69669812021-07-27 10:00:12 -060087 fTessellator = GrPathWedgeTessellator::Make(alloc, shaderMatrix, kCyan,
Chris Dalton198ac152021-06-09 13:49:43 -060088 numVerbsToGetMiddleOut, *pipeline,
89 caps);
Chris Daltond2b8ba32021-06-09 00:12:59 -060090 break;
Chris Daltona9f759d2021-05-18 12:37:08 -060091 case Mode::kCurveMiddleOut:
Chris Dalton69669812021-07-27 10:00:12 -060092 fTessellator = GrPathCurveTessellator::Make(alloc, shaderMatrix, kCyan,
Chris Dalton26666bd2021-06-08 16:25:46 -060093 DrawInnerFan::kYes,
Chris Dalton198ac152021-06-09 13:49:43 -060094 numVerbsToGetMiddleOut, *pipeline,
95 caps);
Chris Daltona9f759d2021-05-18 12:37:08 -060096 break;
97 case Mode::kWedgeTessellate:
Chris Dalton69669812021-07-27 10:00:12 -060098 fTessellator = GrPathWedgeTessellator::Make(alloc, shaderMatrix, kCyan,
Chris Dalton198ac152021-06-09 13:49:43 -060099 numVerbsToGetTessellation, *pipeline,
100 caps);
Chris Daltona9f759d2021-05-18 12:37:08 -0600101 break;
102 case Mode::kCurveTessellate:
Chris Dalton69669812021-07-27 10:00:12 -0600103 fTessellator = GrPathCurveTessellator::Make(alloc, shaderMatrix, kCyan,
Chris Dalton26666bd2021-06-08 16:25:46 -0600104 DrawInnerFan::kYes,
Chris Dalton198ac152021-06-09 13:49:43 -0600105 numVerbsToGetTessellation, *pipeline,
106 caps);
Chris Daltona9f759d2021-05-18 12:37:08 -0600107 break;
108 }
Chris Dalton17eaf622021-07-28 09:43:52 -0600109 fTessellator->prepare(flushState, this->bounds(), {pathMatrix, fPath}, fPath.countVerbs());
Chris Dalton2f733ec2021-06-01 12:11:57 -0600110 fProgram = GrTessellationShader::MakeProgram({alloc, flushState->writeView(),
111 &flushState->dstProxyView(),
112 flushState->renderPassBarriers(),
113 GrLoadOp::kClear, &flushState->caps()},
114 fTessellator->shader(), pipeline,
115 &GrUserStencilSettings::kUnused);
Chris Daltona9f759d2021-05-18 12:37:08 -0600116 }
117 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
118 flushState->bindPipeline(*fProgram, chainBounds);
119 fTessellator->draw(flushState);
120 }
121
122 const SkPath fPath;
123 const SkMatrix fMatrix;
124 const GrPipeline::InputFlags fPipelineFlags;
125 const Mode fMode;
126 GrPathTessellator* fTessellator = nullptr;
127 GrProgramInfo* fProgram;
128 GrProcessorSet fProcessors{SkBlendMode::kSrcOver};
129
130 friend class GrOp; // For ctor.
131};
132
133} // namespace
134
135// This sample enables wireframe and visualizes the triangles generated by path tessellators.
136class SamplePathTessellators : public Sample {
137public:
138 SamplePathTessellators() {
139#if 0
140 // For viewing middle-out triangulations of the inner fan.
141 fPath.moveTo(1, 0);
142 int numSides = 32 * 3;
143 for (int i = 1; i < numSides; ++i) {
144 float theta = 2*3.1415926535897932384626433832785 * i / numSides;
145 fPath.lineTo(std::cos(theta), std::sin(theta));
146 }
147 fPath.transform(SkMatrix::Scale(200, 200));
148 fPath.transform(SkMatrix::Translate(300, 300));
149#else
150 fPath.moveTo(100, 500);
151 fPath.cubicTo(300, 400, -100, 300, 100, 200);
152 fPath.quadTo(250, 0, 400, 200);
153 fPath.conicTo(600, 350, 400, 500, fConicWeight);
154 fPath.close();
155#endif
156 }
157
158private:
159 void onDrawContent(SkCanvas*) override;
160 Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override;
161 bool onClick(Sample::Click*) override;
162 bool onChar(SkUnichar) override;
163
164 SkString name() override { return SkString("PathTessellators"); }
165
166 SkPath fPath;
167 GrPipeline::InputFlags fPipelineFlags = GrPipeline::InputFlags::kHWAntialias |
168 GrPipeline::InputFlags::kWireframe;
Chris Daltond2b8ba32021-06-09 00:12:59 -0600169 Mode fMode = Mode::kWedgeMiddleOut;
Chris Daltona9f759d2021-05-18 12:37:08 -0600170
171 float fConicWeight = .5;
172
173 class Click;
174};
175
176void SamplePathTessellators::onDrawContent(SkCanvas* canvas) {
177 canvas->clear(SK_ColorBLACK);
178
179 auto ctx = canvas->recordingContext();
Robert Phillips4dca8312021-07-28 15:13:20 -0400180 auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas);
Chris Daltona9f759d2021-05-18 12:37:08 -0600181
182 SkString error;
183 if (!sdc || !ctx) {
184 error = "GPU Only.";
185 } else if (!GrTessellationPathRenderer::IsSupported(*ctx->priv().caps())) {
186 error = "GrTessellationPathRenderer not supported.";
187 } else if (fMode >= Mode::kWedgeTessellate &&
188 !ctx->priv().caps()->shaderCaps()->tessellationSupport()) {
189 error.printf("%s requires hardware tessellation support.", ModeName(fMode));
190 }
191 if (!error.isEmpty()) {
192 canvas->clear(SK_ColorRED);
193 SkFont font(nullptr, 20);
194 SkPaint captionPaint;
195 captionPaint.setColor(SK_ColorWHITE);
196 canvas->drawString(error.c_str(), 10, 30, font, captionPaint);
197 return;
198 }
199
200 sdc->addDrawOp(GrOp::Make<SamplePathTessellatorOp>(ctx,
201 sdc->asRenderTargetProxy()->getBoundsRect(),
202 fPath, canvas->getTotalMatrix(),
203 fPipelineFlags, fMode));
204
205 // Draw the path points.
206 SkPaint pointsPaint;
207 pointsPaint.setColor(SK_ColorBLUE);
208 pointsPaint.setStrokeWidth(8);
209 SkPath devPath = fPath;
210 devPath.transform(canvas->getTotalMatrix());
211 {
212 SkAutoCanvasRestore acr(canvas, true);
213 canvas->setMatrix(SkMatrix::I());
214 SkString caption(ModeName(fMode));
215 caption.appendf(" (w=%g)", fConicWeight);
216 SkFont font(nullptr, 20);
217 SkPaint captionPaint;
218 captionPaint.setColor(SK_ColorWHITE);
219 canvas->drawString(caption, 10, 30, font, captionPaint);
220 canvas->drawPoints(SkCanvas::kPoints_PointMode, devPath.countPoints(),
221 SkPathPriv::PointData(devPath), pointsPaint);
222 }
223}
224
225class SamplePathTessellators::Click : public Sample::Click {
226public:
227 Click(int ptIdx) : fPtIdx(ptIdx) {}
228
229 void doClick(SkPath* path) {
230 SkPoint pt = path->getPoint(fPtIdx);
231 SkPathPriv::UpdatePathPoint(path, fPtIdx, pt + fCurr - fPrev);
232 }
233
234private:
235 int fPtIdx;
236};
237
238Sample::Click* SamplePathTessellators::onFindClickHandler(SkScalar x, SkScalar y,
239 skui::ModifierKey) {
240 const SkPoint* pts = SkPathPriv::PointData(fPath);
241 float fuzz = 30;
242 for (int i = 0; i < fPath.countPoints(); ++i) {
243 if (fabs(x - pts[i].x()) < fuzz && fabsf(y - pts[i].y()) < fuzz) {
244 return new Click(i);
245 }
246 }
247 return nullptr;
248}
249
250bool SamplePathTessellators::onClick(Sample::Click* click) {
251 Click* myClick = (Click*)click;
252 myClick->doClick(&fPath);
253 return true;
254}
255
256static SkPath update_weight(const SkPath& path, float w) {
257 SkPath path_;
258 for (auto [verb, pts, _] : SkPathPriv::Iterate(path)) {
259 switch (verb) {
260 case SkPathVerb::kMove:
261 path_.moveTo(pts[0]);
262 break;
263 case SkPathVerb::kLine:
264 path_.lineTo(pts[1]);
265 break;
266 case SkPathVerb::kQuad:
267 path_.quadTo(pts[1], pts[2]);
268 break;
269 case SkPathVerb::kCubic:
270 path_.cubicTo(pts[1], pts[2], pts[3]);
271 break;
272 case SkPathVerb::kConic:
273 path_.conicTo(pts[1], pts[2], (w != 1) ? w : .99f);
274 break;
275 case SkPathVerb::kClose:
276 break;
277 }
278 }
279 return path_;
280}
281
282bool SamplePathTessellators::onChar(SkUnichar unichar) {
283 switch (unichar) {
284 case 'w':
285 fPipelineFlags = (GrPipeline::InputFlags)(
286 (int)fPipelineFlags ^ (int)GrPipeline::InputFlags::kWireframe);
287 return true;
288 case 'D': {
289 fPath.dump();
290 return true;
291 }
292 case '+':
293 fConicWeight *= 2;
294 fPath = update_weight(fPath, fConicWeight);
295 return true;
296 case '=':
297 fConicWeight *= 5/4.f;
298 fPath = update_weight(fPath, fConicWeight);
299 return true;
300 case '_':
301 fConicWeight *= .5f;
302 fPath = update_weight(fPath, fConicWeight);
303 return true;
304 case '-':
305 fConicWeight *= 4/5.f;
306 fPath = update_weight(fPath, fConicWeight);
307 return true;
308 case '1':
309 case '2':
310 case '3':
Chris Daltond2b8ba32021-06-09 00:12:59 -0600311 case '4':
Chris Daltona9f759d2021-05-18 12:37:08 -0600312 fMode = (Mode)(unichar - '1');
313 return true;
314 }
315 return false;
316}
317
318Sample* MakeTessellatedPathSample() { return new SamplePathTessellators; }
319static SampleRegistry gTessellatedPathSample(MakeTessellatedPathSample);
320
321#endif // SK_SUPPORT_GPU