Compare commits

..

9 Commits

Author SHA1 Message Date
gdkchan
296c4a3d01 Relax Vulkan requirements (#4282)
* Relax Vulkan requirements

* Fix MaxColorAttachmentIndex

* Fix ColorBlendAttachmentStateCount value mismatch for background pipelines

* Change query capability check to check for pipeline statistics query rather than geometry shader support
2023-01-26 18:34:35 -03:00
riperiperi
e7cf4e6eaf Vulkan: Reset queries on same command buffer (#4329)
* Reset queries on same command buffer

Vulkan seems to complain when the queries are reset on another command buffer. No idea why, the spec really could be written better in this regard. This fixes complaints, and hopefully any implementations that care extensively about them.

This change _guesses_ how many queries need to be reset and resets as many as possible at the same time to avoid splitting render passes. If it resets too many queries, we didn't waste too much time - if it runs out of resets it will batch reset 10 more.

The number of queries reset is the maximum number of queries in the last 3 frames. This has been worked into the AutoFlushCounter so that it only resets up to 32 if it is yet to force a command buffer submission in this attachment.

This is only done for samples passed queries right now, as they have by far the most resets.

* Address Feedback
2023-01-24 13:32:56 -03:00
gdkchan
a1a4771ac1 Remove use of GetFunctionPointerForDelegate to get JIT cache function pointer (#4337)
* Remove use of GetFunctionPointerForDelegate to get JIT cache function pointer

* Rename FuncPtr to FuncPointer
2023-01-23 22:37:53 +00:00
gdkchan
2fd819613f SPIR-V: Change BitfieldExtract and BitfieldInsert for SPIRV-Cross (#4336)
* SPIR-V: Change BitfieldExtract and BitfieldInsert types to make Metal MSL compiler happy

* Shader cache version bump
2023-01-23 19:20:40 -03:00
TSRBerry
ad6ff6ce99 GUI: Add option to register file types (#4250)
* Add FileAssociationHelper.cs

* Add register file types option to gtk

* Add register file types option to avalonia

* Add Windows support to FileAssociationHelper.cs

* linux: Add uninstall support for file types

* Ignore .glade~ backup files

* Rename Register/Unregister methods

* gtk: Add manage file types submenu

* ava: Add manage file types submenu

* windows: Add uninstall support for file types

* Don't invert uninstall condition (formatting change)

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

* Add IsTypesRegisteredWindows & Fix Windows install function

* Add AreMimeTypesRegisteredLinux()

* Fix wrong indention

Co-authored-by: AcK77 <acoustik666@gmail.com>
Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2023-01-22 17:39:00 +00:00
gnisman
dc30d94852 Handle parsing of corrupt Config.json and prevent crash on launch (#4309)
* Handle parsing of corrupt Config.json and prevent crash on launch

* Implement a cleaner solution to handle empty json object
2023-01-22 17:04:33 +01:00
merry
4f293f8cbe Arm64: Simplify TryEncodeBitMask and use for constants (#4328)
* Arm64: Simplify TryEncodeBitMask

* CodeGenerator: Use TryEncodeBitMask in GenerateConstantCopy

* Ptc: Bump version
2023-01-22 14:15:49 +00:00
merry
32a1cd83fd AvaloniaKeyboardDriver: Swallow TextInput events to avoid bell (#4320) 2023-01-22 11:21:52 +01:00
riperiperi
e3d0ccf8d5 Allow setting texture data from 1x to fix some textures resetting randomly (#2860)
* Allow setting texture data from 1x to fix some textures resetting randomly

Expected targets:

- Deltarune 1+2
- Crash Team Racing
- Those new pokemon games idk

* Allow scaling of MSAA textures, propagate scale on copy.

* Fix Rebase

Oops

* Automatic disable

* A bit more aggressive

* Without the debug log

* Actually decrement the score when writing.
2023-01-22 02:03:30 +00:00
42 changed files with 949 additions and 555 deletions

3
.gitignore vendored
View File

@@ -170,3 +170,6 @@ launchSettings.json
# NetCore Publishing Profiles # NetCore Publishing Profiles
PublishProfiles/ PublishProfiles/
# Glade backup files
*.glade~

View File

@@ -16,4 +16,10 @@
</ContentWithTargetPath> </ContentWithTargetPath>
</ItemGroup> </ItemGroup>
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>Ryujinx.Tests</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
</Project> </Project>

View File

@@ -1,5 +1,4 @@
using ARMeilleure.IntermediateRepresentation; using ARMeilleure.IntermediateRepresentation;
using System;
using System.Numerics; using System.Numerics;
namespace ARMeilleure.CodeGen.Arm64 namespace ARMeilleure.CodeGen.Arm64
@@ -32,9 +31,12 @@ namespace ARMeilleure.CodeGen.Arm64
public static bool TryEncodeBitMask(Operand operand, out int immN, out int immS, out int immR) public static bool TryEncodeBitMask(Operand operand, out int immN, out int immS, out int immR)
{ {
ulong value = operand.Value; return TryEncodeBitMask(operand.Type, operand.Value, out immN, out immS, out immR);
}
if (operand.Type == OperandType.I32) public static bool TryEncodeBitMask(OperandType type, ulong value, out int immN, out int immS, out int immR)
{
if (type == OperandType.I32)
{ {
value |= value << 32; value |= value << 32;
} }
@@ -50,7 +52,7 @@ namespace ARMeilleure.CodeGen.Arm64
// Any value AND all ones will be equal itself, so it's effectively a no-op. // Any value AND all ones will be equal itself, so it's effectively a no-op.
// Any value OR all ones will be equal all ones, so one can just use MOV. // Any value OR all ones will be equal all ones, so one can just use MOV.
// Any value XOR all ones will be equal its inverse, so one can just use MVN. // Any value XOR all ones will be equal its inverse, so one can just use MVN.
if (value == ulong.MaxValue) if (value == 0 || value == ulong.MaxValue)
{ {
immN = 0; immN = 0;
immS = 0; immS = 0;
@@ -59,79 +61,18 @@ namespace ARMeilleure.CodeGen.Arm64
return false; return false;
} }
int bitLength = CountSequence(value); // Normalize value, rotating it such that the LSB is 1: Ensures we get a complete element that has not
// been cut-in-half across the word boundary.
int rotation = BitOperations.TrailingZeroCount(value & (value + 1));
ulong rotatedValue = ulong.RotateRight(value, rotation);
if ((value >> bitLength) != 0) // Now that we have a complete element in the LSB with the LSB = 1, determine size and number of ones
{ // in element.
bitLength += CountSequence(value >> bitLength); int elementSize = BitOperations.TrailingZeroCount(rotatedValue & (rotatedValue + 1));
} int onesInElement = BitOperations.TrailingZeroCount(~rotatedValue);
int bitLengthLog2 = BitOperations.Log2((uint)bitLength); // Check the value is repeating; also ensures element size is a power of two.
int bitLengthPow2 = 1 << bitLengthLog2; if (ulong.RotateRight(value, elementSize) != value)
if (bitLengthPow2 < bitLength)
{
bitLengthLog2++;
bitLengthPow2 <<= 1;
}
int selectedESize = 64;
int repetitions = 1;
int onesCount = BitOperations.PopCount(value);
if (bitLengthPow2 < 64 && (value >> bitLengthPow2) != 0)
{
for (int eSizeLog2 = bitLengthLog2; eSizeLog2 < 6; eSizeLog2++)
{
bool match = true;
int eSize = 1 << eSizeLog2;
ulong mask = (1UL << eSize) - 1;
ulong eValue = value & mask;
for (int e = 1; e < 64 / eSize; e++)
{
if (((value >> (e * eSize)) & mask) != eValue)
{
match = false;
break;
}
}
if (match)
{
selectedESize = eSize;
repetitions = 64 / eSize;
onesCount = BitOperations.PopCount(eValue);
break;
}
}
}
// Find rotation. We have two cases, one where the highest bit is 0
// and one where it is 1.
// If it's 1, we just need to count the number of 1 bits on the MSB to find the right rotation.
// If it's 0, we just need to count the number of 0 bits on the LSB to find the left rotation,
// then we can convert it to the right rotation shift by subtracting the value from the element size.
int rotation;
long vHigh = (long)(value << (64 - selectedESize));
if (vHigh < 0)
{
rotation = BitOperations.LeadingZeroCount(~(ulong)vHigh);
}
else
{
rotation = (selectedESize - BitOperations.TrailingZeroCount(value)) & (selectedESize - 1);
}
// Reconstruct value and see if it matches. If not, we can't encode.
ulong reconstructed = onesCount == 64 ? ulong.MaxValue : RotateRight((1UL << onesCount) - 1, rotation, selectedESize);
for (int bit = 32; bit >= selectedESize; bit >>= 1)
{
reconstructed |= reconstructed << bit;
}
if (reconstructed != value || onesCount == 0)
{ {
immN = 0; immN = 0;
immS = 0; immS = 0;
@@ -140,34 +81,11 @@ namespace ARMeilleure.CodeGen.Arm64
return false; return false;
} }
immR = rotation; immN = (elementSize >> 6) & 1;
immS = (((~elementSize + 1) << 1) | (onesInElement - 1)) & 0x3f;
// immN indicates that there are no repetitions. immR = (elementSize - rotation) & (elementSize - 1);
// The MSB of immS indicates the amount of repetitions, and the LSB the number of bits set.
if (repetitions == 1)
{
immN = 1;
immS = 0;
}
else
{
immN = 0;
immS = (0xf80 >> BitOperations.Log2((uint)repetitions)) & 0x3f;
}
immS |= onesCount - 1;
return true; return true;
} }
private static int CountSequence(ulong value)
{
return BitOperations.TrailingZeroCount(value) + BitOperations.TrailingZeroCount(~value);
}
private static ulong RotateRight(ulong bits, int shift, int size)
{
return (bits >> shift) | ((bits << (size - shift)) & (size == 64 ? ulong.MaxValue : (1UL << size) - 1));
}
} }
} }

View File

@@ -1303,7 +1303,15 @@ namespace ARMeilleure.CodeGen.Arm64
private static void GenerateConstantCopy(CodeGenContext context, Operand dest, ulong value) private static void GenerateConstantCopy(CodeGenContext context, Operand dest, ulong value)
{ {
if (value != 0) if (value == 0)
{
context.Assembler.Mov(dest, Register(ZrRegister, dest.Type));
}
else if (CodeGenCommon.TryEncodeBitMask(dest.Type, value, out _, out _, out _))
{
context.Assembler.Orr(dest, Register(ZrRegister, dest.Type), Const(dest.Type, (long)value));
}
else
{ {
int hw = 0; int hw = 0;
bool first = true; bool first = true;
@@ -1328,10 +1336,6 @@ namespace ARMeilleure.CodeGen.Arm64
value >>= 16; value >>= 16;
} }
} }
else
{
context.Assembler.Mov(dest, Register(ZrRegister, dest.Type));
}
} }
private static void GenerateAtomicCas( private static void GenerateAtomicCas(

View File

@@ -48,9 +48,21 @@ namespace ARMeilleure.CodeGen
/// <returns>A delegate of type <typeparamref name="T"/> pointing to the mapped function</returns> /// <returns>A delegate of type <typeparamref name="T"/> pointing to the mapped function</returns>
public T Map<T>() public T Map<T>()
{ {
IntPtr codePtr = JitCache.Map(this); return MapWithPointer<T>(out _);
}
return Marshal.GetDelegateForFunctionPointer<T>(codePtr); /// <summary>
/// Maps the <see cref="CompiledFunction"/> onto the <see cref="JitCache"/> and returns a delegate of type
/// <typeparamref name="T"/> pointing to the mapped function.
/// </summary>
/// <typeparam name="T">Type of delegate</typeparam>
/// <param name="codePointer">Pointer to the function code in memory</param>
/// <returns>A delegate of type <typeparamref name="T"/> pointing to the mapped function</returns>
public T MapWithPointer<T>(out IntPtr codePointer)
{
codePointer = JitCache.Map(this);
return Marshal.GetDelegateForFunctionPointer<T>(codePointer);
} }
} }
} }

View File

@@ -191,7 +191,7 @@ namespace ARMeilleure.Instructions
{ {
TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode); TranslatedFunction function = Context.Translator.GetOrTranslate(address, GetContext().ExecutionMode);
return (ulong)function.FuncPtr.ToInt64(); return (ulong)function.FuncPointer.ToInt64();
} }
public static void InvalidateCacheLine(ulong address) public static void InvalidateCacheLine(ulong address)

View File

