Partially fix panics when setting WGPU_SETTINGS_PRIO=webgl2
(#18113)
# Overview Fixes https://github.com/bevyengine/bevy/issues/17869. # Summary `WGPU_SETTINGS_PRIO` changes various limits on `RenderDevice`. This is useful to simulate platforms with lower limits. However, some plugins only check the limits on `RenderAdapter` (the actual GPU) - these limits are not affected by `WGPU_SETTINGS_PRIO`. So the plugins try to use features that are unavailable and wgpu says "oh no". See https://github.com/bevyengine/bevy/issues/17869 for details. The PR adds various checks on `RenderDevice` limits. This is enough to get most examples working, but some are not fixed (see below). # Testing - Tested native, with and without "WGPU_SETTINGS=webgl2". Win10/Vulkan/Nvidia". - Also WebGL. Win10/Chrome/Nvidia. ``` $env:WGPU_SETTINGS_PRIO = "webgl2" cargo run --example testbed_3d cargo run --example testbed_2d cargo run --example testbed_ui cargo run --example deferred_rendering cargo run --example many_lights cargo run --example order_independent_transparency # Still broken, see below. cargo run --example occlusion_culling # Still broken, see below. ``` # Not Fixed While testing I found a few other cases of limits being broken. "Compatibility" settings (WebGPU minimums) breaks native in various examples. ``` $env:WGPU_SETTINGS_PRIO = "compatibility" cargo run --example testbed_3d In Device::create_bind_group_layout, label = 'build mesh uniforms GPU early occlusion culling bind group layout' Too many bindings of type StorageBuffers in Stage ShaderStages(COMPUTE), limit is 8, count was 9. Check the limit `max_storage_buffers_per_shader_stage` passed to `Adapter::request_device` ``` `occlusion_culling` breaks fake webgl. ``` $env:WGPU_SETTINGS_PRIO = "webgl2" cargo run --example occlusion_culling thread '<unnamed>' panicked at C:\Projects\bevy\crates\bevy_render\src\render_resource\pipeline_cache.rs:555:28: index out of bounds: the len is 0 but the index is 2 Encountered a panic in system `bevy_render::renderer::render_system`! ``` `occlusion_culling` breaks real webgl. ``` cargo run --example occlusion_culling --target wasm32-unknown-unknown ERROR app: panicked at C:\Users\T\.cargo\registry\src\index.crates.io-1949cf8c6b5b557f\glow-0.16.0\src\web_sys.rs:4223:9: Tex storage 2D multisample is not supported ``` OIT breaks fake webgl. ``` $env:WGPU_SETTINGS_PRIO = "webgl2" cargo run --example order_independent_transparency In Device::create_bind_group, label = 'mesh_view_bind_group' Number of bindings in bind group descriptor (28) does not match the number of bindings defined in the bind group layout (25) ``` OIT breaks real webgl ``` cargo run --example order_independent_transparency --target wasm32-unknown-unknown In Device::create_render_pipeline, label = 'pbr_oit_mesh_pipeline' Error matching ShaderStages(FRAGMENT) shader requirements against the pipeline Shader global ResourceBinding { group: 0, binding: 34 } is not available in the pipeline layout Binding is missing from the pipeline layout ```
This commit is contained in:
parent
cbc931723e
commit
09ff7ce9f6
@ -325,6 +325,18 @@ pub struct DownsampleDepthPipelines {
|
|||||||
sampler: Sampler,
|
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
|
/// Creates the [`DownsampleDepthPipelines`] if downsampling is supported on the
|
||||||
/// current platform.
|
/// current platform.
|
||||||
fn create_downsample_depth_pipelines(
|
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
|
// If we don't have compute shaders, we can't invoke the downsample depth
|
||||||
// compute shader.
|
// compute shader.
|
||||||
if !render_adapter
|
if !supports_compute_shaders(&render_device, &render_adapter) {
|
||||||
.get_downlevel_capabilities()
|
|
||||||
.flags
|
|
||||||
.contains(DownlevelFlags::COMPUTE_SHADERS)
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,6 +33,9 @@ pub const OIT_RESOLVE_SHADER_HANDLE: Handle<Shader> =
|
|||||||
/// Contains the render node used to run the resolve pass.
|
/// Contains the render node used to run the resolve pass.
|
||||||
pub mod node;
|
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.
|
/// Plugin needed to resolve the Order Independent Transparency (OIT) buffer to the screen.
|
||||||
pub struct OitResolvePlugin;
|
pub struct OitResolvePlugin;
|
||||||
impl Plugin for OitResolvePlugin {
|
impl Plugin for OitResolvePlugin {
|
||||||
@ -50,14 +53,11 @@ impl Plugin for OitResolvePlugin {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
if !render_app
|
if !is_oit_supported(
|
||||||
.world()
|
render_app.world().resource::<RenderAdapter>(),
|
||||||
.resource::<RenderAdapter>()
|
render_app.world().resource::<RenderDevice>(),
|
||||||
.get_downlevel_capabilities()
|
true,
|
||||||
.flags
|
) {
|
||||||
.contains(DownlevelFlags::FRAGMENT_WRITABLE_STORAGE)
|
|
||||||
{
|
|
||||||
warn!("OrderIndependentTransparencyPlugin not loaded. GPU lacks support: DownlevelFlags::FRAGMENT_WRITABLE_STORAGE.");
|
|
||||||
return;
|
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.
|
/// Bind group for the OIT resolve pass.
|
||||||
#[derive(Resource, Deref)]
|
#[derive(Resource, Deref)]
|
||||||
pub struct OitResolveBindGroup(pub BindGroup);
|
pub struct OitResolveBindGroup(pub BindGroup);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use bevy_core_pipeline::{
|
use bevy_core_pipeline::{
|
||||||
core_3d::ViewTransmissionTexture,
|
core_3d::ViewTransmissionTexture,
|
||||||
oit::{OitBuffers, OrderIndependentTransparencySettings},
|
oit::{resolve::is_oit_supported, OitBuffers, OrderIndependentTransparencySettings},
|
||||||
prepass::ViewPrepassTextures,
|
prepass::ViewPrepassTextures,
|
||||||
tonemapping::{
|
tonemapping::{
|
||||||
get_lut_bind_group_layout_entries, get_lut_bindings, Tonemapping, TonemappingLuts,
|
get_lut_bind_group_layout_entries, get_lut_bindings, Tonemapping, TonemappingLuts,
|
||||||
@ -380,15 +380,10 @@ fn layout_entries(
|
|||||||
|
|
||||||
// OIT
|
// OIT
|
||||||
if layout_key.contains(MeshPipelineViewLayoutKey::OIT_ENABLED) {
|
if layout_key.contains(MeshPipelineViewLayoutKey::OIT_ENABLED) {
|
||||||
// Check if the GPU supports writable storage buffers in the fragment shader
|
// Check if we can use OIT. This is a hack to avoid errors on webgl --
|
||||||
// If not, we can't use OIT, so we skip the OIT bindings.
|
// the OIT plugin will warn the user that OIT is not supported on their
|
||||||
// This is a hack to avoid errors on webgl -- the OIT plugin will warn the user that OIT
|
// platform, so we don't need to do it here.
|
||||||
// is not supported on their platform, so we don't need to do it here.
|
if is_oit_supported(render_adapter, render_device, false) {
|
||||||
if render_adapter
|
|
||||||
.get_downlevel_capabilities()
|
|
||||||
.flags
|
|
||||||
.contains(DownlevelFlags::FRAGMENT_WRITABLE_STORAGE)
|
|
||||||
{
|
|
||||||
entries = entries.extend_with_indices((
|
entries = entries.extend_with_indices((
|
||||||
// oit_layers
|
// oit_layers
|
||||||
(34, storage_buffer_sized(false, None)),
|
(34, storage_buffer_sized(false, None)),
|
||||||
|
Loading…
Reference in New Issue
Block a user