1#![allow(clippy::all)]
6
7pub mod backend;
8#[cfg(feature = "vulkan")]
9pub mod vulkan;
10pub use backend::*;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum Backend {
14 Vulkan,
15 Metal,
16 DirectX12,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum DeviceType {
21 DiscreteGpu,
22 IntegratedGpu,
23 VirtualGpu,
24 Cpu,
25 Other,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub struct DeviceLimits {
30 pub max_buffer_size: u64,
31 pub max_texture_dimension_1d: u32,
32 pub max_texture_dimension_2d: u32,
33 pub max_texture_dimension_3d: u32,
34 pub max_texture_array_layers: u32,
35 pub min_uniform_buffer_offset_alignment: u64,
36 pub min_storage_buffer_offset_alignment: u64,
37 pub max_compute_workgroup_invocations: u32,
38 pub max_compute_workgroup_size: [u32; 3],
39}
40
41impl Default for DeviceLimits {
42 fn default() -> Self {
43 Self {
44 max_buffer_size: 256 * 1024 * 1024,
45 max_texture_dimension_1d: 8192,
46 max_texture_dimension_2d: 8192,
47 max_texture_dimension_3d: 2048,
48 max_texture_array_layers: 2048,
49 min_uniform_buffer_offset_alignment: 256,
50 min_storage_buffer_offset_alignment: 64,
51 max_compute_workgroup_invocations: 1024,
52 max_compute_workgroup_size: [1024, 1024, 64],
53 }
54 }
55}
56
57#[derive(Debug, Clone)]
58pub struct AdapterInfo {
59 pub name: String,
60 pub backend: Backend,
61 pub device_type: DeviceType,
62 pub vendor_id: u32,
63 pub device_id: u32,
64 pub supports_dynamic_rendering: bool,
65 pub supports_timeline_semaphores: bool,
66 pub supports_descriptor_indexing: bool,
67 pub max_compute_workgroup_invocations: u32,
68 pub max_compute_workgroup_size: [u32; 3],
69 pub limits: DeviceLimits,
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub enum MemoryLocation {
74 GpuOnly,
76 CpuToGpu,
78 GpuToCpu,
80}
81
82bitflags::bitflags! {
83 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
84 pub struct BufferUsage: u32 {
85 const TRANSFER_SRC = 1 << 0;
86 const TRANSFER_DST = 1 << 1;
87 const UNIFORM = 1 << 2;
88 const STORAGE = 1 << 3;
89 const VERTEX = 1 << 4;
90 const INDEX = 1 << 5;
91 const INDIRECT = 1 << 6;
92 }
93}
94
95#[derive(Debug, Clone)]
96pub struct BufferDesc<'a> {
97 pub label: Option<&'a str>,
98 pub size: u64,
99 pub usage: BufferUsage,
100 pub memory_location: MemoryLocation,
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104pub enum TextureFormat {
105 Rgba8Unorm,
106 Rgba8Srgb,
107 Bgra8Unorm,
108 Bgra8Srgb,
109 R32Float,
110 Rgba16Float,
111 Rgba32Float,
112 Depth32Float,
113}
114
115#[cfg(feature = "vulkan")]
116impl TextureFormat {
117 pub fn to_vk(&self) -> ash::vk::Format {
118 match self {
119 TextureFormat::Rgba8Unorm => ash::vk::Format::R8G8B8A8_UNORM,
120 TextureFormat::Rgba8Srgb => ash::vk::Format::R8G8B8A8_SRGB,
121 TextureFormat::Bgra8Unorm => ash::vk::Format::B8G8R8A8_UNORM,
122 TextureFormat::Bgra8Srgb => ash::vk::Format::B8G8R8A8_SRGB,
123 TextureFormat::R32Float => ash::vk::Format::R32_SFLOAT,
124 TextureFormat::Rgba16Float => ash::vk::Format::R16G16B16A16_SFLOAT,
125 TextureFormat::Rgba32Float => ash::vk::Format::R32G32B32A32_SFLOAT,
126 TextureFormat::Depth32Float => ash::vk::Format::D32_SFLOAT,
127 }
128 }
129
130 pub fn from_vk(format: ash::vk::Format) -> Option<Self> {
131 match format {
132 ash::vk::Format::R8G8B8A8_UNORM => Some(TextureFormat::Rgba8Unorm),
133 ash::vk::Format::R8G8B8A8_SRGB => Some(TextureFormat::Rgba8Srgb),
134 ash::vk::Format::B8G8R8A8_UNORM => Some(TextureFormat::Bgra8Unorm),
135 ash::vk::Format::B8G8R8A8_SRGB => Some(TextureFormat::Bgra8Srgb),
136 ash::vk::Format::R32_SFLOAT => Some(TextureFormat::R32Float),
137 ash::vk::Format::R16G16B16A16_SFLOAT => Some(TextureFormat::Rgba16Float),
138 ash::vk::Format::R32G32B32A32_SFLOAT => Some(TextureFormat::Rgba32Float),
139 ash::vk::Format::D32_SFLOAT => Some(TextureFormat::Depth32Float),
140 _ => None,
141 }
142 }
143}
144
145bitflags::bitflags! {
146 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
147 pub struct TextureUsage: u32 {
148 const COPY_SRC = 1 << 0;
149 const COPY_DST = 1 << 1;
150 const TEXTURE_BINDING = 1 << 2;
151 const STORAGE_BINDING = 1 << 3;
152 const RENDER_ATTACHMENT = 1 << 4;
153 }
154}
155
156#[derive(Debug, Clone)]
157pub struct TextureDesc<'a> {
158 pub label: Option<&'a str>,
159 pub width: u32,
160 pub height: u32,
161 pub depth: u32,
162 pub mip_levels: u32,
163 pub sample_count: u32,
164 pub format: TextureFormat,
165 pub usage: TextureUsage,
166}
167
168impl<'a> TextureDesc<'a> {
169 pub fn new_2d(width: u32, height: u32, format: TextureFormat, usage: TextureUsage) -> Self {
170 Self {
171 label: None,
172 width,
173 height,
174 depth: 1,
175 mip_levels: 1,
176 sample_count: 1,
177 format,
178 usage,
179 }
180 }
181}
182
183#[derive(Debug, Clone, Copy, PartialEq, Eq)]
184pub enum FilterMode {
185 Nearest,
186 Linear,
187}
188
189#[derive(Debug, Clone, Copy, PartialEq, Eq)]
190pub enum AddressMode {
191 ClampToEdge,
192 Repeat,
193 MirrorRepeat,
194}
195
196#[derive(Debug, Clone)]
197pub struct SamplerDesc<'a> {
198 pub label: Option<&'a str>,
199 pub min_filter: FilterMode,
200 pub mag_filter: FilterMode,
201 pub mipmap_filter: FilterMode,
202 pub address_mode_u: AddressMode,
203 pub address_mode_v: AddressMode,
204 pub address_mode_w: AddressMode,
205}
206
207impl Default for SamplerDesc<'_> {
208 fn default() -> Self {
209 Self {
210 label: None,
211 min_filter: FilterMode::Linear,
212 mag_filter: FilterMode::Linear,
213 mipmap_filter: FilterMode::Linear,
214 address_mode_u: AddressMode::ClampToEdge,
215 address_mode_v: AddressMode::ClampToEdge,
216 address_mode_w: AddressMode::ClampToEdge,
217 }
218 }
219}
220
221#[derive(Debug, Clone, Copy, PartialEq)]
222pub enum LoadOp<T> {
223 Clear(T),
224 Load,
225 DontCare,
226}
227
228pub type ColorLoadOp = LoadOp<[f32; 4]>;
229pub type DepthLoadOp = LoadOp<f32>;
230
231#[derive(Debug, Clone, Copy, PartialEq, Eq)]
232pub enum PrimitiveTopology {
233 PointList,
234 LineList,
235 LineStrip,
236 TriangleList,
237 TriangleStrip,
238}
239
240#[cfg(feature = "vulkan")]
241impl PrimitiveTopology {
242 pub fn to_vk(&self) -> ash::vk::PrimitiveTopology {
243 match self {
244 PrimitiveTopology::PointList => ash::vk::PrimitiveTopology::POINT_LIST,
245 PrimitiveTopology::LineList => ash::vk::PrimitiveTopology::LINE_LIST,
246 PrimitiveTopology::LineStrip => ash::vk::PrimitiveTopology::LINE_STRIP,
247 PrimitiveTopology::TriangleList => ash::vk::PrimitiveTopology::TRIANGLE_LIST,
248 PrimitiveTopology::TriangleStrip => ash::vk::PrimitiveTopology::TRIANGLE_STRIP,
249 }
250 }
251}
252
253#[derive(Debug, Clone, Copy, PartialEq, Eq)]
254pub enum CullMode {
255 None,
256 Front,
257 Back,
258}
259
260#[cfg(feature = "vulkan")]
261impl CullMode {
262 pub fn to_vk(&self) -> ash::vk::CullModeFlags {
263 match self {
264 CullMode::None => ash::vk::CullModeFlags::NONE,
265 CullMode::Front => ash::vk::CullModeFlags::FRONT,
266 CullMode::Back => ash::vk::CullModeFlags::BACK,
267 }
268 }
269}
270
271#[derive(Debug, Clone, Copy, PartialEq, Eq)]
272pub enum FrontFace {
273 Ccw,
274 Cw,
275}
276
277#[cfg(feature = "vulkan")]
278impl FrontFace {
279 pub fn to_vk(&self) -> ash::vk::FrontFace {
280 match self {
281 FrontFace::Ccw => ash::vk::FrontFace::COUNTER_CLOCKWISE,
282 FrontFace::Cw => ash::vk::FrontFace::CLOCKWISE,
283 }
284 }
285}
286
287#[derive(Debug, Clone, Copy, PartialEq, Eq)]
288pub struct PrimitiveState {
289 pub topology: PrimitiveTopology,
290 pub cull_mode: CullMode,
291 pub front_face: FrontFace,
292}
293
294impl Default for PrimitiveState {
295 fn default() -> Self {
296 Self {
297 topology: PrimitiveTopology::TriangleList,
298 cull_mode: CullMode::None,
299 front_face: FrontFace::Cw,
300 }
301 }
302}
303
304#[derive(Debug, Clone, Copy, PartialEq, Eq)]
305pub enum BlendFactor {
306 Zero,
307 One,
308 SrcColor,
309 OneMinusSrcColor,
310 SrcAlpha,
311 OneMinusSrcAlpha,
312 DstColor,
313 OneMinusDstColor,
314 DstAlpha,
315 OneMinusDstAlpha,
316}
317
318#[cfg(feature = "vulkan")]
319impl BlendFactor {
320 pub fn to_vk(&self) -> ash::vk::BlendFactor {
321 match self {
322 BlendFactor::Zero => ash::vk::BlendFactor::ZERO,
323 BlendFactor::One => ash::vk::BlendFactor::ONE,
324 BlendFactor::SrcColor => ash::vk::BlendFactor::SRC_COLOR,
325 BlendFactor::OneMinusSrcColor => ash::vk::BlendFactor::ONE_MINUS_SRC_COLOR,
326 BlendFactor::SrcAlpha => ash::vk::BlendFactor::SRC_ALPHA,
327 BlendFactor::OneMinusSrcAlpha => ash::vk::BlendFactor::ONE_MINUS_SRC_ALPHA,
328 BlendFactor::DstColor => ash::vk::BlendFactor::DST_COLOR,
329 BlendFactor::OneMinusDstColor => ash::vk::BlendFactor::ONE_MINUS_DST_COLOR,
330 BlendFactor::DstAlpha => ash::vk::BlendFactor::DST_ALPHA,
331 BlendFactor::OneMinusDstAlpha => ash::vk::BlendFactor::ONE_MINUS_DST_ALPHA,
332 }
333 }
334}
335
336#[derive(Debug, Clone, Copy, PartialEq, Eq)]
337pub enum BlendOp {
338 Add,
339 Subtract,
340 ReverseSubtract,
341 Min,
342 Max,
343}
344
345#[cfg(feature = "vulkan")]
346impl BlendOp {
347 pub fn to_vk(&self) -> ash::vk::BlendOp {
348 match self {
349 BlendOp::Add => ash::vk::BlendOp::ADD,
350 BlendOp::Subtract => ash::vk::BlendOp::SUBTRACT,
351 BlendOp::ReverseSubtract => ash::vk::BlendOp::REVERSE_SUBTRACT,
352 BlendOp::Min => ash::vk::BlendOp::MIN,
353 BlendOp::Max => ash::vk::BlendOp::MAX,
354 }
355 }
356}
357
358#[derive(Debug, Clone, Copy, PartialEq, Eq)]
359pub struct BlendComponent {
360 pub src_factor: BlendFactor,
361 pub dst_factor: BlendFactor,
362 pub operation: BlendOp,
363}
364
365impl BlendComponent {
366 pub const REPLACE: Self = Self {
367 src_factor: BlendFactor::One,
368 dst_factor: BlendFactor::Zero,
369 operation: BlendOp::Add,
370 };
371
372 pub const OVER: Self = Self {
373 src_factor: BlendFactor::One,
374 dst_factor: BlendFactor::OneMinusSrcAlpha,
375 operation: BlendOp::Add,
376 };
377}
378
379#[derive(Debug, Clone, Copy, PartialEq, Eq)]
380pub struct BlendState {
381 pub color: BlendComponent,
382 pub alpha: BlendComponent,
383}
384
385impl BlendState {
386 pub const REPLACE: Self = Self {
387 color: BlendComponent::REPLACE,
388 alpha: BlendComponent::REPLACE,
389 };
390
391 pub const ALPHA_BLENDING: Self = Self {
392 color: BlendComponent {
393 src_factor: BlendFactor::SrcAlpha,
394 dst_factor: BlendFactor::OneMinusSrcAlpha,
395 operation: BlendOp::Add,
396 },
397 alpha: BlendComponent {
398 src_factor: BlendFactor::One,
399 dst_factor: BlendFactor::OneMinusSrcAlpha,
400 operation: BlendOp::Add,
401 },
402 };
403
404 pub const PREMULTIPLIED_ALPHA_BLENDING: Self = Self {
405 color: BlendComponent::OVER,
406 alpha: BlendComponent::OVER,
407 };
408
409 pub const ADDITIVE: Self = Self {
410 color: BlendComponent {
411 src_factor: BlendFactor::One,
412 dst_factor: BlendFactor::One,
413 operation: BlendOp::Add,
414 },
415 alpha: BlendComponent {
416 src_factor: BlendFactor::One,
417 dst_factor: BlendFactor::One,
418 operation: BlendOp::Add,
419 },
420 };
421}
422
423#[derive(Debug, Clone)]
424pub struct GraphicsPipelineDesc<'a> {
425 pub label: Option<&'a str>,
426 pub vs_entry: &'a str,
427 pub fs_entry: &'a str,
428 pub color_format: TextureFormat,
429 pub depth_format: Option<TextureFormat>,
430 pub depth_test: bool,
431 pub depth_write: bool,
432 pub sample_count: u32,
433 pub primitive: PrimitiveState,
434 pub blend: Option<BlendState>,
435}
436
437impl<'a> GraphicsPipelineDesc<'a> {
438 pub fn new(vs_entry: &'a str, fs_entry: &'a str, color_format: TextureFormat) -> Self {
439 Self {
440 label: None,
441 vs_entry,
442 fs_entry,
443 color_format,
444 depth_format: None,
445 depth_test: false,
446 depth_write: false,
447 sample_count: 1,
448 primitive: PrimitiveState::default(),
449 blend: None,
450 }
451 }
452}
453
454#[derive(thiserror::Error, Debug)]
455pub enum HalError {
456 #[error("Dynamic loader failed to load driver library: {0}")]
457 LibraryLoadError(String),
458 #[error("No compatible graphics adapter was found")]
459 NoAdapterFound,
460 #[error("Device creation failed: {0}")]
461 DeviceCreationFailed(String),
462 #[error("Out of memory: {0}")]
463 OutOfMemory(String),
464 #[error("Shader module compilation failed: {0}")]
465 ShaderError(String),
466 #[error("Pipeline creation failed: {0}")]
467 PipelineError(String),
468 #[error("Command recording or submission error: {0}")]
469 CommandError(String),
470 #[error("Map buffer error: {0}")]
471 MapError(String),
472 #[error("Invalid parameter: {0}")]
473 InvalidParameter(String),
474 #[error("Backend specific error: {0}")]
475 BackendError(String),
476}