Compare commits

...

4 Commits

Author SHA1 Message Date
NitroTears
55557525b1 Create Desktop Shortcut fixes (#5852)
* remove duplicate basePath arg, add --fullscreen arg

* Changing FriendlyName to set "Ryujinx" text

* Fix GetArgsString using the base path

* Change desktop path to the Applications folder when creating shortcut on Mac

Co-authored-by: Nicko Anastassiu <134955950+nickoanastassiu@users.noreply.github.com>

* Move Create Shortcut button to top of context menu

---------

Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com>
Co-authored-by: Nicko Anastassiu <134955950+nickoanastassiu@users.noreply.github.com>
2023-11-11 16:08:42 +01:00
Isaac Marovitz
7e6342e44d Add accelerator keys for Options and Help (#5884) 2023-11-11 15:57:15 +01:00
jcm
c3555cb5d6 UI: Change default hide cursor mode to OnIdle (#5906)
Co-authored-by: jcm <butt@butts.com>
2023-11-11 15:27:53 +01:00
gdkchan
815819767c Force all exclusive memory accesses to be ordered on AppleHv (#5898)
* Implement reprotect method on virtual memory manager (currently stubbed)

* Force all exclusive memory accesses to be ordered on AppleHv

* Format whitespace

* Fix test build

* Fix comment copy/paste

* Fix wrong bit for isLoad

* Update src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs

Co-authored-by: riperiperi <rhy3756547@hotmail.com>

---------

Co-authored-by: riperiperi <rhy3756547@hotmail.com>
2023-11-07 13:24:10 -03:00
16 changed files with 188 additions and 79 deletions

View File

@@ -14,7 +14,7 @@
"MenuBarFileOpenEmuFolder": "Open Ryujinx Folder",
"MenuBarFileOpenLogsFolder": "Open Logs Folder",
"MenuBarFileExit": "_Exit",
"MenuBarOptions": "Options",
"MenuBarOptions": "_Options",
"MenuBarOptionsToggleFullscreen": "Toggle Fullscreen",
"MenuBarOptionsStartGamesInFullscreen": "Start Games in Fullscreen Mode",
"MenuBarOptionsStopEmulation": "Stop Emulation",
@@ -30,7 +30,7 @@
"MenuBarToolsManageFileTypes": "Manage file types",
"MenuBarToolsInstallFileTypes": "Install file types",
"MenuBarToolsUninstallFileTypes": "Uninstall file types",
"MenuBarHelp": "Help",
"MenuBarHelp": "_Help",
"MenuBarHelpCheckForUpdates": "Check for Updates",
"MenuBarHelpAbout": "About",
"MenuSearch": "Search...",

View File

@@ -12,6 +12,11 @@
Click="ToggleFavorite_Click"
Header="{locale:Locale GameListContextMenuToggleFavorite}"
ToolTip.Tip="{locale:Locale GameListContextMenuToggleFavoriteToolTip}" />
<MenuItem
Click="CreateApplicationShortcut_Click"
Header="{locale:Locale GameListContextMenuCreateShortcut}"
IsEnabled="{Binding CreateShortcutEnabled}"
ToolTip.Tip="{locale:Locale GameListContextMenuCreateShortcutToolTip}" />
<Separator />
<MenuItem
Click="OpenUserSaveDirectory_Click"
@@ -82,9 +87,4 @@
Header="{locale:Locale GameListContextMenuExtractDataLogo}"
ToolTip.Tip="{locale:Locale GameListContextMenuExtractDataLogoToolTip}" />
</MenuItem>
<MenuItem
Click="CreateApplicationShortcut_Click"
Header="{locale:Locale GameListContextMenuCreateShortcut}"
IsEnabled="{Binding CreateShortcutEnabled}"
ToolTip.Tip="{locale:Locale GameListContextMenuCreateShortcutToolTip}" />
</MenuFlyout>

View File

@@ -0,0 +1,62 @@
using System;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
namespace Ryujinx.Cpu.AppleHv
{
static class HvCodePatcher
{
private const uint XMask = 0x3f808000u;
private const uint XValue = 0x8000000u;
private const uint ZrIndex = 31u;
public static void RewriteUnorderedExclusiveInstructions(Span<byte> code)
{
Span<uint> codeUint = MemoryMarshal.Cast<byte, uint>(code);
Span<Vector128<uint>> codeVector = MemoryMarshal.Cast<byte, Vector128<uint>>(code);
Vector128<uint> mask = Vector128.Create(XMask);
Vector128<uint> value = Vector128.Create(XValue);
for (int index = 0; index < codeVector.Length; index++)
{
Vector128<uint> v = codeVector[index];
if (Vector128.EqualsAny(Vector128.BitwiseAnd(v, mask), value))
{
int baseIndex = index * 4;
for (int instIndex = baseIndex; instIndex < baseIndex + 4; instIndex++)
{
ref uint inst = ref codeUint[instIndex];
if ((inst & XMask) != XValue)
{
continue;
}
bool isPair = (inst & (1u << 21)) != 0;
bool isLoad = (inst & (1u << 22)) != 0;
uint rt2 = (inst >> 10) & 0x1fu;
uint rs = (inst >> 16) & 0x1fu;
if (isLoad && rs != ZrIndex)
{
continue;
}
if (!isPair && rt2 != ZrIndex)
{
continue;
}
// Set the ordered flag.
inst |= 1u << 15;
}
}
}
}
}
}

View File

@@ -128,21 +128,6 @@ namespace Ryujinx.Cpu.AppleHv
}
}
#pragma warning disable IDE0051 // Remove unused private member
/// <summary>
/// Ensures the combination of virtual address and size is part of the addressable space and fully mapped.
/// </summary>
/// <param name="va">Virtual address of the range</param>
/// <param name="size">Size of the range in bytes</param>
private void AssertMapped(ulong va, ulong size)
{
if (!ValidateAddressAndSize(va, size) || !IsRangeMappedImpl(va, size))
{
throw new InvalidMemoryRegionException($"Not mapped: va=0x{va:X16}, size=0x{size:X16}");
}
}
#pragma warning restore IDE0051
/// <inheritdoc/>
public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags)
{
@@ -736,6 +721,24 @@ namespace Ryujinx.Cpu.AppleHv
return (int)(vaSpan / PageSize);
}
/// <inheritdoc/>
public void Reprotect(ulong va, ulong size, MemoryPermission protection)
{
if (protection.HasFlag(MemoryPermission.Execute))
{
// Some applications use unordered exclusive memory access instructions
// where it is not valid to do so, leading to memory re-ordering that
// makes the code behave incorrectly on some CPUs.
// To work around this, we force all such accesses to be ordered.
using WritableRegion writableRegion = GetWritableRegion(va, (int)size);
HvCodePatcher.RewriteUnorderedExclusiveInstructions(writableRegion.Memory.Span);
}
// TODO
}
/// <inheritdoc/>
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
{

View File

@@ -575,24 +575,17 @@ namespace Ryujinx.Cpu.Jit
}
}
#pragma warning disable IDE0051 // Remove unused private member
private ulong GetPhysicalAddress(ulong va)
{
// We return -1L if the virtual address is invalid or unmapped.
if (!ValidateAddress(va) || !IsMapped(va))
{
return ulong.MaxValue;
}
return GetPhysicalAddressInternal(va);
}
#pragma warning restore IDE0051
private ulong GetPhysicalAddressInternal(ulong va)
{
return PteToPa(_pageTable.Read<ulong>((va / PageSize) * PteSize) & ~(0xffffUL << 48)) + (va & PageMask);
}
/// <inheritdoc/>
public void Reprotect(ulong va, ulong size, MemoryPermission protection)
{
// TODO
}
/// <inheritdoc/>
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
{
@@ -698,9 +691,5 @@ namespace Ryujinx.Cpu.Jit
/// Disposes of resources used by the memory manager.
/// </summary>
protected override void Destroy() => _pageTable.Dispose();
#pragma warning disable IDE0051 // Remove unused private member
private static void ThrowInvalidMemoryRegionException(string message) => throw new InvalidMemoryRegionException(message);
#pragma warning restore IDE0051
}
}

