summaryrefslogtreecommitdiff
path: root/engine/src
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src')
-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
5 files changed, 236 insertions, 1 deletions
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);