D3D9纹理内存释放问题

问题发生场景:用 CreateTexture 函数创建纹理,在纹理 Surface 上绘制图像,用完之后调用 Release 函数释放纹理,发现在多次创建纹理后内存暴增,texture->Release() 函数并未生效。

问题原因:在绘制图像时使用了 LPD3DXSPRITE, IDirect3DSurface9 2D绘制和渲染目标切换,但是没有释放这些绘制对象。

修改后基本代码如下:

#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }

HRESULT hr = S_FALSE;
// D3D文本绘制对象
LPD3DXFONT pFont = NULL;
// 2D绘制对象
LPD3DXSPRITE pSprite = NULL;
// 水印文本渲染目标纹理
LPDIRECT3DTEXTURE9 pRenderTexture = NULL;
// 水印文本渲染场景
IDirect3DSurface9* pRenderSurface = NULL;
// 原D3D渲染目标
IDirect3DSurface9 *pOldRenderTarget = NULL;

do 
{
	pFont = InitD3DFont(pDevice, conf->font_family, font_size);
	if (!pFont)
	{
		logerr("[TextureBuilder.cpp] d3d字体初始化失败");
		break;
	}

	hr = D3DXCreateSprite(pDevice, &pSprite);
	if (FAILED(hr))
	{
		logerr("[TextureBuilder.cpp] 创建D3D 2D绘制对象失败");
		break;
	}

	// 创建透明纹理,注意在设置参数的时候需要将usage设置为D3DUSAGE_RENDERTARGET,因为只有这样才能在纹理上渲染
	hr = pDevice->CreateTexture(
		t_width, t_height, 1, 
		D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8,
		D3DPOOL_DEFAULT, &pRenderTexture, NULL);
	if (FAILED(hr))
	{
		logerr("[TextureBuilder.cpp] 渲染目标纹理创建失败");
		break;
	}

	// 取得纹理对应的Surface,以便在其上绘制场景
	hr = pRenderTexture->GetSurfaceLevel(0, &pRenderSurface);
	if (FAILED(hr))
	{
		logerr("[TextureBuilder.cpp] 获取纹理表面失败");
		break;
	}

	// 向纹理的Surface中绘制场景,注意绘制时需要将纹理的表面设置为当前的RenderTarget,所以首先要保存原来的RenderTarget
	hr = pDevice->GetRenderTarget(0, &pOldRenderTarget);
	if (FAILED(hr))
	{
		logerr("[TextureBuilder.cpp] 获取原始渲染目标失败");
		break;
	}

	// 设置当前纹理表面为渲染目标
	hr = pDevice->SetRenderTarget(0, pRenderSurface);
	if (FAILED(hr))
	{
		logerr("[TextureBuilder.cpp] 设置新渲染目标失败");
		break;
	}

	hr = pDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_ARGB(0, 0, 0, 0), 1.0f, 0);
	if (FAILED(hr))
	{
		logerr("[TextureBuilder.cpp] D3D Clear 背景颜色错误");
		break;
	}

	// 开始绘制
	if (SUCCEEDED(pDevice->BeginScene()))
	{
		if (SUCCEEDED(pSprite->Begin(D3DXSPRITE_ALPHABLEND)))
		{
			// pSprite绘制,略
			pSprite->End();
		}
		pDevice->EndScene();
	}

	//D3DXSaveTextureToFile(L"texture.png", D3DXIFF_PNG, g_pRenderTexture, NULL);
	// 恢复原来的RenderTarget
	pDevice->SetRenderTarget(0, pOldRenderTarget);
} while (false);

// 必须在绘制完成后释放这些对象,否则造成内存泄露
SAFE_RELEASE(pFont);
SAFE_RELEASE(pSprite);
SAFE_RELEASE(pRenderSurface);
SAFE_RELEASE(pOldRenderTarget);

// 返回给外部使用
return pRenderTexture;

注意

调用 GetSurfaceLevel 函数获得的 pRenderSurface 对象需要手动释放,GetRenderTarget 函数获得的 pOldRenderTarget 亦然,否则只释放目标纹理是无效的。

posted @ 2022-02-18 14:38  jixhua  阅读(437)  评论(0编辑  收藏  举报