use awsm_renderer_core::buffers::{BufferDescriptor, BufferUsage};
use awsm_renderer_core::error::AwsmCoreError;
use awsm_renderer_core::renderer::AwsmRendererWebGpu;
use thiserror::Error;
use super::snapshot::FrameGlobalsSnapshot;
use crate::buffer::mapped_uploader::MappedUploader;
use crate::{AwsmRenderer, AwsmRendererLogging};
pub const DELTA_TIME_CLAMP_SECS: f32 = 0.25;
pub struct FrameGlobals {
pub gpu_buffer: web_sys::GpuBuffer,
raw_data: [u8; Self::BYTE_SIZE],
uploader: MappedUploader,
construction_ms: f64,
last_time: Option<f32>,
time_override: Option<f32>,
snapshot: FrameGlobalsSnapshot,
}
impl FrameGlobals {
pub const BYTE_SIZE: usize = 32;
pub fn new(gpu: &AwsmRendererWebGpu) -> std::result::Result<Self, AwsmFrameGlobalsError> {
let gpu_buffer = gpu.create_buffer(
&BufferDescriptor::new(
Some("FrameGlobals"),
Self::BYTE_SIZE,
BufferUsage::new().with_uniform().with_copy_dst(),
)
.into(),
)?;
let construction_ms = performance_now_ms();
Ok(Self {
gpu_buffer,
raw_data: [0; Self::BYTE_SIZE],
uploader: MappedUploader::new("FrameGlobals"),
construction_ms,
last_time: None,
time_override: None,
snapshot: FrameGlobalsSnapshot {
time: 0.0,
delta_time: 0.0,
frame_count: 0,
resolution: [0, 0],
},
})
}
pub fn upload_stats(&self) -> crate::buffer::mapped_staging_ring::UploadStats {
self.uploader.stats()
}
pub fn snapshot(&self) -> FrameGlobalsSnapshot {
self.snapshot
}
pub fn set_time_source(&mut self, time: f32) {
self.time_override = Some(time);
}
pub fn time_source(&self) -> Option<f32> {
self.time_override
}
pub fn clear_time_source(&mut self) {
self.time_override = None;
}
pub fn write_gpu(
&mut self,
logging: &AwsmRendererLogging,
gpu: &AwsmRendererWebGpu,
frame_count: u32,
resolution: [u32; 2],
) -> std::result::Result<(), AwsmFrameGlobalsError> {
let _maybe_span_guard = if logging.render_timings.sub_frame() {
Some(tracing::span!(tracing::Level::INFO, "FrameGlobals GPU write").entered())
} else {
None
};
let time = match self.time_override {
Some(t) => t,
None => {
let elapsed_ms = performance_now_ms() - self.construction_ms;
(elapsed_ms / 1000.0) as f32
}
};
let delta_time = match self.last_time {
Some(prev) => (time - prev).clamp(0.0, DELTA_TIME_CLAMP_SECS),
None => 0.0,
};
self.last_time = Some(time);
self.raw_data[0..4].copy_from_slice(&time.to_ne_bytes());
self.raw_data[4..8].copy_from_slice(&delta_time.to_ne_bytes());
self.raw_data[8..12].copy_from_slice(&frame_count.to_ne_bytes());
self.raw_data[12..16].copy_from_slice(&0u32.to_ne_bytes());
self.raw_data[16..20].copy_from_slice(&resolution[0].to_ne_bytes());
self.raw_data[20..24].copy_from_slice(&resolution[1].to_ne_bytes());
self.raw_data[24..28].copy_from_slice(&0u32.to_ne_bytes());
self.raw_data[28..32].copy_from_slice(&0u32.to_ne_bytes());
self.snapshot = FrameGlobalsSnapshot {
time,
delta_time,
frame_count,
resolution,
};
self.uploader.write_dirty_ranges(
gpu,
&self.gpu_buffer,
Self::BYTE_SIZE,
self.raw_data.as_slice(),
&[(0, Self::BYTE_SIZE)],
)?;
Ok(())
}
}
impl AwsmRenderer {
pub fn frame_globals(&self) -> FrameGlobalsSnapshot {
self.frame_globals.snapshot()
}
pub fn set_time_source(&mut self, time: f32) {
self.frame_globals.set_time_source(time);
}
pub fn time_source(&self) -> Option<f32> {
self.frame_globals.time_source()
}
pub fn tick_texture_flows(&mut self, dt_seconds: f32) {
match self.time_source() {
Some(t) => self.textures.set_texture_flows_elapsed(t),
None => self.textures.advance_texture_flows(dt_seconds),
}
}
pub fn clear_time_source(&mut self) {
self.frame_globals.clear_time_source();
}
}
fn performance_now_ms() -> f64 {
crate::web_global::performance()
.map(|p| p.now())
.unwrap_or(0.0)
}
#[derive(Error, Debug)]
pub enum AwsmFrameGlobalsError {
#[error("[frame_globals] {0:?}")]
Core(#[from] AwsmCoreError),
}