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