Compare commits

...

28 Commits

Author SHA1 Message Date
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
124 changed files with 1999 additions and 669 deletions

View File

@@ -1587,6 +1587,12 @@ namespace ARMeilleure.CodeGen.X86
Debug.Assert(dest.Type.IsInteger() && source.Type.IsInteger()); 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); context.Assembler.Mov(dest, source, OperandType.I32);
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -27,7 +27,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 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 ActualDir = "0";
private const string BackupDir = "1"; private const string BackupDir = "1";

View File

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

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> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="OpenTK.OpenAL" Version="4.7.5" /> <PackageReference Include="OpenTK.OpenAL" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

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

View File

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

View File

@@ -8,7 +8,7 @@ namespace Ryujinx.Ava.Ui.Helper
public delegate void UpdateBoundsCallbackDelegate(Rect rect); public delegate void UpdateBoundsCallbackDelegate(Rect rect);
[SupportedOSPlatform("macos")] [SupportedOSPlatform("macos")]
static class MetalHelper static partial class MetalHelper
{ {
private const string LibObjCImport = "/usr/lib/libobjc.A.dylib"; private const string LibObjCImport = "/usr/lib/libobjc.A.dylib";
@@ -100,28 +100,28 @@ namespace Ryujinx.Ava.Ui.Helper
// TODO // TODO
} }
[DllImport(LibObjCImport)] [LibraryImport(LibObjCImport)]
private static unsafe extern IntPtr sel_registerName(byte* data); private static unsafe partial IntPtr sel_registerName(byte* data);
[DllImport(LibObjCImport)] [LibraryImport(LibObjCImport)]
private static unsafe extern IntPtr objc_getClass(byte* data); private static unsafe partial IntPtr objc_getClass(byte* data);
[DllImport(LibObjCImport)] [LibraryImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector); private static partial void objc_msgSend(IntPtr receiver, Selector selector);
[DllImport(LibObjCImport)] [LibraryImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector, byte value); private static partial void objc_msgSend(IntPtr receiver, Selector selector, byte value);
[DllImport(LibObjCImport)] [LibraryImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value); private static partial void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value);
[DllImport(LibObjCImport)] [LibraryImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector, NSRect point); private static partial void objc_msgSend(IntPtr receiver, Selector selector, NSRect point);
[DllImport(LibObjCImport)] [LibraryImport(LibObjCImport)]
private static extern void objc_msgSend(IntPtr receiver, Selector selector, double value); private static partial void objc_msgSend(IntPtr receiver, Selector selector, double value);
[DllImport(LibObjCImport, EntryPoint = "objc_msgSend")] [LibraryImport(LibObjCImport, EntryPoint = "objc_msgSend")]
private static extern IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector); 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 internal static class AvaloniaKeyboardMappingHelper
{ {
private static readonly AvaKey[] _keyMapping = new AvaKey[(int)Key.Count] private static readonly AvaKey[] _keyMapping = {
{
// NOTE: Invalid // NOTE: Invalid
AvaKey.None, AvaKey.None,
@@ -151,16 +150,16 @@ namespace Ryujinx.Ava.Input
static AvaloniaKeyboardMappingHelper() 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. // NOTE: Avalonia.Input.Key is not contiguous and quite large, so use a dictionary instead of an array.
_avaKeyMapping = new Dictionary<AvaKey, Key>(); _avaKeyMapping = new Dictionary<AvaKey, Key>();
foreach (var key in inputKeys) 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.Ava.Ui.Windows;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Ui.Common.Helper;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@@ -278,14 +279,15 @@ namespace Ryujinx.Modules
{ {
string ryuName = Path.GetFileName(Environment.ProcessPath); string ryuName = Path.GetFileName(Environment.ProcessPath);
string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName); 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); Environment.Exit(0);
} }
@@ -456,16 +458,19 @@ namespace Ryujinx.Modules
worker.Start(); worker.Start();
} }
[DllImport("libc", SetLastError = true)] private static void SetFileExecutable(string path)
private static extern int chmod(string path, uint mode);
private static void SetUnixPermissions()
{ {
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); Directory.Delete(UpdateDir, true);
SetUnixPermissions(); SetFileExecutable(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Ryujinx"));
UpdateSuccessful = true; UpdateSuccessful = true;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -8,7 +8,7 @@ using Ryujinx.Common.Logging;
namespace Ryujinx.Common.SystemInfo namespace Ryujinx.Common.SystemInfo
{ {
[SupportedOSPlatform("macos")] [SupportedOSPlatform("macos")]
class MacOSSystemInfo : SystemInfo partial class MacOSSystemInfo : SystemInfo
{ {
internal MacOSSystemInfo() internal MacOSSystemInfo()
{ {
@@ -60,8 +60,8 @@ namespace Ryujinx.Common.SystemInfo
private const string SystemLibraryName = "libSystem.dylib"; private const string SystemLibraryName = "libSystem.dylib";
[DllImport(SystemLibraryName, CharSet = CharSet.Ansi, SetLastError = true)] [LibraryImport(SystemLibraryName, SetLastError = true)]
private static extern int sysctlbyname(string name, IntPtr oldValue, ref ulong oldSize, IntPtr newValue, ulong newValueSize); 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) private static int sysctlbyname(string name, IntPtr oldValue, ref ulong oldSize)
{ {
@@ -116,11 +116,11 @@ namespace Ryujinx.Common.SystemInfo
return res; return res;
} }
[DllImport(SystemLibraryName, CharSet = CharSet.Ansi, SetLastError = true)] [LibraryImport(SystemLibraryName, SetLastError = true)]
private static extern uint mach_host_self(); private static partial uint mach_host_self();
[DllImport(SystemLibraryName, CharSet = CharSet.Ansi, SetLastError = true)] [LibraryImport(SystemLibraryName, SetLastError = true)]
private static extern int host_page_size(uint host, ref uint out_page_size); private static partial int host_page_size(uint host, ref uint out_page_size);
[StructLayout(LayoutKind.Sequential, Pack = 8)] [StructLayout(LayoutKind.Sequential, Pack = 8)]
struct VMStatistics64 struct VMStatistics64
@@ -151,7 +151,7 @@ namespace Ryujinx.Common.SystemInfo
public ulong TotalUncompressedPagesInCompressor; public ulong TotalUncompressedPagesInCompressor;
} }
[DllImport(SystemLibraryName, CharSet = CharSet.Ansi, SetLastError = true)] [LibraryImport(SystemLibraryName, SetLastError = true)]
private static extern int host_statistics64(uint host_priv, int host_flavor, ref VMStatistics64 host_info64_out, ref uint host_info64_outCnt); 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 namespace Ryujinx.Common.SystemInfo
{ {
[SupportedOSPlatform("windows")] [SupportedOSPlatform("windows")]
class WindowsSystemInfo : SystemInfo partial class WindowsSystemInfo : SystemInfo
{ {
internal WindowsSystemInfo() internal WindowsSystemInfo()
{ {
@@ -18,7 +18,7 @@ namespace Ryujinx.Common.SystemInfo
private static (ulong Total, ulong Available) GetMemoryStats() private static (ulong Total, ulong Available) GetMemoryStats()
{ {
MemoryStatusEx memStatus = new MemoryStatusEx(); MemoryStatusEx memStatus = new MemoryStatusEx();
if (GlobalMemoryStatusEx(memStatus)) if (GlobalMemoryStatusEx(ref memStatus))
{ {
return (memStatus.TotalPhys, memStatus.AvailPhys); // Bytes return (memStatus.TotalPhys, memStatus.AvailPhys); // Bytes
} }
@@ -45,8 +45,8 @@ namespace Ryujinx.Common.SystemInfo
return Environment.GetEnvironmentVariable("PROCESSOR_IDENTIFIER").Trim(); return Environment.GetEnvironmentVariable("PROCESSOR_IDENTIFIER").Trim();
} }
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] [StructLayout(LayoutKind.Sequential)]
private class MemoryStatusEx private struct MemoryStatusEx
{ {
public uint Length; public uint Length;
public uint MemoryLoad; public uint MemoryLoad;
@@ -60,13 +60,13 @@ namespace Ryujinx.Common.SystemInfo
public MemoryStatusEx() public MemoryStatusEx()
{ {
Length = (uint)Marshal.SizeOf(typeof(MemoryStatusEx)); Length = (uint)Marshal.SizeOf<MemoryStatusEx>();
} }
} }
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static partial bool GlobalMemoryStatusEx(ref MemoryStatusEx lpBuffer);
private static extern bool GlobalMemoryStatusEx([In, Out] MemoryStatusEx lpBuffer);
private static ManagementObjectCollection GetWMIObjects(string scope, string query) private static ManagementObjectCollection GetWMIObjects(string scope, string query)
{ {

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,12 +1,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Device 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); private const int RegisterSize = sizeof(int);

View File

@@ -14,6 +14,7 @@ namespace Ryujinx.Graphics.GAL
public readonly bool SupportsBc123Compression; public readonly bool SupportsBc123Compression;
public readonly bool SupportsBc45Compression; public readonly bool SupportsBc45Compression;
public readonly bool SupportsBc67Compression; public readonly bool SupportsBc67Compression;
public readonly bool SupportsEtc2Compression;
public readonly bool Supports3DTextureCompression; public readonly bool Supports3DTextureCompression;
public readonly bool SupportsBgraFormat; public readonly bool SupportsBgraFormat;
public readonly bool SupportsR4G4Format; public readonly bool SupportsR4G4Format;
@@ -50,6 +51,7 @@ namespace Ryujinx.Graphics.GAL
bool supportsBc123Compression, bool supportsBc123Compression,
bool supportsBc45Compression, bool supportsBc45Compression,
bool supportsBc67Compression, bool supportsBc67Compression,
bool supportsEtc2Compression,
bool supports3DTextureCompression, bool supports3DTextureCompression,
bool supportsBgraFormat, bool supportsBgraFormat,
bool supportsR4G4Format, bool supportsR4G4Format,
@@ -83,6 +85,7 @@ namespace Ryujinx.Graphics.GAL
SupportsBc123Compression = supportsBc123Compression; SupportsBc123Compression = supportsBc123Compression;
SupportsBc45Compression = supportsBc45Compression; SupportsBc45Compression = supportsBc45Compression;
SupportsBc67Compression = supportsBc67Compression; SupportsBc67Compression = supportsBc67Compression;
SupportsEtc2Compression = supportsEtc2Compression;
Supports3DTextureCompression = supports3DTextureCompression; Supports3DTextureCompression = supports3DTextureCompression;
SupportsBgraFormat = supportsBgraFormat; SupportsBgraFormat = supportsBgraFormat;
SupportsR4G4Format = supportsR4G4Format; SupportsR4G4Format = supportsR4G4Format;

View File

@@ -516,6 +516,27 @@ namespace Ryujinx.Graphics.GAL
return false; 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> /// <summary>
/// Checks if the texture format is a BGR format. /// Checks if the texture format is a BGR format.
/// </summary> /// </summary>

View File

@@ -2,6 +2,7 @@ using Ryujinx.Graphics.Device;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Ryujinx.Graphics.Gpu.Engine namespace Ryujinx.Graphics.Gpu.Engine
@@ -21,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Engine
/// Represents a device's state, with a additional shadow state. /// Represents a device's state, with a additional shadow state.
/// </summary> /// </summary>
/// <typeparam name="TState">Type of the state</typeparam> /// <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> _state;
private readonly DeviceState<TState> _shadowState; private readonly DeviceState<TState> _shadowState;

View File

@@ -98,7 +98,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// <param name="argument">Method call argument</param> /// <param name="argument">Method call argument</param>
public void DrawEnd(ThreedClass engine, int argument) 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> /// <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="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="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> /// <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( ConditionalRenderEnabled renderEnable = ConditionalRendering.GetRenderEnable(
_context, _context,
@@ -135,6 +142,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_drawState.FirstIndex = firstIndex; _drawState.FirstIndex = firstIndex;
_drawState.IndexCount = indexCount; _drawState.IndexCount = indexCount;
_drawState.DrawFirstVertex = drawFirstVertex;
_drawState.DrawVertexCount = drawVertexCount;
_currentSpecState.SetHasConstantBufferDrawParameters(false); _currentSpecState.SetHasConstantBufferDrawParameters(false);
engine.UpdateState(); engine.UpdateState();
@@ -156,10 +165,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_instancedIndexCount = ibCount != 0 ? ibCount : indexCount; _instancedIndexCount = ibCount != 0 ? ibCount : indexCount;
var drawState = _state.State.VertexBufferDrawState; _instancedDrawStateFirst = drawFirstVertex;
_instancedDrawStateCount = drawVertexCount;
_instancedDrawStateFirst = drawState.First;
_instancedDrawStateCount = drawState.Count;
_drawState.DrawIndexed = false; _drawState.DrawIndexed = false;
@@ -195,7 +202,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{ {
var drawState = _state.State.VertexBufferDrawState; 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; _drawState.DrawIndexed = false;
@@ -216,16 +223,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
bool incrementInstance = (argument & (1 << 26)) != 0; bool incrementInstance = (argument & (1 << 26)) != 0;
bool resetInstance = (argument & (1 << 27)) == 0; bool resetInstance = (argument & (1 << 27)) == 0;
if (_state.State.PrimitiveTypeOverrideEnable) PrimitiveType type = (PrimitiveType)(argument & 0xffff);
{ DrawBegin(incrementInstance, resetInstance, type);
PrimitiveTypeOverride typeOverride = _state.State.PrimitiveTypeOverride;
DrawBegin(incrementInstance, resetInstance, typeOverride.Convert());
}
else
{
PrimitiveType type = (PrimitiveType)(argument & 0xffff);
DrawBegin(incrementInstance, resetInstance, type.Convert());
}
} }
/// <summary> /// <summary>
@@ -234,8 +233,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary> /// </summary>
/// <param name="incrementInstance">Indicates if the current instance should be incremented</param> /// <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="resetInstance">Indicates if the current instance should be set to zero</param>
/// <param name="topology">Primitive topology</param> /// <param name="primitiveType">Primitive type</param>
private void DrawBegin(bool incrementInstance, bool resetInstance, PrimitiveTopology topology) private void DrawBegin(bool incrementInstance, bool resetInstance, PrimitiveType primitiveType)
{ {
if (incrementInstance) if (incrementInstance)
{ {
@@ -248,6 +247,18 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_instanceIndex = 0; _instanceIndex = 0;
} }
PrimitiveTopology topology;
if (_state.State.PrimitiveTypeOverrideEnable)
{
PrimitiveTypeOverride typeOverride = _state.State.PrimitiveTypeOverride;
topology = typeOverride.Convert();
}
else
{
topology = primitiveType.Convert();
}
UpdateTopology(topology); UpdateTopology(topology);
} }
@@ -276,46 +287,70 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_drawState.DrawIndexed = true; _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> /// <summary>
/// Performs a indexed draw with a low number of index buffer elements. /// Performs a indexed draw with 8-bit index buffer elements.
/// </summary> /// </summary>
/// <param name="engine">3D engine where this method is being called</param> /// <param name="engine">3D engine where this method is being called</param>
/// <param name="argument">Method call argument</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> /// <summary>
/// Performs a indexed draw with a low number of index buffer elements. /// Performs a indexed draw with 16-bit index buffer elements.
/// </summary> /// </summary>
/// <param name="engine">3D engine where this method is being called</param> /// <param name="engine">3D engine where this method is being called</param>
/// <param name="argument">Method call argument</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> /// <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. /// while also pre-incrementing the current instance value.
/// </summary> /// </summary>
/// <param name="engine">3D engine where this method is being called</param> /// <param name="engine">3D engine where this method is being called</param>
/// <param name="argument">Method call argument</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> /// <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. /// while also pre-incrementing the current instance value.
/// </summary> /// </summary>
/// <param name="engine">3D engine where this method is being called</param> /// <param name="engine">3D engine where this method is being called</param>
/// <param name="argument">Method call argument</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> /// <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="engine">3D engine where this method is being called</param>
/// <param name="argument">Method call argument</param> /// <param name="argument">Method call argument</param>
/// <param name="instanced">True to increment the current instance value, false otherwise</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, (PrimitiveType)((argument >> 28) & 0xf));
DrawBegin(instanced, !instanced, typeOverride.Convert());
int firstIndex = argument & 0xffff; int firstIndex = argument & 0xffff;
int indexCount = (argument >> 16) & 0xfff; int indexCount = (argument >> 16) & 0xfff;
@@ -339,7 +372,52 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_drawState.DrawIndexed = true; _drawState.DrawIndexed = true;
engine.ForceStateDirty(IndexBufferCountMethodOffset * 4); 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; _drawState.DrawIndexed = oldDrawIndexed;
} }
@@ -449,8 +527,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
} }
else else
{ {
_state.State.VertexBufferDrawState.First = firstVertex; _drawState.DrawFirstVertex = firstVertex;
_state.State.VertexBufferDrawState.Count = count; _drawState.DrawVertexCount = count;
engine.ForceStateDirty(VertexBufferFirstMethodOffset * 4); engine.ForceStateDirty(VertexBufferFirstMethodOffset * 4);
} }

View File

@@ -17,6 +17,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary> /// </summary>
public int IndexCount; 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> /// <summary>
/// Indicates if the next draw will be a indexed draw. /// Indicates if the next draw will be a indexed draw.
/// </summary> /// </summary>

View File

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

View File

@@ -1,7 +1,5 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
using Ryujinx.Graphics.Gpu.Engine.Types; using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.Gpu.Shader;
@@ -991,6 +989,8 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
bool drawIndexed = _drawState.DrawIndexed; bool drawIndexed = _drawState.DrawIndexed;
bool drawIndirect = _drawState.DrawIndirect; bool drawIndirect = _drawState.DrawIndirect;
int drawFirstVertex = _drawState.DrawFirstVertex;
int drawVertexCount = _drawState.DrawVertexCount;
uint vbEnableMask = 0; uint vbEnableMask = 0;
for (int index = 0; index < Constants.TotalVertexBuffers; index++) for (int index = 0; index < Constants.TotalVertexBuffers; index++)
@@ -1052,9 +1052,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
int firstInstance = (int)_state.State.FirstInstance; int firstInstance = (int)_state.State.FirstInstance;
var drawState = _state.State.VertexBufferDrawState; size = Math.Min(vbSize, (ulong)((firstInstance + drawFirstVertex + drawVertexCount) * stride));
size = Math.Min(vbSize, (ulong)((firstInstance + drawState.First + drawState.Count) * stride));
} }
_pipeline.VertexBuffers[index] = new BufferPipelineDescriptor(_channel.MemoryManager.IsMapped(address), stride, divisor); _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++) 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); _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.TextureBarrier), new RwCallback(TextureBarrier, null) },
{ nameof(ThreedClassState.TextureBarrierTiled), new RwCallback(TextureBarrierTiled, null) }, { nameof(ThreedClassState.TextureBarrierTiled), new RwCallback(TextureBarrierTiled, null) },
{ nameof(ThreedClassState.DrawTextureSrcY), new RwCallback(DrawTexture, 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.VbElementU8), new RwCallback(VbElementU8, null) },
{ nameof(ThreedClassState.VbElementU16), new RwCallback(VbElementU16, null) }, { nameof(ThreedClassState.VbElementU16), new RwCallback(VbElementU16, null) },
{ nameof(ThreedClassState.VbElementU32), new RwCallback(VbElementU32, 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.RenderEnableCondition), new RwCallback(null, Zero) },
{ nameof(ThreedClassState.DrawEnd), new RwCallback(DrawEnd, null) }, { nameof(ThreedClassState.DrawEnd), new RwCallback(DrawEnd, null) },
{ nameof(ThreedClassState.DrawBegin), new RwCallback(DrawBegin, null) }, { nameof(ThreedClassState.DrawBegin), new RwCallback(DrawBegin, null) },
{ nameof(ThreedClassState.DrawIndexedSmall), new RwCallback(DrawIndexedSmall, null) }, { nameof(ThreedClassState.DrawIndexBuffer32BeginEndInstanceFirst), new RwCallback(DrawIndexBuffer32BeginEndInstanceFirst, null) },
{ nameof(ThreedClassState.DrawIndexedSmall2), new RwCallback(DrawIndexedSmall2, null) }, { nameof(ThreedClassState.DrawIndexBuffer16BeginEndInstanceFirst), new RwCallback(DrawIndexBuffer16BeginEndInstanceFirst, null) },
{ nameof(ThreedClassState.DrawIndexedSmallIncInstance), new RwCallback(DrawIndexedSmallIncInstance, null) }, { nameof(ThreedClassState.DrawIndexBuffer8BeginEndInstanceFirst), new RwCallback(DrawIndexBuffer8BeginEndInstanceFirst, null) },
{ nameof(ThreedClassState.DrawIndexedSmallIncInstance2), new RwCallback(DrawIndexedSmallIncInstance2, 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.IndexBufferCount), new RwCallback(SetIndexBufferCount, null) },
{ nameof(ThreedClassState.Clear), new RwCallback(Clear, null) }, { nameof(ThreedClassState.Clear), new RwCallback(Clear, null) },
{ nameof(ThreedClassState.SemaphoreControl), new RwCallback(Report, null) }, { nameof(ThreedClassState.SemaphoreControl), new RwCallback(Report, null) },
@@ -303,6 +307,25 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_drawManager.DrawTexture(this, argument); _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> /// <summary>
/// Pushes four 8-bit index buffer elements. /// Pushes four 8-bit index buffer elements.
/// </summary> /// </summary>
@@ -370,41 +393,60 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
} }
/// <summary> /// <summary>
/// Performs a indexed draw with a low number of index buffer elements. /// Performs a indexed draw with 8-bit index buffer elements.
/// </summary> /// </summary>
/// <param name="argument">Method call argument</param> /// <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> /// <summary>
/// Performs a indexed draw with a low number of index buffer elements. /// Performs a indexed draw with 16-bit index buffer elements.
/// </summary> /// </summary>
/// <param name="argument">Method call argument</param> /// <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> /// <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. /// while also pre-incrementing the current instance value.
/// </summary> /// </summary>
/// <param name="argument">Method call argument</param> /// <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> /// <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. /// while also pre-incrementing the current instance value.
/// </summary> /// </summary>
/// <param name="argument">Method call argument</param> /// <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> /// <summary>

