diff --git a/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs b/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs index 245914e0a..c57cb1a95 100644 --- a/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs +++ b/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs @@ -1,5 +1,6 @@ using Silk.NET.Vulkan; using System; +using VkBuffer = Silk.NET.Vulkan.Buffer; namespace Ryujinx.Graphics.Vulkan { @@ -15,17 +16,15 @@ namespace Ryujinx.Graphics.Vulkan _descriptorSets = descriptorSets; } - public void InitializeBuffers(int setIndex, int baseBinding, int countPerUnit, DescriptorType type) + public void InitializeBuffers(int setIndex, int baseBinding, int countPerUnit, DescriptorType type, VkBuffer dummyBuffer) { Span infos = stackalloc DescriptorBufferInfo[countPerUnit]; - for (int j = 0; j < countPerUnit; j++) + infos.Fill(new DescriptorBufferInfo() { - infos[j] = new DescriptorBufferInfo() - { - Range = Vk.WholeSize - }; - } + Buffer = dummyBuffer, + Range = Vk.WholeSize + }); UpdateBuffers(setIndex, baseBinding, infos, type); } diff --git a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index 94f4b9b6e..cb8403e14 100644 --- a/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -45,6 +45,7 @@ namespace Ryujinx.Graphics.Vulkan private DirtyFlags _dirty; + private readonly BufferHolder _dummyBuffer; private readonly TextureView _dummyTexture; private readonly SamplerHolder _dummySampler; @@ -62,6 +63,17 @@ namespace Ryujinx.Graphics.Vulkan _bufferTextures = Array.Empty(); _bufferImages = Array.Empty(); + if (gd.Capabilities.SupportsNullDescriptors) + { + // If null descriptors are supported, we can pass null as the handle. + _dummyBuffer = null; + } + else + { + // If null descriptors are not supported, we need to pass the handle of a dummy buffer on unused bindings. + _dummyBuffer = gd.BufferManager.Create(gd, 0x10000, forConditionalRendering: false, deviceLocal: true); + } + _dummyTexture = (TextureView)gd.CreateTexture(new GAL.TextureCreateInfo( 1, 1, @@ -283,13 +295,18 @@ namespace Ryujinx.Graphics.Vulkan } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void UpdateBuffer(CommandBufferScoped cbs, ref DescriptorBufferInfo info, Auto buffer) + private static void UpdateBuffer( + CommandBufferScoped cbs, + ref DescriptorBufferInfo info, + Auto buffer, + Auto dummyBuffer) { info.Buffer = buffer?.Get(cbs, (int)info.Offset, (int)info.Range).Value ?? default; // The spec requires that buffers with null handle have offset as 0 and range as VK_WHOLE_SIZE. if (info.Buffer.Handle == 0) { + info.Buffer = dummyBuffer?.Get(cbs).Value ?? default; info.Offset = 0; info.Range = Vk.WholeSize; } @@ -304,6 +321,8 @@ namespace Ryujinx.Graphics.Vulkan return; } + var dummyBuffer = _dummyBuffer?.GetBuffer(); + var dsc = _program.GetNewDescriptorSetCollection(_gd, cbs.CommandBufferIndex, setIndex, out var isNew).Get(cbs); if (isNew) @@ -352,7 +371,7 @@ namespace Ryujinx.Graphics.Vulkan for (int i = 0; i < count; i++) { - UpdateBuffer(cbs, ref _uniformBuffers[binding + i], _uniformBufferRefs[binding + i]); + UpdateBuffer(cbs, ref _uniformBuffers[binding + i], _uniformBufferRefs[binding + i], dummyBuffer); } ReadOnlySpan uniformBuffers = _uniformBuffers; @@ -369,7 +388,7 @@ namespace Ryujinx.Graphics.Vulkan for (int i = 0; i < count; i++) { - UpdateBuffer(cbs, ref _storageBuffers[binding + i], _storageBufferRefs[binding + i]); + UpdateBuffer(cbs, ref _storageBuffers[binding + i], _storageBufferRefs[binding + i], dummyBuffer); } ReadOnlySpan storageBuffers = _storageBuffers; @@ -461,6 +480,8 @@ namespace Ryujinx.Graphics.Vulkan [MethodImpl(MethodImplOptions.AggressiveInlining)] private void Initialize(CommandBufferScoped cbs, int setIndex, DescriptorSetCollection dsc) { + var dummyBuffer = _dummyBuffer?.GetBuffer().Get(cbs).Value ?? default; + uint stages = _program.Stages; while (stages != 0) @@ -470,11 +491,21 @@ namespace Ryujinx.Graphics.Vulkan if (setIndex == PipelineBase.UniformSetIndex) { - dsc.InitializeBuffers(0, 1 + stage * Constants.MaxUniformBuffersPerStage, Constants.MaxUniformBuffersPerStage, DescriptorType.UniformBuffer); + dsc.InitializeBuffers( + 0, + 1 + stage * Constants.MaxUniformBuffersPerStage, + Constants.MaxUniformBuffersPerStage, + DescriptorType.UniformBuffer, + dummyBuffer); } else if (setIndex == PipelineBase.StorageSetIndex) { - dsc.InitializeBuffers(0, stage * Constants.MaxStorageBuffersPerStage, Constants.MaxStorageBuffersPerStage, DescriptorType.StorageBuffer); + dsc.InitializeBuffers( + 0, + stage * Constants.MaxStorageBuffersPerStage, + Constants.MaxStorageBuffersPerStage, + DescriptorType.StorageBuffer, + dummyBuffer); } } } diff --git a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs index efcd96615..b6a7d0d63 100644 --- a/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs +++ b/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs @@ -6,6 +6,7 @@ namespace Ryujinx.Graphics.Vulkan { public bool SupportsConditionalRendering { get; } public bool SupportsExtendedDynamicState { get; } + public bool SupportsNullDescriptors { get; } public bool SupportsTransformFeedback { get; } public bool SupportsTransformFeedbackQueries { get; } public bool SupportsGeometryShader { get; } @@ -16,6 +17,7 @@ namespace Ryujinx.Graphics.Vulkan public HardwareCapabilities( bool supportsConditionalRendering, bool supportsExtendedDynamicState, + bool supportsNullDescriptors, bool supportsTransformFeedback, bool supportsTransformFeedbackQueries, bool supportsGeometryShader, @@ -25,6 +27,7 @@ namespace Ryujinx.Graphics.Vulkan { SupportsConditionalRendering = supportsConditionalRendering; SupportsExtendedDynamicState = supportsExtendedDynamicState; + SupportsNullDescriptors = supportsNullDescriptors; SupportsTransformFeedback = supportsTransformFeedback; SupportsTransformFeedbackQueries = supportsTransformFeedbackQueries; SupportsGeometryShader = supportsGeometryShader; diff --git a/Ryujinx.Graphics.Vulkan/VulkanGraphicsDevice.cs b/Ryujinx.Graphics.Vulkan/VulkanGraphicsDevice.cs index 30fa8626d..60c2ffafd 100644 --- a/Ryujinx.Graphics.Vulkan/VulkanGraphicsDevice.cs +++ b/Ryujinx.Graphics.Vulkan/VulkanGraphicsDevice.cs @@ -195,9 +195,27 @@ namespace Ryujinx.Graphics.Vulkan Api.GetPhysicalDeviceProperties2(_physicalDevice, &properties2); + PhysicalDeviceFeatures2 features2 = new PhysicalDeviceFeatures2() + { + SType = StructureType.PhysicalDeviceFeatures2 + }; + + PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() + { + SType = StructureType.PhysicalDeviceRobustness2FeaturesExt + }; + + if (supportedExtensions.Contains("VK_EXT_robustness2")) + { + features2.PNext = &featuresRobustness2; + } + + Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2); + Capabilities = new HardwareCapabilities( supportedExtensions.Contains(ExtConditionalRendering.ExtensionName), supportedExtensions.Contains(ExtExtendedDynamicState.ExtensionName), + featuresRobustness2.NullDescriptor, supportsTransformFeedback, propertiesTransformFeedback.TransformFeedbackQueries, supportedFeatures.GeometryShader, @@ -392,7 +410,7 @@ namespace Ryujinx.Graphics.Vulkan { uint driverVersionRaw = properties.DriverVersion; - // NVIDIA differ from the standard here and use a different format. + // NVIDIA differ from the standard here and uses a different format. if (properties.VendorID == 0x10DE) { return $"{(driverVersionRaw >> 22) & 0x3FF}.{(driverVersionRaw >> 14) & 0xFF}.{(driverVersionRaw >> 6) & 0xFF}.{driverVersionRaw & 0x3F}";