diff --git a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index a91a82d1b..e6a9bff4c 100644 --- a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -223,22 +223,29 @@ namespace Ryujinx.Graphics.Vulkan if (_dirty.HasFlag(DirtyFlags.Uniform)) { - UpdateAndBind(cbs, PipelineBase.UniformSetIndex, DirtyFlags.Uniform, pbp); + if (_program.UsePushDescriptors) + { + UpdateAndBindUniformBufferPd(cbs, pbp); + } + else + { + UpdateAndBind(cbs, PipelineBase.UniformSetIndex, pbp); + } } if (_dirty.HasFlag(DirtyFlags.Storage)) { - UpdateAndBind(cbs, PipelineBase.StorageSetIndex, DirtyFlags.Storage, pbp); + UpdateAndBind(cbs, PipelineBase.StorageSetIndex, pbp); } if (_dirty.HasFlag(DirtyFlags.Texture)) { - UpdateAndBind(cbs, PipelineBase.TextureSetIndex, DirtyFlags.Texture, pbp); + UpdateAndBind(cbs, PipelineBase.TextureSetIndex, pbp); } if (_dirty.HasFlag(DirtyFlags.Image)) { - UpdateAndBind(cbs, PipelineBase.ImageSetIndex, DirtyFlags.Image, pbp); + UpdateAndBind(cbs, PipelineBase.ImageSetIndex, pbp); } _dirty = DirtyFlags.None; @@ -263,7 +270,7 @@ namespace Ryujinx.Graphics.Vulkan } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void UpdateAndBind(CommandBufferScoped cbs, int setIndex, DirtyFlags flag, PipelineBindPoint pbp) + private void UpdateAndBind(CommandBufferScoped cbs, int setIndex, PipelineBindPoint pbp) { var program = _program; int stagesCount = program.Bindings[setIndex].Length; @@ -402,6 +409,77 @@ namespace Ryujinx.Graphics.Vulkan _gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan.Empty); } + private unsafe void UpdateBuffers( + CommandBufferScoped cbs, + PipelineBindPoint pbp, + int baseBinding, + ReadOnlySpan bufferInfo, + DescriptorType type) + { + if (bufferInfo.Length == 0) + { + return; + } + + fixed (DescriptorBufferInfo* pBufferInfo = bufferInfo) + { + var writeDescriptorSet = new WriteDescriptorSet + { + SType = StructureType.WriteDescriptorSet, + DstBinding = (uint)baseBinding, + DescriptorType = type, + DescriptorCount = (uint)bufferInfo.Length, + PBufferInfo = pBufferInfo + }; + + _gd.PushDescriptorApi.CmdPushDescriptorSet(cbs.CommandBuffer, pbp, _program.PipelineLayout, 0, 1, &writeDescriptorSet); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs, PipelineBindPoint pbp) + { + var dummyBuffer = _dummyBuffer?.GetBuffer(); + int stagesCount = _program.Bindings[PipelineBase.UniformSetIndex].Length; + + Span uniformBuffer = stackalloc DescriptorBufferInfo[1]; + + uniformBuffer[0] = new DescriptorBufferInfo() + { + Offset = 0, + Range = (ulong)SupportBuffer.RequiredSize, + Buffer = _gd.BufferManager.GetBuffer(cbs.CommandBuffer, _pipeline.SupportBufferUpdater.Handle, false).Get(cbs, 0, SupportBuffer.RequiredSize).Value + }; + + UpdateBuffers(cbs, pbp, 0, uniformBuffer, DescriptorType.UniformBuffer); + + for (int stageIndex = 0; stageIndex < stagesCount; stageIndex++) + { + var stageBindings = _program.Bindings[PipelineBase.UniformSetIndex][stageIndex]; + int bindingsCount = stageBindings.Length; + int count; + + for (int bindingIndex = 0; bindingIndex < bindingsCount; bindingIndex += count) + { + int binding = stageBindings[bindingIndex]; + count = 1; + + while (bindingIndex + count < bindingsCount && stageBindings[bindingIndex + count] == binding + count) + { + count++; + } + + for (int i = 0; i < count; i++) + { + UpdateBuffer(cbs, ref _uniformBuffers[binding + i], _uniformBufferRefs[binding + i], dummyBuffer); + } + + ReadOnlySpan uniformBuffers = _uniformBuffers; + UpdateBuffers(cbs, pbp, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer); + } + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Initialize(CommandBufferScoped cbs, int setIndex, DescriptorSetCollection dsc) { diff --git a/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs b/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs index 15a24fa51..096be455e 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineLayoutCache.cs @@ -22,11 +22,11 @@ namespace Ryujinx.Graphics.Vulkan return plce; } - public PipelineLayoutCacheEntry GetOrCreate(VulkanGraphicsDevice gd, Device device, uint stages) + public PipelineLayoutCacheEntry GetOrCreate(VulkanGraphicsDevice gd, Device device, uint stages, bool usePd) { if (_plce[stages] == null) { - _plce[stages] = new PipelineLayoutCacheEntry(gd, device, stages); + _plce[stages] = new PipelineLayoutCacheEntry(gd, device, stages, usePd); } return _plce[stages]; diff --git a/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs b/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs index d86d04faa..e25c311fb 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineLayoutCacheEntry.cs @@ -36,9 +36,9 @@ namespace Ryujinx.Graphics.Vulkan _dsCacheCursor = new int[PipelineBase.DescriptorSetLayouts]; } - public PipelineLayoutCacheEntry(VulkanGraphicsDevice gd, Device device, uint stages) : this(gd, device) + public PipelineLayoutCacheEntry(VulkanGraphicsDevice gd, Device device, uint stages, bool usePd) : this(gd, device) { - DescriptorSetLayouts = PipelineLayoutFactory.Create(gd, device, stages, out var pipelineLayout); + DescriptorSetLayouts = PipelineLayoutFactory.Create(gd, device, stages, usePd, out var pipelineLayout); PipelineLayout = pipelineLayout; } diff --git a/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs b/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs index a6368fe55..d37a4bc14 100644 --- a/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs +++ b/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs @@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.Vulkan ShaderStageFlags.ShaderStageFragmentBit | ShaderStageFlags.ShaderStageComputeBit; - public static unsafe DescriptorSetLayout[] Create(VulkanGraphicsDevice gd, Device device, uint stages, out PipelineLayout layout) + public static unsafe DescriptorSetLayout[] Create(VulkanGraphicsDevice gd, Device device, uint stages, bool usePd, out PipelineLayout layout) { int stagesCount = BitOperations.PopCount(stages); @@ -92,7 +92,8 @@ namespace Ryujinx.Graphics.Vulkan { SType = StructureType.DescriptorSetLayoutCreateInfo, PBindings = uLayoutBindings, - BindingCount = (uint)uCount + BindingCount = (uint)uCount, + Flags = usePd ? DescriptorSetLayoutCreateFlags.DescriptorSetLayoutCreatePushDescriptorBitKhr : 0 }; var sDescriptorSetLayoutCreateInfo = new DescriptorSetLayoutCreateInfo() diff --git a/Ryujinx.Graphics.Vulkan/ShaderCollection.cs b/Ryujinx.Graphics.Vulkan/ShaderCollection.cs index da765b847..cd911ce23 100644 --- a/Ryujinx.Graphics.Vulkan/ShaderCollection.cs +++ b/Ryujinx.Graphics.Vulkan/ShaderCollection.cs @@ -18,6 +18,7 @@ namespace Ryujinx.Graphics.Vulkan public PipelineLayout PipelineLayout => _plce.PipelineLayout; public bool HasMinimalLayout { get; } + public bool UsePushDescriptors { get; } public uint Stages { get; } @@ -89,11 +90,14 @@ namespace Ryujinx.Graphics.Vulkan _shaders = internalShaders; + bool usePd = !isMinimal && VulkanConfiguration.UsePushDescriptors; + _plce = isMinimal ? gd.PipelineLayoutCache.Create(gd, device, shaders) - : gd.PipelineLayoutCache.GetOrCreate(gd, device, stages); + : gd.PipelineLayoutCache.GetOrCreate(gd, device, stages, usePd); HasMinimalLayout = isMinimal; + UsePushDescriptors = usePd; Stages = stages; diff --git a/Ryujinx.Graphics.Vulkan/VulkanConfiguration.cs b/Ryujinx.Graphics.Vulkan/VulkanConfiguration.cs index a91f1330f..c3bc65dd9 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanConfiguration.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanConfiguration.cs @@ -7,6 +7,7 @@ public const bool UseFastBufferUpdates = true; public const bool UseGranularBufferTracking = true; public const bool UseSlowSafeBlitOnAmd = true; + public const bool UsePushDescriptors = false; public const bool ForceD24S8Unsupported = false; } diff --git a/Ryujinx.Graphics.Vulkan/VulkanGraphicsDevice.cs b/Ryujinx.Graphics.Vulkan/VulkanGraphicsDevice.cs index 3dc314cbc..a85a12dd5 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanGraphicsDevice.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanGraphicsDevice.cs @@ -31,6 +31,7 @@ namespace Ryujinx.Graphics.Vulkan internal KhrSwapchain SwapchainApi { get; private set; } internal ExtConditionalRendering ConditionalRenderingApi { get; private set; } internal ExtExtendedDynamicState ExtendedDynamicStateApi { get; private set; } + internal KhrPushDescriptor PushDescriptorApi { get; private set; } internal ExtTransformFeedback TransformFeedbackApi { get; private set; } internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; } internal ExtDebugReport DebugReportApi { get; private set; } @@ -137,6 +138,11 @@ namespace Ryujinx.Graphics.Vulkan ExtendedDynamicStateApi = extendedDynamicStateApi; } + if (Api.TryGetDeviceExtension(_instance, _device, out KhrPushDescriptor pushDescriptorApi)) + { + PushDescriptorApi = pushDescriptorApi; + } + if (Api.TryGetDeviceExtension(_instance, _device, out ExtTransformFeedback transformFeedbackApi)) { TransformFeedbackApi = transformFeedbackApi; diff --git a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index 52f6e774f..8b71a8f6a 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -22,14 +22,15 @@ namespace Ryujinx.Graphics.Vulkan ExtConditionalRendering.ExtensionName, ExtExtendedDynamicState.ExtensionName, KhrDrawIndirectCount.ExtensionName, + KhrPushDescriptor.ExtensionName, "VK_EXT_custom_border_color", + "VK_EXT_descriptor_indexing", // Enabling this works around an issue with disposed buffer bindings on RADV. "VK_EXT_fragment_shader_interlock", "VK_EXT_index_type_uint8", "VK_EXT_robustness2", "VK_EXT_shader_subgroup_ballot", "VK_EXT_subgroup_size_control", - "VK_NV_geometry_shader_passthrough", - "VK_EXT_descriptor_indexing" // Enabling this works around an issue with disposed buffer bindings on RADV. + "VK_NV_geometry_shader_passthrough" }; public static string[] RequiredExtensions { get; } = new string[]