음... 현재 DirectX12를 하면서 Texture Pool 까지 만들어서 기분이 좋았지만... 문제를 하나 발견했다.

매시 하나를 출력하면... Vertex,Index가 64KB씩 할당 받게 된다. 그러면 총 합 128KB씩 매시 하나당 증가 하게 된다.
그래서 바로 전에 했던
https://commongamedeveloper.tistory.com/62
GPU Memory Heap
https://github.com/GPUOpen-LibrariesAndSDKs/D3D12MemoryAllocator GitHub - GPUOpen-LibrariesAndSDKs/D3D12MemoryAllocator: Easy to integrate memory allocation library for Direct3D 12Easy to integrate memory allocation library for Direct3D 12 - GPUOpen-Librar
commongamedeveloper.tistory.com
이것과 비슷하게 Vertex/Index도 Pool을 만들어 관리 하자.
개념
위에 MemoryHeap과 같은 개념이다.
다만 패딩을 64KB가 아닌,
Vertex는 4바이트(float하나) Index는 두개로 나눴다. 2바이트(UINT16)와 4바이트(UINT32)
또한 MemoryHeap과 비슷하게 해제 하는 풀, 해제를 안하는 풀로 만들었다. 코드와 개념은 는 MemoryHeap과 똑같다. 위의 개념을 참고 부탁한다.
다만 Memory Heap과 다른점 이라고 하면, ID3D12Resource로 처음에 Commited로 할당 받고 그 안에 값만 복사 해준다.
Bump
#include "pch.h"
#include "GpuBumpBufferPool.h"
///////////////////////
// GpuBumpBufferPage //
///////////////////////
GpuBumpBufferPage::GpuBumpBufferPage(UINT8 pageIndex, UINT64 size) : _pageIndex(pageIndex), _totalSize(size)
{
_offset = 0;
CreateResource();
}
GpuBumpBufferPage::~GpuBumpBufferPage()
{
_resource.Reset();
}
void GpuBumpBufferPage::CreateResource()
{
D3D12_HEAP_PROPERTIES properties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
D3D12_RESOURCE_DESC desc = {};
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc.Width = _totalSize;
desc.Height = 1;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
desc.SampleDesc.Count = 1;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
ThrowIfFailed(DEVICE->CreateCommittedResource(&properties, D3D12_HEAP_FLAG_NONE,
&desc, D3D12_RESOURCE_STATE_COPY_DEST,
nullptr, IID_PPV_ARGS(_resource.GetAddressOf())));
}
bool GpuBumpBufferPage::Alloc(UINT64 size, UINT8 aligneSize, OUT GpuBufferHandle& handle)
{
assert(aligneSize != 0 && (aligneSize & (aligneSize - 1)) == 0);
UINT64 alignedOffset = (_offset + aligneSize - 1) & ~(aligneSize - 1);
// 용량이 모자란경우
if (alignedOffset + size > _totalSize)
{
return false;
}
handle.gen = 0;
handle.offset = alignedOffset;
handle.size = size;
handle.pageIndex = _pageIndex;
_offset = alignedOffset + size;
return true;
}
void GpuBumpBufferPage::Reset()
{
_offset = 0;
}
UINT64 GpuBumpBufferPage::GetFreeByte()
{
return _totalSize - _offset;
}
///////////////////////
// GpuBufferBumpPool //
///////////////////////
GpuBumpBufferPool::GpuBumpBufferPool(UINT8 poolID) : GpuBumpBufferPool(poolID, DEFAULT_SIZE)
{
}
GpuBumpBufferPool::GpuBumpBufferPool(UINT8 poolID, UINT64 size) : _poolID(poolID), _totalSize(size), _pages(DEFAULT_RESOURCE_COUNT)
{
for (int i = 0; i < DEFAULT_RESOURCE_COUNT; ++i)
{
GpuBumpBufferPage* page = new GpuBumpBufferPage(i, _totalSize);
_pages[i] = page;
}
}
GpuBumpBufferPool::~GpuBumpBufferPool()
{
for (int i = 0; i < _pages.GetCount(); ++i)
{
delete _pages[i];
}
}
void GpuBumpBufferPool::IncreaseGpuResource()
{
UINT32 index = _pages.GetCount();
_pages.SetCount(_pages.GetCount() + 1);
GpuBumpBufferPage* page = new GpuBumpBufferPage(index, _totalSize);
_pages[index] = page;
}
ComPtr<ID3D12Resource>& GpuBumpBufferPool::GetMemoryHeap(UINT8 index)
{
assert(_pages.GetCount() > index);
return _pages[index]->GetResource();
}
bool GpuBumpBufferPool::GetBufferHandle(eGpuBufferKind type, UINT64 size, UINT8 aligneSize, OUT GpuBufferHandle& handle)
{
bool allocSuccess = false;
int count = _pages.GetCount();
for (int i = 0; i < count; ++i)
{
GpuBumpBufferPage* page = _pages[i];
bool isSuccess = page->Alloc(size, aligneSize, handle);
if (isSuccess == false)
{
if (i + 1 >= count)
{
IncreaseGpuResource();
count++;
}
continue;
}
allocSuccess = true;
handle.poolID = _poolID;
break;
}
return allocSuccess;
}
void GpuBumpBufferPool::ResetAllPage()
{
for (int i = 0; i < _pages.GetCount(); ++i)
{
_pages[i]->Reset();
}
}
Dynamic
#include "pch.h"
#include "GpuDynamicBufferPool.h"
GpuDynamicBufferPage::GpuDynamicBufferPage(UINT8 pageIndex, UINT64 size)
: _pageIndex(pageIndex), _currentSize(size), _totalSize(size),
_handlerCount(DEFAULT_BUFFER_HANDLER_ARRAY_SIZE), _handlers(DEFAULT_BUFFER_HANDLER_ARRAY_SIZE), _indexes(DEFAULT_BUFFER_HANDLER_ARRAY_SIZE),
_startReleaseTable(), _endReleaseTable()
{
CreateResource();
for (int i = 0; i < _handlers.GetCount(); ++i)
{
_indexes.Push(i);
_handlers[i].gen = 0;
_handlers[i].arrayIndex = i;
_handlers[i].pageIndex = _pageIndex;
}
_startReleaseTable.Add(0, _totalSize);
_endReleaseTable.Add(_totalSize, 0);
}
GpuDynamicBufferPage::~GpuDynamicBufferPage()
{
_resource.Reset();
}
void GpuDynamicBufferPage::CreateResource()
{
D3D12_HEAP_PROPERTIES properties = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
D3D12_RESOURCE_DESC desc = {};
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc.Width = _totalSize;
desc.Height = 1;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
desc.SampleDesc.Count = 1;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
ThrowIfFailed(DEVICE->CreateCommittedResource(&properties, D3D12_HEAP_FLAG_NONE,
&desc, D3D12_RESOURCE_STATE_COPY_DEST,
nullptr, IID_PPV_ARGS(_resource.GetAddressOf())));
}
bool GpuDynamicBufferPage::Alloc(UINT64 size, UINT8 aligneSize, OUT GpuBufferHandle & handle)
{
assert(aligneSize != 0 && (aligneSize & (aligneSize - 1)) == 0);
if (_currentSize < size)
{
assert(false);
return false;
}
if (_indexes.GetCount() <= 0)
{
IncreaseHandlerSize(_handlerCount * 2);
}
UINT32 index = 0;
bool isSuccess = _indexes.Pop(index);
if (isSuccess == false)
{
return false;
}
UINT32 tableCapacity = _startReleaseTable.GetCapacity();
for (UINT32 i = 0; i < tableCapacity; ++i)
{
UINT64 offset;
UINT64 emptySize;
if (_startReleaseTable.IsOccupiedAt(i, offset, emptySize))
{
UINT64 alignedOffset = (offset + aligneSize - 1) & ~(aligneSize - 1);
if ((alignedOffset + size) <= (offset + emptySize))
{
_handlers[index].arrayIndex = index;
_handlers[index].offset = alignedOffset;
_handlers[index].size = size;
_handlers[index].pageIndex = _pageIndex;
handle = _handlers[index];
_currentSize -= size;
// 앞 비어있는 공간 존재할 시
if (alignedOffset > offset)
{
_startReleaseTable.Add(offset, alignedOffset - offset);
_endReleaseTable.Add(alignedOffset, offset);
}
else
{
_startReleaseTable.RemoveKey(offset);
}
// 뒤 비어있는 공간 존재할 시
if ((alignedOffset + size) < (offset + emptySize))
{
_startReleaseTable.Add(alignedOffset + size, (offset + emptySize) - (alignedOffset + size));
_endReleaseTable.Add(offset + emptySize, alignedOffset + size);
}
else
{
_endReleaseTable.RemoveKey(offset + emptySize);
}
return true;
}
}
}
// 남는 사이즈는 있지만 자리가 없음
return false;
}
void GpuDynamicBufferPage::Reset()
{
_currentSize = _totalSize;
_indexes.Clear();
for (int i = 0; i < _handlers.GetCount(); ++i)
{
_indexes.Push(i);
_handlers[i].gen = 0;
_handlers[i].arrayIndex = i;
_handlers[i].pageIndex = _pageIndex;
}
_startReleaseTable.Add(0, _totalSize);
_endReleaseTable.Add(_totalSize, 0);
}
bool GpuDynamicBufferPage::Free(const GpuBufferHandle& handle)
{
if (_handlers.GetCount() <= handle.arrayIndex) { assert(false); return false; }
if (_handlers[handle.arrayIndex].gen != handle.gen) { assert(false); return false; }
UINT64 offset = handle.offset;
UINT64 size = handle.size;
UINT64 frontNeighborStart = _totalSize + 1;
UINT64 backNeighborSize = _totalSize + 1;
// 블록 앞에 빈 블록이 있음
_endReleaseTable.GetValue(offset, frontNeighborStart);
// 블록 뒤에 빈 블록이 있음
_startReleaseTable.GetValue((offset + size), backNeighborSize);
// 구간 시작 끝 둘다
if (frontNeighborStart != _totalSize + 1 && backNeighborSize != _totalSize + 1)
{
_startReleaseTable.Add(frontNeighborStart, offset + size + backNeighborSize - frontNeighborStart);
_endReleaseTable.Add((offset + size + backNeighborSize), frontNeighborStart);
_startReleaseTable.RemoveKey(offset + size);
_endReleaseTable.RemoveKey(offset);
}
else if (frontNeighborStart != _totalSize + 1)
{
_startReleaseTable.Add(frontNeighborStart, (offset + size) - frontNeighborStart);
_endReleaseTable.Add(offset + size, frontNeighborStart);
_endReleaseTable.RemoveKey(offset);
}
else if (backNeighborSize != _totalSize + 1)
{
_startReleaseTable.Add(offset, backNeighborSize + size);
_endReleaseTable.Add(offset + size + backNeighborSize, offset);
_startReleaseTable.RemoveKey(offset + size);
}
else if (frontNeighborStart == _totalSize + 1 && backNeighborSize == _totalSize + 1)
{
_startReleaseTable.Add(offset, size);
_endReleaseTable.Add(offset + size, offset);
}
_handlers[handle.arrayIndex].gen++;
_handlers[handle.arrayIndex].size = 0;
_handlers[handle.arrayIndex].offset = 0;
_indexes.Push(handle.arrayIndex);
_currentSize += handle.size;
return true;
}
void GpuDynamicBufferPage::IncreaseHandlerSize(UINT32 size)
{
_handlers.SetCount(size);
for (int i = _handlerCount; i < size; ++i)
{
_indexes.Push(i);
_handlers[i].gen = 0;
_handlers[i].arrayIndex = i;
_handlers[i].pageIndex = _pageIndex;
}
_handlerCount = size;
}
GpuDynamicBufferPool::GpuDynamicBufferPool(UINT8 poolID) : GpuDynamicBufferPool(poolID, DEFAULT_SIZE)
{
}
GpuDynamicBufferPool::GpuDynamicBufferPool(UINT8 poolID, UINT64 size) : _poolID(poolID), _totalSize(size), _pages(DEFAULT_RESOURCE_COUNT)
{
for (int i = 0; i < DEFAULT_RESOURCE_COUNT; ++i)
{
GpuDynamicBufferPage* page = new GpuDynamicBufferPage(i, _totalSize);
_pages[i] = page;
}
}
GpuDynamicBufferPool::~GpuDynamicBufferPool()
{
for (int i = 0; i < _pages.GetCount(); ++i)
{
delete _pages[i];
}
}
void GpuDynamicBufferPool::IncreaseGpuResource()
{
UINT32 index = _pages.GetCount();
_pages.SetCount(_pages.GetCount() + 1);
GpuDynamicBufferPage* page = new GpuDynamicBufferPage(index, _totalSize);
_pages[index] = page;
}
ComPtr<ID3D12Resource>& GpuDynamicBufferPool::GetMemoryHeap(UINT8 index)
{
assert(_pages.GetCount() > index);
return _pages[index]->GetResource();
}
bool GpuDynamicBufferPool::GetBufferHandle(eGpuBufferKind type, UINT64 size, UINT8 aligneSize, OUT GpuBufferHandle& handle)
{
bool allocSuccess = false;
int count = _pages.GetCount();
for (int i = 0; i < count; ++i)
{
GpuDynamicBufferPage* page = _pages[i];
bool isSuccess = page->Alloc(size, aligneSize, handle);
if (isSuccess == false)
{
if (i + 1 >= count)
{
IncreaseGpuResource();
count++;
}
continue;
}
allocSuccess = true;
handle.poolID = _poolID;
break;
}
return allocSuccess;
}
bool GpuDynamicBufferPool::ReleaseBufferHandle(UINT8 index, const GpuBufferHandle& handle)
{
bool isSuccess = _pages[index]->Free(handle);
return isSuccess;
}
void GpuDynamicBufferPool::ResetPage(UINT8 pageIndex)
{
assert(pageIndex < _pages.GetCount());
_pages[pageIndex]->Reset();
}
void GpuDynamicBufferPool::ResetAllPage()
{
for (int i = 0; i < _pages.GetCount(); ++i)
{
ResetPage(i);
}
}
결과

구 하나를 출력하는데 무려 8KB 밖에 안들었다.
저 8KB는 추적해보니, ConstantBuffer를 만들 때 생기는 바이트 이다. 이건 다음에 개선을 하겠다.
'DirectX12' 카테고리의 다른 글
| Constant Buffer Pool (0) | 2026.09.26 |
|---|---|
| GPU Memory Heap (0) | 2026.09.12 |
| DescripterAllocator Pool (0) | 2026.09.05 |
| Texture! (0) | 2026.08.28 |
| Mesh Rendering - 2 (0) | 2026.08.28 |