Skip to main content

oximedia_gpu/
descriptor_set.rs

1#![allow(dead_code)]
2//! Descriptor set and layout management for GPU pipeline bindings.
3
4/// The kind of resource bound at a particular descriptor slot.
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
6pub enum BindingType {
7    /// A uniform (constant) buffer.
8    UniformBuffer,
9    /// A storage buffer that can be read/written by a shader.
10    StorageBuffer,
11    /// A read-only sampled texture.
12    SampledTexture,
13    /// A read/write storage texture.
14    StorageTexture,
15    /// A combined image + sampler.
16    CombinedImageSampler,
17}
18
19impl BindingType {
20    /// Returns `true` if this binding type is backed by a buffer (not a texture).
21    #[must_use]
22    pub fn is_buffer(&self) -> bool {
23        matches!(self, Self::UniformBuffer | Self::StorageBuffer)
24    }
25
26    /// Returns `true` if this binding type involves a texture.
27    #[must_use]
28    pub fn is_texture(&self) -> bool {
29        matches!(
30            self,
31            Self::SampledTexture | Self::StorageTexture | Self::CombinedImageSampler
32        )
33    }
34
35    /// Returns `true` if the shader can write to this resource.
36    #[must_use]
37    pub fn is_writable(&self) -> bool {
38        matches!(self, Self::StorageBuffer | Self::StorageTexture)
39    }
40}
41
42/// A single binding within a descriptor set.
43#[derive(Debug, Clone)]
44pub struct DescriptorBinding {
45    /// The binding slot index.
46    pub slot: u32,
47    /// The type of resource at this slot.
48    pub binding_type: BindingType,
49    /// The number of array elements (1 for non-array bindings).
50    pub count: u32,
51    /// An optional debug label.
52    pub label: Option<String>,
53}
54
55impl DescriptorBinding {
56    /// Create a new binding for a single resource.
57    #[must_use]
58    pub fn new(slot: u32, binding_type: BindingType) -> Self {
59        Self {
60            slot,
61            binding_type,
62            count: 1,
63            label: None,
64        }
65    }
66
67    /// Create a new array binding.
68    #[must_use]
69    pub fn array(slot: u32, binding_type: BindingType, count: u32) -> Self {
70        Self {
71            slot,
72            binding_type,
73            count,
74            label: None,
75        }
76    }
77
78    /// Attach a debug label.
79    #[must_use]
80    pub fn with_label(mut self, label: impl Into<String>) -> Self {
81        self.label = Some(label.into());
82        self
83    }
84
85    /// A binding is valid when `count` is at least 1.
86    #[must_use]
87    pub fn is_valid(&self) -> bool {
88        self.count >= 1
89    }
90}
91
92/// A descriptor set: a collection of resource bindings.
93#[derive(Debug, Default)]
94pub struct DescriptorSet {
95    bindings: Vec<DescriptorBinding>,
96}
97
98impl DescriptorSet {
99    /// Create an empty descriptor set.
100    #[must_use]
101    pub fn new() -> Self {
102        Self::default()
103    }
104
105    /// Add a binding to the set, replacing any existing binding at the same slot.
106    pub fn add_binding(&mut self, binding: DescriptorBinding) {
107        if let Some(existing) = self.bindings.iter_mut().find(|b| b.slot == binding.slot) {
108            *existing = binding;
109        } else {
110            self.bindings.push(binding);
111        }
112    }
113
114    /// Total number of bindings in the set.
115    #[must_use]
116    pub fn binding_count(&self) -> usize {
117        self.bindings.len()
118    }
119
120    /// Retrieve a binding by its slot number.
121    #[must_use]
122    pub fn get_binding(&self, slot: u32) -> Option<&DescriptorBinding> {
123        self.bindings.iter().find(|b| b.slot == slot)
124    }
125
126    /// Returns all buffer bindings.
127    #[must_use]
128    pub fn buffer_bindings(&self) -> Vec<&DescriptorBinding> {
129        self.bindings
130            .iter()
131            .filter(|b| b.binding_type.is_buffer())
132            .collect()
133    }
134
135    /// Returns all texture bindings.
136    #[must_use]
137    pub fn texture_bindings(&self) -> Vec<&DescriptorBinding> {
138        self.bindings
139            .iter()
140            .filter(|b| b.binding_type.is_texture())
141            .collect()
142    }
143}
144
145/// A descriptor layout: the schema that describes a set's bindings
146/// without being tied to specific resources.
147#[derive(Debug, Default)]
148pub struct DescriptorLayout {
149    entries: Vec<DescriptorBinding>,
150}
151
152impl DescriptorLayout {
153    /// Create an empty layout.
154    #[must_use]
155    pub fn new() -> Self {
156        Self::default()
157    }
158
159    /// Add a binding entry to the layout.
160    pub fn add_entry(&mut self, entry: DescriptorBinding) {
161        self.entries.push(entry);
162    }
163
164    /// All binding entries in this layout.
165    #[must_use]
166    pub fn bindings(&self) -> &[DescriptorBinding] {
167        &self.entries
168    }
169
170    /// Number of binding entries.
171    #[must_use]
172    pub fn len(&self) -> usize {
173        self.entries.len()
174    }
175
176    /// Returns `true` when there are no entries.
177    #[must_use]
178    pub fn is_empty(&self) -> bool {
179        self.entries.is_empty()
180    }
181}
182
183#[cfg(test)]
184mod tests {
185    use super::*;
186
187    #[test]
188    fn test_binding_type_is_buffer_uniform() {
189        assert!(BindingType::UniformBuffer.is_buffer());
190    }
191
192    #[test]
193    fn test_binding_type_is_buffer_storage() {
194        assert!(BindingType::StorageBuffer.is_buffer());
195    }
196
197    #[test]
198    fn test_binding_type_is_buffer_texture_false() {
199        assert!(!BindingType::SampledTexture.is_buffer());
200    }
201
202    #[test]
203    fn test_binding_type_is_texture() {
204        assert!(BindingType::SampledTexture.is_texture());
205        assert!(BindingType::StorageTexture.is_texture());
206        assert!(BindingType::CombinedImageSampler.is_texture());
207    }
208
209    #[test]
210    fn test_binding_type_is_writable() {
211        assert!(BindingType::StorageBuffer.is_writable());
212        assert!(BindingType::StorageTexture.is_writable());
213        assert!(!BindingType::UniformBuffer.is_writable());
214        assert!(!BindingType::SampledTexture.is_writable());
215    }
216
217    #[test]
218    fn test_descriptor_binding_is_valid() {
219        let b = DescriptorBinding::new(0, BindingType::UniformBuffer);
220        assert!(b.is_valid());
221    }
222
223    #[test]
224    fn test_descriptor_binding_array_count() {
225        let b = DescriptorBinding::array(1, BindingType::SampledTexture, 4);
226        assert_eq!(b.count, 4);
227        assert!(b.is_valid());
228    }
229
230    #[test]
231    fn test_descriptor_binding_with_label() {
232        let b = DescriptorBinding::new(2, BindingType::StorageBuffer).with_label("my_buf");
233        assert_eq!(b.label.as_deref(), Some("my_buf"));
234    }
235
236    #[test]
237    fn test_descriptor_set_add_binding_count() {
238        let mut set = DescriptorSet::new();
239        set.add_binding(DescriptorBinding::new(0, BindingType::UniformBuffer));
240        set.add_binding(DescriptorBinding::new(1, BindingType::SampledTexture));
241        assert_eq!(set.binding_count(), 2);
242    }
243
244    #[test]
245    fn test_descriptor_set_replace_binding() {
246        let mut set = DescriptorSet::new();
247        set.add_binding(DescriptorBinding::new(0, BindingType::UniformBuffer));
248        set.add_binding(DescriptorBinding::new(0, BindingType::StorageBuffer));
249        assert_eq!(set.binding_count(), 1);
250        assert_eq!(
251            set.get_binding(0)
252                .expect("binding should exist")
253                .binding_type,
254            BindingType::StorageBuffer
255        );
256    }
257
258    #[test]
259    fn test_descriptor_set_buffer_bindings() {
260        let mut set = DescriptorSet::new();
261        set.add_binding(DescriptorBinding::new(0, BindingType::UniformBuffer));
262        set.add_binding(DescriptorBinding::new(1, BindingType::SampledTexture));
263        set.add_binding(DescriptorBinding::new(2, BindingType::StorageBuffer));
264        assert_eq!(set.buffer_bindings().len(), 2);
265    }
266
267    #[test]
268    fn test_descriptor_layout_bindings() {
269        let mut layout = DescriptorLayout::new();
270        layout.add_entry(DescriptorBinding::new(0, BindingType::UniformBuffer));
271        layout.add_entry(DescriptorBinding::new(1, BindingType::StorageTexture));
272        assert_eq!(layout.bindings().len(), 2);
273        assert!(!layout.is_empty());
274    }
275
276    #[test]
277    fn test_descriptor_layout_empty() {
278        let layout = DescriptorLayout::new();
279        assert!(layout.is_empty());
280        assert_eq!(layout.len(), 0);
281    }
282}