2019-02-22 05:27:25 +01:00
|
|
|
// Copyright 2019 yuzu Emulator Project
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
2020-02-21 00:35:53 +01:00
|
|
|
#include <memory>
|
|
|
|
#include <mutex>
|
|
|
|
#include <optional>
|
|
|
|
#include <thread>
|
|
|
|
#include <utility>
|
|
|
|
|
2019-12-13 06:24:48 +01:00
|
|
|
#include "common/microprofile.h"
|
2020-04-05 15:48:53 +02:00
|
|
|
#include "common/thread.h"
|
2020-09-10 08:43:30 +02:00
|
|
|
#include "video_core/renderer_vulkan/vk_command_pool.h"
|
|
|
|
#include "video_core/renderer_vulkan/vk_master_semaphore.h"
|
2020-02-11 22:59:44 +01:00
|
|
|
#include "video_core/renderer_vulkan/vk_query_cache.h"
|
2019-02-22 05:27:25 +01:00
|
|
|
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
2020-02-21 00:35:53 +01:00
|
|
|
#include "video_core/renderer_vulkan/vk_state_tracker.h"
|
2020-12-30 06:25:23 +01:00
|
|
|
#include "video_core/renderer_vulkan/vk_texture_cache.h"
|
2020-12-26 05:19:46 +01:00
|
|
|
#include "video_core/vulkan_common/vulkan_device.h"
|
2020-12-25 01:30:11 +01:00
|
|
|
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
2019-02-22 05:27:25 +01:00
|
|
|
|
|
|
|
namespace Vulkan {
|
|
|
|
|
2019-12-13 06:24:48 +01:00
|
|
|
MICROPROFILE_DECLARE(Vulkan_WaitForWorker);
|
|
|
|
|
2020-03-27 05:33:21 +01:00
|
|
|
void VKScheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf) {
|
2019-12-13 06:24:48 +01:00
|
|
|
auto command = first;
|
|
|
|
while (command != nullptr) {
|
|
|
|
auto next = command->GetNext();
|
2020-03-27 05:33:21 +01:00
|
|
|
command->Execute(cmdbuf);
|
2019-12-13 06:24:48 +01:00
|
|
|
command->~Command();
|
|
|
|
command = next;
|
|
|
|
}
|
2021-04-27 03:11:31 +02:00
|
|
|
submit = false;
|
2019-12-13 06:24:48 +01:00
|
|
|
command_offset = 0;
|
|
|
|
first = nullptr;
|
|
|
|
last = nullptr;
|
|
|
|
}
|
|
|
|
|
2020-12-26 05:10:53 +01:00
|
|
|
VKScheduler::VKScheduler(const Device& device_, StateTracker& state_tracker_)
|
2020-09-10 08:43:30 +02:00
|
|
|
: device{device_}, state_tracker{state_tracker_},
|
|
|
|
master_semaphore{std::make_unique<MasterSemaphore>(device)},
|
|
|
|
command_pool{std::make_unique<CommandPool>(*master_semaphore, device)} {
|
2019-12-13 06:24:48 +01:00
|
|
|
AcquireNewChunk();
|
2021-04-27 03:11:31 +02:00
|
|
|
AllocateWorkerCommandBuffer();
|
2021-09-16 02:10:25 +02:00
|
|
|
worker_thread = std::jthread([this](std::stop_token token) { WorkerThread(token); });
|
2019-02-22 05:27:25 +01:00
|
|
|
}
|
|
|
|
|
2021-09-16 02:10:25 +02:00
|
|
|
VKScheduler::~VKScheduler() = default;
|
2019-02-22 05:27:25 +01:00
|
|
|
|
2021-09-02 07:26:18 +02:00
|
|
|
void VKScheduler::Flush(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
|
|
|
|
SubmitExecution(signal_semaphore, wait_semaphore);
|
2019-02-22 05:27:25 +01:00
|
|
|
AllocateNewContext();
|
|
|
|
}
|
|
|
|
|
2021-09-02 07:26:18 +02:00
|
|
|
void VKScheduler::Finish(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
|
2020-09-10 08:43:30 +02:00
|
|
|
const u64 presubmit_tick = CurrentTick();
|
2021-09-02 07:26:18 +02:00
|
|
|
SubmitExecution(signal_semaphore, wait_semaphore);
|
2021-04-27 03:11:31 +02:00
|
|
|
WaitWorker();
|
2020-09-10 08:43:30 +02:00
|
|
|
Wait(presubmit_tick);
|
2019-02-22 05:27:25 +01:00
|
|
|
AllocateNewContext();
|
|
|
|
}
|
|
|
|
|
2019-12-13 06:24:48 +01:00
|
|
|
void VKScheduler::WaitWorker() {
|
|
|
|
MICROPROFILE_SCOPE(Vulkan_WaitForWorker);
|
|
|
|
DispatchWork();
|
|
|
|
|
2021-05-07 11:26:12 +02:00
|
|
|
std::unique_lock lock{work_mutex};
|
|
|
|
wait_cv.wait(lock, [this] { return work_queue.empty(); });
|
2019-12-13 06:24:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void VKScheduler::DispatchWork() {
|
|
|
|
if (chunk->Empty()) {
|
|
|
|
return;
|
|
|
|
}
|
2021-05-07 11:26:12 +02:00
|
|
|
{
|
|
|
|
std::lock_guard lock{work_mutex};
|
|
|
|
work_queue.push(std::move(chunk));
|
|
|
|
}
|
|
|
|
work_cv.notify_one();
|
2019-12-13 06:24:48 +01:00
|
|
|
AcquireNewChunk();
|
|
|
|
}
|
|
|
|
|
2020-12-30 06:25:23 +01:00
|
|
|
void VKScheduler::RequestRenderpass(const Framebuffer* framebuffer) {
|
|
|
|
const VkRenderPass renderpass = framebuffer->RenderPass();
|
|
|
|
const VkFramebuffer framebuffer_handle = framebuffer->Handle();
|
|
|
|
const VkExtent2D render_area = framebuffer->RenderArea();
|
|
|
|
if (renderpass == state.renderpass && framebuffer_handle == state.framebuffer &&
|
2020-03-27 05:33:21 +01:00
|
|
|
render_area.width == state.render_area.width &&
|
|
|
|
render_area.height == state.render_area.height) {
|
2019-12-13 06:24:48 +01:00
|
|
|
return;
|
|
|
|
}
|
2020-12-30 06:25:23 +01:00
|
|
|
EndRenderPass();
|
2020-03-27 05:33:21 +01:00
|
|
|
state.renderpass = renderpass;
|
2020-12-30 06:25:23 +01:00
|
|
|
state.framebuffer = framebuffer_handle;
|
2020-03-27 05:33:21 +01:00
|
|
|
state.render_area = render_area;
|
|
|
|
|
2020-12-30 06:25:23 +01:00
|
|
|
Record([renderpass, framebuffer_handle, render_area](vk::CommandBuffer cmdbuf) {
|
|
|
|
const VkRenderPassBeginInfo renderpass_bi{
|
|
|
|
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
|
|
|
.pNext = nullptr,
|
|
|
|
.renderPass = renderpass,
|
|
|
|
.framebuffer = framebuffer_handle,
|
|
|
|
.renderArea =
|
|
|
|
{
|
|
|
|
.offset = {.x = 0, .y = 0},
|
|
|
|
.extent = render_area,
|
|
|
|
},
|
|
|
|
.clearValueCount = 0,
|
|
|
|
.pClearValues = nullptr,
|
|
|
|
};
|
2020-03-27 05:33:21 +01:00
|
|
|
cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE);
|
2019-12-13 06:24:48 +01:00
|
|
|
});
|
2020-12-30 06:25:23 +01:00
|
|
|
num_renderpass_images = framebuffer->NumImages();
|
|
|
|
renderpass_images = framebuffer->Images();
|
|
|
|
renderpass_image_ranges = framebuffer->ImageRanges();
|
2019-12-13 06:24:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void VKScheduler::RequestOutsideRenderPassOperationContext() {
|
|
|
|
EndRenderPass();
|
|
|
|
}
|
|
|
|
|
2021-04-01 06:36:22 +02:00
|
|
|
bool VKScheduler::UpdateGraphicsPipeline(GraphicsPipeline* pipeline) {
|
2019-12-13 06:24:48 +01:00
|
|
|
if (state.graphics_pipeline == pipeline) {
|
2021-04-01 06:36:22 +02:00
|
|
|
return false;
|
2019-12-13 06:24:48 +01:00
|
|
|
}
|
|
|
|
state.graphics_pipeline = pipeline;
|
2021-04-01 06:36:22 +02:00
|
|
|
return true;
|
2019-12-13 06:24:48 +01:00
|
|
|
}
|
|
|
|
|
2021-09-16 02:10:25 +02:00
|
|
|
void VKScheduler::WorkerThread(std::stop_token stop_token) {
|
2021-04-01 06:36:22 +02:00
|
|
|
Common::SetCurrentThreadName("yuzu:VulkanWorker");
|
2019-12-13 06:24:48 +01:00
|
|
|
do {
|
2021-05-07 11:26:12 +02:00
|
|
|
if (work_queue.empty()) {
|
|
|
|
wait_cv.notify_all();
|
2019-12-13 06:24:48 +01:00
|
|
|
}
|
2021-05-07 11:26:12 +02:00
|
|
|
std::unique_ptr<CommandChunk> work;
|
|
|
|
{
|
|
|
|
std::unique_lock lock{work_mutex};
|
2021-09-16 02:10:25 +02:00
|
|
|
work_cv.wait(lock, stop_token, [this] { return !work_queue.empty(); });
|
|
|
|
if (stop_token.stop_requested()) {
|
2021-05-07 11:26:12 +02:00
|
|
|
continue;
|
2021-04-27 03:11:31 +02:00
|
|
|
}
|
2021-05-07 11:26:12 +02:00
|
|
|
work = std::move(work_queue.front());
|
|
|
|
work_queue.pop();
|
|
|
|
}
|
|
|
|
const bool has_submit = work->HasSubmit();
|
|
|
|
work->ExecuteAll(current_cmdbuf);
|
|
|
|
if (has_submit) {
|
|
|
|
AllocateWorkerCommandBuffer();
|
2021-04-27 03:11:31 +02:00
|
|
|
}
|
2021-05-07 11:26:12 +02:00
|
|
|
std::lock_guard reserve_lock{reserve_mutex};
|
|
|
|
chunk_reserve.push_back(std::move(work));
|
2021-09-16 02:10:25 +02:00
|
|
|
} while (!stop_token.stop_requested());
|
2019-12-13 06:24:48 +01:00
|
|
|
}
|
|
|
|
|
2021-04-27 03:11:31 +02:00
|
|
|
void VKScheduler::AllocateWorkerCommandBuffer() {
|
|
|
|
current_cmdbuf = vk::CommandBuffer(command_pool->Commit(), device.GetDispatchLoader());
|
|
|
|
current_cmdbuf.Begin({
|
|
|
|
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
|
|
|
.pNext = nullptr,
|
|
|
|
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
|
|
|
|
.pInheritanceInfo = nullptr,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-09-02 07:26:18 +02:00
|
|
|
void VKScheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_semaphore) {
|
2019-12-13 06:24:48 +01:00
|
|
|
EndPendingOperations();
|
|
|
|
InvalidateState();
|
2020-09-10 08:43:30 +02:00
|
|
|
|
2021-05-07 05:29:08 +02:00
|
|
|
const u64 signal_value = master_semaphore->NextTick();
|
2021-09-02 07:26:18 +02:00
|
|
|
Record([signal_semaphore, wait_semaphore, signal_value, this](vk::CommandBuffer cmdbuf) {
|
2021-04-27 03:11:31 +02:00
|
|
|
cmdbuf.End();
|
|
|
|
const VkSemaphore timeline_semaphore = master_semaphore->Handle();
|
2021-09-02 07:26:18 +02:00
|
|
|
|
|
|
|
const u32 num_signal_semaphores = signal_semaphore ? 2U : 1U;
|
2021-04-27 03:11:31 +02:00
|
|
|
const std::array signal_values{signal_value, u64(0)};
|
2021-09-02 07:26:18 +02:00
|
|
|
const std::array signal_semaphores{timeline_semaphore, signal_semaphore};
|
|
|
|
|
|
|
|
const u32 num_wait_semaphores = wait_semaphore ? 2U : 1U;
|
|
|
|
const std::array wait_values{signal_value - 1, u64(1)};
|
|
|
|
const std::array wait_semaphores{timeline_semaphore, wait_semaphore};
|
|
|
|
static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{
|
|
|
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
|
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
|
|
|
};
|
2021-04-27 03:11:31 +02:00
|
|
|
|
|
|
|
const VkTimelineSemaphoreSubmitInfoKHR timeline_si{
|
|
|
|
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR,
|
|
|
|
.pNext = nullptr,
|
2021-09-02 07:26:18 +02:00
|
|
|
.waitSemaphoreValueCount = num_wait_semaphores,
|
|
|
|
.pWaitSemaphoreValues = wait_values.data(),
|
2021-04-27 03:11:31 +02:00
|
|
|
.signalSemaphoreValueCount = num_signal_semaphores,
|
|
|
|
.pSignalSemaphoreValues = signal_values.data(),
|
|
|
|
};
|
|
|
|
const VkSubmitInfo submit_info{
|
|
|
|
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
|
|
|
.pNext = &timeline_si,
|
2021-09-02 07:26:18 +02:00
|
|
|
.waitSemaphoreCount = num_wait_semaphores,
|
|
|
|
.pWaitSemaphores = wait_semaphores.data(),
|
|
|
|
.pWaitDstStageMask = wait_stage_masks.data(),
|
2021-04-27 03:11:31 +02:00
|
|
|
.commandBufferCount = 1,
|
|
|
|
.pCommandBuffers = cmdbuf.address(),
|
|
|
|
.signalSemaphoreCount = num_signal_semaphores,
|
|
|
|
.pSignalSemaphores = signal_semaphores.data(),
|
|
|
|
};
|
|
|
|
switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info)) {
|
|
|
|
case VK_SUCCESS:
|
|
|
|
break;
|
|
|
|
case VK_ERROR_DEVICE_LOST:
|
|
|
|
device.ReportLoss();
|
|
|
|
[[fallthrough]];
|
|
|
|
default:
|
|
|
|
vk::Check(result);
|
|
|
|
}
|
2020-07-17 01:10:49 +02:00
|
|
|
});
|
2021-04-27 03:11:31 +02:00
|
|
|
chunk->MarkSubmit();
|
|
|
|
DispatchWork();
|
|
|
|
}
|
2020-03-27 05:33:21 +01:00
|
|
|
|
2021-04-27 03:11:31 +02:00
|
|
|
void VKScheduler::AllocateNewContext() {
|
2020-02-11 22:59:44 +01:00
|
|
|
// Enable counters once again. These are disabled when a command buffer is finished.
|
|
|
|
if (query_cache) {
|
|
|
|
query_cache->UpdateCounters();
|
|
|
|
}
|
2019-12-13 06:24:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void VKScheduler::InvalidateState() {
|
|
|
|
state.graphics_pipeline = nullptr;
|
2020-02-21 00:35:53 +01:00
|
|
|
state_tracker.InvalidateCommandBufferState();
|
2019-12-13 06:24:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void VKScheduler::EndPendingOperations() {
|
2020-02-11 22:59:44 +01:00
|
|
|
query_cache->DisableStreams();
|
2019-12-13 06:24:48 +01:00
|
|
|
EndRenderPass();
|
|
|
|
}
|
|
|
|
|
|
|
|
void VKScheduler::EndRenderPass() {
|
|
|
|
if (!state.renderpass) {
|
|
|
|
return;
|
|
|
|
}
|
2020-12-30 06:25:23 +01:00
|
|
|
Record([num_images = num_renderpass_images, images = renderpass_images,
|
|
|
|
ranges = renderpass_image_ranges](vk::CommandBuffer cmdbuf) {
|
|
|
|
std::array<VkImageMemoryBarrier, 9> barriers;
|
|
|
|
for (size_t i = 0; i < num_images; ++i) {
|
|
|
|
barriers[i] = VkImageMemoryBarrier{
|
|
|
|
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
|
|
|
.pNext = nullptr,
|
|
|
|
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
|
|
|
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
|
|
|
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT |
|
|
|
|
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
|
|
|
|
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT |
|
|
|
|
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT |
|
|
|
|
VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
|
|
|
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
|
|
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
|
|
|
|
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
|
|
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
|
|
|
.image = images[i],
|
|
|
|
.subresourceRange = ranges[i],
|
|
|
|
};
|
|
|
|
}
|
|
|
|
cmdbuf.EndRenderPass();
|
|
|
|
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
|
|
|
|
VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
|
|
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
2021-01-17 00:48:58 +01:00
|
|
|
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, nullptr, nullptr,
|
2020-12-30 06:25:23 +01:00
|
|
|
vk::Span(barriers.data(), num_images));
|
|
|
|
});
|
2020-03-27 05:33:21 +01:00
|
|
|
state.renderpass = nullptr;
|
2020-12-30 06:25:23 +01:00
|
|
|
num_renderpass_images = 0;
|
2019-12-13 06:24:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void VKScheduler::AcquireNewChunk() {
|
2021-05-07 11:26:12 +02:00
|
|
|
std::lock_guard lock{reserve_mutex};
|
|
|
|
if (chunk_reserve.empty()) {
|
2019-12-13 06:24:48 +01:00
|
|
|
chunk = std::make_unique<CommandChunk>();
|
|
|
|
return;
|
|
|
|
}
|
2021-05-07 11:26:12 +02:00
|
|
|
chunk = std::move(chunk_reserve.back());
|
|
|
|
chunk_reserve.pop_back();
|
2019-02-22 05:27:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Vulkan
|