skia2/samplecode/SampleTessellatedWedge.cpp
Chris Dalton b832ce61ee Add a path renderer that uses GPU tessellation
Implements a simple first pass for a path renderer that uses the
classic Red Book "stencil then cover" method, and linearizes curves
with GPU tessellation shaders.

The new path renderer is disabled by default, and can only be enabled
in the viewer UI or by passing the "--pr gtess" flag.

Change-Id: Ic9354952e93c8b108577961760b4f0daa82d35aa
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/261715
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
2020-01-07 20:00:22 +00:00

149 lines
4.4 KiB
C++

/*
* 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"
#include "tools/ToolUtils.h"
#if SK_SUPPORT_GPU
#include "include/gpu/GrContext.h"
#include "src/gpu/GrClip.h"
#include "src/gpu/GrContextPriv.h"
#include "src/gpu/GrMemoryPool.h"
#include "src/gpu/GrRenderTargetContext.h"
#include "src/gpu/GrRenderTargetContextPriv.h"
#include "src/gpu/tessellate/GrTessellatePathOp.h"
// This sample enables wireframe and visualizes the triangulation generated by
// GrTessellateWedgeShader.
class TessellatedWedgeView : public Sample {
public:
TessellatedWedgeView() {
fPath.moveTo(100, 200);
fPath.cubicTo(100, 100, 400, 100, 400, 200);
fPath.lineTo(250, 500);
}
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("TessellatedWedge"); }
SkMatrix fLastViewMatrix = SkMatrix::I();
SkPath fPath;
GrTessellatePathOp::Flags fFlags = GrTessellatePathOp::Flags::kWireframe;
class Click;
};
void TessellatedWedgeView::onDrawContent(SkCanvas* canvas) {
canvas->clear(SK_ColorBLACK);
GrContext* ctx = canvas->getGrContext();
GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext();
SkString error;
if (!rtc || !ctx) {
error = "GPU Only.";
} else if (!ctx->priv().caps()->shaderCaps()->tessellationSupport()) {
error = "GPU tessellation not supported.";
} else if (1 == rtc->numSamples() && !ctx->priv().caps()->mixedSamplesSupport()) {
error = "MSAA/mixed samples only.";
}
if (!error.isEmpty()) {
SkFont font(nullptr, 20);
SkPaint captionPaint;
captionPaint.setColor(SK_ColorWHITE);
canvas->drawString(error.c_str(), 10, 30, font, captionPaint);
return;
}
GrPaint paint;
paint.setColor4f({1,0,1,1});
GrAAType aa;
if (rtc->numSamples() > 1) {
aa = GrAAType::kMSAA;
} else if (rtc->asRenderTargetProxy()->canUseMixedSamples(*ctx->priv().caps())) {
aa = GrAAType::kCoverage;
} else {
aa = GrAAType::kNone;
}
GrOpMemoryPool* pool = ctx->priv().opMemoryPool();
rtc->priv().testingOnly_addDrawOp(pool->allocate<GrTessellatePathOp>(
canvas->getTotalMatrix(), fPath, std::move(paint), aa, fFlags));
// Draw the path points.
SkPaint pointsPaint;
pointsPaint.setColor(SK_ColorBLUE);
pointsPaint.setStrokeWidth(8);
canvas->drawPoints(SkCanvas::kPoints_PointMode, fPath.countPoints(),
SkPathPriv::PointData(fPath), pointsPaint);
fLastViewMatrix = canvas->getTotalMatrix();
}
class TessellatedWedgeView::Click : public Sample::Click {
public:
Click(int ptIdx) : fPtIdx(ptIdx) {}
void doClick(SkPath* path) {
if (fPtIdx >= 0) {
SkPoint pt = path->getPoint(fPtIdx);
ToolUtils::set_path_pt(fPtIdx, pt + fCurr - fPrev, path);
} else {
path->transform(
SkMatrix::MakeTrans(fCurr.x() - fPrev.x(), fCurr.y() - fPrev.y()), path);
}
}
private:
int fPtIdx;
};
Sample::Click* TessellatedWedgeView::onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) {
const SkPoint* pts = SkPathPriv::PointData(fPath);
float fuzz = 20 / fLastViewMatrix.getMaxScale();
for (int i = 0; i < fPath.countPoints(); ++i) {
SkPoint screenPoint = pts[i];
if (fabs(x - screenPoint.x()) < fuzz && fabsf(y - screenPoint.y()) < fuzz) {
return new Click(i);
}
}
return new Click(-1);
}
bool TessellatedWedgeView::onClick(Sample::Click* click) {
Click* myClick = (Click*)click;
myClick->doClick(&fPath);
return true;
}
bool TessellatedWedgeView::onChar(SkUnichar unichar) {
switch (unichar) {
case 'w':
fFlags = (GrTessellatePathOp::Flags)(
(int)fFlags ^ (int)GrTessellatePathOp::Flags::kWireframe);
return true;
case 'D': {
fPath.dump();
return true;
}
}
return false;
}
DEF_SAMPLE(return new TessellatedWedgeView;)
#endif // SK_SUPPORT_GPU