Skip to main content

bevy_render/renderer/
render_context.rs

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/// A resource that holds command buffers and encoders that are pending submission to the render queue.
28#[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
70/// A resource that holds the current render context state, including command encoder and command buffers.
71/// This is used internally by the [`RenderContext`] system parameter. Implements [`SystemBuffer`] to flush
72/// command buffers at the end of each render system in topological system order.
73pub 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        // flush to ensure correct submission order
115        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/// A system parameter that provides access to a command encoder and render device for issuing
129/// rendering commands inside any system running beneath the root [`super::RenderGraph`] schedule in the
130/// [`super::render_system`] system.
131#[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    /// Returns the render device associated with this render context.
146    pub fn render_device(&self) -> &RenderDevice {
147        &self.render_device
148    }
149
150    /// Returns the diagnostics recorder, if available.
151    pub fn diagnostic_recorder(&self) -> Option<Res<'w, DiagnosticsRecorder>> {
152        self.diagnostics_recorder.as_ref().map(Res::clone)
153    }
154
155    /// Returns the current command encoder, creating one if it does not already exist.
156    pub fn command_encoder(&mut self) -> &mut CommandEncoder {
157        self.ensure_device();
158        self.state.command_encoder()
159    }
160
161    /// Begins a tracked render pass with the given descriptor.
162    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    /// Adds a finished command buffer to be submitted later.
178    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/// A system parameter that can be used to explicitly flush pending command buffers to the render queue.
185/// This is typically not necessary, as command buffers are automatically flushed at the end of each
186/// render system. However, in some cases it may be useful to flush command buffers earlier.
187#[derive(SystemParam)]
188pub struct FlushCommands<'w> {
189    pending: ResMut<'w, PendingCommandBuffers>,
190    queue: Res<'w, super::RenderQueue>,
191}
192
193impl<'w> FlushCommands<'w> {
194    /// Flushes all pending command buffers to the render queue.
195    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/// The entity corresponding to the current view being rendered.
204#[derive(Resource, Debug, Clone, Copy, PartialEq, Eq, Deref, DerefMut)]
205pub struct CurrentView(pub Entity);
206
207/// A query that fetches components for the entity corresponding to the current view being rendered,
208/// as defined by the [`CurrentView`] resource, equivalent to `query.get(current_view.entity())`.
209pub 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
232// SAFETY: ViewQuery accesses the CurrentView resource (read) and query components.
233// Access is properly registered in init_access.
234unsafe 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        // SAFETY: We have registered resource read access in init_access
273        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        // SAFETY: Query state access is properly registered in init_access.
284        // The caller ensures the world matches the one used in init_state.
285        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
297// SAFETY: ViewQuery with ReadOnlyQueryData only reads from the world.
298unsafe 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}