View File

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

View File

@@ -208,8 +208,10 @@ namespace Ryujinx.Graphics.Gpu.Image
Bc6HSf16Float = Bc6HSf16 | RFloat | GFloat | BFloat | AFloat, // 0x7ff90 Bc6HSf16Float = Bc6HSf16 | RFloat | GFloat | BFloat | AFloat, // 0x7ff90
Bc6HUf16Float = Bc6HUf16 | RFloat | GFloat | BFloat | AFloat, // 0x7ff91 Bc6HUf16Float = Bc6HUf16 | RFloat | GFloat | BFloat | AFloat, // 0x7ff91
Etc2RgbUnorm = Etc2Rgb | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24906 Etc2RgbUnorm = Etc2Rgb | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24906
Etc2RgbPtaUnorm = Etc2RgbPta | RUnorm | GUnorm | BUnorm | AUnorm, // 0x2490a
Etc2RgbaUnorm = Etc2Rgba | RUnorm | GUnorm | BUnorm | AUnorm, // 0x2490b Etc2RgbaUnorm = Etc2Rgba | RUnorm | GUnorm | BUnorm | AUnorm, // 0x2490b
Etc2RgbUnormSrgb = Etc2Rgb | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4906 Etc2RgbUnormSrgb = Etc2Rgb | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa4906
Etc2RgbPtaUnormSrgb = Etc2RgbPta | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa490a
Etc2RgbaUnormSrgb = Etc2Rgba | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa490b Etc2RgbaUnormSrgb = Etc2Rgba | RUnorm | GUnorm | BUnorm | AUnorm | Srgb, // 0xa490b
Astc2D4x4Unorm = Astc2D4x4 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24940 Astc2D4x4Unorm = Astc2D4x4 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24940
Astc2D5x4Unorm = Astc2D5x4 | RUnorm | GUnorm | BUnorm | AUnorm, // 0x24950 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.Bc6HSf16Float, new FormatInfo(Format.Bc6HSfloat, 4, 4, 16, 4) },
{ TextureFormat.Bc6HUf16Float, new FormatInfo(Format.Bc6HUfloat, 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.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.Etc2RgbaUnorm, new FormatInfo(Format.Etc2RgbaUnorm, 4, 4, 16, 4) },
{ TextureFormat.Etc2RgbUnormSrgb, new FormatInfo(Format.Etc2RgbSrgb, 4, 4, 8, 3) }, { 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.Etc2RgbaUnormSrgb, new FormatInfo(Format.Etc2RgbaSrgb, 4, 4, 16, 4) },
{ TextureFormat.Astc2D4x4Unorm, new FormatInfo(Format.Astc4x4Unorm, 4, 4, 16, 4) }, { TextureFormat.Astc2D4x4Unorm, new FormatInfo(Format.Astc4x4Unorm, 4, 4, 16, 4) },
{ TextureFormat.Astc2D5x4Unorm, new FormatInfo(Format.Astc5x4Unorm, 5, 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; 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)) else if (!TextureCompatibility.HostSupportsBcFormat(Format, Target, _context.Capabilities))
{ {
@@ -895,6 +909,10 @@ namespace Ryujinx.Graphics.Gpu.Image
break; break;
} }
} }
else if (!_context.Capabilities.SupportsR4G4Format && Format == Format.R4G4Unorm)
{
result = PixelConverter.ConvertR4G4ToR4G4B4A4(result);
}
return result; 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. // 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. // 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; _cachedTexturePool = texturePool;
_cachedSamplerPool = samplerPool; _cachedSamplerPool = samplerPool;
}
bool poolModified = false;
if (texturePool != null) if (texturePool != null)
{ {

View File

@@ -67,6 +67,10 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>A host compatible format</returns> /// <returns>A host compatible format</returns>
public static FormatInfo ToHostCompatibleFormat(TextureInfo info, Capabilities caps) 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 (!caps.SupportsAstcCompression)
{ {
if (info.FormatInfo.Format.IsAstcUnorm()) 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)) 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) switch (info.FormatInfo.Format)
{ {
case Format.Bc1RgbaSrgb: case Format.Bc1RgbaSrgb:
@@ -119,6 +115,26 @@ 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)
{
return new FormatInfo(Format.R4G4B4A4Unorm, 1, 1, 2, 4);
}
return info.FormatInfo; return info.FormatInfo;
} }

