Skip to content

Architecture

Marko Koljancic edited this page May 28, 2026 · 4 revisions

Home

Architecture

A contributor on-ramp. Closes the page knowing where each piece of Solarxy lives in the seven-crate workspace, how the render pipeline composes, and which file to open when adding a feature in a given area.

For the deepest patterns - GPU uniform alignment discipline, MSAA boundary nuances, the IBL update chokepoint, dock layout serde compatibility caveats - see the in-repo CLAUDE.md. This page is the structural orientation; that file is the deeper code-side reference, maintained alongside the code.

The seven crates

Solarxy is a workspace of seven crates. The GPU-free crates (solarxy-core, solarxy-formats) sit at the bottom; the GPU-bound and UI-bound crates layer on top. This boundary lets the CLI link the analyzer without pulling wgpu, winit, or egui.

Crate What it owns
solarxy (root) Thin GUI entrypoint binary. Parses GuiArgs, sets up tracing, loads preferences, calls solarxy_app::run_viewer.
solarxy-core Pure CPU types: geometry primitives, validation logic, preferences I/O, view-config enums, JSON / report shapes, review sidecar. No GPU.
solarxy-formats Format loaders (OBJ, STL, PLY, glTF / GLB) returning RawModelData. Integration tests live alongside under tests/loaders.rs.
solarxy-renderer All wgpu state - pipelines, bind groups, shaders, IBL, SSAO, bloom, shadow, composite, camera, per-frame draw orchestration.
solarxy-app winit + egui application shell. Owns State, input routing, sidebar, menu bar, dock, console.
solarxy-cli clap-driven CLI + ratatui Analyze TUI + the analyzer + the optional self-updater.
solarxy-validate Batch validation orchestration + CI pipeline adapters (GitHub Actions, generic-JSON). Library API consumed by solarxy-cli and by external vendors.

The version is single-sourced in [workspace.package] and inherited by every crate via version.workspace = true.

Dependency DAG

The seven crates form an acyclic dependency graph. No crate depends upward; solarxy-core and solarxy-formats are pure data / algo utilities.

flowchart BT
    classDef base fill:#1F2430,stroke:#5C6773,color:#CCCAC2
    classDef mid fill:#1F2430,stroke:#78A0EE,color:#78A0EE
    classDef top fill:#1F2430,stroke:#FFC44C,color:#FFC44C

    core[solarxy-core<br/>CPU types, validation]:::base
    formats[solarxy-formats<br/>OBJ STL PLY glTF loaders]:::base
    renderer[solarxy-renderer<br/>wgpu pipelines, shaders]:::mid
    validate[solarxy-validate<br/>batch + CI adapters]:::mid
    app[solarxy-app<br/>winit + egui shell]:::mid
    cli[solarxy-cli<br/>clap + TUI + updater]:::top
    root[solarxy root<br/>GUI entrypoint]:::top

    formats --> core
    renderer --> core
    renderer --> formats
    validate --> core
    validate --> formats
    app --> core
    app --> formats
    app --> renderer
    cli --> core
    cli --> formats
    cli --> validate
    root --> core
    root --> app
Loading

Foundation crates at the bottom, GPU- and UI-bound layers on top. solarxy-core and solarxy-formats carry no GPU dependencies so the CLI can link them without pulling wgpu, winit, or egui.

The acyclic shape is what lets solarxy-cli ship without wgpu, winit, or egui dependencies. The CLI's analyzer feature gate pulls in solarxy-formats and solarxy-validate only when analyze mode is needed.

The render pipeline

flowchart LR
    classDef stage fill:#1F2430,stroke:#5C6773,color:#CCCAC2
    classDef msaa fill:#1F2430,stroke:#FFC44C,color:#FFC44C
    classDef post fill:#1F2430,stroke:#78A0EE,color:#78A0EE
    classDef ui fill:#1F2430,stroke:#7FD962,color:#7FD962
    classDef gate fill:#33415E,stroke:#FFC44C,color:#FFC44C

    s1[1. Shadow pass]:::stage --> s2[2. GBuffer<br/>if SSAO on]:::stage
    s2 --> s3[3. Background]:::msaa
    s3 --> s4[4. Main pass<br/>PBR + Inspection]:::msaa
    s4 --> s5[5. Wireframe<br/>overlays]:::msaa
    s5 --> s6[6. Visualization<br/>overlays]:::msaa
    s6 --> R{MSAA resolve}:::gate
    R --> s7[7. SSAO]:::post
    R --> s8[8. Bloom]:::post
    s7 --> s9[9. Composite<br/>+ tone map]:::post
    s8 --> s9
    s9 --> s10[10. egui overlay<br/>+ review markers]:::ui
Loading

Stages 3-6 run inside the MSAA main pass; resolve happens before SSAO and Bloom read the HDR target. Review markers draw in the egui overlay after composite, so they never z-fight with post-processing.

