Vendor Bevy Renderer and Eliminate Window Component Duplication #2

Open
opened 2025-12-14 23:16:25 +00:00 by siennathesane · 1 comment
siennathesane commented 2025-12-14 23:16:25 +00:00 (Migrated from github.com)

Summary

Vendor Bevy's rendering stack (including PBR) and eliminate duplicate window state management between winit and Bevy Window components. Currently Marathon owns the winit event loop but creates separate bevy::window::Window components, leading to duplicate state, DPI scaling bugs, and unclear ownership.

Scope

In Scope

  • Vendor bevy_render, bevy_pbr, bevy_core_pipeline, and necessary wgpu integration
  • Full PBR material system (metallic/roughness workflow, normal maps, AO)
  • Lighting system (point, directional, spot lights with shadows)
  • Refactor renderer to work directly with winit window handles
  • Remove all bevy::window::Window component creation from platform executors
  • Update egui integration to query window properties from winit only
  • Establish winit as single source of truth for window state

Out of Scope

  • Changes to Bevy ECS (we still use it)
  • Custom rendering features beyond current functionality
  • Window management features not already supported

Success Criteria

Scenario: Single source of truth for window state
  Given the Marathon engine owns the winit event loop
  When a window is created by the platform executor
  Then only a winit window handle should exist
  And the renderer should read all window properties from winit
  And no duplicate window state should exist in Bevy components

Scenario: PBR materials render correctly with low-poly assets
  Given a low-poly 3D model with PBR materials
  When the scene is rendered
  Then metallic and roughness values should display correctly
  And lighting should create realistic shadows and reflections
  And normal maps should add surface detail to simple geometry

Features & Tasks

This epic breaks down into the following work items:

Phase 1: Vendoring

  • Vendor bevy_render into Marathon codebase
  • Vendor bevy_pbr for full PBR material system
  • Vendor bevy_core_pipeline for rendering pipeline
  • Vendor wgpu integration and necessary asset dependencies
  • Verify existing rendering functionality after vendoring
  • Update Cargo dependencies

Phase 2: Renderer Refactoring

  • Modify renderer to accept winit handles directly
  • Create WindowInfo wrapper for render target queries
  • Update RawHandleWrapper to provide necessary info
  • Refactor camera viewport calculations
  • Ensure PBR materials system works with new architecture

Phase 3: Executor Cleanup

  • Remove bevy::window::Window creation from iOS executor
  • Remove bevy::window::Window creation from desktop executor
  • Use winit WindowAttributes for all window configuration
  • Remove Bevy WindowMode usage

Phase 4: egui Integration

  • Update debug UI to query scale factor from winit
  • Verify custom input system compatibility
  • Test DPI scaling on HiDPI displays

Phase 5: Testing & Documentation

  • Test PBR materials with low-poly assets
  • Verify lighting system (point, directional, spot lights)
  • Test shadow rendering on all platforms
  • Cross-platform testing (macOS, iOS, desktop)
  • DPI scaling verification on Retina/HiDPI displays
  • Update architecture documentation
  • Remove obsolete TODOs

Dependencies

Depends on: None
Blocks: Future renderer improvements, cleaner window API

Background

Marathon currently manages windows in two places:

  1. winit window handle - Created by platform executors
  2. bevy::window::Window component - Created to satisfy renderer

This duplication causes:

  • DPI scaling bugs (two scale factor sources)
  • Unclear window lifecycle ownership
  • Inconsistent APIs (winit vs Bevy abstractions)
  • iOS fullscreen mode confusion

The solution is to vendor Bevy's renderer (including full PBR pipeline for low-poly + realistic lighting aesthetic) and refactor it to work directly with our winit handles.

Technical Notes

Why PBR?
Aspen uses spatial audio for immersion, so we need visuals that reinforce that sense of place. Low-poly geometry with PBR materials gives us:

  • Stylized, performant art style
  • Realistic lighting that makes spaces feel inhabitable
  • Material depth (worn surfaces, metallic accents) on simple forms
  • That "miniature diorama you could step into" feeling

Current Problem (iOS executor example):

// We create both a winit window AND a Bevy Window component
let mut window = bevy::window::Window {
    resolution: WindowResolution::new(logical_width, logical_height),
    mode: WindowMode::Windowed,  // But we want winit Fullscreen
    // ...
};
window.resolution.set_scale_factor(scale_factor);  // Duplicate state!

Target Architecture:

