summaryrefslogtreecommitdiff
path: root/opengl-bindings/src/debug.rs
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2025-09-19 16:36:57 +0200
committerHampusM <hampus@hampusmat.com>2025-10-02 16:55:33 +0200
commitea1d70c8c28e3b96da6264021fa1c62e28fcd8e4 (patch)
tree62ae9b75ee84602899b51483ed26fa664df36888 /opengl-bindings/src/debug.rs
parent0008b374c7f3a9ef6b30ea31a4a8c98bce64649f (diff)
feat: add OpenGL bindings crate
Diffstat (limited to 'opengl-bindings/src/debug.rs')
-rw-r--r--opengl-bindings/src/debug.rs161
1 files changed, 161 insertions, 0 deletions
diff --git a/opengl-bindings/src/debug.rs b/opengl-bindings/src/debug.rs
new file mode 100644
index 0000000..a9369a4
--- /dev/null
+++ b/opengl-bindings/src/debug.rs
@@ -0,0 +1,161 @@
+use std::ffi::c_void;
+use std::io::{stderr, Write};
+use std::mem::transmute;
+use std::panic::catch_unwind;
+
+use util_macros::FromRepr;
+
+use crate::CurrentContextWithFns;
+
+pub fn set_debug_message_callback(
+ current_context: &CurrentContextWithFns<'_>,
+ cb: MessageCallback,
+)
+{
+ unsafe {
+ current_context
+ .fns()
+ .DebugMessageCallback(Some(debug_message_cb), cb as *mut c_void);
+ }
+}
+
+/// Sets debug message parameters.
+///
+/// # Errors
+/// Returns `Err` if `ids` contains too many ids.
+pub fn set_debug_message_control(
+ current_context: &CurrentContextWithFns<'_>,
+ source: Option<MessageSource>,
+ ty: Option<MessageType>,
+ severity: Option<MessageSeverity>,
+ ids: &[u32],
+ ids_action: MessageIdsAction,
+) -> Result<(), SetDebugMessageControlError>
+{
+ let ids_len: crate::sys::types::GLsizei =
+ ids.len()
+ .try_into()
+ .map_err(|_| SetDebugMessageControlError::TooManyIds {
+ id_cnt: ids.len(),
+ max_id_cnt: crate::sys::types::GLsizei::MAX as usize,
+ })?;
+
+ unsafe {
+ current_context.fns().DebugMessageControl(
+ source.map_or(crate::sys::DONT_CARE, |source| source as u32),
+ ty.map_or(crate::sys::DONT_CARE, |ty| ty as u32),
+ severity.map_or(crate::sys::DONT_CARE, |severity| severity as u32),
+ ids_len,
+ ids.as_ptr(),
+ ids_action as u8,
+ );
+ }
+
+ Ok(())
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum SetDebugMessageControlError
+{
+ #[error("Too many ids provided ({id_cnt}). Must be < {max_id_cnt}")]
+ TooManyIds
+ {
+ id_cnt: usize, max_id_cnt: usize
+ },
+}
+
+pub type MessageCallback = fn(
+ source: MessageSource,
+ ty: MessageType,
+ id: u32,
+ severity: MessageSeverity,
+ message: &str,
+);
+
+#[derive(Debug, Clone, Copy)]
+#[repr(u8)] // GLboolean = u8
+pub enum MessageIdsAction
+{
+ Enable = crate::sys::TRUE,
+ Disable = crate::sys::FALSE,
+}
+
+#[derive(Debug, Clone, Copy, FromRepr)]
+#[repr(u32)] // GLenum = u32
+pub enum MessageSource
+{
+ Api = crate::sys::DEBUG_SOURCE_API,
+ WindowSystem = crate::sys::DEBUG_SOURCE_WINDOW_SYSTEM,
+ ShaderCompiler = crate::sys::DEBUG_SOURCE_SHADER_COMPILER,
+ ThirdParty = crate::sys::DEBUG_SOURCE_THIRD_PARTY,
+ Application = crate::sys::DEBUG_SOURCE_APPLICATION,
+ Other = crate::sys::DEBUG_SOURCE_OTHER,
+}
+
+#[derive(Debug, Clone, Copy, FromRepr)]
+#[repr(u32)] // GLenum = u32
+pub enum MessageType
+{
+ DeprecatedBehavior = crate::sys::DEBUG_TYPE_DEPRECATED_BEHAVIOR,
+ Error = crate::sys::DEBUG_TYPE_ERROR,
+ Marker = crate::sys::DEBUG_TYPE_MARKER,
+ Other = crate::sys::DEBUG_TYPE_OTHER,
+ Performance = crate::sys::DEBUG_TYPE_PERFORMANCE,
+ PopGroup = crate::sys::DEBUG_TYPE_POP_GROUP,
+ PushGroup = crate::sys::DEBUG_TYPE_PUSH_GROUP,
+ Portability = crate::sys::DEBUG_TYPE_PORTABILITY,
+ UndefinedBehavior = crate::sys::DEBUG_TYPE_UNDEFINED_BEHAVIOR,
+}
+
+#[derive(Debug, Clone, Copy, FromRepr)]
+#[repr(u32)] // GLenum = u32
+pub enum MessageSeverity
+{
+ High = crate::sys::DEBUG_SEVERITY_HIGH,
+ Medium = crate::sys::DEBUG_SEVERITY_MEDIUM,
+ Low = crate::sys::DEBUG_SEVERITY_LOW,
+ Notification = crate::sys::DEBUG_SEVERITY_NOTIFICATION,
+}
+
+extern "system" fn debug_message_cb(
+ source: crate::sys::types::GLenum,
+ ty: crate::sys::types::GLenum,
+ id: crate::sys::types::GLuint,
+ severity: crate::sys::types::GLenum,
+ message_length: crate::sys::types::GLsizei,
+ message: *const crate::sys::types::GLchar,
+ user_cb: *mut c_void,
+)
+{
+ let user_cb = unsafe { transmute::<*mut c_void, MessageCallback>(user_cb) };
+
+ let Ok(msg_length) = usize::try_from(message_length) else {
+ return;
+ };
+
+ // Unwinds are catched because unwinding from Rust code into foreign code is UB.
+ let res = catch_unwind(|| {
+ let msg_source = MessageSource::from_repr(source).unwrap();
+ let msg_type = MessageType::from_repr(ty).unwrap();
+ let msg_severity = MessageSeverity::from_repr(severity).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,
+ ))
+ };
+
+ user_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!();
+ }
+}