release dx12 objects on exit

This commit is contained in:
jacob 2025-06-06 17:31:36 -05:00
parent d0ec962123
commit 50ca2387fa
4 changed files with 174 additions and 127 deletions

1
.gitignore vendored
View File

@ -7,6 +7,7 @@
*.pdb *.pdb
*.exe *.exe
.vs/* .vs/*
.vscode/*
unused/ unused/
build/ build/

View File

@ -355,6 +355,7 @@ void app_entry_point(struct string args_str)
struct sys_lock lock = sys_mutex_lock_e(&G.exit_callbacks_mutex); struct sys_lock lock = sys_mutex_lock_e(&G.exit_callbacks_mutex);
/* Start callback threads */ /* Start callback threads */
/* TODO: Create these threads when the callbacks are initially registered and have them wait on exit */
{ {
__profscope(app_start_exit_callbacks); __profscope(app_start_exit_callbacks);
for (struct exit_callback *callback = G.exit_callbacks_head; callback; callback = callback->next) { for (struct exit_callback *callback = G.exit_callbacks_head; callback; callback = callback->next) {

View File

@ -6,6 +6,7 @@
#include "memory.h" #include "memory.h"
#include "string.h" #include "string.h"
#include "scratch.h" #include "scratch.h"
#include "app.h"
#pragma warning(push, 0) #pragma warning(push, 0)
# define UNICODE # define UNICODE
@ -68,13 +69,21 @@ GLOBAL struct {
struct dx12_handle_entry *first_free_handle_entry; struct dx12_handle_entry *first_free_handle_entry;
u64 num_handle_entries_reserved; u64 num_handle_entries_reserved;
/* Device */
ID3D12Device *device;
/* Swapchain */ /* Swapchain */
u32 swapchain_frame_index;
ID3D12CommandQueue *swapchain_cq;
IDXGISwapChain3 *swapchain;
} G = ZI, DEBUG_ALIAS(G, G_gpu_dx12); } G = ZI, DEBUG_ALIAS(G, G_gpu_dx12);
/* ========================== * /* ========================== *
* Startup * Startup
* ========================== */ * ========================== */
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(gpu_shutdown);
struct gpu_startup_receipt gpu_startup(struct sys_window *window) struct gpu_startup_receipt gpu_startup(struct sys_window *window)
{ {
struct temp_arena scratch = scratch_begin_no_conflict(); struct temp_arena scratch = scratch_begin_no_conflict();
@ -84,160 +93,195 @@ struct gpu_startup_receipt gpu_startup(struct sys_window *window)
G.handle_entries_mutex = sys_mutex_alloc(); G.handle_entries_mutex = sys_mutex_alloc();
G.handle_entries_arena = arena_alloc(GIGABYTE(64)); G.handle_entries_arena = arena_alloc(GIGABYTE(64));
/* Create debug controller */ /* Initialize dx12 */
u32 dxgi_factory_flags = 0;
#if DX12_DEBUG
ID3D12Debug *debug_controller = NULL;
{ {
hr = D3D12GetDebugInterface(&IID_ID3D12Debug, (void **)&debug_controller); /* Enable debug layer */
if (!SUCCEEDED(hr)) { u32 dxgi_factory_flags = 0;
sys_panic(LIT("Failed to create D3D12 debug controller")); #if DX12_DEBUG
{
ID3D12Debug *debug_controller0 = NULL;
hr = D3D12GetDebugInterface(&IID_ID3D12Debug, (void **)&debug_controller0);
if (!SUCCEEDED(hr)) {
sys_panic(LIT("Failed to create ID3D12Debug0"));
}
ID3D12Debug1 *debug_controller1 = NULL;
hr = ID3D12Debug_QueryInterface(debug_controller0, &IID_ID3D12Debug1, (void **)&debug_controller1);
if (!SUCCEEDED(hr)) {
sys_panic(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;
} }
ID3D12Debug_EnableDebugLayer(debug_controller);
ID3D12Debug_Release(debug_controller);
dxgi_factory_flags |= DXGI_CREATE_FACTORY_DEBUG;
}
#endif #endif
/* Create factory */ /* Create factory */
IDXGIFactory6 *factory = NULL; IDXGIFactory6 *factory = NULL;
hr = CreateDXGIFactory2(dxgi_factory_flags, &IID_IDXGIFactory6, (void **)&factory); hr = CreateDXGIFactory2(dxgi_factory_flags, &IID_IDXGIFactory6, (void **)&factory);
if (!SUCCEEDED(hr)) { if (!SUCCEEDED(hr)) {
sys_panic(LIT("Failed to initialize DXGI factory")); sys_panic(LIT("Failed to initialize DXGI factory"));
} }
/* Create device */ /* Create device */
ID3D12Device *device = NULL; ID3D12Device *device = NULL;
{ {
struct string error = LIT("Could not initialize GPU device."); struct string error = LIT("Could not initialize GPU device.");
struct string first_gpu_name = ZI; struct string first_gpu_name = ZI;
u32 adapter_index = 0; u32 adapter_index = 0;
while (true) { while (true) {
IDXGIAdapter1 *adapter = NULL; IDXGIAdapter1 *adapter = NULL;
hr = IDXGIFactory6_EnumAdapterByGpuPreference(factory, adapter_index, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, &IID_IDXGIAdapter1, (void **)&adapter); hr = IDXGIFactory6_EnumAdapterByGpuPreference(factory, adapter_index, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, &IID_IDXGIAdapter1, (void **)&adapter);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
DXGI_ADAPTER_DESC1 desc; DXGI_ADAPTER_DESC1 desc;
IDXGIAdapter1_GetDesc1(adapter, &desc); IDXGIAdapter1_GetDesc1(adapter, &desc);
if (!(desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)) { if (!(desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE)) {
if (first_gpu_name.len == 0) { if (first_gpu_name.len == 0) {
first_gpu_name = string_from_wstr_no_limit(scratch.arena, desc.Description); 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 /* 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) */ * (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); hr = D3D12CreateDevice((IUnknown *)adapter, D3D_FEATURE_LEVEL_12_0, &IID_ID3D12Device, (void **)&device);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
D3D12_FEATURE_DATA_D3D12_OPTIONS7 features = ZI; D3D12_FEATURE_DATA_D3D12_OPTIONS7 features = ZI;
hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_D3D12_OPTIONS7, &features, sizeof(features)); hr = ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_D3D12_OPTIONS7, &features, sizeof(features));
if (SUCCEEDED(hr) && features.MeshShaderTier >= D3D12_MESH_SHADER_TIER_1) { if (SUCCEEDED(hr) && features.MeshShaderTier >= D3D12_MESH_SHADER_TIER_1) {
IDXGIAdapter1_Release(adapter); IDXGIAdapter1_Release(adapter);
adapter = NULL; adapter = NULL;
break; break;
}
} }
} }
ID3D12Device_Release(device);
IDXGIAdapter1_Release(adapter);
adapter = NULL;
device = NULL;
++adapter_index;
} else {
break;
} }
ID3D12Device_Release(device); }
IDXGIAdapter1_Release(adapter); /* TODO: Fall back to compute shaders if mesh shaders aren't supported */
adapter = NULL; if (!device) {
device = NULL; if (first_gpu_name.len > 0) {
++adapter_index; struct string fmt = LIT("Device '%F' does not support DirectX 12_2. Ensure that your drivers are up to date.\n\n"
} else { "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.");
break; error = string_format(scratch.arena, fmt, FMT_STR(first_gpu_name));
}
sys_panic(error);
} }
} }
/* 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 #if DX12_DEBUG
/* D3D12 Debug break */ /* Enable D3D12 Debug break */
{ {
ID3D12InfoQueue *info = NULL; ID3D12InfoQueue *info = NULL;
hr = ID3D12Device_QueryInterface(device, &IID_ID3D12InfoQueue, (void **)&info); hr = ID3D12Device_QueryInterface(device, &IID_ID3D12InfoQueue, (void **)&info);
if (!SUCCEEDED(hr)) { if (!SUCCEEDED(hr)) {
sys_panic(LIT("Failed to query ID3D12Device interface")); 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);
} }
ID3D12InfoQueue_SetBreakOnSeverity(info, D3D12_MESSAGE_SEVERITY_CORRUPTION, TRUE);
ID3D12InfoQueue_SetBreakOnSeverity(info, D3D12_MESSAGE_SEVERITY_ERROR, TRUE);
ID3D12InfoQueue_Release(info);
}
/* DXGI Debug break */ /* Enable DXGI Debug break */
{ {
IDXGIInfoQueue *dxgi_info = NULL; IDXGIInfoQueue *dxgi_info = NULL;
hr = DXGIGetDebugInterface1(0, &IID_IDXGIInfoQueue, (void **)&dxgi_info); hr = DXGIGetDebugInterface1(0, &IID_IDXGIInfoQueue, (void **)&dxgi_info);
if (!SUCCEEDED(hr)) { if (!SUCCEEDED(hr)) {
sys_panic(LIT("Failed to get DXGI debug interface")); 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);
} }
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 #endif
/* Create command queue */ /* Create direct command queue */
ID3D12CommandQueue *cq = NULL; ID3D12CommandQueue *swapchain_cq = NULL;
{ {
D3D12_COMMAND_QUEUE_DESC desc = ZI; D3D12_COMMAND_QUEUE_DESC desc = ZI;
desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE; desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
hr = ID3D12Device_CreateCommandQueue(device, &desc, &IID_ID3D12CommandQueue, (void **)&cq); hr = ID3D12Device_CreateCommandQueue(device, &desc, &IID_ID3D12CommandQueue, (void **)&swapchain_cq);
if (!SUCCEEDED(hr)) { if (!SUCCEEDED(hr)) {
sys_panic(LIT("Failed to create command queue")); sys_panic(LIT("Failed to create swapchain 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 *)swapchain_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);
/* Get initial frame index */
swapchain_frame_index = IDXGISwapChain3_GetCurrentBackBufferIndex(swapchain);
IDXGISwapChain1_Release(swapchain1);
}
G.device = device;
G.swapchain_frame_index = swapchain_frame_index;
G.swapchain_cq = swapchain_cq;
G.swapchain = swapchain;
IDXGIFactory6_Release(factory);
} }
/* Create swapchain */ app_register_exit_callback(gpu_shutdown);
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); scratch_end(scratch);
struct gpu_startup_receipt res = ZI; struct gpu_startup_receipt res = ZI;
return res; return res;
} }
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(gpu_shutdown)
{
#if DX12_DEBUG
/* Release objects to make live object reporting less noisy */
{
ID3D12Device_Release(G.device);
ID3D12CommandQueue_Release(G.swapchain_cq);
IDXGISwapChain3_Release(G.swapchain);
}
#endif
}
/* ========================== * /* ========================== *
* Handle * Handle
* ========================== */ * ========================== */

View File

@ -2076,6 +2076,7 @@ INTERNAL void user_update(void)
{ {
__profscope(render); __profscope(render);
struct rect user_viewport = RECT_FROM_V2(V2(0, 0), G.user_size); struct rect user_viewport = RECT_FROM_V2(V2(0, 0), G.user_size);
struct rect backbuffer_viewport = RECT_FROM_V2(V2(0, 0), G.screen_size); struct rect backbuffer_viewport = RECT_FROM_V2(V2(0, 0), G.screen_size);
struct v2i32 user_resolution = v2_round_to_int(user_viewport.size); struct v2i32 user_resolution = v2_round_to_int(user_viewport.size);