Import Cobalt 25.master.0.1033734
diff --git a/third_party/skia/samplecode/SamplePathTessellators.cpp b/third_party/skia/samplecode/SamplePathTessellators.cpp
new file mode 100644
index 0000000..30f4383
--- /dev/null
+++ b/third_party/skia/samplecode/SamplePathTessellators.cpp
@@ -0,0 +1,367 @@
+/*
+ * Copyright 2019 Google LLC.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "include/core/SkCanvas.h"
+#include "samplecode/Sample.h"
+#include "src/core/SkPathPriv.h"
+
+#if SK_SUPPORT_GPU
+
+#include "src/core/SkCanvasPriv.h"
+#include "src/gpu/GrOpFlushState.h"
+#include "src/gpu/GrRecordingContextPriv.h"
+#include "src/gpu/ops/GrDrawOp.h"
+#include "src/gpu/ops/GrSimpleMeshDrawOpHelper.h"
+#include "src/gpu/ops/TessellationPathRenderer.h"
+#include "src/gpu/tessellate/AffineMatrix.h"
+#include "src/gpu/tessellate/MiddleOutPolygonTriangulator.h"
+#include "src/gpu/tessellate/PathCurveTessellator.h"
+#include "src/gpu/tessellate/PathWedgeTessellator.h"
+#include "src/gpu/tessellate/shaders/GrPathTessellationShader.h"
+#include "src/gpu/v1/SurfaceDrawContext_v1.h"
+
+namespace skgpu {
+
+namespace {
+
+enum class Mode {
+ kWedgeMiddleOut,
+ kCurveMiddleOut,
+ kWedgeTessellate,
+ kCurveTessellate
+};
+
+static const char* ModeName(Mode mode) {
+ switch (mode) {
+ case Mode::kWedgeMiddleOut:
+ return "MiddleOutShader (kWedges)";
+ case Mode::kCurveMiddleOut:
+ return "MiddleOutShader (kCurves)";
+ case Mode::kWedgeTessellate:
+ return "HardwareWedgeShader";
+ case Mode::kCurveTessellate:
+ return "HardwareCurveShader";
+ }
+ SkUNREACHABLE;
+}
+
+// Draws a path directly to the screen using a specific tessellator.
+class SamplePathTessellatorOp : public GrDrawOp {
+private:
+ DEFINE_OP_CLASS_ID
+
+ SamplePathTessellatorOp(const SkRect& drawBounds, const SkPath& path, const SkMatrix& m,
+ GrPipeline::InputFlags pipelineFlags, Mode mode)
+ : GrDrawOp(ClassID())
+ , fPath(path)
+ , fMatrix(m)
+ , fPipelineFlags(pipelineFlags)
+ , fMode(mode) {
+ this->setBounds(drawBounds, HasAABloat::kNo, IsHairline::kNo);
+ }
+ const char* name() const override { return "SamplePathTessellatorOp"; }
+ void visitProxies(const GrVisitProxyFunc&) const override {}
+ FixedFunctionFlags fixedFunctionFlags() const override {
+ return FixedFunctionFlags::kUsesHWAA;
+ }
+ GrProcessorSet::Analysis finalize(const GrCaps& caps, const GrAppliedClip* clip,
+ GrClampType clampType) override {
+ SkPMColor4f color;
+ return fProcessors.finalize(SK_PMColor4fWHITE, GrProcessorAnalysisCoverage::kNone, clip,
+ nullptr, caps, clampType, &color);
+ }
+ void onPrePrepare(GrRecordingContext*, const GrSurfaceProxyView&, GrAppliedClip*,
+ const GrDstProxyView&, GrXferBarrierFlags, GrLoadOp colorLoadOp) override {}
+ void onPrepare(GrOpFlushState* flushState) override {
+ constexpr static SkPMColor4f kCyan = {0,1,1,1};
+ auto alloc = flushState->allocator();
+ const SkMatrix& shaderMatrix = SkMatrix::I();
+ const SkMatrix& pathMatrix = fMatrix;
+ const GrCaps& caps = flushState->caps();
+ const GrShaderCaps& shaderCaps = *caps.shaderCaps();
+ int numVerbsToGetMiddleOut = 0;
+ int numVerbsToGetTessellation = caps.minPathVerbsForHwTessellation();
+ auto pipeline = GrSimpleMeshDrawOpHelper::CreatePipeline(flushState, std::move(fProcessors),
+ fPipelineFlags);
+ int numVerbs;
+ bool needsInnerFan;
+ switch (fMode) {
+ case Mode::kWedgeMiddleOut:
+ fTessellator = PathWedgeTessellator::Make(alloc, shaderCaps.infinitySupport());
+ numVerbs = numVerbsToGetMiddleOut;
+ needsInnerFan = false;
+ break;
+ case Mode::kCurveMiddleOut:
+ fTessellator = PathCurveTessellator::Make(alloc,
+ shaderCaps.infinitySupport());
+ numVerbs = numVerbsToGetMiddleOut;
+ needsInnerFan = true;
+ break;
+ case Mode::kWedgeTessellate:
+ fTessellator = PathWedgeTessellator::Make(alloc, shaderCaps.infinitySupport());
+ numVerbs = numVerbsToGetTessellation;
+ needsInnerFan = false;
+ break;
+ case Mode::kCurveTessellate:
+ fTessellator = PathCurveTessellator::Make(alloc,
+ shaderCaps.infinitySupport());
+ numVerbs = numVerbsToGetTessellation;
+ needsInnerFan = true;
+ break;
+ }
+ auto* tessShader = GrPathTessellationShader::Make(alloc,
+ shaderMatrix,
+ kCyan,
+ numVerbs,
+ *pipeline,
+ fTessellator->patchAttribs(),
+ caps);
+ fProgram = GrTessellationShader::MakeProgram({alloc, flushState->writeView(),
+ flushState->usesMSAASurface(),
+ &flushState->dstProxyView(),
+ flushState->renderPassBarriers(),
+ GrLoadOp::kClear, &flushState->caps()},
+ tessShader,
+ pipeline,
+ &GrUserStencilSettings::kUnused);
+
+
+ int patchPreallocCount = fTessellator->patchPreallocCount(fPath.countVerbs());
+ if (needsInnerFan) {
+ patchPreallocCount += fPath.countVerbs() - 1;
+ }
+ PatchWriter patchWriter(flushState,
+ fTessellator,
+ tessShader->maxTessellationSegments(*caps.shaderCaps()),
+ patchPreallocCount);
+
+ if (needsInnerFan) {
+ // Write out inner fan triangles.
+ AffineMatrix m(pathMatrix);
+ for (PathMiddleOutFanIter it(fPath); !it.done();) {
+ for (auto [p0, p1, p2] : it.nextStack()) {
+ auto [mp0, mp1] = m.map2Points(p0, p1);
+ auto mp2 = m.map1Point(&p2);
+ patchWriter.writeTriangle(mp0, mp1, mp2);
+ }
+ }
+ }
+
+ // Write out the curves.
+ fTessellator->writePatches(patchWriter, shaderMatrix, {pathMatrix, fPath, kCyan});
+
+ if (!tessShader->willUseTessellationShaders()) {
+ fTessellator->prepareFixedCountBuffers(flushState);
+ }
+
+ }
+ void onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) override {
+ flushState->bindPipeline(*fProgram, chainBounds);
+ fTessellator->draw(flushState, fProgram->geomProc().willUseTessellationShaders());
+ }
+
+ const SkPath fPath;
+ const SkMatrix fMatrix;
+ const GrPipeline::InputFlags fPipelineFlags;
+ const Mode fMode;
+ PathTessellator* fTessellator = nullptr;
+ GrProgramInfo* fProgram;
+ GrProcessorSet fProcessors{SkBlendMode::kSrcOver};
+
+ friend class GrOp; // For ctor.
+};
+
+} // namespace
+
+// This sample enables wireframe and visualizes the triangles generated by path tessellators.
+class SamplePathTessellators : public Sample {
+public:
+ SamplePathTessellators() {
+#if 0
+ // For viewing middle-out triangulations of the inner fan.
+ fPath.moveTo(1, 0);
+ int numSides = 32 * 3;
+ for (int i = 1; i < numSides; ++i) {
+ float theta = 2*3.1415926535897932384626433832785 * i / numSides;
+ fPath.lineTo(std::cos(theta), std::sin(theta));
+ }
+ fPath.transform(SkMatrix::Scale(200, 200));
+ fPath.transform(SkMatrix::Translate(300, 300));
+#else
+ fPath.moveTo(100, 500);
+ fPath.cubicTo(300, 400, -100, 300, 100, 200);
+ fPath.quadTo(250, 0, 400, 200);
+ fPath.conicTo(600, 350, 400, 500, fConicWeight);
+ fPath.close();
+#endif
+ }
+
+private:
+ void onDrawContent(SkCanvas*) override;
+ Sample::Click* onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) override;
+ bool onClick(Sample::Click*) override;
+ bool onChar(SkUnichar) override;
+
+ SkString name() override { return SkString("PathTessellators"); }
+
+ SkPath fPath;
+ GrPipeline::InputFlags fPipelineFlags = GrPipeline::InputFlags::kWireframe;
+ Mode fMode = Mode::kWedgeMiddleOut;
+
+ float fConicWeight = .5;
+
+ class Click;
+};
+
+void SamplePathTessellators::onDrawContent(SkCanvas* canvas) {
+ canvas->clear(SK_ColorBLACK);
+
+ auto ctx = canvas->recordingContext();
+ auto sdc = SkCanvasPriv::TopDeviceSurfaceDrawContext(canvas);
+
+ SkString error;
+ if (!sdc || !ctx) {
+ error = "GPU Only.";
+ } else if (!skgpu::v1::TessellationPathRenderer::IsSupported(*ctx->priv().caps())) {
+ error = "TessellationPathRenderer not supported.";
+ } else if (fMode >= Mode::kWedgeTessellate &&
+ !ctx->priv().caps()->shaderCaps()->tessellationSupport()) {
+ error.printf("%s requires hardware tessellation support.", ModeName(fMode));
+ }
+ if (!error.isEmpty()) {
+ canvas->clear(SK_ColorRED);
+ SkFont font(nullptr, 20);
+ SkPaint captionPaint;
+ captionPaint.setColor(SK_ColorWHITE);
+ canvas->drawString(error.c_str(), 10, 30, font, captionPaint);
+ return;
+ }
+
+ sdc->addDrawOp(GrOp::Make<SamplePathTessellatorOp>(ctx,
+ sdc->asRenderTargetProxy()->getBoundsRect(),
+ fPath, canvas->getTotalMatrix(),
+ fPipelineFlags, fMode));
+
+ // Draw the path points.
+ SkPaint pointsPaint;
+ pointsPaint.setColor(SK_ColorBLUE);
+ pointsPaint.setStrokeWidth(8);
+ SkPath devPath = fPath;
+ devPath.transform(canvas->getTotalMatrix());
+ {
+ SkAutoCanvasRestore acr(canvas, true);
+ canvas->setMatrix(SkMatrix::I());
+ SkString caption(ModeName(fMode));
+ caption.appendf(" (w=%g)", fConicWeight);
+ SkFont font(nullptr, 20);
+ SkPaint captionPaint;
+ captionPaint.setColor(SK_ColorWHITE);
+ canvas->drawString(caption, 10, 30, font, captionPaint);
+ canvas->drawPoints(SkCanvas::kPoints_PointMode, devPath.countPoints(),
+ SkPathPriv::PointData(devPath), pointsPaint);
+ }
+}
+
+class SamplePathTessellators::Click : public Sample::Click {
+public:
+ Click(int ptIdx) : fPtIdx(ptIdx) {}
+
+ void doClick(SkPath* path) {
+ SkPoint pt = path->getPoint(fPtIdx);
+ SkPathPriv::UpdatePathPoint(path, fPtIdx, pt + fCurr - fPrev);
+ }
+
+private:
+ int fPtIdx;
+};
+
+Sample::Click* SamplePathTessellators::onFindClickHandler(SkScalar x, SkScalar y,
+ skui::ModifierKey) {
+ const SkPoint* pts = SkPathPriv::PointData(fPath);
+ float fuzz = 30;
+ for (int i = 0; i < fPath.countPoints(); ++i) {
+ if (fabs(x - pts[i].x()) < fuzz && fabsf(y - pts[i].y()) < fuzz) {
+ return new Click(i);
+ }
+ }
+ return nullptr;
+}
+
+bool SamplePathTessellators::onClick(Sample::Click* click) {
+ Click* myClick = (Click*)click;
+ myClick->doClick(&fPath);
+ return true;
+}
+
+static SkPath update_weight(const SkPath& path, float w) {
+ SkPath path_;
+ for (auto [verb, pts, _] : SkPathPriv::Iterate(path)) {
+ switch (verb) {
+ case SkPathVerb::kMove:
+ path_.moveTo(pts[0]);
+ break;
+ case SkPathVerb::kLine:
+ path_.lineTo(pts[1]);
+ break;
+ case SkPathVerb::kQuad:
+ path_.quadTo(pts[1], pts[2]);
+ break;
+ case SkPathVerb::kCubic:
+ path_.cubicTo(pts[1], pts[2], pts[3]);
+ break;
+ case SkPathVerb::kConic:
+ path_.conicTo(pts[1], pts[2], (w != 1) ? w : .99f);
+ break;
+ case SkPathVerb::kClose:
+ break;
+ }
+ }
+ return path_;
+}
+
+bool SamplePathTessellators::onChar(SkUnichar unichar) {
+ switch (unichar) {
+ case 'w':
+ fPipelineFlags = (GrPipeline::InputFlags)(
+ (int)fPipelineFlags ^ (int)GrPipeline::InputFlags::kWireframe);
+ return true;
+ case 'D': {
+ fPath.dump();
+ return true;
+ }
+ case '+':
+ fConicWeight *= 2;
+ fPath = update_weight(fPath, fConicWeight);
+ return true;
+ case '=':
+ fConicWeight *= 5/4.f;
+ fPath = update_weight(fPath, fConicWeight);
+ return true;
+ case '_':
+ fConicWeight *= .5f;
+ fPath = update_weight(fPath, fConicWeight);
+ return true;
+ case '-':
+ fConicWeight *= 4/5.f;
+ fPath = update_weight(fPath, fConicWeight);
+ return true;
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ fMode = (Mode)(unichar - '1');
+ return true;
+ }
+ return false;
+}
+
+Sample* MakeTessellatedPathSample() { return new SamplePathTessellators; }
+static SampleRegistry gTessellatedPathSample(MakeTessellatedPathSample);
+
+} // namespace skgpu
+
+#endif // SK_SUPPORT_GPU