Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 1 | pmbaty | 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 | |||
| 27 | ogl_sync::ogl_sync() |
||
| 28 | { |
||
| 29 | method=SYNC_GL_NONE; |
||
| 30 | wait_timeout = 0; |
||
| 31 | } |
||
| 32 | |||
| 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 | |||
| 39 | void ogl_sync::sync_deleter::operator()(GLsync fence_func) const |
||
| 40 | { |
||
| 41 | glDeleteSyncFunc(fence_func); |
||
| 42 | } |
||
| 43 | |||
| 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 | } |
||
| 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 | |||
| 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 | |||
| 133 | void ogl_sync::deinit() |
||
| 134 | { |
||
| 135 | fence.reset(); |
||
| 136 | } |
||
| 137 | |||
| 138 | } |