ccpr: Tessellate fans for very large and/or simple paths
This increases CPU work, but reduces overdraw on the GPU as compared to Redbook fanning. Bug: skia: Change-Id: I47239c964261e0014a94266a71223eab0597bfb8 Reviewed-on: https://skia-review.googlesource.com/105203 Reviewed-by: Brian Salomon <bsalomon@google.com> Commit-Queue: Chris Dalton <csmartdalton@google.com>
This commit is contained in:
parent
a58bcf7ef8
commit
4138c972ef
@ -26,8 +26,8 @@
|
||||
#include "gl/GrGLGpu.cpp"
|
||||
#include "ops/GrDrawOp.h"
|
||||
|
||||
using TriangleInstance = GrCCCoverageProcessor::TriangleInstance;
|
||||
using CubicInstance = GrCCCoverageProcessor::CubicInstance;
|
||||
using TriPointInstance = GrCCCoverageProcessor::TriPointInstance;
|
||||
using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance;
|
||||
using RenderPass = GrCCCoverageProcessor::RenderPass;
|
||||
|
||||
static constexpr float kDebugBloat = 40;
|
||||
@ -66,8 +66,8 @@ private:
|
||||
SkPoint fPoints[4] = {
|
||||
{100.05f, 100.05f}, {400.75f, 100.05f}, {400.75f, 300.95f}, {100.05f, 300.95f}};
|
||||
|
||||
SkTArray<TriangleInstance> fTriangleInstances;
|
||||
SkTArray<CubicInstance> fCubicInstances;
|
||||
SkTArray<TriPointInstance> fTriPointInstances;
|
||||
SkTArray<QuadPointInstance> fQuadPointInstances;
|
||||
|
||||
typedef SampleView INHERITED;
|
||||
};
|
||||
@ -190,8 +190,8 @@ void CCPRGeometryView::onDrawContent(SkCanvas* canvas) {
|
||||
}
|
||||
|
||||
void CCPRGeometryView::updateGpuData() {
|
||||
fTriangleInstances.reset();
|
||||
fCubicInstances.reset();
|
||||
fTriPointInstances.reset();
|
||||
fQuadPointInstances.reset();
|
||||
|
||||
if (GrCCCoverageProcessor::RenderPassIsCubic(fRenderPass)) {
|
||||
double t[2], s[2];
|
||||
@ -210,7 +210,7 @@ void CCPRGeometryView::updateGpuData() {
|
||||
ptsIdx += 2;
|
||||
continue;
|
||||
case GrCCGeometry::Verb::kMonotonicCubicTo:
|
||||
fCubicInstances.push_back().set(&geometry.points()[ptsIdx], 0, 0);
|
||||
fQuadPointInstances.push_back().set(&geometry.points()[ptsIdx], 0, 0);
|
||||
ptsIdx += 3;
|
||||
continue;
|
||||
default:
|
||||
@ -234,11 +234,11 @@ void CCPRGeometryView::updateGpuData() {
|
||||
continue;
|
||||
}
|
||||
SkASSERT(GrCCGeometry::Verb::kMonotonicQuadraticTo == verb);
|
||||
fTriangleInstances.push_back().set(&geometry.points()[ptsIdx], Sk2f(0, 0));
|
||||
fTriPointInstances.push_back().set(&geometry.points()[ptsIdx], Sk2f(0, 0));
|
||||
ptsIdx += 2;
|
||||
}
|
||||
} else {
|
||||
fTriangleInstances.push_back().set(fPoints[0], fPoints[1], fPoints[3], Sk2f(0, 0));
|
||||
fTriPointInstances.push_back().set(fPoints[0], fPoints[1], fPoints[3], Sk2f(0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@ -253,27 +253,28 @@ void CCPRGeometryView::Op::onExecute(GrOpFlushState* state) {
|
||||
return;
|
||||
}
|
||||
|
||||
GrCCCoverageProcessor proc(rp, fView->fRenderPass, state->caps());
|
||||
GrCCCoverageProcessor proc(rp, fView->fRenderPass,
|
||||
GrCCCoverageProcessor::WindMethod::kCrossProduct);
|
||||
SkDEBUGCODE(proc.enableDebugVisualizations(kDebugBloat));
|
||||
|
||||
SkSTArray<1, GrMesh> mesh;
|
||||
if (GrCCCoverageProcessor::RenderPassIsCubic(fView->fRenderPass)) {
|
||||
sk_sp<GrBuffer> instBuff(rp->createBuffer(
|
||||
fView->fCubicInstances.count() * sizeof(CubicInstance), kVertex_GrBufferType,
|
||||
kDynamic_GrAccessPattern,
|
||||
fView->fQuadPointInstances.count() * sizeof(QuadPointInstance),
|
||||
kVertex_GrBufferType, kDynamic_GrAccessPattern,
|
||||
GrResourceProvider::kNoPendingIO_Flag | GrResourceProvider::kRequireGpuMemory_Flag,
|
||||
fView->fCubicInstances.begin()));
|
||||
if (!fView->fCubicInstances.empty() && instBuff) {
|
||||
proc.appendMesh(instBuff.get(), fView->fCubicInstances.count(), 0, &mesh);
|
||||
fView->fQuadPointInstances.begin()));
|
||||
if (!fView->fQuadPointInstances.empty() && instBuff) {
|
||||
proc.appendMesh(instBuff.get(), fView->fQuadPointInstances.count(), 0, &mesh);
|
||||
}
|
||||
} else {
|
||||
sk_sp<GrBuffer> instBuff(rp->createBuffer(
|
||||
fView->fTriangleInstances.count() * sizeof(TriangleInstance), kVertex_GrBufferType,
|
||||
fView->fTriPointInstances.count() * sizeof(TriPointInstance), kVertex_GrBufferType,
|
||||
kDynamic_GrAccessPattern,
|
||||
GrResourceProvider::kNoPendingIO_Flag | GrResourceProvider::kRequireGpuMemory_Flag,
|
||||
fView->fTriangleInstances.begin()));
|
||||
if (!fView->fTriangleInstances.empty() && instBuff) {
|
||||
proc.appendMesh(instBuff.get(), fView->fTriangleInstances.count(), 0, &mesh);
|
||||
fView->fTriPointInstances.begin()));
|
||||
if (!fView->fTriPointInstances.empty() && instBuff) {
|
||||
proc.appendMesh(instBuff.get(), fView->fTriPointInstances.count(), 0, &mesh);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2359,6 +2359,7 @@ int PathToVertices(const SkPath& path, SkScalar tolerance, const SkRect& clipBou
|
||||
GrTessellator::WindingVertex** verts) {
|
||||
int contourCnt = get_contour_count(path, tolerance);
|
||||
if (contourCnt <= 0) {
|
||||
*verts = nullptr;
|
||||
return 0;
|
||||
}
|
||||
SkArenaAlloc alloc(kArenaChunkSize);
|
||||
|
@ -66,8 +66,11 @@ int GrCCCoverageProcessor::Shader::DefineSoftSampleLocations(GrGLSLPPFragmentBui
|
||||
|
||||
void GrCCCoverageProcessor::getGLSLProcessorKey(const GrShaderCaps&,
|
||||
GrProcessorKeyBuilder* b) const {
|
||||
int key = (int)fRenderPass << 1;
|
||||
if (Impl::kGeometryShader == fImpl) {
|
||||
int key = (int)fRenderPass << 2;
|
||||
if (WindMethod::kInstanceData == fWindMethod) {
|
||||
key |= 2;
|
||||
}
|
||||
if (Impl::kVertexShader == fImpl) {
|
||||
key |= 1;
|
||||
}
|
||||
#ifdef SK_DEBUG
|
||||
|
@ -33,8 +33,9 @@ class GrMesh;
|
||||
*/
|
||||
class GrCCCoverageProcessor : public GrGeometryProcessor {
|
||||
public:
|
||||
// Defines a single triangle or closed quadratic bezier, with transposed x,y point values.
|
||||
struct TriangleInstance {
|
||||
// Defines a single primitive shape with 3 input points (i.e. Triangles and Quadratics).
|
||||
// X,Y point values are transposed.
|
||||
struct TriPointInstance {
|
||||
float fX[3];
|
||||
float fY[3];
|
||||
|
||||
@ -42,12 +43,15 @@ public:
|
||||
void set(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& trans);
|
||||
};
|
||||
|
||||
// Defines a single closed cubic bezier, with transposed x,y point values.
|
||||
struct CubicInstance {
|
||||
// Defines a single primitive shape with 4 input points, or 3 input points plus a W parameter
|
||||
// duplicated in both 4th components (i.e. Cubics or Triangles with a custom winding number).
|
||||
// X,Y point values are transposed.
|
||||
struct QuadPointInstance {
|
||||
float fX[4];
|
||||
float fY[4];
|
||||
|
||||
void set(const SkPoint[4], float dx, float dy);
|
||||
void set(const SkPoint&, const SkPoint&, const SkPoint&, const Sk2f& trans, float w);
|
||||
};
|
||||
|
||||
// All primitive shapes (triangles and closed, convex bezier curves) require more than one
|
||||
@ -93,24 +97,29 @@ public:
|
||||
caps.shaderCaps()->geometryShaderSupport();
|
||||
}
|
||||
|
||||
GrCCCoverageProcessor(GrResourceProvider* rp, RenderPass pass, const GrCaps& caps)
|
||||
enum class WindMethod : bool {
|
||||
kCrossProduct, // Calculate wind = +/-1 by sign of the cross product.
|
||||
kInstanceData // Instance data provides custom, signed wind values of any magnitude.
|
||||
// (For tightly-wound tessellated triangles.)
|
||||
};
|
||||
|
||||
GrCCCoverageProcessor(GrResourceProvider* rp, RenderPass pass, WindMethod windMethod)
|
||||
: INHERITED(kGrCCCoverageProcessor_ClassID)
|
||||
, fRenderPass(pass)
|
||||
, fImpl(caps.shaderCaps()->geometryShaderSupport() ? Impl::kGeometryShader
|
||||
: Impl::kVertexShader) {
|
||||
SkASSERT(DoesRenderPass(pass, caps));
|
||||
, fWindMethod(windMethod)
|
||||
, fImpl(rp->caps()->shaderCaps()->geometryShaderSupport() ? Impl::kGeometryShader
|
||||
: Impl::kVertexShader) {
|
||||
SkASSERT(DoesRenderPass(pass, *rp->caps()));
|
||||
if (Impl::kGeometryShader == fImpl) {
|
||||
this->initGS();
|
||||
} else {
|
||||
this->initVS(rp, caps);
|
||||
this->initVS(rp);
|
||||
}
|
||||
}
|
||||
|
||||
// Appends a GrMesh that will draw the provided instances. The instanceBuffer must be an array
|
||||
// of either TriangleInstance or CubicInstance, depending on this processor's RendererPass, with
|
||||
// coordinates in the desired shape's final atlas-space position.
|
||||
//
|
||||
// NOTE: Quadratics use TriangleInstance since both have 3 points.
|
||||
// of either TriPointInstance or QuadPointInstance, depending on this processor's RendererPass,
|
||||
// with coordinates in the desired shape's final atlas-space position.
|
||||
void appendMesh(GrBuffer* instanceBuffer, int instanceCount, int baseInstance,
|
||||
SkTArray<GrMesh>* out) {
|
||||
if (Impl::kGeometryShader == fImpl) {
|
||||
@ -227,7 +236,7 @@ private:
|
||||
};
|
||||
|
||||
void initGS();
|
||||
void initVS(GrResourceProvider*, const GrCaps&);
|
||||
void initVS(GrResourceProvider*);
|
||||
|
||||
void appendGSMesh(GrBuffer* instanceBuffer, int instanceCount, int baseInstance,
|
||||
SkTArray<GrMesh>* out) const;
|
||||
@ -238,6 +247,7 @@ private:
|
||||
GrGLSLPrimitiveProcessor* createVSImpl(std::unique_ptr<Shader>) const;
|
||||
|
||||
const RenderPass fRenderPass;
|
||||
const WindMethod fWindMethod;
|
||||
const Impl fImpl;
|
||||
SkDEBUGCODE(float fDebugBloat = 0);
|
||||
|
||||
@ -250,11 +260,11 @@ private:
|
||||
typedef GrGeometryProcessor INHERITED;
|
||||
};
|
||||
|
||||
inline void GrCCCoverageProcessor::TriangleInstance::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);
|
||||
}
|
||||
|
||||
inline void GrCCCoverageProcessor::TriangleInstance::set(const SkPoint& p0, const SkPoint& p1,
|
||||
inline void GrCCCoverageProcessor::TriPointInstance::set(const SkPoint& p0, const SkPoint& p1,
|
||||
const SkPoint& p2, const Sk2f& trans) {
|
||||
Sk2f P0 = Sk2f::Load(&p0) + trans;
|
||||
Sk2f P1 = Sk2f::Load(&p1) + trans;
|
||||
@ -262,13 +272,23 @@ inline void GrCCCoverageProcessor::TriangleInstance::set(const SkPoint& p0, cons
|
||||
Sk2f::Store3(this, P0, P1, P2);
|
||||
}
|
||||
|
||||
inline void GrCCCoverageProcessor::CubicInstance::set(const SkPoint p[4], float dx, float dy) {
|
||||
inline void GrCCCoverageProcessor::QuadPointInstance::set(const SkPoint p[4], float dx, float dy) {
|
||||
Sk4f X,Y;
|
||||
Sk4f::Load2(p, &X, &Y);
|
||||
(X + dx).store(&fX);
|
||||
(Y + dy).store(&fY);
|
||||
}
|
||||
|
||||
inline void GrCCCoverageProcessor::QuadPointInstance::set(const SkPoint& p0, const SkPoint& p1,
|
||||
const SkPoint& p2, const Sk2f& trans,
|
||||
float w) {
|
||||
Sk2f P0 = Sk2f::Load(&p0) + trans;
|
||||
Sk2f P1 = Sk2f::Load(&p1) + trans;
|
||||
Sk2f P2 = Sk2f::Load(&p2) + trans;
|
||||
Sk2f W = Sk2f(w);
|
||||
Sk2f::Store4(this, P0, P1, P2, W);
|
||||
}
|
||||
|
||||
inline bool GrCCCoverageProcessor::RenderPassIsCubic(RenderPass pass) {
|
||||
switch (pass) {
|
||||
case RenderPass::kTriangleHulls:
|
||||
|
@ -31,7 +31,7 @@ protected:
|
||||
|
||||
// The vertex shader simply forwards transposed x or y values to the geometry shader.
|
||||
SkASSERT(1 == proc.numAttribs());
|
||||
gpArgs->fPositionVar.set(4 == proc.numInputPoints() ? kFloat4_GrSLType : kFloat3_GrSLType,
|
||||
gpArgs->fPositionVar.set(GrVertexAttribTypeToSLType(proc.getAttrib(0).fType),
|
||||
proc.getAttrib(0).fName);
|
||||
|
||||
// Geometry shader.
|
||||
@ -57,11 +57,20 @@ protected:
|
||||
|
||||
GrShaderVar wind("wind", kHalf_GrSLType);
|
||||
g->declareGlobal(wind);
|
||||
g->codeAppend ("float area_x2 = determinant(float2x2(pts[0] - pts[1], pts[0] - pts[2]));");
|
||||
if (4 == numInputPoints) {
|
||||
g->codeAppend ("area_x2 += determinant(float2x2(pts[0] - pts[2], pts[0] - pts[3]));");
|
||||
if (WindMethod::kCrossProduct == proc.fWindMethod) {
|
||||
g->codeAppend ("float area_x2 = determinant(float2x2(pts[0] - pts[1], "
|
||||
"pts[0] - pts[2]));");
|
||||
if (4 == numInputPoints) {
|
||||
g->codeAppend ("area_x2 += determinant(float2x2(pts[0] - pts[2], "
|
||||
"pts[0] - pts[3]));");
|
||||
}
|
||||
g->codeAppendf("%s = sign(area_x2);", wind.c_str());
|
||||
} else {
|
||||
SkASSERT(WindMethod::kInstanceData == proc.fWindMethod);
|
||||
SkASSERT(3 == numInputPoints);
|
||||
SkASSERT(kFloat4_GrVertexAttribType == proc.getAttrib(0).fType);
|
||||
g->codeAppendf("%s = sk_in[0].sk_Position.w;", wind.c_str());
|
||||
}
|
||||
g->codeAppendf("%s = sign(area_x2);", wind.c_str());
|
||||
|
||||
SkString emitVertexFn;
|
||||
SkSTArray<2, GrShaderVar> emitArgs;
|
||||
@ -322,12 +331,17 @@ private:
|
||||
|
||||
void GrCCCoverageProcessor::initGS() {
|
||||
SkASSERT(Impl::kGeometryShader == fImpl);
|
||||
if (RenderPassIsCubic(fRenderPass)) {
|
||||
this->addVertexAttrib("x_or_y_values", kFloat4_GrVertexAttribType); // (See appendMesh.)
|
||||
SkASSERT(sizeof(CubicInstance) == this->getVertexStride() * 2);
|
||||
if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) {
|
||||
SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints());
|
||||
this->addVertexAttrib("x_or_y_values", kFloat4_GrVertexAttribType);
|
||||
SkASSERT(sizeof(QuadPointInstance) == this->getVertexStride() * 2);
|
||||
SkASSERT(offsetof(QuadPointInstance, fY) == this->getVertexStride());
|
||||
GR_STATIC_ASSERT(0 == offsetof(QuadPointInstance, fX));
|
||||
} else {
|
||||
this->addVertexAttrib("x_or_y_values", kFloat3_GrVertexAttribType); // (See appendMesh.)
|
||||
SkASSERT(sizeof(TriangleInstance) == this->getVertexStride() * 2);
|
||||
this->addVertexAttrib("x_or_y_values", kFloat3_GrVertexAttribType);
|
||||
SkASSERT(sizeof(TriPointInstance) == this->getVertexStride() * 2);
|
||||
SkASSERT(offsetof(TriPointInstance, fY) == this->getVertexStride());
|
||||
GR_STATIC_ASSERT(0 == offsetof(TriPointInstance, fX));
|
||||
}
|
||||
this->setWillUseGeoShader();
|
||||
}
|
||||
|
@ -35,15 +35,25 @@ protected:
|
||||
GrGLSLVertexBuilder* v = args.fVertBuilder;
|
||||
int numInputPoints = proc.numInputPoints();
|
||||
|
||||
v->codeAppendf("float%ix2 pts = transpose(float2x%i(%s, %s));",
|
||||
numInputPoints, numInputPoints, proc.getAttrib(kAttribIdx_X).fName,
|
||||
proc.getAttrib(kAttribIdx_Y).fName);
|
||||
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);
|
||||
|
||||
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]));");
|
||||
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);
|
||||
}
|
||||
v->codeAppend ("half wind = sign(area_x2);");
|
||||
|
||||
float bloat = kAABloatRadius;
|
||||
#ifdef SK_DEBUG
|
||||
@ -340,17 +350,9 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void GrCCCoverageProcessor::initVS(GrResourceProvider* rp, const GrCaps& caps) {
|
||||
void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) {
|
||||
SkASSERT(Impl::kVertexShader == fImpl);
|
||||
|
||||
GrVertexAttribType inputPtsType = RenderPassIsCubic(fRenderPass) ?
|
||||
kFloat4_GrVertexAttribType : kFloat3_GrVertexAttribType;
|
||||
|
||||
SkASSERT(kAttribIdx_X == this->numAttribs());
|
||||
this->addInstanceAttrib("X", inputPtsType);
|
||||
|
||||
SkASSERT(kAttribIdx_Y == this->numAttribs());
|
||||
this->addInstanceAttrib("Y", inputPtsType);
|
||||
const GrCaps& caps = *rp->caps();
|
||||
|
||||
switch (fRenderPass) {
|
||||
case RenderPass::kTriangleHulls: {
|
||||
@ -373,8 +375,6 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp, const GrCaps& caps) {
|
||||
gHull3AndEdgeIndexBufferKey);
|
||||
fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull3AndEdgeIndicesAsTris);
|
||||
}
|
||||
SkASSERT(kAttribIdx_VertexData == this->numAttribs());
|
||||
this->addVertexAttrib("vertexdata", kInt_GrVertexAttribType);
|
||||
break;
|
||||
}
|
||||
case RenderPass::kQuadraticHulls:
|
||||
@ -396,8 +396,6 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp, const GrCaps& caps) {
|
||||
gHull4IndexBufferKey);
|
||||
fNumIndicesPerInstance = SK_ARRAY_COUNT(kHull4IndicesAsTris);
|
||||
}
|
||||
SkASSERT(kAttribIdx_VertexData == this->numAttribs());
|
||||
this->addVertexAttrib("vertexdata", kInt_GrVertexAttribType);
|
||||
break;
|
||||
}
|
||||
case RenderPass::kTriangleEdges:
|
||||
@ -427,20 +425,36 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp, const GrCaps& caps) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef SK_DEBUG
|
||||
if (RenderPassIsCubic(fRenderPass)) {
|
||||
SkASSERT(offsetof(CubicInstance, fX) == this->getAttrib(kAttribIdx_X).fOffsetInRecord);
|
||||
SkASSERT(offsetof(CubicInstance, fY) == this->getAttrib(kAttribIdx_Y).fOffsetInRecord);
|
||||
SkASSERT(sizeof(CubicInstance) == this->getInstanceStride());
|
||||
if (RenderPassIsCubic(fRenderPass) || WindMethod::kInstanceData == fWindMethod) {
|
||||
SkASSERT(WindMethod::kCrossProduct == fWindMethod || 3 == this->numInputPoints());
|
||||
|
||||
SkASSERT(kAttribIdx_X == this->numAttribs());
|
||||
this->addInstanceAttrib("X", kFloat4_GrVertexAttribType);
|
||||
|
||||
SkASSERT(kAttribIdx_Y == this->numAttribs());
|
||||
this->addInstanceAttrib("Y", kFloat4_GrVertexAttribType);
|
||||
|
||||
SkASSERT(offsetof(QuadPointInstance, fX) == this->getAttrib(kAttribIdx_X).fOffsetInRecord);
|
||||
SkASSERT(offsetof(QuadPointInstance, fY) == this->getAttrib(kAttribIdx_Y).fOffsetInRecord);
|
||||
SkASSERT(sizeof(QuadPointInstance) == this->getInstanceStride());
|
||||
} else {
|
||||
SkASSERT(offsetof(TriangleInstance, fX) == this->getAttrib(kAttribIdx_X).fOffsetInRecord);
|
||||
SkASSERT(offsetof(TriangleInstance, fY) == this->getAttrib(kAttribIdx_Y).fOffsetInRecord);
|
||||
SkASSERT(sizeof(TriangleInstance) == this->getInstanceStride());
|
||||
SkASSERT(kAttribIdx_X == this->numAttribs());
|
||||
this->addInstanceAttrib("X", kFloat3_GrVertexAttribType);
|
||||
|
||||
SkASSERT(kAttribIdx_Y == this->numAttribs());
|
||||
this->addInstanceAttrib("Y", kFloat3_GrVertexAttribType);
|
||||
|
||||
SkASSERT(offsetof(TriPointInstance, fX) == this->getAttrib(kAttribIdx_X).fOffsetInRecord);
|
||||
SkASSERT(offsetof(TriPointInstance, fY) == this->getAttrib(kAttribIdx_Y).fOffsetInRecord);
|
||||
SkASSERT(sizeof(TriPointInstance) == this->getInstanceStride());
|
||||
}
|
||||
|
||||
if (fVertexBuffer) {
|
||||
SkASSERT(kAttribIdx_VertexData == this->numAttribs());
|
||||
this->addVertexAttrib("vertexdata", kInt_GrVertexAttribType);
|
||||
|
||||
SkASSERT(sizeof(int32_t) == this->getVertexStride());
|
||||
}
|
||||
#endif
|
||||
|
||||
if (caps.usePrimitiveRestart()) {
|
||||
this->setWillUsePrimitiveRestart();
|
||||
|
@ -30,7 +30,7 @@ void GrCCGeometry::beginContour(const SkPoint& devPt) {
|
||||
|
||||
// Store the current verb count in the fTriangles field for now. When we close the contour we
|
||||
// will use this value to calculate the actual number of triangles in its fan.
|
||||
fCurrContourTallies = {fVerbs.count(), 0, 0};
|
||||
fCurrContourTallies = {fVerbs.count(), 0, 0, 0};
|
||||
|
||||
fPoints.push_back(devPt);
|
||||
fVerbs.push_back(Verb::kBeginContour);
|
||||
|
@ -35,9 +35,10 @@ public:
|
||||
kEndOpenContour // endPt != startPt.
|
||||
};
|
||||
|
||||
// These tallies track numbers of CCPR primitives are required to draw a contour.
|
||||
// These tallies track numbers of CCPR primitives that are required to draw a contour.
|
||||
struct PrimitiveTallies {
|
||||
int fTriangles; // Number of triangles in the contour's fan.
|
||||
int fWoundTriangles; // Triangles (from the tessellator) whose winding magnitude > 1.
|
||||
int fQuadratics;
|
||||
int fCubics;
|
||||
|
||||
@ -115,11 +116,11 @@ private:
|
||||
int maxSubdivisions = kMaxSubdivionsPerCubicSection);
|
||||
|
||||
// Transient state used while building a contour.
|
||||
SkPoint fCurrAnchorPoint;
|
||||
SkPoint fCurrFanPoint;
|
||||
PrimitiveTallies fCurrContourTallies;
|
||||
SkCubicType fCurrCubicType;
|
||||
SkDEBUGCODE(bool fBuildingContour = false);
|
||||
SkPoint fCurrAnchorPoint;
|
||||
SkPoint fCurrFanPoint;
|
||||
PrimitiveTallies fCurrContourTallies;
|
||||
SkCubicType fCurrCubicType;
|
||||
SkDEBUGCODE(bool fBuildingContour = false);
|
||||
|
||||
// TODO: These points could eventually be written directly to block-allocated GPU buffers.
|
||||
SkSTArray<128, SkPoint, true> fPoints;
|
||||
@ -128,6 +129,7 @@ private:
|
||||
|
||||
inline void GrCCGeometry::PrimitiveTallies::operator+=(const PrimitiveTallies& b) {
|
||||
fTriangles += b.fTriangles;
|
||||
fWoundTriangles += b.fWoundTriangles;
|
||||
fQuadratics += b.fQuadratics;
|
||||
fCubics += b.fCubics;
|
||||
}
|
||||
@ -135,12 +137,14 @@ inline void GrCCGeometry::PrimitiveTallies::operator+=(const PrimitiveTallies& b
|
||||
GrCCGeometry::PrimitiveTallies
|
||||
inline GrCCGeometry::PrimitiveTallies::operator-(const PrimitiveTallies& b) const {
|
||||
return {fTriangles - b.fTriangles,
|
||||
fWoundTriangles - b.fWoundTriangles,
|
||||
fQuadratics - b.fQuadratics,
|
||||
fCubics - b.fCubics};
|
||||
}
|
||||
|
||||
inline bool GrCCGeometry::PrimitiveTallies::operator==(const PrimitiveTallies& b) {
|
||||
return fTriangles == b.fTriangles && fQuadratics == b.fQuadratics && fCubics == b.fCubics;
|
||||
return fTriangles == b.fTriangles && fWoundTriangles == b.fWoundTriangles &&
|
||||
fQuadratics == b.fQuadratics && fCubics == b.fCubics;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -17,8 +17,8 @@
|
||||
#include "SkPoint.h"
|
||||
#include "ccpr/GrCCGeometry.h"
|
||||
|
||||
using TriangleInstance = GrCCCoverageProcessor::TriangleInstance;
|
||||
using CubicInstance = GrCCCoverageProcessor::CubicInstance;
|
||||
using TriPointInstance = GrCCCoverageProcessor::TriPointInstance;
|
||||
using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance;
|
||||
|
||||
GrCCPathParser::GrCCPathParser(int maxTotalPaths, int maxPathPoints, int numSkPoints,
|
||||
int numSkVerbs)
|
||||
@ -32,7 +32,8 @@ GrCCPathParser::GrCCPathParser(int maxTotalPaths, int maxPathPoints, int numSkPo
|
||||
// that "end" at the beginning of the data. These will not be drawn, but will only be be read by
|
||||
// the first actual batch.
|
||||
fScissorSubBatches.push_back() = {PrimitiveTallies(), SkIRect::MakeEmpty()};
|
||||
fCoverageCountBatches.push_back() = {PrimitiveTallies(), fScissorSubBatches.count()};
|
||||
fCoverageCountBatches.push_back() = {PrimitiveTallies(), fScissorSubBatches.count(),
|
||||
PrimitiveTallies()};
|
||||
}
|
||||
|
||||
void GrCCPathParser::parsePath(const SkMatrix& m, const SkPath& path, SkRect* devBounds,
|
||||
@ -105,6 +106,7 @@ void GrCCPathParser::parsePath(const SkPath& path, const SkPoint* deviceSpacePts
|
||||
fCurrPathPointsIdx = fGeometry.points().count();
|
||||
fCurrPathVerbsIdx = fGeometry.verbs().count();
|
||||
fCurrPathPrimitiveCounts = PrimitiveTallies();
|
||||
fCurrPathFillType = path.getFillType();
|
||||
|
||||
fGeometry.beginPath();
|
||||
|
||||
@ -160,7 +162,81 @@ void GrCCPathParser::saveParsedPath(ScissorMode scissorMode, const SkIRect& clip
|
||||
int16_t atlasOffsetX, int16_t atlasOffsetY) {
|
||||
SkASSERT(fParsingPath);
|
||||
|
||||
fPathsInfo.push_back() = {scissorMode, atlasOffsetX, atlasOffsetY};
|
||||
fPathsInfo.emplace_back(scissorMode, atlasOffsetX, atlasOffsetY);
|
||||
|
||||
// Tessellate fans from very large and/or simple paths, in order to reduce overdraw.
|
||||
int numVerbs = fGeometry.verbs().count() - fCurrPathVerbsIdx - 1;
|
||||
int64_t tessellationWork = (int64_t)numVerbs * (32 - SkCLZ(numVerbs)); // N log N.
|
||||
int64_t fanningWork = (int64_t)clippedDevIBounds.height() * clippedDevIBounds.width();
|
||||
if (tessellationWork * (50*50) + (100*100) < fanningWork) { // Don't tessellate under 100x100.
|
||||
fCurrPathPrimitiveCounts.fTriangles =
|
||||
fCurrPathPrimitiveCounts.fWoundTriangles = 0;
|
||||
|
||||
const SkTArray<GrCCGeometry::Verb, true>& verbs = fGeometry.verbs();
|
||||
const SkTArray<SkPoint, true>& pts = fGeometry.points();
|
||||
int ptsIdx = fCurrPathPointsIdx;
|
||||
|
||||
// Build an SkPath of the Redbook fan.
|
||||
SkPath fan;
|
||||
fan.setFillType(fCurrPathFillType);
|
||||
SkASSERT(GrCCGeometry::Verb::kBeginPath == verbs[fCurrPathVerbsIdx]);
|
||||
for (int i = fCurrPathVerbsIdx + 1; i < fGeometry.verbs().count(); ++i) {
|
||||
switch (verbs[i]) {
|
||||
case GrCCGeometry::Verb::kBeginPath:
|
||||
SK_ABORT("Invalid GrCCGeometry");
|
||||
continue;
|
||||
|
||||
case GrCCGeometry::Verb::kBeginContour:
|
||||
fan.moveTo(pts[ptsIdx++]);
|
||||
continue;
|
||||
|
||||
case GrCCGeometry::Verb::kLineTo:
|
||||
fan.lineTo(pts[ptsIdx++]);
|
||||
continue;
|
||||
|
||||
case GrCCGeometry::Verb::kMonotonicQuadraticTo:
|
||||
fan.lineTo(pts[ptsIdx + 1]);
|
||||
ptsIdx += 2;
|
||||
continue;
|
||||
|
||||
case GrCCGeometry::Verb::kMonotonicCubicTo:
|
||||
fan.lineTo(pts[ptsIdx + 2]);
|
||||
ptsIdx += 3;
|
||||
continue;
|
||||
|
||||
case GrCCGeometry::Verb::kEndClosedContour:
|
||||
case GrCCGeometry::Verb::kEndOpenContour:
|
||||
fan.close();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
GrTessellator::WindingVertex* vertices = nullptr;
|
||||
int count = GrTessellator::PathToVertices(fan, std::numeric_limits<float>::infinity(),
|
||||
SkRect::Make(clippedDevIBounds), &vertices);
|
||||
SkASSERT(0 == count % 3);
|
||||
for (int i = 0; i < count; i += 3) {
|
||||
SkASSERT(vertices[i].fWinding == vertices[i + 1].fWinding);
|
||||
SkASSERT(vertices[i].fWinding == vertices[i + 2].fWinding);
|
||||
if (1 == abs(vertices[i].fWinding)) {
|
||||
// Ensure this triangle's points actually wind in the same direction as fWinding.
|
||||
float ax = vertices[i].fPos.fX - vertices[i + 1].fPos.fX;
|
||||
float ay = vertices[i].fPos.fY - vertices[i + 1].fPos.fY;
|
||||
float bx = vertices[i].fPos.fX - vertices[i + 2].fPos.fX;
|
||||
float by = vertices[i].fPos.fY - vertices[i + 2].fPos.fY;
|
||||
float wind = ay*bx - ax*by;
|
||||
if ((wind > 0) != (vertices[i].fWinding > 0)) {
|
||||
std::swap(vertices[i + 1].fPos, vertices[i + 2].fPos);
|
||||
}
|
||||
++fCurrPathPrimitiveCounts.fTriangles;
|
||||
} else {
|
||||
++fCurrPathPrimitiveCounts.fWoundTriangles;
|
||||
}
|
||||
}
|
||||
|
||||
fPathsInfo.back().fFanTessellation.reset(vertices);
|
||||
fPathsInfo.back().fFanTessellationCount = count;
|
||||
}
|
||||
|
||||
fTotalPrimitiveCounts[(int)scissorMode] += fCurrPathPrimitiveCounts;
|
||||
|
||||
if (ScissorMode::kScissored == scissorMode) {
|
||||
@ -180,15 +256,23 @@ void GrCCPathParser::discardParsedPath() {
|
||||
GrCCPathParser::CoverageCountBatchID GrCCPathParser::closeCurrentBatch() {
|
||||
SkASSERT(!fInstanceBuffer);
|
||||
SkASSERT(!fCoverageCountBatches.empty());
|
||||
const auto& lastBatch = fCoverageCountBatches.back();
|
||||
const auto& lastScissorSubBatch = fScissorSubBatches[lastBatch.fEndScissorSubBatchIdx - 1];
|
||||
|
||||
int maxMeshes = 1 + fScissorSubBatches.count() -
|
||||
fCoverageCountBatches.back().fEndScissorSubBatchIdx;
|
||||
fMaxMeshesPerDraw = SkTMax(fMaxMeshesPerDraw, maxMeshes);
|
||||
PrimitiveTallies batchTotalCounts = fTotalPrimitiveCounts[(int)ScissorMode::kNonScissored] -
|
||||
lastBatch.fEndNonScissorIndices;
|
||||
batchTotalCounts += fTotalPrimitiveCounts[(int)ScissorMode::kScissored] -
|
||||
lastScissorSubBatch.fEndPrimitiveIndices;
|
||||
|
||||
fCoverageCountBatches.push_back() = {
|
||||
fTotalPrimitiveCounts[(int)ScissorMode::kNonScissored],
|
||||
fScissorSubBatches.count()
|
||||
fScissorSubBatches.count(),
|
||||
batchTotalCounts
|
||||
};
|
||||
|
||||
int maxMeshes = 1 + fScissorSubBatches.count() - lastBatch.fEndScissorSubBatchIdx;
|
||||
fMaxMeshesPerDraw = SkTMax(fMaxMeshesPerDraw, maxMeshes);
|
||||
|
||||
return fCoverageCountBatches.count() - 1;
|
||||
}
|
||||
|
||||
@ -205,10 +289,10 @@ GrCCPathParser::CoverageCountBatchID GrCCPathParser::closeCurrentBatch() {
|
||||
// elements past the end for this method to use as scratch space.
|
||||
//
|
||||
// Returns the next triangle instance after the final one emitted.
|
||||
static TriangleInstance* emit_recursive_fan(const SkTArray<SkPoint, true>& pts,
|
||||
static TriPointInstance* emit_recursive_fan(const SkTArray<SkPoint, true>& pts,
|
||||
SkTArray<int32_t, true>& indices, int firstIndex,
|
||||
int indexCount, const Sk2f& atlasOffset,
|
||||
TriangleInstance out[]) {
|
||||
TriPointInstance out[]) {
|
||||
if (indexCount < 3) {
|
||||
return out;
|
||||
}
|
||||
@ -232,6 +316,22 @@ static TriangleInstance* emit_recursive_fan(const SkTArray<SkPoint, true>& pts,
|
||||
return out;
|
||||
}
|
||||
|
||||
static void emit_tessellated_fan(const GrTessellator::WindingVertex* vertices, int numVertices,
|
||||
const Sk2f& atlasOffset, TriPointInstance* triPointInstanceData,
|
||||
QuadPointInstance* quadPointInstanceData,
|
||||
GrCCGeometry::PrimitiveTallies* indices) {
|
||||
for (int i = 0; i < numVertices; i += 3) {
|
||||
if (1 == abs(vertices[i].fWinding)) {
|
||||
triPointInstanceData[indices->fTriangles++].set(vertices[i].fPos, vertices[i + 1].fPos,
|
||||
vertices[i + 2].fPos, atlasOffset);
|
||||
} else {
|
||||
quadPointInstanceData[indices->fWoundTriangles++].set(
|
||||
vertices[i].fPos, vertices[i+1].fPos, vertices[i + 2].fPos, atlasOffset,
|
||||
static_cast<float>(vertices[i].fWinding));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
||||
SkASSERT(!fParsingPath); // Call saveParsedPath() or discardParsedPath().
|
||||
SkASSERT(fCoverageCountBatches.back().fEndNonScissorIndices == // Call closeCurrentBatch().
|
||||
@ -250,7 +350,7 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
||||
//
|
||||
// We already know how big to make each of the 6 arrays from fTotalPrimitiveCounts, so layout is
|
||||
// straightforward. Start with triangles and quadratics. They both view the instance buffer as
|
||||
// an array of TriangleInstance[], so we can begin at zero and lay them out one after the other.
|
||||
// an array of TriPointInstance[], so we can begin at zero and lay them out one after the other.
|
||||
fBaseInstances[0].fTriangles = 0;
|
||||
fBaseInstances[1].fTriangles = fBaseInstances[0].fTriangles +
|
||||
fTotalPrimitiveCounts[0].fTriangles;
|
||||
@ -260,85 +360,112 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
||||
fTotalPrimitiveCounts[0].fQuadratics;
|
||||
int triEndIdx = fBaseInstances[1].fQuadratics + fTotalPrimitiveCounts[1].fQuadratics;
|
||||
|
||||
// Cubics view the same instance buffer as an array of CubicInstance[]. So, reinterpreting the
|
||||
// instance data as CubicInstance[], we start them on the first index that will not overwrite
|
||||
// previous TriangleInstance data.
|
||||
int cubicBaseIdx =
|
||||
GR_CT_DIV_ROUND_UP(triEndIdx * sizeof(TriangleInstance), sizeof(CubicInstance));
|
||||
fBaseInstances[0].fCubics = cubicBaseIdx;
|
||||
// Wound triangles and cubics both view the same instance buffer as an array of
|
||||
// QuadPointInstance[]. So, reinterpreting the instance data as QuadPointInstance[], we start
|
||||
// them on the first index that will not overwrite previous TriPointInstance data.
|
||||
int quadBaseIdx =
|
||||
GR_CT_DIV_ROUND_UP(triEndIdx * sizeof(TriPointInstance), sizeof(QuadPointInstance));
|
||||
fBaseInstances[0].fWoundTriangles = quadBaseIdx;
|
||||
fBaseInstances[1].fWoundTriangles = fBaseInstances[0].fWoundTriangles +
|
||||
fTotalPrimitiveCounts[0].fWoundTriangles;
|
||||
fBaseInstances[0].fCubics = fBaseInstances[1].fWoundTriangles +
|
||||
fTotalPrimitiveCounts[1].fWoundTriangles;
|
||||
fBaseInstances[1].fCubics = fBaseInstances[0].fCubics + fTotalPrimitiveCounts[0].fCubics;
|
||||
int cubicEndIdx = fBaseInstances[1].fCubics + fTotalPrimitiveCounts[1].fCubics;
|
||||
int quadEndIdx = fBaseInstances[1].fCubics + fTotalPrimitiveCounts[1].fCubics;
|
||||
|
||||
fInstanceBuffer = onFlushRP->makeBuffer(kVertex_GrBufferType,
|
||||
cubicEndIdx * sizeof(CubicInstance));
|
||||
quadEndIdx * sizeof(QuadPointInstance));
|
||||
if (!fInstanceBuffer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TriangleInstance* triangleInstanceData = static_cast<TriangleInstance*>(fInstanceBuffer->map());
|
||||
CubicInstance* cubicInstanceData = reinterpret_cast<CubicInstance*>(triangleInstanceData);
|
||||
SkASSERT(cubicInstanceData);
|
||||
TriPointInstance* triPointInstanceData = static_cast<TriPointInstance*>(fInstanceBuffer->map());
|
||||
QuadPointInstance* quadPointInstanceData =
|
||||
reinterpret_cast<QuadPointInstance*>(triPointInstanceData);
|
||||
SkASSERT(quadPointInstanceData);
|
||||
|
||||
PathInfo* currPathInfo = fPathsInfo.begin();
|
||||
PathInfo* nextPathInfo = fPathsInfo.begin();
|
||||
float atlasOffsetX = 0.0, atlasOffsetY = 0.0;
|
||||
Sk2f atlasOffset;
|
||||
int ptsIdx = -1;
|
||||
PrimitiveTallies instanceIndices[2] = {fBaseInstances[0], fBaseInstances[1]};
|
||||
PrimitiveTallies* currIndices = nullptr;
|
||||
SkSTArray<256, int32_t, true> currFan;
|
||||
bool currFanIsTessellated = false;
|
||||
|
||||
const SkTArray<SkPoint, true>& pts = fGeometry.points();
|
||||
int ptsIdx = -1;
|
||||
|
||||
// Expand the ccpr verbs into GPU instance buffers.
|
||||
for (GrCCGeometry::Verb verb : fGeometry.verbs()) {
|
||||
switch (verb) {
|
||||
case GrCCGeometry::Verb::kBeginPath:
|
||||
SkASSERT(currFan.empty());
|
||||
currIndices = &instanceIndices[(int)currPathInfo->fScissorMode];
|
||||
atlasOffsetX = static_cast<float>(currPathInfo->fAtlasOffsetX);
|
||||
atlasOffsetY = static_cast<float>(currPathInfo->fAtlasOffsetY);
|
||||
currIndices = &instanceIndices[(int)nextPathInfo->fScissorMode];
|
||||
atlasOffsetX = static_cast<float>(nextPathInfo->fAtlasOffsetX);
|
||||
atlasOffsetY = static_cast<float>(nextPathInfo->fAtlasOffsetY);
|
||||
atlasOffset = {atlasOffsetX, atlasOffsetY};
|
||||
++currPathInfo;
|
||||
currFanIsTessellated = nextPathInfo->fFanTessellation.get();
|
||||
if (currFanIsTessellated) {
|
||||
emit_tessellated_fan(nextPathInfo->fFanTessellation.get(),
|
||||
nextPathInfo->fFanTessellationCount, atlasOffset,
|
||||
triPointInstanceData, quadPointInstanceData, currIndices);
|
||||
}
|
||||
++nextPathInfo;
|
||||
continue;
|
||||
|
||||
case GrCCGeometry::Verb::kBeginContour:
|
||||
SkASSERT(currFan.empty());
|
||||
currFan.push_back(++ptsIdx);
|
||||
++ptsIdx;
|
||||
if (!currFanIsTessellated) {
|
||||
currFan.push_back(ptsIdx);
|
||||
}
|
||||
continue;
|
||||
|
||||
case GrCCGeometry::Verb::kLineTo:
|
||||
SkASSERT(!currFan.empty());
|
||||
currFan.push_back(++ptsIdx);
|
||||
++ptsIdx;
|
||||
if (!currFanIsTessellated) {
|
||||
SkASSERT(!currFan.empty());
|
||||
currFan.push_back(ptsIdx);
|
||||
}
|
||||
continue;
|
||||
|
||||
case GrCCGeometry::Verb::kMonotonicQuadraticTo:
|
||||
SkASSERT(!currFan.empty());
|
||||
triangleInstanceData[currIndices->fQuadratics++].set(&pts[ptsIdx], atlasOffset);
|
||||
currFan.push_back(ptsIdx += 2);
|
||||
triPointInstanceData[currIndices->fQuadratics++].set(&pts[ptsIdx], atlasOffset);
|
||||
ptsIdx += 2;
|
||||
if (!currFanIsTessellated) {
|
||||
SkASSERT(!currFan.empty());
|
||||
currFan.push_back(ptsIdx);
|
||||
}
|
||||
continue;
|
||||
|
||||
case GrCCGeometry::Verb::kMonotonicCubicTo:
|
||||
SkASSERT(!currFan.empty());
|
||||
cubicInstanceData[currIndices->fCubics++].set(&pts[ptsIdx], atlasOffsetX,
|
||||
atlasOffsetY);
|
||||
currFan.push_back(ptsIdx += 3);
|
||||
quadPointInstanceData[currIndices->fCubics++].set(&pts[ptsIdx], atlasOffsetX,
|
||||
atlasOffsetY);
|
||||
ptsIdx += 3;
|
||||
if (!currFanIsTessellated) {
|
||||
SkASSERT(!currFan.empty());
|
||||
currFan.push_back(ptsIdx);
|
||||
}
|
||||
continue;
|
||||
|
||||
case GrCCGeometry::Verb::kEndClosedContour: // endPt == startPt.
|
||||
SkASSERT(!currFan.empty());
|
||||
currFan.pop_back();
|
||||
if (!currFanIsTessellated) {
|
||||
SkASSERT(!currFan.empty());
|
||||
currFan.pop_back();
|
||||
}
|
||||
// fallthru.
|
||||
case GrCCGeometry::Verb::kEndOpenContour: // endPt != startPt.
|
||||
if (currFan.count() >= 3) {
|
||||
SkASSERT(!currFanIsTessellated || currFan.empty());
|
||||
if (!currFanIsTessellated && currFan.count() >= 3) {
|
||||
int fanSize = currFan.count();
|
||||
// Reserve space for emit_recursive_fan. Technically this can grow to
|
||||
// fanSize + log3(fanSize), but we approximate with log2.
|
||||
currFan.push_back_n(SkNextLog2(fanSize));
|
||||
SkDEBUGCODE(TriangleInstance* end =)
|
||||
SkDEBUGCODE(TriPointInstance* end =)
|
||||
emit_recursive_fan(pts, currFan, 0, fanSize, atlasOffset,
|
||||
triangleInstanceData + currIndices->fTriangles);
|
||||
triPointInstanceData + currIndices->fTriangles);
|
||||
currIndices->fTriangles += fanSize - 2;
|
||||
SkASSERT(triangleInstanceData + currIndices->fTriangles == end);
|
||||
SkASSERT(triPointInstanceData + currIndices->fTriangles == end);
|
||||
}
|
||||
currFan.reset();
|
||||
continue;
|
||||
@ -347,14 +474,16 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
||||
|
||||
fInstanceBuffer->unmap();
|
||||
|
||||
SkASSERT(currPathInfo == fPathsInfo.end());
|
||||
SkASSERT(nextPathInfo == fPathsInfo.end());
|
||||
SkASSERT(ptsIdx == pts.count() - 1);
|
||||
SkASSERT(instanceIndices[0].fTriangles == fBaseInstances[1].fTriangles);
|
||||
SkASSERT(instanceIndices[1].fTriangles == fBaseInstances[0].fQuadratics);
|
||||
SkASSERT(instanceIndices[0].fQuadratics == fBaseInstances[1].fQuadratics);
|
||||
SkASSERT(instanceIndices[1].fQuadratics == triEndIdx);
|
||||
SkASSERT(instanceIndices[0].fWoundTriangles == fBaseInstances[1].fWoundTriangles);
|
||||
SkASSERT(instanceIndices[1].fWoundTriangles == fBaseInstances[0].fCubics);
|
||||
SkASSERT(instanceIndices[0].fCubics == fBaseInstances[1].fCubics);
|
||||
SkASSERT(instanceIndices[1].fCubics == cubicEndIdx);
|
||||
SkASSERT(instanceIndices[1].fCubics == quadEndIdx);
|
||||
|
||||
fMeshesScratchBuffer.reserve(fMaxMeshesPerDraw);
|
||||
fDynamicStatesScratchBuffer.reserve(fMaxMeshesPerDraw);
|
||||
@ -365,36 +494,56 @@ bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
|
||||
void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCountBatchID batchID,
|
||||
const SkIRect& drawBounds) const {
|
||||
using RenderPass = GrCCCoverageProcessor::RenderPass;
|
||||
using WindMethod = GrCCCoverageProcessor::WindMethod;
|
||||
|
||||
SkASSERT(fInstanceBuffer);
|
||||
|
||||
const PrimitiveTallies& batchTotalCounts = fCoverageCountBatches[batchID].fTotalPrimitiveCounts;
|
||||
|
||||
GrPipeline pipeline(flushState->drawOpArgs().fProxy, GrPipeline::ScissorState::kEnabled,
|
||||
SkBlendMode::kPlus);
|
||||
|
||||
// Triangles.
|
||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleHulls,
|
||||
&PrimitiveTallies::fTriangles, drawBounds);
|
||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleEdges,
|
||||
&PrimitiveTallies::fTriangles, drawBounds); // Might get skipped.
|
||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners,
|
||||
&PrimitiveTallies::fTriangles, drawBounds);
|
||||
if (batchTotalCounts.fTriangles) {
|
||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleHulls,
|
||||
WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, drawBounds);
|
||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleEdges,
|
||||
WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles,
|
||||
drawBounds); // Might get skipped.
|
||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners,
|
||||
WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, drawBounds);
|
||||
}
|
||||
|
||||
// Quadratics.
|
||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadraticHulls,
|
||||
&PrimitiveTallies::fQuadratics, drawBounds);
|
||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadraticCorners,
|
||||
&PrimitiveTallies::fQuadratics, drawBounds);
|
||||
if (batchTotalCounts.fWoundTriangles) {
|
||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleHulls,
|
||||
WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles,
|
||||
drawBounds);
|
||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleEdges,
|
||||
WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles,
|
||||
drawBounds); // Might get skipped.
|
||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners,
|
||||
WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles,
|
||||
drawBounds);
|
||||
}
|
||||
|
||||
// Cubics.
|
||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubicHulls,
|
||||
&PrimitiveTallies::fCubics, drawBounds);
|
||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubicCorners,
|
||||
&PrimitiveTallies::fCubics, drawBounds);
|
||||
if (batchTotalCounts.fQuadratics) {
|
||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadraticHulls,
|
||||
WindMethod::kCrossProduct, &PrimitiveTallies::fQuadratics, drawBounds);
|
||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadraticCorners,
|
||||
WindMethod::kCrossProduct, &PrimitiveTallies::fQuadratics, drawBounds);
|
||||
}
|
||||
|
||||
if (batchTotalCounts.fCubics) {
|
||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubicHulls,
|
||||
WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds);
|
||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubicCorners,
|
||||
WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds);
|
||||
}
|
||||
}
|
||||
|
||||
void GrCCPathParser::drawRenderPass(GrOpFlushState* flushState, const GrPipeline& pipeline,
|
||||
CoverageCountBatchID batchID,
|
||||
GrCCCoverageProcessor::RenderPass renderPass,
|
||||
GrCCCoverageProcessor::WindMethod windMethod,
|
||||
int PrimitiveTallies::*instanceType,
|
||||
const SkIRect& drawBounds) const {
|
||||
SkASSERT(pipeline.getScissorState().enabled());
|
||||
@ -407,12 +556,13 @@ void GrCCPathParser::drawRenderPass(GrOpFlushState* flushState, const GrPipeline
|
||||
fMeshesScratchBuffer.pop_back_n(fMeshesScratchBuffer.count());
|
||||
fDynamicStatesScratchBuffer.pop_back_n(fDynamicStatesScratchBuffer.count());
|
||||
|
||||
GrCCCoverageProcessor proc(flushState->resourceProvider(), renderPass, flushState->caps());
|
||||
GrCCCoverageProcessor proc(flushState->resourceProvider(), renderPass, windMethod);
|
||||
|
||||
SkASSERT(batchID > 0);
|
||||
SkASSERT(batchID < fCoverageCountBatches.count());
|
||||
const CoverageCountBatch& previousBatch = fCoverageCountBatches[batchID - 1];
|
||||
const CoverageCountBatch& batch = fCoverageCountBatches[batchID];
|
||||
SkDEBUGCODE(int totalInstanceCount = 0);
|
||||
|
||||
if (int instanceCount = batch.fEndNonScissorIndices.*instanceType -
|
||||
previousBatch.fEndNonScissorIndices.*instanceType) {
|
||||
@ -422,6 +572,7 @@ void GrCCPathParser::drawRenderPass(GrOpFlushState* flushState, const GrPipeline
|
||||
proc.appendMesh(fInstanceBuffer.get(), instanceCount, baseInstance, &fMeshesScratchBuffer);
|
||||
fDynamicStatesScratchBuffer.push_back().fScissorRect.setXYWH(0, 0, drawBounds.width(),
|
||||
drawBounds.height());
|
||||
SkDEBUGCODE(totalInstanceCount += instanceCount);
|
||||
}
|
||||
|
||||
SkASSERT(previousBatch.fEndScissorSubBatchIdx > 0);
|
||||
@ -439,10 +590,12 @@ void GrCCPathParser::drawRenderPass(GrOpFlushState* flushState, const GrPipeline
|
||||
proc.appendMesh(fInstanceBuffer.get(), instanceCount,
|
||||
baseScissorInstance + startIndex, &fMeshesScratchBuffer);
|
||||
fDynamicStatesScratchBuffer.push_back().fScissorRect = scissorSubBatch.fScissor;
|
||||
SkDEBUGCODE(totalInstanceCount += instanceCount);
|
||||
}
|
||||
|
||||
SkASSERT(fMeshesScratchBuffer.count() == fDynamicStatesScratchBuffer.count());
|
||||
SkASSERT(fMeshesScratchBuffer.count() <= fMaxMeshesPerDraw);
|
||||
SkASSERT(totalInstanceCount == batch.fTotalPrimitiveCounts.*instanceType);
|
||||
|
||||
if (!fMeshesScratchBuffer.empty()) {
|
||||
SkASSERT(flushState->rtCommandBuffer());
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include "GrMesh.h"
|
||||
#include "GrNonAtomicRef.h"
|
||||
#include "GrTessellator.h"
|
||||
#include "SkRect.h"
|
||||
#include "SkRefCnt.h"
|
||||
#include "ccpr/GrCCCoverageProcessor.h"
|
||||
@ -76,8 +77,13 @@ private:
|
||||
|
||||
// Every kBeginPath verb has a corresponding PathInfo entry.
|
||||
struct PathInfo {
|
||||
PathInfo(ScissorMode scissorMode, int16_t offsetX, int16_t offsetY)
|
||||
: fScissorMode(scissorMode), fAtlasOffsetX(offsetX), fAtlasOffsetY(offsetY) {}
|
||||
|
||||
ScissorMode fScissorMode;
|
||||
int16_t fAtlasOffsetX, fAtlasOffsetY;
|
||||
std::unique_ptr<GrTessellator::WindingVertex[]> fFanTessellation;
|
||||
int fFanTessellationCount = 0;
|
||||
};
|
||||
|
||||
// Defines a batch of CCPR primitives. Start indices are deduced by looking at the previous
|
||||
@ -85,6 +91,7 @@ private:
|
||||
struct CoverageCountBatch {
|
||||
PrimitiveTallies fEndNonScissorIndices;
|
||||
int fEndScissorSubBatchIdx;
|
||||
PrimitiveTallies fTotalPrimitiveCounts;
|
||||
};
|
||||
|
||||
// Defines a sub-batch from CoverageCountBatch that will be drawn with the given scissor rect.
|
||||
@ -98,8 +105,8 @@ private:
|
||||
void endContourIfNeeded(bool insideContour);
|
||||
|
||||
void drawRenderPass(GrOpFlushState*, const GrPipeline&, CoverageCountBatchID,
|
||||
GrCCCoverageProcessor::RenderPass, int PrimitiveTallies::*instanceType,
|
||||
const SkIRect& drawBounds) const;
|
||||
GrCCCoverageProcessor::RenderPass, GrCCCoverageProcessor::WindMethod,
|
||||
int PrimitiveTallies::*instanceType, const SkIRect& drawBounds) const;
|
||||
|
||||
// Staging area for the path being parsed.
|
||||
SkDEBUGCODE(int fParsingPath = false);
|
||||
@ -107,6 +114,7 @@ private:
|
||||
int fCurrPathPointsIdx;
|
||||
int fCurrPathVerbsIdx;
|
||||
PrimitiveTallies fCurrPathPrimitiveCounts;
|
||||
SkPath::FillType fCurrPathFillType;
|
||||
|
||||
GrCCGeometry fGeometry;
|
||||
SkSTArray<32, PathInfo, true> fPathsInfo;
|
||||
|
Loading…
Reference in New Issue
Block a user