video_core: Fix a few vulkan validation issues (#1818)

This commit is contained in:
PabloMK7 2026-03-04 21:05:22 +01:00 committed by GitHub
parent d49aa070fd
commit 8d284aeccf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 87 additions and 51 deletions

View File

@ -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<vk::Filter::eLinear>)},
nearest_sampler{device.createSampler(SAMPLER_CREATE_INFO<vk::Filter::eNearest>)} {
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);

View File

@ -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{},

View File

@ -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 <limits>
#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<vk::MemoryRequirements2, vk::MemoryDedicatedRequirements>(
{.buffer = buffer});
vk::DeviceSize attempt_size = std::min(preferred_size, allocable_size);
const auto& requirements = requirements_chain.get<vk::MemoryRequirements2>();
const auto& dedicated_requirements = requirements_chain.get<vk::MemoryDedicatedRequirements>();
// Retry allocation until we reach minimum 8 KiB allocation size
const vk::DeviceSize min_buffer_size = std::min<vk::DeviceSize>(8_KiB, attempt_size);
stream_buffer_size = static_cast<u64>(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<vk::MemoryAllocateInfo, vk::MemoryDedicatedAllocateInfo> alloc_chain =
{};
const auto& requirements = requirements_chain.get<vk::MemoryRequirements2>();
const auto& dedicated_requirements =
requirements_chain.get<vk::MemoryDedicatedRequirements>();
auto& alloc_info = alloc_chain.get<vk::MemoryAllocateInfo>();
alloc_info.allocationSize = requirements.memoryRequirements.size;
alloc_info.memoryTypeIndex = preferred_type;
if (dedicated_requirements.prefersDedicatedAllocation) {
vk::StructureChain<vk::MemoryAllocateInfo, vk::MemoryDedicatedAllocateInfo>
alloc_chain{};
auto& dedicated_alloc_info = alloc_chain.get<vk::MemoryDedicatedAllocateInfo>();
dedicated_alloc_info.buffer = buffer;
auto& alloc_info = alloc_chain.get<vk::MemoryAllocateInfo>();
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<vk::MemoryDedicatedAllocateInfo>();
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<u8*>(
device.mapMemory(memory, 0, requirements.memoryRequirements.size));
stream_buffer_size = static_cast<u64>(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<u8*>(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<Watch>& watches, std::size_t grow_size) {