Compare commits

..

9 Commits

Author SHA1 Message Date
gdkchan
dca5b14493 Relax Vulkan requirements (#4228) 2023-01-13 06:09:48 +01:00
Ac_K
4d2c8e2a44 Prepo: Fix SaveSystemReport* IPC definitions (#4278)
* Prepo: Fix SaveSystemReport IPC definitions

* Follow original code

* Fix args index in HipcGenerator

* Addresses feedback

* oops
2023-01-13 01:50:14 +01:00
riperiperi
8fa248ceb4 Vulkan: Add workarounds for MoltenVK (#4202)
* Add MVK basics.

* Use appropriate output attribute types

* 4kb vertex alignment, bunch of fixes

* Add reduced shader precision mode for mvk.

* Disable ASTC on MVK for now

* Only request robustnes2 when it is available.

* It's just the one feature actually

* Add triangle fan conversion

* Allow NullDescriptor on MVK for some reason.

* Force safe blit on MoltenVK

* Use ASTC only when formats are all available.

* Disable multilevel 3d texture views

* Filter duplicate render targets (on backend)

* Add Automatic MoltenVK Configuration

* Do not create color attachment views with formats that are not RT compatible

* Make sure that the host format matches the vertex shader input types for invalid/unknown guest formats

* FIx rebase for Vertex Attrib State

* Fix 4b alignment for vertex

* Use asynchronous queue submits for MVK

* Ensure color clear shader has correct output type

* Update MoltenVK config

* Always use MoltenVK workarounds on MacOS

* Make MVK supersede all vendors

* Fix rebase

* Various fixes on rebase

* Get portability flags from extension

* Fix some minor rebasing issues

* Style change

* Use LibraryImport for MVKConfiguration

* Rename MoltenVK vendor to Apple

Intel and AMD GPUs on moltenvk report with the those vendors - only apple silicon reports with vendor 0x106B.

* Fix features2 rebase conflict

* Rename fragment output type

* Add missing check for fragment output types

Might have caused the crash in MK8

* Only do fragment output specialization on MoltenVK

* Avoid copy when passing capabilities

* Self feedback

* Address feedback

Co-authored-by: gdk <gab.dark.100@gmail.com>
Co-authored-by: nastys <nastys@users.noreply.github.com>
2023-01-13 01:31:21 +01:00
Ac_K
30862b5ffd ava: Reorder settings of Resolution Scaler (#4270) 2023-01-13 00:07:53 +01:00
Isaac Marovitz
9f57747c57 Ava UI: Various Fixes (#4268)
* Fix saves disappearing

* Better size formatter

* Move TextBox alignment fix to Styles

* Fix bug

* Left align

* Add border

* Update Ryujinx.Ava/UI/Models/SaveModel.cs

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

* Update Ryujinx.Ava/UI/Models/SaveModel.cs

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

* Update Ryujinx.Ava/UI/Models/SaveModel.cs

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

* Whitespace

Co-authored-by: Ac_K <Acoustik666@gmail.com>
2023-01-12 12:23:24 +00:00
Isaac Marovitz
fe29a2ff6e Ava UI: Settings Adjustments (#4273)
* Visual adjustments

* Match border to rest of app

* Fix overlapping controls

* Fix

* Fix
2023-01-12 12:09:32 +00:00
merry
e9a173e00c Ptc: Check process architecture (#4272) 2023-01-12 07:50:45 +00:00
merry
a11784fcbf Arm64: Cpu feature detection (#4264)
* Arm64: Cpu feature detection

* Ptc: Add Arm64 feature info

* nits

* simplify CheckSysctlName

* restore some macos flags

* feedback
2023-01-12 08:05:18 +01:00
Ac_K
fd36c8deca lm: Handle Tail flag in LogPacket (#4274)
* lm: Handle TailFlag in LogPacket

* Addresses feedback
2023-01-12 07:42:05 +01:00
65 changed files with 1530 additions and 320 deletions

View File

@@ -0,0 +1,185 @@
using System;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics.Arm;
using System.Runtime.Versioning;
namespace ARMeilleure.CodeGen.Arm64
{
static partial class HardwareCapabilities
{
static HardwareCapabilities()
{
if (!ArmBase.Arm64.IsSupported)
{
return;
}
if (OperatingSystem.IsLinux())
{
LinuxFeatureInfoHwCap = (LinuxFeatureFlagsHwCap)getauxval(AT_HWCAP);
LinuxFeatureInfoHwCap2 = (LinuxFeatureFlagsHwCap2)getauxval(AT_HWCAP2);
}
if (OperatingSystem.IsMacOS())
{
for (int i = 0; i < _sysctlNames.Length; i++)
{
if (CheckSysctlName(_sysctlNames[i]))
{
MacOsFeatureInfo |= (MacOsFeatureFlags)(1 << i);
}
}
}
}
#region Linux
private const ulong AT_HWCAP = 16;
private const ulong AT_HWCAP2 = 26;
[LibraryImport("libc", SetLastError = true)]
private static partial ulong getauxval(ulong type);
[Flags]
public enum LinuxFeatureFlagsHwCap : ulong
{
Fp = 1 << 0,
Asimd = 1 << 1,
Evtstrm = 1 << 2,
Aes = 1 << 3,
Pmull = 1 << 4,
Sha1 = 1 << 5,
Sha2 = 1 << 6,
Crc32 = 1 << 7,
Atomics = 1 << 8,
FpHp = 1 << 9,
AsimdHp = 1 << 10,
CpuId = 1 << 11,
AsimdRdm = 1 << 12,
Jscvt = 1 << 13,
Fcma = 1 << 14,
Lrcpc = 1 << 15,
DcpOp = 1 << 16,
Sha3 = 1 << 17,
Sm3 = 1 << 18,
Sm4 = 1 << 19,
AsimdDp = 1 << 20,
Sha512 = 1 << 21,
Sve = 1 << 22,
AsimdFhm = 1 << 23,
Dit = 1 << 24,
Uscat = 1 << 25,
Ilrcpc = 1 << 26,
FlagM = 1 << 27,
Ssbs = 1 << 28,
Sb = 1 << 29,
Paca = 1 << 30,
Pacg = 1UL << 31
}
[Flags]
public enum LinuxFeatureFlagsHwCap2 : ulong
{
Dcpodp = 1 << 0,
Sve2 = 1 << 1,
SveAes = 1 << 2,
SvePmull = 1 << 3,
SveBitperm = 1 << 4,
SveSha3 = 1 << 5,
SveSm4 = 1 << 6,
FlagM2 = 1 << 7,
Frint = 1 << 8,
SveI8mm = 1 << 9,
SveF32mm = 1 << 10,
SveF64mm = 1 << 11,
SveBf16 = 1 << 12,
I8mm = 1 << 13,
Bf16 = 1 << 14,
Dgh = 1 << 15,
Rng = 1 << 16,
Bti = 1 << 17,
Mte = 1 << 18,
Ecv = 1 << 19,
Afp = 1 << 20,
Rpres = 1 << 21,
Mte3 = 1 << 22,
Sme = 1 << 23,
Sme_i16i64 = 1 << 24,
Sme_f64f64 = 1 << 25,
Sme_i8i32 = 1 << 26,
Sme_f16f32 = 1 << 27,
Sme_b16f32 = 1 << 28,
Sme_f32f32 = 1 << 29,
Sme_fa64 = 1 << 30,
Wfxt = 1UL << 31,
Ebf16 = 1UL << 32,
Sve_Ebf16 = 1UL << 33,
Cssc = 1UL << 34,
Rprfm = 1UL << 35,
Sve2p1 = 1UL << 36
}
public static LinuxFeatureFlagsHwCap LinuxFeatureInfoHwCap { get; } = 0;
public static LinuxFeatureFlagsHwCap2 LinuxFeatureInfoHwCap2 { get; } = 0;
#endregion
#region macOS
[LibraryImport("libSystem.dylib", SetLastError = true)]
private static unsafe partial int sysctlbyname([MarshalAs(UnmanagedType.LPStr)] string name, out int oldValue, ref ulong oldSize, IntPtr newValue, ulong newValueSize);
[SupportedOSPlatform("macos")]
private static bool CheckSysctlName(string name)
{
ulong size = sizeof(int);
if (sysctlbyname(name, out int val, ref size, IntPtr.Zero, 0) == 0 && size == sizeof(int))
{
return val != 0;
}
return false;
}
private static string[] _sysctlNames = new string[]
{
"hw.optional.floatingpoint",
"hw.optional.AdvSIMD",
"hw.optional.arm.FEAT_FP16",
"hw.optional.arm.FEAT_AES",
"hw.optional.arm.FEAT_PMULL",
"hw.optional.arm.FEAT_LSE",
"hw.optional.armv8_crc32",
"hw.optional.arm.FEAT_SHA1",
"hw.optional.arm.FEAT_SHA256"
};
[Flags]
public enum MacOsFeatureFlags
{
Fp = 1 << 0,
AdvSimd = 1 << 1,
Fp16 = 1 << 2,
Aes = 1 << 3,
Pmull = 1 << 4,
Lse = 1 << 5,
Crc32 = 1 << 6,
Sha1 = 1 << 7,
Sha256 = 1 << 8
}
public static MacOsFeatureFlags MacOsFeatureInfo { get; } = 0;
#endregion
public static bool SupportsAdvSimd => LinuxFeatureInfoHwCap.HasFlag(LinuxFeatureFlagsHwCap.Asimd) || MacOsFeatureInfo.HasFlag(MacOsFeatureFlags.AdvSimd);
public static bool SupportsAes => LinuxFeatureInfoHwCap.HasFlag(LinuxFeatureFlagsHwCap.Aes) || MacOsFeatureInfo.HasFlag(MacOsFeatureFlags.Aes);
public static bool SupportsPmull => LinuxFeatureInfoHwCap.HasFlag(LinuxFeatureFlagsHwCap.Pmull) || MacOsFeatureInfo.HasFlag(MacOsFeatureFlags.Pmull);
public static bool SupportsLse => LinuxFeatureInfoHwCap.HasFlag(LinuxFeatureFlagsHwCap.Atomics) || MacOsFeatureInfo.HasFlag(MacOsFeatureFlags.Lse);
public static bool SupportsCrc32 => LinuxFeatureInfoHwCap.HasFlag(LinuxFeatureFlagsHwCap.Crc32) || MacOsFeatureInfo.HasFlag(MacOsFeatureFlags.Crc32);
public static bool SupportsSha1 => LinuxFeatureInfoHwCap.HasFlag(LinuxFeatureFlagsHwCap.Sha1) || MacOsFeatureInfo.HasFlag(MacOsFeatureFlags.Sha1);
public static bool SupportsSha256 => LinuxFeatureInfoHwCap.HasFlag(LinuxFeatureFlagsHwCap.Sha2) || MacOsFeatureInfo.HasFlag(MacOsFeatureFlags.Sha256);
}
}

View File

@@ -2556,7 +2556,7 @@ namespace ARMeilleure.Instructions
{ {
OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp; OpCodeSimdReg op = (OpCodeSimdReg)context.CurrOp;
if (Optimizations.UseAdvSimd && false) // Not supported by all Arm CPUs. if (Optimizations.UseArm64Pmull)
{ {
InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64PmullV); InstEmitSimdHelperArm64.EmitVectorBinaryOp(context, Intrinsic.Arm64PmullV);
} }

View File

@@ -1,8 +1,10 @@
using ARMeilleure.CodeGen.X86;
using System.Runtime.Intrinsics.Arm; using System.Runtime.Intrinsics.Arm;
namespace ARMeilleure namespace ARMeilleure
{ {
using Arm64HardwareCapabilities = ARMeilleure.CodeGen.Arm64.HardwareCapabilities;
using X86HardwareCapabilities = ARMeilleure.CodeGen.X86.HardwareCapabilities;
public static class Optimizations public static class Optimizations
{ {
public static bool FastFP { get; set; } = true; public static bool FastFP { get; set; } = true;
@@ -10,7 +12,8 @@ namespace ARMeilleure
public static bool AllowLcqInFunctionTable { get; set; } = true; public static bool AllowLcqInFunctionTable { get; set; } = true;
public static bool UseUnmanagedDispatchLoop { get; set; } = true; public static bool UseUnmanagedDispatchLoop { get; set; } = true;
public static bool UseAdvSimdIfAvailable { get; set; } = true; public static bool UseAdvSimdIfAvailable { get; set; } = true;
public static bool UseArm64PmullIfAvailable { get; set; } = true;
public static bool UseSseIfAvailable { get; set; } = true; public static bool UseSseIfAvailable { get; set; } = true;
public static bool UseSse2IfAvailable { get; set; } = true; public static bool UseSse2IfAvailable { get; set; } = true;
@@ -29,25 +32,26 @@ namespace ARMeilleure
public static bool ForceLegacySse public static bool ForceLegacySse
{ {
get => HardwareCapabilities.ForceLegacySse; get => X86HardwareCapabilities.ForceLegacySse;
set => HardwareCapabilities.ForceLegacySse = value; set => X86HardwareCapabilities.ForceLegacySse = value;
} }
internal static bool UseAdvSimd => UseAdvSimdIfAvailable && AdvSimd.IsSupported; internal static bool UseAdvSimd => UseAdvSimdIfAvailable && Arm64HardwareCapabilities.SupportsAdvSimd;
internal static bool UseArm64Pmull => UseArm64PmullIfAvailable && Arm64HardwareCapabilities.SupportsPmull;
internal static bool UseSse => UseSseIfAvailable && HardwareCapabilities.SupportsSse; internal static bool UseSse => UseSseIfAvailable && X86HardwareCapabilities.SupportsSse;
internal static bool UseSse2 => UseSse2IfAvailable && HardwareCapabilities.SupportsSse2; internal static bool UseSse2 => UseSse2IfAvailable && X86HardwareCapabilities.SupportsSse2;
internal static bool UseSse3 => UseSse3IfAvailable && HardwareCapabilities.SupportsSse3; internal static bool UseSse3 => UseSse3IfAvailable && X86HardwareCapabilities.SupportsSse3;
internal static bool UseSsse3 => UseSsse3IfAvailable && HardwareCapabilities.SupportsSsse3; internal static bool UseSsse3 => UseSsse3IfAvailable && X86HardwareCapabilities.SupportsSsse3;
internal static bool UseSse41 => UseSse41IfAvailable && HardwareCapabilities.SupportsSse41; internal static bool UseSse41 => UseSse41IfAvailable && X86HardwareCapabilities.SupportsSse41;
internal static bool UseSse42 => UseSse42IfAvailable && HardwareCapabilities.SupportsSse42; internal static bool UseSse42 => UseSse42IfAvailable && X86HardwareCapabilities.SupportsSse42;
internal static bool UsePopCnt => UsePopCntIfAvailable && HardwareCapabilities.SupportsPopcnt; internal static bool UsePopCnt => UsePopCntIfAvailable && X86HardwareCapabilities.SupportsPopcnt;
internal static bool UseAvx => UseAvxIfAvailable && HardwareCapabilities.SupportsAvx && !ForceLegacySse; internal static bool UseAvx => UseAvxIfAvailable && X86HardwareCapabilities.SupportsAvx && !ForceLegacySse;
internal static bool UseF16c => UseF16cIfAvailable && HardwareCapabilities.SupportsF16c; internal static bool UseF16c => UseF16cIfAvailable && X86HardwareCapabilities.SupportsF16c;
internal static bool UseFma => UseFmaIfAvailable && HardwareCapabilities.SupportsFma; internal static bool UseFma => UseFmaIfAvailable && X86HardwareCapabilities.SupportsFma;
internal static bool UseAesni => UseAesniIfAvailable && HardwareCapabilities.SupportsAesni; internal static bool UseAesni => UseAesniIfAvailable && X86HardwareCapabilities.SupportsAesni;
internal static bool UsePclmulqdq => UsePclmulqdqIfAvailable && HardwareCapabilities.SupportsPclmulqdq; internal static bool UsePclmulqdq => UsePclmulqdqIfAvailable && X86HardwareCapabilities.SupportsPclmulqdq;
internal static bool UseSha => UseShaIfAvailable && HardwareCapabilities.SupportsSha; internal static bool UseSha => UseShaIfAvailable && X86HardwareCapabilities.SupportsSha;
internal static bool UseGfni => UseGfniIfAvailable && HardwareCapabilities.SupportsGfni; internal static bool UseGfni => UseGfniIfAvailable && X86HardwareCapabilities.SupportsGfni;
} }
} }

View File

@@ -1,7 +1,6 @@
using ARMeilleure.CodeGen; using ARMeilleure.CodeGen;
using ARMeilleure.CodeGen.Linking; using ARMeilleure.CodeGen.Linking;
using ARMeilleure.CodeGen.Unwinding; using ARMeilleure.CodeGen.Unwinding;
using ARMeilleure.CodeGen.X86;
using ARMeilleure.Common; using ARMeilleure.Common;
using ARMeilleure.Memory; using ARMeilleure.Memory;
using Ryujinx.Common; using Ryujinx.Common;
@@ -22,12 +21,15 @@ using static ARMeilleure.Translation.PTC.PtcFormatter;
namespace ARMeilleure.Translation.PTC namespace ARMeilleure.Translation.PTC
{ {
using Arm64HardwareCapabilities = ARMeilleure.CodeGen.Arm64.HardwareCapabilities;
using X86HardwareCapabilities = ARMeilleure.CodeGen.X86.HardwareCapabilities;
class Ptc : IPtcLoadState class Ptc : IPtcLoadState
{ {
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 = 4114; //! To be incremented manually for each change to the ARMeilleure project. private const uint InternalVersion = 4272; //! 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";
@@ -259,6 +261,13 @@ namespace ARMeilleure.Translation.PTC
return false; return false;
} }
if (outerHeader.Architecture != (uint)RuntimeInformation.ProcessArchitecture)
{
InvalidateCompressedStream(compressedStream);
return false;
}
IntPtr intPtr = IntPtr.Zero; IntPtr intPtr = IntPtr.Zero;
try try
@@ -435,6 +444,7 @@ namespace ARMeilleure.Translation.PTC
outerHeader.FeatureInfo = GetFeatureInfo(); outerHeader.FeatureInfo = GetFeatureInfo();
outerHeader.MemoryManagerMode = GetMemoryManagerMode(); outerHeader.MemoryManagerMode = GetMemoryManagerMode();
outerHeader.OSPlatform = GetOSPlatform(); outerHeader.OSPlatform = GetOSPlatform();
outerHeader.Architecture = (uint)RuntimeInformation.ProcessArchitecture;
outerHeader.UncompressedStreamSize = outerHeader.UncompressedStreamSize =
(long)Unsafe.SizeOf<InnerHeader>() + (long)Unsafe.SizeOf<InnerHeader>() +
@@ -952,11 +962,26 @@ namespace ARMeilleure.Translation.PTC
private static FeatureInfo GetFeatureInfo() private static FeatureInfo GetFeatureInfo()
{ {
return new FeatureInfo( if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
(uint)HardwareCapabilities.FeatureInfo1Ecx, {
(uint)HardwareCapabilities.FeatureInfo1Edx, return new FeatureInfo(
(uint)HardwareCapabilities.FeatureInfo7Ebx, (ulong)Arm64HardwareCapabilities.LinuxFeatureInfoHwCap,
(uint)HardwareCapabilities.FeatureInfo7Ecx); (ulong)Arm64HardwareCapabilities.LinuxFeatureInfoHwCap2,
(ulong)Arm64HardwareCapabilities.MacOsFeatureInfo,
0);
}
else if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
{
return new FeatureInfo(
(ulong)X86HardwareCapabilities.FeatureInfo1Ecx,
(ulong)X86HardwareCapabilities.FeatureInfo1Edx,
(ulong)X86HardwareCapabilities.FeatureInfo7Ebx,
(ulong)X86HardwareCapabilities.FeatureInfo7Ecx);
}
else
{
return new FeatureInfo(0, 0, 0, 0);
}
} }
private byte GetMemoryManagerMode() private byte GetMemoryManagerMode()
@@ -976,7 +1001,7 @@ namespace ARMeilleure.Translation.PTC
return osPlatform; return osPlatform;
} }
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 58*/)] [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 78*/)]
private struct OuterHeader private struct OuterHeader
{ {
public ulong Magic; public ulong Magic;
@@ -987,6 +1012,7 @@ namespace ARMeilleure.Translation.PTC
public FeatureInfo FeatureInfo; public FeatureInfo FeatureInfo;
public byte MemoryManagerMode; public byte MemoryManagerMode;
public uint OSPlatform; public uint OSPlatform;
public uint Architecture;
public long UncompressedStreamSize; public long UncompressedStreamSize;
@@ -1007,8 +1033,8 @@ namespace ARMeilleure.Translation.PTC
} }
} }
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 16*/)] [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 32*/)]
private record struct FeatureInfo(uint FeatureInfo0, uint FeatureInfo1, uint FeatureInfo2, uint FeatureInfo3); private record struct FeatureInfo(ulong FeatureInfo0, ulong FeatureInfo1, ulong FeatureInfo2, ulong FeatureInfo3);
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 128*/)] [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 128*/)]
private struct InnerHeader private struct InnerHeader

