libs/render/src/render/opengl_utils.cc
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include "render/opengl_utils.h" | ||
| 2 | |||
| 3 | #include "assert/assert.h" | ||
| 4 | // #include "klotter/str.h" | ||
| 5 | #include "dependency_glad.h" | ||
| 6 | |||
| 7 | |||
| 8 | namespace eu::render | ||
| 9 | { | ||
| 10 | |||
| 11 | #if FF_HAS(ENABLE_GL_DEBUG) | ||
| 12 | ✗ | const char* string_from_opengl_error_enum(GLenum error_code) | |
| 13 | { | ||
| 14 | ✗ | switch (error_code) | |
| 15 | { | ||
| 16 | ✗ | case GL_INVALID_ENUM: return "INVALID_ENUM"; | |
| 17 | ✗ | case GL_INVALID_VALUE: return "INVALID_VALUE"; | |
| 18 | ✗ | case GL_INVALID_OPERATION: return "INVALID_OPERATION"; | |
| 19 | #ifdef GL_STACK_OVERFLOW | ||
| 20 | ✗ | case GL_STACK_OVERFLOW: return "STACK_OVERFLOW"; | |
| 21 | #endif | ||
| 22 | #ifdef GL_STACK_UNDERFLOW | ||
| 23 | ✗ | case GL_STACK_UNDERFLOW: return "STACK_UNDERFLOW"; | |
| 24 | #endif | ||
| 25 | ✗ | case GL_OUT_OF_MEMORY: return "OUT_OF_MEMORY"; | |
| 26 | ✗ | case GL_INVALID_FRAMEBUFFER_OPERATION: return "INVALID_FRAMEBUFFER_OPERATION"; | |
| 27 | ✗ | default: return "UNKNOWN"; | |
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | namespace | ||
| 32 | { | ||
| 33 | ✗ | const char* string_from_debug_source(GLenum source) | |
| 34 | { | ||
| 35 | ✗ | switch (source) | |
| 36 | { | ||
| 37 | ✗ | case GL_DEBUG_SOURCE_API: return "API"; | |
| 38 | ✗ | case GL_DEBUG_SOURCE_WINDOW_SYSTEM: return "Window System"; | |
| 39 | ✗ | case GL_DEBUG_SOURCE_SHADER_COMPILER: return "Shader Compiler"; | |
| 40 | ✗ | case GL_DEBUG_SOURCE_THIRD_PARTY: return "Third Party"; | |
| 41 | ✗ | case GL_DEBUG_SOURCE_APPLICATION: return "Application"; | |
| 42 | ✗ | case GL_DEBUG_SOURCE_OTHER: return "Other"; | |
| 43 | ✗ | default: return "Unknown"; | |
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | ✗ | const char* string_from_debug_type(GLenum type) | |
| 48 | { | ||
| 49 | ✗ | switch (type) | |
| 50 | { | ||
| 51 | ✗ | case GL_DEBUG_TYPE_ERROR: return "Error"; | |
| 52 | ✗ | case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: return "Deprecated Behaviour"; | |
| 53 | ✗ | case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: return "Undefined Behaviour"; | |
| 54 | ✗ | case GL_DEBUG_TYPE_PORTABILITY: return "Portability"; | |
| 55 | ✗ | case GL_DEBUG_TYPE_PERFORMANCE: return "Performance"; | |
| 56 | ✗ | case GL_DEBUG_TYPE_OTHER: return "Other"; | |
| 57 | ✗ | default: return "Unknown"; | |
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | ✗ | const char* string_from_debug_severity(GLenum severity) | |
| 62 | { | ||
| 63 | ✗ | switch (severity) | |
| 64 | { | ||
| 65 | ✗ | case GL_DEBUG_SEVERITY_HIGH: return "High"; | |
| 66 | ✗ | case GL_DEBUG_SEVERITY_MEDIUM: return "Medium"; | |
| 67 | ✗ | case GL_DEBUG_SEVERITY_LOW: return "Low"; | |
| 68 | ✗ | case GL_DEBUG_SEVERITY_NOTIFICATION: return "Notification"; | |
| 69 | ✗ | default: return "Unknown"; | |
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | } // namespace | ||
| 74 | |||
| 75 | ✗ | void APIENTRY on_opengl_debug_output( | |
| 76 | GLenum source, | ||
| 77 | GLenum type, | ||
| 78 | GLuint id, | ||
| 79 | GLenum severity, | ||
| 80 | GLsizei /*length*/, | ||
| 81 | const GLchar* message, | ||
| 82 | const GLvoid* /*userParam*/ | ||
| 83 | ) | ||
| 84 | { | ||
| 85 | ✗ | switch (type) | |
| 86 | { | ||
| 87 | // this is from ScopedDebugGroup | ||
| 88 | ✗ | case GL_DEBUG_TYPE_PUSH_GROUP: | |
| 89 | ✗ | case GL_DEBUG_TYPE_POP_GROUP: return; | |
| 90 | ✗ | default: break; | |
| 91 | } | ||
| 92 | |||
| 93 | // ignore non-significant error/warning codes | ||
| 94 | ✗ | const auto is_important = type != GL_DEBUG_TYPE_OTHER; | |
| 95 | ✗ | const bool is_low = severity == GL_DEBUG_SEVERITY_LOW || severity == GL_DEBUG_SEVERITY_NOTIFICATION; | |
| 96 | |||
| 97 | ✗ | if (is_important == false && is_low) | |
| 98 | { | ||
| 99 | /* | ||
| 100 | Tries to hide the following notification: | ||
| 101 | Buffer object 40 (bound to GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB (0), | ||
| 102 | GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB (1), GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING_ARB (2), | ||
| 103 | and GL_ARRAY_BUFFER_ARB, usage hint is GL_STREAM_DRAW) will use VIDEO memory as the source | ||
| 104 | for buffer object operations. | ||
| 105 | Buffer object 41 (bound to GL_ELEMENT_ARRAY_BUFFER_ARB, usage hint is | ||
| 106 | GL_STREAM_DRAW) will use VIDEO memory as the source for buffer object operations. | ||
| 107 | */ | ||
| 108 | ✗ | return; | |
| 109 | } | ||
| 110 | |||
| 111 | // only display the first 10 notifications | ||
| 112 | static int error_count = 0; | ||
| 113 | ✗ | if (is_important == false) | |
| 114 | { | ||
| 115 | ✗ | if (error_count > 10) | |
| 116 | { | ||
| 117 | ✗ | return; | |
| 118 | } | ||
| 119 | ✗ | ++error_count; | |
| 120 | } | ||
| 121 | |||
| 122 | ✗ | const std::string to_out = fmt::format("OpenGL #{} [src: {} | type: {} | sev: {}]: ", id, string_from_debug_source(source), string_from_debug_type(type), string_from_debug_severity(severity), message); | |
| 123 | |||
| 124 | ✗ | if (is_low) | |
| 125 | { | ||
| 126 | ✗ | LOG_INFOS(to_out); | |
| 127 | } | ||
| 128 | else | ||
| 129 | { | ||
| 130 | ✗ | LOG_ERRS(to_out.c_str()); | |
| 131 | } | ||
| 132 | |||
| 133 | ✗ | if (is_important) | |
| 134 | { | ||
| 135 | ✗ | DIE("OpenGL error"); | |
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 139 | ✗ | bool has_khr_debug() | |
| 140 | { | ||
| 141 | ✗ | return GLAD_GL_KHR_debug != 0; | |
| 142 | } | ||
| 143 | #endif | ||
| 144 | |||
| 145 | ✗ | void setup_opengl_debug() | |
| 146 | { | ||
| 147 | #if FF_HAS(ENABLE_GL_DEBUG) | ||
| 148 | ✗ | if (has_khr_debug()) | |
| 149 | { | ||
| 150 | ✗ | LOG_INFO("Enabling OpenGL debug output (KHR_debug)"); | |
| 151 | ✗ | glEnable(GL_DEBUG_OUTPUT); | |
| 152 | ✗ | glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR); | |
| 153 | ✗ | glDebugMessageCallback(on_opengl_debug_output, nullptr); | |
| 154 | ✗ | glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, nullptr, GL_TRUE); | |
| 155 | } | ||
| 156 | else | ||
| 157 | { | ||
| 158 | ✗ | LOG_ERR("KHR_debug not available, OpenGL debug output not enabled"); | |
| 159 | } | ||
| 160 | #endif | ||
| 161 | ✗ | } | |
| 162 | |||
| 163 | ✗ | GLenum glenum_from_int(int i) | |
| 164 | { | ||
| 165 | ✗ | return static_cast<GLenum>(i); | |
| 166 | } | ||
| 167 | |||
| 168 | ✗ | GLuint gluint_from_int(int i) | |
| 169 | { | ||
| 170 | ✗ | return static_cast<GLuint>(i); | |
| 171 | } | ||
| 172 | |||
| 173 | ✗ | GLsizeiptr glsizeiptr_from_sizet(std::size_t t) | |
| 174 | { | ||
| 175 | ✗ | return static_cast<GLsizeiptr>(t); | |
| 176 | } | ||
| 177 | |||
| 178 | ✗ | GLsizei glsizei_from_sizet(std::size_t t) | |
| 179 | { | ||
| 180 | ✗ | return static_cast<GLsizei>(t); | |
| 181 | } | ||
| 182 | |||
| 183 | ✗ | u32 create_buffer() | |
| 184 | { | ||
| 185 | ✗ | u32 vbo = 0; | |
| 186 | ✗ | glGenBuffers(1, &vbo); | |
| 187 | ✗ | return vbo; | |
| 188 | } | ||
| 189 | |||
| 190 | ✗ | void destroy_buffer(u32 vbo) | |
| 191 | { | ||
| 192 | ✗ | glDeleteBuffers(1, &vbo); | |
| 193 | ✗ | } | |
| 194 | |||
| 195 | ✗ | u32 create_vertex_array() | |
| 196 | { | ||
| 197 | ✗ | u32 vao = 0; | |
| 198 | ✗ | glGenVertexArrays(1, &vao); | |
| 199 | ✗ | return vao; | |
| 200 | } | ||
| 201 | |||
| 202 | ✗ | void destroy_vertex_array(u32 vao) | |
| 203 | { | ||
| 204 | ✗ | glDeleteVertexArrays(1, &vao); | |
| 205 | ✗ | } | |
| 206 | |||
| 207 | ✗ | void set_gl_viewport(const Size& sz) | |
| 208 | { | ||
| 209 | ✗ | glViewport(0, 0, sz.width, sz.height); | |
| 210 | ✗ | } | |
| 211 | |||
| 212 | #if FF_HAS(ENABLE_GL_DEBUG) | ||
| 213 | ✗ | GLenum glenum_from_object_type(DebugLabelFor type) | |
| 214 | { | ||
| 215 | ✗ | switch (type) | |
| 216 | { | ||
| 217 | ✗ | case DebugLabelFor::Buffer: return GL_BUFFER; | |
| 218 | ✗ | case DebugLabelFor::Shader: return GL_SHADER; | |
| 219 | ✗ | case DebugLabelFor::Program: return GL_PROGRAM; | |
| 220 | ✗ | case DebugLabelFor::VertexArray: return GL_VERTEX_ARRAY; | |
| 221 | ✗ | case DebugLabelFor::Query: return GL_QUERY; | |
| 222 | ✗ | case DebugLabelFor::ProgramPipeline: return GL_PROGRAM_PIPELINE; | |
| 223 | ✗ | case DebugLabelFor::Sampler: return GL_SAMPLER; | |
| 224 | ✗ | case DebugLabelFor::Texture: return GL_TEXTURE; | |
| 225 | ✗ | case DebugLabelFor::RenderBuffer: return GL_RENDERBUFFER; | |
| 226 | ✗ | case DebugLabelFor::FrameBuffer: return GL_FRAMEBUFFER; | |
| 227 | ✗ | default: return GL_NONE; | |
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | ✗ | void set_gl_debug_label_with_size(DebugLabelFor type, GLuint object, std::size_t size, const char* label) | |
| 232 | { | ||
| 233 | ✗ | if (! has_khr_debug()) | |
| 234 | { | ||
| 235 | ✗ | return; | |
| 236 | } | ||
| 237 | |||
| 238 | ✗ | const auto gl_type = glenum_from_object_type(type); | |
| 239 | ✗ | if (gl_type == GL_NONE) | |
| 240 | { | ||
| 241 | ✗ | return; | |
| 242 | } | ||
| 243 | |||
| 244 | ✗ | glObjectLabel(gl_type, object, glsizei_from_sizet(size), label); | |
| 245 | } | ||
| 246 | |||
| 247 | ✗ | void set_gl_debug_label(DebugLabelFor type, GLuint object, const std::string& label) | |
| 248 | { | ||
| 249 | ✗ | set_gl_debug_label_with_size(type, object, label.size(), label.data()); | |
| 250 | ✗ | } | |
| 251 | |||
| 252 | ✗ | void set_gl_debug_label(DebugLabelFor type, GLuint object, std::string_view label) | |
| 253 | { | ||
| 254 | ✗ | set_gl_debug_label_with_size(type, object, label.size(), label.data()); | |
| 255 | ✗ | } | |
| 256 | |||
| 257 | ✗ | void push_debug_group(unsigned int id, std::size_t size, const char* label) | |
| 258 | { | ||
| 259 | ✗ | glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, id, glsizei_from_sizet(size), label); | |
| 260 | ✗ | } | |
| 261 | |||
| 262 | ✗ | ScopedDebugGroup::ScopedDebugGroup(const std::string& message, unsigned int id) | |
| 263 | { | ||
| 264 | ✗ | push_debug_group(id, message.size(), message.data()); | |
| 265 | ✗ | } | |
| 266 | |||
| 267 | ✗ | ScopedDebugGroup::ScopedDebugGroup(std::string_view message, unsigned int id) | |
| 268 | { | ||
| 269 | ✗ | push_debug_group(id, message.size(), message.data()); | |
| 270 | ✗ | } | |
| 271 | |||
| 272 | ✗ | ScopedDebugGroup::~ScopedDebugGroup() | |
| 273 | { | ||
| 274 | ✗ | glPopDebugGroup(); | |
| 275 | ✗ | } | |
| 276 | #endif | ||
| 277 | |||
| 278 | } | ||
| 279 |