Sign in to follow this  
Dan Caranfil

Smart pointers in a DirectX game

Recommended Posts

I'm making a game that will use a lot of LPD3DXMESH meshes. After doing some research I found claims that the best method to avoid COM-specific memory leaks is using smart pointers. So I have this questions: 1. Is is easier to use smart pointers instead of AddRef - Release method? 2. Since I can't write a smart pointer myself yet, I plan using ATL CComPtr class. Do this instructions have to be followed in each ATL + DirectX project? 3. I'm staking my meshes in a STL list and want to be able to modify textures for each mesh in the stack. I managed putting up a copy constructor that gives me this behavior I want. My question is how will the smart pointer affect the copy constructor and do I need a copy constructor at all if I plan using a smart pointer?

Share this post


Link to post
Share on other sites
Quote:
Original post by Dan Caranfil
1. Is is easier to use smart pointers instead of AddRef - Release method?


Yes because calls to addref/release are automated.

Quote:
Original post by Dan Caranfil
2. Since I can't write a smart pointer myself yet, I plan using ATL CComPtr class. Do this instructions have to be followed in each ATL + DirectX project?


I don't know. You can use boost::intrusive_ptr with COM/COM like objects. It's very simple to use.

Quote:
Original post by Dan Caranfil
3. I'm staking my meshes in a STL list and want to be able to modify textures for each mesh in the stack. I managed putting up a copy constructor that gives me this behavior I want. My question is how will the smart pointer affect the copy constructor and do I need a copy constructor at all if I plan using a smart pointer?


I don't understand what you're trying to say so i may have understood you wrong but typical smart pointer strategies do nothing with copy constructors on there own. The only strategy i can think of where copying is involved is copy-on-write (COW) but this isn't applicable in this context.

Also unless you declare a copy constructor private/protected you always have a copy constructor defined either implicitly compiler generated that does a memberwise (or bitwise for POD-struct types) copy or explicitly you define the copy constructor yourself.

No you don't need to user-define a copy constructor to use most smart pointers.

Share this post


Link to post
Share on other sites
Quote:
Original post by Dan Caranfil
2. Since I can't write a smart pointer myself yet, I plan using ATL CComPtr class. Do this instructions have to be followed in each ATL + DirectX project?

No, CComPtr is a very simple class. Just include <atlbase.h> and go ahead.

Quote:
3. I'm staking my meshes in a STL list and want to be able to modify textures for each mesh in the stack. I managed putting up a copy constructor that gives me this behavior I want. My question is how will the smart pointer affect the copy constructor and do I need a copy constructor at all if I plan using a smart pointer?

You have a list of meshes. Where do you store the textures for each mesh? What does your copy constructor do?
I don't really follow what you're saying or what the problem is, but if you're asking about what copying a smart COM pointer does: It will typically increment the reference count of the underlying interface.

Share this post


Link to post
Share on other sites
Thanks for your replies

Quote:

You have a list of meshes. Where do you store the textures for each mesh? What does your copy constructor do?
I don't really follow what you're saying or what the problem is, but if you're asking about what copying a smart COM pointer does: It will typically increment the reference count of the underlying interface.


I'm loading a mesh with textures from HDD and then do 'push_back' to place it in the stack. I do this many times for same mesh. This way I get many instances of the mesh in the container. Then what I want to do is change a texture on a particular mesh instance. If I don't use a copy constructor the texture is being applied on all instances of same type.
If I use a copy constructor I can change the textures for each mesh individually.

Here's my mesh class:
function definition:

#include "MeshObject.h"
#include "FileUtil.h"
#include "../common/dhUtility.h"
#include "criptare.h"
#include "utility.h"


MeshObjMold::MeshObjMold()
{
m_dwNumMaterials = 0;
m_pMaterialsBuffer = NULL;
m_pMaterials = NULL;
m_pTextures = NULL;
m_pMesh = NULL;

Clone = false;
}


MeshObjMold::~MeshObjMold()
{
string Message;
string war = "WARNING: Mesh Object was not released. Mesh: ";
Message = war + Name;
if(Full)
{
Log(Message.c_str());
Log("\n");
}
}