@@ -29,7 +29,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 4272; //! To be incremented manually for each change to the ARMeilleure project. private const uint InternalVersion = 4328; //! 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";
@@ -745,9 +745,9 @@ namespace ARMeilleure.Translation.PTC
bool highCq) bool highCq)
{ {
var cFunc = new CompiledFunction(code, unwindInfo, RelocInfo.Empty); var cFunc = new CompiledFunction(code, unwindInfo, RelocInfo.Empty);
var gFunc = cFunc.Map<GuestFunction>(); var gFunc = cFunc.MapWithPointer<GuestFunction>(out IntPtr gFuncPointer);
return new TranslatedFunction(gFunc, callCounter, guestSize, highCq); return new TranslatedFunction(gFunc, gFuncPointer, callCounter, guestSize, highCq);
} }
private void UpdateInfo(InfoEntry infoEntry) private void UpdateInfo(InfoEntry infoEntry)

View File

@@ -1,6 +1,5 @@
using ARMeilleure.Common; using ARMeilleure.Common;
using System; using System;
using System.Runtime.InteropServices;
namespace ARMeilleure.Translation namespace ARMeilleure.Translation
{ {
@@ -8,18 +7,18 @@ namespace ARMeilleure.Translation
{ {
private readonly GuestFunction _func; // Ensure that this delegate will not be garbage collected. private readonly GuestFunction _func; // Ensure that this delegate will not be garbage collected.
public IntPtr FuncPointer { get; }
public Counter<uint> CallCounter { get; } public Counter<uint> CallCounter { get; }
public ulong GuestSize { get; } public ulong GuestSize { get; }
public bool HighCq { get; } public bool HighCq { get; }
public IntPtr FuncPtr { get; }
public TranslatedFunction(GuestFunction func, Counter<uint> callCounter, ulong guestSize, bool highCq) public TranslatedFunction(GuestFunction func, IntPtr funcPointer, Counter<uint> callCounter, ulong guestSize, bool highCq)
{ {
_func = func; _func = func;
FuncPointer = funcPointer;
CallCounter = callCounter; CallCounter = callCounter;
GuestSize = guestSize; GuestSize = guestSize;
HighCq = highCq; HighCq = highCq;
FuncPtr = Marshal.GetFunctionPointerForDelegate(func);
} }
public ulong Execute(State.ExecutionContext context) public ulong Execute(State.ExecutionContext context)

View File

@@ -211,7 +211,7 @@ namespace ARMeilleure.Translation
if (oldFunc != func) if (oldFunc != func)
{ {
JitCache.Unmap(func.FuncPtr); JitCache.Unmap(func.FuncPointer);
func = oldFunc; func = oldFunc;
} }
@@ -230,7 +230,7 @@ namespace ARMeilleure.Translation
{ {
if (FunctionTable.IsValid(guestAddress) && (Optimizations.AllowLcqInFunctionTable || func.HighCq)) if (FunctionTable.IsValid(guestAddress) && (Optimizations.AllowLcqInFunctionTable || func.HighCq))
{ {
Volatile.Write(ref FunctionTable.GetValue(guestAddress), (ulong)func.FuncPtr); Volatile.Write(ref FunctionTable.GetValue(guestAddress), (ulong)func.FuncPointer);
} }
} }
@@ -292,11 +292,11 @@ namespace ARMeilleure.Translation
_ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc); _ptc.WriteCompiledFunction(address, funcSize, hash, highCq, compiledFunc);
} }
GuestFunction func = compiledFunc.Map<GuestFunction>(); GuestFunction func = compiledFunc.MapWithPointer<GuestFunction>(out IntPtr funcPointer);
Allocators.ResetAll(); Allocators.ResetAll();
return new TranslatedFunction(func, counter, funcSize, highCq); return new TranslatedFunction(func, funcPointer, counter, funcSize, highCq);
} }
private void BackgroundTranslate() private void BackgroundTranslate()
@@ -537,7 +537,7 @@ namespace ARMeilleure.Translation
foreach (var func in functions) foreach (var func in functions)
{ {
JitCache.Unmap(func.FuncPtr); JitCache.Unmap(func.FuncPointer);
func.CallCounter?.Dispose(); func.CallCounter?.Dispose();
} }
@@ -546,7 +546,7 @@ namespace ARMeilleure.Translation
while (_oldFuncs.TryDequeue(out var kv)) while (_oldFuncs.TryDequeue(out var kv))
{ {
JitCache.Unmap(kv.Value.FuncPtr); JitCache.Unmap(kv.Value.FuncPointer);
kv.Value.CallCounter?.Dispose(); kv.Value.CallCounter?.Dispose();
} }

View File

@@ -26,6 +26,9 @@
"MenuBarToolsInstallFirmware": "Install Firmware", "MenuBarToolsInstallFirmware": "Install Firmware",
"MenuBarFileToolsInstallFirmwareFromFile": "Install a firmware from XCI or ZIP", "MenuBarFileToolsInstallFirmwareFromFile": "Install a firmware from XCI or ZIP",
"MenuBarFileToolsInstallFirmwareFromDirectory": "Install a firmware from a directory", "MenuBarFileToolsInstallFirmwareFromDirectory": "Install a firmware from a directory",
"MenuBarToolsManageFileTypes": "Manage file types",
"MenuBarToolsInstallFileTypes": "Install file types",
"MenuBarToolsUninstallFileTypes": "Uninstall file types",
"MenuBarHelp": "Help", "MenuBarHelp": "Help",
"MenuBarHelpCheckForUpdates": "Check for Updates", "MenuBarHelpCheckForUpdates": "Check for Updates",
"MenuBarHelpAbout": "About", "MenuBarHelpAbout": "About",
@@ -339,6 +342,10 @@
"DialogFirmwareInstallEmbeddedSuccessMessage": "No installed firmware was found but Ryujinx was able to install firmware {0} from the provided game.\\nThe emulator will now start.", "DialogFirmwareInstallEmbeddedSuccessMessage": "No installed firmware was found but Ryujinx was able to install firmware {0} from the provided game.\\nThe emulator will now start.",
"DialogFirmwareNoFirmwareInstalledMessage": "No Firmware Installed", "DialogFirmwareNoFirmwareInstalledMessage": "No Firmware Installed",
"DialogFirmwareInstalledMessage": "Firmware {0} was installed", "DialogFirmwareInstalledMessage": "Firmware {0} was installed",
"DialogInstallFileTypesSuccessMessage": "Successfully installed file types!",
"DialogInstallFileTypesErrorMessage": "Failed to install file types.",
"DialogUninstallFileTypesSuccessMessage": "Successfully uninstalled file types!",
"DialogUninstallFileTypesErrorMessage": "Failed to uninstall file types.",
"DialogOpenSettingsWindowLabel": "Open Settings Window", "DialogOpenSettingsWindowLabel": "Open Settings Window",
"DialogControllerAppletTitle": "Controller Applet", "DialogControllerAppletTitle": "Controller Applet",
"DialogMessageDialogErrorExceptionMessage": "Error displaying Message Dialog: {0}", "DialogMessageDialogErrorExceptionMessage": "Error displaying Message Dialog: {0}",

View File

@@ -1,5 +1,6 @@
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Interactivity;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Input; using Ryujinx.Input;
using System; using System;
@@ -30,6 +31,7 @@ namespace Ryujinx.Ava.Input
_control.KeyDown += OnKeyPress; _control.KeyDown += OnKeyPress;
_control.KeyUp += OnKeyRelease; _control.KeyUp += OnKeyRelease;
_control.TextInput += Control_TextInput; _control.TextInput += Control_TextInput;
_control.AddHandler(InputElement.TextInputEvent, Control_LastChanceTextInput, RoutingStrategies.Bubble);
} }
private void Control_TextInput(object sender, TextInputEventArgs e) private void Control_TextInput(object sender, TextInputEventArgs e)
@@ -37,6 +39,12 @@ namespace Ryujinx.Ava.Input
TextInput?.Invoke(this, e.Text); TextInput?.Invoke(this, e.Text);
} }
private void Control_LastChanceTextInput(object sender, TextInputEventArgs e)
{
// Swallow event
e.Handled = true;
}
public event Action<string> OnGamepadConnected public event Action<string> OnGamepadConnected
{ {
add { } add { }

View File

@@ -14,10 +14,8 @@ using Ryujinx.Ui.Common;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Helper; using Ryujinx.Ui.Common.Helper;
using System; using System;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ryujinx.Ava namespace Ryujinx.Ava
@@ -35,48 +33,6 @@ namespace Ryujinx.Ava
private const uint MB_ICONWARNING = 0x30; private const uint MB_ICONWARNING = 0x30;
[SupportedOSPlatform("linux")]
static void RegisterMimeTypes()
{
if (ReleaseInformation.IsFlatHubBuild())
{
return;
}
string mimeDbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "mime");
if (!File.Exists(Path.Combine(mimeDbPath, "packages", "Ryujinx.xml")))
{
string mimeTypesFile = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "mime", "Ryujinx.xml");
using Process mimeProcess = new();
mimeProcess.StartInfo.FileName = "xdg-mime";
mimeProcess.StartInfo.Arguments = $"install --novendor --mode user {mimeTypesFile}";
mimeProcess.Start();
mimeProcess.WaitForExit();
if (mimeProcess.ExitCode != 0)
{
Logger.Error?.PrintMsg(LogClass.Application, $"Unable to install mime types. Make sure xdg-utils is installed. Process exited with code: {mimeProcess.ExitCode}");
return;
}
using Process updateMimeProcess = new();
updateMimeProcess.StartInfo.FileName = "update-mime-database";
updateMimeProcess.StartInfo.Arguments = mimeDbPath;
updateMimeProcess.Start();
updateMimeProcess.WaitForExit();
if (updateMimeProcess.ExitCode != 0)
{
Logger.Error?.PrintMsg(LogClass.Application, $"Could not update local mime database. Process exited with code: {updateMimeProcess.ExitCode}");
}
}
}
public static void Main(string[] args) public static void Main(string[] args)
{ {
Version = ReleaseInformation.GetVersion(); Version = ReleaseInformation.GetVersion();
@@ -139,12 +95,6 @@ namespace Ryujinx.Ava
// Initialize the logger system. // Initialize the logger system.
LoggerModule.Initialize(); LoggerModule.Initialize();
// Register mime types on linux.
if (OperatingSystem.IsLinux())
{
RegisterMimeTypes();
}
// Initialize Discord integration. // Initialize Discord integration.
DiscordIntegrationModule.Initialize(); DiscordIntegrationModule.Initialize();

View File

@@ -683,6 +683,11 @@ namespace Ryujinx.Ava.UI.ViewModels
get => ConsoleHelper.SetConsoleWindowStateSupported; get => ConsoleHelper.SetConsoleWindowStateSupported;
} }
public bool ManageFileTypesVisible
{
get => FileAssociationHelper.IsTypeAssociationSupported;
}
public ObservableCollection<ApplicationData> Applications public ObservableCollection<ApplicationData> Applications
{ {
get => _applications; get => _applications;

View File

@@ -77,8 +77,7 @@
</MenuItem.Icon> </MenuItem.Icon>
</MenuItem> </MenuItem>
<Separator /> <Separator />
<MenuItem Name="ChangeLanguageMenuItem" Header="{locale:Locale MenuBarOptionsChangeLanguage}"> <MenuItem Name="ChangeLanguageMenuItem" Header="{locale:Locale MenuBarOptionsChangeLanguage}" />
</MenuItem>
<Separator /> <Separator />
<MenuItem <MenuItem
Click="OpenSettings" Click="OpenSettings"
@@ -141,6 +140,10 @@
<MenuItem Command="{ReflectionBinding InstallFirmwareFromFile}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromFile}" /> <MenuItem Command="{ReflectionBinding InstallFirmwareFromFile}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromFile}" />
<MenuItem Command="{ReflectionBinding InstallFirmwareFromFolder}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromDirectory}" /> <MenuItem Command="{ReflectionBinding InstallFirmwareFromFolder}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromDirectory}" />
</MenuItem> </MenuItem>
<MenuItem Header="{locale:Locale MenuBarToolsManageFileTypes}" IsVisible="{Binding ManageFileTypesVisible}">
<MenuItem Header="{locale:Locale MenuBarToolsInstallFileTypes}" Click="InstallFileTypes_Click"/>
<MenuItem Header="{locale:Locale MenuBarToolsUninstallFileTypes}" Click="UninstallFileTypes_Click"/>
</MenuItem>
</MenuItem> </MenuItem>
<MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarHelp}"> <MenuItem VerticalAlignment="Center" Header="{locale:Locale MenuBarHelp}">
<MenuItem <MenuItem

View File

@@ -3,6 +3,7 @@ using Avalonia.Controls;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using LibHac.FsSystem; using LibHac.FsSystem;
using LibHac.Ncm; using LibHac.Ncm;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers; 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;
@@ -10,6 +11,7 @@ using Ryujinx.Common;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
using Ryujinx.Modules; using Ryujinx.Modules;
using Ryujinx.Ui.Common.Helper;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@@ -163,6 +165,32 @@ namespace Ryujinx.Ava.UI.Views.Main
} }
} }
private async void InstallFileTypes_Click(object sender, RoutedEventArgs e)
{
if (FileAssociationHelper.Install())
{
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesSuccessMessage],
string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
}
else
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogInstallFileTypesErrorMessage]);
}
}
private async void UninstallFileTypes_Click(object sender, RoutedEventArgs e)
{
if (FileAssociationHelper.Uninstall())
{
await ContentDialogHelper.CreateInfoDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesSuccessMessage],
string.Empty, LocaleManager.Instance[LocaleKeys.InputDialogOk], string.Empty, string.Empty);
}
else
{
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance[LocaleKeys.DialogUninstallFileTypesErrorMessage]);
}
}
public async void CheckForUpdates(object sender, RoutedEventArgs e) public async void CheckForUpdates(object sender, RoutedEventArgs e)
{ {
if (Updater.CanUpdate(true)) if (Updater.CanUpdate(true))

View File

@@ -349,6 +349,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
return; return;
} }
if (srcTexture.Info.Samples > 1 || dstTexture.Info.Samples > 1)
{
srcTexture.PropagateScale(dstTexture);
}
float scale = srcTexture.ScaleFactor; float scale = srcTexture.ScaleFactor;
float dstScale = dstTexture.ScaleFactor; float dstScale = dstTexture.ScaleFactor;

