Shader rework #775

Merged
SakulFlee merged 48 commits from shader-rework into main 2026-09-07 00:03:33 +00:00
Owner

This changes the shader system by splitting it up into individual "nodes" which can be chained together. Think of it as a in-source/code node graph. Later, we can extend this with a visual "Node editor".

This changes the shader system by splitting it up into individual "nodes" which can be chained together. Think of it as a in-source/code node graph. Later, we can extend this with a visual "Node editor".
Replace the runtime filesystem-based shader loading with a code-defined
node system. Each WGSL function/struct/constant is a named ShaderNode
that can be assembled at build time via ShaderBuilder.

New modules:
- node.rs: ShaderNode with name, source, and dependency tracking
- library.rs: NodeLibrary for grouping related nodes
- registry.rs: NodeRegistry with global lazy-init and idempotent registration
- builder.rs: ShaderBuilder with dependency-cycle detection and debug flag
- prelude.rs: Engine's built-in node library (30 nodes: constants, structs, BRDF functions, sky math)

The prelude replaces the old #import preprocessor. Android-portability
goal: no filesystem-based shader loading at runtime.
ShaderDescriptor trait for node-based shader assembly, VariableType/Variables
for bind-group building, ShaderError type. Replaces the old ShaderSource enum
and #import preprocessor with node-graph-based assembly.
Texture, TextureDescriptor, TextureSize, FilterMode, BufferDescriptor,
TextureError. Extracted from orbital_resources.
Transform, projection utilities (perspective_wgpu, ortho_wgpu), Mode<T>
generic (moved from orbital_camera). Extracted from orbital_resources.
Vertex type with position, normal, tangent, bitangent, uv and their
GPU buffer layout descriptors. Extracted from orbital_resources.
22 WGSL node files for math utilities: distribution_ggx, schlick_smith_ggx,
fresnel_schlick_roughness, radical_inverse, hammersley, importance sampling,
slope_scaled_bias, luminance, etc. Each function is its own node.
16 WGSL node files for engine-wide structs and layouts: CameraUniform,
Light, VertexData, InstanceData, FragmentData, ShadowSlot, sky_params,
sky_color, etc. Shared across all material shaders.
12 PBR-specific WGSL nodes (BRDF orchestration, material data gathering,
IBL, shadow sampling) plus PBRMaterialShaderDescriptor that assembles
the full PBR material shader from reusable node libraries.
Wireframe debug shader with node-based assembly. wireframe_descriptor()
returns a MaterialShaderDescriptor using reusable engine/math nodes.
MaterialShader, MaterialShaderDescriptor (node-based), MaterialShaderCache,
VertexStageLayout, make_world_bind_group_layout. The central material
shader system that assembles WGSL from named node libraries.
Camera, CameraDescriptor, CameraChange, Frustum. Re-exports Mode<T>
from orbital_math for backward compatibility. Extracted from orbital_resources.
CullResources with inline GPU frustum-cull compute shader.
Extracted from orbital_resources.
Fine-grained crates extracted from orbital_resources:
- orbital_ibl_brdf: IBL BRDF LUT generation shader
- orbital_instance: Instance type and InstanceDescriptor
- orbital_light: LightType and LightDescriptor
- orbital_mesh: Mesh, MeshDescriptor, bounds, cache
- orbital_model: Model and ModelDescriptor
- orbital_shadow: ShadowRenderer and shadow depth shader
WorldEnvironment, descriptors, error types, cache_file, sampling/skybox types,
9 WGSL shader files for sky generation, IBL, and material shaders.
Extracted from orbital_resources.
Delete all old source modules (camera, texture, light, mesh, etc.) and
convert lib.rs to a thin re-export layer: pub use <crate>::* for all
15 sub-crates. Provides backward compatibility for existing imports.
Switch orbital_procgeo, orbital_app, orbital_renderer, orbital_ecs_bridge,
orbital_importer_gltf, and orbital_debug_render to import directly from
specific crates (orbital_camera, orbital_light, etc.) instead of
orbital_resources.
Remove monolithic shader files that have been decomposed into node-based
equivalents in the new crate structure:
- pbr.wgsl (598 lines) → 14 node files in orbital_shader_pbr
- default.wgsl → orbital_material_shader/src/default_shader.wgsl
- wireframe.wgsl → orbital_shader_debug::wireframe_descriptor()
- test.wgsl, instance_cull.wgsl → unreferenced

