globject_rs/
pipeline.rs

1
2#![allow(clippy::suspicious_else_formatting)]
3
4use crate::prelude::*;
5use std::{
6	any::Any,
7	collections::BTreeMap,
8	ffi::c_void,
9	fmt::{self, Debug, Formatter},
10	marker::PhantomData,
11	mem::{size_of, align_of},
12	ptr::null,
13	rc::Rc,
14};
15
16/// The trait that the struct of vertices or instances must implement
17pub trait VertexType: Copy + Clone + Sized + Default + Debug + Iterable {}
18impl<T> VertexType for T where T: Copy + Clone + Sized + Default + Debug + Iterable {}
19
20/// Use this macro to convert your struct into `VertexType`
21#[macro_export]
22macro_rules! derive_vertex_type {
23	($item: item) => {
24		#[derive(Iterable, Default, Debug, Clone, Copy)]
25		$item
26	};
27}
28
29/// The pipeline is used to draw a mesh with a shader to a framebuffer.
30pub struct Pipeline<V: VertexType, I: VertexType> {
31	pub glcore: Rc<GLCore>,
32	name: u32,
33	pub mesh: Rc<dyn GenericMeshWithMaterial>,
34	pub shader: Rc<Shader>,
35	vertex_stride: usize,
36	instance_stride: usize,
37	_phantom_vertex_type: PhantomData<V>,
38	_phantom_instance_type: PhantomData<I>,
39}
40
41/// The data type described in the OpenGL way.
42#[derive(Debug, Clone, Copy)]
43struct DataGlType {
44	data_type: u32,
45	size: u32,
46	rows: u32,
47}
48
49/// The error of the pipeline
50#[derive(Debug, Clone)]
51pub enum PipelineError {
52	ShaderError(ShaderError),
53	FramebufferError(FramebufferError),
54	GLCoreError(GLCoreError),
55}
56
57impl From<ShaderError> for PipelineError {
58	fn from(val: ShaderError) -> Self {
59		Self::ShaderError(val)
60	}
61}
62
63impl From<FramebufferError> for PipelineError {
64	fn from(val: FramebufferError) -> Self {
65		Self::FramebufferError(val)
66	}
67}
68
69impl From<GLCoreError> for PipelineError {
70	fn from(val: GLCoreError) -> Self {
71		Self::GLCoreError(val)
72	}
73}
74
75/// The binding state of the pipeline
76#[derive(Debug)]
77pub struct PipelineBind<'a, V: VertexType, I: VertexType> {
78	pub pipeline: &'a Pipeline<V, I>,
79}
80
81impl DataGlType {
82	/// Does this data type use integers as the basic component type
83	fn is_integer(&self) -> bool {
84		matches!(self.data_type, GL_BYTE | GL_SHORT | GL_INT | GL_UNSIGNED_BYTE | GL_UNSIGNED_SHORT | GL_UNSIGNED_INT)
85	}
86
87	/// Does this data type use `double` as the basic component type
88	fn is_double(&self) -> bool {
89		matches!(self.data_type, GL_DOUBLE)
90	}
91
92	/// The size in bytes of this data type
93	fn size_in_bytes(&self) -> usize {
94		match self.data_type {
95			GL_BYTE | GL_UNSIGNED_BYTE => (self.size as usize) * self.rows as usize,
96			GL_SHORT | GL_UNSIGNED_SHORT | GL_HALF_FLOAT => 2usize * self.size as usize * self.rows as usize,
97			GL_INT | GL_UNSIGNED_INT | GL_FLOAT => 4usize * self.size as usize * self.rows as usize,
98			GL_DOUBLE => 8usize * self.size as usize * self.rows as usize,
99			other => panic!("Invalid `data_type` ({other})"),
100		}
101	}
102}
103
104impl<V: VertexType, I: VertexType> Pipeline<V, I> {
105	/// Get the internal name
106	pub fn get_name(&self) -> u32 {
107		self.name
108	}
109
110	/// Create a new pipeline
111	pub fn new(glcore: Rc<GLCore>, mesh: Rc<dyn GenericMeshWithMaterial>, shader: Rc<Shader>) -> Result<Self, PipelineError> {
112		let mut name: u32 = 0;
113		glcore.glGenVertexArrays(1, &mut name as *mut u32)?;
114		let mut ret = Self {
115			glcore,
116			name,
117			mesh,
118			shader,
119			vertex_stride: size_of::<V>(),
120			instance_stride: size_of::<I>(),
121			_phantom_vertex_type: PhantomData,
122			_phantom_instance_type: PhantomData,
123		};
124		ret.establish_pipeline()?;
125		Ok(ret)
126	}
127
128	/// Establish the pipeline by describing the vertex/instance data and the shader attrib inputs to the VAO.
129	fn establish_pipeline(&mut self) -> Result<(), PipelineError> {
130		let program = self.shader.use_program()?;
131		let active_attribs = self.shader.get_active_attribs()?;
132		let bind = self.bind()?;
133
134		let vb_bind = self.mesh.get_vertex_buffer().bind()?;
135		self.describe::<V>(&active_attribs, 0)?;
136		vb_bind.unbind();
137
138		if let Some(ib) = self.mesh.get_instance_buffer() {
139			let ib_bind = ib.bind()?;
140			self.describe::<I>(&active_attribs, 1)?;
141			ib_bind.unbind();
142		}
143
144		bind.unbind();
145		program.unuse();
146		Ok(())
147	}
148
149	/// Describe a `VertexType` to a VAO
150	fn describe<T: VertexType>(&self, active_attribs: &BTreeMap<String, ShaderInputVarType>, v_a_d: u32) -> Result<(), PipelineError> {
151		let instance = T::default();
152		let stride = size_of::<T>();
153		let alignment = align_of::<T>();
154		let mut cur_offset: usize = 0;
155		for (field_name, field_value) in instance.iter() {
156			let typename = Self::get_typename_of_vertex_struct_member(field_value);
157			let datainfo = Self::get_vertex_struct_member_gltype(typename);
158			if let Some(attrib_type) = active_attribs.get(field_name) {
159				let (p_size, p_rows) = attrib_type.get_size_and_rows();
160				if p_size != datainfo.size || p_rows != datainfo.rows {
161					panic!("The size and rows of the shader attrib is {p_size}x{p_rows}, but the given member of the vertex struct is {}x{}", datainfo.size, datainfo.rows);
162				}
163				let location = self.shader.get_attrib_location(field_name)?;
164				if location >= 0 {
165					let location = location as u32;
166					for row in 0..datainfo.rows {
167						let location = location + row;
168						let do_normalize = if field_name.contains("normalized") && field_name.contains("_") {
169							1
170						} else {
171							0
172						};
173						let ptr_param = cur_offset as *const c_void;
174						self.glcore.glEnableVertexAttribArray(location)?;
175						if attrib_type.is_float()	{self.glcore.glVertexAttribPointer (location, p_size as i32, attrib_type.get_base_type() as u32, do_normalize, stride as i32, ptr_param)?} else
176						if attrib_type.is_integer()	{self.glcore.glVertexAttribIPointer(location, p_size as i32, attrib_type.get_base_type() as u32, stride as i32, ptr_param)?} else
177						if attrib_type.is_double()	{self.glcore.glVertexAttribLPointer(location, p_size as i32, attrib_type.get_base_type() as u32, stride as i32, ptr_param)?} else
178						{panic!("Unknown data type of the attrib `{} {field_name}`", attrib_type.get_type())}
179						self.glcore.glVertexAttribDivisor(location, v_a_d)?;
180					}
181				} else {
182					eprintln!("Attrib `{typename} {field_name}` is active, but can't get its location.");
183				}
184			} else {
185				eprintln!("Attrib `{typename} {field_name}` is not active.");
186			}
187			cur_offset += datainfo.size_in_bytes();
188			cur_offset = ((cur_offset - 1) / alignment + 1) * alignment;
189		}
190		Ok(())
191	}
192
193	/// Bind the pipeline for drawing
194	pub fn bind<'a>(&'a self) -> Result<PipelineBind<'a, V, I>, PipelineError> {
195		PipelineBind::new(self)
196	}
197
198	/// Parse the type name of the `VertexType` struct members, and return a `DataGlType`.
199	fn get_vertex_struct_member_gltype(member_type: &str) -> DataGlType {
200		match member_type {
201			"i8" => DataGlType{data_type: GL_BYTE, size: 1, rows: 1},
202			"i16" => DataGlType{data_type: GL_SHORT, size: 1, rows: 1},
203			"i32" => DataGlType{data_type: GL_INT, size: 1, rows: 1},
204			"u8" => DataGlType{data_type: GL_UNSIGNED_BYTE, size: 1, rows: 1},
205			"u16" => DataGlType{data_type: GL_UNSIGNED_SHORT, size: 1, rows: 1},
206			"u32" => DataGlType{data_type: GL_UNSIGNED_INT, size: 1, rows: 1},
207			"f16" => DataGlType{data_type: GL_HALF_FLOAT, size: 1, rows: 1},
208			"f32" => DataGlType{data_type: GL_FLOAT, size: 1, rows: 1},
209			"f64" => DataGlType{data_type: GL_DOUBLE, size: 1, rows: 1},
210			_ => {
211				if member_type.contains("vec") {
212					let data_type =
213					     if member_type.starts_with("u32") {GL_UNSIGNED_INT}
214					else if member_type.starts_with("u16") {GL_UNSIGNED_SHORT}
215					else if member_type.starts_with("u8")  {GL_UNSIGNED_BYTE}
216					else if member_type.starts_with("i32") {GL_INT}
217					else if member_type.starts_with("i16") {GL_SHORT}
218					else if member_type.starts_with("i8")  {GL_BYTE}
219					else {
220						match member_type.chars().next().unwrap() {
221							'v' => GL_FLOAT,
222							'd' => GL_DOUBLE,
223							'b' => GL_BYTE,
224							'i' => GL_INT,
225							'u' => GL_UNSIGNED_INT,
226							_ => panic!("Unsupported type of member: `{member_type}`"),
227						}
228					};
229					let size = u32::from(member_type.chars().last().unwrap()) - u32::from('0');
230					DataGlType{data_type, size, rows: 1}
231				} else if member_type.contains("mat") {
232					let data_type = if member_type.starts_with("d") {
233						GL_DOUBLE
234					} else {
235						GL_FLOAT
236					};
237					let (size, rows) =
238					     if member_type.ends_with("2x2") {(2, 2)}
239					else if member_type.ends_with("2x3") {(2, 3)}
240					else if member_type.ends_with("2x4") {(2, 4)}
241					else if member_type.ends_with("3x2") {(3, 2)}
242					else if member_type.ends_with("3x3") {(3, 3)}
243					else if member_type.ends_with("3x4") {(3, 4)}
244					else if member_type.ends_with("4x2") {(4, 2)}
245					else if member_type.ends_with("4x3") {(4, 3)}
246					else if member_type.ends_with("4x4") {(4, 4)}
247					else {
248						match member_type.chars().last().unwrap() {
249							'2' => (2, 2),
250							'3' => (3, 3),
251							'4' => (4, 4),
252							_ => panic!("Unsupported type of member: `{member_type}`"),
253						}
254					};
255					DataGlType{data_type, size, rows}
256				} else if member_type.contains("quat") {
257					let data_type = if member_type.starts_with("d") {
258						GL_DOUBLE
259					} else {
260						GL_FLOAT
261					};
262					DataGlType{data_type, size: 4, rows: 1}
263				} else {
264					panic!("Unsupported type of member: `{member_type}`")
265				}
266			}
267		}
268	}
269
270	/// Get the exact type of the struct member by the member instance
271	pub fn get_typename_of_vertex_struct_member(data: &dyn Any) -> &str {
272		     if data.is::<u8>() {"u8"}
273		else if data.is::<u16>() {"u16"}
274		else if data.is::<u32>() {"u32"}
275		else if data.is::<i8>() {"i8"}
276		else if data.is::<i16>() {"i16"}
277		else if data.is::<i32>() {"i32"}
278		else if data.is::<f16>() {"f16"}
279		else if data.is::<f32>() {"f32"}
280		else if data.is::<f64>() {"f64"}
281		else if data.is::<Vec1>() {"vec1"}
282		else if data.is::<Vec2>() {"vec2"}
283		else if data.is::<Vec3>() {"vec3"}
284		else if data.is::<Vec4>() {"vec4"}
285		else if data.is::<DVec1>() {"dvec1"}
286		else if data.is::<DVec2>() {"dvec2"}
287		else if data.is::<DVec3>() {"dvec3"}
288		else if data.is::<DVec4>() {"dvec4"}
289		else if data.is::<BVec1>() {"bvec1"}
290		else if data.is::<BVec2>() {"bvec2"}
291		else if data.is::<BVec3>() {"bvec3"}
292		else if data.is::<BVec4>() {"bvec4"}
293		else if data.is::<IVec1>() {"ivec1"}
294		else if data.is::<IVec2>() {"ivec2"}
295		else if data.is::<IVec3>() {"ivec3"}
296		else if data.is::<IVec4>() {"ivec4"}
297		else if data.is::<I8Vec1>() {"i8vec1"}
298		else if data.is::<I8Vec2>() {"i8vec2"}
299		else if data.is::<I8Vec3>() {"i8vec3"}
300		else if data.is::<I8Vec4>() {"i8vec4"}
301		else if data.is::<I16Vec1>() {"i16vec1"}
302		else if data.is::<I16Vec2>() {"i16vec2"}
303		else if data.is::<I16Vec3>() {"i16vec3"}
304		else if data.is::<I16Vec4>() {"i16vec4"}
305		else if data.is::<I32Vec1>() {"i32vec1"}
306		else if data.is::<I32Vec2>() {"i32vec2"}
307		else if data.is::<I32Vec3>() {"i32vec3"}
308		else if data.is::<I32Vec4>() {"i32vec4"}
309		else if data.is::<UVec1>() {"uvec1"}
310		else if data.is::<UVec2>() {"uvec2"}
311		else if data.is::<UVec3>() {"uvec3"}
312		else if data.is::<UVec4>() {"uvec4"}
313		else if data.is::<U8Vec1>() {"u8vec1"}
314		else if data.is::<U8Vec2>() {"u8vec2"}
315		else if data.is::<U8Vec3>() {"u8vec3"}
316		else if data.is::<U8Vec4>() {"u8vec4"}
317		else if data.is::<U16Vec1>() {"u16vec1"}
318		else if data.is::<U16Vec2>() {"u16vec2"}
319		else if data.is::<U16Vec3>() {"u16vec3"}
320		else if data.is::<U16Vec4>() {"u16vec4"}
321		else if data.is::<U32Vec1>() {"u32vec1"}
322		else if data.is::<U32Vec2>() {"u32vec2"}
323		else if data.is::<U32Vec3>() {"u32vec3"}
324		else if data.is::<U32Vec4>() {"u32vec4"}
325		else if data.is::<Quat>() {"quat"}
326		else if data.is::<DQuat>() {"dquat"}
327		else if data.is::<Mat2>() {"mat2"}
328		else if data.is::<Mat3>() {"mat3"}
329		else if data.is::<Mat4>() {"mat4"}
330		else if data.is::<Mat2x2>() {"mat2x2"}
331		else if data.is::<Mat2x3>() {"mat2x3"}
332		else if data.is::<Mat2x4>() {"mat2x4"}
333		else if data.is::<Mat3x2>() {"mat3x2"}
334		else if data.is::<Mat3x3>() {"mat3x3"}
335		else if data.is::<Mat3x4>() {"mat3x4"}
336		else if data.is::<Mat4x2>() {"mat4x2"}
337		else if data.is::<Mat4x3>() {"mat4x3"}
338		else if data.is::<Mat4x4>() {"mat4x4"}
339		else if data.is::<DMat2>() {"dmat2"}
340		else if data.is::<DMat3>() {"dmat3"}
341		else if data.is::<DMat4>() {"dmat4"}
342		else if data.is::<DMat2x2>() {"dmat2x2"}
343		else if data.is::<DMat2x3>() {"dmat2x3"}
344		else if data.is::<DMat2x4>() {"dmat2x4"}
345		else if data.is::<DMat3x2>() {"dmat3x2"}
346		else if data.is::<DMat3x3>() {"dmat3x3"}
347		else if data.is::<DMat3x4>() {"dmat3x4"}
348		else if data.is::<DMat4x2>() {"dmat4x2"}
349		else if data.is::<DMat4x3>() {"dmat4x3"}
350		else if data.is::<DMat4x4>() {"dmat4x4"}
351		else {panic!("Unsupported type of value: {data:?}")}
352	}
353}
354
355impl<'a, V: VertexType, I: VertexType> PipelineBind<'a, V, I> {
356	/// Create a binding state of the pipeline
357	fn new(pipeline: &'a Pipeline<V, I>) -> Result<Self, PipelineError> {
358		pipeline.glcore.glBindVertexArray(pipeline.name)?;
359		Ok(Self {
360			pipeline,
361		})
362	}
363
364	/// Run the pipeline for drawing
365	pub fn draw(&self, fbo: Option<&Framebuffer>) -> Result<(), PipelineError>  {
366		let glcore = &self.pipeline.glcore;
367		let program = self.pipeline.shader.use_program()?;
368		let fbo_bind = if let Some(fbo) = fbo {
369			let bind = fbo.bind()?;
370			bind.setup(&self.pipeline.shader)?;
371			Some(bind)
372		} else {
373			Framebuffer::default_bind(glcore)?;
374			None
375		};
376
377		if let Some(material) = self.pipeline.mesh.get_material() {
378			program.setup_material_uniforms(material, Some("i"), true)?;
379		} else {
380			let default_material = MaterialLegacy::default();
381			program.setup_material_uniforms(&default_material, Some("i"), true)?;
382		}
383
384		let mesh = &self.pipeline.mesh;
385		let element_buffer = mesh.get_element_buffer();
386		let e_bind = mesh.bind_element_buffer()?;
387
388		if let Some(command_buffer) = mesh.get_command_buffer() {
389			assert_eq!(command_buffer.get_target(), BufferTarget::DrawIndirectBuffer);
390			let c_bind = command_buffer.bind()?;
391			let num_commands = mesh.get_command_count();
392			if element_buffer.is_some() {
393				glcore.glMultiDrawElementsIndirect(mesh.get_primitive() as u32, mesh.get_element_type() as u32, null(), num_commands as i32, size_of::<DrawElementsCommand>() as i32)?;
394			} else {
395				glcore.glMultiDrawArraysIndirect(mesh.get_primitive() as u32, null(), num_commands as i32, size_of::<DrawArrayCommand>() as i32)?;
396			}
397			c_bind.unbind();
398		} else {
399			let num_vertices = mesh.get_vertex_count();
400			if mesh.get_instance_buffer().is_some() {
401				let num_instances = mesh.get_instance_count();
402				if element_buffer.is_some() {
403					glcore.glDrawElementsInstanced(mesh.get_primitive() as u32, mesh.get_element_count() as i32, mesh.get_element_type() as u32, null(), num_instances as i32)?;
404				} else {
405					glcore.glDrawArraysInstanced(mesh.get_primitive() as u32, 0, num_vertices as i32, num_instances as i32)?;
406				}
407			} else if element_buffer.is_some() {
408				glcore.glDrawElements(mesh.get_primitive() as u32, mesh.get_element_count() as i32, mesh.get_element_type() as u32, null())?;
409			} else {
410				glcore.glDrawArrays(mesh.get_primitive() as u32, 0, num_vertices as i32)?;
411			}
412		}
413
414		if let Some(b) = e_bind { b.unbind() }
415		program.unuse();
416		if let Some(b) = fbo_bind { b.unbind() }
417		Ok(())
418	}
419
420	/// Explicitly unbind the VAO pipeline
421	pub fn unbind(self) {}
422}
423
424impl<'a, V: VertexType, I: VertexType> Drop for PipelineBind<'a, V, I> {
425	fn drop(&mut self) {
426		self.pipeline.glcore.glBindVertexArray(0).unwrap();
427	}
428}
429
430impl<V: VertexType, I: VertexType> Drop for Pipeline<V, I> {
431	fn drop(&mut self) {
432		self.glcore.glDeleteVertexArrays(1, &self.name as *const u32).unwrap();
433	}
434}
435
436impl<V: VertexType, I: VertexType> Debug for Pipeline<V, I> {
437	fn fmt(&self, f: &mut Formatter) -> fmt::Result {
438		f.debug_struct("Pipeline")
439		.field("name", &self.name)
440		.field("mesh", &self.mesh)
441		.field("shader", &self.shader)
442		.field("vertex_stride", &self.vertex_stride)
443		.field("instance_stride", &self.instance_stride)
444		.finish()
445	}
446}
447
448derive_vertex_type! {
449	/// The unused type for you if you don't want to use the instanced mesh
450	pub struct UnusedType {}
451}