skia2/samplecode/SampleCCPRGeometry.cpp
Chris Dalton 8d17769700 CCPR: Remove kCombinedTriangleHullsAndEdges
Removes the mode that generates edge and hull geometry simultaneously
from the geometry shader. Perf was hit and miss and it's not
compatible with vertex shaders. We can revisit if geometry shaders
still show promise on some platforms after a vertex shader impl is
finished.

Bug: skia:
Change-Id: I984231e9a5bb60fe31d3ba280c7390a74aa5bc27
Reviewed-on: https://skia-review.googlesource.com/51300
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
2017-09-26 17:00:06 +00:00

402 lines
14 KiB
C++

/*
* Copyright 2017 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "SkTypes.h"
#if SK_SUPPORT_GPU
#include "GrContextPriv.h"
#include "GrPathUtils.h"
#include "GrRenderTargetContext.h"
#include "GrRenderTargetContextPriv.h"
#include "GrResourceProvider.h"
#include "SampleCode.h"
#include "SkCanvas.h"
#include "SkMakeUnique.h"
#include "SkPaint.h"
#include "SkPath.h"
#include "SkView.h"
#include "ccpr/GrCCPRCoverageProcessor.h"
#include "ccpr/GrCCPRGeometry.h"
#include "gl/GrGLGpu.cpp"
#include "ops/GrDrawOp.h"
using TriangleInstance = GrCCPRCoverageProcessor::TriangleInstance;
using CurveInstance = GrCCPRCoverageProcessor::CurveInstance;
using Mode = GrCCPRCoverageProcessor::Mode;
static constexpr float kDebugBloat = 40;
static int num_points(Mode mode) {
return mode >= Mode::kSerpentineHulls ? 4 : 3;
}
static int is_quadratic(Mode mode) {
return mode >= Mode::kQuadraticHulls && mode < Mode::kSerpentineHulls;
}
/**
* This sample visualizes the AA bloat geometry generated by the ccpr geometry shaders. It
* increases the AA bloat by 50x and outputs color instead of coverage (coverage=+1 -> green,
* coverage=0 -> black, coverage=-1 -> red). Use the keys 1-7 to cycle through the different
* geometry processors.
*/
class CCPRGeometryView : public SampleView {
public:
CCPRGeometryView() { this->updateGpuData(); }
void onDrawContent(SkCanvas*) override;
SkView::Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned) override;
bool onClick(SampleView::Click*) override;
bool onQuery(SkEvent* evt) override;
private:
class Click;
class Op;
void updateAndInval() {
this->updateGpuData();
this->inval(nullptr);
}
void updateGpuData();
Mode fMode = Mode::kTriangleHulls;
SkMatrix fCubicKLM;
SkPoint fPoints[4] = {
{100.05f, 100.05f},
{100.05f, 300.95f},
{400.75f, 300.95f},
{400.75f, 100.05f}
};
SkTArray<SkPoint> fGpuPoints;
SkTArray<int32_t> fInstanceData;
int fInstanceCount;
typedef SampleView INHERITED;
};
class CCPRGeometryView::Op : public GrDrawOp {
DEFINE_OP_CLASS_ID
public:
Op(CCPRGeometryView* view)
: INHERITED(ClassID())
, fView(view) {
this->setBounds(SkRect::MakeLargest(), GrOp::HasAABloat::kNo, GrOp::IsZeroArea::kNo);
}
const char* name() const override { return "[Testing/Sample code] CCPRGeometryView::Op"; }
private:
FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
RequiresDstTexture finalize(const GrCaps&, const GrAppliedClip*,
GrPixelConfigIsClamped) override {
return RequiresDstTexture::kNo;
}
bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override { return false; }
void onPrepare(GrOpFlushState*) override {}
void onExecute(GrOpFlushState*) override;
CCPRGeometryView* fView;
typedef GrDrawOp INHERITED;
};
static void draw_klm_line(int w, int h, SkCanvas* canvas, const SkScalar line[3], SkColor color) {
SkPoint p1, p2;
if (SkScalarAbs(line[1]) > SkScalarAbs(line[0])) {
// Draw from vertical edge to vertical edge.
p1 = {0, -line[2] / line[1]};
p2 = {(SkScalar) w, (-line[2] - w * line[0]) / line[1]};
} else {
// Draw from horizontal edge to horizontal edge.
p1 = {-line[2] / line[0], 0};
p2 = {(-line[2] - h * line[1]) / line[0], (SkScalar) h};
}
SkPaint linePaint;
linePaint.setColor(color);
linePaint.setAlpha(128);
linePaint.setStyle(SkPaint::kStroke_Style);
linePaint.setStrokeWidth(0);
linePaint.setAntiAlias(true);
canvas->drawLine(p1, p2, linePaint);
}
void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
SkAutoCanvasRestore acr(canvas, true);
canvas->setMatrix(SkMatrix::I());
SkPath outline;
outline.moveTo(fPoints[0]);
if (4 == num_points(fMode)) {
outline.cubicTo(fPoints[1], fPoints[2], fPoints[3]);
} else if (is_quadratic(fMode)) {
outline.quadTo(fPoints[1], fPoints[3]);
} else {
outline.lineTo(fPoints[1]);
outline.lineTo(fPoints[3]);
outline.close();
}
SkPaint outlinePaint;
outlinePaint.setColor(0x30000000);
outlinePaint.setStyle(SkPaint::kStroke_Style);
outlinePaint.setStrokeWidth(0);
outlinePaint.setAntiAlias(true);
canvas->drawPath(outline, outlinePaint);
#if 0
SkPaint gridPaint;
gridPaint.setColor(0x10000000);
gridPaint.setStyle(SkPaint::kStroke_Style);
gridPaint.setStrokeWidth(0);
gridPaint.setAntiAlias(true);
for (int y = 0; y < this->height(); y += kDebugBloat) {
canvas->drawLine(0, y, this->width(), y, gridPaint);
}
for (int x = 0; x < this->width(); x += kDebugBloat) {
canvas->drawLine(x, 0, x, this->height(), outlinePaint);
}
#endif
const char* caption = "Use GPU backend to visualize geometry.";
if (GrRenderTargetContext* rtc =
canvas->internal_private_accessTopLayerRenderTargetContext()) {
rtc->priv().testingOnly_addDrawOp(skstd::make_unique<Op>(this));
caption = GrCCPRCoverageProcessor::GetProcessorName(fMode);
}
SkPaint pointsPaint;
pointsPaint.setColor(SK_ColorBLUE);
pointsPaint.setStrokeWidth(8);
pointsPaint.setAntiAlias(true);
if (4 == num_points(fMode)) {
int w = this->width(), h = this->height();
canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPoints, pointsPaint);
draw_klm_line(w, h, canvas, &fCubicKLM[0], SK_ColorYELLOW);
draw_klm_line(w, h, canvas, &fCubicKLM[3], SK_ColorBLUE);
draw_klm_line(w, h, canvas, &fCubicKLM[6], SK_ColorRED);
} else {
canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, fPoints, pointsPaint);
canvas->drawPoints(SkCanvas::kPoints_PointMode, 1, fPoints + 3, pointsPaint);
}
SkPaint captionPaint;
captionPaint.setTextSize(20);
captionPaint.setColor(SK_ColorBLACK);
captionPaint.setAntiAlias(true);
canvas->drawText(caption, strlen(caption), 10, 30, captionPaint);
}
void CCPRGeometryView::updateGpuData() {
int vertexCount = num_points(fMode);
fGpuPoints.reset();
fInstanceData.reset();
fInstanceCount = 0;
if (4 == vertexCount) {
double t[2], s[2];
SkCubicType type = GrPathUtils::getCubicKLM(fPoints, &fCubicKLM, t, s);
if (Mode::kSerpentineHulls == fMode && SkCubicType::kLoop == type) {
fMode = Mode::kLoopHulls;
}
if (Mode::kSerpentineCorners == fMode && SkCubicType::kLoop == type) {
fMode = Mode::kLoopCorners;
}
if (Mode::kLoopHulls == fMode && SkCubicType::kLoop != type) {
fMode = Mode::kSerpentineHulls;
}
if (Mode::kLoopCorners == fMode && SkCubicType::kLoop != type) {
fMode = Mode::kSerpentineCorners;
}
GrCCPRGeometry geometry;
geometry.beginContour(fPoints[0]);
geometry.cubicTo(fPoints[1], fPoints[2], fPoints[3], kDebugBloat/2, kDebugBloat/2);
geometry.endContour();
fGpuPoints.push_back_n(geometry.points().count(), geometry.points().begin());
int ptsIdx = 0;
for (GrCCPRGeometry::Verb verb : geometry.verbs()) {
switch (verb) {
case GrCCPRGeometry::Verb::kLineTo:
++ptsIdx;
continue;
case GrCCPRGeometry::Verb::kMonotonicQuadraticTo:
ptsIdx += 2;
continue;
case GrCCPRGeometry::Verb::kMonotonicSerpentineTo:
case GrCCPRGeometry::Verb::kMonotonicLoopTo:
fInstanceData.push_back(ptsIdx);
fInstanceData.push_back(0); // Atlas offset.
ptsIdx += 3;
++fInstanceCount;
continue;
default: continue;
}
}
} else if (is_quadratic(fMode)) {
GrCCPRGeometry geometry;
geometry.beginContour(fPoints[0]);
geometry.quadraticTo(fPoints[1], fPoints[3]);
geometry.endContour();
fGpuPoints.push_back_n(geometry.points().count(), geometry.points().begin());
for (GrCCPRGeometry::Verb verb : geometry.verbs()) {
if (GrCCPRGeometry::Verb::kBeginContour == verb ||
GrCCPRGeometry::Verb::kEndOpenContour == verb ||
GrCCPRGeometry::Verb::kEndClosedContour == verb) {
continue;
}
SkASSERT(GrCCPRGeometry::Verb::kMonotonicQuadraticTo == verb);
fInstanceData.push_back(2 * fInstanceCount++); // Pts idx.
fInstanceData.push_back(0); // Atlas offset.
}
} else {
fGpuPoints.push_back(fPoints[0]);
fGpuPoints.push_back(fPoints[1]);
fGpuPoints.push_back(fPoints[3]);
fInstanceData.push_back(0);
fInstanceData.push_back(1);
fInstanceData.push_back(2);
fInstanceData.push_back(0); // Atlas offset.
fInstanceCount = 1;
}
}
void CCPRGeometryView::Op::onExecute(GrOpFlushState* state) {
if (fView->fInstanceData.empty()) {
return;
}
GrResourceProvider* rp = state->resourceProvider();
GrContext* context = state->gpu()->getContext();
GrGLGpu* glGpu = kOpenGL_GrBackend == context->contextPriv().getBackend() ?
static_cast<GrGLGpu*>(state->gpu()) : nullptr;
int vertexCount = num_points(fView->fMode);
sk_sp<GrBuffer> pointsBuffer(rp->createBuffer(fView->fGpuPoints.count() * sizeof(SkPoint),
kTexel_GrBufferType, kDynamic_GrAccessPattern,
GrResourceProvider::kNoPendingIO_Flag |
GrResourceProvider::kRequireGpuMemory_Flag,
fView->fGpuPoints.begin()));
if (!pointsBuffer) {
return;
}
sk_sp<GrBuffer> instanceBuffer(rp->createBuffer(fView->fInstanceData.count() * sizeof(int),
kVertex_GrBufferType, kDynamic_GrAccessPattern,
GrResourceProvider::kNoPendingIO_Flag |
GrResourceProvider::kRequireGpuMemory_Flag,
fView->fInstanceData.begin()));
if (!instanceBuffer) {
return;
}
GrPipeline pipeline(state->drawOpArgs().fProxy, GrPipeline::ScissorState::kDisabled,
SkBlendMode::kSrcOver);
GrCCPRCoverageProcessor ccprProc(fView->fMode, pointsBuffer.get());
SkDEBUGCODE(ccprProc.enableDebugVisualizations(kDebugBloat);)
GrMesh mesh(4 == vertexCount ? GrPrimitiveType::kLinesAdjacency : GrPrimitiveType::kTriangles);
mesh.setInstanced(instanceBuffer.get(), fView->fInstanceCount, 0, vertexCount);
if (glGpu) {
glGpu->handleDirtyContext();
GR_GL_CALL(glGpu->glInterface(), PolygonMode(GR_GL_FRONT_AND_BACK, GR_GL_LINE));
GR_GL_CALL(glGpu->glInterface(), Enable(GR_GL_LINE_SMOOTH));
}
state->rtCommandBuffer()->draw(pipeline, ccprProc, &mesh, nullptr, 1, this->bounds());
if (glGpu) {
context->resetContext(kMisc_GrGLBackendState);
}
}
class CCPRGeometryView::Click : public SampleView::Click {
public:
Click(SkView* target, int ptIdx) : SampleView::Click(target), fPtIdx(ptIdx) {}
void doClick(SkPoint points[]) {
if (fPtIdx >= 0) {
this->dragPoint(points, fPtIdx);
} else {
for (int i = 0; i < 4; ++i) {
this->dragPoint(points, i);
}
}
}
private:
void dragPoint(SkPoint points[], int idx) {
SkIPoint delta = fICurr - fIPrev;
points[idx] += SkPoint::Make(delta.x(), delta.y());
}
int fPtIdx;
};
SkView::Click* CCPRGeometryView::onFindClickHandler(SkScalar x, SkScalar y, unsigned) {
for (int i = 0; i < 4; ++i) {
if (4 != num_points(fMode) && 2 == i) {
continue;
}
if (fabs(x - fPoints[i].x()) < 20 && fabsf(y - fPoints[i].y()) < 20) {
return new Click(this, i);
}
}
return new Click(this, -1);
}
bool CCPRGeometryView::onClick(SampleView::Click* click) {
Click* myClick = (Click*) click;
myClick->doClick(fPoints);
this->updateAndInval();
return true;
}
bool CCPRGeometryView::onQuery(SkEvent* evt) {
if (SampleCode::TitleQ(*evt)) {
SampleCode::TitleR(evt, "CCPRGeometry");
return true;
}
SkUnichar unichar;
if (SampleCode::CharQ(*evt, &unichar)) {
if (unichar >= '1' && unichar <= '7') {
fMode = Mode(unichar - '1');
if (fMode >= Mode::kLoopHulls) {
// '6' -> kSerpentineHulls, '7' -> kSerpentineCorners. updateGpuData converts to
// kLoop* if needed.
fMode = Mode(int(fMode) + 1);
}
this->updateAndInval();
return true;
}
if (unichar == 'D') {
SkDebugf(" SkPoint fPoints[4] = {\n");
SkDebugf(" {%ff, %ff},\n", fPoints[0].x(), fPoints[0].y());
SkDebugf(" {%ff, %ff},\n", fPoints[1].x(), fPoints[1].y());
SkDebugf(" {%ff, %ff},\n", fPoints[2].x(), fPoints[2].y());
SkDebugf(" {%ff, %ff}\n", fPoints[3].x(), fPoints[3].y());
SkDebugf(" };\n");
return true;
}
}
return this->INHERITED::onQuery(evt);
}
DEF_SAMPLE( return new CCPRGeometryView; )
#endif // SK_SUPPORT_GPU