diff --git a/ARMeilleure/Instructions/InstEmitMemoryExHelper.cs b/ARMeilleure/Instructions/InstEmitMemoryExHelper.cs
index 317e4276e..15f5e2abc 100644
--- a/ARMeilleure/Instructions/InstEmitMemoryExHelper.cs
+++ b/ARMeilleure/Instructions/InstEmitMemoryExHelper.cs
@@ -19,19 +19,8 @@ namespace ARMeilleure.Instructions
if (size == 4)
{
- Operand isUnalignedAddr = InstEmitMemoryHelper.EmitAddressCheck(context, address, size);
-
- Operand lblFastPath = Label();
-
- context.BranchIfFalse(lblFastPath, isUnalignedAddr);
-
- // The call is not expected to return (it should throw).
- context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)), address);
-
- context.MarkLabel(lblFastPath);
-
// Only 128-bit CAS is guaranteed to have a atomic load.
- Operand physAddr = InstEmitMemoryHelper.EmitPtPointerLoad(context, address, null, write: false);
+ Operand physAddr = InstEmitMemoryHelper.EmitPtPointerLoad(context, address, null, write: false, 4);
Operand zero = context.VectorZero();
@@ -119,20 +108,8 @@ namespace ARMeilleure.Instructions
context.BranchIfTrue(lblExit, exFailed);
- // STEP 2: We have exclusive access, make sure that the address is valid.
- Operand isUnalignedAddr = InstEmitMemoryHelper.EmitAddressCheck(context, address, size);
-
- Operand lblFastPath = Label();
-
- context.BranchIfFalse(lblFastPath, isUnalignedAddr);
-
- // The call is not expected to return (it should throw).
- context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)), address);
-
- // STEP 3: We have exclusive access and the address is valid, attempt the store using CAS.
- context.MarkLabel(lblFastPath);
-
- Operand physAddr = InstEmitMemoryHelper.EmitPtPointerLoad(context, address, null, write: true);
+ // STEP 2: We have exclusive access and the address is valid, attempt the store using CAS.
+ Operand physAddr = InstEmitMemoryHelper.EmitPtPointerLoad(context, address, null, write: true, size);
Operand exValuePtr = context.Add(arg0, Const((long)NativeContext.GetExclusiveValueOffset()));
Operand exValue = size switch
@@ -151,7 +128,7 @@ namespace ARMeilleure.Instructions
_ => context.CompareAndSwap(physAddr, exValue, value)
};
- // STEP 4: Check if we succeeded by comparing expected and in-memory values.
+ // STEP 3: Check if we succeeded by comparing expected and in-memory values.
Operand storeFailed;
if (size == 4)
diff --git a/ARMeilleure/Instructions/InstEmitMemoryHelper.cs b/ARMeilleure/Instructions/InstEmitMemoryHelper.cs
index fd5c5bca3..cb4fae8f9 100644
--- a/ARMeilleure/Instructions/InstEmitMemoryHelper.cs
+++ b/ARMeilleure/Instructions/InstEmitMemoryHelper.cs
@@ -127,11 +127,7 @@ namespace ARMeilleure.Instructions
Operand lblSlowPath = Label();
Operand lblEnd = Label();
- Operand isUnalignedAddr = EmitAddressCheck(context, address, size);
-
- context.BranchIfTrue(lblSlowPath, isUnalignedAddr);
-
- Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath, write: false);
+ Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath, write: false, size);
Operand value = null;
@@ -161,18 +157,7 @@ namespace ARMeilleure.Instructions
throw new ArgumentOutOfRangeException(nameof(size));
}
- Operand isUnalignedAddr = EmitAddressCheck(context, address, size);
-
- Operand lblFastPath = Label();
-
- context.BranchIfFalse(lblFastPath, isUnalignedAddr, BasicBlockFrequency.Cold);
-
- // The call is not expected to return (it should throw).
- context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)), address);
-
- context.MarkLabel(lblFastPath);
-
- Operand physAddr = EmitPtPointerLoad(context, address, null, write: false);
+ Operand physAddr = EmitPtPointerLoad(context, address, null, write: false, size);
return size switch
{
@@ -195,11 +180,7 @@ namespace ARMeilleure.Instructions
Operand lblSlowPath = Label();
Operand lblEnd = Label();
- Operand isUnalignedAddr = EmitAddressCheck(context, address, size);
-
- context.BranchIfTrue(lblSlowPath, isUnalignedAddr);
-
- Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath, write: false);
+ Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath, write: false, size);
Operand value = null;
@@ -233,11 +214,7 @@ namespace ARMeilleure.Instructions
Operand lblSlowPath = Label();
Operand lblEnd = Label();
- Operand isUnalignedAddr = EmitAddressCheck(context, address, size);
-
- context.BranchIfTrue(lblSlowPath, isUnalignedAddr);
-
- Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath, write: true);
+ Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath, write: true, size);
Operand value = GetInt(context, rt);
@@ -270,18 +247,7 @@ namespace ARMeilleure.Instructions
throw new ArgumentOutOfRangeException(nameof(size));
}
- Operand isUnalignedAddr = EmitAddressCheck(context, address, size);
-
- Operand lblFastPath = Label();
-
- context.BranchIfFalse(lblFastPath, isUnalignedAddr, BasicBlockFrequency.Cold);
-
- // The call is not expected to return (it should throw).
- context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.ThrowInvalidMemoryAccess)), address);
-
- context.MarkLabel(lblFastPath);
-
- Operand physAddr = EmitPtPointerLoad(context, address, null, write: true);
+ Operand physAddr = EmitPtPointerLoad(context, address, null, write: true, size);
if (size < 3 && value.Type == OperandType.I64)
{
@@ -312,11 +278,7 @@ namespace ARMeilleure.Instructions
Operand lblSlowPath = Label();
Operand lblEnd = Label();
- Operand isUnalignedAddr = EmitAddressCheck(context, address, size);
-
- context.BranchIfTrue(lblSlowPath, isUnalignedAddr);
-
- Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath, write: true);
+ Operand physAddr = EmitPtPointerLoad(context, address, lblSlowPath, write: true, size);
Operand value = GetVec(rt);
@@ -338,61 +300,49 @@ namespace ARMeilleure.Instructions
context.MarkLabel(lblEnd);
}
- public static Operand EmitAddressCheck(ArmEmitterContext context, Operand address, int size)
+ public static Operand EmitPtPointerLoad(ArmEmitterContext context, Operand address, Operand lblSlowPath, bool write, int size)
{
- ulong addressCheckMask = ~((1UL << context.Memory.AddressSpaceBits) - 1);
-
- addressCheckMask |= (1u << size) - 1;
-
- return context.BitwiseAnd(address, Const(address.Type, (long)addressCheckMask));
- }
-
- public static Operand EmitPtPointerLoad(ArmEmitterContext context, Operand address, Operand lblSlowPath, bool write)
- {
- int ptLevelBits = context.Memory.AddressSpaceBits - 12; // 12 = Number of page bits.
+ int ptLevelBits = context.Memory.AddressSpaceBits - PageBits;
int ptLevelSize = 1 << ptLevelBits;
int ptLevelMask = ptLevelSize - 1;
+ Operand addrRotated = size != 0 ? context.RotateRight(address, Const(size)) : address;
+ Operand addrShifted = context.ShiftRightUI(addrRotated, Const(PageBits - size));
+
Operand pte = Ptc.State == PtcState.Disabled
? Const(context.Memory.PageTablePointer.ToInt64())
: Const(context.Memory.PageTablePointer.ToInt64(), true, Ptc.PageTablePointerIndex);
- int bit = PageBits;
+ Operand pteOffset = context.BitwiseAnd(addrShifted, Const(addrShifted.Type, ptLevelMask));
- // Load page table entry from the page table.
- // This was designed to support multi-level page tables of any size, however right
- // now we only use flat page tables (so there's only one level).
- // The page table entry contains the host address where the page is located.
- // Additionally, the higher 16-bits of the host address may contain extra information
- // used for write tracking, so this must be handled here aswell.
- do
+ if (pteOffset.Type == OperandType.I32)
{
- Operand addrPart = context.ShiftRightUI(address, Const(bit));
-
- bit += ptLevelBits;
-
- if (bit < context.Memory.AddressSpaceBits)
- {
- addrPart = context.BitwiseAnd(addrPart, Const(addrPart.Type, ptLevelMask));
- }
-
- Operand pteOffset = context.ShiftLeft(addrPart, Const(3));
-
- if (pteOffset.Type == OperandType.I32)
- {
- pteOffset = context.ZeroExtend32(OperandType.I64, pteOffset);
- }
-
- Operand pteAddress = context.Add(pte, pteOffset);
-
- pte = context.Load(OperandType.I64, pteAddress);
+ pteOffset = context.ZeroExtend32(OperandType.I64, pteOffset);
}
- while (bit < context.Memory.AddressSpaceBits);
+
+ pte = context.Load(OperandType.I64, context.Add(pte, context.ShiftLeft(pteOffset, Const(3))));
+
+ if (addrShifted.Type == OperandType.I32)
+ {
+ addrShifted = context.ZeroExtend32(OperandType.I64, addrShifted);
+ }
+
+ // If the VA is out of range, or not aligned to the access size, force PTE to 0 by masking it.
+ pte = context.BitwiseAnd(pte, context.ShiftRightSI(context.Add(addrShifted, Const(-(long)ptLevelSize)), Const(63)));
if (lblSlowPath != null)
{
- ulong protection = (write ? 3UL : 1UL) << 48;
- context.BranchIfTrue(lblSlowPath, context.BitwiseAnd(pte, Const(protection)));
+ if (write)
+ {
+ pte = context.ShiftLeft(pte, Const(1));
+ context.BranchIf(lblSlowPath, pte, Const(0L), Comparison.LessOrEqual);
+ pte = context.ShiftRightUI(pte, Const(1));
+ }
+ else
+ {
+ context.BranchIf(lblSlowPath, pte, Const(0L), Comparison.LessOrEqual);
+ pte = context.BitwiseAnd(pte, Const(0xffffffffffffUL)); // Ignore any software protection bits. (they are still used by C# memory access)
+ }
}
else
{
@@ -401,13 +351,15 @@ namespace ARMeilleure.Instructions
Operand lblNotWatched = Label();
- // Is the page currently being tracked for read/write? If so we need to call MarkRegionAsModified.
+ // Is the page currently being tracked for read/write? If so we need to call SignalMemoryTracking.
context.BranchIf(lblNotWatched, pte, Const(0L), Comparison.GreaterOrEqual, BasicBlockFrequency.Cold);
- // Mark the region as modified. Size here doesn't matter as address is assumed to be size aligned here.
+ // Signal memory tracking. Size here doesn't matter as address is assumed to be size aligned here.
context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.SignalMemoryTracking)), address, Const(1UL), Const(write ? 1 : 0));
context.MarkLabel(lblNotWatched);
+ pte = context.BitwiseAnd(pte, Const(0xffffffffffffUL)); // Ignore any software protection bits. (they are still used by C# memory access)
+
Operand lblNonNull = Label();
// Skip exception if the PTE address is non-null (not zero).
@@ -418,8 +370,6 @@ namespace ARMeilleure.Instructions
context.MarkLabel(lblNonNull);
}
- pte = context.BitwiseAnd(pte, Const(0xffffffffffffUL)); // Ignore any software protection bits. (they are still used by c# memory access)
-
Operand pageOffset = context.BitwiseAnd(address, Const(address.Type, PageMask));
if (pageOffset.Type == OperandType.I32)
diff --git a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
index 79993d879..901c823e5 100644
--- a/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
+++ b/Ryujinx.Common/Configuration/ConfigurationFileFormat.cs
@@ -14,7 +14,7 @@ namespace Ryujinx.Configuration
///
/// The current version of the file format
///
- public const int CurrentVersion = 20;
+ public const int CurrentVersion = 22;
public int Version { get; set; }
@@ -133,6 +133,11 @@ namespace Ryujinx.Configuration
///
public bool ShowConfirmExit { get; set; }
+ ///
+ /// Hide Cursor on Idle
+ ///
+ public bool HideCursorOnIdle { get; set; }
+
///
/// Enables or disables Vertical Sync
///
@@ -253,4 +258,4 @@ namespace Ryujinx.Configuration
JsonHelper.Serialize(fileStream, this, true);
}
}
-}
\ No newline at end of file
+}
diff --git a/Ryujinx.Common/Configuration/ConfigurationState.cs b/Ryujinx.Common/Configuration/ConfigurationState.cs
index 61cc8f899..d51ee9efc 100644
--- a/Ryujinx.Common/Configuration/ConfigurationState.cs
+++ b/Ryujinx.Common/Configuration/ConfigurationState.cs
@@ -365,6 +365,11 @@ namespace Ryujinx.Configuration
///
public ReactiveObject ShowConfirmExit { get; private set; }
+ ///
+ /// Hide Cursor on Idle
+ ///
+ public ReactiveObject HideCursorOnIdle { get; private set; }
+
private ConfigurationState()
{
Ui = new UiSection();
@@ -375,6 +380,7 @@ namespace Ryujinx.Configuration
EnableDiscordIntegration = new ReactiveObject();
CheckUpdatesOnStart = new ReactiveObject();
ShowConfirmExit = new ReactiveObject();
+ HideCursorOnIdle = new ReactiveObject();
}
public ConfigurationFileFormat ToFileFormat()
@@ -420,6 +426,7 @@ namespace Ryujinx.Configuration
EnableDiscordIntegration = EnableDiscordIntegration,
CheckUpdatesOnStart = CheckUpdatesOnStart,
ShowConfirmExit = ShowConfirmExit,
+ HideCursorOnIdle = HideCursorOnIdle,
EnableVsync = Graphics.EnableVsync,
EnableShaderCache = Graphics.EnableShaderCache,
EnablePtc = System.EnablePtc,
@@ -483,6 +490,7 @@ namespace Ryujinx.Configuration
EnableDiscordIntegration.Value = true;
CheckUpdatesOnStart.Value = true;
ShowConfirmExit.Value = true;
+ HideCursorOnIdle.Value = false;
Graphics.EnableVsync.Value = true;
Graphics.EnableShaderCache.Value = true;
System.EnablePtc.Value = true;
@@ -787,6 +795,15 @@ namespace Ryujinx.Configuration
configurationFileUpdated = true;
}
+ if (configurationFileFormat.Version < 22)
+ {
+ Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 22.");
+
+ configurationFileFormat.HideCursorOnIdle = false;
+
+ configurationFileUpdated = true;
+ }
+
List inputConfig = new List();
inputConfig.AddRange(configurationFileFormat.ControllerConfig);
inputConfig.AddRange(configurationFileFormat.KeyboardConfig);
@@ -814,6 +831,7 @@ namespace Ryujinx.Configuration
EnableDiscordIntegration.Value = configurationFileFormat.EnableDiscordIntegration;
CheckUpdatesOnStart.Value = configurationFileFormat.CheckUpdatesOnStart;
ShowConfirmExit.Value = configurationFileFormat.ShowConfirmExit;
+ HideCursorOnIdle.Value = configurationFileFormat.HideCursorOnIdle;
Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync;
Graphics.EnableShaderCache.Value = configurationFileFormat.EnableShaderCache;
System.EnablePtc.Value = configurationFileFormat.EnablePtc;
diff --git a/Ryujinx.Cpu/MemoryManager.cs b/Ryujinx.Cpu/MemoryManager.cs
index cef201265..8c8bd3a4c 100644
--- a/Ryujinx.Cpu/MemoryManager.cs
+++ b/Ryujinx.Cpu/MemoryManager.cs
@@ -21,6 +21,8 @@ namespace Ryujinx.Cpu
private const int PteSize = 8;
+ private const int PointerTagBit = 62;
+
private readonly InvalidAccessHandler _invalidAccessHandler;
///
@@ -556,11 +558,12 @@ namespace Ryujinx.Cpu
// Protection is inverted on software pages, since the default value is 0.
protection = (~protection) & MemoryPermission.ReadAndWrite;
- long tag = (long)protection << 48;
- if (tag > 0)
+ long tag = protection switch
{
- tag |= long.MinValue; // If any protection is present, the whole pte is negative.
- }
+ MemoryPermission.None => 0L,
+ MemoryPermission.Read => 2L << PointerTagBit,
+ _ => 3L << PointerTagBit
+ };
ulong endVa = (va + size + PageMask) & ~(ulong)PageMask;
long invTagMask = ~(0xffffL << 48);
@@ -628,7 +631,7 @@ namespace Ryujinx.Cpu
// tracking using host guard pages in future, but also supporting platforms where this is not possible.
// Write tag includes read protection, since we don't have any read actions that aren't performed before write too.
- long tag = (write ? 3L : 1L) << 48;
+ long tag = (write ? 3L : 2L) << PointerTagBit;
ulong endVa = (va + size + PageMask) & ~(ulong)PageMask;
diff --git a/Ryujinx/Config.json b/Ryujinx/Config.json
index 76c0a139a..804b020c4 100644
--- a/Ryujinx/Config.json
+++ b/Ryujinx/Config.json
@@ -1,5 +1,5 @@
{
- "version": 20,
+ "version": 22,
"res_scale": 1,
"res_scale_custom": 1,
"max_anisotropy": -1,
@@ -22,6 +22,7 @@
"enable_discord_integration": true,
"check_updates_on_start": true,
"show_confirm_exit": true,
+ "hide_cursor_on_idle": false,
"enable_vsync": true,
"enable_shader_cache": true,
"enable_ptc": true,
diff --git a/Ryujinx/Ui/GLRenderer.cs b/Ryujinx/Ui/GLRenderer.cs
index 35c00a332..52dcedbc9 100644
--- a/Ryujinx/Ui/GLRenderer.cs
+++ b/Ryujinx/Ui/GLRenderer.cs
@@ -42,6 +42,8 @@ namespace Ryujinx.Ui
private double _mouseY;
private bool _mousePressed;
+ private DateTime _lastCursorMoveTime = DateTime.Now;
+
private bool _toggleFullscreen;
private bool _toggleDockedMode;
@@ -62,6 +64,8 @@ namespace Ryujinx.Ui
private GraphicsDebugLevel _glLogLevel;
private readonly ManualResetEvent _exitEvent;
+
+ private Gdk.Cursor _invisibleCursor = new Gdk.Cursor (Gdk.Display.Default, Gdk.CursorType.BlankCursor);
public GlRenderer(Switch device, GraphicsDebugLevel glLogLevel)
: base (GetGraphicsMode(),
@@ -304,9 +308,37 @@ namespace Ryujinx.Ui
_mouseY = evnt.Y;
}
+ ResetCursorIdle();
+
return false;
}
+ private void ResetCursorIdle()
+ {
+ if (ConfigurationState.Instance.HideCursorOnIdle)
+ {
+ _lastCursorMoveTime = DateTime.Now;
+ }
+
+ if (Window.Cursor != null)
+ {
+ Window.Cursor = null;
+ }
+ }
+
+ private void HideCursorIdle()
+ {
+ if (ConfigurationState.Instance.HideCursorOnIdle)
+ {
+ TimeSpan elapsedTime = DateTime.Now.Subtract(_lastCursorMoveTime);
+
+ if (elapsedTime.TotalSeconds > 8)
+ {
+ Gtk.Application.Invoke(delegate { Window.Cursor = _invisibleCursor; });
+ }
+ }
+ }
+
protected override void OnGetPreferredHeight(out int minimumHeight, out int naturalHeight)
{
Gdk.Monitor monitor = Display.GetMonitorAtWindow(Window);
@@ -485,6 +517,8 @@ namespace Ryujinx.Ui
MotionDevice motionDevice = new MotionDevice(_dsuClient);
+ HideCursorIdle();
+
foreach (InputConfig inputConfig in ConfigurationState.Instance.Hid.InputConfig.Value)
{
ControllerKeys currentButton = 0;
diff --git a/Ryujinx/Ui/Windows/SettingsWindow.cs b/Ryujinx/Ui/Windows/SettingsWindow.cs
index ba64226c3..e839a366c 100644
--- a/Ryujinx/Ui/Windows/SettingsWindow.cs
+++ b/Ryujinx/Ui/Windows/SettingsWindow.cs
@@ -43,6 +43,7 @@ namespace Ryujinx.Ui.Windows
[GUI] CheckButton _discordToggle;
[GUI] CheckButton _checkUpdatesToggle;
[GUI] CheckButton _showConfirmExitToggle;
+ [GUI] CheckButton _hideCursorOnIdleToggle;
[GUI] CheckButton _vSyncToggle;
[GUI] CheckButton _shaderCacheToggle;
[GUI] CheckButton _ptcToggle;
@@ -185,6 +186,11 @@ namespace Ryujinx.Ui.Windows
_showConfirmExitToggle.Click();
}
+ if (ConfigurationState.Instance.HideCursorOnIdle)
+ {
+ _hideCursorOnIdleToggle.Click();
+ }
+
if (ConfigurationState.Instance.Graphics.EnableVsync)
{
_vSyncToggle.Click();
@@ -403,6 +409,7 @@ namespace Ryujinx.Ui.Windows
ConfigurationState.Instance.EnableDiscordIntegration.Value = _discordToggle.Active;
ConfigurationState.Instance.CheckUpdatesOnStart.Value = _checkUpdatesToggle.Active;
ConfigurationState.Instance.ShowConfirmExit.Value = _showConfirmExitToggle.Active;
+ ConfigurationState.Instance.HideCursorOnIdle.Value = _hideCursorOnIdleToggle.Active;
ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active;
ConfigurationState.Instance.Graphics.EnableShaderCache.Value = _shaderCacheToggle.Active;
ConfigurationState.Instance.System.EnablePtc.Value = _ptcToggle.Active;
@@ -582,4 +589,4 @@ namespace Ryujinx.Ui.Windows
Dispose();
}
}
-}
\ No newline at end of file
+}
diff --git a/Ryujinx/Ui/Windows/SettingsWindow.glade b/Ryujinx/Ui/Windows/SettingsWindow.glade
index 5ead8dd82..e9d241f80 100644
--- a/Ryujinx/Ui/Windows/SettingsWindow.glade
+++ b/Ryujinx/Ui/Windows/SettingsWindow.glade
@@ -150,7 +150,23 @@
False
True
5
- 1
+ 2
+
+
+
+
+
+ False
+ True
+ 5
+ 3
diff --git a/Ryujinx/_schema.json b/Ryujinx/_schema.json
index eff1f9d4c..b61e9ed25 100644
--- a/Ryujinx/_schema.json
+++ b/Ryujinx/_schema.json
@@ -1209,6 +1209,17 @@
true,
false
]
+ },
+ "hide_cursor_on_idle": {
+ "$id": "#/properties/hide_cursor_on_idle",
+ "type": "boolean",
+ "title": "Hide Cursor On Idle",
+ "description": "Hides the cursor after being idle for 5 seconds",
+ "default": false,
+ "examples": [
+ true,
+ false
+ ]
},
"enable_vsync": {
"$id": "#/properties/enable_vsync",