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;

         }

     }