diff options
Diffstat (limited to 'engine')
| -rw-r--r-- | engine/res/default_shader.slang | 2 | ||||
| -rw-r--r-- | engine/res/imgui_shader.slang | 2 | ||||
| -rw-r--r-- | engine/src/rendering/backend/opengl/graphics_mesh.rs | 59 | ||||
| -rw-r--r-- | engine/src/rendering/shader.rs | 268 |
4 files changed, 205 insertions, 126 deletions
diff --git a/engine/res/default_shader.slang b/engine/res/default_shader.slang index 1f79dbe..62b01f7 100644 --- a/engine/res/default_shader.slang +++ b/engine/res/default_shader.slang @@ -235,7 +235,7 @@ struct Fragment }; [shader("vertex")] -VertexStageOutput vertex_main(Vertex vertex: VERTEX) +VertexStageOutput vertex_main(Vertex vertex) { VertexStageOutput stage_output; diff --git a/engine/res/imgui_shader.slang b/engine/res/imgui_shader.slang index 6ce51d6..2bdd718 100644 --- a/engine/res/imgui_shader.slang +++ b/engine/res/imgui_shader.slang @@ -30,7 +30,7 @@ cbuffer Uniforms Sampler2D main_texture; [shader("vertex")] -VertexStageOutput vertex_main(Vertex vertex: VERTEX) +VertexStageOutput vertex_main(Vertex vertex) { VertexStageOutput stage_output; diff --git a/engine/src/rendering/backend/opengl/graphics_mesh.rs b/engine/src/rendering/backend/opengl/graphics_mesh.rs index a1c903b..fba60c8 100644 --- a/engine/src/rendering/backend/opengl/graphics_mesh.rs +++ b/engine/src/rendering/backend/opengl/graphics_mesh.rs @@ -157,47 +157,27 @@ impl GraphicsMesh shader_vertex_desc: &ShaderVertexDescription, ) -> Result<(), VertexAttrsUpdatingError> { - let vertex_field_desc_cnt = u32::try_from(shader_vertex_desc.fields.len()) - .expect("Shader has too many vertex fields. Count does not fit into u32"); + let vertex_input_cnt = u32::try_from(shader_vertex_desc.inputs.len()) + .expect("Shader has too many vertex inputs. Count does not fit into u32"); - if self.last_used_vertex_attr_cnt > vertex_field_desc_cnt { - for index in vertex_field_desc_cnt..self.last_used_vertex_attr_cnt { + if self.last_used_vertex_attr_cnt > vertex_input_cnt { + for index in vertex_input_cnt..self.last_used_vertex_attr_cnt { self.vertex_arr.disable_attrib(curr_gl_ctx, index); } } let mut used_vertex_attr_cnt = 0u32; - let mut last_attr_index: Option<u32> = None; - for vertex_attr_props in &self.vertex_attr_props { - let Some(vertex_field_desc) = - shader_vertex_desc.fields.iter().find(|vertex_field_desc| { - *vertex_field_desc.name == vertex_attr_props.name + let Some(vertex_input_desc) = + shader_vertex_desc.inputs.iter().find(|vertex_input_desc| { + *vertex_input_desc.name == vertex_attr_props.name }) else { continue; }; - let attrib_index: u32 = - vertex_field_desc.varying_input_offset.try_into().unwrap(); - - if let Some(last_attr_index) = last_attr_index { - if last_attr_index.wrapping_add(1) != attrib_index { - cold_path(); - return Err( - VertexAttrsUpdatingError::VertexAttrIndicesNotConsecutive, - ); - } - } else if attrib_index != 0 { - cold_path(); - return Err(VertexAttrsUpdatingError::FirstVertexAttrIndexNotZero { - vertex_attr_name: vertex_attr_props.name.to_string().into_boxed_str(), - unexpected_index: attrib_index, - }); - } - - last_attr_index = Some(attrib_index); + let attrib_index: u32 = vertex_input_desc.index.try_into().unwrap(); self.vertex_arr.enable_attrib(curr_gl_ctx, attrib_index); @@ -233,23 +213,23 @@ impl GraphicsMesh self.last_used_vertex_attr_cnt = used_vertex_attr_cnt; - if used_vertex_attr_cnt as usize != shader_vertex_desc.fields.len() { + if used_vertex_attr_cnt as usize != shader_vertex_desc.inputs.len() { cold_path(); return Err(VertexAttrsUpdatingError::MissingVertexAttrs( shader_vertex_desc - .fields + .inputs .iter() - .filter_map(|vertex_field_desc| { + .filter_map(|vertex_input_desc| { if self .vertex_attr_props .iter() - .any(|prop| prop.name == *vertex_field_desc.name) + .any(|prop| prop.name == *vertex_input_desc.name) { return None; } - Some(vertex_field_desc.name.clone()) + Some(vertex_input_desc.name.clone()) }) .collect(), )); @@ -284,19 +264,6 @@ pub enum VertexAttrsUpdatingError { #[error("Mesh is missing vertex attribute(s) required by shader: {0:?}")] MissingVertexAttrs(Vec<Box<str>>), - - #[error("Shader's vertex attribute indices are not consecutive")] - VertexAttrIndicesNotConsecutive, - - #[error( - "Shader's first vertex attribute ({vertex_attr_name}) index is not 0, is {}", - unexpected_index - )] - FirstVertexAttrIndexNotZero - { - vertex_attr_name: Box<str>, - unexpected_index: u32, - }, } fn mesh_usage_to_gl_buffer_usage(mesh_usage: MeshUsage) -> GlBufferUsage diff --git a/engine/src/rendering/shader.rs b/engine/src/rendering/shader.rs index 3bcf73e..3142400 100644 --- a/engine/src/rendering/shader.rs +++ b/engine/src/rendering/shader.rs @@ -38,10 +38,6 @@ use crate::ecs::{declare_entity, pair, Component, Sole}; pub mod cursor; pub mod default; -/// The vertex parameter of a vertex entrypoint function in a shader should have this -/// semantic name. -pub const VERTEX_PARAM_SEMANTIC_NAME: &str = "VERTEX"; - #[derive(Debug, Clone, Component)] pub struct Shader { @@ -88,6 +84,11 @@ impl Module Some(EntryPoint { inner: entry_point }) } + + pub fn file_path(&self) -> &str + { + self.inner.file_path() + } } pub struct EntryPoint @@ -405,11 +406,13 @@ impl<'a> TypeLayout<'a> self.inner.ty().map(|ty| TypeReflection { inner: ty }) } - pub fn fields(&self) -> impl ExactSizeIterator<Item = VariableLayout<'a>> + pub fn fields(&self) -> FieldIter<'a> { - self.inner - .fields() - .map(|field| VariableLayout { inner: field }) + FieldIter { + type_layout: self.clone(), + cnt: self.field_cnt(), + index: 0, + } } pub fn field_cnt(&self) -> u32 @@ -487,6 +490,66 @@ impl<'a> TypeLayout<'a> } } +pub struct FieldIter<'a> +{ + type_layout: TypeLayout<'a>, + cnt: u32, + index: u32, +} + +impl<'a> Iterator for FieldIter<'a> +{ + type Item = VariableLayout<'a>; + + fn next(&mut self) -> Option<Self::Item> + { + if self.index == self.cnt { + return None; + } + + let Some(field) = self.type_layout.inner.field_by_index(self.index) else { + unreachable!(); + }; + + self.index += 1; + + Some(VariableLayout { inner: field }) + } + + fn size_hint(&self) -> (usize, Option<usize>) + { + let len = (self.cnt - self.index) as usize; + + (len, Some(len)) + } +} + +impl ExactSizeIterator for FieldIter<'_> +{ + fn len(&self) -> usize + { + (self.cnt - self.index) as usize + } +} + +impl DoubleEndedIterator for FieldIter<'_> +{ + fn next_back(&mut self) -> Option<Self::Item> + { + if self.cnt == 0 || self.index == self.cnt - 1 { + return None; + } + + let Some(field) = self.type_layout.inner.field_by_index(self.cnt - 1) else { + unreachable!(); + }; + + self.cnt -= 1; + + Some(VariableLayout { inner: field }) + } +} + pub struct TypeReflection<'a> { inner: &'a shader_slang::reflection::Type, @@ -816,6 +879,7 @@ impl Context self.programs.get(asset_id) } + #[tracing::instrument(skip_all, fields(module_file = module.file_path()))] pub fn compose_into_program( &self, module: Module, @@ -889,11 +953,12 @@ pub struct ProgramMetadata #[non_exhaustive] pub struct VertexDescription { - pub fields: Arc<[VertexFieldDescription]>, + pub inputs: Arc<[VertexInputDescription]>, } impl VertexDescription { + #[tracing::instrument(skip_all, fields(vs_entry_point_name = vs_entrypoint.name()))] pub fn new( vs_entrypoint: &EntryPointReflection<'_>, ) -> Result<Self, VertexDescriptionError> @@ -902,71 +967,65 @@ impl VertexDescription return Err(VertexDescriptionError::EntrypointNotInVertexStage); } - let vs_entrypoint_vertex_param = vs_entrypoint - .parameters() - .find(|param| param.semantic_name() == Some(VERTEX_PARAM_SEMANTIC_NAME)) - .ok_or(VertexDescriptionError::EntrypointMissingVertexParam)?; - - let vs_entrypoint_vertex_param = vs_entrypoint_vertex_param - .type_layout() - .expect("Not possible"); - - if vs_entrypoint_vertex_param.parameter_category() - != ParameterCategory::VaryingInput - { - return Err(VertexDescriptionError::EntryPointVertexParamNotVaryingInput); - } - - if vs_entrypoint_vertex_param.kind() != TypeKind::Struct { - return Err(VertexDescriptionError::EntrypointVertexTypeNotStruct); - } - - let fields = vs_entrypoint_vertex_param - .fields() - .map(|field| { - let varying_input_offset = - field.varying_input_offset().expect("Not possible"); + let inputs = VarInputDfsIter::new(vs_entrypoint).inspect(|var_input| { + if var_input.type_layout.kind() != TypeKind::Struct { + return; + } - let field_ty = field.type_layout().expect("Maybe not possible"); + let Some(semantic_name) = var_input.var_layout.semantic_name() else { + return; + }; - let scalar_type = match field_ty.kind() { - TypeKind::Scalar => field_ty.scalar_type().expect("Not possible"), - TypeKind::Vector => { - let Some(scalar_type) = field_ty.scalar_type() else { + tracing::warn!( + "Semantic name '{}' of '{}' will be inherited by it's fields", + semantic_name, + var_input.var_layout.name().unwrap_or("<unnamed>") + ); + }); + + Ok(Self { + inputs: inputs + .filter(|var_input| var_input.type_layout.kind() != TypeKind::Struct) + .map(|var_input| { + let name = var_input + .var_layout + .name() + .unwrap_or("<unnamed>") + .to_string(); + + let scalar_type = match ( + var_input.type_layout.kind(), + var_input.type_layout.scalar_type(), + ) { + (TypeKind::Scalar, Some(scalar_type)) => scalar_type, + (TypeKind::Vector, Some(scalar_type)) => scalar_type, + _ => { return Err( - VertexDescriptionError::UnsupportedVertexFieldType { - field_name: field.name().unwrap_or("").to_string(), + VertexDescriptionError::UnsupportedVertexInputType { + name, }, ); - }; - - scalar_type - } - _ => { - return Err(VertexDescriptionError::UnsupportedVertexFieldType { - field_name: field.name().unwrap_or("").to_string(), - }); - } - }; - - Ok(VertexFieldDescription { - name: field.name().unwrap_or("").to_string().into_boxed_str(), - varying_input_offset, - type_kind: field_ty.kind(), - scalar_type, + } + }; + + Ok(VertexInputDescription { + name: name.into_boxed_str(), + index: var_input.index, + type_kind: var_input.type_layout.kind(), + scalar_type, + }) }) - }) - .collect::<Result<Vec<_>, _>>()?; - - Ok(Self { fields: fields.into() }) + .collect::<Result<Vec<_>, _>>()? + .into(), + }) } } #[derive(Debug)] -pub struct VertexFieldDescription +pub struct VertexInputDescription { pub name: Box<str>, - pub varying_input_offset: usize, + pub index: usize, pub type_kind: TypeKind, pub scalar_type: ScalarType, } @@ -977,22 +1036,10 @@ pub enum VertexDescriptionError #[error("Entrypoint is not in vertex stage")] EntrypointNotInVertexStage, - #[error( - "Entrypoint does not have a vertex parameter (parameter with semantic name {})", - VERTEX_PARAM_SEMANTIC_NAME - )] - EntrypointMissingVertexParam, - - #[error("Entrypoint vertex parameter is not a varying input")] - EntryPointVertexParamNotVaryingInput, - - #[error("Entrypoint vertex type is not a struct")] - EntrypointVertexTypeNotStruct, - - #[error("Type of field '{field_name}' of vertex type is not supported")] - UnsupportedVertexFieldType + #[error("Type of vertex input '{name}' is not supported")] + UnsupportedVertexInputType { - field_name: String + name: String }, } @@ -1016,6 +1063,68 @@ pub enum ComposeProgramError #[error(transparent)] pub struct Error(#[from] shader_slang::Error); +struct VarInputDfsIter<'a> +{ + stack: Vec<(VariableLayout<'a>, TypeLayout<'a>, usize)>, +} + +impl<'a> VarInputDfsIter<'a> +{ + fn new(entry_point: &EntryPointReflection<'a>) -> Self + { + Self { + stack: entry_point + .parameters() + .map(|param| { + let Some(param_type_layout) = param.type_layout() else { + // I do not know in which cases type_layout can return None + unimplemented!(); + }; + + (param, param_type_layout, 0) + }) + .collect(), + } + } +} + +impl<'a> Iterator for VarInputDfsIter<'a> +{ + type Item = VarInput<'a>; + + fn next(&mut self) -> Option<Self::Item> + { + let (var_layout, type_layout, index) = loop { + let (var_layout, type_layout, acc_offset) = self.stack.pop()?; + + if let Some(offset) = var_layout.varying_input_offset() { + break (var_layout, type_layout, acc_offset + offset); + } + }; + + if type_layout.kind() == TypeKind::Struct { + self.stack.extend(type_layout.fields().map(|field| { + let Some(field_type_layout) = field.type_layout() else { + // I do not know in which cases type_layout can return + // None + unimplemented!(); + }; + + (field, field_type_layout, index) + })); + } + + Some(VarInput { var_layout, type_layout, index }) + } +} + +struct VarInput<'a> +{ + var_layout: VariableLayout<'a>, + type_layout: TypeLayout<'a>, + index: usize, +} + fn import_slang_asset( asset_submitter: &mut AssetSubmitter<'_>, file_path: &Path, @@ -1171,7 +1280,10 @@ fn load_modules( { Ok(shader_program) => shader_program, Err(err) => { - tracing::error!("Failed to compose shader into program: {err}"); + tracing::error!( + "Failed to compose shader into program: {:#}", + crate::Error::new(err) + ); continue; } }; |
