1use crate::{
18 math::{Mat4, Vec3},
19 renderer::{BufferId, RenderPipelineId},
20};
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
24pub enum IndexFormat {
25 Uint16,
27 Uint32,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
33pub enum GraphicsBackendType {
34 Vulkan,
36 Metal,
38 Dx12,
40 Dx11,
42 OpenGL,
44 WebGpu,
46 #[default]
48 Unknown,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
53pub enum RendererDeviceType {
54 IntegratedGpu,
56 DiscreteGpu,
58 VirtualGpu,
60 Cpu,
62 #[default]
64 Unknown,
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub enum RenderStrategy {
71 Forward,
73 Deferred,
75 Custom(u32),
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
81pub enum SampleCount {
82 #[default]
84 X1,
85 X2,
87 X4,
89 X8,
91 X16,
93 X32,
95 X64,
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
101pub enum ShaderStage {
102 Vertex,
104 Fragment,
106 Compute,
108}
109
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
115pub struct ShaderStageFlags {
116 bits: u32,
117}
118
119impl ShaderStageFlags {
120 pub const NONE: Self = Self { bits: 0 };
122 pub const VERTEX: Self = Self { bits: 1 << 0 };
124 pub const FRAGMENT: Self = Self { bits: 1 << 1 };
126 pub const COMPUTE: Self = Self { bits: 1 << 2 };
128 pub const VERTEX_FRAGMENT: Self = Self {
130 bits: Self::VERTEX.bits | Self::FRAGMENT.bits,
131 };
132 pub const ALL: Self = Self {
134 bits: Self::VERTEX.bits | Self::FRAGMENT.bits | Self::COMPUTE.bits,
135 };
136
137 pub const fn from_bits(bits: u32) -> Self {
139 Self { bits }
140 }
141
142 pub const fn from_stage(stage: ShaderStage) -> Self {
144 match stage {
145 ShaderStage::Vertex => Self::VERTEX,
146 ShaderStage::Fragment => Self::FRAGMENT,
147 ShaderStage::Compute => Self::COMPUTE,
148 }
149 }
150
151 pub const fn bits(&self) -> u32 {
153 self.bits
154 }
155
156 pub const fn union(self, other: Self) -> Self {
158 Self {
159 bits: self.bits | other.bits,
160 }
161 }
162
163 pub const fn contains(&self, stage: ShaderStage) -> bool {
165 let stage_bits = Self::from_stage(stage).bits;
166 (self.bits & stage_bits) == stage_bits
167 }
168
169 pub const fn is_empty(&self) -> bool {
171 self.bits == 0
172 }
173}
174
175impl std::ops::BitOr for ShaderStageFlags {
176 type Output = Self;
177
178 fn bitor(self, rhs: Self) -> Self::Output {
179 self.union(rhs)
180 }
181}
182
183impl std::ops::BitOrAssign for ShaderStageFlags {
184 fn bitor_assign(&mut self, rhs: Self) {
185 *self = self.union(rhs);
186 }
187}
188
189#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
191pub enum TextureFormat {
192 R8Unorm,
195 Rg8Unorm,
197 Rgba8Unorm,
199 Rgba8UnormSrgb,
201 Bgra8UnormSrgb,
203 R16Float,
206 Rg16Float,
208 Rgba16Float,
210 R32Float,
213 Rg32Float,
215 Rgba32Float,
217 Depth16Unorm,
220 Depth24Plus,
222 Depth24PlusStencil8,
224 Depth32Float,
226 Depth32FloatStencil8,
228}
229
230impl TextureFormat {
231 pub fn bytes_per_pixel(&self) -> u32 {
234 match self {
235 TextureFormat::R8Unorm => 1,
236 TextureFormat::Rg8Unorm => 2,
237 TextureFormat::Rgba8Unorm => 4,
238 TextureFormat::Rgba8UnormSrgb => 4,
239 TextureFormat::Bgra8UnormSrgb => 4,
240 TextureFormat::R16Float => 2,
241 TextureFormat::Rg16Float => 4,
242 TextureFormat::Rgba16Float => 8,
243 TextureFormat::R32Float => 4,
244 TextureFormat::Rg32Float => 8,
245 TextureFormat::Rgba32Float => 16,
246 TextureFormat::Depth16Unorm => 2,
247 TextureFormat::Depth24Plus => 4,
248 TextureFormat::Depth24PlusStencil8 => 4,
249 TextureFormat::Depth32Float => 4,
250 TextureFormat::Depth32FloatStencil8 => 5,
251 }
252 }
253}
254
255#[derive(Debug, Clone, Default)]
259pub struct RendererAdapterInfo {
260 pub name: String,
262 pub backend_type: GraphicsBackendType,
264 pub device_type: RendererDeviceType,
266}
267
268#[derive(Debug, Clone, Default)]
270pub struct GraphicsAdapterInfo {
271 pub name: String,
273 pub backend_type: GraphicsBackendType,
275 pub device_type: RendererDeviceType,
277}
278
279#[derive(Debug, Clone)]
282pub struct RenderObject {
283 pub pipeline: RenderPipelineId,
285 pub vertex_buffer: BufferId,
287 pub index_buffer: BufferId,
289 pub index_count: u32,
291}
292
293#[derive(Debug, Clone)]
295pub struct RenderSettings {
296 pub strategy: RenderStrategy,
298 pub quality_level: u32,
300 pub show_wireframe: bool,
302 pub resize_debounce_ms: u64,
304 pub resize_max_pending_frames: u32,
306 pub enable_gpu_timestamps: bool,
308}
309
310impl Default for RenderSettings {
311 fn default() -> Self {
312 Self {
313 strategy: RenderStrategy::Forward,
314 quality_level: 1,
315 show_wireframe: false,
316 resize_debounce_ms: 120,
317 resize_max_pending_frames: 10,
318 enable_gpu_timestamps: true,
319 }
320 }
321}
322
323#[derive(Debug, Clone)]
325pub struct RenderStats {
326 pub frame_number: u64,
328 pub cpu_preparation_time_ms: f32,
330 pub cpu_render_submission_time_ms: f32,
332 pub gpu_main_pass_time_ms: f32,
334 pub gpu_frame_total_time_ms: f32,
336 pub draw_calls: u32,
338 pub triangles_rendered: u32,
340 pub vram_usage_estimate_mb: f32,
342}
343
344#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
348pub enum GpuHook {
349 FrameStart,
351 MainPassBegin,
353 MainPassEnd,
355 FrameEnd,
357}
358
359impl GpuHook {
360 pub const ALL: [GpuHook; 4] = [
362 GpuHook::FrameStart,
363 GpuHook::MainPassBegin,
364 GpuHook::MainPassEnd,
365 GpuHook::FrameEnd,
366 ];
367}
368
369impl Default for RenderStats {
370 fn default() -> Self {
371 Self {
372 frame_number: 0,
373 cpu_preparation_time_ms: 0.0,
374 cpu_render_submission_time_ms: 0.0,
375 gpu_main_pass_time_ms: 0.0,
376 gpu_frame_total_time_ms: 0.0,
377 draw_calls: 0,
378 triangles_rendered: 0,
379 vram_usage_estimate_mb: 0.0,
380 }
381 }
382}
383
384#[derive(Debug, Clone)]
387pub struct ViewInfo {
388 pub view_matrix: Mat4,
390 pub projection_matrix: Mat4,
392 pub camera_position: Vec3,
394}
395
396impl ViewInfo {
397 pub fn new(view_matrix: Mat4, projection_matrix: Mat4, camera_position: Vec3) -> Self {
399 Self {
400 view_matrix,
401 projection_matrix,
402 camera_position,
403 }
404 }
405
406 pub fn view_projection_matrix(&self) -> Mat4 {
411 self.projection_matrix * self.view_matrix
412 }
413}
414
415impl Default for ViewInfo {
416 fn default() -> Self {
417 Self {
418 view_matrix: Mat4::IDENTITY,
419 projection_matrix: Mat4::IDENTITY,
420 camera_position: Vec3::ZERO,
421 }
422 }
423}
424
425#[repr(C, align(16))]
433#[derive(Debug, Clone, Copy)]
434pub struct CameraUniformData {
435 pub view_projection: Mat4,
437 pub camera_position: [f32; 4],
440}
441
442impl CameraUniformData {
443 pub fn from_view_info(view_info: &ViewInfo) -> Self {
445 Self {
446 view_projection: view_info.view_projection_matrix(),
447 camera_position: [
448 view_info.camera_position.x,
449 view_info.camera_position.y,
450 view_info.camera_position.z,
451 0.0, ],
453 }
454 }
455
456 pub fn as_bytes(&self) -> &[u8] {
458 unsafe {
459 std::slice::from_raw_parts(
460 self as *const Self as *const u8,
461 std::mem::size_of::<Self>(),
462 )
463 }
464 }
465}
466
467unsafe impl bytemuck::Pod for CameraUniformData {}
469unsafe impl bytemuck::Zeroable for CameraUniformData {}
470
471#[cfg(test)]
472mod tests {
473 use super::*;
474
475 #[test]
476 fn test_camera_uniform_data_size() {
477 assert_eq!(std::mem::size_of::<CameraUniformData>(), 80);
480 }
481
482 #[test]
483 fn test_camera_uniform_data_alignment() {
484 assert_eq!(std::mem::align_of::<CameraUniformData>(), 16);
486 }
487
488 #[test]
489 fn test_camera_uniform_data_from_view_info() {
490 let view_matrix = Mat4::IDENTITY;
491 let projection_matrix = Mat4::IDENTITY;
492 let camera_position = Vec3::new(1.0, 2.0, 3.0);
493
494 let view_info = ViewInfo::new(view_matrix, projection_matrix, camera_position);
495 let uniform_data = CameraUniformData::from_view_info(&view_info);
496
497 assert_eq!(uniform_data.camera_position[0], 1.0);
499 assert_eq!(uniform_data.camera_position[1], 2.0);
500 assert_eq!(uniform_data.camera_position[2], 3.0);
501 assert_eq!(uniform_data.camera_position[3], 0.0); let expected_vp = projection_matrix * view_matrix;
505 assert_eq!(uniform_data.view_projection, expected_vp);
506 }
507
508 #[test]
509 fn test_camera_uniform_data_as_bytes() {
510 let view_info = ViewInfo::default();
511 let uniform_data = CameraUniformData::from_view_info(&view_info);
512
513 let bytes = uniform_data.as_bytes();
514 assert_eq!(bytes.len(), std::mem::size_of::<CameraUniformData>());
515 }
516
517 #[test]
518 fn test_camera_uniform_data_bytemuck() {
519 let uniform_data = CameraUniformData {
521 view_projection: Mat4::IDENTITY,
522 camera_position: [0.0, 0.0, 0.0, 0.0],
523 };
524
525 let data_array = [uniform_data];
526 let bytes: &[u8] = bytemuck::cast_slice(&data_array);
527 assert_eq!(bytes.len(), std::mem::size_of::<CameraUniformData>());
528 }
529
530 #[test]
531 fn test_view_info_new() {
532 let view = Mat4::from_translation(Vec3::new(0.0, 0.0, -5.0));
533 let proj = Mat4::IDENTITY;
534 let pos = Vec3::new(0.0, 1.0, 5.0);
535
536 let view_info = ViewInfo::new(view, proj, pos);
537
538 assert_eq!(view_info.view_matrix, view);
539 assert_eq!(view_info.projection_matrix, proj);
540 assert_eq!(view_info.camera_position, pos);
541 }
542
543 #[test]
544 fn test_view_info_view_projection_matrix() {
545 let view = Mat4::from_translation(Vec3::new(1.0, 2.0, 3.0));
546 let proj = Mat4::from_scale(Vec3::new(2.0, 2.0, 1.0));
547 let pos = Vec3::ZERO;
548
549 let view_info = ViewInfo::new(view, proj, pos);
550 let vp = view_info.view_projection_matrix();
551
552 assert_eq!(vp, proj * view);
554 }
555
556 #[test]
557 fn test_view_info_default() {
558 let view_info = ViewInfo::default();
559
560 assert_eq!(view_info.view_matrix, Mat4::IDENTITY);
561 assert_eq!(view_info.projection_matrix, Mat4::IDENTITY);
562 assert_eq!(view_info.camera_position, Vec3::ZERO);
563 }
564
565 #[test]
566 fn test_shader_stage_flags_bitwise() {
567 let vertex_fragment = ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT;
568
569 assert!(vertex_fragment.contains(ShaderStage::Vertex));
570 assert!(vertex_fragment.contains(ShaderStage::Fragment));
571 assert!(!vertex_fragment.contains(ShaderStage::Compute));
572 }
573
574 #[test]
575 fn test_shader_stage_flags_all() {
576 let all = ShaderStageFlags::ALL;
577
578 assert!(all.contains(ShaderStage::Vertex));
579 assert!(all.contains(ShaderStage::Fragment));
580 assert!(all.contains(ShaderStage::Compute));
581 }
582
583 #[test]
584 fn test_shader_stage_flags_union() {
585 let vertex = ShaderStageFlags::VERTEX;
586 let fragment = ShaderStageFlags::FRAGMENT;
587 let combined = vertex.union(fragment);
588
589 assert!(combined.contains(ShaderStage::Vertex));
590 assert!(combined.contains(ShaderStage::Fragment));
591 assert!(!combined.contains(ShaderStage::Compute));
592 }
593
594 #[test]
595 fn test_shader_stage_flags_from_stage() {
596 let vertex_flags = ShaderStageFlags::from_stage(ShaderStage::Vertex);
597 assert_eq!(vertex_flags, ShaderStageFlags::VERTEX);
598
599 let fragment_flags = ShaderStageFlags::from_stage(ShaderStage::Fragment);
600 assert_eq!(fragment_flags, ShaderStageFlags::FRAGMENT);
601
602 let compute_flags = ShaderStageFlags::from_stage(ShaderStage::Compute);
603 assert_eq!(compute_flags, ShaderStageFlags::COMPUTE);
604 }
605
606 #[test]
607 fn test_shader_stage_flags_is_empty() {
608 let none = ShaderStageFlags::NONE;
609 assert!(none.is_empty());
610
611 let vertex = ShaderStageFlags::VERTEX;
612 assert!(!vertex.is_empty());
613 }
614
615 #[test]
616 fn test_shader_stage_flags_bitor_assign() {
617 let mut flags = ShaderStageFlags::VERTEX;
618 flags |= ShaderStageFlags::FRAGMENT;
619
620 assert!(flags.contains(ShaderStage::Vertex));
621 assert!(flags.contains(ShaderStage::Fragment));
622 }
623}