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")))