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(#[automatically_derived]
impl ::core::default::Default for PendingCommandBuffersInner {
    #[inline]
    fn default() -> PendingCommandBuffersInner {
        PendingCommandBuffersInner {
            buffers: ::core::default::Default::default(),
            encoders: ::core::default::Default::default(),
        }
    }
}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(impl bevy_ecs::resource::Resource for PendingCommandBuffers where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {}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(#[automatically_derived]
impl ::core::default::Default for RenderContextStateInner {
    #[inline]
    fn default() -> RenderContextStateInner {
        RenderContextStateInner {
            command_encoder: ::core::default::Default::default(),
            command_buffers: ::core::default::Default::default(),
            render_device: ::core::default::Default::default(),
        }
    }
}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            {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("RenderContextState::apply",
                        "bevy_render::renderer::render_context",
                        ::tracing::Level::INFO,
                        ::tracing_core::__macro_support::Option::Some("src/renderer/render_context.rs"),
                        ::tracing_core::__macro_support::Option::Some(110u32),
                        ::tracing_core::__macro_support::Option::Some("bevy_render::renderer::render_context"),
                        ::tracing_core::field::FieldSet::new(&[{
                                            const NAME:
                                                ::tracing::__macro_support::FieldName<{
                                                    ::tracing::__macro_support::FieldName::len("system")
                                                }> =
                                                ::tracing::__macro_support::FieldName::new("system");
                                            NAME.as_str()
                                        }], ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::SPAN)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let mut interest = ::tracing::subscriber::Interest::never();
    if ::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL &&
                    ::tracing::Level::INFO <=
                        ::tracing::level_filters::LevelFilter::current() &&
                { interest = __CALLSITE.interest(); !interest.is_never() } &&
            ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                interest) {
        let meta = __CALLSITE.metadata();
        ::tracing::Span::new(meta,
            &{
                    #[allow(unused_imports)]
                    use ::tracing::field::{debug, display, Value};
                    meta.fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&::tracing::field::display(&_system_meta.name())
                                                as &dyn ::tracing::field::Value))])
                })
    } else {
        let span =
            ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
        {};
        span
    }
}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(const _: () =
    {
        type __StructFieldsAlias<'w, 's> =
            (Deferred<'s, RenderContextState>, Res<'w, RenderDevice>,
            Option<Res<'w, DiagnosticsRecorder>>);
        #[doc(hidden)]
        pub struct FetchState {
            state: <__StructFieldsAlias<'static, 'static> as
            bevy_ecs::system::SystemParam>::State,
        }
        unsafe impl bevy_ecs::system::SystemParam for RenderContext<'_, '_> {
            type State = FetchState<>;
            type Item<'w, 's> = RenderContext<'w, 's>;
            fn init_state(world: &mut bevy_ecs::world::World) -> Self::State {
                FetchState {
                    state: <__StructFieldsAlias<'_, '_> as
                            bevy_ecs::system::SystemParam>::init_state(world),
                }
            }
            fn init_access(state: &Self::State,
                system_meta: &mut bevy_ecs::system::SystemMeta,
                component_access_set: &mut bevy_ecs::query::FilteredAccessSet,
                world: &mut bevy_ecs::world::World) {
                <__StructFieldsAlias<'_, '_> as
                        bevy_ecs::system::SystemParam>::init_access(&state.state,
                    system_meta, component_access_set, world);
            }
            fn apply(state: &mut Self::State,
                system_meta: &bevy_ecs::system::SystemMeta,
                world: &mut bevy_ecs::world::World) {
                <__StructFieldsAlias<'_, '_> as
                        bevy_ecs::system::SystemParam>::apply(&mut state.state,
                    system_meta, world);
            }
            fn queue(state: &mut Self::State,
                system_meta: &bevy_ecs::system::SystemMeta,
                world: bevy_ecs::world::DeferredWorld) {
                <__StructFieldsAlias<'_, '_> as
                        bevy_ecs::system::SystemParam>::queue(&mut state.state,
                    system_meta, world);
            }
            #[inline]
            unsafe fn get_param<'w,
                's>(state: &'s mut Self::State,
                system_meta: &bevy_ecs::system::SystemMeta,
                world:
                    bevy_ecs::world::unsafe_world_cell::UnsafeWorldCell<'w>,
                change_tick: bevy_ecs::change_detection::Tick)
                ->
                    ::core::result::Result<Self::Item<'w, 's>,
                    bevy_ecs::system::SystemParamValidationError> {
                let (fieldstate, fieldrender_device,
                        fielddiagnostics_recorder) = &mut state.state;
                let fieldstate =
                    unsafe {
                                <Deferred<'s, RenderContextState> as
                                        bevy_ecs::system::SystemParam>::get_param(fieldstate,
                                    system_meta, world, change_tick)
                            }.map_err(|err|
                                bevy_ecs::system::SystemParamValidationError::new::<Self>(err.skipped,
                                    err.message, "::state"))?;
                let fieldrender_device =
                    unsafe {
                                <Res<'w, RenderDevice> as
                                        bevy_ecs::system::SystemParam>::get_param(fieldrender_device,
                                    system_meta, world, change_tick)
                            }.map_err(|err|
                                bevy_ecs::system::SystemParamValidationError::new::<Self>(err.skipped,
                                    err.message, "::render_device"))?;
                let fielddiagnostics_recorder =
                    unsafe {
                                <Option<Res<'w, DiagnosticsRecorder>> as
                                        bevy_ecs::system::SystemParam>::get_param(fielddiagnostics_recorder,
                                    system_meta, world, change_tick)
                            }.map_err(|err|
                                bevy_ecs::system::SystemParamValidationError::new::<Self>(err.skipped,
                                    err.message, "::diagnostics_recorder"))?;
                ::core::result::Result::Ok(RenderContext {
                        state: fieldstate,
                        render_device: fieldrender_device,
                        diagnostics_recorder: fielddiagnostics_recorder,
                    })
            }
        }
        unsafe impl<'w, 's> bevy_ecs::system::ReadOnlySystemParam for
            RenderContext<'w, 's> where
            Deferred<'s,
            RenderContextState>: bevy_ecs::system::ReadOnlySystemParam,
            Res<'w, RenderDevice>: bevy_ecs::system::ReadOnlySystemParam,
            Option<Res<'w,
            DiagnosticsRecorder>>: bevy_ecs::system::ReadOnlySystemParam {}
    };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(const _: () =
    {
        type __StructFieldsAlias<'w, 's> =
            (ResMut<'w, PendingCommandBuffers>, Res<'w, super::RenderQueue>);
        #[doc(hidden)]
        pub struct FetchState {
            state: <__StructFieldsAlias<'static, 'static> as
            bevy_ecs::system::SystemParam>::State,
        }
        unsafe impl bevy_ecs::system::SystemParam for FlushCommands<'_> {
            type State = FetchState<>;
            type Item<'w, 's> = FlushCommands<'w>;
            fn init_state(world: &mut bevy_ecs::world::World) -> Self::State {
                FetchState {
                    state: <__StructFieldsAlias<'_, '_> as
                            bevy_ecs::system::SystemParam>::init_state(world),
                }
            }
            fn init_access(state: &Self::State,
                system_meta: &mut bevy_ecs::system::SystemMeta,
                component_access_set: &mut bevy_ecs::query::FilteredAccessSet,
                world: &mut bevy_ecs::world::World) {
                <__StructFieldsAlias<'_, '_> as
                        bevy_ecs::system::SystemParam>::init_access(&state.state,
                    system_meta, component_access_set, world);
            }
            fn apply(state: &mut Self::State,
                system_meta: &bevy_ecs::system::SystemMeta,
                world: &mut bevy_ecs::world::World) {
                <__StructFieldsAlias<'_, '_> as
                        bevy_ecs::system::SystemParam>::apply(&mut state.state,
                    system_meta, world);
            }
            fn queue(state: &mut Self::State,
                system_meta: &bevy_ecs::system::SystemMeta,
                world: bevy_ecs::world::DeferredWorld) {
                <__StructFieldsAlias<'_, '_> as
                        bevy_ecs::system::SystemParam>::queue(&mut state.state,
                    system_meta, world);
            }
            #[inline]
            unsafe fn get_param<'w,
                's>(state: &'s mut Self::State,
                system_meta: &bevy_ecs::system::SystemMeta,
                world:
                    bevy_ecs::world::unsafe_world_cell::UnsafeWorldCell<'w>,
                change_tick: bevy_ecs::change_detection::Tick)
                ->
                    ::core::result::Result<Self::Item<'w, 's>,
                    bevy_ecs::system::SystemParamValidationError> {
                let (fieldpending, fieldqueue) = &mut state.state;
                let fieldpending =
                    unsafe {
                                <ResMut<'w, PendingCommandBuffers> as
                                        bevy_ecs::system::SystemParam>::get_param(fieldpending,
                                    system_meta, world, change_tick)
                            }.map_err(|err|
                                bevy_ecs::system::SystemParamValidationError::new::<Self>(err.skipped,
                                    err.message, "::pending"))?;
                let fieldqueue =
                    unsafe {
                                <Res<'w, super::RenderQueue> as
                                        bevy_ecs::system::SystemParam>::get_param(fieldqueue,
                                    system_meta, world, change_tick)
                            }.map_err(|err|
                                bevy_ecs::system::SystemParamValidationError::new::<Self>(err.skipped,
                                    err.message, "::queue"))?;
                ::core::result::Result::Ok(FlushCommands {
                        pending: fieldpending,
                        queue: fieldqueue,
                    })
            }
        }
        unsafe impl<'w, 's> bevy_ecs::system::ReadOnlySystemParam for
            FlushCommands<'w> where
            ResMut<'w,
            PendingCommandBuffers>: bevy_ecs::system::ReadOnlySystemParam,
            Res<'w, super::RenderQueue>: bevy_ecs::system::ReadOnlySystemParam
            {}
    };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(impl bevy_ecs::resource::Resource for CurrentView where
    Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource, #[automatically_derived]
impl ::core::fmt::Debug for CurrentView {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_tuple_field1_finish(f, "CurrentView",
            &&self.0)
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for CurrentView {
    #[inline]
    fn clone(&self) -> CurrentView {
        let _: ::core::clone::AssertParamIsClone<Entity>;
        *self
    }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for CurrentView { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for CurrentView {
    #[inline]
    fn eq(&self, other: &CurrentView) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for CurrentView {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<Entity>;
    }
}Eq, impl ::core::ops::Deref for CurrentView {
    type Target = Entity;
    fn deref(&self) -> &Self::Target { &self.0 }
}Deref, impl ::core::ops::DerefMut for CurrentView {
    fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}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}