Skip to content
This repository was archived by the owner on Oct 28, 2024. It is now read-only.

Commit 01e4075

Browse files
committed
Fix synchronization in async_compute sample
The "mailbox" caused images to be released by the async compute queue without a corresponding acquire on the render queue, which is illegal. To fix this issue, whenever the async compute thread sends a buffer to the mailbox, the existing buffer(s) stay in the mailbox; only the most recent buffer in the mailbox will actually be rendered, and the remaining buffers will simply be acquired on the render queue and immediately released back to the async compute queue.
1 parent 93f09bd commit 01e4075

File tree

1 file changed

+114
-64
lines changed
  • application_sandbox/async_compute

1 file changed

+114
-64
lines changed

application_sandbox/async_compute/main.cpp

Lines changed: 114 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ class ASyncThreadRunner {
237237
vulkan::CreateFence(&app_->device()),
238238
app_->CreateAndBindDeviceBuffer(&create_info),
239239
app_->GetCommandBuffer(app_->async_compute_queue()->index()),
240-
app_->GetCommandBuffer(),
240+
app_->GetCommandBuffer(), app_->GetCommandBuffer(),
241241
containers::make_unique<vulkan::DescriptorSet>(
242242
allocator_, app_->AllocateDescriptorSet(
243243
{compute_descriptor_set_layouts_[0],
@@ -349,29 +349,75 @@ class ASyncThreadRunner {
349349
command_buffer->vkEndCommandBuffer(command_buffer);
350350
ready_buffers_.push_back(static_cast<uint32_t>(i));
351351

352-
// Wake command buffer
353-
auto& wake_command_buffer = dat.wake_command_buffer_;
354-
wake_command_buffer->vkBeginCommandBuffer(
355-
wake_command_buffer, &sample_application::kBeginCommandBuffer);
356-
VkBufferMemoryBarrier wake_barrier = {
357-
VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, // sType
358-
nullptr, // pNext
359-
VK_ACCESS_SHADER_READ_BIT, // srcAccessMask
360-
0, // dstAccessMask
361-
app_->render_queue().index(), // srcQueueFamilyIndex
362-
app_->async_compute_queue()->index(), // dstQueueFamilyIndex
363-
*dat.render_ssbo_, // buffer
364-
0, // offset
365-
dat.render_ssbo_->size(), // size
366-
};
367-
wake_command_buffer->vkCmdPipelineBarrier(
368-
wake_command_buffer, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,
369-
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 1, &wake_barrier,
370-
0, nullptr);
352+
{
353+
// Acquire command buffer
354+
auto& acquire_command_buffer = dat.acquire_command_buffer_;
355+
acquire_command_buffer->vkBeginCommandBuffer(
356+
acquire_command_buffer, &sample_application::kBeginCommandBuffer);
357+
358+
VkBufferMemoryBarrier barrier = {
359+
VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, // sType
360+
nullptr, // pNext
361+
0, // srcAccessMask
362+
VK_ACCESS_SHADER_READ_BIT, // dstAccessMask
363+
app_->async_compute_queue()->index(), // srcQueueFamilyIndex
364+
app_->render_queue().index(), // dstQueueFamilyIndex
365+
*dat.render_ssbo_, // bufferdraw_data
366+
0, // offset
367+
dat.render_ssbo_->size(), // size
368+
};
369+
acquire_command_buffer->vkCmdPipelineBarrier(
370+
acquire_command_buffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
371+
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, 0, 0, nullptr, 1, &barrier, 0,
372+
nullptr);
373+
acquire_command_buffer->vkEndCommandBuffer(acquire_command_buffer);
374+
}
371375

372-
wake_command_buffer->vkEndCommandBuffer(wake_command_buffer);
376+
{
377+
// Wake command buffer
378+
auto& wake_command_buffer = dat.wake_command_buffer_;
379+
wake_command_buffer->vkBeginCommandBuffer(
380+
wake_command_buffer, &sample_application::kBeginCommandBuffer);
381+
VkBufferMemoryBarrier wake_barrier = {
382+
VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, // sType
383+
nullptr, // pNext
384+
VK_ACCESS_SHADER_READ_BIT, // srcAccessMask
385+
0, // dstAccessMask
386+
app_->render_queue().index(), // srcQueueFamilyIndex
387+
app_->async_compute_queue()->index(), // dstQueueFamilyIndex
388+
*dat.render_ssbo_, // buffer
389+
0, // offset
390+
dat.render_ssbo_->size(), // size
391+
};
392+
wake_command_buffer->vkCmdPipelineBarrier(
393+
wake_command_buffer, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT,
394+
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 1,
395+
&wake_barrier, 0, nullptr);
396+
397+
wake_command_buffer->vkEndCommandBuffer(wake_command_buffer);
398+
}
373399
}
374400

401+
std::vector<VkCommandBuffer> all_wake_command_buffers;
402+
for (const auto& dat : data_) {
403+
all_wake_command_buffers.push_back(dat.wake_command_buffer_);
404+
}
405+
406+
VkSubmitInfo wake_all_submit_info{
407+
VK_STRUCTURE_TYPE_SUBMIT_INFO, // sType
408+
nullptr, // pNext
409+
0, // waitSemaphoreCount
410+
nullptr, // pWaitSemaphores
411+
nullptr, // pWaitDstStageMask,
412+
static_cast<uint32_t>(all_wake_command_buffers.size()),
413+
all_wake_command_buffers.data(),
414+
0, // signalSemaphoreCount
415+
nullptr // pSignalSemaphores
416+
};
417+
app_->render_queue()->vkQueueSubmit(app_->render_queue(), 1,
418+
&wake_all_submit_info,
419+
::VkFence(VK_NULL_HANDLE));
420+
375421
(*initial_data_buffer)->vkEndCommandBuffer(*initial_data_buffer);
376422
VkSubmitInfo setup_submit_info{
377423
VK_STRUCTURE_TYPE_SUBMIT_INFO, // sType
@@ -426,28 +472,23 @@ class ASyncThreadRunner {
426472

427473
int32_t mb = mailbox_buffer_;
428474
mailbox_buffer_ = -1;
429-
if (index != -1) {
430-
// Enqueues a command-buffer that transitions the buffer back to
431-
// the compute queue. It also sets the fence that we can wait on
432-
// in the future.
433-
auto& data = data_[index];
434-
VkSubmitInfo wake_submit_info{
435-
VK_STRUCTURE_TYPE_SUBMIT_INFO, // sType
436-
nullptr, // pNext
437-
0, // waitSemaphoreCount
438-
nullptr, // pWaitSemaphores
439-
nullptr, // pWaitDstStageMask,
440-
1, // commandBufferCount
441-
&(data.wake_command_buffer_.get_command_buffer()),
442-
0, // signalSemaphoreCount
443-
nullptr // pSignalSemaphores
444-
};
445475

446-
app_->render_queue()->vkQueueSubmit(
447-
app_->render_queue(), 1, &wake_submit_info, data.return_fence_);
448-
returned_buffers_.push_back(index);
476+
// Acquire the mailbox buffer to the gfx queue for rendering
477+
TransferBuffer(mb, true, false);
478+
479+
if (index != -1) {
480+
// Release the previously rendered buffer back to async compute
481+
TransferBuffer(index, false, true);
449482
}
450483

484+
for (int32_t released : released_buffers_) {
485+
if (released != mb) {
486+
// Acquire and immediately release any other buffers that have been
487+
// released by async compute.
488+
TransferBuffer(released, true, true);
489+
}
490+
}
491+
released_buffers_.clear();
451492
return mb;
452493
}
453494

@@ -597,12 +638,38 @@ class ASyncThreadRunner {
597638
// already in the mailbox, moves it to the ready_buffers_.
598639
void PutBufferInMailbox(int32_t buffer) {
599640
std::lock_guard<std::mutex> lock(data_mutex_);
600-
if (mailbox_buffer_ != -1) {
601-
ready_buffers_.push_back(mailbox_buffer_);
602-
}
603641
mailbox_buffer_ = buffer;
642+
released_buffers_.push_back(buffer);
604643
}
605644

645+
// Acquires and/or releases a buffer *on the graphics queue*
646+
void TransferBuffer(int32_t index, bool acquire_to_gfx,
647+
bool release_from_gfx) {
648+
std::vector<VkCommandBuffer> command_buffers;
649+
auto& data = data_[index];
650+
VkFence fence = VK_NULL_HANDLE;
651+
if (acquire_to_gfx) {
652+
command_buffers.push_back(data.acquire_command_buffer_);
653+
}
654+
if (release_from_gfx) {
655+
command_buffers.push_back(data.wake_command_buffer_);
656+
fence = data.return_fence_;
657+
returned_buffers_.push_back(index);
658+
}
659+
VkSubmitInfo submit_info{
660+
VK_STRUCTURE_TYPE_SUBMIT_INFO, // sType
661+
nullptr, // pNext
662+
0, // waitSemaphoreCount
663+
nullptr, // pWaitSemaphores
664+
nullptr, // pWaitDstStageMask,
665+
static_cast<uint32_t>(command_buffers.size()), // commandBufferCount
666+
command_buffers.data(),
667+
0, // signalSemaphoreCount
668+
nullptr // pSignalSemaphores
669+
};
670+
app_->render_queue()->vkQueueSubmit(app_->render_queue(), 1, &submit_info,
671+
fence);
672+
}
606673
struct PrivateAsyncData {
607674
// Fence that is signalled once a buffer is returned.
608675
vulkan::VkFence return_fence_;
@@ -612,6 +679,8 @@ class ASyncThreadRunner {
612679
vulkan::VkCommandBuffer command_buffer_;
613680
// The command buffer for transferring this back to the simulation thread.
614681
vulkan::VkCommandBuffer wake_command_buffer_;
682+
// The command buffer for acquiring this from the simulation thread.
683+
vulkan::VkCommandBuffer acquire_command_buffer_;
615684
// The descriptor set needed for simulating.
616685
containers::unique_ptr<vulkan::DescriptorSet> compute_descriptor_set_;
617686
};
@@ -651,6 +720,8 @@ class ASyncThreadRunner {
651720

652721
// The current buffer sitting in the output mailbox.
653722
int32_t mailbox_buffer_;
723+
// Buffers that have been released by async and need to be acquired by gfx
724+
std::vector<int32_t> released_buffers_;
654725
bool first = true;
655726
int current_frame = 0;
656727

@@ -878,7 +949,6 @@ class AsyncSample : public sample_application::Sample<AsyncFrameData> {
878949
current_computation_result_buffer_ =
879950
thread_runner_.TryToReturnAndGetNextBuffer(
880951
current_computation_result_buffer_);
881-
bool swapped_buffer = old_buffer != current_computation_result_buffer_;
882952
auto* buffer =
883953
thread_runner_.GetBufferForIndex(current_computation_result_buffer_);
884954
aspect_buffer_->UpdateBuffer(&app()->render_queue(), frame_index);
@@ -971,26 +1041,6 @@ class AsyncSample : public sample_application::Sample<AsyncFrameData> {
9711041
vulkan::MemoryClear(&clear);
9721042
clear.color.float32[3] = 1.0f;
9731043

974-
if (swapped_buffer) {
975-
// If we have not transitioned this buffer yet, then move it from
976-
// the compute queue over to this queue.
977-
VkBufferMemoryBarrier barrier = {
978-
VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, // sType
979-
nullptr, // pNext
980-
0, // srcAccessMask
981-
VK_ACCESS_SHADER_READ_BIT, // dstAccessMask
982-
app()->async_compute_queue()->index(), // srcQueueFamilyIndex
983-
app()->render_queue().index(), // dstQueueFamilyIndex
984-
*buffer, // bufferdraw_data
985-
0, // offset
986-
buffer->size(), // size
987-
};
988-
cmdBuffer->vkCmdPipelineBarrier(cmdBuffer,
989-
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
990-
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT, 0, 0,
991-
nullptr, 1, &barrier, 0, nullptr);
992-
}
993-
9941044
// The rest of the normal drawing.
9951045
VkRenderPassBeginInfo pass_begin = {
9961046
VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, // sType

0 commit comments

Comments
 (0)