mirror of
https://github.com/KytyPS5/KytyPS5.git
synced 2026-08-03 11:23:49 +00:00
321 lines
11 KiB
C++
321 lines
11 KiB
C++
#include "common/assert.h"
|
|
#include "common/common.h"
|
|
#include "common/emulatorConfig.h"
|
|
#include "common/logging/log.h"
|
|
#include "common/profiler.h"
|
|
#include "common/threads.h"
|
|
#include "graphics/host_gpu/graphicContext.h"
|
|
#include "graphics/host_gpu/renderer/colorRenderTarget.h"
|
|
#include "graphics/host_gpu/renderer/debug.h"
|
|
#include "graphics/host_gpu/renderer/depthRenderTarget.h"
|
|
#include "graphics/host_gpu/renderer/image/imageView.h"
|
|
#include "graphics/host_gpu/renderer/pipeline/descriptorCache.h"
|
|
#include "graphics/host_gpu/renderer/render.h"
|
|
#include "graphics/host_gpu/renderer/renderContext.h"
|
|
#include "graphics/host_gpu/vma.h"
|
|
#include "graphics/host_gpu/vulkanCommon.h"
|
|
|
|
#include <algorithm>
|
|
#include <bit>
|
|
#include <cstring>
|
|
#include <memory>
|
|
namespace Libs::Graphics {
|
|
|
|
FenceResourceRetainer::~FenceResourceRetainer() {
|
|
if (!m_resources.empty()) {
|
|
EXIT("fence resource retainer destroyed before release\n");
|
|
}
|
|
}
|
|
|
|
void FenceResourceRetainer::Retain(std::shared_ptr<void> resource) {
|
|
if (resource == nullptr) {
|
|
EXIT("cannot retain a null fence resource\n");
|
|
}
|
|
if (std::ranges::none_of(m_resources, [&resource](const auto& retained) {
|
|
return retained.get() == resource.get();
|
|
})) {
|
|
m_resources.push_back(std::move(resource));
|
|
}
|
|
}
|
|
|
|
void FenceResourceRetainer::ReleaseAfterFence() noexcept {
|
|
m_resources.clear();
|
|
}
|
|
|
|
CommandBuffer::CommandBuffer(CommandScheduler& scheduler)
|
|
: m_context(scheduler.Context()), m_scheduler(scheduler), m_graphics(scheduler.Graphics()),
|
|
m_slot(scheduler.AllocateCommandBuffer()) {}
|
|
|
|
CommandBuffer::~CommandBuffer() {
|
|
Release();
|
|
}
|
|
|
|
bool CommandBuffer::IsInvalid() const {
|
|
return m_slot == nullptr;
|
|
}
|
|
|
|
vk::CommandBuffer CommandBuffer::Handle() const {
|
|
EXIT_IF(IsInvalid());
|
|
|
|
const auto handle = m_slot->buffer;
|
|
EXIT_IF(handle == nullptr);
|
|
return handle;
|
|
}
|
|
|
|
void CommandBuffer::Release() {
|
|
EXIT_IF(IsInvalid());
|
|
|
|
Common::LockGuard lock(*m_slot->pool_mutex);
|
|
|
|
WaitForFence();
|
|
|
|
m_slot->busy = false;
|
|
m_slot->Reset();
|
|
ReleaseResourcesAfterFence();
|
|
m_slot = nullptr;
|
|
|
|
EXIT_NOT_IMPLEMENTED(!IsInvalid());
|
|
}
|
|
|
|
void CommandBuffer::RetireBufferAfterFence(std::unique_ptr<VulkanBuffer> buffer) {
|
|
if (IsInvalid() || m_execute || buffer == nullptr || buffer->buffer == nullptr) {
|
|
EXIT("cannot retire a buffer on an invalid or submitted command buffer\n");
|
|
}
|
|
m_retired_buffers.push_back(std::move(buffer));
|
|
}
|
|
|
|
void CommandBuffer::RetainResourceUntilFence(std::shared_ptr<void> resource) {
|
|
if (IsInvalid() || m_execute) {
|
|
EXIT("cannot retain a resource on an invalid or submitted command buffer\n");
|
|
}
|
|
m_fence_resources.Retain(std::move(resource));
|
|
}
|
|
|
|
void CommandBuffer::RecycleDescriptorAfterFence(VulkanDescriptorSet& set) {
|
|
m_descriptor_sets_after_fence.push_back(&set);
|
|
}
|
|
|
|
void CommandBuffer::RecycleDescriptorsAfterFence() {
|
|
for (auto* set: m_descriptor_sets_after_fence) {
|
|
m_context.GetDescriptorCache().Recycle(*set);
|
|
}
|
|
m_descriptor_sets_after_fence.clear();
|
|
}
|
|
|
|
void CommandBuffer::Begin() const {
|
|
EXIT_IF(m_rendering);
|
|
auto buffer = Handle();
|
|
|
|
vk::CommandBufferBeginInfo begin_info {};
|
|
begin_info.sType = vk::StructureType::eCommandBufferBeginInfo;
|
|
begin_info.pNext = nullptr;
|
|
begin_info.flags = {};
|
|
begin_info.pInheritanceInfo = nullptr;
|
|
|
|
auto result = buffer.begin(&begin_info);
|
|
|
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
|
}
|
|
|
|
void CommandBuffer::End() const {
|
|
EndRendering();
|
|
auto buffer = Handle();
|
|
|
|
auto result = buffer.end();
|
|
|
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
|
}
|
|
|
|
void CommandBuffer::SetDebugInfo(uint32_t op, uint64_t submit_id, uint32_t arg0, uint32_t arg1,
|
|
uint32_t arg2, uint32_t arg3, uint64_t arg4) {
|
|
m_debug_op = op;
|
|
m_debug_submit_id = submit_id;
|
|
m_debug_arg0 = arg0;
|
|
m_debug_arg1 = arg1;
|
|
m_debug_arg2 = arg2;
|
|
m_debug_arg3 = arg3;
|
|
m_debug_arg4 = arg4;
|
|
}
|
|
|
|
void CommandBuffer::Execute(const SubmitInfo& submit) {
|
|
EXIT_IF(IsInvalid());
|
|
EXIT_IF(m_execute);
|
|
EXIT_IF(submit.num_wait_semaphores > SubmitInfo::MaxSemaphores ||
|
|
submit.num_signal_semaphores > SubmitInfo::MaxSemaphores);
|
|
|
|
auto buffer = Handle();
|
|
auto fence = m_slot->fence;
|
|
|
|
vk::TimelineSemaphoreSubmitInfo timeline_info {};
|
|
timeline_info.sType = vk::StructureType::eTimelineSemaphoreSubmitInfo;
|
|
timeline_info.waitSemaphoreValueCount = submit.num_wait_semaphores;
|
|
timeline_info.pWaitSemaphoreValues = submit.wait_ticks.data();
|
|
timeline_info.signalSemaphoreValueCount = submit.num_signal_semaphores;
|
|
timeline_info.pSignalSemaphoreValues = submit.signal_ticks.data();
|
|
|
|
vk::SubmitInfo submit_info {};
|
|
submit_info.sType = vk::StructureType::eSubmitInfo;
|
|
submit_info.pNext = &timeline_info;
|
|
submit_info.waitSemaphoreCount = submit.num_wait_semaphores;
|
|
submit_info.pWaitSemaphores = submit.wait_semaphores.data();
|
|
submit_info.pWaitDstStageMask = submit.wait_stages.data();
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pCommandBuffers = &buffer;
|
|
submit_info.signalSemaphoreCount = submit.num_signal_semaphores;
|
|
submit_info.pSignalSemaphores = submit.signal_semaphores.data();
|
|
|
|
auto& graphics = m_graphics;
|
|
EXIT_IF(graphics.queue == nullptr);
|
|
|
|
auto result = graphics.device.resetFences(1, &fence);
|
|
if (result != vk::Result::eSuccess) {
|
|
LOGF("vkResetFences failed before submit: %s (%d)\n", VulkanToString(result).c_str(),
|
|
static_cast<int>(result));
|
|
}
|
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
|
|
|
if (Config::GraphicsDebugDumpEnabled()) {
|
|
LOGF("vkQueueSubmit begin: slot=%u waits=%u signals=%u debug_op=%u debug_submit=%" PRIu64
|
|
" args=%u,%u,%u,%u,0x%016" PRIx64 "\n",
|
|
m_slot->id, submit.num_wait_semaphores, submit.num_signal_semaphores, m_debug_op,
|
|
m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3,
|
|
m_debug_arg4);
|
|
}
|
|
|
|
{
|
|
Common::LockGuard lock(graphics.queue_mutex);
|
|
m_submit_seq = m_scheduler.NextSubmitSequence();
|
|
result = graphics.queue.submit(1, &submit_info, fence);
|
|
}
|
|
|
|
m_execute = true;
|
|
m_fence_waited = false;
|
|
|
|
if (result != vk::Result::eSuccess) {
|
|
LOGF("vkQueueSubmit failed: %s (%d), slot=%u submit_seq=%" PRIu64
|
|
" debug_op=%u debug_submit=%" PRIu64 " args=%u,%u,%u,%u,0x%016" PRIx64 "\n",
|
|
VulkanToString(result).c_str(), static_cast<int>(result), m_slot->id, m_submit_seq,
|
|
m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3,
|
|
m_debug_arg4);
|
|
}
|
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
|
}
|
|
|
|
void CommandBuffer::WaitForFence() {
|
|
FinalizeFence(false);
|
|
}
|
|
|
|
void CommandBuffer::WaitForFenceOnly() {
|
|
EXIT_IF(IsInvalid());
|
|
if (!m_execute || m_fence_waited) {
|
|
return;
|
|
}
|
|
auto device = m_graphics.device;
|
|
auto result = device.waitForFences(1, &m_slot->fence, VK_TRUE, UINT64_MAX);
|
|
if (result != vk::Result::eSuccess) {
|
|
LOGF("vkWaitForFences failed: %s (%d), slot=%u submit_seq=%" PRIu64
|
|
" debug_op=%u debug_submit=%" PRIu64 " args=%u,%u,%u,%u,0x%016" PRIx64 "\n",
|
|
VulkanToString(result).c_str(), static_cast<int>(result), m_slot->id, m_submit_seq,
|
|
m_debug_op, m_debug_submit_id, m_debug_arg0, m_debug_arg1, m_debug_arg2, m_debug_arg3,
|
|
m_debug_arg4);
|
|
}
|
|
EXIT_NOT_IMPLEMENTED(result != vk::Result::eSuccess);
|
|
m_fence_waited = true;
|
|
}
|
|
|
|
void CommandBuffer::WaitForFenceAndReset() {
|
|
FinalizeFence(true);
|
|
}
|
|
|
|
void CommandBuffer::FinalizeFence(bool reset_recording) {
|
|
const bool was_executed = m_execute;
|
|
WaitForFenceOnly();
|
|
if (was_executed) {
|
|
m_execute = false;
|
|
m_fence_waited = false;
|
|
if (reset_recording) {
|
|
Common::LockGuard lock(*m_slot->pool_mutex);
|
|
m_slot->Reset();
|
|
}
|
|
}
|
|
if (was_executed) {
|
|
ReleaseResourcesAfterFence();
|
|
}
|
|
DeleteBuffersAfterFence();
|
|
}
|
|
|
|
void CommandBuffer::ReleaseResourcesAfterFence() {
|
|
RecycleDescriptorsAfterFence();
|
|
m_fence_resources.ReleaseAfterFence();
|
|
}
|
|
|
|
void CommandBuffer::DeleteBuffersAfterFence() {
|
|
for (const auto& buffer: m_retired_buffers) {
|
|
m_graphics.DeleteBuffer(*buffer);
|
|
}
|
|
m_retired_buffers.clear();
|
|
}
|
|
|
|
void CommandBuffer::BeginRendering(const RenderState& state) const {
|
|
EXIT_IF(state.width == 0 || state.height == 0 || state.num_layers == 0 ||
|
|
state.num_color_attachments > RENDER_COLOR_ATTACHMENTS_MAX);
|
|
if (m_rendering && m_render_state == state) {
|
|
return;
|
|
}
|
|
EndRendering();
|
|
|
|
std::array<vk::RenderingAttachmentInfo, RENDER_COLOR_ATTACHMENTS_MAX> colors {};
|
|
for (uint32_t i = 0; i < state.num_color_attachments; i++) {
|
|
const auto& attachment = state.color_attachments[i];
|
|
colors[i].sType = vk::StructureType::eRenderingAttachmentInfo;
|
|
colors[i].imageView = attachment.image_view;
|
|
colors[i].imageLayout = attachment.image_layout;
|
|
colors[i].loadOp =
|
|
attachment.is_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad;
|
|
colors[i].storeOp = vk::AttachmentStoreOp::eStore;
|
|
colors[i].clearValue.color.uint32 = attachment.clear_value;
|
|
}
|
|
|
|
const auto& depth_stencil = state.depth_stencil_attachment;
|
|
vk::RenderingAttachmentInfo depth {};
|
|
depth.sType = vk::StructureType::eRenderingAttachmentInfo;
|
|
depth.imageView = depth_stencil.image_view;
|
|
depth.imageLayout = depth_stencil.image_layout;
|
|
depth.loadOp =
|
|
depth_stencil.depth_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad;
|
|
depth.storeOp = vk::AttachmentStoreOp::eStore;
|
|
depth.clearValue.depthStencil.depth = std::bit_cast<float>(depth_stencil.clear_value[0]);
|
|
|
|
vk::RenderingAttachmentInfo stencil {};
|
|
stencil.sType = vk::StructureType::eRenderingAttachmentInfo;
|
|
stencil.imageView = depth_stencil.image_view;
|
|
stencil.imageLayout = depth_stencil.image_layout;
|
|
stencil.loadOp =
|
|
depth_stencil.stencil_clear ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad;
|
|
stencil.storeOp = vk::AttachmentStoreOp::eStore;
|
|
stencil.clearValue.depthStencil.stencil = depth_stencil.clear_value[1];
|
|
|
|
vk::RenderingInfo rendering {};
|
|
rendering.sType = vk::StructureType::eRenderingInfo;
|
|
rendering.renderArea.extent = {state.width, state.height};
|
|
rendering.layerCount = state.num_layers;
|
|
rendering.colorAttachmentCount = state.num_color_attachments;
|
|
rendering.pColorAttachments = colors.data();
|
|
rendering.pDepthAttachment = depth_stencil.has_depth ? &depth : nullptr;
|
|
rendering.pStencilAttachment = depth_stencil.has_stencil ? &stencil : nullptr;
|
|
Handle().beginRendering(rendering);
|
|
m_render_state = state;
|
|
m_rendering = true;
|
|
}
|
|
|
|
void CommandBuffer::EndRendering() const {
|
|
if (!m_rendering) {
|
|
return;
|
|
}
|
|
Handle().endRendering();
|
|
m_rendering = false;
|
|
m_render_state = {};
|
|
}
|
|
|
|
} // namespace Libs::Graphics
|