From 19af01d4beedecd7cbf18f91feec54cbf9798d4f Mon Sep 17 00:00:00 2001 From: vitalyr Date: Thu, 21 Jul 2022 15:26:07 +0800 Subject: [PATCH] get proper texture format after the renderer is initialized, fix #3897 # Objective There is no Srgb support on some platforms with some GPUs and display protocols (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 #3897 ## Solution Make `initialize_renderer` expose `wgpu::Adapter` and use `wgpu::Adapter` to get proper `TextureFormat`. ## Changelog * Fixed #3897 --- crates/bevy_pbr/src/render/mesh.rs | 14 +++--- crates/bevy_render/src/lib.rs | 21 ++++++-- crates/bevy_render/src/renderer/mod.rs | 34 ++++++++++++- crates/bevy_render/src/view/mod.rs | 9 ++-- crates/bevy_render/src/view/window.rs | 8 ++- 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, 198 insertions(+), 40 deletions(-) diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 3d70cfd110eb5..b6aadcfeb8ea7 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -20,10 +20,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, }; @@ -269,8 +267,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, firstly_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); @@ -427,7 +427,7 @@ impl FromWorld for MeshPipeline { Extent3d::default(), TextureDimension::D2, &[255u8; 4], - TextureFormat::bevy_default(), + firstly_available_texture_format.0, ); let texture = render_device.create_texture(&image.texture_descriptor); let sampler = match image.sampler_descriptor { @@ -616,7 +616,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 fbe5f14923970..d3d321627c5f1 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -42,7 +42,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}, }; @@ -152,14 +152,24 @@ 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 firstly_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(firstly_available_texture_format.clone()) .init_resource::() .register_type::() .register_type::(); @@ -201,6 +211,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(firstly_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 5b175a1464406..dde6afb548db4 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,10 @@ pub fn render_system(world: &mut World) { #[derive(Resource, Clone, Deref, DerefMut)] pub struct RenderQueue(pub Arc); +/// This is the adapter that the GPU is using to render. +#[derive(Resource, Clone, 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 +101,26 @@ pub struct RenderInstance(pub Instance); #[derive(Resource, Clone, Deref, DerefMut)] pub struct RenderAdapterInfo(pub AdapterInfo); +/// The `AdapterInfo` of the adapter in use by the renderer. +#[derive(Resource, Clone, Deref, DerefMut)] +pub struct RenderTextureFormat(pub wgpu::TextureFormat); + +#[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 +267,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); + }; + let available_texture_formats = Arc::new(available_texture_formats); + if available_texture_formats.is_empty() { + info!("{:?}", adapter_info); + panic!("No supported texture formats found!"); + } ( 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 55ca6a31b6c23..dd18eb6441af9 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::{AvailableTextureFormats, RenderDevice, RenderQueue}, + texture::TextureCache, RenderApp, RenderStage, }; use bevy_app::{App, Plugin}; @@ -183,6 +183,7 @@ fn prepare_view_targets( images: Res>, msaa: Res, render_device: Res, + available_texture_formats: Res, mut texture_cache: ResMut, cameras: Query<(Entity, &ExtractedCamera)>, ) { @@ -206,7 +207,7 @@ fn prepare_view_targets( mip_level_count: 1, sample_count: msaa.samples, dimension: TextureDimension::D2, - format: TextureFormat::bevy_default(), + format: available_texture_formats[0], usage: TextureUsages::RENDER_ATTACHMENT, }, ) diff --git a/crates/bevy_render/src/view/window.rs b/crates/bevy_render/src/view/window.rs index bba940680f6e3..2feeefeb977c2 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)] @@ -149,6 +147,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() { @@ -161,7 +160,7 @@ pub fn prepare_windows( }); let swap_chain_descriptor = wgpu::SurfaceConfiguration { - format: TextureFormat::bevy_default(), + format: surface.get_supported_formats(&render_adapter)[0], width: window.physical_width, height: window.physical_height, usage: wgpu::TextureUsages::RENDER_ATTACHMENT, @@ -173,7 +172,6 @@ pub fn prepare_windows( PresentMode::AutoNoVsync => wgpu::PresentMode::AutoNoVsync, }, }; - // Do the initial surface configuration if it hasn't been configured yet if window_surfaces.configured_windows.insert(window.id) || window.size_changed { render_device.configure_surface(surface, &swap_chain_descriptor); diff --git a/crates/bevy_sprite/src/mesh2d/mesh.rs b/crates/bevy_sprite/src/mesh2d/mesh.rs index 18513f0d5362d..0f693246cd397 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, firstly_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(), + firstly_available_texture_format.0, ); let texture = render_device.create_texture(&image.texture_descriptor); let sampler = match image.sampler_descriptor { @@ -353,7 +355,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 c2f98b2aab286..b3102a7e65735 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::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, }, @@ -38,11 +38,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, firstly_available_texture_format) = + system_state.get_mut(world); let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { entries: &[BindGroupLayoutEntry { @@ -79,10 +87,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], + firstly_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 e199cef521e0b..ed3958bef74b0 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, firstly_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], + firstly_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, })],