HRESULT MeshObjMold::Initialise(string Id, char *szMeshFile, LPDIRECT3DDEVICE9 &pDevice)
{
Name = Id;
Full = true;
D3Ddev = pDevice;

HRESULT rslt = D3D_OK;




// First, load the .X file into a mesh object, and fill out the materials buffer
rslt = D3DXLoadMeshFromX(szMeshFile, D3DXMESH_MANAGED, pDevice, NULL,
&m_pMaterialsBuffer, NULL, &m_dwNumMaterials, &m_pMesh);
if(FAILED(rslt)) {
LogDX("Failed to load mesh from file",rslt);
}
// Next, get a pointer to the first element of data in the materials buffer, ready
// to read out.


D3DXMATERIAL* pMaterials = (D3DXMATERIAL*)m_pMaterialsBuffer->GetBufferPointer();

// Create 2 arrays, 1 for textures and 1 for materials. D3DXLoadMeshFromX puts the
// number of materials/textures (always the same) into m_dwNumMaterials above.
m_pMaterials = new D3DMATERIAL9[m_dwNumMaterials];
m_pTextures = new LPDIRECT3DTEXTURE9[m_dwNumMaterials];

// Next, iterate through the pMaterials buffer.
{
for(int iCount = 0; iCount < (int)m_dwNumMaterials; iCount++)
{
// For each material buffer element, copy the D3DMATERIAL into this class' array.
m_pMaterials[iCount] = pMaterials[iCount].MatD3D;


m_pMaterials[iCount].Ambient = m_pMaterials[iCount].Diffuse;


if (IsFileExist2(pMaterials[iCount].pTextureFilename)) // Check if there is a file under that name on disk and if yes make a texture out of it
{
if (DEBUG_V){Log("Loading texture from file: ");Log(pMaterials[iCount].pTextureFilename);Log("\n");}


//Decrypt the texture file
decrypt(pMaterials[iCount].pTextureFilename,50);


// Finally, create a texture from the file specified in the mateirals buffer and put
// it in the m_pTextures array.

rslt=D3DXCreateTextureFromFile(pDevice, pMaterials[iCount].pTextureFilename, &m_pTextures[iCount]);

if (DEBUG_V){if(FAILED(rslt)) {Log("\n");LogDX("Failed to load mesh texture from file",rslt); }}

//Encrypt the texture file
encrypt(pMaterials[iCount].pTextureFilename,50);

if (DEBUG_V)
{Log("Texture successifully loaded");Log("\n");}
}
else
{ if (DEBUG_V) //DEBUG_V code VV
{
Log("-------------------------------!ERROR!--------------------------------- \n");

if (pMaterials[iCount].pTextureFilename != NULL)
{
Log("Could not load file: ");
Log(pMaterials[iCount].pTextureFilename);
Log("\n");
Log("-------------------------------------------------------------------------\n");
}
if (pMaterials[iCount].pTextureFilename == NULL)
{
Log("Bad X file format ");
Log("\n");
Log("-------------------------------------------------------------------------\n");

}
}

//DEBUG_V code ^^




}





}
}

// m_pMaterialsBuffer is a COM object created by D3DXLoadMeshFromX. We only need it when
// reading the data from the X file - now that we've populated our mesh, materials and
// textures we can get rid of it.
m_pMaterialsBuffer->Release();
m_pMaterialsBuffer = NULL;

return D3D_OK;
}


MeshObjMold::MeshObjMold(const MeshObjMold &OrigM)
{
Clone =true; // Mark object as clone (this is required for the ShutDown -> the m_pMesh is not released for clones;
D3Ddev = OrigM.D3Ddev;
Name = OrigM.Name;

m_dwNumMaterials = OrigM.m_dwNumMaterials;





m_pMesh = OrigM.m_pMesh;



m_pMaterials = new D3DMATERIAL9[m_dwNumMaterials];
m_pTextures = new LPDIRECT3DTEXTURE9[m_dwNumMaterials];



for(int iCount = 0; iCount < (int)m_dwNumMaterials; iCount++)
{
m_pMaterials[iCount] = OrigM.m_pMaterials[iCount];
m_pTextures[iCount] = OrigM.m_pTextures[iCount];
}

Identity = OrigM.Identity;

}



HRESULT MeshObjMold::ShutDown()
{
Full = false;
Log("Shuting down ");
Log(Name.c_str());
Log("\n");




// Free the materials array
if(m_pMaterials)
delete [] m_pMaterials;

// If there are textures...
if(m_pTextures)
{
// ...index through the texture array & release the texture object
for(int iCount = 0; iCount < (int)m_dwNumMaterials; iCount++)
{
if(m_pTextures[iCount] != NULL)
{
m_pTextures[iCount]->Release();
m_pTextures[iCount] = NULL;
}
}

// Don't forget to delete the array as well
delete [] m_pTextures;
}

// Release the mesh object

if(m_pMesh)
{
m_pMesh->Release();
m_pMesh = NULL;
}


m_dwNumMaterials = 0;
m_pMaterialsBuffer = NULL;
m_pMaterials = NULL;



return D3D_OK;
}

