Compare commits

..

4 Commits

Author SHA1 Message Date
riperiperi
a64fee29dc Vulkan: add situational "Fast Flush" mode (#4667)
* Flush in the middle of long command buffers.

* Vulkan: add situational "Fast Flush" mode

The AutoFlushCounter class was added to periodically flush Vulkan command buffers throughout a frame, which reduces latency to the GPU as commands are submitted and processed much sooner. This was done by allowing command buffers to flush when framebuffer attachments changed.

However, some games have incredibly long render passes with a large number of draws, and really aggressive data access that forces GPU sync.

The Vulkan backend could potentially end up building a single command buffer for 4-5ms if a pass has enough draws, such as in BOTW. In the scenario where sync is waited on immediately after submission, this would have to wait for the completion of a much longer command buffer than usual.

The solution is to force command buffer submission periodically in a "fast flush" mode. This will end up splitting render passes, but it will only enable if sync is aggressive enough.

This should improve performance in GPU limited scenarios, or in games that aggressively wait on synchronization. In some games, it may only kick in when res scaling. It won't trigger in games like SMO where sync is not an issue.

Improves performance in Pokemon Scarlet/Violet (res scaled) and BOTW (in general).

* Add conversions in milliseconds next to flush timers.
2023-04-11 09:23:41 +02:00
riperiperi
9ef94c8292 ARMeilleure: Move TPIDR_EL0 and TPIDRRO_EL0 to NativeContext (#4661)
* ARMeilleure: Move TPIDR_EL0 and TPIDRRO_EL0 to NativeContext

Some games access these system registers several tens of thousands of times in a second from many different threads. While this isn't really crippling, it is a lot of wasted time spent in a reverse pinvoke transition.

Example games are Pokemon Scarlet/Violet and BOTW. These games have a lot of different potential bottlenecks so it's unlikely you will see a consistent improvement, but it definitely disappears from the cpu profile.

* Remove unreachable code.

* Add ulong conversion for offsets

* Nit
2023-04-11 08:55:04 +02:00
riperiperi
915d6d044c OpenGL: Fix OBS/Overlays again by binding FB before present (#4668)
This seems to have been removed by the Post-Processing PR, but it is required for the display in OBS to be the right way up and properly scaled.

I've tested this with AA and FSR on MK8D and it seems to behave properly. Testing is welcome.
2023-04-11 08:32:31 +02:00
MutantAura
a4780ab33b Force activate parent window before dialog is shown (#4663) 2023-04-11 00:04:31 +02:00
13 changed files with 200 additions and 68 deletions

View File

@@ -33,8 +33,8 @@ namespace ARMeilleure.Instructions
case 0b11_011_0100_0010_000: EmitGetNzcv(context); return; case 0b11_011_0100_0010_000: EmitGetNzcv(context); return;
case 0b11_011_0100_0100_000: EmitGetFpcr(context); return; case 0b11_011_0100_0100_000: EmitGetFpcr(context); return;
case 0b11_011_0100_0100_001: EmitGetFpsr(context); return; case 0b11_011_0100_0100_001: EmitGetFpsr(context); return;
case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)); break; case 0b11_011_1101_0000_010: EmitGetTpidrEl0(context); return;
case 0b11_011_1101_0000_011: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)); break; case 0b11_011_1101_0000_011: EmitGetTpidrroEl0(context); return;
case 0b11_011_1110_0000_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)); break; case 0b11_011_1110_0000_000: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntfrqEl0)); break;
case 0b11_011_1110_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break; case 0b11_011_1110_0000_001: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntpctEl0)); break;
case 0b11_011_1110_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)); break; case 0b11_011_1110_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetCntvctEl0)); break;
@@ -49,19 +49,15 @@ namespace ARMeilleure.Instructions
{ {
OpCodeSystem op = (OpCodeSystem)context.CurrOp; OpCodeSystem op = (OpCodeSystem)context.CurrOp;
MethodInfo info;
switch (GetPackedId(op)) switch (GetPackedId(op))
{ {
case 0b11_011_0100_0010_000: EmitSetNzcv(context); return; case 0b11_011_0100_0010_000: EmitSetNzcv(context); return;
case 0b11_011_0100_0100_000: EmitSetFpcr(context); return; case 0b11_011_0100_0100_000: EmitSetFpcr(context); return;
case 0b11_011_0100_0100_001: EmitSetFpsr(context); return; case 0b11_011_0100_0100_001: EmitSetFpsr(context); return;
case 0b11_011_1101_0000_010: info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)); break; case 0b11_011_1101_0000_010: EmitSetTpidrEl0(context); return;
default: throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); default: throw new NotImplementedException($"Unknown MSR 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
} }
context.Call(info, GetIntOrZR(context, op.Rt));
} }
public static void Nop(ArmEmitterContext context) public static void Nop(ArmEmitterContext context)
@@ -165,6 +161,28 @@ namespace ARMeilleure.Instructions
SetIntOrZR(context, op.Rt, fpsr); SetIntOrZR(context, op.Rt, fpsr);
} }
private static void EmitGetTpidrEl0(ArmEmitterContext context)
{
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
Operand result = context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())));
SetIntOrZR(context, op.Rt, result);
}
private static void EmitGetTpidrroEl0(ArmEmitterContext context)
{
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
Operand result = context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrroEl0Offset())));
SetIntOrZR(context, op.Rt, result);
}
private static void EmitSetNzcv(ArmEmitterContext context) private static void EmitSetNzcv(ArmEmitterContext context)
{ {
OpCodeSystem op = (OpCodeSystem)context.CurrOp; OpCodeSystem op = (OpCodeSystem)context.CurrOp;
@@ -215,5 +233,16 @@ namespace ARMeilleure.Instructions
context.UpdateArmFpMode(); context.UpdateArmFpMode();
} }
private static void EmitSetTpidrEl0(ArmEmitterContext context)
{
OpCodeSystem op = (OpCodeSystem)context.CurrOp;
Operand value = GetIntOrZR(context, op.Rt);
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
context.Store(context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())), value);
}
} }
} }

