Skip to main content
gamedev.net
🔒 Locked

Destroying objects

Started by Attala101 Mar 24, 2006 at 4:31 AM 11 replies 2k views
Original Post
Attala101
Attala101
I'm not sure if I should put this here or in the beginners forum since I'm a newvie. Well, here goes. I've been happily working away on my game implementing directx graphics. I have unfortunately discovered that my game has a serious memory leak. I ran Pix for Windows and discovered that a lot of directx object is not destroyed. Here's what it looks like: PIX My dxgraphics.h looks like:

#ifndef _DXGRAPHICS_H
#define _DXGRAPHICS_H

//function prototypes
int Init_Direct3D(HWND, int, int, int);
LPDIRECT3DSURFACE9 LoadSurface(char *, D3DCOLOR);
LPDIRECT3DTEXTURE9 LoadTexture(char *, D3DCOLOR);

//variable declarations
extern LPDIRECT3D9 d3d; 
extern LPDIRECT3DDEVICE9 d3ddev; 
extern LPD3DXSPRITE sprite_handler;

#endif

dxgraphics.cpp:

#include <d3d9.h>
#include <d3dx9.h>
#include "dxgraphics.h"
#include "game.h"

//variable declarations
LPDIRECT3D9 d3d = NULL;
LPDIRECT3DDEVICE9 d3ddev = NULL;
LPD3DXSPRITE sprite_handler = NULL;



int Init_Direct3D(HWND hwnd, int width, int height, int fullscreen)
{
    //initialize Direct3D
    d3d = Direct3DCreate9(D3D_SDK_VERSION);
    if (d3d == NULL)
    {
        MessageBox(hwnd, "Error initializing Direct3D", "Error", MB_OK);
        return 0;
    }

    //set Direct3D presentation parameters
    D3DPRESENT_PARAMETERS d3dpp; 
    ZeroMemory(&d3dpp, sizeof(d3dpp));

	if (FULLSCREEN)
	{d3dpp.Windowed = FALSE;}else{d3dpp.Windowed = TRUE;}
    d3dpp.SwapEffect = D3DSWAPEFFECT_COPY;
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
    d3dpp.BackBufferCount = 1;
    d3dpp.BackBufferWidth = SCREEN_WIDTH;
    d3dpp.BackBufferHeight = SCREEN_HEIGHT;
    d3dpp.hDeviceWindow = hwnd;
	d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;

    //create Direct3D device
    d3d->CreateDevice(
        D3DADAPTER_DEFAULT, 
        D3DDEVTYPE_HAL, 
        hwnd,
        D3DCREATE_HARDWARE_VERTEXPROCESSING,
        &d3dpp, 
        &d3ddev);

    if (d3ddev == NULL)
    {
        MessageBox(hwnd, "Error creating Direct3D device", "Error", MB_OK);
        return 0;
    }

    //clear the backbuffer to black
    d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f, 0);

    //create pointer to the back buffer
//    d3ddev->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &backbuffer);

	worldmap = new Spriteclass("worldmap.png", 0, 0, 1280, 1024, false);
	button_strike = new Spriteclass("Buttonwar.png", 120, 1024, 200, 100, false);
	button_build = new Spriteclass("Buttonbuild.png", 120, 1024, 200, 100, false);
	build_window = new Spriteclass("Buildbackground.png", 350, 10, 500, 500, false);
	inventory_window = new Spriteclass("Inventorybackground.png", 350, 390, 500, 500, false);
	spriteICBMbig = new Spriteclass("icbmbig.png", 300, 500, 25, 200, false);
	spriteICBMmedium = new Spriteclass("icbmmedium.png", 0, 0, 25, 200, false);
	spriteICBMsmall = new Spriteclass("icbmsmall.png", 0, 0, 25, 200, false);
	city = new Spriteclass("city.png", 0, 0, 100, 150, false);
	warheadbox = new Spriteclass("warheadbox.png", 0, 0, 200, 200, false);
	carrierbox = new Spriteclass("carrierbox.png", 0, 0, 200, 300, false);
	okbutton = new Spriteclass("okbutton.png", 600, 500, 200, 100, false);
	bullseye = new Spriteclass("bullseye.png", 0, 0, 200, 200, false);

	worldmap->init();
	button_strike->init();
	button_build->init();
	build_window->init();
	inventory_window->init();
	spriteICBMbig->init();
	spriteICBMmedium->init();
	spriteICBMsmall->init();
	city->init();
	warheadbox->init();
	carrierbox->init();
	okbutton->init();
	bullseye->init();

		
    return 1;

}

