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:
parent
ec10dedc3a
commit
d2b8ba3369
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user