Reland "Lift recursive curve culling out of tessellators"
This is a reland of 75e1f4c028
Original change's description:
> Lift recursive curve culling out of tessellators
>
> We need recursive chopping/culling logic in order to draw
> astronomically large paths. But rather than do that at the same time
> the tessellators chop curves, this CL moves that logic into an
> SkPath -> SkPath transformation that runs ahead of time (and only if
> the path is extremely large to begin with). This will enable us to
> remove recursion from the tessellators and quickly determine ahead of
> time the size of buffers they need.
>
> Bug: skia:12524
> Change-Id: Ib2800fb23054f1548501811203173e58273fbc83
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/463936
> Commit-Queue: Chris Dalton <csmartdalton@google.com>
> Reviewed-by: Greg Daniel <egdaniel@google.com>
Bug: skia:12524
Change-Id: Idf54f0c2bddaaddc9fc17bee99c910f3961682a5
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/465361
Reviewed-by: Greg Daniel <egdaniel@google.com>
Auto-Submit: Chris Dalton <csmartdalton@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
This commit is contained in:
parent
d90e09b1ae
commit
6bb17ab48d
@ -128,8 +128,7 @@ DEF_PATH_TESS_BENCH(GrPathCurveTessellator, make_cubic_path(8), SkMatrix::I()) {
|
||||
fTarget->caps().minPathVerbsForHwTessellation(),
|
||||
noVaryingsPipeline,
|
||||
fTarget->caps());
|
||||
tess->prepare(fTarget.get(), SkRectPriv::MakeLargest(), {gAlmostIdentity, fPath},
|
||||
fPath.countVerbs());
|
||||
tess->prepare(fTarget.get(), {gAlmostIdentity, fPath}, fPath.countVerbs());
|
||||
}
|
||||
|
||||
DEF_PATH_TESS_BENCH(GrPathWedgeTessellator, make_cubic_path(8), SkMatrix::I()) {
|
||||
@ -142,8 +141,7 @@ DEF_PATH_TESS_BENCH(GrPathWedgeTessellator, make_cubic_path(8), SkMatrix::I()) {
|
||||
fTarget->caps().minPathVerbsForHwTessellation(),
|
||||
noVaryingsPipeline,
|
||||
fTarget->caps());
|
||||
tess->prepare(fTarget.get(), SkRectPriv::MakeLargest(), {gAlmostIdentity, fPath},
|
||||
fPath.countVerbs());
|
||||
tess->prepare(fTarget.get(), {gAlmostIdentity, fPath}, fPath.countVerbs());
|
||||
}
|
||||
|
||||
static void benchmark_wangs_formula_cubic_log2(const SkMatrix& matrix, const SkPath& path) {
|
||||
@ -231,19 +229,16 @@ using MakeTessellatorFn = std::unique_ptr<StrokeTessellator>(*)(ShaderFlags,
|
||||
const GrShaderCaps&,
|
||||
const SkMatrix&,
|
||||
PathStrokeList*,
|
||||
std::array<float, 2>,
|
||||
const SkRect&);
|
||||
std::array<float, 2>);
|
||||
|
||||
static std::unique_ptr<StrokeTessellator> make_hw_tessellator(
|
||||
ShaderFlags shaderFlags,
|
||||
const GrShaderCaps& shaderCaps,
|
||||
const SkMatrix& viewMatrix,
|
||||
PathStrokeList* pathStrokeList,
|
||||
std::array<float,2> matrixMinMaxScales,
|
||||
const SkRect& strokeCullBounds) {
|
||||
std::array<float,2> matrixMinMaxScales) {
|
||||
return std::make_unique<StrokeHardwareTessellator>(shaderCaps, shaderFlags, viewMatrix,
|
||||
pathStrokeList, matrixMinMaxScales,
|
||||
strokeCullBounds);
|
||||
pathStrokeList, matrixMinMaxScales);
|
||||
}
|
||||
|
||||
static std::unique_ptr<StrokeTessellator> make_fixed_count_tessellator(
|
||||
@ -251,11 +246,9 @@ static std::unique_ptr<StrokeTessellator> make_fixed_count_tessellator(
|
||||
const GrShaderCaps& shaderCaps,
|
||||
const SkMatrix& viewMatrix,
|
||||
PathStrokeList* pathStrokeList,
|
||||
std::array<float, 2> matrixMinMaxScales,
|
||||
const SkRect& strokeCullBounds) {
|
||||
std::array<float, 2> matrixMinMaxScales) {
|
||||
return std::make_unique<StrokeFixedCountTessellator>(shaderCaps, shaderFlags, viewMatrix,
|
||||
pathStrokeList, matrixMinMaxScales,
|
||||
strokeCullBounds);
|
||||
pathStrokeList, matrixMinMaxScales);
|
||||
}
|
||||
|
||||
using MakePathStrokesFn = std::vector<PathStrokeList>(*)();
|
||||
@ -357,8 +350,7 @@ private:
|
||||
|
||||
fTessellator = fMakeTessellatorFn(fShaderFlags, *fTarget->caps().shaderCaps(),
|
||||
SkMatrix::Scale(fMatrixScale, fMatrixScale),
|
||||
fPathStrokes.data(), {fMatrixScale, fMatrixScale},
|
||||
SkRectPriv::MakeLargest());
|
||||
fPathStrokes.data(), {fMatrixScale, fMatrixScale});
|
||||
}
|
||||
|
||||
void onDraw(int loops, SkCanvas*) final {
|
||||
|
@ -323,7 +323,6 @@ skia_gpu_sources = [
|
||||
"$_src/gpu/gradients/GrGradientShader.h",
|
||||
|
||||
# tessellate
|
||||
"$_src/gpu/tessellate/CullTest.h",
|
||||
"$_src/gpu/tessellate/PathCurveTessellator.cpp",
|
||||
"$_src/gpu/tessellate/PathCurveTessellator.h",
|
||||
"$_src/gpu/tessellate/PathTessellator.h",
|
||||
@ -336,7 +335,6 @@ skia_gpu_sources = [
|
||||
"$_src/gpu/tessellate/StrokeHardwareTessellator.h",
|
||||
"$_src/gpu/tessellate/StrokeIterator.h",
|
||||
"$_src/gpu/tessellate/StrokeTessellator.h",
|
||||
"$_src/gpu/tessellate/WangsFormula.h",
|
||||
|
||||
# tessellate/shaders
|
||||
"$_src/gpu/tessellate/shaders/GrPathTessellationShader.cpp",
|
||||
@ -829,7 +827,9 @@ skia_shared_gpu_sources = [
|
||||
"$_src/gpu/BufferWriter.h",
|
||||
|
||||
# tessellate
|
||||
"$_src/gpu/tessellate/CullTest.h",
|
||||
"$_src/gpu/tessellate/MiddleOutPolygonTriangulator.h",
|
||||
"$_src/gpu/tessellate/Tessellation.cpp",
|
||||
"$_src/gpu/tessellate/Tessellation.h",
|
||||
"$_src/gpu/tessellate/WangsFormula.h",
|
||||
]
|
||||
|
@ -122,7 +122,7 @@ private:
|
||||
caps);
|
||||
break;
|
||||
}
|
||||
fTessellator->prepare(flushState, this->bounds(), {pathMatrix, fPath}, fPath.countVerbs());
|
||||
fTessellator->prepare(flushState, {pathMatrix, fPath}, fPath.countVerbs());
|
||||
fProgram = GrTessellationShader::MakeProgram({alloc, flushState->writeView(),
|
||||
flushState->usesMSAASurface(),
|
||||
&flushState->dstProxyView(),
|
||||
|
@ -408,8 +408,10 @@ void PathInnerTriangulateOp::onPrepare(GrOpFlushState* flushState) {
|
||||
|
||||
if (fTessellator) {
|
||||
// Must be called after polysToTriangles() in order for fFanBreadcrumbs to be complete.
|
||||
fTessellator->prepare(flushState, this->bounds(), {SkMatrix::I(), fPath},
|
||||
fPath.countVerbs(), &fFanBreadcrumbs);
|
||||
fTessellator->prepare(flushState,
|
||||
{SkMatrix::I(), fPath},
|
||||
fPath.countVerbs(),
|
||||
&fFanBreadcrumbs);
|
||||
}
|
||||
|
||||
if (!flushState->caps().shaderCaps()->vertexIDSupport()) {
|
||||
|
@ -259,7 +259,7 @@ void PathStencilCoverOp::onPrepare(GrOpFlushState* flushState) {
|
||||
vertexAlloc.unlock(fFanVertexCount);
|
||||
}
|
||||
|
||||
fTessellator->prepare(flushState, this->bounds(), *fPathDrawList, fTotalCombinedPathVerbCnt);
|
||||
fTessellator->prepare(flushState, *fPathDrawList, fTotalCombinedPathVerbCnt);
|
||||
|
||||
if (fCoverBBoxProgram) {
|
||||
size_t instanceStride = fCoverBBoxProgram->geomProc().instanceStride();
|
||||
|
@ -64,7 +64,7 @@ void PathTessellateOp::onPrepare(GrOpFlushState* flushState) {
|
||||
&flushState->caps()}, flushState->detachAppliedClip());
|
||||
SkASSERT(fTessellator);
|
||||
}
|
||||
fTessellator->prepare(flushState, this->bounds(), {SkMatrix::I(), fPath}, fPath.countVerbs());
|
||||
fTessellator->prepare(flushState, {SkMatrix::I(), fPath}, fPath.countVerbs());
|
||||
}
|
||||
|
||||
void PathTessellateOp::onExecute(GrOpFlushState* flushState, const SkRect& chainBounds) {
|
||||
|
@ -54,15 +54,15 @@ StrokeTessellateOp::StrokeTessellateOp(GrAAType aaType, const SkMatrix& viewMatr
|
||||
SkRect devBounds = path.getBounds();
|
||||
if (!this->headStroke().isHairlineStyle()) {
|
||||
// Non-hairlines inflate in local path space (pre-transform).
|
||||
fInflationRadius = stroke.getInflationRadius();
|
||||
devBounds.outset(fInflationRadius, fInflationRadius);
|
||||
float r = stroke.getInflationRadius();
|
||||
devBounds.outset(r, r);
|
||||
}
|
||||
viewMatrix.mapRect(&devBounds, devBounds);
|
||||
if (this->headStroke().isHairlineStyle()) {
|
||||
// Hairlines inflate in device space (post-transform).
|
||||
fInflationRadius = SkStrokeRec::GetInflationRadius(stroke.getJoin(), stroke.getMiter(),
|
||||
stroke.getCap(), 1);
|
||||
devBounds.outset(fInflationRadius, fInflationRadius);
|
||||
float r = SkStrokeRec::GetInflationRadius(stroke.getJoin(), stroke.getMiter(),
|
||||
stroke.getCap(), 1);
|
||||
devBounds.outset(r, r);
|
||||
}
|
||||
this->setBounds(devBounds, HasAABloat::kNo, IsHairline::kNo);
|
||||
}
|
||||
@ -143,7 +143,6 @@ GrOp::CombineResult StrokeTessellateOp::onCombineIfPossible(GrOp* grOp, SkArenaA
|
||||
fPathStrokeTail = (op->fPathStrokeTail == &op->fPathStrokeList.fNext) ? &headCopy->fNext
|
||||
: op->fPathStrokeTail;
|
||||
|
||||
fInflationRadius = std::max(fInflationRadius, op->fInflationRadius);
|
||||
fTotalCombinedVerbCnt += op->fTotalCombinedVerbCnt;
|
||||
return CombineResult::kMerged;
|
||||
}
|
||||
@ -187,12 +186,6 @@ void StrokeTessellateOp::prePrepareTessellator(GrTessellationShader::ProgramArgs
|
||||
matrixMinMaxScales.fill(1);
|
||||
}
|
||||
|
||||
float devInflationRadius = fInflationRadius;
|
||||
if (!this->headStroke().isHairlineStyle()) {
|
||||
devInflationRadius *= matrixMinMaxScales[1];
|
||||
}
|
||||
SkRect strokeCullBounds = this->bounds().makeOutset(devInflationRadius, devInflationRadius);
|
||||
|
||||
auto* pipeline = GrTessellationShader::MakePipeline(args, fAAType, std::move(clip),
|
||||
std::move(fProcessors));
|
||||
|
||||
@ -203,15 +196,13 @@ void StrokeTessellateOp::prePrepareTessellator(GrTessellationShader::ProgramArgs
|
||||
fShaderFlags,
|
||||
fViewMatrix,
|
||||
&fPathStrokeList,
|
||||
matrixMinMaxScales,
|
||||
strokeCullBounds);
|
||||
matrixMinMaxScales);
|
||||
} else {
|
||||
fTessellator = arena->make<StrokeFixedCountTessellator>(*caps.shaderCaps(),
|
||||
fShaderFlags,
|
||||
fViewMatrix,
|
||||
&fPathStrokeList,
|
||||
matrixMinMaxScales,
|
||||
strokeCullBounds);
|
||||
matrixMinMaxScales);
|
||||
}
|
||||
|
||||
auto fillStencil = &GrUserStencilSettings::kUnused;
|
||||
|
@ -69,7 +69,6 @@ private:
|
||||
ShaderFlags fShaderFlags = ShaderFlags::kNone;
|
||||
PathStrokeList fPathStrokeList;
|
||||
PathStrokeList** fPathStrokeTail = &fPathStrokeList.fNext;
|
||||
float fInflationRadius = 0;
|
||||
int fTotalCombinedVerbCnt = 0;
|
||||
GrProcessorSet fProcessors;
|
||||
bool fNeedsStencil;
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include "src/gpu/ops/PathStencilCoverOp.h"
|
||||
#include "src/gpu/ops/PathTessellateOp.h"
|
||||
#include "src/gpu/ops/StrokeTessellateOp.h"
|
||||
#include "src/gpu/tessellate/Tessellation.h"
|
||||
#include "src/gpu/tessellate/WangsFormula.h"
|
||||
#include "src/gpu/v1/SurfaceDrawContext_v1.h"
|
||||
|
||||
namespace {
|
||||
@ -113,6 +115,33 @@ bool TessellationPathRenderer::onDrawPath(const DrawPathArgs& args) {
|
||||
SkPath path;
|
||||
args.fShape->asPath(&path);
|
||||
|
||||
const SkRect pathDevBounds = args.fViewMatrix->mapRect(args.fShape->bounds());
|
||||
float n = wangs_formula::worst_case_cubic_pow4(kTessellationPrecision,
|
||||
pathDevBounds.width(),
|
||||
pathDevBounds.height());
|
||||
if (n > pow4(kMaxTessellationSegmentsPerCurve)) {
|
||||
// The path is extremely large. Pre-chop its curves to keep the number of tessellation
|
||||
// segments tractable. This will also flatten curves that fall completely outside the
|
||||
// viewport.
|
||||
SkRect viewport = SkRect::Make(*args.fClipConservativeBounds);
|
||||
if (!args.fShape->style().isSimpleFill()) {
|
||||
// Outset the viewport to pad for the stroke width.
|
||||
const SkStrokeRec& stroke = args.fShape->style().strokeRec();
|
||||
float inflationRadius;
|
||||
if (stroke.isHairlineStyle()) {
|
||||
// SkStrokeRec::getInflationRadius() doesn't handle hairlines robustly. Instead
|
||||
// find the inflation of an equivalent stroke in device space with a width of 1.
|
||||
inflationRadius = SkStrokeRec::GetInflationRadius(stroke.getJoin(),
|
||||
stroke.getMiter(),
|
||||
stroke.getCap(), 1);
|
||||
} else {
|
||||
inflationRadius = stroke.getInflationRadius() * args.fViewMatrix->getMaxScale();
|
||||
}
|
||||
viewport.outset(inflationRadius, inflationRadius);
|
||||
}
|
||||
path = PreChopPathCurves(path, *args.fViewMatrix, viewport);
|
||||
}
|
||||
|
||||
// Handle strokes first.
|
||||
if (!args.fShape->style().isSimpleFill()) {
|
||||
SkASSERT(!path.isInverseFillType()); // See onGetStencilSupport().
|
||||
@ -126,7 +155,6 @@ bool TessellationPathRenderer::onDrawPath(const DrawPathArgs& args) {
|
||||
}
|
||||
|
||||
// Handle empty paths.
|
||||
const SkRect pathDevBounds = args.fViewMatrix->mapRect(args.fShape->bounds());
|
||||
if (pathDevBounds.isEmpty()) {
|
||||
if (path.isInverseFillType()) {
|
||||
args.fSurfaceDrawContext->drawPaint(args.fClip, std::move(args.fPaint),
|
||||
@ -173,6 +201,14 @@ void TessellationPathRenderer::onStencilPath(const StencilPathArgs& args) {
|
||||
SkPath path;
|
||||
args.fShape->asPath(&path);
|
||||
|
||||
float n = wangs_formula::worst_case_cubic_pow4(kTessellationPrecision,
|
||||
pathDevBounds.width(),
|
||||
pathDevBounds.height());
|
||||
if (n > pow4(kMaxTessellationSegmentsPerCurve)) {
|
||||
SkRect viewport = SkRect::Make(*args.fClipConservativeBounds);
|
||||
path = PreChopPathCurves(path, *args.fViewMatrix, viewport);
|
||||
}
|
||||
|
||||
if (args.fShape->knownToBeConvex()) {
|
||||
constexpr static GrUserStencilSettings kMarkStencil(
|
||||
GrUserStencilSettings::StaticInit<
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "src/gpu/GrMeshDrawTarget.h"
|
||||
#include "src/gpu/GrResourceProvider.h"
|
||||
#include "src/gpu/geometry/GrPathUtils.h"
|
||||
#include "src/gpu/tessellate/CullTest.h"
|
||||
#include "src/gpu/tessellate/MiddleOutPolygonTriangulator.h"
|
||||
#include "src/gpu/tessellate/PathXform.h"
|
||||
#include "src/gpu/tessellate/Tessellation.h"
|
||||
@ -35,12 +34,10 @@ public:
|
||||
, fMaxSegments_pow4(fMaxSegments_pow2 * fMaxSegments_pow2) {
|
||||
}
|
||||
|
||||
void setMatrices(const SkRect& cullBounds,
|
||||
const SkMatrix& shaderMatrix,
|
||||
void setMatrices(const SkMatrix& shaderMatrix,
|
||||
const SkMatrix& pathMatrix) {
|
||||
SkMatrix totalMatrix;
|
||||
totalMatrix.setConcat(shaderMatrix, pathMatrix);
|
||||
fCullTest.set(cullBounds, totalMatrix);
|
||||
fTotalVectorXform = totalMatrix;
|
||||
fPathXform = pathMatrix;
|
||||
}
|
||||
@ -111,12 +108,8 @@ private:
|
||||
const SkPoint p[3]) {
|
||||
SkPoint chops[5];
|
||||
SkChopQuadAtHalf(p, chops);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
const SkPoint* q = chops + i*2;
|
||||
if (fCullTest.areVisible3(q)) {
|
||||
this->writeQuadratic(shaderCaps, chunker, q);
|
||||
}
|
||||
}
|
||||
this->writeQuadratic(shaderCaps, chunker, chops);
|
||||
this->writeQuadratic(shaderCaps, chunker, chops + 2);
|
||||
// Connect the two halves.
|
||||
this->writeTriangle(shaderCaps, chunker, chops[0], chops[2], chops[4]);
|
||||
}
|
||||
@ -127,11 +120,8 @@ private:
|
||||
if (!conic.chopAt(.5, chops)) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (fCullTest.areVisible3(chops[i].fPts)) {
|
||||
this->writeConic(shaderCaps, chunker, chops[i].fPts, chops[i].fW);
|
||||
}
|
||||
}
|
||||
this->writeConic(shaderCaps, chunker, chops[0].fPts, chops[0].fW);
|
||||
this->writeConic(shaderCaps, chunker, chops[1].fPts, chops[1].fW);
|
||||
// Connect the two halves.
|
||||
this->writeTriangle(shaderCaps, chunker, conic.fPts[0], chops[0].fPts[2], chops[1].fPts[2]);
|
||||
}
|
||||
@ -140,12 +130,8 @@ private:
|
||||
const SkPoint p[4]) {
|
||||
SkPoint chops[7];
|
||||
SkChopCubicAtHalf(p, chops);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
const SkPoint* c = chops + i*3;
|
||||
if (fCullTest.areVisible4(c)) {
|
||||
this->writeCubic(shaderCaps, chunker, c);
|
||||
}
|
||||
}
|
||||
this->writeCubic(shaderCaps, chunker, chops);
|
||||
this->writeCubic(shaderCaps, chunker, chops + 3);
|
||||
// Connect the two halves.
|
||||
this->writeTriangle(shaderCaps, chunker, chops[0], chops[3], chops[6]);
|
||||
}
|
||||
@ -163,7 +149,6 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
CullTest fCullTest;
|
||||
wangs_formula::VectorXform fTotalVectorXform;
|
||||
PathXform fPathXform;
|
||||
const float fMaxSegments_pow2;
|
||||
@ -206,7 +191,6 @@ GR_DECLARE_STATIC_UNIQUE_KEY(gFixedCountVertexBufferKey);
|
||||
GR_DECLARE_STATIC_UNIQUE_KEY(gFixedCountIndexBufferKey);
|
||||
|
||||
void PathCurveTessellator::prepare(GrMeshDrawTarget* target,
|
||||
const SkRect& cullBounds,
|
||||
const PathDrawList& pathDrawList,
|
||||
int totalCombinedPathVerbCnt,
|
||||
const BreadcrumbTriangleList* breadcrumbTriangleList) {
|
||||
@ -312,7 +296,7 @@ void PathCurveTessellator::prepare(GrMeshDrawTarget* target,
|
||||
|
||||
CurveWriter curveWriter(maxSegments);
|
||||
for (auto [pathMatrix, path] : pathDrawList) {
|
||||
curveWriter.setMatrices(cullBounds, fShader->viewMatrix(), pathMatrix);
|
||||
curveWriter.setMatrices(fShader->viewMatrix(), pathMatrix);
|
||||
for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
|
||||
switch (verb) {
|
||||
case SkPathVerb::kQuad:
|
||||
|
@ -39,10 +39,9 @@ public:
|
||||
const GrCaps&);
|
||||
|
||||
void prepare(GrMeshDrawTarget* target,
|
||||
const SkRect& cullBounds,
|
||||
const PathDrawList& pathDrawList,
|
||||
int totalCombinedPathVerbCnt) override {
|
||||
this->prepare(target, cullBounds, pathDrawList, totalCombinedPathVerbCnt, nullptr);
|
||||
this->prepare(target, pathDrawList, totalCombinedPathVerbCnt, nullptr);
|
||||
}
|
||||
|
||||
// Implements PathTessellator::prepare(), also sending an additional list of breadcrumb
|
||||
@ -51,7 +50,6 @@ public:
|
||||
// ALSO NOTE: The breadcrumb triangles do not have a matrix. These need to be pre-transformed by
|
||||
// the caller if a CPU-side transformation is desired.
|
||||
void prepare(GrMeshDrawTarget*,
|
||||
const SkRect& cullBounds,
|
||||
const PathDrawList&,
|
||||
int totalCombinedPathVerbCnt,
|
||||
const BreadcrumbTriangleList*);
|
||||
|
@ -55,10 +55,7 @@ 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 prepare(GrMeshDrawTarget*,
|
||||
const SkRect& cullBounds,
|
||||
const PathDrawList&,
|
||||
int totalCombinedPathVerbCnt) = 0;
|
||||
virtual void prepare(GrMeshDrawTarget*, const PathDrawList&, int totalCombinedPathVerbCnt) = 0;
|
||||
|
||||
#if SK_GPU_V1
|
||||
// Issues draw calls for the tessellated geometry. The caller is responsible for binding its
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "src/gpu/GrMeshDrawTarget.h"
|
||||
#include "src/gpu/GrResourceProvider.h"
|
||||
#include "src/gpu/geometry/GrPathUtils.h"
|
||||
#include "src/gpu/tessellate/CullTest.h"
|
||||
#include "src/gpu/tessellate/PathXform.h"
|
||||
#include "src/gpu/tessellate/Tessellation.h"
|
||||
#include "src/gpu/tessellate/WangsFormula.h"
|
||||
@ -130,12 +129,10 @@ public:
|
||||
, fMaxSegments_pow4(fMaxSegments_pow2 * fMaxSegments_pow2) {
|
||||
}
|
||||
|
||||
void setMatrices(const SkRect& cullBounds,
|
||||
const SkMatrix& shaderMatrix,
|
||||
void setMatrices(const SkMatrix& shaderMatrix,
|
||||
const SkMatrix& pathMatrix) {
|
||||
SkMatrix totalMatrix;
|
||||
totalMatrix.setConcat(shaderMatrix, pathMatrix);
|
||||
fCullTest.set(cullBounds, totalMatrix);
|
||||
fTotalVectorXform = totalMatrix;
|
||||
fPathXform = pathMatrix;
|
||||
}
|
||||
@ -222,14 +219,8 @@ private:
|
||||
SkPoint midpoint) {
|
||||
SkPoint chops[5];
|
||||
SkChopQuadAtHalf(p, chops);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
const SkPoint* q = chops + i*2;
|
||||
if (fCullTest.areVisible3(q)) {
|
||||
this->writeQuadraticWedge(shaderCaps, q, midpoint);
|
||||
} else {
|
||||
this->writeFlatWedge(shaderCaps, q[0], q[2], midpoint);
|
||||
}
|
||||
}
|
||||
this->writeQuadraticWedge(shaderCaps, chops, midpoint);
|
||||
this->writeQuadraticWedge(shaderCaps, chops + 2, midpoint);
|
||||
}
|
||||
|
||||
void chopAndWriteConicWedges(const GrShaderCaps& shaderCaps,
|
||||
@ -239,13 +230,8 @@ private:
|
||||
if (!conic.chopAt(.5, chops)) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (fCullTest.areVisible3(chops[i].fPts)) {
|
||||
this->writeConicWedge(shaderCaps, chops[i].fPts, chops[i].fW, midpoint);
|
||||
} else {
|
||||
this->writeFlatWedge(shaderCaps, chops[i].fPts[0], chops[i].fPts[2], midpoint);
|
||||
}
|
||||
}
|
||||
this->writeConicWedge(shaderCaps, chops[0].fPts, chops[0].fW, midpoint);
|
||||
this->writeConicWedge(shaderCaps, chops[1].fPts, chops[1].fW, midpoint);
|
||||
}
|
||||
|
||||
void chopAndWriteCubicWedges(const GrShaderCaps& shaderCaps,
|
||||
@ -253,18 +239,11 @@ private:
|
||||
SkPoint midpoint) {
|
||||
SkPoint chops[7];
|
||||
SkChopCubicAtHalf(p, chops);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
const SkPoint* c = chops + i*3;
|
||||
if (fCullTest.areVisible4(c)) {
|
||||
this->writeCubicWedge(shaderCaps, c, midpoint);
|
||||
} else {
|
||||
this->writeFlatWedge(shaderCaps, c[0], c[3], midpoint);
|
||||
}
|
||||
}
|
||||
this->writeCubicWedge(shaderCaps, chops, midpoint);
|
||||
this->writeCubicWedge(shaderCaps, chops + 3, midpoint);
|
||||
}
|
||||
|
||||
GrVertexChunkBuilder fChunker;
|
||||
CullTest fCullTest;
|
||||
wangs_formula::VectorXform fTotalVectorXform;
|
||||
PathXform fPathXform;
|
||||
const float fMaxSegments_pow2;
|
||||
@ -302,7 +281,6 @@ GR_DECLARE_STATIC_UNIQUE_KEY(gFixedCountVertexBufferKey);
|
||||
GR_DECLARE_STATIC_UNIQUE_KEY(gFixedCountIndexBufferKey);
|
||||
|
||||
void PathWedgeTessellator::prepare(GrMeshDrawTarget* target,
|
||||
const SkRect& cullBounds,
|
||||
const PathDrawList& pathDrawList,
|
||||
int totalCombinedPathVerbCnt) {
|
||||
SkASSERT(fVertexChunkArray.empty());
|
||||
@ -327,7 +305,7 @@ void PathWedgeTessellator::prepare(GrMeshDrawTarget* target,
|
||||
|
||||
WedgeWriter wedgeWriter(target, &fVertexChunkArray, patchStride, wedgeAllocCount, maxSegments);
|
||||
for (auto [pathMatrix, path] : pathDrawList) {
|
||||
wedgeWriter.setMatrices(cullBounds, fShader->viewMatrix(), pathMatrix);
|
||||
wedgeWriter.setMatrices(fShader->viewMatrix(), pathMatrix);
|
||||
MidpointContourParser parser(path);
|
||||
while (parser.parseNextContour()) {
|
||||
SkPoint midpoint = wedgeWriter.pathXform().mapPoint(parser.currentMidpoint());
|
||||
|
@ -31,10 +31,7 @@ public:
|
||||
const GrPipeline&,
|
||||
const GrCaps&);
|
||||
|
||||
void prepare(GrMeshDrawTarget*,
|
||||
const SkRect& cullBounds,
|
||||
const PathDrawList&,
|
||||
int totalCombinedPathVerbCnt) override;
|
||||
void prepare(GrMeshDrawTarget*, const PathDrawList&, int totalCombinedPathVerbCnt) override;
|
||||
|
||||
|
||||
#if SK_GPU_V1
|
||||
|
@ -11,7 +11,6 @@
|
||||
#include "src/gpu/GrMeshDrawTarget.h"
|
||||
#include "src/gpu/GrResourceProvider.h"
|
||||
#include "src/gpu/geometry/GrPathUtils.h"
|
||||
#include "src/gpu/tessellate/CullTest.h"
|
||||
#include "src/gpu/tessellate/StrokeIterator.h"
|
||||
#include "src/gpu/tessellate/WangsFormula.h"
|
||||
|
||||
@ -33,13 +32,16 @@ class InstanceWriter {
|
||||
public:
|
||||
using ShaderFlags = StrokeTessellator::ShaderFlags;
|
||||
|
||||
InstanceWriter(const GrShaderCaps* shaderCaps, ShaderFlags shaderFlags,
|
||||
GrMeshDrawTarget* target, float matrixMaxScale, const SkRect& strokeCullBounds,
|
||||
const SkMatrix& viewMatrix, GrVertexChunkArray* patchChunks,
|
||||
size_t instanceStride, int minInstancesPerChunk)
|
||||
InstanceWriter(const GrShaderCaps* shaderCaps,
|
||||
ShaderFlags shaderFlags,
|
||||
GrMeshDrawTarget* target,
|
||||
float matrixMaxScale,
|
||||
const SkMatrix& viewMatrix,
|
||||
GrVertexChunkArray* patchChunks,
|
||||
size_t instanceStride,
|
||||
int minInstancesPerChunk)
|
||||
: fShaderCaps(shaderCaps)
|
||||
, fShaderFlags(shaderFlags)
|
||||
, fCullTest(strokeCullBounds, viewMatrix)
|
||||
, fChunkBuilder(target, patchChunks, instanceStride, minInstancesPerChunk)
|
||||
, fParametricPrecision(StrokeTolerances::CalcParametricPrecision(matrixMaxScale)) {
|
||||
}
|
||||
@ -149,14 +151,8 @@ private:
|
||||
void chopQuadraticTo(const SkPoint p[3]) {
|
||||
SkPoint chops[5];
|
||||
SkChopQuadAtHalf(p, chops);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
const SkPoint* q = chops + i*2;
|
||||
if (fCullTest.areVisible3(q)) {
|
||||
this->quadraticTo(q);
|
||||
} else {
|
||||
this->discardStroke(q, 3);
|
||||
}
|
||||
}
|
||||
this->quadraticTo(chops);
|
||||
this->quadraticTo(chops + 2);
|
||||
}
|
||||
|
||||
void chopConicTo(const SkConic& conic) {
|
||||
@ -164,26 +160,15 @@ private:
|
||||
if (!conic.chopAt(.5f, chops)) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
if (fCullTest.areVisible3(chops[i].fPts)) {
|
||||
this->conicTo(chops[i].fPts, chops[i].fW);
|
||||
} else {
|
||||
this->discardStroke(chops[i].fPts, 3);
|
||||
}
|
||||
}
|
||||
this->conicTo(chops[0].fPts, chops[0].fW);
|
||||
this->conicTo(chops[1].fPts, chops[1].fW);
|
||||
}
|
||||
|
||||
void chopCubicConvex180To(const SkPoint p[4]) {
|
||||
SkPoint chops[7];
|
||||
SkChopCubicAtHalf(p, chops);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
const SkPoint* c = chops + i*3;
|
||||
if (fCullTest.areVisible4(c)) {
|
||||
this->cubicConvex180To(c);
|
||||
} else {
|
||||
this->discardStroke(c, 4);
|
||||
}
|
||||
}
|
||||
this->cubicConvex180To(chops);
|
||||
this->cubicConvex180To(chops + 3);
|
||||
}
|
||||
|
||||
SK_ALWAYS_INLINE void writeStroke(const SkPoint p[4], SkPoint endControlPoint,
|
||||
@ -223,7 +208,6 @@ private:
|
||||
|
||||
const GrShaderCaps* fShaderCaps;
|
||||
const ShaderFlags fShaderFlags;
|
||||
const CullTest fCullTest;
|
||||
GrVertexChunkBuilder fChunkBuilder;
|
||||
const float fParametricPrecision;
|
||||
float fMaxParametricSegments_pow4 = 1;
|
||||
@ -258,11 +242,10 @@ StrokeFixedCountTessellator::StrokeFixedCountTessellator(const GrShaderCaps& sha
|
||||
ShaderFlags shaderFlags,
|
||||
const SkMatrix& viewMatrix,
|
||||
PathStrokeList* pathStrokeList,
|
||||
std::array<float,2> matrixMinMaxScales,
|
||||
const SkRect& strokeCullBounds)
|
||||
std::array<float,2> matrixMinMaxScales)
|
||||
: StrokeTessellator(shaderCaps, GrStrokeTessellationShader::Mode::kFixedCount, shaderFlags,
|
||||
kMaxParametricSegments_log2, viewMatrix, pathStrokeList,
|
||||
matrixMinMaxScales, strokeCullBounds) {
|
||||
matrixMinMaxScales) {
|
||||
}
|
||||
|
||||
GR_DECLARE_STATIC_UNIQUE_KEY(gVertexIDFallbackBufferKey);
|
||||
@ -278,8 +261,8 @@ void StrokeFixedCountTessellator::prepare(GrMeshDrawTarget* target, int totalCom
|
||||
int capPreallocCount = 8;
|
||||
int minInstancesPerChunk = strokePreallocCount + capPreallocCount;
|
||||
InstanceWriter instanceWriter(target->caps().shaderCaps(), fShader.flags(), target,
|
||||
fMatrixMinMaxScales[1], fStrokeCullBounds, fShader.viewMatrix(),
|
||||
&fInstanceChunks, fShader.instanceStride(), minInstancesPerChunk);
|
||||
fMatrixMinMaxScales[1], fShader.viewMatrix(), &fInstanceChunks,
|
||||
fShader.instanceStride(), minInstancesPerChunk);
|
||||
|
||||
if (!fShader.hasDynamicStroke()) {
|
||||
// Strokes are static. Calculate tolerances once.
|
||||
|
@ -21,8 +21,7 @@ public:
|
||||
ShaderFlags,
|
||||
const SkMatrix&,
|
||||
PathStrokeList*,
|
||||
std::array<float, 2> matrixMinMaxScales,
|
||||
const SkRect& strokeCullBounds);
|
||||
std::array<float, 2> matrixMinMaxScales);
|
||||
|
||||
void prepare(GrMeshDrawTarget*, int totalCombinedVerbCnt) override;
|
||||
#if SK_GPU_V1
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include "src/gpu/GrMeshDrawTarget.h"
|
||||
#include "src/gpu/GrRecordingContextPriv.h"
|
||||
#include "src/gpu/geometry/GrPathUtils.h"
|
||||
#include "src/gpu/tessellate/CullTest.h"
|
||||
#include "src/gpu/tessellate/WangsFormula.h"
|
||||
|
||||
#if SK_GPU_V1
|
||||
@ -58,11 +57,14 @@ public:
|
||||
kBowtie = SkPaint::kLast_Join + 1 // Double sided round join.
|
||||
};
|
||||
|
||||
PatchWriter(ShaderFlags shaderFlags, GrMeshDrawTarget* target,
|
||||
const SkRect& strokeCullBounds, const SkMatrix& viewMatrix, float matrixMaxScale,
|
||||
GrVertexChunkArray* patchChunks, size_t patchStride, int minPatchesPerChunk)
|
||||
PatchWriter(ShaderFlags shaderFlags,
|
||||
GrMeshDrawTarget* target,
|
||||
const SkMatrix& viewMatrix,
|
||||
float matrixMaxScale,
|
||||
GrVertexChunkArray* patchChunks,
|
||||
size_t patchStride,
|
||||
int minPatchesPerChunk)
|
||||
: fShaderFlags(shaderFlags)
|
||||
, fCullTest(strokeCullBounds, viewMatrix)
|
||||
, fChunkBuilder(target, patchChunks, patchStride, minPatchesPerChunk)
|
||||
// Subtract 2 because the tessellation shader chops every cubic at two locations, and
|
||||
// each chop has the potential to introduce an extra segment.
|
||||
@ -356,11 +358,6 @@ private:
|
||||
// tessellation patches.
|
||||
void internalConicPatchesTo(JoinType prevJoinType, const SkPoint p[3], float w,
|
||||
int maxDepth = -1) {
|
||||
if (!fCullTest.areVisible3(p)) {
|
||||
// The stroke is out of view. Discard it.
|
||||
this->discardStroke(p, 3);
|
||||
return;
|
||||
}
|
||||
// Zero-length paths need special treatment because they are spec'd to behave differently.
|
||||
// If the control point is colocated on an endpoint then this might end up being the case.
|
||||
// Fall back on a lineTo and let it make the final check.
|
||||
@ -440,11 +437,6 @@ private:
|
||||
// tessellation patches. The cubic must be convex and must not rotate more than 180 degrees.
|
||||
void internalCubicConvex180PatchesTo(JoinType prevJoinType, const SkPoint p[4],
|
||||
int maxDepth = -1) {
|
||||
if (!fCullTest.areVisible4(p)) {
|
||||
// The stroke is out of view. Discard it.
|
||||
this->discardStroke(p, 4);
|
||||
return;
|
||||
}
|
||||
// The stroke tessellation shader assigns special meaning to p0==p1==p2 and p1==p2==p3. If
|
||||
// this is the case then we need to rewrite the cubic.
|
||||
if (p[1] == p[2] && (p[1] == p[0] || p[1] == p[3])) {
|
||||
@ -629,7 +621,6 @@ private:
|
||||
}
|
||||
|
||||
const ShaderFlags fShaderFlags;
|
||||
const CullTest fCullTest;
|
||||
GrVertexChunkBuilder fChunkBuilder;
|
||||
|
||||
// The maximum number of tessellation segments the hardware can emit for a single patch.
|
||||
@ -710,11 +701,10 @@ StrokeHardwareTessellator::StrokeHardwareTessellator(const GrShaderCaps& shaderC
|
||||
ShaderFlags shaderFlags,
|
||||
const SkMatrix& viewMatrix,
|
||||
PathStrokeList* pathStrokeList,
|
||||
std::array<float,2> matrixMinMaxScales,
|
||||
const SkRect& strokeCullBounds)
|
||||
std::array<float,2> matrixMinMaxScales)
|
||||
: StrokeTessellator(shaderCaps, GrStrokeTessellationShader::Mode::kHardwareTessellation,
|
||||
shaderFlags, SkNextLog2(shaderCaps.maxTessellationSegments()),
|
||||
viewMatrix, pathStrokeList, matrixMinMaxScales, strokeCullBounds) {
|
||||
viewMatrix, pathStrokeList, matrixMinMaxScales) {
|
||||
}
|
||||
|
||||
void StrokeHardwareTessellator::prepare(GrMeshDrawTarget* target, int totalCombinedVerbCnt) {
|
||||
@ -724,9 +714,8 @@ void StrokeHardwareTessellator::prepare(GrMeshDrawTarget* target, int totalCombi
|
||||
int strokePreallocCount = totalCombinedVerbCnt * 5/4;
|
||||
int capPreallocCount = 8;
|
||||
int minPatchesPerChunk = strokePreallocCount + capPreallocCount;
|
||||
PatchWriter patchWriter(fShader.flags(), target, fStrokeCullBounds, fShader.viewMatrix(),
|
||||
fMatrixMinMaxScales[1], &fPatchChunks, fShader.vertexStride(),
|
||||
minPatchesPerChunk);
|
||||
PatchWriter patchWriter(fShader.flags(), target, fShader.viewMatrix(), fMatrixMinMaxScales[1],
|
||||
&fPatchChunks, fShader.vertexStride(), minPatchesPerChunk);
|
||||
|
||||
if (!fShader.hasDynamicStroke()) {
|
||||
// Strokes are static. Calculate tolerances once.
|
||||
|
@ -22,8 +22,7 @@ public:
|
||||
ShaderFlags shaderFlags,
|
||||
const SkMatrix& viewMatrix,
|
||||
PathStrokeList* pathStrokeList,
|
||||
std::array<float,2> matrixMinMaxScales,
|
||||
const SkRect& strokeCullBounds);
|
||||
std::array<float,2> matrixMinMaxScales);
|
||||
|
||||
void prepare(GrMeshDrawTarget*, int totalCombinedVerbCnt) override;
|
||||
#if SK_GPU_V1
|
||||
|
@ -36,13 +36,11 @@ public:
|
||||
int8_t maxParametricSegments_log2,
|
||||
const SkMatrix& viewMatrix,
|
||||
PathStrokeList* pathStrokeList,
|
||||
std::array<float, 2> matrixMinMaxScales,
|
||||
const SkRect& strokeCullBounds)
|
||||
std::array<float, 2> matrixMinMaxScales)
|
||||
: fShader(shaderCaps, shaderMode, shaderFlags, viewMatrix, pathStrokeList->fStroke,
|
||||
pathStrokeList->fColor, maxParametricSegments_log2)
|
||||
, fPathStrokeList(pathStrokeList)
|
||||
, fMatrixMinMaxScales(matrixMinMaxScales)
|
||||
, fStrokeCullBounds(strokeCullBounds) {
|
||||
, fMatrixMinMaxScales(matrixMinMaxScales) {
|
||||
}
|
||||
|
||||
const GrTessellationShader* shader() const { return &fShader; }
|
||||
@ -62,7 +60,6 @@ protected:
|
||||
GrStrokeTessellationShader fShader;
|
||||
PathStrokeList* fPathStrokeList;
|
||||
const std::array<float,2> fMatrixMinMaxScales;
|
||||
const SkRect fStrokeCullBounds; // See SkStrokeRec::inflationRadius.
|
||||
};
|
||||
|
||||
// These tolerances decide the number of parametric and radial segments the tessellator will
|
||||
|
@ -8,12 +8,120 @@
|
||||
#include "src/gpu/tessellate/Tessellation.h"
|
||||
|
||||
#include "include/core/SkPath.h"
|
||||
#include "src/core/SkGeometry.h"
|
||||
#include "src/core/SkPathPriv.h"
|
||||
#include "src/gpu/BufferWriter.h"
|
||||
#include "src/gpu/tessellate/CullTest.h"
|
||||
#include "src/gpu/tessellate/MiddleOutPolygonTriangulator.h"
|
||||
#include "src/gpu/tessellate/WangsFormula.h"
|
||||
|
||||
namespace skgpu {
|
||||
|
||||
namespace {
|
||||
|
||||
// Writes a new path, chopping as necessary so no verbs require more segments than
|
||||
// kMaxTessellationSegmentsPerCurve. Curves completely outside the viewport are flattened into
|
||||
// lines.
|
||||
class PathChopper {
|
||||
public:
|
||||
PathChopper(const SkMatrix& matrix, const SkRect& viewport)
|
||||
: fCullTest(viewport, matrix)
|
||||
, fVectorXform(matrix) {
|
||||
fPath.setIsVolatile(true);
|
||||
}
|
||||
|
||||
SkPath path() const { return fPath; }
|
||||
|
||||
void moveTo(SkPoint p) { fPath.moveTo(p); }
|
||||
void lineTo(SkPoint p1) { fPath.lineTo(p1); }
|
||||
void close() { fPath.close(); }
|
||||
|
||||
void quadTo(const SkPoint p[3]) {
|
||||
if (!fCullTest.areVisible3(p)) {
|
||||
this->lineTo(p[2]);
|
||||
return;
|
||||
}
|
||||
float n = wangs_formula::quadratic_pow4(kTessellationPrecision, p, fVectorXform);
|
||||
if (n > pow4(kMaxTessellationSegmentsPerCurve)) {
|
||||
SkPoint chops[5];
|
||||
SkChopQuadAtHalf(p, chops);
|
||||
this->quadTo(chops);
|
||||
this->quadTo(chops + 2);
|
||||
return;
|
||||
}
|
||||
fPath.quadTo(p[1], p[2]);
|
||||
}
|
||||
|
||||
void conicTo(const SkPoint p[3], float w) {
|
||||
if (!fCullTest.areVisible3(p)) {
|
||||
this->lineTo(p[2]);
|
||||
return;
|
||||
}
|
||||
float n = wangs_formula::conic_pow2(kTessellationPrecision, p, w, fVectorXform);
|
||||
if (n > pow2(kMaxTessellationSegmentsPerCurve)) {
|
||||
SkConic chops[2];
|
||||
if (!SkConic(p,w).chopAt(.5, chops)) {
|
||||
this->lineTo(p[2]);
|
||||
return;
|
||||
}
|
||||
this->conicTo(chops[0].fPts, chops[0].fW);
|
||||
this->conicTo(chops[1].fPts, chops[1].fW);
|
||||
return;
|
||||
}
|
||||
fPath.conicTo(p[1], p[2], w);
|
||||
}
|
||||
|
||||
void cubicTo(const SkPoint p[4]) {
|
||||
if (!fCullTest.areVisible4(p)) {
|
||||
this->lineTo(p[3]);
|
||||
return;
|
||||
}
|
||||
float n = wangs_formula::cubic_pow4(kTessellationPrecision, p, fVectorXform);
|
||||
if (n > pow4(kMaxTessellationSegmentsPerCurve)) {
|
||||
SkPoint chops[7];
|
||||
SkChopCubicAtHalf(p, chops);
|
||||
this->cubicTo(chops);
|
||||
this->cubicTo(chops + 3);
|
||||
return;
|
||||
}
|
||||
fPath.cubicTo(p[1], p[2], p[3]);
|
||||
}
|
||||
|
||||
private:
|
||||
const CullTest fCullTest;
|
||||
const wangs_formula::VectorXform fVectorXform;
|
||||
SkPath fPath;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
SkPath PreChopPathCurves(const SkPath& path, const SkMatrix& matrix, const SkRect& viewport) {
|
||||
PathChopper chopper(matrix, viewport);
|
||||
for (auto [verb, p, w] : SkPathPriv::Iterate(path)) {
|
||||
switch (verb) {
|
||||
case SkPathVerb::kMove:
|
||||
chopper.moveTo(p[0]);
|
||||
break;
|
||||
case SkPathVerb::kLine:
|
||||
chopper.lineTo(p[1]);
|
||||
break;
|
||||
case SkPathVerb::kQuad:
|
||||
chopper.quadTo(p);
|
||||
break;
|
||||
case SkPathVerb::kConic:
|
||||
chopper.conicTo(p, *w);
|
||||
break;
|
||||
case SkPathVerb::kCubic:
|
||||
chopper.cubicTo(p);
|
||||
break;
|
||||
case SkPathVerb::kClose:
|
||||
chopper.close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return chopper.path();
|
||||
}
|
||||
|
||||
VertexWriter WritePathMiddleOutInnerFan(VertexWriter&& vertexWriter,
|
||||
int pad32Count,
|
||||
uint32_t pad32Value,
|
||||
|
@ -11,8 +11,9 @@
|
||||
#include "include/core/SkTypes.h"
|
||||
#include "include/private/SkVx.h"
|
||||
|
||||
class SkPath;
|
||||
class SkMatrix;
|
||||
class SkPath;
|
||||
struct SkRect;
|
||||
|
||||
namespace skgpu {
|
||||
|
||||
@ -44,9 +45,21 @@ SK_MAYBE_UNUSED SK_ALWAYS_INLINE float cross(float2 a, float2 b) {
|
||||
return x[0] - x[1];
|
||||
}
|
||||
|
||||
// Writes out the path's inner fan using a middle-out topology. Writes 3 SkPoints per triangle to
|
||||
// the VertexWriter. Additionally writes out "pad32Count" repetitions of "pad32Value" after each
|
||||
// triangle. Set pad32Count to 0 if the triangles are to be tightly packed.
|
||||
SK_MAYBE_UNUSED constexpr SK_ALWAYS_INLINE float pow2(float x) { return x*x; }
|
||||
SK_MAYBE_UNUSED constexpr SK_ALWAYS_INLINE float pow4(float x) { return pow2(x*x); }
|
||||
|
||||
// Don't tessellate paths that might have an individual curve that requires more than 1024 segments.
|
||||
// (See wangs_formula::worst_case_cubic). If this is the case, call "PreChopPathCurves" first.
|
||||
constexpr static float kMaxTessellationSegmentsPerCurve SK_MAYBE_UNUSED = 1024;
|
||||
|
||||
// Returns a new path, equivalent to 'path' within the given viewport, whose verbs can all be drawn
|
||||
// with 'maxSegments' tessellation segments or fewer. Curves and chops that fall completely outside
|
||||
// the viewport are flattened into lines.
|
||||
SkPath PreChopPathCurves(const SkPath&, const SkMatrix&, const SkRect& viewport);
|
||||
|
||||
// Writes out the path's inner fan using a middle-out topology. Writes 3 points per triangle.
|
||||
// Additionally writes out "pad32Count" repetitions of "pad32Value" after each triangle. Set
|
||||
// pad32Count to 0 if the triangles are to be tightly packed.
|
||||
VertexWriter WritePathMiddleOutInnerFan(VertexWriter&&,
|
||||
int pad32Count,
|
||||
uint32_t pad32Value,
|
||||
|
@ -173,19 +173,25 @@ AI int cubic_log2(float precision,
|
||||
}
|
||||
|
||||
// Returns the maximum number of line segments a cubic with the given device-space bounding box size
|
||||
// would ever need to be divided into. This is simply a special case of the cubic formula where we
|
||||
// maximize its value by placing control points on specific corners of the bounding box.
|
||||
// would ever need to be divided into, raised to the 4th power. This is simply a special case of the
|
||||
// cubic formula where we maximize its value by placing control points on specific corners of the
|
||||
// bounding box.
|
||||
AI float worst_case_cubic_pow4(float precision, float devWidth, float devHeight) {
|
||||
float kk = length_term_pow2<3>(precision);
|
||||
return 4*kk * (devWidth * devWidth + devHeight * devHeight);
|
||||
}
|
||||
|
||||
// Returns the maximum number of line segments a cubic with the given device-space bounding box size
|
||||
// would ever need to be divided into.
|
||||
AI float worst_case_cubic(float precision, float devWidth, float devHeight) {
|
||||
float k = length_term<3>(precision);
|
||||
return sqrtf(2*k * SkVector::Length(devWidth, devHeight));
|
||||
return root4(worst_case_cubic_pow4(precision, devWidth, devHeight));
|
||||
}
|
||||
|
||||
// Returns the maximum log2 number of line segments a cubic with the given device-space bounding box
|
||||
// size would ever need to be divided into.
|
||||
AI int worst_case_cubic_log2(float precision, float devWidth, float devHeight) {
|
||||
float kk = length_term_pow2<3>(precision);
|
||||
// nextlog16(x) == ceil(log2(sqrt(sqrt(x))))
|
||||
return nextlog16(4*kk * (devWidth * devWidth + devHeight * devHeight));
|
||||
return nextlog16(worst_case_cubic_pow4(precision, devWidth, devHeight));
|
||||
}
|
||||
|
||||
// Returns Wang's formula specialized for a conic curve, raised to the second power.
|
||||
|
@ -342,6 +342,10 @@ DEF_TEST(wangs_formula_worst_case_cubic, r) {
|
||||
check_worst_case_cubic(pts);
|
||||
});
|
||||
}
|
||||
// Make sure overflow saturates at infinity (not NaN).
|
||||
constexpr static float inf = std::numeric_limits<float>::infinity();
|
||||
REPORTER_ASSERT(r, wangs_formula::worst_case_cubic_pow4(kPrecision, inf, inf) == inf);
|
||||
REPORTER_ASSERT(r, wangs_formula::worst_case_cubic(kPrecision, inf, inf) == inf);
|
||||
}
|
||||
|
||||
// Ensure Wang's formula for quads produces max error within tolerance.
|
||||
|
Loading…
Reference in New Issue
Block a user