2161 lines
74 KiB
C
2161 lines
74 KiB
C
#if !DX12_TEST
|
|
|
|
#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")
|
|
|
|
/* 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_BACKBUFFER_RECREATION_DELAY (1.0 / 30.0)
|
|
|
|
#define DX11_WAIT_FRAME_LATENCY 1
|
|
#define DX11_ALLOW_TEARING 1
|
|
|
|
#define DX11_SWAPCHAIN_FLAGS ((DX11_ALLOW_TEARING * DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING) | (DX11_WAIT_FRAME_LATENCY * DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT))
|
|
|
|
#define DX11_SWAPCHAIN_FORMAT (DXGI_FORMAT_R8G8B8A8_UNORM)
|
|
#define DX11_SWAPCHAIN_RTV_FORMAT (DXGI_FORMAT_R8G8B8A8_UNORM_SRGB)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* FIXME: Remove this */
|
|
#include "draw.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
enum dx11_shader_kind {
|
|
DX11_SHADER_KIND_NONE,
|
|
DX11_SHADER_KIND_MESH,
|
|
DX11_SHADER_KIND_TEXTURE,
|
|
DX11_SHADER_KIND_GRID,
|
|
DX11_SHADER_KIND_TEST,
|
|
|
|
NUM_DX11_SHADER_KINDS
|
|
};
|
|
|
|
enum dx11_handle_kind {
|
|
DX11_HANDLE_KIND_NONE,
|
|
DX11_HANDLE_KIND_TEXTURE,
|
|
DX11_HANDLE_KIND_PLAN,
|
|
|
|
NUM_DX11_HANDLE_KINDS
|
|
};
|
|
|
|
struct dx11_handle_header {
|
|
enum dx11_handle_kind kind;
|
|
};
|
|
|
|
struct dx11_shader {
|
|
enum dx11_shader_kind kind;
|
|
b32 valid; /* Is this shader allocated */
|
|
ID3D11InputLayout *input_layout;
|
|
ID3D11VertexShader *vs;
|
|
ID3D11PixelShader *ps;
|
|
};
|
|
|
|
struct dx11_buffer {
|
|
D3D11_BUFFER_DESC desc;
|
|
|
|
/* Cpu buffer */
|
|
struct arena cpu_buffer_arena;
|
|
u8 *cpu_buffer;
|
|
|
|
/* Gpu buffer */
|
|
ID3D11ShaderResourceView *srv;
|
|
ID3D11Buffer *gpu_buffer;
|
|
u64 gpu_buffer_pos;
|
|
u64 gpu_buffer_capacity;
|
|
|
|
struct dx11_buffer *next_free;
|
|
};
|
|
|
|
struct dx11_cmd_buffers {
|
|
union {
|
|
struct {
|
|
struct dx11_buffer *vertex_buffer;
|
|
struct dx11_buffer *index_buffer;
|
|
} mesh;
|
|
struct {
|
|
struct dx11_buffer *instance_buffer;
|
|
} texture;
|
|
struct {
|
|
struct dx11_buffer *instance_buffer;
|
|
} grid;
|
|
};
|
|
};
|
|
|
|
struct dx11_cmd {
|
|
enum gpu_cmd_kind kind;
|
|
union {
|
|
struct {
|
|
struct xform xf;
|
|
} view;
|
|
struct {
|
|
u32 vertex_offset;
|
|
u32 vertex_count;
|
|
u32 index_offset;
|
|
u32 index_count;
|
|
} mesh;
|
|
struct {
|
|
struct gpu_handle texture; /* Overrides sprite if set */
|
|
struct sprite_tag sprite;
|
|
u32 instance_offset;
|
|
u32 instance_count;
|
|
} texture;
|
|
struct {
|
|
u32 instance_offset;
|
|
u32 instance_count;
|
|
} grid;
|
|
struct {
|
|
u32 instance_offset;
|
|
u32 instance_count;
|
|
} test;
|
|
};
|
|
|
|
struct dx11_cmd *next;
|
|
};
|
|
|
|
struct dx11_plan {
|
|
struct dx11_handle_header header;
|
|
|
|
/* Commands w/ data still in cpu memory */
|
|
struct arena cpu_cmds_arena;
|
|
struct dx11_cmd *cpu_first_cmd;
|
|
struct dx11_cmd *cpu_last_cmd;
|
|
|
|
/* Commands w/ buffer data submitted to video memory */
|
|
struct arena gpu_cmds_arena;
|
|
struct dx11_cmd *gpu_first_cmd;
|
|
struct dx11_cmd *gpu_last_cmd;
|
|
|
|
struct {
|
|
struct {
|
|
struct dx11_buffer *vertex_buffer;
|
|
struct dx11_buffer *index_buffer;
|
|
} mesh;
|
|
struct {
|
|
struct dx11_buffer *instance_buffer;
|
|
} texture;
|
|
struct {
|
|
struct dx11_buffer *instance_buffer;
|
|
} grid;
|
|
struct {
|
|
struct dx11_buffer *instance_buffer;
|
|
} test;
|
|
} cmd_buffers;
|
|
|
|
struct dx11_buffer *constant_buffer;
|
|
|
|
struct dx11_plan *next_free;
|
|
};
|
|
|
|
struct dx11_texture {
|
|
struct dx11_handle_header header;
|
|
|
|
ID3D11Texture2D *texture;
|
|
ID3D11ShaderResourceView *srv;
|
|
ID3D11RenderTargetView *rtv;
|
|
struct v2i32 size;
|
|
struct dx11_texture *next_free;
|
|
};
|
|
|
|
/* ========================== *
|
|
* Global state
|
|
* ========================== */
|
|
|
|
struct dx11_shader_desc {
|
|
enum dx11_shader_kind kind;
|
|
char *name_cstr;
|
|
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;
|
|
|
|
IDXGISwapChain2 *swapchain;
|
|
HANDLE swapchain_waitable;
|
|
struct dx11_texture backbuffer_texture;
|
|
i64 last_backbuffer_resize_ns;
|
|
|
|
ID3D11BlendState *blend_state;
|
|
ID3D11RasterizerState *rasterizer_state;
|
|
ID3D11DepthStencilState *depth_stencil_state;
|
|
ID3D11SamplerState *sampler_state;
|
|
|
|
/* Buffer pool */
|
|
struct sys_mutex buffers_mutex;
|
|
struct arena buffers_arena;
|
|
struct dx11_buffer *first_free_buffer;
|
|
|
|
/* Plan pool */
|
|
struct sys_mutex plans_mutex;
|
|
struct arena plans_arena;
|
|
struct dx11_plan *first_free_plan;
|
|
|
|
/* Dispatch state pool */
|
|
struct sys_mutex dispatch_states_mutex;
|
|
struct arena dispatch_states_arena;
|
|
struct dx11_dispatch_state *first_free_dispatch_state;
|
|
|
|
/* Texture pool */
|
|
struct sys_mutex textures_mutex;
|
|
struct arena textures_arena;
|
|
struct dx11_texture *first_free_texture;
|
|
|
|
/* Shaders */
|
|
struct dx11_shader shaders[NUM_DX11_SHADER_KINDS];
|
|
struct dx11_shader_desc shader_info[NUM_DX11_SHADER_KINDS];
|
|
|
|
/* Dummy buffers */
|
|
struct dx11_buffer *dummy_vertex_buffer;
|
|
struct dx11_buffer *quad_index_buffer;
|
|
|
|
/* Blit plan */
|
|
struct gpu_handle present_blit_plan;
|
|
|
|
} G = ZI, DEBUG_ALIAS(G, G_gpu_dx11);
|
|
|
|
/* ========================== *
|
|
* Startup
|
|
* ========================== */
|
|
|
|
INTERNAL void init_shader_table(void);
|
|
INTERNAL void reload_shader(struct dx11_shader *shader, struct dx11_shader_desc *desc);
|
|
INTERNAL struct dx11_buffer *dx11_buffer_alloc(struct D3D11_BUFFER_DESC desc, D3D11_SUBRESOURCE_DATA *initial_data);
|
|
|
|
#if RESOURCE_RELOADING
|
|
INTERNAL RESOURCE_WATCH_CALLBACK_FUNC_DEF(shader_resource_watch_callback, name);
|
|
#endif
|
|
|
|
struct gpu_startup_receipt gpu_startup(struct work_startup_receipt *work_sr, struct sys_window *window)
|
|
{
|
|
__prof;
|
|
(UNUSED)work_sr;
|
|
|
|
G.arena = arena_alloc(GIGABYTE(64));
|
|
|
|
/* Initialize buffers pool */
|
|
G.buffers_mutex = sys_mutex_alloc();
|
|
G.buffers_arena = arena_alloc(GIGABYTE(64));
|
|
|
|
/* Initialize plans pool */
|
|
G.plans_mutex = sys_mutex_alloc();
|
|
G.plans_arena = arena_alloc(GIGABYTE(64));
|
|
|
|
/* Initialize dispatch state pool */
|
|
G.dispatch_states_mutex = sys_mutex_alloc();
|
|
G.dispatch_states_arena = arena_alloc(GIGABYTE(64));
|
|
|
|
/* Initialize texture pool */
|
|
G.textures_mutex = sys_mutex_alloc();
|
|
G.textures_arena = arena_alloc(GIGABYTE(64));
|
|
|
|
/* Initialize shader table */
|
|
init_shader_table();
|
|
|
|
HRESULT hr;
|
|
ID3D11Device *device = NULL;
|
|
ID3D11DeviceContext *context = NULL;
|
|
IDXGISwapChain2 *swapchain = NULL;
|
|
|
|
/* 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 *dxgi_info;
|
|
hr = DXGIGetDebugInterface1(0, &IID_IDXGIInfoQueue, (void **)&dxgi_info);
|
|
ASSERT(SUCCEEDED(hr));
|
|
IDXGIInfoQueue_SetBreakOnSeverity(dxgi_info, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_CORRUPTION, TRUE);
|
|
IDXGIInfoQueue_SetBreakOnSeverity(dxgi_info, DXGI_DEBUG_ALL, DXGI_INFO_QUEUE_MESSAGE_SEVERITY_ERROR, TRUE);
|
|
IDXGIInfoQueue_Release(dxgi_info);
|
|
}
|
|
#endif
|
|
|
|
/* Create 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_SHADER_INPUT | DXGI_USAGE_RENDER_TARGET_OUTPUT,
|
|
.BufferCount = 3,
|
|
.Scaling = DXGI_SCALING_NONE,
|
|
.Flags = DX11_SWAPCHAIN_FLAGS,
|
|
.AlphaMode = DXGI_ALPHA_MODE_IGNORE,
|
|
.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD
|
|
};
|
|
|
|
IDXGISwapChain1 *swapchain1 = NULL;
|
|
hr = IDXGIFactory2_CreateSwapChainForHwnd(factory, (IUnknown *)device, hwnd, &desc, NULL, NULL, &swapchain1);
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
IDXGISwapChain1_QueryInterface(swapchain1, &IID_IDXGISwapChain2, (void **)&swapchain);
|
|
}
|
|
|
|
/* Disable Alt+Enter changing monitor resolution to match window size */
|
|
IDXGIFactory_MakeWindowAssociation(factory, hwnd, DXGI_MWA_NO_ALT_ENTER);
|
|
|
|
IDXGISwapChain1_Release(swapchain1);
|
|
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;
|
|
|
|
/* Create the swapchain waitable object */
|
|
#if DX11_WAIT_FRAME_LATENCY
|
|
if (G.swapchain != NULL) {
|
|
IDXGISwapChain2_SetMaximumFrameLatency(G.swapchain, 1);
|
|
G.swapchain_waitable = IDXGISwapChain2_GetFrameLatencyWaitableObject(G.swapchain);
|
|
ASSERT(G.swapchain_waitable != NULL);
|
|
}
|
|
#endif
|
|
|
|
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 blend state */
|
|
{
|
|
__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);
|
|
}
|
|
|
|
/* Init shaders */
|
|
logf_info("Compiling shaders");
|
|
for (u32 i = DX11_SHADER_KIND_NONE + 1; i < NUM_DX11_SHADER_KINDS; ++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");
|
|
|
|
/* Init dummy buffers */
|
|
{
|
|
/* Dummy vertex buffer */
|
|
u8 dummy_data[16] = ZI;
|
|
D3D11_BUFFER_DESC vdesc = ZI;
|
|
vdesc.Usage = D3D11_USAGE_IMMUTABLE;
|
|
vdesc.ByteWidth = sizeof(dummy_data);
|
|
vdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
|
|
D3D11_SUBRESOURCE_DATA dummy_data_subres = ZI;
|
|
dummy_data_subres.pSysMem = dummy_data;
|
|
G.dummy_vertex_buffer = dx11_buffer_alloc(vdesc, &dummy_data_subres);
|
|
|
|
/* Quad index buffer */
|
|
LOCAL_PERSIST u16 quad_indices[6] = { 0, 1, 2, 0, 2, 3 };
|
|
D3D11_BUFFER_DESC idesc = ZI;
|
|
idesc.Usage = D3D11_USAGE_IMMUTABLE;
|
|
idesc.ByteWidth = sizeof(quad_indices);
|
|
idesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
|
|
D3D11_SUBRESOURCE_DATA idata = ZI;
|
|
idata.pSysMem = quad_indices;
|
|
G.quad_index_buffer = dx11_buffer_alloc(idesc, &idata);
|
|
}
|
|
|
|
/* Setup file change callbacks */
|
|
#if RESOURCE_RELOADING
|
|
resource_register_watch_callback(shader_resource_watch_callback);
|
|
#endif
|
|
|
|
G.present_blit_plan = gpu_plan_alloc();
|
|
|
|
return (struct gpu_startup_receipt) { 0 };
|
|
}
|
|
|
|
/* ========================== *
|
|
* 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);
|
|
}
|
|
|
|
/* ========================== *
|
|
* Handle
|
|
* ========================== */
|
|
|
|
INTERNAL void dx11_texture_release(struct dx11_texture *t);
|
|
|
|
void gpu_release(struct gpu_handle handle)
|
|
{
|
|
struct dx11_handle_header *header = (struct dx11_handle_header *)handle.v;
|
|
switch (header->kind) {
|
|
default: break;
|
|
|
|
case DX11_HANDLE_KIND_TEXTURE:
|
|
{
|
|
dx11_texture_release((struct dx11_texture *)header);
|
|
} break;
|
|
|
|
case DX11_HANDLE_KIND_PLAN:
|
|
{
|
|
/* TODO */
|
|
ASSERT(false);
|
|
} break;
|
|
}
|
|
}
|
|
|
|
/* ========================== *
|
|
* Mesh shader structs
|
|
* ========================== */
|
|
|
|
PACK(struct dx11_mesh_uniform {
|
|
struct mat4x4 vp;
|
|
});
|
|
|
|
PACK(struct dx11_mesh_vertex {
|
|
struct v2 pos;
|
|
u32 color_srgb;
|
|
});
|
|
|
|
/* ========================== *
|
|
* Texture shader structs
|
|
* ========================== */
|
|
|
|
PACK(struct dx11_texture_uniform {
|
|
struct mat4x4 vp;
|
|
u32 instance_offset;
|
|
});
|
|
|
|
PACK(struct dx11_texture_instance {
|
|
struct xform xf;
|
|
struct v2 uv0;
|
|
struct v2 uv1;
|
|
u32 tint_srgb;
|
|
f32 emittance;
|
|
});
|
|
|
|
/* ========================== *
|
|
* Grid shader structs
|
|
* ========================== */
|
|
|
|
PACK(struct dx11_grid_uniform {
|
|
struct mat4x4 vp;
|
|
u32 instance_offset;
|
|
});
|
|
|
|
PACK(struct dx11_grid_instance {
|
|
struct xform xf;
|
|
f32 line_thickness;
|
|
f32 line_spacing;
|
|
struct v2 offset;
|
|
u32 bg0_srgb;
|
|
u32 bg1_srgb;
|
|
u32 line_srgb;
|
|
u32 x_srgb;
|
|
u32 y_srgb;
|
|
});
|
|
|
|
/* ========================== *
|
|
* Test shader structs
|
|
* ========================== */
|
|
|
|
PACK(struct dx11_test_uniform {
|
|
struct mat4x4 vp;
|
|
u32 instance_offset;
|
|
});
|
|
|
|
PACK(struct dx11_test_instance {
|
|
struct xform xf;
|
|
});
|
|
|
|
/* ========================== *
|
|
* Shader table
|
|
* ========================== */
|
|
|
|
INTERNAL void init_shader_table(void)
|
|
{
|
|
MEMZERO_ARRAY(G.shader_info);
|
|
|
|
/* Mesh shader layout */
|
|
G.shader_info[DX11_SHADER_KIND_MESH] = (struct dx11_shader_desc) {
|
|
.kind = DX11_SHADER_KIND_MESH,
|
|
.name_cstr = "shaders_dx11/mesh.hlsl",
|
|
.input_layout_desc = {
|
|
{ "pos", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
|
|
{ "color_srgb", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }
|
|
}
|
|
};
|
|
|
|
/* Texture shader layout */
|
|
G.shader_info[DX11_SHADER_KIND_TEXTURE] = (struct dx11_shader_desc) {
|
|
.kind = DX11_SHADER_KIND_TEXTURE,
|
|
.name_cstr = "shaders_dx11/texture.hlsl"
|
|
};
|
|
|
|
/* Grid shader layout */
|
|
G.shader_info[DX11_SHADER_KIND_GRID] = (struct dx11_shader_desc) {
|
|
.kind = DX11_SHADER_KIND_GRID,
|
|
.name_cstr = "shaders_dx11/grid.hlsl"
|
|
};
|
|
|
|
/* Test shader layout */
|
|
G.shader_info[DX11_SHADER_KIND_TEST] = (struct dx11_shader_desc) {
|
|
.kind = DX11_SHADER_KIND_TEST,
|
|
.name_cstr = "shaders_dx11/test.hlsl"
|
|
};
|
|
|
|
#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_DX11_SHADER_KINDS; ++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("Dx11 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 function returns error string 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 arena_temp scratch = scratch_begin(arena);
|
|
struct string error_str = ZI;
|
|
i64 start_ns = sys_time_ns();
|
|
|
|
struct string shader_name = string_from_cstr_no_limit(shader_desc->name_cstr);
|
|
|
|
shader->kind = shader_desc->kind;
|
|
#if RESOURCE_RELOADING
|
|
shader_reset_includes(shader_desc);
|
|
#endif
|
|
|
|
struct dx11_include_handler include_handler = dx11_include_handler_alloc(shader);
|
|
|
|
u32 flags = 0;
|
|
#if DX11_SHADER_DEBUG
|
|
flags |= D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_ENABLE_STRICTNESS;
|
|
#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? */
|
|
struct string friendly_name = string_cat(scratch.arena, LIT("res/"), shader_name);
|
|
char *friendly_name_cstr = cstr_from_string(scratch.arena, friendly_name);
|
|
HRESULT hr = D3DCompile(shader_src.text, shader_src.len, friendly_name_cstr, 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, friendly_name_cstr, 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 */
|
|
if (elem_count > 0) {
|
|
HRESULT hr = ID3D11Device_CreateInputLayout(G.dev, shader_desc->input_layout_desc, elem_count, ID3D10Blob_GetBufferPointer(vs_blob), ID3D10Blob_GetBufferSize(vs_blob), &shader->input_layout);
|
|
if (!SUCCEEDED(hr)) {
|
|
success = false;
|
|
error_str = LIT("Failed to create input layout");
|
|
}
|
|
}
|
|
} else {
|
|
success = false;
|
|
}
|
|
|
|
if (!success || error_blob) {
|
|
if (error_str.len <= 0) {
|
|
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 (success) {
|
|
logf_success("Finished compiling shader \"%F\" in %F seconds", FMT_STR(shader_name), FMT_FLOAT(SECONDS_FROM_NS(sys_time_ns() - start_ns)));
|
|
}
|
|
|
|
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 arena_temp 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%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) {
|
|
log_error(error_msg);
|
|
if (!old_shader->valid) {
|
|
/* If shader failed to load and no previous shader exists, show message box in case in game console can't be rendered */
|
|
sys_message_box(SYS_MESSAGE_BOX_KIND_WARNING, 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_debug("Shader source file \"%F\" has changed", FMT_STR(name));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* ========================== *
|
|
* Texture
|
|
* ========================== */
|
|
|
|
INTERNAL enum DXGI_FORMAT dx11_format_from_gpu_format(enum gpu_texture_format gpu_format)
|
|
{
|
|
LOCAL_PERSIST const enum DXGI_FORMAT dx11_formats[NUM_GPU_TEXTURE_FORMATS] = {
|
|
[GPU_TEXTURE_FORMAT_R8G8B8A8_UNORM] = DXGI_FORMAT_R8G8B8A8_UNORM,
|
|
[GPU_TEXTURE_FORMAT_R8G8B8A8_UNORM_SRGB] = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB
|
|
};
|
|
enum DXGI_FORMAT res = DXGI_FORMAT_UNKNOWN;
|
|
if ((u32)gpu_format < ARRAY_COUNT(dx11_formats)) {
|
|
res = dx11_formats[gpu_format];
|
|
}
|
|
return res;
|
|
}
|
|
|
|
INTERNAL u32 pixel_size_from_dx11_format(enum DXGI_FORMAT dx11_format)
|
|
{
|
|
LOCAL_PERSIST const u32 pixel_sizes[] = {
|
|
[DXGI_FORMAT_R8G8B8A8_UNORM] = 4,
|
|
[DXGI_FORMAT_R8G8B8A8_UNORM_SRGB] = 4
|
|
};
|
|
u32 res = 0;
|
|
if ((u32)dx11_format < ARRAY_COUNT(pixel_sizes)) {
|
|
res = pixel_sizes[dx11_format];
|
|
}
|
|
return res;
|
|
}
|
|
|
|
INTERNAL struct dx11_texture *dx11_texture_alloc(enum DXGI_FORMAT format, u32 flags, struct v2i32 size, void *initial_data)
|
|
{
|
|
struct dx11_texture *t = NULL;
|
|
{
|
|
struct sys_lock lock = sys_mutex_lock_e(&G.textures_mutex);
|
|
if (G.first_free_texture) {
|
|
t = G.first_free_texture;
|
|
G.first_free_texture = t->next_free;
|
|
} else {
|
|
t = arena_push_no_zero(&G.textures_arena, struct dx11_texture);
|
|
}
|
|
sys_mutex_unlock(&lock);
|
|
}
|
|
MEMZERO_STRUCT(t);
|
|
t->header.kind = DX11_HANDLE_KIND_TEXTURE;
|
|
|
|
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 = flags;
|
|
desc.Format = format;
|
|
|
|
/* Create texture */
|
|
ID3D11Texture2D *texture = NULL;
|
|
if (initial_data) {
|
|
u32 pixel_size = pixel_size_from_dx11_format(format);
|
|
D3D11_SUBRESOURCE_DATA subresource_data = { .pSysMem = initial_data, .SysMemPitch = size.x * 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;
|
|
|
|
/* Create SRV */
|
|
if (t->texture && (flags & D3D11_BIND_SHADER_RESOURCE)) {
|
|
D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc = ZI;
|
|
srv_desc.Format = desc.Format;
|
|
srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
|
|
srv_desc.Texture2D.MipLevels = desc.MipLevels;
|
|
ID3D11Device_CreateShaderResourceView(G.dev, (ID3D11Resource *)texture, &srv_desc, &t->srv);
|
|
}
|
|
|
|
/* Create RTV */
|
|
if (t->texture && (flags & D3D11_BIND_RENDER_TARGET)) {
|
|
ID3D11Device_CreateRenderTargetView(G.dev, (ID3D11Resource *)t->texture, NULL, &t->rtv);
|
|
}
|
|
|
|
t->size = size;
|
|
return t;
|
|
}
|
|
|
|
INTERNAL void dx11_texture_release(struct dx11_texture *t)
|
|
{
|
|
{
|
|
struct sys_lock lock = sys_mutex_lock_e(&G.textures_mutex);
|
|
t->next_free = G.first_free_texture;
|
|
G.first_free_texture = t;
|
|
sys_mutex_unlock(&lock);
|
|
}
|
|
if (t->rtv) {
|
|
ID3D11RenderTargetView_Release(t->rtv);
|
|
}
|
|
if (t->srv) {
|
|
ID3D11ShaderResourceView_Release(t->srv);
|
|
}
|
|
if (t->texture) {
|
|
ID3D11Texture2D_Release(t->texture);
|
|
}
|
|
}
|
|
|
|
struct gpu_handle gpu_texture_alloc(enum gpu_texture_format format, u32 flags, struct v2i32 size, void *initial_data)
|
|
{
|
|
__prof;
|
|
struct gpu_handle res = ZI;
|
|
|
|
/* Convert format to dx11 format */
|
|
enum DXGI_FORMAT dx11_format = dx11_format_from_gpu_format(format);
|
|
if (dx11_format == DXGI_FORMAT_UNKNOWN) {
|
|
/* Unknown format */
|
|
ASSERT(false);
|
|
sys_panic(LIT("Unknown dx11 texture format during texture allocation"));
|
|
}
|
|
|
|
/* Convert flags to dx11 flags */
|
|
u32 dx11_flags = D3D11_BIND_SHADER_RESOURCE;
|
|
if (flags & GPU_TEXTURE_FLAG_TARGETABLE) {
|
|
dx11_flags |= D3D11_BIND_RENDER_TARGET;
|
|
}
|
|
|
|
struct dx11_texture *t = dx11_texture_alloc(dx11_format, dx11_flags, size, initial_data);
|
|
res.v = (u64)t;
|
|
return res;
|
|
}
|
|
|
|
void gpu_texture_clear(struct gpu_handle target_texture, u32 clear_color)
|
|
{
|
|
__prof;
|
|
struct dx11_texture *t = (struct dx11_texture *)target_texture.v;
|
|
if (t->rtv) {
|
|
__profscope_dx11(G.profiling_ctx, Clear, RGB32_F(0.5, 0.1, 0.1));
|
|
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, t->rtv, fill);
|
|
}
|
|
}
|
|
|
|
struct v2i32 gpu_texture_get_size(struct gpu_handle texture)
|
|
{
|
|
return ((struct dx11_texture *)texture.v)->size;
|
|
}
|
|
|
|
/* ========================== *
|
|
* Dx11 buffer
|
|
* ========================== */
|
|
|
|
/* TODO: Buffer caching based on size */
|
|
|
|
/* NOTE: If initial_data is not provided, then ByteWidth will be ignored (set dynamically when buffer grows) */
|
|
INTERNAL struct dx11_buffer *dx11_buffer_alloc(struct D3D11_BUFFER_DESC desc, D3D11_SUBRESOURCE_DATA *initial_data)
|
|
{
|
|
__prof;
|
|
struct dx11_buffer *buffer = NULL;
|
|
{
|
|
struct arena cpu_buffer_arena = ZI;
|
|
{
|
|
struct sys_lock lock = sys_mutex_lock_e(&G.buffers_mutex);
|
|
if (G.first_free_buffer) {
|
|
buffer = G.first_free_buffer;
|
|
G.first_free_buffer = buffer->next_free;
|
|
cpu_buffer_arena = buffer->cpu_buffer_arena;
|
|
} else {
|
|
buffer = arena_push_no_zero(&G.buffers_arena, struct dx11_buffer);
|
|
}
|
|
sys_mutex_unlock(&lock);
|
|
}
|
|
MEMZERO_STRUCT(buffer);
|
|
if (!cpu_buffer_arena.base) {
|
|
cpu_buffer_arena = arena_alloc(GIGABYTE(64));
|
|
}
|
|
buffer->cpu_buffer_arena = cpu_buffer_arena;
|
|
}
|
|
buffer->desc = desc;
|
|
buffer->cpu_buffer = arena_push_dry(&buffer->cpu_buffer_arena, u8);
|
|
|
|
if (desc.BindFlags & D3D11_BIND_SHADER_RESOURCE) {
|
|
ASSERT(desc.StructureByteStride != 0); /* Must provide stride for shader resource buffers */
|
|
}
|
|
|
|
if (initial_data) {
|
|
ASSERT(desc.ByteWidth > 0); /* Must provide size of subresource in desc */
|
|
buffer->gpu_buffer_capacity = desc.ByteWidth;
|
|
ID3D11Device_CreateBuffer(G.dev, &buffer->desc, initial_data, &buffer->gpu_buffer);
|
|
|
|
/* Create SRV */
|
|
if (desc.BindFlags & D3D11_BIND_SHADER_RESOURCE) {
|
|
D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc = ZI;
|
|
srv_desc.Format = DXGI_FORMAT_UNKNOWN;
|
|
srv_desc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
|
|
srv_desc.Buffer.NumElements = buffer->gpu_buffer_capacity / buffer->desc.StructureByteStride;
|
|
ID3D11Device_CreateShaderResourceView(G.dev, (ID3D11Resource *)buffer->gpu_buffer, &srv_desc, &buffer->srv);
|
|
}
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
/* TODO */
|
|
#if 0
|
|
INTERNAL void dx11_buffer_release(struct dx11_buffer *buffer)
|
|
{
|
|
(UNUSED)buffer;
|
|
ASSERT(false);
|
|
}
|
|
#endif
|
|
|
|
INTERNAL void *dx11_buffer_push(struct dx11_buffer *buffer, u64 size)
|
|
{
|
|
void *data = arena_push_array_no_zero(&buffer->cpu_buffer_arena, u8, size);
|
|
return data;
|
|
}
|
|
|
|
INTERNAL void dx11_buffer_submit(struct dx11_buffer *buffer)
|
|
{
|
|
__prof;
|
|
|
|
/* Grow GPU buffer if necessary */
|
|
u64 cpu_data_size = buffer->cpu_buffer_arena.pos;
|
|
if (cpu_data_size > buffer->gpu_buffer_capacity) {
|
|
|
|
if (buffer->srv) {
|
|
ID3D11ShaderResourceView_Release(buffer->srv);
|
|
}
|
|
if (buffer->gpu_buffer) {
|
|
ID3D11Buffer_Release(buffer->gpu_buffer);
|
|
}
|
|
|
|
/* Create buffer */
|
|
/* FIXME: Cap to prevent infinite loop */
|
|
u64 new_capacity = buffer->gpu_buffer_capacity * 2;
|
|
if (new_capacity == 0) {
|
|
if (buffer->desc.StructureByteStride != 0) {
|
|
new_capacity = buffer->desc.StructureByteStride;
|
|
} else {
|
|
new_capacity = KILOBYTE(64);
|
|
}
|
|
}
|
|
while (new_capacity < cpu_data_size) {
|
|
new_capacity *= 2;
|
|
}
|
|
buffer->desc.ByteWidth = new_capacity;
|
|
buffer->gpu_buffer_capacity = new_capacity;
|
|
ID3D11Device_CreateBuffer(G.dev, &buffer->desc, NULL, &buffer->gpu_buffer);
|
|
|
|
/* Create SRV */
|
|
if (buffer->desc.BindFlags & D3D11_BIND_SHADER_RESOURCE) {
|
|
D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc = ZI;
|
|
srv_desc.Format = DXGI_FORMAT_UNKNOWN;
|
|
srv_desc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
|
|
srv_desc.Buffer.NumElements = new_capacity / buffer->desc.StructureByteStride;
|
|
ID3D11Device_CreateShaderResourceView(G.dev, (ID3D11Resource *)buffer->gpu_buffer, &srv_desc, &buffer->srv);
|
|
}
|
|
}
|
|
|
|
/* Upload cpu data */
|
|
if (buffer->gpu_buffer) {
|
|
D3D11_MAPPED_SUBRESOURCE subres;
|
|
ID3D11DeviceContext_Map(G.devcon, (ID3D11Resource *)buffer->gpu_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &subres);
|
|
MEMCPY(subres.pData, buffer->cpu_buffer, cpu_data_size);
|
|
ID3D11DeviceContext_Unmap(G.devcon, (ID3D11Resource *)buffer->gpu_buffer, 0);
|
|
}
|
|
|
|
/* Reset cpu data */
|
|
arena_reset(&buffer->cpu_buffer_arena);
|
|
}
|
|
|
|
/* ========================== *
|
|
* Plan
|
|
* ========================== */
|
|
|
|
struct gpu_handle gpu_plan_alloc(void)
|
|
{
|
|
__prof;
|
|
struct dx11_plan *plan = NULL;
|
|
{
|
|
struct arena cpu_cmds_arena = ZI;
|
|
struct arena gpu_cmds_arena = ZI;
|
|
{
|
|
struct sys_lock lock = sys_mutex_lock_e(&G.plans_mutex);
|
|
if (G.first_free_plan) {
|
|
plan = G.first_free_plan;
|
|
G.first_free_plan = plan->next_free;
|
|
cpu_cmds_arena = plan->cpu_cmds_arena;
|
|
gpu_cmds_arena = plan->gpu_cmds_arena;
|
|
} else {
|
|
plan = arena_push_no_zero(&G.plans_arena, struct dx11_plan);
|
|
}
|
|
sys_mutex_unlock(&lock);
|
|
}
|
|
MEMZERO_STRUCT(plan);
|
|
if (!cpu_cmds_arena.base) {
|
|
cpu_cmds_arena = arena_alloc(GIGABYTE(64));
|
|
}
|
|
if (!gpu_cmds_arena.base) {
|
|
gpu_cmds_arena = arena_alloc(GIGABYTE(64));
|
|
}
|
|
plan->cpu_cmds_arena = cpu_cmds_arena;
|
|
plan->gpu_cmds_arena = gpu_cmds_arena;
|
|
arena_reset(&plan->cpu_cmds_arena);
|
|
arena_reset(&plan->gpu_cmds_arena);
|
|
}
|
|
plan->header.kind = DX11_HANDLE_KIND_PLAN;
|
|
|
|
/* Desc template */
|
|
const D3D11_BUFFER_DESC structured_buffer_desc = {
|
|
.Usage = D3D11_USAGE_DYNAMIC,
|
|
.BindFlags = D3D11_BIND_SHADER_RESOURCE,
|
|
.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE,
|
|
.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED
|
|
};
|
|
|
|
/* Allocate buffers */
|
|
{
|
|
/* Mesh buffers */
|
|
{
|
|
D3D11_BUFFER_DESC vdesc = ZI;
|
|
vdesc.Usage = D3D11_USAGE_DYNAMIC;
|
|
vdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
|
|
vdesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
|
|
|
/* Quad index buffer */
|
|
D3D11_BUFFER_DESC idesc = ZI;
|
|
idesc.Usage = D3D11_USAGE_DYNAMIC;
|
|
idesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
|
|
idesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
|
|
|
|
plan->cmd_buffers.mesh.vertex_buffer = dx11_buffer_alloc(vdesc, NULL);
|
|
plan->cmd_buffers.mesh.index_buffer = dx11_buffer_alloc(idesc, NULL);
|
|
}
|
|
|
|
/* Texture buffers */
|
|
{
|
|
struct D3D11_BUFFER_DESC desc = structured_buffer_desc;
|
|
desc.StructureByteStride = sizeof(struct dx11_texture_instance);
|
|
plan->cmd_buffers.texture.instance_buffer = dx11_buffer_alloc(desc, NULL);
|
|
}
|
|
|
|
/* Grid buffers */
|
|
{
|
|
struct D3D11_BUFFER_DESC desc = structured_buffer_desc;
|
|
desc.StructureByteStride = sizeof(struct dx11_grid_instance);
|
|
plan->cmd_buffers.grid.instance_buffer = dx11_buffer_alloc(desc, NULL);
|
|
}
|
|
|
|
/* Test buffers */
|
|
{
|
|
struct D3D11_BUFFER_DESC desc = structured_buffer_desc;
|
|
desc.StructureByteStride = sizeof(struct dx11_test_instance);
|
|
plan->cmd_buffers.test.instance_buffer = dx11_buffer_alloc(desc, NULL);
|
|
}
|
|
}
|
|
|
|
struct gpu_handle res = ZI;
|
|
res.v = (u64)plan;
|
|
return res;
|
|
}
|
|
|
|
void gpu_push_cmd(struct gpu_handle gpu_plan, struct gpu_cmd_params params)
|
|
{
|
|
__prof;
|
|
struct dx11_plan *plan = (struct dx11_plan *)gpu_plan.v;
|
|
|
|
switch (params.kind) {
|
|
default:
|
|
{
|
|
/* Unknown cmd kind */
|
|
ASSERT(false);
|
|
} break;
|
|
|
|
case GPU_CMD_KIND_DRAW_MESH:
|
|
{
|
|
struct dx11_cmd *cmd = plan->cpu_last_cmd;
|
|
if (cmd && cmd->kind != params.kind) {
|
|
/* Cannot batch */
|
|
cmd = NULL;
|
|
}
|
|
|
|
/* Start new cmd */
|
|
if (!cmd) {
|
|
/* TODO: Better count method */
|
|
cmd = arena_push(&plan->cpu_cmds_arena, struct dx11_cmd);
|
|
cmd->kind = params.kind;
|
|
cmd->mesh.vertex_offset = (plan->cmd_buffers.mesh.vertex_buffer->cpu_buffer_arena.pos / sizeof(struct dx11_mesh_vertex));
|
|
cmd->mesh.index_offset = (plan->cmd_buffers.mesh.index_buffer->cpu_buffer_arena.pos / sizeof(u32));
|
|
if (plan->cpu_last_cmd) {
|
|
plan->cpu_last_cmd->next = cmd;
|
|
} else {
|
|
plan->cpu_first_cmd = cmd;
|
|
}
|
|
plan->cpu_last_cmd = cmd;
|
|
}
|
|
|
|
/* Push vertices */
|
|
u64 vertex_count = params.mesh.vertices.count;
|
|
u64 start_index = cmd->mesh.vertex_count;
|
|
cmd->mesh.vertex_count += vertex_count;
|
|
struct dx11_mesh_vertex *verts = dx11_buffer_push(plan->cmd_buffers.mesh.vertex_buffer, sizeof(struct dx11_mesh_vertex) * vertex_count);
|
|
for (u64 i = 0; i < vertex_count; ++i) {
|
|
struct dx11_mesh_vertex *vert = &verts[i];
|
|
vert->pos = params.mesh.vertices.points[i];
|
|
vert->color_srgb = params.mesh.color;
|
|
}
|
|
|
|
/* Push indices */
|
|
u64 index_count = params.mesh.indices.count;
|
|
u32 *indices = dx11_buffer_push(plan->cmd_buffers.mesh.index_buffer, sizeof(u32) * index_count);
|
|
cmd->mesh.index_count += index_count;
|
|
for (u64 i = 0; i < index_count; ++i) {
|
|
indices[i] = start_index + params.mesh.indices.indices[i];
|
|
}
|
|
} break;
|
|
|
|
case GPU_CMD_KIND_DRAW_TEXTURE:
|
|
{
|
|
struct dx11_cmd *cmd = plan->cpu_last_cmd;
|
|
if (cmd &&
|
|
((cmd->kind != params.kind) ||
|
|
(cmd->texture.sprite.hash != params.texture.sprite.hash) ||
|
|
(cmd->texture.texture.v != params.texture.texture.v))) {
|
|
/* Cannot batch */
|
|
cmd = NULL;
|
|
}
|
|
|
|
/* Start new cmd */
|
|
if (!cmd) {
|
|
/* TODO: Better count method */
|
|
cmd = arena_push(&plan->cpu_cmds_arena, struct dx11_cmd);
|
|
cmd->kind = params.kind;
|
|
cmd->texture.sprite = params.texture.sprite;
|
|
cmd->texture.texture = params.texture.texture;
|
|
cmd->texture.instance_offset = (plan->cmd_buffers.texture.instance_buffer->cpu_buffer_arena.pos / sizeof(struct dx11_texture_instance));
|
|
if (plan->cpu_last_cmd) {
|
|
plan->cpu_last_cmd->next = cmd;
|
|
} else {
|
|
plan->cpu_first_cmd = cmd;
|
|
}
|
|
plan->cpu_last_cmd = cmd;
|
|
}
|
|
|
|
/* Push instance data */
|
|
++cmd->texture.instance_count;
|
|
struct dx11_texture_instance *instance = dx11_buffer_push(plan->cmd_buffers.texture.instance_buffer, sizeof(struct dx11_texture_instance));
|
|
instance->xf = params.texture.xf;
|
|
instance->uv0 = params.texture.clip.p0;
|
|
instance->uv1 = params.texture.clip.p1;
|
|
instance->tint_srgb = params.texture.tint;
|
|
instance->emittance = params.texture.emittance;
|
|
} break;
|
|
|
|
case GPU_CMD_KIND_DRAW_GRID:
|
|
{
|
|
struct dx11_cmd *cmd = plan->cpu_last_cmd;
|
|
if (cmd && cmd->kind != params.kind) {
|
|
/* Cannot batch */
|
|
cmd = NULL;
|
|
}
|
|
|
|
/* Start new cmd */
|
|
if (!cmd) {
|
|
/* TODO: Better count method */
|
|
cmd = arena_push(&plan->cpu_cmds_arena, struct dx11_cmd);
|
|
cmd->kind = params.kind;
|
|
cmd->grid.instance_offset = (plan->cmd_buffers.grid.instance_buffer->cpu_buffer_arena.pos / sizeof(struct dx11_grid_instance));
|
|
if (plan->cpu_last_cmd) {
|
|
plan->cpu_last_cmd->next = cmd;
|
|
} else {
|
|
plan->cpu_first_cmd = cmd;
|
|
}
|
|
plan->cpu_last_cmd = cmd;
|
|
}
|
|
|
|
/* Push instance data */
|
|
++cmd->grid.instance_count;
|
|
struct dx11_grid_instance *instance = dx11_buffer_push(plan->cmd_buffers.grid.instance_buffer, sizeof(struct dx11_grid_instance));
|
|
instance->xf = params.grid.xf;
|
|
instance->line_thickness = params.grid.line_thickness;
|
|
instance->line_spacing = params.grid.line_spacing;
|
|
instance->offset = params.grid.offset;
|
|
instance->bg0_srgb = params.grid.bg0_color;
|
|
instance->bg1_srgb = params.grid.bg1_color;
|
|
instance->line_srgb = params.grid.line_color;
|
|
instance->x_srgb = params.grid.x_color;
|
|
instance->y_srgb = params.grid.y_color;
|
|
} break;
|
|
|
|
case GPU_CMD_KIND_TEST:
|
|
{
|
|
struct dx11_cmd *cmd = arena_push(&plan->cpu_cmds_arena, struct dx11_cmd);
|
|
cmd->kind = params.kind;
|
|
cmd->test.instance_offset = (plan->cmd_buffers.test.instance_buffer->cpu_buffer_arena.pos / sizeof(struct dx11_test_instance));
|
|
if (plan->cpu_last_cmd) {
|
|
plan->cpu_last_cmd->next = cmd;
|
|
} else {
|
|
plan->cpu_first_cmd = cmd;
|
|
}
|
|
plan->cpu_last_cmd = cmd;
|
|
|
|
/* Push instance data */
|
|
++cmd->test.instance_count;
|
|
struct dx11_test_instance *instance = dx11_buffer_push(plan->cmd_buffers.test.instance_buffer, sizeof(struct dx11_test_instance));
|
|
instance->xf = params.test.xf;
|
|
} break;
|
|
}
|
|
}
|
|
|
|
/* ========================== *
|
|
* Dispatch
|
|
* ========================== */
|
|
|
|
enum dx11_unbind_flags {
|
|
DX11_UNBIND_NONE = 0,
|
|
DX11_UNBIND_VS = (1 << 0),
|
|
DX11_UNBIND_PS = (1 << 1),
|
|
DX11_UNBIND_IA = (1 << 2),
|
|
DX11_UNBIND_CBUFF = (1 << 3),
|
|
DX11_UNBIND_VBUFF = (1 << 4),
|
|
DX11_UNBIND_IBUFF = (1 << 5),
|
|
DX11_UNBIND_SRV = (1 << 6),
|
|
DX11_UNBIND_RTV = (1 << 7)
|
|
};
|
|
|
|
INTERNAL void dx11_unbind(u32 flags)
|
|
{
|
|
__prof;
|
|
ID3D11RenderTargetView *null_rtvs[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
|
|
ID3D11ShaderResourceView *null_srvs[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
|
|
ID3D11Buffer *null_buffers[8] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
|
|
ID3D11InputLayout *null_ia = NULL;
|
|
ID3D11VertexShader *null_vs = NULL;
|
|
ID3D11PixelShader *null_ps = NULL;
|
|
u32 zero = 0;
|
|
|
|
if (flags & DX11_UNBIND_RTV) {
|
|
ID3D11DeviceContext_OMSetRenderTargets(G.devcon, 8, null_rtvs, NULL);
|
|
}
|
|
|
|
if (flags & DX11_UNBIND_SRV) {
|
|
if (flags & DX11_UNBIND_VS) {
|
|
ID3D11DeviceContext_VSSetShaderResources(G.devcon, 0, 8, null_srvs);
|
|
}
|
|
if (flags & DX11_UNBIND_PS) {
|
|
ID3D11DeviceContext_PSSetShaderResources(G.devcon, 0, 8, null_srvs);
|
|
}
|
|
}
|
|
|
|
if (flags & DX11_UNBIND_VBUFF) {
|
|
ID3D11DeviceContext_IASetVertexBuffers(G.devcon, 0, 1, null_buffers, &zero, &zero);
|
|
}
|
|
|
|
if (flags & DX11_UNBIND_IBUFF) {
|
|
ID3D11DeviceContext_IASetIndexBuffer(G.devcon, null_buffers[0], DXGI_FORMAT_R16_UINT, zero);
|
|
}
|
|
|
|
if (flags & DX11_UNBIND_IA) {
|
|
ID3D11DeviceContext_IASetInputLayout(G.devcon, null_ia);
|
|
}
|
|
|
|
if (flags & DX11_UNBIND_CBUFF) {
|
|
if (flags & DX11_UNBIND_VS) {
|
|
ID3D11DeviceContext_VSSetConstantBuffers(G.devcon, 0, 8, null_buffers);
|
|
}
|
|
if (flags & DX11_UNBIND_PS) {
|
|
ID3D11DeviceContext_PSSetConstantBuffers(G.devcon, 0, 8, null_buffers);
|
|
}
|
|
}
|
|
|
|
if (flags & DX11_UNBIND_VS) {
|
|
ID3D11DeviceContext_VSSetShader(G.devcon, null_vs, 0, 0);
|
|
}
|
|
|
|
if (flags & DX11_UNBIND_PS) {
|
|
ID3D11DeviceContext_PSSetShader(G.devcon, null_ps, 0, 0);
|
|
}
|
|
}
|
|
|
|
/* TODO: Lock resources during dispatch */
|
|
void gpu_dispatch(struct gpu_dispatch_params params)
|
|
{
|
|
__prof;
|
|
__profscope_dx11(G.profiling_ctx, Dispatch, RGB32_F(0.5, 0.2, 0.2));
|
|
struct dx11_plan *plan = (struct dx11_plan *)params.plan.v;
|
|
|
|
{
|
|
__profscope(Submit buffers);
|
|
|
|
/* Swap cmd plans */
|
|
struct arena swp_arena = plan->gpu_cmds_arena;
|
|
plan->gpu_cmds_arena = plan->cpu_cmds_arena;
|
|
plan->gpu_first_cmd = plan->cpu_first_cmd;
|
|
plan->gpu_last_cmd = plan->cpu_last_cmd;
|
|
|
|
/* Reset cpu cmds */
|
|
plan->cpu_cmds_arena = swp_arena;
|
|
plan->cpu_first_cmd = NULL;
|
|
plan->cpu_last_cmd = NULL;
|
|
arena_reset(&plan->cpu_cmds_arena);
|
|
|
|
/* Submit mesh buffers */
|
|
dx11_buffer_submit(plan->cmd_buffers.mesh.vertex_buffer);
|
|
dx11_buffer_submit(plan->cmd_buffers.mesh.index_buffer);
|
|
|
|
/* Submit texture buffers */
|
|
dx11_buffer_submit(plan->cmd_buffers.texture.instance_buffer);
|
|
|
|
/* Submit grid buffers */
|
|
dx11_buffer_submit(plan->cmd_buffers.grid.instance_buffer);
|
|
|
|
/* Submit test buffers */
|
|
dx11_buffer_submit(plan->cmd_buffers.test.instance_buffer);
|
|
}
|
|
|
|
struct sprite_scope *sprite_scope = sprite_scope_begin();
|
|
|
|
struct rect viewport = params.draw_target_viewport;
|
|
|
|
/* Set viewport */
|
|
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);
|
|
|
|
struct dx11_texture *final_tex = (struct dx11_texture *)params.draw_target.v;
|
|
|
|
/* Allocate constant buffer */
|
|
struct dx11_buffer *constant_buffer = plan->constant_buffer;
|
|
if (!constant_buffer) {
|
|
const D3D11_BUFFER_DESC desc = {
|
|
.Usage = D3D11_USAGE_DYNAMIC,
|
|
.BindFlags = D3D11_BIND_CONSTANT_BUFFER,
|
|
.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE
|
|
};
|
|
constant_buffer = dx11_buffer_alloc(desc, NULL);
|
|
plan->constant_buffer = constant_buffer;
|
|
}
|
|
|
|
/* Regular pass */
|
|
struct mat4x4 vp_matrix = calculate_vp(params.draw_target_view, viewport.width, viewport.height);
|
|
{
|
|
__profscope(Regular pass);
|
|
__profscope_dx11(G.profiling_ctx, Regular pass, RGB32_F(0.2, 0.5, 0.5));
|
|
for (struct dx11_cmd *cmd = plan->gpu_first_cmd; cmd; cmd = cmd->next) {
|
|
enum gpu_cmd_kind cmd_kind = cmd->kind;
|
|
|
|
switch (cmd_kind) {
|
|
default:
|
|
{
|
|
/* Unknown cmd kind */
|
|
ASSERT(false);
|
|
} break;
|
|
|
|
case GPU_CMD_KIND_DRAW_MESH:
|
|
{
|
|
__profscope(Draw mesh);
|
|
__profscope_dx11(G.profiling_ctx, Draw mesh, RGB32_F(0.5, 0.2, 0.2));
|
|
struct dx11_shader *shader = &G.shaders[DX11_SHADER_KIND_MESH];
|
|
if (shader->valid) {
|
|
struct dx11_buffer *vertex_buffer = plan->cmd_buffers.mesh.vertex_buffer;
|
|
struct dx11_buffer *index_buffer = plan->cmd_buffers.mesh.index_buffer;
|
|
|
|
u32 vertex_offset = cmd->mesh.vertex_offset;
|
|
u32 index_offset = cmd->mesh.index_offset;
|
|
u32 index_count = cmd->mesh.index_count;
|
|
|
|
/* Bind shader */
|
|
ID3D11DeviceContext_VSSetShader(G.devcon, shader->vs, 0, 0);
|
|
ID3D11DeviceContext_PSSetShader(G.devcon, shader->ps, 0, 0);
|
|
ID3D11DeviceContext_IASetInputLayout(G.devcon, shader->input_layout);
|
|
|
|
/* Fill & bind constant buffer */
|
|
{
|
|
struct dx11_mesh_uniform *uniform = dx11_buffer_push(constant_buffer, sizeof(struct dx11_mesh_uniform));
|
|
uniform->vp = vp_matrix;
|
|
dx11_buffer_submit(constant_buffer);
|
|
}
|
|
ID3D11DeviceContext_VSSetConstantBuffers(G.devcon, 0, 1, &constant_buffer->gpu_buffer);
|
|
ID3D11DeviceContext_PSSetConstantBuffers(G.devcon, 0, 1, &constant_buffer->gpu_buffer);
|
|
|
|
/* Bind vertex buffer */
|
|
u32 zero = 0;
|
|
u32 stride = sizeof(struct dx11_mesh_vertex);
|
|
ID3D11DeviceContext_IASetVertexBuffers(G.devcon, 0, 1, &vertex_buffer->gpu_buffer, &stride, &zero);
|
|
ID3D11DeviceContext_IASetIndexBuffer(G.devcon, index_buffer->gpu_buffer, DXGI_FORMAT_R32_UINT, zero);
|
|
|
|
/* Bind RTVs */
|
|
ID3D11RenderTargetView *rtvs[] = { final_tex->rtv };
|
|
ID3D11DeviceContext_OMSetRenderTargets(G.devcon, ARRAY_COUNT(rtvs), rtvs, NULL);
|
|
|
|
/* Draw */
|
|
ID3D11DeviceContext_IASetPrimitiveTopology(G.devcon, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
ID3D11DeviceContext_DrawIndexed(G.devcon, index_count, index_offset, vertex_offset);
|
|
|
|
/* Unbind */
|
|
dx11_unbind(DX11_UNBIND_VS | DX11_UNBIND_PS | DX11_UNBIND_IA | DX11_UNBIND_CBUFF | DX11_UNBIND_VBUFF | DX11_UNBIND_IBUFF | DX11_UNBIND_RTV);
|
|
}
|
|
} break;
|
|
|
|
case GPU_CMD_KIND_DRAW_TEXTURE:
|
|
{
|
|
__profscope(Draw texture);
|
|
__profscope_dx11(G.profiling_ctx, Draw texture, RGB32_F(0.2, 0.5, 0.2));
|
|
struct dx11_shader *shader = &G.shaders[DX11_SHADER_KIND_TEXTURE];
|
|
if (shader->valid) {
|
|
struct dx11_texture *texture = NULL;
|
|
if (cmd->texture.texture.v) {
|
|
/* Load texture if handle is set */
|
|
texture = (struct dx11_texture *)cmd->texture.texture.v;
|
|
} else if (cmd->texture.sprite.hash) {
|
|
/* Otherwise load sprite */
|
|
struct sprite_texture *sprite_texture = sprite_texture_from_tag_async(sprite_scope, cmd->texture.sprite);
|
|
if (sprite_texture->loaded) {
|
|
texture = (struct dx11_texture *)sprite_texture->texture.v;
|
|
}
|
|
}
|
|
|
|
if (texture && texture->srv) {
|
|
struct dx11_buffer *instance_buffer = plan->cmd_buffers.texture.instance_buffer;
|
|
u32 instance_offset = cmd->texture.instance_offset;
|
|
u32 instance_count = cmd->texture.instance_count;
|
|
|
|
/* Bind shader */
|
|
ID3D11DeviceContext_VSSetShader(G.devcon, shader->vs, 0, 0);
|
|
ID3D11DeviceContext_PSSetShader(G.devcon, shader->ps, 0, 0);
|
|
|
|
/* Fill & bind constant buffer */
|
|
{
|
|
struct dx11_texture_uniform *uniform = dx11_buffer_push(constant_buffer, sizeof(struct dx11_texture_uniform));
|
|
uniform->vp = vp_matrix;
|
|
uniform->instance_offset = instance_offset;
|
|
dx11_buffer_submit(constant_buffer);
|
|
}
|
|
ID3D11DeviceContext_VSSetConstantBuffers(G.devcon, 0, 1, &constant_buffer->gpu_buffer);
|
|
ID3D11DeviceContext_PSSetConstantBuffers(G.devcon, 0, 1, &constant_buffer->gpu_buffer);
|
|
|
|
/* Bind dummy vertex buffer */
|
|
u32 zero = 0;
|
|
ID3D11DeviceContext_IASetVertexBuffers(G.devcon, 0, 1, &G.dummy_vertex_buffer->gpu_buffer, &zero, &zero);
|
|
ID3D11DeviceContext_IASetIndexBuffer(G.devcon, G.quad_index_buffer->gpu_buffer, DXGI_FORMAT_R16_UINT, zero);
|
|
|
|
/* Bind SRVs */
|
|
ID3D11ShaderResourceView *srvs[] = { instance_buffer->srv, texture->srv };
|
|
ID3D11DeviceContext_VSSetShaderResources(G.devcon, 0, ARRAY_COUNT(srvs), srvs);
|
|
ID3D11DeviceContext_PSSetShaderResources(G.devcon, 0, ARRAY_COUNT(srvs), srvs);
|
|
|
|
/* Bind RTVs */
|
|
ID3D11RenderTargetView *rtvs[] = { final_tex->rtv };
|
|
ID3D11DeviceContext_OMSetRenderTargets(G.devcon, ARRAY_COUNT(rtvs), rtvs, NULL);
|
|
|
|
/* Draw */
|
|
ID3D11DeviceContext_IASetPrimitiveTopology(G.devcon, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
/* FIXME: Set StartInstanceLocation parameter here rather than passing instance_offset into CBV */
|
|
ID3D11DeviceContext_DrawIndexedInstanced(G.devcon, 6, instance_count, 0, 0, 0);
|
|
|
|
/* Unbind */
|
|
dx11_unbind(DX11_UNBIND_VS | DX11_UNBIND_PS | DX11_UNBIND_CBUFF | DX11_UNBIND_VBUFF | DX11_UNBIND_IBUFF | DX11_UNBIND_SRV | DX11_UNBIND_RTV);
|
|
}
|
|
}
|
|
} break;
|
|
|
|
case GPU_CMD_KIND_DRAW_GRID:
|
|
{
|
|
__profscope(Draw grid);
|
|
__profscope_dx11(G.profiling_ctx, Draw grid, RGB32_F(0.2, 0.2, 0.5));
|
|
struct dx11_shader *shader = &G.shaders[DX11_SHADER_KIND_GRID];
|
|
if (shader->valid) {
|
|
struct dx11_buffer *instance_buffer = plan->cmd_buffers.grid.instance_buffer;
|
|
u32 instance_offset = cmd->grid.instance_offset;
|
|
u32 instance_count = cmd->grid.instance_count;
|
|
|
|
/* Bind shader */
|
|
ID3D11DeviceContext_VSSetShader(G.devcon, shader->vs, 0, 0);
|
|
ID3D11DeviceContext_PSSetShader(G.devcon, shader->ps, 0, 0);
|
|
|
|
/* Fill & bind constant buffer */
|
|
{
|
|
struct dx11_grid_uniform *uniform = dx11_buffer_push(constant_buffer, sizeof(struct dx11_grid_uniform));
|
|
uniform->vp = vp_matrix;
|
|
uniform->instance_offset = instance_offset;
|
|
dx11_buffer_submit(constant_buffer);
|
|
}
|
|
ID3D11DeviceContext_VSSetConstantBuffers(G.devcon, 0, 1, &constant_buffer->gpu_buffer);
|
|
ID3D11DeviceContext_PSSetConstantBuffers(G.devcon, 0, 1, &constant_buffer->gpu_buffer);
|
|
|
|
/* Bind dummy vertex buffer */
|
|
u32 zero = 0;
|
|
ID3D11DeviceContext_IASetVertexBuffers(G.devcon, 0, 1, &G.dummy_vertex_buffer->gpu_buffer, &zero, &zero);
|
|
ID3D11DeviceContext_IASetIndexBuffer(G.devcon, G.quad_index_buffer->gpu_buffer, DXGI_FORMAT_R16_UINT, zero);
|
|
|
|
/* Bind SRVs */
|
|
ID3D11ShaderResourceView *srvs[] = { instance_buffer->srv };
|
|
ID3D11DeviceContext_VSSetShaderResources(G.devcon, 0, ARRAY_COUNT(srvs), srvs);
|
|
ID3D11DeviceContext_PSSetShaderResources(G.devcon, 0, ARRAY_COUNT(srvs), srvs);
|
|
|
|
/* Bind RTVs */
|
|
ID3D11RenderTargetView *rtvs[] = { final_tex->rtv };
|
|
ID3D11DeviceContext_OMSetRenderTargets(G.devcon, ARRAY_COUNT(rtvs), rtvs, NULL);
|
|
|
|
/* Draw */
|
|
ID3D11DeviceContext_IASetPrimitiveTopology(G.devcon, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
/* FIXME: Set StartInstanceLocation parameter here rather than passing instance_offset into CBV */
|
|
ID3D11DeviceContext_DrawIndexedInstanced(G.devcon, 6, instance_count, 0, 0, 0);
|
|
|
|
/* Unbind */
|
|
dx11_unbind(DX11_UNBIND_VS | DX11_UNBIND_PS | DX11_UNBIND_CBUFF | DX11_UNBIND_VBUFF | DX11_UNBIND_IBUFF | DX11_UNBIND_SRV | DX11_UNBIND_RTV);
|
|
}
|
|
} break;
|
|
|
|
case GPU_CMD_KIND_TEST:
|
|
{
|
|
__profscope(Test);
|
|
__profscope_dx11(G.profiling_ctx, Test, RGB32_F(1, 0.2, 1));
|
|
struct dx11_shader *shader = &G.shaders[DX11_SHADER_KIND_TEST];
|
|
if (shader->valid) {
|
|
struct dx11_buffer *instance_buffer = plan->cmd_buffers.test.instance_buffer;
|
|
u32 instance_offset = cmd->test.instance_offset;
|
|
u32 instance_count = cmd->test.instance_count;
|
|
|
|
/* Bind shader */
|
|
ID3D11DeviceContext_VSSetShader(G.devcon, shader->vs, 0, 0);
|
|
ID3D11DeviceContext_PSSetShader(G.devcon, shader->ps, 0, 0);
|
|
|
|
/* Fill & bind constant buffer */
|
|
{
|
|
struct dx11_test_uniform *uniform = dx11_buffer_push(constant_buffer, sizeof(struct dx11_test_uniform));
|
|
uniform->vp = vp_matrix;
|
|
uniform->instance_offset = instance_offset;
|
|
dx11_buffer_submit(constant_buffer);
|
|
}
|
|
ID3D11DeviceContext_VSSetConstantBuffers(G.devcon, 0, 1, &constant_buffer->gpu_buffer);
|
|
ID3D11DeviceContext_PSSetConstantBuffers(G.devcon, 0, 1, &constant_buffer->gpu_buffer);
|
|
|
|
/* Bind dummy vertex buffer */
|
|
u32 zero = 0;
|
|
ID3D11DeviceContext_IASetVertexBuffers(G.devcon, 0, 1, &G.dummy_vertex_buffer->gpu_buffer, &zero, &zero);
|
|
ID3D11DeviceContext_IASetIndexBuffer(G.devcon, G.quad_index_buffer->gpu_buffer, DXGI_FORMAT_R16_UINT, zero);
|
|
|
|
/* Bind SRVs */
|
|
ID3D11ShaderResourceView *srvs[] = { instance_buffer->srv };
|
|
ID3D11DeviceContext_VSSetShaderResources(G.devcon, 0, ARRAY_COUNT(srvs), srvs);
|
|
ID3D11DeviceContext_PSSetShaderResources(G.devcon, 0, ARRAY_COUNT(srvs), srvs);
|
|
|
|
/* Bind RTVs */
|
|
ID3D11RenderTargetView *rtvs[] = { final_tex->rtv };
|
|
ID3D11DeviceContext_OMSetRenderTargets(G.devcon, ARRAY_COUNT(rtvs), rtvs, NULL);
|
|
|
|
/* Draw */
|
|
ID3D11DeviceContext_IASetPrimitiveTopology(G.devcon, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
/* FIXME: Set StartInstanceLocation parameter here rather than passing instance_offset into CBV */
|
|
ID3D11DeviceContext_DrawIndexedInstanced(G.devcon, 6, instance_count, 0, 0, 0);
|
|
|
|
/* Unbind */
|
|
dx11_unbind(DX11_UNBIND_VS | DX11_UNBIND_PS | DX11_UNBIND_CBUFF | DX11_UNBIND_VBUFF | DX11_UNBIND_IBUFF | DX11_UNBIND_SRV | DX11_UNBIND_RTV);
|
|
}
|
|
} break;
|
|
}
|
|
}
|
|
}
|
|
|
|
sprite_scope_end(sprite_scope);
|
|
}
|
|
|
|
/* ========================== *
|
|
* Backbuffer
|
|
* ========================== */
|
|
|
|
INTERNAL void gpu_capture_image_for_profiler(void);
|
|
|
|
#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
|
|
|
|
INTERNAL void present_resize(struct v2i32 size)
|
|
{
|
|
__prof;
|
|
/* Delay backbuffer recreation so that we don't recreate it too quickly.
|
|
* It seems that doing this without a delay too often will cause windows
|
|
* to eventually just display a blackscreen rather than the backbuffer
|
|
* (e.g. when resizing for more than a few seconds). */
|
|
i64 now_ns = sys_time_ns();
|
|
if (G.last_backbuffer_resize_ns != 0) {
|
|
i64 next_resize_ns = G.last_backbuffer_resize_ns + NS_FROM_SECONDS(DX11_BACKBUFFER_RECREATION_DELAY);
|
|
if (now_ns < next_resize_ns) {
|
|
sys_sleep_precise(SECONDS_FROM_NS(next_resize_ns - now_ns));
|
|
now_ns = sys_time_ns();
|
|
}
|
|
}
|
|
G.last_backbuffer_resize_ns = now_ns;
|
|
|
|
/* Release */
|
|
if (G.backbuffer_texture.texture != 0) {
|
|
ID3D11RenderTargetView_Release(G.backbuffer_texture.rtv);
|
|
ID3D11Texture2D_Release(G.backbuffer_texture.texture);
|
|
}
|
|
|
|
/* Resize */
|
|
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);
|
|
G.backbuffer_texture.size = size;
|
|
|
|
/* Create rtv */
|
|
D3D11_RENDER_TARGET_VIEW_DESC rtv_desc = ZI;
|
|
rtv_desc.Format = DX11_SWAPCHAIN_RTV_FORMAT;
|
|
rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
|
|
ID3D11Device_CreateRenderTargetView(G.dev, (ID3D11Resource *)G.backbuffer_texture.texture, &rtv_desc, &G.backbuffer_texture.rtv);
|
|
}
|
|
|
|
INTERNAL void present_blit(struct dx11_texture *dst, struct dx11_texture *src, struct xform src_xf)
|
|
{
|
|
__prof;
|
|
__profscope_dx11(G.profiling_ctx, Blit, RGB32_F(0.3, 0.1, 0.1));
|
|
struct gpu_handle src_texture_handle = { .v = (u64)src };
|
|
struct gpu_handle dst_texture_handle = { .v = (u64)dst };
|
|
|
|
/* 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(G.present_blit_plan, params);
|
|
}
|
|
|
|
/* Clear textures */
|
|
gpu_texture_clear(dst_texture_handle, RGBA32_F(0, 0, 0, 1));
|
|
|
|
/* Render to backbuffer texture */
|
|
{
|
|
struct gpu_dispatch_params params = ZI;
|
|
params.plan = G.present_blit_plan;
|
|
params.draw_target = dst_texture_handle;
|
|
params.draw_target_viewport = RECT_FROM_V2(V2(0, 0), V2(dst->size.x, dst->size.y));
|
|
params.draw_target_view = XFORM_IDENT;
|
|
gpu_dispatch(params);
|
|
}
|
|
}
|
|
|
|
void gpu_present(struct v2i32 backbuffer_resolution, struct gpu_handle texture, struct xform texture_xf, i32 vsync)
|
|
{
|
|
__prof;
|
|
|
|
/* Reload dirty shaders */
|
|
#if RESOURCE_RELOADING
|
|
for (u64 i = DX11_SHADER_KIND_NONE + 1; i < NUM_DX11_SHADER_KINDS; ++i) {
|
|
struct dx11_shader_desc *desc = &G.shader_info[i];
|
|
if (shader_unset_dirty(desc)) {
|
|
reload_shader(&G.shaders[i], desc);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* Track vram usage */
|
|
#if GSTAT_ENABLED || PROFILING
|
|
{
|
|
struct DXGI_QUERY_VIDEO_MEMORY_INFO info = get_memory_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
|
|
}
|
|
#endif
|
|
|
|
/* Wait */
|
|
i32 flags = 0;
|
|
if (vsync) {
|
|
if (G.swapchain_waitable != NULL) {
|
|
__profscope(Present wait);
|
|
WaitForSingleObjectEx(G.swapchain_waitable, 1000, TRUE);
|
|
}
|
|
} else {
|
|
#if DX11_ALLOW_TEARING
|
|
flags = DXGI_PRESENT_ALLOW_TEARING;
|
|
#endif
|
|
}
|
|
|
|
/* Resize backbuffer */
|
|
if (!v2i32_eq(G.backbuffer_texture.size, backbuffer_resolution)) {
|
|
present_resize(backbuffer_resolution);
|
|
}
|
|
|
|
/* Blit to backbuffer */
|
|
present_blit(&G.backbuffer_texture, (struct dx11_texture *)texture.v, texture_xf);
|
|
|
|
/* Present */
|
|
gpu_capture_image_for_profiler();
|
|
{
|
|
__profscope(Present);
|
|
IDXGISwapChain2_Present(G.swapchain, vsync, flags);
|
|
__prof_dx11_collect(G.profiling_ctx);
|
|
__profframe(0);
|
|
}
|
|
}
|
|
|
|
/* ========================== *
|
|
* 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 arena_temp 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
|
|
|
|
|
|
#endif
|