1use bevy_app::{App, Plugin};
8use bevy_asset::{Asset, AssetId, Handle, UntypedAssetId};
9use bevy_derive::{Deref, DerefMut};
10use bevy_ecs::{
11 resource::Resource,
12 schedule::IntoScheduleConfigs as _,
13 system::{Commands, Res, ResMut, SystemParamItem},
14};
15use bevy_platform::collections::{hash_map::Entry, HashMap, HashSet};
16use bevy_reflect::{prelude::ReflectDefault, Reflect};
17use bevy_utils::{default, TypeIdHashMap};
18use bytemuck::{Pod, Zeroable};
19use core::hash::Hash;
20use core::{cmp::Ordering, iter, mem, ops::Range};
21use std::any::TypeId;
22use tracing::{error, trace};
23
24use crate::{
25 erased_render_asset::PrepareAssetError,
26 render_asset::RenderAssets,
27 render_resource::{
28 AsBindGroup, AsBindGroupError, BindGroupBuilder, BindlessSlabResourceLimit,
29 OwnedBindingResource, PipelineCache, UnpreparedBindingResource, UnpreparedBindingResources,
30 },
31 storage::ShaderBuffer,
32 GpuResourceAppExt as _, Render, RenderApp, RenderStartup, RenderSystems,
33};
34use crate::{
35 render_resource::{
36 BindGroup, BindGroupEntry, BindGroupLayoutDescriptor, BindingNumber, BindingResource,
37 BindlessDescriptor, BindlessIndex, BindlessIndexTableDescriptor, BindlessResourceType,
38 Buffer, BufferBinding, BufferDescriptor, BufferId, BufferInitDescriptor, BufferUsages,
39 CompareFunction, FilterMode, MipmapFilterMode, PreparedBindGroup, RawBufferVec, Sampler,
40 SamplerDescriptor, SamplerId, TextureView, TextureViewDimension, TextureViewId,
41 WgpuSampler, WgpuTextureView,
42 },
43 renderer::{RenderDevice, RenderQueue},
44 settings::WgpuFeatures,
45 storage::{GpuShaderBuffer, RenderChangedShaderBuffers},
46 texture::FallbackImage,
47};
48
49pub struct MaterialBindGroupPlugin;
55
56#[derive(impl bevy_ecs::component::Component for RenderMaterialBindings where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {
const STORAGE_TYPE: bevy_ecs::component::StorageType =
bevy_ecs::component::StorageType::SparseSet;
type Mutability = bevy_ecs::component::Mutable;
fn register_required_components(_requiree:
bevy_ecs::component::ComponentId,
required_components:
&mut bevy_ecs::component::RequiredComponentsRegistrator) {
let resource_component_id =
if let ::core::option::Option::Some(id) =
required_components.components_registrator().component_id::<RenderMaterialBindings>()
{
id
} else {
required_components.components_registrator().register_component::<RenderMaterialBindings>()
};
required_components.register_required::<bevy_ecs::resource::IsResource>(move
||
bevy_ecs::resource::IsResource::new(resource_component_id));
}
fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
use bevy_ecs::component::{
DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
};
(&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
}
fn relationship_accessor()
->
::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
::core::option::Option::None
}
}
impl bevy_ecs::resource::Resource for RenderMaterialBindings where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource, #[automatically_derived]
impl ::core::default::Default for RenderMaterialBindings {
#[inline]
fn default() -> Self {
Self {
bindings: ::core::default::Default::default(),
bind_group_builder: ::core::default::Default::default(),
}
}
}Default, impl ::core::ops::Deref for RenderMaterialBindings {
type Target = HashMap<UntypedAssetId, MaterialBindingId>;
fn deref(&self) -> &Self::Target { &self.bindings }
}Deref, impl ::core::ops::DerefMut for RenderMaterialBindings {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.bindings }
}DerefMut)]
62pub struct RenderMaterialBindings {
63 #[deref]
66 bindings: HashMap<UntypedAssetId, MaterialBindingId>,
67
68 bind_group_builder: BindGroupBuilder,
73}
74
75#[derive(impl bevy_ecs::component::Component for MaterialBindGroupAllocators where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {
const STORAGE_TYPE: bevy_ecs::component::StorageType =
bevy_ecs::component::StorageType::SparseSet;
type Mutability = bevy_ecs::component::Mutable;
fn register_required_components(_requiree:
bevy_ecs::component::ComponentId,
required_components:
&mut bevy_ecs::component::RequiredComponentsRegistrator) {
let resource_component_id =
if let ::core::option::Option::Some(id) =
required_components.components_registrator().component_id::<MaterialBindGroupAllocators>()
{
id
} else {
required_components.components_registrator().register_component::<MaterialBindGroupAllocators>()
};
required_components.register_required::<bevy_ecs::resource::IsResource>(move
||
bevy_ecs::resource::IsResource::new(resource_component_id));
}
fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
use bevy_ecs::component::{
DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
};
(&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
}
fn relationship_accessor()
->
::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
::core::option::Option::None
}
}
impl bevy_ecs::resource::Resource for MaterialBindGroupAllocators where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource, impl ::core::ops::Deref for MaterialBindGroupAllocators {
type Target = TypeIdHashMap<MaterialBindGroupAllocator>;
fn deref(&self) -> &Self::Target { &self.0 }
}Deref, impl ::core::ops::DerefMut for MaterialBindGroupAllocators {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}DerefMut, #[automatically_derived]
impl ::core::default::Default for MaterialBindGroupAllocators {
#[inline]
fn default() -> Self { Self(::core::default::Default::default()) }
}Default)]
82pub struct MaterialBindGroupAllocators(TypeIdHashMap<MaterialBindGroupAllocator>);
83
84pub enum MaterialBindGroupAllocator {
86 Bindless(Box<MaterialBindGroupBindlessAllocator>),
88 NonBindless(Box<MaterialBindGroupNonBindlessAllocator>),
90}
91
92pub struct MaterialBindGroupBindlessAllocator {
95 label: &'static str,
97 slabs: Vec<MaterialBindlessSlab>,
99 bind_group_layout: BindGroupLayoutDescriptor,
101 bindless_descriptor: BindlessDescriptor,
105
106 fallback_buffers: HashMap<BindlessIndex, Buffer>,
112
113 slab_capacity: u32,
118}
119
120pub struct MaterialBindlessSlab {
122 bind_group: Option<BindGroup>,
127
128 bindless_index_tables: Vec<MaterialBindlessIndexTable>,
138
139 samplers: HashMap<BindlessResourceType, MaterialBindlessBindingArray<Sampler>>,
141 textures: HashMap<BindlessResourceType, MaterialBindlessBindingArray<TextureView>>,
143 buffers: HashMap<BindlessIndex, MaterialBindlessBindingArray<Buffer>>,
145 shader_buffers: HashMap<BindlessIndex, MaterialBindlessBindingArray<Handle<ShaderBuffer>>>,
151 data_buffers: HashMap<BindlessIndex, MaterialDataBuffer>,
154
155 free_slots: Vec<MaterialBindGroupSlot>,
157 live_allocation_count: u32,
159 allocated_resource_count: u32,
161}
162
163struct MaterialBindlessIndexTable {
170 buffer: RetainedRawBufferVec<u32>,
172 index_range: Range<BindlessIndex>,
180 binding_number: BindingNumber,
182}
183
184struct MaterialBindlessBindingArray<R>
187where
188 R: GetBindingResourceId,
189{
190 binding_number: BindingNumber,
192 bindings: Vec<Option<MaterialBindlessBinding<R>>>,
195 resource_type: BindlessResourceType,
197 resource_to_slot: HashMap<BindingResourceId, u32>,
201 free_slots: Vec<u32>,
203 len: u32,
205}
206
207struct MaterialBindlessBinding<R>
213where
214 R: GetBindingResourceId,
215{
216 resource: R,
218 ref_count: u32,
221}
222
223pub struct MaterialBindGroupNonBindlessAllocator {
225 label: &'static str,
227 bind_groups: Vec<Option<MaterialNonBindlessAllocatedBindGroup>>,
230 to_prepare: HashSet<MaterialBindGroupIndex>,
235 free_indices: Vec<MaterialBindGroupIndex>,
237}
238
239enum MaterialNonBindlessAllocatedBindGroup {
242 Unprepared {
248 bind_group: BindGroupBuilder,
250 layout: BindGroupLayoutDescriptor,
252 },
253 Prepared {
255 bind_group: PreparedBindGroup,
257 layout: BindGroupLayoutDescriptor,
264 #[expect(dead_code, reason = "These buffers are only referenced by bind groups")]
266 uniform_buffers: Vec<Buffer>,
267 },
268}
269
270#[derive(impl bevy_ecs::component::Component for FallbackBindlessResources where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {
const STORAGE_TYPE: bevy_ecs::component::StorageType =
bevy_ecs::component::StorageType::SparseSet;
type Mutability = bevy_ecs::component::Mutable;
fn register_required_components(_requiree:
bevy_ecs::component::ComponentId,
required_components:
&mut bevy_ecs::component::RequiredComponentsRegistrator) {
let resource_component_id =
if let ::core::option::Option::Some(id) =
required_components.components_registrator().component_id::<FallbackBindlessResources>()
{
id
} else {
required_components.components_registrator().register_component::<FallbackBindlessResources>()
};
required_components.register_required::<bevy_ecs::resource::IsResource>(move
||
bevy_ecs::resource::IsResource::new(resource_component_id));
}
fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
use bevy_ecs::component::{
DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
};
(&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
}
fn relationship_accessor()
->
::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
::core::option::Option::None
}
}
impl bevy_ecs::resource::Resource for FallbackBindlessResources where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource)]
273pub struct FallbackBindlessResources {
274 filtering_sampler: Sampler,
276 non_filtering_sampler: Sampler,
278 comparison_sampler: Sampler,
280}
281
282#[derive(impl bevy_ecs::component::Component for FallbackBuffer where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {
const STORAGE_TYPE: bevy_ecs::component::StorageType =
bevy_ecs::component::StorageType::SparseSet;
type Mutability = bevy_ecs::component::Mutable;
fn register_required_components(_requiree:
bevy_ecs::component::ComponentId,
required_components:
&mut bevy_ecs::component::RequiredComponentsRegistrator) {
let resource_component_id =
if let ::core::option::Option::Some(id) =
required_components.components_registrator().component_id::<FallbackBuffer>()
{
id
} else {
required_components.components_registrator().register_component::<FallbackBuffer>()
};
required_components.register_required::<bevy_ecs::resource::IsResource>(move
||
bevy_ecs::resource::IsResource::new(resource_component_id));
}
fn clone_behavior() -> bevy_ecs::component::ComponentCloneBehavior {
use bevy_ecs::component::{
DefaultCloneBehaviorBase, DefaultCloneBehaviorViaClone,
};
(&&&bevy_ecs::component::DefaultCloneBehaviorSpecialization::<Self>::default()).default_clone_behavior()
}
fn relationship_accessor()
->
::core::option::Option<bevy_ecs::relationship::ComponentRelationshipAccessor<Self>> {
::core::option::Option::None
}
}
impl bevy_ecs::resource::Resource for FallbackBuffer where
Self: ::core::marker::Send + ::core::marker::Sync + 'static {}Resource, impl ::core::ops::Deref for FallbackBuffer {
type Target = Buffer;
fn deref(&self) -> &Self::Target { &self.0 }
}Deref, impl ::core::ops::DerefMut for FallbackBuffer {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}DerefMut)]
285pub struct FallbackBuffer(Buffer);
286
287#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for BindingResourceId { }
#[automatically_derived]
impl ::core::clone::Clone for BindingResourceId {
#[inline]
fn clone(&self) -> Self {
let _: ::core::clone::AssertParamIsClone<BufferId>;
let _: ::core::clone::AssertParamIsClone<TextureViewDimension>;
let _: ::core::clone::AssertParamIsClone<TextureViewId>;
let _: ::core::clone::AssertParamIsClone<SamplerId>;
let _: ::core::clone::AssertParamIsClone<AssetId<ShaderBuffer>>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for BindingResourceId { }Copy, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for BindingResourceId { }
#[automatically_derived]
impl ::core::cmp::PartialEq for BindingResourceId {
#[inline]
fn eq(&self, other: &Self) -> bool {
::core::intrinsics::discriminant_value(self) ==
::core::intrinsics::discriminant_value(other) &&
match (self, other) {
(Self::Buffer(__self_0), Self::Buffer(__arg1_0)) =>
__self_0 == __arg1_0,
(Self::TextureView(__self_0, __self_1),
Self::TextureView(__arg1_0, __arg1_1)) =>
__self_0 == __arg1_0 && __self_1 == __arg1_1,
(Self::Sampler(__self_0), Self::Sampler(__arg1_0)) =>
__self_0 == __arg1_0,
(Self::ShaderBuffer(__self_0), Self::ShaderBuffer(__arg1_0))
=> __self_0 == __arg1_0,
_ => true,
}
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for BindingResourceId {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<BufferId>;
let _: ::core::cmp::AssertParamIsEq<TextureViewDimension>;
let _: ::core::cmp::AssertParamIsEq<TextureViewId>;
let _: ::core::cmp::AssertParamIsEq<SamplerId>;
let _: ::core::cmp::AssertParamIsEq<AssetId<ShaderBuffer>>;
}
}Eq, #[automatically_derived]
impl ::core::hash::Hash for BindingResourceId {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&::core::intrinsics::discriminant_value(self),
state);
match self {
Self::Buffer(__self_0) =>
::core::hash::Hash::hash(__self_0, state),
Self::TextureView(__self_0, __self_1) => {
::core::hash::Hash::hash(__self_0, state);
::core::hash::Hash::hash(__self_1, state)
}
Self::Sampler(__self_0) =>
::core::hash::Hash::hash(__self_0, state),
Self::ShaderBuffer(__self_0) =>
::core::hash::Hash::hash(__self_0, state),
_ => {}
}
}
}Hash, #[automatically_derived]
impl ::core::fmt::Debug for BindingResourceId {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Self::Buffer(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Buffer",
&__self_0),
Self::TextureView(__self_0, __self_1) =>
::core::fmt::Formatter::debug_tuple_field2_finish(f,
"TextureView", __self_0, &__self_1),
Self::Sampler(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"Sampler", &__self_0),
Self::DataBuffer =>
::core::fmt::Formatter::write_str(f, "DataBuffer"),
Self::ShaderBuffer(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"ShaderBuffer", &__self_0),
}
}
}Debug)]
289enum BindingResourceId {
290 Buffer(BufferId),
292 TextureView(TextureViewDimension, TextureViewId),
294 Sampler(SamplerId),
296 DataBuffer,
301 ShaderBuffer(AssetId<ShaderBuffer>),
303}
304
305enum BindingResourceArray<'a> {
311 Buffers(Vec<BufferBinding<'a>>),
313 TextureViews(Vec<&'a WgpuTextureView>),
315 Samplers(Vec<&'a WgpuSampler>),
317}
318
319#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for MaterialBindingId { }
#[automatically_derived]
impl ::core::clone::Clone for MaterialBindingId {
#[inline]
fn clone(&self) -> Self {
let _: ::core::clone::AssertParamIsClone<MaterialBindGroupIndex>;
let _: ::core::clone::AssertParamIsClone<MaterialBindGroupSlot>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for MaterialBindingId { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for MaterialBindingId {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f,
"MaterialBindingId", "group", &self.group, "slot", &&self.slot)
}
}Debug, #[automatically_derived]
impl ::core::default::Default for MaterialBindingId {
#[inline]
fn default() -> Self {
Self {
group: ::core::default::Default::default(),
slot: ::core::default::Default::default(),
}
}
}Default, const _: () =
{
if !(::core::mem::size_of::<MaterialBindingId>() ==
(::core::mem::size_of::<MaterialBindGroupIndex>() +
::core::mem::size_of::<MaterialBindGroupSlot>())) {
{
::std::rt::begin_panic("derive(Pod) was applied to a type with padding");
}
};
};
const _: fn() =
||
{
#[allow(clippy :: missing_const_for_fn)]
#[doc(hidden)]
fn check() {
fn assert_impl<T: ::bytemuck::Pod>() {}
assert_impl::<MaterialBindGroupIndex>();
}
};
const _: fn() =
||
{
#[allow(clippy :: missing_const_for_fn)]
#[doc(hidden)]
fn check() {
fn assert_impl<T: ::bytemuck::Pod>() {}
assert_impl::<MaterialBindGroupSlot>();
}
};
unsafe impl ::bytemuck::Pod for MaterialBindingId {}Pod, const _: fn() =
||
{
#[allow(clippy :: missing_const_for_fn)]
#[doc(hidden)]
fn check() {
fn assert_impl<T: ::bytemuck::Zeroable>() {}
assert_impl::<MaterialBindGroupIndex>();
}
};
const _: fn() =
||
{
#[allow(clippy :: missing_const_for_fn)]
#[doc(hidden)]
fn check() {
fn assert_impl<T: ::bytemuck::Zeroable>() {}
assert_impl::<MaterialBindGroupSlot>();
}
};
unsafe impl ::bytemuck::Zeroable for MaterialBindingId {}Zeroable, const _: () =
{
impl bevy_reflect::GetTypeRegistration for MaterialBindingId where {
fn get_type_registration() -> bevy_reflect::TypeRegistration {
let mut registration =
bevy_reflect::TypeRegistration::of::<Self>();
registration.insert(<bevy_reflect::ReflectFromPtr as
bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
registration.insert(<bevy_reflect::ReflectFromReflect as
bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
registration.register_type_data_with::<ReflectDefault, Self,
_>(());
registration
}
#[inline(never)]
fn register_type_dependencies(registry:
&mut bevy_reflect::TypeRegistry) {
<MaterialBindGroupIndex as
bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
<MaterialBindGroupSlot as
bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
}
}
impl bevy_reflect::Typed for MaterialBindingId where {
#[inline(never)]
fn type_info() -> &'static bevy_reflect::TypeInfo {
static CELL: bevy_reflect::utility::NonGenericTypeInfoCell =
bevy_reflect::utility::NonGenericTypeInfoCell::new();
CELL.get_or_set(#[inline(never)] ||
{
bevy_reflect::TypeInfo::Struct(bevy_reflect::structs::StructInfo::new::<Self>(&[bevy_reflect::NamedField::new::<MaterialBindGroupIndex>("group"),
bevy_reflect::NamedField::new::<MaterialBindGroupSlot>("slot")]))
})
}
}
#[allow(deprecated, reason =
"derives on a deprecated type shouldn't be considered a usage")]
impl bevy_reflect::TypePath for MaterialBindingId where {
fn type_path() -> &'static str {
"bevy_render::material_bind_groups::MaterialBindingId"
}
fn short_type_path() -> &'static str { "MaterialBindingId" }
fn type_ident() -> ::core::option::Option<&'static str> {
::core::option::Option::Some("MaterialBindingId")
}
fn crate_name() -> ::core::option::Option<&'static str> {
::core::option::Option::Some("bevy_render::material_bind_groups".split(':').next().unwrap())
}
fn module_path() -> ::core::option::Option<&'static str> {
::core::option::Option::Some("bevy_render::material_bind_groups")
}
}
impl bevy_reflect::Reflect for MaterialBindingId where {
#[inline]
fn into_any(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
->
bevy_reflect::__macro_exports::alloc_utils::Box<dyn ::core::any::Any> {
self
}
#[inline]
fn as_any(&self) -> &dyn ::core::any::Any { self }
#[inline]
fn as_any_mut(&mut self) -> &mut dyn ::core::any::Any { self }
#[inline]
fn into_reflect(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
->
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect> {
self
}
#[inline]
fn as_reflect(&self) -> &dyn bevy_reflect::Reflect { self }
#[inline]
fn as_reflect_mut(&mut self) -> &mut dyn bevy_reflect::Reflect {
self
}
#[inline]
fn set(&mut self,
value:
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>)
->
::core::result::Result<(),
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>> {
*self = <dyn bevy_reflect::Reflect>::take(value)?;
::core::result::Result::Ok(())
}
}
impl bevy_reflect::func::args::GetOwnership for MaterialBindingId
where {
fn ownership() -> bevy_reflect::func::args::Ownership {
bevy_reflect::func::args::Ownership::Owned
}
}
impl bevy_reflect::func::args::FromArg for MaterialBindingId where {
type This<'from_arg> = MaterialBindingId;
fn from_arg(arg: bevy_reflect::func::args::Arg)
->
::core::result::Result<Self::This<'_>,
bevy_reflect::func::args::ArgError> {
arg.take_owned()
}
}
impl bevy_reflect::func::IntoReturn for MaterialBindingId where {
fn into_return<'into_return>(self)
-> bevy_reflect::func::Return<'into_return> where
Self: 'into_return {
bevy_reflect::func::Return::Owned(bevy_reflect::__macro_exports::alloc_utils::Box::new(self))
}
}
#[allow(non_upper_case_globals)]
const _: () =
{
static __INVENTORY: ::inventory::Node =
::inventory::Node {
value: &{
bevy_reflect::__macro_exports::auto_register::AutomaticReflectRegistrations(<MaterialBindingId
as
bevy_reflect::__macro_exports::auto_register::RegisterForReflection>::__register)
},
next: ::inventory::__private::UnsafeCell::new(::inventory::__private::Option::None),
};
#[link_section = ".text.startup"]
unsafe extern "C" fn __ctor() {
unsafe {
::inventory::ErasedNode::submit(__INVENTORY.value,
&__INVENTORY)
}
}
#[used]
#[link_section = ".init_array"]
static __CTOR: unsafe extern "C" fn() = __ctor;
};
impl bevy_reflect::structs::Struct for MaterialBindingId where {
fn field(&self, name: &str)
-> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
match name {
"group" => ::core::option::Option::Some(&self.group),
"slot" => ::core::option::Option::Some(&self.slot),
_ => ::core::option::Option::None,
}
}
fn field_mut(&mut self, name: &str)
->
::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
match name {
"group" => ::core::option::Option::Some(&mut self.group),
"slot" => ::core::option::Option::Some(&mut self.slot),
_ => ::core::option::Option::None,
}
}
fn field_at(&self, index: usize)
-> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
match index {
0usize => ::core::option::Option::Some(&self.group),
1usize => ::core::option::Option::Some(&self.slot),
_ => ::core::option::Option::None,
}
}
fn field_at_mut(&mut self, index: usize)
->
::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
match index {
0usize => ::core::option::Option::Some(&mut self.group),
1usize => ::core::option::Option::Some(&mut self.slot),
_ => ::core::option::Option::None,
}
}
fn name_at(&self, index: usize) -> ::core::option::Option<&str> {
match index {
0usize => ::core::option::Option::Some("group"),
1usize => ::core::option::Option::Some("slot"),
_ => ::core::option::Option::None,
}
}
fn index_of_name(&self, name: &str)
-> ::core::option::Option<usize> {
match name {
"group" => ::core::option::Option::Some(0usize),
"slot" => ::core::option::Option::Some(1usize),
_ => ::core::option::Option::None,
}
}
fn field_len(&self) -> usize { 2usize }
fn iter_fields(&self) -> bevy_reflect::structs::FieldIter {
bevy_reflect::structs::FieldIter::new(self)
}
fn to_dynamic_struct(&self)
->
::core::result::Result<bevy_reflect::structs::DynamicStruct,
bevy_reflect::ReflectCloneError> {
let mut dynamic: bevy_reflect::structs::DynamicStruct =
::core::default::Default::default();
dynamic.set_represented_type(bevy_reflect::PartialReflect::get_represented_type_info(self));
dynamic.insert_boxed("group",
bevy_reflect::PartialReflect::to_dynamic(&self.group)?);
dynamic.insert_boxed("slot",
bevy_reflect::PartialReflect::to_dynamic(&self.slot)?);
::core::result::Result::Ok(dynamic)
}
}
impl bevy_reflect::PartialReflect for MaterialBindingId where {
#[inline]
fn get_represented_type_info(&self)
-> ::core::option::Option<&'static bevy_reflect::TypeInfo> {
::core::option::Option::Some(<Self as
bevy_reflect::Typed>::type_info())
}
#[inline]
fn try_apply(&mut self, value: &dyn bevy_reflect::PartialReflect)
-> ::core::result::Result<(), bevy_reflect::ApplyError> {
if let bevy_reflect::ReflectRef::Struct(struct_value) =
bevy_reflect::PartialReflect::reflect_ref(value) {
for (name, value) in
bevy_reflect::structs::Struct::iter_fields(struct_value) {
if let ::core::option::Option::Some(v) =
bevy_reflect::structs::Struct::field_mut(self, name) {
bevy_reflect::PartialReflect::try_apply(v, value)?;
}
}
} else {
return ::core::result::Result::Err(bevy_reflect::ApplyError::MismatchedKinds {
from_kind: bevy_reflect::PartialReflect::reflect_kind(value),
to_kind: bevy_reflect::ReflectKind::Struct,
});
}
::core::result::Result::Ok(())
}
#[inline]
fn reflect_kind(&self) -> bevy_reflect::ReflectKind {
bevy_reflect::ReflectKind::Struct
}
#[inline]
fn reflect_ref(&self) -> bevy_reflect::ReflectRef {
bevy_reflect::ReflectRef::Struct(self)
}
#[inline]
fn reflect_mut(&mut self) -> bevy_reflect::ReflectMut {
bevy_reflect::ReflectMut::Struct(self)
}
#[inline]
fn reflect_owned(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
-> bevy_reflect::ReflectOwned {
bevy_reflect::ReflectOwned::Struct(self)
}
#[inline]
fn try_into_reflect(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
->
::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect>> {
::core::result::Result::Ok(self)
}
#[inline]
fn try_as_reflect(&self)
-> ::core::option::Option<&dyn bevy_reflect::Reflect> {
::core::option::Option::Some(self)
}
#[inline]
fn try_as_reflect_mut(&mut self)
-> ::core::option::Option<&mut dyn bevy_reflect::Reflect> {
::core::option::Option::Some(self)
}
#[inline]
fn into_partial_reflect(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
->
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect> {
self
}
#[inline]
fn as_partial_reflect(&self)
-> &dyn bevy_reflect::PartialReflect {
self
}
#[inline]
fn as_partial_reflect_mut(&mut self)
-> &mut dyn bevy_reflect::PartialReflect {
self
}
fn reflect_partial_eq(&self,
value: &dyn bevy_reflect::PartialReflect)
-> ::core::option::Option<bool> {
(bevy_reflect::structs::struct_partial_eq)(self, value)
}
fn reflect_partial_cmp(&self,
value: &dyn bevy_reflect::PartialReflect)
-> ::core::option::Option<::core::cmp::Ordering> {
(bevy_reflect::structs::struct_partial_cmp)(self, value)
}
#[inline]
fn reflect_clone(&self)
->
::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
bevy_reflect::ReflectCloneError> {
::core::result::Result::Ok(bevy_reflect::__macro_exports::alloc_utils::Box::new(::core::clone::Clone::clone(self)))
}
}
impl bevy_reflect::FromReflect for MaterialBindingId where {
fn from_reflect(reflect: &dyn bevy_reflect::PartialReflect)
-> ::core::option::Option<Self> {
if let bevy_reflect::ReflectRef::Struct(__ref_struct) =
bevy_reflect::PartialReflect::reflect_ref(reflect) {
let mut __this =
<Self as ::core::default::Default>::default();
if let ::core::option::Option::Some(__field) =
(||
<MaterialBindGroupIndex as
bevy_reflect::FromReflect>::from_reflect(bevy_reflect::structs::Struct::field(__ref_struct,
"group")?))() {
__this.group = __field;
}
if let ::core::option::Option::Some(__field) =
(||
<MaterialBindGroupSlot as
bevy_reflect::FromReflect>::from_reflect(bevy_reflect::structs::Struct::field(__ref_struct,
"slot")?))() {
__this.slot = __field;
}
::core::option::Option::Some(__this)
} else { ::core::option::Option::None }
}
}
};Reflect)]
322#[reflect(Clone, Default)]
323#[repr(C)]
324pub struct MaterialBindingId {
325 pub group: MaterialBindGroupIndex,
327 pub slot: MaterialBindGroupSlot,
331}
332
333#[derive(
338 #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for MaterialBindGroupIndex { }
#[automatically_derived]
impl ::core::clone::Clone for MaterialBindGroupIndex {
#[inline]
fn clone(&self) -> Self {
let _: ::core::clone::AssertParamIsClone<u32>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for MaterialBindGroupIndex { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for MaterialBindGroupIndex {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"MaterialBindGroupIndex", &&self.0)
}
}Debug, #[automatically_derived]
impl ::core::default::Default for MaterialBindGroupIndex {
#[inline]
fn default() -> Self { Self(::core::default::Default::default()) }
}Default, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for MaterialBindGroupIndex { }
#[automatically_derived]
impl ::core::cmp::PartialEq for MaterialBindGroupIndex {
#[inline]
fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for MaterialBindGroupIndex {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<u32>;
}
}Eq, #[automatically_derived]
impl ::core::hash::Hash for MaterialBindGroupIndex {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&self.0, state)
}
}Hash, const _: () =
{
if !(::core::mem::size_of::<MaterialBindGroupIndex>() ==
(::core::mem::size_of::<u32>())) {
{
::std::rt::begin_panic("derive(Pod) was applied to a type with padding");
}
};
};
const _: fn() =
||
{
#[allow(clippy :: missing_const_for_fn)]
#[doc(hidden)]
fn check() {
fn assert_impl<T: ::bytemuck::Pod>() {}
assert_impl::<u32>();
}
};
unsafe impl ::bytemuck::Pod for MaterialBindGroupIndex {}Pod, const _: fn() =
||
{
#[allow(clippy :: missing_const_for_fn)]
#[doc(hidden)]
fn check() {
fn assert_impl<T: ::bytemuck::Zeroable>() {}
assert_impl::<u32>();
}
};
unsafe impl ::bytemuck::Zeroable for MaterialBindGroupIndex {}Zeroable, const _: () =
{
impl bevy_reflect::GetTypeRegistration for MaterialBindGroupIndex
where {
fn get_type_registration() -> bevy_reflect::TypeRegistration {
let mut registration =
bevy_reflect::TypeRegistration::of::<Self>();
registration.insert(<bevy_reflect::ReflectFromPtr as
bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
registration.insert(<bevy_reflect::ReflectFromReflect as
bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
registration.register_type_data_with::<ReflectDefault, Self,
_>(());
registration
}
#[inline(never)]
fn register_type_dependencies(registry:
&mut bevy_reflect::TypeRegistry) {
<u32 as
bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
}
}
impl bevy_reflect::Typed for MaterialBindGroupIndex where {
#[inline(never)]
fn type_info() -> &'static bevy_reflect::TypeInfo {
static CELL: bevy_reflect::utility::NonGenericTypeInfoCell =
bevy_reflect::utility::NonGenericTypeInfoCell::new();
CELL.get_or_set(#[inline(never)] ||
{
bevy_reflect::TypeInfo::TupleStruct(bevy_reflect::tuple_struct::TupleStructInfo::new::<Self>(&[bevy_reflect::UnnamedField::new::<u32>(0usize)]))
})
}
}
#[allow(deprecated, reason =
"derives on a deprecated type shouldn't be considered a usage")]
impl bevy_reflect::TypePath for MaterialBindGroupIndex where {
fn type_path() -> &'static str {
"bevy_render::material_bind_groups::MaterialBindGroupIndex"
}
fn short_type_path() -> &'static str { "MaterialBindGroupIndex" }
fn type_ident() -> ::core::option::Option<&'static str> {
::core::option::Option::Some("MaterialBindGroupIndex")
}
fn crate_name() -> ::core::option::Option<&'static str> {
::core::option::Option::Some("bevy_render::material_bind_groups".split(':').next().unwrap())
}
fn module_path() -> ::core::option::Option<&'static str> {
::core::option::Option::Some("bevy_render::material_bind_groups")
}
}
impl bevy_reflect::Reflect for MaterialBindGroupIndex where {
#[inline]
fn into_any(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
->
bevy_reflect::__macro_exports::alloc_utils::Box<dyn ::core::any::Any> {
self
}
#[inline]
fn as_any(&self) -> &dyn ::core::any::Any { self }
#[inline]
fn as_any_mut(&mut self) -> &mut dyn ::core::any::Any { self }
#[inline]
fn into_reflect(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
->
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect> {
self
}
#[inline]
fn as_reflect(&self) -> &dyn bevy_reflect::Reflect { self }
#[inline]
fn as_reflect_mut(&mut self) -> &mut dyn bevy_reflect::Reflect {
self
}
#[inline]
fn set(&mut self,
value:
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>)
->
::core::result::Result<(),
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>> {
*self = <dyn bevy_reflect::Reflect>::take(value)?;
::core::result::Result::Ok(())
}
}
impl bevy_reflect::func::args::GetOwnership for MaterialBindGroupIndex
where {
fn ownership() -> bevy_reflect::func::args::Ownership {
bevy_reflect::func::args::Ownership::Owned
}
}
impl bevy_reflect::func::args::FromArg for MaterialBindGroupIndex
where {
type This<'from_arg> = MaterialBindGroupIndex;
fn from_arg(arg: bevy_reflect::func::args::Arg)
->
::core::result::Result<Self::This<'_>,
bevy_reflect::func::args::ArgError> {
arg.take_owned()
}
}
impl bevy_reflect::func::IntoReturn for MaterialBindGroupIndex where
{
fn into_return<'into_return>(self)
-> bevy_reflect::func::Return<'into_return> where
Self: 'into_return {
bevy_reflect::func::Return::Owned(bevy_reflect::__macro_exports::alloc_utils::Box::new(self))
}
}
#[allow(non_upper_case_globals)]
const _: () =
{
static __INVENTORY: ::inventory::Node =
::inventory::Node {
value: &{
bevy_reflect::__macro_exports::auto_register::AutomaticReflectRegistrations(<MaterialBindGroupIndex
as
bevy_reflect::__macro_exports::auto_register::RegisterForReflection>::__register)
},
next: ::inventory::__private::UnsafeCell::new(::inventory::__private::Option::None),
};
#[link_section = ".text.startup"]
unsafe extern "C" fn __ctor() {
unsafe {
::inventory::ErasedNode::submit(__INVENTORY.value,
&__INVENTORY)
}
}
#[used]
#[link_section = ".init_array"]
static __CTOR: unsafe extern "C" fn() = __ctor;
};
impl bevy_reflect::tuple_struct::TupleStruct for
MaterialBindGroupIndex where {
fn field(&self, index: usize)
-> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
match index {
0usize => ::core::option::Option::Some(&self.0),
_ => ::core::option::Option::None,
}
}
fn field_mut(&mut self, index: usize)
->
::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
match index {
0usize => ::core::option::Option::Some(&mut self.0),
_ => ::core::option::Option::None,
}
}
#[inline]
fn field_len(&self) -> usize { 1usize }
#[inline]
fn iter_fields(&self)
-> bevy_reflect::tuple_struct::TupleStructFieldIter {
bevy_reflect::tuple_struct::TupleStructFieldIter::new(self)
}
fn to_dynamic_tuple_struct(&self)
->
::core::result::Result<bevy_reflect::tuple_struct::DynamicTupleStruct,
bevy_reflect::ReflectCloneError> {
let mut dynamic:
bevy_reflect::tuple_struct::DynamicTupleStruct =
::core::default::Default::default();
dynamic.set_represented_type(bevy_reflect::PartialReflect::get_represented_type_info(self));
dynamic.insert_boxed(bevy_reflect::PartialReflect::to_dynamic(&self.0)?);
::core::result::Result::Ok(dynamic)
}
}
impl bevy_reflect::PartialReflect for MaterialBindGroupIndex where {
#[inline]
fn get_represented_type_info(&self)
-> ::core::option::Option<&'static bevy_reflect::TypeInfo> {
::core::option::Option::Some(<Self as
bevy_reflect::Typed>::type_info())
}
#[inline]
fn try_apply(&mut self, value: &dyn bevy_reflect::PartialReflect)
-> ::core::result::Result<(), bevy_reflect::ApplyError> {
if let bevy_reflect::ReflectRef::TupleStruct(struct_value) =
bevy_reflect::PartialReflect::reflect_ref(value) {
for (i, value) in
::core::iter::Iterator::enumerate(bevy_reflect::tuple_struct::TupleStruct::iter_fields(struct_value))
{
if let ::core::option::Option::Some(v) =
bevy_reflect::tuple_struct::TupleStruct::field_mut(self, i)
{
bevy_reflect::PartialReflect::try_apply(v, value)?;
}
}
} else {
return ::core::result::Result::Err(bevy_reflect::ApplyError::MismatchedKinds {
from_kind: bevy_reflect::PartialReflect::reflect_kind(value),
to_kind: bevy_reflect::ReflectKind::TupleStruct,
});
}
::core::result::Result::Ok(())
}
#[inline]
fn reflect_kind(&self) -> bevy_reflect::ReflectKind {
bevy_reflect::ReflectKind::TupleStruct
}
#[inline]
fn reflect_ref(&self) -> bevy_reflect::ReflectRef {
bevy_reflect::ReflectRef::TupleStruct(self)
}
#[inline]
fn reflect_mut(&mut self) -> bevy_reflect::ReflectMut {
bevy_reflect::ReflectMut::TupleStruct(self)
}
#[inline]
fn reflect_owned(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
-> bevy_reflect::ReflectOwned {
bevy_reflect::ReflectOwned::TupleStruct(self)
}
#[inline]
fn try_into_reflect(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
->
::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect>> {
::core::result::Result::Ok(self)
}
#[inline]
fn try_as_reflect(&self)
-> ::core::option::Option<&dyn bevy_reflect::Reflect> {
::core::option::Option::Some(self)
}
#[inline]
fn try_as_reflect_mut(&mut self)
-> ::core::option::Option<&mut dyn bevy_reflect::Reflect> {
::core::option::Option::Some(self)
}
#[inline]
fn into_partial_reflect(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
->
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect> {
self
}
#[inline]
fn as_partial_reflect(&self)
-> &dyn bevy_reflect::PartialReflect {
self
}
#[inline]
fn as_partial_reflect_mut(&mut self)
-> &mut dyn bevy_reflect::PartialReflect {
self
}
fn reflect_hash(&self) -> ::core::option::Option<u64> {
use ::core::hash::{Hash, Hasher};
let mut hasher = bevy_reflect::utility::reflect_hasher();
Hash::hash(&::core::any::Any::type_id(self), &mut hasher);
Hash::hash(self, &mut hasher);
::core::option::Option::Some(Hasher::finish(&hasher))
}
fn reflect_partial_eq(&self,
value: &dyn bevy_reflect::PartialReflect)
-> ::core::option::Option<bool> {
let value =
<dyn bevy_reflect::PartialReflect>::try_downcast_ref::<Self>(value);
if let ::core::option::Option::Some(value) = value {
::core::option::Option::Some(::core::cmp::PartialEq::eq(self,
value))
} else { ::core::option::Option::Some(false) }
}
fn reflect_partial_cmp(&self,
value: &dyn bevy_reflect::PartialReflect)
-> ::core::option::Option<::core::cmp::Ordering> {
(bevy_reflect::tuple_struct::tuple_struct_partial_cmp)(self,
value)
}
#[inline]
fn reflect_clone(&self)
->
::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
bevy_reflect::ReflectCloneError> {
::core::result::Result::Ok(bevy_reflect::__macro_exports::alloc_utils::Box::new(::core::clone::Clone::clone(self)))
}
}
impl bevy_reflect::FromReflect for MaterialBindGroupIndex where {
fn from_reflect(reflect: &dyn bevy_reflect::PartialReflect)
-> ::core::option::Option<Self> {
if let bevy_reflect::ReflectRef::TupleStruct(__ref_struct) =
bevy_reflect::PartialReflect::reflect_ref(reflect) {
let mut __this =
<Self as ::core::default::Default>::default();
if let ::core::option::Option::Some(__field) =
(||
<u32 as
bevy_reflect::FromReflect>::from_reflect(bevy_reflect::tuple_struct::TupleStruct::field(__ref_struct,
0)?))() {
__this.0 = __field;
}
::core::option::Option::Some(__this)
} else { ::core::option::Option::None }
}
}
};Reflect, impl ::core::ops::Deref for MaterialBindGroupIndex {
type Target = u32;
fn deref(&self) -> &Self::Target { &self.0 }
}Deref, impl ::core::ops::DerefMut for MaterialBindGroupIndex {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}DerefMut,
339)]
340#[reflect(Default, Clone, PartialEq, Hash)]
341#[repr(C)]
342pub struct MaterialBindGroupIndex(pub u32);
343
344impl From<u32> for MaterialBindGroupIndex {
345 fn from(value: u32) -> Self {
346 MaterialBindGroupIndex(value)
347 }
348}
349
350#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for MaterialBindGroupSlot { }
#[automatically_derived]
impl ::core::clone::Clone for MaterialBindGroupSlot {
#[inline]
fn clone(&self) -> Self {
let _: ::core::clone::AssertParamIsClone<u32>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::Copy for MaterialBindGroupSlot { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for MaterialBindGroupSlot {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field1_finish(f,
"MaterialBindGroupSlot", &&self.0)
}
}Debug, #[automatically_derived]
impl ::core::default::Default for MaterialBindGroupSlot {
#[inline]
fn default() -> Self { Self(::core::default::Default::default()) }
}Default, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for MaterialBindGroupSlot { }
#[automatically_derived]
impl ::core::cmp::PartialEq for MaterialBindGroupSlot {
#[inline]
fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}PartialEq, const _: () =
{
if !(::core::mem::size_of::<MaterialBindGroupSlot>() ==
(::core::mem::size_of::<u32>())) {
{
::std::rt::begin_panic("derive(Pod) was applied to a type with padding");
}
};
};
const _: fn() =
||
{
#[allow(clippy :: missing_const_for_fn)]
#[doc(hidden)]
fn check() {
fn assert_impl<T: ::bytemuck::Pod>() {}
assert_impl::<u32>();
}
};
unsafe impl ::bytemuck::Pod for MaterialBindGroupSlot {}Pod, const _: fn() =
||
{
#[allow(clippy :: missing_const_for_fn)]
#[doc(hidden)]
fn check() {
fn assert_impl<T: ::bytemuck::Zeroable>() {}
assert_impl::<u32>();
}
};
unsafe impl ::bytemuck::Zeroable for MaterialBindGroupSlot {}Zeroable, const _: () =
{
impl bevy_reflect::GetTypeRegistration for MaterialBindGroupSlot where
{
fn get_type_registration() -> bevy_reflect::TypeRegistration {
let mut registration =
bevy_reflect::TypeRegistration::of::<Self>();
registration.insert(<bevy_reflect::ReflectFromPtr as
bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
registration.insert(<bevy_reflect::ReflectFromReflect as
bevy_reflect::CreateTypeData<Self>>::create_type_data(()));
registration.register_type_data_with::<ReflectDefault, Self,
_>(());
registration
}
#[inline(never)]
fn register_type_dependencies(registry:
&mut bevy_reflect::TypeRegistry) {
<u32 as
bevy_reflect::__macro_exports::RegisterForReflection>::__register(registry);
}
}
impl bevy_reflect::Typed for MaterialBindGroupSlot where {
#[inline(never)]
fn type_info() -> &'static bevy_reflect::TypeInfo {
static CELL: bevy_reflect::utility::NonGenericTypeInfoCell =
bevy_reflect::utility::NonGenericTypeInfoCell::new();
CELL.get_or_set(#[inline(never)] ||
{
bevy_reflect::TypeInfo::TupleStruct(bevy_reflect::tuple_struct::TupleStructInfo::new::<Self>(&[bevy_reflect::UnnamedField::new::<u32>(0usize)]))
})
}
}
#[allow(deprecated, reason =
"derives on a deprecated type shouldn't be considered a usage")]
impl bevy_reflect::TypePath for MaterialBindGroupSlot where {
fn type_path() -> &'static str {
"bevy_render::material_bind_groups::MaterialBindGroupSlot"
}
fn short_type_path() -> &'static str { "MaterialBindGroupSlot" }
fn type_ident() -> ::core::option::Option<&'static str> {
::core::option::Option::Some("MaterialBindGroupSlot")
}
fn crate_name() -> ::core::option::Option<&'static str> {
::core::option::Option::Some("bevy_render::material_bind_groups".split(':').next().unwrap())
}
fn module_path() -> ::core::option::Option<&'static str> {
::core::option::Option::Some("bevy_render::material_bind_groups")
}
}
impl bevy_reflect::Reflect for MaterialBindGroupSlot where {
#[inline]
fn into_any(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
->
bevy_reflect::__macro_exports::alloc_utils::Box<dyn ::core::any::Any> {
self
}
#[inline]
fn as_any(&self) -> &dyn ::core::any::Any { self }
#[inline]
fn as_any_mut(&mut self) -> &mut dyn ::core::any::Any { self }
#[inline]
fn into_reflect(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
->
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect> {
self
}
#[inline]
fn as_reflect(&self) -> &dyn bevy_reflect::Reflect { self }
#[inline]
fn as_reflect_mut(&mut self) -> &mut dyn bevy_reflect::Reflect {
self
}
#[inline]
fn set(&mut self,
value:
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>)
->
::core::result::Result<(),
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>> {
*self = <dyn bevy_reflect::Reflect>::take(value)?;
::core::result::Result::Ok(())
}
}
impl bevy_reflect::func::args::GetOwnership for MaterialBindGroupSlot
where {
fn ownership() -> bevy_reflect::func::args::Ownership {
bevy_reflect::func::args::Ownership::Owned
}
}
impl bevy_reflect::func::args::FromArg for MaterialBindGroupSlot where
{
type This<'from_arg> = MaterialBindGroupSlot;
fn from_arg(arg: bevy_reflect::func::args::Arg)
->
::core::result::Result<Self::This<'_>,
bevy_reflect::func::args::ArgError> {
arg.take_owned()
}
}
impl bevy_reflect::func::IntoReturn for MaterialBindGroupSlot where {
fn into_return<'into_return>(self)
-> bevy_reflect::func::Return<'into_return> where
Self: 'into_return {
bevy_reflect::func::Return::Owned(bevy_reflect::__macro_exports::alloc_utils::Box::new(self))
}
}
#[allow(non_upper_case_globals)]
const _: () =
{
static __INVENTORY: ::inventory::Node =
::inventory::Node {
value: &{
bevy_reflect::__macro_exports::auto_register::AutomaticReflectRegistrations(<MaterialBindGroupSlot
as
bevy_reflect::__macro_exports::auto_register::RegisterForReflection>::__register)
},
next: ::inventory::__private::UnsafeCell::new(::inventory::__private::Option::None),
};
#[link_section = ".text.startup"]
unsafe extern "C" fn __ctor() {
unsafe {
::inventory::ErasedNode::submit(__INVENTORY.value,
&__INVENTORY)
}
}
#[used]
#[link_section = ".init_array"]
static __CTOR: unsafe extern "C" fn() = __ctor;
};
impl bevy_reflect::tuple_struct::TupleStruct for MaterialBindGroupSlot
where {
fn field(&self, index: usize)
-> ::core::option::Option<&dyn bevy_reflect::PartialReflect> {
match index {
0usize => ::core::option::Option::Some(&self.0),
_ => ::core::option::Option::None,
}
}
fn field_mut(&mut self, index: usize)
->
::core::option::Option<&mut dyn bevy_reflect::PartialReflect> {
match index {
0usize => ::core::option::Option::Some(&mut self.0),
_ => ::core::option::Option::None,
}
}
#[inline]
fn field_len(&self) -> usize { 1usize }
#[inline]
fn iter_fields(&self)
-> bevy_reflect::tuple_struct::TupleStructFieldIter {
bevy_reflect::tuple_struct::TupleStructFieldIter::new(self)
}
fn to_dynamic_tuple_struct(&self)
->
::core::result::Result<bevy_reflect::tuple_struct::DynamicTupleStruct,
bevy_reflect::ReflectCloneError> {
let mut dynamic:
bevy_reflect::tuple_struct::DynamicTupleStruct =
::core::default::Default::default();
dynamic.set_represented_type(bevy_reflect::PartialReflect::get_represented_type_info(self));
dynamic.insert_boxed(bevy_reflect::PartialReflect::to_dynamic(&self.0)?);
::core::result::Result::Ok(dynamic)
}
}
impl bevy_reflect::PartialReflect for MaterialBindGroupSlot where {
#[inline]
fn get_represented_type_info(&self)
-> ::core::option::Option<&'static bevy_reflect::TypeInfo> {
::core::option::Option::Some(<Self as
bevy_reflect::Typed>::type_info())
}
#[inline]
fn try_apply(&mut self, value: &dyn bevy_reflect::PartialReflect)
-> ::core::result::Result<(), bevy_reflect::ApplyError> {
if let bevy_reflect::ReflectRef::TupleStruct(struct_value) =
bevy_reflect::PartialReflect::reflect_ref(value) {
for (i, value) in
::core::iter::Iterator::enumerate(bevy_reflect::tuple_struct::TupleStruct::iter_fields(struct_value))
{
if let ::core::option::Option::Some(v) =
bevy_reflect::tuple_struct::TupleStruct::field_mut(self, i)
{
bevy_reflect::PartialReflect::try_apply(v, value)?;
}
}
} else {
return ::core::result::Result::Err(bevy_reflect::ApplyError::MismatchedKinds {
from_kind: bevy_reflect::PartialReflect::reflect_kind(value),
to_kind: bevy_reflect::ReflectKind::TupleStruct,
});
}
::core::result::Result::Ok(())
}
#[inline]
fn reflect_kind(&self) -> bevy_reflect::ReflectKind {
bevy_reflect::ReflectKind::TupleStruct
}
#[inline]
fn reflect_ref(&self) -> bevy_reflect::ReflectRef {
bevy_reflect::ReflectRef::TupleStruct(self)
}
#[inline]
fn reflect_mut(&mut self) -> bevy_reflect::ReflectMut {
bevy_reflect::ReflectMut::TupleStruct(self)
}
#[inline]
fn reflect_owned(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
-> bevy_reflect::ReflectOwned {
bevy_reflect::ReflectOwned::TupleStruct(self)
}
#[inline]
fn try_into_reflect(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
->
::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect>> {
::core::result::Result::Ok(self)
}
#[inline]
fn try_as_reflect(&self)
-> ::core::option::Option<&dyn bevy_reflect::Reflect> {
::core::option::Option::Some(self)
}
#[inline]
fn try_as_reflect_mut(&mut self)
-> ::core::option::Option<&mut dyn bevy_reflect::Reflect> {
::core::option::Option::Some(self)
}
#[inline]
fn into_partial_reflect(self:
bevy_reflect::__macro_exports::alloc_utils::Box<Self>)
->
bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::PartialReflect> {
self
}
#[inline]
fn as_partial_reflect(&self)
-> &dyn bevy_reflect::PartialReflect {
self
}
#[inline]
fn as_partial_reflect_mut(&mut self)
-> &mut dyn bevy_reflect::PartialReflect {
self
}
fn reflect_partial_eq(&self,
value: &dyn bevy_reflect::PartialReflect)
-> ::core::option::Option<bool> {
let value =
<dyn bevy_reflect::PartialReflect>::try_downcast_ref::<Self>(value);
if let ::core::option::Option::Some(value) = value {
::core::option::Option::Some(::core::cmp::PartialEq::eq(self,
value))
} else { ::core::option::Option::Some(false) }
}
fn reflect_partial_cmp(&self,
value: &dyn bevy_reflect::PartialReflect)
-> ::core::option::Option<::core::cmp::Ordering> {
(bevy_reflect::tuple_struct::tuple_struct_partial_cmp)(self,
value)
}
#[inline]
fn reflect_clone(&self)
->
::core::result::Result<bevy_reflect::__macro_exports::alloc_utils::Box<dyn bevy_reflect::Reflect>,
bevy_reflect::ReflectCloneError> {
::core::result::Result::Ok(bevy_reflect::__macro_exports::alloc_utils::Box::new(::core::clone::Clone::clone(self)))
}
}
impl bevy_reflect::FromReflect for MaterialBindGroupSlot where {
fn from_reflect(reflect: &dyn bevy_reflect::PartialReflect)
-> ::core::option::Option<Self> {
if let bevy_reflect::ReflectRef::TupleStruct(__ref_struct) =
bevy_reflect::PartialReflect::reflect_ref(reflect) {
let mut __this =
<Self as ::core::default::Default>::default();
if let ::core::option::Option::Some(__field) =
(||
<u32 as
bevy_reflect::FromReflect>::from_reflect(bevy_reflect::tuple_struct::TupleStruct::field(__ref_struct,
0)?))() {
__this.0 = __field;
}
::core::option::Option::Some(__this)
} else { ::core::option::Option::None }
}
}
};Reflect, impl ::core::ops::Deref for MaterialBindGroupSlot {
type Target = u32;
fn deref(&self) -> &Self::Target { &self.0 }
}Deref, impl ::core::ops::DerefMut for MaterialBindGroupSlot {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}DerefMut)]
357#[reflect(Default, Clone, PartialEq)]
358#[repr(C)]
359pub struct MaterialBindGroupSlot(pub u32);
360
361#[derive(#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for BufferDirtyState { }
#[automatically_derived]
impl ::core::clone::Clone for BufferDirtyState {
#[inline]
fn clone(&self) -> Self { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for BufferDirtyState { }Copy, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for BufferDirtyState { }
#[automatically_derived]
impl ::core::cmp::PartialEq for BufferDirtyState {
#[inline]
fn eq(&self, other: &Self) -> bool {
::core::intrinsics::discriminant_value(self) ==
::core::intrinsics::discriminant_value(other)
}
}PartialEq)]
366enum BufferDirtyState {
367 Clean,
369 NeedsReserve,
371 NeedsUpload,
373}
374
375struct BindlessAllocationCandidate {
378 pre_existing_resources: HashMap<BindlessIndex, u32>,
382 needed_free_slots: u32,
385}
386
387trait GetBindingResourceId {
394 fn binding_resource_id(&self, resource_type: BindlessResourceType) -> BindingResourceId;
400}
401
402pub struct MaterialSlab<'a>(MaterialSlabImpl<'a>);
404
405enum MaterialSlabImpl<'a> {
409 Bindless(&'a MaterialBindlessSlab),
412 NonBindless(MaterialNonBindlessSlab<'a>),
415}
416
417enum MaterialNonBindlessSlab<'a> {
420 Prepared(&'a PreparedBindGroup),
422 Unprepared,
424}
425
426struct MaterialDataBuffer {
431 binding_number: BindingNumber,
433 buffer: RetainedRawBufferVec<u8>,
438 aligned_element_size: u32,
441 free_slots: Vec<u32>,
443 len: u32,
445}
446
447#[derive(impl<T> ::core::ops::Deref for RetainedRawBufferVec<T> where T: Pod {
type Target = RawBufferVec<T>;
fn deref(&self) -> &Self::Target { &self.buffer }
}Deref, impl<T> ::core::ops::DerefMut for RetainedRawBufferVec<T> where T: Pod {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.buffer }
}DerefMut)]
453struct RetainedRawBufferVec<T>
454where
455 T: Pod,
456{
457 #[deref]
459 buffer: RawBufferVec<T>,
460 dirty: BufferDirtyState,
462}
463
464const DEFAULT_BINDLESS_FALLBACK_BUFFER_SIZE: u64 = 16;
469
470impl From<u32> for MaterialBindGroupSlot {
471 fn from(value: u32) -> Self {
472 MaterialBindGroupSlot(value)
473 }
474}
475
476impl From<MaterialBindGroupSlot> for u32 {
477 fn from(value: MaterialBindGroupSlot) -> Self {
478 value.0
479 }
480}
481
482impl<'a> From<&'a UnpreparedBindingResource> for BindingResourceId {
483 fn from(value: &'a UnpreparedBindingResource) -> Self {
484 match *value {
485 UnpreparedBindingResource::Buffer(ref buffer) => BindingResourceId::Buffer(buffer.id()),
486 UnpreparedBindingResource::ShaderBuffer(ref shader_buffer) => {
487 BindingResourceId::ShaderBuffer(shader_buffer.id())
488 }
489 UnpreparedBindingResource::Data(_) => BindingResourceId::DataBuffer,
490 UnpreparedBindingResource::TextureView(
491 ref texture_view_dimension,
492 ref texture_view,
493 ) => BindingResourceId::TextureView(*texture_view_dimension, texture_view.id()),
494 UnpreparedBindingResource::Sampler(_, ref sampler) => {
495 BindingResourceId::Sampler(sampler.id())
496 }
497 }
498 }
499}
500
501impl GetBindingResourceId for Buffer {
502 fn binding_resource_id(&self, _: BindlessResourceType) -> BindingResourceId {
503 BindingResourceId::Buffer(self.id())
504 }
505}
506
507impl GetBindingResourceId for Sampler {
508 fn binding_resource_id(&self, _: BindlessResourceType) -> BindingResourceId {
509 BindingResourceId::Sampler(self.id())
510 }
511}
512
513impl GetBindingResourceId for TextureView {
514 fn binding_resource_id(&self, resource_type: BindlessResourceType) -> BindingResourceId {
515 let texture_view_dimension = match resource_type {
516 BindlessResourceType::Texture1d => TextureViewDimension::D1,
517 BindlessResourceType::Texture2d => TextureViewDimension::D2,
518 BindlessResourceType::Texture2dArray => TextureViewDimension::D2Array,
519 BindlessResourceType::Texture3d => TextureViewDimension::D3,
520 BindlessResourceType::TextureCube => TextureViewDimension::Cube,
521 BindlessResourceType::TextureCubeArray => TextureViewDimension::CubeArray,
522 _ => {
::core::panicking::panic_fmt(format_args!("Resource type is not a texture"));
}panic!("Resource type is not a texture"),
523 };
524 BindingResourceId::TextureView(texture_view_dimension, self.id())
525 }
526}
527
528impl Plugin for MaterialBindGroupPlugin {
529 fn build(&self, app: &mut App) {
530 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
531 return;
532 };
533
534 render_app
535 .init_resource::<MaterialBindGroupAllocators>()
536 .allow_ambiguous_resource::<MaterialBindGroupAllocators>()
537 .init_gpu_resource::<RenderMaterialBindings>()
538 .allow_ambiguous_resource::<RenderMaterialBindings>()
539 .add_systems(RenderStartup, init_fallback_resources)
540 .add_systems(
541 Render,
542 (
543 prepare_material_bind_groups,
544 write_material_bind_group_buffers,
545 )
546 .chain()
547 .in_set(RenderSystems::PrepareBindGroups),
548 );
549 }
550}
551
552impl GetBindingResourceId for Handle<ShaderBuffer> {
553 fn binding_resource_id(&self, _: BindlessResourceType) -> BindingResourceId {
554 BindingResourceId::ShaderBuffer(self.id())
555 }
556}
557
558impl MaterialBindGroupAllocator {
559 pub fn new(
562 render_device: &RenderDevice,
563 label: &'static str,
564 bindless_descriptor: Option<BindlessDescriptor>,
565 bind_group_layout: BindGroupLayoutDescriptor,
566 slab_capacity: Option<BindlessSlabResourceLimit>,
567 ) -> MaterialBindGroupAllocator {
568 if let Some(bindless_descriptor) = bindless_descriptor {
569 MaterialBindGroupAllocator::Bindless(Box::new(MaterialBindGroupBindlessAllocator::new(
570 render_device,
571 label,
572 bindless_descriptor,
573 bind_group_layout,
574 slab_capacity,
575 )))
576 } else {
577 MaterialBindGroupAllocator::NonBindless(Box::new(
578 MaterialBindGroupNonBindlessAllocator::new(label),
579 ))
580 }
581 }
582
583 pub fn get(&self, group: MaterialBindGroupIndex) -> Option<MaterialSlab<'_>> {
585 match *self {
586 MaterialBindGroupAllocator::Bindless(ref bindless_allocator) => bindless_allocator
587 .get(group)
588 .map(|bindless_slab| MaterialSlab(MaterialSlabImpl::Bindless(bindless_slab))),
589 MaterialBindGroupAllocator::NonBindless(ref non_bindless_allocator) => {
590 non_bindless_allocator.get(group).map(|non_bindless_slab| {
591 MaterialSlab(MaterialSlabImpl::NonBindless(non_bindless_slab))
592 })
593 }
594 }
595 }
596
597 pub fn allocate_unprepared(
605 &mut self,
606 unprepared_bind_group: &mut BindGroupBuilder,
607 bind_group_layout: &BindGroupLayoutDescriptor,
608 ) -> MaterialBindingId {
609 match *self {
610 MaterialBindGroupAllocator::Bindless(
611 ref mut material_bind_group_bindless_allocator,
612 ) => material_bind_group_bindless_allocator.allocate_unprepared(unprepared_bind_group),
613 MaterialBindGroupAllocator::NonBindless(
614 ref mut material_bind_group_non_bindless_allocator,
615 ) => material_bind_group_non_bindless_allocator
616 .allocate_unprepared(unprepared_bind_group, (*bind_group_layout).clone()),
617 }
618 }
619
620 pub fn allocate_prepared(
630 &mut self,
631 prepared_bind_group: PreparedBindGroup,
632 bind_group_layout: BindGroupLayoutDescriptor,
633 ) -> MaterialBindingId {
634 match *self {
635 MaterialBindGroupAllocator::Bindless(_) => {
636 {
::core::panicking::panic_fmt(format_args!("Bindless resources are incompatible with implementing `as_bind_group` directly; implement `unprepared_bind_group` instead or disable bindless"));
}panic!(
637 "Bindless resources are incompatible with implementing `as_bind_group` \
638 directly; implement `unprepared_bind_group` instead or disable bindless"
639 )
640 }
641 MaterialBindGroupAllocator::NonBindless(ref mut non_bindless_allocator) => {
642 non_bindless_allocator.allocate_prepared(prepared_bind_group, bind_group_layout)
643 }
644 }
645 }
646
647 pub fn free(&mut self, material_binding_id: MaterialBindingId) {
651 match *self {
652 MaterialBindGroupAllocator::Bindless(
653 ref mut material_bind_group_bindless_allocator,
654 ) => material_bind_group_bindless_allocator.free(material_binding_id),
655 MaterialBindGroupAllocator::NonBindless(
656 ref mut material_bind_group_non_bindless_allocator,
657 ) => material_bind_group_non_bindless_allocator.free(material_binding_id),
658 }
659 }
660
661 pub fn try_update_data(
673 &mut self,
674 existing_binding_id: MaterialBindingId,
675 unprepared_bind_group: &mut BindGroupBuilder,
676 ) -> bool {
677 match *self {
678 MaterialBindGroupAllocator::Bindless(
679 ref mut material_bind_group_bindless_allocator,
680 ) => material_bind_group_bindless_allocator
681 .try_update_data(existing_binding_id, unprepared_bind_group),
682 MaterialBindGroupAllocator::NonBindless(_) => {
683 false
685 }
686 }
687 }
688
689 pub fn prepare_bind_groups(
692 &mut self,
693 render_device: &RenderDevice,
694 pipeline_cache: &PipelineCache,
695 fallback_bindless_resources: &FallbackBindlessResources,
696 fallback_image: &FallbackImage,
697 fallback_buffer: &FallbackBuffer,
698 shader_buffer_assets: &RenderAssets<GpuShaderBuffer>,
699 changed_shader_buffers: &RenderChangedShaderBuffers,
700 ) {
701 match *self {
702 MaterialBindGroupAllocator::Bindless(
703 ref mut material_bind_group_bindless_allocator,
704 ) => material_bind_group_bindless_allocator.prepare_bind_groups(
705 render_device,
706 pipeline_cache,
707 fallback_bindless_resources,
708 fallback_image,
709 shader_buffer_assets,
710 changed_shader_buffers,
711 ),
712 MaterialBindGroupAllocator::NonBindless(
713 ref mut material_bind_group_non_bindless_allocator,
714 ) => material_bind_group_non_bindless_allocator.prepare_bind_groups(
715 render_device,
716 pipeline_cache,
717 fallback_buffer,
718 shader_buffer_assets,
719 changed_shader_buffers,
720 ),
721 }
722 }
723
724 pub fn write_buffers(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
730 match *self {
731 MaterialBindGroupAllocator::Bindless(
732 ref mut material_bind_group_bindless_allocator,
733 ) => material_bind_group_bindless_allocator.write_buffers(render_device, render_queue),
734 MaterialBindGroupAllocator::NonBindless(_) => {
735 }
737 }
738 }
739
740 pub fn slab_count(&self) -> usize {
743 match self {
744 Self::Bindless(bless) => bless.slabs.len(),
745 Self::NonBindless(_) => 0,
746 }
747 }
748
749 pub fn slabs_size(&self) -> usize {
752 match self {
753 Self::Bindless(bless) => bless
754 .slabs
755 .iter()
756 .flat_map(|slab| {
757 slab.data_buffers
758 .iter()
759 .map(|(_, buffer)| buffer.buffer.len())
760 })
761 .sum(),
762 Self::NonBindless(_) => 0,
763 }
764 }
765
766 pub fn allocations(&self) -> u64 {
769 match self {
770 Self::Bindless(bless) => bless
771 .slabs
772 .iter()
773 .map(|slab| u64::from(slab.allocated_resource_count))
774 .sum(),
775 Self::NonBindless(_) => 0,
776 }
777 }
778}
779
780impl MaterialBindlessIndexTable {
781 fn new(
783 bindless_index_table_descriptor: &BindlessIndexTableDescriptor,
784 ) -> MaterialBindlessIndexTable {
785 let mut buffer = RetainedRawBufferVec::new(BufferUsages::STORAGE);
787 for _ in *bindless_index_table_descriptor.indices.start
788 ..*bindless_index_table_descriptor.indices.end
789 {
790 buffer.push(0);
791 }
792
793 MaterialBindlessIndexTable {
794 buffer,
795 index_range: bindless_index_table_descriptor.indices.clone(),
796 binding_number: bindless_index_table_descriptor.binding_number,
797 }
798 }
799
800 fn get(&self, slot: MaterialBindGroupSlot) -> &[u32] {
806 let struct_size = *self.index_range.end as usize - *self.index_range.start as usize;
807 let start = struct_size * slot.0 as usize;
808 &self.buffer.values()[start..(start + struct_size)]
809 }
810
811 fn get_binding(
813 &self,
814 slot: MaterialBindGroupSlot,
815 bindless_index: BindlessIndex,
816 ) -> Option<u32> {
817 if bindless_index < self.index_range.start || bindless_index >= self.index_range.end {
818 return None;
819 }
820 self.get(slot)
821 .get((*bindless_index - *self.index_range.start) as usize)
822 .copied()
823 }
824
825 fn table_length(&self) -> u32 {
826 self.index_range.end.0 - self.index_range.start.0
827 }
828
829 fn set(
837 &mut self,
838 slot: MaterialBindGroupSlot,
839 allocated_resource_slots: &HashMap<BindlessIndex, u32>,
840 ) {
841 let table_len = self.table_length() as usize;
842 let range = (slot.0 as usize * table_len)..((slot.0 as usize + 1) * table_len);
843 while self.buffer.len() < range.end {
844 self.buffer.push(0);
845 }
846
847 for (&bindless_index, &resource_slot) in allocated_resource_slots {
848 if self.index_range.contains(&bindless_index) {
849 self.buffer.set(
850 *bindless_index + range.start as u32 - *self.index_range.start,
851 resource_slot,
852 );
853 }
854 }
855
856 self.buffer.dirty = BufferDirtyState::NeedsReserve;
858 }
859
860 fn bind_group_entry(&self) -> BindGroupEntry<'_> {
862 BindGroupEntry {
863 binding: *self.binding_number,
864 resource: self
865 .buffer
866 .buffer()
867 .expect("Bindings buffer must exist")
868 .as_entire_binding(),
869 }
870 }
871}
872
873impl<T> RetainedRawBufferVec<T>
874where
875 T: Pod,
876{
877 fn new(buffer_usages: BufferUsages) -> RetainedRawBufferVec<T> {
880 RetainedRawBufferVec {
881 buffer: RawBufferVec::new(buffer_usages),
882 dirty: BufferDirtyState::NeedsUpload,
883 }
884 }
885
886 fn prepare(&mut self, render_device: &RenderDevice) {
888 match self.dirty {
889 BufferDirtyState::Clean | BufferDirtyState::NeedsUpload => {}
890 BufferDirtyState::NeedsReserve => {
891 let capacity = self.buffer.len();
892 self.buffer.reserve(capacity, render_device);
893 self.dirty = BufferDirtyState::NeedsUpload;
894 }
895 }
896 }
897
898 fn write(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
900 match self.dirty {
901 BufferDirtyState::Clean => {}
902 BufferDirtyState::NeedsReserve | BufferDirtyState::NeedsUpload => {
903 self.buffer.write_buffer(render_device, render_queue);
904 self.dirty = BufferDirtyState::Clean;
905 }
906 }
907 }
908}
909
910impl MaterialBindGroupBindlessAllocator {
911 fn new(
914 render_device: &RenderDevice,
915 label: &'static str,
916 bindless_descriptor: BindlessDescriptor,
917 bind_group_layout: BindGroupLayoutDescriptor,
918 slab_capacity: Option<BindlessSlabResourceLimit>,
919 ) -> MaterialBindGroupBindlessAllocator {
920 let fallback_buffers = bindless_descriptor
921 .buffers
922 .iter()
923 .map(|bindless_buffer_descriptor| {
924 (
925 bindless_buffer_descriptor.bindless_index,
926 render_device.create_buffer(&BufferDescriptor {
927 label: Some("bindless fallback buffer"),
928 size: match bindless_buffer_descriptor.size {
929 Some(size) => size as u64,
930 None => DEFAULT_BINDLESS_FALLBACK_BUFFER_SIZE,
931 },
932 usage: BufferUsages::STORAGE,
933 mapped_at_creation: false,
934 }),
935 )
936 })
937 .collect();
938
939 MaterialBindGroupBindlessAllocator {
940 label,
941 slabs: ::alloc::vec::Vec::new()vec![],
942 bind_group_layout,
943 bindless_descriptor,
944 fallback_buffers,
945 slab_capacity: slab_capacity
946 .expect("Non-bindless materials should use the non-bindless allocator")
947 .resolve(),
948 }
949 }
950
951 fn allocate_unprepared(
960 &mut self,
961 unprepared_bind_group: &mut BindGroupBuilder,
962 ) -> MaterialBindingId {
963 for (slab_index, slab) in self.slabs.iter_mut().enumerate() {
964 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event src/material_bind_groups.rs:964",
"bevy_render::material_bind_groups",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("src/material_bind_groups.rs"),
::tracing_core::__macro_support::Option::Some(964u32),
::tracing_core::__macro_support::Option::Some("bevy_render::material_bind_groups"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Trying to allocate in slab {0}",
slab_index) as &dyn ::tracing::field::Value))])
});
} else { ; }
};trace!("Trying to allocate in slab {}", slab_index);
965 if let Ok(slot) = slab.try_allocate(unprepared_bind_group, self.slab_capacity) {
966 return MaterialBindingId {
967 group: MaterialBindGroupIndex(slab_index as u32),
968 slot,
969 };
970 }
971 }
972
973 let group = MaterialBindGroupIndex(self.slabs.len() as u32);
974 self.slabs
975 .push(MaterialBindlessSlab::new(&self.bindless_descriptor));
976
977 let Ok(slot) = self
979 .slabs
980 .last_mut()
981 .expect("We just pushed a slab")
982 .try_allocate(unprepared_bind_group, self.slab_capacity)
983 else {
984 {
::core::panicking::panic_fmt(format_args!("An allocation into an empty slab should always succeed"));
}panic!("An allocation into an empty slab should always succeed")
985 };
986
987 MaterialBindingId { group, slot }
988 }
989
990 fn try_update_data(
996 &mut self,
997 existing_binding_id: MaterialBindingId,
998 unprepared_bind_group: &mut BindGroupBuilder,
999 ) -> bool {
1000 self.slabs
1001 .get_mut(existing_binding_id.group.0 as usize)
1002 .is_some_and(|slab| {
1003 slab.try_update_data(existing_binding_id.slot, unprepared_bind_group)
1004 })
1005 }
1006
1007 fn free(&mut self, material_binding_id: MaterialBindingId) {
1011 self.slabs
1012 .get_mut(material_binding_id.group.0 as usize)
1013 .expect("Slab should exist")
1014 .free(material_binding_id.slot, &self.bindless_descriptor);
1015 }
1016
1017 fn get(&self, group: MaterialBindGroupIndex) -> Option<&MaterialBindlessSlab> {
1022 self.slabs.get(group.0 as usize)
1023 }
1024
1025 fn prepare_bind_groups(
1029 &mut self,
1030 render_device: &RenderDevice,
1031 pipeline_cache: &PipelineCache,
1032 fallback_bindless_resources: &FallbackBindlessResources,
1033 fallback_image: &FallbackImage,
1034 shader_buffer_assets: &RenderAssets<GpuShaderBuffer>,
1035 changed_shader_buffers: &RenderChangedShaderBuffers,
1036 ) {
1037 for slab in &mut self.slabs {
1038 slab.prepare(
1039 render_device,
1040 pipeline_cache,
1041 self.label,
1042 &self.bind_group_layout,
1043 fallback_bindless_resources,
1044 &self.fallback_buffers,
1045 fallback_image,
1046 shader_buffer_assets,
1047 changed_shader_buffers,
1048 &self.bindless_descriptor,
1049 self.slab_capacity,
1050 );
1051 }
1052 }
1053
1054 fn write_buffers(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
1058 for slab in &mut self.slabs {
1059 slab.write_buffer(render_device, render_queue);
1060 }
1061 }
1062}
1063
1064impl MaterialBindlessSlab {
1065 fn try_allocate(
1072 &mut self,
1073 unprepared_bind_group: &mut BindGroupBuilder,
1074 slot_capacity: u32,
1075 ) -> Result<MaterialBindGroupSlot, ()> {
1076 let Some(allocation_candidate) =
1078 self.check_allocation(&unprepared_bind_group.binding_resources)
1079 else {
1080 return Err(());
1081 };
1082
1083 if self.allocated_resource_count > 0
1092 && self.allocated_resource_count + allocation_candidate.needed_free_slots
1093 > slot_capacity
1094 {
1095 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event src/material_bind_groups.rs:1095",
"bevy_render::material_bind_groups",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("src/material_bind_groups.rs"),
::tracing_core::__macro_support::Option::Some(1095u32),
::tracing_core::__macro_support::Option::Some("bevy_render::material_bind_groups"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Slab is full, can\'t allocate")
as &dyn ::tracing::field::Value))])
});
} else { ; }
};trace!("Slab is full, can't allocate");
1096 return Err(());
1097 }
1098
1099 let slot = match self.free_slots.pop() {
1101 Some(slot) => slot,
1102 None => {
1103 if self.live_allocation_count > 0xFFFF {
1106 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event src/material_bind_groups.rs:1106",
"bevy_render::material_bind_groups",
::tracing::Level::TRACE,
::tracing_core::__macro_support::Option::Some("src/material_bind_groups.rs"),
::tracing_core::__macro_support::Option::Some(1106u32),
::tracing_core::__macro_support::Option::Some("bevy_render::material_bind_groups"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::TRACE <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::TRACE <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Slab material bind group slot would overflow, can\'t allocate")
as &dyn ::tracing::field::Value))])
});
} else { ; }
};trace!("Slab material bind group slot would overflow, can't allocate");
1107 return Err(());
1108 }
1109 MaterialBindGroupSlot(self.live_allocation_count)
1110 }
1111 };
1112
1113 self.live_allocation_count += 1;
1115
1116 let allocated_resource_slots =
1118 self.insert_resources(unprepared_bind_group, allocation_candidate);
1119
1120 for bindless_index_table in &mut self.bindless_index_tables {
1122 bindless_index_table.set(slot, &allocated_resource_slots);
1123 }
1124
1125 self.bind_group = None;
1127
1128 Ok(slot)
1129 }
1130
1131 fn try_update_data(
1137 &mut self,
1138 existing_slot: MaterialBindGroupSlot,
1139 unprepared_bind_group: &mut BindGroupBuilder,
1140 ) -> bool {
1141 if !self
1144 .bindless_index_tables
1145 .iter()
1146 .all(|bindless_index_table| {
1147 self.gpu_resources_are_unchanged(
1148 &unprepared_bind_group.binding_resources,
1149 existing_slot,
1150 bindless_index_table,
1151 )
1152 })
1153 {
1154 return false;
1155 }
1156
1157 for bindless_index_table_index in 0..self.bindless_index_tables.len() {
1161 self.update_data(
1162 unprepared_bind_group,
1163 existing_slot,
1164 bindless_index_table_index,
1165 );
1166 }
1167
1168 true
1169 }
1170
1171 fn check_allocation(
1174 &self,
1175 unprepared_binding_resources: &UnpreparedBindingResources,
1176 ) -> Option<BindlessAllocationCandidate> {
1177 let mut allocation_candidate = BindlessAllocationCandidate {
1178 pre_existing_resources: HashMap::default(),
1179 needed_free_slots: 0,
1180 };
1181
1182 for &(bindless_index, ref unprepared_binding_resource) in
1183 unprepared_binding_resources.iter()
1184 {
1185 let bindless_index = BindlessIndex(bindless_index);
1186 match *unprepared_binding_resource {
1187 UnpreparedBindingResource::Buffer(ref buffer) => {
1188 let Some(binding_array) = self.buffers.get(&bindless_index) else {
1189 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event src/material_bind_groups.rs:1189",
"bevy_render::material_bind_groups",
::tracing::Level::ERROR,
::tracing_core::__macro_support::Option::Some("src/material_bind_groups.rs"),
::tracing_core::__macro_support::Option::Some(1189u32),
::tracing_core::__macro_support::Option::Some("bevy_render::material_bind_groups"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::ERROR <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::ERROR <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Binding array wasn\'t present for buffer at index {0:?}",
bindless_index) as &dyn ::tracing::field::Value))])
});
} else { ; }
};error!(
1190 "Binding array wasn't present for buffer at index {:?}",
1191 bindless_index
1192 );
1193 return None;
1194 };
1195 match binding_array.find(BindingResourceId::Buffer(buffer.id())) {
1196 Some(slot) => {
1197 allocation_candidate
1198 .pre_existing_resources
1199 .insert(bindless_index, slot);
1200 }
1201 None => allocation_candidate.needed_free_slots += 1,
1202 }
1203 }
1204
1205 UnpreparedBindingResource::ShaderBuffer(ref shader_buffer) => {
1206 let Some(binding_array) = self.shader_buffers.get(&bindless_index) else {
1207 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event src/material_bind_groups.rs:1207",
"bevy_render::material_bind_groups",
::tracing::Level::ERROR,
::tracing_core::__macro_support::Option::Some("src/material_bind_groups.rs"),
::tracing_core::__macro_support::Option::Some(1207u32),
::tracing_core::__macro_support::Option::Some("bevy_render::material_bind_groups"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::ERROR <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::ERROR <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("Binding array wasn\'t present for shader buffer at index {0:?}",
bindless_index) as &dyn ::tracing::field::Value))])
});
} else { ; }
};error!(
1208 "Binding array wasn't present for shader buffer at index {:?}",
1209 bindless_index
1210 );
1211 return None;
1212 };
1213 match binding_array.find(BindingResourceId::ShaderBuffer(shader_buffer.id())) {
1214 Some(slot) => {
1215 allocation_candidate
1216 .pre_existing_resources
1217 .insert(bindless_index, slot);
1218 }
1219 None => allocation_candidate.needed_free_slots += 1,
1220 }
1221 }
1222
1223 UnpreparedBindingResource::Data(_) => {
1224 }
1226
1227 UnpreparedBindingResource::TextureView(
1228 texture_view_dimension,
1229 ref texture_view,
1230 ) => {
1231 let bindless_resource_type = BindlessResourceType::from(texture_view_dimension);
1232 match self
1233 .textures
1234 .get(&bindless_resource_type)
1235 .expect("Missing binding array for texture")
1236 .find(BindingResourceId::TextureView(
1237 texture_view_dimension,
1238 texture_view.id(),
1239 )) {
1240 Some(slot) => {
1241 allocation_candidate
1242 .pre_existing_resources
1243 .insert(bindless_index, slot);
1244 }
1245 None => {
1246 allocation_candidate.needed_free_slots += 1;
1247 }
1248 }
1249 }
1250
1251 UnpreparedBindingResource::Sampler(sampler_binding_type, ref sampler) => {
1252 let bindless_resource_type = BindlessResourceType::from(sampler_binding_type);
1253 match self
1254 .samplers
1255 .get(&bindless_resource_type)
1256 .expect("Missing binding array for sampler")
1257 .find(BindingResourceId::Sampler(sampler.id()))
1258 {
1259 Some(slot) => {
1260 allocation_candidate
1261 .pre_existing_resources
1262 .insert(bindless_index, slot);
1263 }
1264 None => {
1265 allocation_candidate.needed_free_slots += 1;
1266 }
1267 }
1268 }
1269 }
1270 }
1271
1272 Some(allocation_candidate)
1273 }
1274
1275 fn insert_resources(
1281 &mut self,
1282 unprepared_bind_group: &BindGroupBuilder,
1283 allocation_candidate: BindlessAllocationCandidate,
1284 ) -> HashMap<BindlessIndex, u32> {
1285 let mut allocated_resource_slots = HashMap::default();
1286
1287 for (bindless_index, unprepared_binding_resource) in unprepared_bind_group.iter() {
1288 let bindless_index = BindlessIndex(*bindless_index);
1289
1290 let pre_existing_slot = allocation_candidate
1291 .pre_existing_resources
1292 .get(&bindless_index);
1293
1294 let binding_resource_id = BindingResourceId::from(unprepared_binding_resource);
1296 let increment_allocated_resource_count = match unprepared_binding_resource {
1297 UnpreparedBindingResource::Buffer(buffer) => {
1298 let slot = self
1299 .buffers
1300 .get_mut(&bindless_index)
1301 .expect("Buffer binding array should exist")
1302 .insert(binding_resource_id, buffer.clone());
1303 allocated_resource_slots.insert(bindless_index, slot);
1304
1305 if true {
if !pre_existing_slot.is_none_or(|pre_existing_slot|
*pre_existing_slot == slot) {
::core::panicking::panic("assertion failed: pre_existing_slot.is_none_or(|pre_existing_slot| *pre_existing_slot == slot)")
};
};debug_assert!(pre_existing_slot
1306 .is_none_or(|pre_existing_slot| *pre_existing_slot == slot));
1307 pre_existing_slot.is_none()
1308 }
1309
1310 UnpreparedBindingResource::ShaderBuffer(shader_buffer) => {
1311 let slot = self
1312 .shader_buffers
1313 .get_mut(&bindless_index)
1314 .expect("Shader buffer binding array should exist")
1315 .insert(binding_resource_id, shader_buffer.clone());
1316 allocated_resource_slots.insert(bindless_index, slot);
1317
1318 if true {
if !pre_existing_slot.is_none_or(|pre_existing_slot|
*pre_existing_slot == slot) {
::core::panicking::panic("assertion failed: pre_existing_slot.is_none_or(|pre_existing_slot| *pre_existing_slot == slot)")
};
};debug_assert!(pre_existing_slot
1319 .is_none_or(|pre_existing_slot| *pre_existing_slot == slot));
1320 pre_existing_slot.is_none()
1321 }
1322
1323 UnpreparedBindingResource::Data(data_range) => {
1324 if pre_existing_slot.is_some() {
1325 {
::core::panicking::panic_fmt(format_args!("Data buffers can\'t be deduplicated"));
}panic!("Data buffers can't be deduplicated")
1326 }
1327
1328 let slot = self
1329 .data_buffers
1330 .get_mut(&bindless_index)
1331 .expect("Data buffer binding array should exist")
1332 .insert(
1333 &unprepared_bind_group.data_buffer
1334 [(data_range.start as usize)..(data_range.end as usize)],
1335 );
1336 allocated_resource_slots.insert(bindless_index, slot);
1337 false
1338 }
1339
1340 UnpreparedBindingResource::TextureView(texture_view_dimension, texture_view) => {
1341 let bindless_resource_type =
1342 BindlessResourceType::from(*texture_view_dimension);
1343 let slot = self
1344 .textures
1345 .get_mut(&bindless_resource_type)
1346 .expect("Texture array should exist")
1347 .insert(binding_resource_id, texture_view.clone());
1348 allocated_resource_slots.insert(bindless_index, slot);
1349
1350 if true {
if !pre_existing_slot.is_none_or(|pre_existing_slot|
*pre_existing_slot == slot) {
::core::panicking::panic("assertion failed: pre_existing_slot.is_none_or(|pre_existing_slot| *pre_existing_slot == slot)")
};
};debug_assert!(pre_existing_slot
1351 .is_none_or(|pre_existing_slot| *pre_existing_slot == slot));
1352 pre_existing_slot.is_none()
1353 }
1354
1355 UnpreparedBindingResource::Sampler(sampler_binding_type, sampler) => {
1356 let bindless_resource_type = BindlessResourceType::from(*sampler_binding_type);
1357 let slot = self
1358 .samplers
1359 .get_mut(&bindless_resource_type)
1360 .expect("Sampler should exist")
1361 .insert(binding_resource_id, sampler.clone());
1362 allocated_resource_slots.insert(bindless_index, slot);
1363
1364 if true {
if !pre_existing_slot.is_none_or(|pre_existing_slot|
*pre_existing_slot == slot) {
::core::panicking::panic("assertion failed: pre_existing_slot.is_none_or(|pre_existing_slot| *pre_existing_slot == slot)")
};
};debug_assert!(pre_existing_slot
1365 .is_none_or(|pre_existing_slot| *pre_existing_slot == slot));
1366 pre_existing_slot.is_none()
1367 }
1368 };
1369
1370 if increment_allocated_resource_count {
1372 self.allocated_resource_count += 1;
1373 }
1374 }
1375
1376 allocated_resource_slots
1377 }
1378
1379 fn free(&mut self, slot: MaterialBindGroupSlot, bindless_descriptor: &BindlessDescriptor) {
1382 for (bindless_index, bindless_resource_type) in
1384 bindless_descriptor.resources.iter().enumerate()
1385 {
1386 let bindless_index = BindlessIndex::from(bindless_index as u32);
1387 let Some(bindless_index_table) = self.get_bindless_index_table(bindless_index) else {
1388 continue;
1389 };
1390 let Some(bindless_binding) = bindless_index_table.get_binding(slot, bindless_index)
1391 else {
1392 continue;
1393 };
1394
1395 let decrement_allocated_resource_count = match *bindless_resource_type {
1399 BindlessResourceType::None => false,
1400 BindlessResourceType::Buffer => self
1401 .buffers
1402 .get_mut(&bindless_index)
1403 .expect("Buffer should exist with that bindless index")
1404 .remove(bindless_binding),
1405 BindlessResourceType::ShaderBuffer => self
1406 .shader_buffers
1407 .get_mut(&bindless_index)
1408 .expect("Shader buffer should exist with that bindless index")
1409 .remove(bindless_binding),
1410 BindlessResourceType::DataBuffer => {
1411 self.data_buffers
1412 .get_mut(&bindless_index)
1413 .expect("Data buffer should exist with that bindless index")
1414 .remove(bindless_binding);
1415 false
1416 }
1417 BindlessResourceType::SamplerFiltering
1418 | BindlessResourceType::SamplerNonFiltering
1419 | BindlessResourceType::SamplerComparison => self
1420 .samplers
1421 .get_mut(bindless_resource_type)
1422 .expect("Sampler array should exist")
1423 .remove(bindless_binding),
1424 BindlessResourceType::Texture1d
1425 | BindlessResourceType::Texture2d
1426 | BindlessResourceType::Texture2dArray
1427 | BindlessResourceType::Texture3d
1428 | BindlessResourceType::TextureCube
1429 | BindlessResourceType::TextureCubeArray => self
1430 .textures
1431 .get_mut(bindless_resource_type)
1432 .expect("Texture array should exist")
1433 .remove(bindless_binding),
1434 };
1435
1436 if decrement_allocated_resource_count {
1439 self.allocated_resource_count -= 1;
1440 }
1441 }
1442
1443 self.bind_group = None;
1445
1446 self.free_slots.push(slot);
1448 self.live_allocation_count -= 1;
1449 }
1450
1451 fn gpu_resources_are_unchanged(
1459 &self,
1460 binding_resources: &UnpreparedBindingResources,
1461 bind_group_slot: MaterialBindGroupSlot,
1462 bindless_index_table: &MaterialBindlessIndexTable,
1463 ) -> bool {
1464 for (bindless_index, unprepared_binding_resource) in binding_resources.iter() {
1465 let bindless_index = BindlessIndex(*bindless_index);
1466 let binding_resource_id = BindingResourceId::from(unprepared_binding_resource);
1467
1468 match unprepared_binding_resource {
1469 UnpreparedBindingResource::Buffer(_)
1470 | UnpreparedBindingResource::ShaderBuffer(_) => {
1471 if bindless_index_table
1474 .get_binding(bind_group_slot, bindless_index)
1475 .is_some_and(|binding_index| {
1476 !self
1479 .buffers
1480 .get(&bindless_index)
1481 .is_some_and(|binding_array| {
1482 binding_array
1483 .slot_references(binding_index, binding_resource_id)
1484 })
1485 })
1486 {
1487 return false;
1488 }
1489 }
1490
1491 UnpreparedBindingResource::TextureView(texture_view_dimension, _) => {
1492 let bindless_resource_type =
1493 BindlessResourceType::from(*texture_view_dimension);
1494 if bindless_index_table
1497 .get_binding(bind_group_slot, bindless_index)
1498 .is_some_and(|binding_index| {
1499 !self.textures.get(&bindless_resource_type).is_some_and(
1502 |binding_array| {
1503 binding_array
1504 .slot_references(binding_index, binding_resource_id)
1505 },
1506 )
1507 })
1508 {
1509 return false;
1510 }
1511 }
1512
1513 UnpreparedBindingResource::Sampler(sampler_binding_type, _) => {
1514 let bindless_resource_type = BindlessResourceType::from(*sampler_binding_type);
1515 if bindless_index_table
1518 .get_binding(bind_group_slot, bindless_index)
1519 .is_some_and(|binding_index| {
1520 !self.samplers.get(&bindless_resource_type).is_some_and(
1523 |binding_array| {
1524 binding_array
1525 .slot_references(binding_index, binding_resource_id)
1526 },
1527 )
1528 })
1529 {
1530 return false;
1531 }
1532 }
1533
1534 UnpreparedBindingResource::Data(_) => {
1535 }
1537 }
1538 }
1539
1540 true
1541 }
1542
1543 fn update_data(
1553 &mut self,
1554 unprepared_bind_group: &BindGroupBuilder,
1555 bind_group_slot: MaterialBindGroupSlot,
1556 bindless_index_table_index: usize,
1557 ) {
1558 let bindless_index_table = &self.bindless_index_tables[bindless_index_table_index];
1559
1560 for (bindless_index, unprepared_binding_resource) in
1562 unprepared_bind_group.binding_resources.iter()
1563 {
1564 let bindless_index = BindlessIndex(*bindless_index);
1565
1566 match unprepared_binding_resource {
1567 UnpreparedBindingResource::Buffer(_)
1569 | UnpreparedBindingResource::ShaderBuffer(_)
1570 | UnpreparedBindingResource::TextureView(..)
1571 | UnpreparedBindingResource::Sampler(..) => {}
1572
1573 UnpreparedBindingResource::Data(data_range) => {
1574 let Some(binding_index) =
1577 bindless_index_table.get_binding(bind_group_slot, bindless_index)
1578 else {
1579 continue;
1580 };
1581
1582 self.data_buffers
1584 .get_mut(&bindless_index)
1585 .expect("Data buffer should exist if we're in the fast path")
1586 .set(
1587 binding_index,
1588 &unprepared_bind_group.data_buffer
1589 [(data_range.start as usize)..(data_range.end as usize)],
1590 );
1591 }
1592 }
1593 }
1594 }
1595
1596 fn prepare(
1598 &mut self,
1599 render_device: &RenderDevice,
1600 pipeline_cache: &PipelineCache,
1601 label: &'static str,
1602 bind_group_layout: &BindGroupLayoutDescriptor,
1603 fallback_bindless_resources: &FallbackBindlessResources,
1604 fallback_buffers: &HashMap<BindlessIndex, Buffer>,
1605 fallback_image: &FallbackImage,
1606 shader_buffer_assets: &RenderAssets<GpuShaderBuffer>,
1607 changed_shader_buffers: &RenderChangedShaderBuffers,
1608 bindless_descriptor: &BindlessDescriptor,
1609 slab_capacity: u32,
1610 ) {
1611 self.invalidate_bind_group_for_changed_shader_buffers_if_needed(changed_shader_buffers);
1612
1613 for bindless_index_table in &mut self.bindless_index_tables {
1615 bindless_index_table.buffer.prepare(render_device);
1616 }
1617
1618 for data_buffer in self.data_buffers.values_mut() {
1620 data_buffer.buffer.prepare(render_device);
1621 }
1622
1623 self.prepare_bind_group(
1625 render_device,
1626 pipeline_cache,
1627 label,
1628 bind_group_layout,
1629 fallback_bindless_resources,
1630 fallback_buffers,
1631 fallback_image,
1632 shader_buffer_assets,
1633 bindless_descriptor,
1634 slab_capacity,
1635 );
1636 }
1637
1638 fn prepare_bind_group(
1641 &mut self,
1642 render_device: &RenderDevice,
1643 pipeline_cache: &PipelineCache,
1644 label: &'static str,
1645 bind_group_layout: &BindGroupLayoutDescriptor,
1646 fallback_bindless_resources: &FallbackBindlessResources,
1647 fallback_buffers: &HashMap<BindlessIndex, Buffer>,
1648 fallback_image: &FallbackImage,
1649 shader_buffer_assets: &RenderAssets<GpuShaderBuffer>,
1650 bindless_descriptor: &BindlessDescriptor,
1651 slab_capacity: u32,
1652 ) {
1653 if self.bind_group.is_some() {
1655 return;
1656 }
1657
1658 let required_binding_array_size = if render_device
1661 .features()
1662 .contains(WgpuFeatures::PARTIALLY_BOUND_BINDING_ARRAY)
1663 {
1664 None
1665 } else {
1666 Some(slab_capacity)
1667 };
1668
1669 let binding_resource_arrays = self.create_binding_resource_arrays(
1670 fallback_bindless_resources,
1671 fallback_buffers,
1672 fallback_image,
1673 shader_buffer_assets,
1674 bindless_descriptor,
1675 required_binding_array_size,
1676 );
1677
1678 let mut bind_group_entries: Vec<_> = self
1679 .bindless_index_tables
1680 .iter()
1681 .map(|bindless_index_table| bindless_index_table.bind_group_entry())
1682 .collect();
1683
1684 for &(&binding, ref binding_resource_array) in binding_resource_arrays.iter() {
1685 bind_group_entries.push(BindGroupEntry {
1686 binding,
1687 resource: match *binding_resource_array {
1688 BindingResourceArray::Buffers(ref buffer_bindings) => {
1689 BindingResource::BufferArray(&buffer_bindings[..])
1690 }
1691 BindingResourceArray::TextureViews(ref texture_views) => {
1692 BindingResource::TextureViewArray(&texture_views[..])
1693 }
1694 BindingResourceArray::Samplers(ref samplers) => {
1695 BindingResource::SamplerArray(&samplers[..])
1696 }
1697 },
1698 });
1699 }
1700
1701 for data_buffer in self.data_buffers.values() {
1703 bind_group_entries.push(BindGroupEntry {
1704 binding: *data_buffer.binding_number,
1705 resource: data_buffer
1706 .buffer
1707 .buffer()
1708 .expect("Backing data buffer must have been uploaded by now")
1709 .as_entire_binding(),
1710 });
1711 }
1712
1713 self.bind_group = Some(render_device.create_bind_group(
1714 Some(label),
1715 &pipeline_cache.get_bind_group_layout(bind_group_layout),
1716 &bind_group_entries,
1717 ));
1718 }
1719
1720 fn invalidate_bind_group_for_changed_shader_buffers_if_needed(
1728 &mut self,
1729 changed_shader_buffers: &RenderChangedShaderBuffers,
1730 ) {
1731 if !changed_shader_buffers.is_empty()
1732 && self.bind_group.is_some()
1733 && self.shader_buffers.values().any(|buffer| {
1734 #[allow(non_exhaustive_omitted_patterns)] match buffer.resource_type {
BindlessResourceType::ShaderBuffer => true,
_ => false,
}matches!(buffer.resource_type, BindlessResourceType::ShaderBuffer)
1735 && buffer.resource_to_slot.keys().any(|binding_resource_id| {
1736 #[allow(non_exhaustive_omitted_patterns)] match *binding_resource_id {
BindingResourceId::ShaderBuffer(ref shader_buffer) if
changed_shader_buffers.contains(shader_buffer) => true,
_ => false,
}matches!(
1737 *binding_resource_id,
1738 BindingResourceId::ShaderBuffer(ref shader_buffer) if
1739 changed_shader_buffers.contains(shader_buffer)
1740 )
1741 })
1742 })
1743 {
1744 self.bind_group = None;
1745 }
1746 }
1747
1748 fn write_buffer(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
1753 for bindless_index_table in &mut self.bindless_index_tables {
1754 bindless_index_table
1755 .buffer
1756 .write(render_device, render_queue);
1757 }
1758
1759 for data_buffer in self.data_buffers.values_mut() {
1760 data_buffer.buffer.write(render_device, render_queue);
1761 }
1762 }
1763
1764 fn create_binding_resource_arrays<'a>(
1767 &'a self,
1768 fallback_bindless_resources: &'a FallbackBindlessResources,
1769 fallback_buffers: &'a HashMap<BindlessIndex, Buffer>,
1770 fallback_image: &'a FallbackImage,
1771 shader_buffer_assets: &'a RenderAssets<GpuShaderBuffer>,
1772 bindless_descriptor: &'a BindlessDescriptor,
1773 required_binding_array_size: Option<u32>,
1774 ) -> Vec<(&'a u32, BindingResourceArray<'a>)> {
1775 let mut binding_resource_arrays = ::alloc::vec::Vec::new()vec![];
1776
1777 self.create_sampler_binding_resource_arrays(
1779 &mut binding_resource_arrays,
1780 fallback_bindless_resources,
1781 bindless_descriptor,
1782 required_binding_array_size,
1783 );
1784
1785 self.create_texture_binding_resource_arrays(
1787 &mut binding_resource_arrays,
1788 fallback_image,
1789 bindless_descriptor,
1790 required_binding_array_size,
1791 );
1792
1793 self.create_buffer_binding_resource_arrays(
1795 &mut binding_resource_arrays,
1796 fallback_buffers,
1797 shader_buffer_assets,
1798 bindless_descriptor,
1799 required_binding_array_size,
1800 );
1801
1802 binding_resource_arrays
1803 }
1804
1805 fn create_sampler_binding_resource_arrays<'a, 'b>(
1808 &'a self,
1809 binding_resource_arrays: &'b mut Vec<(&'a u32, BindingResourceArray<'a>)>,
1810 fallback_bindless_resources: &'a FallbackBindlessResources,
1811 bindless_descriptor: &'a BindlessDescriptor,
1812 required_binding_array_size: Option<u32>,
1813 ) {
1814 for (bindless_resource_type, fallback_sampler) in [
1816 (
1817 BindlessResourceType::SamplerFiltering,
1818 &fallback_bindless_resources.filtering_sampler,
1819 ),
1820 (
1821 BindlessResourceType::SamplerNonFiltering,
1822 &fallback_bindless_resources.non_filtering_sampler,
1823 ),
1824 (
1825 BindlessResourceType::SamplerComparison,
1826 &fallback_bindless_resources.comparison_sampler,
1827 ),
1828 ] {
1829 if !bindless_descriptor
1831 .resources
1832 .contains(&bindless_resource_type)
1833 {
1834 continue;
1835 }
1836
1837 let mut sampler_bindings = ::alloc::vec::Vec::new()vec![];
1838
1839 match self.samplers.get(&bindless_resource_type) {
1840 Some(sampler_bindless_binding_array) => {
1841 for maybe_bindless_binding in sampler_bindless_binding_array.bindings.iter() {
1842 match *maybe_bindless_binding {
1843 Some(ref bindless_binding) => {
1844 sampler_bindings.push(&*bindless_binding.resource);
1845 }
1846 None => sampler_bindings.push(&**fallback_sampler),
1847 }
1848 }
1849 }
1850
1851 None => {
1852 sampler_bindings.push(&**fallback_sampler);
1854 }
1855 }
1856
1857 if let Some(required_binding_array_size) = required_binding_array_size {
1858 sampler_bindings.extend(iter::repeat_n(
1859 &**fallback_sampler,
1860 required_binding_array_size as usize - sampler_bindings.len(),
1861 ));
1862 }
1863
1864 let binding_number = bindless_resource_type
1865 .binding_number()
1866 .expect("Sampler bindless resource type must have a binding number");
1867
1868 binding_resource_arrays.push((
1869 &**binding_number,
1870 BindingResourceArray::Samplers(sampler_bindings),
1871 ));
1872 }
1873 }
1874
1875 fn create_texture_binding_resource_arrays<'a, 'b>(
1878 &'a self,
1879 binding_resource_arrays: &'b mut Vec<(&'a u32, BindingResourceArray<'a>)>,
1880 fallback_image: &'a FallbackImage,
1881 bindless_descriptor: &'a BindlessDescriptor,
1882 required_binding_array_size: Option<u32>,
1883 ) {
1884 for (bindless_resource_type, fallback_image) in [
1885 (BindlessResourceType::Texture1d, &fallback_image.d1),
1886 (BindlessResourceType::Texture2d, &fallback_image.d2),
1887 (
1888 BindlessResourceType::Texture2dArray,
1889 &fallback_image.d2_array,
1890 ),
1891 (BindlessResourceType::Texture3d, &fallback_image.d3),
1892 (BindlessResourceType::TextureCube, &fallback_image.cube),
1893 (
1894 BindlessResourceType::TextureCubeArray,
1895 &fallback_image.cube_array,
1896 ),
1897 ] {
1898 if !bindless_descriptor
1900 .resources
1901 .contains(&bindless_resource_type)
1902 {
1903 continue;
1904 }
1905
1906 let mut texture_bindings = ::alloc::vec::Vec::new()vec![];
1907
1908 let binding_number = bindless_resource_type
1909 .binding_number()
1910 .expect("Texture bindless resource type must have a binding number");
1911
1912 match self.textures.get(&bindless_resource_type) {
1913 Some(texture_bindless_binding_array) => {
1914 for maybe_bindless_binding in texture_bindless_binding_array.bindings.iter() {
1915 match *maybe_bindless_binding {
1916 Some(ref bindless_binding) => {
1917 texture_bindings.push(&*bindless_binding.resource);
1918 }
1919 None => texture_bindings.push(&*fallback_image.texture_view),
1920 }
1921 }
1922 }
1923
1924 None => {
1925 texture_bindings.push(&*fallback_image.texture_view);
1927 }
1928 }
1929
1930 if let Some(required_binding_array_size) = required_binding_array_size {
1931 texture_bindings.extend(iter::repeat_n(
1932 &*fallback_image.texture_view,
1933 required_binding_array_size as usize - texture_bindings.len(),
1934 ));
1935 }
1936
1937 binding_resource_arrays.push((
1938 binding_number,
1939 BindingResourceArray::TextureViews(texture_bindings),
1940 ));
1941 }
1942 }
1943
1944 fn create_buffer_binding_resource_arrays<'a, 'b>(
1947 &'a self,
1948 binding_resource_arrays: &'b mut Vec<(&'a u32, BindingResourceArray<'a>)>,
1949 fallback_buffers: &'a HashMap<BindlessIndex, Buffer>,
1950 shader_buffer_assets: &'a RenderAssets<GpuShaderBuffer>,
1951 bindless_descriptor: &'a BindlessDescriptor,
1952 required_binding_array_size: Option<u32>,
1953 ) {
1954 for bindless_buffer_descriptor in bindless_descriptor.buffers.iter() {
1955 if let Some(buffer_bindless_binding_array) =
1957 self.buffers.get(&bindless_buffer_descriptor.bindless_index)
1958 {
1959 let fallback_buffer = fallback_buffers
1960 .get(&bindless_buffer_descriptor.bindless_index)
1961 .expect("Fallback buffer should exist");
1962
1963 let mut buffer_bindings: Vec<_> = buffer_bindless_binding_array
1964 .bindings
1965 .iter()
1966 .map(|maybe_bindless_binding| {
1967 let buffer = match *maybe_bindless_binding {
1968 None => fallback_buffer,
1969 Some(ref bindless_binding) => &bindless_binding.resource,
1970 };
1971 BufferBinding {
1972 buffer,
1973 offset: 0,
1974 size: None,
1975 }
1976 })
1977 .collect();
1978
1979 if let Some(required_binding_array_size) = required_binding_array_size {
1980 buffer_bindings.extend(iter::repeat_n(
1981 BufferBinding {
1982 buffer: fallback_buffer,
1983 offset: 0,
1984 size: None,
1985 },
1986 required_binding_array_size as usize - buffer_bindings.len(),
1987 ));
1988 }
1989
1990 binding_resource_arrays.push((
1991 &*buffer_bindless_binding_array.binding_number,
1992 BindingResourceArray::Buffers(buffer_bindings),
1993 ));
1994
1995 continue;
1996 }
1997
1998 if let Some(shader_buffer_bindless_binding_array) = self
2001 .shader_buffers
2002 .get(&bindless_buffer_descriptor.bindless_index)
2003 {
2004 let fallback_buffer = fallback_buffers
2005 .get(&bindless_buffer_descriptor.bindless_index)
2006 .expect("Fallback buffer should exist");
2007
2008 let mut buffer_bindings: Vec<_> = shader_buffer_bindless_binding_array
2009 .bindings
2010 .iter()
2011 .map(|maybe_bindless_binding| {
2012 let buffer =
2013 match maybe_bindless_binding
2014 .as_ref()
2015 .and_then(|bindless_binding| {
2016 shader_buffer_assets.get(bindless_binding.resource.id())
2017 }) {
2018 None => fallback_buffer,
2019 Some(gpu_shader_buffer) => &gpu_shader_buffer.buffer,
2020 };
2021 BufferBinding {
2022 buffer,
2023 offset: 0,
2024 size: None,
2025 }
2026 })
2027 .collect();
2028
2029 if let Some(required_binding_array_size) = required_binding_array_size {
2030 buffer_bindings.extend(iter::repeat_n(
2031 BufferBinding {
2032 buffer: fallback_buffer,
2033 offset: 0,
2034 size: None,
2035 },
2036 required_binding_array_size as usize - buffer_bindings.len(),
2037 ));
2038 }
2039
2040 binding_resource_arrays.push((
2041 &*shader_buffer_bindless_binding_array.binding_number,
2042 BindingResourceArray::Buffers(buffer_bindings),
2043 ));
2044
2045 continue;
2046 };
2047
2048 }
2053 }
2054
2055 fn bind_group(&self) -> Option<&BindGroup> {
2058 self.bind_group.as_ref()
2059 }
2060
2061 fn get_bindless_index_table(
2063 &self,
2064 bindless_index: BindlessIndex,
2065 ) -> Option<&MaterialBindlessIndexTable> {
2066 let table_index = self
2067 .bindless_index_tables
2068 .binary_search_by(|bindless_index_table| {
2069 if bindless_index_table.index_range.end <= bindless_index {
2070 Ordering::Less
2071 } else if bindless_index_table.index_range.start > bindless_index {
2072 Ordering::Greater
2073 } else {
2074 Ordering::Equal
2075 }
2076 })
2077 .ok()?;
2078 self.bindless_index_tables.get(table_index)
2079 }
2080}
2081
2082impl<R> MaterialBindlessBindingArray<R>
2083where
2084 R: GetBindingResourceId,
2085{
2086 fn new(
2089 binding_number: BindingNumber,
2090 resource_type: BindlessResourceType,
2091 ) -> MaterialBindlessBindingArray<R> {
2092 MaterialBindlessBindingArray {
2093 binding_number,
2094 bindings: ::alloc::vec::Vec::new()vec![],
2095 resource_type,
2096 resource_to_slot: HashMap::default(),
2097 free_slots: ::alloc::vec::Vec::new()vec![],
2098 len: 0,
2099 }
2100 }
2101
2102 fn find(&self, binding_resource_id: BindingResourceId) -> Option<u32> {
2107 self.resource_to_slot.get(&binding_resource_id).copied()
2108 }
2109
2110 fn slot_references(&self, slot: u32, binding_resource_id: BindingResourceId) -> bool {
2113 self.bindings
2114 .get(slot as usize)
2115 .is_some_and(|maybe_binding| {
2116 maybe_binding.as_ref().is_some_and(|binding| {
2117 binding.resource.binding_resource_id(self.resource_type) == binding_resource_id
2118 })
2119 })
2120 }
2121
2122 fn insert(&mut self, binding_resource_id: BindingResourceId, resource: R) -> u32 {
2125 match self.resource_to_slot.entry(binding_resource_id) {
2126 Entry::Occupied(o) => {
2127 let slot = *o.get();
2128
2129 self.bindings[slot as usize]
2130 .as_mut()
2131 .expect("A slot in the resource_to_slot map should have a value")
2132 .ref_count += 1;
2133
2134 slot
2135 }
2136 Entry::Vacant(v) => {
2137 let slot = self.free_slots.pop().unwrap_or(self.len);
2138 v.insert(slot);
2139
2140 if self.bindings.len() < slot as usize + 1 {
2141 self.bindings.resize_with(slot as usize + 1, || None);
2142 }
2143 if true {
if !self.bindings[slot as usize].is_none() {
::core::panicking::panic("assertion failed: self.bindings[slot as usize].is_none()")
};
};debug_assert!(self.bindings[slot as usize].is_none());
2144 self.bindings[slot as usize] = Some(MaterialBindlessBinding::new(resource));
2145
2146 self.len += 1;
2147 slot
2148 }
2149 }
2150 }
2151
2152 fn remove(&mut self, slot: u32) -> bool {
2158 let maybe_binding = &mut self.bindings[slot as usize];
2159 let binding = maybe_binding
2160 .as_mut()
2161 .expect("Attempted to free an already-freed binding");
2162
2163 binding.ref_count -= 1;
2164 if binding.ref_count != 0 {
2165 return false;
2166 }
2167
2168 let binding_resource_id = binding.resource.binding_resource_id(self.resource_type);
2169 self.resource_to_slot.remove(&binding_resource_id);
2170
2171 *maybe_binding = None;
2172 self.free_slots.push(slot);
2173 self.len -= 1;
2174 true
2175 }
2176}
2177
2178impl<R> MaterialBindlessBinding<R>
2179where
2180 R: GetBindingResourceId,
2181{
2182 fn new(resource: R) -> MaterialBindlessBinding<R> {
2186 MaterialBindlessBinding {
2187 resource,
2188 ref_count: 1,
2189 }
2190 }
2191}
2192
2193pub fn material_uses_bindless_resources<M>(render_device: &RenderDevice) -> bool
2199where
2200 M: AsBindGroup,
2201{
2202 M::bindless_slot_count().is_some_and(|bindless_slot_count| {
2203 M::bindless_supported(render_device) && bindless_slot_count.resolve() > 1
2204 })
2205}
2206
2207impl MaterialBindlessSlab {
2208 fn new(bindless_descriptor: &BindlessDescriptor) -> MaterialBindlessSlab {
2213 let mut buffers = HashMap::default();
2214 let mut samplers = HashMap::default();
2215 let mut textures = HashMap::default();
2216 let mut data_buffers = HashMap::default();
2217 let mut shader_buffers = HashMap::default();
2218
2219 for (bindless_index, bindless_resource_type) in
2220 bindless_descriptor.resources.iter().enumerate()
2221 {
2222 let bindless_index = BindlessIndex(bindless_index as u32);
2223 match *bindless_resource_type {
2224 BindlessResourceType::None => {}
2225 BindlessResourceType::Buffer => {
2226 let binding_number = bindless_descriptor
2227 .buffers
2228 .iter()
2229 .find(|bindless_buffer_descriptor| {
2230 bindless_buffer_descriptor.bindless_index == bindless_index
2231 })
2232 .expect(
2233 "Bindless buffer descriptor matching that bindless index should be \
2234 present",
2235 )
2236 .binding_number;
2237 buffers.insert(
2238 bindless_index,
2239 MaterialBindlessBindingArray::new(binding_number, *bindless_resource_type),
2240 );
2241 }
2242 BindlessResourceType::ShaderBuffer => {
2243 let binding_number = bindless_descriptor
2244 .buffers
2245 .iter()
2246 .find(|bindless_buffer_descriptor| {
2247 bindless_buffer_descriptor.bindless_index == bindless_index
2248 })
2249 .expect(
2250 "Bindless buffer descriptor matching that bindless index should be \
2251 present",
2252 )
2253 .binding_number;
2254 shader_buffers.insert(
2255 bindless_index,
2256 MaterialBindlessBindingArray::new(binding_number, *bindless_resource_type),
2257 );
2258 }
2259 BindlessResourceType::DataBuffer => {
2260 let buffer_descriptor = bindless_descriptor
2262 .buffers
2263 .iter()
2264 .find(|bindless_buffer_descriptor| {
2265 bindless_buffer_descriptor.bindless_index == bindless_index
2266 })
2267 .expect(
2268 "Bindless buffer descriptor matching that bindless index should be \
2269 present",
2270 );
2271 data_buffers.insert(
2272 bindless_index,
2273 MaterialDataBuffer::new(
2274 buffer_descriptor.binding_number,
2275 buffer_descriptor
2276 .size
2277 .expect("Data buffers should have a size")
2278 as u32,
2279 ),
2280 );
2281 }
2282 BindlessResourceType::SamplerFiltering
2283 | BindlessResourceType::SamplerNonFiltering
2284 | BindlessResourceType::SamplerComparison => {
2285 samplers.insert(
2286 *bindless_resource_type,
2287 MaterialBindlessBindingArray::new(
2288 *bindless_resource_type.binding_number().unwrap(),
2289 *bindless_resource_type,
2290 ),
2291 );
2292 }
2293 BindlessResourceType::Texture1d
2294 | BindlessResourceType::Texture2d
2295 | BindlessResourceType::Texture2dArray
2296 | BindlessResourceType::Texture3d
2297 | BindlessResourceType::TextureCube
2298 | BindlessResourceType::TextureCubeArray => {
2299 textures.insert(
2300 *bindless_resource_type,
2301 MaterialBindlessBindingArray::new(
2302 *bindless_resource_type.binding_number().unwrap(),
2303 *bindless_resource_type,
2304 ),
2305 );
2306 }
2307 }
2308 }
2309
2310 let bindless_index_tables = bindless_descriptor
2311 .index_tables
2312 .iter()
2313 .map(MaterialBindlessIndexTable::new)
2314 .collect();
2315
2316 MaterialBindlessSlab {
2317 bind_group: None,
2318 bindless_index_tables,
2319 samplers,
2320 textures,
2321 buffers,
2322 shader_buffers,
2323 data_buffers,
2324 free_slots: ::alloc::vec::Vec::new()vec![],
2325 live_allocation_count: 0,
2326 allocated_resource_count: 0,
2327 }
2328 }
2329}
2330
2331fn init_fallback_resources(mut commands: Commands, render_device: Res<RenderDevice>) {
2333 commands.insert_resource(FallbackBindlessResources {
2335 filtering_sampler: render_device.create_sampler(&SamplerDescriptor {
2336 label: Some("fallback filtering sampler"),
2337 ..default()
2338 }),
2339 non_filtering_sampler: render_device.create_sampler(&SamplerDescriptor {
2340 label: Some("fallback non-filtering sampler"),
2341 mag_filter: FilterMode::Nearest,
2342 min_filter: FilterMode::Nearest,
2343 mipmap_filter: MipmapFilterMode::Nearest,
2344 ..default()
2345 }),
2346 comparison_sampler: render_device.create_sampler(&SamplerDescriptor {
2347 label: Some("fallback comparison sampler"),
2348 compare: Some(CompareFunction::Always),
2349 ..default()
2350 }),
2351 });
2352
2353 commands.insert_resource(FallbackBuffer(render_device.create_buffer(
2355 &BufferDescriptor {
2356 label: Some("fallback buffer"),
2357 size: 1,
2358 usage: BufferUsages::COPY_SRC
2359 | BufferUsages::COPY_DST
2360 | BufferUsages::STORAGE
2361 | BufferUsages::UNIFORM,
2362 mapped_at_creation: false,
2363 },
2364 )));
2365}
2366
2367impl MaterialBindGroupNonBindlessAllocator {
2368 fn new(label: &'static str) -> MaterialBindGroupNonBindlessAllocator {
2371 MaterialBindGroupNonBindlessAllocator {
2372 label,
2373 bind_groups: ::alloc::vec::Vec::new()vec![],
2374 to_prepare: HashSet::default(),
2375 free_indices: ::alloc::vec::Vec::new()vec![],
2376 }
2377 }
2378
2379 fn allocate(&mut self, bind_group: MaterialNonBindlessAllocatedBindGroup) -> MaterialBindingId {
2385 let group_id = self
2386 .free_indices
2387 .pop()
2388 .unwrap_or(MaterialBindGroupIndex(self.bind_groups.len() as u32));
2389 if self.bind_groups.len() < *group_id as usize + 1 {
2390 self.bind_groups
2391 .resize_with(*group_id as usize + 1, || None);
2392 }
2393
2394 if #[allow(non_exhaustive_omitted_patterns)] match bind_group {
MaterialNonBindlessAllocatedBindGroup::Unprepared { .. } => true,
_ => false,
}matches!(
2395 bind_group,
2396 MaterialNonBindlessAllocatedBindGroup::Unprepared { .. }
2397 ) {
2398 self.to_prepare.insert(group_id);
2399 }
2400
2401 self.bind_groups[*group_id as usize] = Some(bind_group);
2402
2403 MaterialBindingId {
2404 group: group_id,
2405 slot: default(),
2406 }
2407 }
2408
2409 fn allocate_unprepared(
2412 &mut self,
2413 unprepared_bind_group: &mut BindGroupBuilder,
2414 bind_group_layout: BindGroupLayoutDescriptor,
2415 ) -> MaterialBindingId {
2416 self.allocate(MaterialNonBindlessAllocatedBindGroup::Unprepared {
2417 bind_group: mem::take(unprepared_bind_group),
2425 layout: bind_group_layout,
2426 })
2427 }
2428
2429 fn allocate_prepared(
2432 &mut self,
2433 prepared_bind_group: PreparedBindGroup,
2434 bind_group_layout: BindGroupLayoutDescriptor,
2435 ) -> MaterialBindingId {
2436 self.allocate(MaterialNonBindlessAllocatedBindGroup::Prepared {
2437 bind_group: prepared_bind_group,
2438 layout: bind_group_layout,
2439 uniform_buffers: ::alloc::vec::Vec::new()vec![],
2440 })
2441 }
2442
2443 fn free(&mut self, binding_id: MaterialBindingId) {
2445 if true {
{
match (&binding_id.slot, &MaterialBindGroupSlot(0)) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};
};debug_assert_eq!(binding_id.slot, MaterialBindGroupSlot(0));
2446 if true {
if !self.bind_groups[*binding_id.group as usize].is_some() {
::core::panicking::panic("assertion failed: self.bind_groups[*binding_id.group as usize].is_some()")
};
};debug_assert!(self.bind_groups[*binding_id.group as usize].is_some());
2447 self.bind_groups[*binding_id.group as usize] = None;
2448 self.to_prepare.remove(&binding_id.group);
2449 self.free_indices.push(binding_id.group);
2450 }
2451
2452 fn get(&self, group: MaterialBindGroupIndex) -> Option<MaterialNonBindlessSlab<'_>> {
2454 self.bind_groups[group.0 as usize]
2455 .as_ref()
2456 .map(|bind_group| match bind_group {
2457 MaterialNonBindlessAllocatedBindGroup::Prepared { bind_group, .. } => {
2458 MaterialNonBindlessSlab::Prepared(bind_group)
2459 }
2460 MaterialNonBindlessAllocatedBindGroup::Unprepared { .. } => {
2461 MaterialNonBindlessSlab::Unprepared
2462 }
2463 })
2464 }
2465
2466 fn prepare_bind_groups(
2473 &mut self,
2474 render_device: &RenderDevice,
2475 pipeline_cache: &PipelineCache,
2476 fallback_buffer: &FallbackBuffer,
2477 shader_buffer_assets: &RenderAssets<GpuShaderBuffer>,
2478 changed_shader_buffers: &RenderChangedShaderBuffers,
2479 ) {
2480 self.invalidate_bind_group_for_changed_shader_buffers_if_needed(changed_shader_buffers);
2481
2482 for bind_group_index in mem::take(&mut self.to_prepare) {
2483 let Some(MaterialNonBindlessAllocatedBindGroup::Unprepared {
2484 bind_group: unprepared_bind_group,
2485 layout: bind_group_layout,
2486 }) = mem::take(&mut self.bind_groups[*bind_group_index as usize])
2487 else {
2488 {
::core::panicking::panic_fmt(format_args!("Allocation didn\'t exist or was already prepared"));
};panic!("Allocation didn't exist or was already prepared");
2489 };
2490
2491 let mut uniform_buffers = ::alloc::vec::Vec::new()vec![];
2493 for (index, binding) in unprepared_bind_group.binding_resources.iter() {
2494 let UnpreparedBindingResource::Data(ref data_range) = *binding else {
2495 continue;
2496 };
2497 let label = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("material uniform data {0}",
*index))
})format!("material uniform data {}", *index);
2498 let uniform_buffer = render_device.create_buffer_with_data(&BufferInitDescriptor {
2499 label: Some(&label),
2500 contents: &unprepared_bind_group.data_buffer
2501 [(data_range.start as usize)..(data_range.end as usize)],
2502 usage: BufferUsages::COPY_DST | BufferUsages::UNIFORM,
2503 });
2504 uniform_buffers.push(uniform_buffer);
2505 }
2506
2507 let mut bind_group_entries = ::alloc::vec::Vec::new()vec![];
2509 let mut uniform_buffers_iter = uniform_buffers.iter();
2510 for (index, binding) in unprepared_bind_group.binding_resources.iter() {
2511 match *binding {
2512 UnpreparedBindingResource::Data(_) => {
2513 bind_group_entries.push(BindGroupEntry {
2514 binding: *index,
2515 resource: uniform_buffers_iter
2516 .next()
2517 .expect("We should have created uniform buffers for each `Data`")
2518 .as_entire_binding(),
2519 });
2520 }
2521 _ => bind_group_entries.push(BindGroupEntry {
2522 binding: *index,
2523 resource: binding.get_binding(fallback_buffer, shader_buffer_assets),
2524 }),
2525 }
2526 }
2527
2528 let bind_group = render_device.create_bind_group(
2530 self.label,
2531 &pipeline_cache.get_bind_group_layout(&bind_group_layout),
2532 &bind_group_entries,
2533 );
2534
2535 self.bind_groups[*bind_group_index as usize] =
2536 Some(MaterialNonBindlessAllocatedBindGroup::Prepared {
2537 bind_group: PreparedBindGroup {
2538 bindings: unprepared_bind_group.into_binding_resources(),
2539 bind_group,
2540 },
2541 layout: bind_group_layout,
2542 uniform_buffers,
2543 });
2544 }
2545 }
2546
2547 fn invalidate_bind_group_for_changed_shader_buffers_if_needed(
2550 &mut self,
2551 changed_shader_buffers: &RenderChangedShaderBuffers,
2552 ) {
2553 if changed_shader_buffers.is_empty() {
2556 return;
2557 }
2558
2559 for (allocated_bind_group_index, maybe_allocated_bind_group) in
2561 self.bind_groups.iter_mut().enumerate()
2562 {
2563 let Some(MaterialNonBindlessAllocatedBindGroup::Prepared {
2567 bind_group: ref mut prepared_bind_group,
2568 ref mut layout,
2569 ..
2570 }) = *maybe_allocated_bind_group
2571 else {
2572 continue;
2573 };
2574
2575 if prepared_bind_group
2578 .bindings
2579 .iter()
2580 .all(|(_, binding_resource)| match *binding_resource {
2581 OwnedBindingResource::ShaderBuffer(ref shader_buffer) => {
2582 !changed_shader_buffers.contains(&shader_buffer.id())
2583 }
2584 _ => true,
2585 })
2586 {
2587 continue;
2588 }
2589
2590 let layout = mem::take(layout);
2595 *maybe_allocated_bind_group = Some(MaterialNonBindlessAllocatedBindGroup::Unprepared {
2596 bind_group: prepared_bind_group.unprepare(),
2597 layout,
2598 });
2599 self.to_prepare
2600 .insert(MaterialBindGroupIndex(allocated_bind_group_index as u32));
2601 }
2602 }
2603}
2604
2605impl<'a> MaterialSlab<'a> {
2606 pub fn bind_group(&self) -> Option<&'a BindGroup> {
2613 match self.0 {
2614 MaterialSlabImpl::Bindless(material_bindless_slab) => {
2615 material_bindless_slab.bind_group()
2616 }
2617 MaterialSlabImpl::NonBindless(MaterialNonBindlessSlab::Prepared(
2618 prepared_bind_group,
2619 )) => Some(&prepared_bind_group.bind_group),
2620 MaterialSlabImpl::NonBindless(MaterialNonBindlessSlab::Unprepared) => None,
2621 }
2622 }
2623}
2624
2625impl MaterialDataBuffer {
2626 fn new(binding_number: BindingNumber, aligned_element_size: u32) -> MaterialDataBuffer {
2630 MaterialDataBuffer {
2631 binding_number,
2632 buffer: RetainedRawBufferVec::new(BufferUsages::STORAGE),
2633 aligned_element_size,
2634 free_slots: ::alloc::vec::Vec::new()vec![],
2635 len: 0,
2636 }
2637 }
2638
2639 fn insert(&mut self, data: &[u8]) -> u32 {
2645 if true {
{
match (&data.len(), &(self.aligned_element_size as usize)) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};
};debug_assert_eq!(data.len(), self.aligned_element_size as usize);
2647
2648 let slot = self.free_slots.pop().unwrap_or(self.len);
2650
2651 let start = slot as usize * self.aligned_element_size as usize;
2653 let end = (slot as usize + 1) * self.aligned_element_size as usize;
2654
2655 if self.buffer.len() < end {
2657 self.buffer.reserve_internal(end);
2658 }
2659 while self.buffer.values().len() < end {
2660 self.buffer.push(0);
2661 }
2662
2663 self.buffer.values_mut()[start..end].copy_from_slice(data);
2665
2666 self.len += 1;
2668 self.buffer.dirty = BufferDirtyState::NeedsReserve;
2669 slot
2670 }
2671
2672 fn set(&mut self, slot: u32, data: &[u8]) {
2674 if true {
{
match (&data.len(), &(self.aligned_element_size as usize)) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val,
&*right_val, ::core::option::Option::None);
}
}
}
};
};debug_assert_eq!(data.len(), self.aligned_element_size as usize);
2676
2677 let start = slot as usize * self.aligned_element_size as usize;
2679 let end = (slot as usize + 1) * self.aligned_element_size as usize;
2680
2681 self.buffer.values_mut()[start..end].copy_from_slice(data);
2682
2683 if self.buffer.dirty == BufferDirtyState::Clean {
2684 self.buffer.dirty = BufferDirtyState::NeedsUpload;
2685 }
2686 }
2687
2688 fn remove(&mut self, slot: u32) {
2690 self.free_slots.push(slot);
2691 self.len -= 1;
2692 }
2693}
2694
2695pub fn prepare_material_bind_groups(
2698 mut allocators: ResMut<MaterialBindGroupAllocators>,
2699 render_device: Res<RenderDevice>,
2700 pipeline_cache: Res<PipelineCache>,
2701 fallback_image: Res<FallbackImage>,
2702 fallback_buffer: Res<FallbackBuffer>,
2703 fallback_resources: Res<FallbackBindlessResources>,
2704 shader_buffer_assets: Res<RenderAssets<GpuShaderBuffer>>,
2705 changed_shader_buffers: Res<RenderChangedShaderBuffers>,
2706) {
2707 for (_, allocator) in allocators.iter_mut() {
2708 allocator.prepare_bind_groups(
2709 &render_device,
2710 &pipeline_cache,
2711 &fallback_resources,
2712 &fallback_image,
2713 &fallback_buffer,
2714 &shader_buffer_assets,
2715 &changed_shader_buffers,
2716 );
2717 }
2718}
2719
2720pub fn write_material_bind_group_buffers(
2726 mut allocators: ResMut<MaterialBindGroupAllocators>,
2727 render_device: Res<RenderDevice>,
2728 render_queue: Res<RenderQueue>,
2729) {
2730 for (_, allocator) in allocators.iter_mut() {
2731 allocator.write_buffers(&render_device, &render_queue);
2732 }
2733}
2734
2735impl MaterialBindGroupAllocators {
2736 pub fn add<M>(&mut self, render_device: &RenderDevice)
2739 where
2740 M: AsBindGroup + 'static,
2741 {
2742 self.insert(
2743 TypeId::of::<M>(),
2744 MaterialBindGroupAllocator::new(
2745 render_device,
2746 M::label(),
2747 material_uses_bindless_resources::<M>(render_device)
2748 .then(|| M::bindless_descriptor())
2749 .flatten(),
2750 M::bind_group_layout_descriptor(render_device),
2751 M::bindless_slot_count(),
2752 ),
2753 );
2754 }
2755}
2756
2757impl RenderMaterialBindings {
2758 pub fn prepare_material<M>(
2763 &mut self,
2764 material: &M,
2765 material_id: AssetId<M>,
2766 material_param: &mut SystemParamItem<'_, '_, M::Param>,
2767 material_layout: &BindGroupLayoutDescriptor,
2768 bind_group_allocators: &mut MaterialBindGroupAllocators,
2769 render_device: &RenderDevice,
2770 pipeline_cache: &PipelineCache,
2771 fallback_buffer: &FallbackBuffer,
2772 shader_buffer_assets: &RenderAssets<GpuShaderBuffer>,
2773 ) -> Result<MaterialBindingId, PrepareAssetError<M>>
2774 where
2775 M: AsBindGroup + Asset + Clone,
2776 {
2777 let actual_material_layout = pipeline_cache.get_bind_group_layout(material_layout);
2778
2779 let result = match material.build_bind_group(
2781 &actual_material_layout,
2782 render_device,
2783 material_param,
2784 false,
2785 &mut self.bind_group_builder,
2786 ) {
2787 Ok(()) => {
2788 let bind_group_allocator =
2789 bind_group_allocators.get_mut(&TypeId::of::<M>()).unwrap();
2790 match self.bindings.entry(material_id.into()) {
2792 Entry::Occupied(mut occupied_entry) => {
2793 let old_binding = *occupied_entry.get();
2798 if bind_group_allocator
2799 .try_update_data(old_binding, &mut self.bind_group_builder)
2800 {
2801 Ok(old_binding)
2802 } else {
2803 bind_group_allocator.free(old_binding);
2806 let new_binding = bind_group_allocator
2807 .allocate_unprepared(&mut self.bind_group_builder, material_layout);
2808 *occupied_entry.get_mut() = new_binding;
2809 Ok(new_binding)
2810 }
2811 }
2812 Entry::Vacant(vacant_entry) => Ok(*vacant_entry.insert(
2813 bind_group_allocator
2814 .allocate_unprepared(&mut self.bind_group_builder, material_layout),
2815 )),
2816 }
2817 }
2818 Err(AsBindGroupError::RetryNextUpdate) => {
2819 Err(PrepareAssetError::RetryNextUpdate((*material).clone()))
2820 }
2821 Err(AsBindGroupError::CreateBindGroupDirectly) => {
2822 match material.as_bind_group(
2823 material_layout,
2824 render_device,
2825 pipeline_cache,
2826 fallback_buffer,
2827 shader_buffer_assets,
2828 material_param,
2829 ) {
2830 Ok(prepared_bind_group) => {
2831 let bind_group_allocator =
2832 bind_group_allocators.get_mut(&TypeId::of::<M>()).unwrap();
2833 let material_binding_id = bind_group_allocator
2835 .allocate_prepared(prepared_bind_group, material_layout.clone());
2836 self.insert(material_id.into(), material_binding_id);
2837 Ok(material_binding_id)
2838 }
2839 Err(AsBindGroupError::RetryNextUpdate) => {
2840 Err(PrepareAssetError::RetryNextUpdate((*material).clone()))
2841 }
2842 Err(other) => Err(PrepareAssetError::AsBindGroupError(other)),
2843 }
2844 }
2845 Err(other) => Err(PrepareAssetError::AsBindGroupError(other)),
2846 };
2847
2848 self.bind_group_builder.clear();
2851
2852 result
2853 }
2854
2855 pub fn unload_material<M>(
2857 &mut self,
2858 source_asset: AssetId<M>,
2859 bind_group_allocators: &mut MaterialBindGroupAllocators,
2860 ) where
2861 M: Asset + AsBindGroup + Clone,
2862 {
2863 if let Some(material_binding_id) = self.remove(&source_asset.untyped()) {
2864 let bind_group_allocator = bind_group_allocators.get_mut(&TypeId::of::<M>()).unwrap();
2865 bind_group_allocator.free(material_binding_id);
2866 }
2867 }
2868}
2869
2870#[cfg(test)]
2871mod tests {
2872 use super::*;
2873 use crate::render_resource::{BindingNumber, BindlessIndexTableDescriptor};
2874
2875 #[test]
2876 fn get_bindless_index_table() {
2877 let descriptor = BindlessDescriptor {
2878 resources: Default::default(),
2879 buffers: Default::default(),
2880 index_tables: vec![
2881 BindlessIndexTableDescriptor {
2882 indices: BindlessIndex(1)..BindlessIndex(2),
2883 binding_number: BindingNumber(0),
2884 },
2885 BindlessIndexTableDescriptor {
2886 indices: BindlessIndex(2)..BindlessIndex(3),
2887 binding_number: BindingNumber(1),
2888 },
2889 BindlessIndexTableDescriptor {
2890 indices: BindlessIndex(4)..BindlessIndex(5),
2891 binding_number: BindingNumber(2),
2892 },
2893 ]
2894 .into(),
2895 };
2896 let slab = MaterialBindlessSlab::new(&descriptor);
2897
2898 assert!(slab.get_bindless_index_table(BindlessIndex(0)).is_none());
2900 assert!(slab.get_bindless_index_table(BindlessIndex(5)).is_none());
2901
2902 assert!(slab.get_bindless_index_table(BindlessIndex(3)).is_none());
2904
2905 let table = slab.get_bindless_index_table(BindlessIndex(2)).unwrap();
2907 assert_eq!(table.binding_number, BindingNumber(1));
2908 }
2909}