-
andreykproductengine authoredandreykproductengine authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
lldxhardware.cpp 30.90 KiB
/**
* @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 <dxdiag.h>
#undef INITGUID
#include <wbemidl.h>
#include <comdef.h>
#include <boost/tokenizer.hpp>
#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;
HRESULT hrCoInitialize = S_OK;
IWbemLocator* pIWbemLocator = nullptr;
IWbemServices* pIWbemServices = nullptr;
BSTR pNamespace = nullptr;
*pdwAdapterRam = 0;
hrCoInitialize = CoInitialize( 0 );
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 );
if( SUCCEEDED( hrCoInitialize ) )
CoUninitialize();
if( bGotMemory )
return S_OK;
else
return E_FAIL;
}
//static
S32 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()
{
std::string mDriverVersion;
HRESULT hrCoInitialize = S_OK;
HRESULT hres;
hrCoInitialize = CoInitialize(0);
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.
}
VARIANT vtProp;
// Get the value of the Name property
hr = pclsObj->Get(L"DriverVersion", 0, &vtProp, 0, 0);
if (FAILED(hr))
{
LL_WARNS("AppInit") << "Query for name 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(vtProp.bstrVal);
//convert BSTR to std::string
std::wstring ws(driverVersion, SysStringLen(driverVersion));
std::string str(ws.begin(), ws.end());
LL_INFOS("AppInit") << " DriverVersion : " << str << LL_ENDL;
if (mDriverVersion.empty())
{
mDriverVersion = str;
}
else if (mDriverVersion != str)
{
LL_WARNS("DriverVersion") << "Different versions of drivers. Version of second driver : " << str << LL_ENDL;
}
VariantClear(&vtProp);
pclsObj->Release();
}
// Cleanup
// ========
if (pSvc)
{
pSvc->Release();
}
if (pLoc)
{
pLoc->Release();
}
if (pEnumerator)
{
pEnumerator->Release();
}
if (SUCCEEDED(hrCoInitialize))
{
CoUninitialize();
}
return mDriverVersion;
}
void get_wstring(IDxDiagContainer* containerp, 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, L"%d", var.ulVal ); /* Flawfinder: ignore */
break;
case VT_I4:
swprintf( wszPropValue, 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, 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<boost::char_separator<char> > tokenizer;
boost::char_separator<char> 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<boost::char_separator<char> > tokenizer;
boost::char_separator<char> 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;
CoInitialize(NULL);
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<boost::char_separator<char> > tokenizer;
boost::char_separator<char> 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;
CoInitialize(NULL);
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"] = 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.emptyMap())
{
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