fix: lower UBO max size to account buffer cache offset (#2388)

* fix: lower UBO max size to account buffer cache offset

* review comments

* remove UBO size from spec and always set it to max on shader side
This commit is contained in:
psucien 2025-02-09 22:03:20 +01:00 committed by GitHub
parent 34a4f6e60e
commit 04fe3a79b9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 61 additions and 40 deletions

View file

@ -11,11 +11,12 @@
namespace Vulkan {
ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler_,
DescriptorHeap& desc_heap_, vk::PipelineCache pipeline_cache,
ComputePipelineKey compute_key_, const Shader::Info& info_,
vk::ShaderModule module)
: Pipeline{instance_, scheduler_, desc_heap_, pipeline_cache, true}, compute_key{compute_key_} {
ComputePipeline::ComputePipeline(const Instance& instance, Scheduler& scheduler,
DescriptorHeap& desc_heap, const Shader::Profile& profile,
vk::PipelineCache pipeline_cache, ComputePipelineKey compute_key_,
const Shader::Info& info_, vk::ShaderModule module)
: Pipeline{instance, scheduler, desc_heap, profile, pipeline_cache, true},
compute_key{compute_key_} {
auto& info = stages[int(Shader::LogicalStage::Compute)];
info = &info_;
const auto debug_str = GetDebugString();
@ -49,8 +50,8 @@ ComputePipeline::ComputePipeline(const Instance& instance_, Scheduler& scheduler
const auto sharp = buffer.GetSharp(*info);
bindings.push_back({
.binding = binding++,
.descriptorType = buffer.IsStorage(sharp) ? vk::DescriptorType::eStorageBuffer
: vk::DescriptorType::eUniformBuffer,
.descriptorType = buffer.IsStorage(sharp, profile) ? vk::DescriptorType::eStorageBuffer
: vk::DescriptorType::eUniformBuffer,
.descriptorCount = 1,
.stageFlags = vk::ShaderStageFlagBits::eCompute,
});

View file

@ -31,8 +31,9 @@ struct ComputePipelineKey {
class ComputePipeline : public Pipeline {
public:
ComputePipeline(const Instance& instance, Scheduler& scheduler, DescriptorHeap& desc_heap,
vk::PipelineCache pipeline_cache, ComputePipelineKey compute_key,
const Shader::Info& info, vk::ShaderModule module);
const Shader::Profile& profile, vk::PipelineCache pipeline_cache,
ComputePipelineKey compute_key, const Shader::Info& info,
vk::ShaderModule module);
~ComputePipeline();
private:

View file

@ -25,13 +25,13 @@ namespace Vulkan {
using Shader::Backend::SPIRV::AuxShaderType;
GraphicsPipeline::GraphicsPipeline(
const Instance& instance_, Scheduler& scheduler_, DescriptorHeap& desc_heap_,
const GraphicsPipelineKey& key_, vk::PipelineCache pipeline_cache,
std::span<const Shader::Info*, MaxShaderStages> infos,
const Instance& instance, Scheduler& scheduler, DescriptorHeap& desc_heap,
const Shader::Profile& profile, const GraphicsPipelineKey& key_,
vk::PipelineCache pipeline_cache, std::span<const Shader::Info*, MaxShaderStages> infos,
std::span<const Shader::RuntimeInfo, MaxShaderStages> runtime_infos,
std::optional<const Shader::Gcn::FetchShaderData> fetch_shader_,
std::span<const vk::ShaderModule> modules)
: Pipeline{instance_, scheduler_, desc_heap_, pipeline_cache}, key{key_},
: Pipeline{instance, scheduler, desc_heap, profile, pipeline_cache}, key{key_},
fetch_shader{std::move(fetch_shader_)} {
const vk::Device device = instance.GetDevice();
std::ranges::copy(infos, stages.begin());
@ -369,8 +369,9 @@ void GraphicsPipeline::BuildDescSetLayout() {
const auto sharp = buffer.GetSharp(*stage);
bindings.push_back({
.binding = binding++,
.descriptorType = buffer.IsStorage(sharp) ? vk::DescriptorType::eStorageBuffer
: vk::DescriptorType::eUniformBuffer,
.descriptorType = buffer.IsStorage(sharp, profile)
? vk::DescriptorType::eStorageBuffer
: vk::DescriptorType::eUniformBuffer,
.descriptorCount = 1,
.stageFlags = gp_stage_flags,
});

View file

@ -75,7 +75,8 @@ struct GraphicsPipelineKey {
class GraphicsPipeline : public Pipeline {
public:
GraphicsPipeline(const Instance& instance, Scheduler& scheduler, DescriptorHeap& desc_heap,
const GraphicsPipelineKey& key, vk::PipelineCache pipeline_cache,
const Shader::Profile& profile, const GraphicsPipelineKey& key,
vk::PipelineCache pipeline_cache,
std::span<const Shader::Info*, MaxShaderStages> stages,
std::span<const Shader::RuntimeInfo, MaxShaderStages> runtime_infos,
std::optional<const Shader::Gcn::FetchShaderData> fetch_shader,

View file

@ -209,6 +209,11 @@ public:
return properties.limits.minUniformBufferOffsetAlignment;
}
/// Returns the maximum size of uniform buffers.
vk::DeviceSize UniformMaxSize() const {
return properties.limits.maxUniformBufferRange;
}
/// Returns the minimum required alignment for storage buffers
vk::DeviceSize StorageMinAlignment() const {
return properties.limits.minStorageBufferOffsetAlignment;
@ -254,10 +259,12 @@ public:
return features.shaderClipDistance;
}
/// Returns the maximim viewport width.
u32 GetMaxViewportWidth() const {
return properties.limits.maxViewportDimensions[0];
}
/// Returns the maximum viewport height.
u32 GetMaxViewportHeight() const {
return properties.limits.maxViewportDimensions[1];
}

View file

@ -204,6 +204,10 @@ PipelineCache::PipelineCache(const Instance& instance_, Scheduler& scheduler_,
instance.GetDriverID() == vk::DriverId::eNvidiaProprietary,
.needs_lds_barriers = instance.GetDriverID() == vk::DriverId::eNvidiaProprietary ||
instance.GetDriverID() == vk::DriverId::eMoltenvk,
// When binding a UBO, we calculate its size considering the offset in the larger buffer
// cache underlying resource. In some cases, it may produce sizes exceeding the system
// maximum allowed UBO range, so we need to reduce the threshold to prevent issues.
.max_ubo_size = instance.UniformMaxSize() - instance.UniformMinAlignment(),
.max_viewport_width = instance.GetMaxViewportWidth(),
.max_viewport_height = instance.GetMaxViewportHeight(),
.max_shared_memory_size = instance.MaxComputeSharedMemorySize(),
@ -222,7 +226,7 @@ const GraphicsPipeline* PipelineCache::GetGraphicsPipeline() {
}
const auto [it, is_new] = graphics_pipelines.try_emplace(graphics_key);
if (is_new) {
it.value() = std::make_unique<GraphicsPipeline>(instance, scheduler, desc_heap,
it.value() = std::make_unique<GraphicsPipeline>(instance, scheduler, desc_heap, profile,
graphics_key, *pipeline_cache, infos,
runtime_infos, fetch_shader, modules);
if (Config::collectShadersForDebug()) {
@ -243,8 +247,9 @@ const ComputePipeline* PipelineCache::GetComputePipeline() {
}
const auto [it, is_new] = compute_pipelines.try_emplace(compute_key);
if (is_new) {
it.value() = std::make_unique<ComputePipeline>(
instance, scheduler, desc_heap, *pipeline_cache, compute_key, *infos[0], modules[0]);
it.value() =
std::make_unique<ComputePipeline>(instance, scheduler, desc_heap, profile,
*pipeline_cache, compute_key, *infos[0], modules[0]);
if (Config::collectShadersForDebug()) {
auto& m = modules[0];
module_related_pipelines[m].emplace_back(compute_key);

View file

@ -68,6 +68,10 @@ public:
static std::string GetShaderName(Shader::Stage stage, u64 hash,
std::optional<size_t> perm = {});
auto& GetProfile() const {
return profile;
}
private:
bool RefreshGraphicsKey();
bool RefreshComputeKey();

View file

@ -14,8 +14,10 @@
namespace Vulkan {
Pipeline::Pipeline(const Instance& instance_, Scheduler& scheduler_, DescriptorHeap& desc_heap_,
vk::PipelineCache pipeline_cache, bool is_compute_ /*= false*/)
: instance{instance_}, scheduler{scheduler_}, desc_heap{desc_heap_}, is_compute{is_compute_} {}
const Shader::Profile& profile_, vk::PipelineCache pipeline_cache,
bool is_compute_ /*= false*/)
: instance{instance_}, scheduler{scheduler_}, desc_heap{desc_heap_}, profile{profile_},
is_compute{is_compute_} {}
Pipeline::~Pipeline() = default;

View file

@ -5,6 +5,7 @@
#include "shader_recompiler/backend/bindings.h"
#include "shader_recompiler/info.h"
#include "shader_recompiler/profile.h"
#include "video_core/renderer_vulkan/vk_common.h"
#include "video_core/texture_cache/texture_cache.h"
@ -26,7 +27,8 @@ class DescriptorHeap;
class Pipeline {
public:
Pipeline(const Instance& instance, Scheduler& scheduler, DescriptorHeap& desc_heap,
vk::PipelineCache pipeline_cache, bool is_compute = false);
const Shader::Profile& profile, vk::PipelineCache pipeline_cache,
bool is_compute = false);
virtual ~Pipeline();
vk::Pipeline Handle() const noexcept {
@ -66,6 +68,7 @@ protected:
const Instance& instance;
Scheduler& scheduler;
DescriptorHeap& desc_heap;
const Shader::Profile& profile;
vk::UniquePipeline pipeline;
vk::UniquePipelineLayout pipeline_layout;
vk::UniqueDescriptorSetLayout desc_layout;

View file

@ -554,11 +554,10 @@ void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Binding
}
// Second pass to re-bind buffers that were updated after binding
auto& null_buffer = buffer_cache.GetBuffer(VideoCore::NULL_BUFFER_ID);
for (u32 i = 0; i < buffer_bindings.size(); i++) {
const auto& [buffer_id, vsharp] = buffer_bindings[i];
const auto& desc = stage.buffers[i];
const bool is_storage = desc.IsStorage(vsharp);
const bool is_storage = desc.IsStorage(vsharp, pipeline_cache.GetProfile());
if (!buffer_id) {
if (desc.is_gds_buffer) {
const auto* gds_buf = buffer_cache.GetGdsBuffer();
@ -566,6 +565,7 @@ void Rasterizer::BindBuffers(const Shader::Info& stage, Shader::Backend::Binding
} else if (instance.IsNullDescriptorSupported()) {
buffer_infos.emplace_back(VK_NULL_HANDLE, 0, VK_WHOLE_SIZE);
} else {
auto& null_buffer = buffer_cache.GetBuffer(VideoCore::NULL_BUFFER_ID);
buffer_infos.emplace_back(null_buffer.Handle(), 0, VK_WHOLE_SIZE);
}
} else {