summaryrefslogtreecommitdiff
path: root/engine/src/opengl/debug.rs
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2023-10-15 00:06:07 +0200
committerHampusM <hampus@hampusmat.com>2023-10-15 00:06:07 +0200
commitda329909f93597970afd169cee28ece3bee7127b (patch)
treeb8ce428d458ab5e3e699334877253b10a62c8fe2 /engine/src/opengl/debug.rs
parent46f27f31e425f5eba494f499bd7a6ac8f8713c2a (diff)
feat(engine): add logging OpenGL debug messages
Diffstat (limited to 'engine/src/opengl/debug.rs')
-rw-r--r--engine/src/opengl/debug.rs145
1 files changed, 145 insertions, 0 deletions
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<MessageSource>,
+ ty: Option<MessageType>,
+ severity: Option<MessageSeverity>,
+ 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<Option<MessageCallback>> = 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!();
+ }
+}