pub struct WindowInfo {
    inner_size: PhysicalSize<u32>,
    scale_factor: f64,
}

impl WindowInfo {
    pub fn from_winit(window: &winit::window::Window) -> Self {
        Self {
            inner_size: window.inner_size(),
            scale_factor: window.scale_factor(),
        }
    }
}

Priority: Medium (architectural improvement)
Effort: Extra Large (multi-phase refactoring with PBR system)
Type: Epic

## Summary Vendor Bevy's rendering stack (including PBR) and eliminate duplicate window state management between winit and Bevy Window components. Currently Marathon owns the winit event loop but creates separate `bevy::window::Window` components, leading to duplicate state, DPI scaling bugs, and unclear ownership. ## Scope ### In Scope - Vendor `bevy_render`, `bevy_pbr`, `bevy_core_pipeline`, and necessary wgpu integration - Full PBR material system (metallic/roughness workflow, normal maps, AO) - Lighting system (point, directional, spot lights with shadows) - Refactor renderer to work directly with winit window handles - Remove all `bevy::window::Window` component creation from platform executors - Update egui integration to query window properties from winit only - Establish winit as single source of truth for window state ### Out of Scope - Changes to Bevy ECS (we still use it) - Custom rendering features beyond current functionality - Window management features not already supported ## Success Criteria ```gherkin Scenario: Single source of truth for window state Given the Marathon engine owns the winit event loop When a window is created by the platform executor Then only a winit window handle should exist And the renderer should read all window properties from winit And no duplicate window state should exist in Bevy components Scenario: PBR materials render correctly with low-poly assets Given a low-poly 3D model with PBR materials When the scene is rendered Then metallic and roughness values should display correctly And lighting should create realistic shadows and reflections And normal maps should add surface detail to simple geometry ``` ## Features & Tasks This epic breaks down into the following work items: ### Phase 1: Vendoring - Vendor `bevy_render` into Marathon codebase - Vendor `bevy_pbr` for full PBR material system - Vendor `bevy_core_pipeline` for rendering pipeline - Vendor wgpu integration and necessary asset dependencies - Verify existing rendering functionality after vendoring - Update Cargo dependencies ### Phase 2: Renderer Refactoring - Modify renderer to accept winit handles directly - Create `WindowInfo` wrapper for render target queries - Update `RawHandleWrapper` to provide necessary info - Refactor camera viewport calculations - Ensure PBR materials system works with new architecture ### Phase 3: Executor Cleanup - Remove `bevy::window::Window` creation from iOS executor - Remove `bevy::window::Window` creation from desktop executor - Use winit `WindowAttributes` for all window configuration - Remove Bevy `WindowMode` usage ### Phase 4: egui Integration - Update debug UI to query scale factor from winit - Verify custom input system compatibility - Test DPI scaling on HiDPI displays ### Phase 5: Testing & Documentation - Test PBR materials with low-poly assets - Verify lighting system (point, directional, spot lights) - Test shadow rendering on all platforms - Cross-platform testing (macOS, iOS, desktop) - DPI scaling verification on Retina/HiDPI displays - Update architecture documentation - Remove obsolete TODOs ## Dependencies **Depends on:** None **Blocks:** Future renderer improvements, cleaner window API ## Background Marathon currently manages windows in two places: 1. **winit window handle** - Created by platform executors 2. **bevy::window::Window component** - Created to satisfy renderer This duplication causes: - DPI scaling bugs (two scale factor sources) - Unclear window lifecycle ownership - Inconsistent APIs (winit vs Bevy abstractions) - iOS fullscreen mode confusion The solution is to vendor Bevy's renderer (including full PBR pipeline for low-poly + realistic lighting aesthetic) and refactor it to work directly with our winit handles. ## Technical Notes **Why PBR?** Aspen uses spatial audio for immersion, so we need visuals that reinforce that sense of place. Low-poly geometry with PBR materials gives us: - Stylized, performant art style - Realistic lighting that makes spaces feel inhabitable - Material depth (worn surfaces, metallic accents) on simple forms - That "miniature diorama you could step into" feeling **Current Problem** (iOS executor example): ```rust // We create both a winit window AND a Bevy Window component let mut window = bevy::window::Window { resolution: WindowResolution::new(logical_width, logical_height), mode: WindowMode::Windowed, // But we want winit Fullscreen // ... }; window.resolution.set_scale_factor(scale_factor); // Duplicate state! ``` **Target Architecture**: ```rust pub struct WindowInfo { inner_size: PhysicalSize<u32>, scale_factor: f64, } impl WindowInfo { pub fn from_winit(window: &winit::window::Window) -> Self { Self { inner_size: window.inner_size(), scale_factor: window.scale_factor(), } } } ``` --- **Priority:** Medium (architectural improvement) **Effort:** Extra Large (multi-phase refactoring with PBR system) **Type:** Epic
siennathesane commented 2025-12-23 23:54:58 +00:00 (Migrated from github.com)

