macosx_glsmp_mutex.m (5221B)
1 /* 2 =========================================================================== 3 Copyright (C) 1999-2005 Id Software, Inc. 4 5 This file is part of Quake III Arena source code. 6 7 Quake III Arena source code is free software; you can redistribute it 8 and/or modify it under the terms of the GNU General Public License as 9 published by the Free Software Foundation; either version 2 of the License, 10 or (at your option) any later version. 11 12 Quake III Arena source code is distributed in the hope that it will be 13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Foobar; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 =========================================================================== 21 */ 22 #import "macosx_glimp.h" 23 24 #include "tr_local.h" 25 #import "macosx_local.h" 26 #import "macosx_display.h" 27 28 #import <AppKit/AppKit.h> 29 #import <Foundation/Foundation.h> 30 #import <pthread.h> 31 32 // 33 // The main Q3 SMP API 34 // 35 36 static pthread_mutex_t smpMutex; 37 static pthread_cond_t mainThreadCondition; 38 static pthread_cond_t renderThreadCondition; 39 40 static volatile qboolean smpDataChanged; 41 static volatile void *smpData; 42 43 44 static void *GLimp_RenderThreadWrapper(void *arg) 45 { 46 Com_Printf("Render thread starting\n"); 47 48 ((void (*)())arg)(); 49 50 #ifndef USE_CGLMACROS 51 // Unbind the context before we die 52 OSX_GLContextClearCurrent(); 53 #endif 54 55 Com_Printf("Render thread terminating\n"); 56 57 return arg; 58 } 59 60 qboolean GLimp_SpawnRenderThread( void (*function)( void ) ) 61 { 62 pthread_t renderThread; 63 int rc; 64 65 pthread_mutex_init(&smpMutex, NULL); 66 pthread_cond_init(&mainThreadCondition, NULL); 67 pthread_cond_init(&renderThreadCondition, NULL); 68 69 rc = pthread_create(&renderThread, NULL, GLimp_RenderThreadWrapper, function); 70 if (rc) { 71 ri.Printf(PRINT_ALL, "pthread_create returned %d: %s", rc, strerror(rc)); 72 return qfalse; 73 } else { 74 rc = pthread_detach(renderThread); 75 if (rc) { 76 ri.Printf(PRINT_ALL, "pthread_detach returned %d: %s", rc, strerror(rc)); 77 } 78 } 79 80 return qtrue; 81 } 82 83 // Called in the rendering thread to wait until a command buffer is ready. 84 // The command buffer returned might be NULL, indicating that the rendering thread should exit. 85 void *GLimp_RendererSleep(void) 86 { 87 void *data; 88 89 GLSTAMP("GLimp_RendererSleep start", 0); 90 91 #ifndef USE_CGLMACROS 92 // Clear the current context while we sleep so the main thread can access it 93 OSX_GLContextClearCurrent(); 94 #endif 95 96 pthread_mutex_lock(&smpMutex); { 97 // Clear out any data we had and signal the main thread that we are no longer busy 98 smpData = NULL; 99 smpDataChanged = qfalse; 100 pthread_cond_signal(&mainThreadCondition); 101 102 // Wait until we get something new to work on 103 while (!smpDataChanged) 104 pthread_cond_wait(&renderThreadCondition, &smpMutex); 105 106 // Record the data (if any). 107 data = smpData; 108 } pthread_mutex_unlock(&smpMutex); 109 110 #ifndef USE_CGLMACROS 111 // We are going to render a frame... retake the context 112 OSX_GLContextSetCurrent(); 113 #endif 114 115 GLSTAMP("GLimp_RendererSleep end", 0); 116 117 return (void *)data; 118 } 119 120 // Called from the main thread to wait until the rendering thread is done with the command buffer. 121 void GLimp_FrontEndSleep(void) 122 { 123 GLSTAMP("GLimp_FrontEndSleep start", 0); 124 125 pthread_mutex_lock(&smpMutex); { 126 while (smpData) { 127 #if 0 128 struct timespec ts; 129 int result; 130 131 ts.tv_sec = 1; 132 ts.tv_nsec = 0; 133 result = pthread_cond_timedwait_relative_np(&mainThreadCondition, &smpMutex, &ts); 134 if (result) { 135 Com_Printf("GLimp_FrontEndSleep timed out. Probably due to R_SyncRenderThread called due to Com_Error being called\n"); 136 break; 137 } 138 #else 139 pthread_cond_wait(&mainThreadCondition, &smpMutex); 140 #endif 141 } 142 } pthread_mutex_unlock(&smpMutex); 143 144 145 #ifndef USE_CGLMACROS 146 // We are done waiting for the background thread, take the current context back. 147 OSX_GLContextSetCurrent(); 148 #endif 149 150 GLSTAMP("GLimp_FrontEndSleep end", 0); 151 } 152 153 // This is called in the main thread to issue another command 154 // buffer to the rendering thread. This is always called AFTER 155 // GLimp_FrontEndSleep, so we know that there is no command 156 // pending in 'smpData'. 157 void GLimp_WakeRenderer( void *data ) 158 { 159 GLSTAMP("GLimp_WakeRenderer start", data); 160 161 #ifndef USE_CGLMACROS 162 // We want the background thread to draw stuff. Give up the current context 163 OSX_GLContextClearCurrent(); 164 #endif 165 166 pthread_mutex_lock(&smpMutex); { 167 // Store the new data pointer and wake up the rendering thread 168 assert(smpData == NULL); 169 smpData = data; 170 smpDataChanged = qtrue; 171 pthread_cond_signal(&renderThreadCondition); 172 } pthread_mutex_unlock(&smpMutex); 173 174 GLSTAMP("GLimp_WakeRenderer end", data); 175 } 176