|
1 | 1 | # Render Sync |
| 2 | + |
| 3 | +Create a new header `resource_buffering.hpp`: |
| 4 | + |
| 5 | +```cpp |
| 6 | +// Number of virtual frames. |
| 7 | +inline constexpr std::size_t buffering_v{2}; |
| 8 | + |
| 9 | +// Alias for N-buffered resources. |
| 10 | +template <typename Type> |
| 11 | +using Buffered = std::array<Type, buffering_v>; |
| 12 | +``` |
| 13 | +
|
| 14 | +Add a private `struct RenderSync` to `App`: |
| 15 | +
|
| 16 | +```cpp |
| 17 | + struct RenderSync { |
| 18 | + // signaled when Swapchain image has been acquired. |
| 19 | + vk::UniqueSemaphore draw{}; |
| 20 | + // signaled when image is ready to be presented. |
| 21 | + vk::UniqueSemaphore present{}; |
| 22 | + // signaled with present Semaphore, waited on before next render. |
| 23 | + vk::UniqueFence drawn{}; |
| 24 | + // used to record rendering commands. |
| 25 | + vk::CommandBuffer command_buffer{}; |
| 26 | + }; |
| 27 | +``` |
| 28 | + |
| 29 | +Add the new members associated with the Swapchain loop: |
| 30 | + |
| 31 | +```cpp |
| 32 | + // command pool for all render Command Buffers. |
| 33 | + vk::UniqueCommandPool m_render_cmd_pool{}; |
| 34 | + // Sync and Command Buffer for virtual frames. |
| 35 | + Buffered<RenderSync> m_render_sync{}; |
| 36 | + // Current virtual frame index. |
| 37 | + std::size_t m_frame_index{}; |
| 38 | +``` |
| 39 | + |
| 40 | +Add, implement, and call the create function: |
| 41 | + |
| 42 | +```cpp |
| 43 | +void App::create_render_sync() { |
| 44 | + // Command Buffers are 'allocated' from a Command Pool (which is 'created' |
| 45 | + // like all other Vulkan objects so far). We can allocate all the buffers |
| 46 | + // from a single pool here. |
| 47 | + auto command_pool_ci = vk::CommandPoolCreateInfo{}; |
| 48 | + // this flag enables resetting the command buffer for re-recording (unlike a |
| 49 | + // single-time submit scenario). |
| 50 | + command_pool_ci.setFlags(vk::CommandPoolCreateFlagBits::eResetCommandBuffer) |
| 51 | + .setQueueFamilyIndex(m_gpu.queue_family); |
| 52 | + m_render_cmd_pool = m_device->createCommandPoolUnique(command_pool_ci); |
| 53 | + |
| 54 | + auto command_buffer_ai = vk::CommandBufferAllocateInfo{}; |
| 55 | + command_buffer_ai.setCommandPool(*m_render_cmd_pool) |
| 56 | + .setCommandBufferCount(static_cast<std::uint32_t>(resource_buffering_v)) |
| 57 | + .setLevel(vk::CommandBufferLevel::ePrimary); |
| 58 | + auto const command_buffers = |
| 59 | + m_device->allocateCommandBuffers(command_buffer_ai); |
| 60 | + assert(command_buffers.size() == m_render_sync.size()); |
| 61 | + |
| 62 | + // we create Render Fences as pre-signaled so that on the first render for |
| 63 | + // each virtual frame we don't wait on their fences (since there's nothing |
| 64 | + // to wait for yet). |
| 65 | + static constexpr auto fence_create_info_v = |
| 66 | + vk::FenceCreateInfo{vk::FenceCreateFlagBits::eSignaled}; |
| 67 | + for (auto [sync, command_buffer] : |
| 68 | + std::views::zip(m_render_sync, command_buffers)) { |
| 69 | + sync.command_buffer = command_buffer; |
| 70 | + sync.draw = m_device->createSemaphoreUnique({}); |
| 71 | + sync.present = m_device->createSemaphoreUnique({}); |
| 72 | + sync.drawn = m_device->createFenceUnique(fence_create_info_v); |
| 73 | + } |
| 74 | +} |
| 75 | +``` |
0 commit comments