View File

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

View File

@@ -53,7 +53,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
{ {
CachedShaderStage currentStage = stages[i]; 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); return ShaderCache.GetBindings(currentStage.Info);
} }

View File

@@ -5,7 +5,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native namespace Ryujinx.Graphics.Nvdec.FFmpeg.Native
{ {
static class FFmpegApi static partial class FFmpegApi
{ {
public const string AvCodecLibraryName = "avcodec"; public const string AvCodecLibraryName = "avcodec";
public const string AvUtilLibraryName = "avutil"; 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); 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)] [LibraryImport(AvUtilLibraryName)]
internal static unsafe extern AVFrame* av_frame_alloc(); internal static unsafe partial AVFrame* av_frame_alloc();
[DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)] [LibraryImport(AvUtilLibraryName)]
internal static unsafe extern void av_frame_unref(AVFrame* frame); internal static unsafe partial void av_frame_unref(AVFrame* frame);
[DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)] [LibraryImport(AvUtilLibraryName)]
internal static unsafe extern void av_free(AVFrame* frame); internal static unsafe partial void av_free(AVFrame* frame);
[DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)] [LibraryImport(AvUtilLibraryName)]
internal static unsafe extern void av_log_set_level(AVLog level); internal static unsafe partial void av_log_set_level(AVLog level);
[DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)] [LibraryImport(AvUtilLibraryName)]
internal static unsafe extern void av_log_set_callback(av_log_set_callback_callback callback); internal static unsafe partial void av_log_set_callback(av_log_set_callback_callback callback);
[DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)] [LibraryImport(AvUtilLibraryName)]
internal static unsafe extern AVLog av_log_get_level(); internal static unsafe partial AVLog av_log_get_level();
[DllImport(AvUtilLibraryName, CallingConvention = CallingConvention.Cdecl)] [LibraryImport(AvUtilLibraryName)]
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); 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)] [LibraryImport(AvCodecLibraryName)]
internal static unsafe extern AVCodec* avcodec_find_decoder(AVCodecID id); internal static unsafe partial AVCodec* avcodec_find_decoder(AVCodecID id);
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)] [LibraryImport(AvCodecLibraryName)]
internal static unsafe extern AVCodecContext* avcodec_alloc_context3(AVCodec* codec); internal static unsafe partial AVCodecContext* avcodec_alloc_context3(AVCodec* codec);
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)] [LibraryImport(AvCodecLibraryName)]
internal static unsafe extern int avcodec_open2(AVCodecContext* avctx, AVCodec* codec, void **options); internal static unsafe partial int avcodec_open2(AVCodecContext* avctx, AVCodec* codec, void **options);
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)] [LibraryImport(AvCodecLibraryName)]
internal static unsafe extern int avcodec_close(AVCodecContext* avctx); internal static unsafe partial int avcodec_close(AVCodecContext* avctx);
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)] [LibraryImport(AvCodecLibraryName)]
internal static unsafe extern void avcodec_free_context(AVCodecContext** avctx); internal static unsafe partial void avcodec_free_context(AVCodecContext** avctx);
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)] [LibraryImport(AvCodecLibraryName)]
internal static unsafe extern AVPacket* av_packet_alloc(); internal static unsafe partial AVPacket* av_packet_alloc();
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)] [LibraryImport(AvCodecLibraryName)]
internal static unsafe extern void av_packet_unref(AVPacket* pkt); internal static unsafe partial void av_packet_unref(AVPacket* pkt);
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)] [LibraryImport(AvCodecLibraryName)]
internal static unsafe extern void av_packet_free(AVPacket** pkt); internal static unsafe partial void av_packet_free(AVPacket** pkt);
[DllImport(AvCodecLibraryName, CallingConvention = CallingConvention.Cdecl)] [LibraryImport(AvCodecLibraryName)]
internal static unsafe extern int avcodec_version(); internal static unsafe partial int avcodec_version();
} }
} }

