diff options
Diffstat (limited to 'engine/src/rendering/shader/cursor.rs')
| -rw-r--r-- | engine/src/rendering/shader/cursor.rs | 356 |
1 files changed, 356 insertions, 0 deletions
diff --git a/engine/src/rendering/shader/cursor.rs b/engine/src/rendering/shader/cursor.rs new file mode 100644 index 0000000..ba8bed7 --- /dev/null +++ b/engine/src/rendering/shader/cursor.rs @@ -0,0 +1,356 @@ +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; +use crate::rendering::object::Id as RenderingObjectId; +use crate::rendering::shader::{ + ResourceShape, + ScalarType, + TypeKind, + TypeLayout, + VariableLayout, +}; + +/// Shader cursor +#[derive(Clone)] +pub struct Cursor<'a> +{ + type_layout: TypeLayout<'a>, + binding_location: BindingLocation, + location_path: LocationPath, +} + +impl<'a> Cursor<'a> +{ + pub fn new(var_layout: VariableLayout<'a>) -> Self + { + let binding_location = BindingLocation { + binding_index: var_layout.binding_index(), + binding_size: 0, + byte_offset: var_layout.offset(), + }; + + Self { + type_layout: var_layout.type_layout().unwrap(), + binding_location, + location_path: LocationPath::default(), + } + } + + pub fn field(&self, name: impl Into<Cow<'static, str>>) -> Self + { + 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"); + }; + + let field_type_kind = field_var_layout.ty().unwrap().kind(); + + let field_var_layout = match field_type_kind { + TypeKind::ConstantBuffer => field_var_layout + .type_layout() + .expect("Constant buffer field has no type layout") + .element_var_layout() + .expect("Constant buffer field type layout has no element var layout"), + TypeKind::Array + | TypeKind::Matrix + | TypeKind::Scalar + | TypeKind::Vector + | TypeKind::Struct + | TypeKind::Resource => field_var_layout, + 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 { + binding_index: self.binding_location.binding_index + + field_var_layout.binding_index(), + binding_size: if field_type_kind == TypeKind::ConstantBuffer { + field_var_layout + .type_layout() + .unwrap() + .uniform_size() + .unwrap() + } else { + self.binding_location.binding_size + }, + byte_offset: self.binding_location.byte_offset + + field_var_layout.offset(), + }, + location_path, + } + } + + pub fn element(mut self, index: usize) -> Self + { + let element_type_layout = self.type_layout.element_type_layout().unwrap(); + + self.binding_location.byte_offset += index * element_type_layout.stride(); + + self.type_layout = element_type_layout; + + self.location_path.push(Location::Element(index)); + + self + } + + pub fn binding(self, value: BindingValue) -> Result<Binding, BindingError> + { + value.validate_for_shader_type(self.type_layout, self.location_path)?; + + Ok(Binding { + location: self.binding_location, + value, + }) + } + + pub fn into_binding_location(self) -> BindingLocation + { + self.binding_location + } +} + +/// Shader cursor location. +#[derive(Debug, Clone, Default)] +pub struct LocationPath +{ + locations: FixedCircularBuffer<Location, 16>, + 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) => <Cow<'static, str> as Display>::fmt(field, formatter), + Self::Element(element) => <usize as Display>::fmt(element, formatter), + } + } +} + +#[derive(Debug, Clone)] +pub struct BindingLocation +{ + pub binding_index: u32, + pub binding_size: usize, + pub byte_offset: usize, +} + +#[derive(Debug, Clone)] +pub enum BindingValue +{ + Uint(u32), + Int(i32), + Float(f32), + FVec3(Vec3<f32>), + Color(Color<f32>), + FMat4x4(Matrix<f32, 4, 4>), + Texture(RenderingObjectId), +} + +impl BindingValue +{ + fn validate_for_shader_type( + &self, + ty: TypeLayout<'_>, + location_path: LocationPath, + ) -> Result<(), BindingError> + { + match ( + self, + ty.kind(), + ty.scalar_type(), + ty.element_type_layout() + .map(|elem_ty| elem_ty.scalar_type()), + ty.element_cnt(), + ty.resource_shape(), + ) { + (Self::Uint(_), TypeKind::Scalar, Some(ScalarType::Uint32), _, _, _) + | (Self::Int(_), TypeKind::Scalar, Some(ScalarType::Int32), _, _, _) + | (Self::Float(_), TypeKind::Scalar, Some(ScalarType::Float32), _, _, _) + | ( + Self::FVec3(_), + TypeKind::Vector, + _, + Some(Some(ScalarType::Float32)), + Some(3), + _, + ) + | ( + Self::Color(Color::Rgb(_)), + TypeKind::Vector, + _, + Some(Some(ScalarType::Float32)), + Some(3), + _, + ) + | ( + Self::Color(Color::Rgba(_)), + TypeKind::Vector, + _, + Some(Some(ScalarType::Float32)), + Some(4), + _, + ) + | ( + Self::Color(Color::Luma(_)), + TypeKind::Vector, + _, + Some(Some(ScalarType::Float32)), + Some(1), + _, + ) + | ( + Self::Color(Color::LumaA(_)), + TypeKind::Vector, + _, + Some(Some(ScalarType::Float32)), + Some(2), + _, + ) => Ok(()), + ( + Self::FMat4x4(_), + TypeKind::Matrix, + _, + Some(Some(ScalarType::Float32)), + _, + _, + ) if ty.row_cnt() == Some(4) && ty.column_cnt() == Some(4) => Ok(()), + (Self::Texture(_), TypeKind::Resource, _, _, _, Some(res_shape)) + if (res_shape & ResourceShape::BASE) + .contains(ResourceShape::TEXTURE_2D) => + { + Ok(()) + } + _ => { + cold_path(); + Err(BindingError::IncorrectValueType { + value: self.clone(), + location_path, + }) + } + } + } +} + +impl From<u32> for BindingValue +{ + fn from(value: u32) -> Self + { + BindingValue::Uint(value) + } +} + +impl From<i32> for BindingValue +{ + fn from(value: i32) -> Self + { + BindingValue::Int(value) + } +} + +impl From<f32> for BindingValue +{ + fn from(value: f32) -> Self + { + BindingValue::Float(value) + } +} + +impl From<Vec3<f32>> for BindingValue +{ + fn from(vec: Vec3<f32>) -> Self + { + BindingValue::FVec3(vec) + } +} + +impl From<Color<f32>> for BindingValue +{ + fn from(color: Color<f32>) -> Self + { + BindingValue::Color(color) + } +} + +impl From<Matrix<f32, 4, 4>> for BindingValue +{ + fn from(matrix: Matrix<f32, 4, 4>) -> Self + { + BindingValue::FMat4x4(matrix) + } +} + +#[derive(Debug, Clone)] +#[non_exhaustive] +pub struct Binding +{ + pub location: BindingLocation, + pub value: BindingValue, +} + +#[derive(Debug, thiserror::Error)] +#[non_exhaustive] +pub enum BindingError +{ + #[error("Value {value:?} has incorrect type for value at {location_path} in shader")] + IncorrectValueType + { + value: BindingValue, + location_path: LocationPath, + }, +} |
