summaryrefslogtreecommitdiff
path: root/engine/src
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src')
-rw-r--r--engine/src/rendering/backend/opengl/graphics_mesh.rs59
-rw-r--r--engine/src/rendering/shader.rs268
2 files changed, 203 insertions, 124 deletions
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;
}
};