mirror of
https://github.com/Ryubing/Ryujinx.git
synced 2025-11-18 04:50:55 -05:00
Compare commits
14 Commits
60b7f7a6ce
...
feature/tu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3247145155 | ||
|
|
709f0dd59c | ||
|
|
aa4a983056 | ||
|
|
51f3554f82 | ||
|
|
8e285579ae | ||
|
|
3644e3fd92 | ||
|
|
3abee2a0be | ||
|
|
94f34a9ed1 | ||
|
|
776c0cb5cf | ||
|
|
ef1529a2d9 | ||
|
|
768406cb67 | ||
|
|
ed5cb82aa8 | ||
|
|
c48a2e6ba0 | ||
|
|
90f2b089eb |
@@ -13,5 +13,7 @@ namespace Ryujinx.Common.Configuration.Hid
|
|||||||
public Key VolumeDown { get; set; }
|
public Key VolumeDown { get; set; }
|
||||||
public Key CustomVSyncIntervalIncrement { get; set; }
|
public Key CustomVSyncIntervalIncrement { get; set; }
|
||||||
public Key CustomVSyncIntervalDecrement { get; set; }
|
public Key CustomVSyncIntervalDecrement { get; set; }
|
||||||
|
public Key TurboMode { get; set; }
|
||||||
|
public bool TurboModeWhileHeld { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,11 +8,18 @@ namespace Ryujinx.Cpu
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ITickSource : ICounter
|
public interface ITickSource : ICounter
|
||||||
{
|
{
|
||||||
|
public const long RealityTickScalar = 100;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Time elapsed since the counter was created.
|
/// Time elapsed since the counter was created.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
TimeSpan ElapsedTime { get; }
|
TimeSpan ElapsedTime { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clock tick scalar, in percent points (100 = 1.0).
|
||||||
|
/// </summary>
|
||||||
|
long TickScalar { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Time elapsed since the counter was created, in seconds.
|
/// Time elapsed since the counter was created, in seconds.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -15,11 +15,36 @@ namespace Ryujinx.Cpu
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public ulong Counter => (ulong)(ElapsedSeconds * Frequency);
|
public ulong Counter => (ulong)(ElapsedSeconds * Frequency);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
|
||||||
public TimeSpan ElapsedTime => _tickCounter.Elapsed;
|
public long TickScalar { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
private static long _acumElapsedTicks;
|
||||||
|
|
||||||
|
|
||||||
|
private static long _lastElapsedTicks;
|
||||||
|
|
||||||
|
|
||||||
|
private long ElapsedTicks
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
long elapsedTicks = _tickCounter.ElapsedTicks;
|
||||||
|
|
||||||
|
_acumElapsedTicks += (elapsedTicks - _lastElapsedTicks) * TickScalar / 100;
|
||||||
|
|
||||||
|
_lastElapsedTicks = elapsedTicks;
|
||||||
|
|
||||||
|
return _acumElapsedTicks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public double ElapsedSeconds => _tickCounter.ElapsedTicks * _hostTickFreq;
|
|
||||||
|
public TimeSpan ElapsedTime => Stopwatch.GetElapsedTime(0, ElapsedTicks);
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public double ElapsedSeconds => ElapsedTicks * _hostTickFreq;
|
||||||
|
|
||||||
public TickSource(ulong frequency)
|
public TickSource(ulong frequency)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1065,7 +1065,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateIndexBufferState()
|
private void UpdateIndexBufferState()
|
||||||
{
|
{
|
||||||
IndexBufferState indexBuffer = _state.State.IndexBufferState;
|
IndexBufferState? indexBufferNullable = _state?.State.IndexBufferState;
|
||||||
|
|
||||||
|
if (!indexBufferNullable.HasValue)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
IndexBufferState indexBuffer = indexBufferNullable.Value;
|
||||||
|
|
||||||
if (_drawState.IndexCount == 0)
|
if (_drawState.IndexCount == 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -127,10 +127,7 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
string serviceName;
|
string serviceName = (service is not DummyService dummyService) ? service.GetType().FullName : dummyService.ServiceName;
|
||||||
|
|
||||||
|
|
||||||
serviceName = (service is not DummyService dummyService) ? service.GetType().FullName : dummyService.ServiceName;
|
|
||||||
|
|
||||||
Logger.Warning?.Print(LogClass.KernelIpc, $"Missing service {serviceName}: {commandId} ignored");
|
Logger.Warning?.Print(LogClass.KernelIpc, $"Missing service {serviceName}: {commandId} ignored");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using Ryujinx.Common;
|
|||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.PreciseSleep;
|
using Ryujinx.Common.PreciseSleep;
|
||||||
|
using Ryujinx.Cpu;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.Gpu;
|
using Ryujinx.Graphics.Gpu;
|
||||||
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
|
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
|
||||||
@@ -89,7 +90,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_ticksPerFrame = Stopwatch.Frequency / _device.TargetVSyncInterval;
|
_ticksPerFrame = ((Stopwatch.Frequency / _device.TargetVSyncInterval) * 100) / _device.TickScalar;
|
||||||
_targetVSyncInterval = _device.TargetVSyncInterval;
|
_targetVSyncInterval = _device.TargetVSyncInterval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,6 +103,11 @@ namespace Ryujinx.HLE
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal readonly bool EnablePtc;
|
internal readonly bool EnablePtc;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Control the arbitrary scalar applied to emulated CPU tick timing.
|
||||||
|
/// </summary>
|
||||||
|
public long TickScalar { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Control if the guest application should be told that there is a Internet connection available.
|
/// Control if the guest application should be told that there is a Internet connection available.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -201,6 +206,7 @@ namespace Ryujinx.HLE
|
|||||||
VSyncMode vSyncMode,
|
VSyncMode vSyncMode,
|
||||||
bool enableDockedMode,
|
bool enableDockedMode,
|
||||||
bool enablePtc,
|
bool enablePtc,
|
||||||
|
long tickScalar,
|
||||||
bool enableInternetAccess,
|
bool enableInternetAccess,
|
||||||
IntegrityCheckLevel fsIntegrityCheckLevel,
|
IntegrityCheckLevel fsIntegrityCheckLevel,
|
||||||
int fsGlobalAccessLogMode,
|
int fsGlobalAccessLogMode,
|
||||||
@@ -226,6 +232,7 @@ namespace Ryujinx.HLE
|
|||||||
CustomVSyncInterval = customVSyncInterval;
|
CustomVSyncInterval = customVSyncInterval;
|
||||||
EnableDockedMode = enableDockedMode;
|
EnableDockedMode = enableDockedMode;
|
||||||
EnablePtc = enablePtc;
|
EnablePtc = enablePtc;
|
||||||
|
TickScalar = tickScalar;
|
||||||
EnableInternetAccess = enableInternetAccess;
|
EnableInternetAccess = enableInternetAccess;
|
||||||
FsIntegrityCheckLevel = fsIntegrityCheckLevel;
|
FsIntegrityCheckLevel = fsIntegrityCheckLevel;
|
||||||
FsGlobalAccessLogMode = fsGlobalAccessLogMode;
|
FsGlobalAccessLogMode = fsGlobalAccessLogMode;
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ namespace Ryujinx.HLE
|
|||||||
{
|
{
|
||||||
public class PerformanceStatistics
|
public class PerformanceStatistics
|
||||||
{
|
{
|
||||||
|
private readonly Switch _device;
|
||||||
|
|
||||||
private const int FrameTypeGame = 0;
|
private const int FrameTypeGame = 0;
|
||||||
private const int PercentTypeFifo = 0;
|
private const int PercentTypeFifo = 0;
|
||||||
|
|
||||||
@@ -28,8 +30,10 @@ namespace Ryujinx.HLE
|
|||||||
|
|
||||||
private readonly System.Timers.Timer _resetTimer;
|
private readonly System.Timers.Timer _resetTimer;
|
||||||
|
|
||||||
public PerformanceStatistics()
|
public PerformanceStatistics(Switch device)
|
||||||
{
|
{
|
||||||
|
_device = device;
|
||||||
|
|
||||||
_frameRate = new double[1];
|
_frameRate = new double[1];
|
||||||
_accumulatedFrameTime = new double[1];
|
_accumulatedFrameTime = new double[1];
|
||||||
_previousFrameTime = new double[1];
|
_previousFrameTime = new double[1];
|
||||||
@@ -162,14 +166,6 @@ namespace Ryujinx.HLE
|
|||||||
return 1000 / _frameRate[FrameTypeGame];
|
return 1000 / _frameRate[FrameTypeGame];
|
||||||
}
|
}
|
||||||
|
|
||||||
public string FormatGameFrameRate()
|
|
||||||
{
|
|
||||||
double frameRate = GetGameFrameRate();
|
|
||||||
double frameTime = GetGameFrameTime();
|
|
||||||
|
|
||||||
return $"{frameRate:00.00} FPS ({frameTime:00.00}ms)";
|
|
||||||
}
|
|
||||||
|
|
||||||
public string FormatFifoPercent()
|
public string FormatFifoPercent()
|
||||||
{
|
{
|
||||||
double fifoPercent = GetFifoPercent();
|
double fifoPercent = GetFifoPercent();
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Ryujinx.Audio.Backends.CompatLayer;
|
|||||||
using Ryujinx.Audio.Integration;
|
using Ryujinx.Audio.Integration;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
|
using Ryujinx.Cpu;
|
||||||
using Ryujinx.Graphics.Gpu;
|
using Ryujinx.Graphics.Gpu;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
using Ryujinx.HLE.HOS;
|
using Ryujinx.HLE.HOS;
|
||||||
@@ -26,18 +27,26 @@ namespace Ryujinx.HLE
|
|||||||
public GpuContext Gpu { get; }
|
public GpuContext Gpu { get; }
|
||||||
public VirtualFileSystem FileSystem { get; }
|
public VirtualFileSystem FileSystem { get; }
|
||||||
public HOS.Horizon System { get; }
|
public HOS.Horizon System { get; }
|
||||||
|
|
||||||
|
public bool TurboMode = false;
|
||||||
|
|
||||||
|
public long TickScalar
|
||||||
|
{
|
||||||
|
get => System?.TickSource?.TickScalar ?? ITickSource.RealityTickScalar;
|
||||||
|
set => System.TickSource.TickScalar = value;
|
||||||
|
}
|
||||||
|
|
||||||
public ProcessLoader Processes { get; }
|
public ProcessLoader Processes { get; }
|
||||||
public PerformanceStatistics Statistics { get; }
|
public PerformanceStatistics Statistics { get; }
|
||||||
public Hid Hid { get; }
|
public Hid Hid { get; }
|
||||||
public TamperMachine TamperMachine { get; }
|
public TamperMachine TamperMachine { get; }
|
||||||
public IHostUIHandler UIHandler { get; }
|
public IHostUIHandler UIHandler { get; }
|
||||||
|
|
||||||
public int CpuCoresCount = 4; //Switch 1 has 4 cores
|
public int CpuCoresCount = 4; // Switch has a quad-core Tegra X1 SoC
|
||||||
|
|
||||||
public VSyncMode VSyncMode { get; set; }
|
public VSyncMode VSyncMode { get; set; }
|
||||||
public bool CustomVSyncIntervalEnabled { get; set; }
|
public bool CustomVSyncIntervalEnabled { get; set; }
|
||||||
public int CustomVSyncInterval { get; set; }
|
public int CustomVSyncInterval { get; set; }
|
||||||
|
|
||||||
public long TargetVSyncInterval { get; set; } = 60;
|
public long TargetVSyncInterval { get; set; } = 60;
|
||||||
|
|
||||||
public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable;
|
public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable;
|
||||||
@@ -64,7 +73,7 @@ namespace Ryujinx.HLE
|
|||||||
Memory = new MemoryBlock(Configuration.MemoryConfiguration.ToDramSize(), memoryAllocationFlags);
|
Memory = new MemoryBlock(Configuration.MemoryConfiguration.ToDramSize(), memoryAllocationFlags);
|
||||||
Gpu = new GpuContext(Configuration.GpuRenderer, DirtyHacks);
|
Gpu = new GpuContext(Configuration.GpuRenderer, DirtyHacks);
|
||||||
System = new HOS.Horizon(this);
|
System = new HOS.Horizon(this);
|
||||||
Statistics = new PerformanceStatistics();
|
Statistics = new PerformanceStatistics(this);
|
||||||
Hid = new Hid(this, System.HidStorage);
|
Hid = new Hid(this, System.HidStorage);
|
||||||
Processes = new ProcessLoader(this);
|
Processes = new ProcessLoader(this);
|
||||||
TamperMachine = new TamperMachine();
|
TamperMachine = new TamperMachine();
|
||||||
@@ -75,6 +84,7 @@ namespace Ryujinx.HLE
|
|||||||
|
|
||||||
VSyncMode = Configuration.VSyncMode;
|
VSyncMode = Configuration.VSyncMode;
|
||||||
CustomVSyncInterval = Configuration.CustomVSyncInterval;
|
CustomVSyncInterval = Configuration.CustomVSyncInterval;
|
||||||
|
TickScalar = TurboMode ? Configuration.TickScalar : ITickSource.RealityTickScalar;
|
||||||
System.State.DockedMode = Configuration.EnableDockedMode;
|
System.State.DockedMode = Configuration.EnableDockedMode;
|
||||||
System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
|
System.PerformanceState.PerformanceMode = System.State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
|
||||||
System.EnablePtc = Configuration.EnablePtc;
|
System.EnablePtc = Configuration.EnablePtc;
|
||||||
@@ -126,6 +136,12 @@ namespace Ryujinx.HLE
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ToggleTurbo()
|
||||||
|
{
|
||||||
|
TurboMode = !TurboMode;
|
||||||
|
TickScalar = TurboMode ? Configuration.TickScalar : ITickSource.RealityTickScalar;
|
||||||
|
}
|
||||||
|
|
||||||
public bool LoadCart(string exeFsDir, string romFsFile = null) => Processes.LoadUnpackedNca(exeFsDir, romFsFile);
|
public bool LoadCart(string exeFsDir, string romFsFile = null) => Processes.LoadUnpackedNca(exeFsDir, romFsFile);
|
||||||
public bool LoadXci(string xciFile, ulong applicationId = 0) => Processes.LoadXci(xciFile, applicationId);
|
public bool LoadXci(string xciFile, ulong applicationId = 0) => Processes.LoadXci(xciFile, applicationId);
|
||||||
public bool LoadNca(string ncaFile, BlitStruct<ApplicationControlProperty>? customNacpData = null) => Processes.LoadNca(ncaFile, customNacpData);
|
public bool LoadNca(string ncaFile, BlitStruct<ApplicationControlProperty>? customNacpData = null) => Processes.LoadNca(ncaFile, customNacpData);
|
||||||
|
|||||||
@@ -4922,6 +4922,81 @@
|
|||||||
"zh_TW": "低功耗 PPTC"
|
"zh_TW": "低功耗 PPTC"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ID": "SettingsTabSystemTurboMultiplier",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "",
|
||||||
|
"de_DE": "",
|
||||||
|
"el_GR": "",
|
||||||
|
"en_US": "Turbo Mode multiplier:",
|
||||||
|
"es_ES": "",
|
||||||
|
"fr_FR": "Multiplicateur du Mode Turbo :",
|
||||||
|
"he_IL": "",
|
||||||
|
"it_IT": "",
|
||||||
|
"ja_JP": "",
|
||||||
|
"ko_KR": "",
|
||||||
|
"no_NO": "",
|
||||||
|
"pl_PL": "",
|
||||||
|
"pt_BR": "",
|
||||||
|
"ru_RU": "",
|
||||||
|
"sv_SE": "",
|
||||||
|
"th_TH": "",
|
||||||
|
"tr_TR": "",
|
||||||
|
"uk_UA": "",
|
||||||
|
"zh_CN": "",
|
||||||
|
"zh_TW": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ID": "SettingsTabSystemTurboMultiplierValueToolTip",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "",
|
||||||
|
"de_DE": "",
|
||||||
|
"el_GR": "",
|
||||||
|
"en_US": "The Turbo mode multiplier target value.\n\nLeave at 200 if unsure.",
|
||||||
|
"es_ES": "",
|
||||||
|
"fr_FR": "La valeur souhaitée du multiplicateur du Mode Turbo.\n\nGarder à 200 si incertain.",
|
||||||
|
"he_IL": "",
|
||||||
|
"it_IT": "",
|
||||||
|
"ja_JP": "",
|
||||||
|
"ko_KR": "",
|
||||||
|
"no_NO": "",
|
||||||
|
"pl_PL": "",
|
||||||
|
"pt_BR": "",
|
||||||
|
"ru_RU": "",
|
||||||
|
"sv_SE": "",
|
||||||
|
"th_TH": "",
|
||||||
|
"tr_TR": "",
|
||||||
|
"uk_UA": "",
|
||||||
|
"zh_CN": "",
|
||||||
|
"zh_TW": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ID": "SettingsTabSystemTurboMultiplierToolTip",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "",
|
||||||
|
"de_DE": "",
|
||||||
|
"el_GR": "",
|
||||||
|
"en_US": "Turbo mode is an emulator feature which effectively causes speed up or slow down when a game is not frame-rate sensitive.\nYou can toggle this feature in-game with a hotkey, configurable in Ryujinx Keyboard Hotkeys settings.\n\nLeave at 200 if unsure.",
|
||||||
|
"es_ES": "",
|
||||||
|
"fr_FR": "Le Mode Turbo est une fonctionnalité de l'émulateur qui accélère ou ralentit le jeu lorsque ce dernier n'est pas sensible au framerate.\nVous pouvez changer cette option en jeu avec un raccourci clavier, configurable dans les paramètres de Raccourcis clavier de Ryujinx.\n\nGarder à 200 si incertain.",
|
||||||
|
"he_IL": "",
|
||||||
|
"it_IT": "",
|
||||||
|
"ja_JP": "",
|
||||||
|
"ko_KR": "",
|
||||||
|
"no_NO": "",
|
||||||
|
"pl_PL": "",
|
||||||
|
"pt_BR": "",
|
||||||
|
"ru_RU": "",
|
||||||
|
"sv_SE": "",
|
||||||
|
"th_TH": "",
|
||||||
|
"tr_TR": "",
|
||||||
|
"uk_UA": "",
|
||||||
|
"zh_CN": "",
|
||||||
|
"zh_TW": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ID": "SettingsTabSystemEnableFsIntegrityChecks",
|
"ID": "SettingsTabSystemEnableFsIntegrityChecks",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
@@ -10505,7 +10580,7 @@
|
|||||||
"el_GR": "",
|
"el_GR": "",
|
||||||
"en_US": "Unbound",
|
"en_US": "Unbound",
|
||||||
"es_ES": "",
|
"es_ES": "",
|
||||||
"fr_FR": "Pas Attribuée",
|
"fr_FR": "Non Attribuée",
|
||||||
"he_IL": "",
|
"he_IL": "",
|
||||||
"it_IT": "Non assegnato",
|
"it_IT": "Non assegnato",
|
||||||
"ja_JP": "",
|
"ja_JP": "",
|
||||||
@@ -18097,6 +18172,56 @@
|
|||||||
"zh_TW": "更新已停用!"
|
"zh_TW": "更新已停用!"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ID": "FpsStatusBarText",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "",
|
||||||
|
"de_DE": "",
|
||||||
|
"el_GR": "",
|
||||||
|
"en_US": "{0} FPS ({1}ms)",
|
||||||
|
"es_ES": "",
|
||||||
|
"fr_FR": "",
|
||||||
|
"he_IL": "",
|
||||||
|
"it_IT": "",
|
||||||
|
"ja_JP": "",
|
||||||
|
"ko_KR": "",
|
||||||
|
"no_NO": "",
|
||||||
|
"pl_PL": "",
|
||||||
|
"pt_BR": "",
|
||||||
|
"ru_RU": "",
|
||||||
|
"sv_SE": "",
|
||||||
|
"th_TH": "",
|
||||||
|
"tr_TR": "",
|
||||||
|
"uk_UA": "",
|
||||||
|
"zh_CN": "",
|
||||||
|
"zh_TW": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ID": "FpsTurboStatusBarText",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "{0} FPS ({1}ms), التوربو %{2}",
|
||||||
|
"de_DE": "",
|
||||||
|
"el_GR": "",
|
||||||
|
"en_US": "{0} FPS ({1}ms), Turbo ({2}%)",
|
||||||
|
"es_ES": "",
|
||||||
|
"fr_FR": "",
|
||||||
|
"he_IL": "",
|
||||||
|
"it_IT": "",
|
||||||
|
"ja_JP": "",
|
||||||
|
"ko_KR": "",
|
||||||
|
"no_NO": "",
|
||||||
|
"pl_PL": "",
|
||||||
|
"pt_BR": "",
|
||||||
|
"ru_RU": "",
|
||||||
|
"sv_SE": "",
|
||||||
|
"th_TH": "",
|
||||||
|
"tr_TR": "",
|
||||||
|
"uk_UA": "",
|
||||||
|
"zh_CN": "",
|
||||||
|
"zh_TW": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ID": "UpdaterBackgroundStatusBarButtonText",
|
"ID": "UpdaterBackgroundStatusBarButtonText",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
@@ -23822,6 +23947,81 @@
|
|||||||
"zh_TW": "降低自訂的重新整理頻率"
|
"zh_TW": "降低自訂的重新整理頻率"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"ID": "SettingsTabHotkeysTurboMode",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "",
|
||||||
|
"de_DE": "",
|
||||||
|
"el_GR": "",
|
||||||
|
"en_US": "Turbo mode:",
|
||||||
|
"es_ES": "",
|
||||||
|
"fr_FR": "Mode Turbo :",
|
||||||
|
"he_IL": "",
|
||||||
|
"it_IT": "",
|
||||||
|
"ja_JP": "",
|
||||||
|
"ko_KR": "",
|
||||||
|
"no_NO": "",
|
||||||
|
"pl_PL": "",
|
||||||
|
"pt_BR": "",
|
||||||
|
"ru_RU": "",
|
||||||
|
"sv_SE": "",
|
||||||
|
"th_TH": "",
|
||||||
|
"tr_TR": "",
|
||||||
|
"uk_UA": "",
|
||||||
|
"zh_CN": "",
|
||||||
|
"zh_TW": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ID": "SettingsTabHotkeysTurboModeToolTip",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "",
|
||||||
|
"de_DE": "",
|
||||||
|
"el_GR": "",
|
||||||
|
"en_US": "The Turbo mode hotkey.\nConfigure the behavior of Turbo mode in Ryujinx CPU settings.\n\nLeave Unbound if unsure.",
|
||||||
|
"es_ES": "",
|
||||||
|
"fr_FR": "Le raccourci clavier Mode Turbo.\nConfigurez le comportement du Mode Turbo dans les paramètres de CPU de Ryujinx.\n\nLaisser Non Attribuée si incertain.",
|
||||||
|
"he_IL": "",
|
||||||
|
"it_IT": "",
|
||||||
|
"ja_JP": "",
|
||||||
|
"ko_KR": "",
|
||||||
|
"no_NO": "",
|
||||||
|
"pl_PL": "",
|
||||||
|
"pt_BR": "",
|
||||||
|
"ru_RU": "",
|
||||||
|
"sv_SE": "",
|
||||||
|
"th_TH": "",
|
||||||
|
"tr_TR": "",
|
||||||
|
"uk_UA": "",
|
||||||
|
"zh_CN": "",
|
||||||
|
"zh_TW": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ID": "SettingsTabHotkeysOnlyWhilePressed",
|
||||||
|
"Translations": {
|
||||||
|
"ar_SA": "",
|
||||||
|
"de_DE": "",
|
||||||
|
"el_GR": "",
|
||||||
|
"en_US": "Only while pressed",
|
||||||
|
"es_ES": "",
|
||||||
|
"fr_FR": "Seulement quand le raccourci est maintenu",
|
||||||
|
"he_IL": "",
|
||||||
|
"it_IT": "",
|
||||||
|
"ja_JP": "",
|
||||||
|
"ko_KR": "",
|
||||||
|
"no_NO": "",
|
||||||
|
"pl_PL": "",
|
||||||
|
"pt_BR": "",
|
||||||
|
"ru_RU": "",
|
||||||
|
"sv_SE": "",
|
||||||
|
"th_TH": "",
|
||||||
|
"tr_TR": "",
|
||||||
|
"uk_UA": "",
|
||||||
|
"zh_CN": "",
|
||||||
|
"zh_TW": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"ID": "CompatibilityListLastUpdated",
|
"ID": "CompatibilityListLastUpdated",
|
||||||
"Translations": {
|
"Translations": {
|
||||||
|
|||||||
@@ -14,5 +14,6 @@ namespace Ryujinx.Ava.Common
|
|||||||
VolumeDown,
|
VolumeDown,
|
||||||
CustomVSyncIntervalIncrement,
|
CustomVSyncIntervalIncrement,
|
||||||
CustomVSyncIntervalDecrement,
|
CustomVSyncIntervalDecrement,
|
||||||
|
TurboMode,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,13 @@ namespace Ryujinx.Ava.Common.Locale
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetUnformatted(LocaleKeys key) => Instance.Get(key);
|
||||||
|
|
||||||
|
public string Get(LocaleKeys key) =>
|
||||||
|
_localeStrings.TryGetValue(key, out string value)
|
||||||
|
? value
|
||||||
|
: key.ToString();
|
||||||
|
|
||||||
public string this[LocaleKeys key]
|
public string this[LocaleKeys key]
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ using Ryujinx.Common.Configuration.Hid.Controller.Motion;
|
|||||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
|
using Ryujinx.Cpu;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
using Ryujinx.Graphics.OpenGL;
|
using Ryujinx.Graphics.OpenGL;
|
||||||
using Ryujinx.Graphics.Vulkan;
|
using Ryujinx.Graphics.Vulkan;
|
||||||
@@ -321,6 +322,7 @@ namespace Ryujinx.Headless
|
|||||||
options.VSyncMode,
|
options.VSyncMode,
|
||||||
!options.DisableDockedMode,
|
!options.DisableDockedMode,
|
||||||
!options.DisablePTC,
|
!options.DisablePTC,
|
||||||
|
ITickSource.RealityTickScalar,
|
||||||
options.EnableInternetAccess,
|
options.EnableInternetAccess,
|
||||||
!options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
|
!options.DisableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
|
||||||
options.FsGlobalAccessLogMode,
|
options.FsGlobalAccessLogMode,
|
||||||
|
|||||||
@@ -142,8 +142,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")))
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Avalonia.Controls.ApplicationLifetimes;
|
|||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using DiscordRPC;
|
using DiscordRPC;
|
||||||
|
using Gommon;
|
||||||
using LibHac.Common;
|
using LibHac.Common;
|
||||||
using LibHac.Ns;
|
using LibHac.Ns;
|
||||||
using Ryujinx.Audio.Backends.Dummy;
|
using Ryujinx.Audio.Backends.Dummy;
|
||||||
@@ -1115,11 +1116,23 @@ namespace Ryujinx.Ava.Systems
|
|||||||
LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%",
|
LocaleManager.Instance[LocaleKeys.VolumeShort] + $": {(int)(Device.GetVolume() * 100)}%",
|
||||||
dockedMode,
|
dockedMode,
|
||||||
ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(),
|
ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(),
|
||||||
Device.Statistics.FormatGameFrameRate(),
|
FormatGameFrameRate(),
|
||||||
Device.Statistics.FormatFifoPercent(),
|
Device.Statistics.FormatFifoPercent(),
|
||||||
_displayCount));
|
_displayCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string FormatGameFrameRate()
|
||||||
|
{
|
||||||
|
string frameRate = Device.Statistics.GetGameFrameRate().ToString("00.00");
|
||||||
|
string frameTime = Device.Statistics.GetGameFrameTime().ToString("00.00");
|
||||||
|
|
||||||
|
return Device.TurboMode
|
||||||
|
? LocaleManager.GetUnformatted(LocaleKeys.FpsTurboStatusBarText)
|
||||||
|
.Format(frameRate, frameTime, Device.TickScalar)
|
||||||
|
: LocaleManager.GetUnformatted(LocaleKeys.FpsStatusBarText)
|
||||||
|
.Format(frameRate, frameTime);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task ShowExitPrompt()
|
public async Task ShowExitPrompt()
|
||||||
{
|
{
|
||||||
bool shouldExit = !ConfigurationState.Instance.ShowConfirmExit;
|
bool shouldExit = !ConfigurationState.Instance.ShowConfirmExit;
|
||||||
@@ -1215,6 +1228,12 @@ namespace Ryujinx.Ava.Systems
|
|||||||
|
|
||||||
if (currentHotkeyState != _prevHotkeyState)
|
if (currentHotkeyState != _prevHotkeyState)
|
||||||
{
|
{
|
||||||
|
if (ConfigurationState.Instance.Hid.Hotkeys.Value.TurboModeWhileHeld &&
|
||||||
|
_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.TurboMode) != Device.TurboMode)
|
||||||
|
{
|
||||||
|
Device.ToggleTurbo();
|
||||||
|
}
|
||||||
|
|
||||||
switch (currentHotkeyState)
|
switch (currentHotkeyState)
|
||||||
{
|
{
|
||||||
case KeyboardHotkeyState.ToggleVSyncMode:
|
case KeyboardHotkeyState.ToggleVSyncMode:
|
||||||
@@ -1226,6 +1245,12 @@ namespace Ryujinx.Ava.Systems
|
|||||||
case KeyboardHotkeyState.CustomVSyncIntervalIncrement:
|
case KeyboardHotkeyState.CustomVSyncIntervalIncrement:
|
||||||
_viewModel.CustomVSyncInterval = Device.IncrementCustomVSyncInterval();
|
_viewModel.CustomVSyncInterval = Device.IncrementCustomVSyncInterval();
|
||||||
break;
|
break;
|
||||||
|
case KeyboardHotkeyState.TurboMode:
|
||||||
|
if (!ConfigurationState.Instance.Hid.Hotkeys.Value.TurboModeWhileHeld)
|
||||||
|
{
|
||||||
|
Device.ToggleTurbo();
|
||||||
|
}
|
||||||
|
break;
|
||||||
case KeyboardHotkeyState.Screenshot:
|
case KeyboardHotkeyState.Screenshot:
|
||||||
ScreenshotRequested = true;
|
ScreenshotRequested = true;
|
||||||
break;
|
break;
|
||||||
@@ -1355,6 +1380,10 @@ namespace Ryujinx.Ava.Systems
|
|||||||
{
|
{
|
||||||
state = KeyboardHotkeyState.CustomVSyncIntervalDecrement;
|
state = KeyboardHotkeyState.CustomVSyncIntervalDecrement;
|
||||||
}
|
}
|
||||||
|
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.TurboMode))
|
||||||
|
{
|
||||||
|
state = KeyboardHotkeyState.TurboMode;
|
||||||
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,6 +259,11 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool EnableLowPowerPtc { get; set; }
|
public bool EnableLowPowerPtc { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clock tick scalar, in percent points (100 = 1.0).
|
||||||
|
/// </summary>
|
||||||
|
public long TickScalar { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enables or disables guest Internet access
|
/// Enables or disables guest Internet access
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
System.EnableDockedMode.Value = cff.DockedMode;
|
System.EnableDockedMode.Value = cff.DockedMode;
|
||||||
System.EnablePtc.Value = cff.EnablePtc;
|
System.EnablePtc.Value = cff.EnablePtc;
|
||||||
System.EnableLowPowerPtc.Value = cff.EnableLowPowerPtc;
|
System.EnableLowPowerPtc.Value = cff.EnableLowPowerPtc;
|
||||||
|
System.TickScalar.Value = cff.TickScalar;
|
||||||
System.EnableInternetAccess.Value = cff.EnableInternetAccess;
|
System.EnableInternetAccess.Value = cff.EnableInternetAccess;
|
||||||
System.EnableFsIntegrityChecks.Value = cff.EnableFsIntegrityChecks;
|
System.EnableFsIntegrityChecks.Value = cff.EnableFsIntegrityChecks;
|
||||||
System.FsGlobalAccessLogMode.Value = cff.FsGlobalAccessLogMode;
|
System.FsGlobalAccessLogMode.Value = cff.FsGlobalAccessLogMode;
|
||||||
@@ -438,9 +439,27 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
(64, static cff => cff.LoggingEnableAvalonia = false),
|
(64, static cff => cff.LoggingEnableAvalonia = false),
|
||||||
(65, static cff => cff.UpdateCheckerType = cff.CheckUpdatesOnStart ? UpdaterType.PromptAtStartup : UpdaterType.Off),
|
(65, static cff => cff.UpdateCheckerType = cff.CheckUpdatesOnStart ? UpdaterType.PromptAtStartup : UpdaterType.Off),
|
||||||
(66, static cff => cff.DisableInputWhenOutOfFocus = false),
|
(66, static cff => cff.DisableInputWhenOutOfFocus = false),
|
||||||
(67, static cff => cff.FocusLostActionType = cff.DisableInputWhenOutOfFocus ? FocusLostType.BlockInput : FocusLostType.DoNothing)
|
(67, static cff => cff.FocusLostActionType = cff.DisableInputWhenOutOfFocus ? FocusLostType.BlockInput : FocusLostType.DoNothing),
|
||||||
// 68 was the version that added per-game configs; the file structure did not change
|
(68, static cff =>
|
||||||
// the version was increased so external tools could know that your Ryujinx version has per-game config capabilities.
|
{
|
||||||
|
cff.TickScalar = 200;
|
||||||
|
cff.Hotkeys = new KeyboardHotkeys
|
||||||
|
{
|
||||||
|
ToggleVSyncMode = cff.Hotkeys.ToggleVSyncMode,
|
||||||
|
Screenshot = cff.Hotkeys.Screenshot,
|
||||||
|
ShowUI = cff.Hotkeys.ShowUI,
|
||||||
|
Pause = cff.Hotkeys.Pause,
|
||||||
|
ToggleMute = cff.Hotkeys.ToggleMute,
|
||||||
|
ResScaleUp = cff.Hotkeys.ResScaleUp,
|
||||||
|
ResScaleDown = cff.Hotkeys.ResScaleDown,
|
||||||
|
VolumeUp = cff.Hotkeys.VolumeUp,
|
||||||
|
VolumeDown = cff.Hotkeys.VolumeDown,
|
||||||
|
CustomVSyncIntervalIncrement = cff.Hotkeys.CustomVSyncIntervalIncrement,
|
||||||
|
CustomVSyncIntervalDecrement = cff.Hotkeys.CustomVSyncIntervalDecrement,
|
||||||
|
TurboMode = Key.Unbound,
|
||||||
|
TurboModeWhileHeld = false
|
||||||
|
};
|
||||||
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -336,6 +336,11 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ReactiveObject<bool> EnablePtc { get; private set; }
|
public ReactiveObject<bool> EnablePtc { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clock tick scalar, in percent points (100 = 1.0).
|
||||||
|
/// </summary>
|
||||||
|
public ReactiveObject<long> TickScalar { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enables or disables low-power persistent profiled translation cache loading
|
/// Enables or disables low-power persistent profiled translation cache loading
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -415,6 +420,15 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
EnableLowPowerPtc.LogChangesToValue(nameof(EnableLowPowerPtc));
|
EnableLowPowerPtc.LogChangesToValue(nameof(EnableLowPowerPtc));
|
||||||
EnableLowPowerPtc.Event += (_, evnt)
|
EnableLowPowerPtc.Event += (_, evnt)
|
||||||
=> Optimizations.LowPower = evnt.NewValue;
|
=> Optimizations.LowPower = evnt.NewValue;
|
||||||
|
TickScalar = new ReactiveObject<long>();
|
||||||
|
TickScalar.LogChangesToValue(nameof(TickScalar));
|
||||||
|
TickScalar.Event += (_, evnt) =>
|
||||||
|
{
|
||||||
|
if (Switch.Shared is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Switch.Shared.Configuration.TickScalar = evnt.NewValue;
|
||||||
|
};
|
||||||
EnableInternetAccess = new ReactiveObject<bool>();
|
EnableInternetAccess = new ReactiveObject<bool>();
|
||||||
EnableInternetAccess.LogChangesToValue(nameof(EnableInternetAccess));
|
EnableInternetAccess.LogChangesToValue(nameof(EnableInternetAccess));
|
||||||
EnableFsIntegrityChecks = new ReactiveObject<bool>();
|
EnableFsIntegrityChecks = new ReactiveObject<bool>();
|
||||||
@@ -842,6 +856,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
Graphics.VSyncMode,
|
Graphics.VSyncMode,
|
||||||
System.EnableDockedMode,
|
System.EnableDockedMode,
|
||||||
System.EnablePtc,
|
System.EnablePtc,
|
||||||
|
System.TickScalar,
|
||||||
System.EnableInternetAccess,
|
System.EnableInternetAccess,
|
||||||
System.EnableFsIntegrityChecks
|
System.EnableFsIntegrityChecks
|
||||||
? IntegrityCheckLevel.ErrorOnInvalid
|
? IntegrityCheckLevel.ErrorOnInvalid
|
||||||
@@ -860,8 +875,8 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
Multiplayer.Mode,
|
Multiplayer.Mode,
|
||||||
Multiplayer.DisableP2p,
|
Multiplayer.DisableP2p,
|
||||||
Multiplayer.LdnPassphrase,
|
Multiplayer.LdnPassphrase,
|
||||||
Instance.Multiplayer.GetLdnServer(),
|
Multiplayer.GetLdnServer(),
|
||||||
Instance.Graphics.CustomVSyncInterval,
|
Graphics.CustomVSyncInterval,
|
||||||
Instance.Hacks.ShowDirtyHacks ? Instance.Hacks.EnabledHacks : null);
|
Hacks.ShowDirtyHacks ? Hacks.EnabledHacks : null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
EnableColorSpacePassthrough = Graphics.EnableColorSpacePassthrough,
|
EnableColorSpacePassthrough = Graphics.EnableColorSpacePassthrough,
|
||||||
EnablePtc = System.EnablePtc,
|
EnablePtc = System.EnablePtc,
|
||||||
EnableLowPowerPtc = System.EnableLowPowerPtc,
|
EnableLowPowerPtc = System.EnableLowPowerPtc,
|
||||||
|
TickScalar = System.TickScalar,
|
||||||
EnableInternetAccess = System.EnableInternetAccess,
|
EnableInternetAccess = System.EnableInternetAccess,
|
||||||
EnableFsIntegrityChecks = System.EnableFsIntegrityChecks,
|
EnableFsIntegrityChecks = System.EnableFsIntegrityChecks,
|
||||||
FsGlobalAccessLogMode = System.FsGlobalAccessLogMode,
|
FsGlobalAccessLogMode = System.FsGlobalAccessLogMode,
|
||||||
@@ -260,6 +261,10 @@ namespace Ryujinx.Ava.Systems.Configuration
|
|||||||
ResScaleDown = Key.Unbound,
|
ResScaleDown = Key.Unbound,
|
||||||
VolumeUp = Key.Unbound,
|
VolumeUp = Key.Unbound,
|
||||||
VolumeDown = Key.Unbound,
|
VolumeDown = Key.Unbound,
|
||||||
|
CustomVSyncIntervalIncrement = Key.Unbound,
|
||||||
|
CustomVSyncIntervalDecrement = Key.Unbound,
|
||||||
|
TurboMode = Key.Unbound,
|
||||||
|
TurboModeWhileHeld = false
|
||||||
};
|
};
|
||||||
Hid.RainbowSpeed.Value = 1f;
|
Hid.RainbowSpeed.Value = 1f;
|
||||||
Hid.InputConfig.Value =
|
Hid.InputConfig.Value =
|
||||||
|
|||||||
@@ -28,6 +28,10 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||||||
|
|
||||||
[ObservableProperty] private Key _customVSyncIntervalDecrement;
|
[ObservableProperty] private Key _customVSyncIntervalDecrement;
|
||||||
|
|
||||||
|
[ObservableProperty] private Key _turboMode;
|
||||||
|
|
||||||
|
[ObservableProperty] private bool _turboModeWhileHeld;
|
||||||
|
|
||||||
public HotkeyConfig(KeyboardHotkeys config)
|
public HotkeyConfig(KeyboardHotkeys config)
|
||||||
{
|
{
|
||||||
if (config == null)
|
if (config == null)
|
||||||
@@ -44,6 +48,8 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||||||
VolumeDown = config.VolumeDown;
|
VolumeDown = config.VolumeDown;
|
||||||
CustomVSyncIntervalIncrement = config.CustomVSyncIntervalIncrement;
|
CustomVSyncIntervalIncrement = config.CustomVSyncIntervalIncrement;
|
||||||
CustomVSyncIntervalDecrement = config.CustomVSyncIntervalDecrement;
|
CustomVSyncIntervalDecrement = config.CustomVSyncIntervalDecrement;
|
||||||
|
TurboMode = config.TurboMode;
|
||||||
|
TurboModeWhileHeld = config.TurboModeWhileHeld;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeyboardHotkeys GetConfig() =>
|
public KeyboardHotkeys GetConfig() =>
|
||||||
@@ -60,6 +66,8 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||||||
VolumeDown = VolumeDown,
|
VolumeDown = VolumeDown,
|
||||||
CustomVSyncIntervalIncrement = CustomVSyncIntervalIncrement,
|
CustomVSyncIntervalIncrement = CustomVSyncIntervalIncrement,
|
||||||
CustomVSyncIntervalDecrement = CustomVSyncIntervalDecrement,
|
CustomVSyncIntervalDecrement = CustomVSyncIntervalDecrement,
|
||||||
|
TurboMode = TurboMode,
|
||||||
|
TurboModeWhileHeld = TurboModeWhileHeld
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private bool _enableCustomVSyncInterval;
|
private bool _enableCustomVSyncInterval;
|
||||||
private int _customVSyncIntervalPercentageProxy;
|
private int _customVSyncIntervalPercentageProxy;
|
||||||
private VSyncMode _vSyncMode;
|
private VSyncMode _vSyncMode;
|
||||||
|
private long _turboModeMultiplier;
|
||||||
|
|
||||||
public event Action CloseWindow;
|
public event Action CloseWindow;
|
||||||
public event Action SaveSettingsEvent;
|
public event Action SaveSettingsEvent;
|
||||||
@@ -206,6 +207,25 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
public bool EnablePptc { get; set; }
|
public bool EnablePptc { get; set; }
|
||||||
public bool EnableLowPowerPptc { get; set; }
|
public bool EnableLowPowerPptc { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public long TurboMultiplier
|
||||||
|
{
|
||||||
|
get => _turboModeMultiplier;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_turboModeMultiplier != value)
|
||||||
|
{
|
||||||
|
_turboModeMultiplier = value;
|
||||||
|
|
||||||
|
OnPropertyChanged();
|
||||||
|
OnPropertyChanged((nameof(TurboMultiplierPercentageText)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string TurboMultiplierPercentageText => $"{TurboMultiplier}%";
|
||||||
|
|
||||||
public bool EnableInternetAccess { get; set; }
|
public bool EnableInternetAccess { get; set; }
|
||||||
public bool EnableFsIntegrityChecks { get; set; }
|
public bool EnableFsIntegrityChecks { get; set; }
|
||||||
public bool IgnoreMissingServices { get; set; }
|
public bool IgnoreMissingServices { get; set; }
|
||||||
@@ -592,6 +612,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
EnableLowPowerPptc = config.System.EnableLowPowerPtc;
|
EnableLowPowerPptc = config.System.EnableLowPowerPtc;
|
||||||
MemoryMode = (int)config.System.MemoryManagerMode.Value;
|
MemoryMode = (int)config.System.MemoryManagerMode.Value;
|
||||||
UseHypervisor = config.System.UseHypervisor;
|
UseHypervisor = config.System.UseHypervisor;
|
||||||
|
TurboMultiplier = config.System.TickScalar;
|
||||||
|
|
||||||
// Graphics
|
// Graphics
|
||||||
GraphicsBackendIndex = (int)config.Graphics.GraphicsBackend.Value;
|
GraphicsBackendIndex = (int)config.Graphics.GraphicsBackend.Value;
|
||||||
@@ -694,6 +715,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
config.System.EnableLowPowerPtc.Value = EnableLowPowerPptc;
|
config.System.EnableLowPowerPtc.Value = EnableLowPowerPptc;
|
||||||
config.System.MemoryManagerMode.Value = (MemoryManagerMode)MemoryMode;
|
config.System.MemoryManagerMode.Value = (MemoryManagerMode)MemoryMode;
|
||||||
config.System.UseHypervisor.Value = UseHypervisor;
|
config.System.UseHypervisor.Value = UseHypervisor;
|
||||||
|
config.System.TickScalar.Value = TurboMultiplier;
|
||||||
|
|
||||||
// Graphics
|
// Graphics
|
||||||
config.Graphics.VSyncMode.Value = VSyncMode;
|
config.Graphics.VSyncMode.Value = VSyncMode;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||||
xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common"
|
xmlns:helper="clr-namespace:Ryujinx.Common.Helper;assembly=Ryujinx.Common"
|
||||||
|
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
x:DataType="viewModels:SettingsViewModel">
|
x:DataType="viewModels:SettingsViewModel">
|
||||||
<Design.DataContext>
|
<Design.DataContext>
|
||||||
@@ -76,6 +77,57 @@
|
|||||||
ToolTip.Tip="{ext:Locale UseHypervisorTooltip}" />
|
ToolTip.Tip="{ext:Locale UseHypervisorTooltip}" />
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
<Separator Height="1" />
|
||||||
|
<StackPanel
|
||||||
|
Orientation="Vertical"
|
||||||
|
Spacing="5">
|
||||||
|
<TextBlock
|
||||||
|
Classes="h1"
|
||||||
|
Text="{ext:Locale SettingsTabSystemHacks}" />
|
||||||
|
<TextBlock
|
||||||
|
Foreground="{DynamicResource SecondaryTextColor}"
|
||||||
|
TextDecorations="Underline"
|
||||||
|
Text="{ext:Locale SettingsTabSystemHacksNote}" />
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel
|
||||||
|
Margin="10,0,0,0"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Orientation="Vertical">
|
||||||
|
<StackPanel Margin="0,0,0,10"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<TextBlock
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Background="Transparent"
|
||||||
|
Text="{ext:Locale SettingsTabSystemTurboMultiplier}"
|
||||||
|
ToolTip.Tip="{ext:Locale SettingsTabSystemTurboMultiplierToolTip}"
|
||||||
|
Width="250" />
|
||||||
|
<ui:NumberBox ToolTip.Tip="{ext:Locale SettingsTabSystemTurboMultiplierValueToolTip}"
|
||||||
|
Value="{Binding TurboMultiplier}"
|
||||||
|
Width="165"
|
||||||
|
SmallChange="1.0"
|
||||||
|
LargeChange="10"
|
||||||
|
SimpleNumberFormat="F0"
|
||||||
|
SpinButtonPlacementMode="Hidden"
|
||||||
|
Minimum="50"
|
||||||
|
Maximum="300" />
|
||||||
|
<Slider Value="{Binding TurboMultiplier}"
|
||||||
|
ToolTip.Tip="{ext:Locale SettingsTabSystemTurboMultiplierValueToolTip}"
|
||||||
|
MinWidth="175"
|
||||||
|
Margin="10,-3,0,0"
|
||||||
|
Height="32"
|
||||||
|
Padding="0,-5"
|
||||||
|
TickFrequency="1"
|
||||||
|
IsSnapToTickEnabled="True"
|
||||||
|
LargeChange="10"
|
||||||
|
SmallChange="1"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Minimum="50"
|
||||||
|
Maximum="500" />
|
||||||
|
<TextBlock Margin="5,0"
|
||||||
|
Width="40"
|
||||||
|
Text="{Binding TurboMultiplierPercentageText}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<Setter Property="Margin" Value="10, 0, 0, 0" />
|
<Setter Property="Margin" Value="10, 0, 0, 0" />
|
||||||
<Setter Property="Orientation" Value="Horizontal" />
|
<Setter Property="Orientation" Value="Horizontal" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style Selector="StackPanel > StackPanel > TextBlock">
|
<Style Selector="StackPanel > StackPanel > TextBlock.settingHeader">
|
||||||
<Setter Property="VerticalAlignment" Value="Center" />
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
<Setter Property="Width" Value="230" />
|
<Setter Property="Width" Value="230" />
|
||||||
</Style>
|
</Style>
|
||||||
@@ -47,71 +47,79 @@
|
|||||||
Classes="h1"
|
Classes="h1"
|
||||||
Text="{ext:Locale SettingsTabHotkeysHotkeys}" />
|
Text="{ext:Locale SettingsTabHotkeysHotkeys}" />
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysToggleVSyncModeHotkey}" />
|
<TextBlock Text="{ext:Locale SettingsTabHotkeysToggleVSyncModeHotkey}" Classes="settingHeader" />
|
||||||
<ToggleButton Name="ToggleVSyncMode">
|
<ToggleButton Name="ToggleVSyncMode">
|
||||||
<TextBlock Text="{Binding KeyboardHotkey.ToggleVSyncMode, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
<TextBlock Text="{Binding KeyboardHotkey.ToggleVSyncMode, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysScreenshotHotkey}" />
|
<TextBlock Text="{ext:Locale SettingsTabHotkeysScreenshotHotkey}" Classes="settingHeader" />
|
||||||
<ToggleButton Name="Screenshot">
|
<ToggleButton Name="Screenshot">
|
||||||
<TextBlock Text="{Binding KeyboardHotkey.Screenshot, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
<TextBlock Text="{Binding KeyboardHotkey.Screenshot, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysShowUiHotkey}" />
|
<TextBlock Text="{ext:Locale SettingsTabHotkeysShowUiHotkey}" Classes="settingHeader" />
|
||||||
<ToggleButton Name="ShowUI">
|
<ToggleButton Name="ShowUI">
|
||||||
<TextBlock Text="{Binding KeyboardHotkey.ShowUI, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
<TextBlock Text="{Binding KeyboardHotkey.ShowUI, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysPauseHotkey}" />
|
<TextBlock Text="{ext:Locale SettingsTabHotkeysPauseHotkey}" Classes="settingHeader" />
|
||||||
<ToggleButton Name="Pause">
|
<ToggleButton Name="Pause">
|
||||||
<TextBlock Text="{Binding KeyboardHotkey.Pause, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
<TextBlock Text="{Binding KeyboardHotkey.Pause, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysToggleMuteHotkey}" />
|
<TextBlock Text="{ext:Locale SettingsTabHotkeysToggleMuteHotkey}" Classes="settingHeader" />
|
||||||
<ToggleButton Name="ToggleMute">
|
<ToggleButton Name="ToggleMute">
|
||||||
<TextBlock Text="{Binding KeyboardHotkey.ToggleMute, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
<TextBlock Text="{Binding KeyboardHotkey.ToggleMute, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysResScaleUpHotkey}" />
|
<TextBlock Text="{ext:Locale SettingsTabHotkeysResScaleUpHotkey}" Classes="settingHeader" />
|
||||||
<ToggleButton Name="ResScaleUp">
|
<ToggleButton Name="ResScaleUp">
|
||||||
<TextBlock Text="{Binding KeyboardHotkey.ResScaleUp, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
<TextBlock Text="{Binding KeyboardHotkey.ResScaleUp, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysResScaleDownHotkey}" />
|
<TextBlock Text="{ext:Locale SettingsTabHotkeysResScaleDownHotkey}" Classes="settingHeader" />
|
||||||
<ToggleButton Name="ResScaleDown">
|
<ToggleButton Name="ResScaleDown">
|
||||||
<TextBlock Text="{Binding KeyboardHotkey.ResScaleDown, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
<TextBlock Text="{Binding KeyboardHotkey.ResScaleDown, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysVolumeUpHotkey}" />
|
<TextBlock Text="{ext:Locale SettingsTabHotkeysVolumeUpHotkey}" Classes="settingHeader" />
|
||||||
<ToggleButton Name="VolumeUp">
|
<ToggleButton Name="VolumeUp">
|
||||||
<TextBlock Text="{Binding KeyboardHotkey.VolumeUp, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
<TextBlock Text="{Binding KeyboardHotkey.VolumeUp, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysVolumeDownHotkey}" />
|
<TextBlock Text="{ext:Locale SettingsTabHotkeysVolumeDownHotkey}" Classes="settingHeader" />
|
||||||
<ToggleButton Name="VolumeDown">
|
<ToggleButton Name="VolumeDown">
|
||||||
<TextBlock Text="{Binding KeyboardHotkey.VolumeDown, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
<TextBlock Text="{Binding KeyboardHotkey.VolumeDown, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysIncrementCustomVSyncIntervalHotkey}" />
|
<TextBlock Text="{ext:Locale SettingsTabHotkeysIncrementCustomVSyncIntervalHotkey}" Classes="settingHeader" />
|
||||||
<ToggleButton Name="CustomVSyncIntervalIncrement">
|
<ToggleButton Name="CustomVSyncIntervalIncrement">
|
||||||
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalIncrement, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalIncrement, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysDecrementCustomVSyncIntervalHotkey}" />
|
<TextBlock Text="{ext:Locale SettingsTabHotkeysDecrementCustomVSyncIntervalHotkey}" Classes="settingHeader" />
|
||||||
<ToggleButton Name="CustomVSyncIntervalDecrement">
|
<ToggleButton Name="CustomVSyncIntervalDecrement">
|
||||||
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalDecrement, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalDecrement, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Text="{ext:Locale SettingsTabHotkeysTurboMode}" Classes="settingHeader" ToolTip.Tip="{ext:Locale SettingsTabHotkeysTurboModeToolTip}" Background="Transparent" />
|
||||||
|
<ToggleButton Name="TurboMode">
|
||||||
|
<TextBlock Text="{Binding KeyboardHotkey.TurboMode, Converter={x:Static helpers:KeyValueConverter.Instance}}" />
|
||||||
|
</ToggleButton>
|
||||||
|
<TextBlock Text="{ext:Locale SettingsTabHotkeysOnlyWhilePressed}" Margin="10,0" />
|
||||||
|
<CheckBox IsChecked="{Binding KeyboardHotkey.TurboModeWhileHeld}" />
|
||||||
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
|
|||||||
@@ -121,6 +121,9 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
|||||||
ViewModel.KeyboardHotkey.CustomVSyncIntervalDecrement =
|
ViewModel.KeyboardHotkey.CustomVSyncIntervalDecrement =
|
||||||
buttonValue.AsHidType<Key>();
|
buttonValue.AsHidType<Key>();
|
||||||
break;
|
break;
|
||||||
|
case "TurboMode":
|
||||||
|
ViewModel.KeyboardHotkey.TurboMode = buttonValue.AsHidType<Key>();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user