
“힙이 전체 리소스를 포함할 만큼 크고 리소스가 힙에 매핑되도록 리소스와 암시적 힙을 모두 만듭니다.” 라고 나와있습니다.힙을 만든다 라는 동작이 아주 큰 문제가 됩니다.CreateCommittedResource 함수 자체는 별도의 메모리 공간이 필요하며, 이는 즉 시스템콜이 필수적으로 들어간다는 뜻입니다.CreateCommittedResource를 호출하면 4KB나 되는 메모리가 낭비되는 셈입니다. 
CreateCommittedResource 함수를 사용하지 않을 수 없습니다.CreateCommittedResource 자체의 호출을 최소화 하는 겁니다!!
CreateCommittedResource 를 통해 커다란 메모리 공간을 생성하고 이를 쪼개 쓸 수 있게 시스템을 만들어주면 됩니다.
malloc같은 메모리 할당 함수의 일부를 운영체제 밖의 영역에서 유저가 직접 구현하는 방식입니다. .
.
.
int64 DefaultPageCnt = _DefaultPages.GetSize();
for (int32 i = 0; i < DefaultPageCnt; i++)
{
SS::PooledLinkedList<AvailableMemspace>::iterator iter = _DefaultPages[i].AvailableChunks.begin();
for (;iter != _DefaultPages[i].AvailableChunks.end(); ++iter)
{
AvailableMemspace& UsableSpaceItem = *iter;
if (UsableSpaceItem.Size >= NeededSize)
{
int32 OriginalChunkOffset = UsableSpaceItem.Offset;
UsableSpaceItem.Offset += NeededSize;
UsableSpaceItem.Size -= NeededSize;
if (UsableSpaceItem.Size == 0) // 사용하고 나서 해당 공간의 사이즈가 0이되면 그냥 없애버린다.
{
_DefaultPages[i].AvailableChunks.Erase(iter);
}
AllocatedChunkHeader NewChunk;
NewChunk.ChunkName = ChunkName;
NewChunk.PageContent = _DefaultPages[i].PageContent;
NewChunk.DefaultPageIdx = i;
NewChunk.ChunkOffset = OriginalChunkOffset;
NewChunk.Size = NeededSize;
_AllocatedDefaultPageChunks.PushBack(NewChunk);
SS::PooledLinkedList<AllocatedChunkHeader>::iterator IterLastItem = _AllocatedDefaultPageChunks.GetLastValidIter();
AllocatedChunkHeader& LastItem = *IterLastItem;
LastItem.IdxInLinkedList = IterLastItem.GetNodeIdx();
return LastItem;
}
}
}
// 가용 가능한 메모리 공간이 없는 경우에는 새로 페이지를 할당한다.
ReserveDefaultPage(DefaultPageCnt + 1); // !! AllocPage !!
SS::PooledLinkedList<AvailableMemspace>::iterator iter = _DefaultPages[DefaultPageCnt].AvailableChunks.begin();
AvailableMemspace& FirstUsableSpace = *iter;
FirstUsableSpace.Offset += NeededSize;
FirstUsableSpace.Size -= NeededSize;
AllocatedChunkHeader NewChunk;
NewChunk.ChunkName = ChunkName;
NewChunk.PageContent = _DefaultPages[DefaultPageCnt].PageContent;
NewChunk.DefaultPageIdx = DefaultPageCnt;
NewChunk.ChunkOffset = 0;
NewChunk.Size = NeededSize;
_AllocatedDefaultPageChunks.PushBack(NewChunk);
SS::PooledLinkedList<AllocatedChunkHeader>::iterator IterLastItem = _AllocatedDefaultPageChunks.GetLastValidIter();
AllocatedChunkHeader& LastItem = *IterLastItem;
LastItem.IdxInLinkedList = IterLastItem.GetNodeIdx();
return LastItem;
void* DX12ConstantBufferResourceCustomAllocator::AllocPage(int32 PageSize)
{
ID3D12Device5* DX12Device = _RenderDevice->GetD3DDevice();
ID3D12Resource* newResource = nullptr;
CD3DX12_HEAP_PROPERTIES heapProperties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
CD3DX12_RESOURCE_DESC resourceDesc = CD3DX12_RESOURCE_DESC::Buffer(GetEachPageSize());
HRESULT hr = DX12Device->CreateCommittedResource(
&heapProperties,
D3D12_HEAP_FLAG_NONE,
&resourceDesc,
D3D12_RESOURCE_STATE_GENERIC_READ,
nullptr,
IID_PPV_ARGS(&newResource)); // 페이지 단위로 할당하고 이걸 쪼개쓴다.
if (FAILED(hr))
{
DEBUG_BREAK();
return nullptr;
}
newResource->SetName(GetAllocatorName().C_Str());
byte* newResourceSysmem = nullptr;
CD3DX12_RANGE writeRange(0, 0);
hr = newResource->Map(0, &writeRange, reinterpret_cast<void**>(&newResourceSysmem));
if (FAILED(hr))
{
DEBUG_BREAK();
return nullptr;
}
DX12ConstantBufferResourcePage* NewResourcePage = DBG_NEW DX12ConstantBufferResourcePage;
NewResourcePage->D3D12Resource = newResource;
NewResourcePage->ResourceSysMem = newResourceSysmem;
return NewResourcePage;
}
CreateCommittedResource함수를 통해 중간 버퍼를 별도로 생성하고 해당 중간 버퍼에 값을 복사 한 이후 GPU전용 버퍼에 CopyBufferRegion 함수를 통해 복사해줍니다. 그리고 임시로 할당한 중간버퍼는 바로 삭제하죠.inline UINT64 UpdateSubresources(
_In_ ID3D12GraphicsCommandList* pCmdList,
_In_ ID3D12Resource* pDestinationResource,
_In_ ID3D12Resource* pIntermediate,
UINT64 IntermediateOffset,
_In_range_(0,D3D12_REQ_SUBRESOURCES) UINT FirstSubresource,
_In_range_(0,D3D12_REQ_SUBRESOURCES-FirstSubresource) UINT NumSubresources,
_In_reads_(NumSubresources) const D3D12_SUBRESOURCE_DATA* pSrcData) noexcept
{
_In_ ID3D12Resource* pIntermediate 라는 인자가 있는 것을 볼 수 있습니다.ID3D12Resource를 넘겨주도록 코드를 작성한 것을 볼 수 있습니다.HRESULT DX12GALResourceUpdater::UpdateTexture(
ID3D12GraphicsCommandList* CommandList,
ID3D12Resource* Dest,
const D3D12_SUBRESOURCE_DATA* pSrcData,
int32 NumSubResource,
int32 UploadBufferSize,
D3D12_RESOURCE_STATES FromState,
D3D12_RESOURCE_STATES ToState)
{
HRESULT hr = S_OK;
byte* pUploadBufferData = nullptr;
CD3DX12_RANGE writeRange(0, 0);
int32 RentBufferStartOffset = 0;
ID3D12Resource* UpdateResourceBuffer = RentUpdateBuffer(RentBufferStartOffset, UploadBufferSize);
// 미리 만들어놓은 메모리 풀을 빌려서 GPU메로리를 업데이트 해줍니다.
CD3DX12_RESOURCE_BARRIER ResourceBarrierCommonToCopyDest = CD3DX12_RESOURCE_BARRIER::Transition(Dest, FromState, D3D12_RESOURCE_STATE_COPY_DEST);
CD3DX12_RESOURCE_BARRIER ResourceBarrierCopyDestToVertexBuffer = CD3DX12_RESOURCE_BARRIER::Transition(Dest, D3D12_RESOURCE_STATE_COPY_DEST, ToState);
CommandList->ResourceBarrier(1, &ResourceBarrierCommonToCopyDest);
uint64 UpdateSize = UpdateSubresources(CommandList, Dest, UpdateResourceBuffer, RentBufferStartOffset, 0, NumSubResource, pSrcData);
CommandList->ResourceBarrier(1, &ResourceBarrierCopyDestToVertexBuffer);
if (UpdateSize <= 0)
{
SS_ASSERT(false);
return S_FALSE;
}
return hr;
}
RentUpdateBuffer 라는 함수를 통해 이미 마련된 메모리 풀을 빌려와서 사용하고 GPU리소스 업데이트가 끝나면 메모리를 해제하지 않고 메모리 풀에 반환해주는 방식으로 오버헤드를 완화해줄 수 있습니다.