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/surface.rs
StratoSDK / crates / strato-renderer / src / gpu / surface.rs
1//! Surface management for window integration
2//!
3//! BLOCCO 2: Surface Configuration
4//! Handles wgpu Surface creation, configuration, and resize management
5 
6use std::sync::Arc;
7use wgpu::{
8 Adapter, Device, PresentMode, Surface, SurfaceConfiguration, SurfaceError, SurfaceTexture,
9 TextureFormat, TextureUsages,
10};
11 
12/// Manages wgpu surface and its configuration
13pub struct SurfaceManager {
14 surface: Surface<'static>,
15 config: SurfaceConfiguration,
16 format: TextureFormat,
17}
18 
19impl SurfaceManager {
20 /// Create a new surface manager
21 ///
22 /// # Arguments
23 /// * `surface` - The WGPU surface
24 /// * `device` - GPU device
25 /// * `adapter` - GPU adapter for capability detection
26 /// * `width` - Initial width
27 /// * `height` - Initial height
28 ///
29 /// # Errors
30 /// Returns error if surface configuration fails
31 pub fn new(
32 surface: Surface<'static>,
33 device: &Device,
34 adapter: &Adapter,
35 width: u32,
36 height: u32,
37 ) -> anyhow::Result<Self> {
38 // Get surface capabilities
39 let capabilities = surface.get_capabilities(adapter);
40 
41 // Select best format (prefer sRGB)
42 let format = capabilities
43 .formats
44 .iter()
45 .copied()
46 .find(|f| f.is_srgb())
47 .unwrap_or(capabilities.formats[0]);
48 
49 println!("=== SURFACE CONFIGURATION ===");
50 println!("Selected format: {:?}", format);
51 println!("Available formats: {:?}", capabilities.formats);
52 println!("Alpha modes: {:?}", capabilities.alpha_modes);
53 println!("============================");
54 
55 // Ensure valid dimensions
56 let width = width.max(1);
57 let height = height.max(1);
58 
59 // Configure surface
60 let config = SurfaceConfiguration {
61 usage: TextureUsages::RENDER_ATTACHMENT,
62 format,
63 width,
64 height,
65 present_mode: PresentMode::AutoVsync,
66 alpha_mode: capabilities.alpha_modes[0],
67 view_formats: vec![],
68 desired_maximum_frame_latency: 2,
69 };
70 
71 surface.configure(device, &config);
72 
73 println!("Surface configured: {}x{}", width, height);
74 
75 Ok(Self {
76 surface,
77 config,
78 format,
79 })
80 }
81 
82 /// Resize the surface
83 ///
84 /// # Arguments
85 /// * `width` - New width (must be > 0)
86 /// * `height` - New height (must be > 0)
87 /// * `device` - GPU device to reconfigure surface
88 pub fn resize(&mut self, width: u32, height: u32, device: &Device) -> anyhow::Result<()> {
89 if width == 0 || height == 0 {
90 return Err(anyhow::anyhow!(
91 "Invalid surface dimensions: {}x{}",
92 width,
93 height
94 ));
95 }
96 
97 self.config.width = width;
98 self.config.height = height;
99 self.surface.configure(device, &self.config);
100 
101 println!("Surface resized to: {}x{}", width, height);
102 
103 Ok(())
104 }
105 
106 /// Get current surface texture for rendering
107 pub fn get_current_texture(&mut self) -> Result<SurfaceTexture, SurfaceError> {
108 self.surface.get_current_texture()
109 }
110 
111 /// Get surface format
112 pub fn format(&self) -> TextureFormat {
113 self.format
114 }
115 
116 /// Get surface width
117 pub fn width(&self) -> u32 {
118 self.config.width
119 }
120 
121 /// Get surface height
122 pub fn height(&self) -> u32 {
123 self.config.height
124 }
125 
126 /// Get surface configuration (for advanced usage)
127 pub fn config(&self) -> &SurfaceConfiguration {
128 &self.config
129 }
130}
131 
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use crate::gpu::DeviceManager;
136 use wgpu::Backends;
137 use winit::dpi::PhysicalSize;
138 use winit::event_loop::EventLoop;
139 
140 // Note: These tests require a window which needs an event loop
141 // They may fail in headless CI environments
142 
143 #[tokio::test]
144 #[ignore] // Requires window system
145 async fn test_surface_creation() {
146 let event_loop = EventLoop::new().unwrap();
147 let window = Arc::new(
148 winit::window::WindowBuilder::new()
149 .with_inner_size(PhysicalSize::new(800, 600))
150 .with_visible(false)
151 .build(&event_loop)
152 .unwrap(),
153 );
154 
155 let dm = DeviceManager::new(Backends::all()).await.unwrap();
156 let surface = dm.instance().create_surface(window.clone()).unwrap();
157 // Safety: for test purposes
158 let surface: Surface<'static> = unsafe { std::mem::transmute(surface) };
159 
160 let surface_mgr = SurfaceManager::new(surface, dm.device(), dm.adapter(), 800, 600);
161 
162 assert!(surface_mgr.is_ok());
163 }
164 
165 #[tokio::test]
166 #[ignore] // Requires window system
167 async fn test_surface_format() {
168 let event_loop = EventLoop::new().unwrap();
169 let window = Arc::new(
170 winit::window::WindowBuilder::new()
171 .with_inner_size(PhysicalSize::new(800, 600))
172 .with_visible(false)
173 .build(&event_loop)
174 .unwrap(),
175 );
176 
177 let dm = DeviceManager::new(Backends::all()).await.unwrap();
178 let surface = dm.instance().create_surface(window.clone()).unwrap();
179 // Safety: for test purposes
180 let surface: Surface<'static> = unsafe { std::mem::transmute(surface) };
181 
182 let surface_mgr =
183 SurfaceManager::new(surface, dm.device(), dm.adapter(), 800, 600).unwrap();
184 
185 let format = surface_mgr.format();
186 assert!(matches!(
187 format,
188 TextureFormat::Bgra8UnormSrgb | TextureFormat::Rgba8UnormSrgb
189 ));
190 }
191 
192 #[test]
193 fn test_resize_validation() {
194 // Test that resize validates dimensions without requiring actual surface
195 // This is a unit test for the validation logic
196 let result_zero_width = validate_dimensions(0, 600);
197 let result_zero_height = validate_dimensions(800, 0);
198 let result_valid = validate_dimensions(800, 600);
199 
200 assert!(result_zero_width.is_err());
201 assert!(result_zero_height.is_err());
202 assert!(result_valid.is_ok());
203 }
204 
205 // Helper function for unit testing dimension validation
206 fn validate_dimensions(width: u32, height: u32) -> anyhow::Result<()> {
207 if width == 0 || height == 0 {
208 return Err(anyhow::anyhow!(
209 "Invalid surface dimensions: {}x{}",
210 width,
211 height
212 ));
213 }
214 Ok(())
215 }
216}
217