diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index f4cd4ad07be5d..4e3f2c44fd57e 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -82,19 +82,47 @@ pub fn render_system( { let _span = info_span!("present_frames").entered(); - world.resource_scope(|world, mut windows: Mut| { + let is_cpu = world + .get_resource::() + .is_some_and(|info| info.device_type == DeviceType::Cpu); + + // Collect windows that need presenting BEFORE removing `ViewTarget` + let windows_to_present: Vec = { let views = state.get(world); - for (view_target, camera) in views.iter() { - if let Some(NormalizedRenderTarget::Window(window)) = camera.target - && view_target.needs_present() - { - let Some(window) = windows.get_mut(&window.entity()) else { - continue; - }; - window.present(); - } + views + .iter() + .filter_map(|(view_target, camera)| { + if let Some(NormalizedRenderTarget::Window(window)) = camera.target + && view_target.needs_present() + { + return Some(window.entity()); + } + None + }) + .collect() + }; + + // On software renderers (CPU device type), remove `ViewTarget` components to ensure + // the cloned `TextureView`s they hold are dropped. This prepares for surface + // reconfiguration on the next frame. (The original `swap_chain_texture_view` is + // dropped in `clear_view_attachments` which runs before `create_surfaces`.) + if is_cpu { + let view_entities: Vec = world + .query_filtered::>() + .iter(world) + .collect(); + for entity in view_entities { + world.entity_mut(entity).remove::(); } - }); + } + + // Present the windows that were marked for presentation + let mut windows = world.resource_mut::(); + for window_entity in windows_to_present { + if let Some(window) = windows.get_mut(&window_entity) { + window.present(); + } + } #[cfg(feature = "tracing-tracy")] tracing::event!( diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index b1375b9a1975d..6c41538b35c90 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -16,7 +16,7 @@ use crate::{ render_asset::RenderAssets, render_phase::ViewRangefinder3d, render_resource::{DynamicUniformBuffer, ShaderType, Texture, TextureView}, - renderer::{RenderDevice, RenderQueue}, + renderer::{RenderAdapterInfo, RenderDevice, RenderQueue}, sync_world::MainEntity, texture::{ CachedTexture, ColorAttachment, DepthAttachment, GpuImage, ManualTextureViews, @@ -1035,9 +1035,25 @@ pub fn prepare_view_attachments( } } -/// Clears the view target [`OutputColorAttachment`]s. -pub fn clear_view_attachments(mut view_target_attachments: ResMut) { +/// Clears the view target [`OutputColorAttachment`]s and drops texture views for windows +/// that need reconfiguration on software renderers. +pub fn clear_view_attachments( + mut view_target_attachments: ResMut, + mut windows: ResMut, + render_adapter_info: Res, +) { view_target_attachments.clear(); + + // On software renderers (CPU device type), drop `swap_chain_texture_view` for windows + // that need reconfiguration. This must happen BEFORE `create_surfaces` runs, otherwise + // wgpu/DX12 will error with "surface is in use" when trying to resize. + if render_adapter_info.device_type == wgpu::DeviceType::Cpu { + for window in windows.values_mut() { + if window.size_changed || window.present_mode_changed { + drop(window.swap_chain_texture_view.take()); + } + } + } } pub fn prepare_view_targets(