Dealing with Direct3D9 Memory leaks....(this is an annoying topic for me)

Started by
5 comments, last by cozzie 10 years, 7 months ago

OK, I will try to give as much info as I can to this problem. Hopefully people will be able to follow my logic (I'm not saying it's too complicated for you to understand, I'm saying that I don't always explain my self exactly as I should and that causes headaches for both parties...(or multiple parties seeing as there's one of me and about a million of you...) Anyway, on to the problem at hand:

What I'm basically doing is this:

(I'm going to start with the classes I think are more important such as the Device class.)

DemDeviceD3D9.h


	class DemDeviceD3D9
	{
	private:
		IDirect3D9* m_D3D9;	// this is the "mother" of Direct3D
		IDirect3DDevice9* m_Device9; // this is the actual device.
		DemDisplayAdapter* m_displayAdapter; // the display adapter.
	public:
		DemDeviceD3D9();	// default constructor.
		
		~DemDeviceD3D9();	// destructor.

		// initialize the device.
		bool init_device(); // this acquire Direct3D9 and makes sure all the files exist.


		// get total adapter count.
		unsigned int getAdaptorCount();

		// check if the current format it supported.
		bool isFormatSupported(DEM_IMAGE_FORMATS imFmt);
		// get the device name.
		char* getDeviceName() const;
		// get the adapter description.
		char* getAdapterDriver() const;

		char* getDeviceDescription() const;
		// return Direct3D9 interface.
		IDirect3D9* getD3D9() const { return m_D3D9; }

		// return the device.
		IDirect3DDevice9* getDirect3DDevice9() const { return m_Device9; }

		DisplayModes getDisplayModes() { return m_displayAdapter->getDisplayModes(); }

OK (now you are wondering about the display adapter class? that is coming. the device first.

DemDevinceD3D9.cpp


	DemDeviceD3D9::DemDeviceD3D9()
	{
		this->m_D3D9 = NULL;
		this->m_Device9 = NULL;
		this->m_displayAdapter = new DemDisplayAdapter(*m_D3D9,*m_Device9);
	}

/*	D3DFORMAT DemDeviceD3D9::convertDIFTOD3DFMT(DEM_IMAGE_FORMATS form)
	{
		switch(form)
		{
		case DIF_UNKNOWN:
			return D3DFMT_UNKNOWN;
			break;
		case DIF_A8R8G8B8:
			return D3DFMT_A8R8G8B8;
			break;
		case DIF_X8R8G8B8:
			return D3DFMT_X8B8G8R8;
			break;
		}
		return D3DFMT_UNKNOWN;
	}
	DEM_IMAGE_FORMATS DemDeviceD3D9::convertD3DFMTToDIF(D3DFORMAT form)
	{
		// just these formats for now.
		switch(form)
		{
		case D3DFMT_UNKNOWN:
			return DIF_UNKNOWN; // unknown format;
			break;
		case D3DFMT_A8R8G8B8:
			return DIF_A8R8G8B8; // return an unsigned format (32 bit).
			break;
		case D3DFMT_X8B8G8R8:
			return DIF_X8R8G8B8; // 32-bit channels but not alpha.
			break;
		case D3DFMT_R5G6B5:
			return DIF_R5G6B5;
			break;
		}
		return D3DFMT_UNKNOWN;
	}*/ 
	DemDeviceD3D9::~DemDeviceD3D9()
	{
		if(m_Device9 != NULL)
		{
			m_Device9->Release();
			m_Device9 = 0;
		}

		if(m_D3D9 != NULL)
		{
			m_D3D9->Release();
			m_D3D9 = 0;
		}
	}

	unsigned int DemDeviceD3D9::getAdaptorCount()
	{
		unsigned int count = 0;
		if(m_D3D9)
		{
			count = m_D3D9->GetAdapterCount();
		}
		else
		{
			count = -1;
		}
		return count;
	}

	char* DemDeviceD3D9::getDeviceName() const
	{
		return m_displayAdapter->getDeviceName();
	//	return m_Adapter->DeviceName;
	}

	char* DemDeviceD3D9::getAdapterDriver() const
	{
		return "";	
	//	return m_Adapter->Driver;
	}


	bool DemDeviceD3D9::init_device()
	{
		// create Direct3D9
		m_D3D9 = Direct3DCreate9(D3D_SDK_VERSION);

		if(m_D3D9 == NULL)
		{
			MessageBoxA(NULL,"Direct3D9 not initialized.....sorry",NULL,MB_OK);
			return false;

		}
		else
		{
			return true;
		}
	
		return false;
	}

OK, now for the display adapter.

DemDisplayAdapter.h


typedef std::vector<DemDisplayMode*> DisplayModes;
	// class for handling a display adapter. 
	class DemDisplayAdapter
	{
	private:
		IDirect3D9* m_direct3D9;	// Direct3D 9
		IDirect3DDevice9* m_D3DDev9;
		D3DDISPLAYMODE m_displayMode;	// this is the display mode used.
		D3DADAPTER_IDENTIFIER9* m_Adapter;	// the adapter.
		DemDisplayMode* m_displayModes;
		DEM_IMAGE_FORMATS convertD3DFMTToDIF(D3DFORMAT form);
		D3DFORMAT convertDIFTOD3DFMT(DEM_IMAGE_FORMATS form);
	public:
		// constructor.
		DemDisplayAdapter(IDirect3D9& d3d9,
						IDirect3DDevice9& dev9);

		~DemDisplayAdapter();


		// check if the current format it supported.
		bool isFormatSupported(DEM_IMAGE_FORMATS imFmt);

		// get a list of all supported display modes.
		DisplayModes getDisplayModes();

		char* getDeviceName() const {
			if(m_direct3D9 != NULL)
			{
				return m_Adapter->DeviceName;
			}
			else
			{
				return " ";
			}
			return " ";
		}
	};

the DemDisplayMode is just a helper class, it has nothing to do with Direct3D9.

DemDisplayAdapter.cpp


	DemDisplayAdapter::DemDisplayAdapter(IDirect3D9& d3d9, IDirect3DDevice9& dev9)
	{
		m_direct3D9 = &d3d9;
		m_D3DDev9 = &dev9;
		if(m_direct3D9 != NULL)
		{
			m_direct3D9->GetAdapterDisplayMode(m_direct3D9->GetAdapterCount(),&m_displayMode);
		}
	}

	DemDisplayAdapter::~DemDisplayAdapter()
	{
		if(m_D3DDev9 != NULL)
		{
			m_D3DDev9->Release();
			m_D3DDev9 = 0;
		}

		if (m_direct3D9 != NULL)
		{
			m_direct3D9->Release();
			m_direct3D9 = 0;
		}
	}

	// get the list of display modes.
	DisplayModes DemDisplayAdapter::getDisplayModes()
	{
		DisplayModes modes; 
		if(m_direct3D9 != NULL)
		{
			for(unsigned int i = 0; i < m_direct3D9->GetAdapterCount(); i++)
			{
				unsigned int mode = m_direct3D9->GetAdapterModeCount(i,D3DFMT_A8R8G8B8);
				for(unsigned int x = 0; x < mode; x++)
				{
					m_direct3D9->EnumAdapterModes(i,D3DFMT_A8R8G8B8,x,&m_displayMode);
					m_displayModes = new DemDisplayMode(m_displayMode.Width,m_displayMode.Height,m_displayMode.RefreshRate);
					modes.push_back(m_displayModes);
				}
			}
			
			return modes;
		}
		return modes;
	}

now.....for the render window.......

DemRenderWindowD3D9.h


	class DemRenderWindowD3D9 : public DemRenderWindow
	{
	private:
		DemDeviceD3D9* m_demDevice;
	public:
		DemRenderWindowD3D9(DemString winName,
			DemString winTitle,
			DemUInt width,
			DemUInt height,
			bool fullScreen,
			bool Stencil,
			bool VSync,
			DemUInt vSyncVal = 0,
			DemUInt bits = 32,
			DemUInt MultiSample = 0,
			DWORD Quality = 0);

		DemRenderWindowD3D9();
		~DemRenderWindowD3D9();

		bool createWindow(DemString winName, DemString winTitle, DemUInt winWidth, DemUInt winHeight, 
			bool fullscreen,
			bool Stencil,
			bool vSync,
			ExtraParameters& params );

		void _beginRender();

		void _endRender();

		void _setClearColor(float a, float r, float g, float b);
	};

OK, I hope someone is seeing a pattern here, if not, don't worry I'll explain the problem I'm having and WHY I did things this way.

DemRenderWindowD3D9.cpp


	DemRenderWindowD3D9::DemRenderWindowD3D9(DemString winName, DemString winTitle, DemUInt width, DemUInt height, bool fullScreen, bool Stencil, bool VSync, DemUInt vSyncVal /* = 0 */, DemUInt bits /* = 32 */, DemUInt MultiSample /* = 0 */, DWORD Quality /* = 0 */)
		: DemRenderWindow(winName,winTitle,width,height,fullScreen,Stencil,VSync,vSyncVal,bits,MultiSample,Quality)
	{
		this->m_demDevice = new DemDeviceD3D9();
	}

	DemRenderWindowD3D9::DemRenderWindowD3D9()
		: DemRenderWindow()
	{
		this->m_demDevice = new DemDeviceD3D9();
	}
	DemRenderWindowD3D9::~DemRenderWindowD3D9()
	{
		IDirect3D9* d = m_demDevice->getD3D9();
		IDirect3DDevice9* dev = m_demDevice->getDirect3DDevice9();
		
		if(dev != NULL)
		{
			dev->Release();
			dev = 0;
		}

		if(d != NULL)
		{
			d->Release();
			d = 0;
		}
		delete m_demDevice;
		m_demDevice = 0;
	}

	bool DemRenderWindowD3D9::createWindow(DemString winName, DemString winTitle, DemUInt winWidth, DemUInt winHeight, 
		bool fullscreen,
		bool Stencil,
		bool vSync,
		ExtraParameters& params )
	{
		this->createWindowImp(winName,winTitle,winWidth,winHeight,params);

		
			if(m_demDevice->init_device())
			{
				IDirect3DDevice9* dev = m_demDevice->getDirect3DDevice9();
				D3DPRESENT_PARAMETERS present_params;
				ZeroMemory(&present_params,sizeof(present_params));
				present_params.BackBufferCount = 3;
				present_params.BackBufferFormat = D3DFMT_X8R8G8B8;
				present_params.SwapEffect = D3DSWAPEFFECT_DISCARD;
				
				present_params.BackBufferWidth = m_width;
				present_params.BackBufferHeight = m_height;
				present_params.Windowed        = (!fullscreen);
				present_params.hDeviceWindow = m_winHandle;

				HRESULT hr = m_demDevice->getD3D9()->CreateDevice(0,
					D3DDEVTYPE_HAL,
					m_winHandle,
					D3DCREATE_HARDWARE_VERTEXPROCESSING,
					&present_params,
					&dev);
				

				if(FAILED(hr))
				{
					const char* error = DXGetErrorDescriptionA(hr);

					char buffer[256];
					sprintf(buffer,"%s",error);
					MessageBoxA(NULL,buffer,NULL,MB_OK);
					return false;
				}
				else
				{
					return true;
				}
			//	dev->Release();
			}
			else
			{

			}
			
		return false;
	}

	void DemRenderWindowD3D9::_beginRender()
	{
		if(m_demDevice->getDirect3DDevice9())
		{

			HRESULT hr = m_demDevice->getDirect3DDevice9()->BeginScene();

			if(FAILED(hr))
			{
				const char* err = DXGetErrorDescriptionA(hr);
				MessageBoxA(NULL,err,"",MB_OK);
			}
		}
	}

	void DemRenderWindowD3D9::_endRender()
	{
		if(m_demDevice->getDirect3DDevice9() )
		{
			m_demDevice->getDirect3DDevice9()->EndScene();
			m_demDevice->getDirect3DDevice9()->Present(0,0,0,0);
		}
	}

	void DemRenderWindowD3D9::_setClearColor(float a, float r, float g, float b)
	{
		
		if(m_demDevice->getDirect3DDevice9())
		{
			m_demDevice->getDirect3DDevice9()->Clear(0,0,D3DCLEAR_TARGET,D3DCOLOR_ARGB(255,0,0,255),1.0f,1);
		}
	}

OK, now, you see how in every destructer I'm releasing Direct3D9? but I'm still getting Direct3D memory leaks, especially at Alloc count = 113.

Now, as you'll see from the main function that I am releasing the data by deleting the classes that are releasing the Direct3D Interfaces.....(I can't get anything to render but that is probably because there is a problem in createWindowImp()....(I was trying for an Ogre3D approach with the extra parameters but that didn't quite work out....I'll fix that after I manage to get rid of these stupid memory leaks....) *sigh* I don't suppose anyone has a very, very,very good tutorial on IUnknown interfaces right? One that will guarantee the memory is ALWAYS released?) Ok, I've ranted enough. This has to be my longest post ever....and I'm off on a tangent....bye.

Advertisement

Direct3D9 memory leaks will be reported if you fail to release any other resource created by this device/d3d-instance. You create a texture and don't release it? Leak. So first of all, check that you do not allocate any other resources whatsoever. If this still leaks, use the debugger to step through the code. Does it actually enter into any of those destructors? You could have missed a virtual destructor declaration, especially if you are using lots of interfaces/base classes. Note too that this is really ugly design here, having 3-4 destructors releasing the same resources, I hope you are just doing that for debug purposes.

You are saying it leaks on alloc count 113. Did you check what exactly that is? Open the directx control pannel, and activate "break on alloc id", enter that id.

One thing you could try, if everything else fails, is the CComPtr, which is a smart-pointer for com-interfaces, that will automatically clean up the com-devices. Thats probably what you want when you demand on memory being ALWAYS released. Note though that there are some functions that increment the allocation count, e.g. on textures if you bind them as render target (don't know if exactly that is true, take it as an example), so you'll probably have to do some manual releasing anyway.

Alloc 113 points to this:

http://puu.sh/4fWvq.png

for some reason, I cannot add this image regularly so I had to do it this way, so.....I'm not sure WHY it goes to that since it is being released. I always have trouble releasing these damn interfaces.....


for some reason, I cannot add this image regularly so I had to do it this way, so.....I'm not sure WHY it goes to that since it is being released. I always have trouble releasing these damn interfaces.....

Yeah, there is some other resource not being released, the DirectX9-class will leak if some other resource isn't properly released. Check everything you create related to the device - textures, surfaces, depth stencils, vertex/index buffer, DX-classes... if that doesn't help, use PIX to see what resources aren't being destroyed properly. After capturing a frame with F12 in PIX, you'll see a "destructed in frame"-column in the resource table, if there is a "never", then you'll have your leak (exclude dx9 and device for stated reasons).

thanks, but before I do this, there's a problem I'm having.........here it is....

http://puu.sh/4fY5k.jpg

now, this happens because of this....:


bool DemRenderWindowD3D9::createWindow(DemString winName, DemString winTitle, DemUInt winWidth, DemUInt winHeight, 
		bool fullscreen,
		bool Stencil,
		bool vSync,
		ExtraParameters& params )
	{
		this->createWindowImp(winName,winTitle,winWidth,winHeight,params);
		
			m_demDevice->init_device();
			
				IDirect3DDevice9* dev = m_demDevice->getDirect3DDevice9();
				D3DPRESENT_PARAMETERS present_params;
				ZeroMemory(&present_params,sizeof(present_params));
				present_params.BackBufferCount = 3;
				present_params.BackBufferFormat = D3DFMT_X8R8G8B8;
				present_params.SwapEffect = D3DSWAPEFFECT_DISCARD;
				
				present_params.BackBufferWidth = m_width;
				present_params.BackBufferHeight = m_height;
				present_params.Windowed        = (!fullscreen);
				present_params.hDeviceWindow =this->getWindowHandle();

				HRESULT hr = m_demDevice->getD3D9()->CreateDevice(0,
					D3DDEVTYPE_HAL,
					this->getWindowHandle(),
					D3DCREATE_HARDWARE_VERTEXPROCESSING,
					&present_params,
					&dev);
				

				if(FAILED(hr))
				{
					const char* error = DXGetErrorDescriptionA(hr);

					char buffer[256];
					sprintf(buffer,"%s",error);
					MessageBoxA(NULL,buffer,NULL,MB_OK);
					return false;
				}
				else
				{
					return true;
				}
			//	dev->Release();
		
			
		return false;
	}

and here is the createWindowImp:


bool DemWindow::createWindowImp(DemString winName, DemString winTitle, DemUInt winWidth, DemUInt winHeight,ExtraParameters& params)
	{
		ExtraParameters ext = params;
		m_windowName = winName;	// the name of the window.
		m_title = winTitle; // the title of the window.
		m_width = winWidth;	// the width of the window./
		m_height = winHeight;	// the height of the window.
		
		// variables that will appear in the ExtraParameters.
		bool fullScreen = false;			// full screen. (used for renderer.)
		bool Stencil	= false;			// this is for setting the stencil buffer.
		bool VSync		= false;			// this is for setting the VSync.
		DemUInt	numSamples = 0;				// the number of samples to use.
		DemUInt bits = 24;					// the number of bits to use (default: 24).
		
		DemString strExtraOne = " ";   // first value of the map.
		DemString strExtraTwo = " ";   // the second value of the map.
		for(ExtraParameters::iterator i = ext.begin(); i != ext.end(); i++)
		{
			strExtraOne = i->first;
			strExtraTwo = i->second;
		}

		if(strExtraOne == "Message")
		{
			MessageBox(NULL,strExtraTwo.c_str(),NULL,MB_OK);
		}
		if(strExtraOne == "full_screen"){
			fullScreen = DemStringConverter::parseBoolean(strExtraTwo);
		}
		if(strExtraOne == "bits"){
			bits = (DemUInt)DemStringConverter::toInt(strExtraTwo);
		}
		
		WNDCLASSEX winClass;
		ZeroMemory(&winClass,sizeof(winClass));

		winClass.cbSize = sizeof(winClass);
		winClass.style  = CS_HREDRAW | CS_VREDRAW;
		winClass.lpfnWndProc = &DemWindowUtility::getInstance()->WinProc;
		winClass.hInstance = GetModuleHandle(NULL);
		winClass.hIcon     = LoadIcon(NULL,IDI_APPLICATION);
		winClass.hCursor   = LoadCursor(NULL,IDC_ARROW);
	//	winClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
		winClass.lpszMenuName = NULL;
		winClass.lpszClassName = m_windowName.c_str();

		if (!RegisterClassEx(&winClass))
		{
			MessageBoxA(NULL,"Window not created.","",MB_OK);
			return false;
		}

		DWORD dwStyle,
			  dwExStyle;

		RECT windowRect;
		windowRect.left = (LONG)0;
		windowRect.top = (LONG)0;
		windowRect.right = (LONG)m_width;
		windowRect.bottom = (LONG)m_height;

		if(fullScreen)
		{
			DEVMODE devMode;
			memset(&devMode,0,sizeof(devMode));
			devMode.dmSize         = sizeof(devMode);
			devMode.dmPanningWidth = this->m_width;
			devMode.dmPanningHeight = this->m_height;
			devMode.dmBitsPerPel    = bits;
			devMode.dmFields        = DM_PANNINGWIDTH | DM_PANNINGHEIGHT | DM_BITSPERPEL;

			// change the display settings.
			if(ChangeDisplaySettings(&devMode,CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
			{
				fullScreen = false;
				return false;
			}
			dwStyle = WS_POPUP;
			dwExStyle = WS_EX_APPWINDOW;
			ShowCursor(!fullScreen);
		}
		else
		{
			dwStyle = WS_OVERLAPPEDWINDOW;
			dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
		}
		AdjustWindowRectEx(&windowRect,dwStyle,NULL,dwExStyle);
		
		this->m_winHandle = 
			CreateWindowEx(dwExStyle,
			m_windowName.c_str(),
			m_title.c_str(),
			dwStyle,
			0,
			0,
			windowRect.right - windowRect.left,
			windowRect.bottom - windowRect.top,
			NULL,
			NULL,
			GetModuleHandle(NULL),
			NULL);

		if(m_winHandle)
		{
			ShowWindow(m_winHandle,SW_SHOW);
			UpdateWindow(m_winHandle);
			//updateWindow(m_winHandle);
			// look into this - WINDOWINFO;
		
		}

		return true;
	}

I suppose I could just put the window creation again in the render window but I was trying to make it so I could write the window creation code once, then use it across renderers.

Regarding the leaks, remember to unset textures, vertexbuffers etc. before you release them (and the device)

Fruny: Ftagn! Ia! Ia! std::time_put_byname! Mglui naflftagn std::codecvt eY'ha-nthlei!,char,mbstate_t>

You could try to make a new application with just the d3d calls in 1 function, without the wrappers etc.

This way you have your d3d calls clear and straight forward to see where it's 'leaking'

(assuming there's no 'full' engine with a scene, textures, shaders, meshes etc. around the code above :))

Crealysm game & engine development: http://www.crealysm.com

Looking for a passionate, disciplined and structured producer? PM me

This topic is closed to new replies.

Advertisement