diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /appframework/glmrendererinfo_osx.mm | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'appframework/glmrendererinfo_osx.mm')
| -rw-r--r-- | appframework/glmrendererinfo_osx.mm | 1583 |
1 files changed, 1583 insertions, 0 deletions
diff --git a/appframework/glmrendererinfo_osx.mm b/appframework/glmrendererinfo_osx.mm new file mode 100644 index 0000000..fbaf315 --- /dev/null +++ b/appframework/glmrendererinfo_osx.mm @@ -0,0 +1,1583 @@ +//========= 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, 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 + + long vMajor = 0; long vMinor = 0; long 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, kCFNumberLongType, &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, kCFNumberLongType, &screenWidth); + number = (CFNumberRef)CFDictionaryGetValue(curModeDict, kCGDisplayHeight); + CFNumberGetValue(number, kCFNumberLongType, &screenHeight); + number = (CFNumberRef)CFDictionaryGetValue(curModeDict, kCGDisplayRefreshRate); + CFNumberGetValue(number, kCFNumberLongType, &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)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); + } +} |