Phase 1: Vendoring - COMPLETE

Successfully vendored all three Bevy rendering crates from v0.17.2 into Marathon:

Crates Vendored

  • bevy_render (core rendering, ~15K LOC, 73 files)
  • bevy_core_pipeline (render pipelines, ~1.6K LOC, 26 files)
  • bevy_pbr (PBR materials & lighting, ~34.5K LOC, 51 files)

Location

All vendored to crates/libmarathon/src/render/ with:

  • Bevy's internal structure preserved
  • bevy_ecs, bevy_app, bevy_transform, etc. kept as external dependencies
  • Source from commit: 566358363126dd69f6e457e47f306c68f8041d2a (v0.17.2)

Key Changes

  1. Vendored bevy_render_macros into crates/macros/ (merged with sync-macros)
  2. Updated all import paths (~535+ automated fixes)
  3. Fixed macro path generation to use crate::render instead of bevy_render
  4. Added missing dependencies: encase 0.11, async-channel, offset-allocator, variadics_please, static_assertions
  5. Created render/mod.rs with proper re-exports and license headers

Build Status

  • 0 compilation errors (down from 773 initial errors)
  • libmarathon builds cleanly
  • app builds cleanly
  • macOS smoke test passed (app launches successfully)

Files Modified

  • Cargo.toml (workspace)
  • crates/libmarathon/Cargo.toml (21 bevy subcrate dependencies added)
  • crates/app/Cargo.toml (removed vendored features from bevy dependency)
  • crates/macros/ (renamed from sync-macros, merged bevy_render_macros)
  • crates/libmarathon/src/lib.rs (export render module)
  • crates/libmarathon/src/render/mod.rs (created with re-exports)
  • Plus ~150 vendored source files with automated path fixes

Next Steps

Phase 2 ready to begin: Renderer refactoring to work directly with winit handles.

Closes the Phase 1 checklist from the issue description.

## ✅ Phase 1: Vendoring - COMPLETE Successfully vendored all three Bevy rendering crates from v0.17.2 into Marathon: ### Crates Vendored - ✅ **bevy_render** (core rendering, ~15K LOC, 73 files) - ✅ **bevy_core_pipeline** (render pipelines, ~1.6K LOC, 26 files) - ✅ **bevy_pbr** (PBR materials & lighting, ~34.5K LOC, 51 files) ### Location All vendored to `crates/libmarathon/src/render/` with: - Bevy's internal structure preserved - bevy_ecs, bevy_app, bevy_transform, etc. kept as external dependencies - Source from commit: 566358363126dd69f6e457e47f306c68f8041d2a (v0.17.2) ### Key Changes 1. **Vendored bevy_render_macros** into `crates/macros/` (merged with sync-macros) 2. **Updated all import paths** (~535+ automated fixes) 3. **Fixed macro path generation** to use `crate::render` instead of `bevy_render` 4. **Added missing dependencies**: encase 0.11, async-channel, offset-allocator, variadics_please, static_assertions 5. **Created render/mod.rs** with proper re-exports and license headers ### Build Status - ✅ **0 compilation errors** (down from 773 initial errors) - ✅ libmarathon builds cleanly - ✅ app builds cleanly - ✅ macOS smoke test passed (app launches successfully) ### Files Modified - `Cargo.toml` (workspace) - `crates/libmarathon/Cargo.toml` (21 bevy subcrate dependencies added) - `crates/app/Cargo.toml` (removed vendored features from bevy dependency) - `crates/macros/` (renamed from sync-macros, merged bevy_render_macros) - `crates/libmarathon/src/lib.rs` (export render module) - `crates/libmarathon/src/render/mod.rs` (created with re-exports) - Plus ~150 vendored source files with automated path fixes ### Next Steps Phase 2 ready to begin: Renderer refactoring to work directly with winit handles. Closes the Phase 1 checklist from the issue description.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: studio/marathon#2