Thursday 4 September 2014

How To Get GPU Video Memory 'In Use' In DirectX 9

It took two half days of research and experimentation, and finally a link from a Reloaded community member to come up with the solution. You can use the code (below) in any DirectX 9 application to get the 'currently used' bytes of your graphics card, ideal for monitoring your resources, debugging and even taking pre-preemptive action when GPU video memory starts to get sparse. When I added this feature to the log report, and ran a simple 'gun and zombie' test, I saw this section of entries:

12748764 : gun 18:modern\colt1911\gunspec.txt                   S:0MB   V:0MB (123)     
12748771 : gun 19:modern\Magnum357\gunspec.txt                  S:0MB   V:0MB (123)     
12748778 : gun 20:modern\RPG\gunspec.txt                        S:0MB   V:0MB (123)     
12748785 : gun 21:modern\Shotgun\gunspec.txt                    S:0MB   V:0MB (123)     
12748792 : gun 22:modern\SniperM700\gunspec.txt                 S:0MB   V:0MB (123)     
12748799 : gun 23:modern\Uzi\gunspec.txt                        S:0MB   V:0MB (123)     
12748806 : total guns=23                                        S:0MB   V:0MB (123)     
12751435 : Load player config                                   S:1MB   V:109MB (232)   
12751453 : LOADING ENTITIES DATA                                S:0MB   V:64MB (296)    
12751481 : Loaded 1:_markers\player start.fpe                   S:0MB   V:-4MB (292)    
12752030 : Loaded 2:\Characters\zombies\Zombie Crawler.fpe      S:3MB   V:0MB (292)     
12752046 : LOADING WAYPOINTS DATA                               S:0MB   V:4MB (296)     

12752065 : LOADING TERRAIN DATA                                 S:0MB   V:0MB (296)     

As you can see, thanks to being able to link video memory usage with stages in the engine resource process, I notice there is something going on in 'Load Player Config' which is taking 109MB of video memory, and it will be interesting to discover what that might be. I write this blog in the afternoon so it could be I have saved mucho memory by the time you are reading this. Just wanted to get this documented and out into the world to emphasis the usefulness of monitoring video memory on the fly!

Also managed to crunch two bugs, one zombie related and one light map related, with more tweaks, twists and turns to follow. As yesterday's blog was image deprived, I have created a quite level with some of the new modern day assets that have been added to the library.


As an aside, this scene without terrain runs on my machine at about 190 fps, a substantial step up from when I first installed by GeForce 9600 GT card :)

REQUEST: As the community has been such a sterling help getting to the bottom of the video memory read issue, I wanted to put another 'home work' task out there. I am looking for a good DirectX 9 shader technique for very fast but realistic water that does NOT rely on reflection or refraction. Often seen used to render completely opaque water but the ripples and light reflections make it look the bomb!  If you can send me shots, links to code, e.t.c. that would certainly help get that ball rolling.

Before I share the code, just wanted to provide an update that the next thing on my list for Friday is what is called wrap-up, which means preparing internal builds, finishing off and cleaning code, backing up and generally cleaning my desk. I fly out to San Francisco on Monday for a week, so I will need a tidy office and work-plate on my return from drinking all that Guinness.  For my immediate future, I will dive back into GPU video memory analysis and find out precisely who is spending all my VMEM budget!


DIRECTX 9 CODE TO READ VIDEO MEMORY 'IN USE':

DARKSDK int DMEMAvailable(void)
{
static int Memory = 0;
HANDLE ProcessHandle = GetCurrentProcess();
LONGLONG dedicatedBytesUsed = 0;
LONGLONG sharedBytesUsed = 0;
LONGLONG committedBytesUsed = 0;
HMODULE gdi32Handle;
PFND3DKMT_QUERYSTATISTICS queryD3DKMTStatistics;
        
if (gdi32Handle = LoadLibrary(TEXT("gdi32.dll")))
queryD3DKMTStatistics = (PFND3DKMT_QUERYSTATISTICS)GetProcAddress(gdi32Handle, "D3DKMTQueryStatistics");
        
if (queryD3DKMTStatistics)
{
D3DKMT_QUERYSTATISTICS queryStatistics;
IDirect3D9Ex* pDX = NULL;
Direct3DCreate9Ex ( D3D_SDK_VERSION, &pDX );
if ( pDX ) 
{
if ( pDX )
{
memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS));
queryStatistics.Type = D3DKMT_QUERYSTATISTICS_PROCESS;
pDX->GetAdapterLUID(0,&queryStatistics.AdapterLuid);
queryStatistics.hProcess = ProcessHandle;
if (queryD3DKMTStatistics(&queryStatistics)==0) 
{
committedBytesUsed = queryStatistics.QueryResult.ProcessInformation.SystemMemory.BytesAllocated;
}
memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS));
queryStatistics.Type = D3DKMT_QUERYSTATISTICS_ADAPTER;
pDX->GetAdapterLUID(0,&queryStatistics.AdapterLuid);
if (queryD3DKMTStatistics(&queryStatistics)==0) 
{
ULONG i;
ULONG segmentCount = queryStatistics.QueryResult.AdapterInformation.NbSegments;
for (i = 0; i < segmentCount; i++) 
{
memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS));
queryStatistics.Type = D3DKMT_QUERYSTATISTICS_SEGMENT;
pDX->GetAdapterLUID(0,&queryStatistics.AdapterLuid);
queryStatistics.QuerySegment.SegmentId = i;
if (queryD3DKMTStatistics(&queryStatistics)==0) 
{
// Windows 7 (Windows 8 and above is aperture = queryStatistics.QueryResult.SegmentInformation.Aperture;)
bool aperture = queryStatistics.QueryResult.SegmentInformationV1.Aperture;
                        
memset(&queryStatistics, 0, sizeof(D3DKMT_QUERYSTATISTICS));
queryStatistics.Type = D3DKMT_QUERYSTATISTICS_PROCESS_SEGMENT;
pDX->GetAdapterLUID(0,&queryStatistics.AdapterLuid);
queryStatistics.hProcess = ProcessHandle;
queryStatistics.QueryProcessSegment.SegmentId = i;
if (queryD3DKMTStatistics(&queryStatistics)==0)
{
if (aperture)
sharedBytesUsed += queryStatistics.QueryResult
.ProcessSegmentInformation
.BytesCommitted;
else
dedicatedBytesUsed += queryStatistics.QueryResult
.ProcessSegmentInformation
.BytesCommitted;
}
}
}
}

// free DX9Ex when done
pDX->Release();
}
}
}
        
// free GDI DLL
FreeLibrary(gdi32Handle);

// Pass dedicated memory used back to DBP
Memory = dedicatedBytesUsed / 1024 / 1024;
return Memory;

}



4 comments:

  1. http://www.rug.nl/cit/hpcv/publications/watershader/result-watershader.jpg

    From: http://www.rug.nl/science-and-society/centre-for-information-technology/research/hpcv/publications/watershader/

    ReplyDelete
  2. They serve Guinness in San Fransisco?

    ReplyDelete
  3. Probably not what you want but has a demo download, evaluation SDK which may give you some insight:

    http://sundog-soft.com/sds/features/ocean-and-water-rendering-with-triton/



    ReplyDelete
  4. ^^^^ That water looks great ^^^^

    ReplyDelete