Improvements in the sample app

Now accepting command line parameters. Command line syntax:
-h, --Help   Print this information
-l, --List   Print list of GPUs
-g S, --GPU S   Select GPU with name containing S
-i N, --GPUIndex N   Select GPU index N

Added printing of adapter and device capabilities related to memory at startup.
Added struct GPUSelection, class DXGIUsage, struct CommandLineParameters.
Fixed macros CHECK_BOOL,. CHECK_HR.
Improved error handling.
This commit is contained in:
Adam Sawicki 2021-03-11 12:26:59 +01:00
parent 8f26fbb39e
commit 2b70645a93
3 changed files with 319 additions and 111 deletions

View File

@ -55,13 +55,14 @@ typedef std::chrono::high_resolution_clock::duration duration;
#define STRINGIZE(x) STRINGIZE2(x)
#define STRINGIZE2(x) #x
#define LINE_STRING STRINGIZE(__LINE__)
#define FAIL(msg) do { \
assert(0 && msg); \
throw std::runtime_error(msg); \
} while(false)
#define CHECK_BOOL(expr) do { if(!(expr)) FAIL(__FILE__ "(" LINE_STRING "): !( " #expr " )"); } while(false)
#define CHECK_HR(expr) do { if(FAILED(expr)) FAIL(__FILE__ "(" LINE_STRING "): FAILED( " #expr " )"); } while(false)
#define CHECK_BOOL(expr) do { if(!(expr)) { \
assert(0 && #expr); \
throw std::runtime_error(__FILE__ "(" LINE_STRING "): ( " #expr " ) == false"); \
} } while(false)
#define CHECK_HR(expr) do { if(FAILED(expr)) { \
assert(0 && #expr); \
throw std::runtime_error(__FILE__ "(" LINE_STRING "): FAILED( " #expr " )"); \
} } while(false)
template <typename T>
inline constexpr T CeilDiv(T x, T y)

View File

