#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 # include # include # include # include #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