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
PublishProfiles/
# Glade backup files
*.glade~

View File

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

View File

@@ -1,5 +1,4 @@
using ARMeilleure.IntermediateRepresentation;
using System;
using System.Numerics;
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)
{
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;
}
@@ -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 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.
if (value == ulong.MaxValue)
if (value == 0 || value == ulong.MaxValue)
{
immN = 0;
immS = 0;
@@ -59,79 +61,18 @@ namespace ARMeilleure.CodeGen.Arm64
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)
{
bitLength += CountSequence(value >> bitLength);
}
// Now that we have a complete element in the LSB with the LSB = 1, determine size and number of ones
// in element.
int elementSize = BitOperations.TrailingZeroCount(rotatedValue & (rotatedValue + 1));
int onesInElement = BitOperations.TrailingZeroCount(~rotatedValue);
int bitLengthLog2 = BitOperations.Log2((uint)bitLength);
int bitLengthPow2 = 1 << bitLengthLog2;
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)
// Check the value is repeating; also ensures element size is a power of two.
if (ulong.RotateRight(value, elementSize) != value)
{
immN = 0;
immS = 0;
@@ -140,34 +81,11 @@ namespace ARMeilleure.CodeGen.Arm64
return false;
}
immR = rotation;
// immN indicates that there are no repetitions.
// 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;
immN = (elementSize >> 6) & 1;
immS = (((~elementSize + 1) << 1) | (onesInElement - 1)) & 0x3f;
immR = (elementSize - rotation) & (elementSize - 1);
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)
{
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;
bool first = true;
@@ -1328,10 +1336,6 @@ namespace ARMeilleure.CodeGen.Arm64
value >>= 16;
}
}
else
{
context.Assembler.Mov(dest, Register(ZrRegister, dest.Type));
}
}
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>
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);
return (ulong)function.FuncPtr.ToInt64();
return (ulong)function.FuncPointer.ToInt64();
}
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 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 BackupDir = "1";
@@ -745,9 +745,9 @@ namespace ARMeilleure.Translation.PTC
bool highCq)
{
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)

View File

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

View File

@@ -211,7 +211,7 @@ namespace ARMeilleure.Translation
if (oldFunc != func)
{
JitCache.Unmap(func.FuncPtr);
JitCache.Unmap(func.FuncPointer);
func = oldFunc;
}
@@ -230,7 +230,7 @@ namespace ARMeilleure.Translation
{
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);
}
GuestFunction func = compiledFunc.Map<GuestFunction>();
GuestFunction func = compiledFunc.MapWithPointer<GuestFunction>(out IntPtr funcPointer);
Allocators.ResetAll();
return new TranslatedFunction(func, counter, funcSize, highCq);
return new TranslatedFunction(func, funcPointer, counter, funcSize, highCq);
}
private void BackgroundTranslate()
@@ -537,7 +537,7 @@ namespace ARMeilleure.Translation
foreach (var func in functions)
{
JitCache.Unmap(func.FuncPtr);
JitCache.Unmap(func.FuncPointer);
func.CallCounter?.Dispose();
}
@@ -546,7 +546,7 @@ namespace ARMeilleure.Translation
while (_oldFuncs.TryDequeue(out var kv))
{
JitCache.Unmap(kv.Value.FuncPtr);
JitCache.Unmap(kv.Value.FuncPointer);
kv.Value.CallCounter?.Dispose();
}

View File

@@ -26,6 +26,9 @@
"MenuBarToolsInstallFirmware": "Install Firmware",
"MenuBarFileToolsInstallFirmwareFromFile": "Install a firmware from XCI or ZIP",
"MenuBarFileToolsInstallFirmwareFromDirectory": "Install a firmware from a directory",
"MenuBarToolsManageFileTypes": "Manage file types",
"MenuBarToolsInstallFileTypes": "Install file types",
"MenuBarToolsUninstallFileTypes": "Uninstall file types",
"MenuBarHelp": "Help",
"MenuBarHelpCheckForUpdates": "Check for Updates",
"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.",
"DialogFirmwareNoFirmwareInstalledMessage": "No Firmware 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",
"DialogControllerAppletTitle": "Controller Applet",
"DialogMessageDialogErrorExceptionMessage": "Error displaying Message Dialog: {0}",
@@ -619,4 +626,4 @@
"UserProfilesRecoverEmptyList": "No profiles to recover",
"UserEditorTitle" : "Edit User",
"UserEditorTitleCreate" : "Create User"
}
}

View File

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

View File

