Skip to content

Commit a9d1ad0

Browse files
committed
Merge branch 'section/dear-imgui' into section/graphics-pipeline
2 parents 39884df + de8d0f7 commit a9d1ad0

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
@@ -222,7 +222,7 @@ void App::main_loop() {
222222
while (glfwWindowShouldClose(m_window.get()) == GLFW_FALSE) {
223223
glfwPollEvents();
224224
if (!acquire_render_target()) { continue; }
225-
auto const command_buffer = wait_for_frame();
225+
auto const command_buffer = begin_frame();
226226
transition_for_render(command_buffer);
227227
render(command_buffer);
228228
transition_for_present(command_buffer);
@@ -236,34 +236,36 @@ auto App::acquire_render_target() -> bool {
236236
if (m_framebuffer_size.x <= 0 || m_framebuffer_size.y <= 0) {
237237
return false;
238238
}
239-
// an eErrorOutOfDateKHR result is not guaranteed if the
240-
// framebuffer size does not match the Swapchain image size, check it
241-
// explicitly.
242-
auto fb_size_changed = m_framebuffer_size != m_swapchain->get_size();
243-
auto& render_sync = m_render_sync.at(m_frame_index);
244-
m_render_target = m_swapchain->acquire_next_image(*render_sync.draw);
245-
if (fb_size_changed || !m_render_target) {
246-
m_swapchain->recreate(m_framebuffer_size);
247-
return false;
248-
}
249239

250-
return true;
251-
}
240+
auto& render_sync = m_render_sync.at(m_frame_index);
252241

253-
auto App::wait_for_frame() -> vk::CommandBuffer {
254-
auto const& render_sync = m_render_sync.at(m_frame_index);
242+
// wait for the fence to be signaled.
255243
static constexpr auto fence_timeout_v =
256244
static_cast<std::uint64_t>(std::chrono::nanoseconds{3s}.count());
257245
auto result =
258246
m_device->waitForFences(*render_sync.drawn, vk::True, fence_timeout_v);
259247
if (result != vk::Result::eSuccess) {
260248
throw std::runtime_error{"Failed to wait for Render Fence"};
261249
}
250+
251+
m_render_target = m_swapchain->acquire_next_image(*render_sync.draw);
252+
if (!m_render_target) {
253+
// acquire failure => ErrorOutOfDate. Recreate Swapchain.
254+
m_swapchain->recreate(m_framebuffer_size);
255+
return false;
256+
}
257+
262258
// reset fence _after_ acquisition of image: if it fails, the
263259
// fence remains signaled.
264260
m_device->resetFences(*render_sync.drawn);
265261
m_imgui->new_frame();
266262

263+
return true;
264+
}
265+
266+
auto App::begin_frame() -> vk::CommandBuffer {
267+
auto const& render_sync = m_render_sync.at(m_frame_index);
268+
267269
auto command_buffer_bi = vk::CommandBufferBeginInfo{};
268270
// this flag means recorded commands will not be reused.
269271
command_buffer_bi.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
@@ -275,15 +277,15 @@ void App::transition_for_render(vk::CommandBuffer const command_buffer) const {
275277
auto dependency_info = vk::DependencyInfo{};
276278
auto barrier = m_swapchain->base_barrier();
277279
// Undefined => AttachmentOptimal
278-
// we don't need to block any operations before the barrier, since we
279-
// rely on the image acquired semaphore to block rendering.
280-
// any color attachment operations must happen after the barrier.
280+
// the barrier must wait for prior color attachment operations to complete,
281+
// and block subsequent ones.
281282
barrier.setOldLayout(vk::ImageLayout::eUndefined)
282283
.setNewLayout(vk::ImageLayout::eAttachmentOptimal)
283-
.setSrcAccessMask(vk::AccessFlagBits2::eNone)
284-
.setSrcStageMask(vk::PipelineStageFlagBits2::eTopOfPipe)
285-
.setDstAccessMask(vk::AccessFlagBits2::eColorAttachmentWrite)
286-
.setDstStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput);
284+
.setSrcAccessMask(vk::AccessFlagBits2::eColorAttachmentRead |
285+
vk::AccessFlagBits2::eColorAttachmentWrite)
286+
.setSrcStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput)
287+
.setDstAccessMask(barrier.srcAccessMask)
288+
.setDstStageMask(barrier.srcStageMask);
287289
dependency_info.setImageMemoryBarriers(barrier);
288290
command_buffer.pipelineBarrier2(dependency_info);
289291
}
@@ -323,15 +325,15 @@ void App::transition_for_present(vk::CommandBuffer const command_buffer) const {
323325
auto dependency_info = vk::DependencyInfo{};
324326
auto barrier = m_swapchain->base_barrier();
325327
// AttachmentOptimal => PresentSrc
326-
// the barrier must wait for color attachment operations to complete.
327-
// we don't need any post-synchronization as the present Sempahore takes
328-
// care of that.
328+
// the barrier must wait for prior color attachment operations to complete,
329+
// and block subsequent ones.
329330
barrier.setOldLayout(vk::ImageLayout::eAttachmentOptimal)
330331
.setNewLayout(vk::ImageLayout::ePresentSrcKHR)
331-
.setSrcAccessMask(vk::AccessFlagBits2::eColorAttachmentWrite)
332+
.setSrcAccessMask(vk::AccessFlagBits2::eColorAttachmentRead |
333+
vk::AccessFlagBits2::eColorAttachmentWrite)
332334
.setSrcStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput)
333-
.setDstAccessMask(vk::AccessFlagBits2::eNone)
334-
.setDstStageMask(vk::PipelineStageFlagBits2::eBottomOfPipe);
335+
.setDstAccessMask(barrier.srcAccessMask)
336+
.setDstStageMask(barrier.srcStageMask);
335337
dependency_info.setImageMemoryBarriers(barrier);
336338
command_buffer.pipelineBarrier2(dependency_info);
337339
}
@@ -345,7 +347,7 @@ void App::submit_and_present() {
345347
vk::CommandBufferSubmitInfo{render_sync.command_buffer};
346348
auto wait_semaphore_info = vk::SemaphoreSubmitInfo{};
347349
wait_semaphore_info.setSemaphore(*render_sync.draw)
348-
.setStageMask(vk::PipelineStageFlagBits2::eTopOfPipe);
350+
.setStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput);
349351
auto signal_semaphore_info = vk::SemaphoreSubmitInfo{};
350352
signal_semaphore_info.setSemaphore(*render_sync.present)
351353
.setStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput);
@@ -357,7 +359,13 @@ void App::submit_and_present() {
357359
m_frame_index = (m_frame_index + 1) % m_render_sync.size();
358360
m_render_target.reset();
359361

360-
if (!m_swapchain->present(m_queue, *render_sync.present)) {
362+
// an eErrorOutOfDateKHR result is not guaranteed if the
363+
// framebuffer size does not match the Swapchain image size, check it
364+
// explicitly.
365+
auto const fb_size_changed = m_framebuffer_size != m_swapchain->get_size();
366+
auto const out_of_date =
367+
!m_swapchain->present(m_queue, *render_sync.present);
368+
if (fb_size_changed || out_of_date) {
361369
m_swapchain->recreate(m_framebuffer_size);
362370
}
363371
}

src/app.hpp

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

4545
auto acquire_render_target() -> bool;
46-
auto wait_for_frame() -> vk::CommandBuffer;
46+
auto begin_frame() -> vk::CommandBuffer;
4747
void transition_for_render(vk::CommandBuffer command_buffer) const;
4848
void render(vk::CommandBuffer command_buffer);
4949
void transition_for_present(vk::CommandBuffer command_buffer) const;

0 commit comments

Comments
 (0)