@ -34,6 +34,15 @@ namespace PS
#include "Shaders\PS_Compiled.h"
}
enum class ExitCode : int
{
GPUList = 2,
Help = 1,
Success = 0,
RuntimeError = -1,
CommandLineError = -2,
};
static const wchar_t * const CLASS_NAME = L"D3D12MemAllocSample";
static const wchar_t * const WINDOW_TITLE = L"D3D12 Memory Allocator Sample";
static const int SIZE_X = 1024;
@ -54,11 +63,31 @@ static D3D12MA::ALLOCATION_CALLBACKS g_AllocationCallbacks = {}; // Used only wh
static HINSTANCE g_Instance;
static HWND g_Wnd;
struct GPUSelection
{
UINT32 Index = UINT32_MAX;
std::wstring Substring;
};
class DXGIUsage
{
public:
void Init();
IDXGIFactory4* GetDXGIFactory() const { return m_DXGIFactory; }
void PrintAdapterList() const;
// If failed, returns null pointer.
CComPtr<IDXGIAdapter1> CreateAdapter(const GPUSelection& GPUSelection) const;
private:
CComPtr<IDXGIFactory4> m_DXGIFactory;
};
static UINT64 g_TimeOffset; // In ms.
static UINT64 g_TimeValue; // Time since g_TimeOffset, in ms.
static float g_Time; // g_TimeValue converted to float, in seconds.
static float g_TimeDelta;
static DXGIUsage* g_DXGIUsage;
static CComPtr<ID3D12Device> g_Device;
static D3D12MA::Allocator* g_Allocator;
@ -152,6 +181,41 @@ static void CustomFree(void* pMemory, void* pUserData)
}
}
struct CommandLineParameters
{
bool m_Help = false;
bool m_List = false;
GPUSelection m_GPUSelection;
bool Parse(int argc, wchar_t** argv)
{
for(int i = 1; i < argc; ++i)
{
if(_wcsicmp(argv[i], L"-h") == 0 || _wcsicmp(argv[i], L"--Help") == 0)
{
m_Help = true;
}
else if(_wcsicmp(argv[i], L"-l") == 0 || _wcsicmp(argv[i], L"--List") == 0)
{
m_List = true;
}
else if((_wcsicmp(argv[i], L"-g") == 0 || _wcsicmp(argv[i], L"--GPU") == 0) && i + 1 < argc)
{
m_GPUSelection.Substring = argv[i + 1];
++i;
}
else if((_wcsicmp(argv[i], L"-i") == 0 || _wcsicmp(argv[i], L"--GPUIndex") == 0) && i + 1 < argc)
{
m_GPUSelection.Index = _wtoi(argv[i + 1]);
++i;
}
else
return false;
}
return true;
}
} g_CommandLineParameters;
static void SetDefaultRasterizerDesc(D3D12_RASTERIZER_DESC& outDesc)
{
outDesc.FillMode = D3D12_FILL_MODE_SOLID;
@ -368,36 +432,197 @@ inline UINT64 UpdateSubresources(
return Result;
}
void InitD3D() // initializes direct3d 12
void DXGIUsage::Init()
{
IDXGIFactory4* dxgiFactory;
CHECK_HR( CreateDXGIFactory1(IID_PPV_ARGS(&dxgiFactory)) );
g_Instance = (HINSTANCE)GetModuleHandle(NULL);
CoInitialize(NULL);
CHECK_HR( CreateDXGIFactory1(IID_PPV_ARGS(&m_DXGIFactory)) );
}
IDXGIAdapter1* adapter = nullptr; // adapters are the graphics card (this includes the embedded graphics on the motherboard)
int adapterIndex = 0; // we'll start looking for directx 12 compatible graphics devices starting at index 0
bool adapterFound = false; // set this to true when a good one was found
// find first hardware gpu that supports d3d 12
while (dxgiFactory->EnumAdapters1(adapterIndex, &adapter) != DXGI_ERROR_NOT_FOUND)
void DXGIUsage::PrintAdapterList() const
{
UINT index = 0;
CComPtr<IDXGIAdapter1> adapter;
while (m_DXGIFactory->EnumAdapters1(index, &adapter) != DXGI_ERROR_NOT_FOUND)
{
DXGI_ADAPTER_DESC1 desc;
adapter->GetDesc1(&desc);
if ((desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) == 0)
const bool isSoftware = (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE) != 0;
const wchar_t* const suffix = isSoftware ? L" (SOFTWARE)" : L"";
wprintf(L"Adapter %u: %s%s\n", index, desc.Description, suffix);
adapter.Release();
++index;
}
}
CComPtr<IDXGIAdapter1> DXGIUsage::CreateAdapter(const GPUSelection& GPUSelection) const
{
CComPtr<IDXGIAdapter1> adapter;
if(GPUSelection.Index != UINT32_MAX)
{
// Cannot specify both index and name.
if(!GPUSelection.Substring.empty())
{
HRESULT hr = D3D12CreateDevice(adapter, MY_D3D_FEATURE_LEVEL, _uuidof(ID3D12Device), nullptr);
if (SUCCEEDED(hr))
return adapter;
}
CHECK_HR(m_DXGIFactory->EnumAdapters1(GPUSelection.Index, &adapter));
return adapter;
}
if(!GPUSelection.Substring.empty())
{
CComPtr<IDXGIAdapter1> tmpAdapter;
for(UINT i = 0; m_DXGIFactory->EnumAdapters1(i, &tmpAdapter) != DXGI_ERROR_NOT_FOUND; ++i)
{
DXGI_ADAPTER_DESC1 desc;
tmpAdapter->GetDesc1(&desc);
if(StrStrI(desc.Description, GPUSelection.Substring.c_str()))
{
adapterFound = true;
break;
// Second matching adapter found - error.
if(adapter)
{
adapter.Release();
return adapter;
}
// First matching adapter found.
adapter = std::move(tmpAdapter);
}
else
{
tmpAdapter.Release();
}
}
adapter->Release();
adapterIndex++;
// Found or not, return it.
return adapter;
}
assert(adapterFound);
// Select first one.
m_DXGIFactory->EnumAdapters1(0, &adapter);
return adapter;
}
static const uint32_t VENDOR_ID_AMD = 0x1002;
static const uint32_t VENDOR_ID_NVIDIA = 0x10DE;
static const uint32_t VENDOR_ID_INTEL = 0x8086;
static const wchar_t* VendorIDToStr(uint32_t vendorID)
{
switch(vendorID)
{
case 0x10001: return L"VIV";
case 0x10002: return L"VSI";
case 0x10003: return L"KAZAN";
case 0x10004: return L"CODEPLAY";
case 0x10005: return L"MESA";
case 0x10006: return L"POCL";
case VENDOR_ID_AMD: return L"AMD";
case VENDOR_ID_NVIDIA: return L"NVIDIA";
case VENDOR_ID_INTEL: return L"Intel";
case 0x1010: return L"ImgTec";
case 0x13B5: return L"ARM";
case 0x5143: return L"Qualcomm";
}
return L"";
}
static std::wstring SizeToStr(size_t size)
{
if(size == 0)
return L"0";
wchar_t result[32];
double size2 = (double)size;
if (size2 >= 1024.0*1024.0*1024.0*1024.0)
{
swprintf_s(result, L"%.2f TB", size2 / (1024.0*1024.0*1024.0*1024.0));
}
else if (size2 >= 1024.0*1024.0*1024.0)
{
swprintf_s(result, L"%.2f GB", size2 / (1024.0*1024.0*1024.0));
}
else if (size2 >= 1024.0*1024.0)
{
swprintf_s(result, L"%.2f MB", size2 / (1024.0*1024.0));
}
else if (size2 >= 1024.0)
{
swprintf_s(result, L"%.2f KB", size2 / 1024.0);
}
else
swprintf_s(result, L"%llu B", size);
return result;
}
static void PrintAdapterInformation(IDXGIAdapter1* adapter)
{
DXGI_ADAPTER_DESC1 adapterDesc = {};
adapter->GetDesc1(&adapterDesc);
wprintf(L"DXGI_ADAPTER_DESC1:\n");
wprintf(L" Description = %s\n", adapterDesc.Description);
wprintf(L" VendorId = 0x%X (%s)\n", adapterDesc.VendorId, VendorIDToStr(adapterDesc.VendorId));
wprintf(L" DeviceId = 0x%X\n", adapterDesc.DeviceId);
wprintf(L" SubSysId = 0x%X\n", adapterDesc.SubSysId);
wprintf(L" Revision = 0x%X\n", adapterDesc.Revision);
wprintf(L" DedicatedVideoMemory = %zu B (%s)\n", adapterDesc.DedicatedVideoMemory, SizeToStr(adapterDesc.DedicatedVideoMemory).c_str());
wprintf(L" DedicatedSystemMemory = %zu B (%s)\n", adapterDesc.DedicatedSystemMemory, SizeToStr(adapterDesc.DedicatedSystemMemory).c_str());
wprintf(L" SharedSystemMemory = %zu B (%s)\n", adapterDesc.SharedSystemMemory, SizeToStr(adapterDesc.SharedSystemMemory).c_str());
const D3D12_FEATURE_DATA_D3D12_OPTIONS& options = g_Allocator->GetD3D12Options();
wprintf(L"D3D12_FEATURE_DATA_D3D12_OPTIONS:\n");
wprintf(L" StandardSwizzle64KBSupported = %u\n", options.StandardSwizzle64KBSupported ? 1 : 0);
wprintf(L" CrossAdapterRowMajorTextureSupported = %u\n", options.CrossAdapterRowMajorTextureSupported ? 1 : 0);
switch(options.ResourceHeapTier)
{
case D3D12_RESOURCE_HEAP_TIER_1:
wprintf(L" ResourceHeapTier = D3D12_RESOURCE_HEAP_TIER_1\n");
break;
case D3D12_RESOURCE_HEAP_TIER_2:
wprintf(L" ResourceHeapTier = D3D12_RESOURCE_HEAP_TIER_2\n");
break;
default:
assert(0);
}
CComPtr<IDXGIAdapter3> adapter3;
if(SUCCEEDED(adapter->QueryInterface(&adapter3)))
{
wprintf(L"DXGI_QUERY_VIDEO_MEMORY_INFO:\n");
for(UINT groupIndex = 0; groupIndex < 2; ++groupIndex)
{
const DXGI_MEMORY_SEGMENT_GROUP group = groupIndex == 0 ? DXGI_MEMORY_SEGMENT_GROUP_LOCAL : DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL;
const wchar_t* const groupName = groupIndex == 0 ? L"DXGI_MEMORY_SEGMENT_GROUP_LOCAL" : L"DXGI_MEMORY_SEGMENT_GROUP_NON_LOCAL";
DXGI_QUERY_VIDEO_MEMORY_INFO info = {};
CHECK_HR(adapter3->QueryVideoMemoryInfo(0, group, &info));
wprintf(L" %s:\n", groupName);
wprintf(L" Budget = %llu B (%s)\n", info.Budget, SizeToStr(info.Budget).c_str());
wprintf(L" CurrentUsage = %llu B (%s)\n", info.CurrentUsage, SizeToStr(info.CurrentUsage).c_str());
wprintf(L" AvailableForReservation = %llu B (%s)\n", info.AvailableForReservation, SizeToStr(info.AvailableForReservation).c_str());
wprintf(L" CurrentReservation = %llu B (%s)\n", info.CurrentReservation, SizeToStr(info.CurrentReservation).c_str());
}
}
assert(g_Device);
D3D12_FEATURE_DATA_ARCHITECTURE1 architecture1 = {};
if(SUCCEEDED(g_Device->CheckFeatureSupport(D3D12_FEATURE_ARCHITECTURE1, &architecture1, sizeof architecture1)))
{
wprintf(L"D3D12_FEATURE_DATA_ARCHITECTURE1:\n");
wprintf(L" UMA: %u\n", architecture1.UMA ? 1 : 0);
wprintf(L" CacheCoherentUMA: %u\n", architecture1.CacheCoherentUMA ? 1 : 0);
wprintf(L" IsolatedMMU: %u\n", architecture1.IsolatedMMU ? 1 : 0);
}
}
static void InitD3D() // initializes direct3d 12
{
assert(g_DXGIUsage);
CComPtr<IDXGIAdapter1> adapter = g_DXGIUsage->CreateAdapter(g_CommandLineParameters.m_GPUSelection);
CHECK_BOOL(adapter);
// Must be done before D3D12 device is created.
if(ENABLE_DEBUG_LAYER)
@ -432,20 +657,11 @@ void InitD3D() // initializes direct3d 12
}
CHECK_HR( D3D12MA::CreateAllocator(&desc, &g_Allocator) );
switch(g_Allocator->GetD3D12Options().ResourceHeapTier)
{
case D3D12_RESOURCE_HEAP_TIER_1:
wprintf(L"ResourceHeapTier = D3D12_RESOURCE_HEAP_TIER_1\n");
break;
case D3D12_RESOURCE_HEAP_TIER_2:
wprintf(L"ResourceHeapTier = D3D12_RESOURCE_HEAP_TIER_2\n");
break;
default:
assert(0);
}
}
PrintAdapterInformation(adapter);
wprintf(L"\n");
// -- Create the Command Queue -- //
D3D12_COMMAND_QUEUE_DESC cqDesc = {}; // we will be using all the default values
@ -477,7 +693,7 @@ void InitD3D() // initializes direct3d 12
IDXGISwapChain* tempSwapChain;
CHECK_HR( dxgiFactory->CreateSwapChain(
CHECK_HR( g_DXGIUsage->GetDXGIFactory()->CreateSwapChain(
g_CommandQueue, // the queue will be flushed once the swap chain is created
&swapChainDesc, // give it the swap chain description we created above
&tempSwapChain // store the created swap chain in a temp IDXGISwapChain interface
@ -1408,23 +1624,47 @@ static void OnKeyDown(WPARAM key)
}
}
#define CATCH_PRINT_ERROR(extraCatchCode) \
catch(const std::exception& ex) \
{ \
fwprintf(stderr, L"ERROR: %hs\n", ex.what()); \
extraCatchCode \
} \
catch(...) \
{ \
fwprintf(stderr, L"UNKNOWN ERROR.\n"); \
extraCatchCode \
}
static LRESULT WINAPI WndProc(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
g_Wnd = wnd;
InitD3D();
g_TimeOffset = GetTickCount64();
try
{
InitD3D();
g_TimeOffset = GetTickCount64();
}
CATCH_PRINT_ERROR(return -1;)
return 0;
case WM_DESTROY:
Cleanup();
try
{
Cleanup();
}
CATCH_PRINT_ERROR(;)
PostQuitMessage(0);
return 0;
case WM_KEYDOWN:
OnKeyDown(wParam);
try
{
OnKeyDown(wParam);
}
CATCH_PRINT_ERROR(DestroyWindow(wnd);)
return 0;
}
@ -1448,40 +1688,6 @@ void EndCommandList(ID3D12GraphicsCommandList* cmdList)
WaitGPUIdle(g_FrameIndex);
}
struct CommandLineParameters
{
bool m_Help;
bool m_List;
UINT32 m_GPUIndex = UINT32_MAX;
std::wstring m_GPUSubstring;
bool Parse(int argc, wchar_t** argv)
{
for(int i = 1; i < argc; ++i)
{
if(_wcsicmp(argv[i], L"-h") == 0 || _wcsicmp(argv[i], L"--Help") == 0)
{
m_Help = true;
}
else if(_wcsicmp(argv[i], L"-l") == 0 || _wcsicmp(argv[i], L"--List") == 0)
{
m_List = true;
}
else if((_wcsicmp(argv[i], L"-g") == 0 || _wcsicmp(argv[i], L"--GPU") == 0) && i + 1 < argc)
{
m_GPUSubstring = argv[i + 1];
}
else if((_wcsicmp(argv[i], L"-i") == 0 || _wcsicmp(argv[i], L"--GPUIndex") == 0) && i + 1 < argc)
{
m_GPUIndex = _wtoi(argv[i + 1]);
}
else
return false;
}
return true;
}
};
static void PrintLogo()
{
wprintf(L"%s\n", WINDOW_TITLE);
@ -1498,28 +1704,8 @@ static void PrintHelp()
);
}
int main2(int argc, wchar_t** argv)
int MainWindow()
{
CommandLineParameters cmdLineParams;
if(!cmdLineParams.Parse(argc, argv))
{
wprintf(L"ERROR: Invalid command line syntax.\n");
PrintLogo();
PrintHelp();
return -1;
}
if(cmdLineParams.m_Help)
{
PrintLogo();
PrintHelp();
return 0;
}
g_Instance = (HINSTANCE)GetModuleHandle(NULL);
CoInitialize(NULL);
WNDCLASSEX wndClass;
ZeroMemory(&wndClass, sizeof(wndClass));
wndClass.cbSize = sizeof(wndClass);
@ -1576,20 +1762,41 @@ int main2(int argc, wchar_t** argv)
return (int)msg.wParam;
}
int Main2(int argc, wchar_t** argv)
{
PrintLogo();
if(!g_CommandLineParameters.Parse(argc, argv))
{
wprintf(L"ERROR: Invalid command line syntax.\n");
PrintHelp();
return (int)ExitCode::CommandLineError;
}
if(g_CommandLineParameters.m_Help)
{
PrintHelp();
return (int)ExitCode::Help;
}
std::unique_ptr<DXGIUsage> DXGIUsage(new DXGIUsage());
DXGIUsage->Init();
g_DXGIUsage = DXGIUsage.get();
if(g_CommandLineParameters.m_List)
{
DXGIUsage->PrintAdapterList();
return (int)ExitCode::GPUList;
}
return MainWindow();
}
int wmain(int argc, wchar_t** argv)
{
try
{
return main2(argc, argv);
}
catch(const std::exception& ex)
{
fwprintf(stderr, L"ERROR: %hs\n", ex.what());
return -1;
}
catch(...)
{
fwprintf(stderr, L"UNKNOWN ERROR.\n");
return -1;
return Main2(argc, argv);
}
CATCH_PRINT_ERROR(return (int)ExitCode::RuntimeError;)
}

View File

@ -98,7 +98,7 @@ static bool ValidateData(const void* ptr, const UINT64 sizeInBytes, UINT seed)
{
if(values[i] != value++)
{
//FAIL("ValidateData failed.");
//CHECK_BOOL(0 && "ValidateData failed.");
return false;
}
}
@ -113,7 +113,7 @@ static bool ValidateDataZero(const void* ptr, const UINT64 sizeInBytes)
{
if(values[i] != 0)
{
//FAIL("ValidateData failed.");
//CHECK_BOOL(0 && "ValidateData failed.");
return false;
}
}