1#![forbid(unsafe_code)]
17#![deny(missing_docs)]
18#![deny(clippy::all)]
19#![deny(clippy::pedantic)]
20#![allow(clippy::module_name_repetitions)]
21
22pub mod backend;
23#[cfg(feature = "charts")]
24pub mod chart;
25pub mod error;
26pub mod holographic;
27#[cfg(feature = "images")]
28pub mod image;
29pub mod quilt;
30pub mod spatial;
31#[cfg(feature = "images")]
32pub mod texture_cache;
33#[cfg(feature = "gpu")]
34pub mod video;
35
36pub use backend::RenderBackend;
37pub use error::{RenderError, RenderResult};
38pub use holographic::{
39 HoloPlayInfo, HolographicRenderResult, HolographicRenderer, HolographicStats,
40};
41pub use quilt::{LookingGlassPreset, Quilt, QuiltRenderSettings, QuiltRenderTarget, QuiltView};
42pub use spatial::{Camera, HolographicConfig, Mat4, QuiltRenderInfo, Vec3};
43#[cfg(feature = "gpu")]
44pub use video::{
45 VideoFrameData, VideoTextureEntry, VideoTextureError, VideoTextureManager, VideoTextureResult,
46};
47
48use canvas_core::Scene;
49
50#[derive(Debug, Clone)]
52pub struct RendererConfig {
53 pub preferred_backend: BackendType,
55 pub target_fps: u32,
57 pub anti_aliasing: bool,
59 pub background_color: [f32; 4],
61}
62
63impl Default for RendererConfig {
64 fn default() -> Self {
65 Self {
66 preferred_backend: BackendType::WebGpu,
67 target_fps: 60,
68 anti_aliasing: true,
69 background_color: [1.0, 1.0, 1.0, 1.0], }
71 }
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
76pub enum BackendType {
77 WebGpu,
79 WebGl2,
81 Canvas2D,
83}
84
85pub struct Renderer {
87 config: RendererConfig,
88 backend: Box<dyn RenderBackend>,
89 frame_count: u64,
90}
91
92impl Renderer {
93 pub fn new(config: RendererConfig) -> RenderResult<Self> {
99 let backend = Self::create_backend(&config)?;
100 Ok(Self::with_backend(backend, config))
101 }
102
103 #[must_use]
105 pub fn with_backend(backend: Box<dyn RenderBackend>, config: RendererConfig) -> Self {
106 Self {
107 config,
108 backend,
109 frame_count: 0,
110 }
111 }
112
113 fn create_backend(config: &RendererConfig) -> RenderResult<Box<dyn RenderBackend>> {
115 match config.preferred_backend {
116 BackendType::WebGpu => {
117 #[cfg(feature = "gpu")]
118 {
119 match backend::wgpu::WgpuBackend::new() {
120 Ok(b) => return Ok(Box::new(b)),
121 Err(e) => {
122 tracing::warn!("WebGPU unavailable, falling back: {}", e);
123 }
124 }
125 }
126 Self::create_backend(&RendererConfig {
128 preferred_backend: BackendType::WebGl2,
129 ..config.clone()
130 })
131 }
132 BackendType::WebGl2 => {
133 tracing::warn!("WebGL2 not yet implemented, falling back to 2D");
135 Self::create_backend(&RendererConfig {
136 preferred_backend: BackendType::Canvas2D,
137 ..config.clone()
138 })
139 }
140 BackendType::Canvas2D => Ok(Box::new(backend::canvas2d::Canvas2DBackend::new())),
141 }
142 }
143
144 pub fn render(&mut self, scene: &Scene) -> RenderResult<()> {
150 self.backend.render(scene)?;
151 self.frame_count += 1;
152 Ok(())
153 }
154
155 #[must_use]
157 pub fn frame_count(&self) -> u64 {
158 self.frame_count
159 }
160
161 #[must_use]
163 pub fn active_backend(&self) -> BackendType {
164 self.backend.backend_type()
165 }
166
167 #[must_use]
169 pub fn config(&self) -> &RendererConfig {
170 &self.config
171 }
172
173 pub fn resize(&mut self, width: u32, height: u32) -> RenderResult<()> {
179 self.backend.resize(width, height)
180 }
181}