View File

@@ -119,7 +119,7 @@
"SettingsTabSystemAudioBackendSoundIO": "SoundIO", "SettingsTabSystemAudioBackendSoundIO": "SoundIO",
"SettingsTabSystemAudioBackendSDL2": "SDL2", "SettingsTabSystemAudioBackendSDL2": "SDL2",
"SettingsTabSystemHacks": "Hacks", "SettingsTabSystemHacks": "Hacks",
"SettingsTabSystemHacksNote": " (may cause instability)", "SettingsTabSystemHacksNote": "May cause instability",
"SettingsTabSystemExpandDramSize": "Use alternative memory layout (Developers)", "SettingsTabSystemExpandDramSize": "Use alternative memory layout (Developers)",
"SettingsTabSystemIgnoreMissingServices": "Ignore Missing Services", "SettingsTabSystemIgnoreMissingServices": "Ignore Missing Services",
"SettingsTabGraphics": "Graphics", "SettingsTabGraphics": "Graphics",
@@ -157,7 +157,8 @@
"SettingsTabLoggingEnableGuestLogs": "Enable Guest Logs", "SettingsTabLoggingEnableGuestLogs": "Enable Guest Logs",
"SettingsTabLoggingEnableFsAccessLogs": "Enable Fs Access Logs", "SettingsTabLoggingEnableFsAccessLogs": "Enable Fs Access Logs",
"SettingsTabLoggingFsGlobalAccessLogMode": "Fs Global Access Log Mode:", "SettingsTabLoggingFsGlobalAccessLogMode": "Fs Global Access Log Mode:",
"SettingsTabLoggingDeveloperOptions": "Developer Options (WARNING: Will reduce performance)", "SettingsTabLoggingDeveloperOptions": "Developer Options",
"SettingsTabLoggingDeveloperOptionsNote": "WARNING: Will reduce performance",
"SettingsTabLoggingGraphicsBackendLogLevel": "Graphics Backend Log Level:", "SettingsTabLoggingGraphicsBackendLogLevel": "Graphics Backend Log Level:",
"SettingsTabLoggingGraphicsBackendLogLevelNone": "None", "SettingsTabLoggingGraphicsBackendLogLevelNone": "None",
"SettingsTabLoggingGraphicsBackendLogLevelError": "Error", "SettingsTabLoggingGraphicsBackendLogLevelError": "Error",
@@ -618,4 +619,4 @@
"UserProfilesRecoverEmptyList": "No profiles to recover", "UserProfilesRecoverEmptyList": "No profiles to recover",
"UserEditorTitle" : "Edit User", "UserEditorTitle" : "Edit User",
"UserEditorTitleCreate" : "Create User" "UserEditorTitleCreate" : "Create User"
} }

View File

@@ -60,5 +60,6 @@
<Color x:Key="MenuFlyoutPresenterBorderColor">#3D3D3D</Color> <Color x:Key="MenuFlyoutPresenterBorderColor">#3D3D3D</Color>
<Color x:Key="AppListBackgroundColor">#0FFFFFFF</Color> <Color x:Key="AppListBackgroundColor">#0FFFFFFF</Color>
<Color x:Key="AppListHoverBackgroundColor">#1EFFFFFF</Color> <Color x:Key="AppListHoverBackgroundColor">#1EFFFFFF</Color>
<Color x:Key="SecondaryTextColor">#A0FFFFFF</Color>
</Styles.Resources> </Styles.Resources>
</Styles> </Styles>

View File

@@ -52,5 +52,6 @@
<Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color> <Color x:Key="MenuFlyoutPresenterBorderColor">#C1C1C1</Color>
<Color x:Key="AppListBackgroundColor">#b3ffffff</Color> <Color x:Key="AppListBackgroundColor">#b3ffffff</Color>
<Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color> <Color x:Key="AppListHoverBackgroundColor">#80cccccc</Color>
<Color x:Key="SecondaryTextColor">#A0000000</Color>
</Styles.Resources> </Styles.Resources>
</Styles> </Styles>

View File

@@ -56,8 +56,8 @@
<Style Selector="Border.settings"> <Style Selector="Border.settings">
<Setter Property="Background" Value="{DynamicResource ThemeDarkColor}" /> <Setter Property="Background" Value="{DynamicResource ThemeDarkColor}" />
<Setter Property="BorderBrush" Value="{DynamicResource MenuFlyoutPresenterBorderColor}" /> <Setter Property="BorderBrush" Value="{DynamicResource MenuFlyoutPresenterBorderColor}" />
<Setter Property="BorderThickness" Value="2" /> <Setter Property="BorderThickness" Value="1" />
<Setter Property="CornerRadius" Value="3" /> <Setter Property="CornerRadius" Value="5" />
</Style> </Style>
<Style Selector="Image.small"> <Style Selector="Image.small">
<Setter Property="Width" Value="50" /> <Setter Property="Width" Value="50" />
@@ -234,6 +234,9 @@
<Setter Property="BorderBrush" Value="{DynamicResource MenuFlyoutPresenterBorderBrush}" /> <Setter Property="BorderBrush" Value="{DynamicResource MenuFlyoutPresenterBorderBrush}" />
<Setter Property="BorderThickness" Value="{DynamicResource MenuFlyoutPresenterBorderThemeThickness}" /> <Setter Property="BorderThickness" Value="{DynamicResource MenuFlyoutPresenterBorderThemeThickness}" />
</Style> </Style>
<Style Selector="TextBox">
<Setter Property="VerticalContentAlignment" Value="Center" />
</Style>
<Style Selector="TextBox.NumberBoxTextBoxStyle"> <Style Selector="TextBox.NumberBoxTextBoxStyle">
<Setter Property="Foreground" Value="{DynamicResource ThemeForegroundColor}" /> <Setter Property="Foreground" Value="{DynamicResource ThemeForegroundColor}" />
</Style> </Style>
@@ -303,6 +306,9 @@
<Color x:Key="ThemeControlBorderColor">#FF505050</Color> <Color x:Key="ThemeControlBorderColor">#FF505050</Color>
<Color x:Key="VsyncEnabled">#FF2EEAC9</Color> <Color x:Key="VsyncEnabled">#FF2EEAC9</Color>
<Color x:Key="VsyncDisabled">#FFFF4554</Color> <Color x:Key="VsyncDisabled">#FFFF4554</Color>
<Color x:Key="AppListBackgroundColor">#0FFFFFFF</Color>
<Color x:Key="AppListHoverBackgroundColor">#1EFFFFFF</Color>
<Color x:Key="SecondaryTextColor">#A0FFFFFF</Color>
<x:Double x:Key="ScrollBarThickness">15</x:Double> <x:Double x:Key="ScrollBarThickness">15</x:Double>
<x:Double x:Key="FontSizeSmall">8</x:Double> <x:Double x:Key="FontSizeSmall">8</x:Double>
<x:Double x:Key="FontSizeNormal">10</x:Double> <x:Double x:Key="FontSizeNormal">10</x:Double>

View File

