Make GrWangsFormula::conic take "precision" instead of "tolerance"
Rearranges the algebra to use "precision". This makes it consistent with the other formulas and removes the need for the caller to think about inverting its precision value. Bug: skia:10419 Change-Id: I186d03393952983e86b0bef69e1f89f86cdbb423 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/414616 Commit-Queue: Chris Dalton <csmartdalton@google.com> Reviewed-by: Tyler Denniston <tdenniston@google.com>
This commit is contained in:
parent
2da6c9cefc
commit
e6f45318ef
@ -161,13 +161,11 @@ DEF_PATH_TESS_BENCH(wangs_formula_cubic_log2_affine, make_cubic_path(18),
|
||||
}
|
||||
|
||||
static void benchmark_wangs_formula_conic(const SkMatrix& matrix, const SkPath& path) {
|
||||
// Conic version expects tolerance, not "precision"
|
||||
constexpr float kTolerance = 4;
|
||||
int sum = 0;
|
||||
GrVectorXform xform(matrix);
|
||||
for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
|
||||
if (verb == SkPathVerb::kConic) {
|
||||
sum += GrWangsFormula::conic(kTolerance, pts, *w, xform);
|
||||
sum += GrWangsFormula::conic(4, pts, *w, xform);
|
||||
}
|
||||
}
|
||||
// Don't let the compiler optimize away GrWangsFormula::conic.
|
||||
@ -177,13 +175,11 @@ static void benchmark_wangs_formula_conic(const SkMatrix& matrix, const SkPath&
|
||||
}
|
||||
|
||||
static void benchmark_wangs_formula_conic_log2(const SkMatrix& matrix, const SkPath& path) {
|
||||
// Conic version expects tolerance, not "precision"
|
||||
constexpr float kTolerance = 4;
|
||||
int sum = 0;
|
||||
GrVectorXform xform(matrix);
|
||||
for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
|
||||
if (verb == SkPathVerb::kConic) {
|
||||
sum += GrWangsFormula::conic_log2(kTolerance, pts, *w, xform);
|
||||
sum += GrWangsFormula::conic_log2(4, pts, *w, xform);
|
||||
}
|
||||
}
|
||||
// Don't let the compiler optimize away GrWangsFormula::conic.
|
||||
|
@ -126,12 +126,12 @@ SK_ALWAYS_INLINE static int worst_case_cubic_log2(float precision, float devWidt
|
||||
}
|
||||
|
||||
// Returns Wang's formula specialized for a conic curve, raised to the second power.
|
||||
// Input points should be in projected space, and note tolerance parameter is not "precision".
|
||||
// Input points should be in projected space.
|
||||
//
|
||||
// This is not actually due to Wang, but is an analogue from (Theorem 3, corollary 1):
|
||||
// J. Zheng, T. Sederberg. "Estimating Tessellation Parameter Intervals for
|
||||
// Rational Curves and Surfaces." ACM Transactions on Graphics 19(1). 2000.
|
||||
SK_ALWAYS_INLINE static float conic_pow2(float tolerance, const SkPoint pts[], float w,
|
||||
SK_ALWAYS_INLINE static float conic_pow2(float precision, const SkPoint pts[], float w,
|
||||
const GrVectorXform& vectorXform = GrVectorXform()) {
|
||||
using grvx::dot, grvx::float2, grvx::float4, skvx::bit_pun;
|
||||
float2 p0 = vectorXform(bit_pun<float2>(pts[0]));
|
||||
@ -152,13 +152,13 @@ SK_ALWAYS_INLINE static float conic_pow2(float tolerance, const SkPoint pts[], f
|
||||
|
||||
// Compute forward differences
|
||||
const float2 dp = grvx::fast_madd<2>(-2 * w, p1, p0) + p2;
|
||||
const float dw = fabsf(1 - 2 * w + 1);
|
||||
const float dw = fabsf(-2 * w + 2);
|
||||
|
||||
// Compute numerator and denominator for parametric step size of linearization
|
||||
const float r_minus_eps = std::max(0.f, max_len - tolerance);
|
||||
const float min_w = std::min(w, 1.f);
|
||||
const float numer = sqrtf(grvx::dot(dp, dp)) + r_minus_eps * dw;
|
||||
const float denom = 4 * min_w * tolerance;
|
||||
// Compute numerator and denominator for parametric step size of linearization. Here, the
|
||||
// epsilon referenced from the cited paper is 1/precision.
|
||||
const float rp_minus_1 = std::max(0.f, max_len * precision - 1);
|
||||
const float numer = sqrtf(grvx::dot(dp, dp)) * precision + rp_minus_1 * dw;
|
||||
const float denom = 4 * std::min(w, 1.f);
|
||||
|
||||
// Number of segments = sqrt(numer / denom).
|
||||
// This assumes parametric interval of curve being linearized is [t0,t1] = [0, 1].
|
||||
|
@ -44,7 +44,7 @@ public:
|
||||
}
|
||||
|
||||
SK_ALWAYS_INLINE void writeConic(GrVertexChunkBuilder* chunker, const SkPoint p[3], float w) {
|
||||
if (GrWangsFormula::conic_pow2(1/kPrecision, p, w, fVectorXform) > fMaxSegments_pow2) {
|
||||
if (GrWangsFormula::conic_pow2(kPrecision, p, w, fVectorXform) > fMaxSegments_pow2) {
|
||||
this->chopAndWriteConic(chunker, {p, w});
|
||||
return;
|
||||
}
|
||||
|
@ -32,12 +32,12 @@ GrPathIndirectTessellator::GrPathIndirectTessellator(GrPathTessellationShader* s
|
||||
for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
|
||||
int level;
|
||||
switch (verb) {
|
||||
case SkPathVerb::kConic:
|
||||
level = GrWangsFormula::conic_log2(1/kPrecision, pts, *w, xform);
|
||||
break;
|
||||
case SkPathVerb::kQuad:
|
||||
level = GrWangsFormula::quadratic_log2(kPrecision, pts, xform);
|
||||
break;
|
||||
case SkPathVerb::kConic:
|
||||
level = GrWangsFormula::conic_log2(kPrecision, pts, *w, xform);
|
||||
break;
|
||||
case SkPathVerb::kCubic:
|
||||
level = GrWangsFormula::cubic_log2(kPrecision, pts, xform);
|
||||
break;
|
||||
@ -171,12 +171,12 @@ void GrPathIndirectTessellator::prepare(GrMeshDrawOp::Target* target, const SkRe
|
||||
switch (verb) {
|
||||
default:
|
||||
continue;
|
||||
case SkPathVerb::kConic:
|
||||
level = GrWangsFormula::conic_log2(1/kPrecision, pts, *w, xform);
|
||||
break;
|
||||
case SkPathVerb::kQuad:
|
||||
level = GrWangsFormula::quadratic_log2(kPrecision, pts, xform);
|
||||
break;
|
||||
case SkPathVerb::kConic:
|
||||
level = GrWangsFormula::conic_log2(kPrecision, pts, *w, xform);
|
||||
break;
|
||||
case SkPathVerb::kCubic:
|
||||
level = GrWangsFormula::cubic_log2(kPrecision, pts, xform);
|
||||
break;
|
||||
|
@ -140,7 +140,7 @@ public:
|
||||
|
||||
SK_ALWAYS_INLINE void writeConicWedge(GrVertexChunkBuilder* chunker, const SkPoint p[3],
|
||||
float w, SkPoint midpoint) {
|
||||
if (GrWangsFormula::conic_pow2(1/kPrecision, p, w, fVectorXform) > fMaxSegments_pow2) {
|
||||
if (GrWangsFormula::conic_pow2(kPrecision, p, w, fVectorXform) > fMaxSegments_pow2) {
|
||||
this->chopAndWriteConicWedges(chunker, {p, w}, midpoint);
|
||||
return;
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ public:
|
||||
}
|
||||
|
||||
SK_ALWAYS_INLINE void conicTo(const SkPoint p[3], float w) {
|
||||
float n = GrWangsFormula::conic_pow2(1/fParametricPrecision, p, w);
|
||||
float n = GrWangsFormula::conic_pow2(fParametricPrecision, p, w);
|
||||
float numParametricSegments_pow4 = n*n;
|
||||
if (numParametricSegments_pow4 > kMaxParametricSegments_pow4) {
|
||||
this->chopConicTo({p, w});
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include "src/gpu/geometry/GrWangsFormula.h"
|
||||
#include "tests/Test.h"
|
||||
|
||||
constexpr static int kPrecision = 4; // 1/4 pixel max error.
|
||||
constexpr static float kPrecision = 4; // 1/4 pixel max error.
|
||||
|
||||
const SkPoint kSerp[4] = {
|
||||
{285.625f, 499.687f}, {411.625f, 808.188f}, {1064.62f, 135.688f}, {1042.63f, 585.187f}};
|
||||
@ -401,7 +401,7 @@ DEF_TEST(WangsFormula_rational_quad_reduces, r) {
|
||||
SkRandom rand;
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
for_random_beziers(3, &rand, [&r](const SkPoint pts[]) {
|
||||
const float rational_nsegs = wangs_formula_conic_reference_impl(kPrecision, pts, 1.f);
|
||||
const float rational_nsegs = GrWangsFormula::conic(kPrecision, pts, 1.f);
|
||||
const float integral_nsegs = wangs_formula_quadratic_reference_impl(kPrecision, pts);
|
||||
REPORTER_ASSERT(
|
||||
r, SkScalarNearlyEqual(rational_nsegs, integral_nsegs, kTessellationTolerance));
|
||||
@ -446,8 +446,7 @@ DEF_TEST(WangsFormula_conic_within_tol, r) {
|
||||
for_random_beziers(
|
||||
3, &rand,
|
||||
[&](const SkPoint pts[]) {
|
||||
const int nsegs = static_cast<int>(
|
||||
std::ceil(wangs_formula_conic_reference_impl(kPrecision, pts, w)));
|
||||
const int nsegs = SkScalarCeilToInt(GrWangsFormula::conic(kPrecision, pts, w));
|
||||
|
||||
const float tdelta = 1.f / nsegs;
|
||||
for (int j = 0; j < nsegs; ++j) {
|
||||
@ -474,14 +473,12 @@ DEF_TEST(WangsFormula_conic_within_tol, r) {
|
||||
|
||||
// Ensure the vectorized conic version equals the reference implementation
|
||||
DEF_TEST(WangsFormula_conic_matches_reference, r) {
|
||||
constexpr static float kTolerance = 1.f / kPrecision;
|
||||
|
||||
SkRandom rand;
|
||||
for (int i = -10; i <= 10; ++i) {
|
||||
const float w = std::ldexp(1 + rand.nextF(), i);
|
||||
for_random_beziers(3, &rand, [&r, w](const SkPoint pts[]) {
|
||||
const float ref_nsegs = wangs_formula_conic_reference_impl(kPrecision, pts, w);
|
||||
const float nsegs = GrWangsFormula::conic(kTolerance, pts, w);
|
||||
const float nsegs = GrWangsFormula::conic(kPrecision, pts, w);
|
||||
|
||||
// Because the Gr version may implement the math differently for performance,
|
||||
// allow different slack in the comparison based on the rough scale of the answer.
|
||||
@ -493,13 +490,11 @@ DEF_TEST(WangsFormula_conic_matches_reference, r) {
|
||||
|
||||
// Ensure using transformations gives the same result as pre-transforming all points.
|
||||
DEF_TEST(WangsFormula_conic_vectorXforms, r) {
|
||||
constexpr static float kTolerance = 1.f / kPrecision;
|
||||
|
||||
auto check_conic_with_transform = [&](const SkPoint* pts, float w, const SkMatrix& m) {
|
||||
SkPoint ptsXformed[3];
|
||||
m.mapPoints(ptsXformed, pts, 3);
|
||||
float expected = GrWangsFormula::conic(kTolerance, ptsXformed, w);
|
||||
float actual = GrWangsFormula::conic(kTolerance, pts, w, GrVectorXform(m));
|
||||
float expected = GrWangsFormula::conic(kPrecision, ptsXformed, w);
|
||||
float actual = GrWangsFormula::conic(kPrecision, pts, w, GrVectorXform(m));
|
||||
REPORTER_ASSERT(r, SkScalarNearlyEqual(actual, expected));
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user