From 2035f73f3ead1cc5f03d59b16f25ef7979047658 Mon Sep 17 00:00:00 2001 From: Kevin Trogant Date: Tue, 2 Apr 2024 19:46:08 +0200 Subject: [PATCH 1/6] init dx11 and basic present --- meson.build | 4 +- meson_options.txt | 2 +- src/game/main.c | 70 ------- src/meson.build | 1 + src/renderer/dx11/gpu.h | 32 +++ src/renderer/dx11/init.cpp | 330 +++++++++++++++++++++++++++++++ src/renderer/dx11/meson.build | 17 ++ src/renderer/dx11/pch/dx11_pch.h | 5 + 8 files changed, 389 insertions(+), 72 deletions(-) create mode 100644 src/renderer/dx11/gpu.h create mode 100644 src/renderer/dx11/init.cpp create mode 100644 src/renderer/dx11/meson.build create mode 100644 src/renderer/dx11/pch/dx11_pch.h diff --git a/meson.build b/meson.build index ccbc968..e7cfb81 100644 --- a/meson.build +++ b/meson.build @@ -2,7 +2,7 @@ project('rtengine', ['c', 'cpp'], default_options: ['buildtype=debug', 'b_sanitize=address', 'c_std=c17', - 'cpp_std=c++14', + 'cpp_std=c++20', 'warning_level=3', 'werror=true', 'b_vscrt=static_from_buildtype', @@ -85,6 +85,8 @@ if get_option('default_library') == 'static' engine_link_libs = [runtime_lib, gfx_lib, app_lib, vk_renderer_lib] elif get_option('static_renderer') == 'null' engine_link_libs = [runtime_lib, gfx_lib, app_lib, null_renderer_lib] + elif get_option('static_renderer') == 'dx11' + engine_link_libs = [runtime_lib, gfx_lib, app_lib, dx11_renderer_lib] else error('Invalid static_renderer option ', get_option('static_renderer')) endif diff --git a/meson_options.txt b/meson_options.txt index 400f79b..a3b7d6e 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -3,4 +3,4 @@ option('use_xlib', type : 'boolean', value : false, description : 'Use Xlib for option('error_report_debugbreak', type : 'boolean', value : true, description : 'Debugbreak in ReportError') option('enable_dxc_shader_compiler', type : 'boolean', value : true, description : 'Enables building the dxc-based shader compiler.') option('game_as_subdir', type : 'boolean', value : false, description : 'If true, adds the directory "src/game" to the build.') - +option('build_dx11', type : 'boolean', value : true, description : 'Enables/disables the build of the dx11 renderer.') diff --git a/src/game/main.c b/src/game/main.c index 030fb12..94d3a9d 100644 --- a/src/game/main.c +++ b/src/game/main.c @@ -13,87 +13,18 @@ void RegisterCVars(void) { static rt_framegraph *_framegraph; -static void PassPrepare(rt_render_pass_id pass, - const rt_render_target_write *writes, - uint32_t write_count, - const rt_render_target_read *reads, - uint32_t read_count) { - // rtLog("GAME", "Prepare pass %x", pass); -} - -static void PassExecute(rt_render_pass_id pass, - const rt_render_target_write *writes, - uint32_t write_count, - const rt_render_target_read *reads, - uint32_t read_count) { - // rtLog("GAME", "Execute pass %x", pass); -} - -static void PassFinalize(rt_render_pass_id pass, - const rt_render_target_write *writes, - uint32_t write_count, - const rt_render_target_read *reads, - uint32_t read_count) { - // rtLog("GAME", "Finalize pass %x", pass); -} - /* Called after the runtime has finished its initialization and before entering the main-loop*/ void Init(void) { rtLog("GAME", "Init"); rtInitAssetCompiler(); rtWaitForAssetProcessing(); - - rt_temp_arena temp = rtGetTemporaryArena(NULL, 0); - -#if 0 - rt_resource_id resid = rtGetResourceID("assets/forward.framegraph"); - size_t size = rtGetResourceSize(resid); - rt_resource *res = rtArenaPush(temp.arena, size); - rtGetResource(resid, res); - - _framegraph = rtCreateFramegraph(res->data); - - rt_render_pass_bind_fns bind = {.Execute = PassExecute, - .Prepare = PassPrepare, - .Finalize = PassFinalize}; - rtBindRenderPass(_framegraph, rtCalculateRenderPassID("forward", sizeof("forward") - 1), &bind); -#else - rt_resource_id resid = rtGetResourceID("assets/test.framegraph"); - size_t size = rtGetResourceSize(resid); - rt_resource *res = rtArenaPush(temp.arena, size); - rtGetResource(resid, res); - - _framegraph = rtCreateFramegraph(res->data); - - rt_render_pass_bind_fns bind = {.Execute = PassExecute, - .Prepare = PassPrepare, - .Finalize = PassFinalize}; - rtBindRenderPass(_framegraph, rtCalculateRenderPassID("pass0", sizeof("pass0") - 1), &bind); - rtBindRenderPass(_framegraph, rtCalculateRenderPassID("pass1", sizeof("pass1") - 1), &bind); - - rt_v2 vertices[] = { - { 0, 0.5}, - { 0.5, -0.5}, - {-0.5, -0.5} - }; - rt_buffer_info info = { - .type = RT_BUFFER_TYPE_VERTEX, - .usage = RT_BUFFER_USAGE_STATIC, - .size = sizeof(vertices), - .data = vertices, - }; - rt_buffer_handle buf; - g_renderer.CreateBuffers(1, &info, &buf); - -#endif } /* Called after exiting the main-loop and before the runtime starts its shutdown */ void Shutdown(void) { rtLog("GAME", "Shutdown"); rtShutdownAssetCompiler(); - rtDestroyFramegraph(_framegraph); } void Update(unsigned int frame_id) { @@ -101,5 +32,4 @@ void Update(unsigned int frame_id) { } void Render(unsigned int frame_id) { - rtExecuteFramegraph(_framegraph, frame_id); } \ No newline at end of file diff --git a/src/meson.build b/src/meson.build index 32aed4b..b7e1d29 100644 --- a/src/meson.build +++ b/src/meson.build @@ -6,3 +6,4 @@ subdir('app_framework') # Renderer libs subdir('renderer/vk') subdir('renderer/null') +subdir('renderer/dx11') diff --git a/src/renderer/dx11/gpu.h b/src/renderer/dx11/gpu.h new file mode 100644 index 0000000..5b40f3c --- /dev/null +++ b/src/renderer/dx11/gpu.h @@ -0,0 +1,32 @@ +#ifndef RT_DX11_GPU_H +#define RT_DX11_GPU_H + +#include +#include +#include +#include + +// Smart pointer for COM-Objects +template +using ComPtr = Microsoft::WRL::ComPtr; + +struct rt_swap_chain { + ComPtr swap_chain; + ComPtr rtv; +}; + +struct rt_gpu { + ComPtr device; + ComPtr device_context; + ComPtr dxgi_factory; + + rt_swap_chain swap_chain; + + D3D_FEATURE_LEVEL feature_level; +}; + +#ifndef DONT_DEFINE_GPU_GLOBAL +extern rt_gpu g_gpu; +#endif + +#endif \ No newline at end of file diff --git a/src/renderer/dx11/init.cpp b/src/renderer/dx11/init.cpp new file mode 100644 index 0000000..c46d19f --- /dev/null +++ b/src/renderer/dx11/init.cpp @@ -0,0 +1,330 @@ +#ifndef _WIN32 +#pragma warning Building DX11 on non - windows is probably a mistake +#endif + +#pragma comment(lib, "d3d11.lib") +#pragma comment(lib, "dxgi.lib") +#pragma comment(lib, "d3dcompiler.lib") +#pragma comment(lib, "winmm.lib") +#pragma comment(lib, "dxguid.lib") + +#include +#include +#include + +#include "gfx/renderer_api.h" +#include "runtime/config.h" + +#define DONT_DEFINE_RENDERER_GLOBAL +#include "gpu.h" + +RT_CVAR_S( + rt_Dx11AdapterName, + "Name of the adapter that should be used for device creation. Default: \"\" (Use default)", + ""); +RT_CVAR_I(rt_Dx11VSync, "Enable vsync. Default: 1", 1); + +rt_gpu g_gpu; + +extern "C" void RT_RENDERER_API_FN(RegisterCVars)(void) { + rtRegisterCVAR(&rt_Dx11AdapterName); + rtRegisterCVAR(&rt_Dx11VSync); +} + +static rt_swap_chain CreateSwapChain(HWND hwnd) { + rt_swap_chain swc; + + DXGI_SWAP_CHAIN_DESC1 desc; + desc.Width = 0; // use window width + desc.Height = 0; // use window height + desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // can't specify _SRGB here when using + // DXGI_SWAP_EFFECT_FLIP_* ...; + desc.Stereo = FALSE; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + desc.BufferCount = 2; + desc.Scaling = DXGI_SCALING_STRETCH; + desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; + desc.Flags = 0; + + if (FAILED(g_gpu.dxgi_factory->CreateSwapChainForHwnd(g_gpu.device.Get(), + hwnd, + &desc, + nullptr, + nullptr, + &swc.swap_chain))) { + rtReportError("dx11", "Failed to create the swap chain."); + return swc; + } + + ID3D11Texture2D *frame_buffer; + if (FAILED(swc.swap_chain->GetBuffer(0, IID_PPV_ARGS(&frame_buffer)))) { + rtReportError("dx11", "Failed to retrieve the backbuffer."); + swc.swap_chain.Reset(); + return swc; + } + + D3D11_RENDER_TARGET_VIEW_DESC rtv_desc = {}; + rtv_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; + rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + + if (FAILED(g_gpu.device->CreateRenderTargetView(frame_buffer, &rtv_desc, &swc.rtv))) { + rtReportError("dx11", "Failed to create the render target view for the backbuffer."); + swc.swap_chain.Reset(); + return swc; + } + + return swc; +} + +static IDXGIAdapter *RetrieveSelectedAdapter(void) { + ComPtr factory; + if (FAILED(CreateDXGIFactory2(0, IID_PPV_ARGS(&factory)))) { + return NULL; + } + + UINT i = 0; + IDXGIAdapter *adapter; + while (factory->EnumAdapters(i, &adapter) == S_OK) { + ++i; + + DXGI_ADAPTER_DESC desc; + adapter->GetDesc(&desc); + + char utf8_desc[256]; + rtWStrToUTF8(desc.Description, utf8_desc, 256); + + if (strncmp(utf8_desc, rt_Dx11AdapterName.s, 256) == 0) + return adapter; + } + return NULL; +} + +extern "C" rt_result RT_RENDERER_API_FN(Init)(const rt_renderer_init_info *info) { + constexpr D3D_FEATURE_LEVEL feature_levels[] = {D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0}; + UINT device_flags = 0; +#ifdef RT_DEBUG + device_flags |= D3D11_CREATE_DEVICE_DEBUG; +#endif + + IDXGIAdapter *selected_adapter = RetrieveSelectedAdapter(); + + ID3D11Device *base_device; + ID3D11DeviceContext *base_context; + if (FAILED(D3D11CreateDevice(selected_adapter, + D3D_DRIVER_TYPE_HARDWARE, + nullptr, + device_flags, + feature_levels, + RT_ARRAY_COUNT(feature_levels), + D3D11_SDK_VERSION, + &base_device, + &g_gpu.feature_level, + &base_context))) { + rtLog("dx11", "Feature level 11.1 creation failed, retrying with feature level 11.0"); + if (FAILED(D3D11CreateDevice(selected_adapter, + D3D_DRIVER_TYPE_HARDWARE, + nullptr, + device_flags, + &feature_levels[1], + RT_ARRAY_COUNT(feature_levels) - 1, + D3D11_SDK_VERSION, + &base_device, + &g_gpu.feature_level, + &base_context))) { + rtReportError("dx11", "Failed to create the d3d11 device."); + return RT_UNKNOWN_ERROR; + } + } + + if (FAILED(base_device->QueryInterface(IID_PPV_ARGS(&g_gpu.device)))) { + rtReportError("dx11", "Failed to query the D3D11Device1 interface."); + return RT_UNKNOWN_ERROR; + } + if (FAILED(base_context->QueryInterface(IID_PPV_ARGS(&g_gpu.device_context)))) { + rtReportError("dx11", "Failed to query the D3D11DeviceContext1 interface."); + return RT_UNKNOWN_ERROR; + } + + IDXGIDevice1 *dxgi_device; + if (FAILED(g_gpu.device->QueryInterface(&dxgi_device))) { + rtReportError("dx11", "Failed to query the DXGIDevice1 interface."); + return RT_UNKNOWN_ERROR; + } + IDXGIAdapter *adapter; + if (FAILED(dxgi_device->GetAdapter(&adapter))) { + rtReportError("dx11", "Failed to retrieve the dxgi adapter."); + return RT_UNKNOWN_ERROR; + } + if (FAILED(adapter->GetParent(IID_PPV_ARGS(&g_gpu.dxgi_factory)))) { + rtReportError("dx11", "Failed to retrieve the dxgi factory."); + return RT_UNKNOWN_ERROR; + } + + g_gpu.swap_chain = CreateSwapChain(info->hWnd); + + return RT_SUCCESS; +} + +extern "C" void RT_RENDERER_API_FN(Shutdown)(void) { +} + +extern "C" void RT_RENDERER_API_FN(BeginFrame)(unsigned int frame_id) { + RT_UNUSED(frame_id); + FLOAT clear_color[4] = { + 0, + 0, + 0, + 0, + }; + g_gpu.device_context->ClearRenderTargetView(g_gpu.swap_chain.rtv.Get(), clear_color); +} + +extern "C" void RT_RENDERER_API_FN(EndFrame)(unsigned int frame_id) { + RT_UNUSED(frame_id); + UINT sync_interval = rt_Dx11VSync.i ? 1 : 0; + g_gpu.swap_chain.swap_chain->Present(sync_interval, 0); +} + +// Copied from null. Delete once no longer needed + +extern "C" { + +#define RETURN_HANDLE_STUB2(type, initial) \ + static unsigned int s_next = (initial); \ + s_next = (s_next + 1) % RT_RENDERER_BACKEND_HANDLE_MAX_INDEX; \ + type h = { \ + 1, \ + s_next, \ + }; \ + return h; + +#define RETURN_HANDLE_STUB(type) RETURN_HANDLE_STUB2(type, 1) + +#define RETURN_HANDLE_ARRAY_STUB2(out, count, initial) \ + static unsigned int s_next = (initial); \ + for (uint32_t i = 0; i < (count); ++i) { \ + (out)[i].index = (s_next++) % RT_RENDERER_BACKEND_HANDLE_MAX_INDEX; \ + (out)[i].version = 1; \ + } + +#define RETURN_HANDLE_ARRAY_STUB(out, count) RETURN_HANDLE_ARRAY_STUB2(out, count, 1) + +unsigned int RT_RENDERER_API_FN(GetMaxFramesInFlight)(void) { + return 2; +} +rt_pipeline_handle RT_RENDERER_API_FN(CompilePipeline)(const rt_pipeline_info *info) { + RT_UNUSED(info); + RETURN_HANDLE_STUB(rt_pipeline_handle); +} + +void RT_RENDERER_API_FN(DestroyPipeline)(rt_pipeline_handle handle) { + RT_UNUSED(handle); +} + +rt_render_target_handle RT_RENDERER_API_FN(CreateRenderTarget)(const rt_render_target_info *info) { + RT_UNUSED(info); + RETURN_HANDLE_STUB2(rt_render_target_handle, 2); +} + +rt_render_target_handle RT_RENDERER_API_FN(GetSwapchainRenderTarget)(void) { + return {1, 1}; +} + +void RT_RENDERER_API_FN(DestroyRenderTarget)(rt_render_target_handle handle) { + RT_UNUSED(handle); +} + +rt_result RT_RENDERER_API_FN(AllocCommandBuffers)(uint32_t count, + const rt_alloc_command_buffer_info *info, + rt_command_buffer_handle *p_command_buffers) { + RT_UNUSED(info); + RETURN_HANDLE_ARRAY_STUB(p_command_buffers, count) + return RT_SUCCESS; +} + +rt_result RT_RENDERER_API_FN(SubmitCommandBuffers)(rt_gpu_queue queue, + const rt_submit_command_buffers_info *info) { + RT_UNUSED(queue); + RT_UNUSED(info); + return RT_SUCCESS; +} + +rt_result RT_RENDERER_API_FN(CreateSemaphores)(uint32_t count, + const rt_gpu_semaphore_info *info, + rt_gpu_semaphore_handle *p_semaphores) { + RT_UNUSED(info); + RETURN_HANDLE_ARRAY_STUB2(p_semaphores, count, 3) + return RT_SUCCESS; +} + +void RT_RENDERER_API_FN(DestroySemaphores)(uint32_t count, rt_gpu_semaphore_handle *semaphores) { + RT_UNUSED(count); + RT_UNUSED(semaphores); +} + +/* NOTE(Kevin): It might become necessary to actually track the value, to correctly simulate gpu + * behaviour */ +uint64_t RT_RENDERER_API_FN(GetSemaphoreValue)(rt_gpu_semaphore_handle sem) { + RT_UNUSED(sem); + return 0; +} + +rt_gpu_semaphore_handle RT_RENDERER_API_FN(GetSwapchainAvailableSemaphore)(void) { + return {1, 1}; +} + +rt_gpu_semaphore_handle RT_RENDERER_API_FN(GetRenderFinishedSemaphore)(void) { + return {1, 2}; +} + +rt_result RT_RENDERER_API_FN(CreateBuffers)(uint32_t count, + const rt_buffer_info *info, + rt_buffer_handle *p_buffers) { + RT_UNUSED(info); + RETURN_HANDLE_ARRAY_STUB(p_buffers, count); + return RT_SUCCESS; +} + +void RT_RENDERER_API_FN(DestroyBuffers)(uint32_t count, rt_buffer_handle *buffers) { + RT_UNUSED(count); + RT_UNUSED(buffers); +} + +void RT_RENDERER_API_FN(CmdBeginPass)(rt_command_buffer_handle cmd, + const rt_cmd_begin_pass_info *info) { + RT_UNUSED(cmd); + RT_UNUSED(info); +} + +void RT_RENDERER_API_FN(CmdEndPass)(rt_command_buffer_handle cmd) { + RT_UNUSED(cmd); +} + +void RT_RENDERER_API_FN(CmdTransitionRenderTarget)(rt_command_buffer_handle cmd, + rt_render_target_handle target, + rt_render_target_state state) { + RT_UNUSED(cmd); + RT_UNUSED(target); + RT_UNUSED(state); +} + +void RT_RENDERER_API_FN(CmdFlushRenderTargetWrite)(rt_command_buffer_handle cmdbuf_handle, + rt_render_target_handle render_target) { + RT_UNUSED(cmdbuf_handle); + RT_UNUSED(render_target); +} + +rt_render_graph_builder RT_RENDERER_API_FN(CreateRenderGraphBuilder)(void) { + rt_render_graph_builder b = { + .obj = NULL, + }; + return b; +} + +void RT_RENDERER_API_FN(DestroyRenderGraphBuilder)(rt_render_graph_builder *builder) { + RT_UNUSED(builder); +} +} \ No newline at end of file diff --git a/src/renderer/dx11/meson.build b/src/renderer/dx11/meson.build new file mode 100644 index 0000000..507df5d --- /dev/null +++ b/src/renderer/dx11/meson.build @@ -0,0 +1,17 @@ +if get_option('build_dx11') + dx11_renderer_lib = library('rtdx11', + # Project Sources + 'gpu.h', + + 'init.cpp', + + dependencies : [m_dep, windowing_dep], + include_directories : [engine_incdir, contrib_incdir], + link_with : [runtime_lib], + cpp_pch : 'pch/dx11_pch.h', + override_options : ['b_sanitize=none'], + install : true) + + engine_libs += dx11_renderer_lib + engine_lib_paths += dx11_renderer_lib.full_path() +endif \ No newline at end of file diff --git a/src/renderer/dx11/pch/dx11_pch.h b/src/renderer/dx11/pch/dx11_pch.h new file mode 100644 index 0000000..8288418 --- /dev/null +++ b/src/renderer/dx11/pch/dx11_pch.h @@ -0,0 +1,5 @@ +// DX11 headers +#include +#include +#include +#include \ No newline at end of file From 565c330f712ffd09afbb86b9385d1f13c3f76961 Mon Sep 17 00:00:00 2001 From: Kevin Trogant Date: Wed, 3 Apr 2024 20:56:10 +0200 Subject: [PATCH 2/6] render targets --- src/gfx/gfx.h | 9 ++ src/gfx/renderer_api.h | 2 +- src/renderer/dx11/command_buffers.cpp | 133 +++++++++++++++++++++ src/renderer/dx11/device_objects.hpp | 45 +++++++ src/renderer/dx11/{gpu.h => gpu.hpp} | 13 +- src/renderer/dx11/helpers.cpp | 32 +++++ src/renderer/dx11/init.cpp | 114 +++++++++--------- src/renderer/dx11/meson.build | 6 +- src/renderer/dx11/render_targets.cpp | 166 ++++++++++++++++++++++++++ src/renderer/null/null.c | 4 +- src/runtime/meson.build | 1 + src/runtime/rt_math.h | 5 + src/runtime/runtime.h | 3 + src/runtime/threading_helpers.hpp | 61 ++++++++++ 14 files changed, 531 insertions(+), 63 deletions(-) create mode 100644 src/renderer/dx11/command_buffers.cpp create mode 100644 src/renderer/dx11/device_objects.hpp rename src/renderer/dx11/{gpu.h => gpu.hpp} (69%) create mode 100644 src/renderer/dx11/helpers.cpp create mode 100644 src/renderer/dx11/render_targets.cpp create mode 100644 src/runtime/threading_helpers.hpp diff --git a/src/gfx/gfx.h b/src/gfx/gfx.h index 612c956..a170d5e 100644 --- a/src/gfx/gfx.h +++ b/src/gfx/gfx.h @@ -20,6 +20,9 @@ extern "C" { #if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4201) /* anonymous struct */ #endif typedef union { float v[4]; @@ -32,6 +35,8 @@ typedef union { } rt_color; #if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) #endif /* NOTE(kevin): When you add a value here, you need to handle them in @@ -59,6 +64,10 @@ typedef enum { RT_PIXEL_FORMAT_count, } rt_pixel_format; +RT_INLINE int rtIsDepthFormat(rt_pixel_format format) { + return format == RT_PIXEL_FORMAT_DEPTH24_STENCIL8 || format == RT_PIXEL_FORMAT_DEPTH32; +} + /* In renderer_api.h -> Not necessary for almost all gfx usage */ typedef struct rt_renderer_init_info_s rt_renderer_init_info; diff --git a/src/gfx/renderer_api.h b/src/gfx/renderer_api.h index 89a4389..fd14d43 100644 --- a/src/gfx/renderer_api.h +++ b/src/gfx/renderer_api.h @@ -17,7 +17,7 @@ extern "C" { /* Handles for backend objects */ -#define RT_RENDERER_BACKEND_HANDLE_MAX_INDEX ((1u<<24)-1) +#define RT_RENDER_BACKEND_HANDLE_MAX_INDEX ((1u<<24)-1) #define RT_RENDER_BACKEND_HANDLE_MAX_VERSION 255 #define RT_RENDER_BACKEND_HANDLE(name) \ diff --git a/src/renderer/dx11/command_buffers.cpp b/src/renderer/dx11/command_buffers.cpp new file mode 100644 index 0000000..319d1cb --- /dev/null +++ b/src/renderer/dx11/command_buffers.cpp @@ -0,0 +1,133 @@ +#include +#include + +#include "gfx/renderer_api.h" +#include "runtime/config.h" +#include "runtime/threading.h" +#include "runtime/threading_helpers.hpp" +#include "runtime/handles.h" + +#include "gpu.hpp" +#include "device_objects.hpp" + +RT_CVAR_I(rt_Dx11MaxCommandBuffers, + "Maximum number of simultaneously created command buffers. Default: 1024", + 1024); + +static rt_command_buffer *_buffers; +static rt_command_buffer *_first_free; +static rt_mutex *_lock; + +rt_result InitCommandBufferManagement() { + _buffers = reinterpret_cast( + calloc((size_t)rt_Dx11MaxCommandBuffers.i, sizeof(rt_command_buffer))); + if (!_buffers) + return RT_OUT_OF_MEMORY; + _first_free = &_buffers[1]; + + _lock = rtCreateMutex(); + if (!_lock) { + free(_buffers); + return RT_UNKNOWN_ERROR; + } + + for (int i = 0; i < rt_Dx11MaxCommandBuffers.i - 1; ++i) { + _buffers[i].next_free = &_buffers[i + 1]; + } + return RT_SUCCESS; +} + +void ShutdownCommandBufferManagement() { + for (int i = 0; i < rt_Dx11MaxCommandBuffers.i; ++i) { + if (_buffers[i].context) + _buffers[i].context->Release(); + } + free(_buffers); + _buffers = nullptr; +} + +extern "C" rt_result +RT_RENDERER_API_FN(AllocCommandBuffers)(uint32_t count, + const rt_alloc_command_buffer_info *, + rt_command_buffer_handle *p_command_buffers) { + for (uint32_t i = 0; i < count; ++i) { + rtLockMutex(_lock); + rt_command_buffer *slot = _first_free; + if (slot) + _first_free = slot->next_free; + rtUnlockMutex(_lock); + + if (!slot) { + rtLog("dx11", "Failed to allocate a command buffer slot."); + rtLockMutex(_lock); + for (uint32_t j = 0; j < i; ++j) { + rt_command_buffer *s = &_buffers[p_command_buffers[j].index]; + s->next_free = _first_free; + _first_free = s; + } + rtUnlockMutex(_lock); + return RT_OUT_OF_MEMORY; + } + + if (!slot->context) { + if (FAILED(g_gpu.device->CreateDeferredContext1(0, &slot->context))) { + rtLog("dx11", "Failed to create a deferred context."); + auto lock_guard = rtAutoLock(_lock); + for (uint32_t j = 0; j < i; ++j) { + rt_command_buffer *s = &_buffers[p_command_buffers[j].index]; + s->next_free = _first_free; + _first_free = s; + } + return RT_UNKNOWN_ERROR; + } + } else { + slot->context->ClearState(); + } + + slot->version = (slot->version + 1) % RT_RENDER_BACKEND_HANDLE_MAX_VERSION; + const uint32_t index = (uint32_t)(slot - _buffers); + p_command_buffers[i].version = slot->version; + p_command_buffers[i].index = index; + } + + return RT_SUCCESS; +} + +extern "C" rt_result +RT_RENDERER_API_FN(SubmitCommandBuffers)(rt_gpu_queue, const rt_submit_command_buffers_info *info) { + // TODO: Handle semaphores + + // Submit the command lists to the gpu + for (uint32_t i = 0; i < info->command_buffer_count; ++i) { + rt_command_buffer *cmdbuf = &_buffers[info->command_buffers[i].index]; + if (cmdbuf->version != info->command_buffers[i].version) { + rtLog("dx11", "Tried to submit an invalid command buffer (version mismatch)"); + return RT_INVALID_VALUE; + } + + ID3D11CommandList *cmdlist; + if (FAILED(cmdbuf->context->FinishCommandList(FALSE, &cmdlist))) { + rtLog("dx11", "FinishCommandList failed"); + return RT_UNKNOWN_ERROR; + } + + rtLockMutex(g_gpu.context_lock); + g_gpu.device_context->ExecuteCommandList(cmdlist, FALSE); + rtUnlockMutex(g_gpu.context_lock); + + rtLockMutex(_lock); + cmdbuf->next_free = _first_free; + _first_free = cmdbuf; + rtUnlockMutex(_lock); + } + return RT_SUCCESS; +} + +rt_command_buffer *rtGetCommandBuffer(rt_command_buffer_handle handle) { + if (!RT_IS_HANDLE_VALID(handle) || (int)handle.index >= rt_Dx11MaxCommandBuffers.i) + return nullptr; + auto lg = rtAutoLock(_lock); + if (handle.version != _buffers[handle.index].version) + return nullptr; + return &_buffers[handle.index]; +} \ No newline at end of file diff --git a/src/renderer/dx11/device_objects.hpp b/src/renderer/dx11/device_objects.hpp new file mode 100644 index 0000000..d5ffec3 --- /dev/null +++ b/src/renderer/dx11/device_objects.hpp @@ -0,0 +1,45 @@ +#ifndef RT_DX11_DEVICE_OBJECTS_HPP +#define RT_DX11_DEVICE_OBJECTS_HPP + +// Types containing various api objects +#include +#include +#include + +#include "runtime/runtime.h" + +struct rt_render_target { + // Only one of these should be valid + ID3D11RenderTargetView *rtv; + ID3D11DepthStencilView *dsv; + + ID3D11Texture2D *texture; + + bool IsColorRenderTarget() const { + RT_ASSERT(!(rtv != nullptr && dsv != nullptr), + "A render target should not contain a render target and a depth stencil view"); + return rtv != nullptr; + } + + bool IsDepthStencilTarget() const { + RT_ASSERT(!(rtv != nullptr && dsv != nullptr), + "A render target should not contain a render target and a depth stencil view"); + return dsv != nullptr; + } + + uint32_t version; + rt_render_target *next_free; +}; + +struct rt_command_buffer { + // Only created once and then re-used. + ID3D11DeviceContext1 *context; + + uint32_t version; + rt_command_buffer *next_free; +}; + +rt_render_target *rtGetRenderTarget(rt_render_target_handle handle); +rt_command_buffer *rtGetCommandBuffer(rt_command_buffer_handle handle); + +#endif \ No newline at end of file diff --git a/src/renderer/dx11/gpu.h b/src/renderer/dx11/gpu.hpp similarity index 69% rename from src/renderer/dx11/gpu.h rename to src/renderer/dx11/gpu.hpp index 5b40f3c..43595bc 100644 --- a/src/renderer/dx11/gpu.h +++ b/src/renderer/dx11/gpu.hpp @@ -1,11 +1,14 @@ -#ifndef RT_DX11_GPU_H -#define RT_DX11_GPU_H +#ifndef RT_DX11_GPU_HPP +#define RT_DX11_GPU_HPP #include #include #include #include +#include "runtime/threading.h" +#include "gfx/renderer_api.h" + // Smart pointer for COM-Objects template using ComPtr = Microsoft::WRL::ComPtr; @@ -15,6 +18,7 @@ struct rt_swap_chain { ComPtr rtv; }; + struct rt_gpu { ComPtr device; ComPtr device_context; @@ -22,11 +26,16 @@ struct rt_gpu { rt_swap_chain swap_chain; + rt_mutex *context_lock; + D3D_FEATURE_LEVEL feature_level; + D3D11_FEATURE_DATA_THREADING threading_support; }; #ifndef DONT_DEFINE_GPU_GLOBAL extern rt_gpu g_gpu; #endif +DXGI_FORMAT rtConvertPixelFormat(rt_pixel_format format); + #endif \ No newline at end of file diff --git a/src/renderer/dx11/helpers.cpp b/src/renderer/dx11/helpers.cpp new file mode 100644 index 0000000..f27b6ae --- /dev/null +++ b/src/renderer/dx11/helpers.cpp @@ -0,0 +1,32 @@ +#include "gpu.hpp" + +DXGI_FORMAT rtConvertPixelFormat(rt_pixel_format format) { + switch (format) { + case RT_PIXEL_FORMAT_INVALID: + return DXGI_FORMAT_UNKNOWN; + case RT_PIXEL_FORMAT_R8G8B8A8_UNORM: + return DXGI_FORMAT_R8G8B8A8_UNORM; + case RT_PIXEL_FORMAT_B8G8R8A8_UNORM: + return DXGI_FORMAT_B8G8R8A8_UNORM; + case RT_PIXEL_FORMAT_R8G8B8A8_SRGB: + return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + case RT_PIXEL_FORMAT_B8G8R8A8_SRGB: + return DXGI_FORMAT_B8G8R8A8_UNORM_SRGB; + case RT_PIXEL_FORMAT_R8G8B8_UNORM: + return DXGI_FORMAT_R8G8B8A8_UNORM; + case RT_PIXEL_FORMAT_B8G8R8_UNORM: + return DXGI_FORMAT_B8G8R8X8_UNORM; + case RT_PIXEL_FORMAT_R8G8B8_SRGB: + return DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + case RT_PIXEL_FORMAT_B8G8R8_SRGB: + return DXGI_FORMAT_B8G8R8X8_UNORM_SRGB; + + case RT_PIXEL_FORMAT_DEPTH24_STENCIL8: + return DXGI_FORMAT_D24_UNORM_S8_UINT; + case RT_PIXEL_FORMAT_DEPTH32: + return DXGI_FORMAT_D32_FLOAT; + + default: + return DXGI_FORMAT_UNKNOWN; + } +} diff --git a/src/renderer/dx11/init.cpp b/src/renderer/dx11/init.cpp index c46d19f..ca9a87b 100644 --- a/src/renderer/dx11/init.cpp +++ b/src/renderer/dx11/init.cpp @@ -16,38 +16,44 @@ #include "runtime/config.h" #define DONT_DEFINE_RENDERER_GLOBAL -#include "gpu.h" +#include "gpu.hpp" RT_CVAR_S( rt_Dx11AdapterName, "Name of the adapter that should be used for device creation. Default: \"\" (Use default)", ""); RT_CVAR_I(rt_Dx11VSync, "Enable vsync. Default: 1", 1); +RT_CVAR_I(rt_Dx11MaxSubmittedCommandBuffers, + "Maximum number of submitted command buffers per frame. Default: 1024", + 1024); + +extern rt_cvar rt_Dx11MaxCommandBuffers; rt_gpu g_gpu; extern "C" void RT_RENDERER_API_FN(RegisterCVars)(void) { rtRegisterCVAR(&rt_Dx11AdapterName); rtRegisterCVAR(&rt_Dx11VSync); + rtRegisterCVAR(&rt_Dx11MaxCommandBuffers); } static rt_swap_chain CreateSwapChain(HWND hwnd) { rt_swap_chain swc; DXGI_SWAP_CHAIN_DESC1 desc; - desc.Width = 0; // use window width + desc.Width = 0; // use window width desc.Height = 0; // use window height - desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // can't specify _SRGB here when using - // DXGI_SWAP_EFFECT_FLIP_* ...; - desc.Stereo = FALSE; - desc.SampleDesc.Count = 1; - desc.SampleDesc.Quality = 0; - desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - desc.BufferCount = 2; - desc.Scaling = DXGI_SCALING_STRETCH; - desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; - desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; - desc.Flags = 0; + desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // can't specify _SRGB here when using + // DXGI_SWAP_EFFECT_FLIP_* ...; + desc.Stereo = FALSE; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + desc.BufferCount = 2; + desc.Scaling = DXGI_SCALING_STRETCH; + desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; + desc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; + desc.Flags = 0; if (FAILED(g_gpu.dxgi_factory->CreateSwapChainForHwnd(g_gpu.device.Get(), hwnd, @@ -84,7 +90,7 @@ static IDXGIAdapter *RetrieveSelectedAdapter(void) { if (FAILED(CreateDXGIFactory2(0, IID_PPV_ARGS(&factory)))) { return NULL; } - + UINT i = 0; IDXGIAdapter *adapter; while (factory->EnumAdapters(i, &adapter) == S_OK) { @@ -102,6 +108,11 @@ static IDXGIAdapter *RetrieveSelectedAdapter(void) { return NULL; } +extern rt_result InitCommandBufferManagement(); +extern void ShutdownCommandBufferManagement(); +extern rt_result InitRenderTargetManagement(); +extern void ShutdownRenderTargetManagement(); + extern "C" rt_result RT_RENDERER_API_FN(Init)(const rt_renderer_init_info *info) { constexpr D3D_FEATURE_LEVEL feature_levels[] = {D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0}; UINT device_flags = 0; @@ -163,12 +174,37 @@ extern "C" rt_result RT_RENDERER_API_FN(Init)(const rt_renderer_init_info *info) return RT_UNKNOWN_ERROR; } + g_gpu.device->CheckFeatureSupport(D3D11_FEATURE_THREADING, + &g_gpu.threading_support, + sizeof(g_gpu.threading_support)); + g_gpu.swap_chain = CreateSwapChain(info->hWnd); + g_gpu.context_lock = rtCreateMutex(); + + rt_result res = InitCommandBufferManagement(); + if (res != RT_SUCCESS) + return res; + res = InitRenderTargetManagement(); + if (res != RT_SUCCESS) + return res; + return RT_SUCCESS; } extern "C" void RT_RENDERER_API_FN(Shutdown)(void) { + ShutdownRenderTargetManagement(); + ShutdownCommandBufferManagement(); + rtDestroyMutex(g_gpu.context_lock); + g_gpu.swap_chain.rtv.Reset(); + g_gpu.swap_chain.swap_chain.Reset(); + g_gpu.dxgi_factory.Reset(); + g_gpu.device.Reset(); +} + +extern "C" unsigned int RT_RENDERER_API_FN(GetMaxFramesInFlight)(void) { + // TODO: Verify this. + return 1; } extern "C" void RT_RENDERER_API_FN(BeginFrame)(unsigned int frame_id) { @@ -179,13 +215,18 @@ extern "C" void RT_RENDERER_API_FN(BeginFrame)(unsigned int frame_id) { 0, 0, }; + rtLockMutex(g_gpu.context_lock); g_gpu.device_context->ClearRenderTargetView(g_gpu.swap_chain.rtv.Get(), clear_color); + rtUnlockMutex(g_gpu.context_lock); } extern "C" void RT_RENDERER_API_FN(EndFrame)(unsigned int frame_id) { RT_UNUSED(frame_id); + + rtLockMutex(g_gpu.context_lock); UINT sync_interval = rt_Dx11VSync.i ? 1 : 0; g_gpu.swap_chain.swap_chain->Present(sync_interval, 0); + rtUnlockMutex(g_gpu.context_lock); } // Copied from null. Delete once no longer needed @@ -194,7 +235,7 @@ extern "C" { #define RETURN_HANDLE_STUB2(type, initial) \ static unsigned int s_next = (initial); \ - s_next = (s_next + 1) % RT_RENDERER_BACKEND_HANDLE_MAX_INDEX; \ + s_next = (s_next + 1) % RT_RENDER_BACKEND_HANDLE_MAX_INDEX; \ type h = { \ 1, \ s_next, \ @@ -206,15 +247,12 @@ extern "C" { #define RETURN_HANDLE_ARRAY_STUB2(out, count, initial) \ static unsigned int s_next = (initial); \ for (uint32_t i = 0; i < (count); ++i) { \ - (out)[i].index = (s_next++) % RT_RENDERER_BACKEND_HANDLE_MAX_INDEX; \ + (out)[i].index = (s_next++) % RT_RENDER_BACKEND_HANDLE_MAX_INDEX; \ (out)[i].version = 1; \ } #define RETURN_HANDLE_ARRAY_STUB(out, count) RETURN_HANDLE_ARRAY_STUB2(out, count, 1) -unsigned int RT_RENDERER_API_FN(GetMaxFramesInFlight)(void) { - return 2; -} rt_pipeline_handle RT_RENDERER_API_FN(CompilePipeline)(const rt_pipeline_info *info) { RT_UNUSED(info); RETURN_HANDLE_STUB(rt_pipeline_handle); @@ -224,33 +262,6 @@ void RT_RENDERER_API_FN(DestroyPipeline)(rt_pipeline_handle handle) { RT_UNUSED(handle); } -rt_render_target_handle RT_RENDERER_API_FN(CreateRenderTarget)(const rt_render_target_info *info) { - RT_UNUSED(info); - RETURN_HANDLE_STUB2(rt_render_target_handle, 2); -} - -rt_render_target_handle RT_RENDERER_API_FN(GetSwapchainRenderTarget)(void) { - return {1, 1}; -} - -void RT_RENDERER_API_FN(DestroyRenderTarget)(rt_render_target_handle handle) { - RT_UNUSED(handle); -} - -rt_result RT_RENDERER_API_FN(AllocCommandBuffers)(uint32_t count, - const rt_alloc_command_buffer_info *info, - rt_command_buffer_handle *p_command_buffers) { - RT_UNUSED(info); - RETURN_HANDLE_ARRAY_STUB(p_command_buffers, count) - return RT_SUCCESS; -} - -rt_result RT_RENDERER_API_FN(SubmitCommandBuffers)(rt_gpu_queue queue, - const rt_submit_command_buffers_info *info) { - RT_UNUSED(queue); - RT_UNUSED(info); - return RT_SUCCESS; -} rt_result RT_RENDERER_API_FN(CreateSemaphores)(uint32_t count, const rt_gpu_semaphore_info *info, @@ -316,15 +327,4 @@ void RT_RENDERER_API_FN(CmdFlushRenderTargetWrite)(rt_command_buffer_handle cmdb RT_UNUSED(cmdbuf_handle); RT_UNUSED(render_target); } - -rt_render_graph_builder RT_RENDERER_API_FN(CreateRenderGraphBuilder)(void) { - rt_render_graph_builder b = { - .obj = NULL, - }; - return b; -} - -void RT_RENDERER_API_FN(DestroyRenderGraphBuilder)(rt_render_graph_builder *builder) { - RT_UNUSED(builder); -} } \ No newline at end of file diff --git a/src/renderer/dx11/meson.build b/src/renderer/dx11/meson.build index 507df5d..4522cfa 100644 --- a/src/renderer/dx11/meson.build +++ b/src/renderer/dx11/meson.build @@ -1,9 +1,13 @@ if get_option('build_dx11') dx11_renderer_lib = library('rtdx11', # Project Sources - 'gpu.h', + 'device_objects.hpp', + 'gpu.hpp', + 'command_buffers.cpp', + 'helpers.cpp', 'init.cpp', + 'render_targets.cpp', dependencies : [m_dep, windowing_dep], include_directories : [engine_incdir, contrib_incdir], diff --git a/src/renderer/dx11/render_targets.cpp b/src/renderer/dx11/render_targets.cpp new file mode 100644 index 0000000..d253231 --- /dev/null +++ b/src/renderer/dx11/render_targets.cpp @@ -0,0 +1,166 @@ +#include +#include + +#include "gfx/renderer_api.h" +#include "runtime/config.h" +#include "runtime/handles.h" +#include "runtime/threading_helpers.hpp" + +#include "device_objects.hpp" +#include "gpu.hpp" + +RT_CVAR_I(rt_Dx11MaxRenderTargets, + "Maximum number of simultaneously existing render targets. Default: 128", + 128); + +static rt_render_target *_render_targets; +static rt_render_target *_first_free; +static rt_mutex *_lock; + +rt_result InitRenderTargetManagement() { + _render_targets = reinterpret_cast( + calloc((size_t)rt_Dx11MaxRenderTargets.i, sizeof(rt_render_target))); + if (!_render_targets) { + return RT_OUT_OF_MEMORY; + } + + _lock = rtCreateMutex(); + if (!_lock) { + free(_render_targets); + return RT_UNKNOWN_ERROR; + } + + _first_free = _render_targets + 2; + for (int i = 0; i < rt_Dx11MaxRenderTargets.i; ++i) { + _render_targets[i].next_free = &_render_targets[i + 1]; + } + return RT_SUCCESS; +} + +void ShutdownRenderTargetManagement() { + for (int i = 0; i < rt_Dx11MaxRenderTargets.i; ++i) { + if (_render_targets[i].rtv) + _render_targets[i].rtv->Release(); + if (_render_targets[i].dsv) + _render_targets[i].dsv->Release(); + if (_render_targets[i].texture) + _render_targets[i].texture->Release(); + } + free(_render_targets); + rtDestroyMutex(_lock); +} + +extern "C" rt_render_target_handle +RT_RENDERER_API_FN(CreateRenderTarget)(const rt_render_target_info *info) { + rt_render_target *slot = nullptr; + { + auto lock_guard = rtAutoLock(_lock); + slot = _first_free; + _first_free = slot->next_free; + } + + if (!slot) { + rtLog("dx11", + "Could not create a new render target, because all available slots are currently in " + "use."); + return RT_INVALID_HANDLE; + } + + if (!rtIsDepthFormat(info->format)) { + D3D11_TEXTURE2D_DESC tex_desc = {}; + tex_desc.Width = info->width; + tex_desc.Height = info->height; + tex_desc.MipLevels = 1; + tex_desc.ArraySize = 1; + tex_desc.Format = rtConvertPixelFormat(info->format); + tex_desc.SampleDesc.Count = 1; + tex_desc.SampleDesc.Quality = 0; + tex_desc.Usage = D3D11_USAGE_DEFAULT; // read and write + tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; + tex_desc.CPUAccessFlags = 0; // none + tex_desc.MiscFlags = 0; + if (FAILED(g_gpu.device->CreateTexture2D(&tex_desc, nullptr, &slot->texture))) { + rtLog("dx11", "Failed to create backing texture for render target %s", info->name); + auto lg = rtAutoLock(_lock); + slot->next_free = _first_free; + _first_free = slot; + return RT_INVALID_HANDLE; + } + + D3D11_RENDER_TARGET_VIEW_DESC rtv_desc = {}; + rtv_desc.Format = rtConvertPixelFormat(info->format); + rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + rtv_desc.Texture2D.MipSlice = 0; + if (FAILED(g_gpu.device->CreateRenderTargetView(slot->texture, &rtv_desc, &slot->rtv))) { + slot->texture->Release(); + rtLog("dx11", + "Failed to create the render target view for render target %s", + info->name); + auto lg = rtAutoLock(_lock); + slot->next_free = _first_free; + _first_free = slot; + return RT_INVALID_HANDLE; + } + + slot->version = (slot->version + 1) % RT_RENDER_BACKEND_HANDLE_MAX_VERSION; + uint32_t index = static_cast(slot - _render_targets); + return {.version = slot->version, .index = index}; + } else { + D3D11_TEXTURE2D_DESC tex_desc = {}; + tex_desc.Width = info->width; + tex_desc.Height = info->height; + tex_desc.MipLevels = 1; + tex_desc.ArraySize = 1; + tex_desc.Format = rtConvertPixelFormat(info->format); + tex_desc.SampleDesc.Count = 1; + tex_desc.SampleDesc.Quality = 0; + tex_desc.Usage = D3D11_USAGE_DEFAULT; // read and write + tex_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL | D3D11_BIND_SHADER_RESOURCE; + tex_desc.CPUAccessFlags = 0; // none + tex_desc.MiscFlags = 0; + if (FAILED(g_gpu.device->CreateTexture2D(&tex_desc, nullptr, &slot->texture))) { + rtLog("dx11", "Failed to create backing texture for render target %s", info->name); + auto lg = rtAutoLock(_lock); + slot->next_free = _first_free; + _first_free = slot; + return RT_INVALID_HANDLE; + } + + D3D11_DEPTH_STENCIL_VIEW_DESC dsv_desc = {}; + dsv_desc.Format = rtConvertPixelFormat(info->format); + dsv_desc.Flags = 0; + dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; + dsv_desc.Texture2D.MipSlice = 0; + if (FAILED(g_gpu.device->CreateDepthStencilView(slot->texture, &dsv_desc, &slot->dsv))) { + slot->texture->Release(); + rtLog("dx11", + "Failed to create the depth stencil view for render target %s", + info->name); + auto lg = rtAutoLock(_lock); + slot->next_free = _first_free; + _first_free = slot; + return RT_INVALID_HANDLE; + } + + slot->version = (slot->version + 1) % RT_RENDER_BACKEND_HANDLE_MAX_VERSION; + uint32_t index = static_cast(slot - _render_targets); + return {.version = slot->version, .index = index}; + } +} + +extern "C" rt_render_target_handle RT_RENDERER_API_FN(GetSwapchainRenderTarget)(void) { + return {1, 1}; +} + +extern "C" void RT_RENDERER_API_FN(DestroyRenderTarget)(rt_render_target_handle handle) { + RT_UNUSED(handle); +} + +rt_render_target *rtGetRenderTarget(rt_render_target_handle handle) { + if (!RT_IS_HANDLE_VALID(handle) || (int)handle.index >= rt_Dx11MaxRenderTargets.i) + return nullptr; + auto lg = rtAutoLock(_lock); + if (_render_targets[handle.index].version != handle.version) + return nullptr; + return &_render_targets[handle.index]; +} diff --git a/src/renderer/null/null.c b/src/renderer/null/null.c index c179d9f..507ac47 100644 --- a/src/renderer/null/null.c +++ b/src/renderer/null/null.c @@ -6,14 +6,14 @@ #define RETURN_HANDLE_STUB2(type, initial) \ static unsigned int s_next = (initial); \ - return (type) { .index = (s_next++) % RT_RENDERER_BACKEND_HANDLE_MAX_INDEX, .version = 1 } + return (type) { .index = (s_next++) % RT_RENDER_BACKEND_HANDLE_MAX_INDEX, .version = 1 } #define RETURN_HANDLE_STUB(type) RETURN_HANDLE_STUB2(type, 1) #define RETURN_HANDLE_ARRAY_STUB2(out, count, initial) \ static unsigned int s_next = (initial); \ for (uint32_t i = 0; i < (count); ++i) { \ - (out)[i].index = (s_next++) % RT_RENDERER_BACKEND_HANDLE_MAX_INDEX; \ + (out)[i].index = (s_next++) % RT_RENDER_BACKEND_HANDLE_MAX_INDEX; \ (out)[i].version = 1; \ } diff --git a/src/runtime/meson.build b/src/runtime/meson.build index 4555b13..0668b33 100644 --- a/src/runtime/meson.build +++ b/src/runtime/meson.build @@ -19,6 +19,7 @@ runtime_lib = library('rt', 'resources.h', 'runtime.h', 'threading.h', + 'threading_helpers.hpp', 'aio.c', 'assert.c', diff --git a/src/runtime/rt_math.h b/src/runtime/rt_math.h index b2668f1..821eb5f 100644 --- a/src/runtime/rt_math.h +++ b/src/runtime/rt_math.h @@ -5,6 +5,9 @@ #if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wpedantic" +#elif defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4201) /* anonymous struct */ #endif struct vec2 { @@ -44,6 +47,8 @@ typedef struct { #if defined(__GNUC__) || defined(__clang__) #pragma GCC diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) #endif #endif diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index bd6ea5d..08843b8 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -121,8 +121,11 @@ RT_DLLEXPORT int rtAssertHandler(const char *expr, const char *msg, const char * } \ } \ } while (0) + +#define RT_ASSERT_ALWAYS_EVAL(x, msg) RT_ASSERT(x, msg) #else #define RT_ASSERT(x, msg) RT_UNUSED(x) +#define RT_ASSERT_ALWAYS_EVAL(x, msg) (x) #endif enum { diff --git a/src/runtime/threading_helpers.hpp b/src/runtime/threading_helpers.hpp new file mode 100644 index 0000000..72272e2 --- /dev/null +++ b/src/runtime/threading_helpers.hpp @@ -0,0 +1,61 @@ +#ifndef RT_THREADING_HELPERS_HPP +#define RT_THREADING_HELPERS_HPP + +// C++ wrappers around threading functions/types + +#ifndef __cplusplus +#error This file must only be used from C++ code +#endif + +#include "threading.h" + +template +class rt_generic_auto_lock { + public: + explicit rt_generic_auto_lock(_LockType *lock) : m_lock(lock) { + _AcquireFunc(lock); + } + + ~rt_generic_auto_lock() { + _ReleaseFunc(m_lock); + } + + rt_generic_auto_lock(const rt_generic_auto_lock &) = delete; + const rt_generic_auto_lock &operator=(const rt_generic_auto_lock &) = delete; + + private: + _LockType *m_lock; +}; + +namespace __rt_auto_lock_mutex_wrappers { + +RT_INLINE void Lock(rt_mutex *mtx) { + RT_ASSERT_ALWAYS_EVAL(rtLockMutex(mtx), "Lock mutex failed!"); +} + +RT_INLINE void Unlock(rt_mutex *mtx) { + RT_ASSERT_ALWAYS_EVAL(rtUnlockMutex(mtx), "Unlock mutex failed!"); +} + +} // namespace __rt_auto_lock_mutex_wrappers + +using rt_mutex_auto_lock = rt_generic_auto_lock; + +using rt_read_auto_lock = rt_generic_auto_lock; +using rt_write_auto_lock = rt_generic_auto_lock; + +RT_INLINE rt_mutex_auto_lock rtAutoLock(rt_mutex *mutex) { + return rt_mutex_auto_lock(mutex); +} + +RT_INLINE rt_read_auto_lock rtAutoLock(rt_rwlock *rw) { + return rt_read_auto_lock(rw); +} + +RT_INLINE rt_write_auto_lock rtAutoWriteLock(rt_rwlock *rw) { + return rt_write_auto_lock(rw); +} + +#endif From 2651ce2e9dcf689857950141e50d3fb703532e1e Mon Sep 17 00:00:00 2001 From: Kevin Trogant Date: Wed, 3 Apr 2024 21:54:09 +0200 Subject: [PATCH 3/6] buffers --- src/renderer/dx11/buffers.cpp | 146 +++++++++++++++++++++++++++ src/renderer/dx11/device_objects.hpp | 10 ++ src/renderer/dx11/init.cpp | 14 +-- src/renderer/dx11/meson.build | 1 + 4 files changed, 158 insertions(+), 13 deletions(-) create mode 100644 src/renderer/dx11/buffers.cpp diff --git a/src/renderer/dx11/buffers.cpp b/src/renderer/dx11/buffers.cpp new file mode 100644 index 0000000..3f565ad --- /dev/null +++ b/src/renderer/dx11/buffers.cpp @@ -0,0 +1,146 @@ +#include +#include + +#include "gfx/renderer_api.h" +#include "runtime/config.h" +#include "runtime/handles.h" +#include "runtime/threading_helpers.hpp" + +#include "device_objects.hpp" +#include "gpu.hpp" + +RT_CVAR_I(rt_Dx11MaxBuffers, + "Maximum number of simultaneously existing buffers. Default: 4096", + 4096); + +static rt_buffer *_buffers; +static rt_buffer *_first_free; +static rt_mutex *_lock; + +rt_result InitBufferManagement() { + _buffers = + reinterpret_cast(calloc((size_t)rt_Dx11MaxBuffers.i, sizeof(rt_buffer))); + if (!_buffers) { + return RT_OUT_OF_MEMORY; + } + + _lock = rtCreateMutex(); + if (!_lock) { + free(_buffers); + return RT_UNKNOWN_ERROR; + } + + _first_free = _buffers + 2; + for (int i = 0; i < rt_Dx11MaxBuffers.i; ++i) { + _buffers[i].next_free = &_buffers[i + 1]; + } + return RT_SUCCESS; +} + +void ShutdownBufferManagement() { + for (int i = 0; i < rt_Dx11MaxBuffers.i; ++i) { + if (_buffers[i].buffer) + _buffers[i].buffer->Release(); + } + free(_buffers); + rtDestroyMutex(_lock); +} + +rt_buffer *rtGetBuffer(rt_buffer_handle handle) { + if (!RT_IS_HANDLE_VALID(handle) || (int)handle.index >= rt_Dx11MaxBuffers.i) + return nullptr; + auto lg = rtAutoLock(_lock); + if (handle.version != _buffers[handle.index].version) + return nullptr; + return &_buffers[handle.index]; +} + +extern "C" rt_result RT_RENDERER_API_FN(CreateBuffers)(uint32_t count, + const rt_buffer_info *info, + rt_buffer_handle *p_buffers) { + for (uint32_t i = 0; i < count; ++i) { + rtLockMutex(_lock); + rt_buffer *slot = _first_free; + if (slot) + _first_free = slot->next_free; + rtUnlockMutex(_lock); + + if (!slot) { + rtLog("dx11", "Failed to allocate a command buffer slot."); + rtLockMutex(_lock); + for (uint32_t j = 0; j < i; ++j) { + rt_buffer *s = &_buffers[p_buffers[j].index]; + s->next_free = _first_free; + _first_free = s; + _first_free = s; + } + rtUnlockMutex(_lock); + return RT_OUT_OF_MEMORY; + } + + D3D11_USAGE usage = D3D11_USAGE_DEFAULT; + if (info[i].usage == RT_BUFFER_USAGE_STATIC) { + usage = D3D11_USAGE_IMMUTABLE; + } else if (info[i].usage == RT_BUFFER_USAGE_DYNAMIC) { + usage = D3D11_USAGE_DEFAULT; + } else if (info[i].usage == RT_BUFFER_USAGE_TRANSIENT) { + usage = D3D11_USAGE_DYNAMIC; + } + + UINT bind_flags = D3D11_BIND_UNORDERED_ACCESS; + if (info[i].type == RT_BUFFER_TYPE_VERTEX) + bind_flags = D3D11_BIND_VERTEX_BUFFER; + else if (info[i].type == RT_BUFFER_TYPE_INDEX) + bind_flags = D3D11_BIND_INDEX_BUFFER; + else if (info[i].type == RT_BUFFER_TYPE_UNIFORM) + bind_flags = D3D11_BIND_CONSTANT_BUFFER; + else if (info[i].type == RT_BUFFER_TYPE_STORAGE) + bind_flags = D3D11_BIND_UNORDERED_ACCESS; + + + D3D11_BUFFER_DESC desc = {}; + desc.ByteWidth = static_cast(((info[i].size + 15) / 16) * 16); + desc.Usage = usage; + desc.BindFlags = bind_flags; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; + desc.StructureByteStride = 1; + + D3D11_SUBRESOURCE_DATA data; + data.pSysMem = info->data; + data.SysMemPitch = 0; + data.SysMemSlicePitch = 0; + + if (FAILED( + g_gpu.device->CreateBuffer(&desc, info[i].data ? &data : nullptr, &slot->buffer))) { + rtLog("dx11", "Failed to create a deferred context."); + auto lock_guard = rtAutoLock(_lock); + for (uint32_t j = 0; j < i; ++j) { + rt_buffer *s = &_buffers[p_buffers[j].index]; + s->next_free = _first_free; + _first_free = s; + } + return RT_UNKNOWN_ERROR; + } + + slot->version = (slot->version + 1) % RT_RENDER_BACKEND_HANDLE_MAX_VERSION; + const uint32_t index = (uint32_t)(slot - _buffers); + p_buffers[i].version = slot->version; + p_buffers[i].index = index; + } + + return RT_SUCCESS; +} + +extern "C" void RT_RENDERER_API_FN(DestroyBuffers)(uint32_t count, rt_buffer_handle *buffers) { + for (uint32_t i = 0; i < count; ++i) { + if (!RT_IS_HANDLE_VALID(buffers[i]) || (int)buffers[i].index >= rt_Dx11MaxBuffers.i) + continue; + auto lg = rtAutoLock(_lock); + if (buffers[i].version != _buffers[buffers[i].index].version) + continue; + _buffers[buffers[i].index].buffer->Release(); + _buffers[buffers[i].index].next_free = _first_free; + _first_free = &_buffers[buffers[i].index]; + } +} diff --git a/src/renderer/dx11/device_objects.hpp b/src/renderer/dx11/device_objects.hpp index d5ffec3..fb4f643 100644 --- a/src/renderer/dx11/device_objects.hpp +++ b/src/renderer/dx11/device_objects.hpp @@ -39,7 +39,17 @@ struct rt_command_buffer { rt_command_buffer *next_free; }; +struct rt_buffer { + ID3D11Buffer *buffer; + rt_buffer_type type; + rt_buffer_usage usage; + + uint32_t version; + rt_buffer *next_free; +}; + rt_render_target *rtGetRenderTarget(rt_render_target_handle handle); rt_command_buffer *rtGetCommandBuffer(rt_command_buffer_handle handle); +rt_buffer *rtGetBuffer(rt_buffer_handle handle); #endif \ No newline at end of file diff --git a/src/renderer/dx11/init.cpp b/src/renderer/dx11/init.cpp index ca9a87b..2676766 100644 --- a/src/renderer/dx11/init.cpp +++ b/src/renderer/dx11/init.cpp @@ -283,6 +283,7 @@ uint64_t RT_RENDERER_API_FN(GetSemaphoreValue)(rt_gpu_semaphore_handle sem) { return 0; } + rt_gpu_semaphore_handle RT_RENDERER_API_FN(GetSwapchainAvailableSemaphore)(void) { return {1, 1}; } @@ -291,19 +292,6 @@ rt_gpu_semaphore_handle RT_RENDERER_API_FN(GetRenderFinishedSemaphore)(void) { return {1, 2}; } -rt_result RT_RENDERER_API_FN(CreateBuffers)(uint32_t count, - const rt_buffer_info *info, - rt_buffer_handle *p_buffers) { - RT_UNUSED(info); - RETURN_HANDLE_ARRAY_STUB(p_buffers, count); - return RT_SUCCESS; -} - -void RT_RENDERER_API_FN(DestroyBuffers)(uint32_t count, rt_buffer_handle *buffers) { - RT_UNUSED(count); - RT_UNUSED(buffers); -} - void RT_RENDERER_API_FN(CmdBeginPass)(rt_command_buffer_handle cmd, const rt_cmd_begin_pass_info *info) { RT_UNUSED(cmd); diff --git a/src/renderer/dx11/meson.build b/src/renderer/dx11/meson.build index 4522cfa..dc6dc1c 100644 --- a/src/renderer/dx11/meson.build +++ b/src/renderer/dx11/meson.build @@ -4,6 +4,7 @@ if get_option('build_dx11') 'device_objects.hpp', 'gpu.hpp', + 'buffers.cpp', 'command_buffers.cpp', 'helpers.cpp', 'init.cpp', From e93847b1877e8d49bb2e5f0603902e438c789a77 Mon Sep 17 00:00:00 2001 From: Kevin Trogant Date: Wed, 3 Apr 2024 22:56:31 +0200 Subject: [PATCH 4/6] Begin and end pass --- src/game/main.c | 20 ++++++ src/renderer/dx11/command_buffers.cpp | 13 +++- src/renderer/dx11/commands.cpp | 87 +++++++++++++++++++++++++++ src/renderer/dx11/device_objects.hpp | 7 +++ src/renderer/dx11/init.cpp | 32 ++-------- src/renderer/dx11/meson.build | 1 + src/renderer/dx11/render_targets.cpp | 8 ++- src/runtime/runtime.h | 9 ++- 8 files changed, 146 insertions(+), 31 deletions(-) create mode 100644 src/renderer/dx11/commands.cpp diff --git a/src/game/main.c b/src/game/main.c index 94d3a9d..2ffdeaa 100644 --- a/src/game/main.c +++ b/src/game/main.c @@ -13,12 +13,16 @@ void RegisterCVars(void) { static rt_framegraph *_framegraph; +static rt_render_target_handle _rt; + /* Called after the runtime has finished its initialization and before entering the main-loop*/ void Init(void) { rtLog("GAME", "Init"); rtInitAssetCompiler(); rtWaitForAssetProcessing(); + + _rt = g_renderer.GetSwapchainRenderTarget(); } /* Called after exiting the main-loop and before the runtime starts its shutdown */ @@ -32,4 +36,20 @@ void Update(unsigned int frame_id) { } void Render(unsigned int frame_id) { + rt_alloc_command_buffer_info info = {RT_GRAPHICS_QUEUE}; + rt_command_buffer_handle cmd; + g_renderer.AllocCommandBuffers(1, &info, &cmd); + + rt_cmd_begin_pass_info pass_info = { + .color_buffer_count = 1, + .color_buffers = {_rt}, + .color_buffer_loads = {RT_PASS_LOAD_MODE_CLEAR}, + .color_buffer_clear_values = {{.color = {1.f, 0.f, 0.f, 1.f}}}, + .name = "testme", + }; + g_renderer.CmdBeginPass(cmd, &pass_info); + g_renderer.CmdEndPass(cmd); + + rt_submit_command_buffers_info submit = {.command_buffer_count = 1, .command_buffers = &cmd}; + g_renderer.SubmitCommandBuffers(RT_GRAPHICS_QUEUE, &submit); } \ No newline at end of file diff --git a/src/renderer/dx11/command_buffers.cpp b/src/renderer/dx11/command_buffers.cpp index 319d1cb..b63821a 100644 --- a/src/renderer/dx11/command_buffers.cpp +++ b/src/renderer/dx11/command_buffers.cpp @@ -3,12 +3,12 @@ #include "gfx/renderer_api.h" #include "runtime/config.h" +#include "runtime/handles.h" #include "runtime/threading.h" #include "runtime/threading_helpers.hpp" -#include "runtime/handles.h" -#include "gpu.hpp" #include "device_objects.hpp" +#include "gpu.hpp" RT_CVAR_I(rt_Dx11MaxCommandBuffers, "Maximum number of simultaneously created command buffers. Default: 1024", @@ -80,6 +80,13 @@ RT_RENDERER_API_FN(AllocCommandBuffers)(uint32_t count, } return RT_UNKNOWN_ERROR; } + +#ifdef RT_DEBUG + if (FAILED(slot->context->QueryInterface(IID_PPV_ARGS(&slot->annotation)))) { + rtLog("dx11", "Failed to retrieve the annotation interface."); + slot->annotation = nullptr; + } +#endif } else { slot->context->ClearState(); } @@ -117,7 +124,7 @@ RT_RENDERER_API_FN(SubmitCommandBuffers)(rt_gpu_queue, const rt_submit_command_b rtLockMutex(_lock); cmdbuf->next_free = _first_free; - _first_free = cmdbuf; + _first_free = cmdbuf; rtUnlockMutex(_lock); } return RT_SUCCESS; diff --git a/src/renderer/dx11/commands.cpp b/src/renderer/dx11/commands.cpp new file mode 100644 index 0000000..78c99d0 --- /dev/null +++ b/src/renderer/dx11/commands.cpp @@ -0,0 +1,87 @@ +#include +#include + +#include "gfx/renderer_api.h" + +#include "device_objects.hpp" +#include "gpu.hpp" + +extern "C" void RT_RENDERER_API_FN(CmdBeginPass)(rt_command_buffer_handle cmdhandle, + const rt_cmd_begin_pass_info *info) { + rt_command_buffer *cmd = rtGetCommandBuffer(cmdhandle); + if (!RT_VERIFY(cmd)) + return; + + if (cmd->annotation) { + WCHAR wname[128]; + if (rtUTF8ToWStr(info->name, wname, sizeof(wname)) == RT_SUCCESS) + cmd->annotation->BeginEvent(wname); + } + + // Setup rtvs + ID3D11RenderTargetView *rtvs[4]; + ID3D11DepthStencilView *dsv = nullptr; + + for (uint32_t i = 0; i < info->color_buffer_count; ++i) { + rt_render_target *rt = rtGetRenderTarget(info->color_buffers[i]); + if (!RT_VERIFY(rt)) + return; + RT_ASSERT(rt->IsColorRenderTarget(), "Needs to provide a valid color render target"); + rtvs[i] = rt->rtv; + + if (info->color_buffer_loads[i] == RT_PASS_LOAD_MODE_CLEAR) { + FLOAT color[4] = { + info->color_buffer_clear_values[i].color.r, + info->color_buffer_clear_values[i].color.g, + info->color_buffer_clear_values[i].color.b, + info->color_buffer_clear_values[i].color.a, + }; + cmd->context->ClearRenderTargetView(rt->rtv, color); + } + } + + rt_render_target *dsvrt = rtGetRenderTarget(info->depth_stencil_buffer); + if (dsvrt) { + RT_ASSERT(dsvrt->IsDepthStencilTarget(), + "Need to provide a valid depth stencil render target"); + dsv = dsvrt->dsv; + + if (info->depth_stencil_buffer_load == RT_PASS_LOAD_MODE_CLEAR) + cmd->context->ClearDepthStencilView( + dsv, + (dsvrt->HasStencilComponent()) ? D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL + : D3D11_CLEAR_DEPTH, + info->depth_stencil_buffer_clear_value.depth_stencil.depth, + static_cast(info->depth_stencil_buffer_clear_value.depth_stencil.stencil)); + } + + cmd->context->OMSetRenderTargets(static_cast(info->color_buffer_count), rtvs, dsv); +} + +extern "C" void RT_RENDERER_API_FN(CmdEndPass)(rt_command_buffer_handle cmdhandle) { + rt_command_buffer *cmd = rtGetCommandBuffer(cmdhandle); + if (!RT_VERIFY(cmd)) + return; + if (cmd->annotation) { + cmd->annotation->EndEvent(); + } +} + +extern "C" void RT_RENDERER_API_FN(CmdTransitionRenderTarget)(rt_command_buffer_handle cmdhandle, + rt_render_target_handle target, + rt_render_target_state state) { + rt_command_buffer *cmd = rtGetCommandBuffer(cmdhandle); + if (!RT_VERIFY(cmd)) + return; + RT_UNUSED(target); + RT_UNUSED(state); +} + +extern "C" void +RT_RENDERER_API_FN(CmdFlushRenderTargetWrite)(rt_command_buffer_handle cmdhandle, + rt_render_target_handle render_target) { + rt_command_buffer *cmd = rtGetCommandBuffer(cmdhandle); + if (!RT_VERIFY(cmd)) + return; + RT_UNUSED(render_target); +} diff --git a/src/renderer/dx11/device_objects.hpp b/src/renderer/dx11/device_objects.hpp index fb4f643..dd8bed3 100644 --- a/src/renderer/dx11/device_objects.hpp +++ b/src/renderer/dx11/device_objects.hpp @@ -15,6 +15,12 @@ struct rt_render_target { ID3D11Texture2D *texture; + rt_pixel_format format; + + bool HasStencilComponent() const { + return format == RT_PIXEL_FORMAT_DEPTH24_STENCIL8; + } + bool IsColorRenderTarget() const { RT_ASSERT(!(rtv != nullptr && dsv != nullptr), "A render target should not contain a render target and a depth stencil view"); @@ -34,6 +40,7 @@ struct rt_render_target { struct rt_command_buffer { // Only created once and then re-used. ID3D11DeviceContext1 *context; + ID3DUserDefinedAnnotation *annotation; uint32_t version; rt_command_buffer *next_free; diff --git a/src/renderer/dx11/init.cpp b/src/renderer/dx11/init.cpp index 2676766..25c2512 100644 --- a/src/renderer/dx11/init.cpp +++ b/src/renderer/dx11/init.cpp @@ -112,6 +112,8 @@ extern rt_result InitCommandBufferManagement(); extern void ShutdownCommandBufferManagement(); extern rt_result InitRenderTargetManagement(); extern void ShutdownRenderTargetManagement(); +extern rt_result InitBufferManagement(); +extern void ShutdownBufferManagement(); extern "C" rt_result RT_RENDERER_API_FN(Init)(const rt_renderer_init_info *info) { constexpr D3D_FEATURE_LEVEL feature_levels[] = {D3D_FEATURE_LEVEL_11_1, D3D_FEATURE_LEVEL_11_0}; @@ -186,6 +188,9 @@ extern "C" rt_result RT_RENDERER_API_FN(Init)(const rt_renderer_init_info *info) if (res != RT_SUCCESS) return res; res = InitRenderTargetManagement(); + if (res != RT_SUCCESS) + return res; + res = InitBufferManagement(); if (res != RT_SUCCESS) return res; @@ -193,6 +198,7 @@ extern "C" rt_result RT_RENDERER_API_FN(Init)(const rt_renderer_init_info *info) } extern "C" void RT_RENDERER_API_FN(Shutdown)(void) { + ShutdownBufferManagement(); ShutdownRenderTargetManagement(); ShutdownCommandBufferManagement(); rtDestroyMutex(g_gpu.context_lock); @@ -262,7 +268,6 @@ void RT_RENDERER_API_FN(DestroyPipeline)(rt_pipeline_handle handle) { RT_UNUSED(handle); } - rt_result RT_RENDERER_API_FN(CreateSemaphores)(uint32_t count, const rt_gpu_semaphore_info *info, rt_gpu_semaphore_handle *p_semaphores) { @@ -283,7 +288,6 @@ uint64_t RT_RENDERER_API_FN(GetSemaphoreValue)(rt_gpu_semaphore_handle sem) { return 0; } - rt_gpu_semaphore_handle RT_RENDERER_API_FN(GetSwapchainAvailableSemaphore)(void) { return {1, 1}; } @@ -291,28 +295,4 @@ rt_gpu_semaphore_handle RT_RENDERER_API_FN(GetSwapchainAvailableSemaphore)(void) rt_gpu_semaphore_handle RT_RENDERER_API_FN(GetRenderFinishedSemaphore)(void) { return {1, 2}; } - -void RT_RENDERER_API_FN(CmdBeginPass)(rt_command_buffer_handle cmd, - const rt_cmd_begin_pass_info *info) { - RT_UNUSED(cmd); - RT_UNUSED(info); -} - -void RT_RENDERER_API_FN(CmdEndPass)(rt_command_buffer_handle cmd) { - RT_UNUSED(cmd); -} - -void RT_RENDERER_API_FN(CmdTransitionRenderTarget)(rt_command_buffer_handle cmd, - rt_render_target_handle target, - rt_render_target_state state) { - RT_UNUSED(cmd); - RT_UNUSED(target); - RT_UNUSED(state); -} - -void RT_RENDERER_API_FN(CmdFlushRenderTargetWrite)(rt_command_buffer_handle cmdbuf_handle, - rt_render_target_handle render_target) { - RT_UNUSED(cmdbuf_handle); - RT_UNUSED(render_target); -} } \ No newline at end of file diff --git a/src/renderer/dx11/meson.build b/src/renderer/dx11/meson.build index dc6dc1c..e03fc5f 100644 --- a/src/renderer/dx11/meson.build +++ b/src/renderer/dx11/meson.build @@ -5,6 +5,7 @@ if get_option('build_dx11') 'gpu.hpp', 'buffers.cpp', + 'commands.cpp', 'command_buffers.cpp', 'helpers.cpp', 'init.cpp', diff --git a/src/renderer/dx11/render_targets.cpp b/src/renderer/dx11/render_targets.cpp index d253231..31cfe54 100644 --- a/src/renderer/dx11/render_targets.cpp +++ b/src/renderer/dx11/render_targets.cpp @@ -23,13 +23,17 @@ rt_result InitRenderTargetManagement() { if (!_render_targets) { return RT_OUT_OF_MEMORY; } - + _lock = rtCreateMutex(); if (!_lock) { free(_render_targets); return RT_UNKNOWN_ERROR; } + _render_targets[1].rtv = g_gpu.swap_chain.rtv.Get(); + _render_targets[1].format = RT_PIXEL_FORMAT_B8G8R8A8_SRGB; + _render_targets[1].version = 1; + _first_free = _render_targets + 2; for (int i = 0; i < rt_Dx11MaxRenderTargets.i; ++i) { _render_targets[i].next_free = &_render_targets[i + 1]; @@ -66,6 +70,8 @@ RT_RENDERER_API_FN(CreateRenderTarget)(const rt_render_target_info *info) { return RT_INVALID_HANDLE; } + slot->format = info->format; + if (!rtIsDepthFormat(info->format)) { D3D11_TEXTURE2D_DESC tex_desc = {}; tex_desc.Width = info->width; diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 08843b8..956e178 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -123,9 +123,16 @@ RT_DLLEXPORT int rtAssertHandler(const char *expr, const char *msg, const char * } while (0) #define RT_ASSERT_ALWAYS_EVAL(x, msg) RT_ASSERT(x, msg) + +// Asserts if p is "false", evaluates to p +// NOTE that this will evaluate p multiple times! +#define RT_VERIFY(p) \ + ((!p) ? (RT_DEBUGBREAK, rtAssertHandler(#p, "Verify failed", __FILE__, __LINE__), p) : p) + #else -#define RT_ASSERT(x, msg) RT_UNUSED(x) +#define RT_ASSERT(x, msg) RT_UNUSED(x) #define RT_ASSERT_ALWAYS_EVAL(x, msg) (x) +#define RT_VERIFY(p) (p) #endif enum { From e7971e7bad60fc58c27f42e6bfd87160808f16fe Mon Sep 17 00:00:00 2001 From: Kevin Trogant Date: Thu, 4 Apr 2024 08:51:48 +0200 Subject: [PATCH 5/6] compiling shaders --- src/asset_compiler/dx11_shader_compiler.cpp | 89 +++++++++++++++++++++ src/asset_compiler/effect_processor.c | 7 ++ src/asset_compiler/meson.build | 7 ++ src/asset_compiler/shader_compiler.c | 22 ++++- src/gfx/gfx_main.c | 2 +- src/gfx/renderer_api.h | 1 + src/renderer/dx11/init.cpp | 6 -- src/renderer/dx11/meson.build | 4 +- src/renderer/dx11/render_targets.cpp | 3 +- 9 files changed, 129 insertions(+), 12 deletions(-) create mode 100644 src/asset_compiler/dx11_shader_compiler.cpp diff --git a/src/asset_compiler/dx11_shader_compiler.cpp b/src/asset_compiler/dx11_shader_compiler.cpp new file mode 100644 index 0000000..fca211d --- /dev/null +++ b/src/asset_compiler/dx11_shader_compiler.cpp @@ -0,0 +1,89 @@ +#include + +#include "shader_compiler.h" + +extern "C" rt_shader_bytecode CompileDX11Shader(rt_shader_stage stage, + rt_shader_optimization_level optimization, + rt_text_span code, + const char *file_path, + rt_arena *arena) { + rt_shader_bytecode bytecode = {}; + + D3D_SHADER_MACRO defines[] = { + {"RT_DX11", "1"}, + { NULL, NULL}, + }; + + LPCSTR target = NULL; + LPCSTR entrypoint = NULL; + switch (stage) { + case RT_SHADER_STAGE_VERTEX: + target = "vs_5_0"; + entrypoint = "VsMain"; + break; + case RT_SHADER_STAGE_FRAGMENT: + target = "ps_5_0"; + entrypoint = "PsMain"; + break; + case RT_SHADER_STAGE_COMPUTE: + target = "cs_5_0"; + entrypoint = "CsMain"; + break; + default: + rtLog("AC", "Tried to compile an invalid shader stage %u for %s", stage, file_path); + return bytecode; + } + + UINT flags = D3DCOMPILE_PARTIAL_PRECISION | D3DCOMPILE_WARNINGS_ARE_ERRORS; + switch (optimization) { + case RT_SHADER_OPTIMIZATION_NONE: + flags |= D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_OPTIMIZATION_LEVEL0; + break; + case RT_SHADER_OPTIMIZATION_SIZE: + flags |= D3DCOMPILE_OPTIMIZATION_LEVEL2; + break; + case RT_SHADER_OPTIMIZATION_SPEED: + flags |= D3DCOMPILE_OPTIMIZATION_LEVEL3; + break; + } +#ifdef RT_DEBUG + flags |= D3DCOMPILE_DEBUG; +#endif + ID3DBlob *bytes = nullptr, *error = nullptr; + if (FAILED(D3DCompile2(code.start, + code.length, + file_path, + defines, + D3D_COMPILE_STANDARD_FILE_INCLUDE, + entrypoint, + target, + flags, + 0, + 0, + nullptr, + 0, + &bytes, + &error))) { + if (error) { + rtLog("AC", + "Shader compilation failed (%s): %s",file_path, + (const char *)error->GetBufferPointer()); + error->Release(); + return bytecode; + } else { + rtLog("AC", "Shader compilation failed (%s): NO ERROR INFORMATION", file_path); + return bytecode; + } + } + + bytecode.bytes = rtArenaPush(arena, bytes->GetBufferSize()); + if (bytecode.bytes) { + bytecode.len = bytes->GetBufferSize(); + memcpy(bytecode.bytes, bytes->GetBufferPointer(), bytecode.len); + } else { + rtLog("AC", "Out of memory while compiling %s", file_path); + } + bytes->Release(); + + return bytecode; +} \ No newline at end of file diff --git a/src/asset_compiler/effect_processor.c b/src/asset_compiler/effect_processor.c index 0c23775..a220bed 100644 --- a/src/asset_compiler/effect_processor.c +++ b/src/asset_compiler/effect_processor.c @@ -62,6 +62,9 @@ static char *GenerateShaderName(rt_shader_type type, case RT_SHADER_TYPE_VULKAN: type_str = ":vk"; break; + case RT_SHADER_TYPE_DX11: + type_str = ":dx11"; + break; default: return NULL; } @@ -135,6 +138,8 @@ static rt_result ParseShader(rt_parse_state *state, rt_shader_type in_file_type = RT_SHADER_TYPE_INVALID; if (rtCompareSpanToString(shader->attribute, "vk") == 0) { in_file_type = RT_SHADER_TYPE_VULKAN; + } else if (rtCompareSpanToString(shader->attribute, "dx11") == 0) { + in_file_type = RT_SHADER_TYPE_DX11; } else { rtReportError("GFX", "Invalid renderer backend" @@ -230,6 +235,8 @@ static rt_result ParsePipeline(rt_parse_state *state, rt_shader_type type = RT_SHADER_TYPE_INVALID; if (strcmp(rt_Renderer.s, "vk") == 0) type = RT_SHADER_TYPE_VULKAN; + else if (strcmp(rt_Renderer.s, "dx11") == 0) + type = RT_SHADER_TYPE_DX11; if (type == RT_SHADER_TYPE_INVALID) { result = RT_ASSET_PROCESSING_FAILED; diff --git a/src/asset_compiler/meson.build b/src/asset_compiler/meson.build index 33f9b0d..50d417d 100644 --- a/src/asset_compiler/meson.build +++ b/src/asset_compiler/meson.build @@ -39,6 +39,13 @@ if get_option('enable_dxc_shader_compiler') ac_cargs += '-DRT_BUILD_DXC_SHADER_COMPILER' endif +if get_option('enable_dx11_shader_compiler') + ac_cargs += '-DRT_BUILD_DX11_SHADER_COMPILER' + ac_sources += ['dx11_shader_compiler.cpp'] + d3dcompiler_dep = declare_dependency(link_args : ['-lD3DCompiler']) + ac_deps += d3dcompiler_dep +endif + asset_compiler = static_library('asset_compiler', 'asset_compiler.h', 'description_parser.h', diff --git a/src/asset_compiler/shader_compiler.c b/src/asset_compiler/shader_compiler.c index ea90644..f2e3862 100644 --- a/src/asset_compiler/shader_compiler.c +++ b/src/asset_compiler/shader_compiler.c @@ -8,10 +8,18 @@ extern rt_shader_bytecode CompileVulkanShader(rt_shader_stage stage, rt_arena *arena); #endif +#ifdef RT_BUILD_DX11_SHADER_COMPILER +extern rt_shader_bytecode CompileDX11Shader(rt_shader_stage stage, + rt_shader_optimization_level optimization, + rt_text_span code, + const char *file_path, + rt_arena *arena); +#endif + static rt_shader_bytecode CompileNullShader(rt_shader_stage stage, rt_shader_optimization_level optimization, rt_text_span code, - const char *file_path, + const char *file_path, rt_arena *arena) { RT_UNUSED(stage); RT_UNUSED(optimization); @@ -24,8 +32,11 @@ static rt_shader_bytecode CompileNullShader(rt_shader_stage stage, }; } -typedef rt_shader_bytecode -shader_compiler_fn(rt_shader_stage, rt_shader_optimization_level, rt_text_span, const char *, rt_arena *); +typedef rt_shader_bytecode shader_compiler_fn(rt_shader_stage, + rt_shader_optimization_level, + rt_text_span, + const char *, + rt_arena *); static shader_compiler_fn *_compiler_funcs[RT_SHADER_TYPE_count] = { CompileNullShader, @@ -36,6 +47,11 @@ static shader_compiler_fn *_compiler_funcs[RT_SHADER_TYPE_count] = { CompileNullShader, #endif +#ifdef RT_BUILD_DX11_SHADER_COMPILER + CompileDX11Shader, +#else + CompileNullShader, +#endif }; rt_shader_bytecode CompileShader(rt_shader_type type, diff --git a/src/gfx/gfx_main.c b/src/gfx/gfx_main.c index fcebcfb..817c152 100644 --- a/src/gfx/gfx_main.c +++ b/src/gfx/gfx_main.c @@ -25,7 +25,7 @@ static bool _renderer_loaded = false; RT_DLLEXPORT RT_CVAR_S(rt_Renderer, "Select the render backend. Available options: [vk, null], Default: vk", - "vk"); + "dx11"); #ifdef RT_STATIC_LIB extern void RT_RENDERER_API_FN(RegisterCVars)(void); diff --git a/src/gfx/renderer_api.h b/src/gfx/renderer_api.h index fd14d43..77e1c2e 100644 --- a/src/gfx/renderer_api.h +++ b/src/gfx/renderer_api.h @@ -87,6 +87,7 @@ typedef struct { typedef enum { RT_SHADER_TYPE_INVALID, RT_SHADER_TYPE_VULKAN, + RT_SHADER_TYPE_DX11, RT_SHADER_TYPE_count, } rt_shader_type; diff --git a/src/renderer/dx11/init.cpp b/src/renderer/dx11/init.cpp index 25c2512..b10d76e 100644 --- a/src/renderer/dx11/init.cpp +++ b/src/renderer/dx11/init.cpp @@ -2,12 +2,6 @@ #pragma warning Building DX11 on non - windows is probably a mistake #endif -#pragma comment(lib, "d3d11.lib") -#pragma comment(lib, "dxgi.lib") -#pragma comment(lib, "d3dcompiler.lib") -#pragma comment(lib, "winmm.lib") -#pragma comment(lib, "dxguid.lib") - #include #include #include diff --git a/src/renderer/dx11/meson.build b/src/renderer/dx11/meson.build index e03fc5f..6f888d1 100644 --- a/src/renderer/dx11/meson.build +++ b/src/renderer/dx11/meson.build @@ -1,4 +1,6 @@ if get_option('build_dx11') + dx11_dep = declare_dependency(link_args: ['-ld3d11', '-ldxgi', '-lwinmm', '-ldxguid']) + dx11_renderer_lib = library('rtdx11', # Project Sources 'device_objects.hpp', @@ -11,7 +13,7 @@ if get_option('build_dx11') 'init.cpp', 'render_targets.cpp', - dependencies : [m_dep, windowing_dep], + dependencies : [m_dep, windowing_dep, dx11_dep], include_directories : [engine_incdir, contrib_incdir], link_with : [runtime_lib], cpp_pch : 'pch/dx11_pch.h', diff --git a/src/renderer/dx11/render_targets.cpp b/src/renderer/dx11/render_targets.cpp index 31cfe54..24c6bad 100644 --- a/src/renderer/dx11/render_targets.cpp +++ b/src/renderer/dx11/render_targets.cpp @@ -42,7 +42,8 @@ rt_result InitRenderTargetManagement() { } void ShutdownRenderTargetManagement() { - for (int i = 0; i < rt_Dx11MaxRenderTargets.i; ++i) { + // Swapchain rtv in slot 1 will be released elsewhere + for (int i = 2; i < rt_Dx11MaxRenderTargets.i; ++i) { if (_render_targets[i].rtv) _render_targets[i].rtv->Release(); if (_render_targets[i].dsv) From 4d575dc229af53bb9fa9930d81c538a68b560a92 Mon Sep 17 00:00:00 2001 From: Kevin Trogant Date: Thu, 4 Apr 2024 20:01:23 +0200 Subject: [PATCH 6/6] pipelines --- assets/shader/static_object.effect | 34 +++++ meson_options.txt | 3 +- src/asset_compiler/shader_compiler.c | 2 +- src/renderer/dx11/device_objects.hpp | 31 +++- src/renderer/dx11/init.cpp | 9 -- src/renderer/dx11/pipelines.cpp | 217 +++++++++++++++++++++++++++ 6 files changed, 279 insertions(+), 17 deletions(-) create mode 100644 src/renderer/dx11/pipelines.cpp diff --git a/assets/shader/static_object.effect b/assets/shader/static_object.effect index 9895f2e..fc4ec34 100644 --- a/assets/shader/static_object.effect +++ b/assets/shader/static_object.effect @@ -14,6 +14,25 @@ struct VSOutput float4 Pos : SV_POSITION; }; +VSOutput VsMain(VSInput input, uint vertexIndex : SV_VertexID) { + VSOutput output = (VSOutput)0; + return output; +} + END + + + dx11 BEGIN +#include "common.hlsl" + +struct VSInput +{ +}; + +struct VSOutput +{ + float4 Pos : SV_POSITION; +}; + VSOutput VsMain(VSInput input, uint vertexIndex : SV_VertexID) { VSOutput output = (VSOutput)0; return output; @@ -27,6 +46,21 @@ struct PSOutput { float4 Color : SV_TARGET0; }; +PSOutput PsMain(void) { + PSOutput output = (PSOutput)0; + output.Color[0] = 0; + output.Color[1] = 0; + output.Color[2] = 0; + output.Color[3] = 0; + return output; +} + END + + dx11 BEGIN +struct PSOutput { +float4 Color : SV_TARGET0; +}; + PSOutput PsMain(void) { PSOutput output = (PSOutput)0; output.Color[0] = 0; diff --git a/meson_options.txt b/meson_options.txt index a3b7d6e..b7fd66d 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -2,5 +2,6 @@ option('static_renderer', type : 'string', value : 'vk', description : 'Name of option('use_xlib', type : 'boolean', value : false, description : 'Use Xlib for window creation under linux') option('error_report_debugbreak', type : 'boolean', value : true, description : 'Debugbreak in ReportError') option('enable_dxc_shader_compiler', type : 'boolean', value : true, description : 'Enables building the dxc-based shader compiler.') +option('enable_dx11_shader_compiler', type : 'boolean', value : true, description : 'Enables building the dx11-bases shader compiler.') option('game_as_subdir', type : 'boolean', value : false, description : 'If true, adds the directory "src/game" to the build.') -option('build_dx11', type : 'boolean', value : true, description : 'Enables/disables the build of the dx11 renderer.') +option('build_dx11', type : 'boolean', value : true, description : 'Enables/disables the build of the dx11 renderer.') \ No newline at end of file diff --git a/src/asset_compiler/shader_compiler.c b/src/asset_compiler/shader_compiler.c index f2e3862..629f54a 100644 --- a/src/asset_compiler/shader_compiler.c +++ b/src/asset_compiler/shader_compiler.c @@ -65,4 +65,4 @@ rt_shader_bytecode CompileShader(rt_shader_type type, return (rt_shader_bytecode){.bytes = NULL, .len = 0}; } return _compiler_funcs[type](stage, optimization, code, file_path, arena); -} \ No newline at end of file +} diff --git a/src/renderer/dx11/device_objects.hpp b/src/renderer/dx11/device_objects.hpp index dd8bed3..b5c2ec0 100644 --- a/src/renderer/dx11/device_objects.hpp +++ b/src/renderer/dx11/device_objects.hpp @@ -17,24 +17,24 @@ struct rt_render_target { rt_pixel_format format; - bool HasStencilComponent() const { + uint32_t version; + rt_render_target *next_free; + + RT_INLINE bool HasStencilComponent() const { return format == RT_PIXEL_FORMAT_DEPTH24_STENCIL8; } - bool IsColorRenderTarget() const { + RT_INLINE bool IsColorRenderTarget() const { RT_ASSERT(!(rtv != nullptr && dsv != nullptr), "A render target should not contain a render target and a depth stencil view"); return rtv != nullptr; } - bool IsDepthStencilTarget() const { + RT_INLINE bool IsDepthStencilTarget() const { RT_ASSERT(!(rtv != nullptr && dsv != nullptr), "A render target should not contain a render target and a depth stencil view"); return dsv != nullptr; } - - uint32_t version; - rt_render_target *next_free; }; struct rt_command_buffer { @@ -55,8 +55,27 @@ struct rt_buffer { rt_buffer *next_free; }; +struct rt_pipeline { + ID3D11InputLayout *input_layout; + ID3D11VertexShader *vertex_shader; + ID3D11PixelShader *pixel_shader; + + ID3D11ComputeShader *compute_shader; + + + rt_pipeline *next_free; + uint32_t version; + + RT_INLINE bool IsComputePipeline() const { + RT_ASSERT(!(compute_shader && (vertex_shader || pixel_shader)), + "A pipeline should contain either a compute shader or graphics shaders."); + return compute_shader != nullptr; + } +}; + rt_render_target *rtGetRenderTarget(rt_render_target_handle handle); rt_command_buffer *rtGetCommandBuffer(rt_command_buffer_handle handle); rt_buffer *rtGetBuffer(rt_buffer_handle handle); +rt_pipeline *rtGetPipeline(rt_pipeline_handle handle); #endif \ No newline at end of file diff --git a/src/renderer/dx11/init.cpp b/src/renderer/dx11/init.cpp index b10d76e..a07c878 100644 --- a/src/renderer/dx11/init.cpp +++ b/src/renderer/dx11/init.cpp @@ -253,15 +253,6 @@ extern "C" { #define RETURN_HANDLE_ARRAY_STUB(out, count) RETURN_HANDLE_ARRAY_STUB2(out, count, 1) -rt_pipeline_handle RT_RENDERER_API_FN(CompilePipeline)(const rt_pipeline_info *info) { - RT_UNUSED(info); - RETURN_HANDLE_STUB(rt_pipeline_handle); -} - -void RT_RENDERER_API_FN(DestroyPipeline)(rt_pipeline_handle handle) { - RT_UNUSED(handle); -} - rt_result RT_RENDERER_API_FN(CreateSemaphores)(uint32_t count, const rt_gpu_semaphore_info *info, rt_gpu_semaphore_handle *p_semaphores) { diff --git a/src/renderer/dx11/pipelines.cpp b/src/renderer/dx11/pipelines.cpp new file mode 100644 index 0000000..2b84e0a --- /dev/null +++ b/src/renderer/dx11/pipelines.cpp @@ -0,0 +1,217 @@ +#include +#include + +#include "gfx/effect.h" +#include "gfx/renderer_api.h" +#include "runtime/config.h" +#include "runtime/handles.h" +#include "runtime/mem_arena.h" +#include "runtime/threading_helpers.hpp" + +#include "device_objects.hpp" +#include "gpu.hpp" + +RT_CVAR_I(rt_Dx11MaxPipelines, + "Maximum number of simultaneously existing pipelines. Default: 128", + 128); + +static rt_pipeline *_pipelines; +static rt_pipeline *_first_free; +static rt_mutex *_lock; + +rt_result InitPipelineManagement() { + _pipelines = + reinterpret_cast(calloc((size_t)rt_Dx11MaxPipelines.i, sizeof(rt_pipeline))); + if (!_pipelines) + return RT_OUT_OF_MEMORY; + + _first_free = _pipelines + 1; + for (int i = 0; i < rt_Dx11MaxPipelines.i - 1; ++i) + _pipelines[i].next_free = &_pipelines[i + 1]; + + _lock = rtCreateMutex(); + if (!_lock) { + free(_pipelines); + return RT_UNKNOWN_ERROR; + } + + return RT_SUCCESS; +} + +void ShutdownPipelineManagement() { + for (int i = 0; i < rt_Dx11MaxPipelines.i; ++i) { + if (_pipelines[i].compute_shader) + _pipelines[i].compute_shader->Release(); + if (_pipelines[i].vertex_shader) + _pipelines[i].vertex_shader->Release(); + if (_pipelines[i].pixel_shader) + _pipelines[i].pixel_shader->Release(); + if (_pipelines[i].input_layout) + _pipelines[i].input_layout->Release(); + } + free(_pipelines); + rtDestroyMutex(_lock); +} + +rt_result GetShader(rt_resource_id id, rt_shader_info **p_shader, rt_arena *arena) { + size_t shader_size = rtGetResourceSize(id); + if (shader_size == 0) + return RT_INVALID_VALUE; + void *buffer = rtArenaPush(arena, shader_size); + if (!buffer) + return RT_OUT_OF_MEMORY; + rt_result res = rtGetResource(id, buffer); + if (res != RT_SUCCESS) { + rtArenaPop(arena, shader_size); + return res; + } + + rt_resource *resource = reinterpret_cast(buffer); + RT_ASSERT(resource->type == RT_RESOURCE_SHADER, "Expected a shader"); + *p_shader = reinterpret_cast(resource->data); + return RT_SUCCESS; +} + +extern "C" rt_pipeline_handle RT_RENDERER_API_FN(CompilePipeline)(const rt_pipeline_info *info) { + rt_pipeline *slot = nullptr; + { + auto lg = rtAutoLock(_lock); + + slot = _first_free; + if (slot) + _first_free = slot->next_free; + } + if (!slot) { + rtLog("dx11", "Could not create pipeline, because no slots are available."); + return RT_INVALID_HANDLE; + } + + rt_temp_arena temp = rtGetTemporaryArena(NULL, 0); + + if (info->vertex_shader != RT_INVALID_RESOURCE_ID) { + rt_shader_info *vs; + if (GetShader(info->vertex_shader, &vs, temp.arena) != RT_SUCCESS) { + rtReportError("dx11", "Could not retrieve vertex shader data."); + auto lg = rtAutoLock(_lock); + slot->next_free = _first_free; + _first_free = slot; + return RT_INVALID_HANDLE; + } + + void *bytecode = rtResolveRelptr(&vs->bytecode); + if (FAILED(g_gpu.device->CreateVertexShader(bytecode, + vs->bytecode_length, + NULL, + &slot->vertex_shader))) { + rtReportError("dx11", "Vertex shader creation failed"); + auto lg = rtAutoLock(_lock); + slot->next_free = _first_free; + _first_free = slot; + return RT_INVALID_HANDLE; + } + + // TODO: effects should specify the expected vertex layout + // For now, we use a default + /* clang-format off */ + D3D11_INPUT_ELEMENT_DESC default_layout[] = { + {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"TANGENT", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0}, + {"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0}, + }; + /* clang-format on */ + + if (FAILED(g_gpu.device->CreateInputLayout(default_layout, + RT_ARRAY_COUNT(default_layout), + bytecode, + vs->bytecode_length, + &slot->input_layout))) { + rtReportError("dx11", "Failed to create the vertex layout."); + auto lg = rtAutoLock(_lock); + slot->next_free = _first_free; + _first_free = slot; + return RT_INVALID_HANDLE; + } + } + + if (info->fragment_shader != RT_INVALID_RESOURCE_ID) { + rt_shader_info *vs; + if (GetShader(info->fragment_shader, &vs, temp.arena) != RT_SUCCESS) { + rtReportError("dx11", "Could not retrieve fragment shader data."); + auto lg = rtAutoLock(_lock); + slot->next_free = _first_free; + _first_free = slot; + return RT_INVALID_HANDLE; + } + + void *bytecode = rtResolveRelptr(&vs->bytecode); + if (FAILED(g_gpu.device->CreatePixelShader(bytecode, + vs->bytecode_length, + NULL, + &slot->pixel_shader))) { + rtReportError("dx11", "Fragment shader creation failed"); + auto lg = rtAutoLock(_lock); + slot->next_free = _first_free; + _first_free = slot; + return RT_INVALID_HANDLE; + } + } + + if (info->compute_shader != RT_INVALID_RESOURCE_ID) { + rt_shader_info *vs; + if (GetShader(info->compute_shader, &vs, temp.arena) != RT_SUCCESS) { + rtReportError("dx11", "Could not retrieve compute shader data."); + auto lg = rtAutoLock(_lock); + slot->next_free = _first_free; + _first_free = slot; + return RT_INVALID_HANDLE; + } + + void *bytecode = rtResolveRelptr(&vs->bytecode); + if (FAILED(g_gpu.device->CreateComputeShader(bytecode, + vs->bytecode_length, + NULL, + &slot->compute_shader))) { + rtReportError("dx11", "Compute shader creation failed"); + auto lg = rtAutoLock(_lock); + slot->next_free = _first_free; + _first_free = slot; + return RT_INVALID_HANDLE; + } + } + + slot->version = (slot->version + 1) % RT_RENDER_BACKEND_HANDLE_MAX_VERSION; + uint32_t index = static_cast(slot - _pipelines); + return {slot->version, index}; +} + +extern "C" void RT_RENDERER_API_FN(DestroyPipeline)(rt_pipeline_handle handle) { + if (!RT_IS_HANDLE_VALID(handle) || (int)handle.index >= rt_Dx11MaxPipelines.i) + return; + auto lg = rtAutoLock(_lock); + if (handle.version != _pipelines[handle.index].version) + return; + + if (_pipelines[handle.index].compute_shader) + _pipelines[handle.index].compute_shader->Release(); + if (_pipelines[handle.index].vertex_shader) + _pipelines[handle.index].vertex_shader->Release(); + if (_pipelines[handle.index].pixel_shader) + _pipelines[handle.index].pixel_shader->Release(); + if (_pipelines[handle.index].input_layout) + _pipelines[handle.index].input_layout->Release(); + + _pipelines[handle.index].next_free = _first_free; + _pipelines[handle.index].version = + (_pipelines[handle.index].version + 1) % RT_RENDER_BACKEND_HANDLE_MAX_VERSION; + _first_free = &_pipelines[handle.index]; +} + +rt_pipeline *rtGetPipeline(rt_pipeline_handle handle) { + if (!RT_IS_HANDLE_VALID(handle) || (int)handle.index >= rt_Dx11MaxPipelines.i) + return nullptr; + auto lg = rtAutoLock(_lock); + if (handle.version != _pipelines[handle.index].version) + return nullptr; + return &_pipelines[handle.index]; +}