View File

@@ -25,6 +25,12 @@ namespace Ryujinx.Graphics.Gpu.Image
// This method uses much more memory so we want to avoid it if possible. // This method uses much more memory so we want to avoid it if possible.
private const int ByteComparisonSwitchThreshold = 4; private const int ByteComparisonSwitchThreshold = 4;
// Tuning for blacklisting textures from scaling when their data is updated from CPU.
// Each write adds the weight, each GPU modification subtracts 1.
// Exceeding the threshold blacklists the texture.
private const int ScaledSetWeight = 10;
private const int ScaledSetThreshold = 30;
private const int MinLevelsForForceAnisotropy = 5; private const int MinLevelsForForceAnisotropy = 5;
private struct TexturePoolOwner private struct TexturePoolOwner
@@ -122,6 +128,8 @@ namespace Ryujinx.Graphics.Gpu.Image
private Target _arrayViewTarget; private Target _arrayViewTarget;
private ITexture _flushHostTexture; private ITexture _flushHostTexture;
private ITexture _setHostTexture;
private int _scaledSetScore;
private Texture _viewStorage; private Texture _viewStorage;
@@ -518,6 +526,25 @@ namespace Ryujinx.Graphics.Gpu.Image
} }
} }
/// <summary>
/// Registers when a texture has had its data set after being scaled, and
/// determines if it should be blacklisted from scaling to improve performance.
/// </summary>
/// <returns>True if setting data for a scaled texture is allowed, false if the texture has been blacklisted</returns>
private bool AllowScaledSetData()
{
_scaledSetScore += ScaledSetWeight;
if (_scaledSetScore >= ScaledSetThreshold)
{
BlacklistScale();
return false;
}
return true;
}
/// <summary> /// <summary>
/// Blacklists this texture from being scaled. Resets its scale to 1 if needed. /// Blacklists this texture from being scaled. Resets its scale to 1 if needed.
/// </summary> /// </summary>
@@ -554,9 +581,10 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Copy the host texture to a scaled one. If a texture is not provided, create it with the given scale. /// Copy the host texture to a scaled one. If a texture is not provided, create it with the given scale.
/// </summary> /// </summary>
/// <param name="scale">Scale factor</param> /// <param name="scale">Scale factor</param>
/// <param name="copy">True if the data should be copied to the texture, false otherwise</param>
/// <param name="storage">Texture to use instead of creating one</param> /// <param name="storage">Texture to use instead of creating one</param>
/// <returns>A host texture containing a scaled version of this texture</returns> /// <returns>A host texture containing a scaled version of this texture</returns>
private ITexture GetScaledHostTexture(float scale, ITexture storage = null) private ITexture GetScaledHostTexture(float scale, bool copy, ITexture storage = null)
{ {
if (storage == null) if (storage == null)
{ {
@@ -564,7 +592,10 @@ namespace Ryujinx.Graphics.Gpu.Image
storage = _context.Renderer.CreateTexture(createInfo, scale); storage = _context.Renderer.CreateTexture(createInfo, scale);
} }
if (copy)
{
HostTexture.CopyTo(storage, new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), new Extents2D(0, 0, storage.Width, storage.Height), true); HostTexture.CopyTo(storage, new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), new Extents2D(0, 0, storage.Width, storage.Height), true);
}
return storage; return storage;
} }
@@ -595,7 +626,7 @@ namespace Ryujinx.Graphics.Gpu.Image
ScaleFactor = scale; ScaleFactor = scale;
ITexture newStorage = GetScaledHostTexture(ScaleFactor); ITexture newStorage = GetScaledHostTexture(ScaleFactor, true);
Logger.Debug?.Print(LogClass.Gpu, $" Copy performed: {HostTexture.Width}x{HostTexture.Height} to {newStorage.Width}x{newStorage.Height}"); Logger.Debug?.Print(LogClass.Gpu, $" Copy performed: {HostTexture.Width}x{HostTexture.Height} to {newStorage.Width}x{newStorage.Height}");
@@ -692,11 +723,6 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
public void SynchronizeFull() public void SynchronizeFull()
{ {
if (_hasData)
{
BlacklistScale();
}
ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Range); ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Range);
// If the host does not support ASTC compression, we need to do the decompression. // If the host does not support ASTC compression, we need to do the decompression.
@@ -723,7 +749,19 @@ namespace Ryujinx.Graphics.Gpu.Image
SpanOrArray<byte> result = ConvertToHostCompatibleFormat(data); SpanOrArray<byte> result = ConvertToHostCompatibleFormat(data);
if (ScaleFactor != 1f && AllowScaledSetData())
{
// If needed, create a texture to load from 1x scale.
ITexture texture = _setHostTexture = GetScaledHostTexture(1f, false, _setHostTexture);
texture.SetData(result);
texture.CopyTo(HostTexture, new Extents2D(0, 0, texture.Width, texture.Height), new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), true);
}
else
{
HostTexture.SetData(result); HostTexture.SetData(result);
}
_hasData = true; _hasData = true;
} }
@@ -1056,7 +1094,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (ScaleFactor != 1f) if (ScaleFactor != 1f)
{ {
// If needed, create a texture to flush back to host at 1x scale. // If needed, create a texture to flush back to host at 1x scale.
texture = _flushHostTexture = GetScaledHostTexture(1f, _flushHostTexture); texture = _flushHostTexture = GetScaledHostTexture(1f, true, _flushHostTexture);
} }
return texture; return texture;
@@ -1456,6 +1494,8 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary> /// </summary>
public void SignalModified() public void SignalModified()
{ {
_scaledSetScore = Math.Max(0, _scaledSetScore - 1);
if (_modifiedStale || Group.HasCopyDependencies) if (_modifiedStale || Group.HasCopyDependencies)
{ {
_modifiedStale = false; _modifiedStale = false;
@@ -1472,6 +1512,11 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="bound">True if the texture has been bound, false if it has been unbound</param> /// <param name="bound">True if the texture has been bound, false if it has been unbound</param>
public void SignalModifying(bool bound) public void SignalModifying(bool bound)
{ {
if (bound)
{
_scaledSetScore = Math.Max(0, _scaledSetScore - 1);
}
if (_modifiedStale || Group.HasCopyDependencies) if (_modifiedStale || Group.HasCopyDependencies)
{ {
_modifiedStale = false; _modifiedStale = false;
@@ -1685,6 +1730,9 @@ namespace Ryujinx.Graphics.Gpu.Image
_flushHostTexture?.Release(); _flushHostTexture?.Release();
_flushHostTexture = null; _flushHostTexture = null;
_setHostTexture?.Release();
_setHostTexture = null;
} }
/// <summary> /// <summary>

View File

@@ -112,7 +112,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>True if eligible</returns> /// <returns>True if eligible</returns>
private static TextureScaleMode IsUpscaleCompatible(TextureInfo info, bool withUpscale) private static TextureScaleMode IsUpscaleCompatible(TextureInfo info, bool withUpscale)
{ {
if ((info.Target == Target.Texture2D || info.Target == Target.Texture2DArray) && !info.FormatInfo.IsCompressed) if ((info.Target == Target.Texture2D || info.Target == Target.Texture2DArray || info.Target == Target.Texture2DMultisample) && !info.FormatInfo.IsCompressed)
{ {
return UpscaleSafeMode(info) ? (withUpscale ? TextureScaleMode.Scaled : TextureScaleMode.Eligible) : TextureScaleMode.Undesired; return UpscaleSafeMode(info) ? (withUpscale ? TextureScaleMode.Scaled : TextureScaleMode.Eligible) : TextureScaleMode.Undesired;
} }

View File

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

View File

@@ -261,17 +261,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
private static OperationResult GenerateBitfieldExtractS32(CodeGenContext context, AstOperation operation) private static OperationResult GenerateBitfieldExtractS32(CodeGenContext context, AstOperation operation)
{ {
return GenerateTernaryS32(context, operation, context.Delegates.BitFieldSExtract); return GenerateBitfieldExtractS32(context, operation, context.Delegates.BitFieldSExtract);
} }
private static OperationResult GenerateBitfieldExtractU32(CodeGenContext context, AstOperation operation) private static OperationResult GenerateBitfieldExtractU32(CodeGenContext context, AstOperation operation)
{ {
return GenerateTernaryS32(context, operation, context.Delegates.BitFieldUExtract); return GenerateTernaryU32(context, operation, context.Delegates.BitFieldUExtract);
} }
private static OperationResult GenerateBitfieldInsert(CodeGenContext context, AstOperation operation) private static OperationResult GenerateBitfieldInsert(CodeGenContext context, AstOperation operation)
{ {
return GenerateQuaternaryS32(context, operation, context.Delegates.BitFieldInsert); return GenerateBitfieldInsert(context, operation, context.Delegates.BitFieldInsert);
} }
private static OperationResult GenerateBitfieldReverse(CodeGenContext context, AstOperation operation) private static OperationResult GenerateBitfieldReverse(CodeGenContext context, AstOperation operation)
@@ -2290,22 +2290,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
} }
} }
private static OperationResult GenerateTernaryS32(
CodeGenContext context,
AstOperation operation,
Func<SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction> emitS)
{
var src1 = operation.GetSource(0);
var src2 = operation.GetSource(1);
var src3 = operation.GetSource(2);
return new OperationResult(AggregateType.S32, emitS(
context.TypeS32(),
context.GetS32(src1),
context.GetS32(src2),
context.GetS32(src3)));
}
private static OperationResult GenerateTernaryU32( private static OperationResult GenerateTernaryU32(
CodeGenContext context, CodeGenContext context,
AstOperation operation, AstOperation operation,
@@ -2322,7 +2306,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
context.GetU32(src3))); context.GetU32(src3)));
} }
private static OperationResult GenerateQuaternaryS32( private static OperationResult GenerateBitfieldExtractS32(
CodeGenContext context,
AstOperation operation,
Func<SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction> emitS)
{
var src1 = operation.GetSource(0);
var src2 = operation.GetSource(1);
var src3 = operation.GetSource(2);
return new OperationResult(AggregateType.S32, emitS(
context.TypeS32(),
context.GetS32(src1),
context.GetU32(src2),
context.GetU32(src3)));
}
private static OperationResult GenerateBitfieldInsert(
CodeGenContext context, CodeGenContext context,
AstOperation operation, AstOperation operation,
Func<SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction> emitS) Func<SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction> emitS)
@@ -2332,12 +2332,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var src3 = operation.GetSource(2); var src3 = operation.GetSource(2);
var src4 = operation.GetSource(3); var src4 = operation.GetSource(3);
return new OperationResult(AggregateType.S32, emitS( return new OperationResult(AggregateType.U32, emitS(
context.TypeS32(), context.TypeU32(),
context.GetS32(src1), context.GetU32(src1),
context.GetS32(src2), context.GetU32(src2),
context.GetS32(src3), context.GetU32(src3),
context.GetS32(src4))); context.GetU32(src4)));
} }
} }
} }

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
@@ -16,6 +17,10 @@ namespace Ryujinx.Graphics.Vulkan
private bool _hasPendingQuery; private bool _hasPendingQuery;
private int _queryCount; private int _queryCount;
private int[] _queryCountHistory = new int[3];
private int _queryCountHistoryIndex;
private int _remainingQueries;
public void RegisterFlush(ulong drawCount) public void RegisterFlush(ulong drawCount)
{ {
_lastFlush = Stopwatch.GetTimestamp(); _lastFlush = Stopwatch.GetTimestamp();
@@ -27,6 +32,9 @@ namespace Ryujinx.Graphics.Vulkan
public bool RegisterPendingQuery() public bool RegisterPendingQuery()
{ {
_hasPendingQuery = true; _hasPendingQuery = true;
_remainingQueries--;
_queryCountHistory[_queryCountHistoryIndex]++;
// Interrupt render passes to flush queries, so that early results arrive sooner. // Interrupt render passes to flush queries, so that early results arrive sooner.
if (++_queryCount == InitialQueryCountForFlush) if (++_queryCount == InitialQueryCountForFlush)
@@ -37,6 +45,21 @@ namespace Ryujinx.Graphics.Vulkan
return false; return false;
} }
public int GetRemainingQueries()
{
if (_remainingQueries <= 0)
{
_remainingQueries = 16;
}
if (_queryCount < InitialQueryCountForFlush)
{
return Math.Min(InitialQueryCountForFlush - _queryCount, _remainingQueries);
}
return _remainingQueries;
}
public bool ShouldFlushQuery() public bool ShouldFlushQuery()
{ {
return _hasPendingQuery; return _hasPendingQuery;
@@ -69,5 +92,14 @@ namespace Ryujinx.Graphics.Vulkan
return now > _lastFlush + flushTimeout; return now > _lastFlush + flushTimeout;
} }
public void Present()
{
_queryCountHistoryIndex = (_queryCountHistoryIndex + 1) % 3;
_remainingQueries = _queryCountHistory.Max() + 10;
_queryCountHistory[_queryCountHistoryIndex] = 0;
}
} }
} }

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);
@@ -66,8 +66,7 @@ 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[colorsCount];
MaxColorAttachmentIndex = colors.Length - 1;
uint width = uint.MaxValue; uint width = uint.MaxValue;
uint height = uint.MaxValue; uint height = uint.MaxValue;

