Render loop
This commit is contained in:
@@ -5,6 +5,8 @@
|
||||
#include "vulkan_profiles/vulkan_profiles.h"
|
||||
#include "ktx.h"
|
||||
#include "ktxvulkan.h"
|
||||
#include <SDL3/SDL_timer.h>
|
||||
#include <glm/ext/matrix_clip_space.hpp>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
@@ -95,6 +97,8 @@ typedef slang::CompilerOptionEntry *SlangCompOptEntryArray;
|
||||
typedef VkVertexInputAttributeDescription *VkVertexInputAttributeDescriptionArray;
|
||||
typedef VkPipelineShaderStageCreateInfo *VkPipelineShaderStageCreateInfoArray;
|
||||
typedef VkDynamicState *VkDynamicStateArray;
|
||||
typedef glm::vec3 *GlmVec3Array;
|
||||
typedef VkImageMemoryBarrier2 *VkImageMemoryBarrier2Array;
|
||||
// }}}
|
||||
|
||||
// {{{ Helper Function Declarations
|
||||
@@ -104,7 +108,10 @@ wapp_intern inline void check(bool result, i32 code);
|
||||
// }}}
|
||||
|
||||
// {{{ Global Variables
|
||||
wapp_intern u32 frame_index = 0;
|
||||
wapp_intern u32 image_index = 0;
|
||||
wapp_intern constexpr u32 max_frames_in_flight = 2;
|
||||
wapp_intern constexpr u32 instance_count = 3;
|
||||
wapp_intern constexpr u32 texture_count = 3;
|
||||
wapp_intern bool running = true;
|
||||
wapp_intern bool update_swapchain = false;
|
||||
@@ -137,7 +144,7 @@ wapp_intern VmaAllocation vert_index_buf_alloc = VK_NULL_HANDLE;
|
||||
wapp_intern ShaderData shader_data = {};
|
||||
wapp_intern ShaderDataBufferArray shader_data_bufs;
|
||||
wapp_intern VkFenceArray fences;
|
||||
wapp_intern VkSemaphoreArray image_acquired_semaphores;
|
||||
wapp_intern VkSemaphoreArray image_acquired_semaphores = nullptr;
|
||||
wapp_intern VkSemaphoreArray render_completed_semaphores = nullptr;
|
||||
wapp_intern VkCommandPool command_pool = VK_NULL_HANDLE;
|
||||
wapp_intern VkCommandBufferArray command_buffers;
|
||||
@@ -150,6 +157,8 @@ wapp_intern Slang::ComPtr<slang::IGlobalSession> slang_global_session;
|
||||
wapp_intern VkShaderModule shader_module = VK_NULL_HANDLE;
|
||||
wapp_intern VkPipelineLayout graphics_pipeline_layout = VK_NULL_HANDLE;
|
||||
wapp_intern VkPipeline graphics_pipeline = VK_NULL_HANDLE;
|
||||
wapp_intern glm::vec3 camera_position = {0.0f, 0.0f, -6.0f};
|
||||
wapp_intern GlmVec3Array object_rotations;
|
||||
// }}}
|
||||
|
||||
int main() {
|
||||
@@ -528,7 +537,7 @@ int main() {
|
||||
|
||||
// {{{ Upload to GPU
|
||||
VkDeviceSize vertex_buf_size = sizeof(Vertex) * wapp_array_count(vertices);
|
||||
VkDeviceSize index_buf_size = sizeof(u16) * wapp_array_count(vertices);
|
||||
VkDeviceSize index_buf_size = sizeof(u16) * wapp_array_count(indices);
|
||||
|
||||
VkBufferCreateInfo buf_create_info = {};
|
||||
buf_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
@@ -1104,8 +1113,229 @@ int main() {
|
||||
// }}}
|
||||
|
||||
// {{{ Render Loop
|
||||
/**
|
||||
* Render loop overview:
|
||||
* - Wait on fence
|
||||
* - Acquire next image
|
||||
* - Update shader data
|
||||
* - Record command buffer
|
||||
* - Submit command buffer
|
||||
* - Present image
|
||||
* - Poll events
|
||||
*/
|
||||
|
||||
object_rotations = wapp_array_with_capacity(glm::vec3, 3, ARRAY_INIT_FILLED);
|
||||
u64 last_time = SDL_GetTicks();
|
||||
SDL_Event event = {};
|
||||
|
||||
while (running) {
|
||||
// {{{ Wait On Fence
|
||||
check(vkWaitForFences(device, 1, &fences[frame_index], VK_TRUE, UINT64_MAX));
|
||||
check(vkResetFences(device, 1, &fences[frame_index]));
|
||||
// }}}
|
||||
|
||||
// {{{ Acquire Next Swapchain Image
|
||||
check_swapchain(vkAcquireNextImageKHR(device, swapchain, UINT64_MAX,
|
||||
image_acquired_semaphores[frame_index], VK_NULL_HANDLE,
|
||||
&image_index));
|
||||
// }}}
|
||||
|
||||
// {{{ Update Shader Data
|
||||
shader_data.projection = glm::perspective(glm::radians(45.0f), (f32)window_size.x / (f32)window_size.y,
|
||||
0.1f, 32.0f);
|
||||
shader_data.view = glm::translate(glm::mat4(1.0f), camera_position);
|
||||
for (u32 i = 0; i < instance_count; ++i) {
|
||||
glm::vec3 instance_pos = glm::vec3((f32)(i - 1) * 3.0f, 0.0f, 0.0f);
|
||||
shader_data.model[i] = glm::translate(glm::mat4(1.0f), instance_pos) *
|
||||
glm::mat4_cast(glm::quat(object_rotations[i]));
|
||||
}
|
||||
|
||||
memcpy(shader_data_bufs[frame_index].allocation_info.pMappedData, &shader_data, sizeof(ShaderData));
|
||||
// }}}
|
||||
|
||||
// {{{ Record Command Buffer
|
||||
VkCommandBuffer cb = command_buffers[frame_index];
|
||||
|
||||
check(vkResetCommandBuffer(cb, 0));
|
||||
|
||||
VkCommandBufferBeginInfo cmd_begin_info = {};
|
||||
cmd_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
cmd_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||
check(vkBeginCommandBuffer(cb, &cmd_begin_info));
|
||||
|
||||
VkImageMemoryBarrier2Array memory_barriers = wapp_array_with_capacity(VkImageMemoryBarrier2, 2, ARRAY_INIT_FILLED);
|
||||
|
||||
// Color Attachment
|
||||
memory_barriers[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
|
||||
memory_barriers[0].srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
memory_barriers[0].srcAccessMask = 0;
|
||||
memory_barriers[0].dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
memory_barriers[0].dstAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
memory_barriers[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
memory_barriers[0].newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL;
|
||||
memory_barriers[0].image = swapchain_images[image_index];
|
||||
memory_barriers[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
memory_barriers[0].subresourceRange.levelCount = 1;
|
||||
memory_barriers[0].subresourceRange.layerCount = 1;
|
||||
|
||||
// Depth Attachment
|
||||
memory_barriers[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
|
||||
memory_barriers[0].srcStageMask = VK_PIPELINE_STAGE_2_LATE_FRAGMENT_TESTS_BIT;
|
||||
memory_barriers[0].srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
memory_barriers[0].dstStageMask = VK_PIPELINE_STAGE_2_EARLY_FRAGMENT_TESTS_BIT;
|
||||
memory_barriers[0].dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
memory_barriers[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
memory_barriers[0].newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL;
|
||||
memory_barriers[0].image = depth_image;
|
||||
memory_barriers[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
|
||||
memory_barriers[0].subresourceRange.levelCount = 1;
|
||||
memory_barriers[0].subresourceRange.layerCount = 1;
|
||||
|
||||
// {{{ Transition Layout Color And Depth Images
|
||||
VkDependencyInfo barrier_dependency_info = {};
|
||||
barrier_dependency_info.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
|
||||
barrier_dependency_info.imageMemoryBarrierCount = wapp_array_count(memory_barriers);
|
||||
barrier_dependency_info.pImageMemoryBarriers = memory_barriers;
|
||||
|
||||
vkCmdPipelineBarrier2(cb, &barrier_dependency_info);
|
||||
// }}}
|
||||
|
||||
// {{{ Dynamic Rendering Commands
|
||||
VkClearValue color_clear_value = {};
|
||||
color_clear_value.color = { 0.0f, 0.0f, 0.2f, 1.0f };
|
||||
VkClearValue depth_clear_value = {};
|
||||
depth_clear_value.depthStencil = { 1.0f, 0 };
|
||||
|
||||
VkRenderingAttachmentInfo color_attachment = {};
|
||||
color_attachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
|
||||
color_attachment.imageView = swapchain_views[image_index];
|
||||
color_attachment.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL;
|
||||
color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
color_attachment.clearValue = color_clear_value;
|
||||
|
||||
VkRenderingAttachmentInfo depth_attachment = {};
|
||||
depth_attachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
|
||||
depth_attachment.imageView = depth_view;
|
||||
depth_attachment.imageLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL;
|
||||
depth_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
depth_attachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
depth_attachment.clearValue = depth_clear_value;
|
||||
|
||||
VkRect2D render_area = {};
|
||||
render_area.extent = { (u32)window_size.x, (u32)window_size.y };
|
||||
|
||||
VkRenderingInfo render_info = {};
|
||||
render_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
|
||||
render_info.renderArea = render_area;
|
||||
render_info.layerCount = 1;
|
||||
render_info.colorAttachmentCount = 1;
|
||||
render_info.pColorAttachments = &color_attachment;
|
||||
render_info.pDepthAttachment = &depth_attachment;
|
||||
vkCmdBeginRendering(cb, &render_info);
|
||||
|
||||
// {{{ Set Viewport And Scissor Dynamic States
|
||||
VkViewport viewport = {};
|
||||
viewport.width = window_size.x;
|
||||
viewport.height = window_size.y;
|
||||
viewport.minDepth = 0.0f;
|
||||
viewport.maxDepth = 1.0f;
|
||||
vkCmdSetViewport(cb, 0, 1, &viewport);
|
||||
|
||||
VkRect2D scissor = {};
|
||||
scissor.extent = render_area.extent;
|
||||
vkCmdSetScissor(cb, 0, 1, &scissor);
|
||||
// }}}
|
||||
|
||||
// {{{ Bind Resources
|
||||
vkCmdBindPipeline(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline);
|
||||
vkCmdBindDescriptorSets(cb, VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0, 1, &desc_set_tex, 0, NULL);
|
||||
|
||||
VkDeviceSize vertex_offset = {};
|
||||
vkCmdBindVertexBuffers(cb, 0, 1, &vert_index_buf, &vertex_offset);
|
||||
vkCmdBindIndexBuffer(cb, vert_index_buf, vertex_buf_size, VK_INDEX_TYPE_UINT16);
|
||||
// }}}
|
||||
|
||||
// {{{ Send ShaderData Buffer Device Address To Shader
|
||||
vkCmdPushConstants(cb, graphics_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, 0,
|
||||
sizeof(VkDeviceAddress), &shader_data_bufs[frame_index].device_address);
|
||||
// }}}
|
||||
|
||||
// {{{ Draw Commands
|
||||
vkCmdDrawIndexed(cb, index_count, instance_count, 0, 0, 0);
|
||||
// }}}
|
||||
|
||||
vkCmdEndRendering(cb);
|
||||
// }}}
|
||||
|
||||
// {{{ Transition Color Attachment For Presentation
|
||||
VkImageMemoryBarrier2 present_barrier = {};
|
||||
present_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2;
|
||||
present_barrier.srcStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
present_barrier.srcAccessMask = VK_ACCESS_2_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
present_barrier.dstStageMask = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
present_barrier.dstAccessMask = 0;
|
||||
present_barrier.oldLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL;
|
||||
present_barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
present_barrier.image = swapchain_images[image_index];
|
||||
present_barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
present_barrier.subresourceRange.levelCount = 1;
|
||||
present_barrier.subresourceRange.layerCount = 1;
|
||||
|
||||
VkDependencyInfo present_dependency_info = {};
|
||||
present_dependency_info.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO;
|
||||
present_dependency_info.imageMemoryBarrierCount = 1;
|
||||
present_dependency_info.pImageMemoryBarriers = &present_barrier;
|
||||
vkCmdPipelineBarrier2(cb, &present_dependency_info);
|
||||
// }}}
|
||||
|
||||
check(vkEndCommandBuffer(cb));
|
||||
// }}}
|
||||
|
||||
// {{{ Submit Command Buffer
|
||||
VkPipelineStageFlags wait_stages = VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
|
||||
// The wait semaphore in pWaitSemaphores ensures that execution of the command buffer does not
|
||||
// begin until the swapchain image we want to render to has been acquired. This means
|
||||
// presentation has finished and the image has been released by the presentation engine. This is
|
||||
// required because swapchain images are owned by the presentation engine rather than by our
|
||||
// application. The pipeline stage specified in pWaitDstStageMask makes that wait happen at the
|
||||
// color attachment output stage, so in theory the GPU may already begin work on earlier
|
||||
// pipeline stages, such as vertex fetching. The signal semaphore in pSignalSemaphores, on the
|
||||
// other hand, is signaled by the GPU once command buffer execution has completed and ensures
|
||||
// that presentation does not begin until the command buffer has finished execution. Together,
|
||||
// these guarantees prevent read/write hazards that could cause the GPU to read from or write
|
||||
// to resources that are still in use.
|
||||
VkSubmitInfo submit_info = {};
|
||||
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
submit_info.waitSemaphoreCount = 1;
|
||||
submit_info.pWaitSemaphores = &image_acquired_semaphores[frame_index];
|
||||
submit_info.pWaitDstStageMask = &wait_stages;
|
||||
submit_info.commandBufferCount = 1;
|
||||
submit_info.pCommandBuffers = &cb;
|
||||
submit_info.signalSemaphoreCount = 1;
|
||||
submit_info.pSignalSemaphores = &render_completed_semaphores[image_index];
|
||||
|
||||
check(vkQueueSubmit(queue, 1, &submit_info, fences[frame_index]));
|
||||
// }}}
|
||||
|
||||
frame_index = (frame_index + 1) % max_frames_in_flight;
|
||||
|
||||
// {{{ Present Image
|
||||
VkPresentInfoKHR present_info = {};
|
||||
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
||||
present_info.waitSemaphoreCount = 1;
|
||||
present_info.pWaitSemaphores = &render_completed_semaphores[image_index];
|
||||
present_info.swapchainCount = 1;
|
||||
present_info.pSwapchains = &swapchain;
|
||||
present_info.pImageIndices = &image_index;
|
||||
|
||||
check_swapchain(vkQueuePresentKHR(queue, &present_info));
|
||||
// }}}
|
||||
|
||||
// {{{ Poll Events
|
||||
f32 elapsed_time = (SDL_GetTicks() - last_time) / 1000.0f;
|
||||
last_time = SDL_GetTicks();
|
||||
while (SDL_PollEvent(&event)) {
|
||||
switch (event.type) {
|
||||
case SDL_EVENT_QUIT:
|
||||
@@ -1114,10 +1344,34 @@ int main() {
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
if (event.key.key == SDLK_ESCAPE) {
|
||||
running = false;
|
||||
break;
|
||||
}
|
||||
// Select active model instance
|
||||
if (event.key.key == SDLK_PLUS || event.key.key == SDLK_KP_PLUS) {
|
||||
shader_data.selected = (shader_data.selected < 2) ? shader_data.selected + 1 : 0;
|
||||
}
|
||||
if (event.key.key == SDLK_MINUS || event.key.key == SDLK_KP_MINUS) {
|
||||
shader_data.selected = (shader_data.selected > 0) ? shader_data.selected - 1 : 2;
|
||||
}
|
||||
break;
|
||||
// Rotate the selected object with mouse drag
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
if (event.button.button == SDL_BUTTON_LEFT) {
|
||||
object_rotations[shader_data.selected].x -= (float)event.motion.yrel * elapsed_time;
|
||||
object_rotations[shader_data.selected].y += (float)event.motion.xrel * elapsed_time;
|
||||
}
|
||||
break;
|
||||
// Zooming with the mouse wheel
|
||||
case SDL_EVENT_MOUSE_WHEEL:
|
||||
camera_position.z += (float)event.wheel.y * elapsed_time * 10.0f;
|
||||
break;
|
||||
// Window resize
|
||||
case SDL_EVENT_WINDOW_RESIZED:
|
||||
update_swapchain = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
}
|
||||
// }}}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user