/** * @file lldxhardware.cpp * @brief LLDXHardware implementation * * $LicenseInfo:firstyear=2001&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #ifdef LL_WINDOWS // Culled from some Microsoft sample code #include "linden_common.h" #define INITGUID #include #undef INITGUID #include #include #include #include "lldxhardware.h" #include "llerror.h" #include "llstring.h" #include "llstl.h" #include "lltimer.h" void (*gWriteDebug)(const char* msg) = NULL; LLDXHardware gDXHardware; //----------------------------------------------------------------------------- // Defines, and constants //----------------------------------------------------------------------------- #define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } } #define SAFE_DELETE_ARRAY(p) { if(p) { delete[] (p); (p)=NULL; } } #define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } } typedef BOOL ( WINAPI* PfnCoSetProxyBlanket )( IUnknown* pProxy, DWORD dwAuthnSvc, DWORD dwAuthzSvc, OLECHAR* pServerPrincName, DWORD dwAuthnLevel, DWORD dwImpLevel, RPC_AUTH_IDENTITY_HANDLE pAuthInfo, DWORD dwCapabilities ); HRESULT GetVideoMemoryViaWMI(WCHAR* strInputDeviceID, DWORD* pdwAdapterRam) { HRESULT hr; bool bGotMemory = false; IWbemLocator* pIWbemLocator = nullptr; IWbemServices* pIWbemServices = nullptr; BSTR pNamespace = nullptr; *pdwAdapterRam = 0; CoInitializeEx(0, COINIT_APARTMENTTHREADED); hr = CoCreateInstance( CLSID_WbemLocator, nullptr, CLSCTX_INPROC_SERVER, IID_IWbemLocator, ( LPVOID* )&pIWbemLocator ); #ifdef PRINTF_DEBUGGING if( FAILED( hr ) ) wprintf( L"WMI: CoCreateInstance failed: 0x%0.8x\n", hr ); #endif if( SUCCEEDED( hr ) && pIWbemLocator ) { // Using the locator, connect to WMI in the given namespace. pNamespace = SysAllocString( L"\\\\.\\root\\cimv2" ); hr = pIWbemLocator->ConnectServer( pNamespace, nullptr, nullptr, 0L, 0L, nullptr, nullptr, &pIWbemServices ); #ifdef PRINTF_DEBUGGING if( FAILED( hr ) ) wprintf( L"WMI: pIWbemLocator->ConnectServer failed: 0x%0.8x\n", hr ); #endif if( SUCCEEDED( hr ) && pIWbemServices != 0 ) { HINSTANCE hinstOle32 = nullptr; hinstOle32 = LoadLibraryW( L"ole32.dll" ); if( hinstOle32 ) { PfnCoSetProxyBlanket pfnCoSetProxyBlanket = nullptr; pfnCoSetProxyBlanket = ( PfnCoSetProxyBlanket )GetProcAddress( hinstOle32, "CoSetProxyBlanket" ); if( pfnCoSetProxyBlanket != 0 ) { // Switch security level to IMPERSONATE. pfnCoSetProxyBlanket( pIWbemServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, nullptr, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, nullptr, 0 ); } FreeLibrary( hinstOle32 ); } IEnumWbemClassObject* pEnumVideoControllers = nullptr; BSTR pClassName = nullptr; pClassName = SysAllocString( L"Win32_VideoController" ); hr = pIWbemServices->CreateInstanceEnum( pClassName, 0, nullptr, &pEnumVideoControllers ); #ifdef PRINTF_DEBUGGING if( FAILED( hr ) ) wprintf( L"WMI: pIWbemServices->CreateInstanceEnum failed: 0x%0.8x\n", hr ); #endif if( SUCCEEDED( hr ) && pEnumVideoControllers ) { IWbemClassObject* pVideoControllers[10] = {0}; DWORD uReturned = 0; BSTR pPropName = nullptr; // Get the first one in the list pEnumVideoControllers->Reset(); hr = pEnumVideoControllers->Next( 5000, // timeout in 5 seconds 10, // return the first 10 pVideoControllers, &uReturned ); #ifdef PRINTF_DEBUGGING if( FAILED( hr ) ) wprintf( L"WMI: pEnumVideoControllers->Next failed: 0x%0.8x\n", hr ); if( uReturned == 0 ) wprintf( L"WMI: pEnumVideoControllers uReturned == 0\n" ); #endif VARIANT var; if( SUCCEEDED( hr ) ) { bool bFound = false; for( UINT iController = 0; iController < uReturned; iController++ ) { if ( !pVideoControllers[iController] ) continue; // if strInputDeviceID is set find this specific device and return memory or specific device // if strInputDeviceID is not set return the best device if (strInputDeviceID) { pPropName = SysAllocString( L"PNPDeviceID" ); hr = pVideoControllers[iController]->Get( pPropName, 0L, &var, nullptr, nullptr ); #ifdef PRINTF_DEBUGGING if( FAILED( hr ) ) wprintf( L"WMI: pVideoControllers[iController]->Get PNPDeviceID failed: 0x%0.8x\n", hr ); #endif if( SUCCEEDED( hr ) && strInputDeviceID) { if( wcsstr( var.bstrVal, strInputDeviceID ) != 0 ) bFound = true; } VariantClear( &var ); if( pPropName ) SysFreeString( pPropName ); } if( bFound || !strInputDeviceID ) { pPropName = SysAllocString( L"AdapterRAM" ); hr = pVideoControllers[iController]->Get( pPropName, 0L, &var, nullptr, nullptr ); #ifdef PRINTF_DEBUGGING if( FAILED( hr ) ) wprintf( L"WMI: pVideoControllers[iController]->Get AdapterRAM failed: 0x%0.8x\n", hr ); #endif if( SUCCEEDED( hr ) ) { bGotMemory = true; *pdwAdapterRam = llmax(var.ulVal, *pdwAdapterRam); } VariantClear( &var ); if( pPropName ) SysFreeString( pPropName ); } SAFE_RELEASE( pVideoControllers[iController] ); if (bFound) { break; } } } } if( pClassName ) SysFreeString( pClassName ); SAFE_RELEASE( pEnumVideoControllers ); } if( pNamespace ) SysFreeString( pNamespace ); SAFE_RELEASE( pIWbemServices ); } SAFE_RELEASE( pIWbemLocator ); CoUninitialize(); if( bGotMemory ) return S_OK; else return E_FAIL; } //static U32 LLDXHardware::getMBVideoMemoryViaWMI() { DWORD vram = 0; if (SUCCEEDED(GetVideoMemoryViaWMI(NULL, &vram))) { return vram / (1024 * 1024);; } return 0; } //Getting the version of graphics controller driver via WMI std::string LLDXHardware::getDriverVersionWMI(EGPUVendor vendor) { std::string mDriverVersion; HRESULT hres; CoInitializeEx(0, COINIT_APARTMENTTHREADED); IWbemLocator *pLoc = NULL; hres = CoCreateInstance( CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *)&pLoc); if (FAILED(hres)) { LL_DEBUGS("AppInit") << "Failed to initialize COM library. Error code = 0x" << hres << LL_ENDL; return std::string(); // Program has failed. } IWbemServices *pSvc = NULL; // Connect to the root\cimv2 namespace with // the current user and obtain pointer pSvc // to make IWbemServices calls. hres = pLoc->ConnectServer( _bstr_t(L"ROOT\\CIMV2"), // Object path of WMI namespace NULL, // User name. NULL = current user NULL, // User password. NULL = current 0, // Locale. NULL indicates current NULL, // Security flags. 0, // Authority (e.g. Kerberos) 0, // Context object &pSvc // pointer to IWbemServices proxy ); if (FAILED(hres)) { LL_WARNS("AppInit") << "Could not connect. Error code = 0x" << hres << LL_ENDL; pLoc->Release(); CoUninitialize(); return std::string(); // Program has failed. } LL_DEBUGS("AppInit") << "Connected to ROOT\\CIMV2 WMI namespace" << LL_ENDL; // Set security levels on the proxy ------------------------- hres = CoSetProxyBlanket( pSvc, // Indicates the proxy to set RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx NULL, // Server principal name RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx NULL, // client identity EOAC_NONE // proxy capabilities ); if (FAILED(hres)) { LL_WARNS("AppInit") << "Could not set proxy blanket. Error code = 0x" << hres << LL_ENDL; pSvc->Release(); pLoc->Release(); CoUninitialize(); return std::string(); // Program has failed. } IEnumWbemClassObject* pEnumerator = NULL; // Get the data from the query ULONG uReturn = 0; hres = pSvc->ExecQuery( bstr_t("WQL"), bstr_t("SELECT * FROM Win32_VideoController"), //Consider using Availability to filter out disabled controllers WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); if (FAILED(hres)) { LL_WARNS("AppInit") << "Query for operating system name failed." << " Error code = 0x" << hres << LL_ENDL; pSvc->Release(); pLoc->Release(); CoUninitialize(); return std::string(); // Program has failed. } while (pEnumerator) { IWbemClassObject *pclsObj = NULL; HRESULT hr = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); if (0 == uReturn) { break; // If quantity less then 1. } if (vendor != GPU_ANY) { VARIANT vtCaptionProp; // Might be preferable to check "AdapterCompatibility" here instead of caption. hr = pclsObj->Get(L"Caption", 0, &vtCaptionProp, 0, 0); if (FAILED(hr)) { LL_WARNS("AppInit") << "Query for Caption property failed." << " Error code = 0x" << hr << LL_ENDL; pSvc->Release(); pLoc->Release(); CoUninitialize(); return std::string(); // Program has failed. } // use characters in the returned driver version BSTR caption(vtCaptionProp.bstrVal); //convert BSTR to std::string std::wstring ws(caption, SysStringLen(caption)); std::string caption_str = ll_convert_wide_to_string(ws); LLStringUtil::toLower(caption_str); bool found = false; switch (vendor) { case GPU_INTEL: found = caption_str.find("intel") != std::string::npos; break; case GPU_NVIDIA: found = caption_str.find("nvidia") != std::string::npos; break; case GPU_AMD: found = caption_str.find("amd") != std::string::npos || caption_str.find("ati ") != std::string::npos || caption_str.find("radeon") != std::string::npos; break; default: break; } if (found) { VariantClear(&vtCaptionProp); } else { VariantClear(&vtCaptionProp); pclsObj->Release(); continue; } } VARIANT vtVersionProp; // Get the value of the DriverVersion property hr = pclsObj->Get(L"DriverVersion", 0, &vtVersionProp, 0, 0); if (FAILED(hr)) { LL_WARNS("AppInit") << "Query for DriverVersion property failed." << " Error code = 0x" << hr << LL_ENDL; pSvc->Release(); pLoc->Release(); CoUninitialize(); return std::string(); // Program has failed. } // use characters in the returned driver version BSTR driverVersion(vtVersionProp.bstrVal); //convert BSTR to std::string std::wstring ws(driverVersion, SysStringLen(driverVersion)); std::string str = ll_convert_wide_to_string(ws); LL_INFOS("AppInit") << " DriverVersion : " << str << LL_ENDL; if (mDriverVersion.empty()) { mDriverVersion = str; } else if (mDriverVersion != str) { if (vendor == GPU_ANY) { // Expected from systems with gpus from different vendors LL_INFOS("DriverVersion") << "Multiple video drivers detected. Version of second driver: " << str << LL_ENDL; } else { // Not Expected! LL_WARNS("DriverVersion") << "Multiple video drivers detected from same vendor. Version of second driver : " << str << LL_ENDL; } } VariantClear(&vtVersionProp); pclsObj->Release(); } // Cleanup // ======== if (pSvc) { pSvc->Release(); } if (pLoc) { pLoc->Release(); } if (pEnumerator) { pEnumerator->Release(); } // supposed to always call CoUninitialize even if init returned false CoUninitialize(); return mDriverVersion; } void get_wstring(IDxDiagContainer* containerp, const WCHAR* wszPropName, WCHAR* wszPropValue, int outputSize) { HRESULT hr; VARIANT var; VariantInit( &var ); hr = containerp->GetProp(wszPropName, &var ); if( SUCCEEDED(hr) ) { // Switch off the type. There's 4 different types: switch( var.vt ) { case VT_UI4: swprintf( wszPropValue, outputSize, L"%d", var.ulVal ); /* Flawfinder: ignore */ break; case VT_I4: swprintf( wszPropValue, outputSize, L"%d", var.lVal ); /* Flawfinder: ignore */ break; case VT_BOOL: wcscpy( wszPropValue, (var.boolVal) ? L"true" : L"false" ); /* Flawfinder: ignore */ break; case VT_BSTR: wcsncpy( wszPropValue, var.bstrVal, outputSize-1 ); /* Flawfinder: ignore */ wszPropValue[outputSize-1] = 0; break; } } // Clear the variant (this is needed to free BSTR memory) VariantClear( &var ); } std::string get_string(IDxDiagContainer *containerp, const WCHAR *wszPropName) { WCHAR wszPropValue[256]; get_wstring(containerp, wszPropName, wszPropValue, 256); return utf16str_to_utf8str(wszPropValue); } LLVersion::LLVersion() { mValid = false; S32 i; for (i = 0; i < 4; i++) { mFields[i] = 0; } } bool LLVersion::set(const std::string &version_string) { S32 i; for (i = 0; i < 4; i++) { mFields[i] = 0; } // Split the version string. std::string str(version_string); typedef boost::tokenizer > tokenizer; boost::char_separator sep(".", "", boost::keep_empty_tokens); tokenizer tokens(str, sep); tokenizer::iterator iter = tokens.begin(); S32 count = 0; for (;(iter != tokens.end()) && (count < 4);++iter) { mFields[count] = atoi(iter->c_str()); count++; } if (count < 4) { //LL_WARNS() << "Potentially bogus version string!" << version_string << LL_ENDL; for (i = 0; i < 4; i++) { mFields[i] = 0; } mValid = false; } else { mValid = true; } return mValid; } S32 LLVersion::getField(const S32 field_num) { if (!mValid) { return -1; } else { return mFields[field_num]; } } std::string LLDXDriverFile::dump() { if (gWriteDebug) { gWriteDebug("Filename:"); gWriteDebug(mName.c_str()); gWriteDebug("\n"); gWriteDebug("Ver:"); gWriteDebug(mVersionString.c_str()); gWriteDebug("\n"); gWriteDebug("Date:"); gWriteDebug(mDateString.c_str()); gWriteDebug("\n"); } LL_INFOS() << mFilepath << LL_ENDL; LL_INFOS() << mName << LL_ENDL; LL_INFOS() << mVersionString << LL_ENDL; LL_INFOS() << mDateString << LL_ENDL; return ""; } LLDXDevice::~LLDXDevice() { for_each(mDriverFiles.begin(), mDriverFiles.end(), DeletePairedPointer()); mDriverFiles.clear(); } std::string LLDXDevice::dump() { if (gWriteDebug) { gWriteDebug("StartDevice\n"); gWriteDebug("DeviceName:"); gWriteDebug(mName.c_str()); gWriteDebug("\n"); gWriteDebug("PCIString:"); gWriteDebug(mPCIString.c_str()); gWriteDebug("\n"); } LL_INFOS() << LL_ENDL; LL_INFOS() << "DeviceName:" << mName << LL_ENDL; LL_INFOS() << "PCIString:" << mPCIString << LL_ENDL; LL_INFOS() << "Drivers" << LL_ENDL; LL_INFOS() << "-------" << LL_ENDL; for (driver_file_map_t::iterator iter = mDriverFiles.begin(), end = mDriverFiles.end(); iter != end; iter++) { LLDXDriverFile *filep = iter->second; filep->dump(); } if (gWriteDebug) { gWriteDebug("EndDevice\n"); } return ""; } LLDXDriverFile *LLDXDevice::findDriver(const std::string &driver) { for (driver_file_map_t::iterator iter = mDriverFiles.begin(), end = mDriverFiles.end(); iter != end; iter++) { LLDXDriverFile *filep = iter->second; if (!utf8str_compare_insensitive(filep->mName,driver)) { return filep; } } return NULL; } LLDXHardware::LLDXHardware() { mVRAM = 0; gWriteDebug = NULL; } void LLDXHardware::cleanup() { // for_each(mDevices.begin(), mDevices.end(), DeletePairedPointer()); // mDevices.clear(); } /* std::string LLDXHardware::dumpDevices() { if (gWriteDebug) { gWriteDebug("\n"); gWriteDebug("StartAllDevices\n"); } for (device_map_t::iterator iter = mDevices.begin(), end = mDevices.end(); iter != end; iter++) { LLDXDevice *devicep = iter->second; devicep->dump(); } if (gWriteDebug) { gWriteDebug("EndAllDevices\n\n"); } return ""; } LLDXDevice *LLDXHardware::findDevice(const std::string &vendor, const std::string &devices) { // Iterate through different devices tokenized in devices string std::string str(devices); typedef boost::tokenizer > tokenizer; boost::char_separator sep("|", "", boost::keep_empty_tokens); tokenizer tokens(str, sep); tokenizer::iterator iter = tokens.begin(); for (;iter != tokens.end();++iter) { std::string dev_str = *iter; for (device_map_t::iterator iter = mDevices.begin(), end = mDevices.end(); iter != end; iter++) { LLDXDevice *devicep = iter->second; if ((devicep->mVendorID == vendor) && (devicep->mDeviceID == dev_str)) { return devicep; } } } return NULL; } */ bool LLDXHardware::getInfo(bool vram_only) { LLTimer hw_timer; bool ok = false; HRESULT hr; // CLSID_DxDiagProvider does not work with Multithreaded? CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); IDxDiagProvider *dx_diag_providerp = NULL; IDxDiagContainer *dx_diag_rootp = NULL; IDxDiagContainer *devices_containerp = NULL; // IDxDiagContainer *system_device_containerp= NULL; IDxDiagContainer *device_containerp = NULL; IDxDiagContainer *file_containerp = NULL; IDxDiagContainer *driver_containerp = NULL; DWORD dw_device_count; mVRAM = 0; // CoCreate a IDxDiagProvider* LL_DEBUGS("AppInit") << "CoCreateInstance IID_IDxDiagProvider" << LL_ENDL; hr = CoCreateInstance(CLSID_DxDiagProvider, NULL, CLSCTX_INPROC_SERVER, IID_IDxDiagProvider, (LPVOID*) &dx_diag_providerp); if (FAILED(hr)) { LL_WARNS("AppInit") << "No DXDiag provider found! DirectX 9 not installed!" << LL_ENDL; gWriteDebug("No DXDiag provider found! DirectX 9 not installed!\n"); goto LCleanup; } if (SUCCEEDED(hr)) // if FAILED(hr) then dx9 is not installed { // Fill out a DXDIAG_INIT_PARAMS struct and pass it to IDxDiagContainer::Initialize // Passing in TRUE for bAllowWHQLChecks, allows dxdiag to check if drivers are // digital signed as logo'd by WHQL which may connect via internet to update // WHQL certificates. DXDIAG_INIT_PARAMS dx_diag_init_params; ZeroMemory(&dx_diag_init_params, sizeof(DXDIAG_INIT_PARAMS)); dx_diag_init_params.dwSize = sizeof(DXDIAG_INIT_PARAMS); dx_diag_init_params.dwDxDiagHeaderVersion = DXDIAG_DX9_SDK_VERSION; dx_diag_init_params.bAllowWHQLChecks = TRUE; dx_diag_init_params.pReserved = NULL; LL_DEBUGS("AppInit") << "dx_diag_providerp->Initialize" << LL_ENDL; hr = dx_diag_providerp->Initialize(&dx_diag_init_params); if(FAILED(hr)) { goto LCleanup; } LL_DEBUGS("AppInit") << "dx_diag_providerp->GetRootContainer" << LL_ENDL; hr = dx_diag_providerp->GetRootContainer( &dx_diag_rootp ); if(FAILED(hr) || !dx_diag_rootp) { goto LCleanup; } HRESULT hr; // Get display driver information LL_DEBUGS("AppInit") << "dx_diag_rootp->GetChildContainer" << LL_ENDL; hr = dx_diag_rootp->GetChildContainer(L"DxDiag_DisplayDevices", &devices_containerp); if(FAILED(hr) || !devices_containerp) { // do not release 'dirty' devices_containerp at this stage, only dx_diag_rootp devices_containerp = NULL; goto LCleanup; } // make sure there is something inside hr = devices_containerp->GetNumberOfChildContainers(&dw_device_count); if (FAILED(hr) || dw_device_count == 0) { goto LCleanup; } // Get device 0 // By default 0 device is the primary one, howhever in case of various hybrid graphics // like itegrated AMD and PCI AMD GPUs system might switch. LL_DEBUGS("AppInit") << "devices_containerp->GetChildContainer" << LL_ENDL; hr = devices_containerp->GetChildContainer(L"0", &device_containerp); if(FAILED(hr) || !device_containerp) { goto LCleanup; } DWORD vram = 0; WCHAR deviceID[512]; get_wstring(device_containerp, L"szDeviceID", deviceID, 512); // Example: searches id like 1F06 in pnp string (aka VEN_10DE&DEV_1F06) // doesn't seem to work on some systems since format is unrecognizable // but in such case keyDeviceID works if (SUCCEEDED(GetVideoMemoryViaWMI(deviceID, &vram))) { mVRAM = vram/(1024*1024); } else { get_wstring(device_containerp, L"szKeyDeviceID", deviceID, 512); LL_WARNS() << "szDeviceID" << deviceID << LL_ENDL; // '+9' to avoid ENUM\\PCI\\ prefix // Returns string like Enum\\PCI\\VEN_10DE&DEV_1F06&SUBSYS... // and since GetVideoMemoryViaWMI searches by PNPDeviceID it is sufficient if (SUCCEEDED(GetVideoMemoryViaWMI(deviceID + 9, &vram))) { mVRAM = vram / (1024 * 1024); } } if (mVRAM == 0) { // Get the English VRAM string std::string ram_str = get_string(device_containerp, L"szDisplayMemoryEnglish"); // We don't need the device any more SAFE_RELEASE(device_containerp); // Dump the string as an int into the structure char *stopstring; mVRAM = strtol(ram_str.c_str(), &stopstring, 10); LL_INFOS("AppInit") << "VRAM Detected: " << mVRAM << " DX9 string: " << ram_str << LL_ENDL; } if (vram_only) { ok = true; goto LCleanup; } /* for now, we ONLY do vram_only the rest of this is commented out, to ensure no-one is tempted to use it // Now let's get device and driver information // Get the IDxDiagContainer object called "DxDiag_SystemDevices". // This call may take some time while dxdiag gathers the info. DWORD num_devices = 0; WCHAR wszContainer[256]; LL_DEBUGS("AppInit") << "dx_diag_rootp->GetChildContainer DxDiag_SystemDevices" << LL_ENDL; hr = dx_diag_rootp->GetChildContainer(L"DxDiag_SystemDevices", &system_device_containerp); if (FAILED(hr)) { goto LCleanup; } hr = system_device_containerp->GetNumberOfChildContainers(&num_devices); if (FAILED(hr)) { goto LCleanup; } LL_DEBUGS("AppInit") << "DX9 iterating over devices" << LL_ENDL; S32 device_num = 0; for (device_num = 0; device_num < (S32)num_devices; device_num++) { hr = system_device_containerp->EnumChildContainerNames(device_num, wszContainer, 256); if (FAILED(hr)) { goto LCleanup; } hr = system_device_containerp->GetChildContainer(wszContainer, &device_containerp); if (FAILED(hr) || device_containerp == NULL) { goto LCleanup; } std::string device_name = get_string(device_containerp, L"szDescription"); std::string device_id = get_string(device_containerp, L"szDeviceID"); LLDXDevice *dxdevicep = new LLDXDevice; dxdevicep->mName = device_name; dxdevicep->mPCIString = device_id; mDevices[dxdevicep->mPCIString] = dxdevicep; // Split the PCI string based on vendor, device, subsys, rev. std::string str(device_id); typedef boost::tokenizer > tokenizer; boost::char_separator sep("&\\", "", boost::keep_empty_tokens); tokenizer tokens(str, sep); tokenizer::iterator iter = tokens.begin(); S32 count = 0; bool valid = true; for (;(iter != tokens.end()) && (count < 3);++iter) { switch (count) { case 0: if (strcmp(iter->c_str(), "PCI")) { valid = false; } break; case 1: dxdevicep->mVendorID = iter->c_str(); break; case 2: dxdevicep->mDeviceID = iter->c_str(); break; default: // Ignore it break; } count++; } // Now, iterate through the related drivers hr = device_containerp->GetChildContainer(L"Drivers", &driver_containerp); if (FAILED(hr) || !driver_containerp) { goto LCleanup; } DWORD num_files = 0; hr = driver_containerp->GetNumberOfChildContainers(&num_files); if (FAILED(hr)) { goto LCleanup; } S32 file_num = 0; for (file_num = 0; file_num < (S32)num_files; file_num++ ) { hr = driver_containerp->EnumChildContainerNames(file_num, wszContainer, 256); if (FAILED(hr)) { goto LCleanup; } hr = driver_containerp->GetChildContainer(wszContainer, &file_containerp); if (FAILED(hr) || file_containerp == NULL) { goto LCleanup; } std::string driver_path = get_string(file_containerp, L"szPath"); std::string driver_name = get_string(file_containerp, L"szName"); std::string driver_version = get_string(file_containerp, L"szVersion"); std::string driver_date = get_string(file_containerp, L"szDatestampEnglish"); LLDXDriverFile *dxdriverfilep = new LLDXDriverFile; dxdriverfilep->mName = driver_name; dxdriverfilep->mFilepath= driver_path; dxdriverfilep->mVersionString = driver_version; dxdriverfilep->mVersion.set(driver_version); dxdriverfilep->mDateString = driver_date; dxdevicep->mDriverFiles[driver_name] = dxdriverfilep; SAFE_RELEASE(file_containerp); } SAFE_RELEASE(device_containerp); } */ } // dumpDevices(); ok = true; LCleanup: if (!ok) { LL_WARNS("AppInit") << "DX9 probe failed" << LL_ENDL; gWriteDebug("DX9 probe failed\n"); } SAFE_RELEASE(file_containerp); SAFE_RELEASE(driver_containerp); SAFE_RELEASE(device_containerp); SAFE_RELEASE(devices_containerp); SAFE_RELEASE(dx_diag_rootp); SAFE_RELEASE(dx_diag_providerp); CoUninitialize(); return ok; } LLSD LLDXHardware::getDisplayInfo() { LLTimer hw_timer; HRESULT hr; LLSD ret; CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); IDxDiagProvider *dx_diag_providerp = NULL; IDxDiagContainer *dx_diag_rootp = NULL; IDxDiagContainer *devices_containerp = NULL; IDxDiagContainer *device_containerp = NULL; IDxDiagContainer *file_containerp = NULL; IDxDiagContainer *driver_containerp = NULL; DWORD dw_device_count; // CoCreate a IDxDiagProvider* LL_INFOS() << "CoCreateInstance IID_IDxDiagProvider" << LL_ENDL; hr = CoCreateInstance(CLSID_DxDiagProvider, NULL, CLSCTX_INPROC_SERVER, IID_IDxDiagProvider, (LPVOID*) &dx_diag_providerp); if (FAILED(hr)) { LL_WARNS() << "No DXDiag provider found! DirectX 9 not installed!" << LL_ENDL; gWriteDebug("No DXDiag provider found! DirectX 9 not installed!\n"); goto LCleanup; } if (SUCCEEDED(hr)) // if FAILED(hr) then dx9 is not installed { // Fill out a DXDIAG_INIT_PARAMS struct and pass it to IDxDiagContainer::Initialize // Passing in TRUE for bAllowWHQLChecks, allows dxdiag to check if drivers are // digital signed as logo'd by WHQL which may connect via internet to update // WHQL certificates. DXDIAG_INIT_PARAMS dx_diag_init_params; ZeroMemory(&dx_diag_init_params, sizeof(DXDIAG_INIT_PARAMS)); dx_diag_init_params.dwSize = sizeof(DXDIAG_INIT_PARAMS); dx_diag_init_params.dwDxDiagHeaderVersion = DXDIAG_DX9_SDK_VERSION; dx_diag_init_params.bAllowWHQLChecks = TRUE; dx_diag_init_params.pReserved = NULL; LL_INFOS() << "dx_diag_providerp->Initialize" << LL_ENDL; hr = dx_diag_providerp->Initialize(&dx_diag_init_params); if(FAILED(hr)) { goto LCleanup; } LL_INFOS() << "dx_diag_providerp->GetRootContainer" << LL_ENDL; hr = dx_diag_providerp->GetRootContainer( &dx_diag_rootp ); if(FAILED(hr) || !dx_diag_rootp) { goto LCleanup; } HRESULT hr; // Get display driver information LL_INFOS() << "dx_diag_rootp->GetChildContainer" << LL_ENDL; hr = dx_diag_rootp->GetChildContainer(L"DxDiag_DisplayDevices", &devices_containerp); if(FAILED(hr) || !devices_containerp) { // do not release 'dirty' devices_containerp at this stage, only dx_diag_rootp devices_containerp = NULL; goto LCleanup; } // make sure there is something inside hr = devices_containerp->GetNumberOfChildContainers(&dw_device_count); if (FAILED(hr) || dw_device_count == 0) { goto LCleanup; } // Get device 0 LL_INFOS() << "devices_containerp->GetChildContainer" << LL_ENDL; hr = devices_containerp->GetChildContainer(L"0", &device_containerp); if(FAILED(hr) || !device_containerp) { goto LCleanup; } // Get the English VRAM string std::string ram_str = get_string(device_containerp, L"szDisplayMemoryEnglish"); // Dump the string as an int into the structure char *stopstring; ret["VRAM"] = LLSD::Integer(strtol(ram_str.c_str(), &stopstring, 10)); std::string device_name = get_string(device_containerp, L"szDescription"); ret["DeviceName"] = device_name; std::string device_driver= get_string(device_containerp, L"szDriverVersion"); ret["DriverVersion"] = device_driver; // ATI has a slightly different version string if(device_name.length() >= 4 && device_name.substr(0,4) == "ATI ") { // get the key HKEY hKey; const DWORD RV_SIZE = 100; WCHAR release_version[RV_SIZE]; // Hard coded registry entry. Using this since it's simpler for now. // And using EnumDisplayDevices to get a registry key also requires // a hard coded Query value. if(ERROR_SUCCESS == RegOpenKey(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\ATI Technologies\\CBT"), &hKey)) { // get the value DWORD dwType = REG_SZ; DWORD dwSize = sizeof(WCHAR) * RV_SIZE; if(ERROR_SUCCESS == RegQueryValueEx(hKey, TEXT("ReleaseVersion"), NULL, &dwType, (LPBYTE)release_version, &dwSize)) { // print the value // windows doesn't guarantee to be null terminated release_version[RV_SIZE - 1] = NULL; ret["DriverVersion"] = utf16str_to_utf8str(release_version); } RegCloseKey(hKey); } } } LCleanup: if (!ret.isMap() || (ret.size() == 0)) { LL_INFOS() << "Failed to get data, cleaning up" << LL_ENDL; } SAFE_RELEASE(file_containerp); SAFE_RELEASE(driver_containerp); SAFE_RELEASE(device_containerp); SAFE_RELEASE(devices_containerp); SAFE_RELEASE(dx_diag_rootp); SAFE_RELEASE(dx_diag_providerp); CoUninitialize(); return ret; } void LLDXHardware::setWriteDebugFunc(void (*func)(const char*)) { gWriteDebug = func; } #endif