improve error handling and add metadata

This commit is contained in:
Chance 2025-04-03 01:00:24 -04:00 committed by BitSyndicate
parent daf95d5ec5
commit afbf3163fd
WARNING! Although there is a key with this ID in the database it does not verify this commit! This commit is SUSPICIOUS.
GPG key ID: 443E4198D6BBA6DE
17 changed files with 1162 additions and 787 deletions

View file

@ -5,132 +5,15 @@ use std::time::Instant;
use cgmath::{Deg, Matrix4, Point3, Rad, SquareMatrix, Vector3, perspective};
use futures::executor::block_on;
use thiserror::Error;
use tracing::{error, info, trace};
use tracing::{debug, error, info, trace};
use wgpu::TextureUsages;
use wgpu::{Backends, InstanceDescriptor, util::DeviceExt};
use wgpu_text::glyph_brush::ab_glyph::FontRef;
use wgpu_text::glyph_brush::{HorizontalAlign, Layout, OwnedSection, OwnedText, VerticalAlign};
use wgpu_text::{BrushBuilder, TextBrush};
use winit::window::Window;
#[derive(Debug, Error)]
#[error(transparent)]
pub enum ContextErrorKind {
#[error("Surface creation failed")]
SurfaceCreation,
#[error("Surface configuration failed")]
SurfaceConfiguration,
#[error("Adapter request failed")]
AdapterRequest,
#[error("Device request failed")]
DeviceRequest,
#[error("Surface texture acquisition failed")]
SurfaceTexture,
}
#[derive(Debug, Error)]
pub struct RenderContextError {
kind: ContextErrorKind,
label: Option<Cow<'static, str>>,
#[source]
source: Option<Box<dyn std::error::Error + Send + Sync>>,
}
impl RenderContextError {
pub fn new(
kind: ContextErrorKind,
label: impl Into<Option<Cow<'static, str>>>,
source: impl Into<Option<Box<dyn std::error::Error + Send + Sync>>>,
) -> Self {
Self {
kind,
label: label.into(),
source: source.into(),
}
}
pub fn with_label(mut self, label: impl Into<Cow<'static, str>>) -> Self {
self.label = Some(label.into());
self
}
}
impl std::fmt::Display for RenderContextError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(label) = &self.label {
writeln!(f, "[{}] {}", label, self.kind)?;
} else {
writeln!(f, "{}", self.kind)?;
}
if let Some(source) = &self.source {
fn fmt_chain(
err: &(dyn std::error::Error + 'static),
indent: usize,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
let indent_str = " ".repeat(indent);
writeln!(f, "{}{}", indent_str, err)?;
if let Some(next) = err.source() {
writeln!(f, "{}Caused by:", indent_str)?;
fmt_chain(next, indent + 1, f)?;
}
Ok(())
}
writeln!(f, "Caused by:")?;
fmt_chain(source.as_ref(), 1, f)?;
}
Ok(())
}
}
trait IntoRenderContextError<T> {
fn ctx_err(
self,
kind: ContextErrorKind,
label: impl Into<Cow<'static, str>>,
) -> Result<T, RenderContextError>;
}
impl<T, E> IntoRenderContextError<T> for Result<T, E>
where
E: std::error::Error + Send + Sync + 'static,
{
fn ctx_err(
self,
kind: ContextErrorKind,
label: impl Into<Cow<'static, str>>,
) -> Result<T, RenderContextError> {
self.map_err(|e| {
RenderContextError::new(
kind,
Some(label.into()),
Some(Box::new(e) as Box<dyn std::error::Error + Send + Sync>),
)
})
}
}
impl From<wgpu::CreateSurfaceError> for RenderContextError {
fn from(err: wgpu::CreateSurfaceError) -> Self {
RenderContextError::new(
ContextErrorKind::SurfaceCreation,
Some("Surface creation".into()),
Some(Box::new(err) as Box<dyn std::error::Error + Send + Sync>),
)
}
}
impl From<wgpu::RequestDeviceError> for RenderContextError {
fn from(err: wgpu::RequestDeviceError) -> Self {
RenderContextError::new(
ContextErrorKind::DeviceRequest,
Some("Device setup".into()),
Some(Box::new(err) as Box<dyn std::error::Error + Send + Sync>),
)
}
}
use crate::error::{ZenyxError, ZenyxErrorKind};
use crate::error::Result;
const SHADER_SRC: &str = include_str!("shader.wgsl");
@ -238,7 +121,7 @@ impl Camera {
queue.write_buffer(&self.uniform_buffer, 0, bytemuck::bytes_of(&uniform));
}
}
#[derive(Debug)]
struct Model {
vertex_buffer: wgpu::Buffer,
index_buffer: wgpu::Buffer,
@ -246,6 +129,7 @@ struct Model {
bind_group: wgpu::BindGroup,
index_count: u32,
transform: Matrix4<f32>,
version: u32,
}
impl Model {
@ -289,6 +173,7 @@ impl Model {
bind_group,
index_count: indices.len() as u32,
transform: Matrix4::identity(),
version: 1
}
}
@ -299,8 +184,12 @@ impl Model {
}
fn set_transform(&mut self, transform: Matrix4<f32>) {
self.transform = transform;
if self.transform != transform {
self.transform = transform;
self.version += 1;
}
}
}
pub struct Renderer<'window> {
@ -321,6 +210,7 @@ pub struct Renderer<'window> {
frame_count: u32,
fps: f32,
font_state: FontState,
model_versions: Vec<u32>,
}
struct FontState {
@ -331,15 +221,13 @@ struct FontState {
}
impl<'window> Renderer<'window> {
pub async fn new(window: Arc<Window>) -> Result<Self, RenderContextError> {
pub async fn new(window: Arc<Window>) -> Result<Self> {
let instance = wgpu::Instance::new(&InstanceDescriptor {
backends: Backends::from_comma_list("dx12,metal,opengl,webgpu"),
..Default::default()
});
let surface = instance
.create_surface(Arc::clone(&window))
.ctx_err(ContextErrorKind::SurfaceCreation, "Surface initialization")?;
let surface = instance.create_surface(Arc::clone(&window))?;
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
@ -349,17 +237,15 @@ impl<'window> Renderer<'window> {
})
.await
.ok_or_else(|| {
RenderContextError::new(
ContextErrorKind::AdapterRequest,
Some("Adapter selection".into()),
None,
)
ZenyxError::builder(ZenyxErrorKind::AdapterRequest)
.with_message("No suitable adapter found")
.build()
})?;
let (device, queue) = adapter
.request_device(&wgpu::DeviceDescriptor::default(), None)
.await
.ctx_err(ContextErrorKind::DeviceRequest, "Device configuration")?;
.map_err(ZenyxError::from)?;
let size = window.inner_size();
let width = size.width.max(1);
@ -473,16 +359,13 @@ impl<'window> Renderer<'window> {
&device,
surface_config.width,
surface_config.height,
// surface_config.format,
);
let font_bytes = include_bytes!("DejaVuSans.ttf");
let font = FontRef::try_from_slice(font_bytes).map_err(|e| {
RenderContextError::new(
ContextErrorKind::DeviceRequest,
Some("Font loading".into()),
None,
)
ZenyxError::builder(ZenyxErrorKind::DeviceRequest)
.with_message("Font loading failed")
.build()
})?;
let brush =
@ -492,6 +375,8 @@ impl<'window> Renderer<'window> {
let scale = base_scale * (surface_config.width as f32 / base_width as f32).clamp(0.5, 2.0);
let color = wgpu::Color::WHITE;
let section = OwnedSection::default()
.add_text(OwnedText::new("FPS: 0.00").with_scale(scale).with_color([
color.r as f32,
@ -535,10 +420,11 @@ impl<'window> Renderer<'window> {
scale,
color,
},
model_versions: vec![]
})
}
pub fn new_blocking(window: Arc<Window>) -> Result<Self, RenderContextError> {
pub fn new_blocking(window: Arc<Window>) -> Result<Self> {
block_on(Self::new(window))
}
@ -550,6 +436,7 @@ impl<'window> Renderer<'window> {
&self.model_bind_group_layout,
);
self.models.push(model);
self.model_versions.push(0);
}
pub fn resize(&mut self, new_size: (u32, u32)) {
@ -577,18 +464,30 @@ impl<'window> Renderer<'window> {
for (i, model) in self.models.iter_mut().enumerate() {
let angle = Rad(elapsed * 0.8 + i as f32 * 0.3);
if i % 2 == 0 {
model.set_transform(Matrix4::from_angle_y(angle));
// model.set_transform(Matrix4::from_angle_x(angle) * Matrix4::from_angle_y(angle));
model.update(&self.queue);
} else {
model.set_transform(Matrix4::from_angle_x(angle) * Matrix4::from_angle_y(angle));
}
}
for (i, model) in self.models.iter().enumerate() {
if model.version > self.model_versions[i] {
model.update(&self.queue);
#[cfg(debug_assertions)]
trace!("Updating model: {:#?}",model);
self.model_versions[i] = model.version;
}
}
let surface_texture = self
.surface
.get_current_texture()
.ctx_err(
ContextErrorKind::SurfaceTexture,
"Surface texture acquisition",
)
.map_err(|e| {
ZenyxError::builder(ZenyxErrorKind::SurfaceTexture)
.with_message("Failed to acquire surface texture")
.with_source(e)
.build()
})
.unwrap();
let view = surface_texture
@ -679,7 +578,7 @@ impl<'window> Renderer<'window> {
self.frame_count += 1;
let elapsed_secs = self.last_frame_instant.elapsed().as_secs_f32();
if elapsed_secs >= 1.0 {
if (elapsed_secs >= 1.0) {
let fps = self.frame_count as f32 / elapsed_secs;
// trace!("Renderer FPS: {:.2}", fps);
self.fps = fps;