diff --git a/crates/bevy_pbr/src/material.rs b/crates/bevy_pbr/src/material.rs index 3c543d3f00..443915dd6c 100644 --- a/crates/bevy_pbr/src/material.rs +++ b/crates/bevy_pbr/src/material.rs @@ -394,6 +394,8 @@ pub struct MaterialPipeline { pub material_layout: BindGroupLayout, pub vertex_shader: Option>, pub fragment_shader: Option>, + /// Whether this material *actually* uses bindless resources, taking the + /// platform support (or lack thereof) of bindless resources into account. pub bindless: bool, pub marker: PhantomData, } @@ -596,6 +598,7 @@ fn extract_mesh_materials( if view_visibility.get() { material_instances.insert(entity.into(), material.id()); + // Allocate a slot for this material in the bind group. let material_id = material.id().untyped(); material_ids .mesh_to_material diff --git a/crates/bevy_pbr/src/material_bind_groups.rs b/crates/bevy_pbr/src/material_bind_groups.rs index 863de12771..b13e97033d 100644 --- a/crates/bevy_pbr/src/material_bind_groups.rs +++ b/crates/bevy_pbr/src/material_bind_groups.rs @@ -484,7 +484,11 @@ where } } -/// Returns true if the material uses bindless resources or false if it doesn't. +/// Returns true if the material will *actually* use bindless resources or false +/// if it won't. +/// +/// This takes the platform support (or lack thereof) for bindless resources +/// into account. pub fn material_uses_bindless_resources(render_device: &RenderDevice) -> bool where M: Material, diff --git a/crates/bevy_render/macros/src/as_bind_group.rs b/crates/bevy_render/macros/src/as_bind_group.rs index 6a16fd12b3..599e40e0b8 100644 --- a/crates/bevy_render/macros/src/as_bind_group.rs +++ b/crates/bevy_render/macros/src/as_bind_group.rs @@ -52,6 +52,9 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result { let mut attr_prepared_data_ident = None; let mut attr_bindless_count = None; + // `actual_bindless_slot_count` holds the actual number of bindless slots + // per bind group, taking into account whether the current platform supports + // bindless resources. let actual_bindless_slot_count = Ident::new("actual_bindless_slot_count", Span::call_site()); // Read struct-level attributes @@ -427,6 +430,9 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result { let struct_name_literal = struct_name_literal.as_str(); let mut field_struct_impls = Vec::new(); + // The `BufferBindingType` and corresponding `BufferUsages` used for + // uniforms. We need this because bindless uniforms don't exist, so in + // bindless mode we must promote uniforms to storage buffers. let uniform_binding_type = Ident::new("uniform_binding_type", Span::call_site()); let uniform_buffer_usages = Ident::new("uniform_buffer_usages", Span::call_site()); @@ -556,6 +562,8 @@ pub fn derive_as_bind_group(ast: syn::DeriveInput) -> Result { (prepared_data.clone(), prepared_data) }; + // Calculate the actual number of bindless slots, taking hardware + // limitations into account. let (bindless_slot_count, actual_bindless_slot_count_declaration) = match attr_bindless_count { Some(bindless_count) => ( quote! { const BINDLESS_SLOT_COUNT: Option = Some(#bindless_count); }, diff --git a/crates/bevy_render/src/render_asset.rs b/crates/bevy_render/src/render_asset.rs index cc126f3e62..0a842c96ed 100644 --- a/crates/bevy_render/src/render_asset.rs +++ b/crates/bevy_render/src/render_asset.rs @@ -26,6 +26,7 @@ pub enum PrepareAssetError { AsBindGroupError(AsBindGroupError), } +/// The system set during which we extract modified assets to the render world. #[derive(SystemSet, Clone, PartialEq, Eq, Debug, Hash)] pub struct ExtractAssetsSet; @@ -68,7 +69,17 @@ pub trait RenderAsset: Send + Sync + 'static + Sized { param: &mut SystemParamItem, ) -> Result>; - fn finalize_asset(_: AssetId, _: &mut SystemParamItem) {} + /// Called whenever the [`RenderAsset::SourceAsset`] has been removed. + /// + /// You can implement this method if you need to access ECS data (via + /// `_param`) in order to perform cleanup tasks when the asset is removed. + /// + /// The default implementation does nothing. + fn finalize_asset( + _source_asset: AssetId, + _param: &mut SystemParamItem, + ) { + } } /// This plugin extracts the changed assets from the "app world" into the "render world" diff --git a/crates/bevy_render/src/render_resource/bind_group.rs b/crates/bevy_render/src/render_resource/bind_group.rs index 481ef867c8..ceccf37a11 100644 --- a/crates/bevy_render/src/render_resource/bind_group.rs +++ b/crates/bevy_render/src/render_resource/bind_group.rs @@ -257,6 +257,9 @@ impl Deref for BindGroup { /// In your shader, the index of the element of each binding array /// corresponding to the mesh currently being drawn can be retrieved with /// `mesh[in.instance_index].material_bind_group_slot`. +/// * Bindless uniforms don't exist, so in bindless mode all uniforms and +/// uniform buffers are automatically replaced with read-only storage +/// buffers. /// * The purpose of bindless mode is to improve performance by reducing /// state changes. By grouping resources together into binding arrays, Bevy /// doesn't have to modify GPU state as often, decreasing API and driver @@ -327,6 +330,13 @@ pub trait AsBindGroup { type Param: SystemParam + 'static; + /// The number of slots per bind group, if bindless mode is enabled. + /// + /// If this bind group doesn't use bindless, then this will be `None`. + /// + /// Note that the *actual* slot count may be different from this value, due + /// to platform limitations. For example, if bindless resources aren't + /// supported on this platform, the actual slot count will be 1. const BINDLESS_SLOT_COUNT: Option = None; /// label @@ -412,6 +422,8 @@ pub struct UnpreparedBindGroup { pub data: T, } +/// A pair of binding index and binding resource, used as part of +/// [`PreparedBindGroup`] and [`UnpreparedBindGroup`]. #[derive(Deref, DerefMut)] pub struct BindingResources(pub Vec<(u32, OwnedBindingResource)>); diff --git a/crates/bevy_render/src/texture/fallback_image.rs b/crates/bevy_render/src/texture/fallback_image.rs index 9ef7c04d2f..b46ee1d1de 100644 --- a/crates/bevy_render/src/texture/fallback_image.rs +++ b/crates/bevy_render/src/texture/fallback_image.rs @@ -35,6 +35,7 @@ pub struct FallbackImage { } impl FallbackImage { + /// Returns the appropriate fallback image for the given texture dimension. pub fn get(&self, texture_dimension: TextureViewDimension) -> &GpuImage { match texture_dimension { TextureViewDimension::D1 => &self.d1, diff --git a/examples/shader/shader_material_bindless.rs b/examples/shader/shader_material_bindless.rs index 5619ca8da6..ba6efbe633 100644 --- a/examples/shader/shader_material_bindless.rs +++ b/examples/shader/shader_material_bindless.rs @@ -5,16 +5,23 @@ use bevy::render::render_resource::{AsBindGroup, ShaderRef}; const SHADER_ASSET_PATH: &str = "shaders/bindless_material.wgsl"; +// `#[bindless(4)]` indicates that we want Bevy to group materials into bind +// groups of at most 4 materials each. #[derive(Asset, TypePath, AsBindGroup, Debug, Clone)] #[bindless(4)] struct BindlessMaterial { + // This will be exposed to the shader as a binding array of 4 *storage* + // buffers (as bindless uniforms don't exist). #[uniform(0)] color: LinearRgba, + // This will be exposed to the shader as a binding array of 4 textures and a + // binding array of 4 samplers. #[texture(1)] #[sampler(2)] color_texture: Option>, } +// The entry point. fn main() { App::new() .add_plugins(( @@ -25,12 +32,14 @@ fn main() { .run(); } +// Creates a simple scene. fn setup( mut commands: Commands, mut meshes: ResMut>, mut materials: ResMut>, asset_server: Res, ) { + // Add a cube with a blue tinted texture. commands.spawn(( Mesh3d(meshes.add(Cuboid::default())), MeshMaterial3d(materials.add(BindlessMaterial { @@ -40,6 +49,7 @@ fn setup( Transform::from_xyz(-2.0, 0.5, 0.0), )); + // Add a cube with a red tinted texture. commands.spawn(( Mesh3d(meshes.add(Cylinder::default())), MeshMaterial3d(materials.add(BindlessMaterial { @@ -49,7 +59,7 @@ fn setup( Transform::from_xyz(2.0, 0.5, 0.0), )); - // camera + // Add a camera. commands.spawn(( Camera3d::default(), Transform::from_xyz(-2.0, 2.5, 5.0).looking_at(Vec3::ZERO, Vec3::Y),