123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- /*
- ===========================================================================
- Copyright (C) 1999-2005 Id Software, Inc.
- This file is part of Quake III Arena source code.
- Quake III Arena source code is free software; you can redistribute it
- and/or modify it under the terms of the GNU General Public License as
- published by the Free Software Foundation; either version 2 of the License,
- or (at your option) any later version.
- Quake III Arena source code is distributed in the hope that it will be
- useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Foobar; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- ===========================================================================
- */
- #import "macosx_glimp.h"
- #include "tr_local.h"
- #import "macosx_local.h"
- #import "macosx_display.h"
- #import <AppKit/AppKit.h>
- #import <Foundation/Foundation.h>
- #import <mach/mach.h>
- #import <mach/mach_error.h>
- #warning Using Mach Ports SMP acceleration implementation
- /*
- ===========================================================
- SMP acceleration
- ===========================================================
- */
- #import <pthread.h>
- #define USE_MACH_PORTS 1
- // This is a small cover layer that makes for easier calling
- typedef struct _MsgPort {
- #if USE_MACH_PORTS
- mach_port_t port;
- id nsPort;
- #else
- pthread_mutex_t mutex;
- pthread_cond_t condition;
- volatile unsigned int status;
- unsigned int msgCode;
- void *msgData;
- #endif
- } MsgPort;
-
- static BOOL portsInited = NO;
- static pthread_mutex_t logMutex;
- static unsigned int renderMsgOutstanding;
- static unsigned int rendererProcessingCommand;
- static MsgPort rendererMsgPort;
- static MsgPort frontEndMsgPort;
- enum {
- MsgNone,
- MsgPending,
- };
- enum {
- MsgCodeInvalid = 0,
- RenderCommandMsg = 1,
- RenderCompletedMsg = 2,
- };
- static /*inline*/ void MsgPortInit(MsgPort *port)
- {
- #if USE_MACH_PORTS
- port->nsPort = [[NSMachPort alloc] init];
- port->port = [port->nsPort machPort];
-
- //rc = mach_port_allocate(mach_task_self(), MACH_PORT_TYPE_SEND_RECEIVE, &port->port);
- //if (rc) {
- // fprintf(stderr, "MsgPortInit: mach_port_allocate returned: %d: %s \n",rc, mach_error_string(rc));
- // }
- #else
- int rc;
- rc = pthread_mutex_init(&port->mutex, NULL);
- if (rc) {
- ri.Printf(PRINT_ALL, "MsgPortInit: pthread_mutex_init returned: %d: %s\n", rc, strerror(rc));
- }
- rc = pthread_cond_init(&port->condition, NULL);
- if (rc) {
- ri.Printf(PRINT_ALL, "EventInit: pthread_cond_init returned %d: %s\n", rc, strerror(rc));
- }
- port->status = MsgNone;
- port->msgCode = MsgCodeInvalid;
- port->msgData = NULL;
- #endif
- }
- static /*inline*/ void _SendMsg(MsgPort *port, unsigned int msgCode, void *msgData,
- const char *functionName, const char *portName, const char *msgName)
- {
- int rc;
-
- #if USE_MACH_PORTS
- mach_msg_header_t msg;
- //printf("SendMsg: %s %s %s (%d %08lx)\n",functionName, portName, msgName, msgCode, msgData);
- /*
- typedef struct
- {
- mach_msg_bits_t msgh_bits;
- mach_msg_size_t msgh_size;
- mach_port_t msgh_remote_port;
- mach_port_t msgh_local_port;
- mach_msg_size_t msgh_reserved;
- mach_msg_id_t msgh_id;
- } mach_msg_header_t;
- */
- msg.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND_ONCE);
- msg.msgh_size=sizeof(msg);
- //msg.msg_type=MSG_TYPE_NORMAL;
- msg.msgh_local_port=MACH_PORT_NULL;
- msg.msgh_remote_port=port->port;
- msg.msgh_reserved = 0;
- msg.msgh_id=(mach_msg_id_t)msgData; // HACK
- rc = mach_msg_send(&msg);
- if(rc) {
- fprintf(stderr,"SendMsg: mach_msg_send returned %d: %s\n", rc, mach_error_string(rc));
- }
- #else
- //printf("SendMsg: %s %s %s (%d %08lx)\n",functionName, portName, msgName, msgCode, msgData);
- rc = pthread_mutex_lock(&port->mutex);
- if(rc) {
- fprintf(stderr,"SendMsg: pthread_mutex_lock returned %d: %s\n", rc, strerror(rc));
- }
-
- /* Block until port is empty */
- while(port->status != MsgNone) {
- //fprintf(stderr, "SendMsg: %s blocking until port %s is empty\n", functionName, portName);
- rc = pthread_cond_wait(&port->condition, &port->mutex);
- if(rc) {
- fprintf(stderr, "SendMsg: pthread_cond_wait returned %d: %s\n", rc, strerror(rc));
- }
- }
-
- /* Queue msg */
- port->msgCode = msgCode;
- port->msgData = msgData;
- port->status = MsgPending;
-
- /* Unlock port */
- rc = pthread_mutex_unlock(&port->mutex);
- if(rc) {
- fprintf(stderr, "SendMsg: pthread_mutex_unlock returned %d: %s\n", rc, strerror(rc));
- }
- /* Wake up any threads blocked waiting for a message */
- rc = pthread_cond_broadcast(&port->condition);
- if(rc) {
- fprintf(stderr, "SendMsg: pthread_cond_broadcast returned %d: %s\n", rc, strerror(rc));
- }
- #endif
- }
- static /*inline*/ void _WaitMsg(MsgPort *port, unsigned int *msgCode, void **msgData,
- const char *functionName, const char *portName)
- {
- int rc;
- #if USE_MACH_PORTS
- mach_msg_empty_rcv_t msg;
- //printf("WaitMsg: %s %s\n",functionName, portName);
-
- msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND_ONCE);
- msg.header.msgh_size= sizeof(msg);
- //msg.msg_type=MSG_TYPE_NORMAL;
- msg.header.msgh_local_port=port->port;
- msg.header.msgh_remote_port=MACH_PORT_NULL;
- msg.header.msgh_reserved = 0;
- msg.header.msgh_id=(mach_msg_id_t)msgData; // HACK
- rc = mach_msg_receive(&msg.header);
- if(rc) {
- fprintf(stderr,"SendMsg: mach_msg_receive returned %d: %s\n", rc, mach_error_string(rc));
- }
- *msgData = (void *)msg.header.msgh_id;
- //printf("WaitMsg: %s %s got %08lx\n",functionName, portName, *msgData);
- #else
- //printf("WaitMsg: %s %s\n",functionName, portName);
-
- rc = pthread_mutex_lock(&port->mutex);
- if(rc) {
- fprintf(stderr, "WaitMsg: pthread_mutex_lock returned %d: %s\n", rc, strerror(rc));
- }
-
- /* Block until port is empty */
- while(port->status != MsgPending) {
- rc = pthread_cond_wait(&port->condition, &port->mutex);
- if(rc) {
- fprintf(stderr, "WaitMsg: pthread_cond_wait returned %d: %s\n", rc, strerror(rc));
- }
- }
-
- /* Remove msg */
- *msgCode = port->msgCode;
- *msgData = port->msgData;
- //printf("WaitMsg: %s %s got %d %08lx\n",functionName, portName, *msgCode, *msgData);
- port->status = MsgNone;
- port->msgCode = 0;
- port->msgData = NULL;
-
- rc = pthread_mutex_unlock(&port->mutex);
- if(rc) {
- fprintf(stderr, "WaitMsg: pthread_mutex_unlock returned %d: %s\n", rc, strerror(rc));
- }
- /* Wake up any threads blocked waiting for port to be empty. */
- rc = pthread_cond_broadcast(&port->condition);
- if(rc) {
- fprintf(stderr, "SendMsg: pthread_cond_broadcast returned %d: %s\n", rc, strerror(rc));
- }
- #endif
- }
- #define SendMsg(p, c, d) _SendMsg(p, c, d, __PRETTY_FUNCTION__, #p, #c)
- #define WaitMsg(p, c, d) _WaitMsg(p, c, d, __PRETTY_FUNCTION__, #p)
- #if 0
- static void _Log(const char *msg)
- {
- int rc;
-
- rc = pthread_mutex_lock(&logMutex);
- if (rc)
- ri.Printf(PRINT_ALL, "_Log: pthread_mutex_lock returned %d: %s\n", rc, strerror(rc));
- fputs(msg,stderr);
- fflush(stderr);
-
- rc = pthread_mutex_unlock(&logMutex);
- if (rc)
- ri.Printf(PRINT_ALL, "_Log: pthread_mutex_unlock returned %d: %s\n", rc, strerror(rc));
- }
- #endif
- //
- // The main Q3 SMP API
- //
- static void (*glimpRenderThread)( void ) = NULL;
- static void *GLimp_RenderThreadWrapper(void *arg)
- {
- Com_Printf("Render thread starting\n");
- glimpRenderThread();
- #ifndef USE_CGLMACROS
- // Unbind the context before we die
- OSX_GLContextClearCurrent();
- #endif
- // Send one last message back to front end before we die...
- // This is somewhat of a hack.. fixme.
- if (rendererProcessingCommand) {
- SendMsg(&frontEndMsgPort, RenderCompletedMsg, NULL);
- rendererProcessingCommand = NO;
- }
- Com_Printf("Render thread terminating\n");
-
- return arg;
- }
- qboolean GLimp_SpawnRenderThread( void (*function)( void ) )
- {
- pthread_t renderThread;
- int rc;
- if (!portsInited) {
- portsInited = YES;
- MsgPortInit(&rendererMsgPort);
- MsgPortInit(&frontEndMsgPort);
- renderMsgOutstanding = NO;
- rendererProcessingCommand = NO;
- pthread_mutex_init(&logMutex, NULL);
- }
-
- glimpRenderThread = function;
- rc = pthread_create(&renderThread,
- NULL, // attributes
- GLimp_RenderThreadWrapper,
- NULL); // argument
- if (rc) {
- ri.Printf(PRINT_ALL, "pthread_create returned %d: %s", rc, strerror(rc));
- return qfalse;
- } else {
- rc = pthread_detach(renderThread);
- if (rc) {
- ri.Printf(PRINT_ALL, "pthread_detach returned %d: %s", rc, strerror(rc));
- }
- }
- return qtrue;
- }
- static volatile void *smpData;
- // TJW - This is calling in the rendering thread to wait until another
- // command buffer is ready. The command buffer returned might be NULL,
- // indicating that the rendering thread should exit.
- void *GLimp_RendererSleep(void)
- {
- //_Log(__PRETTY_FUNCTION__ " entered");
- unsigned int msgCode;
- void *msgData;
-
- GLSTAMP("GLimp_RendererSleep start", 0);
- #ifndef USE_CGLMACROS
- // Clear the current context while we sleep so the main thread can access it
- OSX_GLContextClearCurrent();
- #endif
- // Let the main thread we are idle and that no work is queued
- //_Log("rs0\n");
- /* If we actually had some work to do, then tell the front end we completed it. */
- if (rendererProcessingCommand) {
- SendMsg(&frontEndMsgPort, RenderCompletedMsg, NULL);
- rendererProcessingCommand = NO;
- }
-
- // Wait for new msg
- for (;;) {
- WaitMsg(&rendererMsgPort, &msgCode, &msgData);
- if (1 || msgCode == RenderCommandMsg) {
- smpData = msgData;
- break;
- } else {
- printf("renderer received unknown message: %d\n",msgCode);
- }
- }
-
- #ifndef USE_CGLMACROS
- // We are going to render a frame... retake the context
- OSX_GLContextSetCurrent();
- #endif
- rendererProcessingCommand = YES;
-
- GLSTAMP("GLimp_RendererSleep end", 0);
- return (void *)smpData;
- }
- // TJW - This is from the main thread to wait until the rendering thread
- // has completed the command buffer that it has
- void GLimp_FrontEndSleep(void)
- {
- unsigned int msgCode;
- void *msgData;
-
- GLSTAMP("GLimp_FrontEndSleep start", 1);
- if (renderMsgOutstanding) {
- for (;;) {
- WaitMsg(&frontEndMsgPort, &msgCode, &msgData);
- if(1 || msgCode == RenderCompletedMsg) {
- break;
- } else {
- printf("front end received unknown message: %d\n",msgCode);
- }
- }
- renderMsgOutstanding = NO;
- }
- #ifndef USE_CGLMACROS
- // We are done waiting for the background thread, take the current context back.
- OSX_GLContextSetCurrent();
- #endif
- GLSTAMP("GLimp_FrontEndSleep end", 1);
- }
- // TJW - This is called in the main thread to issue another command
- // buffer to the rendering thread. This is always called AFTER
- // GLimp_FrontEndSleep, so we know that there is no command
- // pending in 'smpData'.
- void GLimp_WakeRenderer( void *data )
- {
- GLSTAMP("GLimp_WakeRenderer start", 1);
- #ifndef USE_CGLMACROS
- // We want the background thread to draw stuff. Give up the current context
- OSX_GLContextClearCurrent();
- #endif
- SendMsg(&rendererMsgPort, RenderCommandMsg, data);
-
- // Don't set flag saying that the renderer is processing something if it's just
- // being told to exit.
- //if(data != NULL)
- renderMsgOutstanding = YES;
- GLSTAMP("GLimp_WakeRenderer end", 1);
- }
|