Refactor middle-out tessellation shader for fixed count
Refactors the middle-out shader in a way that will allow the fixed
count tessellator to easily run Wang's formula and "throw away"
unnecessary triangles by making them empty.
Bug: skia:10419
Change-Id: I86b76cb24ef43ccdbc890f7dfffcda91f8b01a9c
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/416542
Reviewed-by: Jim Van Verth <jvanverth@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/tessellate/shaders/GrPathTessellationShader_MiddleOut.cpp b/src/gpu/tessellate/shaders/GrPathTessellationShader_MiddleOut.cpp
index 4bd2ad5..ee1d162 100644
--- a/src/gpu/tessellate/shaders/GrPathTessellationShader_MiddleOut.cpp
+++ b/src/gpu/tessellate/shaders/GrPathTessellationShader_MiddleOut.cpp
@@ -36,26 +36,24 @@
private:
const char* name() const final { return "tessellate_MiddleOutShader"; }
- void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {}
+ void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
};
GrGLSLGeometryProcessor* MiddleOutShader::createGLSLInstance(const GrShaderCaps&) const {
class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
- v->insertFunction(kUnpackRationalCubicFn);
- v->insertFunction(kEvalRationalCubicFn);
if (v->getProgramBuilder()->shaderCaps()->bitManipulationSupport()) {
// Determines the T value at which to place the given vertex in a "middle-out"
// topology.
v->insertFunction(R"(
float find_middle_out_T() {
int totalTriangleIdx = sk_VertexID/3 + 1;
- int depth = findMSB(totalTriangleIdx);
- int firstTriangleAtDepth = (1 << depth);
+ int resolveLevel = findMSB(totalTriangleIdx) + 1;
+ int firstTriangleAtDepth = (1 << (resolveLevel - 1));
int triangleIdxWithinDepth = totalTriangleIdx - firstTriangleAtDepth;
int vertexIdxWithinDepth = triangleIdxWithinDepth * 2 + sk_VertexID % 3;
- return ldexp(float(vertexIdxWithinDepth), -1 - depth);
+ return ldexp(float(vertexIdxWithinDepth), -resolveLevel);
})");
} else {
// Determines the T value at which to place the given vertex in a "middle-out"
@@ -63,11 +61,11 @@
v->insertFunction(R"(
float find_middle_out_T() {
float totalTriangleIdx = float(sk_VertexID/3) + 1;
- float depth = floor(log2(totalTriangleIdx));
- float firstTriangleAtDepth = exp2(depth);
+ float resolveLevel = floor(log2(totalTriangleIdx)) + 1;
+ float firstTriangleAtDepth = exp2(resolveLevel - 1);
float triangleIdxWithinDepth = totalTriangleIdx - firstTriangleAtDepth;
float vertexIdxWithinDepth = triangleIdxWithinDepth*2 + float(sk_VertexID % 3);
- return vertexIdxWithinDepth * exp2(-1 - depth);
+ return vertexIdxWithinDepth * exp2(-resolveLevel);
})");
}
v->codeAppend(R"(
@@ -78,10 +76,33 @@
: (sk_VertexID == 1) ? inputPoints_0_1.zw
: inputPoints_2_3.xy;
} else {
- float4x3 P = unpack_rational_cubic(inputPoints_0_1.xy, inputPoints_0_1.zw,
- inputPoints_2_3.xy, inputPoints_2_3.zw);
+ float w = -1; // w < 0 tells us to treat the instance as an integral cubic.
+ float4x2 P = float4x2(inputPoints_0_1, inputPoints_2_3);
+ if (isinf(P[3].y)) {
+ // The patch is a conic.
+ w = P[3].x;
+ P[3] = P[2]; // Duplicate the endpoint.
+ P[1] *= w; // Unproject p1.
+ }
float T = find_middle_out_T();
- localcoord = eval_rational_cubic(P, T);
+ if (0 < T && T < 1) {
+ // Evaluate at T. Use De Casteljau's for its accuracy and stability.
+ float2 ab = mix(P[0], P[1], T);
+ float2 bc = mix(P[1], P[2], T);
+ float2 cd = mix(P[2], P[3], T);
+ float2 abc = mix(ab, bc, T);
+ float2 bcd = mix(bc, cd, T);
+ float2 abcd = mix(abc, bcd, T);
+
+ // Evaluate the conic weight at T.
+ float u = mix(1.0, w, T);
+ float v = w + 1 - u; // == mix(w, 1, T)
+ float uv = mix(u, v, T);
+
+ localcoord = (w < 0) ? /*cubic*/ abcd : /*conic*/ abc/uv;
+ } else {
+ localcoord = (T == 0) ? P[0].xy : P[3].xy;
+ }
}
float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
gpArgs->fLocalCoordVar.set(kFloat2_GrSLType, "localcoord");