Use constants for max parametric segments everywhere

There had been plumbing for variable amounts of segments throughout
the patch writers and shaders because HW tessellation had to limit it
based on what the GPU could handle in its tessellation pipeline.

Now that everything just uses the fixed count approach, we can keep it
all as constants. I considered letting it remain parameterized in the
event we wanted ganesh and graphite to diverge, but figured it was
better to have consistent handling of paths.

Bug: skia:13263, skia:13056
Change-Id: I351d5d762bdab47e4091b18efbd857ac1e500be4
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/534567
Commit-Queue: Michael Ludwig <michaelludwig@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
This commit is contained in:
Michael Ludwig 2022-05-05 09:38:08 -04:00 committed by SkCQ
parent 094bcdb9e5
commit 842383b985
9 changed files with 30 additions and 51 deletions

View File

@ -163,8 +163,7 @@ void PathCurveTessellator::prepareWithTriangles(
int patchPreallocCount = FixedCountCurves::PreallocCount(totalCombinedPathVerbCnt) +
(extraTriangles ? extraTriangles->count() : 0);
if (patchPreallocCount) {
CurveWriter writer{fAttribs, skgpu::kMaxParametricSegments,
target, &fVertexChunkArray, patchPreallocCount};
CurveWriter writer{fAttribs, target, &fVertexChunkArray, patchPreallocCount};
// Write out extra space-filling triangles to connect the curve patches with any external
// source of geometry (e.g. inner triangulation that handles winding explicitly).
@ -237,8 +236,7 @@ void PathWedgeTessellator::prepare(GrMeshDrawTarget* target,
const PathDrawList& pathDrawList,
int totalCombinedPathVerbCnt) {
if (int patchPreallocCount = FixedCountWedges::PreallocCount(totalCombinedPathVerbCnt)) {
WedgeWriter writer{fAttribs, skgpu::kMaxParametricSegments,
target, &fVertexChunkArray, patchPreallocCount};
WedgeWriter writer{fAttribs, target, &fVertexChunkArray, patchPreallocCount};
int resolveLevel = write_wedge_patches(std::move(writer), shaderMatrix, pathDrawList);
this->updateResolveLevel(resolveLevel);
}

View File

@ -172,8 +172,7 @@ void StrokeTessellateOp::prePrepareTessellator(GrTessellationShader::ProgramArgs
fPatchAttribs,
fViewMatrix,
this->headStroke(),
this->headColor(),
StrokeTessellator::kMaxParametricSegments_log2);
this->headColor());
auto fillStencil = &GrUserStencilSettings::kUnused;
if (fNeedsStencil) {

View File

@ -236,8 +236,7 @@ void StrokeTessellator::prepare(GrMeshDrawTarget* target,
PathStrokeList* pathStrokeList,
int totalCombinedStrokeVerbCnt) {
int preallocCount = FixedCountStrokes::PreallocCount(totalCombinedStrokeVerbCnt);
FixedCountStrokeWriter patchWriter{fAttribs, kMaxParametricSegments,
target, &fVertexChunkArray, preallocCount};
FixedCountStrokeWriter patchWriter{fAttribs, target, &fVertexChunkArray, preallocCount};
fFixedEdgeCount = write_fixed_count_patches(std::move(patchWriter),
shaderMatrix,

View File

@ -27,8 +27,6 @@ namespace skgpu::v1 {
// as degenerate triangles.
class StrokeTessellator {
public:
constexpr static int8_t kMaxParametricSegments_log2 =
SkNextLog2_portable(kMaxParametricSegments);
struct PathStrokeList {
PathStrokeList(const SkPath& path, const SkStrokeRec& stroke, const SkPMColor4f& color)

View File

@ -86,13 +86,11 @@ GrStrokeTessellationShader::GrStrokeTessellationShader(const GrShaderCaps& shade
PatchAttribs attribs,
const SkMatrix& viewMatrix,
const SkStrokeRec& stroke,
SkPMColor4f color,
int8_t maxParametricSegments_log2)
SkPMColor4f color)
: GrTessellationShader(kTessellate_GrStrokeTessellationShader_ClassID,
GrPrimitiveType::kTriangleStrip, viewMatrix, color)
, fPatchAttribs(attribs | PatchAttribs::kJoinControlPoint)
, fStroke(stroke)
, fMaxParametricSegments_log2(maxParametricSegments_log2) {
, fStroke(stroke) {
// We should use explicit curve type when, and only when, there isn't infinity support.
// Otherwise the GPU can infer curve type based on infinity.
SkASSERT(shaderCaps.infinitySupport() != (attribs & PatchAttribs::kExplicitCurveType));
@ -606,7 +604,7 @@ void GrStrokeTessellationShader::Impl::emitTessellationCode(
// ensures crack-free seaming between instances.
tangent = (combinedEdgeID == 0) ? tan0 : tan1;
strokeCoord = (combinedEdgeID == 0) ? p0 : p3;
})", shader.maxParametricSegments_log2() /* Parametric/radial sort loop count. */);
})", skgpu::kMaxFixedResolveLevel /* Parametric/radial sort loop count. */);
code->append(R"(
// At this point 'tangent' is normalized, so the orthogonal vector is also normalized.
@ -690,7 +688,6 @@ void GrStrokeTessellationShader::addToKey(const GrShaderCaps&, skgpu::KeyBuilder
uint32_t key = (uint32_t)(fPatchAttribs & ~PatchAttribs::kColor);
key = (key << 2) | ((keyNeedsJoin) ? fStroke.getJoin() : 0);
key = (key << 1) | (uint32_t)fStroke.isHairlineStyle();
key = (key << 8) | fMaxParametricSegments_log2;
b->add32(key);
}

View File

@ -29,14 +29,13 @@ public:
// 'viewMatrix' is applied to the geometry post tessellation. It cannot have perspective.
GrStrokeTessellationShader(const GrShaderCaps&, PatchAttribs, const SkMatrix& viewMatrix,
const SkStrokeRec&, SkPMColor4f, int8_t maxParametricSegments_log2);
const SkStrokeRec&, SkPMColor4f);
PatchAttribs attribs() const { return fPatchAttribs; }
bool hasDynamicStroke() const { return fPatchAttribs & PatchAttribs::kStrokeParams; }
bool hasDynamicColor() const { return fPatchAttribs & PatchAttribs::kColor; }
bool hasExplicitCurveType() const { return fPatchAttribs & PatchAttribs::kExplicitCurveType; }
const SkStrokeRec& stroke() const { return fStroke;}
int8_t maxParametricSegments_log2() const { return fMaxParametricSegments_log2; }
private:
const char* name() const override { return "GrStrokeTessellationShader"; }
@ -45,7 +44,6 @@ private:
const PatchAttribs fPatchAttribs;
const SkStrokeRec fStroke;
const int8_t fMaxParametricSegments_log2;
constexpr static int kMaxAttribCount = 6;
SkSTArray<kMaxAttribCount, Attribute> fAttribs;

View File

@ -95,8 +95,7 @@ void TessellateCurvesRenderStep::writeVertices(DrawWriter* dw, const DrawGeometr
FixedCountCurves::IndexBufferSize);
int patchReserveCount = FixedCountCurves::PreallocCount(path.countVerbs());
Writer writer{kAttribs, kMaxParametricSegments,
*dw, fixedVertexBuffer, fixedIndexBuffer, patchReserveCount};
Writer writer{kAttribs, *dw, fixedVertexBuffer, fixedIndexBuffer, patchReserveCount};
writer.updatePaintDepthAttrib(geom.order().depthAsFloat());

View File

@ -99,8 +99,7 @@ void TessellateWedgesRenderStep::writeVertices(DrawWriter* dw, const DrawGeometr
FixedCountWedges::IndexBufferSize);
int patchReserveCount = FixedCountWedges::PreallocCount(path.countVerbs());
Writer writer{kAttribs, kMaxParametricSegments,
*dw, fixedVertexBuffer, fixedIndexBuffer, patchReserveCount};
Writer writer{kAttribs, *dw, fixedVertexBuffer, fixedIndexBuffer, patchReserveCount};
writer.updatePaintDepthAttrib(geom.order().depthAsFloat());
// TODO: Is it better to pre-transform on the CPU and only have a matrix uniform to compute

View File

@ -238,11 +238,8 @@ class PatchWriter {
public:
template <typename... Args> // forwarded to PatchAllocator
PatchWriter(PatchAttribs attribs,
int maxTessellationSegments,
Args&&... allocArgs)
: fAttribs(attribs)
, fMaxSegments_pow2(pow2(maxTessellationSegments))
, fMaxSegments_pow4(pow2(fMaxSegments_pow2))
, fCurrMinSegments_pow4(1.f)
, fPatchAllocator(PatchStride(attribs), std::forward<Args>(allocArgs)...)
, fJoin(attribs)
@ -350,9 +347,8 @@ public:
// Write a cubic curve with its four control points.
AI void writeCubic(float2 p0, float2 p1, float2 p2, float2 p3,
const VectorXform& shaderXform,
float precision = kTessellationPrecision) {
float n4 = wangs_formula::cubic_pow4(precision, p0, p1, p2, p3, shaderXform);
const VectorXform& shaderXform) {
float n4 = wangs_formula::cubic_pow4(kTessellationPrecision, p0, p1, p2, p3, shaderXform);
if constexpr (kDiscardFlatCurves) {
if (n4 <= 1.f) {
// This cubic only needs one segment (e.g. a line) but we're not filling space with
@ -364,24 +360,23 @@ public:
this->writeCubicPatch(p0, p1, p2, p3);
} else {
int numPatches = SkScalarCeilToInt(wangs_formula::root4(
std::min(n4, pow4(kMaxTessellationSegmentsPerCurve)) / fMaxSegments_pow4));
std::min(n4, pow4(kMaxTessellationSegmentsPerCurve)) /
pow4(kMaxParametricSegments)));
this->chopAndWriteCubics(p0, p1, p2, p3, numPatches);
}
}
AI void writeCubic(const SkPoint pts[4],
const VectorXform& shaderXform,
float precision = kTessellationPrecision) {
const VectorXform& shaderXform) {
float4 p0p1 = float4::Load(pts);
float4 p2p3 = float4::Load(pts + 2);
this->writeCubic(p0p1.lo, p0p1.hi, p2p3.lo, p2p3.hi, shaderXform, precision);
this->writeCubic(p0p1.lo, p0p1.hi, p2p3.lo, p2p3.hi, shaderXform);
}
// Write a conic curve with three control points and 'w', with the last coord of the last
// control point signaling a conic by being set to infinity.
AI void writeConic(float2 p0, float2 p1, float2 p2, float w,
const VectorXform& shaderXform,
float precision = kTessellationPrecision) {
float n2 = wangs_formula::conic_pow2(precision, p0, p1, p2, w, shaderXform);
const VectorXform& shaderXform) {
float n2 = wangs_formula::conic_pow2(kTessellationPrecision, p0, p1, p2, w, shaderXform);
if constexpr (kDiscardFlatCurves) {
if (n2 <= 1.f) {
// This conic only needs one segment (e.g. a line) but we're not filling space with
@ -393,25 +388,24 @@ public:
this->writeConicPatch(p0, p1, p2, w);
} else {
int numPatches = SkScalarCeilToInt(sqrtf(
std::min(n2, pow2(kMaxTessellationSegmentsPerCurve)) / fMaxSegments_pow2));
std::min(n2, pow2(kMaxTessellationSegmentsPerCurve)) /
pow2(kMaxParametricSegments)));
this->chopAndWriteConics(p0, p1, p2, w, numPatches);
}
}
AI void writeConic(const SkPoint pts[3], float w,
const VectorXform& shaderXform,
float precision = kTessellationPrecision) {
const VectorXform& shaderXform) {
this->writeConic(skvx::bit_pun<float2>(pts[0]),
skvx::bit_pun<float2>(pts[1]),
skvx::bit_pun<float2>(pts[2]),
w, shaderXform, precision);
w, shaderXform);
}
// Write a quadratic curve that automatically converts its three control points into an
// equivalent cubic.
AI void writeQuadratic(float2 p0, float2 p1, float2 p2,
const VectorXform& shaderXform,
float precision = kTessellationPrecision) {
float n4 = wangs_formula::quadratic_pow4(precision, p0, p1, p2, shaderXform);
const VectorXform& shaderXform) {
float n4 = wangs_formula::quadratic_pow4(kTessellationPrecision, p0, p1, p2, shaderXform);
if constexpr (kDiscardFlatCurves) {
if (n4 <= 1.f) {
// This quad only needs one segment (e.g. a line) but we're not filling space with
@ -423,17 +417,17 @@ public:
this->writeQuadPatch(p0, p1, p2);
} else {
int numPatches = SkScalarCeilToInt(wangs_formula::root4(
std::min(n4, pow4(kMaxTessellationSegmentsPerCurve)) / fMaxSegments_pow4));
std::min(n4, pow4(kMaxTessellationSegmentsPerCurve)) /
pow4(kMaxParametricSegments)));
this->chopAndWriteQuads(p0, p1, p2, numPatches);
}
}
AI void writeQuadratic(const SkPoint pts[3],
const VectorXform& shaderXform,
float precision = kTessellationPrecision) {
const VectorXform& shaderXform) {
this->writeQuadratic(skvx::bit_pun<float2>(pts[0]),
skvx::bit_pun<float2>(pts[1]),
skvx::bit_pun<float2>(pts[2]),
shaderXform, precision);
shaderXform);
}
// Write a line that is automatically converted into an equivalent cubic.
@ -547,11 +541,11 @@ private:
// Returns true if curve can be written w/o needing to chop (e.g. represented by one instance)
bool curveFitsInMaxSegments(float n4) {
if (n4 <= fMaxSegments_pow4) {
if (n4 <= pow4(kMaxParametricSegments)) {
fCurrMinSegments_pow4 = std::max(n4, fCurrMinSegments_pow4);
return true;
} else {
fCurrMinSegments_pow4 = fMaxSegments_pow4;
fCurrMinSegments_pow4 = pow4(kMaxParametricSegments);
return false;
}
}
@ -695,8 +689,6 @@ private:
// attribs enabled (e.g. depending on caps or batching).
const PatchAttribs fAttribs;
const float fMaxSegments_pow2;
const float fMaxSegments_pow4;
float fCurrMinSegments_pow4;
PatchAllocator fPatchAllocator;