summaryrefslogtreecommitdiff
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
parent46f27f31e425f5eba494f499bd7a6ac8f8713c2a (diff)
feat(engine): add logging OpenGL debug messages
-rw-r--r--Cargo.lock38
-rw-r--r--engine/Cargo.toml4
-rw-r--r--engine/src/lib.rs8
-rw-r--r--engine/src/opengl/debug.rs145
-rw-r--r--engine/src/opengl/mod.rs5
-rw-r--r--engine/src/opengl/util.rs30
-rw-r--r--engine/src/renderer/mod.rs49
7 files changed, 278 insertions, 1 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 13b0fcb..461fbd7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -89,6 +89,7 @@ dependencies = [
"gl",
"glfw",
"thiserror",
+ "tracing",
]
[[package]]
@@ -244,6 +245,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
+name = "pin-project-lite"
+version = "0.2.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
+
+[[package]]
name = "prettyplease"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -357,6 +364,37 @@ dependencies = [
]
[[package]]
+name = "tracing"
+version = "0.1.39"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee2ef2af84856a50c1d430afce2fdded0a4ec7eda868db86409b4543df0797f9"
+dependencies = [
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/engine/Cargo.toml b/engine/Cargo.toml
index 40b2e9f..ff52cce 100644
--- a/engine/Cargo.toml
+++ b/engine/Cargo.toml
@@ -3,8 +3,12 @@ name = "engine"
version = "0.1.0"
edition = "2021"
+[features]
+debug = ["dep:tracing"]
+
[dependencies]
glfw = { path = "../glfw" }
thiserror = "1.0.49"
gl = "0.14.0"
bitflags = "2.4.0"
+tracing = { version = "0.1.39", optional = true }
diff --git a/engine/src/lib.rs b/engine/src/lib.rs
index 407df32..dc76b37 100644
--- a/engine/src/lib.rs
+++ b/engine/src/lib.rs
@@ -29,7 +29,13 @@ impl Engine
/// Will return `Err` if window creation or window configuration fails.
pub fn new(window_size: &WindowSize, window_title: &str) -> Result<Self, Error>
{
- let window = WindowBuilder::new()
+ let window_builder = WindowBuilder::new();
+
+ #[cfg(feature = "debug")]
+ let window_builder =
+ window_builder.hint(glfw::window::Hint::OpenGLDebugContext, 1);
+
+ let window = window_builder
.create(window_size, window_title)
.map_err(Error::CreateWindowFailed)?;
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!();
+ }
+}
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<u32>, 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<Self>
+ {
+ match num {
+ $(gl::$gl_enum => Some(Self::$variant),)+
+ _ => None
+ }
+ }
+ }
+ };
+}
+
+pub(crate) use gl_enum;
diff --git a/engine/src/renderer/mod.rs b/engine/src/renderer/mod.rs
index 4ed3c0a..cc1631f 100644
--- a/engine/src/renderer/mod.rs
+++ b/engine/src/renderer/mod.rs
@@ -3,6 +3,8 @@ use std::process::abort;
use glfw::WindowSize;
+#[cfg(feature = "debug")]
+use crate::opengl::debug::{MessageSeverity, MessageSource, MessageType};
use crate::opengl::shader::Program as ShaderProgram;
use crate::opengl::vertex_array::{PrimitiveKind, VertexArray};
use crate::opengl::vertex_buffer::{BufferUsage, VertexBuffer};
@@ -28,6 +30,22 @@ pub fn initialize(window: &glfw::Window) -> Result<(), Error>
}
});
+ #[cfg(feature = "debug")]
+ {
+ use crate::opengl::debug::{
+ enable_debug_output,
+ set_debug_message_callback,
+ set_debug_message_control,
+ MessageIdsAction,
+ };
+
+ enable_debug_output();
+
+ set_debug_message_callback(opengl_debug_message_cb);
+
+ set_debug_message_control(None, None, None, &[], MessageIdsAction::Disable);
+ }
+
let window_size = window.size().map_err(Error::GetWindowSizeFailed)?;
set_viewport(&Vec2 { x: 0, y: 0 }, &window_size);
@@ -35,6 +53,37 @@ pub fn initialize(window: &glfw::Window) -> Result<(), Error>
Ok(())
}
+#[cfg(feature = "debug")]
+#[tracing::instrument(skip_all)]
+fn opengl_debug_message_cb(
+ source: MessageSource,
+ ty: MessageType,
+ id: u32,
+ severity: MessageSeverity,
+ message: &str,
+)
+{
+ use tracing::{event, Level};
+
+ macro_rules! create_event {
+ ($level: expr) => {
+ event!($level, ?source, ?ty, id, ?severity, message);
+ };
+ }
+
+ match ty {
+ MessageType::Error => {
+ create_event!(Level::ERROR);
+ }
+ MessageType::Other => {
+ create_event!(Level::INFO);
+ }
+ _ => {
+ create_event!(Level::WARN);
+ }
+ };
+}
+
pub fn render<'renderable>(renderables: impl IntoIterator<Item = &'renderable Renderable>)
{
clear_buffers(BufferClearMask::COLOR);