Skip to content

Commit de8d0f7

Browse files
committed
Merge branch 'section/rendering' into section/dear-imgui
2 parents 8a9d69f + fbb84f0 commit de8d0f7

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
@@ -157,7 +157,7 @@ void App::main_loop() {
157157
while (glfwWindowShouldClose(m_window.get()) == GLFW_FALSE) {
158158
glfwPollEvents();
159159
if (!acquire_render_target()) { continue; }
160-
auto const command_buffer = wait_for_frame();
160+
auto const command_buffer = begin_frame();
161161
transition_for_render(command_buffer);
162162
render(command_buffer);
163163
transition_for_present(command_buffer);
@@ -171,34 +171,36 @@ auto App::acquire_render_target() -> bool {
171171
if (m_framebuffer_size.x <= 0 || m_framebuffer_size.y <= 0) {
172172
return false;
173173
}
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 fb_size_changed = m_framebuffer_size != m_swapchain->get_size();
178-
auto& render_sync = m_render_sync.at(m_frame_index);
179-
m_render_target = m_swapchain->acquire_next_image(*render_sync.draw);
180-
if (fb_size_changed || !m_render_target) {
181-
m_swapchain->recreate(m_framebuffer_size);
182-
return false;
183-
}
184174

185-
return true;
186-
}
175+
auto& render_sync = m_render_sync.at(m_frame_index);
187176

188-
auto App::wait_for_frame() -> vk::CommandBuffer {
189-
auto const& render_sync = m_render_sync.at(m_frame_index);
177+
// wait for the fence to be signaled.
190178
static constexpr auto fence_timeout_v =
191179
static_cast<std::uint64_t>(std::chrono::nanoseconds{3s}.count());
192180
auto result =
193181
m_device->waitForFences(*render_sync.drawn, vk::True, fence_timeout_v);
194182
if (result != vk::Result::eSuccess) {
195183
throw std::runtime_error{"Failed to wait for Render Fence"};
196184
}
185+
186+
m_render_target = m_swapchain->acquire_next_image(*render_sync.draw);
187+
if (!m_render_target) {
188+
// acquire failure => ErrorOutOfDate. Recreate Swapchain.
189+
m_swapchain->recreate(m_framebuffer_size);
190+
return false;
191+
}
192+
197193
// reset fence _after_ acquisition of image: if it fails, the
198194
// fence remains signaled.
199195
m_device->resetFences(*render_sync.drawn);
200196
m_imgui->new_frame();
201197

198+
return true;
199+
}
200+
201+
auto App::begin_frame() -> vk::CommandBuffer {
202+
auto const& render_sync = m_render_sync.at(m_frame_index);
203+
202204
auto command_buffer_bi = vk::CommandBufferBeginInfo{};
203205
// this flag means recorded commands will not be reused.
204206
command_buffer_bi.setFlags(vk::CommandBufferUsageFlagBits::eOneTimeSubmit);
@@ -210,15 +212,15 @@ void App::transition_for_render(vk::CommandBuffer const command_buffer) const {
210212
auto dependency_info = vk::DependencyInfo{};
211213
auto barrier = m_swapchain->base_barrier();
212214
// Undefined => AttachmentOptimal
213-
// we don't need to block any operations before the barrier, since we
214-
// rely on the image acquired semaphore to block rendering.
215-
// any color attachment operations must happen after the barrier.
215+
// the barrier must wait for prior color attachment operations to complete,
216+
// and block subsequent ones.
216217
barrier.setOldLayout(vk::ImageLayout::eUndefined)
217218
.setNewLayout(vk::ImageLayout::eAttachmentOptimal)
218-
.setSrcAccessMask(vk::AccessFlagBits2::eNone)
219-
.setSrcStageMask(vk::PipelineStageFlagBits2::eTopOfPipe)
220-
.setDstAccessMask(vk::AccessFlagBits2::eColorAttachmentWrite)
221-
.setDstStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput);
219+
.setSrcAccessMask(vk::AccessFlagBits2::eColorAttachmentRead |
220+
vk::AccessFlagBits2::eColorAttachmentWrite)
221+
.setSrcStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput)
222+
.setDstAccessMask(barrier.srcAccessMask)
223+
.setDstStageMask(barrier.srcStageMask);
222224
dependency_info.setImageMemoryBarriers(barrier);
223225
command_buffer.pipelineBarrier2(dependency_info);
224226
}
@@ -258,15 +260,15 @@ void App::transition_for_present(vk::CommandBuffer const command_buffer) const {
258260
auto dependency_info = vk::DependencyInfo{};
259261
auto barrier = m_swapchain->base_barrier();
260262
// AttachmentOptimal => PresentSrc
261-
// the barrier must wait for color attachment operations to complete.
262-
// we don't need any post-synchronization as the present Sempahore takes
263-
// care of that.
263+
// the barrier must wait for prior color attachment operations to complete,
264+
// and block subsequent ones.
264265
barrier.setOldLayout(vk::ImageLayout::eAttachmentOptimal)
265266
.setNewLayout(vk::ImageLayout::ePresentSrcKHR)
266-
.setSrcAccessMask(vk::AccessFlagBits2::eColorAttachmentWrite)
267+
.setSrcAccessMask(vk::AccessFlagBits2::eColorAttachmentRead |
268+
vk::AccessFlagBits2::eColorAttachmentWrite)
267269
.setSrcStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput)
268-
.setDstAccessMask(vk::AccessFlagBits2::eNone)
269-
.setDstStageMask(vk::PipelineStageFlagBits2::eBottomOfPipe);
270+
.setDstAccessMask(barrier.srcAccessMask)
271+
.setDstStageMask(barrier.srcStageMask);
270272
dependency_info.setImageMemoryBarriers(barrier);
271273
command_buffer.pipelineBarrier2(dependency_info);
272274
}
@@ -280,7 +282,7 @@ void App::submit_and_present() {
280282
vk::CommandBufferSubmitInfo{render_sync.command_buffer};
281283
auto wait_semaphore_info = vk::SemaphoreSubmitInfo{};
282284
wait_semaphore_info.setSemaphore(*render_sync.draw)
283-
.setStageMask(vk::PipelineStageFlagBits2::eTopOfPipe);
285+
.setStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput);
284286
auto signal_semaphore_info = vk::SemaphoreSubmitInfo{};
285287
signal_semaphore_info.setSemaphore(*render_sync.present)
286288
.setStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput);
@@ -292,7 +294,13 @@ void App::submit_and_present() {
292294
m_frame_index = (m_frame_index + 1) % m_render_sync.size();
293295
m_render_target.reset();
294296

295-
if (!m_swapchain->present(m_queue, *render_sync.present)) {
297+
// an eErrorOutOfDateKHR result is not guaranteed if the
298+
// framebuffer size does not match the Swapchain image size, check it
299+
// explicitly.
300+
auto const fb_size_changed = m_framebuffer_size != m_swapchain->get_size();
301+
auto const out_of_date =
302+
!m_swapchain->present(m_queue, *render_sync.present);
303+
if (fb_size_changed || out_of_date) {
296304
m_swapchain->recreate(m_framebuffer_size);
297305
}
298306
}

src/app.hpp

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

3838
auto acquire_render_target() -> bool;
39-
auto wait_for_frame() -> vk::CommandBuffer;
39+
auto begin_frame() -> vk::CommandBuffer;
4040
void transition_for_render(vk::CommandBuffer command_buffer) const;
4141
void render(vk::CommandBuffer command_buffer);
4242
void transition_for_present(vk::CommandBuffer command_buffer) const;

0 commit comments

Comments
 (0)