[D3D12/Vulkan] Simplify host GPU fence management
Replace the `SubmissionTracker`s with new `GPUCompletionTimeline`s with a more unified interface (using a base class), and without the internal logic for queue ownership transfers since that idea was scrapped during the development of the `Presenter`. Also use this fence management logic for GPU emulation, though without architectural reworks for now, just on the bottom level. Still very messy, but can be cleaned up in further GPU command processor and presenter reworks.
This commit is contained in:
@@ -158,8 +158,8 @@ VulkanPresenter::~VulkanPresenter() {
|
||||
// (paint submission completion already awaited).
|
||||
// From most likely the latest to most likely the earliest to be signaled, so
|
||||
// just one sleep will likely be needed.
|
||||
ui_submission_tracker_.Shutdown();
|
||||
guest_output_image_refresher_submission_tracker_.Shutdown();
|
||||
ui_completion_timeline_.AwaitAllSubmissions();
|
||||
guest_output_image_refresher_completion_timeline_.AwaitAllSubmissions();
|
||||
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
const VkDevice device = vulkan_device_->device();
|
||||
@@ -382,51 +382,24 @@ bool VulkanPresenter::CaptureGuestOutput(RawImage& image_out) {
|
||||
return false;
|
||||
}
|
||||
|
||||
VkSubmitInfo submit_info = {};
|
||||
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
submit_info.commandBufferCount = 1;
|
||||
submit_info.pCommandBuffers = &command_buffer;
|
||||
VulkanSubmissionTracker submission_tracker(vulkan_device_);
|
||||
{
|
||||
VulkanSubmissionTracker::FenceAcquisition fence_acqusition(
|
||||
submission_tracker.AcquireFenceToAdvanceSubmission());
|
||||
if (!fence_acqusition.fence()) {
|
||||
XELOGE(
|
||||
"VulkanPresenter: Failed to acquire a fence for guest output "
|
||||
"capturing");
|
||||
fence_acqusition.SubmissionFailedOrDropped();
|
||||
dfn.vkDestroyCommandPool(device, command_pool, nullptr);
|
||||
dfn.vkDestroyBuffer(device, buffer, nullptr);
|
||||
dfn.vkFreeMemory(device, buffer_memory, nullptr);
|
||||
return false;
|
||||
}
|
||||
VkResult submit_result;
|
||||
{
|
||||
const VulkanDevice::Queue::Acquisition queue_acquisition =
|
||||
vulkan_device_->AcquireQueue(
|
||||
vulkan_device_->queue_family_graphics_compute(), 0);
|
||||
submit_result =
|
||||
dfn.vkQueueSubmit(queue_acquisition.queue(), 1, &submit_info,
|
||||
fence_acqusition.fence());
|
||||
}
|
||||
VulkanGPUCompletionTimeline completion_timeline(vulkan_device_);
|
||||
VkSubmitInfo submit_info = {VK_STRUCTURE_TYPE_SUBMIT_INFO};
|
||||
submit_info.commandBufferCount = 1;
|
||||
submit_info.pCommandBuffers = &command_buffer;
|
||||
const VkResult submit_result = completion_timeline.AcquireFenceAndSubmit(
|
||||
vulkan_device_->queue_family_graphics_compute(), 0, 1, &submit_info);
|
||||
if (submit_result != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"VulkanPresenter: Failed to submit the guest output capturing "
|
||||
"command buffer");
|
||||
fence_acqusition.SubmissionFailedOrDropped();
|
||||
"command buffer: {}",
|
||||
vk::to_string(vk::Result(submit_result)));
|
||||
dfn.vkDestroyCommandPool(device, command_pool, nullptr);
|
||||
dfn.vkDestroyBuffer(device, buffer, nullptr);
|
||||
dfn.vkFreeMemory(device, buffer_memory, nullptr);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!submission_tracker.AwaitAllSubmissionsCompletion()) {
|
||||
XELOGE(
|
||||
"VulkanPresenter: Failed to await the guest output capturing fence");
|
||||
dfn.vkDestroyCommandPool(device, command_pool, nullptr);
|
||||
dfn.vkDestroyBuffer(device, buffer, nullptr);
|
||||
dfn.vkFreeMemory(device, buffer_memory, nullptr);
|
||||
return false;
|
||||
// Destroying the completion timeline causes the submission to be awaited.
|
||||
}
|
||||
|
||||
dfn.vkDestroyCommandPool(device, command_pool, nullptr);
|
||||
@@ -479,8 +452,8 @@ VkCommandBuffer VulkanPresenter::AcquireUISetupCommandBufferFromUIThread() {
|
||||
|
||||
// Try to reuse an existing command buffer.
|
||||
if (!paint_context_.ui_setup_command_buffers.empty()) {
|
||||
uint64_t submission_index_completed =
|
||||
ui_submission_tracker_.UpdateAndGetCompletedSubmission();
|
||||
const uint64_t submission_index_completed =
|
||||
ui_completion_timeline_.UpdateAndGetCompletedSubmission();
|
||||
for (size_t i = 0; i < paint_context_.ui_setup_command_buffers.size();
|
||||
++i) {
|
||||
PaintContext::UISetupCommandBuffer& ui_setup_command_buffer =
|
||||
@@ -503,7 +476,7 @@ VkCommandBuffer VulkanPresenter::AcquireUISetupCommandBufferFromUIThread() {
|
||||
}
|
||||
paint_context_.ui_setup_command_buffer_current_index = i;
|
||||
ui_setup_command_buffer.last_usage_submission_index =
|
||||
ui_submission_tracker_.GetCurrentSubmission();
|
||||
ui_completion_timeline_.GetUpcomingSubmission();
|
||||
return ui_setup_command_buffer.command_buffer;
|
||||
}
|
||||
}
|
||||
@@ -546,7 +519,7 @@ VkCommandBuffer VulkanPresenter::AcquireUISetupCommandBufferFromUIThread() {
|
||||
paint_context_.ui_setup_command_buffers.size();
|
||||
paint_context_.ui_setup_command_buffers.emplace_back(
|
||||
new_command_pool, new_command_buffer,
|
||||
ui_submission_tracker_.GetCurrentSubmission());
|
||||
ui_completion_timeline_.GetUpcomingSubmission());
|
||||
return new_command_buffer;
|
||||
}
|
||||
|
||||
@@ -877,8 +850,9 @@ bool VulkanPresenter::RefreshGuestOutputImpl(
|
||||
if (image_instance.image &&
|
||||
(image_instance.image->extent().width != frontbuffer_width ||
|
||||
image_instance.image->extent().height != frontbuffer_height)) {
|
||||
guest_output_image_refresher_submission_tracker_.AwaitSubmissionCompletion(
|
||||
image_instance.last_refresher_submission);
|
||||
guest_output_image_refresher_completion_timeline_
|
||||
.AwaitSubmissionAndUpdateCompleted(
|
||||
image_instance.last_refresher_submission);
|
||||
image_instance.image.reset();
|
||||
}
|
||||
if (!image_instance.image) {
|
||||
@@ -904,26 +878,21 @@ bool VulkanPresenter::RefreshGuestOutputImpl(
|
||||
// signal and wait slightly longer, for nothing important, while shutting down
|
||||
// than to destroy the image while it's still in use.
|
||||
image_instance.last_refresher_submission =
|
||||
guest_output_image_refresher_submission_tracker_.GetCurrentSubmission();
|
||||
guest_output_image_refresher_completion_timeline_.GetUpcomingSubmission();
|
||||
// No need to make the refresher signal the fence by itself - signal it here
|
||||
// instead to have more control:
|
||||
// "Fence signal operations that are defined by vkQueueSubmit additionally
|
||||
// include in the first synchronization scope all commands that occur earlier
|
||||
// in submission order."
|
||||
const VulkanDevice::Functions& dfn = vulkan_device_->functions();
|
||||
{
|
||||
VulkanSubmissionTracker::FenceAcquisition fence_acqusition(
|
||||
guest_output_image_refresher_submission_tracker_
|
||||
.AcquireFenceToAdvanceSubmission());
|
||||
const VulkanDevice::Queue::Acquisition queue_acquisition =
|
||||
vulkan_device_->AcquireQueue(
|
||||
vulkan_device_->queue_family_graphics_compute(), 0);
|
||||
if (dfn.vkQueueSubmit(queue_acquisition.queue(), 0, nullptr,
|
||||
fence_acqusition.fence()) != VK_SUCCESS) {
|
||||
fence_acqusition.SubmissionSucceededSignalFailed();
|
||||
}
|
||||
const VkResult submit_result =
|
||||
guest_output_image_refresher_completion_timeline_.AcquireFenceAndSubmit(
|
||||
vulkan_device_->queue_family_graphics_compute(), 0, 0, nullptr);
|
||||
if (submit_result != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"VulkanPresenter: Failed to submit the guest output image refresh "
|
||||
"fence signal: {}",
|
||||
vk::to_string(vk::Result(submit_result)));
|
||||
}
|
||||
|
||||
return refresher_succeeded;
|
||||
}
|
||||
|
||||
@@ -1277,7 +1246,7 @@ VkSwapchainKHR VulkanPresenter::PaintContext::CreateSwapchainForVulkanSurface(
|
||||
|
||||
VkSwapchainKHR VulkanPresenter::PaintContext::PrepareForSwapchainRetirement() {
|
||||
if (swapchain != VK_NULL_HANDLE) {
|
||||
submission_tracker.AwaitAllSubmissionsCompletion();
|
||||
completion_timeline.AwaitAllSubmissions();
|
||||
}
|
||||
const VulkanDevice::Functions& dfn = vulkan_device->functions();
|
||||
const VkDevice device = vulkan_device->device();
|
||||
@@ -1385,13 +1354,11 @@ Presenter::PaintResult VulkanPresenter::PaintAndPresentImpl(
|
||||
bool execute_ui_drawers) {
|
||||
// Begin the submission in place of the one not currently potentially used on
|
||||
// the GPU.
|
||||
uint64_t current_paint_submission_index =
|
||||
paint_context_.submission_tracker.GetCurrentSubmission();
|
||||
const uint64_t current_paint_submission_index =
|
||||
paint_context_.completion_timeline.GetUpcomingSubmission();
|
||||
uint64_t paint_submission_count = uint64_t(paint_context_.submissions.size());
|
||||
if (current_paint_submission_index >= paint_submission_count) {
|
||||
paint_context_.submission_tracker.AwaitSubmissionCompletion(
|
||||
current_paint_submission_index - paint_submission_count);
|
||||
}
|
||||
paint_context_.completion_timeline
|
||||
.AwaitMaxSubmissionsPendingAndUpdateCompleted(paint_submission_count);
|
||||
const PaintContext::Submission& paint_submission =
|
||||
*paint_context_.submissions[current_paint_submission_index %
|
||||
paint_submission_count];
|
||||
@@ -1555,7 +1522,7 @@ Presenter::PaintResult VulkanPresenter::PaintAndPresentImpl(
|
||||
}
|
||||
// Await the completion of the usage of the old guest output image and
|
||||
// its descriptors.
|
||||
paint_context_.submission_tracker.AwaitSubmissionCompletion(
|
||||
paint_context_.completion_timeline.AwaitSubmissionAndUpdateCompleted(
|
||||
paint_context_
|
||||
.guest_output_image_paint_refs
|
||||
[guest_output_image_paint_ref_new_index]
|
||||
@@ -1617,9 +1584,10 @@ Presenter::PaintResult VulkanPresenter::PaintAndPresentImpl(
|
||||
// Need to replace immediately as a new image with the requested
|
||||
// size is needed.
|
||||
if (intermediate_image_ptr_ref) {
|
||||
paint_context_.submission_tracker.AwaitSubmissionCompletion(
|
||||
paint_context_
|
||||
.guest_output_intermediate_image_last_submission);
|
||||
paint_context_.completion_timeline
|
||||
.AwaitSubmissionAndUpdateCompleted(
|
||||
paint_context_
|
||||
.guest_output_intermediate_image_last_submission);
|
||||
intermediate_image_ptr_ref.reset();
|
||||
util::DestroyAndNullHandle(
|
||||
dfn.vkDestroyFramebuffer, device,
|
||||
@@ -1696,7 +1664,7 @@ Presenter::PaintResult VulkanPresenter::PaintAndPresentImpl(
|
||||
} else {
|
||||
// Was previously needed, but not anymore - destroy when possible.
|
||||
if (intermediate_image_ptr_ref &&
|
||||
paint_context_.submission_tracker
|
||||
paint_context_.completion_timeline
|
||||
.UpdateAndGetCompletedSubmission() >=
|
||||
paint_context_
|
||||
.guest_output_intermediate_image_last_submission) {
|
||||
@@ -1731,7 +1699,7 @@ Presenter::PaintResult VulkanPresenter::PaintAndPresentImpl(
|
||||
if (swapchain_effect_pipeline.swapchain_pipeline != VK_NULL_HANDLE &&
|
||||
swapchain_effect_pipeline.swapchain_format !=
|
||||
paint_context_.swapchain_render_pass_format) {
|
||||
paint_context_.submission_tracker.AwaitSubmissionCompletion(
|
||||
paint_context_.completion_timeline.AwaitSubmissionAndUpdateCompleted(
|
||||
paint_context_.guest_output_image_paint_last_submission);
|
||||
util::DestroyAndNullHandle(
|
||||
dfn.vkDestroyPipeline, device,
|
||||
@@ -1959,10 +1927,10 @@ Presenter::PaintResult VulkanPresenter::PaintAndPresentImpl(
|
||||
|
||||
// Release main target guest output image references that aren't needed
|
||||
// anymore (this is done after various potential guest-output-related main
|
||||
// target submission tracker waits so the completed submission value is the
|
||||
// target completion timeline waits so the completed submission index is the
|
||||
// most actual).
|
||||
uint64_t completed_paint_submission =
|
||||
paint_context_.submission_tracker.UpdateAndGetCompletedSubmission();
|
||||
paint_context_.completion_timeline.UpdateAndGetCompletedSubmission();
|
||||
for (std::pair<uint64_t, std::shared_ptr<GuestOutputImage>>&
|
||||
guest_output_image_paint_ref :
|
||||
paint_context_.guest_output_image_paint_refs) {
|
||||
@@ -2001,8 +1969,8 @@ Presenter::PaintResult VulkanPresenter::PaintAndPresentImpl(
|
||||
VulkanUIDrawContext ui_draw_context(
|
||||
*this, paint_context_.swapchain_extent.width,
|
||||
paint_context_.swapchain_extent.height, draw_command_buffer,
|
||||
ui_submission_tracker_.GetCurrentSubmission(),
|
||||
ui_submission_tracker_.UpdateAndGetCompletedSubmission(),
|
||||
ui_completion_timeline_.GetUpcomingSubmission(),
|
||||
ui_completion_timeline_.UpdateAndGetCompletedSubmission(),
|
||||
paint_context_.swapchain_render_pass,
|
||||
paint_context_.swapchain_render_pass_format);
|
||||
ExecuteUIDrawersFromUIThread(ui_draw_context);
|
||||
@@ -2045,48 +2013,35 @@ Presenter::PaintResult VulkanPresenter::PaintAndPresentImpl(
|
||||
submit_info.pCommandBuffers = command_buffers;
|
||||
submit_info.signalSemaphoreCount = 1;
|
||||
submit_info.pSignalSemaphores = &present_semaphore;
|
||||
{
|
||||
VulkanSubmissionTracker::FenceAcquisition fence_acqusition(
|
||||
paint_context_.submission_tracker.AcquireFenceToAdvanceSubmission());
|
||||
// Also update the submission tracker giving submission indices to UI draw
|
||||
// callbacks if submission is successful.
|
||||
VulkanSubmissionTracker::FenceAcquisition ui_fence_acquisition;
|
||||
if (execute_ui_drawers) {
|
||||
ui_fence_acquisition =
|
||||
ui_submission_tracker_.AcquireFenceToAdvanceSubmission();
|
||||
const VkResult submit_result =
|
||||
paint_context_.completion_timeline.AcquireFenceAndSubmit(
|
||||
vulkan_device_->queue_family_graphics_compute(), 0, 1, &submit_info);
|
||||
if (submit_result != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"VulkanPresenter: Failed to submit the presentation command buffer: {}",
|
||||
vk::to_string(vk::Result(submit_result)));
|
||||
if (ui_setup_command_buffer_index != SIZE_MAX) {
|
||||
// If failed to submit, make the UI setup command buffer available for
|
||||
// immediate reuse, as the completed submission index won't be updated to
|
||||
// the current index, and failing submissions with setup command buffer
|
||||
// over and over will result in never reusing the setup command buffers.
|
||||
paint_context_.ui_setup_command_buffers[ui_setup_command_buffer_index]
|
||||
.last_usage_submission_index = 0;
|
||||
}
|
||||
VkResult submit_result;
|
||||
{
|
||||
const VulkanDevice::Queue::Acquisition queue_acquisition =
|
||||
vulkan_device_->AcquireQueue(
|
||||
vulkan_device_->queue_family_graphics_compute(), 0);
|
||||
submit_result = dfn.vkQueueSubmit(queue_acquisition.queue(), 1,
|
||||
&submit_info, fence_acqusition.fence());
|
||||
if (ui_fence_acquisition.fence() != VK_NULL_HANDLE &&
|
||||
submit_result == VK_SUCCESS) {
|
||||
if (dfn.vkQueueSubmit(queue_acquisition.queue(), 0, nullptr,
|
||||
ui_fence_acquisition.fence()) != VK_SUCCESS) {
|
||||
ui_fence_acquisition.SubmissionSucceededSignalFailed();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (submit_result != VK_SUCCESS) {
|
||||
XELOGE("VulkanPresenter: Failed to submit command buffers");
|
||||
fence_acqusition.SubmissionFailedOrDropped();
|
||||
ui_fence_acquisition.SubmissionFailedOrDropped();
|
||||
if (ui_setup_command_buffer_index != SIZE_MAX) {
|
||||
// If failed to submit, make the UI setup command buffer available for
|
||||
// immediate reuse, as the completed submission index won't be updated
|
||||
// to the current index, and failing submissions with setup command
|
||||
// buffer over and over will result in never reusing the setup command
|
||||
// buffers.
|
||||
paint_context_.ui_setup_command_buffers[ui_setup_command_buffer_index]
|
||||
.last_usage_submission_index = 0;
|
||||
}
|
||||
// The image is in an acquired state - but now, it will be in it forever.
|
||||
// To avoid that, recreate the swapchain - don't return just
|
||||
// kNotPresented.
|
||||
return PaintResult::kNotPresentedConnectionOutdated;
|
||||
// The image is in an acquired state - but now, it will be in it forever.
|
||||
// To avoid that, recreate the swapchain - don't return just kNotPresented.
|
||||
return PaintResult::kNotPresentedConnectionOutdated;
|
||||
}
|
||||
if (execute_ui_drawers) {
|
||||
// Also update the completion timeline providing submission indices to UI
|
||||
// draw callbacks if submission is successful.
|
||||
const VkResult ui_signal_submit_result =
|
||||
ui_completion_timeline_.AcquireFenceAndSubmit(
|
||||
vulkan_device_->queue_family_graphics_compute(), 0, 0, nullptr);
|
||||
if (ui_signal_submit_result != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"VulkanPresenter: Failed to submit the UI drawing fence signal: {}",
|
||||
vk::to_string(vk::Result(ui_signal_submit_result)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user