win_shared.cpp.patch (11,167 bytes)
Index: sys/win32/win_shared.cpp
===================================================================
--- sys/win32/win_shared.cpp (revision 6374)
+++ sys/win32/win_shared.cpp (working copy)
@@ -40,6 +40,9 @@
#pragma comment (lib, "wbemuuid.lib")
#endif
+#include <dxgi.h>
+
+
/*
================
Sys_Milliseconds
@@ -106,52 +109,338 @@
#ifdef ID_DEDICATED
return 0;
#else
- unsigned int retSize = 64;
+ // Based on information from
+ // https://code.msdn.microsoft.com/windowsdesktop/DirectX-Video-Memory-ee7d8319
- CComPtr<IWbemLocator> spLoc = NULL;
- HRESULT hr = CoCreateInstance( CLSID_WbemLocator, 0, CLSCTX_SERVER, IID_IWbemLocator, ( LPVOID * ) &spLoc );
- if ( hr != S_OK || spLoc == NULL ) {
- return retSize;
- }
+ ULONGLONG vram_size = 0;
+ BOOL api_result = FALSE;
- CComBSTR bstrNamespace( _T( "\\\\.\\root\\CIMV2" ) );
- CComPtr<IWbemServices> spServices;
+ OSVERSIONINFOEXW version_info;
+ version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
+ version_info.dwMajorVersion = 6;
- // Connect to CIM
- hr = spLoc->ConnectServer( bstrNamespace, NULL, NULL, 0, NULL, 0, 0, &spServices );
- if ( hr != WBEM_S_NO_ERROR ) {
- return retSize;
- }
+ ULONGLONG ver_condition_mask = 0;
- // Switch the security level to IMPERSONATE so that provider will grant access to system-level objects.
- hr = CoSetProxyBlanket( spServices, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE );
- if ( hr != S_OK ) {
- return retSize;
- }
+ ver_condition_mask = ::VerSetConditionMask(
+ ver_condition_mask,
+ VER_MAJORVERSION,
+ VER_GREATER_EQUAL);
- // Get the vid controller
- CComPtr<IEnumWbemClassObject> spEnumInst = NULL;
- hr = spServices->CreateInstanceEnum( CComBSTR( "Win32_VideoController" ), WBEM_FLAG_SHALLOW, NULL, &spEnumInst );
- if ( hr != WBEM_S_NO_ERROR || spEnumInst == NULL ) {
- return retSize;
- }
+ api_result = ::VerifyVersionInfoW(
+ &version_info,
+ VER_MAJORVERSION,
+ ver_condition_mask);
- ULONG uNumOfInstances = 0;
- CComPtr<IWbemClassObject> spInstance = NULL;
- hr = spEnumInst->Next( 10000, 1, &spInstance, &uNumOfInstances );
+ auto is_vista_or_higher = (api_result != FALSE);
- if ( hr == S_OK && spInstance ) {
- // Get properties from the object
- CComVariant varSize;
- hr = spInstance->Get( CComBSTR( _T( "AdapterRAM" ) ), 0, &varSize, 0, 0 );
- if ( hr == S_OK ) {
- retSize = varSize.intVal / ( 1024 * 1024 );
- if ( retSize == 0 ) {
- retSize = 64;
- }
- }
- }
- return retSize;
+
+ //
+ // Retrieve via DXGI 1.0/1.1 (Vista or higher)
+ //
+
+ ULONGLONG dxgi_vram_size = 0;
+
+ if (is_vista_or_higher) {
+ using LPCREATEDXGIFACTORY = HRESULT(WINAPI*)(
+ REFIID riid,
+ void** pp_factory);
+
+ HRESULT h_result = S_OK;
+ auto is_dxgi_1_1 = false;
+ LPCREATEDXGIFACTORY dxgi_create_factory = nullptr;
+ HMODULE dxgi_module = nullptr;
+
+ dxgi_module = ::LoadLibraryW(L"dxgi.dll");
+
+ if (dxgi_module) {
+ dxgi_create_factory = reinterpret_cast<LPCREATEDXGIFACTORY>(
+ ::GetProcAddress(dxgi_module, "CreateDXGIFactory1"));
+
+ if (dxgi_create_factory) {
+ is_dxgi_1_1 = true;
+ } else {
+ dxgi_create_factory = reinterpret_cast<LPCREATEDXGIFACTORY>(
+ ::GetProcAddress(dxgi_module, "CreateDXGIFactory"));
+ }
+ }
+
+ if (dxgi_create_factory) {
+ IDXGIFactory* dxgi_factory = nullptr;
+
+ if (SUCCEEDED(h_result)) {
+ h_result = dxgi_create_factory(
+ is_dxgi_1_1 ? ::IID_IDXGIFactory1 : ::IID_IDXGIFactory,
+ reinterpret_cast<void**>(&dxgi_factory));
+ }
+
+
+ IDXGIAdapter* dxgi_adapter = nullptr;
+
+ if (SUCCEEDED(h_result)) {
+ h_result = dxgi_factory->EnumAdapters(0, &dxgi_adapter);
+ }
+
+ if (SUCCEEDED(h_result)) {
+ DXGI_ADAPTER_DESC desc;
+
+ h_result = dxgi_adapter->GetDesc(&desc);
+
+ if (SUCCEEDED(h_result)) {
+ dxgi_vram_size = desc.DedicatedVideoMemory;
+ }
+ }
+
+ if (dxgi_adapter) {
+ static_cast<void>(dxgi_adapter->Release());
+ }
+
+ if (dxgi_factory) {
+ static_cast<void>(dxgi_factory->Release());
+ }
+ }
+
+ static_cast<void>(::FreeLibrary(dxgi_module));
+ }
+
+
+ //
+ // Retrieve via WMI
+ //
+
+ ULONGLONG vmi_vram_size = 0;
+
+ if (dxgi_vram_size == 0) {
+ HRESULT h_result = S_OK;
+
+ HMONITOR primary_monitor = nullptr;
+
+ if (SUCCEEDED(h_result)) {
+ primary_monitor = ::MonitorFromWindow(
+ ::GetDesktopWindow(),
+ MONITOR_DEFAULTTOPRIMARY);
+
+ if (!primary_monitor) {
+ h_result = E_FAIL;
+ }
+ }
+
+
+ std::wstring device_id;
+
+ if (SUCCEEDED(h_result)) {
+ //
+ // Get video adapter for primary desktop
+ //
+
+ auto is_device_found = false;
+ const DWORD max_adapters = 0xFFFFFFFF;
+
+ for (DWORD i = 0; i < max_adapters; ++i) {
+ DISPLAY_DEVICEW device_info;
+ device_info.cb = sizeof(DISPLAY_DEVICEW);
+
+ auto api_result = ::EnumDisplayDevicesW(
+ nullptr,
+ i,
+ &device_info,
+ 0);
+
+ if (api_result == FALSE)
+ break;
+
+ if ((device_info.StateFlags &
+ DISPLAY_DEVICE_PRIMARY_DEVICE) == 0)
+ {
+ continue;
+ }
+
+ is_device_found = true;
+ device_id = device_info.DeviceID;
+ break;
+ }
+
+ if (!is_device_found && SUCCEEDED(h_result)) {
+ h_result = E_FAIL;
+ }
+ }
+
+
+ auto is_com_initialized = false;
+
+ if (SUCCEEDED(h_result)) {
+ h_result = ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
+
+ if (SUCCEEDED(h_result))
+ is_com_initialized = true;
+ }
+
+
+ IWbemLocator* wbem_locator = nullptr;
+
+ if (SUCCEEDED(h_result)) {
+ h_result = ::CoCreateInstance(
+ CLSID_WbemLocator,
+ nullptr,
+ CLSCTX_INPROC_SERVER,
+ IID_IWbemLocator,
+ reinterpret_cast<LPVOID*>(&wbem_locator));
+ }
+
+
+ IWbemServices* wbem_services = nullptr;
+
+ if (SUCCEEDED(h_result)) {
+ h_result = wbem_locator->ConnectServer(
+ BSTR(L"\\\\.\\root\\cimv2"),
+ nullptr,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ nullptr,
+ &wbem_services);
+ }
+
+
+ IClientSecurity* client_security = nullptr;
+
+ if (SUCCEEDED(h_result)) {
+ h_result = wbem_services->QueryInterface(
+ IID_PPV_ARGS(&client_security));
+ }
+
+ if (SUCCEEDED(h_result)) {
+ h_result = client_security->SetBlanket(
+ wbem_services,
+ RPC_C_AUTHN_WINNT,
+ RPC_C_AUTHZ_NONE,
+ nullptr,
+ RPC_C_AUTHN_LEVEL_CALL,
+ RPC_C_IMP_LEVEL_IMPERSONATE,
+ 0,
+ EOAC_NONE);
+
+ static_cast<void>(client_security->Release());
+ }
+
+
+ IEnumWbemClassObject* controllers_enumerator = nullptr;
+
+ if (SUCCEEDED(h_result)) {
+ h_result = wbem_services->CreateInstanceEnum(
+ L"Win32_VideoController",
+ 0,
+ nullptr,
+ &controllers_enumerator);
+ }
+
+ if (SUCCEEDED(h_result)) {
+ h_result = controllers_enumerator->Reset();
+ }
+
+ if (SUCCEEDED(h_result)) {
+ HRESULT var_result = S_OK;
+ ULONG controller_count = 0;
+ IWbemClassObject* controller_object = nullptr;
+ auto is_controller_found = false;
+ const LONG timeout_ms = 5 * 1000;
+
+ VARIANT pnp_device_id_var;
+ VARIANT adapter_ram_var;
+
+ ::VariantInit(&pnp_device_id_var);
+ ::VariantInit(&adapter_ram_var);
+
+ ULONG adapter_ram_size = 0;
+
+ while (true) {
+ h_result = controllers_enumerator->Next(
+ timeout_ms,
+ 1,
+ &controller_object,
+ &controller_count);
+
+ if (FAILED(h_result) || controller_count == 0) {
+ break;
+ }
+
+ h_result = controller_object->Get(
+ L"PNPDeviceID",
+ 0,
+ &pnp_device_id_var,
+ nullptr,
+ nullptr);
+
+ if (SUCCEEDED(h_result)) {
+ h_result = controller_object->Get(
+ L"AdapterRAM",
+ 0,
+ &adapter_ram_var,
+ nullptr,
+ nullptr);
+ }
+
+ static_cast<void>(controller_object->Release());
+
+
+ std::wstring wmi_device_id;
+
+ if (SUCCEEDED(h_result)) {
+ wmi_device_id = pnp_device_id_var.bstrVal;
+ adapter_ram_size = adapter_ram_var.ulVal;
+ }
+
+ var_result = ::VariantClear(&pnp_device_id_var);
+ assert(SUCCEEDED(var_result));
+
+ var_result = ::VariantClear(&adapter_ram_var);
+ assert(SUCCEEDED(var_result));
+
+ if (FAILED(h_result)) {
+ continue;
+ }
+
+ if (wmi_device_id.find(device_id) != 0) {
+ continue;
+ }
+
+ vmi_vram_size = adapter_ram_size;
+
+ break;
+ }
+ }
+
+ if (controllers_enumerator) {
+ static_cast<void>(controllers_enumerator->Release());
+ }
+
+ if (wbem_services) {
+ static_cast<void>(wbem_services->Release());
+ }
+
+ if (wbem_locator) {
+ static_cast<void>(wbem_locator->Release());
+ }
+
+ if (is_com_initialized) {
+ ::CoUninitialize();
+ }
+ }
+
+
+ if (dxgi_vram_size > 0) {
+ vram_size = dxgi_vram_size;
+ } else if (vmi_vram_size > 0) {
+ vram_size = vmi_vram_size;
+ }
+
+ vram_size /= 1024 * 1024;
+
+ if (vram_size < 64) {
+ vram_size = 64;
+ }
+
+ return static_cast<int>(vram_size);
#endif
}