glium/
fbo.rs

1/*!
2Contains everything related to the internal handling of framebuffer objects.
3
4*/
5/*
6Here are the rules taken from the official wiki:
7
8Attachment Completeness
9
10Each attachment point itctxt.framebuffer_objects must be complete according to these rules. Empty attachments
11(attachments with no image attached) are complete by default. If an image is attached, it must
12adhere to the following rules:
13
14The source object for the image still exists and has the same type it was attached with.
15The image has a non-zero width and height (the height of a 1D image is assumed to be 1). The
16  width/height must also be less than GL_MAX_FRAMEBUFFER_WIDTH and GL_MAX_FRAMEBUFFER_HEIGHT
17  respectively (if GL 4.3/ARB_framebuffer_no_attachments).
18The layer for 3D or array textures attachments is less than the depth of the texture. It must
19  also be less than GL_MAX_FRAMEBUFFER_LAYERS (if GL 4.3/ARB_framebuffer_no_attachments).
20The number of samples must be less than GL_MAX_FRAMEBUFFER_SAMPLES (if
21  GL 4.3/ARB_framebuffer_no_attachments).
22The image's format must match the attachment point's requirements, as defined above.
23  Color-renderable formats for color attachments, etc.
24
25Completeness Rules
26
27These are the rules for framebuffer completeness. The order of these rules matters.
28
29If the target​ of glCheckFramebufferStatus references the Default Framebuffer (ie: FBO object
30  number 0 is bound), and the default framebuffer does not exist, then you will get
31  GL_FRAMEBUFFER_UNDEFINEZ. If the default framebuffer exists, then you always get
32  GL_FRAMEBUFFER_COMPLETE. The rest of the rules apply when an FBO is bound.
33All attachments must be attachment complete. (GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT when false).
34There must be at least one image attached to the FBO, or if OpenGL 4.3 or
35  ARB_framebuffer_no_attachment is available, the GL_FRAMEBUFFER_DEFAULT_WIDTH and
36  GL_FRAMEBUFFER_DEFAULT_HEIGHT parameters of the framebuffer must both be non-zero.
37  (GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT when false).
38Each draw buffers must either specify color attachment points that have images attached or
39  must be GL_NONE. (GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER when false). Note that this test is
40  not performed if OpenGL 4.1 or ARB_ES2_compatibility is available.
41If the read buffer is set, then it must specify an attachment point that has an image
42  attached. (GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER when false). Note that this test is not
43  performed if OpenGL 4.1 or ARB_ES2_compatibility is available.
44All images must have the same number of multisample samples.
45  (GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE when false).
46If a layered image is attached to one attachment, then all attachments must be layered
47  attachments. The attached layers do not have to have the same number of layers, nor do the
48  layers have to come from the same kind of texture (a cubemap color texture can be paired
49  with an array depth texture) (GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS when false).
50
51*/
52use std::{ cmp, mem, fmt };
53use std::error::Error;
54use std::cell::RefCell;
55use std::marker::PhantomData;
56use std::hash::BuildHasherDefault;
57use std::collections::HashMap;
58
59use fnv::FnvHasher;
60use smallvec::SmallVec;
61
62use crate::CapabilitiesSource;
63use crate::GlObject;
64use crate::TextureExt;
65
66use crate::texture::CubeLayer;
67use crate::texture::TextureAnyImage;
68use crate::texture::TextureAnyMipmap;
69use crate::texture::TextureKind;
70use crate::framebuffer::RenderBufferAny;
71
72use crate::gl;
73use crate::context::CommandContext;
74use crate::version::Version;
75use crate::version::Api;
76
77/// Returns true if the backend supports attachments with varying dimensions.
78///
79/// If this function returns `true` and you pass attachments with different dimensions, the
80/// intersection between all the attachments will be used. If this function returns `false`, you'll
81/// get an error instead.
82pub fn is_dimensions_mismatch_supported<C: ?Sized>(context: &C) -> bool where C: CapabilitiesSource {
83    context.get_version() >= &Version(Api::Gl, 3, 0) ||
84    context.get_version() >= &Version(Api::GlEs, 2, 0) ||
85    context.get_extensions().gl_arb_framebuffer_object
86}
87
88/// Represents the attachments to use for an OpenGL framebuffer.
89#[derive(Clone)]
90pub enum FramebufferAttachments<'a> {
91    /// Each attachment is a single image.
92    Regular(FramebufferSpecificAttachments<RegularAttachment<'a>>),
93
94    /// Each attachment is a layer of images.
95    Layered(FramebufferSpecificAttachments<LayeredAttachment<'a>>),
96
97    /// An empty framebuffer.
98    Empty {
99        width: u32,
100        height: u32,
101        layers: Option<u32>,
102        samples: Option<u32>,
103        fixed_samples: bool,
104    },
105}
106
107/// Describes a single non-layered framebuffer attachment.
108#[derive(Copy, Clone)]
109pub enum RegularAttachment<'a> {
110    /// A texture.
111    Texture(TextureAnyImage<'a>),
112    /// A renderbuffer.
113    RenderBuffer(&'a RenderBufferAny),
114}
115
116impl<'a> RegularAttachment<'a> {
117    /// Returns the kind of attachment (float, integral, unsigned, depth, stencil, depthstencil).
118    #[inline]
119    pub fn kind(&self) -> TextureKind {
120        match self {
121            RegularAttachment::Texture(t) => t.get_texture().kind(),
122            RegularAttachment::RenderBuffer(rb) => rb.kind(),
123        }
124    }
125}
126
127/// Describes a single layered framebuffer attachment.
128#[derive(Copy, Clone)]
129pub struct LayeredAttachment<'a>(TextureAnyMipmap<'a>);
130
131/// Depth and/or stencil attachment to use.
132#[derive(Copy, Clone)]
133pub enum DepthStencilAttachments<T> {
134    /// No depth or stencil buffer.
135    None,
136
137    /// A depth attachment.
138    DepthAttachment(T),
139
140    /// A stencil attachment.
141    StencilAttachment(T),
142
143    /// A depth attachment and a stencil attachment.
144    DepthAndStencilAttachments(T, T),
145
146    /// A single attachment that serves as both depth and stencil buffer.
147    DepthStencilAttachment(T),
148}
149
150/// Represents the attachments to use for an OpenGL framebuffer.
151#[derive(Clone)]
152pub struct FramebufferSpecificAttachments<T> {
153    /// List of color attachments. The first parameter of the tuple is the index, and the
154    /// second element is the attachment.
155    pub colors: SmallVec<[(u32, T); 5]>,
156
157    /// The depth and/or stencil attachment to use.
158    pub depth_stencil: DepthStencilAttachments<T>,
159}
160
161impl<'a> FramebufferAttachments<'a> {
162    /// After building a `FramebufferAttachments` struct, you must use this function
163    /// to "compile" the attachments and make sure that they are valid together.
164    #[inline]
165    pub fn validate<C: ?Sized>(self, context: &C) -> Result<ValidatedAttachments<'a>, ValidationError>
166                       where C: CapabilitiesSource
167    {
168        match self {
169            FramebufferAttachments::Regular(a) => FramebufferAttachments::validate_regular(context, a),
170            FramebufferAttachments::Layered(a) => FramebufferAttachments::validate_layered(context, a),
171
172            FramebufferAttachments::Empty { width, height, layers, samples, fixed_samples } => {
173                if context.get_version() >= &Version(Api::Gl, 4, 3) ||
174                   context.get_version() >= &Version(Api::GlEs, 3, 1) ||
175                   context.get_extensions().gl_arb_framebuffer_no_attachments
176                {
177                    assert!(width >= 1);
178                    assert!(height >= 1);
179                    if let Some(layers) = layers { assert!(layers >= 1); }
180                    if let Some(samples) = samples { assert!(samples >= 1); }
181
182                    if width > context.get_capabilities().max_framebuffer_width.unwrap_or(0) as u32 ||
183                       height > context.get_capabilities().max_framebuffer_height.unwrap_or(0) as u32 ||
184                       samples.unwrap_or(0) > context.get_capabilities()
185                                                     .max_framebuffer_samples.unwrap_or(0) as u32 ||
186                       layers.unwrap_or(0) > context.get_capabilities()
187                                                    .max_framebuffer_layers.unwrap_or(0) as u32
188                    {
189                        return Err(ValidationError::EmptyFramebufferUnsupportedDimensions);
190                    }
191
192                    Ok(ValidatedAttachments {
193                        raw: RawAttachments {
194                            color: Vec::new(),
195                            depth: None,
196                            stencil: None,
197                            depth_stencil: None,
198                            default_width: Some(width),
199                            default_height: Some(height),
200                            default_layers: if context.get_version() <= &Version(Api::GlEs, 3, 1) { None } else { Some(layers.unwrap_or(0)) },
201                            default_samples: Some(samples.unwrap_or(0)),
202                            default_samples_fixed: Some(fixed_samples),
203                        },
204                        dimensions: (width, height),
205                        layers,
206                        depth_buffer_bits: None,
207                        stencil_buffer_bits: None,
208                        marker: PhantomData,
209                    })
210
211                } else {
212                    Err(ValidationError::EmptyFramebufferObjectsNotSupported)
213                }
214            },
215        }
216    }
217
218    fn validate_layered<C: ?Sized>(context: &C, FramebufferSpecificAttachments { colors, depth_stencil }:
219                           FramebufferSpecificAttachments<LayeredAttachment<'a>>)
220                           -> Result<ValidatedAttachments<'a>, ValidationError>
221                           where C: CapabilitiesSource
222    {
223        // TODO: make sure that all attachments are layered
224
225        macro_rules! handle_tex {
226            ($tex:ident, $dim:ident, $samples:ident, $num_bits:ident) => ({
227                $num_bits = Some($tex.get_texture().get_internal_format()
228                                     .map(|f| f.get_total_bits()).ok().unwrap_or(24) as u16);     // TODO: how to handle this?
229                handle_tex!($tex, $dim, $samples)
230            });
231
232            ($tex:ident, $dim:ident, $samples:ident) => ({
233                // TODO: check that internal format is renderable
234                let context = $tex.get_texture().get_context();
235
236                match &mut $samples {
237                    &mut Some(samples) => {
238                        if samples != $tex.get_samples().unwrap_or(0) {
239                            return Err(ValidationError::SamplesCountMismatch);
240                        }
241                    },
242                    s @ &mut None => {
243                        *s = Some($tex.get_samples().unwrap_or(0));
244                    }
245                }
246
247                match &mut $dim {
248                    &mut Some((ref mut w, ref mut h)) => {
249                        let height = $tex.get_height().unwrap_or(1);
250                        if *w != $tex.get_width() || *h != height {
251                            *w = cmp::min(*w, $tex.get_width());
252                            *h = cmp::min(*h, height);
253
254                            // checking that multiple different sizes is supported by the backend
255                            if !is_dimensions_mismatch_supported(context) {
256                                return Err(ValidationError::DimensionsMismatchNotSupported);
257                            }
258                        }
259                    },
260
261                    dim @ &mut None => {
262                        *dim = Some(($tex.get_width(), $tex.get_height().unwrap_or(1)));
263                    },
264                }
265
266                RawAttachment::Texture {
267                    texture: $tex.get_texture().get_id(),
268                    bind_point: $tex.get_texture().get_bind_point(),
269                    layer: None,
270                    level: $tex.get_level(),
271                    cubemap_layer: None,
272                }
273            });
274        }
275
276        let max_color_attachments = context.get_capabilities().max_color_attachments;
277        if colors.len() > max_color_attachments as usize {
278            return Err(ValidationError::TooManyColorAttachments{
279                maximum: max_color_attachments as usize,
280                obtained: colors.len(),
281            });
282        }
283
284        let mut raw_attachments = RawAttachments {
285            color: Vec::with_capacity(colors.len()),
286            depth: None,
287            stencil: None,
288            depth_stencil: None,
289            default_width: None,
290            default_height: None,
291            default_layers: None,
292            default_samples: None,
293            default_samples_fixed: None,
294        };
295
296        let mut dimensions = None;
297        let mut depth_bits = None;
298        let mut stencil_bits = None;
299        let mut samples = None;     // contains `0` if not multisampling and `None` if unknown
300
301        for &(index, LayeredAttachment(ref attachment)) in colors.iter() {
302            if index >= max_color_attachments as u32 {
303                return Err(ValidationError::TooManyColorAttachments{
304                    maximum: max_color_attachments as usize,
305                    obtained: index as usize,
306                });
307            }
308            raw_attachments.color.push((index, handle_tex!(attachment, dimensions, samples)));
309        }
310
311        match depth_stencil {
312            DepthStencilAttachments::None => (),
313            DepthStencilAttachments::DepthAttachment(LayeredAttachment(ref d)) => {
314                raw_attachments.depth = Some(handle_tex!(d, dimensions, samples, depth_bits));
315            },
316            DepthStencilAttachments::StencilAttachment(LayeredAttachment(ref s)) => {
317                raw_attachments.stencil = Some(handle_tex!(s, dimensions, samples, stencil_bits));
318            },
319            DepthStencilAttachments::DepthAndStencilAttachments(LayeredAttachment(ref d),
320                                                                 LayeredAttachment(ref s))
321            => {
322                raw_attachments.depth = Some(handle_tex!(d, dimensions, samples, depth_bits));
323                raw_attachments.stencil = Some(handle_tex!(s, dimensions, samples, stencil_bits));
324            },
325            DepthStencilAttachments::DepthStencilAttachment(LayeredAttachment(ref ds)) => {
326                let depth_stencil_bits = ds.get_texture().get_depth_stencil_bits();
327                depth_bits = Some(depth_stencil_bits.0);
328                stencil_bits = Some(depth_stencil_bits.1);
329                raw_attachments.depth_stencil = Some(handle_tex!(ds, dimensions, samples));
330            },
331        }
332
333        let dimensions = if let Some(dimensions) = dimensions {
334            if dimensions.0 * dimensions.1 == 0 {
335                return Err(ValidationError::EmptyFramebufferUnsupportedDimensions);
336            }
337            dimensions
338        } else {
339            // TODO: handle this
340            return Err(ValidationError::EmptyFramebufferObjectsNotSupported);
341        };
342
343        Ok(ValidatedAttachments {
344            raw: raw_attachments,
345            dimensions,
346            layers: None,       // FIXME: count layers
347            depth_buffer_bits: depth_bits,
348            stencil_buffer_bits: stencil_bits,
349            marker: PhantomData,
350        })
351    }
352
353    fn validate_regular<C: ?Sized>(context: &C, FramebufferSpecificAttachments { colors, depth_stencil }:
354                        FramebufferSpecificAttachments<RegularAttachment<'a>>)
355                        -> Result<ValidatedAttachments<'a>, ValidationError>
356                        where C: CapabilitiesSource
357    {
358        macro_rules! handle_tex {
359            ($tex:ident, $dim:ident, $samples:ident, $num_bits:ident) => ({
360                $num_bits = Some($tex.get_texture().get_internal_format()
361                                     .map(|f| f.get_total_bits()).ok().unwrap_or(24) as u16);     // TODO: how to handle this?
362                handle_tex!($tex, $dim, $samples)
363            });
364
365            ($tex:ident, $dim:ident, $samples:ident) => ({
366                // TODO: check that internal format is renderable
367                let context = $tex.get_texture().get_context();
368
369                match &mut $samples {
370                    &mut Some(samples) => {
371                        if samples != $tex.get_samples().unwrap_or(0) {
372                            return Err(ValidationError::SamplesCountMismatch);
373                        }
374                    },
375                    s @ &mut None => {
376                        *s = Some($tex.get_samples().unwrap_or(0));
377                    }
378                }
379
380                match &mut $dim {
381                    &mut Some((ref mut w, ref mut h)) => {
382                        let height = $tex.get_height().unwrap_or(1);
383                        if *w != $tex.get_width() || *h != height {
384                            *w = cmp::min(*w, $tex.get_width());
385                            *h = cmp::min(*h, height);
386
387                            // checking that multiple different sizes is supported by the backend
388                            if !is_dimensions_mismatch_supported(context) {
389                                return Err(ValidationError::DimensionsMismatchNotSupported);
390                            }
391                        }
392                    },
393
394                    dim @ &mut None => {
395                        *dim = Some(($tex.get_width(), $tex.get_height().unwrap_or(1)));
396                    },
397                }
398
399                RawAttachment::Texture {
400                    texture: $tex.get_texture().get_id(),
401                    bind_point: $tex.get_texture().get_bind_point(),
402                    layer: Some($tex.get_layer()),
403                    level: $tex.get_level(),
404                    cubemap_layer: $tex.get_cubemap_layer(),
405                }
406            });
407        }
408
409        macro_rules! handle_rb {
410            ($rb:ident, $dim:ident, $samples:ident, $num_bits:ident) => ({
411                $num_bits = Some(24);       // FIXME: totally arbitrary
412                handle_rb!($rb, $dim, $samples)
413            });
414
415            ($rb:ident, $dim:ident, $samples:ident) => ({
416                // TODO: check that internal format is renderable
417                let context = $rb.get_context();
418                let dimensions = $rb.get_dimensions();
419
420                match &mut $samples {
421                    &mut Some(samples) => {
422                        if samples != $rb.get_samples().unwrap_or(0) {
423                            return Err(ValidationError::SamplesCountMismatch);
424                        }
425                    },
426                    s @ &mut None => {
427                        *s = Some($rb.get_samples().unwrap_or(0));
428                    }
429                }
430
431                match &mut $dim {
432                    &mut Some((ref mut w, ref mut h)) => {
433                        if *w != dimensions.0 || *h != dimensions.1 {
434                            *w = cmp::min(*w, dimensions.0);
435                            *h = cmp::min(*h, dimensions.1);
436
437                            // checking that multiple different sizes is supported by the backend
438                            if !is_dimensions_mismatch_supported(context) {
439                                return Err(ValidationError::DimensionsMismatchNotSupported);
440                            }
441                        }
442                    },
443
444                    dim @ &mut None => {
445                        *dim = Some((dimensions.0, dimensions.1));
446                    },
447                }
448
449                RawAttachment::RenderBuffer($rb.get_id())
450            });
451        }
452
453        macro_rules! handle_atch {
454            ($atch:ident, $($t:tt)*) => (
455                match $atch {
456                    &RegularAttachment::Texture(ref tex) => handle_tex!(tex, $($t)*),
457                    &RegularAttachment::RenderBuffer(ref rb) => handle_rb!(rb, $($t)*),
458                }
459            );
460        }
461
462        let max_color_attachments = context.get_capabilities().max_color_attachments;
463        if colors.len() > max_color_attachments as usize {
464            return Err(ValidationError::TooManyColorAttachments{
465                maximum: max_color_attachments as usize,
466                obtained: colors.len(),
467            });
468        }
469
470        let mut raw_attachments = RawAttachments {
471            color: Vec::with_capacity(colors.len()),
472            depth: None,
473            stencil: None,
474            depth_stencil: None,
475            default_width: None,
476            default_height: None,
477            default_layers: None,
478            default_samples: None,
479            default_samples_fixed: None,
480        };
481
482        let mut dimensions = None;
483        let mut depth_bits = None;
484        let mut stencil_bits = None;
485        let mut samples = None;     // contains `0` if not multisampling and `None` if unknown
486
487        for &(index, ref attachment) in colors.iter() {
488            if index >= max_color_attachments as u32 {
489                return Err(ValidationError::TooManyColorAttachments{
490                    maximum: max_color_attachments as usize,
491                    obtained: index as usize,
492                });
493            }
494            raw_attachments.color.push((index, handle_atch!(attachment, dimensions, samples)));
495        }
496
497        match depth_stencil {
498            DepthStencilAttachments::None => (),
499            DepthStencilAttachments::DepthAttachment(ref d) => {
500                raw_attachments.depth = Some(handle_atch!(d, dimensions, samples, depth_bits));
501            },
502            DepthStencilAttachments::StencilAttachment(ref s) => {
503                raw_attachments.stencil = Some(handle_atch!(s, dimensions, samples, stencil_bits));
504            },
505            DepthStencilAttachments::DepthAndStencilAttachments(ref d, ref s) => {
506                raw_attachments.depth = Some(handle_atch!(d, dimensions, samples, depth_bits));
507                raw_attachments.stencil = Some(handle_atch!(s, dimensions, samples, stencil_bits));
508            },
509            DepthStencilAttachments::DepthStencilAttachment(ref ds) => {
510                let depth_stencil_bits = match ds {
511                    RegularAttachment::Texture(ref tex) =>
512                        tex.get_texture().get_depth_stencil_bits(),
513                    RegularAttachment::RenderBuffer(ref rb) =>
514                        rb.get_depth_stencil_bits(),
515                };
516                depth_bits = Some(depth_stencil_bits.0);
517                stencil_bits = Some(depth_stencil_bits.1);
518                raw_attachments.depth_stencil = Some(handle_atch!(ds, dimensions, samples));
519            },
520        }
521
522        let dimensions = if let Some(dimensions) = dimensions {
523            if dimensions.0 * dimensions.1 == 0 {
524                return Err(ValidationError::EmptyFramebufferUnsupportedDimensions);
525            }
526            dimensions
527        } else {
528            // TODO: handle this
529            return Err(ValidationError::EmptyFramebufferObjectsNotSupported);
530        };
531
532        Ok(ValidatedAttachments {
533            raw: raw_attachments,
534            dimensions,
535            layers: None,
536            depth_buffer_bits: depth_bits,
537            stencil_buffer_bits: stencil_bits,
538            marker: PhantomData,
539        })
540    }
541}
542
543/// Represents attachments that have been validated and are usable.
544#[derive(Clone)]
545pub struct ValidatedAttachments<'a> {
546    raw: RawAttachments,
547    dimensions: (u32, u32),
548    layers: Option<u32>,
549    depth_buffer_bits: Option<u16>,
550    stencil_buffer_bits: Option<u16>,
551    marker: PhantomData<&'a ()>,
552}
553
554impl<'a> ValidatedAttachments<'a> {
555    /// Returns `true` if the framebuffer is layered.
556    #[inline]
557    pub fn is_layered(&self) -> bool {
558        self.layers.is_some()
559    }
560
561    /// Returns the dimensions that the framebuffer will have if you use these attachments.
562    #[inline]
563    pub fn get_dimensions(&self) -> (u32, u32) {
564        self.dimensions
565    }
566
567    /// Returns the number of bits of precision of the depth buffer, or `None` if there is no
568    /// depth buffer. Also works for depth-stencil buffers.
569    #[inline]
570    pub fn get_depth_buffer_bits(&self) -> Option<u16> {
571        self.depth_buffer_bits
572    }
573
574    /// Returns the number of bits of precision of the stencil buffer, or `None` if there is no
575    /// stencil buffer. Also works for depth-stencil buffers.
576    #[inline]
577    pub fn get_stencil_buffer_bits(&self) -> Option<u16> {
578        self.stencil_buffer_bits
579    }
580}
581
582/// An error that can happen while validating attachments.
583#[derive(Copy, Clone, Debug, PartialEq, Eq)]
584pub enum ValidationError {
585    /// You requested an empty framebuffer object, but they are not supported.
586    EmptyFramebufferObjectsNotSupported,
587
588    /// The requested characteristics of an empty framebuffer object are out of range.
589    EmptyFramebufferUnsupportedDimensions,
590
591    /// The backend doesn't support attachments with various dimensions.
592    ///
593    /// Note that almost all OpenGL implementations support attachments with various dimensions.
594    /// Only very old versions don't.
595    DimensionsMismatchNotSupported,
596
597    /// All attachments must have the same number of samples.
598    SamplesCountMismatch,
599
600    /// Backends only support a certain number of color attachments.
601    TooManyColorAttachments {
602        /// Maximum number of attachments.
603        maximum: usize,
604        /// Number of attachments that were given.
605        obtained: usize,
606    },
607}
608
609impl fmt::Display for ValidationError {
610    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
611        use self::ValidationError::*;
612        let desc = match self {
613            EmptyFramebufferObjectsNotSupported =>
614                "You requested an empty framebuffer object, but they are not supported",
615            EmptyFramebufferUnsupportedDimensions =>
616                "The requested characteristics of an empty framebuffer object are out of range",
617            DimensionsMismatchNotSupported =>
618                "The backend doesn't support attachments with various dimensions",
619            SamplesCountMismatch =>
620                "All attachments must have the same number of samples",
621            TooManyColorAttachments {..} =>
622                "Backends only support a certain number of color attachments",
623        };
624        match self {
625            TooManyColorAttachments{ ref maximum, ref obtained } =>
626                write!(fmt, "{}: found {}, maximum: {}", desc, obtained, maximum),
627            _ =>
628                fmt.write_str(desc),
629        }
630    }
631}
632
633impl Error for ValidationError {}
634
635/// Data structure stored in the hashmap.
636///
637/// These attachments are guaranteed to be valid.
638#[derive(Hash, Clone, Eq, PartialEq)]
639struct RawAttachments {
640    // for each frag output the location, the attachment to use
641    color: Vec<(u32, RawAttachment)>,
642    depth: Option<RawAttachment>,
643    stencil: Option<RawAttachment>,
644    depth_stencil: Option<RawAttachment>,
645
646    // values to set through `glFramebufferParameteri`, they are `None` if they should not be set
647    default_width: Option<u32>,
648    default_height: Option<u32>,
649    default_layers: Option<u32>,
650    default_samples: Option<u32>,
651    default_samples_fixed: Option<bool>,
652}
653
654/// Single attachment of `RawAttachments`.
655#[derive(Hash, Copy, Clone, Eq, PartialEq)]
656enum RawAttachment {
657    /// A texture.
658    Texture {
659        // a GLenum like `TEXTURE_2D`, `TEXTURE_3D`, etc.
660        bind_point: gl::types::GLenum,      // TODO: Dimensions instead
661        // id of the texture
662        texture: gl::types::GLuint,
663        // if `Some`, use a regular attachment ; if `None`, use a layered attachment
664        // if `None`, the texture **must** be an array, cubemap, or texture 3d
665        layer: Option<u32>,
666        // mipmap level
667        level: u32,
668        // layer of the cubemap, if this is a cubemap
669        cubemap_layer: Option<CubeLayer>,
670    },
671
672    /// A renderbuffer with its ID.
673    RenderBuffer(gl::types::GLuint),
674}
675
676/// Data to pass to the `clear_buffer` function.
677#[derive(Debug, Copy, Clone)]
678pub enum ClearBufferData {
679    /// Suitable for float attachments.
680    Float([f32; 4]),
681    /// Suitable for integral textures.
682    Integral([i32; 4]),
683    /// Suitable for unsigned textures.
684    Unsigned([u32; 4]),
685    /// Suitable for depth attachments.
686    Depth(f32),
687    /// Suitable for stencil attachments.
688    Stencil(i32),
689    /// Suitable for depth-stencil attachments.
690    DepthStencil(f32, i32),
691}
692
693impl From<[f32; 4]> for ClearBufferData {
694    #[inline]
695    fn from(data: [f32; 4]) -> ClearBufferData {
696        ClearBufferData::Float(data)
697    }
698}
699
700impl From<[i32; 4]> for ClearBufferData {
701    #[inline]
702    fn from(data: [i32; 4]) -> ClearBufferData {
703        ClearBufferData::Integral(data)
704    }
705}
706
707impl From<[u32; 4]> for ClearBufferData {
708    #[inline]
709    fn from(data: [u32; 4]) -> ClearBufferData {
710        ClearBufferData::Unsigned(data)
711    }
712}
713
714/// Manages all the framebuffer objects.
715///
716/// `cleanup` **must** be called when destroying the container, otherwise `Drop` will panic.
717pub struct FramebuffersContainer {
718    framebuffers: RefCell<HashMap<RawAttachments, FrameBufferObject, BuildHasherDefault<FnvHasher>>>,
719}
720
721impl FramebuffersContainer {
722    /// Initializes the container.
723    #[inline]
724    pub fn new() -> FramebuffersContainer {
725        FramebuffersContainer {
726            framebuffers: RefCell::new(HashMap::with_hasher(Default::default())),
727        }
728    }
729
730    /// Destroys all framebuffer objects. This is used when using a new context for example.
731    pub fn purge_all(ctxt: &mut CommandContext<'_>) {
732        let mut other = HashMap::with_hasher(Default::default());
733        mem::swap(&mut *ctxt.framebuffer_objects.framebuffers.borrow_mut(), &mut other);
734
735        for (_, obj) in other.into_iter() {
736            obj.destroy(ctxt);
737        }
738    }
739
740    /// Destroys all framebuffer objects that contain a precise texture.
741    #[inline]
742    pub fn purge_texture(ctxt: &mut CommandContext<'_>, texture: gl::types::GLuint) {
743        FramebuffersContainer::purge_if(ctxt, |a| {
744            matches!(a, &RawAttachment::Texture { texture: id, .. } if id == texture)
745        });
746    }
747
748    /// Destroys all framebuffer objects that contain a precise renderbuffer.
749    #[inline]
750    pub fn purge_renderbuffer(ctxt: &mut CommandContext<'_>, renderbuffer: gl::types::GLuint) {
751        FramebuffersContainer::purge_if(ctxt, |a| a == &RawAttachment::RenderBuffer(renderbuffer));
752    }
753
754    /// Destroys all framebuffer objects that match a certain condition.
755    fn purge_if<F>(ctxt: &mut CommandContext<'_>, condition: F)
756                   where F: Fn(&RawAttachment) -> bool
757    {
758        let mut framebuffers = ctxt.framebuffer_objects.framebuffers.borrow_mut();
759
760        let mut attachments = Vec::with_capacity(0);
761        for (key, _) in framebuffers.iter() {
762            if key.color.iter().any(|&(_, ref id)| condition(id)) {
763                attachments.push(key.clone());
764                continue;
765            }
766
767            if let Some(ref atch) = key.depth {
768                if condition(atch) {
769                    attachments.push(key.clone());
770                    continue;
771                }
772            }
773
774            if let Some(ref atch) = key.stencil {
775                if condition(atch) {
776                    attachments.push(key.clone());
777                    continue;
778                }
779            }
780
781            if let Some(ref atch) = key.depth_stencil {
782                if condition(atch) {
783                    attachments.push(key.clone());
784                    continue;
785                }
786            }
787        }
788
789        for atch in attachments.into_iter() {
790            framebuffers.remove(&atch).unwrap().destroy(ctxt);
791        }
792    }
793
794    /// Destroys all framebuffer objects.
795    ///
796    /// This is very similar to `purge_all`, but optimized for when the container will soon
797    /// be destroyed.
798    pub fn cleanup(ctxt: &mut CommandContext<'_>) {
799        let mut other = HashMap::with_hasher(Default::default());
800        mem::swap(&mut *ctxt.framebuffer_objects.framebuffers.borrow_mut(), &mut other);
801
802        for (_, obj) in other.into_iter() {
803            obj.destroy(ctxt);
804        }
805    }
806
807    ///
808    /// # Unsafety
809    ///
810    /// After calling this function, you **must** make sure to call `purge_texture`
811    /// and/or `purge_renderbuffer` when one of the attachment is destroyed.
812    #[inline]
813    pub fn get_framebuffer_for_drawing(ctxt: &mut CommandContext<'_>,
814                                       attachments: Option<&ValidatedAttachments<'_>>)
815                                       -> gl::types::GLuint
816    {
817        if let Some(attachments) = attachments {
818            FramebuffersContainer::get_framebuffer(ctxt, attachments)
819        } else {
820            0
821        }
822    }
823
824    /// Binds the default framebuffer to `GL_READ_FRAMEBUFFER` or `GL_FRAMEBUFFER` so that it
825    /// becomes the target of `glReadPixels`, `glCopyTexImage2D`, etc.
826    // TODO: use an enum for the read buffer instead
827    #[inline]
828    pub fn bind_default_framebuffer_for_reading(ctxt: &mut CommandContext<'_>,
829                                                read_buffer: gl::types::GLenum)
830    {
831        unsafe { bind_framebuffer(ctxt, 0, false, true) };
832        unsafe { ctxt.gl.ReadBuffer(read_buffer) };     // TODO: cache
833    }
834
835    /// Binds a framebuffer to `GL_READ_FRAMEBUFFER` or `GL_FRAMEBUFFER` so that it becomes the
836    /// target of `glReadPixels`, `glCopyTexImage2D`, etc.
837    ///
838    /// # Unsafety
839    ///
840    /// After calling this function, you **must** make sure to call `purge_texture`
841    /// and/or `purge_renderbuffer` when one of the attachment is destroyed.
842    pub unsafe fn bind_framebuffer_for_reading(ctxt: &mut CommandContext<'_>, attachment: &RegularAttachment<'_>) {
843        // TODO: restore this optimisation
844        /*for (attachments, fbo) in ctxt.framebuffer_objects.framebuffers.borrow_mut().iter() {
845            for &(key, ref atc) in attachments.color.iter() {
846                if atc == attachment {
847                    return (fbo.get_id(), gl::COLOR_ATTACHMENT0 + key);
848                }
849            }
850        }*/
851
852        let attachments = FramebufferAttachments::Regular(FramebufferSpecificAttachments {
853            colors: { let mut v = SmallVec::new(); v.push((0, *attachment)); v },
854            depth_stencil: DepthStencilAttachments::None,
855        }).validate(ctxt).unwrap();
856
857        let framebuffer = FramebuffersContainer::get_framebuffer_for_drawing(ctxt, Some(&attachments));
858        bind_framebuffer(ctxt, framebuffer, false, true);
859        ctxt.gl.ReadBuffer(gl::COLOR_ATTACHMENT0);     // TODO: cache
860    }
861
862    /// Calls `glClearBuffer` on a framebuffer that contains the attachment.
863    ///
864    /// # Panic
865    ///
866    /// Panics if `data` is incompatible with the kind of attachment.
867    ///
868    /// # Unsafety
869    ///
870    /// After calling this function, you **must** make sure to call `purge_texture`
871    /// and/or `purge_renderbuffer` when one of the attachment is destroyed.
872    pub unsafe fn clear_buffer<D>(ctxt: &mut CommandContext<'_>, attachment: &RegularAttachment<'_>,
873                                  data: D)
874        where D: Into<ClearBufferData>
875    {
876        // TODO: look for an existing framebuffer with this attachment
877
878        let data = data.into();
879
880        let fb = FramebufferAttachments::Regular(FramebufferSpecificAttachments {
881            colors: { let mut v = SmallVec::new(); v.push((0, *attachment)); v },
882            depth_stencil: DepthStencilAttachments::None,
883        }).validate(ctxt).unwrap();
884        let fb = FramebuffersContainer::get_framebuffer_for_drawing(ctxt, Some(&fb));
885
886        // TODO: use DSA if supported
887        // TODO: what if glClearBuffer is not supported?
888
889        bind_framebuffer(ctxt, fb, true, false);
890
891        match (attachment.kind(), data) {
892            (TextureKind::Float, ClearBufferData::Float(data)) => {
893                ctxt.gl.ClearBufferfv(gl::COLOR, 0, data.as_ptr());
894            },
895            (TextureKind::Integral, ClearBufferData::Integral(data)) => {
896                ctxt.gl.ClearBufferiv(gl::COLOR, 0, data.as_ptr());
897            },
898            (TextureKind::Unsigned, ClearBufferData::Unsigned(data)) => {
899                ctxt.gl.ClearBufferuiv(gl::COLOR, 0, data.as_ptr());
900            },
901            (TextureKind::Depth, _) => {
902                unimplemented!()        // TODO: can't work with the code above ^
903            },
904            (TextureKind::Stencil, _) => {
905                unimplemented!()        // TODO: can't work with the code above ^
906            },
907            (TextureKind::DepthStencil, _) => {
908                unimplemented!()        // TODO: can't work with the code above ^
909            },
910            _ => {
911                panic!("The data passed to `clear_buffer` does not match the kind of attachment");
912            }
913        }
914    }
915
916    ///
917    /// # Unsafety
918    ///
919    /// After calling this function, you **must** make sure to call `purge_texture`
920    /// and/or `purge_renderbuffer` when one of the attachment is destroyed.
921    fn get_framebuffer(ctxt: &mut CommandContext<'_>, attachments: &ValidatedAttachments<'_>)
922                       -> gl::types::GLuint
923    {
924        // TODO: use entries API
925        let mut framebuffers = ctxt.framebuffer_objects.framebuffers.borrow_mut();
926        if let Some(value) = framebuffers.get(&attachments.raw) {
927            return value.id;
928        }
929
930        let new_fbo = FrameBufferObject::new(ctxt, &attachments.raw);
931        let new_fbo_id = new_fbo.id;
932        framebuffers.insert(attachments.raw.clone(), new_fbo);
933        new_fbo_id
934    }
935}
936
937impl Drop for FramebuffersContainer {
938    #[inline]
939    fn drop(&mut self) {
940        if self.framebuffers.borrow().len() != 0 {
941            panic!()
942        }
943    }
944}
945
946/// A framebuffer object.
947struct FrameBufferObject {
948    id: gl::types::GLuint,
949    current_read_buffer: gl::types::GLenum,
950}
951
952impl FrameBufferObject {
953    /// Builds a new FBO.
954    ///
955    /// # Panic
956    ///
957    /// Panics if anything wrong or not supported is detected with the raw attachments.
958    ///
959    fn new(mut ctxt: &mut CommandContext<'_>, attachments: &RawAttachments) -> FrameBufferObject {
960        if attachments.color.len() > ctxt.capabilities.max_draw_buffers as usize {
961            panic!("Trying to attach {} color buffers, but the hardware only supports {}",
962                   attachments.color.len(), ctxt.capabilities.max_draw_buffers);
963        }
964
965        // building the FBO
966        let id = unsafe {
967            let mut id = 0;
968
969            if ctxt.version >= &Version(Api::Gl, 4, 5) ||
970                ctxt.extensions.gl_arb_direct_state_access
971            {
972                ctxt.gl.CreateFramebuffers(1, &mut id);
973
974            } else if ctxt.version >= &Version(Api::Gl, 3, 0) ||
975                      ctxt.version >= &Version(Api::GlEs, 2, 0) ||
976                      ctxt.extensions.gl_arb_framebuffer_object
977            {
978                ctxt.gl.GenFramebuffers(1, &mut id);
979                bind_framebuffer(&mut ctxt, id, true, false);
980
981            } else if ctxt.extensions.gl_ext_framebuffer_object {
982                ctxt.gl.GenFramebuffersEXT(1, &mut id);
983                bind_framebuffer(&mut ctxt, id, true, false);
984
985            } else {
986                // glium doesn't allow creating contexts that don't support FBOs
987                unreachable!();
988            }
989
990            id
991        };
992
993        // framebuffer parameters
994        // TODO: DSA
995        if let Some(width) = attachments.default_width {
996            unsafe { bind_framebuffer(&mut ctxt, id, true, false) };       // TODO: remove once DSA is used
997            if ctxt.version >= &Version(Api::Gl, 4, 3) || ctxt.version >= &Version(Api::GlEs, 3, 1) ||
998               ctxt.extensions.gl_arb_framebuffer_no_attachments
999            {
1000                unsafe {
1001                    ctxt.gl.FramebufferParameteri(gl::DRAW_FRAMEBUFFER, gl::FRAMEBUFFER_DEFAULT_WIDTH,
1002                                                  width as gl::types::GLint);
1003                }
1004            } else {
1005                unreachable!();
1006            }
1007        }
1008        if let Some(height) = attachments.default_height {
1009            unsafe { bind_framebuffer(&mut ctxt, id, true, false) };       // TODO: remove once DSA is used
1010            if ctxt.version >= &Version(Api::Gl, 4, 3) || ctxt.version >= &Version(Api::GlEs, 3, 1) ||
1011               ctxt.extensions.gl_arb_framebuffer_no_attachments
1012            {
1013                unsafe {
1014                    ctxt.gl.FramebufferParameteri(gl::DRAW_FRAMEBUFFER, gl::FRAMEBUFFER_DEFAULT_HEIGHT,
1015                                                  height as gl::types::GLint);
1016                }
1017            } else {
1018                unreachable!();
1019            }
1020        }
1021        if let Some(layers) = attachments.default_layers {
1022            unsafe { bind_framebuffer(&mut ctxt, id, true, false) };       // TODO: remove once DSA is used
1023            if ctxt.version >= &Version(Api::Gl, 4, 3) || ctxt.version >= &Version(Api::GlEs, 3, 2) ||
1024               ctxt.extensions.gl_arb_framebuffer_no_attachments
1025            {
1026                unsafe {
1027                    ctxt.gl.FramebufferParameteri(gl::DRAW_FRAMEBUFFER, gl::FRAMEBUFFER_DEFAULT_LAYERS,
1028                                                  layers as gl::types::GLint);
1029                }
1030            } else {
1031                unreachable!();
1032            }
1033        }
1034        if let Some(samples) = attachments.default_samples {
1035            unsafe { bind_framebuffer(&mut ctxt, id, true, false) };       // TODO: remove once DSA is used
1036            if ctxt.version >= &Version(Api::Gl, 4, 3) || ctxt.version >= &Version(Api::GlEs, 3, 1) ||
1037               ctxt.extensions.gl_arb_framebuffer_no_attachments
1038            {
1039                unsafe {
1040                    ctxt.gl.FramebufferParameteri(gl::DRAW_FRAMEBUFFER, gl::FRAMEBUFFER_DEFAULT_SAMPLES,
1041                                                  samples as gl::types::GLint);
1042                }
1043            } else {
1044                unreachable!();
1045            }
1046        }
1047        if let Some(samples_fixed) = attachments.default_samples_fixed {
1048            unsafe { bind_framebuffer(&mut ctxt, id, true, false) };       // TODO: remove once DSA is used
1049            if ctxt.version >= &Version(Api::Gl, 4, 3) || ctxt.version >= &Version(Api::GlEs, 3, 1) ||
1050               ctxt.extensions.gl_arb_framebuffer_no_attachments
1051            {
1052                unsafe {
1053                    ctxt.gl.FramebufferParameteri(gl::DRAW_FRAMEBUFFER, gl::FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS,
1054                                                  if samples_fixed { 1 } else { 0 });
1055                }
1056            } else {
1057                unreachable!();
1058            }
1059        }
1060
1061        // attaching the attachments, and building the list of enums to pass to `glDrawBuffers`
1062        let mut raw_attachments = Vec::with_capacity(attachments.color.len());
1063        for (attachment_pos, &(pos_in_drawbuffers, atchmnt)) in attachments.color.iter().enumerate() {
1064            if attachment_pos >= ctxt.capabilities.max_color_attachments as usize {
1065                panic!("Trying to attach a color buffer to slot {}, but the hardware only supports {} bind points",
1066                    attachment_pos, ctxt.capabilities.max_color_attachments);
1067            }
1068            unsafe { attach(&mut ctxt, gl::COLOR_ATTACHMENT0 + attachment_pos as u32, id, atchmnt) };
1069
1070            while raw_attachments.len() <= pos_in_drawbuffers as usize { raw_attachments.push(gl::NONE); }
1071            raw_attachments[pos_in_drawbuffers as usize] = gl::COLOR_ATTACHMENT0 + attachment_pos as u32;
1072        }
1073        if let Some(depth) = attachments.depth {
1074            unsafe { attach(&mut ctxt, gl::DEPTH_ATTACHMENT, id, depth) };
1075        }
1076        if let Some(stencil) = attachments.stencil {
1077            unsafe { attach(&mut ctxt, gl::STENCIL_ATTACHMENT, id, stencil) };
1078        }
1079        if let Some(depth_stencil) = attachments.depth_stencil {
1080            unsafe { attach(&mut ctxt, gl::DEPTH_STENCIL_ATTACHMENT, id, depth_stencil) };
1081        }
1082
1083        // calling `glDrawBuffers` if necessary
1084        if raw_attachments != &[gl::COLOR_ATTACHMENT0] {
1085            if ctxt.version >= &Version(Api::Gl, 4, 5) ||
1086               ctxt.extensions.gl_arb_direct_state_access
1087            {
1088                unsafe {
1089                    ctxt.gl.NamedFramebufferDrawBuffers(id, raw_attachments.len()
1090                                                        as gl::types::GLsizei,
1091                                                        raw_attachments.as_ptr());
1092                }
1093
1094            } else if ctxt.version >= &Version(Api::Gl, 2, 0) ||
1095                      ctxt.version >= &Version(Api::GlEs, 3, 0)
1096            {
1097                unsafe {
1098                    bind_framebuffer(&mut ctxt, id, true, false);
1099                    ctxt.gl.DrawBuffers(raw_attachments.len() as gl::types::GLsizei,
1100                                        raw_attachments.as_ptr());
1101                }
1102
1103            } else if ctxt.extensions.gl_arb_draw_buffers {
1104                unsafe {
1105                    bind_framebuffer(&mut ctxt, id, true, false);
1106                    ctxt.gl.DrawBuffersARB(raw_attachments.len() as gl::types::GLsizei,
1107                                           raw_attachments.as_ptr());
1108                }
1109
1110            } else if ctxt.extensions.gl_ati_draw_buffers {
1111                unsafe {
1112                    bind_framebuffer(&mut ctxt, id, true, false);
1113                    ctxt.gl.DrawBuffersATI(raw_attachments.len() as gl::types::GLsizei,
1114                                           raw_attachments.as_ptr());
1115                }
1116
1117            } else {
1118                // OpenGL ES 2 and OpenGL 1 don't support calling `glDrawBuffers`
1119                panic!("Using more than one attachment is not supported by the backend");
1120            }
1121        }
1122
1123
1124        FrameBufferObject {
1125            id,
1126            current_read_buffer: gl::BACK,
1127        }
1128    }
1129
1130    /// Destroys the FBO. Must be called, or things will leak.
1131    fn destroy(self, ctxt: &mut CommandContext<'_>) {
1132        // unbinding framebuffer
1133        if ctxt.state.draw_framebuffer == self.id {
1134            ctxt.state.draw_framebuffer = 0;
1135        }
1136
1137        if ctxt.state.read_framebuffer == self.id {
1138            ctxt.state.read_framebuffer = 0;
1139        }
1140
1141        // deleting
1142        if ctxt.version >= &Version(Api::Gl, 3, 0) ||
1143            ctxt.version >= &Version(Api::GlEs, 2, 0) ||
1144            ctxt.extensions.gl_arb_framebuffer_object
1145        {
1146            unsafe { ctxt.gl.DeleteFramebuffers(1, [ self.id ].as_ptr()) };
1147        } else if ctxt.extensions.gl_ext_framebuffer_object {
1148            unsafe { ctxt.gl.DeleteFramebuffersEXT(1, [ self.id ].as_ptr()) };
1149        } else {
1150            unreachable!();
1151        }
1152    }
1153}
1154
1155impl GlObject for FrameBufferObject {
1156    type Id = gl::types::GLuint;
1157
1158    #[inline]
1159    fn get_id(&self) -> gl::types::GLuint {
1160        self.id
1161    }
1162}
1163
1164/// Binds a framebuffer object, either for drawing, reading, or both.
1165///
1166/// # Safety
1167///
1168/// The id of the FBO must be valid.
1169///
1170pub unsafe fn bind_framebuffer(ctxt: &mut CommandContext<'_>, fbo_id: gl::types::GLuint,
1171                               draw: bool, read: bool)
1172{
1173    if draw && read {
1174        if ctxt.state.draw_framebuffer != fbo_id || ctxt.state.read_framebuffer != fbo_id {
1175            if ctxt.version >= &Version(Api::Gl, 3, 0) ||
1176               ctxt.version >= &Version(Api::GlEs, 2, 0) ||
1177               ctxt.extensions.gl_arb_framebuffer_object
1178            {
1179                ctxt.gl.BindFramebuffer(gl::FRAMEBUFFER, fbo_id);
1180                ctxt.state.draw_framebuffer = fbo_id;
1181                ctxt.state.read_framebuffer = fbo_id;
1182            } else if ctxt.extensions.gl_ext_framebuffer_object {
1183                ctxt.gl.BindFramebufferEXT(gl::FRAMEBUFFER_EXT, fbo_id);
1184                ctxt.state.draw_framebuffer = fbo_id;
1185                ctxt.state.read_framebuffer = fbo_id;
1186            } else {
1187                unreachable!();
1188            }
1189        }
1190
1191
1192    } else {
1193
1194        if draw && ctxt.state.draw_framebuffer != fbo_id {
1195            if ctxt.version >= &Version(Api::Gl, 3, 0) ||
1196               ctxt.extensions.gl_arb_framebuffer_object
1197            {
1198                ctxt.gl.BindFramebuffer(gl::DRAW_FRAMEBUFFER, fbo_id);
1199                ctxt.state.draw_framebuffer = fbo_id;
1200            } else if ctxt.version >= &Version(Api::GlEs, 2, 0) {
1201                ctxt.gl.BindFramebuffer(gl::FRAMEBUFFER, fbo_id);
1202                ctxt.state.draw_framebuffer = fbo_id;
1203                ctxt.state.read_framebuffer = fbo_id;
1204            } else if ctxt.extensions.gl_ext_framebuffer_object {
1205                ctxt.gl.BindFramebufferEXT(gl::FRAMEBUFFER_EXT, fbo_id);
1206                ctxt.state.draw_framebuffer = fbo_id;
1207                ctxt.state.read_framebuffer = fbo_id;
1208            } else {
1209                unreachable!();
1210            }
1211        }
1212
1213        if read && ctxt.state.read_framebuffer != fbo_id {
1214            if ctxt.version >= &Version(Api::Gl, 3, 0) ||
1215               ctxt.extensions.gl_arb_framebuffer_object
1216            {
1217                ctxt.gl.BindFramebuffer(gl::READ_FRAMEBUFFER, fbo_id);
1218                ctxt.state.read_framebuffer = fbo_id;
1219            } else if ctxt.version >= &Version(Api::GlEs, 2, 0) {
1220                ctxt.gl.BindFramebuffer(gl::FRAMEBUFFER, fbo_id);
1221                ctxt.state.draw_framebuffer = fbo_id;
1222                ctxt.state.read_framebuffer = fbo_id;
1223            } else if ctxt.extensions.gl_ext_framebuffer_object {
1224                ctxt.gl.BindFramebufferEXT(gl::FRAMEBUFFER_EXT, fbo_id);
1225                ctxt.state.draw_framebuffer = fbo_id;
1226                ctxt.state.read_framebuffer = fbo_id;
1227            } else {
1228                unreachable!();
1229            }
1230        }
1231
1232    }
1233}
1234
1235/// Attaches something to a framebuffer object.
1236///
1237/// # Panic
1238///
1239/// - Panics if `layer` is `None` and layered attachments are not supported.
1240/// - Panics if `layer` is `None` and the texture is not an array or a 3D texture.
1241/// - Panics if the texture is an array and attaching an array is not supported.
1242///
1243/// # Safety
1244///
1245/// All parameters must be valid.
1246///
1247unsafe fn attach(ctxt: &mut CommandContext<'_>, slot: gl::types::GLenum,
1248                 id: gl::types::GLuint, attachment: RawAttachment)
1249{
1250    match attachment {
1251        RawAttachment::Texture { texture: tex_id, level, layer, bind_point, cubemap_layer } => {
1252            match bind_point {
1253                // these textures can't be layered
1254                gl::TEXTURE_2D | gl::TEXTURE_2D_MULTISAMPLE | gl::TEXTURE_1D |
1255                gl::TEXTURE_RECTANGLE =>
1256                {
1257                    assert_eq!(layer, Some(0));
1258                    debug_assert!(cubemap_layer.is_none());
1259
1260                    if ctxt.version >= &Version(Api::Gl, 4, 5) ||
1261                       ctxt.extensions.gl_arb_direct_state_access
1262                    {
1263                        ctxt.gl.NamedFramebufferTexture(id, slot, tex_id,
1264                                                        level as gl::types::GLint);
1265
1266                    } else if ctxt.extensions.gl_ext_direct_state_access &&
1267                              ctxt.extensions.gl_ext_geometry_shader4
1268                    {
1269                        ctxt.gl.NamedFramebufferTextureEXT(id, slot, tex_id,
1270                                                           level as gl::types::GLint);
1271
1272                    } else if ctxt.version >= &Version(Api::Gl, 3, 2) {
1273                        bind_framebuffer(ctxt, id, true, false);
1274                        ctxt.gl.FramebufferTexture(gl::DRAW_FRAMEBUFFER,
1275                                                   slot, tex_id, level as gl::types::GLint);
1276
1277                    } else if ctxt.version >= &Version(Api::Gl, 3, 0) ||
1278                              ctxt.extensions.gl_arb_framebuffer_object
1279                    {
1280                        bind_framebuffer(ctxt, id, true, false);
1281
1282                        match bind_point {
1283                            gl::TEXTURE_1D | gl::TEXTURE_RECTANGLE => {
1284                                ctxt.gl.FramebufferTexture1D(gl::DRAW_FRAMEBUFFER,
1285                                                             slot, bind_point, tex_id,
1286                                                             level as gl::types::GLint);
1287                            },
1288                            gl::TEXTURE_2D | gl::TEXTURE_2D_MULTISAMPLE => {
1289                                ctxt.gl.FramebufferTexture2D(gl::DRAW_FRAMEBUFFER,
1290                                                             slot, bind_point, tex_id,
1291                                                             level as gl::types::GLint);
1292                            },
1293                            _ => unreachable!()
1294                        }
1295
1296                    } else if ctxt.version >= &Version(Api::GlEs, 2, 0) {
1297                        bind_framebuffer(ctxt, id, true, true);
1298                        assert!(bind_point == gl::TEXTURE_2D);
1299                        ctxt.gl.FramebufferTexture2D(gl::FRAMEBUFFER, slot, bind_point, tex_id,
1300                                                     level as gl::types::GLint);
1301
1302                    } else if ctxt.extensions.gl_ext_framebuffer_object {
1303                        bind_framebuffer(ctxt, id, true, true);
1304
1305                        match bind_point {
1306                            gl::TEXTURE_1D | gl::TEXTURE_RECTANGLE => {
1307                                ctxt.gl.FramebufferTexture1DEXT(gl::FRAMEBUFFER_EXT,
1308                                                                slot, bind_point, tex_id,
1309                                                                level as gl::types::GLint);
1310                            },
1311                            gl::TEXTURE_2D | gl::TEXTURE_2D_MULTISAMPLE => {
1312                                ctxt.gl.FramebufferTexture2DEXT(gl::FRAMEBUFFER_EXT,
1313                                                                slot, bind_point, tex_id,
1314                                                                level as gl::types::GLint);
1315                            },
1316                            _ => unreachable!()
1317                        }
1318
1319                    } else {
1320                        // it's not possible to create an OpenGL context that doesn't support FBOs
1321                        unreachable!();
1322                    }
1323                },
1324
1325                // non-layered attachments
1326                gl::TEXTURE_1D_ARRAY | gl::TEXTURE_2D_ARRAY | gl::TEXTURE_2D_MULTISAMPLE_ARRAY |
1327                gl::TEXTURE_3D | gl::TEXTURE_CUBE_MAP_ARRAY if layer.is_some() =>
1328                {
1329                    let layer = if bind_point == gl::TEXTURE_CUBE_MAP_ARRAY {
1330                        layer.unwrap() * 6 + cubemap_layer.unwrap().get_layer_index()
1331                                                                               as gl::types::GLenum
1332                    } else {
1333                        layer.unwrap()
1334                    };
1335
1336                    if ctxt.version >= &Version(Api::Gl, 4, 5) ||
1337                       ctxt.extensions.gl_arb_direct_state_access
1338                    {
1339                        ctxt.gl.NamedFramebufferTextureLayer(id, slot, tex_id,
1340                                                             level as gl::types::GLint,
1341                                                             layer as gl::types::GLint);
1342
1343                    } else if ctxt.extensions.gl_ext_direct_state_access &&
1344                              ctxt.extensions.gl_ext_geometry_shader4
1345                    {
1346                        ctxt.gl.NamedFramebufferTextureLayerEXT(id, slot, tex_id,
1347                                                                level as gl::types::GLint,
1348                                                                layer as gl::types::GLint);
1349
1350                    } else if ctxt.version >= &Version(Api::Gl, 3, 0) ||
1351                              ctxt.extensions.gl_arb_framebuffer_object
1352                    {
1353                        bind_framebuffer(ctxt, id, true, false);
1354
1355                        match bind_point {
1356                            gl::TEXTURE_1D_ARRAY | gl::TEXTURE_2D_ARRAY |
1357                            gl::TEXTURE_2D_MULTISAMPLE_ARRAY => {
1358                                ctxt.gl.FramebufferTextureLayer(gl::DRAW_FRAMEBUFFER,
1359                                                                slot, tex_id,
1360                                                                level as gl::types::GLint,
1361                                                                layer as gl::types::GLint);
1362
1363                            },
1364
1365                            gl::TEXTURE_3D => {
1366                                ctxt.gl.FramebufferTexture3D(gl::DRAW_FRAMEBUFFER,
1367                                                             slot, bind_point, tex_id,
1368                                                             level as gl::types::GLint,
1369                                                             layer as gl::types::GLint);
1370                            },
1371
1372                            _ => unreachable!()
1373                        }
1374
1375                    } else if ctxt.extensions.gl_ext_framebuffer_object &&
1376                              bind_point == gl::TEXTURE_3D
1377                    {
1378                        bind_framebuffer(ctxt, id, true, true);
1379                        ctxt.gl.FramebufferTexture3DEXT(gl::FRAMEBUFFER_EXT,
1380                                                        slot, bind_point, tex_id,
1381                                                        level as gl::types::GLint,
1382                                                        layer as gl::types::GLint);
1383
1384                    } else if ctxt.extensions.gl_ext_texture_array &&
1385                              bind_point == gl::TEXTURE_1D_ARRAY ||
1386                              bind_point == gl::TEXTURE_2D_ARRAY ||
1387                              bind_point == gl::TEXTURE_2D_MULTISAMPLE_ARRAY
1388                    {
1389                        bind_framebuffer(ctxt, id, true, false);
1390                        ctxt.gl.FramebufferTextureLayerEXT(gl::DRAW_FRAMEBUFFER,
1391                                                           slot, tex_id,
1392                                                           level as gl::types::GLint,
1393                                                           layer as gl::types::GLint);
1394
1395                    } else {
1396                        panic!("Attaching a texture array is not supported");
1397                    }
1398                },
1399
1400                // layered attachments
1401                gl::TEXTURE_1D_ARRAY | gl::TEXTURE_2D_ARRAY | gl::TEXTURE_2D_MULTISAMPLE_ARRAY |
1402                gl::TEXTURE_3D | gl::TEXTURE_CUBE_MAP_ARRAY if layer.is_none() =>
1403                {
1404                    if ctxt.version >= &Version(Api::Gl, 4, 5) ||
1405                       ctxt.extensions.gl_arb_direct_state_access
1406                    {
1407                        ctxt.gl.NamedFramebufferTexture(id, slot, tex_id,
1408                                                        level as gl::types::GLint);
1409
1410                    } else if ctxt.extensions.gl_ext_direct_state_access &&
1411                              ctxt.extensions.gl_ext_geometry_shader4
1412                    {
1413                        ctxt.gl.NamedFramebufferTextureEXT(id, slot, tex_id,
1414                                                           level as gl::types::GLint);
1415
1416                    } else if ctxt.version >= &Version(Api::Gl, 3, 2) {
1417                        bind_framebuffer(ctxt, id, true, false);
1418                        ctxt.gl.FramebufferTexture(gl::DRAW_FRAMEBUFFER,
1419                                                   slot, tex_id, level as gl::types::GLint);
1420
1421                    } else {
1422                        // note that this should have been detected earlier
1423                        panic!("Layered framebuffers are not supported");
1424                    }
1425                },
1426
1427                // non-layered cubemaps
1428                gl::TEXTURE_CUBE_MAP if layer.is_some() => {
1429                    let bind_point = gl::TEXTURE_CUBE_MAP_POSITIVE_X +
1430                                    cubemap_layer.unwrap().get_layer_index() as gl::types::GLenum;
1431
1432                    if ctxt.version >= &Version(Api::Gl, 3, 0) ||
1433                              ctxt.extensions.gl_arb_framebuffer_object
1434                    {
1435                        bind_framebuffer(ctxt, id, true, false);
1436                        ctxt.gl.FramebufferTexture2D(gl::DRAW_FRAMEBUFFER,
1437                                                     slot, bind_point, tex_id,
1438                                                     level as gl::types::GLint);
1439
1440                    } else if ctxt.version >= &Version(Api::GlEs, 2, 0) {
1441                        bind_framebuffer(ctxt, id, true, true);
1442                        ctxt.gl.FramebufferTexture2D(gl::FRAMEBUFFER, slot, bind_point, tex_id,
1443                                                     level as gl::types::GLint);
1444
1445                    } else if ctxt.extensions.gl_ext_framebuffer_object {
1446                        bind_framebuffer(ctxt, id, true, true);
1447                        ctxt.gl.FramebufferTexture2DEXT(gl::FRAMEBUFFER_EXT,
1448                                                        slot, bind_point, tex_id,
1449                                                        level as gl::types::GLint);
1450
1451                    } else {
1452                        // it's not possible to create an OpenGL context that doesn't support FBOs
1453                        unreachable!();
1454                    }
1455                },
1456
1457                // layered cubemaps
1458                gl::TEXTURE_CUBE_MAP if layer.is_none() => {
1459                    if ctxt.version >= &Version(Api::Gl, 4, 5) ||
1460                       ctxt.extensions.gl_arb_direct_state_access
1461                    {
1462                        ctxt.gl.NamedFramebufferTexture(id, slot, tex_id,
1463                                                        level as gl::types::GLint);
1464
1465                    } else if ctxt.extensions.gl_ext_direct_state_access &&
1466                              ctxt.extensions.gl_ext_geometry_shader4
1467                    {
1468                        ctxt.gl.NamedFramebufferTextureEXT(id, slot, tex_id,
1469                                                           level as gl::types::GLint);
1470
1471                    } else if ctxt.version >= &Version(Api::Gl, 3, 2) {
1472                        bind_framebuffer(ctxt, id, true, false);
1473                        ctxt.gl.FramebufferTexture(gl::DRAW_FRAMEBUFFER,
1474                                                   slot, tex_id, level as gl::types::GLint);
1475
1476                    } else {
1477                        // note that this should have been detected earlier
1478                        panic!("Layered framebuffers are not supported");
1479                    }
1480                },
1481
1482                _ => unreachable!()
1483            }
1484        },
1485
1486        // renderbuffers are straight-forward
1487        RawAttachment::RenderBuffer(renderbuffer) => {
1488            if ctxt.version >= &Version(Api::Gl, 4, 5) ||
1489               ctxt.extensions.gl_arb_direct_state_access
1490            {
1491                ctxt.gl.NamedFramebufferRenderbuffer(id, slot, gl::RENDERBUFFER, renderbuffer);
1492
1493            } else if ctxt.extensions.gl_ext_direct_state_access &&
1494                      ctxt.extensions.gl_ext_geometry_shader4
1495            {
1496                ctxt.gl.NamedFramebufferRenderbufferEXT(id, slot, gl::RENDERBUFFER, renderbuffer);
1497
1498            } else if ctxt.version >= &Version(Api::Gl, 3, 0) ||
1499                      ctxt.extensions.gl_arb_framebuffer_object
1500            {
1501                bind_framebuffer(ctxt, id, true, false);
1502                ctxt.gl.FramebufferRenderbuffer(gl::DRAW_FRAMEBUFFER, slot,
1503                                                gl::RENDERBUFFER, renderbuffer);
1504
1505            } else if ctxt.version >= &Version(Api::GlEs, 2, 0) {
1506                bind_framebuffer(ctxt, id, true, true);
1507                ctxt.gl.FramebufferRenderbuffer(gl::DRAW_FRAMEBUFFER, slot,
1508                                                gl::RENDERBUFFER, renderbuffer);
1509
1510            } else if ctxt.extensions.gl_ext_framebuffer_object {
1511                bind_framebuffer(ctxt, id, true, true);
1512                ctxt.gl.FramebufferRenderbufferEXT(gl::DRAW_FRAMEBUFFER, slot,
1513                                                   gl::RENDERBUFFER, renderbuffer);
1514
1515            } else {
1516                // it's not possible to create an OpenGL context that doesn't support FBOs
1517                unreachable!();
1518            }
1519        },
1520    }
1521}