summaryrefslogtreecommitdiff
path: root/engine/src/opengl/debug.rs
blob: 203590aa1c61d21a6ca5d220071201506388c0c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
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!();
    }
}