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;
|
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>(*)();
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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)) {
|
||||||
|
@ -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;
|
||||||
|
@ -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
|
||||||
|
@ -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:
|
||||||
|
@ -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"(
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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:
|
||||||
|
@ -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:
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
|
@ -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.
|
||||||
|
@ -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 =
|
||||||
|
@ -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
|
||||||
|
@ -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; }
|
||||||
|
@ -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.
|
||||||
})");
|
})");
|
||||||
|
@ -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.
|
||||||
})");
|
})");
|
||||||
|
@ -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 {
|
||||||
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user