Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

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