diff --git a/Directory.Packages.props b/Directory.Packages.props index 8a9fdc3be5..301024cf8a 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -39,9 +39,9 @@ - - - + + + @@ -49,4 +49,4 @@ - + \ No newline at end of file diff --git a/src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs b/src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs index 7fe2a4f024..a9163f3485 100644 --- a/src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs +++ b/src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs @@ -1,13 +1,33 @@ +using Ryujinx.Common.Utilities; using System; namespace Ryujinx.Common.GraphicsDriver { public static class DriverUtilities { + private static void AddMesaFlags(string envVar, string newFlags) + { + string existingFlags = Environment.GetEnvironmentVariable(envVar); + + string flags = existingFlags == null ? newFlags : $"{existingFlags},{newFlags}"; + + OsUtils.SetEnvironmentVariableNoCaching(envVar, flags); + } + + public static void InitDriverConfig(bool oglThreading) + { + if (OperatingSystem.IsLinux()) + { + AddMesaFlags("RADV_DEBUG", "nodcc"); + } + + ToggleOGLThreading(oglThreading); + } + public static void ToggleOGLThreading(bool enabled) { - Environment.SetEnvironmentVariable("mesa_glthread", enabled.ToString().ToLower()); - Environment.SetEnvironmentVariable("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0"); + OsUtils.SetEnvironmentVariableNoCaching("mesa_glthread", enabled.ToString().ToLower()); + OsUtils.SetEnvironmentVariableNoCaching("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0"); try { diff --git a/src/Ryujinx.Common/Utilities/OsUtils.cs b/src/Ryujinx.Common/Utilities/OsUtils.cs new file mode 100644 index 0000000000..a0791b0924 --- /dev/null +++ b/src/Ryujinx.Common/Utilities/OsUtils.cs @@ -0,0 +1,24 @@ +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace Ryujinx.Common.Utilities +{ + public partial class OsUtils + { + [LibraryImport("libc", SetLastError = true)] + private static partial int setenv([MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value, int overwrite); + + public static void SetEnvironmentVariableNoCaching(string key, string value) + { + // Set the value in the cached environment variables, too. + Environment.SetEnvironmentVariable(key, value); + + if (!OperatingSystem.IsWindows()) + { + int res = setenv(key, value, 1); + Debug.Assert(res != -1); + } + } + } +} diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs index dd55e7d1d6..35051c6e03 100644 --- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs +++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs @@ -415,7 +415,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed #pragma warning disable CS0649 // Field is never assigned to public int Width; public int Height; - public int Depth; + public ushort Depth; + public ushort Flags; + + public readonly bool UnpackIsLayered() + { + return (Flags & 1) == 0; + } #pragma warning restore CS0649 } diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index b9ff060e25..b6fa842e35 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -468,13 +468,11 @@ namespace Ryujinx.Graphics.Gpu.Image int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY(); int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ(); + layered &= size.UnpackIsLayered(); + Target target; - if (dsState.MemoryLayout.UnpackIsTarget3D()) - { - target = Target.Texture3D; - } - else if ((samplesInX | samplesInY) != 1) + if ((samplesInX | samplesInY) != 1) { target = size.Depth > 1 && layered ? Target.Texture2DMultisampleArray diff --git a/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs b/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs index a6a006bb9e..bcfb3dbfe5 100644 --- a/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs +++ b/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs @@ -32,10 +32,12 @@ namespace Ryujinx.Graphics.Vulkan CommandBuffer } + private bool _feedbackLoopActive; private PipelineStageFlags _incoherentBufferWriteStages; private PipelineStageFlags _incoherentTextureWriteStages; private PipelineStageFlags _extraStages; private IncoherentBarrierType _queuedIncoherentBarrier; + private bool _queuedFeedbackLoopBarrier; public BarrierBatch(VulkanRenderer gd) { @@ -53,17 +55,6 @@ namespace Ryujinx.Graphics.Vulkan stages |= PipelineStageFlags.TransformFeedbackBitExt; } - if (!gd.IsTBDR) - { - // Desktop GPUs can transform image barriers into memory barriers. - - access |= AccessFlags.DepthStencilAttachmentWriteBit | AccessFlags.ColorAttachmentWriteBit; - access |= AccessFlags.DepthStencilAttachmentReadBit | AccessFlags.ColorAttachmentReadBit; - - stages |= PipelineStageFlags.EarlyFragmentTestsBit | PipelineStageFlags.LateFragmentTestsBit; - stages |= PipelineStageFlags.ColorAttachmentOutputBit; - } - return (access, stages); } @@ -178,16 +169,34 @@ namespace Ryujinx.Graphics.Vulkan } _queuedIncoherentBarrier = IncoherentBarrierType.None; + _queuedFeedbackLoopBarrier = false; } + else if (_feedbackLoopActive && _queuedFeedbackLoopBarrier) + { + // Feedback loop barrier. + + MemoryBarrier barrier = new MemoryBarrier() + { + SType = StructureType.MemoryBarrier, + SrcAccessMask = AccessFlags.ShaderWriteBit, + DstAccessMask = AccessFlags.ShaderReadBit + }; + + QueueBarrier(barrier, PipelineStageFlags.FragmentShaderBit, PipelineStageFlags.AllGraphicsBit); + + _queuedFeedbackLoopBarrier = false; + } + + _feedbackLoopActive = false; } } public unsafe void Flush(CommandBufferScoped cbs, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass) { - Flush(cbs, null, inRenderPass, rpHolder, endRenderPass); + Flush(cbs, null, false, inRenderPass, rpHolder, endRenderPass); } - public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass) + public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool feedbackLoopActive, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass) { if (program != null) { @@ -195,6 +204,8 @@ namespace Ryujinx.Graphics.Vulkan _incoherentTextureWriteStages |= program.IncoherentTextureWriteStages; } + _feedbackLoopActive |= feedbackLoopActive; + FlushMemoryBarrier(program, inRenderPass); if (!inRenderPass && rpHolder != null) @@ -406,6 +417,8 @@ namespace Ryujinx.Graphics.Vulkan { _queuedIncoherentBarrier = type; } + + _queuedFeedbackLoopBarrier = true; } public void QueueTextureBarrier() diff --git a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs index 3dcbc3130b..e840fdc02b 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs @@ -122,7 +122,7 @@ namespace Ryujinx.Graphics.Vulkan Range = (uint)size, }; - _gd.Api.CreateBufferView(_device, bufferViewCreateInfo, null, out var bufferView).ThrowOnError(); + _gd.Api.CreateBufferView(_device, in bufferViewCreateInfo, null, out var bufferView).ThrowOnError(); return new Auto(new DisposableBufferView(_gd.Api, _device, bufferView), this, _waitable, _buffer); } @@ -153,7 +153,7 @@ namespace Ryujinx.Graphics.Vulkan PipelineStageFlags.AllCommandsBit, DependencyFlags.DeviceGroupBit, 1, - memoryBarrier, + in memoryBarrier, 0, null, 0, @@ -770,7 +770,7 @@ namespace Ryujinx.Graphics.Vulkan 0, null, 1, - memoryBarrier, + in memoryBarrier, 0, null); } diff --git a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs index 1b6ac99880..7523913ec0 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs @@ -221,7 +221,7 @@ namespace Ryujinx.Graphics.Vulkan PBufferBinds = &bufferBind }; - gd.Api.QueueBindSparse(gd.Queue, 1, bindSparseInfo, default).ThrowOnError(); + gd.Api.QueueBindSparse(gd.Queue, 1, in bindSparseInfo, default).ThrowOnError(); } var holder = new BufferHolder(gd, _device, buffer, (int)size, storageAllocations); diff --git a/src/Ryujinx.Graphics.Vulkan/BufferState.cs b/src/Ryujinx.Graphics.Vulkan/BufferState.cs index d585dd53cc..e49df765d1 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferState.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferState.cs @@ -25,7 +25,10 @@ namespace Ryujinx.Graphics.Vulkan { var buffer = _buffer.Get(cbs, _offset, _size, true).Value; - gd.TransformFeedbackApi.CmdBindTransformFeedbackBuffers(cbs.CommandBuffer, binding, 1, buffer, (ulong)_offset, (ulong)_size); + ulong offset = (ulong)_offset; + ulong size = (ulong)_size; + + gd.TransformFeedbackApi.CmdBindTransformFeedbackBuffers(cbs.CommandBuffer, binding, 1, in buffer, in offset, in size); } } diff --git a/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs b/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs index e3938392f2..e1fd3fb9dc 100644 --- a/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs +++ b/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs @@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.Vulkan Level = CommandBufferLevel.Primary, }; - api.AllocateCommandBuffers(device, allocateInfo, out CommandBuffer); + api.AllocateCommandBuffers(device, in allocateInfo, out CommandBuffer); Dependants = new List(); Waitables = new List(); @@ -83,7 +83,7 @@ namespace Ryujinx.Graphics.Vulkan CommandPoolCreateFlags.ResetCommandBufferBit, }; - api.CreateCommandPool(device, commandPoolCreateInfo, null, out _pool).ThrowOnError(); + api.CreateCommandPool(device, in commandPoolCreateInfo, null, out _pool).ThrowOnError(); // We need at least 2 command buffers to get texture data in some cases. _totalCommandBuffers = isLight ? 2 : MaxCommandBuffers; @@ -253,7 +253,7 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.CommandBufferBeginInfo, }; - _api.BeginCommandBuffer(entry.CommandBuffer, commandBufferBeginInfo).ThrowOnError(); + _api.BeginCommandBuffer(entry.CommandBuffer, in commandBufferBeginInfo).ThrowOnError(); return new CommandBufferScoped(this, entry.CommandBuffer, cursor); } @@ -311,7 +311,7 @@ namespace Ryujinx.Graphics.Vulkan lock (_queueLock) { - _api.QueueSubmit(_queue, 1, sInfo, entry.Fence.GetUnsafe()).ThrowOnError(); + _api.QueueSubmit(_queue, 1, in sInfo, entry.Fence.GetUnsafe()).ThrowOnError(); } } } diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs index 846dd5c7d5..40fc01b247 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs @@ -43,7 +43,7 @@ namespace Ryujinx.Graphics.Vulkan PBufferInfo = &bufferInfo, }; - _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); + _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null); } } @@ -66,7 +66,7 @@ namespace Ryujinx.Graphics.Vulkan PBufferInfo = pBufferInfo, }; - _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); + _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null); } } @@ -84,7 +84,7 @@ namespace Ryujinx.Graphics.Vulkan PImageInfo = &imageInfo, }; - _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); + _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null); } } @@ -107,7 +107,7 @@ namespace Ryujinx.Graphics.Vulkan PImageInfo = pImageInfo, }; - _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); + _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null); } } @@ -144,7 +144,7 @@ namespace Ryujinx.Graphics.Vulkan PImageInfo = pImageInfo, }; - _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); + _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null); i += count - 1; } @@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.Vulkan PTexelBufferView = &texelBufferView, }; - _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); + _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null); } } @@ -200,7 +200,7 @@ namespace Ryujinx.Graphics.Vulkan PTexelBufferView = pTexelBufferView + i, }; - _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null); + _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null); } i += count; diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs index 707ae12922..97669942cb 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs @@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Vulkan PPoolSizes = pPoolsSize, }; - Api.CreateDescriptorPool(device, descriptorPoolCreateInfo, null, out _pool).ThrowOnError(); + Api.CreateDescriptorPool(device, in descriptorPoolCreateInfo, null, out _pool).ThrowOnError(); } } diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs index 563fdafd3c..298526d51e 100644 --- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs +++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs @@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader; using Silk.NET.Vulkan; using System; using System.Buffers; +using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using CompareOp = Ryujinx.Graphics.GAL.CompareOp; @@ -42,15 +43,15 @@ namespace Ryujinx.Graphics.Vulkan private record struct TextureRef { public ShaderStage Stage; - public TextureStorage Storage; - public Auto View; + public TextureView View; + public Auto ImageView; public Auto Sampler; - public TextureRef(ShaderStage stage, TextureStorage storage, Auto view, Auto sampler) + public TextureRef(ShaderStage stage, TextureView view, Auto imageView, Auto sampler) { Stage = stage; - Storage = storage; View = view; + ImageView = imageView; Sampler = sampler; } } @@ -58,14 +59,14 @@ namespace Ryujinx.Graphics.Vulkan private record struct ImageRef { public ShaderStage Stage; - public TextureStorage Storage; - public Auto View; + public TextureView View; + public Auto ImageView; - public ImageRef(ShaderStage stage, TextureStorage storage, Auto view) + public ImageRef(ShaderStage stage, TextureView view, Auto imageView) { Stage = stage; - Storage = storage; View = view; + ImageView = imageView; } } @@ -124,6 +125,8 @@ namespace Ryujinx.Graphics.Vulkan private readonly TextureView _dummyTexture; private readonly SamplerHolder _dummySampler; + public List FeedbackLoopHazards { get; private set; } + public DescriptorSetUpdater(VulkanRenderer gd, Device device) { _gd = gd; @@ -209,10 +212,15 @@ namespace Ryujinx.Graphics.Vulkan _templateUpdater = new(); } - public void Initialize() + public void Initialize(bool isMainPipeline) { MemoryOwner dummyTextureData = MemoryOwner.RentCleared(4); _dummyTexture.SetData(dummyTextureData); + + if (isMainPipeline) + { + FeedbackLoopHazards = new(); + } } private static bool BindingOverlaps(ref DescriptorBufferInfo info, int bindingOffset, int offset, int size) @@ -275,6 +283,18 @@ namespace Ryujinx.Graphics.Vulkan public void InsertBindingBarriers(CommandBufferScoped cbs) { + if ((FeedbackLoopHazards?.Count ?? 0) > 0) + { + // Clear existing hazards - they will be rebuilt. + + foreach (TextureView hazard in FeedbackLoopHazards) + { + hazard.DecrementHazardUses(); + } + + FeedbackLoopHazards.Clear(); + } + foreach (ResourceBindingSegment segment in _program.BindingSegments[PipelineBase.TextureSetIndex]) { if (segment.Type == ResourceType.TextureAndSampler) @@ -284,7 +304,7 @@ namespace Ryujinx.Graphics.Vulkan for (int i = 0; i < segment.Count; i++) { ref var texture = ref _textureRefs[segment.Binding + i]; - texture.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, texture.Stage.ConvertToPipelineStageFlags()); + texture.View?.PrepareForUsage(cbs, texture.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards); } } else @@ -305,7 +325,7 @@ namespace Ryujinx.Graphics.Vulkan for (int i = 0; i < segment.Count; i++) { ref var image = ref _imageRefs[segment.Binding + i]; - image.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, image.Stage.ConvertToPipelineStageFlags()); + image.View?.PrepareForUsage(cbs, image.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards); } } else @@ -385,9 +405,12 @@ namespace Ryujinx.Graphics.Vulkan } else if (image is TextureView view) { - view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags()); + ref ImageRef iRef = ref _imageRefs[binding]; - _imageRefs[binding] = new(stage, view.Storage, view.GetView(imageFormat).GetIdentityImageView()); + iRef.View?.ClearUsage(FeedbackLoopHazards); + view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards); + + iRef = new(stage, view, view.GetView(imageFormat).GetIdentityImageView()); } else { @@ -486,9 +509,12 @@ namespace Ryujinx.Graphics.Vulkan } else if (texture is TextureView view) { - view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags()); + ref TextureRef iRef = ref _textureRefs[binding]; - _textureRefs[binding] = new(stage, view.Storage, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler()); + iRef.View?.ClearUsage(FeedbackLoopHazards); + view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards); + + iRef = new(stage, view, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler()); } else { @@ -510,7 +536,7 @@ namespace Ryujinx.Graphics.Vulkan { view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags()); - _textureRefs[binding] = new(stage, view.Storage, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler()); + _textureRefs[binding] = new(stage, view, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler()); SignalDirty(DirtyFlags.Texture); } @@ -836,7 +862,7 @@ namespace Ryujinx.Graphics.Vulkan ref var texture = ref textures[i]; ref var refs = ref _textureRefs[binding + i]; - texture.ImageView = refs.View?.Get(cbs).Value ?? default; + texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default; texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default; if (texture.ImageView.Handle == 0) @@ -886,7 +912,7 @@ namespace Ryujinx.Graphics.Vulkan for (int i = 0; i < count; i++) { - images[i].ImageView = _imageRefs[binding + i].View?.Get(cbs).Value ?? default; + images[i].ImageView = _imageRefs[binding + i].ImageView?.Get(cbs).Value ?? default; } tu.Push(images[..count]); @@ -957,7 +983,7 @@ namespace Ryujinx.Graphics.Vulkan ref var texture = ref textures[i]; ref var refs = ref _textureRefs[binding + i]; - texture.ImageView = refs.View?.Get(cbs).Value ?? default; + texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default; texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default; if (texture.ImageView.Handle == 0) diff --git a/src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs b/src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs new file mode 100644 index 0000000000..22f73679d8 --- /dev/null +++ b/src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs @@ -0,0 +1,12 @@ +using System; + +namespace Ryujinx.Graphics.Vulkan +{ + [Flags] + internal enum FeedbackLoopAspects + { + None = 0, + Color = 1 << 0, + Depth = 1 << 1, + } +} diff --git a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs index 5c5a8f3ad4..8d80e9d05e 100644 --- a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs +++ b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs @@ -250,7 +250,7 @@ namespace Ryujinx.Graphics.Vulkan Layers = Layers, }; - api.CreateFramebuffer(_device, framebufferCreateInfo, null, out var framebuffer).ThrowOnError(); + api.CreateFramebuffer(_device, in framebufferCreateInfo, null, out var framebuffer).ThrowOnError(); return new Auto(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments); } @@ -302,6 +302,27 @@ namespace Ryujinx.Graphics.Vulkan _depthStencil?.Storage?.AddStoreOpUsage(true); } + public void ClearBindings() + { + _depthStencil?.Storage.ClearBindings(); + + for (int i = 0; i < _colorsCanonical.Length; i++) + { + _colorsCanonical[i]?.Storage.ClearBindings(); + } + } + + public void AddBindings() + { + _depthStencil?.Storage.AddBinding(_depthStencil); + + for (int i = 0; i < _colorsCanonical.Length; i++) + { + TextureView color = _colorsCanonical[i]; + color?.Storage.AddBinding(color); + } + } + public (RenderPassHolder rpHolder, Auto framebuffer) GetPassAndFramebuffer( VulkanRenderer gd, Device device, diff --git a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs index b6694bcb36..bd17867b10 100644 --- a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs +++ b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs @@ -46,6 +46,8 @@ namespace Ryujinx.Graphics.Vulkan public readonly bool SupportsViewportArray2; public readonly bool SupportsHostImportedMemory; public readonly bool SupportsDepthClipControl; + public readonly bool SupportsAttachmentFeedbackLoop; + public readonly bool SupportsDynamicAttachmentFeedbackLoop; public readonly uint SubgroupSize; public readonly SampleCountFlags SupportedSampleCounts; public readonly PortabilitySubsetFlags PortabilitySubset; @@ -84,6 +86,8 @@ namespace Ryujinx.Graphics.Vulkan bool supportsViewportArray2, bool supportsHostImportedMemory, bool supportsDepthClipControl, + bool supportsAttachmentFeedbackLoop, + bool supportsDynamicAttachmentFeedbackLoop, uint subgroupSize, SampleCountFlags supportedSampleCounts, PortabilitySubsetFlags portabilitySubset, @@ -121,6 +125,8 @@ namespace Ryujinx.Graphics.Vulkan SupportsViewportArray2 = supportsViewportArray2; SupportsHostImportedMemory = supportsHostImportedMemory; SupportsDepthClipControl = supportsDepthClipControl; + SupportsAttachmentFeedbackLoop = supportsAttachmentFeedbackLoop; + SupportsDynamicAttachmentFeedbackLoop = supportsDynamicAttachmentFeedbackLoop; SubgroupSize = subgroupSize; SupportedSampleCounts = supportedSampleCounts; PortabilitySubset = portabilitySubset; diff --git a/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs b/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs index baccc698f2..ff15652467 100644 --- a/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs +++ b/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs @@ -115,7 +115,7 @@ namespace Ryujinx.Graphics.Vulkan PNext = &importInfo, }; - Result result = _api.AllocateMemory(_device, memoryAllocateInfo, null, out var deviceMemory); + Result result = _api.AllocateMemory(_device, in memoryAllocateInfo, null, out var deviceMemory); if (result < Result.Success) { diff --git a/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs b/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs index a1acc90f94..3d42ed7e2c 100644 --- a/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs +++ b/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs @@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Vulkan MemoryTypeIndex = (uint)MemoryTypeIndex, }; - _api.AllocateMemory(_device, memoryAllocateInfo, null, out var deviceMemory).ThrowOnError(); + _api.AllocateMemory(_device, in memoryAllocateInfo, null, out var deviceMemory).ThrowOnError(); IntPtr hostPointer = IntPtr.Zero; diff --git a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs index 457240aa08..930d6b5259 100644 --- a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs +++ b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs @@ -1,3 +1,4 @@ +using Silk.NET.Core.Loader; using Silk.NET.Vulkan; using System; using System.Runtime.InteropServices; @@ -8,6 +9,8 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK [SupportedOSPlatform("macos")] public static partial class MVKInitialization { + private const string VulkanLib = "libvulkan.dylib"; + [LibraryImport("libMoltenVK.dylib")] private static partial Result vkGetMoltenVKConfigurationMVK(IntPtr unusedInstance, out MVKConfiguration config, in IntPtr configSize); @@ -29,5 +32,20 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK vkSetMoltenVKConfigurationMVK(IntPtr.Zero, config, configSize); } + + private static string[] Resolver(string path) + { + if (path.EndsWith(VulkanLib)) + { + path = path[..^VulkanLib.Length] + "libMoltenVK.dylib"; + return [path]; + } + return Array.Empty(); + } + + public static void InitializeResolver() + { + ((DefaultPathResolver)PathResolver.Default).Resolvers.Insert(0, Resolver); + } } } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs index bda6167d7b..20c4b25726 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs @@ -2,6 +2,7 @@ using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Shader; using Silk.NET.Vulkan; using System; +using System.Collections.Generic; using System.Linq; using System.Numerics; using System.Runtime.CompilerServices; @@ -33,6 +34,7 @@ namespace Ryujinx.Graphics.Vulkan public readonly Action EndRenderPassDelegate; protected PipelineDynamicState DynamicState; + protected bool IsMainPipeline; private PipelineState _newState; private bool _graphicsStateDirty; private bool _computeStateDirty; @@ -85,6 +87,9 @@ namespace Ryujinx.Graphics.Vulkan private bool _tfEnabled; private bool _tfActive; + private FeedbackLoopAspects _feedbackLoop; + private bool _passWritesDepthStencil; + private readonly PipelineColorBlendAttachmentState[] _storedBlend; public ulong DrawCount { get; private set; } public bool RenderPassActive { get; private set; } @@ -102,7 +107,7 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.PipelineCacheCreateInfo, }; - gd.Api.CreatePipelineCache(device, pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError(); + gd.Api.CreatePipelineCache(device, in pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError(); _descriptorSetUpdater = new DescriptorSetUpdater(gd, device); _vertexBufferUpdater = new VertexBufferUpdater(gd); @@ -126,7 +131,7 @@ namespace Ryujinx.Graphics.Vulkan public void Initialize() { - _descriptorSetUpdater.Initialize(); + _descriptorSetUpdater.Initialize(IsMainPipeline); QuadsToTrisPattern = new IndexBufferPattern(Gd, 4, 6, 0, new[] { 0, 1, 2, 0, 2, 3 }, 4, false); TriFanToTrisPattern = new IndexBufferPattern(Gd, 3, 3, 2, new[] { int.MinValue, -1, 0 }, 1, true); @@ -814,6 +819,8 @@ namespace Ryujinx.Graphics.Vulkan _newState.DepthTestEnable = depthTest.TestEnable; _newState.DepthWriteEnable = depthTest.WriteEnable; _newState.DepthCompareOp = depthTest.Func.Convert(); + + UpdatePassDepthStencil(); SignalStateChange(); } @@ -1079,6 +1086,8 @@ namespace Ryujinx.Graphics.Vulkan _newState.StencilFrontPassOp = stencilTest.FrontDpPass.Convert(); _newState.StencilFrontDepthFailOp = stencilTest.FrontDpFail.Convert(); _newState.StencilFrontCompareOp = stencilTest.FrontFunc.Convert(); + + UpdatePassDepthStencil(); SignalStateChange(); } @@ -1426,7 +1435,23 @@ namespace Ryujinx.Graphics.Vulkan } } + if (IsMainPipeline) + { + FramebufferParams?.ClearBindings(); + } + FramebufferParams = new FramebufferParams(Device, colors, depthStencil); + + if (IsMainPipeline) + { + FramebufferParams.AddBindings(); + + _newState.FeedbackLoopAspects = FeedbackLoopAspects.None; + _bindingBarriersDirty = true; + } + + _passWritesDepthStencil = false; + UpdatePassDepthStencil(); UpdatePipelineAttachmentFormats(); } @@ -1493,11 +1518,82 @@ namespace Ryujinx.Graphics.Vulkan } } - Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate); + Gd.Barriers.Flush(Cbs, _program, _feedbackLoop != 0, RenderPassActive, _rpHolder, EndRenderPassDelegate); _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute); } + private bool ChangeFeedbackLoop(FeedbackLoopAspects aspects) + { + if (_feedbackLoop != aspects) + { + if (Gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop) + { + DynamicState.SetFeedbackLoop(aspects); + } + else + { + _newState.FeedbackLoopAspects = aspects; + } + + _feedbackLoop = aspects; + + return true; + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool UpdateFeedbackLoop() + { + List hazards = _descriptorSetUpdater.FeedbackLoopHazards; + + if ((hazards?.Count ?? 0) > 0) + { + FeedbackLoopAspects aspects = 0; + + foreach (TextureView view in hazards) + { + // May need to enforce feedback loop layout here in the future. + // Though technically, it should always work with the general layout. + + if (view.Info.Format.IsDepthOrStencil()) + { + if (_passWritesDepthStencil) + { + // If depth/stencil isn't written in the pass, it doesn't count as a feedback loop. + + aspects |= FeedbackLoopAspects.Depth; + } + } + else + { + aspects |= FeedbackLoopAspects.Color; + } + } + + return ChangeFeedbackLoop(aspects); + } + else if (_feedbackLoop != 0) + { + return ChangeFeedbackLoop(FeedbackLoopAspects.None); + } + + return false; + } + + private void UpdatePassDepthStencil() + { + if (!RenderPassActive) + { + _passWritesDepthStencil = false; + } + + // Stencil test being enabled doesn't necessarily mean a write, but it's not critical to check. + _passWritesDepthStencil |= (_newState.DepthTestEnable && _newState.DepthWriteEnable) || _newState.StencilTestEnable; + } + private bool RecreateGraphicsPipelineIfNeeded() { if (AutoFlush.ShouldFlushDraw(DrawCount)) @@ -1505,7 +1601,7 @@ namespace Ryujinx.Graphics.Vulkan Gd.FlushAllCommands(); } - DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer); + DynamicState.ReplayIfDirty(Gd, CommandBuffer); if (_needsIndexBufferRebind && _indexBufferPattern == null) { @@ -1539,7 +1635,15 @@ namespace Ryujinx.Graphics.Vulkan _vertexBufferUpdater.Commit(Cbs); } - if (_graphicsStateDirty || Pbp != PipelineBindPoint.Graphics) + if (_bindingBarriersDirty) + { + // Stale barriers may have been activated by switching program. Emit any that are relevant. + _descriptorSetUpdater.InsertBindingBarriers(Cbs); + + _bindingBarriersDirty = false; + } + + if (UpdateFeedbackLoop() || _graphicsStateDirty || Pbp != PipelineBindPoint.Graphics) { if (!CreatePipeline(PipelineBindPoint.Graphics)) { @@ -1548,17 +1652,9 @@ namespace Ryujinx.Graphics.Vulkan _graphicsStateDirty = false; Pbp = PipelineBindPoint.Graphics; - - if (_bindingBarriersDirty) - { - // Stale barriers may have been activated by switching program. Emit any that are relevant. - _descriptorSetUpdater.InsertBindingBarriers(Cbs); - - _bindingBarriersDirty = false; - } } - Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate); + Gd.Barriers.Flush(Cbs, _program, _feedbackLoop != 0, RenderPassActive, _rpHolder, EndRenderPassDelegate); _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics); @@ -1628,7 +1724,7 @@ namespace Ryujinx.Graphics.Vulkan ClearValueCount = 1, }; - Gd.Api.CmdBeginRenderPass(CommandBuffer, renderPassBeginInfo, SubpassContents.Inline); + Gd.Api.CmdBeginRenderPass(CommandBuffer, in renderPassBeginInfo, SubpassContents.Inline); RenderPassActive = true; } } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs index 89ce10b0aa..85069c6b27 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs @@ -116,7 +116,7 @@ namespace Ryujinx.Graphics.Vulkan DependencyCount = 1, }; - gd.Api.CreateRenderPass(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError(); + gd.Api.CreateRenderPass(device, in renderPassCreateInfo, null, out var renderPass).ThrowOnError(); return new DisposableRenderPass(gd.Api, device, renderPass); } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs index 1cc33f728c..ad26ff7b39 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Memory; using Silk.NET.Vulkan; +using Silk.NET.Vulkan.Extensions.EXT; namespace Ryujinx.Graphics.Vulkan { @@ -21,6 +22,8 @@ namespace Ryujinx.Graphics.Vulkan private Array4 _blendConstants; + private FeedbackLoopAspects _feedbackLoopAspects; + public uint ViewportsCount; public Array16 Viewports; @@ -32,7 +35,8 @@ namespace Ryujinx.Graphics.Vulkan Scissor = 1 << 2, Stencil = 1 << 3, Viewport = 1 << 4, - All = Blend | DepthBias | Scissor | Stencil | Viewport, + FeedbackLoop = 1 << 5, + All = Blend | DepthBias | Scissor | Stencil | Viewport | FeedbackLoop, } private DirtyFlags _dirty; @@ -99,13 +103,22 @@ namespace Ryujinx.Graphics.Vulkan } } + public void SetFeedbackLoop(FeedbackLoopAspects aspects) + { + _feedbackLoopAspects = aspects; + + _dirty |= DirtyFlags.FeedbackLoop; + } + public void ForceAllDirty() { _dirty = DirtyFlags.All; } - public void ReplayIfDirty(Vk api, CommandBuffer commandBuffer) + public void ReplayIfDirty(VulkanRenderer gd, CommandBuffer commandBuffer) { + Vk api = gd.Api; + if (_dirty.HasFlag(DirtyFlags.Blend)) { RecordBlend(api, commandBuffer); @@ -131,6 +144,11 @@ namespace Ryujinx.Graphics.Vulkan RecordViewport(api, commandBuffer); } + if (_dirty.HasFlag(DirtyFlags.FeedbackLoop) && gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop) + { + RecordFeedbackLoop(gd.DynamicFeedbackLoopApi, commandBuffer); + } + _dirty = DirtyFlags.None; } @@ -169,5 +187,17 @@ namespace Ryujinx.Graphics.Vulkan api.CmdSetViewport(commandBuffer, 0, ViewportsCount, Viewports.AsSpan()); } } + + private readonly void RecordFeedbackLoop(ExtAttachmentFeedbackLoopDynamicState api, CommandBuffer commandBuffer) + { + ImageAspectFlags aspects = (_feedbackLoopAspects & FeedbackLoopAspects.Color) != 0 ? ImageAspectFlags.ColorBit : 0; + + if ((_feedbackLoopAspects & FeedbackLoopAspects.Depth) != 0) + { + aspects |= ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit; + } + + api.CmdSetAttachmentFeedbackLoopEnable(commandBuffer, aspects); + } } } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs index cf65eefb0d..54d43bdba7 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs @@ -28,6 +28,8 @@ namespace Ryujinx.Graphics.Vulkan _activeBufferMirrors = new(); CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer; + + IsMainPipeline = true; } private void CopyPendingQuery() @@ -235,7 +237,7 @@ namespace Ryujinx.Graphics.Vulkan if (Pipeline != null && Pbp == PipelineBindPoint.Graphics) { - DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer); + DynamicState.ReplayIfDirty(Gd, CommandBuffer); } } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs index bca119f6ad..8d78156161 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs @@ -91,7 +91,7 @@ namespace Ryujinx.Graphics.Vulkan Flags = flags, }; - gd.Api.CreateDescriptorSetLayout(device, descriptorSetLayoutCreateInfo, null, out layouts[setIndex]).ThrowOnError(); + gd.Api.CreateDescriptorSetLayout(device, in descriptorSetLayoutCreateInfo, null, out layouts[setIndex]).ThrowOnError(); } } diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs index 6b6b46a914..a726b9edb5 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs @@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Vulkan struct PipelineState : IDisposable { private const int RequiredSubgroupSize = 32; + private const int MaxDynamicStatesCount = 9; public PipelineUid Internal; @@ -299,6 +300,12 @@ namespace Ryujinx.Graphics.Vulkan set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6); } + public FeedbackLoopAspects FeedbackLoopAspects + { + readonly get => (FeedbackLoopAspects)((Internal.Id8 >> 7) & 0x3); + set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFE7F) | (((ulong)value) << 7); + } + public bool HasTessellationControlShader; public NativeArray Stages; public PipelineLayout PipelineLayout; @@ -564,9 +571,11 @@ namespace Ryujinx.Graphics.Vulkan } bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState; - int dynamicStatesCount = supportsExtDynamicState ? 8 : 7; + bool supportsFeedbackLoopDynamicState = gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop; - DynamicState* dynamicStates = stackalloc DynamicState[dynamicStatesCount]; + DynamicState* dynamicStates = stackalloc DynamicState[MaxDynamicStatesCount]; + + int dynamicStatesCount = 7; dynamicStates[0] = DynamicState.Viewport; dynamicStates[1] = DynamicState.Scissor; @@ -578,7 +587,12 @@ namespace Ryujinx.Graphics.Vulkan if (supportsExtDynamicState) { - dynamicStates[7] = DynamicState.VertexInputBindingStrideExt; + dynamicStates[dynamicStatesCount++] = DynamicState.VertexInputBindingStrideExt; + } + + if (supportsFeedbackLoopDynamicState) + { + dynamicStates[dynamicStatesCount++] = DynamicState.AttachmentFeedbackLoopEnableExt; } var pipelineDynamicStateCreateInfo = new PipelineDynamicStateCreateInfo @@ -588,9 +602,27 @@ namespace Ryujinx.Graphics.Vulkan PDynamicStates = dynamicStates, }; + PipelineCreateFlags flags = 0; + + if (gd.Capabilities.SupportsAttachmentFeedbackLoop) + { + FeedbackLoopAspects aspects = FeedbackLoopAspects; + + if ((aspects & FeedbackLoopAspects.Color) != 0) + { + flags |= PipelineCreateFlags.CreateColorAttachmentFeedbackLoopBitExt; + } + + if ((aspects & FeedbackLoopAspects.Depth) != 0) + { + flags |= PipelineCreateFlags.CreateDepthStencilAttachmentFeedbackLoopBitExt; + } + } + var pipelineCreateInfo = new GraphicsPipelineCreateInfo { SType = StructureType.GraphicsPipelineCreateInfo, + Flags = flags, StageCount = StagesCount, PStages = Stages.Pointer, PVertexInputState = &vertexInputState, diff --git a/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs b/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs index 714cb2833c..c9a546648f 100644 --- a/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs +++ b/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs @@ -52,7 +52,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries PipelineStatistics = flags, }; - gd.Api.CreateQueryPool(device, queryPoolCreateInfo, null, out _queryPool).ThrowOnError(); + gd.Api.CreateQueryPool(device, in queryPoolCreateInfo, null, out _queryPool).ThrowOnError(); } var buffer = gd.BufferManager.Create(gd, sizeof(long), forConditionalRendering: true); diff --git a/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs b/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs index b2dd0dd874..a364c57163 100644 --- a/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs @@ -125,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan DependencyCount = 1, }; - gd.Api.CreateRenderPass(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError(); + gd.Api.CreateRenderPass(device, in renderPassCreateInfo, null, out var renderPass).ThrowOnError(); _renderPass = new Auto(new DisposableRenderPass(gd.Api, device, renderPass)); } diff --git a/src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs b/src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs index f67daeeccb..7f37ab1398 100644 --- a/src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs @@ -68,7 +68,7 @@ namespace Ryujinx.Graphics.Vulkan samplerCreateInfo.BorderColor = BorderColor.FloatCustomExt; } - gd.Api.CreateSampler(device, samplerCreateInfo, null, out var sampler).ThrowOnError(); + gd.Api.CreateSampler(device, in samplerCreateInfo, null, out var sampler).ThrowOnError(); _sampler = new Auto(new DisposableSampler(gd.Api, device, sampler)); } diff --git a/src/Ryujinx.Graphics.Vulkan/Shader.cs b/src/Ryujinx.Graphics.Vulkan/Shader.cs index 06f3499db9..1c8caffd9a 100644 --- a/src/Ryujinx.Graphics.Vulkan/Shader.cs +++ b/src/Ryujinx.Graphics.Vulkan/Shader.cs @@ -64,7 +64,7 @@ namespace Ryujinx.Graphics.Vulkan PCode = (uint*)pCode, }; - api.CreateShaderModule(device, shaderModuleCreateInfo, null, out _module).ThrowOnError(); + api.CreateShaderModule(device, in shaderModuleCreateInfo, null, out _module).ThrowOnError(); } CompileStatus = ProgramLinkStatus.Success; diff --git a/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs b/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs index fdc0a248bd..45cddd772f 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs @@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Vulkan DstOffsets = dstOffsets, }; - api.CmdBlitImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, region, filter); + api.CmdBlitImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, in region, filter); copySrcLevel++; copyDstLevel++; @@ -320,13 +320,13 @@ namespace Ryujinx.Graphics.Vulkan { var region = new ImageResolve(srcSl, new Offset3D(0, 0, srcZ), dstSl, new Offset3D(0, 0, dstZ), extent); - api.CmdResolveImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, region); + api.CmdResolveImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, in region); } else { var region = new ImageCopy(srcSl, new Offset3D(0, 0, srcZ), dstSl, new Offset3D(0, 0, dstZ), extent); - api.CmdCopyImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, region); + api.CmdCopyImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, in region); } width = Math.Max(1, width >> 1); @@ -422,7 +422,7 @@ namespace Ryujinx.Graphics.Vulkan DependencyCount = 1, }; - gd.Api.CreateRenderPass2(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError(); + gd.Api.CreateRenderPass2(device, in renderPassCreateInfo, null, out var renderPass).ThrowOnError(); using var rp = new Auto(new DisposableRenderPass(gd.Api, device, renderPass)); @@ -445,7 +445,7 @@ namespace Ryujinx.Graphics.Vulkan Layers = (uint)src.Layers, }; - gd.Api.CreateFramebuffer(device, framebufferCreateInfo, null, out var framebuffer).ThrowOnError(); + gd.Api.CreateFramebuffer(device, in framebufferCreateInfo, null, out var framebuffer).ThrowOnError(); using var fb = new Auto(new DisposableFramebuffer(gd.Api, device, framebuffer), null, srcView, dstView); var renderArea = new Rect2D(null, new Extent2D((uint)src.Info.Width, (uint)src.Info.Height)); @@ -465,7 +465,7 @@ namespace Ryujinx.Graphics.Vulkan // to resolve the depth-stencil texture. // TODO: Do speculative resolve and part of the same render pass as the draw to avoid // ending the current render pass? - gd.Api.CmdBeginRenderPass(cbs.CommandBuffer, renderPassBeginInfo, SubpassContents.Inline); + gd.Api.CmdBeginRenderPass(cbs.CommandBuffer, in renderPassBeginInfo, SubpassContents.Inline); gd.Api.CmdEndRenderPass(cbs.CommandBuffer); } } diff --git a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs index f36db68de3..10b36a3f9a 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs @@ -4,6 +4,7 @@ using Silk.NET.Vulkan; using System; using System.Collections.Generic; using System.Numerics; +using System.Runtime.CompilerServices; using Format = Ryujinx.Graphics.GAL.Format; using VkBuffer = Silk.NET.Vulkan.Buffer; using VkFormat = Silk.NET.Vulkan.Format; @@ -12,6 +13,11 @@ namespace Ryujinx.Graphics.Vulkan { class TextureStorage : IDisposable { + private struct TextureSliceInfo + { + public int BindCount; + } + private const MemoryPropertyFlags DefaultImageMemoryFlags = MemoryPropertyFlags.DeviceLocalBit; @@ -43,6 +49,7 @@ namespace Ryujinx.Graphics.Vulkan private readonly Image _image; private readonly Auto _imageAuto; private readonly Auto _allocationAuto; + private readonly int _depthOrLayers; private Auto _foreignAllocationAuto; private Dictionary _aliasedStorages; @@ -55,6 +62,9 @@ namespace Ryujinx.Graphics.Vulkan private int _viewsCount; private readonly ulong _size; + private int _bindCount; + private readonly TextureSliceInfo[] _slices; + public VkFormat VkFormat { get; } public unsafe TextureStorage( @@ -73,6 +83,7 @@ namespace Ryujinx.Graphics.Vulkan var depth = (uint)(info.Target == Target.Texture3D ? info.Depth : 1); VkFormat = format; + _depthOrLayers = info.GetDepthOrLayers(); var type = info.Target.Convert(); @@ -80,7 +91,7 @@ namespace Ryujinx.Graphics.Vulkan var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples); - var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample); + var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities); var flags = ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit; @@ -114,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan Flags = flags, }; - gd.Api.CreateImage(device, imageCreateInfo, null, out _image).ThrowOnError(); + gd.Api.CreateImage(device, in imageCreateInfo, null, out _image).ThrowOnError(); if (foreignAllocation == null) { @@ -148,6 +159,8 @@ namespace Ryujinx.Graphics.Vulkan InitialTransition(ImageLayout.Preinitialized, ImageLayout.General); } + + _slices = new TextureSliceInfo[levels * _depthOrLayers]; } public TextureStorage CreateAliasedColorForDepthStorageUnsafe(Format format) @@ -284,7 +297,7 @@ namespace Ryujinx.Graphics.Vulkan 0, null, 1, - barrier); + in barrier); if (useTempCbs) { @@ -292,7 +305,7 @@ namespace Ryujinx.Graphics.Vulkan } } - public static ImageUsageFlags GetImageUsage(Format format, Target target, bool supportsMsStorage) + public static ImageUsageFlags GetImageUsage(Format format, Target target, in HardwareCapabilities capabilities) { var usage = DefaultUsageFlags; @@ -305,11 +318,19 @@ namespace Ryujinx.Graphics.Vulkan usage |= ImageUsageFlags.ColorAttachmentBit; } + bool supportsMsStorage = capabilities.SupportsShaderStorageImageMultisample; + if (format.IsImageCompatible() && (supportsMsStorage || !target.IsMultisample())) { usage |= ImageUsageFlags.StorageBit; } + if (capabilities.SupportsAttachmentFeedbackLoop && + (usage & (ImageUsageFlags.DepthStencilAttachmentBit | ImageUsageFlags.ColorAttachmentBit)) != 0) + { + usage |= ImageUsageFlags.AttachmentFeedbackLoopBitExt; + } + return usage; } @@ -401,11 +422,11 @@ namespace Ryujinx.Graphics.Vulkan if (to) { - _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, region); + _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, in region); } else { - _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, region); + _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, in region); } offset += mipSize; @@ -510,6 +531,55 @@ namespace Ryujinx.Graphics.Vulkan } } + public void AddBinding(TextureView view) + { + // Assumes a view only has a first level. + + int index = view.FirstLevel * _depthOrLayers + view.FirstLayer; + int layers = view.Layers; + + for (int i = 0; i < layers; i++) + { + ref TextureSliceInfo info = ref _slices[index++]; + + info.BindCount++; + } + + _bindCount++; + } + + public void ClearBindings() + { + if (_bindCount != 0) + { + Array.Clear(_slices, 0, _slices.Length); + + _bindCount = 0; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsBound(TextureView view) + { + if (_bindCount != 0) + { + int index = view.FirstLevel * _depthOrLayers + view.FirstLayer; + int layers = view.Layers; + + for (int i = 0; i < layers; i++) + { + ref TextureSliceInfo info = ref _slices[index++]; + + if (info.BindCount != 0) + { + return true; + } + } + } + + return false; + } + public void IncrementViewsCount() { _viewsCount++; diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs index d4f26a2dd4..9b3f466627 100644 --- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs +++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs @@ -23,6 +23,8 @@ namespace Ryujinx.Graphics.Vulkan private readonly Auto _imageView2dArray; private Dictionary _selfManagedViews; + private int _hazardUses; + private readonly TextureCreateInfo _info; private HashTableSlim _renderPasses; @@ -60,7 +62,7 @@ namespace Ryujinx.Graphics.Vulkan gd.Textures.Add(this); var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format); - var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample); + var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities); var levels = (uint)info.Levels; var layers = (uint)info.GetLayers(); @@ -117,7 +119,7 @@ namespace Ryujinx.Graphics.Vulkan PNext = &imageViewUsage, }; - gd.Api.CreateImageView(device, imageCreateInfo, null, out var imageView).ThrowOnError(); + gd.Api.CreateImageView(device, in imageCreateInfo, null, out var imageView).ThrowOnError(); return new Auto(new DisposableImageView(gd.Api, device, imageView), null, storage.GetImage()); } @@ -492,7 +494,7 @@ namespace Ryujinx.Graphics.Vulkan dstStageMask, DependencyFlags.None, 1, - memoryBarrier, + in memoryBarrier, 0, null, 0, @@ -557,7 +559,7 @@ namespace Ryujinx.Graphics.Vulkan 0, null, 1, - memoryBarrier); + in memoryBarrier); } public TextureView GetView(Format format) @@ -949,11 +951,11 @@ namespace Ryujinx.Graphics.Vulkan if (to) { - _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, region); + _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, in region); } else { - _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, region); + _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, in region); } offset += mipSize; @@ -1010,11 +1012,11 @@ namespace Ryujinx.Graphics.Vulkan if (to) { - _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, region); + _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, in region); } else { - _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, region); + _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, in region); } } @@ -1034,6 +1036,34 @@ namespace Ryujinx.Graphics.Vulkan throw new NotImplementedException(); } + public void PrepareForUsage(CommandBufferScoped cbs, PipelineStageFlags flags, List feedbackLoopHazards) + { + Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, flags); + + if (feedbackLoopHazards != null && Storage.IsBound(this)) + { + feedbackLoopHazards.Add(this); + _hazardUses++; + } + } + + public void ClearUsage(List feedbackLoopHazards) + { + if (_hazardUses != 0 && feedbackLoopHazards != null) + { + feedbackLoopHazards.Remove(this); + _hazardUses--; + } + } + + public void DecrementHazardUses() + { + if (_hazardUses != 0) + { + _hazardUses--; + } + } + public (RenderPassHolder rpHolder, Auto framebuffer) GetPassAndFramebuffer( VulkanRenderer gd, Device device, diff --git a/src/Ryujinx.Graphics.Vulkan/Vendor.cs b/src/Ryujinx.Graphics.Vulkan/Vendor.cs index 802771ede5..55ae0cd819 100644 --- a/src/Ryujinx.Graphics.Vulkan/Vendor.cs +++ b/src/Ryujinx.Graphics.Vulkan/Vendor.cs @@ -90,11 +90,9 @@ namespace Ryujinx.Graphics.Vulkan DriverId.SamsungProprietary => "Samsung", DriverId.MesaVenus => "Venus", DriverId.MesaDozen => "Dozen", - - // TODO: Use real enum when we have an up to date Silk.NET. - (DriverId)24 => "NVK", - (DriverId)25 => "Imagination (Open)", - (DriverId)26 => "Honeykrisp", + DriverId.MesaNvk => "NVK", + DriverId.ImaginationOpenSourceMesa => "Imagination (Open)", + DriverId.MesaAgxv => "Honeykrisp", _ => id.ToString(), }; } diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs index 5a9844cb96..2c327fdb75 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs @@ -44,6 +44,8 @@ namespace Ryujinx.Graphics.Vulkan "VK_EXT_4444_formats", "VK_KHR_8bit_storage", "VK_KHR_maintenance2", + "VK_EXT_attachment_feedback_loop_layout", + "VK_EXT_attachment_feedback_loop_dynamic_state", }; private static readonly string[] _requiredExtensions = { @@ -357,6 +359,28 @@ namespace Ryujinx.Graphics.Vulkan features2.PNext = &supportedFeaturesDepthClipControl; } + PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT supportedFeaturesAttachmentFeedbackLoopLayout = new() + { + SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt, + PNext = features2.PNext, + }; + + if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout")) + { + features2.PNext = &supportedFeaturesAttachmentFeedbackLoopLayout; + } + + PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT supportedFeaturesDynamicAttachmentFeedbackLoopLayout = new() + { + SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt, + PNext = features2.PNext, + }; + + if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state")) + { + features2.PNext = &supportedFeaturesDynamicAttachmentFeedbackLoopLayout; + } + PhysicalDeviceVulkan12Features supportedPhysicalDeviceVulkan12Features = new() { SType = StructureType.PhysicalDeviceVulkan12Features, @@ -531,6 +555,36 @@ namespace Ryujinx.Graphics.Vulkan pExtendedFeatures = &featuresDepthClipControl; } + PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT featuresAttachmentFeedbackLoopLayout; + + if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout") && + supportedFeaturesAttachmentFeedbackLoopLayout.AttachmentFeedbackLoopLayout) + { + featuresAttachmentFeedbackLoopLayout = new() + { + SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt, + PNext = pExtendedFeatures, + AttachmentFeedbackLoopLayout = true, + }; + + pExtendedFeatures = &featuresAttachmentFeedbackLoopLayout; + } + + PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT featuresDynamicAttachmentFeedbackLoopLayout; + + if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state") && + supportedFeaturesDynamicAttachmentFeedbackLoopLayout.AttachmentFeedbackLoopDynamicState) + { + featuresDynamicAttachmentFeedbackLoopLayout = new() + { + SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt, + PNext = pExtendedFeatures, + AttachmentFeedbackLoopDynamicState = true, + }; + + pExtendedFeatures = &featuresDynamicAttachmentFeedbackLoopLayout; + } + var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray(); IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length]; diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs index c9ce678b77..33e41ab489 100644 --- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs +++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs @@ -38,6 +38,7 @@ namespace Ryujinx.Graphics.Vulkan internal KhrPushDescriptor PushDescriptorApi { get; private set; } internal ExtTransformFeedback TransformFeedbackApi { get; private set; } internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; } + internal ExtAttachmentFeedbackLoopDynamicState DynamicFeedbackLoopApi { get; private set; } internal uint QueueFamilyIndex { get; private set; } internal Queue Queue { get; private set; } @@ -149,6 +150,11 @@ namespace Ryujinx.Graphics.Vulkan DrawIndirectCountApi = drawIndirectCountApi; } + if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtAttachmentFeedbackLoopDynamicState dynamicFeedbackLoopApi)) + { + DynamicFeedbackLoopApi = dynamicFeedbackLoopApi; + } + if (maxQueueCount >= 2) { Api.GetDeviceQueue(_device, queueFamilyIndex, 1, out var backgroundQueue); @@ -243,6 +249,16 @@ namespace Ryujinx.Graphics.Vulkan SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt, }; + PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT featuresAttachmentFeedbackLoop = new() + { + SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt, + }; + + PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT featuresDynamicAttachmentFeedbackLoop = new() + { + SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt, + }; + PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new() { SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr, @@ -279,6 +295,22 @@ namespace Ryujinx.Graphics.Vulkan features2.PNext = &featuresDepthClipControl; } + bool supportsAttachmentFeedbackLoop = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout"); + + if (supportsAttachmentFeedbackLoop) + { + featuresAttachmentFeedbackLoop.PNext = features2.PNext; + features2.PNext = &featuresAttachmentFeedbackLoop; + } + + bool supportsDynamicAttachmentFeedbackLoop = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state"); + + if (supportsDynamicAttachmentFeedbackLoop) + { + featuresDynamicAttachmentFeedbackLoop.PNext = features2.PNext; + features2.PNext = &featuresDynamicAttachmentFeedbackLoop; + } + bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset"); if (usePortability) @@ -401,6 +433,8 @@ namespace Ryujinx.Graphics.Vulkan _physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"), _physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName), supportsDepthClipControl && featuresDepthClipControl.DepthClipControl, + supportsAttachmentFeedbackLoop && featuresAttachmentFeedbackLoop.AttachmentFeedbackLoopLayout, + supportsDynamicAttachmentFeedbackLoop && featuresDynamicAttachmentFeedbackLoop.AttachmentFeedbackLoopDynamicState, propertiesSubgroup.SubgroupSize, supportedSampleCounts, portabilityFlags, diff --git a/src/Ryujinx.Graphics.Vulkan/Window.cs b/src/Ryujinx.Graphics.Vulkan/Window.cs index efb0b31f97..d67362be30 100644 --- a/src/Ryujinx.Graphics.Vulkan/Window.cs +++ b/src/Ryujinx.Graphics.Vulkan/Window.cs @@ -160,7 +160,7 @@ namespace Ryujinx.Graphics.Vulkan SwizzleComponent.Blue, SwizzleComponent.Alpha); - _gd.SwapchainApi.CreateSwapchain(_device, swapchainCreateInfo, null, out _swapchain).ThrowOnError(); + _gd.SwapchainApi.CreateSwapchain(_device, in swapchainCreateInfo, null, out _swapchain).ThrowOnError(); _gd.SwapchainApi.GetSwapchainImages(_device, _swapchain, &imageCount, null); @@ -187,14 +187,14 @@ namespace Ryujinx.Graphics.Vulkan for (int i = 0; i < _imageAvailableSemaphores.Length; i++) { - _gd.Api.CreateSemaphore(_device, semaphoreCreateInfo, null, out _imageAvailableSemaphores[i]).ThrowOnError(); + _gd.Api.CreateSemaphore(_device, in semaphoreCreateInfo, null, out _imageAvailableSemaphores[i]).ThrowOnError(); } _renderFinishedSemaphores = new Semaphore[imageCount]; for (int i = 0; i < _renderFinishedSemaphores.Length; i++) { - _gd.Api.CreateSemaphore(_device, semaphoreCreateInfo, null, out _renderFinishedSemaphores[i]).ThrowOnError(); + _gd.Api.CreateSemaphore(_device, in semaphoreCreateInfo, null, out _renderFinishedSemaphores[i]).ThrowOnError(); } } @@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Vulkan SubresourceRange = subresourceRange, }; - _gd.Api.CreateImageView(_device, imageCreateInfo, null, out var imageView).ThrowOnError(); + _gd.Api.CreateImageView(_device, in imageCreateInfo, null, out var imageView).ThrowOnError(); return new TextureView(_gd, _device, new DisposableImageView(_gd.Api, _device, imageView), info, format); } @@ -479,7 +479,7 @@ namespace Ryujinx.Graphics.Vulkan lock (_gd.QueueLock) { - _gd.SwapchainApi.QueuePresent(_gd.Queue, presentInfo); + _gd.SwapchainApi.QueuePresent(_gd.Queue, in presentInfo); } } @@ -611,7 +611,7 @@ namespace Ryujinx.Graphics.Vulkan 0, null, 1, - barrier); + in barrier); } private void CaptureFrame(TextureView texture, int x, int y, int width, int height, bool isBgra, bool flipX, bool flipY) diff --git a/src/Ryujinx.Gtk3/Program.cs b/src/Ryujinx.Gtk3/Program.cs index 745335ac95..2d350374bf 100644 --- a/src/Ryujinx.Gtk3/Program.cs +++ b/src/Ryujinx.Gtk3/Program.cs @@ -4,6 +4,8 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.GraphicsDriver; using Ryujinx.Common.Logging; using Ryujinx.Common.SystemInterop; +using Ryujinx.Common.Utilities; +using Ryujinx.Graphics.Vulkan.MoltenVK; using Ryujinx.Modules; using Ryujinx.SDL2.Common; using Ryujinx.UI; @@ -40,9 +42,6 @@ namespace Ryujinx [LibraryImport("user32.dll", SetLastError = true)] public static partial int MessageBoxA(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type); - [LibraryImport("libc", SetLastError = true)] - private static partial int setenv([MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value, int overwrite); - private const uint MbIconWarning = 0x30; static Program() @@ -104,12 +103,13 @@ namespace Ryujinx throw new NotSupportedException("Failed to initialize multi-threading support."); } - Environment.SetEnvironmentVariable("GDK_BACKEND", "x11"); - setenv("GDK_BACKEND", "x11", 1); + OsUtils.SetEnvironmentVariableNoCaching("GDK_BACKEND", "x11"); } if (OperatingSystem.IsMacOS()) { + MVKInitialization.InitializeResolver(); + string baseDirectory = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory); string resourcesDataDir; @@ -122,19 +122,13 @@ namespace Ryujinx resourcesDataDir = baseDirectory; } - static void SetEnvironmentVariableNoCaching(string key, string value) - { - int res = setenv(key, value, 1); - Debug.Assert(res != -1); - } - // On macOS, GTK3 needs XDG_DATA_DIRS to be set, otherwise it will try searching for "gschemas.compiled" in system directories. - SetEnvironmentVariableNoCaching("XDG_DATA_DIRS", Path.Combine(resourcesDataDir, "share")); + OsUtils.SetEnvironmentVariableNoCaching("XDG_DATA_DIRS", Path.Combine(resourcesDataDir, "share")); // On macOS, GTK3 needs GDK_PIXBUF_MODULE_FILE to be set, otherwise it will try searching for "loaders.cache" in system directories. - SetEnvironmentVariableNoCaching("GDK_PIXBUF_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gdk-pixbuf-2.0", "2.10.0", "loaders.cache")); + OsUtils.SetEnvironmentVariableNoCaching("GDK_PIXBUF_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gdk-pixbuf-2.0", "2.10.0", "loaders.cache")); - SetEnvironmentVariableNoCaching("GTK_IM_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gtk-3.0", "3.0.0", "immodules.cache")); + OsUtils.SetEnvironmentVariableNoCaching("GTK_IM_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gtk-3.0", "3.0.0", "immodules.cache")); } string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine); @@ -230,9 +224,9 @@ namespace Ryujinx // Logging system information. PrintSystemInfo(); - // Enable OGL multithreading on the driver, when available. + // Enable OGL multithreading on the driver, and some other flags. BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading; - DriverUtilities.ToggleOGLThreading(threadingMode == BackendThreading.Off); + DriverUtilities.InitDriverConfig(threadingMode == BackendThreading.Off); // Initialize Gtk. Application.Init(); diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index 85aff67129..07995dbdd7 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -7,6 +7,7 @@ using Ryujinx.Common.Configuration.Hid; using Ryujinx.Common.Configuration.Hid.Controller; using Ryujinx.Common.Configuration.Hid.Controller.Motion; using Ryujinx.Common.Configuration.Hid.Keyboard; +using Ryujinx.Common.GraphicsDriver; using Ryujinx.Common.Logging; using Ryujinx.Common.Logging.Targets; using Ryujinx.Common.SystemInterop; @@ -18,6 +19,7 @@ using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.Vulkan; +using Ryujinx.Graphics.Vulkan.MoltenVK; using Ryujinx.Headless.SDL2.OpenGL; using Ryujinx.Headless.SDL2.Vulkan; using Ryujinx.HLE; @@ -88,6 +90,11 @@ namespace Ryujinx.Headless.SDL2 }; } + if (OperatingSystem.IsMacOS()) + { + MVKInitialization.InitializeResolver(); + } + Parser.Default.ParseArguments(args) .WithParsed(Load) .WithNotParsed(errors => errors.Output()); @@ -457,6 +464,8 @@ namespace Ryujinx.Headless.SDL2 GraphicsConfig.ShadersDumpPath = option.GraphicsShadersDumpPath; GraphicsConfig.EnableMacroHLE = !option.DisableMacroHLE; + DriverUtilities.InitDriverConfig(option.BackendThreading == BackendThreading.Off); + while (true) { LoadApplication(option); diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs index 976963422d..6c83cedcf3 100644 --- a/src/Ryujinx/Program.cs +++ b/src/Ryujinx/Program.cs @@ -7,6 +7,7 @@ using Ryujinx.Common.Configuration; using Ryujinx.Common.GraphicsDriver; using Ryujinx.Common.Logging; using Ryujinx.Common.SystemInterop; +using Ryujinx.Graphics.Vulkan.MoltenVK; using Ryujinx.Modules; using Ryujinx.SDL2.Common; using Ryujinx.UI.Common; @@ -80,6 +81,11 @@ namespace Ryujinx.Ava // Parse arguments CommandLineState.ParseArguments(args); + if (OperatingSystem.IsMacOS()) + { + MVKInitialization.InitializeResolver(); + } + // Delete backup files after updating. Task.Run(Updater.CleanupUpdate); @@ -111,8 +117,8 @@ namespace Ryujinx.Ava // Logging system information. PrintSystemInfo(); - // Enable OGL multithreading on the driver, when available. - DriverUtilities.ToggleOGLThreading(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off); + // Enable OGL multithreading on the driver, and some other flags. + DriverUtilities.InitDriverConfig(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off); // Check if keys exists. if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys")))