Seregon/StratoSDK

StratoSDK is a framework with a declarative approach similar to Flutter/React, written and designed entirely for Rust.

Rust/27.3 KB/No license
crates/strato-renderer/src/gpu/shader_mgr.rs
StratoSDK / crates / strato-renderer / src / gpu / shader_mgr.rs
1//! Shader module management
2//!
3//! BLOCCO 3: Shader Compilation
4//! Handles WGSL shader loading, compilation, and validation
5 
6use wgpu::{Device, ShaderModule, ShaderModuleDescriptor, ShaderSource};
7 
8/// Manages shader module
9pub struct ShaderManager {
10 module: ShaderModule,
11 vertex_entry: String,
12 fragment_entry: String,
13}
14 
15impl ShaderManager {
16 /// Create shader from WGSL source
17 ///
18 /// # Arguments
19 /// * `device` - GPU device
20 /// * `source` - WGSL source code
21 /// * `label` - Optional label for debugging
22 ///
23 /// # Errors
24 /// Returns error if shader compilation fails
25 pub fn from_wgsl(device: &Device, source: &str, label: Option<&str>) -> anyhow::Result<Self> {
26 println!("=== SHADER COMPILATION ===");
27 println!("Label: {:?}", label);
28 println!("Source length: {} bytes", source.len());
29 
30 // Create shader module descriptor
31 let descriptor = ShaderModuleDescriptor {
32 label,
33 source: ShaderSource::Wgsl(source.into()),
34 };
35 
36 // Compile shader (wgpu validates automatically)
37 let module = device.create_shader_module(descriptor);
38 
39 // Extract entry points from WGSL source
40 // For simple.wgsl, we have hardcoded: vs_main and fs_main
41 let vertex_entry = "vs_main".to_string();
42 let fragment_entry = "fs_main".to_string();
43 
44 println!("Vertex entry: {}", vertex_entry);
45 println!("Fragment entry: {}", fragment_entry);
46 println!("Compilation: SUCCESS");
47 println!("==========================");
48 
49 Ok(Self {
50 module,
51 vertex_entry,
52 fragment_entry,
53 })
54 }
55 
56 /// Get shader module reference
57 pub fn module(&self) -> &ShaderModule {
58 &self.module
59 }
60 
61 /// Get vertex shader entry point
62 pub fn vertex_entry(&self) -> &str {
63 &self.vertex_entry
64 }
65 
66 /// Get fragment shader entry point
67 pub fn fragment_entry(&self) -> &str {
68 &self.fragment_entry
69 }
70}
71 
72#[cfg(test)]
73mod tests {
74 use super::*;
75 use crate::gpu::DeviceManager;
76 use wgpu::Backends;
77 
78 const SIMPLE_WGSL: &str = include_str!("../shaders/simple.wgsl");
79 
80 #[tokio::test]
81 async fn test_shader_compilation() {
82 let dm = DeviceManager::new(Backends::all())
83 .await
84 .expect("Failed to create device");
85 
86 let shader = ShaderManager::from_wgsl(dm.device(), SIMPLE_WGSL, Some("Test Shader"));
87 
88 assert!(shader.is_ok());
89 let shader = shader.unwrap();
90 assert_eq!(shader.vertex_entry(), "vs_main");
91 assert_eq!(shader.fragment_entry(), "fs_main");
92 }
93 
94 #[tokio::test]
95 #[should_panic(expected = "Shader")]
96 async fn test_invalid_wgsl_rejection() {
97 let dm = DeviceManager::new(Backends::all())
98 .await
99 .expect("Failed to create device");
100 
101 // wgpu DOES validate during shader module creation and will panic
102 // This test ensures we get expected behavior (panic on invalid WGSL)
103 let invalid_source = "this is not valid WGSL!!!";
104 let _result = ShaderManager::from_wgsl(dm.device(), invalid_source, Some("Invalid"));
105 
106 // Should panic before reaching here
107 }
108 
109 #[tokio::test]
110 async fn test_entry_point_detection() {
111 let dm = DeviceManager::new(Backends::all())
112 .await
113 .expect("Failed to create device");
114 
115 let shader = ShaderManager::from_wgsl(dm.device(), SIMPLE_WGSL, None)
116 .expect("Shader compilation failed");
117 
118 // Verify entry points are correct
119 assert_eq!(shader.vertex_entry(), "vs_main");
120 assert_eq!(shader.fragment_entry(), "fs_main");
121 }
122}
123