macosx_glimp.m 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115
  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 "macosx_timers.h"
  23. #import <AppKit/AppKit.h>
  24. #import <Foundation/Foundation.h>
  25. #import <mach-o/dyld.h>
  26. #import <mach/mach.h>
  27. #import <mach/mach_error.h>
  28. cvar_t *r_allowSoftwareGL; // don't abort out if the pixelformat claims software
  29. cvar_t *r_enablerender; // Enable actual rendering
  30. cvar_t *r_appleTransformHint; // Enable Apple transform hint
  31. static void GLW_InitExtensions( void );
  32. static qboolean CreateGameWindow( qboolean isSecondTry );
  33. static unsigned long Sys_QueryVideoMemory();
  34. static CGDisplayErr Sys_CaptureActiveDisplays(void);
  35. glwstate_t glw_state;
  36. qboolean Sys_IsHidden = qfalse;
  37. #ifdef OMNI_TIMER
  38. OTStampList glThreadStampList;
  39. #endif
  40. @interface NSOpenGLContext (CGLContextAccess)
  41. - (CGLContextObj) cglContext;
  42. @end
  43. @implementation NSOpenGLContext (CGLContextAccess)
  44. - (CGLContextObj) cglContext;
  45. {
  46. return _contextAuxiliary;
  47. }
  48. @end
  49. /*
  50. ============
  51. CheckErrors
  52. ============
  53. */
  54. void CheckErrors( void )
  55. {
  56. GLenum err;
  57. err = qglGetError();
  58. if ( err != GL_NO_ERROR ) {
  59. ri.Error( ERR_FATAL, "glGetError: %s\n", qglGetString( err ) );
  60. }
  61. }
  62. #if !defined(NDEBUG) && defined(QGL_CHECK_GL_ERRORS)
  63. unsigned int QGLBeginStarted = 0;
  64. void QGLErrorBreak(void)
  65. {
  66. }
  67. void QGLCheckError(const char *message)
  68. {
  69. GLenum error;
  70. static unsigned int errorCount = 0;
  71. error = _glGetError();
  72. if (error != GL_NO_ERROR) {
  73. if (errorCount == 100) {
  74. Com_Printf("100 GL errors printed ... disabling further error reporting.\n");
  75. } else if (errorCount < 100) {
  76. if (errorCount == 0) {
  77. fprintf(stderr, "BREAK ON QGLErrorBreak to stop at the GL errors\n");
  78. }
  79. fprintf(stderr, "OpenGL Error(%s): 0x%04x -- %s\n", message, (int)error, gluErrorString(error));
  80. QGLErrorBreak();
  81. }
  82. errorCount++;
  83. }
  84. }
  85. #endif
  86. /*
  87. ** GLimp_SetMode
  88. */
  89. qboolean GLimp_SetMode( qboolean isSecondTry )
  90. {
  91. if ( !CreateGameWindow(isSecondTry) ) {
  92. ri.Printf( PRINT_ALL, "GLimp_Init: window could not be created!\n" );
  93. return qfalse;
  94. }
  95. // draw something to show that GL is alive
  96. if (r_enablerender->integer) {
  97. qglClearColor( 0.5, 0.5, 0.7, 0 );
  98. qglClear( GL_COLOR_BUFFER_BIT );
  99. GLimp_EndFrame();
  100. qglClearColor( 0.5, 0.5, 0.7, 0 );
  101. qglClear( GL_COLOR_BUFFER_BIT );
  102. GLimp_EndFrame();
  103. }
  104. Sys_UnfadeScreen(Sys_DisplayToUse(), NULL);
  105. CheckErrors();
  106. return qtrue;
  107. }
  108. /*
  109. =================
  110. GetPixelAttributes
  111. =================
  112. */
  113. #define ADD_ATTR(x) \
  114. do { \
  115. if (attributeIndex >= attributeSize) { \
  116. attributeSize *= 2; \
  117. pixelAttributes = NSZoneRealloc(NULL, pixelAttributes, sizeof(*pixelAttributes) * attributeSize); \
  118. } \
  119. pixelAttributes[attributeIndex] = x; \
  120. attributeIndex++; \
  121. if (verbose) { \
  122. ri.Printf(PRINT_ALL, "Adding pixel attribute: %d (%s)\n", x, #x); \
  123. } \
  124. } while(0)
  125. static NSOpenGLPixelFormatAttribute *GetPixelAttributes()
  126. {
  127. NSOpenGLPixelFormatAttribute *pixelAttributes;
  128. unsigned int attributeIndex = 0;
  129. unsigned int attributeSize = 128;
  130. int verbose = 0;
  131. unsigned int colorDepth;
  132. verbose = r_verbose->integer;
  133. pixelAttributes = NSZoneMalloc(NULL, sizeof(*pixelAttributes) * attributeSize);
  134. if (r_fullscreen->integer) {
  135. ADD_ATTR(NSOpenGLPFAFullScreen);
  136. // Since we are fullscreen, specify the screen that we need.
  137. ADD_ATTR(NSOpenGLPFAScreenMask);
  138. ADD_ATTR(CGDisplayIDToOpenGLDisplayMask(Sys_DisplayToUse()));
  139. }
  140. // Require hardware acceleration unless otherwise directed
  141. if (!r_allowSoftwareGL->integer) {
  142. ADD_ATTR(NSOpenGLPFAAccelerated);
  143. }
  144. // Require double-buffer
  145. ADD_ATTR(NSOpenGLPFADoubleBuffer);
  146. // Specify the number of color bits. If we don't have a valid specified value or we are not full screen, use the current display mode's value.
  147. ADD_ATTR(NSOpenGLPFAColorSize);
  148. colorDepth = r_colorbits->integer;
  149. if (colorDepth < 16)
  150. colorDepth = 16;
  151. else if (colorDepth > 16)
  152. colorDepth = 32;
  153. if (!r_fullscreen->integer)
  154. colorDepth = [[glw_state.desktopMode objectForKey: (id)kCGDisplayBitsPerPixel] intValue];
  155. ADD_ATTR(colorDepth);
  156. // Specify the number of depth bits
  157. ADD_ATTR(NSOpenGLPFADepthSize);
  158. ADD_ATTR(r_depthbits->integer ? r_depthbits->integer : 16);
  159. // Specify the number of stencil bits
  160. if (r_stencilbits->integer) {
  161. ADD_ATTR(NSOpenGLPFAStencilSize);
  162. ADD_ATTR(r_stencilbits->integer);
  163. }
  164. // Terminate the list
  165. ADD_ATTR(0);
  166. return pixelAttributes;
  167. }
  168. // Needs to be visible to Q3Controller.m.
  169. void Sys_UpdateWindowMouseInputRect(void)
  170. {
  171. NSRect windowRect, screenRect;
  172. NSScreen *screen;
  173. // It appears we need to flip the coordinate system here. This means we need
  174. // to know the size of the screen.
  175. screen = [glw_state.window screen];
  176. screenRect = [screen frame];
  177. windowRect = [glw_state.window frame];
  178. windowRect.origin.y = screenRect.size.height - (windowRect.origin.y + windowRect.size.height);
  179. Sys_SetMouseInputRect(CGRectMake(windowRect.origin.x, windowRect.origin.y,
  180. windowRect.size.width, windowRect.size.height));
  181. }
  182. // This is needed since CGReleaseAllDisplays() restores the gamma on the displays and we want to fade it up rather than just flickering all the displays
  183. static void ReleaseAllDisplays()
  184. {
  185. CGDisplayCount displayIndex;
  186. Com_Printf("Releasing displays\n");
  187. for (displayIndex = 0; displayIndex < glw_state.displayCount; displayIndex++) {
  188. CGDisplayRelease(glw_state.originalDisplayGammaTables[displayIndex].display);
  189. }
  190. }
  191. /*
  192. =================
  193. CreateGameWindow
  194. =================
  195. */
  196. static qboolean CreateGameWindow( qboolean isSecondTry )
  197. {
  198. const char *windowed[] = { "Windowed", "Fullscreen" };
  199. int current_mode;
  200. NSOpenGLPixelFormatAttribute *pixelAttributes;
  201. NSOpenGLPixelFormat *pixelFormat;
  202. CGDisplayErr err;
  203. // get mode info
  204. current_mode = r_mode->integer;
  205. glConfig.isFullscreen = (r_fullscreen->integer != 0);
  206. glw_state.desktopMode = (NSDictionary *)CGDisplayCurrentMode(glw_state.display);
  207. if (!glw_state.desktopMode) {
  208. ri.Error(ERR_FATAL, "Could not get current graphics mode for display 0x%08x\n", glw_state.display);
  209. }
  210. #if 0
  211. ri.Printf( PRINT_ALL, "... desktop mode %d = %dx%d %s\n", glw_state.desktopMode,
  212. glw_state.desktopDesc.width, glw_state.desktopDesc.height,
  213. depthStrings[glw_state.desktopDesc.depth]);
  214. #endif
  215. ri.Printf( PRINT_ALL, "...setting mode %d:\n", current_mode );
  216. if ( !R_GetModeInfo( &glConfig.vidWidth, &glConfig.vidHeight, &glConfig.windowAspect, current_mode ) ) {
  217. ri.Printf( PRINT_ALL, " invalid mode\n" );
  218. return qfalse;
  219. }
  220. ri.Printf( PRINT_ALL, " %d %d %s\n", glConfig.vidWidth, glConfig.vidHeight, windowed[glConfig.isFullscreen] );
  221. if (glConfig.isFullscreen) {
  222. // We'll set up the screen resolution first in case that effects the list of pixel
  223. // formats that are available (for example, a smaller frame buffer might mean more
  224. // bits for depth/stencil buffers). Allow stretched video modes if we are in fallback mode.
  225. glw_state.gameMode = Sys_GetMatchingDisplayMode(isSecondTry);
  226. if (!glw_state.gameMode) {
  227. ri.Printf( PRINT_ALL, "Unable to find requested display mode.\n");
  228. return qfalse;
  229. }
  230. // Fade all screens to black
  231. Sys_FadeScreens();
  232. err = Sys_CaptureActiveDisplays();
  233. if ( err != CGDisplayNoErr ) {
  234. CGDisplayRestoreColorSyncSettings();
  235. ri.Printf( PRINT_ALL, " Unable to capture displays err = %d\n", err );
  236. return qfalse;
  237. }
  238. err = CGDisplaySwitchToMode(glw_state.display, (CFDictionaryRef)glw_state.gameMode);
  239. if ( err != CGDisplayNoErr ) {
  240. CGDisplayRestoreColorSyncSettings();
  241. ReleaseAllDisplays();
  242. ri.Printf( PRINT_ALL, " Unable to set display mode, err = %d\n", err );
  243. return qfalse;
  244. }
  245. } else {
  246. glw_state.gameMode = glw_state.desktopMode;
  247. }
  248. // Get the GL pixel format
  249. pixelAttributes = GetPixelAttributes();
  250. pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes: pixelAttributes] autorelease];
  251. NSZoneFree(NULL, pixelAttributes);
  252. if (!pixelFormat) {
  253. CGDisplayRestoreColorSyncSettings();
  254. CGDisplaySwitchToMode(glw_state.display, (CFDictionaryRef)glw_state.desktopMode);
  255. ReleaseAllDisplays();
  256. ri.Printf( PRINT_ALL, " No pixel format found\n");
  257. return qfalse;
  258. }
  259. // Create a context with the desired pixel attributes
  260. OSX_SetGLContext([[NSOpenGLContext alloc] initWithFormat: pixelFormat shareContext: nil]);
  261. if (!OSX_GetNSGLContext()) {
  262. CGDisplayRestoreColorSyncSettings();
  263. CGDisplaySwitchToMode(glw_state.display, (CFDictionaryRef)glw_state.desktopMode);
  264. ReleaseAllDisplays();
  265. ri.Printf(PRINT_ALL, "... +[NSOpenGLContext createWithFormat:share:] failed.\n" );
  266. return qfalse;
  267. }
  268. if (!glConfig.isFullscreen) {
  269. cvar_t *vid_xpos;
  270. cvar_t *vid_ypos;
  271. NSRect windowRect;
  272. vid_xpos = ri.Cvar_Get( "vid_xpos", "100", CVAR_ARCHIVE );
  273. vid_ypos = ri.Cvar_Get( "vid_ypos", "100", CVAR_ARCHIVE );
  274. // Create a window of the desired size
  275. windowRect.origin.x = vid_xpos->integer;
  276. windowRect.origin.y = vid_ypos->integer;
  277. windowRect.size.width = glConfig.vidWidth;
  278. windowRect.size.height = glConfig.vidHeight;
  279. glw_state.window = [[NSWindow alloc] initWithContentRect:windowRect
  280. styleMask:NSTitledWindowMask
  281. backing:NSBackingStoreRetained
  282. defer:NO];
  283. [glw_state.window setTitle: @"Quake3"];
  284. [glw_state.window orderFront: nil];
  285. // Always get mouse moved events (if mouse support is turned off (rare)
  286. // the event system will filter them out.
  287. [glw_state.window setAcceptsMouseMovedEvents: YES];
  288. // Direct the context to draw in this window
  289. [OSX_GetNSGLContext() setView: [glw_state.window contentView]];
  290. // Sync input rect with where the window actually is...
  291. Sys_UpdateWindowMouseInputRect();
  292. } else {
  293. CGLError err;
  294. err = CGLSetFullScreen(OSX_GetCGLContext());
  295. if (err) {
  296. CGDisplayRestoreColorSyncSettings();
  297. CGDisplaySwitchToMode(glw_state.display, (CFDictionaryRef)glw_state.desktopMode);
  298. ReleaseAllDisplays();
  299. Com_Printf("CGLSetFullScreen -> %d (%s)\n", err, CGLErrorString(err));
  300. return qfalse;
  301. }
  302. Sys_SetMouseInputRect(CGDisplayBounds(glw_state.display));
  303. }
  304. #ifndef USE_CGLMACROS
  305. // Make this the current context
  306. OSX_GLContextSetCurrent();
  307. #endif
  308. // Store off the pixel format attributes that we actually got
  309. [pixelFormat getValues: (long *) &glConfig.colorBits forAttribute: NSOpenGLPFAColorSize forVirtualScreen: 0];
  310. [pixelFormat getValues: (long *) &glConfig.depthBits forAttribute: NSOpenGLPFADepthSize forVirtualScreen: 0];
  311. [pixelFormat getValues: (long *) &glConfig.stencilBits forAttribute: NSOpenGLPFAStencilSize forVirtualScreen: 0];
  312. glConfig.displayFrequency = [[glw_state.gameMode objectForKey: (id)kCGDisplayRefreshRate] intValue];
  313. ri.Printf(PRINT_ALL, "ok\n" );
  314. return qtrue;
  315. }
  316. // This can be used to temporarily disassociate the GL context from the screen so that CoreGraphics can be used to draw to the screen.
  317. void Sys_PauseGL ()
  318. {
  319. if (!glw_state.glPauseCount) {
  320. qglFinish (); // must do this to ensure the queue is complete
  321. // Have to call both to actually deallocate kernel resources and free the NSSurface
  322. CGLClearDrawable(OSX_GetCGLContext());
  323. [OSX_GetNSGLContext() clearDrawable];
  324. }
  325. glw_state.glPauseCount++;
  326. }
  327. // This can be used to reverse the pausing caused by Sys_PauseGL()
  328. void Sys_ResumeGL ()
  329. {
  330. if (glw_state.glPauseCount) {
  331. glw_state.glPauseCount--;
  332. if (!glw_state.glPauseCount) {
  333. if (!glConfig.isFullscreen) {
  334. [OSX_GetNSGLContext() setView: [glw_state.window contentView]];
  335. } else {
  336. CGLError err;
  337. err = CGLSetFullScreen(OSX_GetCGLContext());
  338. if (err)
  339. Com_Printf("CGLSetFullScreen -> %d (%s)\n", err, CGLErrorString(err));
  340. }
  341. }
  342. }
  343. }
  344. /*
  345. ===================
  346. GLimp_Init
  347. Don't return unless OpenGL has been properly initialized
  348. ===================
  349. */
  350. static void GLImp_Toggle_Renderer_f(void)
  351. {
  352. ri.Cvar_Set("r_enablerender", r_enablerender->integer ? "0" : "1");
  353. }
  354. #ifdef OMNI_TIMER
  355. static void GLImp_Dump_Stamp_List_f(void)
  356. {
  357. OTStampListDumpToFile(glThreadStampList, "/tmp/gl_stamps");
  358. }
  359. #endif
  360. void GLimp_Init( void )
  361. {
  362. static BOOL addedCommands = NO;
  363. cvar_t *lastValidRenderer = ri.Cvar_Get( "r_lastValidRenderer", "(uninitialized)", CVAR_ARCHIVE );
  364. char *buf;
  365. if (!addedCommands) {
  366. addedCommands = YES;
  367. #ifdef OMNI_TIMER
  368. glThreadStampList = OTStampListCreate(64);
  369. Cmd_AddCommand ("dump_stamp_list", GLImp_Dump_Stamp_List_f);
  370. #endif
  371. Cmd_AddCommand ("toggle_renderer", GLImp_Toggle_Renderer_f);
  372. }
  373. ri.Printf( PRINT_ALL, "Initializing OpenGL subsystem\n" );
  374. ri.Printf( PRINT_ALL, " Last renderer was '%s'\n", lastValidRenderer->string);
  375. ri.Printf( PRINT_ALL, " r_fullscreen = %d\n", r_fullscreen->integer);
  376. memset( &glConfig, 0, sizeof( glConfig ) );
  377. // We only allow changing the gamma if we are full screen
  378. glConfig.deviceSupportsGamma = (r_fullscreen->integer != 0);
  379. if (glConfig.deviceSupportsGamma) {
  380. Sys_StoreGammaTables();
  381. }
  382. r_allowSoftwareGL = ri.Cvar_Get( "r_allowSoftwareGL", "0", CVAR_LATCH );
  383. r_enablerender = ri.Cvar_Get("r_enablerender", "1", 0 );
  384. if (Sys_QueryVideoMemory() == 0 && !r_allowSoftwareGL->integer) {
  385. ri.Error( ERR_FATAL, "Could not initialize OpenGL. There does not appear to be an OpenGL-supported video card in your system.\n" );
  386. }
  387. if ( ! GLimp_SetMode(qfalse) ) {
  388. // fall back to the known-good mode
  389. ri.Cvar_Set( "r_fullscreen", "1" );
  390. ri.Cvar_Set( "r_mode", "3" );
  391. ri.Cvar_Set( "r_stereo", "0" );
  392. ri.Cvar_Set( "r_depthBits", "16" );
  393. ri.Cvar_Set( "r_colorBits", "16" );
  394. ri.Cvar_Set( "r_stencilBits", "0" );
  395. if ( GLimp_SetMode(qtrue) ) {
  396. ri.Printf( PRINT_ALL, "------------------\n" );
  397. return;
  398. }
  399. ri.Error( ERR_FATAL, "Could not initialize OpenGL\n" );
  400. return;
  401. }
  402. ri.Printf( PRINT_ALL, "------------------\n" );
  403. // get our config strings
  404. Q_strncpyz( glConfig.vendor_string, (const char *)qglGetString (GL_VENDOR), sizeof( glConfig.vendor_string ) );
  405. Q_strncpyz( glConfig.renderer_string, (const char *)qglGetString (GL_RENDERER), sizeof( glConfig.renderer_string ) );
  406. Q_strncpyz( glConfig.version_string, (const char *)qglGetString (GL_VERSION), sizeof( glConfig.version_string ) );
  407. Q_strncpyz( glConfig.extensions_string, (const char *)qglGetString (GL_EXTENSIONS), sizeof( glConfig.extensions_string ) );
  408. //
  409. // chipset specific configuration
  410. //
  411. buf = malloc(strlen(glConfig.renderer_string) + 1);
  412. strcpy( buf, glConfig.renderer_string );
  413. Q_strlwr( buf );
  414. ri.Cvar_Set( "r_lastValidRenderer", glConfig.renderer_string );
  415. free(buf);
  416. GLW_InitExtensions();
  417. #ifndef USE_CGLMACROS
  418. if (!r_enablerender->integer)
  419. OSX_GLContextClearCurrent();
  420. #endif
  421. }
  422. /*
  423. ** GLimp_EndFrame
  424. **
  425. ** Responsible for doing a swapbuffers and possibly for other stuff
  426. ** as yet to be determined. Probably better not to make this a GLimp
  427. ** function and instead do a call to GLimp_SwapBuffers.
  428. */
  429. void GLimp_EndFrame (void)
  430. {
  431. GLSTAMP("GLimp_EndFrame start", 0);
  432. //
  433. // swapinterval stuff
  434. //
  435. if ( r_swapInterval->modified ) {
  436. r_swapInterval->modified = qfalse;
  437. if ( !glConfig.stereoEnabled ) { // why?
  438. [[NSOpenGLContext currentContext] setValues: (long *)&r_swapInterval->integer
  439. forParameter: NSOpenGLCPSwapInterval];
  440. }
  441. }
  442. #if !defined(NDEBUG) && defined(QGL_CHECK_GL_ERRORS)
  443. QGLCheckError("GLimp_EndFrame");
  444. #endif
  445. if (!glw_state.glPauseCount && !Sys_IsHidden) {
  446. glw_state.bufferSwapCount++;
  447. [OSX_GetNSGLContext() flushBuffer];
  448. }
  449. // Enable turning off GL at any point for performance testing
  450. if (OSX_GLContextIsCurrent() != r_enablerender->integer) {
  451. if (r_enablerender->integer) {
  452. Com_Printf("--- Enabling Renderer ---\n");
  453. OSX_GLContextSetCurrent();
  454. } else {
  455. Com_Printf("--- Disabling Renderer ---\n");
  456. OSX_GLContextClearCurrent();
  457. }
  458. }
  459. GLSTAMP("GLimp_EndFrame end", 0);
  460. }
  461. /*
  462. ** GLimp_Shutdown
  463. **
  464. ** This routine does all OS specific shutdown procedures for the OpenGL
  465. ** subsystem. Under OpenGL this means NULLing out the current DC and
  466. ** HGLRC, deleting the rendering context, and releasing the DC acquired
  467. ** for the window. The state structure is also nulled out.
  468. **
  469. */
  470. static void _GLimp_RestoreOriginalVideoSettings()
  471. {
  472. CGDisplayErr err;
  473. // CGDisplayCurrentMode lies because we've captured the display and thus we won't
  474. // get any notifications about what the current display mode really is. For now,
  475. // we just always force it back to what mode we remember the desktop being in.
  476. if (glConfig.isFullscreen) {
  477. err = CGDisplaySwitchToMode(glw_state.display, (CFDictionaryRef)glw_state.desktopMode);
  478. if ( err != CGDisplayNoErr )
  479. ri.Printf( PRINT_ALL, " Unable to restore display mode!\n" );
  480. ReleaseAllDisplays();
  481. }
  482. }
  483. void GLimp_Shutdown( void )
  484. {
  485. CGDisplayCount displayIndex;
  486. Com_Printf("----- Shutting down GL -----\n");
  487. Sys_FadeScreen(Sys_DisplayToUse());
  488. if (OSX_GetNSGLContext()) {
  489. #ifndef USE_CGLMACROS
  490. OSX_GLContextClearCurrent();
  491. #endif
  492. // Have to call both to actually deallocate kernel resources and free the NSSurface
  493. CGLClearDrawable(OSX_GetCGLContext());
  494. [OSX_GetNSGLContext() clearDrawable];
  495. [OSX_GetNSGLContext() release];
  496. OSX_SetGLContext((id)nil);
  497. }
  498. _GLimp_RestoreOriginalVideoSettings();
  499. Sys_UnfadeScreens();
  500. // Restore the original gamma if needed.
  501. if (glConfig.deviceSupportsGamma) {
  502. Com_Printf("Restoring ColorSync settings\n");
  503. CGDisplayRestoreColorSyncSettings();
  504. }
  505. if (glw_state.window) {
  506. [glw_state.window release];
  507. glw_state.window = nil;
  508. }
  509. if (glw_state.log_fp) {
  510. fclose(glw_state.log_fp);
  511. glw_state.log_fp = 0;
  512. }
  513. for (displayIndex = 0; displayIndex < glw_state.displayCount; displayIndex++) {
  514. free(glw_state.originalDisplayGammaTables[displayIndex].red);
  515. free(glw_state.originalDisplayGammaTables[displayIndex].blue);
  516. free(glw_state.originalDisplayGammaTables[displayIndex].green);
  517. }
  518. free(glw_state.originalDisplayGammaTables);
  519. if (glw_state.tempTable.red) {
  520. free(glw_state.tempTable.red);
  521. free(glw_state.tempTable.blue);
  522. free(glw_state.tempTable.green);
  523. }
  524. if (glw_state.inGameTable.red) {
  525. free(glw_state.inGameTable.red);
  526. free(glw_state.inGameTable.blue);
  527. free(glw_state.inGameTable.green);
  528. }
  529. memset(&glConfig, 0, sizeof(glConfig));
  530. memset(&glState, 0, sizeof(glState));
  531. memset(&glw_state, 0, sizeof(glw_state));
  532. Com_Printf("----- Done shutting down GL -----\n");
  533. }
  534. /*
  535. ===============
  536. GLimp_LogComment
  537. ===============
  538. */
  539. void GLimp_LogComment( char *comment ) {
  540. }
  541. /*
  542. ===============
  543. GLimp_SetGamma
  544. ===============
  545. */
  546. void GLimp_SetGamma(unsigned char red[256],
  547. unsigned char green[256],
  548. unsigned char blue[256])
  549. {
  550. CGGammaValue redGamma[256], greenGamma[256], blueGamma[256];
  551. CGTableCount i;
  552. CGDisplayErr err;
  553. if (!glConfig.deviceSupportsGamma)
  554. return;
  555. for (i = 0; i < 256; i++) {
  556. redGamma[i] = red[i] / 255.0;
  557. greenGamma[i] = green[i] / 255.0;
  558. blueGamma[i] = blue[i] / 255.0;
  559. }
  560. err = CGSetDisplayTransferByTable(glw_state.display, 256, redGamma, greenGamma, blueGamma);
  561. if (err != CGDisplayNoErr) {
  562. Com_Printf("GLimp_SetGamma: CGSetDisplayTransferByByteTable returned %d.\n", err);
  563. }
  564. // Store the gamma table that we ended up using so we can reapply it later when unhiding or to work around the bug where if you leave the game sitting and the monitor sleeps, when it wakes, the gamma isn't reset.
  565. glw_state.inGameTable.display = glw_state.display;
  566. Sys_GetGammaTable(&glw_state.inGameTable);
  567. }
  568. qboolean GLimp_ChangeMode( int mode )
  569. {
  570. qboolean result;
  571. int oldvalue = r_mode->integer;
  572. Com_Printf("*** GLimp_ChangeMode\n");
  573. r_mode->integer = mode;
  574. if (!(result = GLimp_SetMode(qfalse)))
  575. r_mode->integer = oldvalue;
  576. return result;
  577. }
  578. /*****************************************************************************/
  579. void *qwglGetProcAddress(const char *name)
  580. {
  581. NSSymbol symbol;
  582. char *symbolName;
  583. // Prepend a '_' for the Unix C symbol mangling convention
  584. symbolName = malloc(strlen(name) + 2);
  585. strcpy(symbolName + 1, name);
  586. symbolName[0] = '_';
  587. if (NSIsSymbolNameDefined(symbolName))
  588. symbol = NSLookupAndBindSymbol(symbolName);
  589. else
  590. symbol = NULL;
  591. free(symbolName);
  592. if (!symbol)
  593. // shouldn't happen ...
  594. return NULL;
  595. return NSAddressOfSymbol(symbol);
  596. }
  597. /*
  598. ** GLW_InitExtensions
  599. */
  600. static void GLW_InitExtensions( void )
  601. {
  602. if ( !r_allowExtensions->integer )
  603. {
  604. ri.Printf( PRINT_ALL, "*** IGNORING OPENGL EXTENSIONS ***\n" );
  605. return;
  606. }
  607. ri.Printf( PRINT_ALL, "Initializing OpenGL extensions\n" );
  608. ri.Printf( PRINT_ALL, "... Supported extensions are %s\n", glConfig.extensions_string);
  609. // GL_S3_s3tc
  610. glConfig.textureCompression = TC_NONE;
  611. if ( strstr( glConfig.extensions_string, "GL_S3_s3tc" ) )
  612. {
  613. if ( r_ext_compressed_textures->integer )
  614. {
  615. glConfig.textureCompression = TC_S3TC;
  616. ri.Printf( PRINT_ALL, "...using GL_S3_s3tc\n" );
  617. }
  618. else
  619. {
  620. glConfig.textureCompression = TC_NONE;
  621. ri.Printf( PRINT_ALL, "...ignoring GL_S3_s3tc\n" );
  622. }
  623. }
  624. else
  625. {
  626. ri.Printf( PRINT_ALL, "...GL_S3_s3tc not found\n" );
  627. }
  628. #ifdef GL_EXT_texture_env_add
  629. // GL_EXT_texture_env_add
  630. glConfig.textureEnvAddAvailable = qfalse;
  631. if ( strstr( glConfig.extensions_string, "GL_EXT_texture_env_add" ) )
  632. {
  633. if ( r_ext_texture_env_add->integer )
  634. {
  635. glConfig.textureEnvAddAvailable = qtrue;
  636. ri.Printf( PRINT_ALL, "...using GL_EXT_texture_env_add\n" );
  637. }
  638. else
  639. {
  640. glConfig.textureEnvAddAvailable = qfalse;
  641. ri.Printf( PRINT_ALL, "...ignoring GL_EXT_texture_env_add\n" );
  642. }
  643. }
  644. else
  645. {
  646. ri.Printf( PRINT_ALL, "...GL_EXT_texture_env_add not found\n" );
  647. }
  648. #endif
  649. #ifdef GL_ARB_texture_env_add
  650. // GL_ARB_texture_env_add -- only if we didn't find GL_EXT_texture_env_add
  651. if (!glConfig.textureEnvAddAvailable) {
  652. if ( strstr( glConfig.extensions_string, "GL_ARB_texture_env_add" ) )
  653. {
  654. if ( r_ext_texture_env_add->integer )
  655. {
  656. glConfig.textureEnvAddAvailable = qtrue;
  657. ri.Printf( PRINT_ALL, "...using GL_ARB_texture_env_add\n" );
  658. }
  659. else
  660. {
  661. glConfig.textureEnvAddAvailable = qfalse;
  662. ri.Printf( PRINT_ALL, "...ignoring GL_ARB_texture_env_add\n" );
  663. }
  664. }
  665. else
  666. {
  667. ri.Printf( PRINT_ALL, "...GL_ARB_texture_env_add not found\n" );
  668. }
  669. }
  670. #endif
  671. #if 0 // Win32 does this differently than we do -- I'll provide a C function that looks the same
  672. // that will do the correct ObjC stuff
  673. // WGL_EXT_swap_control
  674. qwglSwapIntervalEXT = ( BOOL (WINAPI *)(int)) qwglGetProcAddress( "wglSwapIntervalEXT" );
  675. if ( qwglSwapIntervalEXT )
  676. {
  677. ri.Printf( PRINT_ALL, "...using WGL_EXT_swap_control\n" );
  678. r_swapInterval->modified = qtrue; // force a set next frame
  679. }
  680. else
  681. {
  682. ri.Printf( PRINT_ALL, "...WGL_EXT_swap_control not found\n" );
  683. }
  684. #else
  685. if (r_swapInterval) {
  686. ri.Printf( PRINT_ALL, "...using +[NSOpenGLContext setParameter:] for qwglSwapIntervalEXT\n" );
  687. r_swapInterval->modified = qtrue; // force a set next frame
  688. }
  689. #endif
  690. // GL_ARB_multitexture
  691. qglMultiTexCoord2fARB = NULL;
  692. qglActiveTextureARB = NULL;
  693. qglClientActiveTextureARB = NULL;
  694. if ( strstr( glConfig.extensions_string, "GL_ARB_multitexture" ) )
  695. {
  696. if ( r_ext_multitexture->integer )
  697. {
  698. qglMultiTexCoord2fARB = ( PFNGLMULTITEXCOORD2FARBPROC ) qwglGetProcAddress( "glMultiTexCoord2fARB" );
  699. qglActiveTextureARB = ( PFNGLACTIVETEXTUREARBPROC ) qwglGetProcAddress( "glActiveTextureARB" );
  700. qglClientActiveTextureARB = ( PFNGLCLIENTACTIVETEXTUREARBPROC ) qwglGetProcAddress( "glClientActiveTextureARB" );
  701. if ( qglActiveTextureARB )
  702. {
  703. qglGetIntegerv( GL_MAX_ACTIVE_TEXTURES_ARB, (GLint *)&glConfig.maxActiveTextures );
  704. if ( glConfig.maxActiveTextures > 1 )
  705. {
  706. ri.Printf( PRINT_ALL, "...using GL_ARB_multitexture\n" );
  707. }
  708. else
  709. {
  710. qglMultiTexCoord2fARB = NULL;
  711. qglActiveTextureARB = NULL;
  712. qglClientActiveTextureARB = NULL;
  713. ri.Printf( PRINT_ALL, "...not using GL_ARB_multitexture, < 2 texture units\n" );
  714. }
  715. }
  716. }
  717. else
  718. {
  719. ri.Printf( PRINT_ALL, "...ignoring GL_ARB_multitexture\n" );
  720. }
  721. }
  722. else
  723. {
  724. ri.Printf( PRINT_ALL, "...GL_ARB_multitexture not found\n" );
  725. }
  726. // GL_EXT_compiled_vertex_array
  727. qglLockArraysEXT = NULL;
  728. qglUnlockArraysEXT = NULL;
  729. if ( strstr( glConfig.extensions_string, "GL_EXT_compiled_vertex_array" ) && ( glConfig.hardwareType != GLHW_RIVA128 ) )
  730. {
  731. if ( r_ext_compiled_vertex_array->integer )
  732. {
  733. ri.Printf( PRINT_ALL, "...using GL_EXT_compiled_vertex_array\n" );
  734. qglLockArraysEXT = ( void ( APIENTRY * )( GLint, GLint ) ) qwglGetProcAddress( "glLockArraysEXT" );
  735. qglUnlockArraysEXT = ( void ( APIENTRY * )( void ) ) qwglGetProcAddress( "glUnlockArraysEXT" );
  736. if (!qglLockArraysEXT || !qglUnlockArraysEXT) {
  737. ri.Error (ERR_FATAL, "bad getprocaddress\n");
  738. }
  739. }
  740. else
  741. {
  742. ri.Printf( PRINT_ALL, "...ignoring GL_EXT_compiled_vertex_array\n" );
  743. }
  744. }
  745. else
  746. {
  747. ri.Printf( PRINT_ALL, "...GL_EXT_compiled_vertex_array not found\n" );
  748. }
  749. #ifdef GL_APPLE_transform_hint
  750. if ( strstr( glConfig.extensions_string, "GL_APPLE_transform_hint" ) ) {
  751. r_appleTransformHint = ri.Cvar_Get("r_appleTransformHint", "1", CVAR_ARCHIVE );
  752. if (r_appleTransformHint->value) {
  753. ri.Printf( PRINT_ALL, "...using GL_APPLE_transform_hint\n");
  754. qglHint(GL_TRANSFORM_HINT_APPLE, GL_FASTEST);
  755. CheckErrors();
  756. } else {
  757. ri.Printf( PRINT_ALL, "...ignoring using GL_APPLE_transform_hint\n");
  758. }
  759. } else {
  760. ri.Printf( PRINT_ALL, "...GL_APPLE_transform_hint not found\n" );
  761. }
  762. #endif
  763. }
  764. #define MAX_RENDERER_INFO_COUNT 128
  765. // Returns zero if there are no hardware renderers. Otherwise, returns the max memory across all renderers (on the presumption that the screen that we'll use has the most memory).
  766. static unsigned long Sys_QueryVideoMemory()
  767. {
  768. CGLError err;
  769. CGLRendererInfoObj rendererInfo, rendererInfos[MAX_RENDERER_INFO_COUNT];
  770. long rendererInfoIndex, rendererInfoCount = MAX_RENDERER_INFO_COUNT;
  771. long rendererIndex, rendererCount;
  772. long maxVRAM = 0, vram;
  773. long accelerated;
  774. long rendererID;
  775. long totalRenderers = 0;
  776. err = CGLQueryRendererInfo(CGDisplayIDToOpenGLDisplayMask(Sys_DisplayToUse()), rendererInfos, &rendererInfoCount);
  777. if (err) {
  778. Com_Printf("CGLQueryRendererInfo -> %d\n", err);
  779. return vram;
  780. }
  781. //Com_Printf("rendererInfoCount = %d\n", rendererInfoCount);
  782. for (rendererInfoIndex = 0; rendererInfoIndex < rendererInfoCount && totalRenderers < rendererInfoCount; rendererInfoIndex++) {
  783. rendererInfo = rendererInfos[rendererInfoIndex];
  784. //Com_Printf("rendererInfo: 0x%08x\n", rendererInfo);
  785. err = CGLDescribeRenderer(rendererInfo, 0, kCGLRPRendererCount, &rendererCount);
  786. if (err) {
  787. Com_Printf("CGLDescribeRenderer(kCGLRPRendererID) -> %d\n", err);
  788. continue;
  789. }
  790. //Com_Printf(" rendererCount: %d\n", rendererCount);
  791. for (rendererIndex = 0; rendererIndex < rendererCount; rendererIndex++) {
  792. totalRenderers++;
  793. //Com_Printf(" rendererIndex: %d\n", rendererIndex);
  794. rendererID = 0xffffffff;
  795. err = CGLDescribeRenderer(rendererInfo, rendererIndex, kCGLRPRendererID, &rendererID);
  796. if (err) {
  797. Com_Printf("CGLDescribeRenderer(kCGLRPRendererID) -> %d\n", err);
  798. continue;
  799. }
  800. //Com_Printf(" rendererID: 0x%08x\n", rendererID);
  801. accelerated = 0;
  802. err = CGLDescribeRenderer(rendererInfo, rendererIndex, kCGLRPAccelerated, &accelerated);
  803. if (err) {
  804. Com_Printf("CGLDescribeRenderer(kCGLRPAccelerated) -> %d\n", err);
  805. continue;
  806. }
  807. //Com_Printf(" accelerated: %d\n", accelerated);
  808. if (!accelerated)
  809. continue;
  810. vram = 0;
  811. err = CGLDescribeRenderer(rendererInfo, rendererIndex, kCGLRPVideoMemory, &vram);
  812. if (err) {
  813. Com_Printf("CGLDescribeRenderer -> %d\n", err);
  814. continue;
  815. }
  816. //Com_Printf(" vram: 0x%08x\n", vram);
  817. // presumably we'll be running on the best card, so we'll take the max of the vrams
  818. if (vram > maxVRAM)
  819. maxVRAM = vram;
  820. }
  821. #if 0
  822. err = CGLDestroyRendererInfo(rendererInfo);
  823. if (err) {
  824. Com_Printf("CGLDestroyRendererInfo -> %d\n", err);
  825. }
  826. #endif
  827. }
  828. return maxVRAM;
  829. }
  830. // We will set the Sys_IsHidden global to cause input to be handle differently (we'll just let NSApp handle events in this case). We also will unbind the GL context and restore the video mode.
  831. qboolean Sys_Hide()
  832. {
  833. if (Sys_IsHidden)
  834. // Eh?
  835. return qfalse;
  836. if (!r_fullscreen->integer)
  837. // We only support hiding in fullscreen mode right now
  838. return qfalse;
  839. Sys_IsHidden = qtrue;
  840. // Don't need to store the current gamma since we always keep it around in glw_state.inGameTable.
  841. Sys_FadeScreen(Sys_DisplayToUse());
  842. // Disassociate the GL context from the screen
  843. // Have to call both to actually deallocate kernel resources and free the NSSurface
  844. CGLClearDrawable(OSX_GetCGLContext());
  845. [OSX_GetNSGLContext() clearDrawable];
  846. // Restore the original video mode
  847. _GLimp_RestoreOriginalVideoSettings();
  848. // Restore the original gamma if needed.
  849. if (glConfig.deviceSupportsGamma) {
  850. CGDisplayRestoreColorSyncSettings();
  851. }
  852. // Release the screen(s)
  853. ReleaseAllDisplays();
  854. Sys_UnfadeScreens();
  855. // Shut down the input system so the mouse and keyboard settings are restore to normal
  856. Sys_ShutdownInput();
  857. // Hide the application so that when the user clicks on our app icon, we'll get an unhide notification
  858. [NSApp hide: nil];
  859. return qtrue;
  860. }
  861. static CGDisplayErr Sys_CaptureActiveDisplays(void)
  862. {
  863. CGDisplayErr err;
  864. CGDisplayCount displayIndex;
  865. for (displayIndex = 0; displayIndex < glw_state.displayCount; displayIndex++) {
  866. const glwgamma_t *table;
  867. table = &glw_state.originalDisplayGammaTables[displayIndex];
  868. err = CGDisplayCapture(table->display);
  869. if (err != CGDisplayNoErr)
  870. return err;
  871. }
  872. return CGDisplayNoErr;
  873. }
  874. qboolean Sys_Unhide()
  875. {
  876. CGDisplayErr err;
  877. CGLError glErr;
  878. if (!Sys_IsHidden)
  879. // Eh?
  880. return qfalse;
  881. Sys_FadeScreens();
  882. // Capture the screen(s)
  883. err = Sys_CaptureActiveDisplays();
  884. if (err != CGDisplayNoErr) {
  885. Sys_UnfadeScreens();
  886. ri.Printf( PRINT_ALL, "Unhide failed -- cannot capture the display again.\n" );
  887. return qfalse;
  888. }
  889. // Restore the game mode
  890. err = CGDisplaySwitchToMode(glw_state.display, (CFDictionaryRef)glw_state.gameMode);
  891. if ( err != CGDisplayNoErr ) {
  892. ReleaseAllDisplays();
  893. Sys_UnfadeScreens();
  894. ri.Printf( PRINT_ALL, "Unhide failed -- Unable to set display mode\n" );
  895. return qfalse;
  896. }
  897. // Reassociate the GL context and the screen
  898. glErr = CGLSetFullScreen(OSX_GetCGLContext());
  899. if (err) {
  900. ReleaseAllDisplays();
  901. Sys_UnfadeScreens();
  902. ri.Printf( PRINT_ALL, "Unhide failed: CGLSetFullScreen -> %d (%s)\n", err, CGLErrorString(err));
  903. return qfalse;
  904. }
  905. // Restore the current context
  906. [OSX_GetNSGLContext() makeCurrentContext];
  907. // Restore the gamma that the game had set
  908. Sys_UnfadeScreen(Sys_DisplayToUse(), &glw_state.inGameTable);
  909. // Restore the input system (last so if something goes wrong we don't eat the mouse)
  910. Sys_InitInput();
  911. Sys_IsHidden = qfalse;
  912. return qtrue;
  913. }