macosx_glsmp_ports.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. /*
  2. ===========================================================================
  3. Copyright (C) 1999-2005 Id Software, Inc.
  4. This file is part of Quake III Arena source code.
  5. Quake III Arena source code is free software; you can redistribute it
  6. and/or modify it under the terms of the GNU General Public License as
  7. published by the Free Software Foundation; either version 2 of the License,
  8. or (at your option) any later version.
  9. Quake III Arena source code is distributed in the hope that it will be
  10. useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with Foobar; if not, write to the Free Software
  15. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  16. ===========================================================================
  17. */
  18. #import "macosx_glimp.h"
  19. #include "tr_local.h"
  20. #import "macosx_local.h"
  21. #import "macosx_display.h"
  22. #import <AppKit/AppKit.h>
  23. #import <Foundation/Foundation.h>
  24. #import <mach/mach.h>
  25. #import <mach/mach_error.h>
  26. #warning Using Mach Ports SMP acceleration implementation
  27. /*
  28. ===========================================================
  29. SMP acceleration
  30. ===========================================================
  31. */
  32. #import <pthread.h>
  33. #define USE_MACH_PORTS 1
  34. // This is a small cover layer that makes for easier calling
  35. typedef struct _MsgPort {
  36. #if USE_MACH_PORTS
  37. mach_port_t port;
  38. id nsPort;
  39. #else
  40. pthread_mutex_t mutex;
  41. pthread_cond_t condition;
  42. volatile unsigned int status;
  43. unsigned int msgCode;
  44. void *msgData;
  45. #endif
  46. } MsgPort;
  47. static BOOL portsInited = NO;
  48. static pthread_mutex_t logMutex;
  49. static unsigned int renderMsgOutstanding;
  50. static unsigned int rendererProcessingCommand;
  51. static MsgPort rendererMsgPort;
  52. static MsgPort frontEndMsgPort;
  53. enum {
  54. MsgNone,
  55. MsgPending,
  56. };
  57. enum {
  58. MsgCodeInvalid = 0,
  59. RenderCommandMsg = 1,
  60. RenderCompletedMsg = 2,
  61. };
  62. static /*inline*/ void MsgPortInit(MsgPort *port)
  63. {
  64. #if USE_MACH_PORTS
  65. port->nsPort = [[NSMachPort alloc] init];
  66. port->port = [port->nsPort machPort];
  67. //rc = mach_port_allocate(mach_task_self(), MACH_PORT_TYPE_SEND_RECEIVE, &port->port);
  68. //if (rc) {
  69. // fprintf(stderr, "MsgPortInit: mach_port_allocate returned: %d: %s \n",rc, mach_error_string(rc));
  70. // }
  71. #else
  72. int rc;
  73. rc = pthread_mutex_init(&port->mutex, NULL);
  74. if (rc) {
  75. ri.Printf(PRINT_ALL, "MsgPortInit: pthread_mutex_init returned: %d: %s\n", rc, strerror(rc));
  76. }
  77. rc = pthread_cond_init(&port->condition, NULL);
  78. if (rc) {
  79. ri.Printf(PRINT_ALL, "EventInit: pthread_cond_init returned %d: %s\n", rc, strerror(rc));
  80. }
  81. port->status = MsgNone;
  82. port->msgCode = MsgCodeInvalid;
  83. port->msgData = NULL;
  84. #endif
  85. }
  86. static /*inline*/ void _SendMsg(MsgPort *port, unsigned int msgCode, void *msgData,
  87. const char *functionName, const char *portName, const char *msgName)
  88. {
  89. int rc;
  90. #if USE_MACH_PORTS
  91. mach_msg_header_t msg;
  92. //printf("SendMsg: %s %s %s (%d %08lx)\n",functionName, portName, msgName, msgCode, msgData);
  93. /*
  94. typedef struct
  95. {
  96. mach_msg_bits_t msgh_bits;
  97. mach_msg_size_t msgh_size;
  98. mach_port_t msgh_remote_port;
  99. mach_port_t msgh_local_port;
  100. mach_msg_size_t msgh_reserved;
  101. mach_msg_id_t msgh_id;
  102. } mach_msg_header_t;
  103. */
  104. msg.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND_ONCE);
  105. msg.msgh_size=sizeof(msg);
  106. //msg.msg_type=MSG_TYPE_NORMAL;
  107. msg.msgh_local_port=MACH_PORT_NULL;
  108. msg.msgh_remote_port=port->port;
  109. msg.msgh_reserved = 0;
  110. msg.msgh_id=(mach_msg_id_t)msgData; // HACK
  111. rc = mach_msg_send(&msg);
  112. if(rc) {
  113. fprintf(stderr,"SendMsg: mach_msg_send returned %d: %s\n", rc, mach_error_string(rc));
  114. }
  115. #else
  116. //printf("SendMsg: %s %s %s (%d %08lx)\n",functionName, portName, msgName, msgCode, msgData);
  117. rc = pthread_mutex_lock(&port->mutex);
  118. if(rc) {
  119. fprintf(stderr,"SendMsg: pthread_mutex_lock returned %d: %s\n", rc, strerror(rc));
  120. }
  121. /* Block until port is empty */
  122. while(port->status != MsgNone) {
  123. //fprintf(stderr, "SendMsg: %s blocking until port %s is empty\n", functionName, portName);
  124. rc = pthread_cond_wait(&port->condition, &port->mutex);
  125. if(rc) {
  126. fprintf(stderr, "SendMsg: pthread_cond_wait returned %d: %s\n", rc, strerror(rc));
  127. }
  128. }
  129. /* Queue msg */
  130. port->msgCode = msgCode;
  131. port->msgData = msgData;
  132. port->status = MsgPending;
  133. /* Unlock port */
  134. rc = pthread_mutex_unlock(&port->mutex);
  135. if(rc) {
  136. fprintf(stderr, "SendMsg: pthread_mutex_unlock returned %d: %s\n", rc, strerror(rc));
  137. }
  138. /* Wake up any threads blocked waiting for a message */
  139. rc = pthread_cond_broadcast(&port->condition);
  140. if(rc) {
  141. fprintf(stderr, "SendMsg: pthread_cond_broadcast returned %d: %s\n", rc, strerror(rc));
  142. }
  143. #endif
  144. }
  145. static /*inline*/ void _WaitMsg(MsgPort *port, unsigned int *msgCode, void **msgData,
  146. const char *functionName, const char *portName)
  147. {
  148. int rc;
  149. #if USE_MACH_PORTS
  150. mach_msg_empty_rcv_t msg;
  151. //printf("WaitMsg: %s %s\n",functionName, portName);
  152. msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,MACH_MSG_TYPE_MAKE_SEND_ONCE);
  153. msg.header.msgh_size= sizeof(msg);
  154. //msg.msg_type=MSG_TYPE_NORMAL;
  155. msg.header.msgh_local_port=port->port;
  156. msg.header.msgh_remote_port=MACH_PORT_NULL;
  157. msg.header.msgh_reserved = 0;
  158. msg.header.msgh_id=(mach_msg_id_t)msgData; // HACK
  159. rc = mach_msg_receive(&msg.header);
  160. if(rc) {
  161. fprintf(stderr,"SendMsg: mach_msg_receive returned %d: %s\n", rc, mach_error_string(rc));
  162. }
  163. *msgData = (void *)msg.header.msgh_id;
  164. //printf("WaitMsg: %s %s got %08lx\n",functionName, portName, *msgData);
  165. #else
  166. //printf("WaitMsg: %s %s\n",functionName, portName);
  167. rc = pthread_mutex_lock(&port->mutex);
  168. if(rc) {
  169. fprintf(stderr, "WaitMsg: pthread_mutex_lock returned %d: %s\n", rc, strerror(rc));
  170. }
  171. /* Block until port is empty */
  172. while(port->status != MsgPending) {
  173. rc = pthread_cond_wait(&port->condition, &port->mutex);
  174. if(rc) {
  175. fprintf(stderr, "WaitMsg: pthread_cond_wait returned %d: %s\n", rc, strerror(rc));
  176. }
  177. }
  178. /* Remove msg */
  179. *msgCode = port->msgCode;
  180. *msgData = port->msgData;
  181. //printf("WaitMsg: %s %s got %d %08lx\n",functionName, portName, *msgCode, *msgData);
  182. port->status = MsgNone;
  183. port->msgCode = 0;
  184. port->msgData = NULL;
  185. rc = pthread_mutex_unlock(&port->mutex);
  186. if(rc) {
  187. fprintf(stderr, "WaitMsg: pthread_mutex_unlock returned %d: %s\n", rc, strerror(rc));
  188. }
  189. /* Wake up any threads blocked waiting for port to be empty. */
  190. rc = pthread_cond_broadcast(&port->condition);
  191. if(rc) {
  192. fprintf(stderr, "SendMsg: pthread_cond_broadcast returned %d: %s\n", rc, strerror(rc));
  193. }
  194. #endif
  195. }
  196. #define SendMsg(p, c, d) _SendMsg(p, c, d, __PRETTY_FUNCTION__, #p, #c)
  197. #define WaitMsg(p, c, d) _WaitMsg(p, c, d, __PRETTY_FUNCTION__, #p)
  198. #if 0
  199. static void _Log(const char *msg)
  200. {
  201. int rc;
  202. rc = pthread_mutex_lock(&logMutex);
  203. if (rc)
  204. ri.Printf(PRINT_ALL, "_Log: pthread_mutex_lock returned %d: %s\n", rc, strerror(rc));
  205. fputs(msg,stderr);
  206. fflush(stderr);
  207. rc = pthread_mutex_unlock(&logMutex);
  208. if (rc)
  209. ri.Printf(PRINT_ALL, "_Log: pthread_mutex_unlock returned %d: %s\n", rc, strerror(rc));
  210. }
  211. #endif
  212. //
  213. // The main Q3 SMP API
  214. //
  215. static void (*glimpRenderThread)( void ) = NULL;
  216. static void *GLimp_RenderThreadWrapper(void *arg)
  217. {
  218. Com_Printf("Render thread starting\n");
  219. glimpRenderThread();
  220. #ifndef USE_CGLMACROS
  221. // Unbind the context before we die
  222. OSX_GLContextClearCurrent();
  223. #endif
  224. // Send one last message back to front end before we die...
  225. // This is somewhat of a hack.. fixme.
  226. if (rendererProcessingCommand) {
  227. SendMsg(&frontEndMsgPort, RenderCompletedMsg, NULL);
  228. rendererProcessingCommand = NO;
  229. }
  230. Com_Printf("Render thread terminating\n");
  231. return arg;
  232. }
  233. qboolean GLimp_SpawnRenderThread( void (*function)( void ) )
  234. {
  235. pthread_t renderThread;
  236. int rc;
  237. if (!portsInited) {
  238. portsInited = YES;
  239. MsgPortInit(&rendererMsgPort);
  240. MsgPortInit(&frontEndMsgPort);
  241. renderMsgOutstanding = NO;
  242. rendererProcessingCommand = NO;
  243. pthread_mutex_init(&logMutex, NULL);
  244. }
  245. glimpRenderThread = function;
  246. rc = pthread_create(&renderThread,
  247. NULL, // attributes
  248. GLimp_RenderThreadWrapper,
  249. NULL); // argument
  250. if (rc) {
  251. ri.Printf(PRINT_ALL, "pthread_create returned %d: %s", rc, strerror(rc));
  252. return qfalse;
  253. } else {
  254. rc = pthread_detach(renderThread);
  255. if (rc) {
  256. ri.Printf(PRINT_ALL, "pthread_detach returned %d: %s", rc, strerror(rc));
  257. }
  258. }
  259. return qtrue;
  260. }
  261. static volatile void *smpData;
  262. // TJW - This is calling in the rendering thread to wait until another
  263. // command buffer is ready. The command buffer returned might be NULL,
  264. // indicating that the rendering thread should exit.
  265. void *GLimp_RendererSleep(void)
  266. {
  267. //_Log(__PRETTY_FUNCTION__ " entered");
  268. unsigned int msgCode;
  269. void *msgData;
  270. GLSTAMP("GLimp_RendererSleep start", 0);
  271. #ifndef USE_CGLMACROS
  272. // Clear the current context while we sleep so the main thread can access it
  273. OSX_GLContextClearCurrent();
  274. #endif
  275. // Let the main thread we are idle and that no work is queued
  276. //_Log("rs0\n");
  277. /* If we actually had some work to do, then tell the front end we completed it. */
  278. if (rendererProcessingCommand) {
  279. SendMsg(&frontEndMsgPort, RenderCompletedMsg, NULL);
  280. rendererProcessingCommand = NO;
  281. }
  282. // Wait for new msg
  283. for (;;) {
  284. WaitMsg(&rendererMsgPort, &msgCode, &msgData);
  285. if (1 || msgCode == RenderCommandMsg) {
  286. smpData = msgData;
  287. break;
  288. } else {
  289. printf("renderer received unknown message: %d\n",msgCode);
  290. }
  291. }
  292. #ifndef USE_CGLMACROS
  293. // We are going to render a frame... retake the context
  294. OSX_GLContextSetCurrent();
  295. #endif
  296. rendererProcessingCommand = YES;
  297. GLSTAMP("GLimp_RendererSleep end", 0);
  298. return (void *)smpData;
  299. }
  300. // TJW - This is from the main thread to wait until the rendering thread
  301. // has completed the command buffer that it has
  302. void GLimp_FrontEndSleep(void)
  303. {
  304. unsigned int msgCode;
  305. void *msgData;
  306. GLSTAMP("GLimp_FrontEndSleep start", 1);
  307. if (renderMsgOutstanding) {
  308. for (;;) {
  309. WaitMsg(&frontEndMsgPort, &msgCode, &msgData);
  310. if(1 || msgCode == RenderCompletedMsg) {
  311. break;
  312. } else {
  313. printf("front end received unknown message: %d\n",msgCode);
  314. }
  315. }
  316. renderMsgOutstanding = NO;
  317. }
  318. #ifndef USE_CGLMACROS
  319. // We are done waiting for the background thread, take the current context back.
  320. OSX_GLContextSetCurrent();
  321. #endif
  322. GLSTAMP("GLimp_FrontEndSleep end", 1);
  323. }
  324. // TJW - This is called in the main thread to issue another command
  325. // buffer to the rendering thread. This is always called AFTER
  326. // GLimp_FrontEndSleep, so we know that there is no command
  327. // pending in 'smpData'.
  328. void GLimp_WakeRenderer( void *data )
  329. {
  330. GLSTAMP("GLimp_WakeRenderer start", 1);
  331. #ifndef USE_CGLMACROS
  332. // We want the background thread to draw stuff. Give up the current context
  333. OSX_GLContextClearCurrent();
  334. #endif
  335. SendMsg(&rendererMsgPort, RenderCommandMsg, data);
  336. // Don't set flag saying that the renderer is processing something if it's just
  337. // being told to exit.
  338. //if(data != NULL)
  339. renderMsgOutstanding = YES;
  340. GLSTAMP("GLimp_WakeRenderer end", 1);
  341. }