diff options
Diffstat (limited to 'engine/src/rendering/shader/cursor.rs')
| -rw-r--r-- | engine/src/rendering/shader/cursor.rs | 372 |
1 files changed, 372 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..49f6b47 --- /dev/null +++ b/engine/src/rendering/shader/cursor.rs @@ -0,0 +1,372 @@ +use std::borrow::Cow; +use std::fmt::Display; +use std::hint::cold_path; + +use circular_buffer::FixedCircularBuffer; + +use crate::color::{Color, Rgb, Rgba}; +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 (new_var_layout, binding_index_offset) = match field_type_kind { + TypeKind::ConstantBuffer => { + let elem_var_layout = 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", + ); + + ( + elem_var_layout, + field_var_layout.binding_index() + elem_var_layout.binding_index(), + ) + } + TypeKind::Array + | TypeKind::Matrix + | TypeKind::Scalar + | TypeKind::Vector + | TypeKind::Struct + | TypeKind::Resource => (field_var_layout, field_var_layout.binding_index()), + 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: new_var_layout.type_layout().unwrap(), + binding_location: BindingLocation { + binding_index: self.binding_location.binding_index + binding_index_offset, + binding_size: if field_type_kind == TypeKind::ConstantBuffer { + new_var_layout + .type_layout() + .unwrap() + .uniform_size() + .unwrap() + } else { + self.binding_location.binding_size + }, + byte_offset: self.binding_location.byte_offset + new_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, + }) + } +} + +/// 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, BindingTextureKind), +} + +impl BindingValue +{ + fn validate_for_shader_type( + &self, + ty: TypeLayout<'_>, + location_path: LocationPath, + ) -> Result<(), BindingError> + { + let ty_kind = ty.kind(); + let scalar_ty = ty.scalar_type(); + + let element_scalar_ty = ty + .element_type_layout() + .and_then(|elem_ty| elem_ty.scalar_type()); + + let element_cnt = ty.element_cnt(); + + let is_valid = match self { + Self::Uint(_) => { + ty_kind == TypeKind::Scalar && scalar_ty == Some(ScalarType::Uint32) + } + Self::Int(_) => { + ty_kind == TypeKind::Scalar && scalar_ty == Some(ScalarType::Int32) + } + Self::Float(_) => { + ty_kind == TypeKind::Scalar && scalar_ty == Some(ScalarType::Float32) + } + Self::FVec3(_) => { + ty_kind == TypeKind::Vector + && element_scalar_ty == Some(ScalarType::Float32) + && element_cnt == Some(3) + } + Self::Color(Color::Rgb(_)) => { + ty_kind == TypeKind::Vector + && element_scalar_ty == Some(ScalarType::Float32) + && element_cnt == Some(3) + } + Self::Color(Color::Rgba(_)) => { + ty_kind == TypeKind::Vector + && element_scalar_ty == Some(ScalarType::Float32) + && element_cnt == Some(4) + } + Self::FMat4x4(_) => { + ty_kind == TypeKind::Matrix + && element_scalar_ty == Some(ScalarType::Float32) + && ty.row_cnt() == Some(4) + && ty.column_cnt() == Some(4) + } + Self::Texture(_, BindingTextureKind::Texture2D) => { + ty_kind == TypeKind::Resource + && ty.resource_shape().is_some_and(|res_shape| { + (res_shape & ResourceShape::BASE) + .contains(ResourceShape::TEXTURE_2D) + }) + } + Self::Texture(_, BindingTextureKind::Cube) => { + ty_kind == TypeKind::Resource + && ty.resource_shape().is_some_and(|res_shape| { + (res_shape & ResourceShape::BASE) + .contains(ResourceShape::TEXTURE_CUBE) + }) + } + }; + + if !is_valid { + cold_path(); + return Err(BindingError::IncorrectValueType { + value: self.clone(), + location_path, + }); + } + + Ok(()) + } +} + +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<Rgb<f32>> for BindingValue +{ + fn from(color: Rgb<f32>) -> Self + { + BindingValue::Color(color.into()) + } +} + +impl From<Rgba<f32>> for BindingValue +{ + fn from(color: Rgba<f32>) -> Self + { + BindingValue::Color(color.into()) + } +} + +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, Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] +pub enum BindingTextureKind +{ + Texture2D, + Cube, +} + +#[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, + }, +} |