View File

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

View File

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

View File

@@ -110,6 +110,7 @@ namespace Ryujinx.Graphics.OpenGL
supportsBc123Compression: HwCapabilities.SupportsTextureCompressionS3tc, supportsBc123Compression: HwCapabilities.SupportsTextureCompressionS3tc,
supportsBc45Compression: HwCapabilities.SupportsTextureCompressionRgtc, supportsBc45Compression: HwCapabilities.SupportsTextureCompressionRgtc,
supportsBc67Compression: true, // Should check BPTC extension, but for some reason NVIDIA is not exposing the extension. supportsBc67Compression: true, // Should check BPTC extension, but for some reason NVIDIA is not exposing the extension.
supportsEtc2Compression: true,
supports3DTextureCompression: false, supports3DTextureCompression: false,
supportsBgraFormat: false, supportsBgraFormat: false,
supportsR4G4Format: false, supportsR4G4Format: false,

View File

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

View File

@@ -262,7 +262,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
value = (value >> operation.Index * 16) & 0xffff; 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) 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

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

View File

@@ -374,7 +374,24 @@ namespace Ryujinx.Graphics.Vulkan
api.GetPhysicalDeviceProperties(physicalDevice, out var properties); api.GetPhysicalDeviceProperties(physicalDevice, out var properties);
bool useRobustBufferAccess = VendorUtils.FromId(properties.VendorID) == Vendor.Nvidia; 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() var features = new PhysicalDeviceFeatures()
{ {
@@ -491,6 +508,23 @@ namespace Ryujinx.Graphics.Vulkan
pExtendedFeatures = &featuresSubgroupSizeControl; 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(); var enabledExtensions = RequiredExtensions.Union(DesirableExtensions.Intersect(supportedExtensions)).ToArray();
IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length]; IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];

