diff --git a/src/config.h b/src/config.h index 3496649b..3b07856b 100644 --- a/src/config.h +++ b/src/config.h @@ -80,7 +80,7 @@ -#define DX12_TEST 0 +#define DX12_TEST 1 diff --git a/src/gpu.h b/src/gpu.h index 3bfdecbd..0d400891 100644 --- a/src/gpu.h +++ b/src/gpu.h @@ -15,7 +15,15 @@ struct gpu_startup_receipt gpu_startup(struct sys_window *window); * ========================== */ struct gpu_handle { - u64 v; + union { + /* dx11 style */ + u64 v; + /* dx12 style */ + struct { + u64 gen; + u64 idx; + }; + }; }; void gpu_release(struct gpu_handle handle); diff --git a/src/gpu_dx11.c b/src/gpu_dx11.c index 6da4409a..8fd0ff7a 100644 --- a/src/gpu_dx11.c +++ b/src/gpu_dx11.c @@ -338,12 +338,12 @@ struct gpu_startup_receipt gpu_startup(struct sys_window *window) /* DXGI Debug break */ { - IDXGIInfoQueue *dxgiInfo; - hr = DXGIGetDebugInterface1(0, &IID_IDXGIInfoQueue, (void **)&dxgiInfo); + IDXGIInfoQueue *dxgi_info; + hr = DXGIGetDebugInterface1(0, &IID_IDXGIInfoQueue, (void **)&dxgi_info); ASSERT(SUCCEEDED(hr)); - IDXGIInfoQueue_SetBreakOnSeverity(dxgiInfo, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, TRUE); - IDXGIInfoQueue_SetBreakOnSeverity(dxgiInfo, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, TRUE); - IDXGIInfoQueue_Release(dxgiInfo); + IDXGIInfoQueue_SetBreakOnSeverity(dxgi_info, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, TRUE); + IDXGIInfoQueue_SetBreakOnSeverity(dxgi_info, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, TRUE); + IDXGIInfoQueue_Release(dxgi_info); } #endif @@ -388,6 +388,7 @@ struct gpu_startup_receipt gpu_startup(struct sys_window *window) /* Disable Alt+Enter changing monitor resolution to match window size */ IDXGIFactory_MakeWindowAssociation(factory, hwnd, DXGI_MWA_NO_ALT_ENTER); + IDXGISwapChain1_Release(swapchain1); IDXGIFactory2_Release(factory); IDXGIAdapter_Release(dxgiAdapter); IDXGIDevice_Release(dxgiDevice); @@ -2066,7 +2067,7 @@ void gpu_swap_backbuffer(i32 vsync) gpu_capture_image_for_profiler(); { __profscope(Present); - IDXGISwapChain1_Present(G.swapchain, vsync, flags); + IDXGISwapChain2_Present(G.swapchain, vsync, flags); __prof_dx11_collect(G.profiling_ctx); __profframe(0); } diff --git a/src/gpu_dx12.c b/src/gpu_dx12.c index 9eeed977..c4beb370 100644 --- a/src/gpu_dx12.c +++ b/src/gpu_dx12.c @@ -2,13 +2,73 @@ #include "gpu.h" #include "sys.h" +#include "arena.h" +#include "memory.h" +#include "string.h" +#include "scratch.h" + +#pragma warning(push, 0) +# define UNICODE +# define COBJMACROS +# include +# include +# include +# include +# include +#pragma warning(pop) + +#pragma comment(lib, "d3d12") +#pragma comment(lib, "dxgi") +#pragma comment(lib, "dxguid") + +#define DX12_WAIT_FRAME_LATENCY 1 +#define DX12_ALLOW_TEARING 1 + +#define DX12_SWAPCHAIN_BUFFER_COUNT (3) +#define DX12_SWAPCHAIN_FLAGS ((DX12_ALLOW_TEARING * DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING) | (DX12_WAIT_FRAME_LATENCY * DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT)) +#define DX12_SWAPCHAIN_FORMAT (DXGI_FORMAT_R8G8B8A8_UNORM) +//#define DX12_SWAPCHAIN_RTV_FORMAT (DXGI_FORMAT_R8G8B8A8_UNORM_SRGB) + +#if RTC +# define DX12_DEBUG 1 +# define DX12_SHADER_DEBUG 1 +#else +# define DX12_DEBUG 0 +# define DX12_SHADER_DEBUG 0 +#endif + +enum dx12_handle_kind { + DX12_HANDLE_KIND_NONE, + DX12_HANDLE_KIND_TEXTURE, + + NUM_DX12_HANDLE_KINDS +}; + +struct dx12_handle_entry { + enum dx12_handle_kind kind; + u64 gen; + u64 idx; + void *data; + + struct dx12_handle_entry *next_free; +}; + +struct dx12_texture { + i32 _; +}; /* ========================== * * Global state * ========================== */ GLOBAL struct { - i32 _; + /* Handles pool */ + struct sys_mutex handle_entries_mutex; + struct arena handle_entries_arena; + struct dx12_handle_entry *first_free_handle_entry; + u64 num_handle_entries_reserved; + + /* Swapchain */ } G = ZI, DEBUG_ALIAS(G, G_gpu_dx12); /* ========================== * @@ -17,7 +77,163 @@ GLOBAL struct { struct gpu_startup_receipt gpu_startup(struct sys_window *window) { - (UNUSED)window; + struct temp_arena scratch = scratch_begin_no_conflict(); + HRESULT hr; + + /* Initialize handles pool */ + G.handle_entries_mutex = sys_mutex_alloc(); + G.handle_entries_arena = arena_alloc(GIGABYTE(64)); + + /* Create debug controller */ + u32 dxgi_factory_flags = 0; +#if DX12_DEBUG + ID3D12Debug *debug_controller = NULL; + { + hr = D3D12GetDebugInterface(&IID_ID3D12Debug, (void **)&debug_controller); + if (!SUCCEEDED(hr)) { + sys_panic(LIT("Failed to create D3D12 debug controller")); + } + ID3D12Debug_EnableDebugLayer(debug_controller); + ID3D12Debug_Release(debug_controller); + dxgi_factory_flags |= DXGI_CREATE_FACTORY_DEBUG; + } +#endif + + /* Create factory */ + IDXGIFactory6 *factory = NULL; + hr = CreateDXGIFactory2(dxgi_factory_flags, &IID_IDXGIFactory6, (void **)&factory); + if (!SUCCEEDED(hr)) { + sys_panic(LIT("Failed to initialize DXGI factory")); + } + + /* Create device */ + ID3D12Device *device = NULL; + { + struct string error = LIT("Could not initialize GPU device."); + struct string first_gpu_name = ZI; + u32 adapter_index = 0; + while (true) { + IDXGIAdapter1 *adapter = NULL; + hr = IDXGIFactory6_EnumAdapterByGpuPreference(factory, adapter_index, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, &IID_IDXGIAdapter1, (void **)&adapter); + if (SUCCEEDED(hr)) { + DXGI_ADAPTER_DESC1 desc; + IDXGIAdapter1_GetDesc1(adapter, &desc); + if (!(desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)) { + if (first_gpu_name.len == 0) { + first_gpu_name = string_from_wstr_no_limit(scratch.arena, desc.Description); + } + /* Using feature level 12_0 instead of 12_2 because mesh feature check happens separately + * (because 1600 series cards support mesh shaders but not the rest of 12_2) */ + hr = D3D12CreateDevice((IUnknown *)adapter, D3D_FEATURE_LEVEL_12_0, &IID_ID3D12Device, (void **)&device); + if (SUCCEEDED(hr)) { + D3D12_FEATURE_DATA_D3D12_OPTIONS7 features = ZI; + hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_D3D12_OPTIONS7, &features, sizeof(features)); + if (SUCCEEDED(hr) && features.MeshShaderTier >= D3D12_MESH_SHADER_TIER_1) { + IDXGIAdapter1_Release(adapter); + adapter = NULL; + break; + } + } + } + ID3D12Device_Release(device); + IDXGIAdapter1_Release(adapter); + adapter = NULL; + device = NULL; + ++adapter_index; + } else { + break; + } + } + /* TODO: Fall back to compute shaders if mesh shaders aren't supported */ + if (!device) { + if (first_gpu_name.len > 0) { + struct string fmt = LIT("Device '%F' does not support DirectX 12_2. Ensure that your drivers are up to date.\n\n" + "Note that older GPUs such as those up to and including the GTX 1000 series, the RX 5000 series, and the Intel Xe-LP series do not support DirectX 12_2 features."); + error = string_format(scratch.arena, fmt, FMT_STR(first_gpu_name)); + } + sys_panic(error); + } + } + +#if DX12_DEBUG + /* D3D12 Debug break */ + { + ID3D12InfoQueue *info = NULL; + hr = ID3D12Device_QueryInterface(device, &IID_ID3D12InfoQueue, (void **)&info); + if (!SUCCEEDED(hr)) { + sys_panic(LIT("Failed to query ID3D12Device interface")); + } + ID3D12InfoQueue_SetBreakOnSeverity(info, D3D12_MESSAGE_SEVERITY_CORRUPTION, TRUE); + ID3D12InfoQueue_SetBreakOnSeverity(info, D3D12_MESSAGE_SEVERITY_ERROR, TRUE); + ID3D12InfoQueue_Release(info); + } + + /* DXGI Debug break */ + { + IDXGIInfoQueue *dxgi_info = NULL; + hr = DXGIGetDebugInterface1(0, &IID_IDXGIInfoQueue, (void **)&dxgi_info); + if (!SUCCEEDED(hr)) { + sys_panic(LIT("Failed to get DXGI debug interface")); + } + IDXGIInfoQueue_SetBreakOnSeverity(dxgi_info, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, TRUE); + IDXGIInfoQueue_SetBreakOnSeverity(dxgi_info, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, TRUE); + IDXGIInfoQueue_Release(dxgi_info); + } +#endif + + /* Create command queue */ + ID3D12CommandQueue *cq = NULL; + { + D3D12_COMMAND_QUEUE_DESC desc = ZI; + desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; + desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; + + hr = ID3D12Device_CreateCommandQueue(device, &desc, &IID_ID3D12CommandQueue, (void **)&cq); + if (!SUCCEEDED(hr)) { + sys_panic(LIT("Failed to create command queue")); + } + } + + /* Create swapchain */ + IDXGISwapChain3 *swapchain = NULL; + u32 swapchain_frame_index = 0; + { + HWND hwnd = (HWND)sys_window_get_internal_handle(window); + DXGI_SWAP_CHAIN_DESC1 desc = { + .Format = DX12_SWAPCHAIN_FORMAT, + .SampleDesc = { 1, 0 }, + .BufferUsage = DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_RENDER_TARGET_OUTPUT, + .BufferCount = 3, + .Scaling = DXGI_SCALING_NONE, + .Flags = DX12_SWAPCHAIN_FLAGS, + .AlphaMode = DXGI_ALPHA_MODE_IGNORE, + .SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD + }; + + /* Create swapchain1 */ + IDXGISwapChain1 *swapchain1 = NULL; + hr = IDXGIFactory2_CreateSwapChainForHwnd(factory, (IUnknown *)cq, hwnd, &desc, NULL, NULL, &swapchain1); + if (!SUCCEEDED(hr)) { + sys_panic(LIT("Failed to create IDXGISwapChain1")); + } + + /* Upgrade to swapchain3 */ + hr = IDXGISwapChain1_QueryInterface(swapchain1, &IID_IDXGISwapChain3, (void **)&swapchain); + if (!SUCCEEDED(hr)) { + sys_panic(LIT("Failed to create IDXGISwapChain3")); + } + + /* Disable Alt+Enter changing monitor resolution to match window size */ + IDXGIFactory_MakeWindowAssociation(factory, hwnd, DXGI_MWA_NO_ALT_ENTER); + + swapchain_frame_index = IDXGISwapChain3_GetCurrentBackBufferIndex(swapchain); + (UNUSED)swapchain_frame_index; + + IDXGISwapChain1_Release(swapchain1); + } + + IDXGIFactory6_Release(factory); + scratch_end(scratch); struct gpu_startup_receipt res = ZI; return res; } @@ -26,23 +242,104 @@ struct gpu_startup_receipt gpu_startup(struct sys_window *window) * Handle * ========================== */ +INTERNAL void dx12_texture_release(struct dx12_texture *t); + +INTERNAL struct gpu_handle handle_alloc(enum dx12_handle_kind kind, void *data) +{ + u64 old_gen = 0; + u64 idx = 0; + struct dx12_handle_entry *entry = NULL; + { + struct sys_lock lock = sys_mutex_lock_e(&G.handle_entries_mutex); + if (G.first_free_handle_entry) { + entry = G.first_free_handle_entry; + G.first_free_handle_entry = entry->next_free; + old_gen = entry->gen; + idx = entry->idx; + } else { + entry = arena_push_no_zero(&G.handle_entries_arena, struct dx12_handle_entry); + idx = G.num_handle_entries_reserved++; + } + sys_mutex_unlock(&lock); + } + MEMZERO_STRUCT(entry); + entry->kind = kind; + entry->gen = old_gen + 1; + entry->idx = idx; + entry->data = data; + + struct gpu_handle res = ZI; + res.gen = entry->gen; + res.idx = entry->idx; + return res; +} + +INTERNAL struct dx12_handle_entry *handle_get_entry(struct gpu_handle handle, struct sys_lock *lock) +{ + sys_assert_locked_e_or_s(lock, &G.handle_entries_mutex); + struct dx12_handle_entry *res = NULL; + if (handle.idx > 0 && handle.idx < G.num_handle_entries_reserved) { + struct dx12_handle_entry *tmp = &((struct dx12_handle_entry *)G.handle_entries_arena.base)[handle.idx]; + if (tmp->gen == handle.gen) { + res = tmp; + } + } + return res; +} + +/* TODO: The GPU api should ensure that resources freed by the caller will not cause issues on the GPU (via fencing), + * however the caller is responsible for managing resource lifetimes on the CPU side (e.g. using sprites w/ sprite scopes + * to ensure freed textures aren't being used in pending command lists. */ void gpu_release(struct gpu_handle handle) { - (UNUSED)handle; + enum dx12_handle_kind kind = NULL; + void *data = NULL; + + /* Release handle entry */ + struct sys_lock lock = sys_mutex_lock_e(&G.handle_entries_mutex); + { + struct dx12_handle_entry *entry = handle_get_entry(handle, &lock); + if (entry) { + kind = entry->kind; + data = entry->data; + } + ++entry->gen; + entry->next_free = G.first_free_handle_entry; + G.first_free_handle_entry = entry; + } + sys_mutex_unlock(&lock); + + /* Release data */ + if (data) { + switch (kind) { + default: break; + + case DX12_HANDLE_KIND_TEXTURE: + { + dx12_texture_release(data); + } break; + } + } } /* ========================== * * Texture * ========================== */ +INTERNAL void dx12_texture_release(struct dx12_texture *t) +{ + (UNUSED)t; +} + struct gpu_handle gpu_texture_alloc(enum gpu_texture_format format, u32 flags, struct v2i32 size, void *initial_data) { (UNUSED)format; (UNUSED)flags; (UNUSED)size; (UNUSED)initial_data; - struct gpu_handle res = ZI; - return res; + struct dx12_texture *t = NULL; + + return handle_alloc(DX12_HANDLE_KIND_TEXTURE, t); } void gpu_texture_clear(struct gpu_handle target_texture, u32 clear_color)