@@ -1,13 +1,9 @@
using LibHac;
using LibHac.Fs; using LibHac.Fs;
using LibHac.Fs.Shim;
using LibHac.Ncm; using LibHac.Ncm;
using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -16,7 +12,6 @@ namespace Ryujinx.Ava.UI.Models
{ {
public class SaveModel : BaseModel public class SaveModel : BaseModel
{ {
private readonly HorizonClient _horizonClient;
private long _size; private long _size;
public ulong SaveId { get; } public ulong SaveId { get; }
@@ -41,11 +36,29 @@ namespace Ryujinx.Ava.UI.Models
public bool SizeAvailable { get; set; } public bool SizeAvailable { get; set; }
public string SizeString => $"{((float)_size * 0.000000954):0.###}MB"; public string SizeString => GetSizeString();
public SaveModel(SaveDataInfo info, HorizonClient horizonClient, VirtualFileSystem virtualFileSystem) private string GetSizeString()
{
const int scale = 1024;
string[] orders = { "GiB", "MiB", "KiB" };
long max = (long)Math.Pow(scale, orders.Length);
foreach (string order in orders)
{
if (Size > max)
{
return $"{decimal.Divide(Size, max):##.##} {order}";
}
max /= scale;
}
return "0 KiB";
}
public SaveModel(SaveDataInfo info, VirtualFileSystem virtualFileSystem)
{ {
_horizonClient = horizonClient;
SaveId = info.SaveDataId; SaveId = info.SaveDataId;
TitleId = info.ProgramId; TitleId = info.ProgramId;
UserId = info.UserId; UserId = info.UserId;

View File

@@ -151,7 +151,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool IsSoundIoEnabled { get; set; } public bool IsSoundIoEnabled { get; set; }
public bool IsSDL2Enabled { get; set; } public bool IsSDL2Enabled { get; set; }
public bool EnableCustomTheme { get; set; } public bool EnableCustomTheme { get; set; }
public bool IsCustomResolutionScaleActive => _resolutionScale == 0; public bool IsCustomResolutionScaleActive => _resolutionScale == 4;
public bool IsVulkanSelected => GraphicsBackendIndex == 0; public bool IsVulkanSelected => GraphicsBackendIndex == 0;
public string TimeZone { get; set; } public string TimeZone { get; set; }
@@ -311,25 +311,66 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
ConfigurationState config = ConfigurationState.Instance; ConfigurationState config = ConfigurationState.Instance;
GameDirectories.Clear(); // User Interface
GameDirectories.AddRange(config.Ui.GameDirs.Value);
EnableDiscordIntegration = config.EnableDiscordIntegration; EnableDiscordIntegration = config.EnableDiscordIntegration;
CheckUpdatesOnStart = config.CheckUpdatesOnStart; CheckUpdatesOnStart = config.CheckUpdatesOnStart;
ShowConfirmExit = config.ShowConfirmExit; ShowConfirmExit = config.ShowConfirmExit;
HideCursorOnIdle = config.HideCursorOnIdle; HideCursorOnIdle = config.HideCursorOnIdle;
GameDirectories.Clear();
GameDirectories.AddRange(config.Ui.GameDirs.Value);
EnableCustomTheme = config.Ui.EnableCustomTheme;
CustomThemePath = config.Ui.CustomThemePath;
BaseStyleIndex = config.Ui.BaseStyle == "Light" ? 0 : 1;
// Input
EnableDockedMode = config.System.EnableDockedMode; EnableDockedMode = config.System.EnableDockedMode;
EnableKeyboard = config.Hid.EnableKeyboard; EnableKeyboard = config.Hid.EnableKeyboard;
EnableMouse = config.Hid.EnableMouse; EnableMouse = config.Hid.EnableMouse;
// Keyboard Hotkeys
KeyboardHotkeys = config.Hid.Hotkeys.Value;
// System
Region = (int)config.System.Region.Value;
Language = (int)config.System.Language.Value;
TimeZone = config.System.TimeZone;
DateTime dateTimeOffset = DateTime.Now.AddSeconds(config.System.SystemTimeOffset);
DateOffset = dateTimeOffset.Date;
TimeOffset = dateTimeOffset.TimeOfDay;
EnableVsync = config.Graphics.EnableVsync; EnableVsync = config.Graphics.EnableVsync;
EnablePptc = config.System.EnablePtc;
EnableInternetAccess = config.System.EnableInternetAccess;
EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks; EnableFsIntegrityChecks = config.System.EnableFsIntegrityChecks;
IgnoreMissingServices = config.System.IgnoreMissingServices;
ExpandDramSize = config.System.ExpandRam; ExpandDramSize = config.System.ExpandRam;
IgnoreMissingServices = config.System.IgnoreMissingServices;
// CPU
EnablePptc = config.System.EnablePtc;
MemoryMode = (int)config.System.MemoryManagerMode.Value;
// Graphics
GraphicsBackendIndex = (int)config.Graphics.GraphicsBackend.Value;
PreferredGpuIndex = _gpuIds.Contains(config.Graphics.PreferredGpu) ? _gpuIds.IndexOf(config.Graphics.PreferredGpu) : 0;
EnableShaderCache = config.Graphics.EnableShaderCache; EnableShaderCache = config.Graphics.EnableShaderCache;
EnableTextureRecompression = config.Graphics.EnableTextureRecompression; EnableTextureRecompression = config.Graphics.EnableTextureRecompression;
EnableMacroHLE = config.Graphics.EnableMacroHLE; EnableMacroHLE = config.Graphics.EnableMacroHLE;
ResolutionScale = config.Graphics.ResScale == -1 ? 4 : config.Graphics.ResScale - 1;
CustomResolutionScale = config.Graphics.ResScaleCustom;
MaxAnisotropy = config.Graphics.MaxAnisotropy == -1 ? 0 : (int)(MathF.Log2(config.Graphics.MaxAnisotropy));
AspectRatio = (int)config.Graphics.AspectRatio.Value;
GraphicsBackendMultithreadingIndex = (int)config.Graphics.BackendThreading.Value;
ShaderDumpPath = config.Graphics.ShadersDumpPath;
// Audio
AudioBackend = (int)config.System.AudioBackend.Value;
Volume = config.System.AudioVolume * 100;
// Network
EnableInternetAccess = config.System.EnableInternetAccess;
// Logging
EnableFileLog = config.Logger.EnableFileLog; EnableFileLog = config.Logger.EnableFileLog;
EnableStub = config.Logger.EnableStub; EnableStub = config.Logger.EnableStub;
EnableInfo = config.Logger.EnableInfo; EnableInfo = config.Logger.EnableInfo;
@@ -339,94 +380,69 @@ namespace Ryujinx.Ava.UI.ViewModels
EnableGuest = config.Logger.EnableGuest; EnableGuest = config.Logger.EnableGuest;
EnableDebug = config.Logger.EnableDebug; EnableDebug = config.Logger.EnableDebug;
EnableFsAccessLog = config.Logger.EnableFsAccessLog; EnableFsAccessLog = config.Logger.EnableFsAccessLog;
EnableCustomTheme = config.Ui.EnableCustomTheme;
Volume = config.System.AudioVolume * 100;
GraphicsBackendMultithreadingIndex = (int)config.Graphics.BackendThreading.Value;
OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value;
TimeZone = config.System.TimeZone;
ShaderDumpPath = config.Graphics.ShadersDumpPath;
CustomThemePath = config.Ui.CustomThemePath;
BaseStyleIndex = config.Ui.BaseStyle == "Light" ? 0 : 1;
GraphicsBackendIndex = (int)config.Graphics.GraphicsBackend.Value;
PreferredGpuIndex = _gpuIds.Contains(config.Graphics.PreferredGpu) ? _gpuIds.IndexOf(config.Graphics.PreferredGpu) : 0;
Language = (int)config.System.Language.Value;
Region = (int)config.System.Region.Value;
FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode; FsGlobalAccessLogMode = config.System.FsGlobalAccessLogMode;
AudioBackend = (int)config.System.AudioBackend.Value; OpenglDebugLevel = (int)config.Logger.GraphicsDebugLevel.Value;
MemoryMode = (int)config.System.MemoryManagerMode.Value;
float anisotropy = config.Graphics.MaxAnisotropy;
MaxAnisotropy = anisotropy == -1 ? 0 : (int)(MathF.Log2(anisotropy));
AspectRatio = (int)config.Graphics.AspectRatio.Value;
int resolution = config.Graphics.ResScale;
ResolutionScale = resolution == -1 ? 0 : resolution;
CustomResolutionScale = config.Graphics.ResScaleCustom;
DateTime dateTimeOffset = DateTime.Now.AddSeconds(config.System.SystemTimeOffset);
DateOffset = dateTimeOffset.Date;
TimeOffset = dateTimeOffset.TimeOfDay;
KeyboardHotkeys = config.Hid.Hotkeys.Value;
} }
public void SaveSettings() public void SaveSettings()
{ {
ConfigurationState config = ConfigurationState.Instance; ConfigurationState config = ConfigurationState.Instance;
// User Interface
config.EnableDiscordIntegration.Value = EnableDiscordIntegration;
config.CheckUpdatesOnStart.Value = CheckUpdatesOnStart;
config.ShowConfirmExit.Value = ShowConfirmExit;
config.HideCursorOnIdle.Value = HideCursorOnIdle;
if (_directoryChanged) if (_directoryChanged)
{ {
List<string> gameDirs = new List<string>(GameDirectories); List<string> gameDirs = new(GameDirectories);
config.Ui.GameDirs.Value = gameDirs; config.Ui.GameDirs.Value = gameDirs;
} }
config.Ui.EnableCustomTheme.Value = EnableCustomTheme;
config.Ui.CustomThemePath.Value = CustomThemePath;
config.Ui.BaseStyle.Value = BaseStyleIndex == 0 ? "Light" : "Dark";
// Input
config.System.EnableDockedMode.Value = EnableDockedMode;
config.Hid.EnableKeyboard.Value = EnableKeyboard;
config.Hid.EnableMouse.Value = EnableMouse;
// Keyboard Hotkeys
config.Hid.Hotkeys.Value = KeyboardHotkeys;
// System
config.System.Region.Value = (Region)Region;
config.System.Language.Value = (Language)Language;
if (_validTzRegions.Contains(TimeZone)) if (_validTzRegions.Contains(TimeZone))
{ {
config.System.TimeZone.Value = TimeZone; config.System.TimeZone.Value = TimeZone;
} }
config.Logger.EnableError.Value = EnableError; TimeSpan systemTimeOffset = DateOffset - DateTime.Now;
config.Logger.EnableTrace.Value = EnableTrace;
config.Logger.EnableWarn.Value = EnableWarn; config.System.SystemTimeOffset.Value = systemTimeOffset.Seconds;
config.Logger.EnableInfo.Value = EnableInfo;
config.Logger.EnableStub.Value = EnableStub;
config.Logger.EnableDebug.Value = EnableDebug;
config.Logger.EnableGuest.Value = EnableGuest;
config.Logger.EnableFsAccessLog.Value = EnableFsAccessLog;
config.Logger.EnableFileLog.Value = EnableFileLog;
config.Logger.GraphicsDebugLevel.Value = (GraphicsDebugLevel)OpenglDebugLevel;
config.System.EnableDockedMode.Value = EnableDockedMode;
config.EnableDiscordIntegration.Value = EnableDiscordIntegration;
config.CheckUpdatesOnStart.Value = CheckUpdatesOnStart;
config.ShowConfirmExit.Value = ShowConfirmExit;
config.HideCursorOnIdle.Value = HideCursorOnIdle;
config.Graphics.EnableVsync.Value = EnableVsync; config.Graphics.EnableVsync.Value = EnableVsync;
config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks;
config.System.ExpandRam.Value = ExpandDramSize;
config.System.IgnoreMissingServices.Value = IgnoreMissingServices;
// CPU
config.System.EnablePtc.Value = EnablePptc;
config.System.MemoryManagerMode.Value = (MemoryManagerMode)MemoryMode;
// Graphics
config.Graphics.GraphicsBackend.Value = (GraphicsBackend)GraphicsBackendIndex;
config.Graphics.PreferredGpu.Value = _gpuIds.ElementAtOrDefault(PreferredGpuIndex);
config.Graphics.EnableShaderCache.Value = EnableShaderCache; config.Graphics.EnableShaderCache.Value = EnableShaderCache;
config.Graphics.EnableTextureRecompression.Value = EnableTextureRecompression; config.Graphics.EnableTextureRecompression.Value = EnableTextureRecompression;
config.Graphics.EnableMacroHLE.Value = EnableMacroHLE; config.Graphics.EnableMacroHLE.Value = EnableMacroHLE;
config.Graphics.GraphicsBackend.Value = (GraphicsBackend)GraphicsBackendIndex; config.Graphics.ResScale.Value = ResolutionScale == 4 ? -1 : ResolutionScale + 1;
config.System.EnablePtc.Value = EnablePptc; config.Graphics.ResScaleCustom.Value = CustomResolutionScale;
config.System.EnableInternetAccess.Value = EnableInternetAccess; config.Graphics.MaxAnisotropy.Value = MaxAnisotropy == 0 ? -1 : MathF.Pow(2, MaxAnisotropy);
config.System.EnableFsIntegrityChecks.Value = EnableFsIntegrityChecks; config.Graphics.AspectRatio.Value = (AspectRatio)AspectRatio;
config.System.IgnoreMissingServices.Value = IgnoreMissingServices;
config.System.ExpandRam.Value = ExpandDramSize;
config.Hid.EnableKeyboard.Value = EnableKeyboard;
config.Hid.EnableMouse.Value = EnableMouse;
config.Ui.CustomThemePath.Value = CustomThemePath;
config.Ui.EnableCustomTheme.Value = EnableCustomTheme;
config.Ui.BaseStyle.Value = BaseStyleIndex == 0 ? "Light" : "Dark";
config.System.Language.Value = (Language)Language;
config.System.Region.Value = (Region)Region;
config.Graphics.PreferredGpu.Value = _gpuIds.ElementAtOrDefault(PreferredGpuIndex);
if (ConfigurationState.Instance.Graphics.BackendThreading != (BackendThreading)GraphicsBackendMultithreadingIndex) if (ConfigurationState.Instance.Graphics.BackendThreading != (BackendThreading)GraphicsBackendMultithreadingIndex)
{ {
@@ -434,22 +450,9 @@ namespace Ryujinx.Ava.UI.ViewModels
} }
config.Graphics.BackendThreading.Value = (BackendThreading)GraphicsBackendMultithreadingIndex; config.Graphics.BackendThreading.Value = (BackendThreading)GraphicsBackendMultithreadingIndex;
TimeSpan systemTimeOffset = DateOffset - DateTime.Now;
config.System.SystemTimeOffset.Value = systemTimeOffset.Seconds;
config.Graphics.ShadersDumpPath.Value = ShaderDumpPath; config.Graphics.ShadersDumpPath.Value = ShaderDumpPath;
config.System.FsGlobalAccessLogMode.Value = FsGlobalAccessLogMode;
config.System.MemoryManagerMode.Value = (MemoryManagerMode)MemoryMode;
float anisotropy = MaxAnisotropy == 0 ? -1 : MathF.Pow(2, MaxAnisotropy);
config.Graphics.MaxAnisotropy.Value = anisotropy;
config.Graphics.AspectRatio.Value = (AspectRatio)AspectRatio;
config.Graphics.ResScale.Value = ResolutionScale == 0 ? -1 : ResolutionScale;
config.Graphics.ResScaleCustom.Value = CustomResolutionScale;
config.System.AudioVolume.Value = Volume / 100;
// Audio
AudioBackend audioBackend = (AudioBackend)AudioBackend; AudioBackend audioBackend = (AudioBackend)AudioBackend;
if (audioBackend != config.System.AudioBackend.Value) if (audioBackend != config.System.AudioBackend.Value)
{ {
@@ -458,7 +461,23 @@ namespace Ryujinx.Ava.UI.ViewModels
Logger.Info?.Print(LogClass.Application, $"AudioBackend toggled to: {audioBackend}"); Logger.Info?.Print(LogClass.Application, $"AudioBackend toggled to: {audioBackend}");
} }
config.Hid.Hotkeys.Value = KeyboardHotkeys; config.System.AudioVolume.Value = Volume / 100;
// Network
config.System.EnableInternetAccess.Value = EnableInternetAccess;
// Logging
config.Logger.EnableFileLog.Value = EnableFileLog;
config.Logger.EnableStub.Value = EnableStub;
config.Logger.EnableInfo.Value = EnableInfo;
config.Logger.EnableWarn.Value = EnableWarn;
config.Logger.EnableError.Value = EnableError;
config.Logger.EnableTrace.Value = EnableTrace;
config.Logger.EnableGuest.Value = EnableGuest;
config.Logger.EnableDebug.Value = EnableDebug;
config.Logger.EnableFsAccessLog.Value = EnableFsAccessLog;
config.System.FsGlobalAccessLogMode.Value = FsGlobalAccessLogMode;
config.Logger.GraphicsDebugLevel.Value = (GraphicsDebugLevel)OpenglDebugLevel;
config.ToFileFormat().SaveConfig(Program.ConfigurationPath); config.ToFileFormat().SaveConfig(Program.ConfigurationPath);

View File

@@ -13,8 +13,8 @@ namespace Ryujinx.Ava.UI.ViewModels
private int _sortIndex; private int _sortIndex;
private int _orderIndex; private int _orderIndex;
private string _search; private string _search;
private ObservableCollection<SaveModel> _saves; private ObservableCollection<SaveModel> _saves = new();
private ObservableCollection<SaveModel> _views; private ObservableCollection<SaveModel> _views = new();
private AccountManager _accountManager; private AccountManager _accountManager;
public string SaveManagerHeading => public string SaveManagerHeading =>
@@ -77,8 +77,6 @@ namespace Ryujinx.Ava.UI.ViewModels
public UserSaveManagerViewModel(AccountManager accountManager) public UserSaveManagerViewModel(AccountManager accountManager)
{ {
_accountManager = accountManager; _accountManager = accountManager;
_saves = new ObservableCollection<SaveModel>();
_views = new ObservableCollection<SaveModel>();
} }
public void Sort() public void Sort()

View File

@@ -74,7 +74,6 @@
Margin="5,0,5,0" Margin="5,0,5,0"
HorizontalAlignment="Right" HorizontalAlignment="Right"
VerticalAlignment="Center" VerticalAlignment="Center"
VerticalContentAlignment="Center"
DockPanel.Dock="Right" DockPanel.Dock="Right"
KeyUp="SearchBox_OnKeyUp" KeyUp="SearchBox_OnKeyUp"
Text="{Binding SearchText}" Text="{Binding SearchText}"

View File

@@ -82,9 +82,6 @@
Width="350" Width="350"
HorizontalContentAlignment="Left" HorizontalContentAlignment="Left"
ToolTip.Tip="{locale:Locale ResolutionScaleTooltip}"> ToolTip.Tip="{locale:Locale ResolutionScaleTooltip}">
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScaleCustom}" />
</ComboBoxItem>
<ComboBoxItem> <ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScaleNative}" /> <TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScaleNative}" />
</ComboBoxItem> </ComboBoxItem>
@@ -97,6 +94,9 @@
<ComboBoxItem> <ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScale4x}" /> <TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScale4x}" />
</ComboBoxItem> </ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="{locale:Locale SettingsTabGraphicsResolutionScaleCustom}" />
</ComboBoxItem>
</ComboBox> </ComboBox>
<ui:NumberBox <ui:NumberBox
Margin="10,0,0,0" Margin="10,0,0,0"

View File

@@ -1,4 +1,4 @@
<UserControl <UserControl
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsLoggingView" x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsLoggingView"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -47,31 +47,34 @@
ToolTip.Tip="{locale:Locale ErrorLogTooltip}"> ToolTip.Tip="{locale:Locale ErrorLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableErrorLogs}" /> <TextBlock Text="{locale:Locale SettingsTabLoggingEnableErrorLogs}" />
</CheckBox> </CheckBox>
<CheckBox IsChecked="{Binding EnableTrace}"
ToolTip.Tip="{locale:Locale TraceLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableTraceLogs}" />
</CheckBox>
<CheckBox IsChecked="{Binding EnableGuest}" <CheckBox IsChecked="{Binding EnableGuest}"
ToolTip.Tip="{locale:Locale GuestLogTooltip}"> ToolTip.Tip="{locale:Locale GuestLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableGuestLogs}" /> <TextBlock Text="{locale:Locale SettingsTabLoggingEnableGuestLogs}" />
</CheckBox> </CheckBox>
</StackPanel> </StackPanel>
<Separator Height="1" /> <Separator Height="1" />
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabLoggingDeveloperOptions}" /> <StackPanel Orientation="Vertical" Spacing="2">
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabLoggingDeveloperOptions}" />
<TextBlock Foreground="{DynamicResource SecondaryTextColor}" Text="{locale:Locale SettingsTabLoggingDeveloperOptionsNote}" />
</StackPanel>
<StackPanel <StackPanel
Margin="10,0,0,0" Margin="10,0,0,0"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
Orientation="Vertical" Orientation="Vertical"
Spacing="10"> Spacing="10">
<StackPanel Orientation="Vertical"> <StackPanel Orientation="Vertical">
<CheckBox IsChecked="{Binding EnableDebug}" <CheckBox IsChecked="{Binding EnableTrace}"
ToolTip.Tip="{locale:Locale DebugLogTooltip}"> ToolTip.Tip="{locale:Locale TraceLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableDebugLogs}" /> <TextBlock Text="{locale:Locale SettingsTabLoggingEnableTraceLogs}" />
</CheckBox> </CheckBox>
<CheckBox IsChecked="{Binding EnableFsAccessLog}" <CheckBox IsChecked="{Binding EnableFsAccessLog}"
ToolTip.Tip="{locale:Locale FileAccessLogTooltip}"> ToolTip.Tip="{locale:Locale FileAccessLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableFsAccessLogs}" /> <TextBlock Text="{locale:Locale SettingsTabLoggingEnableFsAccessLogs}" />
</CheckBox> </CheckBox>
<CheckBox IsChecked="{Binding EnableDebug}"
ToolTip.Tip="{locale:Locale DebugLogTooltip}">
<TextBlock Text="{locale:Locale SettingsTabLoggingEnableDebugLogs}" />
</CheckBox>
<StackPanel Margin="0,10,0,0" Orientation="Horizontal" VerticalAlignment="Stretch"> <StackPanel Margin="0,10,0,0" Orientation="Horizontal" VerticalAlignment="Stretch">
<TextBlock VerticalAlignment="Center" <TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale FSAccessLogModeTooltip}" ToolTip.Tip="{locale:Locale FSAccessLogModeTooltip}"

View File

@@ -1,4 +1,4 @@
<UserControl <UserControl
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsSystemView" x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsSystemView"
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
@@ -12,7 +12,7 @@
<Design.DataContext> <Design.DataContext>
<viewModels:SettingsViewModel /> <viewModels:SettingsViewModel />
</Design.DataContext> </Design.DataContext>
<ScrollViewer <ScrollViewer
Name="SystemPage" Name="SystemPage"
HorizontalAlignment="Stretch" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" VerticalAlignment="Stretch"
@@ -172,9 +172,9 @@
</CheckBox> </CheckBox>
</StackPanel> </StackPanel>
<Separator Height="1" /> <Separator Height="1" />
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Vertical" Spacing="2">
<TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemHacks}" /> <TextBlock Classes="h1" Text="{locale:Locale SettingsTabSystemHacks}" />
<TextBlock Text="{locale:Locale SettingsTabSystemHacksNote}" /> <TextBlock Foreground="{DynamicResource SecondaryTextColor}" Text="{locale:Locale SettingsTabSystemHacksNote}" />
</StackPanel> </StackPanel>
<StackPanel <StackPanel
Margin="10,0,0,0" Margin="10,0,0,0"

View File

@@ -55,6 +55,11 @@
HorizontalContentAlignment="Left" HorizontalContentAlignment="Left"
Content="{locale:Locale Size}" /> Content="{locale:Locale Size}" />
</ComboBoxItem> </ComboBoxItem>
<ComboBox.Styles>
<Style Selector="ContentControl#ContentPresenter">
<Setter Property="HorizontalAlignment" Value="Left" />
</Style>
</ComboBox.Styles>
</ComboBox> </ComboBox>
<ComboBox SelectedIndex="{Binding OrderIndex}" Width="150"> <ComboBox SelectedIndex="{Binding OrderIndex}" Width="150">
<ComboBoxItem> <ComboBoxItem>
@@ -69,6 +74,11 @@
HorizontalContentAlignment="Left" HorizontalContentAlignment="Left"
Content="{locale:Locale OrderDescending}" /> Content="{locale:Locale OrderDescending}" />
</ComboBoxItem> </ComboBoxItem>
<ComboBox.Styles>
<Style Selector="ContentControl#ContentPresenter">
<Setter Property="HorizontalAlignment" Value="Left" />
</Style>
</ComboBox.Styles>
</ComboBox> </ComboBox>
</StackPanel> </StackPanel>
<Grid <Grid
@@ -122,6 +132,8 @@
Height="42" Height="42"
Width="42" Width="42"
Padding="10" Padding="10"
BorderBrush="{DynamicResource AppListHoverBackgroundColor}"
BorderThickness="1"
IsVisible="{Binding !InGameList}"> IsVisible="{Binding !InGameList}">
<ui:SymbolIcon <ui:SymbolIcon
Symbol="Help" Symbol="Help"

View File

@@ -94,7 +94,7 @@ namespace Ryujinx.Ava.UI.Views.User
var save = saveDataInfo[i]; var save = saveDataInfo[i];
if (save.ProgramId.Value != 0) if (save.ProgramId.Value != 0)
{ {
var saveModel = new SaveModel(save, _horizonClient, _virtualFileSystem); var saveModel = new SaveModel(save, _virtualFileSystem);
saves.Add(saveModel); saves.Add(saveModel);
} }
} }
@@ -137,10 +137,9 @@ namespace Ryujinx.Ava.UI.Views.User
if (result == UserResult.Yes) if (result == UserResult.Yes)
{ {
_horizonClient.Fs.DeleteSaveData(SaveDataSpaceId.User, saveModel.SaveId); _horizonClient.Fs.DeleteSaveData(SaveDataSpaceId.User, saveModel.SaveId);
ViewModel.Saves.Remove(saveModel);
ViewModel.Sort();
} }
ViewModel.Saves.Remove(saveModel);
ViewModel.Views.Remove(saveModel);
} }
} }
} }

View File

@@ -44,20 +44,22 @@
<settings:SettingsNetworkView Name="NetworkPage" /> <settings:SettingsNetworkView Name="NetworkPage" />
<settings:SettingsLoggingView Name="LoggingPage" /> <settings:SettingsLoggingView Name="LoggingPage" />
</Grid> </Grid>
<ui:NavigationView Grid.Row="1" <ui:NavigationView
IsSettingsVisible="False" Grid.Row="1"
Name="NavPanel" IsSettingsVisible="False"
IsBackEnabled="False" Name="NavPanel"
PaneDisplayMode="Left" IsBackEnabled="False"
Margin="2,10,10,0" PaneDisplayMode="Left"
VerticalAlignment="Stretch" Margin="2,10,10,0"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
OpenPaneLength="200"> HorizontalAlignment="Stretch"
OpenPaneLength="200">
<ui:NavigationView.MenuItems> <ui:NavigationView.MenuItems>
<ui:NavigationViewItem IsSelected="True" <ui:NavigationViewItem
Content="{locale:Locale SettingsTabGeneral}" IsSelected="True"
Tag="UiPage" Content="{locale:Locale SettingsTabGeneral}"
Icon="New" /> Tag="UiPage"
Icon="New" />
<ui:NavigationViewItem <ui:NavigationViewItem
Content="{locale:Locale SettingsTabInput}" Content="{locale:Locale SettingsTabInput}"
Tag="InputPage" Tag="InputPage"
@@ -74,8 +76,9 @@
Content="{locale:Locale SettingsTabCpu}" Content="{locale:Locale SettingsTabCpu}"
Tag="CpuPage"> Tag="CpuPage">
<ui:NavigationViewItem.Icon> <ui:NavigationViewItem.Icon>
<ui:FontIcon FontFamily="avares://Ryujinx.Ava/Assets/Fonts#Segoe Fluent Icons" <ui:FontIcon
Glyph="{helpers:GlyphValueConverter Chip}" /> FontFamily="avares://Ryujinx.Ava/Assets/Fonts#Segoe Fluent Icons"
Glyph="{helpers:GlyphValueConverter Chip}" />
</ui:NavigationViewItem.Icon> </ui:NavigationViewItem.Icon>
</ui:NavigationViewItem> </ui:NavigationViewItem>
<ui:NavigationViewItem <ui:NavigationViewItem
@@ -95,6 +98,11 @@
Tag="LoggingPage" Tag="LoggingPage"
Icon="Document" /> Icon="Document" />
</ui:NavigationView.MenuItems> </ui:NavigationView.MenuItems>
<ui:NavigationView.Styles>
<Style Selector="Grid#PlaceholderGrid">
<Setter Property="Height" Value="40" />
</Style>
</ui:NavigationView.Styles>
</ui:NavigationView> </ui:NavigationView>
<ReversibleStackPanel <ReversibleStackPanel
Grid.Row="2" Grid.Row="2"
@@ -103,17 +111,17 @@
Orientation="Horizontal" Orientation="Horizontal"
HorizontalAlignment="Right" HorizontalAlignment="Right"
ReverseOrder="{Binding IsMacOS}"> ReverseOrder="{Binding IsMacOS}">
<Button <Button
HotKey="Enter" HotKey="Enter"
Classes="accent" Classes="accent"
Content="{locale:Locale SettingsButtonOk}" Content="{locale:Locale SettingsButtonOk}"
Command="{ReflectionBinding OkButton}" /> Command="{ReflectionBinding OkButton}" />
<Button <Button
HotKey="Escape" HotKey="Escape"
Content="{locale:Locale SettingsButtonCancel}" Content="{locale:Locale SettingsButtonCancel}"
Command="{ReflectionBinding CancelButton}" /> Command="{ReflectionBinding CancelButton}" />
<Button <Button
Content="{locale:Locale SettingsButtonApply}" Content="{locale:Locale SettingsButtonApply}"
Command="{ReflectionBinding ApplyButton}" /> Command="{ReflectionBinding ApplyButton}" />
</ReversibleStackPanel> </ReversibleStackPanel>
</Grid> </Grid>

