关于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: wlq_729@163.com
*  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: wlq_729@163.com
*  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();
   }
}



<think>好的,我现在需要解决用户在Amplify Shader Editor(ASE)中遇到的“Property (_MainTex) already exists. Use SetTexture instead”错误。用户提到要使用SetTexture替代,所以需要根据之前的对话和Unity Shader的知识来找出解决方案。 首先,回顾用户的历史问题,之前讨论了在Unity Shader中解决_MainTex重复定义的方法。现在用户在ASE中遇到同样的问题,但ASE生成的代码可能有所不同,需要针对性处理。 错误原因分析: 1. **ASE自动生成属性**:ASE可能会自动为某些节点(如主纹理)生成_MainTex属性,导致重复声明。 2. **手动添加同名属性**:用户可能在Properties区块手动添加了_MainTex,与ASE生成的冲突。 3. **旧版SetTexture用法**:可能在固定功能着色器部分使用了SetTexture,隐式创建了属性。 解决方案步骤: 1. **检查ASE生成的Properties**:查看ASE生成的代码,确认是否自动包含_MainTex。如果是,需删除手动添加的部分。 2. **重命名自定义纹理属性**:在ASE中使用唯一的属性名,如_CustomTex,避免与自动生成的冲突。 3. **调整节点连接方式**:通过ASE的节点图连接纹理,而不是手动编写Properties代码。 4. **使用SetTexture的情况**:如果是固定功能着色器,确保正确使用SetTexture命令,并避免在Properties中重复声明。 验证流程: 1. 在ASE中检查纹理节点的属性名称,确保唯一性。 2. 编译Shader后查看错误是否消失。 3. 使用Unity的Shader Inspector查看最终生成的代码,确认没有重复的_MainTex。 相关问题部分需要涵盖用户可能遇到的扩展问题,比如如何在ASE中避免属性冲突,或者如何处理不同着色器类型的属性定义。 现在需要将这些思路整理成结构化的回答,确保符合用户要求的格式,使用中文,正确使用LaTeX,并在末尾添加相关问题。</think>### Amplify Shader Editor(ASE)中解决 "_MainTex属性重复定义" 的专项方案 #### 错误触发机制分析 1. **ASE自动属性生成**:当使用`Texture Sample`节点时,ASE默认生成`_MainTex`属性(若未手动命名)[^2] 2. **传统着色器模板冲突**:选择旧版`Unlit`或`Legacy`模板时自动注入`_MainTex`声明 3. **混合编码冲突**:在ASE生成的`Properties{}`块外手动添加同名属性 ```glsl // ASE自动生成部分 Properties { _MainTex ("MainTex", 2D) = "white" {} // ✗ 自动生成 } // 用户手动添加部分 Properties { _MainTex ("CustomTex", 2D) = "black" {} // ✗ 冲突源 } ``` #### 分步解决方案 **方案一:节点重命名法**(推荐) 1. 在ASE编辑器中选中`Texture Sample`节点 2. 修改属性面板的`Parameter Name`为唯一标识(如`_CustomTex`) ```glsl // 修改后生成的代码 Properties { _CustomTex ("CustomTex", 2D) = "white" {} // ✓ 唯一标识 } ``` **方案二:模板修正法** 1. 创建新着色器时选择`Template: Surface`而非`Unlit` 2. 通过`Node > Property`手动创建纹理属性时,禁用`Auto-Name`选项 ```glsl // 正确声明方式 [ASE_Properties] _CustomTex("My Texture", 2D) = "white" {} ``` **方案三:代码注入控制**(高阶) 1. 在ASE的`Custom Editor`字段添加预处理指令 ```glsl #pragma shader_feature _USE_CUSTOMTEX ``` 2. 使用条件编译隔离属性声明 ```glsl #ifndef _USE_CUSTOMTEX sampler2D _MainTex; // ✓ 仅保留一处定义 #endif ``` #### 验证流程图 ```mermaid graph TD A[发生错误] --> B{检查Texture Sample节点} B --> |存在默认_MainTex| C[重命名节点参数] B --> |手动添加属性| D[删除重复Properties] C --> E[重新编译Shader] D --> E E --> F{错误是否解决?} F --> |是| G[完成] F --> |否| H[检查模板类型] H --> I[更换Surface模板] ``` #### 进阶调试技巧 1. **查看Generated Code**: - 点击ASE窗口右上角`Generated Code`按钮 - 搜索`_MainTex`出现次数,定位重复位置[^3] 2. **版本兼容处理**: ```glsl #if ASE_VERSION >= 1000 // ASE 1.0+版本特性 [NoScaleOffset] _CustomTex("Tex", 2D) = "white" {} #endif ```
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值