Path: blob/main/crates/bevy_render/src/renderer/render_context.rs
9328 views
use super::WgpuWrapper;1use crate::diagnostic::internal::DiagnosticsRecorder;2use crate::render_phase::TrackedRenderPass;3use crate::render_resource::{CommandEncoder, RenderPassDescriptor};4use crate::renderer::RenderDevice;5use bevy_derive::{Deref, DerefMut};6use bevy_ecs::change_detection::Tick;7use bevy_ecs::component::ComponentId;8use bevy_ecs::prelude::*;9use bevy_ecs::query::{FilteredAccessSet, QueryData, QueryFilter, QueryState};10use bevy_ecs::system::{11Deferred, SystemBuffer, SystemMeta, SystemParam, SystemParamValidationError,12};13use bevy_ecs::world::unsafe_world_cell::UnsafeWorldCell;14use bevy_ecs::world::DeferredWorld;15use bevy_log::info_span;16use core::marker::PhantomData;17use wgpu::CommandBuffer;1819#[derive(Default)]20struct PendingCommandBuffersInner {21buffers: Vec<CommandBuffer>,22encoders: Vec<CommandEncoder>,23}2425/// A resource that holds command buffers and encoders that are pending submission to the render queue.26#[derive(Resource)]27pub struct PendingCommandBuffers(WgpuWrapper<PendingCommandBuffersInner>);2829impl Default for PendingCommandBuffers {30fn default() -> Self {31Self(WgpuWrapper::new(PendingCommandBuffersInner::default()))32}33}3435impl PendingCommandBuffers {36pub fn push(&mut self, buffers: impl IntoIterator<Item = CommandBuffer>) {37self.0.buffers.extend(buffers);38}3940pub fn push_encoder(&mut self, encoder: CommandEncoder) {41self.0.encoders.push(encoder);42}4344pub fn take(&mut self) -> Vec<CommandBuffer> {45let encoders: Vec<_> = self.0.encoders.drain(..).collect();46for encoder in encoders {47self.0.buffers.push(encoder.finish());48}49core::mem::take(&mut self.0.buffers)50}5152pub fn is_empty(&self) -> bool {53self.0.buffers.is_empty() && self.0.encoders.is_empty()54}5556pub fn len(&self) -> usize {57self.0.buffers.len() + self.0.encoders.len()58}59}6061#[derive(Default)]62struct RenderContextStateInner {63command_encoder: Option<CommandEncoder>,64command_buffers: Vec<CommandBuffer>,65render_device: Option<RenderDevice>,66}6768/// A resource that holds the current render context state, including command encoder and command buffers.69/// This is used internally by the [`RenderContext`] system parameter. Implements [`SystemBuffer`] to flush70/// command buffers at the end of each render system in topological system order.71pub struct RenderContextState(WgpuWrapper<RenderContextStateInner>);7273impl Default for RenderContextState {74fn default() -> Self {75Self(WgpuWrapper::new(RenderContextStateInner::default()))76}77}7879impl RenderContextState {80fn flush_encoder(&mut self) {81if let Some(encoder) = self.0.command_encoder.take() {82self.0.command_buffers.push(encoder.finish());83}84}8586fn command_encoder(&mut self) -> &mut CommandEncoder {87let render_device = self88.089.render_device90.clone()91.expect("RenderDevice must be set before accessing command_encoder");9293self.0.command_encoder.get_or_insert_with(|| {94render_device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default())95})96}9798pub fn finish(&mut self) -> Vec<CommandBuffer> {99self.flush_encoder();100core::mem::take(&mut self.0.command_buffers)101}102}103104impl SystemBuffer for RenderContextState {105fn queue(&mut self, system_meta: &SystemMeta, mut world: DeferredWorld) {106let _span = info_span!("RenderContextState::apply", system = %system_meta.name()).entered();107108let inner = &mut *self.0;109110// flush to ensure correct submission order111if let Some(encoder) = inner.command_encoder.take() {112inner.command_buffers.push(encoder.finish());113}114115if !inner.command_buffers.is_empty() {116let mut pending = world.resource_mut::<PendingCommandBuffers>();117pending.push(core::mem::take(&mut inner.command_buffers));118}119120inner.render_device = None;121}122}123124/// A system parameter that provides access to a command encoder and render device for issuing125/// rendering commands inside any system running beneath the root [`super::RenderGraph`] schedule in the126/// [`super::render_system`] system.127#[derive(SystemParam)]128pub struct RenderContext<'w, 's> {129state: Deferred<'s, RenderContextState>,130render_device: Res<'w, RenderDevice>,131diagnostics_recorder: Option<Res<'w, DiagnosticsRecorder>>,132}133134impl<'w, 's> RenderContext<'w, 's> {135fn ensure_device(&mut self) {136if self.state.0.render_device.is_none() {137self.state.0.render_device = Some(self.render_device.clone());138}139}140141/// Returns the render device associated with this render context.142pub fn render_device(&self) -> &RenderDevice {143&self.render_device144}145146/// Returns the diagnostics recorder, if available.147pub fn diagnostic_recorder(&self) -> Option<Res<'w, DiagnosticsRecorder>> {148self.diagnostics_recorder.as_ref().map(Res::clone)149}150151/// Returns the current command encoder, creating one if it does not already exist.152pub fn command_encoder(&mut self) -> &mut CommandEncoder {153self.ensure_device();154self.state.command_encoder()155}156157/// Begins a tracked render pass with the given descriptor.158pub fn begin_tracked_render_pass<'a>(159&'a mut self,160descriptor: RenderPassDescriptor<'_>,161) -> TrackedRenderPass<'a> {162self.ensure_device();163164let command_encoder = self.state.0.command_encoder.get_or_insert_with(|| {165self.render_device166.create_command_encoder(&wgpu::CommandEncoderDescriptor::default())167});168169let render_pass = command_encoder.begin_render_pass(&descriptor);170TrackedRenderPass::new(&self.render_device, render_pass)171}172173/// Adds a finished command buffer to be submitted later.174pub fn add_command_buffer(&mut self, command_buffer: CommandBuffer) {175self.state.flush_encoder();176self.state.0.command_buffers.push(command_buffer);177}178}179180/// A system parameter that can be used to explicitly flush pending command buffers to the render queue.181/// This is typically not necessary, as command buffers are automatically flushed at the end of each182/// render system. However, in some cases it may be useful to flush command buffers earlier.183#[derive(SystemParam)]184pub struct FlushCommands<'w> {185pending: ResMut<'w, PendingCommandBuffers>,186queue: Res<'w, super::RenderQueue>,187}188189impl<'w> FlushCommands<'w> {190/// Flushes all pending command buffers to the render queue.191pub fn flush(&mut self) {192let buffers = self.pending.take();193if !buffers.is_empty() {194self.queue.submit(buffers);195}196}197}198199/// The entity corresponding to the current view being rendered.200#[derive(Resource, Debug, Clone, Copy, PartialEq, Eq, Deref, DerefMut)]201pub struct CurrentView(pub Entity);202203/// A query that fetches components for the entity corresponding to the current view being rendered,204/// as defined by the [`CurrentView`] resource, equivalent to `query.get(current_view.entity())`.205pub struct ViewQuery<'w, 's, D: QueryData, F: QueryFilter = ()> {206entity: Entity,207item: D::Item<'w, 's>,208_filter: PhantomData<F>,209}210211impl<'w, 's, D: QueryData, F: QueryFilter> ViewQuery<'w, 's, D, F> {212#[inline]213pub fn entity(&self) -> Entity {214self.entity215}216217#[inline]218pub fn into_inner(self) -> D::Item<'w, 's> {219self.item220}221}222223pub struct ViewQueryState<D: QueryData, F: QueryFilter> {224resource_id: ComponentId,225query_state: QueryState<D, F>,226}227228// SAFETY: ViewQuery accesses the CurrentView resource (read) and query components.229// Access is properly registered in init_access.230unsafe impl<'a, D: QueryData + 'static, F: QueryFilter + 'static> SystemParam231for ViewQuery<'a, '_, D, F>232{233type State = ViewQueryState<D, F>;234type Item<'w, 's> = ViewQuery<'w, 's, D, F>;235236fn init_state(world: &mut World) -> Self::State {237ViewQueryState {238resource_id: world239.components_registrator()240.register_resource::<CurrentView>(),241query_state: QueryState::new(world),242}243}244245fn init_access(246state: &Self::State,247system_meta: &mut SystemMeta,248component_access_set: &mut FilteredAccessSet,249world: &mut World,250) {251component_access_set.add_unfiltered_resource_read(state.resource_id);252253<Query<'_, '_, D, F> as SystemParam>::init_access(254&state.query_state,255system_meta,256component_access_set,257world,258);259}260261#[inline]262unsafe fn validate_param(263state: &mut Self::State,264_system_meta: &SystemMeta,265world: UnsafeWorldCell,266) -> Result<(), SystemParamValidationError> {267// SAFETY: We have registered resource read access in init_access268let current_view = unsafe { world.get_resource::<CurrentView>() };269270let Some(current_view) = current_view else {271return Err(SystemParamValidationError::skipped::<Self>(272"CurrentView resource not present",273));274};275276let entity = current_view.entity();277278// SAFETY: Query state access is properly registered in init_access.279// The caller ensures the world matches the one used in init_state.280let result = unsafe { state.query_state.get_unchecked(world, entity) };281282if result.is_err() {283return Err(SystemParamValidationError::skipped::<Self>(284"Current view entity does not match query",285));286}287288Ok(())289}290291#[inline]292unsafe fn get_param<'w, 's>(293state: &'s mut Self::State,294_system_meta: &SystemMeta,295world: UnsafeWorldCell<'w>,296_change_tick: Tick,297) -> Self::Item<'w, 's> {298// SAFETY: We have registered resource read access and validate_param succeeded299let current_view = unsafe {300world301.get_resource::<CurrentView>()302.expect("CurrentView must exist")303};304305let entity = current_view.entity();306307// SAFETY: Query state access is properly registered in init_access.308// validate_param verified the entity matches.309let item = unsafe {310state311.query_state312.get_unchecked(world, entity)313.expect("view entity must match query")314};315316ViewQuery {317entity,318item,319_filter: PhantomData,320}321}322}323324// SAFETY: ViewQuery with ReadOnlyQueryData only reads from the world.325unsafe impl<'w, 's, D: bevy_ecs::query::ReadOnlyQueryData + 'static, F: QueryFilter + 'static>326bevy_ecs::system::ReadOnlySystemParam for ViewQuery<'w, 's, D, F>327{328}329330331