1#[cfg(feature = "skia")]
5pub mod skia;
6pub mod wgpu;
7
8#[cfg(feature = "skia")]
10pub use skia::{RendererError, RendererMessage, RendererResult, SkiaRenderer};
11
12use crate::component::{ComponentId, Node};
13use std::collections::HashMap;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum RendererType {
18 Skia,
20 Wgpu,
22 WebGL,
24 Auto,
26}
27
28#[derive(Debug, Default)]
30pub struct RenderContext {
31 pub viewport_width: u32,
33 pub viewport_height: u32,
34 pub device_pixel_ratio: f32,
36 pub vsync_enabled: bool,
38 pub target_fps: u32,
40 dirty_components: HashMap<ComponentId, bool>,
42}
43
44impl RenderContext {
45 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 pub fn mark_dirty(&mut self, component_id: ComponentId) {
59 self.dirty_components.insert(component_id, true);
60 }
61
62 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 pub fn mark_clean(&mut self, component_id: ComponentId) {
72 self.dirty_components.remove(&component_id);
73 }
74
75 pub fn get_dirty_components(&self) -> Vec<ComponentId> {
77 self.dirty_components.keys().copied().collect()
78 }
79
80 pub fn clear_all_dirty(&mut self) {
82 self.dirty_components.clear();
83 }
84}
85
86#[derive(Debug, Default, Clone)]
88pub struct RenderStats {
89 pub frame_count: u64,
91 pub avg_frame_time_ms: f32,
93 pub current_fps: f32,
95 pub draw_calls: u32,
97 pub vertex_count: u32,
99 pub component_count: u32,
101}
102
103pub trait Renderer {
105 fn init(&mut self) -> Result<(), crate::Error> {
107 Ok(()) }
109
110 fn render(&mut self, root: &Node, context: &mut RenderContext) -> Result<(), crate::Error>;
112
113 fn render_selective(
115 &mut self,
116 root: &Node,
117 context: &mut RenderContext,
118 _dirty_components: &[ComponentId],
119 ) -> Result<(), crate::Error> {
120 self.render(root, context)
122 }
123
124 fn flush(&mut self) -> Result<(), crate::Error> {
126 Ok(()) }
128
129 fn cleanup(&mut self) -> Result<(), crate::Error> {
131 Ok(()) }
133
134 fn name(&self) -> &str;
136
137 fn get_stats(&self) -> RenderStats {
139 RenderStats::default()
140 }
141
142 fn reset_stats(&mut self) {}
144
145 fn set_quality_level(&mut self, _level: QualityLevel) -> Result<(), crate::Error> {
147 Ok(()) }
149}
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq)]
153pub enum QualityLevel {
154 Performance,
156 Balanced,
158 Quality,
160}
161
162pub 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 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 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
251pub struct CompositeRenderer {
253 pub renderer_2d: Box<dyn Renderer>,
255
256 pub renderer_3d: Box<dyn Renderer>,
258
259 stats: RenderStats,
261}
262
263impl CompositeRenderer {
264 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 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 self.renderer_3d.render(root, context)?;
292
293 self.renderer_2d.render(root, context)?;
295
296 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 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 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}