View File

@@ -23,8 +23,6 @@ namespace ARMeilleure.Instructions
return; return;
} }
MethodInfo info;
switch (op.CRn) switch (op.CRn)
{ {
case 13: // Process and Thread Info. case 13: // Process and Thread Info.
@@ -36,14 +34,12 @@ namespace ARMeilleure.Instructions
switch (op.Opc2) switch (op.Opc2)
{ {
case 2: case 2:
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032)); break; EmitSetTpidrEl0(context); return;
default: default:
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X})."); throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
} }
break;
case 7: case 7:
switch (op.CRm) // Cache and Memory barrier. switch (op.CRm) // Cache and Memory barrier.
{ {
@@ -64,8 +60,6 @@ namespace ARMeilleure.Instructions
default: default:
throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}."); throw new NotImplementedException($"Unknown MRC 0x{op.RawOpCode:X8} at 0x{op.Address:X16}.");
} }
context.Call(info, GetIntA32(context, op.Rt));
} }
public static void Mrc(ArmEmitterContext context) public static void Mrc(ArmEmitterContext context)
@@ -79,7 +73,7 @@ namespace ARMeilleure.Instructions
return; return;
} }
MethodInfo info; Operand result;
switch (op.CRn) switch (op.CRn)
{ {
@@ -92,10 +86,10 @@ namespace ARMeilleure.Instructions
switch (op.Opc2) switch (op.Opc2)
{ {
case 2: case 2:
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032)); break; result = EmitGetTpidrEl0(context); break;
case 3: case 3:
info = typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32)); break; result = EmitGetTpidrroEl0(context); break;
default: default:
throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X})."); throw new NotImplementedException($"Unknown MRC Opc2 0x{op.Opc2:X} at 0x{op.Address:X} (0x{op.RawOpCode:X}).");
@@ -110,13 +104,13 @@ namespace ARMeilleure.Instructions
if (op.Rt == RegisterAlias.Aarch32Pc) if (op.Rt == RegisterAlias.Aarch32Pc)
{ {
// Special behavior: copy NZCV flags into APSR. // Special behavior: copy NZCV flags into APSR.
EmitSetNzcv(context, context.Call(info)); EmitSetNzcv(context, result);
return; return;
} }
else else
{ {
SetIntA32(context, op.Rt, context.Call(info)); SetIntA32(context, op.Rt, result);
} }
} }
@@ -324,5 +318,34 @@ namespace ARMeilleure.Instructions
context.UpdateArmFpMode(); context.UpdateArmFpMode();
} }
private static Operand EmitGetTpidrEl0(ArmEmitterContext context)
{
OpCode32System op = (OpCode32System)context.CurrOp;
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
return context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())));
}
private static Operand EmitGetTpidrroEl0(ArmEmitterContext context)
{
OpCode32System op = (OpCode32System)context.CurrOp;
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
return context.Load(OperandType.I64, context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrroEl0Offset())));
}
private static void EmitSetTpidrEl0(ArmEmitterContext context)
{
OpCode32System op = (OpCode32System)context.CurrOp;
Operand value = GetIntA32(context, op.Rt);
Operand nativeContext = context.LoadArgument(OperandType.I64, 0);
context.Store(context.Add(nativeContext, Const((ulong)NativeContext.GetTpidrEl0Offset())), context.ZeroExtend32(OperandType.I64, value));
}
} }
} }

