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