상수 버퍼는 셰이더 프로그램에서 참조하는 자료를 담은 GPU 자원(ID3D12Resource)의 예이다. Vertex Shader 코드 중 이런 구문이 있다.
cbuffer cbPerObject : register(b0)
{
float4x4 gWorldViewProj;
};
여기서 cbPerObject 라는 cbuffer 객체(상수 버퍼)를 참조한다. 여기서 상수 버퍼는 gWorldViewProj라는 4 x 4 행렬 하나만 저장한다.
정점, 색인 버퍼와는 달리 상수 버퍼는 프레임마다 갱신해주는 것이 일반적이다. 카메라가 매 프레임 이동할 때 마다 상수 버퍼를 새 시야 행렬로 갱신해야 할 것이다. 따라서 상수 버퍼는 디폴트 힙이 아닌 업로드 힙에 만든다.
또한, 상수 버퍼에는 특별한 하드웨어 조건이 따른다. 크기가 반드시 최소 하드웨어 할당 크기(256byte)의 배수이어야 한다는 것이다.
같은 종류의 상수 버퍼를 여러 개 사용해야 하는 경우가 많다. 예를 들어 cbPerObject는 물체마다 달라지는 상수들을 담으므로, 물체가 n개면 이 종류의 상수 버퍼는 n개가 필요하다. 다음 코드는 NumElements개의 상수 버퍼 객체를 담는 하나의 버퍼를 생성한다.
struct ObjectConstants
{
DirectX::XMFLOAT4X4 WorldViewProj = MathHelper::Identity4x4();
};
UploadBuffer(ID3D12Device* pDevice, UINT elementCount, bool isConstantBuffer)
:
mIsConstantBuffer(isConstantBuffer)
{
if (isConstantBuffer)
{
mElementByteSize = D3DUtil::CalcConstantBufferByteSize(sizeof(T));
}
mElementByteSize = sizeof(T);
ThrowIfFailed(pDevice->CreateCommittedResource(
&CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD),
D3D12_HEAP_FLAG_NONE,
&CD3DX12_RESOURCE_DESC::Buffer(mElementByteSize * elementCount),
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(mUploadBuffer.GetAddressOf())));
}
// 256byte 경계에 맞게 바이트들이 암묵적으로 채워진다.
cbuffer cbPerObject : register(b0)
{
float4x4 gWorldViewProj;
};
// 256byte 경계에 맞게 명시적으로 바이트들을 채운다.
cbuffer cbPerObject : register(b0)
{
float4x4 gWorldViewProj;
float4x4 Pad0;
float4x4 Pad1;
float4x4 Pad1;
};
상수 버퍼를 D3D12_HEAP_TYPE_UPLOAD에 생성했으므로 CPU에서 상수 버퍼 자원에 자료를 올릴 수 있다. 자원을 올리려면 자원 자료를 가리키는 포인터를 얻어야 하는데 그러려면 Map()을 호출해야 한다.
ThrowIfFailed(mUploadBuffer->Map(
0,
nullptr,
reinterpret_cast<void**>(&mMappedData)));
시스템 메모리에 있는 자료를 상수 버퍼에 복사하려면 memcpy를 이용한다.
memcpy(mMappedData, &data, dataSizeInBytes);
상수버퍼에 자료를 다 복사했으면 해당 메모리를 해제하기 전에 Ummap()을 호출한다.
if(mUploadBuffer != nullptr)
{
mUploadBuffer->Unmap(0, nullptr);
}
mMappedData = nullptr;
이제 이 상수 버퍼를 렌더링 파이프라인에 묶으려면 서술자 객체가 필요하다. 상수 버퍼 서술자는 D3D12_DESCRIPTOR_HEAP_TYPE_CBV_STV_UAV 형식의 서술자 힙에 담긴다.
(상수 버퍼는 업로드 힙에, 상수 버퍼 뷰는 CBV_STV_UAV 힙에 생성)
void BoxApp::BuildConstantDescriptorHeap()
{
D3D12_DESCRIPTOR_HEAP_DESC cbvDesc;
cbvDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
cbvDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
cbvDesc.NumDescriptors = 1;
cbvDesc.NodeMask = 0;
ThrowIfFailed(mDevice->CreateDescriptorHeap(
&cbvDesc,
IID_PPV_ARGS(mCbvHeap.GetAddressOf())));
}
그리고 이제 상수 버퍼 뷰(=서술자)를 생성한다.
void BoxApp::BuildConstantBuffer()
{
mConstantBuffer =
std::make_unique<UploadBuffer<ObjectConstants>>(mDevice.Get(), 1, true);
UINT cbByteSize = D3DUtil::CalcConstantBufferByteSize(sizeof(ObjectConstants));
D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {};
cbvDesc.BufferLocation = mConstantBuffer->GetConstantBuffer()->GetGPUVirtualAddress();
cbvDesc.SizeInBytes = cbByteSize;
mDevice->CreateConstantBufferView(
&cbvDesc,
mCbvHeap->GetCPUDescriptorHandleForHeapStart());
}
'DirectX' 카테고리의 다른 글
6.7 셰이더의 컴파일 (0) | 2021.12.31 |
---|---|
6.6 루트 서명과 서술자 테이블 (0) | 2021.12.31 |
6.4 픽셀 셰이더(Pixel Shader) (0) | 2021.12.26 |
6.3 정점 셰이더(Vertex Shader) (0) | 2021.12.26 |
6.2 색인(index) (0) | 2021.12.24 |