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/integration.rs
StratoSDK / crates / strato-renderer / src / integration.rs
1//! Integration module for coordinating all advanced wgpu systems
2//!
3//! This module provides a unified API that coordinates all the advanced systems:
4//! - Device management with automatic fallback
5//! - Resource management with intelligent pooling
6//! - Memory management with multi-tier allocation
7//! - Shader management with hot-reload
8//! - Pipeline management with render graph optimization
9//! - Buffer management with lock-free operations
10//! - Performance profiling and monitoring
11//!
12//! The integration layer ensures all systems work together seamlessly and provides
13//! a clean, easy-to-use API for the rest of the framework.
14 
15use anyhow::{Context, Result};
16use slotmap::DefaultKey;
17use std::sync::Arc;
18use tracing::{debug, info, instrument, warn};
19use wgpu::*;
20 
21use crate::{
22 buffer::BufferManager,
23 device::{DeviceManager, ManagedDevice},
24 memory::{AllocationStrategy, MemoryManager},
25 pipeline::PipelineManager,
26 profiler::{PerformanceReport, Profiler},
27 resources::{ResourceHandle, ResourceManager},
28 shader::{CompiledShader, ShaderManager},
29};
30 
31/// Configuration for the integrated renderer system
32#[derive(Debug, Clone)]
33pub struct RendererConfig {
34 /// Enable performance profiling
35 pub enable_profiling: bool,
36 /// Enable detailed profiling (higher overhead)
37 pub detailed_profiling: bool,
38 /// Enable automatic performance analysis
39 pub auto_analysis: bool,
40 /// Memory allocation strategy
41 pub memory_strategy: AllocationStrategy,
42 /// Maximum memory pool size in bytes
43 pub max_memory_pool_size: u64,
44 /// Enable shader hot-reload in debug builds
45 pub enable_shader_hot_reload: bool,
46 /// Preferred GPU adapter type
47 pub preferred_adapter: Option<PowerPreference>,
48 /// Enable validation layers
49 pub enable_validation: bool,
50 /// Maximum number of frames in flight
51 pub max_frames_in_flight: u32,
52}
53 
54impl Default for RendererConfig {
55 fn default() -> Self {
56 Self {
57 enable_profiling: cfg!(debug_assertions),
58 detailed_profiling: false,
59 auto_analysis: true,
60 memory_strategy: AllocationStrategy::Balanced,
61 max_memory_pool_size: 512 * 1024 * 1024, // 512MB
62 enable_shader_hot_reload: cfg!(debug_assertions),
63 preferred_adapter: Some(PowerPreference::HighPerformance),
64 enable_validation: cfg!(debug_assertions),
65 max_frames_in_flight: 2,
66 }
67 }
68}
69 
70/// Integrated renderer system that coordinates all subsystems
71pub struct IntegratedRenderer {
72 // Core systems
73 device_manager: Arc<DeviceManager>,
74 device: Arc<ManagedDevice>,
75 
76 // Management systems
77 resource_manager: Arc<ResourceManager>,
78 memory_manager: Arc<parking_lot::Mutex<MemoryManager>>,
79 shader_manager: Arc<ShaderManager>,
80 buffer_manager: Arc<BufferManager>,
81 pipeline_manager: Arc<PipelineManager>,
82 
83 // Monitoring
84 profiler: Option<Arc<Profiler>>,
85 
86 // Configuration
87 config: RendererConfig,
88 
89 // State
90 initialized: bool,
91 frame_count: u64,
92}
93 
94/// Render context for a single frame
95pub struct RenderContext {
96 pub device: Arc<ManagedDevice>,
97 pub encoder: CommandEncoder,
98 pub profiler: Option<Arc<Profiler>>,
99 pub frame_id: u64,
100 
101 // Timing queries
102 gpu_timer_id: Option<u32>,
103}
104 
105/// Render statistics for monitoring
106#[derive(Debug, Clone)]
107pub struct RenderStats {
108 pub frame_count: u64,
109 pub average_frame_time: f64,
110 pub memory_usage: u64,
111 pub active_resources: u32,
112 pub shader_reloads: u32,
113 pub pipeline_switches: u32,
114}
115 
116impl IntegratedRenderer {
117 /// Create a new integrated renderer with default configuration
118 pub async fn new() -> Result<Self> {
119 Self::with_config(RendererConfig::default(), None, None).await
120 }
121 
122 /// Create a new integrated renderer with custom configuration
123 pub async fn with_config(
124 config: RendererConfig,
125 instance: Option<Instance>,
126 surface: Option<&Surface<'_>>,
127 ) -> Result<Self> {
128 info!("Initializing integrated renderer system");
129 
130 // Initialize device manager
131 let device_manager = Arc::new(DeviceManager::new(instance, surface).await?);
132 
133 // Configure device selection based on renderer config
134 let mut criteria = crate::device::DeviceSelectionCriteria::default();
135 
136 // Check feature support
137 let has_timestamp = device_manager
138 .adapters()
139 .iter()
140 .any(|(_, caps)| caps.supported_features.contains(Features::TIMESTAMP_QUERY));
141 let has_pipeline_stats = device_manager.adapters().iter().any(|(_, caps)| {
142 caps.supported_features
143 .contains(Features::PIPELINE_STATISTICS_QUERY)
144 });
145 
146 if config.enable_profiling {
147 if has_timestamp {
148 criteria.require_timestamp_queries = true;
149 } else {
150 warn!("Timestamp queries not supported. GPU profiling will be disabled.");
151 }
152 }
153 
154 if config.detailed_profiling {
155 if has_pipeline_stats {
156 criteria.require_pipeline_statistics = true;
157 } else {
158 warn!("Pipeline statistics not supported. Detailed profiling will be disabled.");
159 }
160 }
161 
162 // Use preferred adapter config if possible (DeviceSelectionCriteria doesn't directly map PowerPreference yet)
163 
164 device_manager.update_selection_criteria(criteria);
165 
166 // Initialize the device before getting it
167 device_manager.initialize_device().await?;
168 
169 // Get the best available device
170 let device = device_manager
171 .get_best_device()
172 .context("Failed to get GPU device")?;
173 
174 info!("Selected GPU device: {}", device.capabilities.device_name);
175 
176 // Initialize management systems
177 let resource_manager = Arc::new(ResourceManager::new(device.clone())?);
178 let memory_manager = MemoryManager::new(device.clone());
179 
180 let shader_manager = Arc::new(ShaderManager::new(device.clone())?);
181 
182 let memory_manager_shared = Arc::new(parking_lot::Mutex::new(memory_manager));
183 let buffer_manager = Arc::new(BufferManager::new(
184 device.clone(),
185 memory_manager_shared.clone(),
186 ));
187 
188 let pipeline_manager = Arc::new(PipelineManager::new(
189 &device.device,
190 wgpu::TextureFormat::Bgra8UnormSrgb, // Default surface format
191 ));
192 
193 // Initialize profiler if enabled
194 let profiler = if config.enable_profiling {
195 let profiler = Arc::new(Profiler::new(device.clone())?);
196 profiler.set_detailed_profiling(config.detailed_profiling);
197 Some(profiler)
198 } else {
199 None
200 };
201 
202 Ok(Self {
203 device_manager,
204 device,
205 resource_manager,
206 memory_manager: memory_manager_shared,
207 shader_manager,
208 buffer_manager,
209 pipeline_manager,
210 profiler,
211 config,
212 initialized: false,
213 frame_count: 0,
214 })
215 }
216 
217 /// Initialize the renderer (call after window creation)
218 #[instrument(skip(self))]
219 pub async fn initialize(&mut self) -> Result<()> {
220 if self.initialized {
221 warn!("Renderer already initialized");
222 return Ok(());
223 }
224 
225 info!("Initializing renderer subsystems");
226 
227 // Initialize shader manager (load default shaders)
228 self.shader_manager.initialize()?;
229 
230 // Initialize pipeline manager (create default pipelines)
231 self.pipeline_manager.initialize()?;
232 
233 // Initialize buffer manager (create default pools)
234 self.buffer_manager.initialize()?;
235 
236 self.initialized = true;
237 info!("Renderer initialization complete");
238 
239 Ok(())
240 }
241 
242 /// Begin a new frame
243 #[instrument(skip(self))]
244 pub fn begin_frame(&mut self) -> Result<RenderContext> {
245 if !self.initialized {
246 return Err(anyhow::anyhow!("Renderer not initialized"));
247 }
248 
249 self.frame_count += 1;
250 
251 // Begin profiling if enabled
252 if let Some(ref profiler) = self.profiler {
253 profiler.begin_frame();
254 }
255 
256 // Create command encoder
257 let encoder = self
258 .device
259 .device
260 .create_command_encoder(&CommandEncoderDescriptor {
261 label: Some(&format!("Frame {}", self.frame_count)),
262 });
263 
264 // Begin GPU timing
265 let gpu_timer_id = if let Some(ref _profiler) = self.profiler {
266 // Note: encoder is moved, so we need to handle this differently
267 None // Placeholder - would need to restructure for actual GPU timing
268 } else {
269 None
270 };
271 
272 Ok(RenderContext {
273 device: self.device.clone(),
274 encoder,
275 profiler: self.profiler.clone(),
276 frame_id: self.frame_count,
277 gpu_timer_id,
278 })
279 }
280 
281 /// End the current frame and submit commands
282 #[instrument(skip(self, context))]
283 pub fn end_frame(&mut self, context: RenderContext) -> Result<()> {
284 // End GPU timing if active
285 if let (Some(_profiler), Some(_timer_id)) = (&context.profiler, context.gpu_timer_id) {
286 // profiler.end_gpu_timing(&mut context.encoder, timer_id);
287 }
288 
289 // Submit command buffer
290 let command_buffer = context.encoder.finish();
291 self.device.queue.submit(std::iter::once(command_buffer));
292 
293 // End profiling if enabled
294 if let Some(ref profiler) = self.profiler {
295 profiler.end_frame();
296 }
297 
298 // Perform maintenance tasks periodically
299 if self.frame_count % 60 == 0 {
300 self.perform_maintenance()?;
301 }
302 
303 Ok(())
304 }
305 
306 /// Get render statistics
307 pub fn get_stats(&self) -> RenderStats {
308 let memory_usage = self.memory_manager.lock().get_total_allocated();
309 let active_resources = self.resource_manager.get_active_count();
310 
311 let (average_frame_time, shader_reloads, pipeline_switches) =
312 if let Some(ref profiler) = self.profiler {
313 let report = profiler.get_performance_report();
314 (
315 report.frame_stats.average_frame_time,
316 0, // Would need to track in shader manager
317 0, // Would need to track in pipeline manager
318 )
319 } else {
320 (0.0, 0, 0)
321 };
322 
323 RenderStats {
324 frame_count: self.frame_count,
325 average_frame_time,
326 memory_usage,
327 active_resources: active_resources.try_into().unwrap(),
328 shader_reloads,
329 pipeline_switches,
330 }
331 }
332 
333 /// Get performance report (if profiling is enabled)
334 pub fn get_performance_report(&self) -> Option<PerformanceReport> {
335 self.profiler.as_ref().map(|p| p.get_performance_report())
336 }
337 
338 /// Create a buffer with automatic management
339 pub fn create_buffer(&self, size: u64, usage: BufferUsages) -> Result<ResourceHandle> {
340 let config = crate::buffer::BufferConfig {
341 name: "auto_buffer".to_string(),
342 size,
343 usage,
344 usage_pattern: crate::buffer::BufferUsagePattern::Dynamic,
345 allocation_strategy: crate::buffer::AllocationStrategy::BestFit,
346 alignment: 256,
347 mapped_at_creation: false,
348 persistent_mapping: false,
349 };
350 self.buffer_manager.create_buffer(&config)
351 }
352 
353 /// Get a buffer by handle
354 pub fn get_buffer(&self, handle: ResourceHandle) -> Option<Arc<Buffer>> {
355 self.buffer_manager.get_buffer(handle)
356 }
357 
358 /// Create a texture with automatic management
359 pub fn create_texture(&self, descriptor: &TextureDescriptor) -> DefaultKey {
360 self.resource_manager.create_texture(descriptor)
361 }
362 
363 /// Load and compile a shader
364 pub fn load_shader(
365 &self,
366 path: &std::path::Path,
367 stage: crate::shader::ShaderStage,
368 variant: crate::shader::ShaderVariant,
369 ) -> Result<Arc<crate::shader::CompiledShader>> {
370 self.shader_manager.load_shader(path, stage, variant)
371 }
372 
373 /// Create a render pipeline
374 pub fn create_render_pipeline(&self) -> Result<()> {
375 self.pipeline_manager.create_render_pipeline()
376 }
377 
378 /// Get the device manager
379 pub fn device_manager(&self) -> &Arc<DeviceManager> {
380 &self.device_manager
381 }
382 
383 /// Get the active adapter used by the renderer
384 pub fn get_active_adapter(&self) -> Option<&wgpu::Adapter> {
385 self.device_manager.get_active_adapter()
386 }
387 
388 /// Get device information
389 pub fn get_device_info(&self) -> &crate::device::GpuCapabilities {
390 &self.device.capabilities
391 }
392 
393 /// Get the managed device
394 pub fn device(&self) -> &ManagedDevice {
395 &self.device
396 }
397 
398 /// Check if a feature is supported
399 pub fn supports_feature(&self, feature: Features) -> bool {
400 self.device
401 .capabilities
402 .supported_features
403 .contains(feature)
404 }
405 
406 /// Perform maintenance tasks
407 #[instrument(skip(self))]
408 fn perform_maintenance(&mut self) -> Result<()> {
409 debug!("Performing maintenance tasks");
410 
411 // Clean up unused resources
412 self.resource_manager.cleanup_unused();
413 
414 // Defragment memory pools
415 let _ = self.memory_manager.lock().defragment();
416 
417 // Check for shader hot-reloads
418 if self.config.enable_shader_hot_reload {
419 if let Err(e) = self.shader_manager.check_for_reloads() {
420 warn!("Shader hot-reload check failed: {}", e);
421 }
422 }
423 
424 // Collect garbage in buffer pools
425 self.buffer_manager.collect_garbage();
426 
427 Ok(())
428 }
429 
430 /// Resize surface (call when window is resized)
431 pub fn resize(&mut self, new_size: (u32, u32)) -> Result<()> {
432 info!("Resizing renderer to {}x{}", new_size.0, new_size.1);
433 
434 // Update any size-dependent resources
435 // This would typically involve recreating swap chain, depth buffers, etc.
436 
437 Ok(())
438 }
439 
440 /// Shutdown the renderer gracefully
441 #[instrument(skip(self))]
442 pub fn shutdown(&mut self) {
443 info!("Shutting down integrated renderer");
444 
445 if let Some(ref profiler) = self.profiler {
446 let report = profiler.get_performance_report();
447 info!("Final performance report: {:#?}", report);
448 }
449 
450 // Cleanup resources
451 self.resource_manager.cleanup_all();
452 self.memory_manager.lock().cleanup();
453 
454 self.initialized = false;
455 info!("Renderer shutdown complete");
456 }
457}
458 
459impl Drop for IntegratedRenderer {
460 fn drop(&mut self) {
461 if self.initialized {
462 self.shutdown();
463 }
464 }
465}
466 
467impl RenderContext {
468 /// Begin a render pass with profiling
469 pub fn begin_render_pass<'a>(
470 &'a mut self,
471 descriptor: &RenderPassDescriptor<'a, '_>,
472 ) -> RenderPass<'a> {
473 // Begin CPU timing if profiler is available
474 if let Some(ref profiler) = self.profiler {
475 profiler.cpu_profiler.begin_section("render_pass");
476 }
477 
478 self.encoder.begin_render_pass(descriptor)
479 }
480 
481 /// End render pass timing
482 pub fn end_render_pass(&self) {
483 if let Some(ref profiler) = self.profiler {
484 profiler.cpu_profiler.end_section("render_pass");
485 }
486 }
487 
488 /// Begin compute pass with profiling
489 pub fn begin_compute_pass<'a>(
490 &'a mut self,
491 descriptor: &ComputePassDescriptor<'a>,
492 ) -> ComputePass<'a> {
493 if let Some(ref profiler) = self.profiler {
494 profiler.cpu_profiler.begin_section("compute_pass");
495 }
496 
497 self.encoder.begin_compute_pass(descriptor)
498 }
499 
500 /// End compute pass timing
501 pub fn end_compute_pass(&self) {
502 if let Some(ref profiler) = self.profiler {
503 profiler.cpu_profiler.end_section("compute_pass");
504 }
505 }
506}
507 
508/// Builder for creating an integrated renderer with custom configuration
509pub struct RendererBuilder<'a> {
510 config: RendererConfig,
511 instance: Option<Instance>,
512 surface: Option<&'a Surface<'a>>,
513}
514 
515impl<'a> RendererBuilder<'a> {
516 /// Create a new renderer builder
517 pub fn new() -> Self {
518 Self {
519 config: RendererConfig::default(),
520 instance: None,
521 surface: None,
522 }
523 }
524 
525 /// Set the surface for compatibility checking
526 pub fn with_surface(mut self, surface: &'a Surface<'a>) -> Self {
527 self.surface = Some(surface);
528 self
529 }
530 
531 /// Set the wgpu Instance to use
532 pub fn with_instance(mut self, instance: Instance) -> Self {
533 self.instance = Some(instance);
534 self
535 }
536 
537 /// Enable or disable profiling
538 pub fn with_profiling(mut self, enabled: bool) -> Self {
539 self.config.enable_profiling = enabled;
540 self
541 }
542 
543 /// Enable or disable detailed profiling
544 pub fn with_detailed_profiling(mut self, enabled: bool) -> Self {
545 self.config.detailed_profiling = enabled;
546 self
547 }
548 
549 /// Set memory allocation strategy
550 pub fn with_memory_strategy(mut self, strategy: AllocationStrategy) -> Self {
551 self.config.memory_strategy = strategy;
552 self
553 }
554 
555 /// Set maximum memory pool size
556 pub fn with_max_memory_pool_size(mut self, size: u64) -> Self {
557 self.config.max_memory_pool_size = size;
558 self
559 }
560 
561 /// Set preferred GPU adapter
562 pub fn with_preferred_adapter(mut self, preference: PowerPreference) -> Self {
563 self.config.preferred_adapter = Some(preference);
564 self
565 }
566 
567 /// Enable or disable validation layers
568 pub fn with_validation(mut self, enabled: bool) -> Self {
569 self.config.enable_validation = enabled;
570 self
571 }
572 
573 /// Build the integrated renderer
574 pub async fn build(self) -> Result<IntegratedRenderer> {
575 IntegratedRenderer::with_config(self.config, self.instance, self.surface).await
576 }
577}
578 
579impl Default for RendererBuilder<'_> {
580 fn default() -> Self {
581 Self::new()
582 }
583}
584 
585/// Convenience macro for creating a renderer with common configurations
586#[macro_export]
587macro_rules! create_renderer {
588 (debug) => {
589 RendererBuilder::new()
590 .with_profiling(true)
591 .with_detailed_profiling(true)
592 .with_validation(true)
593 .build()
594 .await
595 };
596 
597 (release) => {
598 RendererBuilder::new()
599 .with_profiling(false)
600 .with_validation(false)
601 .build()
602 .await
603 };
604 
605 (performance) => {
606 RendererBuilder::new()
607 .with_profiling(true)
608 .with_detailed_profiling(false)
609 .with_preferred_adapter(PowerPreference::HighPerformance)
610 .with_memory_strategy(AllocationStrategy::Performance)
611 .build()
612 .await
613 };
614}
615