Line data Source code
1 : // Copyright (c) 2025 Milton McDonald
2 : // This source code is licensed under the MIT License. See LICENSE file in the
3 : // project root for details.
4 :
5 : #include "graphics-engine/gl-wrappers.h"
6 :
7 : #include <cassert>
8 : #include <iostream>
9 : #include <unordered_map>
10 : #include <utility>
11 :
12 : #include "error.h"
13 : #include "glad/glad.h"
14 : #include "graphics-engine/gl-types.h"
15 : #include "graphics-engine/types.h"
16 :
17 : using enum graphics_engine::types::ErrorCode;
18 : using enum graphics_engine::gl_types::GLBufferTarget;
19 : using enum graphics_engine::gl_types::GLClearBit;
20 : using enum graphics_engine::gl_types::GLDataType;
21 : using enum graphics_engine::gl_types::GLDataUsagePattern;
22 : using enum graphics_engine::gl_types::GLDrawMode;
23 : using enum graphics_engine::gl_types::GLShaderObjectParameter;
24 : using enum graphics_engine::gl_types::GLShaderType;
25 :
26 : using graphics_engine::error::MakeErrorCode;
27 : using graphics_engine::gl_clear_flags::IGLClearFlags;
28 : using graphics_engine::gl_types::GLBufferTarget;
29 : using graphics_engine::gl_types::GLDataType;
30 : using graphics_engine::gl_types::GLDataUsagePattern;
31 : using graphics_engine::gl_types::GLDrawMode;
32 : using graphics_engine::gl_types::GLShaderObjectParameter;
33 : using graphics_engine::gl_types::GLShaderType;
34 : using graphics_engine::types::Expected;
35 :
36 : using std::cerr;
37 : using std::is_same_v;
38 : using std::to_underlying;
39 : using std::unexpected;
40 :
41 : static_assert(is_same_v<GLbitfield, unsigned int>,
42 : "GLbitfield and unsigned int are not the same type!");
43 : static_assert(is_same_v<GLboolean, unsigned char>,
44 : "GLboolean and unsigned char are not the same type!");
45 : static_assert(is_same_v<GLchar, char>,
46 : "GLchar and char are not the same type!");
47 : static_assert(is_same_v<GLint, int>, "GLint and int are not the same type!");
48 : static_assert(is_same_v<GLsizei, int>,
49 : "GLsizei and int are not the same type!");
50 :
51 : #ifdef _WIN64
52 : static_assert(is_same_v<GLsizeiptr, long long int>,
53 : "GLsizeiptr and long long int are not the same type!");
54 : #else
55 : static_assert(is_same_v<GLsizeiptr, long int>,
56 : "GLsizeiptr and long long int are not the same type!");
57 : #endif
58 : static_assert(is_same_v<GLuint, unsigned int>,
59 : "GLuint and unsigned int are not the same type!");
60 : static_assert(is_same_v<GLvoid, void>,
61 : "GLvoid and void are not the same type!");
62 :
63 : namespace graphics_engine::gl_wrappers {
64 :
65 : namespace {
66 :
67 0 : auto ConvertGLBufferTarget(GLBufferTarget target) -> GLenum {
68 0 : switch (target) {
69 : default:
70 : assert(false); // If we get here, add a new case to the switch.
71 : [[fallthrough]];
72 : case kArray:
73 : return GL_ARRAY_BUFFER;
74 : case kCopyRead:
75 : return GL_COPY_READ_BUFFER;
76 : case kCopyWrite:
77 : return GL_COPY_WRITE_BUFFER;
78 : case kElementArray:
79 : return GL_ELEMENT_ARRAY_BUFFER;
80 : case kPixelPack:
81 : return GL_PIXEL_PACK_BUFFER;
82 : case kPixelUnpack:
83 : return GL_PIXEL_UNPACK_BUFFER;
84 : case kTexture:
85 : return GL_TEXTURE;
86 : case kTransformFeedback:
87 : return GL_TRANSFORM_FEEDBACK_BUFFER;
88 : case kUniform:
89 : return GL_UNIFORM_BUFFER;
90 : }
91 : }
92 :
93 0 : auto ConvertGLDataType(GLDataType type) -> GLenum {
94 0 : switch (type) {
95 : default:
96 : assert(false); // If we get here, add a new case to the switch.
97 : [[fallthrough]];
98 : case kByte:
99 : return GL_BYTE;
100 : case kUnsignedByte:
101 : return GL_UNSIGNED_BYTE;
102 : case kShort:
103 : return GL_SHORT;
104 : case kUnsignedShort:
105 : return GL_UNSIGNED_SHORT;
106 : case kInt:
107 : return GL_INT;
108 : case kUnsignedInt:
109 : return GL_UNSIGNED_INT;
110 : case kHalfFloat:
111 : return GL_HALF_FLOAT;
112 : case kFloat:
113 : return GL_FLOAT;
114 : case kDouble:
115 : return GL_DOUBLE;
116 : case kInt_2_10_10_10_Rev:
117 : return GL_INT_2_10_10_10_REV;
118 : case kUnsignedInt_2_10_10_10_Rev:
119 : return GL_UNSIGNED_INT_2_10_10_10_REV;
120 : }
121 : }
122 :
123 0 : auto ConvertGLDataUsagePattern(GLDataUsagePattern usage) -> GLenum {
124 0 : switch (usage) {
125 : default:
126 : assert(false); // If we get here, add a new case to the switch.
127 : [[fallthrough]];
128 : case kStreamDraw:
129 : return GL_STREAM_DRAW;
130 : case kStreamRead:
131 : return GL_STREAM_READ;
132 : case kStreamCopy:
133 : return GL_STREAM_COPY;
134 : case kStaticDraw:
135 : return GL_STATIC_DRAW;
136 : case kStaticRead:
137 : return GL_STATIC_READ;
138 : case kStaticCopy:
139 : return GL_STATIC_COPY;
140 : case kDynamicDraw:
141 : return GL_DYNAMIC_DRAW;
142 : case kDynamicRead:
143 : return GL_DYNAMIC_READ;
144 : case kDynamicCopy:
145 : return GL_DYNAMIC_COPY;
146 : }
147 : }
148 :
149 0 : auto ConvertGLDrawMode(GLDrawMode mode) -> GLenum {
150 0 : switch (mode) {
151 : default:
152 : assert(false); // If we get here, add a new case to the switch.
153 : [[fallthrough]];
154 : case kPoints:
155 : return GL_POINTS;
156 : case kLineStrip:
157 : return GL_LINE_STRIP;
158 : case kLineLoop:
159 : return GL_LINE_LOOP;
160 : case kLines:
161 : return GL_LINES;
162 : case kLineStripAdjacency:
163 : return GL_LINE_STRIP_ADJACENCY;
164 : case kLinesAdjacency:
165 : return GL_LINES_ADJACENCY;
166 : case kTriangleStrip:
167 : return GL_TRIANGLE_STRIP;
168 : case kTriangleFan:
169 : return GL_TRIANGLE_FAN;
170 : case kTriangles:
171 : return GL_TRIANGLES;
172 : case kTriangleStripAdjacency:
173 : return GL_TRIANGLE_STRIP_ADJACENCY;
174 : case kTrianglesAdjacency:
175 : return GL_TRIANGLES_ADJACENCY;
176 : }
177 : }
178 :
179 2 : auto ConvertGLShaderObjectParameter(GLShaderObjectParameter pname) -> GLenum {
180 2 : switch (pname) {
181 : default:
182 : assert(false); // If we get here, add a new case to the switch.
183 : [[fallthrough]];
184 : case kShaderType:
185 : return GL_SHADER_TYPE;
186 : case kDeleteStatus:
187 : return GL_DELETE_STATUS;
188 : case kCompileStatus:
189 : return GL_COMPILE_STATUS;
190 : case kInfoLogLength:
191 : return GL_INFO_LOG_LENGTH;
192 : case kShaderSourceLength:
193 : return GL_SHADER_SOURCE_LENGTH;
194 : }
195 : }
196 :
197 2 : auto ConvertGLShaderType(GLShaderType shader_type) -> GLenum {
198 2 : switch (shader_type) {
199 0 : default:
200 0 : std::cerr << "ConvertGLShaderType failed with underlying value "
201 0 : << static_cast<int>(shader_type) << '\n';
202 : assert(false); // If we get here, add a new case to the switch.
203 : [[fallthrough]];
204 : case kFragment:
205 : return GL_FRAGMENT_SHADER;
206 : case kGeometry:
207 : return GL_GEOMETRY_SHADER;
208 1 : case kVertex:
209 1 : return GL_VERTEX_SHADER;
210 : }
211 : }
212 :
213 : } // namespace
214 :
215 2 : auto AttachShader(unsigned int program, unsigned int shader) -> Expected<void> {
216 2 : glAttachShader(program, shader);
217 2 : if (GLenum error = glGetError(); error != GL_NO_ERROR) {
218 0 : cerr << "glAttachShader failed with error code " << error << '\n';
219 0 : switch (error) {
220 0 : default:
221 0 : assert(false); // If we get here, add a new case to the switch.
222 0 : [[fallthrough]];
223 0 : case GL_INVALID_VALUE:
224 0 : return unexpected(MakeErrorCode(kGLErrorInvalidValue));
225 0 : case GL_INVALID_OPERATION:
226 0 : return unexpected(MakeErrorCode(kGLErrorInvalidOperation));
227 : }
228 : }
229 :
230 2 : return {};
231 : }
232 :
233 0 : auto BindBuffer(GLBufferTarget target, unsigned int buffer) -> Expected<void> {
234 0 : GLenum gl_target = ConvertGLBufferTarget(target);
235 0 : glBindBuffer(gl_target, buffer);
236 0 : if (GLenum error = glGetError(); error != GL_NO_ERROR) {
237 0 : cerr << "glBindBuffer failed with error code " << error << '\n';
238 0 : switch (error) {
239 0 : default:
240 0 : assert(false); // If we get here, add a new case to the switch.
241 0 : [[fallthrough]];
242 0 : case GL_INVALID_ENUM:
243 0 : return unexpected(MakeErrorCode(kGLErrorInvalidEnum));
244 0 : case GL_INVALID_OPERATION:
245 0 : return unexpected(MakeErrorCode(kGLErrorInvalidOperation));
246 : }
247 : }
248 :
249 0 : return {};
250 : }
251 :
252 0 : auto BindVertexArray(unsigned int array) -> Expected<void> {
253 0 : glBindVertexArray(array);
254 0 : if (GLenum error = glGetError(); error != GL_NO_ERROR) {
255 0 : cerr << "glBindVertexArray failed with error code " << error << '\n';
256 0 : switch (error) {
257 0 : default:
258 0 : assert(false); // If we get here, add a new case to the switch.
259 0 : [[fallthrough]];
260 0 : case GL_INVALID_OPERATION:
261 0 : return unexpected(MakeErrorCode(kGLErrorInvalidOperation));
262 : }
263 : }
264 :
265 0 : return {};
266 : }
267 :
268 0 : auto BufferData(GLBufferTarget target, long long int size, const void* data,
269 : GLDataUsagePattern usage) -> Expected<void> {
270 0 : GLenum gl_target = ConvertGLBufferTarget(target);
271 0 : GLenum gl_usage = ConvertGLDataUsagePattern(usage);
272 0 : glBufferData(gl_target, size, data, gl_usage);
273 0 : if (GLenum error = glGetError(); error != GL_NO_ERROR) {
274 0 : cerr << "glBufferData failed with error code " << error << '\n';
275 0 : switch (error) {
276 0 : default:
277 0 : assert(false); // If we get here, add a new case to the switch.
278 0 : [[fallthrough]];
279 0 : case GL_INVALID_ENUM:
280 0 : return unexpected(MakeErrorCode(kGLErrorInvalidEnum));
281 0 : case GL_INVALID_OPERATION:
282 0 : return unexpected(MakeErrorCode(kGLErrorInvalidOperation));
283 0 : case GL_INVALID_VALUE:
284 0 : return unexpected(MakeErrorCode(kGLErrorInvalidValue));
285 0 : case GL_OUT_OF_MEMORY:
286 0 : return unexpected(MakeErrorCode(kGLErrorOutOfMemory));
287 : }
288 : }
289 :
290 0 : return {};
291 : }
292 :
293 0 : auto Clear(const IGLClearFlags& flags) -> types::Expected<void> {
294 0 : GLbitfield mask = 0;
295 0 : if (flags.Test(kColor)) {
296 0 : mask |= GL_COLOR_BUFFER_BIT;
297 : }
298 0 : if (flags.Test(kDepth)) {
299 0 : mask |= GL_DEPTH_BUFFER_BIT;
300 : }
301 0 : if (flags.Test(kStencil)) {
302 0 : mask |= GL_STENCIL_BUFFER_BIT;
303 : }
304 0 : glClear(mask);
305 0 : if (GLenum error = glGetError(); error != GL_NO_ERROR) {
306 0 : cerr << "glClear failed with error code " << error << '\n';
307 0 : switch (error) {
308 0 : default:
309 0 : assert(false); // If we get here, add a new case to the switch.
310 0 : [[fallthrough]];
311 0 : case GL_INVALID_VALUE:
312 0 : return unexpected(MakeErrorCode(kGLErrorInvalidValue));
313 : }
314 : }
315 :
316 0 : return {};
317 : }
318 :
319 2 : auto CompileShader(unsigned int shader) -> Expected<void> {
320 2 : glCompileShader(shader);
321 2 : if (GLenum error = glGetError(); error != GL_NO_ERROR) {
322 0 : cerr << "glCompileShader failed with error code " << error << '\n';
323 0 : switch (error) {
324 0 : default:
325 0 : assert(false); // If we get here, add a new case to the switch.
326 0 : [[fallthrough]];
327 0 : case GL_INVALID_OPERATION:
328 0 : return unexpected(MakeErrorCode(kGLErrorInvalidOperation));
329 0 : case GL_INVALID_VALUE:
330 0 : return unexpected(MakeErrorCode(kGLErrorInvalidValue));
331 : }
332 : }
333 :
334 2 : return {};
335 : }
336 :
337 1 : auto CreateProgram() -> Expected<unsigned int> {
338 1 : GLuint program_id = glCreateProgram();
339 1 : if (program_id == 0) {
340 0 : cerr << "An error occurred creating the program object.";
341 0 : return unexpected(MakeErrorCode(kGLError));
342 : }
343 :
344 1 : return program_id;
345 : }
346 :
347 2 : auto CreateShader(GLShaderType shader_type) -> Expected<unsigned int> {
348 2 : GLenum gl_shader_type = ConvertGLShaderType(shader_type);
349 2 : GLuint shader = glCreateShader(gl_shader_type);
350 2 : if (GLenum error = glGetError(); error != GL_NO_ERROR) {
351 0 : cerr << "glCreateShader failed with error code " << error << '\n';
352 0 : switch (error) {
353 0 : default:
354 0 : assert(false); // If we get here, add a new case to the switch.
355 0 : [[fallthrough]];
356 0 : case GL_INVALID_ENUM:
357 0 : return unexpected(MakeErrorCode(kGLErrorInvalidEnum));
358 : }
359 : }
360 :
361 2 : assert(shader > 0U);
362 2 : return shader;
363 : }
364 :
365 0 : auto DrawArrays(GLDrawMode mode, int first, int count) -> Expected<void> {
366 0 : GLenum gl_mode = ConvertGLDrawMode(mode);
367 0 : glDrawArrays(gl_mode, first, count);
368 0 : if (GLenum error = glGetError(); error != GL_NO_ERROR) {
369 0 : cerr << "glDrawArrays failed with error code " << error << '\n';
370 0 : switch (error) {
371 0 : default:
372 0 : assert(false); // If we get here, add a new case to the switch.
373 0 : [[fallthrough]];
374 0 : case GL_INVALID_ENUM:
375 0 : return unexpected(MakeErrorCode(kGLErrorInvalidEnum));
376 0 : case GL_INVALID_OPERATION:
377 0 : return unexpected(MakeErrorCode(kGLErrorInvalidOperation));
378 0 : case GL_INVALID_VALUE:
379 0 : return unexpected(MakeErrorCode(kGLErrorInvalidValue));
380 : }
381 : }
382 :
383 0 : return {};
384 : }
385 :
386 0 : auto EnableVertexAttribArray(unsigned int index) -> Expected<void> {
387 0 : glEnableVertexAttribArray(index);
388 0 : if (GLenum error = glGetError(); error != GL_NO_ERROR) {
389 0 : cerr << "glEnableVertexAttribArray failed with error code " << error
390 0 : << '\n';
391 0 : switch (error) {
392 0 : default:
393 0 : assert(false); // If we get here, add a new case to the switch.
394 0 : [[fallthrough]];
395 0 : case GL_INVALID_OPERATION:
396 0 : return unexpected(MakeErrorCode(kGLErrorInvalidOperation));
397 0 : case GL_INVALID_VALUE:
398 0 : return unexpected(MakeErrorCode(kGLErrorInvalidValue));
399 : }
400 : }
401 :
402 0 : return {};
403 : }
404 :
405 0 : auto GenBuffers(int n, unsigned int* buffers) -> Expected<void> {
406 0 : glGenBuffers(n, buffers);
407 0 : if (GLenum error = glGetError(); error != GL_NO_ERROR) {
408 0 : cerr << "glGenBuffers failed with error code " << error << '\n';
409 0 : switch (error) {
410 0 : default:
411 0 : assert(false); // If we get here, add a new case to the switch.
412 0 : [[fallthrough]];
413 0 : case GL_INVALID_VALUE:
414 0 : return unexpected(MakeErrorCode(kGLErrorInvalidValue));
415 : }
416 : }
417 :
418 0 : return {};
419 : }
420 :
421 0 : auto GenVertexArrays(int n, unsigned int* arrays) -> Expected<void> {
422 0 : glGenVertexArrays(n, arrays);
423 0 : if (GLenum error = glGetError(); error != GL_NO_ERROR) {
424 0 : cerr << "glGenVertexArrays failed with error code " << error << '\n';
425 0 : switch (error) {
426 0 : default:
427 0 : assert(false); // If we get here, add a new case to the switch.
428 0 : [[fallthrough]];
429 0 : case GL_INVALID_VALUE:
430 0 : return unexpected(MakeErrorCode(kGLErrorInvalidValue));
431 : }
432 : }
433 :
434 0 : return {};
435 : }
436 :
437 0 : DLLEXPORT [[nodiscard]] auto GetShaderInfoLog(unsigned int shader,
438 : int max_length, int* length,
439 : char* info_log)
440 : -> types::Expected<void> {
441 0 : glGetShaderInfoLog(shader, max_length, length, info_log);
442 0 : if (GLenum error = glGetError(); error != GL_NO_ERROR) {
443 0 : cerr << "glGetShaderInfoLog failed with error code " << error << '\n';
444 0 : switch (error) {
445 0 : default:
446 0 : assert(false); // If we get here, add a new case to the switch.
447 0 : [[fallthrough]];
448 0 : case GL_INVALID_VALUE:
449 0 : return unexpected(MakeErrorCode(kGLErrorInvalidValue));
450 0 : case GL_INVALID_OPERATION:
451 0 : return unexpected(MakeErrorCode(kGLErrorInvalidOperation));
452 : }
453 : }
454 :
455 0 : return {};
456 : }
457 :
458 2 : DLLEXPORT [[nodiscard]] auto GetShaderiv(
459 : unsigned int shader, gl_types::GLShaderObjectParameter pname, int* params)
460 : -> types::Expected<void> {
461 2 : GLenum gl_pname = ConvertGLShaderObjectParameter(pname);
462 2 : glGetShaderiv(shader, gl_pname, params);
463 2 : if (GLenum error = glGetError(); error != GL_NO_ERROR) {
464 0 : cerr << "glGetShaderiv failed with error code " << error << '\n';
465 0 : switch (error) {
466 0 : default:
467 0 : assert(false); // If we get here, add a new case to the switch.
468 0 : [[fallthrough]];
469 0 : case GL_INVALID_ENUM:
470 0 : return unexpected(MakeErrorCode(kGLErrorInvalidEnum));
471 0 : case GL_INVALID_OPERATION:
472 0 : return unexpected(MakeErrorCode(kGLErrorInvalidOperation));
473 0 : case GL_INVALID_VALUE:
474 0 : return unexpected(MakeErrorCode(kGLErrorInvalidValue));
475 : }
476 : }
477 :
478 2 : return {};
479 : }
480 :
481 1 : auto LinkProgram(unsigned int program) -> types::Expected<void> {
482 1 : glLinkProgram(program);
483 1 : if (GLenum error = glGetError(); error != GL_NO_ERROR) {
484 0 : cerr << "glLinkProgram failed with error code " << error << '\n';
485 0 : switch (error) {
486 0 : default:
487 0 : assert(false); // If we get here, add a new case to the switch.
488 0 : [[fallthrough]];
489 0 : case GL_INVALID_VALUE:
490 0 : return unexpected(MakeErrorCode(kGLErrorInvalidValue));
491 0 : case GL_INVALID_OPERATION:
492 0 : return unexpected(MakeErrorCode(kGLErrorInvalidOperation));
493 : }
494 : }
495 :
496 1 : return {};
497 : }
498 :
499 2 : auto ShaderSource(unsigned int shader, int count, const char** string,
500 : const int* length) -> Expected<void> {
501 2 : glShaderSource(shader, count, string, length);
502 2 : if (GLenum error = glGetError(); error != GL_NO_ERROR) {
503 0 : cerr << "glShaderSource failed with error code " << error << '\n';
504 0 : switch (error) {
505 0 : default:
506 0 : assert(false); // If we get here, add a new case to the switch
507 0 : [[fallthrough]];
508 0 : case GL_INVALID_VALUE:
509 0 : return unexpected(MakeErrorCode(kGLErrorInvalidValue));
510 0 : case GL_INVALID_OPERATION:
511 0 : return unexpected(MakeErrorCode(kGLErrorInvalidOperation));
512 : }
513 : }
514 :
515 2 : return {};
516 : }
517 :
518 0 : auto UseProgram(unsigned int program) -> Expected<void> {
519 0 : glUseProgram(program);
520 0 : if (GLenum error = glGetError(); error != GL_NO_ERROR) {
521 0 : cerr << "glUseProgram failed with error code " << error << '\n';
522 0 : switch (error) {
523 0 : default:
524 0 : assert(false); // If we get here, add a new case to the switch.
525 0 : [[fallthrough]];
526 0 : case GL_INVALID_OPERATION:
527 0 : return unexpected(MakeErrorCode(kGLErrorInvalidOperation));
528 0 : case GL_INVALID_VALUE:
529 0 : return unexpected(MakeErrorCode(kGLErrorInvalidValue));
530 : }
531 : }
532 :
533 0 : return {};
534 : }
535 :
536 0 : auto VertexAttribPointer(unsigned int index, int size, GLDataType type,
537 : unsigned char normalized, int stride,
538 : const void* pointer) -> Expected<void> {
539 0 : GLenum gl_type = ConvertGLDataType(type);
540 0 : glVertexAttribPointer(index, size, gl_type, normalized, stride, pointer);
541 0 : if (GLenum error = glGetError(); error != GL_NO_ERROR) {
542 0 : cerr << "glVertexAttribPointer failed with error code " << error << '\n';
543 0 : switch (error) {
544 0 : default:
545 0 : assert(false); // If we get here, add a new case to the switch.
546 0 : [[fallthrough]];
547 0 : case GL_INVALID_ENUM:
548 0 : return unexpected(MakeErrorCode(kGLErrorInvalidEnum));
549 0 : case GL_INVALID_OPERATION:
550 0 : return unexpected(MakeErrorCode(kGLErrorInvalidOperation));
551 0 : case GL_INVALID_VALUE:
552 0 : return unexpected(MakeErrorCode(kGLErrorInvalidValue));
553 : }
554 : }
555 :
556 0 : return {};
557 : }
558 :
559 : } // namespace graphics_engine::gl_wrappers
|