diff options
Diffstat (limited to 'engine')
| -rw-r--r-- | engine/src/rendering/backend/opengl.rs | 164 | ||||
| -rw-r--r-- | engine/src/rendering/backend/opengl/graphics_mesh.rs | 185 | ||||
| -rw-r--r-- | engine/src/rendering/shader.rs | 191 |
3 files changed, 338 insertions, 202 deletions
diff --git a/engine/src/rendering/backend/opengl.rs b/engine/src/rendering/backend/opengl.rs index 12aaaf0..0dfe960 100644 --- a/engine/src/rendering/backend/opengl.rs +++ b/engine/src/rendering/backend/opengl.rs @@ -47,7 +47,6 @@ use opengl_bindings::shader::{ Kind as ShaderKind, Program as GlShaderProgram, Shader as GlShader, - // UniformLocation as GlUniformLocation, }; use opengl_bindings::texture::{ ColorSpace as GlTextureColorSpace, @@ -101,9 +100,9 @@ use crate::rendering::shader::cursor::{ BindingValue as ShaderBindingValue, }; use crate::rendering::shader::{ - Context as ShaderContext, Error as ShaderError, Program as ShaderProgram, + ProgramMetadata as ShaderProgramMetadata, Stage as ShaderStage, }; use crate::rendering::{ @@ -173,7 +172,12 @@ enum GraphicsContextObject Mesh { mesh: GraphicsMesh, - compatible_shader_program_obj_id: ObjectId, + vertex_attrs_updated_for_shader: Option<ObjectId>, + }, + Shader + { + program: GlShaderProgram, + program_metadata: ShaderProgramMetadata, }, } @@ -443,7 +447,6 @@ fn handle_commands( mut object_store: Single<ObjectStore>, mut command_queue: Single<CommandQueue>, assets: Single<Assets>, - shader_context: Single<ShaderContext>, ) -> Result<(), crate::Error> { let Ok(graphics_ctx) = graphics_ctx.get_mut() else { @@ -453,7 +456,6 @@ fn handle_commands( let object_store = object_store.get_mut()?; let command_queue = command_queue.get_mut()?; let assets = assets.get()?; - let shader_context = shader_context.get()?; let GraphicsContext { ref gl_context, @@ -467,7 +469,11 @@ fn handle_commands( return Ok(()); }; - let mut activated_gl_shader_program: Option<(ObjectId, GlShaderProgram)> = None; + let mut activated_gl_shader_program: Option<( + ObjectId, + GlShaderProgram, + ShaderProgramMetadata, + )> = None; for command in command_queue.drain() { let tracing_span = tracing::info_span!( @@ -593,35 +599,80 @@ fn handle_commands( } }; + let key = *next_graphics_ctx_object_key; + + graphics_ctx_objects.insert( + key, + GraphicsContextObject::Shader { + program: gl_shader_program, + program_metadata: shader_program.metadata().clone(), + }, + ); + object_store.insert( shader_program_obj_id, - Object::from_raw( - gl_shader_program.into_raw(), - ObjectKind::ShaderProgram, - ), + Object::from_raw(key, ObjectKind::ShaderProgram), ); + + *next_graphics_ctx_object_key += 1; } Command::ActivateShader(shader_program_obj_id) => { let Some(shader_program_obj) = - object_store.get_shader_program_obj(&shader_program_obj_id) + object_store.get_obj(&shader_program_obj_id) else { - tracing::error!("Shader object does not exist or has a wrong kind"); + tracing::error!( + object_id = ?shader_program_obj_id, + "Object does not exist" + ); + continue; }; - let gl_shader_program = - GlShaderProgram::from_raw(shader_program_obj.as_raw()); + if shader_program_obj.kind() != ObjectKind::ShaderProgram { + tracing::error!( + object_id = ?shader_program_obj_id, + actual_kind = ?shader_program_obj.kind(), + "Invalid object kind: not a shader program" + ); - gl_shader_program.activate(gl_context); + continue; + } + + let key = shader_program_obj.as_raw(); - activated_gl_shader_program = - Some((shader_program_obj_id, gl_shader_program)); + let Some(graphics_ctx_obj) = graphics_ctx_objects.get(&key) else { + tracing::error!( + object_id = ?shader_program_obj_id, + key = key, + "Object not found in graphics context object store" + ); + continue; + }; + + let GraphicsContextObject::Shader { program, program_metadata } = + graphics_ctx_obj + else { + tracing::error!( + object_id = ?shader_program_obj_id, + key = key, + "Graphics context object is not a shader" + ); + continue; + }; + + program.activate(gl_context); + + activated_gl_shader_program = Some(( + shader_program_obj_id, + program.clone(), + program_metadata.clone(), + )); } Command::SetShaderBinding(ShaderBinding { location: binding_location, value: binding_value, }) => { - let Some((activated_gl_shader_program_obj_id, _)) = + let Some((activated_gl_shader_program_obj_id, _, _)) = &activated_gl_shader_program else { tracing::error!("No shader program is activated"); @@ -786,24 +837,6 @@ fn handle_commands( continue; } - let Some((ObjectId::Asset(curr_shader_program_asset_id), _)) = - &activated_gl_shader_program - else { - tracing::error!("No shader program is activated"); - continue; - }; - - let curr_shader_program_metadata = shader_context - .get_program_metadata(curr_shader_program_asset_id) - .expect("Not possible"); - - let Some(vertex_desc) = &curr_shader_program_metadata.vertex_desc else { - tracing::error!( - "Current shader program does not have a vertex description" - ); - continue; - }; - let key = *next_graphics_ctx_object_key; let mesh = match &mesh { @@ -821,12 +854,8 @@ fn handle_commands( AssetOrValue::Value(mesh) => mesh, }; - let graphics_mesh = match GraphicsMesh::new( - gl_context, - &mesh, - mesh_usage, - &vertex_desc, - ) { + let graphics_mesh = match GraphicsMesh::new(gl_context, &mesh, mesh_usage) + { Ok(graphics_mesh) => graphics_mesh, Err(err) => { tracing::error!("Failed to create mesh: {err}"); @@ -838,9 +867,7 @@ fn handle_commands( key, GraphicsContextObject::Mesh { mesh: graphics_mesh, - compatible_shader_program_obj_id: ObjectId::Asset( - *curr_shader_program_asset_id, - ), + vertex_attrs_updated_for_shader: None, }, ); @@ -881,7 +908,7 @@ fn handle_commands( #[allow(irrefutable_let_patterns)] let GraphicsContextObject::Mesh { mesh: graphics_mesh, - compatible_shader_program_obj_id: _, + vertex_attrs_updated_for_shader: _, } = mesh_graphics_ctx_obj else { tracing::error!( @@ -947,7 +974,7 @@ fn handle_commands( }; let Some(mesh_graphics_ctx_obj) = - graphics_ctx_objects.get(&mesh_graphics_ctx_obj_key) + graphics_ctx_objects.get_mut(&mesh_graphics_ctx_obj_key) else { tracing::error!( object_id=?mesh_object_id, @@ -957,10 +984,9 @@ fn handle_commands( continue; }; - #[allow(irrefutable_let_patterns)] let GraphicsContextObject::Mesh { mesh: graphics_mesh, - compatible_shader_program_obj_id, + vertex_attrs_updated_for_shader, } = mesh_graphics_ctx_obj else { tracing::error!( @@ -971,18 +997,36 @@ fn handle_commands( continue; }; - if Some(compatible_shader_program_obj_id) - != activated_gl_shader_program.as_ref().map( - |(activated_gl_shader_program_obj_id, _)| { - activated_gl_shader_program_obj_id - }, - ) - { - tracing::error!(concat!( - "Activated shader program is not the ", - "compatible shader program of the mesh" - )); + let Some((shader_program_obj_id, _, shader_program_metadata)) = + &activated_gl_shader_program + else { + tracing::error!("No shader program is activated"); continue; + }; + + if vertex_attrs_updated_for_shader.is_none_or( + |vertex_attrs_updated_for_shader| { + vertex_attrs_updated_for_shader != *shader_program_obj_id + }, + ) { + let Some(shader_program_vertex_desc) = + &shader_program_metadata.vertex_desc + else { + tracing::error!( + "Activated shader program does not have a vertex description" + ); + continue; + }; + + if let Err(err) = graphics_mesh.update_vertex_attrs_for_shader( + gl_context, + shader_program_vertex_desc, + ) { + tracing::error!("Cannot draw mesh: {err}"); + continue; + } + + *vertex_attrs_updated_for_shader = Some(*shader_program_obj_id); } if let Err(err) = draw_mesh(gl_context, graphics_mesh, &draw_mesh_opts) { diff --git a/engine/src/rendering/backend/opengl/graphics_mesh.rs b/engine/src/rendering/backend/opengl/graphics_mesh.rs index 6d5c248..a1c903b 100644 --- a/engine/src/rendering/backend/opengl/graphics_mesh.rs +++ b/engine/src/rendering/backend/opengl/graphics_mesh.rs @@ -1,3 +1,5 @@ +use std::hint::cold_path; + use opengl_bindings::buffer::{Buffer as GlBuffer, Usage as GlBufferUsage}; use opengl_bindings::vertex_array::{ AttributeFormat as GlVertexArrayAttributeFormat, @@ -8,15 +10,20 @@ use opengl_bindings::vertex_array::{ }; use opengl_bindings::MaybeCurrentContextWithFns as GlCurrentContextWithFns; +use crate::mesh::vertex_buffer::VertexAttrProperties as MeshVertexAttrProperties; use crate::mesh::{Mesh, VertexAttrType}; use crate::rendering::shader::VertexDescription as ShaderVertexDescription; use crate::rendering::MeshUsage; +const VERTEX_BUF_BINDING_INDEX: u32 = 0; + #[derive(Debug)] pub struct GraphicsMesh { /// Vertex and index buffer has to live as long as the vertex array vertex_buffer: GlBuffer<u8>, + vertex_attr_props: Vec<MeshVertexAttrProperties>, + last_used_vertex_attr_cnt: u32, pub index_buffer: Option<GlBuffer<u32>>, pub element_cnt: u32, pub vertex_arr: GlVertexArray, @@ -29,7 +36,6 @@ impl GraphicsMesh current_context: &GlCurrentContextWithFns, mesh: &Mesh, mesh_usage: MeshUsage, - vertex_desc: &ShaderVertexDescription, ) -> Result<Self, Error> { let buffer_usage = mesh_usage_to_gl_buffer_usage(mesh_usage); @@ -41,11 +47,9 @@ impl GraphicsMesh .store(current_context, mesh.vertex_buf().as_bytes(), buffer_usage) .map_err(Error::StoreVerticesFailed)?; - let vertex_buf_binding_index = 0; - if let Err(err) = vertex_arr.bind_vertex_buffer( current_context, - vertex_buf_binding_index, + VERTEX_BUF_BINDING_INDEX, &vertex_buffer, GlVertexArrayVertexBufferSpec { offset: 0, @@ -69,48 +73,6 @@ impl GraphicsMesh } } - for vertex_attr_props in mesh.vertex_buf().vertex_attr_props() { - let vertex_field_desc = vertex_desc - .fields - .iter() - .find(|vertex_field_desc| { - *vertex_field_desc.name == vertex_attr_props.name - }) - .unwrap(); - - let attrib_index: u32 = - vertex_field_desc.varying_input_offset.try_into().unwrap(); - - vertex_arr.enable_attrib(current_context, attrib_index); - - vertex_arr.set_attrib_format( - current_context, - attrib_index, - match &vertex_attr_props.ty { - VertexAttrType::Float32 => GlVertexArrayAttributeFormat { - data_type: GlVertexArrayDataType::Float, - count: 1, - normalized: false, - offset: vertex_attr_props.byte_offset.try_into().unwrap(), - }, - VertexAttrType::Float32Array { length } => { - GlVertexArrayAttributeFormat { - data_type: GlVertexArrayDataType::Float, - count: (*length).try_into().unwrap(), - normalized: false, - offset: vertex_attr_props.byte_offset.try_into().unwrap(), - } - } - }, - ); - - vertex_arr.set_attrib_vertex_buf_binding( - current_context, - attrib_index, - vertex_buf_binding_index, - ); - } - if let Some(indices) = mesh.indices() { let index_buffer = GlBuffer::new(current_context); @@ -122,6 +84,8 @@ impl GraphicsMesh return Ok(Self { vertex_buffer: vertex_buffer, + vertex_attr_props: mesh.vertex_buf().vertex_attr_props().to_vec(), + last_used_vertex_attr_cnt: 0, index_buffer: Some(index_buffer), element_cnt: indices .len() @@ -133,6 +97,8 @@ impl GraphicsMesh Ok(Self { vertex_buffer: vertex_buffer, + vertex_attr_props: mesh.vertex_buf().vertex_attr_props().to_vec(), + last_used_vertex_attr_cnt: 0, index_buffer: None, element_cnt: mesh .vertex_buf() @@ -185,6 +151,113 @@ impl GraphicsMesh Ok(()) } + pub fn update_vertex_attrs_for_shader( + &mut self, + curr_gl_ctx: &GlCurrentContextWithFns, + 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"); + + if self.last_used_vertex_attr_cnt > vertex_field_desc_cnt { + for index in vertex_field_desc_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 + }) + 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); + + self.vertex_arr.enable_attrib(curr_gl_ctx, attrib_index); + + self.vertex_arr.set_attrib_format( + curr_gl_ctx, + attrib_index, + match &vertex_attr_props.ty { + VertexAttrType::Float32 => GlVertexArrayAttributeFormat { + data_type: GlVertexArrayDataType::Float, + count: 1, + normalized: false, + offset: vertex_attr_props.byte_offset.try_into().unwrap(), + }, + VertexAttrType::Float32Array { length } => { + GlVertexArrayAttributeFormat { + data_type: GlVertexArrayDataType::Float, + count: (*length).try_into().unwrap(), + normalized: false, + offset: vertex_attr_props.byte_offset.try_into().unwrap(), + } + } + }, + ); + + self.vertex_arr.set_attrib_vertex_buf_binding( + curr_gl_ctx, + attrib_index, + VERTEX_BUF_BINDING_INDEX, + ); + + used_vertex_attr_cnt += 1; + } + + self.last_used_vertex_attr_cnt = used_vertex_attr_cnt; + + if used_vertex_attr_cnt as usize != shader_vertex_desc.fields.len() { + cold_path(); + + return Err(VertexAttrsUpdatingError::MissingVertexAttrs( + shader_vertex_desc + .fields + .iter() + .filter_map(|vertex_field_desc| { + if self + .vertex_attr_props + .iter() + .any(|prop| prop.name == *vertex_field_desc.name) + { + return None; + } + + Some(vertex_field_desc.name.clone()) + }) + .collect(), + )); + } + + Ok(()) + } + pub fn destroy(&mut self, curr_gl_ctx: &GlCurrentContextWithFns) { self.vertex_arr.delete(curr_gl_ctx); @@ -206,6 +279,26 @@ pub enum Error StoreIndicesFailed(#[source] opengl_bindings::buffer::Error), } +#[derive(Debug, thiserror::Error)] +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 { match mesh_usage { diff --git a/engine/src/rendering/shader.rs b/engine/src/rendering/shader.rs index b508670..3bcf73e 100644 --- a/engine/src/rendering/shader.rs +++ b/engine/src/rendering/shader.rs @@ -4,6 +4,7 @@ use std::collections::HashMap; use std::fmt::Debug; use std::path::Path; use std::str::Utf8Error; +use std::sync::Arc; use bitflags::{bitflags, bitflags_match}; use ecs::phase::INIT as INIT_PHASE; @@ -156,15 +157,24 @@ impl<'a> EntryPointReflection<'a> pub struct Program { inner: SlangComponentType, + metadata: ProgramMetadata, } impl Program { - pub fn link(&self) -> Result<Program, Error> + pub fn into_linked(self) -> Result<Program, Error> { let linked_program = self.inner.link()?; - Ok(Program { inner: linked_program }) + Ok(Program { + inner: linked_program, + metadata: self.metadata, + }) + } + + pub fn metadata(&self) -> &ProgramMetadata + { + &self.metadata } pub fn get_entry_point_code(&self, entry_point_index: u32) -> Result<Blob, Error> @@ -791,7 +801,7 @@ pub struct Context _global_session: SlangGlobalSession, session: SlangSession, modules: HashMap<AssetId, Module>, - programs: HashMap<AssetId, (Program, ProgramMetadata)>, + programs: HashMap<AssetId, Program>, } impl Context @@ -803,38 +813,70 @@ impl Context pub fn get_program(&self, asset_id: &AssetId) -> Option<&Program> { - self.programs.get(asset_id).map(|(program, _)| program) - } - - pub fn get_program_metadata(&self, asset_id: &AssetId) -> Option<&ProgramMetadata> - { - self.programs - .get(asset_id) - .map(|(_, program_metadata)| program_metadata) + self.programs.get(asset_id) } pub fn compose_into_program( &self, - modules: impl IntoIterator<Item = Module>, - entry_points: impl IntoIterator<Item = EntryPoint>, - ) -> Result<Program, Error> + module: Module, + link_entrypoints: EntrypointFlags, + ) -> Result<Program, ComposeProgramError> { - let components = - modules - .into_iter() - .map(|module| SlangComponentType::from(module.inner.clone())) - .chain(entry_points.into_iter().map(|entry_point| { - SlangComponentType::from(entry_point.inner.clone()) - })) - .collect::<Vec<_>>(); - - let program = self.session.create_composite_component_type(&components)?; - - Ok(Program { inner: program }) + let entry_points = link_entrypoints + .iter() + .filter_map(|entrypoint_flag| { + let entrypoint_name = bitflags_match!(entrypoint_flag, { + EntrypointFlags::VERTEX => Some("vertex_main"), + EntrypointFlags::FRAGMENT => Some("fragment_main"), + _ => None + })?; + + let Some(entry_point) = module.get_entry_point(entrypoint_name) else { + return Some(Err(ComposeProgramError::EntrypointNotFoundError { + entrypoint_name, + })); + }; + + Some(Ok(entry_point)) + }) + .collect::<Result<Vec<_>, _>>()?; + + let components = entry_points + .into_iter() + .map(|entry_point| SlangComponentType::from(entry_point.inner)) + .chain([SlangComponentType::from(module.inner)]) + .collect::<Vec<_>>(); + + let program = self + .session + .create_composite_component_type(&components) + .map_err(|err| ComposeProgramError::Other(Error(err)))?; + + let vertex_desc = if link_entrypoints.contains(EntrypointFlags::VERTEX) { + // TODO: Do not have hard coded target here + let Ok(program_reflection) = program.layout(0) else { + unreachable!(); + }; + + let program_reflection = ProgramReflection { inner: program_reflection }; + + Some(VertexDescription::new( + &program_reflection + .get_entry_point_by_name("vertex_main") + .expect("Not possible"), + )?) + } else { + None + }; + + Ok(Program { + inner: program, + metadata: ProgramMetadata { vertex_desc }, + }) } } -#[derive(Debug)] +#[derive(Debug, Clone)] #[non_exhaustive] pub struct ProgramMetadata { @@ -843,11 +885,11 @@ pub struct ProgramMetadata pub vertex_desc: Option<VertexDescription>, } -#[derive(Debug)] +#[derive(Debug, Clone)] #[non_exhaustive] pub struct VertexDescription { - pub fields: Box<[VertexFieldDescription]>, + pub fields: Arc<[VertexFieldDescription]>, } impl VertexDescription @@ -916,7 +958,7 @@ impl VertexDescription }) .collect::<Result<Vec<_>, _>>()?; - Ok(Self { fields: fields.into_boxed_slice() }) + Ok(Self { fields: fields.into() }) } } @@ -955,6 +997,22 @@ pub enum VertexDescriptionError } #[derive(Debug, thiserror::Error)] +pub enum ComposeProgramError +{ + #[error("Shader module does not have a '{entrypoint_name}' entry point")] + EntrypointNotFoundError + { + entrypoint_name: &'static str + }, + + #[error("Failed to create vertex description")] + VertexDescriptionCreationFailed(#[from] VertexDescriptionError), + + #[error(transparent)] + Other(Error), +} + +#[derive(Debug, thiserror::Error)] #[error(transparent)] pub struct Error(#[from] shader_slang::Error); @@ -1108,44 +1166,17 @@ fn load_modules( if !module_source.link_entrypoints.is_empty() { assert!(context.programs.get(asset_id).is_none()); - let entry_points = match module_source - .link_entrypoints - .iter() - .filter_map(|entrypoint_flag| { - let entrypoint_name = bitflags_match!(entrypoint_flag, { - EntrypointFlags::VERTEX => Some("vertex_main"), - EntrypointFlags::FRAGMENT => Some("fragment_main"), - _ => None - })?; - - let Some(entry_point) = module.get_entry_point(entrypoint_name) - else { - return Some(Err(EntrypointNotFoundError { entrypoint_name })); - }; - - Some(Ok(entry_point)) - }) - .collect::<Result<Vec<_>, EntrypointNotFoundError>>() + let shader_program = match context + .compose_into_program(module, module_source.link_entrypoints) { - Ok(entry_points) => entry_points, - Err(EntrypointNotFoundError { entrypoint_name }) => { - tracing::error!( - "Shader module does not have a '{entrypoint_name}' entry point" - ); + Ok(shader_program) => shader_program, + Err(err) => { + tracing::error!("Failed to compose shader into program: {err}"); continue; } }; - let shader_program = - match context.compose_into_program([module], entry_points) { - Ok(shader_program) => shader_program, - Err(err) => { - tracing::error!("Failed to compose shader into program: {err}"); - continue; - } - }; - - let linked_shader_program = match shader_program.link() { + let linked_shader_program = match shader_program.into_linked() { Ok(linked_shader_program) => linked_shader_program, Err(err) => { tracing::error!("Failed to link shader: {err}"); @@ -1153,33 +1184,7 @@ fn load_modules( } }; - let vertex_desc = if module_source - .link_entrypoints - .contains(EntrypointFlags::VERTEX) - { - VertexDescription::new( - &shader_program - .reflection(0) - .expect("Not possible") - .get_entry_point_by_name("vertex_main") - .expect("Not possible"), - ) - .inspect_err(|err| { - tracing::error!( - "Failed to create a vertex description for shader {}: {}", - asset_label, - err - ); - }) - .ok() - } else { - None - }; - - context.programs.insert( - *asset_id, - (linked_shader_program, ProgramMetadata { vertex_desc }), - ); + context.programs.insert(*asset_id, linked_shader_program); } } @@ -1199,9 +1204,3 @@ fn load_module( Ok(Module { inner: module }) } - -#[derive(Debug)] -struct EntrypointNotFoundError -{ - entrypoint_name: &'static str, -} |
