Load framegraphs from resource files

This commit is contained in:
Kevin Trogant 2024-02-06 17:57:14 +01:00
parent bdd3db98bb
commit cc49a017f9
12 changed files with 222 additions and 28 deletions

64
assets/test.framegraph Normal file
View File

@ -0,0 +1,64 @@
render_targets {
color0 {
format R8G8B8A8_SRGB;
width 1024;
height 768;
sample_count 4;
}
swapchain_out {
format SWAPCHAIN;
width SWAPCHAIN;
height SWAPCHAIN;
sample_count 1;
}
depth0 {
format DEPTH24_STENCIL8;
width 512;
height 384;
sample_count 4;
}
}
passes {
pass0 {
writes {
color0 {
clear_value {
r 1.0;
g 1.0;
b 1.0;
a 1.0;
}
clear YES;
discard NO;
}
depth0 {
clear_value {
d 0.0;
s 0;
}
clear YES;
discard YES;
}
}
}
pass1 {
reads {
color0 {
mode SAMPLED;
}
}
writes {
swapchain_out {
clear NO;
discard NO;
}
}
}
}

View File

@ -28,6 +28,8 @@ elif compiler.get_argument_syntax() == 'msvc'
if buildtype == 'debug'
add_project_arguments(['/RTCsu'], language : ['c', 'cpp'])
endif
# Allow nameless struct/unions (standard in C11)
add_project_arguments(['/wd4201'], language : 'cpp')
endif
if get_option('default_library') == 'static'
@ -98,6 +100,7 @@ if get_option('build_asset_compiler')
'src/runtime/asset_compiler.c',
'src/runtime/description_parser.c',
'src/runtime/framegraph_processor.c',
'src/runtime/pipeline_processor.c',
'src/runtime/shader_compiler.c',
]

View File

