summaryrefslogtreecommitdiff
path: root/engine/src/rendering/shader/cursor.rs
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src/rendering/shader/cursor.rs')
-rw-r--r--engine/src/rendering/shader/cursor.rs356
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,
+ },
+}