Skip to content

Commit dd4a196

Browse files
committed
Flexible camera bindings (#1689)
Alternative to #1203 and #1611 Camera bindings have historically been "hacked in". They were _required_ in all shaders and only supported a single Mat4. PBR (#1554) requires the CameraView matrix, but adding this using the "hacked" method forced users to either include all possible camera data in a single binding (#1203) or include all possible bindings (#1611). This approach instead assigns each "active camera" its own RenderResourceBindings, which are populated by CameraNode. The PassNode then retrieves (and initializes) the relevant bind groups for all render pipelines used by visible entities. * Enables any number of camera bindings , including zero (with any set or binding number ... set 0 should still be used to avoid rebinds). * Renames Camera binding to CameraViewProj * Adds CameraView binding
1 parent 6121e5f commit dd4a196

File tree

15 files changed

+270
-235
lines changed

15 files changed

+270
-235
lines changed

assets/shaders/hot.vert

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
layout(location = 0) in vec3 Vertex_Position;
44

5-
layout(set = 0, binding = 0) uniform Camera {
5+
layout(set = 0, binding = 0) uniform CameraViewProj {
66
mat4 ViewProj;
77
};
88

crates/bevy_pbr/src/render_graph/forward_pipeline/forward.frag

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ layout(location = 2) in vec2 v_Uv;
1414

1515
layout(location = 0) out vec4 o_Target;
1616

17-
layout(set = 0, binding = 0) uniform Camera {
17+
layout(set = 0, binding = 0) uniform CameraViewProj {
1818
mat4 ViewProj;
1919
};
2020

crates/bevy_pbr/src/render_graph/forward_pipeline/forward.vert

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ layout(location = 0) out vec3 v_Position;
88
layout(location = 1) out vec3 v_Normal;
99
layout(location = 2) out vec2 v_Uv;
1010

11-
layout(set = 0, binding = 0) uniform Camera {
11+
layout(set = 0, binding = 0) uniform CameraViewProj {
1212
mat4 ViewProj;
1313
};
1414

crates/bevy_render/src/camera/active_cameras.rs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,35 @@
1+
use crate::renderer::RenderResourceBindings;
2+
13
use super::Camera;
24
use bevy_ecs::{
35
entity::Entity,
46
system::{Query, ResMut},
57
};
68
use bevy_utils::HashMap;
79

10+
#[derive(Debug, Default)]
11+
pub struct ActiveCamera {
12+
pub entity: Option<Entity>,
13+
pub bindings: RenderResourceBindings,
14+
}
15+
816
#[derive(Debug, Default)]
917
pub struct ActiveCameras {
10-
pub cameras: HashMap<String, Option<Entity>>,
18+
cameras: HashMap<String, ActiveCamera>,
1119
}
1220

1321
impl ActiveCameras {
1422
pub fn add(&mut self, name: &str) {
15-
self.cameras.insert(name.to_string(), None);
23+
self.cameras
24+
.insert(name.to_string(), ActiveCamera::default());
1625
}
1726

18-
pub fn set(&mut self, name: &str, entity: Entity) {
19-
self.cameras.insert(name.to_string(), Some(entity));
27+
pub fn get(&self, name: &str) -> Option<&ActiveCamera> {
28+
self.cameras.get(name)
2029
}
2130

22-
pub fn get(&self, name: &str) -> Option<Entity> {
23-
self.cameras.get(name).and_then(|e| *e)
31+
pub fn get_mut(&mut self, name: &str) -> Option<&mut ActiveCamera> {
32+
self.cameras.get_mut(name)
2433
}
2534
}
2635

@@ -29,11 +38,11 @@ pub fn active_cameras_system(
2938
query: Query<(Entity, &Camera)>,
3039
) {
3140
for (name, active_camera) in active_cameras.cameras.iter_mut() {
32-
if active_camera.is_none() {
41+
if active_camera.entity.is_none() {
3342
for (camera_entity, camera) in query.iter() {
3443
if let Some(ref current_name) = camera.name {
3544
if current_name == name {
36-
*active_camera = Some(camera_entity);
45+
active_camera.entity = Some(camera_entity);
3746
}
3847
}
3948
}

crates/bevy_render/src/render_graph/nodes/camera_node.rs

Lines changed: 84 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::{
33
render_graph::{CommandQueue, Node, ResourceSlots, SystemNode},
44
renderer::{
55
BufferId, BufferInfo, BufferMapMode, BufferUsage, RenderContext, RenderResourceBinding,
6-
RenderResourceBindings, RenderResourceContext,
6+
RenderResourceContext,
77
},
88
};
99
use bevy_core::AsBytes;
@@ -50,88 +50,126 @@ impl SystemNode for CameraNode {
5050
config.0 = Some(CameraNodeState {
5151
camera_name: self.camera_name.clone(),
5252
command_queue: self.command_queue.clone(),
53-
camera_buffer: None,
5453
staging_buffer: None,
5554
})
5655
});
5756
Box::new(system)
5857
}
5958
}
6059

60+
const CAMERA_VIEW_PROJ: &str = "CameraViewProj";
61+
const CAMERA_VIEW: &str = "CameraView";
62+
6163
#[derive(Debug, Default)]
6264
pub struct CameraNodeState {
6365
command_queue: CommandQueue,
6466
camera_name: Cow<'static, str>,
65-
camera_buffer: Option<BufferId>,
6667
staging_buffer: Option<BufferId>,
6768
}
6869

70+
const MATRIX_SIZE: usize = std::mem::size_of::<[[f32; 4]; 4]>();
71+
6972
pub fn camera_node_system(
7073
mut state: Local<CameraNodeState>,
71-
active_cameras: Res<ActiveCameras>,
74+
mut active_cameras: ResMut<ActiveCameras>,
7275
render_resource_context: Res<Box<dyn RenderResourceContext>>,
73-
// PERF: this write on RenderResourceAssignments will prevent this system from running in
74-
// parallel with other systems that do the same
75-
mut render_resource_bindings: ResMut<RenderResourceBindings>,
76-
query: Query<(&Camera, &GlobalTransform)>,
76+
mut query: Query<(&Camera, &GlobalTransform)>,
7777
) {
7878
let render_resource_context = &**render_resource_context;
7979

80-
let (camera, global_transform) = if let Some(entity) = active_cameras.get(&state.camera_name) {
81-
query.get(entity).unwrap()
82-
} else {
83-
return;
84-
};
80+
let ((camera, global_transform), bindings) =
81+
if let Some(active_camera) = active_cameras.get_mut(&state.camera_name) {
82+
if let Some(entity) = active_camera.entity {
83+
(query.get_mut(entity).unwrap(), &mut active_camera.bindings)
84+
} else {
85+
return;
86+
}
87+
} else {
88+
return;
89+
};
8590

8691
let staging_buffer = if let Some(staging_buffer) = state.staging_buffer {
8792
render_resource_context.map_buffer(staging_buffer, BufferMapMode::Write);
8893
staging_buffer
8994
} else {
90-
let size = std::mem::size_of::<[[f32; 4]; 4]>();
95+
let staging_buffer = render_resource_context.create_buffer(BufferInfo {
96+
size: MATRIX_SIZE * 2,
97+
buffer_usage: BufferUsage::COPY_SRC | BufferUsage::MAP_WRITE,
98+
mapped_at_creation: true,
99+
});
100+
101+
state.staging_buffer = Some(staging_buffer);
102+
staging_buffer
103+
};
104+
105+
if bindings.get(CAMERA_VIEW_PROJ).is_none() {
91106
let buffer = render_resource_context.create_buffer(BufferInfo {
92-
size,
107+
size: MATRIX_SIZE,
93108
buffer_usage: BufferUsage::COPY_DST | BufferUsage::UNIFORM,
94109
..Default::default()
95110
});
96-
render_resource_bindings.set(
97-
&state.camera_name,
111+
bindings.set(
112+
CAMERA_VIEW_PROJ,
98113
RenderResourceBinding::Buffer {
99114
buffer,
100-
range: 0..size as u64,
115+
range: 0..MATRIX_SIZE as u64,
101116
dynamic_index: None,
102117
},
103118
);
104-
state.camera_buffer = Some(buffer);
119+
}
105120

106-
let staging_buffer = render_resource_context.create_buffer(BufferInfo {
107-
size,
108-
buffer_usage: BufferUsage::COPY_SRC | BufferUsage::MAP_WRITE,
109-
mapped_at_creation: true,
121+
if bindings.get(CAMERA_VIEW).is_none() {
122+
let buffer = render_resource_context.create_buffer(BufferInfo {
123+
size: MATRIX_SIZE,
124+
buffer_usage: BufferUsage::COPY_DST | BufferUsage::UNIFORM,
125+
..Default::default()
110126
});
127+
bindings.set(
128+
CAMERA_VIEW,
129+
RenderResourceBinding::Buffer {
130+
buffer,
131+
range: 0..MATRIX_SIZE as u64,
132+
dynamic_index: None,
133+
},
134+
);
135+
}
111136

112-
state.staging_buffer = Some(staging_buffer);
113-
staging_buffer
114-
};
137+
let view = global_transform.compute_matrix();
115138

116-
let matrix_size = std::mem::size_of::<[[f32; 4]; 4]>();
117-
let camera_matrix: [f32; 16] =
118-
(camera.projection_matrix * global_transform.compute_matrix().inverse()).to_cols_array();
119-
120-
render_resource_context.write_mapped_buffer(
121-
staging_buffer,
122-
0..matrix_size as u64,
123-
&mut |data, _renderer| {
124-
data[0..matrix_size].copy_from_slice(camera_matrix.as_bytes());
125-
},
126-
);
127-
render_resource_context.unmap_buffer(staging_buffer);
139+
if let Some(RenderResourceBinding::Buffer { buffer, .. }) = bindings.get(CAMERA_VIEW) {
140+
render_resource_context.write_mapped_buffer(
141+
staging_buffer,
142+
0..MATRIX_SIZE as u64,
143+
&mut |data, _renderer| {
144+
data[0..MATRIX_SIZE].copy_from_slice(view.to_cols_array_2d().as_bytes());
145+
},
146+
);
147+
state.command_queue.copy_buffer_to_buffer(
148+
staging_buffer,
149+
0,
150+
*buffer,
151+
0,
152+
MATRIX_SIZE as u64,
153+
);
154+
}
155+
156+
if let Some(RenderResourceBinding::Buffer { buffer, .. }) = bindings.get(CAMERA_VIEW_PROJ) {
157+
let view_proj = camera.projection_matrix * view.inverse();
158+
render_resource_context.write_mapped_buffer(
159+
staging_buffer,
160+
MATRIX_SIZE as u64..(2 * MATRIX_SIZE) as u64,
161+
&mut |data, _renderer| {
162+
data[0..MATRIX_SIZE].copy_from_slice(view_proj.to_cols_array_2d().as_bytes());
163+
},
164+
);
165+
state.command_queue.copy_buffer_to_buffer(
166+
staging_buffer,
167+
MATRIX_SIZE as u64,
168+
*buffer,
169+
0,
170+
MATRIX_SIZE as u64,
171+
);
172+
}
128173

129-
let camera_buffer = state.camera_buffer.unwrap();
130-
state.command_queue.copy_buffer_to_buffer(
131-
staging_buffer,
132-
0,
133-
camera_buffer,
134-
0,
135-
matrix_size as u64,
136-
);
174+
render_resource_context.unmap_buffer(staging_buffer);
137175
}

0 commit comments

Comments
 (0)