[D3D11] C++与GPU通信:在C++代码中更新HLSL中的常量缓冲区

时间:2021-11-09 15:04:01
先贴出一段HLSL代码,本文将以这段HLSL为例,说明如何在C++中更新HLSL的常量:
Texture2D txDiffuse : register(t0);
SamplerState samLinear : register(s0);

cbuffer cbNeverChanges : register(b0)
{
	matrix View;
};

cbuffer cbChangeOnResize : register(b1)
{
	matrix Projection;
};

cbuffer cbChangesEveryFrame : register(b2)
{
	matrix World;
	float4 vMeshColor;
};

//-----------------------------------------------
struct VS_INPUT
{
	float4 Pos : POSITION;
	float2 Tex : TEXCOORD0;
};

struct PS_INPUT
{
	float4 Pos : SV_POSITION;
	float2 Tex : TEXCOORD0;
};

//-----------------------------------------------
PS_INPUT VS(VS_INPUT input)
{
	PS_INPUT output = (PS_INPUT)0;
	output.Pos = mul(input.Pos, World);
	output.Pos = mul(output.Pos, View);
	output.Pos = mul(output.Pos, Projection);
	output.Tex = input.Tex;

	return output;
}

float4 PS(PS_INPUT input) : SV_Target
{
	return txDiffuse.Sample(samLinear, input.Tex) * vMeshColor;
}
一般在定义常量时,根据数据被修改的频率,将修改频率不同的数据定义在不同的cbuffer中,这样可以提升效率。如上所示的HLSL代码,定义了以下几个常量:
matrix View;   // 几乎没有修改
matrix Projection;    // 修改频繁很小
matrix World;   // 修改频繁较大
float4 vMeshColor;   // 修改频繁较大

以下是C++中更新这几个变量的逻辑。主要有三个函数:初始化、更新、使用

ID3D11Buffer* g_pCBNeverChanges = nullptr;
ID3D11Buffer* g_pCBChangeOnResize = nullptr;
ID3D11Buffer* g_pCBChangesEveryFrame = nullptr;

struct CBNeverChanges
{
	XMMATRIX mView;
};

struct CBChangeOnResize
{
	XMMATRIX mProjection;
};

struct CBChangesEveryFrame
{
	XMMATRIX mWorld;
	XMFLOAT4 vMeshColor;
};

void InitConstantBuffer()
{
	D3D11_BUFFER_DESC bd;
	ZeroMemory(&bd, sizeof(bd));
	bd.Usage = D3D11_USAGE_DEFAULT;
	bd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;

	bd.ByteWidth = sizeof(CBNeverChanges);
	g_pd3dDevice->CreateBuffer(&bd, nullptr, &g_pCBNeverChanges);

	bd.ByteWidth = sizeof(CBChangeOnResize);
	g_pd3dDevice->CreateBuffer(&bd, nullptr, &g_pCBChangeOnResize);

	bd.ByteWidth = sizeof(CBChangesEveryFrame);
	g_pd3dDevice->CreateBuffer(&bd, nullptr, &g_pCBChangesEveryFrame);
}

void UpdateConstantBuffer()
{
	CBNeverChanges cbNeverChanges;
	cbNeverChanges.mView = ...; // 赋值
	g_pD3DDeviceContext->UpdateSubresource(g_pCBNeverChanges, 0, nullptr, &cbNeverChanges, 0, 0);

	CBChangeOnResize cbChangesOnResize;
	cbChangesOnResize.mProjection = ...; // 赋值
	g_pD3DDeviceContext->UpdateSubresource(g_pCBChangeOnResize, 0, nullptr, &cbChangesOnResize, 0, 0);

	CBChangesEveryFrame cb;
	cb.mWorld = ...; // 赋值
	cb.vMeshColor = ...; // 赋值
	g_pD3DDeviceContext->UpdateSubresource(g_pCBChangesEveryFrame, 0, nullptr, &cb, 0, 0);
}

void Render()
{
	// clear
	g_pD3DDeviceContext->ClearRenderTargetView(g_pRenderTargetView, Colors::MidnightBlue);
	g_pD3DDeviceContext->ClearDepthStencilView(g_pDepthStencilView, D3D11_CLEAR_DEPTH, 1.0f, 0);

	// render

	g_pD3DDeviceContext->VSSetShader(g_pVertexShader, nullptr, 0);
	g_pD3DDeviceContext->VSSetConstantBuffers(0, 1, &g_pCBNeverChanges);
	g_pD3DDeviceContext->VSSetConstantBuffers(1, 1, &g_pCBChangeOnResize);
	g_pD3DDeviceContext->VSSetConstantBuffers(2, 1, &g_pCBChangesEveryFrame); // 顶点着色器用到了全部三个常量区,所以需要设置3个常量区

	g_pD3DDeviceContext->PSSetShader(g_pPixelShader, nullptr, 0);
	g_pD3DDeviceContext->PSSetConstantBuffers(2, 1, &g_pCBChangesEveryFrame); // 像素着色器只用到了第三个常量区,所以只需要设置索引为2的常量区
							  
	g_pD3DDeviceContext->PSSetShaderResources(0, 1, &g_pTextureRV); // 对应HLSL的 Texture2D txDiffuse
	g_pD3DDeviceContext->PSSetSamplers(0, 1, &g_pSamplerLinear);   // 对应HLSL的 SamplerState samLinear

	g_pD3DDeviceContext->DrawIndexed(36, 0, 0);
	g_pSwapChain->Present(0, 0);
}