Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1583 lines
47 KiB

  1. //========= Copyright 1996-2009, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Defines a group of app systems that all have the same lifetime
  4. // that need to be connected/initialized, etc. in a well-defined order
  5. //
  6. // $Revision: $
  7. // $NoKeywords: $
  8. //=============================================================================//
  9. #include <Cocoa/Cocoa.h>
  10. #include <OpenGL/OpenGL.h>
  11. #include <OpenGL/gl.h>
  12. #include <OpenGL/glext.h>
  13. #include <IOKit/IOKitLib.h>
  14. #undef MIN
  15. #undef MAX
  16. #define DONT_DEFINE_BOOL // Don't define BOOL!
  17. #include "tier0/threadtools.h"
  18. #include "tier0/icommandline.h"
  19. #include "tier1/interface.h"
  20. #include "tier1/strtools.h"
  21. #include "tier1/utllinkedlist.h"
  22. #include "togl/rendermechanism.h"
  23. #include "appframework/ilaunchermgr.h" // gets pulled in from glmgr.h
  24. #include "appframework/iappsystemgroup.h"
  25. #include "inputsystem/ButtonCode.h"
  26. // some helper functions, relocated out of GLM since they are used here
  27. // this one makes a new context
  28. bool GLMDetectSLGU( void );
  29. bool GLMDetectSLGU( void )
  30. {
  31. CGLError cgl_error = (CGLError)0;
  32. bool result = false;
  33. CGLContextObj oldctx = CGLGetCurrentContext();
  34. static CGLPixelFormatAttribute attribs[] =
  35. {
  36. kCGLPFADoubleBuffer,
  37. kCGLPFANoRecovery,
  38. kCGLPFAAccelerated,
  39. kCGLPFADepthSize,
  40. (CGLPixelFormatAttribute)0,
  41. kCGLPFAColorSize,
  42. (CGLPixelFormatAttribute)32,
  43. (CGLPixelFormatAttribute)0 // list term
  44. };
  45. CGLPixelFormatObj pixfmtobj = NULL;
  46. GLint npix;
  47. CGLContextObj ctxobj = NULL;
  48. cgl_error = CGLChoosePixelFormat( attribs, &pixfmtobj, &npix );
  49. if (!cgl_error)
  50. {
  51. // got pixel format, make a context
  52. cgl_error = CGLCreateContext( pixfmtobj, NULL, &ctxobj );
  53. if (!cgl_error)
  54. {
  55. CGLSetCurrentContext( ctxobj );
  56. // now do the test
  57. _CGLContextParameter kCGLCPGCDMPEngine = ((_CGLContextParameter)1314);
  58. GLint dummyval = 0;
  59. cgl_error = CGLGetParameter( CGLGetCurrentContext(), kCGLCPGCDMPEngine, &dummyval );
  60. result = (!cgl_error);
  61. // all done, go back to old context, and destroy the temp one
  62. CGLSetCurrentContext( oldctx );
  63. CGLDestroyContext( ctxobj );
  64. }
  65. // destroy the pixel format obj
  66. CGLDestroyPixelFormat( pixfmtobj );
  67. }
  68. return result;
  69. }
  70. bool GLMDetectScaledResolveMode( uint osComboVersion, bool hasSLGU );
  71. bool GLMDetectScaledResolveMode( uint osComboVersion, bool hasSLGU )
  72. {
  73. bool result = false;
  74. // note this function assumes a current context on the renderer in question
  75. // and that FB blit and SLGU are present..
  76. if (!hasSLGU)
  77. return false;
  78. if (osComboVersion <= 0x000A0604) // we know no one has it before 10.6.5
  79. return false;
  80. // in 10.6.6 and later, just check for the ext string.
  81. char *gl_ext_string = (char*)glGetString(GL_EXTENSIONS);
  82. // avoid crashing due to strstr'ing NULL pointer returned from glGetString
  83. if (!gl_ext_string)
  84. gl_ext_string = "";
  85. result = strstr(gl_ext_string, "GL_EXT_framebuffer_multisample_blit_scaled") != NULL;
  86. if ( !result )
  87. {
  88. // make two FBO's
  89. GLuint fbos[2];
  90. GLuint rbos[2];
  91. int extent = 64;
  92. // make two render buffers
  93. for( int fbi = 0; fbi < 2; fbi++ )
  94. {
  95. glGenFramebuffersEXT( 1, &fbos[fbi] ); CheckGLError( __LINE__ );
  96. glBindFramebufferEXT( fbi ? GL_DRAW_FRAMEBUFFER_EXT : GL_READ_FRAMEBUFFER_EXT , fbos[fbi] ); CheckGLError( __LINE__ );
  97. glGenRenderbuffersEXT( 1, &rbos[fbi] ); CheckGLError( __LINE__ );
  98. glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, rbos[fbi] ); CheckGLError( __LINE__ );
  99. // make it multisampled if 0
  100. if (!fbi)
  101. {
  102. glRenderbufferStorageMultisampleEXT( GL_RENDERBUFFER_EXT, 2, GL_RGBA8, extent,extent ); CheckGLError( __LINE__ );
  103. }
  104. else
  105. {
  106. glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_RGBA8, extent,extent ); CheckGLError( __LINE__ );
  107. }
  108. // attach it
  109. // #0 gets to be read and multisampled
  110. // #1 gets to be draw and multisampled
  111. glFramebufferRenderbufferEXT( fbi ? GL_DRAW_FRAMEBUFFER_EXT : GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, rbos[fbi] ); CheckGLError( __LINE__ );
  112. }
  113. // now test
  114. while( glGetError() ) // clear error queue
  115. {
  116. ;
  117. }
  118. // now do the dummy blit
  119. glBlitFramebufferEXT( 0,0,extent,extent, 0,0,extent,extent, GL_COLOR_BUFFER_BIT, XGL_SCALED_RESOLVE_FASTEST_EXT );
  120. // type of error we get back lets us know what the outcome is.
  121. // invalid enum error -> unsupported
  122. // no error or invalid op -> supported
  123. GLenum errorcode = (GLenum)glGetError();
  124. switch(errorcode)
  125. {
  126. // expected outcomes.
  127. // positive
  128. case GL_NO_ERROR:
  129. case GL_INVALID_OPERATION:
  130. result = true; // new scaled resolve detected
  131. break;
  132. default:
  133. result = false; // no scaled resolve
  134. break;
  135. }
  136. // unbind and wipe stuff
  137. glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); CheckGLError( __LINE__ );
  138. for( int xfbi = 0; xfbi < 2; xfbi++ )
  139. {
  140. // unbind FBO
  141. glBindFramebufferEXT( xfbi ? GL_DRAW_FRAMEBUFFER_EXT : GL_READ_FRAMEBUFFER_EXT , 0 ); CheckGLError( __LINE__ );
  142. // del FBO and RBO
  143. glDeleteFramebuffersEXT( 1, &fbos[xfbi] ); CheckGLError( __LINE__ );
  144. glDeleteRenderbuffersEXT( 1, &rbos[xfbi] ); CheckGLError( __LINE__ );
  145. }
  146. }
  147. return result; // no SLGU, no scaled resolve blit even possible
  148. }
  149. //===============================================================================
  150. GLMRendererInfo::GLMRendererInfo( GLMRendererInfoFields *info )
  151. {
  152. NSAutoreleasePool *tempPool = [[NSAutoreleasePool alloc] init ];
  153. // absorb info obtained so far by caller
  154. m_info = *info;
  155. m_displays = NULL;
  156. // gather more info using a dummy context
  157. unsigned int attribs[] =
  158. {
  159. kCGLPFADoubleBuffer, kCGLPFANoRecovery, kCGLPFAAccelerated,
  160. kCGLPFADepthSize, 0,
  161. kCGLPFAColorSize, 32,
  162. kCGLPFARendererID, info->m_rendererID,
  163. 0
  164. };
  165. NSOpenGLPixelFormat *pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:(NSOpenGLPixelFormatAttribute*)attribs];
  166. NSOpenGLContext *nsglCtx = [[NSOpenGLContext alloc] initWithFormat: pixFmt shareContext: NULL ];
  167. [nsglCtx makeCurrentContext];
  168. // run queries.
  169. char *gl_ext_string = (char*)glGetString(GL_EXTENSIONS);
  170. uint vers = m_info.m_osComboVersion;
  171. // avoid crashing due to strstr'ing NULL pointer returned from glGetString
  172. if (!gl_ext_string)
  173. gl_ext_string = "";
  174. // effectively blacklist the renderer if it doesn't actually work; sort it to back of list
  175. if ( !nsglCtx )
  176. {
  177. m_info.m_vidMemory = 1;
  178. m_info.m_texMemory = 1;
  179. }
  180. //-------------------------------------------------------------------
  181. // booleans
  182. //-------------------------------------------------------------------
  183. // gamma writes.
  184. m_info.m_hasGammaWrites = true;
  185. if ( vers < 0x000A0600 ) // pre 10.6.0, no SRGB write - see http://developer.apple.com/graphicsimaging/opengl/capabilities/GLInfo_1058.html
  186. {
  187. m_info.m_hasGammaWrites = false;
  188. }
  189. if (m_info.m_atiR5xx)
  190. {
  191. m_info.m_hasGammaWrites = false; // it just don't, even post 10.6.3
  192. }
  193. // if CLI option for fake SRGB mode is enabled, turn off this cap, act like we do not have EXT FB SRGB
  194. if (CommandLine()->FindParm("-glmenablefakesrgb"))
  195. {
  196. m_info.m_hasGammaWrites = false;
  197. }
  198. // extension string *could* be checked, but on 10.6.3 the ext string is not there, but the func *is*
  199. //-------------------------------------------------------------------
  200. // mixed attach sizes for FBO
  201. m_info.m_hasMixedAttachmentSizes = true;
  202. if ( vers < 0x000A0603 ) // pre 10.6.3, no mixed attach sizes
  203. {
  204. m_info.m_hasMixedAttachmentSizes = false;
  205. }
  206. else
  207. {
  208. if (!strstr(gl_ext_string, "GL_ARB_framebuffer_object"))
  209. {
  210. // ARB_framebuffer_object not available
  211. m_info.m_hasMixedAttachmentSizes = false;
  212. }
  213. }
  214. // also check ext string
  215. //-------------------------------------------------------------------
  216. // BGRA vert attribs
  217. m_info.m_hasBGRA = true;
  218. if ( vers < 0x000A0603 ) // pre 10.6.3, no BGRA attribs
  219. {
  220. m_info.m_hasBGRA = false;
  221. }
  222. else
  223. {
  224. if (!strstr(gl_ext_string, "EXT_vertex_array_bgra"))
  225. {
  226. // EXT_vertex_array_bgra not available
  227. m_info.m_hasBGRA = false;
  228. }
  229. }
  230. //-------------------------------------------------------------------
  231. m_info.m_hasNewFullscreenMode = true;
  232. if ( vers < 0x000A0600 ) // pre 10.6.0, no clever window server full screen mode
  233. {
  234. m_info.m_hasNewFullscreenMode = false;
  235. }
  236. //-------------------------------------------------------------------
  237. m_info.m_hasNativeClipVertexMode = true;
  238. // this one uses a heuristic, and allows overrides in case the heuristic is wrong
  239. // or someone wants to try a beta driver or something.
  240. // known bad combinations get turned off here..
  241. // any ATI hardware...(or Intel - sm3 path for CSGO is falling back to SW on some HDX000 HW otherwise)
  242. // TURNED OFF OS CHECK if (m_info.m_osComboVersion <= 0x000A0603)
  243. // still believe to be broken in 10.6.4
  244. {
  245. if (m_info.m_ati || m_info.m_intel)
  246. {
  247. m_info.m_hasNativeClipVertexMode = false;
  248. }
  249. }
  250. // R500, forever..
  251. if (m_info.m_atiR5xx)
  252. {
  253. m_info.m_hasNativeClipVertexMode = false;
  254. }
  255. // if user disabled them
  256. if (CommandLine()->FindParm("-glmdisableclipplanes"))
  257. {
  258. m_info.m_hasNativeClipVertexMode = false;
  259. }
  260. // or maybe enabled them..
  261. if (CommandLine()->FindParm("-glmenableclipplanes"))
  262. {
  263. m_info.m_hasNativeClipVertexMode = true;
  264. }
  265. //-------------------------------------------------------------------
  266. m_info.m_hasOcclusionQuery = true;
  267. if (!strstr(gl_ext_string, "ARB_occlusion_query"))
  268. {
  269. m_info.m_hasOcclusionQuery = false; // you don't got it!
  270. }
  271. //-------------------------------------------------------------------
  272. m_info.m_hasFramebufferBlit = true;
  273. if (!strstr(gl_ext_string, "EXT_framebuffer_blit"))
  274. {
  275. m_info.m_hasFramebufferBlit = false; // you know you don't got it!
  276. }
  277. //-------------------------------------------------------------------
  278. m_info.m_maxAniso = 4; //FIXME needs real query
  279. //-------------------------------------------------------------------
  280. m_info.m_hasBindableUniforms = true;
  281. if (!strstr(gl_ext_string, "EXT_bindable_uniform"))
  282. {
  283. m_info.m_hasBindableUniforms = false;
  284. }
  285. m_info.m_hasBindableUniforms = false; // hardwiring this path to false until we see how to accelerate it properly
  286. //-------------------------------------------------------------------
  287. m_info.m_hasUniformBuffers = true;
  288. if (!strstr(gl_ext_string, "ARB_uniform_buffer"))
  289. {
  290. m_info.m_hasUniformBuffers = false;
  291. }
  292. //-------------------------------------------------------------------
  293. // test for performance pack (10.6.4+)
  294. bool perfPackageDetected = GLMDetectSLGU();
  295. if (perfPackageDetected)
  296. {
  297. m_info.m_hasPerfPackage1 = true;
  298. }
  299. if (CommandLine()->FindParm("-glmenableperfpackage")) // force it on
  300. {
  301. m_info.m_hasPerfPackage1 = true;
  302. }
  303. if (CommandLine()->FindParm("-glmdisableperfpackage")) // force it off
  304. {
  305. m_info.m_hasPerfPackage1 = false;
  306. }
  307. //-------------------------------------------------------------------
  308. // runtime options that aren't negotiable once set
  309. m_info.m_hasDualShaders = CommandLine()->FindParm("-glmdualshaders");
  310. //-------------------------------------------------------------------
  311. // "can'ts "
  312. m_info.m_cantBlitReliably = (m_info.m_osComboVersion < 0x000A0606) && m_info.m_intel; //don't trust FBO blit on Intel before 10.6.6
  313. if (CommandLine()->FindParm("-glmenabletrustblit"))
  314. {
  315. m_info.m_cantBlitReliably = false; // we trust the blit, so set the cant-blit cap to false
  316. }
  317. if (CommandLine()->FindParm("-glmdisabletrustblit"))
  318. {
  319. m_info.m_cantBlitReliably = true; // we do not trust the blit, so set the cant-blit cap to true
  320. }
  321. //m_info.m_cantAttachSRGB = (m_info.m_nv && m_info.m_osComboVersion < 0x000A0600); //NV drivers won't accept SRGB tex on an FBO color target in 10.5.8
  322. //m_info.m_cantAttachSRGB = (m_info.m_ati && m_info.m_osComboVersion < 0x000A0600); //... does ATI have the same problem?
  323. m_info.m_cantAttachSRGB = (m_info.m_osComboVersion < 0x000A0600); // across the board on 10.5.x actually..
  324. // MSAA resolve issues
  325. m_info.m_cantResolveFlipped = false; // initial stance
  326. if (m_info.m_ati)
  327. {
  328. //Jan 2011 - ATI says it's better to do two step blit than to try and resolve upside down
  329. m_info.m_cantResolveFlipped = true;
  330. }
  331. if (m_info.m_nv)
  332. {
  333. // we're going to mark it 'broken' unless perf package 1 (10.6.4+) is present
  334. if (!m_info.m_hasPerfPackage1)
  335. {
  336. m_info.m_cantResolveFlipped = true;
  337. }
  338. }
  339. // this is just the private assessment of whather scaled resolve is available.
  340. // the activation of it will stay tied to the gl_minify_resolve_mode / gl_magnify_resolve_mode convars in glmgr
  341. if (m_info.m_osComboVersion > 0x000A0700 || CommandLine()->FindParm("-gl_enable_scaled_resolve") )
  342. {
  343. bool scaledResolveDetected = GLMDetectScaledResolveMode( m_info.m_osComboVersion, m_info.m_hasPerfPackage1 );
  344. m_info.m_cantResolveScaled = !scaledResolveDetected;
  345. }
  346. else
  347. {
  348. m_info.m_cantResolveScaled = true;
  349. }
  350. // and you can force it to be "available" if you really want to..
  351. if ( CommandLine()->FindParm("-gl_force_enable_scaled_resolve") )
  352. {
  353. m_info.m_cantResolveScaled = false;
  354. }
  355. // gamma decode impacting shader codegen
  356. m_info.m_costlyGammaFlips = false;
  357. if (m_info.m_osComboVersion < 0x000A0600) // if Leopard
  358. m_info.m_costlyGammaFlips = true;
  359. if (m_info.m_atiR5xx) // or r5xx - always
  360. m_info.m_costlyGammaFlips = true;
  361. if ( (m_info.m_atiR6xx) && (m_info.m_osComboVersion < 0x000A0605) ) // or r6xx prior to 10.6.5
  362. m_info.m_costlyGammaFlips = true;
  363. // The OpenGL driver for Intel HD4000 on 10.8 has a bug in the GLSL compiler, which was fixed
  364. // in 10.9 (and unlikely to be fixed in 10.8). See intelglmallocworkaround.h for more info.
  365. bool mountainLion = (m_info.m_osComboVersion >= 0x000A0800) && (m_info.m_osComboVersion < 0x000A0900);
  366. m_info.m_badDriver108Intel = mountainLion && m_info.m_intelHD4000;
  367. if ( CommandLine()->FindParm("-glmenablemallocworkaround") )
  368. {
  369. m_info.m_badDriver108Intel = true;
  370. }
  371. if ( CommandLine()->FindParm("-glmdisablemallocworkaround") )
  372. {
  373. m_info.m_badDriver108Intel = false;
  374. }
  375. [nsglCtx release];
  376. [pixFmt release];
  377. [tempPool release];
  378. }
  379. GLMRendererInfo::~GLMRendererInfo( void )
  380. {
  381. if (m_displays)
  382. {
  383. // delete all the new'd renderer infos that the table tracks
  384. FOR_EACH_VEC( *m_displays, i )
  385. {
  386. delete (*this->m_displays)[i];
  387. }
  388. delete m_displays;
  389. m_displays = NULL;
  390. }
  391. }
  392. extern "C" int DisplayInfoSortFunction( GLMDisplayInfo* const *A, GLMDisplayInfo* const *B )
  393. {
  394. int bigger = -1;
  395. int smaller = 1; // adjust these to get the ordering you want
  396. // check main-ness - main should win
  397. uint maskOfMainDisplay = CGDisplayIDToOpenGLDisplayMask( CGMainDisplayID() );
  398. //Assert( maskOfMainDisplay==1 ); // just curious
  399. int mainscreena = (*A)->m_info.m_glDisplayMask & maskOfMainDisplay;
  400. int mainscreenb = (*B)->m_info.m_glDisplayMask & maskOfMainDisplay;
  401. if ( mainscreena > mainscreenb )
  402. {
  403. return bigger;
  404. }
  405. else if ( mainscreena < mainscreenb )
  406. {
  407. return smaller;
  408. }
  409. // check area - larger screen should win
  410. int areaa = (*A)->m_info.m_displayPixelWidth * (*A)->m_info.m_displayPixelHeight;
  411. int areab = (*B)->m_info.m_displayPixelWidth * (*B)->m_info.m_displayPixelHeight;
  412. if ( areaa > areab )
  413. {
  414. return bigger;
  415. }
  416. else if ( areaa < areab )
  417. {
  418. return smaller;
  419. }
  420. return 0; // equal rank
  421. }
  422. void GLMRendererInfo::PopulateDisplays( void )
  423. {
  424. Assert( !m_displays );
  425. m_displays = new CUtlVector< GLMDisplayInfo* >;
  426. for( int i=0; i<32; i++)
  427. {
  428. // check mask to see if the selected display intersects this renderer
  429. CGOpenGLDisplayMask dspMask = (CGOpenGLDisplayMask)(1<<i);
  430. if ( m_info.m_displayMask & dspMask )
  431. {
  432. // exclude teeny displays (they may represent offline displays)
  433. // exclude inactive displays
  434. CGDirectDisplayID cgid = CGOpenGLDisplayMaskToDisplayID ( dspMask );
  435. if ( (cgid != kCGNullDirectDisplay) && CGDisplayIsActive( cgid ) && (CGDisplayPixelsWide( cgid ) >= 512) && (CGDisplayPixelsHigh( cgid ) >= 384) )
  436. {
  437. GLMDisplayInfo *newdisp = new GLMDisplayInfo( cgid, dspMask );
  438. m_displays->AddToTail( newdisp );
  439. }
  440. }
  441. }
  442. // now sort the table of displays.
  443. m_displays->Sort( DisplayInfoSortFunction );
  444. // then go back and ask each display to populate its display mode table.
  445. FOR_EACH_VEC( *m_displays, i )
  446. {
  447. (*this->m_displays)[i]->PopulateModes();
  448. }
  449. }
  450. const char *CheesyRendererDecode( uint value )
  451. {
  452. switch(value)
  453. {
  454. case 0x00020200 : return "Generic";
  455. case 0x00020400 : return "GenericFloat";
  456. case 0x00020600 : return "AppleSW";
  457. case 0x00021000 : return "ATIRage128";
  458. case 0x00021200 : return "ATIRadeon";
  459. case 0x00021400 : return "ATIRagePro";
  460. case 0x00021600 : return "ATIRadeon8500";
  461. case 0x00021800 : return "ATIRadeon9700";
  462. case 0x00021900 : return "ATIRadeonX1000";
  463. case 0x00021A00 : return "ATIRadeonX2000";
  464. case 0x00022000 : return "NVGeForce2MX";
  465. case 0x00022200 : return "NVGeForce3";
  466. case 0x00022400 : return "NVGeForceFX";
  467. case 0x00022600 : return "NVGeForce8xxx";
  468. case 0x00023000 : return "VTBladeXP2";
  469. case 0x00024000 : return "Intel900";
  470. case 0x00024200 : return "IntelX3100";
  471. case 0x00040000 : return "Mesa3DFX";
  472. default: return "UNKNOWN";
  473. }
  474. }
  475. extern const char *GLMDecode( GLMThing_t thingtype, unsigned long value );
  476. void GLMRendererInfo::Dump( int which )
  477. {
  478. GLMPRINTF(("\n #%d: GLMRendererInfo @ %08x, renderer-id=%s(%08x) display-mask=%08x vram=%dMB",
  479. which, this,
  480. CheesyRendererDecode( m_info.m_rendererID & 0x00FFFF00 ), m_info.m_rendererID,
  481. m_info.m_displayMask,
  482. m_info.m_vidMemory >> 20
  483. ));
  484. GLMPRINTF(("\n VendorID=%04x DeviceID=%04x Model=%s",
  485. m_info.m_pciVendorID,
  486. m_info.m_pciDeviceID,
  487. m_info.m_pciModelString
  488. ));
  489. FOR_EACH_VEC( *m_displays, i )
  490. {
  491. (*m_displays)[i]->Dump(i);
  492. }
  493. }
  494. //===============================================================================
  495. GLMDisplayDB::GLMDisplayDB ( void )
  496. {
  497. m_renderers = NULL;
  498. }
  499. GLMDisplayDB::~GLMDisplayDB ( void )
  500. {
  501. if (m_renderers)
  502. {
  503. // delete all the new'd renderer infos that the table tracks
  504. FOR_EACH_VEC( *m_renderers, i )
  505. {
  506. delete (*this->m_renderers)[i];
  507. }
  508. delete m_renderers;
  509. m_renderers = NULL;
  510. }
  511. }
  512. extern "C" int RendererInfoSortFunction( GLMRendererInfo * const *A, GLMRendererInfo* const *B )
  513. {
  514. int bigger = -1;
  515. int smaller = 1;
  516. // check VRAM
  517. if ( (*A)->m_info.m_vidMemory > (*B)->m_info.m_vidMemory )
  518. {
  519. return bigger;
  520. }
  521. else if ( (*A)->m_info.m_vidMemory < (*B)->m_info.m_vidMemory )
  522. {
  523. return smaller;
  524. }
  525. // check MSAA limit
  526. if ( (*A)->m_info.m_maxSamples > (*B)->m_info.m_maxSamples )
  527. {
  528. return bigger;
  529. }
  530. else if ( (*A)->m_info.m_maxSamples < (*B)->m_info.m_maxSamples )
  531. {
  532. return smaller;
  533. }
  534. /*
  535. // this was not a great idea here..
  536. // check if one has the main screen - is that index 0 in all cases?
  537. uint maskOfMainDisplay = CGDisplayIDToOpenGLDisplayMask( CGMainDisplayID() );
  538. Assert( maskOfMainDisplay==1 ); // just curious
  539. int mainscreena = (*A)->m_info.m_displayMask & maskOfMainDisplay;
  540. int mainscreenb = (*B)->m_info.m_displayMask & maskOfMainDisplay;
  541. if ( mainscreena > mainscreenb )
  542. {
  543. return bigger;
  544. }
  545. else if ( mainscreena < mainscreenb )
  546. {
  547. return smaller;
  548. }
  549. */
  550. return 0; // equal rank
  551. }
  552. /** some code that NV gave us. more generalized approach below..
  553. static io_registry_entry_t lookup_dev_NV(char *name)
  554. {
  555. mach_port_t master_port = 0;
  556. io_iterator_t iterator;
  557. io_registry_entry_t nub = 0;
  558. kern_return_t ret;
  559. IOMasterPort(MACH_PORT_NULL, &master_port);
  560. ret = IOServiceGetMatchingServices(master_port, IOServiceMatching(name), &iterator);
  561. if (iterator) {
  562. nub = IOIteratorNext(iterator);
  563. if (IOIteratorNext(iterator)) {
  564. printf("warning: more than one card?\n");
  565. }
  566. IOObjectRelease(iterator);
  567. }
  568. IOObjectRelease(master_port);
  569. return nub;
  570. }
  571. void GetDriverInfoString_NV( char *driverNameBuf, int driverNameBufLen )
  572. {
  573. // courtesy NVIDIA dev rel
  574. io_registry_entry_t registry;
  575. kern_return_t ret;
  576. //
  577. // Get NVKernel / IOGLBundleName
  578. //
  579. registry = lookup_dev_NV("NVKernel");
  580. if (!registry) {
  581. fprintf(stderr, "error: could not find NVKernel IORegistry entry!\n");
  582. return;
  583. }
  584. CFMutableDictionaryRef entry;
  585. ret = IORegistryEntryCreateCFProperties(registry, &entry, kCFAllocatorDefault, 0);
  586. if (ret != kIOReturnSuccess) {
  587. fprintf(stderr, "error: could not create CFProperties dictionary!\n");
  588. return;
  589. }
  590. CFStringRef bundle_name_ref = (CFStringRef) CFDictionaryGetValue(entry, CFSTR("IOGLBundleName"));
  591. if (!bundle_name_ref) {
  592. fprintf(stderr, "error: could not get IOGLBundleName reference!\n");
  593. return;
  594. }
  595. const char *bundle_name = CFStringGetCStringPtr(bundle_name_ref, CFStringGetSystemEncoding());
  596. if (!bundle_name) {
  597. fprintf(stderr, "error: could not get IOGLBundleName!\n");
  598. return;
  599. }
  600. CFStringRef identifier = CFStringCreateWithFormat(NULL, NULL, CFSTR("com.apple.%s"), bundle_name);
  601. //
  602. // Get bundle information
  603. //
  604. CFBundleRef bundle;
  605. bundle = CFBundleGetBundleWithIdentifier(identifier);
  606. if (!bundle) {
  607. fprintf(stderr, "error: could not get GL driver bundle!\n");
  608. return;
  609. }
  610. CFDictionaryRef dict;
  611. CFStringRef info;
  612. dict = CFBundleGetInfoDictionary(bundle);
  613. if (!dict) {
  614. fprintf(stderr, "error: could not get bundle info dictionary!\n");
  615. return;
  616. }
  617. info = (CFStringRef) CFDictionaryGetValue(dict, CFSTR("CFBundleGetInfoString"));
  618. if (!info) {
  619. fprintf(stderr, "error: could not get CFBundleGetInfoString!\n");
  620. return;
  621. }
  622. CFStringGetCString(info, driverNameBuf, driverNameBufLen, CFStringGetSystemEncoding());
  623. IOObjectRelease(registry);
  624. }
  625. **/
  626. void GLMDisplayDB::PopulateRenderers( void )
  627. {
  628. Assert( !m_renderers );
  629. m_renderers = new CUtlVector< GLMRendererInfo* >;
  630. // now walk the renderer list
  631. // find the eligible ones and insert them into vector
  632. // if more than one, sort the vector by desirability with favorite at 0
  633. // then ask each renderer object to populate its displays
  634. // turns out how you have to do this is to walk the display mask 1<<n..
  635. // and query at each one, what renderers can hit that one.
  636. // when you find one, see if it's already in the vector above. if not, add it.
  637. // later, we sort them.
  638. for( int i=0; i<32; i++ )
  639. {
  640. CGLError cgl_err = (CGLError)0;
  641. CGLRendererInfoObj cgl_rend = NULL;
  642. GLint nrend;
  643. CGOpenGLDisplayMask dspMask = (CGOpenGLDisplayMask)(1<<i);
  644. CGDirectDisplayID cgid = CGOpenGLDisplayMaskToDisplayID( dspMask );
  645. bool selected = true; // assume the best
  646. if (selected)
  647. {
  648. if ( (cgid == kCGNullDirectDisplay) || (!CGDisplayIsActive( cgid )) )
  649. {
  650. selected = false;
  651. }
  652. }
  653. if (selected)
  654. {
  655. cgl_err = CGLQueryRendererInfo( dspMask, &cgl_rend, &nrend ); // FIXME this call spams the console if you ask about an out of bounds display mask
  656. // "<Error>: unknown error code: invalid display"
  657. // we can fix that by getting the active display mask first.
  658. if (!cgl_err)
  659. {
  660. // walk the renderers that can hit this display
  661. // add to table if not already in table, and minimums met
  662. for( int j=0; j<nrend; j++)
  663. {
  664. int problems = 0;
  665. GLMRendererInfoFields fields;
  666. memset( &fields, 0, sizeof(fields) );
  667. // early out if renderer ID already in the table
  668. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPRendererID, &fields.m_rendererID ); problems += (cgl_err != 0);
  669. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPDisplayMask, &fields.m_displayMask ); problems += (cgl_err != 0);
  670. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPFullScreen, &fields.m_fullscreen ); problems += (cgl_err != 0);
  671. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPAccelerated, &fields.m_accelerated ); problems += (cgl_err != 0);
  672. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPWindow, &fields.m_windowed ); problems += (cgl_err != 0);
  673. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPBufferModes, &fields.m_bufferModes ); problems += (cgl_err != 0);
  674. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPColorModes, &fields.m_colorModes ); problems += (cgl_err != 0);
  675. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPDepthModes, &fields.m_depthModes ); problems += (cgl_err != 0);
  676. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPStencilModes, &fields.m_stencilModes ); problems += (cgl_err != 0);
  677. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPMaxAuxBuffers, &fields.m_maxAuxBuffers ); problems += (cgl_err != 0);
  678. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPMaxSampleBuffers, &fields.m_maxSampleBuffers ); problems += (cgl_err != 0);
  679. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPMaxSamples, &fields.m_maxSamples ); problems += (cgl_err != 0);
  680. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPSampleModes, &fields.m_sampleModes ); problems += (cgl_err != 0);
  681. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPSampleAlpha, &fields.m_sampleAlpha ); problems += (cgl_err != 0);
  682. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPVideoMemory, &fields.m_vidMemory ); problems += (cgl_err != 0);
  683. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPTextureMemory, &fields.m_texMemory ); problems += (cgl_err != 0);
  684. // Make sure the renderer is attached to a display.
  685. GLint online;
  686. cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPOnline, &online ); problems += (cgl_err != 0);
  687. problems += ( online == 0 );
  688. // decide if this renderer goes in the table.
  689. bool selected = !problems;
  690. if (selected)
  691. {
  692. // grab the OS version
  693. SInt32 vMajor = 0; SInt32 vMinor = 0; SInt32 vMinorMinor = 0;
  694. OSStatus gestalt_err = 0;
  695. gestalt_err = Gestalt(gestaltSystemVersionMajor, &vMajor);
  696. Assert(!gestalt_err);
  697. gestalt_err = Gestalt(gestaltSystemVersionMinor, &vMinor);
  698. Assert(!gestalt_err);
  699. gestalt_err = Gestalt(gestaltSystemVersionBugFix, &vMinorMinor);
  700. Assert(!gestalt_err);
  701. //encode into one quantity - 10.6.3 becomes 0x000A0603
  702. fields.m_osComboVersion = (vMajor << 16) | (vMinor << 8) | (vMinorMinor);
  703. if (CommandLine()->FindParm("-fakeleopard"))
  704. {
  705. // lie
  706. fields.m_osComboVersion = 0x000A0508;
  707. }
  708. if (fields.m_osComboVersion < 0x000A0508)
  709. {
  710. // no support below 10.5.8
  711. // we'll wind up with no valid renderers and give up
  712. selected = false;
  713. }
  714. }
  715. if (selected)
  716. {
  717. // gather more info from IOKit
  718. // cribbed from http://developer.apple.com/mac/library/samplecode/VideoHardwareInfo/listing3.html
  719. CFTypeRef typeCode;
  720. CFDataRef vendorID, deviceID, model;
  721. io_registry_entry_t dspPort;
  722. // Get the I/O Kit service port for the display
  723. dspPort = CGDisplayIOServicePort( cgid );
  724. // Get the information for the device
  725. // The vendor ID, device ID, and model are all available as properties of the hardware's I/O Kit service port
  726. vendorID = (CFDataRef)IORegistryEntrySearchCFProperty(dspPort,kIOServicePlane,CFSTR("vendor-id"), kCFAllocatorDefault,kIORegistryIterateRecursively | kIORegistryIterateParents);
  727. deviceID = (CFDataRef)IORegistryEntrySearchCFProperty(dspPort,kIOServicePlane,CFSTR("device-id"), kCFAllocatorDefault,kIORegistryIterateRecursively | kIORegistryIterateParents);
  728. model = (CFDataRef)IORegistryEntrySearchCFProperty(dspPort,kIOServicePlane,CFSTR("model"), kCFAllocatorDefault,kIORegistryIterateRecursively | kIORegistryIterateParents);
  729. // Send the appropriate data to the outputs checking to validate the data
  730. if(vendorID)
  731. {
  732. fields.m_pciVendorID = *((UInt32*)CFDataGetBytePtr(vendorID));
  733. }
  734. else
  735. {
  736. fields.m_pciVendorID = 0;
  737. }
  738. if(deviceID)
  739. {
  740. fields.m_pciDeviceID = *((UInt32*)CFDataGetBytePtr(deviceID));
  741. }
  742. else
  743. {
  744. fields.m_pciDeviceID = 0;
  745. }
  746. if(model)
  747. {
  748. int length = CFDataGetLength(model);
  749. char *data = (char*)CFDataGetBytePtr(model);
  750. Q_strncpy( fields.m_pciModelString, data, sizeof(fields.m_pciModelString) );
  751. }
  752. else
  753. {
  754. Q_strncpy( fields.m_pciModelString, "UnknownModel", sizeof(fields.m_pciModelString) );
  755. }
  756. // iterate through IOAccelerators til we find one that matches the vendorid and deviceid of this renderer (ugh!)
  757. // this provides the driver version string which can in turn be used to uniquely identify bad drivers and special case for them
  758. // first example to date - forcing vsync on 10.6.4 + NV
  759. {
  760. io_iterator_t ioIterator = (io_iterator_t)0;
  761. io_service_t ioAccelerator;
  762. kern_return_t ioResult = 0;
  763. bool ioDone = false;
  764. ioResult = IOServiceGetMatchingServices( kIOMasterPortDefault, IOServiceMatching("IOAccelerator"), &ioIterator );
  765. if( ioResult == KERN_SUCCESS )
  766. {
  767. ioAccelerator = 0;
  768. while( ( !ioDone ) && ( ioAccelerator = IOIteratorNext( ioIterator ) ) )
  769. {
  770. io_service_t ioDevice;
  771. ioDevice = 0;
  772. ioResult = IORegistryEntryGetParentEntry( ioAccelerator, kIOServicePlane, &ioDevice);
  773. CFDataRef this_vendorID, this_deviceID;
  774. if(ioResult == KERN_SUCCESS)
  775. {
  776. this_vendorID = (CFDataRef)IORegistryEntryCreateCFProperty(ioDevice, CFSTR("vendor-id"), kCFAllocatorDefault, kNilOptions );
  777. this_deviceID = (CFDataRef)IORegistryEntryCreateCFProperty(ioDevice, CFSTR("device-id"), kCFAllocatorDefault, kNilOptions );
  778. if (this_vendorID && this_deviceID) // null check..
  779. {
  780. // see if it matches. if so, do our business (get the extended version string), set ioDone, call it a day
  781. unsigned short this_vendorIDValue = *(unsigned short*)CFDataGetBytePtr(this_vendorID);
  782. unsigned short this_deviceIDValue = *(unsigned short*)CFDataGetBytePtr(this_deviceID);
  783. if ( (fields.m_pciVendorID == this_vendorIDValue) && (fields.m_pciDeviceID == this_deviceIDValue) )
  784. {
  785. // see if it matches. if so, do our business (get the extended version string), set ioDone, call it a day
  786. unsigned short* this_vendorIDBytes = (unsigned short*)CFDataGetBytePtr( this_vendorID );
  787. unsigned short* this_deviceIDBytes = (unsigned short*)CFDataGetBytePtr( this_deviceID );
  788. if (this_vendorIDBytes && this_deviceIDBytes) // null check...
  789. {
  790. unsigned short this_vendorIDValue = *this_vendorIDBytes;
  791. unsigned short this_deviceIDValue = *this_deviceIDBytes;
  792. if ( (fields.m_pciVendorID == this_vendorIDValue) && (fields.m_pciDeviceID == this_deviceIDValue) )
  793. {
  794. // match, stop looking
  795. ioDone = true;
  796. // get extended info
  797. CFStringRef this_ioglName = (CFStringRef)IORegistryEntryCreateCFProperty( ioAccelerator, CFSTR("IOGLBundleName"), kCFAllocatorDefault, kNilOptions );
  798. NSString *bundlePath = [ NSString stringWithFormat:@"/System/Library/Extensions/%@.bundle", this_ioglName ];
  799. NSDictionary* this_driverDict = [ [NSBundle bundleWithPath: bundlePath] infoDictionary ];
  800. if (this_driverDict)
  801. {
  802. NSString* this_driverInfo = [ this_driverDict objectForKey:@"CFBundleGetInfoString" ];
  803. if ( this_driverInfo )
  804. {
  805. const char* theString = [ this_driverInfo UTF8String ];
  806. strncpy(fields.m_driverInfoString, theString, sizeof( fields.m_driverInfoString ) );
  807. }
  808. }
  809. // [bundlePath release];
  810. CFRelease(this_ioglName);
  811. }
  812. }
  813. CFRelease(this_vendorID);
  814. CFRelease(this_deviceID);
  815. }
  816. }
  817. }
  818. }
  819. }
  820. IOObjectRelease(ioAccelerator);
  821. IOObjectRelease(ioIterator);
  822. }
  823. // Release vendorID, deviceID, and model as appropriate
  824. if(vendorID)
  825. CFRelease(vendorID);
  826. if(deviceID)
  827. CFRelease(deviceID);
  828. if(model)
  829. CFRelease(model);
  830. // generate shorthand bools
  831. switch( fields.m_pciVendorID )
  832. {
  833. case 0x1002: //ATI
  834. {
  835. fields.m_ati = true;
  836. // http://www.pcidatabase.com/search.php?device_search_str=radeon&device_search.x=0&device_search.y=0&device_search=search+devices
  837. // Mac-relevant ATI R5xx PCI device ID's lie in this range: 0x7100 - 0x72FF
  838. // X1600, X1900, X1950
  839. if ( (fields.m_pciDeviceID >= 0x7100) && (fields.m_pciDeviceID <= 0x72ff) )
  840. {
  841. fields.m_atiR5xx = true;
  842. }
  843. // R6xx PCI device ID's lie in these ranges:
  844. // 0x94C1 - 0x9515 ... also 0x9581 - 0x9713
  845. // 2400HD, 2600HD, 3870, et al
  846. if (
  847. ( (fields.m_pciDeviceID >= 0x94C1) && (fields.m_pciDeviceID <= 0x9515) )
  848. || ( (fields.m_pciDeviceID >= 0x9581) && (fields.m_pciDeviceID <= 0x9713) )
  849. )
  850. {
  851. fields.m_atiR6xx = true;
  852. }
  853. // R7xx PCI device ID's lie in: 0x9440 - 0x9460, also 9480-94b5.
  854. // why there is an HD5000 at 9462, I dunno. Don't think that's an R8xx part.
  855. if (
  856. ( (fields.m_pciDeviceID >= 0x9440) && (fields.m_pciDeviceID <= 0x9460) )
  857. || ( (fields.m_pciDeviceID >= 0x9480) && (fields.m_pciDeviceID <= 0x94B5) )
  858. )
  859. {
  860. fields.m_atiR7xx = true;
  861. }
  862. // R8xx: 0x6898-0x68BE
  863. if ( (fields.m_pciDeviceID >= 0x6898) && (fields.m_pciDeviceID <= 0x68Be) )
  864. {
  865. fields.m_atiR8xx = true;
  866. }
  867. #if 0
  868. // turned off, but we could use this for cross check.
  869. // we could also use the bit encoding of the renderer ID to ferret out a geberation clue.
  870. // string-scan for each generation
  871. // this could be a lot better if we got the precise PCI ID's used and/or cross-ref'd that against the driver name
  872. if (strstr("X1600", fields.m_pciModelString) || strstr("X1900", fields.m_pciModelString) || strstr("X1950", fields.m_pciModelString) )
  873. {
  874. fields.m_atiR5xx = true;
  875. }
  876. if (strstr("2600", fields.m_pciModelString) || strstr("3870", fields.m_pciModelString) || strstr("X2000", fields.m_pciModelString) )
  877. {
  878. fields.m_atiR6xx = true;
  879. }
  880. if (strstr("4670", fields.m_pciModelString) || strstr("4650", fields.m_pciModelString) || strstr("4850", fields.m_pciModelString)|| strstr("4870", fields.m_pciModelString) )
  881. {
  882. fields.m_atiR7xx = true;
  883. }
  884. #endif
  885. }
  886. break;
  887. case 0x8086: //INTC
  888. {
  889. fields.m_intel = true;
  890. switch( fields.m_pciDeviceID )
  891. {
  892. case 0x27A6: fields.m_intel95x = true; break; // GMA 950
  893. case 0x2A02: fields.m_intel3100 = true; break; // X3100
  894. case 0x0166: fields.m_intelHD4000 = true; break; // HD4000
  895. }
  896. }
  897. break;
  898. case 0x10DE: //NV
  899. {
  900. fields.m_nv = true;
  901. // G7x: 0x0391 0x393 0x0395 (7300/7600 GT) 0x009D (Quadro FX)
  902. if ( (fields.m_pciDeviceID == 0x0391) || (fields.m_pciDeviceID == 0x0393) || (fields.m_pciDeviceID == 0x0395) || (fields.m_pciDeviceID == 0x009D) )
  903. {
  904. fields.m_nvG7x = true;
  905. }
  906. // G8x: 0400-04ff, also 0x5E1 (GTX280) through 0x08FF
  907. if (
  908. ( (fields.m_pciDeviceID >= 0x0400) && (fields.m_pciDeviceID <= 0x04ff) )
  909. || ( (fields.m_pciDeviceID >= 0x05E1) && (fields.m_pciDeviceID <= 0x08ff) )
  910. )
  911. {
  912. fields.m_nvG8x = true;
  913. }
  914. if ( fields.m_pciDeviceID > 0x0900 )
  915. {
  916. fields.m_nvNewer = true;
  917. }
  918. // detect the specific revision of NV driver in 10.6.4 that caused all the grief
  919. if (strstr(fields.m_driverInfoString, "1.6.16.11 (19.5.8f01)"))
  920. {
  921. fields.m_badDriver1064NV = true;
  922. }
  923. }
  924. break;
  925. }
  926. }
  927. if (selected)
  928. {
  929. // dupe check
  930. FOR_EACH_VEC( *m_renderers, i )
  931. {
  932. uint rendid = (*m_renderers)[i]->m_info.m_rendererID;
  933. if ( rendid == fields.m_rendererID )
  934. {
  935. // don't add to table, it's a dupe
  936. selected = false;
  937. }
  938. }
  939. }
  940. if (selected)
  941. {
  942. // criteria check
  943. if (fields.m_fullscreen==0)
  944. selected = false;
  945. if (fields.m_accelerated==0)
  946. selected = false;
  947. if (fields.m_windowed==0)
  948. selected = false;
  949. }
  950. Assert( fields.m_displayMask != 0 );
  951. if (selected)
  952. {
  953. // add to table
  954. // note this constructor makes a dummy context just long enough to query remaining fields in the m_info.
  955. GLMRendererInfo *newinfo = new GLMRendererInfo( &fields );
  956. m_renderers->AddToTail( newinfo );
  957. }
  958. }
  959. if (cgl_rend)
  960. {
  961. CGLDestroyRendererInfo( cgl_rend );
  962. }
  963. }
  964. }
  965. }
  966. // now sort the table.
  967. m_renderers->Sort( RendererInfoSortFunction );
  968. // then go back and ask each renderer to populate its display info table.
  969. FOR_EACH_VEC( *m_renderers, i )
  970. {
  971. (*m_renderers)[i]->PopulateDisplays();
  972. }
  973. }
  974. void GLMDisplayDB::PopulateFakeAdapters( uint realRendererIndex ) // fake adapters = one real adapter times however many displays are on it
  975. {
  976. // presumption is that renderers have been populated.
  977. Assert( GetRendererCount() > 0 );
  978. Assert( realRendererIndex < GetRendererCount() );
  979. m_fakeAdapters.RemoveAll();
  980. // for( int r = 0; r < GetRendererCount(); r++ )
  981. int r = realRendererIndex;
  982. {
  983. for( int d = 0; d < GetDisplayCount( r ); d++ )
  984. {
  985. GLMFakeAdapter temp;
  986. temp.m_rendererIndex = r;
  987. temp.m_displayIndex = d;
  988. m_fakeAdapters.AddToTail( temp );
  989. }
  990. }
  991. }
  992. void GLMDisplayDB::Populate(void)
  993. {
  994. this->PopulateRenderers();
  995. // passing in zero here, constrains the set of fake adapters (GL renderer + a display) to the ones using the highest ranked renderer.
  996. //FIXME introduce some kind of convar allowing selection of other GPU's in the system.
  997. int realRendererIndex = 0;
  998. if (CommandLine()->FindParm("-glmrenderer0"))
  999. realRendererIndex = 0;
  1000. if (CommandLine()->FindParm("-glmrenderer1"))
  1001. realRendererIndex = 1;
  1002. if (CommandLine()->FindParm("-glmrenderer2"))
  1003. realRendererIndex = 2;
  1004. if (CommandLine()->FindParm("-glmrenderer3"))
  1005. realRendererIndex = 3;
  1006. if (realRendererIndex >= GetRendererCount())
  1007. {
  1008. // fall back to 0
  1009. realRendererIndex = 0;
  1010. }
  1011. this->PopulateFakeAdapters( 0 );
  1012. #if GLMDEBUG
  1013. this->Dump();
  1014. #endif
  1015. }
  1016. int GLMDisplayDB::GetFakeAdapterCount( void )
  1017. {
  1018. return m_fakeAdapters.Count();
  1019. }
  1020. bool GLMDisplayDB::GetFakeAdapterInfo( int fakeAdapterIndex, int *rendererOut, int *displayOut, GLMRendererInfoFields *rendererInfoOut, GLMDisplayInfoFields *displayInfoOut )
  1021. {
  1022. if (fakeAdapterIndex >= GetFakeAdapterCount() )
  1023. {
  1024. *rendererOut = 0;
  1025. *displayOut = 0;
  1026. return true; // fail
  1027. }
  1028. *rendererOut = m_fakeAdapters[fakeAdapterIndex].m_rendererIndex;
  1029. *displayOut = m_fakeAdapters[fakeAdapterIndex].m_displayIndex;
  1030. bool rendResult = GetRendererInfo( *rendererOut, rendererInfoOut );
  1031. bool dispResult = GetDisplayInfo( *rendererOut, *displayOut, displayInfoOut );
  1032. return rendResult || dispResult;
  1033. }
  1034. int GLMDisplayDB::GetRendererCount( void )
  1035. {
  1036. return m_renderers->Count();
  1037. }
  1038. bool GLMDisplayDB::GetRendererInfo( int rendererIndex, GLMRendererInfoFields *infoOut )
  1039. {
  1040. memset( infoOut, 0, sizeof( GLMRendererInfoFields ) );
  1041. if (rendererIndex >= GetRendererCount())
  1042. return true; // fail
  1043. GLMRendererInfo *rendInfo = (*m_renderers)[rendererIndex];
  1044. *infoOut = rendInfo->m_info;
  1045. return false;
  1046. }
  1047. int GLMDisplayDB::GetDisplayCount( int rendererIndex )
  1048. {
  1049. if (rendererIndex >= GetRendererCount())
  1050. return 0; // fail
  1051. GLMRendererInfo *rendInfo = (*m_renderers)[rendererIndex];
  1052. return rendInfo->m_displays->Count();
  1053. }
  1054. bool GLMDisplayDB::GetDisplayInfo( int rendererIndex, int displayIndex, GLMDisplayInfoFields *infoOut )
  1055. {
  1056. memset( infoOut, 0, sizeof( GLMDisplayInfoFields ) );
  1057. if (rendererIndex >= GetRendererCount())
  1058. return true; // fail
  1059. if (displayIndex >= GetDisplayCount(rendererIndex))
  1060. return true; // fail
  1061. GLMDisplayInfo *displayInfo = (*(*m_renderers)[rendererIndex]->m_displays)[displayIndex];
  1062. *infoOut = displayInfo->m_info;
  1063. return false;
  1064. }
  1065. int GLMDisplayDB::GetModeCount( int rendererIndex, int displayIndex )
  1066. {
  1067. if (rendererIndex >= GetRendererCount())
  1068. return 0; // fail
  1069. if (displayIndex >= GetDisplayCount(rendererIndex))
  1070. return 0; // fail
  1071. GLMDisplayInfo *displayInfo = (*(*m_renderers)[rendererIndex]->m_displays)[displayIndex];
  1072. return displayInfo->m_modes->Count();
  1073. }
  1074. bool GLMDisplayDB::GetModeInfo( int rendererIndex, int displayIndex, int modeIndex, GLMDisplayModeInfoFields *infoOut )
  1075. {
  1076. memset( infoOut, 0, sizeof( GLMDisplayModeInfoFields ) );
  1077. if (rendererIndex >= GetRendererCount())
  1078. return true; // fail
  1079. if (displayIndex >= GetDisplayCount(rendererIndex))
  1080. return true; // fail
  1081. if (modeIndex >= GetModeCount(rendererIndex,displayIndex))
  1082. return true; // fail
  1083. if (modeIndex>=0)
  1084. {
  1085. GLMDisplayMode *displayModeInfo = (*(*(*m_renderers)[rendererIndex]->m_displays)[displayIndex]->m_modes)[ modeIndex ];
  1086. *infoOut = displayModeInfo->m_info;
  1087. }
  1088. else
  1089. {
  1090. // passing modeIndex = -1 means "tell me about current mode"..
  1091. GLMRendererInfo *rendInfo = (*m_renderers)[ rendererIndex ];
  1092. GLMDisplayInfo *dispinfo = (*rendInfo ->m_displays)[displayIndex];
  1093. CGDirectDisplayID cgid = dispinfo->m_info.m_cgDisplayID;
  1094. CFDictionaryRef curModeDict = CGDisplayCurrentMode( cgid );
  1095. CFNumberRef number;
  1096. CFBooleanRef boolean;
  1097. CFArrayRef modeList;
  1098. CGDisplayErr cgderr;
  1099. // get the mode number from the mode dict (using system mode numbering, not our sorted numbering)
  1100. if (curModeDict)
  1101. {
  1102. int modeIndex=0;
  1103. number = (CFNumberRef)CFDictionaryGetValue(curModeDict, kCGDisplayMode);
  1104. CFNumberGetValue(number, kCFNumberIntType, &modeIndex);
  1105. // grab the width and height, I am unclear on whether this is the displayed FB width or the display device width.
  1106. int screenWidth=0;
  1107. int screenHeight=0;
  1108. int refreshHz=0;
  1109. number = (CFNumberRef)CFDictionaryGetValue(curModeDict, kCGDisplayWidth);
  1110. CFNumberGetValue(number, kCFNumberIntType, &screenWidth);
  1111. number = (CFNumberRef)CFDictionaryGetValue(curModeDict, kCGDisplayHeight);
  1112. CFNumberGetValue(number, kCFNumberIntType, &screenHeight);
  1113. number = (CFNumberRef)CFDictionaryGetValue(curModeDict, kCGDisplayRefreshRate);
  1114. CFNumberGetValue(number, kCFNumberIntType, &refreshHz);
  1115. GLMPRINTF(( "-D- GLMDisplayDB::GetModeInfo sees mode-index=%d, width=%d, height=%d on CGID %08x (display index %d on rendererindex %d)",
  1116. modeIndex,
  1117. screenWidth,
  1118. screenHeight,
  1119. cgid,
  1120. displayIndex,
  1121. rendererIndex ));
  1122. // now match
  1123. int foundIndex = -1;
  1124. FOR_EACH_VEC( (*dispinfo->m_modes), i )
  1125. {
  1126. GLMDisplayMode *mode = (*dispinfo->m_modes)[i];
  1127. if (mode->m_info.m_modePixelWidth == screenWidth)
  1128. {
  1129. if (mode->m_info.m_modePixelHeight == screenHeight)
  1130. {
  1131. if (mode->m_info.m_modeRefreshHz == refreshHz)
  1132. {
  1133. foundIndex = i;
  1134. *infoOut = mode->m_info;
  1135. return false;
  1136. }
  1137. }
  1138. }
  1139. }
  1140. }
  1141. // if we get here, we could not find the mode
  1142. memset( infoOut, 0, sizeof( *infoOut ) );
  1143. return true; // fail
  1144. }
  1145. return false;
  1146. }
  1147. void GLMDisplayDB::Dump( void )
  1148. {
  1149. GLMPRINTF(("\n GLMDisplayDB @ %08x ",this ));
  1150. FOR_EACH_VEC( *m_renderers, i )
  1151. {
  1152. (*m_renderers)[i]->Dump(i);
  1153. }
  1154. }
  1155. //===============================================================================
  1156. GLMDisplayInfo::GLMDisplayInfo( CGDirectDisplayID displayID, CGOpenGLDisplayMask displayMask )
  1157. {
  1158. m_info.m_cgDisplayID = displayID;
  1159. m_info.m_glDisplayMask = displayMask;
  1160. // extract info about this display such as pixel width and height
  1161. m_info.m_displayPixelWidth = (uint)CGDisplayPixelsWide( m_info.m_cgDisplayID );
  1162. m_info.m_displayPixelHeight = (uint)CGDisplayPixelsHigh( m_info.m_cgDisplayID );
  1163. m_modes = NULL;
  1164. }
  1165. GLMDisplayInfo::~GLMDisplayInfo( void )
  1166. {
  1167. if (m_modes)
  1168. {
  1169. // delete all the new'd display modes
  1170. FOR_EACH_VEC( *m_modes, i )
  1171. {
  1172. delete (*this->m_modes)[i];
  1173. }
  1174. delete m_modes;
  1175. m_modes = NULL;
  1176. }
  1177. }
  1178. extern "C" int DisplayModeSortFunction( GLMDisplayMode * const *A, GLMDisplayMode * const *B )
  1179. {
  1180. int bigger = -1;
  1181. int smaller = 1; // adjust these for desired ordering
  1182. // check refreshrate - higher should win
  1183. if ( (*A)->m_info.m_modeRefreshHz > (*B)->m_info.m_modeRefreshHz )
  1184. {
  1185. return bigger;
  1186. }
  1187. else if ( (*A)->m_info.m_modeRefreshHz < (*B)->m_info.m_modeRefreshHz )
  1188. {
  1189. return smaller;
  1190. }
  1191. // check area - larger mode should win
  1192. int areaa = (*A)->m_info.m_modePixelWidth * (*A)->m_info.m_modePixelHeight;
  1193. int areab = (*B)->m_info.m_modePixelWidth * (*B)->m_info.m_modePixelHeight;
  1194. if ( areaa > areab )
  1195. {
  1196. return bigger;
  1197. }
  1198. else if ( areaa < areab )
  1199. {
  1200. return smaller;
  1201. }
  1202. return 0; // equal rank
  1203. }
  1204. void GLMDisplayInfo::PopulateModes( void )
  1205. {
  1206. Assert( !m_modes );
  1207. m_modes = new CUtlVector< GLMDisplayMode* >;
  1208. CFArrayRef modeList;
  1209. // CGDisplayErr cgderr;
  1210. CFDictionaryRef cgvidmode;
  1211. CFNumberRef number;
  1212. CFBooleanRef boolean;
  1213. modeList = CGDisplayAvailableModes( m_info.m_cgDisplayID );
  1214. if ( modeList != NULL )
  1215. {
  1216. // examine each mode
  1217. CFIndex count = CFArrayGetCount( modeList );
  1218. for (CFIndex i = 0; i < count; i++)
  1219. {
  1220. long modeHeight = 0, modeWidth = 0;
  1221. long depth = 0;
  1222. long refreshrate = 0;
  1223. Boolean usable, stretched = false;
  1224. // grab the mode dictionary
  1225. cgvidmode = (CFDictionaryRef)CFArrayGetValueAtIndex( modeList, i);
  1226. // grab mode params we need
  1227. number = (CFNumberRef)CFDictionaryGetValue(cgvidmode, kCGDisplayBitsPerPixel);
  1228. CFNumberGetValue(number, kCFNumberLongType, &depth);
  1229. boolean = (CFBooleanRef)CFDictionaryGetValue(cgvidmode, kCGDisplayModeUsableForDesktopGUI) ;
  1230. usable = CFBooleanGetValue(boolean);
  1231. boolean = (CFBooleanRef)CFDictionaryGetValue(cgvidmode, kCGDisplayModeIsStretched);
  1232. if (NULL != boolean)
  1233. {
  1234. stretched = CFBooleanGetValue(boolean);
  1235. }
  1236. if ( usable && (!stretched) && (depth==32) )
  1237. {
  1238. // we're going to log this mode to the mode table.
  1239. // get height of mode
  1240. number = (CFNumberRef)CFDictionaryGetValue( cgvidmode, kCGDisplayHeight );
  1241. CFNumberGetValue(number, kCFNumberLongType, &modeHeight);
  1242. // get width of mode
  1243. number = (CFNumberRef)CFDictionaryGetValue( cgvidmode, kCGDisplayWidth );
  1244. CFNumberGetValue(number, kCFNumberLongType, &modeWidth);
  1245. // get refresh rate of mode
  1246. number = (CFNumberRef)CFDictionaryGetValue( cgvidmode, kCGDisplayRefreshRate );
  1247. double flrefreshrate = 0.0f;
  1248. CFNumberGetValue( number, kCFNumberDoubleType, &flrefreshrate );
  1249. refreshrate = (int)flrefreshrate;
  1250. // exclude silly small modes
  1251. if ( (modeHeight >= 384) && (modeWidth >= 512) )
  1252. {
  1253. GLMDisplayMode *newmode = new GLMDisplayMode( modeWidth, modeHeight, refreshrate );
  1254. m_modes->AddToTail( newmode );
  1255. }
  1256. }
  1257. }
  1258. }
  1259. // now sort the modes
  1260. // primary key is refresh rate
  1261. // secondary key is area
  1262. m_modes->Sort( DisplayModeSortFunction );
  1263. }
  1264. void GLMDisplayInfo::Dump( int which )
  1265. {
  1266. GLMPRINTF(("\n #%d: GLMDisplayInfo @ %p, cg-id=%08x display-mask=%08x pixwidth=%d pixheight=%d", which, this, m_info.m_cgDisplayID, m_info.m_glDisplayMask, m_info.m_displayPixelWidth, m_info.m_displayPixelHeight ));
  1267. FOR_EACH_VEC( *m_modes, i )
  1268. {
  1269. (*m_modes)[i]->Dump(i);
  1270. }
  1271. }