#if DX12_TEST #include "gpu.h" #include "sys.h" #include "arena.h" #include "memory.h" #include "string.h" #include "scratch.h" #include "app.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 { /* 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; /* Device */ ID3D12Device *device; /* Desc sizes */ u32 desc_size_rtv; /* Command queues */ ID3D12CommandQueue *cq_direct; ID3D12CommandQueue *cq_compute; /* Swapchain */ u32 swapchain_frame_index; ID3D12CommandAllocator *swapchain_ca; IDXGISwapChain3 *swapchain; ID3D12DescriptorHeap *swapchain_rtv_heap; ID3D12Resource *swapchain_rtvs[DX12_SWAPCHAIN_BUFFER_COUNT]; } G = ZI, DEBUG_ALIAS(G, G_gpu_dx12); /* ========================== * * Startup * ========================== */ INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(gpu_shutdown); INTERNAL void dx12_init_base(struct sys_window *window); INTERNAL void dx12_init_shaders(void); struct gpu_startup_receipt gpu_startup(struct sys_window *window) { /* Initialize handles pool */ G.handle_entries_mutex = sys_mutex_alloc(); G.handle_entries_arena = arena_alloc(GIGABYTE(64)); /* Initialize dx12 */ dx12_init_base(window); dx12_init_shaders(); /* Register callbacks */ app_register_exit_callback(gpu_shutdown); struct gpu_startup_receipt res = ZI; return res; } INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(gpu_shutdown) { #if DX12_DEBUG /* Release objects to make live object reporting less noisy */ for (u64 i = 0; i < ARRAY_COUNT(G.swapchain_rtvs); ++i) { ID3D12Resource_Release(G.swapchain_rtvs[i]); } ID3D12DescriptorHeap_Release(G.swapchain_rtv_heap); ID3D12CommandQueue_Release(G.swapchain_ca); ID3D12CommandQueue_Release(G.cq_direct); ID3D12CommandQueue_Release(G.cq_compute); IDXGISwapChain3_Release(G.swapchain); ID3D12Device_Release(G.device); #endif } /* ========================== * * Dx12 base initialization * ========================== */ INTERNAL void dx12_init_error(struct string error) { struct temp_arena scratch = scratch_begin_no_conflict(); struct string msg = string_format(scratch.arena, LIT("Failed to initialize DirectX 12.\n\n%F"), FMT_STR(error)); sys_panic(msg); scratch_end(scratch); } INTERNAL void dx12_init_base(struct sys_window *window) { __prof; struct temp_arena scratch = scratch_begin_no_conflict(); HRESULT hr = 0; /* Enable debug layer */ u32 dxgi_factory_flags = 0; #if DX12_DEBUG { ID3D12Debug *debug_controller0 = NULL; hr = D3D12GetDebugInterface(&IID_ID3D12Debug, (void **)&debug_controller0); if (FAILED(hr)) { dx12_init_error(LIT("Failed to create ID3D12Debug0")); } ID3D12Debug1 *debug_controller1 = NULL; hr = ID3D12Debug_QueryInterface(debug_controller0, &IID_ID3D12Debug1, (void **)&debug_controller1); if (FAILED(hr)) { dx12_init_error(LIT("Failed to create ID3D12Debug1")); } ID3D12Debug_EnableDebugLayer(debug_controller0); /* FIXME: Enable this */ //ID3D12Debug1_SetEnableGPUBasedValidation(debug_controller1, true); ID3D12Debug_Release(debug_controller1); ID3D12Debug_Release(debug_controller0); dxgi_factory_flags |= DXGI_CREATE_FACTORY_DEBUG; } #endif /* Create factory */ IDXGIFactory6 *factory = NULL; hr = CreateDXGIFactory2(dxgi_factory_flags, &IID_IDXGIFactory6, (void **)&factory); if (FAILED(hr)) { dx12_init_error(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); } hr = D3D12CreateDevice((IUnknown *)adapter, D3D_FEATURE_LEVEL_12_0, &IID_ID3D12Device, (void **)&device); if (SUCCEEDED(hr)) { IDXGIAdapter1_Release(adapter); adapter = NULL; break; } } ID3D12Device_Release(device); IDXGIAdapter1_Release(adapter); adapter = NULL; device = NULL; ++adapter_index; } else { break; } } if (!device) { if (first_gpu_name.len > 0) { struct string fmt = LIT("Could not initialize device '%F' with D3D_FEATURE_LEVEL_12_0. Ensure that the device is capable and drivers are up to date."); error = string_format(scratch.arena, fmt, FMT_STR(first_gpu_name)); } dx12_init_error(error); } } /* Get desc sizes */ u32 desc_size_rtv = ID3D12Device_GetDescriptorHandleIncrementSize(device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV); #if DX12_DEBUG /* Enable D3D12 Debug break */ { ID3D12InfoQueue *info = NULL; hr = ID3D12Device_QueryInterface(device, &IID_ID3D12InfoQueue, (void **)&info); if (FAILED(hr)) { dx12_init_error(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); } /* Enable DXGI Debug break */ { IDXGIInfoQueue *dxgi_info = NULL; hr = DXGIGetDebugInterface1(0, &IID_IDXGIInfoQueue, (void **)&dxgi_info); if (FAILED(hr)) { dx12_init_error(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 direct command queue */ ID3D12CommandQueue *cq_direct = 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_direct); if (FAILED(hr)) { dx12_init_error(LIT("Failed to create direct command queue")); } } /* Create compute command queue */ ID3D12CommandQueue *cq_compute = NULL; { D3D12_COMMAND_QUEUE_DESC desc = ZI; desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; desc.Type = D3D12_COMMAND_LIST_TYPE_COMPUTE; hr = ID3D12Device_CreateCommandQueue(device, &desc, &IID_ID3D12CommandQueue, (void **)&cq_compute); if (FAILED(hr)) { dx12_init_error(LIT("Failed to create compute command queue")); } } /* Create swapchain command allocator */ ID3D12CommandAllocator *swapchain_ca = NULL; { hr = ID3D12Device_CreateCommandAllocator(device, D3D12_COMMAND_LIST_TYPE_DIRECT, &IID_ID3D12CommandAllocator, (void **)&swapchain_ca); if (FAILED(hr)) { dx12_init_error(LIT("Failed to create swapchain command allocator")); } } /* 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 = DX12_SWAPCHAIN_BUFFER_COUNT, .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_direct, hwnd, &desc, NULL, NULL, &swapchain1); if (FAILED(hr)) { dx12_init_error(LIT("Failed to create IDXGISwapChain1")); } /* Upgrade to swapchain3 */ hr = IDXGISwapChain1_QueryInterface(swapchain1, &IID_IDXGISwapChain3, (void **)&swapchain); if (FAILED(hr)) { dx12_init_error(LIT("Failed to create IDXGISwapChain3")); } /* Disable Alt+Enter changing monitor resolution to match window size */ IDXGIFactory_MakeWindowAssociation(factory, hwnd, DXGI_MWA_NO_ALT_ENTER); /* Get initial frame index */ swapchain_frame_index = IDXGISwapChain3_GetCurrentBackBufferIndex(swapchain); IDXGISwapChain1_Release(swapchain1); } /* Create swapchain RTV heap */ ID3D12DescriptorHeap *swapchain_rtv_heap = NULL; { D3D12_DESCRIPTOR_HEAP_DESC desc = ZI; desc.NumDescriptors = DX12_SWAPCHAIN_BUFFER_COUNT; desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE; hr = ID3D12Device_CreateDescriptorHeap(device, &desc, &IID_ID3D12DescriptorHeap, (void **)&swapchain_rtv_heap); if (FAILED(hr)) { dx12_init_error(LIT("Failed to create swapchain RTV heap")); } } /* Create swacphain RTVs */ ID3D12Resource *swapchain_rtvs[DX12_SWAPCHAIN_BUFFER_COUNT] = ZI; { D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle = ZI; ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(swapchain_rtv_heap, &rtv_handle); for (u32 i = 0; i < DX12_SWAPCHAIN_BUFFER_COUNT; ++i) { hr = IDXGISwapChain3_GetBuffer(swapchain, i, &IID_ID3D12Resource, (void **)&swapchain_rtvs[i]); if (FAILED(hr)) { dx12_init_error(LIT("Failed to create swapchain RTV")); } ID3D12Device_CreateRenderTargetView(device, swapchain_rtvs[i], NULL, rtv_handle); rtv_handle.ptr += desc_size_rtv; } } G.device = device; G.desc_size_rtv = desc_size_rtv; G.swapchain_frame_index = swapchain_frame_index; G.cq_direct = cq_direct; G.cq_compute = cq_compute; G.swapchain_ca = swapchain_ca; G.swapchain = swapchain; G.swapchain_rtv_heap = swapchain_rtv_heap; MEMCPY(&G.swapchain_rtvs, swapchain_rtvs, sizeof(G.swapchain_rtvs)); IDXGIFactory6_Release(factory); scratch_end(scratch); } /* ========================== * * Dx12 shader initialization * ========================== */ INTERNAL void dx12_init_shaders(void) { } /* ========================== * * 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) { 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 dx12_texture *t = NULL; return handle_alloc(DX12_HANDLE_KIND_TEXTURE, t); } void gpu_texture_clear(struct gpu_handle target_texture, u32 clear_color) { (UNUSED)target_texture; (UNUSED)clear_color; } struct v2i32 gpu_texture_get_size(struct gpu_handle texture) { (UNUSED)texture; struct v2i32 res = ZI; return res; } /* ========================== * * Cmd buffer * ========================== */ struct gpu_handle gpu_cmd_store_alloc(void) { struct gpu_handle res = ZI; return res; } void gpu_push_cmd(struct gpu_handle gpu_cmd_store, struct gpu_cmd_params params) { (UNUSED)gpu_cmd_store; (UNUSED)params; } void gpu_submit_cmds(struct gpu_handle gpu_cmd_store) { (UNUSED)gpu_cmd_store; } /* ========================== * * Dispatch * ========================== */ struct gpu_handle gpu_dispatch_state_alloc(void) { struct gpu_handle res = ZI; return res; } void gpu_dispatch(struct gpu_handle gpu_dispatch_state, struct gpu_dispatch_params params) { (UNUSED)gpu_dispatch_state; (UNUSED)params; } /* ========================== * * Backbuffer * ========================== */ struct gpu_handle gpu_recreate_backbuffer(struct v2i32 size) { (UNUSED)size; struct gpu_handle res = ZI; return res; } void gpu_swap_backbuffer(i32 vsync) { (UNUSED)vsync; } #endif