关于SetTexture()调用引起显存泄露的问题

  项目中遇到一个问题,添加了GPU特技后,播放多次就会出现申请显存失败的问题,并且使用GPU-Z查看显存,也是在不断增加,于是断定是由于显存泄露造成的,仔细检查代码,发现在调用LPDIRECT3DTEXTURE9的Release时,返回值为1而不是0,这样的话实际上DX并没有真正释放显存,而只是将其引用计数减一,结果造成了显存泄露。问题在于DX的文档中只是说明了在调用LPDIRECT3DTEXTURE9的GetSurfaceLevel()接口时会增加引用计数,实际中我的代码在做GPU特技中并没用调用该接口,最后实在没办法只能使用AddRef()接口一点一点定位,结果发现SetTexture()调用也会增加引用计数。但是在DX的文档中并没有明确指出该调用会增加引用计数。但是在DX的在线文档中有如下描述:

Note  The IDirect3DDevice9::SetTexture method increments the reference count of the texture surface being assigned. When the texture is no longer needed, you should set the texture at the appropriate stage to NULL. If you fail to do this, the surface will not be released, resulting in a memory leak.、

确认了是由于该函数调用引起的,于是在做完特技后使用NULL调用SetTexture()就可了。问题解决,下面给出一段测试demo,该代码也是之前边缘检测的代码,可以在创建资源时通过GPU-Z查看显存占用,在RenderScreen()中分别注释和放开g_pEffect->SetTexture(hTexScreen, NULL);这段代码,然后在release中再次查看其显存占用是否会减少。这里又做了一个实验,是不是必须使用NULL指针来调用SetTexture()才能将引用计数减一,实验结果是可以的,不过建议最好使用NULL,大家可以使用g_pEffect->SetTexture(hTexScreen, g_pTextureScreen1);来代替NULL试验一下。

/********************************
*  Author: rabbit729
*  E-mail: [email protected]
*  Date:   2012-09-23
*  Description: 图像的边缘检测
********************************/
#include <d3dx9.h>

//-----------------------------------------------------------------------------
// Desc: 全局变量
//-----------------------------------------------------------------------------
LPDIRECT3D9             g_pD3D                 = NULL;  //Direct3D对象
LPDIRECT3DDEVICE9       g_pd3dDevice           = NULL;  //Direct3D设备对象

LPDIRECT3DTEXTURE9      g_pTextureScreen        = NULL; //待处理图片
LPDIRECT3DTEXTURE9      g_pTextureScreen1        = NULL; //待处理图片

ID3DXEffect*            g_pEffect               = NULL;  //效果指针
//常量句柄
D3DXHANDLE              hTechScreen             = NULL;  //Effect句柄
D3DXHANDLE              hTexScreen              = NULL;  //纹理句柄
D3DXHANDLE              hViewPortWidthInv       = NULL;  //视口宽倒数句柄
D3DXHANDLE              hViewPortHeightInv      = NULL;  //视口高倒数句柄

LPDIRECT3DVERTEXBUFFER9 g_pScreenSpaceQuad      = NULL;  //背板VB

const int WIDTH  = 465;
const int HEIGHT = 669;

int nRef = 0;

#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_TEX1)

struct Vertex
{
	Vertex(){}
	Vertex(float x, float y, float z, float w)
	{
		_x = x;   _y = y;   _z = z; _w = w;
	}

	float _x, _y, _z, _w;

	static const DWORD FVF;
};
const DWORD Vertex::FVF = D3DFVF_XYZW;


//-----------------------------------------------------------------------------
// Desc: 设置世界矩阵
//-----------------------------------------------------------------------------
VOID SetWorldMatrix()
{
	//创建并设置世界矩阵
	D3DXMATRIXA16 matWorld, matRotateX, matRotateY;
	D3DXMATRIXA16 matScale;
	D3DXMatrixIdentity(&matScale);
	matScale._11 = matScale._22 = matScale._33 = 0.5f;

	D3DXMatrixIdentity(&matWorld);
	D3DXMatrixIdentity(&matRotateX);
	D3DXMatrixIdentity(&matRotateY);
	D3DXMatrixRotationX( &matRotateX, D3DX_PI / 3.0 );
	D3DXMatrixRotationY( &matRotateY, -D3DX_PI / 8.0 );
	D3DXMatrixMultiply(&matWorld, &matRotateX, &matRotateY);

	D3DXMatrixMultiply(&matWorld, &matScale, &matWorld);
	g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );
}


