Jump to content
  • Advertisement
  • entries
    422
  • comments
    1540
  • views
    490101

Smartly pointing

Sign in to follow this  
jollyjeffers

163 views

In anticipation of getting some Vista/D3D10 action next week I've been giving some thought to my standard code-base. Over the years I've picked up various bits of code-related trickery that can really clean up the code you write.

I like things that make my code cleaner, simpler and less error-prone [grin]

d3d_utils.h

I'm putting together a header file that imports all the other bits-n-pieces. Makes it nice and convenient to drag everything in with a single declaration...

#ifndef INC_D3D_UTILS_H
#define INC_D3D_UTILS_H

#ifndef NO_D3D_SMART_POINTERS
#include "d3d_utils_smart_pointers.h"
#endif

#ifndef NO_PIX_PROFILING_HELPER
#include "d3d_utils_scope_profiler.h"
#endif

#ifndef NO_HRESULT_TO_EXCEPTION_MAPPING
// Rich Thompson's THR() stuff
#endif

#ifndef NO_MATCHING_CALL_HELPER
// Lock()/Unlock()
// LockRect()/UnlockRect()
// LockBox()/UnlockBox()
// Begin()/End()
// BeginPass()/EndPass()
// BeginScene()/EndScene()
#endif

#ifndef NO_MEMORY_LEAK_TRACKING
// SetPrivateData() leak tracking
#endif

#endif


I've wrapped each #include in conditionals so that its possible to remove any sub-components if they aren't desired. Seems that plenty of people avoid C++ exceptions (seemingly for either good or misguided reasons) so it'd be perfectly valid to do something like the following:

#define NO_HRESULT_TO_EXCEPTION_MAPPING
#include 'd3d_utils.h'


Dead-code elimination would probably remove it eventually, but having it culled at a relatively high level should help compilation times.

I'm currently prototyping this in D3D9 but as originally stated I'm mostly interested in tidying this up for D3D10. What I'll need to check is whether I can use the same #ifdef / #ifndef blocks to include/exclude Direct3D 10 definitions and maintain support for D3D9 without compile errors.

d3d_utils_smart_pointers.h

Smart pointers aren't really new and I'd assume that a lot of people reading this will have at least tried them. Having said that, the SDK samples don't use them and the vast majority of code I see posted in the forums is completely devoid of them.

What I've put together is a simple header of aliases similar to:

#define SP_D3D_DEVICE9 D3DUtils::IDirect3DDevice9_SP
typedef ATL::CComPtr< IDirect3DDevice9 > IDirect3DDevice9_SP;


The SDK uses a typedef to create LPDIRECT3DDEVICE9 and I was thinking of introducing an SPDIRECT3DDEVICE9 but I figured that "stealing" the standard naming convention was an easy way to introduce confusion. It also allowed me the opportunity to create some shorter aliases (e.g. SP_D3D_DEVICE9).

The full header:
// ===============================================================================================================
//
// FILE: d3d_utils_smart_pointers.h
// LAST MODIFIED: 12th August 2006
//
// AUTHOR: Jack Hoxley
//
// LICENCE: All code is provided as-is, do what you want with it but if it breaks
// your code then it aint my problem ;-)
//
// DESCRIPTION: This part of the D3DUtils namespace provides convenient type definitions
// for COM smart pointers (provided via the ATL library). Smart points remove
// much of the complexity regarding safely releasing COM-based Direct3D resources.
//
// USAGE: Usage is much the same as with a normal COM interface pointer. Refer to
// "Using ATL with DirectX COM Interfaces" in the DX SDK documentation.
//
// For example:
// IDirect3DDevice9* pDevice;
// LPDIRECT3DDEVICE9 pDevice;
// SP_D3D_DEVICE9 pDevice;
// D3DUtils::IDirect3DDevice9_SP pDevice;
// Are all equivalent declarations for a D3D device.
//
// When creating an IDirect3D9:
// SP_D3D9 spD3D;
// spD3D.Attach( Direct3DCreate9( D3D_SDK_VERSION ) );
// Do *NOT* use the following:
// SP_D3D9 spD3D = Direct3DCreate9( D3D_SDK_VERSION );
//
// To force an early release:
// Use ".Release()" instead of "->Release()"
// No need to use SAFE_RELEASE() as CComPtr NULL's itself.
//
// To make a smart-pointer from a provided "naked" pointer:
// IDirect3DTexture9 *pTex;
// SP_D3D_TEXTURE9 spTex;
// spTex.Attach( pTex );
// pd3dDevice->SetTexture( 0, spTex ); // use pTex via spTex
// // No need to worry about Release()'ing pTex anymore...
//
//
//
// CHANGE LOG: [12-08-2006] Initial version based on August 2006 DX9 SDK
//
// ===============================================================================================================

