From 6904303a66737b6fde5605d53161f40590a7f2a2 Mon Sep 17 00:00:00 2001 From: Chris Dalton Date: Thu, 1 Jul 2021 11:17:53 -0600 Subject: [PATCH] Add an isinf() polyfill for tessellation shaders Tessellation uses infinity to flag conics, and also as a conic weight that converts them into triangles. The es2 shading language doesn't support infinity. This CL adds a "portable infinity" mode, where we pretend inputs >= 2^126 are infinity. The rationale for doing this is that those numbers are so large, they will overflow if we try to do any bezier math with then anyway. Bug: chromium:1220246 Change-Id: I3ad29ebb6fbbad5d23cfdba7a2717605009f8180 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/423697 Commit-Queue: Chris Dalton Reviewed-by: Brian Osman --- bench/TessellateBench.cpp | 11 +++-- src/gpu/GrShaderCaps.cpp | 2 + src/gpu/GrShaderCaps.h | 6 ++- src/gpu/GrVertexWriter.h | 2 - src/gpu/d3d/GrD3DCaps.cpp | 1 + src/gpu/gl/GrGLCaps.cpp | 7 +++ src/gpu/mtl/GrMtlCaps.mm | 1 + .../GrMiddleOutPolygonTriangulator.h | 29 ++++++------ src/gpu/tessellate/GrPathCurveTessellator.cpp | 24 ++++++---- .../tessellate/GrPathInnerTriangulateOp.cpp | 12 ++--- src/gpu/tessellate/GrPathStencilCoverOp.cpp | 12 +++-- src/gpu/tessellate/GrPathWedgeTessellator.cpp | 16 ++++--- .../GrStrokeFixedCountTessellator.cpp | 29 ++++++------ .../GrStrokeFixedCountTessellator.h | 6 +-- .../GrStrokeHardwareTessellator.cpp | 41 +++++++++-------- .../tessellate/GrStrokeHardwareTessellator.h | 2 +- src/gpu/tessellate/GrStrokeTessellateOp.cpp | 9 ++-- src/gpu/tessellate/GrStrokeTessellator.h | 12 ++--- .../tessellate/GrTessellationPathRenderer.cpp | 1 + .../shaders/GrPathTessellationShader.cpp | 19 ++------ .../shaders/GrPathTessellationShader.h | 9 +--- .../GrPathTessellationShader_Hardware.cpp | 26 ++++++----- .../GrPathTessellationShader_MiddleOut.cpp | 12 ++--- .../shaders/GrStrokeTessellationShader.cpp | 6 +-- .../shaders/GrStrokeTessellationShader.h | 4 +- ...rStrokeTessellationShader_HardwareImpl.cpp | 21 +++++---- ...StrokeTessellationShader_InstancedImpl.cpp | 3 +- .../tessellate/shaders/GrTessellationShader.h | 44 +++++++++++++++++-- src/gpu/vk/GrVkCaps.cpp | 1 + 29 files changed, 210 insertions(+), 158 deletions(-) diff --git a/bench/TessellateBench.cpp b/bench/TessellateBench.cpp index f5e9e3f5a0..7a6cb228a4 100644 --- a/bench/TessellateBench.cpp +++ b/bench/TessellateBench.cpp @@ -202,8 +202,7 @@ DEF_PATH_TESS_BENCH(middle_out_triangulation, int baseVertex; GrVertexWriter vertexWriter = static_cast(fTarget->makeVertexSpace( sizeof(SkPoint), kNumCubicsInChalkboard, &buffer, &baseVertex)); - GrMiddleOutPolygonTriangulator::WritePathInnerFan( - &vertexWriter, GrMiddleOutPolygonTriangulator::OutputType::kTriangles, fPath); + GrMiddleOutPolygonTriangulator::WritePathInnerFan(&vertexWriter, 0, 0, fPath); } using PathStrokeList = GrStrokeTessellator::PathStrokeList; @@ -216,7 +215,7 @@ static std::unique_ptr make_hw_tessellator( ShaderFlags shaderFlags, const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, PathStrokeList* pathStrokeList, std::array matrixMinMaxScales, const SkRect& strokeCullBounds) { - return std::make_unique(shaderFlags, shaderCaps, viewMatrix, + return std::make_unique(shaderCaps, shaderFlags, viewMatrix, pathStrokeList, matrixMinMaxScales, strokeCullBounds); } @@ -225,9 +224,9 @@ static std::unique_ptr make_fixed_count_tessellator( ShaderFlags shaderFlags, const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, PathStrokeList* pathStrokeList, std::array matrixMinMaxScales, const SkRect& strokeCullBounds) { - return std::make_unique(shaderFlags, viewMatrix, pathStrokeList, - matrixMinMaxScales, strokeCullBounds, - shaderCaps); + return std::make_unique(shaderCaps, shaderFlags, viewMatrix, + pathStrokeList, matrixMinMaxScales, + strokeCullBounds); } using MakePathStrokesFn = std::vector(*)(); diff --git a/src/gpu/GrShaderCaps.cpp b/src/gpu/GrShaderCaps.cpp index aa2cf3e634..b1194d3fbd 100644 --- a/src/gpu/GrShaderCaps.cpp +++ b/src/gpu/GrShaderCaps.cpp @@ -53,6 +53,7 @@ GrShaderCaps::GrShaderCaps(const GrContextOptions& options) { fSampleMaskSupport = false; fExternalTextureSupport = false; fVertexIDSupport = false; + fInfinitySupport = false; fBitManipulationSupport = false; fFloatIs32Bits = true; fHalfIs32Bits = false; @@ -140,6 +141,7 @@ void GrShaderCaps::dumpJSON(SkJSONWriter* writer) const { writer->appendBool("Sample mask support", fSampleMaskSupport); writer->appendBool("External texture support", fExternalTextureSupport); writer->appendBool("sk_VertexID support", fVertexIDSupport); + writer->appendBool("Infinity support", fInfinitySupport); writer->appendBool("Bit manipulation support", fBitManipulationSupport); writer->appendBool("float == fp32", fFloatIs32Bits); writer->appendBool("half == fp32", fHalfIs32Bits); diff --git a/src/gpu/GrShaderCaps.h b/src/gpu/GrShaderCaps.h index 576d95c107..7eb8992875 100644 --- a/src/gpu/GrShaderCaps.h +++ b/src/gpu/GrShaderCaps.h @@ -76,7 +76,10 @@ public: bool vertexIDSupport() const { return fVertexIDSupport; } - // frexp, ldexp, findMSB, findLSB. + // isinf() is defined, and floating point infinities are handled according to IEEE standards. + bool infinitySupport() const { return fInfinitySupport; } + + // frexp(), ldexp(), findMSB(), findLSB(). bool bitManipulationSupport() const { return fBitManipulationSupport; } bool floatIs32Bits() const { return fFloatIs32Bits; } @@ -297,6 +300,7 @@ private: bool fSampleMaskSupport : 1; bool fExternalTextureSupport : 1; bool fVertexIDSupport : 1; + bool fInfinitySupport : 1; bool fBitManipulationSupport : 1; bool fFloatIs32Bits : 1; bool fHalfIs32Bits : 1; diff --git a/src/gpu/GrVertexWriter.h b/src/gpu/GrVertexWriter.h index a4c13a3216..b926ab8063 100644 --- a/src/gpu/GrVertexWriter.h +++ b/src/gpu/GrVertexWriter.h @@ -23,8 +23,6 @@ * thereof. */ struct GrVertexWriter { - constexpr static uint32_t kIEEE_32_infinity = 0x7f800000; - void* fPtr; GrVertexWriter() = default; diff --git a/src/gpu/d3d/GrD3DCaps.cpp b/src/gpu/d3d/GrD3DCaps.cpp index 9487ab7368..9597280734 100644 --- a/src/gpu/d3d/GrD3DCaps.cpp +++ b/src/gpu/d3d/GrD3DCaps.cpp @@ -243,6 +243,7 @@ void GrD3DCaps::initShaderCaps(int vendorID, const D3D12_FEATURE_DATA_D3D12_OPTI shaderCaps->fIntegerSupport = true; shaderCaps->fVertexIDSupport = true; + shaderCaps->fInfinitySupport = true; shaderCaps->fBitManipulationSupport = true; shaderCaps->fFloatIs32Bits = true; diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp index b933af7356..89dbe7c423 100644 --- a/src/gpu/gl/GrGLCaps.cpp +++ b/src/gpu/gl/GrGLCaps.cpp @@ -971,6 +971,13 @@ void GrGLCaps::initGLSL(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli shaderCaps->fVertexIDSupport = ctxInfo.glslGeneration() >= k330_GrGLSLGeneration; } + if (GR_IS_GR_GL(standard)) { + shaderCaps->fInfinitySupport = true; + } else if (GR_IS_GR_GL_ES(standard) || GR_IS_GR_WEBGL(standard)) { + // Desktop GLSL 3.30 == ES GLSL 3.00. + shaderCaps->fInfinitySupport = ctxInfo.glslGeneration() >= k330_GrGLSLGeneration; + } + if (GR_IS_GR_GL(standard)) { shaderCaps->fBitManipulationSupport = ctxInfo.glslGeneration() >= k400_GrGLSLGeneration; } else if (GR_IS_GR_GL_ES(standard) || GR_IS_GR_WEBGL(standard)) { diff --git a/src/gpu/mtl/GrMtlCaps.mm b/src/gpu/mtl/GrMtlCaps.mm index 1a451895c1..82f76e0f24 100644 --- a/src/gpu/mtl/GrMtlCaps.mm +++ b/src/gpu/mtl/GrMtlCaps.mm @@ -476,6 +476,7 @@ void GrMtlCaps::initShaderCaps() { shaderCaps->fIntegerSupport = true; shaderCaps->fNonsquareMatrixSupport = true; shaderCaps->fVertexIDSupport = false; + shaderCaps->fInfinitySupport = true; // Metal uses IEEE float and half floats so assuming those values here. shaderCaps->fFloatIs32Bits = true; diff --git a/src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h b/src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h index 79b80b3cdd..a2df17b7df 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 @@ public: 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 @@ private: 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 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 bc796ece79..459bf4886f 100644 --- a/src/gpu/tessellate/GrPathCurveTessellator.cpp +++ b/src/gpu/tessellate/GrPathCurveTessellator.cpp @@ -22,8 +22,10 @@ constexpr static float kPrecision = GrTessellationShader::kLinearizationPrecisio // supported by the hardware. class CurveWriter { public: - CurveWriter(const SkRect& cullBounds, const SkMatrix& viewMatrix, int maxSegments) - : fCullTest(cullBounds, viewMatrix) + CurveWriter(const GrShaderCaps& shaderCaps, const SkRect& cullBounds, + const SkMatrix& viewMatrix, int maxSegments) + : fShaderCaps(shaderCaps) + , fCullTest(cullBounds, viewMatrix) , fVectorXform(viewMatrix) , fMaxSegments_pow2(maxSegments * maxSegments) , fMaxSegments_pow4(fMaxSegments_pow2 * fMaxSegments_pow2) { @@ -51,7 +53,7 @@ public: } if (numSegments_pow2 > 1) { if (GrVertexWriter vertexWriter = chunker->appendVertex()) { - GrTessellationShader::WriteConicPatch(p, w, &vertexWriter); + GrTessellationShader::WriteConicPatch(fShaderCaps, p, w, &vertexWriter); } fNumFixedSegments_pow4 = std::max(numSegments_pow2 * numSegments_pow2, fNumFixedSegments_pow4); @@ -119,10 +121,11 @@ private: 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.fill(GrTessellationShader::PortableInfinityBits32(fShaderCaps), 2); } } + const GrShaderCaps& fShaderCaps; GrCullTest fCullTest; GrVectorXform fVectorXform; const float fMaxSegments_pow2; @@ -166,6 +169,8 @@ void GrPathCurveTessellator::prepare(GrMeshDrawTarget* target, const SkRect& cul const BreadcrumbTriangleList* breadcrumbTriangleList) { SkASSERT(fVertexChunkArray.empty()); + const GrShaderCaps& shaderCaps = *target->caps().shaderCaps(); + // Determine how many triangles to allocate. int maxTriangles = 0; if (fDrawInnerFan) { @@ -193,9 +198,10 @@ void GrPathCurveTessellator::prepare(GrMeshDrawTarget* target, const SkRect& cul } int numRemainingTriangles = maxTriangles; if (fDrawInnerFan) { + // Pad the triangles with 2 infinities. This produces conic patches with w=Inf. int numWritten = GrMiddleOutPolygonTriangulator::WritePathInnerFan( - &vertexWriter, - GrMiddleOutPolygonTriangulator::OutputType::kConicsWithInfiniteWeight, path); + &vertexWriter, 2, GrPathTessellationShader::PortableInfinityBits32(shaderCaps), + path); numRemainingTriangles -= numWritten; } if (breadcrumbTriangleList) { @@ -216,7 +222,7 @@ void GrPathCurveTessellator::prepare(GrMeshDrawTarget* target, const SkRect& cul } 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.fill(GrTessellationShader::PortableInfinityBits32(shaderCaps), 2); ++numWritten; } SkASSERT(count == breadcrumbTriangleList->count()); @@ -230,12 +236,12 @@ void GrPathCurveTessellator::prepare(GrMeshDrawTarget* target, const SkRect& cul // 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; } - CurveWriter curveWriter(cullBounds, fShader->viewMatrix(), maxSegments); + CurveWriter curveWriter(shaderCaps, cullBounds, fShader->viewMatrix(), maxSegments); for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) { switch (verb) { case SkPathVerb::kQuad: diff --git a/src/gpu/tessellate/GrPathInnerTriangulateOp.cpp b/src/gpu/tessellate/GrPathInnerTriangulateOp.cpp index e2463e544b..bfa9cbfc94 100644 --- a/src/gpu/tessellate/GrPathInnerTriangulateOp.cpp +++ b/src/gpu/tessellate/GrPathInnerTriangulateOp.cpp @@ -13,7 +13,6 @@ #include "src/gpu/GrOpFlushState.h" #include "src/gpu/GrRecordingContextPriv.h" #include "src/gpu/GrResourceProvider.h" -#include "src/gpu/glsl/GrGLSLProgramBuilder.h" #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h" #include "src/gpu/tessellate/GrPathCurveTessellator.h" #include "src/gpu/tessellate/GrTessellationPathRenderer.h" @@ -49,13 +48,14 @@ private: GrGLSLGeometryProcessor* HullShader::createGLSLInstance(const GrShaderCaps&) const { class Impl : public GrPathTessellationShader::Impl { - void emitVertexCode(const GrPathTessellationShader&, GrGLSLVertexBuilder* v, - GrGPArgs* gpArgs) override { + void emitVertexCode(const GrShaderCaps& shaderCaps, const GrPathTessellationShader&, + GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override { + v->insertFunction(SkSLPortable_isinf(shaderCaps)); v->codeAppend(R"( float4x2 P = float4x2(input_points_0_1, input_points_2_3); - if (isinf(P[3].y)) { // Is the curve a conic? + if (isinf_portable(P[3].y)) { // Is the curve a conic? float w = P[3].x; - if (isinf(w)) { + if (isinf_portable(w)) { // A conic with w=Inf is an exact triangle. P = float4x2(P[0], P[1], P[2], P[2]); } else { @@ -84,7 +84,7 @@ GrGLSLGeometryProcessor* HullShader::createGLSLInstance(const GrShaderCaps&) con } })"); - if (v->getProgramBuilder()->caps()->shaderCaps()->vertexIDSupport()) { + if (shaderCaps.vertexIDSupport()) { // If we don't have sk_VertexID support then "vertexidx" already came in as a // vertex attrib. v->codeAppend(R"( diff --git a/src/gpu/tessellate/GrPathStencilCoverOp.cpp b/src/gpu/tessellate/GrPathStencilCoverOp.cpp index b68d96f0ce..7bc5deaf85 100644 --- a/src/gpu/tessellate/GrPathStencilCoverOp.cpp +++ b/src/gpu/tessellate/GrPathStencilCoverOp.cpp @@ -12,7 +12,6 @@ #include "src/gpu/GrOpFlushState.h" #include "src/gpu/GrRecordingContextPriv.h" #include "src/gpu/GrResourceProvider.h" -#include "src/gpu/glsl/GrGLSLProgramBuilder.h" #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h" #include "src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h" #include "src/gpu/tessellate/GrPathCurveTessellator.h" @@ -50,9 +49,9 @@ private: GrGLSLGeometryProcessor* BoundingBoxShader::createGLSLInstance(const GrShaderCaps&) const { class Impl : public GrPathTessellationShader::Impl { - void emitVertexCode(const GrPathTessellationShader&, GrGLSLVertexBuilder* v, - GrGPArgs* gpArgs) override { - if (v->getProgramBuilder()->caps()->shaderCaps()->vertexIDSupport()) { + void emitVertexCode(const GrShaderCaps& shaderCaps, const GrPathTessellationShader&, + GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override { + if (shaderCaps.vertexIDSupport()) { // If we don't have sk_VertexID support then "unitCoord" already came in as a vertex // attrib. v->codeAppendf(R"( @@ -185,9 +184,8 @@ void GrPathStencilCoverOp::onPrepare(GrOpFlushState* flushState) { GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fFanBuffer, &fFanBaseVertex); int maxFanTriangles = fPath.countVerbs() - 2; // n - 2 triangles make an n-gon. GrVertexWriter triangleVertexWriter = vertexAlloc.lock(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 f311b2aebf..4f22e5dab3 100644 --- a/src/gpu/tessellate/GrPathWedgeTessellator.cpp +++ b/src/gpu/tessellate/GrPathWedgeTessellator.cpp @@ -139,15 +139,16 @@ public: 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); + GrTessellationShader::WriteConicPatch(shaderCaps, p, w, &vertexWriter); vertexWriter.write(midpoint); } fNumFixedSegments_pow4 = std::max(numSegments_pow2 * numSegments_pow2, @@ -185,15 +186,15 @@ private: } } - 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); } @@ -269,6 +270,7 @@ void GrPathWedgeTessellator::prepare(GrMeshDrawTarget* target, const SkRect& cul maxSegments = GrPathTessellationShader::kMaxFixedCountSegments; } + const GrShaderCaps& shaderCaps = *target->caps().shaderCaps(); WedgeWriter wedgeWriter(cullBounds, fShader->viewMatrix(), maxSegments); MidpointContourParser parser(path); while (parser.parseNextContour()) { @@ -291,7 +293,7 @@ void GrPathWedgeTessellator::prepare(GrMeshDrawTarget* target, const SkRect& cul 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: diff --git a/src/gpu/tessellate/GrStrokeFixedCountTessellator.cpp b/src/gpu/tessellate/GrStrokeFixedCountTessellator.cpp index 15cad27084..af0563639d 100644 --- a/src/gpu/tessellate/GrStrokeFixedCountTessellator.cpp +++ b/src/gpu/tessellate/GrStrokeFixedCountTessellator.cpp @@ -77,15 +77,15 @@ public: fMaxParametricSegments_pow4); } - SK_ALWAYS_INLINE void conicTo(const SkPoint p[3], float w) { + SK_ALWAYS_INLINE void conicTo(const GrShaderCaps& shaderCaps, const SkPoint p[3], float w) { float n = GrWangsFormula::conic_pow2(fParametricPrecision, p, w); float numParametricSegments_pow4 = n*n; if (numParametricSegments_pow4 > kMaxParametricSegments_pow4) { - this->chopConicTo({p, w}); + this->chopConicTo(shaderCaps, {p, w}); return; } SkPoint conic[4]; - GrTessellationShader::WriteConicPatch(p, w, conic); + GrTessellationShader::WriteConicPatch(shaderCaps, p, w, conic); SkPoint endControlPoint = conic[1]; this->writeStroke(conic, endControlPoint); fMaxParametricSegments_pow4 = std::max(numParametricSegments_pow4, @@ -147,14 +147,14 @@ private: } } - void chopConicTo(const SkConic& conic) { + void chopConicTo(const GrShaderCaps& shaderCaps, const SkConic& conic) { SkConic chops[2]; if (!conic.chopAt(.5f, chops)) { return; } for (int i = 0; i < 2; ++i) { if (fCullTest.areVisible3(chops[i].fPts)) { - this->conicTo(chops[i].fPts, chops[i].fW); + this->conicTo(shaderCaps, chops[i].fPts, chops[i].fW); } else { this->discardStroke(chops[i].fPts, 3); } @@ -235,21 +235,22 @@ static int worst_case_edges_in_join(SkPaint::Join joinType, float numRadialSegme } // namespace -GrStrokeFixedCountTessellator::GrStrokeFixedCountTessellator(ShaderFlags shaderFlags, +GrStrokeFixedCountTessellator::GrStrokeFixedCountTessellator(const GrShaderCaps& shaderCaps, + ShaderFlags shaderFlags, const SkMatrix& viewMatrix, - PathStrokeList* pathStrokeList, - std::array matrixMinMaxScales, - const SkRect& strokeCullBounds, - const GrShaderCaps& shaderCaps) - : GrStrokeTessellator(GrStrokeTessellationShader::Mode::kFixedCount, shaderFlags, - kMaxParametricSegments_log2, viewMatrix, pathStrokeList, - matrixMinMaxScales, strokeCullBounds, shaderCaps) { + PathStrokeList* pathStrokeList,std::array matrixMinMaxScales, + const SkRect& strokeCullBounds) + : GrStrokeTessellator(shaderCaps, GrStrokeTessellationShader::Mode::kFixedCount, + shaderFlags, kMaxParametricSegments_log2, viewMatrix, + pathStrokeList, matrixMinMaxScales, strokeCullBounds) { } GR_DECLARE_STATIC_UNIQUE_KEY(gVertexIDFallbackBufferKey); void GrStrokeFixedCountTessellator::prepare(GrMeshDrawTarget* target, int totalCombinedVerbCnt) { + const GrShaderCaps& shaderCaps = *target->caps().shaderCaps(); int maxEdgesInJoin = 0; float maxRadialSegmentsPerRadian = 0; @@ -335,7 +336,7 @@ void GrStrokeFixedCountTessellator::prepare(GrMeshDrawTarget* target, instanceWriter.lineTo(p[0], cusp); instanceWriter.lineTo(cusp, p[2]); } else { - instanceWriter.conicTo(p, strokeIter.w()); + instanceWriter.conicTo(shaderCaps, p, strokeIter.w()); } break; case Verb::kCubic: diff --git a/src/gpu/tessellate/GrStrokeFixedCountTessellator.h b/src/gpu/tessellate/GrStrokeFixedCountTessellator.h index 112de91603..36a713d70b 100644 --- a/src/gpu/tessellate/GrStrokeFixedCountTessellator.h +++ b/src/gpu/tessellate/GrStrokeFixedCountTessellator.h @@ -15,9 +15,9 @@ // instance are emitted as degenerate triangles. class GrStrokeFixedCountTessellator : public GrStrokeTessellator { public: - GrStrokeFixedCountTessellator(ShaderFlags, const SkMatrix&, PathStrokeList*, - std::array matrixMinMaxScales, - const SkRect& strokeCullBounds, const GrShaderCaps&); + GrStrokeFixedCountTessellator(const GrShaderCaps&, ShaderFlags, const SkMatrix&, + PathStrokeList*, std::array matrixMinMaxScales, + const SkRect& strokeCullBounds); void prepare(GrMeshDrawTarget*, int totalCombinedVerbCnt) override; void draw(GrOpFlushState*) const override; diff --git a/src/gpu/tessellate/GrStrokeHardwareTessellator.cpp b/src/gpu/tessellate/GrStrokeHardwareTessellator.cpp index 65c15c51b0..c10f9bdc73 100644 --- a/src/gpu/tessellate/GrStrokeHardwareTessellator.cpp +++ b/src/gpu/tessellate/GrStrokeHardwareTessellator.cpp @@ -167,8 +167,8 @@ public: // Recursively chops the given conic and its previous join until the segments fit in // tessellation patches. - void writeConicPatchesTo(const SkPoint p[3], float w) { - this->internalConicPatchesTo(fStrokeJoinType, p, w); + void writeConicPatchesTo(const GrShaderCaps& shaderCaps, const SkPoint p[3], float w) { + this->internalConicPatchesTo(shaderCaps, fStrokeJoinType, p, w); } // Chops the given cubic at points of inflection and 180-degree rotation, and then recursively @@ -350,8 +350,8 @@ private: // Recursively chops the given conic and its previous join until the segments fit in // tessellation patches. - void internalConicPatchesTo(JoinType prevJoinType, const SkPoint p[3], float w, - int maxDepth = -1) { + void internalConicPatchesTo(const GrShaderCaps& shaderCaps, JoinType prevJoinType, + const SkPoint p[3], float w, int maxDepth = -1) { if (!fCullTest.areVisible3(p)) { // The stroke is out of view. Discard it. this->discardStroke(p, 3); @@ -370,7 +370,7 @@ private: if (w == 1) { GrPathUtils::convertQuadToCubic(p, asPatch); } else { - GrTessellationShader::WriteConicPatch(p, w, asPatch); + GrTessellationShader::WriteConicPatch(shaderCaps, p, w, asPatch); } float numParametricSegments_pow4; @@ -411,18 +411,19 @@ private: } else { SkChopQuadAtMidTangent(p, chops); } - this->internalConicPatchesTo(prevJoinType, chops, 1, maxDepth - 1); - this->internalConicPatchesTo(JoinType::kBowtie, chops + 2, 1, maxDepth - 1); + this->internalConicPatchesTo(shaderCaps, prevJoinType, chops, 1, maxDepth - 1); + this->internalConicPatchesTo(shaderCaps, JoinType::kBowtie, chops + 2, 1, + maxDepth - 1); } else { SkConic conic(p, w); float chopT = (numParametricSegments >= numRadialSegments) ? .5f : conic.findMidTangent(); SkConic chops[2]; if (conic.chopAt(chopT, chops)) { - this->internalConicPatchesTo(prevJoinType, chops[0].fPts, chops[0].fW, - maxDepth - 1); - this->internalConicPatchesTo(JoinType::kBowtie, chops[1].fPts, chops[1].fW, - maxDepth - 1); + this->internalConicPatchesTo(shaderCaps, prevJoinType, chops[0].fPts, + chops[0].fW, maxDepth - 1); + this->internalConicPatchesTo(shaderCaps, JoinType::kBowtie, chops[1].fPts, + chops[1].fW, maxDepth - 1); } } return; @@ -703,20 +704,22 @@ SK_ALWAYS_INLINE static bool cubic_has_cusp(const SkPoint p[4]) { } // namespace -GrStrokeHardwareTessellator::GrStrokeHardwareTessellator(ShaderFlags shaderFlags, - const GrShaderCaps& shaderCaps, +GrStrokeHardwareTessellator::GrStrokeHardwareTessellator(const GrShaderCaps& shaderCaps, + ShaderFlags shaderFlags, const SkMatrix& viewMatrix, PathStrokeList* pathStrokeList, std::array matrixMinMaxScales, const SkRect& strokeCullBounds) - : GrStrokeTessellator(GrStrokeTessellationShader::Mode::kHardwareTessellation, shaderFlags, - SkNextLog2(shaderCaps.maxTessellationSegments()), viewMatrix, - pathStrokeList, matrixMinMaxScales, strokeCullBounds, shaderCaps) { + : GrStrokeTessellator(shaderCaps, GrStrokeTessellationShader::Mode::kHardwareTessellation, + shaderFlags, SkNextLog2(shaderCaps.maxTessellationSegments()), + viewMatrix, pathStrokeList, matrixMinMaxScales, strokeCullBounds) { } void GrStrokeHardwareTessellator::prepare(GrMeshDrawTarget* target, int totalCombinedVerbCnt) { using JoinType = PatchWriter::JoinType; + const GrShaderCaps& shaderCaps = *target->caps().shaderCaps(); + // Over-allocate enough patches for 1 in 4 strokes to chop and for 8 extra caps. int strokePreallocCount = totalCombinedVerbCnt * 5/4; int capPreallocCount = 8; @@ -807,7 +810,7 @@ void GrStrokeHardwareTessellator::prepare(GrMeshDrawTarget* target, int totalCom if (!patchWriter.stroke180FitsInPatch(numParametricSegments_pow4)) { // The curve requires more tessellation segments than the hardware can // support. This is rare. Recursively chop until each sub-curve fits. - patchWriter.writeConicPatchesTo(p, 1); + patchWriter.writeConicPatchesTo(shaderCaps, p, 1); continue; } // The curve fits in a single tessellation patch. This is the most common case. @@ -846,14 +849,14 @@ void GrStrokeHardwareTessellator::prepare(GrMeshDrawTarget* target, int totalCom if (!patchWriter.stroke180FitsInPatch(numParametricSegments_pow4)) { // The curve requires more tessellation segments than the hardware can // support. This is rare. Recursively chop until each sub-curve fits. - patchWriter.writeConicPatchesTo(p, *w); + patchWriter.writeConicPatchesTo(shaderCaps, p, *w); continue; } // The curve fits in a single tessellation patch. This is the most common // case. Write it out directly. prevJoinFitsInPatch = patchWriter.stroke180FitsInPatch_withJoin( numParametricSegments_pow4); - GrTessellationShader::WriteConicPatch(p, *w, scratchPts); + GrTessellationShader::WriteConicPatch(shaderCaps, p, *w, scratchPts); patchPts = scratchPts; endControlPoint = p[1]; break; diff --git a/src/gpu/tessellate/GrStrokeHardwareTessellator.h b/src/gpu/tessellate/GrStrokeHardwareTessellator.h index cd413bbfa6..afd2e7dc49 100644 --- a/src/gpu/tessellate/GrStrokeHardwareTessellator.h +++ b/src/gpu/tessellate/GrStrokeHardwareTessellator.h @@ -16,7 +16,7 @@ // MSAA if antialiasing is desired. class GrStrokeHardwareTessellator : public GrStrokeTessellator { public: - GrStrokeHardwareTessellator(ShaderFlags shaderFlags, const GrShaderCaps& shaderCaps, + GrStrokeHardwareTessellator(const GrShaderCaps& shaderCaps, ShaderFlags shaderFlags, const SkMatrix& viewMatrix, PathStrokeList* pathStrokeList, std::array matrixMinMaxScales, const SkRect& strokeCullBounds); diff --git a/src/gpu/tessellate/GrStrokeTessellateOp.cpp b/src/gpu/tessellate/GrStrokeTessellateOp.cpp index e8c9342c95..df87b4c635 100644 --- a/src/gpu/tessellate/GrStrokeTessellateOp.cpp +++ b/src/gpu/tessellate/GrStrokeTessellateOp.cpp @@ -190,16 +190,15 @@ void GrStrokeTessellateOp::prePrepareTessellator(GrTessellationShader::ProgramAr if (can_use_hardware_tessellation(fTotalCombinedVerbCnt, *pipeline, caps)) { // Only use hardware tessellation if we're drawing a somewhat large number of verbs. // Otherwise we seem to be better off using instanced draws. - fTessellator = arena->make(fShaderFlags, *caps.shaderCaps(), + fTessellator = arena->make(*caps.shaderCaps(), fShaderFlags, fViewMatrix, &fPathStrokeList, matrixMinMaxScales, strokeCullBounds); } else { - fTessellator = arena->make(fShaderFlags, fViewMatrix, - &fPathStrokeList, + fTessellator = arena->make(*caps.shaderCaps(), fShaderFlags, + fViewMatrix, &fPathStrokeList, matrixMinMaxScales, - strokeCullBounds, - *caps.shaderCaps()); + strokeCullBounds); } auto fillStencil = &GrUserStencilSettings::kUnused; diff --git a/src/gpu/tessellate/GrStrokeTessellator.h b/src/gpu/tessellate/GrStrokeTessellator.h index 2bf7cfccf9..9a9f93d609 100644 --- a/src/gpu/tessellate/GrStrokeTessellator.h +++ b/src/gpu/tessellate/GrStrokeTessellator.h @@ -25,12 +25,12 @@ public: PathStrokeList* fNext = nullptr; }; - GrStrokeTessellator(GrStrokeTessellationShader::Mode shaderMode, ShaderFlags shaderFlags, - int8_t maxParametricSegments_log2, const SkMatrix& viewMatrix, - PathStrokeList* pathStrokeList, std::array matrixMinMaxScales, - const SkRect& strokeCullBounds, const GrShaderCaps& shaderCaps) - : fShader(shaderMode, shaderFlags, viewMatrix, pathStrokeList->fStroke, - pathStrokeList->fColor, maxParametricSegments_log2, shaderCaps) + GrStrokeTessellator(const GrShaderCaps& shaderCaps, GrStrokeTessellationShader::Mode shaderMode, + ShaderFlags shaderFlags, int8_t maxParametricSegments_log2, + const SkMatrix& viewMatrix, PathStrokeList* pathStrokeList, + std::array matrixMinMaxScales, const SkRect& strokeCullBounds) + : fShader(shaderCaps, shaderMode, shaderFlags, viewMatrix, pathStrokeList->fStroke, + pathStrokeList->fColor, maxParametricSegments_log2) , fPathStrokeList(pathStrokeList) , fMatrixMinMaxScales(matrixMinMaxScales) , fStrokeCullBounds(strokeCullBounds) { diff --git a/src/gpu/tessellate/GrTessellationPathRenderer.cpp b/src/gpu/tessellate/GrTessellationPathRenderer.cpp index fec78d5ec5..3d7ce865da 100644 --- a/src/gpu/tessellate/GrTessellationPathRenderer.cpp +++ b/src/gpu/tessellate/GrTessellationPathRenderer.cpp @@ -39,6 +39,7 @@ bool GrTessellationPathRenderer::IsSupported(const GrCaps& caps) { return !caps.avoidStencilBuffers() && caps.drawInstancedSupport() && caps.shaderCaps()->integerSupport() && + GrTessellationShader::SupportsPortableInfinity(*caps.shaderCaps()) && !caps.disableTessellationPathRenderer(); } diff --git a/src/gpu/tessellate/shaders/GrPathTessellationShader.cpp b/src/gpu/tessellate/shaders/GrPathTessellationShader.cpp index 173366f307..fbc230fc02 100644 --- a/src/gpu/tessellate/shaders/GrPathTessellationShader.cpp +++ b/src/gpu/tessellate/shaders/GrPathTessellationShader.cpp @@ -31,8 +31,8 @@ private: GrGLSLGeometryProcessor* SimpleTriangleShader::createGLSLInstance(const GrShaderCaps&) const { class Impl : public GrPathTessellationShader::Impl { - void emitVertexCode(const GrPathTessellationShader&, GrGLSLVertexBuilder* v, - GrGPArgs* gpArgs) override { + void emitVertexCode(const GrShaderCaps&, const GrPathTessellationShader&, + GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override { v->codeAppend(R"( float2 localcoord = inputPoint; float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)"); @@ -50,19 +50,6 @@ GrPathTessellationShader* GrPathTessellationShader::MakeSimpleTriangleShader( return arena->make(viewMatrix, color); } -// Converts a 4-point input patch into the rational cubic it intended to represent. -const char* GrPathTessellationShader::Impl::kUnpackRationalCubicFn = R"( -float4x3 unpack_rational_cubic(float2 p0, float2 p1, float2 p2, float2 p3) { - float4x3 P = float4x3(p0,1, p1,1, p2,1, p3,1); - if (isinf(P[3].y)) { - // This patch is actually a conic. Convert to a rational cubic. - float w = P[3].x; - float3 c = P[1] * ((2.0/3.0) * w); - P = float4x3(P[0], fma(P[0], float3(1.0/3.0), c), fma(P[2], float3(1.0/3.0), c), P[2]); - } - return P; -})"; - // Evaluate our point of interest using numerically stable linear interpolations. We add our own // "safe_mix" method to guarantee we get exactly "b" when T=1. The builtin mix() function seems // spec'd to behave this way, but empirical results results have shown it does not always. @@ -94,7 +81,7 @@ void GrPathTessellationShader::Impl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs kFloat2_GrSLType, "translate", &translate); args.fVertBuilder->codeAppendf("float2x2 AFFINE_MATRIX = float2x2(%s);", affineMatrix); args.fVertBuilder->codeAppendf("float2 TRANSLATE = %s;", translate); - this->emitVertexCode(shader, args.fVertBuilder, gpArgs); + this->emitVertexCode(*args.fShaderCaps, shader, args.fVertBuilder, gpArgs); // Fragment shader. const char* color; diff --git a/src/gpu/tessellate/shaders/GrPathTessellationShader.h b/src/gpu/tessellate/shaders/GrPathTessellationShader.h index bb1256c82d..7da91a8e63 100644 --- a/src/gpu/tessellate/shaders/GrPathTessellationShader.h +++ b/src/gpu/tessellate/shaders/GrPathTessellationShader.h @@ -187,11 +187,6 @@ protected: const GrGeometryProcessor&) override; protected: - // float2 eval_rational_cubic(float4x3 P, float T) { ... - // - // Converts a 4-point input patch into the rational cubic it intended to represent. - static const char* kUnpackRationalCubicFn; - // float4x3 unpack_rational_cubic(float2 p0, float2 p1, float2 p2, float2 p3) { ... // // Evaluate our point of interest using numerically stable linear interpolations. We add our @@ -200,8 +195,8 @@ protected: // does not always. static const char* kEvalRationalCubicFn; - virtual void emitVertexCode(const GrPathTessellationShader&, GrGLSLVertexBuilder*, - GrGPArgs*) = 0; + virtual void emitVertexCode(const GrShaderCaps&, const GrPathTessellationShader&, + GrGLSLVertexBuilder*, GrGPArgs*) = 0; GrGLSLUniformHandler::UniformHandle fAffineMatrixUniform; GrGLSLUniformHandler::UniformHandle fTranslateUniform; diff --git a/src/gpu/tessellate/shaders/GrPathTessellationShader_Hardware.cpp b/src/gpu/tessellate/shaders/GrPathTessellationShader_Hardware.cpp index 63a2192b51..e42d94bcbc 100644 --- a/src/gpu/tessellate/shaders/GrPathTessellationShader_Hardware.cpp +++ b/src/gpu/tessellate/shaders/GrPathTessellationShader_Hardware.cpp @@ -46,12 +46,14 @@ private: GrGLSLGeometryProcessor* HardwareWedgeShader::createGLSLInstance(const GrShaderCaps&) const { class Impl : public GrPathTessellationShader::Impl { - void emitVertexCode(const GrPathTessellationShader&, GrGLSLVertexBuilder* v, - GrGPArgs*) override { + void emitVertexCode(const GrShaderCaps& shaderCaps, const GrPathTessellationShader&, + GrGLSLVertexBuilder* v, GrGPArgs*) override { v->declareGlobal(GrShaderVar("vsPt", kFloat2_GrSLType, GrShaderVar::TypeModifier::Out)); + v->insertFunction(SkSLPortable_isinf(shaderCaps)); v->codeAppend(R"( // If y is infinity then x is a conic weight. Don't transform. - vsPt = (isinf(inputPoint.y)) ? inputPoint : AFFINE_MATRIX * inputPoint + TRANSLATE;)"); + vsPt = (isinf_portable(inputPoint.y)) ? inputPoint + : AFFINE_MATRIX * inputPoint + TRANSLATE;)"); } SkString getTessControlShaderGLSL(const GrGeometryProcessor&, const char* versionAndExtensionDecls, @@ -64,7 +66,7 @@ GrGLSLGeometryProcessor* HardwareWedgeShader::createGLSLInstance(const GrShaderC #define PRECISION %f)", GrTessellationShader::kLinearizationPrecision); code.append(kSkSLTypeDefs); code.append(GrWangsFormula::as_sksl()); - code.append(kUnpackRationalCubicFn); + code.append(SkSLPortable_isinf(shaderCaps)); code.append(R"( layout(vertices = 1) out; @@ -76,7 +78,7 @@ GrGLSLGeometryProcessor* HardwareWedgeShader::createGLSLInstance(const GrShaderC void main() { mat4x2 P = mat4x2(vsPt[0], vsPt[1], vsPt[2], vsPt[3]); float numSegments; - if (isinf(P[3].y)) { + if (isinf_portable(P[3].y)) { // This is a conic. float w = P[3].x; numSegments = wangs_formula_conic(PRECISION, mat3x2(P), w); @@ -174,12 +176,14 @@ private: GrGLSLGeometryProcessor* HardwareCurveShader::createGLSLInstance(const GrShaderCaps&) const { class Impl : public GrPathTessellationShader::Impl { - void emitVertexCode(const GrPathTessellationShader&, GrGLSLVertexBuilder* v, - GrGPArgs*) override { + void emitVertexCode(const GrShaderCaps& shaderCaps, const GrPathTessellationShader&, + GrGLSLVertexBuilder* v, GrGPArgs*) override { v->declareGlobal(GrShaderVar("P", kFloat2_GrSLType, GrShaderVar::TypeModifier::Out)); + v->insertFunction(SkSLPortable_isinf(shaderCaps)); v->codeAppend(R"( // If y is infinity then x is a conic weight. Don't transform. - P = (isinf(inputPoint.y)) ? inputPoint : AFFINE_MATRIX * inputPoint + TRANSLATE;)"); + P = (isinf_portable(inputPoint.y)) ? inputPoint + : AFFINE_MATRIX * inputPoint + TRANSLATE;)"); } SkString getTessControlShaderGLSL(const GrGeometryProcessor&, const char* versionAndExtensionDecls, @@ -192,7 +196,7 @@ GrGLSLGeometryProcessor* HardwareCurveShader::createGLSLInstance(const GrShaderC #define PRECISION %f)", GrTessellationShader::kLinearizationPrecision); code.append(kSkSLTypeDefs); code.append(GrWangsFormula::as_sksl()); - code.append(kUnpackRationalCubicFn); + code.append(SkSLPortable_isinf(shaderCaps)); code.append(R"( layout(vertices = 1) out; @@ -203,7 +207,7 @@ GrGLSLGeometryProcessor* HardwareCurveShader::createGLSLInstance(const GrShaderC void main() { float w = -1; // w<0 means a cubic. vec2 p1w = P[1]; - if (isinf(P[3].y)) { + if (isinf_portable(P[3].y)) { // This patch is actually a conic. Project to homogeneous space. w = P[3].x; p1w *= w; @@ -218,7 +222,7 @@ GrGLSLGeometryProcessor* HardwareCurveShader::createGLSLInstance(const GrShaderC vec2 abcd = (abc + bcd) * .5; float n0, n1; - if (w < 0 || isinf(w)) { + if (w < 0 || isinf_portable(w)) { if (w < 0) { // The patch is a cubic. Calculate how many segments are required to // linearize each half of the curve. diff --git a/src/gpu/tessellate/shaders/GrPathTessellationShader_MiddleOut.cpp b/src/gpu/tessellate/shaders/GrPathTessellationShader_MiddleOut.cpp index d2ff0135bd..761c07b79b 100644 --- a/src/gpu/tessellate/shaders/GrPathTessellationShader_MiddleOut.cpp +++ b/src/gpu/tessellate/shaders/GrPathTessellationShader_MiddleOut.cpp @@ -9,7 +9,6 @@ #include "src/core/SkMathPriv.h" #include "src/gpu/geometry/GrWangsFormula.h" -#include "src/gpu/glsl/GrGLSLProgramBuilder.h" #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h" namespace { @@ -61,13 +60,14 @@ private: GrGLSLGeometryProcessor* MiddleOutShader::createGLSLInstance(const GrShaderCaps&) const { class Impl : public GrPathTessellationShader::Impl { - void emitVertexCode(const GrPathTessellationShader& shader, GrGLSLVertexBuilder* v, - GrGPArgs* gpArgs) override { + void emitVertexCode(const GrShaderCaps& shaderCaps, const GrPathTessellationShader& shader, + GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override { v->defineConstant("PRECISION", GrTessellationShader::kLinearizationPrecision); v->defineConstant("MAX_FIXED_RESOLVE_LEVEL", (float)kMaxFixedCountResolveLevel); v->defineConstant("MAX_FIXED_SEGMENTS", (float)kMaxFixedCountSegments); v->insertFunction(GrWangsFormula::as_sksl().c_str()); - if (v->getProgramBuilder()->shaderCaps()->bitManipulationSupport()) { + v->insertFunction(SkSLPortable_isinf(shaderCaps)); + if (shaderCaps.bitManipulationSupport()) { v->insertFunction(R"( float ldexp_portable(float x, float p) { return ldexp(x, int(p)); @@ -90,7 +90,7 @@ GrGLSLGeometryProcessor* MiddleOutShader::createGLSLInstance(const GrShaderCaps& } else)"); // Fall through to next if (). } v->codeAppend(R"( - if (isinf(inputPoints_2_3.z)) { + if (isinf_portable(inputPoints_2_3.z)) { // A conic with w=Inf is an exact triangle. localcoord = (resolveLevel != 0) ? inputPoints_0_1.zw : (idxInResolveLevel != 0) ? inputPoints_2_3.xy @@ -99,7 +99,7 @@ GrGLSLGeometryProcessor* MiddleOutShader::createGLSLInstance(const GrShaderCaps& float w = -1; // w < 0 tells us to treat the instance as an integral cubic. float4x2 P = float4x2(inputPoints_0_1, inputPoints_2_3); float maxResolveLevel; - if (isinf(P[3].y)) { + if (isinf_portable(P[3].y)) { // The patch is a conic. w = P[3].x; maxResolveLevel = diff --git a/src/gpu/tessellate/shaders/GrStrokeTessellationShader.cpp b/src/gpu/tessellate/shaders/GrStrokeTessellationShader.cpp index c415a514e7..9c47245d4a 100644 --- a/src/gpu/tessellate/shaders/GrStrokeTessellationShader.cpp +++ b/src/gpu/tessellate/shaders/GrStrokeTessellationShader.cpp @@ -13,11 +13,11 @@ #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h" #include "src/gpu/tessellate/GrStrokeTessellator.h" -GrStrokeTessellationShader::GrStrokeTessellationShader(Mode mode, ShaderFlags shaderFlags, +GrStrokeTessellationShader::GrStrokeTessellationShader(const GrShaderCaps& shaderCaps, Mode mode, + ShaderFlags shaderFlags, const SkMatrix& viewMatrix, const SkStrokeRec& stroke, SkPMColor4f color, - int8_t maxParametricSegments_log2, - const GrShaderCaps& shaderCaps) + int8_t maxParametricSegments_log2) : GrTessellationShader(kTessellate_GrStrokeTessellationShader_ClassID, (mode == Mode::kHardwareTessellation) ? GrPrimitiveType::kPatches diff --git a/src/gpu/tessellate/shaders/GrStrokeTessellationShader.h b/src/gpu/tessellate/shaders/GrStrokeTessellationShader.h index 0f4af3c5a2..384c8e4af5 100644 --- a/src/gpu/tessellate/shaders/GrStrokeTessellationShader.h +++ b/src/gpu/tessellate/shaders/GrStrokeTessellationShader.h @@ -98,8 +98,8 @@ public: }; // 'viewMatrix' is applied to the geometry post tessellation. It cannot have perspective. - GrStrokeTessellationShader(Mode, ShaderFlags, const SkMatrix& viewMatrix, const SkStrokeRec&, - SkPMColor4f, int8_t maxParametricSegments_log2, const GrShaderCaps&); + GrStrokeTessellationShader(const GrShaderCaps&, Mode, ShaderFlags, const SkMatrix& viewMatrix, + const SkStrokeRec&, SkPMColor4f, int8_t maxParametricSegments_log2); Mode mode() const { return fMode; } ShaderFlags flags() const { return fShaderFlags; } diff --git a/src/gpu/tessellate/shaders/GrStrokeTessellationShader_HardwareImpl.cpp b/src/gpu/tessellate/shaders/GrStrokeTessellationShader_HardwareImpl.cpp index 764d20216d..0b1658dde4 100644 --- a/src/gpu/tessellate/shaders/GrStrokeTessellationShader_HardwareImpl.cpp +++ b/src/gpu/tessellate/shaders/GrStrokeTessellationShader_HardwareImpl.cpp @@ -57,6 +57,7 @@ void GrStrokeTessellationShader::HardwareImpl::onEmitCode(EmitArgs& args, GrGPAr v->insertFunction(kCosineBetweenVectorsFn); v->insertFunction(kMiterExtentFn); v->insertFunction(kUncheckedMixFn); + v->insertFunction(SkSLPortable_isinf(*args.fShaderCaps)); if (shader.hasDynamicStroke()) { v->insertFunction(kNumRadialSegmentsPerRadianFn); } @@ -113,7 +114,7 @@ void GrStrokeTessellationShader::HardwareImpl::onEmitCode(EmitArgs& args, GrGPAr // translate until the end; we just need to perform the scale and skew right now. v->codeAppend(R"( P = AFFINE_MATRIX * P; - if (isinf(pts23Attr.w)) { + if (isinf_portable(pts23Attr.w)) { // If y3 is infinity then x3 is a conic weight. Don't transform. P[3] = pts23Attr.zw; } @@ -125,7 +126,7 @@ void GrStrokeTessellationShader::HardwareImpl::onEmitCode(EmitArgs& args, GrGPAr // (pre-chopping) input points or else the seams might crack. float2 prevJoinTangent = P[0] - prevControlPoint; float2 tan0 = ((P[1] == P[0]) ? P[2] : P[1]) - P[0]; - float2 tan1 = (P[3] == P[2] || isinf(P[3].y)) ? P[2] - P[1] : P[3] - P[2]; + float2 tan1 = (P[3] == P[2] || isinf_portable(P[3].y)) ? P[2] - P[1] : P[3] - P[2]; if (tan0 == float2(0)) { // [p0, p0, p0, p3] is a reserved pattern that means this patch is a "bowtie". @@ -266,7 +267,7 @@ void GrStrokeTessellationShader::HardwareImpl::onEmitCode(EmitArgs& args, GrGPAr } // If the curve is a straight line, point, or conic, don't chop it into sections after all. - if ((P[0] == P[1] && P[2] == P[3]) || isinf(P[3].y)) { + if ((P[0] == P[1] && P[2] == P[3]) || isinf_portable(P[3].y)) { chopT = float2(0); innerTangents = float2x2(tan0, tan0); } @@ -274,7 +275,8 @@ void GrStrokeTessellationShader::HardwareImpl::onEmitCode(EmitArgs& args, GrGPAr // Chop the curve at chopT[0] and chopT[1]. float4 ab = unchecked_mix(P[0].xyxy, P[1].xyxy, chopT.sstt); float4 bc = unchecked_mix(P[1].xyxy, P[2].xyxy, chopT.sstt); - float4 cd = isinf(P[3].y) ? P[2].xyxy : unchecked_mix(P[2].xyxy, P[3].xyxy, chopT.sstt); + float4 cd = isinf_portable(P[3].y) ? P[2].xyxy + : unchecked_mix(P[2].xyxy, P[3].xyxy, chopT.sstt); float4 abc = unchecked_mix(ab, bc, chopT.sstt); float4 bcd = unchecked_mix(bc, cd, chopT.sstt); float4 abcd = unchecked_mix(abc, bcd, chopT.sstt); @@ -353,6 +355,7 @@ SkString GrStrokeTessellationShader::HardwareImpl::getTessControlShaderGLSL( code.append(kAtan2Fn); code.append(kCosineBetweenVectorsFn); code.append(kMiterExtentFn); + code.append(SkSLPortable_isinf(shaderCaps)); code.append(R"( float cross2d(vec2 a, vec2 b) { return determinant(mat2(a,b)); @@ -428,7 +431,7 @@ SkString GrStrokeTessellationShader::HardwareImpl::getTessControlShaderGLSL( // Calculate the number of parametric segments. The final tessellated strip will be a // composition of these parametric segments as well as radial segments. - float w = isinf(P[3].y) ? P[3].x : -1.0; // w<0 means the curve is an integral cubic. + float w = isinf_portable(P[3].y) ? P[3].x : -1.0; // w<0 means integral cubic. float numParametricSegments; if (w < 0.0) { numParametricSegments = wangs_formula_cubic(PARAMETRIC_PRECISION, P, mat2(1)); @@ -452,8 +455,8 @@ SkString GrStrokeTessellationShader::HardwareImpl::getTessControlShaderGLSL( // Adjust sign of rotation to match the direction the curve turns. // NOTE: Since the curve is not allowed to inflect, we can just check F'(.5) x F''(.5). // NOTE: F'(.5) x F''(.5) has the same sign as (P2 - P0) x (P3 - P1) - float turn = isinf(P[3].y) ? cross2d(P[1] - P[0], P[2] - P[1]) - : cross2d(P[2] - P[0], P[3] - P[1]); + float turn = isinf_portable(P[3].y) ? cross2d(P[1] - P[0], P[2] - P[1]) + : cross2d(P[2] - P[0], P[3] - P[1]); if (turn == 0.0) { // This is the case for joins and cusps where points are co-located. turn = determinant(tangents); } @@ -561,6 +564,8 @@ SkString GrStrokeTessellationShader::HardwareImpl::getTessEvaluationShaderGLSL( code.appendf("uniform vec4 %s;\n", affineMatrixName); code.appendf("#define AFFINE_MATRIX mat2(%s)\n", affineMatrixName); + code.append(SkSLPortable_isinf(shaderCaps)); + code.append(R"( in vec4 tcsPts01[]; in vec4 tcsPt2Tan0[]; @@ -632,7 +637,7 @@ SkString GrStrokeTessellationShader::HardwareImpl::getTessEvaluationShaderGLSL( float2 tan1 = tcsEndPtEndTan.zw; bool isFinalEdge = (gl_TessCoord.x == 1); float w = -1.0; // w<0 means the curve is an integral cubic. - if (isinf(P[3].y)) { + if (isinf_portable(P[3].y)) { w = P[3].x; // The curve is actually a conic. P[3] = P[2]; // Setting p3 equal to p2 works for the remaining rotational logic. })"); diff --git a/src/gpu/tessellate/shaders/GrStrokeTessellationShader_InstancedImpl.cpp b/src/gpu/tessellate/shaders/GrStrokeTessellationShader_InstancedImpl.cpp index 53776e3cd7..42fa451871 100644 --- a/src/gpu/tessellate/shaders/GrStrokeTessellationShader_InstancedImpl.cpp +++ b/src/gpu/tessellate/shaders/GrStrokeTessellationShader_InstancedImpl.cpp @@ -28,6 +28,7 @@ void GrStrokeTessellationShader::InstancedImpl::onEmitCode(EmitArgs& args, GrGPA args.fVertBuilder->insertFunction(kMiterExtentFn); args.fVertBuilder->insertFunction(kUncheckedMixFn); args.fVertBuilder->insertFunction(GrWangsFormula::as_sksl().c_str()); + args.fVertBuilder->insertFunction(SkSLPortable_isinf(*args.fShaderCaps)); // Tessellation control uniforms and/or dynamic attributes. if (!shader.hasDynamicStroke()) { @@ -90,7 +91,7 @@ void GrStrokeTessellationShader::InstancedImpl::onEmitCode(EmitArgs& args, GrGPA float4x2 P = float4x2(pts01Attr, pts23Attr); float2 lastControlPoint = argsAttr.xy; float w = -1; // w<0 means the curve is an integral cubic. - if (isinf(P[3].y)) { + if (isinf_portable(P[3].y)) { w = P[3].x; // The curve is actually a conic. P[3] = P[2]; // Setting p3 equal to p2 works for the remaining rotational logic. })"); diff --git a/src/gpu/tessellate/shaders/GrTessellationShader.h b/src/gpu/tessellate/shaders/GrTessellationShader.h index b565dcd4ed..cce727d1f0 100644 --- a/src/gpu/tessellate/shaders/GrTessellationShader.h +++ b/src/gpu/tessellate/shaders/GrTessellationShader.h @@ -39,16 +39,52 @@ public: const SkMatrix& viewMatrix() const { return fViewMatrix; } const SkPMColor4f& color() const { return fColor;} + static bool SupportsPortableInfinity(const GrShaderCaps& shaderCaps) { + // If we don't have native infinity then we need full 32-bit floats in order to emulate it. + return shaderCaps.infinitySupport() || shaderCaps.floatIs32Bits(); + } + + static uint32_t PortableInfinityBits32(const GrShaderCaps& shaderCaps) { + SkASSERT(SupportsPortableInfinity(shaderCaps)); + // Tessellation shaders use infinity to flag conics, and also as a weight that turns conics + // into triangles. + constexpr static uint32_t kIEEE_32_infinity = 0xff << 23; + // On hardware that doesn't support infinity, we pretend that inputs >= 2^126 are infinity. + // The rationale for doing this is that these number are so large, they will overflow if we + // try to do any bezier math with them anyway. + constexpr static uint32_t kIEEE_32_pseudo_infinity = + (0xfd << 23) | (1 << 22); // 1.5 * 2^126. + return shaderCaps.infinitySupport() ? kIEEE_32_infinity : kIEEE_32_pseudo_infinity; + } + + static const char* SkSLPortable_isinf(const GrShaderCaps& shaderCaps) { + SkASSERT(SupportsPortableInfinity(shaderCaps)); + if (shaderCaps.infinitySupport()) { + return R"( + bool isinf_portable(float x) { + return isinf(x); + })"; + } else { + SkASSERT(shaderCaps.floatIs32Bits()); + return R"( + bool isinf_portable(float x) { + return abs(x) >= exp2(126); + })"; + } + } + // 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) { + static void WriteConicPatch(const GrShaderCaps& shaderCaps, 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 // patch[3].y as NaN to flag this patch as a conic. writer->writeArray(pts, 3); - writer->write(w, GrVertexWriter::kIEEE_32_infinity); + writer->write(w, PortableInfinityBits32(shaderCaps)); } - static void WriteConicPatch(const SkPoint pts[3], float w, SkPoint patch[4]) { + static void WriteConicPatch(const GrShaderCaps& shaderCaps, const SkPoint pts[3], float w, + SkPoint patch[4]) { GrVertexWriter writer(patch); - WriteConicPatch(pts, w, &writer); + WriteConicPatch(shaderCaps, pts, w, &writer); } struct ProgramArgs { diff --git a/src/gpu/vk/GrVkCaps.cpp b/src/gpu/vk/GrVkCaps.cpp index 3c18723ae4..e09162430a 100644 --- a/src/gpu/vk/GrVkCaps.cpp +++ b/src/gpu/vk/GrVkCaps.cpp @@ -716,6 +716,7 @@ void GrVkCaps::initShaderCaps(const VkPhysicalDeviceProperties& properties, shaderCaps->fIntegerSupport = true; shaderCaps->fNonsquareMatrixSupport = true; shaderCaps->fVertexIDSupport = true; + shaderCaps->fInfinitySupport = true; shaderCaps->fBitManipulationSupport = true; // Assume the minimum precisions mandated by the SPIR-V spec.