diff --git a/crates/bevy_core_pipeline/src/experimental/mip_generation/mod.rs b/crates/bevy_core_pipeline/src/experimental/mip_generation/mod.rs index f324193983..8b8cbacaff 100644 --- a/crates/bevy_core_pipeline/src/experimental/mip_generation/mod.rs +++ b/crates/bevy_core_pipeline/src/experimental/mip_generation/mod.rs @@ -325,6 +325,18 @@ pub struct DownsampleDepthPipelines { sampler: Sampler, } +fn supports_compute_shaders(device: &RenderDevice, adapter: &RenderAdapter) -> bool { + adapter + .get_downlevel_capabilities() + .flags + .contains(DownlevelFlags::COMPUTE_SHADERS) + // Even if the adapter supports compute, we might be simulating a lack of + // compute via device limits (see `WgpuSettingsPriority::WebGL2` and + // `wgpu::Limits::downlevel_webgl2_defaults()`). This will have set all the + // `max_compute_*` limits to zero, so we arbitrarily pick one as a canary. + && (device.limits().max_compute_workgroup_storage_size != 0) +} + /// Creates the [`DownsampleDepthPipelines`] if downsampling is supported on the /// current platform. fn create_downsample_depth_pipelines( @@ -346,11 +358,7 @@ fn create_downsample_depth_pipelines( // If we don't have compute shaders, we can't invoke the downsample depth // compute shader. - if !render_adapter - .get_downlevel_capabilities() - .flags - .contains(DownlevelFlags::COMPUTE_SHADERS) - { + if !supports_compute_shaders(&render_device, &render_adapter) { return; } diff --git a/crates/bevy_core_pipeline/src/oit/resolve/mod.rs b/crates/bevy_core_pipeline/src/oit/resolve/mod.rs index f73192b19d..95ded7a5c0 100644 --- a/crates/bevy_core_pipeline/src/oit/resolve/mod.rs +++ b/crates/bevy_core_pipeline/src/oit/resolve/mod.rs @@ -33,6 +33,9 @@ pub const OIT_RESOLVE_SHADER_HANDLE: Handle = /// Contains the render node used to run the resolve pass. pub mod node; +/// Minimum required value of `wgpu::Limits::max_storage_buffers_per_shader_stage`. +pub const OIT_REQUIRED_STORAGE_BUFFERS: u32 = 2; + /// Plugin needed to resolve the Order Independent Transparency (OIT) buffer to the screen. pub struct OitResolvePlugin; impl Plugin for OitResolvePlugin { @@ -50,14 +53,11 @@ impl Plugin for OitResolvePlugin { return; }; - if !render_app - .world() - .resource::() - .get_downlevel_capabilities() - .flags - .contains(DownlevelFlags::FRAGMENT_WRITABLE_STORAGE) - { - warn!("OrderIndependentTransparencyPlugin not loaded. GPU lacks support: DownlevelFlags::FRAGMENT_WRITABLE_STORAGE."); + if !is_oit_supported( + render_app.world().resource::(), + render_app.world().resource::(), + true, + ) { return; } @@ -73,6 +73,34 @@ impl Plugin for OitResolvePlugin { } } +pub fn is_oit_supported(adapter: &RenderAdapter, device: &RenderDevice, warn: bool) -> bool { + if !adapter + .get_downlevel_capabilities() + .flags + .contains(DownlevelFlags::FRAGMENT_WRITABLE_STORAGE) + { + if warn { + warn!("OrderIndependentTransparencyPlugin not loaded. GPU lacks support: DownlevelFlags::FRAGMENT_WRITABLE_STORAGE."); + } + return false; + } + + let max_storage_buffers_per_shader_stage = device.limits().max_storage_buffers_per_shader_stage; + + if max_storage_buffers_per_shader_stage < OIT_REQUIRED_STORAGE_BUFFERS { + if warn { + warn!( + max_storage_buffers_per_shader_stage, + OIT_REQUIRED_STORAGE_BUFFERS, + "OrderIndependentTransparencyPlugin not loaded. RenderDevice lacks support: max_storage_buffers_per_shader_stage < OIT_REQUIRED_STORAGE_BUFFERS." + ); + } + return false; + } + + true +} + /// Bind group for the OIT resolve pass. #[derive(Resource, Deref)] pub struct OitResolveBindGroup(pub BindGroup); diff --git a/crates/bevy_pbr/src/render/mesh_view_bindings.rs b/crates/bevy_pbr/src/render/mesh_view_bindings.rs index 796ae096c2..8e231886ba 100644 --- a/crates/bevy_pbr/src/render/mesh_view_bindings.rs +++ b/crates/bevy_pbr/src/render/mesh_view_bindings.rs @@ -1,7 +1,7 @@ use alloc::sync::Arc; use bevy_core_pipeline::{ core_3d::ViewTransmissionTexture, - oit::{OitBuffers, OrderIndependentTransparencySettings}, + oit::{resolve::is_oit_supported, OitBuffers, OrderIndependentTransparencySettings}, prepass::ViewPrepassTextures, tonemapping::{ get_lut_bind_group_layout_entries, get_lut_bindings, Tonemapping, TonemappingLuts, @@ -380,15 +380,10 @@ fn layout_entries( // OIT if layout_key.contains(MeshPipelineViewLayoutKey::OIT_ENABLED) { - // Check if the GPU supports writable storage buffers in the fragment shader - // If not, we can't use OIT, so we skip the OIT bindings. - // This is a hack to avoid errors on webgl -- the OIT plugin will warn the user that OIT - // is not supported on their platform, so we don't need to do it here. - if render_adapter - .get_downlevel_capabilities() - .flags - .contains(DownlevelFlags::FRAGMENT_WRITABLE_STORAGE) - { + // Check if we can use OIT. This is a hack to avoid errors on webgl -- + // the OIT plugin will warn the user that OIT is not supported on their + // platform, so we don't need to do it here. + if is_oit_supported(render_adapter, render_device, false) { entries = entries.extend_with_indices(( // oit_layers (34, storage_buffer_sized(false, None)),