root signature의 슬롯 메모리 한계로 인하여 테이블을 사용해야할 경우
table은 포인터처럼 사용되어 마치 사용할 레지스터 배열의 시작점을 알려주는 것과 비슷함
Constant buffer들을 가리키는 Decriptor heap(CBV)를 만들고 이를
복사하여 GPU에게 요청하면 GPU는 레지스터에 해당 값들을 로드함
CBV를 복사하는 destination 메모리는 이전과 마찬가지로 복사는 즉시되지만
실제 사용은 command 요청 후이다.
그렇기 때문에 버퍼를 만들어 사용해야함
SetDescriptorHeaps 함수로 메모리 버퍼를 가리키고,
SetGraphicsRootDescriptorTable 함수로 버퍼 내의 몇번째 데이터를 사용할지 알려줌
SetDescriptorHeaps는 매우 느리니 남발하면 안되고 프레임당 한번만 불릴 수 있게 구현
=>
SetDescriptorHeaps를 여러번 불러야 하는 경우, table 설계 자체를 다시해야 함
CD3DX12_DESCRIPTOR_RANGE ranges[] =
{
CD3DX12_DESCRIPTOR_RANGE(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 5, 0),
};
CD3DX12_ROOT_PARAMETER param[1];
param[0].InitAsDescriptorTable(_countof(ranges), ranges);
root signature 파라미터 설정 시에
CD3DX12_DESCRIPTOR_RANGE 데이터에 b0부터 5개를 사용한다고 입력
InitAsDescriptorTable 함수로 사용할 뷰가 몇개인지 입력하여 초기화
ComPtr<ID3D12DescriptorHeap> _cbvHeap;
D3D12_CPU_DESCRIPTOR_HANDLE _cpuHandleBegin = {};
uint32 _handleIncrementSize = 0;
CBV는 위와 같은 데이터들이 필요
_cpuHandleBegin는 heap의 시작 지점을 가리키는 핸들
_handleIncrementSize는 데이터 하나의 크기를 말함
void ConstantBuffer::CreateView()
{
D3D12_DESCRIPTOR_HEAP_DESC cbvDesc = {};
cbvDesc.NumDescriptors = _elementCount;
cbvDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
cbvDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
DEVICE->CreateDescriptorHeap(&cbvDesc, IID_PPV_ARGS(&_cbvHeap));
...
}
constant buffer를 view로 만들어 저장할 view 버퍼를 만드는 함수
프로그램에서 사용하는 view 버퍼 플래그는 D3D12_DESCRIPTOR_HEAP_FLAG_NONE
GPU가 실제 사용할 view 버퍼 플래그는 D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE 이어야 함
_cpuHandleBegin = _cbvHeap->GetCPUDescriptorHandleForHeapStart();
_handleIncrementSize = DEVICE->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
for (uint32 i = 0; i < _elementCount; ++i)
{
D3D12_CPU_DESCRIPTOR_HANDLE cbvHandle = GetCpuHandle(i);
D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {};
cbvDesc.BufferLocation = _cbvBuffer->GetGPUVirtualAddress() + static_cast<uint64>(_elementSize) * i;
cbvDesc.SizeInBytes = _elementSize; // CB size is required to be 256-byte aligned.
DEVICE->CreateConstantBufferView(&cbvDesc, cbvHandle);
}
_handleIncrementSize는 그래픽카드마다 다르기 때문에 장치에서 가지고 와야 한다
그리고 constant buffer의 정보들을 가지고 view들을 생성
table descriptor heap => GPU에서 사용할 descriptor heap 구현
class TableDescriptorHeap
{
...
private:
ComPtr<ID3D12DescriptorHeap> _descHeap;
uint64 _handleSize = 0;
uint64 _groupSize = 0;
uint64 _groupCount = 0;
uint32 _currentGroupIndex = 0;
};
_groupSize 버퍼의사이즈 (_handleSize * 데이터 개수)
_groupCount 버퍼의 개수
void TableDescriptorHeap::Init(uint32 count)
{
...
D3D12_DESCRIPTOR_HEAP_DESC desc = {};
desc.NumDescriptors = count * REGISTER_COUNT;
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
DEVICE->CreateDescriptorHeap(&desc, IID_PPV_ARGS(&_descHeap));
...
}
앞에서 말한 바와 같이 플래그는 D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE
해당 플래그가 있어야 GPU 메모리에 상주 한다고 함
void TableDescriptorHeap::SetCBV(D3D12_CPU_DESCRIPTOR_HANDLE srcHandle, CBV_REGISTER reg)
{
D3D12_CPU_DESCRIPTOR_HANDLE destHandle = GetCPUHandle(reg);
uint32 destRange = 1;
uint32 srcRange = 1;
DEVICE->CopyDescriptors(1, &destHandle, &destRange, 1, &srcHandle, &srcRange, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
}
constant buffer를 가지고 만들어둔 descriptor heap를
CopyDescriptors 함수를 통해 GPU가 사용할 descriptor heap에 복사한다.
void TableDescriptorHeap::CommitTable()
{
D3D12_GPU_DESCRIPTOR_HANDLE handle = _descHeap->GetGPUDescriptorHandleForHeapStart();
handle.ptr += _currentGroupIndex * _groupSize;
CMD_LIST->SetGraphicsRootDescriptorTable(0, handle);
_currentGroupIndex++;
}
command list에 사용할 테이블 주소를 SetGraphicsRootDescriptorTable 함수로 등록한다.
GEngine->GetTableDescHeap()->Clear();
ID3D12DescriptorHeap* descHeap = GEngine->GetTableDescHeap()->GetDescriptorHeap().Get();
_cmdList->SetDescriptorHeaps(1, &descHeap);
렌더링 시작 함수에서 SetDescriptorHeaps 함수를 통해 사용하는 heap을 지정해야 함
D3D12_CPU_DESCRIPTOR_HANDLE ConstantBuffer::PushData(int32 rootParamIndex, void* buffer, uint32 size)
{
...
::memcpy(&_mappedBuffer[_currentIndex * _elementSize], buffer, size);
D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle = GetCpuHandle(_currentIndex);
_currentIndex++;
return cpuHandle;
}
constant buffer에 데이터를 넣는 함수는 넣은 메모리 주소를 반환하게 하여
view를 만들 때 쓸 수 있도록 수정
{
D3D12_CPU_DESCRIPTOR_HANDLE handle = GEngine->GetCB()->PushData(0, &_transform, sizeof(_transform));
GEngine->GetTableDescHeap()->SetCBV(handle, CBV_REGISTER::b0);
GEngine->GetTableDescHeap()->CommitTable();
}
메쉬 렌더 시에 handle의 데이터는 b0 레지스터에 로드해 달라고 설정
이후 commit 하여 SetGraphicsRootDescriptorTable 함수가 요청할 수 있도록 함