@@ -14,10 +14,8 @@ using Ryujinx.Ui.Common;
using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Helper;
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading.Tasks;
namespace Ryujinx.Ava
@@ -35,48 +33,6 @@ namespace Ryujinx.Ava
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)
{
Version = ReleaseInformation.GetVersion();
@@ -139,12 +95,6 @@ namespace Ryujinx.Ava
// Initialize the logger system.
LoggerModule.Initialize();
// Register mime types on linux.
if (OperatingSystem.IsLinux())
{
RegisterMimeTypes();
}
// Initialize Discord integration.
DiscordIntegrationModule.Initialize();

View File

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

View File

@@ -77,8 +77,7 @@
</MenuItem.Icon>
</MenuItem>
<Separator />
<MenuItem Name="ChangeLanguageMenuItem" Header="{locale:Locale MenuBarOptionsChangeLanguage}">
</MenuItem>
<MenuItem Name="ChangeLanguageMenuItem" Header="{locale:Locale MenuBarOptionsChangeLanguage}" />
<Separator />
<MenuItem
Click="OpenSettings"
@@ -141,6 +140,10 @@
<MenuItem Command="{ReflectionBinding InstallFirmwareFromFile}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromFile}" />
<MenuItem Command="{ReflectionBinding InstallFirmwareFromFolder}" Header="{locale:Locale MenuBarFileToolsInstallFirmwareFromDirectory}" />
</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 VerticalAlignment="Center" Header="{locale:Locale MenuBarHelp}">
<MenuItem
@@ -157,4 +160,4 @@
</MenuItem>
</Menu>
</DockPanel>
</UserControl>
</UserControl>

View File

@@ -3,6 +3,7 @@ using Avalonia.Controls;
using Avalonia.Interactivity;
using LibHac.FsSystem;
using LibHac.Ncm;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;
@@ -10,6 +11,7 @@ using Ryujinx.Common;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.HOS;
using Ryujinx.Modules;
using Ryujinx.Ui.Common.Helper;
using System;
using System.Collections.Generic;
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)
{
if (Updater.CanUpdate(true))

View File

@@ -349,6 +349,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Twod
return;
}
if (srcTexture.Info.Samples > 1 || dstTexture.Info.Samples > 1)
{
srcTexture.PropagateScale(dstTexture);
}
float scale = srcTexture.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.
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 struct TexturePoolOwner
@@ -122,6 +128,8 @@ namespace Ryujinx.Graphics.Gpu.Image
private Target _arrayViewTarget;
private ITexture _flushHostTexture;
private ITexture _setHostTexture;
private int _scaledSetScore;
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>
/// Blacklists this texture from being scaled. Resets its scale to 1 if needed.
/// </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.
/// </summary>
/// <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>
/// <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)
{
@@ -564,7 +592,10 @@ namespace Ryujinx.Graphics.Gpu.Image
storage = _context.Renderer.CreateTexture(createInfo, scale);
}
HostTexture.CopyTo(storage, new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), new Extents2D(0, 0, storage.Width, storage.Height), true);
if (copy)
{
HostTexture.CopyTo(storage, new Extents2D(0, 0, HostTexture.Width, HostTexture.Height), new Extents2D(0, 0, storage.Width, storage.Height), true);
}
return storage;
}
@@ -595,7 +626,7 @@ namespace Ryujinx.Graphics.Gpu.Image
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}");
@@ -692,11 +723,6 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
public void SynchronizeFull()
{
if (_hasData)
{
BlacklistScale();
}
ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Range);
// 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);
HostTexture.SetData(result);
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);
}
_hasData = true;
}
@@ -1056,7 +1094,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (ScaleFactor != 1f)
{
// 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;
@@ -1456,6 +1494,8 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
public void SignalModified()
{
_scaledSetScore = Math.Max(0, _scaledSetScore - 1);
if (_modifiedStale || Group.HasCopyDependencies)
{
_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>
public void SignalModifying(bool bound)
{
if (bound)
{
_scaledSetScore = Math.Max(0, _scaledSetScore - 1);
}
if (_modifiedStale || Group.HasCopyDependencies)
{
_modifiedStale = false;
@@ -1685,6 +1730,9 @@ namespace Ryujinx.Graphics.Gpu.Image
_flushHostTexture?.Release();
_flushHostTexture = null;
_setHostTexture?.Release();
_setHostTexture = null;
}
/// <summary>

View File

@@ -112,7 +112,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <returns>True if eligible</returns>
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;
}

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 4318;
private const uint CodeGenVersion = 4336;
private const string SharedTocFileName = "shared.toc";
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)
{
return GenerateTernaryS32(context, operation, context.Delegates.BitFieldSExtract);
return GenerateBitfieldExtractS32(context, operation, context.Delegates.BitFieldSExtract);
}
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)
{
return GenerateQuaternaryS32(context, operation, context.Delegates.BitFieldInsert);
return GenerateBitfieldInsert(context, operation, context.Delegates.BitFieldInsert);
}
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(
CodeGenContext context,
AstOperation operation,
@@ -2322,7 +2306,23 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
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,
AstOperation operation,
Func<SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction> emitS)
@@ -2332,12 +2332,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
var src3 = operation.GetSource(2);
var src4 = operation.GetSource(3);
return new OperationResult(AggregateType.S32, emitS(
context.TypeS32(),
context.GetS32(src1),
context.GetS32(src2),
context.GetS32(src3),
context.GetS32(src4)));
return new OperationResult(AggregateType.U32, emitS(
context.TypeU32(),
context.GetU32(src1),
context.GetU32(src2),
context.GetU32(src3),
context.GetU32(src4)));
}
}
}

