• Advertisement
Sign in to follow this  

Smart pointers in a DirectX game

This topic is 4333 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

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
Advertisement
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 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

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
Sign in to follow this  

  • Advertisement