Add a robust "isinf" workaround for tessellation
When isinf is not supported, we simply add another instanced attrib that
tells the shader exactly what type of curve it's dealing with.
Bug: chromium:1220246
Change-Id: I3496de674ce8c7df205e3c40559ae89dc29488e1
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/429676
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index e075995..9e8f56b 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -245,6 +245,20 @@
fDrawInstancedSupport = version >= GR_GL_VER(2, 0);
}
+#ifdef GR_DISABLE_TESSELLATION_ON_ES2
+ if (GR_IS_GR_GL_ES(standard) && version < GR_GL_VER(3, 0)) {
+ // Temporarily disable the tessellation path renderer on Chrome ES2 while we roll the
+ // necessary Skia changes.
+ fDisableTessellationPathRenderer = true;
+ }
+#else
+ if (GR_IS_GR_GL_ES(standard) && ctxInfo.isOverCommandBuffer() && version < GR_GL_VER(3, 0)) {
+ // Temporarily disable the tessellation path renderer over the ES2 command buffer. This is
+ // an attempt to lower impact while we roll out tessellation in Chrome.
+ fDisableTessellationPathRenderer = true;
+ }
+#endif
+
if (GR_IS_GR_GL(standard)) {
if (version >= GR_GL_VER(3, 0)) {
fBindFragDataLocationSupport = true;
diff --git a/src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h b/src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h
index 79b80b3..a2df17b 100644
--- a/src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h
+++ b/src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h
@@ -43,15 +43,14 @@
// recursion, we manipulate an O(log N) stack to determine the correct middle-out triangulation.
class GrMiddleOutPolygonTriangulator {
public:
- enum class OutputType : bool {
- kTriangles, // Output 3-vertex triangles.
- kConicsWithInfiniteWeight // Output 4-vertex conics with w=Inf.
- };
-
- GrMiddleOutPolygonTriangulator(GrVertexWriter* vertexWriter, OutputType outputType,
- int maxPushVertexCalls)
- : fVertexWriter(vertexWriter)
- , fOutputType(outputType) {
+ // Writes out 3 SkPoints per triangle to "vertexWriter". Additionally writes out "pad32Count"
+ // repetitions of "pad32Value" after each triangle. Set pad32Count to 0 if the triangles are
+ // to be tightly packed.
+ GrMiddleOutPolygonTriangulator(GrVertexWriter* vertexWriter, int pad32Count,
+ uint32_t pad32Value, int maxPushVertexCalls)
+ : fPad32Count(pad32Count)
+ , fPad32Value(pad32Value)
+ , fVertexWriter(vertexWriter) {
// Determine the deepest our stack can ever go.
int maxStackDepth = SkNextLog2(maxPushVertexCalls) + 1;
if (maxStackDepth > kStackPreallocCount) {
@@ -123,9 +122,10 @@
SkASSERT(fTop->fVertexIdxDelta == 0); // Ensure we are in the initial stack state.
}
- static int WritePathInnerFan(GrVertexWriter* vertexWriter, OutputType outputType,
+ static int WritePathInnerFan(GrVertexWriter* vertexWriter, int pad32Count, uint32_t pad32Value,
const SkPath& path) {
- GrMiddleOutPolygonTriangulator middleOut(vertexWriter, outputType, path.countVerbs());
+ GrMiddleOutPolygonTriangulator middleOut(vertexWriter, pad32Count, pad32Value,
+ path.countVerbs());
for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
switch (verb) {
case SkPathVerb::kMove:
@@ -169,19 +169,20 @@
SkASSERT(fTop > fVertexStack); // We should never pop the starting point.
--fTop;
fVertexWriter->write(fTop[0].fPoint, fTop[1].fPoint, lastPt);
- if (fOutputType == OutputType::kConicsWithInfiniteWeight) {
+ if (fPad32Count) {
// Output a 4-point conic with w=Inf.
- fVertexWriter->fill(GrVertexWriter::kIEEE_32_infinity, 2);
+ fVertexWriter->fill(fPad32Value, fPad32Count);
}
}
constexpr static int kStackPreallocCount = 32;
+ const int fPad32Count;
+ const uint32_t fPad32Value;
SkAutoSTMalloc<kStackPreallocCount, StackVertex> fVertexStack;
SkDEBUGCODE(int fStackAllocCount;)
StackVertex* fTop;
GrVertexWriter* fVertexWriter;
int fTotalClosedTriangleCount = 0;
- OutputType fOutputType;
};
#endif
diff --git a/src/gpu/tessellate/GrPathCurveTessellator.cpp b/src/gpu/tessellate/GrPathCurveTessellator.cpp
index bc796ec..f0b5423 100644
--- a/src/gpu/tessellate/GrPathCurveTessellator.cpp
+++ b/src/gpu/tessellate/GrPathCurveTessellator.cpp
@@ -7,6 +7,7 @@
#include "src/gpu/tessellate/GrPathCurveTessellator.h"
+#include "src/core/SkUtils.h"
#include "src/gpu/GrResourceProvider.h"
#include "src/gpu/geometry/GrPathUtils.h"
#include "src/gpu/geometry/GrWangsFormula.h"
@@ -29,44 +30,53 @@
, fMaxSegments_pow4(fMaxSegments_pow2 * fMaxSegments_pow2) {
}
- SK_ALWAYS_INLINE void writeQuadratic(GrVertexChunkBuilder* chunker, const SkPoint p[3]) {
+ SK_ALWAYS_INLINE void writeQuadratic(const GrShaderCaps& shaderCaps,
+ GrVertexChunkBuilder* chunker, const SkPoint p[3]) {
float numSegments_pow4 = GrWangsFormula::quadratic_pow4(kPrecision, p, fVectorXform);
if (numSegments_pow4 > fMaxSegments_pow4) {
- this->chopAndWriteQuadratic(chunker, p);
+ this->chopAndWriteQuadratic(shaderCaps, chunker, p);
return;
}
if (numSegments_pow4 > 1) {
if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
GrPathUtils::writeQuadAsCubic(p, &vertexWriter);
+ vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
+ GrTessellationShader::kCubicCurveType));
}
fNumFixedSegments_pow4 = std::max(numSegments_pow4, fNumFixedSegments_pow4);
}
}
- SK_ALWAYS_INLINE void writeConic(GrVertexChunkBuilder* chunker, const SkPoint p[3], float w) {
+ SK_ALWAYS_INLINE void writeConic(const GrShaderCaps& shaderCaps, GrVertexChunkBuilder* chunker,
+ const SkPoint p[3], float w) {
float numSegments_pow2 = GrWangsFormula::conic_pow2(kPrecision, p, w, fVectorXform);
if (numSegments_pow2 > fMaxSegments_pow2) {
- this->chopAndWriteConic(chunker, {p, w});
+ this->chopAndWriteConic(shaderCaps, chunker, {p, w});
return;
}
if (numSegments_pow2 > 1) {
if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
GrTessellationShader::WriteConicPatch(p, w, &vertexWriter);
+ vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
+ GrTessellationShader::kConicCurveType));
}
fNumFixedSegments_pow4 = std::max(numSegments_pow2 * numSegments_pow2,
fNumFixedSegments_pow4);
}
}
- SK_ALWAYS_INLINE void writeCubic(GrVertexChunkBuilder* chunker, const SkPoint p[4]) {
+ SK_ALWAYS_INLINE void writeCubic(const GrShaderCaps& shaderCaps, GrVertexChunkBuilder* chunker,
+ const SkPoint p[4]) {
float numSegments_pow4 = GrWangsFormula::cubic_pow4(kPrecision, p, fVectorXform);
if (numSegments_pow4 > fMaxSegments_pow4) {
- this->chopAndWriteCubic(chunker, p);
+ this->chopAndWriteCubic(shaderCaps, chunker, p);
return;
}
if (numSegments_pow4 > 1) {
if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
vertexWriter.writeArray(p, 4);
+ vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
+ GrTessellationShader::kCubicCurveType));
}
fNumFixedSegments_pow4 = std::max(numSegments_pow4, fNumFixedSegments_pow4);
}
@@ -75,51 +85,57 @@
int numFixedSegments_pow4() const { return fNumFixedSegments_pow4; }
private:
- void chopAndWriteQuadratic(GrVertexChunkBuilder* chunker, const SkPoint p[3]) {
+ void chopAndWriteQuadratic(const GrShaderCaps& shaderCaps, GrVertexChunkBuilder* chunker,
+ const SkPoint p[3]) {
SkPoint chops[5];
SkChopQuadAtHalf(p, chops);
for (int i = 0; i < 2; ++i) {
const SkPoint* q = chops + i*2;
if (fCullTest.areVisible3(q)) {
- this->writeQuadratic(chunker, q);
+ this->writeQuadratic(shaderCaps, chunker, q);
}
}
// Connect the two halves.
- this->writeTriangle(chunker, chops[0], chops[2], chops[4]);
+ this->writeTriangle(shaderCaps, chunker, chops[0], chops[2], chops[4]);
}
- void chopAndWriteConic(GrVertexChunkBuilder* chunker, const SkConic& conic) {
+ void chopAndWriteConic(const GrShaderCaps& shaderCaps, GrVertexChunkBuilder* chunker,
+ const SkConic& conic) {
SkConic chops[2];
if (!conic.chopAt(.5, chops)) {
return;
}
for (int i = 0; i < 2; ++i) {
if (fCullTest.areVisible3(chops[i].fPts)) {
- this->writeConic(chunker, chops[i].fPts, chops[i].fW);
+ this->writeConic(shaderCaps, chunker, chops[i].fPts, chops[i].fW);
}
}
// Connect the two halves.
- this->writeTriangle(chunker, conic.fPts[0], chops[0].fPts[2], chops[1].fPts[2]);
+ this->writeTriangle(shaderCaps, chunker, conic.fPts[0], chops[0].fPts[2], chops[1].fPts[2]);
}
- void chopAndWriteCubic(GrVertexChunkBuilder* chunker, const SkPoint p[4]) {
+ void chopAndWriteCubic(const GrShaderCaps& shaderCaps, GrVertexChunkBuilder* chunker,
+ const SkPoint p[4]) {
SkPoint chops[7];
SkChopCubicAtHalf(p, chops);
for (int i = 0; i < 2; ++i) {
const SkPoint* c = chops + i*3;
if (fCullTest.areVisible4(c)) {
- this->writeCubic(chunker, c);
+ this->writeCubic(shaderCaps, chunker, c);
}
}
// Connect the two halves.
- this->writeTriangle(chunker, chops[0], chops[3], chops[6]);
+ this->writeTriangle(shaderCaps, chunker, chops[0], chops[3], chops[6]);
}
- void writeTriangle(GrVertexChunkBuilder* chunker, SkPoint p0, SkPoint p1, SkPoint p2) {
+ void writeTriangle(const GrShaderCaps& shaderCaps, GrVertexChunkBuilder* chunker, SkPoint p0,
+ SkPoint p1, SkPoint p2) {
if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
vertexWriter.write(p0, p1, p2);
// Mark this instance as a triangle by setting it to a conic with w=Inf.
vertexWriter.fill(GrVertexWriter::kIEEE_32_infinity, 2);
+ vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
+ GrTessellationShader::kTriangularConicCurveType));
}
}
@@ -145,12 +161,14 @@
using PatchType = GrPathTessellationShader::PatchType;
GrPathTessellationShader* shader;
if (caps.shaderCaps()->tessellationSupport() &&
+ caps.shaderCaps()->infinitySupport() && // The hw tessellation shaders use infinity.
!pipeline.usesVaryingCoords() && // Our tessellation back door doesn't handle varyings.
numPathVerbs >= caps.minPathVerbsForHwTessellation()) {
shader = GrPathTessellationShader::MakeHardwareTessellationShader(arena, viewMatrix, color,
PatchType::kCurves);
} else {
- shader = GrPathTessellationShader::MakeMiddleOutFixedCountShader(arena, viewMatrix, color,
+ shader = GrPathTessellationShader::MakeMiddleOutFixedCountShader(*caps.shaderCaps(), arena,
+ viewMatrix, color,
PatchType::kCurves);
}
return arena->make([=](void* objStart) {
@@ -166,6 +184,8 @@
const BreadcrumbTriangleList* breadcrumbTriangleList) {
SkASSERT(fVertexChunkArray.empty());
+ const GrShaderCaps& shaderCaps = *target->caps().shaderCaps();
+
// Determine how many triangles to allocate.
int maxTriangles = 0;
if (fDrawInnerFan) {
@@ -183,7 +203,9 @@
if (!patchAllocCount) {
return;
}
- GrVertexChunkBuilder chunker(target, &fVertexChunkArray, sizeof(SkPoint) * 4, patchAllocCount);
+ size_t patchStride = fShader->willUseTessellationShaders() ? fShader->vertexStride() * 4
+ : fShader->instanceStride();
+ GrVertexChunkBuilder chunker(target, &fVertexChunkArray, patchStride, patchAllocCount);
// Write out the triangles.
if (maxTriangles) {
@@ -193,9 +215,16 @@
}
int numRemainingTriangles = maxTriangles;
if (fDrawInnerFan) {
- int numWritten = GrMiddleOutPolygonTriangulator::WritePathInnerFan(
- &vertexWriter,
- GrMiddleOutPolygonTriangulator::OutputType::kConicsWithInfiniteWeight, path);
+ // Pad the triangles with 2 infinities. This produces conic patches with w=Infinity. In
+ // the case where infinity is not supported, we also write out a 3rd float that
+ // explicitly tells the shader to interpret these patches as triangles.
+ int pad32Count = shaderCaps.infinitySupport() ? 2 : 3;
+ uint32_t pad32Value = shaderCaps.infinitySupport()
+ ? GrVertexWriter::kIEEE_32_infinity
+ : sk_bit_cast<uint32_t>(GrTessellationShader::kTriangularConicCurveType);
+ int numWritten = GrMiddleOutPolygonTriangulator::WritePathInnerFan(&vertexWriter,
+ pad32Count,
+ pad32Value, path);
numRemainingTriangles -= numWritten;
}
if (breadcrumbTriangleList) {
@@ -217,6 +246,9 @@
vertexWriter.writeArray(tri->fPts, 3);
// Mark this instance as a triangle by setting it to a conic with w=Inf.
vertexWriter.fill(GrVertexWriter::kIEEE_32_infinity, 2);
+ vertexWriter.write(
+ GrVertexWriter::If(!shaderCaps.infinitySupport(),
+ GrTessellationShader::kTriangularConicCurveType));
++numWritten;
}
SkASSERT(count == breadcrumbTriangleList->count());
@@ -230,7 +262,7 @@
// The curve shader tessellates T=0..(1/2) on the first side of the canonical triangle and
// T=(1/2)..1 on the second side. This means we get double the max tessellation segments
// for the range T=0..1.
- maxSegments = target->caps().shaderCaps()->maxTessellationSegments() * 2;
+ maxSegments = shaderCaps.maxTessellationSegments() * 2;
} else {
maxSegments = GrPathTessellationShader::kMaxFixedCountSegments;
}
@@ -239,13 +271,13 @@
for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
switch (verb) {
case SkPathVerb::kQuad:
- curveWriter.writeQuadratic(&chunker, pts);
+ curveWriter.writeQuadratic(shaderCaps, &chunker, pts);
break;
case SkPathVerb::kConic:
- curveWriter.writeConic(&chunker, pts, *w);
+ curveWriter.writeConic(shaderCaps, &chunker, pts, *w);
break;
case SkPathVerb::kCubic:
- curveWriter.writeCubic(&chunker, pts);
+ curveWriter.writeCubic(shaderCaps, &chunker, pts);
break;
default:
break;
diff --git a/src/gpu/tessellate/GrPathInnerTriangulateOp.cpp b/src/gpu/tessellate/GrPathInnerTriangulateOp.cpp
index 0a69e0a..be9875a 100644
--- a/src/gpu/tessellate/GrPathInnerTriangulateOp.cpp
+++ b/src/gpu/tessellate/GrPathInnerTriangulateOp.cpp
@@ -29,10 +29,17 @@
HullShader(const SkMatrix& viewMatrix, SkPMColor4f color, const GrShaderCaps& shaderCaps)
: GrPathTessellationShader(kTessellate_HullShader_ClassID,
GrPrimitiveType::kTriangleStrip, 0, viewMatrix, color) {
- constexpr static Attribute kPtsAttribs[] = {
- {"p01", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
- {"p23", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
- this->setInstanceAttributes(kPtsAttribs, SK_ARRAY_COUNT(kPtsAttribs));
+ fInstanceAttribs.emplace_back("p01", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
+ fInstanceAttribs.emplace_back("p23", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
+ if (!shaderCaps.infinitySupport()) {
+ // A conic curve is written out with p3=[w,Infinity], but GPUs that don't support
+ // infinity can't detect this. On these platforms we also write out an extra float with
+ // each patch that explicitly tells the shader what type of curve it is.
+ fInstanceAttribs.emplace_back("curveType", kFloat_GrVertexAttribType, kFloat_GrSLType);
+ }
+ this->setInstanceAttributes(fInstanceAttribs.data(), fInstanceAttribs.count());
+ SkASSERT(fInstanceAttribs.count() <= kMaxInstanceAttribCount);
+
if (!shaderCaps.vertexIDSupport()) {
constexpr static Attribute kVertexIdxAttrib("vertexidx", kFloat_GrVertexAttribType,
kFloat_GrSLType);
@@ -44,20 +51,39 @@
const char* name() const final { return "tessellate_HullShader"; }
void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
+
+ constexpr static int kMaxInstanceAttribCount = 3;
+ SkSTArray<kMaxInstanceAttribCount, Attribute> fInstanceAttribs;
};
GrGLSLGeometryProcessor* HullShader::createGLSLInstance(const GrShaderCaps&) const {
class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(const GrShaderCaps& shaderCaps, const GrPathTessellationShader&,
GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
+ if (shaderCaps.infinitySupport()) {
+ v->insertFunction(R"(
+ bool is_conic_curve() { return isinf(p23.w); }
+ bool is_non_triangular_conic_curve() {
+ // We consider a conic non-triangular as long as its weight isn't infinity.
+ // NOTE: "isinf == false" works on Mac Radeon GLSL; "!isinf" can get the wrong
+ // answer.
+ return isinf(p23.z) == false;
+ })");
+ } else {
+ v->insertFunction(SkStringPrintf(R"(
+ bool is_conic_curve() { return curveType != %g; })", kCubicCurveType).c_str());
+ v->insertFunction(SkStringPrintf(R"(
+ bool is_non_triangular_conic_curve() {
+ return curveType == %g;
+ })", kConicCurveType).c_str());
+ }
v->codeAppend(R"(
float2 p0=p01.xy, p1=p01.zw, p2=p23.xy, p3=p23.zw;
- if (isinf(p3.y)) { // Is the curve a conic?
+ if (is_conic_curve()) {
+ // Conics are 3 points, with the weight in p3.
float w = p3.x;
- p3 = p2;
- // A conic with w=Inf is an exact triangle.
- // NOTE: "isinf == false" works on Mac Radeon GLSL. "!isinf" gets the wrong answer.
- if (isinf(w) == false) {
+ p3 = p2; // Duplicate the endpoint for shared code that also runs on cubics.
+ if (is_non_triangular_conic_curve()) {
// Convert the points to a trapeziodal hull that circumcscribes the conic.
float2 p1w = p1 * w;
float T = .51; // Bias outward a bit to ensure we cover the outermost samples.
diff --git a/src/gpu/tessellate/GrPathStencilCoverOp.cpp b/src/gpu/tessellate/GrPathStencilCoverOp.cpp
index 1dfc2de..7bc5dea 100644
--- a/src/gpu/tessellate/GrPathStencilCoverOp.cpp
+++ b/src/gpu/tessellate/GrPathStencilCoverOp.cpp
@@ -184,9 +184,8 @@
GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fFanBuffer, &fFanBaseVertex);
int maxFanTriangles = fPath.countVerbs() - 2; // n - 2 triangles make an n-gon.
GrVertexWriter triangleVertexWriter = vertexAlloc.lock<SkPoint>(maxFanTriangles * 3);
- fFanVertexCount = GrMiddleOutPolygonTriangulator::WritePathInnerFan(
- &triangleVertexWriter, GrMiddleOutPolygonTriangulator::OutputType::kTriangles,
- fPath) * 3;
+ fFanVertexCount = 3 * GrMiddleOutPolygonTriangulator::WritePathInnerFan(
+ &triangleVertexWriter, 0, 0, fPath);
SkASSERT(fFanVertexCount <= maxFanTriangles * 3);
vertexAlloc.unlock(fFanVertexCount);
}
diff --git a/src/gpu/tessellate/GrPathWedgeTessellator.cpp b/src/gpu/tessellate/GrPathWedgeTessellator.cpp
index f311b2a..5949795 100644
--- a/src/gpu/tessellate/GrPathWedgeTessellator.cpp
+++ b/src/gpu/tessellate/GrPathWedgeTessellator.cpp
@@ -117,53 +117,65 @@
, fMaxSegments_pow4(fMaxSegments_pow2 * fMaxSegments_pow2) {
}
- SK_ALWAYS_INLINE void writeFlatWedge(GrVertexChunkBuilder* chunker, SkPoint p0, SkPoint p1,
+ SK_ALWAYS_INLINE void writeFlatWedge(const GrShaderCaps& shaderCaps,
+ GrVertexChunkBuilder* chunker, SkPoint p0, SkPoint p1,
SkPoint midpoint) {
if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
GrPathUtils::writeLineAsCubic(p0, p1, &vertexWriter);
vertexWriter.write(midpoint);
+ vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
+ GrTessellationShader::kCubicCurveType));
}
}
- SK_ALWAYS_INLINE void writeQuadraticWedge(GrVertexChunkBuilder* chunker, const SkPoint p[3],
+ SK_ALWAYS_INLINE void writeQuadraticWedge(const GrShaderCaps& shaderCaps,
+ GrVertexChunkBuilder* chunker, const SkPoint p[3],
SkPoint midpoint) {
float numSegments_pow4 = GrWangsFormula::quadratic_pow4(kPrecision, p, fVectorXform);
if (numSegments_pow4 > fMaxSegments_pow4) {
- this->chopAndWriteQuadraticWedges(chunker, p, midpoint);
+ this->chopAndWriteQuadraticWedges(shaderCaps, chunker, p, midpoint);
return;
}
if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
GrPathUtils::writeQuadAsCubic(p, &vertexWriter);
vertexWriter.write(midpoint);
+ vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
+ GrTessellationShader::kCubicCurveType));
}
fNumFixedSegments_pow4 = std::max(numSegments_pow4, fNumFixedSegments_pow4);
}
- SK_ALWAYS_INLINE void writeConicWedge(GrVertexChunkBuilder* chunker, const SkPoint p[3],
+ SK_ALWAYS_INLINE void writeConicWedge(const GrShaderCaps& shaderCaps,
+ GrVertexChunkBuilder* chunker, const SkPoint p[3],
float w, SkPoint midpoint) {
float numSegments_pow2 = GrWangsFormula::conic_pow2(kPrecision, p, w, fVectorXform);
if (GrWangsFormula::conic_pow2(kPrecision, p, w, fVectorXform) > fMaxSegments_pow2) {
- this->chopAndWriteConicWedges(chunker, {p, w}, midpoint);
+ this->chopAndWriteConicWedges(shaderCaps, chunker, {p, w}, midpoint);
return;
}
if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
GrTessellationShader::WriteConicPatch(p, w, &vertexWriter);
vertexWriter.write(midpoint);
+ vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
+ GrTessellationShader::kConicCurveType));
}
fNumFixedSegments_pow4 = std::max(numSegments_pow2 * numSegments_pow2,
fNumFixedSegments_pow4);
}
- SK_ALWAYS_INLINE void writeCubicWedge(GrVertexChunkBuilder* chunker, const SkPoint p[4],
+ SK_ALWAYS_INLINE void writeCubicWedge(const GrShaderCaps& shaderCaps,
+ GrVertexChunkBuilder* chunker, const SkPoint p[4],
SkPoint midpoint) {
float numSegments_pow4 = GrWangsFormula::cubic_pow4(kPrecision, p, fVectorXform);
if (numSegments_pow4 > fMaxSegments_pow4) {
- this->chopAndWriteCubicWedges(chunker, p, midpoint);
+ this->chopAndWriteCubicWedges(shaderCaps, chunker, p, midpoint);
return;
}
if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
vertexWriter.writeArray(p, 4);
vertexWriter.write(midpoint);
+ vertexWriter.write(GrVertexWriter::If(!shaderCaps.infinitySupport(),
+ GrTessellationShader::kCubicCurveType));
}
fNumFixedSegments_pow4 = std::max(numSegments_pow4, fNumFixedSegments_pow4);
}
@@ -171,45 +183,46 @@
int numFixedSegments_pow4() const { return fNumFixedSegments_pow4; }
private:
- void chopAndWriteQuadraticWedges(GrVertexChunkBuilder* chunker, const SkPoint p[3],
- SkPoint midpoint) {
+ void chopAndWriteQuadraticWedges(const GrShaderCaps& shaderCaps, GrVertexChunkBuilder* chunker,
+ const SkPoint p[3], SkPoint midpoint) {
SkPoint chops[5];
SkChopQuadAtHalf(p, chops);
for (int i = 0; i < 2; ++i) {
const SkPoint* q = chops + i*2;
if (fCullTest.areVisible3(q)) {
- this->writeQuadraticWedge(chunker, q, midpoint);
+ this->writeQuadraticWedge(shaderCaps, chunker, q, midpoint);
} else {
- this->writeFlatWedge(chunker, q[0], q[2], midpoint);
+ this->writeFlatWedge(shaderCaps, chunker, q[0], q[2], midpoint);
}
}
}
- void chopAndWriteConicWedges(GrVertexChunkBuilder* chunker, const SkConic& conic,
- SkPoint midpoint) {
+ void chopAndWriteConicWedges(const GrShaderCaps& shaderCaps, GrVertexChunkBuilder* chunker,
+ const SkConic& conic, SkPoint midpoint) {
SkConic chops[2];
if (!conic.chopAt(.5, chops)) {
return;
}
for (int i = 0; i < 2; ++i) {
if (fCullTest.areVisible3(chops[i].fPts)) {
- this->writeConicWedge(chunker, chops[i].fPts, chops[i].fW, midpoint);
+ this->writeConicWedge(shaderCaps, chunker, chops[i].fPts, chops[i].fW, midpoint);
} else {
- this->writeFlatWedge(chunker, chops[i].fPts[0], chops[i].fPts[2], midpoint);
+ this->writeFlatWedge(shaderCaps, chunker, chops[i].fPts[0], chops[i].fPts[2],
+ midpoint);
}
}
}
- void chopAndWriteCubicWedges(GrVertexChunkBuilder* chunker, const SkPoint p[4],
- SkPoint midpoint) {
+ void chopAndWriteCubicWedges(const GrShaderCaps& shaderCaps, GrVertexChunkBuilder* chunker,
+ const SkPoint p[4], SkPoint midpoint) {
SkPoint chops[7];
SkChopCubicAtHalf(p, chops);
for (int i = 0; i < 2; ++i) {
const SkPoint* c = chops + i*3;
if (fCullTest.areVisible4(c)) {
- this->writeCubicWedge(chunker, c, midpoint);
+ this->writeCubicWedge(shaderCaps, chunker, c, midpoint);
} else {
- this->writeFlatWedge(chunker, c[0], c[3], midpoint);
+ this->writeFlatWedge(shaderCaps, chunker, c[0], c[3], midpoint);
}
}
}
@@ -232,12 +245,14 @@
using PatchType = GrPathTessellationShader::PatchType;
GrPathTessellationShader* shader;
if (caps.shaderCaps()->tessellationSupport() &&
+ caps.shaderCaps()->infinitySupport() && // The hw tessellation shaders use infinity.
!pipeline.usesVaryingCoords() && // Our tessellation back door doesn't handle varyings.
numPathVerbs >= caps.minPathVerbsForHwTessellation()) {
shader = GrPathTessellationShader::MakeHardwareTessellationShader(arena, viewMatrix, color,
PatchType::kWedges);
} else {
- shader = GrPathTessellationShader::MakeMiddleOutFixedCountShader(arena, viewMatrix, color,
+ shader = GrPathTessellationShader::MakeMiddleOutFixedCountShader(*caps.shaderCaps(), arena,
+ viewMatrix, color,
PatchType::kWedges);
}
return arena->make([=](void* objStart) {
@@ -254,17 +269,21 @@
SkASSERT(!breadcrumbTriangleList);
SkASSERT(fVertexChunkArray.empty());
+ const GrShaderCaps& shaderCaps = *target->caps().shaderCaps();
+
// Over-allocate enough wedges for 1 in 4 to chop.
int maxWedges = GrPathTessellator::MaxSegmentsInPath(path);
int wedgeAllocCount = (maxWedges * 5 + 3) / 4; // i.e., ceil(maxWedges * 5/4)
if (!wedgeAllocCount) {
return;
}
- GrVertexChunkBuilder chunker(target, &fVertexChunkArray, sizeof(SkPoint) * 5, wedgeAllocCount);
+ size_t patchStride = fShader->willUseTessellationShaders() ? fShader->vertexStride() * 5
+ : fShader->instanceStride();
+ GrVertexChunkBuilder chunker(target, &fVertexChunkArray, patchStride, wedgeAllocCount);
int maxSegments;
if (fShader->willUseTessellationShaders()) {
- maxSegments = target->caps().shaderCaps()->maxTessellationSegments();
+ maxSegments = shaderCaps.maxTessellationSegments();
} else {
maxSegments = GrPathTessellationShader::kMaxFixedCountSegments;
}
@@ -283,25 +302,25 @@
case SkPathVerb::kClose:
break; // Ignore. We can assume an implicit close at the end.
case SkPathVerb::kLine:
- wedgeWriter.writeFlatWedge(&chunker, pts[0], pts[1], midpoint);
+ wedgeWriter.writeFlatWedge(shaderCaps, &chunker, pts[0], pts[1], midpoint);
lastPoint = pts[1];
break;
case SkPathVerb::kQuad:
- wedgeWriter.writeQuadraticWedge(&chunker, pts, midpoint);
+ wedgeWriter.writeQuadraticWedge(shaderCaps, &chunker, pts, midpoint);
lastPoint = pts[2];
break;
case SkPathVerb::kConic:
- wedgeWriter.writeConicWedge(&chunker, pts, *w, midpoint);
+ wedgeWriter.writeConicWedge(shaderCaps, &chunker, pts, *w, midpoint);
lastPoint = pts[2];
break;
case SkPathVerb::kCubic:
- wedgeWriter.writeCubicWedge(&chunker, pts, midpoint);
+ wedgeWriter.writeCubicWedge(shaderCaps, &chunker, pts, midpoint);
lastPoint = pts[3];
break;
}
}
if (lastPoint != startPoint) {
- wedgeWriter.writeFlatWedge(&chunker, lastPoint, startPoint, midpoint);
+ wedgeWriter.writeFlatWedge(shaderCaps, &chunker, lastPoint, startPoint, midpoint);
}
}
diff --git a/src/gpu/tessellate/GrStrokeFixedCountTessellator.cpp b/src/gpu/tessellate/GrStrokeFixedCountTessellator.cpp
index 3c181a6..4e66de6 100644
--- a/src/gpu/tessellate/GrStrokeFixedCountTessellator.cpp
+++ b/src/gpu/tessellate/GrStrokeFixedCountTessellator.cpp
@@ -26,10 +26,12 @@
public:
using ShaderFlags = GrStrokeTessellator::ShaderFlags;
- InstanceWriter(ShaderFlags shaderFlags, GrMeshDrawTarget* target, float matrixMaxScale,
- const SkRect& strokeCullBounds, const SkMatrix& viewMatrix,
- GrVertexChunkArray* patchChunks, size_t instanceStride, int minInstancesPerChunk)
- : fShaderFlags(shaderFlags)
+ InstanceWriter(const GrShaderCaps* shaderCaps, ShaderFlags shaderFlags,
+ GrMeshDrawTarget* target, float matrixMaxScale, const SkRect& strokeCullBounds,
+ const SkMatrix& viewMatrix, GrVertexChunkArray* patchChunks,
+ size_t instanceStride, int minInstancesPerChunk)
+ : fShaderCaps(shaderCaps)
+ , fShaderFlags(shaderFlags)
, fCullTest(strokeCullBounds, viewMatrix)
, fChunkBuilder(target, patchChunks, instanceStride, minInstancesPerChunk)
, fParametricPrecision(GrStrokeTolerances::CalcParametricPrecision(matrixMaxScale)) {
@@ -60,7 +62,7 @@
SK_ALWAYS_INLINE void lineTo(SkPoint start, SkPoint end) {
SkPoint cubic[] = {start, start, end, end};
SkPoint endControlPoint = start;
- this->writeStroke(cubic, endControlPoint);
+ this->writeStroke(cubic, endControlPoint, GrTessellationShader::kCubicCurveType);
}
SK_ALWAYS_INLINE void quadraticTo(const SkPoint p[3]) {
@@ -72,7 +74,7 @@
SkPoint cubic[4];
GrPathUtils::convertQuadToCubic(p, cubic);
SkPoint endControlPoint = cubic[2];
- this->writeStroke(cubic, endControlPoint);
+ this->writeStroke(cubic, endControlPoint, GrTessellationShader::kCubicCurveType);
fMaxParametricSegments_pow4 = std::max(numParametricSegments_pow4,
fMaxParametricSegments_pow4);
}
@@ -87,7 +89,7 @@
SkPoint conic[4];
GrTessellationShader::WriteConicPatch(p, w, conic);
SkPoint endControlPoint = conic[1];
- this->writeStroke(conic, endControlPoint);
+ this->writeStroke(conic, endControlPoint, GrTessellationShader::kConicCurveType);
fMaxParametricSegments_pow4 = std::max(numParametricSegments_pow4,
fMaxParametricSegments_pow4);
}
@@ -99,7 +101,7 @@
return;
}
SkPoint endControlPoint = (p[3] != p[2]) ? p[2] : (p[2] != p[1]) ? p[1] : p[0];
- this->writeStroke(p, endControlPoint);
+ this->writeStroke(p, endControlPoint, GrTessellationShader::kCubicCurveType);
fMaxParametricSegments_pow4 = std::max(numParametricSegments_pow4,
fMaxParametricSegments_pow4);
}
@@ -118,6 +120,8 @@
// The shader interprets an empty stroke + empty join as a special case that denotes a
// circle, or 180-degree point stroke.
writer.fill(location, 5);
+ writer.write(GrVertexWriter::If(!fShaderCaps->infinitySupport(),
+ GrTessellationShader::kCubicCurveType));
this->writeDynamicAttribs(&writer);
}
}
@@ -127,7 +131,8 @@
// We deferred the first stroke because we didn't know the previous control point to use
// for its join. We write it out now.
SkASSERT(fHasLastControlPoint);
- this->writeStroke(fDeferredFirstStroke, SkPoint());
+ this->writeStroke(fDeferredFirstStroke, SkPoint(),
+ fDeferredCurveTypeIfUnsupportedInfinity);
fHasDeferredFirstStroke = false;
}
fHasLastControlPoint = false;
@@ -174,16 +179,20 @@
}
}
- SK_ALWAYS_INLINE void writeStroke(const SkPoint p[4], SkPoint endControlPoint) {
+ SK_ALWAYS_INLINE void writeStroke(const SkPoint p[4], SkPoint endControlPoint,
+ float curveTypeIfUnsupportedInfinity) {
if (!fHasLastControlPoint) {
// We don't know the previous control point yet to use for the join. Defer writing out
// this stroke until the end.
memcpy(fDeferredFirstStroke, p, sizeof(fDeferredFirstStroke));
+ fDeferredCurveTypeIfUnsupportedInfinity = curveTypeIfUnsupportedInfinity;
fHasDeferredFirstStroke = true;
fHasLastControlPoint = true;
} else if (GrVertexWriter writer = fChunkBuilder.appendVertex()) {
writer.writeArray(p, 4);
writer.write(fLastControlPoint);
+ writer.write(GrVertexWriter::If(!fShaderCaps->infinitySupport(),
+ curveTypeIfUnsupportedInfinity));
this->writeDynamicAttribs(&writer);
}
fLastControlPoint = endControlPoint;
@@ -205,6 +214,7 @@
fHasLastControlPoint = true;
}
+ const GrShaderCaps* fShaderCaps;
const ShaderFlags fShaderFlags;
const GrCullTest fCullTest;
GrVertexChunkBuilder fChunkBuilder;
@@ -213,6 +223,7 @@
// We can't write out the first stroke until we know the previous control point for its join.
SkPoint fDeferredFirstStroke[4];
+ float fDeferredCurveTypeIfUnsupportedInfinity;
SkPoint fLastControlPoint; // Used to configure the joins in the instance data.
bool fHasDeferredFirstStroke = false;
bool fHasLastControlPoint = false;
@@ -259,9 +270,9 @@
int strokePreallocCount = totalCombinedVerbCnt * 2;
int capPreallocCount = 8;
int minInstancesPerChunk = strokePreallocCount + capPreallocCount;
- InstanceWriter instanceWriter(fShader.flags(), target, fMatrixMinMaxScales[1],
- fStrokeCullBounds, fShader.viewMatrix(), &fInstanceChunks,
- fShader.instanceStride(), minInstancesPerChunk);
+ InstanceWriter instanceWriter(target->caps().shaderCaps(), fShader.flags(), target,
+ fMatrixMinMaxScales[1], fStrokeCullBounds, fShader.viewMatrix(),
+ &fInstanceChunks, fShader.instanceStride(), minInstancesPerChunk);
if (!fShader.hasDynamicStroke()) {
// Strokes are static. Calculate tolerances once.
diff --git a/src/gpu/tessellate/GrStrokeTessellateOp.cpp b/src/gpu/tessellate/GrStrokeTessellateOp.cpp
index df87b4c..d83670d 100644
--- a/src/gpu/tessellate/GrStrokeTessellateOp.cpp
+++ b/src/gpu/tessellate/GrStrokeTessellateOp.cpp
@@ -147,7 +147,8 @@
0xffff>());
bool can_use_hardware_tessellation(int numVerbs, const GrPipeline& pipeline, const GrCaps& caps) {
- if (!caps.shaderCaps()->tessellationSupport()) {
+ if (!caps.shaderCaps()->tessellationSupport() ||
+ !caps.shaderCaps()->infinitySupport() /* The hw tessellation shaders use infinity. */) {
return false;
}
if (pipeline.usesVaryingCoords()) {
diff --git a/src/gpu/tessellate/GrTessellationPathRenderer.cpp b/src/gpu/tessellate/GrTessellationPathRenderer.cpp
index b80071f..be3a73b 100644
--- a/src/gpu/tessellate/GrTessellationPathRenderer.cpp
+++ b/src/gpu/tessellate/GrTessellationPathRenderer.cpp
@@ -39,7 +39,6 @@
bool GrTessellationPathRenderer::IsSupported(const GrCaps& caps) {
return !caps.avoidStencilBuffers() &&
caps.drawInstancedSupport() &&
- caps.shaderCaps()->infinitySupport() &&
!caps.disableTessellationPathRenderer();
}
diff --git a/src/gpu/tessellate/shaders/GrPathTessellationShader.h b/src/gpu/tessellate/shaders/GrPathTessellationShader.h
index 844bd15..a2000b0 100644
--- a/src/gpu/tessellate/shaders/GrPathTessellationShader.h
+++ b/src/gpu/tessellate/shaders/GrPathTessellationShader.h
@@ -50,7 +50,8 @@
// smoothly, and emits empty triangles at any vertices whose sk_VertexIDs are higher than
// necessary. It is the caller's responsibility to draw enough vertices per instance for the
// most complex curve in the batch to render smoothly (i.e., NumTrianglesAtResolveLevel() * 3).
- static GrPathTessellationShader* MakeMiddleOutFixedCountShader(SkArenaAlloc*,
+ static GrPathTessellationShader* MakeMiddleOutFixedCountShader(const GrShaderCaps&,
+ SkArenaAlloc*,
const SkMatrix& viewMatrix,
const SkPMColor4f&, PatchType);
diff --git a/src/gpu/tessellate/shaders/GrPathTessellationShader_MiddleOut.cpp b/src/gpu/tessellate/shaders/GrPathTessellationShader_MiddleOut.cpp
index 8e6c7c5..da8a82d 100644
--- a/src/gpu/tessellate/shaders/GrPathTessellationShader_MiddleOut.cpp
+++ b/src/gpu/tessellate/shaders/GrPathTessellationShader_MiddleOut.cpp
@@ -27,7 +27,8 @@
// batch to render smoothly (i.e., NumTrianglesAtResolveLevel() * 3).
class MiddleOutShader : public GrPathTessellationShader {
public:
- MiddleOutShader(const SkMatrix& viewMatrix, const SkPMColor4f& color, PatchType patchType)
+ MiddleOutShader(const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix,
+ const SkPMColor4f& color, PatchType patchType)
: GrPathTessellationShader(kTessellate_MiddleOutShader_ClassID,
GrPrimitiveType::kTriangles, 0, viewMatrix, color)
, fPatchType(patchType) {
@@ -36,6 +37,12 @@
if (fPatchType == PatchType::kWedges) {
fInstanceAttribs.emplace_back("fanPoint", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
}
+ if (!shaderCaps.infinitySupport()) {
+ // A conic curve is written out with p3=[w,Infinity], but GPUs that don't support
+ // infinity can't detect this. On these platforms we also write out an extra float with
+ // each patch that explicitly tells the shader what type of curve it is.
+ fInstanceAttribs.emplace_back("curveType", kFloat_GrVertexAttribType, kFloat_GrSLType);
+ }
this->setInstanceAttributes(fInstanceAttribs.data(), fInstanceAttribs.count());
SkASSERT(fInstanceAttribs.count() <= kMaxInstanceAttribCount);
@@ -52,7 +59,7 @@
GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
const PatchType fPatchType;
- constexpr static int kMaxInstanceAttribCount = 3;
+ constexpr static int kMaxInstanceAttribCount = 4;
SkSTArray<kMaxInstanceAttribCount, Attribute> fInstanceAttribs;
};
@@ -64,6 +71,18 @@
v->defineConstant("MAX_FIXED_RESOLVE_LEVEL", (float)kMaxFixedCountResolveLevel);
v->defineConstant("MAX_FIXED_SEGMENTS", (float)kMaxFixedCountSegments);
v->insertFunction(GrWangsFormula::as_sksl().c_str());
+ if (shaderCaps.infinitySupport()) {
+ v->insertFunction(R"(
+ bool is_conic_curve() { return isinf(p23.w); }
+ bool is_triangular_conic_curve() { return isinf(p23.z); })");
+ } else {
+ v->insertFunction(SkStringPrintf(R"(
+ bool is_conic_curve() { return curveType != %g; })", kCubicCurveType).c_str());
+ v->insertFunction(SkStringPrintf(R"(
+ bool is_triangular_conic_curve() {
+ return curveType == %g;
+ })", kTriangularConicCurveType).c_str());
+ }
if (shaderCaps.bitManipulationSupport()) {
v->insertFunction(R"(
float ldexp_portable(float x, float p) {
@@ -87,8 +106,8 @@
} else)"); // Fall through to next if ().
}
v->codeAppend(R"(
- if (isinf(p23.z)) {
- // A conic with w=Inf is an exact triangle.
+ if (is_triangular_conic_curve()) {
+ // This patch is an exact triangle.
localcoord = (resolveLevel != 0) ? p01.zw
: (idxInResolveLevel != 0) ? p23.xy
: p01.xy;
@@ -96,14 +115,14 @@
float2 p0=p01.xy, p1=p01.zw, p2=p23.xy, p3=p23.zw;
float w = -1; // w < 0 tells us to treat the instance as an integral cubic.
float maxResolveLevel;
- if (isinf(p3.y)) {
- // The patch is a conic.
+ if (is_conic_curve()) {
+ // Conics are 3 points, with the weight in p3.
w = p3.x;
maxResolveLevel = wangs_formula_conic_log2(PRECISION, AFFINE_MATRIX * p0,
AFFINE_MATRIX * p1,
AFFINE_MATRIX * p2, w);
p1 *= w; // Unproject p1.
- p3 = p2; // Duplicate the endpoint.
+ p3 = p2; // Duplicate the endpoint for shared code that also runs on cubics.
} else {
// The patch is an integral cubic.
maxResolveLevel = wangs_formula_cubic_log2(PRECISION, p0, p1, p2, p3,
@@ -154,9 +173,9 @@
} // namespace
GrPathTessellationShader* GrPathTessellationShader::MakeMiddleOutFixedCountShader(
- SkArenaAlloc* arena, const SkMatrix& viewMatrix, const SkPMColor4f& color,
- PatchType patchType) {
- return arena->make<MiddleOutShader>(viewMatrix, color, patchType);
+ const GrShaderCaps& shaderCaps, SkArenaAlloc* arena, const SkMatrix& viewMatrix,
+ const SkPMColor4f& color, PatchType patchType) {
+ return arena->make<MiddleOutShader>(shaderCaps, viewMatrix, color, patchType);
}
void GrPathTessellationShader::InitializeVertexBufferForMiddleOutCurves(GrVertexWriter vertexWriter,
diff --git a/src/gpu/tessellate/shaders/GrStrokeTessellationShader.cpp b/src/gpu/tessellate/shaders/GrStrokeTessellationShader.cpp
index a524ce7..2e88d9f 100644
--- a/src/gpu/tessellate/shaders/GrStrokeTessellationShader.cpp
+++ b/src/gpu/tessellate/shaders/GrStrokeTessellationShader.cpp
@@ -64,6 +64,12 @@
// argsAttr contains the lastControlPoint for setting up the join.
fAttribs.emplace_back("argsAttr", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
}
+ if (!shaderCaps.infinitySupport()) {
+ // A conic curve is written out with p3=[w,Infinity], but GPUs that don't support
+ // infinity can't detect this. On these platforms we write out an extra float with each
+ // patch that explicitly tells the shader what type of curve it is.
+ fAttribs.emplace_back("curveTypeAttr", kFloat_GrVertexAttribType, kFloat_GrSLType);
+ }
}
if (fShaderFlags & ShaderFlags::kDynamicStroke) {
fAttribs.emplace_back("dynamicStrokeAttr", kFloat2_GrVertexAttribType,
diff --git a/src/gpu/tessellate/shaders/GrStrokeTessellationShader.h b/src/gpu/tessellate/shaders/GrStrokeTessellationShader.h
index 3b5e24f..2da360e 100644
--- a/src/gpu/tessellate/shaders/GrStrokeTessellationShader.h
+++ b/src/gpu/tessellate/shaders/GrStrokeTessellationShader.h
@@ -141,7 +141,7 @@
const SkStrokeRec fStroke;
const int8_t fMaxParametricSegments_log2;
- constexpr static int kMaxAttribCount = 5;
+ constexpr static int kMaxAttribCount = 6;
SkSTArray<kMaxAttribCount, Attribute> fAttribs;
// This is a uniform value used when fMode is kFixedCount that tells the shader how many total
diff --git a/src/gpu/tessellate/shaders/GrStrokeTessellationShader_InstancedImpl.cpp b/src/gpu/tessellate/shaders/GrStrokeTessellationShader_InstancedImpl.cpp
index a8c3e29..42081f0 100644
--- a/src/gpu/tessellate/shaders/GrStrokeTessellationShader_InstancedImpl.cpp
+++ b/src/gpu/tessellate/shaders/GrStrokeTessellationShader_InstancedImpl.cpp
@@ -84,13 +84,22 @@
args.fVertBuilder->codeAppendf("float2x2 AFFINE_MATRIX = float2x2(%s);\n", affineMatrixName);
args.fVertBuilder->codeAppendf("float2 TRANSLATE = %s;\n", translateName);
+ if (args.fShaderCaps->infinitySupport()) {
+ args.fVertBuilder->insertFunction(R"(
+ bool is_conic_curve() { return isinf(pts23Attr.w); })");
+ } else {
+ args.fVertBuilder->insertFunction(SkStringPrintf(R"(
+ bool is_conic_curve() { return curveTypeAttr != %g; })", kCubicCurveType).c_str());
+ }
+
// Tessellation code.
args.fVertBuilder->codeAppend(R"(
float2 p0=pts01Attr.xy, p1=pts01Attr.zw, p2=pts23Attr.xy, p3=pts23Attr.zw;
float2 lastControlPoint = argsAttr.xy;
float w = -1; // w<0 means the curve is an integral cubic.
- if (isinf(p3.y)) {
- w = p3.x; // The curve is actually a conic.
+ if (is_conic_curve()) {
+ // Conics are 3 points, with the weight in p3.
+ w = p3.x;
p3 = p2; // Setting p3 equal to p2 works for the remaining rotational logic.
})");
if (shader.stroke().isHairlineStyle()) {
diff --git a/src/gpu/tessellate/shaders/GrTessellationShader.h b/src/gpu/tessellate/shaders/GrTessellationShader.h
index b565dcd..ef02ea1 100644
--- a/src/gpu/tessellate/shaders/GrTessellationShader.h
+++ b/src/gpu/tessellate/shaders/GrTessellationShader.h
@@ -39,6 +39,13 @@
const SkMatrix& viewMatrix() const { return fViewMatrix; }
const SkPMColor4f& color() const { return fColor;}
+ // A conic curve is written out with p3=[w,Infinity], but GPUs that don't support infinity can't
+ // detect this. On these platforms we also write out an extra float with each patch that
+ // explicitly tells the shader what type of curve it is.
+ constexpr static float kCubicCurveType = 0;
+ constexpr static float kConicCurveType = 1;
+ constexpr static float kTriangularConicCurveType = 2; // Conic curve with w=Infinity.
+
// Fills in a 4-point patch in such a way that the shader will recognize it as a conic.
static void WriteConicPatch(const SkPoint pts[3], float w, GrVertexWriter* writer) {
// Write out the 3 conic points to patch[0..2], the weight to patch[3].x, and then set