From 5bb58d740486ce16168b0504388a5b7a5a7a8bcd Mon Sep 17 00:00:00 2001 From: ryana Date: Tue, 28 Apr 2026 21:43:57 +0800 Subject: [PATCH] [spirv] mark descriptor indices as non-uniform --- .../backend/spirv/emit_spirv_image.cpp | 83 +++++++++++++++---- src/shader_recompiler/profile.h | 4 + .../renderer_vulkan/vk_pipeline_cache.cpp | 1 + src/video_core/vulkan_common/vulkan_device.h | 8 ++ 4 files changed, 82 insertions(+), 14 deletions(-) diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index c2511942d9..72d7e56092 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -14,6 +14,40 @@ namespace Shader::Backend::SPIRV { namespace { +class DescriptorIndex { +public: + explicit DescriptorIndex(EmitContext& ctx_, const IR::Value& index, + spv::Capability capability) + : ctx{ctx_}, id{index.IsImmediate() ? ctx.Const(index.U32()) : ctx.Def(index)}, + is_non_uniform{ctx.profile.support_descriptor_nonuniform_indexing && + !index.IsImmediate()} { + if (!is_non_uniform) { + return; + } + if (ctx.profile.supported_spirv < 0x00010400) { + ctx.AddExtension("SPV_EXT_descriptor_indexing"); + } + ctx.AddCapability(spv::Capability::ShaderNonUniform); + ctx.AddCapability(capability); + Decorate(id); + } + + Id Value() const { + return id; + } + + void Decorate(Id object) const { + if (is_non_uniform) { + ctx.Decorate(object, spv::Decoration::NonUniform); + } + } + +private: + EmitContext& ctx; + Id id; + bool is_non_uniform; +}; + class ImageOperands { public: [[maybe_unused]] static constexpr bool ImageSampleOffsetAllowed = false; @@ -189,8 +223,13 @@ private: Id Texture(EmitContext& ctx, IR::TextureInstInfo info, [[maybe_unused]] const IR::Value& index) { const TextureDefinition& def{ctx.textures.at(info.descriptor_index)}; if (def.count > 1) { - const Id pointer{ctx.OpAccessChain(def.pointer_type, def.id, ctx.Def(index))}; - return ctx.OpLoad(def.sampled_type, pointer); + const DescriptorIndex idx{ + ctx, index, spv::Capability::SampledImageArrayNonUniformIndexing}; + const Id pointer{ctx.OpAccessChain(def.pointer_type, def.id, idx.Value())}; + idx.Decorate(pointer); + const Id object{ctx.OpLoad(def.sampled_type, pointer)}; + idx.Decorate(object); + return object; } else { return ctx.OpLoad(def.sampled_type, def.id); } @@ -200,17 +239,25 @@ Id TextureImage(EmitContext& ctx, IR::TextureInstInfo info, const IR::Value& ind if (info.type == TextureType::Buffer) { const TextureBufferDefinition& def{ctx.texture_buffers.at(info.descriptor_index)}; if (def.count > 1) { - const Id idx{index.IsImmediate() ? ctx.Const(index.U32()) : ctx.Def(index)}; - const Id ptr{ctx.OpAccessChain(ctx.image_buffer_type, def.id, idx)}; - return ctx.OpLoad(ctx.image_buffer_type, ptr); + const DescriptorIndex idx{ + ctx, index, spv::Capability::UniformTexelBufferArrayNonUniformIndexing}; + const Id ptr{ctx.OpAccessChain(ctx.image_buffer_type, def.id, idx.Value())}; + idx.Decorate(ptr); + const Id object{ctx.OpLoad(ctx.image_buffer_type, ptr)}; + idx.Decorate(object); + return object; } return ctx.OpLoad(ctx.image_buffer_type, def.id); } else { const TextureDefinition& def{ctx.textures.at(info.descriptor_index)}; if (def.count > 1) { - const Id idx{index.IsImmediate() ? ctx.Const(index.U32()) : ctx.Def(index)}; - const Id ptr{ctx.OpAccessChain(def.pointer_type, def.id, idx)}; - return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, ptr)); + const DescriptorIndex idx{ + ctx, index, spv::Capability::SampledImageArrayNonUniformIndexing}; + const Id ptr{ctx.OpAccessChain(def.pointer_type, def.id, idx.Value())}; + idx.Decorate(ptr); + const Id object{ctx.OpLoad(def.sampled_type, ptr)}; + idx.Decorate(object); + return ctx.OpImage(def.image_type, object); } return ctx.OpImage(def.image_type, ctx.OpLoad(def.sampled_type, def.id)); } @@ -220,17 +267,25 @@ std::pair Image(EmitContext& ctx, const IR::Value& index, IR::TextureI if (info.type == TextureType::Buffer) { const ImageBufferDefinition def{ctx.image_buffers.at(info.descriptor_index)}; if (def.count > 1) { - const Id idx{index.IsImmediate() ? ctx.Const(index.U32()) : ctx.Def(index)}; - const Id ptr{ctx.OpAccessChain(def.pointer_type, def.id, idx)}; - return {ctx.OpLoad(def.image_type, ptr), def.is_integer}; + const DescriptorIndex idx{ + ctx, index, spv::Capability::StorageTexelBufferArrayNonUniformIndexing}; + const Id ptr{ctx.OpAccessChain(def.pointer_type, def.id, idx.Value())}; + idx.Decorate(ptr); + const Id object{ctx.OpLoad(def.image_type, ptr)}; + idx.Decorate(object); + return {object, def.is_integer}; } return {ctx.OpLoad(def.image_type, def.id), def.is_integer}; } else { const ImageDefinition def{ctx.images.at(info.descriptor_index)}; if (def.count > 1) { - const Id idx{index.IsImmediate() ? ctx.Const(index.U32()) : ctx.Def(index)}; - const Id ptr{ctx.OpAccessChain(def.pointer_type, def.id, idx)}; - return {ctx.OpLoad(def.image_type, ptr), def.is_integer}; + const DescriptorIndex idx{ + ctx, index, spv::Capability::StorageImageArrayNonUniformIndexing}; + const Id ptr{ctx.OpAccessChain(def.pointer_type, def.id, idx.Value())}; + idx.Decorate(ptr); + const Id object{ctx.OpLoad(def.image_type, ptr)}; + idx.Decorate(object); + return {object, def.is_integer}; } return {ctx.OpLoad(def.image_type, def.id), def.is_integer}; } diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h index 90e46bb1ba..37a2d2df2b 100644 --- a/src/shader_recompiler/profile.h +++ b/src/shader_recompiler/profile.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -45,6 +48,7 @@ struct Profile { bool support_scaled_attributes{}; bool support_multi_viewport{}; bool support_geometry_streams{}; + bool support_descriptor_nonuniform_indexing{}; bool warp_size_potentially_larger_than_guest{}; diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 817320c96c..b720c7077e 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -412,6 +412,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, .support_scaled_attributes = !device.MustEmulateScaledFormats(), .support_multi_viewport = device.SupportsMultiViewport(), .support_geometry_streams = device.AreTransformFeedbackGeometryStreamsSupported(), + .support_descriptor_nonuniform_indexing = device.IsDescriptorNonUniformIndexingSupported(), .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(), diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h index a8a89aee89..cbaea58ec3 100644 --- a/src/video_core/vulkan_common/vulkan_device.h +++ b/src/video_core/vulkan_common/vulkan_device.h @@ -33,6 +33,7 @@ VK_DEFINE_HANDLE(VmaAllocator) FEATURE(KHR, VariablePointer, VARIABLE_POINTERS, variable_pointer) #define FOR_EACH_VK_FEATURE_1_2(FEATURE) \ + FEATURE(EXT, DescriptorIndexing, DESCRIPTOR_INDEXING, descriptor_indexing) \ FEATURE(EXT, HostQueryReset, HOST_QUERY_RESET, host_query_reset) \ FEATURE(KHR, 8BitStorage, 8BIT_STORAGE, bit8_storage) \ FEATURE(KHR, TimelineSemaphore, TIMELINE_SEMAPHORE, timeline_semaphore) @@ -355,6 +356,13 @@ public: return GetDriverID() != VK_DRIVER_ID_QUALCOMM_PROPRIETARY; } + bool IsDescriptorNonUniformIndexingSupported() const { + return features.descriptor_indexing.shaderSampledImageArrayNonUniformIndexing && + features.descriptor_indexing.shaderStorageImageArrayNonUniformIndexing && + features.descriptor_indexing.shaderUniformTexelBufferArrayNonUniformIndexing && + features.descriptor_indexing.shaderStorageTexelBufferArrayNonUniformIndexing; + } + /// Returns true if the device supports float64 natively. bool IsFloat64Supported() const { return features.features.shaderFloat64;