View File

@@ -72,26 +72,6 @@ namespace ARMeilleure.Instructions
return (ulong)GetContext().DczidEl0; return (ulong)GetContext().DczidEl0;
} }
public static ulong GetTpidrEl0()
{
return (ulong)GetContext().TpidrEl0;
}
public static uint GetTpidrEl032()
{
return (uint)GetContext().TpidrEl0;
}
public static ulong GetTpidrroEl0()
{
return (ulong)GetContext().TpidrroEl0;
}
public static uint GetTpidr32()
{
return (uint)GetContext().TpidrroEl0;
}
public static ulong GetCntfrqEl0() public static ulong GetCntfrqEl0()
{ {
return GetContext().CntfrqEl0; return GetContext().CntfrqEl0;
@@ -106,16 +86,6 @@ namespace ARMeilleure.Instructions
{ {
return GetContext().CntvctEl0; return GetContext().CntvctEl0;
} }
public static void SetTpidrEl0(ulong value)
{
GetContext().TpidrEl0 = (long)value;
}
public static void SetTpidrEl032(uint value)
{
GetContext().TpidrEl0 = (long)value;
}
#endregion #endregion
#region "Read" #region "Read"

View File

@@ -27,8 +27,17 @@ namespace ARMeilleure.State
// Since EL2 isn't implemented, CNTVOFF_EL2 = 0 // Since EL2 isn't implemented, CNTVOFF_EL2 = 0
public ulong CntvctEl0 => CntpctEl0; public ulong CntvctEl0 => CntpctEl0;
public long TpidrEl0 { get; set; } public long TpidrEl0
public long TpidrroEl0 { get; set; } {
get => _nativeContext.GetTpidrEl0();
set => _nativeContext.SetTpidrEl0(value);
}
public long TpidrroEl0
{
get => _nativeContext.GetTpidrroEl0();
set => _nativeContext.SetTpidrroEl0(value);
}
public uint Pstate public uint Pstate
{ {

View File

@@ -13,6 +13,8 @@ namespace ARMeilleure.State
public fixed ulong V[RegisterConsts.VecRegsCount * 2]; public fixed ulong V[RegisterConsts.VecRegsCount * 2];
public fixed uint Flags[RegisterConsts.FlagsCount]; public fixed uint Flags[RegisterConsts.FlagsCount];
public fixed uint FpFlags[RegisterConsts.FpFlagsCount]; public fixed uint FpFlags[RegisterConsts.FpFlagsCount];
public long TpidrEl0;
public long TpidrroEl0;
public int Counter; public int Counter;
public ulong DispatchAddress; public ulong DispatchAddress;
public ulong ExclusiveAddress; public ulong ExclusiveAddress;
@@ -168,6 +170,12 @@ namespace ARMeilleure.State
} }
} }
public long GetTpidrEl0() => GetStorage().TpidrEl0;
public void SetTpidrEl0(long value) => GetStorage().TpidrEl0 = value;
public long GetTpidrroEl0() => GetStorage().TpidrroEl0;
public void SetTpidrroEl0(long value) => GetStorage().TpidrroEl0 = value;
public int GetCounter() => GetStorage().Counter; public int GetCounter() => GetStorage().Counter;
public void SetCounter(int value) => GetStorage().Counter = value; public void SetCounter(int value) => GetStorage().Counter = value;
@@ -214,6 +222,16 @@ namespace ARMeilleure.State
} }
} }
public static int GetTpidrEl0Offset()
{
return StorageOffset(ref _dummyStorage, ref _dummyStorage.TpidrEl0);
}
public static int GetTpidrroEl0Offset()
{
return StorageOffset(ref _dummyStorage, ref _dummyStorage.TpidrroEl0);
}
public static int GetCounterOffset() public static int GetCounterOffset()
{ {
return StorageOffset(ref _dummyStorage, ref _dummyStorage.Counter); return StorageOffset(ref _dummyStorage, ref _dummyStorage.Counter);

View File

@@ -105,17 +105,11 @@ namespace ARMeilleure.Translation
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetDczidEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetFunctionAddress)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.InvalidateCacheLine)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrroEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidr32))); // A32 only.
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.GetTpidrEl032))); // A32 only.
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadByte)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt16)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt32)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadUInt64)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ReadVector128)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl0)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SetTpidrEl032))); // A32 only.
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SupervisorCall))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SupervisorCall)));
SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess))); SetDelegateInfo(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)));

