#![forbid(unsafe_code)]
#![deny(missing_docs)]
#![deny(clippy::all)]
#![deny(clippy::pedantic)]
#![allow(clippy::module_name_repetitions)]
pub mod backend;
#[cfg(feature = "charts")]
pub mod chart;
pub mod error;
pub mod holographic;
#[cfg(feature = "images")]
pub mod image;
pub mod quilt;
pub mod spatial;
#[cfg(feature = "images")]
pub mod texture_cache;
#[cfg(feature = "gpu")]
pub mod video;
pub use backend::RenderBackend;
pub use error::{RenderError, RenderResult};
pub use holographic::{
HoloPlayInfo, HolographicRenderResult, HolographicRenderer, HolographicStats,
};
pub use quilt::{LookingGlassPreset, Quilt, QuiltRenderSettings, QuiltRenderTarget, QuiltView};
pub use spatial::{Camera, HolographicConfig, Mat4, QuiltRenderInfo, Vec3};
#[cfg(feature = "gpu")]
pub use video::{
VideoFrameData, VideoTextureEntry, VideoTextureError, VideoTextureManager, VideoTextureResult,
};
use canvas_core::Scene;
#[derive(Debug, Clone)]
pub struct RendererConfig {
pub preferred_backend: BackendType,
pub target_fps: u32,
pub anti_aliasing: bool,
pub background_color: [f32; 4],
}
impl Default for RendererConfig {
fn default() -> Self {
Self {
preferred_backend: BackendType::WebGpu,
target_fps: 60,
anti_aliasing: true,
background_color: [1.0, 1.0, 1.0, 1.0], }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BackendType {
WebGpu,
WebGl2,
Canvas2D,
}
pub struct Renderer {
config: RendererConfig,
backend: Box<dyn RenderBackend>,
frame_count: u64,
}
impl Renderer {
pub fn new(config: RendererConfig) -> RenderResult<Self> {
let backend = Self::create_backend(&config)?;
Ok(Self::with_backend(backend, config))
}
#[must_use]
pub fn with_backend(backend: Box<dyn RenderBackend>, config: RendererConfig) -> Self {
Self {
config,
backend,
frame_count: 0,
}
}
fn create_backend(config: &RendererConfig) -> RenderResult<Box<dyn RenderBackend>> {
match config.preferred_backend {
BackendType::WebGpu => {
#[cfg(feature = "gpu")]
{
match backend::wgpu::WgpuBackend::new() {
Ok(b) => return Ok(Box::new(b)),
Err(e) => {
tracing::warn!("WebGPU unavailable, falling back: {}", e);
}
}
}
Self::create_backend(&RendererConfig {
preferred_backend: BackendType::WebGl2,
..config.clone()
})
}
BackendType::WebGl2 => {
tracing::warn!("WebGL2 not yet implemented, falling back to 2D");
Self::create_backend(&RendererConfig {
preferred_backend: BackendType::Canvas2D,
..config.clone()
})
}
BackendType::Canvas2D => Ok(Box::new(backend::canvas2d::Canvas2DBackend::new())),
}
}
pub fn render(&mut self, scene: &Scene) -> RenderResult<()> {
self.backend.render(scene)?;
self.frame_count += 1;
Ok(())
}
#[must_use]
pub fn frame_count(&self) -> u64 {
self.frame_count
}
#[must_use]
pub fn active_backend(&self) -> BackendType {
self.backend.backend_type()
}
#[must_use]
pub fn config(&self) -> &RendererConfig {
&self.config
}
pub fn resize(&mut self, width: u32, height: u32) -> RenderResult<()> {
self.backend.resize(width, height)
}
}