Browse Source

[vulkan] Fix Vulkan rendering, image layout, and synchronization issues (#3511)

This fixes a visual corruption issue that occurred intermittently after loading screens, where some games would start the scene with vertex explosions, artifacts or with all colors blown out, resembling neon.

Among the known games affected by this bug are Mario Kart 8 Deluxe, The Legend of Zelda: Breath of the Wild, The Legend of Zelda: Tears of the Kingdom, Kirby and the Forgotten Land, Luigi's Mansion 3, Xenoblade Chronicles 3 and possibly others as well.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3511
Reviewed-by: DraVee <dravee@eden-emu.dev>
Reviewed-by: Lizzie <lizzie@eden-emu.dev>
Co-authored-by: MaranBr <maranbr@outlook.com>
Co-committed-by: MaranBr <maranbr@outlook.com>
open-mod
MaranBr 4 days ago
committed by crueter
parent
commit
b5f9f8b743
No known key found for this signature in database GPG Key ID: 425ACD2D4830EBC6
  1. 41
      src/video_core/renderer_vulkan/vk_blit_screen.cpp
  2. 10
      src/video_core/renderer_vulkan/vk_master_semaphore.cpp
  3. 16
      src/video_core/renderer_vulkan/vk_scheduler.cpp
  4. 2
      src/video_core/renderer_vulkan/vk_texture_cache.cpp
  5. 8
      src/video_core/vulkan_common/vulkan_device.cpp

41
src/video_core/renderer_vulkan/vk_blit_screen.cpp

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 Torzu Emulator Project
@ -22,7 +22,8 @@ BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, const
MemoryAllocator& memory_allocator_, PresentManager& present_manager_, MemoryAllocator& memory_allocator_, PresentManager& present_manager_,
Scheduler& scheduler_, const PresentFilters& filters_) Scheduler& scheduler_, const PresentFilters& filters_)
: device_memory{device_memory_}, device{device_}, memory_allocator{memory_allocator_}, : device_memory{device_memory_}, device{device_}, memory_allocator{memory_allocator_},
present_manager{present_manager_}, scheduler{scheduler_}, filters{filters_}, image_count{1},
present_manager{present_manager_}, scheduler{scheduler_}, filters{filters_},
image_count{1}, image_index{0},
swapchain_view_format{VK_FORMAT_B8G8R8A8_UNORM} {} swapchain_view_format{VK_FORMAT_B8G8R8A8_UNORM} {}
BlitScreen::~BlitScreen() = default; BlitScreen::~BlitScreen() = default;
@ -87,57 +88,49 @@ void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame,
bool resource_update_required = false; bool resource_update_required = false;
bool presentation_recreate_required = false; bool presentation_recreate_required = false;
// Recreate dynamic resources if the adapting filter changed
if (!window_adapt || scaling_filter != filters.get_scaling_filter()) { if (!window_adapt || scaling_filter != filters.get_scaling_filter()) {
resource_update_required = true; resource_update_required = true;
} }
// Recreate dynamic resources if the image count changed
const size_t old_swapchain_image_count =
std::exchange(image_count, current_swapchain_image_count);
if (old_swapchain_image_count != current_swapchain_image_count) {
if (image_count != current_swapchain_image_count) {
resource_update_required = true; resource_update_required = true;
image_count = current_swapchain_image_count;
} }
// Recreate the presentation frame if the format or dimensions of the window changed
const VkFormat old_swapchain_view_format =
std::exchange(swapchain_view_format, current_swapchain_view_format);
if (old_swapchain_view_format != current_swapchain_view_format ||
if (swapchain_view_format != current_swapchain_view_format ||
layout.width != frame->width || layout.height != frame->height) { layout.width != frame->width || layout.height != frame->height) {
resource_update_required = true; resource_update_required = true;
presentation_recreate_required = true; presentation_recreate_required = true;
swapchain_view_format = current_swapchain_view_format;
} }
// If we have a pending resource update, perform it
if (resource_update_required) { if (resource_update_required) {
// Wait for idle to ensure no resources are in use
WaitIdle(); WaitIdle();
// Update window adapt pass
SetWindowAdaptPass(); SetWindowAdaptPass();
// Update frame format if needed
if (presentation_recreate_required) { if (presentation_recreate_required) {
present_manager.RecreateFrame(frame, layout.width, layout.height, swapchain_view_format, present_manager.RecreateFrame(frame, layout.width, layout.height, swapchain_view_format,
window_adapt->GetRenderPass()); window_adapt->GetRenderPass());
} }
image_index = 0;
} }
// Add additional layers if needed
const VkExtent2D window_size{ const VkExtent2D window_size{
.width = layout.screen.GetWidth(), .width = layout.screen.GetWidth(),
.height = layout.screen.GetHeight(), .height = layout.screen.GetHeight(),
}; };
while (layers.size() < framebuffers.size()) {
if (layers.size() != framebuffers.size()) {
layers.clear();
for (size_t i = 0; i < framebuffers.size(); ++i) {
layers.emplace_back(device, memory_allocator, scheduler, device_memory, image_count, layers.emplace_back(device, memory_allocator, scheduler, device_memory, image_count,
window_size, window_adapt->GetDescriptorSetLayout(), filters); window_size, window_adapt->GetDescriptorSetLayout(), filters);
} }
}
// Perform the draw
window_adapt->Draw(rasterizer, scheduler, image_index, layers, framebuffers, layout, frame); window_adapt->Draw(rasterizer, scheduler, image_index, layers, framebuffers, layout, frame);
// Advance to next image
if (++image_index >= image_count) { if (++image_index >= image_count) {
image_index = 0; image_index = 0;
} }
@ -146,16 +139,20 @@ void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame,
vk::Framebuffer BlitScreen::CreateFramebuffer(const Layout::FramebufferLayout& layout, vk::Framebuffer BlitScreen::CreateFramebuffer(const Layout::FramebufferLayout& layout,
VkImageView image_view, VkImageView image_view,
VkFormat current_view_format) { VkFormat current_view_format) {
const bool format_updated =
std::exchange(swapchain_view_format, current_view_format) != current_view_format;
bool format_updated = swapchain_view_format != current_view_format;
swapchain_view_format = current_view_format;
if (!window_adapt || scaling_filter != filters.get_scaling_filter() || format_updated) { if (!window_adapt || scaling_filter != filters.get_scaling_filter() || format_updated) {
WaitIdle(); WaitIdle();
SetWindowAdaptPass(); SetWindowAdaptPass();
image_index = 0;
} }
const VkExtent2D extent{ const VkExtent2D extent{
.width = layout.width, .width = layout.width,
.height = layout.height, .height = layout.height,
}; };
return CreateFramebuffer(image_view, extent, window_adapt->GetRenderPass()); return CreateFramebuffer(image_view, extent, window_adapt->GetRenderPass());
} }

