Skip to content

Commit

Permalink
get proper texture format after the renderer is initialized, fix #3897
Browse files Browse the repository at this point in the history
# 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
  • Loading branch information
VitalyAnkh committed Jul 21, 2022
1 parent 1c23421 commit 06166d3
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 33 deletions.
14 changes: 7 additions & 7 deletions crates/bevy_pbr/src/render/mesh.rs
Expand Up @@ -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,
};
Expand Down Expand Up @@ -263,8 +261,10 @@ impl FromWorld for MeshPipeline {
Res<RenderDevice>,
Res<DefaultImageSampler>,
Res<RenderQueue>,
Res<AvailableTextureFormats>,
)> = 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);

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
})],
Expand Down
13 changes: 10 additions & 3 deletions crates/bevy_render/src/lib.rs
Expand Up @@ -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::<ScratchMainWorld>()
.register_type::<Frustum>()
.register_type::<CubemapFrusta>();
Expand Down Expand Up @@ -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)
Expand Down
30 changes: 27 additions & 3 deletions crates/bevy_render/src/renderer/mod.rs
Expand Up @@ -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) {
Expand Down Expand Up @@ -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<Queue>;

/// This is the adapter that the GPU is using to render.
pub type RenderAdapter = Arc<Adapter>;

/// 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<Vec<wgpu::TextureFormat>>;

/// 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
Expand Down Expand Up @@ -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.
Expand Down
9 changes: 5 additions & 4 deletions crates/bevy_render/src/view/mod.rs
Expand Up @@ -4,7 +4,7 @@ pub mod window;
pub use visibility::*;
use wgpu::{
Color, Extent3d, Operations, RenderPassColorAttachment, TextureDescriptor, TextureDimension,
TextureFormat, TextureUsages,
TextureUsages,
};
pub use window::*;

Expand All @@ -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};
Expand Down Expand Up @@ -183,6 +183,7 @@ fn prepare_view_targets(
images: Res<RenderAssets<Image>>,
msaa: Res<Msaa>,
render_device: Res<RenderDevice>,
available_texture_formats: Res<AvailableTextureFormats>,
mut texture_cache: ResMut<TextureCache>,
cameras: Query<(Entity, &ExtractedCamera)>,
) {
Expand All @@ -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,
},
)
Expand Down
8 changes: 3 additions & 5 deletions crates/bevy_render/src/view/window.rs
@@ -1,15 +1,13 @@
use crate::{
render_resource::TextureView,
renderer::{RenderDevice, RenderInstance},
texture::BevyDefault,
renderer::{RenderAdapter, RenderDevice, RenderInstance},
Extract, RenderApp, RenderStage,
};
use bevy_app::{App, Plugin};
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)]
Expand Down Expand Up @@ -149,6 +147,7 @@ pub fn prepare_windows(
mut window_surfaces: ResMut<WindowSurfaces>,
render_device: Res<RenderDevice>,
render_instance: Res<RenderInstance>,
render_adapter: Res<RenderAdapter>,
) {
let window_surfaces = window_surfaces.deref_mut();
for window in windows.windows.values_mut() {
Expand All @@ -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,
Expand All @@ -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);
Expand Down
8 changes: 3 additions & 5 deletions crates/bevy_sprite/src/mesh2d/mesh.rs
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
})],
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_sprite/src/render/mod.rs
Expand Up @@ -21,7 +21,7 @@ use bevy_render::{
},
render_resource::*,
renderer::{RenderDevice, RenderQueue},
texture::{BevyDefault, Image},
texture::Image,
view::{
ComputedVisibility, Msaa, ViewUniform, ViewUniformOffset, ViewUniforms, VisibleEntities,
},
Expand Down Expand Up @@ -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,
})],
Expand Down
67 changes: 63 additions & 4 deletions 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::<RenderDevice>();
let mut system_state: SystemState<(
Res<RenderDevice>,
Res<DefaultImageSampler>,
Res<RenderQueue>,
Res<AvailableTextureFormats>,
)> = 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 {
Expand Down Expand Up @@ -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,
}
}
}
Expand Down Expand Up @@ -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,
})],
Expand Down

0 comments on commit 06166d3

Please sign in to comment.