View File

@@ -177,6 +177,11 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDeviceShaderFloat16Int8Features SType = StructureType.PhysicalDeviceShaderFloat16Int8Features
}; };
PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT()
{
SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt
};
if (supportedExtensions.Contains("VK_EXT_robustness2")) if (supportedExtensions.Contains("VK_EXT_robustness2"))
{ {
features2.PNext = &featuresRobustness2; features2.PNext = &featuresRobustness2;
@@ -188,8 +193,18 @@ namespace Ryujinx.Graphics.Vulkan
features2.PNext = &featuresShaderInt8; features2.PNext = &featuresShaderInt8;
} }
if (supportedExtensions.Contains("VK_EXT_custom_border_color"))
{
featuresCustomBorderColor.PNext = features2.PNext;
features2.PNext = &featuresCustomBorderColor;
}
Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2); Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2);
bool customBorderColorSupported = supportedExtensions.Contains("VK_EXT_custom_border_color") &&
featuresCustomBorderColor.CustomBorderColors &&
featuresCustomBorderColor.CustomBorderColorWithoutFormat;
ref var properties = ref properties2.Properties; ref var properties = ref properties2.Properties;
SampleCountFlags supportedSampleCounts = SampleCountFlags supportedSampleCounts =
@@ -199,7 +214,7 @@ namespace Ryujinx.Graphics.Vulkan
Capabilities = new HardwareCapabilities( Capabilities = new HardwareCapabilities(
supportedExtensions.Contains("VK_EXT_index_type_uint8"), supportedExtensions.Contains("VK_EXT_index_type_uint8"),
supportedExtensions.Contains("VK_EXT_custom_border_color"), customBorderColorSupported,
supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName), supportedExtensions.Contains(KhrDrawIndirectCount.ExtensionName),
supportedExtensions.Contains("VK_EXT_fragment_shader_interlock"), supportedExtensions.Contains("VK_EXT_fragment_shader_interlock"),
supportedExtensions.Contains("VK_NV_geometry_shader_passthrough"), supportedExtensions.Contains("VK_NV_geometry_shader_passthrough"),
@@ -373,6 +388,13 @@ namespace Ryujinx.Graphics.Vulkan
GAL.Format.Bc7Srgb, GAL.Format.Bc7Srgb,
GAL.Format.Bc7Unorm); 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);
PhysicalDeviceVulkan12Features featuresVk12 = new PhysicalDeviceVulkan12Features() PhysicalDeviceVulkan12Features featuresVk12 = new PhysicalDeviceVulkan12Features()
{ {
@@ -399,6 +421,7 @@ namespace Ryujinx.Graphics.Vulkan
supportsBc123Compression: supportsBc123CompressionFormat, supportsBc123Compression: supportsBc123CompressionFormat,
supportsBc45Compression: supportsBc45CompressionFormat, supportsBc45Compression: supportsBc45CompressionFormat,
supportsBc67Compression: supportsBc67CompressionFormat, supportsBc67Compression: supportsBc67CompressionFormat,
supportsEtc2Compression: supportsEtc2CompressionFormat,
supports3DTextureCompression: true, supports3DTextureCompression: true,
supportsBgraFormat: true, supportsBgraFormat: true,
supportsR4G4Format: false, supportsR4G4Format: false,

View File

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

View File

@@ -9,6 +9,7 @@ using Ryujinx.HLE.Ui.Input;
using Ryujinx.Memory; using Ryujinx.Memory;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -797,7 +798,7 @@ namespace Ryujinx.HLE.HOS.Applets
return sb.ToString(); 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 where T : struct
{ {
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned); GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);

View File

@@ -4,8 +4,6 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
{ {
public class UserProfile public class UserProfile
{ {
private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public UserId UserId { get; } public UserId UserId { get; }
public long LastModifiedTimestamp { get; set; } public long LastModifiedTimestamp { get; set; }
@@ -83,7 +81,7 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc
private void UpdateLastModifiedTimestamp() 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 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. applicationAlbumEntry.AlbumFileDateTime.UniqueId++;
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);
// TODO: Handle that using the FS service implementation and return the right error code instead of throwing exceptions. filePath = GenerateFilePath(folderPath, applicationAlbumEntry, currentDateTime, hash);
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);
} }
// 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; return ResultCode.Success;
} }

View File

@@ -2,7 +2,6 @@
using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Account.Acc;
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService
{ {
@@ -29,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend.ServiceCreator.FriendService
public override string ToString() 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; 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(); uint deviceHandle = (uint)context.RequestData.ReadUInt64();
@@ -665,9 +665,9 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
ulong outputPosition = context.Request.RecvListBuff[0].Position; 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(); uint deviceHandle = (uint)context.RequestData.ReadUInt64();
@@ -728,9 +728,9 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
ulong outputPosition = context.Request.RecvListBuff[0].Position; 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(); uint deviceHandle = (uint)context.RequestData.ReadUInt64();
@@ -788,9 +788,9 @@ namespace Ryujinx.HLE.HOS.Services.Nfc.Nfp
ulong outputPosition = context.Request.RecvListBuff[0].Position; 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(); 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.NvHostChannel;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl; using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu; 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.NvDrvServices.NvMap;
using Ryujinx.HLE.HOS.Services.Nv.Types; using Ryujinx.HLE.HOS.Services.Nv.Types;
using Ryujinx.Memory; using Ryujinx.Memory;
@@ -23,7 +25,13 @@ namespace Ryujinx.HLE.HOS.Services.Nv
[Service("nvdrv:t")] [Service("nvdrv:t")]
class INvDrvServices : IpcService 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/nvmap", typeof(NvMapDeviceFile) },
{ "/dev/nvhost-ctrl", typeof(NvHostCtrlDeviceFile) }, { "/dev/nvhost-ctrl", typeof(NvHostCtrlDeviceFile) },
@@ -35,6 +43,8 @@ namespace Ryujinx.HLE.HOS.Services.Nv
//{ "/dev/nvhost-nvjpg", typeof(NvHostChannelDeviceFile) }, //{ "/dev/nvhost-nvjpg", typeof(NvHostChannelDeviceFile) },
{ "/dev/nvhost-vic", typeof(NvHostChannelDeviceFile) }, { "/dev/nvhost-vic", typeof(NvHostChannelDeviceFile) },
//{ "/dev/nvhost-display", 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(); public static IdDictionary DeviceFileIdRegistry = new IdDictionary();
@@ -44,13 +54,23 @@ namespace Ryujinx.HLE.HOS.Services.Nv
private bool _transferMemInitialized = false; private bool _transferMemInitialized = false;
// TODO: This should call set:sys::GetDebugModeFlag
private bool _debugModeEnabled = false;
public INvDrvServices(ServiceCtx context) : base(context.Device.System.NvDrvServer) public INvDrvServices(ServiceCtx context) : base(context.Device.System.NvDrvServer)
{ {
_owner = 0; _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)) if (_deviceFileRegistry.TryGetValue(path, out Type deviceFileClass))
{ {
ConstructorInfo constructor = deviceFileClass.GetConstructor(new Type[] { typeof(ServiceCtx), typeof(IVirtualMemoryManager), typeof(ulong) }); 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; deviceFile.Path = path;
return DeviceFileIdRegistry.Add(deviceFile); fd = DeviceFileIdRegistry.Add(deviceFile);
}
else return NvResult.Success;
{
Logger.Warning?.Print(LogClass.ServiceNv, $"Cannot find file device \"{path}\"!");
} }
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) 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); string path = MemoryHelper.ReadAsciiString(context.Memory, pathPtr, (long)pathSize);
fd = Open(context, path); errorCode = Open(context, path, out fd);
if (fd == -1)
{
errorCode = NvResult.FileOperationFailed;
}
} }
context.ResponseData.Write(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() { }
}
}

