#![allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BindingType {
UniformBuffer,
StorageBuffer,
SampledTexture,
StorageTexture,
CombinedImageSampler,
}
impl BindingType {
#[must_use]
pub fn is_buffer(&self) -> bool {
matches!(self, Self::UniformBuffer | Self::StorageBuffer)
}
#[must_use]
pub fn is_texture(&self) -> bool {
matches!(
self,
Self::SampledTexture | Self::StorageTexture | Self::CombinedImageSampler
)
}
#[must_use]
pub fn is_writable(&self) -> bool {
matches!(self, Self::StorageBuffer | Self::StorageTexture)
}
}
#[derive(Debug, Clone)]
pub struct DescriptorBinding {
pub slot: u32,
pub binding_type: BindingType,
pub count: u32,
pub label: Option<String>,
}
impl DescriptorBinding {
#[must_use]
pub fn new(slot: u32, binding_type: BindingType) -> Self {
Self {
slot,
binding_type,
count: 1,
label: None,
}
}
#[must_use]
pub fn array(slot: u32, binding_type: BindingType, count: u32) -> Self {
Self {
slot,
binding_type,
count,
label: None,
}
}
#[must_use]
pub fn with_label(mut self, label: impl Into<String>) -> Self {
self.label = Some(label.into());
self
}
#[must_use]
pub fn is_valid(&self) -> bool {
self.count >= 1
}
}
#[derive(Debug, Default)]
pub struct DescriptorSet {
bindings: Vec<DescriptorBinding>,
}
impl DescriptorSet {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn add_binding(&mut self, binding: DescriptorBinding) {
if let Some(existing) = self.bindings.iter_mut().find(|b| b.slot == binding.slot) {
*existing = binding;
} else {
self.bindings.push(binding);
}
}
#[must_use]
pub fn binding_count(&self) -> usize {
self.bindings.len()
}
#[must_use]
pub fn get_binding(&self, slot: u32) -> Option<&DescriptorBinding> {
self.bindings.iter().find(|b| b.slot == slot)
}
#[must_use]
pub fn buffer_bindings(&self) -> Vec<&DescriptorBinding> {
self.bindings
.iter()
.filter(|b| b.binding_type.is_buffer())
.collect()
}
#[must_use]
pub fn texture_bindings(&self) -> Vec<&DescriptorBinding> {
self.bindings
.iter()
.filter(|b| b.binding_type.is_texture())
.collect()
}
}
#[derive(Debug, Default)]
pub struct DescriptorLayout {
entries: Vec<DescriptorBinding>,
}
impl DescriptorLayout {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn add_entry(&mut self, entry: DescriptorBinding) {
self.entries.push(entry);
}
#[must_use]
pub fn bindings(&self) -> &[DescriptorBinding] {
&self.entries
}
#[must_use]
pub fn len(&self) -> usize {
self.entries.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_binding_type_is_buffer_uniform() {
assert!(BindingType::UniformBuffer.is_buffer());
}
#[test]
fn test_binding_type_is_buffer_storage() {
assert!(BindingType::StorageBuffer.is_buffer());
}
#[test]
fn test_binding_type_is_buffer_texture_false() {
assert!(!BindingType::SampledTexture.is_buffer());
}
#[test]
fn test_binding_type_is_texture() {
assert!(BindingType::SampledTexture.is_texture());
assert!(BindingType::StorageTexture.is_texture());
assert!(BindingType::CombinedImageSampler.is_texture());
}
#[test]
fn test_binding_type_is_writable() {
assert!(BindingType::StorageBuffer.is_writable());
assert!(BindingType::StorageTexture.is_writable());
assert!(!BindingType::UniformBuffer.is_writable());
assert!(!BindingType::SampledTexture.is_writable());
}
#[test]
fn test_descriptor_binding_is_valid() {
let b = DescriptorBinding::new(0, BindingType::UniformBuffer);
assert!(b.is_valid());
}
#[test]
fn test_descriptor_binding_array_count() {
let b = DescriptorBinding::array(1, BindingType::SampledTexture, 4);
assert_eq!(b.count, 4);
assert!(b.is_valid());
}
#[test]
fn test_descriptor_binding_with_label() {
let b = DescriptorBinding::new(2, BindingType::StorageBuffer).with_label("my_buf");
assert_eq!(b.label.as_deref(), Some("my_buf"));
}
#[test]
fn test_descriptor_set_add_binding_count() {
let mut set = DescriptorSet::new();
set.add_binding(DescriptorBinding::new(0, BindingType::UniformBuffer));
set.add_binding(DescriptorBinding::new(1, BindingType::SampledTexture));
assert_eq!(set.binding_count(), 2);
}
#[test]
fn test_descriptor_set_replace_binding() {
let mut set = DescriptorSet::new();
set.add_binding(DescriptorBinding::new(0, BindingType::UniformBuffer));
set.add_binding(DescriptorBinding::new(0, BindingType::StorageBuffer));
assert_eq!(set.binding_count(), 1);
assert_eq!(
set.get_binding(0)
.expect("binding should exist")
.binding_type,
BindingType::StorageBuffer
);
}
#[test]
fn test_descriptor_set_buffer_bindings() {
let mut set = DescriptorSet::new();
set.add_binding(DescriptorBinding::new(0, BindingType::UniformBuffer));
set.add_binding(DescriptorBinding::new(1, BindingType::SampledTexture));
set.add_binding(DescriptorBinding::new(2, BindingType::StorageBuffer));
assert_eq!(set.buffer_bindings().len(), 2);
}
#[test]
fn test_descriptor_layout_bindings() {
let mut layout = DescriptorLayout::new();
layout.add_entry(DescriptorBinding::new(0, BindingType::UniformBuffer));
layout.add_entry(DescriptorBinding::new(1, BindingType::StorageTexture));
assert_eq!(layout.bindings().len(), 2);
assert!(!layout.is_empty());
}
#[test]
fn test_descriptor_layout_empty() {
let layout = DescriptorLayout::new();
assert!(layout.is_empty());
assert_eq!(layout.len(), 0);
}
}