Document the single-crate app architecture, WGPU integration path, module boundaries, and next steps. Also clean up the terrain_gen test assertion so clippy stays green.
5.9 KiB
WGPU/egui Interactive App Architecture Spike
Goal: introduce an interactive OpenVistaPro app without destabilizing the existing CLI, importer pipeline, or deterministic CPU reference renderer.
Decision
Keep the repository as a single Rust crate for now.
The current boundary set is still moving, but it is already useful:
src/terrain.rs,src/terrain_gen.rs,src/import.rs,src/scene.rs,src/scene_file.rs,src/script.rs,src/script_exec.rs,src/path.rs,src/render.rs, andsrc/colormap.rsare the clean-room core.src/cli.rsandsrc/main.rsare the command-line adapter.src/app_state.rs,src/ui_shell.rs,src/app.rs, andsrc/bin/openvistapro_app.rsare the optional interactive shell.
A workspace split into openvistapro-core, openvistapro-cli, and openvistapro-app would add maintenance overhead before there is a second renderer/backend boundary that truly justifies it. The better near-term move is to keep a monolith and enforce boundaries with modules, feature flags, and tests.
Revisit a workspace split only when at least one of these becomes true:
- the GPU renderer needs materially different dependencies from the CLI/reference renderer,
- the core API stabilizes enough that the app shell can depend on it as a published internal crate boundary, or
- build times / dependency fanout make the single-crate layout painful.
Integration recommendation
Use egui as the interactive shell now, and treat WGPU as the rendering backend that arrives later behind a narrow viewport abstraction.
Recommended path:
- Keep
eframefor the windowing / egui host while the shell is still mostly controls and state. - Keep the current CPU renderer as the canonical reference output for tests and CLI smoke runs.
- Introduce a tiny viewport trait or adapter boundary before any full GPU renderer lands.
- Add a WGPU-backed viewport only once the render pipeline actually needs GPU surfaces, textures, and custom passes.
Why this order:
eframealready gives a working egui host with the least glue.- CLI builds stay GPU-free because the app stays behind the
appfeature. - The CPU renderer remains the reference implementation for determinism and testability.
- WGPU can then become an implementation detail of the viewport, not a rewrite of the app state model.
Module boundaries
Keep the following responsibilities separate:
Core domain
terrain.rs: immutable height grid and sampling rules.terrain_gen.rs: deterministic procedural generation inputs and output grid creation.import.rs: import boundary and source metadata.scene.rs: camera, lighting, hydrology, and palette-linked scene state.scene_file.rs:.ovp.tomlpersistence.script.rs/script_exec.rs: project-owned script language and executor.path.rs: MakePath-style camera path generation.render.rs: deterministic CPU render outputs.colormap.rs: palette / band mapping.
CLI adapter
cli.rsshould stay focused on argument parsing and orchestration.- Keep it thin enough that the same core operations can be called from future automation or from the app shell.
Interactive shell state
app_state.rsshould remain pure state plus reducers / actions.ui_shell.rsshould own section ordering, labels, and placeholder metadata.- Avoid leaking egui types into either file.
egui view/controller
app.rsshould remain the presentation layer that turnsAppDatainto panels, menus, and a preview texture.- The shell should consume state snapshots and call backend actions, not embed render or import logic inline.
Future GPU viewport
- Introduce a narrow backend boundary for viewport rendering before the first custom WGPU renderer lands.
- Keep surface creation, texture upload, and draw passes out of
AppData. - Prefer a dedicated GPU adapter module over scattering WGPU setup across the UI code.
Minimal first app shell
The smallest durable shell is:
- a stable top command bar,
- a left or right dock for section-specific controls,
- a central viewport that can show the CPU preview now and a GPU surface later,
- a status bar for file paths, script status, and render feedback,
- a tiny about/help dialog for orientation.
That is already enough to validate layout, interaction, and state management without pretending the GPU renderer exists yet.
Testing strategy
Keep the test surface split the same way as the code.
Unit tests
AppDatareducers and state transitions.UiShellStatenavigation order and section metadata.- render-mode selection and preview-state plumbing.
- scene / script / path / importer behavior.
Smoke commands
Use existing CLI and app entry points as smoke tests for the architecture:
cargo run -- infocargo run -- render --preset fractal --seed 1337 --width 64 --height 64 --output /tmp/openvistapro-fractal.pngcargo run --features app --bin openvistapro_appwhen a display is available
Later integration tests
Once the GPU viewport exists, add lightweight screenshot or frame-smoke coverage around the viewport adapter rather than baking WGPU assumptions into the core state model.
Next tasks
- Add a small viewport abstraction so the egui shell can switch between CPU preview and a future GPU backend without changing
AppData. - Keep the CPU renderer as the reference implementation for deterministic validation.
- Add a dedicated GPU adapter module only when the WGPU surface truly exists.
- Revisit a workspace split after the GPU path forces a second hard boundary.
- Keep
docs/knowledgebase/architecture-notes.mdanddocs/knowledgebase/ui-panel-map.mdaligned with any later shell or viewport changes.
Recommendation summary
Stay monolithic now, keep egui as the host shell, and treat WGPU as a future viewport backend rather than the reason to split the crate today. That gives OpenVistaPro the fastest path to a usable interactive app while protecting the CLI and deterministic renderer from churn.