Add a fixed-count impl for tessellated wedges

Bug: skia:10419
Change-Id: Ibb5adb581045e98cb636006aa84f792847041ca5
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/416856
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
This commit is contained in:
Chris Dalton 2021-06-09 00:12:59 -06:00 committed by Skia Commit-Bot
parent ec10dedc3a
commit d2b8ba3369
13 changed files with 208 additions and 139 deletions

View File

@ -122,7 +122,9 @@ DEF_PATH_TESS_BENCH(GrPathOuterCurveTessellator, make_cubic_path(8), SkMatrix::I
DEF_PATH_TESS_BENCH(GrPathWedgeTessellator, make_cubic_path(8), SkMatrix::I()) {
SkArenaAlloc arena(1024);
auto tess = GrPathWedgeTessellator::Make(&arena, fMatrix, SK_PMColor4fTRANSPARENT);
auto tess = GrPathWedgeTessellator::Make(&arena, fMatrix, SK_PMColor4fTRANSPARENT,
fTarget->caps().minPathVerbsForHwTessellation(),
fTarget->caps());
tess->prepare(fTarget.get(), SkRectPriv::MakeLargest(), fPath, nullptr);
}

View File

@ -21,6 +21,7 @@
namespace {
enum class Mode {
kWedgeMiddleOut,
kCurveMiddleOut,
kWedgeTessellate,
kCurveTessellate
@ -28,12 +29,14 @@ enum class Mode {
static const char* ModeName(Mode mode) {
switch (mode) {
case Mode::kWedgeMiddleOut:
return "MiddleOutShader (kWedges)";
case Mode::kCurveMiddleOut:
return "GrCurveMiddleOutShader";
return "MiddleOutShader (kCurves)";
case Mode::kWedgeTessellate:
return "GrWedgeTessellateShader";
return "HardwareWedgeShader";
case Mode::kCurveTessellate:
return "GrCurveTessellateShader";
return "HardwareCurveShader";
}
SkUNREACHABLE;
}
@ -68,21 +71,28 @@ private:
void onPrepare(GrOpFlushState* flushState) override {
constexpr static SkPMColor4f kCyan = {0,1,1,1};
auto alloc = flushState->allocator();
const GrCaps& caps = flushState->caps();
int numVerbsToGetMiddleOut = 0;
int numVerbsToGetTessellation = caps.minPathVerbsForHwTessellation();
switch (fMode) {
using DrawInnerFan = GrPathTessellator::DrawInnerFan;
using ShaderType = GrPathCurveTessellator::ShaderType;
using DrawInnerFan = GrPathCurveTessellator::DrawInnerFan;
case Mode::kWedgeMiddleOut:
fTessellator = GrPathWedgeTessellator::Make(alloc, fMatrix, kCyan,
numVerbsToGetMiddleOut, caps);
break;
case Mode::kCurveMiddleOut:
fTessellator = GrPathCurveTessellator::Make(alloc, fMatrix, kCyan,
DrawInnerFan::kYes,
ShaderType::kFixedCountMiddleOut);
numVerbsToGetMiddleOut, caps);
break;
case Mode::kWedgeTessellate:
fTessellator = GrPathWedgeTessellator::Make(alloc, fMatrix, kCyan);
fTessellator = GrPathWedgeTessellator::Make(alloc, fMatrix, kCyan,
numVerbsToGetTessellation, caps);
break;
case Mode::kCurveTessellate:
fTessellator = GrPathCurveTessellator::Make(alloc, fMatrix, kCyan,
DrawInnerFan::kYes,
ShaderType::kHardwareTessellation);
numVerbsToGetTessellation, caps);
break;
}
fTessellator->prepare(flushState, this->bounds(), fPath);
@ -147,7 +157,7 @@ private:
SkPath fPath;
GrPipeline::InputFlags fPipelineFlags = GrPipeline::InputFlags::kHWAntialias |
GrPipeline::InputFlags::kWireframe;
Mode fMode = Mode::kCurveMiddleOut;
Mode fMode = Mode::kWedgeMiddleOut;
float fConicWeight = .5;
@ -289,6 +299,7 @@ bool SamplePathTessellators::onChar(SkUnichar unichar) {
case '1':
case '2':
case '3':
case '4':
fMode = (Mode)(unichar - '1');
return true;
}

View File

@ -138,26 +138,15 @@ private:
GrPathTessellator* GrPathCurveTessellator::Make(SkArenaAlloc* arena, const SkMatrix& viewMatrix,
const SkPMColor4f& color, DrawInnerFan drawInnerFan,
int numPathVerbs, const GrCaps& caps) {
using PatchType = GrPathTessellationShader::PatchType;
GrPathTessellationShader* shader;
if (caps.shaderCaps()->tessellationSupport() &&
numPathVerbs >= caps.minPathVerbsForHwTessellation()) {
return Make(arena, viewMatrix, color, drawInnerFan, ShaderType::kHardwareTessellation);
shader = GrPathTessellationShader::MakeHardwareTessellationShader(arena, viewMatrix, color,
PatchType::kCurves);
} else {
return Make(arena, viewMatrix, color, drawInnerFan, ShaderType::kFixedCountMiddleOut);
}
}
GrPathTessellator* GrPathCurveTessellator::Make(SkArenaAlloc* arena, const SkMatrix& viewMatrix,
const SkPMColor4f& color, DrawInnerFan drawInnerFan,
ShaderType shaderType) {
GrPathTessellationShader* shader;
switch (shaderType) {
case ShaderType::kFixedCountMiddleOut:
shader = GrPathTessellationShader::MakeMiddleOutInstancedShader(arena, viewMatrix,
color);
break;
case ShaderType::kHardwareTessellation:
shader = GrPathTessellationShader::MakeHardwareCurveShader(arena, viewMatrix, color);
break;
shader = GrPathTessellationShader::MakeMiddleOutFixedCountShader(arena, viewMatrix, color,
PatchType::kCurves);
}
return arena->make([=](void* objStart) {
return new(objStart) GrPathCurveTessellator(shader, drawInnerFan);
@ -259,7 +248,7 @@ void GrPathCurveTessellator::prepare(GrMeshDrawOp::Target* target, const SkRect&
// log2(n) == log16(n^4).
int fixedResolveLevel = GrWangsFormula::nextlog16(curveWriter.numFixedSegments_pow4());
fFixedVertexCount =
GrPathTessellationShader::NumTrianglesAtResolveLevel(fixedResolveLevel) * 3;
GrPathTessellationShader::NumCurveTrianglesAtResolveLevel(fixedResolveLevel) * 3;
}
}

View File

@ -16,19 +16,17 @@
// or a conic. Quadratics are converted to cubics and triangles are converted to conics with w=Inf.
class GrPathCurveTessellator : public GrPathTessellator {
public:
// If DrawInnerFan is kNo, this class only emits the path's outer curves. In that case the
// caller is responsible to handle the path's inner fan.
enum class DrawInnerFan : bool {
kNo = false,
kYes
};
// Creates a curve tessellator with the shader type best suited for the given path description.
static GrPathTessellator* Make(SkArenaAlloc*, const SkMatrix& viewMatrix, const SkPMColor4f&,
DrawInnerFan, int numPathVerbs, const GrCaps&);
enum class ShaderType {
kFixedCountMiddleOut,
kHardwareTessellation
};
// Creates a curve tessellator with a specific shader type.
static GrPathTessellator* Make(SkArenaAlloc*, const SkMatrix& viewMatrix, const SkPMColor4f&,
DrawInnerFan, ShaderType);
void prepare(GrMeshDrawOp::Target*, const SkRect& cullBounds, const SkPath&,
const BreadcrumbTriangleList*) override;
void draw(GrOpFlushState*) const override;

View File

@ -42,7 +42,8 @@ private:
GrGLSLGeometryProcessor* HullShader::createGLSLInstance(const GrShaderCaps&) const {
class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
void emitVertexCode(const GrPathTessellationShader&, GrGLSLVertexBuilder* v,
GrGPArgs* gpArgs) override {
v->codeAppend(R"(
float4x2 P = float4x2(input_points_0_1, input_points_2_3);
if (isinf(P[3].y)) { // Is the curve a conic?
@ -190,7 +191,7 @@ void GrPathInnerTriangulateOp::prePreparePrograms(const GrTessellationShader::Pr
if (!isLinear) {
fTessellator = GrPathCurveTessellator::Make(args.fArena, fViewMatrix,
SK_PMColor4fTRANSPARENT,
GrPathTessellator::DrawInnerFan::kNo,
GrPathCurveTessellator::DrawInnerFan::kNo,
fPath.countVerbs(), *args.fCaps);
const GrUserStencilSettings* stencilPathSettings =
GrPathTessellationShader::StencilPathSettings(fPath.getFillType());

View File

@ -43,7 +43,8 @@ private:
GrGLSLGeometryProcessor* BoundingBoxShader::createGLSLInstance(const GrShaderCaps&) const {
class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
void emitVertexCode(const GrPathTessellationShader&, GrGLSLVertexBuilder* v,
GrGPArgs* gpArgs) override {
v->codeAppend(R"(
// Bloat the bounding box by 1/4px to avoid potential T-junctions at the edges.
float2x2 M_ = inverse(AFFINE_MATRIX);
@ -101,29 +102,22 @@ void GrPathStencilCoverOp::prePreparePrograms(const GrTessellationShader::Progra
const GrUserStencilSettings* stencilPathSettings =
GrPathTessellationShader::StencilPathSettings(fPath.getFillType());
auto drawFanWithTessellator = GrPathTessellator::DrawInnerFan::kYes;
if (fPath.countVerbs() > 50 && this->bounds().height() * this->bounds().width() > 256 * 256) {
// Large complex paths do better with a dedicated triangle shader for the inner fan. This
// takes less PCI bus bandwidth (6 floats per triangle instead of 8) and allows us to make
// sure it has an efficient middle-out topology.
// Large complex paths do better with a dedicated triangle shader for the inner fan.
// This takes less PCI bus bandwidth (6 floats per triangle instead of 8) and allows us
// to make sure it has an efficient middle-out topology.
auto shader = GrPathTessellationShader::MakeSimpleTriangleShader(
args.fArena, fViewMatrix, SK_PMColor4fTRANSPARENT);
fStencilFanProgram = GrTessellationShader::MakeProgram(args, shader, stencilPipeline,
stencilPathSettings);
drawFanWithTessellator = GrPathTessellator::DrawInnerFan::kNo;
}
if (!args.fCaps->shaderCaps()->tessellationSupport() ||
fPath.countVerbs() < args.fCaps->minPathVerbsForHwTessellation()) {
fTessellator = GrPathCurveTessellator::Make(
args.fArena, fViewMatrix, SK_PMColor4fTRANSPARENT, drawFanWithTessellator,
GrPathCurveTessellator::ShaderType::kFixedCountMiddleOut);
} else if (drawFanWithTessellator == GrPathTessellator::DrawInnerFan::kNo) {
fTessellator = GrPathCurveTessellator::Make(
args.fArena, fViewMatrix, SK_PMColor4fTRANSPARENT, drawFanWithTessellator,
GrPathCurveTessellator::ShaderType::kHardwareTessellation);
fTessellator = GrPathCurveTessellator::Make(args.fArena, fViewMatrix,
SK_PMColor4fTRANSPARENT,
GrPathCurveTessellator::DrawInnerFan::kNo,
fPath.countVerbs(), *args.fCaps);
} else {
fTessellator = GrPathWedgeTessellator::Make(args.fArena, fViewMatrix,
SK_PMColor4fTRANSPARENT);
SK_PMColor4fTRANSPARENT, fPath.countVerbs(),
*args.fCaps);
}
fStencilPathProgram = GrTessellationShader::MakeProgram(args, fTessellator->shader(),
stencilPipeline, stencilPathSettings);

View File

@ -27,13 +27,6 @@ public:
// single instance before we need to chop.
constexpr static int kMaxFixedCountSegments = 32;
// For subclasses that use this enum, if DrawInnerFan is kNo, the class only emits the path's
// outer curves. In that case the caller is responsible to handle the path's inner fan.
enum class DrawInnerFan : bool {
kNo = false,
kYes
};
const GrPathTessellationShader* shader() const { return fShader; }
// Called before draw(). Prepares GPU buffers containing the geometry to tessellate. If the

View File

@ -109,13 +109,11 @@ private:
// by the hardware.
class WedgeWriter {
public:
WedgeWriter(const SkRect& cullBounds, const SkMatrix& viewMatrix,
const GrShaderCaps& shaderCaps)
WedgeWriter(const SkRect& cullBounds, const SkMatrix& viewMatrix, int maxSegments)
: fCullTest(cullBounds, viewMatrix)
, fVectorXform(viewMatrix) {
float maxSegments = shaderCaps.maxTessellationSegments();
fMaxSegments_pow2 = maxSegments * maxSegments;
fMaxSegments_pow4 = fMaxSegments_pow2 * fMaxSegments_pow2;
, fVectorXform(viewMatrix)
, fMaxSegments_pow2(maxSegments * maxSegments)
, fMaxSegments_pow4(fMaxSegments_pow2 * fMaxSegments_pow2) {
}
SK_ALWAYS_INLINE void writeFlatWedge(GrVertexChunkBuilder* chunker, SkPoint p0, SkPoint p1,
@ -128,7 +126,8 @@ public:
SK_ALWAYS_INLINE void writeQuadraticWedge(GrVertexChunkBuilder* chunker, const SkPoint p[3],
SkPoint midpoint) {
if (GrWangsFormula::quadratic_pow4(kPrecision, p, fVectorXform) > fMaxSegments_pow4) {
float numSegments_pow4 = GrWangsFormula::quadratic_pow4(kPrecision, p, fVectorXform);
if (numSegments_pow4 > fMaxSegments_pow4) {
this->chopAndWriteQuadraticWedges(chunker, p, midpoint);
return;
}
@ -136,10 +135,12 @@ public:
GrPathUtils::writeQuadAsCubic(p, &vertexWriter);
vertexWriter.write(midpoint);
}
fNumFixedSegments_pow4 = std::max(numSegments_pow4, fNumFixedSegments_pow4);
}
SK_ALWAYS_INLINE void writeConicWedge(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);
return;
@ -148,11 +149,14 @@ public:
GrTessellationShader::WriteConicPatch(p, w, &vertexWriter);
vertexWriter.write(midpoint);
}
fNumFixedSegments_pow4 = std::max(numSegments_pow2 * numSegments_pow2,
fNumFixedSegments_pow4);
}
SK_ALWAYS_INLINE void writeCubicWedge(GrVertexChunkBuilder* chunker, const SkPoint p[4],
SkPoint midpoint) {
if (GrWangsFormula::cubic_pow4(kPrecision, p, fVectorXform) > fMaxSegments_pow4) {
float numSegments_pow4 = GrWangsFormula::cubic_pow4(kPrecision, p, fVectorXform);
if (numSegments_pow4 > fMaxSegments_pow4) {
this->chopAndWriteCubicWedges(chunker, p, midpoint);
return;
}
@ -160,8 +164,11 @@ public:
vertexWriter.writeArray(p, 4);
vertexWriter.write(midpoint);
}
fNumFixedSegments_pow4 = std::max(numSegments_pow4, fNumFixedSegments_pow4);
}
int numFixedSegments_pow4() const { return fNumFixedSegments_pow4; }
private:
void chopAndWriteQuadraticWedges(GrVertexChunkBuilder* chunker, const SkPoint p[3],
SkPoint midpoint) {
@ -208,23 +215,37 @@ private:
GrCullTest fCullTest;
GrVectorXform fVectorXform;
float fMaxSegments_pow2;
float fMaxSegments_pow4;
const float fMaxSegments_pow2;
const float fMaxSegments_pow4;
// If using fixed count, this is the max number of curve segments we need to draw per instance.
float fNumFixedSegments_pow4 = 1;
};
} // namespace
GrPathTessellator* GrPathWedgeTessellator::Make(SkArenaAlloc* arena, const SkMatrix& viewMatrix,
const SkPMColor4f& color) {
auto shader = GrPathTessellationShader::MakeHardwareWedgeShader(arena, viewMatrix, color);
return arena->make<GrPathWedgeTessellator>(shader);
const SkPMColor4f& color, int numPathVerbs,
const GrCaps& caps) {
using PatchType = GrPathTessellationShader::PatchType;
GrPathTessellationShader* shader;
if (caps.shaderCaps()->tessellationSupport() &&
numPathVerbs >= caps.minPathVerbsForHwTessellation()) {
shader = GrPathTessellationShader::MakeHardwareTessellationShader(arena, viewMatrix, color,
PatchType::kWedges);
} else {
shader = GrPathTessellationShader::MakeMiddleOutFixedCountShader(arena, viewMatrix, color,
PatchType::kWedges);
}
return arena->make([=](void* objStart) {
return new(objStart) GrPathWedgeTessellator(shader);
});
}
void GrPathWedgeTessellator::prepare(GrMeshDrawOp::Target* target, const SkRect& cullBounds,
const SkPath& path,
const BreadcrumbTriangleList* breadcrumbTriangleList) {
SkASSERT(target->caps().shaderCaps()->tessellationSupport());
SkASSERT(!breadcrumbTriangleList);
SkASSERT(fVertexChunkArray.empty());
@ -236,7 +257,14 @@ void GrPathWedgeTessellator::prepare(GrMeshDrawOp::Target* target, const SkRect&
}
GrVertexChunkBuilder chunker(target, &fVertexChunkArray, sizeof(SkPoint) * 5, wedgeAllocCount);
WedgeWriter wedgeWriter(cullBounds, fShader->viewMatrix(), *target->caps().shaderCaps());
int maxSegments;
if (fShader->willUseTessellationShaders()) {
maxSegments = target->caps().shaderCaps()->maxTessellationSegments();
} else {
maxSegments = kMaxFixedCountSegments;
}
WedgeWriter wedgeWriter(cullBounds, fShader->viewMatrix(), maxSegments);
MidpointContourParser parser(path);
while (parser.parseNextContour()) {
SkPoint midpoint = parser.currentMidpoint();
@ -271,11 +299,28 @@ void GrPathWedgeTessellator::prepare(GrMeshDrawOp::Target* target, const SkRect&
wedgeWriter.writeFlatWedge(&chunker, lastPoint, startPoint, midpoint);
}
}
if (!fShader->willUseTessellationShaders()) {
// log2(n) == log16(n^4).
int fixedResolveLevel = GrWangsFormula::nextlog16(wedgeWriter.numFixedSegments_pow4());
int numCurveTriangles =
GrPathTessellationShader::NumCurveTrianglesAtResolveLevel(fixedResolveLevel);
// Emit 3 vertices per curve triangle, plus 3 more for the fan triangle.
fFixedVertexCount = numCurveTriangles * 3 + 3;
}
}
void GrPathWedgeTessellator::draw(GrOpFlushState* flushState) const {
for (const GrVertexChunk& chunk : fVertexChunkArray) {
flushState->bindBuffers(nullptr, nullptr, chunk.fBuffer);
flushState->draw(chunk.fCount * 5, chunk.fBase * 5);
if (fShader->willUseTessellationShaders()) {
for (const GrVertexChunk& chunk : fVertexChunkArray) {
flushState->bindBuffers(nullptr, nullptr, chunk.fBuffer);
flushState->draw(chunk.fCount * 5, chunk.fBase * 5);
}
} else {
SkASSERT(fShader->hasInstanceAttributes());
for (const GrVertexChunk& chunk : fVertexChunkArray) {
flushState->bindBuffers(nullptr, chunk.fBuffer, nullptr);
flushState->drawInstanced(chunk.fCount, chunk.fBase, fFixedVertexCount, 0);
}
}
}

View File

@ -17,7 +17,9 @@
// lines are converted to cubics. Once stencilled, these wedges alone define the complete path.
class GrPathWedgeTessellator : public GrPathTessellator {
public:
static GrPathTessellator* Make(SkArenaAlloc*, const SkMatrix&, const SkPMColor4f&);
// Creates a wedge tessellator with the shader type best suited for the given path description.
static GrPathTessellator* Make(SkArenaAlloc*, const SkMatrix& viewMatrix, const SkPMColor4f&,
int numPathVerbs, const GrCaps&);
void prepare(GrMeshDrawOp::Target*, const SkRect& cullBounds, const SkPath&,
const BreadcrumbTriangleList*) override;
@ -29,7 +31,8 @@ private:
GrVertexChunkArray fVertexChunkArray;
friend class SkArenaAlloc; // For constructor.
// If using fixed count, this is the number of vertices we need to emit per instance.
int fFixedVertexCount;
};
#endif

View File

@ -31,7 +31,8 @@ private:
GrGLSLGeometryProcessor* SimpleTriangleShader::createGLSLInstance(const GrShaderCaps&) const {
class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
void emitVertexCode(const GrPathTessellationShader&, GrGLSLVertexBuilder* v,
GrGPArgs* gpArgs) override {
v->codeAppend(R"(
float2 localcoord = inputPoint;
float2 vertexpos = AFFINE_MATRIX * localcoord + TRANSLATE;)");
@ -81,7 +82,8 @@ float2 eval_rational_cubic(float4x3 P, float T) {
})";
void GrPathTessellationShader::Impl::onEmitCode(EmitArgs& args, GrGPArgs* gpArgs) {
args.fVaryingHandler->emitAttributes(args.fGeomProc);
const auto& shader = args.fGeomProc.cast<GrPathTessellationShader>();
args.fVaryingHandler->emitAttributes(shader);
// Vertex shader.
const char* affineMatrix, *translate;
@ -92,7 +94,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(args.fVertBuilder, gpArgs);
this->emitVertexCode(shader, args.fVertBuilder, gpArgs);
// Fragment shader.
const char* color;

View File

@ -21,7 +21,7 @@ public:
const SkPMColor4f&);
// How many triangles are in a curve with 2^resolveLevel line segments?
constexpr static int NumTrianglesAtResolveLevel(int resolveLevel) {
constexpr static int NumCurveTrianglesAtResolveLevel(int resolveLevel) {
// resolveLevel=0 -> 0 line segments -> 0 triangles
// resolveLevel=1 -> 2 line segments -> 1 triangle
// resolveLevel=2 -> 4 line segments -> 3 triangles
@ -30,8 +30,16 @@ public:
return (1 << resolveLevel) - 1;
}
// Uses instanced draws to triangulate standalone closed curves with a "middle-out" topology.
// Middle-out draws a triangle with vertices at T=[0, 1/2, 1] and then recurses breadth first:
enum class PatchType : bool {
// An ice cream cone shaped patch, with 4 curve control points on top of a triangle that
// fans from a 5th point at the center of the contour. (5 points per patch.)
kWedges,
// A standalone closed curve made up 4 control points. (4 points per patch.)
kCurves
};
// Uses instanced draws to triangulate curves with a "middle-out" topology. Middle-out draws a
// triangle with vertices at T=[0, 1/2, 1] and then recurses breadth first:
//
// depth=0: T=[0, 1/2, 1]
// depth=1: T=[0, 1/4, 2/4], T=[2/4, 3/4, 1]
@ -42,21 +50,16 @@ public:
// smoothly, and emits empty triangles at any vertices whose sk_VertexIDs are higher than
// necessary. It is the caller's responsibility to draw enough vertices per instance for the
// most complex curve in the batch to render smoothly (i.e., NumTrianglesAtResolveLevel() * 3).
static GrPathTessellationShader* MakeMiddleOutInstancedShader(SkArenaAlloc*,
const SkMatrix& viewMatrix,
const SkPMColor4f&);
static GrPathTessellationShader* MakeMiddleOutFixedCountShader(SkArenaAlloc*,
const SkMatrix& viewMatrix,
const SkPMColor4f&, PatchType);
// Uses GPU tessellation shaders to linearize, triangulate, and render cubic "wedge" patches. A
// wedge is a 5-point patch consisting of 4 cubic control points, plus an anchor point fanning
// from the center of the curve's resident contour.
static GrPathTessellationShader* MakeHardwareWedgeShader(SkArenaAlloc*,
const SkMatrix& viewMatrix,
const SkPMColor4f&);
// Uses GPU tessellation shaders to linearize, triangulate, and render standalone closed cubics.
static GrPathTessellationShader* MakeHardwareCurveShader(SkArenaAlloc*,
const SkMatrix& viewMatrix,
const SkPMColor4f&);
static GrPathTessellationShader* MakeHardwareTessellationShader(SkArenaAlloc*,
const SkMatrix& viewMatrix,
const SkPMColor4f&, PatchType);
// Returns the stencil settings to use for a standard Redbook "stencil" pass.
static const GrUserStencilSettings* StencilPathSettings(SkPathFillType fillType) {
@ -148,7 +151,8 @@ protected:
// does not always.
static const char* kEvalRationalCubicFn;
virtual void emitVertexCode(GrGLSLVertexBuilder*, GrGPArgs*) = 0;
virtual void emitVertexCode(const GrPathTessellationShader&, GrGLSLVertexBuilder*,
GrGPArgs*) = 0;
GrGLSLUniformHandler::UniformHandle fAffineMatrixUniform;
GrGLSLUniformHandler::UniformHandle fTranslateUniform;

View File

@ -11,6 +11,8 @@
#include "src/gpu/glsl/GrGLSLGeometryProcessor.h"
#include "src/gpu/glsl/GrGLSLVertexGeoBuilder.h"
namespace {
// Converts keywords from shared SkSL strings to native GLSL keywords.
constexpr static char kSkSLTypeDefs[] = R"(
#define float4x3 mat4x3
@ -22,8 +24,6 @@ constexpr static char kSkSLTypeDefs[] = R"(
#define float4 vec4
)";
namespace {
// Uses GPU tessellation shaders to linearize, triangulate, and render cubic "wedge" patches. A
// wedge is a 5-point patch consisting of 4 cubic control points, plus an anchor point fanning from
// the center of the curve's resident contour.
@ -46,7 +46,8 @@ private:
GrGLSLGeometryProcessor* HardwareWedgeShader::createGLSLInstance(const GrShaderCaps&) const {
class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(GrGLSLVertexBuilder* v, GrGPArgs*) override {
void emitVertexCode(const GrPathTessellationShader&, GrGLSLVertexBuilder* v,
GrGPArgs*) override {
v->declareGlobal(GrShaderVar("vsPt", kFloat2_GrSLType, GrShaderVar::TypeModifier::Out));
v->codeAppend(R"(
// If y is infinity then x is a conic weight. Don't transform.
@ -153,15 +154,6 @@ GrGLSLGeometryProcessor* HardwareWedgeShader::createGLSLInstance(const GrShaderC
return new Impl;
}
} // namespace
GrPathTessellationShader* GrPathTessellationShader::MakeHardwareWedgeShader(
SkArenaAlloc* arena, const SkMatrix& viewMatrix, const SkPMColor4f& color) {
return arena->make<HardwareWedgeShader>(viewMatrix, color);
}
namespace {
// Uses GPU tessellation shaders to linearize, triangulate, and render standalone closed cubics.
// TODO: Eventually we want to use rational cubic wedges in order to support perspective and conics.
class HardwareCurveShader : public GrPathTessellationShader {
@ -182,7 +174,8 @@ private:
GrGLSLGeometryProcessor* HardwareCurveShader::createGLSLInstance(const GrShaderCaps&) const {
class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(GrGLSLVertexBuilder* v, GrGPArgs*) override {
void emitVertexCode(const GrPathTessellationShader&, GrGLSLVertexBuilder* v,
GrGPArgs*) override {
v->declareGlobal(GrShaderVar("P", kFloat2_GrSLType, GrShaderVar::TypeModifier::Out));
v->codeAppend(R"(
// If y is infinity then x is a conic weight. Don't transform.
@ -319,7 +312,14 @@ GrGLSLGeometryProcessor* HardwareCurveShader::createGLSLInstance(const GrShaderC
} // namespace
GrPathTessellationShader* GrPathTessellationShader::MakeHardwareCurveShader(
SkArenaAlloc* arena, const SkMatrix& viewMatrix, const SkPMColor4f& color) {
return arena->make<HardwareCurveShader>(viewMatrix, color);
GrPathTessellationShader* GrPathTessellationShader::MakeHardwareTessellationShader(
SkArenaAlloc* arena, const SkMatrix& viewMatrix, const SkPMColor4f& color,
PatchType patchType) {
switch (patchType) {
case PatchType::kWedges:
return arena->make<HardwareWedgeShader>(viewMatrix, color);
case PatchType::kCurves:
return arena->make<HardwareCurveShader>(viewMatrix, color);
}
SkUNREACHABLE;
}

View File

@ -27,32 +27,43 @@ namespace {
// batch to render smoothly (i.e., NumTrianglesAtResolveLevel() * 3).
class MiddleOutShader : public GrPathTessellationShader {
public:
MiddleOutShader(const SkMatrix& viewMatrix, const SkPMColor4f& color)
MiddleOutShader(const SkMatrix& viewMatrix, const SkPMColor4f& color, PatchType patchType)
: GrPathTessellationShader(kTessellate_MiddleOutShader_ClassID,
GrPrimitiveType::kTriangles, 0, viewMatrix, color) {
constexpr static Attribute kInputPtsAttribs[] = {
{"inputPoints_0_1", kFloat4_GrVertexAttribType, kFloat4_GrSLType},
{"inputPoints_2_3", kFloat4_GrVertexAttribType, kFloat4_GrSLType}};
this->setInstanceAttributes(kInputPtsAttribs, 2);
GrPrimitiveType::kTriangles, 0, viewMatrix, color)
, fPatchType(patchType) {
fAttribs.emplace_back("inputPoints_0_1", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
fAttribs.emplace_back("inputPoints_2_3", kFloat4_GrVertexAttribType, kFloat4_GrSLType);
if (fPatchType == PatchType::kWedges) {
fAttribs.emplace_back("fanPoint", kFloat2_GrVertexAttribType, kFloat2_GrSLType);
}
this->setInstanceAttributes(fAttribs.data(), fAttribs.count());
SkASSERT(fAttribs.count() <= kMaxAttribCount);
}
private:
const char* name() const final { return "tessellate_MiddleOutShader"; }
void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const final {}
void getGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder* b) const final {
b->add32((uint32_t)fPatchType);
}
GrGLSLGeometryProcessor* createGLSLInstance(const GrShaderCaps&) const final;
const PatchType fPatchType;
constexpr static int kMaxAttribCount = 3;
SkSTArray<kMaxAttribCount, Attribute> fAttribs;
};
GrGLSLGeometryProcessor* MiddleOutShader::createGLSLInstance(const GrShaderCaps&) const {
class Impl : public GrPathTessellationShader::Impl {
void emitVertexCode(GrGLSLVertexBuilder* v, GrGPArgs* gpArgs) override {
void emitVertexCode(const GrPathTessellationShader& shader, GrGLSLVertexBuilder* v,
GrGPArgs* gpArgs) override {
v->defineConstant("PRECISION", GrTessellationPathRenderer::kLinearizationPrecision);
v->insertFunction(GrWangsFormula::as_sksl().c_str());
if (v->getProgramBuilder()->shaderCaps()->bitManipulationSupport()) {
// Determines the T value at which to place the given vertex in a "middle-out"
// topology.
v->insertFunction(R"(
float find_middle_out_T(float maxResolveLevel) {
int totalTriangleIdx = sk_VertexID/3 + 1;
float find_middle_out_T(int middleOutVertexID, float maxResolveLevel) {
int totalTriangleIdx = middleOutVertexID/3 + 1;
int resolveLevel = findMSB(totalTriangleIdx) + 1;
if (resolveLevel > int(maxResolveLevel)) {
// This vertex is at a higher resolve level than we need. Emit a degenerate
@ -61,15 +72,15 @@ GrGLSLGeometryProcessor* MiddleOutShader::createGLSLInstance(const GrShaderCaps&
}
int firstTriangleAtDepth = (1 << (resolveLevel - 1));
int triangleIdxWithinDepth = totalTriangleIdx - firstTriangleAtDepth;
int vertexIdxWithinDepth = triangleIdxWithinDepth * 2 + sk_VertexID % 3;
int vertexIdxWithinDepth = triangleIdxWithinDepth * 2 + middleOutVertexID % 3;
return ldexp(float(vertexIdxWithinDepth), -resolveLevel);
})");
} else {
// Determines the T value at which to place the given vertex in a "middle-out"
// topology.
v->insertFunction(R"(
float find_middle_out_T(float maxResolveLevel) {
float totalTriangleIdx = float(sk_VertexID/3) + 1;
float find_middle_out_T(int middleOutVertexID, float maxResolveLevel) {
float totalTriangleIdx = float(middleOutVertexID/3) + 1;
float resolveLevel = floor(log2(totalTriangleIdx)) + 1;
if (resolveLevel > maxResolveLevel) {
// This vertex is at a higher resolve level than we need. Emit a degenerate
@ -78,17 +89,32 @@ GrGLSLGeometryProcessor* MiddleOutShader::createGLSLInstance(const GrShaderCaps&
}
float firstTriangleAtDepth = exp2(resolveLevel - 1);
float triangleIdxWithinDepth = totalTriangleIdx - firstTriangleAtDepth;
float vertexIdxWithinDepth = triangleIdxWithinDepth*2 + float(sk_VertexID % 3);
float vertexIdxWithinDepth =
triangleIdxWithinDepth * 2 + float(middleOutVertexID % 3);
return vertexIdxWithinDepth * exp2(-resolveLevel);
})");
}
v->codeAppend(R"(
float2 localcoord;
int middleOutVertexID = sk_VertexID;
float2 localcoord;)");
if (shader.cast<MiddleOutShader>().fPatchType == PatchType::kWedges) {
v->codeAppend(R"(
// The first triangle is the fan triangle.
middleOutVertexID -= 3;
if (middleOutVertexID < 0) {
float2 endpoint = isinf(inputPoints_2_3.w) ? inputPoints_2_3.xy /*conic*/
: inputPoints_2_3.zw /*cubic*/;
localcoord = (sk_VertexID < 1) ? inputPoints_0_1.xy
: (sk_VertexID == 1) ? endpoint
: fanPoint;
} else)"); // Fall through to next if ().
}
v->codeAppend(R"(
if (isinf(inputPoints_2_3.z)) {
// A conic with w=Inf is an exact triangle.
localcoord = (sk_VertexID < 1) ? inputPoints_0_1.xy
: (sk_VertexID == 1) ? inputPoints_0_1.zw
: inputPoints_2_3.xy;
localcoord = (middleOutVertexID < 1) ? inputPoints_0_1.xy
: (middleOutVertexID == 1) ? inputPoints_0_1.zw
: inputPoints_2_3.xy;
} else {
float w = -1; // w < 0 tells us to treat the instance as an integral cubic.
float4x2 P = float4x2(inputPoints_0_1, inputPoints_2_3);
@ -104,7 +130,7 @@ GrGLSLGeometryProcessor* MiddleOutShader::createGLSLInstance(const GrShaderCaps&
// The patch is an integral cubic.
maxResolveLevel = wangs_formula_cubic_log2(PRECISION, P, AFFINE_MATRIX);
}
float T = find_middle_out_T(maxResolveLevel);
float T = find_middle_out_T(middleOutVertexID, maxResolveLevel);
if (0 < T && T < 1) {
// Evaluate at T. Use De Casteljau's for its accuracy and stability.
float2 ab = mix(P[0], P[1], T);
@ -134,7 +160,8 @@ GrGLSLGeometryProcessor* MiddleOutShader::createGLSLInstance(const GrShaderCaps&
} // namespace
GrPathTessellationShader* GrPathTessellationShader::MakeMiddleOutInstancedShader(
SkArenaAlloc* arena, const SkMatrix& viewMatrix, const SkPMColor4f& color) {
return arena->make<MiddleOutShader>(viewMatrix, color);
GrPathTessellationShader* GrPathTessellationShader::MakeMiddleOutFixedCountShader(
SkArenaAlloc* arena, const SkMatrix& viewMatrix, const SkPMColor4f& color,
PatchType patchType) {
return arena->make<MiddleOutShader>(viewMatrix, color, patchType);
}