@@ -6,7 +6,7 @@ Add these new members to `App`:
66
77``` cpp
88auto acquire_render_target () -> bool;
9- auto wait_for_frame () -> vk::CommandBuffer;
9+ auto begin_frame () -> vk::CommandBuffer;
1010void transition_for_render (vk::CommandBuffer command_buffer) const;
1111void render(vk::CommandBuffer command_buffer);
1212void transition_for_present(vk::CommandBuffer command_buffer) const;
@@ -23,15 +23,15 @@ The main loop can now use these to implement the Swapchain and rendering loop:
2323while (glfwWindowShouldClose(m_window.get()) == GLFW_FALSE) {
2424 glfwPollEvents();
2525 if (!acquire_render_target()) { continue; }
26- auto const command_buffer = wait_for_frame ();
26+ auto const command_buffer = begin_frame ();
2727 transition_for_render(command_buffer);
2828 render(command_buffer);
2929 transition_for_present(command_buffer);
3030 submit_and_present();
3131}
3232```
3333
34- Acquire the Render Target :
34+ Before acquiring a Swapchain image, we need to wait for the current frame's fence. If acquisition is successful, reset the fence ('un'signal it) :
3535
3636``` cpp
3737auto App::acquire_render_target () -> bool {
@@ -40,36 +40,38 @@ auto App::acquire_render_target() -> bool {
4040 if (m_framebuffer_size.x <= 0 || m_framebuffer_size.y <= 0) {
4141 return false;
4242 }
43- // an eErrorOutOfDateKHR result is not guaranteed if the
44- // framebuffer size does not match the Swapchain image size, check it
45- // explicitly.
46- auto fb_size_changed = m_framebuffer_size != m_swapchain->get_size();
43+
4744 auto& render_sync = m_render_sync.at(m_frame_index);
45+
46+ // wait for the fence to be signaled.
47+ static constexpr auto fence_timeout_v =
48+ static_cast< std::uint64_t > (std::chrono::nanoseconds{3s}.count());
49+ auto result =
50+ m_device->waitForFences(* render_sync.drawn, vk::True, fence_timeout_v);
51+ if (result != vk::Result::eSuccess) {
52+ throw std::runtime_error{"Failed to wait for Render Fence"};
53+ }
54+
4855 m_render_target = m_swapchain->acquire_next_image(* render_sync.draw);
49- if (fb_size_changed || !m_render_target) {
56+ if (!m_render_target) {
57+ // acquire failure => ErrorOutOfDate. Recreate Swapchain.
5058 m_swapchain->recreate(m_framebuffer_size);
5159 return false;
5260 }
5361
62+ // reset fence _ after_ acquisition of image: if it fails, the
63+ // fence remains signaled.
64+ m_device->resetFences(* render_sync.drawn);
65+
5466 return true;
5567}
5668```
5769
58- Wait for the Fence associated with the current frame and reset ('un'signal) it, and begin Command Buffer recording:
70+ Since the fence has been reset, a queue submission must be made that signals it before continuing, otherwise the app will deadlock on the next wait ( and eventually throw after 3s). Begin Command Buffer recording:
5971
6072``` cpp
61- auto App::wait_for_frame () -> vk::CommandBuffer {
73+ auto App::begin_frame () -> vk::CommandBuffer {
6274 auto const& render_sync = m_render_sync.at(m_frame_index);
63- static constexpr auto fence_timeout_v =
64- static_cast< std::uint64_t > (std::chrono::nanoseconds{3s}.count());
65- auto result =
66- m_device->waitForFences(* render_sync.drawn, vk::True, fence_timeout_v);
67- if (result != vk::Result::eSuccess) {
68- throw std::runtime_error{"Failed to wait for Render Fence"};
69- }
70- // reset fence _ after_ acquisition of image: if it fails, the
71- // fence remains signaled.
72- m_device->resetFences(* render_sync.drawn);
7375
7476 auto command_buffer_bi = vk::CommandBufferBeginInfo{};
7577 // this flag means recorded commands will not be reused.
@@ -79,24 +81,22 @@ auto App::wait_for_frame() -> vk::CommandBuffer {
7981}
8082```
8183
82- Since the fence has been reset, a queue submission must be made that signals it before continuing, otherwise the app will deadlock on the next wait (and eventually throw after 3s).
83-
8484Transition the image for rendering, ie Attachment Optimal layout. Set up the image barrier and record it:
8585
8686``` cpp
8787void App::transition_for_render (vk::CommandBuffer const command_buffer) const {
8888 auto dependency_info = vk::DependencyInfo{};
8989 auto barrier = m_swapchain->base_barrier();
9090 // Undefined => AttachmentOptimal
91- // we don't need to block any operations before the barrier, since we
92- // rely on the image acquired semaphore to block rendering.
93- // any color attachment operations must happen after the barrier.
91+ // the barrier must wait for prior color attachment operations to complete,
92+ // and block subsequent ones.
9493 barrier.setOldLayout(vk::ImageLayout::eUndefined)
9594 .setNewLayout(vk::ImageLayout::eAttachmentOptimal)
96- .setSrcAccessMask(vk::AccessFlagBits2::eNone)
97- .setSrcStageMask(vk::PipelineStageFlagBits2::eTopOfPipe)
98- .setDstAccessMask(vk::AccessFlagBits2::eColorAttachmentWrite)
99- .setDstStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput);
95+ .setSrcAccessMask(vk::AccessFlagBits2::eColorAttachmentRead |
96+ vk::AccessFlagBits2::eColorAttachmentWrite)
97+ .setSrcStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput)
98+ .setDstAccessMask(barrier.srcAccessMask)
99+ .setDstStageMask(barrier.srcStageMask);
100100 dependency_info.setImageMemoryBarriers(barrier);
101101 command_buffer.pipelineBarrier2(dependency_info);
102102}
@@ -133,15 +133,15 @@ void App::transition_for_present(vk::CommandBuffer const command_buffer) const {
133133 auto dependency_info = vk::DependencyInfo{};
134134 auto barrier = m_swapchain->base_barrier();
135135 // AttachmentOptimal => PresentSrc
136- // the barrier must wait for color attachment operations to complete.
137- // we don't need any post-synchronization as the present Sempahore takes
138- // care of that.
136+ // the barrier must wait for prior color attachment operations to complete,
137+ // and block subsequent ones.
139138 barrier.setOldLayout(vk::ImageLayout::eAttachmentOptimal)
140139 .setNewLayout(vk::ImageLayout::ePresentSrcKHR)
141- .setSrcAccessMask(vk::AccessFlagBits2::eColorAttachmentWrite)
140+ .setSrcAccessMask(vk::AccessFlagBits2::eColorAttachmentRead |
141+ vk::AccessFlagBits2::eColorAttachmentWrite)
142142 .setSrcStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput)
143- .setDstAccessMask(vk::AccessFlagBits2::eNone )
144- .setDstStageMask(vk::PipelineStageFlagBits2::eBottomOfPipe );
143+ .setDstAccessMask(barrier.srcAccessMask )
144+ .setDstStageMask(barrier.srcStageMask );
145145 dependency_info.setImageMemoryBarriers(barrier);
146146 command_buffer.pipelineBarrier2(dependency_info);
147147}
@@ -159,7 +159,7 @@ void App::submit_and_present() {
159159 vk::CommandBufferSubmitInfo{render_sync.command_buffer};
160160 auto wait_semaphore_info = vk::SemaphoreSubmitInfo{};
161161 wait_semaphore_info.setSemaphore(*render_sync.draw)
162- .setStageMask(vk::PipelineStageFlagBits2::eTopOfPipe );
162+ .setStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput );
163163 auto signal_semaphore_info = vk::SemaphoreSubmitInfo{};
164164 signal_semaphore_info.setSemaphore(*render_sync.present)
165165 .setStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput);
@@ -171,7 +171,13 @@ void App::submit_and_present() {
171171 m_frame_index = (m_frame_index + 1) % m_render_sync.size();
172172 m_render_target.reset();
173173
174- if (!m_swapchain->present(m_queue, *render_sync.present)) {
174+ // an eErrorOutOfDateKHR result is not guaranteed if the
175+ // framebuffer size does not match the Swapchain image size, check it
176+ // explicitly.
177+ auto const fb_size_changed = m_framebuffer_size != m_swapchain->get_size();
178+ auto const out_of_date =
179+ !m_swapchain->present(m_queue, *render_sync.present);
180+ if (fb_size_changed || out_of_date) {
175181 m_swapchain->recreate(m_framebuffer_size);
176182 }
177183}
0 commit comments