Merge commit 'f2fabfdf0' into canary_experimental

Sync changes from master up to before the recent GPU changes, those will be
merged later
This commit is contained in:
Herman S.
2026-02-17 15:46:27 +09:00
294 changed files with 165264 additions and 173899 deletions

View File

@@ -153,8 +153,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();
@@ -377,51 +377,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);
@@ -474,8 +447,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 =
@@ -498,7 +471,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;
}
}
@@ -541,7 +514,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;
}
@@ -872,8 +845,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) {
@@ -899,26 +873,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;
}
@@ -1272,7 +1241,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();
@@ -1380,13 +1349,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];
@@ -1550,7 +1517,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]
@@ -1612,9 +1579,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,
@@ -1691,7 +1659,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) {
@@ -1726,7 +1694,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,
@@ -1954,10 +1922,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) {
@@ -1996,8 +1964,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);
@@ -2040,48 +2008,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)));
}
}