Files
renderdoc/util/test/demos/d3d12/d3d12_test.cpp
T
Steve KarolewicsandBaldur Karlsson 67447b7b7e Improve handling of resource arrays for D3D12
When getting read only resources from the pipe state, stitch back up
according to the bindpoint mappings. When displaying resources in UI,
don't traverse unbounded arrays. Fix resource swizzle on load/sample/
gather instructions, which happens on fetch result, not on the source
operand. Added more tests for unbounded arrays and different ways to
index into arrays.
2020-03-16 20:01:30 +00:00

1191 lines
35 KiB
C++

/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019-2020 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/
#define INITGUID
#include "d3d12_test.h"
#include <stdio.h>
#include "../3rdparty/lz4/lz4.h"
#include "../renderdoc_app.h"
#include "../win32/win32_window.h"
typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY1)(REFIID, void **);
typedef HRESULT(WINAPI *PFN_CREATE_DXGI_FACTORY2)(UINT, REFIID, void **);
namespace
{
HMODULE d3d12 = NULL;
HMODULE dxgi = NULL;
HMODULE d3dcompiler = NULL;
IDXGIFactory1Ptr factory;
std::vector<IDXGIAdapterPtr> adapters;
bool d3d12on7 = false;
}; // namespace
void D3D12GraphicsTest::Prepare(int argc, char **argv)
{
GraphicsTest::Prepare(argc, argv);
static bool prepared = false;
if(!prepared)
{
prepared = true;
d3d12 = LoadLibraryA("d3d12.dll");
dxgi = LoadLibraryA("dxgi.dll");
d3dcompiler = LoadLibraryA("d3dcompiler_47.dll");
if(!d3dcompiler)
d3dcompiler = LoadLibraryA("d3dcompiler_46.dll");
if(!d3dcompiler)
d3dcompiler = LoadLibraryA("d3dcompiler_45.dll");
if(!d3dcompiler)
d3dcompiler = LoadLibraryA("d3dcompiler_44.dll");
if(!d3dcompiler)
d3dcompiler = LoadLibraryA("d3dcompiler_43.dll");
if(!d3d12)
{
d3d12 = LoadLibraryA("12on7/d3d12.dll");
d3d12on7 = (d3d12 != NULL);
}
if(d3d12)
{
PFN_CREATE_DXGI_FACTORY1 createFactory1 =
(PFN_CREATE_DXGI_FACTORY1)GetProcAddress(dxgi, "CreateDXGIFactory1");
PFN_CREATE_DXGI_FACTORY2 createFactory2 =
(PFN_CREATE_DXGI_FACTORY2)GetProcAddress(dxgi, "CreateDXGIFactory2");
HRESULT hr = E_FAIL;
if(createFactory2)
hr = createFactory2(debugDevice ? DXGI_CREATE_FACTORY_DEBUG : 0, __uuidof(IDXGIFactory1),
(void **)&factory);
else if(createFactory1)
hr = createFactory1(__uuidof(IDXGIFactory1), (void **)&factory);
if(SUCCEEDED(hr))
{
bool warp = false;
adapters = FindD3DAdapters(factory, argc, argv, warp);
if(warp && !d3d12on7)
{
IDXGIFactory4Ptr factory4 = factory;
IDXGIAdapterPtr warpAdapter;
if(factory4)
{
hr = factory4->EnumWarpAdapter(__uuidof(IDXGIAdapter), (void **)&warpAdapter);
if(SUCCEEDED(hr))
adapters.push_back(warpAdapter);
}
}
}
}
}
if(!d3d12)
Avail = "d3d12.dll is not available";
else if(!dxgi)
Avail = "dxgi.dll is not available";
else if(!d3dcompiler)
Avail = "d3dcompiler_XX.dll is not available";
else if(!factory)
Avail = "Couldn't create DXGI factory";
m_12On7 = d3d12on7;
for(int i = 0; i < argc; i++)
{
if(!strcmp(argv[i], "--gpuva") || !strcmp(argv[i], "--debug-gpu"))
{
gpuva = true;
}
}
m_Factory = factory;
}
bool D3D12GraphicsTest::Init()
{
// parse parameters here to override parameters
if(!GraphicsTest::Init())
return false;
// we can assume d3d12, dxgi and d3dcompiler are valid since we shouldn't be running the test if
// Prepare() failed
dyn_D3D12CreateDevice = (PFN_D3D12_CREATE_DEVICE)GetProcAddress(d3d12, "D3D12CreateDevice");
dyn_D3DCompile = (pD3DCompile)GetProcAddress(d3dcompiler, "D3DCompile");
dyn_D3DStripShader = (pD3DStripShader)GetProcAddress(d3dcompiler, "D3DStripShader");
dyn_D3DSetBlobPart = (pD3DSetBlobPart)GetProcAddress(d3dcompiler, "D3DSetBlobPart");
dyn_serializeRootSig = (PFN_D3D12_SERIALIZE_VERSIONED_ROOT_SIGNATURE)GetProcAddress(
d3d12, "D3D12SerializeVersionedRootSignature");
dyn_serializeRootSigOld =
(PFN_D3D12_SERIALIZE_ROOT_SIGNATURE)GetProcAddress(d3d12, "D3D12SerializeRootSignature");
if(dyn_serializeRootSig == NULL)
{
TEST_WARN("Can't get D3D12SerializeVersionedRootSignature - old version of windows?");
if(dyn_serializeRootSigOld == NULL)
{
TEST_ERROR("Can't get D3D12SerializeRootSignature!");
return false;
}
}
if(debugDevice)
{
PFN_D3D12_GET_DEBUG_INTERFACE getD3D12DebugInterface =
(PFN_D3D12_GET_DEBUG_INTERFACE)GetProcAddress(d3d12, "D3D12GetDebugInterface");
if(!getD3D12DebugInterface)
{
TEST_ERROR("Couldn't find D3D12GetDebugInterface!");
return false;
}
HRESULT hr = getD3D12DebugInterface(__uuidof(ID3D12Debug), (void **)&d3d12Debug);
if(SUCCEEDED(hr) && d3d12Debug)
{
d3d12Debug->EnableDebugLayer();
if(gpuva)
{
ID3D12Debug1Ptr debug1 = d3d12Debug;
if(debug1)
debug1->SetEnableGPUBasedValidation(true);
}
}
}
dev = CreateDevice(adapters, D3D_FEATURE_LEVEL_11_0);
if(!dev)
return false;
{
LUID luid = dev->GetAdapterLuid();
IDXGIAdapterPtr pDXGIAdapter;
HRESULT hr = EnumAdapterByLuid(dev->GetAdapterLuid(), pDXGIAdapter);
if(FAILED(hr))
{
TEST_ERROR("Couldn't get DXGI adapter by LUID from D3D device");
}
else
{
pDXGIAdapter->GetDesc(&adapterDesc);
TEST_LOG("Running D3D12 test on %ls", adapterDesc.Description);
}
}
{
D3D12_COMMAND_QUEUE_DESC desc = {};
desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
dev->CreateCommandQueue(&desc, __uuidof(ID3D12CommandQueue), (void **)&queue);
}
if(!headless)
{
Win32Window *win = new Win32Window(screenWidth, screenHeight, screenTitle);
mainWindow = win;
DXGI_SWAP_CHAIN_DESC1 swapDesc = {};
swapDesc.BufferCount = backbufferCount;
swapDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT;
swapDesc.Flags = 0;
swapDesc.Format = backbufferFmt;
swapDesc.Width = screenWidth;
swapDesc.Height = screenHeight;
swapDesc.SampleDesc.Count = 1;
swapDesc.SampleDesc.Quality = 0;
swapDesc.Scaling = DXGI_SCALING_STRETCH;
swapDesc.Stereo = FALSE;
swapDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
if(!d3d12on7)
{
IDXGIFactory4Ptr factory4 = m_Factory;
CHECK_HR(factory4->CreateSwapChainForHwnd(queue, win->wnd, &swapDesc, NULL, NULL, &swap));
CHECK_HR(swap->GetBuffer(0, __uuidof(ID3D12Resource), (void **)&bbTex[0]));
CHECK_HR(swap->GetBuffer(1, __uuidof(ID3D12Resource), (void **)&bbTex[1]));
}
else
{
D3D12_RESOURCE_DESC bbDesc;
bbDesc.Alignment = 0;
bbDesc.DepthOrArraySize = 1;
bbDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
bbDesc.Flags = D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
bbDesc.Format = backbufferFmt;
bbDesc.Height = screenHeight;
bbDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
bbDesc.MipLevels = 1;
bbDesc.SampleDesc.Count = 1;
bbDesc.SampleDesc.Quality = 0;
bbDesc.Width = screenWidth;
if(bbDesc.Format == DXGI_FORMAT_R8G8B8A8_UNORM)
bbDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
D3D12_HEAP_PROPERTIES heapProps;
heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
heapProps.CreationNodeMask = 1;
heapProps.VisibleNodeMask = 1;
CHECK_HR(dev->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &bbDesc,
D3D12_RESOURCE_STATE_PRESENT, NULL,
__uuidof(ID3D12Resource), (void **)&bbTex[0]));
CHECK_HR(dev->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &bbDesc,
D3D12_RESOURCE_STATE_PRESENT, NULL,
__uuidof(ID3D12Resource), (void **)&bbTex[1]));
}
}
dev->CreateFence(0, D3D12_FENCE_FLAG_SHARED, __uuidof(ID3D12Fence), (void **)&m_GPUSyncFence);
m_GPUSyncHandle = ::CreateEvent(NULL, FALSE, FALSE, NULL);
m_GPUSyncFence->SetName(L"GPUSync fence");
const D3D12_INPUT_CLASSIFICATION vertex = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA;
m_DefaultInputLayout = {
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, vertex, 0},
{"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, vertex, 0},
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 28, vertex, 0},
};
CHECK_HR(dev->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT,
__uuidof(ID3D12CommandAllocator), (void **)&m_Alloc));
m_Alloc->SetName(L"Command allocator");
CHECK_HR(dev->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_Alloc, NULL,
__uuidof(ID3D12GraphicsCommandList), (void **)&m_DebugList));
// command buffers are allocated opened, close it immediately.
m_DebugList->Close();
m_DebugList->SetName(L"Debug command list");
{
D3D12_DESCRIPTOR_HEAP_DESC desc;
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
desc.NodeMask = 1;
desc.NumDescriptors = 32;
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
CHECK_HR(dev->CreateDescriptorHeap(&desc, __uuidof(ID3D12DescriptorHeap), (void **)&m_RTV));
m_RTV->SetName(L"RTV heap");
desc.NumDescriptors = 1;
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
CHECK_HR(dev->CreateDescriptorHeap(&desc, __uuidof(ID3D12DescriptorHeap), (void **)&m_DSV));
m_DSV->SetName(L"DSV heap");
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
desc.NumDescriptors = 8;
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER;
CHECK_HR(dev->CreateDescriptorHeap(&desc, __uuidof(ID3D12DescriptorHeap), (void **)&m_Sampler));
m_Sampler->SetName(L"Sampler heap");
desc.NumDescriptors = 1024;
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
CHECK_HR(dev->CreateDescriptorHeap(&desc, __uuidof(ID3D12DescriptorHeap), (void **)&m_CBVUAVSRV));
m_CBVUAVSRV->SetName(L"CBV/UAV/SRV heap");
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
CHECK_HR(dev->CreateDescriptorHeap(&desc, __uuidof(ID3D12DescriptorHeap), (void **)&m_Clear));
m_CBVUAVSRV->SetName(L"UAV clear heap");
}
{
D3D12_RESOURCE_DESC readbackDesc;
readbackDesc.Alignment = 0;
readbackDesc.DepthOrArraySize = 1;
readbackDesc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
readbackDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
readbackDesc.Format = DXGI_FORMAT_UNKNOWN;
readbackDesc.Height = 1;
readbackDesc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
readbackDesc.MipLevels = 1;
readbackDesc.SampleDesc.Count = 1;
readbackDesc.SampleDesc.Quality = 0;
readbackDesc.Width = m_DebugBufferSize;
D3D12_HEAP_PROPERTIES heapProps;
heapProps.Type = D3D12_HEAP_TYPE_READBACK;
heapProps.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heapProps.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
heapProps.CreationNodeMask = 1;
heapProps.VisibleNodeMask = 1;
CHECK_HR(dev->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &readbackDesc,
D3D12_RESOURCE_STATE_COPY_DEST, NULL,
__uuidof(ID3D12Resource), (void **)&m_ReadbackBuffer));
m_ReadbackBuffer->SetName(L"Readback buffer");
heapProps.Type = D3D12_HEAP_TYPE_UPLOAD;
CHECK_HR(dev->CreateCommittedResource(&heapProps, D3D12_HEAP_FLAG_NONE, &readbackDesc,
D3D12_RESOURCE_STATE_GENERIC_READ, NULL,
__uuidof(ID3D12Resource), (void **)&m_UploadBuffer));
m_UploadBuffer->SetName(L"Upload buffer");
}
// mute useless messages
D3D12_MESSAGE_ID mute[] = {
// super spammy, mostly just perf warning
D3D12_MESSAGE_ID_CLEARRENDERTARGETVIEW_MISMATCHINGCLEARVALUE,
D3D12_MESSAGE_ID_CLEARDEPTHSTENCILVIEW_MISMATCHINGCLEARVALUE,
};
infoqueue = dev;
if(infoqueue)
{
D3D12_INFO_QUEUE_FILTER filter = {};
filter.DenyList.NumIDs = ARRAY_COUNT(mute);
filter.DenyList.pIDList = mute;
infoqueue->AddStorageFilterEntries(&filter);
}
return true;
}
HRESULT D3D12GraphicsTest::EnumAdapterByLuid(LUID luid, IDXGIAdapterPtr &pAdapter)
{
HRESULT hr = S_OK;
pAdapter = NULL;
for(UINT i = 0; i < 10; i++)
{
IDXGIAdapterPtr ad;
hr = factory->EnumAdapters(i, &ad);
if(hr == S_OK && ad)
{
DXGI_ADAPTER_DESC desc;
ad->GetDesc(&desc);
if(desc.AdapterLuid.LowPart == luid.LowPart && desc.AdapterLuid.HighPart == luid.HighPart)
{
pAdapter = ad;
return S_OK;
}
}
else
{
break;
}
}
return E_FAIL;
}
ID3D12DevicePtr D3D12GraphicsTest::CreateDevice(const std::vector<IDXGIAdapterPtr> &adaptersToTry,
D3D_FEATURE_LEVEL features)
{
HRESULT hr = S_OK;
ID3D12DevicePtr ret;
for(size_t i = 0; i < adaptersToTry.size(); ++i)
{
hr = dyn_D3D12CreateDevice(adaptersToTry[i], features, __uuidof(ID3D12Device), (void **)&ret);
if(SUCCEEDED(hr))
return ret;
}
TEST_ERROR("D3D12CreateDevice failed: %x", hr);
return ret;
}
GraphicsWindow *D3D12GraphicsTest::MakeWindow(int width, int height, const char *title)
{
return new Win32Window(width, height, title);
}
void D3D12GraphicsTest::Shutdown()
{
GPUSync();
infoqueue = NULL;
pendingCommandBuffers.clear();
freeCommandBuffers.clear();
m_ReadbackBuffer = NULL;
m_UploadBuffer = NULL;
m_RTV = m_DSV = m_CBVUAVSRV = m_Sampler = NULL;
m_Alloc = NULL;
m_DebugList = NULL;
m_GPUSyncFence = NULL;
CloseHandle(m_GPUSyncHandle);
bbTex[0] = bbTex[1] = NULL;
swap = NULL;
m_Factory = NULL;
delete mainWindow;
queue = NULL;
dev = NULL;
}
bool D3D12GraphicsTest::Running()
{
if(!FrameLimit())
return false;
return mainWindow->Update();
}
ID3D12ResourcePtr D3D12GraphicsTest::StartUsingBackbuffer(ID3D12GraphicsCommandListPtr cmd,
D3D12_RESOURCE_STATES useState)
{
ID3D12ResourcePtr bb = bbTex[texIdx];
if(useState != D3D12_RESOURCE_STATE_PRESENT)
ResourceBarrier(cmd, bbTex[texIdx], D3D12_RESOURCE_STATE_PRESENT, useState);
return bbTex[texIdx];
}
void D3D12GraphicsTest::FinishUsingBackbuffer(ID3D12GraphicsCommandListPtr cmd,
D3D12_RESOURCE_STATES usedState)
{
ID3D12ResourcePtr bb = bbTex[texIdx];
if(usedState != D3D12_RESOURCE_STATE_PRESENT)
ResourceBarrier(cmd, bbTex[texIdx], usedState, D3D12_RESOURCE_STATE_PRESENT);
texIdx = 1 - texIdx;
}
void D3D12GraphicsTest::Submit(const std::vector<ID3D12GraphicsCommandListPtr> &cmds)
{
std::vector<ID3D12CommandList *> submits;
m_GPUSyncCounter++;
for(const ID3D12GraphicsCommandListPtr &cmd : cmds)
{
pendingCommandBuffers.push_back(std::make_pair(cmd, m_GPUSyncCounter));
submits.push_back(cmd);
}
queue->ExecuteCommandLists((UINT)submits.size(), submits.data());
queue->Signal(m_GPUSyncFence, m_GPUSyncCounter);
}
void D3D12GraphicsTest::GPUSync()
{
m_GPUSyncCounter++;
CHECK_HR(queue->Signal(m_GPUSyncFence, m_GPUSyncCounter));
CHECK_HR(m_GPUSyncFence->SetEventOnCompletion(m_GPUSyncCounter, m_GPUSyncHandle));
WaitForSingleObject(m_GPUSyncHandle, 10000);
}
void D3D12GraphicsTest::Present()
{
if(swap)
{
swap->Present(0, 0);
}
else
{
ID3D12CommandQueueDownlevelPtr downlevel = queue;
ID3D12GraphicsCommandListPtr cmd = GetCommandBuffer();
Reset(cmd);
downlevel->Present(cmd, bbTex[1 - texIdx], ((Win32Window *)mainWindow)->wnd,
D3D12_DOWNLEVEL_PRESENT_FLAG_NONE);
m_GPUSyncCounter++;
queue->Signal(m_GPUSyncFence, m_GPUSyncCounter);
pendingCommandBuffers.push_back(std::make_pair(cmd, m_GPUSyncFence));
}
for(auto it = pendingCommandBuffers.begin(); it != pendingCommandBuffers.end();)
{
if(m_GPUSyncFence->GetCompletedValue() >= it->second)
{
freeCommandBuffers.push_back(it->first);
it = pendingCommandBuffers.erase(it);
}
else
{
++it;
}
}
GPUSync();
m_Alloc->Reset();
}
std::vector<byte> D3D12GraphicsTest::GetBufferData(ID3D12ResourcePtr buffer,
D3D12_RESOURCE_STATES state, uint32_t offset,
uint64_t length)
{
std::vector<byte> ret;
if(buffer == NULL)
return ret;
D3D12_RESOURCE_DESC desc = buffer->GetDesc();
D3D12_HEAP_PROPERTIES heapProps;
buffer->GetHeapProperties(&heapProps, NULL);
if(offset >= desc.Width)
{
TEST_ERROR("Out of bounds offset passed to GetBufferData");
// can't read past the end of the buffer, return empty
return ret;
}
if(length == 0)
{
length = desc.Width - offset;
}
if(length > 0 && offset + length > desc.Width)
{
TEST_WARN("Attempting to read off the end of the array. Will be clamped");
length = std::min(length, desc.Width - offset);
}
uint64_t outOffs = 0;
ret.resize((size_t)length);
// directly CPU mappable (and possibly invalid to transition and copy from), so just memcpy
if(heapProps.Type == D3D12_HEAP_TYPE_UPLOAD || heapProps.Type == D3D12_HEAP_TYPE_READBACK)
{
D3D12_RANGE range = {(size_t)offset, size_t(offset + length)};
byte *data = NULL;
CHECK_HR(buffer->Map(0, &range, (void **)&data));
memcpy(&ret[0], data + offset, (size_t)length);
range.Begin = range.End = 0;
buffer->Unmap(0, &range);
return ret;
}
m_DebugList->Reset(m_Alloc, NULL);
D3D12_RESOURCE_BARRIER barrier = {};
barrier.Transition.pResource = buffer;
barrier.Transition.StateBefore = state;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
if(barrier.Transition.StateBefore != barrier.Transition.StateAfter)
m_DebugList->ResourceBarrier(1, &barrier);
while(length > 0)
{
uint64_t chunkSize = std::min(length, m_DebugBufferSize);
m_DebugList->CopyBufferRegion(m_ReadbackBuffer, 0, buffer, offset, chunkSize);
m_DebugList->Close();
ID3D12CommandList *l = m_DebugList;
queue->ExecuteCommandLists(1, &l);
GPUSync();
m_Alloc->Reset();
D3D12_RANGE range = {0, (size_t)chunkSize};
void *data = NULL;
CHECK_HR(m_ReadbackBuffer->Map(0, &range, &data));
memcpy(&ret[(size_t)outOffs], data, (size_t)chunkSize);
range.End = 0;
m_ReadbackBuffer->Unmap(0, &range);
outOffs += chunkSize;
length -= chunkSize;
m_DebugList->Reset(m_Alloc, NULL);
}
if(barrier.Transition.StateBefore != barrier.Transition.StateAfter)
{
std::swap(barrier.Transition.StateBefore, barrier.Transition.StateAfter);
m_DebugList->ResourceBarrier(1, &barrier);
}
m_DebugList->Close();
ID3D12CommandList *l = m_DebugList;
queue->ExecuteCommandLists(1, &l);
GPUSync();
m_Alloc->Reset();
return ret;
}
void D3D12GraphicsTest::SetBufferData(ID3D12ResourcePtr buffer, D3D12_RESOURCE_STATES state,
const byte *data, uint64_t len)
{
D3D12_RESOURCE_DESC desc = buffer->GetDesc();
D3D12_HEAP_PROPERTIES heapProps;
buffer->GetHeapProperties(&heapProps, NULL);
if(len > desc.Width)
{
TEST_ERROR("Can't upload more data than buffer contains");
return;
}
// directly CPU mappable (and possibly invalid to transition and copy from), so just memcpy
if(heapProps.Type == D3D12_HEAP_TYPE_UPLOAD || heapProps.Type == D3D12_HEAP_TYPE_READBACK)
{
D3D12_RANGE range = {0, 0};
byte *ptr = NULL;
CHECK_HR(buffer->Map(0, &range, (void **)&ptr));
memcpy(ptr, data, (size_t)len);
range.End = (size_t)len;
buffer->Unmap(0, &range);
return;
}
m_DebugList->Reset(m_Alloc, NULL);
D3D12_RESOURCE_BARRIER barrier = {};
barrier.Transition.pResource = buffer;
barrier.Transition.StateBefore = state;
barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
if(barrier.Transition.StateBefore != barrier.Transition.StateAfter)
m_DebugList->ResourceBarrier(1, &barrier);
uint64_t offset = 0;
while(len > 0)
{
uint64_t chunkSize = std::min(len, m_DebugBufferSize);
{
D3D12_RANGE range = {0, 0};
void *ptr = NULL;
CHECK_HR(m_UploadBuffer->Map(0, &range, &ptr));
memcpy(ptr, data + offset, (size_t)chunkSize);
range.End = (size_t)chunkSize;
m_UploadBuffer->Unmap(0, &range);
}
m_DebugList->CopyBufferRegion(buffer, offset, m_UploadBuffer, 0, chunkSize);
m_DebugList->Close();
ID3D12CommandList *l = m_DebugList;
queue->ExecuteCommandLists(1, &l);
GPUSync();
m_Alloc->Reset();
offset += chunkSize;
len -= chunkSize;
m_DebugList->Reset(m_Alloc, NULL);
}
if(barrier.Transition.StateBefore != barrier.Transition.StateAfter)
{
std::swap(barrier.Transition.StateBefore, barrier.Transition.StateAfter);
m_DebugList->ResourceBarrier(1, &barrier);
}
m_DebugList->Close();
ID3D12CommandList *l = m_DebugList;
queue->ExecuteCommandLists(1, &l);
GPUSync();
m_Alloc->Reset();
}
void D3D12GraphicsTest::pushMarker(ID3D12GraphicsCommandListPtr cmd, const std::string &name)
{
// D3D debug layer spams un-mutable errors if we call this. Don't do anything unless renderdoc is
// attached
if(rdoc)
cmd->BeginEvent(1, name.data(), (UINT)name.size());
}
void D3D12GraphicsTest::setMarker(ID3D12GraphicsCommandListPtr cmd, const std::string &name)
{
if(rdoc)
cmd->SetMarker(1, name.data(), (UINT)name.size());
}
void D3D12GraphicsTest::popMarker(ID3D12GraphicsCommandListPtr cmd)
{
if(rdoc)
cmd->EndEvent();
}
void D3D12GraphicsTest::ResourceBarrier(ID3D12GraphicsCommandListPtr cmd, ID3D12ResourcePtr res,
D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after)
{
D3D12_RESOURCE_BARRIER barrier;
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barrier.Transition.pResource = res;
barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barrier.Transition.StateBefore = before;
barrier.Transition.StateAfter = after;
cmd->ResourceBarrier(1, &barrier);
}
void D3D12GraphicsTest::ResourceBarrier(ID3D12ResourcePtr res, D3D12_RESOURCE_STATES before,
D3D12_RESOURCE_STATES after)
{
ID3D12GraphicsCommandListPtr cmd = GetCommandBuffer();
Reset(cmd);
ResourceBarrier(cmd, res, before, after);
cmd->Close();
Submit({cmd});
}
void D3D12GraphicsTest::IASetVertexBuffer(ID3D12GraphicsCommandListPtr cmd, ID3D12ResourcePtr vb,
UINT stride, UINT offset)
{
D3D12_VERTEX_BUFFER_VIEW view;
view.BufferLocation = vb->GetGPUVirtualAddress() + offset;
view.SizeInBytes = UINT(vb->GetDesc().Width - offset);
view.StrideInBytes = stride;
cmd->IASetVertexBuffers(0, 1, &view);
}
void D3D12GraphicsTest::IASetIndexBuffer(ID3D12GraphicsCommandListPtr cmd, ID3D12ResourcePtr ib,
DXGI_FORMAT fmt, UINT offset)
{
D3D12_INDEX_BUFFER_VIEW view;
view.BufferLocation = ib->GetGPUVirtualAddress() + offset;
view.Format = fmt;
view.SizeInBytes = UINT(ib->GetDesc().Width - offset);
cmd->IASetIndexBuffer(&view);
}
void D3D12GraphicsTest::ClearRenderTargetView(ID3D12GraphicsCommandListPtr cmd,
ID3D12ResourcePtr rt, Vec4f col)
{
cmd->ClearRenderTargetView(MakeRTV(rt).CreateCPU(0), &col.x, 0, NULL);
}
void D3D12GraphicsTest::ClearRenderTargetView(ID3D12GraphicsCommandListPtr cmd,
D3D12_CPU_DESCRIPTOR_HANDLE rt, Vec4f col)
{
cmd->ClearRenderTargetView(rt, &col.x, 0, NULL);
}
void D3D12GraphicsTest::ClearDepthStencilView(ID3D12GraphicsCommandListPtr cmd, ID3D12ResourcePtr dsv,
D3D12_CLEAR_FLAGS flags, float depth, UINT8 stencil)
{
MakeDSV(dsv).CreateCPU(0);
cmd->ClearDepthStencilView(m_DSV->GetCPUDescriptorHandleForHeapStart(), flags, depth, stencil, 0,
NULL);
}
void D3D12GraphicsTest::RSSetViewport(ID3D12GraphicsCommandListPtr cmd, D3D12_VIEWPORT view)
{
cmd->RSSetViewports(1, &view);
}
void D3D12GraphicsTest::RSSetScissorRect(ID3D12GraphicsCommandListPtr cmd, D3D12_RECT rect)
{
cmd->RSSetScissorRects(1, &rect);
}
void D3D12GraphicsTest::OMSetRenderTargets(ID3D12GraphicsCommandListPtr cmd,
const std::vector<ID3D12ResourcePtr> &rtvs,
ID3D12ResourcePtr dsv)
{
std::vector<D3D12_CPU_DESCRIPTOR_HANDLE> handles;
handles.resize(rtvs.size());
for(size_t i = 0; i < rtvs.size(); i++)
handles[i] = MakeRTV(rtvs[i]).CreateCPU((uint32_t)i);
if(dsv)
OMSetRenderTargets(cmd, handles, MakeDSV(dsv).CreateCPU(0));
else
OMSetRenderTargets(cmd, handles, {});
}
void D3D12GraphicsTest::OMSetRenderTargets(ID3D12GraphicsCommandListPtr cmd,
const std::vector<D3D12_CPU_DESCRIPTOR_HANDLE> &rtvs,
D3D12_CPU_DESCRIPTOR_HANDLE dsv)
{
cmd->OMSetRenderTargets((UINT)rtvs.size(), rtvs.data(), FALSE, dsv.ptr ? &dsv : NULL);
}
ID3DBlobPtr D3D12GraphicsTest::Compile(std::string src, std::string entry, std::string profile,
ID3DBlob **unstripped)
{
ID3DBlobPtr blob = NULL;
ID3DBlobPtr error = NULL;
HRESULT hr = dyn_D3DCompile(
src.c_str(), src.length(), "", NULL, NULL, entry.c_str(), profile.c_str(),
D3DCOMPILE_WARNINGS_ARE_ERRORS | D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION |
D3DCOMPILE_OPTIMIZATION_LEVEL0 | D3DCOMPILE_ENABLE_UNBOUNDED_DESCRIPTOR_TABLES,
0, &blob, &error);
if(FAILED(hr))
{
TEST_ERROR("Failed to compile shader, error %x / %s", hr,
error ? (char *)error->GetBufferPointer() : "Unknown");
blob = NULL;
error = NULL;
return NULL;
}
if(unstripped)
{
blob.AddRef();
*unstripped = blob.GetInterfacePtr();
ID3DBlobPtr stripped = NULL;
dyn_D3DStripShader(blob->GetBufferPointer(), blob->GetBufferSize(),
D3DCOMPILER_STRIP_REFLECTION_DATA | D3DCOMPILER_STRIP_DEBUG_INFO, &stripped);
blob = NULL;
return stripped;
}
return blob;
}
void D3D12GraphicsTest::WriteBlob(std::string name, ID3DBlob *blob, bool compress)
{
FILE *f = NULL;
fopen_s(&f, name.c_str(), "wb");
if(f == NULL)
{
TEST_ERROR("Can't open blob file to write %s", name.c_str());
return;
}
if(compress)
{
int uncompSize = (int)blob->GetBufferSize();
char *compBuf = new char[uncompSize];
int compressedSize = LZ4_compress_default((const char *)blob->GetBufferPointer(), compBuf,
uncompSize, uncompSize);
fwrite(compBuf, 1, compressedSize, f);
delete[] compBuf;
}
else
{
fwrite(blob->GetBufferPointer(), 1, blob->GetBufferSize(), f);
}
fclose(f);
}
ID3DBlobPtr D3D12GraphicsTest::SetBlobPath(std::string name, ID3DBlob *blob)
{
ID3DBlobPtr newBlob = NULL;
const GUID RENDERDOC_ShaderDebugMagicValue = RENDERDOC_ShaderDebugMagicValue_struct;
std::string pathData;
for(size_t i = 0; i < sizeof(RENDERDOC_ShaderDebugMagicValue); i++)
pathData.push_back(' ');
pathData += name;
memcpy(&pathData[0], &RENDERDOC_ShaderDebugMagicValue, sizeof(RENDERDOC_ShaderDebugMagicValue));
dyn_D3DSetBlobPart(blob->GetBufferPointer(), blob->GetBufferSize(), D3D_BLOB_PRIVATE_DATA, 0,
pathData.c_str(), pathData.size() + 1, &newBlob);
return newBlob;
}
void D3D12GraphicsTest::SetBlobPath(std::string name, ID3D12DeviceChild *shader)
{
const GUID RENDERDOC_ShaderDebugMagicValue = RENDERDOC_ShaderDebugMagicValue_struct;
shader->SetPrivateData(RENDERDOC_ShaderDebugMagicValue, (UINT)name.size() + 1, name.c_str());
}
ID3D12GraphicsCommandListPtr D3D12GraphicsTest::GetCommandBuffer()
{
if(freeCommandBuffers.empty())
{
ID3D12GraphicsCommandListPtr list = NULL;
CHECK_HR(dev->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_Alloc, NULL,
__uuidof(ID3D12GraphicsCommandList), (void **)&list));
// list starts opened, close it
list->Close();
freeCommandBuffers.push_back(list);
}
ID3D12GraphicsCommandListPtr ret = freeCommandBuffers.back();
freeCommandBuffers.pop_back();
return ret;
}
void D3D12GraphicsTest::Reset(ID3D12GraphicsCommandListPtr cmd)
{
cmd->Reset(m_Alloc, NULL);
}
ID3D12RootSignaturePtr D3D12GraphicsTest::MakeSig(const std::vector<D3D12_ROOT_PARAMETER1> &params,
D3D12_ROOT_SIGNATURE_FLAGS Flags,
UINT NumStaticSamplers,
const D3D12_STATIC_SAMPLER_DESC *StaticSamplers)
{
ID3DBlobPtr blob;
if(dyn_serializeRootSig == NULL)
{
D3D12_ROOT_SIGNATURE_DESC desc;
desc.Flags = Flags;
desc.NumStaticSamplers = NumStaticSamplers;
desc.pStaticSamplers = StaticSamplers;
desc.NumParameters = (UINT)params.size();
std::vector<D3D12_ROOT_PARAMETER> params_1_0;
params_1_0.resize(params.size());
for(size_t i = 0; i < params.size(); i++)
{
params_1_0[i].ShaderVisibility = params[i].ShaderVisibility;
params_1_0[i].ParameterType = params[i].ParameterType;
if(params[i].ParameterType == D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS)
{
params_1_0[i].Constants = params[i].Constants;
}
else if(params[i].ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE)
{
params_1_0[i].DescriptorTable.NumDescriptorRanges =
params[i].DescriptorTable.NumDescriptorRanges;
D3D12_DESCRIPTOR_RANGE *dst =
new D3D12_DESCRIPTOR_RANGE[params[i].DescriptorTable.NumDescriptorRanges];
params_1_0[i].DescriptorTable.pDescriptorRanges = dst;
for(UINT r = 0; r < params[i].DescriptorTable.NumDescriptorRanges; r++)
{
dst[r].BaseShaderRegister =
params[i].DescriptorTable.pDescriptorRanges[r].BaseShaderRegister;
dst[r].NumDescriptors = params[i].DescriptorTable.pDescriptorRanges[r].NumDescriptors;
dst[r].OffsetInDescriptorsFromTableStart =
params[i].DescriptorTable.pDescriptorRanges[r].OffsetInDescriptorsFromTableStart;
dst[r].RangeType = params[i].DescriptorTable.pDescriptorRanges[r].RangeType;
dst[r].RegisterSpace = params[i].DescriptorTable.pDescriptorRanges[r].RegisterSpace;
if(params[i].DescriptorTable.pDescriptorRanges[r].Flags !=
(D3D12_DESCRIPTOR_RANGE_FLAG_DATA_VOLATILE |
D3D12_DESCRIPTOR_RANGE_FLAG_DESCRIPTORS_VOLATILE))
TEST_WARN("Losing information when reducing down to 1.0 root signature");
}
}
else
{
params_1_0[i].Descriptor.RegisterSpace = params[i].Descriptor.RegisterSpace;
params_1_0[i].Descriptor.ShaderRegister = params[i].Descriptor.ShaderRegister;
if(params[i].Descriptor.Flags != D3D12_ROOT_DESCRIPTOR_FLAG_DATA_VOLATILE)
TEST_WARN("Losing information when reducing down to 1.0 root signature");
}
}
desc.pParameters = &params_1_0[0];
ID3DBlobPtr errBlob;
HRESULT hr = dyn_serializeRootSigOld(&desc, D3D_ROOT_SIGNATURE_VERSION_1, &blob, &errBlob);
for(size_t i = 0; i < params_1_0.size(); i++)
if(params_1_0[i].ParameterType == D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE)
delete[] params_1_0[i].DescriptorTable.pDescriptorRanges;
if(FAILED(hr))
{
std::string errors = (char *)errBlob->GetBufferPointer();
std::string logerror = errors;
if(logerror.length() > 1024)
logerror = logerror.substr(0, 1024) + "...";
TEST_ERROR("Root signature serialize error:\n%s", logerror.c_str());
return NULL;
}
}
else
{
D3D12_VERSIONED_ROOT_SIGNATURE_DESC verdesc;
verdesc.Version = D3D_ROOT_SIGNATURE_VERSION_1_1;
D3D12_ROOT_SIGNATURE_DESC1 &desc = verdesc.Desc_1_1;
desc.Flags = Flags;
desc.NumStaticSamplers = NumStaticSamplers;
desc.pStaticSamplers = StaticSamplers;
desc.NumParameters = (UINT)params.size();
desc.pParameters = &params[0];
ID3DBlobPtr errBlob = NULL;
HRESULT hr = dyn_serializeRootSig(&verdesc, &blob, &errBlob);
if(FAILED(hr))
{
std::string errors = (char *)errBlob->GetBufferPointer();
std::string logerror = errors;
if(logerror.length() > 1024)
logerror = logerror.substr(0, 1024) + "...";
TEST_ERROR("Root signature serialize error:\n%s", logerror.c_str());
return NULL;
}
}
if(!blob)
return NULL;
ID3D12RootSignaturePtr ret;
CHECK_HR(dev->CreateRootSignature(0, blob->GetBufferPointer(), blob->GetBufferSize(),
__uuidof(ID3D12RootSignature), (void **)&ret));
return ret;
}
ID3D12CommandSignaturePtr D3D12GraphicsTest::MakeCommandSig(
ID3D12RootSignaturePtr rootSig, const std::vector<D3D12_INDIRECT_ARGUMENT_DESC> &params)
{
D3D12_COMMAND_SIGNATURE_DESC desc = {};
desc.pArgumentDescs = params.data();
desc.NumArgumentDescs = (UINT)params.size();
for(const D3D12_INDIRECT_ARGUMENT_DESC &p : params)
{
switch(p.Type)
{
case D3D12_INDIRECT_ARGUMENT_TYPE_DRAW:
desc.ByteStride += sizeof(D3D12_DRAW_ARGUMENTS);
break;
case D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED:
desc.ByteStride += sizeof(D3D12_DRAW_INDEXED_ARGUMENTS);
break;
case D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH:
desc.ByteStride += sizeof(D3D12_DISPATCH_ARGUMENTS);
break;
case D3D12_INDIRECT_ARGUMENT_TYPE_VERTEX_BUFFER_VIEW:
desc.ByteStride += sizeof(D3D12_VERTEX_BUFFER_VIEW);
break;
case D3D12_INDIRECT_ARGUMENT_TYPE_INDEX_BUFFER_VIEW:
desc.ByteStride += sizeof(D3D12_INDEX_BUFFER_VIEW);
break;
case D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT:
desc.ByteStride += p.Constant.Num32BitValuesToSet * sizeof(uint32_t);
break;
case D3D12_INDIRECT_ARGUMENT_TYPE_CONSTANT_BUFFER_VIEW:
desc.ByteStride += sizeof(D3D12_GPU_VIRTUAL_ADDRESS);
break;
case D3D12_INDIRECT_ARGUMENT_TYPE_SHADER_RESOURCE_VIEW:
desc.ByteStride += sizeof(D3D12_GPU_VIRTUAL_ADDRESS);
break;
case D3D12_INDIRECT_ARGUMENT_TYPE_UNORDERED_ACCESS_VIEW:
desc.ByteStride += sizeof(D3D12_GPU_VIRTUAL_ADDRESS);
break;
}
}
ID3D12CommandSignaturePtr ret;
CHECK_HR(
dev->CreateCommandSignature(&desc, rootSig, __uuidof(ID3D12CommandSignature), (void **)&ret));
return ret;
}