Speed up QuadSurface::gen() warps by hoisting the separable x axis#1076
Open
robert-kofler42 wants to merge 1 commit into
Open
Speed up QuadSurface::gen() warps by hoisting the separable x axis#1076robert-kofler42 wants to merge 1 commit into
robert-kofler42 wants to merge 1 commit into
Conversation
The four warp helpers (warpBilinearReplicateVec3f, warpNearestConstU8,
warpNearestConstVec3f, warpBilinearConstVec3f) implement an axis-aligned
scale+translate resample: src_x = ox + dx*sx, src_y = oy + dy*sy. The
mapping is separable, so the source x sample (x0/x1/wx for bilinear, the
rounded source index for nearest) depends only on dx, not dy -- yet the
inner loop recomputed it for every one of the dh rows, including an
std::lround per pixel in the nearest variants.
Hoist the x-axis computation into a per-column array computed once per
call and reuse it across all rows; also hoist iwy = 1 - wy to per-row.
Bit-identical for all finite warp parameters (the only kind gen()
produces): the same float expressions are evaluated in the same order,
just memoized. On non-finite source coordinates -- unreachable here --
the bilinear-const variant now writes the border value instead of the
original's out-of-bounds read, i.e. strictly safer.
Profiled with perf on a synthetic 4000x4000 surface: the warp helpers
were ~53% of gen() self-time, __lroundf alone 8.9%.
Benchmark (gen() with normals, median of 5, 32-core machine):
interactive (one gen() call, internally parallel):
512x512 tile: 2.54 -> 1.43 ms/tile (1.78x)
1024x1024 tile: 10.13 -> 6.09 ms/tile (1.66x)
tiled batch render (outer OMP over tiles): unchanged (~0.90 ms/tile,
memory-bandwidth bound).
Verified bit-identical via an FNV-1a checksum over all output float bits
(identical before/after) and the existing render/quadsurface unit tests.
|
Someone is attempting to deploy a commit to the scroll Team on Vercel. A member of the Team first needs to authorize it. |
This was referenced Jun 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
QuadSurface::gen()'s four warp helpers (warpBilinearReplicateVec3f,warpNearestConstU8,warpNearestConstVec3f,warpBilinearConstVec3f) implement an axis-aligned scale+translate resample:src_x = ox + dx*sx,src_y = oy + dy*sy.The mapping is separable: the source-x sample (
x0/x1/wxfor bilinear, the rounded source index for nearest) depends only ondx, notdy. But the inner loop recomputed it for every one of thedhrows — including anstd::lroundper pixel in the nearest variants. This hoists the x-axis computation into a per-column array computed once per call and reused across all rows (and hoistsiwy = 1 - wyto per-row).Correctness — bit-identical
Bit-identical for all finite warp parameters (the only kind
gen()produces): the same float expressions are evaluated in the same order, just memoized.Honest edge note: on non-finite source coordinates (not reachable from
gen(), whose warp params are always finite), the bilinear-const variant now writes thebordervalue instead of the original's out-of-bounds read (int(NaN)→row[INT_MIN]) — i.e. strictly safer, not a regression.Verified with:
18b53e9196982b08);Performance
Profiled with
perfon a synthetic 4000×4000 surface: the warp helpers were ~53% ofgen()self-time,__lroundfalone 8.9%.Benchmark (
gen()with normals, median of 5, 32-core machine,RelWithDebInfo):gen()call, internally parallel)The win lands on the compute-bound interactive render path (the VC3D viewer issues one
gen()per view, which parallelizes internally) — i.e. pan/zoom latency. The tiled batch renderer (vc_render_tifxyz) is memory-bandwidth bound and unchanged, but does the same total work with fewer instructions.Notes
main.std::vectors filled single-threaded before the row loop and read-only inside it (safe under the warps' internal OpenMP).