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
1583 lines
47 KiB
//========= Copyright 1996-2009, Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Defines a group of app systems that all have the same lifetime |
|
// that need to be connected/initialized, etc. in a well-defined order |
|
// |
|
// $Revision: $ |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include <Cocoa/Cocoa.h> |
|
#include <OpenGL/OpenGL.h> |
|
#include <OpenGL/gl.h> |
|
#include <OpenGL/glext.h> |
|
#include <IOKit/IOKitLib.h> |
|
|
|
|
|
#undef MIN |
|
#undef MAX |
|
#define DONT_DEFINE_BOOL // Don't define BOOL! |
|
#include "tier0/threadtools.h" |
|
#include "tier0/icommandline.h" |
|
#include "tier1/interface.h" |
|
#include "tier1/strtools.h" |
|
#include "tier1/utllinkedlist.h" |
|
#include "togl/rendermechanism.h" |
|
#include "appframework/ilaunchermgr.h" // gets pulled in from glmgr.h |
|
#include "appframework/iappsystemgroup.h" |
|
#include "inputsystem/ButtonCode.h" |
|
|
|
|
|
// some helper functions, relocated out of GLM since they are used here |
|
|
|
// this one makes a new context |
|
bool GLMDetectSLGU( void ); |
|
bool GLMDetectSLGU( void ) |
|
{ |
|
CGLError cgl_error = (CGLError)0; |
|
bool result = false; |
|
|
|
CGLContextObj oldctx = CGLGetCurrentContext(); |
|
|
|
static CGLPixelFormatAttribute attribs[] = |
|
{ |
|
kCGLPFADoubleBuffer, |
|
kCGLPFANoRecovery, |
|
kCGLPFAAccelerated, |
|
kCGLPFADepthSize, |
|
(CGLPixelFormatAttribute)0, |
|
kCGLPFAColorSize, |
|
(CGLPixelFormatAttribute)32, |
|
|
|
(CGLPixelFormatAttribute)0 // list term |
|
}; |
|
|
|
CGLPixelFormatObj pixfmtobj = NULL; |
|
GLint npix; |
|
|
|
CGLContextObj ctxobj = NULL; |
|
|
|
cgl_error = CGLChoosePixelFormat( attribs, &pixfmtobj, &npix ); |
|
if (!cgl_error) |
|
{ |
|
// got pixel format, make a context |
|
|
|
cgl_error = CGLCreateContext( pixfmtobj, NULL, &ctxobj ); |
|
if (!cgl_error) |
|
{ |
|
CGLSetCurrentContext( ctxobj ); |
|
|
|
// now do the test |
|
|
|
_CGLContextParameter kCGLCPGCDMPEngine = ((_CGLContextParameter)1314); |
|
|
|
GLint dummyval = 0; |
|
cgl_error = CGLGetParameter( CGLGetCurrentContext(), kCGLCPGCDMPEngine, &dummyval ); |
|
|
|
result = (!cgl_error); |
|
|
|
// all done, go back to old context, and destroy the temp one |
|
CGLSetCurrentContext( oldctx ); |
|
CGLDestroyContext( ctxobj ); |
|
} |
|
|
|
// destroy the pixel format obj |
|
CGLDestroyPixelFormat( pixfmtobj ); |
|
} |
|
|
|
return result; |
|
} |
|
|
|
|
|
bool GLMDetectScaledResolveMode( uint osComboVersion, bool hasSLGU ); |
|
bool GLMDetectScaledResolveMode( uint osComboVersion, bool hasSLGU ) |
|
{ |
|
bool result = false; |
|
|
|
// note this function assumes a current context on the renderer in question |
|
// and that FB blit and SLGU are present.. |
|
|
|
if (!hasSLGU) |
|
return false; |
|
|
|
if (osComboVersion <= 0x000A0604) // we know no one has it before 10.6.5 |
|
return false; |
|
|
|
// in 10.6.6 and later, just check for the ext string. |
|
char *gl_ext_string = (char*)glGetString(GL_EXTENSIONS); |
|
// avoid crashing due to strstr'ing NULL pointer returned from glGetString |
|
if (!gl_ext_string) |
|
gl_ext_string = ""; |
|
|
|
result = strstr(gl_ext_string, "GL_EXT_framebuffer_multisample_blit_scaled") != NULL; |
|
|
|
if ( !result ) |
|
{ |
|
// make two FBO's |
|
GLuint fbos[2]; |
|
GLuint rbos[2]; |
|
int extent = 64; |
|
|
|
// make two render buffers |
|
|
|
for( int fbi = 0; fbi < 2; fbi++ ) |
|
{ |
|
glGenFramebuffersEXT( 1, &fbos[fbi] ); CheckGLError( __LINE__ ); |
|
glBindFramebufferEXT( fbi ? GL_DRAW_FRAMEBUFFER_EXT : GL_READ_FRAMEBUFFER_EXT , fbos[fbi] ); CheckGLError( __LINE__ ); |
|
|
|
glGenRenderbuffersEXT( 1, &rbos[fbi] ); CheckGLError( __LINE__ ); |
|
glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, rbos[fbi] ); CheckGLError( __LINE__ ); |
|
|
|
// make it multisampled if 0 |
|
if (!fbi) |
|
{ |
|
glRenderbufferStorageMultisampleEXT( GL_RENDERBUFFER_EXT, 2, GL_RGBA8, extent,extent ); CheckGLError( __LINE__ ); |
|
} |
|
else |
|
{ |
|
glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_RGBA8, extent,extent ); CheckGLError( __LINE__ ); |
|
} |
|
|
|
// attach it |
|
// #0 gets to be read and multisampled |
|
// #1 gets to be draw and multisampled |
|
glFramebufferRenderbufferEXT( fbi ? GL_DRAW_FRAMEBUFFER_EXT : GL_READ_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_RENDERBUFFER_EXT, rbos[fbi] ); CheckGLError( __LINE__ ); |
|
} |
|
// now test |
|
while( glGetError() ) // clear error queue |
|
{ |
|
; |
|
} |
|
|
|
// now do the dummy blit |
|
glBlitFramebufferEXT( 0,0,extent,extent, 0,0,extent,extent, GL_COLOR_BUFFER_BIT, XGL_SCALED_RESOLVE_FASTEST_EXT ); |
|
|
|
// type of error we get back lets us know what the outcome is. |
|
// invalid enum error -> unsupported |
|
// no error or invalid op -> supported |
|
|
|
GLenum errorcode = (GLenum)glGetError(); |
|
switch(errorcode) |
|
{ |
|
// expected outcomes. |
|
|
|
// positive |
|
case GL_NO_ERROR: |
|
case GL_INVALID_OPERATION: |
|
result = true; // new scaled resolve detected |
|
break; |
|
|
|
default: |
|
result = false; // no scaled resolve |
|
break; |
|
} |
|
|
|
// unbind and wipe stuff |
|
|
|
glBindRenderbufferEXT( GL_RENDERBUFFER_EXT, 0 ); CheckGLError( __LINE__ ); |
|
|
|
for( int xfbi = 0; xfbi < 2; xfbi++ ) |
|
{ |
|
// unbind FBO |
|
glBindFramebufferEXT( xfbi ? GL_DRAW_FRAMEBUFFER_EXT : GL_READ_FRAMEBUFFER_EXT , 0 ); CheckGLError( __LINE__ ); |
|
|
|
// del FBO and RBO |
|
glDeleteFramebuffersEXT( 1, &fbos[xfbi] ); CheckGLError( __LINE__ ); |
|
glDeleteRenderbuffersEXT( 1, &rbos[xfbi] ); CheckGLError( __LINE__ ); |
|
} |
|
} |
|
|
|
return result; // no SLGU, no scaled resolve blit even possible |
|
} |
|
|
|
//=============================================================================== |
|
|
|
GLMRendererInfo::GLMRendererInfo( GLMRendererInfoFields *info ) |
|
{ |
|
NSAutoreleasePool *tempPool = [[NSAutoreleasePool alloc] init ]; |
|
|
|
// absorb info obtained so far by caller |
|
m_info = *info; |
|
m_displays = NULL; |
|
|
|
// gather more info using a dummy context |
|
unsigned int attribs[] = |
|
{ |
|
kCGLPFADoubleBuffer, kCGLPFANoRecovery, kCGLPFAAccelerated, |
|
kCGLPFADepthSize, 0, |
|
kCGLPFAColorSize, 32, |
|
kCGLPFARendererID, (unsigned int)info->m_rendererID, |
|
0 |
|
}; |
|
|
|
NSOpenGLPixelFormat *pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:(NSOpenGLPixelFormatAttribute*)attribs]; |
|
NSOpenGLContext *nsglCtx = [[NSOpenGLContext alloc] initWithFormat: pixFmt shareContext: NULL ]; |
|
|
|
[nsglCtx makeCurrentContext]; |
|
|
|
// run queries. |
|
char *gl_ext_string = (char*)glGetString(GL_EXTENSIONS); |
|
|
|
uint vers = m_info.m_osComboVersion; |
|
// avoid crashing due to strstr'ing NULL pointer returned from glGetString |
|
if (!gl_ext_string) |
|
gl_ext_string = ""; |
|
|
|
// effectively blacklist the renderer if it doesn't actually work; sort it to back of list |
|
if ( !nsglCtx ) |
|
{ |
|
m_info.m_vidMemory = 1; |
|
m_info.m_texMemory = 1; |
|
} |
|
|
|
//------------------------------------------------------------------- |
|
// booleans |
|
//------------------------------------------------------------------- |
|
// gamma writes. |
|
m_info.m_hasGammaWrites = true; |
|
if ( vers < 0x000A0600 ) // pre 10.6.0, no SRGB write - see http://developer.apple.com/graphicsimaging/opengl/capabilities/GLInfo_1058.html |
|
{ |
|
m_info.m_hasGammaWrites = false; |
|
} |
|
|
|
if (m_info.m_atiR5xx) |
|
{ |
|
m_info.m_hasGammaWrites = false; // it just don't, even post 10.6.3 |
|
} |
|
|
|
// if CLI option for fake SRGB mode is enabled, turn off this cap, act like we do not have EXT FB SRGB |
|
if (CommandLine()->FindParm("-glmenablefakesrgb")) |
|
{ |
|
m_info.m_hasGammaWrites = false; |
|
} |
|
|
|
// extension string *could* be checked, but on 10.6.3 the ext string is not there, but the func *is* |
|
|
|
//------------------------------------------------------------------- |
|
// mixed attach sizes for FBO |
|
m_info.m_hasMixedAttachmentSizes = true; |
|
if ( vers < 0x000A0603 ) // pre 10.6.3, no mixed attach sizes |
|
{ |
|
m_info.m_hasMixedAttachmentSizes = false; |
|
} |
|
else |
|
{ |
|
if (!strstr(gl_ext_string, "GL_ARB_framebuffer_object")) |
|
{ |
|
// ARB_framebuffer_object not available |
|
m_info.m_hasMixedAttachmentSizes = false; |
|
} |
|
} |
|
// also check ext string |
|
|
|
//------------------------------------------------------------------- |
|
// BGRA vert attribs |
|
m_info.m_hasBGRA = true; |
|
if ( vers < 0x000A0603 ) // pre 10.6.3, no BGRA attribs |
|
{ |
|
m_info.m_hasBGRA = false; |
|
} |
|
else |
|
{ |
|
if (!strstr(gl_ext_string, "EXT_vertex_array_bgra")) |
|
{ |
|
// EXT_vertex_array_bgra not available |
|
m_info.m_hasBGRA = false; |
|
} |
|
} |
|
|
|
//------------------------------------------------------------------- |
|
m_info.m_hasNewFullscreenMode = true; |
|
if ( vers < 0x000A0600 ) // pre 10.6.0, no clever window server full screen mode |
|
{ |
|
m_info.m_hasNewFullscreenMode = false; |
|
} |
|
|
|
//------------------------------------------------------------------- |
|
m_info.m_hasNativeClipVertexMode = true; |
|
// this one uses a heuristic, and allows overrides in case the heuristic is wrong |
|
// or someone wants to try a beta driver or something. |
|
|
|
// known bad combinations get turned off here.. |
|
|
|
// any ATI hardware... |
|
// TURNED OFF OS CHECK if (m_info.m_osComboVersion <= 0x000A0603) |
|
// still believe to be broken in 10.6.4 |
|
{ |
|
if (m_info.m_ati) |
|
{ |
|
m_info.m_hasNativeClipVertexMode = false; |
|
} |
|
} |
|
|
|
// R500, forever.. |
|
if (m_info.m_atiR5xx) |
|
{ |
|
m_info.m_hasNativeClipVertexMode = false; |
|
} |
|
|
|
// if user disabled them |
|
if (CommandLine()->FindParm("-glmdisableclipplanes")) |
|
{ |
|
m_info.m_hasNativeClipVertexMode = false; |
|
} |
|
|
|
// or maybe enabled them.. |
|
if (CommandLine()->FindParm("-glmenableclipplanes")) |
|
{ |
|
m_info.m_hasNativeClipVertexMode = true; |
|
} |
|
|
|
//------------------------------------------------------------------- |
|
m_info.m_hasOcclusionQuery = true; |
|
if (!strstr(gl_ext_string, "ARB_occlusion_query")) |
|
{ |
|
m_info.m_hasOcclusionQuery = false; // you don't got it! |
|
} |
|
|
|
//------------------------------------------------------------------- |
|
m_info.m_hasFramebufferBlit = true; |
|
if (!strstr(gl_ext_string, "EXT_framebuffer_blit")) |
|
{ |
|
m_info.m_hasFramebufferBlit = false; // you know you don't got it! |
|
} |
|
|
|
//------------------------------------------------------------------- |
|
m_info.m_maxAniso = 4; //FIXME needs real query |
|
|
|
//------------------------------------------------------------------- |
|
m_info.m_hasBindableUniforms = true; |
|
if (!strstr(gl_ext_string, "EXT_bindable_uniform")) |
|
{ |
|
m_info.m_hasBindableUniforms = false; |
|
} |
|
m_info.m_hasBindableUniforms = false; // hardwiring this path to false until we see how to accelerate it properly |
|
|
|
//------------------------------------------------------------------- |
|
m_info.m_hasUniformBuffers = true; |
|
if (!strstr(gl_ext_string, "ARB_uniform_buffer")) |
|
{ |
|
m_info.m_hasUniformBuffers = false; |
|
} |
|
|
|
//------------------------------------------------------------------- |
|
// test for performance pack (10.6.4+) |
|
|
|
bool perfPackageDetected = GLMDetectSLGU(); |
|
|
|
if (perfPackageDetected) |
|
{ |
|
m_info.m_hasPerfPackage1 = true; |
|
} |
|
|
|
if (CommandLine()->FindParm("-glmenableperfpackage")) // force it on |
|
{ |
|
m_info.m_hasPerfPackage1 = true; |
|
} |
|
|
|
if (CommandLine()->FindParm("-glmdisableperfpackage")) // force it off |
|
{ |
|
m_info.m_hasPerfPackage1 = false; |
|
} |
|
|
|
|
|
//------------------------------------------------------------------- |
|
// runtime options that aren't negotiable once set |
|
|
|
m_info.m_hasDualShaders = CommandLine()->FindParm("-glmdualshaders"); |
|
|
|
//------------------------------------------------------------------- |
|
// "can'ts " |
|
|
|
m_info.m_cantBlitReliably = (m_info.m_osComboVersion < 0x000A0606) && m_info.m_intel; //don't trust FBO blit on Intel before 10.6.6 |
|
if (CommandLine()->FindParm("-glmenabletrustblit")) |
|
{ |
|
m_info.m_cantBlitReliably = false; // we trust the blit, so set the cant-blit cap to false |
|
} |
|
if (CommandLine()->FindParm("-glmdisabletrustblit")) |
|
{ |
|
m_info.m_cantBlitReliably = true; // we do not trust the blit, so set the cant-blit cap to true |
|
} |
|
|
|
//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 |
|
//m_info.m_cantAttachSRGB = (m_info.m_ati && m_info.m_osComboVersion < 0x000A0600); //... does ATI have the same problem? |
|
m_info.m_cantAttachSRGB = (m_info.m_osComboVersion < 0x000A0600); // across the board on 10.5.x actually.. |
|
|
|
// MSAA resolve issues |
|
m_info.m_cantResolveFlipped = false; // initial stance |
|
|
|
if (m_info.m_ati) |
|
{ |
|
//Jan 2011 - ATI says it's better to do two step blit than to try and resolve upside down |
|
m_info.m_cantResolveFlipped = true; |
|
} |
|
|
|
if (m_info.m_nv) |
|
{ |
|
// we're going to mark it 'broken' unless perf package 1 (10.6.4+) is present |
|
if (!m_info.m_hasPerfPackage1) |
|
{ |
|
m_info.m_cantResolveFlipped = true; |
|
} |
|
} |
|
|
|
// this is just the private assessment of whather scaled resolve is available. |
|
// the activation of it will stay tied to the gl_minify_resolve_mode / gl_magnify_resolve_mode convars in glmgr |
|
if (m_info.m_osComboVersion > 0x000A0700 || CommandLine()->FindParm("-gl_enable_scaled_resolve") ) |
|
{ |
|
bool scaledResolveDetected = GLMDetectScaledResolveMode( m_info.m_osComboVersion, m_info.m_hasPerfPackage1 ); |
|
m_info.m_cantResolveScaled = !scaledResolveDetected; |
|
} |
|
else |
|
{ |
|
m_info.m_cantResolveScaled = true; |
|
} |
|
|
|
// and you can force it to be "available" if you really want to.. |
|
if ( CommandLine()->FindParm("-gl_force_enable_scaled_resolve") ) |
|
{ |
|
m_info.m_cantResolveScaled = false; |
|
} |
|
|
|
// gamma decode impacting shader codegen |
|
m_info.m_costlyGammaFlips = false; |
|
if (m_info.m_osComboVersion < 0x000A0600) // if Leopard |
|
m_info.m_costlyGammaFlips = true; |
|
|
|
if (m_info.m_atiR5xx) // or r5xx - always |
|
m_info.m_costlyGammaFlips = true; |
|
|
|
if ( (m_info.m_atiR6xx) && (m_info.m_osComboVersion < 0x000A0605) ) // or r6xx prior to 10.6.5 |
|
m_info.m_costlyGammaFlips = true; |
|
|
|
// The OpenGL driver for Intel HD4000 on 10.8 has a bug in the GLSL compiler, which was fixed |
|
// in 10.9 (and unlikely to be fixed in 10.8). See intelglmallocworkaround.h for more info. |
|
bool mountainLion = (m_info.m_osComboVersion >= 0x000A0800) && (m_info.m_osComboVersion < 0x000A0900); |
|
m_info.m_badDriver108Intel = mountainLion && m_info.m_intelHD4000; |
|
if ( CommandLine()->FindParm("-glmenablemallocworkaround") ) |
|
{ |
|
m_info.m_badDriver108Intel = true; |
|
} |
|
if ( CommandLine()->FindParm("-glmdisablemallocworkaround") ) |
|
{ |
|
m_info.m_badDriver108Intel = false; |
|
} |
|
|
|
[nsglCtx release]; |
|
[pixFmt release]; |
|
|
|
[tempPool release]; |
|
} |
|
|
|
GLMRendererInfo::~GLMRendererInfo( void ) |
|
{ |
|
if (m_displays) |
|
{ |
|
// delete all the new'd renderer infos that the table tracks |
|
FOR_EACH_VEC( *m_displays, i ) |
|
{ |
|
delete (*this->m_displays)[i]; |
|
} |
|
delete m_displays; |
|
m_displays = NULL; |
|
} |
|
} |
|
|
|
extern "C" int DisplayInfoSortFunction( GLMDisplayInfo* const *A, GLMDisplayInfo* const *B ) |
|
{ |
|
int bigger = -1; |
|
int smaller = 1; // adjust these to get the ordering you want |
|
|
|
// check main-ness - main should win |
|
|
|
uint maskOfMainDisplay = CGDisplayIDToOpenGLDisplayMask( CGMainDisplayID() ); |
|
//Assert( maskOfMainDisplay==1 ); // just curious |
|
|
|
int mainscreena = (*A)->m_info.m_glDisplayMask & maskOfMainDisplay; |
|
int mainscreenb = (*B)->m_info.m_glDisplayMask & maskOfMainDisplay; |
|
|
|
if ( mainscreena > mainscreenb ) |
|
{ |
|
return bigger; |
|
} |
|
else if ( mainscreena < mainscreenb ) |
|
{ |
|
return smaller; |
|
} |
|
|
|
// check area - larger screen should win |
|
int areaa = (*A)->m_info.m_displayPixelWidth * (*A)->m_info.m_displayPixelHeight; |
|
int areab = (*B)->m_info.m_displayPixelWidth * (*B)->m_info.m_displayPixelHeight; |
|
|
|
if ( areaa > areab ) |
|
{ |
|
return bigger; |
|
} |
|
else if ( areaa < areab ) |
|
{ |
|
return smaller; |
|
} |
|
|
|
return 0; // equal rank |
|
} |
|
|
|
|
|
void GLMRendererInfo::PopulateDisplays( void ) |
|
{ |
|
Assert( !m_displays ); |
|
m_displays = new CUtlVector< GLMDisplayInfo* >; |
|
|
|
for( int i=0; i<32; i++) |
|
{ |
|
// check mask to see if the selected display intersects this renderer |
|
CGOpenGLDisplayMask dspMask = (CGOpenGLDisplayMask)(1<<i); |
|
|
|
if ( m_info.m_displayMask & dspMask ) |
|
{ |
|
// exclude teeny displays (they may represent offline displays) |
|
// exclude inactive displays |
|
|
|
CGDirectDisplayID cgid = CGOpenGLDisplayMaskToDisplayID ( dspMask ); |
|
|
|
if ( (cgid != kCGNullDirectDisplay) && CGDisplayIsActive( cgid ) && (CGDisplayPixelsWide( cgid ) >= 512) && (CGDisplayPixelsHigh( cgid ) >= 384) ) |
|
{ |
|
GLMDisplayInfo *newdisp = new GLMDisplayInfo( cgid, dspMask ); |
|
m_displays->AddToTail( newdisp ); |
|
} |
|
} |
|
} |
|
|
|
// now sort the table of displays. |
|
m_displays->Sort( DisplayInfoSortFunction ); |
|
|
|
// then go back and ask each display to populate its display mode table. |
|
FOR_EACH_VEC( *m_displays, i ) |
|
{ |
|
(*this->m_displays)[i]->PopulateModes(); |
|
} |
|
} |
|
|
|
const char *CheesyRendererDecode( uint value ) |
|
{ |
|
switch(value) |
|
{ |
|
case 0x00020200 : return "Generic"; |
|
case 0x00020400 : return "GenericFloat"; |
|
case 0x00020600 : return "AppleSW"; |
|
case 0x00021000 : return "ATIRage128"; |
|
case 0x00021200 : return "ATIRadeon"; |
|
case 0x00021400 : return "ATIRagePro"; |
|
case 0x00021600 : return "ATIRadeon8500"; |
|
case 0x00021800 : return "ATIRadeon9700"; |
|
case 0x00021900 : return "ATIRadeonX1000"; |
|
case 0x00021A00 : return "ATIRadeonX2000"; |
|
case 0x00022000 : return "NVGeForce2MX"; |
|
case 0x00022200 : return "NVGeForce3"; |
|
case 0x00022400 : return "NVGeForceFX"; |
|
case 0x00022600 : return "NVGeForce8xxx"; |
|
case 0x00023000 : return "VTBladeXP2"; |
|
case 0x00024000 : return "Intel900"; |
|
case 0x00024200 : return "IntelX3100"; |
|
case 0x00040000 : return "Mesa3DFX"; |
|
|
|
default: return "UNKNOWN"; |
|
} |
|
} |
|
|
|
extern const char *GLMDecode( GLMThing_t thingtype, unsigned long value ); |
|
|
|
void GLMRendererInfo::Dump( int which ) |
|
{ |
|
GLMPRINTF(("\n #%d: GLMRendererInfo @ %08x, renderer-id=%s(%08x) display-mask=%08x vram=%dMB", |
|
which, this, |
|
CheesyRendererDecode( m_info.m_rendererID & 0x00FFFF00 ), m_info.m_rendererID, |
|
m_info.m_displayMask, |
|
m_info.m_vidMemory >> 20 |
|
)); |
|
GLMPRINTF(("\n VendorID=%04x DeviceID=%04x Model=%s", |
|
m_info.m_pciVendorID, |
|
m_info.m_pciDeviceID, |
|
m_info.m_pciModelString |
|
)); |
|
|
|
FOR_EACH_VEC( *m_displays, i ) |
|
{ |
|
(*m_displays)[i]->Dump(i); |
|
} |
|
} |
|
|
|
|
|
//=============================================================================== |
|
|
|
|
|
GLMDisplayDB::GLMDisplayDB ( void ) |
|
{ |
|
m_renderers = NULL; |
|
} |
|
|
|
GLMDisplayDB::~GLMDisplayDB ( void ) |
|
{ |
|
if (m_renderers) |
|
{ |
|
// delete all the new'd renderer infos that the table tracks |
|
FOR_EACH_VEC( *m_renderers, i ) |
|
{ |
|
delete (*this->m_renderers)[i]; |
|
} |
|
delete m_renderers; |
|
m_renderers = NULL; |
|
} |
|
} |
|
|
|
extern "C" int RendererInfoSortFunction( GLMRendererInfo * const *A, GLMRendererInfo* const *B ) |
|
{ |
|
int bigger = -1; |
|
int smaller = 1; |
|
|
|
// check VRAM |
|
if ( (*A)->m_info.m_vidMemory > (*B)->m_info.m_vidMemory ) |
|
{ |
|
return bigger; |
|
} |
|
else if ( (*A)->m_info.m_vidMemory < (*B)->m_info.m_vidMemory ) |
|
{ |
|
return smaller; |
|
} |
|
|
|
// check MSAA limit |
|
if ( (*A)->m_info.m_maxSamples > (*B)->m_info.m_maxSamples ) |
|
{ |
|
return bigger; |
|
} |
|
else if ( (*A)->m_info.m_maxSamples < (*B)->m_info.m_maxSamples ) |
|
{ |
|
return smaller; |
|
} |
|
|
|
/* |
|
// this was not a great idea here.. |
|
|
|
// check if one has the main screen - is that index 0 in all cases? |
|
uint maskOfMainDisplay = CGDisplayIDToOpenGLDisplayMask( CGMainDisplayID() ); |
|
Assert( maskOfMainDisplay==1 ); // just curious |
|
|
|
int mainscreena = (*A)->m_info.m_displayMask & maskOfMainDisplay; |
|
int mainscreenb = (*B)->m_info.m_displayMask & maskOfMainDisplay; |
|
|
|
if ( mainscreena > mainscreenb ) |
|
{ |
|
return bigger; |
|
} |
|
else if ( mainscreena < mainscreenb ) |
|
{ |
|
return smaller; |
|
} |
|
*/ |
|
|
|
return 0; // equal rank |
|
} |
|
|
|
/** some code that NV gave us. more generalized approach below.. |
|
|
|
static io_registry_entry_t lookup_dev_NV(char *name) |
|
{ |
|
mach_port_t master_port = 0; |
|
io_iterator_t iterator; |
|
io_registry_entry_t nub = 0; |
|
kern_return_t ret; |
|
|
|
IOMasterPort(MACH_PORT_NULL, &master_port); |
|
|
|
ret = IOServiceGetMatchingServices(master_port, IOServiceMatching(name), &iterator); |
|
|
|
if (iterator) { |
|
nub = IOIteratorNext(iterator); |
|
|
|
if (IOIteratorNext(iterator)) { |
|
printf("warning: more than one card?\n"); |
|
} |
|
IOObjectRelease(iterator); |
|
} |
|
IOObjectRelease(master_port); |
|
|
|
return nub; |
|
} |
|
|
|
|
|
void GetDriverInfoString_NV( char *driverNameBuf, int driverNameBufLen ) |
|
{ |
|
// courtesy NVIDIA dev rel |
|
|
|
io_registry_entry_t registry; |
|
kern_return_t ret; |
|
|
|
// |
|
// Get NVKernel / IOGLBundleName |
|
// |
|
|
|
registry = lookup_dev_NV("NVKernel"); |
|
if (!registry) { |
|
fprintf(stderr, "error: could not find NVKernel IORegistry entry!\n"); |
|
return; |
|
} |
|
|
|
CFMutableDictionaryRef entry; |
|
ret = IORegistryEntryCreateCFProperties(registry, &entry, kCFAllocatorDefault, 0); |
|
if (ret != kIOReturnSuccess) { |
|
fprintf(stderr, "error: could not create CFProperties dictionary!\n"); |
|
return; |
|
} |
|
|
|
CFStringRef bundle_name_ref = (CFStringRef) CFDictionaryGetValue(entry, CFSTR("IOGLBundleName")); |
|
if (!bundle_name_ref) { |
|
fprintf(stderr, "error: could not get IOGLBundleName reference!\n"); |
|
return; |
|
} |
|
|
|
const char *bundle_name = CFStringGetCStringPtr(bundle_name_ref, CFStringGetSystemEncoding()); |
|
if (!bundle_name) { |
|
fprintf(stderr, "error: could not get IOGLBundleName!\n"); |
|
return; |
|
} |
|
|
|
CFStringRef identifier = CFStringCreateWithFormat(NULL, NULL, CFSTR("com.apple.%s"), bundle_name); |
|
|
|
// |
|
// Get bundle information |
|
// |
|
|
|
CFBundleRef bundle; |
|
bundle = CFBundleGetBundleWithIdentifier(identifier); |
|
if (!bundle) { |
|
fprintf(stderr, "error: could not get GL driver bundle!\n"); |
|
return; |
|
} |
|
|
|
CFDictionaryRef dict; |
|
CFStringRef info; |
|
|
|
dict = CFBundleGetInfoDictionary(bundle); |
|
if (!dict) { |
|
fprintf(stderr, "error: could not get bundle info dictionary!\n"); |
|
return; |
|
} |
|
|
|
info = (CFStringRef) CFDictionaryGetValue(dict, CFSTR("CFBundleGetInfoString")); |
|
if (!info) { |
|
fprintf(stderr, "error: could not get CFBundleGetInfoString!\n"); |
|
return; |
|
} |
|
|
|
CFStringGetCString(info, driverNameBuf, driverNameBufLen, CFStringGetSystemEncoding()); |
|
|
|
IOObjectRelease(registry); |
|
} |
|
**/ |
|
|
|
void GLMDisplayDB::PopulateRenderers( void ) |
|
{ |
|
Assert( !m_renderers ); |
|
m_renderers = new CUtlVector< GLMRendererInfo* >; |
|
|
|
// now walk the renderer list |
|
// find the eligible ones and insert them into vector |
|
// if more than one, sort the vector by desirability with favorite at 0 |
|
// then ask each renderer object to populate its displays |
|
|
|
// turns out how you have to do this is to walk the display mask 1<<n.. |
|
// and query at each one, what renderers can hit that one. |
|
|
|
// when you find one, see if it's already in the vector above. if not, add it. |
|
// later, we sort them. |
|
|
|
for( int i=0; i<32; i++ ) |
|
{ |
|
CGLError cgl_err = (CGLError)0; |
|
CGLRendererInfoObj cgl_rend = NULL; |
|
GLint nrend; |
|
|
|
CGOpenGLDisplayMask dspMask = (CGOpenGLDisplayMask)(1<<i); |
|
CGDirectDisplayID cgid = CGOpenGLDisplayMaskToDisplayID( dspMask ); |
|
|
|
bool selected = true; // assume the best |
|
|
|
if (selected) |
|
{ |
|
if ( (cgid == kCGNullDirectDisplay) || (!CGDisplayIsActive( cgid )) ) |
|
{ |
|
selected = false; |
|
} |
|
} |
|
|
|
if (selected) |
|
{ |
|
cgl_err = CGLQueryRendererInfo( dspMask, &cgl_rend, &nrend ); // FIXME this call spams the console if you ask about an out of bounds display mask |
|
// "<Error>: unknown error code: invalid display" |
|
// we can fix that by getting the active display mask first. |
|
if (!cgl_err) |
|
{ |
|
// walk the renderers that can hit this display |
|
// add to table if not already in table, and minimums met |
|
|
|
for( int j=0; j<nrend; j++) |
|
{ |
|
int problems = 0; |
|
|
|
GLMRendererInfoFields fields; |
|
memset( &fields, 0, sizeof(fields) ); |
|
|
|
// early out if renderer ID already in the table |
|
cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPRendererID, &fields.m_rendererID ); problems += (cgl_err != 0); |
|
cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPDisplayMask, &fields.m_displayMask ); problems += (cgl_err != 0); |
|
|
|
cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPFullScreen, &fields.m_fullscreen ); problems += (cgl_err != 0); |
|
cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPAccelerated, &fields.m_accelerated ); problems += (cgl_err != 0); |
|
cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPWindow, &fields.m_windowed ); problems += (cgl_err != 0); |
|
|
|
cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPBufferModes, &fields.m_bufferModes ); problems += (cgl_err != 0); |
|
cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPColorModes, &fields.m_colorModes ); problems += (cgl_err != 0); |
|
cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPDepthModes, &fields.m_depthModes ); problems += (cgl_err != 0); |
|
cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPStencilModes, &fields.m_stencilModes ); problems += (cgl_err != 0); |
|
cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPMaxAuxBuffers, &fields.m_maxAuxBuffers ); problems += (cgl_err != 0); |
|
cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPMaxSampleBuffers, &fields.m_maxSampleBuffers ); problems += (cgl_err != 0); |
|
cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPMaxSamples, &fields.m_maxSamples ); problems += (cgl_err != 0); |
|
cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPSampleModes, &fields.m_sampleModes ); problems += (cgl_err != 0); |
|
cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPSampleAlpha, &fields.m_sampleAlpha ); problems += (cgl_err != 0); |
|
cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPVideoMemory, &fields.m_vidMemory ); problems += (cgl_err != 0); |
|
cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPTextureMemory, &fields.m_texMemory ); problems += (cgl_err != 0); |
|
|
|
// Make sure the renderer is attached to a display. |
|
GLint online; |
|
cgl_err = CGLDescribeRenderer( cgl_rend, j, kCGLRPOnline, &online ); problems += (cgl_err != 0); |
|
problems += ( online == 0 ); |
|
|
|
// decide if this renderer goes in the table. |
|
|
|
bool selected = !problems; |
|
|
|
if (selected) |
|
{ |
|
// grab the OS version |
|
|
|
SInt32 vMajor = 0; SInt32 vMinor = 0; SInt32 vMinorMinor = 0; |
|
|
|
OSStatus gestalt_err = 0; |
|
gestalt_err = Gestalt(gestaltSystemVersionMajor, &vMajor); |
|
Assert(!gestalt_err); |
|
|
|
gestalt_err = Gestalt(gestaltSystemVersionMinor, &vMinor); |
|
Assert(!gestalt_err); |
|
|
|
gestalt_err = Gestalt(gestaltSystemVersionBugFix, &vMinorMinor); |
|
Assert(!gestalt_err); |
|
|
|
//encode into one quantity - 10.6.3 becomes 0x000A0603 |
|
fields.m_osComboVersion = (vMajor << 16) | (vMinor << 8) | (vMinorMinor); |
|
|
|
if (CommandLine()->FindParm("-fakeleopard")) |
|
{ |
|
// lie |
|
fields.m_osComboVersion = 0x000A0508; |
|
} |
|
|
|
if (fields.m_osComboVersion < 0x000A0508) |
|
{ |
|
// no support below 10.5.8 |
|
// we'll wind up with no valid renderers and give up |
|
selected = false; |
|
} |
|
} |
|
|
|
if (selected) |
|
{ |
|
// gather more info from IOKit |
|
// cribbed from http://developer.apple.com/mac/library/samplecode/VideoHardwareInfo/listing3.html |
|
|
|
CFTypeRef typeCode; |
|
CFDataRef vendorID, deviceID, model; |
|
io_registry_entry_t dspPort; |
|
|
|
// Get the I/O Kit service port for the display |
|
dspPort = CGDisplayIOServicePort( cgid ); |
|
|
|
// Get the information for the device |
|
// The vendor ID, device ID, and model are all available as properties of the hardware's I/O Kit service port |
|
|
|
vendorID = (CFDataRef)IORegistryEntrySearchCFProperty(dspPort,kIOServicePlane,CFSTR("vendor-id"), kCFAllocatorDefault,kIORegistryIterateRecursively | kIORegistryIterateParents); |
|
deviceID = (CFDataRef)IORegistryEntrySearchCFProperty(dspPort,kIOServicePlane,CFSTR("device-id"), kCFAllocatorDefault,kIORegistryIterateRecursively | kIORegistryIterateParents); |
|
model = (CFDataRef)IORegistryEntrySearchCFProperty(dspPort,kIOServicePlane,CFSTR("model"), kCFAllocatorDefault,kIORegistryIterateRecursively | kIORegistryIterateParents); |
|
|
|
// Send the appropriate data to the outputs checking to validate the data |
|
if(vendorID) |
|
{ |
|
fields.m_pciVendorID = *((UInt32*)CFDataGetBytePtr(vendorID)); |
|
} |
|
else |
|
{ |
|
fields.m_pciVendorID = 0; |
|
} |
|
|
|
if(deviceID) |
|
{ |
|
fields.m_pciDeviceID = *((UInt32*)CFDataGetBytePtr(deviceID)); |
|
} |
|
else |
|
{ |
|
fields.m_pciDeviceID = 0; |
|
} |
|
|
|
if(model) |
|
{ |
|
int length = CFDataGetLength(model); |
|
char *data = (char*)CFDataGetBytePtr(model); |
|
Q_strncpy( fields.m_pciModelString, data, sizeof(fields.m_pciModelString) ); |
|
} |
|
else |
|
{ |
|
Q_strncpy( fields.m_pciModelString, "UnknownModel", sizeof(fields.m_pciModelString) ); |
|
} |
|
|
|
|
|
// iterate through IOAccelerators til we find one that matches the vendorid and deviceid of this renderer (ugh!) |
|
// this provides the driver version string which can in turn be used to uniquely identify bad drivers and special case for them |
|
// first example to date - forcing vsync on 10.6.4 + NV |
|
|
|
{ |
|
io_iterator_t ioIterator = (io_iterator_t)0; |
|
io_service_t ioAccelerator; |
|
kern_return_t ioResult = 0; |
|
bool ioDone = false; |
|
|
|
ioResult = IOServiceGetMatchingServices( kIOMasterPortDefault, IOServiceMatching("IOAccelerator"), &ioIterator ); |
|
if( ioResult == KERN_SUCCESS ) |
|
{ |
|
ioAccelerator = 0; |
|
|
|
while( ( !ioDone ) && ( ioAccelerator = IOIteratorNext( ioIterator ) ) ) |
|
{ |
|
io_service_t ioDevice; |
|
|
|
ioDevice = 0; |
|
ioResult = IORegistryEntryGetParentEntry( ioAccelerator, kIOServicePlane, &ioDevice); |
|
|
|
CFDataRef this_vendorID, this_deviceID; |
|
|
|
if(ioResult == KERN_SUCCESS) |
|
{ |
|
this_vendorID = (CFDataRef)IORegistryEntryCreateCFProperty(ioDevice, CFSTR("vendor-id"), kCFAllocatorDefault, kNilOptions ); |
|
this_deviceID = (CFDataRef)IORegistryEntryCreateCFProperty(ioDevice, CFSTR("device-id"), kCFAllocatorDefault, kNilOptions ); |
|
|
|
if (this_vendorID && this_deviceID) // null check.. |
|
{ |
|
// see if it matches. if so, do our business (get the extended version string), set ioDone, call it a day |
|
unsigned short this_vendorIDValue = *(unsigned short*)CFDataGetBytePtr(this_vendorID); |
|
unsigned short this_deviceIDValue = *(unsigned short*)CFDataGetBytePtr(this_deviceID); |
|
|
|
if ( (fields.m_pciVendorID == this_vendorIDValue) && (fields.m_pciDeviceID == this_deviceIDValue) ) |
|
{ |
|
// see if it matches. if so, do our business (get the extended version string), set ioDone, call it a day |
|
unsigned short* this_vendorIDBytes = (unsigned short*)CFDataGetBytePtr( this_vendorID ); |
|
unsigned short* this_deviceIDBytes = (unsigned short*)CFDataGetBytePtr( this_deviceID ); |
|
|
|
if (this_vendorIDBytes && this_deviceIDBytes) // null check... |
|
{ |
|
unsigned short this_vendorIDValue = *this_vendorIDBytes; |
|
unsigned short this_deviceIDValue = *this_deviceIDBytes; |
|
|
|
if ( (fields.m_pciVendorID == this_vendorIDValue) && (fields.m_pciDeviceID == this_deviceIDValue) ) |
|
{ |
|
// match, stop looking |
|
ioDone = true; |
|
|
|
// get extended info |
|
CFStringRef this_ioglName = (CFStringRef)IORegistryEntryCreateCFProperty( ioAccelerator, CFSTR("IOGLBundleName"), kCFAllocatorDefault, kNilOptions ); |
|
|
|
NSString *bundlePath = [ NSString stringWithFormat:@"/System/Library/Extensions/%@.bundle", this_ioglName ]; |
|
|
|
NSDictionary* this_driverDict = [ [NSBundle bundleWithPath: bundlePath] infoDictionary ]; |
|
if (this_driverDict) |
|
{ |
|
NSString* this_driverInfo = [ this_driverDict objectForKey:@"CFBundleGetInfoString" ]; |
|
if ( this_driverInfo ) |
|
{ |
|
const char* theString = [ this_driverInfo UTF8String ]; |
|
|
|
strncpy(fields.m_driverInfoString, theString, sizeof( fields.m_driverInfoString ) ); |
|
} |
|
} |
|
|
|
// [bundlePath release]; |
|
|
|
CFRelease(this_ioglName); |
|
} |
|
} |
|
|
|
CFRelease(this_vendorID); |
|
CFRelease(this_deviceID); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
IOObjectRelease(ioAccelerator); |
|
IOObjectRelease(ioIterator); |
|
} |
|
|
|
// Release vendorID, deviceID, and model as appropriate |
|
if(vendorID) |
|
CFRelease(vendorID); |
|
if(deviceID) |
|
CFRelease(deviceID); |
|
if(model) |
|
CFRelease(model); |
|
|
|
// generate shorthand bools |
|
switch( fields.m_pciVendorID ) |
|
{ |
|
case 0x1002: //ATI |
|
{ |
|
fields.m_ati = true; |
|
|
|
// http://www.pcidatabase.com/search.php?device_search_str=radeon&device_search.x=0&device_search.y=0&device_search=search+devices |
|
|
|
// Mac-relevant ATI R5xx PCI device ID's lie in this range: 0x7100 - 0x72FF |
|
// X1600, X1900, X1950 |
|
if ( (fields.m_pciDeviceID >= 0x7100) && (fields.m_pciDeviceID <= 0x72ff) ) |
|
{ |
|
fields.m_atiR5xx = true; |
|
} |
|
|
|
// R6xx PCI device ID's lie in these ranges: |
|
// 0x94C1 - 0x9515 ... also 0x9581 - 0x9713 |
|
// 2400HD, 2600HD, 3870, et al |
|
if ( |
|
( (fields.m_pciDeviceID >= 0x94C1) && (fields.m_pciDeviceID <= 0x9515) ) |
|
|| ( (fields.m_pciDeviceID >= 0x9581) && (fields.m_pciDeviceID <= 0x9713) ) |
|
) |
|
{ |
|
fields.m_atiR6xx = true; |
|
} |
|
|
|
// R7xx PCI device ID's lie in: 0x9440 - 0x9460, also 9480-94b5. |
|
// why there is an HD5000 at 9462, I dunno. Don't think that's an R8xx part. |
|
if ( |
|
( (fields.m_pciDeviceID >= 0x9440) && (fields.m_pciDeviceID <= 0x9460) ) |
|
|| ( (fields.m_pciDeviceID >= 0x9480) && (fields.m_pciDeviceID <= 0x94B5) ) |
|
) |
|
{ |
|
fields.m_atiR7xx = true; |
|
} |
|
|
|
// R8xx: 0x6898-0x68BE |
|
if ( (fields.m_pciDeviceID >= 0x6898) && (fields.m_pciDeviceID <= 0x68Be) ) |
|
{ |
|
fields.m_atiR8xx = true; |
|
} |
|
|
|
#if 0 |
|
// turned off, but we could use this for cross check. |
|
// we could also use the bit encoding of the renderer ID to ferret out a geberation clue. |
|
|
|
// string-scan for each generation |
|
// 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 |
|
if (strstr("X1600", fields.m_pciModelString) || strstr("X1900", fields.m_pciModelString) || strstr("X1950", fields.m_pciModelString) ) |
|
{ |
|
fields.m_atiR5xx = true; |
|
} |
|
|
|
if (strstr("2600", fields.m_pciModelString) || strstr("3870", fields.m_pciModelString) || strstr("X2000", fields.m_pciModelString) ) |
|
{ |
|
fields.m_atiR6xx = true; |
|
} |
|
|
|
if (strstr("4670", fields.m_pciModelString) || strstr("4650", fields.m_pciModelString) || strstr("4850", fields.m_pciModelString)|| strstr("4870", fields.m_pciModelString) ) |
|
{ |
|
fields.m_atiR7xx = true; |
|
} |
|
#endif |
|
} |
|
break; |
|
|
|
case 0x8086: //INTC |
|
{ |
|
fields.m_intel = true; |
|
|
|
switch( fields.m_pciDeviceID ) |
|
{ |
|
case 0x27A6: fields.m_intel95x = true; break; // GMA 950 |
|
case 0x2A02: fields.m_intel3100 = true; break; // X3100 |
|
case 0x0166: fields.m_intelHD4000 = true; break; // HD4000 |
|
} |
|
} |
|
break; |
|
|
|
case 0x10DE: //NV |
|
{ |
|
fields.m_nv = true; |
|
|
|
// G7x: 0x0391 0x393 0x0395 (7300/7600 GT) 0x009D (Quadro FX) |
|
if ( (fields.m_pciDeviceID == 0x0391) || (fields.m_pciDeviceID == 0x0393) || (fields.m_pciDeviceID == 0x0395) || (fields.m_pciDeviceID == 0x009D) ) |
|
{ |
|
fields.m_nvG7x = true; |
|
} |
|
|
|
// G8x: 0400-04ff, also 0x5E1 (GTX280) through 0x08FF |
|
if ( |
|
( (fields.m_pciDeviceID >= 0x0400) && (fields.m_pciDeviceID <= 0x04ff) ) |
|
|| ( (fields.m_pciDeviceID >= 0x05E1) && (fields.m_pciDeviceID <= 0x08ff) ) |
|
) |
|
{ |
|
fields.m_nvG8x = true; |
|
} |
|
|
|
if ( fields.m_pciDeviceID > 0x0900 ) |
|
{ |
|
fields.m_nvNewer = true; |
|
} |
|
|
|
// detect the specific revision of NV driver in 10.6.4 that caused all the grief |
|
if (strstr(fields.m_driverInfoString, "1.6.16.11 (19.5.8f01)")) |
|
{ |
|
fields.m_badDriver1064NV = true; |
|
} |
|
} |
|
break; |
|
} |
|
} |
|
|
|
if (selected) |
|
{ |
|
// dupe check |
|
FOR_EACH_VEC( *m_renderers, i ) |
|
{ |
|
uint rendid = (*m_renderers)[i]->m_info.m_rendererID; |
|
|
|
if ( rendid == fields.m_rendererID ) |
|
{ |
|
// don't add to table, it's a dupe |
|
selected = false; |
|
} |
|
} |
|
} |
|
|
|
if (selected) |
|
{ |
|
// criteria check |
|
if (fields.m_fullscreen==0) |
|
selected = false; |
|
if (fields.m_accelerated==0) |
|
selected = false; |
|
if (fields.m_windowed==0) |
|
selected = false; |
|
} |
|
|
|
Assert( fields.m_displayMask != 0 ); |
|
|
|
if (selected) |
|
{ |
|
// add to table |
|
// note this constructor makes a dummy context just long enough to query remaining fields in the m_info. |
|
GLMRendererInfo *newinfo = new GLMRendererInfo( &fields ); |
|
m_renderers->AddToTail( newinfo ); |
|
} |
|
} |
|
if (cgl_rend) |
|
{ |
|
CGLDestroyRendererInfo( cgl_rend ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
// now sort the table. |
|
m_renderers->Sort( RendererInfoSortFunction ); |
|
|
|
// then go back and ask each renderer to populate its display info table. |
|
FOR_EACH_VEC( *m_renderers, i ) |
|
{ |
|
(*m_renderers)[i]->PopulateDisplays(); |
|
} |
|
} |
|
|
|
void GLMDisplayDB::PopulateFakeAdapters( uint realRendererIndex ) // fake adapters = one real adapter times however many displays are on it |
|
{ |
|
// presumption is that renderers have been populated. |
|
Assert( GetRendererCount() > 0 ); |
|
Assert( realRendererIndex < GetRendererCount() ); |
|
|
|
m_fakeAdapters.RemoveAll(); |
|
|
|
// for( int r = 0; r < GetRendererCount(); r++ ) |
|
int r = realRendererIndex; |
|
{ |
|
for( int d = 0; d < GetDisplayCount( r ); d++ ) |
|
{ |
|
GLMFakeAdapter temp; |
|
|
|
temp.m_rendererIndex = r; |
|
temp.m_displayIndex = d; |
|
|
|
m_fakeAdapters.AddToTail( temp ); |
|
} |
|
} |
|
} |
|
|
|
void GLMDisplayDB::Populate(void) |
|
{ |
|
this->PopulateRenderers(); |
|
|
|
// passing in zero here, constrains the set of fake adapters (GL renderer + a display) to the ones using the highest ranked renderer. |
|
//FIXME introduce some kind of convar allowing selection of other GPU's in the system. |
|
|
|
int realRendererIndex = 0; |
|
|
|
if (CommandLine()->FindParm("-glmrenderer0")) |
|
realRendererIndex = 0; |
|
if (CommandLine()->FindParm("-glmrenderer1")) |
|
realRendererIndex = 1; |
|
if (CommandLine()->FindParm("-glmrenderer2")) |
|
realRendererIndex = 2; |
|
if (CommandLine()->FindParm("-glmrenderer3")) |
|
realRendererIndex = 3; |
|
|
|
if (realRendererIndex >= GetRendererCount()) |
|
{ |
|
// fall back to 0 |
|
realRendererIndex = 0; |
|
} |
|
|
|
this->PopulateFakeAdapters( 0 ); |
|
|
|
#if GLMDEBUG |
|
this->Dump(); |
|
#endif |
|
} |
|
|
|
|
|
|
|
int GLMDisplayDB::GetFakeAdapterCount( void ) |
|
{ |
|
return m_fakeAdapters.Count(); |
|
} |
|
|
|
bool GLMDisplayDB::GetFakeAdapterInfo( int fakeAdapterIndex, int *rendererOut, int *displayOut, GLMRendererInfoFields *rendererInfoOut, GLMDisplayInfoFields *displayInfoOut ) |
|
{ |
|
if (fakeAdapterIndex >= GetFakeAdapterCount() ) |
|
{ |
|
*rendererOut = 0; |
|
*displayOut = 0; |
|
return true; // fail |
|
} |
|
|
|
*rendererOut = m_fakeAdapters[fakeAdapterIndex].m_rendererIndex; |
|
*displayOut = m_fakeAdapters[fakeAdapterIndex].m_displayIndex; |
|
|
|
bool rendResult = GetRendererInfo( *rendererOut, rendererInfoOut ); |
|
bool dispResult = GetDisplayInfo( *rendererOut, *displayOut, displayInfoOut ); |
|
|
|
return rendResult || dispResult; |
|
} |
|
|
|
|
|
int GLMDisplayDB::GetRendererCount( void ) |
|
{ |
|
return m_renderers->Count(); |
|
} |
|
|
|
bool GLMDisplayDB::GetRendererInfo( int rendererIndex, GLMRendererInfoFields *infoOut ) |
|
{ |
|
memset( infoOut, 0, sizeof( GLMRendererInfoFields ) ); |
|
|
|
if (rendererIndex >= GetRendererCount()) |
|
return true; // fail |
|
|
|
GLMRendererInfo *rendInfo = (*m_renderers)[rendererIndex]; |
|
*infoOut = rendInfo->m_info; |
|
|
|
return false; |
|
} |
|
|
|
int GLMDisplayDB::GetDisplayCount( int rendererIndex ) |
|
{ |
|
if (rendererIndex >= GetRendererCount()) |
|
return 0; // fail |
|
|
|
GLMRendererInfo *rendInfo = (*m_renderers)[rendererIndex]; |
|
|
|
return rendInfo->m_displays->Count(); |
|
} |
|
|
|
bool GLMDisplayDB::GetDisplayInfo( int rendererIndex, int displayIndex, GLMDisplayInfoFields *infoOut ) |
|
{ |
|
memset( infoOut, 0, sizeof( GLMDisplayInfoFields ) ); |
|
|
|
if (rendererIndex >= GetRendererCount()) |
|
return true; // fail |
|
|
|
if (displayIndex >= GetDisplayCount(rendererIndex)) |
|
return true; // fail |
|
|
|
GLMDisplayInfo *displayInfo = (*(*m_renderers)[rendererIndex]->m_displays)[displayIndex]; |
|
*infoOut = displayInfo->m_info; |
|
|
|
return false; |
|
} |
|
|
|
int GLMDisplayDB::GetModeCount( int rendererIndex, int displayIndex ) |
|
{ |
|
if (rendererIndex >= GetRendererCount()) |
|
return 0; // fail |
|
|
|
if (displayIndex >= GetDisplayCount(rendererIndex)) |
|
return 0; // fail |
|
|
|
GLMDisplayInfo *displayInfo = (*(*m_renderers)[rendererIndex]->m_displays)[displayIndex]; |
|
|
|
return displayInfo->m_modes->Count(); |
|
} |
|
|
|
bool GLMDisplayDB::GetModeInfo( int rendererIndex, int displayIndex, int modeIndex, GLMDisplayModeInfoFields *infoOut ) |
|
{ |
|
memset( infoOut, 0, sizeof( GLMDisplayModeInfoFields ) ); |
|
|
|
if (rendererIndex >= GetRendererCount()) |
|
return true; // fail |
|
|
|
if (displayIndex >= GetDisplayCount(rendererIndex)) |
|
return true; // fail |
|
|
|
if (modeIndex >= GetModeCount(rendererIndex,displayIndex)) |
|
return true; // fail |
|
|
|
if (modeIndex>=0) |
|
{ |
|
GLMDisplayMode *displayModeInfo = (*(*(*m_renderers)[rendererIndex]->m_displays)[displayIndex]->m_modes)[ modeIndex ]; |
|
*infoOut = displayModeInfo->m_info; |
|
} |
|
else |
|
{ |
|
// passing modeIndex = -1 means "tell me about current mode".. |
|
|
|
GLMRendererInfo *rendInfo = (*m_renderers)[ rendererIndex ]; |
|
GLMDisplayInfo *dispinfo = (*rendInfo ->m_displays)[displayIndex]; |
|
CGDirectDisplayID cgid = dispinfo->m_info.m_cgDisplayID; |
|
|
|
CFDictionaryRef curModeDict = CGDisplayCurrentMode( cgid ); |
|
CFNumberRef number; |
|
CFBooleanRef boolean; |
|
CFArrayRef modeList; |
|
CGDisplayErr cgderr; |
|
|
|
// get the mode number from the mode dict (using system mode numbering, not our sorted numbering) |
|
if (curModeDict) |
|
{ |
|
int modeIndex=0; |
|
number = (CFNumberRef)CFDictionaryGetValue(curModeDict, kCGDisplayMode); |
|
CFNumberGetValue(number, kCFNumberIntType, &modeIndex); |
|
|
|
// grab the width and height, I am unclear on whether this is the displayed FB width or the display device width. |
|
int screenWidth=0; |
|
int screenHeight=0; |
|
int refreshHz=0; |
|
|
|
number = (CFNumberRef)CFDictionaryGetValue(curModeDict, kCGDisplayWidth); |
|
CFNumberGetValue(number, kCFNumberIntType, &screenWidth); |
|
number = (CFNumberRef)CFDictionaryGetValue(curModeDict, kCGDisplayHeight); |
|
CFNumberGetValue(number, kCFNumberIntType, &screenHeight); |
|
number = (CFNumberRef)CFDictionaryGetValue(curModeDict, kCGDisplayRefreshRate); |
|
CFNumberGetValue(number, kCFNumberIntType, &refreshHz); |
|
|
|
GLMPRINTF(( "-D- GLMDisplayDB::GetModeInfo sees mode-index=%d, width=%d, height=%d on CGID %08x (display index %d on rendererindex %d)", |
|
modeIndex, |
|
screenWidth, |
|
screenHeight, |
|
cgid, |
|
displayIndex, |
|
rendererIndex )); |
|
|
|
// now match |
|
int foundIndex = -1; |
|
FOR_EACH_VEC( (*dispinfo->m_modes), i ) |
|
{ |
|
GLMDisplayMode *mode = (*dispinfo->m_modes)[i]; |
|
|
|
if (mode->m_info.m_modePixelWidth == screenWidth) |
|
{ |
|
if (mode->m_info.m_modePixelHeight == screenHeight) |
|
{ |
|
if (mode->m_info.m_modeRefreshHz == refreshHz) |
|
{ |
|
foundIndex = i; |
|
*infoOut = mode->m_info; |
|
return false; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
// if we get here, we could not find the mode |
|
memset( infoOut, 0, sizeof( *infoOut ) ); |
|
return true; // fail |
|
} |
|
return false; |
|
} |
|
|
|
|
|
void GLMDisplayDB::Dump( void ) |
|
{ |
|
GLMPRINTF(("\n GLMDisplayDB @ %08x ",this )); |
|
|
|
FOR_EACH_VEC( *m_renderers, i ) |
|
{ |
|
(*m_renderers)[i]->Dump(i); |
|
} |
|
} |
|
|
|
//=============================================================================== |
|
|
|
GLMDisplayInfo::GLMDisplayInfo( CGDirectDisplayID displayID, CGOpenGLDisplayMask displayMask ) |
|
{ |
|
m_info.m_cgDisplayID = displayID; |
|
m_info.m_glDisplayMask = displayMask; |
|
|
|
// extract info about this display such as pixel width and height |
|
m_info.m_displayPixelWidth = (uint)CGDisplayPixelsWide( m_info.m_cgDisplayID ); |
|
m_info.m_displayPixelHeight = (uint)CGDisplayPixelsHigh( m_info.m_cgDisplayID ); |
|
|
|
m_modes = NULL; |
|
} |
|
|
|
GLMDisplayInfo::~GLMDisplayInfo( void ) |
|
{ |
|
if (m_modes) |
|
{ |
|
// delete all the new'd display modes |
|
FOR_EACH_VEC( *m_modes, i ) |
|
{ |
|
delete (*this->m_modes)[i]; |
|
} |
|
delete m_modes; |
|
m_modes = NULL; |
|
} |
|
} |
|
|
|
|
|
extern "C" int DisplayModeSortFunction( GLMDisplayMode * const *A, GLMDisplayMode * const *B ) |
|
{ |
|
int bigger = -1; |
|
int smaller = 1; // adjust these for desired ordering |
|
|
|
// check refreshrate - higher should win |
|
if ( (*A)->m_info.m_modeRefreshHz > (*B)->m_info.m_modeRefreshHz ) |
|
{ |
|
return bigger; |
|
} |
|
else if ( (*A)->m_info.m_modeRefreshHz < (*B)->m_info.m_modeRefreshHz ) |
|
{ |
|
return smaller; |
|
} |
|
|
|
// check area - larger mode should win |
|
int areaa = (*A)->m_info.m_modePixelWidth * (*A)->m_info.m_modePixelHeight; |
|
int areab = (*B)->m_info.m_modePixelWidth * (*B)->m_info.m_modePixelHeight; |
|
|
|
if ( areaa > areab ) |
|
{ |
|
return bigger; |
|
} |
|
else if ( areaa < areab ) |
|
{ |
|
return smaller; |
|
} |
|
|
|
return 0; // equal rank |
|
} |
|
|
|
|
|
void GLMDisplayInfo::PopulateModes( void ) |
|
{ |
|
Assert( !m_modes ); |
|
m_modes = new CUtlVector< GLMDisplayMode* >; |
|
|
|
CFArrayRef modeList; |
|
// CGDisplayErr cgderr; |
|
CFDictionaryRef cgvidmode; |
|
CFNumberRef number; |
|
CFBooleanRef boolean; |
|
|
|
modeList = CGDisplayAvailableModes( m_info.m_cgDisplayID ); |
|
if ( modeList != NULL ) |
|
{ |
|
// examine each mode |
|
CFIndex count = CFArrayGetCount( modeList ); |
|
|
|
for (CFIndex i = 0; i < count; i++) |
|
{ |
|
long modeHeight = 0, modeWidth = 0; |
|
long depth = 0; |
|
long refreshrate = 0; |
|
Boolean usable, stretched = false; |
|
|
|
// grab the mode dictionary |
|
cgvidmode = (CFDictionaryRef)CFArrayGetValueAtIndex( modeList, i); |
|
|
|
// grab mode params we need |
|
number = (CFNumberRef)CFDictionaryGetValue(cgvidmode, kCGDisplayBitsPerPixel); |
|
CFNumberGetValue(number, kCFNumberLongType, &depth); |
|
|
|
boolean = (CFBooleanRef)CFDictionaryGetValue(cgvidmode, kCGDisplayModeUsableForDesktopGUI) ; |
|
usable = CFBooleanGetValue(boolean); |
|
|
|
boolean = (CFBooleanRef)CFDictionaryGetValue(cgvidmode, kCGDisplayModeIsStretched); |
|
if (NULL != boolean) |
|
{ |
|
stretched = CFBooleanGetValue(boolean); |
|
} |
|
|
|
if ( usable && (!stretched) && (depth==32) ) |
|
{ |
|
// we're going to log this mode to the mode table. |
|
|
|
// get height of mode |
|
number = (CFNumberRef)CFDictionaryGetValue( cgvidmode, kCGDisplayHeight ); |
|
CFNumberGetValue(number, kCFNumberLongType, &modeHeight); |
|
|
|
// get width of mode |
|
number = (CFNumberRef)CFDictionaryGetValue( cgvidmode, kCGDisplayWidth ); |
|
CFNumberGetValue(number, kCFNumberLongType, &modeWidth); |
|
|
|
// get refresh rate of mode |
|
number = (CFNumberRef)CFDictionaryGetValue( cgvidmode, kCGDisplayRefreshRate ); |
|
double flrefreshrate = 0.0f; |
|
CFNumberGetValue( number, kCFNumberDoubleType, &flrefreshrate ); |
|
refreshrate = (int)flrefreshrate; |
|
|
|
// exclude silly small modes |
|
if ( (modeHeight >= 384) && (modeWidth >= 512) ) |
|
{ |
|
GLMDisplayMode *newmode = new GLMDisplayMode( modeWidth, modeHeight, refreshrate ); |
|
m_modes->AddToTail( newmode ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
// now sort the modes |
|
// primary key is refresh rate |
|
// secondary key is area |
|
|
|
m_modes->Sort( DisplayModeSortFunction ); |
|
} |
|
|
|
|
|
void GLMDisplayInfo::Dump( int which ) |
|
{ |
|
GLMPRINTF(("\n #%d: GLMDisplayInfo @ %08x, cg-id=%08x display-mask=%08x pixwidth=%d pixheight=%d", which, (int)(intp)this, m_info.m_cgDisplayID, m_info.m_glDisplayMask, m_info.m_displayPixelWidth, m_info.m_displayPixelHeight )); |
|
|
|
FOR_EACH_VEC( *m_modes, i ) |
|
{ |
|
(*m_modes)[i]->Dump(i); |
|
} |
|
}
|
|
|