VulkanDevice: Backport buggy NVIDIA driver workarounds from PCSX2

This commit is contained in:
Stenzek 2024-04-30 21:30:41 +10:00
parent 25f725c263
commit cbe95b281a
No known key found for this signature in database
2 changed files with 53 additions and 14 deletions

View file

@ -1645,6 +1645,11 @@ void VulkanDevice::DisableDebugUtils()
} }
} }
bool VulkanDevice::IsDeviceNVIDIA() const
{
return (m_device_properties.vendorID == 0x10DE);
}
bool VulkanDevice::IsDeviceAdreno() const bool VulkanDevice::IsDeviceAdreno() const
{ {
// Assume turnip is fine... // Assume turnip is fine...
@ -2700,13 +2705,22 @@ void VulkanDevice::ClearRenderTarget(GPUTexture* t, u32 c)
const s32 idx = IsRenderTargetBoundIndex(t); const s32 idx = IsRenderTargetBoundIndex(t);
if (idx >= 0) if (idx >= 0)
{ {
// Use an attachment clear so the render pass isn't restarted. VulkanTexture* T = static_cast<VulkanTexture*>(t);
const VkClearAttachment ca = {VK_IMAGE_ASPECT_COLOR_BIT,
static_cast<u32>(idx), if (IsDeviceNVIDIA())
{.color = static_cast<VulkanTexture*>(t)->GetClearColorValue()}}; {
const VkClearRect rc = {{{0, 0}, {t->GetWidth(), t->GetHeight()}}, 0u, 1u}; EndRenderPass();
vkCmdClearAttachments(m_current_command_buffer, 1, &ca, 1, &rc); }
t->SetState(GPUTexture::State::Dirty); else
{
// Use an attachment clear so the render pass isn't restarted.
const VkClearAttachment ca = {VK_IMAGE_ASPECT_COLOR_BIT,
static_cast<u32>(idx),
{.color = static_cast<VulkanTexture*>(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); GPUDevice::ClearDepth(t, d);
if (InRenderPass() && m_current_depth_target == t) if (InRenderPass() && m_current_depth_target == t)
{ {
// Use an attachment clear so the render pass isn't restarted. // Using vkCmdClearAttachments() within a render pass on NVIDIA seems to cause dependency issues
const VkClearAttachment ca = { // between draws that are testing depth which precede it. The result is flickering where Z tests
VK_IMAGE_ASPECT_DEPTH_BIT, 0, {.depthStencil = static_cast<VulkanTexture*>(t)->GetClearDepthValue()}}; // should be failing. Breaking/restarting the render pass isn't enough to work around the bug,
const VkClearRect rc = {{{0, 0}, {t->GetWidth(), t->GetHeight()}}, 0u, 1u}; // it needs an explicit pipeline barrier.
vkCmdClearAttachments(m_current_command_buffer, 1, &ca, 1, &rc); VulkanTexture* T = static_cast<VulkanTexture*>(t);
t->SetState(GPUTexture::State::Dirty); 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() void VulkanDevice::BeginRenderPass()
{ {
// TODO: Stats
DebugAssert(!InRenderPass()); DebugAssert(!InRenderPass());
// All textures should be in shader read only optimal already, but just in case.. // 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++) for (u32 i = 0; i < num_textures; i++)
m_current_textures[i]->TransitionToLayout(VulkanTexture::Layout::ShaderReadOnly); 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 || if (m_optional_extensions.vk_khr_dynamic_rendering && (m_optional_extensions.vk_khr_dynamic_rendering_local_read ||
!(m_current_feedback_loop & GPUPipeline::ColorFeedbackLoop))) !(m_current_feedback_loop & GPUPipeline::ColorFeedbackLoop)))
{ {

View file

@ -302,6 +302,9 @@ private:
bool EnableDebugUtils(); bool EnableDebugUtils();
void DisableDebugUtils(); void DisableDebugUtils();
/// Returns true if running on an NVIDIA GPU.
bool IsDeviceNVIDIA() const;
// Vendor queries. // Vendor queries.
bool IsDeviceAdreno() const; bool IsDeviceAdreno() const;
bool IsDeviceMali() const; bool IsDeviceMali() const;