1use super::WgpuWrapper;
2use crate::diagnostic::internal::DiagnosticsRecorder;
3use crate::render_phase::TrackedRenderPass;
4use crate::render_resource::{CommandEncoder, RenderPassDescriptor};
5use crate::renderer::RenderDevice;
6use bevy_derive::{Deref, DerefMut};
7use bevy_ecs::change_detection::Tick;
8use bevy_ecs::component::ComponentId;
9use bevy_ecs::prelude::*;
10use bevy_ecs::query::{FilteredAccessSet, QueryData, QueryFilter, QueryState};
11use bevy_ecs::system::{
12 Deferred, SystemBuffer, SystemMeta, SystemParam, SystemParamValidationError,
13};
14use bevy_ecs::world::unsafe_world_cell::UnsafeWorldCell;
15use bevy_ecs::world::DeferredWorld;
16#[cfg(feature = "trace")]
17use bevy_log::info_span;
18use core::marker::PhantomData;
19use wgpu::CommandBuffer;
20
21#[derive(Default)]
22struct PendingCommandBuffersInner {
23 buffers: Vec<CommandBuffer>,
24 encoders: Vec<CommandEncoder>,
25}
26
27#[derive(Resource)]
29pub struct PendingCommandBuffers(WgpuWrapper<PendingCommandBuffersInner>);
30
31impl Default for PendingCommandBuffers {
32 fn default() -> Self {
33 Self(WgpuWrapper::new(PendingCommandBuffersInner::default()))
34 }
35}
36
37impl PendingCommandBuffers {
38 pub fn push(&mut self, buffers: impl IntoIterator<Item = CommandBuffer>) {
39 self.0.buffers.extend(buffers);
40 }
41
42 pub fn push_encoder(&mut self, encoder: CommandEncoder) {
43 self.0.encoders.push(encoder);
44 }
45
46 pub fn take(&mut self) -> Vec<CommandBuffer> {
47 let encoders: Vec<_> = self.0.encoders.drain(..).collect();
48 for encoder in encoders {
49 self.0.buffers.push(encoder.finish());
50 }
51 core::mem::take(&mut self.0.buffers)
52 }
53
54 pub fn is_empty(&self) -> bool {
55 self.0.buffers.is_empty() && self.0.encoders.is_empty()
56 }
57
58 pub fn len(&self) -> usize {
59 self.0.buffers.len() + self.0.encoders.len()
60 }
61}
62
63#[derive(Default)]
64struct RenderContextStateInner {
65 command_encoder: Option<CommandEncoder>,
66 command_buffers: Vec<CommandBuffer>,
67 render_device: Option<RenderDevice>,
68}
69
70pub struct RenderContextState(WgpuWrapper<RenderContextStateInner>);
74
75impl Default for RenderContextState {
76 fn default() -> Self {
77 Self(WgpuWrapper::new(RenderContextStateInner::default()))
78 }
79}
80
81impl RenderContextState {
82 fn flush_encoder(&mut self) {
83 if let Some(encoder) = self.0.command_encoder.take() {
84 self.0.command_buffers.push(encoder.finish());
85 }
86 }
87
88 fn command_encoder(&mut self) -> &mut CommandEncoder {
89 let render_device = self
90 .0
91 .render_device
92 .clone()
93 .expect("RenderDevice must be set before accessing command_encoder");
94
95 self.0.command_encoder.get_or_insert_with(|| {
96 render_device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default())
97 })
98 }
99
100 pub fn finish(&mut self) -> Vec<CommandBuffer> {
101 self.flush_encoder();
102 core::mem::take(&mut self.0.command_buffers)
103 }
104}
105
106impl SystemBuffer for RenderContextState {
107 fn queue(&mut self, _system_meta: &SystemMeta, mut world: DeferredWorld) {
108 #[cfg(feature = "trace")]
109 let _span =
110 info_span!("RenderContextState::apply", system = %_system_meta.name()).entered();
111
112 let inner = &mut *self.0;
113
114 if let Some(encoder) = inner.command_encoder.take() {
116 inner.command_buffers.push(encoder.finish());
117 }
118
119 if !inner.command_buffers.is_empty() {
120 let mut pending = world.resource_mut::<PendingCommandBuffers>();
121 pending.push(core::mem::take(&mut inner.command_buffers));
122 }
123
124 inner.render_device = None;
125 }
126}
127
128#[derive(SystemParam)]
132pub struct RenderContext<'w, 's> {
133 state: Deferred<'s, RenderContextState>,
134 render_device: Res<'w, RenderDevice>,
135 diagnostics_recorder: Option<Res<'w, DiagnosticsRecorder>>,
136}
137
138impl<'w, 's> RenderContext<'w, 's> {
139 fn ensure_device(&mut self) {
140 if self.state.0.render_device.is_none() {
141 self.state.0.render_device = Some(self.render_device.clone());
142 }
143 }
144
145 pub fn render_device(&self) -> &RenderDevice {
147 &self.render_device
148 }
149
150 pub fn diagnostic_recorder(&self) -> Option<Res<'w, DiagnosticsRecorder>> {
152 self.diagnostics_recorder.as_ref().map(Res::clone)
153 }
154
155 pub fn command_encoder(&mut self) -> &mut CommandEncoder {
157 self.ensure_device();
158 self.state.command_encoder()
159 }
160
161 pub fn begin_tracked_render_pass<'a>(
163 &'a mut self,
164 descriptor: RenderPassDescriptor<'_>,
165 ) -> TrackedRenderPass<'a> {
166 self.ensure_device();
167
168 let command_encoder = self.state.0.command_encoder.get_or_insert_with(|| {
169 self.render_device
170 .create_command_encoder(&wgpu::CommandEncoderDescriptor::default())
171 });
172
173 let render_pass = command_encoder.begin_render_pass(&descriptor);
174 TrackedRenderPass::new(&self.render_device, render_pass)
175 }
176
177 pub fn add_command_buffer(&mut self, command_buffer: CommandBuffer) {
179 self.state.flush_encoder();
180 self.state.0.command_buffers.push(command_buffer);
181 }
182}
183
184#[derive(SystemParam)]
188pub struct FlushCommands<'w> {
189 pending: ResMut<'w, PendingCommandBuffers>,
190 queue: Res<'w, super::RenderQueue>,
191}
192
193impl<'w> FlushCommands<'w> {
194 pub fn flush(&mut self) {
196 let buffers = self.pending.take();
197 if !buffers.is_empty() {
198 self.queue.submit(buffers);
199 }
200 }
201}
202
203#[derive(Resource, Debug, Clone, Copy, PartialEq, Eq, Deref, DerefMut)]
205pub struct CurrentView(pub Entity);
206
207pub struct ViewQuery<'w, 's, D: QueryData, F: QueryFilter = ()> {
210 entity: Entity,
211 item: D::Item<'w, 's>,
212 _filter: PhantomData<F>,
213}
214
215impl<'w, 's, D: QueryData, F: QueryFilter> ViewQuery<'w, 's, D, F> {
216 #[inline]
217 pub fn entity(&self) -> Entity {
218 self.entity
219 }
220
221 #[inline]
222 pub fn into_inner(self) -> D::Item<'w, 's> {
223 self.item
224 }
225}
226
227pub struct ViewQueryState<D: QueryData, F: QueryFilter> {
228 resource_id: ComponentId,
229 query_state: QueryState<D, F>,
230}
231
232unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam
235 for ViewQuery<'a, '_, D, F>
236{
237 type State = ViewQueryState<D, F>;
238 type Item<'w, 's> = ViewQuery<'w, 's, D, F>;
239
240 fn init_state(world: &mut World) -> Self::State {
241 ViewQueryState {
242 resource_id: world
243 .components_registrator()
244 .register_component::<CurrentView>(),
245 query_state: QueryState::new(world),
246 }
247 }
248
249 fn init_access(
250 state: &Self::State,
251 system_meta: &mut SystemMeta,
252 component_access_set: &mut FilteredAccessSet,
253 world: &mut World,
254 ) {
255 component_access_set.add_resource_read(state.resource_id);
256
257 <Query<'_, '_, D, F> as SystemParam>::init_access(
258 &state.query_state,
259 system_meta,
260 component_access_set,
261 world,
262 );
263 }
264
265 #[inline]
266 unsafe fn get_param<'w, 's>(
267 state: &'s mut Self::State,
268 _system_meta: &SystemMeta,
269 world: UnsafeWorldCell<'w>,
270 _change_tick: Tick,
271 ) -> Result<Self::Item<'w, 's>, SystemParamValidationError> {
272 let current_view = unsafe { world.get_resource::<CurrentView>() };
274
275 let Some(current_view) = current_view else {
276 return Err(SystemParamValidationError::skipped::<Self>(
277 "CurrentView resource not present",
278 ));
279 };
280
281 let entity = current_view.entity();
282
283 let item = unsafe { state.query_state.get_unchecked(world, entity) }.map_err(|_| {
286 SystemParamValidationError::skipped::<Self>("Current view entity does not match query")
287 })?;
288
289 Ok(ViewQuery {
290 entity,
291 item,
292 _filter: PhantomData,
293 })
294 }
295}
296
297unsafe impl<'w, 's, D: bevy_ecs::query::ReadOnlyQueryData + 'static, F: QueryFilter + 'static>
299 bevy_ecs::system::ReadOnlySystemParam for ViewQuery<'w, 's, D, F>
300{
301}