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:
parent
ebf89a0eed
commit
6904303a66
@ -202,8 +202,7 @@ DEF_PATH_TESS_BENCH(middle_out_triangulation,
|
||||
int baseVertex;
|
||||
GrVertexWriter vertexWriter = static_cast<SkPoint*>(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<GrStrokeTessellator> make_hw_tessellator(
|
||||
ShaderFlags shaderFlags, const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix,
|
||||
PathStrokeList* pathStrokeList, std::array<float, 2> matrixMinMaxScales,
|
||||
const SkRect& strokeCullBounds) {
|
||||
return std::make_unique<GrStrokeHardwareTessellator>(shaderFlags, shaderCaps, viewMatrix,
|
||||
return std::make_unique<GrStrokeHardwareTessellator>(shaderCaps, shaderFlags, viewMatrix,
|
||||
pathStrokeList, matrixMinMaxScales,
|
||||
strokeCullBounds);
|
||||
}
|
||||
@ -225,9 +224,9 @@ static std::unique_ptr<GrStrokeTessellator> make_fixed_count_tessellator(
|
||||
ShaderFlags shaderFlags, const GrShaderCaps& shaderCaps, const SkMatrix& viewMatrix,
|
||||
PathStrokeList* pathStrokeList, std::array<float, 2> matrixMinMaxScales,
|
||||
const SkRect& strokeCullBounds) {
|
||||
return std::make_unique<GrStrokeFixedCountTessellator>(shaderFlags, viewMatrix, pathStrokeList,
|
||||
matrixMinMaxScales, strokeCullBounds,
|
||||
shaderCaps);
|
||||
return std::make_unique<GrStrokeFixedCountTessellator>(shaderCaps, shaderFlags, viewMatrix,
|
||||
pathStrokeList, matrixMinMaxScales,
|
||||
strokeCullBounds);
|
||||
}
|
||||
|
||||
using MakePathStrokesFn = std::vector<PathStrokeList>(*)();
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -23,8 +23,6 @@
|
||||
* thereof.
|
||||
*/
|
||||
struct GrVertexWriter {
|
||||
constexpr static uint32_t kIEEE_32_infinity = 0x7f800000;
|
||||
|
||||
void* fPtr;
|
||||
|
||||
GrVertexWriter() = default;
|
||||
|
@ -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;
|
||||
|
@ -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)) {
|
||||
|
@ -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;
|
||||
|
@ -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<kStackPreallocCount, StackVertex> fVertexStack;
|
||||
SkDEBUGCODE(int fStackAllocCount;)
|
||||
StackVertex* fTop;
|
||||
GrVertexWriter* fVertexWriter;
|
||||
int fTotalClosedTriangleCount = 0;
|
||||
OutputType fOutputType;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -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:
|
||||
|
@ -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"(
|
||||
|
@ -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<SkPoint>(maxFanTriangles * 3);
|
||||
fFanVertexCount = GrMiddleOutPolygonTriangulator::WritePathInnerFan(
|
||||
&triangleVertexWriter, GrMiddleOutPolygonTriangulator::OutputType::kTriangles,
|
||||
fPath) * 3;
|
||||
fFanVertexCount = 3 * GrMiddleOutPolygonTriangulator::WritePathInnerFan(
|
||||
&triangleVertexWriter, 0, 0, fPath);
|
||||
SkASSERT(fFanVertexCount <= maxFanTriangles * 3);
|
||||
vertexAlloc.unlock(fFanVertexCount);
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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<float,2> matrixMinMaxScales,
|
||||
const SkRect& strokeCullBounds,
|
||||
const GrShaderCaps& shaderCaps)
|
||||
: GrStrokeTessellator(GrStrokeTessellationShader::Mode::kFixedCount, shaderFlags,
|
||||
kMaxParametricSegments_log2, viewMatrix, pathStrokeList,
|
||||
matrixMinMaxScales, strokeCullBounds, shaderCaps) {
|
||||
PathStrokeList* pathStrokeList,std::array<float,
|
||||
2> 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:
|
||||
|
@ -15,9 +15,9 @@
|
||||
// instance are emitted as degenerate triangles.
|
||||
class GrStrokeFixedCountTessellator : public GrStrokeTessellator {
|
||||
public:
|
||||
GrStrokeFixedCountTessellator(ShaderFlags, const SkMatrix&, PathStrokeList*,
|
||||
std::array<float,2> matrixMinMaxScales,
|
||||
const SkRect& strokeCullBounds, const GrShaderCaps&);
|
||||
GrStrokeFixedCountTessellator(const GrShaderCaps&, ShaderFlags, const SkMatrix&,
|
||||
PathStrokeList*, std::array<float, 2> matrixMinMaxScales,
|
||||
const SkRect& strokeCullBounds);
|
||||
|
||||
void prepare(GrMeshDrawTarget*, int totalCombinedVerbCnt) override;
|
||||
void draw(GrOpFlushState*) const override;
|
||||
|
@ -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<float,2> 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;
|
||||
|
@ -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<float,2> matrixMinMaxScales,
|
||||
const SkRect& strokeCullBounds);
|
||||
|
@ -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<GrStrokeHardwareTessellator>(fShaderFlags, *caps.shaderCaps(),
|
||||
fTessellator = arena->make<GrStrokeHardwareTessellator>(*caps.shaderCaps(), fShaderFlags,
|
||||
fViewMatrix, &fPathStrokeList,
|
||||
matrixMinMaxScales,
|
||||
strokeCullBounds);
|
||||
} else {
|
||||
fTessellator = arena->make<GrStrokeFixedCountTessellator>(fShaderFlags, fViewMatrix,
|
||||
&fPathStrokeList,
|
||||
fTessellator = arena->make<GrStrokeFixedCountTessellator>(*caps.shaderCaps(), fShaderFlags,
|
||||
fViewMatrix, &fPathStrokeList,
|
||||
matrixMinMaxScales,
|
||||
strokeCullBounds,
|
||||
*caps.shaderCaps());
|
||||
strokeCullBounds);
|
||||
}
|
||||
|
||||
auto fillStencil = &GrUserStencilSettings::kUnused;
|
||||
|
@ -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<float,2> 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<float, 2> matrixMinMaxScales, const SkRect& strokeCullBounds)
|
||||
: fShader(shaderCaps, shaderMode, shaderFlags, viewMatrix, pathStrokeList->fStroke,
|
||||
pathStrokeList->fColor, maxParametricSegments_log2)
|
||||
, fPathStrokeList(pathStrokeList)
|
||||
, fMatrixMinMaxScales(matrixMinMaxScales)
|
||||
, fStrokeCullBounds(strokeCullBounds) {
|
||||
|
@ -39,6 +39,7 @@ bool GrTessellationPathRenderer::IsSupported(const GrCaps& caps) {
|
||||
return !caps.avoidStencilBuffers() &&
|
||||
caps.drawInstancedSupport() &&
|
||||
caps.shaderCaps()->integerSupport() &&
|
||||
GrTessellationShader::SupportsPortableInfinity(*caps.shaderCaps()) &&
|
||||
!caps.disableTessellationPathRenderer();
|
||||
}
|
||||
|
||||
|
@ -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<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
|
||||
// "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;
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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 =
|
||||
|
@ -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
|
||||
|
@ -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; }
|
||||
|
@ -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.
|
||||
})");
|
||||
|
@ -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.
|
||||
})");
|
||||
|
@ -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 {
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user