From 42f22fe5d79a2aa5866e84f4b0bcf3d2114fa62a Mon Sep 17 00:00:00 2001
From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com>
Date: Sun, 4 Aug 2024 18:56:27 +0100
Subject: [PATCH 01/36] Infra: Update Microsoft.IdentityModel.JsonWebTokens
(#7070)
* Update Microsoft.IdentityModel.JsonWebTokens
* Update
---
Directory.Packages.props | 2 +-
src/Ryujinx/UI/ViewModels/UserProfileViewModel.cs | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index e6e2f14b80..e722dd8838 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -20,7 +20,7 @@
-
+
diff --git a/src/Ryujinx/UI/ViewModels/UserProfileViewModel.cs b/src/Ryujinx/UI/ViewModels/UserProfileViewModel.cs
index 70274847f0..0b65e6d134 100644
--- a/src/Ryujinx/UI/ViewModels/UserProfileViewModel.cs
+++ b/src/Ryujinx/UI/ViewModels/UserProfileViewModel.cs
@@ -1,7 +1,7 @@
-using Microsoft.IdentityModel.Tokens;
using Ryujinx.Ava.UI.Models;
using System;
using System.Collections.ObjectModel;
+using System.Linq;
namespace Ryujinx.Ava.UI.ViewModels
{
@@ -11,7 +11,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{
Profiles = new ObservableCollection();
LostProfiles = new ObservableCollection();
- IsEmpty = LostProfiles.IsNullOrEmpty();
+ IsEmpty = !LostProfiles.Any();
}
public ObservableCollection Profiles { get; set; }
From e85ee673b10da5a314e68cea88caeacd2918f311 Mon Sep 17 00:00:00 2001
From: Julien Lebosquain
Date: Sun, 4 Aug 2024 20:04:12 +0200
Subject: [PATCH 02/36] Fix LocaleExtension SetRawSource usages + language perf
improvement (#7121)
* Avoid Avalonia CompiledBindingPathBuilder.SetRawSource
* Improve UI language change performance
---
src/Ryujinx/Common/Locale/LocaleExtension.cs | 7 +++++--
src/Ryujinx/Common/Locale/LocaleManager.cs | 4 +++-
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/src/Ryujinx/Common/Locale/LocaleExtension.cs b/src/Ryujinx/Common/Locale/LocaleExtension.cs
index 40661bf3a6..b5964aa85a 100644
--- a/src/Ryujinx/Common/Locale/LocaleExtension.cs
+++ b/src/Ryujinx/Common/Locale/LocaleExtension.cs
@@ -21,7 +21,7 @@ namespace Ryujinx.Ava.Common.Locale
var builder = new CompiledBindingPathBuilder();
- builder.SetRawSource(LocaleManager.Instance)
+ builder
.Property(new ClrPropertyInfo("Item",
obj => (LocaleManager.Instance[keyToUse]),
null,
@@ -32,7 +32,10 @@ namespace Ryujinx.Ava.Common.Locale
var path = builder.Build();
- var binding = new CompiledBindingExtension(path);
+ var binding = new CompiledBindingExtension(path)
+ {
+ Source = LocaleManager.Instance
+ };
return binding.ProvideValue(serviceProvider);
}
diff --git a/src/Ryujinx/Common/Locale/LocaleManager.cs b/src/Ryujinx/Common/Locale/LocaleManager.cs
index 257611e65a..96f648761b 100644
--- a/src/Ryujinx/Common/Locale/LocaleManager.cs
+++ b/src/Ryujinx/Common/Locale/LocaleManager.cs
@@ -139,9 +139,11 @@ namespace Ryujinx.Ava.Common.Locale
foreach (var item in locale)
{
- this[item.Key] = item.Value;
+ _localeStrings[item.Key] = item.Value;
}
+ OnPropertyChanged("Item");
+
LocaleChanged?.Invoke();
}
From 4a4b11871e362016b41c56d4dd4654ade0b894e0 Mon Sep 17 00:00:00 2001
From: gdkchan
Date: Mon, 5 Aug 2024 11:00:41 -0300
Subject: [PATCH 03/36] Fix same textures with unmapped start being considered
different (#7141)
* Fix same textures with unmapped start being considered different
* Consolidate IsInvalid check
* InvalidAddress const
* Fix typo
Co-authored-by: riperiperi
---------
Co-authored-by: riperiperi
---
src/Ryujinx.Memory/Range/IMultiRangeItem.cs | 18 +++++++++++++++++-
src/Ryujinx.Memory/Range/MemoryRange.cs | 18 +++++++++++++++++-
src/Ryujinx.Memory/Range/MultiRangeList.cs | 17 +++--------------
3 files changed, 37 insertions(+), 16 deletions(-)
diff --git a/src/Ryujinx.Memory/Range/IMultiRangeItem.cs b/src/Ryujinx.Memory/Range/IMultiRangeItem.cs
index 87fde2465f..5f9611c758 100644
--- a/src/Ryujinx.Memory/Range/IMultiRangeItem.cs
+++ b/src/Ryujinx.Memory/Range/IMultiRangeItem.cs
@@ -4,6 +4,22 @@ namespace Ryujinx.Memory.Range
{
MultiRange Range { get; }
- ulong BaseAddress => Range.GetSubRange(0).Address;
+ ulong BaseAddress
+ {
+ get
+ {
+ for (int index = 0; index < Range.Count; index++)
+ {
+ MemoryRange subRange = Range.GetSubRange(index);
+
+ if (!MemoryRange.IsInvalid(ref subRange))
+ {
+ return subRange.Address;
+ }
+ }
+
+ return MemoryRange.InvalidAddress;
+ }
+ }
}
}
diff --git a/src/Ryujinx.Memory/Range/MemoryRange.cs b/src/Ryujinx.Memory/Range/MemoryRange.cs
index 46aca9ba03..20e9d00bbd 100644
--- a/src/Ryujinx.Memory/Range/MemoryRange.cs
+++ b/src/Ryujinx.Memory/Range/MemoryRange.cs
@@ -5,6 +5,11 @@ namespace Ryujinx.Memory.Range
///
public readonly record struct MemoryRange
{
+ ///
+ /// Special address value used to indicate than an address is invalid.
+ ///
+ internal const ulong InvalidAddress = ulong.MaxValue;
+
///
/// An empty memory range, with a null address and zero size.
///
@@ -58,13 +63,24 @@ namespace Ryujinx.Memory.Range
return thisAddress < otherEndAddress && otherAddress < thisEndAddress;
}
+ ///
+ /// Checks if a given sub-range of memory is invalid.
+ /// Those are used to represent unmapped memory regions (holes in the region mapping).
+ ///
+ /// Memory range to check
+ /// True if the memory range is considered invalid, false otherwise
+ internal static bool IsInvalid(ref MemoryRange subRange)
+ {
+ return subRange.Address == InvalidAddress;
+ }
+
///
/// Returns a string summary of the memory range.
///
/// A string summary of the memory range
public override string ToString()
{
- if (Address == ulong.MaxValue)
+ if (Address == InvalidAddress)
{
return $"[Unmapped 0x{Size:X}]";
}
diff --git a/src/Ryujinx.Memory/Range/MultiRangeList.cs b/src/Ryujinx.Memory/Range/MultiRangeList.cs
index 1804ff5c82..c3c6ae7972 100644
--- a/src/Ryujinx.Memory/Range/MultiRangeList.cs
+++ b/src/Ryujinx.Memory/Range/MultiRangeList.cs
@@ -30,7 +30,7 @@ namespace Ryujinx.Memory.Range
{
var subrange = range.GetSubRange(i);
- if (IsInvalid(ref subrange))
+ if (MemoryRange.IsInvalid(ref subrange))
{
continue;
}
@@ -56,7 +56,7 @@ namespace Ryujinx.Memory.Range
{
var subrange = range.GetSubRange(i);
- if (IsInvalid(ref subrange))
+ if (MemoryRange.IsInvalid(ref subrange))
{
continue;
}
@@ -99,7 +99,7 @@ namespace Ryujinx.Memory.Range
{
var subrange = range.GetSubRange(i);
- if (IsInvalid(ref subrange))
+ if (MemoryRange.IsInvalid(ref subrange))
{
continue;
}
@@ -142,17 +142,6 @@ namespace Ryujinx.Memory.Range
return overlapCount;
}
- ///
- /// Checks if a given sub-range of memory is invalid.
- /// Those are used to represent unmapped memory regions (holes in the region mapping).
- ///
- /// Memory range to checl
- /// True if the memory range is considered invalid, false otherwise
- private static bool IsInvalid(ref MemoryRange subRange)
- {
- return subRange.Address == ulong.MaxValue;
- }
-
///
/// Gets all items on the list starting at the specified memory address.
///
From 7969fb6bbaf49a7a84df379d072b94286e4f7ada Mon Sep 17 00:00:00 2001
From: jhorv <38920027+jhorv@users.noreply.github.com>
Date: Mon, 5 Aug 2024 20:09:08 -0400
Subject: [PATCH 04/36] Replace and remove obsolete ByteMemoryPool type (#7155)
* refactor: replace usage of ByteMemoryPool with MemoryOwner
* refactor: delete unused ByteMemoryPool and ByteMemoryPool.ByteMemoryPoolBuffer types
* refactor: change IMemoryOwner return types to MemoryOwner
* fix(perf): get span via `MemoryOwner.Span` directly instead of `MemoryOwner.Memory.Span`
* fix(perf): get span via MemoryOwner.Span directly instead of `MemoryOwner.Memory.Span`
* fix(perf): get span via MemoryOwner.Span directly instead of `MemoryOwner.Memory.Span`
---
.../ByteMemoryPool.ByteMemoryPoolBuffer.cs | 51 ---------
src/Ryujinx.Common/Memory/ByteMemoryPool.cs | 106 ------------------
.../Utilities/EmbeddedResources.cs | 6 +-
src/Ryujinx.Common/Utilities/StreamUtils.cs | 13 +--
.../Jit/MemoryManagerHostTracked.cs | 4 +-
.../VirtualMemoryManagerBase.cs | 4 +-
6 files changed, 13 insertions(+), 171 deletions(-)
delete mode 100644 src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs
delete mode 100644 src/Ryujinx.Common/Memory/ByteMemoryPool.cs
diff --git a/src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs b/src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs
deleted file mode 100644
index 05fb29ac71..0000000000
--- a/src/Ryujinx.Common/Memory/ByteMemoryPool.ByteMemoryPoolBuffer.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using System;
-using System.Buffers;
-using System.Threading;
-
-namespace Ryujinx.Common.Memory
-{
- public partial class ByteMemoryPool
- {
- ///
- /// Represents a that wraps an array rented from
- /// and exposes it as
- /// with a length of the requested size.
- ///
- private sealed class ByteMemoryPoolBuffer : IMemoryOwner
- {
- private byte[] _array;
- private readonly int _length;
-
- public ByteMemoryPoolBuffer(int length)
- {
- _array = ArrayPool.Shared.Rent(length);
- _length = length;
- }
-
- ///
- /// Returns a belonging to this owner.
- ///
- public Memory Memory
- {
- get
- {
- byte[] array = _array;
-
- ObjectDisposedException.ThrowIf(array is null, this);
-
- return new Memory(array, 0, _length);
- }
- }
-
- public void Dispose()
- {
- var array = Interlocked.Exchange(ref _array, null);
-
- if (array != null)
- {
- ArrayPool.Shared.Return(array);
- }
- }
- }
- }
-}
diff --git a/src/Ryujinx.Common/Memory/ByteMemoryPool.cs b/src/Ryujinx.Common/Memory/ByteMemoryPool.cs
deleted file mode 100644
index 6fd6a98aa7..0000000000
--- a/src/Ryujinx.Common/Memory/ByteMemoryPool.cs
+++ /dev/null
@@ -1,106 +0,0 @@
-using System;
-using System.Buffers;
-
-namespace Ryujinx.Common.Memory
-{
- ///
- /// Provides a pool of re-usable byte array instances.
- ///
- public static partial class ByteMemoryPool
- {
- ///
- /// Returns the maximum buffer size supported by this pool.
- ///
- public static int MaxBufferSize => Array.MaxLength;
-
- ///
- /// Rents a byte memory buffer from .
- /// The buffer may contain data from a prior use.
- ///
- /// The buffer's required length in bytes
- /// A wrapping the rented memory
- ///
- public static IMemoryOwner Rent(long length)
- => RentImpl(checked((int)length));
-
- ///
- /// Rents a byte memory buffer from .
- /// The buffer may contain data from a prior use.
- ///
- /// The buffer's required length in bytes
- /// A wrapping the rented memory
- ///
- public static IMemoryOwner Rent(ulong length)
- => RentImpl(checked((int)length));
-
- ///
- /// Rents a byte memory buffer from .
- /// The buffer may contain data from a prior use.
- ///
- /// The buffer's required length in bytes
- /// A wrapping the rented memory
- ///
- public static IMemoryOwner Rent(int length)
- => RentImpl(length);
-
- ///
- /// Rents a byte memory buffer from .
- /// The buffer's contents are cleared (set to all 0s) before returning.
- ///
- /// The buffer's required length in bytes
- /// A wrapping the rented memory
- ///
- public static IMemoryOwner RentCleared(long length)
- => RentCleared(checked((int)length));
-
- ///
- /// Rents a byte memory buffer from .
- /// The buffer's contents are cleared (set to all 0s) before returning.
- ///
- /// The buffer's required length in bytes
- /// A wrapping the rented memory
- ///
- public static IMemoryOwner RentCleared(ulong length)
- => RentCleared(checked((int)length));
-
- ///
- /// Rents a byte memory buffer from .
- /// The buffer's contents are cleared (set to all 0s) before returning.
- ///
- /// The buffer's required length in bytes
- /// A wrapping the rented memory
- ///
- public static IMemoryOwner RentCleared(int length)
- {
- var buffer = RentImpl(length);
-
- buffer.Memory.Span.Clear();
-
- return buffer;
- }
-
- ///
- /// Copies into a newly rented byte memory buffer.
- ///
- /// The byte buffer to copy
- /// A wrapping the rented memory with copied to it
- public static IMemoryOwner RentCopy(ReadOnlySpan buffer)
- {
- var copy = RentImpl(buffer.Length);
-
- buffer.CopyTo(copy.Memory.Span);
-
- return copy;
- }
-
- private static ByteMemoryPoolBuffer RentImpl(int length)
- {
- if ((uint)length > Array.MaxLength)
- {
- throw new ArgumentOutOfRangeException(nameof(length), length, null);
- }
-
- return new ByteMemoryPoolBuffer(length);
- }
- }
-}
diff --git a/src/Ryujinx.Common/Utilities/EmbeddedResources.cs b/src/Ryujinx.Common/Utilities/EmbeddedResources.cs
index e22571c966..7530c012a0 100644
--- a/src/Ryujinx.Common/Utilities/EmbeddedResources.cs
+++ b/src/Ryujinx.Common/Utilities/EmbeddedResources.cs
@@ -1,6 +1,6 @@
+using Ryujinx.Common.Memory;
using Ryujinx.Common.Utilities;
using System;
-using System.Buffers;
using System.IO;
using System.Linq;
using System.Reflection;
@@ -42,14 +42,14 @@ namespace Ryujinx.Common
return StreamUtils.StreamToBytes(stream);
}
- public static IMemoryOwner ReadFileToRentedMemory(string filename)
+ public static MemoryOwner ReadFileToRentedMemory(string filename)
{
var (assembly, path) = ResolveManifestPath(filename);
return ReadFileToRentedMemory(assembly, path);
}
- public static IMemoryOwner ReadFileToRentedMemory(Assembly assembly, string filename)
+ public static MemoryOwner ReadFileToRentedMemory(Assembly assembly, string filename)
{
using var stream = GetStream(assembly, filename);
diff --git a/src/Ryujinx.Common/Utilities/StreamUtils.cs b/src/Ryujinx.Common/Utilities/StreamUtils.cs
index 74b6af5ecf..aeb6e0d52a 100644
--- a/src/Ryujinx.Common/Utilities/StreamUtils.cs
+++ b/src/Ryujinx.Common/Utilities/StreamUtils.cs
@@ -1,6 +1,5 @@
using Microsoft.IO;
using Ryujinx.Common.Memory;
-using System.Buffers;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@@ -16,7 +15,7 @@ namespace Ryujinx.Common.Utilities
return output.ToArray();
}
- public static IMemoryOwner StreamToRentedMemory(Stream input)
+ public static MemoryOwner StreamToRentedMemory(Stream input)
{
if (input is MemoryStream inputMemoryStream)
{
@@ -26,9 +25,9 @@ namespace Ryujinx.Common.Utilities
{
long bytesExpected = input.Length;
- IMemoryOwner ownedMemory = ByteMemoryPool.Rent(bytesExpected);
+ MemoryOwner ownedMemory = MemoryOwner.Rent(checked((int)bytesExpected));
- var destSpan = ownedMemory.Memory.Span;
+ var destSpan = ownedMemory.Span;
int totalBytesRead = 0;
@@ -66,14 +65,14 @@ namespace Ryujinx.Common.Utilities
return stream.ToArray();
}
- private static IMemoryOwner MemoryStreamToRentedMemory(MemoryStream input)
+ private static MemoryOwner MemoryStreamToRentedMemory(MemoryStream input)
{
input.Position = 0;
- IMemoryOwner ownedMemory = ByteMemoryPool.Rent(input.Length);
+ MemoryOwner ownedMemory = MemoryOwner.Rent(checked((int)input.Length));
// Discard the return value because we assume reading a MemoryStream always succeeds completely.
- _ = input.Read(ownedMemory.Memory.Span);
+ _ = input.Read(ownedMemory.Span);
return ownedMemory;
}
diff --git a/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs b/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs
index 663d0aeb15..501109b861 100644
--- a/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs
+++ b/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs
@@ -303,9 +303,9 @@ namespace Ryujinx.Cpu.Jit
}
else
{
- IMemoryOwner memoryOwner = ByteMemoryPool.Rent(size);
+ MemoryOwner memoryOwner = MemoryOwner.Rent(size);
- Read(va, memoryOwner.Memory.Span);
+ Read(va, memoryOwner.Span);
return new WritableRegion(this, va, memoryOwner);
}
diff --git a/src/Ryujinx.Memory/VirtualMemoryManagerBase.cs b/src/Ryujinx.Memory/VirtualMemoryManagerBase.cs
index 506e25f668..f410722443 100644
--- a/src/Ryujinx.Memory/VirtualMemoryManagerBase.cs
+++ b/src/Ryujinx.Memory/VirtualMemoryManagerBase.cs
@@ -130,9 +130,9 @@ namespace Ryujinx.Memory
}
else
{
- IMemoryOwner memoryOwner = ByteMemoryPool.Rent(size);
+ MemoryOwner memoryOwner = MemoryOwner.Rent(size);
- Read(va, memoryOwner.Memory.Span);
+ Read(va, memoryOwner.Span);
return new WritableRegion(this, va, memoryOwner);
}
From 8d8983049ea23af0600e077b6389e2cd5de74c38 Mon Sep 17 00:00:00 2001
From: gdkchan
Date: Thu, 8 Aug 2024 17:07:24 -0300
Subject: [PATCH 05/36] Implement UQADD16, UQADD8, UQSUB16, UQSUB8, VQRDMULH,
VSLI and VSWP Arm32 instructions (#7174)
---
src/ARMeilleure/Decoders/OpCodeTable.cs | 8 +
src/ARMeilleure/Instructions/InstEmitAlu32.cs | 184 ++++++++++++++++++
.../Instructions/InstEmitSimdArithmetic32.cs | 27 +++
.../Instructions/InstEmitSimdMove32.cs | 20 ++
.../Instructions/InstEmitSimdShift32.cs | 30 +++
src/ARMeilleure/Instructions/InstName.cs | 7 +
.../Arm32/Target/Arm64/InstEmitMove.cs | 1 -
.../Arm32/Target/Arm64/InstEmitSaturate.cs | 32 +--
src/Ryujinx.Tests/Cpu/CpuTestAlu32.cs | 54 +++++
src/Ryujinx.Tests/Cpu/CpuTestSimd32.cs | 26 +++
src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs | 33 ++++
src/Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs | 36 +++-
12 files changed, 445 insertions(+), 13 deletions(-)
diff --git a/src/ARMeilleure/Decoders/OpCodeTable.cs b/src/ARMeilleure/Decoders/OpCodeTable.cs
index edc004125b..8595356704 100644
--- a/src/ARMeilleure/Decoders/OpCodeTable.cs
+++ b/src/ARMeilleure/Decoders/OpCodeTable.cs
@@ -822,6 +822,10 @@ namespace ARMeilleure.Decoders
SetA32("<<<<00000100xxxxxxxxxxxx1001xxxx", InstName.Umaal, InstEmit32.Umaal, OpCode32AluUmull.Create);
SetA32("<<<<0000101xxxxxxxxxxxxx1001xxxx", InstName.Umlal, InstEmit32.Umlal, OpCode32AluUmull.Create);
SetA32("<<<<0000100xxxxxxxxxxxxx1001xxxx", InstName.Umull, InstEmit32.Umull, OpCode32AluUmull.Create);
+ SetA32("<<<<01100110xxxxxxxx11110001xxxx", InstName.Uqadd16, InstEmit32.Uqadd16, OpCode32AluReg.Create);
+ SetA32("<<<<01100110xxxxxxxx11111001xxxx", InstName.Uqadd8, InstEmit32.Uqadd8, OpCode32AluReg.Create);
+ SetA32("<<<<01100110xxxxxxxx11110111xxxx", InstName.Uqsub16, InstEmit32.Uqsub16, OpCode32AluReg.Create);
+ SetA32("<<<<01100110xxxxxxxx11111111xxxx", InstName.Uqsub8, InstEmit32.Uqsub8, OpCode32AluReg.Create);
SetA32("<<<<0110111xxxxxxxxxxxxxxx01xxxx", InstName.Usat, InstEmit32.Usat, OpCode32Sat.Create);
SetA32("<<<<01101110xxxxxxxx11110011xxxx", InstName.Usat16, InstEmit32.Usat16, OpCode32Sat16.Create);
SetA32("<<<<01100101xxxxxxxx11111111xxxx", InstName.Usub8, InstEmit32.Usub8, OpCode32AluReg.Create);
@@ -1007,6 +1011,8 @@ namespace ARMeilleure.Decoders
SetAsimd("111100100x10xxxxxxxx1011xxx0xxxx", InstName.Vqdmulh, InstEmit32.Vqdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
SetAsimd("111100111x11<<10xxxx00101xx0xxx0", InstName.Vqmovn, InstEmit32.Vqmovn, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32);
SetAsimd("111100111x11<<10xxxx001001x0xxx0", InstName.Vqmovun, InstEmit32.Vqmovun, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32);
+ SetAsimd("111100110x01xxxxxxxx1011xxx0xxxx", InstName.Vqrdmulh, InstEmit32.Vqrdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
+ SetAsimd("111100110x10xxxxxxxx1011xxx0xxxx", InstName.Vqrdmulh, InstEmit32.Vqrdmulh, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
SetAsimd("1111001x1x>>>xxxxxxx100101x1xxx0", InstName.Vqrshrn, InstEmit32.Vqrshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
SetAsimd("111100111x>>>xxxxxxx100001x1xxx0", InstName.Vqrshrun, InstEmit32.Vqrshrun, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
SetAsimd("1111001x1x>>>xxxxxxx100100x1xxx0", InstName.Vqshrn, InstEmit32.Vqshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
@@ -1030,6 +1036,7 @@ namespace ARMeilleure.Decoders
SetAsimd("1111001x1x>>>xxxxxxx101000x1xxxx", InstName.Vshll, InstEmit32.Vshll, OpCode32SimdShImmLong.Create, OpCode32SimdShImmLong.CreateT32); // A1 encoding.
SetAsimd("1111001x1x>>>xxxxxxx0000>xx1xxxx", InstName.Vshr, InstEmit32.Vshr, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
SetAsimd("111100101x>>>xxxxxxx100000x1xxx0", InstName.Vshrn, InstEmit32.Vshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
+ SetAsimd("111100111x>>>xxxxxxx0101>xx1xxxx", InstName.Vsli, InstEmit32.Vsli_I, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
SetAsimd("1111001x1x>>>xxxxxxx0001>xx1xxxx", InstName.Vsra, InstEmit32.Vsra, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
SetAsimd("111101001x00xxxxxxxx0000xxx0xxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32);
SetAsimd("111101001x00xxxxxxxx0100xx0xxxxx", InstName.Vst1, InstEmit32.Vst1, OpCode32SimdMemSingle.Create, OpCode32SimdMemSingle.CreateT32);
@@ -1054,6 +1061,7 @@ namespace ARMeilleure.Decoders
SetAsimd("111100100x10xxxxxxxx1101xxx0xxxx", InstName.Vsub, InstEmit32.Vsub_V, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
SetAsimd("1111001x1x<
+ {
+ EmitSaturateUqadd(context, d, context.Add(n, m), 16);
+ }));
+ }
+
+ public static void Uqadd8(ArmEmitterContext context)
+ {
+ OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
+
+ SetIntA32(context, op.Rd, EmitUnsigned8BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
+ {
+ EmitSaturateUqadd(context, d, context.Add(n, m), 8);
+ }));
+ }
+
+ public static void Uqsub16(ArmEmitterContext context)
+ {
+ OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
+
+ SetIntA32(context, op.Rd, EmitUnsigned16BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
+ {
+ EmitSaturateUqsub(context, d, context.Subtract(n, m), 16);
+ }));
+ }
+
+ public static void Uqsub8(ArmEmitterContext context)
+ {
+ OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
+
+ SetIntA32(context, op.Rd, EmitUnsigned8BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
+ {
+ EmitSaturateUqsub(context, d, context.Subtract(n, m), 8);
+ }));
+ }
+
public static void Usat(ArmEmitterContext context)
{
OpCode32Sat op = (OpCode32Sat)context.CurrOp;
@@ -934,6 +976,148 @@ namespace ARMeilleure.Instructions
}
}
+ private static void EmitSaturateUqadd(ArmEmitterContext context, Operand result, Operand value, uint saturateTo)
+ {
+ Debug.Assert(saturateTo <= 32);
+
+ if (saturateTo == 32)
+ {
+ // No saturation possible for this case.
+
+ context.Copy(result, value);
+
+ return;
+ }
+ else if (saturateTo == 0)
+ {
+ // Result is always zero if we saturate 0 bits.
+
+ context.Copy(result, Const(0));
+
+ return;
+ }
+
+ // If the result is 0, the values are equal and we don't need saturation.
+ Operand lblNoSat = Label();
+ context.BranchIfFalse(lblNoSat, context.ShiftRightUI(value, Const((int)saturateTo)));
+
+ // Saturate.
+ context.Copy(result, Const(uint.MaxValue >> (32 - (int)saturateTo)));
+
+ Operand lblExit = Label();
+ context.Branch(lblExit);
+
+ context.MarkLabel(lblNoSat);
+
+ context.Copy(result, value);
+
+ context.MarkLabel(lblExit);
+ }
+
+ private static void EmitSaturateUqsub(ArmEmitterContext context, Operand result, Operand value, uint saturateTo)
+ {
+ Debug.Assert(saturateTo <= 32);
+
+ if (saturateTo == 32)
+ {
+ // No saturation possible for this case.
+
+ context.Copy(result, value);
+
+ return;
+ }
+ else if (saturateTo == 0)
+ {
+ // Result is always zero if we saturate 0 bits.
+
+ context.Copy(result, Const(0));
+
+ return;
+ }
+
+ // If the result is 0, the values are equal and we don't need saturation.
+ Operand lblNoSat = Label();
+ context.BranchIf(lblNoSat, value, Const(0), Comparison.GreaterOrEqual);
+
+ // Saturate.
+ // Assumes that the value can only underflow, since this is only used for unsigned subtraction.
+ context.Copy(result, Const(0));
+
+ Operand lblExit = Label();
+ context.Branch(lblExit);
+
+ context.MarkLabel(lblNoSat);
+
+ context.Copy(result, value);
+
+ context.MarkLabel(lblExit);
+ }
+
+ private static Operand EmitUnsigned16BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action elementAction)
+ {
+ Operand tempD = context.AllocateLocal(OperandType.I32);
+
+ Operand tempN = context.ZeroExtend16(OperandType.I32, rn);
+ Operand tempM = context.ZeroExtend16(OperandType.I32, rm);
+ elementAction(tempD, tempN, tempM);
+ Operand tempD2 = context.ZeroExtend16(OperandType.I32, tempD);
+
+ tempN = context.ShiftRightUI(rn, Const(16));
+ tempM = context.ShiftRightUI(rm, Const(16));
+ elementAction(tempD, tempN, tempM);
+ return context.BitwiseOr(tempD2, context.ShiftLeft(tempD, Const(16)));
+ }
+
+ private static Operand EmitSigned8BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action elementAction)
+ {
+ return Emit8BitPair(context, rn, rm, elementAction, unsigned: false);
+ }
+
+ private static Operand EmitUnsigned8BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action elementAction)
+ {
+ return Emit8BitPair(context, rn, rm, elementAction, unsigned: true);
+ }
+
+ private static Operand Emit8BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action elementAction, bool unsigned)
+ {
+ Operand tempD = context.AllocateLocal(OperandType.I32);
+ Operand result = default;
+
+ for (int b = 0; b < 4; b++)
+ {
+ Operand nByte = b != 0 ? context.ShiftRightUI(rn, Const(b * 8)) : rn;
+ Operand mByte = b != 0 ? context.ShiftRightUI(rm, Const(b * 8)) : rm;
+
+ if (unsigned)
+ {
+ nByte = context.ZeroExtend8(OperandType.I32, nByte);
+ mByte = context.ZeroExtend8(OperandType.I32, mByte);
+ }
+ else
+ {
+ nByte = context.SignExtend8(OperandType.I32, nByte);
+ mByte = context.SignExtend8(OperandType.I32, mByte);
+ }
+
+ elementAction(tempD, nByte, mByte);
+
+ if (b == 0)
+ {
+ result = context.ZeroExtend8(OperandType.I32, tempD);
+ }
+ else if (b < 3)
+ {
+ result = context.BitwiseOr(result, context.ShiftLeft(context.ZeroExtend8(OperandType.I32, tempD), Const(b * 8)));
+ }
+ else
+ {
+ result = context.BitwiseOr(result, context.ShiftLeft(tempD, Const(24)));
+ }
+ }
+
+ return result;
+ }
+
private static void EmitAluStore(ArmEmitterContext context, Operand value)
{
IOpCode32Alu op = (IOpCode32Alu)context.CurrOp;
diff --git a/src/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs b/src/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs
index dc2646a550..c807fc8585 100644
--- a/src/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs
+++ b/src/ARMeilleure/Instructions/InstEmitSimdArithmetic32.cs
@@ -1246,6 +1246,33 @@ namespace ARMeilleure.Instructions
EmitVectorUnaryNarrowOp32(context, (op1) => EmitSatQ(context, op1, 8 << op.Size, signedSrc: true, signedDst: false), signed: true);
}
+ public static void Vqrdmulh(ArmEmitterContext context)
+ {
+ OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
+ int eSize = 8 << op.Size;
+
+ EmitVectorBinaryOpI32(context, (op1, op2) =>
+ {
+ if (op.Size == 2)
+ {
+ op1 = context.SignExtend32(OperandType.I64, op1);
+ op2 = context.SignExtend32(OperandType.I64, op2);
+ }
+
+ Operand res = context.Multiply(op1, op2);
+ res = context.Add(res, Const(res.Type, 1L << (eSize - 2)));
+ res = context.ShiftRightSI(res, Const(eSize - 1));
+ res = EmitSatQ(context, res, eSize, signedSrc: true, signedDst: true);
+
+ if (op.Size == 2)
+ {
+ res = context.ConvertI64ToI32(res);
+ }
+
+ return res;
+ }, signed: true);
+ }
+
public static void Vqsub(ArmEmitterContext context)
{
OpCode32SimdReg op = (OpCode32SimdReg)context.CurrOp;
diff --git a/src/ARMeilleure/Instructions/InstEmitSimdMove32.cs b/src/ARMeilleure/Instructions/InstEmitSimdMove32.cs
index 9fa7409979..fb2641f66e 100644
--- a/src/ARMeilleure/Instructions/InstEmitSimdMove32.cs
+++ b/src/ARMeilleure/Instructions/InstEmitSimdMove32.cs
@@ -191,6 +191,26 @@ namespace ARMeilleure.Instructions
context.Copy(GetVecA32(op.Qd), res);
}
+ public static void Vswp(ArmEmitterContext context)
+ {
+ OpCode32Simd op = (OpCode32Simd)context.CurrOp;
+
+ if (op.Q)
+ {
+ Operand temp = context.Copy(GetVecA32(op.Qd));
+
+ context.Copy(GetVecA32(op.Qd), GetVecA32(op.Qm));
+ context.Copy(GetVecA32(op.Qm), temp);
+ }
+ else
+ {
+ Operand temp = ExtractScalar(context, OperandType.I64, op.Vd);
+
+ InsertScalar(context, op.Vd, ExtractScalar(context, OperandType.I64, op.Vm));
+ InsertScalar(context, op.Vm, temp);
+ }
+ }
+
public static void Vtbl(ArmEmitterContext context)
{
OpCode32SimdTbl op = (OpCode32SimdTbl)context.CurrOp;
diff --git a/src/ARMeilleure/Instructions/InstEmitSimdShift32.cs b/src/ARMeilleure/Instructions/InstEmitSimdShift32.cs
index e40600a477..e9e3b52b90 100644
--- a/src/ARMeilleure/Instructions/InstEmitSimdShift32.cs
+++ b/src/ARMeilleure/Instructions/InstEmitSimdShift32.cs
@@ -130,6 +130,36 @@ namespace ARMeilleure.Instructions
EmitVectorUnaryNarrowOp32(context, (op1) => context.ShiftRightUI(op1, Const(shift)));
}
+ public static void Vsli_I(ArmEmitterContext context)
+ {
+ OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
+ int shift = op.Shift;
+ int eSize = 8 << op.Size;
+
+ ulong mask = shift != 0 ? ulong.MaxValue >> (64 - shift) : 0UL;
+
+ Operand res = GetVec(op.Qd);
+
+ int elems = op.GetBytesCount() >> op.Size;
+
+ for (int index = 0; index < elems; index++)
+ {
+ Operand me = EmitVectorExtractZx(context, op.Qm, op.Im + index, op.Size);
+
+ Operand neShifted = context.ShiftLeft(me, Const(shift));
+
+ Operand de = EmitVectorExtractZx(context, op.Qd, op.Id + index, op.Size);
+
+ Operand deMasked = context.BitwiseAnd(de, Const(mask));
+
+ Operand e = context.BitwiseOr(neShifted, deMasked);
+
+ res = EmitVectorInsert(context, res, e, op.Id + index, op.Size);
+ }
+
+ context.Copy(GetVec(op.Qd), res);
+ }
+
public static void Vsra(ArmEmitterContext context)
{
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
diff --git a/src/ARMeilleure/Instructions/InstName.cs b/src/ARMeilleure/Instructions/InstName.cs
index 457abbf495..ac85412d1b 100644
--- a/src/ARMeilleure/Instructions/InstName.cs
+++ b/src/ARMeilleure/Instructions/InstName.cs
@@ -571,6 +571,10 @@ namespace ARMeilleure.Instructions
Umaal,
Umlal,
Umull,
+ Uqadd16,
+ Uqadd8,
+ Uqsub16,
+ Uqsub8,
Usat,
Usat16,
Usub8,
@@ -645,6 +649,7 @@ namespace ARMeilleure.Instructions
Vqdmulh,
Vqmovn,
Vqmovun,
+ Vqrdmulh,
Vqrshrn,
Vqrshrun,
Vqshrn,
@@ -666,6 +671,7 @@ namespace ARMeilleure.Instructions
Vshll,
Vshr,
Vshrn,
+ Vsli,
Vst1,
Vst2,
Vst3,
@@ -682,6 +688,7 @@ namespace ARMeilleure.Instructions
Vsub,
Vsubl,
Vsubw,
+ Vswp,
Vtbl,
Vtrn,
Vtst,
diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitMove.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitMove.cs
index 88850cb33f..d57750fc10 100644
--- a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitMove.cs
+++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitMove.cs
@@ -1,6 +1,5 @@
using Ryujinx.Cpu.LightningJit.CodeGen;
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
-using System.Diagnostics;
namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
{
diff --git a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitSaturate.cs b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitSaturate.cs
index e2354f448c..f1b6e395b6 100644
--- a/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitSaturate.cs
+++ b/src/Ryujinx.Cpu/LightningJit/Arm32/Target/Arm64/InstEmitSaturate.cs
@@ -114,7 +114,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
InstEmitCommon.EmitUnsigned16BitPair(context, rd, rn, rm, (d, n, m) =>
{
context.Arm64Assembler.Add(d, n, m);
- EmitSaturateUnsignedRange(context, d, 16);
+ EmitSaturateUqadd(context, d, 16);
});
}
@@ -123,7 +123,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
InstEmitCommon.EmitUnsigned8BitPair(context, rd, rn, rm, (d, n, m) =>
{
context.Arm64Assembler.Add(d, n, m);
- EmitSaturateUnsignedRange(context, d, 8);
+ EmitSaturateUqadd(context, d, 8);
});
}
@@ -140,7 +140,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
context.Arm64Assembler.Add(d, n, m);
}
- EmitSaturateUnsignedRange(context, d, 16);
+ EmitSaturateUq(context, d, 16, e == 0);
});
}
@@ -157,25 +157,25 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
context.Arm64Assembler.Sub(d, n, m);
}
- EmitSaturateUnsignedRange(context, d, 16);
+ EmitSaturateUq(context, d, 16, e != 0);
});
}
public static void Uqsub16(CodeGenContext context, uint rd, uint rn, uint rm)
{
- InstEmitCommon.EmitSigned16BitPair(context, rd, rn, rm, (d, n, m) =>
+ InstEmitCommon.EmitUnsigned16BitPair(context, rd, rn, rm, (d, n, m) =>
{
context.Arm64Assembler.Sub(d, n, m);
- EmitSaturateUnsignedRange(context, d, 16);
+ EmitSaturateUqsub(context, d, 16);
});
}
public static void Uqsub8(CodeGenContext context, uint rd, uint rn, uint rm)
{
- InstEmitCommon.EmitSigned8BitPair(context, rd, rn, rm, (d, n, m) =>
+ InstEmitCommon.EmitUnsigned8BitPair(context, rd, rn, rm, (d, n, m) =>
{
context.Arm64Assembler.Sub(d, n, m);
- EmitSaturateUnsignedRange(context, d, 8);
+ EmitSaturateUqsub(context, d, 8);
});
}
@@ -358,7 +358,17 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
}
}
- private static void EmitSaturateUnsignedRange(CodeGenContext context, Operand value, uint saturateTo)
+ private static void EmitSaturateUqadd(CodeGenContext context, Operand value, uint saturateTo)
+ {
+ EmitSaturateUq(context, value, saturateTo, isSub: false);
+ }
+
+ private static void EmitSaturateUqsub(CodeGenContext context, Operand value, uint saturateTo)
+ {
+ EmitSaturateUq(context, value, saturateTo, isSub: true);
+ }
+
+ private static void EmitSaturateUq(CodeGenContext context, Operand value, uint saturateTo, bool isSub)
{
Debug.Assert(saturateTo <= 32);
@@ -379,7 +389,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
return;
}
- context.Arm64Assembler.Lsr(tempRegister.Operand, value, InstEmitCommon.Const(32 - (int)saturateTo));
+ context.Arm64Assembler.Lsr(tempRegister.Operand, value, InstEmitCommon.Const((int)saturateTo));
int branchIndex = context.CodeWriter.InstructionPointer;
@@ -387,7 +397,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
context.Arm64Assembler.Cbz(tempRegister.Operand, 0);
// Saturate.
- context.Arm64Assembler.Mov(value, uint.MaxValue >> (32 - (int)saturateTo));
+ context.Arm64Assembler.Mov(value, isSub ? 0u : uint.MaxValue >> (32 - (int)saturateTo));
int delta = context.CodeWriter.InstructionPointer - branchIndex;
context.CodeWriter.WriteInstructionAt(branchIndex, context.CodeWriter.ReadInstructionAt(branchIndex) | (uint)((delta & 0x7ffff) << 5));
diff --git a/src/Ryujinx.Tests/Cpu/CpuTestAlu32.cs b/src/Ryujinx.Tests/Cpu/CpuTestAlu32.cs
index 41365c624a..132ddfd0e8 100644
--- a/src/Ryujinx.Tests/Cpu/CpuTestAlu32.cs
+++ b/src/Ryujinx.Tests/Cpu/CpuTestAlu32.cs
@@ -25,6 +25,24 @@ namespace Ryujinx.Tests.Cpu
};
}
+ private static uint[] UQAddSub16()
+ {
+ return new[]
+ {
+ 0xe6600f10u, // UQADD16 R0, R0, R0
+ 0xe6600f70u, // UQSUB16 R0, R0, R0
+ };
+ }
+
+ private static uint[] UQAddSub8()
+ {
+ return new[]
+ {
+ 0xe6600f90u, // UQADD8 R0, R0, R0
+ 0xe6600ff0u, // UQSUB8 R0, R0, R0
+ };
+ }
+
private static uint[] SsatUsat()
{
return new[]
@@ -182,6 +200,42 @@ namespace Ryujinx.Tests.Cpu
CompareAgainstUnicorn();
}
+ [Test, Pairwise]
+ public void U_Q_AddSub_16([ValueSource(nameof(UQAddSub16))] uint opcode,
+ [Values(0u, 0xdu)] uint rd,
+ [Values(1u)] uint rm,
+ [Values(2u)] uint rn,
+ [Random(RndCnt)] uint w0,
+ [Random(RndCnt)] uint w1,
+ [Random(RndCnt)] uint w2)
+ {
+ opcode |= ((rm & 15) << 0) | ((rd & 15) << 12) | ((rn & 15) << 16);
+
+ uint sp = TestContext.CurrentContext.Random.NextUInt();
+
+ SingleOpcode(opcode, r0: w0, r1: w1, r2: w2, sp: sp);
+
+ CompareAgainstUnicorn();
+ }
+
+ [Test, Pairwise]
+ public void U_Q_AddSub_8([ValueSource(nameof(UQAddSub8))] uint opcode,
+ [Values(0u, 0xdu)] uint rd,
+ [Values(1u)] uint rm,
+ [Values(2u)] uint rn,
+ [Random(RndCnt)] uint w0,
+ [Random(RndCnt)] uint w1,
+ [Random(RndCnt)] uint w2)
+ {
+ opcode |= ((rm & 15) << 0) | ((rd & 15) << 12) | ((rn & 15) << 16);
+
+ uint sp = TestContext.CurrentContext.Random.NextUInt();
+
+ SingleOpcode(opcode, r0: w0, r1: w1, r2: w2, sp: sp);
+
+ CompareAgainstUnicorn();
+ }
+
[Test, Pairwise]
public void Uadd8_Sel([Values(0u)] uint rd,
[Values(1u)] uint rm,
diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimd32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimd32.cs
index 6087a68344..f843fd561a 100644
--- a/src/Ryujinx.Tests/Cpu/CpuTestSimd32.cs
+++ b/src/Ryujinx.Tests/Cpu/CpuTestSimd32.cs
@@ -327,6 +327,32 @@ namespace Ryujinx.Tests.Cpu
CompareAgainstUnicorn();
}
+
+ [Test, Pairwise, Description("VSWP D0, D0")]
+ public void Vswp([Values(0u, 1u)] uint rd,
+ [Values(0u, 1u)] uint rm,
+ [Values] bool q)
+ {
+ uint opcode = 0xf3b20000u; // VSWP D0, D0
+
+ if (q)
+ {
+ opcode |= 1u << 6;
+
+ rd &= ~1u;
+ rm &= ~1u;
+ }
+
+ opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
+ opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
+
+ V128 v0 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong());
+ V128 v1 = new(TestContext.CurrentContext.Random.NextULong(), TestContext.CurrentContext.Random.NextULong());
+
+ SingleOpcode(opcode, v0: v0, v1: v1);
+
+ CompareAgainstUnicorn();
+ }
#endif
}
}
diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs
index 38e08bf895..843273dc23 100644
--- a/src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs
+++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdReg32.cs
@@ -909,6 +909,39 @@ namespace Ryujinx.Tests.Cpu
CompareAgainstUnicorn();
}
+ [Test, Pairwise, Description("VQRDMULH. , , ")]
+ public void Vqrdmulh_I([Range(0u, 5u)] uint rd,
+ [Range(0u, 5u)] uint rn,
+ [Range(0u, 5u)] uint rm,
+ [ValueSource(nameof(_8B4H2S1D_))] ulong z,
+ [ValueSource(nameof(_8B4H2S1D_))] ulong a,
+ [ValueSource(nameof(_8B4H2S1D_))] ulong b,
+ [Values(1u, 2u)] uint size) //
+ {
+ rd >>= 1;
+ rd <<= 1;
+ rn >>= 1;
+ rn <<= 1;
+ rm >>= 1;
+ rm <<= 1;
+
+ uint opcode = 0xf3100b40u & ~(3u << 20); // VQRDMULH.S16 Q0, Q0, Q0
+
+ opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
+ opcode |= ((rn & 0xf) << 16) | ((rn & 0x10) << 3);
+ opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
+
+ opcode |= (size & 0x3) << 20;
+
+ V128 v0 = MakeVectorE0E1(z, ~z);
+ V128 v1 = MakeVectorE0E1(a, ~a);
+ V128 v2 = MakeVectorE0E1(b, ~b);
+
+ SingleOpcode(opcode, v0: v0, v1: v1, v2: v2);
+
+ CompareAgainstUnicorn();
+ }
+
[Test, Pairwise]
public void Vp_Add_Long_Accumulate([Values(0u, 2u, 4u, 8u)] uint rd,
[Values(0u, 2u, 4u, 8u)] uint rm,
diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs
index 39b50867fd..7375f4d55c 100644
--- a/src/Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs
+++ b/src/Ryujinx.Tests/Cpu/CpuTestSimdShImm32.cs
@@ -202,7 +202,7 @@ namespace Ryujinx.Tests.Cpu
}
[Test, Pairwise, Description("VSHL. {}, , #")]
- public void Vshl_Imm([Values(0u)] uint rd,
+ public void Vshl_Imm([Values(0u, 1u)] uint rd,
[Values(2u, 0u)] uint rm,
[Values(0u, 1u, 2u, 3u)] uint size,
[Random(RndCntShiftImm)] uint shiftImm,
@@ -262,6 +262,40 @@ namespace Ryujinx.Tests.Cpu
CompareAgainstUnicorn();
}
+ [Test, Pairwise, Description("VSLI. {}, , #")]
+ public void Vsli([Values(0u, 1u)] uint rd,
+ [Values(2u, 0u)] uint rm,
+ [Values(0u, 1u, 2u, 3u)] uint size,
+ [Random(RndCntShiftImm)] uint shiftImm,
+ [Random(RndCnt)] ulong z,
+ [Random(RndCnt)] ulong a,
+ [Random(RndCnt)] ulong b,
+ [Values] bool q)
+ {
+ uint opcode = 0xf3800510u; // VORR.I32 D0, #0x800000 (immediate value changes it into SLI)
+ if (q)
+ {
+ opcode |= 1 << 6;
+ rm <<= 1;
+ rd <<= 1;
+ }
+
+ uint imm = 1u << ((int)size + 3);
+ imm |= shiftImm & (imm - 1);
+
+ opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
+ opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
+ opcode |= ((imm & 0x3f) << 16) | ((imm & 0x40) << 1);
+
+ V128 v0 = MakeVectorE0E1(z, z);
+ V128 v1 = MakeVectorE0E1(a, z);
+ V128 v2 = MakeVectorE0E1(b, z);
+
+ SingleOpcode(opcode, v0: v0, v1: v1, v2: v2);
+
+ CompareAgainstUnicorn();
+ }
+
[Test, Pairwise]
public void Vqshrn_Vqrshrn_Vrshrn_Imm([ValueSource(nameof(_Vqshrn_Vqrshrn_Vrshrn_Imm_))] uint opcode,
[Values(0u, 1u)] uint rd,
From 4f75e26ec7e61e606f812bd0149eef69bdc8a4ea Mon Sep 17 00:00:00 2001
From: gdkchan
Date: Mon, 12 Aug 2024 17:45:25 -0300
Subject: [PATCH 06/36] Clamp amount of mipmap levels to max allowed for all
backends (#7197)
* Clamp amount of mipmap levels to max allowed for all backends
* XML docs
* Remove using
---
src/Ryujinx.Graphics.GAL/TextureCreateInfo.cs | 20 ------------
src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs | 31 +++++++++++++++++++
.../Image/TextureStorage.cs | 2 +-
.../Image/TextureView.cs | 8 ++---
4 files changed, 36 insertions(+), 25 deletions(-)
diff --git a/src/Ryujinx.Graphics.GAL/TextureCreateInfo.cs b/src/Ryujinx.Graphics.GAL/TextureCreateInfo.cs
index 44090291dd..79c84db016 100644
--- a/src/Ryujinx.Graphics.GAL/TextureCreateInfo.cs
+++ b/src/Ryujinx.Graphics.GAL/TextureCreateInfo.cs
@@ -1,6 +1,5 @@
using Ryujinx.Common;
using System;
-using System.Numerics;
namespace Ryujinx.Graphics.GAL
{
@@ -113,25 +112,6 @@ namespace Ryujinx.Graphics.GAL
return 1;
}
- public int GetLevelsClamped()
- {
- int maxSize = Width;
-
- if (Target != Target.Texture1D &&
- Target != Target.Texture1DArray)
- {
- maxSize = Math.Max(maxSize, Height);
- }
-
- if (Target == Target.Texture3D)
- {
- maxSize = Math.Max(maxSize, Depth);
- }
-
- int maxLevels = BitOperations.Log2((uint)maxSize) + 1;
- return Math.Min(Levels, maxLevels);
- }
-
private static int GetLevelSize(int size, int level)
{
return Math.Max(1, size >> level);
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
index a4035577d3..4ed0a93c17 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TexturePool.cs
@@ -6,6 +6,7 @@ using Ryujinx.Memory.Range;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.Numerics;
using System.Threading;
namespace Ryujinx.Graphics.Gpu.Image
@@ -490,6 +491,8 @@ namespace Ryujinx.Graphics.Gpu.Image
levels = (maxLod - minLod) + 1;
}
+ levels = ClampLevels(target, width, height, depthOrLayers, levels);
+
SwizzleComponent swizzleR = descriptor.UnpackSwizzleR().Convert();
SwizzleComponent swizzleG = descriptor.UnpackSwizzleG().Convert();
SwizzleComponent swizzleB = descriptor.UnpackSwizzleB().Convert();
@@ -540,6 +543,34 @@ namespace Ryujinx.Graphics.Gpu.Image
swizzleA);
}
+ ///
+ /// Clamps the amount of mipmap levels to the maximum allowed for the given texture dimensions.
+ ///
+ /// Number of texture dimensions (1D, 2D, 3D, Cube, etc)
+ /// Width of the texture
+ /// Height of the texture, ignored for 1D textures
+ /// Depth of the texture for 3D textures, otherwise ignored
+ /// Original amount of mipmap levels
+ /// Clamped mipmap levels
+ private static int ClampLevels(Target target, int width, int height, int depthOrLayers, int levels)
+ {
+ int maxSize = width;
+
+ if (target != Target.Texture1D &&
+ target != Target.Texture1DArray)
+ {
+ maxSize = Math.Max(maxSize, height);
+ }
+
+ if (target == Target.Texture3D)
+ {
+ maxSize = Math.Max(maxSize, depthOrLayers);
+ }
+
+ int maxLevels = BitOperations.Log2((uint)maxSize) + 1;
+ return Math.Min(levels, maxLevels);
+ }
+
///
/// Gets the texture depth-stencil mode, based on the swizzle components of each color channel.
/// The depth-stencil mode is determined based on how the driver sets those parameters.
diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs
index 79c6cb685b..0ebafb04e9 100644
--- a/src/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs
+++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs
@@ -48,7 +48,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
internalFormat = (SizedInternalFormat)format.PixelInternalFormat;
}
- int levels = Info.GetLevelsClamped();
+ int levels = Info.Levels;
switch (Info.Target)
{
diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs
index 8a18e6132a..946eb755cc 100644
--- a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs
+++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs
@@ -51,7 +51,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
pixelInternalFormat = format.PixelInternalFormat;
}
- int levels = Info.GetLevelsClamped();
+ int levels = Info.Levels;
GL.TextureView(
Handle,
@@ -267,7 +267,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
public unsafe PinnedSpan GetData()
{
int size = 0;
- int levels = Info.GetLevelsClamped();
+ int levels = Info.Levels;
for (int level = 0; level < levels; level++)
{
@@ -426,7 +426,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
faces = 6;
}
- int levels = Info.GetLevelsClamped();
+ int levels = Info.Levels;
for (int level = 0; level < levels; level++)
{
@@ -716,7 +716,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
int width = Info.Width;
int height = Info.Height;
int depth = Info.Depth;
- int levels = Info.GetLevelsClamped();
+ int levels = Info.Levels;
int offset = 0;
From 23fa5f4c9c0ee865dd0a4def3be1822e598c9a91 Mon Sep 17 00:00:00 2001
From: Logan Stromberg
Date: Tue, 13 Aug 2024 06:23:11 -0700
Subject: [PATCH 07/36] Fix arbitrary game ordering when sorting by Favorites
(#7170)
* Fix arbitrary sorting by "Favorite" in the UI by making it the same as sorting alphabetically while giving favorites priority.
* Use a more engineered solution rather than string hacks.
* Address code style warnings. Add null checking. Make title name comparison case insensitive.
* one more style fix
---------
Co-authored-by: Logan Stromberg
---
.../ViewModels/AppListFavoriteComparable.cs | 43 +++++++++++++++++++
.../UI/ViewModels/MainWindowViewModel.cs | 4 +-
2 files changed, 45 insertions(+), 2 deletions(-)
create mode 100644 src/Ryujinx/UI/ViewModels/AppListFavoriteComparable.cs
diff --git a/src/Ryujinx/UI/ViewModels/AppListFavoriteComparable.cs b/src/Ryujinx/UI/ViewModels/AppListFavoriteComparable.cs
new file mode 100644
index 0000000000..e80984508e
--- /dev/null
+++ b/src/Ryujinx/UI/ViewModels/AppListFavoriteComparable.cs
@@ -0,0 +1,43 @@
+using Ryujinx.UI.App.Common;
+using System;
+
+namespace Ryujinx.Ava.UI.ViewModels
+{
+ ///
+ /// Implements a custom comparer which is used for sorting titles by favorite on a UI.
+ /// Returns a sorted list of favorites in alphabetical order, followed by all non-favorites sorted alphabetical.
+ ///
+ public readonly struct AppListFavoriteComparable : IComparable
+ {
+ ///
+ /// The application data being compared.
+ ///
+ private readonly ApplicationData app;
+
+ ///
+ /// Constructs a new with the specified application data.
+ ///
+ /// The app data being compared.
+ public AppListFavoriteComparable(ApplicationData app)
+ {
+ ArgumentNullException.ThrowIfNull(app, nameof(app));
+ this.app = app;
+ }
+
+ ///
+ public readonly int CompareTo(object o)
+ {
+ if (o is AppListFavoriteComparable other)
+ {
+ if (app.Favorite == other.app.Favorite)
+ {
+ return string.Compare(app.Name, other.app.Name, StringComparison.OrdinalIgnoreCase);
+ }
+
+ return app.Favorite ? -1 : 1;
+ }
+
+ throw new InvalidCastException($"Cannot cast {o.GetType()} to {nameof(AppListFavoriteComparable)}");
+ }
+ }
+}
diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs
index 134e903002..bd9f165b92 100644
--- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs
+++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs
@@ -965,8 +965,8 @@ namespace Ryujinx.Ava.UI.ViewModels
: SortExpressionComparer.Descending(app => app.FileSize),
ApplicationSort.Path => IsAscending ? SortExpressionComparer.Ascending(app => app.Path)
: SortExpressionComparer.Descending(app => app.Path),
- ApplicationSort.Favorite => !IsAscending ? SortExpressionComparer.Ascending(app => app.Favorite)
- : SortExpressionComparer.Descending(app => app.Favorite),
+ ApplicationSort.Favorite => IsAscending ? SortExpressionComparer.Ascending(app => new AppListFavoriteComparable(app))
+ : SortExpressionComparer.Descending(app => new AppListFavoriteComparable(app)),
_ => null,
#pragma warning restore IDE0055
};
From 0137c9e6353b7866153daf2859c48715a5c39349 Mon Sep 17 00:00:00 2001
From: Tsubasa0504 <60139445+Tsubasa0504@users.noreply.github.com>
Date: Sat, 17 Aug 2024 17:57:22 +0900
Subject: [PATCH 08/36] nim:eca : Stub CreateServerInterface2 (#7128)
* Add files via upload
* Add files via upload
* Update src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs
---------
Co-authored-by: Ac_K
---
.../HOS/Services/Nim/IShopServiceAccessServerInterface.cs | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs b/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs
index 52412489a7..d7e276ea01 100644
--- a/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs
@@ -40,5 +40,12 @@ namespace Ryujinx.HLE.HOS.Services.Nim
return ResultCode.Success;
}
+
+ [CommandCmif(5)] // 17.0.0+
+ // CreateServerInterface2(pid, handle, u64) -> object
+ public ResultCode CreateServerInterface2(ServiceCtx context)
+ {
+ return CreateServerInterface(context);
+ }
}
}
From 552c15739c10e9443e7e7a2acc775bfbc08faa0c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Toni=20F=C3=B6rster?=
Date: Tue, 20 Aug 2024 23:26:32 +0200
Subject: [PATCH 09/36] nuget: bump ImageSharp from 2.1.8 to 2.1.9 (#7160)
While building I got some warnings, so I updated the dependency.
`warning NU1903: Package 'SixLabors.ImageSharp' 2.1.8 has a known high severity vulnerability, https://github.com/advisories/GHSA-63p8-c4ww-9cg7`
---
Directory.Packages.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index e722dd8838..bffe8486e4 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -42,7 +42,7 @@
-
+
From 460f9faf4e3ccb5a21b1c6f149815dfda095a16e Mon Sep 17 00:00:00 2001
From: gdkchan
Date: Tue, 20 Aug 2024 20:49:17 -0300
Subject: [PATCH 10/36] Fix NRE when using buffer image array (#7159)
---
.../Image/TextureBindingsArrayCache.cs | 27 ++++++++++++-------
src/Ryujinx.Graphics.Vulkan/ImageArray.cs | 2 +-
src/Ryujinx.Graphics.Vulkan/ResourceArray.cs | 11 ++++++--
src/Ryujinx.Graphics.Vulkan/TextureArray.cs | 2 +-
4 files changed, 29 insertions(+), 13 deletions(-)
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs
index 01e34c7771..8b9243b1ec 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureBindingsArrayCache.cs
@@ -340,7 +340,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// True if any used entries of the pool might have been modified, false otherwise
public bool SamplerPoolModified()
{
- return SamplerPool.WasModified(ref _samplerPoolSequence);
+ return SamplerPool != null && SamplerPool.WasModified(ref _samplerPoolSequence);
}
}
@@ -516,12 +516,15 @@ namespace Ryujinx.Graphics.Gpu.Image
}
// Check if any of our cached samplers changed on the pool.
- foreach ((int samplerId, (Sampler sampler, SamplerDescriptor descriptor)) in SamplerIds)
+ if (SamplerPool != null)
{
- if (SamplerPool.GetCachedItem(samplerId) != sampler ||
- (sampler == null && SamplerPool.IsValidId(samplerId) && !SamplerPool.GetDescriptorRef(samplerId).Equals(descriptor)))
+ foreach ((int samplerId, (Sampler sampler, SamplerDescriptor descriptor)) in SamplerIds)
{
- return true;
+ if (SamplerPool.GetCachedItem(samplerId) != sampler ||
+ (sampler == null && SamplerPool.IsValidId(samplerId) && !SamplerPool.GetDescriptorRef(samplerId).Equals(descriptor)))
+ {
+ return true;
+ }
}
}
@@ -899,13 +902,19 @@ namespace Ryujinx.Graphics.Gpu.Image
}
}
- Sampler sampler = samplerPool?.Get(samplerId);
-
entry.TextureIds[textureId] = (texture, descriptor);
- entry.SamplerIds[samplerId] = (sampler, samplerPool?.GetDescriptorRef(samplerId) ?? default);
ITexture hostTexture = texture?.GetTargetTexture(bindingInfo.Target);
- ISampler hostSampler = sampler?.GetHostSampler(texture);
+ ISampler hostSampler = null;
+
+ if (!isImage && bindingInfo.Target != Target.TextureBuffer)
+ {
+ Sampler sampler = samplerPool?.Get(samplerId);
+
+ entry.SamplerIds[samplerId] = (sampler, samplerPool?.GetDescriptorRef(samplerId) ?? default);
+
+ hostSampler = sampler?.GetHostSampler(texture);
+ }
Format format = bindingInfo.Format;
diff --git a/src/Ryujinx.Graphics.Vulkan/ImageArray.cs b/src/Ryujinx.Graphics.Vulkan/ImageArray.cs
index e42750d3ce..467b011111 100644
--- a/src/Ryujinx.Graphics.Vulkan/ImageArray.cs
+++ b/src/Ryujinx.Graphics.Vulkan/ImageArray.cs
@@ -95,7 +95,7 @@ namespace Ryujinx.Graphics.Vulkan
{
_cachedCommandBufferIndex = -1;
_storages = null;
- SetDirty(_gd);
+ SetDirty(_gd, isImage: true);
}
public void QueueWriteToReadBarriers(CommandBufferScoped cbs, PipelineStageFlags stageFlags)
diff --git a/src/Ryujinx.Graphics.Vulkan/ResourceArray.cs b/src/Ryujinx.Graphics.Vulkan/ResourceArray.cs
index 0880a10f07..f96b4a8450 100644
--- a/src/Ryujinx.Graphics.Vulkan/ResourceArray.cs
+++ b/src/Ryujinx.Graphics.Vulkan/ResourceArray.cs
@@ -14,13 +14,20 @@ namespace Ryujinx.Graphics.Vulkan
private int _bindCount;
- protected void SetDirty(VulkanRenderer gd)
+ protected void SetDirty(VulkanRenderer gd, bool isImage)
{
ReleaseDescriptorSet();
if (_bindCount != 0)
{
- gd.PipelineInternal.ForceTextureDirty();
+ if (isImage)
+ {
+ gd.PipelineInternal.ForceImageDirty();
+ }
+ else
+ {
+ gd.PipelineInternal.ForceTextureDirty();
+ }
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/TextureArray.cs b/src/Ryujinx.Graphics.Vulkan/TextureArray.cs
index 31c408d64f..99238b1f5b 100644
--- a/src/Ryujinx.Graphics.Vulkan/TextureArray.cs
+++ b/src/Ryujinx.Graphics.Vulkan/TextureArray.cs
@@ -104,7 +104,7 @@ namespace Ryujinx.Graphics.Vulkan
{
_cachedCommandBufferIndex = -1;
_storages = null;
- SetDirty(_gd);
+ SetDirty(_gd, isImage: false);
}
public void QueueWriteToReadBarriers(CommandBufferScoped cbs, PipelineStageFlags stageFlags)
From b45a81458a0b48acd519ce280c7b8f3970e0ffed Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 21 Aug 2024 12:30:43 +0200
Subject: [PATCH 11/36] nuget: bump DynamicData from 9.0.1 to 9.0.4 (#7220)
Bumps [DynamicData](https://github.com/reactiveui/DynamicData) from 9.0.1 to 9.0.4.
- [Release notes](https://github.com/reactiveui/DynamicData/releases)
- [Changelog](https://github.com/reactivemarbles/DynamicData/blob/main/ReleaseNotes.md)
- [Commits](https://github.com/reactiveui/DynamicData/compare/9.0.1...9.0.4)
---
updated-dependencies:
- dependency-name: DynamicData
dependency-type: direct:production
update-type: version-update:semver-patch
...
Signed-off-by: dependabot[bot]
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
Directory.Packages.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index bffe8486e4..c310990720 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -13,7 +13,7 @@
-
+
From 3c61d560c39d6edf897183fe33b8047c25d2d895 Mon Sep 17 00:00:00 2001
From: David McFarland
Date: Tue, 27 Aug 2024 10:10:24 -0700
Subject: [PATCH 12/36] Fix deadlock in background translation thread shutdown
(#7239)
TryDequeue checks for _disposed before taking the lock. If another
thread calls Dispose before it takes the lock, it won't get woken up by
the PulseAll call, and will deadlock in Monitor.Wait.
Double-checking _disposed with the lock taken should avoid this.
---
src/ARMeilleure/Translation/TranslatorQueue.cs | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/ARMeilleure/Translation/TranslatorQueue.cs b/src/ARMeilleure/Translation/TranslatorQueue.cs
index cee2f9080d..831522bc14 100644
--- a/src/ARMeilleure/Translation/TranslatorQueue.cs
+++ b/src/ARMeilleure/Translation/TranslatorQueue.cs
@@ -80,7 +80,10 @@ namespace ARMeilleure.Translation
return true;
}
- Monitor.Wait(Sync);
+ if (!_disposed)
+ {
+ Monitor.Wait(Sync);
+ }
}
}
From e0acde04bb032fd056904b909b3fd00c1a6fb996 Mon Sep 17 00:00:00 2001
From: Emmanuel Hansen
Date: Sat, 31 Aug 2024 14:32:53 +0000
Subject: [PATCH 13/36] Replace ImageSharp with SkiaSharp everywhere (#7030)
* replace ImageSharp with SkiaSharp for inline keyboard applet rendering
* fix avalonia inline keyboard input
* remove image sharp from gtk3 project
* add skiasharp linux assets
* fix whitespace
* fix format
* fix ico image offset when saving shortcut to windows
---
Directory.Packages.props | 6 +-
src/Ryujinx.Gtk3/Program.cs | 7 -
src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj | 1 -
src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs | 41 +-
src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs | 29 +-
.../UI/Windows/UserProfilesManagerWindow.cs | 10 +-
.../SoftwareKeyboardRenderer.cs | 9 +-
.../SoftwareKeyboardRendererBase.cs | 390 ++++++++++--------
.../HOS/Services/Caps/CaptureManager.cs | 10 +-
src/Ryujinx.HLE/Ryujinx.HLE.csproj | 5 +-
.../Helper/ShortcutHelper.cs | 28 +-
.../Applet/AvaloniaDynamicTextInputHandler.cs | 9 +-
src/Ryujinx/UI/Helpers/OffscreenTextBox.cs | 3 +
src/Ryujinx/UI/Windows/MainWindow.axaml | 4 +-
14 files changed, 296 insertions(+), 256 deletions(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index c310990720..8a9fdc3be5 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -42,11 +42,11 @@
-
-
+
+
-
\ No newline at end of file
+
diff --git a/src/Ryujinx.Gtk3/Program.cs b/src/Ryujinx.Gtk3/Program.cs
index 8bb6516409..745335ac95 100644
--- a/src/Ryujinx.Gtk3/Program.cs
+++ b/src/Ryujinx.Gtk3/Program.cs
@@ -13,7 +13,6 @@ using Ryujinx.UI.Common.Configuration;
using Ryujinx.UI.Common.Helper;
using Ryujinx.UI.Common.SystemInfo;
using Ryujinx.UI.Widgets;
-using SixLabors.ImageSharp.Formats.Jpeg;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -162,12 +161,6 @@ namespace Ryujinx
});
};
- // Sets ImageSharp Jpeg Encoder Quality.
- SixLabors.ImageSharp.Configuration.Default.ImageFormatsManager.SetEncoder(JpegFormat.Instance, new JpegEncoder()
- {
- Quality = 100,
- });
-
string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName);
string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName);
diff --git a/src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj b/src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj
index b4453f9d79..722d6080be 100644
--- a/src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj
+++ b/src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj
@@ -30,7 +30,6 @@
-
diff --git a/src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs b/src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs
index 0e636792db..12139e87d9 100644
--- a/src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs
+++ b/src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs
@@ -13,16 +13,13 @@ using Ryujinx.Input.HLE;
using Ryujinx.UI.Common.Configuration;
using Ryujinx.UI.Common.Helper;
using Ryujinx.UI.Widgets;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Formats.Png;
-using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
+using SkiaSharp;
using System;
using System.Diagnostics;
using System.IO;
+using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
-using Image = SixLabors.ImageSharp.Image;
using Key = Ryujinx.Input.Key;
using ScalingFilter = Ryujinx.Graphics.GAL.ScalingFilter;
using Switch = Ryujinx.HLE.Switch;
@@ -404,23 +401,31 @@ namespace Ryujinx.UI
return;
}
- Image image = e.IsBgra ? Image.LoadPixelData(e.Data, e.Width, e.Height)
- : Image.LoadPixelData(e.Data, e.Width, e.Height);
+ var colorType = e.IsBgra ? SKColorType.Bgra8888 : SKColorType.Rgba8888;
+ using var image = new SKBitmap(new SKImageInfo(e.Width, e.Height, colorType, SKAlphaType.Premul));
- if (e.FlipX)
+ Marshal.Copy(e.Data, 0, image.GetPixels(), e.Data.Length);
+ using var surface = SKSurface.Create(image.Info);
+ var canvas = surface.Canvas;
+
+ if (e.FlipX || e.FlipY)
{
- image.Mutate(x => x.Flip(FlipMode.Horizontal));
+ canvas.Clear(SKColors.Transparent);
+
+ float scaleX = e.FlipX ? -1 : 1;
+ float scaleY = e.FlipY ? -1 : 1;
+
+ var matrix = SKMatrix.CreateScale(scaleX, scaleY, image.Width / 2f, image.Height / 2f);
+
+ canvas.SetMatrix(matrix);
}
+ canvas.DrawBitmap(image, new SKPoint());
- if (e.FlipY)
- {
- image.Mutate(x => x.Flip(FlipMode.Vertical));
- }
-
- image.SaveAsPng(path, new PngEncoder()
- {
- ColorType = PngColorType.Rgb,
- });
+ surface.Flush();
+ using var snapshot = surface.Snapshot();
+ using var encoded = snapshot.Encode(SKEncodedImageFormat.Png, 80);
+ using var file = File.OpenWrite(path);
+ encoded.SaveTo(file);
image.Dispose();
diff --git a/src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs
index d9ecd47b76..fcd960df0a 100644
--- a/src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs
+++ b/src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs
@@ -9,16 +9,13 @@ using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Memory;
using Ryujinx.HLE.FileSystem;
using Ryujinx.UI.Common.Configuration;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Formats.Png;
-using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
+using SkiaSharp;
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
-using Image = SixLabors.ImageSharp.Image;
+using System.Runtime.InteropServices;
namespace Ryujinx.UI.Windows
{
@@ -144,9 +141,11 @@ namespace Ryujinx.UI.Windows
stream.Position = 0;
- Image avatarImage = Image.LoadPixelData(DecompressYaz0(stream), 256, 256);
+ using var avatarImage = new SKBitmap(new SKImageInfo(256, 256, SKColorType.Rgba8888));
+ var data = DecompressYaz0(stream);
+ Marshal.Copy(data, 0, avatarImage.GetPixels(), data.Length);
- avatarImage.SaveAsPng(streamPng);
+ avatarImage.Encode(streamPng, SKEncodedImageFormat.Png, 80);
_avatarDict.Add(item.FullPath, streamPng.ToArray());
}
@@ -170,15 +169,23 @@ namespace Ryujinx.UI.Windows
{
using MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream();
- Image avatarImage = Image.Load(data, new PngDecoder());
+ using var avatarImage = SKBitmap.Decode(data);
+ using var surface = SKSurface.Create(avatarImage.Info);
- avatarImage.Mutate(x => x.BackgroundColor(new Rgba32(
+ var background = new SKColor(
(byte)(_backgroundColor.Red * 255),
(byte)(_backgroundColor.Green * 255),
(byte)(_backgroundColor.Blue * 255),
(byte)(_backgroundColor.Alpha * 255)
- )));
- avatarImage.SaveAsJpeg(streamJpg);
+ );
+ var canvas = surface.Canvas;
+ canvas.Clear(background);
+ canvas.DrawBitmap(avatarImage, new SKPoint());
+
+ surface.Flush();
+ using var snapshot = surface.Snapshot();
+ using var encoded = snapshot.Encode(SKEncodedImageFormat.Jpeg, 80);
+ encoded.SaveTo(streamJpg);
return streamJpg.ToArray();
}
diff --git a/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.cs
index d1e5fa9fc1..77afc5d1f1 100644
--- a/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.cs
+++ b/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.cs
@@ -4,15 +4,13 @@ using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.UI.Common.Configuration;
using Ryujinx.UI.Widgets;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Processing;
+using SkiaSharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
-using Image = SixLabors.ImageSharp.Image;
namespace Ryujinx.UI.Windows
{
@@ -177,13 +175,13 @@ namespace Ryujinx.UI.Windows
private void ProcessProfileImage(byte[] buffer)
{
- using Image image = Image.Load(buffer);
+ using var image = SKBitmap.Decode(buffer);
- image.Mutate(x => x.Resize(256, 256));
+ image.Resize(new SKImageInfo(256, 256), SKFilterQuality.High);
using MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream();
- image.SaveAsJpeg(streamJpg);
+ image.Encode(streamJpg, SKEncodedImageFormat.Jpeg, 80);
_bufferImageProfile = streamJpg.ToArray();
}
diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs
index 3f7516e6a7..239535ad5f 100644
--- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs
+++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRenderer.cs
@@ -112,11 +112,16 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{
// Update the parameters that were provided.
_state.InputText = inputText ?? _state.InputText;
- _state.CursorBegin = cursorBegin.GetValueOrDefault(_state.CursorBegin);
- _state.CursorEnd = cursorEnd.GetValueOrDefault(_state.CursorEnd);
+ _state.CursorBegin = Math.Max(0, cursorBegin.GetValueOrDefault(_state.CursorBegin));
+ _state.CursorEnd = Math.Min(cursorEnd.GetValueOrDefault(_state.CursorEnd), _state.InputText.Length);
_state.OverwriteMode = overwriteMode.GetValueOrDefault(_state.OverwriteMode);
_state.TypingEnabled = typingEnabled.GetValueOrDefault(_state.TypingEnabled);
+ var begin = _state.CursorBegin;
+ var end = _state.CursorEnd;
+ _state.CursorBegin = Math.Min(begin, end);
+ _state.CursorEnd = Math.Max(begin, end);
+
// Reset the cursor blink.
_state.TextBoxBlinkCounter = 0;
diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs
index 9e48568e13..cc62eca1df 100644
--- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs
+++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardRendererBase.cs
@@ -1,14 +1,9 @@
using Ryujinx.HLE.UI;
using Ryujinx.Memory;
-using SixLabors.Fonts;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Drawing.Processing;
-using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
+using SkiaSharp;
using System;
using System.Diagnostics;
using System.IO;
-using System.Numerics;
using System.Reflection;
using System.Runtime.InteropServices;
@@ -29,38 +24,39 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
private readonly object _bufferLock = new();
private RenderingSurfaceInfo _surfaceInfo = null;
- private Image _surface = null;
+ private SKImageInfo _imageInfo;
+ private SKSurface _surface = null;
private byte[] _bufferData = null;
- private readonly Image _ryujinxLogo = null;
- private readonly Image _padAcceptIcon = null;
- private readonly Image _padCancelIcon = null;
- private readonly Image _keyModeIcon = null;
+ private readonly SKBitmap _ryujinxLogo = null;
+ private readonly SKBitmap _padAcceptIcon = null;
+ private readonly SKBitmap _padCancelIcon = null;
+ private readonly SKBitmap _keyModeIcon = null;
private readonly float _textBoxOutlineWidth;
private readonly float _padPressedPenWidth;
- private readonly Color _textNormalColor;
- private readonly Color _textSelectedColor;
- private readonly Color _textOverCursorColor;
+ private readonly SKColor _textNormalColor;
+ private readonly SKColor _textSelectedColor;
+ private readonly SKColor _textOverCursorColor;
- private readonly Brush _panelBrush;
- private readonly Brush _disabledBrush;
- private readonly Brush _cursorBrush;
- private readonly Brush _selectionBoxBrush;
+ private readonly SKPaint _panelBrush;
+ private readonly SKPaint _disabledBrush;
+ private readonly SKPaint _cursorBrush;
+ private readonly SKPaint _selectionBoxBrush;
- private readonly Pen _textBoxOutlinePen;
- private readonly Pen _cursorPen;
- private readonly Pen _selectionBoxPen;
- private readonly Pen _padPressedPen;
+ private readonly SKPaint _textBoxOutlinePen;
+ private readonly SKPaint _cursorPen;
+ private readonly SKPaint _selectionBoxPen;
+ private readonly SKPaint _padPressedPen;
private readonly int _inputTextFontSize;
- private Font _messageFont;
- private Font _inputTextFont;
- private Font _labelsTextFont;
+ private SKFont _messageFont;
+ private SKFont _inputTextFont;
+ private SKFont _labelsTextFont;
- private RectangleF _panelRectangle;
- private Point _logoPosition;
+ private SKRect _panelRectangle;
+ private SKPoint _logoPosition;
private float _messagePositionY;
public SoftwareKeyboardRendererBase(IHostUITheme uiTheme)
@@ -78,10 +74,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
_padCancelIcon = LoadResource(typeof(SoftwareKeyboardRendererBase).Assembly, padCancelIconPath, 0, 0);
_keyModeIcon = LoadResource(typeof(SoftwareKeyboardRendererBase).Assembly, keyModeIconPath, 0, 0);
- Color panelColor = ToColor(uiTheme.DefaultBackgroundColor, 255);
- Color panelTransparentColor = ToColor(uiTheme.DefaultBackgroundColor, 150);
- Color borderColor = ToColor(uiTheme.DefaultBorderColor);
- Color selectionBackgroundColor = ToColor(uiTheme.SelectionBackgroundColor);
+ var panelColor = ToColor(uiTheme.DefaultBackgroundColor, 255);
+ var panelTransparentColor = ToColor(uiTheme.DefaultBackgroundColor, 150);
+ var borderColor = ToColor(uiTheme.DefaultBorderColor);
+ var selectionBackgroundColor = ToColor(uiTheme.SelectionBackgroundColor);
_textNormalColor = ToColor(uiTheme.DefaultForegroundColor);
_textSelectedColor = ToColor(uiTheme.SelectionForegroundColor);
@@ -92,15 +88,29 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
_textBoxOutlineWidth = 2;
_padPressedPenWidth = 2;
- _panelBrush = new SolidBrush(panelColor);
- _disabledBrush = new SolidBrush(panelTransparentColor);
- _cursorBrush = new SolidBrush(_textNormalColor);
- _selectionBoxBrush = new SolidBrush(selectionBackgroundColor);
+ _panelBrush = new SKPaint()
+ {
+ Color = panelColor,
+ IsAntialias = true
+ };
+ _disabledBrush = new SKPaint()
+ {
+ Color = panelTransparentColor,
+ IsAntialias = true
+ };
+ _cursorBrush = new SKPaint() { Color = _textNormalColor, IsAntialias = true };
+ _selectionBoxBrush = new SKPaint() { Color = selectionBackgroundColor, IsAntialias = true };
- _textBoxOutlinePen = Pens.Solid(borderColor, _textBoxOutlineWidth);
- _cursorPen = Pens.Solid(_textNormalColor, cursorWidth);
- _selectionBoxPen = Pens.Solid(selectionBackgroundColor, cursorWidth);
- _padPressedPen = Pens.Solid(borderColor, _padPressedPenWidth);
+ _textBoxOutlinePen = new SKPaint()
+ {
+ Color = borderColor,
+ StrokeWidth = _textBoxOutlineWidth,
+ IsStroke = true,
+ IsAntialias = true
+ };
+ _cursorPen = new SKPaint() { Color = _textNormalColor, StrokeWidth = cursorWidth, IsStroke = true, IsAntialias = true };
+ _selectionBoxPen = new SKPaint() { Color = selectionBackgroundColor, StrokeWidth = cursorWidth, IsStroke = true, IsAntialias = true };
+ _padPressedPen = new SKPaint() { Color = borderColor, StrokeWidth = _padPressedPenWidth, IsStroke = true, IsAntialias = true };
_inputTextFontSize = 20;
@@ -123,9 +133,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{
try
{
- _messageFont = SystemFonts.CreateFont(fontFamily, 26, FontStyle.Regular);
- _inputTextFont = SystemFonts.CreateFont(fontFamily, _inputTextFontSize, FontStyle.Regular);
- _labelsTextFont = SystemFonts.CreateFont(fontFamily, 24, FontStyle.Regular);
+ using var typeface = SKTypeface.FromFamilyName(fontFamily, SKFontStyle.Normal);
+ _messageFont = new SKFont(typeface, 26);
+ _inputTextFont = new SKFont(typeface, _inputTextFontSize);
+ _labelsTextFont = new SKFont(typeface, 24);
return;
}
@@ -137,7 +148,7 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
throw new Exception($"None of these fonts were found in the system: {String.Join(", ", availableFonts)}!");
}
- private static Color ToColor(ThemeColor color, byte? overrideAlpha = null, bool flipRgb = false)
+ private static SKColor ToColor(ThemeColor color, byte? overrideAlpha = null, bool flipRgb = false)
{
var a = (byte)(color.A * 255);
var r = (byte)(color.R * 255);
@@ -151,34 +162,33 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
b = (byte)(255 - b);
}
- return Color.FromRgba(r, g, b, overrideAlpha.GetValueOrDefault(a));
+ return new SKColor(r, g, b, overrideAlpha.GetValueOrDefault(a));
}
- private static Image LoadResource(Assembly assembly, string resourcePath, int newWidth, int newHeight)
+ private static SKBitmap LoadResource(Assembly assembly, string resourcePath, int newWidth, int newHeight)
{
Stream resourceStream = assembly.GetManifestResourceStream(resourcePath);
return LoadResource(resourceStream, newWidth, newHeight);
}
- private static Image LoadResource(Stream resourceStream, int newWidth, int newHeight)
+ private static SKBitmap LoadResource(Stream resourceStream, int newWidth, int newHeight)
{
Debug.Assert(resourceStream != null);
- var image = Image.Load(resourceStream);
+ var bitmap = SKBitmap.Decode(resourceStream);
if (newHeight != 0 && newWidth != 0)
{
- image.Mutate(x => x.Resize(newWidth, newHeight, KnownResamplers.Lanczos3));
+ var resized = bitmap.Resize(new SKImageInfo(newWidth, newHeight), SKFilterQuality.High);
+ if (resized != null)
+ {
+ bitmap.Dispose();
+ bitmap = resized;
+ }
}
- return image;
- }
-
- private static void SetGraphicsOptions(IImageProcessingContext context)
- {
- context.GetGraphicsOptions().Antialias = true;
- context.GetDrawingOptions().GraphicsOptions.Antialias = true;
+ return bitmap;
}
private void DrawImmutableElements()
@@ -187,22 +197,18 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
{
return;
}
+ var canvas = _surface.Canvas;
- _surface.Mutate(context =>
- {
- SetGraphicsOptions(context);
+ canvas.Clear(SKColors.Transparent);
+ canvas.DrawRect(_panelRectangle, _panelBrush);
+ canvas.DrawBitmap(_ryujinxLogo, _logoPosition);
- context.Clear(Color.Transparent);
- context.Fill(_panelBrush, _panelRectangle);
- context.DrawImage(_ryujinxLogo, _logoPosition, 1);
+ float halfWidth = _panelRectangle.Width / 2;
+ float buttonsY = _panelRectangle.Top + 185;
- float halfWidth = _panelRectangle.Width / 2;
- float buttonsY = _panelRectangle.Y + 185;
+ SKPoint disableButtonPosition = new(halfWidth + 180, buttonsY);
- PointF disableButtonPosition = new(halfWidth + 180, buttonsY);
-
- DrawControllerToggle(context, disableButtonPosition);
- });
+ DrawControllerToggle(canvas, disableButtonPosition);
}
public void DrawMutableElements(SoftwareKeyboardUIState state)
@@ -212,40 +218,43 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
return;
}
- _surface.Mutate(context =>
+ using var paint = new SKPaint(_messageFont)
{
- var messageRectangle = MeasureString(MessageText, _messageFont);
- float messagePositionX = (_panelRectangle.Width - messageRectangle.Width) / 2 - messageRectangle.X;
- float messagePositionY = _messagePositionY - messageRectangle.Y;
- var messagePosition = new PointF(messagePositionX, messagePositionY);
- var messageBoundRectangle = new RectangleF(messagePositionX, messagePositionY, messageRectangle.Width, messageRectangle.Height);
+ Color = _textNormalColor,
+ IsAntialias = true
+ };
- SetGraphicsOptions(context);
+ var canvas = _surface.Canvas;
+ var messageRectangle = MeasureString(MessageText, paint);
+ float messagePositionX = (_panelRectangle.Width - messageRectangle.Width) / 2 - messageRectangle.Left;
+ float messagePositionY = _messagePositionY - messageRectangle.Top;
+ var messagePosition = new SKPoint(messagePositionX, messagePositionY);
+ var messageBoundRectangle = SKRect.Create(messagePositionX, messagePositionY, messageRectangle.Width, messageRectangle.Height);
- context.Fill(_panelBrush, messageBoundRectangle);
+ canvas.DrawRect(messageBoundRectangle, _panelBrush);
- context.DrawText(MessageText, _messageFont, _textNormalColor, messagePosition);
+ canvas.DrawText(MessageText, messagePosition.X, messagePosition.Y + _messageFont.Metrics.XHeight + _messageFont.Metrics.Descent, paint);
- if (!state.TypingEnabled)
- {
- // Just draw a semi-transparent rectangle on top to fade the component with the background.
- // TODO (caian): This will not work if one decides to add make background semi-transparent as well.
+ if (!state.TypingEnabled)
+ {
+ // Just draw a semi-transparent rectangle on top to fade the component with the background.
+ // TODO (caian): This will not work if one decides to add make background semi-transparent as well.
- context.Fill(_disabledBrush, messageBoundRectangle);
- }
+ canvas.DrawRect(messageBoundRectangle, _disabledBrush);
+ }
- DrawTextBox(context, state);
+ DrawTextBox(canvas, state);
- float halfWidth = _panelRectangle.Width / 2;
- float buttonsY = _panelRectangle.Y + 185;
+ float halfWidth = _panelRectangle.Width / 2;
+ float buttonsY = _panelRectangle.Top + 185;
- PointF acceptButtonPosition = new(halfWidth - 180, buttonsY);
- PointF cancelButtonPosition = new(halfWidth, buttonsY);
- PointF disableButtonPosition = new(halfWidth + 180, buttonsY);
+ SKPoint acceptButtonPosition = new(halfWidth - 180, buttonsY);
+ SKPoint cancelButtonPosition = new(halfWidth, buttonsY);
+ SKPoint disableButtonPosition = new(halfWidth + 180, buttonsY);
+
+ DrawPadButton(canvas, acceptButtonPosition, _padAcceptIcon, AcceptText, state.AcceptPressed, state.ControllerEnabled);
+ DrawPadButton(canvas, cancelButtonPosition, _padCancelIcon, CancelText, state.CancelPressed, state.ControllerEnabled);
- DrawPadButton(context, acceptButtonPosition, _padAcceptIcon, AcceptText, state.AcceptPressed, state.ControllerEnabled);
- DrawPadButton(context, cancelButtonPosition, _padCancelIcon, CancelText, state.CancelPressed, state.ControllerEnabled);
- });
}
public void CreateSurface(RenderingSurfaceInfo surfaceInfo)
@@ -268,7 +277,8 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
Debug.Assert(_surfaceInfo.Height <= totalHeight);
Debug.Assert(_surfaceInfo.Pitch * _surfaceInfo.Height <= _surfaceInfo.Size);
- _surface = new Image((int)totalWidth, (int)totalHeight);
+ _imageInfo = new SKImageInfo((int)totalWidth, (int)totalHeight, SKColorType.Rgba8888);
+ _surface = SKSurface.Create(_imageInfo);
ComputeConstants();
DrawImmutableElements();
@@ -282,76 +292,81 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
int panelHeight = 240;
int panelPositionY = totalHeight - panelHeight;
- _panelRectangle = new RectangleF(0, panelPositionY, totalWidth, panelHeight);
+ _panelRectangle = SKRect.Create(0, panelPositionY, totalWidth, panelHeight);
_messagePositionY = panelPositionY + 60;
int logoPositionX = (totalWidth - _ryujinxLogo.Width) / 2;
int logoPositionY = panelPositionY + 18;
- _logoPosition = new Point(logoPositionX, logoPositionY);
+ _logoPosition = new SKPoint(logoPositionX, logoPositionY);
}
- private static RectangleF MeasureString(string text, Font font)
+ private static SKRect MeasureString(string text, SKPaint paint)
{
- TextOptions options = new(font);
+ SKRect bounds = SKRect.Empty;
if (text == "")
{
- FontRectangle emptyRectangle = TextMeasurer.MeasureSize(" ", options);
-
- return new RectangleF(0, emptyRectangle.Y, 0, emptyRectangle.Height);
+ paint.MeasureText(" ", ref bounds);
+ }
+ else
+ {
+ paint.MeasureText(text, ref bounds);
}
- FontRectangle rectangle = TextMeasurer.MeasureSize(text, options);
-
- return new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
+ return bounds;
}
- private static RectangleF MeasureString(ReadOnlySpan text, Font font)
+ private static SKRect MeasureString(ReadOnlySpan text, SKPaint paint)
{
- TextOptions options = new(font);
+ SKRect bounds = SKRect.Empty;
if (text == "")
{
- FontRectangle emptyRectangle = TextMeasurer.MeasureSize(" ", options);
- return new RectangleF(0, emptyRectangle.Y, 0, emptyRectangle.Height);
+ paint.MeasureText(" ", ref bounds);
+ }
+ else
+ {
+ paint.MeasureText(text, ref bounds);
}
- FontRectangle rectangle = TextMeasurer.MeasureSize(text, options);
-
- return new RectangleF(rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
+ return bounds;
}
- private void DrawTextBox(IImageProcessingContext context, SoftwareKeyboardUIState state)
+ private void DrawTextBox(SKCanvas canvas, SoftwareKeyboardUIState state)
{
- var inputTextRectangle = MeasureString(state.InputText, _inputTextFont);
+ using var textPaint = new SKPaint(_labelsTextFont)
+ {
+ IsAntialias = true,
+ Color = _textNormalColor
+ };
+ var inputTextRectangle = MeasureString(state.InputText, textPaint);
- float boxWidth = (int)(Math.Max(300, inputTextRectangle.Width + inputTextRectangle.X + 8));
+ float boxWidth = (int)(Math.Max(300, inputTextRectangle.Width + inputTextRectangle.Left + 8));
float boxHeight = 32;
- float boxY = _panelRectangle.Y + 110;
+ float boxY = _panelRectangle.Top + 110;
float boxX = (int)((_panelRectangle.Width - boxWidth) / 2);
- RectangleF boxRectangle = new(boxX, boxY, boxWidth, boxHeight);
+ SKRect boxRectangle = SKRect.Create(boxX, boxY, boxWidth, boxHeight);
- RectangleF boundRectangle = new(_panelRectangle.X, boxY - _textBoxOutlineWidth,
+ SKRect boundRectangle = SKRect.Create(_panelRectangle.Left, boxY - _textBoxOutlineWidth,
_panelRectangle.Width, boxHeight + 2 * _textBoxOutlineWidth);
- context.Fill(_panelBrush, boundRectangle);
+ canvas.DrawRect(boundRectangle, _panelBrush);
- context.Draw(_textBoxOutlinePen, boxRectangle);
+ canvas.DrawRect(boxRectangle, _textBoxOutlinePen);
- float inputTextX = (_panelRectangle.Width - inputTextRectangle.Width) / 2 - inputTextRectangle.X;
+ float inputTextX = (_panelRectangle.Width - inputTextRectangle.Width) / 2 - inputTextRectangle.Left;
float inputTextY = boxY + 5;
- var inputTextPosition = new PointF(inputTextX, inputTextY);
-
- context.DrawText(state.InputText, _inputTextFont, _textNormalColor, inputTextPosition);
+ var inputTextPosition = new SKPoint(inputTextX, inputTextY);
+ canvas.DrawText(state.InputText, inputTextPosition.X, inputTextPosition.Y + (_labelsTextFont.Metrics.XHeight + _labelsTextFont.Metrics.Descent), textPaint);
// Draw the cursor on top of the text and redraw the text with a different color if necessary.
- Color cursorTextColor;
- Brush cursorBrush;
- Pen cursorPen;
+ SKColor cursorTextColor;
+ SKPaint cursorBrush;
+ SKPaint cursorPen;
float cursorPositionYTop = inputTextY + 1;
float cursorPositionYBottom = cursorPositionYTop + _inputTextFontSize + 1;
@@ -371,12 +386,12 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
ReadOnlySpan textUntilBegin = state.InputText.AsSpan(0, state.CursorBegin);
ReadOnlySpan textUntilEnd = state.InputText.AsSpan(0, state.CursorEnd);
- var selectionBeginRectangle = MeasureString(textUntilBegin, _inputTextFont);
- var selectionEndRectangle = MeasureString(textUntilEnd, _inputTextFont);
+ var selectionBeginRectangle = MeasureString(textUntilBegin, textPaint);
+ var selectionEndRectangle = MeasureString(textUntilEnd, textPaint);
cursorVisible = true;
- cursorPositionXLeft = inputTextX + selectionBeginRectangle.Width + selectionBeginRectangle.X;
- cursorPositionXRight = inputTextX + selectionEndRectangle.Width + selectionEndRectangle.X;
+ cursorPositionXLeft = inputTextX + selectionBeginRectangle.Width + selectionBeginRectangle.Left;
+ cursorPositionXRight = inputTextX + selectionEndRectangle.Width + selectionEndRectangle.Left;
}
else
{
@@ -390,10 +405,10 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
int cursorBegin = Math.Min(state.InputText.Length, state.CursorBegin);
ReadOnlySpan textUntilCursor = state.InputText.AsSpan(0, cursorBegin);
- var cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont);
+ var cursorTextRectangle = MeasureString(textUntilCursor, textPaint);
cursorVisible = true;
- cursorPositionXLeft = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.X;
+ cursorPositionXLeft = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.Left;
if (state.OverwriteMode)
{
@@ -402,8 +417,8 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
if (state.CursorBegin < state.InputText.Length)
{
textUntilCursor = state.InputText.AsSpan(0, cursorBegin + 1);
- cursorTextRectangle = MeasureString(textUntilCursor, _inputTextFont);
- cursorPositionXRight = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.X;
+ cursorTextRectangle = MeasureString(textUntilCursor, textPaint);
+ cursorPositionXRight = inputTextX + cursorTextRectangle.Width + cursorTextRectangle.Left;
}
else
{
@@ -430,29 +445,32 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
if (cursorWidth == 0)
{
- PointF[] points = {
- new PointF(cursorPositionXLeft, cursorPositionYTop),
- new PointF(cursorPositionXLeft, cursorPositionYBottom),
- };
-
- context.DrawLine(cursorPen, points);
+ canvas.DrawLine(new SKPoint(cursorPositionXLeft, cursorPositionYTop),
+ new SKPoint(cursorPositionXLeft, cursorPositionYBottom),
+ cursorPen);
}
else
{
- var cursorRectangle = new RectangleF(cursorPositionXLeft, cursorPositionYTop, cursorWidth, cursorHeight);
+ var cursorRectangle = SKRect.Create(cursorPositionXLeft, cursorPositionYTop, cursorWidth, cursorHeight);
- context.Draw(cursorPen, cursorRectangle);
- context.Fill(cursorBrush, cursorRectangle);
+ canvas.DrawRect(cursorRectangle, cursorPen);
+ canvas.DrawRect(cursorRectangle, cursorBrush);
- Image textOverCursor = new((int)cursorRectangle.Width, (int)cursorRectangle.Height);
- textOverCursor.Mutate(context =>
+ using var textOverCursor = SKSurface.Create(new SKImageInfo((int)cursorRectangle.Width, (int)cursorRectangle.Height, SKColorType.Rgba8888));
+ var textOverCanvas = textOverCursor.Canvas;
+ var textRelativePosition = new SKPoint(inputTextPosition.X - cursorRectangle.Left, inputTextPosition.Y - cursorRectangle.Top);
+
+ using var cursorPaint = new SKPaint(_inputTextFont)
{
- var textRelativePosition = new PointF(inputTextPosition.X - cursorRectangle.X, inputTextPosition.Y - cursorRectangle.Y);
- context.DrawText(state.InputText, _inputTextFont, cursorTextColor, textRelativePosition);
- });
+ Color = cursorTextColor,
+ IsAntialias = true
+ };
- var cursorPosition = new Point((int)cursorRectangle.X, (int)cursorRectangle.Y);
- context.DrawImage(textOverCursor, cursorPosition, 1);
+ textOverCanvas.DrawText(state.InputText, textRelativePosition.X, textRelativePosition.Y + _inputTextFont.Metrics.XHeight + _inputTextFont.Metrics.Descent, cursorPaint);
+
+ var cursorPosition = new SKPoint((int)cursorRectangle.Left, (int)cursorRectangle.Top);
+ textOverCursor.Flush();
+ canvas.DrawSurface(textOverCursor, cursorPosition);
}
}
else if (!state.TypingEnabled)
@@ -460,11 +478,11 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
// Just draw a semi-transparent rectangle on top to fade the component with the background.
// TODO (caian): This will not work if one decides to add make background semi-transparent as well.
- context.Fill(_disabledBrush, boundRectangle);
+ canvas.DrawRect(boundRectangle, _disabledBrush);
}
}
- private void DrawPadButton(IImageProcessingContext context, PointF point, Image icon, string label, bool pressed, bool enabled)
+ private void DrawPadButton(SKCanvas canvas, SKPoint point, SKBitmap icon, string label, bool pressed, bool enabled)
{
// Use relative positions so we can center the entire drawing later.
@@ -473,12 +491,18 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
float iconWidth = icon.Width;
float iconHeight = icon.Height;
- var labelRectangle = MeasureString(label, _labelsTextFont);
+ using var paint = new SKPaint(_labelsTextFont)
+ {
+ Color = _textNormalColor,
+ IsAntialias = true
+ };
- float labelPositionX = iconWidth + 8 - labelRectangle.X;
+ var labelRectangle = MeasureString(label, paint);
+
+ float labelPositionX = iconWidth + 8 - labelRectangle.Left;
float labelPositionY = 3;
- float fullWidth = labelPositionX + labelRectangle.Width + labelRectangle.X;
+ float fullWidth = labelPositionX + labelRectangle.Width + labelRectangle.Left;
float fullHeight = iconHeight;
// Convert all relative positions into absolute.
@@ -489,24 +513,24 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
iconX += originX;
iconY += originY;
- var iconPosition = new Point((int)iconX, (int)iconY);
- var labelPosition = new PointF(labelPositionX + originX, labelPositionY + originY);
+ var iconPosition = new SKPoint((int)iconX, (int)iconY);
+ var labelPosition = new SKPoint(labelPositionX + originX, labelPositionY + originY);
- var selectedRectangle = new RectangleF(originX - 2 * _padPressedPenWidth, originY - 2 * _padPressedPenWidth,
+ var selectedRectangle = SKRect.Create(originX - 2 * _padPressedPenWidth, originY - 2 * _padPressedPenWidth,
fullWidth + 4 * _padPressedPenWidth, fullHeight + 4 * _padPressedPenWidth);
- var boundRectangle = new RectangleF(originX, originY, fullWidth, fullHeight);
+ var boundRectangle = SKRect.Create(originX, originY, fullWidth, fullHeight);
boundRectangle.Inflate(4 * _padPressedPenWidth, 4 * _padPressedPenWidth);
- context.Fill(_panelBrush, boundRectangle);
- context.DrawImage(icon, iconPosition, 1);
- context.DrawText(label, _labelsTextFont, _textNormalColor, labelPosition);
+ canvas.DrawRect(boundRectangle, _panelBrush);
+ canvas.DrawBitmap(icon, iconPosition);
+ canvas.DrawText(label, labelPosition.X, labelPosition.Y + _labelsTextFont.Metrics.XHeight + _labelsTextFont.Metrics.Descent, paint);
if (enabled)
{
if (pressed)
{
- context.Draw(_padPressedPen, selectedRectangle);
+ canvas.DrawRect(selectedRectangle, _padPressedPen);
}
}
else
@@ -514,21 +538,26 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
// Just draw a semi-transparent rectangle on top to fade the component with the background.
// TODO (caian): This will not work if one decides to add make background semi-transparent as well.
- context.Fill(_disabledBrush, boundRectangle);
+ canvas.DrawRect(boundRectangle, _disabledBrush);
}
}
- private void DrawControllerToggle(IImageProcessingContext context, PointF point)
+ private void DrawControllerToggle(SKCanvas canvas, SKPoint point)
{
- var labelRectangle = MeasureString(ControllerToggleText, _labelsTextFont);
+ using var paint = new SKPaint(_labelsTextFont)
+ {
+ IsAntialias = true,
+ Color = _textNormalColor
+ };
+ var labelRectangle = MeasureString(ControllerToggleText, paint);
// Use relative positions so we can center the entire drawing later.
float keyWidth = _keyModeIcon.Width;
float keyHeight = _keyModeIcon.Height;
- float labelPositionX = keyWidth + 8 - labelRectangle.X;
- float labelPositionY = -labelRectangle.Y - 1;
+ float labelPositionX = keyWidth + 8 - labelRectangle.Left;
+ float labelPositionY = -labelRectangle.Top - 1;
float keyX = 0;
float keyY = (int)((labelPositionY + labelRectangle.Height - keyHeight) / 2);
@@ -544,14 +573,14 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
keyX += originX;
keyY += originY;
- var labelPosition = new PointF(labelPositionX + originX, labelPositionY + originY);
- var overlayPosition = new Point((int)keyX, (int)keyY);
+ var labelPosition = new SKPoint(labelPositionX + originX, labelPositionY + originY);
+ var overlayPosition = new SKPoint((int)keyX, (int)keyY);
- context.DrawImage(_keyModeIcon, overlayPosition, 1);
- context.DrawText(ControllerToggleText, _labelsTextFont, _textNormalColor, labelPosition);
+ canvas.DrawBitmap(_keyModeIcon, overlayPosition);
+ canvas.DrawText(ControllerToggleText, labelPosition.X, labelPosition.Y + _labelsTextFont.Metrics.XHeight, paint);
}
- public void CopyImageToBuffer()
+ public unsafe void CopyImageToBuffer()
{
lock (_bufferLock)
{
@@ -561,21 +590,20 @@ namespace Ryujinx.HLE.HOS.Applets.SoftwareKeyboard
}
// Convert the pixel format used in the image to the one used in the Switch surface.
+ _surface.Flush();
- if (!_surface.DangerousTryGetSinglePixelMemory(out Memory pixels))
+ var buffer = new byte[_imageInfo.BytesSize];
+ fixed (byte* bufferPtr = buffer)
{
- return;
+ if (!_surface.ReadPixels(_imageInfo, (nint)bufferPtr, _imageInfo.RowBytes, 0, 0))
+ {
+ return;
+ }
}
- _bufferData = MemoryMarshal.AsBytes(pixels.Span).ToArray();
- Span dataConvert = MemoryMarshal.Cast(_bufferData);
+ _bufferData = buffer;
- Debug.Assert(_bufferData.Length == _surfaceInfo.Size);
-
- for (int i = 0; i < dataConvert.Length; i++)
- {
- dataConvert[i] = BitOperations.RotateRight(dataConvert[i], 8);
- }
+ Debug.Assert(buffer.Length == _surfaceInfo.Size);
}
}
diff --git a/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs b/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs
index 91a8958e6c..bf0c7e9dc6 100644
--- a/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Caps/CaptureManager.cs
@@ -1,10 +1,10 @@
using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Services.Caps.Types;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.PixelFormats;
+using SkiaSharp;
using System;
using System.IO;
using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
using System.Security.Cryptography;
namespace Ryujinx.HLE.HOS.Services.Caps
@@ -118,7 +118,11 @@ namespace Ryujinx.HLE.HOS.Services.Caps
}
// NOTE: The saved JPEG file doesn't have the limitation in the extra EXIF data.
- Image.LoadPixelData(screenshotData, 1280, 720).SaveAsJpegAsync(filePath);
+ using var bitmap = new SKBitmap(new SKImageInfo(1280, 720, SKColorType.Rgba8888));
+ Marshal.Copy(screenshotData, 0, bitmap.GetPixels(), screenshotData.Length);
+ using var data = bitmap.Encode(SKEncodedImageFormat.Jpeg, 80);
+ using var file = File.OpenWrite(filePath);
+ data.SaveTo(file);
return ResultCode.Success;
}
diff --git a/src/Ryujinx.HLE/Ryujinx.HLE.csproj b/src/Ryujinx.HLE/Ryujinx.HLE.csproj
index 0fcf9e4b57..83a11d4e02 100644
--- a/src/Ryujinx.HLE/Ryujinx.HLE.csproj
+++ b/src/Ryujinx.HLE/Ryujinx.HLE.csproj
@@ -2,6 +2,7 @@
net8.0
+ true
@@ -24,8 +25,8 @@
-
-
+
+
diff --git a/src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs b/src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs
index 58bdc90e6a..1849f40cbb 100644
--- a/src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs
+++ b/src/Ryujinx.UI.Common/Helper/ShortcutHelper.cs
@@ -1,10 +1,7 @@
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using ShellLink;
-using SixLabors.ImageSharp;
-using SixLabors.ImageSharp.Formats.Png;
-using SixLabors.ImageSharp.PixelFormats;
-using SixLabors.ImageSharp.Processing;
+using SkiaSharp;
using System;
using System.Collections.Generic;
using System.IO;
@@ -21,8 +18,8 @@ namespace Ryujinx.UI.Common.Helper
iconPath += ".ico";
MemoryStream iconDataStream = new(iconData);
- var image = Image.Load(iconDataStream);
- image.Mutate(x => x.Resize(128, 128));
+ using var image = SKBitmap.Decode(iconDataStream);
+ image.Resize(new SKImageInfo(128, 128), SKFilterQuality.High);
SaveBitmapAsIcon(image, iconPath);
var shortcut = Shortcut.CreateShortcut(basePath, GetArgsString(applicationFilePath, applicationId), iconPath, 0);
@@ -37,8 +34,10 @@ namespace Ryujinx.UI.Common.Helper
var desktopFile = EmbeddedResources.ReadAllText("Ryujinx.UI.Common/shortcut-template.desktop");
iconPath += ".png";
- var image = Image.Load(iconData);
- image.SaveAsPng(iconPath);
+ var image = SKBitmap.Decode(iconData);
+ using var data = image.Encode(SKEncodedImageFormat.Png, 100);
+ using var file = File.OpenWrite(iconPath);
+ data.SaveTo(file);
using StreamWriter outputFile = new(Path.Combine(desktopPath, cleanedAppName + ".desktop"));
outputFile.Write(desktopFile, cleanedAppName, iconPath, $"{basePath} {GetArgsString(applicationFilePath, applicationId)}");
@@ -78,8 +77,10 @@ namespace Ryujinx.UI.Common.Helper
}
const string IconName = "icon.png";
- var image = Image.Load(iconData);
- image.SaveAsPng(Path.Combine(resourceFolderPath, IconName));
+ var image = SKBitmap.Decode(iconData);
+ using var data = image.Encode(SKEncodedImageFormat.Png, 100);
+ using var file = File.OpenWrite(Path.Combine(resourceFolderPath, IconName));
+ data.SaveTo(file);
// plist file
using StreamWriter outputFile = new(Path.Combine(contentFolderPath, "Info.plist"));
@@ -148,7 +149,7 @@ namespace Ryujinx.UI.Common.Helper
/// The source bitmap image that will be saved as an .ico file
/// The location that the new .ico file will be saved too (Make sure to include '.ico' in the path).
[SupportedOSPlatform("windows")]
- private static void SaveBitmapAsIcon(Image source, string filePath)
+ private static void SaveBitmapAsIcon(SKBitmap source, string filePath)
{
// Code Modified From https://stackoverflow.com/a/11448060/368354 by Benlitz
byte[] header = { 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 32, 0, 0, 0, 0, 0, 22, 0, 0, 0 };
@@ -156,13 +157,16 @@ namespace Ryujinx.UI.Common.Helper
fs.Write(header);
// Writing actual data
- source.Save(fs, PngFormat.Instance);
+ using var data = source.Encode(SKEncodedImageFormat.Png, 100);
+ data.SaveTo(fs);
// Getting data length (file length minus header)
long dataLength = fs.Length - header.Length;
// Write it in the correct place
fs.Seek(14, SeekOrigin.Begin);
fs.WriteByte((byte)dataLength);
fs.WriteByte((byte)(dataLength >> 8));
+ fs.WriteByte((byte)(dataLength >> 16));
+ fs.WriteByte((byte)(dataLength >> 24));
}
}
}
diff --git a/src/Ryujinx/UI/Applet/AvaloniaDynamicTextInputHandler.cs b/src/Ryujinx/UI/Applet/AvaloniaDynamicTextInputHandler.cs
index 531d006115..0e7cfb8e6c 100644
--- a/src/Ryujinx/UI/Applet/AvaloniaDynamicTextInputHandler.cs
+++ b/src/Ryujinx/UI/Applet/AvaloniaDynamicTextInputHandler.cs
@@ -41,17 +41,12 @@ namespace Ryujinx.Ava.UI.Applet
private void TextChanged(string text)
{
- TextChangedEvent?.Invoke(text ?? string.Empty, _hiddenTextBox.SelectionStart, _hiddenTextBox.SelectionEnd, true);
+ TextChangedEvent?.Invoke(text ?? string.Empty, _hiddenTextBox.SelectionStart, _hiddenTextBox.SelectionEnd, false);
}
private void SelectionChanged(int selection)
{
- if (_hiddenTextBox.SelectionEnd < _hiddenTextBox.SelectionStart)
- {
- _hiddenTextBox.SelectionStart = _hiddenTextBox.SelectionEnd;
- }
-
- TextChangedEvent?.Invoke(_hiddenTextBox.Text ?? string.Empty, _hiddenTextBox.SelectionStart, _hiddenTextBox.SelectionEnd, true);
+ TextChangedEvent?.Invoke(_hiddenTextBox.Text ?? string.Empty, _hiddenTextBox.SelectionStart, _hiddenTextBox.SelectionEnd, false);
}
private void AvaloniaDynamicTextInputHandler_TextInput(object sender, string text)
diff --git a/src/Ryujinx/UI/Helpers/OffscreenTextBox.cs b/src/Ryujinx/UI/Helpers/OffscreenTextBox.cs
index a055f33538..dd736037ee 100644
--- a/src/Ryujinx/UI/Helpers/OffscreenTextBox.cs
+++ b/src/Ryujinx/UI/Helpers/OffscreenTextBox.cs
@@ -1,11 +1,14 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
+using System;
namespace Ryujinx.Ava.UI.Helpers
{
public class OffscreenTextBox : TextBox
{
+ protected override Type StyleKeyOverride => typeof(TextBox);
+
public static RoutedEvent GetKeyDownRoutedEvent()
{
return KeyDownEvent;
diff --git a/src/Ryujinx/UI/Windows/MainWindow.axaml b/src/Ryujinx/UI/Windows/MainWindow.axaml
index 6c2042f93c..3a2e02c260 100644
--- a/src/Ryujinx/UI/Windows/MainWindow.axaml
+++ b/src/Ryujinx/UI/Windows/MainWindow.axaml
@@ -42,12 +42,10 @@
-
-
+
From 2c5c0392f9ff80a3907bbf376a13f797ebbc12cc Mon Sep 17 00:00:00 2001
From: Emmanuel Hansen
Date: Sat, 31 Aug 2024 14:39:26 +0000
Subject: [PATCH 14/36] Make HLE project AOT friendly (#7085)
* add hle service generator
remove usage of reflection in device state
* remove rd.xml generation
* make applet manager reflection free
* fix typos
* fix encoding
* fix style report
* remove rogue generator reference
* remove double assignment
---
Ryujinx.sln | 6 ++
src/Ryujinx.Graphics.Device/DeviceState.cs | 5 +-
src/Ryujinx.Graphics.Device/SizeCalculator.cs | 63 ---------------
.../Engine/Threed/StateUpdateTracker.cs | 5 +-
src/Ryujinx.HLE.Generators/CodeGenerator.cs | 63 +++++++++++++++
.../IpcServiceGenerator.cs | 76 +++++++++++++++++++
.../Ryujinx.HLE.Generators.csproj | 19 +++++
.../ServiceSyntaxReceiver.cs | 24 ++++++
src/Ryujinx.HLE/HOS/Applets/AppletManager.cs | 33 ++++----
.../HOS/Services/Sm/IUserInterface.cs | 7 +-
src/Ryujinx.HLE/Ryujinx.HLE.csproj | 1 +
11 files changed, 215 insertions(+), 87 deletions(-)
delete mode 100644 src/Ryujinx.Graphics.Device/SizeCalculator.cs
create mode 100644 src/Ryujinx.HLE.Generators/CodeGenerator.cs
create mode 100644 src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs
create mode 100644 src/Ryujinx.HLE.Generators/Ryujinx.HLE.Generators.csproj
create mode 100644 src/Ryujinx.HLE.Generators/ServiceSyntaxReceiver.cs
diff --git a/Ryujinx.sln b/Ryujinx.sln
index b8304164d5..76ebd573f3 100644
--- a/Ryujinx.sln
+++ b/Ryujinx.sln
@@ -87,6 +87,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon", "src\Ryuj
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Kernel.Generators", "src\Ryujinx.Horizon.Kernel.Generators\Ryujinx.Horizon.Kernel.Generators.csproj", "{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.HLE.Generators", "src\Ryujinx.HLE.Generators\Ryujinx.HLE.Generators.csproj", "{B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -249,6 +251,10 @@ Global
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7F55A45D-4E1D-4A36-ADD3-87F29A285AA2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B575BCDE-2FD8-4A5D-8756-31CDD7FE81F0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/Ryujinx.Graphics.Device/DeviceState.cs b/src/Ryujinx.Graphics.Device/DeviceState.cs
index de8582a3b6..54178a4140 100644
--- a/src/Ryujinx.Graphics.Device/DeviceState.cs
+++ b/src/Ryujinx.Graphics.Device/DeviceState.cs
@@ -39,7 +39,10 @@ namespace Ryujinx.Graphics.Device
{
var field = fields[fieldIndex];
- int sizeOfField = SizeCalculator.SizeOf(field.FieldType);
+ var currentFieldOffset = (int)Marshal.OffsetOf(field.Name);
+ var nextFieldOffset = fieldIndex + 1 == fields.Length ? Unsafe.SizeOf() : (int)Marshal.OffsetOf(fields[fieldIndex + 1].Name);
+
+ int sizeOfField = nextFieldOffset - currentFieldOffset;
for (int i = 0; i < ((sizeOfField + 3) & ~3); i += 4)
{
diff --git a/src/Ryujinx.Graphics.Device/SizeCalculator.cs b/src/Ryujinx.Graphics.Device/SizeCalculator.cs
deleted file mode 100644
index 54820ec36f..0000000000
--- a/src/Ryujinx.Graphics.Device/SizeCalculator.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-using System;
-using System.Reflection;
-
-namespace Ryujinx.Graphics.Device
-{
- public static class SizeCalculator
- {
- public static int SizeOf(Type type)
- {
- // Is type a enum type?
- if (type.IsEnum)
- {
- type = type.GetEnumUnderlyingType();
- }
-
- // Is type a pointer type?
- if (type.IsPointer || type == typeof(IntPtr) || type == typeof(UIntPtr))
- {
- return IntPtr.Size;
- }
-
- // Is type a struct type?
- if (type.IsValueType && !type.IsPrimitive)
- {
- // Check if the struct has a explicit size, if so, return that.
- if (type.StructLayoutAttribute.Size != 0)
- {
- return type.StructLayoutAttribute.Size;
- }
-
- // Otherwise we calculate the sum of the sizes of all fields.
- int size = 0;
- var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
-
- for (int fieldIndex = 0; fieldIndex < fields.Length; fieldIndex++)
- {
- size += SizeOf(fields[fieldIndex].FieldType);
- }
-
- return size;
- }
-
- // Primitive types.
- return (Type.GetTypeCode(type)) switch
- {
- TypeCode.SByte => sizeof(sbyte),
- TypeCode.Byte => sizeof(byte),
- TypeCode.Int16 => sizeof(short),
- TypeCode.UInt16 => sizeof(ushort),
- TypeCode.Int32 => sizeof(int),
- TypeCode.UInt32 => sizeof(uint),
- TypeCode.Int64 => sizeof(long),
- TypeCode.UInt64 => sizeof(ulong),
- TypeCode.Char => sizeof(char),
- TypeCode.Single => sizeof(float),
- TypeCode.Double => sizeof(double),
- TypeCode.Decimal => sizeof(decimal),
- TypeCode.Boolean => sizeof(bool),
- _ => throw new ArgumentException($"Length for type \"{type.Name}\" is unknown."),
- };
- }
- }
-}
diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs
index e54855a8ff..effcb7bbb7 100644
--- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs
+++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/StateUpdateTracker.cs
@@ -79,7 +79,10 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
var field = fields[fieldIndex];
- int sizeOfField = SizeCalculator.SizeOf(field.FieldType);
+ var currentFieldOffset = (int)Marshal.OffsetOf(field.Name);
+ var nextFieldOffset = fieldIndex + 1 == fields.Length ? Unsafe.SizeOf() : (int)Marshal.OffsetOf(fields[fieldIndex + 1].Name);
+
+ int sizeOfField = nextFieldOffset - currentFieldOffset;
if (fieldToDelegate.TryGetValue(field.Name, out int entryIndex))
{
diff --git a/src/Ryujinx.HLE.Generators/CodeGenerator.cs b/src/Ryujinx.HLE.Generators/CodeGenerator.cs
new file mode 100644
index 0000000000..7e4848ad39
--- /dev/null
+++ b/src/Ryujinx.HLE.Generators/CodeGenerator.cs
@@ -0,0 +1,63 @@
+using System.Text;
+
+namespace Ryujinx.HLE.Generators
+{
+ class CodeGenerator
+ {
+ private const int IndentLength = 4;
+
+ private readonly StringBuilder _sb;
+ private int _currentIndentCount;
+
+ public CodeGenerator()
+ {
+ _sb = new StringBuilder();
+ }
+
+ public void EnterScope(string header = null)
+ {
+ if (header != null)
+ {
+ AppendLine(header);
+ }
+
+ AppendLine("{");
+ IncreaseIndentation();
+ }
+
+ public void LeaveScope(string suffix = "")
+ {
+ DecreaseIndentation();
+ AppendLine($"}}{suffix}");
+ }
+
+ public void IncreaseIndentation()
+ {
+ _currentIndentCount++;
+ }
+
+ public void DecreaseIndentation()
+ {
+ if (_currentIndentCount - 1 >= 0)
+ {
+ _currentIndentCount--;
+ }
+ }
+
+ public void AppendLine()
+ {
+ _sb.AppendLine();
+ }
+
+ public void AppendLine(string text)
+ {
+ _sb.Append(' ', IndentLength * _currentIndentCount);
+ _sb.AppendLine(text);
+ }
+
+ public override string ToString()
+ {
+ return _sb.ToString();
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs b/src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs
new file mode 100644
index 0000000000..19fdbe1972
--- /dev/null
+++ b/src/Ryujinx.HLE.Generators/IpcServiceGenerator.cs
@@ -0,0 +1,76 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using System.Linq;
+
+namespace Ryujinx.HLE.Generators
+{
+ [Generator]
+ public class IpcServiceGenerator : ISourceGenerator
+ {
+ public void Execute(GeneratorExecutionContext context)
+ {
+ var syntaxReceiver = (ServiceSyntaxReceiver)context.SyntaxReceiver;
+ CodeGenerator generator = new CodeGenerator();
+
+ generator.AppendLine("using System;");
+ generator.EnterScope($"namespace Ryujinx.HLE.HOS.Services.Sm");
+ generator.EnterScope($"partial class IUserInterface");
+
+ generator.EnterScope($"public IpcService? GetServiceInstance(Type type, ServiceCtx context, object? parameter = null)");
+ foreach (var className in syntaxReceiver.Types)
+ {
+ if (className.Modifiers.Any(SyntaxKind.AbstractKeyword) || className.Modifiers.Any(SyntaxKind.PrivateKeyword) || !className.AttributeLists.Any(x => x.Attributes.Any(y => y.ToString().StartsWith("Service"))))
+ continue;
+ var name = GetFullName(className, context).Replace("global::", "");
+ if (!name.StartsWith("Ryujinx.HLE.HOS.Services"))
+ continue;
+ var constructors = className.ChildNodes().Where(x => x.IsKind(SyntaxKind.ConstructorDeclaration)).Select(y => y as ConstructorDeclarationSyntax);
+
+ if (!constructors.Any(x => x.ParameterList.Parameters.Count >= 1))
+ continue;
+
+ if (constructors.Where(x => x.ParameterList.Parameters.Count >= 1).FirstOrDefault().ParameterList.Parameters[0].Type.ToString() == "ServiceCtx")
+ {
+ generator.EnterScope($"if (type == typeof({GetFullName(className, context)}))");
+ if (constructors.Any(x => x.ParameterList.Parameters.Count == 2))
+ {
+ var type = constructors.Where(x => x.ParameterList.Parameters.Count == 2).FirstOrDefault().ParameterList.Parameters[1].Type;
+ var model = context.Compilation.GetSemanticModel(type.SyntaxTree);
+ var typeSymbol = model.GetSymbolInfo(type).Symbol as INamedTypeSymbol;
+ var fullName = typeSymbol.ToString();
+ generator.EnterScope("if (parameter != null)");
+ generator.AppendLine($"return new {GetFullName(className, context)}(context, ({fullName})parameter);");
+ generator.LeaveScope();
+ }
+
+ if (constructors.Any(x => x.ParameterList.Parameters.Count == 1))
+ {
+ generator.AppendLine($"return new {GetFullName(className, context)}(context);");
+ }
+
+ generator.LeaveScope();
+ }
+ }
+
+ generator.AppendLine("return null;");
+ generator.LeaveScope();
+
+ generator.LeaveScope();
+ generator.LeaveScope();
+ context.AddSource($"IUserInterface.g.cs", generator.ToString());
+ }
+
+ private string GetFullName(ClassDeclarationSyntax syntaxNode, GeneratorExecutionContext context)
+ {
+ var typeSymbol = context.Compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetDeclaredSymbol(syntaxNode);
+
+ return typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat);
+ }
+
+ public void Initialize(GeneratorInitializationContext context)
+ {
+ context.RegisterForSyntaxNotifications(() => new ServiceSyntaxReceiver());
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE.Generators/Ryujinx.HLE.Generators.csproj b/src/Ryujinx.HLE.Generators/Ryujinx.HLE.Generators.csproj
new file mode 100644
index 0000000000..eeab9c0e97
--- /dev/null
+++ b/src/Ryujinx.HLE.Generators/Ryujinx.HLE.Generators.csproj
@@ -0,0 +1,19 @@
+
+
+
+ netstandard2.0
+ true
+ true
+ Generated
+ true
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
diff --git a/src/Ryujinx.HLE.Generators/ServiceSyntaxReceiver.cs b/src/Ryujinx.HLE.Generators/ServiceSyntaxReceiver.cs
new file mode 100644
index 0000000000..e4269cb9a4
--- /dev/null
+++ b/src/Ryujinx.HLE.Generators/ServiceSyntaxReceiver.cs
@@ -0,0 +1,24 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using System.Collections.Generic;
+
+namespace Ryujinx.HLE.Generators
+{
+ internal class ServiceSyntaxReceiver : ISyntaxReceiver
+ {
+ public HashSet Types = new HashSet();
+
+ public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
+ {
+ if (syntaxNode is ClassDeclarationSyntax classDeclaration)
+ {
+ if (classDeclaration.BaseList == null)
+ {
+ return;
+ }
+
+ Types.Add(classDeclaration);
+ }
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs b/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs
index 30300f1b63..3c34d5c789 100644
--- a/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs
+++ b/src/Ryujinx.HLE/HOS/Applets/AppletManager.cs
@@ -8,27 +8,24 @@ namespace Ryujinx.HLE.HOS.Applets
{
static class AppletManager
{
- private static readonly Dictionary _appletMapping;
-
- static AppletManager()
- {
- _appletMapping = new Dictionary
- {
- { AppletId.Error, typeof(ErrorApplet) },
- { AppletId.PlayerSelect, typeof(PlayerSelectApplet) },
- { AppletId.Controller, typeof(ControllerApplet) },
- { AppletId.SoftwareKeyboard, typeof(SoftwareKeyboardApplet) },
- { AppletId.LibAppletWeb, typeof(BrowserApplet) },
- { AppletId.LibAppletShop, typeof(BrowserApplet) },
- { AppletId.LibAppletOff, typeof(BrowserApplet) },
- };
- }
-
public static IApplet Create(AppletId applet, Horizon system)
{
- if (_appletMapping.TryGetValue(applet, out Type appletClass))
+ switch (applet)
{
- return (IApplet)Activator.CreateInstance(appletClass, system);
+ case AppletId.Controller:
+ return new ControllerApplet(system);
+ case AppletId.Error:
+ return new ErrorApplet(system);
+ case AppletId.PlayerSelect:
+ return new PlayerSelectApplet(system);
+ case AppletId.SoftwareKeyboard:
+ return new SoftwareKeyboardApplet(system);
+ case AppletId.LibAppletWeb:
+ return new BrowserApplet(system);
+ case AppletId.LibAppletShop:
+ return new BrowserApplet(system);
+ case AppletId.LibAppletOff:
+ return new BrowserApplet(system);
}
throw new NotImplementedException($"{applet} applet is not implemented.");
diff --git a/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
index 3dc82035fd..7a90c664e3 100644
--- a/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
+++ b/src/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs
@@ -2,6 +2,7 @@ using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Kernel.Ipc;
+using Ryujinx.HLE.HOS.Services.Apm;
using Ryujinx.Horizon.Common;
using System;
using System.Collections.Generic;
@@ -12,7 +13,7 @@ using System.Text;
namespace Ryujinx.HLE.HOS.Services.Sm
{
- class IUserInterface : IpcService
+ partial class IUserInterface : IpcService
{
private static readonly Dictionary _services;
@@ -95,9 +96,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
{
ServiceAttribute serviceAttribute = (ServiceAttribute)type.GetCustomAttributes(typeof(ServiceAttribute)).First(service => ((ServiceAttribute)service).Name == name);
- IpcService service = serviceAttribute.Parameter != null
- ? (IpcService)Activator.CreateInstance(type, context, serviceAttribute.Parameter)
- : (IpcService)Activator.CreateInstance(type, context);
+ IpcService service = GetServiceInstance(type, context, serviceAttribute.Parameter);
service.TrySetServer(_commonServer);
service.Server.AddSessionObj(session.ServerSession, service);
diff --git a/src/Ryujinx.HLE/Ryujinx.HLE.csproj b/src/Ryujinx.HLE/Ryujinx.HLE.csproj
index 83a11d4e02..a7bb3cd7f6 100644
--- a/src/Ryujinx.HLE/Ryujinx.HLE.csproj
+++ b/src/Ryujinx.HLE/Ryujinx.HLE.csproj
@@ -12,6 +12,7 @@
+
From 398fa1c238df75ee93f7106a578569f87cae8c0b Mon Sep 17 00:00:00 2001
From: riperiperi
Date: Sun, 1 Sep 2024 21:33:11 +0100
Subject: [PATCH 15/36] Vulkan: Update Silk.NET to 2.21 (#7266)
* Update Silk.NET version
* fix: add MoltenVK resolver workaround
fix: add MoltenVK resolver workaround
* Cleanup
* Readonly ref warnings
* Remove driver id todo
---
Directory.Packages.props | 8 ++++----
src/Ryujinx.Graphics.Vulkan/BufferHolder.cs | 6 +++---
src/Ryujinx.Graphics.Vulkan/BufferManager.cs | 2 +-
src/Ryujinx.Graphics.Vulkan/BufferState.cs | 5 ++++-
.../CommandBufferPool.cs | 8 ++++----
.../DescriptorSetCollection.cs | 14 +++++++-------
.../DescriptorSetManager.cs | 2 +-
.../FramebufferParams.cs | 2 +-
.../HostMemoryAllocator.cs | 2 +-
.../MemoryAllocatorBlockList.cs | 2 +-
.../MoltenVK/MVKInitialization.cs | 18 ++++++++++++++++++
src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 4 ++--
.../PipelineConverter.cs | 2 +-
.../PipelineLayoutFactory.cs | 2 +-
.../Queries/BufferedQuery.cs | 2 +-
.../RenderPassHolder.cs | 2 +-
src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs | 2 +-
src/Ryujinx.Graphics.Vulkan/Shader.cs | 2 +-
src/Ryujinx.Graphics.Vulkan/TextureCopy.cs | 12 ++++++------
src/Ryujinx.Graphics.Vulkan/TextureStorage.cs | 8 ++++----
src/Ryujinx.Graphics.Vulkan/TextureView.cs | 14 +++++++-------
src/Ryujinx.Graphics.Vulkan/Vendor.cs | 8 +++-----
src/Ryujinx.Graphics.Vulkan/Window.cs | 12 ++++++------
src/Ryujinx.Gtk3/Program.cs | 3 +++
src/Ryujinx.Headless.SDL2/Program.cs | 6 ++++++
src/Ryujinx/Program.cs | 6 ++++++
26 files changed, 94 insertions(+), 60 deletions(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 8a9fdc3be5..301024cf8a 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -39,9 +39,9 @@
-
-
-
+
+
+
@@ -49,4 +49,4 @@
-
+
\ No newline at end of file
diff --git a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs
index 3dcbc3130b..e840fdc02b 100644
--- a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs
+++ b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs
@@ -122,7 +122,7 @@ namespace Ryujinx.Graphics.Vulkan
Range = (uint)size,
};
- _gd.Api.CreateBufferView(_device, bufferViewCreateInfo, null, out var bufferView).ThrowOnError();
+ _gd.Api.CreateBufferView(_device, in bufferViewCreateInfo, null, out var bufferView).ThrowOnError();
return new Auto(new DisposableBufferView(_gd.Api, _device, bufferView), this, _waitable, _buffer);
}
@@ -153,7 +153,7 @@ namespace Ryujinx.Graphics.Vulkan
PipelineStageFlags.AllCommandsBit,
DependencyFlags.DeviceGroupBit,
1,
- memoryBarrier,
+ in memoryBarrier,
0,
null,
0,
@@ -770,7 +770,7 @@ namespace Ryujinx.Graphics.Vulkan
0,
null,
1,
- memoryBarrier,
+ in memoryBarrier,
0,
null);
}
diff --git a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs
index 1b6ac99880..7523913ec0 100644
--- a/src/Ryujinx.Graphics.Vulkan/BufferManager.cs
+++ b/src/Ryujinx.Graphics.Vulkan/BufferManager.cs
@@ -221,7 +221,7 @@ namespace Ryujinx.Graphics.Vulkan
PBufferBinds = &bufferBind
};
- gd.Api.QueueBindSparse(gd.Queue, 1, bindSparseInfo, default).ThrowOnError();
+ gd.Api.QueueBindSparse(gd.Queue, 1, in bindSparseInfo, default).ThrowOnError();
}
var holder = new BufferHolder(gd, _device, buffer, (int)size, storageAllocations);
diff --git a/src/Ryujinx.Graphics.Vulkan/BufferState.cs b/src/Ryujinx.Graphics.Vulkan/BufferState.cs
index d585dd53cc..e49df765d1 100644
--- a/src/Ryujinx.Graphics.Vulkan/BufferState.cs
+++ b/src/Ryujinx.Graphics.Vulkan/BufferState.cs
@@ -25,7 +25,10 @@ namespace Ryujinx.Graphics.Vulkan
{
var buffer = _buffer.Get(cbs, _offset, _size, true).Value;
- gd.TransformFeedbackApi.CmdBindTransformFeedbackBuffers(cbs.CommandBuffer, binding, 1, buffer, (ulong)_offset, (ulong)_size);
+ ulong offset = (ulong)_offset;
+ ulong size = (ulong)_size;
+
+ gd.TransformFeedbackApi.CmdBindTransformFeedbackBuffers(cbs.CommandBuffer, binding, 1, in buffer, in offset, in size);
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs b/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs
index e3938392f2..e1fd3fb9dc 100644
--- a/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs
+++ b/src/Ryujinx.Graphics.Vulkan/CommandBufferPool.cs
@@ -45,7 +45,7 @@ namespace Ryujinx.Graphics.Vulkan
Level = CommandBufferLevel.Primary,
};
- api.AllocateCommandBuffers(device, allocateInfo, out CommandBuffer);
+ api.AllocateCommandBuffers(device, in allocateInfo, out CommandBuffer);
Dependants = new List();
Waitables = new List();
@@ -83,7 +83,7 @@ namespace Ryujinx.Graphics.Vulkan
CommandPoolCreateFlags.ResetCommandBufferBit,
};
- api.CreateCommandPool(device, commandPoolCreateInfo, null, out _pool).ThrowOnError();
+ api.CreateCommandPool(device, in commandPoolCreateInfo, null, out _pool).ThrowOnError();
// We need at least 2 command buffers to get texture data in some cases.
_totalCommandBuffers = isLight ? 2 : MaxCommandBuffers;
@@ -253,7 +253,7 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.CommandBufferBeginInfo,
};
- _api.BeginCommandBuffer(entry.CommandBuffer, commandBufferBeginInfo).ThrowOnError();
+ _api.BeginCommandBuffer(entry.CommandBuffer, in commandBufferBeginInfo).ThrowOnError();
return new CommandBufferScoped(this, entry.CommandBuffer, cursor);
}
@@ -311,7 +311,7 @@ namespace Ryujinx.Graphics.Vulkan
lock (_queueLock)
{
- _api.QueueSubmit(_queue, 1, sInfo, entry.Fence.GetUnsafe()).ThrowOnError();
+ _api.QueueSubmit(_queue, 1, in sInfo, entry.Fence.GetUnsafe()).ThrowOnError();
}
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs
index 846dd5c7d5..40fc01b247 100644
--- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs
+++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetCollection.cs
@@ -43,7 +43,7 @@ namespace Ryujinx.Graphics.Vulkan
PBufferInfo = &bufferInfo,
};
- _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
+ _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
}
}
@@ -66,7 +66,7 @@ namespace Ryujinx.Graphics.Vulkan
PBufferInfo = pBufferInfo,
};
- _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
+ _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
}
}
@@ -84,7 +84,7 @@ namespace Ryujinx.Graphics.Vulkan
PImageInfo = &imageInfo,
};
- _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
+ _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
}
}
@@ -107,7 +107,7 @@ namespace Ryujinx.Graphics.Vulkan
PImageInfo = pImageInfo,
};
- _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
+ _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
}
}
@@ -144,7 +144,7 @@ namespace Ryujinx.Graphics.Vulkan
PImageInfo = pImageInfo,
};
- _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
+ _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
i += count - 1;
}
@@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.Vulkan
PTexelBufferView = &texelBufferView,
};
- _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
+ _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
}
}
@@ -200,7 +200,7 @@ namespace Ryujinx.Graphics.Vulkan
PTexelBufferView = pTexelBufferView + i,
};
- _holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
+ _holder.Api.UpdateDescriptorSets(_holder.Device, 1, in writeDescriptorSet, 0, null);
}
i += count;
diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs
index 707ae12922..97669942cb 100644
--- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs
+++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetManager.cs
@@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Vulkan
PPoolSizes = pPoolsSize,
};
- Api.CreateDescriptorPool(device, descriptorPoolCreateInfo, null, out _pool).ThrowOnError();
+ Api.CreateDescriptorPool(device, in descriptorPoolCreateInfo, null, out _pool).ThrowOnError();
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
index 5c5a8f3ad4..763d26eb54 100644
--- a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
+++ b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
@@ -250,7 +250,7 @@ namespace Ryujinx.Graphics.Vulkan
Layers = Layers,
};
- api.CreateFramebuffer(_device, framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
+ api.CreateFramebuffer(_device, in framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
return new Auto(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments);
}
diff --git a/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs b/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs
index baccc698f2..ff15652467 100644
--- a/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs
+++ b/src/Ryujinx.Graphics.Vulkan/HostMemoryAllocator.cs
@@ -115,7 +115,7 @@ namespace Ryujinx.Graphics.Vulkan
PNext = &importInfo,
};
- Result result = _api.AllocateMemory(_device, memoryAllocateInfo, null, out var deviceMemory);
+ Result result = _api.AllocateMemory(_device, in memoryAllocateInfo, null, out var deviceMemory);
if (result < Result.Success)
{
diff --git a/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs b/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs
index a1acc90f94..3d42ed7e2c 100644
--- a/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs
+++ b/src/Ryujinx.Graphics.Vulkan/MemoryAllocatorBlockList.cs
@@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Vulkan
MemoryTypeIndex = (uint)MemoryTypeIndex,
};
- _api.AllocateMemory(_device, memoryAllocateInfo, null, out var deviceMemory).ThrowOnError();
+ _api.AllocateMemory(_device, in memoryAllocateInfo, null, out var deviceMemory).ThrowOnError();
IntPtr hostPointer = IntPtr.Zero;
diff --git a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs
index 457240aa08..930d6b5259 100644
--- a/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs
+++ b/src/Ryujinx.Graphics.Vulkan/MoltenVK/MVKInitialization.cs
@@ -1,3 +1,4 @@
+using Silk.NET.Core.Loader;
using Silk.NET.Vulkan;
using System;
using System.Runtime.InteropServices;
@@ -8,6 +9,8 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK
[SupportedOSPlatform("macos")]
public static partial class MVKInitialization
{
+ private const string VulkanLib = "libvulkan.dylib";
+
[LibraryImport("libMoltenVK.dylib")]
private static partial Result vkGetMoltenVKConfigurationMVK(IntPtr unusedInstance, out MVKConfiguration config, in IntPtr configSize);
@@ -29,5 +32,20 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK
vkSetMoltenVKConfigurationMVK(IntPtr.Zero, config, configSize);
}
+
+ private static string[] Resolver(string path)
+ {
+ if (path.EndsWith(VulkanLib))
+ {
+ path = path[..^VulkanLib.Length] + "libMoltenVK.dylib";
+ return [path];
+ }
+ return Array.Empty();
+ }
+
+ public static void InitializeResolver()
+ {
+ ((DefaultPathResolver)PathResolver.Default).Resolvers.Insert(0, Resolver);
+ }
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
index bda6167d7b..57fa592640 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
@@ -102,7 +102,7 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PipelineCacheCreateInfo,
};
- gd.Api.CreatePipelineCache(device, pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError();
+ gd.Api.CreatePipelineCache(device, in pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError();
_descriptorSetUpdater = new DescriptorSetUpdater(gd, device);
_vertexBufferUpdater = new VertexBufferUpdater(gd);
@@ -1628,7 +1628,7 @@ namespace Ryujinx.Graphics.Vulkan
ClearValueCount = 1,
};
- Gd.Api.CmdBeginRenderPass(CommandBuffer, renderPassBeginInfo, SubpassContents.Inline);
+ Gd.Api.CmdBeginRenderPass(CommandBuffer, in renderPassBeginInfo, SubpassContents.Inline);
RenderPassActive = true;
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs
index 89ce10b0aa..85069c6b27 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineConverter.cs
@@ -116,7 +116,7 @@ namespace Ryujinx.Graphics.Vulkan
DependencyCount = 1,
};
- gd.Api.CreateRenderPass(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError();
+ gd.Api.CreateRenderPass(device, in renderPassCreateInfo, null, out var renderPass).ThrowOnError();
return new DisposableRenderPass(gd.Api, device, renderPass);
}
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs
index bca119f6ad..8d78156161 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs
@@ -91,7 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
Flags = flags,
};
- gd.Api.CreateDescriptorSetLayout(device, descriptorSetLayoutCreateInfo, null, out layouts[setIndex]).ThrowOnError();
+ gd.Api.CreateDescriptorSetLayout(device, in descriptorSetLayoutCreateInfo, null, out layouts[setIndex]).ThrowOnError();
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs b/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs
index 714cb2833c..c9a546648f 100644
--- a/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs
+++ b/src/Ryujinx.Graphics.Vulkan/Queries/BufferedQuery.cs
@@ -52,7 +52,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
PipelineStatistics = flags,
};
- gd.Api.CreateQueryPool(device, queryPoolCreateInfo, null, out _queryPool).ThrowOnError();
+ gd.Api.CreateQueryPool(device, in queryPoolCreateInfo, null, out _queryPool).ThrowOnError();
}
var buffer = gd.BufferManager.Create(gd, sizeof(long), forConditionalRendering: true);
diff --git a/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs b/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs
index b2dd0dd874..a364c57163 100644
--- a/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs
+++ b/src/Ryujinx.Graphics.Vulkan/RenderPassHolder.cs
@@ -125,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan
DependencyCount = 1,
};
- gd.Api.CreateRenderPass(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError();
+ gd.Api.CreateRenderPass(device, in renderPassCreateInfo, null, out var renderPass).ThrowOnError();
_renderPass = new Auto(new DisposableRenderPass(gd.Api, device, renderPass));
}
diff --git a/src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs b/src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs
index f67daeeccb..7f37ab1398 100644
--- a/src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs
+++ b/src/Ryujinx.Graphics.Vulkan/SamplerHolder.cs
@@ -68,7 +68,7 @@ namespace Ryujinx.Graphics.Vulkan
samplerCreateInfo.BorderColor = BorderColor.FloatCustomExt;
}
- gd.Api.CreateSampler(device, samplerCreateInfo, null, out var sampler).ThrowOnError();
+ gd.Api.CreateSampler(device, in samplerCreateInfo, null, out var sampler).ThrowOnError();
_sampler = new Auto(new DisposableSampler(gd.Api, device, sampler));
}
diff --git a/src/Ryujinx.Graphics.Vulkan/Shader.cs b/src/Ryujinx.Graphics.Vulkan/Shader.cs
index 06f3499db9..1c8caffd9a 100644
--- a/src/Ryujinx.Graphics.Vulkan/Shader.cs
+++ b/src/Ryujinx.Graphics.Vulkan/Shader.cs
@@ -64,7 +64,7 @@ namespace Ryujinx.Graphics.Vulkan
PCode = (uint*)pCode,
};
- api.CreateShaderModule(device, shaderModuleCreateInfo, null, out _module).ThrowOnError();
+ api.CreateShaderModule(device, in shaderModuleCreateInfo, null, out _module).ThrowOnError();
}
CompileStatus = ProgramLinkStatus.Success;
diff --git a/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs b/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs
index fdc0a248bd..45cddd772f 100644
--- a/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs
+++ b/src/Ryujinx.Graphics.Vulkan/TextureCopy.cs
@@ -88,7 +88,7 @@ namespace Ryujinx.Graphics.Vulkan
DstOffsets = dstOffsets,
};
- api.CmdBlitImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, region, filter);
+ api.CmdBlitImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, in region, filter);
copySrcLevel++;
copyDstLevel++;
@@ -320,13 +320,13 @@ namespace Ryujinx.Graphics.Vulkan
{
var region = new ImageResolve(srcSl, new Offset3D(0, 0, srcZ), dstSl, new Offset3D(0, 0, dstZ), extent);
- api.CmdResolveImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, region);
+ api.CmdResolveImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, in region);
}
else
{
var region = new ImageCopy(srcSl, new Offset3D(0, 0, srcZ), dstSl, new Offset3D(0, 0, dstZ), extent);
- api.CmdCopyImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, region);
+ api.CmdCopyImage(commandBuffer, srcImage, ImageLayout.General, dstImage, ImageLayout.General, 1, in region);
}
width = Math.Max(1, width >> 1);
@@ -422,7 +422,7 @@ namespace Ryujinx.Graphics.Vulkan
DependencyCount = 1,
};
- gd.Api.CreateRenderPass2(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError();
+ gd.Api.CreateRenderPass2(device, in renderPassCreateInfo, null, out var renderPass).ThrowOnError();
using var rp = new Auto(new DisposableRenderPass(gd.Api, device, renderPass));
@@ -445,7 +445,7 @@ namespace Ryujinx.Graphics.Vulkan
Layers = (uint)src.Layers,
};
- gd.Api.CreateFramebuffer(device, framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
+ gd.Api.CreateFramebuffer(device, in framebufferCreateInfo, null, out var framebuffer).ThrowOnError();
using var fb = new Auto(new DisposableFramebuffer(gd.Api, device, framebuffer), null, srcView, dstView);
var renderArea = new Rect2D(null, new Extent2D((uint)src.Info.Width, (uint)src.Info.Height));
@@ -465,7 +465,7 @@ namespace Ryujinx.Graphics.Vulkan
// to resolve the depth-stencil texture.
// TODO: Do speculative resolve and part of the same render pass as the draw to avoid
// ending the current render pass?
- gd.Api.CmdBeginRenderPass(cbs.CommandBuffer, renderPassBeginInfo, SubpassContents.Inline);
+ gd.Api.CmdBeginRenderPass(cbs.CommandBuffer, in renderPassBeginInfo, SubpassContents.Inline);
gd.Api.CmdEndRenderPass(cbs.CommandBuffer);
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs
index f36db68de3..f78b9ed476 100644
--- a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs
+++ b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs
@@ -114,7 +114,7 @@ namespace Ryujinx.Graphics.Vulkan
Flags = flags,
};
- gd.Api.CreateImage(device, imageCreateInfo, null, out _image).ThrowOnError();
+ gd.Api.CreateImage(device, in imageCreateInfo, null, out _image).ThrowOnError();
if (foreignAllocation == null)
{
@@ -284,7 +284,7 @@ namespace Ryujinx.Graphics.Vulkan
0,
null,
1,
- barrier);
+ in barrier);
if (useTempCbs)
{
@@ -401,11 +401,11 @@ namespace Ryujinx.Graphics.Vulkan
if (to)
{
- _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, region);
+ _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, in region);
}
else
{
- _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, region);
+ _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, in region);
}
offset += mipSize;
diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs
index d4f26a2dd4..c5453c0c7e 100644
--- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs
+++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs
@@ -117,7 +117,7 @@ namespace Ryujinx.Graphics.Vulkan
PNext = &imageViewUsage,
};
- gd.Api.CreateImageView(device, imageCreateInfo, null, out var imageView).ThrowOnError();
+ gd.Api.CreateImageView(device, in imageCreateInfo, null, out var imageView).ThrowOnError();
return new Auto(new DisposableImageView(gd.Api, device, imageView), null, storage.GetImage());
}
@@ -492,7 +492,7 @@ namespace Ryujinx.Graphics.Vulkan
dstStageMask,
DependencyFlags.None,
1,
- memoryBarrier,
+ in memoryBarrier,
0,
null,
0,
@@ -557,7 +557,7 @@ namespace Ryujinx.Graphics.Vulkan
0,
null,
1,
- memoryBarrier);
+ in memoryBarrier);
}
public TextureView GetView(Format format)
@@ -949,11 +949,11 @@ namespace Ryujinx.Graphics.Vulkan
if (to)
{
- _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, region);
+ _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, in region);
}
else
{
- _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, region);
+ _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, in region);
}
offset += mipSize;
@@ -1010,11 +1010,11 @@ namespace Ryujinx.Graphics.Vulkan
if (to)
{
- _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, region);
+ _gd.Api.CmdCopyImageToBuffer(commandBuffer, image, ImageLayout.General, buffer, 1, in region);
}
else
{
- _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, region);
+ _gd.Api.CmdCopyBufferToImage(commandBuffer, buffer, image, ImageLayout.General, 1, in region);
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/Vendor.cs b/src/Ryujinx.Graphics.Vulkan/Vendor.cs
index 802771ede5..55ae0cd819 100644
--- a/src/Ryujinx.Graphics.Vulkan/Vendor.cs
+++ b/src/Ryujinx.Graphics.Vulkan/Vendor.cs
@@ -90,11 +90,9 @@ namespace Ryujinx.Graphics.Vulkan
DriverId.SamsungProprietary => "Samsung",
DriverId.MesaVenus => "Venus",
DriverId.MesaDozen => "Dozen",
-
- // TODO: Use real enum when we have an up to date Silk.NET.
- (DriverId)24 => "NVK",
- (DriverId)25 => "Imagination (Open)",
- (DriverId)26 => "Honeykrisp",
+ DriverId.MesaNvk => "NVK",
+ DriverId.ImaginationOpenSourceMesa => "Imagination (Open)",
+ DriverId.MesaAgxv => "Honeykrisp",
_ => id.ToString(),
};
}
diff --git a/src/Ryujinx.Graphics.Vulkan/Window.cs b/src/Ryujinx.Graphics.Vulkan/Window.cs
index efb0b31f97..d67362be30 100644
--- a/src/Ryujinx.Graphics.Vulkan/Window.cs
+++ b/src/Ryujinx.Graphics.Vulkan/Window.cs
@@ -160,7 +160,7 @@ namespace Ryujinx.Graphics.Vulkan
SwizzleComponent.Blue,
SwizzleComponent.Alpha);
- _gd.SwapchainApi.CreateSwapchain(_device, swapchainCreateInfo, null, out _swapchain).ThrowOnError();
+ _gd.SwapchainApi.CreateSwapchain(_device, in swapchainCreateInfo, null, out _swapchain).ThrowOnError();
_gd.SwapchainApi.GetSwapchainImages(_device, _swapchain, &imageCount, null);
@@ -187,14 +187,14 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < _imageAvailableSemaphores.Length; i++)
{
- _gd.Api.CreateSemaphore(_device, semaphoreCreateInfo, null, out _imageAvailableSemaphores[i]).ThrowOnError();
+ _gd.Api.CreateSemaphore(_device, in semaphoreCreateInfo, null, out _imageAvailableSemaphores[i]).ThrowOnError();
}
_renderFinishedSemaphores = new Semaphore[imageCount];
for (int i = 0; i < _renderFinishedSemaphores.Length; i++)
{
- _gd.Api.CreateSemaphore(_device, semaphoreCreateInfo, null, out _renderFinishedSemaphores[i]).ThrowOnError();
+ _gd.Api.CreateSemaphore(_device, in semaphoreCreateInfo, null, out _renderFinishedSemaphores[i]).ThrowOnError();
}
}
@@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Vulkan
SubresourceRange = subresourceRange,
};
- _gd.Api.CreateImageView(_device, imageCreateInfo, null, out var imageView).ThrowOnError();
+ _gd.Api.CreateImageView(_device, in imageCreateInfo, null, out var imageView).ThrowOnError();
return new TextureView(_gd, _device, new DisposableImageView(_gd.Api, _device, imageView), info, format);
}
@@ -479,7 +479,7 @@ namespace Ryujinx.Graphics.Vulkan
lock (_gd.QueueLock)
{
- _gd.SwapchainApi.QueuePresent(_gd.Queue, presentInfo);
+ _gd.SwapchainApi.QueuePresent(_gd.Queue, in presentInfo);
}
}
@@ -611,7 +611,7 @@ namespace Ryujinx.Graphics.Vulkan
0,
null,
1,
- barrier);
+ in barrier);
}
private void CaptureFrame(TextureView texture, int x, int y, int width, int height, bool isBgra, bool flipX, bool flipY)
diff --git a/src/Ryujinx.Gtk3/Program.cs b/src/Ryujinx.Gtk3/Program.cs
index 745335ac95..8bad1a0c7b 100644
--- a/src/Ryujinx.Gtk3/Program.cs
+++ b/src/Ryujinx.Gtk3/Program.cs
@@ -4,6 +4,7 @@ using Ryujinx.Common.Configuration;
using Ryujinx.Common.GraphicsDriver;
using Ryujinx.Common.Logging;
using Ryujinx.Common.SystemInterop;
+using Ryujinx.Graphics.Vulkan.MoltenVK;
using Ryujinx.Modules;
using Ryujinx.SDL2.Common;
using Ryujinx.UI;
@@ -110,6 +111,8 @@ namespace Ryujinx
if (OperatingSystem.IsMacOS())
{
+ MVKInitialization.InitializeResolver();
+
string baseDirectory = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory);
string resourcesDataDir;
diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs
index 85aff67129..5c30cd18fb 100644
--- a/src/Ryujinx.Headless.SDL2/Program.cs
+++ b/src/Ryujinx.Headless.SDL2/Program.cs
@@ -18,6 +18,7 @@ using Ryujinx.Graphics.Gpu;
using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Graphics.OpenGL;
using Ryujinx.Graphics.Vulkan;
+using Ryujinx.Graphics.Vulkan.MoltenVK;
using Ryujinx.Headless.SDL2.OpenGL;
using Ryujinx.Headless.SDL2.Vulkan;
using Ryujinx.HLE;
@@ -88,6 +89,11 @@ namespace Ryujinx.Headless.SDL2
};
}
+ if (OperatingSystem.IsMacOS())
+ {
+ MVKInitialization.InitializeResolver();
+ }
+
Parser.Default.ParseArguments(args)
.WithParsed(Load)
.WithNotParsed(errors => errors.Output());
diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs
index 976963422d..af9db7d636 100644
--- a/src/Ryujinx/Program.cs
+++ b/src/Ryujinx/Program.cs
@@ -7,6 +7,7 @@ using Ryujinx.Common.Configuration;
using Ryujinx.Common.GraphicsDriver;
using Ryujinx.Common.Logging;
using Ryujinx.Common.SystemInterop;
+using Ryujinx.Graphics.Vulkan.MoltenVK;
using Ryujinx.Modules;
using Ryujinx.SDL2.Common;
using Ryujinx.UI.Common;
@@ -80,6 +81,11 @@ namespace Ryujinx.Ava
// Parse arguments
CommandLineState.ParseArguments(args);
+ if (OperatingSystem.IsMacOS())
+ {
+ MVKInitialization.InitializeResolver();
+ }
+
// Delete backup files after updating.
Task.Run(Updater.CleanupUpdate);
From fdd7ee791cd37546390856f38eab16ea78451742 Mon Sep 17 00:00:00 2001
From: gdkchan
Date: Sun, 1 Sep 2024 17:58:01 -0300
Subject: [PATCH 16/36] Fix incorrect depth texture 3D flag (#7262)
---
.../Engine/Threed/ThreedClassState.cs | 8 +++++++-
src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs | 8 +++-----
2 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
index dd55e7d1d6..35051c6e03 100644
--- a/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
+++ b/src/Ryujinx.Graphics.Gpu/Engine/Threed/ThreedClassState.cs
@@ -415,7 +415,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
#pragma warning disable CS0649 // Field is never assigned to
public int Width;
public int Height;
- public int Depth;
+ public ushort Depth;
+ public ushort Flags;
+
+ public readonly bool UnpackIsLayered()
+ {
+ return (Flags & 1) == 0;
+ }
#pragma warning restore CS0649
}
diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
index b9ff060e25..b6fa842e35 100644
--- a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
+++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs
@@ -468,13 +468,11 @@ namespace Ryujinx.Graphics.Gpu.Image
int gobBlocksInY = dsState.MemoryLayout.UnpackGobBlocksInY();
int gobBlocksInZ = dsState.MemoryLayout.UnpackGobBlocksInZ();
+ layered &= size.UnpackIsLayered();
+
Target target;
- if (dsState.MemoryLayout.UnpackIsTarget3D())
- {
- target = Target.Texture3D;
- }
- else if ((samplesInX | samplesInY) != 1)
+ if ((samplesInX | samplesInY) != 1)
{
target = size.Depth > 1 && layered
? Target.Texture2DMultisampleArray
From ca59c3f4998e2d1beb3b0d0214611e3332238557 Mon Sep 17 00:00:00 2001
From: riperiperi
Date: Mon, 2 Sep 2024 01:28:16 +0100
Subject: [PATCH 17/36] Vulkan: Feedback loop detection and barriers (#7226)
* Vulkan: Feedback loop improvements
This PR allows the Vulkan backend to detect attachment feedback loops. These are currently used in the following ways:
- Partial use of VK_EXT_attachment_feedback_loop_layout
- All renderable textures have AttachmentFeedbackLoopBitExt
- Compile pipelines with Color/DepthStencil feedback loop flags when present
- Support using FragmentBarrier for feedback loops (fixes regressions from https://github.com/Ryujinx/Ryujinx/pull/7012 )
TODO:
- AMD GPUs may need layout transitions for it to properly allow textures to be used in feedback loops.
- Use dynamic state for feedback loops. The background pipeline will always miss since feedback loop state isn't known on the GPU project.
- How is the barrier dependency flag used? (DXVK just ignores it, there's no vulkan validation...)
- Improve subpass dependencies to fix validation errors
* Mark field readonly
* Add feedback loop dynamic state
* fix: add MoltenVK resolver workaround
fix: add MoltenVK resolver workaround
* Formatting
* Fix more complaints
* RADV dcc workaround
* Use dynamic state properly, cleanup.
* Use aspects flags in more places
---
.../GraphicsDriver/DriverUtilities.cs | 24 +++-
src/Ryujinx.Common/Utilities/OsUtils.cs | 24 ++++
src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs | 39 ++++--
.../DescriptorSetUpdater.cs | 64 ++++++---
.../FeedbackLoopAspects.cs | 12 ++
.../FramebufferParams.cs | 21 +++
.../HardwareCapabilities.cs | 6 +
src/Ryujinx.Graphics.Vulkan/PipelineBase.cs | 122 ++++++++++++++++--
.../PipelineDynamicState.cs | 34 ++++-
src/Ryujinx.Graphics.Vulkan/PipelineFull.cs | 4 +-
src/Ryujinx.Graphics.Vulkan/PipelineState.cs | 38 +++++-
src/Ryujinx.Graphics.Vulkan/TextureStorage.cs | 74 ++++++++++-
src/Ryujinx.Graphics.Vulkan/TextureView.cs | 32 ++++-
.../VulkanInitialization.cs | 54 ++++++++
src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs | 34 +++++
src/Ryujinx.Gtk3/Program.cs | 23 +---
src/Ryujinx.Headless.SDL2/Program.cs | 3 +
src/Ryujinx/Program.cs | 4 +-
18 files changed, 538 insertions(+), 74 deletions(-)
create mode 100644 src/Ryujinx.Common/Utilities/OsUtils.cs
create mode 100644 src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs
diff --git a/src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs b/src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs
index 7fe2a4f024..a9163f3485 100644
--- a/src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs
+++ b/src/Ryujinx.Common/GraphicsDriver/DriverUtilities.cs
@@ -1,13 +1,33 @@
+using Ryujinx.Common.Utilities;
using System;
namespace Ryujinx.Common.GraphicsDriver
{
public static class DriverUtilities
{
+ private static void AddMesaFlags(string envVar, string newFlags)
+ {
+ string existingFlags = Environment.GetEnvironmentVariable(envVar);
+
+ string flags = existingFlags == null ? newFlags : $"{existingFlags},{newFlags}";
+
+ OsUtils.SetEnvironmentVariableNoCaching(envVar, flags);
+ }
+
+ public static void InitDriverConfig(bool oglThreading)
+ {
+ if (OperatingSystem.IsLinux())
+ {
+ AddMesaFlags("RADV_DEBUG", "nodcc");
+ }
+
+ ToggleOGLThreading(oglThreading);
+ }
+
public static void ToggleOGLThreading(bool enabled)
{
- Environment.SetEnvironmentVariable("mesa_glthread", enabled.ToString().ToLower());
- Environment.SetEnvironmentVariable("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0");
+ OsUtils.SetEnvironmentVariableNoCaching("mesa_glthread", enabled.ToString().ToLower());
+ OsUtils.SetEnvironmentVariableNoCaching("__GL_THREADED_OPTIMIZATIONS", enabled ? "1" : "0");
try
{
diff --git a/src/Ryujinx.Common/Utilities/OsUtils.cs b/src/Ryujinx.Common/Utilities/OsUtils.cs
new file mode 100644
index 0000000000..a0791b0924
--- /dev/null
+++ b/src/Ryujinx.Common/Utilities/OsUtils.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+
+namespace Ryujinx.Common.Utilities
+{
+ public partial class OsUtils
+ {
+ [LibraryImport("libc", SetLastError = true)]
+ private static partial int setenv([MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value, int overwrite);
+
+ public static void SetEnvironmentVariableNoCaching(string key, string value)
+ {
+ // Set the value in the cached environment variables, too.
+ Environment.SetEnvironmentVariable(key, value);
+
+ if (!OperatingSystem.IsWindows())
+ {
+ int res = setenv(key, value, 1);
+ Debug.Assert(res != -1);
+ }
+ }
+ }
+}
diff --git a/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs b/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs
index a6a006bb9e..bcfb3dbfe5 100644
--- a/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs
+++ b/src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs
@@ -32,10 +32,12 @@ namespace Ryujinx.Graphics.Vulkan
CommandBuffer
}
+ private bool _feedbackLoopActive;
private PipelineStageFlags _incoherentBufferWriteStages;
private PipelineStageFlags _incoherentTextureWriteStages;
private PipelineStageFlags _extraStages;
private IncoherentBarrierType _queuedIncoherentBarrier;
+ private bool _queuedFeedbackLoopBarrier;
public BarrierBatch(VulkanRenderer gd)
{
@@ -53,17 +55,6 @@ namespace Ryujinx.Graphics.Vulkan
stages |= PipelineStageFlags.TransformFeedbackBitExt;
}
- if (!gd.IsTBDR)
- {
- // Desktop GPUs can transform image barriers into memory barriers.
-
- access |= AccessFlags.DepthStencilAttachmentWriteBit | AccessFlags.ColorAttachmentWriteBit;
- access |= AccessFlags.DepthStencilAttachmentReadBit | AccessFlags.ColorAttachmentReadBit;
-
- stages |= PipelineStageFlags.EarlyFragmentTestsBit | PipelineStageFlags.LateFragmentTestsBit;
- stages |= PipelineStageFlags.ColorAttachmentOutputBit;
- }
-
return (access, stages);
}
@@ -178,16 +169,34 @@ namespace Ryujinx.Graphics.Vulkan
}
_queuedIncoherentBarrier = IncoherentBarrierType.None;
+ _queuedFeedbackLoopBarrier = false;
}
+ else if (_feedbackLoopActive && _queuedFeedbackLoopBarrier)
+ {
+ // Feedback loop barrier.
+
+ MemoryBarrier barrier = new MemoryBarrier()
+ {
+ SType = StructureType.MemoryBarrier,
+ SrcAccessMask = AccessFlags.ShaderWriteBit,
+ DstAccessMask = AccessFlags.ShaderReadBit
+ };
+
+ QueueBarrier(barrier, PipelineStageFlags.FragmentShaderBit, PipelineStageFlags.AllGraphicsBit);
+
+ _queuedFeedbackLoopBarrier = false;
+ }
+
+ _feedbackLoopActive = false;
}
}
public unsafe void Flush(CommandBufferScoped cbs, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
{
- Flush(cbs, null, inRenderPass, rpHolder, endRenderPass);
+ Flush(cbs, null, false, inRenderPass, rpHolder, endRenderPass);
}
- public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
+ public unsafe void Flush(CommandBufferScoped cbs, ShaderCollection program, bool feedbackLoopActive, bool inRenderPass, RenderPassHolder rpHolder, Action endRenderPass)
{
if (program != null)
{
@@ -195,6 +204,8 @@ namespace Ryujinx.Graphics.Vulkan
_incoherentTextureWriteStages |= program.IncoherentTextureWriteStages;
}
+ _feedbackLoopActive |= feedbackLoopActive;
+
FlushMemoryBarrier(program, inRenderPass);
if (!inRenderPass && rpHolder != null)
@@ -406,6 +417,8 @@ namespace Ryujinx.Graphics.Vulkan
{
_queuedIncoherentBarrier = type;
}
+
+ _queuedFeedbackLoopBarrier = true;
}
public void QueueTextureBarrier()
diff --git a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
index 563fdafd3c..298526d51e 100644
--- a/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
+++ b/src/Ryujinx.Graphics.Vulkan/DescriptorSetUpdater.cs
@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader;
using Silk.NET.Vulkan;
using System;
using System.Buffers;
+using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using CompareOp = Ryujinx.Graphics.GAL.CompareOp;
@@ -42,15 +43,15 @@ namespace Ryujinx.Graphics.Vulkan
private record struct TextureRef
{
public ShaderStage Stage;
- public TextureStorage Storage;
- public Auto View;
+ public TextureView View;
+ public Auto ImageView;
public Auto Sampler;
- public TextureRef(ShaderStage stage, TextureStorage storage, Auto view, Auto sampler)
+ public TextureRef(ShaderStage stage, TextureView view, Auto imageView, Auto sampler)
{
Stage = stage;
- Storage = storage;
View = view;
+ ImageView = imageView;
Sampler = sampler;
}
}
@@ -58,14 +59,14 @@ namespace Ryujinx.Graphics.Vulkan
private record struct ImageRef
{
public ShaderStage Stage;
- public TextureStorage Storage;
- public Auto View;
+ public TextureView View;
+ public Auto ImageView;
- public ImageRef(ShaderStage stage, TextureStorage storage, Auto view)
+ public ImageRef(ShaderStage stage, TextureView view, Auto imageView)
{
Stage = stage;
- Storage = storage;
View = view;
+ ImageView = imageView;
}
}
@@ -124,6 +125,8 @@ namespace Ryujinx.Graphics.Vulkan
private readonly TextureView _dummyTexture;
private readonly SamplerHolder _dummySampler;
+ public List FeedbackLoopHazards { get; private set; }
+
public DescriptorSetUpdater(VulkanRenderer gd, Device device)
{
_gd = gd;
@@ -209,10 +212,15 @@ namespace Ryujinx.Graphics.Vulkan
_templateUpdater = new();
}
- public void Initialize()
+ public void Initialize(bool isMainPipeline)
{
MemoryOwner dummyTextureData = MemoryOwner.RentCleared(4);
_dummyTexture.SetData(dummyTextureData);
+
+ if (isMainPipeline)
+ {
+ FeedbackLoopHazards = new();
+ }
}
private static bool BindingOverlaps(ref DescriptorBufferInfo info, int bindingOffset, int offset, int size)
@@ -275,6 +283,18 @@ namespace Ryujinx.Graphics.Vulkan
public void InsertBindingBarriers(CommandBufferScoped cbs)
{
+ if ((FeedbackLoopHazards?.Count ?? 0) > 0)
+ {
+ // Clear existing hazards - they will be rebuilt.
+
+ foreach (TextureView hazard in FeedbackLoopHazards)
+ {
+ hazard.DecrementHazardUses();
+ }
+
+ FeedbackLoopHazards.Clear();
+ }
+
foreach (ResourceBindingSegment segment in _program.BindingSegments[PipelineBase.TextureSetIndex])
{
if (segment.Type == ResourceType.TextureAndSampler)
@@ -284,7 +304,7 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < segment.Count; i++)
{
ref var texture = ref _textureRefs[segment.Binding + i];
- texture.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, texture.Stage.ConvertToPipelineStageFlags());
+ texture.View?.PrepareForUsage(cbs, texture.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
}
}
else
@@ -305,7 +325,7 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < segment.Count; i++)
{
ref var image = ref _imageRefs[segment.Binding + i];
- image.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, image.Stage.ConvertToPipelineStageFlags());
+ image.View?.PrepareForUsage(cbs, image.Stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
}
}
else
@@ -385,9 +405,12 @@ namespace Ryujinx.Graphics.Vulkan
}
else if (image is TextureView view)
{
- view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
+ ref ImageRef iRef = ref _imageRefs[binding];
- _imageRefs[binding] = new(stage, view.Storage, view.GetView(imageFormat).GetIdentityImageView());
+ iRef.View?.ClearUsage(FeedbackLoopHazards);
+ view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
+
+ iRef = new(stage, view, view.GetView(imageFormat).GetIdentityImageView());
}
else
{
@@ -486,9 +509,12 @@ namespace Ryujinx.Graphics.Vulkan
}
else if (texture is TextureView view)
{
- view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
+ ref TextureRef iRef = ref _textureRefs[binding];
- _textureRefs[binding] = new(stage, view.Storage, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler());
+ iRef.View?.ClearUsage(FeedbackLoopHazards);
+ view?.PrepareForUsage(cbs, stage.ConvertToPipelineStageFlags(), FeedbackLoopHazards);
+
+ iRef = new(stage, view, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler());
}
else
{
@@ -510,7 +536,7 @@ namespace Ryujinx.Graphics.Vulkan
{
view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
- _textureRefs[binding] = new(stage, view.Storage, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler());
+ _textureRefs[binding] = new(stage, view, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler());
SignalDirty(DirtyFlags.Texture);
}
@@ -836,7 +862,7 @@ namespace Ryujinx.Graphics.Vulkan
ref var texture = ref textures[i];
ref var refs = ref _textureRefs[binding + i];
- texture.ImageView = refs.View?.Get(cbs).Value ?? default;
+ texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default;
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
if (texture.ImageView.Handle == 0)
@@ -886,7 +912,7 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < count; i++)
{
- images[i].ImageView = _imageRefs[binding + i].View?.Get(cbs).Value ?? default;
+ images[i].ImageView = _imageRefs[binding + i].ImageView?.Get(cbs).Value ?? default;
}
tu.Push(images[..count]);
@@ -957,7 +983,7 @@ namespace Ryujinx.Graphics.Vulkan
ref var texture = ref textures[i];
ref var refs = ref _textureRefs[binding + i];
- texture.ImageView = refs.View?.Get(cbs).Value ?? default;
+ texture.ImageView = refs.ImageView?.Get(cbs).Value ?? default;
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
if (texture.ImageView.Handle == 0)
diff --git a/src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs b/src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs
new file mode 100644
index 0000000000..22f73679d8
--- /dev/null
+++ b/src/Ryujinx.Graphics.Vulkan/FeedbackLoopAspects.cs
@@ -0,0 +1,12 @@
+using System;
+
+namespace Ryujinx.Graphics.Vulkan
+{
+ [Flags]
+ internal enum FeedbackLoopAspects
+ {
+ None = 0,
+ Color = 1 << 0,
+ Depth = 1 << 1,
+ }
+}
diff --git a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
index 763d26eb54..8d80e9d05e 100644
--- a/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
+++ b/src/Ryujinx.Graphics.Vulkan/FramebufferParams.cs
@@ -302,6 +302,27 @@ namespace Ryujinx.Graphics.Vulkan
_depthStencil?.Storage?.AddStoreOpUsage(true);
}
+ public void ClearBindings()
+ {
+ _depthStencil?.Storage.ClearBindings();
+
+ for (int i = 0; i < _colorsCanonical.Length; i++)
+ {
+ _colorsCanonical[i]?.Storage.ClearBindings();
+ }
+ }
+
+ public void AddBindings()
+ {
+ _depthStencil?.Storage.AddBinding(_depthStencil);
+
+ for (int i = 0; i < _colorsCanonical.Length; i++)
+ {
+ TextureView color = _colorsCanonical[i];
+ color?.Storage.AddBinding(color);
+ }
+ }
+
public (RenderPassHolder rpHolder, Auto framebuffer) GetPassAndFramebuffer(
VulkanRenderer gd,
Device device,
diff --git a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs
index b6694bcb36..bd17867b10 100644
--- a/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs
+++ b/src/Ryujinx.Graphics.Vulkan/HardwareCapabilities.cs
@@ -46,6 +46,8 @@ namespace Ryujinx.Graphics.Vulkan
public readonly bool SupportsViewportArray2;
public readonly bool SupportsHostImportedMemory;
public readonly bool SupportsDepthClipControl;
+ public readonly bool SupportsAttachmentFeedbackLoop;
+ public readonly bool SupportsDynamicAttachmentFeedbackLoop;
public readonly uint SubgroupSize;
public readonly SampleCountFlags SupportedSampleCounts;
public readonly PortabilitySubsetFlags PortabilitySubset;
@@ -84,6 +86,8 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsViewportArray2,
bool supportsHostImportedMemory,
bool supportsDepthClipControl,
+ bool supportsAttachmentFeedbackLoop,
+ bool supportsDynamicAttachmentFeedbackLoop,
uint subgroupSize,
SampleCountFlags supportedSampleCounts,
PortabilitySubsetFlags portabilitySubset,
@@ -121,6 +125,8 @@ namespace Ryujinx.Graphics.Vulkan
SupportsViewportArray2 = supportsViewportArray2;
SupportsHostImportedMemory = supportsHostImportedMemory;
SupportsDepthClipControl = supportsDepthClipControl;
+ SupportsAttachmentFeedbackLoop = supportsAttachmentFeedbackLoop;
+ SupportsDynamicAttachmentFeedbackLoop = supportsDynamicAttachmentFeedbackLoop;
SubgroupSize = subgroupSize;
SupportedSampleCounts = supportedSampleCounts;
PortabilitySubset = portabilitySubset;
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
index 57fa592640..20c4b25726 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineBase.cs
@@ -2,6 +2,7 @@ using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
using Silk.NET.Vulkan;
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
@@ -33,6 +34,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly Action EndRenderPassDelegate;
protected PipelineDynamicState DynamicState;
+ protected bool IsMainPipeline;
private PipelineState _newState;
private bool _graphicsStateDirty;
private bool _computeStateDirty;
@@ -85,6 +87,9 @@ namespace Ryujinx.Graphics.Vulkan
private bool _tfEnabled;
private bool _tfActive;
+ private FeedbackLoopAspects _feedbackLoop;
+ private bool _passWritesDepthStencil;
+
private readonly PipelineColorBlendAttachmentState[] _storedBlend;
public ulong DrawCount { get; private set; }
public bool RenderPassActive { get; private set; }
@@ -126,7 +131,7 @@ namespace Ryujinx.Graphics.Vulkan
public void Initialize()
{
- _descriptorSetUpdater.Initialize();
+ _descriptorSetUpdater.Initialize(IsMainPipeline);
QuadsToTrisPattern = new IndexBufferPattern(Gd, 4, 6, 0, new[] { 0, 1, 2, 0, 2, 3 }, 4, false);
TriFanToTrisPattern = new IndexBufferPattern(Gd, 3, 3, 2, new[] { int.MinValue, -1, 0 }, 1, true);
@@ -814,6 +819,8 @@ namespace Ryujinx.Graphics.Vulkan
_newState.DepthTestEnable = depthTest.TestEnable;
_newState.DepthWriteEnable = depthTest.WriteEnable;
_newState.DepthCompareOp = depthTest.Func.Convert();
+
+ UpdatePassDepthStencil();
SignalStateChange();
}
@@ -1079,6 +1086,8 @@ namespace Ryujinx.Graphics.Vulkan
_newState.StencilFrontPassOp = stencilTest.FrontDpPass.Convert();
_newState.StencilFrontDepthFailOp = stencilTest.FrontDpFail.Convert();
_newState.StencilFrontCompareOp = stencilTest.FrontFunc.Convert();
+
+ UpdatePassDepthStencil();
SignalStateChange();
}
@@ -1426,7 +1435,23 @@ namespace Ryujinx.Graphics.Vulkan
}
}
+ if (IsMainPipeline)
+ {
+ FramebufferParams?.ClearBindings();
+ }
+
FramebufferParams = new FramebufferParams(Device, colors, depthStencil);
+
+ if (IsMainPipeline)
+ {
+ FramebufferParams.AddBindings();
+
+ _newState.FeedbackLoopAspects = FeedbackLoopAspects.None;
+ _bindingBarriersDirty = true;
+ }
+
+ _passWritesDepthStencil = false;
+ UpdatePassDepthStencil();
UpdatePipelineAttachmentFormats();
}
@@ -1493,11 +1518,82 @@ namespace Ryujinx.Graphics.Vulkan
}
}
- Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate);
+ Gd.Barriers.Flush(Cbs, _program, _feedbackLoop != 0, RenderPassActive, _rpHolder, EndRenderPassDelegate);
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute);
}
+ private bool ChangeFeedbackLoop(FeedbackLoopAspects aspects)
+ {
+ if (_feedbackLoop != aspects)
+ {
+ if (Gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop)
+ {
+ DynamicState.SetFeedbackLoop(aspects);
+ }
+ else
+ {
+ _newState.FeedbackLoopAspects = aspects;
+ }
+
+ _feedbackLoop = aspects;
+
+ return true;
+ }
+
+ return false;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private bool UpdateFeedbackLoop()
+ {
+ List hazards = _descriptorSetUpdater.FeedbackLoopHazards;
+
+ if ((hazards?.Count ?? 0) > 0)
+ {
+ FeedbackLoopAspects aspects = 0;
+
+ foreach (TextureView view in hazards)
+ {
+ // May need to enforce feedback loop layout here in the future.
+ // Though technically, it should always work with the general layout.
+
+ if (view.Info.Format.IsDepthOrStencil())
+ {
+ if (_passWritesDepthStencil)
+ {
+ // If depth/stencil isn't written in the pass, it doesn't count as a feedback loop.
+
+ aspects |= FeedbackLoopAspects.Depth;
+ }
+ }
+ else
+ {
+ aspects |= FeedbackLoopAspects.Color;
+ }
+ }
+
+ return ChangeFeedbackLoop(aspects);
+ }
+ else if (_feedbackLoop != 0)
+ {
+ return ChangeFeedbackLoop(FeedbackLoopAspects.None);
+ }
+
+ return false;
+ }
+
+ private void UpdatePassDepthStencil()
+ {
+ if (!RenderPassActive)
+ {
+ _passWritesDepthStencil = false;
+ }
+
+ // Stencil test being enabled doesn't necessarily mean a write, but it's not critical to check.
+ _passWritesDepthStencil |= (_newState.DepthTestEnable && _newState.DepthWriteEnable) || _newState.StencilTestEnable;
+ }
+
private bool RecreateGraphicsPipelineIfNeeded()
{
if (AutoFlush.ShouldFlushDraw(DrawCount))
@@ -1505,7 +1601,7 @@ namespace Ryujinx.Graphics.Vulkan
Gd.FlushAllCommands();
}
- DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer);
+ DynamicState.ReplayIfDirty(Gd, CommandBuffer);
if (_needsIndexBufferRebind && _indexBufferPattern == null)
{
@@ -1539,7 +1635,15 @@ namespace Ryujinx.Graphics.Vulkan
_vertexBufferUpdater.Commit(Cbs);
}
- if (_graphicsStateDirty || Pbp != PipelineBindPoint.Graphics)
+ if (_bindingBarriersDirty)
+ {
+ // Stale barriers may have been activated by switching program. Emit any that are relevant.
+ _descriptorSetUpdater.InsertBindingBarriers(Cbs);
+
+ _bindingBarriersDirty = false;
+ }
+
+ if (UpdateFeedbackLoop() || _graphicsStateDirty || Pbp != PipelineBindPoint.Graphics)
{
if (!CreatePipeline(PipelineBindPoint.Graphics))
{
@@ -1548,17 +1652,9 @@ namespace Ryujinx.Graphics.Vulkan
_graphicsStateDirty = false;
Pbp = PipelineBindPoint.Graphics;
-
- if (_bindingBarriersDirty)
- {
- // Stale barriers may have been activated by switching program. Emit any that are relevant.
- _descriptorSetUpdater.InsertBindingBarriers(Cbs);
-
- _bindingBarriersDirty = false;
- }
}
- Gd.Barriers.Flush(Cbs, _program, RenderPassActive, _rpHolder, EndRenderPassDelegate);
+ Gd.Barriers.Flush(Cbs, _program, _feedbackLoop != 0, RenderPassActive, _rpHolder, EndRenderPassDelegate);
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics);
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs
index 1cc33f728c..ad26ff7b39 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineDynamicState.cs
@@ -1,5 +1,6 @@
using Ryujinx.Common.Memory;
using Silk.NET.Vulkan;
+using Silk.NET.Vulkan.Extensions.EXT;
namespace Ryujinx.Graphics.Vulkan
{
@@ -21,6 +22,8 @@ namespace Ryujinx.Graphics.Vulkan
private Array4 _blendConstants;
+ private FeedbackLoopAspects _feedbackLoopAspects;
+
public uint ViewportsCount;
public Array16 Viewports;
@@ -32,7 +35,8 @@ namespace Ryujinx.Graphics.Vulkan
Scissor = 1 << 2,
Stencil = 1 << 3,
Viewport = 1 << 4,
- All = Blend | DepthBias | Scissor | Stencil | Viewport,
+ FeedbackLoop = 1 << 5,
+ All = Blend | DepthBias | Scissor | Stencil | Viewport | FeedbackLoop,
}
private DirtyFlags _dirty;
@@ -99,13 +103,22 @@ namespace Ryujinx.Graphics.Vulkan
}
}
+ public void SetFeedbackLoop(FeedbackLoopAspects aspects)
+ {
+ _feedbackLoopAspects = aspects;
+
+ _dirty |= DirtyFlags.FeedbackLoop;
+ }
+
public void ForceAllDirty()
{
_dirty = DirtyFlags.All;
}
- public void ReplayIfDirty(Vk api, CommandBuffer commandBuffer)
+ public void ReplayIfDirty(VulkanRenderer gd, CommandBuffer commandBuffer)
{
+ Vk api = gd.Api;
+
if (_dirty.HasFlag(DirtyFlags.Blend))
{
RecordBlend(api, commandBuffer);
@@ -131,6 +144,11 @@ namespace Ryujinx.Graphics.Vulkan
RecordViewport(api, commandBuffer);
}
+ if (_dirty.HasFlag(DirtyFlags.FeedbackLoop) && gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop)
+ {
+ RecordFeedbackLoop(gd.DynamicFeedbackLoopApi, commandBuffer);
+ }
+
_dirty = DirtyFlags.None;
}
@@ -169,5 +187,17 @@ namespace Ryujinx.Graphics.Vulkan
api.CmdSetViewport(commandBuffer, 0, ViewportsCount, Viewports.AsSpan());
}
}
+
+ private readonly void RecordFeedbackLoop(ExtAttachmentFeedbackLoopDynamicState api, CommandBuffer commandBuffer)
+ {
+ ImageAspectFlags aspects = (_feedbackLoopAspects & FeedbackLoopAspects.Color) != 0 ? ImageAspectFlags.ColorBit : 0;
+
+ if ((_feedbackLoopAspects & FeedbackLoopAspects.Depth) != 0)
+ {
+ aspects |= ImageAspectFlags.DepthBit | ImageAspectFlags.StencilBit;
+ }
+
+ api.CmdSetAttachmentFeedbackLoopEnable(commandBuffer, aspects);
+ }
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs
index cf65eefb0d..54d43bdba7 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineFull.cs
@@ -28,6 +28,8 @@ namespace Ryujinx.Graphics.Vulkan
_activeBufferMirrors = new();
CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
+
+ IsMainPipeline = true;
}
private void CopyPendingQuery()
@@ -235,7 +237,7 @@ namespace Ryujinx.Graphics.Vulkan
if (Pipeline != null && Pbp == PipelineBindPoint.Graphics)
{
- DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer);
+ DynamicState.ReplayIfDirty(Gd, CommandBuffer);
}
}
diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs
index 6b6b46a914..a726b9edb5 100644
--- a/src/Ryujinx.Graphics.Vulkan/PipelineState.cs
+++ b/src/Ryujinx.Graphics.Vulkan/PipelineState.cs
@@ -8,6 +8,7 @@ namespace Ryujinx.Graphics.Vulkan
struct PipelineState : IDisposable
{
private const int RequiredSubgroupSize = 32;
+ private const int MaxDynamicStatesCount = 9;
public PipelineUid Internal;
@@ -299,6 +300,12 @@ namespace Ryujinx.Graphics.Vulkan
set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6);
}
+ public FeedbackLoopAspects FeedbackLoopAspects
+ {
+ readonly get => (FeedbackLoopAspects)((Internal.Id8 >> 7) & 0x3);
+ set => Internal.Id8 = (Internal.Id8 & 0xFFFFFFFFFFFFFE7F) | (((ulong)value) << 7);
+ }
+
public bool HasTessellationControlShader;
public NativeArray Stages;
public PipelineLayout PipelineLayout;
@@ -564,9 +571,11 @@ namespace Ryujinx.Graphics.Vulkan
}
bool supportsExtDynamicState = gd.Capabilities.SupportsExtendedDynamicState;
- int dynamicStatesCount = supportsExtDynamicState ? 8 : 7;
+ bool supportsFeedbackLoopDynamicState = gd.Capabilities.SupportsDynamicAttachmentFeedbackLoop;
- DynamicState* dynamicStates = stackalloc DynamicState[dynamicStatesCount];
+ DynamicState* dynamicStates = stackalloc DynamicState[MaxDynamicStatesCount];
+
+ int dynamicStatesCount = 7;
dynamicStates[0] = DynamicState.Viewport;
dynamicStates[1] = DynamicState.Scissor;
@@ -578,7 +587,12 @@ namespace Ryujinx.Graphics.Vulkan
if (supportsExtDynamicState)
{
- dynamicStates[7] = DynamicState.VertexInputBindingStrideExt;
+ dynamicStates[dynamicStatesCount++] = DynamicState.VertexInputBindingStrideExt;
+ }
+
+ if (supportsFeedbackLoopDynamicState)
+ {
+ dynamicStates[dynamicStatesCount++] = DynamicState.AttachmentFeedbackLoopEnableExt;
}
var pipelineDynamicStateCreateInfo = new PipelineDynamicStateCreateInfo
@@ -588,9 +602,27 @@ namespace Ryujinx.Graphics.Vulkan
PDynamicStates = dynamicStates,
};
+ PipelineCreateFlags flags = 0;
+
+ if (gd.Capabilities.SupportsAttachmentFeedbackLoop)
+ {
+ FeedbackLoopAspects aspects = FeedbackLoopAspects;
+
+ if ((aspects & FeedbackLoopAspects.Color) != 0)
+ {
+ flags |= PipelineCreateFlags.CreateColorAttachmentFeedbackLoopBitExt;
+ }
+
+ if ((aspects & FeedbackLoopAspects.Depth) != 0)
+ {
+ flags |= PipelineCreateFlags.CreateDepthStencilAttachmentFeedbackLoopBitExt;
+ }
+ }
+
var pipelineCreateInfo = new GraphicsPipelineCreateInfo
{
SType = StructureType.GraphicsPipelineCreateInfo,
+ Flags = flags,
StageCount = StagesCount,
PStages = Stages.Pointer,
PVertexInputState = &vertexInputState,
diff --git a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs
index f78b9ed476..10b36a3f9a 100644
--- a/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs
+++ b/src/Ryujinx.Graphics.Vulkan/TextureStorage.cs
@@ -4,6 +4,7 @@ using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
using System.Numerics;
+using System.Runtime.CompilerServices;
using Format = Ryujinx.Graphics.GAL.Format;
using VkBuffer = Silk.NET.Vulkan.Buffer;
using VkFormat = Silk.NET.Vulkan.Format;
@@ -12,6 +13,11 @@ namespace Ryujinx.Graphics.Vulkan
{
class TextureStorage : IDisposable
{
+ private struct TextureSliceInfo
+ {
+ public int BindCount;
+ }
+
private const MemoryPropertyFlags DefaultImageMemoryFlags =
MemoryPropertyFlags.DeviceLocalBit;
@@ -43,6 +49,7 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Image _image;
private readonly Auto _imageAuto;
private readonly Auto _allocationAuto;
+ private readonly int _depthOrLayers;
private Auto _foreignAllocationAuto;
private Dictionary _aliasedStorages;
@@ -55,6 +62,9 @@ namespace Ryujinx.Graphics.Vulkan
private int _viewsCount;
private readonly ulong _size;
+ private int _bindCount;
+ private readonly TextureSliceInfo[] _slices;
+
public VkFormat VkFormat { get; }
public unsafe TextureStorage(
@@ -73,6 +83,7 @@ namespace Ryujinx.Graphics.Vulkan
var depth = (uint)(info.Target == Target.Texture3D ? info.Depth : 1);
VkFormat = format;
+ _depthOrLayers = info.GetDepthOrLayers();
var type = info.Target.Convert();
@@ -80,7 +91,7 @@ namespace Ryujinx.Graphics.Vulkan
var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples);
- var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample);
+ var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities);
var flags = ImageCreateFlags.CreateMutableFormatBit | ImageCreateFlags.CreateExtendedUsageBit;
@@ -148,6 +159,8 @@ namespace Ryujinx.Graphics.Vulkan
InitialTransition(ImageLayout.Preinitialized, ImageLayout.General);
}
+
+ _slices = new TextureSliceInfo[levels * _depthOrLayers];
}
public TextureStorage CreateAliasedColorForDepthStorageUnsafe(Format format)
@@ -292,7 +305,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
- public static ImageUsageFlags GetImageUsage(Format format, Target target, bool supportsMsStorage)
+ public static ImageUsageFlags GetImageUsage(Format format, Target target, in HardwareCapabilities capabilities)
{
var usage = DefaultUsageFlags;
@@ -305,11 +318,19 @@ namespace Ryujinx.Graphics.Vulkan
usage |= ImageUsageFlags.ColorAttachmentBit;
}
+ bool supportsMsStorage = capabilities.SupportsShaderStorageImageMultisample;
+
if (format.IsImageCompatible() && (supportsMsStorage || !target.IsMultisample()))
{
usage |= ImageUsageFlags.StorageBit;
}
+ if (capabilities.SupportsAttachmentFeedbackLoop &&
+ (usage & (ImageUsageFlags.DepthStencilAttachmentBit | ImageUsageFlags.ColorAttachmentBit)) != 0)
+ {
+ usage |= ImageUsageFlags.AttachmentFeedbackLoopBitExt;
+ }
+
return usage;
}
@@ -510,6 +531,55 @@ namespace Ryujinx.Graphics.Vulkan
}
}
+ public void AddBinding(TextureView view)
+ {
+ // Assumes a view only has a first level.
+
+ int index = view.FirstLevel * _depthOrLayers + view.FirstLayer;
+ int layers = view.Layers;
+
+ for (int i = 0; i < layers; i++)
+ {
+ ref TextureSliceInfo info = ref _slices[index++];
+
+ info.BindCount++;
+ }
+
+ _bindCount++;
+ }
+
+ public void ClearBindings()
+ {
+ if (_bindCount != 0)
+ {
+ Array.Clear(_slices, 0, _slices.Length);
+
+ _bindCount = 0;
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool IsBound(TextureView view)
+ {
+ if (_bindCount != 0)
+ {
+ int index = view.FirstLevel * _depthOrLayers + view.FirstLayer;
+ int layers = view.Layers;
+
+ for (int i = 0; i < layers; i++)
+ {
+ ref TextureSliceInfo info = ref _slices[index++];
+
+ if (info.BindCount != 0)
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
public void IncrementViewsCount()
{
_viewsCount++;
diff --git a/src/Ryujinx.Graphics.Vulkan/TextureView.cs b/src/Ryujinx.Graphics.Vulkan/TextureView.cs
index c5453c0c7e..9b3f466627 100644
--- a/src/Ryujinx.Graphics.Vulkan/TextureView.cs
+++ b/src/Ryujinx.Graphics.Vulkan/TextureView.cs
@@ -23,6 +23,8 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Auto _imageView2dArray;
private Dictionary _selfManagedViews;
+ private int _hazardUses;
+
private readonly TextureCreateInfo _info;
private HashTableSlim _renderPasses;
@@ -60,7 +62,7 @@ namespace Ryujinx.Graphics.Vulkan
gd.Textures.Add(this);
var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format);
- var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample);
+ var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities);
var levels = (uint)info.Levels;
var layers = (uint)info.GetLayers();
@@ -1034,6 +1036,34 @@ namespace Ryujinx.Graphics.Vulkan
throw new NotImplementedException();
}
+ public void PrepareForUsage(CommandBufferScoped cbs, PipelineStageFlags flags, List feedbackLoopHazards)
+ {
+ Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, flags);
+
+ if (feedbackLoopHazards != null && Storage.IsBound(this))
+ {
+ feedbackLoopHazards.Add(this);
+ _hazardUses++;
+ }
+ }
+
+ public void ClearUsage(List feedbackLoopHazards)
+ {
+ if (_hazardUses != 0 && feedbackLoopHazards != null)
+ {
+ feedbackLoopHazards.Remove(this);
+ _hazardUses--;
+ }
+ }
+
+ public void DecrementHazardUses()
+ {
+ if (_hazardUses != 0)
+ {
+ _hazardUses--;
+ }
+ }
+
public (RenderPassHolder rpHolder, Auto framebuffer) GetPassAndFramebuffer(
VulkanRenderer gd,
Device device,
diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs
index 5a9844cb96..2c327fdb75 100644
--- a/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs
+++ b/src/Ryujinx.Graphics.Vulkan/VulkanInitialization.cs
@@ -44,6 +44,8 @@ namespace Ryujinx.Graphics.Vulkan
"VK_EXT_4444_formats",
"VK_KHR_8bit_storage",
"VK_KHR_maintenance2",
+ "VK_EXT_attachment_feedback_loop_layout",
+ "VK_EXT_attachment_feedback_loop_dynamic_state",
};
private static readonly string[] _requiredExtensions = {
@@ -357,6 +359,28 @@ namespace Ryujinx.Graphics.Vulkan
features2.PNext = &supportedFeaturesDepthClipControl;
}
+ PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT supportedFeaturesAttachmentFeedbackLoopLayout = new()
+ {
+ SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt,
+ PNext = features2.PNext,
+ };
+
+ if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout"))
+ {
+ features2.PNext = &supportedFeaturesAttachmentFeedbackLoopLayout;
+ }
+
+ PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT supportedFeaturesDynamicAttachmentFeedbackLoopLayout = new()
+ {
+ SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt,
+ PNext = features2.PNext,
+ };
+
+ if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state"))
+ {
+ features2.PNext = &supportedFeaturesDynamicAttachmentFeedbackLoopLayout;
+ }
+
PhysicalDeviceVulkan12Features supportedPhysicalDeviceVulkan12Features = new()
{
SType = StructureType.PhysicalDeviceVulkan12Features,
@@ -531,6 +555,36 @@ namespace Ryujinx.Graphics.Vulkan
pExtendedFeatures = &featuresDepthClipControl;
}
+ PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT featuresAttachmentFeedbackLoopLayout;
+
+ if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout") &&
+ supportedFeaturesAttachmentFeedbackLoopLayout.AttachmentFeedbackLoopLayout)
+ {
+ featuresAttachmentFeedbackLoopLayout = new()
+ {
+ SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt,
+ PNext = pExtendedFeatures,
+ AttachmentFeedbackLoopLayout = true,
+ };
+
+ pExtendedFeatures = &featuresAttachmentFeedbackLoopLayout;
+ }
+
+ PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT featuresDynamicAttachmentFeedbackLoopLayout;
+
+ if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state") &&
+ supportedFeaturesDynamicAttachmentFeedbackLoopLayout.AttachmentFeedbackLoopDynamicState)
+ {
+ featuresDynamicAttachmentFeedbackLoopLayout = new()
+ {
+ SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt,
+ PNext = pExtendedFeatures,
+ AttachmentFeedbackLoopDynamicState = true,
+ };
+
+ pExtendedFeatures = &featuresDynamicAttachmentFeedbackLoopLayout;
+ }
+
var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray();
IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];
diff --git a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
index c9ce678b77..33e41ab489 100644
--- a/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
+++ b/src/Ryujinx.Graphics.Vulkan/VulkanRenderer.cs
@@ -38,6 +38,7 @@ namespace Ryujinx.Graphics.Vulkan
internal KhrPushDescriptor PushDescriptorApi { get; private set; }
internal ExtTransformFeedback TransformFeedbackApi { get; private set; }
internal KhrDrawIndirectCount DrawIndirectCountApi { get; private set; }
+ internal ExtAttachmentFeedbackLoopDynamicState DynamicFeedbackLoopApi { get; private set; }
internal uint QueueFamilyIndex { get; private set; }
internal Queue Queue { get; private set; }
@@ -149,6 +150,11 @@ namespace Ryujinx.Graphics.Vulkan
DrawIndirectCountApi = drawIndirectCountApi;
}
+ if (Api.TryGetDeviceExtension(_instance.Instance, _device, out ExtAttachmentFeedbackLoopDynamicState dynamicFeedbackLoopApi))
+ {
+ DynamicFeedbackLoopApi = dynamicFeedbackLoopApi;
+ }
+
if (maxQueueCount >= 2)
{
Api.GetDeviceQueue(_device, queueFamilyIndex, 1, out var backgroundQueue);
@@ -243,6 +249,16 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt,
};
+ PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesEXT featuresAttachmentFeedbackLoop = new()
+ {
+ SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopLayoutFeaturesExt,
+ };
+
+ PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesEXT featuresDynamicAttachmentFeedbackLoop = new()
+ {
+ SType = StructureType.PhysicalDeviceAttachmentFeedbackLoopDynamicStateFeaturesExt,
+ };
+
PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new()
{
SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr,
@@ -279,6 +295,22 @@ namespace Ryujinx.Graphics.Vulkan
features2.PNext = &featuresDepthClipControl;
}
+ bool supportsAttachmentFeedbackLoop = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_layout");
+
+ if (supportsAttachmentFeedbackLoop)
+ {
+ featuresAttachmentFeedbackLoop.PNext = features2.PNext;
+ features2.PNext = &featuresAttachmentFeedbackLoop;
+ }
+
+ bool supportsDynamicAttachmentFeedbackLoop = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_attachment_feedback_loop_dynamic_state");
+
+ if (supportsDynamicAttachmentFeedbackLoop)
+ {
+ featuresDynamicAttachmentFeedbackLoop.PNext = features2.PNext;
+ features2.PNext = &featuresDynamicAttachmentFeedbackLoop;
+ }
+
bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset");
if (usePortability)
@@ -401,6 +433,8 @@ namespace Ryujinx.Graphics.Vulkan
_physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"),
_physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName),
supportsDepthClipControl && featuresDepthClipControl.DepthClipControl,
+ supportsAttachmentFeedbackLoop && featuresAttachmentFeedbackLoop.AttachmentFeedbackLoopLayout,
+ supportsDynamicAttachmentFeedbackLoop && featuresDynamicAttachmentFeedbackLoop.AttachmentFeedbackLoopDynamicState,
propertiesSubgroup.SubgroupSize,
supportedSampleCounts,
portabilityFlags,
diff --git a/src/Ryujinx.Gtk3/Program.cs b/src/Ryujinx.Gtk3/Program.cs
index 8bad1a0c7b..2d350374bf 100644
--- a/src/Ryujinx.Gtk3/Program.cs
+++ b/src/Ryujinx.Gtk3/Program.cs
@@ -4,6 +4,7 @@ using Ryujinx.Common.Configuration;
using Ryujinx.Common.GraphicsDriver;
using Ryujinx.Common.Logging;
using Ryujinx.Common.SystemInterop;
+using Ryujinx.Common.Utilities;
using Ryujinx.Graphics.Vulkan.MoltenVK;
using Ryujinx.Modules;
using Ryujinx.SDL2.Common;
@@ -41,9 +42,6 @@ namespace Ryujinx
[LibraryImport("user32.dll", SetLastError = true)]
public static partial int MessageBoxA(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type);
- [LibraryImport("libc", SetLastError = true)]
- private static partial int setenv([MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string value, int overwrite);
-
private const uint MbIconWarning = 0x30;
static Program()
@@ -105,8 +103,7 @@ namespace Ryujinx
throw new NotSupportedException("Failed to initialize multi-threading support.");
}
- Environment.SetEnvironmentVariable("GDK_BACKEND", "x11");
- setenv("GDK_BACKEND", "x11", 1);
+ OsUtils.SetEnvironmentVariableNoCaching("GDK_BACKEND", "x11");
}
if (OperatingSystem.IsMacOS())
@@ -125,19 +122,13 @@ namespace Ryujinx
resourcesDataDir = baseDirectory;
}
- static void SetEnvironmentVariableNoCaching(string key, string value)
- {
- int res = setenv(key, value, 1);
- Debug.Assert(res != -1);
- }
-
// On macOS, GTK3 needs XDG_DATA_DIRS to be set, otherwise it will try searching for "gschemas.compiled" in system directories.
- SetEnvironmentVariableNoCaching("XDG_DATA_DIRS", Path.Combine(resourcesDataDir, "share"));
+ OsUtils.SetEnvironmentVariableNoCaching("XDG_DATA_DIRS", Path.Combine(resourcesDataDir, "share"));
// On macOS, GTK3 needs GDK_PIXBUF_MODULE_FILE to be set, otherwise it will try searching for "loaders.cache" in system directories.
- SetEnvironmentVariableNoCaching("GDK_PIXBUF_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gdk-pixbuf-2.0", "2.10.0", "loaders.cache"));
+ OsUtils.SetEnvironmentVariableNoCaching("GDK_PIXBUF_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gdk-pixbuf-2.0", "2.10.0", "loaders.cache"));
- SetEnvironmentVariableNoCaching("GTK_IM_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gtk-3.0", "3.0.0", "immodules.cache"));
+ OsUtils.SetEnvironmentVariableNoCaching("GTK_IM_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gtk-3.0", "3.0.0", "immodules.cache"));
}
string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine);
@@ -233,9 +224,9 @@ namespace Ryujinx
// Logging system information.
PrintSystemInfo();
- // Enable OGL multithreading on the driver, when available.
+ // Enable OGL multithreading on the driver, and some other flags.
BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading;
- DriverUtilities.ToggleOGLThreading(threadingMode == BackendThreading.Off);
+ DriverUtilities.InitDriverConfig(threadingMode == BackendThreading.Off);
// Initialize Gtk.
Application.Init();
diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs
index 5c30cd18fb..07995dbdd7 100644
--- a/src/Ryujinx.Headless.SDL2/Program.cs
+++ b/src/Ryujinx.Headless.SDL2/Program.cs
@@ -7,6 +7,7 @@ using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
using Ryujinx.Common.Configuration.Hid.Keyboard;
+using Ryujinx.Common.GraphicsDriver;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Logging.Targets;
using Ryujinx.Common.SystemInterop;
@@ -463,6 +464,8 @@ namespace Ryujinx.Headless.SDL2
GraphicsConfig.ShadersDumpPath = option.GraphicsShadersDumpPath;
GraphicsConfig.EnableMacroHLE = !option.DisableMacroHLE;
+ DriverUtilities.InitDriverConfig(option.BackendThreading == BackendThreading.Off);
+
while (true)
{
LoadApplication(option);
diff --git a/src/Ryujinx/Program.cs b/src/Ryujinx/Program.cs
index af9db7d636..6c83cedcf3 100644
--- a/src/Ryujinx/Program.cs
+++ b/src/Ryujinx/Program.cs
@@ -117,8 +117,8 @@ namespace Ryujinx.Ava
// Logging system information.
PrintSystemInfo();
- // Enable OGL multithreading on the driver, when available.
- DriverUtilities.ToggleOGLThreading(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off);
+ // Enable OGL multithreading on the driver, and some other flags.
+ DriverUtilities.InitDriverConfig(ConfigurationState.Instance.Graphics.BackendThreading == BackendThreading.Off);
// Check if keys exists.
if (!File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys")))
From 2f36a6665cc5ad0aeec49d6cce9425830d1cee41 Mon Sep 17 00:00:00 2001
From: gdkchan
Date: Thu, 12 Sep 2024 18:22:30 -0300
Subject: [PATCH 18/36] Implement Arm32 VSHLL and QADD16 instructions (#7301)
---
src/ARMeilleure/Decoders/OpCodeTable.cs | 2 +
src/ARMeilleure/Instructions/InstEmitAlu32.cs | 113 ++++++++++++++++++
.../Instructions/InstEmitSimdShift32.cs | 32 +++++
src/ARMeilleure/Instructions/InstName.cs | 1 +
src/Ryujinx.Tests/Cpu/CpuTestAlu32.cs | 1 +
src/Ryujinx.Tests/Cpu/CpuTestSimd32.cs | 23 ++++
6 files changed, 172 insertions(+)
diff --git a/src/ARMeilleure/Decoders/OpCodeTable.cs b/src/ARMeilleure/Decoders/OpCodeTable.cs
index 8595356704..20d567fe59 100644
--- a/src/ARMeilleure/Decoders/OpCodeTable.cs
+++ b/src/ARMeilleure/Decoders/OpCodeTable.cs
@@ -746,6 +746,7 @@ namespace ARMeilleure.Decoders
SetA32("<<<<01101000xxxxxxxxxxxxxx01xxxx", InstName.Pkh, InstEmit32.Pkh, OpCode32AluRsImm.Create);
SetA32("11110101xx01xxxx1111xxxxxxxxxxxx", InstName.Pld, InstEmit32.Nop, OpCode32.Create);
SetA32("11110111xx01xxxx1111xxxxxxx0xxxx", InstName.Pld, InstEmit32.Nop, OpCode32.Create);
+ SetA32("<<<<01100010xxxxxxxx11110001xxxx", InstName.Qadd16, InstEmit32.Qadd16, OpCode32AluReg.Create);
SetA32("<<<<011011111111xxxx11110011xxxx", InstName.Rbit, InstEmit32.Rbit, OpCode32AluReg.Create);
SetA32("<<<<011010111111xxxx11110011xxxx", InstName.Rev, InstEmit32.Rev, OpCode32AluReg.Create);
SetA32("<<<<011010111111xxxx11111011xxxx", InstName.Rev16, InstEmit32.Rev16, OpCode32AluReg.Create);
@@ -1034,6 +1035,7 @@ namespace ARMeilleure.Decoders
SetAsimd("111100101x>>>xxxxxxx0101>xx1xxxx", InstName.Vshl, InstEmit32.Vshl, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
SetAsimd("1111001x0xxxxxxxxxxx0100xxx0xxxx", InstName.Vshl, InstEmit32.Vshl_I, OpCode32SimdReg.Create, OpCode32SimdReg.CreateT32);
SetAsimd("1111001x1x>>>xxxxxxx101000x1xxxx", InstName.Vshll, InstEmit32.Vshll, OpCode32SimdShImmLong.Create, OpCode32SimdShImmLong.CreateT32); // A1 encoding.
+ SetAsimd("111100111x11<<10xxxx001100x0xxxx", InstName.Vshll, InstEmit32.Vshll2, OpCode32SimdMovn.Create, OpCode32SimdMovn.CreateT32); // A2 encoding.
SetAsimd("1111001x1x>>>xxxxxxx0000>xx1xxxx", InstName.Vshr, InstEmit32.Vshr, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
SetAsimd("111100101x>>>xxxxxxx100000x1xxx0", InstName.Vshrn, InstEmit32.Vshrn, OpCode32SimdShImmNarrow.Create, OpCode32SimdShImmNarrow.CreateT32);
SetAsimd("111100111x>>>xxxxxxx0101>xx1xxxx", InstName.Vsli, InstEmit32.Vsli_I, OpCode32SimdShImm.Create, OpCode32SimdShImm.CreateT32);
diff --git a/src/ARMeilleure/Instructions/InstEmitAlu32.cs b/src/ARMeilleure/Instructions/InstEmitAlu32.cs
index 9f419ba99e..8eabe093e8 100644
--- a/src/ARMeilleure/Instructions/InstEmitAlu32.cs
+++ b/src/ARMeilleure/Instructions/InstEmitAlu32.cs
@@ -292,6 +292,16 @@ namespace ARMeilleure.Instructions
EmitAluStore(context, res);
}
+ public static void Qadd16(ArmEmitterContext context)
+ {
+ OpCode32AluReg op = (OpCode32AluReg)context.CurrOp;
+
+ SetIntA32(context, op.Rd, EmitSigned16BitPair(context, GetIntA32(context, op.Rn), GetIntA32(context, op.Rm), (d, n, m) =>
+ {
+ EmitSaturateRange(context, d, context.Add(n, m), 16, unsigned: false, setQ: false);
+ }));
+ }
+
public static void Rbit(ArmEmitterContext context)
{
Operand m = GetAluM(context);
@@ -976,6 +986,94 @@ namespace ARMeilleure.Instructions
}
}
+ private static void EmitSaturateRange(ArmEmitterContext context, Operand result, Operand value, uint saturateTo, bool unsigned, bool setQ = true)
+ {
+ Debug.Assert(saturateTo <= 32);
+ Debug.Assert(!unsigned || saturateTo < 32);
+
+ if (!unsigned && saturateTo == 32)
+ {
+ // No saturation possible for this case.
+
+ context.Copy(result, value);
+
+ return;
+ }
+ else if (saturateTo == 0)
+ {
+ // Result is always zero if we saturate 0 bits.
+
+ context.Copy(result, Const(0));
+
+ return;
+ }
+
+ Operand satValue;
+
+ if (unsigned)
+ {
+ // Negative values always saturate (to zero).
+ // So we must always ignore the sign bit when masking, so that the truncated value will differ from the original one.
+
+ satValue = context.BitwiseAnd(value, Const((int)(uint.MaxValue >> (32 - (int)saturateTo))));
+ }
+ else
+ {
+ satValue = context.ShiftLeft(value, Const(32 - (int)saturateTo));
+ satValue = context.ShiftRightSI(satValue, Const(32 - (int)saturateTo));
+ }
+
+ // If the result is 0, the values are equal and we don't need saturation.
+ Operand lblNoSat = Label();
+ context.BranchIfFalse(lblNoSat, context.Subtract(value, satValue));
+
+ // Saturate and set Q flag.
+ if (unsigned)
+ {
+ if (saturateTo == 31)
+ {
+ // Only saturation case possible when going from 32 bits signed to 32 or 31 bits unsigned
+ // is when the signed input is negative, as all positive values are representable on a 31 bits range.
+
+ satValue = Const(0);
+ }
+ else
+ {
+ satValue = context.ShiftRightSI(value, Const(31));
+ satValue = context.BitwiseNot(satValue);
+ satValue = context.ShiftRightUI(satValue, Const(32 - (int)saturateTo));
+ }
+ }
+ else
+ {
+ if (saturateTo == 1)
+ {
+ satValue = context.ShiftRightSI(value, Const(31));
+ }
+ else
+ {
+ satValue = Const(uint.MaxValue >> (33 - (int)saturateTo));
+ satValue = context.BitwiseExclusiveOr(satValue, context.ShiftRightSI(value, Const(31)));
+ }
+ }
+
+ if (setQ)
+ {
+ SetFlag(context, PState.QFlag, Const(1));
+ }
+
+ context.Copy(result, satValue);
+
+ Operand lblExit = Label();
+ context.Branch(lblExit);
+
+ context.MarkLabel(lblNoSat);
+
+ context.Copy(result, value);
+
+ context.MarkLabel(lblExit);
+ }
+
private static void EmitSaturateUqadd(ArmEmitterContext context, Operand result, Operand value, uint saturateTo)
{
Debug.Assert(saturateTo <= 32);
@@ -1053,6 +1151,21 @@ namespace ARMeilleure.Instructions
context.MarkLabel(lblExit);
}
+ private static Operand EmitSigned16BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action elementAction)
+ {
+ Operand tempD = context.AllocateLocal(OperandType.I32);
+
+ Operand tempN = context.SignExtend16(OperandType.I32, rn);
+ Operand tempM = context.SignExtend16(OperandType.I32, rm);
+ elementAction(tempD, tempN, tempM);
+ Operand tempD2 = context.ZeroExtend16(OperandType.I32, tempD);
+
+ tempN = context.ShiftRightSI(rn, Const(16));
+ tempM = context.ShiftRightSI(rm, Const(16));
+ elementAction(tempD, tempN, tempM);
+ return context.BitwiseOr(tempD2, context.ShiftLeft(tempD, Const(16)));
+ }
+
private static Operand EmitUnsigned16BitPair(ArmEmitterContext context, Operand rn, Operand rm, Action elementAction)
{
Operand tempD = context.AllocateLocal(OperandType.I32);
diff --git a/src/ARMeilleure/Instructions/InstEmitSimdShift32.cs b/src/ARMeilleure/Instructions/InstEmitSimdShift32.cs
index e9e3b52b90..eb28a0c5af 100644
--- a/src/ARMeilleure/Instructions/InstEmitSimdShift32.cs
+++ b/src/ARMeilleure/Instructions/InstEmitSimdShift32.cs
@@ -106,6 +106,38 @@ namespace ARMeilleure.Instructions
context.Copy(GetVecA32(op.Qd), res);
}
+ public static void Vshll2(ArmEmitterContext context)
+ {
+ OpCode32Simd op = (OpCode32Simd)context.CurrOp;
+
+ Operand res = context.VectorZero();
+
+ int elems = op.GetBytesCount() >> op.Size;
+
+ for (int index = 0; index < elems; index++)
+ {
+ Operand me = EmitVectorExtract32(context, op.Qm, op.Im + index, op.Size, !op.U);
+
+ if (op.Size == 2)
+ {
+ if (op.U)
+ {
+ me = context.ZeroExtend32(OperandType.I64, me);
+ }
+ else
+ {
+ me = context.SignExtend32(OperandType.I64, me);
+ }
+ }
+
+ me = context.ShiftLeft(me, Const(8 << op.Size));
+
+ res = EmitVectorInsert(context, res, me, index, op.Size + 1);
+ }
+
+ context.Copy(GetVecA32(op.Qd), res);
+ }
+
public static void Vshr(ArmEmitterContext context)
{
OpCode32SimdShImm op = (OpCode32SimdShImm)context.CurrOp;
diff --git a/src/ARMeilleure/Instructions/InstName.cs b/src/ARMeilleure/Instructions/InstName.cs
index ac85412d1b..74c33155b1 100644
--- a/src/ARMeilleure/Instructions/InstName.cs
+++ b/src/ARMeilleure/Instructions/InstName.cs
@@ -527,6 +527,7 @@ namespace ARMeilleure.Instructions
Pld,
Pop,
Push,
+ Qadd16,
Rev,
Revsh,
Rsb,
diff --git a/src/Ryujinx.Tests/Cpu/CpuTestAlu32.cs b/src/Ryujinx.Tests/Cpu/CpuTestAlu32.cs
index 132ddfd0e8..1e66d8112e 100644
--- a/src/Ryujinx.Tests/Cpu/CpuTestAlu32.cs
+++ b/src/Ryujinx.Tests/Cpu/CpuTestAlu32.cs
@@ -29,6 +29,7 @@ namespace Ryujinx.Tests.Cpu
{
return new[]
{
+ 0xe6200f10u, // QADD16 R0, R0, R0
0xe6600f10u, // UQADD16 R0, R0, R0
0xe6600f70u, // UQSUB16 R0, R0, R0
};
diff --git a/src/Ryujinx.Tests/Cpu/CpuTestSimd32.cs b/src/Ryujinx.Tests/Cpu/CpuTestSimd32.cs
index f843fd561a..08202c9e17 100644
--- a/src/Ryujinx.Tests/Cpu/CpuTestSimd32.cs
+++ b/src/Ryujinx.Tests/Cpu/CpuTestSimd32.cs
@@ -328,6 +328,29 @@ namespace Ryujinx.Tests.Cpu
CompareAgainstUnicorn();
}
+ [Test, Pairwise, Description("VSHLL. {}, , #")]
+ public void Vshll([Values(0u, 2u)] uint rd,
+ [Values(1u, 0u)] uint rm,
+ [Values(0u, 1u, 2u)] uint size,
+ [Random(RndCnt)] ulong z,
+ [Random(RndCnt)] ulong a,
+ [Random(RndCnt)] ulong b)
+ {
+ uint opcode = 0xf3b20300u; // VSHLL.I8 Q0, D0, #8
+
+ opcode |= ((rm & 0xf) << 0) | ((rm & 0x10) << 1);
+ opcode |= ((rd & 0xf) << 12) | ((rd & 0x10) << 18);
+ opcode |= size << 18;
+
+ V128 v0 = MakeVectorE0E1(z, z);
+ V128 v1 = MakeVectorE0E1(a, z);
+ V128 v2 = MakeVectorE0E1(b, z);
+
+ SingleOpcode(opcode, v0: v0, v1: v1, v2: v2);
+
+ CompareAgainstUnicorn();
+ }
+
[Test, Pairwise, Description("VSWP D0, D0")]
public void Vswp([Values(0u, 1u)] uint rd,
[Values(0u, 1u)] uint rm,
From 62216782ca9ca12ea01c88b2a43733e8949c5692 Mon Sep 17 00:00:00 2001
From: Emmanuel Hansen
Date: Sun, 15 Sep 2024 20:39:10 +0000
Subject: [PATCH 19/36] Make GetFunctionPointerForDelegate as explicit as
possible (#7279)
Co-authored-by: gdk
---
src/ARMeilleure/Translation/DelegateInfo.cs | 6 +-
src/ARMeilleure/Translation/Delegates.cs | 509 +++++++++++++-------
2 files changed, 344 insertions(+), 171 deletions(-)
diff --git a/src/ARMeilleure/Translation/DelegateInfo.cs b/src/ARMeilleure/Translation/DelegateInfo.cs
index 27479a0038..7066254375 100644
--- a/src/ARMeilleure/Translation/DelegateInfo.cs
+++ b/src/ARMeilleure/Translation/DelegateInfo.cs
@@ -1,5 +1,4 @@
using System;
-using System.Runtime.InteropServices;
namespace ARMeilleure.Translation
{
@@ -11,11 +10,10 @@ namespace ARMeilleure.Translation
public IntPtr FuncPtr { get; }
- public DelegateInfo(Delegate dlg)
+ public DelegateInfo(Delegate dlg, IntPtr funcPtr)
{
_dlg = dlg;
-
- FuncPtr = Marshal.GetFunctionPointerForDelegate(dlg);
+ FuncPtr = funcPtr;
}
}
}
diff --git a/src/ARMeilleure/Translation/Delegates.cs b/src/ARMeilleure/Translation/Delegates.cs
index 63db789dfa..66412b8e68 100644
--- a/src/ARMeilleure/Translation/Delegates.cs
+++ b/src/ARMeilleure/Translation/Delegates.cs
@@ -3,6 +3,7 @@ using ARMeilleure.State;
using System;
using System.Collections.Generic;
using System.Reflection;
+using System.Runtime.InteropServices;
namespace ARMeilleure.Translation
{
@@ -64,11 +65,11 @@ namespace ARMeilleure.Translation
return index;
}
- private static void SetDelegateInfo(Delegate dlg)
+ private static void SetDelegateInfo(Delegate dlg, IntPtr funcPtr)
{
string key = GetKey(dlg.Method);
- _delegates.Add(key, new DelegateInfo(dlg)); // ArgumentException (key).
+ _delegates.Add(key, new DelegateInfo(dlg, funcPtr)); // ArgumentException (key).
}
private static string GetKey(MethodInfo info)
@@ -82,179 +83,353 @@ namespace ARMeilleure.Translation
{
_delegates = new SortedList();
- SetDelegateInfo(new MathAbs(Math.Abs));
- SetDelegateInfo(new MathCeiling(Math.Ceiling));
- SetDelegateInfo(new MathFloor(Math.Floor));
- SetDelegateInfo(new MathRound(Math.Round));
- SetDelegateInfo(new MathTruncate(Math.Truncate));
+ var dlgMathAbs = new MathAbs(Math.Abs);
+ var dlgMathCeiling = new MathCeiling(Math.Ceiling);
+ var dlgMathFloor = new MathFloor(Math.Floor);
+ var dlgMathRound = new MathRound(Math.Round);
+ var dlgMathTruncate = new MathTruncate(Math.Truncate);
- SetDelegateInfo(new MathFAbs(MathF.Abs));
- SetDelegateInfo(new MathFCeiling(MathF.Ceiling));
- SetDelegateInfo(new MathFFloor(MathF.Floor));
- SetDelegateInfo(new MathFRound(MathF.Round));
- SetDelegateInfo(new MathFTruncate(MathF.Truncate));
+ var dlgMathFAbs = new MathFAbs(MathF.Abs);
+ var dlgMathFCeiling = new MathFCeiling(MathF.Ceiling);
+ var dlgMathFFloor = new MathFFloor(MathF.Floor);
+ var dlgMathFRound = new MathFRound(MathF.Round);
+ var dlgMathFTruncate = new MathFTruncate(MathF.Truncate);
- SetDelegateInfo(new NativeInterfaceBreak(NativeInterface.Break));
- SetDelegateInfo(new NativeInterfaceCheckSynchronization(NativeInterface.CheckSynchronization));
- SetDelegateInfo(new NativeInterfaceEnqueueForRejit(NativeInterface.EnqueueForRejit));
- SetDelegateInfo(new NativeInterfaceGetCntfrqEl0(NativeInterface.GetCntfrqEl0));
- SetDelegateInfo(new NativeInterfaceGetCntpctEl0(NativeInterface.GetCntpctEl0));
- SetDelegateInfo(new NativeInterfaceGetCntvctEl0(NativeInterface.GetCntvctEl0));
- SetDelegateInfo(new NativeInterfaceGetCtrEl0(NativeInterface.GetCtrEl0));
- SetDelegateInfo(new NativeInterfaceGetDczidEl0(NativeInterface.GetDczidEl0));
- SetDelegateInfo(new NativeInterfaceGetFunctionAddress(NativeInterface.GetFunctionAddress));
- SetDelegateInfo(new NativeInterfaceInvalidateCacheLine(NativeInterface.InvalidateCacheLine));
- SetDelegateInfo(new NativeInterfaceReadByte(NativeInterface.ReadByte));
- SetDelegateInfo(new NativeInterfaceReadUInt16(NativeInterface.ReadUInt16));
- SetDelegateInfo(new NativeInterfaceReadUInt32(NativeInterface.ReadUInt32));
- SetDelegateInfo(new NativeInterfaceReadUInt64(NativeInterface.ReadUInt64));
- SetDelegateInfo(new NativeInterfaceReadVector128(NativeInterface.ReadVector128));
- SetDelegateInfo(new NativeInterfaceSignalMemoryTracking(NativeInterface.SignalMemoryTracking));
- SetDelegateInfo(new NativeInterfaceSupervisorCall(NativeInterface.SupervisorCall));
- SetDelegateInfo(new NativeInterfaceThrowInvalidMemoryAccess(NativeInterface.ThrowInvalidMemoryAccess));
- SetDelegateInfo(new NativeInterfaceUndefined(NativeInterface.Undefined));
- SetDelegateInfo(new NativeInterfaceWriteByte(NativeInterface.WriteByte));
- SetDelegateInfo(new NativeInterfaceWriteUInt16(NativeInterface.WriteUInt16));
- SetDelegateInfo(new NativeInterfaceWriteUInt32(NativeInterface.WriteUInt32));
- SetDelegateInfo(new NativeInterfaceWriteUInt64(NativeInterface.WriteUInt64));
- SetDelegateInfo(new NativeInterfaceWriteVector128(NativeInterface.WriteVector128));
+ var dlgNativeInterfaceBreak = new NativeInterfaceBreak(NativeInterface.Break);
+ var dlgNativeInterfaceCheckSynchronization = new NativeInterfaceCheckSynchronization(NativeInterface.CheckSynchronization);
+ var dlgNativeInterfaceEnqueueForRejit = new NativeInterfaceEnqueueForRejit(NativeInterface.EnqueueForRejit);
+ var dlgNativeInterfaceGetCntfrqEl0 = new NativeInterfaceGetCntfrqEl0(NativeInterface.GetCntfrqEl0);
+ var dlgNativeInterfaceGetCntpctEl0 = new NativeInterfaceGetCntpctEl0(NativeInterface.GetCntpctEl0);
+ var dlgNativeInterfaceGetCntvctEl0 = new NativeInterfaceGetCntvctEl0(NativeInterface.GetCntvctEl0);
+ var dlgNativeInterfaceGetCtrEl0 = new NativeInterfaceGetCtrEl0(NativeInterface.GetCtrEl0);
+ var dlgNativeInterfaceGetDczidEl0 = new NativeInterfaceGetDczidEl0(NativeInterface.GetDczidEl0);
+ var dlgNativeInterfaceGetFunctionAddress = new NativeInterfaceGetFunctionAddress(NativeInterface.GetFunctionAddress);
+ var dlgNativeInterfaceInvalidateCacheLine = new NativeInterfaceInvalidateCacheLine(NativeInterface.InvalidateCacheLine);
+ var dlgNativeInterfaceReadByte = new NativeInterfaceReadByte(NativeInterface.ReadByte);
+ var dlgNativeInterfaceReadUInt16 = new NativeInterfaceReadUInt16(NativeInterface.ReadUInt16);
+ var dlgNativeInterfaceReadUInt32 = new NativeInterfaceReadUInt32(NativeInterface.ReadUInt32);
+ var dlgNativeInterfaceReadUInt64 = new NativeInterfaceReadUInt64(NativeInterface.ReadUInt64);
+ var dlgNativeInterfaceReadVector128 = new NativeInterfaceReadVector128(NativeInterface.ReadVector128);
+ var dlgNativeInterfaceSignalMemoryTracking = new NativeInterfaceSignalMemoryTracking(NativeInterface.SignalMemoryTracking);
+ var dlgNativeInterfaceSupervisorCall = new NativeInterfaceSupervisorCall(NativeInterface.SupervisorCall);
+ var dlgNativeInterfaceThrowInvalidMemoryAccess = new NativeInterfaceThrowInvalidMemoryAccess(NativeInterface.ThrowInvalidMemoryAccess);
+ var dlgNativeInterfaceUndefined = new NativeInterfaceUndefined(NativeInterface.Undefined);
+ var dlgNativeInterfaceWriteByte = new NativeInterfaceWriteByte(NativeInterface.WriteByte);
+ var dlgNativeInterfaceWriteUInt16 = new NativeInterfaceWriteUInt16(NativeInterface.WriteUInt16);
+ var dlgNativeInterfaceWriteUInt32 = new NativeInterfaceWriteUInt32(NativeInterface.WriteUInt32);
+ var dlgNativeInterfaceWriteUInt64 = new NativeInterfaceWriteUInt64(NativeInterface.WriteUInt64);
+ var dlgNativeInterfaceWriteVector128 = new NativeInterfaceWriteVector128(NativeInterface.WriteVector128);
- SetDelegateInfo(new SoftFallbackCountLeadingSigns(SoftFallback.CountLeadingSigns));
- SetDelegateInfo(new SoftFallbackCountLeadingZeros(SoftFallback.CountLeadingZeros));
- SetDelegateInfo(new SoftFallbackCrc32b(SoftFallback.Crc32b));
- SetDelegateInfo(new SoftFallbackCrc32cb(SoftFallback.Crc32cb));
- SetDelegateInfo(new SoftFallbackCrc32ch(SoftFallback.Crc32ch));
- SetDelegateInfo(new SoftFallbackCrc32cw(SoftFallback.Crc32cw));
- SetDelegateInfo(new SoftFallbackCrc32cx(SoftFallback.Crc32cx));
- SetDelegateInfo(new SoftFallbackCrc32h(SoftFallback.Crc32h));
- SetDelegateInfo(new SoftFallbackCrc32w(SoftFallback.Crc32w));
- SetDelegateInfo(new SoftFallbackCrc32x(SoftFallback.Crc32x));
- SetDelegateInfo(new SoftFallbackDecrypt(SoftFallback.Decrypt));
- SetDelegateInfo(new SoftFallbackEncrypt(SoftFallback.Encrypt));
- SetDelegateInfo(new SoftFallbackFixedRotate(SoftFallback.FixedRotate));
- SetDelegateInfo(new SoftFallbackHashChoose(SoftFallback.HashChoose));
- SetDelegateInfo(new SoftFallbackHashLower(SoftFallback.HashLower));
- SetDelegateInfo(new SoftFallbackHashMajority(SoftFallback.HashMajority));
- SetDelegateInfo(new SoftFallbackHashParity(SoftFallback.HashParity));
- SetDelegateInfo(new SoftFallbackHashUpper(SoftFallback.HashUpper));
- SetDelegateInfo(new SoftFallbackInverseMixColumns(SoftFallback.InverseMixColumns));
- SetDelegateInfo(new SoftFallbackMixColumns(SoftFallback.MixColumns));
- SetDelegateInfo(new SoftFallbackPolynomialMult64_128(SoftFallback.PolynomialMult64_128));
- SetDelegateInfo(new SoftFallbackSatF32ToS32(SoftFallback.SatF32ToS32));
- SetDelegateInfo(new SoftFallbackSatF32ToS64(SoftFallback.SatF32ToS64));
- SetDelegateInfo(new SoftFallbackSatF32ToU32(SoftFallback.SatF32ToU32));
- SetDelegateInfo(new SoftFallbackSatF32ToU64(SoftFallback.SatF32ToU64));
- SetDelegateInfo(new SoftFallbackSatF64ToS32(SoftFallback.SatF64ToS32));
- SetDelegateInfo(new SoftFallbackSatF64ToS64(SoftFallback.SatF64ToS64));
- SetDelegateInfo(new SoftFallbackSatF64ToU32(SoftFallback.SatF64ToU32));
- SetDelegateInfo(new SoftFallbackSatF64ToU64(SoftFallback.SatF64ToU64));
- SetDelegateInfo(new SoftFallbackSha1SchedulePart1(SoftFallback.Sha1SchedulePart1));
- SetDelegateInfo(new SoftFallbackSha1SchedulePart2(SoftFallback.Sha1SchedulePart2));
- SetDelegateInfo(new SoftFallbackSha256SchedulePart1(SoftFallback.Sha256SchedulePart1));
- SetDelegateInfo(new SoftFallbackSha256SchedulePart2(SoftFallback.Sha256SchedulePart2));
- SetDelegateInfo(new SoftFallbackSignedShrImm64(SoftFallback.SignedShrImm64));
- SetDelegateInfo(new SoftFallbackTbl1(SoftFallback.Tbl1));
- SetDelegateInfo(new SoftFallbackTbl2(SoftFallback.Tbl2));
- SetDelegateInfo(new SoftFallbackTbl3(SoftFallback.Tbl3));
- SetDelegateInfo(new SoftFallbackTbl4(SoftFallback.Tbl4));
- SetDelegateInfo(new SoftFallbackTbx1(SoftFallback.Tbx1));
- SetDelegateInfo(new SoftFallbackTbx2(SoftFallback.Tbx2));
- SetDelegateInfo(new SoftFallbackTbx3(SoftFallback.Tbx3));
- SetDelegateInfo(new SoftFallbackTbx4(SoftFallback.Tbx4));
- SetDelegateInfo(new SoftFallbackUnsignedShrImm64(SoftFallback.UnsignedShrImm64));
+ var dlgSoftFallbackCountLeadingSigns = new SoftFallbackCountLeadingSigns(SoftFallback.CountLeadingSigns);
+ var dlgSoftFallbackCountLeadingZeros = new SoftFallbackCountLeadingZeros(SoftFallback.CountLeadingZeros);
+ var dlgSoftFallbackCrc32b = new SoftFallbackCrc32b(SoftFallback.Crc32b);
+ var dlgSoftFallbackCrc32cb = new SoftFallbackCrc32cb(SoftFallback.Crc32cb);
+ var dlgSoftFallbackCrc32ch = new SoftFallbackCrc32ch(SoftFallback.Crc32ch);
+ var dlgSoftFallbackCrc32cw = new SoftFallbackCrc32cw(SoftFallback.Crc32cw);
+ var dlgSoftFallbackCrc32cx = new SoftFallbackCrc32cx(SoftFallback.Crc32cx);
+ var dlgSoftFallbackCrc32h = new SoftFallbackCrc32h(SoftFallback.Crc32h);
+ var dlgSoftFallbackCrc32w = new SoftFallbackCrc32w(SoftFallback.Crc32w);
+ var dlgSoftFallbackCrc32x = new SoftFallbackCrc32x(SoftFallback.Crc32x);
+ var dlgSoftFallbackDecrypt = new SoftFallbackDecrypt(SoftFallback.Decrypt);
+ var dlgSoftFallbackEncrypt = new SoftFallbackEncrypt(SoftFallback.Encrypt);
+ var dlgSoftFallbackFixedRotate = new SoftFallbackFixedRotate(SoftFallback.FixedRotate);
+ var dlgSoftFallbackHashChoose = new SoftFallbackHashChoose(SoftFallback.HashChoose);
+ var dlgSoftFallbackHashLower = new SoftFallbackHashLower(SoftFallback.HashLower);
+ var dlgSoftFallbackHashMajority = new SoftFallbackHashMajority(SoftFallback.HashMajority);
+ var dlgSoftFallbackHashParity = new SoftFallbackHashParity(SoftFallback.HashParity);
+ var dlgSoftFallbackHashUpper = new SoftFallbackHashUpper(SoftFallback.HashUpper);
+ var dlgSoftFallbackInverseMixColumns = new SoftFallbackInverseMixColumns(SoftFallback.InverseMixColumns);
+ var dlgSoftFallbackMixColumns = new SoftFallbackMixColumns(SoftFallback.MixColumns);
+ var dlgSoftFallbackPolynomialMult64_128 = new SoftFallbackPolynomialMult64_128(SoftFallback.PolynomialMult64_128);
+ var dlgSoftFallbackSatF32ToS32 = new SoftFallbackSatF32ToS32(SoftFallback.SatF32ToS32);
+ var dlgSoftFallbackSatF32ToS64 = new SoftFallbackSatF32ToS64(SoftFallback.SatF32ToS64);
+ var dlgSoftFallbackSatF32ToU32 = new SoftFallbackSatF32ToU32(SoftFallback.SatF32ToU32);
+ var dlgSoftFallbackSatF32ToU64 = new SoftFallbackSatF32ToU64(SoftFallback.SatF32ToU64);
+ var dlgSoftFallbackSatF64ToS32 = new SoftFallbackSatF64ToS32(SoftFallback.SatF64ToS32);
+ var dlgSoftFallbackSatF64ToS64 = new SoftFallbackSatF64ToS64(SoftFallback.SatF64ToS64);
+ var dlgSoftFallbackSatF64ToU32 = new SoftFallbackSatF64ToU32(SoftFallback.SatF64ToU32);
+ var dlgSoftFallbackSatF64ToU64 = new SoftFallbackSatF64ToU64(SoftFallback.SatF64ToU64);
+ var dlgSoftFallbackSha1SchedulePart1 = new SoftFallbackSha1SchedulePart1(SoftFallback.Sha1SchedulePart1);
+ var dlgSoftFallbackSha1SchedulePart2 = new SoftFallbackSha1SchedulePart2(SoftFallback.Sha1SchedulePart2);
+ var dlgSoftFallbackSha256SchedulePart1 = new SoftFallbackSha256SchedulePart1(SoftFallback.Sha256SchedulePart1);
+ var dlgSoftFallbackSha256SchedulePart2 = new SoftFallbackSha256SchedulePart2(SoftFallback.Sha256SchedulePart2);
+ var dlgSoftFallbackSignedShrImm64 = new SoftFallbackSignedShrImm64(SoftFallback.SignedShrImm64);
+ var dlgSoftFallbackTbl1 = new SoftFallbackTbl1(SoftFallback.Tbl1);
+ var dlgSoftFallbackTbl2 = new SoftFallbackTbl2(SoftFallback.Tbl2);
+ var dlgSoftFallbackTbl3 = new SoftFallbackTbl3(SoftFallback.Tbl3);
+ var dlgSoftFallbackTbl4 = new SoftFallbackTbl4(SoftFallback.Tbl4);
+ var dlgSoftFallbackTbx1 = new SoftFallbackTbx1(SoftFallback.Tbx1);
+ var dlgSoftFallbackTbx2 = new SoftFallbackTbx2(SoftFallback.Tbx2);
+ var dlgSoftFallbackTbx3 = new SoftFallbackTbx3(SoftFallback.Tbx3);
+ var dlgSoftFallbackTbx4 = new SoftFallbackTbx4(SoftFallback.Tbx4);
+ var dlgSoftFallbackUnsignedShrImm64 = new SoftFallbackUnsignedShrImm64(SoftFallback.UnsignedShrImm64);
- SetDelegateInfo(new SoftFloat16_32FPConvert(SoftFloat16_32.FPConvert));
- SetDelegateInfo(new SoftFloat16_64FPConvert(SoftFloat16_64.FPConvert));
+ var dlgSoftFloat16_32FPConvert = new SoftFloat16_32FPConvert(SoftFloat16_32.FPConvert);
+ var dlgSoftFloat16_64FPConvert = new SoftFloat16_64FPConvert(SoftFloat16_64.FPConvert);
- SetDelegateInfo(new SoftFloat32FPAdd(SoftFloat32.FPAdd));
- SetDelegateInfo(new SoftFloat32FPAddFpscr(SoftFloat32.FPAddFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat32FPCompare(SoftFloat32.FPCompare));
- SetDelegateInfo(new SoftFloat32FPCompareEQ(SoftFloat32.FPCompareEQ));
- SetDelegateInfo(new SoftFloat32FPCompareEQFpscr(SoftFloat32.FPCompareEQFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat32FPCompareGE(SoftFloat32.FPCompareGE));
- SetDelegateInfo(new SoftFloat32FPCompareGEFpscr(SoftFloat32.FPCompareGEFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat32FPCompareGT(SoftFloat32.FPCompareGT));
- SetDelegateInfo(new SoftFloat32FPCompareGTFpscr(SoftFloat32.FPCompareGTFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat32FPCompareLE(SoftFloat32.FPCompareLE));
- SetDelegateInfo(new SoftFloat32FPCompareLEFpscr(SoftFloat32.FPCompareLEFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat32FPCompareLT(SoftFloat32.FPCompareLT));
- SetDelegateInfo(new SoftFloat32FPCompareLTFpscr(SoftFloat32.FPCompareLTFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat32FPDiv(SoftFloat32.FPDiv));
- SetDelegateInfo(new SoftFloat32FPMax(SoftFloat32.FPMax));
- SetDelegateInfo(new SoftFloat32FPMaxFpscr(SoftFloat32.FPMaxFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat32FPMaxNum(SoftFloat32.FPMaxNum));
- SetDelegateInfo(new SoftFloat32FPMaxNumFpscr(SoftFloat32.FPMaxNumFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat32FPMin(SoftFloat32.FPMin));
- SetDelegateInfo(new SoftFloat32FPMinFpscr(SoftFloat32.FPMinFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat32FPMinNum(SoftFloat32.FPMinNum));
- SetDelegateInfo(new SoftFloat32FPMinNumFpscr(SoftFloat32.FPMinNumFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat32FPMul(SoftFloat32.FPMul));
- SetDelegateInfo(new SoftFloat32FPMulFpscr(SoftFloat32.FPMulFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat32FPMulAdd(SoftFloat32.FPMulAdd));
- SetDelegateInfo(new SoftFloat32FPMulAddFpscr(SoftFloat32.FPMulAddFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat32FPMulSub(SoftFloat32.FPMulSub));
- SetDelegateInfo(new SoftFloat32FPMulSubFpscr(SoftFloat32.FPMulSubFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat32FPMulX(SoftFloat32.FPMulX));
- SetDelegateInfo(new SoftFloat32FPNegMulAdd(SoftFloat32.FPNegMulAdd));
- SetDelegateInfo(new SoftFloat32FPNegMulSub(SoftFloat32.FPNegMulSub));
- SetDelegateInfo(new SoftFloat32FPRecipEstimate(SoftFloat32.FPRecipEstimate));
- SetDelegateInfo(new SoftFloat32FPRecipEstimateFpscr(SoftFloat32.FPRecipEstimateFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat32FPRecipStep(SoftFloat32.FPRecipStep)); // A32 only.
- SetDelegateInfo(new SoftFloat32FPRecipStepFused(SoftFloat32.FPRecipStepFused));
- SetDelegateInfo(new SoftFloat32FPRecpX(SoftFloat32.FPRecpX));
- SetDelegateInfo(new SoftFloat32FPRSqrtEstimate(SoftFloat32.FPRSqrtEstimate));
- SetDelegateInfo(new SoftFloat32FPRSqrtEstimateFpscr(SoftFloat32.FPRSqrtEstimateFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat32FPRSqrtStep(SoftFloat32.FPRSqrtStep)); // A32 only.
- SetDelegateInfo(new SoftFloat32FPRSqrtStepFused(SoftFloat32.FPRSqrtStepFused));
- SetDelegateInfo(new SoftFloat32FPSqrt(SoftFloat32.FPSqrt));
- SetDelegateInfo(new SoftFloat32FPSub(SoftFloat32.FPSub));
+ var dlgSoftFloat32FPAdd = new SoftFloat32FPAdd(SoftFloat32.FPAdd);
+ var dlgSoftFloat32FPAddFpscr = new SoftFloat32FPAddFpscr(SoftFloat32.FPAddFpscr); // A32 only.
+ var dlgSoftFloat32FPCompare = new SoftFloat32FPCompare(SoftFloat32.FPCompare);
+ var dlgSoftFloat32FPCompareEQ = new SoftFloat32FPCompareEQ(SoftFloat32.FPCompareEQ);
+ var dlgSoftFloat32FPCompareEQFpscr = new SoftFloat32FPCompareEQFpscr(SoftFloat32.FPCompareEQFpscr); // A32 only.
+ var dlgSoftFloat32FPCompareGE = new SoftFloat32FPCompareGE(SoftFloat32.FPCompareGE);
+ var dlgSoftFloat32FPCompareGEFpscr = new SoftFloat32FPCompareGEFpscr(SoftFloat32.FPCompareGEFpscr); // A32 only.
+ var dlgSoftFloat32FPCompareGT = new SoftFloat32FPCompareGT(SoftFloat32.FPCompareGT);
+ var dlgSoftFloat32FPCompareGTFpscr = new SoftFloat32FPCompareGTFpscr(SoftFloat32.FPCompareGTFpscr); // A32 only.
+ var dlgSoftFloat32FPCompareLE = new SoftFloat32FPCompareLE(SoftFloat32.FPCompareLE);
+ var dlgSoftFloat32FPCompareLEFpscr = new SoftFloat32FPCompareLEFpscr(SoftFloat32.FPCompareLEFpscr); // A32 only.
+ var dlgSoftFloat32FPCompareLT = new SoftFloat32FPCompareLT(SoftFloat32.FPCompareLT);
+ var dlgSoftFloat32FPCompareLTFpscr = new SoftFloat32FPCompareLTFpscr(SoftFloat32.FPCompareLTFpscr); // A32 only.
+ var dlgSoftFloat32FPDiv = new SoftFloat32FPDiv(SoftFloat32.FPDiv);
+ var dlgSoftFloat32FPMax = new SoftFloat32FPMax(SoftFloat32.FPMax);
+ var dlgSoftFloat32FPMaxFpscr = new SoftFloat32FPMaxFpscr(SoftFloat32.FPMaxFpscr); // A32 only.
+ var dlgSoftFloat32FPMaxNum = new SoftFloat32FPMaxNum(SoftFloat32.FPMaxNum);
+ var dlgSoftFloat32FPMaxNumFpscr = new SoftFloat32FPMaxNumFpscr(SoftFloat32.FPMaxNumFpscr); // A32 only.
+ var dlgSoftFloat32FPMin = new SoftFloat32FPMin(SoftFloat32.FPMin);
+ var dlgSoftFloat32FPMinFpscr = new SoftFloat32FPMinFpscr(SoftFloat32.FPMinFpscr); // A32 only.
+ var dlgSoftFloat32FPMinNum = new SoftFloat32FPMinNum(SoftFloat32.FPMinNum);
+ var dlgSoftFloat32FPMinNumFpscr = new SoftFloat32FPMinNumFpscr(SoftFloat32.FPMinNumFpscr); // A32 only.
+ var dlgSoftFloat32FPMul = new SoftFloat32FPMul(SoftFloat32.FPMul);
+ var dlgSoftFloat32FPMulFpscr = new SoftFloat32FPMulFpscr(SoftFloat32.FPMulFpscr); // A32 only.
+ var dlgSoftFloat32FPMulAdd = new SoftFloat32FPMulAdd(SoftFloat32.FPMulAdd);
+ var dlgSoftFloat32FPMulAddFpscr = new SoftFloat32FPMulAddFpscr(SoftFloat32.FPMulAddFpscr); // A32 only.
+ var dlgSoftFloat32FPMulSub = new SoftFloat32FPMulSub(SoftFloat32.FPMulSub);
+ var dlgSoftFloat32FPMulSubFpscr = new SoftFloat32FPMulSubFpscr(SoftFloat32.FPMulSubFpscr); // A32 only.
+ var dlgSoftFloat32FPMulX = new SoftFloat32FPMulX(SoftFloat32.FPMulX);
+ var dlgSoftFloat32FPNegMulAdd = new SoftFloat32FPNegMulAdd(SoftFloat32.FPNegMulAdd);
+ var dlgSoftFloat32FPNegMulSub = new SoftFloat32FPNegMulSub(SoftFloat32.FPNegMulSub);
+ var dlgSoftFloat32FPRecipEstimate = new SoftFloat32FPRecipEstimate(SoftFloat32.FPRecipEstimate);
+ var dlgSoftFloat32FPRecipEstimateFpscr = new SoftFloat32FPRecipEstimateFpscr(SoftFloat32.FPRecipEstimateFpscr); // A32 only.
+ var dlgSoftFloat32FPRecipStep = new SoftFloat32FPRecipStep(SoftFloat32.FPRecipStep); // A32 only.
+ var dlgSoftFloat32FPRecipStepFused = new SoftFloat32FPRecipStepFused(SoftFloat32.FPRecipStepFused);
+ var dlgSoftFloat32FPRecpX = new SoftFloat32FPRecpX(SoftFloat32.FPRecpX);
+ var dlgSoftFloat32FPRSqrtEstimate = new SoftFloat32FPRSqrtEstimate(SoftFloat32.FPRSqrtEstimate);
+ var dlgSoftFloat32FPRSqrtEstimateFpscr = new SoftFloat32FPRSqrtEstimateFpscr(SoftFloat32.FPRSqrtEstimateFpscr); // A32 only.
+ var dlgSoftFloat32FPRSqrtStep = new SoftFloat32FPRSqrtStep(SoftFloat32.FPRSqrtStep); // A32 only.
+ var dlgSoftFloat32FPRSqrtStepFused = new SoftFloat32FPRSqrtStepFused(SoftFloat32.FPRSqrtStepFused);
+ var dlgSoftFloat32FPSqrt = new SoftFloat32FPSqrt(SoftFloat32.FPSqrt);
+ var dlgSoftFloat32FPSub = new SoftFloat32FPSub(SoftFloat32.FPSub);
- SetDelegateInfo(new SoftFloat32_16FPConvert(SoftFloat32_16.FPConvert));
+ var dlgSoftFloat32_16FPConvert = new SoftFloat32_16FPConvert(SoftFloat32_16.FPConvert);
- SetDelegateInfo(new SoftFloat64FPAdd(SoftFloat64.FPAdd));
- SetDelegateInfo(new SoftFloat64FPAddFpscr(SoftFloat64.FPAddFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat64FPCompare(SoftFloat64.FPCompare));
- SetDelegateInfo(new SoftFloat64FPCompareEQ(SoftFloat64.FPCompareEQ));
- SetDelegateInfo(new SoftFloat64FPCompareEQFpscr(SoftFloat64.FPCompareEQFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat64FPCompareGE(SoftFloat64.FPCompareGE));
- SetDelegateInfo(new SoftFloat64FPCompareGEFpscr(SoftFloat64.FPCompareGEFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat64FPCompareGT(SoftFloat64.FPCompareGT));
- SetDelegateInfo(new SoftFloat64FPCompareGTFpscr(SoftFloat64.FPCompareGTFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat64FPCompareLE(SoftFloat64.FPCompareLE));
- SetDelegateInfo(new SoftFloat64FPCompareLEFpscr(SoftFloat64.FPCompareLEFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat64FPCompareLT(SoftFloat64.FPCompareLT));
- SetDelegateInfo(new SoftFloat64FPCompareLTFpscr(SoftFloat64.FPCompareLTFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat64FPDiv(SoftFloat64.FPDiv));
- SetDelegateInfo(new SoftFloat64FPMax(SoftFloat64.FPMax));
- SetDelegateInfo(new SoftFloat64FPMaxFpscr(SoftFloat64.FPMaxFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat64FPMaxNum(SoftFloat64.FPMaxNum));
- SetDelegateInfo(new SoftFloat64FPMaxNumFpscr(SoftFloat64.FPMaxNumFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat64FPMin(SoftFloat64.FPMin));
- SetDelegateInfo(new SoftFloat64FPMinFpscr(SoftFloat64.FPMinFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat64FPMinNum(SoftFloat64.FPMinNum));
- SetDelegateInfo(new SoftFloat64FPMinNumFpscr(SoftFloat64.FPMinNumFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat64FPMul(SoftFloat64.FPMul));
- SetDelegateInfo(new SoftFloat64FPMulFpscr(SoftFloat64.FPMulFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat64FPMulAdd(SoftFloat64.FPMulAdd));
- SetDelegateInfo(new SoftFloat64FPMulAddFpscr(SoftFloat64.FPMulAddFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat64FPMulSub(SoftFloat64.FPMulSub));
- SetDelegateInfo(new SoftFloat64FPMulSubFpscr(SoftFloat64.FPMulSubFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat64FPMulX(SoftFloat64.FPMulX));
- SetDelegateInfo(new SoftFloat64FPNegMulAdd(SoftFloat64.FPNegMulAdd));
- SetDelegateInfo(new SoftFloat64FPNegMulSub(SoftFloat64.FPNegMulSub));
- SetDelegateInfo(new SoftFloat64FPRecipEstimate(SoftFloat64.FPRecipEstimate));
- SetDelegateInfo(new SoftFloat64FPRecipEstimateFpscr(SoftFloat64.FPRecipEstimateFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat64FPRecipStep(SoftFloat64.FPRecipStep)); // A32 only.
- SetDelegateInfo(new SoftFloat64FPRecipStepFused(SoftFloat64.FPRecipStepFused));
- SetDelegateInfo(new SoftFloat64FPRecpX(SoftFloat64.FPRecpX));
- SetDelegateInfo(new SoftFloat64FPRSqrtEstimate(SoftFloat64.FPRSqrtEstimate));
- SetDelegateInfo(new SoftFloat64FPRSqrtEstimateFpscr(SoftFloat64.FPRSqrtEstimateFpscr)); // A32 only.
- SetDelegateInfo(new SoftFloat64FPRSqrtStep(SoftFloat64.FPRSqrtStep)); // A32 only.
- SetDelegateInfo(new SoftFloat64FPRSqrtStepFused(SoftFloat64.FPRSqrtStepFused));
- SetDelegateInfo(new SoftFloat64FPSqrt(SoftFloat64.FPSqrt));
- SetDelegateInfo(new SoftFloat64FPSub(SoftFloat64.FPSub));
+ var dlgSoftFloat64FPAdd = new SoftFloat64FPAdd(SoftFloat64.FPAdd);
+ var dlgSoftFloat64FPAddFpscr = new SoftFloat64FPAddFpscr(SoftFloat64.FPAddFpscr); // A32 only.
+ var dlgSoftFloat64FPCompare = new SoftFloat64FPCompare(SoftFloat64.FPCompare);
+ var dlgSoftFloat64FPCompareEQ = new SoftFloat64FPCompareEQ(SoftFloat64.FPCompareEQ);
+ var dlgSoftFloat64FPCompareEQFpscr = new SoftFloat64FPCompareEQFpscr(SoftFloat64.FPCompareEQFpscr); // A32 only.
+ var dlgSoftFloat64FPCompareGE = new SoftFloat64FPCompareGE(SoftFloat64.FPCompareGE);
+ var dlgSoftFloat64FPCompareGEFpscr = new SoftFloat64FPCompareGEFpscr(SoftFloat64.FPCompareGEFpscr); // A32 only.
+ var dlgSoftFloat64FPCompareGT = new SoftFloat64FPCompareGT(SoftFloat64.FPCompareGT);
+ var dlgSoftFloat64FPCompareGTFpscr = new SoftFloat64FPCompareGTFpscr(SoftFloat64.FPCompareGTFpscr); // A32 only.
+ var dlgSoftFloat64FPCompareLE = new SoftFloat64FPCompareLE(SoftFloat64.FPCompareLE);
+ var dlgSoftFloat64FPCompareLEFpscr = new SoftFloat64FPCompareLEFpscr(SoftFloat64.FPCompareLEFpscr); // A32 only.
+ var dlgSoftFloat64FPCompareLT = new SoftFloat64FPCompareLT(SoftFloat64.FPCompareLT);
+ var dlgSoftFloat64FPCompareLTFpscr = new SoftFloat64FPCompareLTFpscr(SoftFloat64.FPCompareLTFpscr); // A32 only.
+ var dlgSoftFloat64FPDiv = new SoftFloat64FPDiv(SoftFloat64.FPDiv);
+ var dlgSoftFloat64FPMax = new SoftFloat64FPMax(SoftFloat64.FPMax);
+ var dlgSoftFloat64FPMaxFpscr = new SoftFloat64FPMaxFpscr(SoftFloat64.FPMaxFpscr); // A32 only.
+ var dlgSoftFloat64FPMaxNum = new SoftFloat64FPMaxNum(SoftFloat64.FPMaxNum);
+ var dlgSoftFloat64FPMaxNumFpscr = new SoftFloat64FPMaxNumFpscr(SoftFloat64.FPMaxNumFpscr); // A32 only.
+ var dlgSoftFloat64FPMin = new SoftFloat64FPMin(SoftFloat64.FPMin);
+ var dlgSoftFloat64FPMinFpscr = new SoftFloat64FPMinFpscr(SoftFloat64.FPMinFpscr); // A32 only.
+ var dlgSoftFloat64FPMinNum = new SoftFloat64FPMinNum(SoftFloat64.FPMinNum);
+ var dlgSoftFloat64FPMinNumFpscr = new SoftFloat64FPMinNumFpscr(SoftFloat64.FPMinNumFpscr); // A32 only.
+ var dlgSoftFloat64FPMul = new SoftFloat64FPMul(SoftFloat64.FPMul);
+ var dlgSoftFloat64FPMulFpscr = new SoftFloat64FPMulFpscr(SoftFloat64.FPMulFpscr); // A32 only.
+ var dlgSoftFloat64FPMulAdd = new SoftFloat64FPMulAdd(SoftFloat64.FPMulAdd);
+ var dlgSoftFloat64FPMulAddFpscr = new SoftFloat64FPMulAddFpscr(SoftFloat64.FPMulAddFpscr); // A32 only.
+ var dlgSoftFloat64FPMulSub = new SoftFloat64FPMulSub(SoftFloat64.FPMulSub);
+ var dlgSoftFloat64FPMulSubFpscr = new SoftFloat64FPMulSubFpscr(SoftFloat64.FPMulSubFpscr); // A32 only.
+ var dlgSoftFloat64FPMulX = new SoftFloat64FPMulX(SoftFloat64.FPMulX);
+ var dlgSoftFloat64FPNegMulAdd = new SoftFloat64FPNegMulAdd(SoftFloat64.FPNegMulAdd);
+ var dlgSoftFloat64FPNegMulSub = new SoftFloat64FPNegMulSub(SoftFloat64.FPNegMulSub);
+ var dlgSoftFloat64FPRecipEstimate = new SoftFloat64FPRecipEstimate(SoftFloat64.FPRecipEstimate);
+ var dlgSoftFloat64FPRecipEstimateFpscr = new SoftFloat64FPRecipEstimateFpscr(SoftFloat64.FPRecipEstimateFpscr); // A32 only.
+ var dlgSoftFloat64FPRecipStep = new SoftFloat64FPRecipStep(SoftFloat64.FPRecipStep); // A32 only.
+ var dlgSoftFloat64FPRecipStepFused = new SoftFloat64FPRecipStepFused(SoftFloat64.FPRecipStepFused);
+ var dlgSoftFloat64FPRecpX = new SoftFloat64FPRecpX(SoftFloat64.FPRecpX);
+ var dlgSoftFloat64FPRSqrtEstimate = new SoftFloat64FPRSqrtEstimate(SoftFloat64.FPRSqrtEstimate);
+ var dlgSoftFloat64FPRSqrtEstimateFpscr = new SoftFloat64FPRSqrtEstimateFpscr(SoftFloat64.FPRSqrtEstimateFpscr); // A32 only.
+ var dlgSoftFloat64FPRSqrtStep = new SoftFloat64FPRSqrtStep(SoftFloat64.FPRSqrtStep); // A32 only.
+ var dlgSoftFloat64FPRSqrtStepFused = new SoftFloat64FPRSqrtStepFused(SoftFloat64.FPRSqrtStepFused);
+ var dlgSoftFloat64FPSqrt = new SoftFloat64FPSqrt(SoftFloat64.FPSqrt);
+ var dlgSoftFloat64FPSub = new SoftFloat64FPSub(SoftFloat64.FPSub);
- SetDelegateInfo(new SoftFloat64_16FPConvert(SoftFloat64_16.FPConvert));
+ var dlgSoftFloat64_16FPConvert = new SoftFloat64_16FPConvert(SoftFloat64_16.FPConvert);
+
+ SetDelegateInfo(dlgMathAbs, Marshal.GetFunctionPointerForDelegate(dlgMathAbs));
+ SetDelegateInfo(dlgMathCeiling, Marshal.GetFunctionPointerForDelegate(dlgMathCeiling));
+ SetDelegateInfo(dlgMathFloor, Marshal.GetFunctionPointerForDelegate(dlgMathFloor));
+ SetDelegateInfo(dlgMathRound, Marshal.GetFunctionPointerForDelegate(dlgMathRound));
+ SetDelegateInfo(dlgMathTruncate, Marshal.GetFunctionPointerForDelegate(dlgMathTruncate));
+
+ SetDelegateInfo(dlgMathFAbs, Marshal.GetFunctionPointerForDelegate(dlgMathFAbs));
+ SetDelegateInfo(dlgMathFCeiling, Marshal.GetFunctionPointerForDelegate(dlgMathFCeiling));
+ SetDelegateInfo(dlgMathFFloor, Marshal.GetFunctionPointerForDelegate(dlgMathFFloor));
+ SetDelegateInfo(dlgMathFRound, Marshal.GetFunctionPointerForDelegate(dlgMathFRound));
+ SetDelegateInfo(dlgMathFTruncate, Marshal.GetFunctionPointerForDelegate(dlgMathFTruncate));
+
+ SetDelegateInfo(dlgNativeInterfaceBreak, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceBreak));
+ SetDelegateInfo(dlgNativeInterfaceCheckSynchronization, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceCheckSynchronization));
+ SetDelegateInfo(dlgNativeInterfaceEnqueueForRejit, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceEnqueueForRejit));
+ SetDelegateInfo(dlgNativeInterfaceGetCntfrqEl0, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceGetCntfrqEl0));
+ SetDelegateInfo(dlgNativeInterfaceGetCntpctEl0, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceGetCntpctEl0));
+ SetDelegateInfo(dlgNativeInterfaceGetCntvctEl0, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceGetCntvctEl0));
+ SetDelegateInfo(dlgNativeInterfaceGetCtrEl0, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceGetCtrEl0));
+ SetDelegateInfo(dlgNativeInterfaceGetDczidEl0, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceGetDczidEl0));
+ SetDelegateInfo(dlgNativeInterfaceGetFunctionAddress, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceGetFunctionAddress));
+ SetDelegateInfo(dlgNativeInterfaceInvalidateCacheLine, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceInvalidateCacheLine));
+ SetDelegateInfo(dlgNativeInterfaceReadByte, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceReadByte));
+ SetDelegateInfo(dlgNativeInterfaceReadUInt16, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceReadUInt16));
+ SetDelegateInfo(dlgNativeInterfaceReadUInt32, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceReadUInt32));
+ SetDelegateInfo(dlgNativeInterfaceReadUInt64, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceReadUInt64));
+ SetDelegateInfo(dlgNativeInterfaceReadVector128, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceReadVector128));
+ SetDelegateInfo(dlgNativeInterfaceSignalMemoryTracking, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceSignalMemoryTracking));
+ SetDelegateInfo(dlgNativeInterfaceSupervisorCall, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceSupervisorCall));
+ SetDelegateInfo(dlgNativeInterfaceThrowInvalidMemoryAccess, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceThrowInvalidMemoryAccess));
+ SetDelegateInfo(dlgNativeInterfaceUndefined, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceUndefined));
+ SetDelegateInfo(dlgNativeInterfaceWriteByte, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceWriteByte));
+ SetDelegateInfo(dlgNativeInterfaceWriteUInt16, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceWriteUInt16));
+ SetDelegateInfo(dlgNativeInterfaceWriteUInt32, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceWriteUInt32));
+ SetDelegateInfo(dlgNativeInterfaceWriteUInt64, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceWriteUInt64));
+ SetDelegateInfo(dlgNativeInterfaceWriteVector128, Marshal.GetFunctionPointerForDelegate(dlgNativeInterfaceWriteVector128));
+
+ SetDelegateInfo(dlgSoftFallbackCountLeadingSigns, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackCountLeadingSigns));
+ SetDelegateInfo(dlgSoftFallbackCountLeadingZeros, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackCountLeadingZeros));
+ SetDelegateInfo(dlgSoftFallbackCrc32b, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackCrc32b));
+ SetDelegateInfo(dlgSoftFallbackCrc32cb, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackCrc32cb));
+ SetDelegateInfo(dlgSoftFallbackCrc32ch, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackCrc32ch));
+ SetDelegateInfo(dlgSoftFallbackCrc32cw, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackCrc32cw));
+ SetDelegateInfo(dlgSoftFallbackCrc32cx, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackCrc32cx));
+ SetDelegateInfo(dlgSoftFallbackCrc32h, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackCrc32h));
+ SetDelegateInfo(dlgSoftFallbackCrc32w, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackCrc32w));
+ SetDelegateInfo(dlgSoftFallbackCrc32x, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackCrc32x));
+ SetDelegateInfo(dlgSoftFallbackDecrypt, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackDecrypt));
+ SetDelegateInfo(dlgSoftFallbackEncrypt, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackEncrypt));
+ SetDelegateInfo(dlgSoftFallbackFixedRotate, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackFixedRotate));
+ SetDelegateInfo(dlgSoftFallbackHashChoose, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackHashChoose));
+ SetDelegateInfo(dlgSoftFallbackHashLower, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackHashLower));
+ SetDelegateInfo(dlgSoftFallbackHashMajority, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackHashMajority));
+ SetDelegateInfo(dlgSoftFallbackHashParity, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackHashParity));
+ SetDelegateInfo(dlgSoftFallbackHashUpper, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackHashUpper));
+ SetDelegateInfo(dlgSoftFallbackInverseMixColumns, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackInverseMixColumns));
+ SetDelegateInfo(dlgSoftFallbackMixColumns, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackMixColumns));
+ SetDelegateInfo(dlgSoftFallbackPolynomialMult64_128, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackPolynomialMult64_128));
+ SetDelegateInfo(dlgSoftFallbackSatF32ToS32, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSatF32ToS32));
+ SetDelegateInfo(dlgSoftFallbackSatF32ToS64, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSatF32ToS64));
+ SetDelegateInfo(dlgSoftFallbackSatF32ToU32, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSatF32ToU32));
+ SetDelegateInfo(dlgSoftFallbackSatF32ToU64, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSatF32ToU64));
+ SetDelegateInfo(dlgSoftFallbackSatF64ToS32, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSatF64ToS32));
+ SetDelegateInfo(dlgSoftFallbackSatF64ToS64, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSatF64ToS64));
+ SetDelegateInfo(dlgSoftFallbackSatF64ToU32, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSatF64ToU32));
+ SetDelegateInfo(dlgSoftFallbackSatF64ToU64, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSatF64ToU64));
+ SetDelegateInfo(dlgSoftFallbackSha1SchedulePart1, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSha1SchedulePart1));
+ SetDelegateInfo(dlgSoftFallbackSha1SchedulePart2, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSha1SchedulePart2));
+ SetDelegateInfo(dlgSoftFallbackSha256SchedulePart1, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSha256SchedulePart1));
+ SetDelegateInfo(dlgSoftFallbackSha256SchedulePart2, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSha256SchedulePart2));
+ SetDelegateInfo(dlgSoftFallbackSignedShrImm64, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackSignedShrImm64));
+ SetDelegateInfo(dlgSoftFallbackTbl1, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackTbl1));
+ SetDelegateInfo(dlgSoftFallbackTbl2, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackTbl2));
+ SetDelegateInfo(dlgSoftFallbackTbl3, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackTbl3));
+ SetDelegateInfo(dlgSoftFallbackTbl4, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackTbl4));
+ SetDelegateInfo(dlgSoftFallbackTbx1, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackTbx1));
+ SetDelegateInfo(dlgSoftFallbackTbx2, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackTbx2));
+ SetDelegateInfo(dlgSoftFallbackTbx3, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackTbx3));
+ SetDelegateInfo(dlgSoftFallbackTbx4, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackTbx4));
+ SetDelegateInfo(dlgSoftFallbackUnsignedShrImm64, Marshal.GetFunctionPointerForDelegate(dlgSoftFallbackUnsignedShrImm64));
+
+ SetDelegateInfo(dlgSoftFloat16_32FPConvert, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat16_32FPConvert));
+ SetDelegateInfo(dlgSoftFloat16_64FPConvert, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat16_64FPConvert));
+
+ SetDelegateInfo(dlgSoftFloat32FPAdd, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPAdd));
+ SetDelegateInfo(dlgSoftFloat32FPAddFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPAddFpscr)); // A32 only.
+ SetDelegateInfo(dlgSoftFloat32FPCompare, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPCompare));
+ SetDelegateInfo(dlgSoftFloat32FPCompareEQ, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPCompareEQ));
+ SetDelegateInfo(dlgSoftFloat32FPCompareEQFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPCompareEQFpscr)); // A32 only.
+ SetDelegateInfo(dlgSoftFloat32FPCompareGE, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPCompareGE));
+ SetDelegateInfo(dlgSoftFloat32FPCompareGEFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPCompareGEFpscr)); // A32 only.
+ SetDelegateInfo(dlgSoftFloat32FPCompareGT, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPCompareGT));
+ SetDelegateInfo(dlgSoftFloat32FPCompareGTFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPCompareGTFpscr)); // A32 only.
+ SetDelegateInfo(dlgSoftFloat32FPCompareLE, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPCompareLE));
+ SetDelegateInfo(dlgSoftFloat32FPCompareLEFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPCompareLEFpscr)); // A32 only.
+ SetDelegateInfo(dlgSoftFloat32FPCompareLT, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPCompareLT));
+ SetDelegateInfo(dlgSoftFloat32FPCompareLTFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPCompareLTFpscr)); // A32 only.
+ SetDelegateInfo(dlgSoftFloat32FPDiv, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPDiv));
+ SetDelegateInfo(dlgSoftFloat32FPMax, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMax));
+ SetDelegateInfo(dlgSoftFloat32FPMaxFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMaxFpscr)); // A32 only.
+ SetDelegateInfo(dlgSoftFloat32FPMaxNum, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMaxNum));
+ SetDelegateInfo(dlgSoftFloat32FPMaxNumFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMaxNumFpscr)); // A32 only.
+ SetDelegateInfo(dlgSoftFloat32FPMin, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMin));
+ SetDelegateInfo(dlgSoftFloat32FPMinFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMinFpscr)); // A32 only.
+ SetDelegateInfo(dlgSoftFloat32FPMinNum, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMinNum));
+ SetDelegateInfo(dlgSoftFloat32FPMinNumFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMinNumFpscr)); // A32 only.
+ SetDelegateInfo(dlgSoftFloat32FPMul, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMul));
+ SetDelegateInfo(dlgSoftFloat32FPMulFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMulFpscr)); // A32 only.
+ SetDelegateInfo(dlgSoftFloat32FPMulAdd, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMulAdd));
+ SetDelegateInfo(dlgSoftFloat32FPMulAddFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMulAddFpscr)); // A32 only.
+ SetDelegateInfo(dlgSoftFloat32FPMulSub, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMulSub));
+ SetDelegateInfo(dlgSoftFloat32FPMulSubFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMulSubFpscr)); // A32 only.
+ SetDelegateInfo(dlgSoftFloat32FPMulX, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPMulX));
+ SetDelegateInfo(dlgSoftFloat32FPNegMulAdd, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPNegMulAdd));
+ SetDelegateInfo(dlgSoftFloat32FPNegMulSub, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPNegMulSub));
+ SetDelegateInfo(dlgSoftFloat32FPRecipEstimate, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPRecipEstimate));
+ SetDelegateInfo(dlgSoftFloat32FPRecipEstimateFpscr, Marshal.GetFunctionPointerForDelegate(dlgSoftFloat32FPRecipEstimateFpscr)); // A32 only.
+ SetDelegateInfo(dlgSoftFloat32FPRecipStep, Marshal.GetFunctionPointerForDelegate