Seems like a valid chain of image transitions.

Now we need to draw something and also have the correct semaphore waits
to establish dependencies.
This commit is contained in:
Kevin Trogant 2024-02-20 13:47:47 +01:00
parent 1e49b14879
commit bc6076b786
37 changed files with 966 additions and 91 deletions

View File

@ -15,8 +15,8 @@ render_targets {
depth0 {
format DEPTH24_STENCIL8;
width 512;
height 384;
width 1024;
height 768;
sample_count 4;
}
}

View File

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

View File

@ -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 <stdbool.h>
RT_CVAR_I(rt_Fullscreen, "Show window in fullscreen mode. [0/1] Default: 0", 0);

View File

@ -3,7 +3,7 @@
/* Platform specific application entry point */
#include "runtime.h"
#include "runtime/runtime.h"
#include "main_loop.h"
#ifdef __cplusplus

View File

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

View File

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

View File

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

View File

@ -0,0 +1 @@
#include "runtime/runtime.h"

View File

@ -2,7 +2,7 @@
#include "description_parser.h"
#include "runtime/buffer_manager.h"
#include "runtime/gfx.h"
#include "gfx/gfx.h"
#include <stdio.h>
#include <string.h>
@ -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,

View File

@ -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 <assert.h>
#include <limits.h>
#include <stdbool.h>

View File

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

View File

@ -1,4 +1,4 @@
#include "runtime/app.h"
#include "app_framework/app.h"
extern void RegisterCVars(void);
extern void Init(void);

View File

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

View File

@ -11,7 +11,7 @@
#include <stdint.h>
#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;

View File

@ -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 <stdbool.h>
#include <stdlib.h>
@ -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 */

View File

@ -2,12 +2,12 @@
#include <string.h>
#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;
}

18
src/gfx/meson.build Normal file
View File

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

4
src/gfx/pch/gfx_pch.h Normal file
View File

@ -0,0 +1,4 @@
#include "gfx.h"
/* Commonly used runtime headers */
#include "runtime/runtime.h"

View File

@ -6,8 +6,10 @@
#include <stddef.h>
#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

View File

@ -1,5 +1,7 @@
subdir('runtime')
subdir('asset_compiler')
subdir('gfx')
subdir('app_framework')
# Renderer libs
subdir('renderer/vk')

View File

@ -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 <stdlib.h>
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 = {
@ -315,3 +332,123 @@ 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;
}

View File

@ -1,9 +1,24 @@
#ifndef RT_COMMAND_BUFFERS_H
#define RT_COMMAND_BUFFERS_H
#include "gfx/renderer_api.h"
#include "runtime/runtime.h"
#include <volk/volk.h>
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

257
src/renderer/vk/commands.c Normal file
View File

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

View File

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

View File

@ -7,7 +7,7 @@
#define VMA_DYNAMI_VULKAN_FUNCTIONS 0
#include <vma/vk_mem_alloc.h>
#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

View File

@ -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 <stdlib.h>
RT_CVAR_I(rt_VkMaxSemaphores, "Maximum number of semaphores. Default: 1024", 1024);

View File

@ -3,7 +3,7 @@
#include <volk/volk.h>
#include "runtime/renderer_api.h"
#include "gfx/renderer_api.h"
VkSemaphore rtGetSemaphore(rt_gpu_semaphore_handle handle);

View File

@ -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 = "<UNKNOWN>";
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;
}

View File

@ -17,6 +17,7 @@ if vk_dep.found()
'swapchain.h',
'command_buffers.c',
'commands.c',
'frame.c',
'gpu_sync.c',
'helper.c',

View File

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

View File

@ -3,7 +3,7 @@
#include <volk/volk.h>
#include "runtime/renderer_api.h"
#include "gfx/renderer_api.h"
typedef struct {
VkPipeline pipeline;

View File

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

View File

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

View File

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

View File

@ -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 <assert.h>
#include <stdlib.h>
#include <string.h>

33
src/runtime/rt_math.h Normal file
View File

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