View File

@@ -24,9 +24,7 @@ namespace Ryujinx.HLE.HOS.Services.Pcv.Bpc
public static ResultCode GetExternalRtcValue(out ulong rtcValue) public static ResultCode GetExternalRtcValue(out ulong rtcValue)
{ {
// TODO: emulate MAX77620/MAX77812 RTC // TODO: emulate MAX77620/MAX77812 RTC
DateTime unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); rtcValue = (ulong)(DateTime.Now.ToUniversalTime() - DateTime.UnixEpoch).TotalSeconds;
rtcValue = (ulong)(DateTime.Now.ToUniversalTime() - unixEpoch).TotalSeconds;
return ResultCode.Success; return ResultCode.Success;
} }

View File

@@ -142,14 +142,9 @@ namespace Ryujinx.HLE.HOS.Services.Ro
_owner.CpuMemory.Read(nroAddress, nroData); _owner.CpuMemory.Read(nroAddress, nroData);
byte[] nroHash = null;
MemoryStream stream = new MemoryStream(nroData); MemoryStream stream = new MemoryStream(nroData);
using (SHA256 hasher = SHA256.Create()) byte[] nroHash = SHA256.HashData(stream);
{
nroHash = hasher.ComputeHash(stream);
}
if (!IsNroHashPresent(nroHash)) if (!IsNroHashPresent(nroHash))
{ {

View File

@@ -1,5 +1,8 @@
using System.Collections.Concurrent; using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{ {
@@ -41,6 +44,27 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
return null; return null;
} }
public List<IFileDescriptor> RetrieveFileDescriptorsFromMask(ReadOnlySpan<byte> mask)
{
List<IFileDescriptor> fds = new();
for (int i = 0; i < mask.Length; i++)
{
byte current = mask[i];
while (current != 0)
{
int bit = BitOperations.TrailingZeroCount(current);
current &= (byte)~(1 << bit);
int fd = i * 8 + bit;
fds.Add(RetrieveFileDescriptor(fd));
}
}
return fds;
}
public int RegisterFileDescriptor(IFileDescriptor file) public int RegisterFileDescriptor(IFileDescriptor file)
{ {
lock (_lock) lock (_lock)
@@ -61,6 +85,16 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
} }
} }
public void BuildMask(List<IFileDescriptor> fds, Span<byte> mask)
{
foreach (IFileDescriptor descriptor in fds)
{
int fd = _fds.IndexOf(descriptor);
mask[fd >> 3] |= (byte)(1 << (fd & 7));
}
}
public int DuplicateFileDescriptor(int fd) public int DuplicateFileDescriptor(int fd)
{ {
IFileDescriptor oldFile = RetrieveFileDescriptor(fd); IFileDescriptor oldFile = RetrieveFileDescriptor(fd);
@@ -147,4 +181,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
return processContext; return processContext;
} }
} }
} }

View File

@@ -1,10 +1,13 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl;
using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types;
using Ryujinx.Memory; using Ryujinx.Memory;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Text; using System.Text;
@@ -202,12 +205,122 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
} }
[CommandHipc(5)] [CommandHipc(5)]
// Select(u32 nfds, nn::socket::timeout timeout, buffer<nn::socket::fd_set, 0x21, 0> readfds_in, buffer<nn::socket::fd_set, 0x21, 0> writefds_in, buffer<nn::socket::fd_set, 0x21, 0> errorfds_in) -> (i32 ret, u32 bsd_errno, buffer<nn::socket::fd_set, 0x22, 0> readfds_out, buffer<nn::socket::fd_set, 0x22, 0> writefds_out, buffer<nn::socket::fd_set, 0x22, 0> errorfds_out) // Select(u32 nfds, nn::socket::timeval timeout, buffer<nn::socket::fd_set, 0x21, 0> readfds_in, buffer<nn::socket::fd_set, 0x21, 0> writefds_in, buffer<nn::socket::fd_set, 0x21, 0> errorfds_in)
// -> (i32 ret, u32 bsd_errno, buffer<nn::socket::fd_set, 0x22, 0> readfds_out, buffer<nn::socket::fd_set, 0x22, 0> writefds_out, buffer<nn::socket::fd_set, 0x22, 0> errorfds_out)
public ResultCode Select(ServiceCtx context) public ResultCode Select(ServiceCtx context)
{ {
WriteBsdResult(context, -1, LinuxError.EOPNOTSUPP); int fdsCount = context.RequestData.ReadInt32();
int timeout = context.RequestData.ReadInt32();
Logger.Stub?.PrintStub(LogClass.ServiceBsd); (ulong readFdsInBufferPosition, ulong readFdsInBufferSize) = context.Request.GetBufferType0x21(0);
(ulong writeFdsInBufferPosition, ulong writeFdsInBufferSize) = context.Request.GetBufferType0x21(1);
(ulong errorFdsInBufferPosition, ulong errorFdsInBufferSize) = context.Request.GetBufferType0x21(2);
(ulong readFdsOutBufferPosition, ulong readFdsOutBufferSize) = context.Request.GetBufferType0x22(0);
(ulong writeFdsOutBufferPosition, ulong writeFdsOutBufferSize) = context.Request.GetBufferType0x22(1);
(ulong errorFdsOutBufferPosition, ulong errorFdsOutBufferSize) = context.Request.GetBufferType0x22(2);
List<IFileDescriptor> readFds = _context.RetrieveFileDescriptorsFromMask(context.Memory.GetSpan(readFdsInBufferPosition, (int)readFdsInBufferSize));
List<IFileDescriptor> writeFds = _context.RetrieveFileDescriptorsFromMask(context.Memory.GetSpan(writeFdsInBufferPosition, (int)writeFdsInBufferSize));
List<IFileDescriptor> errorFds = _context.RetrieveFileDescriptorsFromMask(context.Memory.GetSpan(errorFdsInBufferPosition, (int)errorFdsInBufferSize));
int actualFdsCount = readFds.Count + writeFds.Count + errorFds.Count;
if (fdsCount == 0 || actualFdsCount == 0)
{
WriteBsdResult(context, 0);
return ResultCode.Success;
}
PollEvent[] events = new PollEvent[actualFdsCount];
int index = 0;
foreach (IFileDescriptor fd in readFds)
{
events[index] = new PollEvent(new PollEventData { InputEvents = PollEventTypeMask.Input }, fd);
index++;
}
foreach (IFileDescriptor fd in writeFds)
{
events[index] = new PollEvent(new PollEventData { InputEvents = PollEventTypeMask.Output }, fd);
index++;
}
foreach (IFileDescriptor fd in errorFds)
{
events[index] = new PollEvent(new PollEventData { InputEvents = PollEventTypeMask.Error }, fd);
index++;
}
List<PollEvent>[] eventsByPollManager = new List<PollEvent>[_pollManagers.Count];
for (int i = 0; i < eventsByPollManager.Length; i++)
{
eventsByPollManager[i] = new List<PollEvent>();
foreach (PollEvent evnt in events)
{
if (_pollManagers[i].IsCompatible(evnt))
{
eventsByPollManager[i].Add(evnt);
}
}
}
int updatedCount = 0;
for (int i = 0; i < _pollManagers.Count; i++)
{
if (eventsByPollManager[i].Count > 0)
{
_pollManagers[i].Select(eventsByPollManager[i], timeout, out int updatedPollCount);
updatedCount += updatedPollCount;
}
}
readFds.Clear();
writeFds.Clear();
errorFds.Clear();
foreach (PollEvent pollEvent in events)
{
for (int i = 0; i < _pollManagers.Count; i++)
{
if (eventsByPollManager[i].Contains(pollEvent))
{
if (pollEvent.Data.OutputEvents.HasFlag(PollEventTypeMask.Input))
{
readFds.Add(pollEvent.FileDescriptor);
}
if (pollEvent.Data.OutputEvents.HasFlag(PollEventTypeMask.Output))
{
writeFds.Add(pollEvent.FileDescriptor);
}
if (pollEvent.Data.OutputEvents.HasFlag(PollEventTypeMask.Error))
{
errorFds.Add(pollEvent.FileDescriptor);
}
}
}
}
using var readFdsOut = context.Memory.GetWritableRegion(readFdsOutBufferPosition, (int)readFdsOutBufferSize);
using var writeFdsOut = context.Memory.GetWritableRegion(writeFdsOutBufferPosition, (int)writeFdsOutBufferSize);
using var errorFdsOut = context.Memory.GetWritableRegion(errorFdsOutBufferPosition, (int)errorFdsOutBufferSize);
_context.BuildMask(readFds, readFdsOut.Memory.Span);
_context.BuildMask(writeFds, writeFdsOut.Memory.Span);
_context.BuildMask(errorFds, errorFdsOut.Memory.Span);
WriteBsdResult(context, updatedCount);
return ResultCode.Success; return ResultCode.Success;
} }
@@ -320,14 +433,14 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
break; break;
} }
// If we are here, that mean nothing was availaible, sleep for 50ms // If we are here, that mean nothing was available, sleep for 50ms
context.Device.System.KernelContext.Syscall.SleepThread(50 * 1000000); context.Device.System.KernelContext.Syscall.SleepThread(50 * 1000000);
} }
while (PerformanceCounter.ElapsedMilliseconds < budgetLeftMilliseconds); while (PerformanceCounter.ElapsedMilliseconds < budgetLeftMilliseconds);
} }
else if (timeout == -1) else if (timeout == -1)
{ {
// FIXME: If we get a timeout of -1 and there is no fds to wait on, this should kill the KProces. (need to check that with re) // FIXME: If we get a timeout of -1 and there is no fds to wait on, this should kill the KProcess. (need to check that with re)
throw new InvalidOperationException(); throw new InvalidOperationException();
} }
else else
@@ -998,4 +1111,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
return WriteBsdResult(context, newSockFd, errno); return WriteBsdResult(context, newSockFd, errno);
} }
} }
} }

