diff options
Diffstat (limited to 'engine/src/rendering/backend')
| -rw-r--r-- | engine/src/rendering/backend/opengl.rs | 164 | ||||
| -rw-r--r-- | engine/src/rendering/backend/opengl/graphics_mesh.rs | 185 |
2 files changed, 243 insertions, 106 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 { |
