diff options
Diffstat (limited to 'opengl-bindings/src/debug.rs')
| -rw-r--r-- | opengl-bindings/src/debug.rs | 161 | 
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!(); +    } +}  | 
