From 8d284aeccf2e348847f855124f958107c8c14557 Mon Sep 17 00:00:00 2001 From: PabloMK7 Date: Wed, 4 Mar 2026 21:05:22 +0100 Subject: [PATCH] video_core: Fix a few vulkan validation issues (#1818) --- .../renderer_vulkan/vk_blit_helper.cpp | 23 ++-- .../renderer_vulkan/vk_instance.cpp | 4 - .../renderer_vulkan/vk_stream_buffer.cpp | 111 ++++++++++++------ 3 files changed, 87 insertions(+), 51 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_blit_helper.cpp b/src/video_core/renderer_vulkan/vk_blit_helper.cpp index 3052082f7..9982948b3 100644 --- a/src/video_core/renderer_vulkan/vk_blit_helper.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_helper.cpp @@ -250,8 +250,7 @@ BlitHelper::BlitHelper(const Instance& instance_, Scheduler& scheduler_, vk::ShaderStageFlagBits::eCompute, device)}, depth_to_buffer_comp{Compile(HostShaders::VULKAN_DEPTH_TO_BUFFER_COMP, vk::ShaderStageFlagBits::eCompute, device)}, - blit_depth_stencil_frag{Compile(HostShaders::VULKAN_BLIT_DEPTH_STENCIL_FRAG, - vk::ShaderStageFlagBits::eFragment, device)}, + blit_depth_stencil_frag{VK_NULL_HANDLE}, // Texture filtering shader modules bicubic_frag{Compile(HostShaders::BICUBIC_FRAG, vk::ShaderStageFlagBits::eFragment, device)}, scale_force_frag{ @@ -263,10 +262,16 @@ BlitHelper::BlitHelper(const Instance& instance_, Scheduler& scheduler_, d24s8_to_rgba8_pipeline{MakeComputePipeline(d24s8_to_rgba8_comp, compute_pipeline_layout)}, depth_to_buffer_pipeline{ MakeComputePipeline(depth_to_buffer_comp, compute_buffer_pipeline_layout)}, - depth_blit_pipeline{MakeDepthStencilBlitPipeline()}, + depth_blit_pipeline{VK_NULL_HANDLE}, linear_sampler{device.createSampler(SAMPLER_CREATE_INFO)}, nearest_sampler{device.createSampler(SAMPLER_CREATE_INFO)} { + if (instance.IsShaderStencilExportSupported()) { + blit_depth_stencil_frag = Compile(HostShaders::VULKAN_BLIT_DEPTH_STENCIL_FRAG, + vk::ShaderStageFlagBits::eFragment, device); + depth_blit_pipeline = MakeDepthStencilBlitPipeline(); + } + if (instance.HasDebuggingToolAttached()) { SetObjectName(device, compute_pipeline_layout, "BlitHelper: compute_pipeline_layout"); SetObjectName(device, compute_buffer_pipeline_layout, @@ -280,7 +285,9 @@ BlitHelper::BlitHelper(const Instance& instance_, Scheduler& scheduler_, SetObjectName(device, full_screen_vert, "BlitHelper: full_screen_vert"); SetObjectName(device, d24s8_to_rgba8_comp, "BlitHelper: d24s8_to_rgba8_comp"); SetObjectName(device, depth_to_buffer_comp, "BlitHelper: depth_to_buffer_comp"); - SetObjectName(device, blit_depth_stencil_frag, "BlitHelper: blit_depth_stencil_frag"); + if (blit_depth_stencil_frag) { + SetObjectName(device, blit_depth_stencil_frag, "BlitHelper: blit_depth_stencil_frag"); + } SetObjectName(device, d24s8_to_rgba8_pipeline, "BlitHelper: d24s8_to_rgba8_pipeline"); SetObjectName(device, depth_to_buffer_pipeline, "BlitHelper: depth_to_buffer_pipeline"); if (depth_blit_pipeline) { @@ -304,7 +311,9 @@ BlitHelper::~BlitHelper() { device.destroyShaderModule(full_screen_vert); device.destroyShaderModule(d24s8_to_rgba8_comp); device.destroyShaderModule(depth_to_buffer_comp); - device.destroyShaderModule(blit_depth_stencil_frag); + if (blit_depth_stencil_frag) { + device.destroyShaderModule(blit_depth_stencil_frag); + } // Destroy texture filtering shader modules device.destroyShaderModule(bicubic_frag); device.destroyShaderModule(scale_force_frag); @@ -592,10 +601,6 @@ vk::Pipeline BlitHelper::MakeComputePipeline(vk::ShaderModule shader, vk::Pipeli } vk::Pipeline BlitHelper::MakeDepthStencilBlitPipeline() { - if (!instance.IsShaderStencilExportSupported()) { - return VK_NULL_HANDLE; - } - const std::array stages = MakeStages(full_screen_vert, blit_depth_stencil_frag); const auto renderpass = renderpass_cache.GetRenderpass(VideoCore::PixelFormat::Invalid, VideoCore::PixelFormat::D24S8, false); diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index a2c2cb6be..d86cc0f67 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -398,8 +398,6 @@ bool Instance::CreateDevice() { const vk::StructureChain feature_chain = physical_device.getFeatures2< vk::PhysicalDeviceFeatures2, vk::PhysicalDevicePortabilitySubsetFeaturesKHR, vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT, - vk::PhysicalDeviceExtendedDynamicState2FeaturesEXT, - vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT, vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR, vk::PhysicalDeviceCustomBorderColorFeaturesEXT, vk::PhysicalDeviceIndexTypeUint8FeaturesEXT, vk::PhysicalDeviceFragmentShaderInterlockFeaturesEXT, @@ -529,8 +527,6 @@ bool Instance::CreateDevice() { vk::PhysicalDevicePortabilitySubsetFeaturesKHR{}, vk::PhysicalDeviceTimelineSemaphoreFeaturesKHR{}, vk::PhysicalDeviceExtendedDynamicStateFeaturesEXT{}, - vk::PhysicalDeviceExtendedDynamicState2FeaturesEXT{}, - vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT{}, vk::PhysicalDeviceCustomBorderColorFeaturesEXT{}, vk::PhysicalDeviceIndexTypeUint8FeaturesEXT{}, vk::PhysicalDeviceFragmentShaderInterlockFeaturesEXT{}, diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp index a12ab53e1..e5a3ddbdc 100644 --- a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp +++ b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp @@ -1,3 +1,7 @@ +// Copyright Citra Emulator Project / Azahar Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + // Copyright 2019 yuzu Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -6,6 +10,7 @@ #include #include "common/alignment.h" #include "common/assert.h" +#include "common/literals.h" #include "video_core/renderer_vulkan/vk_instance.h" #include "video_core/renderer_vulkan/vk_memory_util.h" #include "video_core/renderer_vulkan/vk_scheduler.h" @@ -15,6 +20,8 @@ namespace Vulkan { namespace { +using namespace Common::Literals; + std::string_view BufferTypeName(BufferType type) { switch (type) { case BufferType::Upload: @@ -167,7 +174,7 @@ void StreamBuffer::Commit(u32 size) { watch.tick = scheduler.CurrentTick(); } -void StreamBuffer::CreateBuffers(u64 prefered_size) { +void StreamBuffer::CreateBuffers(u64 preferred_size) { const vk::Device device = instance.GetDevice(); const auto memory_properties = instance.GetPhysicalDevice().getMemoryProperties(); const u32 preferred_type = GetMemoryType(memory_properties, type); @@ -180,53 +187,81 @@ void StreamBuffer::CreateBuffers(u64 prefered_size) { const vk::DeviceSize heap_size = memory_properties.memoryHeaps[preferred_heap].size; // As per DXVK's example, using `heap_size / 2` const vk::DeviceSize allocable_size = heap_size / 2; - buffer = device.createBuffer({ - .size = std::min(prefered_size, allocable_size), - .usage = usage, - }); - const auto requirements_chain = - device - .getBufferMemoryRequirements2( - {.buffer = buffer}); + vk::DeviceSize attempt_size = std::min(preferred_size, allocable_size); - const auto& requirements = requirements_chain.get(); - const auto& dedicated_requirements = requirements_chain.get(); + // Retry allocation until we reach minimum 8 KiB allocation size + const vk::DeviceSize min_buffer_size = std::min(8_KiB, attempt_size); - stream_buffer_size = static_cast(requirements.memoryRequirements.size); + while (attempt_size >= min_buffer_size) { + try { + // Create buffer with current attempt size + buffer = device.createBuffer({ + .size = attempt_size, + .usage = usage, + }); - LOG_INFO(Render_Vulkan, "Creating {} buffer with size {} KiB with flags {}", - BufferTypeName(type), stream_buffer_size / 1024, - vk::to_string(mem_type.propertyFlags)); + const auto requirements_chain = device.getBufferMemoryRequirements2< + vk::MemoryRequirements2, vk::MemoryDedicatedRequirements>({.buffer = buffer}); - if (dedicated_requirements.prefersDedicatedAllocation) { - vk::StructureChain alloc_chain = - {}; + const auto& requirements = requirements_chain.get(); + const auto& dedicated_requirements = + requirements_chain.get(); - auto& alloc_info = alloc_chain.get(); - alloc_info.allocationSize = requirements.memoryRequirements.size; - alloc_info.memoryTypeIndex = preferred_type; + if (dedicated_requirements.prefersDedicatedAllocation) { + vk::StructureChain + alloc_chain{}; - auto& dedicated_alloc_info = alloc_chain.get(); - dedicated_alloc_info.buffer = buffer; + auto& alloc_info = alloc_chain.get(); + alloc_info.allocationSize = requirements.memoryRequirements.size; + alloc_info.memoryTypeIndex = preferred_type; - memory = device.allocateMemory(alloc_chain.get()); - } else { - memory = device.allocateMemory({ - .allocationSize = requirements.memoryRequirements.size, - .memoryTypeIndex = preferred_type, - }); + auto& dedicated_alloc_info = alloc_chain.get(); + dedicated_alloc_info.buffer = buffer; + + memory = device.allocateMemory(alloc_chain.get()); + } else { + memory = device.allocateMemory({ + .allocationSize = requirements.memoryRequirements.size, + .memoryTypeIndex = preferred_type, + }); + } + + // Allocation succeeded, bind and map + device.bindBufferMemory(buffer, memory, 0); + + mapped = reinterpret_cast( + device.mapMemory(memory, 0, requirements.memoryRequirements.size)); + + stream_buffer_size = static_cast(attempt_size); + + LOG_INFO(Render_Vulkan, "Created {} buffer with size {} KiB (flags {})", + BufferTypeName(type), stream_buffer_size / 1024, + vk::to_string(mem_type.propertyFlags)); + + if (instance.HasDebuggingToolAttached()) { + SetObjectName(device, buffer, "StreamBuffer({}): {} KiB {}", BufferTypeName(type), + stream_buffer_size / 1024, vk::to_string(mem_type.propertyFlags)); + SetObjectName(device, memory, "StreamBufferMemory({}): {} Kib {}", + BufferTypeName(type), stream_buffer_size / 1024, + vk::to_string(mem_type.propertyFlags)); + } + + return; + } catch (const vk::SystemError& err) { + // Allocation failed, clean up and retry smaller + if (buffer) { + device.destroyBuffer(buffer); + buffer = VK_NULL_HANDLE; + } + + attempt_size /= 2; + } } - device.bindBufferMemory(buffer, memory, 0); - mapped = reinterpret_cast(device.mapMemory(memory, 0, VK_WHOLE_SIZE)); - - if (instance.HasDebuggingToolAttached()) { - SetObjectName(device, buffer, "StreamBuffer({}): {} KiB {}", BufferTypeName(type), - stream_buffer_size / 1024, vk::to_string(mem_type.propertyFlags)); - SetObjectName(device, memory, "StreamBufferMemory({}): {} Kib {}", BufferTypeName(type), - stream_buffer_size / 1024, vk::to_string(mem_type.propertyFlags)); - } + UNREACHABLE_MSG("Failed to allocate {} buffer of preferred size {} KiB (flags {})", + BufferTypeName(type), preferred_size / 1024, + vk::to_string(mem_type.propertyFlags)); } void StreamBuffer::ReserveWatches(std::vector& watches, std::size_t grow_size) {