use gl;
use backtrace;
use std::collections::HashMap;
use std::mem;
use std::ptr;
use std::str;
use std::borrow::Cow;
use std::cell::{Cell, RefCell, RefMut};
use std::marker::PhantomData;
use std::ffi::CStr;
use std::rc::Rc;
use std::os::raw;
use std::hash::BuildHasherDefault;
use fnv::FnvHasher;
use IncompatibleOpenGl;
use SwapBuffersError;
use CapabilitiesSource;
use ContextExt;
use backend::Backend;
use version;
use version::Api;
use version::Version;
use debug;
use fbo;
use ops;
use sampler_object;
use texture;
use uniforms;
use vertex_array_object;
pub use self::capabilities::{ReleaseBehavior, Capabilities, Profile};
pub use self::extensions::ExtensionsList;
pub use self::state::GlState;
mod capabilities;
mod extensions;
mod state;
pub struct Context {
gl: gl::Gl,
state: RefCell<GlState>,
version: Version,
extensions: ExtensionsList,
capabilities: Capabilities,
backend: RefCell<Box<dyn Backend>>,
check_current_context: bool,
debug_callback: Option<debug::DebugCallback>,
report_debug_output_errors: Cell<bool>,
framebuffer_objects: Option<fbo::FramebuffersContainer>,
vertex_array_objects: vertex_array_object::VertexAttributesSystem,
samplers: RefCell<HashMap<uniforms::SamplerBehavior, sampler_object::SamplerObject, BuildHasherDefault<FnvHasher>>>,
resident_texture_handles: RefCell<Vec<gl::types::GLuint64>>,
resident_image_handles: RefCell<Vec<(gl::types::GLuint64, gl::types::GLenum)>>,
}
pub struct CommandContext<'a> {
pub gl: &'a gl::Gl,
pub state: RefMut<'a, GlState>,
pub version: &'a Version,
pub extensions: &'a ExtensionsList,
pub capabilities: &'a Capabilities,
pub report_debug_output_errors: &'a Cell<bool>,
pub vertex_array_objects: &'a vertex_array_object::VertexAttributesSystem,
pub framebuffer_objects: &'a fbo::FramebuffersContainer,
pub samplers: RefMut<'a, HashMap<uniforms::SamplerBehavior, sampler_object::SamplerObject, BuildHasherDefault<FnvHasher>>>,
pub resident_texture_handles: RefMut<'a, Vec<gl::types::GLuint64>>,
pub resident_image_handles: RefMut<'a, Vec<(gl::types::GLuint64, gl::types::GLenum)>>,
marker: PhantomData<*mut u8>,
}
impl Context {
pub unsafe fn new<B>(
backend: B,
check_current_context: bool,
callback_behavior: DebugCallbackBehavior,
) -> Result<Rc<Context>, IncompatibleOpenGl>
where B: Backend + 'static
{
backend.make_current();
let gl = gl::Gl::load_with(|symbol| backend.get_proc_address(symbol) as *const _);
let gl_state: RefCell<GlState> = RefCell::new(Default::default());
let version = version::get_gl_version(&gl);
let extensions = extensions::get_extensions(&gl, &version);
check_gl_compatibility(&version, &extensions)?;
let capabilities = capabilities::get_capabilities(&gl, &version, &extensions);
let report_debug_output_errors = Cell::new(true);
let vertex_array_objects = vertex_array_object::VertexAttributesSystem::new();
let framebuffer_objects = fbo::FramebuffersContainer::new();
let samplers = RefCell::new({
let mut map = HashMap::with_hasher(Default::default());
map.reserve(16);
map
});
let resident_texture_handles = RefCell::new(Vec::new());
let resident_image_handles = RefCell::new(Vec::new());
let (debug_callback, synchronous) = match callback_behavior {
DebugCallbackBehavior::Ignore => (None, false),
DebugCallbackBehavior::DebugMessageOnError => {
(Some(Box::new(default_debug_callback) as debug::DebugCallback), true)
},
DebugCallbackBehavior::PrintAll => {
(Some(Box::new(printall_debug_callback) as debug::DebugCallback), false)
},
DebugCallbackBehavior::Custom { callback, synchronous } => {
(Some(callback), synchronous)
},
};
let context = Rc::new(Context {
gl: gl,
state: gl_state,
version: version,
extensions: extensions,
capabilities: capabilities,
debug_callback: debug_callback,
report_debug_output_errors: report_debug_output_errors,
backend: RefCell::new(Box::new(backend)),
check_current_context: check_current_context,
framebuffer_objects: Some(framebuffer_objects),
vertex_array_objects: vertex_array_objects,
samplers: samplers,
resident_texture_handles: resident_texture_handles,
resident_image_handles: resident_image_handles,
});
if context.debug_callback.is_some() {
init_debug_callback(&context, synchronous);
}
{
let mut ctxt = context.make_current();
if ::get_gl_error(&mut ctxt).is_some() {
println!("glium has triggered an OpenGL error during initialization. Please report \
this error: https://github.com/tomaka/glium/issues");
}
if ctxt.version >= &Version(Api::Gl, 3, 2) && ctxt.extensions.gl_arb_seamless_cube_map {
ctxt.gl.Enable(gl::TEXTURE_CUBE_MAP_SEAMLESS);
}
}
Ok(context)
}
#[inline]
pub fn get_framebuffer_dimensions(&self) -> (u32, u32) {
self.backend.borrow().get_framebuffer_dimensions()
}
pub unsafe fn rebuild<B>(&self, new_backend: B) -> Result<(), IncompatibleOpenGl>
where B: Backend + 'static
{
{
let mut ctxt = self.make_current();
fbo::FramebuffersContainer::purge_all(&mut ctxt);
vertex_array_object::VertexAttributesSystem::purge_all(&mut ctxt);
}
new_backend.make_current();
*self.state.borrow_mut() = Default::default();
*self.backend.borrow_mut() = Box::new(new_backend);
let textures = self.resident_texture_handles.borrow();
for &texture in textures.iter() {
self.gl.MakeTextureHandleResidentARB(texture);
}
let images = self.resident_image_handles.borrow();
for &(image, access) in images.iter() {
self.gl.MakeImageHandleResidentARB(image, access);
}
Ok(())
}
pub fn swap_buffers(&self) -> Result<(), SwapBuffersError> {
if self.state.borrow().lost_context {
return Err(SwapBuffersError::ContextLost);
}
if self.state.borrow().draw_framebuffer != 0 || self.state.borrow().read_framebuffer != 0 {
let mut ctxt = self.make_current();
if ctxt.version >= &Version(Api::Gl, 3, 0) ||
ctxt.extensions.gl_arb_framebuffer_object
{
unsafe { ctxt.gl.BindFramebuffer(gl::FRAMEBUFFER, 0); }
ctxt.state.draw_framebuffer = 0;
ctxt.state.read_framebuffer = 0;
} else if ctxt.version >= &Version(Api::GlEs, 2, 0) {
unsafe { ctxt.gl.BindFramebuffer(gl::FRAMEBUFFER, 0); }
ctxt.state.draw_framebuffer = 0;
ctxt.state.read_framebuffer = 0;
} else if ctxt.extensions.gl_ext_framebuffer_object {
unsafe { ctxt.gl.BindFramebufferEXT(gl::FRAMEBUFFER_EXT, 0); }
ctxt.state.draw_framebuffer = 0;
ctxt.state.read_framebuffer = 0;
} else {
unreachable!();
}
}
let backend = self.backend.borrow();
if self.check_current_context {
if !backend.is_current() {
unsafe { backend.make_current() };
}
}
let err = backend.swap_buffers();
if let Err(SwapBuffersError::ContextLost) = err {
self.state.borrow_mut().lost_context = true;
}
err
}
#[inline]
#[deprecated(note = "use `get_opengl_version` instead.")]
pub fn get_version(&self) -> &Version {
&self.version
}
#[inline]
pub fn get_opengl_version(&self) -> &Version {
&self.version
}
#[inline]
pub fn get_supported_glsl_version(&self) -> Version {
version::get_supported_glsl_version(self.get_opengl_version())
}
#[inline]
pub fn is_glsl_version_supported(&self, version: &Version) -> bool {
self.capabilities().supported_glsl_versions.iter().find(|&v| v == version).is_some()
}
#[inline]
pub fn get_opengl_version_string(&self) -> &str {
&self.capabilities().version
}
#[inline]
pub fn get_opengl_vendor_string(&self) -> &str {
&self.capabilities().vendor
}
#[inline]
pub fn get_opengl_renderer_string(&self) -> &str {
&self.capabilities().renderer
}
#[inline]
pub fn is_debug(&self) -> bool {
self.capabilities().debug
}
#[inline]
pub fn is_forward_compatible(&self) -> bool {
self.capabilities().forward_compatible
}
pub fn get_opengl_profile(&self) -> Option<Profile> {
self.capabilities().profile
}
#[inline]
pub fn is_robust(&self) -> bool {
self.capabilities().robustness
}
#[inline]
pub fn is_context_loss_possible(&self) -> bool {
self.capabilities().can_lose_context
}
pub fn is_context_lost(&self) -> bool {
if self.state.borrow().lost_context {
return true;
}
let mut ctxt = self.make_current();
let lost = if ctxt.version >= &Version(Api::Gl, 4, 5) ||
ctxt.version >= &Version(Api::GlEs, 3, 2) ||
ctxt.extensions.gl_khr_robustness
{
unsafe { ctxt.gl.GetGraphicsResetStatus() != gl::NO_ERROR }
} else if ctxt.extensions.gl_ext_robustness {
unsafe { ctxt.gl.GetGraphicsResetStatusEXT() != gl::NO_ERROR }
} else if ctxt.extensions.gl_arb_robustness {
unsafe { ctxt.gl.GetGraphicsResetStatusARB() != gl::NO_ERROR }
} else {
false
};
if lost { ctxt.state.lost_context = true; }
lost
}
#[inline]
pub fn get_release_behavior(&self) -> ReleaseBehavior {
self.capabilities().release_behavior
}
#[inline]
pub fn get_max_anisotropy_support(&self) -> Option<u16> {
self.capabilities().max_texture_max_anisotropy.map(|v| v as u16)
}
#[inline]
pub fn get_max_viewport_dimensions(&self) -> (u32, u32) {
let d = self.capabilities().max_viewport_dims;
(d.0 as u32, d.1 as u32)
}
pub fn release_shader_compiler(&self) {
unsafe {
let ctxt = self.make_current();
if ctxt.version >= &Version(Api::GlEs, 2, 0) ||
ctxt.version >= &Version(Api::Gl, 4, 1)
{
if !ctxt.capabilities.supported_glsl_versions.is_empty() {
ctxt.gl.ReleaseShaderCompiler();
}
}
}
}
pub fn get_free_video_memory(&self) -> Option<usize> {
unsafe {
let ctxt = self.make_current();
let mut value: [gl::types::GLint; 4] = [0; 4];
if ctxt.extensions.gl_nvx_gpu_memory_info {
ctxt.gl.GetIntegerv(gl::GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX,
&mut value[0]);
Some(value[0] as usize * 1024)
} else if ctxt.extensions.gl_ati_meminfo {
ctxt.gl.GetIntegerv(gl::TEXTURE_FREE_MEMORY_ATI, &mut value[0]);
Some(value[0] as usize * 1024)
} else {
return None;
}
}
}
pub fn read_front_buffer<T>(&self) -> Result<T, ops::ReadError>
where T: texture::Texture2dDataSink<(u8, u8, u8, u8)>
{
let mut ctxt = self.make_current();
let dimensions = self.get_framebuffer_dimensions();
let rect = ::Rect { left: 0, bottom: 0, width: dimensions.0, height: dimensions.1 };
let mut data = Vec::with_capacity(0);
ops::read(&mut ctxt, ops::Source::DefaultFramebuffer(gl::FRONT_LEFT), &rect,
&mut data, false)?;
Ok(T::from_raw(Cow::Owned(data), dimensions.0, dimensions.1))
}
#[inline]
pub unsafe fn exec_in_context<'a, T, F>(&self, action: F) -> T
where T: Send + 'static,
F: FnOnce() -> T + 'a
{
let _ctxt = self.make_current();
action()
}
pub fn assert_no_error(&self, user_msg: Option<&str>) {
let mut ctxt = self.make_current();
match (::get_gl_error(&mut ctxt), user_msg) {
(Some(msg), None) => panic!("{}", msg),
(Some(msg), Some(user_msg)) => panic!("{} : {}", user_msg, msg),
(None, _) => ()
};
}
#[inline]
pub fn synchronize(&self) {
self.finish();
}
#[inline]
pub fn finish(&self) {
let ctxt = self.make_current();
unsafe { ctxt.gl.Finish(); }
}
#[inline]
pub fn flush(&self) {
let ctxt = self.make_current();
unsafe { ctxt.gl.Flush(); }
}
pub fn insert_debug_marker(&self, marker: &str) -> Result<(), ()> {
let ctxt = self.make_current();
if ctxt.extensions.gl_gremedy_string_marker {
let marker = marker.as_bytes();
unsafe { ctxt.gl.StringMarkerGREMEDY(marker.len() as gl::types::GLsizei,
marker.as_ptr() as *const _) };
Ok(())
} else if ctxt.extensions.gl_ext_debug_marker {
let marker = marker.as_bytes();
unsafe { ctxt.gl.InsertEventMarkerEXT(marker.len() as gl::types::GLsizei,
marker.as_ptr() as *const _) };
Ok(())
} else {
Err(())
}
}
#[inline]
pub fn debug_insert_debug_marker(&self, marker: &str) -> Result<(), ()> {
if cfg!(debug_assertions) {
self.insert_debug_marker(marker)
} else {
Ok(())
}
}
}
impl ContextExt for Context {
#[inline]
fn set_report_debug_output_errors(&self, value: bool) {
self.report_debug_output_errors.set(value);
}
fn make_current(&self) -> CommandContext {
if self.check_current_context {
let backend = self.backend.borrow();
if !backend.is_current() {
unsafe { backend.make_current() };
debug_assert!(backend.is_current());
}
}
CommandContext {
gl: &self.gl,
state: self.state.borrow_mut(),
version: &self.version,
extensions: &self.extensions,
capabilities: &self.capabilities,
report_debug_output_errors: &self.report_debug_output_errors,
vertex_array_objects: &self.vertex_array_objects,
framebuffer_objects: self.framebuffer_objects.as_ref().unwrap(),
samplers: self.samplers.borrow_mut(),
resident_texture_handles: self.resident_texture_handles.borrow_mut(),
resident_image_handles: self.resident_image_handles.borrow_mut(),
marker: PhantomData,
}
}
#[inline]
fn capabilities(&self) -> &Capabilities {
&self.capabilities
}
}
impl CapabilitiesSource for Context {
#[inline]
fn get_version(&self) -> &Version {
&self.version
}
#[inline]
fn get_extensions(&self) -> &ExtensionsList {
&self.extensions
}
#[inline]
fn get_capabilities(&self) -> &Capabilities {
&self.capabilities
}
}
impl Drop for Context {
fn drop(&mut self) {
unsafe {
if self.check_current_context {
let backend = self.backend.borrow();
if !backend.is_current() {
backend.make_current();
}
}
let mut ctxt = CommandContext {
gl: &self.gl,
state: self.state.borrow_mut(),
version: &self.version,
extensions: &self.extensions,
capabilities: &self.capabilities,
report_debug_output_errors: &self.report_debug_output_errors,
vertex_array_objects: &self.vertex_array_objects,
framebuffer_objects: self.framebuffer_objects.as_ref().unwrap(),
samplers: self.samplers.borrow_mut(),
resident_texture_handles: self.resident_texture_handles.borrow_mut(),
resident_image_handles: self.resident_image_handles.borrow_mut(),
marker: PhantomData,
};
fbo::FramebuffersContainer::cleanup(&mut ctxt);
vertex_array_object::VertexAttributesSystem::cleanup(&mut ctxt);
for (_, s) in mem::replace(&mut *ctxt.samplers, HashMap::with_hasher(Default::default())) {
s.destroy(&mut ctxt);
}
if ctxt.state.enabled_debug_output != Some(false) {
if ctxt.version >= &Version(Api::Gl, 4,5) || ctxt.extensions.gl_khr_debug {
ctxt.gl.Disable(gl::DEBUG_OUTPUT);
} else if ctxt.extensions.gl_arb_debug_output {
ctxt.gl.DebugMessageCallbackARB(None,
ptr::null());
}
ctxt.state.enabled_debug_output = Some(false);
ctxt.gl.Finish();
}
}
}
}
impl<'a> CapabilitiesSource for CommandContext<'a> {
#[inline]
fn get_version(&self) -> &Version {
self.version
}
#[inline]
fn get_extensions(&self) -> &ExtensionsList {
self.extensions
}
#[inline]
fn get_capabilities(&self) -> &Capabilities {
self.capabilities
}
}
fn check_gl_compatibility(version: &Version, extensions: &ExtensionsList)
-> Result<(), IncompatibleOpenGl>
{
let mut result = Vec::with_capacity(0);
if !(version >= &Version(Api::Gl, 1, 5)) &&
!(version >= &Version(Api::GlEs, 2, 0)) &&
(!extensions.gl_arb_vertex_buffer_object || !extensions.gl_arb_map_buffer_range)
{
result.push("OpenGL implementation doesn't support buffer objects");
}
if !(version >= &Version(Api::Gl, 2, 0)) &&
!(version >= &Version(Api::GlEs, 2, 0)) &&
(!extensions.gl_arb_shader_objects ||
!extensions.gl_arb_vertex_shader || !extensions.gl_arb_fragment_shader)
{
result.push("OpenGL implementation doesn't support vertex/fragment shaders");
}
if !extensions.gl_ext_framebuffer_object && !(version >= &Version(Api::Gl, 3, 0)) &&
!(version >= &Version(Api::GlEs, 2, 0)) && !extensions.gl_arb_framebuffer_object
{
result.push("OpenGL implementation doesn't support framebuffers");
}
if !extensions.gl_ext_framebuffer_blit && !(version >= &Version(Api::Gl, 3, 0)) &&
!(version >= &Version(Api::GlEs, 2, 0))
{
result.push("OpenGL implementation doesn't support blitting framebuffers");
}
if result.len() == 0 {
Ok(())
} else {
Err(IncompatibleOpenGl(result.join("\n")))
}
}
pub enum DebugCallbackBehavior {
Ignore,
DebugMessageOnError,
PrintAll,
Custom {
callback: debug::DebugCallback,
synchronous: bool,
},
}
impl Default for DebugCallbackBehavior {
#[inline]
fn default() -> DebugCallbackBehavior {
if cfg!(debug_assertions) {
DebugCallbackBehavior::DebugMessageOnError
} else {
DebugCallbackBehavior::Ignore
}
}
}
fn default_debug_callback(_: debug::Source, ty: debug::MessageType, severity: debug::Severity,
_: u32, report_debug_output_errors: bool, message: &str)
{
match severity {
debug::Severity::Medium => (),
debug::Severity::High => (),
_ => return
};
match ty {
debug::MessageType::Error => (),
debug::MessageType::DeprecatedBehavior => (),
debug::MessageType::UndefinedBehavior => (),
debug::MessageType::Portability => (),
_ => return,
};
if report_debug_output_errors {
print!("Debug message with high or medium severity: `{}`.\n\
Please report this error: https://github.com/tomaka/glium/issues\n\
Backtrace:",
message);
let mut frame_id = 1;
backtrace::trace(|frame| {
let ip = frame.ip();
print!("\n{:>#4} - {:p}", frame_id, ip);
backtrace::resolve(ip, |symbol| {
let name = symbol.name()
.map(|n| n.as_str().unwrap_or("<not-utf8>"))
.unwrap_or("<unknown>");
let filename = symbol.filename()
.map(|p| p.to_str().unwrap_or("<not-utf8>"))
.unwrap_or("<unknown>");
let line = symbol.lineno().map(|l| l.to_string())
.unwrap_or_else(|| "??".to_owned());
print!("\n {} at {}:{}", name, filename, line);
});
frame_id += 1;
true
});
println!("\n");
}
}
fn printall_debug_callback(source: debug::Source, ty: debug::MessageType, severity: debug::Severity,
id: u32, _: bool, message: &str)
{
println!("Source: {src:?}\t\tSeverity: {sev:?}\t\tType: {ty:?}\t\tId: {id}\n{msg}",
src = source, sev = severity, ty = ty, id = id, msg = message);
}
fn init_debug_callback(context: &Rc<Context>, synchronous: bool) {
extern "system" fn callback_wrapper(source: gl::types::GLenum, ty: gl::types::GLenum,
id: gl::types::GLuint, severity: gl::types::GLenum,
_length: gl::types::GLsizei,
message: *const gl::types::GLchar,
user_param: *mut raw::c_void)
{
let user_param = user_param as *const Context;
let user_param: &mut Context = unsafe { mem::transmute(user_param) };
let message = unsafe {
String::from_utf8(CStr::from_ptr(message).to_bytes().to_vec()).unwrap()
};
let severity = match severity {
gl::DEBUG_SEVERITY_NOTIFICATION => debug::Severity::Notification,
gl::DEBUG_SEVERITY_LOW => debug::Severity::Low,
gl::DEBUG_SEVERITY_MEDIUM => debug::Severity::Medium,
gl::DEBUG_SEVERITY_HIGH => debug::Severity::High,
_ => return,
};
let source = match source {
gl::DEBUG_SOURCE_API => debug::Source::Api,
gl::DEBUG_SOURCE_WINDOW_SYSTEM => debug::Source::WindowSystem,
gl::DEBUG_SOURCE_SHADER_COMPILER => debug::Source::ShaderCompiler,
gl::DEBUG_SOURCE_THIRD_PARTY => debug::Source::ThirdParty,
gl::DEBUG_SOURCE_APPLICATION => debug::Source::Application,
gl::DEBUG_SOURCE_OTHER => debug::Source::OtherSource,
_ => return,
};
let ty = match ty {
gl::DEBUG_TYPE_ERROR => debug::MessageType::Error,
gl::DEBUG_TYPE_DEPRECATED_BEHAVIOR => debug::MessageType::DeprecatedBehavior,
gl::DEBUG_TYPE_UNDEFINED_BEHAVIOR => debug::MessageType::UndefinedBehavior,
gl::DEBUG_TYPE_PORTABILITY => debug::MessageType::Portability,
gl::DEBUG_TYPE_PERFORMANCE => debug::MessageType::Performance,
gl::DEBUG_TYPE_MARKER => debug::MessageType::Marker,
gl::DEBUG_TYPE_PUSH_GROUP => debug::MessageType::PushGroup,
gl::DEBUG_TYPE_POP_GROUP => debug::MessageType::PopGroup,
gl::DEBUG_TYPE_OTHER => debug::MessageType::Other,
_ => return,
};
if let Some(callback) = user_param.debug_callback.as_mut() {
callback(source, ty, severity, id, user_param.report_debug_output_errors.get(),
&message);
}
}
struct ContextRawPtr(*const Context);
unsafe impl Send for ContextRawPtr {}
let context_raw_ptr = ContextRawPtr(&**context);
unsafe {
let mut ctxt = context.make_current();
if ctxt.version >= &Version(Api::Gl, 4,5) || ctxt.version >= &Version(Api::GlEs, 3, 2) ||
ctxt.extensions.gl_khr_debug || ctxt.extensions.gl_arb_debug_output
{
if synchronous {
if ctxt.state.enabled_debug_output_synchronous != true {
ctxt.gl.Enable(gl::DEBUG_OUTPUT_SYNCHRONOUS);
ctxt.state.enabled_debug_output_synchronous = true;
}
}
if ctxt.version >= &Version(Api::Gl, 4, 5) ||
ctxt.version >= &Version(Api::GlEs, 3, 2) ||
(ctxt.version >= &Version(Api::Gl, 1, 0) && ctxt.extensions.gl_khr_debug)
{
ctxt.gl.DebugMessageCallback(Some(callback_wrapper), context_raw_ptr.0
as *const _);
ctxt.gl.DebugMessageControl(gl::DONT_CARE, gl::DONT_CARE, gl::DONT_CARE, 0,
ptr::null(), gl::TRUE);
if ctxt.state.enabled_debug_output != Some(true) {
ctxt.gl.Enable(gl::DEBUG_OUTPUT);
ctxt.state.enabled_debug_output = Some(true);
}
} else if ctxt.version >= &Version(Api::GlEs, 2, 0) &&
ctxt.extensions.gl_khr_debug
{
ctxt.gl.DebugMessageCallbackKHR(Some(callback_wrapper), context_raw_ptr.0
as *const _);
ctxt.gl.DebugMessageControlKHR(gl::DONT_CARE, gl::DONT_CARE, gl::DONT_CARE, 0,
ptr::null(), gl::TRUE);
if ctxt.state.enabled_debug_output != Some(true) {
ctxt.gl.Enable(gl::DEBUG_OUTPUT);
ctxt.state.enabled_debug_output = Some(true);
}
} else {
ctxt.gl.DebugMessageCallbackARB(Some(callback_wrapper), context_raw_ptr.0
as *const _);
ctxt.gl.DebugMessageControlARB(gl::DONT_CARE, gl::DONT_CARE, gl::DONT_CARE,
0, ptr::null(), gl::TRUE);
ctxt.state.enabled_debug_output = Some(true);
}
}
}
}