ID3D10Counter usage

I'm trying to test Counters, with some problems. Here is a code snippet:

const int nCounters = 5;
const
D3D10_COUNTER type[nCounters ] = {
D3D10_COUNTER_GPU_IDLE, D3D10_COUNTER_VERTEX_PROCESSING,
D3D10_COUNTER_GEOMETRY_PROCESSING, D3D10_COUNTER_PIXEL_PROCESSING,
D3D10_COUNTER_OTHER_GPU_PROCESSING
};
int Value[ nCounters ];
ID3D10Counter *Counter[nCounters];
for(int i=0;i<nCounters;i++) {
D3D10_COUNTER_DESC d = { type[ i ], 0 };
HRESULT h = pDevice->CreateCounter( &d, &(Counter[ i ]) );
if (FAILED(h)) throw i;
}

The counter creation failed when the third counter is created (I tried permutations) with the following error:
First-chance exception at 0x767edde0 in ezDXUT2console.exe: Microsoft C++ exception: _com_error at memory location 0x0012f23c..

The values are read with:
while( Counter[ i ]->GetData(NULL,0,0) == S_FALSE )continue;
HRESULT h = Counter[ i ]->GetData(&(Value[ i ]),
sizeof(float),0);
on all counters created without error.

Is it a reference driver limitation?
By the way, OTHER_GPU_PROCESSING is not supported and theses counters always said the GPU is idle. Is it normal? (Ok, the reference driver is not a gpu, but should it not work like one?) Or, what am I doing or undestanding wrong?

[1934 byte] By [PascalMignot] at [2008-2-7]
# 1

I'm not on my Vista box right now so I don't have my code to hand, but I'm pretty sure I know what you're hitting.

Call ID3D10Device::CheckCounterInfo() and check the returned D3D10_COUNTER_INFO::NumSimultaneousCounters field. I'm pretty sure the RefRast has "4" for this field, but check anyway.

Thing is, each counter is not necessarily 1 counter (if that makes sense). Use ID3D10Device::CheckCounter() with mostly NULL values but inspect the returned pActiveCounters value. You'll find that some, if not all, occupy 2 counter slots.

Put this together and it basically means you have a finite number of counters and have to choose only a couple - which would fit with your code failing when you try and create the 3rd.

I can understand this for hardware devices (I'd not be surprised if we still need instrumented drivers for D3D10 GPU's thus making this feature largely unusable for the wider developer audience) but I'm a little surprised that the RefRast can't support more simultaneous counters. If I remember correctly you can't get all four (or is it five?) load-balancing counters (VS,GS,PS, Idle) at the same time which makes it pretty difficult to get useful results back. Oh well..

hth
Jack

JackHoxley at 2007-8-31 > top of Msdn Tech,Game Technologies: DirectX, XNA, XACT, etc.,Direct3D 10...
# 2

Yes, I missed these ones. From using CheckCounter and CheckCounterInfo, we can learn that:

CheckCounterInfo: LastCounter=0x40000003 CountersAvailable=4 NUnit=1
CheckCounter D3D10_COUNTER_GPU_IDLE Type=float32 HWslots=1 Name="GPU Idle" Unit=fraction
CheckCounter D3D10_COUNTER_VERTEX_PROCESSING Type=float32 CounterSlots=2 Name="Vertex Processing" Unit=fraction
CheckCounter D3D10_COUNTER_GEOMETRY_PROCESSING Type=float32 CounterSlots=2 Name="Geometry Processing" Unit=fraction
CheckCounter D3D10_COUNTER_PIXEL_PROCESSING Type=float32 CounterSlots=2 Name="Pixel Processing" Unit=fraction
CheckCounter D3D10_COUNTER_OTHER_GPU_PROCESSING Type=float32 CounterSlots=2 Name="?garbage?" Unit="?garbage?"

So, you are right. 4 counters on the REF driver, but since Vertex/Geometry/Pixel processing use 2, I can only have two
of these counters at the same time. A pity for a REF driver which should be able to run with all counters on.

From CheckCounterInfo, D3D10_COUNTER_OTHER_GPU_PROCESSING should be available
since 0x40000003 > D3D10_COUNTER_DEVICE_DEPENDENT_0, and it's not. The garbages
in the CheckCounter descriptions of D3D10_COUNTER_OTHER_GPU_PROCESSING are strange
too.

Thanks for your help.

PascalMignot at 2007-8-31 > top of Msdn Tech,Game Technologies: DirectX, XNA, XACT, etc.,Direct3D 10...
# 3
Pascal Mignot wrote:
A pity for a REF driver which should be able to run with all counters on.
I'd also like to see this, but I wouldn't be surprised if it is far too late in the development cycle to be a reality.