10
src/video_core/renderer_vulkan/vk_master_semaphore.cpp

@ -121,10 +121,8 @@ VkResult MasterSemaphore::SubmitQueue(vk::CommandBuffer& cmdbuf, vk::CommandBuff
} }
} }
static constexpr std::array<VkPipelineStageFlags, 2> wait_stage_masks{
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
};
static constexpr VkPipelineStageFlags wait_stage_mask = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf, VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf,
vk::CommandBuffer& upload_cmdbuf, vk::CommandBuffer& upload_cmdbuf,
@ -143,7 +141,7 @@ VkResult MasterSemaphore::SubmitQueueTimeline(vk::CommandBuffer& cmdbuf,
const VkSemaphore* p_wait_sems = const VkSemaphore* p_wait_sems =
(num_wait_semaphores > 0) ? &wait_semaphore : nullptr; (num_wait_semaphores > 0) ? &wait_semaphore : nullptr;
const VkPipelineStageFlags* p_wait_masks = const VkPipelineStageFlags* p_wait_masks =
(num_wait_semaphores > 0) ? wait_stage_masks.data() : nullptr;
(num_wait_semaphores > 0) ? &wait_stage_mask : nullptr;
const VkSemaphore* p_signal_sems = const VkSemaphore* p_signal_sems =
(num_signal_semaphores > 0) ? signal_semaphores.data() : nullptr; (num_signal_semaphores > 0) ? signal_semaphores.data() : nullptr;
const u64 wait_zero = 0; // dummy for binary wait const u64 wait_zero = 0; // dummy for binary wait
@ -180,7 +178,7 @@ VkResult MasterSemaphore::SubmitQueueFence(vk::CommandBuffer& cmdbuf,
const VkSemaphore* p_wait_sems = const VkSemaphore* p_wait_sems =
(num_wait_semaphores > 0) ? &wait_semaphore : nullptr; (num_wait_semaphores > 0) ? &wait_semaphore : nullptr;
const VkPipelineStageFlags* p_wait_masks = const VkPipelineStageFlags* p_wait_masks =
(num_wait_semaphores > 0) ? wait_stage_masks.data() : nullptr;
(num_wait_semaphores > 0) ? &wait_stage_mask : nullptr;
const VkSemaphore* p_signal_sems = const VkSemaphore* p_signal_sems =
(num_signal_semaphores > 0) ? &signal_semaphore : nullptr; (num_signal_semaphores > 0) ? &signal_semaphore : nullptr;
const std::array cmdbuffers{*upload_cmdbuf, *cmdbuf}; const std::array cmdbuffers{*upload_cmdbuf, *cmdbuf};

16
src/video_core/renderer_vulkan/vk_scheduler.cpp

@ -270,8 +270,8 @@ u64 Scheduler::SubmitExecution(VkSemaphore signal_semaphore, VkSemaphore wait_se
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT, .srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
}; };
upload_cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, WRITE_BARRIER);
upload_cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_VERTEX_SHADER_BIT |
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, WRITE_BARRIER);
upload_cmdbuf.End(); upload_cmdbuf.End();
cmdbuf.End(); cmdbuf.End();
@ -372,15 +372,9 @@ void Scheduler::EndRenderPass()
}; };
} }
cmdbuf.EndRenderPass(); 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,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
0,
nullptr,
nullptr,
vk::Span(barriers.data(), num_images) // Batched image barriers
);
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, nullptr, nullptr, vk::Span(barriers.data(), num_images));
}); });
state.renderpass = nullptr; state.renderpass = nullptr;

