Fix several regressions from recent rendering changes. (#16890)

This commit fixes the following regressions:

1. Transmission-specific calls to shader lighting functions didn't pass
the `enable_diffuse` parameter, breaking the `transmission` example.

2. The combination of bindless `StandardMaterial` and bindless lightmaps
caused us to blow past the 128 texture limit on M1/M2 chips in some
cases, in particular the `depth_of_field` example.
https://github.com/gfx-rs/wgpu/issues/3334 should fix this, but in the
meantime this patch reduces the number of bindless lightmaps from 16 to
4 in order to stay under the limit.

3. The renderer was crashing on startup on Adreno 610 chips. This PR
simply disables bindless on Adreno 610 and lower.
This commit is contained in:
Patrick Walton 2024-12-22 18:03:06 -05:00 committed by GitHub
parent 20277006ce
commit 6a4e0c801e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 80 additions and 51 deletions

View File

@ -63,7 +63,7 @@ use bevy_render::{
BindGroupLayoutEntryBuilder, Sampler, SamplerBindingType, Shader, ShaderStages,
TextureSampleType, TextureView,
},
renderer::RenderDevice,
renderer::{RenderAdapter, RenderDevice},
texture::{FallbackImage, GpuImage},
};
@ -232,10 +232,11 @@ impl ExtractInstance for EnvironmentMapIds {
/// specular binding arrays respectively, in addition to the sampler.
pub(crate) fn get_bind_group_layout_entries(
render_device: &RenderDevice,
render_adapter: &RenderAdapter,
) -> [BindGroupLayoutEntryBuilder; 4] {
let mut texture_cube_binding =
binding_types::texture_cube(TextureSampleType::Float { filterable: true });
if binding_arrays_are_usable(render_device) {
if binding_arrays_are_usable(render_device, render_adapter) {
texture_cube_binding =
texture_cube_binding.count(NonZero::<u32>::new(MAX_VIEW_LIGHT_PROBES as _).unwrap());
}
@ -256,8 +257,9 @@ impl<'a> RenderViewEnvironmentMapBindGroupEntries<'a> {
images: &'a RenderAssets<GpuImage>,
fallback_image: &'a FallbackImage,
render_device: &RenderDevice,
render_adapter: &RenderAdapter,
) -> RenderViewEnvironmentMapBindGroupEntries<'a> {
if binding_arrays_are_usable(render_device) {
if binding_arrays_are_usable(render_device, render_adapter) {
let mut diffuse_texture_views = vec![];
let mut specular_texture_views = vec![];
let mut sampler = None;

View File

@ -140,7 +140,7 @@ use bevy_render::{
binding_types, BindGroupLayoutEntryBuilder, Sampler, SamplerBindingType, Shader,
TextureSampleType, TextureView,
},
renderer::RenderDevice,
renderer::{RenderAdapter, RenderDevice},
texture::{FallbackImage, GpuImage},
};
use bevy_utils::default;
@ -242,8 +242,9 @@ impl<'a> RenderViewIrradianceVolumeBindGroupEntries<'a> {
images: &'a RenderAssets<GpuImage>,
fallback_image: &'a FallbackImage,
render_device: &RenderDevice,
render_adapter: &RenderAdapter,
) -> RenderViewIrradianceVolumeBindGroupEntries<'a> {
if binding_arrays_are_usable(render_device) {
if binding_arrays_are_usable(render_device, render_adapter) {
RenderViewIrradianceVolumeBindGroupEntries::get_multiple(
render_view_irradiance_volumes,
images,
@ -328,10 +329,11 @@ impl<'a> RenderViewIrradianceVolumeBindGroupEntries<'a> {
/// respectively.
pub(crate) fn get_bind_group_layout_entries(
render_device: &RenderDevice,
render_adapter: &RenderAdapter,
) -> [BindGroupLayoutEntryBuilder; 2] {
let mut texture_3d_binding =
binding_types::texture_3d(TextureSampleType::Float { filterable: true });
if binding_arrays_are_usable(render_device) {
if binding_arrays_are_usable(render_device, render_adapter) {
texture_3d_binding =
texture_3d_binding.count(NonZero::<u32>::new(MAX_VIEW_LIGHT_PROBES as _).unwrap());
}

View File

@ -20,7 +20,7 @@ use bevy_render::{
primitives::{Aabb, Frustum},
render_asset::RenderAssets,
render_resource::{DynamicUniformBuffer, Sampler, Shader, ShaderType, TextureView},
renderer::{RenderDevice, RenderQueue},
renderer::{RenderAdapter, RenderDevice, RenderQueue},
settings::WgpuFeatures,
sync_world::RenderEntity,
texture::{FallbackImage, GpuImage},
@ -778,15 +778,20 @@ pub(crate) fn add_cubemap_texture_view<'a>(
/// enough texture bindings available in the fragment shader.
///
/// 3. If binding arrays aren't supported on the hardware, then we obviously
/// can't use them.
/// can't use them. Adreno <= 610 claims to support bindless, but seems to be
/// too buggy to be usable.
///
/// 4. If binding arrays are supported on the hardware, but they can only be
/// accessed by uniform indices, that's not good enough, and we bail out.
///
/// If binding arrays aren't usable, we disable reflection probes and limit the
/// number of irradiance volumes in the scene to 1.
pub(crate) fn binding_arrays_are_usable(render_device: &RenderDevice) -> bool {
pub(crate) fn binding_arrays_are_usable(
render_device: &RenderDevice,
render_adapter: &RenderAdapter,
) -> bool {
!cfg!(feature = "shader_format_glsl")
&& bevy_render::get_adreno_model(render_adapter).is_none_or(|model| model > 610)
&& render_device.limits().max_storage_textures_per_shader_stage
>= (STANDARD_MATERIAL_FRAGMENT_SHADER_MIN_TEXTURE_BINDINGS + MAX_VIEW_LIGHT_PROBES)
as u32

View File

@ -3,8 +3,8 @@
#import bevy_pbr::mesh_bindings::mesh
#ifdef MULTIPLE_LIGHTMAPS_IN_ARRAY
@group(1) @binding(4) var lightmaps_textures: binding_array<texture_2d<f32>>;
@group(1) @binding(5) var lightmaps_samplers: binding_array<sampler>;
@group(1) @binding(4) var lightmaps_textures: binding_array<texture_2d<f32>, 4>;
@group(1) @binding(5) var lightmaps_samplers: binding_array<sampler, 4>;
#else // MULTIPLE_LIGHTMAPS_IN_ARRAY
@group(1) @binding(4) var lightmaps_texture: texture_2d<f32>;
@group(1) @binding(5) var lightmaps_sampler: sampler;

View File

@ -50,6 +50,7 @@ use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::{
render_asset::RenderAssets,
render_resource::{Sampler, Shader, TextureView, WgpuSampler, WgpuTextureView},
renderer::RenderAdapter,
sync_world::MainEntity,
texture::{FallbackImage, GpuImage},
view::ViewVisibility,
@ -71,7 +72,7 @@ pub const LIGHTMAP_SHADER_HANDLE: Handle<Shader> =
///
/// If bindless textures aren't in use, then only a single lightmap can be bound
/// at a time.
pub const LIGHTMAPS_PER_SLAB: usize = 16;
pub const LIGHTMAPS_PER_SLAB: usize = 4;
/// A plugin that provides an implementation of lightmaps.
pub struct LightmapPlugin;
@ -332,7 +333,9 @@ impl Default for Lightmap {
impl FromWorld for RenderLightmaps {
fn from_world(world: &mut World) -> Self {
let render_device = world.resource::<RenderDevice>();
let bindless_supported = binding_arrays_are_usable(render_device);
let render_adapter = world.resource::<RenderAdapter>();
let bindless_supported = binding_arrays_are_usable(render_device, render_adapter);
RenderLightmaps {
render_lightmaps: default(),

View File

@ -34,7 +34,7 @@ use bevy_render::{
RenderCommandResult, SortedRenderPhasePlugin, TrackedRenderPass,
},
render_resource::*,
renderer::{RenderDevice, RenderQueue},
renderer::{RenderAdapter, RenderDevice, RenderQueue},
texture::DefaultImageSampler,
view::{
prepare_view_targets, NoFrustumCulling, NoIndirectDrawing, RenderVisibilityRanges,
@ -1484,11 +1484,12 @@ impl FromWorld for MeshPipeline {
fn from_world(world: &mut World) -> Self {
let mut system_state: SystemState<(
Res<RenderDevice>,
Res<RenderAdapter>,
Res<DefaultImageSampler>,
Res<RenderQueue>,
Res<MeshPipelineViewLayouts>,
)> = SystemState::new(world);
let (render_device, default_sampler, render_queue, view_layouts) =
let (render_device, render_adapter, default_sampler, render_queue, view_layouts) =
system_state.get_mut(world);
let clustered_forward_buffer_binding_type = render_device
@ -1532,9 +1533,9 @@ impl FromWorld for MeshPipeline {
view_layouts: view_layouts.clone(),
clustered_forward_buffer_binding_type,
dummy_white_gpu_image,
mesh_layouts: MeshLayouts::new(&render_device),
mesh_layouts: MeshLayouts::new(&render_device, &render_adapter),
per_object_buffer_batch_size: GpuArrayBuffer::<MeshUniform>::batch_size(&render_device),
binding_arrays_are_usable: binding_arrays_are_usable(&render_device),
binding_arrays_are_usable: binding_arrays_are_usable(&render_device, &render_adapter),
skins_use_uniform_buffers: skin::skins_use_uniform_buffers(&render_device),
}
}

View File

@ -1,7 +1,11 @@
//! Bind group layout related definitions for the mesh pipeline.
use bevy_math::Mat4;
use bevy_render::{mesh::morph::MAX_MORPH_WEIGHTS, render_resource::*, renderer::RenderDevice};
use bevy_render::{
mesh::morph::MAX_MORPH_WEIGHTS,
render_resource::*,
renderer::{RenderAdapter, RenderDevice},
};
use crate::{binding_arrays_are_usable, render::skin::MAX_JOINTS, LightmapSlab};
@ -194,10 +198,10 @@ impl MeshLayouts {
/// Prepare the layouts used by the default bevy [`Mesh`].
///
/// [`Mesh`]: bevy_render::prelude::Mesh
pub fn new(render_device: &RenderDevice) -> Self {
pub fn new(render_device: &RenderDevice, render_adapter: &RenderAdapter) -> Self {
MeshLayouts {
model_only: Self::model_only_layout(render_device),
lightmapped: Self::lightmapped_layout(render_device),
lightmapped: Self::lightmapped_layout(render_device, render_adapter),
skinned: Self::skinned_layout(render_device),
skinned_motion: Self::skinned_motion_layout(render_device),
morphed: Self::morphed_layout(render_device),
@ -329,8 +333,11 @@ impl MeshLayouts {
)
}
fn lightmapped_layout(render_device: &RenderDevice) -> BindGroupLayout {
if binding_arrays_are_usable(render_device) {
fn lightmapped_layout(
render_device: &RenderDevice,
render_adapter: &RenderAdapter,
) -> BindGroupLayout {
if binding_arrays_are_usable(render_device, render_adapter) {
render_device.create_bind_group_layout(
"lightmapped_mesh_layout",
&BindGroupLayoutEntries::with_indices(

View File

@ -312,7 +312,8 @@ fn layout_entries(
);
// EnvironmentMapLight
let environment_map_entries = environment_map::get_bind_group_layout_entries(render_device);
let environment_map_entries =
environment_map::get_bind_group_layout_entries(render_device, render_adapter);
entries = entries.extend_with_indices((
(17, environment_map_entries[0]),
(18, environment_map_entries[1]),
@ -323,7 +324,7 @@ fn layout_entries(
// Irradiance volumes
if IRRADIANCE_VOLUMES_ARE_USABLE {
let irradiance_volume_entries =
irradiance_volume::get_bind_group_layout_entries(render_device);
irradiance_volume::get_bind_group_layout_entries(render_device, render_adapter);
entries = entries.extend_with_indices((
(21, irradiance_volume_entries[0]),
(22, irradiance_volume_entries[1]),
@ -493,6 +494,7 @@ pub struct MeshViewBindGroup {
pub fn prepare_mesh_view_bind_groups(
mut commands: Commands,
render_device: Res<RenderDevice>,
render_adapter: Res<RenderAdapter>,
mesh_pipeline: Res<MeshPipeline>,
shadow_samplers: Res<ShadowSamplers>,
(light_meta, global_light_meta): (Res<LightMeta>, Res<GlobalClusterableObjectMeta>),
@ -607,6 +609,7 @@ pub fn prepare_mesh_view_bind_groups(
&images,
&fallback_image,
&render_device,
&render_adapter,
);
match environment_map_bind_group_entries {
@ -642,6 +645,7 @@ pub fn prepare_mesh_view_bind_groups(
&images,
&fallback_image,
&render_device,
&render_adapter,
))
} else {
None

View File

@ -443,7 +443,7 @@ fn apply_pbr_lighting(
}
let transmitted_light_contrib =
lighting::point_light(light_id, &transmissive_lighting_input);
lighting::point_light(light_id, &transmissive_lighting_input, enable_diffuse);
transmitted_light += transmitted_light_contrib * transmitted_shadow;
#endif
}
@ -501,7 +501,7 @@ fn apply_pbr_lighting(
}
let transmitted_light_contrib =
lighting::spot_light(light_id, &transmissive_lighting_input);
lighting::spot_light(light_id, &transmissive_lighting_input, enable_diffuse);
transmitted_light += transmitted_light_contrib * transmitted_shadow;
#endif
}
@ -557,7 +557,7 @@ fn apply_pbr_lighting(
}
let transmitted_light_contrib =
lighting::directional_light(i, &transmissive_lighting_input);
lighting::directional_light(i, &transmissive_lighting_input, enable_diffuse);
transmitted_light += transmitted_light_contrib * transmitted_shadow;
#endif
}

View File

@ -36,7 +36,7 @@ use bevy_render::{
ShaderStages, ShaderType, SpecializedRenderPipeline, SpecializedRenderPipelines,
TextureFormat, TextureSampleType,
},
renderer::{RenderContext, RenderDevice, RenderQueue},
renderer::{RenderAdapter, RenderContext, RenderDevice, RenderQueue},
view::{ExtractedView, Msaa, ViewTarget, ViewUniformOffset},
Render, RenderApp, RenderSet,
};
@ -354,6 +354,7 @@ impl FromWorld for ScreenSpaceReflectionsPipeline {
fn from_world(world: &mut World) -> Self {
let mesh_view_layouts = world.resource::<MeshPipelineViewLayouts>().clone();
let render_device = world.resource::<RenderDevice>();
let render_adapter = world.resource::<RenderAdapter>();
// Create the bind group layout.
let bind_group_layout = render_device.create_bind_group_layout(
@ -404,7 +405,7 @@ impl FromWorld for ScreenSpaceReflectionsPipeline {
depth_linear_sampler,
depth_nearest_sampler,
bind_group_layout,
binding_arrays_are_usable: binding_arrays_are_usable(render_device),
binding_arrays_are_usable: binding_arrays_are_usable(render_device, render_adapter),
}
}
}

View File

@ -354,28 +354,12 @@ impl FromWorld for GpuPreprocessingSupport {
let adapter = world.resource::<RenderAdapter>();
let device = world.resource::<RenderDevice>();
// filter some Qualcomm devices on Android as they crash when using GPU preprocessing.
// Filter some Qualcomm devices on Android as they crash when using GPU
// preprocessing.
// We filter out Adreno 730 and earlier GPUs (except 720, as it's newer
// than 730).
fn is_non_supported_android_device(adapter: &RenderAdapter) -> bool {
if cfg!(target_os = "android") {
let adapter_name = adapter.get_info().name;
// Filter out Adreno 730 and earlier GPUs (except 720, as it's newer than 730)
// while also taking suffixes into account like Adreno 642L.
let non_supported_adreno_model = |model: &str| -> bool {
let model = model
.chars()
.map_while(|c| c.to_digit(10))
.fold(0, |acc, digit| acc * 10 + digit);
model != 720 && model <= 730
};
adapter_name
.strip_prefix("Adreno (TM) ")
.is_some_and(non_supported_adreno_model)
} else {
false
}
crate::get_adreno_model(adapter).is_some_and(|model| model != 720 && model <= 730)
}
let max_supported_mode = if device.limits().max_compute_workgroup_size_x == 0 || is_non_supported_android_device(adapter)

View File

@ -80,7 +80,7 @@ use bevy_window::{PrimaryWindow, RawHandleWrapperHolder};
use extract_resource::ExtractResourcePlugin;
use globals::GlobalsPlugin;
use render_asset::RenderAssetBytesPerFrame;
use renderer::{RenderDevice, RenderQueue};
use renderer::{RenderAdapter, RenderDevice, RenderQueue};
use settings::RenderResources;
use sync_world::{
despawn_temporary_render_entities, entity_sync_system, SyncToRenderWorld, SyncWorldPlugin,
@ -514,3 +514,23 @@ fn apply_extract_commands(render_world: &mut World) {
.apply_deferred(render_world);
});
}
/// If the [`RenderAdapter`] is a Qualcomm Adreno, returns its model number.
///
/// This lets us work around hardware bugs.
pub fn get_adreno_model(adapter: &RenderAdapter) -> Option<u32> {
if !cfg!(target_os = "android") {
return None;
}
let adapter_name = adapter.get_info().name;
let adreno_model = adapter_name.strip_prefix("Adreno (TM) ")?;
// Take suffixes into account (like Adreno 642L).
Some(
adreno_model
.chars()
.map_while(|c| c.to_digit(10))
.fold(0, |acc, digit| acc * 10 + digit),
)
}