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