View File

@@ -9,6 +9,8 @@ namespace Ryujinx.Graphics.GAL
public readonly bool HasFrontFacingBug; public readonly bool HasFrontFacingBug;
public readonly bool HasVectorIndexingBug; public readonly bool HasVectorIndexingBug;
public readonly bool NeedsFragmentOutputSpecialization;
public readonly bool ReduceShaderPrecision;
public readonly bool SupportsAstcCompression; public readonly bool SupportsAstcCompression;
public readonly bool SupportsBc123Compression; public readonly bool SupportsBc123Compression;
@@ -49,6 +51,8 @@ namespace Ryujinx.Graphics.GAL
string vendorName, string vendorName,
bool hasFrontFacingBug, bool hasFrontFacingBug,
bool hasVectorIndexingBug, bool hasVectorIndexingBug,
bool needsFragmentOutputSpecialization,
bool reduceShaderPrecision,
bool supportsAstcCompression, bool supportsAstcCompression,
bool supportsBc123Compression, bool supportsBc123Compression,
bool supportsBc45Compression, bool supportsBc45Compression,
@@ -85,6 +89,8 @@ namespace Ryujinx.Graphics.GAL
VendorName = vendorName; VendorName = vendorName;
HasFrontFacingBug = hasFrontFacingBug; HasFrontFacingBug = hasFrontFacingBug;
HasVectorIndexingBug = hasVectorIndexingBug; HasVectorIndexingBug = hasVectorIndexingBug;
NeedsFragmentOutputSpecialization = needsFragmentOutputSpecialization;
ReduceShaderPrecision = reduceShaderPrecision;
SupportsAstcCompression = supportsAstcCompression; SupportsAstcCompression = supportsAstcCompression;
SupportsBc123Compression = supportsBc123Compression; SupportsBc123Compression = supportsBc123Compression;
SupportsBc45Compression = supportsBc45Compression; SupportsBc45Compression = supportsBc45Compression;

View File

@@ -1,5 +1,6 @@
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.Types;
using Ryujinx.Graphics.Gpu.Shader; using Ryujinx.Graphics.Gpu.Shader;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
@@ -10,6 +11,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
/// </summary> /// </summary>
internal class SpecializationStateUpdater internal class SpecializationStateUpdater
{ {
private readonly GpuContext _context;
private GpuChannelGraphicsState _graphics; private GpuChannelGraphicsState _graphics;
private GpuChannelPoolState _pool; private GpuChannelPoolState _pool;
@@ -18,6 +20,15 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private bool _changed; private bool _changed;
/// <summary>
/// Creates a new instance of the specialization state updater class.
/// </summary>
/// <param name="context">GPU context</param>
public SpecializationStateUpdater(GpuContext context)
{
_context = context;
}
/// <summary> /// <summary>
/// Signal that the specialization state has changed. /// Signal that the specialization state has changed.
/// </summary> /// </summary>
@@ -232,6 +243,42 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
} }
} }
/// <summary>
/// Updates the type of the outputs produced by the fragment shader based on the current render target state.
/// </summary>
/// <param name="rtControl">The render target control register</param>
/// <param name="state">The color attachment state</param>
public void SetFragmentOutputTypes(RtControl rtControl, ref Array8<RtColorState> state)
{
bool changed = false;
int count = rtControl.UnpackCount();
for (int index = 0; index < Constants.TotalRenderTargets; index++)
{
int rtIndex = rtControl.UnpackPermutationIndex(index);
var colorState = state[rtIndex];
if (index < count && StateUpdater.IsRtEnabled(colorState))
{
Format format = colorState.Format.Convert().Format;
AttributeType type = format.IsInteger() ? (format.IsSint() ? AttributeType.Sint : AttributeType.Uint) : AttributeType.Float;
if (type != _graphics.FragmentOutputTypes[index])
{
_graphics.FragmentOutputTypes[index] = type;
changed = true;
}
}
}
if (changed && _context.Capabilities.NeedsFragmentOutputSpecialization)
{
Signal();
}
}
/// <summary> /// <summary>
/// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0. /// Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0.
/// </summary> /// </summary>

View File

@@ -138,6 +138,16 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_dirtyMask = ulong.MaxValue >> ((sizeof(ulong) * 8) - _callbacks.Length); _dirtyMask = ulong.MaxValue >> ((sizeof(ulong) * 8) - _callbacks.Length);
} }
/// <summary>
/// Check if the given register group is dirty without clearing it.
/// </summary>
/// <param name="groupIndex">Index of the group to check</param>
/// <returns>True if dirty, false otherwise</returns>
public bool IsDirty(int groupIndex)
{
return (_dirtyMask & (1UL << groupIndex)) != 0;
}
/// <summary> /// <summary>
/// Check all the groups specified by <paramref name="checkMask"/> for modification, and update if modified. /// Check all the groups specified by <paramref name="checkMask"/> for modification, and update if modified.
/// </summary> /// </summary>

View File

@@ -20,6 +20,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
public const int ScissorStateIndex = 16; public const int ScissorStateIndex = 16;
public const int VertexBufferStateIndex = 0; public const int VertexBufferStateIndex = 0;
public const int PrimitiveRestartStateIndex = 12; public const int PrimitiveRestartStateIndex = 12;
public const int RenderTargetStateIndex = 27;
private readonly GpuContext _context; private readonly GpuContext _context;
private readonly GpuChannel _channel; private readonly GpuChannel _channel;
@@ -264,6 +265,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_prevTfEnable = false; _prevTfEnable = false;
} }
if (_updateTracker.IsDirty(RenderTargetStateIndex))
{
UpdateRenderTargetSpecialization();
}
_updateTracker.Update(ulong.MaxValue); _updateTracker.Update(ulong.MaxValue);
// If any state that the shader depends on changed, // If any state that the shader depends on changed,
@@ -526,12 +532,20 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
} }
} }
/// <summary>
/// Updates specialization state based on render target state.
/// </summary>
public void UpdateRenderTargetSpecialization()
{
_currentSpecState.SetFragmentOutputTypes(_state.State.RtControl, ref _state.State.RtColorState);
}
/// <summary> /// <summary>
/// Checks if a render target color buffer is used. /// Checks if a render target color buffer is used.
/// </summary> /// </summary>
/// <param name="colorState">Color buffer information</param> /// <param name="colorState">Color buffer information</param>
/// <returns>True if the specified buffer is enabled/used, false otherwise</returns> /// <returns>True if the specified buffer is enabled/used, false otherwise</returns>
private static bool IsRtEnabled(RtColorState colorState) internal static bool IsRtEnabled(RtColorState colorState)
{ {
// Colors are disabled by writing 0 to the format. // Colors are disabled by writing 0 to the format.
return colorState.Format != 0 && colorState.WidthOrStride != 0; return colorState.Format != 0 && colorState.WidthOrStride != 0;
@@ -893,7 +907,12 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
{ {
Logger.Debug?.Print(LogClass.Gpu, $"Invalid attribute format 0x{vertexAttrib.UnpackFormat():X}."); Logger.Debug?.Print(LogClass.Gpu, $"Invalid attribute format 0x{vertexAttrib.UnpackFormat():X}.");
format = Format.R32G32B32A32Float; format = vertexAttrib.UnpackType() switch
{
VertexAttribType.Sint => Format.R32G32B32A32Sint,
VertexAttribType.Uint => Format.R32G32B32A32Uint,
_ => Format.R32G32B32A32Float
};
} }
vertexAttribs[index] = new VertexAttribDescriptor( vertexAttribs[index] = new VertexAttribDescriptor(

View File

@@ -71,7 +71,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_i2mClass = new InlineToMemoryClass(context, channel, initializeState: false); _i2mClass = new InlineToMemoryClass(context, channel, initializeState: false);
var spec = new SpecializationStateUpdater(); var spec = new SpecializationStateUpdater(context);
var drawState = new DrawState(); var drawState = new DrawState();
_drawManager = new DrawManager(context, channel, _state, drawState, spec); _drawManager = new DrawManager(context, channel, _state, drawState, spec);

View File

@@ -1,4 +1,5 @@
using Ryujinx.Graphics.Gpu.Engine.GPFifo; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Engine.GPFifo;
using Ryujinx.Graphics.Gpu.Image; using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Gpu.Memory;
using System; using System;
@@ -31,6 +32,11 @@ namespace Ryujinx.Graphics.Gpu
/// </summary> /// </summary>
internal MemoryManager MemoryManager => _memoryManager; internal MemoryManager MemoryManager => _memoryManager;
/// <summary>
/// Host hardware capabilities from the GPU context.
/// </summary>
internal ref Capabilities Capabilities => ref _context.Capabilities;
/// <summary> /// <summary>
/// Creates a new instance of a GPU channel. /// Creates a new instance of a GPU channel.
/// </summary> /// </summary>

View File

@@ -107,6 +107,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
return _oldSpecState.GraphicsState.AttributeTypes[location]; return _oldSpecState.GraphicsState.AttributeTypes[location];
} }
/// <inheritdoc/>
public AttributeType QueryFragmentOutputType(int location)
{
return _oldSpecState.GraphicsState.FragmentOutputTypes[location];
}
/// <inheritdoc/> /// <inheritdoc/>
public int QueryComputeLocalSizeX() => _oldSpecState.ComputeState.LocalSizeX; public int QueryComputeLocalSizeX() => _oldSpecState.ComputeState.LocalSizeX;

View File

@@ -113,6 +113,12 @@ namespace Ryujinx.Graphics.Gpu.Shader
return _state.GraphicsState.AttributeTypes[location]; return _state.GraphicsState.AttributeTypes[location];
} }
/// <inheritdoc/>
public AttributeType QueryFragmentOutputType(int location)
{
return _state.GraphicsState.FragmentOutputTypes[location];
}
/// <inheritdoc/> /// <inheritdoc/>
public int QueryComputeLocalSizeX() => _state.ComputeState.LocalSizeX; public int QueryComputeLocalSizeX() => _state.ComputeState.LocalSizeX;

View File

@@ -112,6 +112,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
}; };
} }
public bool QueryHostReducedPrecision() => _context.Capabilities.ReduceShaderPrecision;
public bool QueryHostHasFrontFacingBug() => _context.Capabilities.HasFrontFacingBug; public bool QueryHostHasFrontFacingBug() => _context.Capabilities.HasFrontFacingBug;
public bool QueryHostHasVectorIndexingBug() => _context.Capabilities.HasVectorIndexingBug; public bool QueryHostHasVectorIndexingBug() => _context.Capabilities.HasVectorIndexingBug;

View File

@@ -87,6 +87,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary> /// </summary>
public bool HasUnalignedStorageBuffer; public bool HasUnalignedStorageBuffer;
/// <summary>
/// Type of the fragment shader outputs.
/// </summary>
public Array8<AttributeType> FragmentOutputTypes;
/// <summary> /// <summary>
/// Creates a new GPU graphics state. /// Creates a new GPU graphics state.
/// </summary> /// </summary>
@@ -105,6 +110,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="attributeTypes">Type of the vertex attributes consumed by the shader</param> /// <param name="attributeTypes">Type of the vertex attributes consumed by the shader</param>
/// <param name="hasConstantBufferDrawParameters">Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0</param> /// <param name="hasConstantBufferDrawParameters">Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0</param>
/// <param name="hasUnalignedStorageBuffer">Indicates that any storage buffer use is unaligned</param> /// <param name="hasUnalignedStorageBuffer">Indicates that any storage buffer use is unaligned</param>
/// <param name="fragmentOutputTypes">Type of the fragment shader outputs</param>
public GpuChannelGraphicsState( public GpuChannelGraphicsState(
bool earlyZForce, bool earlyZForce,
PrimitiveTopology topology, PrimitiveTopology topology,
@@ -120,7 +126,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
float alphaTestReference, float alphaTestReference,
ref Array32<AttributeType> attributeTypes, ref Array32<AttributeType> attributeTypes,
bool hasConstantBufferDrawParameters, bool hasConstantBufferDrawParameters,
bool hasUnalignedStorageBuffer) bool hasUnalignedStorageBuffer,
ref Array8<AttributeType> fragmentOutputTypes)
{ {
EarlyZForce = earlyZForce; EarlyZForce = earlyZForce;
Topology = topology; Topology = topology;
@@ -137,6 +144,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
AttributeTypes = attributeTypes; AttributeTypes = attributeTypes;
HasConstantBufferDrawParameters = hasConstantBufferDrawParameters; HasConstantBufferDrawParameters = hasConstantBufferDrawParameters;
HasUnalignedStorageBuffer = hasUnalignedStorageBuffer; HasUnalignedStorageBuffer = hasUnalignedStorageBuffer;
FragmentOutputTypes = fragmentOutputTypes;
} }
} }
} }

View File

@@ -530,6 +530,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
return false; return false;
} }
if (channel.Capabilities.NeedsFragmentOutputSpecialization && !graphicsState.FragmentOutputTypes.AsSpan().SequenceEqual(GraphicsState.FragmentOutputTypes.AsSpan()))
{
return false;
}
return Matches(channel, ref poolState, checkTextures, isCompute: false); return Matches(channel, ref poolState, checkTextures, isCompute: false);
} }

View File

@@ -106,6 +106,8 @@ namespace Ryujinx.Graphics.OpenGL
vendorName: GpuVendor, vendorName: GpuVendor,
hasFrontFacingBug: HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows, hasFrontFacingBug: HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows,
hasVectorIndexingBug: HwCapabilities.Vendor == HwCapabilities.GpuVendor.AmdWindows, hasVectorIndexingBug: HwCapabilities.Vendor == HwCapabilities.GpuVendor.AmdWindows,
needsFragmentOutputSpecialization: false,
reduceShaderPrecision: false,
supportsAstcCompression: HwCapabilities.SupportsAstcCompression, supportsAstcCompression: HwCapabilities.SupportsAstcCompression,
supportsBc123Compression: HwCapabilities.SupportsTextureCompressionS3tc, supportsBc123Compression: HwCapabilities.SupportsTextureCompressionS3tc,
supportsBc45Compression: HwCapabilities.SupportsTextureCompressionRgtc, supportsBc45Compression: HwCapabilities.SupportsTextureCompressionRgtc,

View File

@@ -346,12 +346,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{ {
string name = context.OperandManager.DeclareLocal(decl); string name = context.OperandManager.DeclareLocal(decl);
context.AppendLine(GetVarTypeName(decl.VarType) + " " + name + ";"); context.AppendLine(GetVarTypeName(context, decl.VarType) + " " + name + ";");
} }
} }
public static string GetVarTypeName(AggregateType type, bool precise = true) public static string GetVarTypeName(CodeGenContext context, AggregateType type, bool precise = true)
{ {
if (context.Config.GpuAccessor.QueryHostReducedPrecision())
{
precise = false;
}
return type switch return type switch
{ {
AggregateType.Void => "void", AggregateType.Void => "void",
@@ -666,7 +671,22 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
} }
else else
{ {
context.AppendLine($"layout (location = {attr}) out vec4 {name};"); string type = context.Config.Stage != ShaderStage.Fragment ? "vec4" :
context.Config.GpuAccessor.QueryFragmentOutputType(attr) switch
{
AttributeType.Sint => "ivec4",
AttributeType.Uint => "uvec4",
_ => "vec4"
};
if (context.Config.GpuAccessor.QueryHostReducedPrecision() && context.Config.Stage == ShaderStage.Vertex && attr == 0)
{
context.AppendLine($"layout (location = {attr}) invariant out {type} {name};");
}
else
{
context.AppendLine($"layout (location = {attr}) out {type} {name};");
}
} }
} }

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{ {
for (int i = 1; i < info.Functions.Count; i++) for (int i = 1; i < info.Functions.Count; i++)
{ {
context.AppendLine($"{GetFunctionSignature(info.Functions[i])};"); context.AppendLine($"{GetFunctionSignature(context, info.Functions[i])};");
} }
context.AppendLine(); context.AppendLine();
@@ -44,7 +44,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
{ {
context.CurrentFunction = function; context.CurrentFunction = function;
context.AppendLine(GetFunctionSignature(function, funcName)); context.AppendLine(GetFunctionSignature(context, function, funcName));
context.EnterScope(); context.EnterScope();
Declarations.DeclareLocals(context, function); Declarations.DeclareLocals(context, function);
@@ -54,23 +54,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.LeaveScope(); context.LeaveScope();
} }
private static string GetFunctionSignature(StructuredFunction function, string funcName = null) private static string GetFunctionSignature(CodeGenContext context, StructuredFunction function, string funcName = null)
{ {
string[] args = new string[function.InArguments.Length + function.OutArguments.Length]; string[] args = new string[function.InArguments.Length + function.OutArguments.Length];
for (int i = 0; i < function.InArguments.Length; i++) for (int i = 0; i < function.InArguments.Length; i++)
{ {
args[i] = $"{Declarations.GetVarTypeName(function.InArguments[i])} {OperandManager.GetArgumentName(i)}"; args[i] = $"{Declarations.GetVarTypeName(context, function.InArguments[i])} {OperandManager.GetArgumentName(i)}";
} }
for (int i = 0; i < function.OutArguments.Length; i++) for (int i = 0; i < function.OutArguments.Length; i++)
{ {
int j = i + function.InArguments.Length; int j = i + function.InArguments.Length;
args[j] = $"out {Declarations.GetVarTypeName(function.OutArguments[i])} {OperandManager.GetArgumentName(j)}"; args[j] = $"out {Declarations.GetVarTypeName(context, function.OutArguments[i])} {OperandManager.GetArgumentName(j)}";
} }
return $"{Declarations.GetVarTypeName(function.ReturnType)} {funcName ?? function.Name}({string.Join(", ", args)})"; return $"{Declarations.GetVarTypeName(context, function.ReturnType)} {funcName ?? function.Name}({string.Join(", ", args)})";
} }
private static void PrintBlock(CodeGenContext context, AstBlock block) private static void PrintBlock(CodeGenContext context, AstBlock block)

View File

@@ -32,7 +32,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if ((outputType & AggregateType.ElementCountMask) != 0) if ((outputType & AggregateType.ElementCountMask) != 0)
{ {
return $"{Declarations.GetVarTypeName(outputType, precise: false)}({imageConst})"; return $"{Declarations.GetVarTypeName(context, outputType, precise: false)}({imageConst})";
} }
return imageConst; return imageConst;
@@ -513,7 +513,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
if ((outputType & AggregateType.ElementCountMask) != 0) if ((outputType & AggregateType.ElementCountMask) != 0)
{ {
return $"{Declarations.GetVarTypeName(outputType, precise: false)}({scalarValue})"; return $"{Declarations.GetVarTypeName(context, outputType, precise: false)}({scalarValue})";
} }
} }