#ifndef INC_D3D_UTILS_SMART_POINTERS
#define INC_D3D_UTILS_SMART_POINTERS

#pragma warning( disable : 4995 ) // Disable warning about deprecated string functions
#include
#pragma warning( default : 4995 )

// Convenient short-hand substitutions
#define SP_D3D9 D3DUtils::IDirect3D9_SP
#define SP_D3D_DEVICE9 D3DUtils::IDirect3DDevice9_SP
#define SP_D3D_RESOURCE9 D3DUtils::IDirect3DResource9_SP

#define SP_D3D_BASETEXTURE9 D3DUtils::IDirect3DBaseTexture9_SP
#define SP_D3D_TEXTURE9 D3DUtils::IDirect3DTexture9_SP
#define SP_D3D_CUBETEXTURE9 D3DUtils::IDirect3DCubTexture9_SP
#define SP_D3D_VOLUMETEXTURE9 D3DUtils::IDirect3DVolumeTexture9_SP
#define SP_D3D_VOLUME9 D3DUtils::IDirect3DVolume9_SP
#define SP_D3D_SURFACE9 D3DUtils::IDirect3DSurface9_SP

#define SP_D3D_VB9 D3DUtils::IDirect3DVertexBuffer9_SP
#define SP_D3D_VBDECL9 D3DUtils::IDirect3DVertexDeclaration9_SP
#define SP_D3D_IB9 D3DUtils::IDirect3DIndexBuffer9_SP

#define SP_D3D_QUERY9 D3DUtils::IDirect3DQuery9_SP
#define SP_D3D_VSHADER9 D3DUtils::IDirect3DVertexShader9_SP
#define SP_D3D_PSHADER9 D3DUtils::IDirect3DPixelShader9_SP
#define SP_D3D_STATEBLOCK9 D3DUtils::IDirect3DStateBlock9_SP
#define SP_D3D_SWAPCHAIN9 D3DUtils::IDirect3DSwapChain9_SP

#define SP_D3D_XFILE D3DUtils::ID3DXFile_SP
#define SP_D3D_XFILEDATA D3DUtils::ID3DXFileData_SP
#define SP_D3D_XFILEENUMOBJECT D3DUtils::ID3DXFileEnumObject_SP
#define SP_D3D_XFILESAVEDATA D3DUtils::ID3DXFileSaveData_SP
#define SP_D3D_XFILESAVEOBJECT D3DUtils::ID3DXFileSaveObject_SP

