power_play/src/gpu_dx11.c

1514 lines
51 KiB
C

#include "gpu.h"
#include "resource.h"
#include "sys.h"
#include "memory.h"
#include "arena.h"
#include "scratch.h"
#include "string.h"
#include "math.h"
#include "inc.h"
#include "sprite.h"
#include "log.h"
#include "gstat.h"
#pragma warning(push, 0)
# define UNICODE
# define COBJMACROS
# include <Windows.h>
# include <d3d11.h>
# include <d3dcompiler.h>
# include <dxgidebug.h>
# include <dxgi1_4.h>
#pragma warning(pop)
#pragma comment(lib, "d3d11")
#pragma comment(lib, "dxgi")
#pragma comment(lib, "dxguid")
#pragma comment(lib, "d3dcompiler")
#define MAX_CMD_BUFFERS 1024
#define SHADER_INFO_LOOKUP_BINS 64
/* FIXME: Enable this and resolve unreleased references */
#if RTC
# define DX11_DEBUG 0
# define DX11_SHADER_DEBUG 1
#else
# define DX11_DEBUG 0
# define DX11_SHADER_DEBUG 0
#endif
#define DX11_SWAPCHAIN_FLAGS (DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING)
#define DX11_SWAPCHAIN_FORMAT (DXGI_FORMAT_R8G8B8A8_UNORM)
#define DX11_SWAPCHAIN_RTV_FORMAT (DXGI_FORMAT_R8G8B8A8_UNORM_SRGB)
struct dx11_shader {
enum shader_kind kind;
b32 valid; /* Is this shader allocated */
u32 vertex_size;
ID3D11InputLayout *input_layout;
ID3D11VertexShader *vs;
ID3D11PixelShader *ps;
};
struct dx11_constant_buffer_data {
struct mat4x4 vp;
};
struct dx11_buffer {
u32 vertex_count;
u32 index_count;
u8 *cpu_vertex_buffer; /* Array of homogeneous vertices (size depends on shader) */
vidx *cpu_index_buffer; /* Array of vertex indices into cpu_vertex_buffer */
struct arena vertex_arena;
struct arena index_arena;
u32 gpu_vertex_buffer_capacity;
u32 gpu_index_buffer_capacity;
ID3D11Buffer *gpu_vertex_buffer;
ID3D11Buffer *gpu_index_buffer;
};
struct gpu_cmd {
struct dx11_shader *shader;
struct gpu_texture texture; /* Overrides sprite if set */
struct sprite_tag sprite;
/* Associated buffer data */
u32 vertex_count;
u32 index_count;
u32 vertex_offset;
u32 index_offset;
b32 offsets_set;
struct gpu_cmd *next;
};
struct cmd_store {
struct gpu_cmd *cmd_first;
struct gpu_cmd *cmd_last;
struct arena arena;
};
struct gpu_cmd_buffer {
struct dx11_buffer buffers[NUM_SHADERS];
struct cmd_store cpu_cmd_store;
struct cmd_store gpu_cmd_store;
b32 valid; /* False if uninitialized (in sparse array) */
};
struct dx11_format {
DXGI_FORMAT format;
u32 pixel_size;
};
struct dx11_texture {
ID3D11Texture2D *texture;
b32 is_backbuffer;
struct dx11_texture *prev;
struct dx11_texture *next;
struct dx11_texture *next_free;
};
INTERNAL void gpu_capture_image_for_profiler(void);
/* ========================== *
* Global state
* ========================== */
struct handle_slot {
u64 idx;
u64 gen;
void *data;
struct handle_slot *next_free;
};
struct handle_store {
struct sys_mutex mutex;
struct arena arena;
struct handle_slot *head_free;
struct handle_slot *array;
u64 count;
};
struct dx11_shader_desc {
enum shader_kind kind;
char *name_cstr;
u32 vertex_size;
D3D11_INPUT_ELEMENT_DESC input_layout_desc[64]; /* NULL terminated array */
/* Internal */
#if RESOURCE_RELOADING
struct arena includes_arena;
struct sys_mutex includes_mutex;
struct dict includes_dict;
struct atomic_i32 is_dirty;
#endif
};
GLOBAL struct {
struct arena arena;
#if PROFILING
struct __prof_dx11_ctx *profiling_ctx;
#endif
ID3D11Device *dev;
ID3D11DeviceContext *devcon;
IDXGISwapChain1 *swapchain;
struct dx11_texture backbuffer_texture;
ID3D11BlendState *blend_state;
ID3D11RasterizerState *rasterizer_state;
ID3D11DepthStencilState *depth_stencil_state;
ID3D11SamplerState *sampler_state;
ID3D11Buffer *vs_constant_buffer;
/* Texture store */
struct sys_mutex textures_mutex;
struct arena textures_arena;
struct dx11_texture *textures_first;
struct dx11_texture *textures_last;
struct dx11_texture *textures_first_free;
/* Sparse array (cmdbuff.valid) */
struct gpu_cmd_buffer cmdbuffs[MAX_CMD_BUFFERS];
struct dx11_shader shaders[NUM_SHADERS];
struct dx11_shader_desc shader_info[NUM_SHADERS];
} G = ZI, DEBUG_ALIAS(G, G_gpu_dx11);
/* ========================== *
* Util
* ========================== */
/* 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);
}
INTERNAL void send_constant_buffer_data(ID3D11Buffer *buffer, struct mat4x4 vp)
{
D3D11_MAPPED_SUBRESOURCE ms;
if (ID3D11DeviceContext_Map(G.devcon, (ID3D11Resource *)buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &ms) != S_OK) {
ASSERT(false);
return;
}
struct dx11_constant_buffer_data *data = (struct dx11_constant_buffer_data *)ms.pData;
MEMCPY(&data->vp, &vp, sizeof(vp));
ID3D11DeviceContext_Unmap(G.devcon, (ID3D11Resource *)buffer, 0);
}
/* ========================== *
* Startup
* ========================== */
INTERNAL void init_shader_table(void);
INTERNAL void reload_shader(struct dx11_shader *shader, struct dx11_shader_desc *desc);
#if RESOURCE_RELOADING
INTERNAL RESOURCE_WATCH_CALLBACK_FUNC_DEF(shader_resource_watch_callback, name);
#endif
struct gpu_startup_receipt gpu_startup(struct sys_window *window)
{
__prof;
G.arena = arena_alloc(GIGABYTE(64));
/* Initialize texture store */
G.textures_mutex = sys_mutex_alloc();
G.textures_arena = arena_alloc(GIGABYTE(64));
/* Initialize shader table */
init_shader_table();
HRESULT hr;
ID3D11Device *device;
ID3D11DeviceContext *context;
IDXGISwapChain1 *swapchain;
/* Create D3D11 device & context */
{
#if DX11_DEBUG
u32 flags = D3D11_CREATE_DEVICE_DEBUG;
#else
u32 flags = 0;
#endif
D3D_FEATURE_LEVEL levels[] = { D3D_FEATURE_LEVEL_11_0 };
hr = D3D11CreateDevice(
NULL,
D3D_DRIVER_TYPE_HARDWARE,
NULL,
flags,
levels,
ARRAY_COUNT(levels),
D3D11_SDK_VERSION,
&device,
NULL,
&context
);
ASSERT(SUCCEEDED(hr));
}
#if DX11_DEBUG
/* D3D11 Debug break */
{
ID3D11InfoQueue *info;
ID3D11Device_QueryInterface(device, &IID_ID3D11InfoQueue, (void **)&info);
ID3D11InfoQueue_SetBreakOnSeverity(info, D3D11_MESSAGE_SEVERITY_CORRUPTION, TRUE);
ID3D11InfoQueue_SetBreakOnSeverity(info, D3D11_MESSAGE_SEVERITY_ERROR, TRUE);
ID3D11InfoQueue_Release(info);
}
/* DXGI Debug break */
{
IDXGIInfoQueue *dxgiInfo;
hr = DXGIGetDebugInterface1(0, &IID_IDXGIInfoQueue, (void **)&dxgiInfo);
ASSERT(SUCCEEDED(hr));
IDXGIInfoQueue_SetBreakOnSeverity(dxgiInfo, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, TRUE);
IDXGIInfoQueue_SetBreakOnSeverity(dxgiInfo, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, TRUE);
IDXGIInfoQueue_Release(dxgiInfo);
}
#endif
/* Create swap chain */
{
HWND hwnd = (HWND)sys_window_get_internal_handle(window);
/* Get DXGI device from D3D11 device */
IDXGIDevice *dxgiDevice;
hr = ID3D11Device_QueryInterface(device, &IID_IDXGIDevice, (void **)&dxgiDevice);
ASSERT(SUCCEEDED(hr));
/* Get DXGI adapter from DXGI device */
IDXGIAdapter *dxgiAdapter;
hr = IDXGIDevice_GetAdapter(dxgiDevice, &dxgiAdapter);
ASSERT(SUCCEEDED(hr));
/* Get DXGI factory from DXGI adapter */
IDXGIFactory2 *factory;
hr = IDXGIAdapter_GetParent(dxgiAdapter, &IID_IDXGIFactory2, (void **)&factory);
ASSERT(SUCCEEDED(hr));
DXGI_SWAP_CHAIN_DESC1 desc = {
.Format = DX11_SWAPCHAIN_FORMAT,
.SampleDesc = { 1, 0 },
.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT,
.BufferCount = 2,
.Scaling = DXGI_SCALING_NONE,
.Flags = DX11_SWAPCHAIN_FLAGS,
.AlphaMode = DXGI_ALPHA_MODE_IGNORE,
.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD,
};
hr = IDXGIFactory2_CreateSwapChainForHwnd(factory, (IUnknown *)device, hwnd, &desc, NULL, NULL, &swapchain);
ASSERT(SUCCEEDED(hr));
/* Disable Alt+Enter changing monitor resolution to match window size */
IDXGIFactory_MakeWindowAssociation(factory, hwnd, DXGI_MWA_NO_ALT_ENTER);
IDXGIFactory2_Release(factory);
IDXGIAdapter_Release(dxgiAdapter);
IDXGIDevice_Release(dxgiDevice);
}
if (!SUCCEEDED(hr) || !device || !context || !swapchain) {
/* Gpu initialization failure */
/* TODO: Better message */
sys_panic(LIT("Failed to initialize DirectX 11"));
}
G.dev = device;
G.devcon = context;
G.swapchain = swapchain;
G.backbuffer_texture.is_backbuffer = true;
struct string prof_ctx_name = LIT("D3d11 Context");
(UNUSED)prof_ctx_name;
__prof_dx11_ctx_alloc(G.profiling_ctx, G.dev, G.devcon, prof_ctx_name.text, prof_ctx_name.len);
/* Create the blending setup */
{
__profscope(create_blend_state);
const f32 blend_factor[4] = { 0.f, 0.f, 0.f, 0.f };
/* TODO: Actually go over these (just want alpha blending/transparency) */
D3D11_BLEND_DESC desc = {
.AlphaToCoverageEnable = false,
.RenderTarget[0].BlendEnable = true,
.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA,
.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA,
.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD,
.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE,
.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA,
.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD,
.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL
};
/* FIXME: Free this? */
ID3D11Device_CreateBlendState(G.dev, &desc, &G.blend_state);
ID3D11DeviceContext_OMSetBlendState(G.devcon, G.blend_state, blend_factor, 0xffffffff);
}
/* Create depth-stencil State */
{
__profscope(create_depth_stencil_state);
/* TODO: Actually go over these (copied from elsewhere) */
D3D11_DEPTH_STENCIL_DESC desc = ZI;
desc.DepthEnable = false;
desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL;
desc.DepthFunc = D3D11_COMPARISON_ALWAYS;
desc.StencilEnable = false;
desc.FrontFace.StencilFailOp = desc.FrontFace.StencilDepthFailOp = desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP;
desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS;
desc.BackFace = desc.FrontFace;
/* FIXME: Free this? */
ID3D11Device_CreateDepthStencilState(G.dev, &desc, &G.depth_stencil_state);
ID3D11DeviceContext_OMSetDepthStencilState(G.devcon, G.depth_stencil_state, 0);
}
/* Create the rasterizer state */
{
__profscope(create_rasterizer_state);
D3D11_RASTERIZER_DESC desc = {
.FillMode = D3D11_FILL_SOLID,
.CullMode = D3D11_CULL_NONE,
//.ScissorEnable = true,
.DepthClipEnable = true
};
/* FIXME: Free this? */
ID3D11Device_CreateRasterizerState(G.dev, &desc, &G.rasterizer_state);
ID3D11DeviceContext_RSSetState(G.devcon, G.rasterizer_state);
}
/* Create the sampler state */
{
__profscope(create_sampler_state);
D3D11_SAMPLER_DESC desc = {
//.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR,
.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT,
.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP,
.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP,
.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP,
.MaxAnisotropy = 1,
//.ComparisonFunc = D3D11_COMPARISON_ALWAYS,
.MaxLOD = D3D11_FLOAT32_MAX
};
/* FIXME: Free this? */
ID3D11Device_CreateSamplerState(G.dev, &desc, &G.sampler_state);
ID3D11DeviceContext_PSSetSamplers(G.devcon, 0, 1, &G.sampler_state);
}
/* Create the constant buffer */
{
__profscope(create_const_buffer);
D3D11_BUFFER_DESC desc = {
.ByteWidth = sizeof(struct dx11_constant_buffer_data),
.Usage = D3D11_USAGE_DYNAMIC,
.BindFlags = D3D11_BIND_CONSTANT_BUFFER,
.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE,
.MiscFlags = 0
};
ID3D11Device_CreateBuffer(G.dev, &desc, NULL, &G.vs_constant_buffer);
/* Apparently ByteWidth needs to be in multiples of 16? */
ASSERT(desc.ByteWidth % 16 == 0);
}
/* Init shaders */
logf_info("Compiling shaders");
for (u32 i = SHADER_NONE + 1; i < NUM_SHADERS; ++i) {
struct dx11_shader *shader = &G.shaders[i];
struct dx11_shader_desc *desc = &G.shader_info[i];
reload_shader(shader, desc);
}
logf_info("Finished compiling shaders");
/* Setup file change callbacks */
#if RESOURCE_RELOADING
resource_register_watch_callback(shader_resource_watch_callback);
#endif
return (struct gpu_startup_receipt) { 0 };
}
/* ========================== *
* Shader table
* ========================== */
INTERNAL void init_shader_table(void)
{
MEMZERO_ARRAY(G.shader_info);
/* Triangle shader layout */
G.shader_info[SHADER_TRIANGLE] = (struct dx11_shader_desc) {
.kind = SHADER_TRIANGLE,
.name_cstr = "shaders/triangle.hlsl",
.vertex_size = sizeof(struct triangle_shader_vertex),
.input_layout_desc = {
{ "pos", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "uv", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "tint_srgb", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }
}
};
/* Grid shader layout */
G.shader_info[SHADER_GRID] = (struct dx11_shader_desc) {
.kind = SHADER_GRID,
.name_cstr = "shaders/grid.hlsl",
.vertex_size = sizeof(struct grid_shader_vertex),
.input_layout_desc = {
{ "pos", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "line_thickness", 0, DXGI_FORMAT_R32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "line_spacing", 0, DXGI_FORMAT_R32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "offset", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "bg0_srgb", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "bg1_srgb", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "line_srgb", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "x_srgb", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "y_srgb", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }
}
};
#if RESOURCE_RELOADING
for (u64 i = 0; i < ARRAY_COUNT(G.shader_info); ++i) {
struct dx11_shader_desc *desc = &G.shader_info[i];
desc->includes_arena = arena_alloc(MEGABYTE(8));
desc->includes_mutex = sys_mutex_alloc();
desc->includes_dict = dict_init(&desc->includes_arena, 64);
}
#endif
}
/* ========================== *
* Shader dirty check
* ========================== */
#if RESOURCE_RELOADING
INTERNAL void shader_add_include(struct dx11_shader_desc *desc, struct string include_name_src)
{
__prof;
u64 hash = hash_fnv64(HASH_FNV64_BASIS, include_name_src);
struct dict *dict = &desc->includes_dict;
struct sys_lock lock = sys_mutex_lock_e(&desc->includes_mutex);
{
dict_set(&desc->includes_arena, dict, hash, 1);
}
sys_mutex_unlock(&lock);
}
INTERNAL void shader_reset_includes(struct dx11_shader_desc *desc)
{
__prof;
struct dict *dict = &desc->includes_dict;
struct sys_lock lock = sys_mutex_lock_e(&desc->includes_mutex);
{
dict_reset(dict);
}
sys_mutex_unlock(&lock);
}
INTERNAL b32 shader_set_dirty(struct string name)
{
__prof;
b32 caused_dirty = false;
for (u64 i = 0; i < NUM_SHADERS; ++i) {
struct dx11_shader_desc *desc = &G.shader_info[i];
struct string desc_name = string_from_cstr_no_limit(desc->name_cstr);
if (string_eq(desc_name, name)) {
atomic_i32_eval_exchange(&desc->is_dirty, 1);
caused_dirty = true;
} else {
struct dict *includes_dict = &desc->includes_dict;
u64 hash = hash_fnv64(HASH_FNV64_BASIS, name);
struct sys_lock lock = sys_mutex_lock_e(&desc->includes_mutex);
{
if (dict_get(includes_dict, hash) != 0) {
atomic_i32_eval_exchange(&desc->is_dirty, 1);
caused_dirty = true;
}
}
sys_mutex_unlock(&lock);
}
}
return caused_dirty;
}
INTERNAL b32 shader_unset_dirty(struct dx11_shader_desc *desc)
{
return atomic_i32_eval_compare_exchange(&desc->is_dirty, 1, 0) == 1;
}
#endif
/* ========================== *
* Shader include handler
* ========================== */
struct dx11_include_handler {
ID3DInclude d3d_handler;
ID3DIncludeVtbl vtbl;
struct dx11_shader *shader;
struct string error;
b32 has_open_resource;
struct resource res;
};
INTERNAL HRESULT dx11_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 dx11_include_handler *handler = (struct dx11_include_handler *)d3d_handler;
struct string name = string_from_cstr_no_limit((char *)name_cstr);
if (handler->has_open_resource) {
sys_panic(LIT("D3d include handler somehow already has a resource open"));
}
struct resource res = resource_open(name);
if (resource_exists(&res)) {
handler->res = res;
handler->has_open_resource = true;
struct string data = resource_get_data(&res);
*data_out = data.text;
*data_len_out = data.len;
result = S_OK;
}
#if RESOURCE_RELOADING
shader_add_include(&G.shader_info[handler->shader->kind], name);
#endif
return result;
}
INTERNAL HRESULT dx11_include_close(ID3DInclude *d3d_handler, LPCVOID data)
{
__prof;
(UNUSED)data;
struct dx11_include_handler *handler = (struct dx11_include_handler *)d3d_handler;
if (handler->has_open_resource) {
resource_close(&handler->res);
handler->has_open_resource = false;
}
return S_OK;
}
INTERNAL struct dx11_include_handler dx11_include_handler_alloc(struct dx11_shader *shader)
{
struct dx11_include_handler handler = ZI;
handler.d3d_handler.lpVtbl = &handler.vtbl;
handler.vtbl.Open = dx11_include_open;
handler.vtbl.Close = dx11_include_close;
handler.shader = shader;
return handler;
}
INTERNAL void dx11_include_handler_release(struct dx11_include_handler *handler)
{
if (handler->has_open_resource) {
ASSERT(false); /* Resource should have been closed by handler by now */
resource_close(&handler->res);
}
}
/* ========================== *
* Shader compilation
* ========================== */
/* TODO: Multithread shader compilation */
/* If shader compilation fails, then error string is returned allocated on `arena` */
INTERNAL struct string shader_alloc(struct arena *arena, struct dx11_shader *shader, struct dx11_shader_desc *shader_desc, struct resource *src_res)
{
__prof;
struct temp_arena scratch = scratch_begin(arena);
struct string error_str = ZI;
struct string shader_name = string_from_cstr_no_limit(shader_desc->name_cstr);
shader->kind = shader_desc->kind;
shader->vertex_size = shader_desc->vertex_size;
#if RESOURCE_RELOADING
shader_reset_includes(shader_desc);
#endif
struct dx11_include_handler include_handler = dx11_include_handler_alloc(shader);
u32 flags = D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR;
#if DX11_SHADER_DEBUG
flags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_WARNINGS_ARE_ERRORS;
#else
flags |= D3DCOMPILE_OPTIMIZATION_LEVEL3;
#endif
/* Compile shader */
ID3DBlob *vs_blob = NULL;
ID3DBlob *ps_blob = NULL;
ID3DBlob *error_blob = NULL;
b32 success = false;
{
struct string shader_src = resource_get_data(src_res);
logf_info("Compiling shader \"%F\"", FMT_STR(shader_name));
/* Compile shader */
/* TODO: pre-compile shaders w/ FXC? */
HRESULT hr = D3DCompile(shader_src.text, shader_src.len, NULL, NULL, (ID3DInclude *)&include_handler, "vs_main", "vs_5_0", flags, 0, &vs_blob, &error_blob);
if (SUCCEEDED(hr)) {
ID3D11Device_CreateVertexShader(G.dev, ID3D10Blob_GetBufferPointer(vs_blob), ID3D10Blob_GetBufferSize(vs_blob), NULL, &shader->vs);
hr = D3DCompile(shader_src.text, shader_src.len, NULL, NULL, (ID3DInclude *)&include_handler, "ps_main", "ps_5_0", flags, 0, &ps_blob, &error_blob);
if (SUCCEEDED(hr)) {
ID3D11Device_CreatePixelShader(G.dev, ID3D10Blob_GetBufferPointer(ps_blob), ID3D10Blob_GetBufferSize(ps_blob), NULL, &shader->ps);
success = true;
}
}
}
if (success && !error_blob) {
/* Get number of device layout elements from NULL terminated array */
u32 elem_count = 0;
for (; elem_count < ARRAY_COUNT(shader_desc->input_layout_desc); ++elem_count) {
const D3D11_INPUT_ELEMENT_DESC *d = &shader_desc->input_layout_desc[elem_count];
if (d->SemanticName == NULL) {
break;
}
}
/* Create device layout */
ID3D11Device_CreateInputLayout(G.dev, shader_desc->input_layout_desc, elem_count, ID3D10Blob_GetBufferPointer(vs_blob), ID3D10Blob_GetBufferSize(vs_blob), &shader->input_layout);
} else {
error_str = LIT("Unknown error");
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) {
if (include_handler.error.len > 0) {
error_str = string_format(arena, LIT("%F %F"), FMT_STR(error_blob_str), FMT_STR(include_handler.error));
} else {
error_str = string_copy(arena, error_blob_str);
}
}
}
}
if (vs_blob) {
ID3D10Blob_Release(vs_blob);
}
if (ps_blob) {
ID3D10Blob_Release(ps_blob);
}
if (error_blob) {
ID3D10Blob_Release(error_blob);
}
shader->valid = true;
dx11_include_handler_release(&include_handler);
scratch_end(scratch);
return error_str;
}
INTERNAL void shader_release(struct dx11_shader *shader)
{
__prof;
if (shader->vs) {
ID3D11VertexShader_Release(shader->vs);
}
if (shader->ps) {
ID3D11PixelShader_Release(shader->ps);
}
if (shader->input_layout) {
ID3D11InputLayout_Release(shader->input_layout);
}
}
INTERNAL void reload_shader(struct dx11_shader *old_shader, struct dx11_shader_desc *desc)
{
__prof;
struct temp_arena scratch = scratch_begin_no_conflict();
{
struct string name = string_from_cstr_no_limit(desc->name_cstr);
struct string error_msg = ZI;
struct resource src_res = resource_open(name);
{
if (resource_exists(&src_res)) {
struct dx11_shader new_shader = ZI;
struct string comp_error = shader_alloc(scratch.arena, &new_shader, desc, &src_res);
if (comp_error.len == 0) {
if (old_shader->valid) {
shader_release(old_shader);
}
*old_shader = new_shader;
} else {
error_msg = string_format(scratch.arena,
LIT("Failed to compile shader \"%F\":\n\n%F"),
FMT_STR(name),
FMT_STR(comp_error));
shader_release(&new_shader);
}
} else {
error_msg = string_format(scratch.arena, LIT("Could not find shader \"%F\""), FMT_STR(name));
}
}
resource_close(&src_res);
if (error_msg.len != 0) {
if (old_shader->valid) {
/* If shader failed to load but a working shader already exists, just error rather than panicking */
log_error(error_msg);
} else {
sys_panic(error_msg);
}
}
}
scratch_end(scratch);
}
#if RESOURCE_RELOADING
INTERNAL RESOURCE_WATCH_CALLBACK_FUNC_DEF(shader_resource_watch_callback, name)
{
if (shader_set_dirty(name)) {
logf_info("Shader source file \"%F\" has changed", FMT_STR(name));
}
}
#endif
/* ========================== *
* Render
* ========================== */
/* TODO: Lock cmdbuff or at least global state? (in-case multi-threaded present).
* Another option is to store a separate device on each cmdbuff?
*
* I'm thinking we may also just need to lock texture modification access while presenting */
INTERNAL void dx11_render(ID3D11RenderTargetView *target, struct gpu_cmd_buffer *cmdbuff, struct xform view, struct rect viewport)
{
__prof;
__profscope_dx11(G.profiling_ctx, Render, RGB_F(0.5, 0.2, 0.2));
struct sprite_scope *sprite_scope = sprite_scope_begin();
ID3D11DeviceContext_OMSetRenderTargets(G.devcon, 1, &target, NULL);
D3D11_VIEWPORT d3d11_viewport = ZI;
d3d11_viewport.Width = viewport.width;
d3d11_viewport.Height = viewport.height;
d3d11_viewport.MinDepth = 0.0f;
d3d11_viewport.MaxDepth = 1.0f;
d3d11_viewport.TopLeftX = viewport.x;
d3d11_viewport.TopLeftY = viewport.y;
ID3D11DeviceContext_RSSetViewports(G.devcon, 1, &d3d11_viewport);
/* Fill and set constant buffer
* NOTE: We're only doing this once per render, rather than once per
* draw call since the only constant right now is VP. */
struct mat4x4 vp_matrix = calculate_vp(view, viewport.width, viewport.height);
send_constant_buffer_data(G.vs_constant_buffer, vp_matrix);
ID3D11DeviceContext_VSSetConstantBuffers(G.devcon, 0, 1, &G.vs_constant_buffer);
ID3D11DeviceContext_PSSetConstantBuffers(G.devcon, 0, 1, &G.vs_constant_buffer);
struct dx11_shader *prev_shader = NULL;
struct gpu_cmd *cmd = cmdbuff ? cmdbuff->gpu_cmd_store.cmd_first : NULL;
for (; cmd; cmd = cmd->next) {
struct dx11_shader *shader = cmd->shader;
struct dx11_buffer *buffer = &cmdbuff->buffers[shader->kind];
/* Activate shader */
if (shader != prev_shader) {
ID3D11DeviceContext_VSSetShader(G.devcon, shader->vs, 0, 0);
ID3D11DeviceContext_PSSetShader(G.devcon, shader->ps, 0, 0);
ID3D11DeviceContext_IASetInputLayout(G.devcon, shader->input_layout);
prev_shader = shader;
}
ID3D11ShaderResourceView *null_srv[1] = { NULL };
switch (shader->kind) {
default:
{
/* Unknown shader */
ASSERT(false);
} break;
case SHADER_TRIANGLE:
{
__profscope_dx11(G.profiling_ctx, Triangle Shader, RGB_F(0.2, 0.2, 0.5));
/* FIXME: Texture refcount needs to be increased here to prevent release mid-render */
ID3D11Texture2D *texture = NULL;
if (cmd->texture.handle) {
/* Load texture if handle is set */
texture = ((struct dx11_texture *)cmd->texture.handle)->texture;
} else {
/* Otherwise load sprite */
struct sprite_texture *sprite_texture = sprite_texture_from_tag_async(sprite_scope, cmd->sprite);
if (sprite_texture->loaded) {
texture = ((struct dx11_texture *)sprite_texture->texture.handle)->texture;
}
}
if (texture) {
/* Bind texture */
/* Create SRV */
D3D11_TEXTURE2D_DESC texture_desc = ZI;
ID3D11Texture2D_GetDesc(texture, &texture_desc);
D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc = { .Format = texture_desc.Format, .ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D, .Texture2D.MipLevels = texture_desc.MipLevels, .Texture2D.MostDetailedMip = 0 };
ID3D11ShaderResourceView *texture_srv = NULL;
ID3D11Device_CreateShaderResourceView(G.dev, (ID3D11Resource *)texture, &srv_desc, &texture_srv);
ID3D11DeviceContext_PSSetShaderResources(G.devcon, 0, 1, &texture_srv);
ID3D11DeviceContext_IASetPrimitiveTopology(G.devcon, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
/* Activate buffer */
u32 zero = 0;
UINT vertex_stride = shader->vertex_size;
ID3D11DeviceContext_IASetVertexBuffers(G.devcon, 0, 1, &buffer->gpu_vertex_buffer, &vertex_stride, &zero);
ID3D11DeviceContext_IASetIndexBuffer(G.devcon, buffer->gpu_index_buffer, DXGI_FORMAT_R32_UINT, zero);
/* Draw */
u32 vertex_offset = cmd->vertex_offset;
u32 index_offset = cmd->index_offset;
u32 index_count = cmd->index_count;
ID3D11DeviceContext_DrawIndexed(G.devcon, index_count, index_offset, vertex_offset);
/* Release SRV */
ID3D11ShaderResourceView_Release(texture_srv);
/* Unbind */
ID3D11DeviceContext_PSSetShaderResources(G.devcon, 0, 1, null_srv);
}
} break;
case SHADER_GRID:
{
__profscope_dx11(G.profiling_ctx, Grid Shader, RGB_F(0.2, 0.5, 0.2));
ID3D11DeviceContext_IASetPrimitiveTopology(G.devcon, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
/* Activate buffer */
u32 zero = 0;
UINT vertex_stride = shader->vertex_size;
ID3D11DeviceContext_IASetVertexBuffers(G.devcon, 0, 1, &buffer->gpu_vertex_buffer, &vertex_stride, &zero);
ID3D11DeviceContext_IASetIndexBuffer(G.devcon, buffer->gpu_index_buffer, DXGI_FORMAT_R32_UINT, zero);
/* Draw */
u32 vertex_offset = cmd->vertex_offset;
u32 index_offset = cmd->index_offset;
u32 index_count = cmd->index_count;
ID3D11DeviceContext_DrawIndexed(G.devcon, index_count, index_offset, vertex_offset);
} break;
}
}
sprite_scope_end(sprite_scope);
}
/* ========================== *
* Texture
* ========================== */
INTERNAL struct dx11_format dx11_format_from_gpu_format(enum gpu_texture_format format)
{
LOCAL_PERSIST const struct dx11_format sizes[NUM_GPU_TEXTURE_FORMATS] = {
[GPU_TEXTURE_FORMAT_R8G8B8A8_UNORM] = {
.format = DXGI_FORMAT_R8G8B8A8_UNORM,
.pixel_size = 4
},
[GPU_TEXTURE_FORMAT_R8G8B8A8_UNORM_SRGB] = {
.format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB,
.pixel_size = 4
}
};
struct dx11_format res = ZI;
if ((u32)format < ARRAY_COUNT(sizes)) {
res = sizes[format];
}
return res;
}
INTERNAL struct dx11_texture *dx11_texture_alloc(enum gpu_texture_format format, u32 flags, struct v2i32 size, void *initial_data)
{
struct dx11_texture *t = NULL;
{
/* Allocate to list */
struct sys_lock lock = sys_mutex_lock_e(&G.textures_mutex);
if (G.textures_first_free) {
t = G.textures_first_free;
G.textures_first_free = t->next_free;
} else {
t = arena_push_no_zero(&G.textures_arena, struct dx11_texture);
}
MEMZERO_STRUCT(t);
if (!G.textures_first) {
G.textures_first = t;
} else {
G.textures_last->next = t;
}
t->prev = G.textures_last;
G.textures_last = t;
sys_mutex_unlock(&lock);
}
struct dx11_format dx11_format = dx11_format_from_gpu_format(format);
if (dx11_format.format == DXGI_FORMAT_UNKNOWN) {
/* Unknown format */
ASSERT(false);
sys_panic(LIT("Unknown dx11 texture format during texture allocation"));
}
u32 bind_flags = D3D11_BIND_SHADER_RESOURCE;
if (flags & GPU_TEXTURE_FLAG_TARGET) {
bind_flags |= D3D11_BIND_RENDER_TARGET;
}
D3D11_TEXTURE2D_DESC desc = ZI;
desc.Width = max_i32(size.x, 1);
desc.Height = max_i32(size.y, 1);
desc.MipLevels = 1;
desc.ArraySize = 1;
desc.SampleDesc.Count = 1;
desc.Usage = D3D11_USAGE_DEFAULT;
desc.CPUAccessFlags = 0;
desc.BindFlags = bind_flags;
desc.Format = dx11_format.format;
ID3D11Texture2D *texture = NULL;
if (initial_data) {
D3D11_SUBRESOURCE_DATA subresource_data = { .pSysMem = initial_data, .SysMemPitch = size.x * dx11_format.pixel_size, .SysMemSlicePitch = 0 };
ID3D11Device_CreateTexture2D(G.dev, &desc, &subresource_data, &texture);
} else {
ID3D11Device_CreateTexture2D(G.dev, &desc, NULL, &texture);
}
ASSERT(texture != NULL);
t->texture = texture;
return t;
}
INTERNAL void dx11_texture_release(struct dx11_texture *t)
{
if (!t->is_backbuffer) {
{
/* Free from list */
struct sys_lock lock = sys_mutex_lock_e(&G.textures_mutex);
struct dx11_texture *prev = t->prev;
struct dx11_texture *next = t->next;
if (prev) {
prev->next = next;
}
if (next) {
next->prev = prev;
}
if (G.textures_first == t) {
G.textures_first = next;
}
if (G.textures_last == t) {
G.textures_last = prev;
}
t->next_free = G.textures_first_free;
G.textures_first_free = t;
sys_mutex_unlock(&lock);
}
if (t->texture) {
ID3D11Texture2D_Release(t->texture);
}
}
}
struct gpu_texture gpu_texture_alloc(enum gpu_texture_format format, u32 flags, struct v2i32 size, void *initial_data)
{
__prof;
struct gpu_texture res = ZI;
struct dx11_texture *t = dx11_texture_alloc(format, flags, size, initial_data);
res.handle = (u64)t;
return res;
}
void gpu_texture_release(struct gpu_texture t)
{
__prof;
dx11_texture_release((struct dx11_texture *)t.handle);
}
void gpu_texture_clear(struct gpu_texture target_texture, u32 clear_color)
{
__prof;
ID3D11Texture2D *texture = ((struct dx11_texture *)target_texture.handle)->texture;
ID3D11RenderTargetView *target_view = NULL;
ID3D11Device_CreateRenderTargetView(G.dev, (ID3D11Resource *)texture, NULL, &target_view);
if (target_view) {
f32 r = (f32)((clear_color >> 0) & 0xFF) / 255.0f;
f32 g = (f32)((clear_color >> 8) & 0xFF) / 255.0f;
f32 b = (f32)((clear_color >> 16) & 0xFF) / 255.0f;
f32 a = (f32)((clear_color >> 24) & 0xFF) / 255.0f;
f32 fill[4] = { r, g, b, a };
ID3D11DeviceContext_ClearRenderTargetView(G.devcon, target_view, fill);
}
if (target_view) {
ID3D11RenderTargetView_Release(target_view);
}
}
void gpu_texture_render(struct gpu_texture texture, struct gpu_cmd_buffer *cmdbuff, struct xform view, struct rect viewport)
{
__prof;
struct dx11_texture *t = (struct dx11_texture *)texture.handle;
D3D11_RENDER_TARGET_VIEW_DESC rtv_desc = ZI;
D3D11_RENDER_TARGET_VIEW_DESC *rtv_desc_param = NULL;
if (t->is_backbuffer) {
rtv_desc.Format = DX11_SWAPCHAIN_RTV_FORMAT;
rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
rtv_desc_param = &rtv_desc;
}
ID3D11RenderTargetView *target_view = NULL;
ID3D11Device_CreateRenderTargetView(G.dev, (ID3D11Resource *)t->texture, rtv_desc_param, &target_view);
if (target_view) {
dx11_render(target_view, cmdbuff, view, viewport);
}
if (target_view) {
ID3D11RenderTargetView_Release(target_view);
}
}
struct v2i32 gpu_texture_get_size(struct gpu_texture texture)
{
struct v2i32 res = ZI;
D3D11_TEXTURE2D_DESC desc;
ID3D11Texture2D_GetDesc(((struct dx11_texture *)texture.handle)->texture, &desc);
res.x = desc.Width;
res.y = desc.Height;
return res;
}
/* ========================== *
* Backbuffer
* ========================== */
#if GSTAT_ENABLED || PROFILING
INTERNAL struct DXGI_QUERY_VIDEO_MEMORY_INFO get_memory_info(void)
{
__prof;
/* Get DXGI device from D3D11 device */
IDXGIDevice *dxgiDevice;
HRESULT hr = ID3D11Device_QueryInterface(G.dev, &IID_IDXGIDevice, (void **)&dxgiDevice);
(UNUSED)hr;
ASSERT(SUCCEEDED(hr));
/* Get DXGI adapter from DXGI device */
IDXGIAdapter *dxgiAdapter;
hr = IDXGIDevice_GetAdapter(dxgiDevice, &dxgiAdapter);
ASSERT(SUCCEEDED(hr));
IDXGIAdapter3 *dxgiAdapter3 = NULL;
hr = IDXGIAdapter_QueryInterface(dxgiAdapter, &IID_IDXGIAdapter3, (void **)&dxgiAdapter3);
ASSERT(SUCCEEDED(hr));
struct DXGI_QUERY_VIDEO_MEMORY_INFO info = ZI;
IDXGIAdapter3_QueryVideoMemoryInfo(
dxgiAdapter3,
0,
DXGI_MEMORY_SEGMENT_GROUP_LOCAL,
&info
);
IDXGIAdapter_Release(dxgiAdapter3);
IDXGIAdapter_Release(dxgiAdapter);
IDXGIDevice_Release(dxgiDevice);
return info;
}
#endif
struct gpu_texture gpu_recreate_backbuffer(struct v2i32 size)
{
struct gpu_texture res = ZI;
/* Resize */
if (G.backbuffer_texture.texture != 0) {
ID3D11Texture2D_Release(G.backbuffer_texture.texture);
}
IDXGISwapChain_ResizeBuffers(G.swapchain, 0, size.x, size.y, DXGI_FORMAT_UNKNOWN, DX11_SWAPCHAIN_FLAGS);
IDXGISwapChain_GetBuffer(G.swapchain, 0, &IID_ID3D11Texture2D, (LPVOID *)&G.backbuffer_texture.texture);
res.handle = (u64)&G.backbuffer_texture;
return res;
}
void gpu_present_backbuffer(i32 vsync)
{
__prof;
#if RESOURCE_RELOADING
for (u64 i = SHADER_NONE + 1; i < NUM_SHADERS; ++i) {
struct dx11_shader_desc *desc = &G.shader_info[i];
if (shader_unset_dirty(desc)) {
reload_shader(&G.shaders[i], desc);
}
}
#endif
#if GSTAT_ENABLED || PROFILING
{
struct DXGI_QUERY_VIDEO_MEMORY_INFO info = get_memory_info();
u64 vram = info.CurrentUsage;
u64 budget = info.Budget;
gstat_set(GSTAT_VRAM_USAGE, vram);
gstat_set(GSTAT_VRAM_BUDGET, budget);
# if PROFILING
static char *plot_name = NULL;
static u64 prev_vram = 0;
if (!plot_name) {
plot_name = "Video memory usage";
__prof_plot_init(plot_name, __prof_plot_type_memory, 1, 1, 0);
}
if (vram != prev_vram) {
__prof_plot_i(plot_name, vram);
}
prev_vram = vram;
# endif
}
#endif
i32 flags = 0;
if (vsync == 0) {
flags = DXGI_PRESENT_ALLOW_TEARING;
}
gpu_capture_image_for_profiler();
{
__profscope(IDXGISwapchain_Present);
IDXGISwapChain1_Present(G.swapchain, vsync, flags);
__prof_dx11_collect(G.profiling_ctx);
__profframe(0);
}
}
/* ========================== *
* Cmd buffer
* ========================== */
struct gpu_cmd_buffer *gpu_cmd_buffer_alloc(void)
{
struct gpu_cmd_buffer *cmdbuff = NULL;
for (u32 i = 0; i < MAX_CMD_BUFFERS; ++i) {
if (!G.cmdbuffs[i].valid) {
cmdbuff = &G.cmdbuffs[i];
break;
}
}
if (!cmdbuff) {
sys_panic(LIT("Max gpu cmdbuffs reached"));
return NULL;
}
MEMZERO_STRUCT(cmdbuff);
cmdbuff->cpu_cmd_store.arena = arena_alloc(GIGABYTE(8));
cmdbuff->gpu_cmd_store.arena = arena_alloc(GIGABYTE(8));
cmdbuff->valid = true;
/* Initialize buffers */
for (u32 i = SHADER_NONE + 1; i < ARRAY_COUNT(cmdbuff->buffers); ++i) {
struct dx11_buffer *buffer = &cmdbuff->buffers[i];
buffer->vertex_arena = arena_alloc(GIGABYTE(8));
buffer->index_arena = arena_alloc(GIGABYTE(8));
buffer->cpu_vertex_buffer = arena_dry_push(&buffer->vertex_arena, u8);
buffer->cpu_index_buffer = arena_dry_push(&buffer->index_arena, vidx);
}
return cmdbuff;
}
void gpu_cmd_buffer_release(struct gpu_cmd_buffer *cmdbuff)
{
cmdbuff->valid = false;
arena_release(&cmdbuff->cpu_cmd_store.arena);
arena_release(&cmdbuff->gpu_cmd_store.arena);
/* Destroy buffers */
for (u32 i = SHADER_NONE + 1; i < ARRAY_COUNT(cmdbuff->buffers); ++i) {
struct dx11_buffer *buffer = &cmdbuff->buffers[i];
arena_release(&buffer->vertex_arena);
arena_release(&buffer->index_arena);
/* FIXME: Clear GPU buffers */
}
}
u32 gpu_cmd_buffer_push_vertices(struct gpu_cmd_buffer *cmdbuff, u8 **vertices_out, vidx **indices_out, u32 vertices_count, u32 indices_count)
{
struct gpu_cmd *cmd = cmdbuff->cpu_cmd_store.cmd_last;
if (!cmd) {
/* Tried to draw to cmdbuff with no active draw cmd */
ASSERT(false);
return 0;
}
struct dx11_shader *shader = cmd->shader;
struct dx11_buffer *buffer = &cmdbuff->buffers[shader->kind];
if (!cmd->offsets_set) {
cmd->vertex_offset = buffer->vertex_count;
cmd->index_offset = buffer->index_count;
cmd->offsets_set = true;
}
u32 first_vertex_index = cmd->vertex_count;
cmd->vertex_count += vertices_count;
cmd->index_count += indices_count;
buffer->vertex_count += vertices_count;
buffer->index_count += indices_count;
*vertices_out = arena_push_array_no_zero(&buffer->vertex_arena, u8, shader->vertex_size * vertices_count);
*indices_out = arena_push_array_no_zero(&buffer->index_arena, vidx, indices_count);
return first_vertex_index;
}
void gpu_cmd_buffer_ensure_cmd(struct gpu_cmd_buffer *cmdbuff, struct gpu_cmd_parameters *params)
{
struct gpu_cmd *last_cmd = cmdbuff->cpu_cmd_store.cmd_last;
struct gpu_cmd *new_cmd = NULL;
switch (params->kind) {
default:
{
/* Unknown shader kind */
ASSERT(false);
} break;
case SHADER_TRIANGLE:
{
if (!last_cmd
|| last_cmd->shader->kind != SHADER_TRIANGLE
|| (last_cmd->texture.handle != params->texture_params.texture.handle)
|| !sprite_tag_eq(last_cmd->sprite, params->texture_params.sprite)) {
new_cmd = arena_push(&cmdbuff->cpu_cmd_store.arena, struct gpu_cmd);
new_cmd->shader = &G.shaders[SHADER_TRIANGLE];
new_cmd->texture = params->texture_params.texture;
new_cmd->sprite = params->texture_params.sprite;
}
} break;
case SHADER_GRID:
{
if (!last_cmd || last_cmd->shader->kind != SHADER_GRID) {
new_cmd = arena_push(&cmdbuff->cpu_cmd_store.arena, struct gpu_cmd);
new_cmd->shader = &G.shaders[SHADER_GRID];
}
} break;
}
if (new_cmd) {
if (!cmdbuff->cpu_cmd_store.cmd_first) {
cmdbuff->cpu_cmd_store.cmd_first = new_cmd;
} else {
last_cmd->next = new_cmd;
}
cmdbuff->cpu_cmd_store.cmd_last = new_cmd;
}
}
void gpu_cmd_buffer_flush_to_gpu(struct gpu_cmd_buffer *cmdbuff)
{
__prof;
/* Create / grow vertex buffers */
for (u32 i = 1; i < ARRAY_COUNT(cmdbuff->buffers); ++i) {
struct dx11_buffer *buffer = &cmdbuff->buffers[i];
struct dx11_shader *shader = &G.shaders[i];
u32 vertex_size = shader->vertex_size;
u32 index_size = sizeof(vidx);
if (buffer->vertex_count == 0 || buffer->index_count == 0) {
continue;
}
/* Grow vertex buffer */
if (buffer->gpu_vertex_buffer_capacity < buffer->vertex_count) {
buffer->gpu_vertex_buffer_capacity = buffer->vertex_count + 5000;
D3D11_BUFFER_DESC desc = {
.Usage = D3D11_USAGE_DYNAMIC,
.ByteWidth = buffer->gpu_vertex_buffer_capacity * vertex_size,
.BindFlags = D3D11_BIND_VERTEX_BUFFER,
.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE
};
/* TODO: Error checking */
if (buffer->gpu_vertex_buffer) {
ID3D11Buffer_Release(buffer->gpu_vertex_buffer);
}
ID3D11Device_CreateBuffer(G.dev, &desc, NULL, &buffer->gpu_vertex_buffer);
}
/* Grow index buffer */
if (buffer->gpu_index_buffer_capacity < buffer->index_count) {
buffer->gpu_index_buffer_capacity = buffer->index_count + 5000;
D3D11_BUFFER_DESC desc = {
.Usage = D3D11_USAGE_DYNAMIC,
.ByteWidth = buffer->gpu_index_buffer_capacity * index_size,
.BindFlags = D3D11_BIND_INDEX_BUFFER,
.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE
};
/* TODO: Error checking */
if (buffer->gpu_index_buffer) {
ID3D11Buffer_Release(buffer->gpu_index_buffer);
}
ID3D11Device_CreateBuffer(G.dev, &desc, NULL, &buffer->gpu_index_buffer);
}
/* Fill GPU vertex buffer */
if (buffer->gpu_vertex_buffer) {
D3D11_MAPPED_SUBRESOURCE res;
ID3D11DeviceContext_Map(G.devcon, (ID3D11Resource *)buffer->gpu_vertex_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &res);
MEMCPY(res.pData, buffer->cpu_vertex_buffer, buffer->vertex_count * vertex_size);
ID3D11DeviceContext_Unmap(G.devcon, (ID3D11Resource *)buffer->gpu_vertex_buffer, 0);
}
/* Fill GPU index buffer */
if (buffer->gpu_index_buffer) {
D3D11_MAPPED_SUBRESOURCE res;
ID3D11DeviceContext_Map(G.devcon, (ID3D11Resource *)buffer->gpu_index_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &res);
MEMCPY(res.pData, buffer->cpu_index_buffer, buffer->index_count * index_size);
ID3D11DeviceContext_Unmap(G.devcon, (ID3D11Resource *)buffer->gpu_index_buffer, 0);
}
/* Reset CPU buffers */
buffer->vertex_count = 0;
buffer->index_count = 0;
arena_reset(&buffer->vertex_arena);
arena_reset(&buffer->index_arena);
}
/* Swap CPU cmds to GPU store */
struct cmd_store temp = cmdbuff->gpu_cmd_store;
cmdbuff->gpu_cmd_store = cmdbuff->cpu_cmd_store;
cmdbuff->cpu_cmd_store = temp;
/* Reset CPU cmds */
cmdbuff->cpu_cmd_store.cmd_first = NULL;
cmdbuff->cpu_cmd_store.cmd_last = NULL;
arena_reset(&cmdbuff->cpu_cmd_store.arena);
}
/* ========================== *
* Profiling frame capture
* ========================== */
/* FIXME: enable this */
#if PROFILING && PROFILING_CAPTURE_FRAME_IMAGE
#define CAP_WIDTH 320
#define CAP_HEIGHT 180
struct prof_cap {
ID3D11Texture2D *texture;
struct v2 size;
};
INTERNAL void gpu_capture_image_for_profiler(void)
{
__prof;
/* A rolling window of staging textures is used. This is because trying to
* map a texture immediately after copying the resource will cause the map
* to hang while it waits for the copy to finish.
*
* At the time of writing this code, 5 textures seems to be the sweet spot
* for performance.
*/
static struct prof_cap staging_caps[5] = ZI;
static u32 cap_index = 0;
static b32 ready_to_read = false;
ID3D11Texture2D *backbuffer = NULL;
IDXGISwapChain_GetBuffer(G.swapchain, 0, &IID_ID3D11Texture2D, (LPVOID *)&backbuffer);
struct prof_cap *write_cap = &staging_caps[cap_index];
MEMZERO_STRUCT(write_cap);
{
D3D11_TEXTURE2D_DESC staging_desc;
ID3D11Texture2D_GetDesc(backbuffer, &staging_desc);
staging_desc.Usage = D3D11_USAGE_STAGING;
staging_desc.BindFlags = 0;
staging_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
write_cap->size = V2(staging_desc.Width, staging_desc.Height);
ID3D11Device_CreateTexture2D(G.dev, &staging_desc, NULL, &write_cap->texture);
}
ID3D11DeviceContext_CopyResource(G.devcon, (ID3D11Resource *)write_cap->texture, (ID3D11Resource *)backbuffer);
ID3D11Texture2D_Release(backbuffer);
++cap_index;
if (cap_index >= ARRAY_COUNT(staging_caps)) {
cap_index = 0;
ready_to_read = true;
}
if (ready_to_read) {
struct prof_cap *read_cap = &staging_caps[cap_index];
{
D3D11_MAPPED_SUBRESOURCE res;
ID3D11DeviceContext_Map(G.devcon, (ID3D11Resource *)read_cap->texture, 0, D3D11_MAP_READ, 0, &res);
u32 final_width = CAP_WIDTH;
u32 final_height = CAP_HEIGHT;
f32 width_frequency = (f32)read_cap->size.x / (f32)final_width;
f32 height_frequency = (f32)read_cap->size.y / (f32)final_height;
{
struct temp_arena scratch = scratch_begin_no_conflict();
u32 *source = res.pData;
u32 *dest = arena_push_array_no_zero(scratch.arena, u32, final_width * final_height);
u32 pitch = res.RowPitch / 4;
for (u32 y = 0; y < final_height; ++y) {
for (u32 x = 0; x < final_width; ++x) {
u32 *pixel = &dest[x + (y * final_width)];
u64 source_x = (u64)(width_frequency * (f32)x);
u64 source_y = (u64)(height_frequency * (f32)y);
*pixel = source[source_x + (source_y * pitch)];
}
}
{
__profscope(prof_frame_image);
__profframeimage(dest, (u16)final_width, (u16)final_height, ARRAY_COUNT(staging_caps) - 1, false);
}
scratch_end(scratch);
}
ID3D11DeviceContext_Unmap(G.devcon, (ID3D11Resource *)read_cap->texture, 0);
}
ID3D11Texture2D_Release(read_cap->texture);
}
}
#else
INTERNAL void gpu_capture_image_for_profiler(void)
{
}
#endif