Brenta Engine 1.2
Loading...
Searching...
No Matches
gl.cpp
1// SPDX-License-Identifier: MIT
2// Author: Giovanni Santini
3// Mail: giovanni.santini@proton.me
4// Github: @San7o
5
6#include <brenta/renderer/opengl/gl.hpp>
7#include <brenta/logger.hpp>
8#include <brenta/window.hpp>
9
10#include <sstream>
11
12using namespace brenta;
13
14//
15// Static variables
16//
17
18const std::string Gl::subsystem_name = "gl";
19Gl::Config Gl::init_config = {};
20bool Gl::initialized = false;
21const GLboolean Gl::True = GL_TRUE;
22const GLboolean Gl::False = GL_FALSE;
23
24// Forward declaration
25void APIENTRY glDebugOutput([[maybe_unused]] GLenum source,
26 [[maybe_unused]] GLenum type,
27 [[maybe_unused]] GLuint id,
28 [[maybe_unused]] GLenum severity,
29 [[maybe_unused]] GLsizei length,
30 [[maybe_unused]] const GLchar *message,
31 [[maybe_unused]] const void *userParam);
32
33//
34// Subsystem interface
35//
36
37std::expected<void, Subsystem::Error> Gl::initialize()
38{
39 if (this->is_initialized()) return {};
40
41 GLADloadproc loadproc = (GLADloadproc) Window::get_proc_address();
42 if (!gladLoadGLLoader(loadproc))
43 {
44 ERROR("{}: failed to initialize GLAD", Gl::subsystem_name);
45 return std::unexpected("Failed to initialize GLAD");
46 }
47
48 int width = Window::get_width();
49 int height = Window::get_height();
50
51 glViewport(0, 0, width, height);
52
53 if (Gl::init_config.enable_depth_test)
54 {
55 glEnable(GL_DEPTH_TEST);
56 INFO("{}: enabled GL_DEPTH_TEST", Gl::subsystem_name);
57 }
58
59 if (Gl::init_config.enable_blending)
60 {
61 glEnable(GL_BLEND);
62 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
63 INFO("{}: enabled GL_BLEND (transparency)", Gl::subsystem_name);
64 }
65
66 if (Gl::init_config.enable_backface_culling)
67 {
68 glEnable(GL_CULL_FACE);
69 INFO("{}: enabled GL_CULL_FACE", Gl::subsystem_name);
70 }
71
72 if (Gl::init_config.enable_multisample)
73 {
74 glEnable(GL_MULTISAMPLE);
75 INFO("{}: enabled GL_MULTISAMPLE", Gl::subsystem_name);
76 }
77
78 int flags;
79 glGetIntegerv(GL_CONTEXT_FLAGS, &flags);
80 if (flags & GL_CONTEXT_FLAG_DEBUG_BIT)
81 {
82 // Enable all messages from all sources, of all types, of all severities.
83 glEnable(GL_DEBUG_OUTPUT);
84 glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
85 glDebugMessageCallback(glDebugOutput, nullptr);
86 glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE,
87 0, nullptr, GL_TRUE);
88 INFO("{}: configured GL_DEBUG_OUTPUT", Gl::subsystem_name);
89 }
90
91 GLenum errcode = Gl::check_error();
92 if (errcode != GL_NO_ERROR)
93 {
94 return std::unexpected("GL error");
95 }
96
97 Gl::initialized = true;
98 INFO("{}: initialized", Gl::subsystem_name);
99 return {};
100}
101
102std::expected<void, Subsystem::Error> Gl::terminate()
103{
104 if (!this->is_initialized()) return {};
105
106 Gl::initialized = false;
107 INFO("{}: terminated", Gl::subsystem_name);
108 return {};
109}
110
111std::string Gl::name()
112{
113 return Gl::subsystem_name;
114}
115
116bool Gl::is_initialized()
117{
118 return Gl::initialized;
119}
120
121//
122// Member functions
123//
124
125void Gl::set_poligon_mode(GLboolean enable)
126{
127 if (enable)
128 {
129 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
130 INFO("{}: enabled GL_POLYGON_MODE (wireframe)", Gl::subsystem_name);
131 }
132 else
133 {
134 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
135 INFO("{}: disabled GL_POLYGON_MODE (fill)", Gl::subsystem_name);
136 }
137 return;
138}
139
140Gl &Gl::instance()
141{
142 static Gl _gl;
143 return _gl;
144}
145
146int Gl::get_num_channels(GLenum color_format)
147{
148 switch (color_format)
149 {
150 case GL_RED:
151 case GL_RED_INTEGER:
152 case GL_STENCIL_INDEX:
153 case GL_DEPTH_COMPONENT:
154 return 1;
155
156 case GL_RG:
157 case GL_RG_INTEGER:
158 case GL_DEPTH_STENCIL: // Depth + Stencil combined
159 return 2;
160
161 case GL_RGB:
162 case GL_BGR:
163 case GL_RGB_INTEGER:
164 case GL_BGR_INTEGER:
165 return 3;
166
167 case GL_RGBA:
168 case GL_BGRA:
169 case GL_RGBA_INTEGER:
170 case GL_BGRA_INTEGER:
171 return 4;
172
173 default:
174 return 0;
175 }
176}
177
178int Gl::get_bytes_per_channel(GLenum type)
179{
180 switch (type)
181 {
182 case GL_UNSIGNED_BYTE:
183 case GL_BYTE:
184 return 1;
185
186 case GL_UNSIGNED_SHORT:
187 case GL_SHORT:
188 case GL_HALF_FLOAT: // 16-bit floating point
189 return 2;
190
191 case GL_UNSIGNED_INT:
192 case GL_INT:
193 case GL_FLOAT:
194 return 4;
195
196 case GL_DOUBLE:
197 return 8;
198
199 case GL_UNSIGNED_INT_2_10_10_10_REV:
200 case GL_UNSIGNED_INT_10F_11F_11F_REV:
201 case GL_UNSIGNED_INT_24_8:
202 return 4; // Total bytes for all channels combined
203
204 default:
205 return 0;
206 }
207}
208
209void Gl::set_viewport(int x, int y, int width, int height)
210{
211 glViewport(x, y, width, height);
212 return;
213}
214
215void Gl::set_color(const Color &color)
216{
217 glClearColor(color.r, color.g, color.b, color.a);
218 return;
219}
220
221void Gl::draw_arrays(GLenum mode, int first, int count)
222{
223 glDrawArrays(mode, first, count);
224 return;
225}
226
227void Gl::draw_elements(GLenum mode, int count, GLenum type, const void *indices)
228{
229 glDrawElements(mode, count, type, indices);
230 return;
231}
232
233void Gl::clear()
234{
235 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
236 return;
237}
238
239GLenum Gl::_check_error(const char *file, int line)
240{
241 GLenum errorCode;
242 while ((errorCode = glGetError()) != GL_NO_ERROR)
243 {
244 std::string error = "";
245 switch (errorCode)
246 {
247 case GL_INVALID_ENUM: error = "invalid enum"; break;
248 case GL_INVALID_VALUE: error = "invalid value"; break;
249 case GL_INVALID_OPERATION: error = "invalid operation"; break;
250 case GL_STACK_OVERFLOW: error = "stack overflow"; break;
251 case GL_STACK_UNDERFLOW: error = "stack underflow"; break;
252 case GL_OUT_OF_MEMORY: error = "out of memory"; break;
253 case GL_INVALID_FRAMEBUFFER_OPERATION:
254 error = "invalid framebuffer operation"; break;
255 default: error = "unknown error"; break;
256 }
257
258 error += " | " + std::string(file) + " (" + std::to_string(line) + ")";
259 ERROR("{}: {}", Gl::subsystem_name, error);
260 }
261 return errorCode;
262}
263
264//
265// Utilities
266//
267
268void APIENTRY glDebugOutput([[maybe_unused]] GLenum source,
269 [[maybe_unused]] GLenum type,
270 [[maybe_unused]] GLuint id,
271 [[maybe_unused]] GLenum severity,
272 [[maybe_unused]] GLsizei length,
273 [[maybe_unused]] const GLchar *message,
274 [[maybe_unused]] const void *userParam)
275{
276 // ignore non-significant error/warning codes
277 if(id == 131169 || id == 131185 || id == 131218 || id == 131204) return;
278
279 std::stringstream out;
280 out << "Debug message (" << id << "): " << message << std::endl;
281
282 switch (source)
283 {
284 case GL_DEBUG_SOURCE_API:
285 out << "Source: API"; break;
286 case GL_DEBUG_SOURCE_WINDOW_SYSTEM:
287 out << "Source: Window System"; break;
288 case GL_DEBUG_SOURCE_SHADER_COMPILER:
289 out << "Source: Shader Compiler"; break;
290 case GL_DEBUG_SOURCE_THIRD_PARTY:
291 out << "Source: Third Party"; break;
292 case GL_DEBUG_SOURCE_APPLICATION:
293 out << "Source: Application"; break;
294 case GL_DEBUG_SOURCE_OTHER:
295 out << "Source: Other"; break;
296 } out << std::endl;
297
298 switch (type)
299 {
300 case GL_DEBUG_TYPE_ERROR:
301 out << "Type: Error"; break;
302 case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR:
303 out << "Type: Deprecated Behaviour"; break;
304 case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR:
305 out << "Type: Undefined Behaviour"; break;
306 case GL_DEBUG_TYPE_PORTABILITY:
307 out << "Type: Portability"; break;
308 case GL_DEBUG_TYPE_PERFORMANCE:
309 out << "Type: Performance"; break;
310 case GL_DEBUG_TYPE_MARKER:
311 out << "Type: Marker"; break;
312 case GL_DEBUG_TYPE_PUSH_GROUP:
313 out << "Type: Push Group"; break;
314 case GL_DEBUG_TYPE_POP_GROUP:
315 out << "Type: Pop Group"; break;
316 case GL_DEBUG_TYPE_OTHER:
317 out << "Type: Other"; break;
318 } out << std::endl;
319
320 switch (severity)
321 {
322 case GL_DEBUG_SEVERITY_HIGH:
323 out << "Severity: high"; break;
324 case GL_DEBUG_SEVERITY_MEDIUM:
325 out << "Severity: medium"; break;
326 case GL_DEBUG_SEVERITY_LOW:
327 out << "Severity: low"; break;
328 case GL_DEBUG_SEVERITY_NOTIFICATION:
329 out << "Severity: notification"; break;
330 } out << std::endl;
331 out << std::endl;
332
333 ERROR("{}: {}", Gl::subsystem_name, out.str());
334}
335
336//
337// Builder
338//
339
340Gl::Builder &Gl::Builder::blending()
341{
342 this->conf.enable_blending = true;
343 return *this;
344}
345
346Gl::Builder &Gl::Builder::backface_culling()
347{
348 this->conf.enable_backface_culling = true;
349 return *this;
350}
351
352Gl::Builder &Gl::Builder::multisample()
353{
354 this->conf.enable_multisample = true;
355 return *this;
356}
357
358Gl::Builder &Gl::Builder::depth_test()
359{
360 this->conf.enable_depth_test = true;
361 return *this;
362}
363
364brenta::Subsystem &Gl::Builder::build()
365{
366 Gl::init_config = this->conf;
367 return Gl::instance();
368}