From c9b3665cddbb301c15f3a186ff5052d478b79597 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Wed, 26 Oct 2022 14:07:38 -0700 Subject: [PATCH 1/4] Support arbitrary RenderTarget texture formats --- .../bevy_core_pipeline/src/upscaling/mod.rs | 57 ++++++------------- crates/bevy_render/src/camera/camera.rs | 18 +++++- crates/bevy_render/src/view/mod.rs | 7 ++- crates/bevy_render/src/view/window.rs | 50 ++++++++++------ examples/3d/render_to_texture.rs | 2 +- 5 files changed, 73 insertions(+), 61 deletions(-) diff --git a/crates/bevy_core_pipeline/src/upscaling/mod.rs b/crates/bevy_core_pipeline/src/upscaling/mod.rs index cf34911565f42..59db4984c5d53 100644 --- a/crates/bevy_core_pipeline/src/upscaling/mod.rs +++ b/crates/bevy_core_pipeline/src/upscaling/mod.rs @@ -5,8 +5,8 @@ pub use node::UpscalingNode; use bevy_app::prelude::*; use bevy_asset::{load_internal_asset, HandleUntyped}; use bevy_ecs::prelude::*; -use bevy_render::renderer::{RenderDevice, SurfaceTextureFormat}; -use bevy_render::view::ExtractedView; +use bevy_render::renderer::RenderDevice; +use bevy_render::view::ViewTarget; use bevy_render::{render_resource::*, RenderApp, RenderStage}; use bevy_reflect::TypeUuid; @@ -39,13 +39,11 @@ impl Plugin for UpscalingPlugin { #[derive(Resource)] pub struct UpscalingPipeline { ldr_texture_bind_group: BindGroupLayout, - surface_texture_format: TextureFormat, } impl FromWorld for UpscalingPipeline { fn from_world(render_world: &mut World) -> Self { let render_device = render_world.resource::(); - let surface_texture_format = render_world.resource::().0; let ldr_texture_bind_group = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { @@ -72,50 +70,26 @@ impl FromWorld for UpscalingPipeline { UpscalingPipeline { ldr_texture_bind_group, - surface_texture_format, } } } -#[repr(u8)] +#[derive(PartialEq, Eq, Hash, Clone, Copy)] pub enum UpscalingMode { - Filtering = 0, - Nearest = 1, + Filtering, + Nearest, } -bitflags::bitflags! { - #[repr(transparent)] - pub struct UpscalingPipelineKey: u32 { - const NONE = 0; - const UPSCALING_MODE_RESERVED_BITS = UpscalingPipelineKey::UPSCALING_MODE_MASK_BITS << UpscalingPipelineKey::UPSCALING_MODE_SHIFT_BITS; - } -} - -impl UpscalingPipelineKey { - const UPSCALING_MODE_MASK_BITS: u32 = 0b1111; // enough for 16 different modes - const UPSCALING_MODE_SHIFT_BITS: u32 = 32 - 4; - - pub fn from_upscaling_mode(upscaling_mode: UpscalingMode) -> Self { - let upscaling_mode_bits = ((upscaling_mode as u32) & Self::UPSCALING_MODE_MASK_BITS) - << Self::UPSCALING_MODE_SHIFT_BITS; - UpscalingPipelineKey::from_bits(upscaling_mode_bits).unwrap() - } - - pub fn upscaling_mode(&self) -> UpscalingMode { - let upscaling_mode_bits = - (self.bits >> Self::UPSCALING_MODE_SHIFT_BITS) & Self::UPSCALING_MODE_MASK_BITS; - match upscaling_mode_bits { - 0 => UpscalingMode::Filtering, - 1 => UpscalingMode::Nearest, - other => panic!("invalid upscaling mode bits in UpscalingPipelineKey: {other}"), - } - } +#[derive(PartialEq, Eq, Hash, Clone, Copy)] +pub struct UpscalingPipelineKey { + upscaling_mode: UpscalingMode, + texture_format: TextureFormat, } impl SpecializedRenderPipeline for UpscalingPipeline { type Key = UpscalingPipelineKey; - fn specialize(&self, _: Self::Key) -> RenderPipelineDescriptor { + fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { RenderPipelineDescriptor { label: Some("upscaling pipeline".into()), layout: Some(vec![self.ldr_texture_bind_group.clone()]), @@ -125,7 +99,7 @@ impl SpecializedRenderPipeline for UpscalingPipeline { shader_defs: vec![], entry_point: "fs_main".into(), targets: vec![Some(ColorTargetState { - format: self.surface_texture_format, + format: key.texture_format, blend: None, write_mask: ColorWrites::ALL, })], @@ -147,10 +121,13 @@ fn queue_upscaling_bind_groups( mut pipeline_cache: ResMut, mut pipelines: ResMut>, upscaling_pipeline: Res, - view_targets: Query>, + view_targets: Query<(Entity, &ViewTarget)>, ) { - for entity in view_targets.iter() { - let key = UpscalingPipelineKey::from_upscaling_mode(UpscalingMode::Filtering); + for (entity, view_target) in view_targets.iter() { + let key = UpscalingPipelineKey { + upscaling_mode: UpscalingMode::Filtering, + texture_format: view_target.out_texture_format, + }; let pipeline = pipelines.specialize(&mut pipeline_cache, &upscaling_pipeline, key); commands.entity(entity).insert(UpscalingTarget { pipeline }); diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 586ca3a786feb..676ffe650dd84 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -24,7 +24,7 @@ use bevy_transform::components::GlobalTransform; use bevy_utils::HashSet; use bevy_window::{WindowCreated, WindowId, WindowResized, Windows}; use std::{borrow::Cow, ops::Range}; -use wgpu::Extent3d; +use wgpu::{Extent3d, TextureFormat}; /// Render viewport configuration for the [`Camera`] component. /// @@ -326,6 +326,22 @@ impl RenderTarget { } } + /// Retrieves the [`TextureFormat`] of this render target, if it exists. + pub fn get_texture_format<'a>( + &self, + windows: &'a ExtractedWindows, + images: &'a RenderAssets, + ) -> Option { + match self { + RenderTarget::Window(window_id) => windows + .get(window_id) + .and_then(|window| window.swap_chain_texture_format), + RenderTarget::Image(image_handle) => { + images.get(image_handle).map(|image| image.texture_format) + } + } + } + pub fn get_render_target_info( &self, windows: &Windows, diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index 06ce106fe2927..fbe42f38d0c36 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -143,6 +143,7 @@ impl ViewMainTexture { pub struct ViewTarget { pub main_texture: ViewMainTexture, pub out_texture: TextureView, + pub out_texture_format: TextureFormat, } impl ViewTarget { @@ -242,7 +243,10 @@ fn prepare_view_targets( let mut textures = HashMap::default(); for (entity, camera, view) in cameras.iter() { if let Some(target_size) = camera.physical_target_size { - if let Some(texture_view) = camera.target.get_texture_view(&windows, &images) { + if let (Some(texture_view), Some(texture_format)) = ( + camera.target.get_texture_view(&windows, &images), + camera.target.get_texture_format(&windows, &images), + ) { let size = Extent3d { width: target_size.x, height: target_size.y, @@ -319,6 +323,7 @@ fn prepare_view_targets( commands.entity(entity).insert(ViewTarget { main_texture: main_texture.clone(), out_texture: texture_view.clone(), + out_texture_format: texture_format, }); } } diff --git a/crates/bevy_render/src/view/window.rs b/crates/bevy_render/src/view/window.rs index 99f0dd0464309..fec72916990ff 100644 --- a/crates/bevy_render/src/view/window.rs +++ b/crates/bevy_render/src/view/window.rs @@ -10,6 +10,7 @@ use bevy_window::{ CompositeAlphaMode, PresentMode, RawHandleWrapper, WindowClosed, WindowId, Windows, }; use std::ops::{Deref, DerefMut}; +use wgpu::TextureFormat; /// Token to ensure a system runs on the main thread. #[derive(Resource, Default)] @@ -45,6 +46,7 @@ pub struct ExtractedWindow { pub physical_height: u32, pub present_mode: PresentMode, pub swap_chain_texture: Option, + pub swap_chain_texture_format: Option, pub size_changed: bool, pub present_mode_changed: bool, pub alpha_mode: CompositeAlphaMode, @@ -91,6 +93,7 @@ fn extract_windows( physical_height: new_height, present_mode: window.present_mode(), swap_chain_texture: None, + swap_chain_texture_format: None, size_changed: false, present_mode_changed: false, alpha_mode: window.alpha_mode(), @@ -127,9 +130,14 @@ fn extract_windows( } } +struct SurfaceData { + surface: wgpu::Surface, + format: TextureFormat, +} + #[derive(Resource, Default)] pub struct WindowSurfaces { - surfaces: HashMap, + surfaces: HashMap, /// List of windows that we have already called the initial `configure_surface` for configured_windows: HashSet, } @@ -172,25 +180,28 @@ pub fn prepare_windows( .filter(|x| x.raw_handle.is_some()) { let window_surfaces = window_surfaces.deref_mut(); - let surface = window_surfaces + let surface_data = window_surfaces .surfaces .entry(window.id) .or_insert_with(|| unsafe { // NOTE: On some OSes this MUST be called from the main thread. - render_instance.create_surface(&window.raw_handle.as_ref().unwrap().get_handle()) + let surface = render_instance + .create_surface(&window.raw_handle.as_ref().unwrap().get_handle()); + let format = *surface + .get_supported_formats(&render_adapter) + .get(0) + .unwrap_or_else(|| { + panic!( + "No supported formats found for surface {:?} on adapter {:?}", + surface, render_adapter + ) + }); + SurfaceData { surface, format } }); // Creates a closure to avoid calling this logic unnecessarily - let create_swap_chain_descriptor = || wgpu::SurfaceConfiguration { - format: *surface - .get_supported_formats(&render_adapter) - .get(0) - .unwrap_or_else(|| { - panic!( - "No supported formats found for surface {:?} on adapter {:?}", - surface, render_adapter - ) - }), + let surface_configuration = wgpu::SurfaceConfiguration { + format: surface_data.format, width: window.physical_width, height: window.physical_height, usage: wgpu::TextureUsages::RENDER_ATTACHMENT, @@ -216,16 +227,18 @@ pub fn prepare_windows( || window.size_changed || window.present_mode_changed { - render_device.configure_surface(surface, &create_swap_chain_descriptor()); - surface + render_device.configure_surface(&surface_data.surface, &surface_configuration); + surface_data + .surface .get_current_texture() .expect("Error configuring surface") } else { - match surface.get_current_texture() { + match surface_data.surface.get_current_texture() { Ok(swap_chain_frame) => swap_chain_frame, Err(wgpu::SurfaceError::Outdated) => { - render_device.configure_surface(surface, &create_swap_chain_descriptor()); - surface + render_device.configure_surface(&surface_data.surface, &surface_configuration); + surface_data + .surface .get_current_texture() .expect("Error reconfiguring surface") } @@ -234,5 +247,6 @@ pub fn prepare_windows( }; window.swap_chain_texture = Some(TextureView::from(frame)); + window.swap_chain_texture_format = Some(surface_data.format); } } diff --git a/examples/3d/render_to_texture.rs b/examples/3d/render_to_texture.rs index 80a95bc1265de..f9e7f5b46f2e9 100644 --- a/examples/3d/render_to_texture.rs +++ b/examples/3d/render_to_texture.rs @@ -49,7 +49,7 @@ fn setup( label: None, size, dimension: TextureDimension::D2, - format: TextureFormat::Bgra8UnormSrgb, + format: TextureFormat::R16Float, mip_level_count: 1, sample_count: 1, usage: TextureUsages::TEXTURE_BINDING From 354618df6df62c83e349068f9c914d879d32017d Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Wed, 26 Oct 2022 14:10:47 -0700 Subject: [PATCH 2/4] Remove SurfaceTextureFormat and AvailableTextureFormats --- crates/bevy_render/src/lib.rs | 21 +++------------------ crates/bevy_render/src/renderer/mod.rs | 24 +----------------------- 2 files changed, 4 insertions(+), 41 deletions(-) diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 10c1a20dff21b..59b5cc5b462b8 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -39,7 +39,6 @@ pub mod prelude { use globals::GlobalsPlugin; pub use once_cell; use prelude::ComputedVisibility; -use wgpu::TextureFormat; use crate::{ camera::CameraPlugin, @@ -48,8 +47,7 @@ use crate::{ primitives::{CubemapFrusta, Frustum}, render_graph::RenderGraph, render_resource::{PipelineCache, Shader, ShaderLoader}, - renderer::{render_system, RenderInstance, SurfaceTextureFormat}, - texture::BevyDefault, + renderer::{render_system, RenderInstance}, view::{ViewPlugin, WindowRenderPlugin}, }; use bevy_app::{App, AppLabel, Plugin}; @@ -160,17 +158,8 @@ impl Plugin for RenderPlugin { compatible_surface: surface.as_ref(), ..Default::default() }; - let (device, queue, adapter_info, render_adapter, available_texture_formats) = - futures_lite::future::block_on(renderer::initialize_renderer( - &instance, - &options, - &request_adapter_options, - )); - let texture_format = SurfaceTextureFormat( - available_texture_formats - .get(0) - .cloned() - .unwrap_or_else(TextureFormat::bevy_default), + let (device, queue, adapter_info, render_adapter) = futures_lite::future::block_on( + renderer::initialize_renderer(&instance, &options, &request_adapter_options), ); debug!("Configured wgpu adapter Limits: {:#?}", device.limits()); debug!("Configured wgpu adapter Features: {:#?}", device.features()); @@ -178,8 +167,6 @@ impl Plugin for RenderPlugin { .insert_resource(queue.clone()) .insert_resource(adapter_info.clone()) .insert_resource(render_adapter.clone()) - .insert_resource(available_texture_formats.clone()) - .insert_resource(texture_format.clone()) .init_resource::() .register_type::() .register_type::(); @@ -222,8 +209,6 @@ impl Plugin for RenderPlugin { .insert_resource(device) .insert_resource(queue) .insert_resource(render_adapter) - .insert_resource(available_texture_formats) - .insert_resource(texture_format) .insert_resource(adapter_info) .insert_resource(pipeline_cache) .insert_resource(asset_server); diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index 4f51bd3ccbc09..74f4dfb9dfcac 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -102,16 +102,6 @@ pub struct RenderInstance(pub Instance); #[derive(Resource, Clone, Deref, DerefMut)] pub struct RenderAdapterInfo(pub AdapterInfo); -/// The [`TextureFormat`](wgpu::TextureFormat) used for rendering to window surfaces. -/// Initially it's the first element in `AvailableTextureFormats`, or Bevy default format. -#[derive(Resource, Clone, Deref, DerefMut)] -pub struct SurfaceTextureFormat(pub wgpu::TextureFormat); - -/// The available [`TextureFormat`](wgpu::TextureFormat)s on the [`RenderAdapter`]. -/// Will be inserted as a `Resource` after the renderer is initialized. -#[derive(Resource, Clone, Deref, DerefMut)] -pub struct AvailableTextureFormats(pub Arc>); - const GPU_NOT_FOUND_ERROR_MESSAGE: &str = if cfg!(target_os = "linux") { "Unable to find a GPU! Make sure you have installed required drivers! For extra information, see: https://github.com/bevyengine/bevy/blob/latest/docs/linux_dependencies.md" } else { @@ -124,13 +114,7 @@ pub async fn initialize_renderer( instance: &Instance, options: &WgpuSettings, request_adapter_options: &RequestAdapterOptions<'_>, -) -> ( - RenderDevice, - RenderQueue, - RenderAdapterInfo, - RenderAdapter, - AvailableTextureFormats, -) { +) -> (RenderDevice, RenderQueue, RenderAdapterInfo, RenderAdapter) { let adapter = instance .request_adapter(request_adapter_options) .await @@ -281,17 +265,11 @@ pub async fn initialize_renderer( let device = Arc::new(device); let queue = Arc::new(queue); let adapter = Arc::new(adapter); - let mut available_texture_formats = Vec::new(); - if let Some(s) = request_adapter_options.compatible_surface { - available_texture_formats = s.get_supported_formats(&adapter); - }; - let available_texture_formats = Arc::new(available_texture_formats); ( RenderDevice::from(device), RenderQueue(queue), RenderAdapterInfo(adapter_info), RenderAdapter(adapter), - AvailableTextureFormats(available_texture_formats), ) } From 16e89ce7224bdea2f862a27968993feb6c8ed1f9 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Wed, 26 Oct 2022 14:22:12 -0700 Subject: [PATCH 3/4] Revert red texture usage (this was just to illustrate the PR changes) --- examples/3d/render_to_texture.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/3d/render_to_texture.rs b/examples/3d/render_to_texture.rs index f9e7f5b46f2e9..80a95bc1265de 100644 --- a/examples/3d/render_to_texture.rs +++ b/examples/3d/render_to_texture.rs @@ -49,7 +49,7 @@ fn setup( label: None, size, dimension: TextureDimension::D2, - format: TextureFormat::R16Float, + format: TextureFormat::Bgra8UnormSrgb, mip_level_count: 1, sample_count: 1, usage: TextureUsages::TEXTURE_BINDING From cb3c9b64533de4fe40c8808056eba60b631eb229 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Wed, 26 Oct 2022 16:02:15 -0700 Subject: [PATCH 4/4] remove old comment --- crates/bevy_render/src/view/window.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/bevy_render/src/view/window.rs b/crates/bevy_render/src/view/window.rs index fec72916990ff..627c96e8c9de8 100644 --- a/crates/bevy_render/src/view/window.rs +++ b/crates/bevy_render/src/view/window.rs @@ -199,7 +199,6 @@ pub fn prepare_windows( SurfaceData { surface, format } }); - // Creates a closure to avoid calling this logic unnecessarily let surface_configuration = wgpu::SurfaceConfiguration { format: surface_data.format, width: window.physical_width,