1 //! Framebuffers and utility types and functions.
2 //!
3 //! Framebuffers are at the core of rendering. They’re the support of rendering operations and can
4 //! be used to highly enhance the visual aspect of a render. You’re always provided with at least
5 //! one framebuffer, `Framebuffer::back_buffer`. That function returns a framebuffer that represents –
6 //! for short – the current back framebuffer. You can render to that framebuffer and when you
7 //! *swap* the buffers, your render appears in the front framebuffer (likely your screen).
8 //!
9 //! # Framebuffers
10 //!
11 //! A framebuffer is an object maintaining the required GPU state to hold images you render to. It
12 //! gathers two important concepts:
13 //!
14 //! - *Color buffers*.
15 //! - *Depth buffers*.
16 //!
17 //! The *color buffers* hold the color images you render to. A framebuffer can hold several of them
18 //! with different color formats. The *depth buffers* hold the depth images you render to.
19 //! Framebuffers can hold only one depth buffer.
20 //!
21 //! # Framebuffer slots
22 //!
23 //! A framebuffer slot contains either its color buffers or its depth buffer. Sometimes, you might
24 //! find it handy to have no slot at all for a given type of buffer. In that case, we use `()`.
25 //!
26 //! The slots are a way to convert the different formats you use for your framebuffers’ buffers into
27 //! their respective texture representation so that you can handle the corresponding texels.
28 //!
29 //! Color buffers are abstracted by `ColorSlot` and the depth buffer by `DepthSlot`.
30
31 #[cfg(feature = "std")]
32 use std::cell::RefCell;
33 use std::fmt;
34 #[cfg(feature = "std")]
35 use std::marker::PhantomData;
36 use std::rc::Rc;
37
38 #[cfg(not(feature = "std"))]
39 use alloc::vec::Vec;
40 #[cfg(not(feature = "std"))]
41 use core::fmt;
42 #[cfg(not(feature = "std"))]
43 use core::marker::PhantomData;
44
45 use crate::context::GraphicsContext;
46 use crate::metagl::*;
47 use crate::pixel::{ColorPixel, DepthPixel, PixelFormat, RenderablePixel};
48 use crate::state::{Bind, GraphicsState};
49 use crate::texture::{
50 create_texture, opengl_target, Dim2, Dimensionable, Flat, Layerable, RawTexture, Sampler,
51 Texture, TextureError,
52 };
53
54 /// Framebuffer error.
55 #[derive(Clone, Debug, Eq, PartialEq)]
56 pub enum FramebufferError {
57 /// Texture error.
58 ///
59 /// This happen while creating / associating the color / depth slots.
60 TextureError(TextureError),
61 /// Incomplete error.
62 ///
63 /// This happens when finalizing the construction of the framebuffer.
64 Incomplete(IncompleteReason),
65 }
66
67 impl fmt::Display for FramebufferError {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>68 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
69 match *self {
70 FramebufferError::TextureError(ref e) => write!(f, "framebuffer texture error: {}", e),
71
72 FramebufferError::Incomplete(ref e) => write!(f, "incomplete framebuffer: {}", e),
73 }
74 }
75 }
76
77 /// Reason a framebuffer is incomplete.
78 #[derive(Clone, Debug, Eq, PartialEq)]
79 pub enum IncompleteReason {
80 /// Incomplete framebuffer.
81 Undefined,
82 /// Incomplete attachment (color / depth).
83 IncompleteAttachment,
84 /// An attachment was missing.
85 MissingAttachment,
86 /// Incomplete draw buffer.
87 IncompleteDrawBuffer,
88 /// Incomplete read buffer.
89 IncompleteReadBuffer,
90 /// Unsupported.
91 Unsupported,
92 /// Incomplete multisample configuration.
93 IncompleteMultisample,
94 /// Incomplete layer targets.
95 IncompleteLayerTargets,
96 }
97
98 impl fmt::Display for IncompleteReason {
fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error>99 fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
100 match *self {
101 IncompleteReason::Undefined => write!(f, "incomplete reason"),
102 IncompleteReason::IncompleteAttachment => write!(f, "incomplete attachment"),
103 IncompleteReason::MissingAttachment => write!(f, "missing attachment"),
104 IncompleteReason::IncompleteDrawBuffer => write!(f, "incomplete draw buffer"),
105 IncompleteReason::IncompleteReadBuffer => write!(f, "incomplete read buffer"),
106 IncompleteReason::Unsupported => write!(f, "unsupported"),
107 IncompleteReason::IncompleteMultisample => write!(f, "incomplete multisample"),
108 IncompleteReason::IncompleteLayerTargets => write!(f, "incomplete layer targets"),
109 }
110 }
111 }
112
113 /// Framebuffer with static layering, dimension, access and slots formats.
114 ///
115 /// A `Framebuffer` is a *GPU* special object used to render to. Because framebuffers have a
116 /// *layering* property, it’s possible to have regular render and *layered rendering*. The dimension
117 /// of a framebuffer makes it possible to render to 1D, 2D, 3D and cubemaps.
118 ///
119 /// A framebuffer has two kind of slots:
120 ///
121 /// - **color slot** ;
122 /// - **depth slot**.
123 ///
124 /// A framebuffer can have zero or several color slots and it can have zero or one depth slot. If
125 /// you use several color slots, you’ll be performing what’s called *MRT* (*M* ultiple *R* ender
126 /// *T* argets), enabling to render to several textures at once.
127 pub struct Framebuffer<L, D, CS, DS>
128 where
129 L: Layerable,
130 D: Dimensionable,
131 D::Size: Copy,
132 CS: ColorSlot<L, D>,
133 DS: DepthSlot<L, D>,
134 {
135 handle: GLuint,
136 renderbuffer: Option<GLuint>,
137 w: u32,
138 h: u32,
139 color_slot: CS::ColorTextures,
140 depth_slot: DS::DepthTexture,
141 state: Rc<RefCell<GraphicsState>>,
142 _l: PhantomData<L>,
143 _d: PhantomData<D>,
144 }
145
146 impl Framebuffer<Flat, Dim2, (), ()> {
147 /// Get the back buffer with the given dimension.
back_buffer<C>(ctx: &mut C, size: <Dim2 as Dimensionable>::Size) -> Self where C: GraphicsContext,148 pub fn back_buffer<C>(ctx: &mut C, size: <Dim2 as Dimensionable>::Size) -> Self
149 where
150 C: GraphicsContext,
151 {
152 Framebuffer {
153 handle: 0,
154 renderbuffer: None,
155 w: size[0],
156 h: size[1],
157 color_slot: (),
158 depth_slot: (),
159 state: ctx.state().clone(),
160 _l: PhantomData,
161 _d: PhantomData,
162 }
163 }
164 }
165
166 impl<L, D, CS, DS> Drop for Framebuffer<L, D, CS, DS>
167 where
168 L: Layerable,
169 D: Dimensionable,
170 D::Size: Copy,
171 CS: ColorSlot<L, D>,
172 DS: DepthSlot<L, D>,
173 {
drop(&mut self)174 fn drop(&mut self) {
175 self.destroy();
176 }
177 }
178
179 impl<L, D, CS, DS> Framebuffer<L, D, CS, DS>
180 where
181 L: Layerable,
182 D: Dimensionable,
183 D::Size: Copy,
184 CS: ColorSlot<L, D>,
185 DS: DepthSlot<L, D>,
186 {
187 /// Create a new framebuffer.
188 ///
189 /// You’re always handed at least the base level of the texture. If you require any *additional*
190 /// levels, you can pass the number via the `mipmaps` parameter.
new<C>( ctx: &mut C, size: D::Size, mipmaps: usize, sampler: Sampler, ) -> Result<Framebuffer<L, D, CS, DS>, FramebufferError> where C: GraphicsContext,191 pub fn new<C>(
192 ctx: &mut C,
193 size: D::Size,
194 mipmaps: usize,
195 sampler: Sampler,
196 ) -> Result<Framebuffer<L, D, CS, DS>, FramebufferError>
197 where
198 C: GraphicsContext,
199 {
200 let mipmaps = mipmaps + 1;
201 let mut handle: GLuint = 0;
202 let color_formats = CS::color_formats();
203 let depth_format = DS::depth_format();
204 let target = opengl_target(L::layering(), D::dim());
205 let mut textures = vec![0; color_formats.len() + if depth_format.is_some() { 1 } else { 0 }];
206 let mut depth_texture: Option<GLuint> = None;
207 let mut depth_renderbuffer: Option<GLuint> = None;
208
209 unsafe {
210 gl::GenFramebuffers(1, &mut handle);
211
212 ctx.state().borrow_mut().bind_draw_framebuffer(handle);
213
214 // generate all the required textures once; the textures vec will be reduced and dispatched
215 // into other containers afterwards (in ColorSlot::reify_textures)
216 gl::GenTextures((textures.len()) as GLint, textures.as_mut_ptr());
217
218 // color textures
219 if color_formats.is_empty() {
220 gl::DrawBuffer(gl::NONE);
221 } else {
222 for (i, (format, texture)) in color_formats.iter().zip(&textures).enumerate() {
223 ctx.state().borrow_mut().bind_texture(target, *texture);
224 create_texture::<L, D>(target, size, mipmaps, *format, sampler)
225 .map_err(FramebufferError::TextureError)?;
226 gl::FramebufferTexture(
227 gl::FRAMEBUFFER,
228 gl::COLOR_ATTACHMENT0 + i as GLenum,
229 *texture,
230 0,
231 );
232 }
233
234 // specify the list of color buffers to draw to
235 let color_buf_nb = color_formats.len() as GLsizei;
236 let color_buffers: Vec<_> =
237 (gl::COLOR_ATTACHMENT0..gl::COLOR_ATTACHMENT0 + color_buf_nb as GLenum).collect();
238
239 gl::DrawBuffers(color_buf_nb, color_buffers.as_ptr());
240 }
241
242 // depth texture, if exists
243 if let Some(format) = depth_format {
244 let texture = textures.pop().unwrap();
245
246 ctx.state().borrow_mut().bind_texture(target, texture);
247 create_texture::<L, D>(target, size, mipmaps, format, Default::default())
248 .map_err(FramebufferError::TextureError)?;
249 gl::FramebufferTexture(gl::FRAMEBUFFER, gl::DEPTH_ATTACHMENT, texture, 0);
250
251 depth_texture = Some(texture);
252 } else {
253 let mut renderbuffer: GLuint = 0;
254
255 gl::GenRenderbuffers(1, &mut renderbuffer);
256 gl::BindRenderbuffer(gl::RENDERBUFFER, renderbuffer);
257 gl::RenderbufferStorage(
258 gl::RENDERBUFFER,
259 gl::DEPTH_COMPONENT32F,
260 D::width(size) as GLsizei,
261 D::height(size) as GLsizei,
262 );
263 gl::BindRenderbuffer(gl::RENDERBUFFER, 0); // FIXME: see whether really needed
264
265 gl::FramebufferRenderbuffer(
266 gl::FRAMEBUFFER,
267 gl::DEPTH_ATTACHMENT,
268 gl::RENDERBUFFER,
269 renderbuffer,
270 );
271
272 depth_renderbuffer = Some(renderbuffer);
273 }
274
275 ctx.state().borrow_mut().bind_texture(target, 0); // FIXME: see whether really needed
276
277 let framebuffer = Framebuffer {
278 handle,
279 renderbuffer: depth_renderbuffer,
280 w: D::width(size),
281 h: D::height(size),
282 color_slot: CS::reify_textures(ctx, size, mipmaps, &mut textures.into_iter()),
283 depth_slot: DS::reify_texture(ctx, size, mipmaps, depth_texture),
284 state: ctx.state().clone(),
285 _l: PhantomData,
286 _d: PhantomData,
287 };
288
289 match get_status() {
290 Ok(_) => {
291 ctx.state().borrow_mut().bind_draw_framebuffer(0); // FIXME: see whether really needed
292
293 Ok(framebuffer)
294 }
295
296 Err(reason) => {
297 ctx.state().borrow_mut().bind_draw_framebuffer(0); // FIXME: see whether really needed
298
299 framebuffer.destroy();
300
301 Err(FramebufferError::Incomplete(reason))
302 }
303 }
304 }
305 }
306
307 // Destroy OpenGL-side stuff.
destroy(&self)308 fn destroy(&self) {
309 unsafe {
310 if let Some(renderbuffer) = self.renderbuffer {
311 gl::DeleteRenderbuffers(1, &renderbuffer);
312 gl::BindRenderbuffer(gl::RENDERBUFFER, 0);
313 }
314
315 if self.handle != 0 {
316 gl::DeleteFramebuffers(1, &self.handle);
317 self.state.borrow_mut().bind_vertex_array(0, Bind::Cached);
318 }
319 }
320 }
321
322 /// OpenGL handle of the framebuffer.
323 #[inline]
handle(&self) -> GLuint324 pub(crate) fn handle(&self) -> GLuint {
325 self.handle
326 }
327
328 /// Width of the framebuffer.
329 #[inline]
width(&self) -> u32330 pub fn width(&self) -> u32 {
331 self.w
332 }
333
334 /// Height of the framebuffer.
335 #[inline]
height(&self) -> u32336 pub fn height(&self) -> u32 {
337 self.h
338 }
339
340 /// Access the underlying color slot.
341 #[inline]
color_slot(&self) -> &CS::ColorTextures342 pub fn color_slot(&self) -> &CS::ColorTextures {
343 &self.color_slot
344 }
345
346 /// Access the underlying depth slot.
347 #[inline]
depth_slot(&self) -> &DS::DepthTexture348 pub fn depth_slot(&self) -> &DS::DepthTexture {
349 &self.depth_slot
350 }
351 }
352
get_status() -> Result<(), IncompleteReason>353 fn get_status() -> Result<(), IncompleteReason> {
354 let status = unsafe { gl::CheckFramebufferStatus(gl::FRAMEBUFFER) };
355
356 match status {
357 gl::FRAMEBUFFER_COMPLETE => Ok(()),
358 gl::FRAMEBUFFER_UNDEFINED => Err(IncompleteReason::Undefined),
359 gl::FRAMEBUFFER_INCOMPLETE_ATTACHMENT => Err(IncompleteReason::IncompleteAttachment),
360 gl::FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT => Err(IncompleteReason::MissingAttachment),
361 gl::FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER => Err(IncompleteReason::IncompleteDrawBuffer),
362 gl::FRAMEBUFFER_INCOMPLETE_READ_BUFFER => Err(IncompleteReason::IncompleteReadBuffer),
363 gl::FRAMEBUFFER_UNSUPPORTED => Err(IncompleteReason::Unsupported),
364 gl::FRAMEBUFFER_INCOMPLETE_MULTISAMPLE => Err(IncompleteReason::IncompleteMultisample),
365 gl::FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS => Err(IncompleteReason::IncompleteLayerTargets),
366 _ => panic!(
367 "unknown OpenGL framebuffer incomplete status! status={}",
368 status
369 ),
370 }
371 }
372
373 /// A framebuffer has a color slot. A color slot can either be empty (the *unit* type is used,`()`)
374 /// or several color formats.
375 pub unsafe trait ColorSlot<L, D>
376 where
377 L: Layerable,
378 D: Dimensionable,
379 D::Size: Copy,
380 {
381 /// Textures associated with this color slot.
382 type ColorTextures;
383
384 /// Turn a color slot into a list of pixel formats.
color_formats() -> Vec<PixelFormat>385 fn color_formats() -> Vec<PixelFormat>;
386
387 /// Reify a list of raw textures.
reify_textures<C, I>( ctx: &mut C, size: D::Size, mipmaps: usize, textures: &mut I, ) -> Self::ColorTextures where C: GraphicsContext, I: Iterator<Item = GLuint>388 fn reify_textures<C, I>(
389 ctx: &mut C,
390 size: D::Size,
391 mipmaps: usize,
392 textures: &mut I,
393 ) -> Self::ColorTextures
394 where
395 C: GraphicsContext,
396 I: Iterator<Item = GLuint>;
397 }
398
399 unsafe impl<L, D> ColorSlot<L, D> for ()
400 where
401 L: Layerable,
402 D: Dimensionable,
403 D::Size: Copy,
404 {
405 type ColorTextures = ();
406
color_formats() -> Vec<PixelFormat>407 fn color_formats() -> Vec<PixelFormat> {
408 Vec::new()
409 }
410
reify_textures<C, I>(_: &mut C, _: D::Size, _: usize, _: &mut I) -> Self::ColorTextures where C: GraphicsContext, I: Iterator<Item = GLuint>,411 fn reify_textures<C, I>(_: &mut C, _: D::Size, _: usize, _: &mut I) -> Self::ColorTextures
412 where
413 C: GraphicsContext,
414 I: Iterator<Item = GLuint>,
415 {
416 }
417 }
418
419 unsafe impl<L, D, P> ColorSlot<L, D> for P
420 where
421 L: Layerable,
422 D: Dimensionable,
423 D::Size: Copy,
424 Self: ColorPixel + RenderablePixel,
425 {
426 type ColorTextures = Texture<L, D, P>;
427
color_formats() -> Vec<PixelFormat>428 fn color_formats() -> Vec<PixelFormat> {
429 vec![P::pixel_format()]
430 }
431
reify_textures<C, I>( ctx: &mut C, size: D::Size, mipmaps: usize, textures: &mut I, ) -> Self::ColorTextures where C: GraphicsContext, I: Iterator<Item = GLuint>,432 fn reify_textures<C, I>(
433 ctx: &mut C,
434 size: D::Size,
435 mipmaps: usize,
436 textures: &mut I,
437 ) -> Self::ColorTextures
438 where
439 C: GraphicsContext,
440 I: Iterator<Item = GLuint>,
441 {
442 let color_texture = textures.next().unwrap();
443
444 unsafe {
445 let raw = RawTexture::new(
446 ctx.state().clone(),
447 color_texture,
448 opengl_target(L::layering(), D::dim()),
449 );
450 Texture::from_raw(raw, size, mipmaps)
451 }
452 }
453 }
454
455 macro_rules! impl_color_slot_tuple {
456 ($($pf:ident),*) => {
457 unsafe impl<L, D, $($pf),*> ColorSlot<L, D> for ($($pf),*)
458 where L: Layerable,
459 D: Dimensionable,
460 D::Size: Copy,
461 $(
462 $pf: ColorPixel + RenderablePixel
463 ),* {
464 type ColorTextures = ($(Texture<L, D, $pf>),*);
465
466 fn color_formats() -> Vec<PixelFormat> {
467 vec![$($pf::pixel_format()),*]
468 }
469
470 fn reify_textures<C, I>(
471 ctx: &mut C,
472 size: D::Size,
473 mipmaps: usize,
474 textures: &mut I
475 ) -> Self::ColorTextures
476 where C: GraphicsContext,
477 I: Iterator<Item = GLuint> {
478 ($($pf::reify_textures(ctx, size, mipmaps, textures)),*)
479 }
480 }
481 }
482 }
483
484 macro_rules! impl_color_slot_tuples {
485 ($first:ident , $second:ident) => {
486 // stop at pairs
487 impl_color_slot_tuple!($first, $second);
488 };
489
490 ($first:ident , $($pf:ident),*) => {
491 // implement the same list without the first type (reduced by one)
492 impl_color_slot_tuples!($($pf),*);
493 // implement the current list
494 impl_color_slot_tuple!($first, $($pf),*);
495 };
496 }
497
498 impl_color_slot_tuples!(P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10, P11);
499
500 /// A framebuffer has a depth slot. A depth slot can either be empty (the *unit* type is used, `()`)
501 /// or a single depth format.
502 pub unsafe trait DepthSlot<L, D>
503 where
504 L: Layerable,
505 D: Dimensionable,
506 D::Size: Copy,
507 {
508 /// Texture associated with this color slot.
509 type DepthTexture;
510
511 /// Turn a depth slot into a pixel format.
depth_format() -> Option<PixelFormat>512 fn depth_format() -> Option<PixelFormat>;
513
514 /// Reify a raw textures into a depth slot.
reify_texture<C, T>( ctx: &mut C, size: D::Size, mipmaps: usize, texture: T, ) -> Self::DepthTexture where C: GraphicsContext, T: Into<Option<GLuint>>515 fn reify_texture<C, T>(
516 ctx: &mut C,
517 size: D::Size,
518 mipmaps: usize,
519 texture: T,
520 ) -> Self::DepthTexture
521 where
522 C: GraphicsContext,
523 T: Into<Option<GLuint>>;
524 }
525
526 unsafe impl<L, D> DepthSlot<L, D> for ()
527 where
528 L: Layerable,
529 D: Dimensionable,
530 D::Size: Copy,
531 {
532 type DepthTexture = ();
533
depth_format() -> Option<PixelFormat>534 fn depth_format() -> Option<PixelFormat> {
535 None
536 }
537
reify_texture<C, T>(_: &mut C, _: D::Size, _: usize, _: T) -> Self::DepthTexture where C: GraphicsContext, T: Into<Option<GLuint>>,538 fn reify_texture<C, T>(_: &mut C, _: D::Size, _: usize, _: T) -> Self::DepthTexture
539 where
540 C: GraphicsContext,
541 T: Into<Option<GLuint>>,
542 {
543 }
544 }
545
546 unsafe impl<L, D, P> DepthSlot<L, D> for P
547 where
548 L: Layerable,
549 D: Dimensionable,
550 D::Size: Copy,
551 P: DepthPixel,
552 {
553 type DepthTexture = Texture<L, D, P>;
554
depth_format() -> Option<PixelFormat>555 fn depth_format() -> Option<PixelFormat> {
556 Some(P::pixel_format())
557 }
558
reify_texture<C, T>( ctx: &mut C, size: D::Size, mipmaps: usize, texture: T, ) -> Self::DepthTexture where C: GraphicsContext, T: Into<Option<GLuint>>,559 fn reify_texture<C, T>(
560 ctx: &mut C,
561 size: D::Size,
562 mipmaps: usize,
563 texture: T,
564 ) -> Self::DepthTexture
565 where
566 C: GraphicsContext,
567 T: Into<Option<GLuint>>,
568 {
569 unsafe {
570 let raw = RawTexture::new(
571 ctx.state().clone(),
572 texture.into().unwrap(),
573 opengl_target(L::layering(), D::dim()),
574 );
575 Texture::from_raw(raw, size, mipmaps)
576 }
577 }
578 }
579