HRESULT MeshObjMold::Render(LPDIRECT3DDEVICE9& pDevice)
{
HRESULT rslt = S_OK;

// Rendering a mesh is very easy. Simply iterate through each material...
{
for(int iCount = 0; iCount < (int)m_dwNumMaterials; iCount++)
{
//... set the material and texture in the normal way
rslt = D3Ddev->SetMaterial(&m_pMaterials[iCount]);
if(FAILED(rslt)) { LogDX("Set Material Failed",rslt);}

rslt = D3Ddev->SetTexture(0, m_pTextures[iCount]);
if(FAILED(rslt)) {LogDX("Set Texture Failed",rslt);}

//... then call ID3DXMesh::DrawSubset to draw the vertices in the mesh
// that have this material and texture applied to them.
rslt = m_pMesh->DrawSubset(iCount);
if(FAILED(rslt)) { LogDX("Draw Subset Failed",rslt);}
}

}

return S_OK;

}








class header file

#ifndef MESHOBJECT_H
#define MESHOBJECT_H

#include "utility.h"
#include <string>

using namespace std;

class MeshObjMold
{
private:
// The actual mesh object

LPD3DXBUFFER m_pMaterialsBuffer; // Receives the materials when the X file is loaded
D3DMATERIAL9* m_pMaterials; // Array of materials extracted from m_pMaterialsBuffer


DWORD m_dwNumMaterials; // The number of materials in this mesh
bool Clone;
bool Full;





public:
string Name,Identity;

LPDIRECT3DDEVICE9 D3Ddev;

LPDIRECT3DTEXTURE9* m_pTextures;// Array of textures extracted from m_pMaterialsBuffer

MeshObjMold( const MeshObjMold & m);

LPD3DXMESH m_pMesh;

MeshObjMold();

~MeshObjMold();

HRESULT Initialise(string Id,char* szMeshFile, LPDIRECT3DDEVICE9& pDevice);

HRESULT Render(LPDIRECT3DDEVICE9& pDevice);

HRESULT ShutDown();


};


#endif








To change a texture I do this:


list <ScnItm> ObjectList; // ScnItm inherits from MeshObjMold
ScnItm ColorO[5];

//some code


for (list <ScnItm>::iterator i = ObjectList.begin(); i != ObjectList.end(); ++i)
{
if (someting)
i->m_pTextures[0] = ColorO[0].m_pTextures[0];

}








[Edited by - Dan Caranfil on March 13, 2006 6:30:39 AM]

Share this post


Link to post
Share on other sites
Quote:
Original post by Dan Caranfil
I'm loading a mesh with textures from HDD and then do 'push_back' to place it in the stack. I do this many times for same mesh. This way I get many instances of the mesh in the container. Then what I want to do is change a texture on a particular mesh instance. If I don't use a copy constructor the texture is being applied on all instances of same type.
If I use a copy constructor I can change the textures for each mesh individually.


Well of-course, like i mentioned already all user-defined types get a copy constructor, assignment operator and destructor either implicitly or explicitly unless otherwise explicitly declared private/protected.

When implicitly defined (compiler generated) the copy constructor & assignment operator do a memberwise (or in somecases bitwise) copy/assignment for (smart) pointer members this is a shallow copy, a copy of address.

The standard library containers expect the contained type to at the very least be an assignable type, they do a copy of new elements.

So either you disable value/copy-semantics of your mesh class and store (smart) pointers into std::list or use boost::ptr_list or define what it means to copy/assign by explicily defining a copy constructor and assignment operator and destructor.

On a side note don't use C-style dynamic arrays use std::vector, simply no excuse here. Use constructor initializer lists, you're not doing initialization in your constructors, you're doing an assignment operation there not equivalent. Pass std::string by (constant) reference to avoid redundant copies. Try to use the standard library generic algorithms over explicict hand-written loops with containers (even for C-style arrays). Lastly don't dump entire namespaces into global namespace.

Share this post


Link to post
Share on other sites
Quote:
Original post by DigiDude
Quote:
Original post by Dan Caranfil2. Since I can't write a smart pointer myself yet, I plan using ATL CComPtr class. Do this instructions have to be followed in each ATL + DirectX project?
You could also try boost::shared_ptr


Not exactly the greatest idea, COM/COM like objects already have an embedded reference count so using boost::/std::tr1::shared_ptr just adds more, unnecessary overhead, in this case it's better to use boost::intrusive_ptr it's specifically designed to work with objects that already have an embedded reference count.

Share this post


Link to post
Share on other sites

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

Sign in to follow this