ccpr: Combine GS triangle hulls and edges into a single draw
Updates the geometry shader backend to match the vertex backend and draw triangle rasters together with their their edges in a single draw call. This gives a performance boost as well as cleaning up some API awkwardness. This is one step toward the final goal of drawing ccpr primitives in a single pass. Bug: skia: Change-Id: I2723692d02b9e39ca5dc5d9e022b528a051988ab Reviewed-on: https://skia-review.googlesource.com/112104 Commit-Queue: Chris Dalton <csmartdalton@google.com> Reviewed-by: Brian Salomon <bsalomon@google.com>
This commit is contained in:
parent
de9f546a04
commit
5183e649ef
@ -33,7 +33,7 @@ using RenderPass = GrCCCoverageProcessor::RenderPass;
|
|||||||
static constexpr float kDebugBloat = 40;
|
static constexpr float kDebugBloat = 40;
|
||||||
|
|
||||||
static int is_quadratic(RenderPass pass) {
|
static int is_quadratic(RenderPass pass) {
|
||||||
return pass == RenderPass::kQuadraticHulls || pass == RenderPass::kQuadraticCorners;
|
return pass == RenderPass::kQuadratics || pass == RenderPass::kQuadraticCorners;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,7 +59,7 @@ private:
|
|||||||
|
|
||||||
void updateGpuData();
|
void updateGpuData();
|
||||||
|
|
||||||
RenderPass fRenderPass = RenderPass::kTriangleHulls;
|
RenderPass fRenderPass = RenderPass::kTriangles;
|
||||||
SkCubicType fCubicType;
|
SkCubicType fCubicType;
|
||||||
SkMatrix fCubicKLM;
|
SkMatrix fCubicKLM;
|
||||||
|
|
||||||
@ -249,10 +249,6 @@ void CCPRGeometryView::Op::onExecute(GrOpFlushState* state) {
|
|||||||
? static_cast<GrGLGpu*>(state->gpu())
|
? static_cast<GrGLGpu*>(state->gpu())
|
||||||
: nullptr;
|
: nullptr;
|
||||||
|
|
||||||
if (!GrCCCoverageProcessor::DoesRenderPass(fView->fRenderPass, state->caps())) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
GrCCCoverageProcessor proc(rp, fView->fRenderPass,
|
GrCCCoverageProcessor proc(rp, fView->fRenderPass,
|
||||||
GrCCCoverageProcessor::WindMethod::kCrossProduct);
|
GrCCCoverageProcessor::WindMethod::kCrossProduct);
|
||||||
SkDEBUGCODE(proc.enableDebugVisualizations(kDebugBloat));
|
SkDEBUGCODE(proc.enableDebugVisualizations(kDebugBloat));
|
||||||
@ -346,7 +342,7 @@ bool CCPRGeometryView::onQuery(SkEvent* evt) {
|
|||||||
}
|
}
|
||||||
SkUnichar unichar;
|
SkUnichar unichar;
|
||||||
if (SampleCode::CharQ(*evt, &unichar)) {
|
if (SampleCode::CharQ(*evt, &unichar)) {
|
||||||
if (unichar >= '1' && unichar <= '7') {
|
if (unichar >= '1' && unichar <= '6') {
|
||||||
fRenderPass = RenderPass(unichar - '1');
|
fRenderPass = RenderPass(unichar - '1');
|
||||||
this->updateAndInval();
|
this->updateAndInval();
|
||||||
return true;
|
return true;
|
||||||
|
@ -119,20 +119,19 @@ 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 (fRenderPass) {
|
||||||
case RenderPass::kTriangleHulls:
|
case RenderPass::kTriangles:
|
||||||
case RenderPass::kTriangleEdges:
|
|
||||||
shader = skstd::make_unique<GrCCTriangleShader>();
|
shader = skstd::make_unique<GrCCTriangleShader>();
|
||||||
break;
|
break;
|
||||||
case RenderPass::kTriangleCorners:
|
case RenderPass::kTriangleCorners:
|
||||||
shader = skstd::make_unique<GrCCTriangleCornerShader>();
|
shader = skstd::make_unique<GrCCTriangleCornerShader>();
|
||||||
break;
|
break;
|
||||||
case RenderPass::kQuadraticHulls:
|
case RenderPass::kQuadratics:
|
||||||
shader = skstd::make_unique<GrCCQuadraticHullShader>();
|
shader = skstd::make_unique<GrCCQuadraticHullShader>();
|
||||||
break;
|
break;
|
||||||
case RenderPass::kQuadraticCorners:
|
case RenderPass::kQuadraticCorners:
|
||||||
shader = skstd::make_unique<GrCCQuadraticCornerShader>();
|
shader = skstd::make_unique<GrCCQuadraticCornerShader>();
|
||||||
break;
|
break;
|
||||||
case RenderPass::kCubicHulls:
|
case RenderPass::kCubics:
|
||||||
shader = skstd::make_unique<GrCCCubicHullShader>();
|
shader = skstd::make_unique<GrCCCubicHullShader>();
|
||||||
break;
|
break;
|
||||||
case RenderPass::kCubicCorners:
|
case RenderPass::kCubicCorners:
|
||||||
|
@ -54,49 +54,21 @@ 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) require more than one
|
// All primitive shapes (triangles and closed, convex bezier curves) require two
|
||||||
// render pass. Here we enumerate every render pass needed in order to produce a complete
|
// render passes: One to draw a rough outline of the shape, and a second pass to touch up the
|
||||||
|
// corners. Here we enumerate every render pass needed in order to produce a complete
|
||||||
// coverage count mask. This is an exhaustive list of all ccpr coverage shaders.
|
// coverage count mask. This is an exhaustive list of all ccpr coverage shaders.
|
||||||
//
|
|
||||||
// During a render pass, the "Impl" (GSImpl or VSimpl) generates conservative geometry for
|
|
||||||
// rasterization, and the Shader decides the coverage value at each pixel.
|
|
||||||
enum class RenderPass {
|
enum class RenderPass {
|
||||||
// For a Hull, the Impl generates a "conservative raster hull" around the input points. This
|
kTriangles,
|
||||||
// is the geometry that causes a pixel to be rasterized if it is touched anywhere by the
|
|
||||||
// input polygon. The input coverage values sent to the Shader at each vertex are either
|
|
||||||
// null, or +1 all around if the Impl combines this pass with kTriangleEdges. Logically,
|
|
||||||
// the conservative raster hull is equivalent to the convex hull of pixel size boxes
|
|
||||||
// centered on each input point.
|
|
||||||
kTriangleHulls,
|
|
||||||
kQuadraticHulls,
|
|
||||||
kCubicHulls,
|
|
||||||
|
|
||||||
// For Edges, the Impl generates conservative rasters around every input edge (i.e. convex
|
|
||||||
// hulls of two pixel-size boxes centered on both of the edge's endpoints). The input
|
|
||||||
// coverage values sent to the Shader at each vertex are -1 on the outside border of the
|
|
||||||
// edge geometry and 0 on the inside. This is the only geometry type that associates
|
|
||||||
// coverage values with the output vertices. Interpolated, these coverage values convert
|
|
||||||
// jagged conservative raster edges into a smooth antialiased edge.
|
|
||||||
//
|
|
||||||
// NOTE: The Impl may combine this pass with kTriangleHulls, in which case DoesRenderPass()
|
|
||||||
// will be false for kTriangleEdges and it must not be used.
|
|
||||||
kTriangleEdges,
|
|
||||||
|
|
||||||
// For Corners, the Impl Generates the conservative rasters of corner points (i.e.
|
|
||||||
// pixel-size boxes). It generates 3 corner boxes for triangles and 2 for curves. The Shader
|
|
||||||
// specifies which corners. Input coverage values sent to the Shader will be null.
|
|
||||||
kTriangleCorners,
|
kTriangleCorners,
|
||||||
|
kQuadratics,
|
||||||
kQuadraticCorners,
|
kQuadraticCorners,
|
||||||
|
kCubics,
|
||||||
kCubicCorners
|
kCubicCorners
|
||||||
};
|
};
|
||||||
static bool RenderPassIsCubic(RenderPass);
|
static bool RenderPassIsCubic(RenderPass);
|
||||||
static const char* RenderPassName(RenderPass);
|
static const char* RenderPassName(RenderPass);
|
||||||
|
|
||||||
constexpr static bool DoesRenderPass(RenderPass renderPass, const GrCaps& caps) {
|
|
||||||
return RenderPass::kTriangleEdges != renderPass ||
|
|
||||||
caps.shaderCaps()->geometryShaderSupport();
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
||||||
@ -109,7 +81,6 @@ public:
|
|||||||
, 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 {
|
||||||
@ -204,8 +175,7 @@ public:
|
|||||||
// Here the subclass adds its internal varyings to the handler and produces code to
|
// Here the subclass adds its internal varyings to the handler and produces code to
|
||||||
// initialize those varyings from a given position, input coverage value, and wind.
|
// initialize those varyings from a given position, input coverage value, and wind.
|
||||||
//
|
//
|
||||||
// NOTE: the coverage input is only relevant for edges (see comments in RenderPass).
|
// NOTE: the coverage input is only relevant for triangles. Otherwise it is null.
|
||||||
// Otherwise it is +1 all around.
|
|
||||||
virtual void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
|
virtual void onEmitVaryings(GrGLSLVaryingHandler*, GrGLSLVarying::Scope, SkString* code,
|
||||||
const char* position, const char* inputCoverage,
|
const char* position, const char* inputCoverage,
|
||||||
const char* wind) = 0;
|
const char* wind) = 0;
|
||||||
@ -301,13 +271,12 @@ inline void GrCCCoverageProcessor::QuadPointInstance::set(const SkPoint& p0, con
|
|||||||
|
|
||||||
inline bool GrCCCoverageProcessor::RenderPassIsCubic(RenderPass pass) {
|
inline bool GrCCCoverageProcessor::RenderPassIsCubic(RenderPass pass) {
|
||||||
switch (pass) {
|
switch (pass) {
|
||||||
case RenderPass::kTriangleHulls:
|
case RenderPass::kTriangles:
|
||||||
case RenderPass::kTriangleEdges:
|
|
||||||
case RenderPass::kTriangleCorners:
|
case RenderPass::kTriangleCorners:
|
||||||
case RenderPass::kQuadraticHulls:
|
case RenderPass::kQuadratics:
|
||||||
case RenderPass::kQuadraticCorners:
|
case RenderPass::kQuadraticCorners:
|
||||||
return false;
|
return false;
|
||||||
case RenderPass::kCubicHulls:
|
case RenderPass::kCubics:
|
||||||
case RenderPass::kCubicCorners:
|
case RenderPass::kCubicCorners:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -317,12 +286,11 @@ inline bool GrCCCoverageProcessor::RenderPassIsCubic(RenderPass pass) {
|
|||||||
|
|
||||||
inline const char* GrCCCoverageProcessor::RenderPassName(RenderPass pass) {
|
inline const char* GrCCCoverageProcessor::RenderPassName(RenderPass pass) {
|
||||||
switch (pass) {
|
switch (pass) {
|
||||||
case RenderPass::kTriangleHulls: return "kTriangleHulls";
|
case RenderPass::kTriangles: return "kTriangles";
|
||||||
case RenderPass::kTriangleEdges: return "kTriangleEdges";
|
|
||||||
case RenderPass::kTriangleCorners: return "kTriangleCorners";
|
case RenderPass::kTriangleCorners: return "kTriangleCorners";
|
||||||
case RenderPass::kQuadraticHulls: return "kQuadraticHulls";
|
case RenderPass::kQuadratics: return "kQuadratics";
|
||||||
case RenderPass::kQuadraticCorners: return "kQuadraticCorners";
|
case RenderPass::kQuadraticCorners: return "kQuadraticCorners";
|
||||||
case RenderPass::kCubicHulls: return "kCubicHulls";
|
case RenderPass::kCubics: return "kCubics";
|
||||||
case RenderPass::kCubicCorners: return "kCubicCorners";
|
case RenderPass::kCubicCorners: return "kCubicCorners";
|
||||||
}
|
}
|
||||||
SK_ABORT("Invalid RenderPass");
|
SK_ABORT("Invalid RenderPass");
|
||||||
|
@ -76,7 +76,7 @@ protected:
|
|||||||
SkSTArray<2, GrShaderVar> emitArgs;
|
SkSTArray<2, GrShaderVar> emitArgs;
|
||||||
const char* position = emitArgs.emplace_back("position", kFloat2_GrSLType).c_str();
|
const char* position = emitArgs.emplace_back("position", kFloat2_GrSLType).c_str();
|
||||||
const char* coverage = nullptr;
|
const char* coverage = nullptr;
|
||||||
if (RenderPass::kTriangleEdges == proc.fRenderPass) {
|
if (RenderPass::kTriangles == proc.fRenderPass) {
|
||||||
coverage = emitArgs.emplace_back("coverage", kHalf_GrSLType).c_str();
|
coverage = emitArgs.emplace_back("coverage", kHalf_GrSLType).c_str();
|
||||||
}
|
}
|
||||||
g->emitFunction(kVoid_GrSLType, "emitVertex", emitArgs.count(), emitArgs.begin(), [&]() {
|
g->emitFunction(kVoid_GrSLType, "emitVertex", emitArgs.count(), emitArgs.begin(), [&]() {
|
||||||
@ -109,80 +109,110 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a conservative raster hull around a triangle. (See comments for RenderPass)
|
* Generates conservative rasters around a triangle and its edges, and calculates coverage ramps.
|
||||||
|
*
|
||||||
|
* Triangle rough outlines are drawn in two steps: (1) draw a conservative raster of the entire
|
||||||
|
* triangle, with a coverage of +1, and (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.
|
||||||
|
*
|
||||||
|
* The final corners get touched up in a later step by GSCornerImpl.
|
||||||
*/
|
*/
|
||||||
class GSHull3Impl : public GrCCCoverageProcessor::GSImpl {
|
class GSTriangleImpl : public GrCCCoverageProcessor::GSImpl {
|
||||||
public:
|
public:
|
||||||
GSHull3Impl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
|
GSTriangleImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
|
||||||
|
|
||||||
void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
|
void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
|
||||||
const char* emitVertexFn) const override {
|
const char* emitVertexFn) const override {
|
||||||
Shader::GeometryVars vars;
|
|
||||||
fShader->emitSetupCode(g, "pts", nullptr, wind.c_str(), &vars);
|
|
||||||
|
|
||||||
const char* hullPts = vars.fHullVars.fAlternatePoints;
|
|
||||||
if (!hullPts) {
|
|
||||||
hullPts = "pts";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Visualize the input triangle as upright and equilateral, with a flat base. Paying special
|
// Visualize the input triangle as upright and equilateral, with a flat base. Paying special
|
||||||
// attention to wind, we can identify the points as top, bottom-left, and bottom-right.
|
// attention to wind, we can identify the points as top, bottom-left, and bottom-right.
|
||||||
//
|
//
|
||||||
// NOTE: We generate the hull in 2 independent invocations, so each invocation designates
|
// NOTE: We generate the rasters in 5 independent invocations, so each invocation designates
|
||||||
// the corner it will begin with as the top.
|
// the corner it will begin with as the top.
|
||||||
g->codeAppendf("int i = %s > 0 ? sk_InvocationID : 1 - sk_InvocationID;", wind.c_str());
|
g->codeAppendf("int i = (%s > 0 ? sk_InvocationID : 4 - sk_InvocationID) %% 3;",
|
||||||
g->codeAppendf("float2 top = %s[i];", hullPts);
|
wind.c_str());
|
||||||
g->codeAppendf("float2 left = %s[%s > 0 ? (1 - i) * 2 : i + 1];", hullPts, wind.c_str());
|
g->codeAppend ("float2 top = pts[i];");
|
||||||
g->codeAppendf("float2 right = %s[%s > 0 ? i + 1 : (1 - i) * 2];", hullPts, wind.c_str());
|
g->codeAppendf("float2 right = pts[(i + (%s > 0 ? 1 : 2)) %% 3];", wind.c_str());
|
||||||
|
g->codeAppendf("float2 left = pts[(i + (%s > 0 ? 2 : 1)) %% 3];", wind.c_str());
|
||||||
|
|
||||||
// Determine how much to outset the conservative raster hull from each of the three edges.
|
// Determine which direction to outset the conservative raster from each of the three edges.
|
||||||
g->codeAppend ("float2 leftbloat = float2(top.y > left.y ? +bloat : -bloat, "
|
g->codeAppend ("float2 leftbloat = sign(top - left);");
|
||||||
"top.x > left.x ? -bloat : +bloat);");
|
g->codeAppend ("leftbloat = float2(0 != leftbloat.y ? leftbloat.y : leftbloat.x, "
|
||||||
g->codeAppend ("float2 rightbloat = float2(right.y > top.y ? +bloat : -bloat, "
|
"0 != leftbloat.x ? -leftbloat.x : -leftbloat.y);");
|
||||||
"right.x > top.x ? -bloat : +bloat);");
|
|
||||||
g->codeAppend ("float2 downbloat = float2(left.y > right.y ? +bloat : -bloat, "
|
|
||||||
"left.x > right.x ? -bloat : +bloat);");
|
|
||||||
|
|
||||||
// Here we generate the conservative raster geometry. It is the convex hull of 3 pixel-size
|
g->codeAppend ("float2 rightbloat = sign(right - top);");
|
||||||
// boxes centered on the input points, split between two invocations. This translates to a
|
g->codeAppend ("rightbloat = float2(0 != rightbloat.y ? rightbloat.y : rightbloat.x, "
|
||||||
// polygon with either one, two, or three vertices at each input point, depending on how
|
"0 != rightbloat.x ? -rightbloat.x : -rightbloat.y);");
|
||||||
// sharp the corner is. For more details on conservative raster, see:
|
|
||||||
|
g->codeAppend ("float2 downbloat = sign(left - right);");
|
||||||
|
g->codeAppend ("downbloat = float2(0 != downbloat.y ? downbloat.y : downbloat.x, "
|
||||||
|
"0 != downbloat.x ? -downbloat.x : -downbloat.y);");
|
||||||
|
|
||||||
|
// The triangle's conservative raster has a coverage of +1 all around.
|
||||||
|
g->codeAppend ("half4 coverages = half4(+1);");
|
||||||
|
|
||||||
|
// Edges have coverage ramps.
|
||||||
|
g->codeAppend ("if (sk_InvocationID >= 2) {"); // Are we an edge?
|
||||||
|
Shader::CalcEdgeCoverageAtBloatVertex(g, "top", "right",
|
||||||
|
"float2(+rightbloat.y, -rightbloat.x)",
|
||||||
|
"coverages[0]");
|
||||||
|
g->codeAppend ( "coverages.yzw = half3(-1, 0, -1 - coverages[0]);");
|
||||||
|
// Reassign bloats to characterize a conservative raster around a single edge, rather than
|
||||||
|
// the entire triangle.
|
||||||
|
g->codeAppend ( "leftbloat = downbloat = -rightbloat;");
|
||||||
|
g->codeAppend ("}");
|
||||||
|
|
||||||
|
// These can't be scaled until after we calculate coverage.
|
||||||
|
g->codeAppend ("leftbloat *= bloat;");
|
||||||
|
g->codeAppend ("rightbloat *= bloat;");
|
||||||
|
g->codeAppend ("downbloat *= bloat;");
|
||||||
|
|
||||||
|
// Here we generate the conservative raster geometry. The triangle's conservative raster is
|
||||||
|
// the convex hull of 3 pixel-size boxes centered on the input points. This translates to a
|
||||||
|
// convex polygon with either one, two, or three vertices at each input point (depending on
|
||||||
|
// how sharp the corner is) that we split between two invocations. Edge conservative rasters
|
||||||
|
// are convex hulls of 2 pixel-size boxes, one at each endpoint. For more details on
|
||||||
|
// conservative raster, see:
|
||||||
// https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html
|
// https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter42.html
|
||||||
g->codeAppendf("bool2 left_right_notequal = notEqual(leftbloat, rightbloat);");
|
g->codeAppendf("bool2 left_right_notequal = notEqual(leftbloat, rightbloat);");
|
||||||
g->codeAppend ("if (all(left_right_notequal)) {");
|
g->codeAppend ("if (all(left_right_notequal)) {");
|
||||||
// The top corner will have three conservative raster vertices. Emit the
|
// The top corner will have three conservative raster vertices. Emit the
|
||||||
// middle one first to the triangle strip.
|
// middle one first to the triangle strip.
|
||||||
g->codeAppendf( "%s(top + float2(-leftbloat.y, leftbloat.x));", emitVertexFn);
|
g->codeAppendf( "%s(top + float2(-leftbloat.y, +leftbloat.x), coverages[0]);",
|
||||||
|
emitVertexFn);
|
||||||
g->codeAppend ("}");
|
g->codeAppend ("}");
|
||||||
g->codeAppend ("if (any(left_right_notequal)) {");
|
g->codeAppend ("if (any(left_right_notequal)) {");
|
||||||
// Second conservative raster vertex for the top corner.
|
// Second conservative raster vertex for the top corner.
|
||||||
g->codeAppendf( "%s(top + rightbloat);", emitVertexFn);
|
g->codeAppendf( "%s(top + rightbloat, coverages[1]);", emitVertexFn);
|
||||||
g->codeAppend ("}");
|
g->codeAppend ("}");
|
||||||
|
|
||||||
// Main interior body of the triangle.
|
// Main interior body.
|
||||||
g->codeAppendf("%s(top + leftbloat);", emitVertexFn);
|
g->codeAppendf("%s(top + leftbloat, coverages[2]);", emitVertexFn);
|
||||||
g->codeAppendf("%s(right + rightbloat);", emitVertexFn);
|
g->codeAppendf("%s(right + rightbloat, coverages[1]);", emitVertexFn);
|
||||||
|
|
||||||
// Here the two invocations diverge. We can't symmetrically divide three triangle points
|
// Here the invocations diverge slightly. We can't symmetrically divide three triangle
|
||||||
// between two invocations, so each does the following:
|
// points between two invocations, so each does the following:
|
||||||
//
|
//
|
||||||
// sk_InvocationID=0: Finishes the main interior body of the triangle.
|
// sk_InvocationID=0: Finishes the main interior body of the triangle hull.
|
||||||
// sk_InvocationID=1: Remaining two conservative raster vertices for the third corner.
|
// sk_InvocationID=1: Remaining two conservative raster vertices for the third hull corner.
|
||||||
|
// sk_InvocationID=2..4: Finish the opposite endpoint of their corresponding edge.
|
||||||
g->codeAppendf("bool2 right_down_notequal = notEqual(rightbloat, downbloat);");
|
g->codeAppendf("bool2 right_down_notequal = notEqual(rightbloat, downbloat);");
|
||||||
g->codeAppend ("if (any(right_down_notequal) || 0 == sk_InvocationID) {");
|
g->codeAppend ("if (any(right_down_notequal) || 0 == sk_InvocationID) {");
|
||||||
g->codeAppendf( "%s(sk_InvocationID == 0 ? left + leftbloat : right + downbloat);",
|
g->codeAppendf( "%s(0 == sk_InvocationID ? left + leftbloat : right + downbloat, "
|
||||||
emitVertexFn);
|
"coverages[2]);", emitVertexFn);
|
||||||
g->codeAppend ("}");
|
g->codeAppend ("}");
|
||||||
g->codeAppend ("if (all(right_down_notequal) && 0 != sk_InvocationID) {");
|
g->codeAppend ("if (all(right_down_notequal) && 0 != sk_InvocationID) {");
|
||||||
g->codeAppendf( "%s(right + float2(-rightbloat.y, rightbloat.x));", emitVertexFn);
|
g->codeAppendf( "%s(right + float2(-rightbloat.y, +rightbloat.x), coverages[3]);",
|
||||||
|
emitVertexFn);
|
||||||
g->codeAppend ("}");
|
g->codeAppend ("}");
|
||||||
|
|
||||||
g->configure(InputType::kLines, OutputType::kTriangleStrip, 6, 2);
|
// 5 invocations: 2 triangle hull invocations and 3 edges.
|
||||||
|
g->configure(InputType::kLines, OutputType::kTriangleStrip, 6, 5);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a conservative raster hull around a convex quadrilateral. (See comments for RenderPass)
|
* Generates a conservative raster around a convex quadrilateral that encloses a cubic or quadratic.
|
||||||
*/
|
*/
|
||||||
class GSHull4Impl : public GrCCCoverageProcessor::GSImpl {
|
class GSHull4Impl : public GrCCCoverageProcessor::GSImpl {
|
||||||
public:
|
public:
|
||||||
@ -252,53 +282,6 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates conservatives around each edge of a triangle. (See comments for RenderPass)
|
|
||||||
*/
|
|
||||||
class GSEdgeImpl : public GrCCCoverageProcessor::GSImpl {
|
|
||||||
public:
|
|
||||||
GSEdgeImpl(std::unique_ptr<Shader> shader) : GSImpl(std::move(shader)) {}
|
|
||||||
|
|
||||||
void onEmitGeometryShader(GrGLSLGeometryBuilder* g, const GrShaderVar& wind,
|
|
||||||
const char* emitVertexFn) const override {
|
|
||||||
fShader->emitSetupCode(g, "pts", "sk_InvocationID", wind.c_str(), nullptr);
|
|
||||||
|
|
||||||
g->codeAppend ("int nextidx = 2 != sk_InvocationID ? sk_InvocationID + 1 : 0;");
|
|
||||||
g->codeAppendf("float2 left = pts[%s > 0 ? sk_InvocationID : nextidx];", wind.c_str());
|
|
||||||
g->codeAppendf("float2 right = pts[%s > 0 ? nextidx : sk_InvocationID];", wind.c_str());
|
|
||||||
|
|
||||||
// Which quadrant does the vector from left -> right fall into?
|
|
||||||
g->codeAppend ("float2 qlr = sign(right - left);");
|
|
||||||
g->codeAppend ("float2x2 outer_pts = float2x2(left - bloat * qlr, right + bloat * qlr);");
|
|
||||||
g->codeAppend ("half outer_coverage;");
|
|
||||||
Shader::CalcEdgeCoverageAtBloatVertex(g, "left", "right", "qlr", "outer_coverage");
|
|
||||||
|
|
||||||
g->codeAppend ("float2 d1 = float2(qlr.y, -qlr.x);");
|
|
||||||
g->codeAppend ("float2 d2 = d1;");
|
|
||||||
g->codeAppend ("bool aligned = qlr.x == 0 || qlr.y == 0;");
|
|
||||||
g->codeAppend ("if (aligned) {");
|
|
||||||
g->codeAppend ( "d1 -= qlr;");
|
|
||||||
g->codeAppend ( "d2 += qlr;");
|
|
||||||
g->codeAppend ("}");
|
|
||||||
|
|
||||||
// Emit the convex hull of 2 pixel-size boxes centered on the endpoints of the edge. Each
|
|
||||||
// invocation emits a different edge. Emit negative coverage that subtracts the appropiate
|
|
||||||
// amount back out from the hull we drew above.
|
|
||||||
g->codeAppend ("if (!aligned) {");
|
|
||||||
g->codeAppendf( "%s(outer_pts[0], -1 - outer_coverage);", emitVertexFn);
|
|
||||||
g->codeAppend ("}");
|
|
||||||
g->codeAppendf("%s(left + bloat * d1, -1);", emitVertexFn);
|
|
||||||
g->codeAppendf("%s(left - bloat * d2, 0);", emitVertexFn);
|
|
||||||
g->codeAppendf("%s(right + bloat * d2, -1);", emitVertexFn);
|
|
||||||
g->codeAppendf("%s(right - bloat * d1, 0);", emitVertexFn);
|
|
||||||
g->codeAppend ("if (!aligned) {");
|
|
||||||
g->codeAppendf( "%s(outer_pts[1], outer_coverage);", emitVertexFn);
|
|
||||||
g->codeAppend ("}");
|
|
||||||
|
|
||||||
g->configure(InputType::kLines, OutputType::kTriangleStrip, 6, 3);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates conservative rasters around corners. (See comments for RenderPass)
|
* Generates conservative rasters around corners. (See comments for RenderPass)
|
||||||
*/
|
*/
|
||||||
@ -358,15 +341,13 @@ 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) {
|
switch (fRenderPass) {
|
||||||
case RenderPass::kTriangleHulls:
|
case RenderPass::kTriangles:
|
||||||
return new GSHull3Impl(std::move(shadr));
|
return new GSTriangleImpl(std::move(shadr));
|
||||||
case RenderPass::kQuadraticHulls:
|
|
||||||
case RenderPass::kCubicHulls:
|
|
||||||
return new GSHull4Impl(std::move(shadr));
|
|
||||||
case RenderPass::kTriangleEdges:
|
|
||||||
return new GSEdgeImpl(std::move(shadr));
|
|
||||||
case RenderPass::kTriangleCorners:
|
case RenderPass::kTriangleCorners:
|
||||||
return new GSCornerImpl(std::move(shadr), 3);
|
return new GSCornerImpl(std::move(shadr), 3);
|
||||||
|
case RenderPass::kQuadratics:
|
||||||
|
case RenderPass::kCubics:
|
||||||
|
return new GSHull4Impl(std::move(shadr));
|
||||||
case RenderPass::kQuadraticCorners:
|
case RenderPass::kQuadraticCorners:
|
||||||
case RenderPass::kCubicCorners:
|
case RenderPass::kCubicCorners:
|
||||||
return new GSCornerImpl(std::move(shadr), 2);
|
return new GSCornerImpl(std::move(shadr), 2);
|
||||||
|
@ -244,8 +244,18 @@ static constexpr uint16_t kHull4IndicesAsTris[] = {
|
|||||||
GR_DECLARE_STATIC_UNIQUE_KEY(gHull4IndexBufferKey);
|
GR_DECLARE_STATIC_UNIQUE_KEY(gHull4IndexBufferKey);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a conservative raster hull around a convex polygon. For triangles, we also generate
|
* Generates a conservative raster hull around a convex polygon. For triangles we generate
|
||||||
* independent conservative rasters around each edge. (See comments for RenderPass)
|
* additional conservative rasters around the edges and calculate coverage ramps.
|
||||||
|
*
|
||||||
|
* Triangle rough outlines are drawn in two steps: (1) draw a conservative raster of the entire
|
||||||
|
* triangle, with a coverage of +1, and (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.
|
||||||
|
*
|
||||||
|
* Curve rough outlines are just the conservative raster of a convex quadrilateral that encloses the
|
||||||
|
* curve. The Shader takes care of everything else for now.
|
||||||
|
*
|
||||||
|
* The final corners get touched up in a later step by VSCornerImpl.
|
||||||
*/
|
*/
|
||||||
class VSHullAndEdgeImpl : public GrCCCoverageProcessor::VSImpl {
|
class VSHullAndEdgeImpl : public GrCCCoverageProcessor::VSImpl {
|
||||||
public:
|
public:
|
||||||
@ -392,7 +402,7 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) {
|
|||||||
const GrCaps& caps = *rp->caps();
|
const GrCaps& caps = *rp->caps();
|
||||||
|
|
||||||
switch (fRenderPass) {
|
switch (fRenderPass) {
|
||||||
case RenderPass::kTriangleHulls: {
|
case RenderPass::kTriangles: {
|
||||||
GR_DEFINE_STATIC_UNIQUE_KEY(gHull3AndEdgeVertexBufferKey);
|
GR_DEFINE_STATIC_UNIQUE_KEY(gHull3AndEdgeVertexBufferKey);
|
||||||
fVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType,
|
fVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType,
|
||||||
sizeof(kHull3AndEdgeVertices),
|
sizeof(kHull3AndEdgeVertices),
|
||||||
@ -414,8 +424,9 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RenderPass::kQuadraticHulls:
|
|
||||||
case RenderPass::kCubicHulls: {
|
case RenderPass::kQuadratics:
|
||||||
|
case RenderPass::kCubics: {
|
||||||
GR_DEFINE_STATIC_UNIQUE_KEY(gHull4VertexBufferKey);
|
GR_DEFINE_STATIC_UNIQUE_KEY(gHull4VertexBufferKey);
|
||||||
fVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType, sizeof(kHull4Vertices),
|
fVertexBuffer = rp->findOrMakeStaticBuffer(kVertex_GrBufferType, sizeof(kHull4Vertices),
|
||||||
kHull4Vertices, gHull4VertexBufferKey);
|
kHull4Vertices, gHull4VertexBufferKey);
|
||||||
@ -435,9 +446,7 @@ void GrCCCoverageProcessor::initVS(GrResourceProvider* rp) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RenderPass::kTriangleEdges:
|
|
||||||
SK_ABORT("kTriangleEdges RenderPass is not used by VSImpl.");
|
|
||||||
break;
|
|
||||||
case RenderPass::kTriangleCorners:
|
case RenderPass::kTriangleCorners:
|
||||||
case RenderPass::kQuadraticCorners:
|
case RenderPass::kQuadraticCorners:
|
||||||
case RenderPass::kCubicCorners: {
|
case RenderPass::kCubicCorners: {
|
||||||
@ -514,14 +523,11 @@ void GrCCCoverageProcessor::appendVSMesh(GrBuffer* instanceBuffer, int instanceC
|
|||||||
|
|
||||||
GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createVSImpl(std::unique_ptr<Shader> shadr) const {
|
GrGLSLPrimitiveProcessor* GrCCCoverageProcessor::createVSImpl(std::unique_ptr<Shader> shadr) const {
|
||||||
switch (fRenderPass) {
|
switch (fRenderPass) {
|
||||||
case RenderPass::kTriangleHulls:
|
case RenderPass::kTriangles:
|
||||||
return new VSHullAndEdgeImpl(std::move(shadr), 3);
|
return new VSHullAndEdgeImpl(std::move(shadr), 3);
|
||||||
case RenderPass::kQuadraticHulls:
|
case RenderPass::kQuadratics:
|
||||||
case RenderPass::kCubicHulls:
|
case RenderPass::kCubics:
|
||||||
return new VSHullAndEdgeImpl(std::move(shadr), 4);
|
return new VSHullAndEdgeImpl(std::move(shadr), 4);
|
||||||
case RenderPass::kTriangleEdges:
|
|
||||||
SK_ABORT("kTriangleEdges RenderPass is not used by VSImpl.");
|
|
||||||
return nullptr;
|
|
||||||
case RenderPass::kTriangleCorners:
|
case RenderPass::kTriangleCorners:
|
||||||
case RenderPass::kQuadraticCorners:
|
case RenderPass::kQuadraticCorners:
|
||||||
case RenderPass::kCubicCorners:
|
case RenderPass::kCubicCorners:
|
||||||
|
@ -512,36 +512,30 @@ void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCount
|
|||||||
SkBlendMode::kPlus);
|
SkBlendMode::kPlus);
|
||||||
|
|
||||||
if (batchTotalCounts.fTriangles) {
|
if (batchTotalCounts.fTriangles) {
|
||||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleHulls,
|
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangles,
|
||||||
WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, drawBounds);
|
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,
|
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners,
|
||||||
WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, drawBounds);
|
WindMethod::kCrossProduct, &PrimitiveTallies::fTriangles, drawBounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (batchTotalCounts.fWoundTriangles) {
|
if (batchTotalCounts.fWoundTriangles) {
|
||||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleHulls,
|
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangles,
|
||||||
WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles,
|
WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles,
|
||||||
drawBounds);
|
drawBounds);
|
||||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleEdges,
|
|
||||||
WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles,
|
|
||||||
drawBounds); // Might get skipped.
|
|
||||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners,
|
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kTriangleCorners,
|
||||||
WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles,
|
WindMethod::kInstanceData, &PrimitiveTallies::fWoundTriangles,
|
||||||
drawBounds);
|
drawBounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (batchTotalCounts.fQuadratics) {
|
if (batchTotalCounts.fQuadratics) {
|
||||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadraticHulls,
|
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadratics,
|
||||||
WindMethod::kCrossProduct, &PrimitiveTallies::fQuadratics, drawBounds);
|
WindMethod::kCrossProduct, &PrimitiveTallies::fQuadratics, drawBounds);
|
||||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kQuadraticCorners,
|
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::kCubicHulls,
|
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubics,
|
||||||
WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds);
|
WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds);
|
||||||
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubicCorners,
|
this->drawRenderPass(flushState, pipeline, batchID, RenderPass::kCubicCorners,
|
||||||
WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds);
|
WindMethod::kCrossProduct, &PrimitiveTallies::fCubics, drawBounds);
|
||||||
@ -556,10 +550,6 @@ void GrCCPathParser::drawRenderPass(GrOpFlushState* flushState, const GrPipeline
|
|||||||
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());
|
||||||
|
@ -16,15 +16,10 @@ void GrCCTriangleShader::onEmitVaryings(GrGLSLVaryingHandler* varyingHandler,
|
|||||||
GrGLSLVarying::Scope scope, SkString* code,
|
GrGLSLVarying::Scope scope, SkString* code,
|
||||||
const char* /*position*/, const char* inputCoverage,
|
const char* /*position*/, const char* inputCoverage,
|
||||||
const char* wind) {
|
const char* wind) {
|
||||||
|
SkASSERT(inputCoverage);
|
||||||
fCoverageTimesWind.reset(kHalf_GrSLType, scope);
|
fCoverageTimesWind.reset(kHalf_GrSLType, scope);
|
||||||
if (!inputCoverage) {
|
varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind);
|
||||||
varyingHandler->addVarying("wind", &fCoverageTimesWind,
|
code->appendf("%s = %s * %s;", OutName(fCoverageTimesWind), inputCoverage, wind);
|
||||||
GrGLSLVaryingHandler::Interpolation::kCanBeFlat);
|
|
||||||
code->appendf("%s = %s;", OutName(fCoverageTimesWind), wind);
|
|
||||||
} else {
|
|
||||||
varyingHandler->addVarying("coverage_times_wind", &fCoverageTimesWind);
|
|
||||||
code->appendf("%s = %s * %s;", OutName(fCoverageTimesWind), inputCoverage, wind);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GrCCTriangleShader::onEmitFragmentCode(GrGLSLFPFragmentBuilder* f,
|
void GrCCTriangleShader::onEmitFragmentCode(GrGLSLFPFragmentBuilder* f,
|
||||||
|
Loading…
Reference in New Issue
Block a user