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.
diff --git a/src/Common.h b/src/Common.h
index 8ba8212..c824dc4 100644
--- a/src/Common.h
+++ b/src/Common.h
@@ -55,13 +55,14 @@
#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)
diff --git a/src/D3D12Sample.cpp b/src/D3D12Sample.cpp
index 8eb91b1..b403a6a 100644
--- a/src/D3D12Sample.cpp
+++ b/src/D3D12Sample.cpp
@@ -34,6 +34,15 @@
#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 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 @@
}
}
+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 @@
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 @@
}
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 @@
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 @@
}
}
+#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 @@
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 @@
);
}
-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 @@
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);
+ 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;
- }
+ CATCH_PRINT_ERROR(return (int)ExitCode::RuntimeError;)
}
diff --git a/src/Tests.cpp b/src/Tests.cpp
index 7a65ad5..9cdb140 100644
--- a/src/Tests.cpp
+++ b/src/Tests.cpp
@@ -98,7 +98,7 @@
{
if(values[i] != value++)
{
- //FAIL("ValidateData failed.");
+ //CHECK_BOOL(0 && "ValidateData failed.");
return false;
}
}
@@ -113,7 +113,7 @@
{
if(values[i] != 0)
{
- //FAIL("ValidateData failed.");
+ //CHECK_BOOL(0 && "ValidateData failed.");
return false;
}
}