Skip to content

Commit

Permalink
🧑‍💻 Introduced debug output behind flags
Browse files Browse the repository at this point in the history
Moved all debug output, assertion and nasty terminations behind the STAPLEGL_DEBUG flag, so that the library is faster and less annoying in a release build. In addition, debug outputs are improved and now feature file/line information so that users can quickly locate the source of the bugs.
  • Loading branch information
dario-loi committed Dec 23, 2023
1 parent 71ec8a0 commit 5741ba0
Show file tree
Hide file tree
Showing 11 changed files with 194 additions and 114 deletions.
20 changes: 16 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,6 @@ foreach(face IN ITEMS right left top bottom front back)
configure_file(${SKYBOX_DIR}/${face}.jpg ${CMAKE_CURRENT_BINARY_DIR}/assets/skybox/${face}.jpg COPYONLY)
endforeach(face)



set(TEAPOT_SOURCES
${EXAMPLES_DIR}/teapot.cpp
)
Expand Down Expand Up @@ -242,7 +240,21 @@ else()
target_compile_options(teapot PRIVATE -Wall -Wextra -pedantic)
endif()


# only if in debug configuration
if(CMAKE_BUILD_TYPE MATCHES Debug)
# add sanitizers
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|AppleClang|GNU")
target_compile_options(sandbox PRIVATE -fsanitize=address -fno-omit-frame-pointer)
target_compile_options(batches PRIVATE -fsanitize=address -fno-omit-frame-pointer)
target_compile_options(teapot PRIVATE -fsanitize=address -fno-omit-frame-pointer)
target_link_libraries(sandbox -fsanitize=address)
target_link_libraries(batches -fsanitize=address)
target_link_libraries(teapot -fsanitize=address)
endif()

# define staplegl debug macro
target_compile_definitions(sandbox PRIVATE STAPLEGL_DEBUG)
endif()

