diff --git a/assets/test.framegraph b/assets/test.framegraph index d05a895..1b11095 100644 --- a/assets/test.framegraph +++ b/assets/test.framegraph @@ -15,8 +15,8 @@ render_targets { depth0 { format DEPTH24_STENCIL8; - width 512; - height 384; + width 1024; + height 768; sample_count 4; } } diff --git a/meson.build b/meson.build index 707003f..5f8cc57 100644 --- a/meson.build +++ b/meson.build @@ -75,12 +75,12 @@ subdir('src') engine_link_libs = [] if get_option('default_library') == 'static' if get_option('static_renderer') == 'vk' - engine_link_libs = [runtime_lib, vk_renderer_lib] + engine_link_libs = [runtime_lib, gfx_lib, app_lib, vk_renderer_lib] else error('Invalid static_renderer option ', get_option('static_renderer')) endif else - engine_link_libs = [runtime_lib] + engine_link_libs = [runtime_lib, gfx_lib, app_lib] endif # Unit/Integration test driver diff --git a/src/runtime/app.c b/src/app_framework/app.c similarity index 98% rename from src/runtime/app.c rename to src/app_framework/app.c index b307b14..fb15c37 100644 --- a/src/runtime/app.c +++ b/src/app_framework/app.c @@ -1,11 +1,13 @@ #include "app.h" -#include "aio.h" -#include "buffer_manager.h" -#include "config.h" -#include "gfx.h" -#include "renderer_api.h" #include "main_loop.h" +#include "runtime/aio.h" +#include "runtime/buffer_manager.h" +#include "runtime/config.h" + +#include "gfx/gfx.h" +#include "gfx/renderer_api.h" + #include RT_CVAR_I(rt_Fullscreen, "Show window in fullscreen mode. [0/1] Default: 0", 0); diff --git a/src/runtime/app.h b/src/app_framework/app.h similarity index 97% rename from src/runtime/app.h rename to src/app_framework/app.h index ebe3446..77a0da8 100644 --- a/src/runtime/app.h +++ b/src/app_framework/app.h @@ -3,7 +3,7 @@ /* Platform specific application entry point */ -#include "runtime.h" +#include "runtime/runtime.h" #include "main_loop.h" #ifdef __cplusplus diff --git a/src/runtime/main_loop.c b/src/app_framework/main_loop.c similarity index 97% rename from src/runtime/main_loop.c rename to src/app_framework/main_loop.c index c070890..68e1c6f 100644 --- a/src/runtime/main_loop.c +++ b/src/app_framework/main_loop.c @@ -1,8 +1,9 @@ #define RT_DONT_DEFINE_MAIN_LOOP_GLOBAL #include "main_loop.h" -#include "runtime.h" -#include "config.h" -#include "gfx.h" +#include "runtime/runtime.h" +#include "runtime/config.h" + +#include "gfx/gfx.h" RT_CVAR_I(rt_MaxFrameLatency, "Maximum latency between update and rendering. Default: 2", 2); diff --git a/src/runtime/main_loop.h b/src/app_framework/main_loop.h similarity index 92% rename from src/runtime/main_loop.h rename to src/app_framework/main_loop.h index 8bb3771..6f369b6 100644 --- a/src/runtime/main_loop.h +++ b/src/app_framework/main_loop.h @@ -1,8 +1,8 @@ #ifndef RT_MAIN_LOOP_H #define RT_MAIN_LOOP_H -#include "runtime.h" -#include "threading.h" +#include "runtime/runtime.h" +#include "runtime/threading.h" typedef void rt_main_loop_update_fn(void); typedef void rt_main_loop_render_fn(void); diff --git a/src/app_framework/meson.build b/src/app_framework/meson.build new file mode 100644 index 0000000..83be91c --- /dev/null +++ b/src/app_framework/meson.build @@ -0,0 +1,16 @@ +app_deps = [thread_dep, m_dep, windowing_dep] +app_lib = library('rtapp', + # Project Sources + 'app.h', + 'main_loop.h', + + 'app.c', + 'main_loop.c', + dependencies : app_deps, + include_directories : engine_incdir, + link_with : [runtime_lib, gfx_lib], + c_pch : 'pch/app_pch.h', + install : true) + +engine_libs += app_lib +engine_lib_paths += app_lib.full_path() diff --git a/src/app_framework/pch/app_pch.h b/src/app_framework/pch/app_pch.h new file mode 100644 index 0000000..aacdde5 --- /dev/null +++ b/src/app_framework/pch/app_pch.h @@ -0,0 +1 @@ +#include "runtime/runtime.h" diff --git a/src/asset_compiler/framegraph_processor.c b/src/asset_compiler/framegraph_processor.c index 9453449..db3ccd8 100644 --- a/src/asset_compiler/framegraph_processor.c +++ b/src/asset_compiler/framegraph_processor.c @@ -2,7 +2,7 @@ #include "description_parser.h" #include "runtime/buffer_manager.h" -#include "runtime/gfx.h" +#include "gfx/gfx.h" #include #include @@ -559,11 +559,11 @@ static rt_result ParseFramegraph(const char *text, } if (rtCompareSpanToString(mode_stmt->value, "SAMPLED") == 0) { read->mode = RT_RENDER_TARGET_READ_SAMPLED; - } else if (rtCompareSpanToString(mode_stmt->value, "INPUT_ATTACHMENT") == 0) { - read->mode = RT_RENDER_TARGET_READ_INPUT_ATTACHMENT; + } else if (rtCompareSpanToString(mode_stmt->value, "DIRECT") == 0) { + read->mode = RT_RENDER_TARGET_READ_DIRECT; } else { rtLog("AC", - "Expected SAMPLED or INPUT_ATTACHMENT as the value of " + "Expected SAMPLED or DIRECT as the value of " "\"passes.%.*s.writes.%.*s.mode\" in %s", pass_stmt->attribute.length, pass_stmt->attribute.start, diff --git a/src/asset_compiler/pipeline_processor.c b/src/asset_compiler/pipeline_processor.c index db32faf..7d61d90 100644 --- a/src/asset_compiler/pipeline_processor.c +++ b/src/asset_compiler/pipeline_processor.c @@ -4,10 +4,11 @@ #include "runtime/buffer_manager.h" #include "runtime/config.h" -#include "runtime/gfx.h" #include "runtime/mem_arena.h" #include "runtime/runtime.h" +#include "gfx/gfx.h" + #include #include #include diff --git a/src/asset_compiler/shader_compiler.h b/src/asset_compiler/shader_compiler.h index 3883012..2168357 100644 --- a/src/asset_compiler/shader_compiler.h +++ b/src/asset_compiler/shader_compiler.h @@ -3,7 +3,7 @@ #include "runtime/runtime.h" #include "runtime/mem_arena.h" -#include "runtime/renderer_api.h" +#include "gfx/renderer_api.h" #ifdef __cplusplus extern "C" { diff --git a/src/game/entry.c b/src/game/entry.c index b151aff..7e09b8a 100644 --- a/src/game/entry.c +++ b/src/game/entry.c @@ -1,4 +1,4 @@ -#include "runtime/app.h" +#include "app_framework/app.h" extern void RegisterCVars(void); extern void Init(void); diff --git a/src/game/main.c b/src/game/main.c index 92fb21a..d399900 100644 --- a/src/game/main.c +++ b/src/game/main.c @@ -1,8 +1,10 @@ -#include "runtime/gfx.h" #include "runtime/mem_arena.h" #include "runtime/resources.h" #include "runtime/threading.h" +#include "gfx/gfx.h" +#include "gfx/renderer_api.h" + #include "asset_compiler/asset_compiler.h" void RegisterCVars(void) { diff --git a/src/runtime/gfx.h b/src/gfx/gfx.h similarity index 98% rename from src/runtime/gfx.h rename to src/gfx/gfx.h index d4edd70..a71f6ea 100644 --- a/src/runtime/gfx.h +++ b/src/gfx/gfx.h @@ -11,7 +11,7 @@ #include -#include "runtime.h" +#include "runtime/runtime.h" #ifdef __cplusplus extern "C" { @@ -88,7 +88,7 @@ typedef struct { typedef enum { RT_RENDER_TARGET_READ_SAMPLED, - RT_RENDER_TARGET_READ_INPUT_ATTACHMENT, + RT_RENDER_TARGET_READ_DIRECT, RT_RENDER_TARGET_READ_count, } rt_render_target_read_mode; diff --git a/src/runtime/gfx_framegraph.c b/src/gfx/gfx_framegraph.c similarity index 73% rename from src/runtime/gfx_framegraph.c rename to src/gfx/gfx_framegraph.c index dfb9020..092598f 100644 --- a/src/runtime/gfx_framegraph.c +++ b/src/gfx/gfx_framegraph.c @@ -1,10 +1,11 @@ -#include "config.h" #include "gfx.h" -#include "handles.h" -#include "hashing.h" -#include "mem_arena.h" #include "renderer_api.h" -#include "threading.h" + +#include "runtime/config.h" +#include "runtime/handles.h" +#include "runtime/hashing.h" +#include "runtime/mem_arena.h" +#include "runtime/threading.h" #include #include @@ -250,8 +251,16 @@ CreateRenderTargets(rt_framegraph *graph, const rt_framegraph_info *info, rt_are graph->render_targets[i].width = render_targets[i].width; graph->render_targets[i].height = render_targets[i].height; graph->render_targets[i].sample_count = render_targets[i].sample_count; - graph->render_targets[i].api_render_target = - g_renderer.CreateRenderTarget(&render_targets[i]); + + if (graph->render_targets[i].width != RT_RENDER_TARGET_SIZE_SWAPCHAIN || + graph->render_targets[i].height != RT_RENDER_TARGET_SIZE_SWAPCHAIN || + graph->render_targets[i].format != RT_PIXEL_FORMAT_SWAPCHAIN) { + + graph->render_targets[i].api_render_target = + g_renderer.CreateRenderTarget(&render_targets[i]); + } else { + graph->render_targets[i].api_render_target = g_renderer.GetSwapchainRenderTarget(); + } if (!RT_IS_HANDLE_VALID(graph->render_targets[i].api_render_target)) { rtReportError("GFX", "Failed to create render target %u of framegraph.", i); for (uint32_t j = 0; j < i; ++j) @@ -259,6 +268,7 @@ CreateRenderTargets(rt_framegraph *graph, const rt_framegraph_info *info, rt_are goto out; } } + graph->render_target_count = info->render_target_count; result = true; out: @@ -386,6 +396,110 @@ RT_DLLEXPORT void rtBindRenderPass(rt_framegraph *framegraph, rtLog("GFX", "Tried to bind functions to unknown render pass %x", id); } +static bool IsDepthFormat(rt_pixel_format format) { + return format == RT_PIXEL_FORMAT_DEPTH32 || format == RT_PIXEL_FORMAT_DEPTH24_STENCIL8; +} + +static rt_render_target *GetRenderTarget(rt_framegraph *framegraph, rt_render_target_id id) { + for (uint32_t i = 0; i < framegraph->render_target_count; ++i) { + if (framegraph->render_targets[i].id == id) + return &framegraph->render_targets[i]; + } + return NULL; +} + +static void +BeginPass(rt_framegraph *framegraph, uint32_t pass_idx, rt_command_buffer_handle cmdbuf) { + const rt_render_target_write *writes = framegraph->passes[pass_idx].writes; + const rt_render_target_read *reads = framegraph->passes[pass_idx].reads; + uint32_t write_count = framegraph->passes[pass_idx].write_count; + uint32_t read_count = framegraph->passes[pass_idx].read_count; + + /* Convert reads and writes into the pass begin info for the renderer */ + rt_cmd_begin_pass_info begin_info; + memset(&begin_info, 0, sizeof(begin_info)); + + /* All written render targets need to have the same size */ + if (write_count > 0) { + rt_render_target *rt = GetRenderTarget(framegraph, writes[0].render_target); + RT_ASSERT(rt != NULL, "Invalid render target in pass write."); + begin_info.render_area = (rt_rect2i){ + .offset = { 0, 0}, + .size = {.x = rt->width, .y = rt->height} + }; + } + + for (uint32_t i = 0; i < write_count; ++i) { + rt_render_target *rt = GetRenderTarget(framegraph, writes[i].render_target); + RT_ASSERT(rt != NULL, "Invalid render target in pass write."); + + g_renderer.CmdTransitionRenderTarget(cmdbuf, + rt->api_render_target, + RT_RENDER_TARGET_STATE_ATTACHMENT); + + if (!IsDepthFormat(rt->format)) { + /* Add as color buffer */ + uint32_t cbidx = begin_info.color_buffer_count; + RT_ASSERT(cbidx < 4, "Maximum of 4 colorbuffers per pass exceeded."); + begin_info.color_buffers[cbidx] = rt->api_render_target; + if ((writes[i].flags & RT_RENDER_TARGET_WRITE_CLEAR) != 0) { + begin_info.color_buffer_loads[cbidx] = RT_PASS_LOAD_MODE_CLEAR; + begin_info.color_buffer_clear_values[cbidx].color = writes[i].clear.color; + } else { + begin_info.color_buffer_loads[cbidx] = RT_PASS_LOAD_MODE_LOAD; + } + if ((writes[i].flags & RT_RENDER_TARGET_WRITE_DISCARD) != 0) { + begin_info.color_buffer_writes[cbidx] = RT_PASS_WRITE_MODE_DISCARD; + } else { + begin_info.color_buffer_writes[cbidx] = RT_PASS_WRITE_MODE_STORE; + } + ++begin_info.color_buffer_count; + } else { + /* Add as depth buffer*/ + RT_ASSERT(!RT_IS_HANDLE_VALID(begin_info.depth_stencil_buffer), + "Only one depth/stencil buffer can be set!"); + begin_info.depth_stencil_buffer = rt->api_render_target; + if ((writes[i].flags & RT_RENDER_TARGET_WRITE_CLEAR) != 0) { + begin_info.depth_stencil_buffer_load = RT_PASS_LOAD_MODE_CLEAR; + begin_info.depth_stencil_buffer_clear_value.depth_stencil.depth = + writes[i].clear.depth_stencil.depth; + begin_info.depth_stencil_buffer_clear_value.depth_stencil.stencil = + writes[i].clear.depth_stencil.stencil; + } else { + begin_info.depth_stencil_buffer_load = RT_PASS_LOAD_MODE_LOAD; + } + if ((writes[i].flags & RT_RENDER_TARGET_WRITE_DISCARD) != 0) { + begin_info.depth_stencil_buffer_write = RT_PASS_WRITE_MODE_DISCARD; + } else { + begin_info.depth_stencil_buffer_write = RT_PASS_WRITE_MODE_STORE; + } + } + } + + for (uint32_t i = 0; i < read_count; ++i) { + rt_render_target *rt = GetRenderTarget(framegraph, reads[i].render_target); + RT_ASSERT(rt != NULL, "Invalid render target in pass read."); + /* We need to transition the render target */ + + switch (reads[i].mode) { + case RT_RENDER_TARGET_READ_SAMPLED: + g_renderer.CmdTransitionRenderTarget(cmdbuf, + rt->api_render_target, + RT_RENDER_TARGET_STATE_SAMPLED_IMAGE); + break; + case RT_RENDER_TARGET_READ_DIRECT: + g_renderer.CmdTransitionRenderTarget(cmdbuf, + rt->api_render_target, + RT_RENDER_TARGET_STATE_STORAGE_IMAGE); + break; + default: + RT_ASSERT(0, "Invalid render target read mode"); + } + } + + g_renderer.CmdBeginPass(cmdbuf, &begin_info); +} + RT_DLLEXPORT void rtExecuteFramegraph(rt_framegraph *framegraph) { int execution_level = framegraph->passes[0].execution_level; uint32_t level_start = 0; @@ -413,6 +527,20 @@ RT_DLLEXPORT void rtExecuteFramegraph(rt_framegraph *framegraph) { /* TODO(Kevin): Every one of these should be a job-dispatch*/ + rt_alloc_command_buffer_info cmdbuf_alloc = { + .target_queue = RT_GRAPHICS_QUEUE, + }; + rt_command_buffer_handle cmdbuf; + if (g_renderer.AllocCommandBuffers(1, &cmdbuf_alloc, &cmdbuf) != RT_SUCCESS) { + rtLog("GFX", + "Failed to allocate a command buffer for framegraph pass %u (%x)", + pass_idx, + framegraph->passes[pass_idx].id); + continue; + } + + BeginPass(framegraph, pass_idx, cmdbuf); + framegraph->passes[pass_idx].bound_fns.Prepare(id, writes, write_count, @@ -428,6 +556,12 @@ RT_DLLEXPORT void rtExecuteFramegraph(rt_framegraph *framegraph) { write_count, reads, read_count); + + g_renderer.CmdEndPass(cmdbuf); + + rt_submit_command_buffers_info submit = {.command_buffer_count = 1, + .command_buffers = &cmdbuf}; + g_renderer.SubmitCommandBuffers(RT_GRAPHICS_QUEUE, &submit); } /* Start next level */ diff --git a/src/runtime/gfx_main.c b/src/gfx/gfx_main.c similarity index 71% rename from src/runtime/gfx_main.c rename to src/gfx/gfx_main.c index 3e1fe07..09a62b1 100644 --- a/src/runtime/gfx_main.c +++ b/src/gfx/gfx_main.c @@ -2,12 +2,12 @@ #include #define RT_DONT_DEFINE_RENDERER_GLOBAL - -#include "config.h" -#include "dynamic_libs.h" #include "gfx.h" #include "renderer_api.h" +#include "runtime/config.h" +#include "runtime/dynamic_libs.h" + /* Attributes are used to bind buffers (or textures) to symbolic values. * For example, an attribute might be bound to "CELL_GRID", which would be * replaced with the (at the time of the invoke) grid buffer of the current @@ -31,6 +31,7 @@ extern rt_pipeline_handle RT_RENDERER_API_FN(CompilePipeline)(const rt_pipeline_ extern void RT_RENDERER_API_FN(DestroyPipeline)(rt_pipeline_handle); extern rt_render_target_handle RT_RENDERER_API_FN(CreateRenderTarget)(const rt_render_target_info *); +extern rt_render_target_handle RT_RENDERER_API_FN(GetSwapchainRenderTarget)(void); extern void RT_RENDERER_API_FN(DestroyRenderTarget)(rt_render_target_handle); extern rt_result RT_RENDERER_API_FN(AllocCommandBuffers)(uint32_t, const rt_alloc_command_buffer_info *, @@ -42,6 +43,12 @@ extern rt_result RT_RENDERER_API_FN(CreateSemaphores)(uint32_t, rt_gpu_semaphore_handle *); extern void RT_RENDERER_API_FN(DestroySemaphores)(uint32_t count, rt_gpu_semaphore_handle *); extern uint64_t RT_RENDERER_API_FN(GetSemaphoreValue)(rt_gpu_semaphore_handle); +extern void RT_RENDERER_API_FN(CmdBeginPass)(rt_command_buffer_handle, + const rt_cmd_begin_pass_info *); +extern void RT_RENDERER_API_FN(CmdEndPass)(rt_command_buffer_handle); +extern void RT_RENDERER_API_FN(CmdTransitionRenderTarget)(rt_command_buffer_handle, + rt_render_target_handle, + rt_render_target_state); #endif extern rt_result InitFramegraphManager(void); @@ -73,12 +80,16 @@ static bool LoadRenderer(void) { RETRIEVE_SYMBOL(CompilePipeline, rt_compile_pipeline_fn); RETRIEVE_SYMBOL(DestroyPipeline, rt_destroy_pipeline_fn); RETRIEVE_SYMBOL(CreateRenderTarget, rt_create_render_target_fn); + RETRIEVE_SYMBOL(GetSwapchainRenderTarget, rt_get_swapchain_render_target_fn); RETRIEVE_SYMBOL(DestroyRenderTarget, rt_destroy_render_target_fn); RETRIEVE_SYMBOL(AllocCommandBuffers, rt_alloc_command_buffers_fn); RETRIEVE_SYMBOL(SubmitCommandBuffers, rt_submit_command_buffers_fn); RETRIEVE_SYMBOL(CreateSemaphores, rt_create_gpu_semaphores_fn); RETRIEVE_SYMBOL(DestroySemaphores, rt_destroy_gpu_semaphores_fn); RETRIEVE_SYMBOL(GetSemaphoreValue, rt_get_gpu_semaphore_value_fn); + RETRIEVE_SYMBOL(CmdBeginPass, rt_cmd_begin_pass_fn); + RETRIEVE_SYMBOL(CmdEndPass, rt_cmd_end_pass_fn); + RETRIEVE_SYMBOL(CmdTransitionRenderTarget, rt_cmd_transition_render_target_fn); } else { rtReportError("GFX", "Unsupported renderer backend: (%s) %s", @@ -88,20 +99,24 @@ static bool LoadRenderer(void) { } #undef RETRIEVE_SYMBOL #else - g_renderer.RegisterCVars = &rtRenRegisterCVars; - g_renderer.Init = &rtRenInit; - g_renderer.Shutdown = &rtRenShutdown; - g_renderer.BeginFrame = &rtRenBeginFrame; - g_renderer.EndFrame = &rtRenEndFrame; - g_renderer.CompilePipeline = &rtRenCompilePipeline; - g_renderer.DestroyPipeline = &rtRenDestroyPipeline; - g_renderer.CreateRenderTarget = &rtRenCreateRenderTarget; - g_renderer.DestroyRenderTarget = &rtRenDestroyRenderTarget; - g_renderer.AllocCommandBuffers = &rtRenAllocCommandBuffers; - g_renderer.SubmitCommandBuffers = &rtRenSubmitCommandBuffers; - g_renderer.CreateSemaphores = &rtRenCreateSemaphores; - g_renderer.DestroySemaphores = &rtRenDestroySemaphores; - g_renderer.GetSemaphoreValue = &rtRenGetSemaphoreValue; + g_renderer.RegisterCVars = &rtRenRegisterCVars; + g_renderer.Init = &rtRenInit; + g_renderer.Shutdown = &rtRenShutdown; + g_renderer.BeginFrame = &rtRenBeginFrame; + g_renderer.EndFrame = &rtRenEndFrame; + g_renderer.CompilePipeline = &rtRenCompilePipeline; + g_renderer.DestroyPipeline = &rtRenDestroyPipeline; + g_renderer.CreateRenderTarget = &rtRenCreateRenderTarget; + g_renderer.GetSwapchainRenderTarget = &rtRenGetSwapchainRenderTarget; + g_renderer.DestroyRenderTarget = &rtRenDestroyRenderTarget; + g_renderer.AllocCommandBuffers = &rtRenAllocCommandBuffers; + g_renderer.SubmitCommandBuffers = &rtRenSubmitCommandBuffers; + g_renderer.CreateSemaphores = &rtRenCreateSemaphores; + g_renderer.DestroySemaphores = &rtRenDestroySemaphores; + g_renderer.GetSemaphoreValue = &rtRenGetSemaphoreValue; + g_renderer.CmdBeginPass = &rtRenCmdBeginPass; + g_renderer.CmdEndPass = &rtRenCmdEndPass; + g_renderer.CmdTransitionRenderTarget = &rtRenCmdTransitionRenderTarget; #endif return true; } diff --git a/src/gfx/meson.build b/src/gfx/meson.build new file mode 100644 index 0000000..444e935 --- /dev/null +++ b/src/gfx/meson.build @@ -0,0 +1,18 @@ +gfx_deps = [thread_dep, m_dep] +gfx_lib = library('rtgfx', + # Project Sources + 'gfx.h', + 'renderer_api.h', + 'render_list.h', + + 'gfx_framegraph.c', + 'gfx_main.c', + # Contrib Sources + dependencies : gfx_deps, + include_directories : engine_incdir, + link_with : runtime_lib, + c_pch : 'pch/gfx_pch.h', + install : true) + +engine_libs += gfx_lib +engine_lib_paths += gfx_lib.full_path() diff --git a/src/gfx/pch/gfx_pch.h b/src/gfx/pch/gfx_pch.h new file mode 100644 index 0000000..564e44a --- /dev/null +++ b/src/gfx/pch/gfx_pch.h @@ -0,0 +1,4 @@ +#include "gfx.h" + +/* Commonly used runtime headers */ +#include "runtime/runtime.h" diff --git a/src/runtime/render_list.h b/src/gfx/render_list.h similarity index 100% rename from src/runtime/render_list.h rename to src/gfx/render_list.h diff --git a/src/runtime/renderer_api.h b/src/gfx/renderer_api.h similarity index 73% rename from src/runtime/renderer_api.h rename to src/gfx/renderer_api.h index db3d81c..42286d7 100644 --- a/src/runtime/renderer_api.h +++ b/src/gfx/renderer_api.h @@ -6,8 +6,10 @@ #include #include "gfx.h" -#include "resources.h" -#include "runtime.h" + +#include "runtime/resources.h" +#include "runtime/rt_math.h" +#include "runtime/runtime.h" #ifdef __cplusplus extern "C" { @@ -134,6 +136,51 @@ typedef struct { uint64_t initial_value; } rt_gpu_semaphore_info; +typedef enum { + RT_PASS_LOAD_MODE_LOAD, + RT_PASS_LOAD_MODE_CLEAR, +} rt_pass_load_mode; + +typedef enum { + RT_PASS_WRITE_MODE_STORE, + RT_PASS_WRITE_MODE_DISCARD, +} rt_pass_write_mode; + +typedef union { + rt_color color; + struct { + float depth; + int32_t stencil; + } depth_stencil; +} rt_pass_clear_value; + +typedef struct { + rt_render_target_handle color_buffers[4]; + rt_pass_load_mode color_buffer_loads[4]; + rt_pass_write_mode color_buffer_writes[4]; + rt_pass_clear_value color_buffer_clear_values[4]; + uint32_t color_buffer_count; + + rt_render_target_handle depth_stencil_buffer; + rt_pass_load_mode depth_stencil_buffer_load; + rt_pass_write_mode depth_stencil_buffer_write; + rt_pass_clear_value depth_stencil_buffer_clear_value; + + rt_rect2i render_area; +} rt_cmd_begin_pass_info; + +typedef enum { + /* Unusable, must be transitioned to an usable state first. */ + RT_RENDER_TARGET_STATE_INVALID, + + /* Used as a color- or depth-buffer */ + RT_RENDER_TARGET_STATE_ATTACHMENT, + + RT_RENDER_TARGET_STATE_SAMPLED_IMAGE, + + RT_RENDER_TARGET_STATE_STORAGE_IMAGE, +} rt_render_target_state; + /* Renderer API */ typedef void rt_register_renderer_cvars_fn(void); @@ -144,6 +191,7 @@ typedef void rt_end_frame_fn(unsigned int frame_id); typedef rt_pipeline_handle rt_compile_pipeline_fn(const rt_pipeline_info *info); typedef void rt_destroy_pipeline_fn(rt_pipeline_handle handle); typedef rt_render_target_handle rt_create_render_target_fn(const rt_render_target_info *info); +typedef rt_render_target_handle rt_get_swapchain_render_target_fn(void); typedef void rt_destroy_render_target_fn(rt_render_target_handle handle); typedef rt_result rt_alloc_command_buffers_fn(uint32_t count, const rt_alloc_command_buffer_info *info, @@ -156,6 +204,13 @@ typedef rt_result rt_create_gpu_semaphores_fn(uint32_t count, typedef void rt_destroy_gpu_semaphores_fn(uint32_t count, rt_gpu_semaphore_handle *semaphores); typedef uint64_t rt_get_gpu_semaphore_value_fn(rt_gpu_semaphore_handle semaphore); +typedef void rt_cmd_begin_pass_fn(rt_command_buffer_handle cmdbuf, + const rt_cmd_begin_pass_info *info); +typedef void rt_cmd_end_pass_fn(rt_command_buffer_handle cmdbuf); +typedef void rt_cmd_transition_render_target_fn(rt_command_buffer_handle cmdbuf, + rt_render_target_handle render_target, + rt_render_target_state new_state); + typedef struct { rt_register_renderer_cvars_fn *RegisterCVars; rt_init_renderer_fn *Init; @@ -165,12 +220,18 @@ typedef struct { rt_compile_pipeline_fn *CompilePipeline; rt_destroy_pipeline_fn *DestroyPipeline; rt_create_render_target_fn *CreateRenderTarget; + rt_get_swapchain_render_target_fn *GetSwapchainRenderTarget; rt_destroy_render_target_fn *DestroyRenderTarget; rt_alloc_command_buffers_fn *AllocCommandBuffers; rt_submit_command_buffers_fn *SubmitCommandBuffers; rt_create_gpu_semaphores_fn *CreateSemaphores; rt_destroy_gpu_semaphores_fn *DestroySemaphores; rt_get_gpu_semaphore_value_fn *GetSemaphoreValue; + + /* Command Buffer Functions */ + rt_cmd_begin_pass_fn *CmdBeginPass; + rt_cmd_end_pass_fn *CmdEndPass; + rt_cmd_transition_render_target_fn *CmdTransitionRenderTarget; } rt_renderer_api; #define RT_RENDERER_API_FN(name) RT_DLLEXPORT rtRen##name diff --git a/src/meson.build b/src/meson.build index 329ac06..f2cf339 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,5 +1,7 @@ subdir('runtime') subdir('asset_compiler') +subdir('gfx') +subdir('app_framework') # Renderer libs subdir('renderer/vk') diff --git a/src/renderer/vk/command_buffers.c b/src/renderer/vk/command_buffers.c index 0513214..7cb69c2 100644 --- a/src/renderer/vk/command_buffers.c +++ b/src/renderer/vk/command_buffers.c @@ -4,10 +4,12 @@ #include "runtime/atomics.h" #include "runtime/config.h" +#include "runtime/handles.h" #include "runtime/mem_arena.h" -#include "runtime/renderer_api.h" #include "runtime/runtime.h" +#include "gfx/renderer_api.h" + #include RT_CVAR_I(rt_VkMaxCommandPools, @@ -54,8 +56,10 @@ rt_result InitCommandBufferManagement(void) { return RT_OUT_OF_MEMORY; } - /* Keep 0 free as a "Not initialized" value for t_first_pool */ - _next_pools = 1; + /* We keep 0 free as a "Not initialized" value for t_first_pool. + * The atomicinc used to acquire a pool returns the incremented value, so 0 is never returned. + */ + _next_pools = 0; return RT_SUCCESS; } @@ -183,7 +187,7 @@ rt_result RT_RENDERER_API_FN(AllocCommandBuffers)(uint32_t count, if ((int)t_first_pool >= rt_VkMaxCommandPools.i) return RT_OUT_OF_MEMORY; - uint32_t frame_id = 0; + uint32_t frame_id = g_gpu.current_frame_id % g_gpu.max_frames_in_flight; rt_result result = RT_SUCCESS; /* TODO: We should probably batch allocations of the same type */ @@ -216,7 +220,13 @@ rt_result RT_RENDERER_API_FN(AllocCommandBuffers)(uint32_t count, break; } - p_command_buffers[i].index = slot; + VkCommandBufferBeginInfo begin_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT, + }; + vkBeginCommandBuffer(_command_buffers[slot].command_buffer, &begin_info); + + p_command_buffers[i].index = (slot + 1); p_command_buffers[i].version = _command_buffers[slot].version; } @@ -243,13 +253,13 @@ rt_result RT_RENDERER_API_FN(SubmitCommandBuffers)(rt_gpu_queue queue, VkSemaphoreSubmitInfo *wait_semaphores = RT_ARENA_PUSH_ARRAY(temp.arena, VkSemaphoreSubmitInfo, info->wait_semaphore_count); - if (!wait_semaphores) { + if (!wait_semaphores && info->wait_semaphore_count > 0) { result = RT_OUT_OF_MEMORY; goto out; } VkSemaphoreSubmitInfo *signal_semaphores = RT_ARENA_PUSH_ARRAY(temp.arena, VkSemaphoreSubmitInfo, info->signal_semaphore_count); - if (!signal_semaphores) { + if (!signal_semaphores && info->signal_semaphore_count > 0) { result = RT_OUT_OF_MEMORY; goto out; } @@ -277,7 +287,12 @@ rt_result RT_RENDERER_API_FN(SubmitCommandBuffers)(rt_gpu_queue queue, } for (uint32_t i = 0; i < count; ++i) { - uint32_t slot = info->command_buffers[i].index; + if (!RT_IS_HANDLE_VALID(info->command_buffers[i])) { + rtLog("vk", "Tried to submit an invalid command buffer."); + result = RT_INVALID_VALUE; + goto out; + } + uint32_t slot = info->command_buffers[i].index - 1; if (_command_buffers[slot].version != info->command_buffers[i].version) { rtLog("vk", "Mismatch between handle version and stored version while submitting a command " @@ -294,6 +309,8 @@ rt_result RT_RENDERER_API_FN(SubmitCommandBuffers)(rt_gpu_queue queue, command_buffers[i].pNext = NULL; command_buffers[i].deviceMask = 0; command_buffers[i].commandBuffer = _command_buffers[slot].command_buffer; + + vkEndCommandBuffer(command_buffers[i].commandBuffer); } VkSubmitInfo2 submit_info = { @@ -311,6 +328,126 @@ rt_result RT_RENDERER_API_FN(SubmitCommandBuffers)(rt_gpu_queue queue, result = RT_UNKNOWN_ERROR; } +out: + rtReturnTemporaryArena(temp); + return result; +} + +VkCommandBuffer rtGetCommandBuffer(rt_command_buffer_handle cmdbuf) { + if (!RT_IS_HANDLE_VALID(cmdbuf) || cmdbuf.index >= (uint32_t)rt_VkCommandBufferRingBufferSize.i) + return VK_NULL_HANDLE; + uint32_t slot = cmdbuf.index - 1; + if (_command_buffers[slot].version != cmdbuf.version) { + return VK_NULL_HANDLE; + } + return _command_buffers[slot].command_buffer; +} + +VkCommandBuffer rtAllocSingleCommandBuffer(rt_gpu_queue queue) { + rt_thread_pools *pools = &_pools[t_first_pool]; + if (t_first_pool == 0) { + /* Acquire pools */ + t_first_pool = rtAtomic32Inc(&_next_pools); + RT_ASSERT((int)t_first_pool < rt_VkMaxCommandPools.i, "Too many command pools created."); + + pools = &_pools[t_first_pool]; + rt_result create_res = CreatePools(pools); + if (create_res != RT_SUCCESS) + return VK_NULL_HANDLE; + } + if ((int)t_first_pool >= rt_VkMaxCommandPools.i) + return VK_NULL_HANDLE; + + uint32_t frame_id = g_gpu.current_frame_id % g_gpu.max_frames_in_flight; + VkCommandPool pool = pools->graphics_pools[frame_id]; + if (queue == RT_COMPUTE_QUEUE) + pool = pools->compute_pools[frame_id]; + else if (queue == RT_TRANSFER_QUEUE) + pool = pools->transfer_pools[frame_id]; + + VkCommandBufferAllocateInfo alloc_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1, + .commandPool = pool, + }; + VkCommandBuffer cmdbuf; + if (vkAllocateCommandBuffers(g_gpu.device, &alloc_info, &cmdbuf) != VK_SUCCESS) { + return VK_NULL_HANDLE; + } + return cmdbuf; +} + +rt_result rtSubmitSingleCommandBuffer(VkCommandBuffer command_buffer, + const VkSemaphore *wait_semaphores, + const uint32_t *wait_values, + uint32_t wait_semaphore_count, + const VkSemaphore *signal_semaphores, + const uint32_t *signal_values, + uint32_t signal_semaphore_count, + rt_gpu_queue queue) { + + rt_temp_arena temp = rtGetTemporaryArena(NULL, 0); + if (!temp.arena) + return RT_OUT_OF_MEMORY; + VkQueue target_queue = rtGetQueue(queue); + rt_result result = RT_SUCCESS; + + VkSemaphoreSubmitInfo *wait_semaphore_info = + RT_ARENA_PUSH_ARRAY(temp.arena, VkSemaphoreSubmitInfo, wait_semaphore_count); + if (!wait_semaphore_info && wait_semaphore_count > 0) { + result = RT_OUT_OF_MEMORY; + goto out; + } + VkSemaphoreSubmitInfo *signal_semaphore_info = + RT_ARENA_PUSH_ARRAY(temp.arena, VkSemaphoreSubmitInfo, signal_semaphore_count); + if (!signal_semaphore_info && signal_semaphore_count > 0) { + result = RT_OUT_OF_MEMORY; + goto out; + } + uint32_t wait_count = wait_semaphore_count; + uint32_t signal_count = signal_semaphore_count; + for (uint32_t i = 0; i < wait_count; ++i) { + VkSemaphoreSubmitInfo semaphore_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, + .semaphore = wait_semaphores[i], + .value = (wait_values) ? wait_values[i] : 0, + .stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + .deviceIndex = 0, + }; + wait_semaphore_info[i] = semaphore_info; + } + for (uint32_t i = 0; i < signal_count; ++i) { + VkSemaphoreSubmitInfo semaphore_info = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, + .semaphore = signal_semaphores[i], + .value = (signal_values) ? signal_values[i] : 0, + .stageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + .deviceIndex = 0, + }; + signal_semaphore_info[i] = semaphore_info; + } + + VkCommandBufferSubmitInfo command_buffer_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO, + .deviceMask = 0, + .commandBuffer = command_buffer, + }; + + VkSubmitInfo2 submit_info = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2, + .waitSemaphoreInfoCount = wait_count, + .signalSemaphoreInfoCount = signal_count, + .pWaitSemaphoreInfos = wait_semaphore_info, + .pSignalSemaphoreInfos = signal_semaphore_info, + .commandBufferInfoCount = 1, + .pCommandBufferInfos = &command_buffer_info, + }; + + if (vkQueueSubmit2(target_queue, 1, &submit_info, VK_NULL_HANDLE) != VK_SUCCESS) { + rtLog("vk", "vkQueueSubmit failed."); + result = RT_UNKNOWN_ERROR; + } out: rtReturnTemporaryArena(temp); return result; diff --git a/src/renderer/vk/command_buffers.h b/src/renderer/vk/command_buffers.h index 06646b7..9a26c57 100644 --- a/src/renderer/vk/command_buffers.h +++ b/src/renderer/vk/command_buffers.h @@ -1,9 +1,24 @@ #ifndef RT_COMMAND_BUFFERS_H #define RT_COMMAND_BUFFERS_H +#include "gfx/renderer_api.h" #include "runtime/runtime.h" +#include + void rtResetCommandPools(unsigned int frame_id); +VkCommandBuffer rtGetCommandBuffer(rt_command_buffer_handle cmdbuf); + +VkCommandBuffer rtAllocSingleCommandBuffer(rt_gpu_queue queue); + +rt_result rtSubmitSingleCommandBuffer(VkCommandBuffer command_buffer, + const VkSemaphore *wait_semaphores, + const uint32_t *wait_values, + uint32_t wait_semaphore_count, + const VkSemaphore *signal_semaphores, + const uint32_t *signal_values, + uint32_t signal_semaphore_count, + rt_gpu_queue queue); #endif diff --git a/src/renderer/vk/commands.c b/src/renderer/vk/commands.c new file mode 100644 index 0000000..637557e --- /dev/null +++ b/src/renderer/vk/commands.c @@ -0,0 +1,257 @@ +#include "command_buffers.h" +#include "gpu.h" +#include "render_targets.h" +#include "swapchain.h" + +#include "runtime/handles.h" +#include "runtime/mem_arena.h" +#include "gfx/renderer_api.h" + +/* Retrieve the VkCommandBuffer as varname, or return */ +#define GET_CMDBUF(varname, handle) \ + VkCommandBuffer varname = rtGetCommandBuffer((handle)); \ + if (varname == VK_NULL_HANDLE) { \ + rtLog("vk", "Failed to retrive VkCommandBuffer for %s", __FUNCTION__); \ + return; \ + } + +void RT_RENDERER_API_FN(CmdBeginPass)(rt_command_buffer_handle cmdbuf_handle, + const rt_cmd_begin_pass_info *info) { + GET_CMDBUF(cmdbuf, cmdbuf_handle) + + rt_temp_arena temp = rtGetTemporaryArena(NULL, 0); + if (!temp.arena) { + rtReportError("vk", "Failed to acquire a temporary arena for CmdBeginPass"); + return; + } + +#ifdef RT_DEBUG + VkDebugUtilsLabelEXT debug_label = { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, + .color = {0.39f, 0.58f, 0.92f, 1.f}, + .pLabelName = "RenderPass" + }; + vkCmdBeginDebugUtilsLabelEXT(cmdbuf, &debug_label); +#endif + + /* Acquire the necessary attachments */ + + VkRenderingAttachmentInfo *colorbuffers = + RT_ARENA_PUSH_ARRAY_ZERO(temp.arena, VkRenderingAttachmentInfo, info->color_buffer_count); + for (uint32_t i = 0; i < info->color_buffer_count; ++i) { + VkImageView image_view = VK_NULL_HANDLE; + if (RT_IS_HANDLE_VALID(info->color_buffers[i])) { + rt_render_target *rt = rtGetRenderTarget(info->color_buffers[i]); + if (rt) + image_view = rt->view[g_gpu.current_frame_id % g_gpu.max_frames_in_flight]; + } + + colorbuffers[i].sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; + colorbuffers[i].pNext = NULL; + colorbuffers[i].imageView = image_view; + colorbuffers[i].imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + switch (info->color_buffer_loads[i]) { + case RT_PASS_LOAD_MODE_CLEAR: + colorbuffers[i].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + break; + case RT_PASS_LOAD_MODE_LOAD: + colorbuffers[i].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + break; + default: + colorbuffers[i].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + break; + } + switch (info->color_buffer_writes[i]) { + case RT_PASS_WRITE_MODE_STORE: + colorbuffers[i].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + break; + case RT_PASS_WRITE_MODE_DISCARD: + colorbuffers[i].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + break; + default: + colorbuffers[i].storeOp = VK_ATTACHMENT_STORE_OP_NONE; + break; + } + memcpy(&colorbuffers[i].clearValue.color.float32, + info->color_buffer_clear_values[i].color.v, + sizeof(float) * 4); + + /* TODO: Multisample resolve */ + colorbuffers[i].resolveMode = VK_RESOLVE_MODE_NONE; + colorbuffers[i].resolveImageView = VK_NULL_HANDLE; + colorbuffers[i].resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED; + } + + /* depth and stencil might be the same */ + VkRenderingAttachmentInfo *depth_stencil_buffer = + RT_IS_HANDLE_VALID(info->depth_stencil_buffer) + ? RT_ARENA_PUSH_STRUCT_ZERO(temp.arena, VkRenderingAttachmentInfo) + : NULL; + if (depth_stencil_buffer) { + VkImageView image_view = VK_NULL_HANDLE; + rt_render_target *rt = rtGetRenderTarget(info->depth_stencil_buffer); + if (rt) + image_view = rt->view[g_gpu.current_frame_id % g_gpu.max_frames_in_flight]; + + depth_stencil_buffer->sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; + depth_stencil_buffer->pNext = NULL; + depth_stencil_buffer->imageView = image_view; + depth_stencil_buffer->imageLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + switch (info->depth_stencil_buffer_load) { + case RT_PASS_LOAD_MODE_CLEAR: + depth_stencil_buffer->loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + break; + case RT_PASS_LOAD_MODE_LOAD: + depth_stencil_buffer->loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + break; + default: + depth_stencil_buffer->loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + break; + } + switch (info->depth_stencil_buffer_write) { + case RT_PASS_WRITE_MODE_STORE: + depth_stencil_buffer->storeOp = VK_ATTACHMENT_STORE_OP_STORE; + break; + case RT_PASS_WRITE_MODE_DISCARD: + depth_stencil_buffer->storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + break; + default: + depth_stencil_buffer->storeOp = VK_ATTACHMENT_STORE_OP_NONE; + break; + } + + /* TODO: Multisample resolve */ + depth_stencil_buffer->resolveMode = VK_RESOLVE_MODE_NONE; + depth_stencil_buffer->resolveImageView = VK_NULL_HANDLE; + depth_stencil_buffer->resolveImageLayout = VK_IMAGE_LAYOUT_UNDEFINED; + } + + VkRect2D render_area = { + .offset = { .x = info->render_area.offset.x, .y = info->render_area.offset.y}, + .extent = {.width = info->render_area.size.x, .height = info->render_area.size.y} + }; + if (render_area.extent.width == 0) + render_area.extent.width = g_swapchain.extent.width; + if (render_area.extent.height == 0) + render_area.extent.height = g_swapchain.extent.height; + + VkRenderingInfo rendering_info = { + .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, + .pColorAttachments = colorbuffers, + .colorAttachmentCount = info->color_buffer_count, + .pDepthAttachment = depth_stencil_buffer, + .pStencilAttachment = depth_stencil_buffer, + .layerCount = 1, + .renderArea = render_area, + }; + + vkCmdBeginRendering(cmdbuf, &rendering_info); + + rtReturnTemporaryArena(temp); +} + +void RT_RENDERER_API_FN(CmdEndPass)(rt_command_buffer_handle cmdbuf_handle) { + GET_CMDBUF(cmdbuf, cmdbuf_handle) + vkCmdEndRendering(cmdbuf); + +#ifdef RT_DEBUG + vkCmdEndDebugUtilsLabelEXT(cmdbuf); +#endif +} + +void RT_RENDERER_API_FN(CmdTransitionRenderTarget)(rt_command_buffer_handle cmdbuf_handle, + rt_render_target_handle render_target, + rt_render_target_state new_state) { + GET_CMDBUF(cmdbuf, cmdbuf_handle) + uint32_t image_index = g_gpu.current_frame_id % g_gpu.max_frames_in_flight; + + rt_render_target *rt = rtGetRenderTarget(render_target); + if (!rt) { + rtLog("vk", "Tried to transition invalid render target"); + return; + } + + if (rt->states[image_index] == new_state) + return; + + /* Determine old and new layout */ + VkImageLayout old_layout; + switch (rt->states[image_index]) { + case RT_RENDER_TARGET_STATE_ATTACHMENT: + if (rt->format == VK_FORMAT_D24_UNORM_S8_UINT || rt->format == VK_FORMAT_D32_SFLOAT) { + old_layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } else { + old_layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + break; + case RT_RENDER_TARGET_STATE_STORAGE_IMAGE: + case RT_RENDER_TARGET_STATE_SAMPLED_IMAGE: + old_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + break; + default: + old_layout = VK_IMAGE_LAYOUT_UNDEFINED; + break; + } + VkImageLayout new_layout; + switch (new_state) { + case RT_RENDER_TARGET_STATE_ATTACHMENT: + if (rt->format == VK_FORMAT_D24_UNORM_S8_UINT || rt->format == VK_FORMAT_D32_SFLOAT) { + new_layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } else { + new_layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } + break; + case RT_RENDER_TARGET_STATE_STORAGE_IMAGE: + case RT_RENDER_TARGET_STATE_SAMPLED_IMAGE: + new_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + break; + default: + new_layout = VK_IMAGE_LAYOUT_UNDEFINED; + } + +#ifdef RT_DEBUG + VkDebugUtilsLabelEXT debug_label = { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, + .pLabelName = "Transition Render Target", + .color = {0.f, 0.f, 0.f, 0.f}, + }; + vkCmdBeginDebugUtilsLabelEXT(cmdbuf, &debug_label); +#endif + + VkImageAspectFlags aspect_mask = + (rt->format == VK_FORMAT_D24_UNORM_S8_UINT || rt->format == VK_FORMAT_D32_SFLOAT) + ? VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT + : VK_IMAGE_ASPECT_COLOR_BIT; + VkImageMemoryBarrier2 image_barrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + .srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT, + .dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + .dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT | VK_ACCESS_2_MEMORY_READ_BIT, + .oldLayout = old_layout, + .newLayout = new_layout, + .image = rt->image[image_index], + /* clang-format off */ + .subresourceRange = { + .aspectMask = aspect_mask, + .baseArrayLayer = 0, + .baseMipLevel = 0, + .layerCount = 1, + .levelCount = 1, + }, + /* clang-format on */ + }; + + VkDependencyInfo dep_info = { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pImageMemoryBarriers = &image_barrier, + .imageMemoryBarrierCount = 1, + }; + vkCmdPipelineBarrier2(cmdbuf, &dep_info); + +#ifdef RT_DEBUG + vkCmdEndDebugUtilsLabelEXT(cmdbuf); +#endif + + rt->states[image_index] = new_state; +} diff --git a/src/renderer/vk/frame.c b/src/renderer/vk/frame.c index 46a055e..1ccab33 100644 --- a/src/renderer/vk/frame.c +++ b/src/renderer/vk/frame.c @@ -1,8 +1,9 @@ #include "command_buffers.h" #include "gpu.h" +#include "render_targets.h" #include "swapchain.h" -#include "runtime/renderer_api.h" +#include "gfx/renderer_api.h" #define ONE_SECOND_NS 1000000000u @@ -32,6 +33,10 @@ void RT_RENDERER_API_FN(BeginFrame)(unsigned int frame_id) { rtReportError("vk", "Failed to recreate the swapchain."); return; } + rtUpdateSwapchainRenderTarget(); + rtUpdateRenderTargetsFromSwapchain(g_swapchain.image_count, + g_swapchain.format, + g_swapchain.extent); rtRenBeginFrame(frame_id); } else if (acquire_res != VK_SUCCESS) { rtReportError("vk", "vkAcquireNextImageKHR failed: %u", acquire_res); @@ -43,12 +48,85 @@ void RT_RENDERER_API_FN(EndFrame)(unsigned int frame_id) { uint32_t image_index = frame->swapchain_image_index; + /* Transition the swap chain image to the correct layout */ + VkCommandBuffer cmd = rtAllocSingleCommandBuffer(RT_GRAPHICS_QUEUE); + if (cmd == VK_NULL_HANDLE) { + rtReportError("vk", + "Failed to allocate a command buffer for transitioning the swapchain image " + "to PRESENT_SRC layout."); + return; + } + + VkCommandBufferBeginInfo begin_info = {.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT}; + vkBeginCommandBuffer(cmd, &begin_info); + +#ifdef RT_DEBUG + VkDebugUtilsLabelEXT debug_label = { + .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT, + .color = {0.f, 0.f, 0.f, 0.f}, + .pLabelName = "Transition Swapchain" + }; + vkCmdBeginDebugUtilsLabelEXT(cmd, &debug_label); +#endif + + + VkImageMemoryBarrier2 image_barrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + .srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT, + .dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + .dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT | VK_ACCESS_2_MEMORY_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + .image = g_swapchain.images[image_index], + /* clang-format off */ + .subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseArrayLayer = 0, + .baseMipLevel = 0, + .layerCount = 1, + .levelCount = 1, + }, + /* clang-format on */ + }; + + VkDependencyInfo dep_info = { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .pImageMemoryBarriers = &image_barrier, + .imageMemoryBarrierCount = 1, + }; + vkCmdPipelineBarrier2(cmd, &dep_info); + +#ifdef RT_DEBUG + vkCmdEndDebugUtilsLabelEXT(cmd); +#endif + + vkEndCommandBuffer(cmd); + if (rtSubmitSingleCommandBuffer(cmd, + &frame->image_available, + NULL, + 1, + &frame->render_finished, + NULL, + 1, + RT_GRAPHICS_QUEUE) != RT_SUCCESS) { + rtReportError("vk", "Failed to submit the layout transition for the swapchain image."); + return; + } + + /* Update the swapchain render target */ + rt_render_target_handle swap_rt_handle = g_renderer.GetSwapchainRenderTarget(); + rt_render_target *swap_rt = rtGetRenderTarget(swap_rt_handle); + swap_rt->states[image_index] = RT_RENDER_TARGET_STATE_INVALID; + + VkPresentInfoKHR present_info = { .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .pImageIndices = &image_index, .pSwapchains = &g_swapchain.swapchain, .swapchainCount = 1, - .pWaitSemaphores = &frame->image_available, + .pWaitSemaphores = &frame->render_finished, .waitSemaphoreCount = 1, }; diff --git a/src/renderer/vk/gpu.h b/src/renderer/vk/gpu.h index 4566aa5..fa750f4 100644 --- a/src/renderer/vk/gpu.h +++ b/src/renderer/vk/gpu.h @@ -7,7 +7,7 @@ #define VMA_DYNAMI_VULKAN_FUNCTIONS 0 #include -#include "runtime/renderer_api.h" +#include "gfx/renderer_api.h" /* Minimum supported value of g_gpu.max_frames_in_flight */ #define RT_VK_MIN_SUPPORTED_FRAMES_IN_FLIGHT 2 diff --git a/src/renderer/vk/gpu_sync.c b/src/renderer/vk/gpu_sync.c index f606fc4..032d5cc 100644 --- a/src/renderer/vk/gpu_sync.c +++ b/src/renderer/vk/gpu_sync.c @@ -1,10 +1,11 @@ #include "gpu.h" -#include "runtime/renderer_api.h" #include "runtime/config.h" #include "runtime/threading.h" #include "runtime/handles.h" +#include "gfx/renderer_api.h" + #include RT_CVAR_I(rt_VkMaxSemaphores, "Maximum number of semaphores. Default: 1024", 1024); diff --git a/src/renderer/vk/gpu_sync.h b/src/renderer/vk/gpu_sync.h index 62de658..53d503e 100644 --- a/src/renderer/vk/gpu_sync.h +++ b/src/renderer/vk/gpu_sync.h @@ -3,7 +3,7 @@ #include -#include "runtime/renderer_api.h" +#include "gfx/renderer_api.h" VkSemaphore rtGetSemaphore(rt_gpu_semaphore_handle handle); diff --git a/src/renderer/vk/init.c b/src/renderer/vk/init.c index cd3c114..b427819 100644 --- a/src/renderer/vk/init.c +++ b/src/renderer/vk/init.c @@ -5,13 +5,15 @@ #define RT_VK_DONT_DEFINE_GPU_GLOBAL #include "gpu.h" +#include "render_targets.h" #include "swapchain.h" #include "runtime/config.h" -#include "runtime/renderer_api.h" #include "runtime/runtime.h" -#define TARGET_API_VERSION VK_API_VERSION_1_2 +#include "gfx/renderer_api.h" + +#define TARGET_API_VERSION VK_API_VERSION_1_3 RT_CVAR_I(r_VkEnableAPIAllocTracking, "Enable tracking of allocations done by the vulkan api. [0/1] Default: 0", @@ -78,7 +80,16 @@ DebugUtilsMessengerCb(VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT types, const VkDebugUtilsMessengerCallbackDataEXT *callbackData, void *userData) { + if (severity < VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) + return VK_FALSE; + const char *severity_str = ""; + if (severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) + severity_str = "WARNING"; + else if (severity == VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) + severity_str = "ERROR"; + rtLog("vk", "[%s] %s", severity_str, callbackData->pMessage); + RT_DEBUGBREAK; return VK_FALSE; } @@ -260,7 +271,6 @@ static rt_queue_indices RetrieveQueueIndices(VkPhysicalDevice phys_dev, VkSurfac static bool CheckDeviceExtensionSupported(VkPhysicalDevice phys_dev) { const char *required_extensions[] = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, - VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME, }; uint32_t extension_count; @@ -318,8 +328,16 @@ static rt_result ChoosePhysicalDevice(void) { uint32_t highscore = 0; uint32_t best_index = phys_device_count; for (uint32_t i = 0; i < phys_device_count; ++i) { + VkPhysicalDeviceSynchronization2Features synchronization2_features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES, + }; + VkPhysicalDeviceDynamicRenderingFeatures dynamic_rendering_features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES, + .pNext = &synchronization2_features, + }; VkPhysicalDeviceDescriptorIndexingFeatures descriptor_indexing_features = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES, + .pNext = &dynamic_rendering_features, }; VkPhysicalDeviceFeatures2 features = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, @@ -345,6 +363,10 @@ static rt_result ChoosePhysicalDevice(void) { indices.graphics == UINT32_MAX) continue; + if (!synchronization2_features.synchronization2 || + !dynamic_rendering_features.dynamicRendering) + continue; + /* Check for bindless support */ if (!descriptor_indexing_features.runtimeDescriptorArray || !descriptor_indexing_features.descriptorBindingPartiallyBound) @@ -461,9 +483,16 @@ static rt_result CreateDevice(void) { queue_info[distinct_queue_count].pQueuePriorities = &priority; ++distinct_queue_count; } - + VkPhysicalDeviceSynchronization2Features synchronization2_features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SYNCHRONIZATION_2_FEATURES, + }; + VkPhysicalDeviceDynamicRenderingFeatures dynamic_rendering_features = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES, + .pNext = &synchronization2_features, + }; VkPhysicalDeviceDescriptorIndexingFeatures indexing_features = { .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES, + .pNext = &dynamic_rendering_features, }; VkPhysicalDeviceFeatures2 features = {.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, .pNext = &indexing_features}; @@ -523,6 +552,8 @@ static rt_result CreateAllocator(void) { SET_KHR_FNC(vkBindBufferMemory2); SET_KHR_FNC(vkBindImageMemory2); SET_KHR_FNC(vkGetPhysicalDeviceMemoryProperties2); + SET_FNC(vkGetDeviceBufferMemoryRequirements); + SET_FNC(vkGetDeviceImageMemoryRequirements); #undef SET_FNC #undef SET_KHR_FNC @@ -549,8 +580,8 @@ static rt_result CreatePerFrameObjects(void) { .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, }; if (vkCreateSemaphore(g_gpu.device, - &semaphore_info, - g_gpu.alloc_cb, + &semaphore_info, + g_gpu.alloc_cb, &g_gpu.frames[i].render_finished) != VK_SUCCESS) { return RT_UNKNOWN_ERROR; } @@ -630,6 +661,7 @@ rt_result RT_RENDERER_API_FN(Init)(const rt_renderer_init_info *info) { res = rtCreateSwapchain(); if (res != RT_SUCCESS) return res; + rtUpdateSwapchainRenderTarget(); return RT_SUCCESS; } diff --git a/src/renderer/vk/meson.build b/src/renderer/vk/meson.build index 163951c..6c8ccc2 100644 --- a/src/renderer/vk/meson.build +++ b/src/renderer/vk/meson.build @@ -17,6 +17,7 @@ if vk_dep.found() 'swapchain.h', 'command_buffers.c', + 'commands.c', 'frame.c', 'gpu_sync.c', 'helper.c', diff --git a/src/renderer/vk/pipelines.c b/src/renderer/vk/pipelines.c index be91aa1..84a0ecb 100644 --- a/src/renderer/vk/pipelines.c +++ b/src/renderer/vk/pipelines.c @@ -1,10 +1,11 @@ #include "runtime/config.h" #include "runtime/handles.h" #include "runtime/mem_arena.h" -#include "runtime/renderer_api.h" #include "runtime/resources.h" #include "runtime/threading.h" +#include "gfx/renderer_api.h" + #include "gpu.h" #include "pipelines.h" diff --git a/src/renderer/vk/pipelines.h b/src/renderer/vk/pipelines.h index 094b09f..9172431 100644 --- a/src/renderer/vk/pipelines.h +++ b/src/renderer/vk/pipelines.h @@ -3,7 +3,7 @@ #include -#include "runtime/renderer_api.h" +#include "gfx/renderer_api.h" typedef struct { VkPipeline pipeline; diff --git a/src/renderer/vk/render_targets.c b/src/renderer/vk/render_targets.c index 63b1c5a..3485e04 100644 --- a/src/renderer/vk/render_targets.c +++ b/src/renderer/vk/render_targets.c @@ -1,7 +1,8 @@ #include "runtime/config.h" -#include "runtime/renderer_api.h" #include "runtime/threading.h" +#include "gfx/renderer_api.h" + #include "gpu.h" #include "render_targets.h" #include "swapchain.h" @@ -21,6 +22,8 @@ static rt_render_target_slot *_render_targets; static rt_render_target_slot *_first_free; static rt_rwlock _lock; +static rt_render_target_handle _swapchain_handle; + static void DestroyRenderTarget(rt_render_target_slot *slot) { for (unsigned int i = 0; i < slot->render_target.image_count; ++i) { vkDestroyImageView(g_gpu.device, slot->render_target.view[i], g_gpu.alloc_cb); @@ -50,6 +53,34 @@ static bool CreateImageAndView(VkExtent2D extent, g_gpu.present_family != g_gpu.compute_family) queue_families[distinct_queue_families++] = g_gpu.present_family; + VkFormatProperties2 props = { + .sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2, + }; + vkGetPhysicalDeviceFormatProperties2(g_gpu.phys_device, format, &props); + if ((props.formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) == 0) { + rtLog("vk", "Requested render target format can not be sampled."); + usage &= ~VK_IMAGE_USAGE_SAMPLED_BIT; + } + if ((props.formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT) == 0) { + rtLog("vk", "Requested render target format can not be used for storage."); + usage &= ~VK_IMAGE_USAGE_STORAGE_BIT; + } + if (((usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) != 0) && + ((props.formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT) == + 0)) { + rtReportError("vk", + "Tried to create a render target color attachment, but the format does not " + "support the color attachment usage."); + return false; + } else if (((usage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) != 0) && + ((props.formatProperties.optimalTilingFeatures & + VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT) == 0)) { + rtReportError("vk", + "Tried to create a render target depth/stencil attachment, but the format " + "does not support the depth/stencil attachment usage."); + return false; + } + VkImageCreateInfo image_info = { .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .imageType = VK_IMAGE_TYPE_2D, @@ -124,6 +155,14 @@ rt_result InitRenderTargetManagement(void) { for (int i = 1; i < r_VkMaxRenderTargetCount.i - 1; ++i) { _render_targets[i].next_free = &_render_targets[i + 1]; } + + /* Reserve the slot for the swap chain rt */ + rt_render_target_slot *slot = _first_free; + _first_free = slot->next_free; + slot->version = (slot->version + 1) & RT_RENDER_BACKEND_HANDLE_MAX_VERSION; + _swapchain_handle = (rt_render_target_handle){.version = slot->version, + .index = (uint32_t)(slot - _render_targets)}; + return RT_SUCCESS; } @@ -175,15 +214,18 @@ rt_render_target_handle RT_RENDERER_API_FN(CreateRenderTarget)(const rt_render_t slot->render_target.format = g_swapchain.format; slot->render_target.match_swapchain |= RT_RENDER_TARGET_MATCH_SWAPCHAIN_FORMAT; } - if (info->format == RT_PIXEL_FORMAT_DEPTH24_STENCIL8 || info->format == RT_PIXEL_FORMAT_DEPTH32) { + if (info->format == RT_PIXEL_FORMAT_DEPTH24_STENCIL8 || + info->format == RT_PIXEL_FORMAT_DEPTH32) { slot->render_target.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | - VK_IMAGE_USAGE_SAMPLED_BIT | - VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; - slot->render_target.aspect = VK_IMAGE_ASPECT_DEPTH_BIT; + VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT; + if (info->format == RT_PIXEL_FORMAT_DEPTH32) + slot->render_target.aspect = VK_IMAGE_ASPECT_DEPTH_BIT; + else + slot->render_target.aspect = + VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT; } else { slot->render_target.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | - VK_IMAGE_USAGE_SAMPLED_BIT | - VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT; + VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_STORAGE_BIT; slot->render_target.aspect = VK_IMAGE_ASPECT_COLOR_BIT; } slot->render_target.sample_count = rtSampleCountToFlags(info->sample_count); @@ -199,6 +241,7 @@ rt_render_target_handle RT_RENDERER_API_FN(CreateRenderTarget)(const rt_render_t DestroyRenderTarget(slot); goto out; } + slot->render_target.states[i] = RT_RENDER_TARGET_STATE_INVALID; } handle.version = slot->version; @@ -207,6 +250,10 @@ out: return handle; } +rt_render_target_handle RT_RENDERER_API_FN(GetSwapchainRenderTarget)(void) { + return _swapchain_handle; +} + void RT_RENDERER_API_FN(DestroyRenderTarget)(rt_render_target_handle handle) { if (handle.index >= (uint32_t)r_VkMaxRenderTargetCount.i) return; @@ -231,6 +278,25 @@ rt_render_target *rtGetRenderTarget(rt_render_target_handle handle) { return res; } +void rtUpdateSwapchainRenderTarget(void) { + RT_ASSERT(_swapchain_handle.index != 0, "Invalid swap chain render target!"); + rt_render_target_slot *slot = &_render_targets[_swapchain_handle.index]; + rt_render_target *rt = &slot->render_target; + + rt->match_swapchain = 0; + rt->format = g_swapchain.format; + rt->extent = g_swapchain.extent; + rt->sample_count = 1; + rt->usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + rt->aspect = VK_IMAGE_ASPECT_COLOR_BIT; + for (uint32_t i = 0; i < g_swapchain.image_count; ++i) { + rt->allocation[i] = NULL; + rt->image[i] = g_swapchain.images[i]; + rt->view[i] = g_swapchain.image_views[i]; + rt->states[i] = RT_RENDER_TARGET_STATE_INVALID; + } +} + void rtUpdateRenderTargetsFromSwapchain(uint32_t image_count, VkFormat format, VkExtent2D extent) { rtLockWrite(&_lock); for (uint32_t i = 1; i < (uint32_t)r_VkMaxRenderTargetCount.i; ++i) { diff --git a/src/renderer/vk/render_targets.h b/src/renderer/vk/render_targets.h index 74fbde0..45ee039 100644 --- a/src/renderer/vk/render_targets.h +++ b/src/renderer/vk/render_targets.h @@ -2,7 +2,7 @@ #define RT_VK_RENDER_TARGETS_H #include "gpu.h" -#include "runtime/renderer_api.h" +#include "gfx/renderer_api.h" /* Must match RT_VK_MAX_SWAPCHAIN_IMAGES */ #define RT_VK_RENDER_TARGET_MAX_IMAGES 3 @@ -16,6 +16,7 @@ typedef struct { VkImage image[RT_VK_RENDER_TARGET_MAX_IMAGES]; VkImageView view[RT_VK_RENDER_TARGET_MAX_IMAGES]; VmaAllocation allocation[RT_VK_RENDER_TARGET_MAX_IMAGES]; + rt_render_target_state states[RT_VK_RENDER_TARGET_MAX_IMAGES]; VkSampleCountFlagBits sample_count; VkFormat format; VkExtent2D extent; @@ -27,6 +28,10 @@ typedef struct { rt_render_target *rtGetRenderTarget(rt_render_target_handle handle); +/* Update the render target that represents the swap chain */ +void rtUpdateSwapchainRenderTarget(void); + +/* Update render targets that match the swap chain*/ void rtUpdateRenderTargetsFromSwapchain(uint32_t image_count, VkFormat format, VkExtent2D extent); #endif diff --git a/src/runtime/meson.build b/src/runtime/meson.build index 0896abe..026c2b1 100644 --- a/src/runtime/meson.build +++ b/src/runtime/meson.build @@ -4,7 +4,6 @@ runtime_incdirs = include_directories(meson.project_source_root() / 'contrib') runtime_lib = library('rt', # Project Sources 'aio.h', - 'app.h', 'atomics.h', 'buffer_manager.h', 'compression.h', @@ -13,20 +12,15 @@ runtime_lib = library('rt', 'dynamic_libs.h', 'file_tab.h', 'fsutils.h', - 'gfx.h', 'handles.h', 'hashing.h', 'jobs.h', - 'main_loop.h', 'mem_arena.h', - 'render_list.h', - 'renderer_api.h', 'resources.h', 'runtime.h', 'threading.h', 'aio.c', - 'app.c', 'assert.c', 'buffer_manager.c', 'compression.c', @@ -36,12 +30,9 @@ runtime_lib = library('rt', 'error_report.c', 'file_tab.c', 'fsutils.c', - 'gfx_framegraph.c', - 'gfx_main.c', 'hashing.c', 'init.c', 'jobs.c', - 'main_loop.c', 'mem_arena.c', 'resource_manager.c', 'sprint.c', @@ -61,4 +52,4 @@ runtime_lib = library('rt', install : true) engine_libs += runtime_lib -engine_lib_paths += runtime_lib.full_path() \ No newline at end of file +engine_lib_paths += runtime_lib.full_path() diff --git a/src/runtime/resource_manager.c b/src/runtime/resource_manager.c index 01920ce..0bb5a1a 100644 --- a/src/runtime/resource_manager.c +++ b/src/runtime/resource_manager.c @@ -7,10 +7,11 @@ #include "fsutils.h" #include "hashing.h" #include "mem_arena.h" -#include "renderer_api.h" #include "resources.h" #include "threading.h" +#include "gfx/renderer_api.h" + #include #include #include diff --git a/src/runtime/rt_math.h b/src/runtime/rt_math.h new file mode 100644 index 0000000..1d0163a --- /dev/null +++ b/src/runtime/rt_math.h @@ -0,0 +1,33 @@ +#ifndef RT_MATH_H +#define RT_MATH_H + +/* Math types and functions */ + +/* 2d vector */ +typedef union { + struct { + float x; + float y; + }; + float e[2]; +} rt_v2; + +typedef union { + struct { + int x; + int y; + }; + int e[2]; +} rt_v2i; + +/* 2d rectangle defined by its upper left corner and its size. */ +typedef struct { + rt_v2 offset; + rt_v2 size; +} rt_rect2; + +typedef struct { + rt_v2i offset; + rt_v2i size; +} rt_rect2i; +#endif