Use conics with w=Inf to describe triangles for the tessellator
Previously, only the indirect tessellator could draw triangles, and
only with special index data. Using conics with w=Inf will allow us to
draw triangles with the hardware tessellator as well, in addition to
being able to wean the indirect tessellator off an index buffer.
Bug: skia:10419
Bug: chromium:1202607
Change-Id: I180af9cb5410c0e0bb25a2edcfb01e17d4a2f590
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/406977
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
diff --git a/src/gpu/tessellate/GrFillPathShader.cpp b/src/gpu/tessellate/GrFillPathShader.cpp
index 6274845..71fd72d 100644
--- a/src/gpu/tessellate/GrFillPathShader.cpp
+++ b/src/gpu/tessellate/GrFillPathShader.cpp
@@ -72,16 +72,20 @@
GrGLSLUniformHandler* uniformHandler) const {
v->codeAppend(R"(
float4x2 P = float4x2(input_points_0_1, input_points_2_3);
- if (isinf(P[3].y)) {
- // This curve is actually a conic. Convert the control points to a trapeziodal hull
- // that circumcscribes the conic.
+ if (isinf(P[3].y)) { // Is the curve a conic?
float w = P[3].x;
- float2 p1w = P[1] * w;
- float T = .51; // Bias outward a bit to ensure we cover the outermost samples.
- float2 c1 = mix(P[0], p1w, T);
- float2 c2 = mix(P[2], p1w, T);
- float iw = 1 / mix(1, w, T);
- P = float4x2(P[0], c1 * iw, c2 * iw, P[2]);
+ if (isinf(w)) {
+ // A conic with w=Inf is an exact triangle.
+ P = float4x2(P[0], P[1], P[2], P[2]);
+ } else {
+ // Convert the control points to a trapeziodal hull that circumcscribes the conic.
+ float2 p1w = P[1] * w;
+ float T = .51; // Bias outward a bit to ensure we cover the outermost samples.
+ float2 c1 = mix(P[0], p1w, T);
+ float2 c2 = mix(P[2], p1w, T);
+ float iw = 1 / mix(1, w, T);
+ P = float4x2(P[0], c1 * iw, c2 * iw, P[2]);
+ }
}
// Translate the points to v0..3 where v0=0.
diff --git a/src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h b/src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h
index b960ab5..fb17db1 100644
--- a/src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h
+++ b/src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h
@@ -13,6 +13,7 @@
#include "include/private/SkTemplates.h"
#include "src/core/SkMathPriv.h"
#include "src/core/SkPathPriv.h"
+#include <limits>
// This class emits a polygon triangulation with a "middle-out" topology. Conceptually, middle-out
// emits one large triangle with vertices on both endpoints and a middle point, then recurses on
@@ -40,6 +41,12 @@
// This class is designed to not know or store all the vertices in the polygon at once. The caller
// pushes each vertex in linear order (perhaps while parsing a path), then rather than relying on
// recursion, we manipulate an O(log N) stack to determine the correct middle-out triangulation.
+//
+// perTriangleVertexAdvance controls how much padding to put after triangles (namely, we append
+// "perTriangleVertexAdvance - 3" vertices of padding after each triangle, including the final one).
+// Padding vertices are filled with infinity. This has the effect, when perTriangleVertexAdvance is
+// 4, of defining a conic with w=Inf for tessellation shaders, which comes out to be an exact
+// triangle.
class GrMiddleOutPolygonTriangulator {
public:
GrMiddleOutPolygonTriangulator(SkPoint* vertexData, int perTriangleVertexAdvance,
@@ -166,6 +173,10 @@
fVertexData[0] = fTop[0].fPoint;
fVertexData[1] = fTop[1].fPoint;
fVertexData[2] = lastPt;
+ for (int i = 3; i < fPerTriangleVertexAdvance; ++i) {
+ fVertexData[i].set(std::numeric_limits<float>::infinity(),
+ std::numeric_limits<float>::infinity());
+ }
fVertexData += fPerTriangleVertexAdvance;
}
diff --git a/src/gpu/tessellate/GrPathTessellator.cpp b/src/gpu/tessellate/GrPathTessellator.cpp
index 24db640..7fe80ae 100644
--- a/src/gpu/tessellate/GrPathTessellator.cpp
+++ b/src/gpu/tessellate/GrPathTessellator.cpp
@@ -14,6 +14,7 @@
#include "src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h"
#include "src/gpu/tessellate/GrMidpointContourParser.h"
#include "src/gpu/tessellate/GrStencilPathShader.h"
+#include <limits>
GrPathIndirectTessellator::GrPathIndirectTessellator(const SkMatrix& viewMatrix, const SkPath& path,
DrawInnerFan drawInnerFan)
@@ -92,8 +93,9 @@
}
SkPoint* breadcrumbData = instanceData + numTrianglesAtBeginningOfData * 4;
memcpy(breadcrumbData, p, sizeof(SkPoint) * 3);
- // Duplicate the final point since it will also be used by the convex hull shader.
- breadcrumbData[3] = p[2];
+ // Mark this instance as a triangle by setting it to a conic with w=Inf.
+ breadcrumbData[3].set(std::numeric_limits<float>::infinity(),
+ std::numeric_limits<float>::infinity());
++numTrianglesAtBeginningOfData;
}
SkASSERT(count == breadcrumbTriangleList->count());
@@ -122,29 +124,24 @@
// location at each resolve level.
SkPoint* instanceLocations[kMaxResolveLevel + 1];
int runningInstanceCount = 0;
- if (numTrianglesAtBeginningOfData) {
- // The caller has already packed "triangleInstanceCount" triangles into 4-point instances
- // at the beginning of the instance buffer. Add a special-case indirect draw here that will
- // emit the triangles [P0, P1, P2] from these 4-point instances.
- SkASSERT(fIndirectDrawCount < indirectLockCnt);
- GrMiddleOutCubicShader::WriteDrawTrianglesIndirectCmd(&indirectWriter,
- numTrianglesAtBeginningOfData,
- fBaseInstance);
- ++fIndirectDrawCount;
- runningInstanceCount = numTrianglesAtBeginningOfData;
- }
SkASSERT(fResolveLevelCounts[0] == 0);
for (int resolveLevel = 1; resolveLevel <= kMaxResolveLevel; ++resolveLevel) {
int instanceCountAtCurrLevel = fResolveLevelCounts[resolveLevel];
+ if (resolveLevel == 1) {
+ instanceCountAtCurrLevel += numTrianglesAtBeginningOfData;
+ }
if (!instanceCountAtCurrLevel) {
SkDEBUGCODE(instanceLocations[resolveLevel] = nullptr;)
continue;
}
instanceLocations[resolveLevel] = instanceData + runningInstanceCount * 4;
+ if (resolveLevel == 1) {
+ instanceLocations[resolveLevel] += numTrianglesAtBeginningOfData * 4;
+ }
SkASSERT(fIndirectDrawCount < indirectLockCnt);
- GrMiddleOutCubicShader::WriteDrawCubicsIndirectCmd(&indirectWriter, resolveLevel,
- instanceCountAtCurrLevel,
- fBaseInstance + runningInstanceCount);
+ GrMiddleOutCubicShader::WriteDrawIndirectCmd(&indirectWriter, resolveLevel,
+ instanceCountAtCurrLevel,
+ fBaseInstance + runningInstanceCount);
++fIndirectDrawCount;
runningInstanceCount += instanceCountAtCurrLevel;
}
diff --git a/src/gpu/tessellate/GrStencilPathShader.cpp b/src/gpu/tessellate/GrStencilPathShader.cpp
index 85b1f42..fb4aee8 100644
--- a/src/gpu/tessellate/GrStencilPathShader.cpp
+++ b/src/gpu/tessellate/GrStencilPathShader.cpp
@@ -374,10 +374,10 @@
args.fVertBuilder->insertFunction(kEvalRationalCubicFn);
args.fVertBuilder->codeAppend(R"(
float2 pos;
- if (sk_VertexID > kMaxVertexID) {
- // This is a special index value that instructs us to emit a specific point.
- pos = ((sk_VertexID & 3) == 0) ? inputPoints_0_1.xy :
- ((sk_VertexID & 2) == 0) ? inputPoints_0_1.zw : inputPoints_2_3.xy;
+ if (isinf(inputPoints_2_3.z)) {
+ // A conic with w=Inf is an exact triangle.
+ pos = (sk_VertexID == 0) ? inputPoints_0_1.xy :
+ (sk_VertexID != kMaxVertexID) ? inputPoints_0_1.zw : inputPoints_2_3.xy;
} else {
// Evaluate the cubic at T = (sk_VertexID / 2^kMaxResolveLevel).
float T = float(sk_VertexID) * kInverseMaxVertexID;
diff --git a/src/gpu/tessellate/GrStencilPathShader.h b/src/gpu/tessellate/GrStencilPathShader.h
index 7236dea..203a184 100644
--- a/src/gpu/tessellate/GrStencilPathShader.h
+++ b/src/gpu/tessellate/GrStencilPathShader.h
@@ -152,9 +152,8 @@
// Configures an indirect draw to render cubic instances with 2^resolveLevel evenly-spaced (in
// the parametric sense) line segments.
- static void WriteDrawCubicsIndirectCmd(GrDrawIndexedIndirectWriter* indirectWriter,
- int resolveLevel, uint32_t instanceCount,
- uint32_t baseInstance) {
+ static void WriteDrawIndirectCmd(GrDrawIndexedIndirectWriter* indirectWriter, int resolveLevel,
+ uint32_t instanceCount, uint32_t baseInstance) {
SkASSERT(resolveLevel > 0 && resolveLevel <= GrTessellationPathRenderer::kMaxResolveLevel);
// Starting at baseIndex=3, the index buffer triangulates a cubic with 2^kMaxResolveLevel
// line segments. Each index value corresponds to a parametric T value on the curve. Since
@@ -164,15 +163,6 @@
indirectWriter->writeIndexed(indexCount, 3, instanceCount, baseInstance, 0);
}
- // For performance reasons we can often express triangles as an indirect cubic draw and sneak
- // them in alongside the other indirect draws. This method configures an indirect draw to emit
- // the triangle [P0, P1, P2] from a 4-point instance.
- static void WriteDrawTrianglesIndirectCmd(GrDrawIndexedIndirectWriter* indirectWriter,
- uint32_t instanceCount, uint32_t baseInstance) {
- // Indices 0,1,2 have special index values that emit points P0, P1, and P2 respectively.
- indirectWriter->writeIndexed(3, 0, instanceCount, baseInstance, 0);
- }
-
// Returns the index buffer that should be bound when drawing with this shader.
// (Our vertex shader uses raw index values directly, so there is no vertex buffer.)
static sk_sp<const GrGpuBuffer> FindOrMakeMiddleOutIndexBuffer(GrResourceProvider*);