use alloc::borrow::Cow;
use core::{
num::{NonZeroU32, NonZeroU64},
ops::Range,
};
use bevy_derive::{Deref, DerefMut};
use wgpu::{
BindGroupLayoutEntry, SamplerBindingType, ShaderStages, TextureSampleType, TextureViewDimension,
};
use crate::render_resource::binding_types::storage_buffer_read_only_sized;
use super::binding_types::{
sampler, texture_1d, texture_2d, texture_2d_array, texture_3d, texture_cube, texture_cube_array,
};
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub const AUTO_BINDLESS_SLAB_RESOURCE_LIMIT: u32 = 64;
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
pub const AUTO_BINDLESS_SLAB_RESOURCE_LIMIT: u32 = 2048;
pub static BINDING_NUMBERS: [(BindlessResourceType, BindingNumber); 9] = [
(BindlessResourceType::SamplerFiltering, BindingNumber(1)),
(BindlessResourceType::SamplerNonFiltering, BindingNumber(2)),
(BindlessResourceType::SamplerComparison, BindingNumber(3)),
(BindlessResourceType::Texture1d, BindingNumber(4)),
(BindlessResourceType::Texture2d, BindingNumber(5)),
(BindlessResourceType::Texture2dArray, BindingNumber(6)),
(BindlessResourceType::Texture3d, BindingNumber(7)),
(BindlessResourceType::TextureCube, BindingNumber(8)),
(BindlessResourceType::TextureCubeArray, BindingNumber(9)),
];
#[derive(Clone, Copy, Default, PartialEq, Debug)]
pub enum BindlessSlabResourceLimit {
#[default]
Auto,
Custom(u32),
}
pub struct BindlessDescriptor {
pub resources: Cow<'static, [BindlessResourceType]>,
pub buffers: Cow<'static, [BindlessBufferDescriptor]>,
pub index_tables: Cow<'static, [BindlessIndexTableDescriptor]>,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum BindlessResourceType {
None,
Buffer,
SamplerFiltering,
SamplerNonFiltering,
SamplerComparison,
Texture1d,
Texture2d,
Texture2dArray,
Texture3d,
TextureCube,
TextureCubeArray,
DataBuffer,
}
#[derive(Clone, Copy, Debug)]
pub struct BindlessBufferDescriptor {
pub binding_number: BindingNumber,
pub bindless_index: BindlessIndex,
pub size: Option<usize>,
}
#[derive(Clone)]
pub struct BindlessIndexTableDescriptor {
pub indices: Range<BindlessIndex>,
pub binding_number: BindingNumber,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Deref, DerefMut)]
pub struct BindingNumber(pub u32);
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Hash, Debug, Deref, DerefMut)]
pub struct BindlessIndex(pub u32);
pub fn create_bindless_bind_group_layout_entries(
bindless_index_table_length: u32,
bindless_slab_resource_limit: u32,
bindless_index_table_binding_number: BindingNumber,
) -> Vec<BindGroupLayoutEntry> {
let bindless_slab_resource_limit =
NonZeroU32::new(bindless_slab_resource_limit).expect("Bindless slot count must be nonzero");
vec![
storage_buffer_read_only_sized(
false,
NonZeroU64::new(bindless_index_table_length as u64 * size_of::<u32>() as u64),
)
.build(
*bindless_index_table_binding_number,
ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,
),
sampler(SamplerBindingType::Filtering)
.count(bindless_slab_resource_limit)
.build(
1,
ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,
),
sampler(SamplerBindingType::NonFiltering)
.count(bindless_slab_resource_limit)
.build(
2,
ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,
),
sampler(SamplerBindingType::Comparison)
.count(bindless_slab_resource_limit)
.build(
3,
ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,
),
texture_1d(TextureSampleType::Float { filterable: true })
.count(bindless_slab_resource_limit)
.build(
4,
ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,
),
texture_2d(TextureSampleType::Float { filterable: true })
.count(bindless_slab_resource_limit)
.build(
5,
ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,
),
texture_2d_array(TextureSampleType::Float { filterable: true })
.count(bindless_slab_resource_limit)
.build(
6,
ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,
),
texture_3d(TextureSampleType::Float { filterable: true })
.count(bindless_slab_resource_limit)
.build(
7,
ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,
),
texture_cube(TextureSampleType::Float { filterable: true })
.count(bindless_slab_resource_limit)
.build(
8,
ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,
),
texture_cube_array(TextureSampleType::Float { filterable: true })
.count(bindless_slab_resource_limit)
.build(
9,
ShaderStages::FRAGMENT | ShaderStages::VERTEX | ShaderStages::COMPUTE,
),
]
}
impl BindlessSlabResourceLimit {
pub fn resolve(&self) -> u32 {
match *self {
BindlessSlabResourceLimit::Auto => AUTO_BINDLESS_SLAB_RESOURCE_LIMIT,
BindlessSlabResourceLimit::Custom(limit) => limit,
}
}
}
impl BindlessResourceType {
pub fn binding_number(&self) -> Option<&'static BindingNumber> {
match BINDING_NUMBERS.binary_search_by_key(self, |(key, _)| *key) {
Ok(binding_number) => Some(&BINDING_NUMBERS[binding_number].1),
Err(_) => None,
}
}
}
impl From<TextureViewDimension> for BindlessResourceType {
fn from(texture_view_dimension: TextureViewDimension) -> Self {
match texture_view_dimension {
TextureViewDimension::D1 => BindlessResourceType::Texture1d,
TextureViewDimension::D2 => BindlessResourceType::Texture2d,
TextureViewDimension::D2Array => BindlessResourceType::Texture2dArray,
TextureViewDimension::Cube => BindlessResourceType::TextureCube,
TextureViewDimension::CubeArray => BindlessResourceType::TextureCubeArray,
TextureViewDimension::D3 => BindlessResourceType::Texture3d,
}
}
}
impl From<SamplerBindingType> for BindlessResourceType {
fn from(sampler_binding_type: SamplerBindingType) -> Self {
match sampler_binding_type {
SamplerBindingType::Filtering => BindlessResourceType::SamplerFiltering,
SamplerBindingType::NonFiltering => BindlessResourceType::SamplerNonFiltering,
SamplerBindingType::Comparison => BindlessResourceType::SamplerComparison,
}
}
}
impl From<u32> for BindlessIndex {
fn from(value: u32) -> Self {
Self(value)
}
}
impl From<u32> for BindingNumber {
fn from(value: u32) -> Self {
Self(value)
}
}