View File

@@ -30,7 +30,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 4626; //! To be incremented manually for each change to the ARMeilleure project. private const uint InternalVersion = 4661; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0"; private const string ActualDir = "0";
private const string BackupDir = "1"; private const string BackupDir = "1";

View File

@@ -53,6 +53,8 @@ namespace Ryujinx.Ava.UI.Applet
bool opened = false; bool opened = false;
_parent.Activate();
UserResult response = await ContentDialogHelper.ShowDeferredContentDialog(_parent, UserResult response = await ContentDialogHelper.ShowDeferredContentDialog(_parent,
title, title,
message, message,

View File

@@ -226,6 +226,7 @@ namespace Ryujinx.Graphics.OpenGL
// Set clip control, viewport and the framebuffer to the output to placate overlays and OBS capture. // Set clip control, viewport and the framebuffer to the output to placate overlays and OBS capture.
GL.ClipControl(ClipOrigin.LowerLeft, ClipDepthMode.NegativeOneToOne); GL.ClipControl(ClipOrigin.LowerLeft, ClipDepthMode.NegativeOneToOne);
GL.Viewport(0, 0, _width, _height); GL.Viewport(0, 0, _width, _height);
GL.BindFramebuffer(FramebufferTarget.Framebuffer, drawFramebuffer);
swapBuffersCallback(); swapBuffersCallback();

View File

@@ -1,4 +1,5 @@
using System; using Ryujinx.Common.Logging;
using System;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
@@ -7,12 +8,26 @@ namespace Ryujinx.Graphics.Vulkan
internal class AutoFlushCounter internal class AutoFlushCounter
{ {
// How often to flush on framebuffer change. // How often to flush on framebuffer change.
private readonly static long FramebufferFlushTimer = Stopwatch.Frequency / 1000; private readonly static long FramebufferFlushTimer = Stopwatch.Frequency / 1000; // (1ms)
// How often to flush on draw when fast flush mode is enabled.
private readonly static long DrawFlushTimer = Stopwatch.Frequency / 666; // (1.5ms)
// Average wait time that triggers fast flush mode to be entered.
private readonly static long FastFlushEnterThreshold = Stopwatch.Frequency / 666; // (1.5ms)
// Average wait time that triggers fast flush mode to be exited.
private readonly static long FastFlushExitThreshold = Stopwatch.Frequency / 10000; // (0.1ms)
// Number of frames to average waiting times over.
private const int SyncWaitAverageCount = 20;
private const int MinDrawCountForFlush = 10; private const int MinDrawCountForFlush = 10;
private const int MinConsecutiveQueryForFlush = 10; private const int MinConsecutiveQueryForFlush = 10;
private const int InitialQueryCountForFlush = 32; private const int InitialQueryCountForFlush = 32;
private readonly VulkanRenderer _gd;
private long _lastFlush; private long _lastFlush;
private ulong _lastDrawCount; private ulong _lastDrawCount;
private bool _hasPendingQuery; private bool _hasPendingQuery;
@@ -23,6 +38,16 @@ namespace Ryujinx.Graphics.Vulkan
private int _queryCountHistoryIndex; private int _queryCountHistoryIndex;
private int _remainingQueries; private int _remainingQueries;
private long[] _syncWaitHistory = new long[SyncWaitAverageCount];
private int _syncWaitHistoryIndex;
private bool _fastFlushMode;
public AutoFlushCounter(VulkanRenderer gd)
{
_gd = gd;
}
public void RegisterFlush(ulong drawCount) public void RegisterFlush(ulong drawCount)
{ {
_lastFlush = Stopwatch.GetTimestamp(); _lastFlush = Stopwatch.GetTimestamp();
@@ -69,6 +94,32 @@ namespace Ryujinx.Graphics.Vulkan
return _hasPendingQuery; return _hasPendingQuery;
} }
public bool ShouldFlushDraw(ulong drawCount)
{
if (_fastFlushMode)
{
long draws = (long)(drawCount - _lastDrawCount);
if (draws < MinDrawCountForFlush)
{
if (draws == 0)
{
_lastFlush = Stopwatch.GetTimestamp();
}
return false;
}
long flushTimeout = DrawFlushTimer;
long now = Stopwatch.GetTimestamp();
return now > _lastFlush + flushTimeout;
}
return false;
}
public bool ShouldFlushAttachmentChange(ulong drawCount) public bool ShouldFlushAttachmentChange(ulong drawCount)
{ {
_queryCount = 0; _queryCount = 0;
@@ -102,11 +153,27 @@ namespace Ryujinx.Graphics.Vulkan
public void Present() public void Present()
{ {
// Query flush prediction.
_queryCountHistoryIndex = (_queryCountHistoryIndex + 1) % 3; _queryCountHistoryIndex = (_queryCountHistoryIndex + 1) % 3;
_remainingQueries = _queryCountHistory.Max() + 10; _remainingQueries = _queryCountHistory.Max() + 10;
_queryCountHistory[_queryCountHistoryIndex] = 0; _queryCountHistory[_queryCountHistoryIndex] = 0;
// Fast flush mode toggle.
_syncWaitHistory[_syncWaitHistoryIndex] = _gd.SyncManager.GetAndResetWaitTicks();
_syncWaitHistoryIndex = (_syncWaitHistoryIndex + 1) % SyncWaitAverageCount;
long averageWait = (long)_syncWaitHistory.Average();
if (_fastFlushMode ? averageWait < FastFlushExitThreshold : averageWait > FastFlushEnterThreshold)
{
_fastFlushMode = !_fastFlushMode;
Logger.Debug?.PrintMsg(LogClass.Gpu, $"Switched fast flush mode: ({_fastFlushMode})");
}
} }
} }
} }

View File

@@ -86,7 +86,7 @@ namespace Ryujinx.Graphics.Vulkan
Gd = gd; Gd = gd;
Device = device; Device = device;
AutoFlush = new AutoFlushCounter(); AutoFlush = new AutoFlushCounter(gd);
var pipelineCacheCreateInfo = new PipelineCacheCreateInfo() var pipelineCacheCreateInfo = new PipelineCacheCreateInfo()
{ {
@@ -1562,6 +1562,11 @@ namespace Ryujinx.Graphics.Vulkan
private void RecreatePipelineIfNeeded(PipelineBindPoint pbp) private void RecreatePipelineIfNeeded(PipelineBindPoint pbp)
{ {
if (AutoFlush.ShouldFlushDraw(DrawCount))
{
Gd.FlushAllCommands();
}
DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer); DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer);
// Commit changes to the support buffer before drawing. // Commit changes to the support buffer before drawing.

View File

@@ -1,6 +1,7 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
@@ -26,6 +27,7 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Device _device; private readonly Device _device;
private List<SyncHandle> _handles; private List<SyncHandle> _handles;
private ulong FlushId; private ulong FlushId;
private long WaitTicks;
public SyncManager(VulkanRenderer gd, Device device) public SyncManager(VulkanRenderer gd, Device device)
{ {
@@ -130,6 +132,8 @@ namespace Ryujinx.Graphics.Vulkan
return; return;
} }
long beforeTicks = Stopwatch.GetTimestamp();
if (result.NeedsFlush(FlushId)) if (result.NeedsFlush(FlushId))
{ {
_gd.InterruptAction(() => _gd.InterruptAction(() =>
@@ -142,12 +146,14 @@ namespace Ryujinx.Graphics.Vulkan
} }
bool signaled = result.Signalled || result.Waitable.WaitForFences(_gd.Api, _device, 1000000000); bool signaled = result.Signalled || result.Waitable.WaitForFences(_gd.Api, _device, 1000000000);
if (!signaled) if (!signaled)
{ {
Logger.Error?.PrintMsg(LogClass.Gpu, $"VK Sync Object {result.ID} failed to signal within 1000ms. Continuing..."); Logger.Error?.PrintMsg(LogClass.Gpu, $"VK Sync Object {result.ID} failed to signal within 1000ms. Continuing...");
} }
else else
{ {
WaitTicks += Stopwatch.GetTimestamp() - beforeTicks;
result.Signalled = true; result.Signalled = true;
} }
} }
@@ -188,5 +194,13 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
} }
public long GetAndResetWaitTicks()
{
long result = WaitTicks;
WaitTicks = 0;
return result;
}
} }
} }