View File

@@ -28,10 +28,11 @@ namespace Ryujinx.Graphics.Vulkan
public readonly bool SupportsExtendedDynamicState; public readonly bool SupportsExtendedDynamicState;
public readonly bool SupportsMultiView; public readonly bool SupportsMultiView;
public readonly bool SupportsNullDescriptors; public readonly bool SupportsNullDescriptors;
public readonly bool SupportsPreciseOcclusionQueries;
public readonly bool SupportsPushDescriptors; public readonly bool SupportsPushDescriptors;
public readonly bool SupportsTransformFeedback; public readonly bool SupportsTransformFeedback;
public readonly bool SupportsTransformFeedbackQueries; public readonly bool SupportsTransformFeedbackQueries;
public readonly bool SupportsPreciseOcclusionQueries;
public readonly bool SupportsPipelineStatisticsQuery;
public readonly bool SupportsGeometryShader; public readonly bool SupportsGeometryShader;
public readonly uint MinSubgroupSize; public readonly uint MinSubgroupSize;
public readonly uint MaxSubgroupSize; public readonly uint MaxSubgroupSize;
@@ -55,6 +56,7 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsTransformFeedback, bool supportsTransformFeedback,
bool supportsTransformFeedbackQueries, bool supportsTransformFeedbackQueries,
bool supportsPreciseOcclusionQueries, bool supportsPreciseOcclusionQueries,
bool supportsPipelineStatisticsQuery,
bool supportsGeometryShader, bool supportsGeometryShader,
uint minSubgroupSize, uint minSubgroupSize,
uint maxSubgroupSize, uint maxSubgroupSize,
@@ -77,6 +79,7 @@ namespace Ryujinx.Graphics.Vulkan
SupportsTransformFeedback = supportsTransformFeedback; SupportsTransformFeedback = supportsTransformFeedback;
SupportsTransformFeedbackQueries = supportsTransformFeedbackQueries; SupportsTransformFeedbackQueries = supportsTransformFeedbackQueries;
SupportsPreciseOcclusionQueries = supportsPreciseOcclusionQueries; SupportsPreciseOcclusionQueries = supportsPreciseOcclusionQueries;
SupportsPipelineStatisticsQuery = supportsPipelineStatisticsQuery;
SupportsGeometryShader = supportsGeometryShader; SupportsGeometryShader = supportsGeometryShader;
MinSubgroupSize = minSubgroupSize; MinSubgroupSize = minSubgroupSize;
MaxSubgroupSize = maxSubgroupSize; MaxSubgroupSize = maxSubgroupSize;

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)
{
if (type.PropertyFlags.HasFlag(flags))
{ {
return i; return i;
} }
else if (type.PropertyFlags.HasFlag(alternativeFlags))
{
bestCandidateIndex = i;
}
}
} }
return -1; return bestCandidateIndex;
} }
public void Dispose() public void Dispose()

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Vulkan
protected readonly Device Device; protected readonly Device Device;
public readonly PipelineCache PipelineCache; public readonly PipelineCache PipelineCache;
protected readonly AutoFlushCounter AutoFlush; public readonly AutoFlushCounter AutoFlush;
protected PipelineDynamicState DynamicState; protected PipelineDynamicState DynamicState;
private PipelineState _newState; private PipelineState _newState;
@@ -1344,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;
} }
@@ -1376,8 +1375,6 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < FramebufferParams.AttachmentsCount; i++) for (int i = 0; i < FramebufferParams.AttachmentsCount; i++)
{ {
int bindIndex = FramebufferParams.AttachmentIndices[i];
attachmentDescs[i] = new AttachmentDescription( attachmentDescs[i] = new AttachmentDescription(
0, 0,
FramebufferParams.AttachmentFormats[i], FramebufferParams.AttachmentFormats[i],

View File

@@ -27,14 +27,11 @@ namespace Ryujinx.Graphics.Vulkan
int attachmentCount = 0; int attachmentCount = 0;
int colorCount = 0; int colorCount = 0;
int maxColorAttachmentIndex = 0;
for (int i = 0; i < state.AttachmentEnable.Length; i++) for (int i = 0; i < state.AttachmentEnable.Length; i++)
{ {
if (state.AttachmentEnable[i]) if (state.AttachmentEnable[i])
{ {
maxColorAttachmentIndex = i;
attachmentFormats[attachmentCount] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]); attachmentFormats[attachmentCount] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]);
attachmentIndices[attachmentCount++] = i; attachmentIndices[attachmentCount++] = i;
@@ -270,7 +267,7 @@ namespace Ryujinx.Graphics.Vulkan
// NOTE: Viewports, Scissors are dynamic. // NOTE: Viewports, Scissors are dynamic.
for (int i = 0; i < 8; i++) for (int i = 0; i < Constants.MaxRenderTargets; i++)
{ {
var blend = state.BlendDescriptors[i]; var blend = state.BlendDescriptors[i];
@@ -293,21 +290,24 @@ namespace Ryujinx.Graphics.Vulkan
} }
} }
int maxAttachmentIndex = 0; int attachmentCount = 0;
for (int i = 0; i < 8; i++) int maxColorAttachmentIndex = -1;
for (int i = 0; i < Constants.MaxRenderTargets; i++)
{ {
if (state.AttachmentEnable[i]) if (state.AttachmentEnable[i])
{ {
pipeline.Internal.AttachmentFormats[maxAttachmentIndex++] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]); pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]);
maxColorAttachmentIndex = i;
} }
} }
if (state.DepthStencilEnable) if (state.DepthStencilEnable)
{ {
pipeline.Internal.AttachmentFormats[maxAttachmentIndex++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat); pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.DepthStencilFormat);
} }
pipeline.ColorBlendAttachmentStateCount = 8; pipeline.ColorBlendAttachmentStateCount = (uint)(maxColorAttachmentIndex + 1);
pipeline.VertexAttributeDescriptionsCount = (uint)Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount); pipeline.VertexAttributeDescriptionsCount = (uint)Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount);
return pipeline; return pipeline;

View File

@@ -14,7 +14,6 @@ namespace Ryujinx.Graphics.Vulkan
private CounterQueueEvent _activeConditionalRender; private CounterQueueEvent _activeConditionalRender;
private readonly List<BufferedQuery> _pendingQueryCopies; private readonly List<BufferedQuery> _pendingQueryCopies;
private readonly List<BufferedQuery> _pendingQueryResets;
private ulong _byteWeight; private ulong _byteWeight;
@@ -22,7 +21,6 @@ namespace Ryujinx.Graphics.Vulkan
{ {
_activeQueries = new List<QueryPool>(); _activeQueries = new List<QueryPool>();
_pendingQueryCopies = new(); _pendingQueryCopies = new();
_pendingQueryResets = new List<BufferedQuery>();
CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer; CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
} }
@@ -34,16 +32,6 @@ namespace Ryujinx.Graphics.Vulkan
query.PoolCopy(Cbs); query.PoolCopy(Cbs);
} }
lock (_pendingQueryResets)
{
foreach (var query in _pendingQueryResets)
{
query.PoolReset(CommandBuffer);
}
_pendingQueryResets.Clear();
}
_pendingQueryCopies.Clear(); _pendingQueryCopies.Clear();
} }
@@ -238,10 +226,12 @@ namespace Ryujinx.Graphics.Vulkan
Gd.Api.CmdBeginQuery(CommandBuffer, queryPool, 0, Gd.Capabilities.SupportsPreciseOcclusionQueries ? QueryControlFlags.PreciseBit : 0); Gd.Api.CmdBeginQuery(CommandBuffer, queryPool, 0, Gd.Capabilities.SupportsPreciseOcclusionQueries ? QueryControlFlags.PreciseBit : 0);
} }
Gd.ResetCounterPool();
Restore(); Restore();
} }
public void BeginQuery(BufferedQuery query, QueryPool pool, bool needsReset) public void BeginQuery(BufferedQuery query, QueryPool pool, bool needsReset, bool fromSamplePool)
{ {
if (needsReset) if (needsReset)
{ {
@@ -249,9 +239,11 @@ namespace Ryujinx.Graphics.Vulkan
Gd.Api.CmdResetQueryPool(CommandBuffer, pool, 0, 1); Gd.Api.CmdResetQueryPool(CommandBuffer, pool, 0, 1);
lock (_pendingQueryResets) if (fromSamplePool)
{ {
_pendingQueryResets.Remove(query); // Might be present on here. // Try reset some additional queries in advance.
Gd.ResetFutureCounters(CommandBuffer, AutoFlush.GetRemainingQueries());
} }
} }
@@ -267,14 +259,6 @@ namespace Ryujinx.Graphics.Vulkan
_activeQueries.Remove(pool); _activeQueries.Remove(pool);
} }
public void ResetQuery(BufferedQuery query)
{
lock (_pendingQueryResets)
{
_pendingQueryResets.Add(query);
}
}
public void CopyQueryResults(BufferedQuery query) public void CopyQueryResults(BufferedQuery query)
{ {
_pendingQueryCopies.Add(query); _pendingQueryCopies.Add(query);

View File

@@ -18,7 +18,6 @@ namespace Ryujinx.Graphics.Vulkan.Queries
private readonly PipelineFull _pipeline; private readonly PipelineFull _pipeline;
private QueryPool _queryPool; private QueryPool _queryPool;
private bool _isReset;
private readonly BufferHolder _buffer; private readonly BufferHolder _buffer;
private readonly IntPtr _bufferMap; private readonly IntPtr _bufferMap;
@@ -27,6 +26,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
private bool _isSupported; private bool _isSupported;
private long _defaultValue; private long _defaultValue;
private int? _resetSequence;
public unsafe BufferedQuery(VulkanRenderer gd, Device device, PipelineFull pipeline, CounterType type, bool result32Bit) public unsafe BufferedQuery(VulkanRenderer gd, Device device, PipelineFull pipeline, CounterType type, bool result32Bit)
{ {
@@ -67,8 +67,8 @@ namespace Ryujinx.Graphics.Vulkan.Queries
return type switch return type switch
{ {
CounterType.SamplesPassed => true, CounterType.SamplesPassed => true,
CounterType.PrimitivesGenerated => gd.Capabilities.SupportsPipelineStatisticsQuery,
CounterType.TransformFeedbackPrimitivesWritten => gd.Capabilities.SupportsTransformFeedbackQueries, CounterType.TransformFeedbackPrimitivesWritten => gd.Capabilities.SupportsTransformFeedbackQueries,
CounterType.PrimitivesGenerated => gd.Capabilities.SupportsGeometryShader,
_ => false _ => false
}; };
} }
@@ -92,16 +92,17 @@ namespace Ryujinx.Graphics.Vulkan.Queries
public void Reset() public void Reset()
{ {
End(false); End(false);
Begin(); Begin(null);
} }
public void Begin() public void Begin(int? resetSequence)
{ {
if (_isSupported) if (_isSupported)
{ {
_pipeline.BeginQuery(this, _queryPool, !_isReset); bool needsReset = resetSequence == null || _resetSequence == null || resetSequence.Value != _resetSequence.Value;
_pipeline.BeginQuery(this, _queryPool, needsReset, _type == CounterType.SamplesPassed && resetSequence != null);
} }
_isReset = false; _resetSequence = null;
} }
public unsafe void End(bool withResult) public unsafe void End(bool withResult)
@@ -162,13 +163,14 @@ namespace Ryujinx.Graphics.Vulkan.Queries
return data; return data;
} }
public void PoolReset(CommandBuffer cmd) public void PoolReset(CommandBuffer cmd, int resetSequence)
{ {
if (_isSupported) if (_isSupported)
{ {
_api.CmdResetQueryPool(cmd, _queryPool, 0, 1); _api.CmdResetQueryPool(cmd, _queryPool, 0, 1);
} }
_isReset = true;
_resetSequence = resetSequence;
} }
public void PoolCopy(CommandBufferScoped cbs) public void PoolCopy(CommandBufferScoped cbs)