View File

@@ -577,6 +577,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.Decorate(spvVar, Decoration.Patch); context.Decorate(spvVar, Decoration.Patch);
} }
if (context.Config.GpuAccessor.QueryHostReducedPrecision() && attr == AttributeConsts.PositionX && context.Config.Stage != ShaderStage.Fragment)
{
context.Decorate(spvVar, Decoration.Invariant);
}
context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)GetBuiltIn(context, attrInfo.BaseValue)); context.Decorate(spvVar, Decoration.BuiltIn, (LiteralInteger)GetBuiltIn(context, attrInfo.BaseValue));
if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline && isOutAttr) if (context.Config.TransformFeedbackEnabled && context.Config.LastInVertexPipeline && isOutAttr)

View File

@@ -2194,13 +2194,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (operation.Inst.HasFlag(Instruction.FP64)) if (operation.Inst.HasFlag(Instruction.FP64))
{ {
var result = emitF(context.TypeFP64(), context.GetFP64(src1), context.GetFP64(src2)); var result = emitF(context.TypeFP64(), context.GetFP64(src1), context.GetFP64(src2));
context.Decorate(result, Decoration.NoContraction);
if (!context.Config.GpuAccessor.QueryHostReducedPrecision())
{
context.Decorate(result, Decoration.NoContraction);
}
return new OperationResult(AggregateType.FP64, result); return new OperationResult(AggregateType.FP64, result);
} }
else if (operation.Inst.HasFlag(Instruction.FP32)) else if (operation.Inst.HasFlag(Instruction.FP32))
{ {
var result = emitF(context.TypeFP32(), context.GetFP32(src1), context.GetFP32(src2)); var result = emitF(context.TypeFP32(), context.GetFP32(src1), context.GetFP32(src2));
context.Decorate(result, Decoration.NoContraction);
if (!context.Config.GpuAccessor.QueryHostReducedPrecision())
{
context.Decorate(result, Decoration.NoContraction);
}
return new OperationResult(AggregateType.FP32, result); return new OperationResult(AggregateType.FP32, result);
} }
else else
@@ -2255,13 +2265,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
if (operation.Inst.HasFlag(Instruction.FP64)) if (operation.Inst.HasFlag(Instruction.FP64))
{ {
var result = emitF(context.TypeFP64(), context.GetFP64(src1), context.GetFP64(src2), context.GetFP64(src3)); var result = emitF(context.TypeFP64(), context.GetFP64(src1), context.GetFP64(src2), context.GetFP64(src3));
context.Decorate(result, Decoration.NoContraction);
if (!context.Config.GpuAccessor.QueryHostReducedPrecision())
{
context.Decorate(result, Decoration.NoContraction);
}
return new OperationResult(AggregateType.FP64, result); return new OperationResult(AggregateType.FP64, result);
} }
else if (operation.Inst.HasFlag(Instruction.FP32)) else if (operation.Inst.HasFlag(Instruction.FP32))
{ {
var result = emitF(context.TypeFP32(), context.GetFP32(src1), context.GetFP32(src2), context.GetFP32(src3)); var result = emitF(context.TypeFP32(), context.GetFP32(src1), context.GetFP32(src2), context.GetFP32(src3));
context.Decorate(result, Decoration.NoContraction);
if (!context.Config.GpuAccessor.QueryHostReducedPrecision())
{
context.Decorate(result, Decoration.NoContraction);
}
return new OperationResult(AggregateType.FP32, result); return new OperationResult(AggregateType.FP32, result);
} }
else else

View File

@@ -114,6 +114,16 @@ namespace Ryujinx.Graphics.Shader
return index; return index;
} }
/// <summary>
/// Queries output type for fragment shaders.
/// </summary>
/// <param name="location">Location of the framgent output</param>
/// <returns>Output location</returns>
AttributeType QueryFragmentOutputType(int location)
{
return AttributeType.Float;
}
/// <summary> /// <summary>
/// Queries Local Size X for compute shaders. /// Queries Local Size X for compute shaders.
/// </summary> /// </summary>
@@ -186,6 +196,15 @@ namespace Ryujinx.Graphics.Shader
return false; return false;
} }
/// <summary>
/// Queries host about whether to reduce precision to improve performance.
/// </summary>
/// <returns>True if precision is limited to vertex position, false otherwise</returns>
bool QueryHostReducedPrecision()
{
return false;
}
/// <summary> /// <summary>
/// Queries host about the presence of the FrontFacing built-in variable bug. /// Queries host about the presence of the FrontFacing built-in variable bug.
/// </summary> /// </summary>

View File

@@ -128,7 +128,15 @@ namespace Ryujinx.Graphics.Shader.Translation
} }
else if (value >= AttributeConsts.FragmentOutputColorBase && value < AttributeConsts.FragmentOutputColorEnd) else if (value >= AttributeConsts.FragmentOutputColorBase && value < AttributeConsts.FragmentOutputColorEnd)
{ {
return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | AggregateType.FP32, false); int location = (value - AttributeConsts.FragmentOutputColorBase) / 16;
var elemType = config.GpuAccessor.QueryFragmentOutputType(location) switch
{
AttributeType.Sint => AggregateType.S32,
AttributeType.Uint => AggregateType.U32,
_ => AggregateType.FP32
};
return new AttributeInfo(value & ~0xf, (value >> 2) & 3, 4, AggregateType.Vector4 | elemType, false);
} }
else if (value == AttributeConsts.SupportBlockViewInverseX || value == AttributeConsts.SupportBlockViewInverseY) else if (value == AttributeConsts.SupportBlockViewInverseX || value == AttributeConsts.SupportBlockViewInverseY)
{ {

View File

@@ -14,6 +14,12 @@ namespace Ryujinx.Graphics.Vulkan
MemoryPropertyFlags.HostCoherentBit | MemoryPropertyFlags.HostCoherentBit |
MemoryPropertyFlags.HostCachedBit; MemoryPropertyFlags.HostCachedBit;
// Some drivers don't expose a "HostCached" memory type,
// so we need those alternative flags for the allocation to succeed there.
private const MemoryPropertyFlags DefaultBufferMemoryAltFlags =
MemoryPropertyFlags.HostVisibleBit |
MemoryPropertyFlags.HostCoherentBit;
private const MemoryPropertyFlags DeviceLocalBufferMemoryFlags = private const MemoryPropertyFlags DeviceLocalBufferMemoryFlags =
MemoryPropertyFlags.DeviceLocalBit; MemoryPropertyFlags.DeviceLocalBit;
@@ -94,9 +100,21 @@ namespace Ryujinx.Graphics.Vulkan
gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError(); gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError();
gd.Api.GetBufferMemoryRequirements(_device, buffer, out var requirements); gd.Api.GetBufferMemoryRequirements(_device, buffer, out var requirements);
var allocateFlags = deviceLocal ? DeviceLocalBufferMemoryFlags : DefaultBufferMemoryFlags; MemoryPropertyFlags allocateFlags;
MemoryPropertyFlags allocateFlagsAlt;
var allocation = gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, requirements, allocateFlags); if (deviceLocal)
{
allocateFlags = DeviceLocalBufferMemoryFlags;
allocateFlagsAlt = DeviceLocalBufferMemoryFlags;
}
else
{
allocateFlags = DefaultBufferMemoryFlags;
allocateFlagsAlt = DefaultBufferMemoryAltFlags;
}
var allocation = gd.MemoryAllocator.AllocateDeviceMemory(_physicalDevice, requirements, allocateFlags, allocateFlagsAlt);
if (allocation.Memory.Handle == 0UL) if (allocation.Memory.Handle == 0UL)
{ {

View File

@@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Vulkan
public int[] AttachmentIndices { get; } public int[] AttachmentIndices { get; }
public int AttachmentsCount { get; } public int AttachmentsCount { get; }
public int MaxColorAttachmentIndex { get; } public int MaxColorAttachmentIndex => AttachmentIndices.Length > 0 ? AttachmentIndices[AttachmentIndices.Length - 1] : -1;
public bool HasDepthStencil { get; } public bool HasDepthStencil { get; }
public int ColorAttachmentsCount => AttachmentsCount - (HasDepthStencil ? 1 : 0); public int ColorAttachmentsCount => AttachmentsCount - (HasDepthStencil ? 1 : 0);
@@ -67,7 +67,6 @@ namespace Ryujinx.Graphics.Vulkan
AttachmentSamples = new uint[count]; AttachmentSamples = new uint[count];
AttachmentFormats = new VkFormat[count]; AttachmentFormats = new VkFormat[count];
AttachmentIndices = new int[count]; AttachmentIndices = new int[count];
MaxColorAttachmentIndex = colors.Length - 1;
uint width = uint.MaxValue; uint width = uint.MaxValue;
uint height = uint.MaxValue; uint height = uint.MaxValue;
@@ -140,6 +139,25 @@ namespace Ryujinx.Graphics.Vulkan
return _attachments[index]; return _attachments[index];
} }
public ComponentType GetAttachmentComponentType(int index)
{
if (_colors != null && (uint)index < _colors.Length)
{
var format = _colors[index].Info.Format;
if (format.IsSint())
{
return ComponentType.SignedInteger;
}
else if (format.IsUint())
{
return ComponentType.UnsignedInteger;
}
}
return ComponentType.Float;
}
public bool IsValidColorAttachment(int bindIndex) public bool IsValidColorAttachment(int bindIndex)
{ {
return (uint)bindIndex < Constants.MaxRenderTargets && (_validColorAttachments & (1u << bindIndex)) != 0; return (uint)bindIndex < Constants.MaxRenderTargets && (_validColorAttachments & (1u << bindIndex)) != 0;

View File

@@ -1,7 +1,20 @@
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
[Flags]
enum PortabilitySubsetFlags
{
None = 0,
VertexBufferAlignment4B = 1,
NoTriangleFans = 1 << 1,
NoPointMode = 1 << 2,
No3DImageView = 1 << 3,
NoLodBias = 1 << 4
}
readonly struct HardwareCapabilities readonly struct HardwareCapabilities
{ {
public readonly bool SupportsIndexTypeUint8; public readonly bool SupportsIndexTypeUint8;
@@ -23,6 +36,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly uint MaxSubgroupSize; public readonly uint MaxSubgroupSize;
public readonly ShaderStageFlags RequiredSubgroupSizeStages; public readonly ShaderStageFlags RequiredSubgroupSizeStages;
public readonly SampleCountFlags SupportedSampleCounts; public readonly SampleCountFlags SupportedSampleCounts;
public readonly PortabilitySubsetFlags PortabilitySubset;
public HardwareCapabilities( public HardwareCapabilities(
bool supportsIndexTypeUint8, bool supportsIndexTypeUint8,
@@ -43,7 +57,8 @@ namespace Ryujinx.Graphics.Vulkan
uint minSubgroupSize, uint minSubgroupSize,
uint maxSubgroupSize, uint maxSubgroupSize,
ShaderStageFlags requiredSubgroupSizeStages, ShaderStageFlags requiredSubgroupSizeStages,
SampleCountFlags supportedSampleCounts) SampleCountFlags supportedSampleCounts,
PortabilitySubsetFlags portabilitySubset)
{ {
SupportsIndexTypeUint8 = supportsIndexTypeUint8; SupportsIndexTypeUint8 = supportsIndexTypeUint8;
SupportsCustomBorderColor = supportsCustomBorderColor; SupportsCustomBorderColor = supportsCustomBorderColor;
@@ -64,6 +79,7 @@ namespace Ryujinx.Graphics.Vulkan
MaxSubgroupSize = maxSubgroupSize; MaxSubgroupSize = maxSubgroupSize;
RequiredSubgroupSizeStages = requiredSubgroupSizeStages; RequiredSubgroupSizeStages = requiredSubgroupSizeStages;
SupportedSampleCounts = supportedSampleCounts; SupportedSampleCounts = supportedSampleCounts;
PortabilitySubset = portabilitySubset;
} }
} }
} }

View File

@@ -9,6 +9,13 @@ using VkFormat = Silk.NET.Vulkan.Format;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
enum ComponentType
{
Float,
SignedInteger,
UnsignedInteger
}
class HelperShader : IDisposable class HelperShader : IDisposable
{ {
private const int UniformBufferAlignment = 256; private const int UniformBufferAlignment = 256;
@@ -18,7 +25,9 @@ namespace Ryujinx.Graphics.Vulkan
private readonly ISampler _samplerNearest; private readonly ISampler _samplerNearest;
private readonly IProgram _programColorBlit; private readonly IProgram _programColorBlit;
private readonly IProgram _programColorBlitClearAlpha; private readonly IProgram _programColorBlitClearAlpha;
private readonly IProgram _programColorClear; private readonly IProgram _programColorClearF;
private readonly IProgram _programColorClearSI;
private readonly IProgram _programColorClearUI;
private readonly IProgram _programStrideChange; private readonly IProgram _programStrideChange;
private readonly IProgram _programConvertIndexBuffer; private readonly IProgram _programConvertIndexBuffer;
private readonly IProgram _programConvertIndirectData; private readonly IProgram _programConvertIndirectData;
@@ -63,10 +72,22 @@ namespace Ryujinx.Graphics.Vulkan
Array.Empty<int>(), Array.Empty<int>(),
Array.Empty<int>()); Array.Empty<int>());
_programColorClear = gd.CreateProgramWithMinimalLayout(new[] _programColorClearF = gd.CreateProgramWithMinimalLayout(new[]
{ {
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv), new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorClearFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv), new ShaderSource(ShaderBinaries.ColorClearFFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
});
_programColorClearSI = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorClearSIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
});
_programColorClearUI = gd.CreateProgramWithMinimalLayout(new[]
{
new ShaderSource(ShaderBinaries.ColorClearVertexShaderSource, colorBlitVertexBindings, ShaderStage.Vertex, TargetLanguage.Spirv),
new ShaderSource(ShaderBinaries.ColorClearUIFragmentShaderSource, colorClearFragmentBindings, ShaderStage.Fragment, TargetLanguage.Spirv),
}); });
var strideChangeBindings = new ShaderBindings( var strideChangeBindings = new ShaderBindings(
@@ -242,6 +263,7 @@ namespace Ryujinx.Graphics.Vulkan
int dstWidth, int dstWidth,
int dstHeight, int dstHeight,
VkFormat dstFormat, VkFormat dstFormat,
ComponentType type,
Rectangle<int> scissor) Rectangle<int> scissor)
{ {
const int ClearColorBufferSize = 16; const int ClearColorBufferSize = 16;
@@ -273,7 +295,22 @@ namespace Ryujinx.Graphics.Vulkan
scissors[0] = scissor; scissors[0] = scissor;
_pipeline.SetProgram(_programColorClear); IProgram program;
if (type == ComponentType.SignedInteger)
{
program = _programColorClearSI;
}
else if (type == ComponentType.UnsignedInteger)
{
program = _programColorClearUI;
}
else
{
program = _programColorClearF;
}
_pipeline.SetProgram(program);
_pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat); _pipeline.SetRenderTarget(dst, (uint)dstWidth, (uint)dstHeight, false, dstFormat);
_pipeline.SetRenderTargetColorMasks(new uint[] { componentMask }); _pipeline.SetRenderTargetColorMasks(new uint[] { componentMask });
_pipeline.SetViewports(viewports, false); _pipeline.SetViewports(viewports, false);
@@ -948,7 +985,9 @@ namespace Ryujinx.Graphics.Vulkan
{ {
_programColorBlitClearAlpha.Dispose(); _programColorBlitClearAlpha.Dispose();
_programColorBlit.Dispose(); _programColorBlit.Dispose();
_programColorClear.Dispose(); _programColorClearF.Dispose();
_programColorClearSI.Dispose();
_programColorClearUI.Dispose();
_programStrideChange.Dispose(); _programStrideChange.Dispose();
_programConvertIndexBuffer.Dispose(); _programConvertIndexBuffer.Dispose();
_programConvertIndirectData.Dispose(); _programConvertIndirectData.Dispose();

View File

@@ -27,7 +27,16 @@ namespace Ryujinx.Graphics.Vulkan
MemoryRequirements requirements, MemoryRequirements requirements,
MemoryPropertyFlags flags = 0) MemoryPropertyFlags flags = 0)
{ {
int memoryTypeIndex = FindSuitableMemoryTypeIndex(_api, physicalDevice, requirements.MemoryTypeBits, flags); return AllocateDeviceMemory(physicalDevice, requirements, flags, flags);
}
public MemoryAllocation AllocateDeviceMemory(
PhysicalDevice physicalDevice,
MemoryRequirements requirements,
MemoryPropertyFlags flags,
MemoryPropertyFlags alternativeFlags)
{
int memoryTypeIndex = FindSuitableMemoryTypeIndex(_api, physicalDevice, requirements.MemoryTypeBits, flags, alternativeFlags);
if (memoryTypeIndex < 0) if (memoryTypeIndex < 0)
{ {
return default; return default;
@@ -56,21 +65,35 @@ namespace Ryujinx.Graphics.Vulkan
return newBl.Allocate(size, alignment, map); return newBl.Allocate(size, alignment, map);
} }
private static int FindSuitableMemoryTypeIndex(Vk api, PhysicalDevice physicalDevice, uint memoryTypeBits, MemoryPropertyFlags flags) private static int FindSuitableMemoryTypeIndex(
Vk api,
PhysicalDevice physicalDevice,
uint memoryTypeBits,
MemoryPropertyFlags flags,
MemoryPropertyFlags alternativeFlags)
{ {
int bestCandidateIndex = -1;
api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties); api.GetPhysicalDeviceMemoryProperties(physicalDevice, out var properties);
for (int i = 0; i < properties.MemoryTypeCount; i++) for (int i = 0; i < properties.MemoryTypeCount; i++)
{ {
var type = properties.MemoryTypes[i]; var type = properties.MemoryTypes[i];
if ((memoryTypeBits & (1 << i)) != 0 && type.PropertyFlags.HasFlag(flags)) if ((memoryTypeBits & (1 << i)) != 0)
{ {
return i; if (type.PropertyFlags.HasFlag(flags))
{
return i;
}
else if (type.PropertyFlags.HasFlag(alternativeFlags))
{
bestCandidateIndex = i;
}
} }
} }
return -1; return bestCandidateIndex;
} }
public void Dispose() public void Dispose()

View File

@@ -0,0 +1,104 @@
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Vulkan.MoltenVK
{
enum MVKConfigLogLevel : int
{
None = 0,
Error = 1,
Warning = 2,
Info = 3,
Debug = 4
}
enum MVKConfigTraceVulkanCalls : int
{
None = 0,
Enter = 1,
EnterExit = 2,
Duration = 3
}
enum MVKConfigAutoGPUCaptureScope : int
{
None = 0,
Device = 1,
Frame = 2
}
[Flags]
enum MVKConfigAdvertiseExtensions : int
{
All = 0x00000001,
MoltenVK = 0x00000002,
WSI = 0x00000004,
Portability = 0x00000008
}
enum MVKVkSemaphoreSupportStyle : int
{
MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_SINGLE_QUEUE = 0,
MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_METAL_EVENTS_WHERE_SAFE = 1,
MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_METAL_EVENTS = 2,
MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_CALLBACK = 3,
MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_MAX_ENUM = 0x7FFFFFFF
}
readonly struct Bool32
{
uint Value { get; }
public Bool32(uint value)
{
Value = value;
}
public Bool32(bool value)
{
Value = value ? 1u : 0u;
}
public static implicit operator bool(Bool32 val) => val.Value == 1;
public static implicit operator Bool32(bool val) => new Bool32(val);
}
[StructLayout(LayoutKind.Sequential)]
struct MVKConfiguration
{
public Bool32 DebugMode;
public Bool32 ShaderConversionFlipVertexY;
public Bool32 SynchronousQueueSubmits;
public Bool32 PrefillMetalCommandBuffers;
public uint MaxActiveMetalCommandBuffersPerQueue;
public Bool32 SupportLargeQueryPools;
public Bool32 PresentWithCommandBuffer;
public Bool32 SwapchainMagFilterUseNearest;
public ulong MetalCompileTimeout;
public Bool32 PerformanceTracking;
public uint PerformanceLoggingFrameCount;
public Bool32 DisplayWatermark;
public Bool32 SpecializedQueueFamilies;
public Bool32 SwitchSystemGPU;
public Bool32 FullImageViewSwizzle;
public uint DefaultGPUCaptureScopeQueueFamilyIndex;
public uint DefaultGPUCaptureScopeQueueIndex;
public Bool32 FastMathEnabled;
public MVKConfigLogLevel LogLevel;
public MVKConfigTraceVulkanCalls TraceVulkanCalls;
public Bool32 ForceLowPowerGPU;
public Bool32 SemaphoreUseMTLFence;
public MVKVkSemaphoreSupportStyle SemaphoreSupportStyle;
public MVKConfigAutoGPUCaptureScope AutoGPUCaptureScope;
public IntPtr AutoGPUCaptureOutputFilepath;
public Bool32 Texture1DAs2D;
public Bool32 PreallocateDescriptors;
public Bool32 UseCommandPooling;
public Bool32 UseMTLHeap;
public Bool32 LogActivityPerformanceInline;
public uint ApiVersionToAdvertise;
public MVKConfigAdvertiseExtensions AdvertiseExtensions;
public Bool32 ResumeLostDevice;
public Bool32 UseMetalArgumentBuffers;
}
}

View File

@@ -0,0 +1,31 @@
using Silk.NET.Vulkan;
using System;
using System.Runtime.Versioning;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.Vulkan.MoltenVK
{
[SupportedOSPlatform("macos")]
public static partial class MVKInitialization
{
[LibraryImport("libMoltenVK.dylib")]
private static partial Result vkGetMoltenVKConfigurationMVK(IntPtr unusedInstance, out MVKConfiguration config, in IntPtr configSize);
[LibraryImport("libMoltenVK.dylib")]
private static partial Result vkSetMoltenVKConfigurationMVK(IntPtr unusedInstance, in MVKConfiguration config, in IntPtr configSize);
public static void Initialize()
{
var configSize = (IntPtr)Marshal.SizeOf<MVKConfiguration>();
vkGetMoltenVKConfigurationMVK(IntPtr.Zero, out MVKConfiguration config, configSize);
config.UseMetalArgumentBuffers = true;
config.SemaphoreSupportStyle = MVKVkSemaphoreSupportStyle.MVK_CONFIG_VK_SEMAPHORE_SUPPORT_STYLE_SINGLE_QUEUE;
config.SynchronousQueueSubmits = false;
vkSetMoltenVKConfigurationMVK(IntPtr.Zero, config, configSize);
}
}
}

View File

@@ -2,6 +2,7 @@
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
using System.Linq;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -50,6 +51,11 @@ namespace Ryujinx.Graphics.Vulkan
private Auto<DisposableRenderPass> _renderPass; private Auto<DisposableRenderPass> _renderPass;
private int _writtenAttachmentCount; private int _writtenAttachmentCount;
private bool _framebufferUsingColorWriteMask;
private ITexture[] _preMaskColors;
private ITexture _preMaskDepthStencil;
private readonly DescriptorSetUpdater _descriptorSetUpdater; private readonly DescriptorSetUpdater _descriptorSetUpdater;
private IndexBufferState _indexBuffer; private IndexBufferState _indexBuffer;
@@ -905,22 +911,35 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
SignalStateChange(); if (_framebufferUsingColorWriteMask)
if (writtenAttachments != _writtenAttachmentCount)
{ {
SignalAttachmentChange(); SetRenderTargetsInternal(_preMaskColors, _preMaskDepthStencil, true);
_writtenAttachmentCount = writtenAttachments;
} }
else
{
SignalStateChange();
if (writtenAttachments != _writtenAttachmentCount)
{
SignalAttachmentChange();
_writtenAttachmentCount = writtenAttachments;
}
}
}
private void SetRenderTargetsInternal(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked)
{
FramebufferParams?.UpdateModifications();
CreateFramebuffer(colors, depthStencil, filterWriteMasked);
CreateRenderPass();
SignalStateChange();
SignalAttachmentChange();
} }
public void SetRenderTargets(ITexture[] colors, ITexture depthStencil) public void SetRenderTargets(ITexture[] colors, ITexture depthStencil)
{ {
FramebufferParams?.UpdateModifications(); _framebufferUsingColorWriteMask = false;
CreateFramebuffer(colors, depthStencil); SetRenderTargetsInternal(colors, depthStencil, Gd.IsTBDR);
CreateRenderPass();
SignalStateChange();
SignalAttachmentChange();
} }
public void SetRenderTargetScale(float scale) public void SetRenderTargetScale(float scale)
@@ -1102,7 +1121,7 @@ namespace Ryujinx.Graphics.Vulkan
int vbSize = vertexBuffer.Buffer.Size; int vbSize = vertexBuffer.Buffer.Size;
if (Gd.Vendor == Vendor.Amd && vertexBuffer.Stride > 0) if (Gd.Vendor == Vendor.Amd && !Gd.IsMoltenVk && vertexBuffer.Stride > 0)
{ {
// AMD has a bug where if offset + stride * count is greater than // AMD has a bug where if offset + stride * count is greater than
// the size, then the last attribute will have the wrong value. // the size, then the last attribute will have the wrong value.
@@ -1119,7 +1138,8 @@ namespace Ryujinx.Graphics.Vulkan
buffer.Dispose(); buffer.Dispose();
if ((vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0) if (!Gd.Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.VertexBufferAlignment4B) &&
(vertexBuffer.Stride % FormatExtensions.MaxBufferFormatScalarSize) == 0)
{ {
buffer = new VertexBufferState( buffer = new VertexBufferState(
vb, vb,
@@ -1259,8 +1279,62 @@ namespace Ryujinx.Graphics.Vulkan
_currentPipelineHandle = 0; _currentPipelineHandle = 0;
} }
private void CreateFramebuffer(ITexture[] colors, ITexture depthStencil) private void CreateFramebuffer(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked)
{ {
if (filterWriteMasked)
{
// TBDR GPUs don't work properly if the same attachment is bound to multiple targets,
// due to each attachment being a copy of the real attachment, rather than a direct write.
// Just try to remove duplicate attachments.
// Save a copy of the array to rebind when mask changes.
void maskOut()
{
if (!_framebufferUsingColorWriteMask)
{
_preMaskColors = colors.ToArray();
_preMaskDepthStencil = depthStencil;
}
// If true, then the framebuffer must be recreated when the mask changes.
_framebufferUsingColorWriteMask = true;
}
// Look for textures that are masked out.
for (int i = 0; i < colors.Length; i++)
{
if (colors[i] == null)
{
continue;
}
ref var vkBlend = ref _newState.Internal.ColorBlendAttachmentState[i];
for (int j = 0; j < i; j++)
{
// Check each binding for a duplicate binding before it.
if (colors[i] == colors[j])
{
// Prefer the binding with no write mask.
ref var vkBlend2 = ref _newState.Internal.ColorBlendAttachmentState[j];
if (vkBlend.ColorWriteMask == 0)
{
colors[i] = null;
maskOut();
}
else if (vkBlend2.ColorWriteMask == 0)
{
colors[j] = null;
maskOut();
}
}
}
}
}
FramebufferParams = new FramebufferParams(Device, colors, depthStencil); FramebufferParams = new FramebufferParams(Device, colors, depthStencil);
UpdatePipelineAttachmentFormats(); UpdatePipelineAttachmentFormats();
} }
@@ -1270,8 +1344,7 @@ namespace Ryujinx.Graphics.Vulkan
var dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan(); var dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan();
FramebufferParams.AttachmentFormats.CopyTo(dstAttachmentFormats); FramebufferParams.AttachmentFormats.CopyTo(dstAttachmentFormats);
int maxAttachmentIndex = FramebufferParams.MaxColorAttachmentIndex + (FramebufferParams.HasDepthStencil ? 1 : 0); for (int i = FramebufferParams.AttachmentFormats.Length; i < dstAttachmentFormats.Length; i++)
for (int i = FramebufferParams.AttachmentFormats.Length; i <= maxAttachmentIndex; i++)
{ {
dstAttachmentFormats[i] = 0; dstAttachmentFormats[i] = 0;
} }

View File

@@ -79,6 +79,7 @@ namespace Ryujinx.Graphics.Vulkan
(int)FramebufferParams.Width, (int)FramebufferParams.Width,
(int)FramebufferParams.Height, (int)FramebufferParams.Height,
FramebufferParams.AttachmentFormats[index], FramebufferParams.AttachmentFormats[index],
FramebufferParams.GetAttachmentComponentType(index),
ClearScissor); ClearScissor);
} }
else else

