Skip to content

Commit 4d388f2

Browse files
committed
Merge branch 'aaa-renderer-architecture-debt'
2 parents 0592175 + babe75d commit 4d388f2

36 files changed

Lines changed: 599 additions & 303 deletions

crates/renderide/src/backend/frame_gpu.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use scene_snapshot::{
3636
///
3737
/// `@group(0)` bind groups are per-view and are owned by
3838
/// [`crate::backend::frame_resource_manager::PerViewFrameState`], keyed by
39-
/// [`crate::render_graph::OcclusionViewId`], and built using
39+
/// [`crate::render_graph::ViewId`], and built using
4040
/// [`Self::build_per_view_bind_group`]. Every per-view bind group references the **same**
4141
/// shared cluster buffers from [`Self::cluster_cache`].
4242
pub struct FrameGpuResources {

crates/renderide/src/backend/frame_resource_manager.rs

Lines changed: 51 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010
//!
1111
//! Per-view cluster buffers are each view's own independent storage so that views cannot stomp
1212
//! one another's clustered light lists under single-submit semantics. Per-view state is keyed by
13-
//! [`OcclusionViewId`] and created lazily on first use; retired explicitly when a secondary RT
13+
//! [`ViewId`] and created lazily on first use; retired explicitly when a secondary RT
1414
//! camera is destroyed.
1515
//!
1616
//! Per-draw resources follow the same ownership model: one grow-on-demand slab per
17-
//! [`OcclusionViewId`], created lazily so no view can exhaust another view's per-draw capacity.
17+
//! [`ViewId`], created lazily so no view can exhaust another view's per-draw capacity.
1818
1919
use std::sync::atomic::{AtomicBool, Ordering};
2020
use std::sync::Arc;
@@ -25,7 +25,7 @@ use parking_lot::Mutex;
2525
use crate::backend::cluster_gpu::{ClusterBufferRefs, CLUSTER_PARAMS_UNIFORM_SIZE};
2626
use crate::gpu::frame_globals::{FrameGpuUniforms, SkyboxSpecularUniformParams};
2727
use crate::gpu::GpuLimits;
28-
use crate::render_graph::OcclusionViewId;
28+
use crate::render_graph::ViewId;
2929

3030
use super::frame_gpu::{
3131
EmptyMaterialBindGroup, FrameGpuResources, PerViewSceneSnapshotSyncParams,
@@ -163,12 +163,12 @@ pub struct FrameResourceManager {
163163
pub(crate) empty_material: Option<EmptyMaterialBindGroup>,
164164
/// Per-view cluster buffers, frame uniform buffer, and `@group(0)` bind group.
165165
///
166-
/// Created lazily on first use per [`OcclusionViewId`]; retired when a secondary RT camera
166+
/// Created lazily on first use per [`ViewId`]; retired when a secondary RT camera
167167
/// is destroyed via [`Self::retire_per_view_frame`].
168168
per_view_frame: PerViewResourceMap<PerViewFrameState>,
169169
/// One grow-on-demand per-draw slab per stable render-view identity.
170170
///
171-
/// Created lazily; keyed by [`OcclusionViewId`] so secondary RT cameras never compete
171+
/// Created lazily; keyed by [`ViewId`] so secondary RT cameras never compete
172172
/// with the main view (or each other) for buffer space.
173173
per_view_draw: PerViewResourceMap<Mutex<PerDrawResources>>,
174174
/// Shared `@group(2)` bind group layout, reflected once at attach time.
@@ -347,7 +347,7 @@ impl FrameResourceManager {
347347
/// when cluster buffers cannot be allocated for the given viewport.
348348
pub fn per_view_frame_or_create(
349349
&mut self,
350-
view_id: OcclusionViewId,
350+
view_id: ViewId,
351351
device: &wgpu::Device,
352352
layout: PreRecordViewResourceLayout,
353353
) -> Option<&mut PerViewFrameState> {
@@ -470,26 +470,31 @@ impl FrameResourceManager {
470470
}
471471

472472
/// Returns the per-view frame state for `view_id`, or `None` if not yet created.
473-
pub fn per_view_frame(&self, view_id: OcclusionViewId) -> Option<&PerViewFrameState> {
473+
pub fn per_view_frame(&self, view_id: ViewId) -> Option<&PerViewFrameState> {
474474
self.per_view_frame.get(view_id)
475475
}
476476

477477
/// Frees per-view cluster buffers and bind group for a view that is no longer active.
478478
///
479479
/// Call alongside [`Self::retire_per_view_per_draw`] when a secondary RT camera is destroyed.
480480
/// Has no effect if the view was never allocated.
481-
pub fn retire_per_view_frame(&mut self, view_id: OcclusionViewId) {
481+
pub fn retire_per_view_frame(&mut self, view_id: ViewId) {
482482
if self.per_view_frame.retire(view_id) {
483483
logger::debug!("per-view frame state: retired for view {view_id:?}");
484484
}
485485
}
486486

487+
/// Number of live per-view frame-state entries.
488+
pub fn per_view_frame_count(&self) -> usize {
489+
self.per_view_frame.len()
490+
}
491+
487492
/// Returns the per-draw slab for the given view, creating it if it does not yet exist.
488493
///
489494
/// Returns `None` when the manager has not been attached (no device limits / layout available).
490495
pub fn per_view_per_draw_or_create(
491496
&mut self,
492-
view_id: OcclusionViewId,
497+
view_id: ViewId,
493498
device: &wgpu::Device,
494499
) -> Option<&Mutex<PerDrawResources>> {
495500
profiling::scope!("render::ensure_per_view_per_draw");
@@ -503,26 +508,31 @@ impl FrameResourceManager {
503508
}
504509

505510
/// Returns the per-draw slab for the given view, or `None` if it has not been created yet.
506-
pub fn per_view_per_draw(&self, view_id: OcclusionViewId) -> Option<&Mutex<PerDrawResources>> {
511+
pub fn per_view_per_draw(&self, view_id: ViewId) -> Option<&Mutex<PerDrawResources>> {
507512
self.per_view_draw.get(view_id)
508513
}
509514

510515
/// Frees the per-draw slab for a view that is no longer active (e.g. render-texture camera destroyed).
511516
///
512517
/// Has no effect if the view was never allocated.
513-
pub fn retire_per_view_per_draw(&mut self, view_id: OcclusionViewId) {
518+
pub fn retire_per_view_per_draw(&mut self, view_id: ViewId) {
514519
if self.per_view_draw.retire(view_id) {
515520
logger::debug!("per-draw slab: retired slab for view {view_id:?}");
516521
}
517522
}
518523

524+
/// Number of live per-view per-draw slabs.
525+
pub fn per_view_per_draw_count(&self) -> usize {
526+
self.per_view_draw.len()
527+
}
528+
519529
/// Returns the per-view scratch slot used for per-draw uniform packing, creating it on first use.
520530
///
521-
/// Keyed per [`OcclusionViewId`] so parallel per-view recording cannot alias the same scratch
531+
/// Keyed per [`ViewId`] so parallel per-view recording cannot alias the same scratch
522532
/// across rayon workers.
523533
pub fn per_view_per_draw_scratch_or_create(
524534
&mut self,
525-
view_id: OcclusionViewId,
535+
view_id: ViewId,
526536
) -> &Mutex<PerViewPerDrawScratch> {
527537
profiling::scope!("render::ensure_per_view_per_draw_scratch");
528538
self.per_view_per_draw_scratch
@@ -535,7 +545,7 @@ impl FrameResourceManager {
535545
/// Returns the per-view scratch slot, or `None` if it has not been created yet.
536546
pub fn per_view_per_draw_scratch(
537547
&self,
538-
view_id: OcclusionViewId,
548+
view_id: ViewId,
539549
) -> Option<&Mutex<PerViewPerDrawScratch>> {
540550
self.per_view_per_draw_scratch.get(view_id)
541551
}
@@ -544,12 +554,24 @@ impl FrameResourceManager {
544554
///
545555
/// Call alongside [`Self::retire_per_view_per_draw`] and [`Self::retire_per_view_frame`] when a
546556
/// secondary RT camera is destroyed. Has no effect if the view was never allocated.
547-
pub fn retire_per_view_per_draw_scratch(&mut self, view_id: OcclusionViewId) {
557+
pub fn retire_per_view_per_draw_scratch(&mut self, view_id: ViewId) {
548558
if self.per_view_per_draw_scratch.retire(view_id) {
549559
logger::debug!("per-draw slab scratch: retired for view {view_id:?}");
550560
}
551561
}
552562

563+
/// Number of live per-view CPU scratch slots.
564+
pub fn per_view_per_draw_scratch_count(&self) -> usize {
565+
self.per_view_per_draw_scratch.len()
566+
}
567+
568+
/// Retires all view-scoped frame resources for `view_id`.
569+
pub fn retire_view(&mut self, view_id: ViewId) {
570+
self.retire_per_view_frame(view_id);
571+
self.retire_per_view_per_draw(view_id);
572+
self.retire_per_view_per_draw_scratch(view_id);
573+
}
574+
553575
/// Fills the light scratch buffer from [`SceneCoordinator`] (active render spaces only,
554576
/// clustered ordering, capped at [`super::MAX_LIGHTS`]).
555577
///
@@ -690,7 +712,7 @@ impl FrameResourceManager {
690712
/// The snapshot must already have been provisioned by [`Self::per_view_frame_or_create`].
691713
pub fn copy_scene_depth_snapshot_for_view(
692714
&self,
693-
view_id: OcclusionViewId,
715+
view_id: ViewId,
694716
encoder: &mut wgpu::CommandEncoder,
695717
source_depth: &wgpu::Texture,
696718
viewport: (u32, u32),
@@ -709,7 +731,7 @@ impl FrameResourceManager {
709731
/// The snapshot must already have been provisioned by [`Self::per_view_frame_or_create`].
710732
pub fn copy_scene_color_snapshot_for_view(
711733
&self,
712-
view_id: OcclusionViewId,
734+
view_id: ViewId,
713735
encoder: &mut wgpu::CommandEncoder,
714736
source_color: &wgpu::Texture,
715737
viewport: (u32, u32),
@@ -770,19 +792,17 @@ mod tests {
770792
#[test]
771793
fn new_manager_has_no_per_view_draw() {
772794
let mgr = FrameResourceManager::new();
773-
assert!(mgr.per_view_per_draw(OcclusionViewId::Main).is_none());
774-
assert!(mgr
775-
.per_view_per_draw(OcclusionViewId::OffscreenRenderTexture(42))
776-
.is_none());
795+
let secondary = ViewId::secondary_camera(RenderSpaceId(42), 0);
796+
assert!(mgr.per_view_per_draw(ViewId::Main).is_none());
797+
assert!(mgr.per_view_per_draw(secondary).is_none());
777798
}
778799

779800
#[test]
780801
fn new_manager_has_no_per_view_frame() {
781802
let mgr = FrameResourceManager::new();
782-
assert!(mgr.per_view_frame(OcclusionViewId::Main).is_none());
783-
assert!(mgr
784-
.per_view_frame(OcclusionViewId::OffscreenRenderTexture(42))
785-
.is_none());
803+
let secondary = ViewId::secondary_camera(RenderSpaceId(42), 0);
804+
assert!(mgr.per_view_frame(ViewId::Main).is_none());
805+
assert!(mgr.per_view_frame(secondary).is_none());
786806
}
787807

788808
/// Shared pre-record work deduplicates only the cluster allocation shape, not snapshot needs.
@@ -831,10 +851,12 @@ mod tests {
831851
#[test]
832852
fn retire_nonexistent_is_noop() {
833853
let mut mgr = FrameResourceManager::new();
834-
mgr.retire_per_view_per_draw(OcclusionViewId::Main);
835-
mgr.retire_per_view_per_draw(OcclusionViewId::OffscreenRenderTexture(99));
836-
mgr.retire_per_view_frame(OcclusionViewId::Main);
837-
mgr.retire_per_view_frame(OcclusionViewId::OffscreenRenderTexture(99));
854+
let secondary = ViewId::secondary_camera(RenderSpaceId(99), 0);
855+
mgr.retire_per_view_per_draw(ViewId::Main);
856+
mgr.retire_per_view_per_draw(secondary);
857+
mgr.retire_per_view_frame(ViewId::Main);
858+
mgr.retire_per_view_frame(secondary);
859+
mgr.retire_view(secondary);
838860
}
839861

840862
fn make_light_data(color_x: f32) -> LightData {

crates/renderide/src/backend/history_registry.rs

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use std::sync::Arc;
1616
use hashbrown::HashMap;
1717
use parking_lot::Mutex;
1818

19-
use crate::render_graph::{HistorySlotId, OcclusionViewId};
19+
use crate::render_graph::{HistorySlotId, ViewId};
2020

2121
/// Errors returned by [`HistoryRegistry`] registration APIs.
2222
#[derive(Debug, thiserror::Error)]
@@ -40,7 +40,7 @@ pub enum HistoryResourceScope {
4040
/// One global resource pair shared by all views.
4141
Global,
4242
/// One resource pair for one logical occlusion/render view.
43-
View(OcclusionViewId),
43+
View(ViewId),
4444
}
4545

4646
/// Concrete key used by the registry's texture and buffer maps.
@@ -444,6 +444,17 @@ impl HistoryRegistry {
444444
pub fn buffer_slot_count(&self) -> usize {
445445
self.buffers.len()
446446
}
447+
448+
/// Retires every texture and buffer slot that belongs to `scope`.
449+
pub fn retire_scope(&mut self, scope: HistoryResourceScope) {
450+
self.textures.retain(|key, _| key.scope != scope);
451+
self.buffers.retain(|key, _| key.scope != scope);
452+
}
453+
454+
/// Retires every texture and buffer slot scoped to one logical view.
455+
pub fn retire_view(&mut self, view_id: ViewId) {
456+
self.retire_scope(HistoryResourceScope::View(view_id));
457+
}
447458
}
448459

449460
fn texture_specs_equivalent(a: &TextureHistorySpec, b: &TextureHistorySpec) -> bool {
@@ -505,6 +516,7 @@ fn create_texture_history_mip_views(
505516
#[cfg(test)]
506517
mod tests {
507518
use super::*;
519+
use crate::scene::RenderSpaceId;
508520

509521
fn tex_spec() -> TextureHistorySpec {
510522
TextureHistorySpec {
@@ -533,6 +545,11 @@ mod tests {
533545
const SLOT_A: HistorySlotId = HistorySlotId::new("test_a");
534546
const SLOT_B: HistorySlotId = HistorySlotId::new("test_b");
535547

548+
/// Builds a secondary-camera view id for history-registry tests.
549+
fn secondary_view(render_space_id: i32, renderable_index: i32) -> ViewId {
550+
ViewId::secondary_camera(RenderSpaceId(render_space_id), renderable_index)
551+
}
552+
536553
#[test]
537554
fn texture_slot_registration_is_idempotent() {
538555
let mut reg = HistoryRegistry::new();
@@ -579,29 +596,50 @@ mod tests {
579596
fn scoped_texture_slots_do_not_alias_global_or_other_views() {
580597
let mut reg = HistoryRegistry::new();
581598
reg.register_texture(SLOT_A, tex_spec()).unwrap();
599+
reg.register_texture_scoped(SLOT_A, HistoryResourceScope::View(ViewId::Main), tex_spec())
600+
.unwrap();
582601
reg.register_texture_scoped(
583602
SLOT_A,
584-
HistoryResourceScope::View(OcclusionViewId::Main),
585-
tex_spec(),
586-
)
587-
.unwrap();
588-
reg.register_texture_scoped(
589-
SLOT_A,
590-
HistoryResourceScope::View(OcclusionViewId::OffscreenRenderTexture(7)),
603+
HistoryResourceScope::View(secondary_view(7, 0)),
591604
tex_spec(),
592605
)
593606
.unwrap();
594607

595608
assert_eq!(reg.texture_slot_count(), 3);
596609
assert!(reg.texture_slot(SLOT_A).is_some());
597610
assert!(reg
598-
.texture_slot_scoped(SLOT_A, HistoryResourceScope::View(OcclusionViewId::Main))
611+
.texture_slot_scoped(SLOT_A, HistoryResourceScope::View(ViewId::Main))
599612
.is_some());
600613
assert!(reg
601-
.texture_slot_scoped(
602-
SLOT_A,
603-
HistoryResourceScope::View(OcclusionViewId::OffscreenRenderTexture(7)),
604-
)
614+
.texture_slot_scoped(SLOT_A, HistoryResourceScope::View(secondary_view(7, 0)))
615+
.is_some());
616+
}
617+
618+
/// Retiring a view-scoped history resource leaves global and unrelated view scopes intact.
619+
#[test]
620+
fn retire_view_removes_only_matching_scope() {
621+
let mut reg = HistoryRegistry::new();
622+
let retired = secondary_view(7, 0);
623+
let surviving = secondary_view(7, 1);
624+
reg.register_texture(SLOT_A, tex_spec()).unwrap();
625+
reg.register_texture_scoped(SLOT_A, HistoryResourceScope::View(retired), tex_spec())
626+
.unwrap();
627+
reg.register_buffer_scoped(SLOT_B, HistoryResourceScope::View(retired), buf_spec())
628+
.unwrap();
629+
reg.register_texture_scoped(SLOT_A, HistoryResourceScope::View(surviving), tex_spec())
630+
.unwrap();
631+
632+
reg.retire_view(retired);
633+
634+
assert!(reg.texture_slot(SLOT_A).is_some());
635+
assert!(reg
636+
.texture_slot_scoped(SLOT_A, HistoryResourceScope::View(retired))
637+
.is_none());
638+
assert!(reg
639+
.buffer_slot_scoped(SLOT_B, HistoryResourceScope::View(retired))
640+
.is_none());
641+
assert!(reg
642+
.texture_slot_scoped(SLOT_A, HistoryResourceScope::View(surviving))
605643
.is_some());
606644
}
607645

crates/renderide/src/backend/mod.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ mod per_view_resource_map;
2525
mod reflection_probe_sh2;
2626
mod render_backend;
2727
mod skybox_specular;
28+
mod view_resource_registry;
2829

2930
pub use crate::assets::AssetTransferQueue;
3031
pub use cluster_gpu::{
@@ -63,9 +64,10 @@ pub(crate) use occlusion::HiZBuildInput;
6364
pub use occlusion::OcclusionSystem;
6465
pub use per_draw_resources::PerDrawResources;
6566
pub(crate) use reflection_probe_sh2::ReflectionProbeSh2System;
66-
pub(crate) use render_backend::{FrameDrawSetup, WorldMeshForwardEncodeRefs};
67+
pub(crate) use render_backend::{ExtractedFrameShared, WorldMeshForwardEncodeRefs};
6768
pub use render_backend::{
6869
RenderBackend, RenderBackendAttachDesc, RenderBackendAttachError, MAX_ASSET_INTEGRATION_QUEUED,
6970
MAX_PENDING_MESH_UPLOADS, MAX_PENDING_TEXTURE_UPLOADS,
7071
};
7172
pub(crate) use skybox_specular::resolve_active_main_skybox_specular_environment;
73+
pub(crate) use view_resource_registry::ViewResourceRegistry;

0 commit comments

Comments
 (0)