//-----------------------------------------------------------------------------
// Desc: 设置观察矩阵和投影矩阵
//-----------------------------------------------------------------------------
VOID SetViewAndProjMatrix()
{
	//创建并设置观察矩阵
	D3DXVECTOR3 vEyePt( 0.0f, 0.0f,-250.0f );
	D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );
	D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );
	D3DXMATRIXA16 matView;
	D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );
	g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );

	//创建并设置投影矩阵
	D3DXMATRIXA16 matProj;
	D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 1.0f, 1000.0f );
	g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
}


//-----------------------------------------------------------------------------
// Desc: 初始化Direct3D
//-----------------------------------------------------------------------------
HRESULT InitD3D( HWND hWnd )
{
	//创建Direct3D对象, 该对象用于创建Direct3D设备对象
	if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
		return E_FAIL;

	//设置D3DPRESENT_PARAMETERS结构, 准备创建Direct3D设备对象
	D3DPRESENT_PARAMETERS d3dpp; 
	ZeroMemory( &d3dpp, sizeof(d3dpp) );
	d3dpp.Windowed = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat = D3DFMT_A8R8G8B8;

	//创建Direct3D设备对象
	if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
		D3DCREATE_SOFTWARE_VERTEXPROCESSING,
		&d3dpp, &g_pd3dDevice ) ) )
	{
		return E_FAIL;
	}

	//创建Effect
	ID3DXBuffer* errBuffer = NULL;
	if (FAILED(D3DXCreateEffectFromFile(g_pd3dDevice, L"edgedetect.fx", NULL, NULL, D3DXSHADER_DEBUG | D3DXSHADER_SKIPOPTIMIZATION, NULL, &g_pEffect, &errBuffer)))
	{
		if (errBuffer)
		{
			MessageBox(0, (LPCTSTR)errBuffer->GetBufferPointer(), 0, 0);
			errBuffer->Release();
		}
		return E_FAIL;
	}

	//获取常量句柄
	hTechScreen         = g_pEffect->GetTechniqueByName("Screen");
	hTexScreen          = g_pEffect->GetParameterByName(0, "TexScreen");
	hViewPortWidthInv   = g_pEffect->GetParameterByName(0, "viewport_inv_width");
	hViewPortHeightInv  = g_pEffect->GetParameterByName(0, "viewport_inv_height");

	//设置环境光
	g_pd3dDevice->SetRenderState( D3DRS_AMBIENT, 0xffffffff );

	//设置观察矩阵和投影矩阵
	SetViewAndProjMatrix();

	return S_OK;
}

//-----------------------------------------------------------------------------
// Desc: 创建场景图形
//-----------------------------------------------------------------------------
HRESULT InitGeometry()
{
	// 创建屏幕板
	g_pd3dDevice->CreateVertexBuffer(
		6 * sizeof(Vertex),
		D3DUSAGE_WRITEONLY,
		Vertex::FVF, 
		D3DPOOL_MANAGED,
		&g_pScreenSpaceQuad,
		0);

	Vertex* vertices;
	g_pScreenSpaceQuad->Lock(0, 0, (void**)&vertices, 0);

	vertices[0] = Vertex(-1.0f, 1.0f, 0.5f, 1.0f);
	vertices[1] = Vertex(1.0f,  1.0f, 0.5f, 1.0f);
	vertices[2] = Vertex( 1.0f, -1.0f, 0.5f, 1.0f);
	vertices[3] = Vertex(-1.0f, 1.0f, 0.5f, 1.0f);
	vertices[4] = Vertex( 1.0f, -1.0f, 0.5f, 1.0f);
	vertices[5] = Vertex( -1.0f, -1.0f, 0.5f, 1.0f);

	g_pScreenSpaceQuad->Unlock();

	//加载纹理
	HRESULT hr = D3DXCreateTextureFromFileEx(g_pd3dDevice, L"meinv.jpg", D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 
		0, D3DFMT_UNKNOWN, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, &g_pTextureScreen);
	if (FAILED(hr))
	{
		return E_FAIL;
	} 

	hr = D3DXCreateTextureFromFileEx(g_pd3dDevice, L"meinv1.jpg", D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 
		0, D3DFMT_UNKNOWN, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, NULL, NULL, &g_pTextureScreen1);
	if (FAILED(hr))
	{
		return E_FAIL;
	} 

	return S_OK;
}