View File

@@ -3,6 +3,7 @@ using Silk.NET.Vulkan;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Linq;
namespace Ryujinx.Graphics.Vulkan.Queries namespace Ryujinx.Graphics.Vulkan.Queries
{ {
@@ -32,6 +33,8 @@ namespace Ryujinx.Graphics.Vulkan.Queries
private Thread _consumerThread; private Thread _consumerThread;
public int ResetSequence { get; private set; }
internal CounterQueue(VulkanRenderer gd, Device device, PipelineFull pipeline, CounterType type) internal CounterQueue(VulkanRenderer gd, Device device, PipelineFull pipeline, CounterType type)
{ {
_gd = gd; _gd = gd;
@@ -53,6 +56,24 @@ namespace Ryujinx.Graphics.Vulkan.Queries
_consumerThread.Start(); _consumerThread.Start();
} }
public void ResetCounterPool()
{
ResetSequence++;
}
public void ResetFutureCounters(CommandBuffer cmd, int count)
{
// Pre-emptively reset queries to avoid render pass splitting.
lock (_queryPool)
{
count = Math.Min(count, _queryPool.Count);
for (int i = 0; i < count; i++)
{
_queryPool.ElementAt(i).PoolReset(cmd, ResetSequence);
}
}
}
private void EventConsumer() private void EventConsumer()
{ {
while (!Disposed) while (!Disposed)
@@ -106,7 +127,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
{ {
lock (_lock) lock (_lock)
{ {
_pipeline.ResetQuery(query); // The query will be reset when it dequeues.
_queryPool.Enqueue(query); _queryPool.Enqueue(query);
} }
} }

View File

@@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
DrawIndex = drawIndex; DrawIndex = drawIndex;
_counter.Begin(); _counter.Begin(_queue.ResetSequence);
} }
public Auto<DisposableBuffer> GetBuffer() public Auto<DisposableBuffer> GetBuffer()

View File

@@ -24,6 +24,19 @@ namespace Ryujinx.Graphics.Vulkan.Queries
} }
} }
public void ResetCounterPool()
{
foreach (var queue in _counterQueues)
{
queue.ResetCounterPool();
}
}
public void ResetFutureCounters(CommandBuffer cmd, int count)
{
_counterQueues[(int)CounterType.SamplesPassed].ResetFutureCounters(cmd, count);
}
public CounterQueueEvent QueueReport(CounterType type, EventHandler<ulong> resultHandler, bool hostReserved) public CounterQueueEvent QueueReport(CounterType type, EventHandler<ulong> resultHandler, bool hostReserved)
{ {
return _counterQueues[(int)type].QueueReport(resultHandler, _pipeline.DrawCount, hostReserved); return _counterQueues[(int)type].QueueReport(resultHandler, _pipeline.DrawCount, hostReserved);

View File

@@ -9,6 +9,7 @@ namespace Ryujinx.Graphics.Vulkan
Intel, Intel,
Nvidia, Nvidia,
ARM, ARM,
Broadcom,
Qualcomm, Qualcomm,
Apple, Apple,
Unknown Unknown
@@ -28,6 +29,7 @@ namespace Ryujinx.Graphics.Vulkan
0x106B => Vendor.Apple, 0x106B => Vendor.Apple,
0x10DE => Vendor.Nvidia, 0x10DE => Vendor.Nvidia,
0x13B5 => Vendor.ARM, 0x13B5 => Vendor.ARM,
0x14E4 => Vendor.Broadcom,
0x8086 => Vendor.Intel, 0x8086 => Vendor.Intel,
0x5143 => Vendor.Qualcomm, 0x5143 => Vendor.Qualcomm,
_ => Vendor.Unknown _ => Vendor.Unknown
@@ -43,6 +45,7 @@ namespace Ryujinx.Graphics.Vulkan
0x106B => "Apple", 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

@@ -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,34 @@ 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() PhysicalDeviceRobustness2FeaturesEXT supportedFeaturesRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT()
@@ -408,42 +427,49 @@ 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,
OcclusionQueryPrecise = supportedFeatures.OcclusionQueryPrecise, OcclusionQueryPrecise = supportedFeatures.OcclusionQueryPrecise,
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))
{
featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT()
{ {
SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt, SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt,
PNext = pExtendedFeatures, PNext = pExtendedFeatures,
TransformFeedback = true TransformFeedback = supportedFeaturesTransformFeedback.TransformFeedback
}; };
pExtendedFeatures = &featuresTransformFeedback; pExtendedFeatures = &featuresTransformFeedback;
}
PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2;
if (supportedExtensions.Contains("VK_EXT_robustness2")) if (supportedExtensions.Contains("VK_EXT_robustness2"))
{ {
var featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT() featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT()
{ {
SType = StructureType.PhysicalDeviceRobustness2FeaturesExt, SType = StructureType.PhysicalDeviceRobustness2FeaturesExt,
PNext = pExtendedFeatures, PNext = pExtendedFeatures,
@@ -466,7 +492,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
SType = StructureType.PhysicalDeviceVulkan11Features, SType = StructureType.PhysicalDeviceVulkan11Features,
PNext = pExtendedFeatures, PNext = pExtendedFeatures,
ShaderDrawParameters = true ShaderDrawParameters = supportedFeaturesVk11.ShaderDrawParameters
}; };
pExtendedFeatures = &featuresVk11; pExtendedFeatures = &featuresVk11;
@@ -527,8 +553,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

@@ -271,6 +271,7 @@ namespace Ryujinx.Graphics.Vulkan
supportsTransformFeedback, supportsTransformFeedback,
propertiesTransformFeedback.TransformFeedbackQueries, propertiesTransformFeedback.TransformFeedbackQueries,
features2.Features.OcclusionQueryPrecise, features2.Features.OcclusionQueryPrecise,
supportedFeatures.PipelineStatisticsQuery,
supportedFeatures.GeometryShader, supportedFeatures.GeometryShader,
propertiesSubgroupSizeControl.MinSubgroupSize, propertiesSubgroupSizeControl.MinSubgroupSize,
propertiesSubgroupSizeControl.MaxSubgroupSize, propertiesSubgroupSizeControl.MaxSubgroupSize,
@@ -589,9 +590,13 @@ namespace Ryujinx.Graphics.Vulkan
Vendor = VendorUtils.FromId(properties.VendorID); Vendor = VendorUtils.FromId(properties.VendorID);
IsAmdWindows = Vendor == Vendor.Amd && RuntimeInformation.IsOSPlatform(OSPlatform.Windows); IsAmdWindows = Vendor == Vendor.Amd && OperatingSystem.IsWindows();
IsIntelWindows = Vendor == Vendor.Intel && RuntimeInformation.IsOSPlatform(OSPlatform.Windows); IsIntelWindows = Vendor == Vendor.Intel && OperatingSystem.IsWindows();
IsTBDR = IsMoltenVk || Vendor == Vendor.Qualcomm || Vendor == Vendor.ARM || Vendor == Vendor.ImgTec; 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);
@@ -679,6 +684,16 @@ namespace Ryujinx.Graphics.Vulkan
_counters.Update(); _counters.Update();
} }
public void ResetCounterPool()
{
_counters.ResetCounterPool();
}
public void ResetFutureCounters(CommandBuffer cmd, int count)
{
_counters?.ResetFutureCounters(cmd, count);
}
public void BackgroundContextAction(Action action, bool alwaysBackground = false) public void BackgroundContextAction(Action action, bool alwaysBackground = false)
{ {
action(); action();

View File

@@ -120,7 +120,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
}; };
@@ -188,6 +188,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))
@@ -198,10 +214,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;
@@ -225,6 +237,8 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe override void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) public unsafe override void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback)
{ {
_gd.PipelineInternal.AutoFlush.Present();
uint nextImage = 0; uint nextImage = 0;
while (true) while (true)

View File

@@ -0,0 +1,46 @@
using ARMeilleure.CodeGen.Arm64;
using NUnit.Framework;
namespace Ryujinx.Tests.Cpu
{
public class Arm64CodeGenCommonTests
{
public struct TestCase
{
public ulong Value;
public bool Valid;
public int ImmN;
public int ImmS;
public int ImmR;
}
public static readonly TestCase[] TestCases =
{
new() { Value = 0, Valid = false, ImmN = 0, ImmS = 0, ImmR = 0 },
new() { Value = 0x970977f35f848714, Valid = false, ImmN = 0, ImmS = 0, ImmR = 0 },
new() { Value = 0xffffffffffffffff, Valid = false, ImmN = 0, ImmS = 0, ImmR = 0 },
new() { Value = 0x5555555555555555, Valid = true, ImmN = 0, ImmS = 0x3c, ImmR = 0 },
new() { Value = 0xaaaaaaaaaaaaaaaa, Valid = true, ImmN = 0, ImmS = 0x3c, ImmR = 1 },
new() { Value = 0x6666666666666666, Valid = true, ImmN = 0, ImmS = 0x39, ImmR = 3 },
new() { Value = 0x1c1c1c1c1c1c1c1c, Valid = true, ImmN = 0, ImmS = 0x32, ImmR = 6 },
new() { Value = 0x0f0f0f0f0f0f0f0f, Valid = true, ImmN = 0, ImmS = 0x33, ImmR = 0 },
new() { Value = 0xf1f1f1f1f1f1f1f1, Valid = true, ImmN = 0, ImmS = 0x34, ImmR = 4 },
new() { Value = 0xe7e7e7e7e7e7e7e7, Valid = true, ImmN = 0, ImmS = 0x35, ImmR = 3 },
new() { Value = 0xc001c001c001c001, Valid = true, ImmN = 0, ImmS = 0x22, ImmR = 2 },
new() { Value = 0x0000038000000380, Valid = true, ImmN = 0, ImmS = 0x02, ImmR = 25 },
new() { Value = 0xffff8fffffff8fff, Valid = true, ImmN = 0, ImmS = 0x1c, ImmR = 17 },
new() { Value = 0x000000000ffff800, Valid = true, ImmN = 1, ImmS = 0x10, ImmR = 53 },
};
[Test]
public void BitImmTests([ValueSource(nameof(TestCases))] TestCase test)
{
bool valid = CodeGenCommon.TryEncodeBitMask(test.Value, out int immN, out int immS, out int immR);
Assert.That(valid, Is.EqualTo(test.Valid));
Assert.That(immN, Is.EqualTo(test.ImmN));
Assert.That(immS, Is.EqualTo(test.ImmS));
Assert.That(immR, Is.EqualTo(test.ImmR));
}
}
}

View File

@@ -340,7 +340,7 @@ namespace Ryujinx.Ui.Common.Configuration
{ {
configurationFileFormat = JsonHelper.DeserializeFromFile<ConfigurationFileFormat>(path); configurationFileFormat = JsonHelper.DeserializeFromFile<ConfigurationFileFormat>(path);
return true; return configurationFileFormat.Version != 0;
} }
catch catch
{ {

View File

@@ -0,0 +1,198 @@
using Microsoft.Win32;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
namespace Ryujinx.Ui.Common.Helper
{
public static partial class FileAssociationHelper
{
private static string[] _fileExtensions = new string[] { ".nca", ".nro", ".nso", ".nsp", ".xci" };
[SupportedOSPlatform("linux")]
private static string _mimeDbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "mime");
private const int SHCNE_ASSOCCHANGED = 0x8000000;
private const int SHCNF_FLUSH = 0x1000;
[LibraryImport("shell32.dll", SetLastError = true)]
public static partial void SHChangeNotify(uint wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
public static bool IsTypeAssociationSupported => (OperatingSystem.IsLinux() || OperatingSystem.IsWindows()) && !ReleaseInformation.IsFlatHubBuild();
[SupportedOSPlatform("linux")]
private static bool AreMimeTypesRegisteredLinux() => File.Exists(Path.Combine(_mimeDbPath, "packages", "Ryujinx.xml"));
[SupportedOSPlatform("linux")]
private static bool InstallLinuxMimeTypes(bool uninstall = false)
{
string installKeyword = uninstall ? "uninstall" : "install";
if (!AreMimeTypesRegisteredLinux())
{
string mimeTypesFile = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "mime", "Ryujinx.xml");
string additionalArgs = !uninstall ? "--novendor" : "";
using Process mimeProcess = new();
mimeProcess.StartInfo.FileName = "xdg-mime";
mimeProcess.StartInfo.Arguments = $"{installKeyword} {additionalArgs} --mode user {mimeTypesFile}";
mimeProcess.Start();
mimeProcess.WaitForExit();
if (mimeProcess.ExitCode != 0)
{
Logger.Error?.PrintMsg(LogClass.Application, $"Unable to {installKeyword} mime types. Make sure xdg-utils is installed. Process exited with code: {mimeProcess.ExitCode}");
return false;
}
using Process updateMimeProcess = new();
updateMimeProcess.StartInfo.FileName = "update-mime-database";
updateMimeProcess.StartInfo.Arguments = _mimeDbPath;
updateMimeProcess.Start();
updateMimeProcess.WaitForExit();
if (updateMimeProcess.ExitCode != 0)
{
Logger.Error?.PrintMsg(LogClass.Application, $"Could not update local mime database. Process exited with code: {updateMimeProcess.ExitCode}");
}
}
return true;
}
[SupportedOSPlatform("windows")]
private static bool AreMimeTypesRegisteredWindows()
{
static bool CheckRegistering(string ext)
{
RegistryKey key = Registry.CurrentUser.OpenSubKey(@$"Software\Classes\{ext}");
if (key is null)
{
return false;
}
key.OpenSubKey(@"shell\open\command");
string keyValue = (string)key.GetValue("");
return keyValue is not null && (keyValue.Contains("Ryujinx") || keyValue.Contains(AppDomain.CurrentDomain.FriendlyName));
}
bool registered = false;
foreach (string ext in _fileExtensions)
{
registered |= CheckRegistering(ext);
}
return registered;
}
[SupportedOSPlatform("windows")]
private static bool InstallWindowsMimeTypes(bool uninstall = false)
{
static bool RegisterExtension(string ext, bool uninstall = false)
{
string keyString = @$"Software\Classes\{ext}";
if (uninstall)
{
if (!AreMimeTypesRegisteredWindows())
{
return false;
}
Registry.CurrentUser.DeleteSubKeyTree(keyString);
}
else
{
RegistryKey key = Registry.CurrentUser.CreateSubKey(keyString);
if (key is null)
{
return false;
}
key.CreateSubKey(@"shell\open\command");
key.SetValue("", $"\"{Environment.ProcessPath}\" \"%1\"");
key.Close();
}
// Notify Explorer the file association has been changed.
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSH, IntPtr.Zero, IntPtr.Zero);
return true;
}
bool registered = false;
foreach (string ext in _fileExtensions)
{
registered |= RegisterExtension(ext, uninstall);
}
return registered;
}
public static bool AreMimeTypesRegistered()
{
if (OperatingSystem.IsLinux())
{
return AreMimeTypesRegisteredLinux();
}
if (OperatingSystem.IsWindows())
{
return AreMimeTypesRegisteredWindows();
}
// TODO: Add macOS support.
return false;
}
public static bool Install()
{
if (OperatingSystem.IsLinux())
{
return InstallLinuxMimeTypes();
}
if (OperatingSystem.IsWindows())
{
return InstallWindowsMimeTypes();
}
// TODO: Add macOS support.
return false;
}
public static bool Uninstall()
{
if (OperatingSystem.IsLinux())
{
return InstallLinuxMimeTypes(true);
}
if (OperatingSystem.IsWindows())
{
return InstallWindowsMimeTypes(true);
}
// TODO: Add macOS support.
return false;
}
}
}

View File

@@ -18,7 +18,6 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ryujinx namespace Ryujinx
@@ -73,48 +72,6 @@ namespace Ryujinx
} }
} }
[SupportedOSPlatform("linux")]
static void RegisterMimeTypes()
{
if (ReleaseInformation.IsFlatHubBuild())
{
return;
}
string mimeDbPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "mime");
if (!File.Exists(Path.Combine(mimeDbPath, "packages", "Ryujinx.xml")))
{
string mimeTypesFile = Path.Combine(ReleaseInformation.GetBaseApplicationDirectory(), "mime", "Ryujinx.xml");
using Process mimeProcess = new();
mimeProcess.StartInfo.FileName = "xdg-mime";
mimeProcess.StartInfo.Arguments = $"install --novendor --mode user {mimeTypesFile}";
mimeProcess.Start();
mimeProcess.WaitForExit();
if (mimeProcess.ExitCode != 0)
{
Logger.Error?.PrintMsg(LogClass.Application, $"Unable to install mime types. Make sure xdg-utils is installed. Process exited with code: {mimeProcess.ExitCode}");
return;
}
using Process updateMimeProcess = new();
updateMimeProcess.StartInfo.FileName = "update-mime-database";
updateMimeProcess.StartInfo.Arguments = mimeDbPath;
updateMimeProcess.Start();
updateMimeProcess.WaitForExit();
if (updateMimeProcess.ExitCode != 0)
{
Logger.Error?.PrintMsg(LogClass.Application, $"Could not update local mime database. Process exited with code: {updateMimeProcess.ExitCode}");
}
}
}
static void Main(string[] args) static void Main(string[] args)
{ {
Version = ReleaseInformation.GetVersion(); Version = ReleaseInformation.GetVersion();
@@ -189,12 +146,6 @@ namespace Ryujinx
// Initialize the logger system. // Initialize the logger system.
LoggerModule.Initialize(); LoggerModule.Initialize();
// Register mime types on linux.
if (OperatingSystem.IsLinux())
{
RegisterMimeTypes();
}
// Initialize Discord integration. // Initialize Discord integration.
DiscordIntegrationModule.Initialize(); DiscordIntegrationModule.Initialize();

View File

@@ -44,7 +44,6 @@ using System.IO;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using GUI = Gtk.Builder.ObjectAttribute; using GUI = Gtk.Builder.ObjectAttribute;
using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState; using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState;
@@ -109,6 +108,7 @@ namespace Ryujinx.Ui
[GUI] CheckMenuItem _favToggle; [GUI] CheckMenuItem _favToggle;
[GUI] MenuItem _firmwareInstallDirectory; [GUI] MenuItem _firmwareInstallDirectory;
[GUI] MenuItem _firmwareInstallFile; [GUI] MenuItem _firmwareInstallFile;
[GUI] MenuItem _fileTypesSubMenu;
[GUI] Label _fifoStatus; [GUI] Label _fifoStatus;
[GUI] CheckMenuItem _iconToggle; [GUI] CheckMenuItem _iconToggle;
[GUI] CheckMenuItem _developerToggle; [GUI] CheckMenuItem _developerToggle;
@@ -220,6 +220,8 @@ namespace Ryujinx.Ui
_pauseEmulation.Sensitive = false; _pauseEmulation.Sensitive = false;
_resumeEmulation.Sensitive = false; _resumeEmulation.Sensitive = false;
_fileTypesSubMenu.Visible = FileAssociationHelper.IsTypeAssociationSupported;
if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) _favToggle.Active = true; if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) _favToggle.Active = true;
if (ConfigurationState.Instance.Ui.GuiColumns.IconColumn) _iconToggle.Active = true; if (ConfigurationState.Instance.Ui.GuiColumns.IconColumn) _iconToggle.Active = true;
if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn) _appToggle.Active = true; if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn) _appToggle.Active = true;
@@ -1500,6 +1502,30 @@ namespace Ryujinx.Ui
}); });
} }
private void InstallFileTypes_Pressed(object sender, EventArgs e)
{
if (FileAssociationHelper.Install())
{
GtkDialog.CreateInfoDialog("Install file types", "File types successfully installed!");
}
else
{
GtkDialog.CreateErrorDialog("Failed to install file types.");
}
}
private void UninstallFileTypes_Pressed(object sender, EventArgs e)
{
if (FileAssociationHelper.Uninstall())
{
GtkDialog.CreateInfoDialog("Uninstall file types", "File types successfully uninstalled!");
}
else
{
GtkDialog.CreateErrorDialog("Failed to uninstall file types.");
}
}
private void HandleRelaunch() private void HandleRelaunch()
{ {
if (_userChannelPersistence.PreviousIndex != -1 && _userChannelPersistence.ShouldRestart) if (_userChannelPersistence.PreviousIndex != -1 && _userChannelPersistence.ShouldRestart)

View File

@@ -1,67 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.21.0 --> <!-- Generated with glade 3.40.0 -->
<interface> <interface>
<requires lib="gtk+" version="3.20"/> <requires lib="gtk+" version="3.20"/>
<object class="GtkApplicationWindow" id="_mainWin"> <object class="GtkApplicationWindow" id="_mainWin">
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="title" translatable="yes">Ryujinx</property> <property name="title" translatable="yes">Ryujinx</property>
<property name="window_position">center</property> <property name="window-position">center</property>
<child> <child>
<object class="GtkBox" id="_box"> <object class="GtkBox" id="_box">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<child> <child>
<object class="GtkMenuBar" id="_menuBar"> <object class="GtkMenuBar" id="_menuBar">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<child> <child>
<object class="GtkMenuItem" id="_fileMenu"> <object class="GtkMenuItem" id="_fileMenu">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="label" translatable="yes">File</property> <property name="label" translatable="yes">File</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<child type="submenu"> <child type="submenu">
<object class="GtkMenu"> <object class="GtkMenu">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<child> <child>
<object class="GtkMenuItem" id="_loadApplicationFile"> <object class="GtkMenuItem" id="_loadApplicationFile">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Open a file explorer to choose a Switch compatible file to load</property> <property name="tooltip-text" translatable="yes">Open a file explorer to choose a Switch compatible file to load</property>
<property name="label" translatable="yes">Load Application from File</property> <property name="label" translatable="yes">Load Application from File</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<signal name="activate" handler="Load_Application_File" swapped="no"/> <signal name="activate" handler="Load_Application_File" swapped="no"/>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkMenuItem" id="_loadApplicationFolder"> <object class="GtkMenuItem" id="_loadApplicationFolder">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Open a file explorer to choose a Switch compatible, unpacked application to load</property> <property name="tooltip-text" translatable="yes">Open a file explorer to choose a Switch compatible, unpacked application to load</property>
<property name="label" translatable="yes">Load Unpacked Game</property> <property name="label" translatable="yes">Load Unpacked Game</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<signal name="activate" handler="Load_Application_Folder" swapped="no"/> <signal name="activate" handler="Load_Application_Folder" swapped="no"/>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkMenuItem" id="_appletMenu"> <object class="GtkMenuItem" id="_appletMenu">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="label" translatable="yes">Load Applet</property> <property name="label" translatable="yes">Load Applet</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<child type="submenu"> <child type="submenu">
<object class="GtkMenu"> <object class="GtkMenu">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<child> <child>
<object class="GtkMenuItem" id="LoadMiiEditApplet"> <object class="GtkMenuItem" id="LoadMiiEditApplet">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Open Mii Editor Applet in Standalone mode</property> <property name="tooltip-text" translatable="yes">Open Mii Editor Applet in Standalone mode</property>
<property name="label" translatable="yes">Mii Editor</property> <property name="label" translatable="yes">Mii Editor</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<signal name="activate" handler="Load_Mii_Edit_Applet" swapped="no"/> <signal name="activate" handler="Load_Mii_Edit_Applet" swapped="no"/>
</object> </object>
</child> </child>
@@ -72,42 +72,42 @@
<child> <child>
<object class="GtkSeparatorMenuItem"> <object class="GtkSeparatorMenuItem">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkMenuItem" id="OpenRyuFolder"> <object class="GtkMenuItem" id="OpenRyuFolder">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Open Ryujinx filesystem folder</property> <property name="tooltip-text" translatable="yes">Open Ryujinx filesystem folder</property>
<property name="label" translatable="yes">Open Ryujinx Folder</property> <property name="label" translatable="yes">Open Ryujinx Folder</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<signal name="activate" handler="Open_Ryu_Folder" swapped="no"/> <signal name="activate" handler="Open_Ryu_Folder" swapped="no"/>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkMenuItem" id="OpenLogsFolder"> <object class="GtkMenuItem" id="OpenLogsFolder">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Opens the folder where logs are written to.</property> <property name="tooltip-text" translatable="yes">Opens the folder where logs are written to.</property>
<property name="label" translatable="yes">Open Logs Folder</property> <property name="label" translatable="yes">Open Logs Folder</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<signal name="activate" handler="OpenLogsFolder_Pressed" swapped="no"/> <signal name="activate" handler="OpenLogsFolder_Pressed" swapped="no"/>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkSeparatorMenuItem"> <object class="GtkSeparatorMenuItem">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkMenuItem" id="ExitMenuItem"> <object class="GtkMenuItem" id="ExitMenuItem">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Exit Ryujinx</property> <property name="tooltip-text" translatable="yes">Exit Ryujinx</property>
<property name="label" translatable="yes">Exit</property> <property name="label" translatable="yes">Exit</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<signal name="activate" handler="Exit_Pressed" swapped="no"/> <signal name="activate" handler="Exit_Pressed" swapped="no"/>
</object> </object>
</child> </child>
@@ -118,144 +118,144 @@
<child> <child>
<object class="GtkMenuItem" id="_optionMenu"> <object class="GtkMenuItem" id="_optionMenu">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="label" translatable="yes">Options</property> <property name="label" translatable="yes">Options</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<child type="submenu"> <child type="submenu">
<object class="GtkMenu"> <object class="GtkMenu">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<child> <child>
<object class="GtkMenuItem" id="_fullScreen"> <object class="GtkMenuItem" id="_fullScreen">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="label" translatable="yes">Enter Fullscreen</property> <property name="label" translatable="yes">Enter Fullscreen</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkCheckMenuItem" id="_startFullScreen"> <object class="GtkCheckMenuItem" id="_startFullScreen">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="label" translatable="yes">Start Games in Fullscreen Mode</property> <property name="label" translatable="yes">Start Games in Fullscreen Mode</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<signal name="toggled" handler="StartFullScreen_Toggled" swapped="no"/> <signal name="toggled" handler="StartFullScreen_Toggled" swapped="no"/>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkCheckMenuItem" id="_showConsole"> <object class="GtkCheckMenuItem" id="_showConsole">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="label" translatable="yes">Show Log Console</property> <property name="label" translatable="yes">Show Log Console</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<signal name="toggled" handler="ShowConsole_Toggled" swapped="no"/> <signal name="toggled" handler="ShowConsole_Toggled" swapped="no"/>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkSeparatorMenuItem"> <object class="GtkSeparatorMenuItem">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkMenuItem" id="GUIColumns"> <object class="GtkMenuItem" id="GUIColumns">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Select which GUI columns to enable</property> <property name="tooltip-text" translatable="yes">Select which GUI columns to enable</property>
<property name="label" translatable="yes">Enable GUI Columns</property> <property name="label" translatable="yes">Enable GUI Columns</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<child type="submenu"> <child type="submenu">
<object class="GtkMenu"> <object class="GtkMenu">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<child> <child>
<object class="GtkCheckMenuItem" id="_favToggle"> <object class="GtkCheckMenuItem" id="_favToggle">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Enable or Disable Favorite Games Column in the game list</property> <property name="tooltip-text" translatable="yes">Enable or Disable Favorite Games Column in the game list</property>
<property name="label" translatable="yes">Enable Favorite Games Column</property> <property name="label" translatable="yes">Enable Favorite Games Column</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkCheckMenuItem" id="_iconToggle"> <object class="GtkCheckMenuItem" id="_iconToggle">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Enable or Disable Icon Column in the game list</property> <property name="tooltip-text" translatable="yes">Enable or Disable Icon Column in the game list</property>
<property name="label" translatable="yes">Enable Icon Column</property> <property name="label" translatable="yes">Enable Icon Column</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkCheckMenuItem" id="_appToggle"> <object class="GtkCheckMenuItem" id="_appToggle">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Enable or Disable Title Name/ID Column in the game list</property> <property name="tooltip-text" translatable="yes">Enable or Disable Title Name/ID Column in the game list</property>
<property name="label" translatable="yes">Enable Title Name/ID Column</property> <property name="label" translatable="yes">Enable Title Name/ID Column</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkCheckMenuItem" id="_developerToggle"> <object class="GtkCheckMenuItem" id="_developerToggle">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Enable or Disable Developer Column in the game list</property> <property name="tooltip-text" translatable="yes">Enable or Disable Developer Column in the game list</property>
<property name="label" translatable="yes">Enable Developer Column</property> <property name="label" translatable="yes">Enable Developer Column</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkCheckMenuItem" id="_versionToggle"> <object class="GtkCheckMenuItem" id="_versionToggle">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Enable or Disable Version Column in the game list</property> <property name="tooltip-text" translatable="yes">Enable or Disable Version Column in the game list</property>
<property name="label" translatable="yes">Enable Version Column</property> <property name="label" translatable="yes">Enable Version Column</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkCheckMenuItem" id="_timePlayedToggle"> <object class="GtkCheckMenuItem" id="_timePlayedToggle">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Enable or Disable Time Played Column in the game list</property> <property name="tooltip-text" translatable="yes">Enable or Disable Time Played Column in the game list</property>
<property name="label" translatable="yes">Enable Time Played Column</property> <property name="label" translatable="yes">Enable Time Played Column</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkCheckMenuItem" id="_lastPlayedToggle"> <object class="GtkCheckMenuItem" id="_lastPlayedToggle">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Enable or Disable Last Played Column in the game list</property> <property name="tooltip-text" translatable="yes">Enable or Disable Last Played Column in the game list</property>
<property name="label" translatable="yes">Enable Last Played Column</property> <property name="label" translatable="yes">Enable Last Played Column</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkCheckMenuItem" id="_fileExtToggle"> <object class="GtkCheckMenuItem" id="_fileExtToggle">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Enable or Disable file extension column in the game list</property> <property name="tooltip-text" translatable="yes">Enable or Disable file extension column in the game list</property>
<property name="label" translatable="yes">Enable File Ext Column</property> <property name="label" translatable="yes">Enable File Ext Column</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkCheckMenuItem" id="_fileSizeToggle"> <object class="GtkCheckMenuItem" id="_fileSizeToggle">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Enable or Disable File Size Column in the game list</property> <property name="tooltip-text" translatable="yes">Enable or Disable File Size Column in the game list</property>
<property name="label" translatable="yes">Enable File Size Column</property> <property name="label" translatable="yes">Enable File Size Column</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkCheckMenuItem" id="_pathToggle"> <object class="GtkCheckMenuItem" id="_pathToggle">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Enable or Disable Path Column in the game list</property> <property name="tooltip-text" translatable="yes">Enable or Disable Path Column in the game list</property>
<property name="label" translatable="yes">Enable Path Column</property> <property name="label" translatable="yes">Enable Path Column</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
</object> </object>
</child> </child>
</object> </object>
@@ -265,26 +265,26 @@
<child> <child>
<object class="GtkSeparatorMenuItem"> <object class="GtkSeparatorMenuItem">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkMenuItem" id="SettingsMenu"> <object class="GtkMenuItem" id="SettingsMenu">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Open settings window</property> <property name="tooltip-text" translatable="yes">Open settings window</property>
<property name="label" translatable="yes">Settings</property> <property name="label" translatable="yes">Settings</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<signal name="activate" handler="Settings_Pressed" swapped="no"/> <signal name="activate" handler="Settings_Pressed" swapped="no"/>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkMenuItem" id="_manageUserProfiles"> <object class="GtkMenuItem" id="_manageUserProfiles">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Open User Profiles Manager window</property> <property name="tooltip-text" translatable="yes">Open User Profiles Manager window</property>
<property name="label" translatable="yes">Manage User Profiles</property> <property name="label" translatable="yes">Manage User Profiles</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<signal name="activate" handler="ManageUserProfiles_Pressed" swapped="no"/> <signal name="activate" handler="ManageUserProfiles_Pressed" swapped="no"/>
</object> </object>
</child> </child>
@@ -295,74 +295,74 @@
<child> <child>
<object class="GtkMenuItem" id="_actionMenu"> <object class="GtkMenuItem" id="_actionMenu">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="label" translatable="yes">Actions</property> <property name="label" translatable="yes">Actions</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<child type="submenu"> <child type="submenu">
<object class="GtkMenu"> <object class="GtkMenu">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<child> <child>
<object class="GtkMenuItem" id="_pauseEmulation"> <object class="GtkMenuItem" id="_pauseEmulation">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Pause emulation</property> <property name="tooltip-text" translatable="yes">Pause emulation</property>
<property name="label" translatable="yes">Pause Emulation</property> <property name="label" translatable="yes">Pause Emulation</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<signal name="activate" handler="PauseEmulation_Pressed" swapped="no"/> <signal name="activate" handler="PauseEmulation_Pressed" swapped="no"/>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkMenuItem" id="_resumeEmulation"> <object class="GtkMenuItem" id="_resumeEmulation">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Resume emulation</property> <property name="tooltip-text" translatable="yes">Resume emulation</property>
<property name="label" translatable="yes">Resume Emulation</property> <property name="label" translatable="yes">Resume Emulation</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<signal name="activate" handler="ResumeEmulation_Pressed" swapped="no"/> <signal name="activate" handler="ResumeEmulation_Pressed" swapped="no"/>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkMenuItem" id="_stopEmulation"> <object class="GtkMenuItem" id="_stopEmulation">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Stop emulation of the current game and return to game selection</property> <property name="tooltip-text" translatable="yes">Stop emulation of the current game and return to game selection</property>
<property name="label" translatable="yes">Stop Emulation</property> <property name="label" translatable="yes">Stop Emulation</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<signal name="activate" handler="StopEmulation_Pressed" swapped="no"/> <signal name="activate" handler="StopEmulation_Pressed" swapped="no"/>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkSeparatorMenuItem"> <object class="GtkSeparatorMenuItem">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkMenuItem" id="_simulateWakeUpMessage"> <object class="GtkMenuItem" id="_simulateWakeUpMessage">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Simulate a Wake-up Message</property> <property name="tooltip-text" translatable="yes">Simulate a Wake-up Message</property>
<property name="label" translatable="yes">Simulate Wake-up Message</property> <property name="label" translatable="yes">Simulate Wake-up Message</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<signal name="activate" handler="Simulate_WakeUp_Message_Pressed" swapped="no"/> <signal name="activate" handler="Simulate_WakeUp_Message_Pressed" swapped="no"/>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkMenuItem" id="_scanAmiibo"> <object class="GtkMenuItem" id="_scanAmiibo">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Scan an Amiibo</property> <property name="tooltip-text" translatable="yes">Scan an Amiibo</property>
<property name="label" translatable="yes">Scan an Amiibo</property> <property name="label" translatable="yes">Scan an Amiibo</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<signal name="activate" handler="Scan_Amiibo" swapped="no"/> <signal name="activate" handler="Scan_Amiibo" swapped="no"/>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkMenuItem" id="_takeScreenshot"> <object class="GtkMenuItem" id="_takeScreenshot">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Take a screenshot</property> <property name="tooltip-text" translatable="yes">Take a screenshot</property>
<property name="label" translatable="yes">Take Screenshot</property> <property name="label" translatable="yes">Take Screenshot</property>
<signal name="activate" handler="Take_Screenshot" swapped="no"/> <signal name="activate" handler="Take_Screenshot" swapped="no"/>
</object> </object>
@@ -370,16 +370,16 @@
<child> <child>
<object class="GtkMenuItem" id="_hideUi"> <object class="GtkMenuItem" id="_hideUi">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="label" translatable="yes">Hide UI (SHOWUIKEY to show)</property> <property name="label" translatable="yes">Hide UI (SHOWUIKEY to show)</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<signal name="activate" handler="HideUi_Pressed" swapped="no"/> <signal name="activate" handler="HideUi_Pressed" swapped="no"/>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkMenuItem" id="_manageCheats"> <object class="GtkMenuItem" id="_manageCheats">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="label" translatable="yes">Manage Cheats</property> <property name="label" translatable="yes">Manage Cheats</property>
<signal name="activate" handler="ManageCheats_Pressed" swapped="no"/> <signal name="activate" handler="ManageCheats_Pressed" swapped="no"/>
</object> </object>
@@ -391,38 +391,38 @@
<child> <child>
<object class="GtkMenuItem" id="_toolsMenu"> <object class="GtkMenuItem" id="_toolsMenu">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="label" translatable="yes">Tools</property> <property name="label" translatable="yes">Tools</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<child type="submenu"> <child type="submenu">
<object class="GtkMenu"> <object class="GtkMenu">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<child> <child>
<object class="GtkMenuItem" id="FirmwareSubMenu"> <object class="GtkMenuItem" id="FirmwareSubMenu">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="label" translatable="yes">Install Firmware</property> <property name="label" translatable="yes">Install Firmware</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<child type="submenu"> <child type="submenu">
<object class="GtkMenu"> <object class="GtkMenu">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<child> <child>
<object class="GtkMenuItem" id="_firmwareInstallFile"> <object class="GtkMenuItem" id="_firmwareInstallFile">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="label" translatable="yes">Install a firmware from XCI or ZIP</property> <property name="label" translatable="yes">Install a firmware from XCI or ZIP</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<signal name="activate" handler="Installer_File_Pressed" swapped="no"/> <signal name="activate" handler="Installer_File_Pressed" swapped="no"/>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkMenuItem" id="_firmwareInstallDirectory"> <object class="GtkMenuItem" id="_firmwareInstallDirectory">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="label" translatable="yes">Install a firmware from a directory</property> <property name="label" translatable="yes">Install a firmware from a directory</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<signal name="activate" handler="Installer_Directory_Pressed" swapped="no"/> <signal name="activate" handler="Installer_Directory_Pressed" swapped="no"/>
</object> </object>
</child> </child>
@@ -430,6 +430,36 @@
</child> </child>
</object> </object>
</child> </child>
<child>
<object class="GtkMenuItem" id="_fileTypesSubMenu">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Manage file types</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can-focus">False</property>
<child>
<object class="GtkMenuItem" id="_installFileTypes">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Install file types</property>
<signal name="activate" handler="InstallFileTypes_Pressed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_uninstallFileTypes">
<property name="visible">True</property>
<property name="can-focus">False</property>
<property name="label" translatable="yes">Uninstall file types</property>
<signal name="activate" handler="UninstallFileTypes_Pressed" swapped="no"/>
</object>
</child>
</object>
</child>
</object>
</child>
</object> </object>
</child> </child>
</object> </object>
@@ -437,36 +467,36 @@
<child> <child>
<object class="GtkMenuItem" id="HelpMenu"> <object class="GtkMenuItem" id="HelpMenu">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="label" translatable="yes">Help</property> <property name="label" translatable="yes">Help</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<child type="submenu"> <child type="submenu">
<object class="GtkMenu"> <object class="GtkMenu">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<child> <child>
<object class="GtkMenuItem" id="UpdateMenuItem"> <object class="GtkMenuItem" id="UpdateMenuItem">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Check for updates to Ryujinx</property> <property name="tooltip-text" translatable="yes">Check for updates to Ryujinx</property>
<property name="label" translatable="yes">Check for Updates</property> <property name="label" translatable="yes">Check for Updates</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<signal name="activate" handler="Update_Pressed" swapped="no"/> <signal name="activate" handler="Update_Pressed" swapped="no"/>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkSeparatorMenuItem"> <object class="GtkSeparatorMenuItem">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
</object> </object>
</child> </child>
<child> <child>
<object class="GtkMenuItem" id="About"> <object class="GtkMenuItem" id="About">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="tooltip_text" translatable="yes">Open about window</property> <property name="tooltip-text" translatable="yes">Open about window</property>
<property name="label" translatable="yes">About</property> <property name="label" translatable="yes">About</property>
<property name="use_underline">True</property> <property name="use-underline">True</property>
<signal name="activate" handler="About_Pressed" swapped="no"/> <signal name="activate" handler="About_Pressed" swapped="no"/>
</object> </object>
</child> </child>
@@ -484,24 +514,24 @@
<child> <child>
<object class="GtkBox" id="_mainBox"> <object class="GtkBox" id="_mainBox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<child> <child>
<object class="GtkBox" id="_viewBox"> <object class="GtkBox" id="_viewBox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<child> <child>
<object class="GtkScrolledWindow" id="_gameTableWindow"> <object class="GtkScrolledWindow" id="_gameTableWindow">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can-focus">True</property>
<property name="shadow_type">in</property> <property name="shadow-type">in</property>
<child> <child>
<object class="GtkTreeView" id="_gameTable"> <object class="GtkTreeView" id="_gameTable">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">True</property> <property name="can-focus">True</property>
<property name="reorderable">True</property> <property name="reorderable">True</property>
<property name="hover_selection">True</property> <property name="hover-selection">True</property>
<signal name="row-activated" handler="Row_Activated" swapped="no"/> <signal name="row-activated" handler="Row_Activated" swapped="no"/>
<child internal-child="selection"> <child internal-child="selection">
<object class="GtkTreeSelection" id="_gameTableSelection"/> <object class="GtkTreeSelection" id="_gameTableSelection"/>
@@ -524,24 +554,24 @@
</child> </child>
<child> <child>
<object class="GtkBox" id="_footerBox"> <object class="GtkBox" id="_footerBox">
<property name="height_request">19</property> <property name="height-request">19</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<child> <child>
<object class="GtkBox" id="_listStatusBox"> <object class="GtkBox" id="_listStatusBox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<child> <child>
<object class="GtkEventBox"> <object class="GtkEventBox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="margin_left">5</property> <property name="margin-left">5</property>
<signal name="button-release-event" handler="RefreshList_Pressed" swapped="no"/> <signal name="button-release-event" handler="RefreshList_Pressed" swapped="no"/>
<child> <child>
<object class="GtkImage"> <object class="GtkImage">
<property name="name">RefreshList</property> <property name="name">RefreshList</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="stock">gtk-refresh</property> <property name="stock">gtk-refresh</property>
</object> </object>
</child> </child>
@@ -555,11 +585,11 @@
<child> <child>
<object class="GtkLabel" id="_progressLabel"> <object class="GtkLabel" id="_progressLabel">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="margin_left">10</property> <property name="margin-left">10</property>
<property name="margin_right">5</property> <property name="margin-right">5</property>
<property name="margin_top">2</property> <property name="margin-top">2</property>
<property name="margin_bottom">2</property> <property name="margin-bottom">2</property>
<property name="label" translatable="yes">0/0 Games Loaded</property> <property name="label" translatable="yes">0/0 Games Loaded</property>
</object> </object>
<packing> <packing>
@@ -570,13 +600,13 @@
</child> </child>
<child> <child>
<object class="GtkProgressBar" id="_progressBar"> <object class="GtkProgressBar" id="_progressBar">
<property name="width_request">200</property> <property name="width-request">200</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="halign">start</property> <property name="halign">start</property>
<property name="margin_left">10</property> <property name="margin-left">10</property>
<property name="margin_right">5</property> <property name="margin-right">5</property>
<property name="margin_bottom">6</property> <property name="margin-bottom">6</property>
</object> </object>
<packing> <packing>
<property name="expand">True</property> <property name="expand">True</property>
@@ -594,19 +624,19 @@
<child> <child>
<object class="GtkBox" id="_statusBar"> <object class="GtkBox" id="_statusBar">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<child> <child>
<object class="GtkEventBox"> <object class="GtkEventBox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<signal name="button-release-event" handler="VSyncStatus_Clicked" swapped="no"/> <signal name="button-release-event" handler="VSyncStatus_Clicked" swapped="no"/>
<child> <child>
<object class="GtkLabel" id="_vSyncStatus"> <object class="GtkLabel" id="_vSyncStatus">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="halign">start</property> <property name="halign">start</property>
<property name="margin_left">5</property> <property name="margin-left">5</property>
<property name="margin_right">5</property> <property name="margin-right">5</property>
<property name="label" translatable="yes">VSync</property> <property name="label" translatable="yes">VSync</property>
</object> </object>
</child> </child>
@@ -620,7 +650,7 @@
<child> <child>
<object class="GtkSeparator"> <object class="GtkSeparator">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@@ -631,15 +661,15 @@
<child> <child>
<object class="GtkEventBox"> <object class="GtkEventBox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<signal name="button-release-event" handler="DockedMode_Clicked" swapped="no"/> <signal name="button-release-event" handler="DockedMode_Clicked" swapped="no"/>
<child> <child>
<object class="GtkLabel" id="_dockedMode"> <object class="GtkLabel" id="_dockedMode">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="halign">start</property> <property name="halign">start</property>
<property name="margin_left">5</property> <property name="margin-left">5</property>
<property name="margin_right">5</property> <property name="margin-right">5</property>
</object> </object>
</child> </child>
</object> </object>
@@ -652,7 +682,7 @@
<child> <child>
<object class="GtkSeparator"> <object class="GtkSeparator">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@@ -663,15 +693,15 @@
<child> <child>
<object class="GtkEventBox"> <object class="GtkEventBox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<signal name="button-release-event" handler="VolumeStatus_Clicked" swapped="no"/> <signal name="button-release-event" handler="VolumeStatus_Clicked" swapped="no"/>
<child> <child>
<object class="GtkLabel" id="_volumeStatus"> <object class="GtkLabel" id="_volumeStatus">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="halign">start</property> <property name="halign">start</property>
<property name="margin_left">5</property> <property name="margin-left">5</property>
<property name="margin_right">5</property> <property name="margin-right">5</property>
</object> </object>
</child> </child>
</object> </object>
@@ -684,7 +714,7 @@
<child> <child>
<object class="GtkSeparator"> <object class="GtkSeparator">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@@ -695,15 +725,15 @@
<child> <child>
<object class="GtkEventBox"> <object class="GtkEventBox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<signal name="button-release-event" handler="AspectRatio_Clicked" swapped="no"/> <signal name="button-release-event" handler="AspectRatio_Clicked" swapped="no"/>
<child> <child>
<object class="GtkLabel" id="_aspectRatio"> <object class="GtkLabel" id="_aspectRatio">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="halign">start</property> <property name="halign">start</property>
<property name="margin_left">5</property> <property name="margin-left">5</property>
<property name="margin_right">5</property> <property name="margin-right">5</property>
</object> </object>
</child> </child>
</object> </object>
@@ -716,7 +746,7 @@
<child> <child>
<object class="GtkSeparator"> <object class="GtkSeparator">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@@ -727,10 +757,10 @@
<child> <child>
<object class="GtkLabel" id="_gameStatus"> <object class="GtkLabel" id="_gameStatus">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="halign">start</property> <property name="halign">start</property>
<property name="margin_left">5</property> <property name="margin-left">5</property>
<property name="margin_right">5</property> <property name="margin-right">5</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@@ -741,7 +771,7 @@
<child> <child>
<object class="GtkSeparator"> <object class="GtkSeparator">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@@ -752,10 +782,10 @@
<child> <child>
<object class="GtkLabel" id="_fifoStatus"> <object class="GtkLabel" id="_fifoStatus">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="halign">start</property> <property name="halign">start</property>
<property name="margin_left">5</property> <property name="margin-left">5</property>
<property name="margin_right">5</property> <property name="margin-right">5</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@@ -766,7 +796,7 @@
<child> <child>
<object class="GtkSeparator"> <object class="GtkSeparator">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@@ -777,10 +807,10 @@
<child> <child>
<object class="GtkLabel" id="_gpuBackend"> <object class="GtkLabel" id="_gpuBackend">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="halign">start</property> <property name="halign">start</property>
<property name="margin_left">5</property> <property name="margin-left">5</property>
<property name="margin_right">5</property> <property name="margin-right">5</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@@ -791,7 +821,7 @@
<child> <child>
<object class="GtkSeparator"> <object class="GtkSeparator">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@@ -802,10 +832,10 @@
<child> <child>
<object class="GtkLabel" id="_gpuName"> <object class="GtkLabel" id="_gpuName">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="halign">start</property> <property name="halign">start</property>
<property name="margin_left">5</property> <property name="margin-left">5</property>
<property name="margin_right">5</property> <property name="margin-right">5</property>
</object> </object>
<packing> <packing>
<property name="expand">True</property> <property name="expand">True</property>
@@ -823,12 +853,12 @@
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="margin_left">5</property> <property name="margin-left">5</property>
<child> <child>
<object class="GtkLabel"> <object class="GtkLabel">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="label" translatable="yes">System Version</property> <property name="label" translatable="yes">System Version</property>
</object> </object>
<packing> <packing>
@@ -839,16 +869,16 @@
</child> </child>
<child> <child>
<object class="GtkLabel" id="_firmwareVersionLabel"> <object class="GtkLabel" id="_firmwareVersionLabel">
<property name="width_request">50</property> <property name="width-request">50</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="margin_left">5</property> <property name="margin-left">5</property>
<property name="margin_right">5</property> <property name="margin-right">5</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="pack_type">end</property> <property name="pack-type">end</property>
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
@@ -856,15 +886,15 @@
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="pack_type">end</property> <property name="pack-type">end</property>
<property name="position">4</property> <property name="position">4</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkLabel" id="_loadingStatusLabel"> <object class="GtkLabel" id="_loadingStatusLabel">
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="margin_left">5</property> <property name="margin-left">5</property>
<property name="margin_right">5</property> <property name="margin-right">5</property>
<property name="label" translatable="yes">0/0 </property> <property name="label" translatable="yes">0/0 </property>
</object> </object>
<packing> <packing>
@@ -875,11 +905,11 @@
</child> </child>
<child> <child>
<object class="GtkProgressBar" id="_loadingStatusBar"> <object class="GtkProgressBar" id="_loadingStatusBar">
<property name="width_request">200</property> <property name="width-request">200</property>
<property name="can_focus">False</property> <property name="can-focus">False</property>
<property name="margin_left">5</property> <property name="margin-left">5</property>
<property name="margin_right">5</property> <property name="margin-right">5</property>
<property name="margin_bottom">6</property> <property name="margin-bottom">6</property>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@@ -903,8 +933,5 @@
</child> </child>
</object> </object>
</child> </child>
<child type="titlebar">
<placeholder/>
</child>
</object> </object>
</interface> </interface>