Files
renderdoc/util/test/demos/d3d12/d3d12_test.cpp
T

1181 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::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,
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;
}