Track min required segments in PatchWriter
Also handles automatically chopping curves to reach the maximum segments allowed for the PatchWriter. Change-Id: Iba817e817dd270d170d305c1256cf9aec7b803d2 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/502056 Reviewed-by: Christopher Dalton <csmartdalton@google.com> Commit-Queue: Michael Ludwig <michaelludwig@google.com>
This commit is contained in:
parent
43539c22a2
commit
220702dac0
@ -134,7 +134,10 @@ private:
|
||||
if (needsInnerFan) {
|
||||
patchPreallocCount += fPath.countVerbs() - 1;
|
||||
}
|
||||
PatchWriter patchWriter(flushState, fTessellator, patchPreallocCount);
|
||||
PatchWriter patchWriter(flushState,
|
||||
fTessellator,
|
||||
tessShader->maxTessellationSegments(*caps.shaderCaps()),
|
||||
patchPreallocCount);
|
||||
|
||||
if (needsInnerFan) {
|
||||
// Write out inner fan triangles.
|
||||
@ -149,10 +152,7 @@ private:
|
||||
}
|
||||
|
||||
// Write out the curves.
|
||||
fTessellator->writePatches(patchWriter,
|
||||
tessShader->maxTessellationSegments(*caps.shaderCaps()),
|
||||
shaderMatrix,
|
||||
{pathMatrix, fPath, kCyan});
|
||||
fTessellator->writePatches(patchWriter, shaderMatrix, {pathMatrix, fPath, kCyan});
|
||||
|
||||
if (!tessShader->willUseTessellationShaders()) {
|
||||
fTessellator->prepareFixedCountBuffers(flushState);
|
||||
|
@ -423,7 +423,9 @@ void PathInnerTriangulateOp::onPrepare(GrOpFlushState* flushState) {
|
||||
fTessellator->patchPreallocCount(fPath.countVerbs());
|
||||
SkASSERT(patchPreallocCount); // Otherwise fTessellator should be null.
|
||||
|
||||
PatchWriter patchWriter(flushState, fTessellator, patchPreallocCount);
|
||||
auto tessShader = &fStencilCurvesProgram->geomProc().cast<GrPathTessellationShader>();
|
||||
int maxSegments = tessShader->maxTessellationSegments(*caps.shaderCaps());
|
||||
PatchWriter patchWriter(flushState, fTessellator, maxSegments, patchPreallocCount);
|
||||
|
||||
// Write out breadcrumb triangles. This must be called after polysToTriangles() in order for
|
||||
// fFanBreadcrumbs to be complete.
|
||||
@ -446,9 +448,7 @@ void PathInnerTriangulateOp::onPrepare(GrOpFlushState* flushState) {
|
||||
SkASSERT(breadcrumbCount == fFanBreadcrumbs.count());
|
||||
|
||||
// Write out the curves.
|
||||
auto tessShader = &fStencilCurvesProgram->geomProc().cast<GrPathTessellationShader>();
|
||||
fTessellator->writePatches(patchWriter,
|
||||
tessShader->maxTessellationSegments(*caps.shaderCaps()),
|
||||
tessShader->viewMatrix(),
|
||||
{SkMatrix::I(), fPath, SK_PMColor4fTRANSPARENT});
|
||||
|
||||
|
@ -28,20 +28,24 @@ void write_triangle_stack(PatchWriter* writer,
|
||||
#if SK_GPU_V1
|
||||
PatchWriter::PatchWriter(GrMeshDrawTarget* target,
|
||||
PathTessellator* tessellator,
|
||||
int maxTessellationSegments,
|
||||
int initialPatchAllocCount)
|
||||
: PatchWriter(target,
|
||||
&tessellator->fVertexChunkArray,
|
||||
tessellator->fAttribs,
|
||||
maxTessellationSegments,
|
||||
sizeof(SkPoint) * 4 + PatchAttribsStride(tessellator->fAttribs),
|
||||
initialPatchAllocCount) {
|
||||
}
|
||||
|
||||
PatchWriter::PatchWriter(GrMeshDrawTarget* target,
|
||||
StrokeTessellator* tessellator,
|
||||
int maxTessellationSegments,
|
||||
int initialPatchAllocCount)
|
||||
: PatchWriter(target,
|
||||
&tessellator->fVertexChunkArray,
|
||||
tessellator->fAttribs,
|
||||
maxTessellationSegments,
|
||||
sizeof(SkPoint) * 4 + PatchAttribsStride(tessellator->fAttribs),
|
||||
initialPatchAllocCount) {
|
||||
}
|
||||
@ -60,11 +64,11 @@ void PatchWriter::chopAndWriteQuads(float2 p0, float2 p1, float2 p2, int numPatc
|
||||
// p1 & p2 of the cubic representation of the middle quad.
|
||||
float4 middle = mix(ab, bc, mix(T, T.zwxy(), 2/3.f));
|
||||
|
||||
this->writeQuadratic(p0, ab.lo, abc.lo); // Write the 1st quad.
|
||||
this->writeQuadPatch(p0, ab.lo, abc.lo); // Write the 1st quad.
|
||||
if (needsInnerTriangles) {
|
||||
this->writeTriangle(p0, abc.lo, abc.hi);
|
||||
}
|
||||
this->writeCubic(abc.lo, middle, abc.hi); // Write the 2nd quad already converted to a cubic
|
||||
this->writeCubicPatch(abc.lo, middle, abc.hi); // Write the 2nd quad (as a cubic already)
|
||||
if (needsInnerTriangles) {
|
||||
write_triangle_stack(this, innerTriangulator.pushVertex(to_skpoint(abc.hi)));
|
||||
}
|
||||
@ -76,14 +80,14 @@ void PatchWriter::chopAndWriteQuads(float2 p0, float2 p1, float2 p2, int numPatc
|
||||
float2 bc = (p1 + p2) * .5f;
|
||||
float2 abc = (ab + bc) * .5f;
|
||||
|
||||
this->writeQuadratic(p0, ab, abc); // Write the 1st quad.
|
||||
this->writeQuadPatch(p0, ab, abc); // Write the 1st quad.
|
||||
if (needsInnerTriangles) {
|
||||
this->writeTriangle(p0, abc, p2);
|
||||
}
|
||||
this->writeQuadratic(abc, bc, p2); // Write the 2nd quad.
|
||||
this->writeQuadPatch(abc, bc, p2); // Write the 2nd quad.
|
||||
} else {
|
||||
SkASSERT(numPatches == 1);
|
||||
this->writeQuadratic(p0, p1, p2); // Write the single remaining quad.
|
||||
this->writeQuadPatch(p0, p1, p2); // Write the single remaining quad.
|
||||
}
|
||||
if (needsInnerTriangles) {
|
||||
write_triangle_stack(this, innerTriangulator.pushVertex(to_skpoint(p2)));
|
||||
@ -108,10 +112,10 @@ void PatchWriter::chopAndWriteConics(float2 p0, float2 p1, float2 p2, float w, i
|
||||
|
||||
// Project and write the 1st conic.
|
||||
float2 midpoint = abc.xy() / abc.w();
|
||||
this->writeConic(h0.xy() / h0.w(),
|
||||
ab.xy() / ab.w(),
|
||||
midpoint,
|
||||
ab.w() / sqrtf(h0.w() * abc.w()));
|
||||
this->writeConicPatch(h0.xy() / h0.w(),
|
||||
ab.xy() / ab.w(),
|
||||
midpoint,
|
||||
ab.w() / sqrtf(h0.w() * abc.w()));
|
||||
if (needsInnerTriangles) {
|
||||
write_triangle_stack(this, innerTriangulator.pushVertex(to_skpoint(midpoint)));
|
||||
}
|
||||
@ -119,10 +123,10 @@ void PatchWriter::chopAndWriteConics(float2 p0, float2 p1, float2 p2, float w, i
|
||||
}
|
||||
// Project and write the remaining conic.
|
||||
SkASSERT(numPatches == 1);
|
||||
this->writeConic(h0.xy() / h0.w(),
|
||||
h1.xy() / h1.w(),
|
||||
h2.xy(), // h2.w == 1
|
||||
h1.w() / sqrtf(h0.w()));
|
||||
this->writeConicPatch(h0.xy() / h0.w(),
|
||||
h1.xy() / h1.w(),
|
||||
h2.xy(), // h2.w == 1
|
||||
h1.w() / sqrtf(h0.w()));
|
||||
if (needsInnerTriangles) {
|
||||
write_triangle_stack(this, innerTriangulator.pushVertex(to_skpoint(h2.xy())));
|
||||
write_triangle_stack(this, innerTriangulator.close());
|
||||
@ -144,11 +148,11 @@ void PatchWriter::chopAndWriteCubics(float2 p0, float2 p1, float2 p2, float2 p3,
|
||||
float4 abcd = mix(abc, bcd, T);
|
||||
float4 middle = mix(abc, bcd, T.zwxy()); // p1 & p2 of the middle cubic.
|
||||
|
||||
this->writeCubic(p0, ab.lo, abc.lo, abcd.lo); // Write the 1st cubic.
|
||||
this->writeCubicPatch(p0, ab.lo, abc.lo, abcd.lo); // Write the 1st cubic.
|
||||
if (needsInnerTriangles) {
|
||||
this->writeTriangle(p0, abcd.lo, abcd.hi);
|
||||
}
|
||||
this->writeCubic(abcd.lo, middle, abcd.hi); // Write the 2nd cubic.
|
||||
this->writeCubicPatch(abcd.lo, middle, abcd.hi); // Write the 2nd cubic.
|
||||
if (needsInnerTriangles) {
|
||||
write_triangle_stack(this, innerTriangulator.pushVertex(to_skpoint(abcd.hi)));
|
||||
}
|
||||
@ -163,14 +167,14 @@ void PatchWriter::chopAndWriteCubics(float2 p0, float2 p1, float2 p2, float2 p3,
|
||||
float2 bcd = (bc + cd) * .5f;
|
||||
float2 abcd = (abc + bcd) * .5f;
|
||||
|
||||
this->writeCubic(p0, ab, abc, abcd); // Write the 1st cubic.
|
||||
this->writeCubicPatch(p0, ab, abc, abcd); // Write the 1st cubic.
|
||||
if (needsInnerTriangles) {
|
||||
this->writeTriangle(p0, abcd, p3);
|
||||
}
|
||||
this->writeCubic(abcd, bcd, cd, p3); // Write the 2nd cubic.
|
||||
this->writeCubicPatch(abcd, bcd, cd, p3); // Write the 2nd cubic.
|
||||
} else {
|
||||
SkASSERT(numPatches == 1);
|
||||
this->writeCubic(p0, p1, p2, p3); // Write the single remaining cubic.
|
||||
this->writeCubicPatch(p0, p1, p2, p3); // Write the single remaining cubic.
|
||||
}
|
||||
if (needsInnerTriangles) {
|
||||
write_triangle_stack(this, innerTriangulator.pushVertex(to_skpoint(p3)));
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "include/private/SkColorData.h"
|
||||
#include "src/gpu/GrVertexChunkArray.h"
|
||||
#include "src/gpu/tessellate/Tessellation.h"
|
||||
#include "src/gpu/tessellate/WangsFormula.h"
|
||||
|
||||
#define AI SK_ALWAYS_INLINE
|
||||
|
||||
@ -27,16 +28,27 @@ public:
|
||||
PatchWriter(GrMeshDrawTarget* target,
|
||||
GrVertexChunkArray* vertexChunkArray,
|
||||
PatchAttribs attribs,
|
||||
int maxTessellationSegments,
|
||||
size_t patchStride,
|
||||
int initialAllocCount)
|
||||
: fAttribs(attribs)
|
||||
, fChunker(target, vertexChunkArray, patchStride, initialAllocCount) {}
|
||||
, fMaxSegments_pow2(pow2(maxTessellationSegments))
|
||||
, fMaxSegments_pow4(pow2(fMaxSegments_pow2))
|
||||
, fChunker(target, vertexChunkArray, patchStride, initialAllocCount) {
|
||||
// For fans or strokes, the minimum required segment count is 1 (making either a triangle
|
||||
// with the fan point, or a stroked line). Otherwise, we need 2 segments to represent
|
||||
// triangles purely from the tessellated vertices.
|
||||
fCurrMinSegments_pow4 = (attribs & PatchAttribs::kFanPoint ||
|
||||
attribs & PatchAttribs::kJoinControlPoint) ? 1.f : 16.f; // 2^4
|
||||
}
|
||||
|
||||
#if SK_GPU_V1
|
||||
// Create PatchWriters that write directly to the GrVertexChunkArrays stored on the provided
|
||||
// tessellators.
|
||||
PatchWriter(GrMeshDrawTarget*, PathTessellator*, int initialPatchAllocCount);
|
||||
PatchWriter(GrMeshDrawTarget*, StrokeTessellator*, int initialPatchAllocCount);
|
||||
PatchWriter(GrMeshDrawTarget*, PathTessellator*,
|
||||
int maxTessellationSegments, int initialPatchAllocCount);
|
||||
PatchWriter(GrMeshDrawTarget*, StrokeTessellator*,
|
||||
int maxTessellationSegments, int initialPatchAllocCount);
|
||||
#endif
|
||||
|
||||
~PatchWriter() {
|
||||
@ -46,6 +58,15 @@ public:
|
||||
|
||||
PatchAttribs attribs() const { return fAttribs; }
|
||||
|
||||
// Fast log2 of minimum required # of segments per tracked Wang's formula calculations.
|
||||
int requiredResolveLevel() const {
|
||||
return wangs_formula::nextlog16(fCurrMinSegments_pow4); // log16(n^4) == log2(n)
|
||||
}
|
||||
// Fast minimum required # of segments from tracked Wang's formula calculations.
|
||||
int requiredFixedSegments() const {
|
||||
return SkScalarCeilToInt(wangs_formula::root4(fCurrMinSegments_pow4));
|
||||
}
|
||||
|
||||
// Updates the stroke's join control point that will be written out with each patch. This is
|
||||
// automatically adjusted when appending various geometries (e.g. Conic/Cubic), but sometimes
|
||||
// must be set explicitly.
|
||||
@ -117,52 +138,62 @@ public:
|
||||
*/
|
||||
|
||||
// Write a cubic curve with its four control points.
|
||||
AI void writeCubic(float2 p0, float2 p1, float2 p2, float2 p3) {
|
||||
// TODO: Have cubic store or automatically compute wang's formula so this can automatically
|
||||
// call into chopAndWriteCubics.
|
||||
this->writePatch(p0, p1, p2, p3, kCubicCurveType);
|
||||
AI void writeCubic(float2 p0, float2 p1, float2 p2, float2 p3, float n4) {
|
||||
if (this->updateRequiredSegments(n4)) {
|
||||
this->writeCubicPatch(p0, p1, p2, p3);
|
||||
} else {
|
||||
int numPatches = SkScalarCeilToInt(wangs_formula::root4(
|
||||
std::min(n4, pow4(kMaxTessellationSegmentsPerCurve)) / fMaxSegments_pow4));
|
||||
this->chopAndWriteCubics(p0, p1, p2, p3, numPatches);
|
||||
}
|
||||
}
|
||||
AI void writeCubic(float4 p0p1, float4 p2p3) {
|
||||
this->writeCubic(p0p1.lo, p0p1.hi, p2p3.lo, p2p3.hi);
|
||||
}
|
||||
AI void writeCubic(float2 p0, float4 p1p2, float2 p3) {
|
||||
this->writeCubic(p0, p1p2.lo, p1p2.hi, p3);
|
||||
}
|
||||
AI void writeCubic(const SkPoint pts[4]) {
|
||||
this->writeCubic(float4::Load(pts), float4::Load(pts + 2));
|
||||
AI void writeCubic(const SkPoint pts[4], float n4) {
|
||||
float4 p0p1 = float4::Load(pts);
|
||||
float4 p2p3 = float4::Load(pts + 2);
|
||||
this->writeCubic(p0p1.lo, p0p1.hi, p2p3.lo, p2p3.hi, n4);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// TODO: Have Conic store or automatically compute Wang's formula so this can automatically
|
||||
// call into chopAndWriteConics.
|
||||
this->writePatch(p0, p1, p2, {w, SK_FloatInfinity}, kConicCurveType);
|
||||
AI void writeConic(float2 p0, float2 p1, float2 p2, float w, float n2) {
|
||||
if (this->updateRequiredSegments(n2*n2)) {
|
||||
this->writeConicPatch(p0, p1, p2, w);
|
||||
} else {
|
||||
int numPatches = SkScalarCeilToInt(sqrtf(
|
||||
std::min(n2, pow2(kMaxTessellationSegmentsPerCurve)) / fMaxSegments_pow2));
|
||||
this->chopAndWriteConics(p0, p1, p2, w, numPatches);
|
||||
}
|
||||
}
|
||||
AI void writeConic(const SkPoint pts[3], float w) {
|
||||
AI void writeConic(const SkPoint pts[3], float w, float n2) {
|
||||
this->writeConic(skvx::bit_pun<float2>(pts[0]),
|
||||
skvx::bit_pun<float2>(pts[1]),
|
||||
skvx::bit_pun<float2>(pts[2]),
|
||||
w);
|
||||
w, n2);
|
||||
}
|
||||
|
||||
// Write a quadratic curve that automatically converts its three control points into an
|
||||
// equivalent cubic.
|
||||
AI void writeQuadratic(float2 p0, float2 p1, float2 p2) {
|
||||
// TODO: Have Quadratic store or automatically compute Wang's formula so this can
|
||||
// automatically call into chopAndWriteQuadratics *before* it is converted to an equivalent
|
||||
// cubic if needed.
|
||||
this->writeCubic(p0, mix(float4(p0, p2), p1.xyxy(), 2/3.f), p2);
|
||||
AI void writeQuadratic(float2 p0, float2 p1, float2 p2, float n4) {
|
||||
if (this->updateRequiredSegments(n4)) {
|
||||
this->writeQuadPatch(p0, p1, p2);
|
||||
} else {
|
||||
int numPatches = SkScalarCeilToInt(wangs_formula::root4(
|
||||
std::min(n4, pow4(kMaxTessellationSegmentsPerCurve)) / fMaxSegments_pow4));
|
||||
this->chopAndWriteQuads(p0, p1, p2, numPatches);
|
||||
}
|
||||
}
|
||||
AI void writeQuadratic(const SkPoint pts[3]) {
|
||||
AI void writeQuadratic(const SkPoint pts[3], float n4) {
|
||||
this->writeQuadratic(skvx::bit_pun<float2>(pts[0]),
|
||||
skvx::bit_pun<float2>(pts[1]),
|
||||
skvx::bit_pun<float2>(pts[2]));
|
||||
skvx::bit_pun<float2>(pts[2]),
|
||||
n4);
|
||||
}
|
||||
|
||||
// Write a line that is automatically converted into an equivalent cubic.
|
||||
AI void writeLine(float4 p0p1) {
|
||||
this->writeCubic(p0p1.lo, (p0p1.zwxy() - p0p1) * (1/3.f) + p0p1, p0p1.hi);
|
||||
// No chopping needed, and should have been reset to 1 segment if using writeLine
|
||||
SkASSERT(fCurrMinSegments_pow4 >= 1.f);
|
||||
this->writeCubicPatch(p0p1.lo, (p0p1.zwxy() - p0p1) * (1/3.f) + p0p1, p0p1.hi);
|
||||
}
|
||||
AI void writeLine(float2 p0, float2 p1) { this->writeLine({p0, p1}); }
|
||||
AI void writeLine(SkPoint p0, SkPoint p1) {
|
||||
@ -172,6 +203,8 @@ public:
|
||||
// Write a triangle by setting it to a conic with w=Inf, and using a distinct
|
||||
// explicit curve type for when inf isn't supported in shaders.
|
||||
AI void writeTriangle(float2 p0, float2 p1, float2 p2) {
|
||||
// No chopping needed, and should have been reset to 2 segments if using writeTriangle.
|
||||
SkASSERT(fCurrMinSegments_pow4 >= (2*2*2*2));
|
||||
this->writePatch(p0, p1, p2, {SK_FloatInfinity, SK_FloatInfinity},
|
||||
kTriangularConicCurveType);
|
||||
}
|
||||
@ -193,31 +226,13 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Chops the given quadratic into 'numPatches' equal segments (in the parametric sense) and
|
||||
// writes them to the GPU buffer.
|
||||
//
|
||||
// Fills space between chops with triangles if PathPatchAttrib::kFanPoint is not enabled.
|
||||
void chopAndWriteQuads(float2 p0, float2 p1, float2 p2, int numPatches);
|
||||
|
||||
// Chops the given conic into 'numPatches' equal segments (in the parametric sense) and
|
||||
// writes them to the GPU buffer.
|
||||
//
|
||||
// Fills space between chops with triangles if PathPatchAttrib::kFanPoint is not enabled.
|
||||
void chopAndWriteConics(float2 p0, float2 p1, float2 p2, float w, int numPatches);
|
||||
|
||||
// Chops the given cubic into 'numPatches' equal segments (in the parametric sense) and
|
||||
// writes them to the GPU buffer.
|
||||
//
|
||||
// Fills space between chops with triangles if PathPatchAttrib::kFanPoint is not enabled.
|
||||
void chopAndWriteCubics(float2 p0, float2 p1, float2 p2, float2 p3, int numPatches);
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
static VertexWriter::Conditional<T> If(bool c, const T& v) { return VertexWriter::If(c,v); }
|
||||
|
||||
void emitPatchAttribs(VertexWriter vertexWriter,
|
||||
SkPoint joinControlPoint,
|
||||
float explicitCurveType) {
|
||||
AI void emitPatchAttribs(VertexWriter vertexWriter,
|
||||
SkPoint joinControlPoint,
|
||||
float explicitCurveType) {
|
||||
vertexWriter << If((fAttribs & PatchAttribs::kJoinControlPoint), joinControlPoint)
|
||||
<< If((fAttribs & PatchAttribs::kFanPoint), fFanPointAttrib)
|
||||
<< If((fAttribs & PatchAttribs::kStrokeParams), fStrokeParamsAttrib)
|
||||
@ -225,8 +240,7 @@ private:
|
||||
<< If((fAttribs & PatchAttribs::kExplicitCurveType), explicitCurveType);
|
||||
}
|
||||
|
||||
SK_ALWAYS_INLINE
|
||||
void writePatch(float2 p0, float2 p1, float2 p2, float2 p3, float explicitCurveType) {
|
||||
AI void writePatch(float2 p0, float2 p1, float2 p2, float2 p3, float explicitCurveType) {
|
||||
const bool defer = (fAttribs & PatchAttribs::kJoinControlPoint) &&
|
||||
!fHasJoinControlPoint;
|
||||
|
||||
@ -259,8 +273,44 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
// Helpers that normalize curves to a generic patch, but does no other work.
|
||||
AI void writeCubicPatch(float2 p0, float2 p1, float2 p2, float2 p3) {
|
||||
this->writePatch(p0, p1, p2, p3, kCubicCurveType);
|
||||
}
|
||||
AI void writeCubicPatch(float2 p0, float4 p1p2, float2 p3) {
|
||||
this->writeCubicPatch(p0, p1p2.lo, p1p2.hi, p3);
|
||||
}
|
||||
AI void writeQuadPatch(float2 p0, float2 p1, float2 p2) {
|
||||
this->writeCubicPatch(p0, mix(float4(p0, p2), p1.xyxy(), 2/3.f), p2);
|
||||
}
|
||||
AI void writeConicPatch(float2 p0, float2 p1, float2 p2, float w) {
|
||||
this->writePatch(p0, p1, p2, {w, SK_FloatInfinity}, kConicCurveType);
|
||||
}
|
||||
|
||||
// Helpers that chop the curve type into 'numPatches' parametrically uniform curves. It is
|
||||
// assumed that 'numPatches' is calculated such that the resulting curves require the maximum
|
||||
// number of segments to draw appropriately (since the original presumably needed even more).
|
||||
void chopAndWriteQuads(float2 p0, float2 p1, float2 p2, int numPatches);
|
||||
void chopAndWriteConics(float2 p0, float2 p1, float2 p2, float w, int numPatches);
|
||||
void chopAndWriteCubics(float2 p0, float2 p1, float2 p2, float2 p3, int numPatches);
|
||||
|
||||
// Returns true if curve can be written w/o needing to chop
|
||||
bool updateRequiredSegments(float n4) {
|
||||
if (n4 <= fMaxSegments_pow4) {
|
||||
fCurrMinSegments_pow4 = std::max(n4, fCurrMinSegments_pow4);
|
||||
return true;
|
||||
} else {
|
||||
fCurrMinSegments_pow4 = fMaxSegments_pow4;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const PatchAttribs fAttribs;
|
||||
|
||||
const float fMaxSegments_pow2;
|
||||
const float fMaxSegments_pow4;
|
||||
float fCurrMinSegments_pow4;
|
||||
|
||||
GrVertexChunkBuilder fChunker;
|
||||
|
||||
SkPoint fJoinControlPointAttrib;
|
||||
@ -268,8 +318,6 @@ private:
|
||||
StrokeParams fStrokeParamsAttrib;
|
||||
VertexColor fColorAttrib;
|
||||
|
||||
bool fHasJoinControlPoint = false;
|
||||
|
||||
static constexpr size_t kMaxStride =
|
||||
4 * sizeof(SkPoint) + // control points
|
||||
sizeof(SkPoint) + // join control point or fan attrib point (not used at same time)
|
||||
@ -281,6 +329,8 @@ private:
|
||||
// Contents are valid (sans join control point) if fHasDeferredPatch is true.
|
||||
char fDeferredPatchStorage[kMaxStride];
|
||||
bool fHasDeferredPatch = false;
|
||||
|
||||
bool fHasJoinControlPoint = false;
|
||||
};
|
||||
|
||||
} // namespace skgpu
|
||||
|
@ -29,16 +29,8 @@ int PathCurveTessellator::patchPreallocCount(int totalCombinedPathVerbCnt) const
|
||||
}
|
||||
|
||||
void PathCurveTessellator::writePatches(PatchWriter& patchWriter,
|
||||
int maxTessellationSegments,
|
||||
const SkMatrix& shaderMatrix,
|
||||
const PathDrawList& pathDrawList) {
|
||||
float maxSegments_pow2 = pow2(maxTessellationSegments);
|
||||
float maxSegments_pow4 = pow2(maxSegments_pow2);
|
||||
|
||||
// If using fixed count, this is the number of segments we need to emit per instance. Always
|
||||
// emit at least 2 segments so we can support triangles.
|
||||
float numFixedSegments_pow4 = 2*2*2*2;
|
||||
|
||||
for (auto [pathMatrix, path, color] : pathDrawList) {
|
||||
AffineMatrix m(pathMatrix);
|
||||
wangs_formula::VectorXform totalXform(SkMatrix::Concat(shaderMatrix, pathMatrix));
|
||||
@ -56,19 +48,8 @@ void PathCurveTessellator::writePatches(PatchWriter& patchWriter,
|
||||
if (n4 <= 1) {
|
||||
break; // This quad only needs 1 segment, which is empty.
|
||||
}
|
||||
if (n4 <= maxSegments_pow4) {
|
||||
// This quad already fits in "maxTessellationSegments".
|
||||
patchWriter.writeQuadratic(p0, p1, p2);
|
||||
} else {
|
||||
// The path should have been pre-chopped if needed, so all curves fit in
|
||||
// kMaxTessellationSegmentsPerCurve.
|
||||
n4 = std::min(n4, pow4(kMaxTessellationSegmentsPerCurve));
|
||||
// Chop until each quad tessellation requires "maxSegments" or fewer.
|
||||
int numPatches =
|
||||
SkScalarCeilToInt(wangs_formula::root4(n4/maxSegments_pow4));
|
||||
patchWriter.chopAndWriteQuads(p0, p1, p2, numPatches);
|
||||
}
|
||||
numFixedSegments_pow4 = std::max(n4, numFixedSegments_pow4);
|
||||
|
||||
patchWriter.writeQuadratic(p0, p1, p2, n4);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -82,18 +63,8 @@ void PathCurveTessellator::writePatches(PatchWriter& patchWriter,
|
||||
if (n2 <= 1) {
|
||||
break; // This conic only needs 1 segment, which is empty.
|
||||
}
|
||||
if (n2 <= maxSegments_pow2) {
|
||||
// This conic already fits in "maxTessellationSegments".
|
||||
patchWriter.writeConic(p0, p1, p2, *w);
|
||||
} else {
|
||||
// The path should have been pre-chopped if needed, so all curves fit in
|
||||
// kMaxTessellationSegmentsPerCurve.
|
||||
n2 = std::min(n2, pow2(kMaxTessellationSegmentsPerCurve));
|
||||
// Chop until each conic tessellation requires "maxSegments" or fewer.
|
||||
int numPatches = SkScalarCeilToInt(sqrtf(n2/maxSegments_pow2));
|
||||
patchWriter.chopAndWriteConics(p0, p1, p2, *w, numPatches);
|
||||
}
|
||||
numFixedSegments_pow4 = std::max(n2*n2, numFixedSegments_pow4);
|
||||
|
||||
patchWriter.writeConic(p0, p1, p2, *w, n2);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -106,19 +77,8 @@ void PathCurveTessellator::writePatches(PatchWriter& patchWriter,
|
||||
if (n4 <= 1) {
|
||||
break; // This cubic only needs 1 segment, which is empty.
|
||||
}
|
||||
if (n4 <= maxSegments_pow4) {
|
||||
// This cubic already fits in "maxTessellationSegments".
|
||||
patchWriter.writeCubic(p0, p1, p2, p3);
|
||||
} else {
|
||||
// The path should have been pre-chopped if needed, so all curves fit in
|
||||
// kMaxTessellationSegmentsPerCurve.
|
||||
n4 = std::min(n4, pow4(kMaxTessellationSegmentsPerCurve));
|
||||
// Chop until each cubic tessellation requires "maxSegments" or fewer.
|
||||
int numPatches =
|
||||
SkScalarCeilToInt(wangs_formula::root4(n4/maxSegments_pow4));
|
||||
patchWriter.chopAndWriteCubics(p0, p1, p2, p3, numPatches);
|
||||
}
|
||||
numFixedSegments_pow4 = std::max(n4, numFixedSegments_pow4);
|
||||
|
||||
patchWriter.writeCubic(p0, p1, p2, p3, n4);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -127,10 +87,9 @@ void PathCurveTessellator::writePatches(PatchWriter& patchWriter,
|
||||
}
|
||||
}
|
||||
|
||||
// log16(n^4) == log2(n).
|
||||
// We already chopped curves to make sure none needed a higher resolveLevel than
|
||||
// kMaxFixedResolveLevel.
|
||||
fFixedResolveLevel = SkTPin(wangs_formula::nextlog16(numFixedSegments_pow4),
|
||||
fFixedResolveLevel = SkTPin(patchWriter.requiredResolveLevel(),
|
||||
fFixedResolveLevel,
|
||||
int(kMaxFixedResolveLevel));
|
||||
}
|
||||
|
@ -31,7 +31,6 @@ public:
|
||||
int patchPreallocCount(int totalCombinedPathVerbCnt) const final;
|
||||
|
||||
void writePatches(PatchWriter& patchWriter,
|
||||
int maxTessellationSegments,
|
||||
const SkMatrix& shaderMatrix,
|
||||
const PathDrawList& pathDrawList) final;
|
||||
|
||||
|
@ -73,7 +73,6 @@ public:
|
||||
// Each path's fPathMatrix in the list is applied on the CPU while the geometry is being written
|
||||
// out. This is a tool for batching, and is applied in addition to the shader's on-GPU matrix.
|
||||
virtual void writePatches(PatchWriter&,
|
||||
int maxTessellationSegments,
|
||||
const SkMatrix& shaderMatrix,
|
||||
const PathDrawList&) = 0;
|
||||
|
||||
@ -89,8 +88,8 @@ public:
|
||||
int totalCombinedPathVerbCnt,
|
||||
bool willUseTessellationShaders) {
|
||||
if (int patchPreallocCount = this->patchPreallocCount(totalCombinedPathVerbCnt)) {
|
||||
PatchWriter patchWriter(target, this, patchPreallocCount);
|
||||
this->writePatches(patchWriter, maxTessellationSegments, shaderMatrix, pathDrawList);
|
||||
PatchWriter patchWriter(target, this, maxTessellationSegments, patchPreallocCount);
|
||||
this->writePatches(patchWriter, shaderMatrix, pathDrawList);
|
||||
}
|
||||
if (!willUseTessellationShaders) {
|
||||
this->prepareFixedCountBuffers(target);
|
||||
|
@ -131,16 +131,8 @@ int PathWedgeTessellator::patchPreallocCount(int totalCombinedPathVerbCnt) const
|
||||
}
|
||||
|
||||
void PathWedgeTessellator::writePatches(PatchWriter& patchWriter,
|
||||
int maxTessellationSegments,
|
||||
const SkMatrix& shaderMatrix,
|
||||
const PathDrawList& pathDrawList) {
|
||||
float maxSegments_pow2 = pow2(maxTessellationSegments);
|
||||
float maxSegments_pow4 = pow2(maxSegments_pow2);
|
||||
|
||||
// If using fixed count, this is the number of segments we need to emit per instance. Always
|
||||
// emit at least 1 segment.
|
||||
float numFixedSegments_pow4 = 1;
|
||||
|
||||
for (auto [pathMatrix, path, color] : pathDrawList) {
|
||||
AffineMatrix m(pathMatrix);
|
||||
wangs_formula::VectorXform totalXform(SkMatrix::Concat(shaderMatrix, pathMatrix));
|
||||
@ -173,19 +165,8 @@ void PathWedgeTessellator::writePatches(PatchWriter& patchWriter,
|
||||
float n4 = wangs_formula::quadratic_pow4(kTessellationPrecision,
|
||||
pts,
|
||||
totalXform);
|
||||
if (n4 <= maxSegments_pow4) {
|
||||
// This quad already fits in "maxTessellationSegments".
|
||||
patchWriter.writeQuadratic(p0, p1, p2);
|
||||
} else {
|
||||
// The path should have been pre-chopped if needed, so all curves fit in
|
||||
// kMaxTessellationSegmentsPerCurve.
|
||||
n4 = std::min(n4, pow4(kMaxTessellationSegmentsPerCurve));
|
||||
// Chop until each quad tessellation requires "maxSegments" or fewer.
|
||||
int numPatches =
|
||||
SkScalarCeilToInt(wangs_formula::root4(n4/maxSegments_pow4));
|
||||
patchWriter.chopAndWriteQuads(p0, p1, p2, numPatches);
|
||||
}
|
||||
numFixedSegments_pow4 = std::max(n4, numFixedSegments_pow4);
|
||||
|
||||
patchWriter.writeQuadratic(p0, p1, p2, n4);
|
||||
lastPoint = pts[2];
|
||||
break;
|
||||
}
|
||||
@ -197,18 +178,8 @@ void PathWedgeTessellator::writePatches(PatchWriter& patchWriter,
|
||||
pts,
|
||||
*w,
|
||||
totalXform);
|
||||
if (n2 <= maxSegments_pow2) {
|
||||
// This conic already fits in "maxTessellationSegments".
|
||||
patchWriter.writeConic(p0, p1, p2, *w);
|
||||
} else {
|
||||
// The path should have been pre-chopped if needed, so all curves fit in
|
||||
// kMaxTessellationSegmentsPerCurve.
|
||||
n2 = std::min(n2, pow2(kMaxTessellationSegmentsPerCurve));
|
||||
// Chop until each conic tessellation requires "maxSegments" or fewer.
|
||||
int numPatches = SkScalarCeilToInt(sqrtf(n2/maxSegments_pow2));
|
||||
patchWriter.chopAndWriteConics(p0, p1, p2, *w, numPatches);
|
||||
}
|
||||
numFixedSegments_pow4 = std::max(n2*n2, numFixedSegments_pow4);
|
||||
|
||||
patchWriter.writeConic(p0, p1, p2, *w, n2);
|
||||
lastPoint = pts[2];
|
||||
break;
|
||||
}
|
||||
@ -219,19 +190,8 @@ void PathWedgeTessellator::writePatches(PatchWriter& patchWriter,
|
||||
float n4 = wangs_formula::cubic_pow4(kTessellationPrecision,
|
||||
pts,
|
||||
totalXform);
|
||||
if (n4 <= maxSegments_pow4) {
|
||||
// This cubic already fits in "maxTessellationSegments".
|
||||
patchWriter.writeCubic(p0, p1, p2, p3);
|
||||
} else {
|
||||
// The path should have been pre-chopped if needed, so all curves fit in
|
||||
// kMaxTessellationSegmentsPerCurve.
|
||||
n4 = std::min(n4, pow4(kMaxTessellationSegmentsPerCurve));
|
||||
// Chop until each cubic tessellation requires "maxSegments" or fewer.
|
||||
int numPatches =
|
||||
SkScalarCeilToInt(wangs_formula::root4(n4/maxSegments_pow4));
|
||||
patchWriter.chopAndWriteCubics(p0, p1, p2, p3, numPatches);
|
||||
}
|
||||
numFixedSegments_pow4 = std::max(n4, numFixedSegments_pow4);
|
||||
|
||||
patchWriter.writeCubic(p0, p1, p2, p3, n4);
|
||||
lastPoint = pts[3];
|
||||
break;
|
||||
}
|
||||
@ -248,10 +208,9 @@ void PathWedgeTessellator::writePatches(PatchWriter& patchWriter,
|
||||
}
|
||||
}
|
||||
|
||||
// log16(n^4) == log2(n).
|
||||
// We already chopped curves to make sure none needed a higher resolveLevel than
|
||||
// kMaxFixedResolveLevel.
|
||||
fFixedResolveLevel = SkTPin(wangs_formula::nextlog16(numFixedSegments_pow4),
|
||||
fFixedResolveLevel = SkTPin(patchWriter.requiredResolveLevel(),
|
||||
fFixedResolveLevel,
|
||||
int(kMaxFixedResolveLevel));
|
||||
}
|
||||
|
@ -34,7 +34,6 @@ public:
|
||||
int patchPreallocCount(int totalCombinedPathVerbCnt) const final;
|
||||
|
||||
void writePatches(PatchWriter&,
|
||||
int maxTessellationSegments,
|
||||
const SkMatrix& shaderMatrix,
|
||||
const PathDrawList&) final;
|
||||
|
||||
|
@ -23,7 +23,7 @@ namespace skgpu {
|
||||
namespace {
|
||||
|
||||
constexpr static float kMaxParametricSegments_pow4 =
|
||||
StrokeFixedCountTessellator::kMaxParametricSegments_pow4;
|
||||
pow4(StrokeFixedCountTessellator::kMaxParametricSegments);
|
||||
|
||||
// Writes out strokes to the given instance chunk array, chopping if necessary so that all instances
|
||||
// require 32 parametric segments or less. (We don't consider radial segments here. The tessellator
|
||||
@ -38,10 +38,6 @@ public:
|
||||
|
||||
float parametricPrecision() const { return fParametricPrecision; }
|
||||
|
||||
// maxParametricSegments^4, or the number of parametric segments, raised to the 4th power,
|
||||
// that are required by the single instance we've written that requires the most segments.
|
||||
float maxParametricSegments_pow4() const { return fMaxParametricSegments_pow4; }
|
||||
|
||||
SK_ALWAYS_INLINE void lineTo(SkPoint start, SkPoint end) {
|
||||
fPatchWriter.writeLine(start, end);
|
||||
}
|
||||
@ -53,9 +49,7 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
fPatchWriter.writeQuadratic(p);
|
||||
fMaxParametricSegments_pow4 = std::max(numParametricSegments_pow4,
|
||||
fMaxParametricSegments_pow4);
|
||||
fPatchWriter.writeQuadratic(p, numParametricSegments_pow4);
|
||||
}
|
||||
|
||||
SK_ALWAYS_INLINE void conicTo(const SkPoint p[3], float w) {
|
||||
@ -65,9 +59,7 @@ public:
|
||||
this->chopConicTo({p, w});
|
||||
return;
|
||||
}
|
||||
fPatchWriter.writeConic(p, w);
|
||||
fMaxParametricSegments_pow4 = std::max(numParametricSegments_pow4,
|
||||
fMaxParametricSegments_pow4);
|
||||
fPatchWriter.writeConic(p, w, n2);
|
||||
}
|
||||
|
||||
SK_ALWAYS_INLINE void cubicConvex180To(const SkPoint p[4]) {
|
||||
@ -76,9 +68,7 @@ public:
|
||||
this->chopCubicConvex180To(p);
|
||||
return;
|
||||
}
|
||||
fPatchWriter.writeCubic(p);
|
||||
fMaxParametricSegments_pow4 = std::max(numParametricSegments_pow4,
|
||||
fMaxParametricSegments_pow4);
|
||||
fPatchWriter.writeCubic(p, numParametricSegments_pow4);
|
||||
}
|
||||
|
||||
// Called when we encounter the verb "kMoveWithinContour". Moves invalidate the previous control
|
||||
@ -123,7 +113,6 @@ private:
|
||||
|
||||
PatchWriter& fPatchWriter;
|
||||
const float fParametricPrecision;
|
||||
float fMaxParametricSegments_pow4 = 1;
|
||||
};
|
||||
|
||||
// Returns the worst-case number of edges we will need in order to draw a join of the given type.
|
||||
@ -276,9 +265,8 @@ int StrokeFixedCountTessellator::writePatches(PatchWriter& patchWriter,
|
||||
int maxRadialSegmentsInStroke =
|
||||
std::max(SkScalarCeilToInt(maxRadialSegmentsPerRadian * SK_ScalarPI), 1);
|
||||
|
||||
int maxParametricSegmentsInStroke = SkScalarCeilToInt(sqrtf(sqrtf(
|
||||
instanceWriter.maxParametricSegments_pow4())));
|
||||
SkASSERT(maxParametricSegmentsInStroke >= 1); // maxParametricSegments_pow4 is always >= 1.
|
||||
int maxParametricSegmentsInStroke = patchWriter.requiredFixedSegments();
|
||||
SkASSERT(maxParametricSegmentsInStroke >= 1);
|
||||
|
||||
// Now calculate the maximum number of edges we will need in the stroke portion of the instance.
|
||||
// The first and last edges in a stroke are shared by both the parametric and radial sets of
|
||||
@ -322,7 +310,11 @@ int StrokeFixedCountTessellator::prepare(GrMeshDrawTarget* target,
|
||||
std::array<float,2> matrixMinMaxScales,
|
||||
PathStrokeList* pathStrokeList,
|
||||
int totalCombinedStrokeVerbCnt) {
|
||||
PatchWriter patchWriter(target, this, this->patchPreallocCount(totalCombinedStrokeVerbCnt));
|
||||
// NOTE: For now InstanceWriter manually chops curves that exceed kMaxParametricSegments_pow4,
|
||||
// so passing in kMaxParametricSegments to PatchWriter avoids its auto-chopping while still
|
||||
// correctly accumulating the min required segment count.
|
||||
PatchWriter patchWriter(target, this, kMaxParametricSegments,
|
||||
this->patchPreallocCount(totalCombinedStrokeVerbCnt));
|
||||
|
||||
fFixedEdgeCount = this->writePatches(patchWriter,
|
||||
shaderMatrix,
|
||||
|
@ -20,7 +20,7 @@ namespace skgpu {
|
||||
// instance are emitted as degenerate triangles.
|
||||
class StrokeFixedCountTessellator final : public StrokeTessellator {
|
||||
public:
|
||||
constexpr static float kMaxParametricSegments_pow4 = 32*32*32*32; // 32^4
|
||||
constexpr static int kMaxParametricSegments = 32;
|
||||
constexpr static int8_t kMaxParametricSegments_log2 = 5; // log2(32)
|
||||
|
||||
StrokeFixedCountTessellator(PatchAttribs attribs) : StrokeTessellator(attribs) {}
|
||||
|
@ -231,7 +231,7 @@ public:
|
||||
// tessellation does not require explicit curve types or automatic CPU chopping and
|
||||
// deferring, PatchWriter::writeCubic() patch type is sufficient, although 'p' may actually
|
||||
// contain data causing it to be interpreted as something other than a cubic in the shader.
|
||||
fPatchWriter.writeCubic(p);
|
||||
fPatchWriter.writeCubic(p, 0.f);
|
||||
fPatchWriter.updateJoinControlPointAttrib(endControlPoint);
|
||||
}
|
||||
|
||||
@ -589,7 +589,7 @@ private:
|
||||
}
|
||||
asPatch[3] = nextControlPoint;
|
||||
|
||||
fPatchWriter.writeCubic(asPatch); // not really a cubic, see note in writePatchTo()
|
||||
fPatchWriter.writeCubic(asPatch, 0.f); // not really a cubic, see note in writePatchTo()
|
||||
}
|
||||
|
||||
fPatchWriter.updateJoinControlPointAttrib(nextControlPoint);
|
||||
@ -860,7 +860,8 @@ int StrokeHardwareTessellator::prepare(GrMeshDrawTarget* target,
|
||||
std::array<float,2> matrixMinMaxScales,
|
||||
PathStrokeList* pathStrokeList,
|
||||
int totalCombinedStrokeVerbCnt) {
|
||||
PatchWriter patchWriter(target, this, this->patchPreallocCount(totalCombinedStrokeVerbCnt));
|
||||
PatchWriter patchWriter(target, this, 0.f, /* unused max segment tracking */
|
||||
this->patchPreallocCount(totalCombinedStrokeVerbCnt));
|
||||
return this->writePatches(patchWriter, shaderMatrix, matrixMinMaxScales, pathStrokeList);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user