From 99e69bc233caa9b73a5297286487476f44f539f9 Mon Sep 17 00:00:00 2001 From: HampusM Date: Tue, 11 Aug 2026 14:11:10 +0200 Subject: feat(engine): set GPU vertex attrs on DrawMesh command if necessary --- .../src/rendering/backend/opengl/graphics_mesh.rs | 185 ++++++++++++++++----- 1 file changed, 139 insertions(+), 46 deletions(-) (limited to 'engine/src/rendering/backend/opengl/graphics_mesh.rs') 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, + vertex_attr_props: Vec, + last_used_vertex_attr_cnt: u32, pub index_buffer: Option>, 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 { 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 = 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>), + + #[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, + unexpected_index: u32, + }, +} + fn mesh_usage_to_gl_buffer_usage(mesh_usage: MeshUsage) -> GlBufferUsage { match mesh_usage { -- cgit v1.2.3-18-g5258