Skip to content

Parallel rendering + per-stage timing#14

Open
dirlligafu wants to merge 6 commits into
6wheel:mainfrom
dirlligafu:perf-parallel-rendering
Open

Parallel rendering + per-stage timing#14
dirlligafu wants to merge 6 commits into
6wheel:mainfrom
dirlligafu:perf-parallel-rendering

Conversation

@dirlligafu

Copy link
Copy Markdown

What?

Parallelizes the two main CPU bottlenecks in the render pipeline and adds a per-stage timing display to the UI.

Why?

On a 385k-face model, a full render with vertex AO takes ~162s sequentially. SSAO takes ~74s on a mid-complexity model. Both are embarrassingly parallel: the 6 views are fully independent, and the AO ray-casting chunks are already sliced at 20k vertices each.

What it does

  • Runs all requested views concurrently via ProcessPoolExecutor. Separate processes are required because pyrender is not thread-safe (each process gets its own OpenGL context)
  • Parallelizes vertex AO ray-casting chunks across worker processes, raw numpy arrays are passed instead of trimesh objects for reliable cross-platform pickling
  • Displays a "Render timings" table below the generated image breaking down each stage individually

Results (Intel Core Ultra 7, 20 cores)

Mode Sequential Parallel Gain
No AO, 83k faces 13s 9s x1.5
SSAO, 83k faces 74s 26s x2.8
SSAO, 385k faces 86s 35s x2.5
Vertex AO, 385k faces ~162s ~117 x1.4

Changes

  • app.py: timing instrumentation + parallel view rendering (_render_view_worker)
  • renderer.py: parallel AO chunk ray-casting (_ao_chunk_worker)
  • templates/index.html: render timings display
  • PERF_NOTES.md (new): full methodology, baseline measurements, and tuning notes. Worth a read if you want the full picture behind these numbers.

Wrap each major rendering stage (AO precompute, per-view renders, SSAO
finalize, cross-sections, composition) with time.perf_counter(). Timings
are returned in the SSE done event and displayed in a table below the
generated image, so any user on this branch can reproduce the benchmark.

Add PERF_NOTES.md documenting baseline measurements across all four AO
modes, key findings (SSAO slower than vertex on large models, composition
4x slower with AO enabled), and the parallelization targets identified
for the next step.
Add measurements for a 45 MB / 385k-face model across all four AO modes.
Vertex AO precompute ran twice to confirm stability (111-140s range).
Document super-linear scaling of vertex AO with mesh density, and update
parallelization priorities accordingly: AO chunk parallelization becomes
highest priority on large models, not just view rendering.
Replace sequential render_view loop with ProcessPoolExecutor so all
requested views run concurrently, each in its own process with its own
OpenGL context. Progress events are streamed via as_completed() as each
view finishes.

Results on small model (83k faces): SSAO 74s -> 26s (x2.8), No AO
13s -> 9s (x1.5). On large model (385k faces): SSAO 86s -> 35s (x2.5),
No AO 25s -> 13s (x2.0). Vertex AO barely moves on large models because
the precompute (117s) dominates -- identified as next target.

Update PERF_NOTES.md with full before/after benchmark tables and revised
priority order for remaining parallelization targets.
…notes

Add _ao_chunk_worker() as a top-level function in renderer.py passing raw
numpy arrays instead of a trimesh object for reliable cross-platform
pickling. Replace the sequential chunk loop in _sample_ao_hemisphere()
with ProcessPoolExecutor, each worker re-initializing trimesh and the
embree intersector independently.

Worker count formula: round(cpu_count / 4) + 1. Tuned empirically on a
20-core Intel Core Ultra 7 265KF: embree already uses multiple threads
internally (OpenMP), so oversubscription degrades performance beyond ~6
concurrent processes. Tested at 4/6/8/19/formula workers -- 6 is the
sweet spot (84s precompute vs 125s sequential on 385k-face model).

Results on large model (385k faces): vertex AO total 162s -> 117s (-28%).
On small model (83k faces): precompute 3.59s -> 0.20s (x18).

Update PERF_NOTES.md with full worker-count benchmark table, final
results across both models, and remaining optimization opportunities.
Add regression test findings: all modes produce visually identical output
vs vanilla Ortho. Vertex AO parallel output differs by ~3 KB in PNG file
size due to per-chunk RNG seeding (seed + chunk index) vs the sequential
shared RNG. Expected and accepted tradeoff -- within 80-ray sampling noise,
visually indistinguishable, not a defect.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants