diff --git a/crates/bevy_pbr/src/cluster/assign.rs b/crates/bevy_pbr/src/cluster/assign.rs index 3ac9f5246c..36a4aadfb3 100644 --- a/crates/bevy_pbr/src/cluster/assign.rs +++ b/crates/bevy_pbr/src/cluster/assign.rs @@ -21,7 +21,8 @@ use bevy_utils::prelude::default; use tracing::warn; use crate::{ - binding_arrays_are_usable, decal::clustered::ClusteredDecal, prelude::EnvironmentMapLight, + decal::{self, clustered::ClusteredDecal}, + prelude::EnvironmentMapLight, ClusterConfig, ClusterFarZMode, Clusters, ExtractedPointLight, GlobalVisibleClusterableObjects, LightProbe, PointLight, SpotLight, ViewClusterBindings, VisibleClusterableObjects, 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)| { ClusterableObjectAssignmentData { entity, diff --git a/crates/bevy_pbr/src/decal/clustered.rs b/crates/bevy_pbr/src/decal/clustered.rs index dcf77c64fb..cc638151d0 100644 --- a/crates/bevy_pbr/src/decal/clustered.rs +++ b/crates/bevy_pbr/src/decal/clustered.rs @@ -6,8 +6,8 @@ //! //! 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 -//! used on WebGL 2 or WebGPU. Bevy's clustered decals can be used with forward -//! or deferred rendering and don't require a prepass. +//! used on WebGL 2, WebGPU, macOS, or iOS. Bevy's clustered decals can be used +//! 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 //! 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, /// 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 -/// or deferred rendering and don't require a prepass. +/// used on WebGL 2, WebGPU, macOS, or iOS. Bevy's clustered decals can be used +/// with forward or deferred rendering and don't require a prepass. #[derive(Component, Debug, Clone, Reflect, ExtractComponent)] #[reflect(Component, Debug)] #[require(Transform, Visibility, VisibilityClass)] @@ -263,7 +263,7 @@ pub(crate) fn get_bind_group_layout_entries( ) -> Option<[BindGroupLayoutEntryBuilder; 3]> { // If binding arrays aren't supported on the current platform, we have no // bind group layout entries. - if !binding_arrays_are_usable(render_device, render_adapter) { + if !clustered_decals_are_usable(render_device, render_adapter) { return None; } @@ -290,7 +290,7 @@ impl<'a> RenderViewClusteredDecalBindGroupEntries<'a> { render_adapter: &RenderAdapter, ) -> Option> { // 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; } @@ -367,3 +367,19 @@ fn upload_decals( 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"))) +} diff --git a/crates/bevy_pbr/src/render/mesh.rs b/crates/bevy_pbr/src/render/mesh.rs index b7e8fcf6f1..33ccbcc9af 100644 --- a/crates/bevy_pbr/src/render/mesh.rs +++ b/crates/bevy_pbr/src/render/mesh.rs @@ -1528,6 +1528,9 @@ pub struct MeshPipeline { /// This affects whether reflection probes can be used. 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 /// being unavailable on this platform. pub skins_use_uniform_buffers: bool, @@ -1589,6 +1592,10 @@ impl FromWorld for MeshPipeline { mesh_layouts: MeshLayouts::new(&render_device, &render_adapter), per_object_buffer_batch_size: GpuArrayBuffer::::batch_size(&render_device), 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), } } @@ -2295,7 +2302,7 @@ impl SpecializedMeshPipeline for MeshPipeline { 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()); } diff --git a/examples/3d/clustered_decals.rs b/examples/3d/clustered_decals.rs index 60c1bd9252..108efe586e 100644 --- a/examples/3d/clustered_decals.rs +++ b/examples/3d/clustered_decals.rs @@ -2,16 +2,22 @@ use std::f32::consts::{FRAC_PI_3, PI}; use std::fmt::{self, Formatter}; +use std::process; -use bevy::pbr::{ExtendedMaterial, MaterialExtension}; -use bevy::window::SystemCursorIcon; -use bevy::winit::cursor::CursorIcon; use bevy::{ color::palettes::css::{LIME, ORANGE_RED, SILVER}, input::mouse::AccumulatedMouseMotion, - pbr::decal::clustered::ClusteredDecal, + pbr::{ + decal::{self, clustered::ClusteredDecal}, + ExtendedMaterial, MaterialExtension, + }, 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 widgets::{ @@ -152,9 +158,17 @@ fn setup( mut commands: Commands, asset_server: Res, app_status: Res, + render_device: Res, + render_adapter: Res, mut meshes: ResMut>, mut materials: ResMut>>, ) { + // 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_camera(&mut commands); spawn_light(&mut commands);