2 * Copyright 2019 Google LLC.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "include/core/SkCanvas.h"
9 #include "samplecode/Sample.h"
10 #include "src/core/SkPathPriv.h"
14 #include "src/core/SkCanvasPriv.h"
15 #include "src/gpu/ganesh/GrOpFlushState.h"
16 #include "src/gpu/ganesh/GrRecordingContextPriv.h"
17 #include "src/gpu/ganesh/ops/GrDrawOp.h"
18 #include "src/gpu/ganesh/ops/GrSimpleMeshDrawOpHelper.h"
19 #include "src/gpu/ganesh/ops/TessellationPathRenderer.h"
20 #include "src/gpu/ganesh/tessellate/GrPathTessellationShader.h"
21 #include "src/gpu/ganesh/tessellate/PathTessellator.h"
22 #include "src/gpu/ganesh/v1/SurfaceDrawContext_v1.h"
23 #include "src/gpu/tessellate/AffineMatrix.h"
24 #include "src/gpu/tessellate/MiddleOutPolygonTriangulator.h"
35 static const char* ModeName(Mode mode) {
37 case Mode::kWedgeMiddleOut:
38 return "MiddleOutShader (kWedges)";
39 case Mode::kCurveMiddleOut:
40 return "MiddleOutShader (kCurves)";
45 // Draws a path directly to the screen using a specific tessellator.
46 class SamplePathTessellatorOp : public GrDrawOp {
50 SamplePathTessellatorOp(const SkRect& drawBounds, const SkPath& path, const SkMatrix& m,
51 GrPipeline::InputFlags pipelineFlags, Mode mode)
55 , fPipelineFlags(pipelineFlags)
57 this->setBounds(drawBounds, HasAABloat::kNo, IsHairline::kNo);
59 const char* name() const override { return "SamplePathTessellatorOp"; }
60 void visitProxies(const GrVisitProxyFunc&) const override {}
61 FixedFunctionFlags fixedFunctionFlags() const override {
62 return FixedFunctionFlags::kUsesHWAA;
64 GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
65 GrClampType clampType) override {
67 return fProcessors.finalize(SK_PMColor4fWHITE, GrProcessorAnalysisCoverage::kNone, clip,
68 nullptr, caps, clampType, &color);
70 void onPrePrepare(GrRecordingContext*, const GrSurfaceProxyView&, GrAppliedClip*,
71 const GrDstProxyView&, GrXferBarrierFlags, GrLoadOp colorLoadOp) override {}
72 void onPrepare(GrOpFlushState* flushState) override {
73 constexpr static SkPMColor4f kCyan = {0,1,1,1};
74 auto alloc = flushState->allocator();
75 const SkMatrix& shaderMatrix = SkMatrix::I();
76 const SkMatrix& pathMatrix = fMatrix;
77 const GrCaps& caps = flushState->caps();
78 const GrShaderCaps& shaderCaps = *caps.shaderCaps();
80 PathTessellator::PathDrawList pathList{pathMatrix, fPath, kCyan};
81 if (fMode == Mode::kCurveMiddleOut) {
82 // This emulates what PathStencilCoverOp does when using curves, except we include the
83 // middle-out triangles directly in the written patches for convenience (normally they
84 // use a simple triangle pipeline). But PathCurveTessellator only knows how to read
85 // extra triangles from BreadcrumbTriangleList, so build on from the middle-out stack.
86 SkArenaAlloc storage{256};
87 GrInnerFanTriangulator::BreadcrumbTriangleList triangles;
88 for (tess::PathMiddleOutFanIter it(fPath); !it.done();) {
89 for (auto [p0, p1, p2] : it.nextStack()) {
90 triangles.append(&storage,
91 pathMatrix.mapPoint(p0),
92 pathMatrix.mapPoint(p1),
93 pathMatrix.mapPoint(p2),
98 auto* tess = PathCurveTessellator::Make(alloc, shaderCaps.infinitySupport());
99 tess->prepareWithTriangles(flushState, shaderMatrix, &triangles, pathList,
103 // This emulates what PathStencilCoverOp does when using wedges.
104 fTessellator = PathWedgeTessellator::Make(alloc, shaderCaps.infinitySupport());
105 fTessellator->prepare(flushState, shaderMatrix, pathList, fPath.countVerbs());
108 auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(flushState, std::move(fProcessors),
110 auto* tessShader = GrPathTessellationShader::Make(*caps.shaderCaps(),
114 fTessellator->patchAttribs());
115 fProgram = GrTessellationShader::MakeProgram({alloc, flushState->writeView(),
116 flushState->usesMSAASurface(),
117 &flushState->dstProxyView(),
118 flushState->renderPassBarriers(),
119 GrLoadOp::kClear, &flushState->caps()},
122 &GrUserStencilSettings::kUnused);
125 void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
126 flushState->bindPipeline(*fProgram, chainBounds);
127 fTessellator->draw(flushState);
131 const SkMatrix fMatrix;
132 const GrPipeline::InputFlags fPipelineFlags;
134 PathTessellator* fTessellator = nullptr;
135 GrProgramInfo* fProgram;
136 GrProcessorSet fProcessors{SkBlendMode::kSrcOver};
138 friend class GrOp; // For ctor.
143 // This sample enables wireframe and visualizes the triangles generated by path tessellators.
144 class SamplePathTessellators : public Sample {
146 SamplePathTessellators() {
148 // For viewing middle-out triangulations of the inner fan.
150 int numSides = 32 * 3;
151 for (int i = 1; i < numSides; ++i) {
152 float theta = 2*3.1415926535897932384626433832785 * i / numSides;
153 fPath.lineTo(std::cos(theta), std::sin(theta));
155 fPath.transform(SkMatrix::Scale(200, 200));
156 fPath.transform(SkMatrix::Translate(300, 300));
158 fPath.moveTo(100, 500);
159 fPath.cubicTo(300, 400, -100, 300, 100, 200);
160 fPath.quadTo(250, 0, 400, 200);
161 fPath.conicTo(600, 350, 400, 500, fConicWeight);
167 void onDrawContent(SkCanvas*) override;
168 Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override;
169 bool onClick(Sample::Click*) override;
170 bool onChar(SkUnichar) override;
172 SkString name() override { return SkString("PathTessellators"); }
175 GrPipeline::InputFlags fPipelineFlags = GrPipeline::InputFlags::kWireframe;
176 Mode fMode = Mode::kWedgeMiddleOut;
178 float fConicWeight = .5;
183 void SamplePathTessellators::onDrawContent(SkCanvas* canvas) {
184 canvas->clear(SK_ColorBLACK);
186 auto ctx = canvas->recordingContext();
187 auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas);
192 } else if (!skgpu::v1::TessellationPathRenderer::IsSupported(*ctx->priv().caps())) {
193 error = "TessellationPathRenderer not supported.";
195 if (!error.isEmpty()) {
196 canvas->clear(SK_ColorRED);
197 SkFont font(nullptr, 20);
198 SkPaint captionPaint;
199 captionPaint.setColor(SK_ColorWHITE);
200 canvas->drawString(error.c_str(), 10, 30, font, captionPaint);
204 sdc->addDrawOp(GrOp::Make<SamplePathTessellatorOp>(ctx,
205 sdc->asRenderTargetProxy()->getBoundsRect(),
206 fPath, canvas->getTotalMatrix(),
207 fPipelineFlags, fMode));
209 // Draw the path points.
211 pointsPaint.setColor(SK_ColorBLUE);
212 pointsPaint.setStrokeWidth(8);
213 SkPath devPath = fPath;
214 devPath.transform(canvas->getTotalMatrix());
216 SkAutoCanvasRestore acr(canvas, true);
217 canvas->setMatrix(SkMatrix::I());
218 SkString caption(ModeName(fMode));
219 caption.appendf(" (w=%g)", fConicWeight);
220 SkFont font(nullptr, 20);
221 SkPaint captionPaint;
222 captionPaint.setColor(SK_ColorWHITE);
223 canvas->drawString(caption, 10, 30, font, captionPaint);
224 canvas->drawPoints(SkCanvas::kPoints_PointMode, devPath.countPoints(),
225 SkPathPriv::PointData(devPath), pointsPaint);
229 class SamplePathTessellators::Click : public Sample::Click {
231 Click(int ptIdx) : fPtIdx(ptIdx) {}
233 void doClick(SkPath* path) {
234 SkPoint pt = path->getPoint(fPtIdx);
235 SkPathPriv::UpdatePathPoint(path, fPtIdx, pt + fCurr - fPrev);
242 Sample::Click* SamplePathTessellators::onFindClickHandler(SkScalar x, SkScalar y,
244 const SkPoint* pts = SkPathPriv::PointData(fPath);
246 for (int i = 0; i < fPath.countPoints(); ++i) {
247 if (fabs(x - pts[i].x()) < fuzz && fabsf(y - pts[i].y()) < fuzz) {
254 bool SamplePathTessellators::onClick(Sample::Click* click) {
255 Click* myClick = (Click*)click;
256 myClick->doClick(&fPath);
260 static SkPath update_weight(const SkPath& path, float w) {
262 for (auto [verb, pts, _] : SkPathPriv::Iterate(path)) {
264 case SkPathVerb::kMove:
265 path_.moveTo(pts[0]);
267 case SkPathVerb::kLine:
268 path_.lineTo(pts[1]);
270 case SkPathVerb::kQuad:
271 path_.quadTo(pts[1], pts[2]);
273 case SkPathVerb::kCubic:
274 path_.cubicTo(pts[1], pts[2], pts[3]);
276 case SkPathVerb::kConic:
277 path_.conicTo(pts[1], pts[2], (w != 1) ? w : .99f);
279 case SkPathVerb::kClose:
286 bool SamplePathTessellators::onChar(SkUnichar unichar) {
289 fPipelineFlags = (GrPipeline::InputFlags)(
290 (int)fPipelineFlags ^ (int)GrPipeline::InputFlags::kWireframe);
298 fPath = update_weight(fPath, fConicWeight);
301 fConicWeight *= 5/4.f;
302 fPath = update_weight(fPath, fConicWeight);
306 fPath = update_weight(fPath, fConicWeight);
309 fConicWeight *= 4/5.f;
310 fPath = update_weight(fPath, fConicWeight);
314 fMode = (Mode)(unichar - '1');
320 Sample* MakeTessellatedPathSample() { return new SamplePathTessellators; }
321 static SampleRegistry gTessellatedPathSample(MakeTessellatedPathSample);
323 } // namespace skgpu::v1
325 #endif // SK_SUPPORT_GPU