2
src/video_core/renderer_vulkan/vk_texture_cache.cpp

@ -2500,7 +2500,7 @@ void TextureCacheRuntime::TransitionImageLayout(Image& image) {
}; };
scheduler.RequestOutsideRenderPassOperationContext(); scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([barrier](vk::CommandBuffer cmdbuf) { scheduler.Record([barrier](vk::CommandBuffer cmdbuf) {
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, barrier); VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, barrier);
}); });
} }

8
src/video_core/vulkan_common/vulkan_device.cpp

@ -557,20 +557,12 @@ Device::Device(VkInstance instance_, vk::PhysicalDevice physical_, VkSurfaceKHR
} }
if (is_nvidia) { if (is_nvidia) {
const u32 nv_major_version = (properties.properties.driverVersion >> 22) & 0x3ff;
const auto arch = GetNvidiaArch(); const auto arch = GetNvidiaArch();
if (arch >= NvidiaArchitecture::Arch_AmpereOrNewer) { if (arch >= NvidiaArchitecture::Arch_AmpereOrNewer) {
LOG_WARNING(Render_Vulkan, "Ampere and newer have broken float16 math"); LOG_WARNING(Render_Vulkan, "Ampere and newer have broken float16 math");
features.shader_float16_int8.shaderFloat16 = false; features.shader_float16_int8.shaderFloat16 = false;
} }
if (nv_major_version >= 510) {
LOG_WARNING(Render_Vulkan,
"NVIDIA Drivers >= 510 do not support MSAA->MSAA image blits. "
"MSAA scaling will use 3D helpers. MSAA resolves work normally.");
cant_blit_msaa = true;
}
// Mali/ NVIDIA proprietary drivers: Shader stencil export not supported // Mali/ NVIDIA proprietary drivers: Shader stencil export not supported
// Use hardware depth/stencil blits instead when available // Use hardware depth/stencil blits instead when available
if (!extensions.shader_stencil_export) { if (!extensions.shader_stencil_export) {

Loading…
Cancel
Save