From 7d87bb8c4d2985cbba86672440c2f6db0dc63684 Mon Sep 17 00:00:00 2001 From: VitalyR Date: Mon, 10 Oct 2022 16:10:05 +0000 Subject: [PATCH] get proper texture format after the renderer is initialized, fix #3897 (#5413) # Objective There is no Srgb support on some GPU and display protocols with `winit` (for example, Nvidia's GPUs with Wayland). Thus `TextureFormat::bevy_default()` which returns `Rgba8UnormSrgb` or `Bgra8UnormSrgb` will cause panics on such platforms. This patch will resolve this problem. Fix https://github.com/bevyengine/bevy/issues/3897. ## Solution Make `initialize_renderer` expose `wgpu::Adapter` and `first_available_texture_format`, use the `first_available_texture_format` by default. ## Changelog * Fixed https://github.com/bevyengine/bevy/issues/3897. --- crates/bevy_pbr/src/render/mesh.rs | 14 +++--- crates/bevy_render/src/lib.rs | 20 ++++++-- crates/bevy_render/src/renderer/mod.rs | 38 ++++++++++++++- crates/bevy_render/src/view/mod.rs | 9 ++-- crates/bevy_render/src/view/window.rs | 15 ++++-- crates/bevy_sprite/src/mesh2d/mesh.rs | 20 ++++---- crates/bevy_sprite/src/render/mod.rs | 65 +++++++++++++++++++++++-- crates/bevy_ui/src/render/pipeline.rs | 67 ++++++++++++++++++++++++-- 8 files changed, 209 insertions(+), 39 deletions(-) diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 3323429c26313f..34b43a6ed1fa3a 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -21,10 +21,8 @@ use bevy_render::{ render_asset::RenderAssets, render_phase::{EntityRenderCommand, RenderCommandResult, TrackedRenderPass}, render_resource::*, - renderer::{RenderDevice, RenderQueue}, - texture::{ - BevyDefault, DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo, - }, + renderer::{RenderDevice, RenderQueue, RenderTextureFormat}, + texture::{DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo}, view::{ComputedVisibility, ViewUniform, ViewUniformOffset, ViewUniforms}, Extract, RenderApp, RenderStage, }; @@ -270,8 +268,10 @@ impl FromWorld for MeshPipeline { Res, Res, Res, + Res, )> = SystemState::new(world); - let (render_device, default_sampler, render_queue) = system_state.get_mut(world); + let (render_device, default_sampler, render_queue, first_available_texture_format) = + system_state.get_mut(world); let clustered_forward_buffer_binding_type = render_device .get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT); @@ -438,7 +438,7 @@ impl FromWorld for MeshPipeline { Extent3d::default(), TextureDimension::D2, &[255u8; 4], - TextureFormat::bevy_default(), + first_available_texture_format.0, ); let texture = render_device.create_texture(&image.texture_descriptor); let sampler = match image.sampler_descriptor { @@ -629,7 +629,7 @@ impl SpecializedMeshPipeline for MeshPipeline { shader_defs, entry_point: "fragment".into(), targets: vec![Some(ColorTargetState { - format: TextureFormat::bevy_default(), + format: self.dummy_white_gpu_image.texture_format, blend, write_mask: ColorWrites::ALL, })], diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index adf5a97c19a8d7..4ba0e53b20154f 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -47,7 +47,7 @@ use crate::{ primitives::{CubemapFrusta, Frustum}, render_graph::RenderGraph, render_resource::{PipelineCache, Shader, ShaderLoader}, - renderer::{render_system, RenderInstance}, + renderer::{render_system, RenderInstance, RenderTextureFormat}, texture::ImagePlugin, view::{ViewPlugin, WindowRenderPlugin}, }; @@ -157,14 +157,23 @@ impl Plugin for RenderPlugin { compatible_surface: surface.as_ref(), ..Default::default() }; - let (device, queue, adapter_info) = futures_lite::future::block_on( - renderer::initialize_renderer(&instance, &options, &request_adapter_options), - ); + let (device, queue, adapter_info, render_adapter, available_texture_formats) = + futures_lite::future::block_on(renderer::initialize_renderer( + &instance, + &options, + &request_adapter_options, + )); + // `available_texture_formats` won't be empty, or else will panick in the former + // `initialize_renderer` call. + let first_available_texture_format = RenderTextureFormat(available_texture_formats[0]); debug!("Configured wgpu adapter Limits: {:#?}", device.limits()); debug!("Configured wgpu adapter Features: {:#?}", device.features()); app.insert_resource(device.clone()) .insert_resource(queue.clone()) .insert_resource(adapter_info.clone()) + .insert_resource(render_adapter.clone()) + .insert_resource(available_texture_formats.clone()) + .insert_resource(first_available_texture_format.clone()) .init_resource::() .register_type::() .register_type::(); @@ -206,6 +215,9 @@ impl Plugin for RenderPlugin { .insert_resource(RenderInstance(instance)) .insert_resource(device) .insert_resource(queue) + .insert_resource(render_adapter) + .insert_resource(available_texture_formats) + .insert_resource(first_available_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 5b175a14644065..41026210a858a9 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -15,7 +15,7 @@ use bevy_ecs::prelude::*; use bevy_time::TimeSender; use bevy_utils::Instant; use std::sync::Arc; -use wgpu::{AdapterInfo, CommandEncoder, Instance, Queue, RequestAdapterOptions}; +use wgpu::{Adapter, AdapterInfo, CommandEncoder, Instance, Queue, RequestAdapterOptions}; /// Updates the [`RenderGraph`] with all of its nodes and then runs it to render the entire frame. pub fn render_system(world: &mut World) { @@ -88,6 +88,11 @@ pub fn render_system(world: &mut World) { #[derive(Resource, Clone, Deref, DerefMut)] pub struct RenderQueue(pub Arc); +/// The handle to the physical device being used for rendering. +/// See [`wgpu::Adapter`] for more info. +#[derive(Resource, Clone, Debug, Deref, DerefMut)] +pub struct RenderAdapter(pub Arc); + /// The GPU instance is used to initialize the [`RenderQueue`] and [`RenderDevice`], /// as well as to create [`WindowSurfaces`](crate::view::window::WindowSurfaces). #[derive(Resource, Deref, DerefMut)] @@ -97,13 +102,29 @@ pub struct RenderInstance(pub Instance); #[derive(Resource, Clone, Deref, DerefMut)] pub struct RenderAdapterInfo(pub AdapterInfo); +/// The [`TextureFormat`](wgpu::TextureFormat) used for rendering. +/// Initially it's the first element in `AvailableTextureFormats`. +#[derive(Resource, Clone, Deref, DerefMut)] +pub struct RenderTextureFormat(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>); + /// Initializes the renderer by retrieving and preparing the GPU instance, device and queue /// for the specified backend. pub async fn initialize_renderer( instance: &Instance, options: &WgpuSettings, request_adapter_options: &RequestAdapterOptions<'_>, -) -> (RenderDevice, RenderQueue, RenderAdapterInfo) { +) -> ( + RenderDevice, + RenderQueue, + RenderAdapterInfo, + RenderAdapter, + AvailableTextureFormats, +) { let adapter = instance .request_adapter(request_adapter_options) .await @@ -250,12 +271,25 @@ pub async fn initialize_renderer( ) .await .unwrap(); + 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); + if available_texture_formats.is_empty() { + info!("{:?}", adapter_info); + panic!("No supported texture formats found!"); + } + }; + let available_texture_formats = Arc::new(available_texture_formats); ( RenderDevice::from(device), RenderQueue(queue), RenderAdapterInfo(adapter_info), + RenderAdapter(adapter), + AvailableTextureFormats(available_texture_formats), ) } diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index 92657ad8f8461d..3df524c58ac162 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -4,7 +4,7 @@ pub mod window; pub use visibility::*; use wgpu::{ Color, Extent3d, Operations, RenderPassColorAttachment, TextureDescriptor, TextureDimension, - TextureFormat, TextureUsages, + TextureUsages, }; pub use window::*; @@ -15,8 +15,8 @@ use crate::{ rangefinder::ViewRangefinder3d, render_asset::RenderAssets, render_resource::{DynamicUniformBuffer, ShaderType, Texture, TextureView}, - renderer::{RenderDevice, RenderQueue}, - texture::{BevyDefault, TextureCache}, + renderer::{RenderDevice, RenderQueue, RenderTextureFormat}, + texture::TextureCache, RenderApp, RenderStage, }; use bevy_app::{App, Plugin}; @@ -182,6 +182,7 @@ fn prepare_view_targets( images: Res>, msaa: Res, render_device: Res, + texture_format: Res, mut texture_cache: ResMut, cameras: Query<(Entity, &ExtractedCamera)>, ) { @@ -205,7 +206,7 @@ fn prepare_view_targets( mip_level_count: 1, sample_count: msaa.samples, dimension: TextureDimension::D2, - format: TextureFormat::bevy_default(), + format: **texture_format, usage: TextureUsages::RENDER_ATTACHMENT, }, ) diff --git a/crates/bevy_render/src/view/window.rs b/crates/bevy_render/src/view/window.rs index 6fe1bc8627fdaf..fbe9ba4f667759 100644 --- a/crates/bevy_render/src/view/window.rs +++ b/crates/bevy_render/src/view/window.rs @@ -1,7 +1,6 @@ use crate::{ render_resource::TextureView, - renderer::{RenderDevice, RenderInstance}, - texture::BevyDefault, + renderer::{RenderAdapter, RenderDevice, RenderInstance}, Extract, RenderApp, RenderStage, }; use bevy_app::{App, Plugin}; @@ -9,7 +8,6 @@ use bevy_ecs::prelude::*; use bevy_utils::{tracing::debug, HashMap, HashSet}; use bevy_window::{PresentMode, RawWindowHandleWrapper, WindowClosed, WindowId, Windows}; use std::ops::{Deref, DerefMut}; -use wgpu::TextureFormat; /// Token to ensure a system runs on the main thread. #[derive(Resource, Default)] @@ -161,6 +159,7 @@ pub fn prepare_windows( mut window_surfaces: ResMut, render_device: Res, render_instance: Res, + render_adapter: Res, ) { let window_surfaces = window_surfaces.deref_mut(); for window in windows.windows.values_mut() { @@ -173,7 +172,15 @@ pub fn prepare_windows( }); let swap_chain_descriptor = wgpu::SurfaceConfiguration { - format: TextureFormat::bevy_default(), + format: *surface + .get_supported_formats(&render_adapter) + .get(0) + .unwrap_or_else(|| { + panic!( + "No supported formats found for surface {:?} on adapter {:?}", + surface, render_adapter + ) + }), width: window.physical_width, height: window.physical_height, usage: wgpu::TextureUsages::RENDER_ATTACHMENT, diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index b2248be595d776..f27b4a5c5efe16 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -12,10 +12,8 @@ use bevy_render::{ render_asset::RenderAssets, render_phase::{EntityRenderCommand, RenderCommandResult, TrackedRenderPass}, render_resource::*, - renderer::{RenderDevice, RenderQueue}, - texture::{ - BevyDefault, DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo, - }, + renderer::{RenderDevice, RenderQueue, RenderTextureFormat}, + texture::{DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo}, view::{ComputedVisibility, ExtractedView, ViewUniform, ViewUniformOffset, ViewUniforms}, Extract, RenderApp, RenderStage, }; @@ -158,9 +156,13 @@ pub struct Mesh2dPipeline { impl FromWorld for Mesh2dPipeline { fn from_world(world: &mut World) -> Self { - let mut system_state: SystemState<(Res, Res)> = - SystemState::new(world); - let (render_device, default_sampler) = system_state.get_mut(world); + let mut system_state: SystemState<( + Res, + Res, + Res, + )> = SystemState::new(world); + let (render_device, default_sampler, first_available_texture_format) = + system_state.get_mut(world); let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { entries: &[ // View @@ -197,7 +199,7 @@ impl FromWorld for Mesh2dPipeline { Extent3d::default(), TextureDimension::D2, &[255u8; 4], - TextureFormat::bevy_default(), + first_available_texture_format.0, ); let texture = render_device.create_texture(&image.texture_descriptor); let sampler = match image.sampler_descriptor { @@ -354,7 +356,7 @@ impl SpecializedMeshPipeline for Mesh2dPipeline { shader_defs, entry_point: "fragment".into(), targets: vec![Some(ColorTargetState { - format: TextureFormat::bevy_default(), + format: self.dummy_white_gpu_image.texture_format, blend: Some(BlendState::ALPHA_BLENDING), write_mask: ColorWrites::ALL, })], diff --git a/crates/bevy_sprite/src/render/mod.rs b/crates/bevy_sprite/src/render/mod.rs index 49e5e0d7f38efb..1e4ab5f57ca1a9 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -8,7 +8,7 @@ use bevy_asset::{AssetEvent, Assets, Handle, HandleId}; use bevy_core_pipeline::core_2d::Transparent2d; use bevy_ecs::{ prelude::*, - system::{lifetimeless::*, SystemParamItem}, + system::{lifetimeless::*, SystemParamItem, SystemState}, }; use bevy_math::{Rect, Vec2}; use bevy_reflect::Uuid; @@ -20,8 +20,8 @@ use bevy_render::{ RenderPhase, SetItemPipeline, TrackedRenderPass, }, render_resource::*, - renderer::{RenderDevice, RenderQueue}, - texture::{BevyDefault, Image}, + renderer::{RenderDevice, RenderQueue, RenderTextureFormat}, + texture::{DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo}, view::{ ComputedVisibility, Msaa, ViewUniform, ViewUniformOffset, ViewUniforms, VisibleEntities, }, @@ -37,11 +37,19 @@ use fixedbitset::FixedBitSet; pub struct SpritePipeline { view_layout: BindGroupLayout, material_layout: BindGroupLayout, + pub dummy_white_gpu_image: GpuImage, } impl FromWorld for SpritePipeline { fn from_world(world: &mut World) -> Self { - let render_device = world.resource::(); + let mut system_state: SystemState<( + Res, + Res, + Res, + Res, + )> = SystemState::new(world); + let (render_device, default_sampler, render_queue, first_available_texture_format) = + system_state.get_mut(world); let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { entries: &[BindGroupLayoutEntry { @@ -78,10 +86,57 @@ impl FromWorld for SpritePipeline { ], label: Some("sprite_material_layout"), }); + let dummy_white_gpu_image = { + let image = Image::new_fill( + Extent3d::default(), + TextureDimension::D2, + &[255u8; 4], + first_available_texture_format.0, + ); + let texture = render_device.create_texture(&image.texture_descriptor); + let sampler = match image.sampler_descriptor { + ImageSampler::Default => (**default_sampler).clone(), + ImageSampler::Descriptor(descriptor) => render_device.create_sampler(&descriptor), + }; + + let format_size = image.texture_descriptor.format.pixel_size(); + render_queue.write_texture( + ImageCopyTexture { + texture: &texture, + mip_level: 0, + origin: Origin3d::ZERO, + aspect: TextureAspect::All, + }, + &image.data, + ImageDataLayout { + offset: 0, + bytes_per_row: Some( + std::num::NonZeroU32::new( + image.texture_descriptor.size.width * format_size as u32, + ) + .unwrap(), + ), + rows_per_image: None, + }, + image.texture_descriptor.size, + ); + let texture_view = texture.create_view(&TextureViewDescriptor::default()); + GpuImage { + texture, + texture_view, + texture_format: image.texture_descriptor.format, + sampler, + size: Vec2::new( + image.texture_descriptor.size.width as f32, + image.texture_descriptor.size.height as f32, + ), + } + }; SpritePipeline { view_layout, material_layout, + dummy_white_gpu_image, } } } @@ -148,7 +203,7 @@ impl SpecializedRenderPipeline for SpritePipeline { shader_defs, entry_point: "fragment".into(), targets: vec![Some(ColorTargetState { - format: TextureFormat::bevy_default(), + format: self.dummy_white_gpu_image.texture_format, blend: Some(BlendState::ALPHA_BLENDING), write_mask: ColorWrites::ALL, })], diff --git a/crates/bevy_ui/src/render/pipeline.rs b/crates/bevy_ui/src/render/pipeline.rs index e199cef521e0bd..5d384518f3d963 100644 --- a/crates/bevy_ui/src/render/pipeline.rs +++ b/crates/bevy_ui/src/render/pipeline.rs @@ -1,17 +1,29 @@ -use bevy_ecs::prelude::*; +use bevy_ecs::{prelude::*, system::SystemState}; +use bevy_math::Vec2; use bevy_render::{ - render_resource::*, renderer::RenderDevice, texture::BevyDefault, view::ViewUniform, + render_resource::*, + renderer::{RenderDevice, RenderQueue, RenderTextureFormat}, + texture::{DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo}, + view::ViewUniform, }; #[derive(Resource)] pub struct UiPipeline { pub view_layout: BindGroupLayout, pub image_layout: BindGroupLayout, + pub dummy_white_gpu_image: GpuImage, } impl FromWorld for UiPipeline { fn from_world(world: &mut World) -> Self { - let render_device = world.resource::(); + let mut system_state: SystemState<( + Res, + Res, + Res, + Res, + )> = SystemState::new(world); + let (render_device, default_sampler, render_queue, first_available_texture_format) = + system_state.get_mut(world); let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { entries: &[BindGroupLayoutEntry { @@ -48,10 +60,57 @@ impl FromWorld for UiPipeline { ], label: Some("ui_image_layout"), }); + let dummy_white_gpu_image = { + let image = Image::new_fill( + Extent3d::default(), + TextureDimension::D2, + &[255u8; 4], + first_available_texture_format.0, + ); + let texture = render_device.create_texture(&image.texture_descriptor); + let sampler = match image.sampler_descriptor { + ImageSampler::Default => (**default_sampler).clone(), + ImageSampler::Descriptor(descriptor) => render_device.create_sampler(&descriptor), + }; + + let format_size = image.texture_descriptor.format.pixel_size(); + render_queue.write_texture( + ImageCopyTexture { + texture: &texture, + mip_level: 0, + origin: Origin3d::ZERO, + aspect: TextureAspect::All, + }, + &image.data, + ImageDataLayout { + offset: 0, + bytes_per_row: Some( + std::num::NonZeroU32::new( + image.texture_descriptor.size.width * format_size as u32, + ) + .unwrap(), + ), + rows_per_image: None, + }, + image.texture_descriptor.size, + ); + let texture_view = texture.create_view(&TextureViewDescriptor::default()); + GpuImage { + texture, + texture_view, + texture_format: image.texture_descriptor.format, + sampler, + size: Vec2::new( + image.texture_descriptor.size.width as f32, + image.texture_descriptor.size.height as f32, + ), + } + }; UiPipeline { view_layout, image_layout, + dummy_white_gpu_image, } } } @@ -88,7 +147,7 @@ impl SpecializedRenderPipeline for UiPipeline { shader_defs, entry_point: "fragment".into(), targets: vec![Some(ColorTargetState { - format: TextureFormat::bevy_default(), + format: self.dummy_white_gpu_image.texture_format, blend: Some(BlendState::ALPHA_BLENDING), write_mask: ColorWrites::ALL, })],