set(CLANG_TIDY_CHECKS
"misc-*,modernize-*,cppcoreguidelines-*,performance-*,bugprone-*,clang-analyzer-*,-bugprone-easily-swappable-parameters,-bugprone-unchecked-optional-access,-misc-no-recursion,-misc-non-private-*,-performance-no-int-to-ptr,-cppcoreguidelines-avoid-magic-numbers,-cppcoreguidelines-pro-bounds-constant-array-index,-cppcoreguidelines-non-private-*")
Expand All @@ -251,7 +263,7 @@ set(CLANG_TIDY_CHECKS
add_custom_target(clang-tidy-sandbox
COMMAND clang-tidy -header-filter=${STAPLEGL_HEADERS} --fix --fix-errors -checks=${CLANG_TIDY_CHECKS} -p ${CMAKE_BINARY_DIR} ${SANDBOX_SOURCES}
-- -I${SANDBOX_DIR}/include -I${GLAD_INCLUDE_DIR} -I${GLFW3_INCLUDE_DIR}
-I${STAPLEGL_DIR} -I${STAPLEGL_MODULES_DIR}/include -std=c++20 -fexperimental-library
-I${STAPLEGL_DIR} -I${STAPLEGL_MODULES_DIR}/include -std=c++20 -fexperimental-library
COMMENT "Running clang-tidy on sandbox"
VERBATIM
)
Expand Down
9 changes: 5 additions & 4 deletions examples/batches.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@
#include "staplegl.hpp"

#include <GLFW/glfw3.h>
#include <algorithm>
#include <array>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <ctime>
#include <execution>
#include <filesystem>
#include <iostream>
#include <optional>
#include <span>
#include <utility>

Expand Down Expand Up @@ -56,7 +57,7 @@ MessageCallback(GLenum source [[maybe_unused]],
{
if (type == GL_DEBUG_TYPE_OTHER || type == GL_DEBUG_TYPE_PERFORMANCE)
return;
//NOLINTNEXTLINE
// NOLINTNEXTLINE
fprintf(stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x,\nmessage = %s\n", // NOLINT
(type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : ""),
type, severity, message);
Expand Down Expand Up @@ -198,7 +199,7 @@ auto main() -> int
color[3] = 1.0F;

for (int i = 0; i < 4; ++i) {
UBO_block.set_attribute_data(std::span { &color[i], 1 }, "u_color", i);
UBO_block.set_attribute_data(std::span { &color[i], 1 }, "u_color", i);
}

// define a struct for the instance data
Expand Down
1 change: 0 additions & 1 deletion examples/teapot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,6 @@ auto main() -> int

// gen camera vector and make it look at the teapot (center of the screen).
glm::vec4 camera_pos { camX, camY, camZ, 1.0F };
const glm::vec4 camera_target { 0.0F, 0.0F, 0.0F, 1.0F };

// gen the view and projection matrices.
glm::mat4 view = glm::lookAt(glm::vec3(camera_pos), glm::vec3(0.0F, 0.0F, 0.0F),
Expand Down
27 changes: 17 additions & 10 deletions include/modules/framebuffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
* @file framebuffer.hpp
* @author Dario Loi
* @brief Wrapper for OpenGL Framebuffer Objects.
*
*
* @date 2023-09-18
*
*
* @copyright MIT License
*
* @details Wraps FBOs allowing for easy creation and usage. functionalities include
* the internal management of renderbuffers, some utility functions to transfer
*
* @details Wraps FBOs allowing for easy creation and usage. functionalities include
* the internal management of renderbuffers, some utility functions to transfer
* contents from one framebuffer to another, and some functions to bind/unbind the
* FBO to the OpenGL context.
*/
Expand All @@ -27,11 +27,15 @@
#include <optional>
#include <span>

#ifdef STAPLEGL_DEBUG
#include <cstdio>
#endif // STAPLEGL_DEBUG

namespace staplegl {

/**
* @brief enum class for framebuffer attachments.
*
*
*/
enum class fbo_attachment : std::uint8_t {
NONE = 0x00,
Expand All @@ -42,7 +46,7 @@ enum class fbo_attachment : std::uint8_t {

/**
* @brief Framebuffer Object (FBO) wrapper.
*
*
*/
class framebuffer {

Expand Down Expand Up @@ -131,7 +135,7 @@ class framebuffer {

/**
* @brief Transfer the contents of a framebuffer to another.
*
*
* @param src The source framebuffer.
* @param dst The destination framebuffer.
* @param res The resolution of the framebuffer.
Expand All @@ -142,7 +146,7 @@ class framebuffer {
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst.id());

glBlitFramebuffer(0, 0, res.width, res.height, 0, 0, res.width, res.height, GL_COLOR_BUFFER_BIT, GL_NEAREST);

glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

Expand Down Expand Up @@ -242,7 +246,10 @@ inline void framebuffer::set_renderbuffer(resolution res, fbo_attachment attachm
type = renderbuffer::attachment_type::depth_stencil;
break;
default:
std::terminate();
#ifdef STAPLEGL_DEBUG
std::fprintf(stderr, STAPLEGL_LINEINFO "invalid attachment enum %d for renderbuffer\n", static_cast<std::uint32_t>(attachment));
#endif
std::terminate(); // crash and burn, this is unrecoverable
}

m_attachment = attachment; // in some cases this is a redundant operation, fair enough.
Expand Down
13 changes: 10 additions & 3 deletions include/modules/gl_functions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,30 @@
* @date 2023-04-28
*
* @details This file is a placeholder for the OpenGL function loader. You can use any loader you
* want, as long as it is compatible with the OpenGL version you are using.
* want, as long as it is compatible with the OpenGL version you are using.
* <br>
* To switch to a different loader, simply replace the include directive with the one you want to
* use. For example, if you want to use GLEW, you would replace the include directive with:
* <br>
* <code>#include "GL/glew.h"</code>
* <br>
*
*
* For the examples, we are using glad.h, if you want to see the examples with a different
* loader, you'll have to modify their loading code accordingly.
*
*
* @copyright MIT License
*
*/

#pragma once

// very ungentelmanly use of C macros for debugging, close your eyes it'll be over soon
#ifdef STAPLEGL_DEBUG
#define STAPLEGL_STRINGIFY_DETAIL(x) #x
#define STAPLEGL_STR(x) STAPLEGL_STRINGIFY_DETAIL(x)
#define STAPLEGL_LINEINFO __FILE__ ":" STAPLEGL_STR(__LINE__)
#endif // STAPLEGL_DEBUG

/* REPLACE THIS INCLUDE DIRECTIVE WITH THE OPENGL FUNCTION LOADER YOU ARE CURRENTLY USING */
#include "glad.h"
/*----------------------------------------------------------------------------------------*/
100 changes: 69 additions & 31 deletions include/modules/shader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ enum class shader_type {
geometry
};

std::string shader_type_to_string(shader_type type) noexcept;

/**
* @brief Individual shader struct.
*
Expand Down Expand Up @@ -204,7 +206,7 @@ class shader_program {
* @brief Upload a 3x3 float matrix uniform to the shader program.
*
* @param name Uniform name.
* @param mat Contiguous span of 16 floats representing the matrix.
* @param mat Contiguous span of 9 9 9 9 9 9 9 9 9 floats representing the matrix.
*/
void upload_uniform_mat3f(std::string_view name, std::span<float, 9> mat) const;

Expand Down Expand Up @@ -271,6 +273,15 @@ class shader_program {
*/
[[nodiscard]] auto parse_shaders(std::string_view source) const -> std::vector<shader>;

/**
* @brief Check if a shader program is valid.
*
* @param id Shader program id.
* @return true, if the shader program is valid.
* @return false, if the shader program is not valid.
*/
[[nodiscard]] static auto is_valid(std::uint32_t id) -> bool;

private:
/**
* @brief Obtain the location of a uniform in the shader program.
Expand All @@ -280,15 +291,6 @@ class shader_program {
*/
[[nodiscard]] auto uniform_location(std::string_view name) const -> int;

/**
* @brief Check if a shader program is valid.
*
* @param id Shader program id.
* @return true, if the shader program is valid.
* @return false, if the shader program is not valid.
*/
[[nodiscard]] auto is_valid(std::uint32_t id) const -> bool;

private:
/**
* @brief Convert a shader type to its OpenGL equivalent.
Expand Down Expand Up @@ -430,13 +432,14 @@ inline auto shader_program::create_program() const -> std::uint32_t
glGetProgramiv(program, GL_LINK_STATUS, &link_success);

if (!link_success) [[unlikely]] {
#ifdef STAPLEGL_DEBUG
int max_length {};
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &max_length);
std::vector<char> error_log(max_length);
glGetProgramInfoLog(program, max_length, &max_length, &error_log[0]);
std::fwrite("Failed to link shader program: ", 1, 32, stdout);
std::fwrite(error_log.data(), error_log.size(), 1, stdout);
std::fwrite("\n", 1, 1, stdout);
std::fprintf(stderr, STAPLEGL_LINEINFO ", failed to link shader program: %s\n",
error_log.data());
#endif // STAPLEGL_DEBUG
glDeleteProgram(program);
return 0;
}
Expand All @@ -446,40 +449,48 @@ inline auto shader_program::create_program() const -> std::uint32_t
int success = 0;
glGetProgramiv(program, GL_VALIDATE_STATUS, &success);
if (!success) [[unlikely]] {
#ifdef STAPLEGL_DEBUG
int max_length {};
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &max_length);
std::vector<char> error_log(max_length);
std::string error_log(max_length, '\0');
glGetProgramInfoLog(program, max_length, &max_length, &error_log[0]);
std::fwrite("failed to validate shader program: ", 1, 36, stdout);
std::fwrite(error_log.data(), error_log.size(), 1, stdout);
std::fwrite("\n", 1, 1, stdout);
std::fprintf(stderr, STAPLEGL_LINEINFO ", failed to validate shader program: %s\n",
error_log.data());
#endif // STAPLEGL_DEBUG
glDeleteProgram(program);
return 0;
}

// Detach and delete shaders after linking the program.
for (const auto& id : shader_ids)
for (const auto& id : shader_ids) {
glDetachShader(program, id);
for (const auto& id : shader_ids)
glDeleteShader(id);
}

return program;
}

inline auto shader_program::compile(shader_type shader_type, std::string_view source) const -> std::uint32_t
{
const std::uint32_t id { glCreateShader(to_gl_type(shader_type)) };

// taking a pointer to this temporary allows us to get its address, without this intermediate
// variable we are forced to use the address of the temporary, which is not allowed.
// if anyone knows a better way to do this, please let me know.
const char* src { source.data() };

glShaderSource(id, 1, &src, nullptr);
glCompileShader(id);
bool const is_compiled { is_valid(id) };

if (!is_compiled) [[unlikely]] {
std::fwrite("Failed to compile shader: ", 1, 26, stdout);
std::fwrite(source.data(), source.size(), 1, stdout);
std::fwrite("\n", 1, 1, stdout);
#ifdef STAPLEGL_DEBUG
std::string shader_type_str { shader_type_to_string(shader_type) };
std::fprintf(stderr, STAPLEGL_LINEINFO ", failed to compile %s shader: \n%s\n",
shader_type_str.data(), source.data());
#endif // STAPLEGL_DEBUG
glDeleteShader(id);

return 0;
}

Expand All @@ -502,10 +513,11 @@ inline auto shader_program::parse_shaders(std::string_view source) const -> std:

// With C++23 we could drastically simplify this thanks to std::optional's monadic operations.
if (!shader_type.has_value()) [[unlikely]] {
std::fwrite("Invalid shader type: ", 1, 21, stderr);
std::fwrite(type.data(), type.size(), 1, stderr);
std::fwrite("\n", 1, 1, stderr);
std::terminate();
#ifdef STAPLEGL_DEBUG
std::fprintf(stderr, STAPLEGL_LINEINFO ", invalid shader type \"%s\"\n",
type.data());
#endif // STAPLEGL_DEBUG
return {};
}

pos = source.find(type_token, next_line_pos);
Expand All @@ -531,19 +543,23 @@ inline auto shader_program::uniform_location(std::string_view name) const -> int
}
}

inline auto shader_program::is_valid(std::uint32_t id) const -> bool
inline auto shader_program::is_valid(std::uint32_t id) -> bool
{
int is_compiled {};
glGetShaderiv(id, GL_COMPILE_STATUS, &is_compiled);

if (is_compiled == GL_FALSE) [[unlikely]] {
#ifdef STAPLEGL_DEBUG
int max_length {};
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &max_length);
std::vector<char> error_log(max_length);
std::string error_log(max_length, '\0');
glGetShaderInfoLog(id, max_length, &max_length, &error_log[0]);
std::fwrite("Failed to compile shader: ", 1, 26, stdout);
std::fwrite(error_log.data(), error_log.size(), 1, stdout);
std::fwrite("\n", 1, 1, stdout);
int32_t type_id {};
glGetShaderiv(id, GL_SHADER_TYPE, &type_id);
std::string shader_type_str = shader_type_to_string(static_cast<shader_type>(type_id));
std::fprintf(stderr, STAPLEGL_LINEINFO ", failed to compile %s shader: \n%s\n",
shader_type_str.data(), error_log.data());
#endif // STAPLEGL_DEBUG
glDeleteShader(id);
return false;
}
Expand All @@ -565,6 +581,10 @@ inline constexpr auto shader_program::to_gl_type(shader_type shader_type) -> std
case shader_type::geometry:
return GL_GEOMETRY_SHADER;
default:
#ifdef STAPLEGL_DEBUG
std::fprintf(stderr, STAPLEGL_LINEINFO ", invalid shader type enum %d, \n",
static_cast<int>(shader_type));
#endif // STAPLEGL_DEBUG
std::terminate();
}
}
Expand All @@ -585,4 +605,22 @@ inline auto shader_program::string_to_shader_type(std::string_view str) -> std::

return std::nullopt;
}
}

std::string staplegl::shader_type_to_string(shader_type type) noexcept
{
switch (type) {
case shader_type::vertex:
return "vertex";
case shader_type::fragment:
return "fragment";
case shader_type::tess_control:
return "tess_control";
case shader_type::tess_eval:
return "tess_eval";
case shader_type::geometry:
return "geometry";
default:
return "unknown";
}
}
Loading

0 comments on commit 5741ba0

Please sign in to comment.