Skip to main content

rotex_types/resource/
descriptors.rs

1use crate::resource::geometry::{IndexFormat, VertexBufferLayout};
2use crate::resource::ids::{BindGroupLayoutId, BufferId, TextureId};
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
6pub enum BindingType {
7    UniformBuffer,
8    UniformBufferDynamic,
9    StorageBuffer,
10    CombinedImageSampler,
11}
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
14pub struct ShaderStageFlags(u8);
15
16impl ShaderStageFlags {
17    pub const VERTEX: Self = Self(1 << 0);
18    pub const FRAGMENT: Self = Self(1 << 1);
19    pub const COMPUTE: Self = Self(1 << 2);
20
21    pub const fn empty() -> Self {
22        Self(0)
23    }
24
25    pub const fn contains(self, other: Self) -> bool {
26        (self.0 & other.0) == other.0
27    }
28
29    pub const fn union(self, other: Self) -> Self {
30        Self(self.0 | other.0)
31    }
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
35pub enum MemoryLocation {
36    #[default]
37    CpuToGpu,
38    GpuOnly,
39    GpuToCpu,
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
43pub struct BindGroupLayoutEntry {
44    pub binding: u32,
45    pub visibility: ShaderStageFlags,
46    pub ty: BindingType,
47    #[serde(default)]
48    pub readonly: bool,
49}
50
51#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
52pub struct BindGroupLayoutDescriptor {
53    pub set: u32,
54    pub entries: Vec<BindGroupLayoutEntry>,
55}
56
57#[derive(Debug, Clone)]
58pub enum BindGroupEntryDescriptor {
59    Buffer {
60        binding: u32,
61        buffer: BufferId,
62        offset: u64,
63        size: u64,
64    },
65    Texture {
66        binding: u32,
67        texture: TextureId,
68    },
69}
70
71#[derive(Debug, Clone)]
72pub struct BindGroupDescriptor {
73    pub layout: BindGroupLayoutId,
74    pub entries: Vec<BindGroupEntryDescriptor>,
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
78pub enum BufferUsage {
79    Vertex,
80    Index,
81    Uniform,
82    Storage,
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
86pub struct BufferUsages(u8);
87
88impl BufferUsages {
89    pub const VERTEX: Self = Self(1 << 0);
90    pub const INDEX: Self = Self(1 << 1);
91    pub const UNIFORM: Self = Self(1 << 2);
92    pub const STORAGE: Self = Self(1 << 3);
93
94    pub const fn empty() -> Self {
95        Self(0)
96    }
97
98    pub const fn from_single(usage: BufferUsage) -> Self {
99        match usage {
100            BufferUsage::Vertex => Self::VERTEX,
101            BufferUsage::Index => Self::INDEX,
102            BufferUsage::Uniform => Self::UNIFORM,
103            BufferUsage::Storage => Self::STORAGE,
104        }
105    }
106
107    pub const fn contains(self, other: Self) -> bool {
108        (self.0 & other.0) == other.0
109    }
110
111    pub const fn union(self, other: Self) -> Self {
112        Self(self.0 | other.0)
113    }
114
115    pub const fn is_empty(self) -> bool {
116        self.0 == 0
117    }
118}
119
120impl From<BufferUsage> for BufferUsages {
121    fn from(usage: BufferUsage) -> Self {
122        Self::from_single(usage)
123    }
124}
125
126#[derive(Debug, Clone)]
127pub struct BufferDescriptor {
128    pub size: u64,
129    pub usage: BufferUsage,
130    pub usages: BufferUsages,
131    pub memory_location: MemoryLocation,
132    pub initial_data: Option<Vec<u8>>,
133}
134
135impl BufferDescriptor {
136    pub fn effective_usages(&self) -> BufferUsages {
137        if self.usages.is_empty() {
138            BufferUsages::from_single(self.usage)
139        } else {
140            self.usages
141        }
142    }
143}
144
145impl Default for BufferDescriptor {
146    fn default() -> Self {
147        Self {
148            size: 0,
149            usage: BufferUsage::Uniform,
150            usages: BufferUsages::empty(),
151            memory_location: MemoryLocation::CpuToGpu,
152            initial_data: None,
153        }
154    }
155}
156
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
158pub enum AccessType {
159    None,
160    ComputeRead,
161    ComputeWrite,
162    ComputeReadWrite,
163    VertexRead,
164    VertexShaderRead,
165    FragmentRead,
166}
167
168/// Describes how a buffer is bound for a compute or graphics pass.
169///
170/// Bind group set indices and bindings are user-defined via `BindGroupLayoutEntry`.
171/// The HAL translates `BufferUsageIntent` entries into descriptor writes without
172/// enforcing any engine-specific set numbering convention.
173#[derive(Debug, Clone)]
174pub struct BufferUsageIntent {
175    pub buffer: BufferId,
176    pub access: AccessType,
177    pub set: u32,
178    pub binding: u32,
179    pub offset: u64,
180    pub size: u64,
181}
182
183#[derive(Debug, Clone)]
184pub enum VertexStreamData {
185    /// Static geometry uploaded at mesh creation (e.g. quad corners).
186    Static(Vec<u8>),
187    /// Dynamic buffer bound at draw time (e.g. compute output).
188    External(BufferId),
189}
190
191#[derive(Debug, Clone)]
192pub struct VertexStream {
193    pub layout: VertexBufferLayout,
194    pub data: VertexStreamData,
195}
196
197#[derive(Debug, Clone)]
198pub struct MeshDescriptor {
199    pub vertex_streams: Vec<VertexStream>,
200    pub index_data: Vec<u8>,
201    pub index_format: IndexFormat,
202    pub index_count: u32,
203}
204
205impl MeshDescriptor {
206    pub fn single(
207        vertex_data: Vec<u8>,
208        vertex_layout: VertexBufferLayout,
209        index_data: Vec<u8>,
210        index_format: IndexFormat,
211        index_count: u32,
212    ) -> Self {
213        Self {
214            vertex_streams: vec![VertexStream {
215                layout: vertex_layout,
216                data: VertexStreamData::Static(vertex_data),
217            }],
218            index_data,
219            index_format,
220            index_count,
221        }
222    }
223}
224
225#[derive(Debug, Clone, Copy)]
226pub enum TextureFormat {
227    Rgba8Unorm,
228}
229
230impl TextureFormat {
231    pub const fn bytes_per_pixel(self) -> usize {
232        match self {
233            Self::Rgba8Unorm => 4,
234        }
235    }
236
237    pub fn expected_byte_len(self, width: u32, height: u32) -> Option<usize> {
238        (width as usize)
239            .checked_mul(height as usize)?
240            .checked_mul(self.bytes_per_pixel())
241    }
242}
243
244#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
245pub enum CullMode {
246    None,
247    Front,
248    Back,
249}
250
251impl Default for CullMode {
252    fn default() -> Self {
253        Self::Back
254    }
255}
256
257#[derive(Debug, Clone)]
258pub struct TextureDescriptor {
259    pub width: u32,
260    pub height: u32,
261    pub format: TextureFormat,
262    pub data: Vec<u8>,
263    pub render_attachment: bool,
264}
265
266impl TextureDescriptor {
267    pub fn with_render_attachment(mut self, render_attachment: bool) -> Self {
268        self.render_attachment = render_attachment;
269        self
270    }
271}
272
273#[derive(Debug, Clone)]
274pub struct MaterialDescriptor {
275    pub shaders: crate::shader::GraphicsShaderPackage,
276    pub enable_depth: bool,
277    pub cull_mode: CullMode,
278    pub texture: Option<TextureId>,
279}
280
281impl MaterialDescriptor {
282    pub fn new(
283        shaders: crate::shader::GraphicsShaderPackage,
284        enable_depth: bool,
285        texture: Option<TextureId>,
286    ) -> Self {
287        Self {
288            shaders,
289            enable_depth,
290            cull_mode: CullMode::default(),
291            texture,
292        }
293    }
294
295    pub fn with_cull_mode(mut self, cull_mode: CullMode) -> Self {
296        self.cull_mode = cull_mode;
297        self
298    }
299}
300
301#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
302pub struct ComputeBindingLayout {
303    pub set: u32,
304    pub binding: u32,
305    pub readonly: bool,
306}
307
308#[derive(Debug, Clone, Hash, PartialEq, Eq)]
309pub struct ComputePipelineDescriptor {
310    pub shader_spv: Vec<u8>,
311    pub entry_point: String,
312    pub bindings: Vec<ComputeBindingLayout>,
313}
314
315impl ComputePipelineDescriptor {
316    pub fn from_raw(shader_spv: Vec<u8>, entry_point: String, bindings: Vec<ComputeBindingLayout>) -> Self {
317        Self { shader_spv, entry_point, bindings }
318    }
319
320    pub fn new(shader: crate::shader::ShaderPackage) -> Self {
321        Self {
322            shader_spv: shader.spirv_bytes().map(|b| b.to_vec()).unwrap_or_default(),
323            entry_point: shader.entry_point,
324            bindings: Vec::new(),
325        }
326    }
327}