diff --git a/assets/shader/static_object.pipeline b/assets/shader/static_object.effect similarity index 99% rename from assets/shader/static_object.pipeline rename to assets/shader/static_object.effect index 159b4c3..9895f2e 100644 --- a/assets/shader/static_object.pipeline +++ b/assets/shader/static_object.effect @@ -1,5 +1,4 @@ optimization speed; - passes { pass0 { vertex { diff --git a/assets/test.effect b/assets/test.effect deleted file mode 100644 index 6e7b091..0000000 --- a/assets/test.effect +++ /dev/null @@ -1,5 +0,0 @@ -passes { - pass0 { - pipeline test/shader/static_object.pipeline; - } -} diff --git a/meson.build b/meson.build index 67da532..ccbc968 100644 --- a/meson.build +++ b/meson.build @@ -83,6 +83,8 @@ engine_link_libs = [] if get_option('default_library') == 'static' if get_option('static_renderer') == 'vk' 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] else error('Invalid static_renderer option ', get_option('static_renderer')) endif diff --git a/src/app_framework/app.c b/src/app_framework/app.c index 96df7ef..8ac31bb 100644 --- a/src/app_framework/app.c +++ b/src/app_framework/app.c @@ -36,6 +36,10 @@ RT_DLLEXPORT int rtWin32Entry(HINSTANCE hInstance, if (rtInitRuntime() != RT_SUCCESS) return 1; + rtRegisterCVAR(&rt_Fullscreen); + rtRegisterCVAR(&rt_WindowWidth); + rtRegisterCVAR(&rt_WindowHeight); + rtRegisterRendererCVars(); if (app_callbacks.RegisterCVars) diff --git a/src/app_framework/main_loop.h b/src/app_framework/main_loop.h index dfeab67..37708db 100644 --- a/src/app_framework/main_loop.h +++ b/src/app_framework/main_loop.h @@ -4,6 +4,10 @@ #include "runtime/runtime.h" #include "runtime/threading.h" +#ifdef __cplusplus +extern "C" { +#endif + typedef void rt_main_loop_update_fn(unsigned int frame_id); typedef void rt_main_loop_render_fn(unsigned int frame_id); @@ -33,4 +37,8 @@ RT_DLLEXPORT rt_result rtInitMainLoop(rt_main_loop_update_fn *update_cb, RT_DLLEXPORT void rtShutdownMainLoop(void); +#ifdef __cplusplus +} +#endif + #endif diff --git a/src/asset_compiler/asset_compiler.c b/src/asset_compiler/asset_compiler.c index 7d41eee..0cd9420 100644 --- a/src/asset_compiler/asset_compiler.c +++ b/src/asset_compiler/asset_compiler.c @@ -63,11 +63,11 @@ static rt_asset_db _asset_db; static rt_processing_queue _processing_queue; -extern RT_ASSET_PROCESSOR_FN(PipelineProcessor); +extern RT_ASSET_PROCESSOR_FN(EffectProcessor); extern RT_ASSET_PROCESSOR_FN(FramegraphProcessor); static rt_asset_processor _processors[] = { - { .file_ext = ".pipeline", .proc = PipelineProcessor}, + { .file_ext = ".effect", .proc = EffectProcessor}, {.file_ext = ".framegraph", .proc = FramegraphProcessor}, }; diff --git a/src/asset_compiler/effect_processor.c b/src/asset_compiler/effect_processor.c index 5d9e969..0c23775 100644 --- a/src/asset_compiler/effect_processor.c +++ b/src/asset_compiler/effect_processor.c @@ -7,6 +7,7 @@ #include "runtime/mem_arena.h" #include "runtime/runtime.h" +#include "gfx/effect.h" #include "gfx/gfx.h" #include @@ -35,6 +36,7 @@ typedef struct { typedef struct { unsigned int pass_count; rt_parsed_pipeline_data pipelines[RT_MAX_SUBRESOURCES]; + rt_render_pass_id pass_ids[RT_MAX_SUBRESOURCES]; } rt_parsed_effect_data; enum { @@ -336,6 +338,7 @@ static rt_result ParseEffect(rt_file_id fid, file_path); return RT_INVALID_VALUE; } + effect->pass_count = passes_list->count; const rt_parsed_stmt *pass_stmt = &state.statements[passes_list->first]; for (unsigned int i = 0; i < passes_list->count; ++i, pass_stmt = &state.statements[pass_stmt->next]) { @@ -347,6 +350,9 @@ static rt_result ParseEffect(rt_file_id fid, file_path); return RT_INVALID_VALUE; } + effect->pass_ids[i] = + rtCalculateRenderPassID(pass_stmt->attribute.start, pass_stmt->attribute.length); + result = ParsePipeline(&state, pass_stmt->list_index, file_path, @@ -360,7 +366,7 @@ static rt_result ParseEffect(rt_file_id fid, return result; } -RT_ASSET_PROCESSOR_FN(PipelineProcessor) { +RT_ASSET_PROCESSOR_FN(EffectProcessor) { rt_loaded_asset asset = LoadAsset(file); if (!asset.buffer) return RT_UNKNOWN_ERROR; @@ -371,7 +377,21 @@ RT_ASSET_PROCESSOR_FN(PipelineProcessor) { if (result != RT_SUCCESS) goto out; + rt_effect_info effect_info; + effect_info.pass_count = effect.pass_count; + + rt_resource effect_resource = {0}; + effect_resource.data = &effect_info; + effect_resource.type = RT_RESOURCE_EFFECT; + effect_resource.subresource_count = effect.pass_count; + + *new_resource_count = 0; + + const char *name = rtGetFilePath(file); for (unsigned int i = 0; i < effect.pass_count; ++i) { + effect_info.passes[i].pass_id = effect.pass_ids[i]; + effect_info.passes[i].pipeline = RT_INVALID_RESOURCE_ID; + rt_parsed_pipeline_data pipeline; memcpy(&pipeline, &effect.pipelines[i], sizeof(pipeline)); @@ -407,14 +427,27 @@ RT_ASSET_PROCESSOR_FN(PipelineProcessor) { ? shader_resources[pipeline.compute_shader] : RT_INVALID_RESOURCE_ID; rt_resource_id pipeline_id; - const char *name = rtGetFilePath(file); - result = rtCreateResources(1, &name, &pipeline_resource, &pipeline_id); + char pipeline_name[260]; + rtSPrint(pipeline_name, sizeof(pipeline_name), "%s:%u", name, i); + + char *ppln = &pipeline_name[0]; + + result = rtCreateResources(1, &ppln, &pipeline_resource, &pipeline_id); if (result == RT_SUCCESS) { - new_resources[0] = pipeline_id; - memcpy(&new_resources[1], shader_resources, sizeof(shader_resources)); - *new_resource_count = 1 + pipeline.shader_count; + new_resources[i] = pipeline_id; + memcpy(&new_resources[i + 1], shader_resources, sizeof(shader_resources)); + *new_resource_count += 1 + pipeline.shader_count; + effect_resource.subresources[i] = pipeline_id; + effect_info.passes[i].pipeline = pipeline_id; } } + + rt_resource_id effect_id = 0; + result = rtCreateResources(1, &name, &effect_resource, &effect_id); + if (result == RT_SUCCESS) { + new_resources[*new_resource_count] = effect_id; + *new_resource_count += 1; + } out: rtLog("AC", "Released %p", asset.buffer); rtReleaseBuffer(asset.buffer, asset.size); diff --git a/src/gfx/effect.h b/src/gfx/effect.h index cfa2abe..2b698f6 100644 --- a/src/gfx/effect.h +++ b/src/gfx/effect.h @@ -9,17 +9,23 @@ #include "gfx.h" #include "runtime/resources.h" -typedef struct { - /* Id of the render pass during which this effect pass is run. */ - rt_render_pass_id pass_id; - +typedef struct rt_pipeline_info_s { rt_resource_id vertex_shader; rt_resource_id fragment_shader; rt_resource_id compute_shader; + + /* TODO(Kevin): Fixed function settings */ +} rt_pipeline_info; + +typedef struct { + /* Id of the render pass during which this effect pass is run. */ + rt_render_pass_id pass_id; + rt_resource_id pipeline; } rt_effect_pass_info; typedef struct { - + uint32_t pass_count; + rt_effect_pass_info passes[RT_MAX_SUBRESOURCES]; } rt_effect_info; #endif diff --git a/src/gfx/gfx_main.c b/src/gfx/gfx_main.c index 721fd7d..12e63e4 100644 --- a/src/gfx/gfx_main.c +++ b/src/gfx/gfx_main.c @@ -15,6 +15,7 @@ */ rt_renderer_api g_renderer; +extern rt_cvar rt_MaxFramegraphs; #ifndef RT_STATIC_LIB static rt_dynlib _renderer_lib; @@ -22,7 +23,7 @@ static rt_dynlib _renderer_lib; static bool _renderer_loaded = false; RT_DLLEXPORT -RT_CVAR_S(rt_Renderer, "Select the render backend. Available options: [vk], Default: vk", "vk"); +RT_CVAR_S(rt_Renderer, "Select the render backend. Available options: [vk, null], Default: vk", "vk"); #ifdef RT_STATIC_LIB extern void RT_RENDERER_API_FN(RegisterCVars)(void); @@ -58,6 +59,9 @@ extern void RT_RENDERER_API_FN(CmdTransitionRenderTarget)(rt_command_buffer_hand extern rt_result InitFramegraphManager(void); extern void ShutdownFramegraphManager(void); +extern rt_result InitRenderLists(void); +extern void ShutdownRenderLists(void); +extern void ResetRenderLists(void); static bool LoadRenderer(void) { @@ -140,6 +144,9 @@ RT_DLLEXPORT void rtRegisterRendererCVars(void) { } RT_DLLEXPORT rt_result rtInitGFX(rt_renderer_init_info *renderer_info) { + rtRegisterCVAR(&rt_Renderer); + rtRegisterCVAR(&rt_MaxFramegraphs); + if (!_renderer_loaded) { if (!LoadRenderer()) return RT_UNKNOWN_ERROR; @@ -154,10 +161,14 @@ RT_DLLEXPORT rt_result rtInitGFX(rt_renderer_init_info *renderer_info) { if ((result = InitFramegraphManager()) != RT_SUCCESS) return result; + if ((result = InitRenderLists()) != RT_SUCCESS) + return result; + return result; } RT_DLLEXPORT void rtShutdownGFX(void) { + ShutdownRenderLists(); ShutdownFramegraphManager(); g_renderer.Shutdown(); } @@ -168,4 +179,5 @@ RT_DLLEXPORT void rtBeginGFXFrame(unsigned int frame_id) { RT_DLLEXPORT void rtEndGFXFrame(unsigned int frame_id) { g_renderer.EndFrame(frame_id); + ResetRenderLists(); } diff --git a/src/gfx/meson.build b/src/gfx/meson.build index b4bd641..c70a14a 100644 --- a/src/gfx/meson.build +++ b/src/gfx/meson.build @@ -8,6 +8,7 @@ gfx_lib = library('rtgfx', 'gfx_framegraph.c', 'gfx_main.c', + 'render_list.c', # Contrib Sources dependencies : gfx_deps, include_directories : engine_incdir, diff --git a/src/gfx/pch/gfx_pch.h b/src/gfx/pch/gfx_pch.h index 564e44a..ee40594 100644 --- a/src/gfx/pch/gfx_pch.h +++ b/src/gfx/pch/gfx_pch.h @@ -1,4 +1,11 @@ +/* Stdlib */ +#include + +/* Project */ #include "gfx.h" /* Commonly used runtime headers */ #include "runtime/runtime.h" +#include "runtime/threading.h" +#include "runtime/mem_arena.h" +#include "runtime/config.h" diff --git a/src/gfx/render_list.c b/src/gfx/render_list.c new file mode 100644 index 0000000..99f53c8 --- /dev/null +++ b/src/gfx/render_list.c @@ -0,0 +1,171 @@ +#include "render_list.h" + +#include "runtime/threading.h" +#include "runtime/mem_arena.h" +#include "runtime/config.h" + +#include + +RT_CVAR_I(rt_RenderListPoolSize, + "Size of the pool allocated for render lists in bytes. Default: 8 MiB", + RT_MB(8)); + +typedef struct { + size_t size; + const char *name; +} rt_render_object_type_data; + +typedef struct rt_list_pool_s { + size_t capacity; + struct rt_list_pool_s *next; +} rt_list_pool; + +#define DEFAULT_LIST_CAPACITY RT_KB(1) + +static rt_render_object_type_data _types[RT_MAX_RENDER_OBJECT_TYPE + 1]; +static unsigned int _type_count = 0; +static rt_rwlock _type_lock; + +static rt_arena _list_arena; +static rt_list_pool *_first_free_list; +static rt_mutex *_list_lock; + +rt_result InitRenderLists(void) { + rt_create_rwlock_result lock_res = rtCreateRWLock(); + if (!lock_res.ok) + return RT_UNKNOWN_ERROR; + _type_lock = lock_res.lock; + + rt_create_arena_result arena_res = rtCreateArena(NULL, (size_t)rt_RenderListPoolSize.i); + if (!arena_res.ok) { + rtDestroyRWLock(&_type_lock); + return RT_OUT_OF_MEMORY; + } + _list_arena = arena_res.arena; + + _list_lock = rtCreateMutex(); + if (!_list_lock) { + rtReleaseArena(&_list_arena); + rtDestroyRWLock(&_type_lock); + } + + return RT_SUCCESS; +} + +void ShutdownRenderLists(void) { + rtDestroyRWLock(&_type_lock); + rtDestroyMutex(_list_lock); + rtReleaseArena(&_list_arena); +} + +RT_DLLEXPORT rt_render_object_type rtRegisterRenderObjectType(size_t object_size, + const char *debug_name) { + if (_type_count == RT_MAX_RENDER_OBJECT_TYPE) { + rtReportError("GFX", "Too many render object types (max is %u)", RT_MAX_RENDER_OBJECT_TYPE); + return RT_INVALID_RENDER_OBJECT_TYPE; + } + rtLockWrite(&_type_lock); + rt_render_object_type type = (rt_render_object_type)++_type_count; + _types[_type_count].size = object_size; + _types[_type_count].name = debug_name; + if (debug_name) + rtLog("GFX", + "Registered render object type %s; object size: %zu. Type: %u", + debug_name, + object_size, + _type_count); + else + rtLog("GFX", + "Registered unnamed render object type; object size: %zu. Type: %u", + object_size, + _type_count); + rtUnlockWrite(&_type_lock); + return type; +} + + +RT_DLLEXPORT size_t rtGetRenderObjectSize(rt_render_object_type type) { + size_t size = 0; + rtLockRead(&_type_lock); + if (type > RT_INVALID_RENDER_OBJECT_TYPE && type <= _type_count) + size = _types[type].size; + rtUnlockRead(&_type_lock); + return size; +} + +RT_DLLEXPORT const char *rtGetRenderObjectTypeDebugName(rt_render_object_type type) { + const char *name = NULL; + rtLockRead(&_type_lock); + if (type > RT_INVALID_RENDER_OBJECT_TYPE && type <= _type_count) + name = _types[type].name; + rtUnlockRead(&_type_lock); + return name; +} + +static rt_create_render_list_result CreateNewList(rt_render_object_type type, size_t capacity) { + rt_create_render_list_result res = {.ok = false}; + rtLockMutex(_list_lock); + + if (!_first_free_list || _first_free_list->capacity < capacity) { /* Allocate a new list */ + rt_list_pool *pool = + rtArenaPush(&_list_arena, sizeof(rt_list_pool) + capacity); + if (!pool) { + rtReportError("GFX", + "Out of render list pool space! Configured space: %d kiB", + rt_RenderListPoolSize.i / 1024); + goto out; + } + pool->capacity = capacity; + pool->next = _first_free_list; + _first_free_list = pool; + } + rt_render_list list; + list.data = (char *)_first_free_list + sizeof(rt_list_pool); + list.type = type; + list.length = 0; + res.ok = true; + res.list = list; + _first_free_list = _first_free_list->next; +out: + rtUnlockMutex(_list_lock); + return res; +} + +RT_DLLEXPORT rt_create_render_list_result rtCreateRenderList(rt_render_object_type type) { + return CreateNewList(type, DEFAULT_LIST_CAPACITY); +} + +void ResetRenderLists(void) { + rtLockMutex(_list_lock); + _first_free_list = NULL; + rtArenaClear(&_list_arena); + rtUnlockMutex(_list_lock); +} + +RT_DLLEXPORT bool rtPushRenderListEntry(rt_render_list *list, const void *object) { + size_t object_size = rtGetRenderObjectSize(list->type); + rt_list_pool *pool = (rt_list_pool *)((char *)list->data - sizeof(rt_list_pool)); + size_t list_capacity = pool->capacity / object_size; + + if (list->length == list_capacity) { + /* "Grow" the list */ + rt_create_render_list_result list_res = CreateNewList(list->type, pool->capacity * 2); + if (!list_res.ok) + return false; + + memcpy(list_res.list.data, list->data, list->length * object_size); + + rtLockMutex(_list_lock); + pool->next = _first_free_list; + _first_free_list = pool; + rtUnlockMutex(_list_lock); + + list_res.list.length = list->length; + *list = list_res.list; + } + + char *dst = (char *)list->data + list->length * object_size; + memcpy(dst, object, object_size); + ++list->length; + return true; +} \ No newline at end of file diff --git a/src/gfx/render_list.h b/src/gfx/render_list.h index 4bf7052..eb5d3ed 100644 --- a/src/gfx/render_list.h +++ b/src/gfx/render_list.h @@ -1,4 +1,70 @@ #ifndef RT_RENDER_LIST_H #define RT_RENDER_LIST_H +/* a render list collects render objects. */ + +#include +#include + +#include "runtime/runtime.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Identifies a type of render objects. */ +typedef uint32_t rt_render_object_type; +typedef uint32_t rt_render_object_type_mask; + +enum { + RT_INVALID_RENDER_OBJECT_TYPE = 0, + RT_MAX_RENDER_OBJECT_TYPE = 32, +}; + +#define RT_RENDER_OBJECT_TYPE_BIT(type) (1u << ((type)-1)) + +/* Registers a new render object type. + * debug_name is optional and may be NULL. + */ +RT_DLLEXPORT rt_render_object_type rtRegisterRenderObjectType(size_t object_size, + const char *debug_name); + +RT_DLLEXPORT size_t rtGetRenderObjectSize(rt_render_object_type type); + +RT_DLLEXPORT const char *rtGetRenderObjectTypeDebugName(rt_render_object_type type); + +typedef struct { + rt_render_object_type type; + size_t length; + void *data; +} rt_render_list; + +/* Returns a pointer to the i-th render list element. + * Works for every valid type, because the size is determined at runtime. */ +RT_INLINE void *rtGetRenderListElement(const rt_render_list *list, size_t index) { + size_t size = rtGetRenderObjectSize(list->type); + return (char *)list->data + size * index; +} + +/* Returns the i-th render list element, cast to type T. + * Saves a rtGetRenderObjectSize call, if the type is known beforehand. */ +#define RT_GET_RENDER_LIST_ELEMENT(list, T, index) *(((T *)(list).data) + (index)) + +typedef struct { + bool ok; + rt_render_list list; +} rt_create_render_list_result; + +/* Create a render list for a particular object type. + * + * Render Lists have a lifetime of one frame. */ +RT_DLLEXPORT rt_create_render_list_result rtCreateRenderList(rt_render_object_type type); + +/* Append a render object to a list. The object must be of the correct type. */ +RT_DLLEXPORT bool rtPushRenderListEntry(rt_render_list *list, const void *object); + +#ifdef __cplusplus +} +#endif + #endif diff --git a/src/gfx/renderer_api.h b/src/gfx/renderer_api.h index e92abc9..5577f0c 100644 --- a/src/gfx/renderer_api.h +++ b/src/gfx/renderer_api.h @@ -17,6 +17,7 @@ extern "C" { /* Handles for backend objects */ +#define RT_RENDERER_BACKEND_HANDLE_MAX_INDEX ((1u<<24)-1) #define RT_RENDER_BACKEND_HANDLE_MAX_VERSION 255 #define RT_RENDER_BACKEND_HANDLE(name) \ @@ -59,13 +60,6 @@ typedef enum { RT_TRANSFER_QUEUE, } rt_gpu_queue; - -typedef struct { - rt_resource_id vertex_shader; - rt_resource_id fragment_shader; - rt_resource_id compute_shader; -} rt_pipeline_info; - #if 0 /* Attributes are used to bind buffers (or textures) to symbolic values. * For example, an attribute might be bound to "CELL_GRID", which would be @@ -182,6 +176,8 @@ typedef enum { /* Renderer API */ +typedef struct rt_pipeline_info_s rt_pipeline_info; + typedef void rt_register_renderer_cvars_fn(void); typedef rt_result rt_init_renderer_fn(const rt_renderer_init_info *info); typedef void rt_shutdown_renderer_fn(void); diff --git a/src/meson.build b/src/meson.build index f2cf339..32aed4b 100644 --- a/src/meson.build +++ b/src/meson.build @@ -5,3 +5,4 @@ subdir('app_framework') # Renderer libs subdir('renderer/vk') +subdir('renderer/null') diff --git a/src/renderer/null/meson.build b/src/renderer/null/meson.build new file mode 100644 index 0000000..1e06f75 --- /dev/null +++ b/src/renderer/null/meson.build @@ -0,0 +1,10 @@ +null_renderer_lib = library('rtnull', + 'null.c', + # Project Sources + include_directories : engine_incdir, + link_with : runtime_lib, + install : true) + +engine_libs += null_renderer_lib +engine_lib_paths += null_renderer_lib.full_path() + diff --git a/src/renderer/null/null.c b/src/renderer/null/null.c new file mode 100644 index 0000000..09339f8 --- /dev/null +++ b/src/renderer/null/null.c @@ -0,0 +1,122 @@ +/* "Null" renderer implementation. + * Useful for headless testing */ + +#include "gfx/renderer_api.h" +#include "runtime/runtime.h" + +#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 } + +#define RETURN_HANDLE_STUB(type) RETURN_HANDLE_STUB2(type, 1) + +#define RETURN_HANDLE_ARRAY_STUB2(out, count, initial) \ + static unsigned int s_next = (initial); \ + for (uint32_t i = 0; i < (count); ++i) { \ + (out)[i].index = (s_next++) % RT_RENDERER_BACKEND_HANDLE_MAX_INDEX; \ + (out)[i].version = 1; \ + } + +#define RETURN_HANDLE_ARRAY_STUB(out, count) RETURN_HANDLE_ARRAY_STUB2(out, count, 1) + +void RT_RENDERER_API_FN(RegisterCVars)(void) { +} + +rt_result RT_RENDERER_API_FN(Init)(const rt_renderer_init_info *info) { + RT_UNUSED(info); + return RT_SUCCESS; +} + +void RT_RENDERER_API_FN(Shutdown)(void) { +} + +void RT_RENDERER_API_FN(BeginFrame)(unsigned int frame_id) { + RT_UNUSED(frame_id); +} + +void RT_RENDERER_API_FN(EndFrame)(unsigned int frame_id) { + RT_UNUSED(frame_id); +} + +rt_pipeline_handle RT_RENDERER_API_FN(CompilePipeline)(const rt_pipeline_info *info) { + RT_UNUSED(info); + RETURN_HANDLE_STUB(rt_pipeline_handle); +} + +void RT_RENDERER_API_FN(DestroyPipeline)(rt_pipeline_handle handle) { + RT_UNUSED(handle); +} + +rt_render_target_handle RT_RENDERER_API_FN(CreateRenderTarget)(const rt_render_target_info *info) { + RT_UNUSED(info); + RETURN_HANDLE_STUB2(rt_render_target_handle, 2); +} + +rt_render_target_handle RT_RENDERER_API_FN(GetSwapchainRenderTarget)(void) { + return (rt_render_target_handle){.index = 1, .version = 1}; +} + +void RT_RENDERER_API_FN(DestroyRenderTarget)(rt_render_target_handle handle) { + RT_UNUSED(handle); +} + +rt_result RT_RENDERER_API_FN(AllocCommandBuffers)(uint32_t count, + const rt_alloc_command_buffer_info *info, + rt_command_buffer_handle *p_command_buffers) { + RT_UNUSED(info); + RETURN_HANDLE_ARRAY_STUB(p_command_buffers, count) + return RT_SUCCESS; +} + +rt_result RT_RENDERER_API_FN(SubmitCommandBuffers)(rt_gpu_queue queue, + const rt_submit_command_buffers_info *info) { + RT_UNUSED(queue); + RT_UNUSED(info); + return RT_SUCCESS; +} + +rt_result RT_RENDERER_API_FN(CreateSemaphores)(uint32_t count, + const rt_gpu_semaphore_info *info, + rt_gpu_semaphore_handle *p_semaphores) { + RT_UNUSED(info); + RETURN_HANDLE_ARRAY_STUB2(p_semaphores, count, 3) + return RT_SUCCESS; +} + +void RT_RENDERER_API_FN(DestroySemaphores)(uint32_t count, rt_gpu_semaphore_handle *semaphores) { + RT_UNUSED(count); + RT_UNUSED(semaphores); +} + +/* NOTE(Kevin): It might become necessary to actually track the value, to correctly simulate gpu + * behaviour */ +uint64_t RT_RENDERER_API_FN(GetSemaphoreValue)(rt_gpu_semaphore_handle sem) { + RT_UNUSED(sem); + return 0; +} + +rt_gpu_semaphore_handle RT_RENDERER_API_FN(GetSwapchainAvailableSemaphore)(void) { + return (rt_gpu_semaphore_handle){.index = 1, .version = 1}; +} + +rt_gpu_semaphore_handle RT_RENDERER_API_FN(GetRenderFinishedSemaphore)(void) { + return (rt_gpu_semaphore_handle){.index = 2, .version = 1}; +} + +void RT_RENDERER_API_FN(CmdBeginPass)(rt_command_buffer_handle cmd, + const rt_cmd_begin_pass_info *info) { + RT_UNUSED(cmd); + RT_UNUSED(info); +} + +void RT_RENDERER_API_FN(CmdEndPass)(rt_command_buffer_handle cmd) { + RT_UNUSED(cmd); +} + +void RT_RENDERER_API_FN(CmdTransitionRenderTarget)(rt_command_buffer_handle cmd, + rt_render_target_handle target, + rt_render_target_state state) { + RT_UNUSED(cmd); + RT_UNUSED(target); + RT_UNUSED(state); +} diff --git a/src/renderer/vk/meson.build b/src/renderer/vk/meson.build index e60a368..eb2d6e6 100644 --- a/src/renderer/vk/meson.build +++ b/src/renderer/vk/meson.build @@ -40,7 +40,7 @@ if vk_dep.found() cpp_args : platform_defs, install : true) - engine_libs = vk_renderer_lib + engine_libs += vk_renderer_lib engine_lib_paths += vk_renderer_lib.full_path() endif diff --git a/src/renderer/vk/pch/vk_pch.h b/src/renderer/vk/pch/vk_pch.h index ee6fba0..b7aa648 100644 --- a/src/renderer/vk/pch/vk_pch.h +++ b/src/renderer/vk/pch/vk_pch.h @@ -8,3 +8,12 @@ #if defined(VY_USE_XLIB) #include #endif + +/* GFX */ +#include "gfx/gfx.h" + +/* Commonly used runtime headers */ +#include "runtime/config.h" +#include "runtime/mem_arena.h" +#include "runtime/runtime.h" +#include "runtime/threading.h" diff --git a/src/renderer/vk/pipelines.c b/src/renderer/vk/pipelines.c index 84a0ecb..ea18952 100644 --- a/src/renderer/vk/pipelines.c +++ b/src/renderer/vk/pipelines.c @@ -5,6 +5,7 @@ #include "runtime/threading.h" #include "gfx/renderer_api.h" +#include "gfx/effect.h" #include "gpu.h" #include "pipelines.h" diff --git a/src/runtime/init.c b/src/runtime/init.c index 0ee1917..ca095e4 100644 --- a/src/runtime/init.c +++ b/src/runtime/init.c @@ -4,10 +4,6 @@ #include "file_tab.h" #include "buffer_manager.h" -extern rt_cvar rt_Renderer; -extern rt_cvar rt_Fullscreen; -extern rt_cvar rt_WindowWidth; -extern rt_cvar rt_WindowHeight; extern rt_cvar rt_BufferMemoryBudget; extern rt_cvar rt_FileTabCapacity; extern rt_cvar rt_MaxConcurrentAsyncIO; @@ -16,13 +12,8 @@ extern rt_cvar rt_ResourceCacheSize; extern rt_cvar rt_MaxCachedResources; extern rt_cvar rt_ResourceNamespaceSize; extern rt_cvar rt_DisableResourceNamespaceLoad; -extern rt_cvar rt_MaxFramegraphs; void RegisterRuntimeCVars(void) { - rtRegisterCVAR(&rt_Renderer); - rtRegisterCVAR(&rt_Fullscreen); - rtRegisterCVAR(&rt_WindowWidth); - rtRegisterCVAR(&rt_WindowHeight); rtRegisterCVAR(&rt_BufferMemoryBudget); rtRegisterCVAR(&rt_FileTabCapacity); rtRegisterCVAR(&rt_MaxConcurrentAsyncIO); @@ -31,7 +22,6 @@ void RegisterRuntimeCVars(void) { rtRegisterCVAR(&rt_MaxCachedResources); rtRegisterCVAR(&rt_ResourceNamespaceSize); rtRegisterCVAR(&rt_DisableResourceNamespaceLoad); - rtRegisterCVAR(&rt_MaxFramegraphs); } extern void SetMainThreadId(void); diff --git a/src/runtime/resource_manager.c b/src/runtime/resource_manager.c index c396f5c..ca72639 100644 --- a/src/runtime/resource_manager.c +++ b/src/runtime/resource_manager.c @@ -10,6 +10,7 @@ #include "resources.h" #include "threading.h" +#include "gfx/effect.h" #include "gfx/renderer_api.h" #include @@ -92,13 +93,13 @@ typedef struct { static size_t GetResourceDataSize(const rt_resource *resource) { switch (resource->type) { - case RT_RESOURCE_PIPELINE: - return sizeof(rt_pipeline_info); case RT_RESOURCE_SHADER: { /* Sizeof metadata + bytecode */ const rt_shader_info *info = resource->data; return sizeof(rt_shader_info) + (info) ? info->bytecode_length : 0; } break; + case RT_RESOURCE_PIPELINE: + return sizeof(rt_pipeline_info); case RT_RESOURCE_FRAMEGRAPH: { const rt_framegraph_info *info = resource->data; size_t size = sizeof(*info) + sizeof(rt_render_target_info) * info->render_target_count + @@ -110,6 +111,9 @@ static size_t GetResourceDataSize(const rt_resource *resource) { } return size; } break; + case RT_RESOURCE_EFFECT: { + return sizeof(rt_effect_info); + } break; default: rtLog("RESMGR", "Tried to get size of an invalid resource type %u", resource->type); } @@ -118,9 +122,6 @@ static size_t GetResourceDataSize(const rt_resource *resource) { static void CopyResourceData(const rt_resource *resource, void *dest) { switch (resource->type) { - case RT_RESOURCE_PIPELINE: - memcpy(dest, resource->data, sizeof(rt_pipeline_info)); - break; case RT_RESOURCE_SHADER: { /* Sizeof metadata + bytecode */ const rt_shader_info *info = resource->data; @@ -129,6 +130,9 @@ static void CopyResourceData(const rt_resource *resource, void *dest) { memcpy(dest_info + 1, rtResolveConstRelptr(&info->bytecode), info->bytecode_length); rtSetRelptr(&dest_info->bytecode, (void *)(dest_info + 1)); } break; + case RT_RESOURCE_PIPELINE: + memcpy(dest, resource->data, sizeof(rt_pipeline_info)); + break; case RT_RESOURCE_FRAMEGRAPH: { const rt_framegraph_info *info = resource->data; rt_framegraph_info *dest_info = dest; @@ -199,6 +203,9 @@ static void CopyResourceData(const rt_resource *resource, void *dest) { rtSetRelptr(&passes_dest[i].name, names_begin + (src_name - src_names)); } } break; + case RT_RESOURCE_EFFECT: { + memcpy(dest, resource->data, sizeof(rt_effect_info)); + } break; default: rtLog("RESMGR", "Tried to copy a resource of invalid type %u", resource->type); } @@ -956,13 +963,6 @@ RT_DLLEXPORT void rDebugLogResource(rt_resource_id id, const rt_resource *resour rtLog("RESMGR", " - %llx", resource->dependencies[i]); } switch (resource->type) { - case RT_RESOURCE_PIPELINE: { - const rt_pipeline_info *pipeline = resource->data; - rtLog("RESMGR", " pipeline data:"); - rtLog("RESMGR", " vertex shader: %llx", pipeline->vertex_shader); - rtLog("RESMGR", " fragment shader: %llx", pipeline->fragment_shader); - rtLog("RESMGR", " compute shader: %llx", pipeline->compute_shader); - } break; case RT_RESOURCE_SHADER: { static const char *stype_str[RT_SHADER_TYPE_count] = {"", "Vulkan"}; static const char *stage_str[RT_SHADER_STAGE_count] = {"Vertex", "Fragment", "Compute"}; @@ -976,6 +976,13 @@ RT_DLLEXPORT void rDebugLogResource(rt_resource_id id, const rt_resource *resour (shader->stage < RT_SHADER_STAGE_count) ? stage_str[shader->stage] : ""); rtLog("RESMGR", " bytecode: %zu bytes", shader->bytecode_length); } break; + case RT_RESOURCE_PIPELINE: { + const rt_pipeline_info *pipeline = resource->data; + rtLog("RESMGR", " pipeline data:"); + rtLog("RESMGR", " vertex shader: %llx", pipeline->vertex_shader); + rtLog("RESMGR", " fragment shader: %llx", pipeline->fragment_shader); + rtLog("RESMGR", " compute shader: %llx", pipeline->compute_shader); + } break; case RT_RESOURCE_FRAMEGRAPH: { static const char *format_str[RT_PIXEL_FORMAT_count] = { "", @@ -1055,6 +1062,15 @@ RT_DLLEXPORT void rDebugLogResource(rt_resource_id id, const rt_resource *resour } } } break; + case RT_RESOURCE_EFFECT: { + const rt_effect_info *effect = resource->data; + rtLog("RESMGR", " effect data:"); + for (uint32_t i = 0; i < effect->pass_count; ++i) { + rtLog("RESMGR", " pass %u:", i); + rtLog("RESMGR", " id: %llx", effect->passes[i].pass_id); + rtLog("RESMGR", " pipeline: %llx", effect->passes[i].pipeline); + } + } break; default: rtLog("RESMGR", " unknown data at: %llx", (uintptr_t)resource->data); } diff --git a/tests/meson.build b/tests/meson.build index c976614..7c0ccf4 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -1,6 +1,11 @@ +test_link_libs = [runtime_lib, gfx_lib] +if get_option('default_library') == 'static' + test_link_libs += null_renderer_lib +endif + rttest_exe = executable('rttest', 'rttest.c', - link_with : engine_link_libs, + link_with : test_link_libs, include_directories : engine_incdir, win_subsystem : 'console') test('runtime test', rttest_exe) diff --git a/tests/rttest.c b/tests/rttest.c index 964443e..483df2c 100644 --- a/tests/rttest.c +++ b/tests/rttest.c @@ -1,13 +1,20 @@ #include +#include "runtime/config.h" #include "runtime/runtime.h" +#include "gfx/gfx.h" +#include "gfx/render_list.h" +#include "gfx/renderer_api.h" + +extern rt_cvar rt_Renderer; + /* Check basic relative pointer behaviour */ static rt_result RelPtrTest(void) { char buf[sizeof(rt_relptr) + sizeof(unsigned int)]; - rt_relptr *ptr = (rt_relptr *)buf; + rt_relptr *ptr = (rt_relptr *)buf; unsigned int *target = (unsigned int *)&buf[sizeof(rt_relptr)]; - *target = 42; + *target = 42; rtSetRelptr(ptr, target); void *resolved = rtResolveRelptr(ptr); @@ -37,37 +44,173 @@ static rt_result NegRelPtrTest(void) { return RT_SUCCESS; } -/* Scaffolding +static rt_result SetupRenderListFixture(void) { + rt_result res = rtInitRuntime(); + if (res != RT_SUCCESS) + return res; + + rt_Renderer.s = "null"; + + rt_renderer_init_info info = {0}; + rtInitGFX(&info); + return res; +} + +static void TeardownRenderListFixture(void) { + rtShutdownGFX(); + rtShutdownRuntime(); +} + +/* Check render list interface */ +static rt_result PushRenderList(void) { + typedef struct { + int a; + float b; + } dummy_type; + rt_render_object_type type = rtRegisterRenderObjectType(sizeof(dummy_type), "DummyType"); + + rt_create_render_list_result list_res = rtCreateRenderList(type); + if (!list_res.ok) { + return RT_INVALID_VALUE; + } + rt_render_list list = list_res.list; + dummy_type dummy = {42, 21.f}; + rtPushRenderListEntry(&list, &dummy); + + dummy_type check = RT_GET_RENDER_LIST_ELEMENT(list, dummy_type, 0); + + if (check.a != dummy.a || check.b != dummy.b) + return RT_INVALID_VALUE; + + return RT_SUCCESS; +} + +static rt_result PushLongRenderList(void) { + typedef struct { + float p[3]; + float v[3]; + } dummy_type; + rt_render_object_type type = rtRegisterRenderObjectType(sizeof(dummy_type), "DummyType"); + + rt_create_render_list_result list_res = rtCreateRenderList(type); + if (!list_res.ok) + return RT_INVALID_VALUE; + rt_render_list list = list_res.list; + for (uint32_t i = 0; i < 512; ++i) { + dummy_type dummy; + for (int j = 0; j < 3; ++j) + dummy.v[j] = dummy.p[j] = (float)i; + rtPushRenderListEntry(&list, &dummy); + + dummy_type check = RT_GET_RENDER_LIST_ELEMENT(list, dummy_type, i); + for (int j = 0; j < 3; ++j) { + if (check.p[j] != (float)i || check.v[j] != (float)i) + return RT_INVALID_VALUE; + } + } + + for (uint32_t i = 0; i < 512; ++i) { + dummy_type check = RT_GET_RENDER_LIST_ELEMENT(list, dummy_type, i); + for (int j = 0; j < 3; ++j) { + if (check.p[j] != (float)i || check.v[j] != (float)i) + return RT_INVALID_VALUE; + } + } + return RT_SUCCESS; +} + +/* Scaffolding * * Run all the test cases, output if they passed or failed. */ +typedef rt_result rt_fixture_setup_fn(void); +typedef void rt_fixture_teardown_fn(void); + +typedef struct { + const char *name; + bool is_initialized; + rt_fixture_setup_fn *setup; + rt_fixture_teardown_fn *teardown; +} rt_test_fixture; + typedef rt_result rt_test_fnc(void); typedef struct { const char *name; rt_test_fnc *fnc; - + rt_test_fixture *fixture; } rt_test_case; -#define TEST_CASE(fn) { .name = #fn, .fnc = fn, } +#define TEST_CASE(fn) \ + { .name = #fn, .fnc = fn, .fixture = NULL, } -static rt_test_case _test_cases[] = {TEST_CASE(RelPtrTest), TEST_CASE(NegRelPtrTest)}; +#define TEST_CASE_FIXTURE(fn, _fixture) \ + { .name = #fn, .fnc = fn, .fixture = &_fixture } + +/* X-Macro to create named fixtures */ + +/* Add more fixtures here*/ +#define TEST_FIXTURE_LIST \ + TEST_FIXTURE(render_list_fixture, SetupRenderListFixture, TeardownRenderListFixture) + +#define TEST_FIXTURE(n, setup_fn, teardown_fn) \ + rt_test_fixture n = {.name = #n, \ + .is_initialized = false, \ + .setup = setup_fn, \ + .teardown = teardown_fn}; +TEST_FIXTURE_LIST + +#undef TEST_FIXTURE +#define TEST_FIXTURE(n, _s, _t) &n + +static rt_test_fixture *_test_fixtures[] = {TEST_FIXTURE_LIST}; + +static rt_test_case _test_cases[] = {TEST_CASE(RelPtrTest), + TEST_CASE(NegRelPtrTest), + TEST_CASE_FIXTURE(PushRenderList, render_list_fixture), + TEST_CASE_FIXTURE(PushLongRenderList, render_list_fixture)}; int main() { int out = 0; for (size_t i = 0; i < RT_ARRAY_COUNT(_test_cases); ++i) { + if (_test_cases[i].fixture) { + rt_test_fixture *fixture = _test_cases[i].fixture; + if (!fixture->is_initialized) { + printf("[SETUP %s] ... ", fixture->name); + rt_result res = fixture->setup(); + if (res == RT_SUCCESS) { + printf("OK\n"); + fixture->is_initialized = true; + } else { + printf("FAILED (%u)\n", res); + ++out; + printf("Cannot run test case %s because the setup of fixture %s failed!\n", + _test_cases[i].name, + fixture->name); + continue; + } + } + } printf("[%s] ... ", _test_cases[i].name); rt_result res = _test_cases[i].fnc(); if (res == RT_SUCCESS) { printf("OK\n"); - } - else { + } else { printf("FAILED (%u)\n", res); ++out; } } + /* Teardown fixtures */ + for (size_t i = 0; i < RT_ARRAY_COUNT(_test_fixtures); ++i) { + if (_test_fixtures[i]->is_initialized) { + printf("[TEARDOWN %s] ... ", _test_fixtures[i]->name); + _test_fixtures[i]->teardown(); + printf("DONE\n"); + } + } + return out; }