//-----------------------------------------------------------------------------
// Desc: 释放创建的对象
//-----------------------------------------------------------------------------
VOID Cleanup()
{
	int nRef = 0;
	if (g_pScreenSpaceQuad != NULL)
	{
		g_pScreenSpaceQuad->Release();
	}

	if (g_pTextureScreen != NULL)
	{
		nRef = g_pTextureScreen->Release();
	}

	if (g_pTextureScreen1 != NULL)
	{
		nRef = g_pTextureScreen1->Release();
	}

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

	//释放Direct3D设备对象
	if( g_pd3dDevice != NULL )
		nRef = g_pd3dDevice->Release();

	//释放Direct3D对象
	if( g_pD3D != NULL )
		g_pD3D->Release();
}

VOID RenderScreen()
{
	//将RenderTarget作为纹理
	g_pEffect->SetTexture(hTexScreen, g_pTextureScreen);
	float fWidthInv = 1.0f / WIDTH;
	float fHeightInv = 1.0f / HEIGHT;
	g_pEffect->SetFloat(hViewPortWidthInv, fWidthInv);
	g_pEffect->SetFloat(hViewPortHeightInv, fHeightInv);

	g_pd3dDevice->SetStreamSource(0, g_pScreenSpaceQuad, 0, sizeof(Vertex));
	g_pd3dDevice->SetFVF(Vertex::FVF);

	// 设置要使用的Technique
	g_pEffect->SetTechnique(hTechScreen);

	UINT numPasses = 0;
	g_pEffect->Begin(&numPasses, 0);

	for (int i = 0; i < numPasses; i++)
	{
		g_pEffect->BeginPass(i);

		g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);

		g_pEffect->EndPass();
	}
	g_pEffect->End();
	g_pEffect->SetTexture(hTexScreen, NULL);
	//g_pEffect->SetTexture(hTexScreen, g_pTextureScreen1);
}

//-----------------------------------------------------------------------------
// Desc: 渲染场景
//-----------------------------------------------------------------------------
VOID Render()
{    
	// 获取backbuffer
	LPDIRECT3DSURFACE9 pBackbuffer;
	g_pd3dDevice->GetRenderTarget(0, &pBackbuffer);

	//开始渲染场景
	if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
	{
		//设回backbuffer
		g_pd3dDevice->SetRenderTarget(0, pBackbuffer);
		RenderScreen();

		//场景渲染结束
		g_pd3dDevice->EndScene();
	}

	//在屏幕上显示场景
	//nRef = g_pTextureScreen->AddRef();
	g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
	//nRef = g_pTextureScreen->AddRef();
}


//-----------------------------------------------------------------------------
// Desc: 窗口过程, 处理消息
//-----------------------------------------------------------------------------
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
	switch( msg )
	{
	case WM_DESTROY:
		Cleanup();
		PostQuitMessage( 0 );
		return 0;
	}

	return DefWindowProc( hWnd, msg, wParam, lParam );
}


