From 06166d3f987c0c7ad12ecd074c66183b26c0fcb5 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 | 13 +++-- crates/bevy_render/src/renderer/mod.rs | 30 ++++++++++-- crates/bevy_render/src/view/mod.rs | 9 ++-- crates/bevy_render/src/view/window.rs | 8 ++- crates/bevy_sprite/src/mesh2d/mesh.rs | 8 ++- crates/bevy_sprite/src/render/mod.rs | 4 +- crates/bevy_ui/src/render/pipeline.rs | 67 ++++++++++++++++++++++++-- 8 files changed, 120 insertions(+), 33 deletions(-) diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index 0bb456bcaa5fa..7b5c0aa8ccd5a 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::{AvailableTextureFormats, RenderDevice, RenderQueue}, + texture::{DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo}, view::{ComputedVisibility, ViewUniform, ViewUniformOffset, ViewUniforms}, Extract, RenderApp, RenderStage, }; @@ -263,8 +261,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, available_texture_formats) = + system_state.get_mut(world); let clustered_forward_buffer_binding_type = render_device .get_supported_read_only_binding_type(CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT); @@ -421,7 +421,7 @@ impl FromWorld for MeshPipeline { Extent3d::default(), TextureDimension::D2, &[255u8; 4], - TextureFormat::bevy_default(), + available_texture_formats[0], ); let texture = render_device.create_texture(&image.texture_descriptor); let sampler = match image.sampler_descriptor { @@ -610,7 +610,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 bebf80dde2fcd..bed1314e495b1 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -148,14 +148,19 @@ 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, + )); 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()) .init_resource::() .register_type::() .register_type::(); @@ -197,6 +202,8 @@ impl Plugin for RenderPlugin { .insert_resource(instance) .insert_resource(device) .insert_resource(queue) + .insert_resource(render_adapter) + .insert_resource(available_texture_formats) .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 bc95217dabcaf..6ea6dea072285 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -14,7 +14,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) { @@ -86,17 +86,28 @@ pub fn render_system(world: &mut World) { /// This queue is used to enqueue tasks for the GPU to execute asynchronously. pub type RenderQueue = Arc; +/// This is the adapter that the GPU is using to render. +pub type RenderAdapter = Arc; + /// The GPU instance is used to initialize the [`RenderQueue`] and [`RenderDevice`], /// aswell as to create [`WindowSurfaces`](crate::view::window::WindowSurfaces). pub type RenderInstance = Instance; +pub type AvailableTextureFormats = 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, AdapterInfo) { +) -> ( + RenderDevice, + RenderQueue, + AdapterInfo, + RenderAdapter, + AvailableTextureFormats, +) { let adapter = instance .request_adapter(request_adapter_options) .await @@ -243,9 +254,22 @@ pub async fn initialize_renderer( ) .await .unwrap(); + let device = Arc::new(device); let queue = Arc::new(queue); - (RenderDevice::from(device), queue, adapter_info) + 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), + queue, + adapter_info, + adapter, + available_texture_formats, + ) } /// The context with all information required to interact with the GPU. diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index 71e3597e3127b..f9a805046e4a7 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 4699cef202006..f8649336b6a37 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(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 41188cd48679f..b3829f6db7103 100644 --- a/crates/bevy_sprite/src/mesh2d/mesh.rs +++ b/crates/bevy_sprite/src/mesh2d/mesh.rs @@ -13,9 +13,7 @@ use bevy_render::{ render_phase::{EntityRenderCommand, RenderCommandResult, TrackedRenderPass}, render_resource::*, renderer::{RenderDevice, RenderQueue}, - texture::{ - BevyDefault, DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo, - }, + texture::{DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo}, view::{ComputedVisibility, ExtractedView, ViewUniform, ViewUniformOffset, ViewUniforms}, Extract, RenderApp, RenderStage, }; @@ -197,7 +195,7 @@ impl FromWorld for Mesh2dPipeline { Extent3d::default(), TextureDimension::D2, &[255u8; 4], - TextureFormat::bevy_default(), + TextureFormat::Bgra8Unorm, ); let texture = render_device.create_texture(&image.texture_descriptor); let sampler = match image.sampler_descriptor { @@ -353,7 +351,7 @@ impl SpecializedMeshPipeline for Mesh2dPipeline { shader_defs, entry_point: "fragment".into(), targets: vec![Some(ColorTargetState { - format: TextureFormat::bevy_default(), + format: TextureFormat::Bgra8Unorm, 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 fb07f73069447..3b2223790f2bf 100644 --- a/crates/bevy_sprite/src/render/mod.rs +++ b/crates/bevy_sprite/src/render/mod.rs @@ -21,7 +21,7 @@ use bevy_render::{ }, render_resource::*, renderer::{RenderDevice, RenderQueue}, - texture::{BevyDefault, Image}, + texture::Image, view::{ ComputedVisibility, Msaa, ViewUniform, ViewUniformOffset, ViewUniforms, VisibleEntities, }, @@ -147,7 +147,7 @@ impl SpecializedRenderPipeline for SpritePipeline { shader_defs, entry_point: "fragment".into(), targets: vec![Some(ColorTargetState { - format: TextureFormat::bevy_default(), + format: TextureFormat::Bgra8Unorm, 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 4404429afffe7..f4b3215ff5f13 100644 --- a/crates/bevy_ui/src/render/pipeline.rs +++ b/crates/bevy_ui/src/render/pipeline.rs @@ -1,16 +1,28 @@ -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::{AvailableTextureFormats, RenderDevice, RenderQueue}, + texture::{DefaultImageSampler, GpuImage, Image, ImageSampler, TextureFormatPixelInfo}, + view::ViewUniform, }; 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, available_texture_formats) = + system_state.get_mut(world); let view_layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { entries: &[BindGroupLayoutEntry { @@ -47,10 +59,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], + available_texture_formats[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, } } } @@ -87,7 +146,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, })],