VideoBackends:Vulkan: Add GPU timeline breadcrumbs for debugging
This commit is contained in:
parent
6aacbc4c35
commit
3aa2ef2fe1
|
@ -89,6 +89,7 @@ const Info<bool> GFX_BORDERLESS_FULLSCREEN{{System::GFX, "Settings", "Borderless
|
|||
false};
|
||||
const Info<bool> GFX_ENABLE_VALIDATION_LAYER{{System::GFX, "Settings", "EnableValidationLayer"},
|
||||
false};
|
||||
const Info<bool> GFX_ENABLE_BREADCRUMBS{{System::GFX, "Settings", "EnableBreadcrumbs"}, false};
|
||||
|
||||
const Info<bool> GFX_BACKEND_MULTITHREADING{{System::GFX, "Settings", "BackendMultithreading"},
|
||||
true};
|
||||
|
|
|
@ -82,6 +82,7 @@ extern const Info<bool> GFX_ENABLE_WIREFRAME;
|
|||
extern const Info<bool> GFX_DISABLE_FOG;
|
||||
extern const Info<bool> GFX_BORDERLESS_FULLSCREEN;
|
||||
extern const Info<bool> GFX_ENABLE_VALIDATION_LAYER;
|
||||
extern const Info<bool> GFX_ENABLE_BREADCRUMBS;
|
||||
extern const Info<bool> GFX_BACKEND_MULTITHREADING;
|
||||
extern const Info<int> GFX_COMMAND_BUFFER_EXECUTE_INTERVAL;
|
||||
extern const Info<bool> GFX_SHADER_CACHE;
|
||||
|
|
|
@ -1279,6 +1279,7 @@
|
|||
<ClCompile Include="VideoBackends\Vulkan\VKTexture.cpp" />
|
||||
<ClCompile Include="VideoBackends\Vulkan\VKVertexFormat.cpp" />
|
||||
<ClCompile Include="VideoBackends\Vulkan\VKVertexManager.cpp" />
|
||||
<ClCompile Include="VideoBackends\Vulkan\VKDebug.cpp" />
|
||||
<ClCompile Include="VideoBackends\Vulkan\VulkanContext.cpp" />
|
||||
<ClCompile Include="VideoBackends\Vulkan\VulkanLoader.cpp" />
|
||||
<ClCompile Include="VideoCommon\AbstractFramebuffer.cpp" />
|
||||
|
|
|
@ -31,6 +31,8 @@ add_library(videovulkan
|
|||
VKVertexFormat.h
|
||||
VKVertexManager.cpp
|
||||
VKVertexManager.h
|
||||
VKDebug.cpp
|
||||
VKDebug.h
|
||||
VulkanContext.cpp
|
||||
VulkanContext.h
|
||||
VulkanLoader.cpp
|
||||
|
|
|
@ -18,6 +18,10 @@ namespace Vulkan
|
|||
CommandBufferManager::CommandBufferManager(bool use_threaded_submission)
|
||||
: m_use_threaded_submission(use_threaded_submission)
|
||||
{
|
||||
for (auto& frame_resource : m_frame_resources)
|
||||
{
|
||||
frame_resource.debug = std::make_unique<VkDebug>(g_ActiveConfig.bEnableBreadcrumbs);
|
||||
}
|
||||
}
|
||||
|
||||
CommandBufferManager::~CommandBufferManager()
|
||||
|
@ -29,6 +33,11 @@ CommandBufferManager::~CommandBufferManager()
|
|||
m_submit_thread.Shutdown();
|
||||
}
|
||||
|
||||
for (auto& frame_resource : m_frame_resources)
|
||||
{
|
||||
frame_resource.debug.reset();
|
||||
}
|
||||
|
||||
DestroyCommandBuffers();
|
||||
}
|
||||
|
||||
|
@ -62,6 +71,7 @@ bool CommandBufferManager::CreateCommandBuffers()
|
|||
&resources.command_pool);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
PrintFaults();
|
||||
LOG_VULKAN_ERROR(res, "vkCreateCommandPool failed: ");
|
||||
return false;
|
||||
}
|
||||
|
@ -73,6 +83,7 @@ bool CommandBufferManager::CreateCommandBuffers()
|
|||
res = vkAllocateCommandBuffers(device, &buffer_info, resources.command_buffers.data());
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
PrintFaults();
|
||||
LOG_VULKAN_ERROR(res, "vkAllocateCommandBuffers failed: ");
|
||||
return false;
|
||||
}
|
||||
|
@ -83,6 +94,7 @@ bool CommandBufferManager::CreateCommandBuffers()
|
|||
res = vkCreateFence(device, &fence_info, nullptr, &resources.fence);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
PrintFaults();
|
||||
LOG_VULKAN_ERROR(res, "vkCreateFence failed: ");
|
||||
return false;
|
||||
}
|
||||
|
@ -90,6 +102,7 @@ bool CommandBufferManager::CreateCommandBuffers()
|
|||
res = vkCreateSemaphore(device, &semaphore_create_info, nullptr, &resources.semaphore);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
PrintFaults();
|
||||
LOG_VULKAN_ERROR(res, "vkCreateSemaphore failed: ");
|
||||
return false;
|
||||
}
|
||||
|
@ -98,6 +111,7 @@ bool CommandBufferManager::CreateCommandBuffers()
|
|||
res = vkCreateSemaphore(device, &semaphore_create_info, nullptr, &m_present_semaphore);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
PrintFaults();
|
||||
LOG_VULKAN_ERROR(res, "vkCreateSemaphore failed: ");
|
||||
return false;
|
||||
}
|
||||
|
@ -175,6 +189,7 @@ VkDescriptorPool CommandBufferManager::CreateDescriptorPool(u32 max_descriptor_s
|
|||
VkResult res = vkCreateDescriptorPool(device, &pool_create_info, nullptr, &descriptor_pool);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
PrintFaults();
|
||||
LOG_VULKAN_ERROR(res, "vkCreateDescriptorPool failed: ");
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
@ -274,7 +289,10 @@ void CommandBufferManager::WaitForCommandBufferCompletion(u32 index)
|
|||
VkResult res =
|
||||
vkWaitForFences(g_vulkan_context->GetDevice(), 1, &resources.fence, VK_TRUE, UINT64_MAX);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
PrintFaults();
|
||||
LOG_VULKAN_ERROR(res, "vkWaitForFences failed: ");
|
||||
}
|
||||
|
||||
// Clean up any resources for command buffers between the last known completed buffer and this
|
||||
// now-completed command buffer. If we use >2 buffers, this may be more than one buffer.
|
||||
|
@ -311,6 +329,7 @@ void CommandBufferManager::SubmitCommandBuffer(bool submit_on_worker_thread,
|
|||
VkResult res = vkEndCommandBuffer(command_buffer);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
PrintFaults();
|
||||
LOG_VULKAN_ERROR(res, "vkEndCommandBuffer failed: ");
|
||||
PanicAlertFmt("Failed to end command buffer: {} ({})", VkResultToString(res),
|
||||
static_cast<int>(res));
|
||||
|
@ -354,12 +373,17 @@ void CommandBufferManager::SubmitCommandBuffer(bool submit_on_worker_thread,
|
|||
// Reset the descriptor pools
|
||||
FrameResources& frame_resources = GetCurrentFrameResources();
|
||||
|
||||
frame_resources.debug->Reset();
|
||||
|
||||
if (frame_resources.descriptor_pools.size() == 1) [[likely]]
|
||||
{
|
||||
VkResult res = vkResetDescriptorPool(g_vulkan_context->GetDevice(),
|
||||
frame_resources.descriptor_pools[0], 0);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
PrintFaults();
|
||||
LOG_VULKAN_ERROR(res, "vkResetDescriptorPool failed: ");
|
||||
}
|
||||
}
|
||||
else [[unlikely]]
|
||||
{
|
||||
|
@ -421,6 +445,7 @@ void CommandBufferManager::SubmitCommandBuffer(u32 command_buffer_index,
|
|||
vkQueueSubmit(g_vulkan_context->GetGraphicsQueue(), 1, &submit_info, resources.fence);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
PrintFaults();
|
||||
LOG_VULKAN_ERROR(res, "vkQueueSubmit failed: ");
|
||||
PanicAlertFmt("Failed to submit command buffer: {} ({})", VkResultToString(res),
|
||||
static_cast<int>(res));
|
||||
|
@ -448,6 +473,7 @@ void CommandBufferManager::SubmitCommandBuffer(u32 command_buffer_index,
|
|||
m_last_present_result != VK_SUBOPTIMAL_KHR &&
|
||||
m_last_present_result != VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT)
|
||||
{
|
||||
PrintFaults();
|
||||
LOG_VULKAN_ERROR(m_last_present_result, "vkQueuePresentKHR failed: ");
|
||||
}
|
||||
|
||||
|
@ -476,12 +502,18 @@ void CommandBufferManager::BeginCommandBuffer()
|
|||
// Reset fence to unsignaled before starting.
|
||||
VkResult res = vkResetFences(g_vulkan_context->GetDevice(), 1, &resources.fence);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
PrintFaults();
|
||||
LOG_VULKAN_ERROR(res, "vkResetFences failed: ");
|
||||
}
|
||||
|
||||
// Reset command pools to beginning since we can re-use the memory now
|
||||
res = vkResetCommandPool(g_vulkan_context->GetDevice(), resources.command_pool, 0);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
PrintFaults();
|
||||
LOG_VULKAN_ERROR(res, "vkResetCommandPool failed: ");
|
||||
}
|
||||
|
||||
// Enable commands to be recorded to the two buffers again.
|
||||
VkCommandBufferBeginInfo begin_info = {VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, nullptr,
|
||||
|
@ -490,7 +522,10 @@ void CommandBufferManager::BeginCommandBuffer()
|
|||
{
|
||||
res = vkBeginCommandBuffer(command_buffer, &begin_info);
|
||||
if (res != VK_SUCCESS)
|
||||
{
|
||||
PrintFaults();
|
||||
LOG_VULKAN_ERROR(res, "vkBeginCommandBuffer failed: ");
|
||||
}
|
||||
}
|
||||
|
||||
// Reset upload command buffer state
|
||||
|
@ -537,5 +572,13 @@ void CommandBufferManager::DeferImageViewDestruction(VkImageView object)
|
|||
[object]() { vkDestroyImageView(g_vulkan_context->GetDevice(), object, nullptr); });
|
||||
}
|
||||
|
||||
void CommandBufferManager::PrintFaults()
|
||||
{
|
||||
for (auto& frame_resource : m_frame_resources)
|
||||
{
|
||||
frame_resource.debug->PrintFault();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<CommandBufferManager> g_command_buffer_mgr;
|
||||
} // namespace Vulkan
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "Common/Semaphore.h"
|
||||
|
||||
#include "VideoBackends/Vulkan/Constants.h"
|
||||
#include "VideoBackends/Vulkan/VKDebug.h"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
|
@ -47,6 +48,8 @@ public:
|
|||
// Allocates a descriptors set from the pool reserved for the current frame.
|
||||
VkDescriptorSet AllocateDescriptorSet(VkDescriptorSetLayout set_layout);
|
||||
|
||||
VkDebug& GetDebug() { return *GetCurrentFrameResources().debug; }
|
||||
|
||||
// Fence "counters" are used to track which commands have been completed by the GPU.
|
||||
// If the last completed fence counter is greater or equal to N, it means that the work
|
||||
// associated counter N has been completed by the GPU. The value of N to associate with
|
||||
|
@ -94,6 +97,8 @@ public:
|
|||
void DeferImageDestruction(VkImage object, VmaAllocation alloc);
|
||||
void DeferImageViewDestruction(VkImageView object);
|
||||
|
||||
void PrintFaults();
|
||||
|
||||
private:
|
||||
bool CreateCommandBuffers();
|
||||
void DestroyCommandBuffers();
|
||||
|
@ -129,6 +134,7 @@ private:
|
|||
{
|
||||
std::vector<VkDescriptorPool> descriptor_pools;
|
||||
u32 current_descriptor_pool_index = 0;
|
||||
std::unique_ptr<VkDebug> debug = nullptr;
|
||||
};
|
||||
|
||||
FrameResources& GetCurrentFrameResources() { return m_frame_resources[m_current_frame]; }
|
||||
|
@ -164,4 +170,11 @@ private:
|
|||
|
||||
extern std::unique_ptr<CommandBufferManager> g_command_buffer_mgr;
|
||||
|
||||
DOLPHIN_FORCE_INLINE void EmitDebugMarker(VkDebugCommand command, u64 aux0 = 0, u64 aux1 = 0,
|
||||
u64 aux2 = 0, u64 aux3 = 0)
|
||||
{
|
||||
if (g_command_buffer_mgr->GetDebug().IsEnabled()) [[unlikely]]
|
||||
g_command_buffer_mgr->GetDebug().EmitMarker(command, aux0, aux1, aux2, aux3);
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
// Copyright 2016 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "Common/Hash.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
|
||||
#include "VideoBackends/Vulkan/ShaderCompiler.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
#include "VideoBackends/Vulkan/CommandBufferManager.h"
|
||||
#include "VideoBackends/Vulkan/VKDebug.h"
|
||||
#include "VideoBackends/Vulkan/VulkanContext.h"
|
||||
#include "VideoCommon/Spirv.h"
|
||||
|
||||
|
@ -113,27 +118,75 @@ static glslang::EShTargetLanguageVersion GetLanguageVersion()
|
|||
return glslang::EShTargetSpv_1_0;
|
||||
}
|
||||
|
||||
std::optional<SPIRVCodeVector> CompileVertexShader(std::string_view source_code)
|
||||
std::optional<CompiledSPIRV> CompileVertexShader(std::string_view source_code)
|
||||
{
|
||||
return SPIRV::CompileVertexShader(GetShaderCode(source_code, SHADER_HEADER), APIType::Vulkan,
|
||||
GetLanguageVersion());
|
||||
std::string code = GetShaderCode(source_code, SHADER_HEADER);
|
||||
u64 hash = Common::GetHash64(reinterpret_cast<const u8*>(code.c_str()), u32(code.size()), 128);
|
||||
std::optional<SPIRVCodeVector> spirv =
|
||||
SPIRV::CompileVertexShader(code, APIType::Vulkan, GetLanguageVersion());
|
||||
|
||||
if (spirv != std::nullopt)
|
||||
{
|
||||
if (g_command_buffer_mgr->GetDebug().IsEnabled())
|
||||
{
|
||||
INFO_LOG_FMT(HOST_GPU, "Vertex Shader: HASH: {}; Code: {}", hash, code);
|
||||
}
|
||||
return std::make_optional<CompiledSPIRV>(CompiledSPIRV{std::move(spirv.value()), hash});
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<SPIRVCodeVector> CompileGeometryShader(std::string_view source_code)
|
||||
std::optional<CompiledSPIRV> CompileGeometryShader(std::string_view source_code)
|
||||
{
|
||||
return SPIRV::CompileGeometryShader(GetShaderCode(source_code, SHADER_HEADER), APIType::Vulkan,
|
||||
GetLanguageVersion());
|
||||
std::string code = GetShaderCode(source_code, SHADER_HEADER);
|
||||
u64 hash = Common::GetHash64(reinterpret_cast<const u8*>(code.c_str()), u32(code.size()), 128);
|
||||
std::optional<SPIRVCodeVector> spirv =
|
||||
SPIRV::CompileGeometryShader(code, APIType::Vulkan, GetLanguageVersion());
|
||||
|
||||
if (spirv != std::nullopt)
|
||||
{
|
||||
if (g_command_buffer_mgr->GetDebug().IsEnabled())
|
||||
{
|
||||
INFO_LOG_FMT(HOST_GPU, "Geometry Shader: HASH: {}; Code: {}", hash, code);
|
||||
}
|
||||
return std::make_optional<CompiledSPIRV>(CompiledSPIRV{std::move(spirv.value()), hash});
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<SPIRVCodeVector> CompileFragmentShader(std::string_view source_code)
|
||||
std::optional<CompiledSPIRV> CompileFragmentShader(std::string_view source_code)
|
||||
{
|
||||
return SPIRV::CompileFragmentShader(GetShaderCode(source_code, SHADER_HEADER), APIType::Vulkan,
|
||||
GetLanguageVersion());
|
||||
std::string code = GetShaderCode(source_code, SHADER_HEADER);
|
||||
u64 hash = Common::GetHash64(reinterpret_cast<const u8*>(code.c_str()), u32(code.size()), 128);
|
||||
std::optional<SPIRVCodeVector> spirv =
|
||||
SPIRV::CompileFragmentShader(code, APIType::Vulkan, GetLanguageVersion());
|
||||
|
||||
if (spirv != std::nullopt)
|
||||
{
|
||||
if (g_command_buffer_mgr->GetDebug().IsEnabled())
|
||||
{
|
||||
INFO_LOG_FMT(HOST_GPU, "Fragment Shader: HASH: {}; Code: {}", hash, code);
|
||||
}
|
||||
return std::make_optional<CompiledSPIRV>(CompiledSPIRV{std::move(spirv.value()), hash});
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<SPIRVCodeVector> CompileComputeShader(std::string_view source_code)
|
||||
std::optional<CompiledSPIRV> CompileComputeShader(std::string_view source_code)
|
||||
{
|
||||
return SPIRV::CompileComputeShader(GetShaderCode(source_code, COMPUTE_SHADER_HEADER),
|
||||
APIType::Vulkan, GetLanguageVersion());
|
||||
std::string code = GetShaderCode(source_code, SHADER_HEADER);
|
||||
u64 hash = Common::GetHash64(reinterpret_cast<const u8*>(code.c_str()), u32(code.size()), 128);
|
||||
std::optional<SPIRVCodeVector> spirv =
|
||||
SPIRV::CompileComputeShader(code, APIType::Vulkan, GetLanguageVersion());
|
||||
|
||||
if (spirv != std::nullopt)
|
||||
{
|
||||
if (g_command_buffer_mgr->GetDebug().IsEnabled())
|
||||
{
|
||||
INFO_LOG_FMT(HOST_GPU, "Compute Shader: HASH: {}; Code: {}", hash, code);
|
||||
}
|
||||
return std::make_optional<CompiledSPIRV>(CompiledSPIRV{std::move(spirv.value()), hash});
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
} // namespace Vulkan::ShaderCompiler
|
||||
|
|
|
@ -16,15 +16,21 @@ namespace Vulkan::ShaderCompiler
|
|||
using SPIRVCodeType = u32;
|
||||
using SPIRVCodeVector = std::vector<SPIRVCodeType>;
|
||||
|
||||
struct CompiledSPIRV
|
||||
{
|
||||
SPIRVCodeVector code;
|
||||
u64 hash;
|
||||
};
|
||||
|
||||
// Compile a vertex shader to SPIR-V.
|
||||
std::optional<SPIRVCodeVector> CompileVertexShader(std::string_view source_code);
|
||||
std::optional<CompiledSPIRV> CompileVertexShader(std::string_view source_code);
|
||||
|
||||
// Compile a geometry shader to SPIR-V.
|
||||
std::optional<SPIRVCodeVector> CompileGeometryShader(std::string_view source_code);
|
||||
std::optional<CompiledSPIRV> CompileGeometryShader(std::string_view source_code);
|
||||
|
||||
// Compile a fragment shader to SPIR-V.
|
||||
std::optional<SPIRVCodeVector> CompileFragmentShader(std::string_view source_code);
|
||||
std::optional<CompiledSPIRV> CompileFragmentShader(std::string_view source_code);
|
||||
|
||||
// Compile a compute shader to SPIR-V.
|
||||
std::optional<SPIRVCodeVector> CompileComputeShader(std::string_view source_code);
|
||||
std::optional<CompiledSPIRV> CompileComputeShader(std::string_view source_code);
|
||||
} // namespace Vulkan::ShaderCompiler
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "VideoBackends/Vulkan/ObjectCache.h"
|
||||
#include "VideoBackends/Vulkan/StagingBuffer.h"
|
||||
#include "VideoBackends/Vulkan/StateTracker.h"
|
||||
#include "VideoBackends/Vulkan/VKDebug.h"
|
||||
#include "VideoBackends/Vulkan/VKGfx.h"
|
||||
#include "VideoBackends/Vulkan/VulkanContext.h"
|
||||
|
||||
|
@ -64,6 +65,8 @@ std::vector<BBoxType> VKBoundingBox::Read(u32 index, u32 length)
|
|||
m_readback_buffer->FlushGPUCache(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
|
||||
EmitDebugMarker(VkDebugCommand::BoundingBoxRead);
|
||||
|
||||
// Wait until these commands complete.
|
||||
VKGfx::GetInstance()->ExecuteCommandBuffer(false, true);
|
||||
|
||||
|
@ -98,6 +101,8 @@ void VKBoundingBox::Write(u32 index, std::span<const BBoxType> values)
|
|||
g_command_buffer_mgr->GetCurrentCommandBuffer(), m_gpu_buffer, VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT, 0, BUFFER_SIZE,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
|
||||
|
||||
EmitDebugMarker(VkDebugCommand::BoundingBoxWrite);
|
||||
}
|
||||
|
||||
bool VKBoundingBox::CreateGPUBuffer()
|
||||
|
|
|
@ -0,0 +1,195 @@
|
|||
// Copyright 2016 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "VKDebug.h"
|
||||
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Common/Logging/Log.h"
|
||||
|
||||
#include "VideoBackends/Vulkan/CommandBufferManager.h"
|
||||
#include "VideoBackends/Vulkan/StateTracker.h"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
// TODO: Add better implementation using VK_NV_device_diagnostic_checkpoints and
|
||||
// VK_AMD_buffer_marker
|
||||
|
||||
constexpr u64 STAGING_BUFFER_SIZE = 4 << 20;
|
||||
|
||||
VkDebug::VkDebug(bool enabled) : m_enabled(enabled)
|
||||
{
|
||||
m_readback_buffer = StagingBuffer::Create(STAGING_BUFFER_TYPE_READBACK, sizeof(u64),
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT);
|
||||
m_readback_buffer->Map();
|
||||
u64* ptr = reinterpret_cast<u64*>(m_readback_buffer->GetMapPointer());
|
||||
*ptr = 0;
|
||||
m_readback_buffer->Unmap();
|
||||
}
|
||||
|
||||
void VkDebug::Reset()
|
||||
{
|
||||
m_staging_buffer_index = 0;
|
||||
m_staging_buffer_offset = 0ul;
|
||||
m_markers.clear();
|
||||
}
|
||||
|
||||
void VkDebug::EnsureStagingBufferCapacity()
|
||||
{
|
||||
if (m_buffers.size() != 0 && m_staging_buffer_offset < STAGING_BUFFER_SIZE - sizeof(u64))
|
||||
[[likely]]
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_buffers.size() != 0)
|
||||
{
|
||||
m_buffers[m_staging_buffer_index]->Unmap();
|
||||
}
|
||||
|
||||
std::unique_ptr<StagingBuffer> buffer = StagingBuffer::Create(
|
||||
STAGING_BUFFER_TYPE_UPLOAD, STAGING_BUFFER_SIZE, VK_BUFFER_USAGE_TRANSFER_SRC_BIT);
|
||||
buffer->Map();
|
||||
m_buffers.push_back(std::move(buffer));
|
||||
m_staging_buffer_offset = 0ul;
|
||||
|
||||
if (m_buffers.size() != 1)
|
||||
{
|
||||
m_staging_buffer_index++;
|
||||
}
|
||||
}
|
||||
|
||||
void VkDebug::EmitMarker(VkDebugCommand cmd, u64 aux0, u64 aux1, u64 aux2, u64 aux3)
|
||||
{
|
||||
bool is_in_renderpass = StateTracker::GetInstance()->InRenderPass();
|
||||
StateTracker::GetInstance()->EndRenderPass();
|
||||
|
||||
EnsureStagingBufferCapacity();
|
||||
std::unique_ptr<StagingBuffer>& buffer = m_buffers[m_staging_buffer_index];
|
||||
u64* ptr = reinterpret_cast<u64*>(buffer->GetMapPointer() + m_staging_buffer_offset);
|
||||
uint64_t seq = m_sequence_number++;
|
||||
*ptr = seq;
|
||||
std::array<u64, 4> aux = {aux0, aux1, aux2, aux3};
|
||||
VkDebugMarker marker = {aux, seq, cmd};
|
||||
m_markers.emplace_back(marker);
|
||||
|
||||
VkMemoryBarrier memory_barrier = {VK_STRUCTURE_TYPE_MEMORY_BARRIER, nullptr,
|
||||
VK_ACCESS_MEMORY_WRITE_BIT,
|
||||
VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT};
|
||||
vkCmdPipelineBarrier(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 1,
|
||||
&memory_barrier, 0, nullptr, 0, nullptr);
|
||||
VkBufferCopy copy = {m_staging_buffer_offset, 0, sizeof(u64)};
|
||||
vkCmdCopyBuffer(g_command_buffer_mgr->GetCurrentCommandBuffer(), buffer->GetBuffer(),
|
||||
m_readback_buffer->GetBuffer(), 1, ©);
|
||||
m_readback_buffer->FlushGPUCache(g_command_buffer_mgr->GetCurrentCommandBuffer(),
|
||||
VK_ACCESS_TRANSFER_WRITE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
|
||||
sizeof(u64));
|
||||
|
||||
m_staging_buffer_offset += sizeof(u64);
|
||||
|
||||
if (is_in_renderpass)
|
||||
StateTracker::GetInstance()->BeginRenderPass();
|
||||
}
|
||||
|
||||
const VkDebugMarker* VkDebug::FindFault()
|
||||
{
|
||||
m_readback_buffer->InvalidateCPUCache(0, sizeof(u64));
|
||||
m_readback_buffer->Map();
|
||||
u64* ptr = reinterpret_cast<u64*>(m_readback_buffer->GetMapPointer());
|
||||
u64 seq = *ptr;
|
||||
m_readback_buffer->Unmap();
|
||||
|
||||
auto result = m_markers.end();
|
||||
for (auto iter = m_markers.begin(); iter != m_markers.end(); iter++)
|
||||
{
|
||||
if (iter->sequence_number == seq)
|
||||
{
|
||||
result = iter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (result == m_markers.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
result++;
|
||||
if (result == m_markers.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return &*result;
|
||||
}
|
||||
|
||||
void VkDebug::PrintFault()
|
||||
{
|
||||
const VkDebugMarker* fault = FindFault();
|
||||
if (fault == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::string command_name;
|
||||
switch (fault->cmd)
|
||||
{
|
||||
case VkDebugCommand::Draw:
|
||||
command_name = "Draw";
|
||||
break;
|
||||
|
||||
case VkDebugCommand::DrawIndexed:
|
||||
command_name = "DrawIndexed";
|
||||
break;
|
||||
|
||||
case VkDebugCommand::Dispatch:
|
||||
command_name = "Dispatch";
|
||||
break;
|
||||
|
||||
case VkDebugCommand::BoundingBoxRead:
|
||||
command_name = "BoundingBoxRead";
|
||||
break;
|
||||
|
||||
case VkDebugCommand::ClearRegion:
|
||||
command_name = "ClearRegion";
|
||||
break;
|
||||
|
||||
case VkDebugCommand::DisableQuery:
|
||||
command_name = "DisableQuery";
|
||||
break;
|
||||
|
||||
case VkDebugCommand::ResetQuery:
|
||||
command_name = "ResetQuery";
|
||||
break;
|
||||
|
||||
case VkDebugCommand::StagingCopyFromTexture:
|
||||
command_name = "StagingCopyFromTexture";
|
||||
break;
|
||||
|
||||
case VkDebugCommand::StagingCopyToTexture:
|
||||
command_name = "StagingCopyToTexture";
|
||||
break;
|
||||
|
||||
case VkDebugCommand::StagingCopyRectFromTexture:
|
||||
command_name = "StagingCopyRectFromTexture";
|
||||
break;
|
||||
|
||||
case VkDebugCommand::ReadbackQuery:
|
||||
command_name = "ReadbackQuery";
|
||||
break;
|
||||
|
||||
case VkDebugCommand::TransitionToComputeLayout:
|
||||
command_name = "TransitionToComputeLayout";
|
||||
break;
|
||||
|
||||
case VkDebugCommand::TransitionToLayout:
|
||||
command_name = "TransitionToLayout";
|
||||
break;
|
||||
|
||||
default:
|
||||
command_name = fmt::format("{}", uint32_t(fault->cmd));
|
||||
break;
|
||||
}
|
||||
|
||||
ERROR_LOG_FMT(HOST_GPU, "Vulkan fault in {}, aux0: {}, aux1: {}, aux2: {}, aux3: {}",
|
||||
command_name, fault->aux[0], fault->aux[1], fault->aux[2], fault->aux[3]);
|
||||
}
|
||||
} // namespace Vulkan
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright 2023 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "VideoBackends/Vulkan/Constants.h"
|
||||
#include "VideoBackends/Vulkan/StagingBuffer.h"
|
||||
#include "VideoCommon/AbstractGfx.h"
|
||||
#include "VideoCommon/Constants.h"
|
||||
|
||||
namespace Vulkan
|
||||
{
|
||||
enum class VkDebugCommand
|
||||
{
|
||||
Draw,
|
||||
DrawIndexed,
|
||||
Dispatch,
|
||||
BoundingBoxRead,
|
||||
BoundingBoxWrite,
|
||||
DisableQuery,
|
||||
ResetQuery,
|
||||
ReadbackQuery,
|
||||
ClearRegion,
|
||||
TransitionToLayout,
|
||||
TransitionToComputeLayout,
|
||||
StagingCopyFromTexture,
|
||||
StagingCopyToTexture,
|
||||
StagingCopyRectFromTexture,
|
||||
};
|
||||
|
||||
struct VkDebugMarker
|
||||
{
|
||||
std::array<u64, 4> aux;
|
||||
u64 sequence_number;
|
||||
VkDebugCommand cmd;
|
||||
};
|
||||
|
||||
class VkDebug
|
||||
{
|
||||
public:
|
||||
VkDebug(bool enabled);
|
||||
void EmitMarker(VkDebugCommand cmd, u64 aux0, u64 aux1, u64 aux2, u64 aux3);
|
||||
void Reset();
|
||||
void PrintFault();
|
||||
bool IsEnabled() const { return m_enabled; }
|
||||
|
||||
private:
|
||||
void EnsureStagingBufferCapacity();
|
||||
const VkDebugMarker* FindFault();
|
||||
|
||||
private:
|
||||
bool m_enabled;
|
||||
u64 m_sequence_number = 0;
|
||||
u64 m_staging_buffer_index = 0;
|
||||
u64 m_staging_buffer_offset = 0;
|
||||
std::vector<std::unique_ptr<StagingBuffer>> m_buffers;
|
||||
std::vector<VkDebugMarker> m_markers;
|
||||
std::unique_ptr<StagingBuffer> m_readback_buffer;
|
||||
};
|
||||
} // namespace Vulkan
|
|
@ -209,6 +209,8 @@ void VKGfx::ClearRegion(const MathUtil::Rectangle<int>& target_rc, bool color_en
|
|||
return;
|
||||
|
||||
AbstractGfx::ClearRegion(target_rc, color_enable, alpha_enable, z_enable, color, z);
|
||||
|
||||
EmitDebugMarker(VkDebugCommand::ClearRegion);
|
||||
}
|
||||
|
||||
void VKGfx::Flush()
|
||||
|
@ -581,6 +583,10 @@ void VKGfx::Draw(u32 base_vertex, u32 num_vertices)
|
|||
return;
|
||||
|
||||
vkCmdDraw(g_command_buffer_mgr->GetCurrentCommandBuffer(), num_vertices, 1, base_vertex, 0);
|
||||
|
||||
const VKPipeline* pipeline = StateTracker::GetInstance()->GetPipeline();
|
||||
EmitDebugMarker(VkDebugCommand::Draw, pipeline->GetVSHash(), pipeline->GetPSHash(),
|
||||
pipeline->GetGSHash(), num_vertices);
|
||||
}
|
||||
|
||||
void VKGfx::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex)
|
||||
|
@ -590,6 +596,10 @@ void VKGfx::DrawIndexed(u32 base_index, u32 num_indices, u32 base_vertex)
|
|||
|
||||
vkCmdDrawIndexed(g_command_buffer_mgr->GetCurrentCommandBuffer(), num_indices, 1, base_index,
|
||||
base_vertex, 0);
|
||||
|
||||
const VKPipeline* pipeline = StateTracker::GetInstance()->GetPipeline();
|
||||
EmitDebugMarker(VkDebugCommand::DrawIndexed, pipeline->GetVSHash(), pipeline->GetPSHash(),
|
||||
pipeline->GetGSHash(), num_indices);
|
||||
}
|
||||
|
||||
void VKGfx::DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x, u32 groupsize_y,
|
||||
|
@ -598,6 +608,8 @@ void VKGfx::DispatchComputeShader(const AbstractShader* shader, u32 groupsize_x,
|
|||
StateTracker::GetInstance()->SetComputeShader(static_cast<const VKShader*>(shader));
|
||||
if (StateTracker::GetInstance()->BindCompute())
|
||||
vkCmdDispatch(g_command_buffer_mgr->GetCurrentCommandBuffer(), groups_x, groups_y, groups_z);
|
||||
|
||||
EmitDebugMarker(VkDebugCommand::Dispatch, 0, groups_x, groups_y, groups_z);
|
||||
}
|
||||
|
||||
SurfaceInfo VKGfx::GetSurfaceInfo() const
|
||||
|
|
|
@ -84,6 +84,7 @@ void PerfQuery::DisableQuery(PerfQueryGroup group)
|
|||
m_query_next_pos = (m_query_next_pos + 1) % PERF_QUERY_BUFFER_SIZE;
|
||||
m_query_count.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
EmitDebugMarker(VkDebugCommand::DisableQuery);
|
||||
}
|
||||
|
||||
void PerfQuery::ResetQuery()
|
||||
|
@ -100,6 +101,7 @@ void PerfQuery::ResetQuery()
|
|||
PERF_QUERY_BUFFER_SIZE);
|
||||
|
||||
std::memset(m_query_buffer.data(), 0, sizeof(ActiveQuery) * m_query_buffer.size());
|
||||
EmitDebugMarker(VkDebugCommand::ResetQuery);
|
||||
}
|
||||
|
||||
u32 PerfQuery::GetQueryResult(PerfQueryType type)
|
||||
|
@ -230,6 +232,8 @@ void PerfQuery::ReadbackQueries(u32 query_count)
|
|||
|
||||
m_query_readback_pos = (m_query_readback_pos + query_count) % PERF_QUERY_BUFFER_SIZE;
|
||||
m_query_count.fetch_sub(query_count, std::memory_order_relaxed);
|
||||
|
||||
EmitDebugMarker(VkDebugCommand::ReadbackQuery);
|
||||
}
|
||||
|
||||
void PerfQuery::PartialFlush(bool blocking)
|
||||
|
|
|
@ -24,6 +24,14 @@ VKPipeline::VKPipeline(const AbstractPipelineConfig& config, VkPipeline pipeline
|
|||
: AbstractPipeline(config), m_pipeline(pipeline), m_pipeline_layout(pipeline_layout),
|
||||
m_usage(usage)
|
||||
{
|
||||
if (config.pixel_shader)
|
||||
m_psHash = static_cast<const VKShader*>(config.pixel_shader)->GetHash();
|
||||
|
||||
if (config.vertex_shader)
|
||||
m_vsHash = static_cast<const VKShader*>(config.vertex_shader)->GetHash();
|
||||
|
||||
if (config.geometry_shader)
|
||||
m_gsHash = static_cast<const VKShader*>(config.geometry_shader)->GetHash();
|
||||
}
|
||||
|
||||
VKPipeline::~VKPipeline()
|
||||
|
|
|
@ -22,10 +22,17 @@ public:
|
|||
AbstractPipelineUsage GetUsage() const { return m_usage; }
|
||||
static std::unique_ptr<VKPipeline> Create(const AbstractPipelineConfig& config);
|
||||
|
||||
u64 GetVSHash() const { return m_vsHash; }
|
||||
u64 GetPSHash() const { return m_psHash; }
|
||||
u64 GetGSHash() const { return m_gsHash; }
|
||||
|
||||
private:
|
||||
VkPipeline m_pipeline;
|
||||
VkPipelineLayout m_pipeline_layout;
|
||||
AbstractPipelineUsage m_usage;
|
||||
u64 m_vsHash = 0;
|
||||
u64 m_psHash = 0;
|
||||
u64 m_gsHash = 0;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "Common/Align.h"
|
||||
#include "Common/Assert.h"
|
||||
#include "Common/Hash.h"
|
||||
|
||||
#include "VideoBackends/Vulkan/ObjectCache.h"
|
||||
#include "VideoBackends/Vulkan/ShaderCompiler.h"
|
||||
|
@ -15,9 +16,9 @@
|
|||
namespace Vulkan
|
||||
{
|
||||
VKShader::VKShader(ShaderStage stage, std::vector<u32> spv, VkShaderModule mod,
|
||||
std::string_view name)
|
||||
std::string_view name, u64 hash)
|
||||
: AbstractShader(stage), m_spv(std::move(spv)), m_module(mod),
|
||||
m_compute_pipeline(VK_NULL_HANDLE), m_name(name)
|
||||
m_compute_pipeline(VK_NULL_HANDLE), m_name(name), m_hash(hash)
|
||||
{
|
||||
if (!m_name.empty() && g_ActiveConfig.backend_info.bSupportsSettingObjectNames)
|
||||
{
|
||||
|
@ -30,9 +31,10 @@ VKShader::VKShader(ShaderStage stage, std::vector<u32> spv, VkShaderModule mod,
|
|||
}
|
||||
}
|
||||
|
||||
VKShader::VKShader(std::vector<u32> spv, VkPipeline compute_pipeline, std::string_view name)
|
||||
VKShader::VKShader(std::vector<u32> spv, VkPipeline compute_pipeline, std::string_view name,
|
||||
u64 hash)
|
||||
: AbstractShader(ShaderStage::Compute), m_spv(std::move(spv)), m_module(VK_NULL_HANDLE),
|
||||
m_compute_pipeline(compute_pipeline), m_name(name)
|
||||
m_compute_pipeline(compute_pipeline), m_name(name), m_hash(hash)
|
||||
{
|
||||
if (!m_name.empty() && g_ActiveConfig.backend_info.bSupportsSettingObjectNames)
|
||||
{
|
||||
|
@ -61,12 +63,12 @@ AbstractShader::BinaryData VKShader::GetBinary() const
|
|||
}
|
||||
|
||||
static std::unique_ptr<VKShader>
|
||||
CreateShaderObject(ShaderStage stage, ShaderCompiler::SPIRVCodeVector spv, std::string_view name)
|
||||
CreateShaderObject(ShaderStage stage, ShaderCompiler::CompiledSPIRV spv, std::string_view name)
|
||||
{
|
||||
VkShaderModuleCreateInfo info = {};
|
||||
info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
info.codeSize = spv.size() * sizeof(u32);
|
||||
info.pCode = spv.data();
|
||||
info.codeSize = spv.code.size() * sizeof(u32);
|
||||
info.pCode = spv.code.data();
|
||||
|
||||
VkShaderModule mod;
|
||||
VkResult res = vkCreateShaderModule(g_vulkan_context->GetDevice(), &info, nullptr, &mod);
|
||||
|
@ -78,7 +80,7 @@ CreateShaderObject(ShaderStage stage, ShaderCompiler::SPIRVCodeVector spv, std::
|
|||
|
||||
// If it's a graphics shader, we defer pipeline creation.
|
||||
if (stage != ShaderStage::Compute)
|
||||
return std::make_unique<VKShader>(stage, std::move(spv), mod, name);
|
||||
return std::make_unique<VKShader>(stage, std::move(spv.code), mod, name, spv.hash);
|
||||
|
||||
// If it's a compute shader, we create the pipeline straight away.
|
||||
const VkComputePipelineCreateInfo pipeline_info = {
|
||||
|
@ -104,13 +106,13 @@ CreateShaderObject(ShaderStage stage, ShaderCompiler::SPIRVCodeVector spv, std::
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<VKShader>(std::move(spv), pipeline, name);
|
||||
return std::make_unique<VKShader>(std::move(spv.code), pipeline, name, spv.hash);
|
||||
}
|
||||
|
||||
std::unique_ptr<VKShader> VKShader::CreateFromSource(ShaderStage stage, std::string_view source,
|
||||
std::string_view name)
|
||||
{
|
||||
std::optional<ShaderCompiler::SPIRVCodeVector> spv;
|
||||
std::optional<ShaderCompiler::CompiledSPIRV> spv;
|
||||
switch (stage)
|
||||
{
|
||||
case ShaderStage::Vertex:
|
||||
|
@ -132,6 +134,7 @@ std::unique_ptr<VKShader> VKShader::CreateFromSource(ShaderStage stage, std::str
|
|||
if (!spv)
|
||||
return nullptr;
|
||||
|
||||
INFO_LOG_FMT(HOST_GPU, "Shader: {}", source);
|
||||
return CreateShaderObject(stage, std::move(*spv), name);
|
||||
}
|
||||
|
||||
|
@ -144,7 +147,9 @@ std::unique_ptr<VKShader> VKShader::CreateFromBinary(ShaderStage stage, const vo
|
|||
if (length > 0)
|
||||
std::memcpy(spv.data(), data, length);
|
||||
|
||||
return CreateShaderObject(stage, std::move(spv), name);
|
||||
ShaderCompiler::CompiledSPIRV compiled_spv = {std::move(spv), 0ul};
|
||||
|
||||
return CreateShaderObject(stage, std::move(compiled_spv), name);
|
||||
}
|
||||
|
||||
} // namespace Vulkan
|
||||
|
|
|
@ -18,14 +18,17 @@ namespace Vulkan
|
|||
class VKShader final : public AbstractShader
|
||||
{
|
||||
public:
|
||||
VKShader(ShaderStage stage, std::vector<u32> spv, VkShaderModule mod, std::string_view name);
|
||||
VKShader(std::vector<u32> spv, VkPipeline compute_pipeline, std::string_view name);
|
||||
VKShader(ShaderStage stage, std::vector<u32> spv, VkShaderModule mod, std::string_view name,
|
||||
u64 hash);
|
||||
VKShader(std::vector<u32> spv, VkPipeline compute_pipeline, std::string_view name, u64 hash);
|
||||
~VKShader() override;
|
||||
|
||||
VkShaderModule GetShaderModule() const { return m_module; }
|
||||
VkPipeline GetComputePipeline() const { return m_compute_pipeline; }
|
||||
BinaryData GetBinary() const override;
|
||||
|
||||
u64 GetHash() const { return m_hash; }
|
||||
|
||||
static std::unique_ptr<VKShader> CreateFromSource(ShaderStage stage, std::string_view source,
|
||||
std::string_view name);
|
||||
static std::unique_ptr<VKShader> CreateFromBinary(ShaderStage stage, const void* data,
|
||||
|
@ -36,6 +39,7 @@ private:
|
|||
VkShaderModule m_module;
|
||||
VkPipeline m_compute_pipeline;
|
||||
std::string m_name;
|
||||
u64 m_hash = 0;
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
|
|
@ -316,6 +316,8 @@ void VKTexture::CopyRectangleFromTexture(const AbstractTexture* src,
|
|||
|
||||
// Only restore the source layout. Destination is restored by FinishedRendering().
|
||||
src_texture->TransitionToLayout(g_command_buffer_mgr->GetCurrentCommandBuffer(), old_src_layout);
|
||||
|
||||
EmitDebugMarker(VkDebugCommand::StagingCopyRectFromTexture);
|
||||
}
|
||||
|
||||
void VKTexture::ResolveFromTexture(const AbstractTexture* src, const MathUtil::Rectangle<int>& rect,
|
||||
|
@ -604,6 +606,8 @@ void VKTexture::TransitionToLayout(VkCommandBuffer command_buffer, VkImageLayout
|
|||
&barrier);
|
||||
|
||||
m_layout = new_layout;
|
||||
|
||||
EmitDebugMarker(VkDebugCommand::TransitionToLayout);
|
||||
}
|
||||
|
||||
void VKTexture::TransitionToLayout(VkCommandBuffer command_buffer,
|
||||
|
@ -707,6 +711,8 @@ void VKTexture::TransitionToLayout(VkCommandBuffer command_buffer,
|
|||
|
||||
vkCmdPipelineBarrier(command_buffer, srcStageMask, dstStageMask, 0, 0, nullptr, 0, nullptr, 1,
|
||||
&barrier);
|
||||
|
||||
EmitDebugMarker(VkDebugCommand::TransitionToComputeLayout);
|
||||
}
|
||||
|
||||
VKStagingTexture::VKStagingTexture(PrivateTag, StagingTextureType type, const TextureConfig& config,
|
||||
|
@ -880,6 +886,8 @@ void VKStagingTexture::CopyFromTexture(const AbstractTexture* src,
|
|||
|
||||
m_needs_flush = true;
|
||||
m_flush_fence_counter = g_command_buffer_mgr->GetCurrentFenceCounter();
|
||||
|
||||
EmitDebugMarker(VkDebugCommand::StagingCopyFromTexture);
|
||||
}
|
||||
|
||||
void VKStagingTexture::CopyFromTextureToLinearImage(const VKTexture* src_tex,
|
||||
|
@ -972,6 +980,8 @@ void VKStagingTexture::CopyToTexture(const MathUtil::Rectangle<int>& src_rect, A
|
|||
|
||||
m_needs_flush = true;
|
||||
m_flush_fence_counter = g_command_buffer_mgr->GetCurrentFenceCounter();
|
||||
|
||||
EmitDebugMarker(VkDebugCommand::StagingCopyToTexture);
|
||||
}
|
||||
|
||||
bool VKStagingTexture::Map()
|
||||
|
|
|
@ -351,6 +351,12 @@ void ShaderCache::ClearPipelineCache(T& cache, Y& disk_cache)
|
|||
|
||||
void ShaderCache::LoadCaches()
|
||||
{
|
||||
if (g_ActiveConfig.bEnableBreadcrumbs)
|
||||
{
|
||||
// We want to print all shaders to the log when using breadcrumbs.
|
||||
return;
|
||||
}
|
||||
|
||||
// Ubershader caches, if present.
|
||||
if (g_ActiveConfig.backend_info.bSupportsShaderBinaries)
|
||||
{
|
||||
|
|
|
@ -142,6 +142,7 @@ void VideoConfig::Refresh()
|
|||
bDisableFog = Config::Get(Config::GFX_DISABLE_FOG);
|
||||
bBorderlessFullscreen = Config::Get(Config::GFX_BORDERLESS_FULLSCREEN);
|
||||
bEnableValidationLayer = Config::Get(Config::GFX_ENABLE_VALIDATION_LAYER);
|
||||
bEnableBreadcrumbs = Config::Get(Config::GFX_ENABLE_BREADCRUMBS);
|
||||
bBackendMultithreading = Config::Get(Config::GFX_BACKEND_MULTITHREADING);
|
||||
iCommandBufferExecuteInterval = Config::Get(Config::GFX_COMMAND_BUFFER_EXECUTE_INTERVAL);
|
||||
bShaderCache = Config::Get(Config::GFX_SHADER_CACHE);
|
||||
|
|
|
@ -245,6 +245,9 @@ struct VideoConfig final
|
|||
// Enable API validation layers, currently only supported with Vulkan.
|
||||
bool bEnableValidationLayer = false;
|
||||
|
||||
// Enable GPU breadcrumbs, currently only supported with Vulkan.
|
||||
bool bEnableBreadcrumbs = false;
|
||||
|
||||
// Multithreaded submission, currently only supported with Vulkan.
|
||||
bool bBackendMultithreading = true;
|
||||
|
||||
|
|
Loading…
Reference in New Issue