Skip to content

Commit 09ff7ce

Browse files
authored
Partially fix panics when setting WGPU_SETTINGS_PRIO=webgl2 (bevyengine#18113)
# Overview Fixes bevyengine#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 bevyengine#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 ```
1 parent cbc9317 commit 09ff7ce

File tree

3 files changed

+54
-23
lines changed

3 files changed

+54
-23
lines changed

crates/bevy_core_pipeline/src/experimental/mip_generation/mod.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,18 @@ pub struct DownsampleDepthPipelines {
325325
sampler: Sampler,
326326
}
327327

328+
fn supports_compute_shaders(device: &RenderDevice, adapter: &RenderAdapter) -> bool {
329+
adapter
330+
.get_downlevel_capabilities()
331+
.flags
332+
.contains(DownlevelFlags::COMPUTE_SHADERS)
333+
// Even if the adapter supports compute, we might be simulating a lack of
334+
// compute via device limits (see `WgpuSettingsPriority::WebGL2` and
335+
// `wgpu::Limits::downlevel_webgl2_defaults()`). This will have set all the
336+
// `max_compute_*` limits to zero, so we arbitrarily pick one as a canary.
337+
&& (device.limits().max_compute_workgroup_storage_size != 0)
338+
}
339+
328340
/// Creates the [`DownsampleDepthPipelines`] if downsampling is supported on the
329341
/// current platform.
330342
fn create_downsample_depth_pipelines(
@@ -346,11 +358,7 @@ fn create_downsample_depth_pipelines(
346358

347359
// If we don't have compute shaders, we can't invoke the downsample depth
348360
// compute shader.
349-
if !render_adapter
350-
.get_downlevel_capabilities()
351-
.flags
352-
.contains(DownlevelFlags::COMPUTE_SHADERS)
353-
{
361+
if !supports_compute_shaders(&render_device, &render_adapter) {
354362
return;
355363
}
356364

crates/bevy_core_pipeline/src/oit/resolve/mod.rs

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ pub const OIT_RESOLVE_SHADER_HANDLE: Handle<Shader> =
3333
/// Contains the render node used to run the resolve pass.
3434
pub mod node;
3535

36+
/// Minimum required value of `wgpu::Limits::max_storage_buffers_per_shader_stage`.
37+
pub const OIT_REQUIRED_STORAGE_BUFFERS: u32 = 2;
38+
3639
/// Plugin needed to resolve the Order Independent Transparency (OIT) buffer to the screen.
3740
pub struct OitResolvePlugin;
3841
impl Plugin for OitResolvePlugin {
@@ -50,14 +53,11 @@ impl Plugin for OitResolvePlugin {
5053
return;
5154
};
5255

53-
if !render_app
54-
.world()
55-
.resource::<RenderAdapter>()
56-
.get_downlevel_capabilities()
57-
.flags
58-
.contains(DownlevelFlags::FRAGMENT_WRITABLE_STORAGE)
59-
{
60-
warn!("OrderIndependentTransparencyPlugin not loaded. GPU lacks support: DownlevelFlags::FRAGMENT_WRITABLE_STORAGE.");
56+
if !is_oit_supported(
57+
render_app.world().resource::<RenderAdapter>(),
58+
render_app.world().resource::<RenderDevice>(),
59+
true,
60+
) {
6161
return;
6262
}
6363

@@ -73,6 +73,34 @@ impl Plugin for OitResolvePlugin {
7373
}
7474
}
7575

76+
pub fn is_oit_supported(adapter: &RenderAdapter, device: &RenderDevice, warn: bool) -> bool {
77+
if !adapter
78+
.get_downlevel_capabilities()
79+
.flags
80+
.contains(DownlevelFlags::FRAGMENT_WRITABLE_STORAGE)
81+
{
82+
if warn {
83+
warn!("OrderIndependentTransparencyPlugin not loaded. GPU lacks support: DownlevelFlags::FRAGMENT_WRITABLE_STORAGE.");
84+
}
85+
return false;
86+
}
87+
88+
let max_storage_buffers_per_shader_stage = device.limits().max_storage_buffers_per_shader_stage;
89+
90+
if max_storage_buffers_per_shader_stage < OIT_REQUIRED_STORAGE_BUFFERS {
91+
if warn {
92+
warn!(
93+
max_storage_buffers_per_shader_stage,
94+
OIT_REQUIRED_STORAGE_BUFFERS,
95+
"OrderIndependentTransparencyPlugin not loaded. RenderDevice lacks support: max_storage_buffers_per_shader_stage < OIT_REQUIRED_STORAGE_BUFFERS."
96+
);
97+
}
98+
return false;
99+
}
100+
101+
true
102+
}
103+
76104
/// Bind group for the OIT resolve pass.
77105
#[derive(Resource, Deref)]
78106
pub struct OitResolveBindGroup(pub BindGroup);

crates/bevy_pbr/src/render/mesh_view_bindings.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use alloc::sync::Arc;
22
use bevy_core_pipeline::{
33
core_3d::ViewTransmissionTexture,
4-
oit::{OitBuffers, OrderIndependentTransparencySettings},
4+
oit::{resolve::is_oit_supported, OitBuffers, OrderIndependentTransparencySettings},
55
prepass::ViewPrepassTextures,
66
tonemapping::{
77
get_lut_bind_group_layout_entries, get_lut_bindings, Tonemapping, TonemappingLuts,
@@ -380,15 +380,10 @@ fn layout_entries(
380380

381381
// OIT
382382
if layout_key.contains(MeshPipelineViewLayoutKey::OIT_ENABLED) {
383-
// Check if the GPU supports writable storage buffers in the fragment shader
384-
// If not, we can't use OIT, so we skip the OIT bindings.
385-
// This is a hack to avoid errors on webgl -- the OIT plugin will warn the user that OIT
386-
// is not supported on their platform, so we don't need to do it here.
387-
if render_adapter
388-
.get_downlevel_capabilities()
389-
.flags
390-
.contains(DownlevelFlags::FRAGMENT_WRITABLE_STORAGE)
391-
{
383+
// Check if we can use OIT. This is a hack to avoid errors on webgl --
384+
// the OIT plugin will warn the user that OIT is not supported on their
385+
// platform, so we don't need to do it here.
386+
if is_oit_supported(render_adapter, render_device, false) {
392387
entries = entries.extend_with_indices((
393388
// oit_layers
394389
(34, storage_buffer_sized(false, None)),

0 commit comments

Comments
 (0)