power_play/src/gp_dx12.c
2025-06-23 15:08:41 -05:00

2281 lines
77 KiB
C

#if DX12_TEST
#include "gp.h"
#include "sys.h"
#include "arena.h"
#include "memory.h"
#include "string.h"
#include "scratch.h"
#include "app.h"
#include "work.h"
#include "log.h"
#include "resource.h"
#include "atomic.h"
#include "util.h"
#include "rand.h"
#include "sprite.h"
#include "gstat.h"
/* Include common shader types */
#define SH_CPU 1
#include "../res/sh/sh_common.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>
# include <d3dcompiler.h>
#pragma warning(pop)
#pragma comment(lib, "d3d12")
#pragma comment(lib, "dxgi")
#pragma comment(lib, "dxguid")
#pragma comment(lib, "d3dcompiler")
//#define DX12_WAIT_FRAME_LATENCY 1
//#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_ALLOW_TEARING 1
#define DX12_SWAPCHAIN_FLAGS (DX12_ALLOW_TEARING * DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING)
#define DX12_SWAPCHAIN_BUFFER_COUNT (3)
#define DX12_SWAPCHAIN_FORMAT (DXGI_FORMAT_R8G8B8A8_UNORM)
//#define DX12_SWAPCHAIN_RTV_FORMAT (DXGI_FORMAT_R8G8B8A8_UNORM_SRGB)
/* Arbitrary limits */
#define DX12_NUM_CBV_SRV_UAV_DESCRIPTORS (1024 * 64)
#define DX12_NUM_RTV_DESCRIPTORS (1024 * 1)
#define DX12_COMMAND_BUFFER_MIN_SIZE (1024 * 64)
#if RTC
# define DX12_DEBUG 1
# define DX12_SHADER_DEBUG 1
#else
# define DX12_DEBUG 0
# define DX12_SHADER_DEBUG 0
#endif
struct shader_desc {
char *file;
char *func;
};
struct pipeline_desc {
char *name;
struct shader_desc vs;
struct shader_desc ps;
u32 flags;
};
struct pipeline {
struct pipeline_desc desc;
ID3D12PipelineState *pso;
ID3D12RootSignature *rootsig;
};
struct pipeline_result {
struct pipeline pipeline;
i64 elapsed;
u64 errors_text_len;
u8 errors_text[KILOBYTE(16)];
};
struct pipeline_error {
struct string msg;
};
struct command_queue {
D3D12_COMMAND_LIST_TYPE type;
ID3D12CommandQueue *cq;
struct arena *arena;
struct sys_mutex *mutex;
struct command_list *first_submitted_command_list;
struct command_list *last_submitted_command_list;
struct atomic_u64 fence_target;
ID3D12Fence *fence;
};
struct command_list {
struct command_queue *cq;
struct ID3D12CommandAllocator *ca;
struct ID3D12GraphicsCommandList *cl;
struct command_descriptor_heap *first_command_descriptor_heap;
struct command_buffer *first_command_buffer;
u64 submitted_fence_target;
struct command_list *prev_submitted;
struct command_list *next_submitted;
};
struct command_descriptor_heap {
D3D12_DESCRIPTOR_HEAP_TYPE type;
ID3D12DescriptorHeap *heap;
D3D12_CPU_DESCRIPTOR_HANDLE cpu_handle;
D3D12_GPU_DESCRIPTOR_HANDLE gp_handle;
struct command_descriptor_heap *next_in_command_list;
u64 submitted_fence_target;
struct command_queue *submitted_cq;
struct command_descriptor_heap *prev_submitted;
struct command_descriptor_heap *next_submitted;
};
struct command_buffer {
struct command_buffer_group *group;
struct dx12_resource *resource;
u64 size;
struct command_buffer *next_in_command_list;
u64 submitted_fence_target;
struct command_queue *submitted_cq;
struct command_buffer *prev_submitted;
struct command_buffer *next_submitted;
};
struct command_buffer_group {
struct command_buffer *first_submitted;
struct command_buffer *last_submitted;
};
struct descriptor {
struct cpu_descriptor_heap *heap;
u32 index;
D3D12_CPU_DESCRIPTOR_HANDLE handle;
struct descriptor *next_free;
};
struct dx12_resource {
ID3D12Resource *resource;
enum D3D12_RESOURCE_STATES state;
#if 0
D3D12_CPU_DESCRIPTOR_HANDLE cbv_handle;
D3D12_CPU_DESCRIPTOR_HANDLE srv_handle;
D3D12_CPU_DESCRIPTOR_HANDLE uav_handle;
D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle;
#else
struct descriptor *cbv_descriptor;
struct descriptor *srv_descriptor;
struct descriptor *uav_descriptor;
struct descriptor *rtv_descriptor;
#endif
D3D12_GPU_VIRTUAL_ADDRESS gpu_address; /* NOTE: 0 for textures */
struct v2i32 texture_size;
struct dx12_resource *next_free;
};
struct cpu_descriptor_heap {
enum D3D12_DESCRIPTOR_HEAP_TYPE type;
struct arena *arena;
struct sys_mutex *mutex;
u32 descriptor_size;
u32 num_descriptors_reserved;
u32 num_descriptors_capacity;
struct descriptor *first_free_descriptor;
ID3D12DescriptorHeap *heap;
struct D3D12_CPU_DESCRIPTOR_HANDLE handle;
};
enum handle_kind {
DX12_HANDLE_KIND_NONE,
DX12_HANDLE_KIND_RESOURCE,
DX12_HANDLE_KIND_FLOW,
NUM_DX12_HANDLE_KINDS
};
struct handle_entry {
enum handle_kind kind;
u64 gen;
u64 idx;
void *data;
struct handle_entry *next_free;
};
/* ========================== *
* Global state
* ========================== */
GLOBAL struct {
/* Handles pool */
struct sys_mutex *handle_entries_mutex;
struct arena *handle_entries_arena;
struct handle_entry *first_free_handle_entry;
u64 num_handle_entries_reserved;
/* Descriptor heaps pool */
struct sys_mutex *command_descriptor_heaps_mutex;
struct arena *command_descriptor_heaps_arena;
struct command_descriptor_heap *first_submitted_command_descriptor_heap;
struct command_descriptor_heap *last_submitted_command_descriptor_heap;
/* Command buffers pool */
struct sys_mutex *command_buffers_mutex;
struct arena *command_buffers_arena;
struct dict *command_buffers_dict;
/* Resources pool */
struct sys_mutex *resources_mutex;
struct arena *resources_arena;
struct dx12_resource *first_free_resource;
/* FIXME: Remove this (testing) */
struct pipeline test_pipeline;
/* Factory */
IDXGIFactory6 *factory;
/* Adapter */
IDXGIAdapter1 *adapter;
/* Device */
ID3D12Device *device;
/* Descriptor sizes */
u32 desc_sizes[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES];
u32 desc_counts[D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES];
/* Global descriptor heaps */
struct cpu_descriptor_heap *cbv_srv_uav_heap;
struct cpu_descriptor_heap *rtv_heap;
/* Command queues */
/* TODO: Add optional mode to route everything to direct queue */
struct command_queue *cq_direct;
struct command_queue *cq_compute;
struct command_queue *cq_copy_critical;
struct command_queue *cq_copy_background;
/* Swapchain */
HWND swapchain_hwnd;
struct v2i32 swapchain_resolution;
u32 swapchain_frame_index;
IDXGISwapChain3 *swapchain;
struct dx12_resource *swapchain_resources[DX12_SWAPCHAIN_BUFFER_COUNT];
} G = ZI, DEBUG_ALIAS(G, G_gp_dx12);
/* ========================== *
* Startup
* ========================== */
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(gp_shutdown);
INTERNAL void dx12_init_device(void);
INTERNAL void dx12_init_objects(void);
INTERNAL void dx12_init_pipelines(void);
INTERNAL struct cpu_descriptor_heap *cpu_descriptor_heap_alloc(enum D3D12_DESCRIPTOR_HEAP_TYPE type);
INTERNAL struct command_queue *command_queue_alloc(enum D3D12_COMMAND_LIST_TYPE type, enum D3D12_COMMAND_QUEUE_PRIORITY priority);
INTERNAL void command_queue_release(struct command_queue *cq);
INTERNAL void dx12_resource_release(struct dx12_resource *resource);
struct gp_startup_receipt gp_startup(struct work_startup_receipt *work_sr)
{
__prof;
(UNUSED)work_sr;
/* Initialize handles pool */
G.handle_entries_mutex = sys_mutex_alloc();
G.handle_entries_arena = arena_alloc(GIGABYTE(64));
/* Initialize command descriptor heaps pool */
G.command_descriptor_heaps_mutex = sys_mutex_alloc();
G.command_descriptor_heaps_arena = arena_alloc(GIGABYTE(64));
/* Initialize command buffers pool */
G.command_buffers_mutex = sys_mutex_alloc();
G.command_buffers_arena = arena_alloc(GIGABYTE(64));
G.command_buffers_dict = dict_init(G.command_buffers_arena, 4096);
/* Initialize resources pool */
G.resources_mutex = sys_mutex_alloc();
G.resources_arena = arena_alloc(GIGABYTE(64));
/* Initialize dx12 */
dx12_init_device();
dx12_init_objects();
dx12_init_pipelines();
/* Register callbacks */
app_register_exit_callback(gp_shutdown);
struct gp_startup_receipt res = ZI;
return res;
}
INTERNAL APP_EXIT_CALLBACK_FUNC_DEF(gp_shutdown)
{
__prof;
#if DX12_DEBUG
/* Release objects to make live object reporting less noisy */
for (u64 i = 0; i < ARRAY_COUNT(G.swapchain_resources); ++i) {
dx12_resource_release(G.swapchain_resources[i]);
}
IDXGISwapChain3_Release(G.swapchain);
command_queue_release(G.cq_copy_background);
command_queue_release(G.cq_copy_critical);
command_queue_release(G.cq_compute);
command_queue_release(G.cq_direct);
ID3D12Device_Release(G.device);
#endif
}
/* ========================== *
* Handle
* ========================== */
INTERNAL void dx12_resource_release(struct dx12_resource *t);
INTERNAL struct gp_handle handle_alloc(enum handle_kind kind, void *data)
{
u64 old_gen = 0;
u64 idx = 0;
struct 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 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 gp_handle res = ZI;
res.gen = entry->gen;
res.idx = entry->idx;
return res;
}
INTERNAL struct handle_entry *handle_get_entry(struct gp_handle handle, struct sys_lock *lock)
{
sys_assert_locked_e_or_s(lock, G.handle_entries_mutex);
struct handle_entry *res = NULL;
if (handle.idx > 0 && handle.idx < G.num_handle_entries_reserved) {
struct handle_entry *tmp = &((struct handle_entry *)arena_base(G.handle_entries_arena))[handle.idx];
if (tmp->gen == handle.gen) {
res = tmp;
}
}
return res;
}
INTERNAL void *handle_get_data(struct gp_handle handle, enum handle_kind kind)
{
void *data = NULL;
if (handle.gen) {
struct sys_lock lock = sys_mutex_lock_s(G.handle_entries_mutex);
{
struct handle_entry *entry = handle_get_entry(handle, &lock);
data = entry->data;
#if RTC
/* Handle should match expected kind */
ASSERT(entry->kind == kind);
#endif
}
sys_mutex_unlock(&lock);
}
return data;
}
/* 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 gp_release(struct gp_handle handle)
{
enum handle_kind kind = 0;
void *data = NULL;
/* Release handle entry */
struct sys_lock lock = sys_mutex_lock_e(G.handle_entries_mutex);
{
struct 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_RESOURCE:
{
dx12_resource_release(data);
} break;
}
}
}
/* ========================== *
* Dx12 device initialization
* ========================== */
INTERNAL void dx12_init_error(struct string error)
{
struct arena_temp 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_device(void)
{
__prof;
struct arena_temp 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 */
hr = CreateDXGIFactory2(dxgi_factory_flags, &IID_IDXGIFactory6, (void **)&G.factory);
if (FAILED(hr)) {
dx12_init_error(LIT("Failed to initialize DXGI factory"));
}
/* Create device */
{
IDXGIAdapter1 *adapter = NULL;
ID3D12Device *device = NULL;
struct string error = LIT("Could not initialize GPU device.");
struct string first_gpu_name = ZI;
u32 adapter_index = 0;
while (true) {
hr = IDXGIFactory6_EnumAdapterByGpuPreference(G.factory, adapter_index, DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, &IID_IDXGIAdapter1, (void **)&adapter);
if (SUCCEEDED(hr)) {
DXGI_ADAPTER_DESC1 desc;
IDXGIAdapter1_GetDesc1(adapter, &desc);
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)) {
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);
}
G.adapter = adapter;
G.device = device;
}
#if DX12_DEBUG
/* Enable D3D12 Debug break */
{
ID3D12InfoQueue *info = NULL;
hr = ID3D12Device_QueryInterface(G.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
scratch_end(scratch);
}
/* ========================== *
* Dx12 object initialization
* ========================== */
INTERNAL void dx12_init_objects(void)
{
/* Initialize desc sizes */
G.desc_sizes[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV] = ID3D12Device_GetDescriptorHandleIncrementSize(G.device, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
G.desc_sizes[D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER] = ID3D12Device_GetDescriptorHandleIncrementSize(G.device, D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
G.desc_sizes[D3D12_DESCRIPTOR_HEAP_TYPE_RTV] = ID3D12Device_GetDescriptorHandleIncrementSize(G.device, D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
G.desc_sizes[D3D12_DESCRIPTOR_HEAP_TYPE_DSV] = ID3D12Device_GetDescriptorHandleIncrementSize(G.device, D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
/* Initialize desc counts */
G.desc_counts[D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV] = DX12_NUM_CBV_SRV_UAV_DESCRIPTORS;
G.desc_counts[D3D12_DESCRIPTOR_HEAP_TYPE_RTV] = DX12_NUM_RTV_DESCRIPTORS;
/* Create global descriptor heaps */
G.cbv_srv_uav_heap = cpu_descriptor_heap_alloc(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
G.rtv_heap = cpu_descriptor_heap_alloc(D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
/* Create direct command queue */
G.cq_direct = command_queue_alloc(D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL);
G.cq_compute = command_queue_alloc(D3D12_COMMAND_LIST_TYPE_COMPUTE, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL);
G.cq_copy_critical = command_queue_alloc(D3D12_COMMAND_LIST_TYPE_COPY, D3D12_COMMAND_QUEUE_PRIORITY_HIGH);
G.cq_copy_background = command_queue_alloc(D3D12_COMMAND_LIST_TYPE_COPY, D3D12_COMMAND_QUEUE_PRIORITY_NORMAL);
}
/* ========================== *
* Dx12 pipeline initialization
* ========================== */
/* TDOO: Rename 'mesh shader' to 'triangle shader' or something */
/* TODO: Move shader structs into shared C-HLSL header file */
/* ============= */
/* Mesh pipeline */
/* ============= */
/* Material pipeline */
#if 0
PACK(struct fx_material_constant {
struct mat4x4 vp;
u32 instance_offset;
});
PACK(struct fx_material_instance {
struct xform xf;
struct v2 uv0;
struct v2 uv1;
u32 tint_srgb;
f32 emittance;
});
#endif
/* ============= */
/* Grid pipeline */
/* ============= */
/* Init pipelines */
INTERNAL struct pipeline_result *pipeline_alloc_from_descs(struct arena *arena, u64 num_pipelines, struct pipeline_desc *descs);
INTERNAL void pipeline_release(struct pipeline *pipeline);
INTERNAL void dx12_init_pipelines(void)
{
__prof;
struct arena_temp scratch = scratch_begin_no_conflict();
struct pipeline_desc pipeline_descs[] = {
/* Material pipeline */
{
.name = "material",
.vs = { "sh/material.hlsl", "vs" },
.ps = { "sh/material.hlsl", "ps" }
}
};
struct pipeline_result *results = pipeline_alloc_from_descs(scratch.arena, ARRAY_COUNT(pipeline_descs), pipeline_descs);
for (u64 i = 0; i < ARRAY_COUNT(pipeline_descs); ++i) {
struct pipeline_result *result = &results[i];
if (result->errors_text_len > 0) {
struct string msg = STRING(result->errors_text_len, result->errors_text);
sys_panic(msg);
pipeline_release(&result->pipeline);
} else {
/* FIXME: remove this */
G.test_pipeline = result->pipeline;
}
}
scratch_end(scratch);
}
/* ========================== *
* Shader compilation
* ========================== */
struct dx12_include_handler {
ID3DInclude d3d_handler;
ID3DIncludeVtbl vtbl;
struct pipeline *pipeline;
u64 num_open_resources;
struct resource open_resources[1024];
};
INTERNAL HRESULT dx12_include_open(ID3DInclude *d3d_handler, D3D_INCLUDE_TYPE include_type, LPCSTR name_cstr, LPCVOID parent_data, LPCVOID *data_out, UINT *data_len_out)
{
__prof;
(UNUSED)include_type;
(UNUSED)parent_data;
HRESULT result = E_FAIL;
struct dx12_include_handler *handler = (struct dx12_include_handler *)d3d_handler;
struct string name = string_from_cstr_no_limit((char *)name_cstr);
if (handler->num_open_resources >= ARRAY_COUNT(handler->open_resources)) {
sys_panic(LIT("Dx12 include handler resource overflow"));
}
struct resource *res = &handler->open_resources[handler->num_open_resources++];
*res = resource_open(name);
if (resource_exists(res)) {
++handler->num_open_resources;
struct string data = resource_get_data(res);
*data_out = data.text;
*data_len_out = data.len;
result = S_OK;
}
#if 0
#if RESOURCE_RELOADING
shader_add_include(&G.shader_info[handler->shader->kind], name);
#endif
#endif
return result;
}
INTERNAL HRESULT dx12_include_close(ID3DInclude *d3d_handler, LPCVOID data)
{
__prof;
(UNUSED)data;
struct dx12_include_handler *handler = (struct dx12_include_handler *)d3d_handler;
for (u64 i = 0; i < handler->num_open_resources; ++i) {
struct resource *res = &handler->open_resources[i];
resource_close(res);
}
handler->num_open_resources = 0;
return S_OK;
}
INTERNAL struct dx12_include_handler *dx12_include_handler_alloc(struct arena *arena, struct pipeline *pipeline)
{
struct dx12_include_handler *handler = arena_push(arena, struct dx12_include_handler);
handler->d3d_handler.lpVtbl = &handler->vtbl;
handler->vtbl.Open = dx12_include_open;
handler->vtbl.Close = dx12_include_close;
handler->pipeline = pipeline;
return handler;
}
INTERNAL void dx12_include_handler_release(struct dx12_include_handler *handler)
{
for (u64 i = 0; i < handler->num_open_resources; ++i) {
ASSERT(false); /* Resource should have been closed by handler by now */
struct resource *res = &handler->open_resources[i];
resource_close(res);
}
handler->num_open_resources = 0;
}
enum shader_compile_task_kind {
SHADER_COMPILE_TASK_KIND_VS,
SHADER_COMPILE_TASK_KIND_PS
};
struct shader_compile_task_arg {
/* In */
enum shader_compile_task_kind kind;
struct pipeline *pipeline;
struct shader_desc shader_desc;
struct resource *shader_res;
/* Out */
b32 success;
ID3DBlob *blob;
ID3DBlob *error_blob;
i64 elapsed;
};
/* TODO: Compile shaders offline w/ dxc for performance & language features like static_assert */
INTERNAL WORK_TASK_FUNC_DEF(shader_compile_task, comp_arg_raw)
{
__prof;
struct shader_compile_task_arg *comp_arg = (struct shader_compile_task_arg *)comp_arg_raw;
enum shader_compile_task_kind kind = comp_arg->kind;
struct pipeline *pipeline = comp_arg->pipeline;
struct shader_desc shader_desc = comp_arg->shader_desc;
struct resource *shader_res = comp_arg->shader_res;
struct arena_temp scratch = scratch_begin_no_conflict();
{
i64 start_ns = sys_time_ns();
b32 success = false;
ID3DBlob *blob = NULL;
ID3DBlob *error_blob = NULL;
struct string file_name = string_from_cstr_no_limit(shader_desc.file);
struct string func_name = string_from_cstr_no_limit(shader_desc.func);
if (resource_exists(shader_res)) {
struct dx12_include_handler *include_handler = dx12_include_handler_alloc(scratch.arena, pipeline);
u32 d3d_compile_flags = D3DCOMPILE_ENABLE_UNBOUNDED_DESCRIPTOR_TABLES;
#if DX12_SHADER_DEBUG
d3d_compile_flags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_ENABLE_STRICTNESS;
#else
d3d_compile_flags |= D3DCOMPILE_OPTIMIZATION_LEVEL3;
#endif
/* Compile shader */
{
struct string shader_src = resource_get_data(shader_res);
logf_info("Compiling shader \"%F:%F\"", FMT_STR(file_name), FMT_STR(func_name));
/* Compile shader */
struct string friendly_name = string_cat(scratch.arena, LIT("res/"), file_name);
char *friendly_name_cstr = cstr_from_string(scratch.arena, friendly_name);
char *target = NULL;
switch (kind) {
case SHADER_COMPILE_TASK_KIND_VS:
{
target = "vs_5_1";
} break;
case SHADER_COMPILE_TASK_KIND_PS:
{
target = "ps_5_1";
} break;
}
D3D_SHADER_MACRO defines[] = {
{ "SH_CPU", "0" },
{ NULL, NULL }
};
HRESULT hr = D3DCompile(shader_src.text, shader_src.len, friendly_name_cstr, defines, (ID3DInclude *)include_handler, shader_desc.func, target, d3d_compile_flags, 0, &blob, &error_blob);
success = SUCCEEDED(hr) && !error_blob;
}
dx12_include_handler_release(include_handler);
}
#if 0
if (success) {
logf_success("Finished compiling shader \"%F\" in %F seconds", FMT_STR(src_name), FMT_FLOAT(SECONDS_FROM_NS(sys_time_ns() - start_ns)));
}
#endif
comp_arg->success = success;
comp_arg->blob = blob;
comp_arg->error_blob = error_blob;
comp_arg->elapsed = sys_time_ns() - start_ns;
}
scratch_end(scratch);
}
/* ========================== *
* Pipeline
* ========================== */
struct pipeline_load_task_arg {
struct pipeline *pipeline;
struct pipeline_result *result;
};
INTERNAL WORK_TASK_FUNC_DEF(pipeline_load_task, load_arg_raw)
{
__prof;
struct pipeline_load_task_arg *load_arg = (struct pipeline_load_task_arg *)load_arg_raw;
struct pipeline *pipeline = load_arg->pipeline;
struct pipeline_desc desc = pipeline->desc;
struct pipeline_result *result = load_arg->result;
struct arena_temp scratch = scratch_begin_no_conflict();
{
i64 start_ns = sys_time_ns();
struct string pipeline_name = string_from_cstr_no_limit(desc.name);
logf_info("Loading pipeline \"%F\"", FMT_STR(pipeline_name));
b32 success = true;
HRESULT hr = 0;
struct string error_str = LIT("Unknown error");
struct string vs_filename = string_from_cstr_no_limit(desc.vs.file);
struct string ps_filename = string_from_cstr_no_limit(desc.ps.file);
b32 ps_res_is_shared = string_eq(vs_filename, ps_filename);
struct resource vs_res = resource_open(vs_filename);
struct resource ps_res = vs_res;
if (!ps_res_is_shared) {
ps_res = resource_open(ps_filename);
}
if (success) {
if (!resource_exists(&vs_res)) {
error_str = string_format(scratch.arena, LIT("Shader source \"%F\" not found"), FMT_STR(vs_filename));
success = false;
} else if (!resource_exists(&ps_res)) {
error_str = string_format(scratch.arena, LIT("Shader source \"%F\" not found"), FMT_STR(ps_filename));
success = false;
}
}
struct shader_compile_task_arg vs = ZI;
vs.kind = SHADER_COMPILE_TASK_KIND_VS;
vs.pipeline = pipeline;
vs.shader_desc = desc.vs;
vs.shader_res = &vs_res;
struct shader_compile_task_arg ps = ZI;
ps.kind = SHADER_COMPILE_TASK_KIND_PS;
ps.pipeline = pipeline;
ps.shader_desc = desc.ps;
ps.shader_res = &ps_res;
/* Compile shaders */
if (success) {
struct work_slate ws = work_slate_begin();
work_slate_push_task(&ws, shader_compile_task, &vs);
work_slate_push_task(&ws, shader_compile_task, &ps);
struct work_handle work = work_slate_end_and_help(&ws, WORK_PRIORITY_HIGH);
work_wait(work);
success = vs.success && ps.success;
}
/* Get root signature blob
* NOTE: This isn't necessary for creating the root signature (since it
* could reuse the shader blob), however we'd like to verify that the
* root signature exists and matches between shaders. */
ID3D10Blob *rootsig_blob = NULL;
if (success) {
__profscope(Validate root signatures);
char *vs_rootsig_data = NULL;
char *ps_rootsig_data = NULL;
u32 vs_rootsig_data_len = 0;
u32 ps_rootsig_data_len = 0;
ID3D10Blob *vs_rootsig_blob = NULL;
ID3D10Blob *ps_rootsig_blob = NULL;
D3DGetBlobPart(ID3D10Blob_GetBufferPointer(vs.blob), ID3D10Blob_GetBufferSize(vs.blob), D3D_BLOB_ROOT_SIGNATURE, 0, &vs_rootsig_blob);
D3DGetBlobPart(ID3D10Blob_GetBufferPointer(ps.blob), ID3D10Blob_GetBufferSize(ps.blob), D3D_BLOB_ROOT_SIGNATURE, 0, &ps_rootsig_blob);
if (vs_rootsig_blob) {
vs_rootsig_data = ID3D10Blob_GetBufferPointer(vs_rootsig_blob);
vs_rootsig_data_len = ID3D10Blob_GetBufferSize(vs_rootsig_blob);
}
if (ps_rootsig_blob) {
ps_rootsig_data = ID3D10Blob_GetBufferPointer(ps_rootsig_blob);
ps_rootsig_data_len = ID3D10Blob_GetBufferSize(ps_rootsig_blob);
}
if (vs_rootsig_data_len == 0) {
success = false;
error_str = LIT("Vertex shader is missing root signature");
} else if (ps_rootsig_data_len == 0) {
success = false;
error_str = LIT("Pixel shader is missing root signature");
} else if (vs_rootsig_data_len != ps_rootsig_data_len || !MEMEQ(vs_rootsig_data, ps_rootsig_data, vs_rootsig_data_len)) {
success = false;
error_str = LIT("Root signature mismatch between vertex and pixel shader");
} else {
rootsig_blob = vs_rootsig_blob;
}
if (ps_rootsig_blob) {
ID3D10Blob_Release(ps_rootsig_blob);
}
}
/* Create root signature */
ID3D12RootSignature *rootsig = NULL;
if (success) {
__profscope(Create root signature);
hr = ID3D12Device_CreateRootSignature(G.device, 0, ID3D10Blob_GetBufferPointer(rootsig_blob), ID3D10Blob_GetBufferSize(rootsig_blob), &IID_ID3D12RootSignature, (void **)&rootsig);
if (FAILED(hr)) {
error_str = LIT("Failed to create root signature");
success = false;
}
}
/* Create PSO */
ID3D12PipelineState *pso = NULL;
if (success) {
/* Default rasterizer state */
__profscope(Create PSO);
D3D12_RASTERIZER_DESC raster_desc = {
.FillMode = D3D12_FILL_MODE_SOLID,
.CullMode = D3D12_CULL_MODE_BACK,
.FrontCounterClockwise = FALSE,
.DepthBias = D3D12_DEFAULT_DEPTH_BIAS,
.DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP,
.SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS,
.DepthClipEnable = TRUE,
.MultisampleEnable = FALSE,
.AntialiasedLineEnable = FALSE,
.ForcedSampleCount = 0,
.ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF
};
/* No input layout */
D3D12_INPUT_LAYOUT_DESC input_layout_desc = {
.pInputElementDescs = NULL,
.NumElements = 0
};
/* Blend state */
D3D12_BLEND_DESC blend_desc = {
.AlphaToCoverageEnable = FALSE,
.IndependentBlendEnable = FALSE
};
blend_desc.RenderTarget[0].BlendEnable = TRUE;
blend_desc.RenderTarget[0].SrcBlend = D3D12_BLEND_SRC_ALPHA;
blend_desc.RenderTarget[0].DestBlend = D3D12_BLEND_INV_SRC_ALPHA;
blend_desc.RenderTarget[0].BlendOp = D3D12_BLEND_OP_ADD;
blend_desc.RenderTarget[0].SrcBlendAlpha = D3D12_BLEND_ONE;
blend_desc.RenderTarget[0].DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA;
blend_desc.RenderTarget[0].BlendOpAlpha = D3D12_BLEND_OP_ADD;
blend_desc.RenderTarget[0].RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL;
/* Disable depth stencil */
D3D12_DEPTH_STENCIL_DESC depth_stencil_desc = {
.DepthEnable = FALSE,
.StencilEnable = FALSE
};
/* PSO */
D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc = { 0 };
pso_desc.pRootSignature = rootsig;
if (vs.success) {
pso_desc.VS.pShaderBytecode = ID3D10Blob_GetBufferPointer(vs.blob);
pso_desc.VS.BytecodeLength = ID3D10Blob_GetBufferSize(vs.blob);
}
if (ps.success) {
pso_desc.PS.pShaderBytecode = ID3D10Blob_GetBufferPointer(ps.blob);
pso_desc.PS.BytecodeLength = ID3D10Blob_GetBufferSize(ps.blob);
}
pso_desc.BlendState = blend_desc;
pso_desc.SampleMask = UINT_MAX;
pso_desc.RasterizerState = raster_desc;
pso_desc.DepthStencilState = depth_stencil_desc;
pso_desc.InputLayout = input_layout_desc;
pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
pso_desc.NumRenderTargets = 1;
pso_desc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
pso_desc.SampleDesc.Count = 1;
pso_desc.SampleDesc.Quality = 0;
hr = ID3D12Device_CreateGraphicsPipelineState(G.device, &pso_desc, &IID_ID3D12PipelineState, (void **)&pso);
if (FAILED(hr)) {
error_str = LIT("Failed to create pipeline state object");
success = false;
}
}
/* Copy error */
if (!success) {
ID3D10Blob *error_blob = vs.error_blob ? vs.error_blob : ps.error_blob;
if (error_blob) {
u64 error_blob_cstr_len = ID3D10Blob_GetBufferSize(error_blob);
char *error_blob_cstr = (char *)ID3D10Blob_GetBufferPointer(error_blob);
struct string error_blob_str = string_copy(scratch.arena, string_from_cstr(error_blob_cstr, error_blob_cstr_len));
if (string_ends_with(error_blob_str, LIT("\n"))) {
/* Remove trailing newline */
error_blob_str.len -= 1;
}
if (error_blob_str.len > 0) {
error_str = error_blob_str;
}
}
result->errors_text_len = min_u64(error_str.len, ARRAY_COUNT(result->errors_text));
MEMCPY(result->errors_text, error_str.text, result->errors_text_len);
}
pipeline->pso = pso;
pipeline->rootsig = rootsig;
result->elapsed = sys_time_ns() - start_ns;
resource_close(&vs_res);
if (!ps_res_is_shared) {
resource_close(&ps_res);
}
if (rootsig_blob) {
ID3D10Blob_Release(rootsig_blob);
}
if (vs.blob) {
ID3D10Blob_Release(vs.blob);
}
if (vs.error_blob) {
ID3D10Blob_Release(vs.error_blob);
}
if (ps.blob) {
ID3D10Blob_Release(ps.blob);
}
if (ps.error_blob) {
ID3D10Blob_Release(ps.error_blob);
}
}
scratch_end(scratch);
}
INTERNAL struct pipeline_result *pipeline_alloc_from_descs(struct arena *arena, u64 num_pipelines, struct pipeline_desc *descs)
{
__prof;
struct pipeline_result *results = arena_push_array(arena, struct pipeline_result, num_pipelines);
struct pipeline_load_task_arg *task_args = arena_push_array(arena, struct pipeline_load_task_arg, num_pipelines);
/* Load pipelines */
struct work_slate ws = work_slate_begin();
for (u64 i = 0; i < num_pipelines; ++i) {
struct pipeline_result *result = &results[i];
struct pipeline *pipeline = &results->pipeline;
pipeline->desc = descs[i];
struct pipeline_load_task_arg *arg = &task_args[i];
arg->pipeline = pipeline;
arg->result = result;
work_slate_push_task(&ws, pipeline_load_task, arg);
}
struct work_handle work = work_slate_end_and_help(&ws, WORK_PRIORITY_HIGH);
work_wait(work);
return results;
}
INTERNAL void pipeline_release(struct pipeline *pipeline)
{
__prof;
if (pipeline->pso) {
ID3D12PipelineState_Release(pipeline->pso);
}
}
/* ========================== *
* Descriptor
* ========================== */
INTERNAL struct descriptor *descriptor_alloc(struct cpu_descriptor_heap *dh)
{
struct descriptor *d = NULL;
u32 index = 0;
D3D12_CPU_DESCRIPTOR_HANDLE handle = ZI;
{
struct sys_lock lock = sys_mutex_lock_e(dh->mutex);
if (dh->first_free_descriptor) {
d = dh->first_free_descriptor;
handle = d->handle;
index = d->index;
} else {
if (dh->num_descriptors_reserved >= dh->num_descriptors_capacity) {
sys_panic(LIT("Max descriptors reached in heap"));
}
d = arena_push_no_zero(dh->arena, struct descriptor);
index = dh->num_descriptors_reserved;
handle.ptr = dh->handle.ptr + (index * dh->descriptor_size);
++dh->num_descriptors_reserved;
}
sys_mutex_unlock(&lock);
}
MEMZERO_STRUCT(d);
d->heap = dh;
d->handle = handle;
d->index = index;
return d;
}
/* ========================== *
* CPU descriptor heap
* ========================== */
INTERNAL struct cpu_descriptor_heap *cpu_descriptor_heap_alloc(enum D3D12_DESCRIPTOR_HEAP_TYPE type)
{
struct cpu_descriptor_heap *dh = NULL;
{
struct arena *arena = arena_alloc(MEGABYTE(64));
dh = arena_push(arena, struct cpu_descriptor_heap);
dh->arena = arena;
}
dh->mutex = sys_mutex_alloc();
u32 num_descriptors = 0;
u32 descriptor_size = 0;
if (type < (i32)ARRAY_COUNT(G.desc_counts) && type < (i32)ARRAY_COUNT(G.desc_sizes)) {
num_descriptors = G.desc_counts[type];
descriptor_size = G.desc_sizes[type];
}
if (num_descriptors == 0 || descriptor_size == 0) {
sys_panic(LIT("Unsupported CPU descriptor type"));
}
dh->num_descriptors_capacity = num_descriptors;
dh->descriptor_size = descriptor_size;
D3D12_DESCRIPTOR_HEAP_DESC desc = ZI;
desc.Type = type;
desc.NumDescriptors = num_descriptors;
HRESULT hr = ID3D12Device_CreateDescriptorHeap(G.device, &desc, &IID_ID3D12DescriptorHeap, (void **)&dh->heap);
if (FAILED(hr)) {
sys_panic(LIT("Failed to create CPU descriptor heap"));
}
ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(dh->heap, &dh->handle);
return dh;
}
#if 0
INTERNAL void cpu_descriptor_heap_release(struct cpu_descriptor_heap *dh)
{
/* TODO */
(UNUSED)dh;
}
#endif
/* ========================== *
* Flow
* ========================== */
struct flow {
struct arena *arena;
/* Below fields are reset each dispatch */
struct sprite_scope *sprite_scope;
struct arena *material_instances_arena;
struct flow *next_free;
};
INTERNAL struct flow *flow_alloc(void)
{
struct flow *flow = NULL;
{
struct arena *arena = arena_alloc(MEGABYTE(64));
flow = arena_push(arena, struct flow);
flow->arena = arena;
}
flow->sprite_scope = sprite_scope_begin();
flow->material_instances_arena = arena_alloc(GIGABYTE(1));
return flow;
}
struct gp_handle gp_flow_alloc(void)
{
struct flow *flow = flow_alloc();
return handle_alloc(DX12_HANDLE_KIND_FLOW, flow);
}
void gp_push_cmd(struct gp_handle gp_flow, struct gp_cmd_params params)
{
struct flow *flow = handle_get_data(gp_flow, DX12_HANDLE_KIND_FLOW);
if (flow) {
switch (params.kind) {
default: break;
case GP_CMD_KIND_DRAW_TEXTURE:
{
struct dx12_resource *texture = NULL;
if (params.texture.texture.gen != 0) {
texture = handle_get_data(params.texture.texture, DX12_HANDLE_KIND_RESOURCE);
} else if (params.texture.sprite.hash != 0) {
struct sprite_texture *st = sprite_texture_from_tag_async(flow->sprite_scope, params.texture.sprite);
texture = handle_get_data(st->texture, DX12_HANDLE_KIND_RESOURCE);
}
if (texture) {
struct sh_material_instance *instance = arena_push(flow->material_instances_arena, struct sh_material_instance);
instance->xf = sh_float2x3_from_xform(params.texture.xf);
instance->uv0 = sh_float2_from_v2(params.texture.clip.p0);
instance->uv1 = sh_float2_from_v2(params.texture.clip.p1);
instance->texture_nuri = sh_uint_from_u32(texture->srv_descriptor->index);
instance->tint_srgb = sh_uint_from_u32(params.texture.tint);
instance->emittance = sh_float_from_f32(params.texture.emittance);
}
} break;
}
}
}
/* ========================== *
* Resource
* ========================== */
enum dx12_resource_view_flags {
DX12_RESOURCE_VIEW_FLAG_NONE = 0,
DX12_RESOURCE_VIEW_FLAG_CBV = (1 << 1),
DX12_RESOURCE_VIEW_FLAG_SRV = (1 << 2),
DX12_RESOURCE_VIEW_FLAG_UAV = (1 << 3),
DX12_RESOURCE_VIEW_FLAG_RTV = (1 << 4)
};
INTERNAL struct dx12_resource *dx12_resource_alloc(D3D12_HEAP_PROPERTIES heap_props, D3D12_HEAP_FLAGS heap_flags, D3D12_RESOURCE_DESC desc, D3D12_RESOURCE_STATES initial_state, enum dx12_resource_view_flags view_flags)
{
struct dx12_resource *r = NULL;
{
struct sys_lock lock = sys_mutex_lock_e(G.resources_mutex);
if (G.first_free_resource) {
r = G.first_free_resource;
G.first_free_resource = r->next_free;
} else {
r = arena_push_no_zero(G.resources_arena, struct dx12_resource);
}
sys_mutex_unlock(&lock);
}
MEMZERO_STRUCT(r);
D3D12_CLEAR_VALUE clear_value = { .Format = desc.Format, .Color = { 0 } };
D3D12_CLEAR_VALUE *clear_value_ptr = desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET ? &clear_value : NULL;
HRESULT hr = ID3D12Device_CreateCommittedResource(G.device, &heap_props, heap_flags, &desc, initial_state, clear_value_ptr, &IID_ID3D12Resource, (void **)&r->resource);
if (FAILED(hr)) {
/* TODO: Don't panic */
sys_panic(LIT("Failed to create resource"));
}
r->state = initial_state;
if (desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER) {
r->gpu_address = ID3D12Resource_GetGPUVirtualAddress(r->resource);
}
if (view_flags & DX12_RESOURCE_VIEW_FLAG_CBV) {
r->cbv_descriptor = descriptor_alloc(G.cbv_srv_uav_heap);
D3D12_CONSTANT_BUFFER_VIEW_DESC cbv_desc = ZI;
cbv_desc.BufferLocation = r->gpu_address;
//cbv_desc.SizeInBytes = desc.ByteWidth;
/* FIXME: Get actual size */
cbv_desc.SizeInBytes = KILOBYTE(64);
ID3D12Device_CreateConstantBufferView(G.device, &cbv_desc, r->cbv_descriptor->handle);
}
if (view_flags & DX12_RESOURCE_VIEW_FLAG_SRV) {
r->srv_descriptor = descriptor_alloc(G.cbv_srv_uav_heap);
ID3D12Device_CreateShaderResourceView(G.device, r->resource, NULL, r->srv_descriptor->handle);
}
if (view_flags & DX12_RESOURCE_VIEW_FLAG_UAV) {
r->uav_descriptor = descriptor_alloc(G.cbv_srv_uav_heap);
ID3D12Device_CreateUnorderedAccessView(G.device, r->resource, NULL, NULL, r->uav_descriptor->handle);
}
if (view_flags & DX12_RESOURCE_VIEW_FLAG_RTV) {
r->rtv_descriptor = descriptor_alloc(G.rtv_heap);
ID3D12Device_CreateRenderTargetView(G.device, r->resource, NULL, r->rtv_descriptor->handle);
}
return r;
}
INTERNAL struct dx12_resource *dx12_resource_alloc_from_swapchain_buffer(ID3D12Resource *buff, struct v2i32 texture_size)
{
struct dx12_resource *r = NULL;
{
struct sys_lock lock = sys_mutex_lock_e(G.resources_mutex);
if (G.first_free_resource) {
r = G.first_free_resource;
G.first_free_resource = r->next_free;
} else {
r = arena_push_no_zero(G.resources_arena, struct dx12_resource);
}
sys_mutex_unlock(&lock);
}
MEMZERO_STRUCT(r);
/* FIXME: Initialize dx12 resource struct here */
r->resource = buff;
r->rtv_descriptor = descriptor_alloc(G.rtv_heap);
r->texture_size = texture_size;
r->state = D3D12_RESOURCE_STATE_PRESENT; /* FIXME */
ID3D12Device_CreateRenderTargetView(G.device, r->resource, NULL, r->rtv_descriptor->handle);
return r;
}
INTERNAL void dx12_resource_release(struct dx12_resource *t)
{
(UNUSED)t;
}
INTERNAL void dx12_resource_release_now(struct dx12_resource *t)
{
(UNUSED)t;
}
INTERNAL enum D3D12_RESOURCE_STATES dx12_resource_barrier(ID3D12GraphicsCommandList *cl, struct dx12_resource *resource, enum D3D12_RESOURCE_STATES state)
{
enum D3D12_RESOURCE_STATES old_state = resource->state;
if (state != resource->state) {
struct D3D12_RESOURCE_TRANSITION_BARRIER rtb = ZI;
rtb.pResource = resource->resource;
rtb.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
rtb.StateBefore = resource->state;
rtb.StateAfter = state;
struct D3D12_RESOURCE_BARRIER rb = ZI;
rb.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
rb.Flags = 0;
rb.Transition = rtb;
ID3D12GraphicsCommandList_ResourceBarrier(cl, 1, &rb);
resource->state = state;
}
return old_state;
}
/* ========================== *
* Command queue
* ========================== */
INTERNAL struct command_queue *command_queue_alloc(enum D3D12_COMMAND_LIST_TYPE type, enum D3D12_COMMAND_QUEUE_PRIORITY priority)
{
struct command_queue *cq = NULL;
{
struct arena *arena = arena_alloc(GIGABYTE(64));
cq = arena_push(arena, struct command_queue);
cq->arena = arena;
}
cq->mutex = sys_mutex_alloc();
cq->type = type;
D3D12_COMMAND_QUEUE_DESC desc = ZI;
desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
desc.Type = type;
desc.Priority = priority;
HRESULT hr = ID3D12Device_CreateCommandQueue(G.device, &desc, &IID_ID3D12CommandQueue, (void **)&cq->cq);
if (FAILED(hr)) {
sys_panic(LIT("Failed to create command queue"));
}
hr = ID3D12Device_CreateFence(G.device, 0, 0, &IID_ID3D12Fence, (void **)&cq->fence);
if (FAILED(hr)) {
sys_panic(LIT("Failed to create command queue fence"));
}
return cq;
}
INTERNAL void command_queue_release(struct command_queue *cq)
{
/* TODO */
(UNUSED)cq;
//ID3D12CommandQueue_Release(G.cq_copy_background->cq);
}
/* ========================== *
* Command list
* ========================== */
INTERNAL struct command_list *command_list_open(struct command_queue *cq)
{
u64 queue_fence_value = ID3D12Fence_GetCompletedValue(cq->fence);
struct command_list *cl = NULL;
struct ID3D12GraphicsCommandList *old_cl = NULL;
struct ID3D12CommandAllocator *old_ca = NULL;
{
struct sys_lock lock = sys_mutex_lock_e(cq->mutex);
/* Find first command list ready for reuse */
for (struct command_list *tmp = cq->first_submitted_command_list; tmp; tmp = tmp->next_submitted) {
if (queue_fence_value >= tmp->submitted_fence_target) {
cl = tmp;
break;
}
}
if (cl) {
/* Remove from submitted list */
old_cl = cl->cl;
old_ca = cl->ca;
struct command_list *prev = cl->prev_submitted;
struct command_list *next = cl->next_submitted;
if (prev) {
prev->next_submitted = next;
} else {
cq->first_submitted_command_list = next;
}
if (next) {
next->prev_submitted = prev;
} else {
cq->last_submitted_command_list = prev;
}
} else {
cl = arena_push_no_zero(cq->arena, struct command_list);
}
sys_mutex_unlock(&lock);
}
MEMZERO_STRUCT(cl);
cl->cq = cq;
HRESULT hr = 0;
/* FIXME: Determine command list type from command queue */
if (old_cl) {
cl->cl = old_cl;
cl->ca = old_ca;
} else {
hr = ID3D12Device_CreateCommandAllocator(G.device, cq->type, &IID_ID3D12CommandAllocator, (void **)&cl->ca);
if (FAILED(hr)) {
sys_panic(LIT("Failed to create command allocator"));
}
hr = ID3D12Device_CreateCommandList(G.device, 0, cq->type, cl->ca, NULL, &IID_ID3D12GraphicsCommandList, (void **)&cl->cl);
if (FAILED(hr)) {
sys_panic(LIT("Failed to create command list"));
}
hr = ID3D12GraphicsCommandList_Close(cl->cl);
if (FAILED(hr)) {
sys_panic(LIT("Failed to close command list during initialization"));
}
}
/* Close */
hr = ID3D12CommandAllocator_Reset(cl->ca);
if (FAILED(hr)) {
sys_panic(LIT("Failed to reset command allocator"));
}
hr = ID3D12GraphicsCommandList_Reset(cl->cl, cl->ca, NULL);
if (FAILED(hr)) {
sys_panic(LIT("Failed to reset command list"));
}
return cl;
}
/* TODO: Allow multiple command list submissions */
INTERNAL u64 command_list_close(struct command_list *cl)
{
struct command_queue *cq = cl->cq;
/* Close & execute */
HRESULT hr = ID3D12GraphicsCommandList_Close(cl->cl);
if (FAILED(hr)) {
sys_panic(LIT("Failed to close command list before execution"));
}
ID3D12CommandQueue_ExecuteCommandLists(cq->cq, 1, (ID3D12CommandList **)&cl->cl);
/* Queue fence signal */
/* FIXME: Wrap execute & signal in mutex */
u64 target_fence_value = atomic_u64_eval_add_u64(&cq->fence_target, 1) + 1;
ID3D12CommandQueue_Signal(cq->cq, cq->fence, target_fence_value);
/* Add descriptor heaps to submitted list */
{
struct sys_lock lock = sys_mutex_lock_e(G.command_descriptor_heaps_mutex);
for (struct command_descriptor_heap *cdh = cl->first_command_descriptor_heap; cdh; cdh = cdh->next_in_command_list) {
cdh->submitted_cq = cq;
cdh->submitted_fence_target = target_fence_value;
if (G.last_submitted_command_descriptor_heap) {
G.last_submitted_command_descriptor_heap->next_submitted = cdh;
} else {
G.first_submitted_command_descriptor_heap = cdh;
}
G.last_submitted_command_descriptor_heap = cdh;
}
sys_mutex_unlock(&lock);
}
/* Add command buffers to submitted list */
{
struct sys_lock lock = sys_mutex_lock_e(G.command_buffers_mutex);
for (struct command_buffer *cb = cl->first_command_buffer; cb; cb = cb->next_in_command_list) {
struct command_buffer_group *group = cb->group;
cb->submitted_cq = cq;
cb->submitted_fence_target = target_fence_value;
if (group->last_submitted) {
group->last_submitted->next_submitted = cb;
} else {
group->first_submitted = cb;
}
group->last_submitted = cb;
}
sys_mutex_unlock(&lock);
}
/* Add command list to submitted list */
cl->submitted_fence_target = target_fence_value;
{
struct sys_lock lock = sys_mutex_lock_e(cq->mutex);
if (cq->last_submitted_command_list) {
cq->last_submitted_command_list->next_submitted = cl;
} else {
cq->first_submitted_command_list = cl;
}
cq->last_submitted_command_list = cl;
sys_mutex_unlock(&lock);
}
return target_fence_value;
}
/* ========================== *
* Command descriptor heap (GPU / shader visible descriptor heap)
* ========================== */
INTERNAL struct command_descriptor_heap *command_list_push_descriptor_heap(struct command_list *cl, struct cpu_descriptor_heap *dh_cpu)
{
ASSERT(dh_cpu->type == D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); /* Src heap must have expected type */
/* Allocate GPU heap */
struct command_descriptor_heap *cdh = NULL;
ID3D12DescriptorHeap *old_heap = NULL;
D3D12_CPU_DESCRIPTOR_HANDLE old_cpu_handle = ZI;
D3D12_GPU_DESCRIPTOR_HANDLE old_gpu_handle = ZI;
{
struct sys_lock lock = sys_mutex_lock_e(G.command_descriptor_heaps_mutex);
/* Find first heap ready for reuse */
for (struct command_descriptor_heap *tmp = G.first_submitted_command_descriptor_heap; tmp; tmp = tmp->next_submitted) {
/* TODO: Cache completed fence values */
u64 queue_fence_value = ID3D12Fence_GetCompletedValue(tmp->submitted_cq->fence);
if (queue_fence_value >= tmp->submitted_fence_target) {
cdh = tmp;
break;
}
}
if (cdh) {
/* Remove from submitted list */
old_heap = cdh->heap;
old_cpu_handle = cdh->cpu_handle;
old_gpu_handle = cdh->gp_handle;
struct command_descriptor_heap *prev = cdh->prev_submitted;
struct command_descriptor_heap *next = cdh->next_submitted;
if (prev) {
prev->next_submitted = next;
} else {
G.first_submitted_command_descriptor_heap = next;
}
if (next) {
next->prev_submitted = prev;
} else {
G.last_submitted_command_descriptor_heap = prev;
}
} else {
/* No available heap available for reuse, allocate new */
cdh = arena_push_no_zero(G.command_descriptor_heaps_arena, struct command_descriptor_heap);
}
sys_mutex_unlock(&lock);
}
MEMZERO_STRUCT(cdh);
if (old_heap) {
cdh->heap = old_heap;
cdh->cpu_handle = old_cpu_handle;
cdh->gp_handle = old_gpu_handle;
} else {
D3D12_DESCRIPTOR_HEAP_DESC desc = ZI;
desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV;
desc.NumDescriptors = DX12_NUM_CBV_SRV_UAV_DESCRIPTORS;
desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE;
HRESULT hr = ID3D12Device_CreateDescriptorHeap(G.device, &desc, &IID_ID3D12DescriptorHeap, (void **)&cdh->heap);
if (FAILED(hr)) {
sys_panic(LIT("Failed to create GPU descriptor heap"));
}
ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(cdh->heap, &cdh->cpu_handle);
ID3D12DescriptorHeap_GetGPUDescriptorHandleForHeapStart(cdh->heap, &cdh->gp_handle);
}
/* Copy CPU heap */
{
struct sys_lock lock = sys_mutex_lock_s(dh_cpu->mutex);
ID3D12Device_CopyDescriptorsSimple(G.device, dh_cpu->num_descriptors_reserved, cdh->cpu_handle, dh_cpu->handle, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
sys_mutex_unlock(&lock);
}
/* Insert into command list */
cdh->next_in_command_list = cl->first_command_descriptor_heap;
cl->first_command_descriptor_heap = cdh;
return cdh;
}
/* ========================== *
* Command buffer
* ========================== */
INTERNAL u64 command_buffer_hash_from_size(u64 size)
{
u64 hash = rand_u64_from_seed(size);
return hash;
}
INTERNAL u64 align_up_pow2(u64 v)
{
u64 res = 0;
if (v > 0) {
res = v - 1;
res |= res >> 1;
res |= res >> 2;
res |= res >> 4;
res |= res >> 8;
res |= res >> 16;
res |= res >> 32;
++res;
}
return res;
}
INTERNAL struct command_buffer *command_list_push_buffer(struct command_list *cl, struct string data)
{
/* Determine size */
u64 size = max_u64(DX12_COMMAND_BUFFER_MIN_SIZE, align_up_pow2(data.len));
/* Allocate buffer */
struct command_buffer_group *cb_group = NULL;
struct command_buffer *cb = NULL;
struct dx12_resource *resource = NULL;
{
struct sys_lock lock = sys_mutex_lock_e(G.command_buffers_mutex);
{
u64 group_hash = command_buffer_hash_from_size(size);
struct dict_entry *cb_group_entry = dict_ensure_entry(G.command_buffers_arena, G.command_buffers_dict, group_hash);
cb_group = cb_group_entry->value;
if (!cb_group) {
/* Create group */
cb_group = arena_push(G.command_buffers_arena, struct command_buffer_group);
cb_group_entry->value = (u64)cb_group;
}
}
/* Find first command buffer ready for reuse */
for (struct command_buffer *tmp = cb_group->first_submitted; tmp; tmp = tmp->next_submitted) {
/* TODO: Cache completed fence values */
u64 queue_fence_value = ID3D12Fence_GetCompletedValue(tmp->submitted_cq->fence);
if (queue_fence_value >= tmp->submitted_fence_target) {
cb = tmp;
break;
}
}
if (cb) {
/* Remove from submitted list */
resource = cb->resource;
struct command_buffer *prev = cb->prev_submitted;
struct command_buffer *next = cb->next_submitted;
if (prev) {
prev->next_submitted = next;
} else {
cb_group->first_submitted = next;
}
if (next) {
next->prev_submitted = prev;
} else {
cb_group->last_submitted = prev;
}
} else {
/* Allocate new */
cb = arena_push_no_zero(G.command_buffers_arena, struct command_buffer);
}
sys_mutex_unlock(&lock);
}
MEMZERO_STRUCT(cb);
cb->group = cb_group;
cb->size = data.len;
if (resource) {
cb->resource = resource;
} else {
enum dx12_resource_view_flags view_flags = DX12_RESOURCE_VIEW_FLAG_NONE;
D3D12_HEAP_PROPERTIES heap_props = { .Type = D3D12_HEAP_TYPE_UPLOAD };
heap_props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heap_props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
D3D12_HEAP_FLAGS heap_flags = D3D12_HEAP_FLAG_CREATE_NOT_ZEROED;
D3D12_RESOURCE_DESC desc = ZI;
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
desc.Format = DXGI_FORMAT_UNKNOWN;
desc.Alignment = 0;
desc.Width = size;
desc.Height = 1;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
D3D12_RESOURCE_STATES initial_state = D3D12_RESOURCE_STATE_GENERIC_READ;
cb->resource = dx12_resource_alloc(heap_props, heap_flags, desc, initial_state, view_flags);
}
/* Copy data to resource */
{
D3D12_RANGE read_range = ZI;
void *dst = NULL;
HRESULT hr = ID3D12Resource_Map(cb->resource->resource, 0, &read_range, &dst);
if (FAILED(hr) || !dst) {
/* TODO: Don't panic */
sys_panic(LIT("Failed to map command buffer resource"));
}
MEMCPY(dst, data.text, data.len);
ID3D12Resource_Unmap(cb->resource->resource, 0, NULL);
}
/* Insert into command list */
cb->next_in_command_list = cl->first_command_buffer;
cl->first_command_buffer = cb;
return cb;
}
/* ========================== *
* Texture
* ========================== */
struct gp_handle gp_texture_alloc(enum gp_texture_format format, u32 flags, struct v2i32 size, void *initial_data)
{
struct dxgi_format_info { DXGI_FORMAT format; u32 size; };
LOCAL_PERSIST const struct dxgi_format_info formats[] = {
[GP_TEXTURE_FORMAT_R8G8B8A8_UNORM] = { DXGI_FORMAT_R8G8B8A8_UNORM, 4 },
[GP_TEXTURE_FORMAT_R8G8B8A8_UNORM_SRGB] = { DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, 4 }
};
DXGI_FORMAT dxgi_format = ZI;
u32 pixel_size = 0;
if (format < (i32)ARRAY_COUNT(formats)) {
dxgi_format = formats[format].format;
pixel_size = formats[format].size;
ASSERT(dxgi_format != 0);
ASSERT(pixel_size != 0);
}
if (format == 0) {
sys_panic(LIT("Tried to create texture with unknown format"));
}
enum dx12_resource_view_flags view_flags = DX12_RESOURCE_VIEW_FLAG_SRV;
D3D12_HEAP_PROPERTIES heap_props = { .Type = D3D12_HEAP_TYPE_DEFAULT };
heap_props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
heap_props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
D3D12_HEAP_FLAGS heap_flags = D3D12_HEAP_FLAG_CREATE_NOT_ZEROED;
D3D12_RESOURCE_DESC desc = ZI;
desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
desc.Format = dxgi_format;
desc.Alignment = 0;
desc.Width = size.x;
desc.Height = size.y;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
if (flags & GP_TEXTURE_FLAG_TARGETABLE) {
desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
view_flags |= DX12_RESOURCE_VIEW_FLAG_RTV;
}
D3D12_RESOURCE_STATES initial_state = D3D12_RESOURCE_STATE_COPY_DEST;
struct dx12_resource *r = dx12_resource_alloc(heap_props, heap_flags, desc, initial_state, view_flags);
r->texture_size = size;
/* Upload texture */
if (initial_data) {
u64 upload_size = 0;
u64 upload_row_size = 0;
u32 upload_num_rows = 0;
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint = ZI;
ID3D12Device_GetCopyableFootprints(G.device, &desc, 0, 1, 0, &footprint, &upload_num_rows, &upload_row_size, &upload_size);
/* Create temp upload heap */
struct dx12_resource *upload = NULL;
{
enum dx12_resource_view_flags upload_view_flags = DX12_RESOURCE_VIEW_FLAG_NONE;
D3D12_HEAP_PROPERTIES upload_heap_props = { .Type = D3D12_HEAP_TYPE_UPLOAD };
upload_heap_props.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
upload_heap_props.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
D3D12_HEAP_FLAGS upload_heap_flags = D3D12_HEAP_FLAG_CREATE_NOT_ZEROED;
D3D12_RESOURCE_DESC upload_desc = ZI;
upload_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
upload_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
upload_desc.Format = DXGI_FORMAT_UNKNOWN;
upload_desc.Alignment = 0;
upload_desc.Width = upload_size;
upload_desc.Height = 1;
upload_desc.DepthOrArraySize = 1;
upload_desc.MipLevels = 1;
upload_desc.SampleDesc.Count = 1;
upload_desc.SampleDesc.Quality = 0;
D3D12_RESOURCE_STATES upload_initial_state = D3D12_RESOURCE_STATE_GENERIC_READ;
/* FIXME: Release */
upload = dx12_resource_alloc(upload_heap_props, upload_heap_flags, upload_desc, upload_initial_state, upload_view_flags);
/* Copy to upload heap */
#if 0
/* FIXME: Copy based on footprint */
{
D3D12_RANGE read_range = ZI;
void *dst = NULL;
HRESULT hr = ID3D12Resource_Map(upload->resource, 0, &read_range, &dst);
if (FAILED(hr) || !dst) {
/* TODO: Don't panic */
sys_panic(LIT("Failed to map texture upload resource"));
}
MEMCPY(dst, initial_data, size.x * size.y * pixel_size);
ID3D12Resource_Unmap(upload->resource, 0, NULL);
}
#else
/* FIXME: Copy based on footprint */
{
D3D12_RANGE read_range = ZI;
void *mapped = NULL;
HRESULT hr = ID3D12Resource_Map(upload->resource, 0, &read_range, &mapped);
if (FAILED(hr) || !mapped) {
/* TODO: Don't panic */
sys_panic(LIT("Failed to map texture upload resource"));
}
u8 *dst = (u8 *)mapped + footprint.Offset;
u8 *src = initial_data;
for (u32 y = 0; y < upload_num_rows; ++y) {
memcpy(dst + y * footprint.Footprint.RowPitch, src + y * size.x * pixel_size, size.x * pixel_size);
}
ID3D12Resource_Unmap(upload->resource, 0, NULL);
}
#endif
}
/* Copy from upload heap to texture */
struct command_queue *cq = G.cq_copy_background;
struct command_list *cl = command_list_open(cq);
{
D3D12_TEXTURE_COPY_LOCATION dst_loc = {
.pResource = r->resource,
.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX,
.SubresourceIndex = 0,
};
D3D12_TEXTURE_COPY_LOCATION src_loc = {
.pResource = upload->resource,
.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT,
.PlacedFootprint = footprint,
};
ID3D12GraphicsCommandList_CopyTextureRegion(cl->cl, &dst_loc, 0, 0, 0, &src_loc, NULL);
/* FIXME: Better barrier? */
//dx12_resource_barrier(cl->cl, r, D3D12_RESOURCE_STATE_ALL_SHADER_RESOURCE);
}
u64 fence_target = command_list_close(cl);
/* Wait */
/* TODO: Return async waitable to caller */
HANDLE event = CreateEvent(NULL, FALSE, FALSE, NULL);
ID3D12Fence_SetEventOnCompletion(cq->fence, fence_target, event);
WaitForSingleObject(event, INFINITE);
CloseHandle(event);
}
return handle_alloc(DX12_HANDLE_KIND_RESOURCE, r);
}
void gp_texture_clear(struct gp_handle target_resource, u32 clear_color)
{
(UNUSED)target_resource;
(UNUSED)clear_color;
}
struct v2i32 gp_texture_get_size(struct gp_handle resource)
{
struct v2i32 res = ZI;
struct dx12_resource *dx12_resource = handle_get_data(resource, DX12_HANDLE_KIND_RESOURCE);
if (dx12_resource) {
res = dx12_resource->texture_size;
}
return res;
}
/* ========================== *
* Dispatch
* ========================== */
/* Calculate the view projection matrix */
INLINE struct mat4x4 calculate_vp(struct xform view, f32 viewport_width, f32 viewport_height)
{
struct mat4x4 projection = mat4x4_from_ortho(0.0, viewport_width, viewport_height, 0.0, -1.0, 1.0);
struct mat4x4 view4x4 = mat4x4_from_xform(view);
return mat4x4_mul(projection, view4x4);
}
void gp_dispatch(struct gp_dispatch_params params)
{
__prof;
struct flow *flow = handle_get_data(params.flow, DX12_HANDLE_KIND_FLOW);
/* Material pass */
struct command_list *cl = command_list_open(G.cq_direct);
{
struct dx12_resource *target = handle_get_data(params.draw_target, DX12_HANDLE_KIND_RESOURCE);
/* Upload dummmy vert & index buffer */
/* TODO: Make these static */
/* Dummy vertex buffer */
LOCAL_PERSIST u16 quad_indices[6] = { 0, 1, 2, 0, 2, 3 };
struct command_buffer *dummy_vertex_buffer = command_list_push_buffer(cl, STRING(0, 0));
struct command_buffer *quad_index_buffer = command_list_push_buffer(cl, STRING_FROM_ARRAY(quad_indices));
D3D12_VERTEX_BUFFER_VIEW dummy_vertex_buffer_view = ZI;
dummy_vertex_buffer_view.BufferLocation = dummy_vertex_buffer->resource->gpu_address;
D3D12_INDEX_BUFFER_VIEW quad_index_buffer_view = ZI;
quad_index_buffer_view.BufferLocation = quad_index_buffer->resource->gpu_address;
quad_index_buffer_view.Format = DXGI_FORMAT_R16_UINT;
quad_index_buffer_view.SizeInBytes = sizeof(quad_indices);
/* Upload instance buffer */
struct command_buffer *instance_buffer = command_list_push_buffer(cl, STRING_FROM_ARENA(flow->material_instances_arena));
/* Upload descriptor heap */
struct command_descriptor_heap *descriptor_heap = command_list_push_descriptor_heap(cl, G.cbv_srv_uav_heap);
/* Viewport */
struct rect viewport = params.draw_target_viewport;
struct D3D12_VIEWPORT d3d12_viewport = ZI;
d3d12_viewport.TopLeftX = viewport.x;
d3d12_viewport.TopLeftY = viewport.y;
d3d12_viewport.Width = viewport.width;
d3d12_viewport.Height = viewport.height;
d3d12_viewport.MinDepth = 0.0f;
d3d12_viewport.MaxDepth = 1.0f;
/* Scissor */
D3D12_RECT d3d12_scissor = ZI;
d3d12_scissor.left = viewport.x;
d3d12_scissor.top = viewport.y;
d3d12_scissor.right = viewport.x + viewport.width;
d3d12_scissor.bottom = viewport.y + viewport.height;
struct mat4x4 vp_matrix = calculate_vp(params.draw_target_view, viewport.width, viewport.height);
/* Create temporary descriptor heap */
/* NOTE: This should always occur after buffers are submitted */
/* Transition render target */
enum D3D12_RESOURCE_STATES target_old_state = dx12_resource_barrier(cl->cl, target, D3D12_RESOURCE_STATE_RENDER_TARGET);
{
ID3D12GraphicsCommandList_OMSetRenderTargets(cl->cl, 1, &target->rtv_descriptor->handle, false, NULL);
if (params.clear_target) {
f32 clear_color[] = { 0.0f, 0.0f, 0.0f, 0.0f };
ID3D12GraphicsCommandList_ClearRenderTargetView(cl->cl, target->rtv_descriptor->handle, clear_color, 0, NULL);
}
}
/* Material pass */
{
//struct pipeline *pipeline = dx12_get_pipeline(pipeline_scope, LIT("material"));
struct pipeline *pipeline = &G.test_pipeline;
u32 instance_count = instance_buffer->size / sizeof(struct sh_material_instance);
/* Bind pipeline */
ID3D12GraphicsCommandList_SetPipelineState(cl->cl, pipeline->pso);
ID3D12GraphicsCommandList_SetGraphicsRootSignature(cl->cl, pipeline->rootsig);
/* Fill & bind constant buffer */
/* TODO: Move into root constant */
struct sh_material_constants constants = ZI;
constants.projection = sh_float4x4_from_mat4x4(vp_matrix);
struct command_buffer *constant_buffer = command_list_push_buffer(cl, STRING_FROM_STRUCT(&constants));
ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(cl->cl, 0, constant_buffer->resource->gpu_address);
/* Bind instance buffer */
ID3D12GraphicsCommandList_SetGraphicsRootShaderResourceView(cl->cl, 1, instance_buffer->resource->gpu_address);
/* Bind descriptor heap */
ID3D12DescriptorHeap *heaps[] = { descriptor_heap->heap };
ID3D12GraphicsCommandList_SetDescriptorHeaps(cl->cl, ARRAY_COUNT(heaps), heaps);
ID3D12GraphicsCommandList_SetGraphicsRootDescriptorTable(cl->cl, 2, descriptor_heap->gp_handle);
/* Setup Rasterizer State */
ID3D12GraphicsCommandList_RSSetViewports(cl->cl, 1, &d3d12_viewport);
ID3D12GraphicsCommandList_RSSetScissorRects(cl->cl, 1, &d3d12_scissor);
/* Draw */
ID3D12GraphicsCommandList_IASetPrimitiveTopology(cl->cl, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
ID3D12GraphicsCommandList_IASetVertexBuffers(cl->cl, 0, 1, &dummy_vertex_buffer_view);
ID3D12GraphicsCommandList_IASetIndexBuffer(cl->cl, &quad_index_buffer_view);
ID3D12GraphicsCommandList_DrawIndexedInstanced(cl->cl, 6, instance_count, 0, 0, 0);
}
/* Reset render target */
{
dx12_resource_barrier(cl->cl, target, target_old_state);
}
}
command_list_close(cl);
/* Reset flow */
sprite_scope_end(flow->sprite_scope);
flow->sprite_scope = sprite_scope_begin();
arena_reset(flow->material_instances_arena);
}
/* ========================== *
* Swapchain
* ========================== */
/* ========================== *
* Present
* ========================== */
#if GSTAT_ENABLED || PROFILING
INTERNAL void query_memory_info(void)
{
HRESULT hr = 0;
IDXGIAdapter3 *dxgiAdapter3 = NULL;
struct DXGI_QUERY_VIDEO_MEMORY_INFO info = ZI;
if (SUCCEEDED(hr)) {
hr = IDXGIAdapter_QueryInterface(G.adapter, &IID_IDXGIAdapter3, (void **)&dxgiAdapter3);
ASSERT(SUCCEEDED(hr));
}
if (SUCCEEDED(hr)) {
IDXGIAdapter3_QueryVideoMemoryInfo(dxgiAdapter3, 0, DXGI_MEMORY_SEGMENT_GROUP_LOCAL, &info);
{
u64 vram = info.CurrentUsage;
u64 budget = info.Budget;
(UNUSED)vram;
(UNUSED)budget;
#if GSTAT_ENABLED
{
gstat_set(GSTAT_VRAM_USAGE, vram);
gstat_set(GSTAT_VRAM_BUDGET, budget);
}
#endif
#if PROFILING
{
LOCAL_PERSIST char *vram_plot_name = NULL;
LOCAL_PERSIST u64 prev_vram = 0;
if (!vram_plot_name) {
vram_plot_name = "Video memory usage";
__prof_plot_init(vram_plot_name, __prof_plot_type_memory, 1, 1, 0);
}
if (vram != prev_vram) {
__prof_plot_i(vram_plot_name, vram);
}
prev_vram = vram;
}
#endif
}
}
if (dxgiAdapter3) {
IDXGIAdapter_Release(dxgiAdapter3);
}
}
#else
INTERNAL void query_memory_info(void)
{
}
#endif
INTERNAL struct dx12_resource *update_swapchain(struct sys_window *window, struct v2i32 resolution)
{
__prof;
HWND hwnd = (HWND)sys_window_get_internal_handle(window);
b32 should_rebuild = !v2i32_eq(G.swapchain_resolution, resolution);
if (should_rebuild) {
HRESULT hr = 0;
if (G.swapchain) {
ASSERT(hwnd == G.swapchain_hwnd);
/* Resize existing swapchain */
/* FIXME: Fence */
/* Release resources */
for (u32 i = 0; i < ARRAY_COUNT(G.swapchain_resources); ++i) {
struct dx12_resource *resource = G.swapchain_resources[i];
dx12_resource_release_now(resource);
}
/* Resize buffers */
IDXGISwapChain_ResizeBuffers(G.swapchain, 0, resolution.x, resolution.y, DXGI_FORMAT_UNKNOWN, DX12_SWAPCHAIN_FLAGS);
} else {
/* Create swapchain1 */
IDXGISwapChain1 *swapchain1 = NULL;
{
DXGI_SWAP_CHAIN_DESC1 desc = ZI;
desc.Format = DX12_SWAPCHAIN_FORMAT;
desc.SampleDesc.Count = 1;
desc.SampleDesc.Quality = 0;
desc.BufferUsage = DXGI_USAGE_SHADER_INPUT | DXGI_USAGE_RENDER_TARGET_OUTPUT;
desc.BufferCount = DX12_SWAPCHAIN_BUFFER_COUNT;
desc.Scaling = DXGI_SCALING_NONE;
desc.Flags = DX12_SWAPCHAIN_FLAGS;
desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
hr = IDXGIFactory2_CreateSwapChainForHwnd(G.factory, (IUnknown *)G.cq_direct->cq, 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 **)&G.swapchain);
if (FAILED(hr)) {
dx12_init_error(LIT("Failed to create IDXGISwapChain3"));
}
/* Disable Alt+Enter changing monitor resolution to match window size */
IDXGIFactory_MakeWindowAssociation(G.factory, hwnd, DXGI_MWA_NO_ALT_ENTER);
IDXGISwapChain1_Release(swapchain1);
G.swapchain_hwnd = hwnd;
}
/* Allocate swapchain resources */
for (u32 i = 0; i < ARRAY_COUNT(G.swapchain_resources); ++i) {
ID3D12Resource *resource = NULL;
hr = IDXGISwapChain3_GetBuffer(G.swapchain, i, &IID_ID3D12Resource, (void **)&resource);
if (FAILED(hr)) {
/* TODO: Don't panic */
dx12_init_error(LIT("Failed to get swapchain buffer"));
}
G.swapchain_resources[i] = dx12_resource_alloc_from_swapchain_buffer(resource, resolution);
}
G.swapchain_resolution = resolution;
}
G.swapchain_frame_index = IDXGISwapChain3_GetCurrentBackBufferIndex(G.swapchain);
return G.swapchain_resources[G.swapchain_frame_index];
}
/* FIXME: Remove this */
#include "draw.h"
INTERNAL void present_blit(struct dx12_resource *dst, struct dx12_resource *src, struct xform src_xf)
{
/* FIXME: Remove this */
static struct gp_handle flow = ZI;
if (!flow.gen) {
flow = gp_flow_alloc();
}
struct gp_handle dst_texture_handle = handle_alloc(DX12_HANDLE_KIND_RESOURCE, dst);
struct gp_handle src_texture_handle = handle_alloc(DX12_HANDLE_KIND_RESOURCE, src);
/* Draw texture to backbuffer texture */
/* TODO: Specialized blit shader */
{
struct draw_texture_params params = DRAW_TEXTURE_PARAMS(.xf = src_xf, .texture = src_texture_handle);
draw_texture(flow, params);
}
/* FIXME: Clear backbuffer */
/* Render to backbuffer texture */
struct gp_dispatch_params params = ZI;
params.flow = flow;
params.draw_target = dst_texture_handle;
params.draw_target_viewport = RECT_FROM_V2(V2(0, 0), V2(dst->texture_size.x, dst->texture_size.y));
params.draw_target_view = XFORM_IDENT;
params.clear_target = true;
gp_dispatch(params);
}
void gp_present(struct sys_window *window, struct v2i32 backbuffer_resolution, struct gp_handle texture, struct xform texture_xf, i32 vsync)
{
query_memory_info();
//sys_sleep(0.1);
struct dx12_resource *backbuffer_resource = update_swapchain(window, backbuffer_resolution);
struct dx12_resource *texture_resource = handle_get_data(texture, DX12_HANDLE_KIND_RESOURCE);
/* Blit */
present_blit(backbuffer_resource, texture_resource, texture_xf);
//sys_sleep(0.1);
/* Present */
HRESULT hr = IDXGISwapChain3_Present(G.swapchain, 0, 0);
if (!SUCCEEDED(hr)) {
ASSERT(false);
}
(UNUSED)backbuffer_resolution;
(UNUSED)texture;
(UNUSED)texture_xf;
(UNUSED)vsync;
}
#endif