View File

@@ -0,0 +1,9 @@
#version 450 core
layout (location = 0) in vec4 clear_colour;
layout (location = 0) out ivec4 colour;
void main()
{
colour = floatBitsToInt(clear_colour);
}

View File

@@ -0,0 +1,9 @@
#version 450 core
layout (location = 0) in vec4 clear_colour;
layout (location = 0) out uvec4 colour;
void main()
{
colour = floatBitsToUint(clear_colour);
}

View File

@@ -431,7 +431,7 @@ namespace Ryujinx.Graphics.Vulkan.Shaders
0x3C, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x3C, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
}; };
public static readonly byte[] ColorClearFragmentShaderSource = new byte[] public static readonly byte[] ColorClearFFragmentShaderSource = new byte[]
{ {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x0D, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
@@ -459,6 +459,68 @@ namespace Ryujinx.Graphics.Vulkan.Shaders
0x0C, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00, 0x0C, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
}; };
public static readonly byte[] ColorClearSIFragmentShaderSource = new byte[]
{
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30,
0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00,
0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x6C, 0x6F,
0x75, 0x72, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x65, 0x61,
0x72, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00,
0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x16, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00,
0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
};
public static readonly byte[] ColorClearUIFragmentShaderSource = new byte[]
{
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x10, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x06, 0x00,
0x01, 0x00, 0x00, 0x00, 0x47, 0x4C, 0x53, 0x4C, 0x2E, 0x73, 0x74, 0x64, 0x2E, 0x34, 0x35, 0x30,
0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00,
0x04, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00,
0xC2, 0x01, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6D, 0x61, 0x69, 0x6E,
0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, 0x63, 0x6F, 0x6C, 0x6F,
0x75, 0x72, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x63, 0x6C, 0x65, 0x61,
0x72, 0x5F, 0x63, 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x09, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00,
0x0D, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00,
0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x15, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
0x3B, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x16, 0x00, 0x03, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00,
0x0B, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x04, 0x00,
0x0C, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x36, 0x00, 0x05, 0x00,
0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0xF8, 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x04, 0x00, 0x0B, 0x00, 0x00, 0x00,
0x0E, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x03, 0x00, 0x09, 0x00, 0x00, 0x00,
0x0F, 0x00, 0x00, 0x00, 0xFD, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
};
public static readonly byte[] ColorClearVertexShaderSource = new byte[] public static readonly byte[] ColorClearVertexShaderSource = new byte[]
{ {
0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x36, 0x00, 0x00, 0x00, 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x0A, 0x00, 0x08, 0x00, 0x36, 0x00, 0x00, 0x00,

View File

@@ -106,7 +106,7 @@ namespace Ryujinx.Graphics.Vulkan
flags |= ImageCreateFlags.CreateCubeCompatibleBit; flags |= ImageCreateFlags.CreateCubeCompatibleBit;
} }
if (type == ImageType.Type3D) if (type == ImageType.Type3D && !gd.Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.No3DImageView))
{ {
flags |= ImageCreateFlags.Create2DArrayCompatibleBit; flags |= ImageCreateFlags.Create2DArrayCompatibleBit;
} }

View File

@@ -94,8 +94,14 @@ namespace Ryujinx.Graphics.Vulkan
var subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, layers); var subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, layers);
var subresourceRangeDepth = new ImageSubresourceRange(aspectFlagsDepth, (uint)firstLevel, levels, (uint)firstLayer, layers); var subresourceRangeDepth = new ImageSubresourceRange(aspectFlagsDepth, (uint)firstLevel, levels, (uint)firstLayer, layers);
unsafe Auto<DisposableImageView> CreateImageView(ComponentMapping cm, ImageSubresourceRange sr, ImageViewType viewType) unsafe Auto<DisposableImageView> CreateImageView(ComponentMapping cm, ImageSubresourceRange sr, ImageViewType viewType, ImageUsageFlags usageFlags = 0)
{ {
var usage = new ImageViewUsageCreateInfo()
{
SType = StructureType.ImageViewUsageCreateInfo,
Usage = usageFlags
};
var imageCreateInfo = new ImageViewCreateInfo() var imageCreateInfo = new ImageViewCreateInfo()
{ {
SType = StructureType.ImageViewCreateInfo, SType = StructureType.ImageViewCreateInfo,
@@ -103,7 +109,8 @@ namespace Ryujinx.Graphics.Vulkan
ViewType = viewType, ViewType = viewType,
Format = format, Format = format,
Components = cm, Components = cm,
SubresourceRange = sr SubresourceRange = sr,
PNext = usageFlags == 0 ? null : &usage
}; };
gd.Api.CreateImageView(device, imageCreateInfo, null, out var imageView).ThrowOnError(); gd.Api.CreateImageView(device, imageCreateInfo, null, out var imageView).ThrowOnError();
@@ -124,9 +131,21 @@ namespace Ryujinx.Graphics.Vulkan
// Framebuffer attachments also require 3D textures to be bound as 2D array. // Framebuffer attachments also require 3D textures to be bound as 2D array.
if (info.Target == Target.Texture3D) if (info.Target == Target.Texture3D)
{ {
subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, (uint)info.Depth); if (gd.Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.No3DImageView))
{
if (levels == 1 && (info.Format.IsRtColorCompatible() || info.Format.IsDepthOrStencil()))
{
subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, 1);
_imageView2dArray = CreateImageView(identityComponentMapping, subresourceRange, ImageViewType.Type2DArray); _imageView2dArray = CreateImageView(identityComponentMapping, subresourceRange, ImageViewType.Type2D, ImageUsageFlags.ColorAttachmentBit);
}
}
else
{
subresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, levels, (uint)firstLayer, (uint)info.Depth);
_imageView2dArray = CreateImageView(identityComponentMapping, subresourceRange, ImageViewType.Type2DArray);
}
} }
Valid = true; Valid = true;
@@ -353,7 +372,7 @@ namespace Ryujinx.Graphics.Vulkan
} }
if (VulkanConfiguration.UseSlowSafeBlitOnAmd && if (VulkanConfiguration.UseSlowSafeBlitOnAmd &&
_gd.Vendor == Vendor.Amd && (_gd.Vendor == Vendor.Amd || _gd.IsMoltenVk) &&
src.Info.Target == Target.Texture2D && src.Info.Target == Target.Texture2D &&
dst.Info.Target == Target.Texture2D && dst.Info.Target == Target.Texture2D &&
!dst.Info.Format.IsDepthOrStencil()) !dst.Info.Format.IsDepthOrStencil())

View File

@@ -5,9 +5,13 @@ namespace Ryujinx.Graphics.Vulkan
enum Vendor enum Vendor
{ {
Amd, Amd,
ImgTec,
Intel, Intel,
Nvidia, Nvidia,
ARM,
Broadcom,
Qualcomm, Qualcomm,
Apple,
Unknown Unknown
} }
@@ -21,7 +25,11 @@ namespace Ryujinx.Graphics.Vulkan
return id switch return id switch
{ {
0x1002 => Vendor.Amd, 0x1002 => Vendor.Amd,
0x1010 => Vendor.ImgTec,
0x106B => Vendor.Apple,
0x10DE => Vendor.Nvidia, 0x10DE => Vendor.Nvidia,
0x13B5 => Vendor.ARM,
0x14E4 => Vendor.Broadcom,
0x8086 => Vendor.Intel, 0x8086 => Vendor.Intel,
0x5143 => Vendor.Qualcomm, 0x5143 => Vendor.Qualcomm,
_ => Vendor.Unknown _ => Vendor.Unknown
@@ -34,8 +42,10 @@ namespace Ryujinx.Graphics.Vulkan
{ {
0x1002 => "AMD", 0x1002 => "AMD",
0x1010 => "ImgTec", 0x1010 => "ImgTec",
0x106B => "Apple",
0x10DE => "NVIDIA", 0x10DE => "NVIDIA",
0x13B5 => "ARM", 0x13B5 => "ARM",
0x14E4 => "Broadcom",
0x1AE0 => "Google", 0x1AE0 => "Google",
0x5143 => "Qualcomm", 0x5143 => "Qualcomm",
0x8086 => "Intel", 0x8086 => "Intel",

View File

@@ -82,9 +82,9 @@ namespace Ryujinx.Graphics.Vulkan
} }
_buffer = autoBuffer; _buffer = autoBuffer;
}
state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)_stride; state.Internal.VertexBindingDescriptions[DescriptorIndex].Stride = (uint)stride;
}
return; return;
} }

View File

