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.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..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.') \ No newline at end of file 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..629f54a 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, @@ -49,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/game/main.c b/src/game/main.c index 030fb12..2ffdeaa 100644 --- a/src/game/main.c +++ b/src/game/main.c @@ -13,29 +13,7 @@ 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); -} +static rt_render_target_handle _rt; /* Called after the runtime has finished its initialization and before entering the main-loop*/ void Init(void) { @@ -44,56 +22,13 @@ void Init(void) { 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 + _rt = g_renderer.GetSwapchainRenderTarget(); } /* 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 +36,20 @@ void Update(unsigned int frame_id) { } void Render(unsigned int frame_id) { - rtExecuteFramegraph(_framegraph, 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/gfx/gfx.h b/src/gfx/gfx.h index 8decd98..2e58217 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/gfx_main.c b/src/gfx/gfx_main.c index 22fd29e..c72bddf 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 c7dce10..a5cc3ac 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) \ @@ -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/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/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/command_buffers.cpp b/src/renderer/dx11/command_buffers.cpp new file mode 100644 index 0000000..b63821a --- /dev/null +++ b/src/renderer/dx11/command_buffers.cpp @@ -0,0 +1,140 @@ +#include +#include + +#include "gfx/renderer_api.h" +#include "runtime/config.h" +#include "runtime/handles.h" +#include "runtime/threading.h" +#include "runtime/threading_helpers.hpp" + +#include "device_objects.hpp" +#include "gpu.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; + } + +#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(); + } + + 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/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 new file mode 100644 index 0000000..b5c2ec0 --- /dev/null +++ b/src/renderer/dx11/device_objects.hpp @@ -0,0 +1,81 @@ +#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; + + rt_pixel_format format; + + uint32_t version; + rt_render_target *next_free; + + RT_INLINE bool HasStencilComponent() const { + return format == RT_PIXEL_FORMAT_DEPTH24_STENCIL8; + } + + 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; + } + + 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; + } +}; + +struct rt_command_buffer { + // Only created once and then re-used. + ID3D11DeviceContext1 *context; + ID3DUserDefinedAnnotation *annotation; + + uint32_t version; + 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; +}; + +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/gpu.hpp b/src/renderer/dx11/gpu.hpp new file mode 100644 index 0000000..43595bc --- /dev/null +++ b/src/renderer/dx11/gpu.hpp @@ -0,0 +1,41 @@ +#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; + +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; + + 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 new file mode 100644 index 0000000..a07c878 --- /dev/null +++ b/src/renderer/dx11/init.cpp @@ -0,0 +1,283 @@ +#ifndef _WIN32 +#pragma warning Building DX11 on non - windows is probably a mistake +#endif + +#include +#include +#include + +#include "gfx/renderer_api.h" +#include "runtime/config.h" + +#define DONT_DEFINE_RENDERER_GLOBAL +#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.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 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}; + 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.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; + res = InitBufferManagement(); + if (res != RT_SUCCESS) + return res; + + return RT_SUCCESS; +} + +extern "C" void RT_RENDERER_API_FN(Shutdown)(void) { + ShutdownBufferManagement(); + 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) { + RT_UNUSED(frame_id); + FLOAT clear_color[4] = { + 0, + 0, + 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 + +extern "C" { + +#define RETURN_HANDLE_STUB2(type, initial) \ + static unsigned int s_next = (initial); \ + s_next = (s_next + 1) % RT_RENDER_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_RENDER_BACKEND_HANDLE_MAX_INDEX; \ + (out)[i].version = 1; \ + } + +#define RETURN_HANDLE_ARRAY_STUB(out, count) RETURN_HANDLE_ARRAY_STUB2(out, count, 1) + +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}; +} +} \ 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..6f888d1 --- /dev/null +++ b/src/renderer/dx11/meson.build @@ -0,0 +1,25 @@ +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', + 'gpu.hpp', + + 'buffers.cpp', + 'commands.cpp', + 'command_buffers.cpp', + 'helpers.cpp', + 'init.cpp', + 'render_targets.cpp', + + dependencies : [m_dep, windowing_dep, dx11_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 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]; +} diff --git a/src/renderer/dx11/render_targets.cpp b/src/renderer/dx11/render_targets.cpp new file mode 100644 index 0000000..24c6bad --- /dev/null +++ b/src/renderer/dx11/render_targets.cpp @@ -0,0 +1,173 @@ +#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; + } + + _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]; + } + return RT_SUCCESS; +} + +void ShutdownRenderTargetManagement() { + // 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) + _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; + } + + slot->format = info->format; + + 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 402967c..c4cb024 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..956e178 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -121,8 +121,18 @@ 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 { 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