View File

@@ -49,6 +49,7 @@ namespace Ryujinx.Graphics.Vulkan
internal PipelineLayoutCache PipelineLayoutCache { get; private set; } internal PipelineLayoutCache PipelineLayoutCache { get; private set; }
internal BackgroundResources BackgroundResources { get; private set; } internal BackgroundResources BackgroundResources { get; private set; }
internal Action<Action> InterruptAction { get; private set; } internal Action<Action> InterruptAction { get; private set; }
internal SyncManager SyncManager { get; private set; }
internal BufferManager BufferManager { get; private set; } internal BufferManager BufferManager { get; private set; }
@@ -58,7 +59,6 @@ namespace Ryujinx.Graphics.Vulkan
private VulkanDebugMessenger _debugMessenger; private VulkanDebugMessenger _debugMessenger;
private Counters _counters; private Counters _counters;
private SyncManager _syncManager;
private PipelineFull _pipeline; private PipelineFull _pipeline;
@@ -327,7 +327,7 @@ namespace Ryujinx.Graphics.Vulkan
BufferManager = new BufferManager(this, _device); BufferManager = new BufferManager(this, _device);
_syncManager = new SyncManager(this, _device); SyncManager = new SyncManager(this, _device);
_pipeline = new PipelineFull(this, _device); _pipeline = new PipelineFull(this, _device);
_pipeline.Initialize(); _pipeline.Initialize();
@@ -436,7 +436,7 @@ namespace Ryujinx.Graphics.Vulkan
internal void RegisterFlush() internal void RegisterFlush()
{ {
_syncManager.RegisterFlush(); SyncManager.RegisterFlush();
} }
public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size) public PinnedSpan<byte> GetBufferData(BufferHandle buffer, int offset, int size)
@@ -696,7 +696,7 @@ namespace Ryujinx.Graphics.Vulkan
public void PreFrame() public void PreFrame()
{ {
_syncManager.Cleanup(); SyncManager.Cleanup();
} }
public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, bool hostReserved) public ICounterEvent ReportCounter(CounterType type, EventHandler<ulong> resultHandler, bool hostReserved)
@@ -736,7 +736,7 @@ namespace Ryujinx.Graphics.Vulkan
public void CreateSync(ulong id, bool strict) public void CreateSync(ulong id, bool strict)
{ {
_syncManager.Create(id, strict); SyncManager.Create(id, strict);
} }
public IProgram LoadProgramBinary(byte[] programBinary, bool isFragment, ShaderInfo info) public IProgram LoadProgramBinary(byte[] programBinary, bool isFragment, ShaderInfo info)
@@ -746,12 +746,12 @@ namespace Ryujinx.Graphics.Vulkan
public void WaitSync(ulong id) public void WaitSync(ulong id)
{ {
_syncManager.Wait(id); SyncManager.Wait(id);
} }
public ulong GetCurrentSync() public ulong GetCurrentSync()
{ {
return _syncManager.GetCurrent(); return SyncManager.GetCurrent();
} }
public void SetInterruptAction(Action<Action> interruptAction) public void SetInterruptAction(Action<Action> interruptAction)