Compare commits
10 Commits
cee1d0eb3a
...
metal
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38ed71ddfe | ||
|
|
3ffcc72117 | ||
|
|
80d619f010 | ||
|
|
fe1617ffea | ||
|
|
eb6b0e9adc | ||
|
|
9631bdfe16 | ||
|
|
2a84656ffc | ||
|
|
6c6580ddcc | ||
|
|
c47448628c | ||
|
|
d0ac83b493 |
@@ -1436,7 +1436,7 @@
|
|||||||
010083A018262000,"Hitman: Blood Money — Reprisal",deadlock,ingame,2024-09-28 16:28:50
|
010083A018262000,"Hitman: Blood Money — Reprisal",deadlock,ingame,2024-09-28 16:28:50
|
||||||
01004B100A5CC000,"Hob: The Definitive Edition",,playable,2021-01-13 09:39:19
|
01004B100A5CC000,"Hob: The Definitive Edition",,playable,2021-01-13 09:39:19
|
||||||
0100F7300ED2C000,"Hoggy2",,playable,2022-10-10 13:53:35
|
0100F7300ED2C000,"Hoggy2",,playable,2022-10-10 13:53:35
|
||||||
0100F7E00C70E000,"Hogwarts Legacy",slow,ingame,2024-09-03 19:53:58
|
0100F7E00C70E000,"Hogwarts Legacy",UE4;slow,ingame,2024-09-03 19:53:58
|
||||||
0100633007D48000,"Hollow Knight",nvdec,playable,2023-01-16 15:44:56
|
0100633007D48000,"Hollow Knight",nvdec,playable,2023-01-16 15:44:56
|
||||||
0100F2100061E800,"Hollow0",UE4;gpu,ingame,2021-03-03 23:42:56
|
0100F2100061E800,"Hollow0",UE4;gpu,ingame,2021-03-03 23:42:56
|
||||||
0100342009E16000,"Holy Potatoes! What The Hell?!",,playable,2020-07-03 10:48:56
|
0100342009E16000,"Holy Potatoes! What The Hell?!",,playable,2020-07-03 10:48:56
|
||||||
|
|||||||
|
@@ -1,3 +1,4 @@
|
|||||||
|
using Ryujinx.Common.Utilities;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Common.GraphicsDriver
|
namespace Ryujinx.Common.GraphicsDriver
|
||||||
@@ -10,7 +11,7 @@ namespace Ryujinx.Common.GraphicsDriver
|
|||||||
|
|
||||||
string flags = existingFlags == null ? newFlags : $"{existingFlags},{newFlags}";
|
string flags = existingFlags == null ? newFlags : $"{existingFlags},{newFlags}";
|
||||||
|
|
||||||
Environment.SetEnvironmentVariable(envVar, flags);
|
OsUtils.SetEnvironmentVariableNoCaching(envVar, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void InitDriverConfig(bool oglThreading)
|
public static void InitDriverConfig(bool oglThreading)
|
||||||
@@ -22,10 +23,11 @@ namespace Ryujinx.Common.GraphicsDriver
|
|||||||
|
|
||||||
ToggleOGLThreading(oglThreading);
|
ToggleOGLThreading(oglThreading);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ToggleOGLThreading(bool enabled)
|
public static void ToggleOGLThreading(bool enabled)
|
||||||
{
|
{
|
||||||
Environment.SetEnvironmentVariable("mesa_glthread", enabled.ToString().ToLower());
|
OsUtils.SetEnvironmentVariableNoCaching("mesa_glthread", enabled.ToString().ToLower());
|
||||||
Environment.SetEnvironmentVariable("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0");
|
OsUtils.SetEnvironmentVariableNoCaching("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|||||||
9
src/Ryujinx.Common/SharedConstants.cs
Normal file
9
src/Ryujinx.Common/SharedConstants.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Ryujinx.Common
|
||||||
|
{
|
||||||
|
public static class SharedConstants
|
||||||
|
{
|
||||||
|
public const string DefaultLanPlayHost = "ryuldn.vudjun.com";
|
||||||
|
public const short LanPlayPort = 30456;
|
||||||
|
public const string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com";
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/Ryujinx.Common/Utilities/OsUtils.cs
Normal file
24
src/Ryujinx.Common/Utilities/OsUtils.cs
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,10 +32,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
CommandBuffer
|
CommandBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool _feedbackLoopActive;
|
||||||
private PipelineStageFlags _incoherentBufferWriteStages;
|
private PipelineStageFlags _incoherentBufferWriteStages;
|
||||||
private PipelineStageFlags _incoherentTextureWriteStages;
|
private PipelineStageFlags _incoherentTextureWriteStages;
|
||||||
private PipelineStageFlags _extraStages;
|
private PipelineStageFlags _extraStages;
|
||||||
private IncoherentBarrierType _queuedIncoherentBarrier;
|
private IncoherentBarrierType _queuedIncoherentBarrier;
|
||||||
|
private bool _queuedFeedbackLoopBarrier;
|
||||||
|
|
||||||
public BarrierBatch(VulkanRenderer gd)
|
public BarrierBatch(VulkanRenderer gd)
|
||||||
{
|
{
|
||||||
@@ -53,17 +55,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
stages |= PipelineStageFlags.TransformFeedbackBitExt;
|
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);
|
return (access, stages);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,16 +169,34 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
|
|
||||||
_queuedIncoherentBarrier = IncoherentBarrierType.None;
|
_queuedIncoherentBarrier = IncoherentBarrierType.None;
|
||||||
|
_queuedFeedbackLoopBarrier = false;
|
||||||
}
|
}
|
||||||
|
else if (_feedbackLoopActive && _queuedFeedbackLoopBarrier)
|
||||||
|
{
|
||||||
|
// Feedback loop barrier.
|
||||||
|
|
||||||
|
MemoryBarrier barrier = new()
|
||||||
|
{
|
||||||
|
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)
|
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)
|
if (program != null)
|
||||||
{
|
{
|
||||||
@@ -195,6 +204,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_incoherentTextureWriteStages |= program.IncoherentTextureWriteStages;
|
_incoherentTextureWriteStages |= program.IncoherentTextureWriteStages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_feedbackLoopActive |= feedbackLoopActive;
|
||||||
|
|
||||||
FlushMemoryBarrier(program, inRenderPass);
|
FlushMemoryBarrier(program, inRenderPass);
|
||||||
|
|
||||||
if (!inRenderPass && rpHolder != null)
|
if (!inRenderPass && rpHolder != null)
|
||||||
@@ -406,6 +417,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
_queuedIncoherentBarrier = type;
|
_queuedIncoherentBarrier = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_queuedFeedbackLoopBarrier = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void QueueTextureBarrier()
|
public void QueueTextureBarrier()
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL;
|
|||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Buffer = Silk.NET.Vulkan.Buffer;
|
using Buffer = Silk.NET.Vulkan.Buffer;
|
||||||
@@ -42,15 +43,15 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private record struct TextureRef
|
private record struct TextureRef
|
||||||
{
|
{
|
||||||
public ShaderStage Stage;
|
public ShaderStage Stage;
|
||||||
public TextureStorage Storage;
|
public TextureView View;
|
||||||
public Auto<DisposableImageView> View;
|
public Auto<DisposableImageView> ImageView;
|
||||||
public Auto<DisposableSampler> Sampler;
|
public Auto<DisposableSampler> Sampler;
|
||||||
|
|
||||||
public TextureRef(ShaderStage stage, TextureStorage storage, Auto<DisposableImageView> view, Auto<DisposableSampler> sampler)
|
public TextureRef(ShaderStage stage, TextureView view, Auto<DisposableImageView> imageView, Auto<DisposableSampler> sampler)
|
||||||
{
|
{
|
||||||
Stage = stage;
|
Stage = stage;
|
||||||
Storage = storage;
|
|
||||||
View = view;
|
View = view;
|
||||||
|
ImageView = imageView;
|
||||||
Sampler = sampler;
|
Sampler = sampler;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,14 +59,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private record struct ImageRef
|
private record struct ImageRef
|
||||||
{
|
{
|
||||||
public ShaderStage Stage;
|
public ShaderStage Stage;
|
||||||
public TextureStorage Storage;
|
public TextureView View;
|
||||||
public Auto<DisposableImageView> View;
|
public Auto<DisposableImageView> ImageView;
|
||||||
|
|
||||||
public ImageRef(ShaderStage stage, TextureStorage storage, Auto<DisposableImageView> view)
|
public ImageRef(ShaderStage stage, TextureView view, Auto<DisposableImageView> imageView)
|
||||||
{
|
{
|
||||||
Stage = stage;
|
Stage = stage;
|
||||||
Storage = storage;
|
|
||||||
View = view;
|
View = view;
|
||||||
|
ImageView = imageView;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,6 +124,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private readonly TextureView _dummyTexture;
|
private readonly TextureView _dummyTexture;
|
||||||
private readonly SamplerHolder _dummySampler;
|
private readonly SamplerHolder _dummySampler;
|
||||||
|
|
||||||
|
public List<TextureView> FeedbackLoopHazards { get; private set; }
|
||||||
|
|
||||||
public DescriptorSetUpdater(VulkanRenderer gd, Device device)
|
public DescriptorSetUpdater(VulkanRenderer gd, Device device)
|
||||||
{
|
{
|
||||||
_gd = gd;
|
_gd = gd;
|
||||||
@@ -207,10 +210,15 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_templateUpdater = new();
|
_templateUpdater = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize()
|
public void Initialize(bool isMainPipeline)
|
||||||
{
|
{
|
||||||
MemoryOwner<byte> dummyTextureData = MemoryOwner<byte>.RentCleared(4);
|
MemoryOwner<byte> dummyTextureData = MemoryOwner<byte>.RentCleared(4);
|
||||||
_dummyTexture.SetData(dummyTextureData);
|
_dummyTexture.SetData(dummyTextureData);
|
||||||
|
|
||||||
|
if (isMainPipeline)
|
||||||
|
{
|
||||||
|
FeedbackLoopHazards = [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool BindingOverlaps(ref DescriptorBufferInfo info, int bindingOffset, int offset, int size)
|
private static bool BindingOverlaps(ref DescriptorBufferInfo info, int bindingOffset, int offset, int size)
|
||||||
@@ -273,6 +281,18 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public void InsertBindingBarriers(CommandBufferScoped cbs)
|
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])
|
foreach (ResourceBindingSegment segment in _program.BindingSegments[PipelineBase.TextureSetIndex])
|
||||||
{
|
{
|
||||||
if (segment.Type == ResourceType.TextureAndSampler)
|
if (segment.Type == ResourceType.TextureAndSampler)
|
||||||
@@ -282,7 +302,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
for (int i = 0; i < segment.Count; i++)
|
for (int i = 0; i < segment.Count; i++)
|
||||||
{
|
{
|
||||||
ref TextureRef texture = ref _textureRefs[segment.Binding + i];
|
ref TextureRef texture = ref _textureRefs[segment.Binding + i];
|
||||||
texture.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, texture.Stage.ConvertToPipelineStageFlags());
|
texture.View?.PrepareForUsage(cbs, texture.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -303,7 +323,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
for (int i = 0; i < segment.Count; i++)
|
for (int i = 0; i < segment.Count; i++)
|
||||||
{
|
{
|
||||||
ref ImageRef image = ref _imageRefs[segment.Binding + i];
|
ref ImageRef image = ref _imageRefs[segment.Binding + i];
|
||||||
image.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, image.Stage.ConvertToPipelineStageFlags());
|
image.View?.PrepareForUsage(cbs, image.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -377,8 +397,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
else if (image is TextureView view)
|
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.GetIdentityImageView());
|
|
||||||
|
iRef.View?.ClearUsage(FeedbackLoopHazards);
|
||||||
|
view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
|
||||||
|
|
||||||
|
iRef = new(stage, view, view.GetIdentityImageView());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -476,9 +500,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
else if (texture is TextureView view)
|
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
|
else
|
||||||
{
|
{
|
||||||
@@ -500,7 +527,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
|
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);
|
SignalDirty(DirtyFlags.Texture);
|
||||||
}
|
}
|
||||||
@@ -826,7 +853,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
ref DescriptorImageInfo texture = ref textures[i];
|
ref DescriptorImageInfo texture = ref textures[i];
|
||||||
ref TextureRef refs = ref _textureRefs[binding + i];
|
ref TextureRef 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;
|
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
|
||||||
|
|
||||||
if (texture.ImageView.Handle == 0)
|
if (texture.ImageView.Handle == 0)
|
||||||
@@ -876,7 +903,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
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<DescriptorImageInfo>(images[..count]);
|
tu.Push<DescriptorImageInfo>(images[..count]);
|
||||||
@@ -947,7 +974,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
ref DescriptorImageInfo texture = ref textures[i];
|
ref DescriptorImageInfo texture = ref textures[i];
|
||||||
ref TextureRef refs = ref _textureRefs[binding + i];
|
ref TextureRef 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;
|
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
|
||||||
|
|
||||||
if (texture.ImageView.Handle == 0)
|
if (texture.ImageView.Handle == 0)
|
||||||
|
|||||||
12
src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs
Normal file
12
src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
|
{
|
||||||
|
[Flags]
|
||||||
|
internal enum FeedbackLoopAspects
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Color = 1 << 0,
|
||||||
|
Depth = 1 << 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -303,6 +303,27 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_depthStencil?.Storage?.AddStoreOpUsage(true);
|
_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<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
|
public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
|
||||||
VulkanRenderer gd,
|
VulkanRenderer gd,
|
||||||
Device device,
|
Device device,
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public readonly bool SupportsViewportArray2;
|
public readonly bool SupportsViewportArray2;
|
||||||
public readonly bool SupportsHostImportedMemory;
|
public readonly bool SupportsHostImportedMemory;
|
||||||
public readonly bool SupportsDepthClipControl;
|
public readonly bool SupportsDepthClipControl;
|
||||||
|
public readonly bool SupportsAttachmentFeedbackLoop;
|
||||||
|
public readonly bool SupportsDynamicAttachmentFeedbackLoop;
|
||||||
public readonly uint SubgroupSize;
|
public readonly uint SubgroupSize;
|
||||||
public readonly SampleCountFlags SupportedSampleCounts;
|
public readonly SampleCountFlags SupportedSampleCounts;
|
||||||
public readonly PortabilitySubsetFlags PortabilitySubset;
|
public readonly PortabilitySubsetFlags PortabilitySubset;
|
||||||
@@ -84,6 +86,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
bool supportsViewportArray2,
|
bool supportsViewportArray2,
|
||||||
bool supportsHostImportedMemory,
|
bool supportsHostImportedMemory,
|
||||||
bool supportsDepthClipControl,
|
bool supportsDepthClipControl,
|
||||||
|
bool supportsAttachmentFeedbackLoop,
|
||||||
|
bool supportsDynamicAttachmentFeedbackLoop,
|
||||||
uint subgroupSize,
|
uint subgroupSize,
|
||||||
SampleCountFlags supportedSampleCounts,
|
SampleCountFlags supportedSampleCounts,
|
||||||
PortabilitySubsetFlags portabilitySubset,
|
PortabilitySubsetFlags portabilitySubset,
|
||||||
@@ -121,6 +125,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SupportsViewportArray2 = supportsViewportArray2;
|
SupportsViewportArray2 = supportsViewportArray2;
|
||||||
SupportsHostImportedMemory = supportsHostImportedMemory;
|
SupportsHostImportedMemory = supportsHostImportedMemory;
|
||||||
SupportsDepthClipControl = supportsDepthClipControl;
|
SupportsDepthClipControl = supportsDepthClipControl;
|
||||||
|
SupportsAttachmentFeedbackLoop = supportsAttachmentFeedbackLoop;
|
||||||
|
SupportsDynamicAttachmentFeedbackLoop = supportsDynamicAttachmentFeedbackLoop;
|
||||||
SubgroupSize = subgroupSize;
|
SubgroupSize = subgroupSize;
|
||||||
SupportedSampleCounts = supportedSampleCounts;
|
SupportedSampleCounts = supportedSampleCounts;
|
||||||
PortabilitySubset = portabilitySubset;
|
PortabilitySubset = portabilitySubset;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Ryujinx.Graphics.GAL;
|
|||||||
using Ryujinx.Graphics.Shader;
|
using Ryujinx.Graphics.Shader;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
@@ -35,6 +36,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public readonly Action EndRenderPassDelegate;
|
public readonly Action EndRenderPassDelegate;
|
||||||
|
|
||||||
protected PipelineDynamicState DynamicState;
|
protected PipelineDynamicState DynamicState;
|
||||||
|
protected bool IsMainPipeline;
|
||||||
private PipelineState _newState;
|
private PipelineState _newState;
|
||||||
private bool _graphicsStateDirty;
|
private bool _graphicsStateDirty;
|
||||||
private bool _computeStateDirty;
|
private bool _computeStateDirty;
|
||||||
@@ -87,6 +89,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private bool _tfEnabled;
|
private bool _tfEnabled;
|
||||||
private bool _tfActive;
|
private bool _tfActive;
|
||||||
|
|
||||||
|
private FeedbackLoopAspects _feedbackLoop;
|
||||||
|
private bool _passWritesDepthStencil;
|
||||||
|
|
||||||
private readonly PipelineColorBlendAttachmentState[] _storedBlend;
|
private readonly PipelineColorBlendAttachmentState[] _storedBlend;
|
||||||
public ulong DrawCount { get; private set; }
|
public ulong DrawCount { get; private set; }
|
||||||
public bool RenderPassActive { get; private set; }
|
public bool RenderPassActive { get; private set; }
|
||||||
@@ -128,7 +133,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
_descriptorSetUpdater.Initialize();
|
_descriptorSetUpdater.Initialize(IsMainPipeline);
|
||||||
|
|
||||||
QuadsToTrisPattern = new IndexBufferPattern(Gd, 4, 6, 0, [0, 1, 2, 0, 2, 3], 4, false);
|
QuadsToTrisPattern = new IndexBufferPattern(Gd, 4, 6, 0, [0, 1, 2, 0, 2, 3], 4, false);
|
||||||
TriFanToTrisPattern = new IndexBufferPattern(Gd, 3, 3, 2, [int.MinValue, -1, 0], 1, true);
|
TriFanToTrisPattern = new IndexBufferPattern(Gd, 3, 3, 2, [int.MinValue, -1, 0], 1, true);
|
||||||
@@ -816,6 +821,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_newState.DepthTestEnable = depthTest.TestEnable;
|
_newState.DepthTestEnable = depthTest.TestEnable;
|
||||||
_newState.DepthWriteEnable = depthTest.WriteEnable;
|
_newState.DepthWriteEnable = depthTest.WriteEnable;
|
||||||
_newState.DepthCompareOp = depthTest.Func.Convert();
|
_newState.DepthCompareOp = depthTest.Func.Convert();
|
||||||
|
|
||||||
|
UpdatePassDepthStencil();
|
||||||
SignalStateChange();
|
SignalStateChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1081,6 +1088,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_newState.StencilFrontPassOp = stencilTest.FrontDpPass.Convert();
|
_newState.StencilFrontPassOp = stencilTest.FrontDpPass.Convert();
|
||||||
_newState.StencilFrontDepthFailOp = stencilTest.FrontDpFail.Convert();
|
_newState.StencilFrontDepthFailOp = stencilTest.FrontDpFail.Convert();
|
||||||
_newState.StencilFrontCompareOp = stencilTest.FrontFunc.Convert();
|
_newState.StencilFrontCompareOp = stencilTest.FrontFunc.Convert();
|
||||||
|
|
||||||
|
UpdatePassDepthStencil();
|
||||||
SignalStateChange();
|
SignalStateChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1428,7 +1437,23 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsMainPipeline)
|
||||||
|
{
|
||||||
|
FramebufferParams?.ClearBindings();
|
||||||
|
}
|
||||||
|
|
||||||
FramebufferParams = new FramebufferParams(Device, colors, depthStencil);
|
FramebufferParams = new FramebufferParams(Device, colors, depthStencil);
|
||||||
|
|
||||||
|
if (IsMainPipeline)
|
||||||
|
{
|
||||||
|
FramebufferParams.AddBindings();
|
||||||
|
|
||||||
|
_newState.FeedbackLoopAspects = FeedbackLoopAspects.None;
|
||||||
|
_bindingBarriersDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_passWritesDepthStencil = false;
|
||||||
|
UpdatePassDepthStencil();
|
||||||
UpdatePipelineAttachmentFormats();
|
UpdatePipelineAttachmentFormats();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1495,11 +1520,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);
|
_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<TextureView> 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()
|
private bool RecreateGraphicsPipelineIfNeeded()
|
||||||
{
|
{
|
||||||
if (AutoFlush.ShouldFlushDraw(DrawCount))
|
if (AutoFlush.ShouldFlushDraw(DrawCount))
|
||||||
@@ -1507,7 +1603,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Gd.FlushAllCommands();
|
Gd.FlushAllCommands();
|
||||||
}
|
}
|
||||||
|
|
||||||
DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer);
|
DynamicState.ReplayIfDirty(Gd, CommandBuffer);
|
||||||
|
|
||||||
if (_needsIndexBufferRebind && _indexBufferPattern == null)
|
if (_needsIndexBufferRebind && _indexBufferPattern == null)
|
||||||
{
|
{
|
||||||
@@ -1541,7 +1637,15 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_vertexBufferUpdater.Commit(Cbs);
|
_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))
|
if (!CreatePipeline(PipelineBindPoint.Graphics))
|
||||||
{
|
{
|
||||||
@@ -1550,17 +1654,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
_graphicsStateDirty = false;
|
_graphicsStateDirty = false;
|
||||||
Pbp = PipelineBindPoint.Graphics;
|
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);
|
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Common.Memory;
|
using Ryujinx.Common.Memory;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
|
using Silk.NET.Vulkan.Extensions.EXT;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
@@ -21,6 +22,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private Array4<float> _blendConstants;
|
private Array4<float> _blendConstants;
|
||||||
|
|
||||||
|
private FeedbackLoopAspects _feedbackLoopAspects;
|
||||||
|
|
||||||
public uint ViewportsCount;
|
public uint ViewportsCount;
|
||||||
public Array16<Viewport> Viewports;
|
public Array16<Viewport> Viewports;
|
||||||
|
|
||||||
@@ -32,7 +35,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
Scissor = 1 << 2,
|
Scissor = 1 << 2,
|
||||||
Stencil = 1 << 3,
|
Stencil = 1 << 3,
|
||||||
Viewport = 1 << 4,
|
Viewport = 1 << 4,
|
||||||
All = Blend | DepthBias | Scissor | Stencil | Viewport,
|
FeedbackLoop = 1 << 5,
|
||||||
|
All = Blend | DepthBias | Scissor | Stencil | Viewport | FeedbackLoop,
|
||||||
}
|
}
|
||||||
|
|
||||||
private DirtyFlags _dirty;
|
private DirtyFlags _dirty;
|
||||||
@@ -99,13 +103,22 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetFeedbackLoop(FeedbackLoopAspects aspects)
|
||||||
|
{
|
||||||
|
_feedbackLoopAspects = aspects;
|
||||||
|
|
||||||
|
_dirty |= DirtyFlags.FeedbackLoop;
|
||||||
|
}
|
||||||
|
|
||||||
public void ForceAllDirty()
|
public void ForceAllDirty()
|
||||||
{
|
{
|
||||||
_dirty = DirtyFlags.All;
|
_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))
|
if (_dirty.HasFlag(DirtyFlags.Blend))
|
||||||
{
|
{
|
||||||
RecordBlend(api, commandBuffer);
|
RecordBlend(api, commandBuffer);
|
||||||
@@ -131,6 +144,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
RecordViewport(api, commandBuffer);
|
RecordViewport(api, commandBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_dirty.HasFlag(DirtyFlags.FeedbackLoop) && gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop)
|
||||||
|
{
|
||||||
|
RecordFeedbackLoop(gd.DynamicFeedbackLoopApi, commandBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
_dirty = DirtyFlags.None;
|
_dirty = DirtyFlags.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,5 +187,17 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
api.CmdSetViewport(commandBuffer, 0, ViewportsCount, Viewports.AsSpan());
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_activeBufferMirrors = [];
|
_activeBufferMirrors = [];
|
||||||
|
|
||||||
CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
|
CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
|
||||||
|
|
||||||
|
IsMainPipeline = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CopyPendingQuery()
|
private void CopyPendingQuery()
|
||||||
@@ -235,7 +237,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
if (Pipeline != null && Pbp == PipelineBindPoint.Graphics)
|
if (Pipeline != null && Pbp == PipelineBindPoint.Graphics)
|
||||||
{
|
{
|
||||||
DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer);
|
DynamicState.ReplayIfDirty(Gd, CommandBuffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
struct PipelineState : IDisposable
|
struct PipelineState : IDisposable
|
||||||
{
|
{
|
||||||
private const int RequiredSubgroupSize = 32;
|
private const int RequiredSubgroupSize = 32;
|
||||||
|
private const int MaxDynamicStatesCount = 9;
|
||||||
|
|
||||||
public PipelineUid Internal;
|
public PipelineUid Internal;
|
||||||
|
|
||||||
@@ -299,6 +300,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6);
|
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 bool HasTessellationControlShader;
|
||||||
public NativeArray<PipelineShaderStageCreateInfo> Stages;
|
public NativeArray<PipelineShaderStageCreateInfo> Stages;
|
||||||
public PipelineLayout PipelineLayout;
|
public PipelineLayout PipelineLayout;
|
||||||
@@ -564,9 +571,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState;
|
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[0] = DynamicState.Viewport;
|
||||||
dynamicStates[1] = DynamicState.Scissor;
|
dynamicStates[1] = DynamicState.Scissor;
|
||||||
@@ -578,7 +587,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
if (supportsExtDynamicState)
|
if (supportsExtDynamicState)
|
||||||
{
|
{
|
||||||
dynamicStates[7] = DynamicState.VertexInputBindingStrideExt;
|
dynamicStates[dynamicStatesCount++] = DynamicState.VertexInputBindingStrideExt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (supportsFeedbackLoopDynamicState)
|
||||||
|
{
|
||||||
|
dynamicStates[dynamicStatesCount++] = DynamicState.AttachmentFeedbackLoopEnableExt;
|
||||||
}
|
}
|
||||||
|
|
||||||
PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo = new()
|
PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo = new()
|
||||||
@@ -588,9 +602,27 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
PDynamicStates = dynamicStates,
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GraphicsPipelineCreateInfo pipelineCreateInfo = new()
|
GraphicsPipelineCreateInfo pipelineCreateInfo = new()
|
||||||
{
|
{
|
||||||
SType = StructureType.GraphicsPipelineCreateInfo,
|
SType = StructureType.GraphicsPipelineCreateInfo,
|
||||||
|
Flags = flags,
|
||||||
StageCount = StagesCount,
|
StageCount = StagesCount,
|
||||||
PStages = Stages.Pointer,
|
PStages = Stages.Pointer,
|
||||||
PVertexInputState = &vertexInputState,
|
PVertexInputState = &vertexInputState,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Silk.NET.Vulkan;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using Format = Ryujinx.Graphics.GAL.Format;
|
using Format = Ryujinx.Graphics.GAL.Format;
|
||||||
using VkBuffer = Silk.NET.Vulkan.Buffer;
|
using VkBuffer = Silk.NET.Vulkan.Buffer;
|
||||||
using VkFormat = Silk.NET.Vulkan.Format;
|
using VkFormat = Silk.NET.Vulkan.Format;
|
||||||
@@ -12,6 +13,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
class TextureStorage : IDisposable
|
class TextureStorage : IDisposable
|
||||||
{
|
{
|
||||||
|
private struct TextureSliceInfo
|
||||||
|
{
|
||||||
|
public int BindCount;
|
||||||
|
}
|
||||||
|
|
||||||
private const MemoryPropertyFlags DefaultImageMemoryFlags =
|
private const MemoryPropertyFlags DefaultImageMemoryFlags =
|
||||||
MemoryPropertyFlags.DeviceLocalBit;
|
MemoryPropertyFlags.DeviceLocalBit;
|
||||||
|
|
||||||
@@ -43,6 +49,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private readonly Image _image;
|
private readonly Image _image;
|
||||||
private readonly Auto<DisposableImage> _imageAuto;
|
private readonly Auto<DisposableImage> _imageAuto;
|
||||||
private readonly Auto<MemoryAllocation> _allocationAuto;
|
private readonly Auto<MemoryAllocation> _allocationAuto;
|
||||||
|
private readonly int _depthOrLayers;
|
||||||
private Auto<MemoryAllocation> _foreignAllocationAuto;
|
private Auto<MemoryAllocation> _foreignAllocationAuto;
|
||||||
|
|
||||||
private Dictionary<Format, TextureStorage> _aliasedStorages;
|
private Dictionary<Format, TextureStorage> _aliasedStorages;
|
||||||
@@ -55,6 +62,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private int _viewsCount;
|
private int _viewsCount;
|
||||||
private readonly ulong _size;
|
private readonly ulong _size;
|
||||||
|
|
||||||
|
private int _bindCount;
|
||||||
|
private readonly TextureSliceInfo[] _slices;
|
||||||
|
|
||||||
public VkFormat VkFormat { get; }
|
public VkFormat VkFormat { get; }
|
||||||
|
|
||||||
public unsafe TextureStorage(
|
public unsafe TextureStorage(
|
||||||
@@ -75,6 +85,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
uint depth = (uint)(info.Target == Target.Texture3D ? info.Depth : 1);
|
uint depth = (uint)(info.Target == Target.Texture3D ? info.Depth : 1);
|
||||||
|
|
||||||
VkFormat = format;
|
VkFormat = format;
|
||||||
|
_depthOrLayers = info.GetDepthOrLayers();
|
||||||
|
|
||||||
ImageType type = info.Target.Convert();
|
ImageType type = info.Target.Convert();
|
||||||
|
|
||||||
@@ -150,6 +161,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
InitialTransition(ImageLayout.Preinitialized, ImageLayout.General);
|
InitialTransition(ImageLayout.Preinitialized, ImageLayout.General);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_slices = new TextureSliceInfo[levels * _depthOrLayers];
|
||||||
}
|
}
|
||||||
|
|
||||||
public TextureStorage CreateAliasedColorForDepthStorageUnsafe(Format format)
|
public TextureStorage CreateAliasedColorForDepthStorageUnsafe(Format format)
|
||||||
@@ -312,6 +325,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
usage |= ImageUsageFlags.StorageBit;
|
usage |= ImageUsageFlags.StorageBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (capabilities.SupportsAttachmentFeedbackLoop &&
|
||||||
|
(usage & (ImageUsageFlags.DepthStencilAttachmentBit | ImageUsageFlags.ColorAttachmentBit)) != 0)
|
||||||
|
{
|
||||||
|
usage |= ImageUsageFlags.AttachmentFeedbackLoopBitExt;
|
||||||
|
}
|
||||||
|
|
||||||
return usage;
|
return usage;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,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()
|
public void IncrementViewsCount()
|
||||||
{
|
{
|
||||||
_viewsCount++;
|
_viewsCount++;
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private readonly Auto<DisposableImageView> _imageView2dArray;
|
private readonly Auto<DisposableImageView> _imageView2dArray;
|
||||||
private Dictionary<Format, TextureView> _selfManagedViews;
|
private Dictionary<Format, TextureView> _selfManagedViews;
|
||||||
|
|
||||||
|
private int _hazardUses;
|
||||||
|
|
||||||
private readonly TextureCreateInfo _info;
|
private readonly TextureCreateInfo _info;
|
||||||
|
|
||||||
private HashTableSlim<RenderPassCacheKey, RenderPassHolder> _renderPasses;
|
private HashTableSlim<RenderPassCacheKey, RenderPassHolder> _renderPasses;
|
||||||
@@ -1037,6 +1039,34 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void PrepareForUsage(CommandBufferScoped cbs, PipelineStageFlags flags, List<TextureView> feedbackLoopHazards)
|
||||||
|
{
|
||||||
|
Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, flags);
|
||||||
|
|
||||||
|
if (feedbackLoopHazards != null && Storage.IsBound(this))
|
||||||
|
{
|
||||||
|
feedbackLoopHazards.Add(this);
|
||||||
|
_hazardUses++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearUsage(List<TextureView> feedbackLoopHazards)
|
||||||
|
{
|
||||||
|
if (_hazardUses != 0 && feedbackLoopHazards != null)
|
||||||
|
{
|
||||||
|
feedbackLoopHazards.Remove(this);
|
||||||
|
_hazardUses--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DecrementHazardUses()
|
||||||
|
{
|
||||||
|
if (_hazardUses != 0)
|
||||||
|
{
|
||||||
|
_hazardUses--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
|
public (RenderPassHolder rpHolder, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
|
||||||
VulkanRenderer gd,
|
VulkanRenderer gd,
|
||||||
Device device,
|
Device device,
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
"VK_EXT_4444_formats",
|
"VK_EXT_4444_formats",
|
||||||
"VK_KHR_8bit_storage",
|
"VK_KHR_8bit_storage",
|
||||||
"VK_KHR_maintenance2",
|
"VK_KHR_maintenance2",
|
||||||
|
"VK_EXT_attachment_feedback_loop_layout",
|
||||||
|
"VK_EXT_attachment_feedback_loop_dynamic_state"
|
||||||
];
|
];
|
||||||
|
|
||||||
private static readonly string[] _requiredExtensions =
|
private static readonly string[] _requiredExtensions =
|
||||||
@@ -360,6 +362,28 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
features2.PNext = &supportedFeaturesDepthClipControl;
|
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()
|
PhysicalDeviceVulkan12Features supportedPhysicalDeviceVulkan12Features = new()
|
||||||
{
|
{
|
||||||
SType = StructureType.PhysicalDeviceVulkan12Features,
|
SType = StructureType.PhysicalDeviceVulkan12Features,
|
||||||
@@ -534,6 +558,36 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
pExtendedFeatures = &featuresDepthClipControl;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
string[] enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray();
|
string[] enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray();
|
||||||
|
|
||||||
nint* ppEnabledExtensions = stackalloc nint[enabledExtensions.Length];
|
nint* ppEnabledExtensions = stackalloc nint[enabledExtensions.Length];
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
internal KhrPushDescriptor PushDescriptorApi { get; private set; }
|
internal KhrPushDescriptor PushDescriptorApi { get; private set; }
|
||||||
internal ExtTransformFeedback TransformFeedbackApi { get; private set; }
|
internal ExtTransformFeedback TransformFeedbackApi { get; private set; }
|
||||||
internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; }
|
internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; }
|
||||||
|
internal ExtAttachmentFeedbackLoopDynamicState DynamicFeedbackLoopApi { get; private set; }
|
||||||
|
|
||||||
internal uint QueueFamilyIndex { get; private set; }
|
internal uint QueueFamilyIndex { get; private set; }
|
||||||
internal Queue Queue { get; private set; }
|
internal Queue Queue { get; private set; }
|
||||||
@@ -157,6 +158,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
DrawIndirectCountApi = drawIndirectCountApi;
|
DrawIndirectCountApi = drawIndirectCountApi;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtAttachmentFeedbackLoopDynamicState dynamicFeedbackLoopApi))
|
||||||
|
{
|
||||||
|
DynamicFeedbackLoopApi = dynamicFeedbackLoopApi;
|
||||||
|
}
|
||||||
|
|
||||||
if (maxQueueCount >= 2)
|
if (maxQueueCount >= 2)
|
||||||
{
|
{
|
||||||
Api.GetDeviceQueue(_device, queueFamilyIndex, 1, out Queue backgroundQueue);
|
Api.GetDeviceQueue(_device, queueFamilyIndex, 1, out Queue backgroundQueue);
|
||||||
@@ -251,6 +257,16 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt,
|
SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT featuresAttachmentFeedbackLoop = new()
|
||||||
|
{
|
||||||
|
SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt,
|
||||||
|
};
|
||||||
|
|
||||||
|
PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT featuresDynamicAttachmentFeedbackLoop = new()
|
||||||
|
{
|
||||||
|
SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt,
|
||||||
|
};
|
||||||
|
|
||||||
PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new()
|
PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new()
|
||||||
{
|
{
|
||||||
SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr,
|
SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr,
|
||||||
@@ -287,6 +303,22 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
features2.PNext = &featuresDepthClipControl;
|
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");
|
bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset");
|
||||||
|
|
||||||
if (usePortability)
|
if (usePortability)
|
||||||
@@ -409,6 +441,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"),
|
_physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"),
|
||||||
_physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName),
|
_physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName),
|
||||||
supportsDepthClipControl && featuresDepthClipControl.DepthClipControl,
|
supportsDepthClipControl && featuresDepthClipControl.DepthClipControl,
|
||||||
|
supportsAttachmentFeedbackLoop && featuresAttachmentFeedbackLoop.AttachmentFeedbackLoopLayout,
|
||||||
|
supportsDynamicAttachmentFeedbackLoop && featuresDynamicAttachmentFeedbackLoop.AttachmentFeedbackLoopDynamicState,
|
||||||
propertiesSubgroup.SubgroupSize,
|
propertiesSubgroup.SubgroupSize,
|
||||||
supportedSampleCounts,
|
supportedSampleCounts,
|
||||||
portabilityFlags,
|
portabilityFlags,
|
||||||
|
|||||||
@@ -23,9 +23,6 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
{
|
{
|
||||||
class IUserLocalCommunicationService : IpcService, IDisposable
|
class IUserLocalCommunicationService : IpcService, IDisposable
|
||||||
{
|
{
|
||||||
public static string DefaultLanPlayHost = "ryuldn.vudjun.com";
|
|
||||||
public static short LanPlayPort = 30456;
|
|
||||||
|
|
||||||
public INetworkClient NetworkClient { get; private set; }
|
public INetworkClient NetworkClient { get; private set; }
|
||||||
|
|
||||||
private const int NifmRequestID = 90;
|
private const int NifmRequestID = 90;
|
||||||
@@ -1092,20 +1089,18 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
|
|||||||
case MultiplayerMode.LdnRyu:
|
case MultiplayerMode.LdnRyu:
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string ldnServer = context.Device.Configuration.MultiplayerLdnServer;
|
string ldnServer = context.Device.Configuration.MultiplayerLdnServer
|
||||||
if (string.IsNullOrEmpty(ldnServer))
|
?? throw new InvalidOperationException("Cannot initialize RyuLDN with a null Multiplayer server.");
|
||||||
{
|
|
||||||
ldnServer = DefaultLanPlayHost;
|
|
||||||
}
|
|
||||||
if (!IPAddress.TryParse(ldnServer, out IPAddress ipAddress))
|
if (!IPAddress.TryParse(ldnServer, out IPAddress ipAddress))
|
||||||
{
|
{
|
||||||
ipAddress = Dns.GetHostEntry(ldnServer).AddressList[0];
|
ipAddress = Dns.GetHostEntry(ldnServer).AddressList[0];
|
||||||
}
|
}
|
||||||
NetworkClient = new LdnMasterProxyClient(ipAddress.ToString(), LanPlayPort, context.Device.Configuration);
|
NetworkClient = new LdnMasterProxyClient(ipAddress.ToString(), SharedConstants.LanPlayPort, context.Device.Configuration);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.Error?.Print(LogClass.ServiceLdn, "Could not locate LdnRyu server. Defaulting to stubbed wireless.");
|
Logger.Error?.Print(LogClass.ServiceLdn, "Could not locate RyuLDN server. Defaulting to stubbed wireless.");
|
||||||
Logger.Error?.Print(LogClass.ServiceLdn, ex.Message);
|
Logger.Error?.Print(LogClass.ServiceLdn, ex.Message);
|
||||||
NetworkClient = new LdnDisabledClient();
|
NetworkClient = new LdnDisabledClient();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ namespace Ryujinx.Input.SDL2
|
|||||||
byte blue = packedRgb > 0 ? (byte)(packedRgb % 256) : (byte)0;
|
byte blue = packedRgb > 0 ? (byte)(packedRgb % 256) : (byte)0;
|
||||||
|
|
||||||
if (SDL_GameControllerSetLED(_gamepadHandle, red, green, blue) != 0)
|
if (SDL_GameControllerSetLED(_gamepadHandle, red, green, blue) != 0)
|
||||||
Logger.Error?.Print(LogClass.Hid, "LED setting failed; probably in the middle of disconnecting.");
|
Logger.Debug?.Print(LogClass.Hid, "LED setting failed; probably in the middle of disconnecting.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private GamepadFeaturesFlag GetFeaturesFlag()
|
private GamepadFeaturesFlag GetFeaturesFlag()
|
||||||
|
|||||||
@@ -951,7 +951,7 @@ namespace Ryujinx.Ava
|
|||||||
ConfigurationState.Instance.Multiplayer.Mode,
|
ConfigurationState.Instance.Multiplayer.Mode,
|
||||||
ConfigurationState.Instance.Multiplayer.DisableP2p,
|
ConfigurationState.Instance.Multiplayer.DisableP2p,
|
||||||
ConfigurationState.Instance.Multiplayer.LdnPassphrase,
|
ConfigurationState.Instance.Multiplayer.LdnPassphrase,
|
||||||
ConfigurationState.Instance.Multiplayer.LdnServer,
|
ConfigurationState.Instance.Multiplayer.GetLdnServer(),
|
||||||
ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value,
|
ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value,
|
||||||
ConfigurationState.Instance.Hacks.ShowDirtyHacks ? ConfigurationState.Instance.Hacks.EnabledHacks : null));
|
ConfigurationState.Instance.Hacks.ShowDirtyHacks ? ConfigurationState.Instance.Hacks.EnabledHacks : null));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -468,7 +468,7 @@
|
|||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "Відкрити теку скріншотів",
|
"uk_UA": "Відкрити теку скріншотів",
|
||||||
"zh_CN": "",
|
"zh_CN": "打开截图文件夹",
|
||||||
"zh_TW": ""
|
"zh_TW": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -5143,7 +5143,7 @@
|
|||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "Ігнорувати Аплет Контролера",
|
"uk_UA": "Ігнорувати Аплет Контролера",
|
||||||
"zh_CN": "",
|
"zh_CN": "忽略控制器小程序",
|
||||||
"zh_TW": ""
|
"zh_TW": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -16518,7 +16518,7 @@
|
|||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "Діалогове вікно Аплету Контролера не з'явиться, якщо геймпад було відключено під час роботи програми.\n\nЗалиште вимкненим якщо не впевнені.",
|
"uk_UA": "Діалогове вікно Аплету Контролера не з'явиться, якщо геймпад було відключено під час роботи програми.\n\nЗалиште вимкненим якщо не впевнені.",
|
||||||
"zh_CN": "",
|
"zh_CN": "在应用程序运行时如果游戏手柄断开连接则不会显示控制器小程序对话框。\n\n如果不确定,请保持关闭状态。",
|
||||||
"zh_TW": ""
|
"zh_TW": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -17193,7 +17193,7 @@
|
|||||||
"th_TH": "",
|
"th_TH": "",
|
||||||
"tr_TR": "",
|
"tr_TR": "",
|
||||||
"uk_UA": "Відкрити теку куди зберігаються скріншоти Ryujinx",
|
"uk_UA": "Відкрити теку куди зберігаються скріншоти Ryujinx",
|
||||||
"zh_CN": "",
|
"zh_CN": "打开 Ryujinx 截图文件夹",
|
||||||
"zh_TW": ""
|
"zh_TW": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -138,8 +138,8 @@ namespace Ryujinx.Ava
|
|||||||
// Logging system information.
|
// Logging system information.
|
||||||
PrintSystemInfo();
|
PrintSystemInfo();
|
||||||
|
|
||||||
// Enable OGL multithreading on the driver, when available.
|
// Enable OGL multithreading on the driver, and some other flags.
|
||||||
DriverUtilities.ToggleOGLThreading(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off);
|
DriverUtilities.InitDriverConfig(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off);
|
||||||
|
|
||||||
// Check if keys exists.
|
// Check if keys exists.
|
||||||
if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys")))
|
if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys")))
|
||||||
|
|||||||
@@ -148,8 +148,11 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
{
|
{
|
||||||
if ((firmwarePath.ExistsAsFile && firmwarePath.Extension is "xci" or "zip") ||
|
if ((firmwarePath.ExistsAsFile && firmwarePath.Extension is "xci" or "zip") ||
|
||||||
firmwarePath.ExistsAsDirectory)
|
firmwarePath.ExistsAsDirectory)
|
||||||
|
{
|
||||||
await Dispatcher.UIThread.InvokeAsync(() =>
|
await Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
ViewModel.HandleFirmwareInstallation(firmwarePath));
|
ViewModel.HandleFirmwareInstallation(firmwarePath));
|
||||||
|
CommandLineState.FirmwareToInstallPathArg = null;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
Logger.Notice.Print(LogClass.UI, "Invalid firmware type provided. Path must be a directory, or a .zip or .xci file.");
|
Logger.Notice.Print(LogClass.UI, "Invalid firmware type provided. Path must be a directory, or a .zip or .xci file.");
|
||||||
}
|
}
|
||||||
@@ -186,17 +189,12 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(() =>
|
Dispatcher.UIThread.Post(() =>
|
||||||
{
|
{
|
||||||
List<LdnGameData> ldnGameDataArray = e.LdnData.ToList();
|
|
||||||
ViewModel.LdnData.Clear();
|
ViewModel.LdnData.Clear();
|
||||||
foreach (ApplicationData application in ViewModel.Applications.Where(it => it.HasControlHolder))
|
foreach (ApplicationData application in ViewModel.Applications.Where(it => it.HasControlHolder))
|
||||||
{
|
{
|
||||||
ref ApplicationControlProperty controlHolder = ref application.ControlHolder.Value;
|
ref ApplicationControlProperty controlHolder = ref application.ControlHolder.Value;
|
||||||
|
|
||||||
ViewModel.LdnData[application.IdString] =
|
ViewModel.LdnData[application.IdString] = e.LdnData.Where(ref controlHolder);
|
||||||
LdnGameData.GetArrayForApp(
|
|
||||||
ldnGameDataArray,
|
|
||||||
ref controlHolder
|
|
||||||
);
|
|
||||||
|
|
||||||
UpdateApplicationWithLdnData(application);
|
UpdateApplicationWithLdnData(application);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
|||||||
{
|
{
|
||||||
public class ApplicationLibrary
|
public class ApplicationLibrary
|
||||||
{
|
{
|
||||||
public const string DefaultLanPlayWebHost = "ryuldnweb.vudjun.com";
|
|
||||||
public Language DesiredLanguage { get; set; }
|
public Language DesiredLanguage { get; set; }
|
||||||
public event EventHandler<ApplicationCountUpdatedEventArgs> ApplicationCountUpdated;
|
public event EventHandler<ApplicationCountUpdatedEventArgs> ApplicationCountUpdated;
|
||||||
public event Action<LdnGameDataReceivedEventArgs> LdnGameDataReceived;
|
public event Action<LdnGameDataReceivedEventArgs> LdnGameDataReceived;
|
||||||
@@ -826,7 +825,6 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
|||||||
|
|
||||||
public async Task RefreshLdn()
|
public async Task RefreshLdn()
|
||||||
{
|
{
|
||||||
|
|
||||||
if (ConfigurationState.Instance.Multiplayer.Mode == MultiplayerMode.LdnRyu)
|
if (ConfigurationState.Instance.Multiplayer.Mode == MultiplayerMode.LdnRyu)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -834,33 +832,22 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
|||||||
string ldnWebHost = ConfigurationState.Instance.Multiplayer.LdnServer;
|
string ldnWebHost = ConfigurationState.Instance.Multiplayer.LdnServer;
|
||||||
if (string.IsNullOrEmpty(ldnWebHost))
|
if (string.IsNullOrEmpty(ldnWebHost))
|
||||||
{
|
{
|
||||||
ldnWebHost = DefaultLanPlayWebHost;
|
ldnWebHost = SharedConstants.DefaultLanPlayWebHost;
|
||||||
}
|
}
|
||||||
IEnumerable<LdnGameData> ldnGameDataArray = Array.Empty<LdnGameData>();
|
|
||||||
using HttpClient httpClient = new();
|
using HttpClient httpClient = new();
|
||||||
string ldnGameDataArrayString = await httpClient.GetStringAsync($"https://{ldnWebHost}/api/public_games");
|
string ldnGameDataArrayString = await httpClient.GetStringAsync($"https://{ldnWebHost}/api/public_games");
|
||||||
ldnGameDataArray = JsonHelper.Deserialize(ldnGameDataArrayString, _ldnDataSerializerContext.IEnumerableLdnGameData);
|
LdnGameData[] ldnGameDataArray = JsonHelper.Deserialize(ldnGameDataArrayString, _ldnDataSerializerContext.IEnumerableLdnGameData).ToArray();
|
||||||
LdnGameDataReceived?.Invoke(new LdnGameDataReceivedEventArgs
|
LdnGameDataReceived?.Invoke(new LdnGameDataReceivedEventArgs(ldnGameDataArray));
|
||||||
{
|
return;
|
||||||
LdnData = ldnGameDataArray
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Application, $"Failed to fetch the public games JSON from the API. Player and game count in the game list will be unavailable.\n{ex.Message}");
|
Logger.Warning?.Print(LogClass.Application, $"Failed to fetch the public games JSON from the API. Player and game count in the game list will be unavailable.\n{ex.Message}");
|
||||||
LdnGameDataReceived?.Invoke(new LdnGameDataReceivedEventArgs
|
|
||||||
{
|
|
||||||
LdnData = Array.Empty<LdnGameData>()
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
LdnGameDataReceived?.Invoke(LdnGameDataReceivedEventArgs.Empty);
|
||||||
LdnGameDataReceived?.Invoke(new LdnGameDataReceivedEventArgs
|
|
||||||
{
|
|
||||||
LdnData = Array.Empty<LdnGameData>()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace the currently stored DLC state for the game with the provided DLC state.
|
// Replace the currently stored DLC state for the game with the provided DLC state.
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
|||||||
public IEnumerable<string> Players { get; set; }
|
public IEnumerable<string> Players { get; set; }
|
||||||
|
|
||||||
public static Array GetArrayForApp(
|
public static Array GetArrayForApp(
|
||||||
IEnumerable<LdnGameData> receivedData, ref ApplicationControlProperty acp)
|
LdnGameData[] receivedData, ref ApplicationControlProperty acp)
|
||||||
{
|
{
|
||||||
LibHac.Common.FixedArrays.Array8<ulong> communicationId = acp.LocalCommunicationId;
|
LibHac.Common.FixedArrays.Array8<ulong> communicationId = acp.LocalCommunicationId;
|
||||||
|
|
||||||
@@ -40,4 +40,10 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
|||||||
public int GameCount => _ldnDatas.Length;
|
public int GameCount => _ldnDatas.Length;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class LdnGameDataHelper
|
||||||
|
{
|
||||||
|
public static LdnGameData.Array Where(this LdnGameData[] unfilteredDatas, ref ApplicationControlProperty acp)
|
||||||
|
=> LdnGameData.GetArrayForApp(unfilteredDatas, ref acp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,14 @@ namespace Ryujinx.Ava.Utilities.AppLibrary
|
|||||||
{
|
{
|
||||||
public class LdnGameDataReceivedEventArgs : EventArgs
|
public class LdnGameDataReceivedEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public IEnumerable<LdnGameData> LdnData { get; set; }
|
public static new readonly LdnGameDataReceivedEventArgs Empty = new(null);
|
||||||
|
|
||||||
|
public LdnGameDataReceivedEventArgs(LdnGameData[] ldnData)
|
||||||
|
{
|
||||||
|
LdnData = ldnData ?? [];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public LdnGameData[] LdnData { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ namespace Ryujinx.Ava.Utilities
|
|||||||
public static string OverrideBackendThreading { get; private set; }
|
public static string OverrideBackendThreading { get; private set; }
|
||||||
public static string OverrideHideCursor { get; private set; }
|
public static string OverrideHideCursor { get; private set; }
|
||||||
public static string BaseDirPathArg { get; private set; }
|
public static string BaseDirPathArg { get; private set; }
|
||||||
public static FilePath FirmwareToInstallPathArg { get; private set; }
|
public static FilePath FirmwareToInstallPathArg { get; set; }
|
||||||
public static string Profile { get; private set; }
|
public static string Profile { get; private set; }
|
||||||
public static string LaunchPathArg { get; private set; }
|
public static string LaunchPathArg { get; private set; }
|
||||||
public static string LaunchApplicationId { get; private set; }
|
public static string LaunchApplicationId { get; private set; }
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using ARMeilleure;
|
using ARMeilleure;
|
||||||
using Gommon;
|
using Gommon;
|
||||||
|
using Ryujinx.Ava.Utilities.AppLibrary;
|
||||||
using Ryujinx.Ava.Utilities.Configuration.System;
|
using Ryujinx.Ava.Utilities.Configuration.System;
|
||||||
using Ryujinx.Ava.Utilities.Configuration.UI;
|
using Ryujinx.Ava.Utilities.Configuration.UI;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
@@ -647,6 +648,14 @@ namespace Ryujinx.Ava.Utilities.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ReactiveObject<string> LdnServer { get; private set; }
|
public ReactiveObject<string> LdnServer { get; private set; }
|
||||||
|
|
||||||
|
public string GetLdnServer()
|
||||||
|
{
|
||||||
|
string ldnServer = LdnServer;
|
||||||
|
return string.IsNullOrEmpty(ldnServer)
|
||||||
|
? SharedConstants.DefaultLanPlayHost
|
||||||
|
: ldnServer;
|
||||||
|
}
|
||||||
|
|
||||||
public MultiplayerSection()
|
public MultiplayerSection()
|
||||||
{
|
{
|
||||||
LanInterfaceId = new ReactiveObject<string>();
|
LanInterfaceId = new ReactiveObject<string>();
|
||||||
|
|||||||
Reference in New Issue
Block a user