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