LPDIRECT3DSURFACE9 LoadSurface(char *filename, D3DCOLOR transcolor)
{
    LPDIRECT3DSURFACE9 image = NULL;
    D3DXIMAGE_INFO info;
    HRESULT result;
    
    //get width and height from bitmap file
    result = D3DXGetImageInfoFromFile(filename, &info);
    if (result != D3D_OK)
        return NULL;

    //create surface
    result = d3ddev->CreateOffscreenPlainSurface(
        info.Width,         //width of the surface
        info.Height,        //height of the surface
        D3DFMT_X8R8G8B8,    //surface format
        D3DPOOL_DEFAULT,    //memory pool to use
        ℑ,             //pointer to the surface
        NULL);              //reserved (always NULL)

    if (result != D3D_OK)
        return NULL;

    //load surface from file into newly created surface
    result = D3DXLoadSurfaceFromFile(
        image,              //destination surface
        NULL,               //destination palette
        NULL,               //destination rectangle
        filename,           //source filename
        NULL,               //source rectangle
        D3DX_DEFAULT,       //controls how image is filtered
        transcolor,         //for transparency (0 for none)
        NULL);              //source image info (usually NULL)

    //make sure file was loaded okay
    if (result != D3D_OK)
        return NULL;

    return image;
	
}


LPDIRECT3DTEXTURE9 LoadTexture(char *filename, D3DCOLOR transcolor)
{
    //the texture pointer
    LPDIRECT3DTEXTURE9 texture = NULL;

    //the struct for reading bitmap file info
    D3DXIMAGE_INFO info;

    //standard Windows return value
    HRESULT result;
    
    //get width and height from bitmap file
    result = D3DXGetImageInfoFromFile(filename, &info);
    if (result != D3D_OK)
        return NULL;

    //create the new texture by loading a bitmap image file
	D3DXCreateTextureFromFileEx( 
        d3ddev,              //Direct3D device object
        filename,            //bitmap filename
        info.Width,          //bitmap image width
        info.Height,         //bitmap image height
        1,                   //mip-map levels (1 for no chain)
        D3DPOOL_DEFAULT,     //the type of surface (standard)
        D3DFMT_UNKNOWN,      //surface format (default)
        D3DPOOL_DEFAULT,     //memory class for the texture
        D3DX_DEFAULT,        //image filter
        D3DX_DEFAULT,        //mip filter
        transcolor,          //color key for transparency
        &info,               //bitmap file info (from loaded file)
        NULL,                //color palette
        &texture );          //destination texture

    //make sure the bitmap texture was loaded correctly
    if (result != D3D_OK)
        return NULL;

    return texture;

}

I have a spriteclass which looks like: Spriteclass.h

#ifndef SPRITECLASS_H
#define SPRITECLASS_H

#include <d3d9.h>
#include <d3dx9.h>
#include <string>
#include "dxgraphics.h"
#include "game.h"



class Spriteclass
{
public:

	Spriteclass();
	~Spriteclass();
	Spriteclass( char *imageName, float, float, int, int, bool );
	
	void init( );
	void draw( );
	RECT GetPos(  );
	void SetPos( float, float );
	void buttonmove( bool, float);
	bool active;
	DWORD displaytime;
	float xposition;

private:

		//sprite structure
typedef struct {
    float x,y;
    int width,height;
    int movex,movey;
    int curframe,lastframe;
    int animdelay,animcount;
    int scalex, scaley;
    int rotation, rotaterate;
	DWORD spriteTickCount, movespeed;
} SPRITE;