#define SP_D3DX_ALLOCATEHIERARCHY D3DUtils::ID3DXAllocateHierarchy_SP;
#define SP_D3DX_ANIMATIONCALLBACKHANDLER D3DUtils::ID3DXAnimationCallbackHandler_SP;
#define SP_D3DX_ANIMATIONCONTROLLER D3DUtils::ID3DXAnimationController_SP;
#define SP_D3DX_ANIMATIONSET D3DUtils::ID3DXAnimationSet_SP;
#define SP_D3DX_BASEEFFECT D3DUtils::ID3DXBaseEffect_SP;
#define SP_D3DX_BASEMESH D3DUtils::ID3DXBaseMesh_SP;
#define SP_D3DX_BUFFER D3DUtils::ID3DXBuffer_SP;
#define SP_D3DX_COMPRESSEDANIMATIONSET D3DUtils::ID3DXCompressedAnimationSet_SP;
#define SP_D3DX_CONSTANTTABLE D3DUtils::ID3DXConstantTable_SP;
#define SP_D3DX_EFFECT D3DUtils::ID3DXEffect_SP;
#define SP_D3DX_EFFECTCOMPILER D3DUtils::ID3DXEffectCompiler_SP;
#define SP_D3DX_EFFECTPOOL D3DUtils::ID3DXEffectPool_SP;
#define SP_D3DX_EFFECTSTATEMANAGER D3DUtils::ID3DXEffectStateManager_SP;
#define SP_D3DX_FONT D3DUtils::ID3DXFont_SP;
#define SP_D3DX_FRAGMENTLINKER D3DUtils::ID3DXFragmentLinker_SP;
#define SP_D3DX_INCLUDE D3DUtils::ID3DXInclude_SP;
#define SP_D3DX_KEYFRAMEDANIMATIONSET D3DUtils::ID3DXKeyframedAnimationSet_SP;
#define SP_D3DX_LINE D3DUtils::ID3DXLine_SP;
#define SP_D3DX_LOADUSERDATA D3DUtils::ID3DXLoadUserData_SP;
#define SP_D3DX_MATRIXSTACK D3DUtils::ID3DXMATRIXStack_SP;
#define SP_D3DX_MESH D3DUtils::ID3DXMesh_SP;
#define SP_D3DX_PATCHMESH D3DUtils::ID3DXPatchMesh_SP;
#define SP_D3DX_PMESH D3DUtils::ID3DXPMesh_SP;
#define SP_D3DX_PRTBUFFER D3DUtils::ID3DXPRTBuffer_SP;
#define SP_D3DX_PRTCOMPBUFFER D3DUtils::ID3DXPRTCompBuffer_SP;
#define SP_D3DX_PRTENGINE D3DUtils::ID3DXPRTEngine_SP;
#define SP_D3DX_RENDERTOENVMAP D3DUtils::ID3DXRenderToEnvMap_SP;
#define SP_D3DX_RENDERTOSURFACE D3DUtils::ID3DXRenderToSurface_SP;
#define SP_D3DX_SAVEUSERDATA D3DUtils::ID3DXSaveUserData_SP;
#define SP_D3DX_SKININFO D3DUtils::ID3DXSkinInfo_SP;
#define SP_D3DX_SPMESH D3DUtils::ID3DXSPMesh_SP;
#define SP_D3DX_SPRITE D3DUtils::ID3DXSprite_SP;
#define SP_D3DX_TEXTUREGUTTERHELPER D3DUtils::ID3DXTextureGutterHelper_SP;
#define SP_D3DX_TEXTURESHADER D3DUtils::ID3DXTextureShader_SP;



