Compare commits

...

34 Commits

Author SHA1 Message Date
TSRBerry
3d1a0bf374 [Headless] Fix for not receiving any SDL events on Linux (#4182)
* headless: Fix SDL events not working

* Add nuget.config
2022-12-29 15:09:18 +01:00
Mary-nyan
c20f3fbebd haydn: Add support for PCMFloat, PCM32 and PCM8 conversions (#4100)
* haydn: Add support for PCMFloat, PCM32 and PCM8 conversions

This adds support in the compatibility layer for other sample format
than PCM16.

This should help extends compatibility with soundio on devices that
doesn't expose PCM16.

I ommited PCM24 conversion for now as it's not simplest of all.

* Address TSRBerry's comment

* Address comments

* Fix conversion issue and clean up saturation usage

* Revert saturation changes

* Address gdkchan's comment
2022-12-28 19:34:30 -03:00
Berkan Diler
0d3b82477e Use new ArgumentNullException and ObjectDisposedException throw-helper API (#4163) 2022-12-27 20:27:11 +01:00
riperiperi
470be03c2f GPU: Add fallback when 16-bit formats are not supported (#4108)
* Add conversion for 16 bit RGBA formats (not supported in Rosetta)

* Rebase fix

Rebase fix

* Forgot to remove this

* Fix RGBA16 format conversion

* Add RGBA4 -> RGBA8 conversion

* Handle host stride alignment

* Address Feedback Part 1

* Can't count

* Don't zero out rgb when alpha is 0

* Separate RGBA4 and 5-bit component formats

Not sure of a better way to name them...

* Add A1B5G5R5 conversion

* Put this in the right place.

* Make format naming consistent for capabilities

* Change method names
2022-12-26 15:50:27 -03:00
Hunter
c963b3c804 Added Generic Math to BitUtils (#3929)
* Generic Math Update

Updated Several functions in Ryujinx.Common/Utilities/BitUtils to use generic math

* Updated BitUtil calls

* Removed Whitespace

* Switched decrement

* Fixed changed method calls.

The method calls were originally changed on accident due to me relying too much on intellisense doing stuff for me

* Update Ryujinx.Common/Utilities/BitUtils.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2022-12-26 14:11:05 +00:00
gnisman
a4fdfb5f94 bsd::RecvFrom: verify output buffer size before writing socket address (#4135)
* bsd::RecvFrom: Ryujinx does not verify output buffer size before writing socket address

* Calculate the size of BsdSockAddr

* use bsdSockAddr variable

* Update Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Update Ryujinx.HLE/HOS/Services/Sockets/Bsd/Types/BsdSockAddr.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>

* set errno to ENOMEM in case we can't write the address to memory

* Update Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Update Ryujinx.HLE/HOS/Services/Sockets/Bsd/IClient.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-12-26 13:49:26 +00:00
Berkan Diler
37d27c4c99 Some minor cleanups and optimizations (#4174)
* Replace Array.Clear(x, 0, x.Length) with Array.Clear(x)

* Use DateTime.UnixEpoch field

* Replace SHA256.ComputeHash calls with static SHA256.HashData call

More performant and avoids the need to initialize a SHA256 instance.
2022-12-24 14:30:39 -03:00
gdkchan
f906eb06c2 Implement a software ETC2 texture decoder (#4121)
* Implement a software ETC2 texture decoder

* Fix output size calculation for non-2D textures

* Address PR feedback
2022-12-21 20:39:58 -03:00
gdkchan
219f63ff4e Fix CPU FCVTN instruction implementation (slow path) (#4159)
* Fix CPU FCVTN instruction implementation (slow path)

* PPTC version bump
2022-12-21 23:05:58 +00:00
gdkchan
1cca3e99ab GPU: Force rebind when pool changes (#4129) 2022-12-21 17:35:28 -03:00
Ccl
55a23e5ec8 make UI display correct content in Chinese (#4155) 2022-12-21 17:20:37 -03:00
Mary-nyan
479d1fd8b0 hle: Handle GPU profiler and debugger device path correctly (#4138)
This fix a warning on "慟哭そして…" by handling correctly the debug mode
flag.

When debug mode isn't enabled, opening /dev/nvhost-dbg-gpu or /dev/nvhost-prof-gpu should fail with a not implemented error code.

This implement this behaviour and also define stubbed interfaces for
completness.
2022-12-21 18:23:11 +00:00
gdkchan
cb70e7bb30 Fix DrawArrays vertex buffer size (#4141) 2022-12-21 19:08:12 +01:00
riperiperi
c200a7b7c6 ARMeilleure: Hash _data pointer instead of value for Operand (#4156)
I noticed a weirdly high cost for dictionary accesses from MarkLabel etc. Turns out that the hash code was always the same for labels, so the whole point of having a dictionary was missed and it was putting everything in the same bucket. I made it always hash the _data pointer as that's a good source of identifiable and "random" data.
2022-12-21 02:28:18 +01:00
Emmanuel Hansen
6268170a10 fix sw kbd row collision (#4144) 2022-12-19 15:09:36 -03:00
gdkchan
ee0f9b03a4 Eliminate zero-extension moves in more cases on 32-bit games (#4140)
* Eliminate zero-extension moves in more cases on 32-bit games

* PPTC version bump

* Revert X86Optimizer changes
2022-12-19 14:45:58 -03:00
gdkchan
f93c5f006a Revert "ARMeilleure: Add initial support for AVX512(EVEX encoding) (#3663)" (#4145)
This reverts commit 295fbd0542.
2022-12-18 20:21:10 -03:00
Wunk
295fbd0542 ARMeilleure: Add initial support for AVX512(EVEX encoding) (#3663)
* ARMeilleure: Add AVX512{F,VL,DQ,BW} detection

Add `UseAvx512Ortho` and `UseAvx512OrthoFloat` optimization flags as
short-hands for `F+VL` and `F+VL+DQ`.

* ARMeilleure: Add initial support for EVEX instruction encoding

Does not implement rounding, or exception controls.

* ARMeilleure: Add `X86Vpternlogd`

Accelerates the vector-`Not` instruction.

* ARMeilleure: Add check for `OSXSAVE` for AVX{2,512}

* ARMeilleure: Add check for `XCR0` flags

Add XCR0 register checks for AVX and AVX512F, following the guidelines
from section 14.3 and 15.2 from the Intel Architecture Software
Developer's Manual.

* ARMeilleure: Increment InternalVersion

* ARMeilleure: Remove redundant `ReProtect` and `Dispose`, formatting

* ARMeilleure: Move XCR0 procedure to GetXcr0Eax

* ARMeilleure: Add `XCR0` to `FeatureInfo` structure

* ARMeilleure: Utilize `ReadOnlySpan` for Xcr0 assembly

Avoids an additional allocation

* ARMeilleure: Formatting fixes
2022-12-18 16:46:13 -03:00
Mary-nyan
d7310d7a1c hle: Fix wrong conversion in UserPresence.ToString (#4142)
This fixes an error from #3805 that caused a wrong conversion of ``AppKeyValueStorage`` to string.
As that information isn't really relevant without appropriate parsing, it was removed from ``ToString``.

This should get ride of "bell warning" in Mario Kart 8 when entering Time Trials.
2022-12-18 14:23:19 +00:00
dependabot[bot]
8c50943a2e nuget: bump Microsoft.NET.Test.Sdk from 17.4.0 to 17.4.1 (#4137)
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.4.0 to 17.4.1.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md)
- [Commits](https://github.com/microsoft/vstest/compare/v17.4.0...v17.4.1)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-17 01:19:04 +01:00
gdkchan
ec4cd57ccf Implement another non-indexed draw method on GPU (#4123) 2022-12-16 12:06:38 -03:00
riperiperi
5a085cba0f GPU: Fix layered attachment write (#4131)
Fixes a regression caused by #4003 where the code that writes `_vtgWritesRtLayer` was removed, breaking the crowd in mario strikers.
2022-12-16 09:40:01 -03:00
TSRBerry
1a1d33a018 ava: Fix invisible swkbd applet on Linux (#4130) 2022-12-16 02:40:55 +01:00
Isaac Marovitz
0fbcd630bc Replace DllImport usage with LibraryImport (#4084)
* Replace usage of `DllImport` with `LibraryImport`

* Mark methods as `partial`

* Marshalling

* More `partial` & marshalling

* More `partial` and marshalling

* More partial and marshalling

* Update GdiPlusHelper to LibraryImport

* Unicorn

* More Partial

* Marshal

* Specify EntryPoint

* Specify EntryPoint

* Change GlobalMemoryStatusEx to LibraryImport

* Change RegisterClassEx to LibraryImport

* Define EntryPoints

* Update Ryujinx.Ava/Ui/Controls/Win32NativeInterop.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx.Graphics.Nvdec.FFmpeg/Native/FFmpegApi.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Move return mashal

* Remove calling convention specification

* Remove calling conventions

* Update Ryujinx.Common/SystemInfo/WindowsSystemInfo.cs

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>

* Update Ryujinx/Modules/Updater/Updater.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>

* Update Ryujinx.Ava/Modules/Updater/Updater.cs

Co-authored-by: Mary-nyan <thog@protonmail.com>

Co-authored-by: TSRBerry <20988865+TSRBerry@users.noreply.github.com>
Co-authored-by: Mary-nyan <thog@protonmail.com>
2022-12-15 18:07:31 +01:00
gdkchan
f4d731ae20 Fix NRE when loading Vulkan shader cache with Vertex A shaders (#4124) 2022-12-15 17:52:12 +01:00
Isaac Marovitz
8ac53c66b4 Remove Half Conversion (#4106)
* Remove HalfConversion

* Update `CodeGenVersion`
2022-12-14 21:13:23 -03:00
Georg Lehmann
0f50de72be Vulkan: enable VK_EXT_custom_border_color features (#4116)
* Vulkan: enable VK_EXT_custom_border_color features

radv only create the border color bo if this feature is enabled, so it crashed when creating samplers with custom border colors
Fixes #4072
Fixes #3993

* Address gdkchan's comment

Co-authored-by: Mary <mary@mary.zone>
2022-12-14 20:53:33 -03:00
TSRBerry
df758eddd1 Bsd: Add support for dns_mitm (#4102)
* bsd: Add dns_mitm from Atmosphère

related AMS files:
- https://github.com/Atmosphere-NX/Atmosphere/blob/master/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_resolver_impl.cpp
- https://github.com/Atmosphere-NX/Atmosphere/blob/master/stratosphere/ams_mitm/source/dns_mitm/dnsmitm_host_redirection.cpp

* Remove debug logging and adjust redirect message

* Improve formatting

Co-authored-by: Ac_K <Acoustik666@gmail.com>

* Replace Initialize with instance property

* bsd: dns_mitm - Ignore empty lines

* bsd: Mark _mitmHostEntries as readonly

* bsd: Initialize Aliases when returning IpHostEntry

Fixes NullReferenceException

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2022-12-12 18:04:08 +01:00
Mary-nyan
5f32a8ed94 misc: Update to Ryujinx.Graphics.Nvdec.Dependencies 5.0.1-build13 (#4097)
Fix packaging issues on macOS related to an unsatisfied dependency on libX11
2022-12-12 16:19:46 +01:00
Andrey Sukharev
535fbec675 Use NuGet Central Package Management to manage package versions solution-wise (#4095) 2022-12-12 16:03:10 +01:00
Mary-nyan
6fe88115a3 misc: Some fixes to the updaters (#4092)
This was meant to be only an upgrade of how we set unix permission in
the updater to use .NET 7 new APIs, but I end up finding bugs along the
way.

Changelog:
- Remove direct usage of chmod to use File.SetUnixFileMode.
- Fix command line being broken when updating (#3744) but on
  Ryujinx.Ava.
- Makes Ryujinx.Ava updater fallback to Ryujinx executable if current
  name isn't found.
- Make permission setter function more generic.
2022-12-12 15:17:22 +01:00
&Olga
475fa4d390 Fix "UI" abbreviation being miscapitalized (#4093) 2022-12-12 15:11:55 +01:00
Andrey Sukharev
edf7e628ca Use method overloads that support trimming. Mark some types to be trimming friendly (#4083)
* Use method overloads that support trimming. Mark some types to be trimming friendly

* Use generic version of marshalling method
2022-12-12 15:10:05 +01:00
TSRBerry
ba5c0cf5d8 Bsd: Implement Select (#4017)
* bsd: Add gdkchan's Select implementation

Co-authored-by: TSRBerry <20988865+tsrberry@users.noreply.github.com>

* bsd: Fix Select() causing a crash with an ArgumentException

.NET Sockets have to be used for the Select() call

* bsd: Make Select more generic

* bsd: Adjust namespaces and remove unused imports

* bsd: Fix NullReferenceException in Select

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2022-12-12 14:59:31 +01:00
163 changed files with 2584 additions and 1040 deletions

3
.gitignore vendored
View File

@@ -125,6 +125,9 @@ ClientBin/
packages/*
*.config
# Include nuget.config
!nuget.config
# RIA/Silverlight projects
Generated_Code/

View File

@@ -1587,6 +1587,12 @@ namespace ARMeilleure.CodeGen.X86
Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger());
// We can eliminate the move if source is already 32-bit and the registers are the same.
if (dest.Value == source.Value && source.Type == OperandType.I32)
{
return;
}
context.Assembler.Mov(dest, source, OperandType.I32);
}

View File

@@ -80,10 +80,7 @@ namespace ARMeilleure.Common
{
get
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
ObjectDisposedException.ThrowIf(_disposed, this);
lock (_pages)
{
@@ -100,10 +97,7 @@ namespace ARMeilleure.Common
/// <exception cref="ArgumentException">Length of <paramref name="levels"/> is less than 2</exception>
public AddressTable(Level[] levels)
{
if (levels == null)
{
throw new ArgumentNullException(nameof(levels));
}
ArgumentNullException.ThrowIfNull(levels);
if (levels.Length < 2)
{
@@ -141,10 +135,7 @@ namespace ARMeilleure.Common
/// <exception cref="ArgumentException"><paramref name="address"/> is not mapped</exception>
public ref TEntry GetValue(ulong address)
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
ObjectDisposedException.ThrowIf(_disposed, this);
if (!IsValid(address))
{

View File

@@ -49,10 +49,7 @@ namespace ARMeilleure.Common
{
get
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
ObjectDisposedException.ThrowIf(_disposed, this);
return ref _countTable.GetValue(_index);
}

View File

@@ -53,10 +53,7 @@ namespace ARMeilleure.Common
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
public int Allocate()
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
ObjectDisposedException.ThrowIf(_disposed, this);
lock (_allocated)
{
@@ -83,10 +80,7 @@ namespace ARMeilleure.Common
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
public void Free(int index)
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
ObjectDisposedException.ThrowIf(_disposed, this);
lock (_allocated)
{
@@ -108,10 +102,7 @@ namespace ARMeilleure.Common
/// <exception cref="ArgumentException">Entry at <paramref name="index"/> is not allocated</exception>
public ref TEntry GetValue(int index)
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
ObjectDisposedException.ThrowIf(_disposed, this);
lock (_allocated)
{

View File

@@ -381,7 +381,7 @@ namespace ARMeilleure.Instructions
for (int index = 0; index < elems; index++)
{
Operand ne = context.VectorExtract(type, GetVec(op.Rn), 0);
Operand ne = context.VectorExtract(type, GetVec(op.Rn), index);
if (sizeF == 0)
{
@@ -389,8 +389,6 @@ namespace ARMeilleure.Instructions
Operand e = context.Call(typeof(SoftFloat32_16).GetMethod(nameof(SoftFloat32_16.FPConvert)), ne);
context.LoadFromContext();
e = context.ZeroExtend16(OperandType.I64, e);
res = EmitVectorInsert(context, res, e, part + index, 1);
}
else /* if (sizeF == 1) */

View File

@@ -48,10 +48,7 @@ namespace ARMeilleure.IntermediateRepresentation
public void AddSuccessor(BasicBlock block)
{
if (block == null)
{
ThrowNull(nameof(block));
}
ArgumentNullException.ThrowIfNull(block);
if ((uint)_succCount + 1 > MaxSuccessors)
{
@@ -100,10 +97,7 @@ namespace ARMeilleure.IntermediateRepresentation
public void SetSuccessor(int index, BasicBlock block)
{
if (block == null)
{
ThrowNull(nameof(block));
}
ArgumentNullException.ThrowIfNull(block);
if ((uint)index >= (uint)_succCount)
{
@@ -144,7 +138,6 @@ namespace ARMeilleure.IntermediateRepresentation
}
}
private static void ThrowNull(string name) => throw new ArgumentNullException(name);
private static void ThrowOutOfRange(string name) => throw new ArgumentOutOfRangeException(name);
private static void ThrowSuccessorOverflow() => throw new OverflowException($"BasicBlock can only have {MaxSuccessors} successors.");

View File

@@ -378,14 +378,7 @@ namespace ARMeilleure.IntermediateRepresentation
public override int GetHashCode()
{
if (Kind == OperandKind.LocalVariable)
{
return base.GetHashCode();
}
else
{
return (int)Value ^ ((int)Kind << 16) ^ ((int)Type << 20);
}
return ((ulong)_data).GetHashCode();
}
public bool Equals(Operand operand)

View File

@@ -18,17 +18,17 @@ namespace ARMeilleure.Signal
public IntPtr sa_restorer;
}
static class UnixSignalHandlerRegistration
static partial class UnixSignalHandlerRegistration
{
private const int SIGSEGV = 11;
private const int SIGBUS = 10;
private const int SA_SIGINFO = 0x00000004;
[DllImport("libc", SetLastError = true)]
private static extern int sigaction(int signum, ref SigAction sigAction, out SigAction oldAction);
[LibraryImport("libc", SetLastError = true)]
private static partial int sigaction(int signum, ref SigAction sigAction, out SigAction oldAction);
[DllImport("libc", SetLastError = true)]
private static extern int sigemptyset(ref SigSet set);
[LibraryImport("libc", SetLastError = true)]
private static partial int sigemptyset(ref SigSet set);
public static SigAction RegisterExceptionHandler(IntPtr action)
{

View File

@@ -3,19 +3,19 @@ using System.Runtime.InteropServices;
namespace ARMeilleure.Signal
{
unsafe class WindowsSignalHandlerRegistration
unsafe partial class WindowsSignalHandlerRegistration
{
[DllImport("kernel32.dll")]
private static extern IntPtr AddVectoredExceptionHandler(uint first, IntPtr handler);
[LibraryImport("kernel32.dll")]
private static partial IntPtr AddVectoredExceptionHandler(uint first, IntPtr handler);
[DllImport("kernel32.dll")]
private static extern ulong RemoveVectoredExceptionHandler(IntPtr handle);
[LibraryImport("kernel32.dll")]
private static partial ulong RemoveVectoredExceptionHandler(IntPtr handle);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Ansi)]
static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);
[LibraryImport("kernel32.dll", SetLastError = true, EntryPoint = "LoadLibraryA")]
private static partial IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[LibraryImport("kernel32.dll", SetLastError = true)]
private static partial IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string procName);
private static IntPtr _getCurrentThreadIdPtr;

View File

@@ -7,7 +7,7 @@ using System.Runtime.InteropServices;
namespace ARMeilleure.Translation.Cache
{
static class JitUnwindWindows
static partial class JitUnwindWindows
{
private const int MaxUnwindCodesArraySize = 32; // Must be an even value.
@@ -42,14 +42,15 @@ namespace ARMeilleure.Translation.Cache
private unsafe delegate RuntimeFunction* GetRuntimeFunctionCallback(ulong controlPc, IntPtr context);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
private static unsafe extern bool RtlInstallFunctionTableCallback(
[LibraryImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static unsafe partial bool RtlInstallFunctionTableCallback(
ulong tableIdentifier,
ulong baseAddress,
uint length,
GetRuntimeFunctionCallback callback,
IntPtr context,
string outOfProcessCallbackDll);
[MarshalAs(UnmanagedType.LPWStr)] string outOfProcessCallbackDll);
private static GetRuntimeFunctionCallback _getRuntimeFunctionCallback;

View File

@@ -25,10 +25,7 @@ namespace ARMeilleure.Translation
public static Delegate GetDelegate(MethodInfo info)
{
if (info == null)
{
throw new ArgumentNullException(nameof(info));
}
ArgumentNullException.ThrowIfNull(info);
Type[] parameters = info.GetParameters().Select(pI => pI.ParameterType).ToArray();
Type returnType = info.ReturnType;

View File

@@ -35,10 +35,7 @@ namespace ARMeilleure.Translation
public static IntPtr GetDelegateFuncPtr(MethodInfo info)
{
if (info == null)
{
throw new ArgumentNullException(nameof(info));
}
ArgumentNullException.ThrowIfNull(info);
string key = GetKey(info);
@@ -52,10 +49,7 @@ namespace ARMeilleure.Translation
public static int GetDelegateIndex(MethodInfo info)
{
if (info == null)
{
throw new ArgumentNullException(nameof(info));
}
ArgumentNullException.ThrowIfNull(info);
string key = GetKey(info);

View File

@@ -67,10 +67,7 @@ namespace ARMeilleure.Translation
/// <returns>True if the value was added, false if the start key was already in the dictionary</returns>
public bool AddOrUpdate(K start, K end, V value, Func<K, V, V> updateFactoryCallback)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
ArgumentNullException.ThrowIfNull(value);
return BSTInsert(start, end, value, updateFactoryCallback, out IntervalTreeNode<K, V> node);
}
@@ -85,10 +82,7 @@ namespace ARMeilleure.Translation
/// <returns><paramref name="value"/> if <paramref name="start"/> is not yet on the tree, or the existing value otherwise</returns>
public V GetOrAdd(K start, K end, V value)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
ArgumentNullException.ThrowIfNull(value);
BSTInsert(start, end, value, null, out IntervalTreeNode<K, V> node);
return node.Value;
@@ -152,10 +146,7 @@ namespace ARMeilleure.Translation
/// <returns>Node reference in the tree</returns>
private IntervalTreeNode<K, V> GetNode(K key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
IntervalTreeNode<K, V> node = _root;
while (node != null)

View File

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

View File

@@ -140,7 +140,7 @@ namespace ARMeilleure.Translation
}
}
Array.Clear(localDefs, 0, localDefs.Length);
Array.Clear(localDefs);
}
}

View File

@@ -30,10 +30,7 @@ namespace ARMeilleure.Translation
{
get
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
ObjectDisposedException.ThrowIf(_disposed, this);
return _dispatchStub.Value;
}
@@ -47,10 +44,7 @@ namespace ARMeilleure.Translation
{
get
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
ObjectDisposedException.ThrowIf(_disposed, this);
return _slowDispatchStub.Value;
}
@@ -64,10 +58,7 @@ namespace ARMeilleure.Translation
{
get
{
if (_disposed)
{
throw new ObjectDisposedException(null);
}
ObjectDisposedException.ThrowIf(_disposed, this);
return _dispatchLoop.Value;
}
@@ -81,7 +72,9 @@ namespace ARMeilleure.Translation
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
public TranslatorStubs(Translator translator)
{
_translator = translator ?? throw new ArgumentNullException(nameof(translator));
ArgumentNullException.ThrowIfNull(translator);
_translator = translator;
_dispatchStub = new(GenerateDispatchStub, isThreadSafe: true);
_dispatchLoop = new(GenerateDispatchLoop, isThreadSafe: true);
}

54
Directory.Packages.props Normal file
View File

@@ -0,0 +1,54 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Avalonia" Version="0.10.18" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.18" />
<PackageVersion Include="Avalonia.Desktop" Version="0.10.18" />
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.18" />
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.18" />
<PackageVersion Include="Avalonia.Svg" Version="0.10.18" />
<PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" />
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="Concentus" Version="1.1.7" />
<PackageVersion Include="Crc32.NET" Version="1.2.0" />
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
<PackageVersion Include="DynamicData" Version="7.12.11" />
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
<PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" />
<PackageVersion Include="LibHac" Version="0.17.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.3" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
<PackageVersion Include="NUnit" Version="3.13.3" />
<PackageVersion Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageVersion Include="OpenTK.Core" Version="4.7.5" />
<PackageVersion Include="OpenTK.Graphics" Version="4.7.5" />
<PackageVersion Include="OpenTK.OpenAL" Version="4.7.5" />
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.5" />
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.24.2-build21" />
<PackageVersion Include="shaderc.net" Version="0.1.0" />
<PackageVersion Include="SharpZipLib" Version="1.4.1" />
<PackageVersion Include="Silk.NET.Vulkan" Version="2.16.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
<PackageVersion Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
<PackageVersion Include="SixLabors.ImageSharp" Version="1.0.4" />
<PackageVersion Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta11" />
<PackageVersion Include="SPB" Version="0.0.4-build28" />
<PackageVersion Include="System.Drawing.Common" Version="7.0.0" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.25.1" />
<PackageVersion Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
<PackageVersion Include="System.Management" Version="7.0.0" />
<PackageVersion Include="System.Net.NameResolution" Version="4.3.0" />
<PackageVersion Include="System.Threading.ThreadPool" Version="4.3.0" />
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" />
</ItemGroup>
</Project>

View File

@@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OpenTK.OpenAL" Version="4.7.5" />
<PackageReference Include="OpenTK.OpenAL" />
</ItemGroup>
<ItemGroup>

View File

@@ -51,6 +51,40 @@ namespace Ryujinx.Audio.Backends.CompatLayer
};
}
private SampleFormat SelectHardwareSampleFormat(SampleFormat targetSampleFormat)
{
if (_realDriver.SupportsSampleFormat(targetSampleFormat))
{
return targetSampleFormat;
}
// Attempt conversion from PCM16.
if (targetSampleFormat == SampleFormat.PcmInt16)
{
// Prefer PCM32 if we need to convert.
if (_realDriver.SupportsSampleFormat(SampleFormat.PcmInt32))
{
return SampleFormat.PcmInt32;
}
// If not supported, PCM float provides the best quality with a cost lower than PCM24.
if (_realDriver.SupportsSampleFormat(SampleFormat.PcmFloat))
{
return SampleFormat.PcmFloat;
}
// TODO: Implement PCM24 conversion.
// If nothing is truly supported, attempt PCM8 at the cost of loosing quality.
if (_realDriver.SupportsSampleFormat(SampleFormat.PcmInt8))
{
return SampleFormat.PcmInt8;
}
}
throw new ArgumentException("No valid sample format configuration found!");
}
public IHardwareDeviceSession OpenDeviceSession(Direction direction, IVirtualMemoryManager memoryManager, SampleFormat sampleFormat, uint sampleRate, uint channelCount, float volume)
{
if (channelCount == 0)
@@ -77,15 +111,26 @@ namespace Ryujinx.Audio.Backends.CompatLayer
throw new NotImplementedException();
}
SampleFormat hardwareSampleFormat = SelectHardwareSampleFormat(sampleFormat);
uint hardwareChannelCount = SelectHardwareChannelCount(channelCount);
IHardwareDeviceSession realSession = _realDriver.OpenDeviceSession(direction, memoryManager, sampleFormat, sampleRate, hardwareChannelCount, volume);
IHardwareDeviceSession realSession = _realDriver.OpenDeviceSession(direction, memoryManager, hardwareSampleFormat, sampleRate, hardwareChannelCount, volume);
if (hardwareChannelCount == channelCount)
if (hardwareChannelCount == channelCount && hardwareSampleFormat == sampleFormat)
{
return realSession;
}
if (hardwareSampleFormat != sampleFormat)
{
Logger.Warning?.Print(LogClass.Audio, $"{sampleFormat} isn't supported by the audio device, conversion to {hardwareSampleFormat} will happen.");
if (hardwareSampleFormat < sampleFormat)
{
Logger.Warning?.Print(LogClass.Audio, $"{hardwareSampleFormat} has lower quality than {sampleFormat}, expect some loss in audio fidelity.");
}
}
if (direction == Direction.Input)
{
Logger.Warning?.Print(LogClass.Audio, $"The selected audio backend doesn't support the requested audio input configuration, fallback to dummy...");
@@ -103,7 +148,7 @@ namespace Ryujinx.Audio.Backends.CompatLayer
}
// If we need to do post processing before sending to the hardware device, wrap around it.
return new CompatLayerHardwareDeviceSession(realSessionOutputBase, channelCount);
return new CompatLayerHardwareDeviceSession(realSessionOutputBase, sampleFormat, channelCount);
}
public bool SupportsChannelCount(uint channelCount)

View File

@@ -1,5 +1,6 @@
using Ryujinx.Audio.Backends.Common;
using Ryujinx.Audio.Common;
using Ryujinx.Audio.Renderer.Dsp;
using System;
using System.Runtime.InteropServices;
@@ -8,11 +9,13 @@ namespace Ryujinx.Audio.Backends.CompatLayer
class CompatLayerHardwareDeviceSession : HardwareDeviceSessionOutputBase
{
private HardwareDeviceSessionOutputBase _realSession;
private SampleFormat _userSampleFormat;
private uint _userChannelCount;
public CompatLayerHardwareDeviceSession(HardwareDeviceSessionOutputBase realSession, uint userChannelCount) : base(realSession.MemoryManager, realSession.RequestedSampleFormat, realSession.RequestedSampleRate, userChannelCount)
public CompatLayerHardwareDeviceSession(HardwareDeviceSessionOutputBase realSession, SampleFormat userSampleFormat, uint userChannelCount) : base(realSession.MemoryManager, realSession.RequestedSampleFormat, realSession.RequestedSampleRate, userChannelCount)
{
_realSession = realSession;
_userSampleFormat = userSampleFormat;
_userChannelCount = userChannelCount;
}
@@ -38,53 +41,86 @@ namespace Ryujinx.Audio.Backends.CompatLayer
public override void QueueBuffer(AudioBuffer buffer)
{
SampleFormat realSampleFormat = _realSession.RequestedSampleFormat;
if (_userSampleFormat != realSampleFormat)
{
if (_userSampleFormat != SampleFormat.PcmInt16)
{
throw new NotImplementedException("Converting formats other than PCM16 is not supported.");
}
int userSampleCount = buffer.Data.Length / BackendHelper.GetSampleSize(_userSampleFormat);
ReadOnlySpan<short> samples = MemoryMarshal.Cast<byte, short>(buffer.Data);
byte[] convertedSamples = new byte[BackendHelper.GetSampleSize(realSampleFormat) * userSampleCount];
switch (realSampleFormat)
{
case SampleFormat.PcmInt8:
PcmHelper.Convert(MemoryMarshal.Cast<byte, sbyte>(convertedSamples), samples);
break;
case SampleFormat.PcmInt32:
PcmHelper.Convert(MemoryMarshal.Cast<byte, int>(convertedSamples), samples);
break;
case SampleFormat.PcmFloat:
PcmHelper.ConvertSampleToPcmFloat(MemoryMarshal.Cast<byte, float>(convertedSamples), samples);
break;
default:
throw new NotImplementedException($"Sample format conversion from {_userSampleFormat} to {realSampleFormat} not implemented.");
}
buffer.Data = convertedSamples;
}
_realSession.QueueBuffer(buffer);
}
public override bool RegisterBuffer(AudioBuffer buffer, byte[] samples)
{
if (RequestedSampleFormat != SampleFormat.PcmInt16)
{
throw new NotImplementedException("Downmixing formats other than PCM16 is not supported.");
}
if (samples == null)
{
return false;
}
short[] downmixedBufferPCM16;
ReadOnlySpan<short> samplesPCM16 = MemoryMarshal.Cast<byte, short>(samples);
if (_userChannelCount == 6)
if (_userChannelCount != _realSession.RequestedChannelCount)
{
downmixedBufferPCM16 = Downmixing.DownMixSurroundToStereo(samplesPCM16);
if (_realSession.RequestedChannelCount == 1)
if (_userSampleFormat != SampleFormat.PcmInt16)
{
downmixedBufferPCM16 = Downmixing.DownMixStereoToMono(downmixedBufferPCM16);
throw new NotImplementedException("Downmixing formats other than PCM16 is not supported.");
}
}
else if (_userChannelCount == 2 && _realSession.RequestedChannelCount == 1)
{
downmixedBufferPCM16 = Downmixing.DownMixStereoToMono(samplesPCM16);
}
else
{
throw new NotImplementedException($"Downmixing from {_userChannelCount} to {_realSession.RequestedChannelCount} not implemented.");
}
byte[] downmixedBuffer = MemoryMarshal.Cast<short, byte>(downmixedBufferPCM16).ToArray();
ReadOnlySpan<short> samplesPCM16 = MemoryMarshal.Cast<byte, short>(samples);
if (_userChannelCount == 6)
{
samplesPCM16 = Downmixing.DownMixSurroundToStereo(samplesPCM16);
if (_realSession.RequestedChannelCount == 1)
{
samplesPCM16 = Downmixing.DownMixStereoToMono(samplesPCM16);
}
}
else if (_userChannelCount == 2 && _realSession.RequestedChannelCount == 1)
{
samplesPCM16 = Downmixing.DownMixStereoToMono(samplesPCM16);
}
else
{
throw new NotImplementedException($"Downmixing from {_userChannelCount} to {_realSession.RequestedChannelCount} not implemented.");
}
samples = MemoryMarshal.Cast<short, byte>(samplesPCM16).ToArray();
}
AudioBuffer fakeBuffer = new AudioBuffer
{
BufferTag = buffer.BufferTag,
DataPointer = buffer.DataPointer,
DataSize = (ulong)downmixedBuffer.Length
DataSize = (ulong)samples.Length
};
bool result = _realSession.RegisterBuffer(fakeBuffer, downmixedBuffer);
bool result = _realSession.RegisterBuffer(fakeBuffer, samples);
if (result)
{

View File

@@ -23,7 +23,7 @@ namespace Ryujinx.Audio.Renderer.Common
if (size != 0)
{
ulong alignedOffset = BitUtils.AlignUp(Offset, align);
ulong alignedOffset = BitUtils.AlignUp<ulong>(Offset, (ulong)align);
if (alignedOffset + size <= (ulong)BackingMemory.Length)
{
@@ -55,7 +55,7 @@ namespace Ryujinx.Audio.Renderer.Common
public static ulong GetTargetSize<T>(ulong currentSize, ulong count, int align) where T : unmanaged
{
return BitUtils.AlignUp(currentSize, align) + (ulong)Unsafe.SizeOf<T>() * count;
return BitUtils.AlignUp<ulong>(currentSize, (ulong)align) + (ulong)Unsafe.SizeOf<T>() * count;
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
namespace Ryujinx.Audio.Renderer.Dsp
@@ -23,6 +24,44 @@ namespace Ryujinx.Audio.Renderer.Dsp
return GetCountToDecode(startSampleOffset, endSampleOffset, offset, count) * Unsafe.SizeOf<T>();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static float ConvertSampleToPcmFloat(short sample)
{
return (float)sample / short.MaxValue;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static short ConvertSampleToPcmInt16(float sample)
{
return Saturate(sample * short.MaxValue);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static TOutput ConvertSample<TInput, TOutput>(TInput value) where TInput: INumber<TInput>, IMinMaxValue<TInput> where TOutput : INumber<TOutput>, IMinMaxValue<TOutput>
{
TInput conversionRate = TInput.CreateSaturating(TOutput.MaxValue / TOutput.CreateSaturating(TInput.MaxValue));
return TOutput.CreateSaturating(value * conversionRate);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Convert<TInput, TOutput>(Span<TOutput> output, ReadOnlySpan<TInput> input) where TInput : INumber<TInput>, IMinMaxValue<TInput> where TOutput : INumber<TOutput>, IMinMaxValue<TOutput>
{
for (int i = 0; i < input.Length; i++)
{
output[i] = ConvertSample<TInput, TOutput>(input[i]);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void ConvertSampleToPcmFloat(Span<float> output, ReadOnlySpan<short> input)
{
for (int i = 0; i < input.Length; i++)
{
output[i] = ConvertSampleToPcmFloat(input[i]);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Decode(Span<short> output, ReadOnlySpan<short> input, int startSampleOffset, int endSampleOffset, int channelIndex, int channelCount)
{
@@ -53,7 +92,7 @@ namespace Ryujinx.Audio.Renderer.Dsp
for (int i = 0; i < decodedCount; i++)
{
output[i] = (short)(input[i * channelCount + channelIndex] * short.MaxValue);
output[i] = ConvertSampleToPcmInt16(input[i * channelCount + channelIndex]);
}
return decodedCount;

View File

@@ -167,7 +167,7 @@ namespace Ryujinx.Audio.Renderer.Server
return ResultCode.WorkBufferTooSmall;
}
_depopBuffer = workBufferAllocator.Allocate<float>((ulong)BitUtils.AlignUp(parameter.MixBufferCount, Constants.BufferAlignment), Constants.BufferAlignment);
_depopBuffer = workBufferAllocator.Allocate<float>(BitUtils.AlignUp<ulong>(parameter.MixBufferCount, Constants.BufferAlignment), Constants.BufferAlignment);
if (_depopBuffer.IsEmpty)
{
@@ -772,7 +772,7 @@ namespace Ryujinx.Audio.Renderer.Server
size = WorkBufferAllocator.GetTargetSize<float>(size, Constants.TargetSampleCount * (Constants.VoiceChannelCountMax + parameter.MixBufferCount) * (parameter.SinkCount + parameter.SubMixBufferCount), 0x10);
// Depop buffer
size = WorkBufferAllocator.GetTargetSize<float>(size, (ulong)BitUtils.AlignUp(parameter.MixBufferCount, Constants.BufferAlignment), Constants.BufferAlignment);
size = WorkBufferAllocator.GetTargetSize<float>(size, BitUtils.AlignUp<ulong>(parameter.MixBufferCount, Constants.BufferAlignment), Constants.BufferAlignment);
// Voice
size = WorkBufferAllocator.GetTargetSize<VoiceState>(size, parameter.VoiceCount, VoiceState.Alignment);
@@ -804,10 +804,10 @@ namespace Ryujinx.Audio.Renderer.Server
{
ulong performanceMetricsPerFramesSize = PerformanceManager.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter, ref behaviourContext) * (parameter.PerformanceMetricFramesCount + 1) + 0xC;
size += BitUtils.AlignUp(performanceMetricsPerFramesSize, Constants.PerformanceMetricsPerFramesSizeAlignment);
size += BitUtils.AlignUp<ulong>(performanceMetricsPerFramesSize, Constants.PerformanceMetricsPerFramesSizeAlignment);
}
return BitUtils.AlignUp(size, Constants.WorkBufferAlignment);
return BitUtils.AlignUp<ulong>(size, Constants.WorkBufferAlignment);
}
public ResultCode QuerySystemEvent(out IWritableEvent systemEvent)

View File

@@ -17,7 +17,7 @@ using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Common.System;
using Ryujinx.Common.SystemInterop;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.GAL.Multithreading;
using Ryujinx.Graphics.Gpu;

View File

@@ -280,7 +280,7 @@
"ControllerSettingsRemoveProfileToolTip": "Remove Profile",
"ControllerSettingsSaveProfileToolTip": "Save Profile",
"MenuBarFileToolsTakeScreenshot": "Take Screenshot",
"MenuBarFileToolsHideUi": "Hide Ui",
"MenuBarFileToolsHideUi": "Hide UI",
"GameListContextMenuToggleFavorite": "Toggle Favorite",
"GameListContextMenuToggleFavoriteToolTip": "Toggle Favorite status of Game",
"SettingsTabGeneralTheme": "Theme",

View File

@@ -8,7 +8,7 @@ namespace Ryujinx.Ava.Ui.Helper
public delegate void UpdateBoundsCallbackDelegate(Rect rect);
[SupportedOSPlatform("macos")]
static class MetalHelper
static partial class MetalHelper
{
private const string LibObjCImport = "/usr/lib/libobjc.A.dylib";
@@ -100,28 +100,28 @@ namespace Ryujinx.Ava.Ui.Helper
// TODO
}
[DllImport(LibObjCImport)]
private static unsafe extern IntPtr sel_registerName(byte* data);
[LibraryImport(LibObjCImport)]
private static unsafe partial IntPtr sel_registerName(byte* data);
[DllImport(LibObjCImport)]
private static unsafe extern IntPtr objc_getClass(byte* data);
[LibraryImport(LibObjCImport)]
private static unsafe partial IntPtr objc_getClass(byte* data);
[DllImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector);
[LibraryImport(LibObjCImport)]
private static partial void objc_msgSend(IntPtr receiver, Selector selector);
[DllImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector, byte value);
[LibraryImport(LibObjCImport)]
private static partial void objc_msgSend(IntPtr receiver, Selector selector, byte value);
[DllImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value);
[LibraryImport(LibObjCImport)]
private static partial void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value);
[DllImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector, NSRect point);
[LibraryImport(LibObjCImport)]
private static partial void objc_msgSend(IntPtr receiver, Selector selector, NSRect point);
[DllImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector, double value);
[LibraryImport(LibObjCImport)]
private static partial void objc_msgSend(IntPtr receiver, Selector selector, double value);
[DllImport(LibObjCImport, EntryPoint = "objc_msgSend")]
private static extern IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector);
[LibraryImport(LibObjCImport, EntryPoint = "objc_msgSend")]
private static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector);
}
}

View File

@@ -7,8 +7,7 @@ namespace Ryujinx.Ava.Input
{
internal static class AvaloniaKeyboardMappingHelper
{
private static readonly AvaKey[] _keyMapping = new AvaKey[(int)Key.Count]
{
private static readonly AvaKey[] _keyMapping = {
// NOTE: Invalid
AvaKey.None,
@@ -151,16 +150,16 @@ namespace Ryujinx.Ava.Input
static AvaloniaKeyboardMappingHelper()
{
var inputKeys = Enum.GetValues(typeof(Key));
var inputKeys = Enum.GetValues<Key>();
// NOTE: Avalonia.Input.Key is not contiguous and quite large, so use a dictionary instead of an array.
_avaKeyMapping = new Dictionary<AvaKey, Key>();
foreach (var key in inputKeys)
{
if (TryGetAvaKey((Key)key, out var index))
if (TryGetAvaKey(key, out var index))
{
_avaKeyMapping[index] = (Key)key;
_avaKeyMapping[index] = key;
}
}
}

View File

@@ -11,6 +11,7 @@ using Ryujinx.Ava.Ui.Controls;
using Ryujinx.Ava.Ui.Windows;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Ui.Common.Helper;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -278,14 +279,15 @@ namespace Ryujinx.Modules
{
string ryuName = Path.GetFileName(Environment.ProcessPath);
string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
var ryuArg = Environment.GetCommandLineArgs().Skip(1);
if (!OperatingSystem.IsWindows())
if (!Path.Exists(ryuExe))
{
chmod(ryuExe, Convert.ToUInt32("0777", 8));
ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx");
}
Process.Start(ryuExe, ryuArg);
SetFileExecutable(ryuExe);
Process.Start(ryuExe, CommandLineState.Arguments);
Environment.Exit(0);
}
@@ -456,16 +458,19 @@ namespace Ryujinx.Modules
worker.Start();
}
[DllImport("libc", SetLastError = true)]
private static extern int chmod(string path, uint mode);
private static void SetUnixPermissions()
private static void SetFileExecutable(string path)
{
string ryuBin = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx");
const UnixFileMode ExecutableFileMode = UnixFileMode.UserExecute |
UnixFileMode.UserWrite |
UnixFileMode.UserRead |
UnixFileMode.GroupRead |
UnixFileMode.GroupWrite |
UnixFileMode.OtherRead |
UnixFileMode.OtherWrite;
if (!OperatingSystem.IsWindows())
if (!OperatingSystem.IsWindows() && File.Exists(path))
{
chmod(ryuBin, 493);
File.SetUnixFileMode(path, ExecutableFileMode);
}
}
@@ -586,7 +591,7 @@ namespace Ryujinx.Modules
Directory.Delete(UpdateDir, true);
SetUnixPermissions();
SetFileExecutable(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx"));
UpdateSuccessful = true;

View File

@@ -6,7 +6,7 @@ using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.GraphicsDriver;
using Ryujinx.Common.Logging;
using Ryujinx.Common.System;
using Ryujinx.Common.SystemInterop;
using Ryujinx.Common.SystemInfo;
using Ryujinx.Modules;
using Ryujinx.SDL2.Common;
@@ -20,7 +20,7 @@ using System.Threading.Tasks;
namespace Ryujinx.Ava
{
internal class Program
internal partial class Program
{
public static double WindowScaleFactor { get; set; }
public static double DesktopScaleFactor { get; set; } = 1.0;
@@ -28,8 +28,8 @@ namespace Ryujinx.Ava
public static string ConfigurationPath { get; private set; }
public static bool PreviewerDetached { get; private set; }
[DllImport("user32.dll", SetLastError = true)]
public static extern int MessageBoxA(IntPtr hWnd, string text, string caption, uint type);
[LibraryImport("user32.dll", SetLastError = true)]
public static partial int MessageBoxA(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type);
private const uint MB_ICONWARNING = 0x30;

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<RuntimeIdentifiers>win10-x64;osx-x64;linux-x64</RuntimeIdentifiers>
@@ -18,31 +18,31 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="0.10.18" />
<PackageReference Include="Avalonia.Desktop" Version="0.10.18" />
<PackageReference Include="Avalonia.Diagnostics" Version="0.10.18" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="0.10.18" />
<PackageReference Include="Avalonia.Markup.Xaml.Loader" Version="0.10.18" />
<PackageReference Include="Avalonia.Svg" Version="0.10.18" />
<PackageReference Include="Avalonia.Svg.Skia" Version="0.10.18" />
<PackageReference Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" />
<PackageReference Include="DynamicData" Version="7.12.11" />
<PackageReference Include="FluentAvaloniaUI" Version="1.4.5" />
<PackageReference Include="XamlNameReferenceGenerator" Version="1.5.1" />
<PackageReference Include="Avalonia" />
<PackageReference Include="Avalonia.Desktop" />
<PackageReference Include="Avalonia.Diagnostics" />
<PackageReference Include="Avalonia.Controls.DataGrid" />
<PackageReference Include="Avalonia.Markup.Xaml.Loader" />
<PackageReference Include="Avalonia.Svg" />
<PackageReference Include="Avalonia.Svg.Skia" />
<PackageReference Include="jp2masa.Avalonia.Flexbox" />
<PackageReference Include="DynamicData" />
<PackageReference Include="FluentAvaloniaUI" />
<PackageReference Include="XamlNameReferenceGenerator" />
<PackageReference Include="OpenTK.Core" Version="4.7.5" />
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build12" />
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win10-x64'" />
<PackageReference Include="Silk.NET.Vulkan" Version="2.16.0" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
<PackageReference Include="SPB" Version="0.0.4-build28" />
<PackageReference Include="SharpZipLib" Version="1.4.1" />
<PackageReference Include="SixLabors.ImageSharp" Version="1.0.4" />
<PackageReference Include="OpenTK.Core" />
<PackageReference Include="Ryujinx.Audio.OpenAL.Dependencies" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'osx-x64' AND '$(RuntimeIdentifier)' != 'osx-arm64'" />
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" />
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win10-x64'" />
<PackageReference Include="Silk.NET.Vulkan" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" />
<PackageReference Include="SPB" />
<PackageReference Include="SharpZipLib" />
<PackageReference Include="SixLabors.ImageSharp" />
<!--NOTE: DO NOT REMOVE, THIS IS REQUIRED AS A RESULT OF A TRIMMING ISSUE IN AVALONIA -->
<PackageReference Include="System.Drawing.Common" Version="7.0.0" />
<PackageReference Include="System.Drawing.Common" />
</ItemGroup>
<ItemGroup>

View File

@@ -45,7 +45,7 @@
TextWrapping="Wrap" />
<TextBox
Name="Input"
Grid.Row="2"
Grid.Row="3"
Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"

View File

@@ -111,7 +111,7 @@ namespace Ryujinx.Ava.Ui.Controls
overlay.Position = window.PointToScreen(new Point());
await contentDialog.ShowAsync();
await contentDialog.ShowAsync(overlay);
contentDialog.Closed -= handler;
overlay.Close();
};

View File

@@ -125,9 +125,9 @@ namespace Ryujinx.Ava.Ui.Controls
{
cbSize = Marshal.SizeOf<WNDCLASSEX>(),
hInstance = GetModuleHandle(null),
lpfnWndProc = _wndProcDelegate,
lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate),
style = ClassStyles.CS_OWNDC,
lpszClassName = _className,
lpszClassName = Marshal.StringToHGlobalUni(_className),
hCursor = LoadCursor(IntPtr.Zero, (IntPtr)Cursors.IDC_ARROW)
};
@@ -148,7 +148,9 @@ namespace Ryujinx.Ava.Ui.Controls
IntPtr.Zero);
WindowHandle = handle;
Marshal.FreeHGlobal(wndClassEx.lpszClassName);
return new PlatformHandle(WindowHandle, "HWND");
}

View File

@@ -5,7 +5,7 @@ using System.Runtime.Versioning;
namespace Ryujinx.Ava.Ui.Controls
{
[SupportedOSPlatform("windows")]
internal class Win32NativeInterop
internal partial class Win32NativeInterop
{
[Flags]
public enum ClassStyles : uint
@@ -48,58 +48,52 @@ namespace Ryujinx.Ava.Ui.Controls
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
internal delegate IntPtr WindowProc(IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
[StructLayout(LayoutKind.Sequential)]
public struct WNDCLASSEX
{
public int cbSize;
public ClassStyles style;
[MarshalAs(UnmanagedType.FunctionPtr)]
public WindowProc lpfnWndProc; // not WndProc
public IntPtr lpfnWndProc; // not WndProc
public int cbClsExtra;
public int cbWndExtra;
public IntPtr hInstance;
public IntPtr hIcon;
public IntPtr hCursor;
public IntPtr hbrBackground;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpszMenuName;
[MarshalAs(UnmanagedType.LPWStr)]
public string lpszClassName;
public IntPtr lpszMenuName;
public IntPtr lpszClassName;
public IntPtr hIconSm;
public static WNDCLASSEX Create()
public WNDCLASSEX()
{
return new WNDCLASSEX
{
cbSize = Marshal.SizeOf<WNDCLASSEX>()
};
cbSize = Marshal.SizeOf<WNDCLASSEX>();
}
}
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern ushort RegisterClassEx(ref WNDCLASSEX param);
[LibraryImport("user32.dll", SetLastError = true, EntryPoint = "RegisterClassExW")]
public static partial ushort RegisterClassEx(ref WNDCLASSEX param);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern short UnregisterClass([MarshalAs(UnmanagedType.LPWStr)] string lpClassName, IntPtr instance);
[LibraryImport("user32.dll", SetLastError = true, EntryPoint = "UnregisterClassW")]
public static partial short UnregisterClass([MarshalAs(UnmanagedType.LPWStr)] string lpClassName, IntPtr instance);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr DefWindowProc(IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam);
[LibraryImport("user32.dll", EntryPoint = "DefWindowProcW")]
public static partial IntPtr DefWindowProc(IntPtr hWnd, WindowsMessages msg, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[LibraryImport("kernel32.dll", EntryPoint = "GetModuleHandleA")]
public static partial IntPtr GetModuleHandle([MarshalAs(UnmanagedType.LPStr)] string lpModuleName);
[DllImport("user32.dll", SetLastError = true)]
[LibraryImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DestroyWindow(IntPtr hwnd);
public static partial bool DestroyWindow(IntPtr hwnd);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr LoadCursor(IntPtr hInstance, IntPtr lpCursorName);
[LibraryImport("user32.dll", SetLastError = true, EntryPoint = "LoadCursorA")]
public static partial IntPtr LoadCursor(IntPtr hInstance, IntPtr lpCursorName);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr CreateWindowEx(
[LibraryImport("user32.dll", SetLastError = true, EntryPoint = "CreateWindowExW")]
public static partial IntPtr CreateWindowEx(
uint dwExStyle,
string lpClassName,
string lpWindowName,
[MarshalAs(UnmanagedType.LPWStr)] string lpClassName,
[MarshalAs(UnmanagedType.LPWStr)] string lpWindowName,
WindowStyles dwStyle,
int x,
int y,

View File

@@ -24,10 +24,7 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
public int Get(K key, ref V[] overlaps)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
IntervalTreeNode<K, V> node = GetNode(key);
@@ -61,15 +58,8 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="start"/> or <paramref name="end"/> is null</exception>
public int Get(K start, K end, ref V[] overlaps, int overlapCount = 0)
{
if (start == null)
{
throw new ArgumentNullException(nameof(start));
}
if (end == null)
{
throw new ArgumentNullException(nameof(end));
}
ArgumentNullException.ThrowIfNull(start);
ArgumentNullException.ThrowIfNull(end);
GetValues(Root, start, end, ref overlaps, ref overlapCount);
@@ -85,20 +75,9 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="start"/>, <paramref name="end"/> or <paramref name="value"/> are null</exception>
public void Add(K start, K end, V value)
{
if (start == null)
{
throw new ArgumentNullException(nameof(start));
}
if (end == null)
{
throw new ArgumentNullException(nameof(end));
}
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
ArgumentNullException.ThrowIfNull(start);
ArgumentNullException.ThrowIfNull(end);
ArgumentNullException.ThrowIfNull(value);
Insert(start, end, value);
}
@@ -112,10 +91,7 @@ namespace Ryujinx.Common.Collections
/// <returns>Number of deleted values</returns>
public int Remove(K key, V value)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
int removed = Delete(key, value);
@@ -168,10 +144,7 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
private IntervalTreeNode<K, V> GetNode(K key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
IntervalTreeNode<K, V> node = Root;
while (node != null)
@@ -462,10 +435,8 @@ namespace Ryujinx.Common.Collections
public bool ContainsKey(K key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
return GetNode(key) != null;
}
}

View File

@@ -17,10 +17,7 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="node"/> is null</exception>
public void Add(T node)
{
if (node == null)
{
throw new ArgumentNullException(nameof(node));
}
ArgumentNullException.ThrowIfNull(node);
Insert(node);
}
@@ -32,10 +29,8 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="node"/> is null</exception>
public void Remove(T node)
{
if (node == null)
{
throw new ArgumentNullException(nameof(node));
}
ArgumentNullException.ThrowIfNull(node);
if (Delete(node) != null)
{
Count--;
@@ -50,10 +45,7 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="searchNode"/> is null</exception>
public T GetNode(T searchNode)
{
if (searchNode == null)
{
throw new ArgumentNullException(nameof(searchNode));
}
ArgumentNullException.ThrowIfNull(searchNode);
T node = Root;
while (node != null)

View File

@@ -92,10 +92,8 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="node"/> is null</exception>
protected static T Minimum(T node)
{
if (node == null)
{
throw new ArgumentNullException(nameof(node));
}
ArgumentNullException.ThrowIfNull(node);
T tmp = node;
while (tmp.Left != null)
{

View File

@@ -22,10 +22,7 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
public V Get(K key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
Node<K, V> node = GetNode(key);
@@ -47,14 +44,8 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="key"/> or <paramref name="value"/> are null</exception>
public void Add(K key, V value)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
ArgumentNullException.ThrowIfNull(key);
ArgumentNullException.ThrowIfNull(value);
Insert(key, value);
}
@@ -66,10 +57,8 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
public void Remove(K key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
if (Delete(key) != null)
{
Count--;
@@ -217,10 +206,7 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
private Node<K, V> GetNode(K key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
Node<K, V> node = Root;
while (node != null)
@@ -370,10 +356,8 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
private Node<K, V> FloorNode(K key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
Node<K, V> tmp = Root;
while (tmp != null)
@@ -424,10 +408,8 @@ namespace Ryujinx.Common.Collections
/// <exception cref="ArgumentNullException"><paramref name="key"/> is null</exception>
private Node<K, V> CeilingNode(K key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
Node<K, V> tmp = Root;
while (tmp != null)
@@ -477,10 +459,8 @@ namespace Ryujinx.Common.Collections
// Method descriptions are not provided as they are already included as part of the interface.
public bool ContainsKey(K key)
{
if (key == null)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
return GetNode(key) != null;
}
@@ -493,10 +473,8 @@ namespace Ryujinx.Common.Collections
public bool TryGetValue(K key, [MaybeNullWhen(false)] out V value)
{
if (null == key)
{
throw new ArgumentNullException(nameof(key));
}
ArgumentNullException.ThrowIfNull(key);
Node<K, V> node = GetNode(key);
value = node != null ? node.Value : default;
return node != null;
@@ -504,10 +482,7 @@ namespace Ryujinx.Common.Collections
public void Add(KeyValuePair<K, V> item)
{
if (item.Key == null)
{
throw new ArgumentNullException(nameof(item.Key));
}
ArgumentNullException.ThrowIfNull(item.Key);
Add(item.Key, item.Value);
}

View File

@@ -5,7 +5,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.Common.GraphicsDriver
{
static class NVThreadedOptimization
static partial class NVThreadedOptimization
{
private const string ProfileName = "Ryujinx Nvidia Profile";
@@ -19,8 +19,8 @@ namespace Ryujinx.Common.GraphicsDriver
private const uint NvAPI_DRS_SaveSettings_ID = 0xFCBC7E14;
private const uint NvAPI_DRS_DestroySession_ID = 0x0DAD9CFF8;
[DllImport("nvapi64")]
private static extern IntPtr nvapi_QueryInterface(uint id);
[LibraryImport("nvapi64")]
private static partial IntPtr nvapi_QueryInterface(uint id);
private delegate int NvAPI_InitializeDelegate();
private static NvAPI_InitializeDelegate NvAPI_Initialize;
@@ -152,7 +152,7 @@ namespace Ryujinx.Common.GraphicsDriver
if (ptr != IntPtr.Zero)
{
return Marshal.GetDelegateForFunctionPointer(ptr, typeof(T)) as T;
return Marshal.GetDelegateForFunctionPointer<T>(ptr);
}
else
{

View File

@@ -1,6 +1,7 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;
using System.Runtime.Versioning;
using System.Threading;
@@ -12,7 +13,7 @@ namespace Ryujinx.Common.Memory.PartialUnmaps
/// State for partial unmaps. Intended to be used on Windows.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct PartialUnmapState
public partial struct PartialUnmapState
{
public NativeReaderWriterLock PartialUnmapLock;
public int PartialUnmapsCount;
@@ -25,20 +26,22 @@ namespace Ryujinx.Common.Memory.PartialUnmaps
public readonly static IntPtr GlobalState;
[SupportedOSPlatform("windows")]
[DllImport("kernel32.dll")]
public static extern int GetCurrentThreadId();
[LibraryImport("kernel32.dll")]
public static partial int GetCurrentThreadId();
[SupportedOSPlatform("windows")]
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr OpenThread(int dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
[LibraryImport("kernel32.dll", SetLastError = true)]
private static partial IntPtr OpenThread(int dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwThreadId);
[SupportedOSPlatform("windows")]
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr hObject);
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs (UnmanagedType.Bool)]
public static partial bool CloseHandle(IntPtr hObject);
[SupportedOSPlatform("windows")]
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool GetExitCodeThread(IntPtr hThread, out uint lpExitCode);
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool GetExitCodeThread(IntPtr hThread, out uint lpExitCode);
/// <summary>
/// Creates a global static PartialUnmapState and populates the field offsets.

View File

@@ -6,8 +6,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MsgPack.Cli" Version="1.0.1" />
<PackageReference Include="System.Management" Version="7.0.0" />
<PackageReference Include="MsgPack.Cli" />
<PackageReference Include="System.Management" />
</ItemGroup>
</Project>

View File

@@ -8,7 +8,7 @@ using Ryujinx.Common.Logging;
namespace Ryujinx.Common.SystemInfo
{
[SupportedOSPlatform("macos")]
class MacOSSystemInfo : SystemInfo
partial class MacOSSystemInfo : SystemInfo
{
internal MacOSSystemInfo()
{
@@ -60,8 +60,8 @@ namespace Ryujinx.Common.SystemInfo
private const string SystemLibraryName = "libSystem.dylib";
[DllImport(SystemLibraryName, CharSet = CharSet.Ansi, SetLastError = true)]
private static extern int sysctlbyname(string name, IntPtr oldValue, ref ulong oldSize, IntPtr newValue, ulong newValueSize);
[LibraryImport(SystemLibraryName, SetLastError = true)]
private static partial int sysctlbyname([MarshalAs(UnmanagedType.LPStr)] string name, IntPtr oldValue, ref ulong oldSize, IntPtr newValue, ulong newValueSize);
private static int sysctlbyname(string name, IntPtr oldValue, ref ulong oldSize)
{
@@ -116,11 +116,11 @@ namespace Ryujinx.Common.SystemInfo
return res;
}
[DllImport(SystemLibraryName, CharSet = CharSet.Ansi, SetLastError = true)]
private static extern uint mach_host_self();
[LibraryImport(SystemLibraryName, SetLastError = true)]
private static partial uint mach_host_self();
[DllImport(SystemLibraryName, CharSet = CharSet.Ansi, SetLastError = true)]
private static extern int host_page_size(uint host, ref uint out_page_size);
[LibraryImport(SystemLibraryName, SetLastError = true)]
private static partial int host_page_size(uint host, ref uint out_page_size);
[StructLayout(LayoutKind.Sequential, Pack = 8)]
struct VMStatistics64
@@ -151,7 +151,7 @@ namespace Ryujinx.Common.SystemInfo
public ulong TotalUncompressedPagesInCompressor;
}
[DllImport(SystemLibraryName, CharSet = CharSet.Ansi, SetLastError = true)]
private static extern int host_statistics64(uint host_priv, int host_flavor, ref VMStatistics64 host_info64_out, ref uint host_info64_outCnt);
[LibraryImport(SystemLibraryName, SetLastError = true)]
private static partial int host_statistics64(uint host_priv, int host_flavor, ref VMStatistics64 host_info64_out, ref uint host_info64_outCnt);
}
}

View File

@@ -7,7 +7,7 @@ using Ryujinx.Common.Logging;
namespace Ryujinx.Common.SystemInfo
{
[SupportedOSPlatform("windows")]
class WindowsSystemInfo : SystemInfo
partial class WindowsSystemInfo : SystemInfo
{
internal WindowsSystemInfo()
{
@@ -18,7 +18,7 @@ namespace Ryujinx.Common.SystemInfo
private static (ulong Total, ulong Available) GetMemoryStats()
{
MemoryStatusEx memStatus = new MemoryStatusEx();
if (GlobalMemoryStatusEx(memStatus))
if (GlobalMemoryStatusEx(ref memStatus))
{
return (memStatus.TotalPhys, memStatus.AvailPhys); // Bytes
}
@@ -45,8 +45,8 @@ namespace Ryujinx.Common.SystemInfo
return Environment.GetEnvironmentVariable("PROCESSOR_IDENTIFIER").Trim();
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
private class MemoryStatusEx
[StructLayout(LayoutKind.Sequential)]
private struct MemoryStatusEx
{
public uint Length;
public uint MemoryLoad;
@@ -60,13 +60,13 @@ namespace Ryujinx.Common.SystemInfo
public MemoryStatusEx()
{
Length = (uint)Marshal.SizeOf(typeof(MemoryStatusEx));
Length = (uint)Marshal.SizeOf<MemoryStatusEx>();
}
}
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool GlobalMemoryStatusEx([In, Out] MemoryStatusEx lpBuffer);
private static partial bool GlobalMemoryStatusEx(ref MemoryStatusEx lpBuffer);
private static ManagementObjectCollection GetWMIObjects(string scope, string query)
{

View File

@@ -1,9 +1,9 @@
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Common.System
namespace Ryujinx.Common.SystemInterop
{
public class DisplaySleep
public partial class DisplaySleep
{
[Flags]
enum EXECUTION_STATE : uint
@@ -13,8 +13,8 @@ namespace Ryujinx.Common.System
ES_SYSTEM_REQUIRED = 0x00000001
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags);
[LibraryImport("kernel32.dll", SetLastError = true)]
private static partial EXECUTION_STATE SetThreadExecutionState(EXECUTION_STATE esFlags);
static public void Prevent()
{

View File

@@ -3,29 +3,30 @@ using System;
using System.Globalization;
using System.Runtime.InteropServices;
namespace Ryujinx.Common.System
namespace Ryujinx.Common.SystemInterop
{
public static class ForceDpiAware
public static partial class ForceDpiAware
{
[DllImport("user32.dll")]
private static extern bool SetProcessDPIAware();
[LibraryImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool SetProcessDPIAware();
private const string X11LibraryName = "libX11.so.6";
[DllImport(X11LibraryName)]
private static extern IntPtr XOpenDisplay(string display);
[LibraryImport(X11LibraryName)]
private static partial IntPtr XOpenDisplay([MarshalAs(UnmanagedType.LPStr)] string display);
[DllImport(X11LibraryName)]
private static extern IntPtr XGetDefault(IntPtr display, string program, string option);
[LibraryImport(X11LibraryName)]
private static partial IntPtr XGetDefault(IntPtr display, [MarshalAs(UnmanagedType.LPStr)] string program, [MarshalAs(UnmanagedType.LPStr)] string option);
[DllImport(X11LibraryName)]
private static extern int XDisplayWidth(IntPtr display, int screenNumber);
[LibraryImport(X11LibraryName)]
private static partial int XDisplayWidth(IntPtr display, int screenNumber);
[DllImport(X11LibraryName)]
private static extern int XDisplayWidthMM(IntPtr display, int screenNumber);
[LibraryImport(X11LibraryName)]
private static partial int XDisplayWidthMM(IntPtr display, int screenNumber);
[DllImport(X11LibraryName)]
private static extern int XCloseDisplay(IntPtr display);
[LibraryImport(X11LibraryName)]
private static partial int XCloseDisplay(IntPtr display);
private static readonly double _standardDpiScale = 96.0;
private static readonly double _maxScaleFactor = 1.25;

View File

@@ -2,10 +2,10 @@
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
namespace Ryujinx.Common.System
namespace Ryujinx.Common.SystemInterop
{
[SupportedOSPlatform("windows")]
public static class GdiPlusHelper
public static partial class GdiPlusHelper
{
private const string LibraryName = "gdiplus.dll";
@@ -52,17 +52,17 @@ namespace Ryujinx.Common.System
public IntPtr NotificationUnhook;
}
[DllImport(LibraryName)]
private static extern int GdiplusStartup(out IntPtr token, in StartupInputEx input, out StartupOutput output);
[LibraryImport(LibraryName)]
private static partial int GdiplusStartup(out IntPtr token, in StartupInputEx input, out StartupOutput output);
[DllImport(LibraryName)]
private static extern int GdipCreateFromHWND(IntPtr hwnd, out IntPtr graphics);
[LibraryImport(LibraryName)]
private static partial int GdipCreateFromHWND(IntPtr hwnd, out IntPtr graphics);
[DllImport(LibraryName)]
private static extern int GdipDeleteGraphics(IntPtr graphics);
[LibraryImport(LibraryName)]
private static partial int GdipDeleteGraphics(IntPtr graphics);
[DllImport(LibraryName)]
private static extern int GdipGetDpiX(IntPtr graphics, out float dpi);
[LibraryImport(LibraryName)]
private static partial int GdipGetDpiX(IntPtr graphics, out float dpi);
public static float GetDpiX(IntPtr hwnd)
{

View File

@@ -4,13 +4,13 @@ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
namespace Ryujinx.Common.System
namespace Ryujinx.Common.SystemInterop
{
/// <summary>
/// Handle Windows Multimedia timer resolution.
/// </summary>
[SupportedOSPlatform("windows")]
public class WindowsMultimediaTimerResolution : IDisposable
public partial class WindowsMultimediaTimerResolution : IDisposable
{
[StructLayout(LayoutKind.Sequential)]
public struct TimeCaps
@@ -19,14 +19,14 @@ namespace Ryujinx.Common.System
public uint wPeriodMax;
};
[DllImport("winmm.dll", EntryPoint = "timeGetDevCaps", SetLastError = true)]
private static extern uint TimeGetDevCaps(ref TimeCaps timeCaps, uint sizeTimeCaps);
[LibraryImport("winmm.dll", EntryPoint = "timeGetDevCaps", SetLastError = true)]
private static partial uint TimeGetDevCaps(ref TimeCaps timeCaps, uint sizeTimeCaps);
[DllImport("winmm.dll", EntryPoint = "timeBeginPeriod")]
private static extern uint TimeBeginPeriod(uint uMilliseconds);
[LibraryImport("winmm.dll", EntryPoint = "timeBeginPeriod")]
private static partial uint TimeBeginPeriod(uint uMilliseconds);
[DllImport("winmm.dll", EntryPoint = "timeEndPeriod")]
private static extern uint TimeEndPeriod(uint uMilliseconds);
[LibraryImport("winmm.dll", EntryPoint = "timeEndPeriod")]
private static partial uint TimeEndPeriod(uint uMilliseconds);
private uint _targetResolutionInMilliseconds;
private bool _isActive;

View File

@@ -1,82 +1,26 @@
using System;
using System.Numerics;
namespace Ryujinx.Common
{
public static class BitUtils
{
public static uint AlignUp(uint value, int size)
public static T AlignUp<T>(T value, T size)
where T : IBinaryInteger<T>
{
return (uint)AlignUp((int)value, size);
return (value + (size - T.One)) & -size;
}
public static int AlignUp(int value, int size)
{
return (value + (size - 1)) & -size;
}
public static ulong AlignUp(ulong value, int size)
{
return (ulong)AlignUp((long)value, size);
}
public static long AlignUp(long value, int size)
{
return AlignUp(value, (long)size);
}
public static ulong AlignUp(ulong value, ulong size)
{
return (ulong)AlignUp((long)value, (long)size);
}
public static long AlignUp(long value, long size)
{
return (value + (size - 1)) & -size;
}
public static uint AlignDown(uint value, int size)
{
return (uint)AlignDown((int)value, size);
}
public static int AlignDown(int value, int size)
public static T AlignDown<T>(T value, T size)
where T : IBinaryInteger<T>
{
return value & -size;
}
public static ulong AlignDown(ulong value, int size)
public static T DivRoundUp<T>(T value, T dividend)
where T : IBinaryInteger<T>
{
return (ulong)AlignDown((long)value, size);
}
public static long AlignDown(long value, int size)
{
return AlignDown(value, (long)size);
}
public static ulong AlignDown(ulong value, ulong size)
{
return (ulong)AlignDown((long)value, (long)size);
}
public static long AlignDown(long value, long size)
{
return value & -size;
}
public static int DivRoundUp(int value, int dividend)
{
return (value + dividend - 1) / dividend;
}
public static ulong DivRoundUp(ulong value, uint dividend)
{
return (value + dividend - 1) / dividend;
}
public static long DivRoundUp(long value, int dividend)
{
return (value + dividend - 1) / dividend;
return (value + (dividend - T.One)) / dividend;
}
public static int Pow2RoundUp(int value)

View File

@@ -1,12 +1,13 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Device
{
public class DeviceState<TState> : IDeviceState where TState : unmanaged
public class DeviceState<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TState> : IDeviceState where TState : unmanaged
{
private const int RegisterSize = sizeof(int);

View File

@@ -14,10 +14,13 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsBc123Compression;
public readonly bool SupportsBc45Compression;
public readonly bool SupportsBc67Compression;
public readonly bool SupportsEtc2Compression;
public readonly bool Supports3DTextureCompression;
public readonly bool SupportsBgraFormat;
public readonly bool SupportsR4G4Format;
public readonly bool SupportsR4G4B4A4Format;
public readonly bool SupportsSnormBufferTextureFormat;
public readonly bool Supports5BitComponentFormat;
public readonly bool SupportsFragmentShaderInterlock;
public readonly bool SupportsFragmentShaderOrderingIntel;
public readonly bool SupportsGeometryShaderPassthrough;
@@ -50,10 +53,13 @@ namespace Ryujinx.Graphics.GAL
bool supportsBc123Compression,
bool supportsBc45Compression,
bool supportsBc67Compression,
bool supportsEtc2Compression,
bool supports3DTextureCompression,
bool supportsBgraFormat,
bool supportsR4G4Format,
bool supportsR4G4B4A4Format,
bool supportsSnormBufferTextureFormat,
bool supports5BitComponentFormat,
bool supportsFragmentShaderInterlock,
bool supportsFragmentShaderOrderingIntel,
bool supportsGeometryShaderPassthrough,
@@ -83,10 +89,13 @@ namespace Ryujinx.Graphics.GAL
SupportsBc123Compression = supportsBc123Compression;
SupportsBc45Compression = supportsBc45Compression;
SupportsBc67Compression = supportsBc67Compression;
SupportsEtc2Compression = supportsEtc2Compression;
Supports3DTextureCompression = supports3DTextureCompression;
SupportsBgraFormat = supportsBgraFormat;
SupportsR4G4Format = supportsR4G4Format;
SupportsR4G4B4A4Format = supportsR4G4B4A4Format;
SupportsSnormBufferTextureFormat = supportsSnormBufferTextureFormat;
Supports5BitComponentFormat = supports5BitComponentFormat;
SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock;
SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel;
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;

View File

@@ -448,6 +448,27 @@ namespace Ryujinx.Graphics.GAL
return false;
}
/// <summary>
/// Checks if the texture format is 16 bit packed.
/// </summary>
/// <param name="format">Texture format</param>
/// <returns>True if the texture format is 16 bit packed, false otherwise</returns>
public static bool Is16BitPacked(this Format format)
{
switch (format)
{
case Format.B5G6R5Unorm:
case Format.B5G5R5A1Unorm:
case Format.R5G5B5X1Unorm:
case Format.R5G5B5A1Unorm:
case Format.R5G6B5Unorm:
case Format.R4G4B4A4Unorm:
return true;
}
return false;
}
/// <summary>
/// Checks if the texture format is an ASTC format.
/// </summary>
@@ -516,6 +537,27 @@ namespace Ryujinx.Graphics.GAL
return false;
}
/// <summary>
/// Checks if the texture format is an ETC2 format.
/// </summary>
/// <param name="format">Texture format</param>
/// <returns>True if the texture format is an ETC2 format, false otherwise</returns>
public static bool IsEtc2(this Format format)
{
switch (format)
{
case Format.Etc2RgbaSrgb:
case Format.Etc2RgbaUnorm:
case Format.Etc2RgbPtaSrgb:
case Format.Etc2RgbPtaUnorm:
case Format.Etc2RgbSrgb:
case Format.Etc2RgbUnorm:
return true;
}
return false;
}
/// <summary>
/// Checks if the texture format is a BGR format.
/// </summary>

View File

@@ -2,6 +2,7 @@ using Ryujinx.Graphics.Device;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
namespace Ryujinx.Graphics.Gpu.Engine
@@ -21,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
/// Represents a device's state, with a additional shadow state.
/// </summary>
/// <typeparam name="TState">Type of the state</typeparam>
class DeviceStateWithShadow<TState> : IDeviceState where TState : unmanaged, IShadowState
class DeviceStateWithShadow<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TState> : IDeviceState where TState : unmanaged, IShadowState
{
private readonly DeviceState<TState> _state;
private readonly DeviceState<TState> _shadowState;

View File

@@ -100,7 +100,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
_isLinear = (argument & 1) != 0;
_offset = 0;
_size = (int)(BitUtils.AlignUp(state.LineLengthIn, 4) * state.LineCount);
_size = (int)(BitUtils.AlignUp<uint>(state.LineLengthIn, 4) * state.LineCount);
int count = _size / 4;

View File

@@ -98,7 +98,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// <param name="argument">Method call argument</param>
public void DrawEnd(ThreedClass engine, int argument)
{
DrawEnd(engine, _state.State.IndexBufferState.First, (int)_state.State.IndexBufferCount);
DrawEnd(
engine,
_state.State.IndexBufferState.First,
(int)_state.State.IndexBufferCount,
_state.State.VertexBufferDrawState.First,
_state.State.VertexBufferDrawState.Count);
}
/// <summary>
@@ -108,7 +113,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// <param name="engine">3D engine where this method is being called</param>
/// <param name="firstIndex">Index of the first index buffer element used on the draw</param>
/// <param name="indexCount">Number of index buffer elements used on the draw</param>
private void DrawEnd(ThreedClass engine, int firstIndex, int indexCount)
/// <param name="drawFirstVertex">Index of the first vertex used on the draw</param>
/// <param name="drawVertexCount">Number of vertices used on the draw</param>
private void DrawEnd(ThreedClass engine, int firstIndex, int indexCount, int drawFirstVertex, int drawVertexCount)
{
ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable(
_context,
@@ -135,6 +142,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_drawState.FirstIndex = firstIndex;
_drawState.IndexCount = indexCount;
_drawState.DrawFirstVertex = drawFirstVertex;
_drawState.DrawVertexCount = drawVertexCount;
_currentSpecState.SetHasConstantBufferDrawParameters(false);
engine.UpdateState();
@@ -156,10 +165,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_instancedIndexCount = ibCount != 0 ? ibCount : indexCount;
var drawState = _state.State.VertexBufferDrawState;
_instancedDrawStateFirst = drawState.First;
_instancedDrawStateCount = drawState.Count;
_instancedDrawStateFirst = drawFirstVertex;
_instancedDrawStateCount = drawVertexCount;
_drawState.DrawIndexed = false;
@@ -195,7 +202,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{
var drawState = _state.State.VertexBufferDrawState;
_context.Renderer.Pipeline.Draw(drawState.Count, 1, drawState.First, firstInstance);
_context.Renderer.Pipeline.Draw(drawVertexCount, 1, drawFirstVertex, firstInstance);
}
_drawState.DrawIndexed = false;
@@ -216,16 +223,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
bool incrementInstance = (argument & (1 << 26)) != 0;
bool resetInstance = (argument & (1 << 27)) == 0;
if (_state.State.PrimitiveTypeOverrideEnable)
{
PrimitiveTypeOverride typeOverride = _state.State.PrimitiveTypeOverride;
DrawBegin(incrementInstance, resetInstance, typeOverride.Convert());
}
else
{
PrimitiveType type = (PrimitiveType)(argument & 0xffff);
DrawBegin(incrementInstance, resetInstance, type.Convert());
}
PrimitiveType type = (PrimitiveType)(argument & 0xffff);
DrawBegin(incrementInstance, resetInstance, type);
}
/// <summary>
@@ -234,8 +233,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary>
/// <param name="incrementInstance">Indicates if the current instance should be incremented</param>
/// <param name="resetInstance">Indicates if the current instance should be set to zero</param>
/// <param name="topology">Primitive topology</param>
private void DrawBegin(bool incrementInstance, bool resetInstance, PrimitiveTopology topology)
/// <param name="primitiveType">Primitive type</param>
private void DrawBegin(bool incrementInstance, bool resetInstance, PrimitiveType primitiveType)
{
if (incrementInstance)
{
@@ -248,6 +247,18 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_instanceIndex = 0;
}
PrimitiveTopology topology;
if (_state.State.PrimitiveTypeOverrideEnable)
{
PrimitiveTypeOverride typeOverride = _state.State.PrimitiveTypeOverride;
topology = typeOverride.Convert();
}
else
{
topology = primitiveType.Convert();
}
UpdateTopology(topology);
}
@@ -276,46 +287,70 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_drawState.DrawIndexed = true;
}
// TODO: Verify if the index type is implied from the method that is called,
// or if it uses the state index type on hardware.
/// <summary>
/// Performs a indexed draw with a low number of index buffer elements.
/// Performs a indexed draw with 8-bit index buffer elements.
/// </summary>
/// <param name="engine">3D engine where this method is being called</param>
/// <param name="argument">Method call argument</param>
public void DrawIndexedSmall(ThreedClass engine, int argument)
public void DrawIndexBuffer8BeginEndInstanceFirst(ThreedClass engine, int argument)
{
DrawIndexedSmall(engine, argument, false);
DrawIndexBufferBeginEndInstance(engine, argument, false);
}
/// <summary>
/// Performs a indexed draw with a low number of index buffer elements.
/// Performs a indexed draw with 16-bit index buffer elements.
/// </summary>
/// <param name="engine">3D engine where this method is being called</param>
/// <param name="argument">Method call argument</param>
public void DrawIndexedSmall2(ThreedClass engine, int argument)
public void DrawIndexBuffer16BeginEndInstanceFirst(ThreedClass engine, int argument)
{
DrawIndexedSmall(engine, argument);
DrawIndexBufferBeginEndInstance(engine, argument, false);
}
/// <summary>
/// Performs a indexed draw with a low number of index buffer elements,
/// Performs a indexed draw with 32-bit index buffer elements.
/// </summary>
/// <param name="engine">3D engine where this method is being called</param>
/// <param name="argument">Method call argument</param>
public void DrawIndexBuffer32BeginEndInstanceFirst(ThreedClass engine, int argument)
{
DrawIndexBufferBeginEndInstance(engine, argument, false);
}
/// <summary>
/// Performs a indexed draw with 8-bit index buffer elements,
/// while also pre-incrementing the current instance value.
/// </summary>
/// <param name="engine">3D engine where this method is being called</param>
/// <param name="argument">Method call argument</param>
public void DrawIndexedSmallIncInstance(ThreedClass engine, int argument)
public void DrawIndexBuffer8BeginEndInstanceSubsequent(ThreedClass engine, int argument)
{
DrawIndexedSmall(engine, argument, true);
DrawIndexBufferBeginEndInstance(engine, argument, true);
}
/// <summary>
/// Performs a indexed draw with a low number of index buffer elements,
/// Performs a indexed draw with 16-bit index buffer elements,
/// while also pre-incrementing the current instance value.
/// </summary>
/// <param name="engine">3D engine where this method is being called</param>
/// <param name="argument">Method call argument</param>
public void DrawIndexedSmallIncInstance2(ThreedClass engine, int argument)
public void DrawIndexBuffer16BeginEndInstanceSubsequent(ThreedClass engine, int argument)
{
DrawIndexedSmallIncInstance(engine, argument);
DrawIndexBufferBeginEndInstance(engine, argument, true);
}
/// <summary>
/// Performs a indexed draw with 32-bit index buffer elements,
/// while also pre-incrementing the current instance value.
/// </summary>
/// <param name="engine">3D engine where this method is being called</param>
/// <param name="argument">Method call argument</param>
public void DrawIndexBuffer32BeginEndInstanceSubsequent(ThreedClass engine, int argument)
{
DrawIndexBufferBeginEndInstance(engine, argument, true);
}
/// <summary>
@@ -325,11 +360,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// <param name="engine">3D engine where this method is being called</param>
/// <param name="argument">Method call argument</param>
/// <param name="instanced">True to increment the current instance value, false otherwise</param>
private void DrawIndexedSmall(ThreedClass engine, int argument, bool instanced)
private void DrawIndexBufferBeginEndInstance(ThreedClass engine, int argument, bool instanced)
{
PrimitiveTypeOverride typeOverride = _state.State.PrimitiveTypeOverride;
DrawBegin(instanced, !instanced, typeOverride.Convert());
DrawBegin(instanced, !instanced, (PrimitiveType)((argument >> 28) & 0xf));
int firstIndex = argument & 0xffff;
int indexCount = (argument >> 16) & 0xfff;
@@ -339,7 +372,52 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_drawState.DrawIndexed = true;
engine.ForceStateDirty(IndexBufferCountMethodOffset * 4);
DrawEnd(engine, firstIndex, indexCount);
DrawEnd(engine, firstIndex, indexCount, 0, 0);
_drawState.DrawIndexed = oldDrawIndexed;
}
/// <summary>
/// Performs a non-indexed draw with the specified topology, index and count.
/// </summary>
/// <param name="engine">3D engine where this method is being called</param>
/// <param name="argument">Method call argument</param>
public void DrawVertexArrayBeginEndInstanceFirst(ThreedClass engine, int argument)
{
DrawVertexArrayBeginEndInstance(engine, argument, false);
}
/// <summary>
/// Performs a non-indexed draw with the specified topology, index and count,
/// while incrementing the current instance.
/// </summary>
/// <param name="engine">3D engine where this method is being called</param>
/// <param name="argument">Method call argument</param>
public void DrawVertexArrayBeginEndInstanceSubsequent(ThreedClass engine, int argument)
{
DrawVertexArrayBeginEndInstance(engine, argument, true);
}
/// <summary>
/// Performs a indexed draw with a low number of index buffer elements,
/// while optionally also pre-incrementing the current instance value.
/// </summary>
/// <param name="engine">3D engine where this method is being called</param>
/// <param name="argument">Method call argument</param>
/// <param name="instanced">True to increment the current instance value, false otherwise</param>
private void DrawVertexArrayBeginEndInstance(ThreedClass engine, int argument, bool instanced)
{
DrawBegin(instanced, !instanced, (PrimitiveType)((argument >> 28) & 0xf));
int firstVertex = argument & 0xffff;
int vertexCount = (argument >> 16) & 0xfff;
bool oldDrawIndexed = _drawState.DrawIndexed;
_drawState.DrawIndexed = false;
engine.ForceStateDirty(VertexBufferFirstMethodOffset * 4);
DrawEnd(engine, 0, 0, firstVertex, vertexCount);
_drawState.DrawIndexed = oldDrawIndexed;
}
@@ -449,8 +527,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
}
else
{
_state.State.VertexBufferDrawState.First = firstVertex;
_state.State.VertexBufferDrawState.Count = count;
_drawState.DrawFirstVertex = firstVertex;
_drawState.DrawVertexCount = count;
engine.ForceStateDirty(VertexBufferFirstMethodOffset * 4);
}

View File

@@ -17,6 +17,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary>
public int IndexCount;
/// <summary>
/// First vertex used on non-indexed draws. This value is stored somewhere else on indexed draws.
/// </summary>
public int DrawFirstVertex;
/// <summary>
/// Vertex count used on non-indexed draws. Indexed draws have a index count instead.
/// </summary>
public int DrawVertexCount;
/// <summary>
/// Indicates if the next draw will be a indexed draw.
/// </summary>

View File

@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -39,7 +40,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// GPU state update tracker.
/// </summary>
/// <typeparam name="TState">State type</typeparam>
class StateUpdateTracker<TState>
class StateUpdateTracker<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] TState>
{
private const int BlockSize = 0xe00;
private const int RegisterSize = sizeof(uint);

View File

@@ -1,7 +1,5 @@
using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Shader;
@@ -991,6 +989,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
bool drawIndexed = _drawState.DrawIndexed;
bool drawIndirect = _drawState.DrawIndirect;
int drawFirstVertex = _drawState.DrawFirstVertex;
int drawVertexCount = _drawState.DrawVertexCount;
uint vbEnableMask = 0;
for (int index = 0; index < Constants.TotalVertexBuffers; index++)
@@ -1052,9 +1052,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
int firstInstance = (int)_state.State.FirstInstance;
var drawState = _state.State.VertexBufferDrawState;
size = Math.Min(vbSize, (ulong)((firstInstance + drawState.First + drawState.Count) * stride));
size = Math.Min(vbSize, (ulong)((firstInstance + drawFirstVertex + drawVertexCount) * stride));
}
_pipeline.VertexBuffers[index] = new BufferPipelineDescriptor(_channel.MemoryManager.IsMapped(address), stride, divisor);
@@ -1295,7 +1293,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
for (int stageIndex = 0; stageIndex < Constants.ShaderStages; stageIndex++)
{
_currentProgramInfo[stageIndex] = gs.Shaders[stageIndex + 1]?.Info;
ShaderProgramInfo info = gs.Shaders[stageIndex + 1]?.Info;
if (info?.UsesRtLayer == true)
{
_vtgWritesRtLayer = true;
}
_currentProgramInfo[stageIndex] = info;
}
_context.Renderer.Pipeline.SetProgram(gs.HostProgram);

View File

@@ -42,6 +42,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{ nameof(ThreedClassState.TextureBarrier), new RwCallback(TextureBarrier, null) },
{ nameof(ThreedClassState.TextureBarrierTiled), new RwCallback(TextureBarrierTiled, null) },
{ nameof(ThreedClassState.DrawTextureSrcY), new RwCallback(DrawTexture, null) },
{ nameof(ThreedClassState.DrawVertexArrayBeginEndInstanceFirst), new RwCallback(DrawVertexArrayBeginEndInstanceFirst, null) },
{ nameof(ThreedClassState.DrawVertexArrayBeginEndInstanceSubsequent), new RwCallback(DrawVertexArrayBeginEndInstanceSubsequent, null) },
{ nameof(ThreedClassState.VbElementU8), new RwCallback(VbElementU8, null) },
{ nameof(ThreedClassState.VbElementU16), new RwCallback(VbElementU16, null) },
{ nameof(ThreedClassState.VbElementU32), new RwCallback(VbElementU32, null) },
@@ -49,10 +51,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{ nameof(ThreedClassState.RenderEnableCondition), new RwCallback(null, Zero) },
{ nameof(ThreedClassState.DrawEnd), new RwCallback(DrawEnd, null) },
{ nameof(ThreedClassState.DrawBegin), new RwCallback(DrawBegin, null) },
{ nameof(ThreedClassState.DrawIndexedSmall), new RwCallback(DrawIndexedSmall, null) },
{ nameof(ThreedClassState.DrawIndexedSmall2), new RwCallback(DrawIndexedSmall2, null) },
{ nameof(ThreedClassState.DrawIndexedSmallIncInstance), new RwCallback(DrawIndexedSmallIncInstance, null) },
{ nameof(ThreedClassState.DrawIndexedSmallIncInstance2), new RwCallback(DrawIndexedSmallIncInstance2, null) },
{ nameof(ThreedClassState.DrawIndexBuffer32BeginEndInstanceFirst), new RwCallback(DrawIndexBuffer32BeginEndInstanceFirst, null) },
{ nameof(ThreedClassState.DrawIndexBuffer16BeginEndInstanceFirst), new RwCallback(DrawIndexBuffer16BeginEndInstanceFirst, null) },
{ nameof(ThreedClassState.DrawIndexBuffer8BeginEndInstanceFirst), new RwCallback(DrawIndexBuffer8BeginEndInstanceFirst, null) },
{ nameof(ThreedClassState.DrawIndexBuffer32BeginEndInstanceSubsequent), new RwCallback(DrawIndexBuffer32BeginEndInstanceSubsequent, null) },
{ nameof(ThreedClassState.DrawIndexBuffer16BeginEndInstanceSubsequent), new RwCallback(DrawIndexBuffer16BeginEndInstanceSubsequent, null) },
{ nameof(ThreedClassState.DrawIndexBuffer8BeginEndInstanceSubsequent), new RwCallback(DrawIndexBuffer8BeginEndInstanceSubsequent, null) },
{ nameof(ThreedClassState.IndexBufferCount), new RwCallback(SetIndexBufferCount, null) },
{ nameof(ThreedClassState.Clear), new RwCallback(Clear, null) },
{ nameof(ThreedClassState.SemaphoreControl), new RwCallback(Report, null) },
@@ -303,6 +307,25 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_drawManager.DrawTexture(this, argument);
}
/// <summary>
/// Performs a non-indexed draw with the specified topology, index and count.
/// </summary>
/// <param name="argument">Method call argument</param>
private void DrawVertexArrayBeginEndInstanceFirst(int argument)
{
_drawManager.DrawVertexArrayBeginEndInstanceFirst(this, argument);
}
/// <summary>
/// Performs a non-indexed draw with the specified topology, index and count,
/// while incrementing the current instance.
/// </summary>
/// <param name="argument">Method call argument</param>
private void DrawVertexArrayBeginEndInstanceSubsequent(int argument)
{
_drawManager.DrawVertexArrayBeginEndInstanceSubsequent(this, argument);
}
/// <summary>
/// Pushes four 8-bit index buffer elements.
/// </summary>
@@ -370,41 +393,60 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
}
/// <summary>
/// Performs a indexed draw with a low number of index buffer elements.
/// Performs a indexed draw with 8-bit index buffer elements.
/// </summary>
/// <param name="argument">Method call argument</param>
private void DrawIndexedSmall(int argument)
private void DrawIndexBuffer8BeginEndInstanceFirst(int argument)
{
_drawManager.DrawIndexedSmall(this, argument);
_drawManager.DrawIndexBuffer8BeginEndInstanceFirst(this, argument);
}
/// <summary>
/// Performs a indexed draw with a low number of index buffer elements.
/// Performs a indexed draw with 16-bit index buffer elements.
/// </summary>
/// <param name="argument">Method call argument</param>
private void DrawIndexedSmall2(int argument)
private void DrawIndexBuffer16BeginEndInstanceFirst(int argument)
{
_drawManager.DrawIndexedSmall2(this, argument);
_drawManager.DrawIndexBuffer16BeginEndInstanceFirst(this, argument);
}
/// <summary>
/// Performs a indexed draw with a low number of index buffer elements,
/// Performs a indexed draw with 32-bit index buffer elements.
/// </summary>
/// <param name="argument">Method call argument</param>
private void DrawIndexBuffer32BeginEndInstanceFirst(int argument)
{
_drawManager.DrawIndexBuffer32BeginEndInstanceFirst(this, argument);
}
/// <summary>
/// Performs a indexed draw with 8-bit index buffer elements,
/// while also pre-incrementing the current instance value.
/// </summary>
/// <param name="argument">Method call argument</param>
private void DrawIndexedSmallIncInstance(int argument)
private void DrawIndexBuffer8BeginEndInstanceSubsequent(int argument)
{
_drawManager.DrawIndexedSmallIncInstance(this, argument);
_drawManager.DrawIndexBuffer8BeginEndInstanceSubsequent(this, argument);
}
/// <summary>
/// Performs a indexed draw with a low number of index buffer elements,
/// Performs a indexed draw with 16-bit index buffer elements,
/// while also pre-incrementing the current instance value.
/// </summary>
/// <param name="argument">Method call argument</param>
private void DrawIndexedSmallIncInstance2(int argument)
private void DrawIndexBuffer16BeginEndInstanceSubsequent(int argument)
{
_drawManager.DrawIndexedSmallIncInstance2(this, argument);
_drawManager.DrawIndexBuffer16BeginEndInstanceSubsequent(this, argument);
}
/// <summary>
/// Performs a indexed draw with 32-bit index buffer elements,
/// while also pre-incrementing the current instance value.
/// </summary>
/// <param name="argument">Method call argument</param>
private void DrawIndexBuffer32BeginEndInstanceSubsequent(int argument)
{
_drawManager.DrawIndexBuffer32BeginEndInstanceSubsequent(this, argument);
}
/// <summary>

View File

@@ -813,7 +813,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
public uint ClearFlags;
public fixed uint Reserved10FC[25];
public Array32<VertexAttribState> VertexAttribState;
public fixed uint Reserved11E0[15];
public fixed uint Reserved11E0[13];
public uint DrawVertexArrayBeginEndInstanceFirst;
public uint DrawVertexArrayBeginEndInstanceSubsequent;
public RtControl RtControl;
public fixed uint Reserved1220[2];
public Size3D RtDepthStencilSize;
@@ -888,12 +890,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
public fixed uint Reserved164C[95];
public IndexBufferState IndexBufferState;
public uint IndexBufferCount;
public uint DrawIndexedSmall;
public uint DrawIndexedSmall2;
public uint Reserved17EC;
public uint DrawIndexedSmallIncInstance;
public uint DrawIndexedSmallIncInstance2;
public fixed uint Reserved17F8[33];
public uint DrawIndexBuffer32BeginEndInstanceFirst;
public uint DrawIndexBuffer16BeginEndInstanceFirst;
public uint DrawIndexBuffer8BeginEndInstanceFirst;
public uint DrawIndexBuffer32BeginEndInstanceSubsequent;
public uint DrawIndexBuffer16BeginEndInstanceSubsequent;
public uint DrawIndexBuffer8BeginEndInstanceSubsequent;
public fixed uint Reserved17FC[32];
public float DepthBiasClamp;
public Array16<Boolean32> VertexBufferInstanced;
public fixed uint Reserved18C0[20];

View File

@@ -208,8 +208,10 @@ namespace Ryujinx.Graphics.Gpu.Image
Bc6HSf16Float = Bc6HSf16 | RFloat | GFloat | BFloat | AFloat, // 0x7ff90
Bc6HUf16Float = Bc6HUf16 | RFloat | GFloat | BFloat | AFloat, // 0x7ff91
Etc2RgbUnorm = Etc2Rgb | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24906
Etc2RgbPtaUnorm = Etc2RgbPta | RUnorm | GUnorm | BUnorm | AUnorm, // 0x2490a
Etc2RgbaUnorm = Etc2Rgba | RUnorm | GUnorm | BUnorm | AUnorm, // 0x2490b
Etc2RgbUnormSrgb = Etc2Rgb | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4906
Etc2RgbPtaUnormSrgb = Etc2RgbPta | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa490a
Etc2RgbaUnormSrgb = Etc2Rgba | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa490b
Astc2D4x4Unorm = Astc2D4x4 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24940
Astc2D5x4Unorm = Astc2D5x4 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24950
@@ -429,8 +431,10 @@ namespace Ryujinx.Graphics.Gpu.Image
{ TextureFormat.Bc6HSf16Float, new FormatInfo(Format.Bc6HSfloat, 4, 4, 16, 4) },
{ TextureFormat.Bc6HUf16Float, new FormatInfo(Format.Bc6HUfloat, 4, 4, 16, 4) },
{ TextureFormat.Etc2RgbUnorm, new FormatInfo(Format.Etc2RgbUnorm, 4, 4, 8, 3) },
{ TextureFormat.Etc2RgbPtaUnorm, new FormatInfo(Format.Etc2RgbPtaUnorm, 4, 4, 8, 4) },
{ TextureFormat.Etc2RgbaUnorm, new FormatInfo(Format.Etc2RgbaUnorm, 4, 4, 16, 4) },
{ TextureFormat.Etc2RgbUnormSrgb, new FormatInfo(Format.Etc2RgbSrgb, 4, 4, 8, 3) },
{ TextureFormat.Etc2RgbPtaUnormSrgb, new FormatInfo(Format.Etc2RgbPtaSrgb, 4, 4, 8, 4) },
{ TextureFormat.Etc2RgbaUnormSrgb, new FormatInfo(Format.Etc2RgbaSrgb, 4, 4, 16, 4) },
{ TextureFormat.Astc2D4x4Unorm, new FormatInfo(Format.Astc4x4Unorm, 4, 4, 16, 4) },
{ TextureFormat.Astc2D5x4Unorm, new FormatInfo(Format.Astc5x4Unorm, 5, 4, 16, 4) },

View File

@@ -857,9 +857,23 @@ namespace Ryujinx.Graphics.Gpu.Image
result = decoded;
}
else if (!_context.Capabilities.SupportsR4G4Format && Format == Format.R4G4Unorm)
else if (!_context.Capabilities.SupportsEtc2Compression && Format.IsEtc2())
{
result = PixelConverter.ConvertR4G4ToR4G4B4A4(result);
switch (Format)
{
case Format.Etc2RgbaSrgb:
case Format.Etc2RgbaUnorm:
result = ETC2Decoder.DecodeRgba(result, width, height, depth, levels, layers);
break;
case Format.Etc2RgbPtaSrgb:
case Format.Etc2RgbPtaUnorm:
result = ETC2Decoder.DecodePta(result, width, height, depth, levels, layers);
break;
case Format.Etc2RgbSrgb:
case Format.Etc2RgbUnorm:
result = ETC2Decoder.DecodeRgb(result, width, height, depth, levels, layers);
break;
}
}
else if (!TextureCompatibility.HostSupportsBcFormat(Format, Target, _context.Capabilities))
{
@@ -895,6 +909,43 @@ namespace Ryujinx.Graphics.Gpu.Image
break;
}
}
else if (!_context.Capabilities.SupportsR4G4Format && Format == Format.R4G4Unorm)
{
result = PixelConverter.ConvertR4G4ToR4G4B4A4(result, width);
if (!_context.Capabilities.SupportsR4G4B4A4Format)
{
result = PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result, width);
}
}
else if (Format == Format.R4G4B4A4Unorm)
{
if (!_context.Capabilities.SupportsR4G4B4A4Format)
{
result = PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result, width);
}
}
else if (!_context.Capabilities.Supports5BitComponentFormat && Format.Is16BitPacked())
{
switch (Format)
{
case Format.B5G6R5Unorm:
case Format.R5G6B5Unorm:
result = PixelConverter.ConvertR5G6B5ToR8G8B8A8(result, width);
break;
case Format.B5G5R5A1Unorm:
case Format.R5G5B5X1Unorm:
case Format.R5G5B5A1Unorm:
result = PixelConverter.ConvertR5G5B5ToR8G8B8A8(result, width, Format == Format.R5G5B5X1Unorm);
break;
case Format.A1B5G5R5Unorm:
result = PixelConverter.ConvertA1B5G5R5ToR8G8B8A8(result, width);
break;
case Format.R4G4B4A4Unorm:
result = PixelConverter.ConvertR4G4B4A4ToR8G8B8A8(result, width);
break;
}
}
return result;
}

View File

@@ -320,10 +320,15 @@ namespace Ryujinx.Graphics.Gpu.Image
// Check if the texture pool has been modified since bindings were last committed.
// If it wasn't, then it's possible to avoid looking up textures again when the handle remains the same.
bool poolModified = _cachedTexturePool != texturePool || _cachedSamplerPool != samplerPool;
if (_cachedTexturePool != texturePool || _cachedSamplerPool != samplerPool)
{
Rebind();
_cachedTexturePool = texturePool;
_cachedSamplerPool = samplerPool;
_cachedTexturePool = texturePool;
_cachedSamplerPool = samplerPool;
}
bool poolModified = false;
if (texturePool != null)
{

View File

@@ -67,6 +67,10 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>A host compatible format</returns>
public static FormatInfo ToHostCompatibleFormat(TextureInfo info, Capabilities caps)
{
// The host API does not support those compressed formats.
// We assume software decompression will be done for those textures,
// and so we adjust the format here to match the decompressor output.
if (!caps.SupportsAstcCompression)
{
if (info.FormatInfo.Format.IsAstcUnorm())
@@ -83,16 +87,8 @@ namespace Ryujinx.Graphics.Gpu.Image
}
}
if (!caps.SupportsR4G4Format && info.FormatInfo.Format == Format.R4G4Unorm)
{
return new FormatInfo(Format.R4G4B4A4Unorm, 1, 1, 2, 4);
}
if (!HostSupportsBcFormat(info.FormatInfo.Format, info.Target, caps))
{
// The host API does not this compressed format.
// We assume software decompression will be done for those textures,
// and so we adjust the format here to match the decompressor output.
switch (info.FormatInfo.Format)
{
case Format.Bc1RgbaSrgb:
@@ -119,6 +115,45 @@ namespace Ryujinx.Graphics.Gpu.Image
}
}
if (!caps.SupportsEtc2Compression)
{
switch (info.FormatInfo.Format)
{
case Format.Etc2RgbaSrgb:
case Format.Etc2RgbPtaSrgb:
case Format.Etc2RgbSrgb:
return new FormatInfo(Format.R8G8B8A8Srgb, 1, 1, 4, 4);
case Format.Etc2RgbaUnorm:
case Format.Etc2RgbPtaUnorm:
case Format.Etc2RgbUnorm:
return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
}
}
if (!caps.SupportsR4G4Format && info.FormatInfo.Format == Format.R4G4Unorm)
{
if (caps.SupportsR4G4B4A4Format)
{
return new FormatInfo(Format.R4G4B4A4Unorm, 1, 1, 2, 4);
}
else
{
return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
}
}
if (info.FormatInfo.Format == Format.R4G4B4A4Unorm)
{
if (!caps.SupportsR4G4B4A4Format)
{
return new FormatInfo(Format.R8G8B8A8Unorm, 1, 1, 4, 4);
}
}
else if (!caps.Supports5BitComponentFormat && info.FormatInfo.Format.Is16BitPacked())
{
return new FormatInfo(info.FormatInfo.Format.IsBgr() ? Format.B8G8R8A8Unorm : Format.R8G8B8A8Unorm, 1, 1, 4, 4);
}
return info.FormatInfo;
}

View File

@@ -252,7 +252,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
RecordStorageAlignment(_cpStorageBuffers, index, gpuVa);
gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
gpuVa = BitUtils.AlignDown<ulong>(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);
@@ -276,7 +276,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
RecordStorageAlignment(buffers, index, gpuVa);
gpuVa = BitUtils.AlignDown(gpuVa, _context.Capabilities.StorageBufferOffsetAlignment);
gpuVa = BitUtils.AlignDown<ulong>(gpuVa, (ulong)_context.Capabilities.StorageBufferOffsetAlignment);
ulong address = _channel.MemoryManager.Physical.BufferCache.TranslateAndCreateBuffer(_channel.MemoryManager, gpuVa, size);

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 4069;
private const uint CodeGenVersion = 4106;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";

View File

@@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
{
CachedShaderStage currentStage = stages[i];
if (currentStage != null && currentStage.Info.Stage == stage && currentStage.Info != null)
if (currentStage?.Info != null && currentStage.Info.Stage == stage)
{
return ShaderCache.GetBindings(currentStage.Info);
}

View File

@@ -5,7 +5,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
{
static class FFmpegApi
static partial class FFmpegApi
{
public const string AvCodecLibraryName = "avcodec";
public const string AvUtilLibraryName = "avutil";
@@ -78,52 +78,52 @@ namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
public unsafe delegate void av_log_set_callback_callback(void* a0, AVLog level, [MarshalAs(UnmanagedType.LPUTF8Str)] string a2, byte* a3);
[DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)]
internal static unsafe extern AVFrame* av_frame_alloc();
[LibraryImport(AvUtilLibraryName)]
internal static unsafe partial AVFrame* av_frame_alloc();
[DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)]
internal static unsafe extern void av_frame_unref(AVFrame* frame);
[LibraryImport(AvUtilLibraryName)]
internal static unsafe partial void av_frame_unref(AVFrame* frame);
[DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)]
internal static unsafe extern void av_free(AVFrame* frame);
[LibraryImport(AvUtilLibraryName)]
internal static unsafe partial void av_free(AVFrame* frame);
[DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)]
internal static unsafe extern void av_log_set_level(AVLog level);
[LibraryImport(AvUtilLibraryName)]
internal static unsafe partial void av_log_set_level(AVLog level);
[DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)]
internal static unsafe extern void av_log_set_callback(av_log_set_callback_callback callback);
[LibraryImport(AvUtilLibraryName)]
internal static unsafe partial void av_log_set_callback(av_log_set_callback_callback callback);
[DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)]
internal static unsafe extern AVLog av_log_get_level();
[LibraryImport(AvUtilLibraryName)]
internal static unsafe partial AVLog av_log_get_level();
[DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)]
internal static unsafe extern void av_log_format_line(void* ptr, AVLog level, [MarshalAs(UnmanagedType.LPUTF8Str)] string fmt, byte* vl, byte* line, int lineSize, int* printPrefix);
[LibraryImport(AvUtilLibraryName)]
internal static unsafe partial void av_log_format_line(void* ptr, AVLog level, [MarshalAs(UnmanagedType.LPUTF8Str)] string fmt, byte* vl, byte* line, int lineSize, int* printPrefix);
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
internal static unsafe extern AVCodec* avcodec_find_decoder(AVCodecID id);
[LibraryImport(AvCodecLibraryName)]
internal static unsafe partial AVCodec* avcodec_find_decoder(AVCodecID id);
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
internal static unsafe extern AVCodecContext* avcodec_alloc_context3(AVCodec* codec);
[LibraryImport(AvCodecLibraryName)]
internal static unsafe partial AVCodecContext* avcodec_alloc_context3(AVCodec* codec);
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
internal static unsafe extern int avcodec_open2(AVCodecContext* avctx, AVCodec* codec, void **options);
[LibraryImport(AvCodecLibraryName)]
internal static unsafe partial int avcodec_open2(AVCodecContext* avctx, AVCodec* codec, void **options);
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
internal static unsafe extern int avcodec_close(AVCodecContext* avctx);
[LibraryImport(AvCodecLibraryName)]
internal static unsafe partial int avcodec_close(AVCodecContext* avctx);
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
internal static unsafe extern void avcodec_free_context(AVCodecContext** avctx);
[LibraryImport(AvCodecLibraryName)]
internal static unsafe partial void avcodec_free_context(AVCodecContext** avctx);
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
internal static unsafe extern AVPacket* av_packet_alloc();
[LibraryImport(AvCodecLibraryName)]
internal static unsafe partial AVPacket* av_packet_alloc();
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
internal static unsafe extern void av_packet_unref(AVPacket* pkt);
[LibraryImport(AvCodecLibraryName)]
internal static unsafe partial void av_packet_unref(AVPacket* pkt);
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
internal static unsafe extern void av_packet_free(AVPacket** pkt);
[LibraryImport(AvCodecLibraryName)]
internal static unsafe partial void av_packet_free(AVPacket** pkt);
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)]
internal static unsafe extern int avcodec_version();
[LibraryImport(AvCodecLibraryName)]
internal static unsafe partial int avcodec_version();
}
}

View File

@@ -5,7 +5,7 @@ using System.Runtime.Versioning;
namespace Ryujinx.Graphics.OpenGL.Helper
{
[SupportedOSPlatform("linux")]
internal static class GLXHelper
internal static partial class GLXHelper
{
private const string LibraryName = "glx.dll";
@@ -30,7 +30,7 @@ namespace Ryujinx.Graphics.OpenGL.Helper
});
}
[DllImport(LibraryName, EntryPoint = "glXGetCurrentContext")]
public static extern IntPtr GetCurrentContext();
[LibraryImport(LibraryName, EntryPoint = "glXGetCurrentContext")]
public static partial IntPtr GetCurrentContext();
}
}

View File

@@ -5,11 +5,11 @@ using System.Runtime.Versioning;
namespace Ryujinx.Graphics.OpenGL.Helper
{
[SupportedOSPlatform("windows")]
internal static class WGLHelper
internal static partial class WGLHelper
{
private const string LibraryName = "OPENGL32.DLL";
[DllImport(LibraryName, EntryPoint = "wglGetCurrentContext")]
public extern static IntPtr GetCurrentContext();
[LibraryImport(LibraryName, EntryPoint = "wglGetCurrentContext")]
public static partial IntPtr GetCurrentContext();
}
}

View File

@@ -110,10 +110,13 @@ namespace Ryujinx.Graphics.OpenGL
supportsBc123Compression: HwCapabilities.SupportsTextureCompressionS3tc,
supportsBc45Compression: HwCapabilities.SupportsTextureCompressionRgtc,
supportsBc67Compression: true, // Should check BPTC extension, but for some reason NVIDIA is not exposing the extension.
supportsEtc2Compression: true,
supports3DTextureCompression: false,
supportsBgraFormat: false,
supportsR4G4Format: false,
supportsR4G4B4A4Format: true,
supportsSnormBufferTextureFormat: false,
supports5BitComponentFormat: true,
supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock,
supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering,
supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough,

View File

@@ -6,7 +6,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OpenTK.Graphics" Version="4.7.5" />
<PackageReference Include="OpenTK.Graphics" />
</ItemGroup>
<ItemGroup>

View File

@@ -262,7 +262,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
value = (value >> operation.Index * 16) & 0xffff;
operation.TurnIntoCopy(ConstF(HalfConversion.HalfToSingle(value)));
operation.TurnIntoCopy(ConstF((float)BitConverter.UInt16BitsToHalf((ushort)value)));
}
private static void FPNegate(Operation operation)

View File

@@ -1,47 +0,0 @@
using System;
namespace Ryujinx.Graphics.Shader.Translation.Optimizations
{
static class HalfConversion
{
public static float HalfToSingle(int value)
{
int mantissa = (value >> 0) & 0x3ff;
int exponent = (value >> 10) & 0x1f;
int sign = (value >> 15) & 0x1;
if (exponent == 0x1f)
{
// NaN or Infinity.
mantissa <<= 13;
exponent = 0xff;
}
else if (exponent != 0 || mantissa != 0 )
{
if (exponent == 0)
{
// Denormal.
int e = -1;
int m = mantissa;
do
{
e++;
m <<= 1;
}
while ((m & 0x400) == 0);
mantissa = m & 0x3ff;
exponent = e;
}
mantissa <<= 13;
exponent = 127 - 15 + exponent;
}
int output = (sign << 31) | (exponent << 23) | mantissa;
return BitConverter.Int32BitsToSingle(output);
}
}
}

View File

@@ -0,0 +1,682 @@
using Ryujinx.Common;
using System;
using System.Buffers.Binary;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Texture
{
public static class ETC2Decoder
{
private const uint AlphaMask = 0xff000000u;
private const int BlockWidth = 4;
private const int BlockHeight = 4;
private static readonly int[][] _etc1Lut =
{
new int[] { 2, 8, -2, -8 },
new int[] { 5, 17, -5, -17 },
new int[] { 9, 29, -9, -29 },
new int[] { 13, 42, -13, -42 },
new int[] { 18, 60, -18, -60 },
new int[] { 24, 80, -24, -80 },
new int[] { 33, 106, -33, -106 },
new int[] { 47, 183, -47, -183 }
};
private static readonly int[] _etc2Lut =
{
3, 6, 11, 16, 23, 32, 41, 64
};
private static readonly int[][] _etc2AlphaLut =
{
new int[] { -3, -6, -9, -15, 2, 5, 8, 14 },
new int[] { -3, -7, -10, -13, 2, 6, 9, 12 },
new int[] { -2, -5, -8, -13, 1, 4, 7, 12 },
new int[] { -2, -4, -6, -13, 1, 3, 5, 12 },
new int[] { -3, -6, -8, -12, 2, 5, 7, 11 },
new int[] { -3, -7, -9, -11, 2, 6, 8, 10 },
new int[] { -4, -7, -8, -11, 3, 6, 7, 10 },
new int[] { -3, -5, -8, -11, 2, 4, 7, 10 },
new int[] { -2, -6, -8, -10, 1, 5, 7, 9 },
new int[] { -2, -5, -8, -10, 1, 4, 7, 9 },
new int[] { -2, -4, -8, -10, 1, 3, 7, 9 },
new int[] { -2, -5, -7, -10, 1, 4, 6, 9 },
new int[] { -3, -4, -7, -10, 2, 3, 6, 9 },
new int[] { -1, -2, -3, -10, 0, 1, 2, 9 },
new int[] { -4, -6, -8, -9, 3, 5, 7, 8 },
new int[] { -3, -5, -7, -9, 2, 4, 6, 8 }
};
public static byte[] DecodeRgb(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
{
ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
int inputOffset = 0;
byte[] output = new byte[CalculateOutputSize(width, height, depth, levels, layers)];
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output);
Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
int imageBaseOOffs = 0;
for (int l = 0; l < levels; l++)
{
int wInBlocks = BitUtils.DivRoundUp(width, BlockWidth);
int hInBlocks = BitUtils.DivRoundUp(height, BlockHeight);
for (int l2 = 0; l2 < layers; l2++)
{
for (int z = 0; z < depth; z++)
{
for (int y = 0; y < hInBlocks; y++)
{
int ty = y * BlockHeight;
int bh = Math.Min(BlockHeight, height - ty);
for (int x = 0; x < wInBlocks; x++)
{
int tx = x * BlockWidth;
int bw = Math.Min(BlockWidth, width - tx);
ulong colorBlock = dataUlong[inputOffset++];
DecodeBlock(tile, colorBlock);
for (int py = 0; py < bh; py++)
{
int oOffsBase = imageBaseOOffs + ((ty + py) * width) + tx;
for (int px = 0; px < bw; px++)
{
int oOffs = oOffsBase + px;
outputUint[oOffs] = tile[py * BlockWidth + px] | AlphaMask;
}
}
}
}
imageBaseOOffs += width * height;
}
}
width = Math.Max(1, width >> 1);
height = Math.Max(1, height >> 1);
depth = Math.Max(1, depth >> 1);
}
return output;
}
public static byte[] DecodePta(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
{
ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
int inputOffset = 0;
byte[] output = new byte[CalculateOutputSize(width, height, depth, levels, layers)];
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output);
Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
int imageBaseOOffs = 0;
for (int l = 0; l < levels; l++)
{
int wInBlocks = BitUtils.DivRoundUp(width, BlockWidth);
int hInBlocks = BitUtils.DivRoundUp(height, BlockHeight);
for (int l2 = 0; l2 < layers; l2++)
{
for (int z = 0; z < depth; z++)
{
for (int y = 0; y < hInBlocks; y++)
{
int ty = y * BlockHeight;
int bh = Math.Min(BlockHeight, height - ty);
for (int x = 0; x < wInBlocks; x++)
{
int tx = x * BlockWidth;
int bw = Math.Min(BlockWidth, width - tx);
ulong colorBlock = dataUlong[inputOffset++];
DecodeBlockPta(tile, colorBlock);
for (int py = 0; py < bh; py++)
{
int oOffsBase = imageBaseOOffs + ((ty + py) * width) + tx;
tile.Slice(py * BlockWidth, bw).CopyTo(outputUint.Slice(oOffsBase, bw));
}
}
}
imageBaseOOffs += width * height;
}
}
width = Math.Max(1, width >> 1);
height = Math.Max(1, height >> 1);
depth = Math.Max(1, depth >> 1);
}
return output;
}
public static byte[] DecodeRgba(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
{
ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
int inputOffset = 0;
byte[] output = new byte[CalculateOutputSize(width, height, depth, levels, layers)];
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output);
Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
int imageBaseOOffs = 0;
for (int l = 0; l < levels; l++)
{
int wInBlocks = BitUtils.DivRoundUp(width, BlockWidth);
int hInBlocks = BitUtils.DivRoundUp(height, BlockHeight);
for (int l2 = 0; l2 < layers; l2++)
{
for (int z = 0; z < depth; z++)
{
for (int y = 0; y < hInBlocks; y++)
{
int ty = y * BlockHeight;
int bh = Math.Min(BlockHeight, height - ty);
for (int x = 0; x < wInBlocks; x++)
{
int tx = x * BlockWidth;
int bw = Math.Min(BlockWidth, width - tx);
ulong alphaBlock = dataUlong[inputOffset];
ulong colorBlock = dataUlong[inputOffset + 1];
inputOffset += 2;
DecodeBlock(tile, colorBlock);
byte alphaBase = (byte)alphaBlock;
int[] alphaTable = _etc2AlphaLut[(alphaBlock >> 8) & 0xf];
int alphaMultiplier = (int)(alphaBlock >> 12) & 0xf;
ulong alphaIndices = BinaryPrimitives.ReverseEndianness(alphaBlock);
if (alphaMultiplier != 0)
{
for (int py = 0; py < bh; py++)
{
int oOffsBase = imageBaseOOffs + ((ty + py) * width) + tx;
for (int px = 0; px < bw; px++)
{
int oOffs = oOffsBase + px;
int alphaIndex = (int)((alphaIndices >> (((px * BlockHeight + py) ^ 0xf) * 3)) & 7);
byte a = Saturate(alphaBase + alphaTable[alphaIndex] * alphaMultiplier);
outputUint[oOffs] = tile[py * BlockWidth + px] | ((uint)a << 24);
}
}
}
else
{
uint a = (uint)alphaBase << 24;
for (int py = 0; py < bh; py++)
{
int oOffsBase = imageBaseOOffs + ((ty + py) * width) + tx;
for (int px = 0; px < bw; px++)
{
int oOffs = oOffsBase + px;
outputUint[oOffs] = tile[py * BlockWidth + px] | a;
}
}
}
}
}
imageBaseOOffs += width * height;
}
}
width = Math.Max(1, width >> 1);
height = Math.Max(1, height >> 1);
depth = Math.Max(1, depth >> 1);
}
return output;
}
private static void DecodeBlock(Span<uint> tile, ulong block)
{
uint blockLow = (uint)(block >> 0);
uint blockHigh = (uint)(block >> 32);
uint r1, g1, b1;
uint r2, g2, b2;
bool differentialMode = (blockLow & 0x2000000) != 0;
if (differentialMode)
{
(r1, g1, b1, r2, g2, b2) = UnpackRgb555DiffEndPoints(blockLow);
if (r2 > 31)
{
DecodeBlock59T(tile, blockLow, blockHigh);
}
else if (g2 > 31)
{
DecodeBlock58H(tile, blockLow, blockHigh);
}
else if (b2 > 31)
{
DecodeBlock57P(tile, block);
}
else
{
r1 |= r1 >> 5;
g1 |= g1 >> 5;
b1 |= b1 >> 5;
r2 = (r2 << 3) | (r2 >> 2);
g2 = (g2 << 3) | (g2 >> 2);
b2 = (b2 << 3) | (b2 >> 2);
DecodeBlockETC1(tile, blockLow, blockHigh, r1, g1, b1, r2, g2, b2);
}
}
else
{
r1 = (blockLow & 0x0000f0) >> 0;
g1 = (blockLow & 0x00f000) >> 8;
b1 = (blockLow & 0xf00000) >> 16;
r2 = (blockLow & 0x00000f) << 4;
g2 = (blockLow & 0x000f00) >> 4;
b2 = (blockLow & 0x0f0000) >> 12;
r1 |= r1 >> 4;
g1 |= g1 >> 4;
b1 |= b1 >> 4;
r2 |= r2 >> 4;
g2 |= g2 >> 4;
b2 |= b2 >> 4;
DecodeBlockETC1(tile, blockLow, blockHigh, r1, g1, b1, r2, g2, b2);
}
}
private static void DecodeBlockPta(Span<uint> tile, ulong block)
{
uint blockLow = (uint)(block >> 0);
uint blockHigh = (uint)(block >> 32);
(uint r1, uint g1, uint b1, uint r2, uint g2, uint b2) = UnpackRgb555DiffEndPoints(blockLow);
bool fullyOpaque = (blockLow & 0x2000000) != 0;
if (fullyOpaque)
{
if (r2 > 31)
{
DecodeBlock59T(tile, blockLow, blockHigh);
}
else if (g2 > 31)
{
DecodeBlock58H(tile, blockLow, blockHigh);
}
else if (b2 > 31)
{
DecodeBlock57P(tile, block);
}
else
{
r1 |= r1 >> 5;
g1 |= g1 >> 5;
b1 |= b1 >> 5;
r2 = (r2 << 3) | (r2 >> 2);
g2 = (g2 << 3) | (g2 >> 2);
b2 = (b2 << 3) | (b2 >> 2);
DecodeBlockETC1(tile, blockLow, blockHigh, r1, g1, b1, r2, g2, b2);
}
for (int i = 0; i < tile.Length; i++)
{
tile[i] |= AlphaMask;
}
}
else
{
if (r2 > 31)
{
DecodeBlock59T(tile, blockLow, blockHigh, AlphaMask);
}
else if (g2 > 31)
{
DecodeBlock58H(tile, blockLow, blockHigh, AlphaMask);
}
else if (b2 > 31)
{
DecodeBlock57P(tile, block);
for (int i = 0; i < tile.Length; i++)
{
tile[i] |= AlphaMask;
}
}
else
{
r1 |= r1 >> 5;
g1 |= g1 >> 5;
b1 |= b1 >> 5;
r2 = (r2 << 3) | (r2 >> 2);
g2 = (g2 << 3) | (g2 >> 2);
b2 = (b2 << 3) | (b2 >> 2);
DecodeBlockETC1(tile, blockLow, blockHigh, r1, g1, b1, r2, g2, b2, AlphaMask);
}
}
}
private static (uint, uint, uint, uint, uint, uint) UnpackRgb555DiffEndPoints(uint blockLow)
{
uint r1 = (blockLow & 0x0000f8) >> 0;
uint g1 = (blockLow & 0x00f800) >> 8;
uint b1 = (blockLow & 0xf80000) >> 16;
uint r2 = (uint)((sbyte)(r1 >> 3) + ((sbyte)((blockLow & 0x000007) << 5) >> 5));
uint g2 = (uint)((sbyte)(g1 >> 3) + ((sbyte)((blockLow & 0x000700) >> 3) >> 5));
uint b2 = (uint)((sbyte)(b1 >> 3) + ((sbyte)((blockLow & 0x070000) >> 11) >> 5));
return (r1, g1, b1, r2, g2, b2);
}
private static void DecodeBlock59T(Span<uint> tile, uint blockLow, uint blockHigh, uint alphaMask = 0)
{
uint r1 = (blockLow & 3) | ((blockLow >> 1) & 0xc);
uint g1 = (blockLow >> 12) & 0xf;
uint b1 = (blockLow >> 8) & 0xf;
uint r2 = (blockLow >> 20) & 0xf;
uint g2 = (blockLow >> 16) & 0xf;
uint b2 = (blockLow >> 28) & 0xf;
r1 |= r1 << 4;
g1 |= g1 << 4;
b1 |= b1 << 4;
r2 |= r2 << 4;
g2 |= g2 << 4;
b2 |= b2 << 4;
int dist = _etc2Lut[((blockLow >> 24) & 1) | ((blockLow >> 25) & 6)];
Span<uint> palette = stackalloc uint[4];
palette[0] = Pack(r1, g1, b1);
palette[1] = Pack(r2, g2, b2, dist);
palette[2] = Pack(r2, g2, b2);
palette[3] = Pack(r2, g2, b2, -dist);
blockHigh = BinaryPrimitives.ReverseEndianness(blockHigh);
for (int y = 0; y < BlockHeight; y++)
{
for (int x = 0; x < BlockWidth; x++)
{
int offset = (y * 4) + x;
int index = (x * 4) + y;
int paletteIndex = (int)((blockHigh >> index) & 1) | (int)((blockHigh >> (index + 15)) & 2);
tile[offset] = palette[paletteIndex];
if (alphaMask != 0)
{
if (paletteIndex == 2)
{
tile[offset] = 0;
}
else
{
tile[offset] |= alphaMask;
}
}
}
}
}
private static void DecodeBlock58H(Span<uint> tile, uint blockLow, uint blockHigh, uint alphaMask = 0)
{
uint r1 = (blockLow >> 3) & 0xf;
uint g1 = ((blockLow << 1) & 0xe) | ((blockLow >> 12) & 1);
uint b1 = ((blockLow >> 23) & 1) | ((blockLow >> 7) & 6) | ((blockLow >> 8) & 8);
uint r2 = (blockLow >> 19) & 0xf;
uint g2 = ((blockLow >> 31) & 1) | ((blockLow >> 15) & 0xe);
uint b2 = (blockLow >> 27) & 0xf;
uint rgb1 = Pack4Be(r1, g1, b1);
uint rgb2 = Pack4Be(r2, g2, b2);
r1 |= r1 << 4;
g1 |= g1 << 4;
b1 |= b1 << 4;
r2 |= r2 << 4;
g2 |= g2 << 4;
b2 |= b2 << 4;
int dist = _etc2Lut[(rgb1 >= rgb2 ? 1u : 0u) | ((blockLow >> 23) & 2) | ((blockLow >> 24) & 4)];
Span<uint> palette = stackalloc uint[4];
palette[0] = Pack(r1, g1, b1, dist);
palette[1] = Pack(r1, g1, b1, -dist);
palette[2] = Pack(r2, g2, b2, dist);
palette[3] = Pack(r2, g2, b2, -dist);
blockHigh = BinaryPrimitives.ReverseEndianness(blockHigh);
for (int y = 0; y < BlockHeight; y++)
{
for (int x = 0; x < BlockWidth; x++)
{
int offset = (y * 4) + x;
int index = (x * 4) + y;
int paletteIndex = (int)((blockHigh >> index) & 1) | (int)((blockHigh >> (index + 15)) & 2);
tile[offset] = palette[paletteIndex];
if (alphaMask != 0)
{
if (paletteIndex == 2)
{
tile[offset] = 0;
}
else
{
tile[offset] |= alphaMask;
}
}
}
}
}
private static void DecodeBlock57P(Span<uint> tile, ulong block)
{
int r0 = (int)((block >> 1) & 0x3f);
int g0 = (int)(((block >> 9) & 0x3f) | ((block & 1) << 6));
int b0 = (int)(((block >> 31) & 1) | ((block >> 15) & 6) | ((block >> 16) & 0x18) | ((block >> 3) & 0x20));
int rh = (int)(((block >> 24) & 1) | ((block >> 25) & 0x3e));
int gh = (int)((block >> 33) & 0x7f);
int bh = (int)(((block >> 43) & 0x1f) | ((block >> 27) & 0x20));
int rv = (int)(((block >> 53) & 7) | ((block >> 37) & 0x38));
int gv = (int)(((block >> 62) & 3) | ((block >> 46) & 0x7c));
int bv = (int)((block >> 56) & 0x3f);
r0 = (r0 << 2) | (r0 >> 4);
g0 = (g0 << 1) | (g0 >> 6);
b0 = (b0 << 2) | (b0 >> 4);
rh = (rh << 2) | (rh >> 4);
gh = (gh << 1) | (gh >> 6);
bh = (bh << 2) | (bh >> 4);
rv = (rv << 2) | (rv >> 4);
gv = (gv << 1) | (gv >> 6);
bv = (bv << 2) | (bv >> 4);
for (int y = 0; y < BlockHeight; y++)
{
for (int x = 0; x < BlockWidth; x++)
{
int offset = y * BlockWidth + x;
byte r = Saturate(((x * (rh - r0)) + (y * (rv - r0)) + (r0 * 4) + 2) >> 2);
byte g = Saturate(((x * (gh - g0)) + (y * (gv - g0)) + (g0 * 4) + 2) >> 2);
byte b = Saturate(((x * (bh - b0)) + (y * (bv - b0)) + (b0 * 4) + 2) >> 2);
tile[offset] = Pack(r, g, b);
}
}
}
private static void DecodeBlockETC1(
Span<uint> tile,
uint blockLow,
uint blockHigh,
uint r1,
uint g1,
uint b1,
uint r2,
uint g2,
uint b2,
uint alphaMask = 0)
{
int[] table1 = _etc1Lut[(blockLow >> 29) & 7];
int[] table2 = _etc1Lut[(blockLow >> 26) & 7];
bool flip = (blockLow & 0x1000000) != 0;
if (!flip)
{
for (int y = 0; y < BlockHeight; y++)
{
for (int x = 0; x < BlockWidth / 2; x++)
{
uint color1 = CalculatePixel(r1, g1, b1, x + 0, y, blockHigh, table1, alphaMask);
uint color2 = CalculatePixel(r2, g2, b2, x + 2, y, blockHigh, table2, alphaMask);
int offset1 = y * BlockWidth + x;
int offset2 = y * BlockWidth + x + 2;
tile[offset1] = color1;
tile[offset2] = color2;
}
}
}
else
{
for (int y = 0; y < BlockHeight / 2; y++)
{
for (int x = 0; x < BlockWidth; x++)
{
uint color1 = CalculatePixel(r1, g1, b1, x, y + 0, blockHigh, table1, alphaMask);
uint color2 = CalculatePixel(r2, g2, b2, x, y + 2, blockHigh, table2, alphaMask);
int offset1 = (y * BlockWidth) + x;
int offset2 = ((y + 2) * BlockWidth) + x;
tile[offset1] = color1;
tile[offset2] = color2;
}
}
}
}
private static uint CalculatePixel(uint r, uint g, uint b, int x, int y, uint block, int[] table, uint alphaMask)
{
int index = x * BlockHeight + y;
uint msb = block << 1;
uint tableIndex = index < 8
? ((block >> (index + 24)) & 1) + ((msb >> (index + 8)) & 2)
: ((block >> (index + 8)) & 1) + ((msb >> (index - 8)) & 2);
if (alphaMask != 0)
{
if (tableIndex == 0)
{
return Pack(r, g, b) | alphaMask;
}
else if (tableIndex == 2)
{
return 0;
}
else
{
return Pack(r, g, b, table[tableIndex]) | alphaMask;
}
}
return Pack(r, g, b, table[tableIndex]);
}
private static uint Pack(uint r, uint g, uint b, int offset)
{
r = Saturate((int)(r + offset));
g = Saturate((int)(g + offset));
b = Saturate((int)(b + offset));
return Pack(r, g, b);
}
private static uint Pack(uint r, uint g, uint b)
{
return r | (g << 8) | (b << 16);
}
private static uint Pack4Be(uint r, uint g, uint b)
{
return (r << 8) | (g << 4) | b;
}
private static byte Saturate(int value)
{
return value > byte.MaxValue ? byte.MaxValue : value < byte.MinValue ? byte.MinValue : (byte)value;
}
private static int CalculateOutputSize(int width, int height, int depth, int levels, int layers)
{
int size = 0;
for (int l = 0; l < levels; l++)
{
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
}
return size;
}
}
}

View File

@@ -7,7 +7,7 @@ namespace Ryujinx.Graphics.Texture
{
public static class LayoutConverter
{
private const int HostStrideAlignment = 4;
public const int HostStrideAlignment = 4;
public static void ConvertBlockLinearToLinear(
Span<byte> dst,

View File

@@ -1,3 +1,4 @@
using Ryujinx.Common;
using System;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
@@ -7,30 +8,206 @@ namespace Ryujinx.Graphics.Texture
{
public static class PixelConverter
{
public unsafe static byte[] ConvertR4G4ToR4G4B4A4(ReadOnlySpan<byte> data)
private static (int remainder, int outRemainder, int height) GetLineRemainders(int length, int width, int bpp, int outBpp)
{
int stride = BitUtils.AlignUp(width * bpp, LayoutConverter.HostStrideAlignment);
int remainder = stride / bpp - width;
int outStride = BitUtils.AlignUp(width * outBpp, LayoutConverter.HostStrideAlignment);
int outRemainder = outStride / outBpp - width;
return (remainder, outRemainder, length / stride);
}
public unsafe static byte[] ConvertR4G4ToR4G4B4A4(ReadOnlySpan<byte> data, int width)
{
byte[] output = new byte[data.Length * 2];
int start = 0;
if (Sse41.IsSupported)
{
int sizeTrunc = data.Length & ~7;
start = sizeTrunc;
fixed (byte* inputPtr = data, outputPtr = output)
{
for (ulong offset = 0; offset < (ulong)sizeTrunc; offset += 8)
{
Sse2.Store(outputPtr + offset * 2, Sse41.ConvertToVector128Int16(inputPtr + offset).AsByte());
}
}
}
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 1, 2);
Span<ushort> outputSpan = MemoryMarshal.Cast<byte, ushort>(output);
for (int i = start; i < data.Length; i++)
if (remainder == 0)
{
outputSpan[i] = (ushort)data[i];
int start = 0;
if (Sse41.IsSupported)
{
int sizeTrunc = data.Length & ~7;
start = sizeTrunc;
fixed (byte* inputPtr = data, outputPtr = output)
{
for (ulong offset = 0; offset < (ulong)sizeTrunc; offset += 8)
{
Sse2.Store(outputPtr + offset * 2, Sse41.ConvertToVector128Int16(inputPtr + offset).AsByte());
}
}
}
for (int i = start; i < data.Length; i++)
{
outputSpan[i] = (ushort)data[i];
}
}
else
{
int offset = 0;
int outOffset = 0;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
outputSpan[outOffset++] = data[offset++];
}
offset += remainder;
outOffset += outRemainder;
}
}
return output;
}
public unsafe static byte[] ConvertR5G6B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
{
byte[] output = new byte[data.Length * 2];
int offset = 0;
int outOffset = 0;
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
uint packed = inputSpan[offset++];
uint outputPacked = 0xff000000;
outputPacked |= (packed << 3) & 0x000000f8;
outputPacked |= (packed << 8) & 0x00f80000;
// Replicate 5 bit components.
outputPacked |= (outputPacked >> 5) & 0x00070007;
// Include and replicate 6 bit component.
outputPacked |= ((packed << 5) & 0x0000fc00) | ((packed >> 1) & 0x00000300);
outputSpan[outOffset++] = outputPacked;
}
offset += remainder;
outOffset += outRemainder;
}
return output;
}
public unsafe static byte[] ConvertR5G5B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width, bool forceAlpha)
{
byte[] output = new byte[data.Length * 2];
int offset = 0;
int outOffset = 0;
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
uint packed = inputSpan[offset++];
uint a = forceAlpha ? 1 : (packed >> 15);
uint outputPacked = a * 0xff000000;
outputPacked |= (packed << 3) & 0x000000f8;
outputPacked |= (packed << 6) & 0x0000f800;
outputPacked |= (packed << 9) & 0x00f80000;
// Replicate 5 bit components.
outputPacked |= (outputPacked >> 5) & 0x00070707;
outputSpan[outOffset++] = outputPacked;
}
offset += remainder;
outOffset += outRemainder;
}
return output;
}
public unsafe static byte[] ConvertA1B5G5R5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
{
byte[] output = new byte[data.Length * 2];
int offset = 0;
int outOffset = 0;
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
uint packed = inputSpan[offset++];
uint a = packed >> 15;
uint outputPacked = a * 0xff000000;
outputPacked |= (packed >> 8) & 0x000000f8;
outputPacked |= (packed << 5) & 0x0000f800;
outputPacked |= (packed << 18) & 0x00f80000;
// Replicate 5 bit components.
outputPacked |= (outputPacked >> 5) & 0x00070707;
outputSpan[outOffset++] = outputPacked;
}
offset += remainder;
outOffset += outRemainder;
}
return output;
}
public unsafe static byte[] ConvertR4G4B4A4ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
{
byte[] output = new byte[data.Length * 2];
int offset = 0;
int outOffset = 0;
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output);
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
uint packed = inputSpan[offset++];
uint outputPacked = packed & 0x0000000f;
outputPacked |= (packed << 4) & 0x00000f00;
outputPacked |= (packed << 8) & 0x000f0000;
outputPacked |= (packed << 12) & 0x0f000000;
outputSpan[outOffset++] = outputPacked * 0x11;
}
offset += remainder;
outOffset += outRemainder;
}
return output;

View File

@@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Vulkan
{
var range = _freeRanges[i];
ulong alignedOffset = BitUtils.AlignUp(range.Offset, (int)alignment);
ulong alignedOffset = BitUtils.AlignUp<ulong>(range.Offset, alignment);
ulong sizeDelta = alignedOffset - range.Offset;
ulong usableSize = range.Size - sizeDelta;
@@ -196,7 +196,7 @@ namespace Ryujinx.Graphics.Vulkan
}
}
ulong blockAlignedSize = BitUtils.AlignUp(size, _blockAlignment);
ulong blockAlignedSize = BitUtils.AlignUp<ulong>(size, (ulong)_blockAlignment);
var memoryAllocateInfo = new MemoryAllocateInfo()
{

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
@@ -13,14 +13,14 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.5" />
<PackageReference Include="shaderc.net" Version="0.1.0" />
<PackageReference Include="Silk.NET.Vulkan" Version="2.16.0" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" Version="2.16.0" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" Version="2.16.0" />
<PackageReference Include="System.IO.FileSystem.Primitives" Version="4.3.0" />
<PackageReference Include="System.Net.NameResolution" Version="4.3.0" />
<PackageReference Include="System.Threading.ThreadPool" Version="4.3.0" />
<PackageReference Include="OpenTK.Windowing.GraphicsLibraryFramework" />
<PackageReference Include="shaderc.net" />
<PackageReference Include="Silk.NET.Vulkan" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.EXT" />
<PackageReference Include="Silk.NET.Vulkan.Extensions.KHR" />
<PackageReference Include="System.IO.FileSystem.Primitives" />
<PackageReference Include="System.Net.NameResolution" />
<PackageReference Include="System.Threading.ThreadPool" />
</ItemGroup>
<ItemGroup>

View File

@@ -374,7 +374,24 @@ namespace Ryujinx.Graphics.Vulkan
api.GetPhysicalDeviceProperties(physicalDevice, out var properties);
bool useRobustBufferAccess = VendorUtils.FromId(properties.VendorID) == Vendor.Nvidia;
var supportedFeatures = api.GetPhysicalDeviceFeature(physicalDevice);
PhysicalDeviceFeatures2 features2 = new PhysicalDeviceFeatures2()
{
SType = StructureType.PhysicalDeviceFeatures2
};
PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColorSupported = new PhysicalDeviceCustomBorderColorFeaturesEXT()
{
SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt
};
if (supportedExtensions.Contains("VK_EXT_custom_border_color"))
{
features2.PNext = &featuresCustomBorderColorSupported;
}
api.GetPhysicalDeviceFeatures2(physicalDevice, &features2);
var supportedFeatures = features2.Features;
var features = new PhysicalDeviceFeatures()
{
@@ -491,6 +508,23 @@ namespace Ryujinx.Graphics.Vulkan
pExtendedFeatures = &featuresSubgroupSizeControl;
}
PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor;
if (supportedExtensions.Contains("VK_EXT_custom_border_color") &&
featuresCustomBorderColorSupported.CustomBorderColors &&
featuresCustomBorderColorSupported.CustomBorderColorWithoutFormat)
{
featuresCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT()
{
SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt,
PNext = pExtendedFeatures,
CustomBorderColors = true,
CustomBorderColorWithoutFormat = true,
};
pExtendedFeatures = &featuresCustomBorderColor;
}
var enabledExtensions = RequiredExtensions.Union(DesirableExtensions.Intersect(supportedExtensions)).ToArray();
IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];

View File

@@ -177,6 +177,11 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDeviceShaderFloat16Int8Features
};
PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT()
{
SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt
};
if (supportedExtensions.Contains("VK_EXT_robustness2"))
{
features2.PNext = &featuresRobustness2;
@@ -188,8 +193,18 @@ namespace Ryujinx.Graphics.Vulkan
features2.PNext = &featuresShaderInt8;
}
if (supportedExtensions.Contains("VK_EXT_custom_border_color"))
{
featuresCustomBorderColor.PNext = features2.PNext;
features2.PNext = &featuresCustomBorderColor;
}
Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2);
bool customBorderColorSupported = supportedExtensions.Contains("VK_EXT_custom_border_color") &&
featuresCustomBorderColor.CustomBorderColors &&
featuresCustomBorderColor.CustomBorderColorWithoutFormat;
ref var properties = ref properties2.Properties;
SampleCountFlags supportedSampleCounts =
@@ -199,7 +214,7 @@ namespace Ryujinx.Graphics.Vulkan
Capabilities = new HardwareCapabilities(
supportedExtensions.Contains("VK_EXT_index_type_uint8"),
supportedExtensions.Contains("VK_EXT_custom_border_color"),
customBorderColorSupported,
supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName),
supportedExtensions.Contains("VK_EXT_fragment_shader_interlock"),
supportedExtensions.Contains("VK_NV_geometry_shader_passthrough"),
@@ -373,6 +388,24 @@ namespace Ryujinx.Graphics.Vulkan
GAL.Format.Bc7Srgb,
GAL.Format.Bc7Unorm);
bool supportsEtc2CompressionFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags,
GAL.Format.Etc2RgbaSrgb,
GAL.Format.Etc2RgbaUnorm,
GAL.Format.Etc2RgbPtaSrgb,
GAL.Format.Etc2RgbPtaUnorm,
GAL.Format.Etc2RgbSrgb,
GAL.Format.Etc2RgbUnorm);
bool supports5BitComponentFormat = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags,
GAL.Format.R5G6B5Unorm,
GAL.Format.R5G5B5A1Unorm,
GAL.Format.R5G5B5X1Unorm,
GAL.Format.B5G6R5Unorm,
GAL.Format.B5G5R5A1Unorm,
GAL.Format.A1B5G5R5Unorm);
bool supportsR4G4B4A4Format = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags,
GAL.Format.R4G4B4A4Unorm);
PhysicalDeviceVulkan12Features featuresVk12 = new PhysicalDeviceVulkan12Features()
{
@@ -399,10 +432,13 @@ namespace Ryujinx.Graphics.Vulkan
supportsBc123Compression: supportsBc123CompressionFormat,
supportsBc45Compression: supportsBc45CompressionFormat,
supportsBc67Compression: supportsBc67CompressionFormat,
supportsEtc2Compression: supportsEtc2CompressionFormat,
supports3DTextureCompression: true,
supportsBgraFormat: true,
supportsR4G4Format: false,
supportsR4G4B4A4Format: supportsR4G4B4A4Format,
supportsSnormBufferTextureFormat: true,
supports5BitComponentFormat: supports5BitComponentFormat,
supportsFragmentShaderInterlock: Capabilities.SupportsFragmentShaderInterlock,
supportsFragmentShaderOrderingIntel: false,
supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough,

View File

@@ -47,7 +47,7 @@ namespace Ryujinx.HLE.HOS.Applets.Error
_errorStorage = _normalSession.Pop();
_errorCommonHeader = IApplet.ReadStruct<ErrorCommonHeader>(_errorStorage);
_errorStorage = _errorStorage.Skip(Marshal.SizeOf(typeof(ErrorCommonHeader))).ToArray();
_errorStorage = _errorStorage.Skip(Marshal.SizeOf<ErrorCommonHeader>()).ToArray();
switch (_errorCommonHeader.Type)
{

View File

@@ -9,6 +9,7 @@ using Ryujinx.HLE.Ui.Input;
using Ryujinx.Memory;
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@@ -797,7 +798,7 @@ namespace Ryujinx.HLE.HOS.Applets
return sb.ToString();
}
private static T ReadStruct<T>(byte[] data)
private static T ReadStruct<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] T>(byte[] data)
where T : struct
{
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);

View File

@@ -81,8 +81,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
attributeMask |= MemoryAttribute.DeviceMapped;
}
ulong clientAddrTruncated = BitUtils.AlignDown(desc.ClientAddress, KPageTableBase.PageSize);
ulong clientAddrRounded = BitUtils.AlignUp (desc.ClientAddress, KPageTableBase.PageSize);
ulong clientAddrTruncated = BitUtils.AlignDown<ulong>(desc.ClientAddress, KPageTableBase.PageSize);
ulong clientAddrRounded = BitUtils.AlignUp<ulong>(desc.ClientAddress, KPageTableBase.PageSize);
// Check if address is not aligned, in this case we need to perform 2 copies.
if (clientAddrTruncated != clientAddrRounded)
@@ -113,9 +113,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
ulong clientEndAddr = desc.ClientAddress + desc.Size;
ulong serverEndAddr = desc.ServerAddress + desc.Size;
ulong clientEndAddrTruncated = BitUtils.AlignDown(clientEndAddr, KPageTableBase.PageSize);
ulong clientEndAddrRounded = BitUtils.AlignUp (clientEndAddr, KPageTableBase.PageSize);
ulong serverEndAddrTruncated = BitUtils.AlignDown(serverEndAddr, KPageTableBase.PageSize);
ulong clientEndAddrTruncated = BitUtils.AlignDown<ulong>(clientEndAddr, (ulong)KPageTableBase.PageSize);
ulong clientEndAddrRounded = BitUtils.AlignUp<ulong>(clientEndAddr, KPageTableBase.PageSize);
ulong serverEndAddrTruncated = BitUtils.AlignDown<ulong>(serverEndAddr, (ulong)KPageTableBase.PageSize);
if (clientEndAddrTruncated < clientEndAddrRounded &&
(clientAddrTruncated == clientAddrRounded || clientAddrTruncated < clientEndAddrTruncated))

View File

@@ -1005,7 +1005,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
recvListEndAddr = recvListBaseAddr + size;
}
recvListBufferAddress = BitUtils.AlignUp(recvListBaseAddr + dstOffset, 0x10);
recvListBufferAddress = BitUtils.AlignUp<ulong>(recvListBaseAddr + dstOffset, 0x10);
ulong endAddress = recvListBufferAddress + descriptor.BufferSize;

View File

@@ -44,7 +44,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
public KernelResult Map(ulong address, ulong size, KMemoryPermission perm)
{
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize))
{
return KernelResult.InvalidSize;
}
@@ -73,7 +73,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
public KernelResult MapToOwner(ulong address, ulong size, KMemoryPermission permission)
{
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize))
{
return KernelResult.InvalidSize;
}
@@ -102,7 +102,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
public KernelResult Unmap(ulong address, ulong size)
{
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize))
{
return KernelResult.InvalidSize;
}
@@ -128,7 +128,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
public KernelResult UnmapFromOwner(ulong address, ulong size)
{
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, KPageTableBase.PageSize))
{
return KernelResult.InvalidSize;
}

View File

@@ -101,7 +101,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
for (int depth = HighestDepthIndex; depth >= 0; depth--)
{
_bitStorages[depth] = storage;
size = BitUtils.DivRoundUp(size, UInt64BitSize);
size = BitUtils.DivRoundUp<ulong>(size, (ulong)UInt64BitSize);
storage = storage.Slice((int)size);
}
@@ -288,7 +288,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
for (int depth = GetRequiredDepth(regionSize) - 1; depth >= 0; depth--)
{
regionSize = BitUtils.DivRoundUp(regionSize, UInt64BitSize);
regionSize = BitUtils.DivRoundUp<ulong>(regionSize, UInt64BitSize);
overheadBits += (int)regionSize;
}

View File

@@ -48,7 +48,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{
int diff = 1 << (NextShift - Shift);
offset = BitUtils.AlignDown(offset, diff);
offset = BitUtils.AlignDown(offset, (ulong)diff);
if (_bitmap.ClearRange(offset, diff))
{

View File

@@ -203,8 +203,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
heapRegion.Size = 0x180000000;
stackRegion.Size = 0x80000000;
tlsIoRegion.Size = 0x1000000000;
CodeRegionStart = BitUtils.AlignDown(address, 0x200000);
codeRegionSize = BitUtils.AlignUp(endAddr, 0x200000) - CodeRegionStart;
CodeRegionStart = BitUtils.AlignDown<ulong>(address, 0x200000);
codeRegionSize = BitUtils.AlignUp<ulong>(endAddr, 0x200000) - CodeRegionStart;
stackAndTlsIoStart = 0;
stackAndTlsIoEnd = 0;
baseAddress = 0x8000000;
@@ -1584,8 +1584,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
return KernelResult.OutOfResource;
}
ulong srcMapAddress = BitUtils.AlignUp(src, PageSize);
ulong srcMapEndAddr = BitUtils.AlignDown(src + size, PageSize);
ulong srcMapAddress = BitUtils.AlignUp<ulong>(src, PageSize);
ulong srcMapEndAddr = BitUtils.AlignDown<ulong>(src + size, PageSize);
ulong srcMapSize = srcMapEndAddr - srcMapAddress;
result = MapPagesFromClientProcess(size, src, permission, state, srcPageTable, send, out ulong va);
@@ -1659,10 +1659,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
attributeMask |= MemoryAttribute.DeviceMapped;
}
ulong addressRounded = BitUtils.AlignUp(address, PageSize);
ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
ulong endAddrRounded = BitUtils.AlignUp(endAddr, PageSize);
ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
ulong addressRounded = BitUtils.AlignUp<ulong>(address, PageSize);
ulong addressTruncated = BitUtils.AlignDown<ulong>(address, PageSize);
ulong endAddrRounded = BitUtils.AlignUp<ulong>(endAddr, PageSize);
ulong endAddrTruncated = BitUtils.AlignDown<ulong>(endAddr, PageSize);
if (!_slabManager.CanAllocate(MaxBlocksNeededForInsertion))
{
@@ -1769,10 +1769,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
ulong endAddr = address + size;
ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
ulong addressRounded = BitUtils.AlignUp(address, PageSize);
ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
ulong endAddrRounded = BitUtils.AlignUp(endAddr, PageSize);
ulong addressTruncated = BitUtils.AlignDown<ulong>(address, PageSize);
ulong addressRounded = BitUtils.AlignUp<ulong>(address, PageSize);
ulong endAddrTruncated = BitUtils.AlignDown<ulong>(endAddr, PageSize);
ulong endAddrRounded = BitUtils.AlignUp<ulong>(endAddr, PageSize);
ulong neededSize = endAddrRounded - addressTruncated;
@@ -1983,10 +1983,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
return KernelResult.OutOfResource;
}
ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
ulong addressRounded = BitUtils.AlignUp(address, PageSize);
ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
ulong endAddrRounded = BitUtils.AlignUp(endAddr, PageSize);
ulong addressTruncated = BitUtils.AlignDown<ulong>(address, PageSize);
ulong addressRounded = BitUtils.AlignUp<ulong>(address, PageSize);
ulong endAddrTruncated = BitUtils.AlignDown<ulong>(endAddr, PageSize);
ulong endAddrRounded = BitUtils.AlignUp<ulong>(endAddr, PageSize);
ulong pagesCount = (endAddrRounded - addressTruncated) / PageSize;
@@ -2010,10 +2010,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{
ulong endAddr = address + size;
ulong addressRounded = BitUtils.AlignUp(address, PageSize);
ulong addressTruncated = BitUtils.AlignDown(address, PageSize);
ulong endAddrRounded = BitUtils.AlignUp(endAddr, PageSize);
ulong endAddrTruncated = BitUtils.AlignDown(endAddr, PageSize);
ulong addressRounded = BitUtils.AlignUp<ulong>(address, PageSize);
ulong addressTruncated = BitUtils.AlignDown<ulong>(address, PageSize);
ulong endAddrRounded = BitUtils.AlignUp<ulong>(endAddr, PageSize);
ulong endAddrTruncated = BitUtils.AlignDown<ulong>(endAddr, PageSize);
ulong pagesCount = addressRounded < endAddrTruncated ? (endAddrTruncated - addressRounded) / PageSize : 0;
@@ -2540,7 +2540,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
for (int attempt = 0; attempt < 8; attempt++)
{
ulong aslrAddress = BitUtils.AlignDown(regionStart + GetRandomValue(0, aslrMaxOffset) * (ulong)alignment, alignment);
ulong aslrAddress = BitUtils.AlignDown(regionStart + GetRandomValue(0, aslrMaxOffset) * (ulong)alignment, (ulong)alignment);
ulong aslrEndAddr = aslrAddress + totalNeededSize;
KMemoryInfo info = _blockManager.FindBlock(aslrAddress).GetInfo();
@@ -2618,7 +2618,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
currBaseAddr += reservedSize;
ulong address = BitUtils.AlignDown(currBaseAddr, alignment) + reservedStart;
ulong address = BitUtils.AlignDown<ulong>(currBaseAddr, (ulong)alignment) + reservedStart;
if (currBaseAddr > address)
{
@@ -2834,7 +2834,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
{
lock (_blockManager)
{
return BitUtils.DivRoundUp(GetMmUsedSize(), PageSize);
return BitUtils.DivRoundUp<ulong>(GetMmUsedSize(), PageSize);
}
}

View File

@@ -33,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
KProcess process,
KMemoryPermission permission)
{
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, KPageTableBase.PageSize))
{
return KernelResult.InvalidSize;
}
@@ -52,7 +52,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
public KernelResult UnmapFromProcess(KPageTableBase memoryManager, ulong address, ulong size, KProcess process)
{
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, KPageTableBase.PageSize))
{
return KernelResult.InvalidSize;
}

View File

@@ -67,7 +67,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
KProcess process,
KMemoryPermission permission)
{
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, KPageTableBase.PageSize))
{
return KernelResult.InvalidSize;
}
@@ -95,7 +95,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
ulong size,
KProcess process)
{
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp(size, KPageTableBase.PageSize))
if (_pageList.GetPagesCount() != BitUtils.DivRoundUp<ulong>(size, (ulong)KPageTableBase.PageSize))
{
return KernelResult.InvalidSize;
}

View File

@@ -472,7 +472,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
public KernelResult FreeThreadLocalStorage(ulong tlsSlotAddr)
{
ulong tlsPageAddr = BitUtils.AlignDown(tlsSlotAddr, KPageTableBase.PageSize);
ulong tlsPageAddr = BitUtils.AlignDown<ulong>(tlsSlotAddr, KPageTableBase.PageSize);
KernelContext.CriticalSection.Enter();
@@ -554,7 +554,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
throw new InvalidOperationException("Trying to start a process with a invalid state!");
}
ulong stackSizeRounded = BitUtils.AlignUp(stackSize, KPageTableBase.PageSize);
ulong stackSizeRounded = BitUtils.AlignUp<ulong>(stackSize, KPageTableBase.PageSize);
ulong neededSize = stackSizeRounded + _imageSize;

View File

@@ -47,7 +47,7 @@ namespace Ryujinx.HLE.HOS
endOffset = kip.BssOffset + kip.BssSize;
}
uint codeSize = BitUtils.AlignUp(kip.TextOffset + endOffset, KPageTableBase.PageSize);
uint codeSize = BitUtils.AlignUp<uint>(kip.TextOffset + endOffset, KPageTableBase.PageSize);
int codePagesCount = (int)(codeSize / KPageTableBase.PageSize);
@@ -195,7 +195,7 @@ namespace Ryujinx.HLE.HOS
nsoSize = dataEnd;
}
nsoSize = BitUtils.AlignUp(nsoSize, KPageTableBase.PageSize);
nsoSize = BitUtils.AlignUp<uint>(nsoSize, KPageTableBase.PageSize);
nsoBase[index] = codeStart + (ulong)codeSize;
@@ -349,7 +349,7 @@ namespace Ryujinx.HLE.HOS
return KernelResult.Success;
}
size = BitUtils.AlignUp(size, KPageTableBase.PageSize);
size = BitUtils.AlignUp<ulong>(size, KPageTableBase.PageSize);
return process.MemoryManager.SetProcessMemoryPermission(address, size, permission);
}

View File

@@ -4,8 +4,6 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
{
public class UserProfile
{
private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public UserId UserId { get; }
public long LastModifiedTimestamp { get; set; }
@@ -83,7 +81,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
private void UpdateLastModifiedTimestamp()
{
LastModifiedTimestamp = (long)(DateTime.Now - Epoch).TotalSeconds;
LastModifiedTimestamp = (long)(DateTime.Now - DateTime.UnixEpoch).TotalSeconds;
}
}
}

View File

@@ -100,27 +100,24 @@ namespace Ryujinx.HLE.HOS.Services.Caps
Unknown0x1f = 1
};
using (SHA256 sha256Hash = SHA256.Create())
// NOTE: The hex hash is a HMAC-SHA256 (first 32 bytes) using a hardcoded secret key over the titleId, we can simulate it by hashing the titleId instead.
string hash = BitConverter.ToString(SHA256.HashData(BitConverter.GetBytes(titleId))).Replace("-", "").Remove(0x20);
string folderPath = Path.Combine(_sdCardPath, "Nintendo", "Album", currentDateTime.Year.ToString("00"), currentDateTime.Month.ToString("00"), currentDateTime.Day.ToString("00"));
string filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
// TODO: Handle that using the FS service implementation and return the right error code instead of throwing exceptions.
Directory.CreateDirectory(folderPath);
while (File.Exists(filePath))
{
// NOTE: The hex hash is a HMAC-SHA256 (first 32 bytes) using a hardcoded secret key over the titleId, we can simulate it by hashing the titleId instead.
string hash = BitConverter.ToString(sha256Hash.ComputeHash(BitConverter.GetBytes(titleId))).Replace("-", "").Remove(0x20);
string folderPath = Path.Combine(_sdCardPath, "Nintendo", "Album", currentDateTime.Year.ToString("00"), currentDateTime.Month.ToString("00"), currentDateTime.Day.ToString("00"));
string filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
applicationAlbumEntry.AlbumFileDateTime.UniqueId++;
// TODO: Handle that using the FS service implementation and return the right error code instead of throwing exceptions.
Directory.CreateDirectory(folderPath);
while (File.Exists(filePath))
{
applicationAlbumEntry.AlbumFileDateTime.UniqueId++;
filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
}
// NOTE: The saved JPEG file doesn't have the limitation in the extra EXIF data.
Image.LoadPixelData<Rgba32>(screenshotData, 1280, 720).SaveAsJpegAsync(filePath);
filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
}
// NOTE: The saved JPEG file doesn't have the limitation in the extra EXIF data.
Image.LoadPixelData<Rgba32>(screenshotData, 1280, 720).SaveAsJpegAsync(filePath);
return ResultCode.Success;
}

View File

@@ -2,7 +2,6 @@
using Ryujinx.HLE.HOS.Services.Account.Acc;
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService
{
@@ -29,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService
public override string ToString()
{
return $"UserPresence {{ UserId: {UserId}, LastTimeOnlineTimestamp: {LastTimeOnlineTimestamp}, Status: {Status}, AppKeyValueStorage: {Encoding.ASCII.GetString(AppKeyValueStorage)} }}";
return $"UserPresence {{ UserId: {UserId}, LastTimeOnlineTimestamp: {LastTimeOnlineTimestamp}, Status: {Status} }}";
}
}
}

View File

@@ -589,9 +589,9 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
ulong outputPosition = context.Request.RecvListBuff[0].Position;
context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf(typeof(TagInfo)));
context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf<TagInfo>());
MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(TagInfo)));
MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf<TagInfo>());
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
@@ -665,9 +665,9 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
ulong outputPosition = context.Request.RecvListBuff[0].Position;
context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf(typeof(RegisterInfo)));
context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf<RegisterInfo>());
MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(RegisterInfo)));
MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf<RegisterInfo>());
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
@@ -728,9 +728,9 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
ulong outputPosition = context.Request.RecvListBuff[0].Position;
context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf(typeof(CommonInfo)));
context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf<CommonInfo>());
MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(CommonInfo)));
MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf<CommonInfo>());
uint deviceHandle = (uint)context.RequestData.ReadUInt64();
@@ -788,9 +788,9 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
ulong outputPosition = context.Request.RecvListBuff[0].Position;
context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf(typeof(ModelInfo)));
context.Response.PtrBuff[0] = context.Response.PtrBuff[0].WithSize((uint)Marshal.SizeOf<ModelInfo>());
MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf(typeof(ModelInfo)));
MemoryHelper.FillWithZeros(context.Memory, outputPosition, Marshal.SizeOf<ModelInfo>());
uint deviceHandle = (uint)context.RequestData.ReadUInt64();

View File

@@ -8,6 +8,8 @@ using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostAsGpu;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostDbgGpu;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostProfGpu;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
using Ryujinx.HLE.HOS.Services.Nv.Types;
using Ryujinx.Memory;
@@ -23,7 +25,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv
[Service("nvdrv:t")]
class INvDrvServices : IpcService
{
private static Dictionary<string, Type> _deviceFileRegistry = new Dictionary<string, Type>()
private static readonly List<string> _deviceFileDebugRegistry = new List<string>()
{
"/dev/nvhost-dbg-gpu",
"/dev/nvhost-prof-gpu"
};
private static readonly Dictionary<string, Type> _deviceFileRegistry = new Dictionary<string, Type>()
{
{ "/dev/nvmap", typeof(NvMapDeviceFile) },
{ "/dev/nvhost-ctrl", typeof(NvHostCtrlDeviceFile) },
@@ -35,6 +43,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv
//{ "/dev/nvhost-nvjpg", typeof(NvHostChannelDeviceFile) },
{ "/dev/nvhost-vic", typeof(NvHostChannelDeviceFile) },
//{ "/dev/nvhost-display", typeof(NvHostChannelDeviceFile) },
{ "/dev/nvhost-dbg-gpu", typeof(NvHostDbgGpuDeviceFile) },
{ "/dev/nvhost-prof-gpu", typeof(NvHostProfGpuDeviceFile) },
};
public static IdDictionary DeviceFileIdRegistry = new IdDictionary();
@@ -44,13 +54,23 @@ namespace Ryujinx.HLE.HOS.Services.Nv
private bool _transferMemInitialized = false;
// TODO: This should call set:sys::GetDebugModeFlag
private bool _debugModeEnabled = false;
public INvDrvServices(ServiceCtx context) : base(context.Device.System.NvDrvServer)
{
_owner = 0;
}
private int Open(ServiceCtx context, string path)
private NvResult Open(ServiceCtx context, string path, out int fd)
{
fd = -1;
if (!_debugModeEnabled && _deviceFileDebugRegistry.Contains(path))
{
return NvResult.NotSupported;
}
if (_deviceFileRegistry.TryGetValue(path, out Type deviceFileClass))
{
ConstructorInfo constructor = deviceFileClass.GetConstructor(new Type[] { typeof(ServiceCtx), typeof(IVirtualMemoryManager), typeof(ulong) });
@@ -59,14 +79,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv
deviceFile.Path = path;
return DeviceFileIdRegistry.Add(deviceFile);
}
else
{
Logger.Warning?.Print(LogClass.ServiceNv, $"Cannot find file device \"{path}\"!");
fd = DeviceFileIdRegistry.Add(deviceFile);
return NvResult.Success;
}
return -1;
Logger.Warning?.Print(LogClass.ServiceNv, $"Cannot find file device \"{path}\"!");
return NvResult.FileOperationFailed;
}
private NvResult GetIoctlArgument(ServiceCtx context, NvIoctl ioctlCommand, out Span<byte> arguments)
@@ -229,12 +249,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv
string path = MemoryHelper.ReadAsciiString(context.Memory, pathPtr, (long)pathSize);
fd = Open(context, path);
if (fd == -1)
{
errorCode = NvResult.FileOperationFailed;
}
errorCode = Open(context, path, out fd);
}
context.ResponseData.Write(fd);

View File

@@ -0,0 +1,11 @@
using Ryujinx.Memory;
using System;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostDbgGpu
{
class NvHostDbgGpuDeviceFile : NvDeviceFile
{
public NvHostDbgGpuDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, ulong owner) : base(context, owner) { }
public override void Close() { }
}
}

View File

@@ -0,0 +1,11 @@
using Ryujinx.Memory;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostProfGpu
{
class NvHostProfGpuDeviceFile : NvDeviceFile
{
public NvHostProfGpuDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, ulong owner) : base(context, owner) { }
public override void Close() { }
}
}

Some files were not shown because too many files have changed in this diff Show More