Compare commits
14 Commits
Canary-1.2
...
Canary-1.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a72fb2088 | ||
|
|
0caeab2270 | ||
|
|
f72d2c1b2b | ||
|
|
a18cecbc30 | ||
|
|
2e6794e69b | ||
|
|
7e16fccfc1 | ||
|
|
a81212bbf1 | ||
|
|
e8d3ad4d8b | ||
|
|
3b6731a351 | ||
|
|
e653848a2c | ||
|
|
5534001152 | ||
|
|
e05875a079 | ||
|
|
49eeb26b6f | ||
|
|
f8d63f9a2f |
4
.github/workflows/canary.yml
vendored
4
.github/workflows/canary.yml
vendored
@@ -103,8 +103,8 @@ jobs:
|
|||||||
|
|
||||||
- name: Publish
|
- name: Publish
|
||||||
run: |
|
run: |
|
||||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained -p:IncludeNativeLibrariesForSelfExtract=true
|
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_ava/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained
|
||||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained -p:IncludeNativeLibrariesForSelfExtract=true
|
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless/publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained
|
||||||
|
|
||||||
- name: Packing Windows builds
|
- name: Packing Windows builds
|
||||||
if: matrix.platform.os == 'windows-latest'
|
if: matrix.platform.os == 'windows-latest'
|
||||||
|
|||||||
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@@ -102,8 +102,8 @@ jobs:
|
|||||||
|
|
||||||
- name: Publish
|
- name: Publish
|
||||||
run: |
|
run: |
|
||||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained -p:IncludeNativeLibrariesForSelfExtract=true
|
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx --self-contained
|
||||||
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained -p:IncludeNativeLibrariesForSelfExtract=true
|
dotnet publish -c Release -r "${{ matrix.platform.name }}" -o ./publish_sdl2_headless -p:Version="${{ steps.version_info.outputs.build_version }}" -p:SourceRevisionId="${{ steps.version_info.outputs.git_short_hash }}" -p:DebugType=embedded src/Ryujinx.Headless.SDL2 --self-contained
|
||||||
|
|
||||||
- name: Packing Windows builds
|
- name: Packing Windows builds
|
||||||
if: matrix.platform.os == 'windows-latest'
|
if: matrix.platform.os == 'windows-latest'
|
||||||
|
|||||||
@@ -38,7 +38,7 @@
|
|||||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" />
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.3-build14" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
|
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.30.0-build32" />
|
||||||
<PackageVersion Include="Gommon" Version="2.6.5" />
|
<PackageVersion Include="Gommon" Version="2.6.6" />
|
||||||
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
<PackageVersion Include="securifybv.ShellLink" Version="0.1.0" />
|
||||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||||
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
<PackageVersion Include="SharpZipLib" Version="1.4.2" />
|
||||||
|
|||||||
@@ -1,252 +0,0 @@
|
|||||||
using ARMeilleure.Diagnostics;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace ARMeilleure.Common
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a table of guest address to a value.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="TEntry">Type of the value</typeparam>
|
|
||||||
public unsafe class AddressTable<TEntry> : IDisposable where TEntry : unmanaged
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Represents a level in an <see cref="AddressTable{TEntry}"/>.
|
|
||||||
/// </summary>
|
|
||||||
public readonly struct Level
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the index of the <see cref="Level"/> in the guest address.
|
|
||||||
/// </summary>
|
|
||||||
public int Index { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the length of the <see cref="Level"/> in the guest address.
|
|
||||||
/// </summary>
|
|
||||||
public int Length { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the mask which masks the bits used by the <see cref="Level"/>.
|
|
||||||
/// </summary>
|
|
||||||
public ulong Mask => ((1ul << Length) - 1) << Index;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Initializes a new instance of the <see cref="Level"/> structure with the specified
|
|
||||||
/// <paramref name="index"/> and <paramref name="length"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="index">Index of the <see cref="Level"/></param>
|
|
||||||
/// <param name="length">Length of the <see cref="Level"/></param>
|
|
||||||
public Level(int index, int length)
|
|
||||||
{
|
|
||||||
(Index, Length) = (index, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the value of the <see cref="Level"/> from the specified guest <paramref name="address"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="address">Guest address</param>
|
|
||||||
/// <returns>Value of the <see cref="Level"/> from the specified guest <paramref name="address"/></returns>
|
|
||||||
public int GetValue(ulong address)
|
|
||||||
{
|
|
||||||
return (int)((address & Mask) >> Index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _disposed;
|
|
||||||
private TEntry** _table;
|
|
||||||
private readonly List<nint> _pages;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the bits used by the <see cref="Levels"/> of the <see cref="AddressTable{TEntry}"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
public ulong Mask { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the <see cref="Level"/>s used by the <see cref="AddressTable{TEntry}"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
public Level[] Levels { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the default fill value of newly created leaf pages.
|
|
||||||
/// </summary>
|
|
||||||
public TEntry Fill { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the base address of the <see cref="EntryTable{TEntry}"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
|
||||||
public nint Base
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
|
||||||
|
|
||||||
lock (_pages)
|
|
||||||
{
|
|
||||||
return (nint)GetRootPage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Constructs a new instance of the <see cref="AddressTable{TEntry}"/> class with the specified list of
|
|
||||||
/// <see cref="Level"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="levels"/> is null</exception>
|
|
||||||
/// <exception cref="ArgumentException">Length of <paramref name="levels"/> is less than 2</exception>
|
|
||||||
public AddressTable(Level[] levels)
|
|
||||||
{
|
|
||||||
ArgumentNullException.ThrowIfNull(levels);
|
|
||||||
|
|
||||||
if (levels.Length < 2)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Table must be at least 2 levels deep.", nameof(levels));
|
|
||||||
}
|
|
||||||
|
|
||||||
_pages = new List<nint>(capacity: 16);
|
|
||||||
|
|
||||||
Levels = levels;
|
|
||||||
Mask = 0;
|
|
||||||
|
|
||||||
foreach (var level in Levels)
|
|
||||||
{
|
|
||||||
Mask |= level.Mask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines if the specified <paramref name="address"/> is in the range of the
|
|
||||||
/// <see cref="AddressTable{TEntry}"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="address">Guest address</param>
|
|
||||||
/// <returns><see langword="true"/> if is valid; otherwise <see langword="false"/></returns>
|
|
||||||
public bool IsValid(ulong address)
|
|
||||||
{
|
|
||||||
return (address & ~Mask) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a reference to the value at the specified guest <paramref name="address"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="address">Guest address</param>
|
|
||||||
/// <returns>Reference to the value at the specified guest <paramref name="address"/></returns>
|
|
||||||
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
|
||||||
/// <exception cref="ArgumentException"><paramref name="address"/> is not mapped</exception>
|
|
||||||
public ref TEntry GetValue(ulong address)
|
|
||||||
{
|
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
|
||||||
|
|
||||||
if (!IsValid(address))
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Address 0x{address:X} is not mapped onto the table.", nameof(address));
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_pages)
|
|
||||||
{
|
|
||||||
return ref GetPage(address)[Levels[^1].GetValue(address)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the leaf page for the specified guest <paramref name="address"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="address">Guest address</param>
|
|
||||||
/// <returns>Leaf page for the specified guest <paramref name="address"/></returns>
|
|
||||||
private TEntry* GetPage(ulong address)
|
|
||||||
{
|
|
||||||
TEntry** page = GetRootPage();
|
|
||||||
|
|
||||||
for (int i = 0; i < Levels.Length - 1; i++)
|
|
||||||
{
|
|
||||||
ref Level level = ref Levels[i];
|
|
||||||
ref TEntry* nextPage = ref page[level.GetValue(address)];
|
|
||||||
|
|
||||||
if (nextPage == null)
|
|
||||||
{
|
|
||||||
ref Level nextLevel = ref Levels[i + 1];
|
|
||||||
|
|
||||||
nextPage = i == Levels.Length - 2 ?
|
|
||||||
(TEntry*)Allocate(1 << nextLevel.Length, Fill, leaf: true) :
|
|
||||||
(TEntry*)Allocate(1 << nextLevel.Length, nint.Zero, leaf: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
page = (TEntry**)nextPage;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (TEntry*)page;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Lazily initialize and get the root page of the <see cref="AddressTable{TEntry}"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Root page of the <see cref="AddressTable{TEntry}"/></returns>
|
|
||||||
private TEntry** GetRootPage()
|
|
||||||
{
|
|
||||||
if (_table == null)
|
|
||||||
{
|
|
||||||
_table = (TEntry**)Allocate(1 << Levels[0].Length, fill: nint.Zero, leaf: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _table;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Allocates a block of memory of the specified type and length.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">Type of elements</typeparam>
|
|
||||||
/// <param name="length">Number of elements</param>
|
|
||||||
/// <param name="fill">Fill value</param>
|
|
||||||
/// <param name="leaf"><see langword="true"/> if leaf; otherwise <see langword="false"/></param>
|
|
||||||
/// <returns>Allocated block</returns>
|
|
||||||
private nint Allocate<T>(int length, T fill, bool leaf) where T : unmanaged
|
|
||||||
{
|
|
||||||
var size = sizeof(T) * length;
|
|
||||||
var page = (nint)NativeAllocator.Instance.Allocate((uint)size);
|
|
||||||
var span = new Span<T>((void*)page, length);
|
|
||||||
|
|
||||||
span.Fill(fill);
|
|
||||||
|
|
||||||
_pages.Add(page);
|
|
||||||
|
|
||||||
TranslatorEventSource.Log.AddressTableAllocated(size, leaf);
|
|
||||||
|
|
||||||
return page;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Releases all resources used by the <see cref="AddressTable{TEntry}"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Releases all unmanaged and optionally managed resources used by the <see cref="AddressTable{TEntry}"/>
|
|
||||||
/// instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param>
|
|
||||||
protected virtual void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (!_disposed)
|
|
||||||
{
|
|
||||||
foreach (var page in _pages)
|
|
||||||
{
|
|
||||||
Marshal.FreeHGlobal(page);
|
|
||||||
}
|
|
||||||
|
|
||||||
_disposed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Frees resources used by the <see cref="AddressTable{TEntry}"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
~AddressTable()
|
|
||||||
{
|
|
||||||
Dispose(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
44
src/ARMeilleure/Common/AddressTableLevel.cs
Normal file
44
src/ARMeilleure/Common/AddressTableLevel.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
namespace ARMeilleure.Common
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a level in an <see cref="IAddressTable{TEntry}"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly struct AddressTableLevel
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the index of the <see cref="Level"/> in the guest address.
|
||||||
|
/// </summary>
|
||||||
|
public int Index { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the length of the <see cref="AddressTableLevel"/> in the guest address.
|
||||||
|
/// </summary>
|
||||||
|
public int Length { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the mask which masks the bits used by the <see cref="AddressTableLevel"/>.
|
||||||
|
/// </summary>
|
||||||
|
public ulong Mask => ((1ul << Length) - 1) << Index;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="AddressTableLevel"/> structure with the specified
|
||||||
|
/// <paramref name="index"/> and <paramref name="length"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="index">Index of the <see cref="AddressTableLevel"/></param>
|
||||||
|
/// <param name="length">Length of the <see cref="AddressTableLevel"/></param>
|
||||||
|
public AddressTableLevel(int index, int length)
|
||||||
|
{
|
||||||
|
(Index, Length) = (index, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the value of the <see cref="AddressTableLevel"/> from the specified guest <paramref name="address"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">Guest address</param>
|
||||||
|
/// <returns>Value of the <see cref="AddressTableLevel"/> from the specified guest <paramref name="address"/></returns>
|
||||||
|
public int GetValue(ulong address)
|
||||||
|
{
|
||||||
|
return (int)((address & Mask) >> Index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
75
src/ARMeilleure/Common/AddressTablePresets.cs
Normal file
75
src/ARMeilleure/Common/AddressTablePresets.cs
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
namespace ARMeilleure.Common
|
||||||
|
{
|
||||||
|
public static class AddressTablePresets
|
||||||
|
{
|
||||||
|
private static readonly AddressTableLevel[] _levels64Bit =
|
||||||
|
new AddressTableLevel[]
|
||||||
|
{
|
||||||
|
new(31, 17),
|
||||||
|
new(23, 8),
|
||||||
|
new(15, 8),
|
||||||
|
new( 7, 8),
|
||||||
|
new( 2, 5),
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly AddressTableLevel[] _levels32Bit =
|
||||||
|
new AddressTableLevel[]
|
||||||
|
{
|
||||||
|
new(31, 17),
|
||||||
|
new(23, 8),
|
||||||
|
new(15, 8),
|
||||||
|
new( 7, 8),
|
||||||
|
new( 1, 6),
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly AddressTableLevel[] _levels64BitSparseTiny =
|
||||||
|
new AddressTableLevel[]
|
||||||
|
{
|
||||||
|
new( 11, 28),
|
||||||
|
new( 2, 9),
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly AddressTableLevel[] _levels32BitSparseTiny =
|
||||||
|
new AddressTableLevel[]
|
||||||
|
{
|
||||||
|
new( 10, 22),
|
||||||
|
new( 1, 9),
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly AddressTableLevel[] _levels64BitSparseGiant =
|
||||||
|
new AddressTableLevel[]
|
||||||
|
{
|
||||||
|
new( 38, 1),
|
||||||
|
new( 2, 36),
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly AddressTableLevel[] _levels32BitSparseGiant =
|
||||||
|
new AddressTableLevel[]
|
||||||
|
{
|
||||||
|
new( 31, 1),
|
||||||
|
new( 1, 30),
|
||||||
|
};
|
||||||
|
|
||||||
|
//high power will run worse on DDR3 systems and some DDR4 systems due to the higher ram utilization
|
||||||
|
//low power will never run worse than non-sparse, but for most systems it won't be necessary
|
||||||
|
//high power is always used, but I've left low power in here for future reference
|
||||||
|
public static AddressTableLevel[] GetArmPreset(bool for64Bits, bool sparse, bool lowPower = false)
|
||||||
|
{
|
||||||
|
if (sparse)
|
||||||
|
{
|
||||||
|
if (lowPower)
|
||||||
|
{
|
||||||
|
return for64Bits ? _levels64BitSparseTiny : _levels32BitSparseTiny;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return for64Bits ? _levels64BitSparseGiant : _levels32BitSparseGiant;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return for64Bits ? _levels64Bit : _levels32Bit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ using System;
|
|||||||
|
|
||||||
namespace ARMeilleure.Common
|
namespace ARMeilleure.Common
|
||||||
{
|
{
|
||||||
unsafe abstract class Allocator : IDisposable
|
public unsafe abstract class Allocator : IDisposable
|
||||||
{
|
{
|
||||||
public T* Allocate<T>(ulong count = 1) where T : unmanaged
|
public T* Allocate<T>(ulong count = 1) where T : unmanaged
|
||||||
{
|
{
|
||||||
|
|||||||
51
src/ARMeilleure/Common/IAddressTable.cs
Normal file
51
src/ARMeilleure/Common/IAddressTable.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace ARMeilleure.Common
|
||||||
|
{
|
||||||
|
public interface IAddressTable<TEntry> : IDisposable where TEntry : unmanaged
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// True if the address table's bottom level is sparsely mapped.
|
||||||
|
/// This also ensures the second bottom level is filled with a dummy page rather than 0.
|
||||||
|
/// </summary>
|
||||||
|
bool Sparse { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the bits used by the <see cref="Levels"/> of the <see cref="IAddressTable{TEntry}"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
ulong Mask { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the <see cref="AddressTableLevel"/>s used by the <see cref="IAddressTable{TEntry}"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
AddressTableLevel[] Levels { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the default fill value of newly created leaf pages.
|
||||||
|
/// </summary>
|
||||||
|
TEntry Fill { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the base address of the <see cref="EntryTable{TEntry}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
||||||
|
nint Base { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines if the specified <paramref name="address"/> is in the range of the
|
||||||
|
/// <see cref="IAddressTable{TEntry}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">Guest address</param>
|
||||||
|
/// <returns><see langword="true"/> if is valid; otherwise <see langword="false"/></returns>
|
||||||
|
bool IsValid(ulong address);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a reference to the value at the specified guest <paramref name="address"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">Guest address</param>
|
||||||
|
/// <returns>Reference to the value at the specified guest <paramref name="address"/></returns>
|
||||||
|
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
||||||
|
/// <exception cref="ArgumentException"><paramref name="address"/> is not mapped</exception>
|
||||||
|
ref TEntry GetValue(ulong address);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
|
|||||||
|
|
||||||
namespace ARMeilleure.Common
|
namespace ARMeilleure.Common
|
||||||
{
|
{
|
||||||
unsafe sealed class NativeAllocator : Allocator
|
public unsafe sealed class NativeAllocator : Allocator
|
||||||
{
|
{
|
||||||
public static NativeAllocator Instance { get; } = new();
|
public static NativeAllocator Instance { get; } = new();
|
||||||
|
|
||||||
|
|||||||
@@ -193,6 +193,8 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
Operand hostAddress;
|
Operand hostAddress;
|
||||||
|
|
||||||
|
var table = context.FunctionTable;
|
||||||
|
|
||||||
// If address is mapped onto the function table, we can skip the table walk. Otherwise we fallback
|
// If address is mapped onto the function table, we can skip the table walk. Otherwise we fallback
|
||||||
// onto the dispatch stub.
|
// onto the dispatch stub.
|
||||||
if (guestAddress.Kind == OperandKind.Constant && context.FunctionTable.IsValid(guestAddress.Value))
|
if (guestAddress.Kind == OperandKind.Constant && context.FunctionTable.IsValid(guestAddress.Value))
|
||||||
@@ -203,6 +205,30 @@ namespace ARMeilleure.Instructions
|
|||||||
|
|
||||||
hostAddress = context.Load(OperandType.I64, hostAddressAddr);
|
hostAddress = context.Load(OperandType.I64, hostAddressAddr);
|
||||||
}
|
}
|
||||||
|
else if (table.Sparse)
|
||||||
|
{
|
||||||
|
// Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels.
|
||||||
|
// Deliberately attempts to avoid branches.
|
||||||
|
|
||||||
|
Operand tableBase = !context.HasPtc ?
|
||||||
|
Const(table.Base) :
|
||||||
|
Const(table.Base, Ptc.FunctionTableSymbol);
|
||||||
|
|
||||||
|
hostAddress = tableBase;
|
||||||
|
|
||||||
|
for (int i = 0; i < table.Levels.Length; i++)
|
||||||
|
{
|
||||||
|
var level = table.Levels[i];
|
||||||
|
int clearBits = 64 - (level.Index + level.Length);
|
||||||
|
|
||||||
|
Operand index = context.ShiftLeft(
|
||||||
|
context.ShiftRightUI(context.ShiftLeft(guestAddress, Const(clearBits)), Const(clearBits + level.Index)),
|
||||||
|
Const(3)
|
||||||
|
);
|
||||||
|
|
||||||
|
hostAddress = context.Load(OperandType.I64, context.Add(hostAddress, index));
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
hostAddress = !context.HasPtc ?
|
hostAddress = !context.HasPtc ?
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace ARMeilleure.Signal
|
|||||||
{
|
{
|
||||||
public static class NativeSignalHandlerGenerator
|
public static class NativeSignalHandlerGenerator
|
||||||
{
|
{
|
||||||
public const int MaxTrackedRanges = 8;
|
public const int MaxTrackedRanges = 16;
|
||||||
|
|
||||||
private const int StructAddressOffset = 0;
|
private const int StructAddressOffset = 0;
|
||||||
private const int StructWriteOffset = 4;
|
private const int StructWriteOffset = 4;
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ namespace ARMeilleure.Translation
|
|||||||
public IMemoryManager Memory { get; }
|
public IMemoryManager Memory { get; }
|
||||||
|
|
||||||
public EntryTable<uint> CountTable { get; }
|
public EntryTable<uint> CountTable { get; }
|
||||||
public AddressTable<ulong> FunctionTable { get; }
|
public IAddressTable<ulong> FunctionTable { get; }
|
||||||
public TranslatorStubs Stubs { get; }
|
public TranslatorStubs Stubs { get; }
|
||||||
|
|
||||||
public ulong EntryAddress { get; }
|
public ulong EntryAddress { get; }
|
||||||
@@ -62,7 +62,7 @@ namespace ARMeilleure.Translation
|
|||||||
public ArmEmitterContext(
|
public ArmEmitterContext(
|
||||||
IMemoryManager memory,
|
IMemoryManager memory,
|
||||||
EntryTable<uint> countTable,
|
EntryTable<uint> countTable,
|
||||||
AddressTable<ulong> funcTable,
|
IAddressTable<ulong> funcTable,
|
||||||
TranslatorStubs stubs,
|
TranslatorStubs stubs,
|
||||||
ulong entryAddress,
|
ulong entryAddress,
|
||||||
bool highCq,
|
bool highCq,
|
||||||
|
|||||||
@@ -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 = 6950; //! To be incremented manually for each change to the ARMeilleure project.
|
private const uint InternalVersion = 6992; //! 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";
|
||||||
@@ -41,6 +41,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
public static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1);
|
public static readonly Symbol PageTableSymbol = new(SymbolType.Special, 1);
|
||||||
public static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2);
|
public static readonly Symbol CountTableSymbol = new(SymbolType.Special, 2);
|
||||||
public static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3);
|
public static readonly Symbol DispatchStubSymbol = new(SymbolType.Special, 3);
|
||||||
|
public static readonly Symbol FunctionTableSymbol = new(SymbolType.Special, 4);
|
||||||
|
|
||||||
private const byte FillingByte = 0x00;
|
private const byte FillingByte = 0x00;
|
||||||
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
|
private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
|
||||||
@@ -101,7 +102,7 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
Disable();
|
Disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerType memoryMode)
|
public void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerType memoryMode, string cacheSelector)
|
||||||
{
|
{
|
||||||
Wait();
|
Wait();
|
||||||
|
|
||||||
@@ -127,6 +128,8 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
DisplayVersion = !string.IsNullOrEmpty(displayVersion) ? displayVersion : DisplayVersionDefault;
|
DisplayVersion = !string.IsNullOrEmpty(displayVersion) ? displayVersion : DisplayVersionDefault;
|
||||||
_memoryMode = memoryMode;
|
_memoryMode = memoryMode;
|
||||||
|
|
||||||
|
Logger.Info?.Print(LogClass.Ptc, $"PPTC (v{InternalVersion}) Profile: {DisplayVersion}-{cacheSelector}");
|
||||||
|
|
||||||
string workPathActual = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", ActualDir);
|
string workPathActual = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", ActualDir);
|
||||||
string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", BackupDir);
|
string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", BackupDir);
|
||||||
|
|
||||||
@@ -140,8 +143,8 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
Directory.CreateDirectory(workPathBackup);
|
Directory.CreateDirectory(workPathBackup);
|
||||||
}
|
}
|
||||||
|
|
||||||
CachePathActual = Path.Combine(workPathActual, DisplayVersion);
|
CachePathActual = Path.Combine(workPathActual, DisplayVersion) + "-" + cacheSelector;
|
||||||
CachePathBackup = Path.Combine(workPathBackup, DisplayVersion);
|
CachePathBackup = Path.Combine(workPathBackup, DisplayVersion) + "-" + cacheSelector;
|
||||||
|
|
||||||
PreLoad();
|
PreLoad();
|
||||||
Profiler.PreLoad();
|
Profiler.PreLoad();
|
||||||
@@ -706,6 +709,10 @@ namespace ARMeilleure.Translation.PTC
|
|||||||
{
|
{
|
||||||
imm = translator.Stubs.DispatchStub;
|
imm = translator.Stubs.DispatchStub;
|
||||||
}
|
}
|
||||||
|
else if (symbol == FunctionTableSymbol)
|
||||||
|
{
|
||||||
|
imm = translator.FunctionTable.Base;
|
||||||
|
}
|
||||||
|
|
||||||
if (imm == null)
|
if (imm == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -22,33 +22,13 @@ namespace ARMeilleure.Translation
|
|||||||
{
|
{
|
||||||
public class Translator
|
public class Translator
|
||||||
{
|
{
|
||||||
private static readonly AddressTable<ulong>.Level[] _levels64Bit =
|
|
||||||
new AddressTable<ulong>.Level[]
|
|
||||||
{
|
|
||||||
new(31, 17),
|
|
||||||
new(23, 8),
|
|
||||||
new(15, 8),
|
|
||||||
new( 7, 8),
|
|
||||||
new( 2, 5),
|
|
||||||
};
|
|
||||||
|
|
||||||
private static readonly AddressTable<ulong>.Level[] _levels32Bit =
|
|
||||||
new AddressTable<ulong>.Level[]
|
|
||||||
{
|
|
||||||
new(31, 17),
|
|
||||||
new(23, 8),
|
|
||||||
new(15, 8),
|
|
||||||
new( 7, 8),
|
|
||||||
new( 1, 6),
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly IJitMemoryAllocator _allocator;
|
private readonly IJitMemoryAllocator _allocator;
|
||||||
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
||||||
|
|
||||||
private readonly Ptc _ptc;
|
private readonly Ptc _ptc;
|
||||||
|
|
||||||
internal TranslatorCache<TranslatedFunction> Functions { get; }
|
internal TranslatorCache<TranslatedFunction> Functions { get; }
|
||||||
internal AddressTable<ulong> FunctionTable { get; }
|
internal IAddressTable<ulong> FunctionTable { get; }
|
||||||
internal EntryTable<uint> CountTable { get; }
|
internal EntryTable<uint> CountTable { get; }
|
||||||
internal TranslatorStubs Stubs { get; }
|
internal TranslatorStubs Stubs { get; }
|
||||||
internal TranslatorQueue Queue { get; }
|
internal TranslatorQueue Queue { get; }
|
||||||
@@ -57,7 +37,7 @@ namespace ARMeilleure.Translation
|
|||||||
private Thread[] _backgroundTranslationThreads;
|
private Thread[] _backgroundTranslationThreads;
|
||||||
private volatile int _threadCount;
|
private volatile int _threadCount;
|
||||||
|
|
||||||
public Translator(IJitMemoryAllocator allocator, IMemoryManager memory, bool for64Bits)
|
public Translator(IJitMemoryAllocator allocator, IMemoryManager memory, IAddressTable<ulong> functionTable)
|
||||||
{
|
{
|
||||||
_allocator = allocator;
|
_allocator = allocator;
|
||||||
Memory = memory;
|
Memory = memory;
|
||||||
@@ -72,15 +52,15 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
CountTable = new EntryTable<uint>();
|
CountTable = new EntryTable<uint>();
|
||||||
Functions = new TranslatorCache<TranslatedFunction>();
|
Functions = new TranslatorCache<TranslatedFunction>();
|
||||||
FunctionTable = new AddressTable<ulong>(for64Bits ? _levels64Bit : _levels32Bit);
|
FunctionTable = functionTable;
|
||||||
Stubs = new TranslatorStubs(FunctionTable);
|
Stubs = new TranslatorStubs(FunctionTable);
|
||||||
|
|
||||||
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
||||||
{
|
{
|
||||||
_ptc.Initialize(titleIdText, displayVersion, enabled, Memory.Type);
|
_ptc.Initialize(titleIdText, displayVersion, enabled, Memory.Type, cacheSelector);
|
||||||
return _ptc;
|
return _ptc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ namespace ARMeilleure.Translation
|
|||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
private readonly AddressTable<ulong> _functionTable;
|
private readonly IAddressTable<ulong> _functionTable;
|
||||||
private readonly Lazy<nint> _dispatchStub;
|
private readonly Lazy<nint> _dispatchStub;
|
||||||
private readonly Lazy<DispatcherFunction> _dispatchLoop;
|
private readonly Lazy<DispatcherFunction> _dispatchLoop;
|
||||||
private readonly Lazy<WrapperFunction> _contextWrapper;
|
private readonly Lazy<WrapperFunction> _contextWrapper;
|
||||||
@@ -86,7 +86,7 @@ namespace ARMeilleure.Translation
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="functionTable">Function table used to store pointers to the functions that the guest code will call</param>
|
/// <param name="functionTable">Function table used to store pointers to the functions that the guest code will call</param>
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
||||||
public TranslatorStubs(AddressTable<ulong> functionTable)
|
public TranslatorStubs(IAddressTable<ulong> functionTable)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(functionTable);
|
ArgumentNullException.ThrowIfNull(functionTable);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ namespace Ryujinx.Common.Configuration.Hid
|
|||||||
{
|
{
|
||||||
public class KeyboardHotkeys
|
public class KeyboardHotkeys
|
||||||
{
|
{
|
||||||
public Key ToggleVsync { get; set; }
|
public Key ToggleVSyncMode { get; set; }
|
||||||
public Key Screenshot { get; set; }
|
public Key Screenshot { get; set; }
|
||||||
public Key ShowUI { get; set; }
|
public Key ShowUI { get; set; }
|
||||||
public Key Pause { get; set; }
|
public Key Pause { get; set; }
|
||||||
@@ -11,5 +11,7 @@ namespace Ryujinx.Common.Configuration.Hid
|
|||||||
public Key ResScaleDown { get; set; }
|
public Key ResScaleDown { get; set; }
|
||||||
public Key VolumeUp { get; set; }
|
public Key VolumeUp { get; set; }
|
||||||
public Key VolumeDown { get; set; }
|
public Key VolumeDown { get; set; }
|
||||||
|
public Key CustomVSyncIntervalIncrement { get; set; }
|
||||||
|
public Key CustomVSyncIntervalDecrement { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
9
src/Ryujinx.Common/Configuration/VSyncMode.cs
Normal file
9
src/Ryujinx.Common/Configuration/VSyncMode.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
|
{
|
||||||
|
public enum VSyncMode
|
||||||
|
{
|
||||||
|
Switch,
|
||||||
|
Unbounded,
|
||||||
|
Custom
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Ryujinx.Common
|
namespace Ryujinx.Common
|
||||||
@@ -35,5 +36,13 @@ namespace Ryujinx.Common
|
|||||||
public static bool IsReleaseBuild => IsValid && ReleaseChannelName.Equals(ReleaseChannel);
|
public static bool IsReleaseBuild => IsValid && ReleaseChannelName.Equals(ReleaseChannel);
|
||||||
|
|
||||||
public static string Version => IsValid ? BuildVersion : Assembly.GetEntryAssembly()!.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
|
public static string Version => IsValid ? BuildVersion : Assembly.GetEntryAssembly()!.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
|
||||||
|
|
||||||
|
public static string GetChangelogUrl(Version currentVersion, Version newVersion) =>
|
||||||
|
IsCanaryBuild
|
||||||
|
? $"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelSourceRepo}/compare/Canary-{currentVersion}...Canary-{newVersion}"
|
||||||
|
: $"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelSourceRepo}/releases/tag/{newVersion}";
|
||||||
|
|
||||||
|
public static string GetChangelogForVersion(Version version) =>
|
||||||
|
$"https://github.com/{ReleaseChannelOwner}/{ReleaseChannelRepo}/releases/tag/{version}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
482
src/Ryujinx.Cpu/AddressTable.cs
Normal file
482
src/Ryujinx.Cpu/AddressTable.cs
Normal file
@@ -0,0 +1,482 @@
|
|||||||
|
using ARMeilleure.Memory;
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Cpu.Signal;
|
||||||
|
using Ryujinx.Memory;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
using static Ryujinx.Cpu.MemoryEhMeilleure;
|
||||||
|
|
||||||
|
namespace ARMeilleure.Common
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a table of guest address to a value.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TEntry">Type of the value</typeparam>
|
||||||
|
public unsafe class AddressTable<TEntry> : IAddressTable<TEntry> where TEntry : unmanaged
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a page of the address table.
|
||||||
|
/// </summary>
|
||||||
|
private readonly struct AddressTablePage
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// True if the allocation belongs to a sparse block, false otherwise.
|
||||||
|
/// </summary>
|
||||||
|
public readonly bool IsSparse;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Base address for the page.
|
||||||
|
/// </summary>
|
||||||
|
public readonly IntPtr Address;
|
||||||
|
|
||||||
|
public AddressTablePage(bool isSparse, IntPtr address)
|
||||||
|
{
|
||||||
|
IsSparse = isSparse;
|
||||||
|
Address = address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A sparsely mapped block of memory with a signal handler to map pages as they're accessed.
|
||||||
|
/// </summary>
|
||||||
|
private readonly struct TableSparseBlock : IDisposable
|
||||||
|
{
|
||||||
|
public readonly SparseMemoryBlock Block;
|
||||||
|
private readonly TrackingEventDelegate _trackingEvent;
|
||||||
|
|
||||||
|
public TableSparseBlock(ulong size, Action<IntPtr> ensureMapped, PageInitDelegate pageInit)
|
||||||
|
{
|
||||||
|
var block = new SparseMemoryBlock(size, pageInit, null);
|
||||||
|
|
||||||
|
_trackingEvent = (ulong address, ulong size, bool write) =>
|
||||||
|
{
|
||||||
|
ulong pointer = (ulong)block.Block.Pointer + address;
|
||||||
|
ensureMapped((IntPtr)pointer);
|
||||||
|
return pointer;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool added = NativeSignalHandler.AddTrackedRegion(
|
||||||
|
(nuint)block.Block.Pointer,
|
||||||
|
(nuint)(block.Block.Pointer + (IntPtr)block.Block.Size),
|
||||||
|
Marshal.GetFunctionPointerForDelegate(_trackingEvent));
|
||||||
|
|
||||||
|
if (!added)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Number of allowed tracked regions exceeded.");
|
||||||
|
}
|
||||||
|
|
||||||
|
Block = block;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
NativeSignalHandler.RemoveTrackedRegion((nuint)Block.Block.Pointer);
|
||||||
|
|
||||||
|
Block.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _disposed;
|
||||||
|
private TEntry** _table;
|
||||||
|
private readonly List<AddressTablePage> _pages;
|
||||||
|
private TEntry _fill;
|
||||||
|
|
||||||
|
private readonly MemoryBlock _sparseFill;
|
||||||
|
private readonly SparseMemoryBlock _fillBottomLevel;
|
||||||
|
private readonly TEntry* _fillBottomLevelPtr;
|
||||||
|
|
||||||
|
private readonly List<TableSparseBlock> _sparseReserved;
|
||||||
|
private readonly ReaderWriterLockSlim _sparseLock;
|
||||||
|
|
||||||
|
private ulong _sparseBlockSize;
|
||||||
|
private ulong _sparseReservedOffset;
|
||||||
|
|
||||||
|
public bool Sparse { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ulong Mask { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public AddressTableLevel[] Levels { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public TEntry Fill
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return _fill;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
UpdateFill(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public IntPtr Base
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
|
||||||
|
lock (_pages)
|
||||||
|
{
|
||||||
|
return (IntPtr)GetRootPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Constructs a new instance of the <see cref="AddressTable{TEntry}"/> class with the specified list of
|
||||||
|
/// <see cref="Level"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="levels">Levels for the address table</param>
|
||||||
|
/// <param name="sparse">True if the bottom page should be sparsely mapped</param>
|
||||||
|
/// <exception cref="ArgumentNullException"><paramref name="levels"/> is null</exception>
|
||||||
|
/// <exception cref="ArgumentException">Length of <paramref name="levels"/> is less than 2</exception>
|
||||||
|
public AddressTable(AddressTableLevel[] levels, bool sparse)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(levels);
|
||||||
|
|
||||||
|
_pages = new List<AddressTablePage>(capacity: 16);
|
||||||
|
|
||||||
|
Levels = levels;
|
||||||
|
Mask = 0;
|
||||||
|
|
||||||
|
foreach (var level in Levels)
|
||||||
|
{
|
||||||
|
Mask |= level.Mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sparse = sparse;
|
||||||
|
|
||||||
|
if (sparse)
|
||||||
|
{
|
||||||
|
// If the address table is sparse, allocate a fill block
|
||||||
|
|
||||||
|
_sparseFill = new MemoryBlock(268435456ul, MemoryAllocationFlags.Mirrorable); //low Power TC uses size: 65536ul
|
||||||
|
|
||||||
|
ulong bottomLevelSize = (1ul << levels.Last().Length) * (ulong)sizeof(TEntry);
|
||||||
|
|
||||||
|
_fillBottomLevel = new SparseMemoryBlock(bottomLevelSize, null, _sparseFill);
|
||||||
|
_fillBottomLevelPtr = (TEntry*)_fillBottomLevel.Block.Pointer;
|
||||||
|
|
||||||
|
_sparseReserved = new List<TableSparseBlock>();
|
||||||
|
_sparseLock = new ReaderWriterLockSlim();
|
||||||
|
|
||||||
|
_sparseBlockSize = bottomLevelSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create an <see cref="AddressTable{TEntry}"/> instance for an ARM function table.
|
||||||
|
/// Selects the best table structure for A32/A64, taking into account the selected memory manager type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="for64Bits">True if the guest is A64, false otherwise</param>
|
||||||
|
/// <param name="type">Memory manager type</param>
|
||||||
|
/// <returns>An <see cref="AddressTable{TEntry}"/> for ARM function lookup</returns>
|
||||||
|
public static AddressTable<TEntry> CreateForArm(bool for64Bits, MemoryManagerType type)
|
||||||
|
{
|
||||||
|
// Assume software memory means that we don't want to use any signal handlers.
|
||||||
|
bool sparse = type != MemoryManagerType.SoftwareMmu && type != MemoryManagerType.SoftwarePageTable;
|
||||||
|
|
||||||
|
return new AddressTable<TEntry>(AddressTablePresets.GetArmPreset(for64Bits, sparse), sparse);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update the fill value for the bottom level of the table.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fillValue">New fill value</param>
|
||||||
|
private void UpdateFill(TEntry fillValue)
|
||||||
|
{
|
||||||
|
if (_sparseFill != null)
|
||||||
|
{
|
||||||
|
Span<byte> span = _sparseFill.GetSpan(0, (int)_sparseFill.Size);
|
||||||
|
MemoryMarshal.Cast<byte, TEntry>(span).Fill(fillValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
_fill = fillValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Signal that the given code range exists.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address"></param>
|
||||||
|
/// <param name="size"></param>
|
||||||
|
public void SignalCodeRange(ulong address, ulong size)
|
||||||
|
{
|
||||||
|
AddressTableLevel bottom = Levels.Last();
|
||||||
|
ulong bottomLevelEntries = 1ul << bottom.Length;
|
||||||
|
|
||||||
|
ulong entryIndex = address >> bottom.Index;
|
||||||
|
ulong entries = size >> bottom.Index;
|
||||||
|
entries += entryIndex - BitUtils.AlignDown(entryIndex, bottomLevelEntries);
|
||||||
|
|
||||||
|
_sparseBlockSize = Math.Max(_sparseBlockSize, BitUtils.AlignUp(entries, bottomLevelEntries) * (ulong)sizeof(TEntry));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool IsValid(ulong address)
|
||||||
|
{
|
||||||
|
return (address & ~Mask) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public ref TEntry GetValue(ulong address)
|
||||||
|
{
|
||||||
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
|
||||||
|
if (!IsValid(address))
|
||||||
|
{
|
||||||
|
throw new ArgumentException($"Address 0x{address:X} is not mapped onto the table.", nameof(address));
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_pages)
|
||||||
|
{
|
||||||
|
TEntry* page = GetPage(address);
|
||||||
|
|
||||||
|
int index = Levels[^1].GetValue(address);
|
||||||
|
|
||||||
|
EnsureMapped((IntPtr)(page + index));
|
||||||
|
|
||||||
|
return ref page[index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the leaf page for the specified guest <paramref name="address"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">Guest address</param>
|
||||||
|
/// <returns>Leaf page for the specified guest <paramref name="address"/></returns>
|
||||||
|
private TEntry* GetPage(ulong address)
|
||||||
|
{
|
||||||
|
TEntry** page = GetRootPage();
|
||||||
|
|
||||||
|
for (int i = 0; i < Levels.Length - 1; i++)
|
||||||
|
{
|
||||||
|
ref AddressTableLevel level = ref Levels[i];
|
||||||
|
ref TEntry* nextPage = ref page[level.GetValue(address)];
|
||||||
|
|
||||||
|
if (nextPage == null || nextPage == _fillBottomLevelPtr)
|
||||||
|
{
|
||||||
|
ref AddressTableLevel nextLevel = ref Levels[i + 1];
|
||||||
|
|
||||||
|
if (i == Levels.Length - 2)
|
||||||
|
{
|
||||||
|
nextPage = (TEntry*)Allocate(1 << nextLevel.Length, Fill, leaf: true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nextPage = (TEntry*)Allocate(1 << nextLevel.Length, GetFillValue(i), leaf: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
page = (TEntry**)nextPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (TEntry*)page;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensure the given pointer is mapped in any overlapping sparse reservations.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ptr">Pointer to be mapped</param>
|
||||||
|
private void EnsureMapped(IntPtr ptr)
|
||||||
|
{
|
||||||
|
if (Sparse)
|
||||||
|
{
|
||||||
|
// Check sparse allocations to see if the pointer is in any of them.
|
||||||
|
// Ensure the page is committed if there's a match.
|
||||||
|
|
||||||
|
_sparseLock.EnterReadLock();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (TableSparseBlock reserved in _sparseReserved)
|
||||||
|
{
|
||||||
|
SparseMemoryBlock sparse = reserved.Block;
|
||||||
|
|
||||||
|
if (ptr >= sparse.Block.Pointer && ptr < sparse.Block.Pointer + (IntPtr)sparse.Block.Size)
|
||||||
|
{
|
||||||
|
sparse.EnsureMapped((ulong)(ptr - sparse.Block.Pointer));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_sparseLock.ExitReadLock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the fill value for a non-leaf level of the table.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="level">Level to get the fill value for</param>
|
||||||
|
/// <returns>The fill value</returns>
|
||||||
|
private IntPtr GetFillValue(int level)
|
||||||
|
{
|
||||||
|
if (_fillBottomLevel != null && level == Levels.Length - 2)
|
||||||
|
{
|
||||||
|
return (IntPtr)_fillBottomLevelPtr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return IntPtr.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Lazily initialize and get the root page of the <see cref="AddressTable{TEntry}"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Root page of the <see cref="AddressTable{TEntry}"/></returns>
|
||||||
|
private TEntry** GetRootPage()
|
||||||
|
{
|
||||||
|
if (_table == null)
|
||||||
|
{
|
||||||
|
if (Levels.Length == 1)
|
||||||
|
_table = (TEntry**)Allocate(1 << Levels[0].Length, Fill, leaf: true);
|
||||||
|
else
|
||||||
|
_table = (TEntry**)Allocate(1 << Levels[0].Length, GetFillValue(0), leaf: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _table;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize a leaf page with the fill value.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="page">Page to initialize</param>
|
||||||
|
private void InitLeafPage(Span<byte> page)
|
||||||
|
{
|
||||||
|
MemoryMarshal.Cast<byte, TEntry>(page).Fill(_fill);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reserve a new sparse block, and add it to the list.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The new sparse block that was added</returns>
|
||||||
|
private TableSparseBlock ReserveNewSparseBlock()
|
||||||
|
{
|
||||||
|
var block = new TableSparseBlock(_sparseBlockSize, EnsureMapped, InitLeafPage);
|
||||||
|
|
||||||
|
_sparseReserved.Add(block);
|
||||||
|
_sparseReservedOffset = 0;
|
||||||
|
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Allocates a block of memory of the specified type and length.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Type of elements</typeparam>
|
||||||
|
/// <param name="length">Number of elements</param>
|
||||||
|
/// <param name="fill">Fill value</param>
|
||||||
|
/// <param name="leaf"><see langword="true"/> if leaf; otherwise <see langword="false"/></param>
|
||||||
|
/// <returns>Allocated block</returns>
|
||||||
|
private IntPtr Allocate<T>(int length, T fill, bool leaf) where T : unmanaged
|
||||||
|
{
|
||||||
|
var size = sizeof(T) * length;
|
||||||
|
|
||||||
|
AddressTablePage page;
|
||||||
|
|
||||||
|
if (Sparse && leaf)
|
||||||
|
{
|
||||||
|
_sparseLock.EnterWriteLock();
|
||||||
|
|
||||||
|
SparseMemoryBlock block;
|
||||||
|
|
||||||
|
if (_sparseReserved.Count == 0)
|
||||||
|
{
|
||||||
|
block = ReserveNewSparseBlock().Block;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
block = _sparseReserved.Last().Block;
|
||||||
|
|
||||||
|
if (_sparseReservedOffset == block.Block.Size)
|
||||||
|
{
|
||||||
|
block = ReserveNewSparseBlock().Block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
page = new AddressTablePage(true, block.Block.Pointer + (IntPtr)_sparseReservedOffset);
|
||||||
|
|
||||||
|
_sparseReservedOffset += (ulong)size;
|
||||||
|
|
||||||
|
_sparseLock.ExitWriteLock();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var address = (IntPtr)NativeAllocator.Instance.Allocate((uint)size);
|
||||||
|
page = new AddressTablePage(false, address);
|
||||||
|
|
||||||
|
var span = new Span<T>((void*)page.Address, length);
|
||||||
|
span.Fill(fill);
|
||||||
|
}
|
||||||
|
|
||||||
|
_pages.Add(page);
|
||||||
|
|
||||||
|
//TranslatorEventSource.Log.AddressTableAllocated(size, leaf);
|
||||||
|
|
||||||
|
return page.Address;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Releases all resources used by the <see cref="AddressTable{TEntry}"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Releases all unmanaged and optionally managed resources used by the <see cref="AddressTable{TEntry}"/>
|
||||||
|
/// instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param>
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!_disposed)
|
||||||
|
{
|
||||||
|
foreach (var page in _pages)
|
||||||
|
{
|
||||||
|
if (!page.IsSparse)
|
||||||
|
{
|
||||||
|
Marshal.FreeHGlobal(page.Address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Sparse)
|
||||||
|
{
|
||||||
|
foreach (TableSparseBlock block in _sparseReserved)
|
||||||
|
{
|
||||||
|
block.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_sparseReserved.Clear();
|
||||||
|
|
||||||
|
_fillBottomLevel.Dispose();
|
||||||
|
_sparseFill.Dispose();
|
||||||
|
_sparseLock.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Frees resources used by the <see cref="AddressTable{TEntry}"/> instance.
|
||||||
|
/// </summary>
|
||||||
|
~AddressTable()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,7 +32,7 @@ namespace Ryujinx.Cpu.AppleHv
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
||||||
{
|
{
|
||||||
return new DummyDiskCacheLoadState();
|
return new DummyDiskCacheLoadState();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ namespace Ryujinx.Cpu
|
|||||||
/// <param name="displayVersion">Version of the application</param>
|
/// <param name="displayVersion">Version of the application</param>
|
||||||
/// <param name="enabled">True if the cache should be loaded from disk if it exists, false otherwise</param>
|
/// <param name="enabled">True if the cache should be loaded from disk if it exists, false otherwise</param>
|
||||||
/// <returns>Disk cache load progress reporter and manager</returns>
|
/// <returns>Disk cache load progress reporter and manager</returns>
|
||||||
IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled);
|
IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates that code has been loaded into guest memory, and that it might be executed in the future.
|
/// Indicates that code has been loaded into guest memory, and that it might be executed in the future.
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using ARMeilleure.Common;
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using Ryujinx.Cpu.Signal;
|
using Ryujinx.Cpu.Signal;
|
||||||
@@ -9,11 +10,13 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
{
|
{
|
||||||
private readonly ITickSource _tickSource;
|
private readonly ITickSource _tickSource;
|
||||||
private readonly Translator _translator;
|
private readonly Translator _translator;
|
||||||
|
private readonly AddressTable<ulong> _functionTable;
|
||||||
|
|
||||||
public JitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
|
public JitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
|
||||||
{
|
{
|
||||||
_tickSource = tickSource;
|
_tickSource = tickSource;
|
||||||
_translator = new Translator(new JitMemoryAllocator(forJit: true), memory, for64Bit);
|
_functionTable = AddressTable<ulong>.CreateForArm(for64Bit, memory.Type);
|
||||||
|
_translator = new Translator(new JitMemoryAllocator(forJit: true), memory, _functionTable);
|
||||||
|
|
||||||
if (memory.Type.IsHostMappedOrTracked())
|
if (memory.Type.IsHostMappedOrTracked())
|
||||||
{
|
{
|
||||||
@@ -47,14 +50,15 @@ namespace Ryujinx.Cpu.Jit
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
||||||
{
|
{
|
||||||
return new JitDiskCacheLoadState(_translator.LoadDiskCache(titleIdText, displayVersion, enabled));
|
return new JitDiskCacheLoadState(_translator.LoadDiskCache(titleIdText, displayVersion, enabled, cacheSelector));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void PrepareCodeRange(ulong address, ulong size)
|
public void PrepareCodeRange(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
|
_functionTable.SignalCodeRange(address, size);
|
||||||
_translator.PrepareCodeRange(address, size);
|
_translator.PrepareCodeRange(address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -140,6 +140,10 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
bool isTail = false)
|
bool isTail = false)
|
||||||
{
|
{
|
||||||
int tempRegister;
|
int tempRegister;
|
||||||
|
int tempGuestAddress = -1;
|
||||||
|
|
||||||
|
bool inlineLookup = guestAddress.Kind != OperandKind.Constant &&
|
||||||
|
funcTable is { Sparse: true };
|
||||||
|
|
||||||
if (guestAddress.Kind == OperandKind.Constant)
|
if (guestAddress.Kind == OperandKind.Constant)
|
||||||
{
|
{
|
||||||
@@ -153,9 +157,16 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
asm.StrRiUn(guestAddress, Register(regAlloc.FixedContextRegister), NativeContextOffsets.DispatchAddressOffset);
|
asm.StrRiUn(guestAddress, Register(regAlloc.FixedContextRegister), NativeContextOffsets.DispatchAddressOffset);
|
||||||
|
|
||||||
|
if (inlineLookup && guestAddress.Value == 0)
|
||||||
|
{
|
||||||
|
// X0 will be overwritten. Move the address to a temp register.
|
||||||
|
tempGuestAddress = regAlloc.AllocateTempGprRegister();
|
||||||
|
asm.Mov(Register(tempGuestAddress), guestAddress);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tempRegister = regAlloc.FixedContextRegister == 1 ? 2 : 1;
|
tempRegister = NextFreeRegister(1, tempGuestAddress);
|
||||||
|
|
||||||
if (!isTail)
|
if (!isTail)
|
||||||
{
|
{
|
||||||
@@ -176,6 +187,40 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
asm.Mov(rn, funcPtrLoc & ~0xfffUL);
|
asm.Mov(rn, funcPtrLoc & ~0xfffUL);
|
||||||
asm.LdrRiUn(rn, rn, (int)(funcPtrLoc & 0xfffUL));
|
asm.LdrRiUn(rn, rn, (int)(funcPtrLoc & 0xfffUL));
|
||||||
}
|
}
|
||||||
|
else if (inlineLookup)
|
||||||
|
{
|
||||||
|
// Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels.
|
||||||
|
|
||||||
|
Operand indexReg = Register(NextFreeRegister(tempRegister + 1, tempGuestAddress));
|
||||||
|
|
||||||
|
if (tempGuestAddress != -1)
|
||||||
|
{
|
||||||
|
guestAddress = Register(tempGuestAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong tableBase = (ulong)funcTable.Base;
|
||||||
|
|
||||||
|
// Index into the table.
|
||||||
|
asm.Mov(rn, tableBase);
|
||||||
|
|
||||||
|
for (int i = 0; i < funcTable.Levels.Length; i++)
|
||||||
|
{
|
||||||
|
var level = funcTable.Levels[i];
|
||||||
|
asm.Ubfx(indexReg, guestAddress, level.Index, level.Length);
|
||||||
|
asm.Lsl(indexReg, indexReg, Const(3));
|
||||||
|
|
||||||
|
// Index into the page.
|
||||||
|
asm.Add(rn, rn, indexReg);
|
||||||
|
|
||||||
|
// Load the page address.
|
||||||
|
asm.LdrRiUn(rn, rn, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tempGuestAddress != -1)
|
||||||
|
{
|
||||||
|
regAlloc.FreeTempGprRegister(tempGuestAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
asm.Mov(rn, (ulong)funcPtr);
|
asm.Mov(rn, (ulong)funcPtr);
|
||||||
@@ -252,5 +297,20 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
|
|||||||
{
|
{
|
||||||
return new Operand(register, RegisterType.Integer, type);
|
return new Operand(register, RegisterType.Integer, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Operand Const(long value, OperandType type = OperandType.I64)
|
||||||
|
{
|
||||||
|
return new Operand(type, (ulong)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int NextFreeRegister(int start, int avoid)
|
||||||
|
{
|
||||||
|
if (start == avoid)
|
||||||
|
{
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return start;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -305,6 +305,10 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
|||||||
bool isTail = false)
|
bool isTail = false)
|
||||||
{
|
{
|
||||||
int tempRegister;
|
int tempRegister;
|
||||||
|
int tempGuestAddress = -1;
|
||||||
|
|
||||||
|
bool inlineLookup = guestAddress.Kind != OperandKind.Constant &&
|
||||||
|
funcTable is { Sparse: true };
|
||||||
|
|
||||||
if (guestAddress.Kind == OperandKind.Constant)
|
if (guestAddress.Kind == OperandKind.Constant)
|
||||||
{
|
{
|
||||||
@@ -318,9 +322,16 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
asm.StrRiUn(guestAddress, Register(regAlloc.FixedContextRegister), NativeContextOffsets.DispatchAddressOffset);
|
asm.StrRiUn(guestAddress, Register(regAlloc.FixedContextRegister), NativeContextOffsets.DispatchAddressOffset);
|
||||||
|
|
||||||
|
if (inlineLookup && guestAddress.Value == 0)
|
||||||
|
{
|
||||||
|
// X0 will be overwritten. Move the address to a temp register.
|
||||||
|
tempGuestAddress = regAlloc.AllocateTempGprRegister();
|
||||||
|
asm.Mov(Register(tempGuestAddress), guestAddress);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tempRegister = regAlloc.FixedContextRegister == 1 ? 2 : 1;
|
tempRegister = NextFreeRegister(1, tempGuestAddress);
|
||||||
|
|
||||||
if (!isTail)
|
if (!isTail)
|
||||||
{
|
{
|
||||||
@@ -341,6 +352,40 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
|||||||
asm.Mov(rn, funcPtrLoc & ~0xfffUL);
|
asm.Mov(rn, funcPtrLoc & ~0xfffUL);
|
||||||
asm.LdrRiUn(rn, rn, (int)(funcPtrLoc & 0xfffUL));
|
asm.LdrRiUn(rn, rn, (int)(funcPtrLoc & 0xfffUL));
|
||||||
}
|
}
|
||||||
|
else if (inlineLookup)
|
||||||
|
{
|
||||||
|
// Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels.
|
||||||
|
|
||||||
|
Operand indexReg = Register(NextFreeRegister(tempRegister + 1, tempGuestAddress));
|
||||||
|
|
||||||
|
if (tempGuestAddress != -1)
|
||||||
|
{
|
||||||
|
guestAddress = Register(tempGuestAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong tableBase = (ulong)funcTable.Base;
|
||||||
|
|
||||||
|
// Index into the table.
|
||||||
|
asm.Mov(rn, tableBase);
|
||||||
|
|
||||||
|
for (int i = 0; i < funcTable.Levels.Length; i++)
|
||||||
|
{
|
||||||
|
var level = funcTable.Levels[i];
|
||||||
|
asm.Ubfx(indexReg, guestAddress, level.Index, level.Length);
|
||||||
|
asm.Lsl(indexReg, indexReg, Const(3));
|
||||||
|
|
||||||
|
// Index into the page.
|
||||||
|
asm.Add(rn, rn, indexReg);
|
||||||
|
|
||||||
|
// Load the page address.
|
||||||
|
asm.LdrRiUn(rn, rn, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tempGuestAddress != -1)
|
||||||
|
{
|
||||||
|
regAlloc.FreeTempGprRegister(tempGuestAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
asm.Mov(rn, (ulong)funcPtr);
|
asm.Mov(rn, (ulong)funcPtr);
|
||||||
@@ -613,5 +658,20 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
|
|||||||
{
|
{
|
||||||
return new Operand(register, RegisterType.Integer, type);
|
return new Operand(register, RegisterType.Integer, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Operand Const(long value, OperandType type = OperandType.I64)
|
||||||
|
{
|
||||||
|
return new Operand(type, (ulong)value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int NextFreeRegister(int start, int avoid)
|
||||||
|
{
|
||||||
|
if (start == avoid)
|
||||||
|
{
|
||||||
|
start++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return start;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using ARMeilleure.Common;
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using Ryujinx.Cpu.Jit;
|
using Ryujinx.Cpu.Jit;
|
||||||
using Ryujinx.Cpu.LightningJit.State;
|
using Ryujinx.Cpu.LightningJit.State;
|
||||||
@@ -8,11 +9,16 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
{
|
{
|
||||||
private readonly ITickSource _tickSource;
|
private readonly ITickSource _tickSource;
|
||||||
private readonly Translator _translator;
|
private readonly Translator _translator;
|
||||||
|
private readonly AddressTable<ulong> _functionTable;
|
||||||
|
|
||||||
public LightningJitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
|
public LightningJitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
|
||||||
{
|
{
|
||||||
_tickSource = tickSource;
|
_tickSource = tickSource;
|
||||||
_translator = new Translator(memory, for64Bit);
|
|
||||||
|
_functionTable = AddressTable<ulong>.CreateForArm(for64Bit, memory.Type);
|
||||||
|
|
||||||
|
_translator = new Translator(memory, _functionTable);
|
||||||
|
|
||||||
memory.UnmapEvent += UnmapHandler;
|
memory.UnmapEvent += UnmapHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +46,7 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
public IDiskCacheLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled, string cacheSelector)
|
||||||
{
|
{
|
||||||
return new DummyDiskCacheLoadState();
|
return new DummyDiskCacheLoadState();
|
||||||
}
|
}
|
||||||
@@ -48,6 +54,7 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void PrepareCodeRange(ulong address, ulong size)
|
public void PrepareCodeRange(ulong address, ulong size)
|
||||||
{
|
{
|
||||||
|
_functionTable.SignalCodeRange(address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|||||||
@@ -19,25 +19,6 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
// Should be enabled on platforms that enforce W^X.
|
// Should be enabled on platforms that enforce W^X.
|
||||||
private static bool IsNoWxPlatform => false;
|
private static bool IsNoWxPlatform => false;
|
||||||
|
|
||||||
private static readonly AddressTable<ulong>.Level[] _levels64Bit =
|
|
||||||
new AddressTable<ulong>.Level[]
|
|
||||||
{
|
|
||||||
new(31, 17),
|
|
||||||
new(23, 8),
|
|
||||||
new(15, 8),
|
|
||||||
new( 7, 8),
|
|
||||||
new( 2, 5),
|
|
||||||
};
|
|
||||||
|
|
||||||
private static readonly AddressTable<ulong>.Level[] _levels32Bit =
|
|
||||||
new AddressTable<ulong>.Level[]
|
|
||||||
{
|
|
||||||
new(23, 9),
|
|
||||||
new(15, 8),
|
|
||||||
new( 7, 8),
|
|
||||||
new( 1, 6),
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
||||||
private readonly NoWxCache _noWxCache;
|
private readonly NoWxCache _noWxCache;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
@@ -47,7 +28,7 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
internal TranslatorStubs Stubs { get; }
|
internal TranslatorStubs Stubs { get; }
|
||||||
internal IMemoryManager Memory { get; }
|
internal IMemoryManager Memory { get; }
|
||||||
|
|
||||||
public Translator(IMemoryManager memory, bool for64Bits)
|
public Translator(IMemoryManager memory, AddressTable<ulong> functionTable)
|
||||||
{
|
{
|
||||||
Memory = memory;
|
Memory = memory;
|
||||||
|
|
||||||
@@ -63,7 +44,7 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
}
|
}
|
||||||
|
|
||||||
Functions = new TranslatorCache<TranslatedFunction>();
|
Functions = new TranslatorCache<TranslatedFunction>();
|
||||||
FunctionTable = new AddressTable<ulong>(for64Bits ? _levels64Bit : _levels32Bit);
|
FunctionTable = functionTable;
|
||||||
Stubs = new TranslatorStubs(FunctionTable, _noWxCache);
|
Stubs = new TranslatorStubs(FunctionTable, _noWxCache);
|
||||||
|
|
||||||
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
private readonly AddressTable<ulong> _functionTable;
|
private readonly IAddressTable<ulong> _functionTable;
|
||||||
private readonly NoWxCache _noWxCache;
|
private readonly NoWxCache _noWxCache;
|
||||||
private readonly GetFunctionAddressDelegate _getFunctionAddressRef;
|
private readonly GetFunctionAddressDelegate _getFunctionAddressRef;
|
||||||
private readonly nint _getFunctionAddress;
|
private readonly nint _getFunctionAddress;
|
||||||
@@ -79,7 +79,7 @@ namespace Ryujinx.Cpu.LightningJit
|
|||||||
/// <param name="functionTable">Function table used to store pointers to the functions that the guest code will call</param>
|
/// <param name="functionTable">Function table used to store pointers to the functions that the guest code will call</param>
|
||||||
/// <param name="noWxCache">Cache used on platforms that enforce W^X, otherwise should be null</param>
|
/// <param name="noWxCache">Cache used on platforms that enforce W^X, otherwise should be null</param>
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
||||||
public TranslatorStubs(AddressTable<ulong> functionTable, NoWxCache noWxCache)
|
public TranslatorStubs(IAddressTable<ulong> functionTable, NoWxCache noWxCache)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(functionTable);
|
ArgumentNullException.ThrowIfNull(functionTable);
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
|
|
||||||
void SetSize(int width, int height);
|
void SetSize(int width, int height);
|
||||||
|
|
||||||
void ChangeVSyncMode(bool vsyncEnabled);
|
void ChangeVSyncMode(VSyncMode vSyncMode);
|
||||||
|
|
||||||
void SetAntiAliasing(AntiAliasing antialiasing);
|
void SetAntiAliasing(AntiAliasing antialiasing);
|
||||||
void SetScalingFilter(ScalingFilter type);
|
void SetScalingFilter(ScalingFilter type);
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
_impl.Window.SetSize(width, height);
|
_impl.Window.SetSize(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ChangeVSyncMode(bool vsyncEnabled) { }
|
public void ChangeVSyncMode(VSyncMode vSyncMode) { }
|
||||||
|
|
||||||
public void SetAntiAliasing(AntiAliasing effect) { }
|
public void SetAntiAliasing(AntiAliasing effect) { }
|
||||||
|
|
||||||
|
|||||||
9
src/Ryujinx.Graphics.GAL/VSyncMode.cs
Normal file
9
src/Ryujinx.Graphics.GAL/VSyncMode.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public enum VSyncMode
|
||||||
|
{
|
||||||
|
Switch,
|
||||||
|
Unbounded,
|
||||||
|
Custom
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -54,7 +54,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 4);
|
GL.PixelStore(PixelStoreParameter.UnpackAlignment, 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ChangeVSyncMode(bool vsyncEnabled) { }
|
public void ChangeVSyncMode(VSyncMode vSyncMode) { }
|
||||||
|
|
||||||
public void SetSize(int width, int height)
|
public void SetSize(int width, int height)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private int _width;
|
private int _width;
|
||||||
private int _height;
|
private int _height;
|
||||||
private bool _vsyncEnabled;
|
private VSyncMode _vSyncMode;
|
||||||
private bool _swapchainIsDirty;
|
private bool _swapchainIsDirty;
|
||||||
private VkFormat _format;
|
private VkFormat _format;
|
||||||
private AntiAliasing _currentAntiAliasing;
|
private AntiAliasing _currentAntiAliasing;
|
||||||
@@ -139,7 +139,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
ImageArrayLayers = 1,
|
ImageArrayLayers = 1,
|
||||||
PreTransform = capabilities.CurrentTransform,
|
PreTransform = capabilities.CurrentTransform,
|
||||||
CompositeAlpha = ChooseCompositeAlpha(capabilities.SupportedCompositeAlpha),
|
CompositeAlpha = ChooseCompositeAlpha(capabilities.SupportedCompositeAlpha),
|
||||||
PresentMode = ChooseSwapPresentMode(presentModes, _vsyncEnabled),
|
PresentMode = ChooseSwapPresentMode(presentModes, _vSyncMode),
|
||||||
Clipped = true,
|
Clipped = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -279,9 +279,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PresentModeKHR ChooseSwapPresentMode(PresentModeKHR[] availablePresentModes, bool vsyncEnabled)
|
private static PresentModeKHR ChooseSwapPresentMode(PresentModeKHR[] availablePresentModes, VSyncMode vSyncMode)
|
||||||
{
|
{
|
||||||
if (!vsyncEnabled && availablePresentModes.Contains(PresentModeKHR.ImmediateKhr))
|
if (vSyncMode == VSyncMode.Unbounded && availablePresentModes.Contains(PresentModeKHR.ImmediateKhr))
|
||||||
{
|
{
|
||||||
return PresentModeKHR.ImmediateKhr;
|
return PresentModeKHR.ImmediateKhr;
|
||||||
}
|
}
|
||||||
@@ -634,9 +634,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_swapchainIsDirty = true;
|
_swapchainIsDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void ChangeVSyncMode(bool vsyncEnabled)
|
public override void ChangeVSyncMode(VSyncMode vSyncMode)
|
||||||
{
|
{
|
||||||
_vsyncEnabled = vsyncEnabled;
|
_vSyncMode = vSyncMode;
|
||||||
|
//present mode may change, so mark the swapchain for recreation
|
||||||
_swapchainIsDirty = true;
|
_swapchainIsDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public abstract void Dispose();
|
public abstract void Dispose();
|
||||||
public abstract void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback);
|
public abstract void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback);
|
||||||
public abstract void SetSize(int width, int height);
|
public abstract void SetSize(int width, int height);
|
||||||
public abstract void ChangeVSyncMode(bool vsyncEnabled);
|
public abstract void ChangeVSyncMode(VSyncMode vSyncMode);
|
||||||
public abstract void SetAntiAliasing(AntiAliasing effect);
|
public abstract void SetAntiAliasing(AntiAliasing effect);
|
||||||
public abstract void SetScalingFilter(ScalingFilter scalerType);
|
public abstract void SetScalingFilter(ScalingFilter scalerType);
|
||||||
public abstract void SetScalingFilterLevel(float scale);
|
public abstract void SetScalingFilterLevel(float scale);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using Ryujinx.HLE.HOS.Services.Account.Acc;
|
|||||||
using Ryujinx.HLE.HOS.SystemState;
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
using Ryujinx.HLE.UI;
|
using Ryujinx.HLE.UI;
|
||||||
using System;
|
using System;
|
||||||
|
using VSyncMode = Ryujinx.Common.Configuration.VSyncMode;
|
||||||
|
|
||||||
namespace Ryujinx.HLE
|
namespace Ryujinx.HLE
|
||||||
{
|
{
|
||||||
@@ -84,9 +85,14 @@ namespace Ryujinx.HLE
|
|||||||
internal readonly RegionCode Region;
|
internal readonly RegionCode Region;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Control the initial state of the vertical sync in the SurfaceFlinger service.
|
/// Control the initial state of the present interval in the SurfaceFlinger service (previously Vsync).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal readonly bool EnableVsync;
|
internal readonly VSyncMode VSyncMode;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Control the custom VSync interval, if enabled and active.
|
||||||
|
/// </summary>
|
||||||
|
internal readonly int CustomVSyncInterval;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Control the initial state of the docked mode.
|
/// Control the initial state of the docked mode.
|
||||||
@@ -195,7 +201,7 @@ namespace Ryujinx.HLE
|
|||||||
IHostUIHandler hostUIHandler,
|
IHostUIHandler hostUIHandler,
|
||||||
SystemLanguage systemLanguage,
|
SystemLanguage systemLanguage,
|
||||||
RegionCode region,
|
RegionCode region,
|
||||||
bool enableVsync,
|
VSyncMode vSyncMode,
|
||||||
bool enableDockedMode,
|
bool enableDockedMode,
|
||||||
bool enablePtc,
|
bool enablePtc,
|
||||||
bool enableInternetAccess,
|
bool enableInternetAccess,
|
||||||
@@ -212,7 +218,8 @@ namespace Ryujinx.HLE
|
|||||||
MultiplayerMode multiplayerMode,
|
MultiplayerMode multiplayerMode,
|
||||||
bool multiplayerDisableP2p,
|
bool multiplayerDisableP2p,
|
||||||
string multiplayerLdnPassphrase,
|
string multiplayerLdnPassphrase,
|
||||||
string multiplayerLdnServer)
|
string multiplayerLdnServer,
|
||||||
|
int customVSyncInterval)
|
||||||
{
|
{
|
||||||
VirtualFileSystem = virtualFileSystem;
|
VirtualFileSystem = virtualFileSystem;
|
||||||
LibHacHorizonManager = libHacHorizonManager;
|
LibHacHorizonManager = libHacHorizonManager;
|
||||||
@@ -225,7 +232,8 @@ namespace Ryujinx.HLE
|
|||||||
HostUIHandler = hostUIHandler;
|
HostUIHandler = hostUIHandler;
|
||||||
SystemLanguage = systemLanguage;
|
SystemLanguage = systemLanguage;
|
||||||
Region = region;
|
Region = region;
|
||||||
EnableVsync = enableVsync;
|
VSyncMode = vSyncMode;
|
||||||
|
CustomVSyncInterval = customVSyncInterval;
|
||||||
EnableDockedMode = enableDockedMode;
|
EnableDockedMode = enableDockedMode;
|
||||||
EnablePtc = enablePtc;
|
EnablePtc = enablePtc;
|
||||||
EnableInternetAccess = enableInternetAccess;
|
EnableInternetAccess = enableInternetAccess;
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ namespace Ryujinx.HLE.HOS
|
|||||||
string displayVersion,
|
string displayVersion,
|
||||||
bool diskCacheEnabled,
|
bool diskCacheEnabled,
|
||||||
ulong codeAddress,
|
ulong codeAddress,
|
||||||
ulong codeSize);
|
ulong codeSize,
|
||||||
|
string cacheSelector);
|
||||||
}
|
}
|
||||||
|
|
||||||
class ArmProcessContext<T> : IArmProcessContext where T : class, IVirtualMemoryManagerTracked, IMemoryManager
|
class ArmProcessContext<T> : IArmProcessContext where T : class, IVirtualMemoryManagerTracked, IMemoryManager
|
||||||
@@ -67,10 +68,11 @@ namespace Ryujinx.HLE.HOS
|
|||||||
string displayVersion,
|
string displayVersion,
|
||||||
bool diskCacheEnabled,
|
bool diskCacheEnabled,
|
||||||
ulong codeAddress,
|
ulong codeAddress,
|
||||||
ulong codeSize)
|
ulong codeSize,
|
||||||
|
string cacheSelector)
|
||||||
{
|
{
|
||||||
_cpuContext.PrepareCodeRange(codeAddress, codeSize);
|
_cpuContext.PrepareCodeRange(codeAddress, codeSize);
|
||||||
return _cpuContext.LoadDiskCache(titleIdText, displayVersion, diskCacheEnabled);
|
return _cpuContext.LoadDiskCache(titleIdText, displayVersion, diskCacheEnabled, cacheSelector);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InvalidateCacheRegion(ulong address, ulong size)
|
public void InvalidateCacheRegion(ulong address, ulong size)
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize);
|
DiskCacheLoadState = processContext.Initialize(_titleIdText, _displayVersion, _diskCacheEnabled, _codeAddress, _codeSize, "default"); //Ready for exefs profiles
|
||||||
|
|
||||||
return processContext;
|
return processContext;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,13 +10,12 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using VSyncMode = Ryujinx.Common.Configuration.VSyncMode;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
{
|
{
|
||||||
class SurfaceFlinger : IConsumerListener, IDisposable
|
class SurfaceFlinger : IConsumerListener, IDisposable
|
||||||
{
|
{
|
||||||
private const int TargetFps = 60;
|
|
||||||
|
|
||||||
private readonly Switch _device;
|
private readonly Switch _device;
|
||||||
|
|
||||||
private readonly Dictionary<long, Layer> _layers;
|
private readonly Dictionary<long, Layer> _layers;
|
||||||
@@ -32,6 +31,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
private readonly long _spinTicks;
|
private readonly long _spinTicks;
|
||||||
private readonly long _1msTicks;
|
private readonly long _1msTicks;
|
||||||
|
|
||||||
|
private VSyncMode _vSyncMode;
|
||||||
|
private long _targetVSyncInterval;
|
||||||
|
|
||||||
private int _swapInterval;
|
private int _swapInterval;
|
||||||
private int _swapIntervalDelay;
|
private int _swapIntervalDelay;
|
||||||
|
|
||||||
@@ -88,7 +90,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_ticksPerFrame = Stopwatch.Frequency / TargetFps;
|
_ticksPerFrame = Stopwatch.Frequency / _device.TargetVSyncInterval;
|
||||||
|
_targetVSyncInterval = _device.TargetVSyncInterval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,15 +373,20 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||||||
|
|
||||||
if (acquireStatus == Status.Success)
|
if (acquireStatus == Status.Success)
|
||||||
{
|
{
|
||||||
// If device vsync is disabled, reflect the change.
|
if (_device.VSyncMode == VSyncMode.Unbounded)
|
||||||
if (!_device.EnableDeviceVsync)
|
|
||||||
{
|
{
|
||||||
if (_swapInterval != 0)
|
if (_swapInterval != 0)
|
||||||
{
|
{
|
||||||
UpdateSwapInterval(0);
|
UpdateSwapInterval(0);
|
||||||
|
_vSyncMode = _device.VSyncMode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (item.SwapInterval != _swapInterval)
|
else if (_device.VSyncMode != _vSyncMode)
|
||||||
|
{
|
||||||
|
UpdateSwapInterval(_device.VSyncMode == VSyncMode.Unbounded ? 0 : item.SwapInterval);
|
||||||
|
_vSyncMode = _device.VSyncMode;
|
||||||
|
}
|
||||||
|
else if (item.SwapInterval != _swapInterval || _device.TargetVSyncInterval != _targetVSyncInterval)
|
||||||
{
|
{
|
||||||
UpdateSwapInterval(item.SwapInterval);
|
UpdateSwapInterval(item.SwapInterval);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,11 @@ namespace Ryujinx.HLE
|
|||||||
public TamperMachine TamperMachine { get; }
|
public TamperMachine TamperMachine { get; }
|
||||||
public IHostUIHandler UIHandler { get; }
|
public IHostUIHandler UIHandler { get; }
|
||||||
|
|
||||||
public bool EnableDeviceVsync { get; set; }
|
public VSyncMode VSyncMode { get; set; } = VSyncMode.Switch;
|
||||||
|
public bool CustomVSyncIntervalEnabled { get; set; } = false;
|
||||||
|
public int CustomVSyncInterval { get; set; }
|
||||||
|
|
||||||
|
public long TargetVSyncInterval { get; set; } = 60;
|
||||||
|
|
||||||
public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable;
|
public bool IsFrameAvailable => Gpu.Window.IsFrameAvailable;
|
||||||
|
|
||||||
@@ -59,12 +63,14 @@ namespace Ryujinx.HLE
|
|||||||
System.State.SetLanguage(Configuration.SystemLanguage);
|
System.State.SetLanguage(Configuration.SystemLanguage);
|
||||||
System.State.SetRegion(Configuration.Region);
|
System.State.SetRegion(Configuration.Region);
|
||||||
|
|
||||||
EnableDeviceVsync = Configuration.EnableVsync;
|
VSyncMode = Configuration.VSyncMode;
|
||||||
|
CustomVSyncInterval = Configuration.CustomVSyncInterval;
|
||||||
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;
|
||||||
System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel;
|
System.FsIntegrityCheckLevel = Configuration.FsIntegrityCheckLevel;
|
||||||
System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode;
|
System.GlobalAccessLogMode = Configuration.FsGlobalAccessLogMode;
|
||||||
|
UpdateVSyncInterval();
|
||||||
#pragma warning restore IDE0055
|
#pragma warning restore IDE0055
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,6 +81,34 @@ namespace Ryujinx.HLE
|
|||||||
Gpu.GPFifo.DispatchCalls();
|
Gpu.GPFifo.DispatchCalls();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void IncrementCustomVSyncInterval()
|
||||||
|
{
|
||||||
|
CustomVSyncInterval += 1;
|
||||||
|
UpdateVSyncInterval();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DecrementCustomVSyncInterval()
|
||||||
|
{
|
||||||
|
CustomVSyncInterval -= 1;
|
||||||
|
UpdateVSyncInterval();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateVSyncInterval()
|
||||||
|
{
|
||||||
|
switch (VSyncMode)
|
||||||
|
{
|
||||||
|
case VSyncMode.Custom:
|
||||||
|
TargetVSyncInterval = CustomVSyncInterval;
|
||||||
|
break;
|
||||||
|
case VSyncMode.Switch:
|
||||||
|
TargetVSyncInterval = 60;
|
||||||
|
break;
|
||||||
|
case VSyncMode.Unbounded:
|
||||||
|
TargetVSyncInterval = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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) => Processes.LoadNca(ncaFile);
|
public bool LoadNca(string ncaFile) => Processes.LoadNca(ncaFile);
|
||||||
|
|||||||
@@ -115,8 +115,11 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
[Option("fs-global-access-log-mode", Required = false, Default = 0, HelpText = "Enables FS access log output to the console.")]
|
[Option("fs-global-access-log-mode", Required = false, Default = 0, HelpText = "Enables FS access log output to the console.")]
|
||||||
public int FsGlobalAccessLogMode { get; set; }
|
public int FsGlobalAccessLogMode { get; set; }
|
||||||
|
|
||||||
[Option("disable-vsync", Required = false, HelpText = "Disables Vertical Sync.")]
|
[Option("vsync-mode", Required = false, Default = VSyncMode.Switch, HelpText = "Sets the emulated VSync mode (Switch, Unbounded, or Custom).")]
|
||||||
public bool DisableVSync { get; set; }
|
public VSyncMode VSyncMode { get; set; }
|
||||||
|
|
||||||
|
[Option("custom-refresh-rate", Required = false, Default = 90, HelpText = "Sets the custom refresh rate target value (integer).")]
|
||||||
|
public int CustomVSyncInterval { get; set; }
|
||||||
|
|
||||||
[Option("disable-shader-cache", Required = false, HelpText = "Disables Shader cache.")]
|
[Option("disable-shader-cache", Required = false, HelpText = "Disables Shader cache.")]
|
||||||
public bool DisableShaderCache { get; set; }
|
public bool DisableShaderCache { get; set; }
|
||||||
|
|||||||
@@ -563,7 +563,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
window,
|
window,
|
||||||
options.SystemLanguage,
|
options.SystemLanguage,
|
||||||
options.SystemRegion,
|
options.SystemRegion,
|
||||||
!options.DisableVSync,
|
options.VSyncMode,
|
||||||
!options.DisableDockedMode,
|
!options.DisableDockedMode,
|
||||||
!options.DisablePTC,
|
!options.DisablePTC,
|
||||||
options.EnableInternetAccess,
|
options.EnableInternetAccess,
|
||||||
@@ -580,7 +580,8 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
Common.Configuration.Multiplayer.MultiplayerMode.Disabled,
|
Common.Configuration.Multiplayer.MultiplayerMode.Disabled,
|
||||||
false,
|
false,
|
||||||
"",
|
"",
|
||||||
"");
|
"",
|
||||||
|
options.CustomVSyncInterval);
|
||||||
|
|
||||||
return new Switch(configuration);
|
return new Switch(configuration);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using System;
|
|||||||
namespace Ryujinx.Headless.SDL2
|
namespace Ryujinx.Headless.SDL2
|
||||||
{
|
{
|
||||||
class StatusUpdatedEventArgs(
|
class StatusUpdatedEventArgs(
|
||||||
bool vSyncEnabled,
|
string vSyncMode,
|
||||||
string dockedMode,
|
string dockedMode,
|
||||||
string aspectRatio,
|
string aspectRatio,
|
||||||
string gameStatus,
|
string gameStatus,
|
||||||
@@ -11,7 +11,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
string gpuName)
|
string gpuName)
|
||||||
: EventArgs
|
: EventArgs
|
||||||
{
|
{
|
||||||
public bool VSyncEnabled = vSyncEnabled;
|
public string VSyncMode = vSyncMode;
|
||||||
public string DockedMode = dockedMode;
|
public string DockedMode = dockedMode;
|
||||||
public string AspectRatio = aspectRatio;
|
public string AspectRatio = aspectRatio;
|
||||||
public string GameStatus = gameStatus;
|
public string GameStatus = gameStatus;
|
||||||
|
|||||||
@@ -314,7 +314,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
}
|
}
|
||||||
|
|
||||||
StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs(
|
StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs(
|
||||||
Device.EnableDeviceVsync,
|
Device.VSyncMode.ToString(),
|
||||||
dockedMode,
|
dockedMode,
|
||||||
Device.Configuration.AspectRatio.ToText(),
|
Device.Configuration.AspectRatio.ToText(),
|
||||||
$"Game: {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)",
|
$"Game: {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)",
|
||||||
|
|||||||
125
src/Ryujinx.Memory/SparseMemoryBlock.cs
Normal file
125
src/Ryujinx.Memory/SparseMemoryBlock.cs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
using Ryujinx.Common;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Ryujinx.Memory
|
||||||
|
{
|
||||||
|
public delegate void PageInitDelegate(Span<byte> page);
|
||||||
|
|
||||||
|
public class SparseMemoryBlock : IDisposable
|
||||||
|
{
|
||||||
|
private const ulong MapGranularity = 1UL << 17;
|
||||||
|
|
||||||
|
private readonly PageInitDelegate _pageInit;
|
||||||
|
|
||||||
|
private readonly object _lock = new object();
|
||||||
|
private readonly ulong _pageSize;
|
||||||
|
private readonly MemoryBlock _reservedBlock;
|
||||||
|
private readonly List<MemoryBlock> _mappedBlocks;
|
||||||
|
private ulong _mappedBlockUsage;
|
||||||
|
|
||||||
|
private readonly ulong[] _mappedPageBitmap;
|
||||||
|
|
||||||
|
public MemoryBlock Block => _reservedBlock;
|
||||||
|
|
||||||
|
public SparseMemoryBlock(ulong size, PageInitDelegate pageInit, MemoryBlock fill)
|
||||||
|
{
|
||||||
|
_pageSize = MemoryBlock.GetPageSize();
|
||||||
|
_reservedBlock = new MemoryBlock(size, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.ViewCompatible);
|
||||||
|
_mappedBlocks = new List<MemoryBlock>();
|
||||||
|
_pageInit = pageInit;
|
||||||
|
|
||||||
|
int pages = (int)BitUtils.DivRoundUp(size, _pageSize);
|
||||||
|
int bitmapEntries = BitUtils.DivRoundUp(pages, 64);
|
||||||
|
_mappedPageBitmap = new ulong[bitmapEntries];
|
||||||
|
|
||||||
|
if (fill != null)
|
||||||
|
{
|
||||||
|
// Fill the block with mappings from the fill block.
|
||||||
|
|
||||||
|
if (fill.Size % _pageSize != 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Fill memory block should be page aligned.", nameof(fill));
|
||||||
|
}
|
||||||
|
|
||||||
|
int repeats = (int)BitUtils.DivRoundUp(size, fill.Size);
|
||||||
|
|
||||||
|
ulong offset = 0;
|
||||||
|
for (int i = 0; i < repeats; i++)
|
||||||
|
{
|
||||||
|
_reservedBlock.MapView(fill, 0, offset, Math.Min(fill.Size, size - offset));
|
||||||
|
offset += fill.Size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a fill block isn't provided, the pages that aren't EnsureMapped are unmapped.
|
||||||
|
// The caller can rely on signal handler to fill empty pages instead.
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MapPage(ulong pageOffset)
|
||||||
|
{
|
||||||
|
// Take a page from the latest mapped block.
|
||||||
|
MemoryBlock block = _mappedBlocks.LastOrDefault();
|
||||||
|
|
||||||
|
if (block == null || _mappedBlockUsage == MapGranularity)
|
||||||
|
{
|
||||||
|
// Need to map some more memory.
|
||||||
|
|
||||||
|
block = new MemoryBlock(MapGranularity, MemoryAllocationFlags.Mirrorable);
|
||||||
|
|
||||||
|
_mappedBlocks.Add(block);
|
||||||
|
|
||||||
|
_mappedBlockUsage = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pageInit(block.GetSpan(_mappedBlockUsage, (int)_pageSize));
|
||||||
|
_reservedBlock.MapView(block, _mappedBlockUsage, pageOffset, _pageSize);
|
||||||
|
|
||||||
|
_mappedBlockUsage += _pageSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnsureMapped(ulong offset)
|
||||||
|
{
|
||||||
|
int pageIndex = (int)(offset / _pageSize);
|
||||||
|
int bitmapIndex = pageIndex >> 6;
|
||||||
|
|
||||||
|
ref ulong entry = ref _mappedPageBitmap[bitmapIndex];
|
||||||
|
ulong bit = 1UL << (pageIndex & 63);
|
||||||
|
|
||||||
|
if ((Volatile.Read(ref entry) & bit) == 0)
|
||||||
|
{
|
||||||
|
// Not mapped.
|
||||||
|
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
// Check the bit while locked to make sure that this only happens once.
|
||||||
|
|
||||||
|
ulong lockedEntry = Volatile.Read(ref entry);
|
||||||
|
|
||||||
|
if ((lockedEntry & bit) == 0)
|
||||||
|
{
|
||||||
|
MapPage(offset & ~(_pageSize - 1));
|
||||||
|
|
||||||
|
lockedEntry |= bit;
|
||||||
|
|
||||||
|
Interlocked.Exchange(ref entry, lockedEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_reservedBlock.Dispose();
|
||||||
|
|
||||||
|
foreach (MemoryBlock block in _mappedBlocks)
|
||||||
|
{
|
||||||
|
block.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using ARMeilleure.Common;
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.State;
|
using ARMeilleure.State;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
@@ -12,7 +13,7 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
|
|
||||||
public CpuContext(IMemoryManager memory, bool for64Bit)
|
public CpuContext(IMemoryManager memory, bool for64Bit)
|
||||||
{
|
{
|
||||||
_translator = new Translator(new JitMemoryAllocator(), memory, for64Bit);
|
_translator = new Translator(new JitMemoryAllocator(), memory, AddressTable<ulong>.CreateForArm(for64Bit, memory.Type));
|
||||||
memory.UnmapEvent += UnmapHandler;
|
memory.UnmapEvent += UnmapHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using ARMeilleure.Common;
|
||||||
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Ryujinx.Cpu.Jit;
|
using Ryujinx.Cpu.Jit;
|
||||||
@@ -17,7 +19,10 @@ namespace Ryujinx.Tests.Cpu
|
|||||||
private static void EnsureTranslator()
|
private static void EnsureTranslator()
|
||||||
{
|
{
|
||||||
// Create a translator, as one is needed to register the signal handler or emit methods.
|
// Create a translator, as one is needed to register the signal handler or emit methods.
|
||||||
_translator ??= new Translator(new JitMemoryAllocator(), new MockMemoryManager(), true);
|
_translator ??= new Translator(
|
||||||
|
new JitMemoryAllocator(),
|
||||||
|
new MockMemoryManager(),
|
||||||
|
AddressTable<ulong>.CreateForArm(true, MemoryManagerType.SoftwarePageTable));
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
|
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using ARMeilleure.Common;
|
||||||
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.Signal;
|
using ARMeilleure.Signal;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
@@ -53,7 +55,10 @@ namespace Ryujinx.Tests.Memory
|
|||||||
private static void EnsureTranslator()
|
private static void EnsureTranslator()
|
||||||
{
|
{
|
||||||
// Create a translator, as one is needed to register the signal handler or emit methods.
|
// Create a translator, as one is needed to register the signal handler or emit methods.
|
||||||
_translator ??= new Translator(new JitMemoryAllocator(), new MockMemoryManager(), true);
|
_translator ??= new Translator(
|
||||||
|
new JitMemoryAllocator(),
|
||||||
|
new MockMemoryManager(),
|
||||||
|
AddressTable<ulong>.CreateForArm(true, MemoryManagerType.SoftwarePageTable));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Configuration.Hid;
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
using Ryujinx.Common.Configuration.Multiplayer;
|
using Ryujinx.Common.Configuration.Multiplayer;
|
||||||
@@ -16,7 +17,7 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current version of the file format
|
/// The current version of the file format
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int CurrentVersion = 56;
|
public const int CurrentVersion = 57;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Version of the configuration file format
|
/// Version of the configuration file format
|
||||||
@@ -191,8 +192,25 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enables or disables Vertical Sync
|
/// Enables or disables Vertical Sync
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>Kept for file format compatibility (to avoid possible failure when parsing configuration on old versions)</remarks>
|
||||||
|
/// TODO: Remove this when those older versions aren't in use anymore.
|
||||||
public bool EnableVsync { get; set; }
|
public bool EnableVsync { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current VSync mode; 60 (Switch), unbounded ("Vsync off"), or custom
|
||||||
|
/// </summary>
|
||||||
|
public VSyncMode VSyncMode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enables or disables the custom present interval
|
||||||
|
/// </summary>
|
||||||
|
public bool EnableCustomVSyncInterval { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The custom present interval value
|
||||||
|
/// </summary>
|
||||||
|
public int CustomVSyncInterval { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enables or disables Shader cache
|
/// Enables or disables Shader cache
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
|
|
||||||
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
||||||
{
|
{
|
||||||
ToggleVsync = Key.F1,
|
ToggleVSyncMode = Key.F1,
|
||||||
};
|
};
|
||||||
|
|
||||||
configurationFileUpdated = true;
|
configurationFileUpdated = true;
|
||||||
@@ -276,7 +276,7 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
|
|
||||||
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
||||||
{
|
{
|
||||||
ToggleVsync = Key.F1,
|
ToggleVSyncMode = Key.F1,
|
||||||
Screenshot = Key.F8,
|
Screenshot = Key.F8,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -289,7 +289,7 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
|
|
||||||
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
||||||
{
|
{
|
||||||
ToggleVsync = Key.F1,
|
ToggleVSyncMode = Key.F1,
|
||||||
Screenshot = Key.F8,
|
Screenshot = Key.F8,
|
||||||
ShowUI = Key.F4,
|
ShowUI = Key.F4,
|
||||||
};
|
};
|
||||||
@@ -332,7 +332,7 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
|
|
||||||
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
||||||
{
|
{
|
||||||
ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync,
|
ToggleVSyncMode = configurationFileFormat.Hotkeys.ToggleVSyncMode,
|
||||||
Screenshot = configurationFileFormat.Hotkeys.Screenshot,
|
Screenshot = configurationFileFormat.Hotkeys.Screenshot,
|
||||||
ShowUI = configurationFileFormat.Hotkeys.ShowUI,
|
ShowUI = configurationFileFormat.Hotkeys.ShowUI,
|
||||||
Pause = Key.F5,
|
Pause = Key.F5,
|
||||||
@@ -347,7 +347,7 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
|
|
||||||
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
||||||
{
|
{
|
||||||
ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync,
|
ToggleVSyncMode = configurationFileFormat.Hotkeys.ToggleVSyncMode,
|
||||||
Screenshot = configurationFileFormat.Hotkeys.Screenshot,
|
Screenshot = configurationFileFormat.Hotkeys.Screenshot,
|
||||||
ShowUI = configurationFileFormat.Hotkeys.ShowUI,
|
ShowUI = configurationFileFormat.Hotkeys.ShowUI,
|
||||||
Pause = configurationFileFormat.Hotkeys.Pause,
|
Pause = configurationFileFormat.Hotkeys.Pause,
|
||||||
@@ -421,7 +421,7 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
|
|
||||||
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
||||||
{
|
{
|
||||||
ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync,
|
ToggleVSyncMode = configurationFileFormat.Hotkeys.ToggleVSyncMode,
|
||||||
Screenshot = configurationFileFormat.Hotkeys.Screenshot,
|
Screenshot = configurationFileFormat.Hotkeys.Screenshot,
|
||||||
ShowUI = configurationFileFormat.Hotkeys.ShowUI,
|
ShowUI = configurationFileFormat.Hotkeys.ShowUI,
|
||||||
Pause = configurationFileFormat.Hotkeys.Pause,
|
Pause = configurationFileFormat.Hotkeys.Pause,
|
||||||
@@ -448,7 +448,7 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
|
|
||||||
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
||||||
{
|
{
|
||||||
ToggleVsync = configurationFileFormat.Hotkeys.ToggleVsync,
|
ToggleVSyncMode = configurationFileFormat.Hotkeys.ToggleVSyncMode,
|
||||||
Screenshot = configurationFileFormat.Hotkeys.Screenshot,
|
Screenshot = configurationFileFormat.Hotkeys.Screenshot,
|
||||||
ShowUI = configurationFileFormat.Hotkeys.ShowUI,
|
ShowUI = configurationFileFormat.Hotkeys.ShowUI,
|
||||||
Pause = configurationFileFormat.Hotkeys.Pause,
|
Pause = configurationFileFormat.Hotkeys.Pause,
|
||||||
@@ -611,6 +611,33 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
configurationFileUpdated = true;
|
configurationFileUpdated = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (configurationFileFormat.Version < 57)
|
||||||
|
{
|
||||||
|
Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 57.");
|
||||||
|
|
||||||
|
configurationFileFormat.VSyncMode = VSyncMode.Switch;
|
||||||
|
configurationFileFormat.EnableCustomVSyncInterval = false;
|
||||||
|
|
||||||
|
configurationFileFormat.Hotkeys = new KeyboardHotkeys
|
||||||
|
{
|
||||||
|
ToggleVSyncMode = Key.F1,
|
||||||
|
Screenshot = configurationFileFormat.Hotkeys.Screenshot,
|
||||||
|
ShowUI = configurationFileFormat.Hotkeys.ShowUI,
|
||||||
|
Pause = configurationFileFormat.Hotkeys.Pause,
|
||||||
|
ToggleMute = configurationFileFormat.Hotkeys.ToggleMute,
|
||||||
|
ResScaleUp = configurationFileFormat.Hotkeys.ResScaleUp,
|
||||||
|
ResScaleDown = configurationFileFormat.Hotkeys.ResScaleDown,
|
||||||
|
VolumeUp = configurationFileFormat.Hotkeys.VolumeUp,
|
||||||
|
VolumeDown = configurationFileFormat.Hotkeys.VolumeDown,
|
||||||
|
CustomVSyncIntervalIncrement = Key.Unbound,
|
||||||
|
CustomVSyncIntervalDecrement = Key.Unbound,
|
||||||
|
};
|
||||||
|
|
||||||
|
configurationFileFormat.CustomVSyncInterval = 120;
|
||||||
|
|
||||||
|
configurationFileUpdated = true;
|
||||||
|
}
|
||||||
|
|
||||||
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
|
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
|
||||||
Graphics.ResScale.Value = configurationFileFormat.ResScale;
|
Graphics.ResScale.Value = configurationFileFormat.ResScale;
|
||||||
Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom;
|
Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom;
|
||||||
@@ -646,7 +673,9 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
ShowTitleBar.Value = configurationFileFormat.ShowTitleBar;
|
ShowTitleBar.Value = configurationFileFormat.ShowTitleBar;
|
||||||
EnableHardwareAcceleration.Value = configurationFileFormat.EnableHardwareAcceleration;
|
EnableHardwareAcceleration.Value = configurationFileFormat.EnableHardwareAcceleration;
|
||||||
HideCursor.Value = configurationFileFormat.HideCursor;
|
HideCursor.Value = configurationFileFormat.HideCursor;
|
||||||
Graphics.EnableVsync.Value = configurationFileFormat.EnableVsync;
|
Graphics.VSyncMode.Value = configurationFileFormat.VSyncMode;
|
||||||
|
Graphics.EnableCustomVSyncInterval.Value = configurationFileFormat.EnableCustomVSyncInterval;
|
||||||
|
Graphics.CustomVSyncInterval.Value = configurationFileFormat.CustomVSyncInterval;
|
||||||
Graphics.EnableShaderCache.Value = configurationFileFormat.EnableShaderCache;
|
Graphics.EnableShaderCache.Value = configurationFileFormat.EnableShaderCache;
|
||||||
Graphics.EnableTextureRecompression.Value = configurationFileFormat.EnableTextureRecompression;
|
Graphics.EnableTextureRecompression.Value = configurationFileFormat.EnableTextureRecompression;
|
||||||
Graphics.EnableMacroHLE.Value = configurationFileFormat.EnableMacroHLE;
|
Graphics.EnableMacroHLE.Value = configurationFileFormat.EnableMacroHLE;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using ARMeilleure;
|
using ARMeilleure;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using Ryujinx.Common.Configuration.Hid;
|
using Ryujinx.Common.Configuration.Hid;
|
||||||
@@ -474,9 +474,19 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
public ReactiveObject<string> ShadersDumpPath { get; private set; }
|
public ReactiveObject<string> ShadersDumpPath { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enables or disables Vertical Sync
|
/// Toggles the present interval mode. Options are Switch (60Hz), Unbounded (previously Vsync off), and Custom, if enabled.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ReactiveObject<bool> EnableVsync { get; private set; }
|
public ReactiveObject<VSyncMode> VSyncMode { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enables or disables the custom present interval mode.
|
||||||
|
/// </summary>
|
||||||
|
public ReactiveObject<bool> EnableCustomVSyncInterval { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the custom present interval.
|
||||||
|
/// </summary>
|
||||||
|
public ReactiveObject<int> CustomVSyncInterval { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Enables or disables Shader cache
|
/// Enables or disables Shader cache
|
||||||
@@ -536,8 +546,12 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
AspectRatio = new ReactiveObject<AspectRatio>();
|
AspectRatio = new ReactiveObject<AspectRatio>();
|
||||||
AspectRatio.LogChangesToValue(nameof(AspectRatio));
|
AspectRatio.LogChangesToValue(nameof(AspectRatio));
|
||||||
ShadersDumpPath = new ReactiveObject<string>();
|
ShadersDumpPath = new ReactiveObject<string>();
|
||||||
EnableVsync = new ReactiveObject<bool>();
|
VSyncMode = new ReactiveObject<VSyncMode>();
|
||||||
EnableVsync.LogChangesToValue(nameof(EnableVsync));
|
VSyncMode.LogChangesToValue(nameof(VSyncMode));
|
||||||
|
EnableCustomVSyncInterval = new ReactiveObject<bool>();
|
||||||
|
EnableCustomVSyncInterval.LogChangesToValue(nameof(EnableCustomVSyncInterval));
|
||||||
|
CustomVSyncInterval = new ReactiveObject<int>();
|
||||||
|
CustomVSyncInterval.LogChangesToValue(nameof(CustomVSyncInterval));
|
||||||
EnableShaderCache = new ReactiveObject<bool>();
|
EnableShaderCache = new ReactiveObject<bool>();
|
||||||
EnableShaderCache.LogChangesToValue(nameof(EnableShaderCache));
|
EnableShaderCache.LogChangesToValue(nameof(EnableShaderCache));
|
||||||
EnableTextureRecompression = new ReactiveObject<bool>();
|
EnableTextureRecompression = new ReactiveObject<bool>();
|
||||||
|
|||||||
@@ -64,7 +64,9 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
ShowTitleBar = ShowTitleBar,
|
ShowTitleBar = ShowTitleBar,
|
||||||
EnableHardwareAcceleration = EnableHardwareAcceleration,
|
EnableHardwareAcceleration = EnableHardwareAcceleration,
|
||||||
HideCursor = HideCursor,
|
HideCursor = HideCursor,
|
||||||
EnableVsync = Graphics.EnableVsync,
|
VSyncMode = Graphics.VSyncMode,
|
||||||
|
EnableCustomVSyncInterval = Graphics.EnableCustomVSyncInterval,
|
||||||
|
CustomVSyncInterval = Graphics.CustomVSyncInterval,
|
||||||
EnableShaderCache = Graphics.EnableShaderCache,
|
EnableShaderCache = Graphics.EnableShaderCache,
|
||||||
EnableTextureRecompression = Graphics.EnableTextureRecompression,
|
EnableTextureRecompression = Graphics.EnableTextureRecompression,
|
||||||
EnableMacroHLE = Graphics.EnableMacroHLE,
|
EnableMacroHLE = Graphics.EnableMacroHLE,
|
||||||
@@ -179,7 +181,9 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
ShowTitleBar.Value = !OperatingSystem.IsWindows();
|
ShowTitleBar.Value = !OperatingSystem.IsWindows();
|
||||||
EnableHardwareAcceleration.Value = true;
|
EnableHardwareAcceleration.Value = true;
|
||||||
HideCursor.Value = HideCursorMode.OnIdle;
|
HideCursor.Value = HideCursorMode.OnIdle;
|
||||||
Graphics.EnableVsync.Value = true;
|
Graphics.VSyncMode.Value = VSyncMode.Switch;
|
||||||
|
Graphics.CustomVSyncInterval.Value = 120;
|
||||||
|
Graphics.EnableCustomVSyncInterval.Value = false;
|
||||||
Graphics.EnableShaderCache.Value = true;
|
Graphics.EnableShaderCache.Value = true;
|
||||||
Graphics.EnableTextureRecompression.Value = false;
|
Graphics.EnableTextureRecompression.Value = false;
|
||||||
Graphics.EnableMacroHLE.Value = true;
|
Graphics.EnableMacroHLE.Value = true;
|
||||||
@@ -240,7 +244,7 @@ namespace Ryujinx.UI.Common.Configuration
|
|||||||
Hid.EnableMouse.Value = false;
|
Hid.EnableMouse.Value = false;
|
||||||
Hid.Hotkeys.Value = new KeyboardHotkeys
|
Hid.Hotkeys.Value = new KeyboardHotkeys
|
||||||
{
|
{
|
||||||
ToggleVsync = Key.F1,
|
ToggleVSyncMode = Key.F1,
|
||||||
ToggleMute = Key.F2,
|
ToggleMute = Key.F2,
|
||||||
Screenshot = Key.F8,
|
Screenshot = Key.F8,
|
||||||
ShowUI = Key.F4,
|
ShowUI = Key.F4,
|
||||||
|
|||||||
@@ -247,6 +247,7 @@ namespace Ryujinx.UI.Common
|
|||||||
"0100dbf01000a000", // Burnout Paradise Remastered
|
"0100dbf01000a000", // Burnout Paradise Remastered
|
||||||
"0100744001588000", // Cars 3: Driven to Win
|
"0100744001588000", // Cars 3: Driven to Win
|
||||||
"0100b41013c82000", // Cruis'n Blast
|
"0100b41013c82000", // Cruis'n Blast
|
||||||
|
"01001b300b9be000", // Diablo III: Eternal Collection
|
||||||
"01008c8012920000", // Dying Light Platinum Edition
|
"01008c8012920000", // Dying Light Platinum Edition
|
||||||
"010073c01af34000", // LEGO Horizon Adventures
|
"010073c01af34000", // LEGO Horizon Adventures
|
||||||
"0100770008dd8000", // Monster Hunter Generations Ultimate
|
"0100770008dd8000", // Monster Hunter Generations Ultimate
|
||||||
@@ -264,6 +265,7 @@ namespace Ryujinx.UI.Common
|
|||||||
"0100800015926000", // Suika Game
|
"0100800015926000", // Suika Game
|
||||||
"0100e46006708000", // Terraria
|
"0100e46006708000", // Terraria
|
||||||
"01000a10041ea000", // The Elder Scrolls V: Skyrim
|
"01000a10041ea000", // The Elder Scrolls V: Skyrim
|
||||||
|
"010057a01e4d4000", // TSUKIHIME -A piece of blue glass moon-
|
||||||
"010080b00ad66000", // Undertale
|
"010080b00ad66000", // Undertale
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ using Key = Ryujinx.Input.Key;
|
|||||||
using MouseButton = Ryujinx.Input.MouseButton;
|
using MouseButton = Ryujinx.Input.MouseButton;
|
||||||
using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter;
|
using ScalingFilter = Ryujinx.Common.Configuration.ScalingFilter;
|
||||||
using Size = Avalonia.Size;
|
using Size = Avalonia.Size;
|
||||||
|
using Switch = Ryujinx.HLE.Switch;
|
||||||
|
using VSyncMode = Ryujinx.Common.Configuration.VSyncMode;
|
||||||
|
|
||||||
namespace Ryujinx.Ava
|
namespace Ryujinx.Ava
|
||||||
{
|
{
|
||||||
@@ -203,6 +205,9 @@ namespace Ryujinx.Ava
|
|||||||
ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter;
|
ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter;
|
||||||
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel;
|
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel;
|
||||||
ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event += UpdateColorSpacePassthrough;
|
ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event += UpdateColorSpacePassthrough;
|
||||||
|
ConfigurationState.Instance.Graphics.VSyncMode.Event += UpdateVSyncMode;
|
||||||
|
ConfigurationState.Instance.Graphics.CustomVSyncInterval.Event += UpdateCustomVSyncIntervalValue;
|
||||||
|
ConfigurationState.Instance.Graphics.EnableCustomVSyncInterval.Event += UpdateCustomVSyncIntervalEnabled;
|
||||||
|
|
||||||
ConfigurationState.Instance.System.EnableInternetAccess.Event += UpdateEnableInternetAccessState;
|
ConfigurationState.Instance.System.EnableInternetAccess.Event += UpdateEnableInternetAccessState;
|
||||||
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState;
|
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState;
|
||||||
@@ -295,6 +300,66 @@ namespace Ryujinx.Ava
|
|||||||
_renderer.Window?.SetColorSpacePassthrough((bool)ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Value);
|
_renderer.Window?.SetColorSpacePassthrough((bool)ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void UpdateVSyncMode(object sender, ReactiveEventArgs<VSyncMode> e)
|
||||||
|
{
|
||||||
|
if (Device != null)
|
||||||
|
{
|
||||||
|
Device.VSyncMode = e.NewValue;
|
||||||
|
Device.UpdateVSyncInterval();
|
||||||
|
}
|
||||||
|
_renderer.Window?.ChangeVSyncMode((Ryujinx.Graphics.GAL.VSyncMode)e.NewValue);
|
||||||
|
|
||||||
|
_viewModel.ShowCustomVSyncIntervalPicker = (e.NewValue == VSyncMode.Custom);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void VSyncModeToggle()
|
||||||
|
{
|
||||||
|
VSyncMode oldVSyncMode = Device.VSyncMode;
|
||||||
|
VSyncMode newVSyncMode = VSyncMode.Switch;
|
||||||
|
bool customVSyncIntervalEnabled = ConfigurationState.Instance.Graphics.EnableCustomVSyncInterval.Value;
|
||||||
|
|
||||||
|
switch (oldVSyncMode)
|
||||||
|
{
|
||||||
|
case VSyncMode.Switch:
|
||||||
|
newVSyncMode = VSyncMode.Unbounded;
|
||||||
|
break;
|
||||||
|
case VSyncMode.Unbounded:
|
||||||
|
if (customVSyncIntervalEnabled)
|
||||||
|
{
|
||||||
|
newVSyncMode = VSyncMode.Custom;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newVSyncMode = VSyncMode.Switch;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case VSyncMode.Custom:
|
||||||
|
newVSyncMode = VSyncMode.Switch;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateVSyncMode(this, new ReactiveEventArgs<VSyncMode>(oldVSyncMode, newVSyncMode));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateCustomVSyncIntervalValue(object sender, ReactiveEventArgs<int> e)
|
||||||
|
{
|
||||||
|
if (Device != null)
|
||||||
|
{
|
||||||
|
Device.TargetVSyncInterval = e.NewValue;
|
||||||
|
Device.UpdateVSyncInterval();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateCustomVSyncIntervalEnabled(object sender, ReactiveEventArgs<bool> e)
|
||||||
|
{
|
||||||
|
if (Device != null)
|
||||||
|
{
|
||||||
|
Device.CustomVSyncIntervalEnabled = e.NewValue;
|
||||||
|
Device.UpdateVSyncInterval();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ShowCursor()
|
private void ShowCursor()
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(() =>
|
Dispatcher.UIThread.Post(() =>
|
||||||
@@ -352,11 +417,7 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
string filename = $"{sanitizedApplicationName}_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png";
|
string filename = $"{sanitizedApplicationName}_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png";
|
||||||
|
|
||||||
string directory = AppDataManager.Mode switch
|
string directory = Path.Combine(AppDataManager.BaseDirPath, "screenshots");
|
||||||
{
|
|
||||||
AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => Path.Combine(AppDataManager.BaseDirPath, "screenshots"),
|
|
||||||
_ => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx"),
|
|
||||||
};
|
|
||||||
|
|
||||||
string path = Path.Combine(directory, filename);
|
string path = Path.Combine(directory, filename);
|
||||||
|
|
||||||
@@ -509,12 +570,6 @@ namespace Ryujinx.Ava
|
|||||||
Device.Configuration.MultiplayerDisableP2p = e.NewValue;
|
Device.Configuration.MultiplayerDisableP2p = e.NewValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ToggleVSync()
|
|
||||||
{
|
|
||||||
Device.EnableDeviceVsync = !Device.EnableDeviceVsync;
|
|
||||||
_renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Stop()
|
public void Stop()
|
||||||
{
|
{
|
||||||
_isActive = false;
|
_isActive = false;
|
||||||
@@ -868,7 +923,7 @@ namespace Ryujinx.Ava
|
|||||||
_viewModel.UiHandler,
|
_viewModel.UiHandler,
|
||||||
(SystemLanguage)ConfigurationState.Instance.System.Language.Value,
|
(SystemLanguage)ConfigurationState.Instance.System.Language.Value,
|
||||||
(RegionCode)ConfigurationState.Instance.System.Region.Value,
|
(RegionCode)ConfigurationState.Instance.System.Region.Value,
|
||||||
ConfigurationState.Instance.Graphics.EnableVsync,
|
ConfigurationState.Instance.Graphics.VSyncMode,
|
||||||
ConfigurationState.Instance.System.EnableDockedMode,
|
ConfigurationState.Instance.System.EnableDockedMode,
|
||||||
ConfigurationState.Instance.System.EnablePtc,
|
ConfigurationState.Instance.System.EnablePtc,
|
||||||
ConfigurationState.Instance.System.EnableInternetAccess,
|
ConfigurationState.Instance.System.EnableInternetAccess,
|
||||||
@@ -885,7 +940,8 @@ namespace Ryujinx.Ava
|
|||||||
ConfigurationState.Instance.Multiplayer.Mode,
|
ConfigurationState.Instance.Multiplayer.Mode,
|
||||||
ConfigurationState.Instance.Multiplayer.DisableP2p,
|
ConfigurationState.Instance.Multiplayer.DisableP2p,
|
||||||
ConfigurationState.Instance.Multiplayer.LdnPassphrase,
|
ConfigurationState.Instance.Multiplayer.LdnPassphrase,
|
||||||
ConfigurationState.Instance.Multiplayer.LdnServer));
|
ConfigurationState.Instance.Multiplayer.LdnServer,
|
||||||
|
ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IHardwareDeviceDriver InitializeAudio()
|
private static IHardwareDeviceDriver InitializeAudio()
|
||||||
@@ -1006,7 +1062,7 @@ namespace Ryujinx.Ava
|
|||||||
Device.Gpu.SetGpuThread();
|
Device.Gpu.SetGpuThread();
|
||||||
Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token);
|
Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token);
|
||||||
|
|
||||||
_renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync);
|
_renderer.Window.ChangeVSyncMode((Ryujinx.Graphics.GAL.VSyncMode)Device.VSyncMode);
|
||||||
|
|
||||||
while (_isActive)
|
while (_isActive)
|
||||||
{
|
{
|
||||||
@@ -1067,6 +1123,7 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
// Run a status update only when a frame is to be drawn. This prevents from updating the ui and wasting a render when no frame is queued.
|
// Run a status update only when a frame is to be drawn. This prevents from updating the ui and wasting a render when no frame is queued.
|
||||||
string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance[LocaleKeys.Docked] : LocaleManager.Instance[LocaleKeys.Handheld];
|
string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance[LocaleKeys.Docked] : LocaleManager.Instance[LocaleKeys.Handheld];
|
||||||
|
string vSyncMode = Device.VSyncMode.ToString();
|
||||||
|
|
||||||
UpdateShaderCount();
|
UpdateShaderCount();
|
||||||
|
|
||||||
@@ -1076,7 +1133,7 @@ namespace Ryujinx.Ava
|
|||||||
}
|
}
|
||||||
|
|
||||||
StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs(
|
StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs(
|
||||||
Device.EnableDeviceVsync,
|
vSyncMode,
|
||||||
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(),
|
||||||
@@ -1179,8 +1236,16 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
switch (currentHotkeyState)
|
switch (currentHotkeyState)
|
||||||
{
|
{
|
||||||
case KeyboardHotkeyState.ToggleVSync:
|
case KeyboardHotkeyState.ToggleVSyncMode:
|
||||||
ToggleVSync();
|
VSyncModeToggle();
|
||||||
|
break;
|
||||||
|
case KeyboardHotkeyState.CustomVSyncIntervalDecrement:
|
||||||
|
Device.DecrementCustomVSyncInterval();
|
||||||
|
_viewModel.CustomVSyncInterval -= 1;
|
||||||
|
break;
|
||||||
|
case KeyboardHotkeyState.CustomVSyncIntervalIncrement:
|
||||||
|
Device.IncrementCustomVSyncInterval();
|
||||||
|
_viewModel.CustomVSyncInterval += 1;
|
||||||
break;
|
break;
|
||||||
case KeyboardHotkeyState.Screenshot:
|
case KeyboardHotkeyState.Screenshot:
|
||||||
ScreenshotRequested = true;
|
ScreenshotRequested = true;
|
||||||
@@ -1267,9 +1332,9 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
KeyboardHotkeyState state = KeyboardHotkeyState.None;
|
KeyboardHotkeyState state = KeyboardHotkeyState.None;
|
||||||
|
|
||||||
if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVsync))
|
if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVSyncMode))
|
||||||
{
|
{
|
||||||
state = KeyboardHotkeyState.ToggleVSync;
|
state = KeyboardHotkeyState.ToggleVSyncMode;
|
||||||
}
|
}
|
||||||
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot))
|
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot))
|
||||||
{
|
{
|
||||||
@@ -1303,6 +1368,14 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
state = KeyboardHotkeyState.VolumeDown;
|
state = KeyboardHotkeyState.VolumeDown;
|
||||||
}
|
}
|
||||||
|
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CustomVSyncIntervalIncrement))
|
||||||
|
{
|
||||||
|
state = KeyboardHotkeyState.CustomVSyncIntervalIncrement;
|
||||||
|
}
|
||||||
|
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.CustomVSyncIntervalDecrement))
|
||||||
|
{
|
||||||
|
state = KeyboardHotkeyState.CustomVSyncIntervalDecrement;
|
||||||
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Language": "اَلْعَرَبِيَّةُ",
|
"Language": "اَلْعَرَبِيَّةُ",
|
||||||
"MenuBarFileOpenApplet": "فتح التطبيق المصغر",
|
"MenuBarFileOpenApplet": "فتح التطبيق المصغر",
|
||||||
|
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "افتح تطبيق تحرير Mii في الوضع المستقل",
|
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "افتح تطبيق تحرير Mii في الوضع المستقل",
|
||||||
"SettingsTabInputDirectMouseAccess": "الوصول المباشر للفأرة",
|
"SettingsTabInputDirectMouseAccess": "الوصول المباشر للفأرة",
|
||||||
"SettingsTabSystemMemoryManagerMode": "وضع إدارة الذاكرة:",
|
"SettingsTabSystemMemoryManagerMode": "وضع إدارة الذاكرة:",
|
||||||
@@ -457,6 +458,7 @@
|
|||||||
"DialogUpdaterExtractionMessage": "جاري استخراج التحديث...",
|
"DialogUpdaterExtractionMessage": "جاري استخراج التحديث...",
|
||||||
"DialogUpdaterRenamingMessage": "إعادة تسمية التحديث...",
|
"DialogUpdaterRenamingMessage": "إعادة تسمية التحديث...",
|
||||||
"DialogUpdaterAddingFilesMessage": "إضافة تحديث جديد...",
|
"DialogUpdaterAddingFilesMessage": "إضافة تحديث جديد...",
|
||||||
|
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||||
"DialogUpdaterCompleteMessage": "اكتمل التحديث",
|
"DialogUpdaterCompleteMessage": "اكتمل التحديث",
|
||||||
"DialogUpdaterRestartMessage": "هل تريد إعادة تشغيل ريوجينكس الآن؟",
|
"DialogUpdaterRestartMessage": "هل تريد إعادة تشغيل ريوجينكس الآن؟",
|
||||||
"DialogUpdaterNoInternetMessage": "أنت غير متصل بالإنترنت.",
|
"DialogUpdaterNoInternetMessage": "أنت غير متصل بالإنترنت.",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Language": "Deutsch",
|
"Language": "Deutsch",
|
||||||
"MenuBarFileOpenApplet": "Öffne Anwendung",
|
"MenuBarFileOpenApplet": "Öffne Anwendung",
|
||||||
|
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Öffnet das Mii-Editor-Applet im Standalone-Modus",
|
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Öffnet das Mii-Editor-Applet im Standalone-Modus",
|
||||||
"SettingsTabInputDirectMouseAccess": "Direkter Mauszugriff",
|
"SettingsTabInputDirectMouseAccess": "Direkter Mauszugriff",
|
||||||
"SettingsTabSystemMemoryManagerMode": "Speichermanagermodus:",
|
"SettingsTabSystemMemoryManagerMode": "Speichermanagermodus:",
|
||||||
@@ -457,6 +458,7 @@
|
|||||||
"DialogUpdaterExtractionMessage": "Update wird entpackt...",
|
"DialogUpdaterExtractionMessage": "Update wird entpackt...",
|
||||||
"DialogUpdaterRenamingMessage": "Update wird umbenannt...",
|
"DialogUpdaterRenamingMessage": "Update wird umbenannt...",
|
||||||
"DialogUpdaterAddingFilesMessage": "Update wird hinzugefügt...",
|
"DialogUpdaterAddingFilesMessage": "Update wird hinzugefügt...",
|
||||||
|
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||||
"DialogUpdaterCompleteMessage": "Update abgeschlossen!",
|
"DialogUpdaterCompleteMessage": "Update abgeschlossen!",
|
||||||
"DialogUpdaterRestartMessage": "Ryujinx jetzt neu starten?",
|
"DialogUpdaterRestartMessage": "Ryujinx jetzt neu starten?",
|
||||||
"DialogUpdaterNoInternetMessage": "Es besteht keine Verbindung mit dem Internet!",
|
"DialogUpdaterNoInternetMessage": "Es besteht keine Verbindung mit dem Internet!",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Language": "Ελληνικά",
|
"Language": "Ελληνικά",
|
||||||
"MenuBarFileOpenApplet": "Άνοιγμα Applet",
|
"MenuBarFileOpenApplet": "Άνοιγμα Applet",
|
||||||
|
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Άνοιγμα του Mii Editor Applet σε Αυτόνομη λειτουργία",
|
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Άνοιγμα του Mii Editor Applet σε Αυτόνομη λειτουργία",
|
||||||
"SettingsTabInputDirectMouseAccess": "Άμεση Πρόσβαση Ποντικιού",
|
"SettingsTabInputDirectMouseAccess": "Άμεση Πρόσβαση Ποντικιού",
|
||||||
"SettingsTabSystemMemoryManagerMode": "Λειτουργία Διαχείρισης Μνήμης:",
|
"SettingsTabSystemMemoryManagerMode": "Λειτουργία Διαχείρισης Μνήμης:",
|
||||||
@@ -457,6 +458,7 @@
|
|||||||
"DialogUpdaterExtractionMessage": "Εξαγωγή Ενημέρωσης...",
|
"DialogUpdaterExtractionMessage": "Εξαγωγή Ενημέρωσης...",
|
||||||
"DialogUpdaterRenamingMessage": "Μετονομασία Ενημέρωσης...",
|
"DialogUpdaterRenamingMessage": "Μετονομασία Ενημέρωσης...",
|
||||||
"DialogUpdaterAddingFilesMessage": "Προσθήκη Νέας Ενημέρωσης...",
|
"DialogUpdaterAddingFilesMessage": "Προσθήκη Νέας Ενημέρωσης...",
|
||||||
|
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||||
"DialogUpdaterCompleteMessage": "Η Ενημέρωση Ολοκληρώθηκε!",
|
"DialogUpdaterCompleteMessage": "Η Ενημέρωση Ολοκληρώθηκε!",
|
||||||
"DialogUpdaterRestartMessage": "Θέλετε να επανεκκινήσετε το Ryujinx τώρα;",
|
"DialogUpdaterRestartMessage": "Θέλετε να επανεκκινήσετε το Ryujinx τώρα;",
|
||||||
"DialogUpdaterNoInternetMessage": "Δεν είστε συνδεδεμένοι στο Διαδίκτυο!",
|
"DialogUpdaterNoInternetMessage": "Δεν είστε συνδεδεμένοι στο Διαδίκτυο!",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Language": "English (US)",
|
"Language": "English (US)",
|
||||||
"MenuBarFileOpenApplet": "Open Applet",
|
"MenuBarFileOpenApplet": "Open Applet",
|
||||||
|
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Open Mii Editor Applet in Standalone mode",
|
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Open Mii Editor Applet in Standalone mode",
|
||||||
"SettingsTabInputDirectMouseAccess": "Direct Mouse Access",
|
"SettingsTabInputDirectMouseAccess": "Direct Mouse Access",
|
||||||
"SettingsTabSystemMemoryManagerMode": "Memory Manager Mode:",
|
"SettingsTabSystemMemoryManagerMode": "Memory Manager Mode:",
|
||||||
@@ -142,9 +143,20 @@
|
|||||||
"SettingsTabSystemSystemLanguageLatinAmericanSpanish": "Latin American Spanish",
|
"SettingsTabSystemSystemLanguageLatinAmericanSpanish": "Latin American Spanish",
|
||||||
"SettingsTabSystemSystemLanguageSimplifiedChinese": "Simplified Chinese",
|
"SettingsTabSystemSystemLanguageSimplifiedChinese": "Simplified Chinese",
|
||||||
"SettingsTabSystemSystemLanguageTraditionalChinese": "Traditional Chinese",
|
"SettingsTabSystemSystemLanguageTraditionalChinese": "Traditional Chinese",
|
||||||
"SettingsTabSystemSystemTimeZone": "System TimeZone:",
|
"SettingsTabSystemSystemTimeZone": "System Time Zone:",
|
||||||
"SettingsTabSystemSystemTime": "System Time:",
|
"SettingsTabSystemSystemTime": "System Time:",
|
||||||
"SettingsTabSystemEnableVsync": "VSync",
|
"SettingsTabSystemVSyncMode": "VSync:",
|
||||||
|
"SettingsTabSystemEnableCustomVSyncInterval": "Enable custom refresh rate (Experimental)",
|
||||||
|
"SettingsTabSystemVSyncModeSwitch": "Switch",
|
||||||
|
"SettingsTabSystemVSyncModeUnbounded": "Unbounded",
|
||||||
|
"SettingsTabSystemVSyncModeCustom": "Custom Refresh Rate",
|
||||||
|
"SettingsTabSystemVSyncModeTooltip": "Emulated Vertical Sync. 'Switch' emulates the Switch's refresh rate of 60Hz. 'Unbounded' is an unbounded refresh rate.",
|
||||||
|
"SettingsTabSystemVSyncModeTooltipCustom": "Emulated Vertical Sync. 'Switch' emulates the Switch's refresh rate of 60Hz. 'Unbounded' is an unbounded refresh rate. 'Custom' emulates the specified custom refresh rate.",
|
||||||
|
"SettingsTabSystemEnableCustomVSyncIntervalTooltip": "Allows the user to specify an emulated refresh rate. In some titles, this may speed up or slow down the rate of gameplay logic. In other titles, it may allow for capping FPS at some multiple of the refresh rate, or lead to unpredictable behavior. This is an experimental feature, with no guarantees for how gameplay will be affected. \n\nLeave OFF if unsure.",
|
||||||
|
"SettingsTabSystemCustomVSyncIntervalValueTooltip": "The custom refresh rate target value.",
|
||||||
|
"SettingsTabSystemCustomVSyncIntervalSliderTooltip": "The custom refresh rate, as a percentage of the normal Switch refresh rate.",
|
||||||
|
"SettingsTabSystemCustomVSyncIntervalPercentage": "Custom Refresh Rate %:",
|
||||||
|
"SettingsTabSystemCustomVSyncIntervalValue": "Custom Refresh Rate Value:",
|
||||||
"SettingsTabSystemEnablePptc": "PPTC (Profiled Persistent Translation Cache)",
|
"SettingsTabSystemEnablePptc": "PPTC (Profiled Persistent Translation Cache)",
|
||||||
"SettingsTabSystemEnableLowPowerPptc": "Low-power PPTC cache",
|
"SettingsTabSystemEnableLowPowerPptc": "Low-power PPTC cache",
|
||||||
"SettingsTabSystemEnableFsIntegrityChecks": "FS Integrity Checks",
|
"SettingsTabSystemEnableFsIntegrityChecks": "FS Integrity Checks",
|
||||||
@@ -153,6 +165,7 @@
|
|||||||
"SettingsTabSystemAudioBackendOpenAL": "OpenAL",
|
"SettingsTabSystemAudioBackendOpenAL": "OpenAL",
|
||||||
"SettingsTabSystemAudioBackendSoundIO": "SoundIO",
|
"SettingsTabSystemAudioBackendSoundIO": "SoundIO",
|
||||||
"SettingsTabSystemAudioBackendSDL2": "SDL2",
|
"SettingsTabSystemAudioBackendSDL2": "SDL2",
|
||||||
|
"SettingsTabSystemCustomVSyncInterval": "Interval",
|
||||||
"SettingsTabSystemHacks": "Hacks",
|
"SettingsTabSystemHacks": "Hacks",
|
||||||
"SettingsTabSystemHacksNote": "May cause instability",
|
"SettingsTabSystemHacksNote": "May cause instability",
|
||||||
"SettingsTabSystemDramSize": "DRAM size:",
|
"SettingsTabSystemDramSize": "DRAM size:",
|
||||||
@@ -457,6 +470,7 @@
|
|||||||
"DialogUpdaterExtractionMessage": "Extracting Update...",
|
"DialogUpdaterExtractionMessage": "Extracting Update...",
|
||||||
"DialogUpdaterRenamingMessage": "Renaming Update...",
|
"DialogUpdaterRenamingMessage": "Renaming Update...",
|
||||||
"DialogUpdaterAddingFilesMessage": "Adding New Update...",
|
"DialogUpdaterAddingFilesMessage": "Adding New Update...",
|
||||||
|
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||||
"DialogUpdaterCompleteMessage": "Update Complete!",
|
"DialogUpdaterCompleteMessage": "Update Complete!",
|
||||||
"DialogUpdaterRestartMessage": "Do you want to restart Ryujinx now?",
|
"DialogUpdaterRestartMessage": "Do you want to restart Ryujinx now?",
|
||||||
"DialogUpdaterNoInternetMessage": "You are not connected to the Internet!",
|
"DialogUpdaterNoInternetMessage": "You are not connected to the Internet!",
|
||||||
@@ -719,11 +733,13 @@
|
|||||||
"RyujinxUpdater": "Ryujinx Updater",
|
"RyujinxUpdater": "Ryujinx Updater",
|
||||||
"SettingsTabHotkeys": "Keyboard Hotkeys",
|
"SettingsTabHotkeys": "Keyboard Hotkeys",
|
||||||
"SettingsTabHotkeysHotkeys": "Keyboard Hotkeys",
|
"SettingsTabHotkeysHotkeys": "Keyboard Hotkeys",
|
||||||
"SettingsTabHotkeysToggleVsyncHotkey": "Toggle VSync:",
|
"SettingsTabHotkeysToggleVSyncModeHotkey": "Toggle VSync mode:",
|
||||||
"SettingsTabHotkeysScreenshotHotkey": "Screenshot:",
|
"SettingsTabHotkeysScreenshotHotkey": "Screenshot:",
|
||||||
"SettingsTabHotkeysShowUiHotkey": "Show UI:",
|
"SettingsTabHotkeysShowUiHotkey": "Show UI:",
|
||||||
"SettingsTabHotkeysPauseHotkey": "Pause:",
|
"SettingsTabHotkeysPauseHotkey": "Pause:",
|
||||||
"SettingsTabHotkeysToggleMuteHotkey": "Mute:",
|
"SettingsTabHotkeysToggleMuteHotkey": "Mute:",
|
||||||
|
"SettingsTabHotkeysIncrementCustomVSyncIntervalHotkey": "Raise custom refresh rate",
|
||||||
|
"SettingsTabHotkeysDecrementCustomVSyncIntervalHotkey": "Lower custom refresh rate",
|
||||||
"ControllerMotionTitle": "Motion Control Settings",
|
"ControllerMotionTitle": "Motion Control Settings",
|
||||||
"ControllerRumbleTitle": "Rumble Settings",
|
"ControllerRumbleTitle": "Rumble Settings",
|
||||||
"SettingsSelectThemeFileDialogTitle": "Select Theme File",
|
"SettingsSelectThemeFileDialogTitle": "Select Theme File",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Language": "Español (ES)",
|
"Language": "Español (ES)",
|
||||||
"MenuBarFileOpenApplet": "Abrir applet",
|
"MenuBarFileOpenApplet": "Abrir applet",
|
||||||
|
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Abre el editor de Mii en modo autónomo",
|
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Abre el editor de Mii en modo autónomo",
|
||||||
"SettingsTabInputDirectMouseAccess": "Acceso directo al ratón",
|
"SettingsTabInputDirectMouseAccess": "Acceso directo al ratón",
|
||||||
"SettingsTabSystemMemoryManagerMode": "Modo del administrador de memoria:",
|
"SettingsTabSystemMemoryManagerMode": "Modo del administrador de memoria:",
|
||||||
@@ -457,6 +458,7 @@
|
|||||||
"DialogUpdaterExtractionMessage": "Extrayendo actualización...",
|
"DialogUpdaterExtractionMessage": "Extrayendo actualización...",
|
||||||
"DialogUpdaterRenamingMessage": "Renombrando actualización...",
|
"DialogUpdaterRenamingMessage": "Renombrando actualización...",
|
||||||
"DialogUpdaterAddingFilesMessage": "Aplicando actualización...",
|
"DialogUpdaterAddingFilesMessage": "Aplicando actualización...",
|
||||||
|
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||||
"DialogUpdaterCompleteMessage": "¡Actualización completa!",
|
"DialogUpdaterCompleteMessage": "¡Actualización completa!",
|
||||||
"DialogUpdaterRestartMessage": "¿Quieres reiniciar Ryujinx?",
|
"DialogUpdaterRestartMessage": "¿Quieres reiniciar Ryujinx?",
|
||||||
"DialogUpdaterNoInternetMessage": "¡No estás conectado a internet!",
|
"DialogUpdaterNoInternetMessage": "¡No estás conectado a internet!",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Language": "Français",
|
"Language": "Français",
|
||||||
"MenuBarFileOpenApplet": "Ouvrir un programme",
|
"MenuBarFileOpenApplet": "Ouvrir un programme",
|
||||||
|
"MenuBarFileOpenAppletOpenMiiApplet": "Éditeur de Mii",
|
||||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Ouvrir l'éditeur Mii en mode Standalone",
|
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Ouvrir l'éditeur Mii en mode Standalone",
|
||||||
"SettingsTabInputDirectMouseAccess": "Accès direct à la souris",
|
"SettingsTabInputDirectMouseAccess": "Accès direct à la souris",
|
||||||
"SettingsTabSystemMemoryManagerMode": "Mode de gestion de la mémoire :",
|
"SettingsTabSystemMemoryManagerMode": "Mode de gestion de la mémoire :",
|
||||||
@@ -457,6 +458,7 @@
|
|||||||
"DialogUpdaterExtractionMessage": "Extraction de la mise à jour…",
|
"DialogUpdaterExtractionMessage": "Extraction de la mise à jour…",
|
||||||
"DialogUpdaterRenamingMessage": "Renommage de la mise à jour...",
|
"DialogUpdaterRenamingMessage": "Renommage de la mise à jour...",
|
||||||
"DialogUpdaterAddingFilesMessage": "Ajout d'une nouvelle mise à jour...",
|
"DialogUpdaterAddingFilesMessage": "Ajout d'une nouvelle mise à jour...",
|
||||||
|
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||||
"DialogUpdaterCompleteMessage": "Mise à jour terminée !",
|
"DialogUpdaterCompleteMessage": "Mise à jour terminée !",
|
||||||
"DialogUpdaterRestartMessage": "Voulez-vous redémarrer Ryujinx maintenant ?",
|
"DialogUpdaterRestartMessage": "Voulez-vous redémarrer Ryujinx maintenant ?",
|
||||||
"DialogUpdaterNoInternetMessage": "Vous n'êtes pas connecté à Internet !",
|
"DialogUpdaterNoInternetMessage": "Vous n'êtes pas connecté à Internet !",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Language": "עִברִית",
|
"Language": "עִברִית",
|
||||||
"MenuBarFileOpenApplet": "פתח יישומון",
|
"MenuBarFileOpenApplet": "פתח יישומון",
|
||||||
|
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "פתח את יישומון עורך ה- Mii במצב עצמאי",
|
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "פתח את יישומון עורך ה- Mii במצב עצמאי",
|
||||||
"SettingsTabInputDirectMouseAccess": "גישה ישירה לעכבר",
|
"SettingsTabInputDirectMouseAccess": "גישה ישירה לעכבר",
|
||||||
"SettingsTabSystemMemoryManagerMode": "מצב מנהל זיכרון:",
|
"SettingsTabSystemMemoryManagerMode": "מצב מנהל זיכרון:",
|
||||||
@@ -457,6 +458,7 @@
|
|||||||
"DialogUpdaterExtractionMessage": "מחלץ עדכון...",
|
"DialogUpdaterExtractionMessage": "מחלץ עדכון...",
|
||||||
"DialogUpdaterRenamingMessage": "משנה את שם העדכון...",
|
"DialogUpdaterRenamingMessage": "משנה את שם העדכון...",
|
||||||
"DialogUpdaterAddingFilesMessage": "מוסיף עדכון חדש...",
|
"DialogUpdaterAddingFilesMessage": "מוסיף עדכון חדש...",
|
||||||
|
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||||
"DialogUpdaterCompleteMessage": "העדכון הושלם!",
|
"DialogUpdaterCompleteMessage": "העדכון הושלם!",
|
||||||
"DialogUpdaterRestartMessage": "האם אתם רוצים להפעיל מחדש את ריוג'ינקס עכשיו?",
|
"DialogUpdaterRestartMessage": "האם אתם רוצים להפעיל מחדש את ריוג'ינקס עכשיו?",
|
||||||
"DialogUpdaterNoInternetMessage": "אתם לא מחוברים לאינטרנט!",
|
"DialogUpdaterNoInternetMessage": "אתם לא מחוברים לאינטרנט!",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Language": "Italiano",
|
"Language": "Italiano",
|
||||||
"MenuBarFileOpenApplet": "Apri applet",
|
"MenuBarFileOpenApplet": "Apri applet",
|
||||||
|
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Apri l'applet Mii Editor in modalità Standalone",
|
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Apri l'applet Mii Editor in modalità Standalone",
|
||||||
"SettingsTabInputDirectMouseAccess": "Accesso diretto al mouse",
|
"SettingsTabInputDirectMouseAccess": "Accesso diretto al mouse",
|
||||||
"SettingsTabSystemMemoryManagerMode": "Modalità di gestione della memoria:",
|
"SettingsTabSystemMemoryManagerMode": "Modalità di gestione della memoria:",
|
||||||
@@ -457,6 +458,7 @@
|
|||||||
"DialogUpdaterExtractionMessage": "Estrazione dell'aggiornamento...",
|
"DialogUpdaterExtractionMessage": "Estrazione dell'aggiornamento...",
|
||||||
"DialogUpdaterRenamingMessage": "Rinominazione dell'aggiornamento...",
|
"DialogUpdaterRenamingMessage": "Rinominazione dell'aggiornamento...",
|
||||||
"DialogUpdaterAddingFilesMessage": "Aggiunta del nuovo aggiornamento...",
|
"DialogUpdaterAddingFilesMessage": "Aggiunta del nuovo aggiornamento...",
|
||||||
|
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||||
"DialogUpdaterCompleteMessage": "Aggiornamento completato!",
|
"DialogUpdaterCompleteMessage": "Aggiornamento completato!",
|
||||||
"DialogUpdaterRestartMessage": "Vuoi riavviare Ryujinx adesso?",
|
"DialogUpdaterRestartMessage": "Vuoi riavviare Ryujinx adesso?",
|
||||||
"DialogUpdaterNoInternetMessage": "Non sei connesso ad Internet!",
|
"DialogUpdaterNoInternetMessage": "Non sei connesso ad Internet!",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Language": "日本語",
|
"Language": "日本語",
|
||||||
"MenuBarFileOpenApplet": "アプレットを開く",
|
"MenuBarFileOpenApplet": "アプレットを開く",
|
||||||
|
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "スタンドアロンモードで Mii エディタアプレットを開きます",
|
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "スタンドアロンモードで Mii エディタアプレットを開きます",
|
||||||
"SettingsTabInputDirectMouseAccess": "マウス直接アクセス",
|
"SettingsTabInputDirectMouseAccess": "マウス直接アクセス",
|
||||||
"SettingsTabSystemMemoryManagerMode": "メモリ管理モード:",
|
"SettingsTabSystemMemoryManagerMode": "メモリ管理モード:",
|
||||||
@@ -457,6 +458,7 @@
|
|||||||
"DialogUpdaterExtractionMessage": "アップデートを展開中...",
|
"DialogUpdaterExtractionMessage": "アップデートを展開中...",
|
||||||
"DialogUpdaterRenamingMessage": "アップデートをリネーム中...",
|
"DialogUpdaterRenamingMessage": "アップデートをリネーム中...",
|
||||||
"DialogUpdaterAddingFilesMessage": "新規アップデートを追加中...",
|
"DialogUpdaterAddingFilesMessage": "新規アップデートを追加中...",
|
||||||
|
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||||
"DialogUpdaterCompleteMessage": "アップデート完了!",
|
"DialogUpdaterCompleteMessage": "アップデート完了!",
|
||||||
"DialogUpdaterRestartMessage": "すぐに Ryujinx を再起動しますか?",
|
"DialogUpdaterRestartMessage": "すぐに Ryujinx を再起動しますか?",
|
||||||
"DialogUpdaterNoInternetMessage": "インターネットに接続されていません!",
|
"DialogUpdaterNoInternetMessage": "インターネットに接続されていません!",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Language": "한국어",
|
"Language": "한국어",
|
||||||
"MenuBarFileOpenApplet": "애플릿 열기",
|
"MenuBarFileOpenApplet": "애플릿 열기",
|
||||||
|
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "독립 실행형 모드로 Mii 편집기 애플릿 열기",
|
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "독립 실행형 모드로 Mii 편집기 애플릿 열기",
|
||||||
"SettingsTabInputDirectMouseAccess": "마우스 직접 접근",
|
"SettingsTabInputDirectMouseAccess": "마우스 직접 접근",
|
||||||
"SettingsTabSystemMemoryManagerMode": "메모리 관리자 모드 :",
|
"SettingsTabSystemMemoryManagerMode": "메모리 관리자 모드 :",
|
||||||
@@ -457,6 +458,7 @@
|
|||||||
"DialogUpdaterExtractionMessage": "업데이트 추출 중...",
|
"DialogUpdaterExtractionMessage": "업데이트 추출 중...",
|
||||||
"DialogUpdaterRenamingMessage": "이름 변경 업데이트...",
|
"DialogUpdaterRenamingMessage": "이름 변경 업데이트...",
|
||||||
"DialogUpdaterAddingFilesMessage": "새 업데이트 추가 중...",
|
"DialogUpdaterAddingFilesMessage": "새 업데이트 추가 중...",
|
||||||
|
"DialogUpdaterShowChangelogMessage": "변경 로그 보기",
|
||||||
"DialogUpdaterCompleteMessage": "업데이트가 완료되었습니다!",
|
"DialogUpdaterCompleteMessage": "업데이트가 완료되었습니다!",
|
||||||
"DialogUpdaterRestartMessage": "지금 Ryujinx를 다시 시작하시겠습니까?",
|
"DialogUpdaterRestartMessage": "지금 Ryujinx를 다시 시작하시겠습니까?",
|
||||||
"DialogUpdaterNoInternetMessage": "인터넷에 연결되어 있지 않습니다!",
|
"DialogUpdaterNoInternetMessage": "인터넷에 연결되어 있지 않습니다!",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Language": "Polski",
|
"Language": "Polski",
|
||||||
"MenuBarFileOpenApplet": "Otwórz Aplet",
|
"MenuBarFileOpenApplet": "Otwórz Aplet",
|
||||||
|
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Otwórz aplet Mii Editor w trybie indywidualnym",
|
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Otwórz aplet Mii Editor w trybie indywidualnym",
|
||||||
"SettingsTabInputDirectMouseAccess": "Bezpośredni dostęp do myszy",
|
"SettingsTabInputDirectMouseAccess": "Bezpośredni dostęp do myszy",
|
||||||
"SettingsTabSystemMemoryManagerMode": "Tryb menedżera pamięci:",
|
"SettingsTabSystemMemoryManagerMode": "Tryb menedżera pamięci:",
|
||||||
@@ -457,6 +458,7 @@
|
|||||||
"DialogUpdaterExtractionMessage": "Wypakowywanie Aktualizacji...",
|
"DialogUpdaterExtractionMessage": "Wypakowywanie Aktualizacji...",
|
||||||
"DialogUpdaterRenamingMessage": "Zmiana Nazwy Aktualizacji...",
|
"DialogUpdaterRenamingMessage": "Zmiana Nazwy Aktualizacji...",
|
||||||
"DialogUpdaterAddingFilesMessage": "Dodawanie Nowej Aktualizacji...",
|
"DialogUpdaterAddingFilesMessage": "Dodawanie Nowej Aktualizacji...",
|
||||||
|
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||||
"DialogUpdaterCompleteMessage": "Aktualizacja Zakończona!",
|
"DialogUpdaterCompleteMessage": "Aktualizacja Zakończona!",
|
||||||
"DialogUpdaterRestartMessage": "Czy chcesz teraz zrestartować Ryujinx?",
|
"DialogUpdaterRestartMessage": "Czy chcesz teraz zrestartować Ryujinx?",
|
||||||
"DialogUpdaterNoInternetMessage": "Nie masz połączenia z Internetem!",
|
"DialogUpdaterNoInternetMessage": "Nie masz połączenia z Internetem!",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Language": "Português (BR)",
|
"Language": "Português (BR)",
|
||||||
"MenuBarFileOpenApplet": "Abrir Applet",
|
"MenuBarFileOpenApplet": "Abrir Applet",
|
||||||
|
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Abrir editor Mii em modo avulso",
|
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Abrir editor Mii em modo avulso",
|
||||||
"SettingsTabInputDirectMouseAccess": "Acesso direto ao mouse",
|
"SettingsTabInputDirectMouseAccess": "Acesso direto ao mouse",
|
||||||
"SettingsTabSystemMemoryManagerMode": "Modo de gerenciamento de memória:",
|
"SettingsTabSystemMemoryManagerMode": "Modo de gerenciamento de memória:",
|
||||||
@@ -457,6 +458,7 @@
|
|||||||
"DialogUpdaterExtractionMessage": "Extraindo atualização...",
|
"DialogUpdaterExtractionMessage": "Extraindo atualização...",
|
||||||
"DialogUpdaterRenamingMessage": "Renomeando atualização...",
|
"DialogUpdaterRenamingMessage": "Renomeando atualização...",
|
||||||
"DialogUpdaterAddingFilesMessage": "Adicionando nova atualização...",
|
"DialogUpdaterAddingFilesMessage": "Adicionando nova atualização...",
|
||||||
|
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||||
"DialogUpdaterCompleteMessage": "Atualização concluída!",
|
"DialogUpdaterCompleteMessage": "Atualização concluída!",
|
||||||
"DialogUpdaterRestartMessage": "Deseja reiniciar o Ryujinx agora?",
|
"DialogUpdaterRestartMessage": "Deseja reiniciar o Ryujinx agora?",
|
||||||
"DialogUpdaterNoInternetMessage": "Você não está conectado à Internet!",
|
"DialogUpdaterNoInternetMessage": "Você não está conectado à Internet!",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Language": "Русский (RU)",
|
"Language": "Русский (RU)",
|
||||||
"MenuBarFileOpenApplet": "Открыть апплет",
|
"MenuBarFileOpenApplet": "Открыть апплет",
|
||||||
|
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Открывает апплет Mii Editor в автономном режиме",
|
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Открывает апплет Mii Editor в автономном режиме",
|
||||||
"SettingsTabInputDirectMouseAccess": "Прямой ввод мыши",
|
"SettingsTabInputDirectMouseAccess": "Прямой ввод мыши",
|
||||||
"SettingsTabSystemMemoryManagerMode": "Режим менеджера памяти:",
|
"SettingsTabSystemMemoryManagerMode": "Режим менеджера памяти:",
|
||||||
@@ -457,6 +458,7 @@
|
|||||||
"DialogUpdaterExtractionMessage": "Извлечение обновления...",
|
"DialogUpdaterExtractionMessage": "Извлечение обновления...",
|
||||||
"DialogUpdaterRenamingMessage": "Переименование обновления...",
|
"DialogUpdaterRenamingMessage": "Переименование обновления...",
|
||||||
"DialogUpdaterAddingFilesMessage": "Добавление нового обновления...",
|
"DialogUpdaterAddingFilesMessage": "Добавление нового обновления...",
|
||||||
|
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||||
"DialogUpdaterCompleteMessage": "Обновление завершено",
|
"DialogUpdaterCompleteMessage": "Обновление завершено",
|
||||||
"DialogUpdaterRestartMessage": "Перезапустить Ryujinx?",
|
"DialogUpdaterRestartMessage": "Перезапустить Ryujinx?",
|
||||||
"DialogUpdaterNoInternetMessage": "Вы не подключены к интернету",
|
"DialogUpdaterNoInternetMessage": "Вы не подключены к интернету",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Language": "ภาษาไทย",
|
"Language": "ภาษาไทย",
|
||||||
"MenuBarFileOpenApplet": "เปิด Applet",
|
"MenuBarFileOpenApplet": "เปิด Applet",
|
||||||
|
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "เปิดโปรแกรม Mii Editor Applet",
|
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "เปิดโปรแกรม Mii Editor Applet",
|
||||||
"SettingsTabInputDirectMouseAccess": "เข้าถึงเมาส์ได้โดยตรง",
|
"SettingsTabInputDirectMouseAccess": "เข้าถึงเมาส์ได้โดยตรง",
|
||||||
"SettingsTabSystemMemoryManagerMode": "โหมดจัดการหน่วยความจำ:",
|
"SettingsTabSystemMemoryManagerMode": "โหมดจัดการหน่วยความจำ:",
|
||||||
@@ -457,6 +458,7 @@
|
|||||||
"DialogUpdaterExtractionMessage": "กำลังแตกไฟล์อัปเดต...",
|
"DialogUpdaterExtractionMessage": "กำลังแตกไฟล์อัปเดต...",
|
||||||
"DialogUpdaterRenamingMessage": "กำลังลบไฟล์เก่า...",
|
"DialogUpdaterRenamingMessage": "กำลังลบไฟล์เก่า...",
|
||||||
"DialogUpdaterAddingFilesMessage": "กำลังเพิ่มไฟล์อัปเดตใหม่...",
|
"DialogUpdaterAddingFilesMessage": "กำลังเพิ่มไฟล์อัปเดตใหม่...",
|
||||||
|
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||||
"DialogUpdaterCompleteMessage": "อัปเดตเสร็จสมบูรณ์แล้ว!",
|
"DialogUpdaterCompleteMessage": "อัปเดตเสร็จสมบูรณ์แล้ว!",
|
||||||
"DialogUpdaterRestartMessage": "คุณต้องการรีสตาร์ท Ryujinx ตอนนี้หรือไม่?",
|
"DialogUpdaterRestartMessage": "คุณต้องการรีสตาร์ท Ryujinx ตอนนี้หรือไม่?",
|
||||||
"DialogUpdaterNoInternetMessage": "คุณไม่ได้เชื่อมต่อกับอินเทอร์เน็ต!",
|
"DialogUpdaterNoInternetMessage": "คุณไม่ได้เชื่อมต่อกับอินเทอร์เน็ต!",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Language": "Türkçe",
|
"Language": "Türkçe",
|
||||||
"MenuBarFileOpenApplet": "Applet'i Aç",
|
"MenuBarFileOpenApplet": "Applet'i Aç",
|
||||||
|
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Mii Editör Applet'ini Bağımsız Mod'da Aç",
|
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Mii Editör Applet'ini Bağımsız Mod'da Aç",
|
||||||
"SettingsTabInputDirectMouseAccess": "Doğrudan Mouse Erişimi",
|
"SettingsTabInputDirectMouseAccess": "Doğrudan Mouse Erişimi",
|
||||||
"SettingsTabSystemMemoryManagerMode": "Hafıza Yönetim Modu:",
|
"SettingsTabSystemMemoryManagerMode": "Hafıza Yönetim Modu:",
|
||||||
@@ -457,6 +458,7 @@
|
|||||||
"DialogUpdaterExtractionMessage": "Güncelleme Ayıklanıyor...",
|
"DialogUpdaterExtractionMessage": "Güncelleme Ayıklanıyor...",
|
||||||
"DialogUpdaterRenamingMessage": "Güncelleme Yeniden Adlandırılıyor...",
|
"DialogUpdaterRenamingMessage": "Güncelleme Yeniden Adlandırılıyor...",
|
||||||
"DialogUpdaterAddingFilesMessage": "Yeni Güncelleme Ekleniyor...",
|
"DialogUpdaterAddingFilesMessage": "Yeni Güncelleme Ekleniyor...",
|
||||||
|
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||||
"DialogUpdaterCompleteMessage": "Güncelleme Tamamlandı!",
|
"DialogUpdaterCompleteMessage": "Güncelleme Tamamlandı!",
|
||||||
"DialogUpdaterRestartMessage": "Ryujinx'i şimdi yeniden başlatmak istiyor musunuz?",
|
"DialogUpdaterRestartMessage": "Ryujinx'i şimdi yeniden başlatmak istiyor musunuz?",
|
||||||
"DialogUpdaterNoInternetMessage": "İnternete bağlı değilsiniz!",
|
"DialogUpdaterNoInternetMessage": "İnternete bağlı değilsiniz!",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Language": "Українська",
|
"Language": "Українська",
|
||||||
"MenuBarFileOpenApplet": "Відкрити аплет",
|
"MenuBarFileOpenApplet": "Відкрити аплет",
|
||||||
|
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Відкрити аплет Mii Editor в автономному режимі",
|
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "Відкрити аплет Mii Editor в автономному режимі",
|
||||||
"SettingsTabInputDirectMouseAccess": "Прямий доступ мишею",
|
"SettingsTabInputDirectMouseAccess": "Прямий доступ мишею",
|
||||||
"SettingsTabSystemMemoryManagerMode": "Режим диспетчера пам’яті:",
|
"SettingsTabSystemMemoryManagerMode": "Режим диспетчера пам’яті:",
|
||||||
@@ -457,6 +458,7 @@
|
|||||||
"DialogUpdaterExtractionMessage": "Видобування оновлення...",
|
"DialogUpdaterExtractionMessage": "Видобування оновлення...",
|
||||||
"DialogUpdaterRenamingMessage": "Перейменування оновлення...",
|
"DialogUpdaterRenamingMessage": "Перейменування оновлення...",
|
||||||
"DialogUpdaterAddingFilesMessage": "Додавання нового оновлення...",
|
"DialogUpdaterAddingFilesMessage": "Додавання нового оновлення...",
|
||||||
|
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||||
"DialogUpdaterCompleteMessage": "Оновлення завершено!",
|
"DialogUpdaterCompleteMessage": "Оновлення завершено!",
|
||||||
"DialogUpdaterRestartMessage": "Перезапустити Ryujinx зараз?",
|
"DialogUpdaterRestartMessage": "Перезапустити Ryujinx зараз?",
|
||||||
"DialogUpdaterNoInternetMessage": "Ви не підключені до Інтернету!",
|
"DialogUpdaterNoInternetMessage": "Ви не підключені до Інтернету!",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Language": "简体中文",
|
"Language": "简体中文",
|
||||||
"MenuBarFileOpenApplet": "打开小程序",
|
"MenuBarFileOpenApplet": "打开小程序",
|
||||||
|
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "打开独立的 Mii 小程序",
|
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "打开独立的 Mii 小程序",
|
||||||
"SettingsTabInputDirectMouseAccess": "直通鼠标操作",
|
"SettingsTabInputDirectMouseAccess": "直通鼠标操作",
|
||||||
"SettingsTabSystemMemoryManagerMode": "内存管理模式:",
|
"SettingsTabSystemMemoryManagerMode": "内存管理模式:",
|
||||||
@@ -457,6 +458,7 @@
|
|||||||
"DialogUpdaterExtractionMessage": "正在提取更新...",
|
"DialogUpdaterExtractionMessage": "正在提取更新...",
|
||||||
"DialogUpdaterRenamingMessage": "正在重命名更新...",
|
"DialogUpdaterRenamingMessage": "正在重命名更新...",
|
||||||
"DialogUpdaterAddingFilesMessage": "安装更新中...",
|
"DialogUpdaterAddingFilesMessage": "安装更新中...",
|
||||||
|
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||||
"DialogUpdaterCompleteMessage": "更新成功!",
|
"DialogUpdaterCompleteMessage": "更新成功!",
|
||||||
"DialogUpdaterRestartMessage": "是否立即重启 Ryujinx 模拟器?",
|
"DialogUpdaterRestartMessage": "是否立即重启 Ryujinx 模拟器?",
|
||||||
"DialogUpdaterNoInternetMessage": "没有连接到网络",
|
"DialogUpdaterNoInternetMessage": "没有连接到网络",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Language": "繁體中文 (台灣)",
|
"Language": "繁體中文 (台灣)",
|
||||||
"MenuBarFileOpenApplet": "開啟小程式",
|
"MenuBarFileOpenApplet": "開啟小程式",
|
||||||
|
"MenuBarFileOpenAppletOpenMiiApplet": "Mii Edit Applet",
|
||||||
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "在獨立模式下開啟 Mii 編輯器小程式",
|
"MenuBarFileOpenAppletOpenMiiAppletToolTip": "在獨立模式下開啟 Mii 編輯器小程式",
|
||||||
"SettingsTabInputDirectMouseAccess": "滑鼠直接存取",
|
"SettingsTabInputDirectMouseAccess": "滑鼠直接存取",
|
||||||
"SettingsTabSystemMemoryManagerMode": "記憶體管理員模式:",
|
"SettingsTabSystemMemoryManagerMode": "記憶體管理員模式:",
|
||||||
@@ -457,6 +458,7 @@
|
|||||||
"DialogUpdaterExtractionMessage": "正在提取更新...",
|
"DialogUpdaterExtractionMessage": "正在提取更新...",
|
||||||
"DialogUpdaterRenamingMessage": "重新命名更新...",
|
"DialogUpdaterRenamingMessage": "重新命名更新...",
|
||||||
"DialogUpdaterAddingFilesMessage": "加入新更新...",
|
"DialogUpdaterAddingFilesMessage": "加入新更新...",
|
||||||
|
"DialogUpdaterShowChangelogMessage": "Show Changelog",
|
||||||
"DialogUpdaterCompleteMessage": "更新成功!",
|
"DialogUpdaterCompleteMessage": "更新成功!",
|
||||||
"DialogUpdaterRestartMessage": "您現在要重新啟動 Ryujinx 嗎?",
|
"DialogUpdaterRestartMessage": "您現在要重新啟動 Ryujinx 嗎?",
|
||||||
"DialogUpdaterNoInternetMessage": "您沒有連線到網際網路!",
|
"DialogUpdaterNoInternetMessage": "您沒有連線到網際網路!",
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<Styles
|
<Styles
|
||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia">
|
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
|
xmlns:windowing="clr-namespace:FluentAvalonia.UI.Windowing;assembly=FluentAvalonia">
|
||||||
<Design.PreviewWith>
|
<Design.PreviewWith>
|
||||||
<Border Height="2000"
|
<Border Height="2000"
|
||||||
Padding="20">
|
Padding="20">
|
||||||
@@ -231,7 +232,7 @@
|
|||||||
<Setter Property="BorderBrush"
|
<Setter Property="BorderBrush"
|
||||||
Value="{DynamicResource HighlightBrush}" />
|
Value="{DynamicResource HighlightBrush}" />
|
||||||
</Style>
|
</Style>
|
||||||
<Style Selector="Button">
|
<Style Selector="Button:not(windowing|MinMaxCloseControl Button)">
|
||||||
<Setter Property="MinWidth"
|
<Setter Property="MinWidth"
|
||||||
Value="80" />
|
Value="80" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|||||||
@@ -26,8 +26,9 @@
|
|||||||
<Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
|
<Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
|
||||||
<Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color>
|
<Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color>
|
||||||
<Color x:Key="SecondaryTextColor">#A0000000</Color>
|
<Color x:Key="SecondaryTextColor">#A0000000</Color>
|
||||||
<Color x:Key="VsyncEnabled">#FF2EEAC9</Color>
|
<Color x:Key="Switch">#FF2EEAC9</Color>
|
||||||
<Color x:Key="VsyncDisabled">#FFFF4554</Color>
|
<Color x:Key="Unbounded">#FFFF4554</Color>
|
||||||
|
<Color x:Key="Custom">#6483F5</Color>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
<ResourceDictionary x:Key="Light">
|
<ResourceDictionary x:Key="Light">
|
||||||
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
|
<SolidColorBrush x:Key="DataGridSelectionBackgroundBrush"
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ namespace Ryujinx.Ava.Common
|
|||||||
public enum KeyboardHotkeyState
|
public enum KeyboardHotkeyState
|
||||||
{
|
{
|
||||||
None,
|
None,
|
||||||
ToggleVSync,
|
ToggleVSyncMode,
|
||||||
Screenshot,
|
Screenshot,
|
||||||
ShowUI,
|
ShowUI,
|
||||||
Pause,
|
Pause,
|
||||||
@@ -12,5 +12,7 @@ namespace Ryujinx.Ava.Common
|
|||||||
ResScaleDown,
|
ResScaleDown,
|
||||||
VolumeUp,
|
VolumeUp,
|
||||||
VolumeDown,
|
VolumeDown,
|
||||||
|
CustomVSyncIntervalIncrement,
|
||||||
|
CustomVSyncIntervalDecrement,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -262,6 +262,16 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||||
(int)Symbol.Important);
|
(int)Symbol.Important);
|
||||||
|
|
||||||
|
internal static async Task<UserResult> CreateUpdaterUpToDateInfoDialog(string primary, string secondaryText)
|
||||||
|
=> await ShowTextDialog(
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterTitle],
|
||||||
|
primary,
|
||||||
|
secondaryText,
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterShowChangelogMessage],
|
||||||
|
string.Empty,
|
||||||
|
LocaleManager.Instance[LocaleKeys.InputDialogOk],
|
||||||
|
(int)Symbol.Important);
|
||||||
|
|
||||||
internal static async Task CreateWarningDialog(string primary, string secondaryText)
|
internal static async Task CreateWarningDialog(string primary, string secondaryText)
|
||||||
=> await ShowTextDialog(
|
=> await ShowTextDialog(
|
||||||
LocaleManager.Instance[LocaleKeys.DialogWarningTitle],
|
LocaleManager.Instance[LocaleKeys.DialogWarningTitle],
|
||||||
@@ -310,6 +320,30 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
return response == UserResult.Yes;
|
return response == UserResult.Yes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static async Task<UserResult> CreateUpdaterChoiceDialog(string title, string primary, string secondaryText)
|
||||||
|
{
|
||||||
|
if (_isChoiceDialogOpen)
|
||||||
|
{
|
||||||
|
return UserResult.Cancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isChoiceDialogOpen = true;
|
||||||
|
|
||||||
|
UserResult response = await ShowTextDialog(
|
||||||
|
title,
|
||||||
|
primary,
|
||||||
|
secondaryText,
|
||||||
|
LocaleManager.Instance[LocaleKeys.InputDialogYes],
|
||||||
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterShowChangelogMessage],
|
||||||
|
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||||
|
(int)Symbol.Help,
|
||||||
|
UserResult.Yes);
|
||||||
|
|
||||||
|
_isChoiceDialogOpen = false;
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
internal static async Task<bool> CreateExitDialog()
|
internal static async Task<bool> CreateExitDialog()
|
||||||
{
|
{
|
||||||
return await CreateChoiceDialog(
|
return await CreateChoiceDialog(
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||||||
{
|
{
|
||||||
public class HotkeyConfig : BaseModel
|
public class HotkeyConfig : BaseModel
|
||||||
{
|
{
|
||||||
private Key _toggleVsync;
|
private Key _toggleVSyncMode;
|
||||||
public Key ToggleVsync
|
public Key ToggleVSyncMode
|
||||||
{
|
{
|
||||||
get => _toggleVsync;
|
get => _toggleVSyncMode;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_toggleVsync = value;
|
_toggleVSyncMode = value;
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -104,11 +104,33 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Key _customVSyncIntervalIncrement;
|
||||||
|
public Key CustomVSyncIntervalIncrement
|
||||||
|
{
|
||||||
|
get => _customVSyncIntervalIncrement;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_customVSyncIntervalIncrement = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Key _customVSyncIntervalDecrement;
|
||||||
|
public Key CustomVSyncIntervalDecrement
|
||||||
|
{
|
||||||
|
get => _customVSyncIntervalDecrement;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_customVSyncIntervalDecrement = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public HotkeyConfig(KeyboardHotkeys config)
|
public HotkeyConfig(KeyboardHotkeys config)
|
||||||
{
|
{
|
||||||
if (config != null)
|
if (config != null)
|
||||||
{
|
{
|
||||||
ToggleVsync = config.ToggleVsync;
|
ToggleVSyncMode = config.ToggleVSyncMode;
|
||||||
Screenshot = config.Screenshot;
|
Screenshot = config.Screenshot;
|
||||||
ShowUI = config.ShowUI;
|
ShowUI = config.ShowUI;
|
||||||
Pause = config.Pause;
|
Pause = config.Pause;
|
||||||
@@ -117,6 +139,8 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||||||
ResScaleDown = config.ResScaleDown;
|
ResScaleDown = config.ResScaleDown;
|
||||||
VolumeUp = config.VolumeUp;
|
VolumeUp = config.VolumeUp;
|
||||||
VolumeDown = config.VolumeDown;
|
VolumeDown = config.VolumeDown;
|
||||||
|
CustomVSyncIntervalIncrement = config.CustomVSyncIntervalIncrement;
|
||||||
|
CustomVSyncIntervalDecrement = config.CustomVSyncIntervalDecrement;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +148,7 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||||||
{
|
{
|
||||||
var config = new KeyboardHotkeys
|
var config = new KeyboardHotkeys
|
||||||
{
|
{
|
||||||
ToggleVsync = ToggleVsync,
|
ToggleVSyncMode = ToggleVSyncMode,
|
||||||
Screenshot = Screenshot,
|
Screenshot = Screenshot,
|
||||||
ShowUI = ShowUI,
|
ShowUI = ShowUI,
|
||||||
Pause = Pause,
|
Pause = Pause,
|
||||||
@@ -133,6 +157,8 @@ namespace Ryujinx.Ava.UI.Models.Input
|
|||||||
ResScaleDown = ResScaleDown,
|
ResScaleDown = ResScaleDown,
|
||||||
VolumeUp = VolumeUp,
|
VolumeUp = VolumeUp,
|
||||||
VolumeDown = VolumeDown,
|
VolumeDown = VolumeDown,
|
||||||
|
CustomVSyncIntervalIncrement = CustomVSyncIntervalIncrement,
|
||||||
|
CustomVSyncIntervalDecrement = CustomVSyncIntervalDecrement,
|
||||||
};
|
};
|
||||||
|
|
||||||
return config;
|
return config;
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ namespace Ryujinx.Ava.UI.Models
|
|||||||
TitleId = info.ProgramId;
|
TitleId = info.ProgramId;
|
||||||
UserId = info.UserId;
|
UserId = info.UserId;
|
||||||
|
|
||||||
var appData = App.MainWindow.ViewModel.Applications.FirstOrDefault(x => x.IdString.Equals(TitleIdString, StringComparison.OrdinalIgnoreCase));
|
var appData = MainWindow.MainWindowViewModel.Applications.FirstOrDefault(x => x.IdString.Equals(TitleIdString, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
InGameList = appData != null;
|
InGameList = appData != null;
|
||||||
|
|
||||||
|
|||||||
@@ -4,18 +4,17 @@ namespace Ryujinx.Ava.UI.Models
|
|||||||
{
|
{
|
||||||
internal class StatusUpdatedEventArgs : EventArgs
|
internal class StatusUpdatedEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public bool VSyncEnabled { get; }
|
public string VSyncMode { get; }
|
||||||
public string VolumeStatus { get; }
|
public string VolumeStatus { get; }
|
||||||
public string AspectRatio { get; }
|
public string AspectRatio { get; }
|
||||||
public string DockedMode { get; }
|
public string DockedMode { get; }
|
||||||
public string FifoStatus { get; }
|
public string FifoStatus { get; }
|
||||||
public string GameStatus { get; }
|
public string GameStatus { get; }
|
||||||
|
|
||||||
public uint ShaderCount { get; }
|
public uint ShaderCount { get; }
|
||||||
|
|
||||||
public StatusUpdatedEventArgs(bool vSyncEnabled, string volumeStatus, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus, uint shaderCount)
|
public StatusUpdatedEventArgs(string vSyncMode, string volumeStatus, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus, uint shaderCount)
|
||||||
{
|
{
|
||||||
VSyncEnabled = vSyncEnabled;
|
VSyncMode = vSyncMode;
|
||||||
VolumeStatus = volumeStatus;
|
VolumeStatus = volumeStatus;
|
||||||
DockedMode = dockedMode;
|
DockedMode = dockedMode;
|
||||||
AspectRatio = aspectRatio;
|
AspectRatio = aspectRatio;
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private string _searchText;
|
private string _searchText;
|
||||||
private Timer _searchTimer;
|
private Timer _searchTimer;
|
||||||
private string _dockedStatusText;
|
private string _dockedStatusText;
|
||||||
|
private string _vSyncModeText;
|
||||||
private string _fifoStatusText;
|
private string _fifoStatusText;
|
||||||
private string _gameStatusText;
|
private string _gameStatusText;
|
||||||
private string _volumeStatusText;
|
private string _volumeStatusText;
|
||||||
@@ -80,7 +81,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private bool _showStatusSeparator;
|
private bool _showStatusSeparator;
|
||||||
private Brush _progressBarForegroundColor;
|
private Brush _progressBarForegroundColor;
|
||||||
private Brush _progressBarBackgroundColor;
|
private Brush _progressBarBackgroundColor;
|
||||||
private Brush _vsyncColor;
|
private Brush _vSyncModeColor;
|
||||||
private byte[] _selectedIcon;
|
private byte[] _selectedIcon;
|
||||||
private bool _isAppletMenuActive;
|
private bool _isAppletMenuActive;
|
||||||
private int _statusBarProgressMaximum;
|
private int _statusBarProgressMaximum;
|
||||||
@@ -111,6 +112,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private WindowState _windowState;
|
private WindowState _windowState;
|
||||||
private double _windowWidth;
|
private double _windowWidth;
|
||||||
private double _windowHeight;
|
private double _windowHeight;
|
||||||
|
private int _customVSyncInterval;
|
||||||
|
private int _customVSyncIntervalPercentageProxy;
|
||||||
|
|
||||||
private bool _isActive;
|
private bool _isActive;
|
||||||
private bool _isSubMenuOpen;
|
private bool _isSubMenuOpen;
|
||||||
@@ -145,6 +148,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
Volume = ConfigurationState.Instance.System.AudioVolume;
|
Volume = ConfigurationState.Instance.System.AudioVolume;
|
||||||
}
|
}
|
||||||
|
CustomVSyncInterval = ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize(
|
public void Initialize(
|
||||||
@@ -447,17 +451,87 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Brush VsyncColor
|
public Brush VSyncModeColor
|
||||||
{
|
{
|
||||||
get => _vsyncColor;
|
get => _vSyncModeColor;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_vsyncColor = value;
|
_vSyncModeColor = value;
|
||||||
|
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ShowCustomVSyncIntervalPicker
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_isGameRunning)
|
||||||
|
{
|
||||||
|
return AppHost.Device.VSyncMode ==
|
||||||
|
VSyncMode.Custom;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CustomVSyncIntervalPercentageProxy
|
||||||
|
{
|
||||||
|
get => _customVSyncIntervalPercentageProxy;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
int newInterval = (int)((value / 100f) * 60);
|
||||||
|
_customVSyncInterval = newInterval;
|
||||||
|
_customVSyncIntervalPercentageProxy = value;
|
||||||
|
if (_isGameRunning)
|
||||||
|
{
|
||||||
|
AppHost.Device.CustomVSyncInterval = newInterval;
|
||||||
|
AppHost.Device.UpdateVSyncInterval();
|
||||||
|
}
|
||||||
|
OnPropertyChanged((nameof(CustomVSyncInterval)));
|
||||||
|
OnPropertyChanged((nameof(CustomVSyncIntervalPercentageText)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CustomVSyncIntervalPercentageText
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string text = CustomVSyncIntervalPercentageProxy.ToString() + "%";
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CustomVSyncInterval
|
||||||
|
{
|
||||||
|
get => _customVSyncInterval;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_customVSyncInterval = value;
|
||||||
|
int newPercent = (int)((value / 60f) * 100);
|
||||||
|
_customVSyncIntervalPercentageProxy = newPercent;
|
||||||
|
if (_isGameRunning)
|
||||||
|
{
|
||||||
|
AppHost.Device.CustomVSyncInterval = value;
|
||||||
|
AppHost.Device.UpdateVSyncInterval();
|
||||||
|
}
|
||||||
|
OnPropertyChanged(nameof(CustomVSyncIntervalPercentageProxy));
|
||||||
|
OnPropertyChanged(nameof(CustomVSyncIntervalPercentageText));
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] SelectedIcon
|
public byte[] SelectedIcon
|
||||||
{
|
{
|
||||||
get => _selectedIcon;
|
get => _selectedIcon;
|
||||||
@@ -578,6 +652,17 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string VSyncModeText
|
||||||
|
{
|
||||||
|
get => _vSyncModeText;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_vSyncModeText = value;
|
||||||
|
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string DockedStatusText
|
public string DockedStatusText
|
||||||
{
|
{
|
||||||
get => _dockedStatusText;
|
get => _dockedStatusText;
|
||||||
@@ -1292,17 +1377,18 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
{
|
{
|
||||||
Dispatcher.UIThread.InvokeAsync(() =>
|
Dispatcher.UIThread.InvokeAsync(() =>
|
||||||
{
|
{
|
||||||
Application.Current!.Styles.TryGetResource(args.VSyncEnabled
|
Application.Current!.Styles.TryGetResource(args.VSyncMode,
|
||||||
? "VsyncEnabled"
|
|
||||||
: "VsyncDisabled",
|
|
||||||
Application.Current.ActualThemeVariant,
|
Application.Current.ActualThemeVariant,
|
||||||
out object color);
|
out object color);
|
||||||
|
|
||||||
if (color is Color clr)
|
if (color is Color clr)
|
||||||
{
|
{
|
||||||
VsyncColor = new SolidColorBrush(clr);
|
VSyncModeColor = new SolidColorBrush(clr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
VSyncModeText = args.VSyncMode == "Custom" ? "Custom" : "VSync";
|
||||||
|
ShowCustomVSyncIntervalPicker =
|
||||||
|
args.VSyncMode == VSyncMode.Custom.ToString();
|
||||||
DockedStatusText = args.DockedMode;
|
DockedStatusText = args.DockedMode;
|
||||||
AspectRatioStatusText = args.AspectRatio;
|
AspectRatioStatusText = args.AspectRatio;
|
||||||
GameStatusText = args.GameStatus;
|
GameStatusText = args.GameStatus;
|
||||||
@@ -1495,6 +1581,27 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ToggleVSyncMode()
|
||||||
|
{
|
||||||
|
AppHost.VSyncModeToggle();
|
||||||
|
OnPropertyChanged(nameof(ShowCustomVSyncIntervalPicker));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void VSyncModeSettingChanged()
|
||||||
|
{
|
||||||
|
if (_isGameRunning)
|
||||||
|
{
|
||||||
|
AppHost.Device.CustomVSyncInterval = ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value;
|
||||||
|
AppHost.Device.UpdateVSyncInterval();
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomVSyncInterval = ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value;
|
||||||
|
OnPropertyChanged(nameof(ShowCustomVSyncIntervalPicker));
|
||||||
|
OnPropertyChanged(nameof(CustomVSyncIntervalPercentageProxy));
|
||||||
|
OnPropertyChanged(nameof(CustomVSyncIntervalPercentageText));
|
||||||
|
OnPropertyChanged(nameof(CustomVSyncInterval));
|
||||||
|
}
|
||||||
|
|
||||||
public async Task ExitCurrentState()
|
public async Task ExitCurrentState()
|
||||||
{
|
{
|
||||||
if (WindowState is WindowState.FullScreen)
|
if (WindowState is WindowState.FullScreen)
|
||||||
|
|||||||
@@ -52,6 +52,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private int _graphicsBackendIndex;
|
private int _graphicsBackendIndex;
|
||||||
private int _scalingFilter;
|
private int _scalingFilter;
|
||||||
private int _scalingFilterLevel;
|
private int _scalingFilterLevel;
|
||||||
|
private int _customVSyncInterval;
|
||||||
|
private bool _enableCustomVSyncInterval;
|
||||||
|
private int _customVSyncIntervalPercentageProxy;
|
||||||
|
private VSyncMode _vSyncMode;
|
||||||
|
|
||||||
public event Action CloseWindow;
|
public event Action CloseWindow;
|
||||||
public event Action SaveSettingsEvent;
|
public event Action SaveSettingsEvent;
|
||||||
@@ -154,7 +158,74 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
public bool EnableDockedMode { get; set; }
|
public bool EnableDockedMode { get; set; }
|
||||||
public bool EnableKeyboard { get; set; }
|
public bool EnableKeyboard { get; set; }
|
||||||
public bool EnableMouse { get; set; }
|
public bool EnableMouse { get; set; }
|
||||||
public bool EnableVsync { get; set; }
|
public VSyncMode VSyncMode
|
||||||
|
{
|
||||||
|
get => _vSyncMode;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == VSyncMode.Custom ||
|
||||||
|
value == VSyncMode.Switch ||
|
||||||
|
value == VSyncMode.Unbounded)
|
||||||
|
{
|
||||||
|
_vSyncMode = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CustomVSyncIntervalPercentageProxy
|
||||||
|
{
|
||||||
|
get => _customVSyncIntervalPercentageProxy;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
int newInterval = (int)((value / 100f) * 60);
|
||||||
|
_customVSyncInterval = newInterval;
|
||||||
|
_customVSyncIntervalPercentageProxy = value;
|
||||||
|
OnPropertyChanged((nameof(CustomVSyncInterval)));
|
||||||
|
OnPropertyChanged((nameof(CustomVSyncIntervalPercentageText)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string CustomVSyncIntervalPercentageText
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string text = CustomVSyncIntervalPercentageProxy.ToString() + "%";
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool EnableCustomVSyncInterval
|
||||||
|
{
|
||||||
|
get => _enableCustomVSyncInterval;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_enableCustomVSyncInterval = value;
|
||||||
|
if (_vSyncMode == VSyncMode.Custom && !value)
|
||||||
|
{
|
||||||
|
VSyncMode = VSyncMode.Switch;
|
||||||
|
}
|
||||||
|
else if (value)
|
||||||
|
{
|
||||||
|
VSyncMode = VSyncMode.Custom;
|
||||||
|
}
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CustomVSyncInterval
|
||||||
|
{
|
||||||
|
get => _customVSyncInterval;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_customVSyncInterval = value;
|
||||||
|
int newPercent = (int)((value / 60f) * 100);
|
||||||
|
_customVSyncIntervalPercentageProxy = newPercent;
|
||||||
|
OnPropertyChanged(nameof(CustomVSyncIntervalPercentageProxy));
|
||||||
|
OnPropertyChanged(nameof(CustomVSyncIntervalPercentageText));
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
public bool EnablePptc { get; set; }
|
public bool EnablePptc { get; set; }
|
||||||
public bool EnableLowPowerPptc { get; set; }
|
public bool EnableLowPowerPptc { get; set; }
|
||||||
public bool EnableInternetAccess { get; set; }
|
public bool EnableInternetAccess { get; set; }
|
||||||
@@ -484,7 +555,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
CurrentDate = currentDateTime.Date;
|
CurrentDate = currentDateTime.Date;
|
||||||
CurrentTime = currentDateTime.TimeOfDay;
|
CurrentTime = currentDateTime.TimeOfDay;
|
||||||
|
|
||||||
EnableVsync = config.Graphics.EnableVsync;
|
EnableCustomVSyncInterval = config.Graphics.EnableCustomVSyncInterval.Value;
|
||||||
|
CustomVSyncInterval = config.Graphics.CustomVSyncInterval;
|
||||||
|
VSyncMode = config.Graphics.VSyncMode;
|
||||||
EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks;
|
EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks;
|
||||||
DramSize = config.System.DramSize;
|
DramSize = config.System.DramSize;
|
||||||
IgnoreMissingServices = config.System.IgnoreMissingServices;
|
IgnoreMissingServices = config.System.IgnoreMissingServices;
|
||||||
@@ -590,7 +663,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
|
|
||||||
config.System.SystemTimeOffset.Value = Convert.ToInt64((CurrentDate.ToUnixTimeSeconds() + CurrentTime.TotalSeconds) - DateTimeOffset.Now.ToUnixTimeSeconds());
|
config.System.SystemTimeOffset.Value = Convert.ToInt64((CurrentDate.ToUnixTimeSeconds() + CurrentTime.TotalSeconds) - DateTimeOffset.Now.ToUnixTimeSeconds());
|
||||||
config.Graphics.EnableVsync.Value = EnableVsync;
|
config.Graphics.VSyncMode.Value = VSyncMode;
|
||||||
|
config.Graphics.EnableCustomVSyncInterval.Value = EnableCustomVSyncInterval;
|
||||||
|
config.Graphics.CustomVSyncInterval.Value = CustomVSyncInterval;
|
||||||
config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks;
|
config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks;
|
||||||
config.System.DramSize.Value = DramSize;
|
config.System.DramSize.Value = DramSize;
|
||||||
config.System.IgnoreMissingServices.Value = IgnoreMissingServices;
|
config.System.IgnoreMissingServices.Value = IgnoreMissingServices;
|
||||||
@@ -660,6 +735,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
config.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||||
|
|
||||||
MainWindow.UpdateGraphicsConfig();
|
MainWindow.UpdateGraphicsConfig();
|
||||||
|
MainWindow.MainWindowViewModel.VSyncModeSettingChanged();
|
||||||
|
|
||||||
SaveSettingsEvent?.Invoke();
|
SaveSettingsEvent?.Invoke();
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@
|
|||||||
<MenuItem Header="{ext:Locale MenuBarFileOpenApplet}" IsEnabled="{Binding IsAppletMenuActive}" Icon="{ext:Icon mdi-launch}">
|
<MenuItem Header="{ext:Locale MenuBarFileOpenApplet}" IsEnabled="{Binding IsAppletMenuActive}" Icon="{ext:Icon mdi-launch}">
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="OpenMiiApplet"
|
Click="OpenMiiApplet"
|
||||||
Header="Mii Edit Applet"
|
Header="{ext:Locale MenuBarFileOpenAppletOpenMiiApplet}"
|
||||||
Icon="{ext:Icon fa-solid fa-person}"
|
Icon="{ext:Icon fa-solid fa-person}"
|
||||||
ToolTip.Tip="{ext:Locale MenuBarFileOpenAppletOpenMiiAppletToolTip}" />
|
ToolTip.Tip="{ext:Locale MenuBarFileOpenAppletOpenMiiAppletToolTip}" />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|||||||
@@ -79,15 +79,59 @@
|
|||||||
MaxHeight="18"
|
MaxHeight="18"
|
||||||
Orientation="Horizontal">
|
Orientation="Horizontal">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Name="VsyncStatus"
|
Name="VSyncMode"
|
||||||
Margin="5,0,5,0"
|
Margin="5,0,5,0"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
Foreground="{Binding VsyncColor}"
|
Foreground="{Binding VSyncModeColor}"
|
||||||
IsVisible="{Binding !ShowLoadProgress}"
|
IsVisible="{Binding !ShowLoadProgress}"
|
||||||
PointerReleased="VsyncStatus_PointerReleased"
|
PointerReleased="VSyncMode_PointerReleased"
|
||||||
Text="VSync"
|
Text="{Binding VSyncModeText}"
|
||||||
TextAlignment="Start" />
|
TextAlignment="Start"/>
|
||||||
|
<Button MinWidth="0"
|
||||||
|
Width="20"
|
||||||
|
IsVisible="{Binding ShowCustomVSyncIntervalPicker}"
|
||||||
|
Margin="-5,0,5,0"
|
||||||
|
Background="Transparent"
|
||||||
|
BorderThickness="0">
|
||||||
|
<ui:SymbolIcon Symbol="Settings"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Width="14"
|
||||||
|
Height="14"/>
|
||||||
|
<Button.Styles>
|
||||||
|
<Style Selector=":checked">
|
||||||
|
<Style Selector="^:checked ContentPresenter">
|
||||||
|
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundColor}" />
|
||||||
|
</Style>
|
||||||
|
</Style>
|
||||||
|
<Style Selector="Border#SeparatorBorder">
|
||||||
|
<Setter Property="Opacity" Value="0" />
|
||||||
|
</Style>
|
||||||
|
</Button.Styles>
|
||||||
|
<Button.Flyout>
|
||||||
|
<Flyout Placement="Top" ShowMode="TransientWithDismissOnPointerMoveAway">
|
||||||
|
<StackPanel Margin="0,0,0,0"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<Slider Value="{Binding CustomVSyncIntervalPercentageProxy}"
|
||||||
|
MinWidth="175"
|
||||||
|
Margin="0,-3,0,0"
|
||||||
|
Height="32"
|
||||||
|
Padding="0,-5"
|
||||||
|
TickFrequency="1"
|
||||||
|
IsSnapToTickEnabled="True"
|
||||||
|
LargeChange="10"
|
||||||
|
SmallChange="1"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Minimum="10"
|
||||||
|
Maximum="400" />
|
||||||
|
<TextBlock Margin="5,0"
|
||||||
|
Width="40"
|
||||||
|
Text="{Binding CustomVSyncIntervalPercentageText}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Flyout>
|
||||||
|
</Button.Flyout>
|
||||||
|
</Button>
|
||||||
<Border
|
<Border
|
||||||
Width="2"
|
Width="2"
|
||||||
Height="12"
|
Height="12"
|
||||||
|
|||||||
@@ -38,11 +38,10 @@ namespace Ryujinx.Ava.UI.Views.Main
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void VsyncStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
|
private void VSyncMode_PointerReleased(object sender, PointerReleasedEventArgs e)
|
||||||
{
|
{
|
||||||
Window.ViewModel.AppHost.ToggleVSync();
|
Window.ViewModel.ToggleVSyncMode();
|
||||||
|
Logger.Info?.Print(LogClass.Application, $"VSync Mode toggled to: {Window.ViewModel.AppHost.Device.VSyncMode}");
|
||||||
Logger.Info?.Print(LogClass.Application, $"VSync toggled to: {Window.ViewModel.AppHost.Device.EnableDeviceVsync}");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DockedStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
|
private void DockedStatus_PointerReleased(object sender, PointerReleasedEventArgs e)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<UserControl
|
<UserControl
|
||||||
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsHotkeysView"
|
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsHotkeysView"
|
||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
@@ -50,9 +50,9 @@
|
|||||||
Classes="h1"
|
Classes="h1"
|
||||||
Text="{ext:Locale SettingsTabHotkeysHotkeys}" />
|
Text="{ext:Locale SettingsTabHotkeysHotkeys}" />
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
<TextBlock Text="{ext:Locale SettingsTabHotkeysToggleVsyncHotkey}" />
|
<TextBlock Text="{ext:Locale SettingsTabHotkeysToggleVSyncModeHotkey}" />
|
||||||
<ToggleButton Name="ToggleVsync">
|
<ToggleButton Name="ToggleVSyncMode">
|
||||||
<TextBlock Text="{Binding KeyboardHotkey.ToggleVsync, Converter={StaticResource Key}}" />
|
<TextBlock Text="{Binding KeyboardHotkey.ToggleVSyncMode, Converter={StaticResource Key}}" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel>
|
<StackPanel>
|
||||||
@@ -103,6 +103,18 @@
|
|||||||
<TextBlock Text="{Binding KeyboardHotkey.VolumeDown, Converter={StaticResource Key}}" />
|
<TextBlock Text="{Binding KeyboardHotkey.VolumeDown, Converter={StaticResource Key}}" />
|
||||||
</ToggleButton>
|
</ToggleButton>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||||
|
<TextBlock Text="{ext:Locale SettingsTabHotkeysIncrementCustomVSyncIntervalHotkey}" />
|
||||||
|
<ToggleButton Name="CustomVSyncIntervalIncrement">
|
||||||
|
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalIncrement, Converter={StaticResource Key}}" />
|
||||||
|
</ToggleButton>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Margin="10,0,0,0" Orientation="Horizontal">
|
||||||
|
<TextBlock Text="{ext:Locale SettingsTabHotkeysDecrementCustomVSyncIntervalHotkey}" />
|
||||||
|
<ToggleButton Name="CustomVSyncIntervalDecrement">
|
||||||
|
<TextBlock Text="{Binding KeyboardHotkey.CustomVSyncIntervalDecrement, Converter={StaticResource Key}}" />
|
||||||
|
</ToggleButton>
|
||||||
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
|
|||||||
@@ -82,8 +82,8 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
|||||||
|
|
||||||
switch (button.Name)
|
switch (button.Name)
|
||||||
{
|
{
|
||||||
case "ToggleVsync":
|
case "ToggleVSyncMode":
|
||||||
viewModel.KeyboardHotkey.ToggleVsync = buttonValue.AsHidType<Key>();
|
viewModel.KeyboardHotkey.ToggleVSyncMode = buttonValue.AsHidType<Key>();
|
||||||
break;
|
break;
|
||||||
case "Screenshot":
|
case "Screenshot":
|
||||||
viewModel.KeyboardHotkey.Screenshot = buttonValue.AsHidType<Key>();
|
viewModel.KeyboardHotkey.Screenshot = buttonValue.AsHidType<Key>();
|
||||||
@@ -109,6 +109,12 @@ namespace Ryujinx.Ava.UI.Views.Settings
|
|||||||
case "VolumeDown":
|
case "VolumeDown":
|
||||||
viewModel.KeyboardHotkey.VolumeDown = buttonValue.AsHidType<Key>();
|
viewModel.KeyboardHotkey.VolumeDown = buttonValue.AsHidType<Key>();
|
||||||
break;
|
break;
|
||||||
|
case "CustomVSyncIntervalIncrement":
|
||||||
|
viewModel.KeyboardHotkey.CustomVSyncIntervalIncrement = buttonValue.AsHidType<Key>();
|
||||||
|
break;
|
||||||
|
case "CustomVSyncIntervalDecrement":
|
||||||
|
viewModel.KeyboardHotkey.CustomVSyncIntervalDecrement = buttonValue.AsHidType<Key>();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
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:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
xmlns:helpers="clr-namespace:Ryujinx.Ava.UI.Helpers"
|
||||||
@@ -181,11 +182,68 @@
|
|||||||
Width="350"
|
Width="350"
|
||||||
ToolTip.Tip="{ext:Locale TimeTooltip}" />
|
ToolTip.Tip="{ext:Locale TimeTooltip}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<CheckBox IsChecked="{Binding EnableVsync}">
|
<StackPanel Margin="0,0,0,10"
|
||||||
|
Orientation="Horizontal">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Text="{ext:Locale SettingsTabSystemEnableVsync}"
|
VerticalAlignment="Center"
|
||||||
ToolTip.Tip="{ext:Locale VSyncToggleTooltip}" />
|
Text="{ext:Locale SettingsTabSystemVSyncMode}"
|
||||||
</CheckBox>
|
ToolTip.Tip="{ext:Locale SettingsTabSystemVSyncModeTooltip}"
|
||||||
|
Width="250" />
|
||||||
|
<ComboBox
|
||||||
|
IsVisible="{Binding EnableCustomVSyncInterval}"
|
||||||
|
SelectedIndex="{Binding VSyncMode}"
|
||||||
|
ToolTip.Tip="{ext:Locale SettingsTabSystemVSyncModeTooltipCustom}"
|
||||||
|
HorizontalContentAlignment="Left"
|
||||||
|
Width="350">
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="{ext:Locale SettingsTabSystemVSyncModeSwitch}" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="{ext:Locale SettingsTabSystemVSyncModeUnbounded}" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="{ext:Locale SettingsTabSystemVSyncModeCustom}" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
</ComboBox>
|
||||||
|
<ComboBox
|
||||||
|
IsVisible="{Binding !EnableCustomVSyncInterval}"
|
||||||
|
SelectedIndex="{Binding VSyncMode}"
|
||||||
|
ToolTip.Tip="{ext:Locale SettingsTabSystemVSyncModeTooltip}"
|
||||||
|
HorizontalContentAlignment="Left"
|
||||||
|
Width="350">
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="{ext:Locale SettingsTabSystemVSyncModeSwitch}" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="{ext:Locale SettingsTabSystemVSyncModeUnbounded}" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
</ComboBox>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel IsVisible="{Binding EnableCustomVSyncInterval}"
|
||||||
|
Margin="0,0,0,10"
|
||||||
|
Orientation="Horizontal">
|
||||||
|
<TextBlock
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{ext:Locale SettingsTabSystemCustomVSyncIntervalPercentage}"
|
||||||
|
ToolTip.Tip="{ext:Locale SettingsTabSystemCustomVSyncIntervalValueTooltip}"
|
||||||
|
Width="250" />
|
||||||
|
<Slider Value="{Binding CustomVSyncIntervalPercentageProxy}"
|
||||||
|
ToolTip.Tip="{ext:Locale SettingsTabSystemCustomVSyncIntervalSliderTooltip}"
|
||||||
|
MinWidth="175"
|
||||||
|
Margin="10,-3,0,0"
|
||||||
|
Height="32"
|
||||||
|
Padding="0,-5"
|
||||||
|
TickFrequency="1"
|
||||||
|
IsSnapToTickEnabled="True"
|
||||||
|
LargeChange="10"
|
||||||
|
SmallChange="1"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Minimum="10"
|
||||||
|
Maximum="400" />
|
||||||
|
<TextBlock Margin="5,0"
|
||||||
|
Width="40"
|
||||||
|
Text="{Binding CustomVSyncIntervalPercentageText}"/>
|
||||||
|
</StackPanel>
|
||||||
<CheckBox IsChecked="{Binding EnableFsIntegrityChecks}">
|
<CheckBox IsChecked="{Binding EnableFsIntegrityChecks}">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Text="{ext:Locale SettingsTabSystemEnableFsIntegrityChecks}"
|
Text="{ext:Locale SettingsTabSystemEnableFsIntegrityChecks}"
|
||||||
@@ -244,6 +302,11 @@
|
|||||||
ToolTip.Tip="{ext:Locale IgnoreAppletTooltip}">
|
ToolTip.Tip="{ext:Locale IgnoreAppletTooltip}">
|
||||||
<TextBlock Text="{ext:Locale SettingsTabSystemIgnoreApplet}" />
|
<TextBlock Text="{ext:Locale SettingsTabSystemIgnoreApplet}" />
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
|
<CheckBox
|
||||||
|
IsChecked="{Binding EnableCustomVSyncInterval}"
|
||||||
|
ToolTip.Tip="{ext:Locale SettingsTabSystemEnableCustomVSyncIntervalTooltip}">
|
||||||
|
<TextBlock Text="{ext:Locale SettingsTabSystemEnableCustomVSyncInterval}" />
|
||||||
|
</CheckBox>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
@@ -6,8 +6,10 @@
|
|||||||
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
xmlns:ext="clr-namespace:Ryujinx.Ava.Common.Markup"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:viewModel="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
xmlns:viewModel="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||||
Width="550"
|
MinWidth="550"
|
||||||
Height="260"
|
MinHeight="260"
|
||||||
|
MaxWidth="600"
|
||||||
|
MaxHeight="500"
|
||||||
Margin="0,-12,0,0"
|
Margin="0,-12,0,0"
|
||||||
d:DesignHeight="260"
|
d:DesignHeight="260"
|
||||||
d:DesignWidth="550"
|
d:DesignWidth="550"
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
{
|
{
|
||||||
public partial class MainWindow : StyleableAppWindow
|
public partial class MainWindow : StyleableAppWindow
|
||||||
{
|
{
|
||||||
|
internal static MainWindowViewModel MainWindowViewModel { get; private set; }
|
||||||
|
|
||||||
public MainWindowViewModel ViewModel { get; }
|
public MainWindowViewModel ViewModel { get; }
|
||||||
|
|
||||||
internal readonly AvaHostUIHandler UiHandler;
|
internal readonly AvaHostUIHandler UiHandler;
|
||||||
@@ -73,7 +75,7 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
|
|
||||||
public MainWindow()
|
public MainWindow()
|
||||||
{
|
{
|
||||||
DataContext = ViewModel = new MainWindowViewModel
|
DataContext = ViewModel = MainWindowViewModel = new MainWindowViewModel
|
||||||
{
|
{
|
||||||
Window = this
|
Window = this
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -109,7 +109,6 @@
|
|||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
ReverseOrder="{Binding IsMacOS}">
|
ReverseOrder="{Binding IsMacOS}">
|
||||||
<Button
|
<Button
|
||||||
HotKey="Enter"
|
|
||||||
Classes="accent"
|
Classes="accent"
|
||||||
Content="{ext:Locale SettingsButtonOk}"
|
Content="{ext:Locale SettingsButtonOk}"
|
||||||
Command="{Binding OkButton}" />
|
Command="{Binding OkButton}" />
|
||||||
|
|||||||
@@ -114,9 +114,14 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
if (showVersionUpToDate)
|
if (showVersionUpToDate)
|
||||||
{
|
{
|
||||||
await ContentDialogHelper.CreateUpdaterInfoDialog(
|
UserResult userResult = await ContentDialogHelper.CreateUpdaterUpToDateInfoDialog(
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
|
||||||
string.Empty);
|
string.Empty);
|
||||||
|
|
||||||
|
if (userResult is UserResult.Ok)
|
||||||
|
{
|
||||||
|
OpenHelper.OpenUrl(ReleaseInformation.GetChangelogForVersion(currentVersion));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_running = false;
|
_running = false;
|
||||||
@@ -133,9 +138,14 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
if (showVersionUpToDate)
|
if (showVersionUpToDate)
|
||||||
{
|
{
|
||||||
await ContentDialogHelper.CreateUpdaterInfoDialog(
|
UserResult userResult = await ContentDialogHelper.CreateUpdaterUpToDateInfoDialog(
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
|
||||||
string.Empty);
|
string.Empty);
|
||||||
|
|
||||||
|
if (userResult is UserResult.Ok)
|
||||||
|
{
|
||||||
|
OpenHelper.OpenUrl(ReleaseInformation.GetChangelogForVersion(currentVersion));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_running = false;
|
_running = false;
|
||||||
@@ -176,9 +186,14 @@ namespace Ryujinx.Ava
|
|||||||
{
|
{
|
||||||
if (showVersionUpToDate)
|
if (showVersionUpToDate)
|
||||||
{
|
{
|
||||||
await ContentDialogHelper.CreateUpdaterInfoDialog(
|
UserResult userResult = await ContentDialogHelper.CreateUpdaterUpToDateInfoDialog(
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterAlreadyOnLatestVersionMessage],
|
||||||
string.Empty);
|
string.Empty);
|
||||||
|
|
||||||
|
if (userResult is UserResult.Ok)
|
||||||
|
{
|
||||||
|
OpenHelper.OpenUrl(ReleaseInformation.GetChangelogForVersion(currentVersion));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_running = false;
|
_running = false;
|
||||||
@@ -206,19 +221,29 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
await Dispatcher.UIThread.InvokeAsync(async () =>
|
await Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
|
string newVersionString = ReleaseInformation.IsCanaryBuild
|
||||||
|
? $"Canary {currentVersion} -> Canary {newVersion}"
|
||||||
|
: $"{currentVersion} -> {newVersion}";
|
||||||
|
|
||||||
|
RequestUserToUpdate:
|
||||||
// Show a message asking the user if they want to update
|
// Show a message asking the user if they want to update
|
||||||
var shouldUpdate = await ContentDialogHelper.CreateChoiceDialog(
|
UserResult shouldUpdate = await ContentDialogHelper.CreateUpdaterChoiceDialog(
|
||||||
LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
|
LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
|
||||||
LocaleManager.Instance[LocaleKeys.RyujinxUpdaterMessage],
|
LocaleManager.Instance[LocaleKeys.RyujinxUpdaterMessage],
|
||||||
$"{Program.Version} -> {newVersion}");
|
newVersionString);
|
||||||
|
|
||||||
if (shouldUpdate)
|
switch (shouldUpdate)
|
||||||
{
|
{
|
||||||
|
case UserResult.Yes:
|
||||||
await UpdateRyujinx(mainWindow, _buildUrl);
|
await UpdateRyujinx(mainWindow, _buildUrl);
|
||||||
}
|
break;
|
||||||
else
|
// Secondary button maps to no, which in this case is the show changelog button.
|
||||||
{
|
case UserResult.No:
|
||||||
|
OpenHelper.OpenUrl(ReleaseInformation.GetChangelogUrl(currentVersion, newVersion));
|
||||||
|
goto RequestUserToUpdate;
|
||||||
|
default:
|
||||||
_running = false;
|
_running = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user