102 Patches: Detours to the Rescue
C reference for DttR maintainers and modders.
Loading...
Searching...
No Matches
backend_opengl.c
Go to the documentation of this file.
1// OpenGL 3.3 fallback backend for environments where SDL GPU is unavailable.
2// (e.g. Parallels on Darwin ARM64)
3
5#include "graphics_private.h"
6
7#include <dttr_log.h>
8
9#include <dttr_config.h>
10
11#define DRIVER_DISPLAY_OPENGL "OpenGL 3.3"
12
13#ifdef DTTR_MODS_ENABLED
16#endif
17
18#include <math.h>
19#include <stdlib.h>
20#include <string.h>
21
23
24#include "gen/opengl_shaders.h"
25
26static GLuint compile_shader(GLenum type, const char *source) {
27 GLuint shader = glCreateShader(type);
28 glShaderSource(shader, 1, &source, NULL);
29 glCompileShader(shader);
30
31 GLint status = 0;
32 glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
33
34 if (!status) {
35 char info[512];
36 glGetShaderInfoLog(shader, sizeof(info), NULL, info);
38 "GL shader compile failed (%s): %s",
39 type == GL_VERTEX_SHADER ? "vert" : "frag",
40 info
41 );
42 glDeleteShader(shader);
43 return 0;
44 }
45
46 return shader;
47}
48
49static GLuint create_program() {
50 GLuint vert = compile_shader(GL_VERTEX_SHADER, OPENGL_BASIC_VERT_SOURCE);
51
52 if (!vert) {
53 return 0;
54 }
55
56 GLuint frag = compile_shader(GL_FRAGMENT_SHADER, OPENGL_BASIC_FRAG_SOURCE);
57
58 if (!frag) {
59 glDeleteShader(vert);
60 return 0;
61 }
62
63 GLuint program = glCreateProgram();
64 glAttachShader(program, vert);
65 glAttachShader(program, frag);
66 glLinkProgram(program);
67
68 glDeleteShader(vert);
69 glDeleteShader(frag);
70
71 GLint status = 0;
72 glGetProgramiv(program, GL_LINK_STATUS, &status);
73
74 if (!status) {
75 char info[512];
76 glGetProgramInfoLog(program, sizeof(info), NULL, info);
77 DTTR_LOG_ERROR("GL program link failed: %s", info);
78 glDeleteProgram(program);
79 return 0;
80 }
81
82 return program;
83}
84
85static bool create_fbo(opengl_backend_data *gl, int width, int height) {
86 glGenFramebuffers(1, &gl->fbo);
87 glBindFramebuffer(GL_FRAMEBUFFER, gl->fbo);
88
89 glGenTextures(1, &gl->fbo_color_tex);
90 glBindTexture(GL_TEXTURE_2D, gl->fbo_color_tex);
91 glTexImage2D(
92 GL_TEXTURE_2D,
93 0,
94 GL_RGBA8,
95 width,
96 height,
97 0,
98 GL_RGBA,
99 GL_UNSIGNED_BYTE,
100 NULL
101 );
102 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
103 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
104 glFramebufferTexture2D(
105 GL_FRAMEBUFFER,
106 GL_COLOR_ATTACHMENT0,
107 GL_TEXTURE_2D,
108 gl->fbo_color_tex,
109 0
110 );
111
112 glGenRenderbuffers(1, &gl->fbo_depth_rbo);
113 glBindRenderbuffer(GL_RENDERBUFFER, gl->fbo_depth_rbo);
114 glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
115 glFramebufferRenderbuffer(
116 GL_FRAMEBUFFER,
117 GL_DEPTH_ATTACHMENT,
118 GL_RENDERBUFFER,
119 gl->fbo_depth_rbo
120 );
121
122 GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
123 glBindFramebuffer(GL_FRAMEBUFFER, 0);
124
125 if (fb_status != GL_FRAMEBUFFER_COMPLETE) {
126 DTTR_LOG_ERROR("GL framebuffer incomplete: 0x%x", fb_status);
127 return false;
128 }
129
130 gl->fbo_width = width;
131 gl->fbo_height = height;
132 return true;
133}
134
136 if (gl->fbo) {
137 glDeleteFramebuffers(1, &gl->fbo);
138 gl->fbo = 0;
139 }
140
141 if (gl->fbo_color_tex) {
142 glDeleteTextures(1, &gl->fbo_color_tex);
143 gl->fbo_color_tex = 0;
144 }
145
146 if (gl->fbo_depth_rbo) {
147 glDeleteRenderbuffers(1, &gl->fbo_depth_rbo);
148 gl->fbo_depth_rbo = 0;
149 }
150}
151
153 glGenSamplers(DTTR_SAMPLER_COUNT, gl->gl_samplers);
154
155 const GLint min_filter = dttr_config.generate_texture_mipmaps
156 ? GL_LINEAR_MIPMAP_LINEAR
157 : GL_LINEAR;
158
159 for (int cu = 0; cu < 2; cu++) {
160 for (int cv = 0; cv < 2; cv++) {
161 GLuint s = gl->gl_samplers[cu * 2 + cv];
162 GLint wrap_s = cu ? GL_CLAMP_TO_EDGE : GL_REPEAT;
163 GLint wrap_t = cv ? GL_CLAMP_TO_EDGE : GL_REPEAT;
164 glSamplerParameteri(s, GL_TEXTURE_WRAP_S, wrap_s);
165 glSamplerParameteri(s, GL_TEXTURE_WRAP_T, wrap_t);
166 glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, min_filter);
167 glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
168 }
169 }
170}
171
173 int requested = dttr_config.msaa_samples;
174
175 if (requested <= 1) {
176 return 0;
177 }
178
179 GLint max_samples = 0;
180 glGetIntegerv(GL_MAX_SAMPLES, &max_samples);
181
182 if (max_samples <= 1) {
183 return 0;
184 }
185
186 if (requested > max_samples) {
187 requested = max_samples;
188 }
189
190 return requested;
191}
192
193static bool create_msaa_fbo(opengl_backend_data *gl, int w, int h, int samples) {
194 glGenFramebuffers(1, &gl->msaa_fbo);
195 glBindFramebuffer(GL_FRAMEBUFFER, gl->msaa_fbo);
196
197 glGenRenderbuffers(1, &gl->msaa_color_rbo);
198 glBindRenderbuffer(GL_RENDERBUFFER, gl->msaa_color_rbo);
199 glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_RGBA8, w, h);
200 glFramebufferRenderbuffer(
201 GL_FRAMEBUFFER,
202 GL_COLOR_ATTACHMENT0,
203 GL_RENDERBUFFER,
205 );
206
207 glGenRenderbuffers(1, &gl->msaa_depth_rbo);
208 glBindRenderbuffer(GL_RENDERBUFFER, gl->msaa_depth_rbo);
209 glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_DEPTH_COMPONENT24, w, h);
210 glFramebufferRenderbuffer(
211 GL_FRAMEBUFFER,
212 GL_DEPTH_ATTACHMENT,
213 GL_RENDERBUFFER,
215 );
216
217 GLenum fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
218 glBindFramebuffer(GL_FRAMEBUFFER, 0);
219
220 if (fb_status != GL_FRAMEBUFFER_COMPLETE) {
221 DTTR_LOG_ERROR("GL MSAA framebuffer incomplete: 0x%x", fb_status);
222 return false;
223 }
224
225 gl->msaa_samples = samples;
226 return true;
227}
228
230 if (gl->msaa_fbo) {
231 glDeleteFramebuffers(1, &gl->msaa_fbo);
232 gl->msaa_fbo = 0;
233 }
234
235 if (gl->msaa_color_rbo) {
236 glDeleteRenderbuffers(1, &gl->msaa_color_rbo);
237 gl->msaa_color_rbo = 0;
238 }
239
240 if (gl->msaa_depth_rbo) {
241 glDeleteRenderbuffers(1, &gl->msaa_depth_rbo);
242 gl->msaa_depth_rbo = 0;
243 }
244
245 gl->msaa_samples = 0;
246}
247
251) {
252 if (!state->texture_mutex) {
253 return;
254 }
255
256 SDL_LockMutex(state->texture_mutex);
257
258 for (int i = 0; i < gl->deferred_gl_destroy_count; i++) {
259 if (gl->deferred_gl_destroys[i]) {
260 glDeleteTextures(1, &gl->deferred_gl_destroys[i]);
261 gl->deferred_gl_destroys[i] = 0;
262 }
263 }
264
266 SDL_UnlockMutex(state->texture_mutex);
267}
268
269static void defer_texture_destroy(DTTR_BackendState *state, int texture_index) {
270 opengl_backend_data *gl = (opengl_backend_data *)state->backend_data;
271
272 if (!gl || texture_index < 0 || texture_index >= DTTR_MAX_STAGED_TEXTURES) {
273 return;
274 }
275
276 if (!gl->gl_textures[texture_index]) {
277 return;
278 }
279
282 [texture_index];
283 }
284
285 gl->gl_textures[texture_index] = 0;
286}
287
289 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
290 SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
291 SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
292 SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
293 SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
294
295 opengl_backend_data *gl = calloc(1, sizeof(opengl_backend_data));
296
297 if (!gl) {
298 return false;
299 }
300
301 gl->gl_context = SDL_GL_CreateContext(state->window);
302
303 if (!gl->gl_context) {
304 DTTR_LOG_ERROR("SDL_GL_CreateContext failed: %s", SDL_GetError());
305 free(gl);
306 return false;
307 }
308
309 if (!SDL_GL_MakeCurrent(state->window, gl->gl_context)) {
310 DTTR_LOG_ERROR("SDL_GL_MakeCurrent failed: %s", SDL_GetError());
311 SDL_GL_DestroyContext(gl->gl_context);
312 free(gl);
313 return false;
314 }
315
316 SDL_GL_SetSwapInterval(0);
317
318 if (!gladLoadGL((GLADloadfunc)SDL_GL_GetProcAddress)) {
319 DTTR_LOG_ERROR("Failed to load OpenGL functions via glad");
320 SDL_GL_DestroyContext(gl->gl_context);
321 free(gl);
322 return false;
323 }
324
325 gl->program = create_program();
326
327 if (!gl->program) {
328 SDL_GL_DestroyContext(gl->gl_context);
329 free(gl);
330 return false;
331 }
332
333 glUseProgram(gl->program);
334 gl->loc_mvp = glGetUniformLocation(gl->program, "u_mvp");
335 gl->loc_screen_size = glGetUniformLocation(gl->program, "u_screen_size");
336 gl->loc_is_2d = glGetUniformLocation(gl->program, "u_is_2d");
337 gl->loc_has_texture = glGetUniformLocation(gl->program, "u_has_texture");
338 gl->loc_color_op = glGetUniformLocation(gl->program, "u_color_op");
339 gl->loc_color_arg1 = glGetUniformLocation(gl->program, "u_color_arg1");
340 gl->loc_color_arg2 = glGetUniformLocation(gl->program, "u_color_arg2");
341 gl->loc_alpha_op = glGetUniformLocation(gl->program, "u_alpha_op");
342 gl->loc_alpha_arg1 = glGetUniformLocation(gl->program, "u_alpha_arg1");
343 gl->loc_alpha_arg2 = glGetUniformLocation(gl->program, "u_alpha_arg2");
344 gl->loc_texture = glGetUniformLocation(gl->program, "u_texture");
345
346 glGenVertexArrays(1, &gl->vao);
347 glBindVertexArray(gl->vao);
348 glGenBuffers(1, &gl->vbo);
349 glBindBuffer(GL_ARRAY_BUFFER, gl->vbo);
350 glBufferData(
351 GL_ARRAY_BUFFER,
353 NULL,
354 GL_DYNAMIC_DRAW
355 );
356
357 // Vertex layout matches DTTR_Vertex
358 const GLsizei stride = (GLsizei)DTTR_VERTEX_SIZE;
359 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride, (void *)0);
360 glEnableVertexAttribArray(0);
361 glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, stride, (void *)(3 * sizeof(float)));
362 glEnableVertexAttribArray(1);
363 glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, stride, (void *)(4 * sizeof(float)));
364 glEnableVertexAttribArray(2);
365 glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, stride, (void *)(8 * sizeof(float)));
366 glEnableVertexAttribArray(3);
367
368 if (!create_fbo(gl, state->width, state->height)) {
369 glDeleteProgram(gl->program);
370 glDeleteVertexArrays(1, &gl->vao);
371 glDeleteBuffers(1, &gl->vbo);
372 SDL_GL_DestroyContext(gl->gl_context);
373 free(gl);
374 return false;
375 }
376
377 create_samplers(gl);
378
379 int msaa = select_gl_msaa_samples();
380 if (msaa > 0) {
381 if (create_msaa_fbo(gl, state->width, state->height, msaa)) {
382 DTTR_LOG_INFO("OpenGL MSAA enabled (%dx samples)", msaa);
383 } else {
384 DTTR_LOG_WARN("OpenGL MSAA %dx failed, falling back to no MSAA", msaa);
386 }
387 }
388
389 // Create a 1-pixel white fallback texture.
390 glGenTextures(1, &gl->dummy_texture);
391 glBindTexture(GL_TEXTURE_2D, gl->dummy_texture);
392 const uint32_t white = 0xFFFFFFFF;
393 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &white);
394
395 // Allocate the (CPU) vertex staging buffer.
397
398 if (!gl->vertex_staging) {
399 DTTR_LOG_ERROR("Failed to allocate OpenGL vertex staging buffer");
400 SDL_GL_DestroyContext(gl->gl_context);
401 free(gl);
402 return false;
403 }
404
405 state->backend_data = gl;
406 state->backend_type = DTTR_BACKEND_OPENGL;
407 state->renderer = &renderer;
408
410 "OpenGL 3.3 backend initialized (vendor: %s, renderer: %s)",
411 glGetString(GL_VENDOR),
412 glGetString(GL_RENDERER)
413 );
414
415 return true;
416}
417
418static bool resize_fbo(DTTR_BackendState *state, int width, int height) {
419 opengl_backend_data *gl = (opengl_backend_data *)state->backend_data;
420
421 if (!gl) {
422 return false;
423 }
424
425 if (width == gl->fbo_width && height == gl->fbo_height) {
426 return true;
427 }
428
429 destroy_fbo(gl);
430
431 if (!create_fbo(gl, width, height)) {
432 DTTR_LOG_ERROR("Failed to recreate OpenGL FBO at %dx%d", width, height);
433 return false;
434 }
435
436 if (gl->msaa_samples > 0) {
437 int prev_samples = gl->msaa_samples;
439
440 if (!create_msaa_fbo(gl, width, height, prev_samples)) {
441 DTTR_LOG_WARN("MSAA FBO resize failed, disabling MSAA");
442 }
443 }
444
445 state->width = width;
446 state->height = height;
447
448 DTTR_LOG_INFO("GL FBO resized to %dx%d", width, height);
449 return true;
450}
451
453 if (!state->texture_mutex) {
454 return;
455 }
456
457 SDL_LockMutex(state->texture_mutex);
458 const size_t queued_count = kv_size(state->pending_upload_indices);
459
460 gl->pending_mipmap_count = 0;
461
462 for (size_t q = 0; q < queued_count; q++) {
463 const int idx = kv_A(state->pending_upload_indices, q);
464
465 if (idx < 0 || idx >= state->staged_texture_count) {
466 continue;
467 }
468
469 DTTR_StagedTexture *st = &state->staged_textures[idx];
470
471 if (!st->pixels) {
472 st->pending_upload = false;
473 continue;
474 }
475
476 st->pending_upload = false;
477
478 bool new_texture = false;
479
480 if (!gl->gl_textures[idx]) {
481 glGenTextures(1, &gl->gl_textures[idx]);
482 new_texture = true;
483 }
484
485 glBindTexture(GL_TEXTURE_2D, gl->gl_textures[idx]);
486
487 if (new_texture) {
488 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
489 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
490 }
491
492 glTexImage2D(
493 GL_TEXTURE_2D,
494 0,
495 GL_RGBA8,
496 st->width,
497 st->height,
498 0,
499 GL_BGRA,
500 GL_UNSIGNED_BYTE,
501 st->pixels
502 );
503
504 if (dttr_config.generate_texture_mipmaps) {
506 }
507
508 free(st->pixels);
509 st->pixels = NULL;
510 }
511
512 state->pending_upload_indices.n = 0;
513 SDL_UnlockMutex(state->texture_mutex);
514
515 for (int i = 0; i < gl->pending_mipmap_count; i++) {
516 glBindTexture(GL_TEXTURE_2D, gl->pending_mipmap_textures[i]);
517 glGenerateMipmap(GL_TEXTURE_2D);
518 }
519
520 gl->pending_mipmap_count = 0;
521}
522
525 const uint8_t *pixels,
526 int width,
527 int height
528) {
529 const bool resized = gl->video_width != width || gl->video_height != height;
530
531 glBindTexture(GL_TEXTURE_2D, gl->video_texture);
532 glTexImage2D(
533 GL_TEXTURE_2D,
534 0,
535 GL_RGBA8,
536 width,
537 height,
538 0,
539 GL_BGRA,
540 GL_UNSIGNED_BYTE,
541 pixels
542 );
543
544 if (!resized) {
545 return;
546 }
547
548 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
549 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
550 gl->video_width = width;
551 gl->video_height = height;
552}
553
555 opengl_backend_data *gl = (opengl_backend_data *)state->backend_data;
556
557 if (!gl || state->frame_active) {
558 return;
559 }
560
561 state->frame_index++;
562
565
566 state->batch_records.n = 0;
567 state->vertex_offset = 0;
568 state->transfer_mapped = gl->vertex_staging;
569 state->frame_active = true;
571}
572
574 const GLuint render_fbo = (gl->msaa_samples > 0) ? gl->msaa_fbo : gl->fbo;
575 glBindFramebuffer(GL_FRAMEBUFFER, render_fbo);
576 glViewport(0, 0, gl->fbo_width, gl->fbo_height);
577
578 glUseProgram(gl->program);
579 glBindVertexArray(gl->vao);
580
581 int last_blend_mode = -1;
582 bool last_depth_test = false;
583 bool last_depth_write = false;
584 GLuint last_texture = 0;
585 int last_sampler_index = -1;
586
587 for (size_t i = 0; i < kv_size(state->batch_records); i++) {
588 const DTTR_BatchRecord *rec = &kv_A(state->batch_records, i);
589
590 if (rec->type == DTTR_BATCH_CLEAR) {
591 GLbitfield clear_mask = 0;
592
593 if (rec->clear.flags & DTTR_CLEAR_COLOR) {
594 glClearColor(
595 rec->clear.color.r,
596 rec->clear.color.g,
597 rec->clear.color.b,
598 rec->clear.color.a
599 );
600 clear_mask |= GL_COLOR_BUFFER_BIT;
601 }
602
603 if (rec->clear.flags & DTTR_CLEAR_DEPTH) {
604 glClearDepth((double)rec->clear.depth);
605 glDepthMask(GL_TRUE);
606 clear_mask |= GL_DEPTH_BUFFER_BIT;
607 last_depth_write = true;
608 }
609
610 if (clear_mask) {
611 glClear(clear_mask);
612 }
613
614 last_blend_mode = -1;
615 last_depth_test = false;
616 last_texture = 0;
617 last_sampler_index = -1;
618 continue;
619 }
620
621 if (rec->draw.blend_mode != last_blend_mode) {
622 if (rec->draw.blend_mode == DTTR_BLEND_OFF) {
623 glDisable(GL_BLEND);
624 } else {
625 glEnable(GL_BLEND);
626 glBlendEquation(GL_FUNC_ADD);
627
628 if (rec->draw.blend_mode == DTTR_BLEND_ADDITIVE) {
629 glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE, GL_SRC_ALPHA, GL_ONE);
630 } else {
631 glBlendFuncSeparate(
632 GL_SRC_ALPHA,
633 GL_ONE_MINUS_SRC_ALPHA,
634 GL_SRC_ALPHA,
635 GL_ONE_MINUS_SRC_ALPHA
636 );
637 }
638 }
639
640 last_blend_mode = rec->draw.blend_mode;
641 }
642
643 if (rec->draw.depth_test != last_depth_test) {
644 if (rec->draw.depth_test) {
645 glEnable(GL_DEPTH_TEST);
646 glDepthFunc(GL_LEQUAL);
647 } else {
648 glDisable(GL_DEPTH_TEST);
649 }
650
651 last_depth_test = rec->draw.depth_test;
652 }
653
654 if (rec->draw.depth_write != last_depth_write) {
655 glDepthMask(rec->draw.depth_write ? GL_TRUE : GL_FALSE);
656 last_depth_write = rec->draw.depth_write;
657 }
658
659 glUniformMatrix4fv(gl->loc_mvp, 1, GL_FALSE, rec->draw.uniforms.mvp);
660 glUniform2f(
661 gl->loc_screen_size,
662 rec->draw.uniforms.screen_size[0],
664 );
665 glUniform1f(gl->loc_is_2d, rec->draw.uniforms.is_2d);
666 glUniform1f(gl->loc_has_texture, rec->draw.uniforms.has_texture);
667 glUniform1f(gl->loc_color_op, rec->draw.uniforms.color_op);
668 glUniform1f(gl->loc_color_arg1, rec->draw.uniforms.color_arg1);
669 glUniform1f(gl->loc_color_arg2, rec->draw.uniforms.color_arg2);
670 glUniform1f(gl->loc_alpha_op, rec->draw.uniforms.alpha_op);
671 glUniform1f(gl->loc_alpha_arg1, rec->draw.uniforms.alpha_arg1);
672 glUniform1f(gl->loc_alpha_arg2, rec->draw.uniforms.alpha_arg2);
673
674 GLuint tex_id = gl->dummy_texture;
675
676 if (rec->draw.uniforms.has_texture > 0.5f && rec->draw.texture_index != UINT32_MAX
678 GLuint staged = gl->gl_textures[rec->draw.texture_index];
679
680 if (staged) {
681 tex_id = staged;
682 }
683 }
684
685 if (tex_id != last_texture) {
686 glActiveTexture(GL_TEXTURE0);
687 glBindTexture(GL_TEXTURE_2D, tex_id);
688 glUniform1i(gl->loc_texture, 0);
689 last_texture = tex_id;
690 }
691
692 if (rec->draw.sampler_index != last_sampler_index) {
693 glBindSampler(0, gl->gl_samplers[rec->draw.sampler_index]);
694 last_sampler_index = rec->draw.sampler_index;
695 }
696
697 glDrawArrays(
698 GL_TRIANGLES,
699 (GLint)rec->draw.first_vertex,
700 (GLsizei)rec->draw.vertex_count
701 );
702 }
703}
704
706 opengl_backend_data *gl = (opengl_backend_data *)state->backend_data;
707
708 if (!gl || !state->frame_active) {
709 return;
710 }
711
712 state->frame_active = false;
714 state->transfer_mapped = NULL;
715
716 if (state->vertex_offset > 0) {
717 glBindBuffer(GL_ARRAY_BUFFER, gl->vbo);
718 glBufferSubData(
719 GL_ARRAY_BUFFER,
720 0,
721 (GLsizeiptr)(state->vertex_offset * DTTR_VERTEX_SIZE),
723 );
724 }
725
726 if (kv_size(state->batch_records) > 0) {
728 }
729
730#ifdef DTTR_MODS_ENABLED
732#endif
734
735 if (gl->msaa_samples > 0) {
736 glBindFramebuffer(GL_READ_FRAMEBUFFER, gl->msaa_fbo);
737 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gl->fbo);
738 glBlitFramebuffer(
739 0,
740 0,
741 gl->fbo_width,
742 gl->fbo_height,
743 0,
744 0,
745 gl->fbo_width,
746 gl->fbo_height,
747 GL_COLOR_BUFFER_BIT,
748 GL_NEAREST
749 );
750 }
751
752 int window_w = 0, window_h = 0;
753 SDL_GetWindowSizeInPixels(state->window, &window_w, &window_h);
754
755 if (window_w <= 0) {
756 window_w = state->width;
757 }
758
759 if (window_h <= 0) {
760 window_h = state->height;
761 }
762
763 const bool
764 is_internal_method = (dttr_config.scaling_method == DTTR_SCALING_METHOD_LOGICAL);
766 window_w,
767 window_h,
768 gl->fbo_width,
769 gl->fbo_height,
771 (!is_internal_method) && (dttr_config.scaling_fit == DTTR_SCALING_MODE_INTEGER),
772 1.0f
773 );
774
775 glBindFramebuffer(GL_READ_FRAMEBUFFER, gl->fbo);
776 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
777
778 glViewport(0, 0, window_w, window_h);
779 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
780 glClear(GL_COLOR_BUFFER_BIT);
781
782 const GLenum blit_filter = (dttr_config.present_filter == SDL_GPU_FILTER_NEAREST)
783 ? GL_NEAREST
784 : GL_LINEAR;
785 glBlitFramebuffer(
786 0,
787 0,
788 gl->fbo_width,
789 gl->fbo_height,
790 present.x,
791 present.y,
792 present.x + present.w,
793 present.y + present.h,
794 GL_COLOR_BUFFER_BIT,
795 blit_filter
796 );
797
798#ifdef DTTR_MODS_ENABLED
799 glBindFramebuffer(GL_FRAMEBUFFER, 0);
801 (uint32_t)present.x,
802 (uint32_t)present.y,
803 (uint32_t)present.w,
804 (uint32_t)present.h
805 );
806#endif
808
809 SDL_GL_SwapWindow(state->window);
812}
813
816 const uint8_t *pixels,
817 int width,
818 int height,
819 int stride
820) {
821 opengl_backend_data *gl = (opengl_backend_data *)state->backend_data;
822
823 if (!gl || !pixels || width <= 0 || height <= 0) {
824 return false;
825 }
826
827 if (state->frame_active) {
828 return false;
829 }
830
831 if (!gl->video_texture) {
832 glGenTextures(1, &gl->video_texture);
833 }
834
835 upload_video_texture(gl, pixels, width, height);
836
837 int window_w = 0, window_h = 0;
838 SDL_GetWindowSizeInPixels(state->window, &window_w, &window_h);
839
840 if (window_w <= 0 || window_h <= 0) {
841 return false;
842 }
843
844 glBindFramebuffer(GL_FRAMEBUFFER, 0);
845 glViewport(0, 0, window_w, window_h);
846 glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
847 glClear(GL_COLOR_BUFFER_BIT);
848
849 glDisable(GL_DEPTH_TEST);
850 glDisable(GL_BLEND);
851
852 glUseProgram(gl->program);
853 glBindVertexArray(gl->vao);
854
856 window_w,
857 window_h,
858 width,
859 height,
860 false,
861 false,
862 0.0f
863 );
864 const float x0 = (float)present.x;
865 const float y0 = (float)present.y;
866 const float x1 = (float)(present.x + present.w);
867 const float y1 = (float)(present.y + present.h);
868
869 float neutral_mvp[16];
870 dttr_graphics_mat4_identity(neutral_mvp);
871 glUniformMatrix4fv(gl->loc_mvp, 1, GL_FALSE, neutral_mvp);
872 glUniform2f(gl->loc_screen_size, (float)window_w, (float)window_h);
873 glUniform1f(gl->loc_is_2d, 1.0f);
874 glUniform1f(gl->loc_has_texture, 1.0f);
875 glUniform1f(gl->loc_color_op, (float)DTTR_D3DTOP_MODULATE);
876 glUniform1f(gl->loc_color_arg1, (float)DTTR_D3DTA_TEXTURE);
877 glUniform1f(gl->loc_color_arg2, (float)DTTR_D3DTA_DIFFUSE);
878 glUniform1f(gl->loc_alpha_op, (float)DTTR_D3DTOP_SELECTARG1);
879 glUniform1f(gl->loc_alpha_arg1, (float)DTTR_D3DTA_TEXTURE);
880 glUniform1f(gl->loc_alpha_arg2, (float)DTTR_D3DTA_DIFFUSE);
881
882 glActiveTexture(GL_TEXTURE0);
883 glBindTexture(GL_TEXTURE_2D, gl->video_texture);
884 glUniform1i(gl->loc_texture, 0);
885 glBindSampler(0, 0);
886
887 DTTR_Vertex verts[6] = {
888 {x0, y0, 0, 1, 1, 1, 1, 1, 0, 0},
889 {x1, y0, 0, 1, 1, 1, 1, 1, 1, 0},
890 {x0, y1, 0, 1, 1, 1, 1, 1, 0, 1},
891 {x0, y1, 0, 1, 1, 1, 1, 1, 0, 1},
892 {x1, y0, 0, 1, 1, 1, 1, 1, 1, 0},
893 {x1, y1, 0, 1, 1, 1, 1, 1, 1, 1},
894 };
895
896 glBindBuffer(GL_ARRAY_BUFFER, gl->vbo);
897 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(verts), verts);
898 glDrawArrays(GL_TRIANGLES, 0, 6);
899
900 SDL_GL_SwapWindow(state->window);
901 return true;
902}
903
905 opengl_backend_data *gl = (opengl_backend_data *)state->backend_data;
906
907 if (!gl) {
908 return;
909 }
910
911 if (gl->program) {
912 glDeleteProgram(gl->program);
913 }
914
915 if (gl->vao) {
916 glDeleteVertexArrays(1, &gl->vao);
917 }
918
919 if (gl->vbo) {
920 glDeleteBuffers(1, &gl->vbo);
921 }
922
924 destroy_fbo(gl);
925
927
928 for (int i = 0; i < DTTR_MAX_STAGED_TEXTURES; i++) {
929 if (gl->gl_textures[i]) {
930 glDeleteTextures(1, &gl->gl_textures[i]);
931 }
932 }
933
934 glDeleteSamplers(DTTR_SAMPLER_COUNT, gl->gl_samplers);
935
936 if (gl->dummy_texture) {
937 glDeleteTextures(1, &gl->dummy_texture);
938 }
939
940 if (gl->video_texture) {
941 glDeleteTextures(1, &gl->video_texture);
942 }
943
944 free(gl->vertex_staging);
945
946 SDL_GL_DestroyContext(gl->gl_context);
947 free(gl);
948 state->backend_data = NULL;
949}
950
951static const char *get_driver_name(const DTTR_BackendState *state) {
953}
954
955static const DTTR_RendererVtbl renderer = {
956 .begin_frame = begin_frame,
957 .end_frame = end_frame,
958 .present_video_frame_bgra = present_video_frame_bgra,
959 .resize = resize_fbo,
960 .cleanup = cleanup,
961 .get_driver_name = get_driver_name,
962 .defer_texture_destroy = defer_texture_destroy,
963};
static void replay_batch_records_gl(DTTR_BackendState *state, opengl_backend_data *gl)
static void upload_pending_textures_gl(DTTR_BackendState *state, opengl_backend_data *gl)
static void defer_texture_destroy(DTTR_BackendState *state, int texture_index)
static void upload_video_texture(opengl_backend_data *gl, const uint8_t *pixels, int width, int height)
bool dttr_graphics_opengl_init(DTTR_BackendState *state)
static GLuint create_program()
static void begin_frame(DTTR_BackendState *state)
static GLuint compile_shader(GLenum type, const char *source)
static bool create_msaa_fbo(opengl_backend_data *gl, int w, int h, int samples)
static int select_gl_msaa_samples()
static const char * get_driver_name(const DTTR_BackendState *state)
static void destroy_fbo(opengl_backend_data *gl)
static void create_samplers(opengl_backend_data *gl)
static void release_deferred_gl_destroys(DTTR_BackendState *state, opengl_backend_data *gl)
static void end_frame(DTTR_BackendState *state)
static bool resize_fbo(DTTR_BackendState *state, int width, int height)
static void cleanup(DTTR_BackendState *state)
static bool create_fbo(opengl_backend_data *gl, int width, int height)
static const DTTR_RendererVtbl renderer
#define DRIVER_DISPLAY_OPENGL
static void destroy_msaa_fbo(opengl_backend_data *gl)
static bool present_video_frame_bgra(DTTR_BackendState *state, const uint8_t *pixels, int width, int height, int stride)
DTTR_Graphics_COM_Direct3DDevice7 DWORD block DTTR_Graphics_COM_Direct3DDevice7 DWORD block DTTR_Graphics_COM_Direct3DDevice7 void void void void DWORD f DTTR_Graphics_COM_Direct3DDevice7 DWORD idx float
DTTR_Graphics_COM_Direct3DDevice7 void *status DTTR_Graphics_COM_Direct3DDevice7 DWORD DWORD void DWORD DWORD f DTTR_Graphics_COM_Direct3DDevice7 DWORD void DWORD st
size_t stride
const DTTR_BackendState * state
const DTTR_PrimitiveType type
DTTR_Graphics_COM_DirectDraw7 *self DWORD DWORD h
void DWORD DWORD * free
DTTR_Graphics_COM_DirectDraw7 *self DWORD w
DTTR_Graphics_COM_DirectDrawSurface7 DWORD flags void NULL
@ DTTR_SCALING_METHOD_LOGICAL
Definition dttr_config.h:22
@ DTTR_SCALING_MODE_STRETCH
Definition dttr_config.h:16
@ DTTR_SCALING_MODE_INTEGER
Definition dttr_config.h:17
DTTR_Config dttr_config
Definition defaults.c:53
#define DTTR_LOG_WARN(...)
Definition dttr_log.h:30
#define DTTR_LOG_INFO(...)
Definition dttr_log.h:29
#define DTTR_LOG_ERROR(...)
Definition dttr_log.h:31
static game_data_source source
Definition game_data.c:21
void dttr_graphics_mod_present_rect_before(DTTR_BackendState *state, const DTTR_PresentRect *present)
Definition graphics.c:424
void dttr_graphics_mod_present_rect_after(DTTR_BackendState *state, const DTTR_PresentRect *present, bool overlay_rendered)
Definition graphics.c:439
#define DTTR_MAX_FRAME_VERTICES
void dttr_graphics_mat4_identity(float *m)
Definition util.c:116
#define DTTR_SAMPLER_COUNT
#define DTTR_D3DTA_DIFFUSE
#define DTTR_CLEAR_COLOR
#define DTTR_BLEND_OFF
#define DTTR_VERTEX_SIZE
#define DTTR_D3DTOP_SELECTARG1
@ DTTR_BACKEND_OPENGL
static void dttr_graphics_mod_before_game_frame(DTTR_BackendState *)
#define DTTR_BLEND_ADDITIVE
#define DTTR_D3DTA_TEXTURE
#define DTTR_CLEAR_DEPTH
@ DTTR_BATCH_CLEAR
DTTR_PresentRect dttr_graphics_compute_present_rect(int dst_w, int dst_h, int src_w, int src_h, bool stretch, bool integer_fit, float fallback_scale)
Definition util.c:48
#define DTTR_D3DTOP_MODULATE
static void dttr_graphics_mod_after_game_frame(DTTR_BackendState *)
static void dttr_graphics_mod_frame_begin(DTTR_BackendState *)
static void dttr_graphics_mod_frame_end(DTTR_BackendState *)
#define DTTR_MAX_STAGED_TEXTURES
void dttr_imgui_render_opengl(uint32_t game_x, uint32_t game_y, uint32_t game_w, uint32_t game_h)
void dttr_imgui_render_game_opengl()
static mss_sample samples[DTTR_MSS_DEFAULT_MIXER_CHANNELS]
Definition mss_sample.c:35
A recorded clear or draw command replayed during frame submission.
struct DTTR_BatchRecord::@173304276063167267021134124022136033175102267147::@317066375077304207167347173122133172104242212006 clear
struct DTTR_BatchRecord::@173304276063167267021134124022136033175102267147::@107356260052026241174121242151011150024304321020 draw
DTTR_Uniforms uniforms
DTTR_BatchRecordType type
Game-image placement within the present target, in target pixels.
Backend-specific operations dispatched through function pointers.
GLuint deferred_gl_destroys[DTTR_MAX_STAGED_TEXTURES]
GLuint pending_mipmap_textures[DTTR_MAX_STAGED_TEXTURES]
GLuint gl_samplers[DTTR_SAMPLER_COUNT]
GLuint gl_textures[DTTR_MAX_STAGED_TEXTURES]