View File

@@ -1,4 +1,5 @@
using System; using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types;
using System;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{ {
@@ -11,4 +12,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
LinuxError Write(out int writeSize, ReadOnlySpan<byte> buffer); LinuxError Write(out int writeSize, ReadOnlySpan<byte> buffer);
} }
} }

View File

@@ -1,4 +1,5 @@
using System; using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types;
using System;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;

View File

@@ -1,8 +1,9 @@
using System; using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types;
using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
{ {
class EventFileDescriptor : IFileDescriptor class EventFileDescriptor : IFileDescriptor
{ {
@@ -149,4 +150,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
} }
} }
} }
} }

View File

@@ -1,8 +1,9 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
{ {
class EventFileDescriptorPollManager : IPollManager class EventFileDescriptorPollManager : IPollManager
{ {
@@ -109,5 +110,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
return LinuxError.SUCCESS; return LinuxError.SUCCESS;
} }
public LinuxError Select(List<PollEvent> events, int timeout, out int updatedCount)
{
// TODO: Implement Select for event file descriptors
updatedCount = 0;
return LinuxError.EOPNOTSUPP;
}
} }
} }

View File

@@ -1,4 +1,5 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@@ -6,7 +7,7 @@ using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
{ {
class ManagedSocket : ISocket class ManagedSocket : ISocket
{ {

View File

@@ -1,8 +1,9 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net.Sockets; using System.Net.Sockets;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
{ {
class ManagedSocketPollManager : IPollManager class ManagedSocketPollManager : IPollManager
{ {
@@ -117,5 +118,60 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
return LinuxError.SUCCESS; return LinuxError.SUCCESS;
} }
public LinuxError Select(List<PollEvent> events, int timeout, out int updatedCount)
{
List<Socket> readEvents = new();
List<Socket> writeEvents = new();
List<Socket> errorEvents = new();
updatedCount = 0;
foreach (PollEvent pollEvent in events)
{
ManagedSocket socket = (ManagedSocket)pollEvent.FileDescriptor;
if (pollEvent.Data.InputEvents.HasFlag(PollEventTypeMask.Input))
{
readEvents.Add(socket.Socket);
}
if (pollEvent.Data.InputEvents.HasFlag(PollEventTypeMask.Output))
{
writeEvents.Add(socket.Socket);
}
if (pollEvent.Data.InputEvents.HasFlag(PollEventTypeMask.Error))
{
errorEvents.Add(socket.Socket);
}
}
Socket.Select(readEvents, writeEvents, errorEvents, timeout);
updatedCount = readEvents.Count + writeEvents.Count + errorEvents.Count;
foreach (PollEvent pollEvent in events)
{
ManagedSocket socket = (ManagedSocket)pollEvent.FileDescriptor;
if (readEvents.Contains(socket.Socket))
{
pollEvent.Data.OutputEvents |= PollEventTypeMask.Input;
}
if (writeEvents.Contains(socket.Socket))
{
pollEvent.Data.OutputEvents |= PollEventTypeMask.Output;
}
if (errorEvents.Contains(socket.Socket))
{
pollEvent.Data.OutputEvents |= PollEventTypeMask.Error;
}
}
return LinuxError.SUCCESS;
}
} }
} }

View File

@@ -1,6 +1,6 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
{ {
[SuppressMessage("ReSharper", "InconsistentNaming")] [SuppressMessage("ReSharper", "InconsistentNaming")]
enum WsaError enum WsaError

View File

@@ -1,7 +1,8 @@
using System.Collections.Generic; using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types;
using System.Collections.Generic;
using System.Net.Sockets; using System.Net.Sockets;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
{ {
static class WinSockHelper static class WinSockHelper
{ {
@@ -162,4 +163,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
return table.TryGetValue(option, out name); return table.TryGetValue(option, out name);
} }
} }
} }

View File

@@ -1,4 +1,4 @@
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types
{ {
enum BsdAddressFamily : uint enum BsdAddressFamily : uint
{ {
@@ -8,4 +8,4 @@
Unknown = uint.MaxValue Unknown = uint.MaxValue
} }
} }

View File

@@ -1,4 +1,4 @@
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types
{ {
enum BsdIoctl enum BsdIoctl
{ {

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types
{ {
class BsdMMsgHdr class BsdMMsgHdr
{ {

View File

@@ -1,7 +1,7 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types
{ {
class BsdMsgHdr class BsdMsgHdr
{ {

View File

@@ -3,7 +3,7 @@ using System;
using System.Net; using System.Net;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types
{ {
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x10)] [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x10)]
struct BsdSockAddr struct BsdSockAddr

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types
{ {
[Flags] [Flags]
enum BsdSocketCreationFlags enum BsdSocketCreationFlags

View File

@@ -1,4 +1,4 @@
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types
{ {
enum BsdSocketFlags enum BsdSocketFlags
{ {

View File

@@ -1,4 +1,4 @@
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types
{ {
enum BsdSocketOption enum BsdSocketOption
{ {

View File

@@ -1,4 +1,4 @@
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types
{ {
enum BsdSocketShutdownFlags enum BsdSocketShutdownFlags
{ {

View File

@@ -1,4 +1,4 @@
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types
{ {
enum BsdSocketType enum BsdSocketType
{ {

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types
{ {
[Flags] [Flags]
enum EventFdFlags : uint enum EventFdFlags : uint

View File

@@ -1,11 +1,13 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types
{ {
interface IPollManager interface IPollManager
{ {
bool IsCompatible(PollEvent evnt); bool IsCompatible(PollEvent evnt);
LinuxError Poll(List<PollEvent> events, int timeoutMilliseconds, out int updatedCount); LinuxError Poll(List<PollEvent> events, int timeoutMilliseconds, out int updatedCount);
LinuxError Select(List<PollEvent> events, int timeout, out int updatedCount);
} }
} }

View File

@@ -1,6 +1,6 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types
{ {
[SuppressMessage("ReSharper", "InconsistentNaming")] [SuppressMessage("ReSharper", "InconsistentNaming")]
enum LinuxError enum LinuxError

View File

@@ -1,4 +1,4 @@
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types
{ {
class PollEvent class PollEvent
{ {

View File

@@ -1,4 +1,4 @@
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types
{ {
struct PollEventData struct PollEventData
{ {
@@ -8,4 +8,4 @@
#pragma warning restore CS0649 #pragma warning restore CS0649
public PollEventTypeMask OutputEvents; public PollEventTypeMask OutputEvents;
} }
} }

View File

@@ -1,6 +1,6 @@
using System; using System;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types
{ {
[Flags] [Flags]
enum PollEventTypeMask : ushort enum PollEventTypeMask : ushort

View File

@@ -1,4 +1,4 @@
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types
{ {
public struct TimeVal public struct TimeVal
{ {

View File

@@ -18,7 +18,10 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
[Service("sfdnsres")] [Service("sfdnsres")]
class IResolver : IpcService class IResolver : IpcService
{ {
public IResolver(ServiceCtx context) { } public IResolver(ServiceCtx context)
{
DnsMitmResolver.Instance.ReloadEntries(context);
}
[CommandHipc(0)] [CommandHipc(0)]
// SetDnsAddressesPrivateRequest(u32, buffer<unknown, 5, 0>) // SetDnsAddressesPrivateRequest(u32, buffer<unknown, 5, 0>)
@@ -259,6 +262,16 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
return ResultCode.Success; return ResultCode.Success;
} }
// Atmosphère extension for dns_mitm
[CommandHipc(65000)]
// AtmosphereReloadHostsFile()
public ResultCode AtmosphereReloadHostsFile(ServiceCtx context)
{
DnsMitmResolver.Instance.ReloadEntries(context);
return ResultCode.Success;
}
private static ResultCode GetHostByNameRequestImpl( private static ResultCode GetHostByNameRequestImpl(
ServiceCtx context, ServiceCtx context,
ulong inputBufferPosition, ulong inputBufferPosition,
@@ -321,7 +334,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
try try
{ {
hostEntry = Dns.GetHostEntry(targetHost); hostEntry = DnsMitmResolver.Instance.ResolveAddress(targetHost);
} }
catch (SocketException exception) catch (SocketException exception)
{ {
@@ -537,7 +550,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres
try try
{ {
hostEntry = Dns.GetHostEntry(targetHost); hostEntry = DnsMitmResolver.Instance.ResolveAddress(targetHost);
} }
catch (SocketException exception) catch (SocketException exception)
{ {

View File

@@ -0,0 +1,106 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Services.Sockets.Nsd;
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Enumeration;
using System.Net;
namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Proxy
{
class DnsMitmResolver
{
private const string HostsFilePath = "/atmosphere/hosts/default.txt";
private static DnsMitmResolver _instance;
public static DnsMitmResolver Instance => _instance ??= new DnsMitmResolver();
private readonly Dictionary<string, IPAddress> _mitmHostEntries = new();
public void ReloadEntries(ServiceCtx context)
{
string sdPath = context.Device.Configuration.VirtualFileSystem.GetSdCardPath();
string filePath = context.Device.Configuration.VirtualFileSystem.GetFullPath(sdPath, HostsFilePath);
_mitmHostEntries.Clear();
if (File.Exists(filePath))
{
using FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.Read);
using StreamReader reader = new(fileStream);
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
if (line == null)
{
break;
}
// Ignore comments and empty lines
if (line.StartsWith("#") || line.Trim().Length == 0)
{
continue;
}
string[] entry = line.Split(new[] { ' ', '\t' }, StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
// Hosts file example entry:
// 127.0.0.1 localhost loopback
// 0. Check the size of the array
if (entry.Length < 2)
{
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, $"Invalid entry in hosts file: {line}");
continue;
}
// 1. Parse the address
if (!IPAddress.TryParse(entry[0], out IPAddress address))
{
Logger.Warning?.PrintMsg(LogClass.ServiceBsd, $"Failed to parse IP address in hosts file: {entry[0]}");
continue;
}
// 2. Check for AMS hosts file extension: "%"
for (int i = 1; i < entry.Length; i++)
{
entry[i] = entry[i].Replace("%", IManager.NsdSettings.Environment);
}
// 3. Add hostname to entry dictionary (updating duplicate entries)
foreach (string hostname in entry[1..])
{
_mitmHostEntries[hostname] = address;
}
}
}
}
public IPHostEntry ResolveAddress(string host)
{
foreach (var hostEntry in _mitmHostEntries)
{
// Check for AMS hosts file extension: "*"
// NOTE: MatchesSimpleExpression also allows "?" as a wildcard
if (FileSystemName.MatchesSimpleExpression(hostEntry.Key, host))
{
Logger.Info?.PrintMsg(LogClass.ServiceBsd, $"Redirecting '{host}' to: {hostEntry.Value}");
return new IPHostEntry
{
AddressList = new[] { hostEntry.Value },
HostName = hostEntry.Key,
Aliases = Array.Empty<string>()
};
}
}
// No match has been found, resolve the host using regular dns
return Dns.GetHostEntry(host);
}
}
}

View File

@@ -1,4 +1,5 @@
using Ryujinx.HLE.HOS.Services.Sockets.Bsd; using Ryujinx.HLE.HOS.Services.Sockets.Bsd;
using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl;
using Ryujinx.HLE.HOS.Services.Ssl.Types; using Ryujinx.HLE.HOS.Services.Ssl.Types;
using System; using System;
using System.IO; using System.IO;

View File

@@ -10,8 +10,6 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
public static readonly TimeSpanType Zero = new TimeSpanType(0); public static readonly TimeSpanType Zero = new TimeSpanType(0);
private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
public long NanoSeconds; public long NanoSeconds;
public TimeSpanType(long nanoSeconds) public TimeSpanType(long nanoSeconds)
@@ -31,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock
public bool IsDaylightSavingTime() public bool IsDaylightSavingTime()
{ {
return UnixEpoch.AddSeconds(ToSeconds()).ToLocalTime().IsDaylightSavingTime(); return DateTime.UnixEpoch.AddSeconds(ToSeconds()).ToLocalTime().IsDaylightSavingTime();
} }
public static TimeSpanType FromSeconds(long seconds) public static TimeSpanType FromSeconds(long seconds)

View File

@@ -57,9 +57,9 @@ namespace Ryujinx.HLE.HOS.SystemState
DesiredTitleLanguage = language switch DesiredTitleLanguage = language switch
{ {
SystemLanguage.Taiwanese or SystemLanguage.Taiwanese or
SystemLanguage.TraditionalChinese => TitleLanguage.Taiwanese, SystemLanguage.TraditionalChinese => TitleLanguage.TraditionalChinese,
SystemLanguage.Chinese or SystemLanguage.Chinese or
SystemLanguage.SimplifiedChinese => TitleLanguage.Chinese, SystemLanguage.SimplifiedChinese => TitleLanguage.SimplifiedChinese,
_ => Enum.Parse<TitleLanguage>(Enum.GetName<SystemLanguage>(language)), _ => Enum.Parse<TitleLanguage>(Enum.GetName<SystemLanguage>(language)),
}; };
} }

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