Progress towards rendering with effects

- Added a null renderer to simplify testing
This commit is contained in:
Kevin Trogant 2024-03-08 01:08:51 +01:00
parent efd1f5f983
commit 3d0d4169f1
24 changed files with 656 additions and 59 deletions

View File

@ -1,5 +1,4 @@
optimization speed;
passes {
pass0 {
vertex {

View File

@ -1,5 +0,0 @@
passes {
pass0 {
pipeline test/shader/static_object.pipeline;
}
}

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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},
};

View File

@ -7,6 +7,7 @@
#include "runtime/mem_arena.h"
#include "runtime/runtime.h"
#include "gfx/effect.h"
#include "gfx/gfx.h"
#include <assert.h>
@ -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);

View File

@ -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

View File

@ -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();
}

View File

@ -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,

View File

@ -1,4 +1,11 @@
/* Stdlib */
#include <stdint.h>
/* 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"

171
src/gfx/render_list.c Normal file
View File

@ -0,0 +1,171 @@
#include "render_list.h"
#include "runtime/threading.h"
#include "runtime/mem_arena.h"
#include "runtime/config.h"
#include <string.h>
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;
}

View File

@ -1,4 +1,70 @@
#ifndef RT_RENDER_LIST_H
#define RT_RENDER_LIST_H
/* a render list collects render objects. */
#include <stdint.h>
#include <stdbool.h>
#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

View File

@ -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);

View File

@ -5,3 +5,4 @@ subdir('app_framework')
# Renderer libs
subdir('renderer/vk')
subdir('renderer/null')

View File

@ -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()

122
src/renderer/null/null.c Normal file
View File

@ -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);
}

View File

@ -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

View File

@ -8,3 +8,12 @@
#if defined(VY_USE_XLIB)
#include <X11/Xlib.h>
#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"

View File

@ -5,6 +5,7 @@
#include "runtime/threading.h"
#include "gfx/renderer_api.h"
#include "gfx/effect.h"
#include "gpu.h"
#include "pipelines.h"

View File

@ -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);

View File

@ -10,6 +10,7 @@
#include "resources.h"
#include "threading.h"
#include "gfx/effect.h"
#include "gfx/renderer_api.h"
#include <assert.h>
@ -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] = {"<INVALID>", "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] : "<INVALID>");
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] = {
"<INVALID>",
@ -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);
}

View File

@ -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)

View File

@ -1,7 +1,14 @@
#include <stdio.h>
#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)];
@ -37,37 +44,173 @@ static rt_result NegRelPtrTest(void) {
return RT_SUCCESS;
}
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;
}