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 <csmartdalton@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
This commit is contained in:
Chris Dalton 2021-07-01 11:17:53 -06:00 committed by Skia Commit-Bot
parent ebf89a0eed
commit 6904303a66
29 changed files with 210 additions and 158 deletions

View File

@ -202,8 +202,7 @@ DEF_PATH_TESS_BENCH(middle_out_triangulation,
int baseVertex; int baseVertex;
GrVertexWriter vertexWriter = static_cast<SkPoint*>(fTarget->makeVertexSpace( GrVertexWriter vertexWriter = static_cast<SkPoint*>(fTarget->makeVertexSpace(
sizeof(SkPoint), kNumCubicsInChalkboard, &buffer, &baseVertex)); sizeof(SkPoint), kNumCubicsInChalkboard, &buffer, &baseVertex));
GrMiddleOutPolygonTriangulator::WritePathInnerFan( GrMiddleOutPolygonTriangulator::WritePathInnerFan(&vertexWriter, 0, 0, fPath);
&vertexWriter, GrMiddleOutPolygonTriangulator::OutputType::kTriangles, fPath);
} }
using PathStrokeList = GrStrokeTessellator::PathStrokeList; using PathStrokeList = GrStrokeTessellator::PathStrokeList;
@ -216,7 +215,7 @@ static std::unique_ptr<GrStrokeTessellator> make_hw_tessellator(
ShaderFlags shaderFlags, const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, ShaderFlags shaderFlags, const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix,
PathStrokeList* pathStrokeList, std::array<float, 2> matrixMinMaxScales, PathStrokeList* pathStrokeList, std::array<float, 2> matrixMinMaxScales,
const SkRect& strokeCullBounds) { const SkRect& strokeCullBounds) {
return std::make_unique<GrStrokeHardwareTessellator>(shaderFlags, shaderCaps, viewMatrix, return std::make_unique<GrStrokeHardwareTessellator>(shaderCaps, shaderFlags, viewMatrix,
pathStrokeList, matrixMinMaxScales, pathStrokeList, matrixMinMaxScales,
strokeCullBounds); strokeCullBounds);
} }
@ -225,9 +224,9 @@ static std::unique_ptr<GrStrokeTessellator> make_fixed_count_tessellator(
ShaderFlags shaderFlags, const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix, ShaderFlags shaderFlags, const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix,
PathStrokeList* pathStrokeList, std::array<float, 2> matrixMinMaxScales, PathStrokeList* pathStrokeList, std::array<float, 2> matrixMinMaxScales,
const SkRect& strokeCullBounds) { const SkRect& strokeCullBounds) {
return std::make_unique<GrStrokeFixedCountTessellator>(shaderFlags, viewMatrix, pathStrokeList, return std::make_unique<GrStrokeFixedCountTessellator>(shaderCaps, shaderFlags, viewMatrix,
matrixMinMaxScales, strokeCullBounds, pathStrokeList, matrixMinMaxScales,
shaderCaps); strokeCullBounds);
} }
using MakePathStrokesFn = std::vector<PathStrokeList>(*)(); using MakePathStrokesFn = std::vector<PathStrokeList>(*)();

View File

