forked from AmigurumiShaders/DX11Starter
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDX12Helper.cpp
More file actions
488 lines (440 loc) · 20.7 KB
/
DX12Helper.cpp
File metadata and controls
488 lines (440 loc) · 20.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
#include "DX12Helper.h"
#include "WICTextureLoader.h"
#include "ResourceUploadBatch.h"
using namespace DirectX;
DX12Helper* DX12Helper::instance;
DX12Helper::~DX12Helper()
{
}
// --------------------------------------------------------
// Sets up the helper with required DX12 objects
// --------------------------------------------------------
void DX12Helper::Initialize(
Microsoft::WRL::ComPtr<ID3D12Device> device,
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList> commandList,
Microsoft::WRL::ComPtr<ID3D12CommandQueue> commandQueue,
Microsoft::WRL::ComPtr<ID3D12CommandAllocator>* commandAllocators,
unsigned int numBackBuffers)
{
// Save objects
this->device = device;
this->commandList = commandList;
this->commandQueue = commandQueue;
this->commandAllocators = commandAllocators;
this->numBackBuffers = numBackBuffers;
// Create the fence for basic synchronization
device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(waitFence.GetAddressOf()));
waitFenceEvent = CreateEventEx(0, 0, 0, EVENT_ALL_ACCESS);
waitFenceCounter = 0;
//create fence for frame sync
device->CreateFence(0, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(frameSyncFence.GetAddressOf()));
frameSyncFenceEvent = CreateEventEx(0, 0, 0, EVENT_ALL_ACCESS);
frameSyncFenceCounters = new UINT64[numBackBuffers];
ZeroMemory(frameSyncFenceCounters, sizeof(UINT64) * numBackBuffers);
CreateConstantBufferUploadHeap();
CreateCBVSRVDescriptorHeap();
}
// --------------------------------------------------------
// Closes the current command list and tells the GPU to start executing those commands.
// We also wait for the GPU to finish this work so we can reset the command allocator
// (which CANNOT be reset while the GPU is using its commands) and the command list itself.
// --------------------------------------------------------
void DX12Helper::CloseAndExecuteCommandList()
{
// Close the current list and execute it as our only list
commandList->Close();
ID3D12CommandList* lists[] = { commandList.Get() };
commandQueue->ExecuteCommandLists(1, lists);
// Always wait before reseting command allocator, as it should not
// be reset while the GPU is processing a command list
// See: https://docs.microsoft.com/en-us/windows/desktop/api/d3d12/nf-d3d12-id3d12commandallocator-reset
//WaitForGPU(); //do i remove this to?
//commandAllocators->Reset();
//commandList->Reset(commandAllocators.Get(), 0);
}
// --------------------------------------------------------
// Makes our C++ code wait for the GPU to finish its
// current batch of work before moving on.
// --------------------------------------------------------
void DX12Helper::WaitForGPU()
{
// Update our ongoing fence value (a unique index for each "stop sign")
// and then place that value into the GPU's command queue
waitFenceCounter++;
commandQueue->Signal(waitFence.Get(), waitFenceCounter);
// Check to see if the most recently completed fence value
// is less than the one we just set.
if (waitFence->GetCompletedValue() < waitFenceCounter)
{
// Tell the fence to let us know when it's hit, and then
// sit an wait until that fence is hit.
waitFence->SetEventOnCompletion(waitFenceCounter, waitFenceEvent);
WaitForSingleObject(waitFenceEvent, INFINITE);
}
}
//adds a signal to the command queue so we can track which frames are completed
//returns the index that should be used for the next frame buffer
unsigned int DX12Helper::SyncSwapChain(unsigned int currentSwapBufferIndex)
{
//grab current fence value
UINT64 currentFenceCounter = frameSyncFenceCounters[currentSwapBufferIndex];
//signal this frame's counter
commandQueue->Signal(frameSyncFence.Get(), currentFenceCounter);
unsigned int nextBuffer = currentSwapBufferIndex + 1;
nextBuffer %= numBackBuffers;
//wait for next frame?
if (frameSyncFence->GetCompletedValue() < frameSyncFenceCounters[nextBuffer])
{
//waiting
frameSyncFence->SetEventOnCompletion(frameSyncFenceCounters[nextBuffer], frameSyncFenceEvent);
WaitForSingleObject(frameSyncFenceEvent, INFINITE);
}
//the frame is done, so update the next frame's counter
frameSyncFenceCounters[nextBuffer] = currentFenceCounter + 1;
//return the next buffer index, so the caller can use it to track which buffer should be used for the next frame.
return nextBuffer;
}
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> DX12Helper::GetCBVSRVDescriptorHeap()
{
return cbvSrvDescriptorHeap;
}
Microsoft::WRL::ComPtr<ID3D12CommandAllocator> DX12Helper::GetDefaultAllocator()
{
return commandAllocators[0];
}
Microsoft::WRL::ComPtr<ID3D12CommandAllocator> DX12Helper::GetAllocatorByIndex(unsigned int index)
{
// Valid index?
if (index >= numBackBuffers)
return 0;
return commandAllocators[index];
}
// --------------------------------------------------------
// Copies the given data into the next "unused" spot in the CBV upload heap (wrapping at the end, since
// we treat it like a ring buffer). Then creates a CBV in the next "unused" spot in the CBV heap that
// points to the aforementioned spot in the upload heap and returns that CBV (a GPU descriptor handle).
//
// data - The data to copy to the GPU
// dataSizeInBytes - The byte size of the data to copy
// --------------------------------------------------------
D3D12_GPU_DESCRIPTOR_HANDLE DX12Helper::FillNextConstantBufferAndGetGPUDescriptorHandle(
void* data, unsigned int dataSizeInBytes)
{
// How much space will we need? Each CBV must point to a chunk of the upload heap that is a multiple
// of 256 bytes, so we need to calculate and reserve that amount.
SIZE_T reservationSize = (SIZE_T)dataSizeInBytes;
reservationSize = (reservationSize + 255); // Add 255 so we can drop last few bits
reservationSize = reservationSize & ~255; // Flip 255 and then use it to mask
// Ensure this upload will fit in the remaining space. If not, reset to beginning.
if (cbUploadHeapOffsetInBytes + reservationSize >= cbUploadHeapSizeInBytes)
cbUploadHeapOffsetInBytes = 0;
// Where in the upload heap will this data go?
D3D12_GPU_VIRTUAL_ADDRESS virtualGPUAddress =
cbUploadHeap->GetGPUVirtualAddress() + cbUploadHeapOffsetInBytes;
// === Copy data to the upload heap ===
{
// Calculate the actual upload address (which we got from mapping the buffer)
// Note that this is different than the GPU virtual address needed for the CBV below
void* uploadAddress = reinterpret_cast<void*>(
(SIZE_T)cbUploadHeapStartAddress + cbUploadHeapOffsetInBytes);
// Perform the mem copy to put new data into this part of the heap
memcpy(uploadAddress, data, dataSizeInBytes);
// Increment the offset and loop back to the beginning if necessary,
// allowing us to treat the upload heap like a ring buffer
cbUploadHeapOffsetInBytes += reservationSize;
if (cbUploadHeapOffsetInBytes >= cbUploadHeapSizeInBytes)
cbUploadHeapOffsetInBytes = 0;
}
// Create a CBV for this section of the heap
{
// Calculate the CPU and GPU side handles for this descriptor
D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle = cbvSrvDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = cbvSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart();
// Offset each by based on how many descriptors we've used
// Note: cbvDescriptorOffset is a COUNT of descriptors, not bytes so we must calculate the size
cpuHandle.ptr += (SIZE_T)cbvDescriptorOffset * cbvSrvDescriptorHeapIncrementSize;
gpuHandle.ptr += (SIZE_T)cbvDescriptorOffset * cbvSrvDescriptorHeapIncrementSize;
// Describe the constant buffer view that points to our latest chunk of the CB upload heap
D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc = {};
cbvDesc.BufferLocation = virtualGPUAddress;
cbvDesc.SizeInBytes = (UINT)reservationSize;
// Create the CBV, which is a lightweight operation in DX12
device->CreateConstantBufferView(&cbvDesc, cpuHandle);
// Increment the offset and loop back to the beginning if necessary
// which allows us to treat the descriptor heap as a ring buffer
cbvDescriptorOffset++;
if (cbvDescriptorOffset >= maxConstantBuffers)
cbvDescriptorOffset = 0;
// Now that the CBV is ready, we return the GPU handle to it
// so it can be set as part of the root signature during drawing
return gpuHandle;
}
}
D3D12_CPU_DESCRIPTOR_HANDLE DX12Helper::LoadTexture(const wchar_t* file, bool generateMips)
{
// Helper function from DXTK for uploading a resource
// (like a texture) to the appropriate GPU memory
ResourceUploadBatch upload(device.Get());
upload.Begin();
// Attempt to create the texture
Microsoft::WRL::ComPtr<ID3D12Resource> texture;
CreateWICTextureFromFile(device.Get(), upload, file, texture.GetAddressOf(), generateMips);
// Perform the upload and wait for it to finish before returning the texture
auto finish = upload.End(commandQueue.Get());
finish.wait();
// Now that we have the texture, add to our list and make a CPU-side descriptor heap
// just for this texture's SRV. Note that it would probably be better to put all
// texture SRVs into the same descriptor heap, but we don't know how many we'll need
// until they're all loaded and this is a quick and dirty implementation!
textures.push_back(texture);
// Create the CPU-SIDE descriptor heap for our descriptor
D3D12_DESCRIPTOR_HEAP_DESC dhDesc = {};
dhDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; // Non-shader visible for CPU-side-only desc heap
dhDesc.NodeMask = 0;
dhDesc.NumDescriptors = 1;
dhDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
Microsoft::WRL::ComPtr<ID3D12DescriptorHeap> descHeap;
device->CreateDescriptorHeap(&dhDesc, IID_PPV_ARGS(descHeap.GetAddressOf()));
cpuSideTextureDescriptorHeaps.push_back(descHeap);
// Create the SRV on this descriptor heap
// Note: Using a null description results in the "default" SRV (same format, all mips, all array slices, etc.)
D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle = descHeap->GetCPUDescriptorHandleForHeapStart();
device->CreateShaderResourceView(texture.Get(), 0, cpuHandle);
// Return the CPU descriptor handle, which can be used to
// copy the descriptor to a shader-visible heap later
return cpuHandle;
}
D3D12_GPU_DESCRIPTOR_HANDLE DX12Helper::CopySRVsToDescriptorHeapAndGetGPUDescriptorHandle(D3D12_CPU_DESCRIPTOR_HANDLE firstDescriptorToCopy, unsigned int numDescriptorsToCopy)
{
// Grab the actual heap start on both sides and offset to the next open SRV portion
D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle =
cbvSrvDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle =
cbvSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart();
cpuHandle.ptr += (SIZE_T)srvDescriptorOffset * cbvSrvDescriptorHeapIncrementSize;
gpuHandle.ptr += (SIZE_T)srvDescriptorOffset * cbvSrvDescriptorHeapIncrementSize;
// We know where to copy these descriptors, so copy all of them and remember the new offset
device->CopyDescriptorsSimple(
numDescriptorsToCopy,
cpuHandle,
firstDescriptorToCopy,
D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
srvDescriptorOffset += numDescriptorsToCopy;
// Pass back the GPU handle to the start of this section
// in the final CBV/SRV heap so the caller can use it later
return gpuHandle;
}
// --------------------------------------------------------
// Creates a single CB upload heap which will store all
// constant buffer data for the entire program. This
// heap is treated as a ring buffer, allowing the program
// to continually re-use the memory as frames progress.
// --------------------------------------------------------
void DX12Helper::CreateConstantBufferUploadHeap()
{
// This heap MUST have a size that is a multiple of 256
// We'll support up to the max number of CBs if they're
// all 256 bytes or less, or fewer overall CBs if they're larger
cbUploadHeapSizeInBytes = maxConstantBuffers * 256;
// Assume the first CB will start at the beginning of the heap
// This offset changes as we use more CBs, and wraps around when full
cbUploadHeapOffsetInBytes = 0;
// Create the upload heap for our constant buffer
D3D12_HEAP_PROPERTIES heapProps = {};
heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heapProps.CreationNodeMask = 1;
heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
heapProps.Type = D3D12_HEAP_TYPE_UPLOAD; // Upload heap since we'll be copying often!
heapProps.VisibleNodeMask = 1;
// Fill out description
D3D12_RESOURCE_DESC resDesc = {};
resDesc.Alignment = 0;
resDesc.DepthOrArraySize = 1;
resDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
resDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
resDesc.Format = DXGI_FORMAT_UNKNOWN;
resDesc.Height = 1;
resDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
resDesc.MipLevels = 1;
resDesc.SampleDesc.Count = 1;
resDesc.SampleDesc.Quality = 0;
resDesc.Width = cbUploadHeapSizeInBytes; // Must be 256 byte aligned!
// Create a constant buffer resource heap
device->CreateCommittedResource(
&heapProps,
D3D12_HEAP_FLAG_NONE,
&resDesc,
D3D12_RESOURCE_STATE_GENERIC_READ,
0,
IID_PPV_ARGS(cbUploadHeap.GetAddressOf()));
// Keep mapped!
D3D12_RANGE range{ 0, 0 };
cbUploadHeap->Map(0, &range, &cbUploadHeapStartAddress);
}
// --------------------------------------------------------
// Creates a single CBV descriptor heap which will store all
// CBVs and SRVs for the entire program. Like the CBV upload heap,
// this heap is treated as a ring buffer, allowing the program
// to continually re-use the memory as frames progress.
// --------------------------------------------------------
void DX12Helper::CreateCBVSRVDescriptorHeap()
{
// Ask the device for the increment size for CBV descriptor heaps
// This can vary by GPU so we need to query for it
cbvSrvDescriptorHeapIncrementSize =
(SIZE_T)device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
// Describe the descriptor heap we want to make
D3D12_DESCRIPTOR_HEAP_DESC dhDesc = {};
dhDesc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE; // Shaders can see these!
dhDesc.NodeMask = 0; // Node here means physical GPU - we only have 1 so its index is 0
dhDesc.NumDescriptors = maxConstantBuffers + maxTextureDescriptors; // How many descriptors will we need?
dhDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; // This heap can store CBVs, SRVs and UAVs
device->CreateDescriptorHeap(&dhDesc, IID_PPV_ARGS(cbvSrvDescriptorHeap.GetAddressOf()));
// Assume the first CBV will be at the beginning of the heap
// This will increase as we use more CBVs and will wrap back to 0
cbvDescriptorOffset = 0;
// Assume the first SRV will be after all possible CBVs
srvDescriptorOffset = maxConstantBuffers;
}
// --------------------------------------------------------
// Helper for creating a basic buffer
//
// size - How big should the buffer be in bytes
// heapType - What kind of D3D12 heap? Default is D3D12_HEAP_TYPE_DEFAULT
// state - What state should the resulting resource be in? Default is D3D12_RESOURCE_STATE_COMMON
// flags - Any special flags? Default is D3D12_RESOURCE_FLAG_NONE
// alignment - What's the buffer alignment? Default is 0
// --------------------------------------------------------
Microsoft::WRL::ComPtr<ID3D12Resource> DX12Helper::CreateBuffer(UINT64 size, D3D12_HEAP_TYPE heapType, D3D12_RESOURCE_STATES state, D3D12_RESOURCE_FLAGS flags, UINT64 alignment)
{
Microsoft::WRL::ComPtr<ID3D12Resource> buffer;
// Describe the heap
D3D12_HEAP_PROPERTIES heapDesc = {};
heapDesc.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heapDesc.CreationNodeMask = 1;
heapDesc.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
heapDesc.Type = heapType;
heapDesc.VisibleNodeMask = 1;
// Describe the resource
D3D12_RESOURCE_DESC desc = {};
desc.Alignment = alignment;
desc.DepthOrArraySize = 1;
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc.Flags = flags;
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.Height = 1;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
desc.MipLevels = 1;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Width = size; // Size of the buffer
// Create the buffer
device->CreateCommittedResource(&heapDesc, D3D12_HEAP_FLAG_NONE, &desc, state, 0, IID_PPV_ARGS(buffer.GetAddressOf()));
return buffer;
}
void DX12Helper::ReserveSrvUavDescriptorHeapSlot(D3D12_CPU_DESCRIPTOR_HANDLE* reservedCPUHandle, D3D12_GPU_DESCRIPTOR_HANDLE* reservedGPUHandle)
{
// Grab the actual heap start on both sides and offset to the next open SRV/UAV portion
D3D12_CPU_DESCRIPTOR_HANDLE cpuHandle = cbvSrvDescriptorHeap->GetCPUDescriptorHandleForHeapStart();
D3D12_GPU_DESCRIPTOR_HANDLE gpuHandle = cbvSrvDescriptorHeap->GetGPUDescriptorHandleForHeapStart();
cpuHandle.ptr += (SIZE_T)srvDescriptorOffset * cbvSrvDescriptorHeapIncrementSize;
gpuHandle.ptr += (SIZE_T)srvDescriptorOffset * cbvSrvDescriptorHeapIncrementSize;
// Set the requested handle(s)
if (reservedCPUHandle) { *reservedCPUHandle = cpuHandle; }
if (reservedGPUHandle) { *reservedGPUHandle = gpuHandle; }
// Update the overall offset
srvDescriptorOffset++;
}
// --------------------------------------------------------
// Helper for creating a static buffer that will get
// data once and remain immutable
//
// dataStride - The size of one piece of data in the buffer (like a vertex)
// dataCount - How many pieces of data (like how many vertices)
// data - Pointer to the data itself
// --------------------------------------------------------
Microsoft::WRL::ComPtr<ID3D12Resource> DX12Helper::CreateStaticBuffer(
unsigned int dataStride, unsigned int dataCount, void* data)
{
//temporary command allocator so we aren't trying to reset a command allocator as its in use
// Note: This certainly isn't efficient, but hopefully this only
// happens during start-up. Otherwise, refactor this to use
// the existing list and allocator(s).
Microsoft::WRL::ComPtr<ID3D12CommandAllocator> localCommandAllocator;
Microsoft::WRL::ComPtr<ID3D12GraphicsCommandList> localCommandList;
device->CreateCommandAllocator(
D3D12_COMMAND_LIST_TYPE_DIRECT,
IID_PPV_ARGS(localCommandAllocator.GetAddressOf()));
device->CreateCommandList(
0, // Which physical GPU will handle these tasks? 0 for single GPU setup
D3D12_COMMAND_LIST_TYPE_DIRECT, // Type of command list - direct is for standard API calls
localCommandAllocator.Get(), // The allocator for this list (to start)
0, // Initial pipeline state - none for now
IID_PPV_ARGS(localCommandList.GetAddressOf()));
// The overall buffer we'll be creating
Microsoft::WRL::ComPtr<ID3D12Resource> buffer;
// Describes the final heap
D3D12_HEAP_PROPERTIES props = {};
props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
props.CreationNodeMask = 1;
props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
props.Type = D3D12_HEAP_TYPE_DEFAULT;
props.VisibleNodeMask = 1;
D3D12_RESOURCE_DESC desc = {};
desc.Alignment = 0;
desc.DepthOrArraySize = 1;
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc.Flags = D3D12_RESOURCE_FLAG_NONE;
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.Height = 1; // Assuming this is a regular buffer, not a texture
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
desc.MipLevels = 1;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.Width = dataStride * dataCount; // Size of the buffer
device->CreateCommittedResource(
&props,
D3D12_HEAP_FLAG_NONE,
&desc,
D3D12_RESOURCE_STATE_COMMON, // Will eventually be "common", but we're copying first
0,
IID_PPV_ARGS(buffer.GetAddressOf()));
// Now create an intermediate upload heap for copying initial data
D3D12_HEAP_PROPERTIES uploadProps = {};
uploadProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
uploadProps.CreationNodeMask = 1;
uploadProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
uploadProps.Type = D3D12_HEAP_TYPE_UPLOAD; // Can only ever be Generic_Read state
uploadProps.VisibleNodeMask = 1;
Microsoft::WRL::ComPtr<ID3D12Resource> uploadHeap;
device->CreateCommittedResource(
&uploadProps,
D3D12_HEAP_FLAG_NONE,
&desc,
D3D12_RESOURCE_STATE_GENERIC_READ,
0,
IID_PPV_ARGS(uploadHeap.GetAddressOf()));
// Do a straight map/memcpy/unmap
void* gpuAddress = 0;
uploadHeap->Map(0, 0, &gpuAddress);
memcpy(gpuAddress, data, dataStride * dataCount);
uploadHeap->Unmap(0, 0);
// Copy the whole buffer from uploadheap to vert buffer (using local temporary command list)
localCommandList->CopyResource(buffer.Get(), uploadHeap.Get());
// Transition the buffer to generic read for the rest of the app lifetime (presumable)
D3D12_RESOURCE_BARRIER rb = {};
rb.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
rb.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
rb.Transition.pResource = buffer.Get();
rb.Transition.StateBefore = D3D12_RESOURCE_STATE_COPY_DEST;
rb.Transition.StateAfter = D3D12_RESOURCE_STATE_GENERIC_READ;
rb.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
localCommandList->ResourceBarrier(1, &rb);
// Execute the local command list and wait for it to complete
// before returning the final buffer
localCommandList->Close();
ID3D12CommandList* list[] = { localCommandList.Get() };
commandQueue->ExecuteCommandLists(1, list);
WaitForGPU();
return buffer;
}