//-----------------------------------------------------------------------------
// Desc: 入口函数
//-----------------------------------------------------------------------------
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
	//注册窗口类
	WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, 
		GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
		L"ClassName", NULL };
	RegisterClassEx( &wc );

	//创建窗口
	HWND hWnd = CreateWindow( L"ClassName", L"图像二值化", 
		WS_OVERLAPPEDWINDOW, 200, 100, WIDTH, HEIGHT,
		GetDesktopWindow(), NULL, wc.hInstance, NULL );

	//初始化Direct3D
	if( SUCCEEDED( InitD3D( hWnd ) ) )
	{ 
		//创建场景图形
		if( SUCCEEDED( InitGeometry() ) )
		{
			//显示窗口
			ShowWindow( hWnd, SW_SHOWDEFAULT );
			UpdateWindow( hWnd );

			//进入消息循环
			MSG msg; 
			ZeroMemory( &msg, sizeof(msg) );
			while( msg.message!=WM_QUIT )
			{
				if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
				{
					TranslateMessage( &msg );
					DispatchMessage( &msg );
				}
				else
				{
					Render();  //渲染场景
				}
			}
		}
	}

	UnregisterClass( L"ClassName", wc.hInstance );
	return 0;
}

Effect代码

/********************************
*  Author: rabbit729
*  E-mail: [email protected]
*  Date:   2011-09-03
********************************/

//------------------------------
//  顶点着色器
//------------------------------

float viewport_inv_width;
float viewport_inv_height;
struct VS_OUTPUTSCREEN {
   float4 Pos: POSITION;
   float2 texCoord: TEXCOORD0;
};

VS_OUTPUTSCREEN vs_mainPassScreen(float4 Pos: POSITION){
   VS_OUTPUTSCREEN Out;

   Out.Pos = float4(Pos.xy, 0, 1);

   Out.texCoord.x = 0.5 * (1 + Pos.x - viewport_inv_width);
   Out.texCoord.y = 0.5 * (1 - Pos.y - viewport_inv_height);

   return Out;
}

//------------------------------
//  像素着色器
//------------------------------

Texture2D TexScreen;

sampler2D TexMapScreen {
    Texture = <TexScreen>;
};

//Laplacian算子盒1
const float4 samples1[5] = {
  0.0, 1.0, 0.0, -1.0,
  -1.0, 0.0, 0.0, -1.0,
  0.0, 0.0, 0.0, 4.0,
  1.0, 0.0, 0.0, -1.0,
  0.0, 1.0, 0.0, -1.0
};

//Laplacian算子盒2
const float4 samples2[9] = {
  -1.0, -1.0, 0.0, -1.0,
  0.0, -1.0, 0.0, -1.0,
  1.0, 1.0, 0.0, -1.0,
  -1.0, 0.0, 0.0, -1.0,
  0.0, 0.0, 0.0, 8.0,
  1.0, 0.0, 0.0, -1.0,
  -1.0, 1.0, 0.0, -1.0,
  0.0, 1.0, 0.0, -1.0,
  1.0, 1.0, 0.0, -1.0
};

//Laplacian算子盒3
const float4 samples3[9] = {
  -1.0, -1.0, 0.0, 1.0,
  0.0, -1.0, 0.0, -2.0,
  1.0, 1.0, 0.0, 1.0,
  -1.0, 0.0, 0.0, -2.0,
  0.0, 0.0, 0.0, 4.0,
  1.0, 0.0, 0.0, -2.0,
  -1.0, 1.0, 0.0, 1.0,
  0.0, 1.0, 0.0, -2.0,
  1.0, 1.0, 0.0, 1.0
};

float4 ps_mainPassScreen(float2 texCoord: TEXCOORD0) : COLOR 
{
   //return float4(Intensity.xxx,col.a);
   float4 col = float4(0, 0, 0, 0);
   
   for(int i = 0; i < 5; i++)
   {
     col += samples1[i].w * tex2D(TexMapScreen, texCoord + float2(samples1[i].x*viewport_inv_width,
     samples1[i].y*viewport_inv_height));
   }
   
   //为了突出显示,此处将结果乘2
   return 2.0 * col;
}

//------------------------------
//  效果框架
//------------------------------

technique Screen
{
   pass P0
   {
      VertexShader = compile vs_3_0 vs_mainPassScreen();
      PixelShader  = compile ps_3_0 ps_mainPassScreen();
   }
}



你可能感兴趣的:(关于SetTexture()调用引起显存泄露的问题)