Keep shadow_depth.wgsl (actively used by orbital_shadow via include_str!).
Add 5 new validation tests (19 total, up from 16):
- every_pbr_node_is_valid_with_its_dependencies: validates self-contained
  PBR nodes individually with transitive dependency resolution
- node_assembled_pbr_shader_is_valid_wgsl: validates full PBR shader
  assembled from 34 named nodes + entrypoint raw source
- node_assembled_world_env_texture_is_valid_wgsl: validates assembled
  texture-based skybox material shader
- node_assembled_world_env_analytic_is_valid_wgsl: validates assembled
  analytic skybox material shader

Also fix fresnel_schlick signature in prelude (was incorrectly taking
PBRData, now correctly takes F0: vec3<f32>) and remove stale PBRData
mock from the prelude node validation test.
The PBR shader failed at runtime with UnknownNode { name: 'pbr_factors' }
because the PBR/math/engine/world-environment node libraries were never
registered in the global NodeRegistry. Only the prelude was loaded.

Changes:
- Replace OnceLock<NodeRegistry> with LazyLock<RwLock<NodeRegistry>> to
  allow late registration of additional libraries
- Add register_global_library() public function
- Register math, engine, PBR, and world-environment libraries at app
  startup before any shader assembly occurs
Updated Cargo.lock
Some checks failed
Main / lint (push) Failing after 12s
Main / test (push) Has been skipped
Main / build-linux (ubuntu-24.04-arm, aarch64-unknown-linux-gnu) (push) Has been skipped
Main / build-linux (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Has been skipped
Main / build-macos (macos-latest, aarch64-apple-darwin) (push) Has been skipped
Main / build-macos (macos-latest, x86_64-apple-darwin) (push) Has been skipped
Main / build-windows (windows-latest, aarch64-pc-windows-msvc) (push) Has been skipped
Main / build-windows (windows-latest, x86_64-pc-windows-gnu) (push) Has been skipped
Main / build-windows (windows-latest, x86_64-pc-windows-msvc) (push) Has been skipped
Main / release (push) Has been skipped
Main / release (pull_request) Has been skipped
Main / lint (pull_request) Failing after 9s
Main / test (pull_request) Has been skipped
Main / build-linux (ubuntu-24.04-arm, aarch64-unknown-linux-gnu) (pull_request) Has been skipped
Main / build-linux (ubuntu-latest, x86_64-unknown-linux-gnu) (pull_request) Has been skipped
Main / build-macos (macos-latest, aarch64-apple-darwin) (pull_request) Has been skipped
Main / build-macos (macos-latest, x86_64-apple-darwin) (pull_request) Has been skipped
Main / build-windows (windows-latest, aarch64-pc-windows-msvc) (pull_request) Has been skipped
Main / build-windows (windows-latest, x86_64-pc-windows-gnu) (pull_request) Has been skipped
Main / build-windows (windows-latest, x86_64-pc-windows-msvc) (pull_request) Has been skipped
c6d2b2c071
Resolve 5 conflicts:
- Assets/Shaders/pbr.wgsl: keep deleted (superseded by node-based PBR)
- cull.rs: keep fine-grained imports (orbital_cull, orbital_instance)
- orbital_cull/src/lib.rs: take main's CPU-only cleanup (remove stale GPU
  compute imports)
- wgsl_shaders.rs: keep expanded node-graph tests
- shader_preprocessor/lib.rs: keep node-based system (old ShaderPreprocessor
  superseded)

Also absorbs main's changes: CPU frustum culling rewrite, sky_common.wgsl
updates, CI migration, Android device handling fixes.
chore: update Cargo.lock after merge
Some checks failed
CI / lint (pull_request) Failing after 22s
CI / test (pull_request) Has been skipped
a3e1e240d6
On Android, device rotation triggers suspended→resumed (native window
destroyed/recreated), not WindowEvent::Resized.  The surface and
renderer were recreated with the new portrait dimensions, but the
camera's CameraDescriptorEcs.aspect stayed at the original 16/9 —
causing a projection mismatch that rendered everything black in portrait.