	bool bAnimated;
	char *privImagename;

	LPDIRECT3DTEXTURE9 sprite_image;
	SPRITE thesprite;
	

	HRESULT result;

	
	
	
};

#endif // SPRITECLASS_H

The Spriteclass Init() function looks like:

void Spriteclass::init ( )
{
	char buf[2048];
   //create sprite handler object
    result = D3DXCreateSprite(d3ddev, &sprite_handler);
	if (FAILED(result))
{
   sprintf_s(buf, "Error: %s error description: %s\n",DXGetErrorString9(result),DXGetErrorDescription9(result));
   OutputDebugString(buf);
}

    if (FAILED(D3DXCreateSprite(d3ddev, &sprite_handler)))
       {
        MessageBox(NULL, buf, "Error", MB_OK);
        
    }

    //load texture with "pink" as the transparent color
    sprite_image = LoadTexture(privImagename, D3DCOLOR_XRGB(255,0,255));
    if (sprite_image == NULL)
	{
        MessageBox(NULL, "sprite_image error", "Error", MB_OK);
        
    }
}

Ok, the game runs and when it exits it calls the function game_end():

void Game_End(HWND hwnd)
{

  delete worldmap;
  delete button_strike;
  delete button_build;
  delete build_window;
  delete inventory_window;
  delete spriteICBMbig;
  delete spriteICBMmedium;
  delete spriteICBMsmall;
  delete bullseye;
  delete okbutton;

  int i;
  for (i=3; i > -1; i--)
  {
  delete nations;
  }
  delete city;
  delete warheadbox;
  delete carrierbox;

	
	if (sprite_handler != NULL)
        sprite_handler->Release();
	
	if (d3d != NULL)
        d3d->Release();

	if (d3ddev != NULL)
        d3ddev->Release();
	
}

According to my calculations everything should now be destroyed, but it's not. The debug output tells me:
Quote:
Direct3D9: :====> ENTER: DLLMAIN(00d5d6e0): Process Detach 00000ae4, tid=00000f30 Direct3D9: (INFO) :MemFini! Direct3D9: (WARN) :Memory still allocated! Alloc count = 208 Direct3D9: (WARN) :Current Process (pid) = 00000ae4 Direct3D9: (WARN) :Memory Address: 003a5060 lAllocID=1 dwSize=000047f8, (pid=00000ae4) Direct3D9: (WARN) : Stack Back Trace Direct3D9: (ERROR) : [0] : Address 00D5D4CB Direct3D9: (ERROR) : [1] : Address 00D5D59B Direct3D9: (ERROR) : [2] : Address 00D5D440 Direct3D9: (ERROR) : [3] : Address 00D51D44 Direct3D9: (ERROR) : [4] : Address 4FE4AF2E Direct3D9: (ERROR) : [5] : Address 00421377 Direct3D9: (ERROR) : [6] : Address 00423916 Direct3D9: (ERROR) : [7] : Address 00458C46 Direct3D9: (ERROR) : [8] : Address 004589AD Direct3D9: (ERROR) : [9] : Address 7C816D4F Direct3D9: (ERROR) : [10] : Address 00000000 Direct3D9: (ERROR) : [11] : Address 00000000 Direct3D9: (ERROR) : [12] : Address 00000000 Direct3D9: (ERROR) : [13] : Address 00000000 Direct3D9: (ERROR) : [14] : Address 00000000 Direct3D9: (ERROR) : [15] : Address 00000000
A lot of errors like the last ones, and finally:
Quote:
Direct3D9: (WARN) :Total Memory Unfreed From Current Process = 400878 bytes Direct3D9: :====> EXIT: DLLMAIN(00d5d6e0): Process Detach 00000ae4
Can anyone help me point out what is not being released/destroyed?
kovacsp
kovacsp
Hi,

the code you posted looks quite incomplete for me, for example, you included SpriteClass::Init() but didn;t include the destruction code, which should do the Release(), which is important if you are looking for something that is not released. You also included LoadSurface, which is used for nothing.

