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