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