namespace D3DUtils
{
// Direct3D Core Objects
typedef ATL::CComPtr< IDirect3D9 > IDirect3D9_SP;
typedef ATL::CComPtr< IDirect3DDevice9 > IDirect3DDevice9_SP;
typedef ATL::CComPtr< IDirect3DResource9 > IDirect3DResource9_SP;

// Direct3D Image Resources
typedef ATL::CComPtr< IDirect3DBaseTexture9 > IDirect3DBaseTexture9_SP;
typedef ATL::CComPtr< IDirect3DTexture9 > IDirect3DTexture9_SP;
typedef ATL::CComPtr< IDirect3DCubeTexture9 > IDirect3DCubeTexture9_SP;
typedef ATL::CComPtr< IDirect3DVolumeTexture9 > IDirect3DVolumeTexture9_SP;
typedef ATL::CComPtr< IDirect3DVolume9 > IDirect3DVolume9_SP;
typedef ATL::CComPtr< IDirect3DSurface9 > IDirect3DSurface9_SP;

// Direct3D Geometry Resources
typedef ATL::CComPtr< IDirect3DVertexBuffer9 > IDirect3DVertexBuffer9_SP;
typedef ATL::CComPtr< IDirect3DVertexDeclaration9 > IDirect3DVertexDeclaration9_SP;
typedef ATL::CComPtr< IDirect3DIndexBuffer9 > IDirect3DIndexBuffer9_SP;

// Direct3D Rendering Resources
typedef ATL::CComPtr< IDirect3DQuery9 > IDirect3DQuery9_SP;
typedef ATL::CComPtr< IDirect3DVertexShader9 > IDirect3DVertexShader9_SP;
typedef ATL::CComPtr< IDirect3DPixelShader9 > IDirect3DPixelShader9_SP;
typedef ATL::CComPtr< IDirect3DStateBlock9 > IDirect3DStateBlock9_SP;
typedef ATL::CComPtr< IDirect3DSwapChain9 > IDirect3DSwapChain9_SP;

// Direct3D X File Interfaces
typedef ATL::CComPtr< ID3DXFile > ID3DXFile_SP;
typedef ATL::CComPtr< ID3DXFileData > ID3DXFileData_SP;
typedef ATL::CComPtr< ID3DXFileEnumObject > ID3DXFileEnumObject_SP;
typedef ATL::CComPtr< ID3DXFileSaveData > ID3DXFileSaveData_SP;
typedef ATL::CComPtr< ID3DXFileSaveObject > ID3DXFileSaveObject_SP;

// D3DX
typedef ATL::CComPtr< ID3DXAllocateHierarchy > ID3DXAllocateHierarchy_SP;
typedef ATL::CComPtr< ID3DXAnimationCallbackHandler > ID3DXAnimationCallbackHandler_SP;
typedef ATL::CComPtr< ID3DXAnimationController > ID3DXAnimationController_SP;
typedef ATL::CComPtr< ID3DXAnimationSet > ID3DXAnimationSet_SP;
typedef ATL::CComPtr< ID3DXBaseEffect > ID3DXBaseEffect_SP;
typedef ATL::CComPtr< ID3DXBaseMesh > ID3DXBaseMesh_SP;
typedef ATL::CComPtr< ID3DXBuffer > ID3DXBuffer_SP;
typedef ATL::CComPtr< ID3DXCompressedAnimationSet > ID3DXCompressedAnimationSet_SP;
typedef ATL::CComPtr< ID3DXConstantTable > ID3DXConstantTable_SP;
typedef ATL::CComPtr< ID3DXEffect > ID3DXEffect_SP;
typedef ATL::CComPtr< ID3DXEffectCompiler > ID3DXEffectCompiler_SP;
typedef ATL::CComPtr< ID3DXEffectPool > ID3DXEffectPool_SP;
typedef ATL::CComPtr< ID3DXEffectStateManager > ID3DXEffectStateManager_SP;
typedef ATL::CComPtr< ID3DXFont > ID3DXFont_SP;
typedef ATL::CComPtr< ID3DXFragmentLinker > ID3DXFragmentLinker_SP;
typedef ATL::CComPtr< ID3DXInclude > ID3DXInclude_SP;
typedef ATL::CComPtr< ID3DXKeyframedAnimationSet > ID3DXKeyframedAnimationSet_SP;
typedef ATL::CComPtr< ID3DXLine > ID3DXLine_SP;
typedef ATL::CComPtr< ID3DXLoadUserData > ID3DXLoadUserData_SP;
typedef ATL::CComPtr< ID3DXMatrixStack > ID3DXMATRIXStack_SP;
typedef ATL::CComPtr< ID3DXMesh > ID3DXMesh_SP;
typedef ATL::CComPtr< ID3DXPatchMesh > ID3DXPatchMesh_SP;
typedef ATL::CComPtr< ID3DXPMesh > ID3DXPMesh_SP;
typedef ATL::CComPtr< ID3DXPRTBuffer > ID3DXPRTBuffer_SP;
typedef ATL::CComPtr< ID3DXPRTCompBuffer > ID3DXPRTCompBuffer_SP;
typedef ATL::CComPtr< ID3DXPRTEngine > ID3DXPRTEngine_SP;
typedef ATL::CComPtr< ID3DXRenderToEnvMap > ID3DXRenderToEnvMap_SP;
typedef ATL::CComPtr< ID3DXRenderToSurface > ID3DXRenderToSurface_SP;
typedef ATL::CComPtr< ID3DXSaveUserData > ID3DXSaveUserData_SP;
typedef ATL::CComPtr< ID3DXSkinInfo > ID3DXSkinInfo_SP;
typedef ATL::CComPtr< ID3DXSPMesh > ID3DXSPMesh_SP;
typedef ATL::CComPtr< ID3DXSprite > ID3DXSprite_SP;
typedef ATL::CComPtr< ID3DXTextureGutterHelper > ID3DXTextureGutterHelper_SP;
typedef ATL::CComPtr< ID3DXTextureShader > ID3DXTextureShader_SP;
};

