ccpr: Various cleanups
Renames GrCCCoverageProcessor::RenderPass to PrimitiveType and handles corners as subpasses instead. Various touchups to coverage processors now that the overhaul is complete. This change should be strictly a refactor. Bug: skia: Change-Id: I52852463330d5ec71fae7e19fadccd9ede8b2c16 Reviewed-on: https://skia-review.googlesource.com/116169 Reviewed-by: Greg Daniel <egdaniel@google.com> Commit-Queue: Chris Dalton <csmartdalton@google.com>
This commit is contained in:
parent
9b685238f6
commit
8dfc70f7e2
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
using TriPointInstance = GrCCCoverageProcessor::TriPointInstance;
|
using TriPointInstance = GrCCCoverageProcessor::TriPointInstance;
|
||||||
using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance;
|
using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance;
|
||||||
using RenderPass = GrCCCoverageProcessor::RenderPass;
|
using PrimitiveType = GrCCCoverageProcessor::PrimitiveType;
|
||||||
|
|
||||||
static constexpr float kDebugBloat = 40;
|
static constexpr float kDebugBloat = 40;
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ private:
|
|||||||
|
|
||||||
void updateGpuData();
|
void updateGpuData();
|
||||||
|
|
||||||
RenderPass fRenderPass = RenderPass::kTriangles;
|
PrimitiveType fPrimitiveType = PrimitiveType::kTriangles;
|
||||||
SkCubicType fCubicType;
|
SkCubicType fCubicType;
|
||||||
SkMatrix fCubicKLM;
|
SkMatrix fCubicKLM;
|
||||||
|
|
||||||
@ -90,7 +90,6 @@ private:
|
|||||||
bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override { return false; }
|
bool onCombineIfPossible(GrOp* other, const GrCaps& caps) override { return false; }
|
||||||
void onPrepare(GrOpFlushState*) override {}
|
void onPrepare(GrOpFlushState*) override {}
|
||||||
void onExecute(GrOpFlushState*) override;
|
void onExecute(GrOpFlushState*) override;
|
||||||
void drawRenderPass(GrOpFlushState*, RenderPass);
|
|
||||||
|
|
||||||
CCPRGeometryView* fView;
|
CCPRGeometryView* fView;
|
||||||
|
|
||||||
@ -149,9 +148,9 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
|
|||||||
|
|
||||||
SkPath outline;
|
SkPath outline;
|
||||||
outline.moveTo(fPoints[0]);
|
outline.moveTo(fPoints[0]);
|
||||||
if (RenderPass::kCubics == fRenderPass) {
|
if (PrimitiveType::kCubics == fPrimitiveType) {
|
||||||
outline.cubicTo(fPoints[1], fPoints[2], fPoints[3]);
|
outline.cubicTo(fPoints[1], fPoints[2], fPoints[3]);
|
||||||
} else if (RenderPass::kQuadratics == fRenderPass) {
|
} else if (PrimitiveType::kQuadratics == fPrimitiveType) {
|
||||||
outline.quadTo(fPoints[1], fPoints[3]);
|
outline.quadTo(fPoints[1], fPoints[3]);
|
||||||
} else {
|
} else {
|
||||||
outline.lineTo(fPoints[1]);
|
outline.lineTo(fPoints[1]);
|
||||||
@ -205,8 +204,8 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
|
|||||||
SkRect::MakeIWH(this->width(), this->height()));
|
SkRect::MakeIWH(this->width(), this->height()));
|
||||||
|
|
||||||
// Add label.
|
// Add label.
|
||||||
caption.appendf("RenderPass_%s", GrCCCoverageProcessor::RenderPassName(fRenderPass));
|
caption.appendf("RenderPass_%s", GrCCCoverageProcessor::PrimitiveTypeName(fPrimitiveType));
|
||||||
if (RenderPass::kCubics == fRenderPass) {
|
if (PrimitiveType::kCubics == fPrimitiveType) {
|
||||||
caption.appendf(" (%s)", SkCubicTypeName(fCubicType));
|
caption.appendf(" (%s)", SkCubicTypeName(fCubicType));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -218,7 +217,7 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
|
|||||||
pointsPaint.setStrokeWidth(8);
|
pointsPaint.setStrokeWidth(8);
|
||||||
pointsPaint.setAntiAlias(true);
|
pointsPaint.setAntiAlias(true);
|
||||||
|
|
||||||
if (RenderPass::kCubics == fRenderPass) {
|
if (PrimitiveType::kCubics == fPrimitiveType) {
|
||||||
int w = this->width(), h = this->height();
|
int w = this->width(), h = this->height();
|
||||||
canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, fPoints, pointsPaint);
|
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[0], SK_ColorYELLOW);
|
||||||
@ -240,7 +239,7 @@ void CCPRGeometryView::updateGpuData() {
|
|||||||
fTriPointInstances.reset();
|
fTriPointInstances.reset();
|
||||||
fQuadPointInstances.reset();
|
fQuadPointInstances.reset();
|
||||||
|
|
||||||
if (RenderPass::kCubics == fRenderPass) {
|
if (PrimitiveType::kCubics == fPrimitiveType) {
|
||||||
double t[2], s[2];
|
double t[2], s[2];
|
||||||
fCubicType = GrPathUtils::getCubicKLM(fPoints, &fCubicKLM, t, s);
|
fCubicType = GrPathUtils::getCubicKLM(fPoints, &fCubicKLM, t, s);
|
||||||
GrCCGeometry geometry;
|
GrCCGeometry geometry;
|
||||||
@ -264,7 +263,7 @@ void CCPRGeometryView::updateGpuData() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (RenderPass::kQuadratics == fRenderPass) {
|
} else if (PrimitiveType::kQuadratics == fPrimitiveType) {
|
||||||
GrCCGeometry geometry;
|
GrCCGeometry geometry;
|
||||||
geometry.beginContour(fPoints[0]);
|
geometry.beginContour(fPoints[0]);
|
||||||
geometry.quadraticTo(fPoints[1], fPoints[3]);
|
geometry.quadraticTo(fPoints[1], fPoints[3]);
|
||||||
@ -290,27 +289,18 @@ void CCPRGeometryView::updateGpuData() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CCPRGeometryView::DrawCoverageCountOp::onExecute(GrOpFlushState* state) {
|
void CCPRGeometryView::DrawCoverageCountOp::onExecute(GrOpFlushState* state) {
|
||||||
this->drawRenderPass(state, fView->fRenderPass);
|
|
||||||
|
|
||||||
RenderPass cornerPass = RenderPass((int)fView->fRenderPass + 1);
|
|
||||||
if (GrCCCoverageProcessor::DoesRenderPass(cornerPass, state->caps())) {
|
|
||||||
this->drawRenderPass(state, cornerPass);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CCPRGeometryView::DrawCoverageCountOp::drawRenderPass(GrOpFlushState* state,
|
|
||||||
RenderPass renderPass) {
|
|
||||||
GrResourceProvider* rp = state->resourceProvider();
|
GrResourceProvider* rp = state->resourceProvider();
|
||||||
GrContext* context = state->gpu()->getContext();
|
GrContext* context = state->gpu()->getContext();
|
||||||
GrGLGpu* glGpu = kOpenGL_GrBackend == context->contextPriv().getBackend()
|
GrGLGpu* glGpu = kOpenGL_GrBackend == context->contextPriv().getBackend()
|
||||||
? static_cast<GrGLGpu*>(state->gpu())
|
? static_cast<GrGLGpu*>(state->gpu())
|
||||||
: nullptr;
|
: nullptr;
|
||||||
|
|
||||||
GrCCCoverageProcessor proc(rp, renderPass, GrCCCoverageProcessor::WindMethod::kCrossProduct);
|
GrCCCoverageProcessor proc(rp, fView->fPrimitiveType,
|
||||||
|
GrCCCoverageProcessor::WindMethod::kCrossProduct);
|
||||||
SkDEBUGCODE(proc.enableDebugBloat(kDebugBloat));
|
SkDEBUGCODE(proc.enableDebugBloat(kDebugBloat));
|
||||||
|
|
||||||
SkSTArray<1, GrMesh> mesh;
|
SkSTArray<1, GrMesh> mesh;
|
||||||
if (GrCCCoverageProcessor::RenderPassIsCubic(renderPass)) {
|
if (PrimitiveType::kCubics == fView->fPrimitiveType) {
|
||||||
sk_sp<GrBuffer> instBuff(rp->createBuffer(
|
sk_sp<GrBuffer> instBuff(rp->createBuffer(
|
||||||
fView->fQuadPointInstances.count() * sizeof(QuadPointInstance),
|
fView->fQuadPointInstances.count() * sizeof(QuadPointInstance),
|
||||||
kVertex_GrBufferType, kDynamic_GrAccessPattern,
|
kVertex_GrBufferType, kDynamic_GrAccessPattern,
|
||||||
@ -341,8 +331,7 @@ void CCPRGeometryView::DrawCoverageCountOp::drawRenderPass(GrOpFlushState* state
|
|||||||
|
|
||||||
if (!mesh.empty()) {
|
if (!mesh.empty()) {
|
||||||
SkASSERT(1 == mesh.count());
|
SkASSERT(1 == mesh.count());
|
||||||
GrGpuRTCommandBuffer* cmdBuff = state->rtCommandBuffer();
|
proc.draw(state, pipeline, mesh.begin(), nullptr, 1, this->bounds());
|
||||||
cmdBuff->draw(pipeline, proc, mesh.begin(), nullptr, 1, this->bounds());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (glGpu) {
|
if (glGpu) {
|
||||||
@ -375,7 +364,7 @@ private:
|
|||||||
|
|
||||||
SkView::Click* CCPRGeometryView::onFindClickHandler(SkScalar x, SkScalar y, unsigned) {
|
SkView::Click* CCPRGeometryView::onFindClickHandler(SkScalar x, SkScalar y, unsigned) {
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
if (RenderPass::kCubics != fRenderPass && 2 == i) {
|
if (PrimitiveType::kCubics != fPrimitiveType && 2 == i) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (fabs(x - fPoints[i].x()) < 20 && fabsf(y - fPoints[i].y()) < 20) {
|
if (fabs(x - fPoints[i].x()) < 20 && fabsf(y - fPoints[i].y()) < 20) {
|
||||||
@ -400,7 +389,7 @@ bool CCPRGeometryView::onQuery(SkEvent* evt) {
|
|||||||
SkUnichar unichar;
|
SkUnichar unichar;
|
||||||
if (SampleCode::CharQ(*evt, &unichar)) {
|
if (SampleCode::CharQ(*evt, &unichar)) {
|
||||||
if (unichar >= '1' && unichar <= '3') {
|
if (unichar >= '1' && unichar <= '3') {
|
||||||
fRenderPass = RenderPass((unichar - '1') * 2);
|
fPrimitiveType = PrimitiveType(unichar - '1');
|
||||||
this->updateAndInval();
|
this->updateAndInval();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
#include "GrCCCoverageProcessor.h"
|
#include "GrCCCoverageProcessor.h"
|
||||||
|
|
||||||
|
#include "GrGpuCommandBuffer.h"
|
||||||
|
#include "GrOpFlushState.h"
|
||||||
#include "SkMakeUnique.h"
|
#include "SkMakeUnique.h"
|
||||||
#include "ccpr/GrCCCubicShader.h"
|
#include "ccpr/GrCCCubicShader.h"
|
||||||
#include "ccpr/GrCCQuadraticShader.h"
|
#include "ccpr/GrCCQuadraticShader.h"
|
||||||
@ -129,7 +131,10 @@ void GrCCCoverageProcessor::Shader::CalcCornerCoverageAttenuation(GrGLSLVertexGe
|
|||||||
|
|
||||||
void GrCCCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&,
|
void GrCCCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&,
|
||||||
GrProcessorKeyBuilder* b) const {
|
GrProcessorKeyBuilder* b) const {
|
||||||
int key = (int)fRenderPass << 2;
|
int key = (int)fPrimitiveType << 3;
|
||||||
|
if (GSSubpass::kCorners == fGSSubpass) {
|
||||||
|
key |= 4;
|
||||||
|
}
|
||||||
if (WindMethod::kInstanceData == fWindMethod) {
|
if (WindMethod::kInstanceData == fWindMethod) {
|
||||||
key |= 2;
|
key |= 2;
|
||||||
}
|
}
|
||||||
@ -146,20 +151,32 @@ void GrCCCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&,
|
|||||||
|
|
||||||
GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGLSLInstance(const GrShaderCaps&) const {
|
GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGLSLInstance(const GrShaderCaps&) const {
|
||||||
std::unique_ptr<Shader> shader;
|
std::unique_ptr<Shader> shader;
|
||||||
switch (fRenderPass) {
|
switch (fPrimitiveType) {
|
||||||
case RenderPass::kTriangles:
|
case PrimitiveType::kTriangles:
|
||||||
case RenderPass::kTriangleCorners:
|
|
||||||
shader = skstd::make_unique<GrCCTriangleShader>();
|
shader = skstd::make_unique<GrCCTriangleShader>();
|
||||||
break;
|
break;
|
||||||
case RenderPass::kQuadratics:
|
case PrimitiveType::kQuadratics:
|
||||||
case RenderPass::kQuadraticCorners:
|
|
||||||
shader = skstd::make_unique<GrCCQuadraticShader>();
|
shader = skstd::make_unique<GrCCQuadraticShader>();
|
||||||
break;
|
break;
|
||||||
case RenderPass::kCubics:
|
case PrimitiveType::kCubics:
|
||||||
case RenderPass::kCubicCorners:
|
|
||||||
shader = skstd::make_unique<GrCCCubicShader>();
|
shader = skstd::make_unique<GrCCCubicShader>();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return Impl::kGeometryShader == fImpl ? this->createGSImpl(std::move(shader))
|
return Impl::kGeometryShader == fImpl ? this->createGSImpl(std::move(shader))
|
||||||
: this->createVSImpl(std::move(shader));
|
: this->createVSImpl(std::move(shader));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GrCCCoverageProcessor::draw(GrOpFlushState* flushState, const GrPipeline& pipeline,
|
||||||
|
const GrMesh meshes[],
|
||||||
|
const GrPipeline::DynamicState dynamicStates[], int meshCount,
|
||||||
|
const SkRect& drawBounds) const {
|
||||||
|
GrGpuRTCommandBuffer* cmdBuff = flushState->rtCommandBuffer();
|
||||||
|
cmdBuff->draw(pipeline, *this, meshes, dynamicStates, meshCount, drawBounds);
|
||||||
|
|
||||||
|
// Geometry shader backend draws primitives in two subpasses.
|
||||||
|
if (Impl::kGeometryShader == fImpl) {
|
||||||
|
SkASSERT(GSSubpass::kHulls == fGSSubpass);
|
||||||
|
GrCCCoverageProcessor cornerProc(*this, GSSubpass::kCorners);
|
||||||
|
cmdBuff->draw(pipeline, cornerProc, meshes, dynamicStates, meshCount, drawBounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "GrCaps.h"
|
#include "GrCaps.h"
|
||||||
#include "GrGeometryProcessor.h"
|
#include "GrGeometryProcessor.h"
|
||||||
|
#include "GrPipeline.h"
|
||||||
#include "GrShaderCaps.h"
|
#include "GrShaderCaps.h"
|
||||||
#include "SkNx.h"
|
#include "SkNx.h"
|
||||||
#include "glsl/GrGLSLGeometryProcessor.h"
|
#include "glsl/GrGLSLGeometryProcessor.h"
|
||||||
@ -18,21 +19,29 @@
|
|||||||
class GrGLSLFPFragmentBuilder;
|
class GrGLSLFPFragmentBuilder;
|
||||||
class GrGLSLVertexGeoBuilder;
|
class GrGLSLVertexGeoBuilder;
|
||||||
class GrMesh;
|
class GrMesh;
|
||||||
|
class GrOpFlushState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is the geometry processor for the simple convex primitive shapes (triangles and closed,
|
* This is the geometry processor for the simple convex primitive shapes (triangles and closed,
|
||||||
* convex bezier curves) from which ccpr paths are composed. The output is a single-channel alpha
|
* convex bezier curves) from which ccpr paths are composed. The output is a single-channel alpha
|
||||||
* value, positive for clockwise shapes and negative for counter-clockwise, that indicates coverage.
|
* value, positive for clockwise shapes and negative for counter-clockwise, that indicates coverage.
|
||||||
*
|
*
|
||||||
* The caller is responsible to execute all render passes for all applicable primitives into a
|
* The caller is responsible to draw all primitives as produced by GrCCGeometry into a cleared,
|
||||||
* cleared, floating point, alpha-only render target using SkBlendMode::kPlus (see RenderPass
|
* floating point, alpha-only render target using SkBlendMode::kPlus. Once all of a path's
|
||||||
* below). Once all of a path's primitives have been drawn, the render target contains a composite
|
* primitives have been drawn, the render target contains a composite coverage count that can then
|
||||||
* coverage count that can then be used to draw the path (see GrCCPathProcessor).
|
* be used to draw the path (see GrCCPathProcessor).
|
||||||
*
|
*
|
||||||
* To draw a renderer pass, see appendMesh below.
|
* To draw primitives, use appendMesh() and draw() (defined below).
|
||||||
*/
|
*/
|
||||||
class GrCCCoverageProcessor : public GrGeometryProcessor {
|
class GrCCCoverageProcessor : public GrGeometryProcessor {
|
||||||
public:
|
public:
|
||||||
|
enum class PrimitiveType {
|
||||||
|
kTriangles,
|
||||||
|
kQuadratics,
|
||||||
|
kCubics,
|
||||||
|
};
|
||||||
|
static const char* PrimitiveTypeName(PrimitiveType);
|
||||||
|
|
||||||
// Defines a single primitive shape with 3 input points (i.e. Triangles and Quadratics).
|
// Defines a single primitive shape with 3 input points (i.e. Triangles and Quadratics).
|
||||||
// X,Y point values are transposed.
|
// X,Y point values are transposed.
|
||||||
struct TriPointInstance {
|
struct TriPointInstance {
|
||||||
@ -54,50 +63,18 @@ public:
|
|||||||
void set(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& trans, float w);
|
void set(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& trans, float w);
|
||||||
};
|
};
|
||||||
|
|
||||||
// All primitive shapes (triangles and closed, convex bezier curves) may require two render
|
|
||||||
// passes: One to draw a rough outline of the shape, and a second pass to touch up the corners.
|
|
||||||
// Check DoesRenderPass() before attempting to draw a given RenderPass. Here we enumerate every
|
|
||||||
// possible render pass needed in order to produce a complete coverage count mask. This is an
|
|
||||||
// exhaustive list of all ccpr coverage shaders.
|
|
||||||
enum class RenderPass {
|
|
||||||
kTriangles,
|
|
||||||
kTriangleCorners,
|
|
||||||
kQuadratics,
|
|
||||||
kQuadraticCorners,
|
|
||||||
kCubics,
|
|
||||||
kCubicCorners
|
|
||||||
};
|
|
||||||
static bool RenderPassIsCubic(RenderPass);
|
|
||||||
static const char* RenderPassName(RenderPass);
|
|
||||||
|
|
||||||
static bool DoesRenderPass(RenderPass renderPass, const GrCaps& caps) {
|
|
||||||
switch (renderPass) {
|
|
||||||
case RenderPass::kTriangles:
|
|
||||||
case RenderPass::kQuadratics:
|
|
||||||
case RenderPass::kCubics:
|
|
||||||
return true;
|
|
||||||
case RenderPass::kTriangleCorners:
|
|
||||||
case RenderPass::kQuadraticCorners:
|
|
||||||
case RenderPass::kCubicCorners:
|
|
||||||
return caps.shaderCaps()->geometryShaderSupport();
|
|
||||||
}
|
|
||||||
SK_ABORT("Invalid RenderPass");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class WindMethod : bool {
|
enum class WindMethod : bool {
|
||||||
kCrossProduct, // Calculate wind = +/-1 by sign of the cross product.
|
kCrossProduct, // Calculate wind = +/-1 by sign of the cross product.
|
||||||
kInstanceData // Instance data provides custom, signed wind values of any magnitude.
|
kInstanceData // Instance data provides custom, signed wind values of any magnitude.
|
||||||
// (For tightly-wound tessellated triangles.)
|
// (For tightly-wound tessellated triangles.)
|
||||||
};
|
};
|
||||||
|
|
||||||
GrCCCoverageProcessor(GrResourceProvider* rp, RenderPass pass, WindMethod windMethod)
|
GrCCCoverageProcessor(GrResourceProvider* rp, PrimitiveType type, WindMethod windMethod)
|
||||||
: INHERITED(kGrCCCoverageProcessor_ClassID)
|
: INHERITED(kGrCCCoverageProcessor_ClassID)
|
||||||
, fRenderPass(pass)
|
, fPrimitiveType(type)
|
||||||
, fWindMethod(windMethod)
|
, fWindMethod(windMethod)
|
||||||
, fImpl(rp->caps()->shaderCaps()->geometryShaderSupport() ? Impl::kGeometryShader
|
, fImpl(rp->caps()->shaderCaps()->geometryShaderSupport() ? Impl::kGeometryShader
|
||||||
: Impl::kVertexShader) {
|
: Impl::kVertexShader) {
|
||||||
SkASSERT(DoesRenderPass(pass, *rp->caps()));
|
|
||||||
if (Impl::kGeometryShader == fImpl) {
|
if (Impl::kGeometryShader == fImpl) {
|
||||||
this->initGS();
|
this->initGS();
|
||||||
} else {
|
} else {
|
||||||
@ -106,7 +83,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GrPrimitiveProcessor overrides.
|
// GrPrimitiveProcessor overrides.
|
||||||
const char* name() const override { return RenderPassName(fRenderPass); }
|
const char* name() const override { return PrimitiveTypeName(fPrimitiveType); }
|
||||||
SkString dumpInfo() const override {
|
SkString dumpInfo() const override {
|
||||||
return SkStringPrintf("%s\n%s", this->name(), this->INHERITED::dumpInfo().c_str());
|
return SkStringPrintf("%s\n%s", this->name(), this->INHERITED::dumpInfo().c_str());
|
||||||
}
|
}
|
||||||
@ -132,6 +109,9 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void draw(GrOpFlushState*, const GrPipeline&, const GrMesh[], const GrPipeline::DynamicState[],
|
||||||
|
int meshCount, const SkRect& drawBounds) const;
|
||||||
|
|
||||||
// The Shader provides code to calculate each pixel's coverage in a RenderPass. It also
|
// The Shader provides code to calculate each pixel's coverage in a RenderPass. It also
|
||||||
// provides details about shape-specific geometry.
|
// provides details about shape-specific geometry.
|
||||||
class Shader {
|
class Shader {
|
||||||
@ -224,13 +204,30 @@ private:
|
|||||||
static constexpr float kAABloatRadius = 0.491111f;
|
static constexpr float kAABloatRadius = 0.491111f;
|
||||||
|
|
||||||
// Number of bezier points for curves, or 3 for triangles.
|
// Number of bezier points for curves, or 3 for triangles.
|
||||||
int numInputPoints() const { return RenderPassIsCubic(fRenderPass) ? 4 : 3; }
|
int numInputPoints() const { return PrimitiveType::kCubics == fPrimitiveType ? 4 : 3; }
|
||||||
|
|
||||||
enum class Impl : bool {
|
enum class Impl : bool {
|
||||||
kGeometryShader,
|
kGeometryShader,
|
||||||
kVertexShader
|
kVertexShader
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Geometry shader backend draws primitives in two subpasses.
|
||||||
|
enum class GSSubpass : bool {
|
||||||
|
kHulls,
|
||||||
|
kCorners
|
||||||
|
};
|
||||||
|
|
||||||
|
GrCCCoverageProcessor(const GrCCCoverageProcessor& proc, GSSubpass subpass)
|
||||||
|
: INHERITED(kGrCCCoverageProcessor_ClassID)
|
||||||
|
, fPrimitiveType(proc.fPrimitiveType)
|
||||||
|
, fWindMethod(proc.fWindMethod)
|
||||||
|
, fImpl(Impl::kGeometryShader)
|
||||||
|
SkDEBUGCODE(, fDebugBloat(proc.fDebugBloat))
|
||||||
|
, fGSSubpass(subpass) {
|
||||||
|
SkASSERT(Impl::kGeometryShader == proc.fImpl);
|
||||||
|
this->initGS();
|
||||||
|
}
|
||||||
|
|
||||||
void initGS();
|
void initGS();
|
||||||
void initVS(GrResourceProvider*);
|
void initVS(GrResourceProvider*);
|
||||||
|
|
||||||
@ -242,20 +239,33 @@ private:
|
|||||||
GrGLSLPrimitiveProcessor* createGSImpl(std::unique_ptr<Shader>) const;
|
GrGLSLPrimitiveProcessor* createGSImpl(std::unique_ptr<Shader>) const;
|
||||||
GrGLSLPrimitiveProcessor* createVSImpl(std::unique_ptr<Shader>) const;
|
GrGLSLPrimitiveProcessor* createVSImpl(std::unique_ptr<Shader>) const;
|
||||||
|
|
||||||
const RenderPass fRenderPass;
|
const PrimitiveType fPrimitiveType;
|
||||||
const WindMethod fWindMethod;
|
const WindMethod fWindMethod;
|
||||||
const Impl fImpl;
|
const Impl fImpl;
|
||||||
SkDEBUGCODE(float fDebugBloat = 0);
|
SkDEBUGCODE(float fDebugBloat = 0);
|
||||||
|
|
||||||
|
// Used by GSImpl.
|
||||||
|
const GSSubpass fGSSubpass = GSSubpass::kHulls;
|
||||||
|
|
||||||
// Used by VSImpl.
|
// Used by VSImpl.
|
||||||
sk_sp<const GrBuffer> fVertexBuffer;
|
sk_sp<const GrBuffer> fVSVertexBuffer;
|
||||||
sk_sp<const GrBuffer> fIndexBuffer;
|
sk_sp<const GrBuffer> fVSIndexBuffer;
|
||||||
int fNumIndicesPerInstance;
|
int fVSNumIndicesPerInstance;
|
||||||
GrPrimitiveType fPrimitiveType;
|
GrPrimitiveType fVSTriangleType;
|
||||||
|
|
||||||
typedef GrGeometryProcessor INHERITED;
|
typedef GrGeometryProcessor INHERITED;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline const char* GrCCCoverageProcessor::PrimitiveTypeName(PrimitiveType type) {
|
||||||
|
switch (type) {
|
||||||
|
case PrimitiveType::kTriangles: return "kTriangles";
|
||||||
|
case PrimitiveType::kQuadratics: return "kQuadratics";
|
||||||
|
case PrimitiveType::kCubics: return "kCubics";
|
||||||
|
}
|
||||||
|
SK_ABORT("Invalid PrimitiveType");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
inline void GrCCCoverageProcessor::TriPointInstance::set(const SkPoint p[3], const Sk2f& trans) {
|
inline void GrCCCoverageProcessor::TriPointInstance::set(const SkPoint p[3], const Sk2f& trans) {
|
||||||
this->set(p[0], p[1], p[2], trans);
|
this->set(p[0], p[1], p[2], trans);
|
||||||
}
|
}
|
||||||
@ -285,32 +295,4 @@ inline void GrCCCoverageProcessor::QuadPointInstance::set(const SkPoint& p0, con
|
|||||||
Sk2f::Store4(this, P0, P1, P2, W);
|
Sk2f::Store4(this, P0, P1, P2, W);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool GrCCCoverageProcessor::RenderPassIsCubic(RenderPass pass) {
|
|
||||||
switch (pass) {
|
|
||||||
case RenderPass::kTriangles:
|
|
||||||
case RenderPass::kTriangleCorners:
|
|
||||||
case RenderPass::kQuadratics:
|
|
||||||
case RenderPass::kQuadraticCorners:
|
|
||||||
return false;
|
|
||||||
case RenderPass::kCubics:
|
|
||||||
case RenderPass::kCubicCorners:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
SK_ABORT("Invalid RenderPass");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const char* GrCCCoverageProcessor::RenderPassName(RenderPass pass) {
|
|
||||||
switch (pass) {
|
|
||||||
case RenderPass::kTriangles: return "kTriangles";
|
|
||||||
case RenderPass::kTriangleCorners: return "kTriangleCorners";
|
|
||||||
case RenderPass::kQuadratics: return "kQuadratics";
|
|
||||||
case RenderPass::kQuadraticCorners: return "kQuadraticCorners";
|
|
||||||
case RenderPass::kCubics: return "kCubics";
|
|
||||||
case RenderPass::kCubicCorners: return "kCubicCorners";
|
|
||||||
}
|
|
||||||
SK_ABORT("Invalid RenderPass");
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -228,6 +228,72 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a conservative raster around a convex quadrilateral that encloses a cubic or quadratic.
|
||||||
|
*/
|
||||||
|
class GrCCCoverageProcessor::GSCurveHullImpl : public GrCCCoverageProcessor::GSImpl {
|
||||||
|
public:
|
||||||
|
GSCurveHullImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
|
||||||
|
|
||||||
|
void onEmitGeometryShader(const GrCCCoverageProcessor&, GrGLSLGeometryBuilder* g,
|
||||||
|
const GrShaderVar& wind, const char* emitVertexFn) const override {
|
||||||
|
const char* hullPts = "pts";
|
||||||
|
fShader->emitSetupCode(g, "pts", wind.c_str(), &hullPts);
|
||||||
|
|
||||||
|
// Visualize the input (convex) quadrilateral as a square. Paying special attention to wind,
|
||||||
|
// we can identify the points by their corresponding corner.
|
||||||
|
//
|
||||||
|
// NOTE: We split the square down the diagonal from top-right to bottom-left, and generate
|
||||||
|
// the hull in two independent invocations. Each invocation designates the corner it will
|
||||||
|
// begin with as top-left.
|
||||||
|
g->codeAppend ("int i = sk_InvocationID * 2;");
|
||||||
|
g->codeAppendf("float2 topleft = %s[i];", hullPts);
|
||||||
|
g->codeAppendf("float2 topright = %s[%s > 0 ? i + 1 : 3 - i];", hullPts, wind.c_str());
|
||||||
|
g->codeAppendf("float2 bottomleft = %s[%s > 0 ? 3 - i : i + 1];", hullPts, wind.c_str());
|
||||||
|
g->codeAppendf("float2 bottomright = %s[2 - i];", hullPts);
|
||||||
|
|
||||||
|
// Determine how much to outset the conservative raster hull from the relevant edges.
|
||||||
|
g->codeAppend ("float2 leftbloat = float2(topleft.y > bottomleft.y ? +bloat : -bloat, "
|
||||||
|
"topleft.x > bottomleft.x ? -bloat : bloat);");
|
||||||
|
g->codeAppend ("float2 upbloat = float2(topright.y > topleft.y ? +bloat : -bloat, "
|
||||||
|
"topright.x > topleft.x ? -bloat : +bloat);");
|
||||||
|
g->codeAppend ("float2 rightbloat = float2(bottomright.y > topright.y ? +bloat : -bloat, "
|
||||||
|
"bottomright.x > topright.x ? -bloat : +bloat);");
|
||||||
|
|
||||||
|
// Here we generate the conservative raster geometry. It is the convex hull of 4 pixel-size
|
||||||
|
// boxes centered on the input points, split evenly between two invocations. This translates
|
||||||
|
// to a polygon with either one, two, or three vertices at each input point, depending on
|
||||||
|
// how sharp the corner is. For more details on conservative raster, see:
|
||||||
|
// https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html
|
||||||
|
g->codeAppendf("bool2 left_up_notequal = notEqual(leftbloat, upbloat);");
|
||||||
|
g->codeAppend ("if (all(left_up_notequal)) {");
|
||||||
|
// The top-left corner will have three conservative raster vertices.
|
||||||
|
// Emit the middle one first to the triangle strip.
|
||||||
|
g->codeAppendf( "%s(topleft + float2(-leftbloat.y, leftbloat.x));", emitVertexFn);
|
||||||
|
g->codeAppend ("}");
|
||||||
|
g->codeAppend ("if (any(left_up_notequal)) {");
|
||||||
|
// Second conservative raster vertex for the top-left corner.
|
||||||
|
g->codeAppendf( "%s(topleft + leftbloat);", emitVertexFn);
|
||||||
|
g->codeAppend ("}");
|
||||||
|
|
||||||
|
// Main interior body of this invocation's half of the hull.
|
||||||
|
g->codeAppendf("%s(topleft + upbloat);", emitVertexFn);
|
||||||
|
g->codeAppendf("%s(bottomleft + leftbloat);", emitVertexFn);
|
||||||
|
g->codeAppendf("%s(topright + upbloat);", emitVertexFn);
|
||||||
|
|
||||||
|
// Remaining two conservative raster vertices for the top-right corner.
|
||||||
|
g->codeAppendf("bool2 up_right_notequal = notEqual(upbloat, rightbloat);");
|
||||||
|
g->codeAppend ("if (any(up_right_notequal)) {");
|
||||||
|
g->codeAppendf( "%s(topright + rightbloat);", emitVertexFn);
|
||||||
|
g->codeAppend ("}");
|
||||||
|
g->codeAppend ("if (all(up_right_notequal)) {");
|
||||||
|
g->codeAppendf( "%s(topright + float2(-upbloat.y, upbloat.x));", emitVertexFn);
|
||||||
|
g->codeAppend ("}");
|
||||||
|
|
||||||
|
g->configure(InputType::kLines, OutputType::kTriangleStrip, 7, 2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates conservative rasters around corners (aka pixel-size boxes) and calculates
|
* Generates conservative rasters around corners (aka pixel-size boxes) and calculates
|
||||||
* coverage and attenuation ramps to fix up the coverage values written by the hulls.
|
* coverage and attenuation ramps to fix up the coverage values written by the hulls.
|
||||||
@ -243,7 +309,7 @@ public:
|
|||||||
const GrShaderVar& wind, const char* emitVertexFn) const override {
|
const GrShaderVar& wind, const char* emitVertexFn) const override {
|
||||||
fShader->emitSetupCode(g, "pts", wind.c_str());
|
fShader->emitSetupCode(g, "pts", wind.c_str());
|
||||||
|
|
||||||
bool isTriangle = RenderPass::kTriangleCorners == proc.fRenderPass;
|
bool isTriangle = PrimitiveType::kTriangles == proc.fPrimitiveType;
|
||||||
g->codeAppendf("int corneridx = sk_InvocationID;");
|
g->codeAppendf("int corneridx = sk_InvocationID;");
|
||||||
if (!isTriangle) {
|
if (!isTriangle) {
|
||||||
g->codeAppendf("corneridx *= %i;", proc.numInputPoints() - 1);
|
g->codeAppendf("corneridx *= %i;", proc.numInputPoints() - 1);
|
||||||
@ -314,75 +380,9 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a conservative raster around a convex quadrilateral that encloses a cubic or quadratic.
|
|
||||||
*/
|
|
||||||
class GrCCCoverageProcessor::GSCurveHullImpl : public GrCCCoverageProcessor::GSImpl {
|
|
||||||
public:
|
|
||||||
GSCurveHullImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
|
|
||||||
|
|
||||||
void onEmitGeometryShader(const GrCCCoverageProcessor&, GrGLSLGeometryBuilder* g,
|
|
||||||
const GrShaderVar& wind, const char* emitVertexFn) const override {
|
|
||||||
const char* hullPts = "pts";
|
|
||||||
fShader->emitSetupCode(g, "pts", wind.c_str(), &hullPts);
|
|
||||||
|
|
||||||
// Visualize the input (convex) quadrilateral as a square. Paying special attention to wind,
|
|
||||||
// we can identify the points by their corresponding corner.
|
|
||||||
//
|
|
||||||
// NOTE: We split the square down the diagonal from top-right to bottom-left, and generate
|
|
||||||
// the hull in two independent invocations. Each invocation designates the corner it will
|
|
||||||
// begin with as top-left.
|
|
||||||
g->codeAppend ("int i = sk_InvocationID * 2;");
|
|
||||||
g->codeAppendf("float2 topleft = %s[i];", hullPts);
|
|
||||||
g->codeAppendf("float2 topright = %s[%s > 0 ? i + 1 : 3 - i];", hullPts, wind.c_str());
|
|
||||||
g->codeAppendf("float2 bottomleft = %s[%s > 0 ? 3 - i : i + 1];", hullPts, wind.c_str());
|
|
||||||
g->codeAppendf("float2 bottomright = %s[2 - i];", hullPts);
|
|
||||||
|
|
||||||
// Determine how much to outset the conservative raster hull from the relevant edges.
|
|
||||||
g->codeAppend ("float2 leftbloat = float2(topleft.y > bottomleft.y ? +bloat : -bloat, "
|
|
||||||
"topleft.x > bottomleft.x ? -bloat : bloat);");
|
|
||||||
g->codeAppend ("float2 upbloat = float2(topright.y > topleft.y ? +bloat : -bloat, "
|
|
||||||
"topright.x > topleft.x ? -bloat : +bloat);");
|
|
||||||
g->codeAppend ("float2 rightbloat = float2(bottomright.y > topright.y ? +bloat : -bloat, "
|
|
||||||
"bottomright.x > topright.x ? -bloat : +bloat);");
|
|
||||||
|
|
||||||
// Here we generate the conservative raster geometry. It is the convex hull of 4 pixel-size
|
|
||||||
// boxes centered on the input points, split evenly between two invocations. This translates
|
|
||||||
// to a polygon with either one, two, or three vertices at each input point, depending on
|
|
||||||
// how sharp the corner is. For more details on conservative raster, see:
|
|
||||||
// https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html
|
|
||||||
g->codeAppendf("bool2 left_up_notequal = notEqual(leftbloat, upbloat);");
|
|
||||||
g->codeAppend ("if (all(left_up_notequal)) {");
|
|
||||||
// The top-left corner will have three conservative raster vertices.
|
|
||||||
// Emit the middle one first to the triangle strip.
|
|
||||||
g->codeAppendf( "%s(topleft + float2(-leftbloat.y, leftbloat.x));", emitVertexFn);
|
|
||||||
g->codeAppend ("}");
|
|
||||||
g->codeAppend ("if (any(left_up_notequal)) {");
|
|
||||||
// Second conservative raster vertex for the top-left corner.
|
|
||||||
g->codeAppendf( "%s(topleft + leftbloat);", emitVertexFn);
|
|
||||||
g->codeAppend ("}");
|
|
||||||
|
|
||||||
// Main interior body of this invocation's half of the hull.
|
|
||||||
g->codeAppendf("%s(topleft + upbloat);", emitVertexFn);
|
|
||||||
g->codeAppendf("%s(bottomleft + leftbloat);", emitVertexFn);
|
|
||||||
g->codeAppendf("%s(topright + upbloat);", emitVertexFn);
|
|
||||||
|
|
||||||
// Remaining two conservative raster vertices for the top-right corner.
|
|
||||||
g->codeAppendf("bool2 up_right_notequal = notEqual(upbloat, rightbloat);");
|
|
||||||
g->codeAppend ("if (any(up_right_notequal)) {");
|
|
||||||
g->codeAppendf( "%s(topright + rightbloat);", emitVertexFn);
|
|
||||||
g->codeAppend ("}");
|
|
||||||
g->codeAppend ("if (all(up_right_notequal)) {");
|
|
||||||
g->codeAppendf( "%s(topright + float2(-upbloat.y, upbloat.x));", emitVertexFn);
|
|
||||||
g->codeAppend ("}");
|
|
||||||
|
|
||||||
g->configure(InputType::kLines, OutputType::kTriangleStrip, 7, 2);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void GrCCCoverageProcessor::initGS() {
|
void GrCCCoverageProcessor::initGS() {
|
||||||
SkASSERT(Impl::kGeometryShader == fImpl);
|
SkASSERT(Impl::kGeometryShader == fImpl);
|
||||||
if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) {
|
if (PrimitiveType::kCubics == fPrimitiveType || WindMethod::kInstanceData == fWindMethod) {
|
||||||
SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints());
|
SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints());
|
||||||
this->addVertexAttrib("x_or_y_values", kFloat4_GrVertexAttribType);
|
this->addVertexAttrib("x_or_y_values", kFloat4_GrVertexAttribType);
|
||||||
SkASSERT(sizeof(QuadPointInstance) == this->getVertexStride() * 2);
|
SkASSERT(sizeof(QuadPointInstance) == this->getVertexStride() * 2);
|
||||||
@ -408,17 +408,11 @@ void GrCCCoverageProcessor::appendGSMesh(GrBuffer* instanceBuffer, int instanceC
|
|||||||
}
|
}
|
||||||
|
|
||||||
GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGSImpl(std::unique_ptr<Shader> shadr) const {
|
GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createGSImpl(std::unique_ptr<Shader> shadr) const {
|
||||||
switch (fRenderPass) {
|
if (GSSubpass::kHulls == fGSSubpass) {
|
||||||
case RenderPass::kTriangles:
|
return (PrimitiveType::kTriangles == fPrimitiveType)
|
||||||
return new GSTriangleHullImpl(std::move(shadr));
|
? (GSImpl*) new GSTriangleHullImpl(std::move(shadr))
|
||||||
case RenderPass::kQuadratics:
|
: (GSImpl*) new GSCurveHullImpl(std::move(shadr));
|
||||||
case RenderPass::kCubics:
|
|
||||||
return new GSCurveHullImpl(std::move(shadr));
|
|
||||||
case RenderPass::kTriangleCorners:
|
|
||||||
case RenderPass::kQuadraticCorners:
|
|
||||||
case RenderPass::kCubicCorners:
|
|
||||||
return new GSCornerImpl(std::move(shadr));
|
|
||||||
}
|
}
|
||||||
SK_ABORT("Invalid RenderPass");
|
SkASSERT(GSSubpass::kCorners == fGSSubpass);
|
||||||
return nullptr;
|
return new GSCornerImpl(std::move(shadr));
|
||||||
}
|
}
|
||||||
|
@ -10,92 +10,30 @@
|
|||||||
#include "GrMesh.h"
|
#include "GrMesh.h"
|
||||||
#include "glsl/GrGLSLVertexGeoBuilder.h"
|
#include "glsl/GrGLSLVertexGeoBuilder.h"
|
||||||
|
|
||||||
using Shader = GrCCCoverageProcessor::Shader;
|
// This class implements the coverage processor with vertex shaders.
|
||||||
|
|
||||||
static constexpr int kAttribIdx_X = 0;
|
|
||||||
static constexpr int kAttribIdx_Y = 1;
|
|
||||||
static constexpr int kAttribIdx_VertexData = 2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class and its subclasses implement the coverage processor with vertex shaders.
|
|
||||||
*/
|
|
||||||
class GrCCCoverageProcessor::VSImpl : public GrGLSLGeometryProcessor {
|
class GrCCCoverageProcessor::VSImpl : public GrGLSLGeometryProcessor {
|
||||||
protected:
|
public:
|
||||||
VSImpl(std::unique_ptr<Shader> shader) : fShader(std::move(shader)) {}
|
VSImpl(std::unique_ptr<Shader> shader, int numSides)
|
||||||
|
: fShader(std::move(shader)), fNumSides(numSides) {}
|
||||||
|
|
||||||
|
private:
|
||||||
void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
|
void setData(const GrGLSLProgramDataManager& pdman, const GrPrimitiveProcessor&,
|
||||||
FPCoordTransformIter&& transformIter) final {
|
FPCoordTransformIter&& transformIter) final {
|
||||||
this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
|
this->setTransformDataHelper(SkMatrix::I(), pdman, &transformIter);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Coverages {
|
void onEmitCode(EmitArgs&, GrGPArgs*) override;
|
||||||
const char* fCoverage = nullptr; // half
|
|
||||||
const char* fAttenuatedCoverage = nullptr; // half2
|
|
||||||
};
|
|
||||||
|
|
||||||
void onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) final {
|
|
||||||
const GrCCCoverageProcessor& proc = args.fGP.cast<GrCCCoverageProcessor>();
|
|
||||||
|
|
||||||
// Vertex shader.
|
|
||||||
GrGLSLVertexBuilder* v = args.fVertBuilder;
|
|
||||||
int numInputPoints = proc.numInputPoints();
|
|
||||||
|
|
||||||
const char* swizzle = (4 == numInputPoints) ? "xyzw" : "xyz";
|
|
||||||
v->codeAppendf("float%ix2 pts = transpose(float2x%i(%s.%s, %s.%s));",
|
|
||||||
numInputPoints, numInputPoints, proc.getAttrib(kAttribIdx_X).fName, swizzle,
|
|
||||||
proc.getAttrib(kAttribIdx_Y).fName, swizzle);
|
|
||||||
|
|
||||||
if (WindMethod::kCrossProduct == proc.fWindMethod) {
|
|
||||||
v->codeAppend ("float area_x2 = determinant(float2x2(pts[0] - pts[1], "
|
|
||||||
"pts[0] - pts[2]));");
|
|
||||||
if (4 == numInputPoints) {
|
|
||||||
v->codeAppend ("area_x2 += determinant(float2x2(pts[0] - pts[2], "
|
|
||||||
"pts[0] - pts[3]));");
|
|
||||||
}
|
|
||||||
v->codeAppend ("half wind = sign(area_x2);");
|
|
||||||
} else {
|
|
||||||
SkASSERT(WindMethod::kInstanceData == proc.fWindMethod);
|
|
||||||
SkASSERT(3 == numInputPoints);
|
|
||||||
SkASSERT(kFloat4_GrVertexAttribType == proc.getAttrib(kAttribIdx_X).fType);
|
|
||||||
v->codeAppendf("half wind = %s.w;", proc.getAttrib(kAttribIdx_X).fName);
|
|
||||||
}
|
|
||||||
|
|
||||||
float bloat = kAABloatRadius;
|
|
||||||
#ifdef SK_DEBUG
|
|
||||||
if (proc.debugBloatEnabled()) {
|
|
||||||
bloat *= proc.debugBloat();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
v->defineConstant("bloat", bloat);
|
|
||||||
|
|
||||||
Coverages coverages;
|
|
||||||
this->emitVertexPosition(proc, v, gpArgs, &coverages);
|
|
||||||
SkASSERT(kFloat2_GrSLType == gpArgs->fPositionVar.getType());
|
|
||||||
|
|
||||||
GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
|
|
||||||
SkString varyingCode;
|
|
||||||
fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kVertToFrag, &varyingCode,
|
|
||||||
gpArgs->fPositionVar.c_str(), coverages.fCoverage,
|
|
||||||
coverages.fAttenuatedCoverage);
|
|
||||||
v->codeAppend(varyingCode.c_str());
|
|
||||||
|
|
||||||
varyingHandler->emitAttributes(proc);
|
|
||||||
SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform());
|
|
||||||
|
|
||||||
// Fragment shader.
|
|
||||||
fShader->emitFragmentCode(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void emitVertexPosition(const GrCCCoverageProcessor&, GrGLSLVertexBuilder*, GrGPArgs*,
|
|
||||||
Coverages* outCoverages) const = 0;
|
|
||||||
|
|
||||||
virtual ~VSImpl() {}
|
|
||||||
|
|
||||||
const std::unique_ptr<Shader> fShader;
|
const std::unique_ptr<Shader> fShader;
|
||||||
|
const int fNumSides;
|
||||||
typedef GrGLSLGeometryProcessor INHERITED;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static constexpr int kAttribIdx_X = 0; // Transposed X values of all input points.
|
||||||
|
static constexpr int kAttribIdx_Y = 1; // Transposed Y values of all input points.
|
||||||
|
static constexpr int kAttribIdx_VertexData = 2;
|
||||||
|
|
||||||
|
// Vertex data tells the shader how to offset vertices for conservative raster, as well as how to
|
||||||
|
// calculate coverage values for corners and edges.
|
||||||
static constexpr int kVertexData_LeftNeighborIdShift = 10;
|
static constexpr int kVertexData_LeftNeighborIdShift = 10;
|
||||||
static constexpr int kVertexData_RightNeighborIdShift = 8;
|
static constexpr int kVertexData_RightNeighborIdShift = 8;
|
||||||
static constexpr int kVertexData_BloatIdxShift = 6;
|
static constexpr int kVertexData_BloatIdxShift = 6;
|
||||||
@ -104,10 +42,6 @@ static constexpr int kVertexData_IsCornerBit = 1 << 4;
|
|||||||
static constexpr int kVertexData_IsEdgeBit = 1 << 3;
|
static constexpr int kVertexData_IsEdgeBit = 1 << 3;
|
||||||
static constexpr int kVertexData_IsHullBit = 1 << 2;
|
static constexpr int kVertexData_IsHullBit = 1 << 2;
|
||||||
|
|
||||||
/**
|
|
||||||
* Vertex data tells the shader how to offset vertices for conservative raster, and how/whether to
|
|
||||||
* calculate initial coverage values for edges. See VSHullAndEdgeImpl.
|
|
||||||
*/
|
|
||||||
static constexpr int32_t pack_vertex_data(int32_t leftNeighborID, int32_t rightNeighborID,
|
static constexpr int32_t pack_vertex_data(int32_t leftNeighborID, int32_t rightNeighborID,
|
||||||
int32_t bloatIdx, int32_t cornerID,
|
int32_t bloatIdx, int32_t cornerID,
|
||||||
int32_t extraData = 0) {
|
int32_t extraData = 0) {
|
||||||
@ -244,7 +178,8 @@ static constexpr uint16_t kTriangleIndicesAsTris[] = {
|
|||||||
|
|
||||||
GR_DECLARE_STATIC_UNIQUE_KEY(gTriangleIndexBufferKey);
|
GR_DECLARE_STATIC_UNIQUE_KEY(gTriangleIndexBufferKey);
|
||||||
|
|
||||||
static constexpr int32_t kHull4Vertices[] = {
|
// Curves, including quadratics, are drawn with a four-sided hull.
|
||||||
|
static constexpr int32_t kCurveVertices[] = {
|
||||||
hull_vertex_data(0, 0, 4),
|
hull_vertex_data(0, 0, 4),
|
||||||
hull_vertex_data(0, 1, 4),
|
hull_vertex_data(0, 1, 4),
|
||||||
hull_vertex_data(0, 2, 4),
|
hull_vertex_data(0, 2, 4),
|
||||||
@ -269,16 +204,16 @@ static constexpr int32_t kHull4Vertices[] = {
|
|||||||
corner_vertex_data(2, 3, 0, 3),
|
corner_vertex_data(2, 3, 0, 3),
|
||||||
};
|
};
|
||||||
|
|
||||||
GR_DECLARE_STATIC_UNIQUE_KEY(gHull4VertexBufferKey);
|
GR_DECLARE_STATIC_UNIQUE_KEY(gCurveVertexBufferKey);
|
||||||
|
|
||||||
static constexpr uint16_t kHull4IndicesAsStrips[] = {
|
static constexpr uint16_t kCurveIndicesAsStrips[] = {
|
||||||
1, 0, 2, 11, 3, 5, 4, kRestartStrip, // First half of the hull (split diagonally).
|
1, 0, 2, 11, 3, 5, 4, kRestartStrip, // First half of the hull (split diagonally).
|
||||||
7, 6, 8, 5, 9, 11, 10, kRestartStrip, // Second half of the hull.
|
7, 6, 8, 5, 9, 11, 10, kRestartStrip, // Second half of the hull.
|
||||||
13, 12, 14, 15, kRestartStrip, // First corner.
|
13, 12, 14, 15, kRestartStrip, // First corner.
|
||||||
17, 16, 18, 19 // Second corner.
|
17, 16, 18, 19 // Final corner.
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr uint16_t kHull4IndicesAsTris[] = {
|
static constexpr uint16_t kCurveIndicesAsTris[] = {
|
||||||
// First half of the hull (split diagonally).
|
// First half of the hull (split diagonally).
|
||||||
1, 0, 2,
|
1, 0, 2,
|
||||||
0, 11, 2,
|
0, 11, 2,
|
||||||
@ -297,243 +232,265 @@ static constexpr uint16_t kHull4IndicesAsTris[] = {
|
|||||||
13, 12, 14,
|
13, 12, 14,
|
||||||
12, 15, 14,
|
12, 15, 14,
|
||||||
|
|
||||||
// Second corner.
|
// Final corner.
|
||||||
17, 16, 18,
|
17, 16, 18,
|
||||||
16, 19, 18,
|
16, 19, 18,
|
||||||
};
|
};
|
||||||
|
|
||||||
GR_DECLARE_STATIC_UNIQUE_KEY(gHull4IndexBufferKey);
|
GR_DECLARE_STATIC_UNIQUE_KEY(gCurveIndexBufferKey);
|
||||||
|
|
||||||
|
// Generates a conservative raster hull around a triangle or curve. For triangles we generate
|
||||||
|
// additional conservative rasters with coverage ramps around the edges and corners.
|
||||||
|
//
|
||||||
|
// Triangles are drawn in three steps: (1) Draw a conservative raster of the entire triangle, with a
|
||||||
|
// coverage of +1. (2) Draw conservative rasters around each edge, with a coverage ramp from -1 to
|
||||||
|
// 0. These edge coverage values convert jagged conservative raster edges into smooth, antialiased
|
||||||
|
// ones. (3) Draw conservative rasters (aka pixel-size boxes) around each corner, replacing the
|
||||||
|
// previous coverage values with ones that ramp to zero in the bloat vertices that fall outside the
|
||||||
|
// triangle.
|
||||||
|
//
|
||||||
|
// Curves are drawn in two separate passes. Here we just draw a conservative raster around the input
|
||||||
|
// points. The Shader takes care of everything else for now. The final curve corners get touched up
|
||||||
|
// in a later step by VSCornerImpl.
|
||||||
|
void GrCCCoverageProcessor::VSImpl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
|
||||||
|
const GrCCCoverageProcessor& proc = args.fGP.cast<GrCCCoverageProcessor>();
|
||||||
|
GrGLSLVertexBuilder* v = args.fVertBuilder;
|
||||||
|
int numInputPoints = proc.numInputPoints();
|
||||||
|
|
||||||
/**
|
const char* swizzle = (4 == numInputPoints) ? "xyzw" : "xyz";
|
||||||
* Generates a conservative raster hull around a triangle or curve. For triangles we generate
|
v->codeAppendf("float%ix2 pts = transpose(float2x%i(%s.%s, %s.%s));",
|
||||||
* additional conservative rasters with coverage ramps around the edges and corners.
|
numInputPoints, numInputPoints, proc.getAttrib(kAttribIdx_X).fName, swizzle,
|
||||||
*
|
proc.getAttrib(kAttribIdx_Y).fName, swizzle);
|
||||||
* Triangles are drawn in three steps: (1) Draw a conservative raster of the entire triangle, with a
|
|
||||||
* coverage of +1. (2) Draw conservative rasters around each edge, with a coverage ramp from -1 to
|
|
||||||
* 0. These edge coverage values convert jagged conservative raster edges into smooth, antialiased
|
|
||||||
* ones. (3) Draw conservative rasters (aka pixel-size boxes) around each corner, replacing the
|
|
||||||
* previous coverage values with ones that ramp to zero in the bloat vertices that fall outside the
|
|
||||||
* triangle.
|
|
||||||
*
|
|
||||||
* Curves are drawn in two separate passes. Here we just draw a conservative raster around the input
|
|
||||||
* points. The Shader takes care of everything else for now. The final curve corners get touched up
|
|
||||||
* in a later step by VSCornerImpl.
|
|
||||||
*/
|
|
||||||
class VSHullAndEdgeImpl : public GrCCCoverageProcessor::VSImpl {
|
|
||||||
public:
|
|
||||||
VSHullAndEdgeImpl(std::unique_ptr<Shader> shader, int numSides)
|
|
||||||
: VSImpl(std::move(shader)), fNumSides(numSides) {}
|
|
||||||
|
|
||||||
void emitVertexPosition(const GrCCCoverageProcessor& proc, GrGLSLVertexBuilder* v,
|
if (WindMethod::kCrossProduct == proc.fWindMethod) {
|
||||||
GrGPArgs* gpArgs, Coverages* outCoverages) const override {
|
v->codeAppend ("float area_x2 = determinant(float2x2(pts[0] - pts[1], "
|
||||||
const char* hullPts = "pts";
|
"pts[0] - pts[2]));");
|
||||||
fShader->emitSetupCode(v, "pts", "wind", &hullPts);
|
if (4 == numInputPoints) {
|
||||||
|
v->codeAppend ("area_x2 += determinant(float2x2(pts[0] - pts[2], "
|
||||||
// Reverse all indices if the wind is counter-clockwise: [0, 1, 2] -> [2, 1, 0].
|
"pts[0] - pts[3]));");
|
||||||
v->codeAppendf("int clockwise_indices = wind > 0 ? %s : 0x%x - %s;",
|
|
||||||
proc.getAttrib(kAttribIdx_VertexData).fName,
|
|
||||||
((fNumSides - 1) << kVertexData_LeftNeighborIdShift) |
|
|
||||||
((fNumSides - 1) << kVertexData_RightNeighborIdShift) |
|
|
||||||
(((1 << kVertexData_RightNeighborIdShift) - 1) ^ 3) |
|
|
||||||
(fNumSides - 1),
|
|
||||||
proc.getAttrib(kAttribIdx_VertexData).fName);
|
|
||||||
|
|
||||||
// Here we generate conservative raster geometry for the input polygon. It is the convex
|
|
||||||
// hull of N pixel-size boxes, one centered on each the input points. Each corner has three
|
|
||||||
// vertices, where one or two may cause degenerate triangles. The vertex data tells us how
|
|
||||||
// to offset each vertex. Triangle edges and corners are also handled here using the same
|
|
||||||
// concept. For more details on conservative raster, see:
|
|
||||||
// https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html
|
|
||||||
v->codeAppendf("float2 corner = %s[clockwise_indices & 3];", hullPts);
|
|
||||||
v->codeAppendf("float2 left = %s[clockwise_indices >> %i];",
|
|
||||||
hullPts, kVertexData_LeftNeighborIdShift);
|
|
||||||
v->codeAppendf("float2 right = %s[(clockwise_indices >> %i) & 3];",
|
|
||||||
hullPts, kVertexData_RightNeighborIdShift);
|
|
||||||
|
|
||||||
v->codeAppend ("float2 leftbloat = sign(corner - left);");
|
|
||||||
v->codeAppend ("leftbloat = float2(0 != leftbloat.y ? leftbloat.y : leftbloat.x, "
|
|
||||||
"0 != leftbloat.x ? -leftbloat.x : -leftbloat.y);");
|
|
||||||
|
|
||||||
v->codeAppend ("float2 rightbloat = sign(right - corner);");
|
|
||||||
v->codeAppend ("rightbloat = float2(0 != rightbloat.y ? rightbloat.y : rightbloat.x, "
|
|
||||||
"0 != rightbloat.x ? -rightbloat.x : -rightbloat.y);");
|
|
||||||
|
|
||||||
v->codeAppend ("bool2 left_right_notequal = notEqual(leftbloat, rightbloat);");
|
|
||||||
|
|
||||||
v->codeAppend ("float2 bloatdir = leftbloat;");
|
|
||||||
|
|
||||||
v->codeAppend ("float2 leftdir = corner - left;");
|
|
||||||
v->codeAppend ("leftdir = (float2(0) != leftdir) ? normalize(leftdir) : float2(1, 0);");
|
|
||||||
|
|
||||||
v->codeAppend ("float2 rightdir = right - corner;");
|
|
||||||
v->codeAppend ("rightdir = (float2(0) != rightdir)"
|
|
||||||
"? normalize(rightdir) : float2(1, 0);");
|
|
||||||
|
|
||||||
v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner?
|
|
||||||
proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit);
|
|
||||||
|
|
||||||
// In corner boxes, all 4 coverage values will not map linearly.
|
|
||||||
// Therefore it is important to align the box so its diagonal shared
|
|
||||||
// edge points out of the triangle, in the direction that ramps to 0.
|
|
||||||
v->codeAppend ( "bloatdir = float2(leftdir.x > rightdir.x ? +1 : -1, "
|
|
||||||
"leftdir.y > rightdir.y ? +1 : -1);");
|
|
||||||
|
|
||||||
// For corner boxes, we hack left_right_notequal to always true. This
|
|
||||||
// in turn causes the upcoming code to always rotate, generating all
|
|
||||||
// 4 vertices of the corner box.
|
|
||||||
v->codeAppendf( "left_right_notequal = bool2(true);");
|
|
||||||
v->codeAppend ("}");
|
|
||||||
|
|
||||||
// At each corner of the polygon, our hull will have either 1, 2, or 3 vertices (or 4 if
|
|
||||||
// it's a corner box). We begin with this corner's first raster vertex (leftbloat), then
|
|
||||||
// continue rotating 90 degrees clockwise until we reach the desired raster vertex for this
|
|
||||||
// invocation. Corners with less than 3 corresponding raster vertices will result in
|
|
||||||
// redundant vertices and degenerate triangles.
|
|
||||||
v->codeAppendf("int bloatidx = (%s >> %i) & 3;",
|
|
||||||
proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_BloatIdxShift);
|
|
||||||
v->codeAppend ("switch (bloatidx) {");
|
|
||||||
v->codeAppend ( "case 3:");
|
|
||||||
// Only corners will have bloatidx=3, and corners always rotate.
|
|
||||||
v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW.
|
|
||||||
// fallthru.
|
|
||||||
v->codeAppend ( "case 2:");
|
|
||||||
v->codeAppendf( "if (all(left_right_notequal)) {");
|
|
||||||
v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW.
|
|
||||||
v->codeAppend ( "}");
|
|
||||||
// fallthru.
|
|
||||||
v->codeAppend ( "case 1:");
|
|
||||||
v->codeAppendf( "if (any(left_right_notequal)) {");
|
|
||||||
v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW.
|
|
||||||
v->codeAppend ( "}");
|
|
||||||
// fallthru.
|
|
||||||
v->codeAppend ("}");
|
|
||||||
|
|
||||||
v->codeAppend ("float2 vertex = corner + bloatdir * bloat;");
|
|
||||||
gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex");
|
|
||||||
|
|
||||||
v->codeAppend ("half left_coverage; {");
|
|
||||||
Shader::CalcEdgeCoverageAtBloatVertex(v, "left", "corner", "bloatdir", "left_coverage");
|
|
||||||
v->codeAppend ("}");
|
|
||||||
|
|
||||||
v->codeAppend ("half right_coverage; {");
|
|
||||||
Shader::CalcEdgeCoverageAtBloatVertex(v, "corner", "right", "bloatdir", "right_coverage");
|
|
||||||
v->codeAppend ("}");
|
|
||||||
|
|
||||||
v->codeAppend ("half attenuation; {");
|
|
||||||
Shader::CalcCornerCoverageAttenuation(v, "leftdir", "rightdir", "attenuation");
|
|
||||||
v->codeAppend ("}");
|
|
||||||
|
|
||||||
// Hulls have a coverage of +1 all around.
|
|
||||||
v->codeAppend ("half coverage = +1;");
|
|
||||||
|
|
||||||
if (3 == fNumSides) {
|
|
||||||
v->codeAppendf("if (0 != (%s & %i)) {", // Are we an edge?
|
|
||||||
proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsEdgeBit);
|
|
||||||
v->codeAppend ( "coverage = left_coverage;");
|
|
||||||
v->codeAppend ("}");
|
|
||||||
|
|
||||||
v->codeAppendf("if (0 != (%s & %i)) {", // Invert coverage?
|
|
||||||
proc.getAttrib(kAttribIdx_VertexData).fName,
|
|
||||||
kVertexData_InvertNegativeCoverageBit);
|
|
||||||
v->codeAppend ( "coverage = -1 - coverage;");
|
|
||||||
v->codeAppend ("}");
|
|
||||||
}
|
}
|
||||||
|
v->codeAppend ("half wind = sign(area_x2);");
|
||||||
// Corner boxes require attenuation.
|
} else {
|
||||||
v->codeAppend ("half2 attenuated_coverage = half2(0, 1);");
|
SkASSERT(WindMethod::kInstanceData == proc.fWindMethod);
|
||||||
|
SkASSERT(3 == numInputPoints);
|
||||||
v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner?
|
SkASSERT(kFloat4_GrVertexAttribType == proc.getAttrib(kAttribIdx_X).fType);
|
||||||
proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit);
|
v->codeAppendf("half wind = %s.w;", proc.getAttrib(kAttribIdx_X).fName);
|
||||||
// We use coverage=-1 to erase what the hull geometry wrote.
|
|
||||||
v->codeAppend ( "coverage = -1;");
|
|
||||||
if (3 == fNumSides) {
|
|
||||||
// Triangle corners also have to erase what the edge geometry wrote.
|
|
||||||
v->codeAppend ("coverage -= left_coverage + right_coverage;");
|
|
||||||
}
|
|
||||||
// The x and y components of "attenuated_coverage" are multiplied
|
|
||||||
// together by the fragment shader. They ramp to 0 with attenuation in
|
|
||||||
// the diagonal that points out of the corner, and linearly from
|
|
||||||
// left-edge coverage to right in the opposite diagonal.
|
|
||||||
// bloatidx=0 is the outermost vertex; the one that has attenuation.
|
|
||||||
v->codeAppend ( "attenuated_coverage = (0 == bloatidx)"
|
|
||||||
"? half2(0, attenuation) : half2(1);");
|
|
||||||
v->codeAppend ( "if (1 == bloatidx || 2 == bloatidx) {");
|
|
||||||
v->codeAppend ( "attenuated_coverage.x += right_coverage;");
|
|
||||||
v->codeAppend ( "}");
|
|
||||||
v->codeAppend ( "if (bloatidx >= 2) {");
|
|
||||||
v->codeAppend ( "attenuated_coverage.x += left_coverage;");
|
|
||||||
v->codeAppend ( "}");
|
|
||||||
v->codeAppend ("}");
|
|
||||||
|
|
||||||
v->codeAppend ("coverage *= wind;");
|
|
||||||
outCoverages->fCoverage = "coverage";
|
|
||||||
|
|
||||||
v->codeAppend ("attenuated_coverage.x *= wind;");
|
|
||||||
outCoverages->fAttenuatedCoverage = "attenuated_coverage";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
float bloat = kAABloatRadius;
|
||||||
const int fNumSides;
|
#ifdef SK_DEBUG
|
||||||
};
|
if (proc.debugBloatEnabled()) {
|
||||||
|
bloat *= proc.debugBloat();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
v->defineConstant("bloat", bloat);
|
||||||
|
|
||||||
|
const char* hullPts = "pts";
|
||||||
|
fShader->emitSetupCode(v, "pts", "wind", &hullPts);
|
||||||
|
|
||||||
|
// Reverse all indices if the wind is counter-clockwise: [0, 1, 2] -> [2, 1, 0].
|
||||||
|
v->codeAppendf("int clockwise_indices = wind > 0 ? %s : 0x%x - %s;",
|
||||||
|
proc.getAttrib(kAttribIdx_VertexData).fName,
|
||||||
|
((fNumSides - 1) << kVertexData_LeftNeighborIdShift) |
|
||||||
|
((fNumSides - 1) << kVertexData_RightNeighborIdShift) |
|
||||||
|
(((1 << kVertexData_RightNeighborIdShift) - 1) ^ 3) |
|
||||||
|
(fNumSides - 1),
|
||||||
|
proc.getAttrib(kAttribIdx_VertexData).fName);
|
||||||
|
|
||||||
|
// Here we generate conservative raster geometry for the input polygon. It is the convex
|
||||||
|
// hull of N pixel-size boxes, one centered on each the input points. Each corner has three
|
||||||
|
// vertices, where one or two may cause degenerate triangles. The vertex data tells us how
|
||||||
|
// to offset each vertex. Triangle edges and corners are also handled here using the same
|
||||||
|
// concept. For more details on conservative raster, see:
|
||||||
|
// https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html
|
||||||
|
v->codeAppendf("float2 corner = %s[clockwise_indices & 3];", hullPts);
|
||||||
|
v->codeAppendf("float2 left = %s[clockwise_indices >> %i];",
|
||||||
|
hullPts, kVertexData_LeftNeighborIdShift);
|
||||||
|
v->codeAppendf("float2 right = %s[(clockwise_indices >> %i) & 3];",
|
||||||
|
hullPts, kVertexData_RightNeighborIdShift);
|
||||||
|
|
||||||
|
v->codeAppend ("float2 leftbloat = sign(corner - left);");
|
||||||
|
v->codeAppend ("leftbloat = float2(0 != leftbloat.y ? leftbloat.y : leftbloat.x, "
|
||||||
|
"0 != leftbloat.x ? -leftbloat.x : -leftbloat.y);");
|
||||||
|
|
||||||
|
v->codeAppend ("float2 rightbloat = sign(right - corner);");
|
||||||
|
v->codeAppend ("rightbloat = float2(0 != rightbloat.y ? rightbloat.y : rightbloat.x, "
|
||||||
|
"0 != rightbloat.x ? -rightbloat.x : -rightbloat.y);");
|
||||||
|
|
||||||
|
v->codeAppend ("bool2 left_right_notequal = notEqual(leftbloat, rightbloat);");
|
||||||
|
|
||||||
|
v->codeAppend ("float2 bloatdir = leftbloat;");
|
||||||
|
|
||||||
|
v->codeAppend ("float2 leftdir = corner - left;");
|
||||||
|
v->codeAppend ("leftdir = (float2(0) != leftdir) ? normalize(leftdir) : float2(1, 0);");
|
||||||
|
|
||||||
|
v->codeAppend ("float2 rightdir = right - corner;");
|
||||||
|
v->codeAppend ("rightdir = (float2(0) != rightdir) ? normalize(rightdir) : float2(1, 0);");
|
||||||
|
|
||||||
|
v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner?
|
||||||
|
proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit);
|
||||||
|
|
||||||
|
// In corner boxes, all 4 coverage values will not map linearly.
|
||||||
|
// Therefore it is important to align the box so its diagonal shared
|
||||||
|
// edge points out of the triangle, in the direction that ramps to 0.
|
||||||
|
v->codeAppend ( "bloatdir = float2(leftdir.x > rightdir.x ? +1 : -1, "
|
||||||
|
"leftdir.y > rightdir.y ? +1 : -1);");
|
||||||
|
|
||||||
|
// For corner boxes, we hack left_right_notequal to always true. This
|
||||||
|
// in turn causes the upcoming code to always rotate, generating all
|
||||||
|
// 4 vertices of the corner box.
|
||||||
|
v->codeAppendf( "left_right_notequal = bool2(true);");
|
||||||
|
v->codeAppend ("}");
|
||||||
|
|
||||||
|
// At each corner of the polygon, our hull will have either 1, 2, or 3 vertices (or 4 if
|
||||||
|
// it's a corner box). We begin with this corner's first raster vertex (leftbloat), then
|
||||||
|
// continue rotating 90 degrees clockwise until we reach the desired raster vertex for this
|
||||||
|
// invocation. Corners with less than 3 corresponding raster vertices will result in
|
||||||
|
// redundant vertices and degenerate triangles.
|
||||||
|
v->codeAppendf("int bloatidx = (%s >> %i) & 3;",
|
||||||
|
proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_BloatIdxShift);
|
||||||
|
v->codeAppend ("switch (bloatidx) {");
|
||||||
|
v->codeAppend ( "case 3:");
|
||||||
|
// Only corners will have bloatidx=3, and corners always rotate.
|
||||||
|
v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW.
|
||||||
|
// fallthru.
|
||||||
|
v->codeAppend ( "case 2:");
|
||||||
|
v->codeAppendf( "if (all(left_right_notequal)) {");
|
||||||
|
v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW.
|
||||||
|
v->codeAppend ( "}");
|
||||||
|
// fallthru.
|
||||||
|
v->codeAppend ( "case 1:");
|
||||||
|
v->codeAppendf( "if (any(left_right_notequal)) {");
|
||||||
|
v->codeAppend ( "bloatdir = float2(-bloatdir.y, +bloatdir.x);"); // 90 deg CW.
|
||||||
|
v->codeAppend ( "}");
|
||||||
|
// fallthru.
|
||||||
|
v->codeAppend ("}");
|
||||||
|
|
||||||
|
v->codeAppend ("float2 vertex = corner + bloatdir * bloat;");
|
||||||
|
gpArgs->fPositionVar.set(kFloat2_GrSLType, "vertex");
|
||||||
|
|
||||||
|
v->codeAppend ("half left_coverage; {");
|
||||||
|
Shader::CalcEdgeCoverageAtBloatVertex(v, "left", "corner", "bloatdir", "left_coverage");
|
||||||
|
v->codeAppend ("}");
|
||||||
|
|
||||||
|
v->codeAppend ("half right_coverage; {");
|
||||||
|
Shader::CalcEdgeCoverageAtBloatVertex(v, "corner", "right", "bloatdir", "right_coverage");
|
||||||
|
v->codeAppend ("}");
|
||||||
|
|
||||||
|
v->codeAppend ("half attenuation; {");
|
||||||
|
Shader::CalcCornerCoverageAttenuation(v, "leftdir", "rightdir", "attenuation");
|
||||||
|
v->codeAppend ("}");
|
||||||
|
|
||||||
|
// Hulls have a coverage of +1 all around.
|
||||||
|
v->codeAppend ("half coverage = +1;");
|
||||||
|
|
||||||
|
if (3 == fNumSides) {
|
||||||
|
v->codeAppendf("if (0 != (%s & %i)) {", // Are we an edge?
|
||||||
|
proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsEdgeBit);
|
||||||
|
v->codeAppend ( "coverage = left_coverage;");
|
||||||
|
v->codeAppend ("}");
|
||||||
|
|
||||||
|
v->codeAppendf("if (0 != (%s & %i)) {", // Invert coverage?
|
||||||
|
proc.getAttrib(kAttribIdx_VertexData).fName,
|
||||||
|
kVertexData_InvertNegativeCoverageBit);
|
||||||
|
v->codeAppend ( "coverage = -1 - coverage;");
|
||||||
|
v->codeAppend ("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Corner boxes require attenuation.
|
||||||
|
v->codeAppend ("half2 attenuated_coverage = half2(0, 1);");
|
||||||
|
|
||||||
|
v->codeAppendf("if (0 != (%s & %i)) {", // Are we a corner?
|
||||||
|
proc.getAttrib(kAttribIdx_VertexData).fName, kVertexData_IsCornerBit);
|
||||||
|
// We use coverage=-1 to erase what the hull geometry wrote.
|
||||||
|
v->codeAppend ( "coverage = -1;");
|
||||||
|
if (3 == fNumSides) {
|
||||||
|
// Triangle corners also have to erase what the edge geometry wrote.
|
||||||
|
v->codeAppend ("coverage -= left_coverage + right_coverage;");
|
||||||
|
}
|
||||||
|
// The x and y components of "attenuated_coverage" are multiplied
|
||||||
|
// together by the fragment shader. They ramp to 0 with attenuation in
|
||||||
|
// the diagonal that points out of the corner, and linearly from
|
||||||
|
// left-edge coverage to right in the opposite diagonal.
|
||||||
|
// bloatidx=0 is the outermost vertex; the one that has attenuation.
|
||||||
|
v->codeAppend ( "attenuated_coverage = (0 == bloatidx)"
|
||||||
|
"? half2(0, attenuation) : half2(1);");
|
||||||
|
v->codeAppend ( "if (1 == bloatidx || 2 == bloatidx) {");
|
||||||
|
v->codeAppend ( "attenuated_coverage.x += right_coverage;");
|
||||||
|
v->codeAppend ( "}");
|
||||||
|
v->codeAppend ( "if (bloatidx >= 2) {");
|
||||||
|
v->codeAppend ( "attenuated_coverage.x += left_coverage;");
|
||||||
|
v->codeAppend ( "}");
|
||||||
|
v->codeAppend ("}");
|
||||||
|
|
||||||
|
GrGLSLVaryingHandler* varyingHandler = args.fVaryingHandler;
|
||||||
|
SkString varyingCode;
|
||||||
|
v->codeAppend ("coverage *= wind;");
|
||||||
|
v->codeAppend ("attenuated_coverage.x *= wind;");
|
||||||
|
fShader->emitVaryings(varyingHandler, GrGLSLVarying::Scope::kVertToFrag, &varyingCode,
|
||||||
|
gpArgs->fPositionVar.c_str(), "coverage", "attenuated_coverage");
|
||||||
|
v->codeAppend(varyingCode.c_str());
|
||||||
|
|
||||||
|
varyingHandler->emitAttributes(proc);
|
||||||
|
SkASSERT(!args.fFPCoordTransformHandler->nextCoordTransform());
|
||||||
|
|
||||||
|
// Fragment shader.
|
||||||
|
fShader->emitFragmentCode(proc, args.fFragBuilder, args.fOutputColor, args.fOutputCoverage);
|
||||||
|
}
|
||||||
|
|
||||||
void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) {
|
void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) {
|
||||||
SkASSERT(Impl::kVertexShader == fImpl);
|
SkASSERT(Impl::kVertexShader == fImpl);
|
||||||
const GrCaps& caps = *rp->caps();
|
const GrCaps& caps = *rp->caps();
|
||||||
|
|
||||||
switch (fRenderPass) {
|
switch (fPrimitiveType) {
|
||||||
case RenderPass::kTriangles: {
|
case PrimitiveType::kTriangles: {
|
||||||
GR_DEFINE_STATIC_UNIQUE_KEY(gTriangleVertexBufferKey);
|
GR_DEFINE_STATIC_UNIQUE_KEY(gTriangleVertexBufferKey);
|
||||||
fVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType,
|
fVSVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType,
|
||||||
sizeof(kTriangleVertices),
|
sizeof(kTriangleVertices),
|
||||||
kTriangleVertices,
|
kTriangleVertices,
|
||||||
gTriangleVertexBufferKey);
|
gTriangleVertexBufferKey);
|
||||||
GR_DEFINE_STATIC_UNIQUE_KEY(gTriangleIndexBufferKey);
|
GR_DEFINE_STATIC_UNIQUE_KEY(gTriangleIndexBufferKey);
|
||||||
if (caps.usePrimitiveRestart()) {
|
if (caps.usePrimitiveRestart()) {
|
||||||
fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
|
fVSIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
|
||||||
sizeof(kTriangleIndicesAsStrips),
|
sizeof(kTriangleIndicesAsStrips),
|
||||||
kTriangleIndicesAsStrips,
|
kTriangleIndicesAsStrips,
|
||||||
gTriangleIndexBufferKey);
|
gTriangleIndexBufferKey);
|
||||||
fNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsStrips);
|
fVSNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsStrips);
|
||||||
} else {
|
} else {
|
||||||
fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
|
fVSIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
|
||||||
sizeof(kTriangleIndicesAsTris),
|
sizeof(kTriangleIndicesAsTris),
|
||||||
kTriangleIndicesAsTris,
|
kTriangleIndicesAsTris,
|
||||||
gTriangleIndexBufferKey);
|
gTriangleIndexBufferKey);
|
||||||
fNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsTris);
|
fVSNumIndicesPerInstance = SK_ARRAY_COUNT(kTriangleIndicesAsTris);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case RenderPass::kQuadratics:
|
case PrimitiveType::kQuadratics:
|
||||||
case RenderPass::kCubics: {
|
case PrimitiveType::kCubics: {
|
||||||
GR_DEFINE_STATIC_UNIQUE_KEY(gHull4VertexBufferKey);
|
GR_DEFINE_STATIC_UNIQUE_KEY(gCurveVertexBufferKey);
|
||||||
fVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType, sizeof(kHull4Vertices),
|
fVSVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType,
|
||||||
kHull4Vertices, gHull4VertexBufferKey);
|
sizeof(kCurveVertices), kCurveVertices,
|
||||||
GR_DEFINE_STATIC_UNIQUE_KEY(gHull4IndexBufferKey);
|
gCurveVertexBufferKey);
|
||||||
|
GR_DEFINE_STATIC_UNIQUE_KEY(gCurveIndexBufferKey);
|
||||||
if (caps.usePrimitiveRestart()) {
|
if (caps.usePrimitiveRestart()) {
|
||||||
fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
|
fVSIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
|
||||||
sizeof(kHull4IndicesAsStrips),
|
sizeof(kCurveIndicesAsStrips),
|
||||||
kHull4IndicesAsStrips,
|
kCurveIndicesAsStrips,
|
||||||
gHull4IndexBufferKey);
|
gCurveIndexBufferKey);
|
||||||
fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4IndicesAsStrips);
|
fVSNumIndicesPerInstance = SK_ARRAY_COUNT(kCurveIndicesAsStrips);
|
||||||
} else {
|
} else {
|
||||||
fIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
|
fVSIndexBuffer = rp->findOrMakeStaticBuffer(kIndex_GrBufferType,
|
||||||
sizeof(kHull4IndicesAsTris),
|
sizeof(kCurveIndicesAsTris),
|
||||||
kHull4IndicesAsTris,
|
kCurveIndicesAsTris,
|
||||||
gHull4IndexBufferKey);
|
gCurveIndexBufferKey);
|
||||||
fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4IndicesAsTris);
|
fVSNumIndicesPerInstance = SK_ARRAY_COUNT(kCurveIndicesAsTris);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case RenderPass::kTriangleCorners:
|
|
||||||
case RenderPass::kQuadraticCorners:
|
|
||||||
case RenderPass::kCubicCorners:
|
|
||||||
SK_ABORT("Corners are not used by VSImpl.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) {
|
if (PrimitiveType::kCubics == fPrimitiveType || WindMethod::kInstanceData == fWindMethod) {
|
||||||
SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints());
|
SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints());
|
||||||
|
|
||||||
SkASSERT(kAttribIdx_X == this->numAttribs());
|
SkASSERT(kAttribIdx_X == this->numAttribs());
|
||||||
@ -563,32 +520,28 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) {
|
|||||||
|
|
||||||
if (caps.usePrimitiveRestart()) {
|
if (caps.usePrimitiveRestart()) {
|
||||||
this->setWillUsePrimitiveRestart();
|
this->setWillUsePrimitiveRestart();
|
||||||
fPrimitiveType = GrPrimitiveType::kTriangleStrip;
|
fVSTriangleType = GrPrimitiveType::kTriangleStrip;
|
||||||
} else {
|
} else {
|
||||||
fPrimitiveType = GrPrimitiveType::kTriangles;
|
fVSTriangleType = GrPrimitiveType::kTriangles;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrCCCoverageProcessor::appendVSMesh(GrBuffer* instanceBuffer, int instanceCount,
|
void GrCCCoverageProcessor::appendVSMesh(GrBuffer* instanceBuffer, int instanceCount,
|
||||||
int baseInstance, SkTArray<GrMesh>* out) const {
|
int baseInstance, SkTArray<GrMesh>* out) const {
|
||||||
SkASSERT(Impl::kVertexShader == fImpl);
|
SkASSERT(Impl::kVertexShader == fImpl);
|
||||||
GrMesh& mesh = out->emplace_back(fPrimitiveType);
|
GrMesh& mesh = out->emplace_back(fVSTriangleType);
|
||||||
mesh.setIndexedInstanced(fIndexBuffer.get(), fNumIndicesPerInstance, instanceBuffer,
|
mesh.setIndexedInstanced(fVSIndexBuffer.get(), fVSNumIndicesPerInstance, instanceBuffer,
|
||||||
instanceCount, baseInstance);
|
instanceCount, baseInstance);
|
||||||
mesh.setVertexData(fVertexBuffer.get(), 0);
|
mesh.setVertexData(fVSVertexBuffer.get(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createVSImpl(std::unique_ptr<Shader> shadr) const {
|
GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createVSImpl(std::unique_ptr<Shader> shadr) const {
|
||||||
switch (fRenderPass) {
|
switch (fPrimitiveType) {
|
||||||
case RenderPass::kTriangles:
|
case PrimitiveType::kTriangles:
|
||||||
return new VSHullAndEdgeImpl(std::move(shadr), 3);
|
return new VSImpl(std::move(shadr), 3);
|
||||||
case RenderPass::kQuadratics:
|
case PrimitiveType::kQuadratics:
|
||||||
case RenderPass::kCubics:
|
case PrimitiveType::kCubics:
|
||||||
return new VSHullAndEdgeImpl(std::move(shadr), 4);
|
return new VSImpl(std::move(shadr), 4);
|
||||||
case RenderPass::kTriangleCorners:
|
|
||||||
case RenderPass::kQuadraticCorners:
|
|
||||||
case RenderPass::kCubicCorners:
|
|
||||||
SK_ABORT("Corners are not used by VSImpl.");
|
|
||||||
}
|
}
|
||||||
SK_ABORT("Invalid RenderPass");
|
SK_ABORT("Invalid RenderPass");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -500,7 +500,7 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
|||||||
|
|
||||||
void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCountBatchID batchID,
|
void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCountBatchID batchID,
|
||||||
const SkIRect& drawBounds) const {
|
const SkIRect& drawBounds) const {
|
||||||
using RenderPass = GrCCCoverageProcessor::RenderPass;
|
using PrimitiveType = GrCCCoverageProcessor::PrimitiveType;
|
||||||
using WindMethod = GrCCCoverageProcessor::WindMethod;
|
using WindMethod = GrCCCoverageProcessor::WindMethod;
|
||||||
|
|
||||||
SkASSERT(fInstanceBuffer);
|
SkASSERT(fInstanceBuffer);
|
||||||
@ -511,54 +511,40 @@ void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCount
|
|||||||
SkBlendMode::kPlus);
|
SkBlendMode::kPlus);
|
||||||
|
|
||||||
if (batchTotalCounts.fTriangles) {
|
if (batchTotalCounts.fTriangles) {
|
||||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangles,
|
this->drawPrimitives(flushState, pipeline, batchID, PrimitiveType::kTriangles,
|
||||||
WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, drawBounds);
|
WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, drawBounds);
|
||||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners,
|
|
||||||
WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles,
|
|
||||||
drawBounds); // Might get skipped.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (batchTotalCounts.fWoundTriangles) {
|
if (batchTotalCounts.fWoundTriangles) {
|
||||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangles,
|
this->drawPrimitives(flushState, pipeline, batchID, PrimitiveType::kTriangles,
|
||||||
WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles,
|
WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles,
|
||||||
drawBounds);
|
drawBounds);
|
||||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners,
|
|
||||||
WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles,
|
|
||||||
drawBounds); // Might get skipped.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (batchTotalCounts.fQuadratics) {
|
if (batchTotalCounts.fQuadratics) {
|
||||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadratics,
|
this->drawPrimitives(flushState, pipeline, batchID, PrimitiveType::kQuadratics,
|
||||||
WindMethod::kCrossProduct, &PrimitiveTallies::fQuadratics, drawBounds);
|
|
||||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadraticCorners,
|
|
||||||
WindMethod::kCrossProduct, &PrimitiveTallies::fQuadratics, drawBounds);
|
WindMethod::kCrossProduct, &PrimitiveTallies::fQuadratics, drawBounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (batchTotalCounts.fCubics) {
|
if (batchTotalCounts.fCubics) {
|
||||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubics,
|
this->drawPrimitives(flushState, pipeline, batchID, PrimitiveType::kCubics,
|
||||||
WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds);
|
|
||||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubicCorners,
|
|
||||||
WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds);
|
WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrCCPathParser::drawRenderPass(GrOpFlushState* flushState, const GrPipeline& pipeline,
|
void GrCCPathParser::drawPrimitives(GrOpFlushState* flushState, const GrPipeline& pipeline,
|
||||||
CoverageCountBatchID batchID,
|
CoverageCountBatchID batchID,
|
||||||
GrCCCoverageProcessor::RenderPass renderPass,
|
GrCCCoverageProcessor::PrimitiveType primitiveType,
|
||||||
GrCCCoverageProcessor::WindMethod windMethod,
|
GrCCCoverageProcessor::WindMethod windMethod,
|
||||||
int PrimitiveTallies::*instanceType,
|
int PrimitiveTallies::*instanceType,
|
||||||
const SkIRect& drawBounds) const {
|
const SkIRect& drawBounds) const {
|
||||||
SkASSERT(pipeline.getScissorState().enabled());
|
SkASSERT(pipeline.getScissorState().enabled());
|
||||||
|
|
||||||
if (!GrCCCoverageProcessor::DoesRenderPass(renderPass, flushState->caps())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't call reset(), as that also resets the reserve count.
|
// Don't call reset(), as that also resets the reserve count.
|
||||||
fMeshesScratchBuffer.pop_back_n(fMeshesScratchBuffer.count());
|
fMeshesScratchBuffer.pop_back_n(fMeshesScratchBuffer.count());
|
||||||
fDynamicStatesScratchBuffer.pop_back_n(fDynamicStatesScratchBuffer.count());
|
fDynamicStatesScratchBuffer.pop_back_n(fDynamicStatesScratchBuffer.count());
|
||||||
|
|
||||||
GrCCCoverageProcessor proc(flushState->resourceProvider(), renderPass, windMethod);
|
GrCCCoverageProcessor proc(flushState->resourceProvider(), primitiveType, windMethod);
|
||||||
|
|
||||||
SkASSERT(batchID > 0);
|
SkASSERT(batchID > 0);
|
||||||
SkASSERT(batchID < fCoverageCountBatches.count());
|
SkASSERT(batchID < fCoverageCountBatches.count());
|
||||||
@ -600,9 +586,8 @@ void GrCCPathParser::drawRenderPass(GrOpFlushState* flushState, const GrPipeline
|
|||||||
SkASSERT(totalInstanceCount == batch.fTotalPrimitiveCounts.*instanceType);
|
SkASSERT(totalInstanceCount == batch.fTotalPrimitiveCounts.*instanceType);
|
||||||
|
|
||||||
if (!fMeshesScratchBuffer.empty()) {
|
if (!fMeshesScratchBuffer.empty()) {
|
||||||
SkASSERT(flushState->rtCommandBuffer());
|
proc.draw(flushState, pipeline, fMeshesScratchBuffer.begin(),
|
||||||
flushState->rtCommandBuffer()->draw(pipeline, proc, fMeshesScratchBuffer.begin(),
|
fDynamicStatesScratchBuffer.begin(), fMeshesScratchBuffer.count(),
|
||||||
fDynamicStatesScratchBuffer.begin(),
|
SkRect::Make(drawBounds));
|
||||||
fMeshesScratchBuffer.count(), SkRect::Make(drawBounds));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -127,8 +127,8 @@ private:
|
|||||||
void parsePath(const SkPath&, const SkPoint* deviceSpacePts);
|
void parsePath(const SkPath&, const SkPoint* deviceSpacePts);
|
||||||
void endContourIfNeeded(bool insideContour);
|
void endContourIfNeeded(bool insideContour);
|
||||||
|
|
||||||
void drawRenderPass(GrOpFlushState*, const GrPipeline&, CoverageCountBatchID,
|
void drawPrimitives(GrOpFlushState*, const GrPipeline&, CoverageCountBatchID,
|
||||||
GrCCCoverageProcessor::RenderPass, GrCCCoverageProcessor::WindMethod,
|
GrCCCoverageProcessor::PrimitiveType, GrCCCoverageProcessor::WindMethod,
|
||||||
int PrimitiveTallies::*instanceType, const SkIRect& drawBounds) const;
|
int PrimitiveTallies::*instanceType, const SkIRect& drawBounds) const;
|
||||||
|
|
||||||
// Staging area for the path being parsed.
|
// Staging area for the path being parsed.
|
||||||
|
Loading…
Reference in New Issue
Block a user