glium/context/
mod.rs

1//! Contains everything related to the interface between glium and the OpenGL implementation.
2
3use crate::gl;
4
5use std::collections::HashMap;
6use std::mem;
7use std::ptr;
8use std::str;
9use std::borrow::Cow;
10use std::cell::{Cell, RefCell, RefMut};
11use std::marker::PhantomData;
12use std::ffi::CStr;
13use std::rc::Rc;
14use std::os::raw;
15use std::hash::BuildHasherDefault;
16
17use fnv::FnvHasher;
18
19use crate::IncompatibleOpenGl;
20use crate::SwapBuffersError;
21use crate::CapabilitiesSource;
22use crate::ContextExt;
23use crate::backend::Backend;
24use crate::version;
25use crate::version::Api;
26use crate::version::Version;
27
28use crate::debug;
29use crate::fbo;
30use crate::ops;
31use crate::sampler_object;
32use crate::texture;
33use crate::uniforms;
34use crate::vertex_array_object;
35
36pub use self::capabilities::{ReleaseBehavior, Capabilities, Profile};
37pub use self::extensions::ExtensionsList;
38pub use self::state::GlState;
39pub use self::uuid::UuidError;
40
41mod capabilities;
42mod extensions;
43mod state;
44mod uuid;
45
46/// Stores the state and information required for glium to execute commands. Most public glium
47/// functions require passing a `Rc<Context>`.
48pub struct Context {
49    /// Contains the pointers to OpenGL functions.
50    gl: gl::Gl,
51
52    /// The current state of the OpenGL state machine. Contains for example which buffer is bound
53    /// to which bind point, whether depth testing is activated, etc.
54    state: RefCell<GlState>,
55
56    /// Version of OpenGL of the backend.
57    version: Version,
58
59    /// Tells whether or not the backend supports each extension.
60    extensions: ExtensionsList,
61
62    /// Constants defined by the backend and retrieved at initialization. For example, number
63    /// of texture units, maximum size of the viewport, etc.
64    capabilities: Capabilities,
65
66    /// Glue between glium and the code that handles windowing. Contains functions that allows
67    /// you to swap buffers, retrieve the size of the framebuffer, etc.
68    backend: RefCell<Box<dyn Backend>>,
69
70    /// Whether or not glium must check that the OpenGL context is the current one before each
71    /// call.
72    check_current_context: bool,
73
74    /// The callback that is used by the debug output feature.
75    debug_callback: Option<debug::DebugCallback>,
76
77    /// Whether or not errors triggered by ARB_debug_output (and similar extensions) should be
78    /// reported to the user when `DebugCallbackBehavior::DebugMessageOnError` is used. This must
79    /// be set to `false` in some situations, like compiling/linking shaders.
80    report_debug_output_errors: Cell<bool>,
81
82    /// We maintain a cache of FBOs.
83    /// The `Option` is here in order to destroy the container. It must be filled at all time
84    /// is a normal situation.
85    framebuffer_objects: Option<fbo::FramebuffersContainer>,
86
87    /// We maintain a list of vertex array objects.
88    vertex_array_objects: vertex_array_object::VertexAttributesSystem,
89
90    /// We maintain a list of samplers for each possible behavior.
91    samplers: RefCell<HashMap<uniforms::SamplerBehavior, sampler_object::SamplerObject, BuildHasherDefault<FnvHasher>>>,
92
93    /// List of texture handles that are resident. We need to call `MakeTextureHandleResidentARB`
94    /// when rebuilding the context.
95    resident_texture_handles: RefCell<Vec<gl::types::GLuint64>>,
96
97    /// List of images handles that are resident. We need to call `MakeImageHandleResidentARB`
98    /// when rebuilding the context.
99    resident_image_handles: RefCell<Vec<(gl::types::GLuint64, gl::types::GLenum)>>,
100}
101
102/// This struct is a guard that is returned when you want to access the OpenGL backend.
103pub struct CommandContext<'a> {
104    /// Source of OpenGL function pointers.
105    pub gl: &'a gl::Gl,
106
107    /// Refers to the state of the OpenGL backend. Maintained between multiple calls.
108    /// **Must** be synchronized with the real state of the backend.
109    pub state: RefMut<'a, GlState>,
110
111    /// Version of the backend.
112    pub version: &'a Version,
113
114    /// Extensions supported by the backend.
115    pub extensions: &'a ExtensionsList,
116
117    /// Capabilities of the backend.
118    pub capabilities: &'a Capabilities,
119
120    /// Whether or not errors triggered by ARB_debug_output (and similar extensions) should be
121    /// reported to the user (by panicking).
122    pub report_debug_output_errors: &'a Cell<bool>,
123
124    /// The list of vertex array objects.
125    pub vertex_array_objects: &'a vertex_array_object::VertexAttributesSystem,
126
127    /// The list of framebuffer objects.
128    pub framebuffer_objects: &'a fbo::FramebuffersContainer,
129
130    /// The list of samplers.
131    pub samplers: RefMut<'a, HashMap<uniforms::SamplerBehavior, sampler_object::SamplerObject, BuildHasherDefault<FnvHasher>>>,
132
133    /// List of texture handles that need to be made resident.
134    pub resident_texture_handles: RefMut<'a, Vec<gl::types::GLuint64>>,
135
136    /// List of image handles and their access that need to be made resident.
137    pub resident_image_handles: RefMut<'a, Vec<(gl::types::GLuint64, gl::types::GLenum)>>,
138
139    /// This marker is here to prevent `CommandContext` from implementing `Send`
140    // TODO: use this when possible
141    //impl<'a, 'b> !Send for CommandContext<'a, 'b> {}
142    marker: PhantomData<*mut u8>,
143}
144
145impl Context {
146    /// Builds a new context.
147    ///
148    /// The `check_current_context` parameter tells the context whether it should check
149    /// if the backend's OpenGL context is the current one before each OpenGL operation.
150    ///
151    /// If you pass `false`, you must ensure that no other OpenGL context is going to be made
152    /// current in the same thread as this context. Passing `true` makes things safe but
153    /// is slightly slower.
154    ///
155    /// The OpenGL context must be newly-created. If you make modifications to the context before
156    /// passing it to this function, glium's state cache may mismatch the actual one.
157    ///
158    pub unsafe fn new<B>(
159        backend: B,
160        check_current_context: bool,
161        callback_behavior: DebugCallbackBehavior,
162    ) -> Result<Rc<Context>, IncompatibleOpenGl>
163        where B: Backend + 'static
164    {
165        backend.make_current();
166
167        let gl = gl::Gl::load_with(|symbol| backend.get_proc_address(symbol) as *const _);
168        let gl_state: RefCell<GlState> = RefCell::new(Default::default());
169
170        let version = version::get_gl_version(&gl);
171        let extensions = extensions::get_extensions(&gl, &version);
172        check_gl_compatibility(&version, &extensions)?;
173
174        let capabilities = capabilities::get_capabilities(&gl, &version, &extensions);
175        let report_debug_output_errors = Cell::new(true);
176
177        let vertex_array_objects = vertex_array_object::VertexAttributesSystem::new();
178        let framebuffer_objects = fbo::FramebuffersContainer::new();
179        let samplers = RefCell::new({
180            let mut map = HashMap::with_hasher(Default::default());
181            map.reserve(16);
182            map
183        });
184        let resident_texture_handles = RefCell::new(Vec::new());
185        let resident_image_handles = RefCell::new(Vec::new());
186
187        let (debug_callback, synchronous) = match callback_behavior {
188            DebugCallbackBehavior::Ignore => (None, false),
189            DebugCallbackBehavior::DebugMessageOnError => {
190                (Some(Box::new(default_debug_callback) as debug::DebugCallback), true)
191            },
192            DebugCallbackBehavior::PrintAll => {
193                (Some(Box::new(printall_debug_callback) as debug::DebugCallback), false)
194            },
195            DebugCallbackBehavior::Custom { callback, synchronous } => {
196                (Some(callback), synchronous)
197            },
198        };
199
200        let context = Rc::new(Context {
201            gl,
202            state: gl_state,
203            version,
204            extensions,
205            capabilities,
206            debug_callback,
207            report_debug_output_errors,
208            backend: RefCell::new(Box::new(backend)),
209            check_current_context,
210            framebuffer_objects: Some(framebuffer_objects),
211            vertex_array_objects,
212            samplers,
213            resident_texture_handles,
214            resident_image_handles,
215        });
216
217        if context.debug_callback.is_some() {
218            init_debug_callback(&context, synchronous);
219        }
220
221        // making sure that an error wasn't triggered during initialization
222        {
223            let mut ctxt = context.make_current();
224            if crate::get_gl_error(&mut ctxt).is_some() {
225                eprintln!("glium has triggered an OpenGL error during initialization. Please report \
226                           this error: https://github.com/glium/glium/issues");
227            }
228            /*assert!(::get_gl_error(&mut ctxt).is_none(),
229                    "glium has triggered an OpenGL error during initialization. Please report \
230                     this error: https://github.com/glium/glium/issues");*/
231            if ctxt.version >= &Version(Api::Gl, 3, 2) && ctxt.extensions.gl_arb_seamless_cube_map {
232                ctxt.gl.Enable(gl::TEXTURE_CUBE_MAP_SEAMLESS);
233            }
234        }
235
236        Ok(context)
237    }
238
239    /// Calls `get_framebuffer_dimensions` on the backend object stored by this context.
240    #[inline]
241    pub fn get_framebuffer_dimensions(&self) -> (u32, u32) {
242        self.backend.borrow().get_framebuffer_dimensions()
243    }
244
245    /// Changes the OpenGL context associated with this context.
246    ///
247    /// The new context **must** have lists shared with the old one.
248    pub unsafe fn rebuild<B>(&self, new_backend: B) -> Result<(), IncompatibleOpenGl>
249        where B: Backend + 'static
250    {
251        // framebuffer objects and vertex array objects aren't shared,
252        // so we have to destroy them
253        {
254            let mut ctxt = self.make_current();
255            fbo::FramebuffersContainer::purge_all(&mut ctxt);
256            vertex_array_object::VertexAttributesSystem::purge_all(&mut ctxt);
257        }
258
259        new_backend.make_current();
260
261        *self.state.borrow_mut() = Default::default();
262        // FIXME: verify version, capabilities and extensions
263        *self.backend.borrow_mut() = Box::new(new_backend);
264
265        // making textures resident
266        let textures = self.resident_texture_handles.borrow();
267        for &texture in textures.iter() {
268            self.gl.MakeTextureHandleResidentARB(texture);
269        }
270
271        // making images resident
272        let images = self.resident_image_handles.borrow();
273        for &(image, access) in images.iter() {
274            self.gl.MakeImageHandleResidentARB(image, access);
275        }
276
277        Ok(())
278    }
279
280    /// Swaps the buffers in the backend.
281    pub fn swap_buffers(&self) -> Result<(), SwapBuffersError> {
282        if self.state.borrow().lost_context {
283            return Err(SwapBuffersError::ContextLost);
284        }
285
286        // Note: This is a work-around for the FRAPS software.
287        //       The Fraps software calls `glClear` with scissoring and reads the image of the
288        //       current framebuffer.
289        //       Therefore we need to bind the default framebuffer before swapping.
290        if self.state.borrow().draw_framebuffer != 0 || self.state.borrow().read_framebuffer != 0 {
291            let mut ctxt = self.make_current();
292
293            if ctxt.version >= &Version(Api::Gl, 3, 0) ||
294               ctxt.extensions.gl_arb_framebuffer_object
295            {
296                unsafe { ctxt.gl.BindFramebuffer(gl::FRAMEBUFFER, 0); }
297                ctxt.state.draw_framebuffer = 0;
298                ctxt.state.read_framebuffer = 0;
299            } else if ctxt.version >= &Version(Api::GlEs, 2, 0) {
300                unsafe { ctxt.gl.BindFramebuffer(gl::FRAMEBUFFER, 0); }
301                ctxt.state.draw_framebuffer = 0;
302                ctxt.state.read_framebuffer = 0;
303            } else if ctxt.extensions.gl_ext_framebuffer_object {
304                unsafe { ctxt.gl.BindFramebufferEXT(gl::FRAMEBUFFER_EXT, 0); }
305                ctxt.state.draw_framebuffer = 0;
306                ctxt.state.read_framebuffer = 0;
307            } else {
308                unreachable!();
309            }
310        }
311
312        let backend = self.backend.borrow();
313        if self.check_current_context && !backend.is_current() {
314            unsafe { backend.make_current() };
315        }
316
317        // swapping
318        let err = backend.swap_buffers();
319        if let Err(SwapBuffersError::ContextLost) = err {
320            self.state.borrow_mut().lost_context = true;
321        }
322        err
323    }
324
325    /// Returns the OpenGL version
326    #[inline]
327    #[deprecated(note = "use `get_opengl_version` instead.")]
328    pub fn get_version(&self) -> &Version {
329        &self.version
330    }
331
332    /// Returns the OpenGL version detected by this context.
333    #[inline]
334    pub fn get_opengl_version(&self) -> &Version {
335        &self.version
336    }
337
338    /// Returns the GLSL version guaranteed to be supported.
339    #[inline]
340    pub fn get_supported_glsl_version(&self) -> Version {
341        version::get_supported_glsl_version(self.get_opengl_version())
342    }
343
344    /// Returns true if the given GLSL version is supported.
345    #[inline]
346    pub fn is_glsl_version_supported(&self, version: &Version) -> bool {
347        self.capabilities().supported_glsl_versions.iter().any(|v| v == version)
348    }
349
350    /// Returns a string containing this GL version or release number used by this context.
351    ///
352    /// Vendor-specific information may follow the version number.
353    #[inline]
354    pub fn get_opengl_version_string(&self) -> &str {
355        &self.capabilities().version
356    }
357
358    /// Returns a string containing the company responsible for this GL implementation.
359    #[inline]
360    pub fn get_opengl_vendor_string(&self) -> &str {
361        &self.capabilities().vendor
362    }
363
364    /// Returns a string containing the name of the GL renderer used by this context.
365    ///
366    /// This name is typically specific to a particular configuration of a hardware platform.
367    #[inline]
368    pub fn get_opengl_renderer_string(&self) -> &str {
369        &self.capabilities().renderer
370    }
371
372    /// Returns true if the context is in debug mode.
373    ///
374    /// Debug mode may provide additional error and performance issue reporting functionality.
375    #[inline]
376    pub fn is_debug(&self) -> bool {
377        self.capabilities().debug
378    }
379
380    /// Returns true if the context is in "forward-compatible" mode.
381    ///
382    /// Forward-compatible mode means that no deprecated functionality will be supported.
383    #[inline]
384    pub fn is_forward_compatible(&self) -> bool {
385        self.capabilities().forward_compatible
386    }
387
388    /// Returns this context's OpenGL profile if available.
389    ///
390    /// The context profile is available from OpenGL 3.2 onwards. Returns `None` if not supported.
391    pub fn get_opengl_profile(&self) -> Option<Profile> {
392        self.capabilities().profile
393    }
394
395    /// Returns true if out-of-bound buffer access from the GPU side (inside a program) cannot
396    /// result in a crash.
397    ///
398    /// You should take extra care if `is_robust` returns false.
399    #[inline]
400    pub fn is_robust(&self) -> bool {
401        self.capabilities().robustness
402    }
403
404    /// Returns true if a context loss is possible.
405    #[inline]
406    pub fn is_context_loss_possible(&self) -> bool {
407        self.capabilities().can_lose_context
408    }
409
410    /// Returns true if the context has been lost and needs to be recreated.
411    ///
412    /// # Implementation
413    ///
414    /// If it has been determined that the context has been lost before, then the function
415    /// immediately returns true. Otherwise, calls `glGetGraphicsResetStatus`. If this function
416    /// is not available, returns false.
417    pub fn is_context_lost(&self) -> bool {
418        if self.state.borrow().lost_context {
419            return true;
420        }
421
422        let mut ctxt = self.make_current();
423
424        let lost = if ctxt.version >= &Version(Api::Gl, 4, 5) ||
425                      ctxt.version >= &Version(Api::GlEs, 3, 2) ||
426                      ctxt.extensions.gl_khr_robustness
427        {
428            unsafe { ctxt.gl.GetGraphicsResetStatus() != gl::NO_ERROR }
429        } else if ctxt.extensions.gl_ext_robustness {
430            unsafe { ctxt.gl.GetGraphicsResetStatusEXT() != gl::NO_ERROR }
431        } else if ctxt.extensions.gl_arb_robustness {
432            unsafe { ctxt.gl.GetGraphicsResetStatusARB() != gl::NO_ERROR }
433        } else {
434            false
435        };
436
437        if lost { ctxt.state.lost_context = true; }
438        lost
439    }
440
441    /// Returns the behavior when the current OpenGL context is changed.
442    ///
443    /// The most common value is `Flush`. In order to get `None` you must explicitly request it
444    /// during creation.
445    #[inline]
446    pub fn get_release_behavior(&self) -> ReleaseBehavior {
447        self.capabilities().release_behavior
448    }
449
450    /// Returns the maximum value that can be used for anisotropic filtering, or `None`
451    /// if the hardware doesn't support it.
452    #[inline]
453    pub fn get_max_anisotropy_support(&self) -> Option<u16> {
454        self.capabilities().max_texture_max_anisotropy.map(|v| v as u16)
455    }
456
457    /// Returns the maximum dimensions of the viewport.
458    ///
459    /// Glium will panic if you request a larger viewport than this when drawing.
460    #[inline]
461    pub fn get_max_viewport_dimensions(&self) -> (u32, u32) {
462        let d = self.capabilities().max_viewport_dims;
463        (d.0 as u32, d.1 as u32)
464    }
465
466    /// Releases the shader compiler, indicating that no new programs will be created for a while.
467    ///
468    /// This method is a no-op if it's not available in the implementation.
469    pub fn release_shader_compiler(&self) {
470        unsafe {
471            let ctxt = self.make_current();
472
473            if (ctxt.version >= &Version(Api::GlEs, 2, 0) ||
474                ctxt.version >= &Version(Api::Gl, 4, 1)) && !ctxt.capabilities.supported_glsl_versions.is_empty() {
475                ctxt.gl.ReleaseShaderCompiler();
476            }
477        }
478    }
479
480    /// Returns an estimate of the amount of video memory available in bytes.
481    ///
482    /// Returns `None` if no estimate is available.
483    pub fn get_free_video_memory(&self) -> Option<usize> {
484        unsafe {
485            let ctxt = self.make_current();
486
487            let mut value: [gl::types::GLint; 4] = [0; 4];
488
489            if ctxt.extensions.gl_nvx_gpu_memory_info {
490                ctxt.gl.GetIntegerv(gl::GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX,
491                               &mut value[0]);
492                Some(value[0] as usize * 1024)
493
494            } else if ctxt.extensions.gl_ati_meminfo {
495                ctxt.gl.GetIntegerv(gl::TEXTURE_FREE_MEMORY_ATI, &mut value[0]);
496                Some(value[0] as usize * 1024)
497
498            } else {
499                None
500            }
501        }
502    }
503
504    /// Reads the content of the front buffer.
505    ///
506    /// You will only see the data that has finished being drawn.
507    ///
508    /// This function can return any type that implements `Texture2dDataSink<(u8, u8, u8, u8)>`.
509    ///
510    /// ## Example
511    ///
512    /// ```no_run
513    /// # use glutin::surface::{ResizeableSurface, SurfaceTypeTrait};
514    /// # fn example<T>(display: glium::Display<T>) where T: SurfaceTypeTrait + ResizeableSurface {
515    /// let pixels: Result<Vec<Vec<(u8, u8, u8, u8)>>, _> = display.read_front_buffer();
516    /// # }
517    /// ```
518    pub fn read_front_buffer<T>(&self) -> Result<T, ops::ReadError>
519        where T: texture::Texture2dDataSink<(u8, u8, u8, u8)>
520    {
521        let mut ctxt = self.make_current();
522        let dimensions = self.get_framebuffer_dimensions();
523        let rect = crate::Rect { left: 0, bottom: 0, width: dimensions.0, height: dimensions.1 };
524
525        let mut data = Vec::with_capacity(0);
526        ops::read(&mut ctxt, ops::Source::DefaultFramebuffer(gl::FRONT_LEFT), &rect,
527                          &mut data, false)?;
528        Ok(T::from_raw(Cow::Owned(data), dimensions.0, dimensions.1))
529    }
530
531    /// Execute an arbitrary closure with the OpenGL context active. Useful if another
532    /// component needs to directly manipulate OpenGL state.
533    ///
534    /// **If `action` manipulates any OpenGL state, it must be restored before `action`
535    /// completes.**
536    #[inline]
537    pub unsafe fn exec_in_context<'a, T, F>(&self, action: F) -> T
538                                            where T: Send + 'static,
539                                            F: FnOnce() -> T + 'a
540    {
541        let _ctxt = self.make_current();
542        action()
543    }
544
545    /// Asserts that there are no OpenGL errors pending.
546    ///
547    /// This function should be used in tests.
548    pub fn assert_no_error(&self, user_msg: Option<&str>) {
549        let mut ctxt = self.make_current();
550
551        match (crate::get_gl_error(&mut ctxt), user_msg) {
552            (Some(msg), None) => panic!("{}", msg),
553            (Some(msg), Some(user_msg)) => panic!("{} : {}", user_msg, msg),
554            (None, _) => ()
555        };
556    }
557
558    /// DEPRECATED. Renamed `finish`.
559    #[inline]
560    pub fn synchronize(&self) {
561        self.finish();
562    }
563
564    /// Calls `glFinish()`. This waits until all the previously issued commands have finished
565    /// being executed.
566    ///
567    /// When you execute OpenGL functions, they are not executed immediately. Instead they are
568    /// put in a queue. This function flushes this queue, then waits until all commands
569    /// have finished being executed.
570    ///
571    /// You normally don't need to call this function manually, except for debugging purposes.
572    #[inline]
573    pub fn finish(&self) {
574        let ctxt = self.make_current();
575        unsafe { ctxt.gl.Finish(); }
576    }
577
578    /// Calls `glFlush()`. This starts executing the commands that you have issued if it is not
579    /// yet the case.
580    ///
581    /// When you execute OpenGL functions, they are not executed immediately. Instead they are
582    /// put in a queue. This function flushes this queue so that commands start being executed.
583    ///
584    /// You normally don't need to call this function manually. Swapping buffers automatically
585    /// flushes the queue. This function can be useful if you want to benchmark the time it
586    /// takes from your OpenGL driver to process commands.
587    #[inline]
588    pub fn flush(&self) {
589        let ctxt = self.make_current();
590        unsafe { ctxt.gl.Flush(); }
591    }
592
593    /// Inserts a debugging string in the commands queue. If you use an OpenGL debugger, you will
594    /// be able to see that string.
595    ///
596    /// This is helpful to understand where you are when you have big applications.
597    ///
598    /// Returns `Err` if the backend doesn't support this functionality. You can choose whether
599    /// to call `.unwrap()` if you want to make sure that it works, or `.ok()` if you don't care.
600    pub fn insert_debug_marker(&self, marker: &str) -> Result<(), ()> {
601        let ctxt = self.make_current();
602
603        if ctxt.extensions.gl_gremedy_string_marker {
604            let marker = marker.as_bytes();
605            unsafe { ctxt.gl.StringMarkerGREMEDY(marker.len() as gl::types::GLsizei,
606                                                 marker.as_ptr() as *const _) };
607            Ok(())
608
609        } else if ctxt.extensions.gl_ext_debug_marker {
610            let marker = marker.as_bytes();
611            unsafe { ctxt.gl.InsertEventMarkerEXT(marker.len() as gl::types::GLsizei,
612                                                  marker.as_ptr() as *const _) };
613            Ok(())
614
615        } else {
616            Err(())
617        }
618    }
619
620    /// Same as `insert_debug_marker`, except that if you don't compile with `debug_assertions`
621    /// it is a no-op and returns `Ok`.
622    #[inline]
623    pub fn debug_insert_debug_marker(&self, marker: &str) -> Result<(), ()> {
624        if cfg!(debug_assertions) {
625            self.insert_debug_marker(marker)
626        } else {
627            Ok(())
628        }
629    }
630}
631
632impl ContextExt for Context {
633    #[inline]
634    fn set_report_debug_output_errors(&self, value: bool) {
635        self.report_debug_output_errors.set(value);
636    }
637
638    fn make_current(&self) -> CommandContext<'_> {
639        if self.check_current_context {
640            let backend = self.backend.borrow();
641            if !backend.is_current() {
642                unsafe { backend.make_current() };
643                debug_assert!(backend.is_current());
644            }
645        }
646
647        CommandContext {
648            gl: &self.gl,
649            state: self.state.borrow_mut(),
650            version: &self.version,
651            extensions: &self.extensions,
652            capabilities: &self.capabilities,
653            report_debug_output_errors: &self.report_debug_output_errors,
654            vertex_array_objects: &self.vertex_array_objects,
655            framebuffer_objects: self.framebuffer_objects.as_ref().unwrap(),
656            samplers: self.samplers.borrow_mut(),
657            resident_texture_handles: self.resident_texture_handles.borrow_mut(),
658            resident_image_handles: self.resident_image_handles.borrow_mut(),
659            marker: PhantomData,
660        }
661    }
662
663    #[inline]
664    fn capabilities(&self) -> &Capabilities {
665        &self.capabilities
666    }
667}
668
669impl CapabilitiesSource for Context {
670    #[inline]
671    fn get_version(&self) -> &Version {
672        &self.version
673    }
674
675    #[inline]
676    fn get_extensions(&self) -> &ExtensionsList {
677        &self.extensions
678    }
679
680    #[inline]
681    fn get_capabilities(&self) -> &Capabilities {
682        &self.capabilities
683    }
684}
685
686impl Drop for Context {
687    fn drop(&mut self) {
688        unsafe {
689            // this is the code of make_current duplicated here because we can't borrow
690            // `self` twice
691            if self.check_current_context {
692                let backend = self.backend.borrow();
693                if !backend.is_current() {
694                    backend.make_current();
695                }
696            }
697
698            let mut ctxt = CommandContext {
699                gl: &self.gl,
700                state: self.state.borrow_mut(),
701                version: &self.version,
702                extensions: &self.extensions,
703                capabilities: &self.capabilities,
704                report_debug_output_errors: &self.report_debug_output_errors,
705                vertex_array_objects: &self.vertex_array_objects,
706                framebuffer_objects: self.framebuffer_objects.as_ref().unwrap(),
707                samplers: self.samplers.borrow_mut(),
708                resident_texture_handles: self.resident_texture_handles.borrow_mut(),
709                resident_image_handles: self.resident_image_handles.borrow_mut(),
710                marker: PhantomData,
711            };
712
713            fbo::FramebuffersContainer::cleanup(&mut ctxt);
714            vertex_array_object::VertexAttributesSystem::cleanup(&mut ctxt);
715
716            for (_, s) in mem::replace(&mut *ctxt.samplers, HashMap::with_hasher(Default::default())) {
717                s.destroy(&mut ctxt);
718            }
719
720            // disabling callback
721            if ctxt.state.enabled_debug_output != Some(false) {
722                if ctxt.version >= &Version(Api::Gl, 4,5) || ctxt.extensions.gl_khr_debug {
723                    ctxt.gl.Disable(gl::DEBUG_OUTPUT);
724                } else if ctxt.extensions.gl_arb_debug_output {
725                    ctxt.gl.DebugMessageCallbackARB(None,
726                                                    ptr::null());
727                }
728
729                ctxt.state.enabled_debug_output = Some(false);
730                ctxt.gl.Finish();
731            }
732        }
733    }
734}
735
736impl<'a> CapabilitiesSource for CommandContext<'a> {
737    #[inline]
738    fn get_version(&self) -> &Version {
739        self.version
740    }
741
742    #[inline]
743    fn get_extensions(&self) -> &ExtensionsList {
744        self.extensions
745    }
746
747    #[inline]
748    fn get_capabilities(&self) -> &Capabilities {
749        self.capabilities
750    }
751}
752
753/// Checks whether the backend supports glium. Returns an `Err` if it doesn't.
754fn check_gl_compatibility(version: &Version, extensions: &ExtensionsList)
755    -> Result<(), IncompatibleOpenGl>
756{
757    let mut result = Vec::with_capacity(0);
758
759    if !(version >= &Version(Api::Gl, 1, 5)) &&
760        !(version >= &Version(Api::GlEs, 2, 0)) &&
761        (!extensions.gl_arb_vertex_buffer_object || !extensions.gl_arb_map_buffer_range)
762    {
763        result.push("OpenGL implementation doesn't support buffer objects");
764    }
765
766    if !(version >= &Version(Api::Gl, 2, 0)) &&
767        !(version >= &Version(Api::GlEs, 2, 0)) &&
768        (!extensions.gl_arb_shader_objects ||
769            !extensions.gl_arb_vertex_shader || !extensions.gl_arb_fragment_shader)
770    {
771        result.push("OpenGL implementation doesn't support vertex/fragment shaders");
772    }
773
774    if !extensions.gl_ext_framebuffer_object && !(version >= &Version(Api::Gl, 3, 0)) &&
775        !(version >= &Version(Api::GlEs, 2, 0)) && !extensions.gl_arb_framebuffer_object
776    {
777        result.push("OpenGL implementation doesn't support framebuffers");
778    }
779
780    if !extensions.gl_ext_framebuffer_blit && !(version >= &Version(Api::Gl, 3, 0)) &&
781        !(version >= &Version(Api::GlEs, 2, 0))
782    {
783        result.push("OpenGL implementation doesn't support blitting framebuffers");
784    }
785
786    if result.len() == 0 {
787        Ok(())
788    } else {
789        Err(IncompatibleOpenGl(result.join("\n")))
790    }
791}
792
793/// Describes the behavior that the debug output should have.
794pub enum DebugCallbackBehavior {
795    /// Don't do anything. This is the default behavior in release.
796    Ignore,
797
798    /// Print a message on stdout on error, except in some circumstances like when compiling
799    /// shaders. This is the default behavior in debug mode.
800    DebugMessageOnError,
801
802    /// Print every single output received by the driver.
803    PrintAll,
804
805    /// Use a custom callback.
806    Custom {
807        /// The function to be called.
808        callback: debug::DebugCallback,
809        /// Whether or not it should be called immediately (true) or asynchronously (false).
810        synchronous: bool,
811    },
812}
813
814impl Default for DebugCallbackBehavior {
815    #[inline]
816    fn default() -> DebugCallbackBehavior {
817        if cfg!(debug_assertions) {
818            DebugCallbackBehavior::DebugMessageOnError
819        } else {
820            DebugCallbackBehavior::Ignore
821        }
822    }
823}
824
825/// The callback corresponding to `DebugMessageOnError`.
826fn default_debug_callback(_: debug::Source, ty: debug::MessageType, severity: debug::Severity,
827                          _: u32, report_debug_output_errors: bool, message: &str)
828{
829    match severity {
830        debug::Severity::Medium => (),
831        debug::Severity::High => (),
832        _ => return
833    };
834
835    match ty {
836        debug::MessageType::Error => (),
837        debug::MessageType::DeprecatedBehavior => (),
838        debug::MessageType::UndefinedBehavior => (),
839        debug::MessageType::Portability => (),
840        _ => return,
841    };
842
843    if report_debug_output_errors {
844        eprint!("Debug message with high or medium severity: `{}`.\n\
845                 Please report this error: https://github.com/glium/glium/issues\n\
846                 Backtrace:",
847                 message);
848
849        let mut frame_id = 1;
850        backtrace::trace(|frame| {
851            let ip = frame.ip();
852            print!("\n{:>#4} - {:p}", frame_id, ip);
853
854            backtrace::resolve(ip, |symbol| {
855                let name = symbol.name()
856                                 .map(|n| n.as_str().unwrap_or("<not-utf8>"))
857                                 .unwrap_or("<unknown>");
858                let filename = symbol.filename()
859                                     .map(|p| p.to_str().unwrap_or("<not-utf8>"))
860                                     .unwrap_or("<unknown>");
861                let line = symbol.lineno().map(|l| l.to_string())
862                                          .unwrap_or_else(|| "??".to_owned());
863
864                print!("\n         {} at {}:{}", name, filename, line);
865            });
866
867            frame_id += 1;
868            true
869        });
870
871        eprintln!("\n");
872    }
873}
874
875/// The callback corresponding to `DebugMessageOnError`.
876fn printall_debug_callback(source: debug::Source, ty: debug::MessageType, severity: debug::Severity,
877                           id: u32, _: bool, message: &str)
878{
879    eprintln!("Source: {src:?}\t\tSeverity: {sev:?}\t\tType: {ty:?}\t\tId: {id}\n{msg}",
880               src = source, sev = severity, ty = ty, id = id, msg = message);
881}
882
883/// Initializes `GL_KHR_debug`, `GL_ARB_debug`, or a similar extension so that the debug output
884/// is reported.
885fn init_debug_callback(context: &Rc<Context>, synchronous: bool) {
886    // this is the C callback
887    extern "system" fn callback_wrapper(source: gl::types::GLenum, ty: gl::types::GLenum,
888                                        id: gl::types::GLuint, severity: gl::types::GLenum,
889                                        _length: gl::types::GLsizei,
890                                        message: *const gl::types::GLchar,
891                                        user_param: *mut raw::c_void)
892    {
893        // note that we transmute the user param into a proper context
894        // in order to enforce safety here, the context disables debug output and flushes in its
895        // destructor
896
897        let user_param = user_param as *const Context;
898        let user_param: &mut Context = unsafe { mem::transmute(user_param) };
899
900        let message = unsafe {
901            String::from_utf8(CStr::from_ptr(message).to_bytes().to_vec()).unwrap()
902        };
903
904        let severity = match severity {
905            gl::DEBUG_SEVERITY_NOTIFICATION => debug::Severity::Notification,
906            gl::DEBUG_SEVERITY_LOW => debug::Severity::Low,
907            gl::DEBUG_SEVERITY_MEDIUM => debug::Severity::Medium,
908            gl::DEBUG_SEVERITY_HIGH => debug::Severity::High,
909            _ => return,        // TODO: what to do in this situation?
910        };
911
912        let source = match source {
913            gl::DEBUG_SOURCE_API => debug::Source::Api,
914            gl::DEBUG_SOURCE_WINDOW_SYSTEM => debug::Source::WindowSystem,
915            gl::DEBUG_SOURCE_SHADER_COMPILER => debug::Source::ShaderCompiler,
916            gl::DEBUG_SOURCE_THIRD_PARTY => debug::Source::ThirdParty,
917            gl::DEBUG_SOURCE_APPLICATION => debug::Source::Application,
918            gl::DEBUG_SOURCE_OTHER => debug::Source::OtherSource,
919            _ => return,        // TODO: what to do in this situation?
920        };
921
922        let ty = match ty {
923            gl::DEBUG_TYPE_ERROR => debug::MessageType::Error,
924            gl::DEBUG_TYPE_DEPRECATED_BEHAVIOR => debug::MessageType::DeprecatedBehavior,
925            gl::DEBUG_TYPE_UNDEFINED_BEHAVIOR => debug::MessageType::UndefinedBehavior,
926            gl::DEBUG_TYPE_PORTABILITY => debug::MessageType::Portability,
927            gl::DEBUG_TYPE_PERFORMANCE => debug::MessageType::Performance,
928            gl::DEBUG_TYPE_MARKER => debug::MessageType::Marker,
929            gl::DEBUG_TYPE_PUSH_GROUP => debug::MessageType::PushGroup,
930            gl::DEBUG_TYPE_POP_GROUP => debug::MessageType::PopGroup,
931            gl::DEBUG_TYPE_OTHER => debug::MessageType::Other,
932            _ => return,        // TODO: what to do in this situation?
933        };
934
935        if let Some(callback) = user_param.debug_callback.as_mut() {
936            // FIXME: catch_panic here once it's stable
937            callback(source, ty, severity, id, user_param.report_debug_output_errors.get(),
938                     &message);
939        }
940    }
941
942    struct ContextRawPtr(*const Context);
943    unsafe impl Send for ContextRawPtr {}
944    let context_raw_ptr = ContextRawPtr(&**context);
945
946    unsafe {
947        let mut ctxt = context.make_current();
948
949        if ctxt.version >= &Version(Api::Gl, 4,5) || ctxt.version >= &Version(Api::GlEs, 3, 2) ||
950           ctxt.extensions.gl_khr_debug || ctxt.extensions.gl_arb_debug_output
951        {
952            if synchronous && !ctxt.state.enabled_debug_output_synchronous {
953                ctxt.gl.Enable(gl::DEBUG_OUTPUT_SYNCHRONOUS);
954                ctxt.state.enabled_debug_output_synchronous = true;
955            }
956
957            if ctxt.version >= &Version(Api::Gl, 4, 5) ||
958               ctxt.version >= &Version(Api::GlEs, 3, 2) ||
959               (ctxt.version >= &Version(Api::Gl, 1, 0) && ctxt.extensions.gl_khr_debug)
960            {
961                ctxt.gl.DebugMessageCallback(Some(callback_wrapper), context_raw_ptr.0
962                                                                     as *const _);
963                ctxt.gl.DebugMessageControl(gl::DONT_CARE, gl::DONT_CARE, gl::DONT_CARE, 0,
964                                            ptr::null(), gl::TRUE);
965
966                if ctxt.state.enabled_debug_output != Some(true) {
967                    ctxt.gl.Enable(gl::DEBUG_OUTPUT);
968                    ctxt.state.enabled_debug_output = Some(true);
969                }
970
971            } else if ctxt.version >= &Version(Api::GlEs, 2, 0) &&
972                      ctxt.extensions.gl_khr_debug
973            {
974                ctxt.gl.DebugMessageCallbackKHR(Some(callback_wrapper), context_raw_ptr.0
975                                                                        as *const _);
976                ctxt.gl.DebugMessageControlKHR(gl::DONT_CARE, gl::DONT_CARE, gl::DONT_CARE, 0,
977                                               ptr::null(), gl::TRUE);
978
979                if ctxt.state.enabled_debug_output != Some(true) {
980                    ctxt.gl.Enable(gl::DEBUG_OUTPUT);
981                    ctxt.state.enabled_debug_output = Some(true);
982                }
983
984            } else {
985                ctxt.gl.DebugMessageCallbackARB(Some(callback_wrapper), context_raw_ptr.0
986                                                                        as *const _);
987                ctxt.gl.DebugMessageControlARB(gl::DONT_CARE, gl::DONT_CARE, gl::DONT_CARE,
988                                               0, ptr::null(), gl::TRUE);
989
990                ctxt.state.enabled_debug_output = Some(true);
991            }
992        }
993    }
994}