View File

@@ -1,5 +1,6 @@
using System;
using System.Diagnostics;
using System.Linq;
namespace Ryujinx.Graphics.Vulkan
{
@@ -16,6 +17,10 @@ namespace Ryujinx.Graphics.Vulkan
private bool _hasPendingQuery;
private int _queryCount;
private int[] _queryCountHistory = new int[3];
private int _queryCountHistoryIndex;
private int _remainingQueries;
public void RegisterFlush(ulong drawCount)
{
_lastFlush = Stopwatch.GetTimestamp();
@@ -27,6 +32,9 @@ namespace Ryujinx.Graphics.Vulkan
public bool RegisterPendingQuery()
{
_hasPendingQuery = true;
_remainingQueries--;
_queryCountHistory[_queryCountHistoryIndex]++;
// Interrupt render passes to flush queries, so that early results arrive sooner.
if (++_queryCount == InitialQueryCountForFlush)
@@ -37,6 +45,21 @@ namespace Ryujinx.Graphics.Vulkan
return false;
}
public int GetRemainingQueries()
{
if (_remainingQueries <= 0)
{
_remainingQueries = 16;
}
if (_queryCount < InitialQueryCountForFlush)
{
return Math.Min(InitialQueryCountForFlush - _queryCount, _remainingQueries);
}
return _remainingQueries;
}
public bool ShouldFlushQuery()
{
return _hasPendingQuery;
@@ -69,5 +92,14 @@ namespace Ryujinx.Graphics.Vulkan
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.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 =
MemoryPropertyFlags.DeviceLocalBit;
@@ -94,9 +100,21 @@ namespace Ryujinx.Graphics.Vulkan
gd.Api.CreateBuffer(_device, in bufferCreateInfo, null, out var buffer).ThrowOnError();
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)
{

View File

@@ -23,7 +23,7 @@ namespace Ryujinx.Graphics.Vulkan
public int[] AttachmentIndices { 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 int ColorAttachmentsCount => AttachmentsCount - (HasDepthStencil ? 1 : 0);
@@ -66,8 +66,7 @@ namespace Ryujinx.Graphics.Vulkan
AttachmentSamples = new uint[count];
AttachmentFormats = new VkFormat[count];
AttachmentIndices = new int[count];
MaxColorAttachmentIndex = colors.Length - 1;
AttachmentIndices = new int[colorsCount];
uint width = uint.MaxValue;
uint height = uint.MaxValue;

View File

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

View File

@@ -27,7 +27,16 @@ namespace Ryujinx.Graphics.Vulkan
MemoryRequirements requirements,
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)
{
return default;
@@ -56,21 +65,35 @@ namespace Ryujinx.Graphics.Vulkan
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);
for (int i = 0; i < properties.MemoryTypeCount; i++)
{
var type = properties.MemoryTypes[i];
if ((memoryTypeBits & (1 << i)) != 0 && type.PropertyFlags.HasFlag(flags))
if ((memoryTypeBits & (1 << i)) != 0)
{
return i;
if (type.PropertyFlags.HasFlag(flags))
{
return i;
}
else if (type.PropertyFlags.HasFlag(alternativeFlags))
{
bestCandidateIndex = i;
}
}
}
return -1;
return bestCandidateIndex;
}
public void Dispose()

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Vulkan
protected readonly Device Device;
public readonly PipelineCache PipelineCache;
protected readonly AutoFlushCounter AutoFlush;
public readonly AutoFlushCounter AutoFlush;
protected PipelineDynamicState DynamicState;
private PipelineState _newState;
@@ -1344,8 +1344,7 @@ namespace Ryujinx.Graphics.Vulkan
var dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan();
FramebufferParams.AttachmentFormats.CopyTo(dstAttachmentFormats);
int maxAttachmentIndex = FramebufferParams.MaxColorAttachmentIndex + (FramebufferParams.HasDepthStencil ? 1 : 0);
for (int i = FramebufferParams.AttachmentFormats.Length; i <= maxAttachmentIndex; i++)
for (int i = FramebufferParams.AttachmentFormats.Length; i < dstAttachmentFormats.Length; i++)
{
dstAttachmentFormats[i] = 0;
}
@@ -1376,8 +1375,6 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < FramebufferParams.AttachmentsCount; i++)
{
int bindIndex = FramebufferParams.AttachmentIndices[i];
attachmentDescs[i] = new AttachmentDescription(
0,
FramebufferParams.AttachmentFormats[i],

View File

@@ -27,14 +27,11 @@ namespace Ryujinx.Graphics.Vulkan
int attachmentCount = 0;
int colorCount = 0;
int maxColorAttachmentIndex = 0;
for (int i = 0; i < state.AttachmentEnable.Length; i++)
{
if (state.AttachmentEnable[i])
{
maxColorAttachmentIndex = i;
attachmentFormats[attachmentCount] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]);
attachmentIndices[attachmentCount++] = i;
@@ -270,7 +267,7 @@ namespace Ryujinx.Graphics.Vulkan
// 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];
@@ -293,21 +290,24 @@ namespace Ryujinx.Graphics.Vulkan
}
}
int maxAttachmentIndex = 0;
for (int i = 0; i < 8; i++)
int attachmentCount = 0;
int maxColorAttachmentIndex = -1;
for (int i = 0; i < Constants.MaxRenderTargets; 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)
{
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);
return pipeline;

View File

@@ -14,7 +14,6 @@ namespace Ryujinx.Graphics.Vulkan
private CounterQueueEvent _activeConditionalRender;
private readonly List<BufferedQuery> _pendingQueryCopies;
private readonly List<BufferedQuery> _pendingQueryResets;
private ulong _byteWeight;
@@ -22,7 +21,6 @@ namespace Ryujinx.Graphics.Vulkan
{
_activeQueries = new List<QueryPool>();
_pendingQueryCopies = new();
_pendingQueryResets = new List<BufferedQuery>();
CommandBuffer = (Cbs = gd.CommandBufferPool.Rent()).CommandBuffer;
}
@@ -34,16 +32,6 @@ namespace Ryujinx.Graphics.Vulkan
query.PoolCopy(Cbs);
}
lock (_pendingQueryResets)
{
foreach (var query in _pendingQueryResets)
{
query.PoolReset(CommandBuffer);
}
_pendingQueryResets.Clear();
}
_pendingQueryCopies.Clear();
}
@@ -238,10 +226,12 @@ namespace Ryujinx.Graphics.Vulkan
Gd.Api.CmdBeginQuery(CommandBuffer, queryPool, 0, Gd.Capabilities.SupportsPreciseOcclusionQueries ? QueryControlFlags.PreciseBit : 0);
}
Gd.ResetCounterPool();
Restore();
}
public void BeginQuery(BufferedQuery query, QueryPool pool, bool needsReset)
public void BeginQuery(BufferedQuery query, QueryPool pool, bool needsReset, bool fromSamplePool)
{
if (needsReset)
{
@@ -249,9 +239,11 @@ namespace Ryujinx.Graphics.Vulkan
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);
}
public void ResetQuery(BufferedQuery query)
{
lock (_pendingQueryResets)
{
_pendingQueryResets.Add(query);
}
}
public void CopyQueryResults(BufferedQuery query)
{
_pendingQueryCopies.Add(query);

View File

@@ -18,7 +18,6 @@ namespace Ryujinx.Graphics.Vulkan.Queries
private readonly PipelineFull _pipeline;
private QueryPool _queryPool;
private bool _isReset;
private readonly BufferHolder _buffer;
private readonly IntPtr _bufferMap;
@@ -27,6 +26,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
private bool _isSupported;
private long _defaultValue;
private int? _resetSequence;
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
{
CounterType.SamplesPassed => true,
CounterType.PrimitivesGenerated => gd.Capabilities.SupportsPipelineStatisticsQuery,
CounterType.TransformFeedbackPrimitivesWritten => gd.Capabilities.SupportsTransformFeedbackQueries,
CounterType.PrimitivesGenerated => gd.Capabilities.SupportsGeometryShader,
_ => false
};
}
@@ -92,16 +92,17 @@ namespace Ryujinx.Graphics.Vulkan.Queries
public void Reset()
{
End(false);
Begin();
Begin(null);
}
public void Begin()
public void Begin(int? resetSequence)
{
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)
@@ -162,13 +163,14 @@ namespace Ryujinx.Graphics.Vulkan.Queries
return data;
}
public void PoolReset(CommandBuffer cmd)
public void PoolReset(CommandBuffer cmd, int resetSequence)
{
if (_isSupported)
{
_api.CmdResetQueryPool(cmd, _queryPool, 0, 1);
}
_isReset = true;
_resetSequence = resetSequence;
}
public void PoolCopy(CommandBufferScoped cbs)