Sync the camera aspect ratio and WindowSize resource in resumed() to
match the freshly-created surface configuration.
Reverts 625468f3 and 37bd65a8 which broke rotation on Android.
The Resized handler and resumed() are back to their original state.
Rotation works again; portrait still shows black (known issue).
The list_devices() parser used split_whitespace().next() to extract the
device serial from 'adb devices' output. WiFi ADB serials like
'adb-d6260ff205bc-htaRwx (3)._adb-tls-connect._tcp' contain spaces,
causing the serial to be truncated and the device to be silently skipped.

Parse the line by scanning for the device state keyword instead, so the
full serial (including spaces) is captured correctly.
The previous fix checked for a space character before the state keyword,
but adb devices uses a tab character to separate the serial from the
state column. This caused WiFi ADB serials (which contain spaces) to
still be silently skipped.

Use is_ascii_whitespace() instead of == b' ' to handle both tabs and
spaces. Also fix the 'no permissions' branch which had the same issue.
Previously defaulted to 'landscape' which locked the activity and
prevented Android from triggering rotation on device tilt.
Three fixes for portrait rendering after Android rotation:

1. resumed(): Update CameraDescriptorEcs.aspect and WindowSize
   after the surface is recreated with new dimensions, so the
   projection matrix matches the portrait surface.

2. Resized handler: Call renderer.change_resolution() to recreate
   the depth texture at the new surface dimensions, preventing a
   depth-attachment size mismatch that causes the 3D scene to fail
   silently (black clear color remains while overlays still render).

3. Resized handler: Update camera aspect ratio and mark CameraDirty
   so realize_cameras propagates the new aspect to the GPU buffer.
fix: auto-fix clippy warnings
Some checks failed
CI / lint (pull_request) Failing after 21s
CI / test (pull_request) Has been skipped
db3cc441c6
On Android, device rotation triggers suspended→resumed (native window
destroyed/recreated), not WindowEvent::Resized.  The surface and
renderer were recreated with the new portrait dimensions, but the
camera's CameraDescriptorEcs.aspect stayed at the original 16/9 —
causing a projection mismatch that rendered everything black in portrait.

Sync the camera aspect ratio and WindowSize resource in resumed() to
match the freshly-created surface configuration.
Reverts 625468f3 and 37bd65a8 which broke rotation on Android.
The Resized handler and resumed() are back to their original state.
Rotation works again; portrait still shows black (known issue).
The list_devices() parser used split_whitespace().next() to extract the
device serial from 'adb devices' output. WiFi ADB serials like
'adb-d6260ff205bc-htaRwx (3)._adb-tls-connect._tcp' contain spaces,
causing the serial to be truncated and the device to be silently skipped.

Parse the line by scanning for the device state keyword instead, so the
full serial (including spaces) is captured correctly.
The previous fix checked for a space character before the state keyword,
but adb devices uses a tab character to separate the serial from the
state column. This caused WiFi ADB serials (which contain spaces) to
still be silently skipped.

Use is_ascii_whitespace() instead of == b' ' to handle both tabs and
spaces. Also fix the 'no permissions' branch which had the same issue.
Three fixes for portrait rendering after Android rotation:

1. resumed(): Update CameraDescriptorEcs.aspect and WindowSize
   after the surface is recreated with new dimensions, so the
   projection matrix matches the portrait surface.

2. Resized handler: Call renderer.change_resolution() to recreate
   the depth texture at the new surface dimensions, preventing a
   depth-attachment size mismatch that causes the 3D scene to fail
   silently (black clear color remains while overlays still render).

3. Resized handler: Update camera aspect ratio and mark CameraDirty
   so realize_cameras propagates the new aspect to the GPU buffer.
fix: auto-fix clippy warnings
Some checks failed
CI / lint (pull_request) Failing after 23s
CI / test (pull_request) Has been skipped
252f6e8065
SakulFlee force-pushed shader-rework from af06e7e94a to cb04b9bcef
Some checks failed
CI / lint (pull_request) Failing after 55s
CI / test (pull_request) Has been skipped
2026-09-06 22:53:11 +00:00
Compare
Sign in to join this conversation.
No description provided.