dx12 testing

This commit is contained in:
jacob 2025-06-04 00:29:25 -05:00
parent da2de90de1
commit d0ec962123
4 changed files with 319 additions and 13 deletions

View File

@ -80,7 +80,7 @@
#define DX12_TEST 0 #define DX12_TEST 1

View File

@ -15,7 +15,15 @@ struct gpu_startup_receipt gpu_startup(struct sys_window *window);
* ========================== */ * ========================== */
struct gpu_handle { struct gpu_handle {
u64 v; union {
/* dx11 style */
u64 v;
/* dx12 style */
struct {
u64 gen;
u64 idx;
};
};
}; };
void gpu_release(struct gpu_handle handle); void gpu_release(struct gpu_handle handle);

View File

@ -338,12 +338,12 @@ struct gpu_startup_receipt gpu_startup(struct sys_window *window)
/* DXGI Debug break */ /* DXGI Debug break */
{ {
IDXGIInfoQueue *dxgiInfo; IDXGIInfoQueue *dxgi_info;
hr = DXGIGetDebugInterface1(0, &IID_IDXGIInfoQueue, (void **)&dxgiInfo); hr = DXGIGetDebugInterface1(0, &IID_IDXGIInfoQueue, (void **)&dxgi_info);
ASSERT(SUCCEEDED(hr)); ASSERT(SUCCEEDED(hr));
IDXGIInfoQueue_SetBreakOnSeverity(dxgiInfo, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, TRUE); IDXGIInfoQueue_SetBreakOnSeverity(dxgi_info, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, TRUE);
IDXGIInfoQueue_SetBreakOnSeverity(dxgiInfo, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, TRUE); IDXGIInfoQueue_SetBreakOnSeverity(dxgi_info, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, TRUE);
IDXGIInfoQueue_Release(dxgiInfo); IDXGIInfoQueue_Release(dxgi_info);
} }
#endif #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 */ /* Disable Alt+Enter changing monitor resolution to match window size */
IDXGIFactory_MakeWindowAssociation(factory, hwnd, DXGI_MWA_NO_ALT_ENTER); IDXGIFactory_MakeWindowAssociation(factory, hwnd, DXGI_MWA_NO_ALT_ENTER);
IDXGISwapChain1_Release(swapchain1);
IDXGIFactory2_Release(factory); IDXGIFactory2_Release(factory);
IDXGIAdapter_Release(dxgiAdapter); IDXGIAdapter_Release(dxgiAdapter);
IDXGIDevice_Release(dxgiDevice); IDXGIDevice_Release(dxgiDevice);
@ -2066,7 +2067,7 @@ void gpu_swap_backbuffer(i32 vsync)
gpu_capture_image_for_profiler(); gpu_capture_image_for_profiler();
{ {
__profscope(Present); __profscope(Present);
IDXGISwapChain1_Present(G.swapchain, vsync, flags); IDXGISwapChain2_Present(G.swapchain, vsync, flags);
__prof_dx11_collect(G.profiling_ctx); __prof_dx11_collect(G.profiling_ctx);
__profframe(0); __profframe(0);
} }

View File

@ -2,13 +2,73 @@
#include "gpu.h" #include "gpu.h"
#include "sys.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 <Windows.h>
# include <d3d12.h>
# include <dxgidebug.h>
# include <dxgi1_6.h>
# include <combaseapi.h>
#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 state
* ========================== */ * ========================== */
GLOBAL struct { 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); } G = ZI, DEBUG_ALIAS(G, G_gpu_dx12);
/* ========================== * /* ========================== *
@ -17,7 +77,163 @@ GLOBAL struct {
struct gpu_startup_receipt gpu_startup(struct sys_window *window) 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; struct gpu_startup_receipt res = ZI;
return res; return res;
} }
@ -26,23 +242,104 @@ struct gpu_startup_receipt gpu_startup(struct sys_window *window)
* Handle * 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) 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 * 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) struct gpu_handle gpu_texture_alloc(enum gpu_texture_format format, u32 flags, struct v2i32 size, void *initial_data)
{ {
(UNUSED)format; (UNUSED)format;
(UNUSED)flags; (UNUSED)flags;
(UNUSED)size; (UNUSED)size;
(UNUSED)initial_data; (UNUSED)initial_data;
struct gpu_handle res = ZI; struct dx12_texture *t = NULL;
return res;
return handle_alloc(DX12_HANDLE_KIND_TEXTURE, t);
} }
void gpu_texture_clear(struct gpu_handle target_texture, u32 clear_color) void gpu_texture_clear(struct gpu_handle target_texture, u32 clear_color)