From cbe95b281a7d1e894c66b4ae54a103b748b239c5 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Tue, 30 Apr 2024 21:30:41 +1000 Subject: [PATCH] VulkanDevice: Backport buggy NVIDIA driver workarounds from PCSX2 --- src/util/vulkan_device.cpp | 64 +++++++++++++++++++++++++++++--------- src/util/vulkan_device.h | 3 ++ 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/src/util/vulkan_device.cpp b/src/util/vulkan_device.cpp index aedf3c413..61f20004b 100644 --- a/src/util/vulkan_device.cpp +++ b/src/util/vulkan_device.cpp @@ -1645,6 +1645,11 @@ void VulkanDevice::DisableDebugUtils() } } +bool VulkanDevice::IsDeviceNVIDIA() const +{ + return (m_device_properties.vendorID == 0x10DE); +} + bool VulkanDevice::IsDeviceAdreno() const { // Assume turnip is fine... @@ -2700,13 +2705,22 @@ void VulkanDevice::ClearRenderTarget(GPUTexture* t, u32 c) const s32 idx = IsRenderTargetBoundIndex(t); if (idx >= 0) { - // Use an attachment clear so the render pass isn't restarted. - const VkClearAttachment ca = {VK_IMAGE_ASPECT_COLOR_BIT, - static_cast(idx), - {.color = static_cast(t)->GetClearColorValue()}}; - const VkClearRect rc = {{{0, 0}, {t->GetWidth(), t->GetHeight()}}, 0u, 1u}; - vkCmdClearAttachments(m_current_command_buffer, 1, &ca, 1, &rc); - t->SetState(GPUTexture::State::Dirty); + VulkanTexture* T = static_cast(t); + + if (IsDeviceNVIDIA()) + { + EndRenderPass(); + } + else + { + // Use an attachment clear so the render pass isn't restarted. + const VkClearAttachment ca = {VK_IMAGE_ASPECT_COLOR_BIT, + static_cast(idx), + {.color = static_cast(T)->GetClearColorValue()}}; + const VkClearRect rc = {{{0, 0}, {T->GetWidth(), T->GetHeight()}}, 0u, 1u}; + vkCmdClearAttachments(m_current_command_buffer, 1, &ca, 1, &rc); + T->SetState(GPUTexture::State::Dirty); + } } } } @@ -2716,12 +2730,24 @@ void VulkanDevice::ClearDepth(GPUTexture* t, float d) GPUDevice::ClearDepth(t, d); if (InRenderPass() && m_current_depth_target == t) { - // Use an attachment clear so the render pass isn't restarted. - const VkClearAttachment ca = { - VK_IMAGE_ASPECT_DEPTH_BIT, 0, {.depthStencil = static_cast(t)->GetClearDepthValue()}}; - const VkClearRect rc = {{{0, 0}, {t->GetWidth(), t->GetHeight()}}, 0u, 1u}; - vkCmdClearAttachments(m_current_command_buffer, 1, &ca, 1, &rc); - t->SetState(GPUTexture::State::Dirty); + // Using vkCmdClearAttachments() within a render pass on NVIDIA seems to cause dependency issues + // between draws that are testing depth which precede it. The result is flickering where Z tests + // should be failing. Breaking/restarting the render pass isn't enough to work around the bug, + // it needs an explicit pipeline barrier. + VulkanTexture* T = static_cast(t); + if (IsDeviceNVIDIA()) + { + EndRenderPass(); + T->TransitionSubresourcesToLayout(GetCurrentCommandBuffer(), 0, 1, 0, 1, T->GetLayout(), T->GetLayout()); + } + else + { + // Use an attachment clear so the render pass isn't restarted. + const VkClearAttachment ca = {VK_IMAGE_ASPECT_DEPTH_BIT, 0, {.depthStencil = T->GetClearDepthValue()}}; + const VkClearRect rc = {{{0, 0}, {T->GetWidth(), T->GetHeight()}}, 0u, 1u}; + vkCmdClearAttachments(m_current_command_buffer, 1, &ca, 1, &rc); + T->SetState(GPUTexture::State::Dirty); + } } } @@ -3202,7 +3228,6 @@ void VulkanDevice::SetRenderTargets(GPUTexture* const* rts, u32 num_rts, GPUText void VulkanDevice::BeginRenderPass() { - // TODO: Stats DebugAssert(!InRenderPass()); // All textures should be in shader read only optimal already, but just in case.. @@ -3210,6 +3235,17 @@ void VulkanDevice::BeginRenderPass() for (u32 i = 0; i < num_textures; i++) m_current_textures[i]->TransitionToLayout(VulkanTexture::Layout::ShaderReadOnly); + // NVIDIA drivers appear to return random garbage when sampling the RT via a feedback loop, if the load op for + // the render pass is CLEAR. Using vkCmdClearAttachments() doesn't work, so we have to clear the image instead. + if (m_current_feedback_loop & GPUPipeline::ColorFeedbackLoop) + { + for (u32 i = 0; i < m_num_current_render_targets; i++) + { + if (m_current_render_targets[i]->GetState() == GPUTexture::State::Cleared) + m_current_render_targets[i]->CommitClear(m_current_command_buffer); + } + } + if (m_optional_extensions.vk_khr_dynamic_rendering && (m_optional_extensions.vk_khr_dynamic_rendering_local_read || !(m_current_feedback_loop & GPUPipeline::ColorFeedbackLoop))) { diff --git a/src/util/vulkan_device.h b/src/util/vulkan_device.h index 0df690cef..ed5d2d330 100644 --- a/src/util/vulkan_device.h +++ b/src/util/vulkan_device.h @@ -302,6 +302,9 @@ private: bool EnableDebugUtils(); void DisableDebugUtils(); + /// Returns true if running on an NVIDIA GPU. + bool IsDeviceNVIDIA() const; + // Vendor queries. bool IsDeviceAdreno() const; bool IsDeviceMali() const;