#endif


The MSDN and DX documentation suggest you refer to the ATL headers for information on how to use CComPtr but my copy is completely devoid of any useful comments. I know there are some tricky issues introduced by smart pointers (usually revolving around who really owns it) but so far I've not found the details via the good or the hard way. Time will tell on this I guess...

Making and restoring a defensive copy of the implicit swap-chain around a RtT block:
IDirect3DSurface9 *pRenderTarget = NULL;
IDirect3DSurface9 *pDepthStencil = NULL;

if( FAILED( pd3dDevice->GetDepthStencilSurface( &pDepthStencil ) ) )
{
SAFE_RELEASE( pRenderTarget );
SAFE_RELEASE( pDepthStencil );

return;
}

if( FAILED( pd3dDevice->GetRenderTarget( 0, &pRenderTarget ) ) )
{
SAFE_RELEASE( pRenderTarget );
SAFE_RELEASE( pDepthStencil );

return;
}

// Do some render-to-texture stuff here

if( FAILED( pd3dDevice->SetDepthStencilSurface( pDepthStencil ) ) )
{
SAFE_RELEASE( pRenderTarget );
SAFE_RELEASE( pDepthStencil );

return;
}

if( FAILED( pd3dDevice->SetRenderTarget( 0, pRenderTarget ) ) )
{
SAFE_RELEASE( pRenderTarget );
SAFE_RELEASE( pDepthStencil );

return;
}

SAFE_RELEASE( pRenderTarget );
SAFE_RELEASE( pDepthStencil );

Looks fairly simple but unless you're careful it gets so very easy to introduce bugs here. Forget just one of those SAFE_RELEASE() calls and you're on your way to memory leak hell. Only two of those SAFE_RELEASE()'s will be called in normal execution (easy to spot if you miss them) but the others will only be hit if an API call fails which is relatively unlikely. I've had a large code-base where tidy-up-on-error was missing in a few places and it caused me days of tracing before I found out under what special condition X failed and then didn't release Y. Not fun.

SP_D3D_SURFACE9 spRenderTarget;
SP_D3D_SURFACE9 spDepthStencil;

if( FAILED( pd3dDevice->GetDepthStencilSurface( &spDepthStencil ) ) )
{
return;
}

if( FAILED( pd3dDevice->GetRenderTarget( 0, &spRenderTarget ) ) )
{
return;
}

// Do some render-to-texture stuff here

if( FAILED( pd3dDevice->SetDepthStencilSurface( spDepthStencil ) ) )
{
return;
}

if( FAILED( pd3dDevice->SetRenderTarget( 0, spRenderTarget ) ) )
{
return;
}

Functionally the same as before but now using smart pointers. If a call fails and we bail out of the function then the SP goes out of scope and automagically releases it for us. It also reduces the amount of "fluff" distracting us from the real purpose of this function...



In the interest of keeping this short-n-sweet I'll cover other parts of my utility framework in a future journal entry.

In the meantime - anyone else got any cool code related tips-n-tricks to share?
Sign in to follow this  


1 Comment


Recommended Comments

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!