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; } }