-
[019] 서술자 힙 관리 클래스Programming/Dev 2025. 5. 1. 16:30

Dev 원래는 씬 변경 구현을 하려 했으나..
일단 이게 더 급하고 중요한 것 같아서 먼저 하게 됐다.
그래픽스 코드 구조를 이쁘게 만든 뒤에 인게임 로직 코드를 작성하는게 목표다.
DescriptorManager::DescriptorManager() : m_srvHeap{ D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1024, D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE, 0 }, m_rtvHeap{ D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 512, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 0 }, m_dsvHeap{ D3D12_DESCRIPTOR_HEAP_TYPE_DSV, 512, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, 0 } { if (s_srvDescriptorSize == 0) s_srvDescriptorSize = g_d3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); if (s_rtvDescriptorSize == 0) s_rtvDescriptorSize = g_d3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); if (s_dsvDescriptorSize == 0) s_dsvDescriptorSize = g_d3dDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_DSV); }`DescriptorManager`는 싱글톤으로 크기가 큰 SRV, RTV, DSV 힙을 관리하도록 했다.
처음에는 가변 크기 힙을 구현하려고 했으나 어려운 부분이 많고 이렇게 크기를 크게 잡아둬도 큰 문제가 없다고 판단했다.
DescriptorManager::Heap::Heap(D3D12_DESCRIPTOR_HEAP_TYPE type, UINT numDescriptors, D3D12_DESCRIPTOR_HEAP_FLAGS flags, UINT nodeMask) : m_desc{} { m_desc.Type = type; m_desc.NumDescriptors = numDescriptors; m_desc.Flags = flags; m_desc.NodeMask = nodeMask; g_d3dDevice->CreateDescriptorHeap(&m_desc, IID_PPV_ARGS(&m_heap)); m_descriptors.resize(numDescriptors); m_freeList.reserve(numDescriptors); for (int i : std::views::iota(0) | std::views::take(numDescriptors) | std::views::reverse) { m_freeList.push_back(i); } }`m_descriptors` 는 CPU, GPU 핸들을 갖고 있고,
`m_freeList` 에는 `m_descriptors` 컨테이너에서 사용 가능한 인덱스를 담고 있다.Descriptor* DescriptorManager::Heap::Allocate() { assert(!m_freeList.empty() && "NO AVAILABLE HEAP SPACE"); INT index{ m_freeList.back() }; m_freeList.pop_back(); UINT descriptorSize{ GetDescriptorSize() }; CD3DX12_CPU_DESCRIPTOR_HANDLE cpuHandle{ m_heap->GetCPUDescriptorHandleForHeapStart(), index, descriptorSize }; CD3DX12_GPU_DESCRIPTOR_HANDLE gpuHandle{ D3D12_DEFAULT }; if (m_desc.Flags == D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE) gpuHandle.InitOffsetted(m_heap->GetGPUDescriptorHandleForHeapStart(), index, descriptorSize); m_descriptors[index] = Descriptor{ cpuHandle, gpuHandle }; return &m_descriptors[index]; } void DescriptorManager::Heap::Deallocate(Descriptor* descriptor) { auto it{ std::ranges::find_if(m_descriptors, [descriptor](const auto& desc) { return &desc == descriptor; }) }; if (it == m_descriptors.end()) return; *it = Descriptor{}; INT index{ static_cast<INT>(std::distance(m_descriptors.begin(), it)) }; m_freeList.push_back(index); }서술자를 할당, 해제 하는 함수이다.
할당할 때 `m_freeList` 에서 인덱스 하나 꺼내서 그 인덱스에 있는 원소에 핸들을 세팅해준다.
해제할 때는 해당 인덱스에 있는 원소를 기본값(=유효하지 않은 값)으로 설정하고 `m_freeList` 에 넣어 사용 가능하다고 표시한다.
void SwapChain::CreateRenderTargetView() { auto dm{ DescriptorManager::GetInstance() }; if (!dm) return; for (size_t i{ 0 }; i < FRAME_COUNT; ++i) { if (FAILED(m_swapChain->GetBuffer(static_cast<UINT>(i), IID_PPV_ARGS(&m_frameResources[i].backBuffer)))) { assert(false); return; } if (m_frameResources[i].rtvDesc) { dm->Deallocate(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, m_frameResources[i].rtvDesc); m_frameResources[i].rtvDesc = nullptr; } m_frameResources[i].rtvDesc = dm->Allocate(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); m_frameResources[i].rtvDesc->CreateRenderTargetView(m_frameResources[i].backBuffer, nullptr); } }사용할 때는 `Allocate` 함수 호출 할 때 할당 받을 힙 타입을 인자로 넘겨주면 된다.
위 코드는 RTV를 만드는 코드이다.
void SwapChain::Begin3D() { if (FAILED(m_frameResources[m_frameIndex].commandAllocator->Reset())) { assert(false); return; } if (FAILED(g_commandList->Reset(m_frameResources[m_frameIndex].commandAllocator.Get(), nullptr))) { assert(false); return; } g_commandList->SetGraphicsRootSignature(g_rootSignature.Get()); g_commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_frameResources[m_frameIndex].backBuffer.Get(), D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET)); auto rtvHandle{ m_frameResources[m_frameIndex].rtvDesc->GetCpuHandle() }; auto dsvHandle{ m_dsvDesc->GetCpuHandle() }; g_commandList->OMSetRenderTargets(1, &rtvHandle, TRUE, &dsvHandle); g_commandList->RSSetViewports(1, &g_viewport); g_commandList->RSSetScissorRects(1, &g_scissorRect); constexpr std::array clearColor{ 0.15625f, 0.171875f, 0.203125f, 1.0f }; g_commandList->ClearRenderTargetView(rtvHandle, clearColor.data(), 0, nullptr); g_commandList->ClearDepthStencilView(dsvHandle, D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 1.0f, 0, 0, nullptr); if (auto dm{ D3D::DescriptorManager::GetInstance() }) dm->SetDescriptorHeaps(); }렌더링 시작할 때 `SetDescriptorHeaps` 함수를 호출해줘서 SRV 서술자 힙을 세팅해준다.
void Viewport::Render() { static auto renderTarget{ Graphics::D3D::CreateRenderTarget(1920, 1080) }; static std::shared_ptr<Graphics::D3D::Camera> camera; if (!camera) { camera = Graphics::D3D::CreateCamera(); } static std::shared_ptr<Resource::Model> robot; if (!robot) { auto prop{ Resource::Get(L"Data/Test.dat/Robot") }; robot = prop->GetModel(); } if (ImGui::Begin(WINDOW_NAME, nullptr, ImGuiWindowFlags_HorizontalScrollbar)) { Graphics::D3D::PushRenderTarget(renderTarget); Graphics::D3D::SetCamera(camera); Graphics::D3D::Render(robot); Graphics::D3D::PopRenderTarget(); Graphics::ImGui::Image(renderTarget, ImVec2{ 1920, 1080 }); } ImGui::End(); }위 코드는 ImGui에서 텍스쳐를 렌더링하는 코드다.
`class RenderTarget` 을 만들 때 SRV를 하나 할당해서 ImGui에서도 쓸 수 있게 만들었다.

ImGui에 잘 들어간 모습 렌더타겟을 만들어서 그 곳에 모델을 하나 그리고, 그것을 ImGui로 그리게 해보았다.
사실 이거 하려고 서술자 힙 관리 클래스를 만든 것 같다...
'Programming > Dev' 카테고리의 다른 글
[021] Visual Studio 솔루션을 CMake로 전환 (0) 2025.10.22 [020] C++ 루아 스크립트 임베딩 (0) 2025.10.13 [018] 회원가입, 로그인 구현 (0) 2025.02.27 [017] C++ ODBC를 이용한 데이터베이스 연결 (0) 2024.11.17 [016] Github Actions 자동 빌드 추가 (0) 2024.10.20