Pascal Mignot wrote:
From CheckCounterInfo, D3D10_COUNTER_OTHER_GPU_PROCESSING should be available since 0x40000003 > D3D10_COUNTER_DEVICE_DEPENDENT_0, and it's not. The garbages in the CheckCounter descriptions of D3D10_COUNTER_OTHER_GPU_PROCESSING are strange
too.
The D3D10_COUNTER enumeration just attempts to abstract out common hardware counters - pretty much the same as what happens with D3D9 queries. There is no requirement for the hardware to support any or all of them (as I previously said, I'm expecting real hardware to support NONE without hard-to-get instrumented drivers). I'm still on the wrong dev-box so don't have my code to hand, but ISTR the RefRast only supports a small subset of the possible standard counters.

You might be misunderstanding the D3D10_COUNTER_DEVICE_DEPENDENT_0 part - this is just a mechanism for a driver to add additional counters that haven't been defined as part of the D3D10 specification. If you check the RefRast's 3 "device dependent" counters you'll get fweddle and snargle measurements (or some other random gibberish analagous to page faults/context-switching). A more real-world example could be a device-dependent counter for the efficiency of an SLI/CrossFire setup - something that is quite specific to the hardware implementation being used.

Don't put too much hope into using the counters. It'd be really nice if they were a required feature like the rest of D3D10's feature set (hmm... dynamic load balancing via counters) but I don't think it'll happen. Same irritation I have with D3D9's queries - the door to lots of interesting information is defined but it's permanently locked.

hth
Jack

JackHoxley at 2007-8-31 > top of Msdn Tech,Game Technologies: DirectX, XNA, XACT, etc.,Direct3D 10...
# 4

> You might be misunderstanding the D3D10_COUNTER_DEVICE_DEPENDENT_0 part
> - this is just a mechanism for a driver to add additional counters
> that haven't been defined as part of the D3D10 specification. If you check
> the RefRast's 3 "device dependent" counters you'll get fweddle and snargle
> measurements (or some other random gibberish analagous to page
> faults/context-switching). A more real-world example could be a device-dependent
> counter for the efficiency of an SLI/CrossFire setup - something that is quite specific to the hardware implementation being used.

Ok, so you tell me, since I read 0x40000003 in LastDeviceDependentCounter, the REF device should have 4 device dependant counters. But why these counters don't work?

Otherwise, only the 4 first counters seem to be actually supported (and count nothing as I already said). By the way, it could be nice that CreateCounter returns E_INVALIDARG (as said in the documentation) rather than a COM error when all the maximum of simultaneous counters is reached.

PascalMignot at 2007-8-31 > top of Msdn Tech,Game Technologies: DirectX, XNA, XACT, etc.,Direct3D 10...
# 5

I'm at work right now, but I'll see about posting the code I've got when I get home. I had some simple code that would enumerate all support/information for counters and it appears to generate valid/correct information.

Jack

JackHoxley at 2007-8-31 > top of Msdn Tech,Game Technologies: DirectX, XNA, XACT, etc.,Direct3D 10...
# 6

Okay, as promised - here's my enumeration code:

void OutputCounters( ID3D10Device* pDevice )
{
D3D10_COUNTER_INFO CounterInfo;
pDevice->CheckCounterInfo( &CounterInfo );

// Print the basic information
WCHAR wcBasicInfo[512];
StringCchPrintf( wcBasicInfo, 512, L"\tBasic device information:\n"
L"\t\tMaximum number of simultaneous counters: %d\n"
L"\t\tDetectable parallel units: %d\n"
L"\t\tNumber of device-specific counters: %d\n",
CounterInfo.NumSimultaneousCounters,
CounterInfo.NumDetectableParallelUnits,
CounterInfo.LastDeviceDependentCounter - D3D10_COUNTER_DEVICE_DEPENDENT_0
);
OutputDebugString( wcBasicInfo );

// Iterate through all the device independent counters (18 total)
OutputDebugString( L"\tDevice independent counters:\n" );
for( UINT i = 0; i < 18; ++i )
OutputCounterDetails( pDevice, static_cast< D3D10_COUNTER >( i ) );

if( 0 != (CounterInfo.LastDeviceDependentCounter - D3D10_COUNTER_DEVICE_DEPENDENT_0) )
OutputDebugString( L"\tDevice dependent counters:\n" );

for( int i = D3D10_COUNTER_DEVICE_DEPENDENT_0; i < CounterInfo.LastDeviceDependentCounter; ++i )
OutputCounterDetails( pDevice, static_cast< D3D10_COUNTER >( i ) );
}

void OutputCounterDetails( ID3D10Device* pDevice, const D3D10_COUNTER& id )
{
// Extract information about this particular counter
D3D10_COUNTER_DESC d = { id, 0 };
D3D10_COUNTER_TYPE type = static_cast< D3D10_COUNTER_TYPE >( 0 );
UINT uiSlotsRequired = 0;
LPSTR sName = NULL;
UINT uiNameLength = 0;
LPSTR sUnits = NULL;
UINT uiUnitsLength = 0;
LPSTR sDesc = NULL;
UINT uiDescLength = 0;

if( SUCCEEDED( pDevice->CheckCounter( &d, &type, &uiSlotsRequired, NULL, &uiNameLength, NULL, &uiUnitsLength, NULL, &uiDescLength ) ) )
{
// We now know how much space to allocate for each string:
sName = new char[ uiNameLength ];
sUnits = new char[ uiUnitsLength ];
sDesc = new char[ uiDescLength ];

// Repeat the call in order to grab the text fields as well:
if( FAILED( pDevice->CheckCounter( &d, &type, &uiSlotsRequired, sName, &uiNameLength, sUnits, &uiUnitsLength, sDesc, &uiDescLength ) ) )
{
ERR_OUT( L"Failed to retrieve counter's string fields!" );

SAFE_DELETE_ARRAY( sName );
SAFE_DELETE_ARRAY( sUnits );
SAFE_DELETE_ARRAY( sDesc );

return;
}

WCHAR wcTypeName[64];
switch( type )
{
case D3D10_COUNTER_TYPE_FLOAT32:
wcscpy_s( wcTypeName, 64, L"32bit float" );
break;

case D3D10_COUNTER_TYPE_UINT16:
wcscpy_s( wcTypeName, 64, L"unsigned 16bit integer" );
break;

case D3D10_COUNTER_TYPE_UINT32:
wcscpy_s( wcTypeName, 64, L"unsigned 32bit integer" );
break;

case D3D10_COUNTER_TYPE_UINT64:
wcscpy_s( wcTypeName, 64, L"unsigned 64bit integer" );
break;
}

// Convert ASCII strings to Unicode for output...
int size = 0;

size = MultiByteToWideChar( CP_ACP, 0, sName, -1, NULL, 0 );
WCHAR *wcName = new WCHAR[ size ];
MultiByteToWideChar( CP_ACP, 0, sName, -1, wcName, size );

size = MultiByteToWideChar( CP_ACP, 0, sUnits, -1, NULL, 0 );
WCHAR *wcUnits = new WCHAR[ size ];
MultiByteToWideChar( CP_ACP, 0, sUnits, -1, wcUnits, size );

size = MultiByteToWideChar( CP_ACP, 0, sDesc, -1, NULL, 0 );
WCHAR *wcDesc = new WCHAR[ size ];
MultiByteToWideChar( CP_ACP, 0, sDesc, -1, wcDesc, size );

// Check if this counter type is actually supported
HRESULT hr = pDevice->CreateCounter( &d, NULL );
if( SUCCEEDED( hr ) )
{
// We can use this counter just fine!
WCHAR wcOut[512];
StringCchPrintf( wcOut, 512, L"\t\t'%s' can be used with this device.\n"
L"\t\t\t%s\n"
L"\t\t\tRequires %d counter(s)\n"
L"\t\t\t%s %s\n",
wcName,
wcDesc,
uiSlotsRequired,
wcTypeName, wcUnits
);
OutputDebugString( wcOut );
}

// Make sure we release the temporary data
SAFE_DELETE_ARRAY( sName );
SAFE_DELETE_ARRAY( sUnits );
SAFE_DELETE_ARRAY( sDesc );
SAFE_DELETE_ARRAY( wcName );
SAFE_DELETE_ARRAY( wcUnits );
SAFE_DELETE_ARRAY( wcDesc );
}
}

That should just output a list of all supported counters and information about them to the debug output window in VS.

hth
Jack

JackHoxley at 2007-8-31 > top of Msdn Tech,Game Technologies: DirectX, XNA, XACT, etc.,Direct3D 10...
# 7

Ok, my test code was the same (except I was not using unicode), and
I was na?ve enough to think that the reference device supported all the
counters, and thus, not testing the CheckCounter returns. That explains
the garbages for unsupported counters.

I've also seen device dependent counters (the infamous Fweddle
Counters) and the private joke in the unit field.

Thanks a lot for your help.

PascalMignot at 2007-8-31 > top of Msdn Tech,Game Technologies: DirectX, XNA, XACT, etc.,Direct3D 10...