@ -53,6 +53,7 @@ GrShaderCaps::GrShaderCaps(const GrContextOptions& options) {
fSampleMaskSupport = false; fSampleMaskSupport = false;
fExternalTextureSupport = false; fExternalTextureSupport = false;
fVertexIDSupport = false; fVertexIDSupport = false;
fInfinitySupport = false;
fBitManipulationSupport = false; fBitManipulationSupport = false;
fFloatIs32Bits = true; fFloatIs32Bits = true;
fHalfIs32Bits = false; fHalfIs32Bits = false;
@ -140,6 +141,7 @@ void GrShaderCaps::dumpJSON(SkJSONWriter* writer) const {
writer->appendBool("Sample mask support", fSampleMaskSupport); writer->appendBool("Sample mask support", fSampleMaskSupport);
writer->appendBool("External texture support", fExternalTextureSupport); writer->appendBool("External texture support", fExternalTextureSupport);
writer->appendBool("sk_VertexID support", fVertexIDSupport); writer->appendBool("sk_VertexID support", fVertexIDSupport);
writer->appendBool("Infinity support", fInfinitySupport);
writer->appendBool("Bit manipulation support", fBitManipulationSupport); writer->appendBool("Bit manipulation support", fBitManipulationSupport);
writer->appendBool("float == fp32", fFloatIs32Bits); writer->appendBool("float == fp32", fFloatIs32Bits);
writer->appendBool("half == fp32", fHalfIs32Bits); writer->appendBool("half == fp32", fHalfIs32Bits);

View File

@ -76,7 +76,10 @@ public:
bool vertexIDSupport() const { return fVertexIDSupport; } 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 bitManipulationSupport() const { return fBitManipulationSupport; }
bool floatIs32Bits() const { return fFloatIs32Bits; } bool floatIs32Bits() const { return fFloatIs32Bits; }
@ -297,6 +300,7 @@ private:
bool fSampleMaskSupport : 1; bool fSampleMaskSupport : 1;
bool fExternalTextureSupport : 1; bool fExternalTextureSupport : 1;
bool fVertexIDSupport : 1; bool fVertexIDSupport : 1;
bool fInfinitySupport : 1;
bool fBitManipulationSupport : 1; bool fBitManipulationSupport : 1;
bool fFloatIs32Bits : 1; bool fFloatIs32Bits : 1;
bool fHalfIs32Bits : 1; bool fHalfIs32Bits : 1;

View File

@ -23,8 +23,6 @@
* thereof. * thereof.
*/ */
struct GrVertexWriter { struct GrVertexWriter {
constexpr static uint32_t kIEEE_32_infinity = 0x7f800000;
void* fPtr; void* fPtr;
GrVertexWriter() = default; GrVertexWriter() = default;

View File

@ -243,6 +243,7 @@ void GrD3DCaps::initShaderCaps(int vendorID, const D3D12_FEATURE_DATA_D3D12_OPTI
shaderCaps->fIntegerSupport = true; shaderCaps->fIntegerSupport = true;
shaderCaps->fVertexIDSupport = true; shaderCaps->fVertexIDSupport = true;
shaderCaps->fInfinitySupport = true;
shaderCaps->fBitManipulationSupport = true; shaderCaps->fBitManipulationSupport = true;
shaderCaps->fFloatIs32Bits = true; shaderCaps->fFloatIs32Bits = true;

View File

@ -971,6 +971,13 @@ void GrGLCaps::initGLSL(const GrGLContextInfo& ctxInfo, const GrGLInterface* gli
shaderCaps->fVertexIDSupport = ctxInfo.glslGeneration() >= k330_GrGLSLGeneration; 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)) { if (GR_IS_GR_GL(standard)) {
shaderCaps->fBitManipulationSupport = ctxInfo.glslGeneration() >= k400_GrGLSLGeneration; shaderCaps->fBitManipulationSupport = ctxInfo.glslGeneration() >= k400_GrGLSLGeneration;
} else if (GR_IS_GR_GL_ES(standard) || GR_IS_GR_WEBGL(standard)) { } else if (GR_IS_GR_GL_ES(standard) || GR_IS_GR_WEBGL(standard)) {

View File

@ -476,6 +476,7 @@ void GrMtlCaps::initShaderCaps() {
shaderCaps->fIntegerSupport = true; shaderCaps->fIntegerSupport = true;
shaderCaps->fNonsquareMatrixSupport = true; shaderCaps->fNonsquareMatrixSupport = true;
shaderCaps->fVertexIDSupport = false; shaderCaps->fVertexIDSupport = false;
shaderCaps->fInfinitySupport = true;
// Metal uses IEEE float and half floats so assuming those values here. // Metal uses IEEE float and half floats so assuming those values here.
shaderCaps->fFloatIs32Bits = true; shaderCaps->fFloatIs32Bits = true;

View File

@ -43,15 +43,14 @@
// recursion, we manipulate an O(log N) stack to determine the correct middle-out triangulation. // recursion, we manipulate an O(log N) stack to determine the correct middle-out triangulation.
class GrMiddleOutPolygonTriangulator { class GrMiddleOutPolygonTriangulator {
public: public:
enum class OutputType : bool { // Writes out 3 SkPoints per triangle to "vertexWriter". Additionally writes out "pad32Count"
kTriangles, // Output 3-vertex triangles. // repetitions of "pad32Value" after each triangle. Set pad32Count to 0 if the triangles are
kConicsWithInfiniteWeight // Output 4-vertex conics with w=Inf. // to be tightly packed.
}; GrMiddleOutPolygonTriangulator(GrVertexWriter* vertexWriter, int pad32Count,
uint32_t pad32Value, int maxPushVertexCalls)
GrMiddleOutPolygonTriangulator(GrVertexWriter* vertexWriter, OutputType outputType, : fPad32Count(pad32Count)
int maxPushVertexCalls) , fPad32Value(pad32Value)
: fVertexWriter(vertexWriter) , fVertexWriter(vertexWriter) {
, fOutputType(outputType) {
// Determine the deepest our stack can ever go. // Determine the deepest our stack can ever go.
int maxStackDepth = SkNextLog2(maxPushVertexCalls) + 1; int maxStackDepth = SkNextLog2(maxPushVertexCalls) + 1;
if (maxStackDepth > kStackPreallocCount) { if (maxStackDepth > kStackPreallocCount) {
@ -123,9 +122,10 @@ public:
SkASSERT(fTop->fVertexIdxDelta == 0); // Ensure we are in the initial stack state. 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) { const SkPath& path) {
GrMiddleOutPolygonTriangulator middleOut(vertexWriter, outputType, path.countVerbs()); GrMiddleOutPolygonTriangulator middleOut(vertexWriter, pad32Count, pad32Value,
path.countVerbs());
for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) { for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
switch (verb) { switch (verb) {
case SkPathVerb::kMove: case SkPathVerb::kMove:
@ -169,19 +169,20 @@ private:
SkASSERT(fTop > fVertexStack); // We should never pop the starting point. SkASSERT(fTop > fVertexStack); // We should never pop the starting point.
--fTop; --fTop;
fVertexWriter->write(fTop[0].fPoint, fTop[1].fPoint, lastPt); fVertexWriter->write(fTop[0].fPoint, fTop[1].fPoint, lastPt);
if (fOutputType == OutputType::kConicsWithInfiniteWeight) { if (fPad32Count) {
// Output a 4-point conic with w=Inf. // Output a 4-point conic with w=Inf.
fVertexWriter->fill(GrVertexWriter::kIEEE_32_infinity, 2); fVertexWriter->fill(fPad32Value, fPad32Count);
} }
} }
constexpr static int kStackPreallocCount = 32; constexpr static int kStackPreallocCount = 32;
const int fPad32Count;
const uint32_t fPad32Value;
SkAutoSTMalloc<kStackPreallocCount, StackVertex> fVertexStack; SkAutoSTMalloc<kStackPreallocCount, StackVertex> fVertexStack;
SkDEBUGCODE(int fStackAllocCount;) SkDEBUGCODE(int fStackAllocCount;)
StackVertex* fTop; StackVertex* fTop;
GrVertexWriter* fVertexWriter; GrVertexWriter* fVertexWriter;
int fTotalClosedTriangleCount = 0; int fTotalClosedTriangleCount = 0;
OutputType fOutputType;
}; };
#endif #endif

View File

@ -22,8 +22,10 @@ constexpr static float kPrecision = GrTessellationShader::kLinearizationPrecisio
// supported by the hardware. // supported by the hardware.
class CurveWriter { class CurveWriter {
public: public:
CurveWriter(const SkRect& cullBounds, const SkMatrix& viewMatrix, int maxSegments) CurveWriter(const GrShaderCaps& shaderCaps, const SkRect& cullBounds,
: fCullTest(cullBounds, viewMatrix) const SkMatrix& viewMatrix, int maxSegments)
: fShaderCaps(shaderCaps)
, fCullTest(cullBounds, viewMatrix)
, fVectorXform(viewMatrix) , fVectorXform(viewMatrix)
, fMaxSegments_pow2(maxSegments * maxSegments) , fMaxSegments_pow2(maxSegments * maxSegments)
, fMaxSegments_pow4(fMaxSegments_pow2 * fMaxSegments_pow2) { , fMaxSegments_pow4(fMaxSegments_pow2 * fMaxSegments_pow2) {
@ -51,7 +53,7 @@ public:
} }
if (numSegments_pow2 > 1) { if (numSegments_pow2 > 1) {
if (GrVertexWriter vertexWriter = chunker->appendVertex()) { 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 = std::max(numSegments_pow2 * numSegments_pow2,
fNumFixedSegments_pow4); fNumFixedSegments_pow4);
@ -119,10 +121,11 @@ private:
if (GrVertexWriter vertexWriter = chunker->appendVertex()) { if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
vertexWriter.write(p0, p1, p2); vertexWriter.write(p0, p1, p2);
// Mark this instance as a triangle by setting it to a conic with w=Inf. // 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; GrCullTest fCullTest;
GrVectorXform fVectorXform; GrVectorXform fVectorXform;
const float fMaxSegments_pow2; const float fMaxSegments_pow2;
@ -166,6 +169,8 @@ void GrPathCurveTessellator::prepare(GrMeshDrawTarget* target, const SkRect& cul
const BreadcrumbTriangleList* breadcrumbTriangleList) { const BreadcrumbTriangleList* breadcrumbTriangleList) {
SkASSERT(fVertexChunkArray.empty()); SkASSERT(fVertexChunkArray.empty());
const GrShaderCaps& shaderCaps = *target->caps().shaderCaps();
// Determine how many triangles to allocate. // Determine how many triangles to allocate.
int maxTriangles = 0; int maxTriangles = 0;
if (fDrawInnerFan) { if (fDrawInnerFan) {
@ -193,9 +198,10 @@ void GrPathCurveTessellator::prepare(GrMeshDrawTarget* target, const SkRect& cul
} }
int numRemainingTriangles = maxTriangles; int numRemainingTriangles = maxTriangles;
if (fDrawInnerFan) { if (fDrawInnerFan) {
// Pad the triangles with 2 infinities. This produces conic patches with w=Inf.
int numWritten = GrMiddleOutPolygonTriangulator::WritePathInnerFan( int numWritten = GrMiddleOutPolygonTriangulator::WritePathInnerFan(
&vertexWriter, &vertexWriter, 2, GrPathTessellationShader::PortableInfinityBits32(shaderCaps),
GrMiddleOutPolygonTriangulator::OutputType::kConicsWithInfiniteWeight, path); path);
numRemainingTriangles -= numWritten; numRemainingTriangles -= numWritten;
} }
if (breadcrumbTriangleList) { if (breadcrumbTriangleList) {
@ -216,7 +222,7 @@ void GrPathCurveTessellator::prepare(GrMeshDrawTarget* target, const SkRect& cul
} }
vertexWriter.writeArray(tri->fPts, 3); vertexWriter.writeArray(tri->fPts, 3);
// Mark this instance as a triangle by setting it to a conic with w=Inf. // 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; ++numWritten;
} }
SkASSERT(count == breadcrumbTriangleList->count()); 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 // 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 // T=(1/2)..1 on the second side. This means we get double the max tessellation segments
// for the range T=0..1. // for the range T=0..1.
maxSegments = target->caps().shaderCaps()->maxTessellationSegments() * 2; maxSegments = shaderCaps.maxTessellationSegments() * 2;
} else { } else {
maxSegments = GrPathTessellationShader::kMaxFixedCountSegments; maxSegments = GrPathTessellationShader::kMaxFixedCountSegments;
} }
CurveWriter curveWriter(cullBounds, fShader->viewMatrix(), maxSegments); CurveWriter curveWriter(shaderCaps, cullBounds, fShader->viewMatrix(), maxSegments);
for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) { for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
switch (verb) { switch (verb) {
case SkPathVerb::kQuad: case SkPathVerb::kQuad:

View File

@ -13,7 +13,6 @@
#include "src/gpu/GrOpFlushState.h" #include "src/gpu/GrOpFlushState.h"
#include "src/gpu/GrRecordingContextPriv.h" #include "src/gpu/GrRecordingContextPriv.h"
#include "src/gpu/GrResourceProvider.h" #include "src/gpu/GrResourceProvider.h"
#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h" #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
#include "src/gpu/tessellate/GrPathCurveTessellator.h" #include "src/gpu/tessellate/GrPathCurveTessellator.h"
#include "src/gpu/tessellate/GrTessellationPathRenderer.h" #include "src/gpu/tessellate/GrTessellationPathRenderer.h"
@ -49,13 +48,14 @@ private:
GrGLSLGeometryProcessor* HullShader::createGLSLInstance(const GrShaderCaps&) const { GrGLSLGeometryProcessor* HullShader::createGLSLInstance(const GrShaderCaps&) const {
class Impl : public GrPathTessellationShader::Impl { class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(const GrPathTessellationShader&, GrGLSLVertexBuilder* v, void emitVertexCode(const GrShaderCaps& shaderCaps, const GrPathTessellationShader&,
GrGPArgs* gpArgs) override { GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
v->insertFunction(SkSLPortable_isinf(shaderCaps));
v->codeAppend(R"( v->codeAppend(R"(
float4x2 P = float4x2(input_points_0_1, input_points_2_3); 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; float w = P[3].x;
if (isinf(w)) { if (isinf_portable(w)) {
// A conic with w=Inf is an exact triangle. // A conic with w=Inf is an exact triangle.
P = float4x2(P[0], P[1], P[2], P[2]); P = float4x2(P[0], P[1], P[2], P[2]);
} else { } 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 // If we don't have sk_VertexID support then "vertexidx" already came in as a
// vertex attrib. // vertex attrib.
v->codeAppend(R"( v->codeAppend(R"(

View File

@ -12,7 +12,6 @@
#include "src/gpu/GrOpFlushState.h" #include "src/gpu/GrOpFlushState.h"
#include "src/gpu/GrRecordingContextPriv.h" #include "src/gpu/GrRecordingContextPriv.h"
#include "src/gpu/GrResourceProvider.h" #include "src/gpu/GrResourceProvider.h"
#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h" #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
#include "src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h" #include "src/gpu/tessellate/GrMiddleOutPolygonTriangulator.h"
#include "src/gpu/tessellate/GrPathCurveTessellator.h" #include "src/gpu/tessellate/GrPathCurveTessellator.h"
@ -50,9 +49,9 @@ private:
GrGLSLGeometryProcessor* BoundingBoxShader::createGLSLInstance(const GrShaderCaps&) const { GrGLSLGeometryProcessor* BoundingBoxShader::createGLSLInstance(const GrShaderCaps&) const {
class Impl : public GrPathTessellationShader::Impl { class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(const GrPathTessellationShader&, GrGLSLVertexBuilder* v, void emitVertexCode(const GrShaderCaps& shaderCaps, const GrPathTessellationShader&,
GrGPArgs* gpArgs) override { GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
if (v->getProgramBuilder()->caps()->shaderCaps()->vertexIDSupport()) { if (shaderCaps.vertexIDSupport()) {
// If we don't have sk_VertexID support then "unitCoord" already came in as a vertex // If we don't have sk_VertexID support then "unitCoord" already came in as a vertex
// attrib. // attrib.
v->codeAppendf(R"( v->codeAppendf(R"(
@ -185,9 +184,8 @@ void GrPathStencilCoverOp::onPrepare(GrOpFlushState* flushState) {
GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fFanBuffer, &fFanBaseVertex); GrEagerDynamicVertexAllocator vertexAlloc(flushState, &fFanBuffer, &fFanBaseVertex);
int maxFanTriangles = fPath.countVerbs() - 2; // n - 2 triangles make an n-gon. int maxFanTriangles = fPath.countVerbs() - 2; // n - 2 triangles make an n-gon.
GrVertexWriter triangleVertexWriter = vertexAlloc.lock<SkPoint>(maxFanTriangles * 3); GrVertexWriter triangleVertexWriter = vertexAlloc.lock<SkPoint>(maxFanTriangles * 3);
fFanVertexCount = GrMiddleOutPolygonTriangulator::WritePathInnerFan( fFanVertexCount = 3 * GrMiddleOutPolygonTriangulator::WritePathInnerFan(
&triangleVertexWriter, GrMiddleOutPolygonTriangulator::OutputType::kTriangles, &triangleVertexWriter, 0, 0, fPath);
fPath) * 3;
SkASSERT(fFanVertexCount <= maxFanTriangles * 3); SkASSERT(fFanVertexCount <= maxFanTriangles * 3);
vertexAlloc.unlock(fFanVertexCount); vertexAlloc.unlock(fFanVertexCount);
} }

View File

@ -139,15 +139,16 @@ public:
fNumFixedSegments_pow4 = std::max(numSegments_pow4, fNumFixedSegments_pow4); 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 w, SkPoint midpoint) {
float numSegments_pow2 = GrWangsFormula::conic_pow2(kPrecision, p, w, fVectorXform); float numSegments_pow2 = GrWangsFormula::conic_pow2(kPrecision, p, w, fVectorXform);
if (GrWangsFormula::conic_pow2(kPrecision, p, w, fVectorXform) > fMaxSegments_pow2) { if (GrWangsFormula::conic_pow2(kPrecision, p, w, fVectorXform) > fMaxSegments_pow2) {
this->chopAndWriteConicWedges(chunker, {p, w}, midpoint); this->chopAndWriteConicWedges(shaderCaps, chunker, {p, w}, midpoint);
return; return;
} }
if (GrVertexWriter vertexWriter = chunker->appendVertex()) { if (GrVertexWriter vertexWriter = chunker->appendVertex()) {
GrTessellationShader::WriteConicPatch(p, w, &vertexWriter); GrTessellationShader::WriteConicPatch(shaderCaps, p, w, &vertexWriter);
vertexWriter.write(midpoint); vertexWriter.write(midpoint);
} }
fNumFixedSegments_pow4 = std::max(numSegments_pow2 * numSegments_pow2, fNumFixedSegments_pow4 = std::max(numSegments_pow2 * numSegments_pow2,
@ -185,15 +186,15 @@ private:
} }
} }
void chopAndWriteConicWedges(GrVertexChunkBuilder* chunker, const SkConic& conic, void chopAndWriteConicWedges(const GrShaderCaps& shaderCaps, GrVertexChunkBuilder* chunker,
SkPoint midpoint) { const SkConic& conic, SkPoint midpoint) {
SkConic chops[2]; SkConic chops[2];
if (!conic.chopAt(.5, chops)) { if (!conic.chopAt(.5, chops)) {
return; return;
} }
for (int i = 0; i < 2; ++i) { for (int i = 0; i < 2; ++i) {
if (fCullTest.areVisible3(chops[i].fPts)) { 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 { } else {
this->writeFlatWedge(chunker, chops[i].fPts[0], chops[i].fPts[2], midpoint); 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; maxSegments = GrPathTessellationShader::kMaxFixedCountSegments;
} }
const GrShaderCaps& shaderCaps = *target->caps().shaderCaps();
WedgeWriter wedgeWriter(cullBounds, fShader->viewMatrix(), maxSegments); WedgeWriter wedgeWriter(cullBounds, fShader->viewMatrix(), maxSegments);
MidpointContourParser parser(path); MidpointContourParser parser(path);
while (parser.parseNextContour()) { while (parser.parseNextContour()) {
@ -291,7 +293,7 @@ void GrPathWedgeTessellator::prepare(GrMeshDrawTarget* target, const SkRect& cul
lastPoint = pts[2]; lastPoint = pts[2];
break; break;
case SkPathVerb::kConic: case SkPathVerb::kConic:
wedgeWriter.writeConicWedge(&chunker, pts, *w, midpoint); wedgeWriter.writeConicWedge(shaderCaps, &chunker, pts, *w, midpoint);
lastPoint = pts[2]; lastPoint = pts[2];
break; break;
case SkPathVerb::kCubic: case SkPathVerb::kCubic:

View File

@ -77,15 +77,15 @@ public:
fMaxParametricSegments_pow4); 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 n = GrWangsFormula::conic_pow2(fParametricPrecision, p, w);
float numParametricSegments_pow4 = n*n; float numParametricSegments_pow4 = n*n;
if (numParametricSegments_pow4 > kMaxParametricSegments_pow4) { if (numParametricSegments_pow4 > kMaxParametricSegments_pow4) {
this->chopConicTo({p, w}); this->chopConicTo(shaderCaps, {p, w});
return; return;
} }
SkPoint conic[4]; SkPoint conic[4];
GrTessellationShader::WriteConicPatch(p, w, conic); GrTessellationShader::WriteConicPatch(shaderCaps, p, w, conic);
SkPoint endControlPoint = conic[1]; SkPoint endControlPoint = conic[1];
this->writeStroke(conic, endControlPoint); this->writeStroke(conic, endControlPoint);
fMaxParametricSegments_pow4 = std::max(numParametricSegments_pow4, 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]; SkConic chops[2];
if (!conic.chopAt(.5f, chops)) { if (!conic.chopAt(.5f, chops)) {
return; return;
} }
for (int i = 0; i < 2; ++i) { for (int i = 0; i < 2; ++i) {
if (fCullTest.areVisible3(chops[i].fPts)) { if (fCullTest.areVisible3(chops[i].fPts)) {
this->conicTo(chops[i].fPts, chops[i].fW); this->conicTo(shaderCaps, chops[i].fPts, chops[i].fW);
} else { } else {
this->discardStroke(chops[i].fPts, 3); this->discardStroke(chops[i].fPts, 3);
} }
@ -235,21 +235,22 @@ static int worst_case_edges_in_join(SkPaint::Join joinType, float numRadialSegme
} // namespace } // namespace
GrStrokeFixedCountTessellator::GrStrokeFixedCountTessellator(ShaderFlags shaderFlags, GrStrokeFixedCountTessellator::GrStrokeFixedCountTessellator(const GrShaderCaps& shaderCaps,
ShaderFlags shaderFlags,
const SkMatrix& viewMatrix, const SkMatrix& viewMatrix,
PathStrokeList* pathStrokeList, PathStrokeList* pathStrokeList,std::array<float,
std::array<float,2> matrixMinMaxScales, 2> matrixMinMaxScales,
const SkRect& strokeCullBounds, const SkRect& strokeCullBounds)
const GrShaderCaps& shaderCaps) : GrStrokeTessellator(shaderCaps, GrStrokeTessellationShader::Mode::kFixedCount,
: GrStrokeTessellator(GrStrokeTessellationShader::Mode::kFixedCount, shaderFlags, shaderFlags, kMaxParametricSegments_log2, viewMatrix,
kMaxParametricSegments_log2, viewMatrix, pathStrokeList, pathStrokeList, matrixMinMaxScales, strokeCullBounds) {
matrixMinMaxScales, strokeCullBounds, shaderCaps) {
} }
GR_DECLARE_STATIC_UNIQUE_KEY(gVertexIDFallbackBufferKey); GR_DECLARE_STATIC_UNIQUE_KEY(gVertexIDFallbackBufferKey);
void GrStrokeFixedCountTessellator::prepare(GrMeshDrawTarget* target, void GrStrokeFixedCountTessellator::prepare(GrMeshDrawTarget* target,
int totalCombinedVerbCnt) { int totalCombinedVerbCnt) {
const GrShaderCaps& shaderCaps = *target->caps().shaderCaps();
int maxEdgesInJoin = 0; int maxEdgesInJoin = 0;
float maxRadialSegmentsPerRadian = 0; float maxRadialSegmentsPerRadian = 0;
@ -335,7 +336,7 @@ void GrStrokeFixedCountTessellator::prepare(GrMeshDrawTarget* target,
instanceWriter.lineTo(p[0], cusp); instanceWriter.lineTo(p[0], cusp);
instanceWriter.lineTo(cusp, p[2]); instanceWriter.lineTo(cusp, p[2]);
} else { } else {
instanceWriter.conicTo(p, strokeIter.w()); instanceWriter.conicTo(shaderCaps, p, strokeIter.w());
} }
break; break;
case Verb::kCubic: case Verb::kCubic:

View File

@ -15,9 +15,9 @@
// instance are emitted as degenerate triangles. // instance are emitted as degenerate triangles.
class GrStrokeFixedCountTessellator : public GrStrokeTessellator { class GrStrokeFixedCountTessellator : public GrStrokeTessellator {
public: public:
GrStrokeFixedCountTessellator(ShaderFlags, const SkMatrix&, PathStrokeList*, GrStrokeFixedCountTessellator(const GrShaderCaps&, ShaderFlags, const SkMatrix&,
std::array<float,2> matrixMinMaxScales, PathStrokeList*, std::array<float, 2> matrixMinMaxScales,
const SkRect& strokeCullBounds, const GrShaderCaps&); const SkRect& strokeCullBounds);
void prepare(GrMeshDrawTarget*, int totalCombinedVerbCnt) override; void prepare(GrMeshDrawTarget*, int totalCombinedVerbCnt) override;
void draw(GrOpFlushState*) const override; void draw(GrOpFlushState*) const override;

View File

@ -167,8 +167,8 @@ public:
// Recursively chops the given conic and its previous join until the segments fit in // Recursively chops the given conic and its previous join until the segments fit in
// tessellation patches. // tessellation patches.
void writeConicPatchesTo(const SkPoint p[3], float w) { void writeConicPatchesTo(const GrShaderCaps& shaderCaps, const SkPoint p[3], float w) {
this->internalConicPatchesTo(fStrokeJoinType, p, w); this->internalConicPatchesTo(shaderCaps, fStrokeJoinType, p, w);
} }
// Chops the given cubic at points of inflection and 180-degree rotation, and then recursively // 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 // Recursively chops the given conic and its previous join until the segments fit in
// tessellation patches. // tessellation patches.
void internalConicPatchesTo(JoinType prevJoinType, const SkPoint p[3], float w, void internalConicPatchesTo(const GrShaderCaps& shaderCaps, JoinType prevJoinType,
int maxDepth = -1) { const SkPoint p[3], float w, int maxDepth = -1) {
if (!fCullTest.areVisible3(p)) { if (!fCullTest.areVisible3(p)) {
// The stroke is out of view. Discard it. // The stroke is out of view. Discard it.
this->discardStroke(p, 3); this->discardStroke(p, 3);
@ -370,7 +370,7 @@ private:
if (w == 1) { if (w == 1) {
GrPathUtils::convertQuadToCubic(p, asPatch); GrPathUtils::convertQuadToCubic(p, asPatch);
} else { } else {
GrTessellationShader::WriteConicPatch(p, w, asPatch); GrTessellationShader::WriteConicPatch(shaderCaps, p, w, asPatch);
} }
float numParametricSegments_pow4; float numParametricSegments_pow4;
@ -411,18 +411,19 @@ private:
} else { } else {
SkChopQuadAtMidTangent(p, chops); SkChopQuadAtMidTangent(p, chops);
} }
this->internalConicPatchesTo(prevJoinType, chops, 1, maxDepth - 1); this->internalConicPatchesTo(shaderCaps, prevJoinType, chops, 1, maxDepth - 1);
this->internalConicPatchesTo(JoinType::kBowtie, chops + 2, 1, maxDepth - 1); this->internalConicPatchesTo(shaderCaps, JoinType::kBowtie, chops + 2, 1,
maxDepth - 1);
} else { } else {
SkConic conic(p, w); SkConic conic(p, w);
float chopT = (numParametricSegments >= numRadialSegments) ? .5f float chopT = (numParametricSegments >= numRadialSegments) ? .5f
: conic.findMidTangent(); : conic.findMidTangent();
SkConic chops[2]; SkConic chops[2];
if (conic.chopAt(chopT, chops)) { if (conic.chopAt(chopT, chops)) {
this->internalConicPatchesTo(prevJoinType, chops[0].fPts, chops[0].fW, this->internalConicPatchesTo(shaderCaps, prevJoinType, chops[0].fPts,
maxDepth - 1); chops[0].fW, maxDepth - 1);
this->internalConicPatchesTo(JoinType::kBowtie, chops[1].fPts, chops[1].fW, this->internalConicPatchesTo(shaderCaps, JoinType::kBowtie, chops[1].fPts,
maxDepth - 1); chops[1].fW, maxDepth - 1);
} }
} }
return; return;
@ -703,20 +704,22 @@ SK_ALWAYS_INLINE static bool cubic_has_cusp(const SkPoint p[4]) {
} // namespace } // namespace
GrStrokeHardwareTessellator::GrStrokeHardwareTessellator(ShaderFlags shaderFlags, GrStrokeHardwareTessellator::GrStrokeHardwareTessellator(const GrShaderCaps& shaderCaps,
const GrShaderCaps& shaderCaps, ShaderFlags shaderFlags,
const SkMatrix& viewMatrix, const SkMatrix& viewMatrix,
PathStrokeList* pathStrokeList, PathStrokeList* pathStrokeList,
std::array<float,2> matrixMinMaxScales, std::array<float,2> matrixMinMaxScales,
const SkRect& strokeCullBounds) const SkRect& strokeCullBounds)
: GrStrokeTessellator(GrStrokeTessellationShader::Mode::kHardwareTessellation, shaderFlags, : GrStrokeTessellator(shaderCaps, GrStrokeTessellationShader::Mode::kHardwareTessellation,
SkNextLog2(shaderCaps.maxTessellationSegments()), viewMatrix, shaderFlags, SkNextLog2(shaderCaps.maxTessellationSegments()),
pathStrokeList, matrixMinMaxScales, strokeCullBounds, shaderCaps) { viewMatrix, pathStrokeList, matrixMinMaxScales, strokeCullBounds) {
} }
void GrStrokeHardwareTessellator::prepare(GrMeshDrawTarget* target, int totalCombinedVerbCnt) { void GrStrokeHardwareTessellator::prepare(GrMeshDrawTarget* target, int totalCombinedVerbCnt) {
using JoinType = PatchWriter::JoinType; 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. // Over-allocate enough patches for 1 in 4 strokes to chop and for 8 extra caps.
int strokePreallocCount = totalCombinedVerbCnt * 5/4; int strokePreallocCount = totalCombinedVerbCnt * 5/4;
int capPreallocCount = 8; int capPreallocCount = 8;
@ -807,7 +810,7 @@ void GrStrokeHardwareTessellator::prepare(GrMeshDrawTarget* target, int totalCom
if (!patchWriter.stroke180FitsInPatch(numParametricSegments_pow4)) { if (!patchWriter.stroke180FitsInPatch(numParametricSegments_pow4)) {
// The curve requires more tessellation segments than the hardware can // The curve requires more tessellation segments than the hardware can
// support. This is rare. Recursively chop until each sub-curve fits. // support. This is rare. Recursively chop until each sub-curve fits.
patchWriter.writeConicPatchesTo(p, 1); patchWriter.writeConicPatchesTo(shaderCaps, p, 1);
continue; continue;
} }
// The curve fits in a single tessellation patch. This is the most common case. // 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)) { if (!patchWriter.stroke180FitsInPatch(numParametricSegments_pow4)) {
// The curve requires more tessellation segments than the hardware can // The curve requires more tessellation segments than the hardware can
// support. This is rare. Recursively chop until each sub-curve fits. // support. This is rare. Recursively chop until each sub-curve fits.
patchWriter.writeConicPatchesTo(p, *w); patchWriter.writeConicPatchesTo(shaderCaps, p, *w);
continue; continue;
} }
// The curve fits in a single tessellation patch. This is the most common // The curve fits in a single tessellation patch. This is the most common
// case. Write it out directly. // case. Write it out directly.
prevJoinFitsInPatch = patchWriter.stroke180FitsInPatch_withJoin( prevJoinFitsInPatch = patchWriter.stroke180FitsInPatch_withJoin(
numParametricSegments_pow4); numParametricSegments_pow4);
GrTessellationShader::WriteConicPatch(p, *w, scratchPts); GrTessellationShader::WriteConicPatch(shaderCaps, p, *w, scratchPts);
patchPts = scratchPts; patchPts = scratchPts;
endControlPoint = p[1]; endControlPoint = p[1];
break; break;

View File

@ -16,7 +16,7 @@
// MSAA if antialiasing is desired. // MSAA if antialiasing is desired.
class GrStrokeHardwareTessellator : public GrStrokeTessellator { class GrStrokeHardwareTessellator : public GrStrokeTessellator {
public: public:
GrStrokeHardwareTessellator(ShaderFlags shaderFlags, const GrShaderCaps& shaderCaps, GrStrokeHardwareTessellator(const GrShaderCaps& shaderCaps, ShaderFlags shaderFlags,
const SkMatrix& viewMatrix, PathStrokeList* pathStrokeList, const SkMatrix& viewMatrix, PathStrokeList* pathStrokeList,
std::array<float,2> matrixMinMaxScales, std::array<float,2> matrixMinMaxScales,
const SkRect& strokeCullBounds); const SkRect& strokeCullBounds);

View File

@ -190,16 +190,15 @@ void GrStrokeTessellateOp::prePrepareTessellator(GrTessellationShader::ProgramAr
if (can_use_hardware_tessellation(fTotalCombinedVerbCnt, *pipeline, caps)) { if (can_use_hardware_tessellation(fTotalCombinedVerbCnt, *pipeline, caps)) {
// Only use hardware tessellation if we're drawing a somewhat large number of verbs. // Only use hardware tessellation if we're drawing a somewhat large number of verbs.
// Otherwise we seem to be better off using instanced draws. // Otherwise we seem to be better off using instanced draws.
fTessellator = arena->make<GrStrokeHardwareTessellator>(fShaderFlags, *caps.shaderCaps(), fTessellator = arena->make<GrStrokeHardwareTessellator>(*caps.shaderCaps(), fShaderFlags,
fViewMatrix, &fPathStrokeList, fViewMatrix, &fPathStrokeList,
matrixMinMaxScales, matrixMinMaxScales,
strokeCullBounds); strokeCullBounds);
} else { } else {
fTessellator = arena->make<GrStrokeFixedCountTessellator>(fShaderFlags, fViewMatrix, fTessellator = arena->make<GrStrokeFixedCountTessellator>(*caps.shaderCaps(), fShaderFlags,
&fPathStrokeList, fViewMatrix, &fPathStrokeList,
matrixMinMaxScales, matrixMinMaxScales,
strokeCullBounds, strokeCullBounds);
*caps.shaderCaps());
} }
auto fillStencil = &GrUserStencilSettings::kUnused; auto fillStencil = &GrUserStencilSettings::kUnused;

View File

@ -25,12 +25,12 @@ public:
PathStrokeList* fNext = nullptr; PathStrokeList* fNext = nullptr;
}; };
GrStrokeTessellator(GrStrokeTessellationShader::Mode shaderMode, ShaderFlags shaderFlags, GrStrokeTessellator(const GrShaderCaps& shaderCaps, GrStrokeTessellationShader::Mode shaderMode,
int8_t maxParametricSegments_log2, const SkMatrix& viewMatrix, ShaderFlags shaderFlags, int8_t maxParametricSegments_log2,
PathStrokeList* pathStrokeList, std::array<float,2> matrixMinMaxScales, const SkMatrix& viewMatrix, PathStrokeList* pathStrokeList,
const SkRect& strokeCullBounds, const GrShaderCaps& shaderCaps) std::array<float, 2> matrixMinMaxScales, const SkRect& strokeCullBounds)
: fShader(shaderMode, shaderFlags, viewMatrix, pathStrokeList->fStroke, : fShader(shaderCaps, shaderMode, shaderFlags, viewMatrix, pathStrokeList->fStroke,
pathStrokeList->fColor, maxParametricSegments_log2, shaderCaps) pathStrokeList->fColor, maxParametricSegments_log2)
, fPathStrokeList(pathStrokeList) , fPathStrokeList(pathStrokeList)
, fMatrixMinMaxScales(matrixMinMaxScales) , fMatrixMinMaxScales(matrixMinMaxScales)
, fStrokeCullBounds(strokeCullBounds) { , fStrokeCullBounds(strokeCullBounds) {

View File

@ -39,6 +39,7 @@ bool GrTessellationPathRenderer::IsSupported(const GrCaps& caps) {
return !caps.avoidStencilBuffers() && return !caps.avoidStencilBuffers() &&
caps.drawInstancedSupport() && caps.drawInstancedSupport() &&
caps.shaderCaps()->integerSupport() && caps.shaderCaps()->integerSupport() &&
GrTessellationShader::SupportsPortableInfinity(*caps.shaderCaps()) &&
!caps.disableTessellationPathRenderer(); !caps.disableTessellationPathRenderer();
} }

View File

@ -31,8 +31,8 @@ private:
GrGLSLGeometryProcessor* SimpleTriangleShader::createGLSLInstance(const GrShaderCaps&) const { GrGLSLGeometryProcessor* SimpleTriangleShader::createGLSLInstance(const GrShaderCaps&) const {
class Impl : public GrPathTessellationShader::Impl { class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(const GrPathTessellationShader&, GrGLSLVertexBuilder* v, void emitVertexCode(const GrShaderCaps&, const GrPathTessellationShader&,
GrGPArgs* gpArgs) override { GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
v->codeAppend(R"( v->codeAppend(R"(
float2 localcoord = inputPoint; float2 localcoord = inputPoint;
float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)"); float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
@ -50,19 +50,6 @@ GrPathTessellationShader* GrPathTessellationShader::MakeSimpleTriangleShader(
return arena->make<SimpleTriangleShader>(viewMatrix, color); return arena->make<SimpleTriangleShader>(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 // 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 // "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. // 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); kFloat2_GrSLType, "translate", &translate);
args.fVertBuilder->codeAppendf("float2x2 AFFINE_MATRIX = float2x2(%s);", affineMatrix); args.fVertBuilder->codeAppendf("float2x2 AFFINE_MATRIX = float2x2(%s);", affineMatrix);
args.fVertBuilder->codeAppendf("float2 TRANSLATE = %s;", translate); args.fVertBuilder->codeAppendf("float2 TRANSLATE = %s;", translate);
this->emitVertexCode(shader, args.fVertBuilder, gpArgs); this->emitVertexCode(*args.fShaderCaps, shader, args.fVertBuilder, gpArgs);
// Fragment shader. // Fragment shader.
const char* color; const char* color;

View File

@ -187,11 +187,6 @@ protected:
const GrGeometryProcessor&) override; const GrGeometryProcessor&) override;
protected: 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) { ... // 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 // Evaluate our point of interest using numerically stable linear interpolations. We add our
@ -200,8 +195,8 @@ protected:
// does not always. // does not always.
static const char* kEvalRationalCubicFn; static const char* kEvalRationalCubicFn;
virtual void emitVertexCode(const GrPathTessellationShader&, GrGLSLVertexBuilder*, virtual void emitVertexCode(const GrShaderCaps&, const GrPathTessellationShader&,
GrGPArgs*) = 0; GrGLSLVertexBuilder*, GrGPArgs*) = 0;
GrGLSLUniformHandler::UniformHandle fAffineMatrixUniform; GrGLSLUniformHandler::UniformHandle fAffineMatrixUniform;
GrGLSLUniformHandler::UniformHandle fTranslateUniform; GrGLSLUniformHandler::UniformHandle fTranslateUniform;

View File

@ -46,12 +46,14 @@ private:
GrGLSLGeometryProcessor* HardwareWedgeShader::createGLSLInstance(const GrShaderCaps&) const { GrGLSLGeometryProcessor* HardwareWedgeShader::createGLSLInstance(const GrShaderCaps&) const {
class Impl : public GrPathTessellationShader::Impl { class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(const GrPathTessellationShader&, GrGLSLVertexBuilder* v, void emitVertexCode(const GrShaderCaps& shaderCaps, const GrPathTessellationShader&,
GrGPArgs*) override { GrGLSLVertexBuilder* v, GrGPArgs*) override {
v->declareGlobal(GrShaderVar("vsPt", kFloat2_GrSLType, GrShaderVar::TypeModifier::Out)); v->declareGlobal(GrShaderVar("vsPt", kFloat2_GrSLType, GrShaderVar::TypeModifier::Out));
v->insertFunction(SkSLPortable_isinf(shaderCaps));
v->codeAppend(R"( v->codeAppend(R"(
// If y is infinity then x is a conic weight. Don't transform. // 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&, SkString getTessControlShaderGLSL(const GrGeometryProcessor&,
const char* versionAndExtensionDecls, const char* versionAndExtensionDecls,
@ -64,7 +66,7 @@ GrGLSLGeometryProcessor* HardwareWedgeShader::createGLSLInstance(const GrShaderC
#define PRECISION %f)", GrTessellationShader::kLinearizationPrecision); #define PRECISION %f)", GrTessellationShader::kLinearizationPrecision);
code.append(kSkSLTypeDefs); code.append(kSkSLTypeDefs);
code.append(GrWangsFormula::as_sksl()); code.append(GrWangsFormula::as_sksl());
code.append(kUnpackRationalCubicFn); code.append(SkSLPortable_isinf(shaderCaps));
code.append(R"( code.append(R"(
layout(vertices = 1) out; layout(vertices = 1) out;
@ -76,7 +78,7 @@ GrGLSLGeometryProcessor* HardwareWedgeShader::createGLSLInstance(const GrShaderC
void main() { void main() {
mat4x2 P = mat4x2(vsPt[0], vsPt[1], vsPt[2], vsPt[3]); mat4x2 P = mat4x2(vsPt[0], vsPt[1], vsPt[2], vsPt[3]);
float numSegments; float numSegments;
if (isinf(P[3].y)) { if (isinf_portable(P[3].y)) {
// This is a conic. // This is a conic.
float w = P[3].x; float w = P[3].x;
numSegments = wangs_formula_conic(PRECISION, mat3x2(P), w); numSegments = wangs_formula_conic(PRECISION, mat3x2(P), w);
@ -174,12 +176,14 @@ private:
GrGLSLGeometryProcessor* HardwareCurveShader::createGLSLInstance(const GrShaderCaps&) const { GrGLSLGeometryProcessor* HardwareCurveShader::createGLSLInstance(const GrShaderCaps&) const {
class Impl : public GrPathTessellationShader::Impl { class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(const GrPathTessellationShader&, GrGLSLVertexBuilder* v, void emitVertexCode(const GrShaderCaps& shaderCaps, const GrPathTessellationShader&,
GrGPArgs*) override { GrGLSLVertexBuilder* v, GrGPArgs*) override {
v->declareGlobal(GrShaderVar("P", kFloat2_GrSLType, GrShaderVar::TypeModifier::Out)); v->declareGlobal(GrShaderVar("P", kFloat2_GrSLType, GrShaderVar::TypeModifier::Out));
v->insertFunction(SkSLPortable_isinf(shaderCaps));
v->codeAppend(R"( v->codeAppend(R"(
// If y is infinity then x is a conic weight. Don't transform. // 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&, SkString getTessControlShaderGLSL(const GrGeometryProcessor&,
const char* versionAndExtensionDecls, const char* versionAndExtensionDecls,
@ -192,7 +196,7 @@ GrGLSLGeometryProcessor* HardwareCurveShader::createGLSLInstance(const GrShaderC
#define PRECISION %f)", GrTessellationShader::kLinearizationPrecision); #define PRECISION %f)", GrTessellationShader::kLinearizationPrecision);
code.append(kSkSLTypeDefs); code.append(kSkSLTypeDefs);
code.append(GrWangsFormula::as_sksl()); code.append(GrWangsFormula::as_sksl());
code.append(kUnpackRationalCubicFn); code.append(SkSLPortable_isinf(shaderCaps));
code.append(R"( code.append(R"(
layout(vertices = 1) out; layout(vertices = 1) out;
@ -203,7 +207,7 @@ GrGLSLGeometryProcessor* HardwareCurveShader::createGLSLInstance(const GrShaderC
void main() { void main() {
float w = -1; // w<0 means a cubic. float w = -1; // w<0 means a cubic.
vec2 p1w = P[1]; 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. // This patch is actually a conic. Project to homogeneous space.
w = P[3].x; w = P[3].x;
p1w *= w; p1w *= w;
@ -218,7 +222,7 @@ GrGLSLGeometryProcessor* HardwareCurveShader::createGLSLInstance(const GrShaderC
vec2 abcd = (abc + bcd) * .5; vec2 abcd = (abc + bcd) * .5;
float n0, n1; float n0, n1;
if (w < 0 || isinf(w)) { if (w < 0 || isinf_portable(w)) {
if (w < 0) { if (w < 0) {
// The patch is a cubic. Calculate how many segments are required to // The patch is a cubic. Calculate how many segments are required to
// linearize each half of the curve. // linearize each half of the curve.

View File

@ -9,7 +9,6 @@
#include "src/core/SkMathPriv.h" #include "src/core/SkMathPriv.h"
#include "src/gpu/geometry/GrWangsFormula.h" #include "src/gpu/geometry/GrWangsFormula.h"
#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h" #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
namespace { namespace {
@ -61,13 +60,14 @@ private:
GrGLSLGeometryProcessor* MiddleOutShader::createGLSLInstance(const GrShaderCaps&) const { GrGLSLGeometryProcessor* MiddleOutShader::createGLSLInstance(const GrShaderCaps&) const {
class Impl : public GrPathTessellationShader::Impl { class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(const GrPathTessellationShader& shader, GrGLSLVertexBuilder* v, void emitVertexCode(const GrShaderCaps& shaderCaps, const GrPathTessellationShader& shader,
GrGPArgs* gpArgs) override { GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
v->defineConstant("PRECISION", GrTessellationShader::kLinearizationPrecision); v->defineConstant("PRECISION", GrTessellationShader::kLinearizationPrecision);
v->defineConstant("MAX_FIXED_RESOLVE_LEVEL", (float)kMaxFixedCountResolveLevel); v->defineConstant("MAX_FIXED_RESOLVE_LEVEL", (float)kMaxFixedCountResolveLevel);
v->defineConstant("MAX_FIXED_SEGMENTS", (float)kMaxFixedCountSegments); v->defineConstant("MAX_FIXED_SEGMENTS", (float)kMaxFixedCountSegments);
v->insertFunction(GrWangsFormula::as_sksl().c_str()); v->insertFunction(GrWangsFormula::as_sksl().c_str());
if (v->getProgramBuilder()->shaderCaps()->bitManipulationSupport()) { v->insertFunction(SkSLPortable_isinf(shaderCaps));
if (shaderCaps.bitManipulationSupport()) {
v->insertFunction(R"( v->insertFunction(R"(
float ldexp_portable(float x, float p) { float ldexp_portable(float x, float p) {
return ldexp(x, int(p)); return ldexp(x, int(p));
@ -90,7 +90,7 @@ GrGLSLGeometryProcessor* MiddleOutShader::createGLSLInstance(const GrShaderCaps&
} else)"); // Fall through to next if (). } else)"); // Fall through to next if ().
} }
v->codeAppend(R"( 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. // A conic with w=Inf is an exact triangle.
localcoord = (resolveLevel != 0) ? inputPoints_0_1.zw localcoord = (resolveLevel != 0) ? inputPoints_0_1.zw
: (idxInResolveLevel != 0) ? inputPoints_2_3.xy : (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. float w = -1; // w < 0 tells us to treat the instance as an integral cubic.
float4x2 P = float4x2(inputPoints_0_1, inputPoints_2_3); float4x2 P = float4x2(inputPoints_0_1, inputPoints_2_3);
float maxResolveLevel; float maxResolveLevel;
if (isinf(P[3].y)) { if (isinf_portable(P[3].y)) {
// The patch is a conic. // The patch is a conic.
w = P[3].x; w = P[3].x;
maxResolveLevel = maxResolveLevel =

View File

@ -13,11 +13,11 @@
#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h" #include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
#include "src/gpu/tessellate/GrStrokeTessellator.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 SkMatrix& viewMatrix,
const SkStrokeRec& stroke, SkPMColor4f color, const SkStrokeRec& stroke, SkPMColor4f color,
int8_t maxParametricSegments_log2, int8_t maxParametricSegments_log2)
const GrShaderCaps& shaderCaps)
: GrTessellationShader(kTessellate_GrStrokeTessellationShader_ClassID, : GrTessellationShader(kTessellate_GrStrokeTessellationShader_ClassID,
(mode == Mode::kHardwareTessellation) (mode == Mode::kHardwareTessellation)
? GrPrimitiveType::kPatches ? GrPrimitiveType::kPatches

View File

@ -98,8 +98,8 @@ public:
}; };
// 'viewMatrix' is applied to the geometry post tessellation. It cannot have perspective. // 'viewMatrix' is applied to the geometry post tessellation. It cannot have perspective.
GrStrokeTessellationShader(Mode, ShaderFlags, const SkMatrix& viewMatrix, const SkStrokeRec&, GrStrokeTessellationShader(const GrShaderCaps&, Mode, ShaderFlags, const SkMatrix& viewMatrix,
SkPMColor4f, int8_t maxParametricSegments_log2, const GrShaderCaps&); const SkStrokeRec&, SkPMColor4f, int8_t maxParametricSegments_log2);
Mode mode() const { return fMode; } Mode mode() const { return fMode; }
ShaderFlags flags() const { return fShaderFlags; } ShaderFlags flags() const { return fShaderFlags; }

View File

@ -57,6 +57,7 @@ void GrStrokeTessellationShader::HardwareImpl::onEmitCode(EmitArgs& args, GrGPAr
v->insertFunction(kCosineBetweenVectorsFn); v->insertFunction(kCosineBetweenVectorsFn);
v->insertFunction(kMiterExtentFn); v->insertFunction(kMiterExtentFn);
v->insertFunction(kUncheckedMixFn); v->insertFunction(kUncheckedMixFn);
v->insertFunction(SkSLPortable_isinf(*args.fShaderCaps));
if (shader.hasDynamicStroke()) { if (shader.hasDynamicStroke()) {
v->insertFunction(kNumRadialSegmentsPerRadianFn); 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. // translate until the end; we just need to perform the scale and skew right now.
v->codeAppend(R"( v->codeAppend(R"(
P = AFFINE_MATRIX * P; 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. // If y3 is infinity then x3 is a conic weight. Don't transform.
P[3] = pts23Attr.zw; 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. // (pre-chopping) input points or else the seams might crack.
float2 prevJoinTangent = P[0] - prevControlPoint; float2 prevJoinTangent = P[0] - prevControlPoint;
float2 tan0 = ((P[1] == P[0]) ? P[2] : P[1]) - P[0]; 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)) { if (tan0 == float2(0)) {
// [p0, p0, p0, p3] is a reserved pattern that means this patch is a "bowtie". // [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 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); chopT = float2(0);
innerTangents = float2x2(tan0, tan0); innerTangents = float2x2(tan0, tan0);
} }
@ -274,7 +275,8 @@ void GrStrokeTessellationShader::HardwareImpl::onEmitCode(EmitArgs& args, GrGPAr
// Chop the curve at chopT[0] and chopT[1]. // Chop the curve at chopT[0] and chopT[1].
float4 ab = unchecked_mix(P[0].xyxy, P[1].xyxy, chopT.sstt); 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 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 abc = unchecked_mix(ab, bc, chopT.sstt);
float4 bcd = unchecked_mix(bc, cd, chopT.sstt); float4 bcd = unchecked_mix(bc, cd, chopT.sstt);
float4 abcd = unchecked_mix(abc, bcd, chopT.sstt); float4 abcd = unchecked_mix(abc, bcd, chopT.sstt);
@ -353,6 +355,7 @@ SkString GrStrokeTessellationShader::HardwareImpl::getTessControlShaderGLSL(
code.append(kAtan2Fn); code.append(kAtan2Fn);
code.append(kCosineBetweenVectorsFn); code.append(kCosineBetweenVectorsFn);
code.append(kMiterExtentFn); code.append(kMiterExtentFn);
code.append(SkSLPortable_isinf(shaderCaps));
code.append(R"( code.append(R"(
float cross2d(vec2 a, vec2 b) { float cross2d(vec2 a, vec2 b) {
return determinant(mat2(a,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 // Calculate the number of parametric segments. The final tessellated strip will be a
// composition of these parametric segments as well as radial segments. // 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; float numParametricSegments;
if (w < 0.0) { if (w < 0.0) {
numParametricSegments = wangs_formula_cubic(PARAMETRIC_PRECISION, P, mat2(1)); 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. // 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: 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) // 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]) 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]); : 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. if (turn == 0.0) { // This is the case for joins and cusps where points are co-located.
turn = determinant(tangents); turn = determinant(tangents);
} }
@ -561,6 +564,8 @@ SkString GrStrokeTessellationShader::HardwareImpl::getTessEvaluationShaderGLSL(
code.appendf("uniform vec4 %s;\n", affineMatrixName); code.appendf("uniform vec4 %s;\n", affineMatrixName);
code.appendf("#define AFFINE_MATRIX mat2(%s)\n", affineMatrixName); code.appendf("#define AFFINE_MATRIX mat2(%s)\n", affineMatrixName);
code.append(SkSLPortable_isinf(shaderCaps));
code.append(R"( code.append(R"(
in vec4 tcsPts01[]; in vec4 tcsPts01[];
in vec4 tcsPt2Tan0[]; in vec4 tcsPt2Tan0[];
@ -632,7 +637,7 @@ SkString GrStrokeTessellationShader::HardwareImpl::getTessEvaluationShaderGLSL(
float2 tan1 = tcsEndPtEndTan.zw; float2 tan1 = tcsEndPtEndTan.zw;
bool isFinalEdge = (gl_TessCoord.x == 1); bool isFinalEdge = (gl_TessCoord.x == 1);
float w = -1.0; // w<0 means the curve is an integral cubic. 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. 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. P[3] = P[2]; // Setting p3 equal to p2 works for the remaining rotational logic.
})"); })");

View File

@ -28,6 +28,7 @@ void GrStrokeTessellationShader::InstancedImpl::onEmitCode(EmitArgs& args, GrGPA
args.fVertBuilder->insertFunction(kMiterExtentFn); args.fVertBuilder->insertFunction(kMiterExtentFn);
args.fVertBuilder->insertFunction(kUncheckedMixFn); args.fVertBuilder->insertFunction(kUncheckedMixFn);
args.fVertBuilder->insertFunction(GrWangsFormula::as_sksl().c_str()); args.fVertBuilder->insertFunction(GrWangsFormula::as_sksl().c_str());
args.fVertBuilder->insertFunction(SkSLPortable_isinf(*args.fShaderCaps));
// Tessellation control uniforms and/or dynamic attributes. // Tessellation control uniforms and/or dynamic attributes.
if (!shader.hasDynamicStroke()) { if (!shader.hasDynamicStroke()) {
@ -90,7 +91,7 @@ void GrStrokeTessellationShader::InstancedImpl::onEmitCode(EmitArgs& args, GrGPA
float4x2 P = float4x2(pts01Attr, pts23Attr); float4x2 P = float4x2(pts01Attr, pts23Attr);
float2 lastControlPoint = argsAttr.xy; float2 lastControlPoint = argsAttr.xy;
float w = -1; // w<0 means the curve is an integral cubic. 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. 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. P[3] = P[2]; // Setting p3 equal to p2 works for the remaining rotational logic.
})"); })");

View File

@ -39,16 +39,52 @@ public:
const SkMatrix& viewMatrix() const { return fViewMatrix; } const SkMatrix& viewMatrix() const { return fViewMatrix; }
const SkPMColor4f& color() const { return fColor;} 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. // 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 // 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. // patch[3].y as NaN to flag this patch as a conic.
writer->writeArray(pts, 3); 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); GrVertexWriter writer(patch);
WriteConicPatch(pts, w, &writer); WriteConicPatch(shaderCaps, pts, w, &writer);
} }
struct ProgramArgs { struct ProgramArgs {

View File

@ -716,6 +716,7 @@ void GrVkCaps::initShaderCaps(const VkPhysicalDeviceProperties& properties,
shaderCaps->fIntegerSupport = true; shaderCaps->fIntegerSupport = true;
shaderCaps->fNonsquareMatrixSupport = true; shaderCaps->fNonsquareMatrixSupport = true;
shaderCaps->fVertexIDSupport = true; shaderCaps->fVertexIDSupport = true;
shaderCaps->fInfinitySupport = true;
shaderCaps->fBitManipulationSupport = true; shaderCaps->fBitManipulationSupport = true;
// Assume the minimum precisions mandated by the SPIR-V spec. // Assume the minimum precisions mandated by the SPIR-V spec.