1 /*
2 * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>.
3 * It is copyright by its individual contributors, as recorded in the
4 * project's Git history. See COPYING.txt at the top level for license
5 * terms and a link to the Git history.
6 */
7
8 /* OpenGL Synchronization code:
9 * either use fence sync objects or glFinish() to prevent the GPU from
10 * lagging behind too much.
11 */
12
13 #include <stdlib.h>
14 #include <SDL.h>
15
16 #include "args.h"
17 #include "config.h"
18 #include "console.h"
19 #include "game.h"
20 #include "maths.h"
21 #include "multi.h"
22 #include "ogl_sync.h"
23 #include "timer.h"
24
25 namespace dcx {
26
ogl_sync()27 ogl_sync::ogl_sync()
28 {
29 method=SYNC_GL_NONE;
30 wait_timeout = 0;
31 }
32
~ogl_sync()33 ogl_sync::~ogl_sync()
34 {
35 if (fence)
36 con_puts(CON_URGENT, "DXX-Rebirth: OpenGL: fence sync object was never destroyed!");
37 }
38
operator ()(GLsync fence_func) const39 void ogl_sync::sync_deleter::operator()(GLsync fence_func) const
40 {
41 glDeleteSyncFunc(fence_func);
42 }
43
before_swap()44 void ogl_sync::before_swap()
45 {
46 if (const auto local_fence = std::move(fence))
47 {
48 /// use a fence sync object to prevent the GPU from queuing up more than one frame
49 const auto waitsync = glClientWaitSyncFunc;
50 if (method == SYNC_GL_FENCE_SLEEP) {
51 const auto local_wait_timeout = wait_timeout;
52 const auto multiplayer = Game_mode & GM_MULTI;
53 while (waitsync(local_fence.get(), GL_SYNC_FLUSH_COMMANDS_BIT, 0ULL) == GL_TIMEOUT_EXPIRED) {
54 if (multiplayer) {
55 multi_do_frame(); // during long wait, keep packets flowing
56 }
57 timer_delay_ms(local_wait_timeout);
58
59 }
60 } else {
61 waitsync(local_fence.get(), GL_SYNC_FLUSH_COMMANDS_BIT, 34000000ULL);
62 }
63 } else if (method == SYNC_GL_FINISH_BEFORE_SWAP) {
64 glFinish();
65 }
66 }
after_swap()67 void ogl_sync::after_swap()
68 {
69 if (method == SYNC_GL_FENCE || method == SYNC_GL_FENCE_SLEEP ) {
70 fence.reset(glFenceSyncFunc(GL_SYNC_GPU_COMMANDS_COMPLETE, 0));
71 } else if (method == SYNC_GL_FINISH_AFTER_SWAP) {
72 glFinish();
73 }
74 }
75
init(SyncGLMethod sync_method,int wait)76 void ogl_sync::init(SyncGLMethod sync_method, int wait)
77 {
78 method = sync_method;
79 fence = NULL;
80 fix a = i2f(wait);
81 fix b = i2f(1000);
82 wait_timeout = f2i(fixdiv(a, b) * 1000);
83
84 bool need_ARB_sync;
85
86 switch (method) {
87 case SYNC_GL_FENCE:
88 case SYNC_GL_FENCE_SLEEP:
89 case SYNC_GL_AUTO:
90 need_ARB_sync = true;
91 break;
92 default:
93 need_ARB_sync = false;
94 }
95
96 if (method == SYNC_GL_AUTO) {
97 if (!CGameCfg.VSync) {
98 con_puts(CON_NORMAL, "DXX-Rebirth: OpenGL: disabling automatic GL sync since VSync is turned off");
99 method = SYNC_GL_NONE;
100 need_ARB_sync = false;
101 } else if (!ogl_have_ARB_sync) {
102 con_puts(CON_NORMAL, "DXX-Rebirth: OpenGL: GL_ARB_sync not available, disabling sync");
103 method = SYNC_GL_NONE;
104 need_ARB_sync = false;
105 } else {
106 method = SYNC_GL_FENCE_SLEEP;
107 }
108 }
109
110 if (need_ARB_sync && !ogl_have_ARB_sync) {
111 con_puts(CON_URGENT, "DXX-Rebirth: OpenGL: GL_ARB_sync not available, using fallback");
112 method = SYNC_GL_FINISH_BEFORE_SWAP;
113 }
114 switch(method) {
115 case SYNC_GL_FENCE:
116 con_puts(CON_VERBOSE, "DXX-Rebirth: OpenGL: using GL_ARB_sync for synchronization (direct)");
117 break;
118 case SYNC_GL_FENCE_SLEEP:
119 con_printf(CON_VERBOSE, "DXX-Rebirth: OpenGL: using GL_ARB_sync for synchronization with interval %dms", wait);
120 break;
121 case SYNC_GL_FINISH_AFTER_SWAP:
122 con_puts(CON_VERBOSE, "DXX-Rebirth: OpenGL: using glFinish synchronization (method 1: after swap)");
123 break;
124 case SYNC_GL_FINISH_BEFORE_SWAP:
125 con_puts(CON_VERBOSE, "DXX-Rebirth: OpenGL: using glFinish synchronization (method 2: before swap)");
126 break;
127 default:
128 con_puts(CON_VERBOSE, "DXX-Rebirth: OpenGL: using no explicit GPU synchronization");
129 break;
130 }
131 }
132
deinit()133 void ogl_sync::deinit()
134 {
135 fence.reset();
136 }
137
138 }
139