-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
/
Copy pathcamera_driver_node.rs
109 lines (100 loc) · 4.28 KB
/
camera_driver_node.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use crate::{
camera::{ExtractedCamera, RenderTarget},
render_graph::{Node, NodeRunError, RenderGraphContext, SlotValue},
renderer::RenderContext,
view::ExtractedWindows,
};
use bevy_ecs::{entity::Entity, prelude::QueryState, world::World};
use bevy_utils::{tracing::warn, HashSet};
use wgpu::{LoadOp, Operations, RenderPassColorAttachment, RenderPassDescriptor};
pub struct CameraDriverNode {
cameras: QueryState<(Entity, &'static ExtractedCamera)>,
}
impl CameraDriverNode {
pub fn new(world: &mut World) -> Self {
Self {
cameras: world.query(),
}
}
}
impl Node for CameraDriverNode {
fn update(&mut self, world: &mut World) {
self.cameras.update_archetypes(world);
}
fn run(
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
let mut sorted_cameras = self
.cameras
.iter_manual(world)
.map(|(e, c)| (e, c.priority, c.target.clone()))
.collect::<Vec<_>>();
// sort by priority and ensure within a priority, RenderTargets of the same type are packed together
sorted_cameras.sort_by(|(_, p1, t1), (_, p2, t2)| match p1.cmp(p2) {
std::cmp::Ordering::Equal => t1.cmp(t2),
ord => ord,
});
let mut camera_windows = HashSet::new();
let mut previous_priority_target = None;
let mut ambiguities = HashSet::new();
for (entity, priority, target) in sorted_cameras {
let new_priority_target = (priority, target);
if let Some(previous_priority_target) = previous_priority_target {
if previous_priority_target == new_priority_target {
ambiguities.insert(new_priority_target.clone());
}
}
previous_priority_target = Some(new_priority_target);
if let Ok((_, camera)) = self.cameras.get_manual(world, entity) {
if let RenderTarget::Window(id) = camera.target {
camera_windows.insert(id);
}
graph
.run_sub_graph(camera.render_graph.clone(), vec![SlotValue::Entity(entity)])?;
}
}
if !ambiguities.is_empty() {
warn!(
"Camera priority ambiguities detected for active cameras with the following priorities: {:?}. \
To fix this, ensure there is exactly one Camera entity spawned with a given priority for a given RenderTarget. \
Ambiguities should be resolved because either (1) multiple active cameras were spawned accidentally, which will \
result in rendering multiple instances of the scene or (2) for cases where multiple active cameras is intentional, \
ambiguities could result in unpredictable render results.",
ambiguities
);
}
// wgpu (and some backends) require doing work for swap chains if you call `get_current_texture()` and `present()`
// This ensures that Bevy doesn't crash, even when there are no cameras (and therefore no work submitted).
for (id, window) in world.resource::<ExtractedWindows>().iter() {
if camera_windows.contains(id) {
continue;
}
let swap_chain_texture = if let Some(swap_chain_texture) = &window.swap_chain_texture {
swap_chain_texture
} else {
continue;
};
#[cfg(feature = "trace")]
let _span = bevy_utils::tracing::info_span!("no_camera_clear_pass").entered();
let pass_descriptor = RenderPassDescriptor {
label: Some("no_camera_clear_pass"),
color_attachments: &[Some(RenderPassColorAttachment {
view: swap_chain_texture,
resolve_target: None,
ops: Operations {
load: LoadOp::Clear(wgpu::Color::BLACK),
store: true,
},
})],
depth_stencil_attachment: None,
};
render_context
.command_encoder
.begin_render_pass(&pass_descriptor);
}
Ok(())
}
}