비디오 메모리 구하기 (DirectDraw, WMI)

2 minute read

PerfHUD나 DirectX Caps Viewer로 볼 수 있지만, 비디오 메모리 사용량을 렌더링하는 게 그리 힘든 일이 아니라 보통 이 정도는 렌더링해준다. 그건 좋은데 문제는 DirectX9 인터페이스로는 사용 가능한 전용 비디오 메모리를 알 수 없는 거다. 그래서 보통 DirectDraw 인터페이스를 구해서 GetAvailableVidMem()를 호출해서 비디오 메모리를 구한다.

이것만 알고 있었는데, DirectX 샘플을 보던 중 비디오 메모리를 5가지 방법으로 구하는 VideoMemory Sample을 발견해서 살펴봤다. 샘플 자체가 비디오 메모리 구하기 완전 정복 필이다.

nil

이 5가지 중에서 DirectDraw, WMI, DXGI를 사용하면 비디오 로컬 메모리를 구할 수 있다. DXGI는 비스타 이상에서만 사용할 수 있어서 XP 타겟으로 개발하는 게 대부분이라서 탈락이고 DirectDraw와 WMI 둘 중 하나를 고려해야 하는데, 문서에는 XP에서는 WMI, Vista에서는 DXGI 사용을 추천하고 있다.

많이 사용하는 DirectDraw를 이용한 코드는 다음과 같다.

// 라이브러리를 로드하고 DirectDrawCreate 함수 주소를 구한다.
HINSTANCE hInstDDraw = LoadLibrary( L"ddraw.dll" );
LPDIRECTDRAWCREATE pDDCreate =
    ( LPDIRECTDRAWCREATE )GetProcAddress( hInstDDraw, "DirectDrawCreate" );

// DirectDrawCreate를 호출해 DirectDraw 인스턴스를 생성한다.
LPDIRECTDRAW pDDraw = NULL;
pDDCreate( ... &pDDraw ... );

// DirectDraw7 인터페이스
LPDIRECTDRAW7 pDDraw7;
pDDraw->QueryInterface( IID_IDirectDraw7, ( VOID** )&pDDraw7 ) );

// 로컬 비디오 메모리를 구한다.
DDSCAPS2 ddscaps;
ZeroMemory( &ddscaps, sizeof( DDSCAPS2 ) );
ddscaps.dwCaps = DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM;

DWORD totalMem, freeMem;
pDDraw7->GetAvailableVidMem( &ddscaps, &totalMem, &freeMem );

WMI(Windows Management Instrumentation)는 로컬과 원격 컴퓨터의 시스템, 애플리케이션, 네크워크, 디바이스 그리고 여러 컴포넌트의 관리 기능을 제공하는 infrastructure 이다. 어마어마한 정보를 제공하기 때문에 비디오 로컬 메모리는 그야말로 빙산의 일각. 제공하는 COM API를 사용해서 접근할 수 있다.

// WMI 연결에 사용하는 locator 인스턴스 생성.
CoInitialize( 0 );
IWbemLocator* pIWbemLocator = NULL;
CoCreateInstance(
    CLSID_WbemLocator, NULL, CLSCTX_INPROC_SERVER,
    IID_IWbemLocator, ( LPVOID* )&pIWbemLocator );

// 로컬 WMI에 연결
BSTR pNamespace = SysAllocString( L"\\\\.\\root\\cimv2" );
IWbemServices* pIWbemServices = NULL;
pIWbemLocator->ConnectServer( pNamespace, NULL, NULL, 0L,
                              0L, NULL, NULL, &pIWbemServices );

// 비디오 컨트롤러 인스턴스의 enumerator를 생성.
IEnumWbemClassObject* pEnumVideoControllers = NULL;
BSTR pClassName = SysAllocString( L"Win32_VideoController" );
pIWbemServices->CreateInstanceEnum( pClassName, 0, NULL, &pEnumVideoControllers );

// enumerator를 사용해서 비디오 컨트롤러를 순회한다.
IWbemClassObject* pVideoControllers[10] = {0};
DWORD uReturned = 0;

pEnumVideoControllers->Reset();
pEnumVideoControllers->Next( 5000, 10, pVideoControllers, &uReturned );
for( UINT iController = 0; iController < uReturned; iController++ )
{
    VARIANT var;

    // 비디오 컨트롤러 중에 찾는 컨트롤러인지 확인한다.
    BSTR deviceId = SysAllocString( L"PNPDeviceID" );
    pVideoControllers[iController]->Get( deviceId, 0L, &var, NULL, NULL );
    if( wcsstr( var.bstrVal, strInputDeviceID ) == 0 ) continue;

    // 비디오 로컬 메모리를 구한다.
    BSTR adapterRame = SysAllocString( L"AdapterRAM" );
    pVideoControllers[iController]->Get( adapterRam, 0L, &var, NULL, NULL );
}

WMI를 사용하면 거의 모든 로컬 하드웨어 정보를 얻을 수 있지만, 비디오 메모리의 잔량(free)을 알지 못한다.(있는데, 못 찾은걸까?) 비디오 메모리 잔량을 알려면 DirectDraw를 사용해야 한다. WMI를 처음 살펴봤는데, 이 뭐~ 유저가 어떤 팬티를 입었는지 빼고는 거의 모든 정보를 얻을 수 있는거 같다. 유저 하드웨어 사양을 리포트할때 유용하게 사용할 수 있을거 같으나 비디오 메모리를 알려고 사용하는 건 에러인거 같다. 원하는 정보는 비디오 메모리 잔량이기 때문에…