glium/framebuffer/
render_buffer.rs

1/*!
2
3A render buffer is similar to a texture, but is optimized for usage as a draw target.
4
5Contrary to a texture, you can't sample nor modify the content of a render buffer.
6You should prefer render buffers over textures when you know that you don't need to read or modify
7the data of the render buffer.
8
9*/
10use std::rc::Rc;
11use std::ops::{Deref, DerefMut};
12use std::fmt;
13use std::error::Error;
14
15use crate::framebuffer::{ColorAttachment, ToColorAttachment};
16use crate::framebuffer::{DepthAttachment, ToDepthAttachment};
17use crate::framebuffer::{StencilAttachment, ToStencilAttachment};
18use crate::framebuffer::{DepthStencilAttachment, ToDepthStencilAttachment};
19use crate::texture::{UncompressedFloatFormat, DepthFormat, StencilFormat, DepthStencilFormat, TextureKind};
20
21use crate::image_format;
22
23use crate::gl;
24use crate::GlObject;
25use crate::fbo::FramebuffersContainer;
26use crate::backend::Facade;
27use crate::context::Context;
28use crate::ContextExt;
29use crate::version::Version;
30use crate::version::Api;
31
32/// Error while creating a render buffer.
33#[derive(Copy, Clone, Debug)]
34pub enum CreationError {
35    /// The requested format is not supported.
36    FormatNotSupported,
37}
38
39impl fmt::Display for CreationError {
40    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
41        use self::CreationError::*;
42        let desc = match *self {
43            FormatNotSupported => "The requested format is not supported",
44        };
45        fmt.write_str(desc)
46    }
47}
48
49impl Error for CreationError {}
50
51impl From<image_format::FormatNotSupportedError> for CreationError {
52    fn from(_: image_format::FormatNotSupportedError) -> CreationError {
53        CreationError::FormatNotSupported
54    }
55}
56
57/// A render buffer is similar to a texture, but is optimized for usage as a draw target.
58///
59/// Contrary to a texture, you can't sample or modify the content of the `RenderBuffer`.
60pub struct RenderBuffer {
61    buffer: RenderBufferAny,
62}
63
64impl RenderBuffer {
65    /// Builds a new render buffer.
66    pub fn new<F: ?Sized>(facade: &F, format: UncompressedFloatFormat, width: u32, height: u32)
67                  -> Result<RenderBuffer, CreationError> where F: Facade
68    {
69        let format = image_format::TextureFormatRequest::Specific(image_format::TextureFormat::UncompressedFloat(format));
70        let format = image_format::format_request_to_glenum(&facade.get_context(), format, image_format::RequestType::Renderbuffer)?;
71
72        Ok(RenderBuffer {
73            buffer: RenderBufferAny::new(facade, format, TextureKind::Float, width, height, None)
74        })
75    }
76    /// Builds a new render buffer with multisampling.
77    pub fn new_multisample<F: ?Sized>(facade: &F, format: UncompressedFloatFormat, width: u32, height: u32, samples: u32)
78                  -> Result<RenderBuffer, CreationError> where F: Facade
79    {
80        let format = image_format::TextureFormatRequest::Specific(image_format::TextureFormat::UncompressedFloat(format));
81        let format = image_format::format_request_to_glenum(&facade.get_context(), format, image_format::RequestType::Renderbuffer)?;
82
83        Ok(RenderBuffer {
84            buffer: RenderBufferAny::new(facade, format, TextureKind::Float, width, height, Some(samples))
85        })
86    }
87}
88
89impl<'a> ToColorAttachment<'a> for &'a RenderBuffer {
90    #[inline]
91    fn to_color_attachment(self) -> ColorAttachment<'a> {
92        ColorAttachment::RenderBuffer(self)
93    }
94}
95
96impl Deref for RenderBuffer {
97    type Target = RenderBufferAny;
98
99    #[inline]
100    fn deref(&self) -> &RenderBufferAny {
101        &self.buffer
102    }
103}
104
105impl DerefMut for RenderBuffer {
106    #[inline]
107    fn deref_mut(&mut self) -> &mut RenderBufferAny {
108        &mut self.buffer
109    }
110}
111
112impl GlObject for RenderBuffer {
113    type Id = gl::types::GLuint;
114
115    #[inline]
116    fn get_id(&self) -> gl::types::GLuint {
117        self.buffer.get_id()
118    }
119}
120
121/// A render buffer is similar to a texture, but is optimized for usage as a draw target.
122///
123/// Contrary to a texture, you can't sample or modify the content of the `DepthRenderBuffer` directly.
124pub struct DepthRenderBuffer {
125    buffer: RenderBufferAny,
126}
127
128impl DepthRenderBuffer {
129    /// Builds a new render buffer.
130    pub fn new<F: ?Sized>(facade: &F, format: DepthFormat, width: u32, height: u32)
131                  -> Result<DepthRenderBuffer, CreationError> where F: Facade
132    {
133        let format = image_format::TextureFormatRequest::Specific(image_format::TextureFormat::DepthFormat(format));
134        let format = image_format::format_request_to_glenum(&facade.get_context(), format, image_format::RequestType::Renderbuffer)?;
135
136        Ok(DepthRenderBuffer {
137            buffer: RenderBufferAny::new(facade, format, TextureKind::Depth, width, height, None)
138        })
139    }
140    /// Builds a new render buffer with multisampling.
141    pub fn new_multisample<F: ?Sized>(facade: &F, format: DepthFormat, width: u32, height: u32, samples: u32)
142                  -> Result<DepthRenderBuffer, CreationError> where F: Facade
143    {
144        let format = image_format::TextureFormatRequest::Specific(image_format::TextureFormat::DepthFormat(format));
145        let format = image_format::format_request_to_glenum(&facade.get_context(), format, image_format::RequestType::Renderbuffer)?;
146
147        Ok(DepthRenderBuffer {
148            buffer: RenderBufferAny::new(facade, format, TextureKind::Depth, width, height, Some(samples))
149        })
150    }
151}
152
153impl<'a> ToDepthAttachment<'a> for &'a DepthRenderBuffer {
154    fn to_depth_attachment(self) -> DepthAttachment<'a> {
155        DepthAttachment::RenderBuffer(self)
156    }
157}
158
159impl Deref for DepthRenderBuffer {
160    type Target = RenderBufferAny;
161
162    #[inline]
163    fn deref(&self) -> &RenderBufferAny {
164        &self.buffer
165    }
166}
167
168impl DerefMut for DepthRenderBuffer {
169    #[inline]
170    fn deref_mut(&mut self) -> &mut RenderBufferAny {
171        &mut self.buffer
172    }
173}
174
175impl GlObject for DepthRenderBuffer {
176    type Id = gl::types::GLuint;
177
178    #[inline]
179    fn get_id(&self) -> gl::types::GLuint {
180        self.buffer.get_id()
181    }
182}
183
184/// A render buffer is similar to a texture, but is optimized for usage as a draw target.
185///
186/// Contrary to a texture, you can't sample or modify the content of the `StencilRenderBuffer` directly.
187pub struct StencilRenderBuffer {
188    buffer: RenderBufferAny,
189}
190
191impl StencilRenderBuffer {
192    /// Builds a new render buffer.
193    pub fn new<F: ?Sized>(facade: &F, format: StencilFormat, width: u32, height: u32)
194                  -> Result<StencilRenderBuffer, CreationError> where F: Facade
195    {
196        let format = image_format::TextureFormatRequest::Specific(image_format::TextureFormat::StencilFormat(format));
197        let format = image_format::format_request_to_glenum(&facade.get_context(), format, image_format::RequestType::Renderbuffer)?;
198
199        Ok(StencilRenderBuffer {
200            buffer: RenderBufferAny::new(facade, format, TextureKind::Stencil, width, height, None)
201        })
202    }
203    /// Builds a new render buffer with multisampling.
204    pub fn new_multisample<F: ?Sized>(facade: &F, format: StencilFormat, width: u32, height: u32, samples: u32)
205                  -> Result<StencilRenderBuffer, CreationError> where F: Facade
206    {
207        let format = image_format::TextureFormatRequest::Specific(image_format::TextureFormat::StencilFormat(format));
208        let format = image_format::format_request_to_glenum(&facade.get_context(), format, image_format::RequestType::Renderbuffer)?;
209
210        Ok(StencilRenderBuffer {
211            buffer: RenderBufferAny::new(facade, format, TextureKind::Stencil, width, height, Some(samples))
212        })
213    }
214}
215
216impl<'a> ToStencilAttachment<'a> for &'a StencilRenderBuffer {
217    #[inline]
218    fn to_stencil_attachment(self) -> StencilAttachment<'a> {
219        StencilAttachment::RenderBuffer(self)
220    }
221}
222
223impl Deref for StencilRenderBuffer {
224    type Target = RenderBufferAny;
225
226    #[inline]
227    fn deref(&self) -> &RenderBufferAny {
228        &self.buffer
229    }
230}
231
232impl DerefMut for StencilRenderBuffer {
233    #[inline]
234    fn deref_mut(&mut self) -> &mut RenderBufferAny {
235        &mut self.buffer
236    }
237}
238
239impl GlObject for StencilRenderBuffer {
240    type Id = gl::types::GLuint;
241
242    #[inline]
243    fn get_id(&self) -> gl::types::GLuint {
244        self.buffer.get_id()
245    }
246}
247
248/// A render buffer is similar to a texture, but is optimized for usage as a draw target.
249///
250/// Contrary to a texture, you can't sample or modify the content of the `DepthStencilRenderBuffer` directly.
251pub struct DepthStencilRenderBuffer {
252    buffer: RenderBufferAny,
253}
254
255impl DepthStencilRenderBuffer {
256    /// Builds a new render buffer.
257    pub fn new<F: ?Sized>(facade: &F, format: DepthStencilFormat, width: u32, height: u32)
258                  -> Result<DepthStencilRenderBuffer, CreationError> where F: Facade
259    {
260        let format = image_format::TextureFormatRequest::Specific(image_format::TextureFormat::DepthStencilFormat(format));
261        let format = image_format::format_request_to_glenum(&facade.get_context(), format, image_format::RequestType::Renderbuffer)?;
262
263        Ok(DepthStencilRenderBuffer {
264            buffer: RenderBufferAny::new(facade, format, TextureKind::DepthStencil, width, height, None)
265        })
266    }
267    /// Builds a new render buffer with multisampling.
268    pub fn new_multisample<F: ?Sized>(facade: &F, format: DepthStencilFormat, width: u32, height: u32, samples: u32)
269                  -> Result<DepthStencilRenderBuffer, CreationError> where F: Facade
270    {
271        let format = image_format::TextureFormatRequest::Specific(image_format::TextureFormat::DepthStencilFormat(format));
272        let format = image_format::format_request_to_glenum(&facade.get_context(), format, image_format::RequestType::Renderbuffer)?;
273
274        Ok(DepthStencilRenderBuffer {
275            buffer: RenderBufferAny::new(facade, format, TextureKind::DepthStencil, width, height, Some(samples))
276        })
277    }
278}
279
280impl<'a> ToDepthStencilAttachment<'a> for &'a DepthStencilRenderBuffer {
281    #[inline]
282    fn to_depth_stencil_attachment(self) -> DepthStencilAttachment<'a> {
283        DepthStencilAttachment::RenderBuffer(self)
284    }
285}
286
287impl Deref for DepthStencilRenderBuffer {
288    type Target = RenderBufferAny;
289
290    #[inline]
291    fn deref(&self) -> &RenderBufferAny {
292        &self.buffer
293    }
294}
295
296impl DerefMut for DepthStencilRenderBuffer {
297    #[inline]
298    fn deref_mut(&mut self) -> &mut RenderBufferAny {
299        &mut self.buffer
300    }
301}
302
303impl GlObject for DepthStencilRenderBuffer {
304    type Id = gl::types::GLuint;
305
306    #[inline]
307    fn get_id(&self) -> gl::types::GLuint {
308        self.buffer.get_id()
309    }
310}
311
312/// A RenderBuffer of indeterminate type.
313pub struct RenderBufferAny {
314    context: Rc<Context>,
315    id: gl::types::GLuint,
316    width: u32,
317    height: u32,
318    samples: Option<u32>,
319    kind: TextureKind,
320}
321
322impl RenderBufferAny {
323    /// Builds a new render buffer.
324    fn new<F: ?Sized>(facade: &F, format: gl::types::GLenum, kind: TextureKind, width: u32, height: u32,
325              samples: Option<u32>) -> RenderBufferAny
326        where F: Facade
327    {
328        unsafe {
329            // TODO: check that dimensions don't exceed GL_MAX_RENDERBUFFER_SIZE
330            // FIXME: gles2 only supports very few formats
331            let mut ctxt = facade.get_context().make_current();
332            let mut id = 0;
333
334            if ctxt.version >= &Version(Api::Gl, 4, 5) ||
335               ctxt.extensions.gl_arb_direct_state_access
336            {
337                ctxt.gl.CreateRenderbuffers(1, &mut id);
338                if let Some(samples) = samples {
339                    ctxt.gl.NamedRenderbufferStorageMultisample(id, samples as gl::types::GLsizei,
340                                                                format, width as gl::types::GLsizei,
341                                                                height as gl::types::GLsizei);
342                } else {
343                    ctxt.gl.NamedRenderbufferStorage(id, format, width as gl::types::GLsizei,
344                                                     height as gl::types::GLsizei);
345                }
346
347            } else if samples.is_some() && (ctxt.version >= &Version(Api::Gl, 3, 0) ||
348                                            ctxt.version >= &Version(Api::GlEs, 3, 0) ||
349                                            ctxt.extensions.gl_apple_framebuffer_multisample ||
350                                            ctxt.extensions.gl_angle_framebuffer_multisample ||
351                                            ctxt.extensions.gl_ext_multisampled_render_to_texture ||
352                                            ctxt.extensions.gl_nv_framebuffer_multisample)
353            {
354                ctxt.gl.GenRenderbuffers(1, &mut id);
355                ctxt.gl.BindRenderbuffer(gl::RENDERBUFFER, id);
356                ctxt.state.renderbuffer = id;
357
358                let samples = samples.unwrap();
359
360                if ctxt.version >= &Version(Api::Gl, 3, 0) ||
361                   ctxt.version >= &Version(Api::GlEs, 3, 0)
362                {
363                    ctxt.gl.RenderbufferStorageMultisample(gl::RENDERBUFFER,
364                                                           samples as gl::types::GLsizei,
365                                                           format,
366                                                           width as gl::types::GLsizei,
367                                                           height as gl::types::GLsizei);
368
369                } else if ctxt.extensions.gl_apple_framebuffer_multisample {
370                    ctxt.gl.RenderbufferStorageMultisampleAPPLE(gl::RENDERBUFFER,
371                                                                samples as gl::types::GLsizei,
372                                                                format,
373                                                                width as gl::types::GLsizei,
374                                                                height as gl::types::GLsizei);
375
376                } else if ctxt.extensions.gl_angle_framebuffer_multisample {
377                    ctxt.gl.RenderbufferStorageMultisampleANGLE(gl::RENDERBUFFER,
378                                                                samples as gl::types::GLsizei,
379                                                                format,
380                                                                width as gl::types::GLsizei,
381                                                                height as gl::types::GLsizei);
382
383                } else if ctxt.extensions.gl_ext_multisampled_render_to_texture {
384                    ctxt.gl.RenderbufferStorageMultisampleEXT(gl::RENDERBUFFER,
385                                                              samples as gl::types::GLsizei,
386                                                              format,
387                                                              width as gl::types::GLsizei,
388                                                              height as gl::types::GLsizei);
389
390                } else if ctxt.extensions.gl_nv_framebuffer_multisample {
391                    ctxt.gl.RenderbufferStorageMultisampleNV(gl::RENDERBUFFER,
392                                                             samples as gl::types::GLsizei,
393                                                             format,
394                                                             width as gl::types::GLsizei,
395                                                             height as gl::types::GLsizei);
396
397                } else {
398                    unreachable!();
399                }
400
401            } else if samples.is_none() && (ctxt.version >= &Version(Api::Gl, 3, 0) ||
402                                            ctxt.version >= &Version(Api::GlEs, 2, 0))
403            {
404                ctxt.gl.GenRenderbuffers(1, &mut id);
405                ctxt.gl.BindRenderbuffer(gl::RENDERBUFFER, id);
406                ctxt.state.renderbuffer = id;
407                ctxt.gl.RenderbufferStorage(gl::RENDERBUFFER, format,
408                                            width as gl::types::GLsizei,
409                                            height as gl::types::GLsizei);
410
411            } else if samples.is_some() && ctxt.extensions.gl_ext_framebuffer_object &&
412                      ctxt.extensions.gl_ext_framebuffer_multisample
413            {
414                ctxt.gl.GenRenderbuffersEXT(1, &mut id);
415                ctxt.gl.BindRenderbufferEXT(gl::RENDERBUFFER_EXT, id);
416                ctxt.state.renderbuffer = id;
417
418                let samples = samples.unwrap();
419                ctxt.gl.RenderbufferStorageMultisampleEXT(gl::RENDERBUFFER_EXT,
420                                                          samples as gl::types::GLsizei,
421                                                          format,
422                                                          width as gl::types::GLsizei,
423                                                          height as gl::types::GLsizei);
424
425            } else if samples.is_none() && ctxt.extensions.gl_ext_framebuffer_object {
426                ctxt.gl.GenRenderbuffersEXT(1, &mut id);
427                ctxt.gl.BindRenderbufferEXT(gl::RENDERBUFFER_EXT, id);
428                ctxt.state.renderbuffer = id;
429                ctxt.gl.RenderbufferStorageEXT(gl::RENDERBUFFER_EXT, format,
430                                               width as gl::types::GLsizei,
431                                               height as gl::types::GLsizei);
432
433            } else {
434                unreachable!();
435            }
436
437            RenderBufferAny {
438                context: facade.get_context().clone(),
439                id,
440                width,
441                height,
442                samples,
443                kind,
444            }
445        }
446    }
447
448    /// Returns the dimensions of the render buffer.
449    #[inline]
450    pub fn get_dimensions(&self) -> (u32, u32) {
451        (self.width, self.height)
452    }
453
454    /// Returns the number of samples of the render buffer, or `None` if multisampling isn't
455    /// enabled.
456    #[inline]
457    pub fn get_samples(&self) -> Option<u32> {
458        self.samples
459    }
460
461    /// Returns the context used to create this renderbuffer.
462    #[inline]
463    pub fn get_context(&self) -> &Rc<Context> {
464        &self.context
465    }
466
467    /// Returns the kind of renderbuffer.
468    #[inline]
469    pub fn kind(&self) -> TextureKind {
470        self.kind
471    }
472
473    /// Determines the number of depth and stencil bits in the format of this render buffer.
474    pub fn get_depth_stencil_bits(&self) -> (u16, u16) {
475        unsafe {
476            let ctxt = self.context.make_current();
477            let mut depth_bits: gl::types::GLint = 0;
478            let mut stencil_bits: gl::types::GLint = 0;
479            ctxt.gl.BindRenderbuffer(gl::RENDERBUFFER, self.id);
480            // FIXME: GL version considerations
481            ctxt.gl.GetRenderbufferParameteriv(gl::RENDERBUFFER, gl::RENDERBUFFER_DEPTH_SIZE, &mut depth_bits);
482            ctxt.gl.GetRenderbufferParameteriv(gl::RENDERBUFFER, gl::RENDERBUFFER_STENCIL_SIZE, &mut stencil_bits);
483            ctxt.gl.BindRenderbuffer(gl::RENDERBUFFER, 0);
484            (depth_bits as u16, stencil_bits as u16)
485        }
486    }
487}
488
489impl Drop for RenderBufferAny {
490    fn drop(&mut self) {
491        unsafe {
492            let mut ctxt = self.context.make_current();
493
494            // removing FBOs which contain this buffer
495            FramebuffersContainer::purge_renderbuffer(&mut ctxt, self.id);
496
497            if ctxt.version >= &Version(Api::Gl, 3, 0) ||
498               ctxt.version >= &Version(Api::GlEs, 2, 0)
499            {
500                if ctxt.state.renderbuffer == self.id {
501                    ctxt.state.renderbuffer = 0;
502                }
503
504                ctxt.gl.DeleteRenderbuffers(1, [ self.id ].as_ptr());
505
506            } else if ctxt.extensions.gl_ext_framebuffer_object {
507                if ctxt.state.renderbuffer == self.id {
508                    ctxt.state.renderbuffer = 0;
509                }
510
511                ctxt.gl.DeleteRenderbuffersEXT(1, [ self.id ].as_ptr());
512
513            } else {
514                unreachable!();
515            }
516        }
517    }
518}
519
520impl GlObject for RenderBufferAny {
521    type Id = gl::types::GLuint;
522
523    #[inline]
524    fn get_id(&self) -> gl::types::GLuint {
525        self.id
526    }
527}