Disable clustered decals on Metal. (#17554)

Unfortunately, Apple platforms don't have enough texture bindings to
properly support clustered decals. This should be fixed once `wgpu` has
first-class bindless texture support. In the meantime, we disable them.

Closes #17553.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
Patrick Walton 2025-01-26 21:39:07 -08:00 committed by GitHub
parent dda97880c4
commit 7aeb1c51a6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 53 additions and 14 deletions

View File

@ -21,7 +21,8 @@ use bevy_utils::prelude::default;
use tracing::warn; use tracing::warn;
use crate::{ use crate::{
binding_arrays_are_usable, decal::clustered::ClusteredDecal, prelude::EnvironmentMapLight, decal::{self, clustered::ClusteredDecal},
prelude::EnvironmentMapLight,
ClusterConfig, ClusterFarZMode, Clusters, ExtractedPointLight, GlobalVisibleClusterableObjects, ClusterConfig, ClusterFarZMode, Clusters, ExtractedPointLight, GlobalVisibleClusterableObjects,
LightProbe, PointLight, SpotLight, ViewClusterBindings, VisibleClusterableObjects, LightProbe, PointLight, SpotLight, ViewClusterBindings, VisibleClusterableObjects,
VolumetricLight, CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT, VolumetricLight, CLUSTERED_FORWARD_STORAGE_BUFFER_COUNT,
@ -257,7 +258,8 @@ pub(crate) fn assign_objects_to_clusters(
)); ));
} }
if binding_arrays_are_usable(&render_device, &render_adapter) { // Add decals if the current platform supports them.
if decal::clustered::clustered_decals_are_usable(&render_device, &render_adapter) {
clusterable_objects.extend(decals_query.iter().map(|(entity, transform)| { clusterable_objects.extend(decals_query.iter().map(|(entity, transform)| {
ClusterableObjectAssignmentData { ClusterableObjectAssignmentData {
entity, entity,

View File

@ -6,8 +6,8 @@
//! //!
//! Clustered decals are the highest-quality types of decals that Bevy supports, //! Clustered decals are the highest-quality types of decals that Bevy supports,
//! but they require bindless textures. This means that they presently can't be //! but they require bindless textures. This means that they presently can't be
//! used on WebGL 2 or WebGPU. Bevy's clustered decals can be used with forward //! used on WebGL 2, WebGPU, macOS, or iOS. Bevy's clustered decals can be used
//! or deferred rendering and don't require a prepass. //! with forward or deferred rendering and don't require a prepass.
//! //!
//! On their own, clustered decals only project the base color of a texture. You //! On their own, clustered decals only project the base color of a texture. You
//! can, however, use the built-in *tag* field to customize the appearance of a //! can, however, use the built-in *tag* field to customize the appearance of a
@ -77,8 +77,8 @@ pub struct ClusteredDecalPlugin;
/// ///
/// Clustered decals are the highest-quality types of decals that Bevy supports, /// Clustered decals are the highest-quality types of decals that Bevy supports,
/// but they require bindless textures. This means that they presently can't be /// but they require bindless textures. This means that they presently can't be
/// used on WebGL 2 or WebGPU. Bevy's clustered decals can be used with forward /// used on WebGL 2, WebGPU, macOS, or iOS. Bevy's clustered decals can be used
/// or deferred rendering and don't require a prepass. /// with forward or deferred rendering and don't require a prepass.
#[derive(Component, Debug, Clone, Reflect, ExtractComponent)] #[derive(Component, Debug, Clone, Reflect, ExtractComponent)]
#[reflect(Component, Debug)] #[reflect(Component, Debug)]
#[require(Transform, Visibility, VisibilityClass)] #[require(Transform, Visibility, VisibilityClass)]
@ -263,7 +263,7 @@ pub(crate) fn get_bind_group_layout_entries(
) -> Option<[BindGroupLayoutEntryBuilder; 3]> { ) -> Option<[BindGroupLayoutEntryBuilder; 3]> {
// If binding arrays aren't supported on the current platform, we have no // If binding arrays aren't supported on the current platform, we have no
// bind group layout entries. // bind group layout entries.
if !binding_arrays_are_usable(render_device, render_adapter) { if !clustered_decals_are_usable(render_device, render_adapter) {
return None; return None;
} }
@ -290,7 +290,7 @@ impl<'a> RenderViewClusteredDecalBindGroupEntries<'a> {
render_adapter: &RenderAdapter, render_adapter: &RenderAdapter,
) -> Option<RenderViewClusteredDecalBindGroupEntries<'a>> { ) -> Option<RenderViewClusteredDecalBindGroupEntries<'a>> {
// Skip the entries if decals are unsupported on the current platform. // Skip the entries if decals are unsupported on the current platform.
if !binding_arrays_are_usable(render_device, render_adapter) { if !clustered_decals_are_usable(render_device, render_adapter) {
return None; return None;
} }
@ -367,3 +367,19 @@ fn upload_decals(
decals_buffer.write_buffer(&render_device, &render_queue); decals_buffer.write_buffer(&render_device, &render_queue);
} }
/// Returns true if clustered decals are usable on the current platform or false
/// otherwise.
///
/// Clustered decals are currently disabled on macOS and iOS due to insufficient
/// texture bindings and limited bindless support in `wgpu`.
pub fn clustered_decals_are_usable(
render_device: &RenderDevice,
render_adapter: &RenderAdapter,
) -> bool {
// Disable binding arrays on Metal. There aren't enough texture bindings available.
// See issue #17553.
// Re-enable this when `wgpu` has first-class bindless.
binding_arrays_are_usable(render_device, render_adapter)
&& cfg!(not(any(target_os = "macos", target_os = "ios")))
}

View File

@ -1528,6 +1528,9 @@ pub struct MeshPipeline {
/// This affects whether reflection probes can be used. /// This affects whether reflection probes can be used.
pub binding_arrays_are_usable: bool, pub binding_arrays_are_usable: bool,
/// Whether clustered decals are usable on the current render device.
pub clustered_decals_are_usable: bool,
/// Whether skins will use uniform buffers on account of storage buffers /// Whether skins will use uniform buffers on account of storage buffers
/// being unavailable on this platform. /// being unavailable on this platform.
pub skins_use_uniform_buffers: bool, pub skins_use_uniform_buffers: bool,
@ -1589,6 +1592,10 @@ impl FromWorld for MeshPipeline {
mesh_layouts: MeshLayouts::new(&render_device, &render_adapter), mesh_layouts: MeshLayouts::new(&render_device, &render_adapter),
per_object_buffer_batch_size: GpuArrayBuffer::<MeshUniform>::batch_size(&render_device), per_object_buffer_batch_size: GpuArrayBuffer::<MeshUniform>::batch_size(&render_device),
binding_arrays_are_usable: binding_arrays_are_usable(&render_device, &render_adapter), binding_arrays_are_usable: binding_arrays_are_usable(&render_device, &render_adapter),
clustered_decals_are_usable: decal::clustered::clustered_decals_are_usable(
&render_device,
&render_adapter,
),
skins_use_uniform_buffers: skin::skins_use_uniform_buffers(&render_device), skins_use_uniform_buffers: skin::skins_use_uniform_buffers(&render_device),
} }
} }
@ -2295,7 +2302,7 @@ impl SpecializedMeshPipeline for MeshPipeline {
shader_defs.push("IRRADIANCE_VOLUMES_ARE_USABLE".into()); shader_defs.push("IRRADIANCE_VOLUMES_ARE_USABLE".into());
} }
if self.binding_arrays_are_usable { if self.clustered_decals_are_usable {
shader_defs.push("CLUSTERED_DECALS_ARE_USABLE".into()); shader_defs.push("CLUSTERED_DECALS_ARE_USABLE".into());
} }

View File

@ -2,16 +2,22 @@
use std::f32::consts::{FRAC_PI_3, PI}; use std::f32::consts::{FRAC_PI_3, PI};
use std::fmt::{self, Formatter}; use std::fmt::{self, Formatter};
use std::process;
use bevy::pbr::{ExtendedMaterial, MaterialExtension};
use bevy::window::SystemCursorIcon;
use bevy::winit::cursor::CursorIcon;
use bevy::{ use bevy::{
color::palettes::css::{LIME, ORANGE_RED, SILVER}, color::palettes::css::{LIME, ORANGE_RED, SILVER},
input::mouse::AccumulatedMouseMotion, input::mouse::AccumulatedMouseMotion,
pbr::decal::clustered::ClusteredDecal, pbr::{
decal::{self, clustered::ClusteredDecal},
ExtendedMaterial, MaterialExtension,
},
prelude::*, prelude::*,
render::render_resource::{AsBindGroup, ShaderRef}, render::{
render_resource::{AsBindGroup, ShaderRef},
renderer::{RenderAdapter, RenderDevice},
},
window::SystemCursorIcon,
winit::cursor::CursorIcon,
}; };
use ops::{acos, cos, sin}; use ops::{acos, cos, sin};
use widgets::{ use widgets::{
@ -152,9 +158,17 @@ fn setup(
mut commands: Commands, mut commands: Commands,
asset_server: Res<AssetServer>, asset_server: Res<AssetServer>,
app_status: Res<AppStatus>, app_status: Res<AppStatus>,
render_device: Res<RenderDevice>,
render_adapter: Res<RenderAdapter>,
mut meshes: ResMut<Assets<Mesh>>, mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ExtendedMaterial<StandardMaterial, CustomDecalExtension>>>, mut materials: ResMut<Assets<ExtendedMaterial<StandardMaterial, CustomDecalExtension>>>,
) { ) {
// Error out if clustered decals aren't supported on the current platform.
if !decal::clustered::clustered_decals_are_usable(&render_device, &render_adapter) {
eprintln!("Clustered decals aren't usable on this platform.");
process::exit(1);
}
spawn_cube(&mut commands, &mut meshes, &mut materials); spawn_cube(&mut commands, &mut meshes, &mut materials);
spawn_camera(&mut commands); spawn_camera(&mut commands);
spawn_light(&mut commands); spawn_light(&mut commands);