View File

@@ -615,6 +615,12 @@ namespace Ryujinx.Cpu.Jit
return (int)(vaSpan / PageSize);
}
/// <inheritdoc/>
public void Reprotect(ulong va, ulong size, MemoryPermission protection)
{
// TODO
}
/// <inheritdoc/>
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
{

View File

@@ -0,0 +1,46 @@
using Ryujinx.Memory;
using System;
namespace Ryujinx.HLE.HOS.Kernel.Memory
{
[Flags]
enum KMemoryPermission : uint
{
None = 0,
UserMask = Read | Write | Execute,
Mask = uint.MaxValue,
Read = 1 << 0,
Write = 1 << 1,
Execute = 1 << 2,
DontCare = 1 << 28,
ReadAndWrite = Read | Write,
ReadAndExecute = Read | Execute,
}
static class KMemoryPermissionExtensions
{
public static MemoryPermission Convert(this KMemoryPermission permission)
{
MemoryPermission output = MemoryPermission.None;
if (permission.HasFlag(KMemoryPermission.Read))
{
output = MemoryPermission.Read;
}
if (permission.HasFlag(KMemoryPermission.Write))
{
output |= MemoryPermission.Write;
}
if (permission.HasFlag(KMemoryPermission.Execute))
{
output |= MemoryPermission.Execute;
}
return output;
}
}
}

View File

@@ -203,15 +203,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
/// <inheritdoc/>
protected override Result Reprotect(ulong address, ulong pagesCount, KMemoryPermission permission)
{
// TODO.
_cpuMemory.Reprotect(address, pagesCount * PageSize, permission.Convert());
return Result.Success;
}
/// <inheritdoc/>
protected override Result ReprotectWithAttributes(ulong address, ulong pagesCount, KMemoryPermission permission)
protected override Result ReprotectAndFlush(ulong address, ulong pagesCount, KMemoryPermission permission)
{
// TODO.
return Result.Success;
// TODO: Flush JIT cache.
return Reprotect(address, pagesCount, permission);
}
/// <inheritdoc/>

View File

@@ -1255,7 +1255,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
if ((oldPermission & KMemoryPermission.Execute) != 0)
{
result = ReprotectWithAttributes(address, pagesCount, permission);
result = ReprotectAndFlush(address, pagesCount, permission);
}
else
{
@@ -3036,13 +3036,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
protected abstract Result Reprotect(ulong address, ulong pagesCount, KMemoryPermission permission);
/// <summary>
/// Changes the permissions of a given virtual memory region.
/// Changes the permissions of a given virtual memory region, while also flushing the cache.
/// </summary>
/// <param name="address">Virtual address of the region to have the permission changes</param>
/// <param name="pagesCount">Number of pages to have their permissions changed</param>
/// <param name="permission">New permission</param>
/// <returns>Result of the permission change operation</returns>
protected abstract Result ReprotectWithAttributes(ulong address, ulong pagesCount, KMemoryPermission permission);
protected abstract Result ReprotectAndFlush(ulong address, ulong pagesCount, KMemoryPermission permission);
/// <summary>
/// Alerts the memory tracking that a given region has been read from or written to.

View File

@@ -1,20 +0,0 @@
using System;
namespace Ryujinx.HLE.HOS.Kernel.Memory
{
[Flags]
enum KMemoryPermission : uint
{
None = 0,
UserMask = Read | Write | Execute,
Mask = uint.MaxValue,
Read = 1 << 0,
Write = 1 << 1,
Execute = 1 << 2,
DontCare = 1 << 28,
ReadAndWrite = Read | Write,
ReadAndExecute = Read | Execute,
}
}

View File

@@ -455,6 +455,11 @@ namespace Ryujinx.Memory
return _pageTable.Read(va) + (nuint)(va & PageMask);
}
/// <inheritdoc/>
public void Reprotect(ulong va, ulong size, MemoryPermission protection)
{
}
/// <inheritdoc/>
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
{

View File

@@ -104,6 +104,12 @@ namespace Ryujinx.Memory
/// <returns>True if the data was changed, false otherwise</returns>
bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan<byte> data);
/// <summary>
/// Fills the specified memory region with the value specified in <paramref name="value"/>.
/// </summary>
/// <param name="va">Virtual address to fill the value into</param>
/// <param name="size">Size of the memory region to fill</param>
/// <param name="value">Value to fill with</param>
void Fill(ulong va, ulong size, byte value)
{
const int MaxChunkSize = 1 << 24;
@@ -194,6 +200,14 @@ namespace Ryujinx.Memory
/// <param name="exemptId">Optional ID of the handles that should not be signalled</param>
void SignalMemoryTracking(ulong va, ulong size, bool write, bool precise = false, int? exemptId = null);
/// <summary>
/// Reprotect a region of virtual memory for guest access.
/// </summary>
/// <param name="va">Virtual address base</param>
/// <param name="size">Size of the region to protect</param>
/// <param name="protection">Memory protection to set</param>
void Reprotect(ulong va, ulong size, MemoryPermission protection);
/// <summary>
/// Reprotect a region of virtual memory for tracking.
/// </summary>

View File

@@ -102,6 +102,11 @@ namespace Ryujinx.Tests.Memory
throw new NotImplementedException();
}
public void Reprotect(ulong va, ulong size, MemoryPermission protection)
{
throw new NotImplementedException();
}
public void TrackingReprotect(ulong va, ulong size, MemoryPermission protection)
{
OnProtect?.Invoke(va, size, protection);

View File

@@ -784,7 +784,7 @@ namespace Ryujinx.Ui.Common.Configuration
EnableDiscordIntegration.Value = true;
CheckUpdatesOnStart.Value = true;
ShowConfirmExit.Value = true;
HideCursor.Value = HideCursorMode.Never;
HideCursor.Value = HideCursorMode.OnIdle;
Graphics.EnableVsync.Value = true;
Graphics.EnableShaderCache.Value = true;
Graphics.EnableTextureRecompression.Value = false;

View File

@@ -30,7 +30,7 @@ namespace Ryujinx.Ui.Common.Helper
graphic.DrawImage(image, 0, 0, 128, 128);
SaveBitmapAsIcon(bitmap, iconPath);
var shortcut = Shortcut.CreateShortcut(basePath, GetArgsString(basePath, applicationFilePath), iconPath, 0);
var shortcut = Shortcut.CreateShortcut(basePath, GetArgsString(applicationFilePath), iconPath, 0);
shortcut.StringData.NameString = cleanedAppName;
shortcut.WriteToFile(Path.Combine(desktopPath, cleanedAppName + ".lnk"));
}
@@ -46,16 +46,16 @@ namespace Ryujinx.Ui.Common.Helper
image.SaveAsPng(iconPath);
using StreamWriter outputFile = new(Path.Combine(desktopPath, cleanedAppName + ".desktop"));
outputFile.Write(desktopFile, cleanedAppName, iconPath, GetArgsString(basePath, applicationFilePath));
outputFile.Write(desktopFile, cleanedAppName, iconPath, $"{basePath} {GetArgsString(applicationFilePath)}");
}
[SupportedOSPlatform("macos")]
private static void CreateShortcutMacos(string appFilePath, byte[] iconData, string desktopPath, string cleanedAppName)
{
string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AppDomain.CurrentDomain.FriendlyName);
string basePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx");
var plistFile = EmbeddedResources.ReadAllText("Ryujinx.Ui.Common/shortcut-template.plist");
// Macos .App folder
string contentFolderPath = Path.Combine(desktopPath, cleanedAppName + ".app", "Contents");
string contentFolderPath = Path.Combine("/Applications", cleanedAppName + ".app", "Contents");
string scriptFolderPath = Path.Combine(contentFolderPath, "MacOS");
if (!Directory.Exists(scriptFolderPath))
@@ -69,7 +69,7 @@ namespace Ryujinx.Ui.Common.Helper
using StreamWriter scriptFile = new(scriptPath);
scriptFile.WriteLine("#!/bin/sh");
scriptFile.WriteLine(GetArgsString(basePath, appFilePath));
scriptFile.WriteLine($"{basePath} {GetArgsString(appFilePath)}");
// Set execute permission
FileInfo fileInfo = new(scriptPath);
@@ -125,13 +125,10 @@ namespace Ryujinx.Ui.Common.Helper
throw new NotImplementedException("Shortcut support has not been implemented yet for this OS.");
}
private static string GetArgsString(string basePath, string appFilePath)
private static string GetArgsString(string appFilePath)
{
// args are first defined as a list, for easier adjustments in the future
var argsList = new List<string>
{
basePath,
};
var argsList = new List<string>();
if (!string.IsNullOrEmpty(CommandLineState.BaseDirPathArg))
{
@@ -141,7 +138,6 @@ namespace Ryujinx.Ui.Common.Helper
argsList.Add($"\"{appFilePath}\"");
return String.Join(" ", argsList);
}

View File

@@ -211,6 +211,8 @@ namespace Ryujinx.Ui.Widgets
_manageSubMenu.Append(_openPtcDirMenuItem);
_manageSubMenu.Append(_openShaderCacheDirMenuItem);
Add(_createShortcutMenuItem);
Add(new SeparatorMenuItem());
Add(_openSaveUserDirMenuItem);
Add(_openSaveDeviceDirMenuItem);
Add(_openSaveBcatDirMenuItem);
@@ -223,7 +225,6 @@ namespace Ryujinx.Ui.Widgets
Add(new SeparatorMenuItem());
Add(_manageCacheMenuItem);
Add(_extractMenuItem);
Add(_createShortcutMenuItem);
ShowAll();
}