Skip to content

Commit

Permalink
059 Render Targets and Configurable Renderpasses (travisvroman#61)
Browse files Browse the repository at this point in the history
  • Loading branch information
travisvroman authored May 31, 2022
1 parent 5f910b6 commit 4ba9e70
Show file tree
Hide file tree
Showing 13 changed files with 745 additions and 598 deletions.
10 changes: 10 additions & 0 deletions engine/src/renderer/renderer_backend.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ b8 renderer_backend_create(renderer_backend_type type, renderer_backend* out_ren
out_renderer_backend->texture_map_acquire_resources = vulkan_renderer_texture_map_acquire_resources;
out_renderer_backend->texture_map_release_resources = vulkan_renderer_texture_map_release_resources;

out_renderer_backend->render_target_create = vulkan_renderer_render_target_create;
out_renderer_backend->render_target_destroy = vulkan_renderer_render_target_destroy;

out_renderer_backend->renderpass_create = vulkan_renderpass_create;
out_renderer_backend->renderpass_destroy = vulkan_renderpass_destroy;
out_renderer_backend->renderpass_get = vulkan_renderer_renderpass_get;
out_renderer_backend->window_attachment_get = vulkan_renderer_window_attachment_get;
out_renderer_backend->depth_attachment_get = vulkan_renderer_depth_attachment_get;
out_renderer_backend->window_attachment_index_get = vulkan_renderer_window_attachment_index_get;

return true;
}

Expand Down
200 changes: 175 additions & 25 deletions engine/src/renderer/renderer_frontend.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,28 @@ typedef struct renderer_system_state {
u32 material_shader_id;
u32 ui_shader_id;
u32 render_mode;
// The number of render targets. Typically lines up with the amount of swapchain images.
u8 window_render_target_count;
// The current window framebuffer width.
u32 framebuffer_width;
// The current window framebuffer height.
u32 framebuffer_height;

// A pointer to the world renderpass. TODO: Configurable via views.
renderpass* world_renderpass;
// A pointer to the UI renderpass. TODO: Configurable via views.
renderpass* ui_renderpass;
// Indicates if the window is currently being resized.
b8 resizing;
// The current number of frames since the last resize operation.'
// Only set if resizing = true. Otherwise 0.
u8 frames_since_resize;
} renderer_system_state;

static renderer_system_state* state_ptr;

void regenerate_render_targets();

b8 renderer_on_event(u16 code, void* sender, void* listener_inst, event_context context) {
switch (code) {
case EVENT_CODE_SET_RENDER_MODE: {
Expand Down Expand Up @@ -75,15 +93,69 @@ b8 renderer_system_initialize(u64* memory_requirement, void* state, const char*
}
state_ptr = state;

// Default framebuffer size. Overridden when window is created.
state_ptr->framebuffer_width = 1280;
state_ptr->framebuffer_height = 720;
state_ptr->resizing = false;
state_ptr->frames_since_resize = 0;

// TODO: make this configurable.
renderer_backend_create(RENDERER_BACKEND_TYPE_VULKAN, &state_ptr->backend);
state_ptr->backend.frame_number = 0;
state_ptr->render_mode = RENDERER_VIEW_MODE_DEFAULT;

event_register(EVENT_CODE_SET_RENDER_MODE, state, renderer_on_event);

renderer_backend_config renderer_config = {};
renderer_config.application_name = application_name;
renderer_config.on_rendertarget_refresh_required = regenerate_render_targets;

// Renderpasses. TODO: read config from file.
renderer_config.renderpass_count = 2;
const char* world_renderpass_name = "Renderpass.Builtin.World";
const char* ui_renderpass_name = "Renderpass.Builtin.UI";
renderpass_config pass_configs[2];
pass_configs[0].name = world_renderpass_name;
pass_configs[0].prev_name = 0;
pass_configs[0].next_name = ui_renderpass_name;
pass_configs[0].render_area = (vec4){0, 0, 1280, 720};
pass_configs[0].clear_colour = (vec4){0.0f, 0.0f, 0.2f, 1.0f};
pass_configs[0].clear_flags = RENDERPASS_CLEAR_COLOUR_BUFFER_FLAG | RENDERPASS_CLEAR_DEPTH_BUFFER_FLAG | RENDERPASS_CLEAR_STENCIL_BUFFER_FLAG;

pass_configs[1].name = ui_renderpass_name;
pass_configs[1].prev_name = world_renderpass_name;
pass_configs[1].next_name = 0;
pass_configs[1].render_area = (vec4){0, 0, 1280, 720};
pass_configs[1].clear_colour = (vec4){0.0f, 0.0f, 0.2f, 1.0f};
pass_configs[1].clear_flags = RENDERPASS_CLEAR_NONE_FLAG;

renderer_config.pass_configs = pass_configs;

// Initialize the backend.
CRITICAL_INIT(state_ptr->backend.initialize(&state_ptr->backend, application_name), "Renderer backend failed to initialize. Shutting down.");
CRITICAL_INIT(state_ptr->backend.initialize(&state_ptr->backend, &renderer_config, &state_ptr->window_render_target_count), "Renderer backend failed to initialize. Shutting down.");

// TODO: Will know how to get these when we define views.
state_ptr->world_renderpass = state_ptr->backend.renderpass_get(world_renderpass_name);
state_ptr->world_renderpass->render_target_count = state_ptr->window_render_target_count;
state_ptr->world_renderpass->targets = kallocate(sizeof(render_target) * state_ptr->window_render_target_count, MEMORY_TAG_ARRAY);

state_ptr->ui_renderpass = state_ptr->backend.renderpass_get(ui_renderpass_name);
state_ptr->ui_renderpass->render_target_count = state_ptr->window_render_target_count;
state_ptr->ui_renderpass->targets = kallocate(sizeof(render_target) * state_ptr->window_render_target_count, MEMORY_TAG_ARRAY);

regenerate_render_targets();

// Update the main/world renderpass dimensions.
state_ptr->world_renderpass->render_area.x = 0;
state_ptr->world_renderpass->render_area.y = 0;
state_ptr->world_renderpass->render_area.z = state_ptr->framebuffer_width;
state_ptr->world_renderpass->render_area.w = state_ptr->framebuffer_height;

// Also update the UI renderpass dimensions.
state_ptr->ui_renderpass->render_area.x = 0;
state_ptr->ui_renderpass->render_area.y = 0;
state_ptr->ui_renderpass->render_area.z = state_ptr->framebuffer_width;
state_ptr->ui_renderpass->render_area.w = state_ptr->framebuffer_height;

// Shaders
resource config_resource;
Expand Down Expand Up @@ -126,16 +198,25 @@ b8 renderer_system_initialize(u64* memory_requirement, void* state, const char*

void renderer_system_shutdown(void* state) {
if (state_ptr) {
// Destroy render targets.
for (u8 i = 0; i < state_ptr->window_render_target_count; ++i) {
state_ptr->backend.render_target_destroy(&state_ptr->world_renderpass->targets[i], true);
state_ptr->backend.render_target_destroy(&state_ptr->ui_renderpass->targets[i], true);
}

state_ptr->backend.shutdown(&state_ptr->backend);
}
state_ptr = 0;
}

void renderer_on_resized(u16 width, u16 height) {
if (state_ptr) {
state_ptr->projection = mat4_perspective(deg_to_rad(45.0f), width / (f32)height, state_ptr->near_clip, state_ptr->far_clip);
state_ptr->ui_projection = mat4_orthographic(0, (f32)width, (f32)height, 0, -100.f, 100.0f); // Intentionally flipped on y axis.
state_ptr->backend.resized(&state_ptr->backend, width, height);
// Flag as resizing and store the change, but wait to regenerate.
state_ptr->resizing = true;
state_ptr->framebuffer_width = width;
state_ptr->framebuffer_height = height;
// Also reset the frame count since the last resize operation.
state_ptr->frames_since_resize = 0;
} else {
KWARN("renderer backend does not exist to accept resize: %i %i", width, height);
}
Expand All @@ -144,10 +225,42 @@ void renderer_on_resized(u16 width, u16 height) {
b8 renderer_draw_frame(render_packet* packet) {
state_ptr->backend.frame_number++;

// Make sure the window is not currently being resized by waiting a designated
// number of frames after the last resize operation before performing the backend updates.
if (state_ptr->resizing) {
state_ptr->frames_since_resize++;

// If the required number of frames have passed since the resize, go ahead and perform the actual updates.
if (state_ptr->frames_since_resize >= 30) {
f32 width = state_ptr->framebuffer_width;
f32 height = state_ptr->framebuffer_height;
state_ptr->projection = mat4_perspective(deg_to_rad(45.0f), width / (f32)height, state_ptr->near_clip, state_ptr->far_clip);
state_ptr->ui_projection = mat4_orthographic(0, (f32)width, (f32)height, 0, -100.f, 100.0f); // Intentionally flipped on y axis.
state_ptr->backend.resized(&state_ptr->backend, width, height);

state_ptr->frames_since_resize = 0;
state_ptr->resizing = false;
} else {
// Skip rendering the frame and try again next time.
return true;
}
}

// TODO: views
// Update the main/world renderpass dimensions.
state_ptr->world_renderpass->render_area.z = state_ptr->framebuffer_width;
state_ptr->world_renderpass->render_area.w = state_ptr->framebuffer_height;

// Also update the UI renderpass dimensions.
state_ptr->ui_renderpass->render_area.z = state_ptr->framebuffer_width;
state_ptr->ui_renderpass->render_area.w = state_ptr->framebuffer_height;

// If the begin frame returned successfully, mid-frame operations may continue.
if (state_ptr->backend.begin_frame(&state_ptr->backend, packet->delta_time)) {
u8 attachment_index = state_ptr->backend.window_attachment_index_get();

// World renderpass
if (!state_ptr->backend.begin_renderpass(&state_ptr->backend, BUILTIN_RENDERPASS_WORLD)) {
if (!state_ptr->backend.begin_renderpass(&state_ptr->backend, state_ptr->world_renderpass, &state_ptr->world_renderpass->targets[attachment_index])) {
KERROR("backend.begin_renderpass -> BUILTIN_RENDERPASS_WORLD failed. Application shutting down...");
return false;
}
Expand Down Expand Up @@ -191,14 +304,14 @@ b8 renderer_draw_frame(render_packet* packet) {
state_ptr->backend.draw_geometry(packet->geometries[i]);
}

if (!state_ptr->backend.end_renderpass(&state_ptr->backend, BUILTIN_RENDERPASS_WORLD)) {
if (!state_ptr->backend.end_renderpass(&state_ptr->backend, state_ptr->world_renderpass)) {
KERROR("backend.end_renderpass -> BUILTIN_RENDERPASS_WORLD failed. Application shutting down...");
return false;
}
// End world renderpass

// UI renderpass
if (!state_ptr->backend.begin_renderpass(&state_ptr->backend, BUILTIN_RENDERPASS_UI)) {
if (!state_ptr->backend.begin_renderpass(&state_ptr->backend, state_ptr->ui_renderpass, &state_ptr->ui_renderpass->targets[attachment_index])) {
KERROR("backend.begin_renderpass -> BUILTIN_RENDERPASS_UI failed. Application shutting down...");
return false;
}
Expand Down Expand Up @@ -241,7 +354,7 @@ b8 renderer_draw_frame(render_packet* packet) {
state_ptr->backend.draw_geometry(packet->ui_geometries[i]);
}

if (!state_ptr->backend.end_renderpass(&state_ptr->backend, BUILTIN_RENDERPASS_UI)) {
if (!state_ptr->backend.end_renderpass(&state_ptr->backend, state_ptr->ui_renderpass)) {
KERROR("backend.end_renderpass -> BUILTIN_RENDERPASS_UI failed. Application shutting down...");
return false;
}
Expand Down Expand Up @@ -284,7 +397,6 @@ void renderer_texture_resize(texture* t, u32 new_width, u32 new_height) {
state_ptr->backend.texture_resize(t, new_width, new_height);
}


b8 renderer_create_geometry(geometry* geometry, u32 vertex_size, u32 vertex_count, const void* vertices, u32 index_size, u32 index_count, const void* indices) {
return state_ptr->backend.create_geometry(geometry, vertex_size, vertex_count, vertices, index_size, index_count, indices);
}
Expand All @@ -293,23 +405,12 @@ void renderer_destroy_geometry(geometry* geometry) {
state_ptr->backend.destroy_geometry(geometry);
}

b8 renderer_renderpass_id(const char* name, u8* out_renderpass_id) {
// TODO: HACK: Need dynamic renderpasses instead of hardcoding them.
if (strings_equali("Renderpass.Builtin.World", name)) {
*out_renderpass_id = BUILTIN_RENDERPASS_WORLD;
return true;
} else if (strings_equali("Renderpass.Builtin.UI", name)) {
*out_renderpass_id = BUILTIN_RENDERPASS_UI;
return true;
}

KERROR("renderer_renderpass_id: No renderpass named '%s'.", name);
*out_renderpass_id = INVALID_ID_U8;
return false;
renderpass* renderer_renderpass_get(const char* name) {
return state_ptr->backend.renderpass_get(name);
}

b8 renderer_shader_create(shader* s, u8 renderpass_id, u8 stage_count, const char** stage_filenames, shader_stage* stages) {
return state_ptr->backend.shader_create(s, renderpass_id, stage_count, stage_filenames, stages);
b8 renderer_shader_create(shader* s, renderpass* pass, u8 stage_count, const char** stage_filenames, shader_stage* stages) {
return state_ptr->backend.shader_create(s, pass, stage_count, stage_filenames, stages);
}

void renderer_shader_destroy(shader* s) {
Expand Down Expand Up @@ -358,4 +459,53 @@ b8 renderer_texture_map_acquire_resources(struct texture_map* map) {

void renderer_texture_map_release_resources(struct texture_map* map) {
state_ptr->backend.texture_map_release_resources(map);
}
}

void renderer_render_target_create(u8 attachment_count, texture** attachments, renderpass* pass, u32 width, u32 height, render_target* out_target) {
state_ptr->backend.render_target_create(attachment_count, attachments, pass, width, height, out_target);
}

void renderer_render_target_destroy(render_target* target, b8 free_internal_memory) {
state_ptr->backend.render_target_destroy(target, free_internal_memory);
}

void renderer_renderpass_create(renderpass* out_renderpass, f32 depth, u32 stencil, b8 has_prev_pass, b8 has_next_pass) {
state_ptr->backend.renderpass_create(out_renderpass, depth, stencil, has_prev_pass, has_next_pass);
}

void renderer_renderpass_destroy(renderpass* pass) {
state_ptr->backend.renderpass_destroy(pass);
}

void regenerate_render_targets() {
// Create render targets for each. TODO: Should be configurable.
for (u8 i = 0; i < state_ptr->window_render_target_count; ++i) {
// Destroy the old first if they exist.
state_ptr->backend.render_target_destroy(&state_ptr->world_renderpass->targets[i], false);
state_ptr->backend.render_target_destroy(&state_ptr->ui_renderpass->targets[i], false);

texture* window_target_texture = state_ptr->backend.window_attachment_get(i);
texture* depth_target_texture = state_ptr->backend.depth_attachment_get();

// World render targets.
texture* attachments[2] = {window_target_texture, depth_target_texture};
state_ptr->backend.render_target_create(
2,
attachments,
state_ptr->world_renderpass,
state_ptr->framebuffer_width,
state_ptr->framebuffer_height,
&state_ptr->world_renderpass->targets[i]);

// UI render targets
texture* ui_attachments[1] = {window_target_texture};
state_ptr->backend.render_target_create(
1,
ui_attachments,
state_ptr->ui_renderpass,
state_ptr->framebuffer_width,
state_ptr->framebuffer_height,
&state_ptr->ui_renderpass->targets[i]);

}
}
50 changes: 44 additions & 6 deletions engine/src/renderer/renderer_frontend.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,25 +127,24 @@ b8 renderer_create_geometry(geometry* geometry, u32 vertex_size, u32 vertex_coun
void renderer_destroy_geometry(geometry* geometry);

/**
* @brief Obtains the identifier of the renderpass with the given name.
* @brief Obtains a pointer to the renderpass with the given name.
*
* @param name The name of the renderpass whose identifier to obtain.
* @param out_renderpass_id A pointer to hold the renderpass id.
* @return True if found; otherwise false.
* @return A pointer to a renderpass if found; otherwise 0.
*/
b8 renderer_renderpass_id(const char* name, u8* out_renderpass_id);
renderpass* renderer_renderpass_get(const char* name);

/**
* @brief Creates internal shader resources using the provided parameters.
*
* @param s A pointer to the shader.
* @param renderpass_id The identifier of the renderpass to be associated with the shader.
* @param pass A pointer to the renderpass to be associated with the shader.
* @param stage_count The total number of stages.
* @param stage_filenames An array of shader stage filenames to be loaded. Should align with stages array.
* @param stages A array of shader_stages indicating what render stages (vertex, fragment, etc.) used in this shader.
* @return b8 True on success; otherwise false.
*/
b8 renderer_shader_create(struct shader* s, u8 renderpass_id, u8 stage_count, const char** stage_filenames, shader_stage* stages);
b8 renderer_shader_create(struct shader* s, renderpass* pass, u8 stage_count, const char** stage_filenames, shader_stage* stages);

/**
* @brief Destroys the given shader and releases any resources held by it.
Expand Down Expand Up @@ -248,3 +247,42 @@ b8 renderer_texture_map_acquire_resources(struct texture_map* map);
* @param map A pointer to the texture map to release resources from.
*/
void renderer_texture_map_release_resources(struct texture_map* map);

/**
* @brief Creates a new render target using the provided data.
*
* @param attachment_count The number of attachments (texture pointers).
* @param attachments An array of attachments (texture pointers).
* @param renderpass A pointer to the renderpass the render target is associated with.
* @param width The width of the render target in pixels.
* @param height The height of the render target in pixels.
* @param out_target A pointer to hold the newly created render target.
*/
void renderer_render_target_create(u8 attachment_count, texture** attachments, renderpass* pass, u32 width, u32 height, render_target* out_target);

/**
* @brief Destroys the provided render target.
*
* @param target A pointer to the render target to be destroyed.
* @param free_internal_memory Indicates if internal memory should be freed.
*/
void renderer_render_target_destroy(render_target* target, b8 free_internal_memory);

/**
* @brief Creates a new renderpass.
*
* @param out_renderpass A pointer to the generic renderpass.
* @param depth The depth clear amount.
* @param stencil The stencil clear value.
* @param clear_flags The combined clear flags indicating what kind of clear should take place.
* @param has_prev_pass Indicates if there is a previous renderpass.
* @param has_next_pass Indicates if there is a next renderpass.
*/
void renderer_renderpass_create(renderpass* out_renderpass, f32 depth, u32 stencil, b8 has_prev_pass, b8 has_next_pass);

/**
* @brief Destroys the given renderpass.
*
* @param pass A pointer to the renderpass to be destroyed.
*/
void renderer_renderpass_destroy(renderpass* pass);
Loading

0 comments on commit 4ba9e70

Please sign in to comment.