@@ -162,7 +162,6 @@ namespace Ryujinx.Graphics.Vulkan
if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt)) if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt))
{ {
Logger.Error?.Print(LogClass.Gpu, msg); Logger.Error?.Print(LogClass.Gpu, msg);
//throw new Exception(msg);
} }
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.WarningBitExt)) else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.WarningBitExt))
{ {
@@ -379,14 +378,46 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDeviceFeatures2 SType = StructureType.PhysicalDeviceFeatures2
}; };
PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColorSupported = new PhysicalDeviceCustomBorderColorFeaturesEXT() PhysicalDeviceVulkan11Features supportedFeaturesVk11 = new PhysicalDeviceVulkan11Features()
{ {
SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt SType = StructureType.PhysicalDeviceVulkan11Features,
PNext = features2.PNext
};
features2.PNext = &supportedFeaturesVk11;
PhysicalDeviceCustomBorderColorFeaturesEXT supportedFeaturesCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT()
{
SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt,
PNext = features2.PNext
}; };
if (supportedExtensions.Contains("VK_EXT_custom_border_color")) if (supportedExtensions.Contains("VK_EXT_custom_border_color"))
{ {
features2.PNext = &featuresCustomBorderColorSupported; features2.PNext = &supportedFeaturesCustomBorderColor;
}
PhysicalDeviceTransformFeedbackFeaturesEXT supportedFeaturesTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT()
{
SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt,
PNext = features2.PNext
};
if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName))
{
features2.PNext = &supportedFeaturesTransformFeedback;
}
PhysicalDeviceRobustness2FeaturesEXT supportedFeaturesRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT()
{
SType = StructureType.PhysicalDeviceRobustness2FeaturesExt
};
if (supportedExtensions.Contains("VK_EXT_robustness2"))
{
supportedFeaturesRobustness2.PNext = features2.PNext;
features2.PNext = &supportedFeaturesRobustness2;
} }
api.GetPhysicalDeviceFeatures2(physicalDevice, &features2); api.GetPhysicalDeviceFeatures2(physicalDevice, &features2);
@@ -396,46 +427,56 @@ namespace Ryujinx.Graphics.Vulkan
var features = new PhysicalDeviceFeatures() var features = new PhysicalDeviceFeatures()
{ {
DepthBiasClamp = true, DepthBiasClamp = true,
DepthClamp = true, DepthClamp = supportedFeatures.DepthClamp,
DualSrcBlend = true, DualSrcBlend = supportedFeatures.DualSrcBlend,
FragmentStoresAndAtomics = true, FragmentStoresAndAtomics = true,
GeometryShader = supportedFeatures.GeometryShader, GeometryShader = supportedFeatures.GeometryShader,
ImageCubeArray = true, ImageCubeArray = true,
IndependentBlend = true, IndependentBlend = true,
LogicOp = supportedFeatures.LogicOp, LogicOp = supportedFeatures.LogicOp,
MultiViewport = true, MultiViewport = supportedFeatures.MultiViewport,
PipelineStatisticsQuery = supportedFeatures.PipelineStatisticsQuery, PipelineStatisticsQuery = supportedFeatures.PipelineStatisticsQuery,
SamplerAnisotropy = true, SamplerAnisotropy = true,
ShaderClipDistance = true, ShaderClipDistance = true,
ShaderFloat64 = supportedFeatures.ShaderFloat64, ShaderFloat64 = supportedFeatures.ShaderFloat64,
ShaderImageGatherExtended = true, ShaderImageGatherExtended = supportedFeatures.ShaderImageGatherExtended,
ShaderStorageImageMultisample = supportedFeatures.ShaderStorageImageMultisample, ShaderStorageImageMultisample = supportedFeatures.ShaderStorageImageMultisample,
// ShaderStorageImageReadWithoutFormat = true, // ShaderStorageImageReadWithoutFormat = true,
// ShaderStorageImageWriteWithoutFormat = true, // ShaderStorageImageWriteWithoutFormat = true,
TessellationShader = true, TessellationShader = supportedFeatures.TessellationShader,
VertexPipelineStoresAndAtomics = true, VertexPipelineStoresAndAtomics = true,
RobustBufferAccess = useRobustBufferAccess RobustBufferAccess = useRobustBufferAccess
}; };
void* pExtendedFeatures = null; void* pExtendedFeatures = null;
var featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT() PhysicalDeviceTransformFeedbackFeaturesEXT featuresTransformFeedback;
if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName))
{ {
SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt, featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT()
PNext = pExtendedFeatures, {
TransformFeedback = true SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt,
}; PNext = pExtendedFeatures,
TransformFeedback = supportedFeaturesTransformFeedback.TransformFeedback
};
pExtendedFeatures = &featuresTransformFeedback; pExtendedFeatures = &featuresTransformFeedback;
}
var featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2;
if (supportedExtensions.Contains("VK_EXT_robustness2"))
{ {
SType = StructureType.PhysicalDeviceRobustness2FeaturesExt, featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT()
PNext = pExtendedFeatures, {
NullDescriptor = true SType = StructureType.PhysicalDeviceRobustness2FeaturesExt,
}; PNext = pExtendedFeatures,
NullDescriptor = supportedFeaturesRobustness2.NullDescriptor
};
pExtendedFeatures = &featuresRobustness2; pExtendedFeatures = &featuresRobustness2;
}
var featuresExtendedDynamicState = new PhysicalDeviceExtendedDynamicStateFeaturesEXT() var featuresExtendedDynamicState = new PhysicalDeviceExtendedDynamicStateFeaturesEXT()
{ {
@@ -450,7 +491,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
SType = StructureType.PhysicalDeviceVulkan11Features, SType = StructureType.PhysicalDeviceVulkan11Features,
PNext = pExtendedFeatures, PNext = pExtendedFeatures,
ShaderDrawParameters = true ShaderDrawParameters = supportedFeaturesVk11.ShaderDrawParameters
}; };
pExtendedFeatures = &featuresVk11; pExtendedFeatures = &featuresVk11;
@@ -511,8 +552,8 @@ namespace Ryujinx.Graphics.Vulkan
PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor; PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor;
if (supportedExtensions.Contains("VK_EXT_custom_border_color") && if (supportedExtensions.Contains("VK_EXT_custom_border_color") &&
featuresCustomBorderColorSupported.CustomBorderColors && supportedFeaturesCustomBorderColor.CustomBorderColors &&
featuresCustomBorderColorSupported.CustomBorderColorWithoutFormat) supportedFeaturesCustomBorderColor.CustomBorderColorWithoutFormat)
{ {
featuresCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT() featuresCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT()
{ {

View File

@@ -3,6 +3,7 @@ using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader; using Ryujinx.Graphics.Shader;
using Ryujinx.Graphics.Shader.Translation; using Ryujinx.Graphics.Shader.Translation;
using Ryujinx.Graphics.Vulkan.MoltenVK;
using Ryujinx.Graphics.Vulkan.Queries; using Ryujinx.Graphics.Vulkan.Queries;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using Silk.NET.Vulkan.Extensions.EXT; using Silk.NET.Vulkan.Extensions.EXT;
@@ -77,6 +78,8 @@ namespace Ryujinx.Graphics.Vulkan
internal bool IsAmdWindows { get; private set; } internal bool IsAmdWindows { get; private set; }
internal bool IsIntelWindows { get; private set; } internal bool IsIntelWindows { get; private set; }
internal bool IsAmdGcn { get; private set; } internal bool IsAmdGcn { get; private set; }
internal bool IsMoltenVk { get; private set; }
internal bool IsTBDR { get; private set; }
public string GpuVendor { get; private set; } public string GpuVendor { get; private set; }
public string GpuRenderer { get; private set; } public string GpuRenderer { get; private set; }
public string GpuVersion { get; private set; } public string GpuVersion { get; private set; }
@@ -93,6 +96,14 @@ namespace Ryujinx.Graphics.Vulkan
Shaders = new HashSet<ShaderCollection>(); Shaders = new HashSet<ShaderCollection>();
Textures = new HashSet<ITexture>(); Textures = new HashSet<ITexture>();
Samplers = new HashSet<SamplerHolder>(); Samplers = new HashSet<SamplerHolder>();
if (OperatingSystem.IsMacOS())
{
MVKInitialization.Initialize();
// Any device running on MacOS is using MoltenVK, even Intel and AMD vendors.
IsMoltenVk = true;
}
} }
private unsafe void LoadFeatures(string[] supportedExtensions, uint maxQueueCount, uint queueFamilyIndex) private unsafe void LoadFeatures(string[] supportedExtensions, uint maxQueueCount, uint queueFamilyIndex)
@@ -161,7 +172,10 @@ namespace Ryujinx.Graphics.Vulkan
properties2.PNext = &propertiesTransformFeedback; properties2.PNext = &propertiesTransformFeedback;
} }
Api.GetPhysicalDeviceProperties2(_physicalDevice, &properties2); PhysicalDevicePortabilitySubsetPropertiesKHR propertiesPortabilitySubset = new PhysicalDevicePortabilitySubsetPropertiesKHR()
{
SType = StructureType.PhysicalDevicePortabilitySubsetPropertiesKhr
};
PhysicalDeviceFeatures2 features2 = new PhysicalDeviceFeatures2() PhysicalDeviceFeatures2 features2 = new PhysicalDeviceFeatures2()
{ {
@@ -183,6 +197,11 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt
}; };
PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new PhysicalDevicePortabilitySubsetFeaturesKHR()
{
SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr
};
if (supportedExtensions.Contains("VK_EXT_robustness2")) if (supportedExtensions.Contains("VK_EXT_robustness2"))
{ {
features2.PNext = &featuresRobustness2; features2.PNext = &featuresRobustness2;
@@ -200,8 +219,31 @@ namespace Ryujinx.Graphics.Vulkan
features2.PNext = &featuresCustomBorderColor; features2.PNext = &featuresCustomBorderColor;
} }
bool usePortability = supportedExtensions.Contains("VK_KHR_portability_subset");
if (usePortability)
{
propertiesPortabilitySubset.PNext = properties2.PNext;
properties2.PNext = &propertiesPortabilitySubset;
featuresPortabilitySubset.PNext = features2.PNext;
features2.PNext = &featuresPortabilitySubset;
}
Api.GetPhysicalDeviceProperties2(_physicalDevice, &properties2);
Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2); Api.GetPhysicalDeviceFeatures2(_physicalDevice, &features2);
var portabilityFlags = PortabilitySubsetFlags.None;
if (usePortability)
{
portabilityFlags |= propertiesPortabilitySubset.MinVertexInputBindingStrideAlignment > 1 ? PortabilitySubsetFlags.VertexBufferAlignment4B : 0;
portabilityFlags |= featuresPortabilitySubset.TriangleFans ? 0 : PortabilitySubsetFlags.NoTriangleFans;
portabilityFlags |= featuresPortabilitySubset.PointPolygons ? 0 : PortabilitySubsetFlags.NoPointMode;
portabilityFlags |= featuresPortabilitySubset.ImageView2DOn3DImage ? 0 : PortabilitySubsetFlags.No3DImageView;
portabilityFlags |= featuresPortabilitySubset.SamplerMipLodBias ? 0 : PortabilitySubsetFlags.NoLodBias;
}
bool customBorderColorSupported = supportedExtensions.Contains("VK_EXT_custom_border_color") && bool customBorderColorSupported = supportedExtensions.Contains("VK_EXT_custom_border_color") &&
featuresCustomBorderColor.CustomBorderColors && featuresCustomBorderColor.CustomBorderColors &&
featuresCustomBorderColor.CustomBorderColorWithoutFormat; featuresCustomBorderColor.CustomBorderColorWithoutFormat;
@@ -224,7 +266,7 @@ namespace Ryujinx.Graphics.Vulkan
supportedExtensions.Contains(ExtConditionalRendering.ExtensionName), supportedExtensions.Contains(ExtConditionalRendering.ExtensionName),
supportedExtensions.Contains(ExtExtendedDynamicState.ExtensionName), supportedExtensions.Contains(ExtExtendedDynamicState.ExtensionName),
features2.Features.MultiViewport, features2.Features.MultiViewport,
featuresRobustness2.NullDescriptor, featuresRobustness2.NullDescriptor || IsMoltenVk,
supportedExtensions.Contains(KhrPushDescriptor.ExtensionName), supportedExtensions.Contains(KhrPushDescriptor.ExtensionName),
supportsTransformFeedback, supportsTransformFeedback,
propertiesTransformFeedback.TransformFeedbackQueries, propertiesTransformFeedback.TransformFeedbackQueries,
@@ -232,7 +274,8 @@ namespace Ryujinx.Graphics.Vulkan
propertiesSubgroupSizeControl.MinSubgroupSize, propertiesSubgroupSizeControl.MinSubgroupSize,
propertiesSubgroupSizeControl.MaxSubgroupSize, propertiesSubgroupSizeControl.MaxSubgroupSize,
propertiesSubgroupSizeControl.RequiredSubgroupSizeStages, propertiesSubgroupSizeControl.RequiredSubgroupSizeStages,
supportedSampleCounts); supportedSampleCounts,
portabilityFlags);
MemoryAllocator = new MemoryAllocator(Api, _device, properties.Limits.MaxMemoryAllocationCount); MemoryAllocator = new MemoryAllocator(Api, _device, properties.Limits.MaxMemoryAllocationCount);
@@ -413,6 +456,36 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsR4G4B4A4Format = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags, bool supportsR4G4B4A4Format = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags,
GAL.Format.R4G4B4A4Unorm); GAL.Format.R4G4B4A4Unorm);
bool supportsAstcFormats = FormatCapabilities.OptimalFormatsSupport(compressedFormatFeatureFlags,
GAL.Format.Astc4x4Unorm,
GAL.Format.Astc5x4Unorm,
GAL.Format.Astc5x5Unorm,
GAL.Format.Astc6x5Unorm,
GAL.Format.Astc6x6Unorm,
GAL.Format.Astc8x5Unorm,
GAL.Format.Astc8x6Unorm,
GAL.Format.Astc8x8Unorm,
GAL.Format.Astc10x5Unorm,
GAL.Format.Astc10x6Unorm,
GAL.Format.Astc10x8Unorm,
GAL.Format.Astc10x10Unorm,
GAL.Format.Astc12x10Unorm,
GAL.Format.Astc12x12Unorm,
GAL.Format.Astc4x4Srgb,
GAL.Format.Astc5x4Srgb,
GAL.Format.Astc5x5Srgb,
GAL.Format.Astc6x5Srgb,
GAL.Format.Astc6x6Srgb,
GAL.Format.Astc8x5Srgb,
GAL.Format.Astc8x6Srgb,
GAL.Format.Astc8x8Srgb,
GAL.Format.Astc10x5Srgb,
GAL.Format.Astc10x6Srgb,
GAL.Format.Astc10x8Srgb,
GAL.Format.Astc10x10Srgb,
GAL.Format.Astc12x10Srgb,
GAL.Format.Astc12x12Srgb);
PhysicalDeviceVulkan12Features featuresVk12 = new PhysicalDeviceVulkan12Features() PhysicalDeviceVulkan12Features featuresVk12 = new PhysicalDeviceVulkan12Features()
{ {
SType = StructureType.PhysicalDeviceVulkan12Features SType = StructureType.PhysicalDeviceVulkan12Features
@@ -434,7 +507,9 @@ namespace Ryujinx.Graphics.Vulkan
GpuVendor, GpuVendor,
hasFrontFacingBug: IsIntelWindows, hasFrontFacingBug: IsIntelWindows,
hasVectorIndexingBug: Vendor == Vendor.Qualcomm, hasVectorIndexingBug: Vendor == Vendor.Qualcomm,
supportsAstcCompression: features2.Features.TextureCompressionAstcLdr, needsFragmentOutputSpecialization: IsMoltenVk,
reduceShaderPrecision: IsMoltenVk,
supportsAstcCompression: features2.Features.TextureCompressionAstcLdr && supportsAstcFormats,
supportsBc123Compression: supportsBc123CompressionFormat, supportsBc123Compression: supportsBc123CompressionFormat,
supportsBc45Compression: supportsBc45CompressionFormat, supportsBc45Compression: supportsBc45CompressionFormat,
supportsBc67Compression: supportsBc67CompressionFormat, supportsBc67Compression: supportsBc67CompressionFormat,
@@ -515,12 +590,17 @@ namespace Ryujinx.Graphics.Vulkan
IsAmdWindows = Vendor == Vendor.Amd && RuntimeInformation.IsOSPlatform(OSPlatform.Windows); IsAmdWindows = Vendor == Vendor.Amd && RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
IsIntelWindows = Vendor == Vendor.Intel && RuntimeInformation.IsOSPlatform(OSPlatform.Windows); IsIntelWindows = Vendor == Vendor.Intel && RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
IsTBDR = IsMoltenVk ||
Vendor == Vendor.Qualcomm ||
Vendor == Vendor.ARM ||
Vendor == Vendor.Broadcom ||
Vendor == Vendor.ImgTec;
GpuVendor = vendorName; GpuVendor = vendorName;
GpuRenderer = Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName); GpuRenderer = Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName);
GpuVersion = $"Vulkan v{ParseStandardVulkanVersion(properties.ApiVersion)}, Driver v{ParseDriverVersion(ref properties)}"; GpuVersion = $"Vulkan v{ParseStandardVulkanVersion(properties.ApiVersion)}, Driver v{ParseDriverVersion(ref properties)}";
IsAmdGcn = Vendor == Vendor.Amd && VendorUtils.AmdGcnRegex().IsMatch(GpuRenderer); IsAmdGcn = !IsMoltenVk && Vendor == Vendor.Amd && VendorUtils.AmdGcnRegex().IsMatch(GpuRenderer);
Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})"); Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
} }
@@ -531,6 +611,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
GAL.PrimitiveTopology.Quads => GAL.PrimitiveTopology.Triangles, GAL.PrimitiveTopology.Quads => GAL.PrimitiveTopology.Triangles,
GAL.PrimitiveTopology.QuadStrip => GAL.PrimitiveTopology.TriangleStrip, GAL.PrimitiveTopology.QuadStrip => GAL.PrimitiveTopology.TriangleStrip,
GAL.PrimitiveTopology.TriangleFan => Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.NoTriangleFans) ? GAL.PrimitiveTopology.Triangles : topology,
_ => topology _ => topology
}; };
} }
@@ -540,6 +621,7 @@ namespace Ryujinx.Graphics.Vulkan
return topology switch return topology switch
{ {
GAL.PrimitiveTopology.Quads => true, GAL.PrimitiveTopology.Quads => true,
GAL.PrimitiveTopology.TriangleFan => Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.NoTriangleFans),
_ => false _ => false
}; };
} }
@@ -553,7 +635,13 @@ namespace Ryujinx.Graphics.Vulkan
public bool NeedsVertexBufferAlignment(int attrScalarAlignment, out int alignment) public bool NeedsVertexBufferAlignment(int attrScalarAlignment, out int alignment)
{ {
if (Vendor != Vendor.Nvidia) if (Capabilities.PortabilitySubset.HasFlag(PortabilitySubsetFlags.VertexBufferAlignment4B))
{
alignment = 4;
return true;
}
else if (Vendor != Vendor.Nvidia)
{ {
// Vulkan requires that vertex attributes are globally aligned by their component size, // Vulkan requires that vertex attributes are globally aligned by their component size,
// so buffer strides that don't divide by the largest scalar element are invalid. // so buffer strides that don't divide by the largest scalar element are invalid.

View File

@@ -113,7 +113,7 @@ namespace Ryujinx.Graphics.Vulkan
ImageSharingMode = SharingMode.Exclusive, ImageSharingMode = SharingMode.Exclusive,
ImageArrayLayers = 1, ImageArrayLayers = 1,
PreTransform = capabilities.CurrentTransform, PreTransform = capabilities.CurrentTransform,
CompositeAlpha = CompositeAlphaFlagsKHR.OpaqueBitKhr, CompositeAlpha = ChooseCompositeAlpha(capabilities.SupportedCompositeAlpha),
PresentMode = ChooseSwapPresentMode(presentModes, _vsyncEnabled), PresentMode = ChooseSwapPresentMode(presentModes, _vsyncEnabled),
Clipped = true, Clipped = true,
OldSwapchain = oldSwapchain OldSwapchain = oldSwapchain
@@ -182,6 +182,22 @@ namespace Ryujinx.Graphics.Vulkan
return availableFormats[0]; return availableFormats[0];
} }
private static CompositeAlphaFlagsKHR ChooseCompositeAlpha(CompositeAlphaFlagsKHR supportedFlags)
{
if (supportedFlags.HasFlag(CompositeAlphaFlagsKHR.OpaqueBitKhr))
{
return CompositeAlphaFlagsKHR.OpaqueBitKhr;
}
else if (supportedFlags.HasFlag(CompositeAlphaFlagsKHR.PreMultipliedBitKhr))
{
return CompositeAlphaFlagsKHR.PreMultipliedBitKhr;
}
else
{
return CompositeAlphaFlagsKHR.InheritBitKhr;
}
}
private static PresentModeKHR ChooseSwapPresentMode(PresentModeKHR[] availablePresentModes, bool vsyncEnabled) private static PresentModeKHR ChooseSwapPresentMode(PresentModeKHR[] availablePresentModes, bool vsyncEnabled)
{ {
if (!vsyncEnabled && availablePresentModes.Contains(PresentModeKHR.ImmediateKhr)) if (!vsyncEnabled && availablePresentModes.Contains(PresentModeKHR.ImmediateKhr))
@@ -192,10 +208,6 @@ namespace Ryujinx.Graphics.Vulkan
{ {
return PresentModeKHR.MailboxKhr; return PresentModeKHR.MailboxKhr;
} }
else if (availablePresentModes.Contains(PresentModeKHR.FifoKhr))
{
return PresentModeKHR.FifoKhr;
}
else else
{ {
return PresentModeKHR.FifoKhr; return PresentModeKHR.FifoKhr;

View File

@@ -267,6 +267,8 @@ namespace Ryujinx.Horizon.Generators.Hipc
} }
int index = 0; int index = 0;
int inArgIndex = 0;
int outArgIndex = 0;
int inCopyHandleIndex = 0; int inCopyHandleIndex = 0;
int inMoveHandleIndex = 0; int inMoveHandleIndex = 0;
int inObjectIndex = 0; int inObjectIndex = 0;
@@ -284,7 +286,7 @@ namespace Ryujinx.Horizon.Generators.Hipc
{ {
if (IsNonSpanOutBuffer(compilation, parameter)) if (IsNonSpanOutBuffer(compilation, parameter))
{ {
generator.AppendLine($"using var {argName} = CommandSerialization.GetWritableRegion(processor.GetBufferRange({index}));"); generator.AppendLine($"using var {argName} = CommandSerialization.GetWritableRegion(processor.GetBufferRange({outArgIndex++}));");
argName = $"out {GenerateSpanCastElement0(canonicalTypeName, $"{argName}.Memory.Span")}"; argName = $"out {GenerateSpanCastElement0(canonicalTypeName, $"{argName}.Memory.Span")}";
} }
@@ -302,7 +304,7 @@ namespace Ryujinx.Horizon.Generators.Hipc
switch (argType) switch (argType)
{ {
case CommandArgType.InArgument: case CommandArgType.InArgument:
value = $"CommandSerialization.DeserializeArg<{canonicalTypeName}>(inRawData, processor.GetInArgOffset({index}))"; value = $"CommandSerialization.DeserializeArg<{canonicalTypeName}>(inRawData, processor.GetInArgOffset({inArgIndex++}))";
break; break;
case CommandArgType.InCopyHandle: case CommandArgType.InCopyHandle:
value = $"CommandSerialization.DeserializeCopyHandle(ref context, {inCopyHandleIndex++})"; value = $"CommandSerialization.DeserializeCopyHandle(ref context, {inCopyHandleIndex++})";

View File

@@ -1,6 +1,7 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Common;
using Ryujinx.Horizon.LogManager.Types;
using Ryujinx.Horizon.Sdk.Lm; using Ryujinx.Horizon.Sdk.Lm;
using Ryujinx.Horizon.Sdk.Sf; using Ryujinx.Horizon.Sdk.Sf;
using Ryujinx.Horizon.Sdk.Sf.Hipc; using Ryujinx.Horizon.Sdk.Sf.Hipc;
@@ -13,13 +14,19 @@ namespace Ryujinx.Horizon.LogManager.Ipc
{ {
partial class LmLogger : ILmLogger partial class LmLogger : ILmLogger
{ {
private const int MessageLengthLimit = 5000;
private readonly LogService _log; private readonly LogService _log;
private readonly ulong _pid; private readonly ulong _pid;
private LogPacket _logPacket;
public LmLogger(LogService log, ulong pid) public LmLogger(LogService log, ulong pid)
{ {
_log = log; _log = log;
_pid = pid; _pid = pid;
_logPacket = new LogPacket();
} }
[CmifCommand(0)] [CmifCommand(0)]
@@ -30,7 +37,12 @@ namespace Ryujinx.Horizon.LogManager.Ipc
return Result.Success; return Result.Success;
} }
Logger.Guest?.Print(LogClass.ServiceLm, LogImpl(message)); if (LogImpl(message))
{
Logger.Guest?.Print(LogClass.ServiceLm, _logPacket.ToString());
_logPacket = new LogPacket();
}
return Result.Success; return Result.Success;
} }
@@ -60,58 +72,86 @@ namespace Ryujinx.Horizon.LogManager.Ipc
return true; return true;
} }
private static string LogImpl(ReadOnlySpan<byte> message) private bool LogImpl(ReadOnlySpan<byte> message)
{ {
SpanReader reader = new(message); SpanReader reader = new(message);
LogPacketHeader header = reader.Read<LogPacketHeader>(); LogPacketHeader header = reader.Read<LogPacketHeader>();
StringBuilder builder = new();
builder.AppendLine($"Guest Log:\n Log level: {header.Severity}"); bool isHeadPacket = (header.Flags & LogPacketFlags.IsHead) != 0;
bool isTailPacket = (header.Flags & LogPacketFlags.IsTail) != 0;
_logPacket.Severity = header.Severity;
while (reader.Length > 0) while (reader.Length > 0)
{ {
int type = ReadUleb128(ref reader); int type = ReadUleb128(ref reader);
int size = ReadUleb128(ref reader); int size = ReadUleb128(ref reader);
LogDataChunkKey field = (LogDataChunkKey)type; LogDataChunkKey key = (LogDataChunkKey)type;
string fieldStr; if (key == LogDataChunkKey.Start)
if (field == LogDataChunkKey.Start)
{ {
reader.Skip(size); reader.Skip(size);
continue; continue;
} }
else if (field == LogDataChunkKey.Stop) else if (key == LogDataChunkKey.Stop)
{ {
break; break;
} }
else if (field == LogDataChunkKey.Line) else if (key == LogDataChunkKey.Line)
{ {
fieldStr = $"{field}: {reader.Read<int>()}"; _logPacket.Line = reader.Read<int>();
} }
else if (field == LogDataChunkKey.DropCount) else if (key == LogDataChunkKey.DropCount)
{ {
fieldStr = $"{field}: {reader.Read<long>()}"; _logPacket.DropCount = reader.Read<long>();
} }
else if (field == LogDataChunkKey.Time) else if (key == LogDataChunkKey.Time)
{ {
fieldStr = $"{field}: {reader.Read<long>()}s"; _logPacket.Time = reader.Read<long>();
} }
else if (field < LogDataChunkKey.Count) else if (key == LogDataChunkKey.Message)
{ {
fieldStr = $"{field}: '{Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd()}'"; string text = Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd();
}
else
{
fieldStr = $"Field{field}: '{Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd()}'";
}
builder.AppendLine($" {fieldStr}"); if (isHeadPacket && isTailPacket)
{
_logPacket.Message = text;
}
else
{
_logPacket.Message += text;
if (_logPacket.Message.Length >= MessageLengthLimit)
{
isTailPacket = true;
}
}
}
else if (key == LogDataChunkKey.Filename)
{
_logPacket.Filename = Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd();
}
else if (key == LogDataChunkKey.Function)
{
_logPacket.Function = Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd();
}
else if (key == LogDataChunkKey.Module)
{
_logPacket.Module = Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd();
}
else if (key == LogDataChunkKey.Thread)
{
_logPacket.Thread = Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd();
}
else if (key == LogDataChunkKey.ProgramName)
{
_logPacket.ProgramName = Encoding.UTF8.GetString(reader.GetSpan(size)).TrimEnd();
}
} }
return builder.ToString(); return isTailPacket;
} }
private static int ReadUleb128(ref SpanReader reader) private static int ReadUleb128(ref SpanReader reader)

View File

@@ -0,0 +1,72 @@
using Ryujinx.Horizon.Sdk.Diag;
using System.Text;
namespace Ryujinx.Horizon.LogManager.Types
{
struct LogPacket
{
public string Message;
public int Line;
public string Filename;
public string Function;
public string Module;
public string Thread;
public long DropCount;
public long Time;
public string ProgramName;
public LogSeverity Severity;
public override string ToString()
{
StringBuilder builder = new();
builder.AppendLine($"Guest Log:\n Log level: {Severity}");
if (Time > 0)
{
builder.AppendLine($" Time: {Time}s");
}
if (DropCount > 0)
{
builder.AppendLine($" DropCount: {DropCount}");
}
if (!string.IsNullOrEmpty(ProgramName))
{
builder.AppendLine($" ProgramName: {ProgramName}");
}
if (!string.IsNullOrEmpty(Module))
{
builder.AppendLine($" Module: {Module}");
}
if (!string.IsNullOrEmpty(Thread))
{
builder.AppendLine($" Thread: {Thread}");
}
if (!string.IsNullOrEmpty(Filename))
{
builder.AppendLine($" Filename: {Filename}");
}
if (Line > 0)
{
builder.AppendLine($" Line: {Line}");
}
if (!string.IsNullOrEmpty(Function))
{
builder.AppendLine($" Function: {Function}");
}
if (!string.IsNullOrEmpty(Message))
{
builder.AppendLine($" Message: {Message}");
}
return builder.ToString();
}
}
}

View File

@@ -42,7 +42,7 @@ namespace Ryujinx.Horizon.Prepo.Ipc
return PrepoResult.PermissionDenied; return PrepoResult.PermissionDenied;
} }
ProcessPlayReport(PlayReportKind.Normal, pid, gameRoomBuffer, reportBuffer, Uid.Null); ProcessPlayReport(PlayReportKind.Normal, gameRoomBuffer, reportBuffer, pid, Uid.Null);
return Result.Success; return Result.Success;
} }
@@ -57,7 +57,7 @@ namespace Ryujinx.Horizon.Prepo.Ipc
return PrepoResult.PermissionDenied; return PrepoResult.PermissionDenied;
} }
ProcessPlayReport(PlayReportKind.Normal, pid, gameRoomBuffer, reportBuffer, userId, true); ProcessPlayReport(PlayReportKind.Normal, gameRoomBuffer, reportBuffer, pid, userId, true);
return Result.Success; return Result.Success;
} }
@@ -107,25 +107,25 @@ namespace Ryujinx.Horizon.Prepo.Ipc
} }
[CmifCommand(20100)] [CmifCommand(20100)]
public Result SaveSystemReport([Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan<byte> gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> reportBuffer, [ClientProcessId] ulong pid) public Result SaveSystemReport([Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan<byte> gameRoomBuffer, Sdk.Ncm.ApplicationId applicationId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> reportBuffer)
{ {
if ((_permissionLevel & PrepoServicePermissionLevel.System) != 0) if ((_permissionLevel & PrepoServicePermissionLevel.System) != 0)
{ {
return PrepoResult.PermissionDenied; return PrepoResult.PermissionDenied;
} }
return ProcessPlayReport(PlayReportKind.System, pid, gameRoomBuffer, reportBuffer, Uid.Null); return ProcessPlayReport(PlayReportKind.System, gameRoomBuffer, reportBuffer, 0, Uid.Null, false, applicationId);
} }
[CmifCommand(20101)] [CmifCommand(20101)]
public Result SaveSystemReportWithUser(Uid userId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan<byte> gameRoomBuffer, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> reportBuffer, [ClientProcessId] ulong pid) public Result SaveSystemReportWithUser(Uid userId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.Pointer)] ReadOnlySpan<byte> gameRoomBuffer, Sdk.Ncm.ApplicationId applicationId, [Buffer(HipcBufferFlags.In | HipcBufferFlags.MapAlias)] ReadOnlySpan<byte> reportBuffer)
{ {
if ((_permissionLevel & PrepoServicePermissionLevel.System) != 0) if ((_permissionLevel & PrepoServicePermissionLevel.System) != 0)
{ {
return PrepoResult.PermissionDenied; return PrepoResult.PermissionDenied;
} }
return ProcessPlayReport(PlayReportKind.System, pid, gameRoomBuffer, reportBuffer, userId, true); return ProcessPlayReport(PlayReportKind.System, gameRoomBuffer, reportBuffer, 0, userId, true, applicationId);
} }
[CmifCommand(40100)] // 2.0.0+ [CmifCommand(40100)] // 2.0.0+
@@ -164,7 +164,7 @@ namespace Ryujinx.Horizon.Prepo.Ipc
return PrepoResult.PermissionDenied; return PrepoResult.PermissionDenied;
} }
private static Result ProcessPlayReport(PlayReportKind playReportKind, ulong pid, ReadOnlySpan<byte> gameRoomBuffer, ReadOnlySpan<byte> reportBuffer, Uid userId, bool withUserId = false) private static Result ProcessPlayReport(PlayReportKind playReportKind, ReadOnlySpan<byte> gameRoomBuffer, ReadOnlySpan<byte> reportBuffer, ulong pid, Uid userId, bool withUserId = false, Sdk.Ncm.ApplicationId applicationId = default)
{ {
if (withUserId) if (withUserId)
{ {
@@ -191,16 +191,23 @@ namespace Ryujinx.Horizon.Prepo.Ipc
return PrepoResult.InvalidBufferSize; return PrepoResult.InvalidBufferSize;
} }
// NOTE: The service calls arp:r using the pid to get the application id, if it fails PrepoResult.InvalidPid is returned.
// Reports are stored internally and an event is signaled to transmit them.
StringBuilder builder = new(); StringBuilder builder = new();
MessagePackObject deserializedReport = MessagePackSerializer.UnpackMessagePackObject(reportBuffer.ToArray()); MessagePackObject deserializedReport = MessagePackSerializer.UnpackMessagePackObject(reportBuffer.ToArray());
builder.AppendLine(); builder.AppendLine();
builder.AppendLine("PlayReport log:"); builder.AppendLine("PlayReport log:");
builder.AppendLine($" Kind: {playReportKind}"); builder.AppendLine($" Kind: {playReportKind}");
builder.AppendLine($" Pid: {pid}");
// NOTE: The service calls arp:r using the pid to get the application id, if it fails PrepoResult.InvalidPid is returned.
// Reports are stored internally and an event is signaled to transmit them.
if (pid != 0)
{
builder.AppendLine($" Pid: {pid}");
}
else
{
builder.AppendLine($" ApplicationId: {applicationId}");
}
if (!userId.IsNull) if (!userId.IsNull)
{ {

View File

@@ -6,7 +6,7 @@ using System.Runtime.InteropServices;
namespace Ryujinx.Horizon.Sdk.Account namespace Ryujinx.Horizon.Sdk.Account
{ {
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public readonly record struct Uid readonly record struct Uid
{ {
public readonly long High; public readonly long High;
public readonly long Low; public readonly long Low;

View File

@@ -0,0 +1,52 @@
namespace Ryujinx.Horizon.Sdk.Ncm
{
readonly struct ApplicationId
{
public readonly ulong Id;
public static int Length => sizeof(ulong);
public static ApplicationId First => new(0x0100000000010000);
public static ApplicationId Last => new(0x01FFFFFFFFFFFFFF);
public static ApplicationId Invalid => new(0);
public bool IsValid => Id >= First.Id && Id <= Last.Id;
public ApplicationId(ulong id)
{
Id = id;
}
public override bool Equals(object obj)
{
return obj is ApplicationId applicationId && applicationId.Equals(this);
}
public bool Equals(ApplicationId other)
{
return other.Id == Id;
}
public override int GetHashCode()
{
return Id.GetHashCode();
}
public static bool operator ==(ApplicationId lhs, ApplicationId rhs)
{
return lhs.Equals(rhs);
}
public static bool operator !=(ApplicationId lhs, ApplicationId rhs)
{
return !lhs.Equals(rhs);
}
public override string ToString()
{
return $"0x{Id:x}";
}
}
}

View File

@@ -12,8 +12,8 @@ namespace Ryujinx.Horizon.Sdk.Prepo
Result RequestImmediateTransmission(); Result RequestImmediateTransmission();
Result GetTransmissionStatus(out int status); Result GetTransmissionStatus(out int status);
Result GetSystemSessionId(out ulong systemSessionId); Result GetSystemSessionId(out ulong systemSessionId);
Result SaveSystemReport(ReadOnlySpan<byte> gameRoomBuffer, ReadOnlySpan<byte> reportBuffer, ulong pid); Result SaveSystemReport(ReadOnlySpan<byte> gameRoomBuffer, Ncm.ApplicationId applicationId, ReadOnlySpan<byte> reportBuffer);
Result SaveSystemReportWithUser(Uid userId, ReadOnlySpan<byte> gameRoomBuffer, ReadOnlySpan<byte> reportBuffer, ulong pid); Result SaveSystemReportWithUser(Uid userId, ReadOnlySpan<byte> gameRoomBuffer, Ncm.ApplicationId applicationId, ReadOnlySpan<byte> reportBuffer);
Result IsUserAgreementCheckEnabled(out bool enabled); Result IsUserAgreementCheckEnabled(out bool enabled);
Result SetUserAgreementCheckEnabled(bool enabled); Result SetUserAgreementCheckEnabled(bool enabled);
} }

View File

@@ -8,7 +8,7 @@ namespace Ryujinx.Horizon.Sdk.Sm
{ {
public static ServiceName Invalid { get; } = new ServiceName(0); public static ServiceName Invalid { get; } = new ServiceName(0);
public bool IsInvalid => Packed == 0; public bool IsValid => Packed != 0;
public int Length => sizeof(ulong); public int Length => sizeof(ulong);