In general: you should use the "Break on AllocID" feature from the debug version of DX (find it on the control panel). Paste the largest allocID that is written to the output, and it should break right at the spot where you create the leaked object.
Note: you can use SAFE_RELEASE instead of if (xxx!=NULL) xxx->release. It's a macro doing the same.
If you do it manually, then find the appropriate Release() for every ..Create..() function that creates some DX object.

EDIT: have a look at this

kp
------------------------------------------------------------Neo, the Matrix should be 16-byte aligned for better performance!
Evil Steve
Evil Steve
If you put the debug output level to maximum, it'll give you a list of allocations. Then you can use the debug control pannel to set "Break on alloc ID", which you should set for the highest ID (Which is the last object you create).
The errors can be a bit misleading sometimes, since if one texture isn't released, then the device isn't released because it's refcount is increased, the D3D class isn't released, and the backbuffer, frontbuffer, z-buffer, etc aren't freed either.
One thing to check is that you're Release()ing both the ID3DXSprite instance and your texture when you free your sprite class (In the destructor would be a good place).
You could also try using a memory manager to log memory leaks in your app. If one Sprite class isn't being delete'd, then you won't be Release()ing the associated resources.
Attala101
Attala101
Thanks for your replies.

The destructor in the Spriteclass looks like this:
Spriteclass::~Spriteclass(){	     if (sprite_image != NULL)        sprite_image->Release();}


From your replies I'm guessing this is not suficcient. As far as I can tell I'm releasing the texture but not the ID3DXSprite instance. Not really sure how to do that.

I tried that "Break on AllocID" feature. I understand now how to find out which objects that are not destroyed, (I think). On the other hand, If I say break on AllocID 1 it points to the line d3d = Direct3DCreate9(D3D_SDK_VERSION); in dxgraphics.cpp. But the object d3d is being released in the game_end() function, is it not?
Evil Steve
Evil Steve
You need to store sprite_handler in your Sprite class somewhere so you can call sprite_handler->Release() in your sprite's destructor. An ID3DXSprite is a DirectX object that needs to be freed.
Also, you only really need one instance of it (So you could make it a static variable if you wish).

Quote:
I tried that "Break on AllocID" feature. I understand now how to find out which objects that are not destroyed, (I think). On the other hand, If I say break on AllocID 1 it points to the line d3d = Direct3DCreate9(D3D_SDK_VERSION); in dxgraphics.cpp. But the object d3d is being released in the game_end() function, is it not?
It tells you where the object was created. That way you can tell if object 42 is a ID3DXSprite, a IDirect3DTexture9 or whatnot. That allows you to keep an eye on the object so you can see where it's not being released.
You should only do it for your highest allocation ID, since using it on ID #1 (the device) won't help. You know that the device isn't being freed, and that's because something else is still referencing it. You need to find out what's referencing it (Which looks like the ID3DSprite).
Attala101
Attala101
Ok, I made the sprite_handler a private object of Spriteclass. Then I Release() it in its destructor. The memory leak is still a big as before though.

Then I AllocID the highest ID number and then it points to this line in my Spriteclass draw() function: sprite_handler->Begin(D3DXSPRITE_ALPHABLEND);

So it still looks like the sprite handler is causing the problem...

