diff --git a/crates/bevy_render/src/view/window/mod.rs b/crates/bevy_render/src/view/window/mod.rs index 656d4ceaa881a..6bca35efdea2d 100644 --- a/crates/bevy_render/src/view/window/mod.rs +++ b/crates/bevy_render/src/view/window/mod.rs @@ -17,7 +17,8 @@ use std::{ sync::PoisonError, }; use wgpu::{ - BufferUsages, SurfaceTargetUnsafe, TextureFormat, TextureUsages, TextureViewDescriptor, + BufferUsages, SurfaceConfiguration, SurfaceTargetUnsafe, TextureFormat, TextureUsages, + TextureViewDescriptor, }; pub mod screenshot; @@ -42,7 +43,7 @@ impl Plugin for WindowRenderPlugin { .add_systems( Render, create_surfaces - .run_if(need_new_surfaces) + .run_if(need_surface_configuration) .before(prepare_windows), ) .add_systems(Render, prepare_windows.in_set(RenderSet::ManageViews)); @@ -198,7 +199,7 @@ fn extract_windows( struct SurfaceData { // TODO: what lifetime should this be? surface: wgpu::Surface<'static>, - format: TextureFormat, + configuration: SurfaceConfiguration, } #[derive(Resource, Default)] @@ -254,45 +255,12 @@ pub fn prepare_windows( continue; }; - let surface_configuration = wgpu::SurfaceConfiguration { - format: surface_data.format, - width: window.physical_width, - height: window.physical_height, - usage: TextureUsages::RENDER_ATTACHMENT, - present_mode: match window.present_mode { - PresentMode::Fifo => wgpu::PresentMode::Fifo, - PresentMode::FifoRelaxed => wgpu::PresentMode::FifoRelaxed, - PresentMode::Mailbox => wgpu::PresentMode::Mailbox, - PresentMode::Immediate => wgpu::PresentMode::Immediate, - PresentMode::AutoVsync => wgpu::PresentMode::AutoVsync, - PresentMode::AutoNoVsync => wgpu::PresentMode::AutoNoVsync, - }, - // TODO: Expose this as a setting somewhere - // 2 is wgpu's default/what we've been using so far. - // 1 is the minimum, but may cause lower framerates due to the cpu waiting for the gpu to finish - // all work for the previous frame before starting work on the next frame, which then means the gpu - // has to wait for the cpu to finish to start on the next frame. - desired_maximum_frame_latency: 2, - alpha_mode: match window.alpha_mode { - CompositeAlphaMode::Auto => wgpu::CompositeAlphaMode::Auto, - CompositeAlphaMode::Opaque => wgpu::CompositeAlphaMode::Opaque, - CompositeAlphaMode::PreMultiplied => wgpu::CompositeAlphaMode::PreMultiplied, - CompositeAlphaMode::PostMultiplied => wgpu::CompositeAlphaMode::PostMultiplied, - CompositeAlphaMode::Inherit => wgpu::CompositeAlphaMode::Inherit, - }, - view_formats: if !surface_data.format.is_srgb() { - vec![surface_data.format.add_srgb_suffix()] - } else { - vec![] - }, - }; - // This is an ugly hack to work around drivers that don't support MSAA. // This should be removed once https://github.com/bevyengine/bevy/issues/7194 lands and we're doing proper // feature detection for MSAA. // When removed, we can also remove the `.after(prepare_windows)` of `prepare_core_3d_depth_textures` and `prepare_prepass_textures` let sample_flags = render_adapter - .get_texture_format_features(surface_configuration.format) + .get_texture_format_features(surface_data.configuration.format) .flags; if !sample_flags.sample_count_supported(msaa.samples()) { @@ -340,7 +308,6 @@ pub fn prepare_windows( let surface = &surface_data.surface; if not_already_configured || window.size_changed || window.present_mode_changed { - render_device.configure_surface(surface, &surface_configuration); let frame = surface .get_current_texture() .expect("Error configuring surface"); @@ -351,7 +318,7 @@ pub fn prepare_windows( window.set_swapchain_texture(frame); } Err(wgpu::SurfaceError::Outdated) => { - render_device.configure_surface(surface, &surface_configuration); + render_device.configure_surface(surface, &surface_data.configuration); let frame = surface .get_current_texture() .expect("Error reconfiguring surface"); @@ -369,20 +336,20 @@ pub fn prepare_windows( } } }; - window.swap_chain_texture_format = Some(surface_data.format); + window.swap_chain_texture_format = Some(surface_data.configuration.format); if window.screenshot_func.is_some() { let texture = render_device.create_texture(&wgpu::TextureDescriptor { label: Some("screenshot-capture-rendertarget"), size: wgpu::Extent3d { - width: surface_configuration.width, - height: surface_configuration.height, + width: surface_data.configuration.width, + height: surface_data.configuration.height, depth_or_array_layers: 1, }, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, - format: surface_configuration.format.add_srgb_suffix(), + format: surface_data.configuration.format.add_srgb_suffix(), usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::COPY_SRC | TextureUsages::TEXTURE_BINDING, @@ -394,7 +361,7 @@ pub fn prepare_windows( size: screenshot::get_aligned_size( window.physical_width, window.physical_height, - surface_data.format.pixel_size() as u32, + surface_data.configuration.format.pixel_size() as u32, ) as u64, usage: BufferUsages::MAP_READ | BufferUsages::COPY_DST, mapped_at_creation: false, @@ -407,7 +374,7 @@ pub fn prepare_windows( let pipeline_id = pipelines.specialize( &pipeline_cache, &screenshot_pipeline, - surface_configuration.format, + surface_data.configuration.format, ); window.swap_chain_texture_view = Some(texture_view); window.screenshot_memory = Some(ScreenshotPreparedState { @@ -420,12 +387,15 @@ pub fn prepare_windows( } } -pub fn need_new_surfaces( +pub fn need_surface_configuration( windows: Res, window_surfaces: Res, ) -> bool { for window in windows.windows.values() { - if !window_surfaces.configured_windows.contains(&window.entity) { + if !window_surfaces.configured_windows.contains(&window.entity) + || window.size_changed + || window.present_mode_changed + { return true; } } @@ -443,9 +413,10 @@ pub fn create_surfaces( mut window_surfaces: ResMut, render_instance: Res, render_adapter: Res, + render_device: Res, ) { for window in windows.windows.values() { - window_surfaces + let data = window_surfaces .surfaces .entry(window.entity) .or_insert_with(|| { @@ -477,7 +448,63 @@ pub fn create_surfaces( } } - SurfaceData { surface, format } + let configuration = wgpu::SurfaceConfiguration { + format, + width: window.physical_width, + height: window.physical_height, + usage: TextureUsages::RENDER_ATTACHMENT, + present_mode: match window.present_mode { + PresentMode::Fifo => wgpu::PresentMode::Fifo, + PresentMode::FifoRelaxed => wgpu::PresentMode::FifoRelaxed, + PresentMode::Mailbox => wgpu::PresentMode::Mailbox, + PresentMode::Immediate => wgpu::PresentMode::Immediate, + PresentMode::AutoVsync => wgpu::PresentMode::AutoVsync, + PresentMode::AutoNoVsync => wgpu::PresentMode::AutoNoVsync, + }, + // TODO: Expose this as a setting somewhere + // 2 is wgpu's default/what we've been using so far. + // 1 is the minimum, but may cause lower framerates due to the cpu waiting for the gpu to finish + // all work for the previous frame before starting work on the next frame, which then means the gpu + // has to wait for the cpu to finish to start on the next frame. + desired_maximum_frame_latency: 2, + alpha_mode: match window.alpha_mode { + CompositeAlphaMode::Auto => wgpu::CompositeAlphaMode::Auto, + CompositeAlphaMode::Opaque => wgpu::CompositeAlphaMode::Opaque, + CompositeAlphaMode::PreMultiplied => { + wgpu::CompositeAlphaMode::PreMultiplied + } + CompositeAlphaMode::PostMultiplied => { + wgpu::CompositeAlphaMode::PostMultiplied + } + CompositeAlphaMode::Inherit => wgpu::CompositeAlphaMode::Inherit, + }, + view_formats: if !format.is_srgb() { + vec![format.add_srgb_suffix()] + } else { + vec![] + }, + }; + + render_device.configure_surface(&surface, &configuration); + + SurfaceData { + surface, + configuration, + } }); + + if window.size_changed || window.present_mode_changed { + data.configuration.width = window.physical_width; + data.configuration.height = window.physical_height; + data.configuration.present_mode = match window.present_mode { + PresentMode::Fifo => wgpu::PresentMode::Fifo, + PresentMode::FifoRelaxed => wgpu::PresentMode::FifoRelaxed, + PresentMode::Mailbox => wgpu::PresentMode::Mailbox, + PresentMode::Immediate => wgpu::PresentMode::Immediate, + PresentMode::AutoVsync => wgpu::PresentMode::AutoVsync, + PresentMode::AutoNoVsync => wgpu::PresentMode::AutoNoVsync, + }; + render_device.configure_surface(&data.surface, &data.configuration); + } } }