From 65bce20d43670d23f433ac11f49f10d2011474b6 Mon Sep 17 00:00:00 2001 From: HampusM Date: Sun, 19 Jul 2026 16:30:58 +0200 Subject: feat(engine): add location path to shader binding validation error --- engine/Cargo.toml | 1 + engine/src/rendering/shader/cursor.rs | 103 +++++++++++++++++++++++++++++----- 2 files changed, 91 insertions(+), 13 deletions(-) (limited to 'engine') diff --git a/engine/Cargo.toml b/engine/Cargo.toml index fa30dd0..df29fd3 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -14,6 +14,7 @@ paste = "1.0.14" safer-ffi = "0.1.13" crossbeam-queue = "0.3.12" parking_lot = "0.12.3" +circular-buffer = "2.0.0" engine-macros = { workspace = true } engine-ecs = { workspace = true } engine-reflection = { workspace = true } diff --git a/engine/src/rendering/shader/cursor.rs b/engine/src/rendering/shader/cursor.rs index 32dee00..ba8bed7 100644 --- a/engine/src/rendering/shader/cursor.rs +++ b/engine/src/rendering/shader/cursor.rs @@ -1,5 +1,9 @@ +use std::borrow::Cow; +use std::fmt::Display; use std::hint::cold_path; +use circular_buffer::FixedCircularBuffer; + use crate::color::Color; use crate::data_types::matrix::Matrix; use crate::data_types::vector::Vec3; @@ -18,6 +22,7 @@ pub struct Cursor<'a> { type_layout: TypeLayout<'a>, binding_location: BindingLocation, + location_path: LocationPath, } impl<'a> Cursor<'a> @@ -33,12 +38,16 @@ impl<'a> Cursor<'a> Self { type_layout: var_layout.type_layout().unwrap(), binding_location, + location_path: LocationPath::default(), } } - pub fn field(&self, name: &str) -> Self + pub fn field(&self, name: impl Into>) -> Self { - let Some(field_var_layout) = self.type_layout.get_field_by_name(name) else { + let name = name.into(); + + let Some(field_var_layout) = self.type_layout.get_field_by_name(name.as_ref()) + else { panic!("Field '{name}' does not exist"); }; @@ -59,6 +68,10 @@ impl<'a> Cursor<'a> type_kind => unimplemented!("Type kind {type_kind:?} is not yet supported"), }; + let mut location_path = self.location_path.clone(); + + location_path.push(Location::Field(name)); + Self { type_layout: field_var_layout.type_layout().unwrap(), binding_location: BindingLocation { @@ -76,6 +89,7 @@ impl<'a> Cursor<'a> byte_offset: self.binding_location.byte_offset + field_var_layout.offset(), }, + location_path, } } @@ -87,19 +101,14 @@ impl<'a> Cursor<'a> self.type_layout = element_type_layout; - self - } - - pub fn with_field(self, name: &str, func: impl FnOnce(Self) -> Self) -> Self - { - let _ = func(self.field(name)); + self.location_path.push(Location::Element(index)); self } pub fn binding(self, value: BindingValue) -> Result { - value.validate_for_shader_type(self.type_layout)?; + value.validate_for_shader_type(self.type_layout, self.location_path)?; Ok(Binding { location: self.binding_location, @@ -113,6 +122,66 @@ impl<'a> Cursor<'a> } } +/// Shader cursor location. +#[derive(Debug, Clone, Default)] +pub struct LocationPath +{ + locations: FixedCircularBuffer, + is_truncated: bool, +} + +impl LocationPath +{ + fn push(&mut self, location: Location) + { + if self.locations.push_back(location).is_some() { + self.is_truncated = true; + } + } +} + +impl Display for LocationPath +{ + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + if self.is_truncated { + write!(formatter, "(...)")?; + } + + for location in &self.locations { + match location { + Location::Field(field) => { + write!(formatter, ".{field}")?; + } + Location::Element(element) => { + write!(formatter, "[{element}]")?; + } + } + } + + Ok(()) + } +} + +/// Shader cursor location. +#[derive(Debug, Clone)] +pub enum Location +{ + Field(Cow<'static, str>), + Element(usize), +} + +impl Display for Location +{ + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + match self { + Self::Field(field) => as Display>::fmt(field, formatter), + Self::Element(element) => ::fmt(element, formatter), + } + } +} + #[derive(Debug, Clone)] pub struct BindingLocation { @@ -135,7 +204,11 @@ pub enum BindingValue impl BindingValue { - fn validate_for_shader_type(&self, ty: TypeLayout<'_>) -> Result<(), BindingError> + fn validate_for_shader_type( + &self, + ty: TypeLayout<'_>, + location_path: LocationPath, + ) -> Result<(), BindingError> { match ( self, @@ -205,7 +278,10 @@ impl BindingValue } _ => { cold_path(); - Err(BindingError::IncorrectValueType { value: self.clone() }) + Err(BindingError::IncorrectValueType { + value: self.clone(), + location_path, + }) } } } @@ -271,9 +347,10 @@ pub struct Binding #[non_exhaustive] pub enum BindingError { - #[error("Value {value:?} has incorrect type for shader binding location")] + #[error("Value {value:?} has incorrect type for value at {location_path} in shader")] IncorrectValueType { - value: BindingValue + value: BindingValue, + location_path: LocationPath, }, } -- cgit v1.2.3-18-g5258