2281 lines
77 KiB
C
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
|