From da329909f93597970afd169cee28ece3bee7127b Mon Sep 17 00:00:00 2001 From: HampusM Date: Sun, 15 Oct 2023 00:06:07 +0200 Subject: feat(engine): add logging OpenGL debug messages --- engine/src/opengl/debug.rs | 145 +++++++++++++++++++++++++++++++++++++++++++++ engine/src/opengl/mod.rs | 5 ++ engine/src/opengl/util.rs | 30 ++++++++++ 3 files changed, 180 insertions(+) create mode 100644 engine/src/opengl/debug.rs create mode 100644 engine/src/opengl/util.rs (limited to 'engine/src/opengl') diff --git a/engine/src/opengl/debug.rs b/engine/src/opengl/debug.rs new file mode 100644 index 0000000..203590a --- /dev/null +++ b/engine/src/opengl/debug.rs @@ -0,0 +1,145 @@ +use std::ffi::c_void; +use std::io::{stderr, Write}; +use std::panic::catch_unwind; +use std::ptr::null_mut; +use std::sync::Mutex; + +use crate::opengl::util::gl_enum; + +pub type MessageCallback = fn( + source: MessageSource, + ty: MessageType, + id: u32, + severity: MessageSeverity, + message: &str, +); + +pub fn enable_debug_output() +{ + unsafe { + gl::Enable(gl::DEBUG_OUTPUT); + gl::Enable(gl::DEBUG_OUTPUT_SYNCHRONOUS); + } +} + +pub fn set_debug_message_callback(cb: MessageCallback) +{ + *DEBUG_MESSAGE_CB.lock().unwrap() = Some(cb); + + unsafe { + gl::DebugMessageCallback(Some(debug_message_cb), null_mut()); + } +} + +pub fn set_debug_message_control( + source: Option, + ty: Option, + severity: Option, + ids: &[u32], + ids_action: MessageIdsAction, +) +{ + // Ids shouldn't realistically be large enough to cause a panic here + let ids_len: i32 = ids.len().try_into().unwrap(); + + unsafe { + gl::DebugMessageControl( + source.map_or(gl::DONT_CARE, |source| source as u32), + ty.map_or(gl::DONT_CARE, |ty| ty as u32), + severity.map_or(gl::DONT_CARE, |severity| severity as u32), + ids_len, + ids.as_ptr(), + ids_action as u8, + ); + } +} + +#[derive(Debug, Clone, Copy)] +#[allow(dead_code)] +pub enum MessageIdsAction +{ + Enable = 1, + Disable = 0, +} + +gl_enum! { +pub enum MessageSource +{ + Api = gl::DEBUG_SOURCE_API, + WindowSystem = gl::DEBUG_SOURCE_WINDOW_SYSTEM, + ShaderCompiler = gl::DEBUG_SOURCE_SHADER_COMPILER, + ThirdParty = gl::DEBUG_SOURCE_THIRD_PARTY, + Application = gl::DEBUG_SOURCE_APPLICATION, + Other = gl::DEBUG_SOURCE_OTHER, +} +} + +gl_enum! { +pub enum MessageType +{ + DeprecatedBehavior = gl::DEBUG_TYPE_DEPRECATED_BEHAVIOR, + Error = gl::DEBUG_TYPE_ERROR, + Marker = gl::DEBUG_TYPE_MARKER, + Other = gl::DEBUG_TYPE_OTHER, + Performance = gl::DEBUG_TYPE_PERFORMANCE, + PopGroup = gl::DEBUG_TYPE_POP_GROUP, + PushGroup = gl::DEBUG_TYPE_PUSH_GROUP, + Portability = gl::DEBUG_TYPE_PORTABILITY, + UndefinedBehavior = gl::DEBUG_TYPE_UNDEFINED_BEHAVIOR, +} +} + +gl_enum! { +pub enum MessageSeverity +{ + High = gl::DEBUG_SEVERITY_HIGH, + Medium = gl::DEBUG_SEVERITY_MEDIUM, + Low = gl::DEBUG_SEVERITY_LOW, + Notification = gl::DEBUG_SEVERITY_NOTIFICATION, +} +} + +static DEBUG_MESSAGE_CB: Mutex> = Mutex::new(None); + +extern "system" fn debug_message_cb( + source: gl::types::GLenum, + ty: gl::types::GLenum, + id: gl::types::GLuint, + severity: gl::types::GLenum, + message_length: gl::types::GLsizei, + message: *const gl::types::GLchar, + _user_param: *mut c_void, +) +{ + // Unwinds are catched because unwinding from Rust code into foreign code is UB. + let res = catch_unwind(|| { + let cb_lock = DEBUG_MESSAGE_CB.lock().unwrap(); + + if let Some(cb) = *cb_lock { + let msg_source = MessageSource::from_gl(source).unwrap(); + let msg_type = MessageType::from_gl(ty).unwrap(); + let msg_severity = MessageSeverity::from_gl(severity).unwrap(); + + let msg_length = usize::try_from(message_length).unwrap(); + + // SAFETY: The received message should be a valid ASCII string + let message = unsafe { + std::str::from_utf8_unchecked(std::slice::from_raw_parts( + message.cast(), + msg_length, + )) + }; + + cb(msg_source, msg_type, id, msg_severity, message); + } + }); + + if res.is_err() { + // eprintln is not used since it can panic and unwinds are unwanted because + // unwinding from Rust code into foreign code is UB. + stderr() + .write_all(b"ERROR: Panic in debug message callback") + .ok(); + println!(); + } +} diff --git a/engine/src/opengl/mod.rs b/engine/src/opengl/mod.rs index a58e72e..ce375f5 100644 --- a/engine/src/opengl/mod.rs +++ b/engine/src/opengl/mod.rs @@ -8,6 +8,11 @@ pub mod shader; pub mod vertex_array; pub mod vertex_buffer; +mod util; + +#[cfg(feature = "debug")] +pub mod debug; + pub fn set_viewport(position: &Vec2, size: &WindowSize) { unsafe { diff --git a/engine/src/opengl/util.rs b/engine/src/opengl/util.rs new file mode 100644 index 0000000..e60778f --- /dev/null +++ b/engine/src/opengl/util.rs @@ -0,0 +1,30 @@ +// May only be used when certain crate features are enabled +#![allow(unused_macros, unused_imports)] + +macro_rules! gl_enum { + ( + $visibility: vis enum $name: ident + {$( + $variant: ident = gl::$gl_enum: ident, + )+} + ) => { + #[derive(Debug, Clone, Copy)] + #[repr(u32)] + $visibility enum $name + {$( + $variant = gl::$gl_enum, + )+} + + impl $name { + fn from_gl(num: gl::types::GLenum) -> Option + { + match num { + $(gl::$gl_enum => Some(Self::$variant),)+ + _ => None + } + } + } + }; +} + +pub(crate) use gl_enum; -- cgit v1.2.3-18-g5258