SleakEngine 0.1.0
C++23 multi-backend game engine
Loading...
Searching...
No Matches
DirectX12UploadContext.cpp
Go to the documentation of this file.
1#include <Core/Logger.hpp>
3#include <utility>
4
5namespace Sleak {
6namespace RenderEngine {
7
8bool DirectX12UploadContext::Initialize(ID3D12Device* device,
9 ID3D12CommandQueue* queue) {
10 Release();
11 if (!device || !queue) return false;
12
13 HRESULT hr =
14 device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence));
15 if (FAILED(hr)) {
16 SLEAK_ERROR("Failed to create upload fence! HRESULT: 0x{:08X}",
17 static_cast<unsigned int>(hr));
18 return false;
19 }
20
21 m_event = CreateEvent(nullptr, FALSE, FALSE, nullptr);
22 if (!m_event) {
23 SLEAK_ERROR("Failed to create upload fence event!");
24 m_fence.Reset();
25 return false;
26 }
27
28 m_device = device;
29 m_queue = queue;
30 return true;
31}
32
33ID3D12GraphicsCommandList* DirectX12UploadContext::GetCommandList() {
34 if (m_recording) return m_commandList.Get();
35 if (!m_device) return nullptr;
36
37 Microsoft::WRL::ComPtr<ID3D12CommandAllocator> allocator;
38 HRESULT hr = S_OK;
39 if (!m_freeAllocators.empty()) {
40 allocator = std::move(m_freeAllocators.back());
41 m_freeAllocators.pop_back();
42 hr = allocator->Reset();
43 } else {
44 hr = m_device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT,
45 IID_PPV_ARGS(&allocator));
46 }
47 if (FAILED(hr)) {
48 SLEAK_ERROR("Failed to prepare upload allocator! HRESULT: 0x{:08X}",
49 static_cast<unsigned int>(hr));
50 return nullptr;
51 }
52
53 if (m_commandList) {
54 hr = m_commandList->Reset(allocator.Get(), nullptr);
55 } else {
56 hr = m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT,
57 allocator.Get(), nullptr,
58 IID_PPV_ARGS(&m_commandList));
59 if (SUCCEEDED(hr)) m_commandList->SetName(L"UploadCommandList");
60 }
61 if (FAILED(hr)) {
62 SLEAK_ERROR("Failed to open upload command list! HRESULT: 0x{:08X}",
63 static_cast<unsigned int>(hr));
64 return nullptr;
65 }
66
67 m_open.allocator = std::move(allocator);
68 m_recording = true;
69 return m_commandList.Get();
70}
71
73 ID3D12Resource** resource) {
74 if (!resource || size == 0 || !GetCommandList()) return nullptr;
75
76 D3D12_HEAP_PROPERTIES heapProps = {};
77 heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
78
79 D3D12_RESOURCE_DESC desc = {};
80 desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
81 desc.Width = size;
82 desc.Height = 1;
83 desc.DepthOrArraySize = 1;
84 desc.MipLevels = 1;
85 desc.Format = DXGI_FORMAT_UNKNOWN;
86 desc.SampleDesc.Count = 1;
87 desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
88
89 Microsoft::WRL::ComPtr<ID3D12Resource> staging;
90 HRESULT hr = m_device->CreateCommittedResource(
91 &heapProps, D3D12_HEAP_FLAG_NONE, &desc,
92 D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&staging));
93 if (FAILED(hr)) {
94 SLEAK_ERROR("Failed to create staging buffer! HRESULT: 0x{:08X}",
95 static_cast<unsigned int>(hr));
96 return nullptr;
97 }
98
99 D3D12_RANGE noRead = {0, 0};
100 void* mapped = nullptr;
101 hr = staging->Map(0, &noRead, &mapped);
102 if (FAILED(hr)) {
103 SLEAK_ERROR("Failed to map staging buffer! HRESULT: 0x{:08X}",
104 static_cast<unsigned int>(hr));
105 return nullptr;
106 }
107
108 *resource = staging.Get();
109 m_open.staging.push_back(std::move(staging));
110 return mapped;
111}
112
114 if (!m_recording) return;
115 m_recording = false;
116
117 HRESULT hr = m_commandList->Close();
118 if (FAILED(hr)) {
119 SLEAK_ERROR("Failed to close upload command list! HRESULT: 0x{:08X}",
120 static_cast<unsigned int>(hr));
121 m_open = Batch{};
122 return;
123 }
124
125 ID3D12CommandList* lists[] = {m_commandList.Get()};
126 m_queue->ExecuteCommandLists(1, lists);
127 m_open.fenceValue = ++m_fenceValue;
128 m_queue->Signal(m_fence.Get(), m_fenceValue);
129
130 m_inFlight.push_back(std::move(m_open));
131 m_open = Batch{};
132}
133
135 Submit();
136 if (!m_fence) return;
137 if (m_fence->GetCompletedValue() < m_fenceValue) {
138 m_fence->SetEventOnCompletion(m_fenceValue, m_event);
139 WaitForSingleObject(m_event, INFINITE);
140 }
141 Retire();
142}
143
145 if (!m_fence) return;
146 const uint64_t completed = m_fence->GetCompletedValue();
147
148 size_t finished = 0;
149 while (finished < m_inFlight.size() &&
150 m_inFlight[finished].fenceValue <= completed) {
151 m_freeAllocators.push_back(std::move(m_inFlight[finished].allocator));
152 ++finished;
153 }
154 m_inFlight.erase(m_inFlight.begin(), m_inFlight.begin() + finished);
155}
156
158 if (m_queue && m_fence) Flush();
159
160 m_inFlight.clear();
161 m_freeAllocators.clear();
162 m_open = Batch{};
163 m_recording = false;
164 m_commandList.Reset();
165 m_fence.Reset();
166 if (m_event) {
167 CloseHandle(m_event);
168 m_event = nullptr;
169 }
170 m_fenceValue = 0;
171 m_device = nullptr;
172 m_queue = nullptr;
173}
174
175} // namespace RenderEngine
176} // namespace Sleak
#define SLEAK_ERROR(...)
Definition Logger.hpp:22
void Release()
Flushes outstanding work and frees everything.
void * AllocateStaging(uint64_t size, ID3D12Resource **resource)
Returns a mapped upload buffer that lives until the open batch is done.
ID3D12GraphicsCommandList * GetCommandList()
Returns the open copy command list, starting a new batch if needed.
void Retire()
Frees staging memory of batches the GPU has finished.
bool Initialize(ID3D12Device *device, ID3D12CommandQueue *queue)
Creates the fence used to track submitted batches.
void Submit()
Executes the recorded copies without waiting for them.
void Flush()
Submits and blocks until every batch has finished on the GPU.
Backend-facing rendering layer shared by the four graphics backends.
Root namespace for everything the engine exposes.
Definition Camera.hpp:10