skia2/samplecode/SampleCCPRGeometry.cpp
Greg Daniel 426274b880 Add support for holding onto refs for input buffers from bindBuffer calls.
Mostly this is a lot of plumbing of sk_sp around instead of const*.

This does allow the d3d and vk backends to hold refs to the GrBuffers that
are bound on a command buffer. This means that our buffer alloc pools will
not try to reuse this buffers until the gpu is done with them. Previously
vk and d3d will sniff out if one of these buffers was being used again
while still active on the gpu and rip out the internal backend buffer and
allocate a new one which is not cheap. We see a lot of perf wins from
not doing this.

Change-Id: I9ffe649151ee43066dce620bd3e2763b029a9811
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/303583
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Commit-Queue: Greg Daniel <egdaniel@google.com>
2020-07-20 18:29:43 +00:00

510 lines
19 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 "include/core/SkTypes.h"
#if SK_SUPPORT_GPU
#include "include/core/SkCanvas.h"
#include "include/core/SkPaint.h"
#include "include/core/SkPath.h"
#include "include/gpu/GrDirectContext.h"
#include "samplecode/Sample.h"
#include "src/core/SkRectPriv.h"
#include "src/gpu/GrContextPriv.h"
#include "src/gpu/GrGpu.h"
#include "src/gpu/GrMemoryPool.h"
#include "src/gpu/GrOnFlushResourceProvider.h"
#include "src/gpu/GrOpFlushState.h"
#include "src/gpu/GrRecordingContextPriv.h"
#include "src/gpu/GrRenderTargetContext.h"
#include "src/gpu/GrRenderTargetContextPriv.h"
#include "src/gpu/GrResourceProvider.h"
#include "src/gpu/ccpr/GrCCCoverageProcessor.h"
#include "src/gpu/ccpr/GrCCFillGeometry.h"
#include "src/gpu/ccpr/GrCCStroker.h"
#include "src/gpu/ccpr/GrGSCoverageProcessor.h"
#include "src/gpu/ccpr/GrVSCoverageProcessor.h"
#include "src/gpu/geometry/GrPathUtils.h"
#include "src/gpu/glsl/GrGLSLFragmentShaderBuilder.h"
#include "src/gpu/ops/GrDrawOp.h"
#ifdef SK_GL
#include "src/gpu/gl/GrGLGpu.h"
#endif
using TriPointInstance = GrCCCoverageProcessor::TriPointInstance;
using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance;
using PrimitiveType = GrCCCoverageProcessor::PrimitiveType;
static constexpr float kDebugBloat = 40;
/**
* 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 Sample {
void onOnceBeforeDraw() override { this->updateGpuData(); }
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("CCPRGeometry"); }
class Click;
class DrawCoverageCountOp;
class VisualizeCoverageCountFP;
void updateAndInval() { this->updateGpuData(); }
void updateGpuData();
PrimitiveType fPrimitiveType = PrimitiveType::kTriangles;
SkCubicType fCubicType;
SkMatrix fCubicKLM;
SkPoint fPoints[4] = {
{100.05f, 100.05f}, {400.75f, 100.05f}, {400.75f, 300.95f}, {100.05f, 300.95f}};
float fConicWeight = .5;
float fStrokeWidth = 40;
bool fDoStroke = false;
SkTArray<TriPointInstance> fTriPointInstances;
SkTArray<QuadPointInstance> fQuadPointInstances;
SkPath fPath;
};
class CCPRGeometryView::DrawCoverageCountOp : public GrDrawOp {
DEFINE_OP_CLASS_ID
public:
DrawCoverageCountOp(CCPRGeometryView* view) : INHERITED(ClassID()), fView(view) {
this->setBounds(SkRect::MakeIWH(fView->width(), fView->height()), GrOp::HasAABloat::kNo,
GrOp::IsHairline::kNo);
}
const char* name() const override {
return "[Testing/Sample code] CCPRGeometryView::DrawCoverageCountOp";
}
private:
FixedFunctionFlags fixedFunctionFlags() const override { return FixedFunctionFlags::kNone; }
GrProcessorSet::Analysis finalize(const GrCaps&, const GrAppliedClip*,
bool hasMixedSampledCoverage, GrClampType) override {
return GrProcessorSet::EmptySetAnalysis();
}
void onPrePrepare(GrRecordingContext*,
const GrSurfaceProxyView* writeView,
GrAppliedClip*,
const GrXferProcessor::DstProxyView&) override {}
void onPrepare(GrOpFlushState*) override {}
void onExecute(GrOpFlushState*, const SkRect& chainBounds) override;
CCPRGeometryView* fView;
typedef GrDrawOp INHERITED;
};
class CCPRGeometryView::VisualizeCoverageCountFP : public GrFragmentProcessor {
public:
VisualizeCoverageCountFP() : GrFragmentProcessor(kTestFP_ClassID, kNone_OptimizationFlags) {}
private:
const char* name() const override {
return "[Testing/Sample code] CCPRGeometryView::VisualizeCoverageCountFP";
}
std::unique_ptr<GrFragmentProcessor> clone() const override {
return std::make_unique<VisualizeCoverageCountFP>();
}
void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override {}
bool onIsEqual(const GrFragmentProcessor&) const override { return true; }
class Impl : public GrGLSLFragmentProcessor {
void emitCode(EmitArgs& args) override {
GrGLSLFPFragmentBuilder* f = args.fFragBuilder;
f->codeAppendf("half count = %s.a;", args.fInputColor);
f->codeAppendf("%s = half4(clamp(-count, 0, 1), clamp(+count, 0, 1), 0, abs(count));",
args.fOutputColor);
}
};
GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { return new Impl; }
};
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) {
canvas->clear(SK_ColorBLACK);
if (!fDoStroke) {
SkPaint outlinePaint;
outlinePaint.setColor(0x80ffffff);
outlinePaint.setStyle(SkPaint::kStroke_Style);
outlinePaint.setStrokeWidth(0);
outlinePaint.setAntiAlias(true);
canvas->drawPath(fPath, 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
SkString caption;
if (GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext()) {
// Render coverage count.
auto ctx = canvas->recordingContext();
SkASSERT(ctx);
GrOpMemoryPool* pool = ctx->priv().opMemoryPool();
int width = this->width();
int height = this->height();
auto ccbuff = GrRenderTargetContext::Make(
ctx, GrColorType::kAlpha_F16, nullptr, SkBackingFit::kApprox, {width, height});
SkASSERT(ccbuff);
ccbuff->clear(SK_PMColor4fTRANSPARENT);
ccbuff->priv().testingOnly_addDrawOp(pool->allocate<DrawCoverageCountOp>(this));
// Visualize coverage count in main canvas.
GrPaint paint;
paint.addColorFragmentProcessor(
GrTextureEffect::Make(ccbuff->readSurfaceView(), ccbuff->colorInfo().alphaType()));
paint.addColorFragmentProcessor(
std::make_unique<VisualizeCoverageCountFP>());
paint.setPorterDuffXPFactory(SkBlendMode::kSrcOver);
rtc->drawRect(nullptr, std::move(paint), GrAA::kNo, SkMatrix::I(),
SkRect::MakeIWH(this->width(), this->height()));
// Add label.
caption.appendf("PrimitiveType_%s",
GrCCCoverageProcessor::PrimitiveTypeName(fPrimitiveType));
if (PrimitiveType::kCubics == fPrimitiveType) {
caption.appendf(" (%s)", SkCubicTypeName(fCubicType));
} else if (PrimitiveType::kConics == fPrimitiveType) {
caption.appendf(" (w=%f)", fConicWeight);
}
if (fDoStroke) {
caption.appendf(" (stroke_width=%f)", fStrokeWidth);
}
} else {
caption = "Use GPU backend to visualize geometry.";
}
SkPaint pointsPaint;
pointsPaint.setColor(SK_ColorBLUE);
pointsPaint.setStrokeWidth(8);
pointsPaint.setAntiAlias(true);
if (PrimitiveType::kCubics == fPrimitiveType) {
canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPoints, pointsPaint);
if (!fDoStroke) {
int w = this->width(), h = this->height();
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);
}
SkFont font(nullptr, 20);
SkPaint captionPaint;
captionPaint.setColor(SK_ColorWHITE);
canvas->drawString(caption, 10, 30, font, captionPaint);
}
void CCPRGeometryView::updateGpuData() {
using Verb = GrCCFillGeometry::Verb;
fTriPointInstances.reset();
fQuadPointInstances.reset();
fPath.reset();
fPath.moveTo(fPoints[0]);
if (PrimitiveType::kCubics == fPrimitiveType) {
double t[2], s[2];
fCubicType = GrPathUtils::getCubicKLM(fPoints, &fCubicKLM, t, s);
GrCCFillGeometry geometry;
geometry.beginContour(fPoints[0]);
geometry.cubicTo(fPoints, kDebugBloat / 2, kDebugBloat / 2);
geometry.endContour();
int ptsIdx = 0;
for (Verb verb : geometry.verbs()) {
switch (verb) {
case Verb::kLineTo:
++ptsIdx;
continue;
case Verb::kMonotonicQuadraticTo:
ptsIdx += 2;
continue;
case Verb::kMonotonicCubicTo:
fQuadPointInstances.push_back().set(&geometry.points()[ptsIdx], 0, 0);
ptsIdx += 3;
continue;
default:
continue;
}
}
fPath.cubicTo(fPoints[1], fPoints[2], fPoints[3]);
} else if (PrimitiveType::kTriangles != fPrimitiveType) {
SkPoint P3[3] = {fPoints[0], fPoints[1], fPoints[3]};
GrCCFillGeometry geometry;
geometry.beginContour(P3[0]);
if (PrimitiveType::kQuadratics == fPrimitiveType) {
geometry.quadraticTo(P3);
fPath.quadTo(fPoints[1], fPoints[3]);
} else {
SkASSERT(PrimitiveType::kConics == fPrimitiveType);
geometry.conicTo(P3, fConicWeight);
fPath.conicTo(fPoints[1], fPoints[3], fConicWeight);
}
geometry.endContour();
int ptsIdx = 0, conicWeightIdx = 0;
for (Verb verb : geometry.verbs()) {
if (Verb::kBeginContour == verb ||
Verb::kEndOpenContour == verb ||
Verb::kEndClosedContour == verb) {
continue;
}
if (Verb::kLineTo == verb) {
++ptsIdx;
continue;
}
SkASSERT(Verb::kMonotonicQuadraticTo == verb || Verb::kMonotonicConicTo == verb);
if (PrimitiveType::kQuadratics == fPrimitiveType &&
Verb::kMonotonicQuadraticTo == verb) {
fTriPointInstances.push_back().set(
&geometry.points()[ptsIdx], Sk2f(0, 0),
TriPointInstance::Ordering::kXYTransposed);
} else if (PrimitiveType::kConics == fPrimitiveType &&
Verb::kMonotonicConicTo == verb) {
fQuadPointInstances.push_back().setW(&geometry.points()[ptsIdx], Sk2f(0, 0),
geometry.getConicWeight(conicWeightIdx++));
}
ptsIdx += 2;
}
} else {
fTriPointInstances.push_back().set(
fPoints[0], fPoints[1], fPoints[3], Sk2f(0, 0),
TriPointInstance::Ordering::kXYTransposed);
fPath.lineTo(fPoints[1]);
fPath.lineTo(fPoints[3]);
fPath.close();
}
}
void CCPRGeometryView::DrawCoverageCountOp::onExecute(GrOpFlushState* state,
const SkRect& chainBounds) {
GrResourceProvider* rp = state->resourceProvider();
auto direct = state->gpu()->getContext();
#ifdef SK_GL
GrGLGpu* glGpu = GrBackendApi::kOpenGL == direct->backend()
? static_cast<GrGLGpu*>(state->gpu())
: nullptr;
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));
}
#endif
GrPipeline pipeline(GrScissorTest::kDisabled, SkBlendMode::kPlus,
state->drawOpArgs().writeSwizzle());
std::unique_ptr<GrCCCoverageProcessor> proc;
if (state->caps().shaderCaps()->geometryShaderSupport()) {
proc = std::make_unique<GrGSCoverageProcessor>();
} else {
proc = std::make_unique<GrVSCoverageProcessor>();
}
SkDEBUGCODE(proc->enableDebugBloat(kDebugBloat));
GrOpsRenderPass* renderPass = state->opsRenderPass();
if (!fView->fDoStroke) {
for (int i = 0; i < proc->numSubpasses(); ++i) {
proc->reset(fView->fPrimitiveType, i, rp);
proc->bindPipeline(state, pipeline, this->bounds());
if (PrimitiveType::kCubics == fView->fPrimitiveType ||
PrimitiveType::kConics == fView->fPrimitiveType) {
sk_sp<GrGpuBuffer> instBuff(rp->createBuffer(
fView->fQuadPointInstances.count() * sizeof(QuadPointInstance),
GrGpuBufferType::kVertex, kDynamic_GrAccessPattern,
fView->fQuadPointInstances.begin()));
if (!fView->fQuadPointInstances.empty() && instBuff) {
proc->bindBuffers(renderPass, std::move(instBuff));
proc->drawInstances(renderPass, fView->fQuadPointInstances.count(), 0);
}
} else {
sk_sp<GrGpuBuffer> instBuff(rp->createBuffer(
fView->fTriPointInstances.count() * sizeof(TriPointInstance),
GrGpuBufferType::kVertex, kDynamic_GrAccessPattern,
fView->fTriPointInstances.begin()));
if (!fView->fTriPointInstances.empty() && instBuff) {
proc->bindBuffers(renderPass, std::move(instBuff));
proc->drawInstances(renderPass, fView->fTriPointInstances.count(), 0);
}
}
}
} else if (PrimitiveType::kConics != fView->fPrimitiveType) { // No conic stroke support yet.
GrCCStroker stroker(0,0,0);
SkPaint p;
p.setStyle(SkPaint::kStroke_Style);
p.setStrokeWidth(fView->fStrokeWidth);
p.setStrokeJoin(SkPaint::kMiter_Join);
p.setStrokeMiter(4);
// p.setStrokeCap(SkPaint::kRound_Cap);
stroker.parseDeviceSpaceStroke(fView->fPath, SkPathPriv::PointData(fView->fPath),
SkStrokeRec(p), p.getStrokeWidth(), GrScissorTest::kDisabled,
SkIRect::MakeWH(fView->width(), fView->height()), {0, 0});
GrCCStroker::BatchID batchID = stroker.closeCurrentBatch();
GrOnFlushResourceProvider onFlushRP(direct->priv().drawingManager());
stroker.prepareToDraw(&onFlushRP);
SkIRect ibounds;
this->bounds().roundOut(&ibounds);
stroker.drawStrokes(state, proc.get(), batchID, ibounds);
}
#ifdef SK_GL
if (glGpu) {
direct->resetContext(kMisc_GrGLBackendState);
}
#endif
}
class CCPRGeometryView::Click : public Sample::Click {
public:
Click(int ptIdx) : fPtIdx(ptIdx) {}
void doClick(SkPoint points[]) {
if (fPtIdx >= 0) {
points[fPtIdx] += fCurr - fPrev;
} else {
for (int i = 0; i < 4; ++i) {
points[i] += fCurr - fPrev;
}
}
}
private:
int fPtIdx;
};
Sample::Click* CCPRGeometryView::onFindClickHandler(SkScalar x, SkScalar y, skui::ModifierKey) {
for (int i = 0; i < 4; ++i) {
if (PrimitiveType::kCubics != fPrimitiveType && 2 == i) {
continue;
}
if (fabs(x - fPoints[i].x()) < 20 && fabsf(y - fPoints[i].y()) < 20) {
return new Click(i);
}
}
return new Click(-1);
}
bool CCPRGeometryView::onClick(Sample::Click* click) {
Click* myClick = (Click*)click;
myClick->doClick(fPoints);
this->updateAndInval();
return true;
}
bool CCPRGeometryView::onChar(SkUnichar unichar) {
if (unichar >= '1' && unichar <= '4') {
fPrimitiveType = PrimitiveType(unichar - '1');
if (fPrimitiveType >= PrimitiveType::kWeightedTriangles) {
fPrimitiveType = (PrimitiveType) ((int)fPrimitiveType + 1);
}
this->updateAndInval();
return true;
}
float* valueToScale = nullptr;
if (fDoStroke) {
valueToScale = &fStrokeWidth;
} else if (PrimitiveType::kConics == fPrimitiveType) {
valueToScale = &fConicWeight;
}
if (valueToScale) {
if (unichar == '+') {
*valueToScale *= 2;
this->updateAndInval();
return true;
}
if (unichar == '+' || unichar == '=') {
*valueToScale *= 5/4.f;
this->updateAndInval();
return true;
}
if (unichar == '-') {
*valueToScale *= 4/5.f;
this->updateAndInval();
return true;
}
if (unichar == '_') {
*valueToScale *= .5f;
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;
}
if (unichar == 'S') {
fDoStroke = !fDoStroke;
this->updateAndInval();
}
return false;
}
DEF_SAMPLE(return new CCPRGeometryView;)
#endif // SK_SUPPORT_GPU