orbit/renderer/
mod.rs

1//! Enhanced renderer module with performance optimizations and component integration
2
3// Renderer modules
4#[cfg(feature = "skia")]
5pub mod skia;
6pub mod wgpu;
7
8// Re-export renderer items
9#[cfg(feature = "skia")]
10pub use skia::{RendererError, RendererMessage, RendererResult, SkiaRenderer};
11
12use crate::component::{ComponentId, Node};
13use std::collections::HashMap;
14
15/// Types of renderers available
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum RendererType {
18    /// Skia-based renderer
19    Skia,
20    /// WGPU-based renderer
21    Wgpu,
22    /// WebGL-based renderer (for web)
23    WebGL,
24    /// Automatic selection based on platform
25    Auto,
26}
27
28/// Render context with performance optimizations
29#[derive(Debug, Default)]
30pub struct RenderContext {
31    /// Viewport dimensions
32    pub viewport_width: u32,
33    pub viewport_height: u32,
34    /// Device pixel ratio for high-DPI displays
35    pub device_pixel_ratio: f32,
36    /// Whether to enable vsync
37    pub vsync_enabled: bool,
38    /// Frame rate target
39    pub target_fps: u32,
40    /// Component dirty tracking for selective re-rendering
41    dirty_components: HashMap<ComponentId, bool>,
42}
43
44impl RenderContext {
45    /// Create new render context with default settings
46    pub fn new(width: u32, height: u32) -> Self {
47        Self {
48            viewport_width: width,
49            viewport_height: height,
50            device_pixel_ratio: 1.0,
51            vsync_enabled: true,
52            target_fps: 60,
53            dirty_components: HashMap::new(),
54        }
55    }
56
57    /// Mark a component as dirty (needing re-render)
58    pub fn mark_dirty(&mut self, component_id: ComponentId) {
59        self.dirty_components.insert(component_id, true);
60    }
61
62    /// Check if a component is dirty
63    pub fn is_dirty(&self, component_id: ComponentId) -> bool {
64        self.dirty_components
65            .get(&component_id)
66            .copied()
67            .unwrap_or(false)
68    }
69
70    /// Clear dirty flag for a component
71    pub fn mark_clean(&mut self, component_id: ComponentId) {
72        self.dirty_components.remove(&component_id);
73    }
74
75    /// Get all dirty components
76    pub fn get_dirty_components(&self) -> Vec<ComponentId> {
77        self.dirty_components.keys().copied().collect()
78    }
79
80    /// Clear all dirty flags
81    pub fn clear_all_dirty(&mut self) {
82        self.dirty_components.clear();
83    }
84}
85
86/// Render statistics for performance monitoring
87#[derive(Debug, Default, Clone)]
88pub struct RenderStats {
89    /// Number of frames rendered
90    pub frame_count: u64,
91    /// Average frame time in milliseconds
92    pub avg_frame_time_ms: f32,
93    /// Current FPS
94    pub current_fps: f32,
95    /// Number of draw calls in last frame
96    pub draw_calls: u32,
97    /// Number of vertices rendered in last frame
98    pub vertex_count: u32,
99    /// Number of components rendered in last frame
100    pub component_count: u32,
101}
102
103/// Enhanced renderer interface with performance monitoring
104pub trait Renderer {
105    /// Initialize the renderer
106    fn init(&mut self) -> Result<(), crate::Error> {
107        Ok(()) // Default implementation does nothing
108    }
109
110    /// Render a component tree with context and performance optimizations
111    fn render(&mut self, root: &Node, context: &mut RenderContext) -> Result<(), crate::Error>;
112
113    /// Render only dirty components for performance
114    fn render_selective(
115        &mut self,
116        root: &Node,
117        context: &mut RenderContext,
118        _dirty_components: &[ComponentId],
119    ) -> Result<(), crate::Error> {
120        // Default implementation falls back to full render
121        self.render(root, context)
122    }
123
124    /// Flush any pending operations
125    fn flush(&mut self) -> Result<(), crate::Error> {
126        Ok(()) // Default implementation does nothing
127    }
128
129    /// Clean up resources
130    fn cleanup(&mut self) -> Result<(), crate::Error> {
131        Ok(()) // Default implementation does nothing
132    }
133
134    /// Get the renderer name
135    fn name(&self) -> &str;
136
137    /// Get render statistics
138    fn get_stats(&self) -> RenderStats {
139        RenderStats::default()
140    }
141
142    /// Reset render statistics
143    fn reset_stats(&mut self) {}
144
145    /// Set render quality/performance level
146    fn set_quality_level(&mut self, _level: QualityLevel) -> Result<(), crate::Error> {
147        Ok(()) // Default implementation does nothing
148    }
149}
150
151/// Render quality levels for performance tuning
152#[derive(Debug, Clone, Copy, PartialEq, Eq)]
153pub enum QualityLevel {
154    /// Fastest rendering, lowest quality
155    Performance,
156    /// Balanced rendering and quality
157    Balanced,
158    /// Highest quality, slower rendering
159    Quality,
160}
161
162/// Create a renderer of the specified type
163pub fn create_renderer(renderer_type: RendererType) -> Result<Box<dyn Renderer>, crate::Error> {
164    match renderer_type {
165        RendererType::Skia => {
166            #[cfg(feature = "skia")]
167            {
168                let renderer = SkiaRenderer::new();
169                Ok(Box::new(renderer))
170            }
171            #[cfg(not(feature = "skia"))]
172            {
173                Err(crate::Error::Renderer(
174                    "Skia renderer not supported in this build".to_string(),
175                ))
176            }
177        }
178        RendererType::Wgpu => {
179            #[cfg(feature = "wgpu")]
180            {
181                let renderer = futures::executor::block_on(wgpu::WgpuRenderer::new())?;
182                Ok(Box::new(renderer))
183            }
184            #[cfg(not(feature = "wgpu"))]
185            {
186                Err(crate::Error::Renderer(
187                    "WGPU renderer not supported in this build".to_string(),
188                ))
189            }
190        }
191        RendererType::WebGL => {
192            #[cfg(feature = "web")]
193            {
194                // WebGL renderer is not yet implemented; this is a placeholder for future implementation
195                // The actual renderer would be defined in a webgl.rs module and imported
196                Err(crate::Error::Renderer(
197                    "WebGL renderer not yet implemented".to_string(),
198                ))
199            }
200            #[cfg(not(feature = "web"))]
201            {
202                Err(crate::Error::Renderer(
203                    "WebGL renderer not supported in this build".to_string(),
204                ))
205            }
206        }
207        RendererType::Auto => {
208            #[cfg(target_arch = "wasm32")]
209            {
210                #[cfg(feature = "web")]
211                {
212                    // WebGL renderer is not yet implemented; this is a placeholder for future implementation
213                    return Err(crate::Error::Renderer(
214                        "WebGL renderer not yet implemented".to_string(),
215                    ));
216                }
217                #[cfg(not(feature = "web"))]
218                {
219                    return Err(crate::Error::Renderer(
220                        "No supported renderer for web platform in this build".to_string(),
221                    ));
222                }
223            }
224
225            #[cfg(not(target_arch = "wasm32"))]
226            {
227                #[cfg(feature = "wgpu")]
228                {
229                    let renderer = futures::executor::block_on(wgpu::WgpuRenderer::new())?;
230                    Ok(Box::new(renderer))
231                }
232                #[cfg(not(feature = "wgpu"))]
233                {
234                    #[cfg(feature = "skia")]
235                    {
236                        let renderer = SkiaRenderer::new();
237                        Ok(Box::new(renderer))
238                    }
239                    #[cfg(not(feature = "skia"))]
240                    {
241                        Err(crate::Error::Renderer(
242                            "No renderer available - neither WGPU nor Skia enabled".to_string(),
243                        ))
244                    }
245                }
246            }
247        }
248    }
249}
250
251/// Enhanced renderer composition for hybrid UIs with performance monitoring
252pub struct CompositeRenderer {
253    /// The 2D renderer (usually Skia)
254    pub renderer_2d: Box<dyn Renderer>,
255
256    /// The 3D renderer (usually WGPU)
257    pub renderer_3d: Box<dyn Renderer>,
258
259    /// Combined render statistics
260    stats: RenderStats,
261}
262
263impl CompositeRenderer {
264    /// Create a new composite renderer with the specified renderers
265    pub fn new(renderer_2d: Box<dyn Renderer>, renderer_3d: Box<dyn Renderer>) -> Self {
266        Self {
267            renderer_2d,
268            renderer_3d,
269            stats: RenderStats::default(),
270        }
271    }
272
273    /// Create a default composite renderer
274    pub fn create_default() -> Result<Self, crate::Error> {
275        let renderer_2d = create_renderer(RendererType::Skia)?;
276        let renderer_3d = create_renderer(RendererType::Wgpu)?;
277
278        Ok(Self {
279            renderer_2d,
280            renderer_3d,
281            stats: RenderStats::default(),
282        })
283    }
284}
285
286impl Renderer for CompositeRenderer {
287    fn render(&mut self, root: &Node, context: &mut RenderContext) -> Result<(), crate::Error> {
288        let start_time = std::time::Instant::now();
289
290        // First render 3D elements
291        self.renderer_3d.render(root, context)?;
292
293        // Then render 2D elements on top
294        self.renderer_2d.render(root, context)?;
295
296        // Update statistics
297        let frame_time = start_time.elapsed().as_secs_f32() * 1000.0;
298        self.stats.frame_count += 1;
299        self.stats.avg_frame_time_ms = (self.stats.avg_frame_time_ms + frame_time) / 2.0;
300        self.stats.current_fps = 1000.0 / frame_time;
301
302        // Combine stats from both renderers
303        let stats_2d = self.renderer_2d.get_stats();
304        let stats_3d = self.renderer_3d.get_stats();
305        self.stats.draw_calls = stats_2d.draw_calls + stats_3d.draw_calls;
306        self.stats.vertex_count = stats_2d.vertex_count + stats_3d.vertex_count;
307        self.stats.component_count = stats_2d.component_count + stats_3d.component_count;
308
309        Ok(())
310    }
311
312    fn render_selective(
313        &mut self,
314        root: &Node,
315        context: &mut RenderContext,
316        dirty_components: &[ComponentId],
317    ) -> Result<(), crate::Error> {
318        // Try selective rendering on both renderers
319        self.renderer_3d
320            .render_selective(root, context, dirty_components)?;
321        self.renderer_2d
322            .render_selective(root, context, dirty_components)?;
323        Ok(())
324    }
325
326    fn name(&self) -> &str {
327        "Enhanced Composite Renderer"
328    }
329
330    fn get_stats(&self) -> RenderStats {
331        self.stats.clone()
332    }
333
334    fn reset_stats(&mut self) {
335        self.stats = RenderStats::default();
336        self.renderer_2d.reset_stats();
337        self.renderer_3d.reset_stats();
338    }
339
340    fn set_quality_level(&mut self, level: QualityLevel) -> Result<(), crate::Error> {
341        self.renderer_2d.set_quality_level(level)?;
342        self.renderer_3d.set_quality_level(level)?;
343        Ok(())
344    }
345}