@ -1,4 +1,6 @@
#include "runtime/gfx.h"
#include "runtime/resources.h"
#include "runtime/mem_arena.h"
static rt_framegraph *_framegraph;
@ -6,6 +8,7 @@ static rt_framegraph *_framegraph;
void Init(void) {
rtLog("GAME", "Init");
#if 0
rt_render_target_id rt_ids[4] = {rtCalculateRenderTargetID("rt0", sizeof("rt0")),
rtCalculateRenderTargetID("rt1", sizeof("rt1")),
rtCalculateRenderTargetID("rt2", sizeof("rt2")),
@ -54,8 +57,16 @@ void Init(void) {
};
rtSetRelptr(&info.render_passes, passes);
rtSetRelptr(&info.render_targets, rts);
#endif
_framegraph = rtCreateFramegraph(&info);
rt_resource_id id = rtGetResourceID("assets/test.framegraph");
rt_temp_arena temp = rtGetTemporaryArena(NULL, 0);
rt_resource *resource =
rtArenaPush(temp.arena, rtGetResourceSize(id));
rtGetResource(id, resource);
_framegraph = rtCreateFramegraph(resource->data);
}
/* Called after exiting the main-loop and before the runtime starts its shutdown */

View File

@ -2,10 +2,26 @@
VkFormat rtPixelFormatToVkFormat(rt_pixel_format format) {
switch (format) {
case RT_PIXEL_FORMAT_R8G8B8A8_UNORM:
return VK_FORMAT_R8G8B8A8_UNORM;
case RT_PIXEL_FORMAT_B8G8R8A8_UNORM:
return VK_FORMAT_B8G8R8A8_UNORM;
case RT_PIXEL_FORMAT_R8G8B8A8_SRGB:
return VK_FORMAT_R8G8B8A8_SRGB;
case RT_PIXEL_FORMAT_B8G8R8A8_SRGB:
return VK_FORMAT_B8G8R8A8_SRGB;
case RT_PIXEL_FORMAT_R8G8B8_UNORM:
return VK_FORMAT_R8G8B8_UNORM;
case RT_PIXEL_FORMAT_B8G8R8_UNORM:
return VK_FORMAT_B8G8R8_UNORM;
case RT_PIXEL_FORMAT_R8G8B8_SRGB:
return VK_FORMAT_R8G8B8_SRGB;
case RT_PIXEL_FORMAT_B8G8R8_SRGB:
return VK_FORMAT_B8G8R8_SRGB;
case RT_PIXEL_FORMAT_DEPTH24_STENCIL8:
return VK_FORMAT_D24_UNORM_S8_UINT;
case RT_PIXEL_FORMAT_DEPTH32:
return VK_FORMAT_D32_SFLOAT;
default:
return VK_FORMAT_UNDEFINED;
}
@ -20,6 +36,8 @@ VkSampleCountFlagBits rtSampleCountToFlags(unsigned int count) {
while (count > 1) {
if ((counts & count) == 0)
count >>= 1;
else
break;
}
return (VkSampleCountFlagBits)count;
}

View File

@ -175,7 +175,7 @@ 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) {
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;

View File

@ -65,9 +65,11 @@ static rt_asset_db _asset_db;
static rt_processing_queue _processing_queue;
extern RT_ASSET_PROCESSOR_FN(PipelineProcessor);
extern RT_ASSET_PROCESSOR_FN(FramegraphProcessor);
static rt_asset_processor _processors[] = {
{.file_ext = ".pipeline", .proc = PipelineProcessor}
{.file_ext = ".pipeline", .proc = PipelineProcessor},
{.file_ext = ".framegraph", .proc = FramegraphProcessor},
};
static void ProcessorThreadEntry(void *);

View File

@ -17,10 +17,34 @@
extern "C" {
#endif
typedef union {
float v[4];
struct {
float r;
float g;
float b;
float a;
};
} rt_color;
/* NOTE(kevin): When you add a value here, you need to handle them in
* framegraph_processor.c : ParseFramegraph
* and in the render target and texture functions of all renderers. */
typedef enum {
RT_PIXEL_FORMAT_INVALID,
RT_PIXEL_FORMAT_R8G8B8A8_UNORM,
RT_PIXEL_FORMAT_B8G8R8A8_UNORM,
RT_PIXEL_FORMAT_R8G8B8A8_SRGB,
RT_PIXEL_FORMAT_B8G8R8A8_SRGB,
RT_PIXEL_FORMAT_R8G8B8_UNORM,
RT_PIXEL_FORMAT_B8G8R8_UNORM,
RT_PIXEL_FORMAT_R8G8B8_SRGB,
RT_PIXEL_FORMAT_B8G8R8_SRGB,
RT_PIXEL_FORMAT_DEPTH24_STENCIL8,
RT_PIXEL_FORMAT_DEPTH32,
/* Special value indicating whichever format the swapchain uses */
RT_PIXEL_FORMAT_SWAPCHAIN,
@ -28,6 +52,21 @@ typedef enum {
RT_PIXEL_FORMAT_count,
} rt_pixel_format;
/* In renderer_api.h -> Not necessary for almost all gfx usage */
typedef struct rt_renderer_init_info_s rt_renderer_init_info;
RT_DLLEXPORT void rtRegisterRendererCVars(void);
RT_DLLEXPORT rt_result rtInitGFX(rt_renderer_init_info *renderer_info);
RT_DLLEXPORT void rtShutdownGFX(void);
/* *********************************************************************
* Framegraph API
*
* The framegraph is used to organize and schedule the work for a frame.
* *********************************************************************/
/* Special value for the .width and .height fields of rt_render_target_info
* to indicate that these should be set to the width or height of the swapchain, respectively. */
#define RT_RENDER_TARGET_SIZE_SWAPCHAIN 0
@ -44,8 +83,8 @@ typedef struct {
} rt_render_target_info;
typedef enum {
RT_RENDER_TARGET_READ_INPUT_ATTACHMENT,
RT_RENDER_TARGET_READ_SAMPLED,
RT_RENDER_TARGET_READ_INPUT_ATTACHMENT,
RT_RENDER_TARGET_READ_count,
} rt_render_target_read_mode;
@ -66,7 +105,7 @@ typedef enum {
typedef struct {
rt_render_target_id render_target;
union {
float color[4];
rt_color color;
struct {
float depth;
int32_t stencil;
@ -102,20 +141,6 @@ typedef struct {
rt_render_pass_finalize_fn *Finalize;
} rt_render_pass_bind_fns;
/* In renderer_api.h -> Not necessary for almost all gfx usage */
typedef struct rt_renderer_init_info_s rt_renderer_init_info;
RT_DLLEXPORT void rtRegisterRendererCVars(void);
RT_DLLEXPORT rt_result rtInitGFX(rt_renderer_init_info *renderer_info);
RT_DLLEXPORT void rtShutdownGFX(void);
/* Framegraph API
*
* The framegraph is used to organize and schedule the work for a frame.
*/
typedef struct rt_framegraph_s rt_framegraph;

View File

@ -310,6 +310,7 @@ static bool ValidateInfo(const rt_framegraph_info *info) {
rtReportError(
"GFX",
"Framegraph pass %u reads too many rendertargets: %u (maximum allowed is %u)",
i,
passes[i].read_render_target_count,
RT_RENDERPASS_MAX_READS);
return false;
@ -317,6 +318,7 @@ static bool ValidateInfo(const rt_framegraph_info *info) {
rtReportError(
"GFX",
"Framegraph pass %u writes too many rendertargets: %u (maximum allowed is %u)",
i,
passes[i].write_render_target_count,
RT_RENDERPASS_MAX_WRITES);
return false;

View File

@ -98,6 +98,16 @@ static size_t GetResourceDataSize(const rt_resource *resource) {
const rt_shader_info *info = resource->data;
return sizeof(rt_shader_info) + (info) ? info->bytecode_length : 0;
} break;
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 +
sizeof(rt_render_pass_info) * info->render_pass_count;
const rt_render_pass_info *passes = rtResolveConstRelptr(&info->render_passes);
for (uint32_t i = 0; i < info->render_pass_count; ++i) {
size += passes[i].read_render_target_count * sizeof(rt_render_target_read) +
passes[i].write_render_target_count * sizeof(rt_render_target_write);
}
} break;
default:
rtLog("RESMGR", "Tried to get size of an invalid resource type %u", resource->type);
}
@ -117,8 +127,38 @@ 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_FRAMEGRAPH: {
const rt_framegraph_info *info = resource->data;
rt_framegraph_info *dest_info = dest;
memcpy(dest_info, info, sizeof(*info));
memcpy(dest_info + 1,
rtResolveConstRelptr(&info->render_targets),
info->render_target_count * sizeof(rt_render_target_info));
rtSetRelptr(&dest_info->render_targets, (void *)(dest_info + 1));
char *passes_begin =
(char *)(dest_info + 1) + info->render_target_count * sizeof(rt_render_target_info);
memcpy(passes_begin,
rtResolveConstRelptr(&info->render_passes),
info->render_pass_count * sizeof(rt_render_pass_info));
char *read_write_dest = passes_begin + info->render_pass_count * sizeof(rt_render_pass_info);
rt_render_pass_info *passes_dest = (rt_render_pass_info *)passes_begin;
const rt_render_pass_info *passes =
(const rt_render_pass_info *)rtResolveConstRelptr(&info->render_passes);
for (uint32_t i = 0; i < info->render_pass_count; ++i) {
rtSetRelptr(&passes_dest[i].read_render_targets, read_write_dest);
memcpy(read_write_dest,
rtResolveConstRelptr(&passes[i].read_render_targets),
sizeof(rt_render_target_read) * passes[i].read_render_target_count);
read_write_dest += sizeof(rt_render_target_read) * passes[i].read_render_target_count;
rtSetRelptr(&passes_dest[i].write_render_targets, read_write_dest);
memcpy(read_write_dest,
rtResolveConstRelptr(&passes[i].write_render_targets),
sizeof(rt_render_target_write) * passes[i].write_render_target_count);
read_write_dest += sizeof(rt_render_target_write) * passes[i].write_render_target_count;
}
} break;
default:
rtLog("RESMGR", "Tried to get copy a resource of invalid type %u", resource->type);
rtLog("RESMGR", "Tried to copy a resource of invalid type %u", resource->type);
}
}
@ -749,6 +789,15 @@ RT_DLLEXPORT void rtPrefetchResources(const rt_resource_id *ids, uint32_t count)
}
}
RT_DLLEXPORT rt_resource_id rtGetResourceID(const char *name) {
size_t name_len = strlen(name);
rt_resource_id id = (rt_resource_id)rtHashBytes(name, name_len);
if (id == RT_INVALID_RESOURCE_ID || id == RT_TOMBSTONE_ID)
id = ~id;
return id;
}
RT_DLLEXPORT rt_result rtCreateResources(uint32_t count,
const char **names,
const rt_resource *resources,
@ -764,11 +813,7 @@ RT_DLLEXPORT rt_result rtCreateResources(uint32_t count,
rtLockWrite(&_namespace.lock);
for (uint32_t i = 0; i < count; ++i) {
size_t name_len = strlen(names[i]);
rt_resource_id id = (rt_resource_id)rtHashBytes(names[i], name_len);
if (id == RT_INVALID_RESOURCE_ID || id == RT_TOMBSTONE_ID)
id = ~id;
rt_resource_id id = rtGetResourceID(names[i]);
bool inserted = false;
for (size_t j = 0; j < ns_size; ++j) {
size_t at = (id + j) % ns_size;

View File

@ -35,6 +35,8 @@ typedef enum {
/* A pipeline state object */
RT_RESOURCE_PIPELINE,
RT_RESOURCE_FRAMEGRAPH,
RT_RESOURCE_TYPE_count,
} rt_resource_type;
@ -74,6 +76,10 @@ RT_DLLEXPORT void rtPrefetchResources(const rt_resource_id *ids, uint32_t count)
/* Returns the size of a resource in bytes, or 0 if the resource id is invalid. */
RT_DLLEXPORT size_t rtGetResourceSize(rt_resource_id id);
/* Returns the resource id that maps to a given name.
* Does not check if a resource with that id does exist. */
RT_DLLEXPORT rt_resource_id rtGetResourceID(const char *name);
/* Logs information about a resource. Useful for debugging */
RT_DLLEXPORT void rDebugLogResource(rt_resource_id id, const rt_resource *resource);

View File

@ -61,6 +61,26 @@ RT_DLLEXPORT void rtLockWrite(rt_rwlock *lock);
RT_DLLEXPORT void rtUnlockWrite(rt_rwlock *lock);
/* Semaphore */
typedef struct {
volatile int counter;
int max;
rt_condition_var *cond;
} rt_semaphore;
typedef struct {
bool ok;
rt_semaphore semaphore;
} rt_create_semaphore_result;
RT_DLLEXPORT rt_create_semaphore_result rtCreateSemaphore(int initial_value, int max_value);
RT_DLLEXPORT void rtDestroySemaphore(rt_semaphore *sem);
RT_DLLEXPORT void rtSignalSemaphore(rt_semaphore *sem);
RT_DLLEXPORT void rtWaitOnSemaphore(rt_semaphore *sem);
/* Threads */
typedef struct rt_thread_s rt_thread;

View File

@ -1,7 +1,5 @@
#include "threading.h"
#include <assert.h>
/* Based on: https://eli.thegreenplace.net/2019/implementing-reader-writer-locks/ */
RT_DLLEXPORT rt_create_rwlock_result rtCreateRWLock(void) {
@ -26,7 +24,7 @@ RT_DLLEXPORT void rtLockRead(rt_rwlock *lock) {
RT_DLLEXPORT void rtUnlockRead(rt_rwlock *lock) {
rtLockConditionVar(lock->cond);
assert(lock->reader_count > 0);
RT_ASSERT(lock->reader_count > 0, "Tried to unlock a read-write lock that is not held.");
--lock->reader_count;
bool signal = lock->reader_count == 0;
rtUnlockConditionVar(lock->cond, signal);