   [C++] D3D/PIX profiling helper
  Evening all,I've been doing some work on one of my projects and came up with a nifty little trick that I thought I'd share with you guys. Maybe you'll find it useful..I would hope that everyone who's using a reasonably up-to-date version of Direct3D 9 has experimented with PIX for Windows. If not, why not? [smile]A lesser known set of features are the D3DPERF_BeginEvent(), D3DPERF_SetMarker() and D3DPERF_EndEvent() API calls. I covered them a while back in my developer journal.What I've written is hardly rocket-science, but it's one of those (I think) more useful uses of the C/C++ preprocessor. Using the following bits of code you can add a PROFILE_BLOCK in your code and it'll do the rest for you - including making sure that it cleans up correctly.D3DUtils.h (Download directly from here)#include "dxstdafx.h"#ifndef INC_D3DUTILS_H#define INC_D3DUTILS_H// These first two macros are taken from the// VStudio help files - necessary to convert the// __FUNCTION__ symbol from char to wchar_t.#define WIDEN2(x) L ## x#define WIDEN(x) WIDEN2(x)// Only the first of these macro's should be used. The _INTERNAL// one is so that the sp##id part generates "sp1234" type identifiers// instead of always "sp__LINE__"...#define PROFILE_BLOCK PROFILE_BLOCK_INTERNAL( __LINE__ )#define PROFILE_BLOCK_INTERNAL(id) D3DUtils::ScopeProfiler sp##id ( WIDEN(__FUNCTION__), __LINE__ );// To avoid polluting the global namespace,// all D3D utility functions/classes are wrapped// up in the D3DUtils namespace.namespace D3DUtils{class ScopeProfiler{public:ScopeProfiler( WCHAR *Name, int Line );~ScopeProfiler( );private:ScopeProfiler( );};}#endifD3DUtils.cpp (Download directly from here)#include "dxstdafx.h"#include "D3DUtils.h"#include <time.h>namespace D3DUtils{// Class constructor. Takes the necessary information and// composes a string that will appear in PIXfW.ScopeProfiler::ScopeProfiler( WCHAR* Name, int Line ){WCHAR wc[ MAX_PATH ];StringCchPrintf( wc, MAX_PATH, L"%s @ Line %d.\0", Name, Line );D3DPERF_BeginEvent( D3DCOLOR_XRGB( rand() % 255, rand() % 255, rand() % 255 ), wc );srand( static_cast< unsigned >( time( NULL ) ) );}// Makes sure that the BeginEvent() has a matching EndEvent()// if used via the macro in D3DUtils.h this will be called when// the variable goes out of scope.ScopeProfiler::~ScopeProfiler( ){D3DPERF_EndEvent( );}}A few notes:Just do a #include "D3DUtils.h" in the code you want to use itA random colour is created for the sampler, but PIXfW doesn't currently make  use of this.I've used the dxstdafx.h PCH file that you find in the SDK. If you're not using this, then make sure you replace it with d3dx9.h, windows.h and math.h.PIXfW only monitors Direct3D calls, so theres no point in using this code to watch sections that don't contain any D3DX/D3D calls!The usage is pretty simple. It creates an instance of ScopeProfiler on the stack such that it's destructor will automagically get called when it goes out of scope. The destructor contains a D3DPERF_EndEvent(), making sure that the D3DPERF_BeginEvent() is correctly matched...// Watch an entire function:void CALLBACK OnFrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext ){    PROFILE_BLOCK    // other code goes here}// Watch a specific subset:{    PROFILE_BLOCK    V( g_HUD.OnRender( fElapsedTime ) );}// Use the class directly to override the default-generated// name:if( g_SettingsDlg.IsActive() ){    D3DUtils::ScopeProfiler( L"OnFrameRender() - Setting Dialog Rendering", __LINE__ );    g_SettingsDlg.OnRender( fElapsedTime );    return;}The result, when you run a PIX Full Call Stream Capture:(The events added by the program are highlighted in pink)Feel free to do whatever you want with the code. Use it and abuse it - At your own risk of course [wink]Cheers,Jack   
  Looks pretty sweet  - I shall certainly start using it for my PIX profiling.Quote:Use it and abuse it - At your own risk of courseOh teh noes!!!  It has explodeded my computar!!!   ;Quote:Original post by circlesoftQuote:Use it and abuse it - At your own risk of courseOh teh noes!!!  It has explodeded my computar!!!Weren't me Guv' [looksaround]Jack   ;  Oh teh noes!!! Us MDX folk really get screwed again...I've been working for hours to get this to work for MDX, using P/Invoke to call the appropriate methods in the D3D DLL. I even wrote a small C++ app to check if there was something wrong with my PIX (you never know :p), but the C++ app worked just fine. I tried just about everything, overriding Marshalling rules with mad alternatives and whatnot, but I just couldn't get it to work.Until I imported the functions from the retail runtime, instead of the developer runtime! Now it works like a charm. I really don't get why these functions work using d3d9.dll and just fail silently when I import them from d3d9d.dll. I checked the d3d9d.dll using dumpbin and it does export the functions... Any ideas?Anyway, I've created a little Profiler class of my own that allows you to do something similar in MDX. It supports both profiler Events and Markers and it can optionally append the filename and line number to the event or marker name. Unfortunately obtaining the filename and line number in C# is quite expensive, since I found no other way than to obtain a stacktrace and follow that out of the profiler class. There is no __LINE__ macro and you can't defined macros in C# for that nifty ScopeProfiler, so I guess this should do.-> Download the C# Profiler class (2KB ZIP)You can use it like this:[source lang=c#]  Profiler.BeginEvent( "Prepare scene" );  scene.Prepare(device);  Profiler.EndEvent();  // Clear the render target and the zbuffer device.Clear(ClearFlags.ZBuffer | ClearFlags.Target, 0x002D32AA, 1.0f, 0);  Profiler.SetMarker( "Device cleared" );Some notes:1) You can switch the profiler messages on and off globally by changing the static Enabled property on the Profiler class to speed up rendering when you don't need the profiler info. The same goes for file/line info, which is turned off by default, being costly and all.2) You'll need to call the EndEvent() method for every BeginEvent() you invoke, otherwise bad things can and will happen3) The BeginEvent and SetMarker methods provide a number of overloads for lazy programmers, so you don't need to explicitly provide a label for the event or marker. This is particularly useful when you have file/line info turned on, otherwise you'll get blank labels in PIX.The gory details:[source lang=c#]// int D3DPERF_BeginEvent( D3DCOLOR col, LPCWSTR wszName );[System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously[DllImport("d3d9.dll", EntryPoint="D3DPERF_BeginEvent", CharSet=CharSet.Auto, CallingConvention=CallingConvention.Winapi)]private static extern int BeginEventDirect( uint col, string wszName );// int D3DPERF_EndEvent();[System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously, really [DllImport("d3d9.dll", EntryPoint="D3DPERF_EndEvent", CallingConvention=CallingConvention.Winapi)]private static extern int EndEventDirect();// void D3DPERF_SetMarker( D3DCOLOR col,LPCWSTR wszName);[System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously, really really[DllImport("d3d9.dll", EntryPoint="D3DPERF_SetMarker", CharSet=CharSet.Auto, CallingConvention=CallingConvention.Winapi)]private static extern void SetMarkerDirect( uint col, string wszName );;Quote:Original post by remigiusUntil I imported the functions from the retail runtime, instead of the developer runtime! Now it works like a charm. I really don't get why these functions work using d3d9.dll and just fail silently when I import them from d3d9d.dll. I checked the d3d9d.dll using dumpbin and it does export the functions... Any ideas?The reason that d3d9d.dll doesn't work is because PIX only intercepts the D3DPERF_ apis from d3d9.dll.  Normally, people don't directly load d3d9d.dll and instead rely on the runtime debug switching built into d3d9.dll.Paul (too lazy to find my Gamedev.net password );  Ah, ok. Thanks for clearing that up. Makes sense too, profiling a game with the debug runtimes isn't very useful, now that I think of it :)   ;  Neat modification remigius [smile]Quote:2) You'll need to call the EndEvent() method for every BeginEvent() you invoke, otherwise bad things can and will happenCan't you do something similar to my code? Where the Begin is implicitly called by the constructor and End is called by it's destructor - and regular scope/stack rules automagically call the destructor when the object goes out of scope?Cheers,Jack   ;Quote:Original post by jollyjeffersQuote:2) You'll need to call the EndEvent() method for every BeginEvent() you invoke, otherwise bad things can and will happenCan't you do something similar to my code? Where the Begin is implicitly called by the constructor and End is called by it's destructor - and regular scope/stack rules automagically call the destructor when the object goes out of scope?Deterministic finalization (calling destructors at end of scope) is only implemented in C++/CLI, as far as I know. C# doesn't do that. One can kinda 'fake' it using the disposable pattern: Inheriting from IDisposable, making the constructor begin the event, and making Dispose end it. Then it'd be used like:using(PixThingy thingy = new PixThingy()){    // stuff}   ;Quote:Deterministic finalization (calling destructors at end of scope) is only implemented in C++/CLI, as far as I know. C# doesn't do that. One can kinda 'fake' it using the disposable pattern: Inheriting from IDisposable, making the constructor begin the event, and making Dispose end it. Then it'd be used like:...That's right, I tried Jack's approach and it generated some 10.000 instances of the profiler class before it started gabagecollecting them. The using block is a very good idea indeed, gonna add that to the class myself. Thanks for pointing it out!   ;  I forgot about the deterministic finalization / GC'ing. Whoops [smile]Jack   