use crate::pipeline::{IntermediateLayer, Pipeline};
use crate::renderer::RenderContext;
use crate::utils::{DirtyRegion, RenderError};
#[cfg(feature = "nostd")]
use alloc::{boxed::Box, format, vec::Vec};
#[cfg(not(feature = "nostd"))]
use std::{boxed::Box, vec::Vec};
#[cfg(feature = "software-backend")]
pub mod blur;
#[cfg(feature = "software-backend")]
pub mod coverage;
#[cfg(feature = "software-backend")]
pub mod geometry;
#[cfg(feature = "software-backend")]
pub mod raster;
#[cfg(feature = "software-backend")]
pub mod software;
#[cfg(feature = "gpu")]
pub mod gpu;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BackendType {
Auto,
Software,
Gpu,
}
impl BackendType {
pub fn as_str(&self) -> &'static str {
match self {
Self::Auto => "Auto",
Self::Software => "Software",
Self::Gpu => "Gpu",
}
}
}
pub trait RenderBackend: Send + Sync {
fn backend_type(&self) -> BackendType;
fn create_pipeline(&self) -> Result<Box<dyn Pipeline>, RenderError>;
fn composite_layers(
&mut self,
layers: &[IntermediateLayer],
context: &RenderContext,
) -> Result<Vec<u8>, RenderError>;
fn composite_layers_incremental(
&mut self,
layers: &[IntermediateLayer],
dirty_regions: &[DirtyRegion],
previous_frame: &[u8],
context: &RenderContext,
) -> Result<Vec<u8>, RenderError> {
let _ = (dirty_regions, previous_frame);
self.composite_layers(layers, context)
}
#[cfg(feature = "software-backend")]
fn render_layers_to_bitmaps(
&mut self,
layers: &[IntermediateLayer],
context: &RenderContext,
) -> Result<Vec<crate::backends::coverage::RenderBitmap>, RenderError> {
let _ = (layers, context);
Err(RenderError::BackendError(
"bitmap-list output not supported by this backend".into(),
))
}
fn supports_feature(&self, feature: BackendFeature) -> bool {
match feature {
BackendFeature::IncrementalRendering => false,
BackendFeature::HardwareAcceleration => false,
BackendFeature::ComputeShaders => false,
BackendFeature::AsyncRendering => false,
}
}
#[cfg(feature = "backend-metrics")]
fn metrics(&self) -> Option<BackendMetrics> {
None
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BackendFeature {
IncrementalRendering,
HardwareAcceleration,
ComputeShaders,
AsyncRendering,
}
#[cfg(feature = "backend-metrics")]
#[derive(Debug, Clone)]
pub struct BackendMetrics {
pub vram_usage: u64,
pub draw_calls: usize,
pub batch_threshold: usize,
pub avg_frame_time_ms: f32,
pub peak_frame_time_ms: f32,
}
#[cfg(feature = "backend-metrics")]
impl BackendMetrics {
pub fn new() -> Self {
Self {
vram_usage: 0,
draw_calls: 0,
batch_threshold: 100,
avg_frame_time_ms: 0.0,
peak_frame_time_ms: 0.0,
}
}
}
#[cfg(feature = "backend-metrics")]
impl Default for BackendMetrics {
fn default() -> Self {
Self::new()
}
}
pub fn create_backend(
backend_type: BackendType,
width: u32,
height: u32,
) -> Result<Box<dyn RenderBackend>, RenderError> {
match backend_type {
BackendType::Auto => {
#[cfg(feature = "software-backend")]
return create_backend(BackendType::Software, width, height);
#[allow(unreachable_code)]
Err(RenderError::BackendError("No backend available".into()))
}
#[cfg(feature = "software-backend")]
BackendType::Software => {
let context = crate::renderer::RenderContext::new(width, height);
let backend = software::SoftwareBackend::new(&context)?;
Ok(Box::new(backend))
}
#[cfg(feature = "gpu")]
BackendType::Gpu => {
let backend = gpu::GpuBackend::new(width, height)?;
Ok(Box::new(backend))
}
#[allow(unreachable_patterns)]
_ => {
let backend_name = backend_type.as_str();
Err(RenderError::BackendError(format!(
"{backend_name} backend not available in this build"
)))
}
}
}