Hmmm...
Evil Steve
Evil Steve
I can't see how that is, since ID3DXSprite::Begin() doesn't actually allocate anything. You are matching it with a call to ID3DXSprite::End(), right? What does your render function look like? Are you allocating anything in it? What happens if you don't render anything, just setup and shutdown? If you still get the same leaks, then it's something else, probably something you're creating in init() but not releasing in the destructor.
Do you ever call any Get*() functions on a D3D or D3DDevice interface that returns another interface? Rememer you need to Release() those too.
Attala101
Attala101
My renderfunction looks like this:
void Spriteclass::draw(  ){            //start sprite handler            sprite_handler->Begin(D3DXSPRITE_ALPHABLEND);		              //create vector to update sprite position	        D3DXVECTOR3 position((float)thesprite.x, (float)thesprite.y, 0);            //configure the rect for the source tile            RECT srcRect;			if (bAnimated == true)			{            int columns = 8;            srcRect.left = (thesprite.curframe % columns) * thesprite.width;            srcRect.top = (thesprite.curframe / columns) * thesprite.height;            srcRect.right = srcRect.left + thesprite.width;	        srcRect.bottom = srcRect.top + thesprite.height;			}else 			{				srcRect.left = 0;				srcRect.top = 0;				srcRect.right = thesprite.width;				srcRect.bottom = thesprite.height;			}            //draw the sprite            sprite_handler->Draw(                sprite_image,                 &srcRect,                NULL,                &position,                D3DCOLOR_XRGB(255,255,255));                        //stop drawing			sprite_handler->End();	}


I tried the game without rendering something and the memory leak persists.

Is it worth mentioning that the AllocIDs get higher and higher the longer the game runs?



Adriano
Adriano
You can set the BreakOnAllocID field of your registry key :
\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Direct3D
to the ids written as IAllocID of the output:
Direct3D9: (WARN) :Memory Address: 003a5060 lAllocID=1 dwSize=000047f8, (pid=00000ae4)
to try find out what causes the leak. However, this is slightly a rough method and might not be easy to use to solve your problem.
(For example if you have, let's say, a sprite interface unreleased it does happen that this method breaks on allocation of the device that created the sprite)

Attala101
Attala101
I hope it's ok to continue this thread as I'm still struggling with this.

I have been hard at work trying to figure this out, but my labour has so far been fruitless. I now strongly suspect there is something wrong with my spriteclass.

As you can see from the Pix-output, (link to image in my first post), there are objects created for each sprite I'm initializing. Some of them are never destroyed, which leads me to the inevitable truth that there is something wrong with my spriteclass.

Ok, I have made some small progress. Here's the output from the earlier link:

Image 1

After getting advice here I put sprite_handler->Release(); in my spriteclass destructor. Now the output says:

Image 2

So, now I have a D3D Vertex Declaration which is not released for each of my sprites. ...and this is where a newbie gets lost.

My spriteclass now looks like this:

Spriteclass.h
#ifndef SPRITECLASS_H#define SPRITECLASS_H#include <d3d9.h>#include <d3dx9.h>#include <string>#include "dxgraphics.h"#include "game.h"class Spriteclass{public:	Spriteclass();	 ~Spriteclass();	Spriteclass( char *imageName, float, float, int, int, bool );		void init( );	void draw( );	RECT GetPos(  );	void SetPos( float, float );	void buttonmove( bool, float);	bool active;	DWORD displaytime;	float xposition;private:		//sprite structuretypedef struct {    float x,y;    int width,height;    int movex,movey;    int curframe,lastframe;    int animdelay,animcount;    int scalex, scaley;    int rotation, rotaterate;	DWORD spriteTickCount, movespeed;} SPRITE;	bool bAnimated;	char *privImagename;	LPDIRECT3DTEXTURE9 sprite_image;	SPRITE thesprite;	LPD3DXSPRITE sprite_handler;		HRESULT result;			};#endif // SPRITECLASS_H


Spriteclass.cpp
#include <d3d9.h>#include <d3dx9.h>#include "Spriteclass.h"#include "dxgraphics.h"#include <string>#include <dxerr9.h>Spriteclass::Spriteclass( char *imageName, float posx, float posy, int spritewidth, int spriteheight, bool animated ){		privImagename = imageName;       	bAnimated = animated;	active = false;	displaytime = 0;	xposition = 0;    //initialize the sprite's properties    thesprite.x = posx;    thesprite.y = posy;    thesprite.width = spritewidth;    thesprite.height = spriteheight;    thesprite.curframe = 1;    thesprite.lastframe = 11;    thesprite.animdelay = 2;    thesprite.animcount = 0;    thesprite.movex = 1;    thesprite.movey = 1;	thesprite.movespeed = 10;	thesprite.spriteTickCount = 0;	   }Spriteclass::~Spriteclass(){   	        sprite_handler->Release();	        sprite_image->Release();         }void Spriteclass::init ( ){		char buf[2048];   //create sprite handler object    result = D3DXCreateSprite(d3ddev, &sprite_handler);	if (FAILED(result)){      sprintf_s(buf, "Error: %s error description: %s\n",DXGetErrorString9(result),DXGetErrorDescription9(result));   OutputDebugString(buf);}    if (FAILED(D3DXCreateSprite(d3ddev, &sprite_handler)))       {        MessageBox(NULL, buf, "Error", MB_OK);            }    //load texture with "pink" as the transparent color    sprite_image = LoadTexture(privImagename, D3DCOLOR_XRGB(255,0,255));    if (sprite_image == NULL)	{        MessageBox(NULL, "sprite_image error", "Error", MB_OK);            }}void Spriteclass::draw(  ){            //start sprite handler            sprite_handler->Begin(D3DXSPRITE_ALPHABLEND);		              //create vector to update sprite position	        D3DXVECTOR3 position((float)thesprite.x, (float)thesprite.y, 0);            //configure the rect for the source tile            RECT srcRect;			if (bAnimated == true)			{            int columns = 8;            srcRect.left = (thesprite.curframe % columns) * thesprite.width;            srcRect.top = (thesprite.curframe / columns) * thesprite.height;            srcRect.right = srcRect.left + thesprite.width;	        srcRect.bottom = srcRect.top + thesprite.height;			}else 			{				srcRect.left = 0;				srcRect.top = 0;				srcRect.right = thesprite.width;				srcRect.bottom = thesprite.height;			}            //draw the sprite            sprite_handler->Draw(                sprite_image,                 &srcRect,                NULL,                &position,                D3DCOLOR_XRGB(255,255,255));                        //stop drawing			sprite_handler->End();	}RECT Spriteclass::GetPos(  ){	RECT rekt;	rekt.left = thesprite.x;	rekt.top = thesprite.y;	rekt.right = thesprite.x + thesprite.width;	rekt.bottom = thesprite.y + thesprite.height;	return rekt;}void Spriteclass::SetPos( float x, float y ){	thesprite.x = x;	thesprite.y = y;}


I really appreciate all the help I can get here as I am really stuck :)

A
Evil Steve
Evil Steve
1. You're allocating sprite_handler twice here:
    result = D3DXCreateSprite(d3ddev, &sprite_handler);	if (FAILED(result)){      sprintf_s(buf, "Error: %s error description: %s\n",DXGetErrorString9(result),DXGetErrorDescription9(result));   OutputDebugString(buf);}    if (FAILED(D3DXCreateSprite(d3ddev, &sprite_handler)))

The second call to D3DXCreateSprite will overwrite the first version of sprite_handler, and you won't be able to release it. Hence the memory leak.

2. You shouldn't be calling Release() in your destructor without first checking that the object isn't NULL. If D3DXCreateSprite fails, then sprite_handler will be NULL, and your app will crash in the destructor. You should change your destructor to:
Spriteclass::~Spriteclass(){   if(sprite_handler)      sprite_handler->Release();   sprite_handler = NULL;   if(sprite_image)      sprite_image->Release();   sprite_image = NULL;}

Setting the interfaces to NULL isn't really nessecary in the destructor, but it's a good habit to get into; particularly if you were to create a Release() or Shutdown() function that did the cleanup for you.
Attala101
Attala101
...and a big warm thanks goes out to you!

The memory leak dissapeared completely, and I'm a happy coder again. (A bit disappointed I didn't see the error myself though).

A
Evil Steve
Evil Steve
Quote:
Original post by Attala101
The memory leak dissapeared completely, and I'm a happy coder again. (A bit disappointed I didn't see the error myself though).
Heh, I've done similar things before. Sometimes it helps to have a fresh pair of eyes go over it.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.