Per pane, per frame, ten ordered stages. The top-level orchestration lives in crates/solarxy-renderer/src/frame.rs; the per-pane app-side call sequencing lives in crates/solarxy-app/src/state/render.rs.

  1. Shadow pass - depth from the key light (shaders/shadow.wgsl).
  2. GBuffer pass - position + normal buffers (shaders/gbuffer.wgsl), only when SSAO is on.
  3. Background - gradient or HDRI sky (shaders/background.wgsl / skybox.wgsl).
  4. Main pass - PBR with inspection-mode switch (shaders/shader.wgsl).
  5. Wireframe overlays - inside the MSAA main pass (shaders/edge_wire.wgsl, ghosted.wgsl).
  6. Visualization overlays - grid, normals, axes, bounds, validation highlights (grid.wgsl, normals.wgsl, gizmo.wgsl, validation.wgsl).
  7. SSAO post-process - reads the resolved HDR target (ssao.wgsl, ssao_blur.wgsl).
  8. Bloom post-process - reads the resolved HDR target (bloom.wgsl).
  9. Composite - tone mapping + viewport scissor (composite.wgsl).
  10. egui overlay - menu bar, panels, status bar, and review markers (drawn as egui shapes, after composite, so they are never z-fought by post-processing).

UV-Map panes follow a parallel path through uv_map.wgsl, uv_debug.wgsl, and uv_overlap.wgsl; they skip SSAO and Bloom and do not participate in the review-marker overlay.

State flow

The central app struct is State, defined in crates/solarxy-app/src/state/mod.rs. It owns:

  • The viewport panes (compute_panes, the layout math for F1-F5).
  • The camera rig (one per pane, all sharing the same orbit interface).
  • The loaded model's GPU scene (ModelScene in scene.rs).
  • The input handler (keyboard, mouse, dialogs, menu actions; lives under state/input/).
  • The Review System state - the in-memory mirror of the current .solarxy-review.json (state/review.rs).
  • The display, rendering, and lighting view config.

Sidebar <-> state synchronization goes through GuiSnapshot in crates/solarxy-app/src/gui/snapshot.rs. Adding a sidebar control means adding a field to GuiSnapshot and wiring both from_state (read) and the matching write_back_* (write).

Dock layout persistence

flowchart LR
    classDef auto fill:#1F2430,stroke:#78A0EE,color:#78A0EE
    classDef manual fill:#1F2430,stroke:#FFC44C,color:#FFC44C
    classDef def fill:#1F2430,stroke:#5C6773,color:#CCCAC2
    classDef active fill:#33415E,stroke:#FFC44C,color:#FFC44C

    DEF[default_dock_state]:::def
    LAST[last_layout_json<br/>auto, on quit]:::auto
    SAVED[saved_layout_json<br/>manual, Save Layout]:::manual
    ACTIVE[Active layout<br/>in memory]:::active

    DEF -->|first launch| ACTIVE
    LAST -->|next launch restore| ACTIVE
    ACTIVE -->|on quit| LAST
    ACTIVE -->|Save Layout| SAVED
    SAVED -->|Restore Saved Layout| ACTIVE
    ACTIVE -->|Reset Layout to Default| DEF
Loading

Three persisted layout states and the three menu actions that move between them. Reset Layout never overwrites a saved blob.

The viewport and all six panels (Sidebar, Review Panel, Console, Material Inspector, Properties, Outliner) live as tabs in a single egui_dock::DockState<SolarxyTab> owned by EguiRenderer in crates/solarxy-app/src/gui/renderer.rs. The tab variants are defined in crates/solarxy-app/src/gui/dock.rs.

Two persisted JSON blobs in Preferences::dock:

Field Written by Read by
last_layout_json Auto, on app quit Startup - restores the window exactly as you left it
saved_layout_json Manual, Layout → Save Layout Layout → Restore Saved Layout

Layout → Reset Layout to Default rebuilds from default_dock_state in gui/dock.rs without touching either persisted blob - a safe escape hatch when a layout gets wedged.

The validation library boundary

solarxy-validate is the validation orchestration library. It is consumable as a Rust library by external vendors who need structured validation results without a subprocess - DAM systems, training pipelines, asset farms.

Its public wire format is ValidationRunReport. The format is versioned via a schema_version: u32 field and is intended to remain semver-stable across patch releases. Vendors should pin to a major version of the crate, or consume the JSON over the CLI boundary (which is also stable).

The crate ships with two pipeline adapters (adapters.rs):

  • generic - JSON / Text / TAP / JUnit XML for any CI system.
  • github-actions - workflow commands + SARIF for GitHub-native annotation.

Adding a new adapter means a PipelineAdapter impl and a matching AdapterName variant; see Where to look for X.

Where to look for X

A starting-point map. Each row names the directory and one or two key files; line numbers are deliberately omitted to keep this page maintainable across refactors.

I want to... Start in
Add a sidebar control crates/solarxy-app/src/gui/sidebar.rs + gui/snapshot.rs
Add a validation check crates/solarxy-core/src/validation/ (one file per check) + validation/mod.rs for dispatch
Add an inspection / shading mode crates/solarxy-core/src/preferences.rs (the enum) + shader.wgsl (the shader switch)
Add a CLI flag crates/solarxy-cli/src/parser.rs
Add a dock tab crates/solarxy-app/src/gui/dock.rs - mind the egui_dock serde compatibility for users' persisted last_layout_json
Add a CI pipeline adapter crates/solarxy-validate/src/adapters.rs
Add a file-format loader crates/solarxy-formats/src/ (one file per format); integration tests in tests/loaders.rs
Add a menu entry crates/solarxy-app/src/gui/menu.rs + gui/actions.rs (the action enum)
Change a render pipeline stage crates/solarxy-renderer/src/frame.rs (the call sequencing) + the relevant shader in shaders/

See also: Contributing · Release Notes · Configuration · Validation Reference

Clone this wiki locally