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
*.exe
.vs/*
.vscode/*
unused/
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);
/* Start callback threads */
/* TODO: Create these threads when the callbacks are initially registered and have them wait on exit */
{
__profscope(app_start_exit_callbacks);
for (struct exit_callback *callback = G.exit_callbacks_head; callback; callback = callback->next) {

View File

@ -6,6 +6,7 @@
#include "memory.h"
#include "string.h"
#include "scratch.h"
#include "app.h"
#pragma warning(push, 0)
# define UNICODE
@ -68,13 +69,21 @@ GLOBAL struct {
struct dx12_handle_entry *first_free_handle_entry;
u64 num_handle_entries_reserved;
/* Device */
ID3D12Device *device;
/* Swapchain */
u32 swapchain_frame_index;
ID3D12CommandQueue *swapchain_cq;
IDXGISwapChain3 *swapchain;
} G = ZI, DEBUG_ALIAS(G, G_gpu_dx12);
/* ========================== *
* Startup
* ========================== */
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(gpu_shutdown);
struct gpu_startup_receipt gpu_startup(struct sys_window *window)
{
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_arena = arena_alloc(GIGABYTE(64));
/* Create debug controller */
u32 dxgi_factory_flags = 0;
#if DX12_DEBUG
ID3D12Debug *debug_controller = NULL;
/* Initialize dx12 */
{
hr = D3D12GetDebugInterface(&IID_ID3D12Debug, (void **)&debug_controller);
if (!SUCCEEDED(hr)) {
sys_panic(LIT("Failed to create D3D12 debug controller"));
/* Enable debug layer */
u32 dxgi_factory_flags = 0;
#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
/* 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 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;
/* 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;
}
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);
}
}
/* 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"));
/* Enable 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);
}
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"));
/* Enable 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);
}
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;
/* Create direct command queue */
ID3D12CommandQueue *swapchain_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"));
hr = ID3D12Device_CreateCommandQueue(device, &desc, &IID_ID3D12CommandQueue, (void **)&swapchain_cq);
if (!SUCCEEDED(hr)) {
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 */
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
};
app_register_exit_callback(gpu_shutdown);
/* 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;
}
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
* ========================== */

View File

@ -2076,6 +2076,7 @@ INTERNAL void user_update(void)
{
__profscope(render);
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 v2i32 user_resolution = v2_round_to_int(user_viewport.size);