Skip to content

Commit fbb84f0

Browse files
authored
Sync fixes (#11)
1 parent 75b219c commit fbb84f0

File tree

3 files changed

+82
-68
lines changed

3 files changed

+82
-68
lines changed

guide/src/rendering/dynamic_rendering.md

Lines changed: 43 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Add these new members to `App`:
66

77
```cpp
88
auto acquire_render_target() -> bool;
9-
auto wait_for_frame() -> vk::CommandBuffer;
9+
auto begin_frame() -> vk::CommandBuffer;
1010
void transition_for_render(vk::CommandBuffer command_buffer) const;
1111
void render(vk::CommandBuffer command_buffer);
1212
void 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:
2323
while (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
3737
auto 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-
8484
Transition the image for rendering, ie Attachment Optimal layout. Set up the image barrier and record it:
8585

8686
```cpp
8787
void 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
}

src/app.cpp

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ void App::main_loop() {
141141
while (glfwWindowShouldClose(m_window.get()) == GLFW_FALSE) {
142142
glfwPollEvents();
143143
if (!acquire_render_target()) { continue; }
144-
auto const command_buffer = wait_for_frame();
144+
auto const command_buffer = begin_frame();
145145
transition_for_render(command_buffer);
146146
render(command_buffer);
147147
transition_for_present(command_buffer);
@@ -155,33 +155,35 @@ auto App::acquire_render_target() -> bool {
155155
if (m_framebuffer_size.x <= 0 || m_framebuffer_size.y <= 0) {
156156
return false;
157157
}
158-
// an eErrorOutOfDateKHR result is not guaranteed if the
159-
// framebuffer size does not match the Swapchain image size, check it
160-
// explicitly.
161-
auto fb_size_changed = m_framebuffer_size != m_swapchain->get_size();
162-
auto& render_sync = m_render_sync.at(m_frame_index);
163-
m_render_target = m_swapchain->acquire_next_image(*render_sync.draw);
164-
if (fb_size_changed || !m_render_target) {
165-
m_swapchain->recreate(m_framebuffer_size);
166-
return false;
167-
}
168158

169-
return true;
170-
}
159+
auto& render_sync = m_render_sync.at(m_frame_index);
171160

172-
auto App::wait_for_frame() -> vk::CommandBuffer {
173-
auto const& render_sync = m_render_sync.at(m_frame_index);
161+
// wait for the fence to be signaled.
174162
static constexpr auto fence_timeout_v =
175163
static_cast<std::uint64_t>(std::chrono::nanoseconds{3s}.count());
176164
auto result =
177165
m_device->waitForFences(*render_sync.drawn, vk::True, fence_timeout_v);
178166
if (result != vk::Result::eSuccess) {
179167
throw std::runtime_error{"Failed to wait for Render Fence"};
180168
}
169+
170+
m_render_target = m_swapchain->acquire_next_image(*render_sync.draw);
171+
if (!m_render_target) {
172+
// acquire failure => ErrorOutOfDate. Recreate Swapchain.
173+
m_swapchain->recreate(m_framebuffer_size);
174+
return false;
175+
}
176+
181177
// reset fence _after_ acquisition of image: if it fails, the
182178
// fence remains signaled.
183179
m_device->resetFences(*render_sync.drawn);
184180

181+
return true;
182+
}
183+
184+
auto App::begin_frame() -> vk::CommandBuffer {
185+
auto const& render_sync = m_render_sync.at(m_frame_index);
186+
185187
auto command_buffer_bi = vk::CommandBufferBeginInfo{};
186188
// this flag means recorded commands will not be reused.
187189
command_buffer_bi.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
@@ -193,15 +195,15 @@ void App::transition_for_render(vk::CommandBuffer const command_buffer) const {
193195
auto dependency_info = vk::DependencyInfo{};
194196
auto barrier = m_swapchain->base_barrier();
195197
// Undefined => AttachmentOptimal
196-
// we don't need to block any operations before the barrier, since we
197-
// rely on the image acquired semaphore to block rendering.
198-
// any color attachment operations must happen after the barrier.
198+
// the barrier must wait for prior color attachment operations to complete,
199+
// and block subsequent ones.
199200
barrier.setOldLayout(vk::ImageLayout::eUndefined)
200201
.setNewLayout(vk::ImageLayout::eAttachmentOptimal)
201-
.setSrcAccessMask(vk::AccessFlagBits2::eNone)
202-
.setSrcStageMask(vk::PipelineStageFlagBits2::eTopOfPipe)
203-
.setDstAccessMask(vk::AccessFlagBits2::eColorAttachmentWrite)
204-
.setDstStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput);
202+
.setSrcAccessMask(vk::AccessFlagBits2::eColorAttachmentRead |
203+
vk::AccessFlagBits2::eColorAttachmentWrite)
204+
.setSrcStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput)
205+
.setDstAccessMask(barrier.srcAccessMask)
206+
.setDstStageMask(barrier.srcStageMask);
205207
dependency_info.setImageMemoryBarriers(barrier);
206208
command_buffer.pipelineBarrier2(dependency_info);
207209
}
@@ -230,15 +232,15 @@ void App::transition_for_present(vk::CommandBuffer const command_buffer) const {
230232
auto dependency_info = vk::DependencyInfo{};
231233
auto barrier = m_swapchain->base_barrier();
232234
// AttachmentOptimal => PresentSrc
233-
// the barrier must wait for color attachment operations to complete.
234-
// we don't need any post-synchronization as the present Sempahore takes
235-
// care of that.
235+
// the barrier must wait for prior color attachment operations to complete,
236+
// and block subsequent ones.
236237
barrier.setOldLayout(vk::ImageLayout::eAttachmentOptimal)
237238
.setNewLayout(vk::ImageLayout::ePresentSrcKHR)
238-
.setSrcAccessMask(vk::AccessFlagBits2::eColorAttachmentWrite)
239+
.setSrcAccessMask(vk::AccessFlagBits2::eColorAttachmentRead |
240+
vk::AccessFlagBits2::eColorAttachmentWrite)
239241
.setSrcStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput)
240-
.setDstAccessMask(vk::AccessFlagBits2::eNone)
241-
.setDstStageMask(vk::PipelineStageFlagBits2::eBottomOfPipe);
242+
.setDstAccessMask(barrier.srcAccessMask)
243+
.setDstStageMask(barrier.srcStageMask);
242244
dependency_info.setImageMemoryBarriers(barrier);
243245
command_buffer.pipelineBarrier2(dependency_info);
244246
}
@@ -252,7 +254,7 @@ void App::submit_and_present() {
252254
vk::CommandBufferSubmitInfo{render_sync.command_buffer};
253255
auto wait_semaphore_info = vk::SemaphoreSubmitInfo{};
254256
wait_semaphore_info.setSemaphore(*render_sync.draw)
255-
.setStageMask(vk::PipelineStageFlagBits2::eTopOfPipe);
257+
.setStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput);
256258
auto signal_semaphore_info = vk::SemaphoreSubmitInfo{};
257259
signal_semaphore_info.setSemaphore(*render_sync.present)
258260
.setStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput);
@@ -264,7 +266,13 @@ void App::submit_and_present() {
264266
m_frame_index = (m_frame_index + 1) % m_render_sync.size();
265267
m_render_target.reset();
266268

267-
if (!m_swapchain->present(m_queue, *render_sync.present)) {
269+
// an eErrorOutOfDateKHR result is not guaranteed if the
270+
// framebuffer size does not match the Swapchain image size, check it
271+
// explicitly.
272+
auto const fb_size_changed = m_framebuffer_size != m_swapchain->get_size();
273+
auto const out_of_date =
274+
!m_swapchain->present(m_queue, *render_sync.present);
275+
if (fb_size_changed || out_of_date) {
268276
m_swapchain->recreate(m_framebuffer_size);
269277
}
270278
}

src/app.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ class App {
3434
void main_loop();
3535

3636
auto acquire_render_target() -> bool;
37-
auto wait_for_frame() -> vk::CommandBuffer;
37+
auto begin_frame() -> vk::CommandBuffer;
3838
void transition_for_render(vk::CommandBuffer command_buffer) const;
3939
void render(vk::CommandBuffer command_buffer);
4040
void transition_for_present(vk::CommandBuffer command_buffer) const;

0 commit comments

Comments
 (0)