globject_rs/
glframebuffer.rs

1
2use crate::prelude::*;
3use std::{
4	cmp::max,
5	collections::BTreeMap,
6	fmt::{self, Debug, Formatter},
7	rc::Rc,
8};
9
10/// The framebuffer render target type
11pub struct FramebufferTarget {
12	/// The texture binding target
13	pub texture_target: TextureTarget,
14
15	/// The layer index of the 3D texture to bind (Only bind a 2D layer to the framebuffer)
16	pub layer_of_3d: i32,
17}
18
19/// The framebuffer object type
20pub struct Framebuffer {
21	pub glcore: Rc<GLCore>,
22	name: u32,
23
24	/// The name of the draw targets and the binding target and the texture
25	pub draw_targets: BTreeMap<String, (FramebufferTarget, Rc<dyn GenericTexture>)>,
26}
27
28/// The error of the framebuffers
29#[derive(Debug, Clone)]
30pub enum FramebufferError {
31	NoDefaultFramebuffer,
32	IncompleteAttachment,
33	IncompleteMissingAttachment,
34	IncompleteDrawBuffer,
35	IncompleteReadBuffer,
36	Unsupported,
37	IncompleteMultisample,
38	IncompleteLayerTarget,
39	UnknownError(GLenum),
40	GLCoreError(GLCoreError),
41}
42
43impl From<GLCoreError> for FramebufferError {
44	fn from(val: GLCoreError) -> Self {
45		Self::GLCoreError(val)
46	}
47}
48
49/// The binding guard of the framebuffer
50pub struct FramebufferBind<'a> {
51	framebuffer: &'a Framebuffer,
52}
53
54impl Framebuffer {
55	/// Create a new empty framebuffer object
56	pub fn new(glcore: Rc<GLCore>) -> Result<Self, FramebufferError> {
57		let mut name: u32 = 0;
58		glcore.glGenFramebuffers(1, &mut name as *mut _)?;
59		Ok(Self {
60			glcore,
61			name,
62			draw_targets: BTreeMap::new(),
63		})
64	}
65
66	/// Utilize the RAII rules to manage binding states.
67	pub fn bind<'a>(&'a self) -> Result<FramebufferBind<'a>, FramebufferError> {
68		FramebufferBind::new(self)
69	}
70
71	/// Bind to the default framebuffer
72	pub fn default_bind(glcore: &GLCore) -> Result<(), FramebufferError> {
73		glcore.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)?;
74		Ok(())
75	}
76}
77
78impl<'a> FramebufferBind<'a> {
79	/// Create a new binding state to the framebuffer object, utilizing the RAII rules to manage the binding state.
80	fn new(framebuffer: &'a Framebuffer) -> Result<Self, FramebufferError> {
81		framebuffer.glcore.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.name)?;
82		Ok(Self {
83			framebuffer,
84		})
85	}
86
87	/// Set up the framebuffer, apply `draw_targets`
88	pub fn setup(&self, program: &Shader) -> Result<(), FramebufferError> {
89		let draw_targets = &self.framebuffer.draw_targets;
90		assert!(!draw_targets.is_empty());
91		let glcore = self.framebuffer.glcore.clone();
92		let mut draw_buffers: Vec<u32> = Vec::with_capacity(draw_targets.len());
93		let mut max_width: u32 = 0;
94		let mut max_height: u32 = 0;
95		for (target_name, target) in draw_targets.iter() {
96			let location = glcore.glGetFragDataLocation(program.get_name(), target_name.as_ptr() as *const i8)?;
97			if location >= 0 {
98				let location = location as u32;
99				let (target, texture) = target;
100				let attachment = GL_COLOR_ATTACHMENT0 + location;
101				max_width = max(max_width, texture.get_width());
102				max_height = max(max_height, texture.get_height());
103				match texture.get_dim() {
104					TextureDimension::Tex1d =>		glcore.glFramebufferTexture1D(GL_DRAW_FRAMEBUFFER, attachment, target.texture_target as u32, texture.get_name(), 0)?,
105					TextureDimension::Tex2d =>		glcore.glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, target.texture_target as u32, texture.get_name(), 0)?,
106					TextureDimension::Tex3d =>		glcore.glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, attachment, target.texture_target as u32, texture.get_name(), 0, target.layer_of_3d)?,
107					TextureDimension::TexCube =>	glcore.glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, target.texture_target as u32, texture.get_name(), 0)?,
108				}
109				draw_buffers.push(attachment);
110			} else {
111				eprintln!("Location of shader output `{target_name}` couldn't be found.");
112			}
113		}
114		glcore.glDrawBuffers(draw_buffers.len() as i32, draw_buffers.as_ptr())?;
115		match glcore.glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER) ?{
116			GL_FRAMEBUFFER_COMPLETE => {},
117			GL_FRAMEBUFFER_UNDEFINED => return Err(FramebufferError::NoDefaultFramebuffer),
118			GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT => return Err(FramebufferError::IncompleteAttachment),
119			GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT => return Err(FramebufferError::IncompleteMissingAttachment),
120			GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER => return Err(FramebufferError::IncompleteDrawBuffer),
121			GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER => return Err(FramebufferError::IncompleteReadBuffer),
122			GL_FRAMEBUFFER_UNSUPPORTED => return Err(FramebufferError::Unsupported),
123			GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE => return Err(FramebufferError::IncompleteMultisample),
124			GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS => return Err(FramebufferError::IncompleteLayerTarget),
125			other => return Err(FramebufferError::UnknownError(other)),
126		}
127		glcore.glViewport(0, 0, max_width as i32, max_height as i32)?;
128		Ok(())
129	}
130
131	/// Explicitly unbind the framebuffer
132	pub fn unbind(self) {}
133}
134
135impl Drop for FramebufferBind<'_> {
136	fn drop(&mut self) {
137		self.framebuffer.glcore.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0).unwrap();
138	}
139}
140
141impl Drop for Framebuffer {
142	fn drop(&mut self) {
143		self.glcore.glDeleteFramebuffers(1, &self.name as *const _).unwrap();
144	}
145}
146
147impl Debug for Framebuffer {
148	fn fmt(&self, f: &mut Formatter) -> fmt::Result {
149		f.debug_struct("Framebuffer")
150		.field("name", &self.name)
151		.finish()
152	}
153}