@@ -6,7 +6,7 @@ Add these new members to `App`:
6
6
7
7
``` cpp
8
8
auto acquire_render_target () -> bool;
9
- auto wait_for_frame () -> vk::CommandBuffer;
9
+ auto begin_frame () -> vk::CommandBuffer;
10
10
void transition_for_render (vk::CommandBuffer command_buffer) const;
11
11
void render(vk::CommandBuffer command_buffer);
12
12
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:
23
23
while (glfwWindowShouldClose(m_window.get()) == GLFW_FALSE) {
24
24
glfwPollEvents();
25
25
if (!acquire_render_target()) { continue; }
26
- auto const command_buffer = wait_for_frame ();
26
+ auto const command_buffer = begin_frame ();
27
27
transition_for_render(command_buffer);
28
28
render(command_buffer);
29
29
transition_for_present(command_buffer);
30
30
submit_and_present();
31
31
}
32
32
```
33
33
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) :
35
35
36
36
``` cpp
37
37
auto App::acquire_render_target () -> bool {
@@ -40,36 +40,38 @@ auto App::acquire_render_target() -> bool {
40
40
if (m_framebuffer_size.x <= 0 || m_framebuffer_size.y <= 0) {
41
41
return false;
42
42
}
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
+
47
44
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
+
48
55
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.
50
58
m_swapchain->recreate(m_framebuffer_size);
51
59
return false;
52
60
}
53
61
62
+ // reset fence _ after_ acquisition of image: if it fails, the
63
+ // fence remains signaled.
64
+ m_device->resetFences(* render_sync.drawn);
65
+
54
66
return true;
55
67
}
56
68
```
57
69
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:
59
71
60
72
``` cpp
61
- auto App::wait_for_frame () -> vk::CommandBuffer {
73
+ auto App::begin_frame () -> vk::CommandBuffer {
62
74
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);
73
75
74
76
auto command_buffer_bi = vk::CommandBufferBeginInfo{};
75
77
// this flag means recorded commands will not be reused.
@@ -79,24 +81,22 @@ auto App::wait_for_frame() -> vk::CommandBuffer {
79
81
}
80
82
```
81
83
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
-
84
84
Transition the image for rendering, ie Attachment Optimal layout. Set up the image barrier and record it:
85
85
86
86
``` cpp
87
87
void App::transition_for_render (vk::CommandBuffer const command_buffer) const {
88
88
auto dependency_info = vk::DependencyInfo{};
89
89
auto barrier = m_swapchain->base_barrier();
90
90
// 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.
94
93
barrier.setOldLayout(vk::ImageLayout::eUndefined)
95
94
.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);
100
100
dependency_info.setImageMemoryBarriers(barrier);
101
101
command_buffer.pipelineBarrier2(dependency_info);
102
102
}
@@ -133,15 +133,15 @@ void App::transition_for_present(vk::CommandBuffer const command_buffer) const {
133
133
auto dependency_info = vk::DependencyInfo{};
134
134
auto barrier = m_swapchain->base_barrier();
135
135
// 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.
139
138
barrier.setOldLayout(vk::ImageLayout::eAttachmentOptimal)
140
139
.setNewLayout(vk::ImageLayout::ePresentSrcKHR)
141
- .setSrcAccessMask(vk::AccessFlagBits2::eColorAttachmentWrite)
140
+ .setSrcAccessMask(vk::AccessFlagBits2::eColorAttachmentRead |
141
+ vk::AccessFlagBits2::eColorAttachmentWrite)
142
142
.setSrcStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput)
143
- .setDstAccessMask(vk::AccessFlagBits2::eNone )
144
- .setDstStageMask(vk::PipelineStageFlagBits2::eBottomOfPipe );
143
+ .setDstAccessMask(barrier.srcAccessMask )
144
+ .setDstStageMask(barrier.srcStageMask );
145
145
dependency_info.setImageMemoryBarriers(barrier);
146
146
command_buffer.pipelineBarrier2(dependency_info);
147
147
}
@@ -159,7 +159,7 @@ void App::submit_and_present() {
159
159
vk::CommandBufferSubmitInfo{render_sync.command_buffer};
160
160
auto wait_semaphore_info = vk::SemaphoreSubmitInfo{};
161
161
wait_semaphore_info.setSemaphore(*render_sync.draw)
162
- .setStageMask(vk::PipelineStageFlagBits2::eTopOfPipe );
162
+ .setStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput );
163
163
auto signal_semaphore_info = vk::SemaphoreSubmitInfo{};
164
164
signal_semaphore_info.setSemaphore(*render_sync.present)
165
165
.setStageMask(vk::PipelineStageFlagBits2::eColorAttachmentOutput);
@@ -171,7 +171,13 @@ void App::submit_and_present() {
171
171
m_frame_index = (m_frame_index + 1) % m_render_sync.size();
172
172
m_render_target.reset();
173
173
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) {
175
181
m_swapchain->recreate(m_framebuffer_size);
176
182
}
177
183
}
0 commit comments