View File

@@ -3,6 +3,7 @@ using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Linq;
namespace Ryujinx.Graphics.Vulkan.Queries
{
@@ -32,6 +33,8 @@ namespace Ryujinx.Graphics.Vulkan.Queries
private Thread _consumerThread;
public int ResetSequence { get; private set; }
internal CounterQueue(VulkanRenderer gd, Device device, PipelineFull pipeline, CounterType type)
{
_gd = gd;
@@ -53,6 +56,24 @@ namespace Ryujinx.Graphics.Vulkan.Queries
_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()
{
while (!Disposed)
@@ -106,7 +127,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
{
lock (_lock)
{
_pipeline.ResetQuery(query);
// The query will be reset when it dequeues.
_queryPool.Enqueue(query);
}
}

View File

@@ -34,7 +34,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
DrawIndex = drawIndex;
_counter.Begin();
_counter.Begin(_queue.ResetSequence);
}
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)
{
return _counterQueues[(int)type].QueueReport(resultHandler, _pipeline.DrawCount, hostReserved);

View File

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

View File

@@ -162,7 +162,6 @@ namespace Ryujinx.Graphics.Vulkan
if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.ErrorBitExt))
{
Logger.Error?.Print(LogClass.Gpu, msg);
//throw new Exception(msg);
}
else if (messageSeverity.HasFlag(DebugUtilsMessageSeverityFlagsEXT.WarningBitExt))
{
@@ -379,14 +378,34 @@ namespace Ryujinx.Graphics.Vulkan
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"))
{
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()
@@ -408,42 +427,49 @@ namespace Ryujinx.Graphics.Vulkan
var features = new PhysicalDeviceFeatures()
{
DepthBiasClamp = true,
DepthClamp = true,
DualSrcBlend = true,
DepthClamp = supportedFeatures.DepthClamp,
DualSrcBlend = supportedFeatures.DualSrcBlend,
FragmentStoresAndAtomics = true,
GeometryShader = supportedFeatures.GeometryShader,
ImageCubeArray = true,
IndependentBlend = true,
LogicOp = supportedFeatures.LogicOp,
MultiViewport = true,
OcclusionQueryPrecise = supportedFeatures.OcclusionQueryPrecise,
MultiViewport = supportedFeatures.MultiViewport,
PipelineStatisticsQuery = supportedFeatures.PipelineStatisticsQuery,
SamplerAnisotropy = true,
ShaderClipDistance = true,
ShaderFloat64 = supportedFeatures.ShaderFloat64,
ShaderImageGatherExtended = true,
ShaderImageGatherExtended = supportedFeatures.ShaderImageGatherExtended,
ShaderStorageImageMultisample = supportedFeatures.ShaderStorageImageMultisample,
// ShaderStorageImageReadWithoutFormat = true,
// ShaderStorageImageWriteWithoutFormat = true,
TessellationShader = true,
TessellationShader = supportedFeatures.TessellationShader,
VertexPipelineStoresAndAtomics = true,
RobustBufferAccess = useRobustBufferAccess
};
void* pExtendedFeatures = null;
var featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT()
{
SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt,
PNext = pExtendedFeatures,
TransformFeedback = true
};
PhysicalDeviceTransformFeedbackFeaturesEXT featuresTransformFeedback;
pExtendedFeatures = &featuresTransformFeedback;
if (supportedExtensions.Contains(ExtTransformFeedback.ExtensionName))
{
featuresTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT()
{
SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt,
PNext = pExtendedFeatures,
TransformFeedback = supportedFeaturesTransformFeedback.TransformFeedback
};
pExtendedFeatures = &featuresTransformFeedback;
}
PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2;
if (supportedExtensions.Contains("VK_EXT_robustness2"))
{
var featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT()
featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT()
{
SType = StructureType.PhysicalDeviceRobustness2FeaturesExt,
PNext = pExtendedFeatures,
@@ -466,7 +492,7 @@ namespace Ryujinx.Graphics.Vulkan
{
SType = StructureType.PhysicalDeviceVulkan11Features,
PNext = pExtendedFeatures,
ShaderDrawParameters = true
ShaderDrawParameters = supportedFeaturesVk11.ShaderDrawParameters
};
pExtendedFeatures = &featuresVk11;
@@ -527,8 +553,8 @@ namespace Ryujinx.Graphics.Vulkan
PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor;
if (supportedExtensions.Contains("VK_EXT_custom_border_color") &&
featuresCustomBorderColorSupported.CustomBorderColors &&
featuresCustomBorderColorSupported.CustomBorderColorWithoutFormat)
supportedFeaturesCustomBorderColor.CustomBorderColors &&
supportedFeaturesCustomBorderColor.CustomBorderColorWithoutFormat)
{
featuresCustomBorderColor = new PhysicalDeviceCustomBorderColorFeaturesEXT()
{

View File

@@ -271,6 +271,7 @@ namespace Ryujinx.Graphics.Vulkan
supportsTransformFeedback,
propertiesTransformFeedback.TransformFeedbackQueries,
features2.Features.OcclusionQueryPrecise,
supportedFeatures.PipelineStatisticsQuery,
supportedFeatures.GeometryShader,
propertiesSubgroupSizeControl.MinSubgroupSize,
propertiesSubgroupSizeControl.MaxSubgroupSize,
@@ -589,9 +590,13 @@ namespace Ryujinx.Graphics.Vulkan
Vendor = VendorUtils.FromId(properties.VendorID);
IsAmdWindows = Vendor == Vendor.Amd && RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
IsIntelWindows = Vendor == Vendor.Intel && RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
IsTBDR = IsMoltenVk || Vendor == Vendor.Qualcomm || Vendor == Vendor.ARM || Vendor == Vendor.ImgTec;
IsAmdWindows = Vendor == Vendor.Amd && OperatingSystem.IsWindows();
IsIntelWindows = Vendor == Vendor.Intel && OperatingSystem.IsWindows();
IsTBDR = IsMoltenVk ||
Vendor == Vendor.Qualcomm ||
Vendor == Vendor.ARM ||
Vendor == Vendor.Broadcom ||
Vendor == Vendor.ImgTec;
GpuVendor = vendorName;
GpuRenderer = Marshal.PtrToStringAnsi((IntPtr)properties.DeviceName);
@@ -679,6 +684,16 @@ namespace Ryujinx.Graphics.Vulkan
_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)
{
action();

View File

@@ -120,7 +120,7 @@ namespace Ryujinx.Graphics.Vulkan
ImageSharingMode = SharingMode.Exclusive,
ImageArrayLayers = 1,
PreTransform = capabilities.CurrentTransform,
CompositeAlpha = CompositeAlphaFlagsKHR.OpaqueBitKhr,
CompositeAlpha = ChooseCompositeAlpha(capabilities.SupportedCompositeAlpha),
PresentMode = ChooseSwapPresentMode(presentModes, _vsyncEnabled),
Clipped = true
};
@@ -188,6 +188,22 @@ namespace Ryujinx.Graphics.Vulkan
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)
{
if (!vsyncEnabled && availablePresentModes.Contains(PresentModeKHR.ImmediateKhr))
@@ -198,10 +214,6 @@ namespace Ryujinx.Graphics.Vulkan
{
return PresentModeKHR.MailboxKhr;
}
else if (availablePresentModes.Contains(PresentModeKHR.FifoKhr))
{
return PresentModeKHR.FifoKhr;
}
else
{
return PresentModeKHR.FifoKhr;
@@ -225,6 +237,8 @@ namespace Ryujinx.Graphics.Vulkan
public unsafe override void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback)
{
_gd.PipelineInternal.AutoFlush.Present();
uint nextImage = 0;
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);
return true;
return configurationFileFormat.Version != 0;
}
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.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading.Tasks;
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)
{
Version = ReleaseInformation.GetVersion();
@@ -189,12 +146,6 @@ namespace Ryujinx
// Initialize the logger system.
LoggerModule.Initialize();
// Register mime types on linux.
if (OperatingSystem.IsLinux())
{
RegisterMimeTypes();
}
// Initialize Discord integration.
DiscordIntegrationModule.Initialize();

View File

@@ -44,7 +44,6 @@ using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using GUI = Gtk.Builder.ObjectAttribute;
using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState;
@@ -109,6 +108,7 @@ namespace Ryujinx.Ui
[GUI] CheckMenuItem _favToggle;
[GUI] MenuItem _firmwareInstallDirectory;
[GUI] MenuItem _firmwareInstallFile;
[GUI] MenuItem _fileTypesSubMenu;
[GUI] Label _fifoStatus;
[GUI] CheckMenuItem _iconToggle;
[GUI] CheckMenuItem _developerToggle;
@@ -220,6 +220,8 @@ namespace Ryujinx.Ui
_pauseEmulation.Sensitive = false;
_resumeEmulation.Sensitive = false;
_fileTypesSubMenu.Visible = FileAssociationHelper.IsTypeAssociationSupported;
if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) _favToggle.Active = true;
if (ConfigurationState.Instance.Ui.GuiColumns.IconColumn) _iconToggle.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()
{
if (_userChannelPersistence.PreviousIndex != -1 && _userChannelPersistence.ShouldRestart)

View File

@@ -1,67 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.21.0 -->
<!-- Generated with glade 3.40.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<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="window_position">center</property>
<property name="window-position">center</property>
<child>
<object class="GtkBox" id="_box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkMenuBar" id="_menuBar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkMenuItem" id="_fileMenu">
<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="use_underline">True</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkMenuItem" id="_loadApplicationFile">
<property name="visible">True</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="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="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"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_loadApplicationFolder">
<property name="visible">True</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="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="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"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_appletMenu">
<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="use_underline">True</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkMenuItem" id="LoadMiiEditApplet">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Open Mii Editor Applet in Standalone mode</property>
<property name="can-focus">False</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="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="Load_Mii_Edit_Applet" swapped="no"/>
</object>
</child>
@@ -72,42 +72,42 @@
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="OpenRyuFolder">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Open Ryujinx filesystem folder</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Open Ryujinx filesystem 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"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="OpenLogsFolder">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Opens the folder where logs are written to.</property>
<property name="can-focus">False</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="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="OpenLogsFolder_Pressed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="ExitMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Exit Ryujinx</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Exit Ryujinx</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"/>
</object>
</child>
@@ -118,144 +118,144 @@
<child>
<object class="GtkMenuItem" id="_optionMenu">
<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="use_underline">True</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkMenuItem" id="_fullScreen">
<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="use_underline">True</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="_startFullScreen">
<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="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="toggled" handler="StartFullScreen_Toggled" swapped="no"/>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="_showConsole">
<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="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="toggled" handler="ShowConsole_Toggled" swapped="no"/>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="GUIColumns">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Select which GUI columns to enable</property>
<property name="can-focus">False</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="use_underline">True</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkCheckMenuItem" id="_favToggle">
<property name="visible">True</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="can-focus">False</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="use_underline">True</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="_iconToggle">
<property name="visible">True</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="can-focus">False</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="use_underline">True</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="_appToggle">
<property name="visible">True</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="can-focus">False</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="use_underline">True</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="_developerToggle">
<property name="visible">True</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="can-focus">False</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="use_underline">True</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="_versionToggle">
<property name="visible">True</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="can-focus">False</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="use_underline">True</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="_timePlayedToggle">
<property name="visible">True</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="can-focus">False</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="use_underline">True</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="_lastPlayedToggle">
<property name="visible">True</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="can-focus">False</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="use_underline">True</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="_fileExtToggle">
<property name="visible">True</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="can-focus">False</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="use_underline">True</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="_fileSizeToggle">
<property name="visible">True</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="can-focus">False</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="use_underline">True</property>
<property name="use-underline">True</property>
</object>
</child>
<child>
<object class="GtkCheckMenuItem" id="_pathToggle">
<property name="visible">True</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="can-focus">False</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="use_underline">True</property>
<property name="use-underline">True</property>
</object>
</child>
</object>
@@ -265,26 +265,26 @@
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="SettingsMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Open settings window</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Open settings window</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"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_manageUserProfiles">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Open User Profiles Manager window</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Open User Profiles Manager window</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"/>
</object>
</child>
@@ -295,74 +295,74 @@
<child>
<object class="GtkMenuItem" id="_actionMenu">
<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="use_underline">True</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkMenuItem" id="_pauseEmulation">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Pause emulation</property>
<property name="can-focus">False</property>
<property name="tooltip-text" 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"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_resumeEmulation">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Resume emulation</property>
<property name="can-focus">False</property>
<property name="tooltip-text" 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"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_stopEmulation">
<property name="visible">True</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="can-focus">False</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="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="StopEmulation_Pressed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_simulateWakeUpMessage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Simulate a Wake-up Message</property>
<property name="can-focus">False</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="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="Simulate_WakeUp_Message_Pressed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_scanAmiibo">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Scan an Amiibo</property>
<property name="can-focus">False</property>
<property name="tooltip-text" 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"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_takeScreenshot">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Take a screenshot</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Take a screenshot</property>
<property name="label" translatable="yes">Take Screenshot</property>
<signal name="activate" handler="Take_Screenshot" swapped="no"/>
</object>
@@ -370,16 +370,16 @@
<child>
<object class="GtkMenuItem" id="_hideUi">
<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="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="HideUi_Pressed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_manageCheats">
<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>
<signal name="activate" handler="ManageCheats_Pressed" swapped="no"/>
</object>
@@ -391,38 +391,38 @@
<child>
<object class="GtkMenuItem" id="_toolsMenu">
<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="use_underline">True</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkMenuItem" id="FirmwareSubMenu">
<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="use_underline">True</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkMenuItem" id="_firmwareInstallFile">
<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="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="Installer_File_Pressed" swapped="no"/>
</object>
</child>
<child>
<object class="GtkMenuItem" id="_firmwareInstallDirectory">
<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="use_underline">True</property>
<property name="use-underline">True</property>
<signal name="activate" handler="Installer_Directory_Pressed" swapped="no"/>
</object>
</child>
@@ -430,6 +430,36 @@
</child>
</object>
</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>
</child>
</object>
@@ -437,36 +467,36 @@
<child>
<object class="GtkMenuItem" id="HelpMenu">
<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="use_underline">True</property>
<property name="use-underline">True</property>
<child type="submenu">
<object class="GtkMenu">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkMenuItem" id="UpdateMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Check for updates to Ryujinx</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Check for updates to Ryujinx</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"/>
</object>
</child>
<child>
<object class="GtkSeparatorMenuItem">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
</child>
<child>
<object class="GtkMenuItem" id="About">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="tooltip_text" translatable="yes">Open about window</property>
<property name="can-focus">False</property>
<property name="tooltip-text" translatable="yes">Open about window</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"/>
</object>
</child>
@@ -484,24 +514,24 @@
<child>
<object class="GtkBox" id="_mainBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkBox" id="_viewBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkScrolledWindow" id="_gameTableWindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="shadow_type">in</property>
<property name="can-focus">True</property>
<property name="shadow-type">in</property>
<child>
<object class="GtkTreeView" id="_gameTable">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="can-focus">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"/>
<child internal-child="selection">
<object class="GtkTreeSelection" id="_gameTableSelection"/>
@@ -524,24 +554,24 @@
</child>
<child>
<object class="GtkBox" id="_footerBox">
<property name="height_request">19</property>
<property name="height-request">19</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkBox" id="_listStatusBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkEventBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="can-focus">False</property>
<property name="margin-left">5</property>
<signal name="button-release-event" handler="RefreshList_Pressed" swapped="no"/>
<child>
<object class="GtkImage">
<property name="name">RefreshList</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="stock">gtk-refresh</property>
</object>
</child>
@@ -555,11 +585,11 @@
<child>
<object class="GtkLabel" id="_progressLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">10</property>
<property name="margin_right">5</property>
<property name="margin_top">2</property>
<property name="margin_bottom">2</property>
<property name="can-focus">False</property>
<property name="margin-left">10</property>
<property name="margin-right">5</property>
<property name="margin-top">2</property>
<property name="margin-bottom">2</property>
<property name="label" translatable="yes">0/0 Games Loaded</property>
</object>
<packing>
@@ -570,13 +600,13 @@
</child>
<child>
<object class="GtkProgressBar" id="_progressBar">
<property name="width_request">200</property>
<property name="width-request">200</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="margin_left">10</property>
<property name="margin_right">5</property>
<property name="margin_bottom">6</property>
<property name="margin-left">10</property>
<property name="margin-right">5</property>
<property name="margin-bottom">6</property>
</object>
<packing>
<property name="expand">True</property>
@@ -594,19 +624,19 @@
<child>
<object class="GtkBox" id="_statusBar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<child>
<object class="GtkEventBox">
<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"/>
<child>
<object class="GtkLabel" id="_vSyncStatus">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin-left">5</property>
<property name="margin-right">5</property>
<property name="label" translatable="yes">VSync</property>
</object>
</child>
@@ -620,7 +650,7 @@
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -631,15 +661,15 @@
<child>
<object class="GtkEventBox">
<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"/>
<child>
<object class="GtkLabel" id="_dockedMode">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin-left">5</property>
<property name="margin-right">5</property>
</object>
</child>
</object>
@@ -652,7 +682,7 @@
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -663,15 +693,15 @@
<child>
<object class="GtkEventBox">
<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"/>
<child>
<object class="GtkLabel" id="_volumeStatus">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin-left">5</property>
<property name="margin-right">5</property>
</object>
</child>
</object>
@@ -684,7 +714,7 @@
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -695,15 +725,15 @@
<child>
<object class="GtkEventBox">
<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"/>
<child>
<object class="GtkLabel" id="_aspectRatio">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin-left">5</property>
<property name="margin-right">5</property>
</object>
</child>
</object>
@@ -716,7 +746,7 @@
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -727,10 +757,10 @@
<child>
<object class="GtkLabel" id="_gameStatus">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin-left">5</property>
<property name="margin-right">5</property>
</object>
<packing>
<property name="expand">False</property>
@@ -741,7 +771,7 @@
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -752,10 +782,10 @@
<child>
<object class="GtkLabel" id="_fifoStatus">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin-left">5</property>
<property name="margin-right">5</property>
</object>
<packing>
<property name="expand">False</property>
@@ -766,7 +796,7 @@
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -777,10 +807,10 @@
<child>
<object class="GtkLabel" id="_gpuBackend">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin-left">5</property>
<property name="margin-right">5</property>
</object>
<packing>
<property name="expand">False</property>
@@ -791,7 +821,7 @@
<child>
<object class="GtkSeparator">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
</object>
<packing>
<property name="expand">False</property>
@@ -802,10 +832,10 @@
<child>
<object class="GtkLabel" id="_gpuName">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="can-focus">False</property>
<property name="halign">start</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin-left">5</property>
<property name="margin-right">5</property>
</object>
<packing>
<property name="expand">True</property>
@@ -823,12 +853,12 @@
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="can-focus">False</property>
<property name="margin-left">5</property>
<child>
<object class="GtkLabel">
<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>
</object>
<packing>
@@ -839,16 +869,16 @@
</child>
<child>
<object class="GtkLabel" id="_firmwareVersionLabel">
<property name="width_request">50</property>
<property name="width-request">50</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="can-focus">False</property>
<property name="margin-left">5</property>
<property name="margin-right">5</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="pack-type">end</property>
<property name="position">1</property>
</packing>
</child>
@@ -856,15 +886,15 @@
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="pack_type">end</property>
<property name="pack-type">end</property>
<property name="position">4</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="_loadingStatusLabel">
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="can-focus">False</property>
<property name="margin-left">5</property>
<property name="margin-right">5</property>
<property name="label" translatable="yes">0/0 </property>
</object>
<packing>
@@ -875,11 +905,11 @@
</child>
<child>
<object class="GtkProgressBar" id="_loadingStatusBar">
<property name="width_request">200</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_bottom">6</property>
<property name="width-request">200</property>
<property name="can-focus">False</property>
<property name="margin-left">5</property>
<property name="margin-right">5</property>
<property name="margin-bottom">6</property>
</object>
<packing>
<property name="expand">False</property>
@@ -903,8 +933,5 @@
</child>
</object>
</child>
<child type="titlebar">
<placeholder/>
</child>
</object>
</interface>