Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
31ed061bea | |||
4218311e6a | |||
e37735ed26 | |||
74a18b7c18 | |||
74fe814329 | |||
d1a093e5ca | |||
dfb14a5607 | |||
904a5ffcb4 | |||
946633276b | |||
baf94e0e3e | |||
cf6201a4a6 | |||
18909195d1 | |||
f06d22d6f0 | |||
84d6e8d121 | |||
95c4912d58 | |||
356a75af0b | |||
4ae9921063 | |||
6a8ac389e5 |
2
.github/labeler.yml
vendored
2
.github/labeler.yml
vendored
@ -20,7 +20,7 @@ gpu:
|
||||
|
||||
gui:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file: ['src/Ryujinx/**', 'src/Ryujinx.Ui.Common/**', 'src/Ryujinx.Ui.LocaleGenerator/**', 'src/Ryujinx.Ava/**']
|
||||
- any-glob-to-any-file: ['src/Ryujinx/**', 'src/Ryujinx.UI.Common/**', 'src/Ryujinx.UI.LocaleGenerator/**', 'src/Ryujinx.Ava/**']
|
||||
|
||||
horizon:
|
||||
- changed-files:
|
||||
|
72
.github/workflows/flatpak.yml
vendored
72
.github/workflows/flatpak.yml
vendored
@ -51,38 +51,76 @@ jobs:
|
||||
- name: Restore Nuget packages
|
||||
# With .NET 8.0.100, Microsoft.NET.ILLink.Tasks isn't restored by default and only seems to appears when publishing.
|
||||
# So we just publish to grab the dependencies
|
||||
run: dotnet publish -c Release -r linux-x64 Ryujinx/${{ env.RYUJINX_PROJECT_FILE }} --self-contained
|
||||
run: |
|
||||
dotnet publish -c Release -r linux-x64 Ryujinx/${{ env.RYUJINX_PROJECT_FILE }} --self-contained
|
||||
dotnet publish -c Release -r linux-arm64 Ryujinx/${{ env.RYUJINX_PROJECT_FILE }} --self-contained
|
||||
|
||||
- name: Generate nuget_sources.json
|
||||
shell: python
|
||||
run: |
|
||||
import hashlib
|
||||
from pathlib import Path
|
||||
import base64
|
||||
import binascii
|
||||
import json
|
||||
import os
|
||||
import urllib.request
|
||||
|
||||
sources = []
|
||||
|
||||
for path in Path(os.environ['NUGET_PACKAGES']).glob('**/*.nupkg.sha512'):
|
||||
name = path.parent.parent.name
|
||||
version = path.parent.name
|
||||
filename = '{}.{}.nupkg'.format(name, version)
|
||||
url = 'https://api.nuget.org/v3-flatcontainer/{}/{}/{}'.format(name, version, filename)
|
||||
|
||||
with path.open() as fp:
|
||||
sha512 = binascii.hexlify(base64.b64decode(fp.read())).decode('ascii')
|
||||
def create_source_from_external(name, version):
|
||||
full_dir_path = Path(os.environ["NUGET_PACKAGES"]).joinpath(name).joinpath(version)
|
||||
os.makedirs(full_dir_path, exist_ok=True)
|
||||
|
||||
sources.append({
|
||||
'type': 'file',
|
||||
'url': url,
|
||||
'sha512': sha512,
|
||||
'dest': os.environ['NUGET_SOURCES_DESTDIR'],
|
||||
'dest-filename': filename,
|
||||
})
|
||||
filename = "{}.{}.nupkg".format(name, version)
|
||||
url = "https://api.nuget.org/v3-flatcontainer/{}/{}/{}".format(
|
||||
name, version, filename
|
||||
)
|
||||
|
||||
with open('flathub/nuget_sources.json', 'w') as fp:
|
||||
json.dump(sources, fp, indent=4)
|
||||
print(f"Processing {url}...")
|
||||
response = urllib.request.urlopen(url)
|
||||
sha512 = hashlib.sha512(response.read()).hexdigest()
|
||||
|
||||
return {
|
||||
"type": "file",
|
||||
"url": url,
|
||||
"sha512": sha512,
|
||||
"dest": os.environ["NUGET_SOURCES_DESTDIR"],
|
||||
"dest-filename": filename,
|
||||
}
|
||||
|
||||
|
||||
has_added_x64_apphost = False
|
||||
|
||||
for path in Path(os.environ["NUGET_PACKAGES"]).glob("**/*.nupkg.sha512"):
|
||||
name = path.parent.parent.name
|
||||
version = path.parent.name
|
||||
filename = "{}.{}.nupkg".format(name, version)
|
||||
url = "https://api.nuget.org/v3-flatcontainer/{}/{}/{}".format(
|
||||
name, version, filename
|
||||
)
|
||||
|
||||
with path.open() as fp:
|
||||
sha512 = binascii.hexlify(base64.b64decode(fp.read())).decode("ascii")
|
||||
|
||||
sources.append(
|
||||
{
|
||||
"type": "file",
|
||||
"url": url,
|
||||
"sha512": sha512,
|
||||
"dest": os.environ["NUGET_SOURCES_DESTDIR"],
|
||||
"dest-filename": filename,
|
||||
}
|
||||
)
|
||||
|
||||
# .NET will not add current installed application host to the list, force inject it here.
|
||||
if not has_added_x64_apphost and name.startswith('microsoft.netcore.app.host'):
|
||||
sources.append(create_source_from_external("microsoft.netcore.app.host.linux-x64", version))
|
||||
has_added_x64_apphost = True
|
||||
|
||||
with open("flathub/nuget_sources.json", "w") as fp:
|
||||
json.dump(sources, fp, indent=4)
|
||||
|
||||
- name: Update flatpak metadata
|
||||
id: metadata
|
||||
|
@ -71,7 +71,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.FFmp
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ava", "src\Ryujinx.Ava\Ryujinx.Ava.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ui.Common", "src\Ryujinx.Ui.Common\Ryujinx.Ui.Common.csproj", "{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.UI.Common", "src\Ryujinx.UI.Common\Ryujinx.UI.Common.csproj", "{BA161CA0-CD65-4E6E-B644-51C8D1E542DC}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Generators", "src\Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj", "{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}"
|
||||
EndProject
|
||||
@ -79,7 +79,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vulkan", "
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spv.Generator", "src\Spv.Generator\Spv.Generator.csproj", "{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ui.LocaleGenerator", "src\Ryujinx.Ui.LocaleGenerator\Ryujinx.Ui.LocaleGenerator.csproj", "{77D01AD9-2C98-478E-AE1D-8F7100738FB4}"
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.UI.LocaleGenerator", "src\Ryujinx.UI.LocaleGenerator\Ryujinx.UI.LocaleGenerator.csproj", "{77D01AD9-2C98-478E-AE1D-8F7100738FB4}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Common", "src\Ryujinx.Horizon.Common\Ryujinx.Horizon.Common.csproj", "{77F96ECE-4952-42DB-A528-DED25572A573}"
|
||||
EndProject
|
||||
|
@ -1,14 +1,21 @@
|
||||
#!/bin/sh
|
||||
|
||||
SCRIPT_DIR=$(dirname "$(realpath "$0")")
|
||||
RYUJINX_BIN="Ryujinx"
|
||||
|
||||
if [ -f "$SCRIPT_DIR/Ryujinx.Headless.SDL2" ]; then
|
||||
RYUJINX_BIN="Ryujinx.Headless.SDL2"
|
||||
fi
|
||||
|
||||
if [ -f "$SCRIPT_DIR/Ryujinx.Ava" ]; then
|
||||
RYUJINX_BIN="Ryujinx.Ava"
|
||||
fi
|
||||
|
||||
if [ -f "$SCRIPT_DIR/Ryujinx.Headless.SDL2" ]; then
|
||||
RYUJINX_BIN="Ryujinx.Headless.SDL2"
|
||||
if [ -f "$SCRIPT_DIR/Ryujinx" ]; then
|
||||
RYUJINX_BIN="Ryujinx"
|
||||
fi
|
||||
|
||||
if [ -z "$RYUJINX_BIN" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
COMMAND="env DOTNET_EnableAlternateStackCheck=1"
|
||||
@ -17,4 +24,4 @@ if command -v gamemoderun > /dev/null 2>&1; then
|
||||
COMMAND="$COMMAND gamemoderun"
|
||||
fi
|
||||
|
||||
$COMMAND "$SCRIPT_DIR/$RYUJINX_BIN" "$@"
|
||||
exec $COMMAND "$SCRIPT_DIR/$RYUJINX_BIN" "$@"
|
||||
|
@ -8,8 +8,8 @@ using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
@ -42,9 +42,9 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
ApplyConfiguredTheme();
|
||||
|
||||
ConfigurationState.Instance.Ui.BaseStyle.Event += ThemeChanged_Event;
|
||||
ConfigurationState.Instance.Ui.CustomThemePath.Event += ThemeChanged_Event;
|
||||
ConfigurationState.Instance.Ui.EnableCustomTheme.Event += CustomThemeChanged_Event;
|
||||
ConfigurationState.Instance.UI.BaseStyle.Event += ThemeChanged_Event;
|
||||
ConfigurationState.Instance.UI.CustomThemePath.Event += ThemeChanged_Event;
|
||||
ConfigurationState.Instance.UI.EnableCustomTheme.Event += CustomThemeChanged_Event;
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,13 +88,13 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
try
|
||||
{
|
||||
string baseStyle = ConfigurationState.Instance.Ui.BaseStyle;
|
||||
string baseStyle = ConfigurationState.Instance.UI.BaseStyle;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(baseStyle))
|
||||
{
|
||||
ConfigurationState.Instance.Ui.BaseStyle.Value = "Dark";
|
||||
ConfigurationState.Instance.UI.BaseStyle.Value = "Dark";
|
||||
|
||||
baseStyle = ConfigurationState.Instance.Ui.BaseStyle;
|
||||
baseStyle = ConfigurationState.Instance.UI.BaseStyle;
|
||||
}
|
||||
|
||||
RequestedThemeVariant = baseStyle switch
|
||||
|
@ -34,10 +34,10 @@ using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using Ryujinx.Input;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.Ui.App.Common;
|
||||
using Ryujinx.Ui.Common;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using Ryujinx.UI.App.Common;
|
||||
using Ryujinx.UI.Common;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using Silk.NET.Vulkan;
|
||||
using SixLabors.ImageSharp;
|
||||
using SixLabors.ImageSharp.Formats.Png;
|
||||
@ -1070,7 +1070,7 @@ namespace Ryujinx.Ava
|
||||
case KeyboardHotkeyState.Screenshot:
|
||||
ScreenshotRequested = true;
|
||||
break;
|
||||
case KeyboardHotkeyState.ShowUi:
|
||||
case KeyboardHotkeyState.ShowUI:
|
||||
_viewModel.ShowMenuAndStatusBar = !_viewModel.ShowMenuAndStatusBar;
|
||||
break;
|
||||
case KeyboardHotkeyState.Pause:
|
||||
@ -1160,9 +1160,9 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
state = KeyboardHotkeyState.Screenshot;
|
||||
}
|
||||
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi))
|
||||
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUI))
|
||||
{
|
||||
state = KeyboardHotkeyState.ShowUi;
|
||||
state = KeyboardHotkeyState.ShowUI;
|
||||
}
|
||||
else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause))
|
||||
{
|
||||
|
@ -73,6 +73,10 @@
|
||||
"GameListContextMenuCreateShortcut": "Create Application Shortcut",
|
||||
"GameListContextMenuCreateShortcutToolTip": "Create a Desktop Shortcut that launches the selected Application",
|
||||
"GameListContextMenuCreateShortcutToolTipMacOS": "Create a shortcut in macOS's Applications folder that launches the selected Application",
|
||||
"GameListContextMenuOpenModsDirectory": "Open Mods Directory",
|
||||
"GameListContextMenuOpenModsDirectoryToolTip": "Opens the directory which contains Application's Mods",
|
||||
"GameListContextMenuOpenSdModsDirectory": "Open Atmosphere Mods Directory",
|
||||
"GameListContextMenuOpenSdModsDirectoryToolTip": "Opens the alternative SD card Atmosphere directory which contains Application's Mods. Useful for mods that are packaged for real hardware.",
|
||||
"StatusBarGamesLoaded": "{0}/{1} Games Loaded",
|
||||
"StatusBarSystemVersion": "System Version: {0}",
|
||||
"LinuxVmMaxMapCountDialogTitle": "Low limit for memory mappings detected",
|
||||
@ -329,8 +333,6 @@
|
||||
"DialogUpdaterAddingFilesMessage": "Adding New Update...",
|
||||
"DialogUpdaterCompleteMessage": "Update Complete!",
|
||||
"DialogUpdaterRestartMessage": "Do you want to restart Ryujinx now?",
|
||||
"DialogUpdaterArchNotSupportedMessage": "You are not running a supported system architecture!",
|
||||
"DialogUpdaterArchNotSupportedSubMessage": "(Only x64 systems are supported!)",
|
||||
"DialogUpdaterNoInternetMessage": "You are not connected to the Internet!",
|
||||
"DialogUpdaterNoInternetSubMessage": "Please verify that you have a working Internet connection!",
|
||||
"DialogUpdaterDirtyBuildMessage": "You Cannot update a Dirty build of Ryujinx!",
|
||||
|
@ -18,8 +18,8 @@ using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.Ui.App.Common;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using Ryujinx.UI.App.Common;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.IO;
|
||||
|
@ -5,7 +5,7 @@ namespace Ryujinx.Ava.Common
|
||||
None,
|
||||
ToggleVSync,
|
||||
Screenshot,
|
||||
ShowUi,
|
||||
ShowUI,
|
||||
Pause,
|
||||
ToggleMute,
|
||||
ResScaleUp,
|
||||
|
@ -1,7 +1,7 @@
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
@ -38,9 +38,9 @@ namespace Ryujinx.Ava.Common.Locale
|
||||
// If the view is loaded with the UI Previewer detached, then override it with the saved one or default.
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(ConfigurationState.Instance.Ui.LanguageCode.Value))
|
||||
if (!string.IsNullOrEmpty(ConfigurationState.Instance.UI.LanguageCode.Value))
|
||||
{
|
||||
localeLanguageCode = ConfigurationState.Instance.Ui.LanguageCode.Value;
|
||||
localeLanguageCode = ConfigurationState.Instance.UI.LanguageCode.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -10,8 +10,8 @@ using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using Ryujinx.Ui.Common.Models.Github;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using Ryujinx.UI.Common.Models.Github;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
@ -68,7 +68,8 @@ namespace Ryujinx.Modules
|
||||
}
|
||||
else if (OperatingSystem.IsLinux())
|
||||
{
|
||||
_platformExt = "linux_x64.tar.gz";
|
||||
var arch = RuntimeInformation.OSArchitecture == Architecture.Arm64 ? "arm64" : "x64";
|
||||
_platformExt = $"linux_{arch}.tar.gz";
|
||||
}
|
||||
|
||||
Version newVersion;
|
||||
@ -637,20 +638,6 @@ namespace Ryujinx.Modules
|
||||
public static bool CanUpdate(bool showWarnings)
|
||||
{
|
||||
#if !DISABLE_UPDATER
|
||||
if (RuntimeInformation.OSArchitecture != Architecture.X64 && !OperatingSystem.IsMacOS())
|
||||
{
|
||||
if (showWarnings)
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
ContentDialogHelper.CreateWarningDialog(
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedMessage],
|
||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterArchNotSupportedSubMessage])
|
||||
);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!NetworkInterface.GetIsNetworkAvailable())
|
||||
{
|
||||
if (showWarnings)
|
||||
|
@ -9,10 +9,10 @@ using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.SystemInterop;
|
||||
using Ryujinx.Modules;
|
||||
using Ryujinx.SDL2.Common;
|
||||
using Ryujinx.Ui.Common;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using Ryujinx.Ui.Common.SystemInfo;
|
||||
using Ryujinx.UI.Common;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using Ryujinx.UI.Common.SystemInfo;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
@ -73,8 +73,8 @@
|
||||
<ProjectReference Include="..\ARMeilleure\ARMeilleure.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Ui.Common\Ryujinx.Ui.Common.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Ui.LocaleGenerator\Ryujinx.Ui.LocaleGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||
<ProjectReference Include="..\Ryujinx.UI.Common\Ryujinx.UI.Common.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.UI.LocaleGenerator\Ryujinx.UI.LocaleGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -103,7 +103,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<AvaloniaResource Include="Ui\**\*.xaml">
|
||||
<AvaloniaResource Include="UI\**\*.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
</AvaloniaResource>
|
||||
<AvaloniaResource Include="Assets\Fonts\SegoeFluentIcons.ttf" />
|
||||
|
@ -8,26 +8,26 @@ using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.HLE;
|
||||
using Ryujinx.HLE.HOS.Applets;
|
||||
using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types;
|
||||
using Ryujinx.HLE.Ui;
|
||||
using Ryujinx.HLE.UI;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Applet
|
||||
{
|
||||
internal class AvaHostUiHandler : IHostUiHandler
|
||||
internal class AvaHostUIHandler : IHostUIHandler
|
||||
{
|
||||
private readonly MainWindow _parent;
|
||||
|
||||
public IHostUiTheme HostUiTheme { get; }
|
||||
public IHostUITheme HostUITheme { get; }
|
||||
|
||||
public AvaHostUiHandler(MainWindow parent)
|
||||
public AvaHostUIHandler(MainWindow parent)
|
||||
{
|
||||
_parent = parent;
|
||||
|
||||
HostUiTheme = new AvaloniaHostUiTheme(parent);
|
||||
HostUITheme = new AvaloniaHostUITheme(parent);
|
||||
}
|
||||
|
||||
public bool DisplayMessageDialog(ControllerAppletUiArgs args)
|
||||
public bool DisplayMessageDialog(ControllerAppletUIArgs args)
|
||||
{
|
||||
ManualResetEvent dialogCloseEvent = new(false);
|
||||
|
||||
@ -110,7 +110,7 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
return okPressed;
|
||||
}
|
||||
|
||||
public bool DisplayInputDialog(SoftwareKeyboardUiArgs args, out string userText)
|
||||
public bool DisplayInputDialog(SoftwareKeyboardUIArgs args, out string userText)
|
||||
{
|
||||
ManualResetEvent dialogCloseEvent = new(false);
|
||||
|
@ -5,7 +5,7 @@ using Avalonia.Threading;
|
||||
using Ryujinx.Ava.Input;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.HLE.Ui;
|
||||
using Ryujinx.HLE.UI;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using HidKey = Ryujinx.Common.Configuration.Hid.Key;
|
||||
|
@ -1,13 +1,13 @@
|
||||
using Avalonia.Media;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.HLE.Ui;
|
||||
using Ryujinx.HLE.UI;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Applet
|
||||
{
|
||||
class AvaloniaHostUiTheme : IHostUiTheme
|
||||
class AvaloniaHostUITheme : IHostUITheme
|
||||
{
|
||||
public AvaloniaHostUiTheme(MainWindow parent)
|
||||
public AvaloniaHostUITheme(MainWindow parent)
|
||||
{
|
||||
FontFamily = OperatingSystem.IsWindows() && OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000) ? "Segoe UI Variable" : parent.FontFamily.Name;
|
||||
DefaultBackgroundColor = BrushToThemeColor(parent.Background);
|
@ -36,7 +36,7 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
|
||||
private readonly MainWindow _mainWindow;
|
||||
|
||||
public ControllerAppletDialog(MainWindow mainWindow, ControllerAppletUiArgs args)
|
||||
public ControllerAppletDialog(MainWindow mainWindow, ControllerAppletUIArgs args)
|
||||
{
|
||||
if (args.PlayerCountMin == args.PlayerCountMax)
|
||||
{
|
||||
@ -69,7 +69,7 @@ namespace Ryujinx.Ava.UI.Applet
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public static async Task<UserResult> ShowControllerAppletDialog(MainWindow window, ControllerAppletUiArgs args)
|
||||
public static async Task<UserResult> ShowControllerAppletDialog(MainWindow window, ControllerAppletUIArgs args)
|
||||
{
|
||||
ContentDialog contentDialog = new();
|
||||
UserResult result = UserResult.Cancel;
|
||||
|
@ -34,7 +34,7 @@
|
||||
Height="80"
|
||||
MinWidth="50"
|
||||
Margin="5,10,20,10"
|
||||
Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" />
|
||||
Source="resm:Ryujinx.UI.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.UI.Common" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
|
@ -31,7 +31,7 @@
|
||||
MinWidth="50"
|
||||
Margin="5,10,20,10"
|
||||
VerticalAlignment="Center"
|
||||
Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" />
|
||||
Source="resm:Ryujinx.UI.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.UI.Common" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
|
@ -54,7 +54,7 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
public string MainText { get; set; } = "";
|
||||
public string SecondaryText { get; set; } = "";
|
||||
|
||||
public static async Task<(UserResult Result, string Input)> ShowInputDialog(string title, SoftwareKeyboardUiArgs args)
|
||||
public static async Task<(UserResult Result, string Input)> ShowInputDialog(string title, SoftwareKeyboardUIArgs args)
|
||||
{
|
||||
ContentDialog contentDialog = new();
|
||||
|
||||
|
@ -51,6 +51,15 @@
|
||||
Header="{locale:Locale GameListContextMenuManageMod}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuManageModToolTip}" />
|
||||
<Separator />
|
||||
<MenuItem
|
||||
Click="OpenModsDirectory_Click"
|
||||
Header="{locale:Locale GameListContextMenuOpenModsDirectory}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenModsDirectoryToolTip}" />
|
||||
<MenuItem
|
||||
Click="OpenSdModsDirectory_Click"
|
||||
Header="{locale:Locale GameListContextMenuOpenSdModsDirectory}"
|
||||
ToolTip.Tip="{locale:Locale GameListContextMenuOpenSdModsDirectoryToolTip}" />
|
||||
<Separator />
|
||||
<MenuItem Header="{locale:Locale GameListContextMenuCacheManagement}">
|
||||
<MenuItem
|
||||
Click="PurgePtcCache_Click"
|
||||
|
@ -11,8 +11,8 @@ using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.Ui.App.Common;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using Ryujinx.UI.App.Common;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
@ -126,6 +126,32 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenModsDirectory_Click(object sender, RoutedEventArgs args)
|
||||
{
|
||||
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
|
||||
|
||||
if (viewModel?.SelectedApplication != null)
|
||||
{
|
||||
string modsBasePath = ModLoader.GetModsBasePath();
|
||||
string titleModsPath = ModLoader.GetApplicationDir(modsBasePath, viewModel.SelectedApplication.TitleId);
|
||||
|
||||
OpenHelper.OpenFolder(titleModsPath);
|
||||
}
|
||||
}
|
||||
|
||||
public void OpenSdModsDirectory_Click(object sender, RoutedEventArgs args)
|
||||
{
|
||||
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
|
||||
|
||||
if (viewModel?.SelectedApplication != null)
|
||||
{
|
||||
string sdModsBasePath = ModLoader.GetSdModsBasePath();
|
||||
string titleModsPath = ModLoader.GetApplicationDir(sdModsBasePath, viewModel.SelectedApplication.TitleId);
|
||||
|
||||
OpenHelper.OpenFolder(titleModsPath);
|
||||
}
|
||||
}
|
||||
|
||||
public async void OpenModManager_Click(object sender, RoutedEventArgs args)
|
||||
{
|
||||
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
|
||||
|
@ -3,7 +3,7 @@ using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ui.App.Common;
|
||||
using Ryujinx.UI.App.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
|
@ -3,7 +3,7 @@ using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ui.App.Common;
|
||||
using Ryujinx.UI.App.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
|
@ -26,7 +26,7 @@
|
||||
Height="70"
|
||||
MinWidth="50"
|
||||
Margin="5,10,20,10"
|
||||
Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" />
|
||||
Source="resm:Ryujinx.UI.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.UI.Common" />
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
@ -39,4 +39,4 @@
|
||||
VerticalAlignment="Center" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Window>
|
||||
</Window>
|
||||
|
@ -1,5 +1,5 @@
|
||||
using Avalonia.Interactivity;
|
||||
using Ryujinx.Ui.App.Common;
|
||||
using Ryujinx.UI.App.Common;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
|
@ -383,7 +383,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
result = ContentDialogResult.None;
|
||||
|
||||
Logger.Warning?.Print(LogClass.Ui, "Content dialog overlay failed to populate. Default value has been returned.");
|
||||
Logger.Warning?.Print(LogClass.UI, "Content dialog overlay failed to populate. Default value has been returned.");
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -1,7 +1,7 @@
|
||||
using Avalonia.Data.Converters;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
|
@ -39,12 +39,12 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
|
||||
public void Log(AvaLogLevel level, string area, object source, string messageTemplate)
|
||||
{
|
||||
GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(level, area, messageTemplate, source, null));
|
||||
GetLog(level)?.PrintMsg(RyuLogClass.UI, Format(level, area, messageTemplate, source, null));
|
||||
}
|
||||
|
||||
public void Log(AvaLogLevel level, string area, object source, string messageTemplate, params object[] propertyValues)
|
||||
{
|
||||
GetLog(level)?.PrintMsg(RyuLogClass.Ui, Format(level, area, messageTemplate, source, propertyValues));
|
||||
GetLog(level)?.PrintMsg(RyuLogClass.UI, Format(level, area, messageTemplate, source, propertyValues));
|
||||
}
|
||||
|
||||
private static string Format(AvaLogLevel level, string area, string template, object source, object[] v)
|
||||
|
@ -1,6 +1,6 @@
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ui.Common;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using Ryujinx.UI.Common;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Helpers
|
||||
|
@ -1,4 +1,4 @@
|
||||
using Ryujinx.Ui.App.Common;
|
||||
using Ryujinx.UI.App.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
using Ryujinx.Ui.App.Common;
|
||||
using Ryujinx.UI.App.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
@ -3,8 +3,8 @@ using LibHac.Ncm;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.Ui.App.Common;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using Ryujinx.UI.App.Common;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -3,8 +3,8 @@ using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Platform;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using SPB.Graphics;
|
||||
using SPB.Platform;
|
||||
using SPB.Platform.GLX;
|
||||
|
@ -3,7 +3,7 @@ using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Ryujinx.Graphics.OpenGL;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using SPB.Graphics;
|
||||
using SPB.Graphics.Exceptions;
|
||||
using SPB.Graphics.OpenGL;
|
||||
@ -75,7 +75,7 @@ namespace Ryujinx.Ava.UI.Renderer
|
||||
throw;
|
||||
}
|
||||
|
||||
Logger.Warning?.Print(LogClass.Ui, $"Failed to {(!unbind ? "bind" : "unbind")} OpenGL context: {e}");
|
||||
Logger.Warning?.Print(LogClass.UI, $"Failed to {(!unbind ? "bind" : "unbind")} OpenGL context: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Renderer
|
||||
|
@ -3,7 +3,7 @@ using Avalonia.Platform;
|
||||
using Avalonia.Threading;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Net.NetworkInformation;
|
||||
@ -87,19 +87,19 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
Version = Program.Version;
|
||||
|
||||
if (ConfigurationState.Instance.Ui.BaseStyle.Value == "Light")
|
||||
if (ConfigurationState.Instance.UI.BaseStyle.Value == "Light")
|
||||
{
|
||||
GithubLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_GitHub_Light.png?assembly=Ryujinx.Ui.Common")));
|
||||
DiscordLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Discord_Light.png?assembly=Ryujinx.Ui.Common")));
|
||||
PatreonLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Patreon_Light.png?assembly=Ryujinx.Ui.Common")));
|
||||
TwitterLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Twitter_Light.png?assembly=Ryujinx.Ui.Common")));
|
||||
GithubLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_GitHub_Light.png?assembly=Ryujinx.UI.Common")));
|
||||
DiscordLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Discord_Light.png?assembly=Ryujinx.UI.Common")));
|
||||
PatreonLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Patreon_Light.png?assembly=Ryujinx.UI.Common")));
|
||||
TwitterLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Twitter_Light.png?assembly=Ryujinx.UI.Common")));
|
||||
}
|
||||
else
|
||||
{
|
||||
GithubLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_GitHub_Dark.png?assembly=Ryujinx.Ui.Common")));
|
||||
DiscordLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Discord_Dark.png?assembly=Ryujinx.Ui.Common")));
|
||||
PatreonLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Patreon_Dark.png?assembly=Ryujinx.Ui.Common")));
|
||||
TwitterLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_Twitter_Dark.png?assembly=Ryujinx.Ui.Common")));
|
||||
GithubLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_GitHub_Dark.png?assembly=Ryujinx.UI.Common")));
|
||||
DiscordLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Discord_Dark.png?assembly=Ryujinx.UI.Common")));
|
||||
PatreonLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Patreon_Dark.png?assembly=Ryujinx.UI.Common")));
|
||||
TwitterLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Twitter_Dark.png?assembly=Ryujinx.UI.Common")));
|
||||
}
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(DownloadPatronsJson);
|
||||
|
@ -9,7 +9,7 @@ using Ryujinx.Common;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.Ui.Common.Models.Amiibo;
|
||||
using Ryujinx.UI.Common.Models.Amiibo;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
@ -65,7 +65,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
_amiiboSeries = new ObservableCollection<string>();
|
||||
_amiibos = new AvaloniaList<AmiiboApi>();
|
||||
|
||||
_amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.Ui.Common/Resources/Logo_Amiibo.png");
|
||||
_amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.UI.Common/Resources/Logo_Amiibo.png");
|
||||
|
||||
_ = LoadContentAsync();
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.Input;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
@ -35,10 +35,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
public class ControllerInputViewModel : BaseModel, IDisposable
|
||||
{
|
||||
private const string Disabled = "disabled";
|
||||
private const string ProControllerResource = "Ryujinx.Ui.Common/Resources/Controller_ProCon.svg";
|
||||
private const string JoyConPairResource = "Ryujinx.Ui.Common/Resources/Controller_JoyConPair.svg";
|
||||
private const string JoyConLeftResource = "Ryujinx.Ui.Common/Resources/Controller_JoyConLeft.svg";
|
||||
private const string JoyConRightResource = "Ryujinx.Ui.Common/Resources/Controller_JoyConRight.svg";
|
||||
private const string ProControllerResource = "Ryujinx.UI.Common/Resources/Controller_ProCon.svg";
|
||||
private const string JoyConPairResource = "Ryujinx.UI.Common/Resources/Controller_JoyConPair.svg";
|
||||
private const string JoyConLeftResource = "Ryujinx.UI.Common/Resources/Controller_JoyConLeft.svg";
|
||||
private const string JoyConRightResource = "Ryujinx.UI.Common/Resources/Controller_JoyConRight.svg";
|
||||
private const string KeyboardString = "keyboard";
|
||||
private const string ControllerString = "controller";
|
||||
private readonly MainWindow _mainWindow;
|
||||
|
@ -25,13 +25,13 @@ using Ryujinx.HLE;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.HLE.Ui;
|
||||
using Ryujinx.HLE.UI;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.Modules;
|
||||
using Ryujinx.Ui.App.Common;
|
||||
using Ryujinx.Ui.Common;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using Ryujinx.UI.App.Common;
|
||||
using Ryujinx.UI.Common;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using SixLabors.ImageSharp.PixelFormats;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@ -138,7 +138,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
InputManager inputManager,
|
||||
UserChannelPersistence userChannelPersistence,
|
||||
LibHacHorizonManager libHacHorizonManager,
|
||||
IHostUiHandler uiHandler,
|
||||
IHostUIHandler uiHandler,
|
||||
Action<bool> showLoading,
|
||||
Action<bool> switchToGameControl,
|
||||
Action<Control> setMainContent,
|
||||
@ -685,10 +685,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public bool StartGamesInFullscreen
|
||||
{
|
||||
get => ConfigurationState.Instance.Ui.StartFullscreen;
|
||||
get => ConfigurationState.Instance.UI.StartFullscreen;
|
||||
set
|
||||
{
|
||||
ConfigurationState.Instance.Ui.StartFullscreen.Value = value;
|
||||
ConfigurationState.Instance.UI.StartFullscreen.Value = value;
|
||||
|
||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||
|
||||
@ -698,10 +698,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public bool ShowConsole
|
||||
{
|
||||
get => ConfigurationState.Instance.Ui.ShowConsole;
|
||||
get => ConfigurationState.Instance.UI.ShowConsole;
|
||||
set
|
||||
{
|
||||
ConfigurationState.Instance.Ui.ShowConsole.Value = value;
|
||||
ConfigurationState.Instance.UI.ShowConsole.Value = value;
|
||||
|
||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||
|
||||
@ -743,10 +743,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public Glyph Glyph
|
||||
{
|
||||
get => (Glyph)ConfigurationState.Instance.Ui.GameListViewMode.Value;
|
||||
get => (Glyph)ConfigurationState.Instance.UI.GameListViewMode.Value;
|
||||
set
|
||||
{
|
||||
ConfigurationState.Instance.Ui.GameListViewMode.Value = (int)value;
|
||||
ConfigurationState.Instance.UI.GameListViewMode.Value = (int)value;
|
||||
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(IsGrid));
|
||||
@ -758,9 +758,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public bool ShowNames
|
||||
{
|
||||
get => ConfigurationState.Instance.Ui.ShowNames && ConfigurationState.Instance.Ui.GridSize > 1; set
|
||||
get => ConfigurationState.Instance.UI.ShowNames && ConfigurationState.Instance.UI.GridSize > 1; set
|
||||
{
|
||||
ConfigurationState.Instance.Ui.ShowNames.Value = value;
|
||||
ConfigurationState.Instance.UI.ShowNames.Value = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(GridSizeScale));
|
||||
@ -772,10 +772,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
internal ApplicationSort SortMode
|
||||
{
|
||||
get => (ApplicationSort)ConfigurationState.Instance.Ui.ApplicationSort.Value;
|
||||
get => (ApplicationSort)ConfigurationState.Instance.UI.ApplicationSort.Value;
|
||||
private set
|
||||
{
|
||||
ConfigurationState.Instance.Ui.ApplicationSort.Value = (int)value;
|
||||
ConfigurationState.Instance.UI.ApplicationSort.Value = (int)value;
|
||||
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(SortName));
|
||||
@ -788,7 +788,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
get
|
||||
{
|
||||
return ConfigurationState.Instance.Ui.GridSize.Value switch
|
||||
return ConfigurationState.Instance.UI.GridSize.Value switch
|
||||
{
|
||||
1 => 78,
|
||||
2 => 100,
|
||||
@ -803,7 +803,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
get
|
||||
{
|
||||
return ConfigurationState.Instance.Ui.GridSize.Value switch
|
||||
return ConfigurationState.Instance.UI.GridSize.Value switch
|
||||
{
|
||||
1 => 120,
|
||||
2 => ShowNames ? 210 : 150,
|
||||
@ -816,10 +816,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public int GridSizeScale
|
||||
{
|
||||
get => ConfigurationState.Instance.Ui.GridSize;
|
||||
get => ConfigurationState.Instance.UI.GridSize;
|
||||
set
|
||||
{
|
||||
ConfigurationState.Instance.Ui.GridSize.Value = value;
|
||||
ConfigurationState.Instance.UI.GridSize.Value = value;
|
||||
|
||||
if (value < 2)
|
||||
{
|
||||
@ -860,10 +860,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public bool IsAscending
|
||||
{
|
||||
get => ConfigurationState.Instance.Ui.IsAscendingOrder;
|
||||
get => ConfigurationState.Instance.UI.IsAscendingOrder;
|
||||
private set
|
||||
{
|
||||
ConfigurationState.Instance.Ui.IsAscendingOrder.Value = value;
|
||||
ConfigurationState.Instance.UI.IsAscendingOrder.Value = value;
|
||||
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(SortMode));
|
||||
@ -919,7 +919,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
public RendererHost RendererHostControl { get; private set; }
|
||||
public bool IsClosing { get; set; }
|
||||
public LibHacHorizonManager LibHacHorizonManager { get; internal set; }
|
||||
public IHostUiHandler UiHandler { get; internal set; }
|
||||
public IHostUIHandler UiHandler { get; internal set; }
|
||||
public bool IsSortedByFavorite => SortMode == ApplicationSort.Favorite;
|
||||
public bool IsSortedByTitle => SortMode == ApplicationSort.Title;
|
||||
public bool IsSortedByDeveloper => SortMode == ApplicationSort.Developer;
|
||||
@ -928,10 +928,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
public bool IsSortedByType => SortMode == ApplicationSort.FileType;
|
||||
public bool IsSortedBySize => SortMode == ApplicationSort.FileSize;
|
||||
public bool IsSortedByPath => SortMode == ApplicationSort.Path;
|
||||
public bool IsGridSmall => ConfigurationState.Instance.Ui.GridSize == 1;
|
||||
public bool IsGridMedium => ConfigurationState.Instance.Ui.GridSize == 2;
|
||||
public bool IsGridLarge => ConfigurationState.Instance.Ui.GridSize == 3;
|
||||
public bool IsGridHuge => ConfigurationState.Instance.Ui.GridSize == 4;
|
||||
public bool IsGridSmall => ConfigurationState.Instance.UI.GridSize == 1;
|
||||
public bool IsGridMedium => ConfigurationState.Instance.UI.GridSize == 2;
|
||||
public bool IsGridLarge => ConfigurationState.Instance.UI.GridSize == 3;
|
||||
public bool IsGridHuge => ConfigurationState.Instance.UI.GridSize == 4;
|
||||
|
||||
#endregion
|
||||
|
||||
@ -1245,7 +1245,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public void LoadConfigurableHotKeys()
|
||||
{
|
||||
if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUi, out var showUiKey))
|
||||
if (AvaloniaKeyboardMappingHelper.TryGetAvaKey((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUI, out var showUiKey))
|
||||
{
|
||||
ShowUiKey = new KeyGesture(showUiKey);
|
||||
}
|
||||
@ -1350,16 +1350,11 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
public void OpenLogsFolder()
|
||||
{
|
||||
string logPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
|
||||
|
||||
if (LoggerModule.LogDirectoryPath != null)
|
||||
string logPath = AppDataManager.GetOrCreateLogsDir();
|
||||
if (!string.IsNullOrEmpty(logPath))
|
||||
{
|
||||
logPath = LoggerModule.LogDirectoryPath;
|
||||
OpenHelper.OpenFolder(logPath);
|
||||
}
|
||||
|
||||
new DirectoryInfo(logPath).Create();
|
||||
|
||||
OpenHelper.OpenFolder(logPath);
|
||||
}
|
||||
|
||||
public void ToggleDockMode()
|
||||
@ -1390,7 +1385,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
if (Program.PreviewerDetached)
|
||||
{
|
||||
ConfigurationState.Instance.Ui.LanguageCode.Value = (string)languageCode;
|
||||
ConfigurationState.Instance.UI.LanguageCode.Value = (string)languageCode;
|
||||
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
|
||||
}
|
||||
}
|
||||
|
@ -16,8 +16,8 @@ using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Graphics.Vulkan;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS.Services.Time.TimeZone;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.Ui.Common.Configuration.System;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using Ryujinx.UI.Common.Configuration.System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
@ -408,9 +408,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
HideCursor = (int)config.HideCursor.Value;
|
||||
|
||||
GameDirectories.Clear();
|
||||
GameDirectories.AddRange(config.Ui.GameDirs.Value);
|
||||
GameDirectories.AddRange(config.UI.GameDirs.Value);
|
||||
|
||||
BaseStyleIndex = config.Ui.BaseStyle == "Light" ? 0 : 1;
|
||||
BaseStyleIndex = config.UI.BaseStyle == "Light" ? 0 : 1;
|
||||
|
||||
// Input
|
||||
EnableDockedMode = config.System.EnableDockedMode;
|
||||
@ -494,10 +494,10 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
if (_directoryChanged)
|
||||
{
|
||||
List<string> gameDirs = new(GameDirectories);
|
||||
config.Ui.GameDirs.Value = gameDirs;
|
||||
config.UI.GameDirs.Value = gameDirs;
|
||||
}
|
||||
|
||||
config.Ui.BaseStyle.Value = BaseStyleIndex == 0 ? "Light" : "Dark";
|
||||
config.UI.BaseStyle.Value = BaseStyleIndex == 0 ? "Light" : "Dark";
|
||||
|
||||
// Input
|
||||
config.System.EnableDockedMode.Value = EnableDockedMode;
|
||||
|
@ -17,7 +17,7 @@ using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.Ui.App.Common;
|
||||
using Ryujinx.UI.App.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
@ -10,9 +10,9 @@ using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.Modules;
|
||||
using Ryujinx.Ui.Common;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using Ryujinx.UI.Common;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
@ -43,7 +43,7 @@ namespace Ryujinx.Ava.UI.Views.Main
|
||||
checkBoxes.Add(new CheckBox
|
||||
{
|
||||
Content = $".{fileName}",
|
||||
IsChecked = ((FileTypes)item).GetConfigValue(ConfigurationState.Instance.Ui.ShownFileTypes),
|
||||
IsChecked = ((FileTypes)item).GetConfigValue(ConfigurationState.Instance.UI.ShownFileTypes),
|
||||
Command = MiniCommand.Create(() => Window.ToggleFileType(fileName)),
|
||||
});
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ using Avalonia.Interactivity;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Views.Main
|
||||
|
@ -45,7 +45,7 @@
|
||||
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysShowUiHotkey}" Width="230" />
|
||||
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
|
||||
<TextBlock
|
||||
Text="{Binding KeyboardHotkeys.ShowUi, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
Text="{Binding KeyboardHotkeys.ShowUI, Mode=TwoWay, Converter={StaticResource Key}}"
|
||||
TextAlignment="Center" />
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
|
@ -44,31 +44,37 @@
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<Image
|
||||
Grid.Column="0"
|
||||
Height="80"
|
||||
Source="resm:Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.Ui.Common" />
|
||||
<WrapPanel
|
||||
Grid.Column="2"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Vertical">
|
||||
<TextBlock
|
||||
<StackPanel
|
||||
Grid.Column="1"
|
||||
Orientation="Horizontal"
|
||||
HorizontalAlignment="Center"
|
||||
Spacing="10">
|
||||
<Image
|
||||
Height="80"
|
||||
Source="resm:Ryujinx.UI.Common.Resources.Logo_Ryujinx.png?assembly=Ryujinx.UI.Common"
|
||||
HorizontalAlignment="Center"
|
||||
IsHitTestVisible="True" />
|
||||
<WrapPanel
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="28"
|
||||
FontWeight="Bold"
|
||||
Text="Ryujinx"
|
||||
TextAlignment="Center"
|
||||
Width="110" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="11"
|
||||
Text="(REE-YOU-JINX)"
|
||||
TextAlignment="Center"
|
||||
Width="110" />
|
||||
</WrapPanel>
|
||||
Orientation="Vertical">
|
||||
<TextBlock
|
||||
FontSize="28"
|
||||
FontWeight="Bold"
|
||||
Text="Ryujinx"
|
||||
TextAlignment="Start"
|
||||
Width="110"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center" />
|
||||
<TextBlock
|
||||
FontSize="11"
|
||||
Text="(REE-YOU-JINX)"
|
||||
TextAlignment="Start"
|
||||
Width="110"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center" />
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
|
@ -7,7 +7,7 @@ using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using System.Threading.Tasks;
|
||||
using Button = Avalonia.Controls.Button;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
using Avalonia.Interactivity;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ui.Common.Models.Amiibo;
|
||||
using Ryujinx.UI.Common.Models.Amiibo;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
|
@ -3,7 +3,7 @@ using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Models;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.Ui.App.Common;
|
||||
using Ryujinx.UI.App.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
|
@ -7,7 +7,7 @@ using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Models;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using System.Threading.Tasks;
|
||||
using Button = Avalonia.Controls.Button;
|
||||
|
||||
|
@ -18,10 +18,10 @@ using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.Input.HLE;
|
||||
using Ryujinx.Input.SDL2;
|
||||
using Ryujinx.Modules;
|
||||
using Ryujinx.Ui.App.Common;
|
||||
using Ryujinx.Ui.Common;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using Ryujinx.UI.App.Common;
|
||||
using Ryujinx.UI.Common;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.Versioning;
|
||||
@ -40,7 +40,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
private static bool _deferLoad;
|
||||
private static string _launchPath;
|
||||
private static bool _startFullscreen;
|
||||
internal readonly AvaHostUiHandler UiHandler;
|
||||
internal readonly AvaHostUIHandler UiHandler;
|
||||
|
||||
public VirtualFileSystem VirtualFileSystem { get; private set; }
|
||||
public ContentManager ContentManager { get; private set; }
|
||||
@ -69,7 +69,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
InitializeComponent();
|
||||
Load();
|
||||
|
||||
UiHandler = new AvaHostUiHandler(this);
|
||||
UiHandler = new AvaHostUIHandler(this);
|
||||
|
||||
ViewModel.Title = $"Ryujinx {Program.Version}";
|
||||
|
||||
@ -319,13 +319,13 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
private void SetWindowSizePosition()
|
||||
{
|
||||
PixelPoint savedPoint = new(ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX,
|
||||
ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY);
|
||||
PixelPoint savedPoint = new(ConfigurationState.Instance.UI.WindowStartup.WindowPositionX,
|
||||
ConfigurationState.Instance.UI.WindowStartup.WindowPositionY);
|
||||
|
||||
ViewModel.WindowHeight = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight * Program.WindowScaleFactor;
|
||||
ViewModel.WindowWidth = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeWidth * Program.WindowScaleFactor;
|
||||
ViewModel.WindowHeight = ConfigurationState.Instance.UI.WindowStartup.WindowSizeHeight * Program.WindowScaleFactor;
|
||||
ViewModel.WindowWidth = ConfigurationState.Instance.UI.WindowStartup.WindowSizeWidth * Program.WindowScaleFactor;
|
||||
|
||||
ViewModel.WindowState = ConfigurationState.Instance.Ui.WindowStartup.WindowMaximized.Value ? WindowState.Maximized : WindowState.Normal;
|
||||
ViewModel.WindowState = ConfigurationState.Instance.UI.WindowStartup.WindowMaximized.Value ? WindowState.Maximized : WindowState.Normal;
|
||||
|
||||
if (CheckScreenBounds(savedPoint))
|
||||
{
|
||||
@ -353,13 +353,13 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
private void SaveWindowSizePosition()
|
||||
{
|
||||
ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight.Value = (int)Height;
|
||||
ConfigurationState.Instance.Ui.WindowStartup.WindowSizeWidth.Value = (int)Width;
|
||||
ConfigurationState.Instance.UI.WindowStartup.WindowSizeHeight.Value = (int)Height;
|
||||
ConfigurationState.Instance.UI.WindowStartup.WindowSizeWidth.Value = (int)Width;
|
||||
|
||||
ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX.Value = Position.X;
|
||||
ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY.Value = Position.Y;
|
||||
ConfigurationState.Instance.UI.WindowStartup.WindowPositionX.Value = Position.X;
|
||||
ConfigurationState.Instance.UI.WindowStartup.WindowPositionY.Value = Position.Y;
|
||||
|
||||
ConfigurationState.Instance.Ui.WindowStartup.WindowMaximized.Value = WindowState == WindowState.Maximized;
|
||||
ConfigurationState.Instance.UI.WindowStartup.WindowMaximized.Value = WindowState == WindowState.Maximized;
|
||||
|
||||
MainWindowViewModel.SaveConfig();
|
||||
}
|
||||
@ -512,12 +512,12 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
_ = fileType switch
|
||||
{
|
||||
#pragma warning disable IDE0055 // Disable formatting
|
||||
"NSP" => ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSP,
|
||||
"PFS0" => ConfigurationState.Instance.Ui.ShownFileTypes.PFS0.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.PFS0,
|
||||
"XCI" => ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.XCI,
|
||||
"NCA" => ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NCA,
|
||||
"NRO" => ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NRO,
|
||||
"NSO" => ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSO,
|
||||
"NSP" => ConfigurationState.Instance.UI.ShownFileTypes.NSP.Value = !ConfigurationState.Instance.UI.ShownFileTypes.NSP,
|
||||
"PFS0" => ConfigurationState.Instance.UI.ShownFileTypes.PFS0.Value = !ConfigurationState.Instance.UI.ShownFileTypes.PFS0,
|
||||
"XCI" => ConfigurationState.Instance.UI.ShownFileTypes.XCI.Value = !ConfigurationState.Instance.UI.ShownFileTypes.XCI,
|
||||
"NCA" => ConfigurationState.Instance.UI.ShownFileTypes.NCA.Value = !ConfigurationState.Instance.UI.ShownFileTypes.NCA,
|
||||
"NRO" => ConfigurationState.Instance.UI.ShownFileTypes.NRO.Value = !ConfigurationState.Instance.UI.ShownFileTypes.NRO,
|
||||
"NSO" => ConfigurationState.Instance.UI.ShownFileTypes.NSO.Value = !ConfigurationState.Instance.UI.ShownFileTypes.NSO,
|
||||
_ => throw new ArgumentOutOfRangeException(fileType),
|
||||
#pragma warning restore IDE0055
|
||||
};
|
||||
@ -537,7 +537,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
|
||||
Thread applicationLibraryThread = new(() =>
|
||||
{
|
||||
ApplicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs, ConfigurationState.Instance.System.Language);
|
||||
ApplicationLibrary.LoadApplications(ConfigurationState.Instance.UI.GameDirs, ConfigurationState.Instance.System.Language);
|
||||
|
||||
_isLoading = false;
|
||||
})
|
||||
|
@ -40,14 +40,14 @@
|
||||
Name="EnableAllButton"
|
||||
MinWidth="90"
|
||||
Margin="5"
|
||||
Command="{ReflectionBinding EnableAll}">
|
||||
Command="{Binding EnableAll}">
|
||||
<TextBlock Text="{locale:Locale DlcManagerEnableAllButton}" />
|
||||
</Button>
|
||||
<Button
|
||||
Name="DisableAllButton"
|
||||
MinWidth="90"
|
||||
Margin="5"
|
||||
Command="{ReflectionBinding DisableAll}">
|
||||
Command="{Binding DisableAll}">
|
||||
<TextBlock Text="{locale:Locale DlcManagerDisableAllButton}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
@ -6,7 +6,7 @@ using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Models;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using System.Threading.Tasks;
|
||||
using Button = Avalonia.Controls.Button;
|
||||
|
||||
|
@ -4,7 +4,7 @@ using Avalonia.Media;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Platform;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.UI.Common.Configuration;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
@ -19,7 +19,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner;
|
||||
TransparencyLevelHint = new[] { WindowTransparencyLevel.None };
|
||||
|
||||
using Stream stream = Assembly.GetAssembly(typeof(ConfigurationState)).GetManifestResourceStream("Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png");
|
||||
using Stream stream = Assembly.GetAssembly(typeof(ConfigurationState)).GetManifestResourceStream("Ryujinx.UI.Common.Resources.Logo_Ryujinx.png");
|
||||
|
||||
Icon = new WindowIcon(stream);
|
||||
stream.Position = 0;
|
||||
|
@ -7,7 +7,7 @@ using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Models;
|
||||
using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
using Ryujinx.UI.Common.Helper;
|
||||
using System.Threading.Tasks;
|
||||
using Button = Avalonia.Controls.Button;
|
||||
|
||||
|
@ -2,6 +2,7 @@ using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace Ryujinx.Common.Configuration
|
||||
{
|
||||
@ -30,6 +31,8 @@ namespace Ryujinx.Common.Configuration
|
||||
public static string KeysDirPath { get; private set; }
|
||||
public static string KeysDirPathUser { get; }
|
||||
|
||||
public static string LogsDirPath { get; private set; }
|
||||
|
||||
public const string DefaultNandDir = "bis";
|
||||
public const string DefaultSdcardDir = "sdcard";
|
||||
private const string DefaultModsDir = "mods";
|
||||
@ -46,15 +49,7 @@ namespace Ryujinx.Common.Configuration
|
||||
|
||||
public static void Initialize(string baseDirPath)
|
||||
{
|
||||
string appDataPath;
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support");
|
||||
}
|
||||
else
|
||||
{
|
||||
appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
}
|
||||
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
|
||||
|
||||
if (appDataPath.Length == 0)
|
||||
{
|
||||
@ -101,26 +96,134 @@ namespace Ryujinx.Common.Configuration
|
||||
|
||||
BaseDirPath = Path.GetFullPath(BaseDirPath); // convert relative paths
|
||||
|
||||
// NOTE: Moves the Ryujinx folder in `~/.config` to `~/Library/Application Support` if one is found
|
||||
// and a Ryujinx folder does not already exist in Application Support.
|
||||
// Also creates a symlink from `~/.config/Ryujinx` to `~/Library/Application Support/Ryujinx` to preserve backwards compatibility.
|
||||
// This should be removed in the future.
|
||||
if (OperatingSystem.IsMacOS() && Mode == LaunchMode.UserProfile)
|
||||
if (IsPathSymlink(BaseDirPath))
|
||||
{
|
||||
string oldConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir);
|
||||
if (Path.Exists(oldConfigPath) && !IsPathSymlink(oldConfigPath) && !Path.Exists(BaseDirPath))
|
||||
{
|
||||
FileSystemUtils.MoveDirectory(oldConfigPath, BaseDirPath);
|
||||
Directory.CreateSymbolicLink(oldConfigPath, BaseDirPath);
|
||||
}
|
||||
Logger.Warning?.Print(LogClass.Application, $"Application data directory is a symlink. This may be unintended.");
|
||||
}
|
||||
|
||||
SetupBasePaths();
|
||||
}
|
||||
|
||||
public static string GetOrCreateLogsDir()
|
||||
{
|
||||
if (Directory.Exists(LogsDirPath))
|
||||
{
|
||||
return LogsDirPath;
|
||||
}
|
||||
|
||||
Logger.Notice.Print(LogClass.Application, "Logging directory not found; attempting to create new logging directory.");
|
||||
LogsDirPath = SetUpLogsDir();
|
||||
|
||||
return LogsDirPath;
|
||||
}
|
||||
|
||||
private static string SetUpLogsDir()
|
||||
{
|
||||
string logDir = "";
|
||||
|
||||
if (Mode == LaunchMode.Portable)
|
||||
{
|
||||
logDir = Path.Combine(BaseDirPath, "Logs");
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(logDir);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (OperatingSystem.IsMacOS())
|
||||
{
|
||||
// NOTE: Should evaluate to "~/Library/Logs/Ryujinx/".
|
||||
logDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Logs", DefaultBaseDir);
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(logDir);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
|
||||
logDir = "";
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(logDir))
|
||||
{
|
||||
// NOTE: Should evaluate to "~/Library/Application Support/Ryujinx/Logs".
|
||||
logDir = Path.Combine(BaseDirPath, "Logs");
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(logDir);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (OperatingSystem.IsWindows())
|
||||
{
|
||||
// NOTE: Should evaluate to a "Logs" directory in whatever directory Ryujinx was launched from.
|
||||
logDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(logDir);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
|
||||
logDir = "";
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(logDir))
|
||||
{
|
||||
// NOTE: Should evaluate to "C:\Users\user\AppData\Roaming\Ryujinx\Logs".
|
||||
logDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir, "Logs");
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(logDir);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (OperatingSystem.IsLinux())
|
||||
{
|
||||
// NOTE: Should evaluate to "~/.config/Ryujinx/Logs".
|
||||
logDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir, "Logs");
|
||||
|
||||
try
|
||||
{
|
||||
Directory.CreateDirectory(logDir);
|
||||
}
|
||||
catch
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"Logging directory could not be created '{logDir}'");
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return logDir;
|
||||
}
|
||||
|
||||
private static void SetupBasePaths()
|
||||
{
|
||||
Directory.CreateDirectory(BaseDirPath);
|
||||
LogsDirPath = SetUpLogsDir();
|
||||
Directory.CreateDirectory(GamesDirPath = Path.Combine(BaseDirPath, GamesDir));
|
||||
Directory.CreateDirectory(ProfilesDirPath = Path.Combine(BaseDirPath, ProfilesDir));
|
||||
Directory.CreateDirectory(KeysDirPath = Path.Combine(BaseDirPath, KeysDir));
|
||||
@ -130,8 +233,91 @@ namespace Ryujinx.Common.Configuration
|
||||
// Should be removed, when the existence of the old directory isn't checked anymore.
|
||||
private static bool IsPathSymlink(string path)
|
||||
{
|
||||
FileAttributes attributes = File.GetAttributes(path);
|
||||
return (attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint;
|
||||
try
|
||||
{
|
||||
FileAttributes attributes = File.GetAttributes(path);
|
||||
return (attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("macos")]
|
||||
public static void FixMacOSConfigurationFolders()
|
||||
{
|
||||
string oldConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
".config", DefaultBaseDir);
|
||||
if (Path.Exists(oldConfigPath) && !IsPathSymlink(oldConfigPath) && !Path.Exists(BaseDirPath))
|
||||
{
|
||||
FileSystemUtils.MoveDirectory(oldConfigPath, BaseDirPath);
|
||||
Directory.CreateSymbolicLink(oldConfigPath, BaseDirPath);
|
||||
}
|
||||
|
||||
string correctApplicationDataDirectoryPath =
|
||||
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir);
|
||||
if (IsPathSymlink(correctApplicationDataDirectoryPath))
|
||||
{
|
||||
//copy the files somewhere temporarily
|
||||
string tempPath = Path.Combine(Path.GetTempPath(), DefaultBaseDir);
|
||||
try
|
||||
{
|
||||
FileSystemUtils.CopyDirectory(correctApplicationDataDirectoryPath, tempPath, true);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application,
|
||||
$"Critical error copying Ryujinx application data into the temp folder. {exception}");
|
||||
try
|
||||
{
|
||||
FileSystemInfo resolvedDirectoryInfo =
|
||||
Directory.ResolveLinkTarget(correctApplicationDataDirectoryPath, true);
|
||||
string resolvedPath = resolvedDirectoryInfo.FullName;
|
||||
Logger.Error?.Print(LogClass.Application, $"Please manually move your Ryujinx data from {resolvedPath} to {correctApplicationDataDirectoryPath}, and remove the symlink.");
|
||||
}
|
||||
catch (Exception symlinkException)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Unable to resolve the symlink for Ryujinx application data: {symlinkException}. Follow the symlink at {correctApplicationDataDirectoryPath} and move your data back to the Application Support folder.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//delete the symlink
|
||||
try
|
||||
{
|
||||
//This will fail if this is an actual directory, so there is no way we can actually delete user data here.
|
||||
File.Delete(correctApplicationDataDirectoryPath);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application,
|
||||
$"Critical error deleting the Ryujinx application data folder symlink at {correctApplicationDataDirectoryPath}. {exception}");
|
||||
try
|
||||
{
|
||||
FileSystemInfo resolvedDirectoryInfo =
|
||||
Directory.ResolveLinkTarget(correctApplicationDataDirectoryPath, true);
|
||||
string resolvedPath = resolvedDirectoryInfo.FullName;
|
||||
Logger.Error?.Print(LogClass.Application, $"Please manually move your Ryujinx data from {resolvedPath} to {correctApplicationDataDirectoryPath}, and remove the symlink.");
|
||||
}
|
||||
catch (Exception symlinkException)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Unable to resolve the symlink for Ryujinx application data: {symlinkException}. Follow the symlink at {correctApplicationDataDirectoryPath} and move your data back to the Application Support folder.");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//put the files back
|
||||
try
|
||||
{
|
||||
FileSystemUtils.CopyDirectory(tempPath, correctApplicationDataDirectoryPath, true);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application,
|
||||
$"Critical error copying Ryujinx application data into the correct location. {exception}. Please manually move your application data from {tempPath} to {correctApplicationDataDirectoryPath}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetModsPath() => CustomModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultModsDir)).FullName;
|
||||
|
@ -6,7 +6,7 @@ namespace Ryujinx.Common.Configuration.Hid
|
||||
{
|
||||
public Key ToggleVsync { get; set; }
|
||||
public Key Screenshot { get; set; }
|
||||
public Key ShowUi { get; set; }
|
||||
public Key ShowUI { get; set; }
|
||||
public Key Pause { get; set; }
|
||||
public Key ToggleMute { get; set; }
|
||||
public Key ResScaleUp { get; set; }
|
||||
|
@ -70,7 +70,7 @@ namespace Ryujinx.Common.Logging
|
||||
ServiceVi,
|
||||
SurfaceFlinger,
|
||||
TamperMachine,
|
||||
Ui,
|
||||
UI,
|
||||
Vic,
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,18 @@ namespace Ryujinx.Common.Logging.Targets
|
||||
public static FileStream PrepareLogFile(string path)
|
||||
{
|
||||
// Ensure directory is present
|
||||
DirectoryInfo logDir = new(path);
|
||||
DirectoryInfo logDir;
|
||||
try
|
||||
{
|
||||
logDir = new DirectoryInfo(path);
|
||||
}
|
||||
catch (ArgumentException exception)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"Logging directory path ('{path}') was invalid: {exception}");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
logDir.Create();
|
||||
|
@ -148,6 +148,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
B8G8R8A8Unorm,
|
||||
B8G8R8A8Srgb,
|
||||
B10G10R10A2Unorm,
|
||||
X8UintD24Unorm,
|
||||
}
|
||||
|
||||
public static class FormatExtensions
|
||||
@ -269,6 +270,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
case Format.D16Unorm:
|
||||
return 2;
|
||||
case Format.S8UintD24Unorm:
|
||||
case Format.X8UintD24Unorm:
|
||||
case Format.D32Float:
|
||||
case Format.D24UnormS8Uint:
|
||||
return 4;
|
||||
@ -349,6 +351,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
case Format.D16Unorm:
|
||||
case Format.D24UnormS8Uint:
|
||||
case Format.S8UintD24Unorm:
|
||||
case Format.X8UintD24Unorm:
|
||||
case Format.D32Float:
|
||||
case Format.D32FloatS8Uint:
|
||||
return true;
|
||||
@ -633,6 +636,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
case Format.D16Unorm:
|
||||
case Format.D24UnormS8Uint:
|
||||
case Format.S8UintD24Unorm:
|
||||
case Format.X8UintD24Unorm:
|
||||
case Format.D32Float:
|
||||
case Format.D32FloatS8Uint:
|
||||
case Format.S8Uint:
|
||||
|
@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
|
||||
void SetIndexBuffer(BufferRange buffer, IndexType type);
|
||||
|
||||
void SetImage(int binding, ITexture texture, Format imageFormat);
|
||||
void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat);
|
||||
|
||||
void SetLineParameters(float width, bool smooth);
|
||||
|
||||
|
@ -1,17 +1,20 @@
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Model;
|
||||
using Ryujinx.Graphics.GAL.Multithreading.Resources;
|
||||
using Ryujinx.Graphics.Shader;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||
{
|
||||
struct SetImageCommand : IGALCommand, IGALCommand<SetImageCommand>
|
||||
{
|
||||
public readonly CommandType CommandType => CommandType.SetImage;
|
||||
private ShaderStage _stage;
|
||||
private int _binding;
|
||||
private TableRef<ITexture> _texture;
|
||||
private Format _imageFormat;
|
||||
|
||||
public void Set(int binding, TableRef<ITexture> texture, Format imageFormat)
|
||||
public void Set(ShaderStage stage, int binding, TableRef<ITexture> texture, Format imageFormat)
|
||||
{
|
||||
_stage = stage;
|
||||
_binding = binding;
|
||||
_texture = texture;
|
||||
_imageFormat = imageFormat;
|
||||
@ -19,7 +22,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
|
||||
|
||||
public static void Run(ref SetImageCommand command, ThreadedRenderer threaded, IRenderer renderer)
|
||||
{
|
||||
renderer.Pipeline.SetImage(command._binding, command._texture.GetAs<ThreadedTexture>(threaded)?.Base, command._imageFormat);
|
||||
renderer.Pipeline.SetImage(command._stage, command._binding, command._texture.GetAs<ThreadedTexture>(threaded)?.Base, command._imageFormat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -177,9 +177,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
public void SetImage(int binding, ITexture texture, Format imageFormat)
|
||||
public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat)
|
||||
{
|
||||
_renderer.New<SetImageCommand>().Set(binding, Ref(texture), imageFormat);
|
||||
_renderer.New<SetImageCommand>().Set(stage, binding, Ref(texture), imageFormat);
|
||||
_renderer.QueueCommand();
|
||||
}
|
||||
|
||||
|
@ -8,13 +8,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types
|
||||
/// </summary>
|
||||
enum ZetaFormat
|
||||
{
|
||||
D32Float = 0xa,
|
||||
D16Unorm = 0x13,
|
||||
D24UnormS8Uint = 0x14,
|
||||
D24Unorm = 0x15,
|
||||
S8UintD24Unorm = 0x16,
|
||||
Zf32 = 0xa,
|
||||
Z16 = 0x13,
|
||||
Z24S8 = 0x14,
|
||||
X8Z24 = 0x15,
|
||||
S8Z24 = 0x16,
|
||||
S8Uint = 0x17,
|
||||
D32FloatS8Uint = 0x19,
|
||||
Zf32X24S8 = 0x19,
|
||||
}
|
||||
|
||||
static class ZetaFormatConverter
|
||||
@ -29,14 +29,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types
|
||||
return format switch
|
||||
{
|
||||
#pragma warning disable IDE0055 // Disable formatting
|
||||
ZetaFormat.D32Float => new FormatInfo(Format.D32Float, 1, 1, 4, 1),
|
||||
ZetaFormat.D16Unorm => new FormatInfo(Format.D16Unorm, 1, 1, 2, 1),
|
||||
ZetaFormat.D24UnormS8Uint => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2),
|
||||
ZetaFormat.D24Unorm => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 1),
|
||||
ZetaFormat.S8UintD24Unorm => new FormatInfo(Format.S8UintD24Unorm, 1, 1, 4, 2),
|
||||
ZetaFormat.S8Uint => new FormatInfo(Format.S8Uint, 1, 1, 1, 1),
|
||||
ZetaFormat.D32FloatS8Uint => new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2),
|
||||
_ => FormatInfo.Default,
|
||||
ZetaFormat.Zf32 => new FormatInfo(Format.D32Float, 1, 1, 4, 1),
|
||||
ZetaFormat.Z16 => new FormatInfo(Format.D16Unorm, 1, 1, 2, 1),
|
||||
ZetaFormat.Z24S8 => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2),
|
||||
ZetaFormat.X8Z24 => new FormatInfo(Format.X8UintD24Unorm, 1, 1, 4, 1),
|
||||
ZetaFormat.S8Z24 => new FormatInfo(Format.S8UintD24Unorm, 1, 1, 4, 2),
|
||||
ZetaFormat.S8Uint => new FormatInfo(Format.S8Uint, 1, 1, 1, 1),
|
||||
ZetaFormat.Zf32X24S8 => new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2),
|
||||
_ => FormatInfo.Default,
|
||||
#pragma warning restore IDE0055
|
||||
};
|
||||
}
|
||||
|
@ -185,6 +185,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
G24R8RUintGUnormBUnormAUnorm = G24R8 | RUint | GUnorm | BUnorm | AUnorm, // 0x24a0e
|
||||
Z24S8RUintGUnormBUnormAUnorm = Z24S8 | RUint | GUnorm | BUnorm | AUnorm, // 0x24a29
|
||||
Z24S8RUintGUnormBUintAUint = Z24S8 | RUint | GUnorm | BUint | AUint, // 0x48a29
|
||||
X8Z24RUnormGUintBUintAUint = X8Z24 | RUnorm | GUint | BUint | AUint, // 0x4912a
|
||||
S8Z24RUnormGUintBUintAUint = S8Z24 | RUnorm | GUint | BUint | AUint, // 0x4912b
|
||||
R32B24G8RFloatGUintBUnormAUnorm = R32B24G8 | RFloat | GUint | BUnorm | AUnorm, // 0x25385
|
||||
Zf32X24S8RFloatGUintBUnormAUnorm = Zf32X24S8 | RFloat | GUint | BUnorm | AUnorm, // 0x253b0
|
||||
@ -410,6 +411,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
{ TextureFormat.G24R8RUintGUnormBUnormAUnorm, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
|
||||
{ TextureFormat.Z24S8RUintGUnormBUnormAUnorm, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
|
||||
{ TextureFormat.Z24S8RUintGUnormBUintAUint, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
|
||||
{ TextureFormat.X8Z24RUnormGUintBUintAUint, new FormatInfo(Format.X8UintD24Unorm, 1, 1, 4, 2) },
|
||||
{ TextureFormat.S8Z24RUnormGUintBUintAUint, new FormatInfo(Format.S8UintD24Unorm, 1, 1, 4, 2) },
|
||||
{ TextureFormat.R32B24G8RFloatGUintBUnormAUnorm, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) },
|
||||
{ TextureFormat.Zf32X24S8RFloatGUintBUnormAUnorm, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) },
|
||||
|
@ -634,7 +634,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
state.Texture = hostTextureRebind;
|
||||
state.ImageFormat = format;
|
||||
|
||||
_context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTextureRebind, format);
|
||||
_context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTextureRebind, format);
|
||||
}
|
||||
|
||||
continue;
|
||||
@ -692,7 +692,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
state.ImageFormat = format;
|
||||
|
||||
_context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTexture, format);
|
||||
_context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTexture, format);
|
||||
}
|
||||
|
||||
state.CachedTexture = texture;
|
||||
|
@ -242,7 +242,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
return TextureMatchQuality.FormatAlias;
|
||||
}
|
||||
else if ((lhs.FormatInfo.Format == Format.D24UnormS8Uint ||
|
||||
lhs.FormatInfo.Format == Format.S8UintD24Unorm) && rhs.FormatInfo.Format == Format.B8G8R8A8Unorm)
|
||||
lhs.FormatInfo.Format == Format.S8UintD24Unorm ||
|
||||
lhs.FormatInfo.Format == Format.X8UintD24Unorm) && rhs.FormatInfo.Format == Format.B8G8R8A8Unorm)
|
||||
{
|
||||
return TextureMatchQuality.FormatAlias;
|
||||
}
|
||||
|
@ -484,7 +484,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
|
||||
if (binding.IsImage)
|
||||
{
|
||||
_context.Renderer.Pipeline.SetImage(binding.BindingInfo.Binding, binding.Texture, binding.Format);
|
||||
_context.Renderer.Pipeline.SetImage(binding.Stage, binding.BindingInfo.Binding, binding.Texture, binding.Format);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -68,6 +68,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
Add(Format.S8Uint, new FormatInfo(1, false, false, All.StencilIndex8, PixelFormat.StencilIndex, PixelType.UnsignedByte));
|
||||
Add(Format.D16Unorm, new FormatInfo(1, false, false, All.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort));
|
||||
Add(Format.S8UintD24Unorm, new FormatInfo(1, false, false, All.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248));
|
||||
Add(Format.X8UintD24Unorm, new FormatInfo(1, false, false, All.DepthComponent24, PixelFormat.DepthComponent, PixelType.UnsignedInt));
|
||||
Add(Format.D32Float, new FormatInfo(1, false, false, All.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float));
|
||||
Add(Format.D24UnormS8Uint, new FormatInfo(1, false, false, All.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248));
|
||||
Add(Format.D32FloatS8Uint, new FormatInfo(1, false, false, All.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev));
|
||||
@ -224,5 +225,17 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
{
|
||||
return _tableImage[(int)format];
|
||||
}
|
||||
|
||||
public static bool IsPackedDepthStencil(Format format)
|
||||
{
|
||||
return format == Format.D24UnormS8Uint ||
|
||||
format == Format.D32FloatS8Uint ||
|
||||
format == Format.S8UintD24Unorm;
|
||||
}
|
||||
|
||||
public static bool IsDepthOnly(Format format)
|
||||
{
|
||||
return format == Format.D16Unorm || format == Format.D32Float || format == Format.X8UintD24Unorm;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -119,11 +119,11 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
|
||||
private static FramebufferAttachment GetAttachment(Format format)
|
||||
{
|
||||
if (IsPackedDepthStencilFormat(format))
|
||||
if (FormatTable.IsPackedDepthStencil(format))
|
||||
{
|
||||
return FramebufferAttachment.DepthStencilAttachment;
|
||||
}
|
||||
else if (IsDepthOnlyFormat(format))
|
||||
else if (FormatTable.IsDepthOnly(format))
|
||||
{
|
||||
return FramebufferAttachment.DepthAttachment;
|
||||
}
|
||||
@ -133,18 +133,6 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsPackedDepthStencilFormat(Format format)
|
||||
{
|
||||
return format == Format.D24UnormS8Uint ||
|
||||
format == Format.D32FloatS8Uint ||
|
||||
format == Format.S8UintD24Unorm;
|
||||
}
|
||||
|
||||
private static bool IsDepthOnlyFormat(Format format)
|
||||
{
|
||||
return format == Format.D16Unorm || format == Format.D32Float;
|
||||
}
|
||||
|
||||
public int GetColorLayerCount(int index)
|
||||
{
|
||||
return _colors[index]?.Info.GetDepthOrLayers() ?? 0;
|
||||
|
@ -294,7 +294,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
{
|
||||
return FramebufferAttachment.DepthStencilAttachment;
|
||||
}
|
||||
else if (IsDepthOnly(format))
|
||||
else if (FormatTable.IsDepthOnly(format))
|
||||
{
|
||||
return FramebufferAttachment.DepthAttachment;
|
||||
}
|
||||
@ -324,11 +324,11 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
|
||||
private static ClearBufferMask GetMask(Format format)
|
||||
{
|
||||
if (format == Format.D24UnormS8Uint || format == Format.D32FloatS8Uint || format == Format.S8UintD24Unorm)
|
||||
if (FormatTable.IsPackedDepthStencil(format))
|
||||
{
|
||||
return ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit;
|
||||
}
|
||||
else if (IsDepthOnly(format))
|
||||
else if (FormatTable.IsDepthOnly(format))
|
||||
{
|
||||
return ClearBufferMask.DepthBufferBit;
|
||||
}
|
||||
@ -342,11 +342,6 @@ namespace Ryujinx.Graphics.OpenGL.Image
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsDepthOnly(Format format)
|
||||
{
|
||||
return format == Format.D16Unorm || format == Format.D32Float;
|
||||
}
|
||||
|
||||
public TextureView BgraSwap(TextureView from)
|
||||
{
|
||||
TextureView to = (TextureView)_renderer.CreateTexture(from.Info);
|
||||
|
@ -935,7 +935,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
SetFrontFace(_frontFace = frontFace.Convert());
|
||||
}
|
||||
|
||||
public void SetImage(int binding, ITexture texture, Format imageFormat)
|
||||
public void SetImage(ShaderStage stage, int binding, ITexture texture, Format imageFormat)
|
||||
{
|
||||
if ((uint)binding < SavedImages)
|
||||
{
|
||||
|
225
src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs
Normal file
225
src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs
Normal file
@ -0,0 +1,225 @@
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
internal class BarrierBatch : IDisposable
|
||||
{
|
||||
private const int MaxBarriersPerCall = 16;
|
||||
|
||||
private readonly VulkanRenderer _gd;
|
||||
|
||||
private readonly NativeArray<MemoryBarrier> _memoryBarrierBatch = new(MaxBarriersPerCall);
|
||||
private readonly NativeArray<BufferMemoryBarrier> _bufferBarrierBatch = new(MaxBarriersPerCall);
|
||||
private readonly NativeArray<ImageMemoryBarrier> _imageBarrierBatch = new(MaxBarriersPerCall);
|
||||
|
||||
private readonly List<BarrierWithStageFlags<MemoryBarrier>> _memoryBarriers = new();
|
||||
private readonly List<BarrierWithStageFlags<BufferMemoryBarrier>> _bufferBarriers = new();
|
||||
private readonly List<BarrierWithStageFlags<ImageMemoryBarrier>> _imageBarriers = new();
|
||||
private int _queuedBarrierCount;
|
||||
|
||||
public BarrierBatch(VulkanRenderer gd)
|
||||
{
|
||||
_gd = gd;
|
||||
}
|
||||
|
||||
private readonly record struct StageFlags : IEquatable<StageFlags>
|
||||
{
|
||||
public readonly PipelineStageFlags Source;
|
||||
public readonly PipelineStageFlags Dest;
|
||||
|
||||
public StageFlags(PipelineStageFlags source, PipelineStageFlags dest)
|
||||
{
|
||||
Source = source;
|
||||
Dest = dest;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly struct BarrierWithStageFlags<T> where T : unmanaged
|
||||
{
|
||||
public readonly StageFlags Flags;
|
||||
public readonly T Barrier;
|
||||
|
||||
public BarrierWithStageFlags(StageFlags flags, T barrier)
|
||||
{
|
||||
Flags = flags;
|
||||
Barrier = barrier;
|
||||
}
|
||||
|
||||
public BarrierWithStageFlags(PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags, T barrier)
|
||||
{
|
||||
Flags = new StageFlags(srcStageFlags, dstStageFlags);
|
||||
Barrier = barrier;
|
||||
}
|
||||
}
|
||||
|
||||
private void QueueBarrier<T>(List<BarrierWithStageFlags<T>> list, T barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags) where T : unmanaged
|
||||
{
|
||||
list.Add(new BarrierWithStageFlags<T>(srcStageFlags, dstStageFlags, barrier));
|
||||
_queuedBarrierCount++;
|
||||
}
|
||||
|
||||
public void QueueBarrier(MemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
|
||||
{
|
||||
QueueBarrier(_memoryBarriers, barrier, srcStageFlags, dstStageFlags);
|
||||
}
|
||||
|
||||
public void QueueBarrier(BufferMemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
|
||||
{
|
||||
QueueBarrier(_bufferBarriers, barrier, srcStageFlags, dstStageFlags);
|
||||
}
|
||||
|
||||
public void QueueBarrier(ImageMemoryBarrier barrier, PipelineStageFlags srcStageFlags, PipelineStageFlags dstStageFlags)
|
||||
{
|
||||
QueueBarrier(_imageBarriers, barrier, srcStageFlags, dstStageFlags);
|
||||
}
|
||||
|
||||
public unsafe void Flush(CommandBuffer cb, bool insideRenderPass, Action endRenderPass)
|
||||
{
|
||||
while (_queuedBarrierCount > 0)
|
||||
{
|
||||
int memoryCount = 0;
|
||||
int bufferCount = 0;
|
||||
int imageCount = 0;
|
||||
|
||||
bool hasBarrier = false;
|
||||
StageFlags flags = default;
|
||||
|
||||
static void AddBarriers<T>(
|
||||
Span<T> target,
|
||||
ref int queuedBarrierCount,
|
||||
ref bool hasBarrier,
|
||||
ref StageFlags flags,
|
||||
ref int count,
|
||||
List<BarrierWithStageFlags<T>> list) where T : unmanaged
|
||||
{
|
||||
int firstMatch = -1;
|
||||
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
BarrierWithStageFlags<T> barrier = list[i];
|
||||
|
||||
if (!hasBarrier)
|
||||
{
|
||||
flags = barrier.Flags;
|
||||
hasBarrier = true;
|
||||
|
||||
target[count++] = barrier.Barrier;
|
||||
queuedBarrierCount--;
|
||||
firstMatch = i;
|
||||
|
||||
if (count >= target.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (flags.Equals(barrier.Flags))
|
||||
{
|
||||
target[count++] = barrier.Barrier;
|
||||
queuedBarrierCount--;
|
||||
|
||||
if (firstMatch == -1)
|
||||
{
|
||||
firstMatch = i;
|
||||
}
|
||||
|
||||
if (count >= target.Length)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Delete consumed barriers from the first match to the current non-match.
|
||||
if (firstMatch != -1)
|
||||
{
|
||||
int deleteCount = i - firstMatch;
|
||||
list.RemoveRange(firstMatch, deleteCount);
|
||||
i -= deleteCount;
|
||||
|
||||
firstMatch = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (firstMatch == 0)
|
||||
{
|
||||
list.Clear();
|
||||
}
|
||||
else if (firstMatch != -1)
|
||||
{
|
||||
int deleteCount = list.Count - firstMatch;
|
||||
|
||||
list.RemoveRange(firstMatch, deleteCount);
|
||||
}
|
||||
}
|
||||
|
||||
if (insideRenderPass)
|
||||
{
|
||||
// Image barriers queued in the batch are meant to be globally scoped,
|
||||
// but inside a render pass they're scoped to just the range of the render pass.
|
||||
|
||||
// On MoltenVK, we just break the rules and always use image barrier.
|
||||
// On desktop GPUs, all barriers are globally scoped, so we just replace it with a generic memory barrier.
|
||||
// TODO: On certain GPUs, we need to split render pass so the barrier scope is global. When this is done,
|
||||
// notify the resource that it should add a barrier as soon as a render pass ends to avoid this in future.
|
||||
|
||||
if (!_gd.IsMoltenVk)
|
||||
{
|
||||
foreach (var barrier in _imageBarriers)
|
||||
{
|
||||
_memoryBarriers.Add(new BarrierWithStageFlags<MemoryBarrier>(
|
||||
barrier.Flags,
|
||||
new MemoryBarrier()
|
||||
{
|
||||
SType = StructureType.MemoryBarrier,
|
||||
SrcAccessMask = barrier.Barrier.SrcAccessMask,
|
||||
DstAccessMask = barrier.Barrier.DstAccessMask
|
||||
}));
|
||||
}
|
||||
|
||||
_imageBarriers.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
AddBarriers(_memoryBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref memoryCount, _memoryBarriers);
|
||||
AddBarriers(_bufferBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref bufferCount, _bufferBarriers);
|
||||
AddBarriers(_imageBarrierBatch.AsSpan(), ref _queuedBarrierCount, ref hasBarrier, ref flags, ref imageCount, _imageBarriers);
|
||||
|
||||
if (hasBarrier)
|
||||
{
|
||||
PipelineStageFlags srcStageFlags = flags.Source;
|
||||
|
||||
if (insideRenderPass)
|
||||
{
|
||||
// Inside a render pass, barrier stages can only be from rasterization.
|
||||
srcStageFlags &= ~PipelineStageFlags.ComputeShaderBit;
|
||||
}
|
||||
|
||||
_gd.Api.CmdPipelineBarrier(
|
||||
cb,
|
||||
srcStageFlags,
|
||||
flags.Dest,
|
||||
0,
|
||||
(uint)memoryCount,
|
||||
_memoryBarrierBatch.Pointer,
|
||||
(uint)bufferCount,
|
||||
_bufferBarrierBatch.Pointer,
|
||||
(uint)imageCount,
|
||||
_imageBarrierBatch.Pointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_memoryBarrierBatch.Dispose();
|
||||
_bufferBarrierBatch.Dispose();
|
||||
_imageBarrierBatch.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages;
|
||||
public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages;
|
||||
public const int MaxImageBindings = MaxImagesPerStage * MaxShaderStages;
|
||||
public const int MaxPushDescriptorBinding = 64;
|
||||
|
||||
public const ulong SparseBufferAlignment = 0x10000;
|
||||
}
|
||||
|
@ -1,19 +1,32 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class DescriptorSetTemplate : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Renderdoc seems to crash when doing a templated uniform update with count > 1 on a push descriptor.
|
||||
/// When this is true, consecutive buffers are always updated individually.
|
||||
/// </summary>
|
||||
private const bool RenderdocPushCountBug = true;
|
||||
|
||||
private readonly VulkanRenderer _gd;
|
||||
private readonly Device _device;
|
||||
|
||||
public readonly DescriptorUpdateTemplate Template;
|
||||
public readonly int Size;
|
||||
|
||||
public unsafe DescriptorSetTemplate(VulkanRenderer gd, Device device, ResourceBindingSegment[] segments, PipelineLayoutCacheEntry plce, PipelineBindPoint pbp, int setIndex)
|
||||
public unsafe DescriptorSetTemplate(
|
||||
VulkanRenderer gd,
|
||||
Device device,
|
||||
ResourceBindingSegment[] segments,
|
||||
PipelineLayoutCacheEntry plce,
|
||||
PipelineBindPoint pbp,
|
||||
int setIndex)
|
||||
{
|
||||
_gd = gd;
|
||||
_device = device;
|
||||
@ -137,6 +150,93 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
Template = result;
|
||||
}
|
||||
|
||||
public unsafe DescriptorSetTemplate(
|
||||
VulkanRenderer gd,
|
||||
Device device,
|
||||
ResourceDescriptorCollection descriptors,
|
||||
long updateMask,
|
||||
PipelineLayoutCacheEntry plce,
|
||||
PipelineBindPoint pbp,
|
||||
int setIndex)
|
||||
{
|
||||
_gd = gd;
|
||||
_device = device;
|
||||
|
||||
// Create a template from the set usages. Assumes the descriptor set is updated in segment order then binding order.
|
||||
int segmentCount = BitOperations.PopCount((ulong)updateMask);
|
||||
|
||||
DescriptorUpdateTemplateEntry* entries = stackalloc DescriptorUpdateTemplateEntry[segmentCount];
|
||||
int entry = 0;
|
||||
nuint structureOffset = 0;
|
||||
|
||||
void AddBinding(int binding, int count)
|
||||
{
|
||||
entries[entry++] = new DescriptorUpdateTemplateEntry()
|
||||
{
|
||||
DescriptorType = DescriptorType.UniformBuffer,
|
||||
DstBinding = (uint)binding,
|
||||
DescriptorCount = (uint)count,
|
||||
Offset = structureOffset,
|
||||
Stride = (nuint)Unsafe.SizeOf<DescriptorBufferInfo>()
|
||||
};
|
||||
|
||||
structureOffset += (nuint)(Unsafe.SizeOf<DescriptorBufferInfo>() * count);
|
||||
}
|
||||
|
||||
int startBinding = 0;
|
||||
int bindingCount = 0;
|
||||
|
||||
foreach (ResourceDescriptor descriptor in descriptors.Descriptors)
|
||||
{
|
||||
for (int i = 0; i < descriptor.Count; i++)
|
||||
{
|
||||
int binding = descriptor.Binding + i;
|
||||
|
||||
if ((updateMask & (1L << binding)) != 0)
|
||||
{
|
||||
if (bindingCount > 0 && (RenderdocPushCountBug || startBinding + bindingCount != binding))
|
||||
{
|
||||
AddBinding(startBinding, bindingCount);
|
||||
|
||||
bindingCount = 0;
|
||||
}
|
||||
|
||||
if (bindingCount == 0)
|
||||
{
|
||||
startBinding = binding;
|
||||
}
|
||||
|
||||
bindingCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bindingCount > 0)
|
||||
{
|
||||
AddBinding(startBinding, bindingCount);
|
||||
}
|
||||
|
||||
Size = (int)structureOffset;
|
||||
|
||||
var info = new DescriptorUpdateTemplateCreateInfo()
|
||||
{
|
||||
SType = StructureType.DescriptorUpdateTemplateCreateInfo,
|
||||
DescriptorUpdateEntryCount = (uint)entry,
|
||||
PDescriptorUpdateEntries = entries,
|
||||
|
||||
TemplateType = DescriptorUpdateTemplateType.PushDescriptorsKhr,
|
||||
DescriptorSetLayout = plce.DescriptorSetLayouts[setIndex],
|
||||
PipelineBindPoint = pbp,
|
||||
PipelineLayout = plce.PipelineLayout,
|
||||
Set = (uint)setIndex,
|
||||
};
|
||||
|
||||
DescriptorUpdateTemplate result;
|
||||
gd.Api.CreateDescriptorUpdateTemplate(device, &info, null, &result).ThrowOnError();
|
||||
|
||||
Template = result;
|
||||
}
|
||||
|
||||
public unsafe void Dispose()
|
||||
{
|
||||
_gd.Api.DestroyDescriptorUpdateTemplate(_device, Template, null);
|
||||
|
@ -52,11 +52,23 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return new DescriptorSetTemplateWriter(new Span<byte>(_data.Pointer, template.Size));
|
||||
}
|
||||
|
||||
public DescriptorSetTemplateWriter Begin(int maxSize)
|
||||
{
|
||||
EnsureSize(maxSize);
|
||||
|
||||
return new DescriptorSetTemplateWriter(new Span<byte>(_data.Pointer, maxSize));
|
||||
}
|
||||
|
||||
public void Commit(VulkanRenderer gd, Device device, DescriptorSet set)
|
||||
{
|
||||
gd.Api.UpdateDescriptorSetWithTemplate(device, set, _activeTemplate.Template, _data.Pointer);
|
||||
}
|
||||
|
||||
public void CommitPushDescriptor(VulkanRenderer gd, CommandBufferScoped cbs, DescriptorSetTemplate template, PipelineLayout layout)
|
||||
{
|
||||
gd.PushDescriptorApi.CmdPushDescriptorSetWithTemplate(cbs.CommandBuffer, template.Template, layout, 0, _data.Pointer);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_data?.Dispose();
|
||||
|
@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using CompareOp = Ryujinx.Graphics.GAL.CompareOp;
|
||||
using Format = Ryujinx.Graphics.GAL.Format;
|
||||
using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo;
|
||||
@ -34,6 +35,36 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
private record struct TextureRef
|
||||
{
|
||||
public ShaderStage Stage;
|
||||
public TextureStorage Storage;
|
||||
public Auto<DisposableImageView> View;
|
||||
public Auto<DisposableSampler> Sampler;
|
||||
|
||||
public TextureRef(ShaderStage stage, TextureStorage storage, Auto<DisposableImageView> view, Auto<DisposableSampler> sampler)
|
||||
{
|
||||
Stage = stage;
|
||||
Storage = storage;
|
||||
View = view;
|
||||
Sampler = sampler;
|
||||
}
|
||||
}
|
||||
|
||||
private record struct ImageRef
|
||||
{
|
||||
public ShaderStage Stage;
|
||||
public TextureStorage Storage;
|
||||
public Auto<DisposableImageView> View;
|
||||
|
||||
public ImageRef(ShaderStage stage, TextureStorage storage, Auto<DisposableImageView> view)
|
||||
{
|
||||
Stage = stage;
|
||||
Storage = storage;
|
||||
View = view;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly VulkanRenderer _gd;
|
||||
private readonly Device _device;
|
||||
private readonly PipelineBase _pipeline;
|
||||
@ -41,9 +72,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
private readonly BufferRef[] _uniformBufferRefs;
|
||||
private readonly BufferRef[] _storageBufferRefs;
|
||||
private readonly Auto<DisposableImageView>[] _textureRefs;
|
||||
private readonly Auto<DisposableSampler>[] _samplerRefs;
|
||||
private readonly Auto<DisposableImageView>[] _imageRefs;
|
||||
private readonly TextureRef[] _textureRefs;
|
||||
private readonly ImageRef[] _imageRefs;
|
||||
private readonly TextureBuffer[] _bufferTextureRefs;
|
||||
private readonly TextureBuffer[] _bufferImageRefs;
|
||||
private readonly Format[] _bufferImageFormats;
|
||||
@ -61,6 +91,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private BitMapStruct<Array2<long>> _storageSet;
|
||||
private BitMapStruct<Array2<long>> _uniformMirrored;
|
||||
private BitMapStruct<Array2<long>> _storageMirrored;
|
||||
private readonly int[] _uniformSetPd;
|
||||
private int _pdSequence = 1;
|
||||
|
||||
private bool _updateDescriptorCacheCbIndex;
|
||||
|
||||
@ -92,9 +124,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
_uniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings];
|
||||
_storageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings];
|
||||
_textureRefs = new Auto<DisposableImageView>[Constants.MaxTextureBindings * 2];
|
||||
_samplerRefs = new Auto<DisposableSampler>[Constants.MaxTextureBindings * 2];
|
||||
_imageRefs = new Auto<DisposableImageView>[Constants.MaxImageBindings * 2];
|
||||
_textureRefs = new TextureRef[Constants.MaxTextureBindings * 2];
|
||||
_imageRefs = new ImageRef[Constants.MaxImageBindings * 2];
|
||||
_bufferTextureRefs = new TextureBuffer[Constants.MaxTextureBindings * 2];
|
||||
_bufferImageRefs = new TextureBuffer[Constants.MaxImageBindings * 2];
|
||||
_bufferImageFormats = new Format[Constants.MaxImageBindings * 2];
|
||||
@ -106,6 +137,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_bufferTextures = new BufferView[Constants.MaxTexturesPerStage];
|
||||
_bufferImages = new BufferView[Constants.MaxImagesPerStage];
|
||||
|
||||
_uniformSetPd = new int[Constants.MaxUniformBufferBindings];
|
||||
|
||||
var initialImageInfo = new DescriptorImageInfo
|
||||
{
|
||||
ImageLayout = ImageLayout.General,
|
||||
@ -193,6 +226,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
if (BindingOverlaps(ref info, bindingOffset, offset, size))
|
||||
{
|
||||
_uniformSet.Clear(binding);
|
||||
_uniformSetPd[binding] = 0;
|
||||
SignalDirty(DirtyFlags.Uniform);
|
||||
}
|
||||
}
|
||||
@ -223,14 +257,68 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
});
|
||||
}
|
||||
|
||||
public void SetProgram(ShaderCollection program)
|
||||
public void InsertBindingBarriers(CommandBufferScoped cbs)
|
||||
{
|
||||
foreach (ResourceBindingSegment segment in _program.BindingSegments[PipelineBase.TextureSetIndex])
|
||||
{
|
||||
if (segment.Type == ResourceType.TextureAndSampler)
|
||||
{
|
||||
for (int i = 0; i < segment.Count; i++)
|
||||
{
|
||||
ref var texture = ref _textureRefs[segment.Binding + i];
|
||||
texture.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, texture.Stage.ConvertToPipelineStageFlags());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (ResourceBindingSegment segment in _program.BindingSegments[PipelineBase.ImageSetIndex])
|
||||
{
|
||||
if (segment.Type == ResourceType.Image)
|
||||
{
|
||||
for (int i = 0; i < segment.Count; i++)
|
||||
{
|
||||
ref var image = ref _imageRefs[segment.Binding + i];
|
||||
image.Storage?.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, image.Stage.ConvertToPipelineStageFlags());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AdvancePdSequence()
|
||||
{
|
||||
if (++_pdSequence == 0)
|
||||
{
|
||||
_pdSequence = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetProgram(CommandBufferScoped cbs, ShaderCollection program, bool isBound)
|
||||
{
|
||||
if (!program.HasSameLayout(_program))
|
||||
{
|
||||
// When the pipeline layout changes, push descriptor bindings are invalidated.
|
||||
|
||||
AdvancePdSequence();
|
||||
|
||||
if (_gd.IsNvidiaPreTuring && !program.UsePushDescriptors && _program?.UsePushDescriptors == true && isBound)
|
||||
{
|
||||
// On older nvidia GPUs, we need to clear out the active push descriptor bindings when switching
|
||||
// to normal descriptors. Keeping them bound can prevent buffers from binding properly in future.
|
||||
ClearAndBindUniformBufferPd(cbs);
|
||||
}
|
||||
}
|
||||
|
||||
_program = program;
|
||||
_updateDescriptorCacheCbIndex = true;
|
||||
_dirty = DirtyFlags.All;
|
||||
}
|
||||
|
||||
public void SetImage(int binding, ITexture image, Format imageFormat)
|
||||
public void SetImage(
|
||||
CommandBufferScoped cbs,
|
||||
ShaderStage stage,
|
||||
int binding,
|
||||
ITexture image,
|
||||
Format imageFormat)
|
||||
{
|
||||
if (image is TextureBuffer imageBuffer)
|
||||
{
|
||||
@ -239,11 +327,13 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
else if (image is TextureView view)
|
||||
{
|
||||
_imageRefs[binding] = view.GetView(imageFormat).GetIdentityImageView();
|
||||
view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
|
||||
|
||||
_imageRefs[binding] = new(stage, view.Storage, view.GetView(imageFormat).GetIdentityImageView());
|
||||
}
|
||||
else
|
||||
{
|
||||
_imageRefs[binding] = null;
|
||||
_imageRefs[binding] = default;
|
||||
_bufferImageRefs[binding] = null;
|
||||
_bufferImageFormats[binding] = default;
|
||||
}
|
||||
@ -253,7 +343,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
public void SetImage(int binding, Auto<DisposableImageView> image)
|
||||
{
|
||||
_imageRefs[binding] = image;
|
||||
_imageRefs[binding] = new(ShaderStage.Compute, null, image);
|
||||
|
||||
SignalDirty(DirtyFlags.Image);
|
||||
}
|
||||
@ -338,15 +428,13 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
else if (texture is TextureView view)
|
||||
{
|
||||
view.Storage.InsertWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
|
||||
view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
|
||||
|
||||
_textureRefs[binding] = view.GetImageView();
|
||||
_samplerRefs[binding] = ((SamplerHolder)sampler)?.GetSampler();
|
||||
_textureRefs[binding] = new(stage, view.Storage, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler());
|
||||
}
|
||||
else
|
||||
{
|
||||
_textureRefs[binding] = null;
|
||||
_samplerRefs[binding] = null;
|
||||
_textureRefs[binding] = default;
|
||||
_bufferTextureRefs[binding] = null;
|
||||
}
|
||||
|
||||
@ -362,10 +450,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
if (texture is TextureView view)
|
||||
{
|
||||
view.Storage.InsertWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
|
||||
view.Storage.QueueWriteToReadBarrier(cbs, AccessFlags.ShaderReadBit, stage.ConvertToPipelineStageFlags());
|
||||
|
||||
_textureRefs[binding] = view.GetIdentityImageView();
|
||||
_samplerRefs[binding] = ((SamplerHolder)sampler)?.GetSampler();
|
||||
_textureRefs[binding] = new(stage, view.Storage, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler());
|
||||
|
||||
SignalDirty(DirtyFlags.Texture);
|
||||
}
|
||||
@ -402,6 +489,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
if (!currentBufferRef.Equals(newRef) || currentInfo.Range != info.Range)
|
||||
{
|
||||
_uniformSet.Clear(index);
|
||||
_uniformSetPd[index] = 0;
|
||||
|
||||
currentInfo = info;
|
||||
currentBufferRef = newRef;
|
||||
@ -579,9 +667,10 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ref var texture = ref textures[i];
|
||||
ref var refs = ref _textureRefs[binding + i];
|
||||
|
||||
texture.ImageView = _textureRefs[binding + i]?.Get(cbs).Value ?? default;
|
||||
texture.Sampler = _samplerRefs[binding + i]?.Get(cbs).Value ?? default;
|
||||
texture.ImageView = refs.View?.Get(cbs).Value ?? default;
|
||||
texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
|
||||
|
||||
if (texture.ImageView.Handle == 0)
|
||||
{
|
||||
@ -616,7 +705,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
images[i].ImageView = _imageRefs[binding + i]?.Get(cbs).Value ?? default;
|
||||
images[i].ImageView = _imageRefs[binding + i].View?.Get(cbs).Value ?? default;
|
||||
}
|
||||
|
||||
tu.Push<DescriptorImageInfo>(images[..count]);
|
||||
@ -671,15 +760,19 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs, PipelineBindPoint pbp)
|
||||
{
|
||||
int sequence = _pdSequence;
|
||||
var bindingSegments = _program.BindingSegments[PipelineBase.UniformSetIndex];
|
||||
var dummyBuffer = _dummyBuffer?.GetBuffer();
|
||||
|
||||
long updatedBindings = 0;
|
||||
DescriptorSetTemplateWriter writer = _templateUpdater.Begin(32 * Unsafe.SizeOf<DescriptorBufferInfo>());
|
||||
|
||||
foreach (ResourceBindingSegment segment in bindingSegments)
|
||||
{
|
||||
int binding = segment.Binding;
|
||||
int count = segment.Count;
|
||||
|
||||
bool doUpdate = false;
|
||||
ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
@ -688,17 +781,58 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
if (_uniformSet.Set(index))
|
||||
{
|
||||
ref BufferRef buffer = ref _uniformBufferRefs[index];
|
||||
UpdateBuffer(cbs, ref _uniformBuffers[index], ref buffer, dummyBuffer, true);
|
||||
doUpdate = true;
|
||||
|
||||
bool mirrored = UpdateBuffer(cbs, ref _uniformBuffers[index], ref buffer, dummyBuffer, true);
|
||||
|
||||
_uniformMirrored.Set(index, mirrored);
|
||||
}
|
||||
|
||||
if (_uniformSetPd[index] != sequence)
|
||||
{
|
||||
// Need to set this push descriptor (even if the buffer binding has not changed)
|
||||
|
||||
_uniformSetPd[index] = sequence;
|
||||
updatedBindings |= 1L << index;
|
||||
|
||||
writer.Push(MemoryMarshal.CreateReadOnlySpan(ref _uniformBuffers[index], 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (doUpdate)
|
||||
if (updatedBindings > 0)
|
||||
{
|
||||
DescriptorSetTemplate template = _program.GetPushDescriptorTemplate(updatedBindings);
|
||||
_templateUpdater.CommitPushDescriptor(_gd, cbs, template, _program.PipelineLayout);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearAndBindUniformBufferPd(CommandBufferScoped cbs)
|
||||
{
|
||||
var bindingSegments = _program.BindingSegments[PipelineBase.UniformSetIndex];
|
||||
|
||||
long updatedBindings = 0;
|
||||
DescriptorSetTemplateWriter writer = _templateUpdater.Begin(32 * Unsafe.SizeOf<DescriptorBufferInfo>());
|
||||
|
||||
foreach (ResourceBindingSegment segment in bindingSegments)
|
||||
{
|
||||
int binding = segment.Binding;
|
||||
int count = segment.Count;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
|
||||
UpdateBuffers(cbs, pbp, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer);
|
||||
int index = binding + i;
|
||||
updatedBindings |= 1L << index;
|
||||
|
||||
var bufferInfo = new DescriptorBufferInfo();
|
||||
writer.Push(MemoryMarshal.CreateReadOnlySpan(ref bufferInfo, 1));
|
||||
}
|
||||
}
|
||||
|
||||
if (updatedBindings > 0)
|
||||
{
|
||||
DescriptorSetTemplate template = _program.GetPushDescriptorTemplate(updatedBindings);
|
||||
_templateUpdater.CommitPushDescriptor(_gd, cbs, template, _program.PipelineLayout);
|
||||
}
|
||||
}
|
||||
|
||||
private void Initialize(CommandBufferScoped cbs, int setIndex, DescriptorSetCollection dsc)
|
||||
@ -724,6 +858,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
_uniformSet.Clear();
|
||||
_storageSet.Clear();
|
||||
AdvancePdSequence();
|
||||
}
|
||||
|
||||
private static void SwapBuffer(BufferRef[] list, Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)
|
||||
|
@ -154,7 +154,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
||||
|
||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) });
|
||||
_pipeline.SetImage(0, _intermediaryTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||
_pipeline.SetImage(ShaderStage.Compute, 0, _intermediaryTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
_pipeline.ComputeBarrier();
|
||||
|
||||
|
@ -75,7 +75,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize);
|
||||
var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize);
|
||||
|
||||
_pipeline.SetImage(0, _texture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||
_pipeline.SetImage(ShaderStage.Compute, 0, _texture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
|
||||
_pipeline.ComputeBarrier();
|
||||
|
@ -219,7 +219,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
|
||||
buffer.Holder.SetDataUnchecked(buffer.Offset, resolutionBuffer);
|
||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) });
|
||||
_pipeline.SetImage(0, _edgeOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||
_pipeline.SetImage(ShaderStage.Compute, 0, _edgeOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
_pipeline.ComputeBarrier();
|
||||
|
||||
@ -229,7 +229,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _edgeOutputTexture, _samplerLinear);
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _areaTexture, _samplerLinear);
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 4, _searchTexture, _samplerLinear);
|
||||
_pipeline.SetImage(0, _blendOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||
_pipeline.SetImage(ShaderStage.Compute, 0, _blendOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
_pipeline.ComputeBarrier();
|
||||
|
||||
@ -238,7 +238,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
_pipeline.Specialize(_specConstants);
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _blendOutputTexture, _samplerLinear);
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear);
|
||||
_pipeline.SetImage(0, _outputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||
_pipeline.SetImage(ShaderStage.Compute, 0, _outputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
_pipeline.ComputeBarrier();
|
||||
|
||||
|
@ -376,7 +376,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
Format.D16Unorm or Format.D32Float => ImageAspectFlags.DepthBit,
|
||||
Format.D16Unorm or Format.D32Float or Format.X8UintD24Unorm => ImageAspectFlags.DepthBit,
|
||||
Format.S8Uint => ImageAspectFlags.StencilBit,
|
||||
Format.D24UnormS8Uint or
|
||||
Format.D32FloatS8Uint or
|
||||
@ -389,7 +389,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
Format.D16Unorm or Format.D32Float => ImageAspectFlags.DepthBit,
|
||||
Format.D16Unorm or Format.D32Float or Format.X8UintD24Unorm => ImageAspectFlags.DepthBit,
|
||||
Format.S8Uint => ImageAspectFlags.StencilBit,
|
||||
Format.D24UnormS8Uint or
|
||||
Format.D32FloatS8Uint or
|
||||
|
@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
public static bool IsD24S8(Format format)
|
||||
{
|
||||
return format == Format.D24UnormS8Uint || format == Format.S8UintD24Unorm;
|
||||
return format == Format.D24UnormS8Uint || format == Format.S8UintD24Unorm || format == Format.X8UintD24Unorm;
|
||||
}
|
||||
|
||||
private static bool IsRGB16IntFloat(Format format)
|
||||
|
@ -67,6 +67,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
Add(Format.S8Uint, VkFormat.S8Uint);
|
||||
Add(Format.D16Unorm, VkFormat.D16Unorm);
|
||||
Add(Format.S8UintD24Unorm, VkFormat.D24UnormS8Uint);
|
||||
Add(Format.X8UintD24Unorm, VkFormat.X8D24UnormPack32);
|
||||
Add(Format.D32Float, VkFormat.D32Sfloat);
|
||||
Add(Format.D24UnormS8Uint, VkFormat.D24UnormS8Uint);
|
||||
Add(Format.D32FloatS8Uint, VkFormat.D32SfloatS8Uint);
|
||||
|
@ -243,41 +243,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return new Auto<DisposableFramebuffer>(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments);
|
||||
}
|
||||
|
||||
public void UpdateModifications()
|
||||
{
|
||||
if (_colors != null)
|
||||
{
|
||||
for (int index = 0; index < _colors.Length; index++)
|
||||
{
|
||||
_colors[index].Storage.SetModification(
|
||||
AccessFlags.ColorAttachmentWriteBit,
|
||||
PipelineStageFlags.ColorAttachmentOutputBit);
|
||||
}
|
||||
}
|
||||
|
||||
_depthStencil?.Storage.SetModification(
|
||||
AccessFlags.DepthStencilAttachmentWriteBit,
|
||||
PipelineStageFlags.LateFragmentTestsBit);
|
||||
}
|
||||
|
||||
public void InsertClearBarrier(CommandBufferScoped cbs, int index)
|
||||
{
|
||||
_colorsCanonical?[index]?.Storage?.InsertReadToWriteBarrier(
|
||||
cbs,
|
||||
AccessFlags.ColorAttachmentWriteBit,
|
||||
PipelineStageFlags.ColorAttachmentOutputBit,
|
||||
insideRenderPass: true);
|
||||
}
|
||||
|
||||
public void InsertClearBarrierDS(CommandBufferScoped cbs)
|
||||
{
|
||||
_depthStencil?.Storage?.InsertReadToWriteBarrier(
|
||||
cbs,
|
||||
AccessFlags.DepthStencilAttachmentWriteBit,
|
||||
PipelineStageFlags.LateFragmentTestsBit,
|
||||
insideRenderPass: true);
|
||||
}
|
||||
|
||||
public TextureView[] GetAttachmentViews()
|
||||
{
|
||||
var result = new TextureView[_attachments.Length];
|
||||
@ -297,23 +262,20 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return new RenderPassCacheKey(_depthStencil, _colorsCanonical);
|
||||
}
|
||||
|
||||
public void InsertLoadOpBarriers(CommandBufferScoped cbs)
|
||||
public void InsertLoadOpBarriers(VulkanRenderer gd, CommandBufferScoped cbs)
|
||||
{
|
||||
if (_colors != null)
|
||||
{
|
||||
foreach (var color in _colors)
|
||||
{
|
||||
// If Clear or DontCare were used, this would need to be write bit.
|
||||
color.Storage?.InsertWriteToReadBarrier(cbs, AccessFlags.ColorAttachmentReadBit, PipelineStageFlags.ColorAttachmentOutputBit);
|
||||
color.Storage?.SetModification(AccessFlags.ColorAttachmentWriteBit, PipelineStageFlags.ColorAttachmentOutputBit);
|
||||
color.Storage?.QueueLoadOpBarrier(cbs, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (_depthStencil != null)
|
||||
{
|
||||
_depthStencil.Storage?.InsertWriteToReadBarrier(cbs, AccessFlags.DepthStencilAttachmentReadBit, PipelineStageFlags.EarlyFragmentTestsBit);
|
||||
_depthStencil.Storage?.SetModification(AccessFlags.DepthStencilAttachmentWriteBit, PipelineStageFlags.LateFragmentTestsBit);
|
||||
}
|
||||
_depthStencil?.Storage?.QueueLoadOpBarrier(cbs, true);
|
||||
|
||||
gd.Barriers.Flush(cbs.CommandBuffer, false, null);
|
||||
}
|
||||
|
||||
public (Auto<DisposableRenderPass> renderPass, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(
|
||||
|
@ -34,6 +34,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
public readonly bool SupportsMultiView;
|
||||
public readonly bool SupportsNullDescriptors;
|
||||
public readonly bool SupportsPushDescriptors;
|
||||
public readonly uint MaxPushDescriptors;
|
||||
public readonly bool SupportsPrimitiveTopologyListRestart;
|
||||
public readonly bool SupportsPrimitiveTopologyPatchListRestart;
|
||||
public readonly bool SupportsTransformFeedback;
|
||||
@ -71,6 +72,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
bool supportsMultiView,
|
||||
bool supportsNullDescriptors,
|
||||
bool supportsPushDescriptors,
|
||||
uint maxPushDescriptors,
|
||||
bool supportsPrimitiveTopologyListRestart,
|
||||
bool supportsPrimitiveTopologyPatchListRestart,
|
||||
bool supportsTransformFeedback,
|
||||
@ -107,6 +109,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
SupportsMultiView = supportsMultiView;
|
||||
SupportsNullDescriptors = supportsNullDescriptors;
|
||||
SupportsPushDescriptors = supportsPushDescriptors;
|
||||
MaxPushDescriptors = maxPushDescriptors;
|
||||
SupportsPrimitiveTopologyListRestart = supportsPrimitiveTopologyListRestart;
|
||||
SupportsPrimitiveTopologyPatchListRestart = supportsPrimitiveTopologyPatchListRestart;
|
||||
SupportsTransformFeedback = supportsTransformFeedback;
|
||||
|
@ -1039,7 +1039,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
var dstView = Create2DLayerView(dst, dstLayer + z, dstLevel + l);
|
||||
|
||||
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null);
|
||||
_pipeline.SetImage(0, dstView, dstFormat);
|
||||
_pipeline.SetImage(ShaderStage.Compute, 0, dstView, dstFormat);
|
||||
|
||||
int dispatchX = (Math.Min(srcView.Info.Width, dstView.Info.Width) + 31) / 32;
|
||||
int dispatchY = (Math.Min(srcView.Info.Height, dstView.Info.Height) + 31) / 32;
|
||||
@ -1168,7 +1168,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
var dstView = Create2DLayerView(dst, dstLayer + z, 0);
|
||||
|
||||
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null);
|
||||
_pipeline.SetImage(0, dstView, format);
|
||||
_pipeline.SetImage(ShaderStage.Compute, 0, dstView, format);
|
||||
|
||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
|
||||
|
@ -36,6 +36,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private PipelineState _newState;
|
||||
private bool _graphicsStateDirty;
|
||||
private bool _computeStateDirty;
|
||||
private bool _bindingBarriersDirty;
|
||||
private PrimitiveTopology _topology;
|
||||
|
||||
private ulong _currentPipelineHandle;
|
||||
@ -248,14 +249,14 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
CreateRenderPass();
|
||||
}
|
||||
|
||||
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
|
||||
|
||||
BeginRenderPass();
|
||||
|
||||
var clearValue = new ClearValue(new ClearColorValue(color.Red, color.Green, color.Blue, color.Alpha));
|
||||
var attachment = new ClearAttachment(ImageAspectFlags.ColorBit, (uint)index, clearValue);
|
||||
var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount);
|
||||
|
||||
FramebufferParams.InsertClearBarrier(Cbs, index);
|
||||
|
||||
Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect);
|
||||
}
|
||||
|
||||
@ -286,13 +287,13 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
CreateRenderPass();
|
||||
}
|
||||
|
||||
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
|
||||
|
||||
BeginRenderPass();
|
||||
|
||||
var attachment = new ClearAttachment(flags, 0, clearValue);
|
||||
var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount);
|
||||
|
||||
FramebufferParams.InsertClearBarrierDS(Cbs);
|
||||
|
||||
Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect);
|
||||
}
|
||||
|
||||
@ -887,9 +888,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
SignalStateChange();
|
||||
}
|
||||
|
||||
public void SetImage(int binding, ITexture image, Format imageFormat)
|
||||
public void SetImage(ShaderStage stage, int binding, ITexture image, Format imageFormat)
|
||||
{
|
||||
_descriptorSetUpdater.SetImage(binding, image, imageFormat);
|
||||
_descriptorSetUpdater.SetImage(Cbs, stage, binding, image, imageFormat);
|
||||
}
|
||||
|
||||
public void SetImage(int binding, Auto<DisposableImageView> image)
|
||||
@ -976,7 +977,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
_program = internalProgram;
|
||||
|
||||
_descriptorSetUpdater.SetProgram(internalProgram);
|
||||
_descriptorSetUpdater.SetProgram(Cbs, internalProgram, _currentPipelineHandle != 0);
|
||||
_bindingBarriersDirty = true;
|
||||
|
||||
_newState.PipelineLayout = internalProgram.PipelineLayout;
|
||||
_newState.StagesCount = (uint)stages.Length;
|
||||
@ -1066,7 +1068,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private void SetRenderTargetsInternal(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked)
|
||||
{
|
||||
CreateFramebuffer(colors, depthStencil, filterWriteMasked);
|
||||
FramebufferParams?.UpdateModifications();
|
||||
CreateRenderPass();
|
||||
SignalStateChange();
|
||||
SignalAttachmentChange();
|
||||
@ -1520,8 +1521,18 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
CreatePipeline(PipelineBindPoint.Compute);
|
||||
_computeStateDirty = false;
|
||||
Pbp = PipelineBindPoint.Compute;
|
||||
|
||||
if (_bindingBarriersDirty)
|
||||
{
|
||||
// Stale barriers may have been activated by switching program. Emit any that are relevant.
|
||||
_descriptorSetUpdater.InsertBindingBarriers(Cbs);
|
||||
|
||||
_bindingBarriersDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
|
||||
|
||||
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute);
|
||||
}
|
||||
|
||||
@ -1575,8 +1586,18 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
_graphicsStateDirty = false;
|
||||
Pbp = PipelineBindPoint.Graphics;
|
||||
|
||||
if (_bindingBarriersDirty)
|
||||
{
|
||||
// Stale barriers may have been activated by switching program. Emit any that are relevant.
|
||||
_descriptorSetUpdater.InsertBindingBarriers(Cbs);
|
||||
|
||||
_bindingBarriersDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
|
||||
|
||||
_descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics);
|
||||
|
||||
return true;
|
||||
@ -1630,6 +1651,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
if (!RenderPassActive)
|
||||
{
|
||||
FramebufferParams.InsertLoadOpBarriers(Gd, Cbs);
|
||||
|
||||
var renderArea = new Rect2D(null, new Extent2D(FramebufferParams.Width, FramebufferParams.Height));
|
||||
var clearValue = new ClearValue();
|
||||
|
||||
|
@ -269,6 +269,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
PreloadCbs = null;
|
||||
}
|
||||
|
||||
Gd.Barriers.Flush(Cbs.CommandBuffer, false, null);
|
||||
CommandBuffer = (Cbs = Gd.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
|
||||
Gd.RegisterFlush();
|
||||
|
||||
|
@ -31,6 +31,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private int _dsLastCbIndex;
|
||||
private int _dsLastSubmissionCount;
|
||||
|
||||
private readonly Dictionary<long, DescriptorSetTemplate> _pdTemplates;
|
||||
private readonly ResourceDescriptorCollection _pdDescriptors;
|
||||
private long _lastPdUsage;
|
||||
private DescriptorSetTemplate _lastPdTemplate;
|
||||
|
||||
private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, int setsCount)
|
||||
{
|
||||
_gd = gd;
|
||||
@ -72,6 +77,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
_consumedDescriptorsPerSet[setIndex] = count;
|
||||
}
|
||||
|
||||
if (usePushDescriptors)
|
||||
{
|
||||
_pdDescriptors = setDescriptors[0];
|
||||
_pdTemplates = new();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateCommandBufferIndex(int commandBufferIndex)
|
||||
@ -143,10 +154,39 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return output[..count];
|
||||
}
|
||||
|
||||
public DescriptorSetTemplate GetPushDescriptorTemplate(PipelineBindPoint pbp, long updateMask)
|
||||
{
|
||||
if (_lastPdUsage == updateMask && _lastPdTemplate != null)
|
||||
{
|
||||
// Most likely result is that it asks to update the same buffers.
|
||||
return _lastPdTemplate;
|
||||
}
|
||||
|
||||
if (!_pdTemplates.TryGetValue(updateMask, out DescriptorSetTemplate template))
|
||||
{
|
||||
template = new DescriptorSetTemplate(_gd, _device, _pdDescriptors, updateMask, this, pbp, 0);
|
||||
|
||||
_pdTemplates.Add(updateMask, template);
|
||||
}
|
||||
|
||||
_lastPdUsage = updateMask;
|
||||
_lastPdTemplate = template;
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
protected virtual unsafe void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
if (_pdTemplates != null)
|
||||
{
|
||||
foreach (DescriptorSetTemplate template in _pdTemplates.Values)
|
||||
{
|
||||
template.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < _dsCache.Length; i++)
|
||||
{
|
||||
for (int j = 0; j < _dsCache[i].Length; j++)
|
||||
|
@ -108,18 +108,25 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
_shaders = internalShaders;
|
||||
|
||||
bool usePushDescriptors = !isMinimal && VulkanConfiguration.UsePushDescriptors && _gd.Capabilities.SupportsPushDescriptors;
|
||||
bool usePushDescriptors = !isMinimal &&
|
||||
VulkanConfiguration.UsePushDescriptors &&
|
||||
_gd.Capabilities.SupportsPushDescriptors &&
|
||||
!IsCompute &&
|
||||
CanUsePushDescriptors(gd, resourceLayout, IsCompute);
|
||||
|
||||
_plce = gd.PipelineLayoutCache.GetOrCreate(gd, device, resourceLayout.Sets, usePushDescriptors);
|
||||
ReadOnlyCollection<ResourceDescriptorCollection> sets = usePushDescriptors ?
|
||||
BuildPushDescriptorSets(gd, resourceLayout.Sets) : resourceLayout.Sets;
|
||||
|
||||
_plce = gd.PipelineLayoutCache.GetOrCreate(gd, device, sets, usePushDescriptors);
|
||||
|
||||
HasMinimalLayout = isMinimal;
|
||||
UsePushDescriptors = usePushDescriptors;
|
||||
|
||||
Stages = stages;
|
||||
|
||||
ClearSegments = BuildClearSegments(resourceLayout.Sets);
|
||||
ClearSegments = BuildClearSegments(sets);
|
||||
BindingSegments = BuildBindingSegments(resourceLayout.SetUsages);
|
||||
Templates = BuildTemplates();
|
||||
Templates = BuildTemplates(usePushDescriptors);
|
||||
|
||||
_compileTask = Task.CompletedTask;
|
||||
_firstBackgroundUse = false;
|
||||
@ -139,6 +146,76 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_firstBackgroundUse = !fromCache;
|
||||
}
|
||||
|
||||
private static bool CanUsePushDescriptors(VulkanRenderer gd, ResourceLayout layout, bool isCompute)
|
||||
{
|
||||
// If binding 3 is immediately used, use an alternate set of reserved bindings.
|
||||
ReadOnlyCollection<ResourceUsage> uniformUsage = layout.SetUsages[0].Usages;
|
||||
bool hasBinding3 = uniformUsage.Any(x => x.Binding == 3);
|
||||
int[] reserved = isCompute ? Array.Empty<int>() : gd.GetPushDescriptorReservedBindings(hasBinding3);
|
||||
|
||||
// Can't use any of the reserved usages.
|
||||
for (int i = 0; i < uniformUsage.Count; i++)
|
||||
{
|
||||
var binding = uniformUsage[i].Binding;
|
||||
|
||||
if (reserved.Contains(binding) ||
|
||||
binding >= Constants.MaxPushDescriptorBinding ||
|
||||
binding >= gd.Capabilities.MaxPushDescriptors + reserved.Count(id => id < binding))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static ReadOnlyCollection<ResourceDescriptorCollection> BuildPushDescriptorSets(
|
||||
VulkanRenderer gd,
|
||||
ReadOnlyCollection<ResourceDescriptorCollection> sets)
|
||||
{
|
||||
// The reserved bindings were selected when determining if push descriptors could be used.
|
||||
int[] reserved = gd.GetPushDescriptorReservedBindings(false);
|
||||
|
||||
var result = new ResourceDescriptorCollection[sets.Count];
|
||||
|
||||
for (int i = 0; i < sets.Count; i++)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
// Push descriptors apply here. Remove reserved bindings.
|
||||
ResourceDescriptorCollection original = sets[i];
|
||||
|
||||
var pdUniforms = new ResourceDescriptor[original.Descriptors.Count];
|
||||
int j = 0;
|
||||
|
||||
foreach (ResourceDescriptor descriptor in original.Descriptors)
|
||||
{
|
||||
if (reserved.Contains(descriptor.Binding))
|
||||
{
|
||||
// If the binding is reserved, set its descriptor count to 0.
|
||||
pdUniforms[j++] = new ResourceDescriptor(
|
||||
descriptor.Binding,
|
||||
0,
|
||||
descriptor.Type,
|
||||
descriptor.Stages);
|
||||
}
|
||||
else
|
||||
{
|
||||
pdUniforms[j++] = descriptor;
|
||||
}
|
||||
}
|
||||
|
||||
result[i] = new ResourceDescriptorCollection(new(pdUniforms));
|
||||
}
|
||||
else
|
||||
{
|
||||
result[i] = sets[i];
|
||||
}
|
||||
}
|
||||
|
||||
return new(result);
|
||||
}
|
||||
|
||||
private static ResourceBindingSegment[][] BuildClearSegments(ReadOnlyCollection<ResourceDescriptorCollection> sets)
|
||||
{
|
||||
ResourceBindingSegment[][] segments = new ResourceBindingSegment[sets.Count][];
|
||||
@ -243,12 +320,18 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return segments;
|
||||
}
|
||||
|
||||
private DescriptorSetTemplate[] BuildTemplates()
|
||||
private DescriptorSetTemplate[] BuildTemplates(bool usePushDescriptors)
|
||||
{
|
||||
var templates = new DescriptorSetTemplate[BindingSegments.Length];
|
||||
|
||||
for (int setIndex = 0; setIndex < BindingSegments.Length; setIndex++)
|
||||
{
|
||||
if (usePushDescriptors && setIndex == 0)
|
||||
{
|
||||
// Push descriptors get updated using templates owned by the pipeline layout.
|
||||
continue;
|
||||
}
|
||||
|
||||
ResourceBindingSegment[] segments = BindingSegments[setIndex];
|
||||
|
||||
if (segments != null && segments.Length > 0)
|
||||
@ -433,6 +516,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return null;
|
||||
}
|
||||
|
||||
public DescriptorSetTemplate GetPushDescriptorTemplate(long updateMask)
|
||||
{
|
||||
return _plce.GetPushDescriptorTemplate(IsCompute ? PipelineBindPoint.Compute : PipelineBindPoint.Graphics, updateMask);
|
||||
}
|
||||
|
||||
public void AddComputePipeline(ref SpecData key, Auto<DisposablePipeline> pipeline)
|
||||
{
|
||||
(_computePipelineCache ??= new()).Add(ref key, pipeline);
|
||||
@ -493,6 +581,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return _plce.GetNewDescriptorSetCollection(setIndex, out isNew);
|
||||
}
|
||||
|
||||
public bool HasSameLayout(ShaderCollection other)
|
||||
{
|
||||
return other != null && _plce == other._plce;
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
|
@ -154,9 +154,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
Format.S8Uint => Format.R8Unorm,
|
||||
Format.D16Unorm => Format.R16Unorm,
|
||||
Format.S8UintD24Unorm => Format.R8G8B8A8Unorm,
|
||||
Format.D24UnormS8Uint or Format.S8UintD24Unorm or Format.X8UintD24Unorm => Format.R8G8B8A8Unorm,
|
||||
Format.D32Float => Format.R32Float,
|
||||
Format.D24UnormS8Uint => Format.R8G8B8A8Unorm,
|
||||
Format.D32FloatS8Uint => Format.R32G32Float,
|
||||
_ => throw new ArgumentException($"\"{format}\" is not a supported depth or stencil format."),
|
||||
};
|
||||
@ -434,99 +433,65 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return FormatCapabilities.IsD24S8(Info.Format) && VkFormat == VkFormat.D32SfloatS8Uint;
|
||||
}
|
||||
|
||||
public void SetModification(AccessFlags accessFlags, PipelineStageFlags stage)
|
||||
public void QueueLoadOpBarrier(CommandBufferScoped cbs, bool depthStencil)
|
||||
{
|
||||
_lastModificationAccess = accessFlags;
|
||||
_lastModificationStage = stage;
|
||||
}
|
||||
PipelineStageFlags srcStageFlags = _lastReadStage | _lastModificationStage;
|
||||
PipelineStageFlags dstStageFlags = depthStencil ?
|
||||
PipelineStageFlags.EarlyFragmentTestsBit | PipelineStageFlags.LateFragmentTestsBit :
|
||||
PipelineStageFlags.ColorAttachmentOutputBit;
|
||||
|
||||
public void InsertReadToWriteBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags, bool insideRenderPass)
|
||||
{
|
||||
var lastReadStage = _lastReadStage;
|
||||
AccessFlags srcAccessFlags = _lastModificationAccess | _lastReadAccess;
|
||||
AccessFlags dstAccessFlags = depthStencil ?
|
||||
AccessFlags.DepthStencilAttachmentWriteBit | AccessFlags.DepthStencilAttachmentReadBit :
|
||||
AccessFlags.ColorAttachmentWriteBit | AccessFlags.ColorAttachmentReadBit;
|
||||
|
||||
if (insideRenderPass)
|
||||
if (srcAccessFlags != AccessFlags.None)
|
||||
{
|
||||
// We can't have barrier from compute inside a render pass,
|
||||
// as it is invalid to specify compute in the subpass dependency stage mask.
|
||||
ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
|
||||
ImageMemoryBarrier barrier = TextureView.GetImageBarrier(
|
||||
_imageAuto.Get(cbs).Value,
|
||||
srcAccessFlags,
|
||||
dstAccessFlags,
|
||||
aspectFlags,
|
||||
0,
|
||||
0,
|
||||
_info.GetLayers(),
|
||||
_info.Levels);
|
||||
|
||||
lastReadStage &= ~PipelineStageFlags.ComputeShaderBit;
|
||||
}
|
||||
_gd.Barriers.QueueBarrier(barrier, srcStageFlags, dstStageFlags);
|
||||
|
||||
if (lastReadStage != PipelineStageFlags.None)
|
||||
{
|
||||
// This would result in a validation error, but is
|
||||
// required on MoltenVK as the generic barrier results in
|
||||
// severe texture flickering in some scenarios.
|
||||
if (_gd.IsMoltenVk)
|
||||
{
|
||||
ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
|
||||
TextureView.InsertImageBarrier(
|
||||
_gd.Api,
|
||||
cbs.CommandBuffer,
|
||||
_imageAuto.Get(cbs).Value,
|
||||
_lastReadAccess,
|
||||
dstAccessFlags,
|
||||
_lastReadStage,
|
||||
dstStageFlags,
|
||||
aspectFlags,
|
||||
0,
|
||||
0,
|
||||
_info.GetLayers(),
|
||||
_info.Levels);
|
||||
}
|
||||
else
|
||||
{
|
||||
TextureView.InsertMemoryBarrier(
|
||||
_gd.Api,
|
||||
cbs.CommandBuffer,
|
||||
_lastReadAccess,
|
||||
dstAccessFlags,
|
||||
lastReadStage,
|
||||
dstStageFlags);
|
||||
}
|
||||
|
||||
_lastReadAccess = AccessFlags.None;
|
||||
_lastReadStage = PipelineStageFlags.None;
|
||||
_lastReadAccess = AccessFlags.None;
|
||||
}
|
||||
|
||||
_lastModificationStage = depthStencil ?
|
||||
PipelineStageFlags.LateFragmentTestsBit :
|
||||
PipelineStageFlags.ColorAttachmentOutputBit;
|
||||
|
||||
_lastModificationAccess = depthStencil ?
|
||||
AccessFlags.DepthStencilAttachmentWriteBit :
|
||||
AccessFlags.ColorAttachmentWriteBit;
|
||||
}
|
||||
|
||||
public void InsertWriteToReadBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags)
|
||||
public void QueueWriteToReadBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags)
|
||||
{
|
||||
_lastReadAccess |= dstAccessFlags;
|
||||
_lastReadStage |= dstStageFlags;
|
||||
|
||||
if (_lastModificationAccess != AccessFlags.None)
|
||||
{
|
||||
// This would result in a validation error, but is
|
||||
// required on MoltenVK as the generic barrier results in
|
||||
// severe texture flickering in some scenarios.
|
||||
if (_gd.IsMoltenVk)
|
||||
{
|
||||
ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
|
||||
TextureView.InsertImageBarrier(
|
||||
_gd.Api,
|
||||
cbs.CommandBuffer,
|
||||
_imageAuto.Get(cbs).Value,
|
||||
_lastModificationAccess,
|
||||
dstAccessFlags,
|
||||
_lastModificationStage,
|
||||
dstStageFlags,
|
||||
aspectFlags,
|
||||
0,
|
||||
0,
|
||||
_info.GetLayers(),
|
||||
_info.Levels);
|
||||
}
|
||||
else
|
||||
{
|
||||
TextureView.InsertMemoryBarrier(
|
||||
_gd.Api,
|
||||
cbs.CommandBuffer,
|
||||
_lastModificationAccess,
|
||||
dstAccessFlags,
|
||||
_lastModificationStage,
|
||||
dstStageFlags);
|
||||
}
|
||||
ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
|
||||
ImageMemoryBarrier barrier = TextureView.GetImageBarrier(
|
||||
_imageAuto.Get(cbs).Value,
|
||||
_lastModificationAccess,
|
||||
dstAccessFlags,
|
||||
aspectFlags,
|
||||
0,
|
||||
0,
|
||||
_info.GetLayers(),
|
||||
_info.Levels);
|
||||
|
||||
_gd.Barriers.QueueBarrier(barrier, _lastModificationStage, dstStageFlags);
|
||||
|
||||
_lastModificationAccess = AccessFlags.None;
|
||||
}
|
||||
|
@ -497,6 +497,30 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
null);
|
||||
}
|
||||
|
||||
public static ImageMemoryBarrier GetImageBarrier(
|
||||
Image image,
|
||||
AccessFlags srcAccessMask,
|
||||
AccessFlags dstAccessMask,
|
||||
ImageAspectFlags aspectFlags,
|
||||
int firstLayer,
|
||||
int firstLevel,
|
||||
int layers,
|
||||
int levels)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
SType = StructureType.ImageMemoryBarrier,
|
||||
SrcAccessMask = srcAccessMask,
|
||||
DstAccessMask = dstAccessMask,
|
||||
SrcQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
||||
DstQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
||||
Image = image,
|
||||
OldLayout = ImageLayout.General,
|
||||
NewLayout = ImageLayout.General,
|
||||
SubresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, (uint)levels, (uint)firstLayer, (uint)layers),
|
||||
};
|
||||
}
|
||||
|
||||
public static unsafe void InsertImageBarrier(
|
||||
Vk api,
|
||||
CommandBuffer commandBuffer,
|
||||
@ -511,18 +535,15 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
int layers,
|
||||
int levels)
|
||||
{
|
||||
ImageMemoryBarrier memoryBarrier = new()
|
||||
{
|
||||
SType = StructureType.ImageMemoryBarrier,
|
||||
SrcAccessMask = srcAccessMask,
|
||||
DstAccessMask = dstAccessMask,
|
||||
SrcQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
||||
DstQueueFamilyIndex = Vk.QueueFamilyIgnored,
|
||||
Image = image,
|
||||
OldLayout = ImageLayout.General,
|
||||
NewLayout = ImageLayout.General,
|
||||
SubresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, (uint)levels, (uint)firstLayer, (uint)layers),
|
||||
};
|
||||
ImageMemoryBarrier memoryBarrier = GetImageBarrier(
|
||||
image,
|
||||
srcAccessMask,
|
||||
dstAccessMask,
|
||||
aspectFlags,
|
||||
firstLayer,
|
||||
firstLevel,
|
||||
layers,
|
||||
levels);
|
||||
|
||||
api.CmdPipelineBarrier(
|
||||
commandBuffer,
|
||||
@ -839,7 +860,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
for (int level = 0; level < levels; level++)
|
||||
{
|
||||
int mipSize = GetBufferDataLength(Info.GetMipSize2D(dstLevel + level) * dstLayers);
|
||||
int mipSize = GetBufferDataLength(is3D && !singleSlice
|
||||
? Info.GetMipSize(dstLevel + level)
|
||||
: Info.GetMipSize2D(dstLevel + level) * dstLayers);
|
||||
|
||||
int endOffset = offset + mipSize;
|
||||
|
||||
|
@ -20,6 +20,9 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
[GeneratedRegex("Radeon (((HD|R(5|7|9|X)) )?((M?[2-6]\\d{2}(\\D|$))|([7-8]\\d{3}(\\D|$))|Fury|Nano))|(Pro Duo)")]
|
||||
public static partial Regex AmdGcnRegex();
|
||||
|
||||
[GeneratedRegex("NVIDIA GeForce (R|G)?TX? (\\d{3}\\d?)M?")]
|
||||
public static partial Regex NvidiaConsumerClassRegex();
|
||||
|
||||
public static Vendor FromId(uint id)
|
||||
{
|
||||
return id switch
|
||||
|
@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
public const bool UseFastBufferUpdates = true;
|
||||
public const bool UseUnsafeBlit = true;
|
||||
public const bool UsePushDescriptors = false;
|
||||
public const bool UsePushDescriptors = true;
|
||||
|
||||
public const bool ForceD24S8Unsupported = false;
|
||||
public const bool ForceRGB16IntFloatUnsupported = false;
|
||||
|
@ -486,20 +486,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
pExtendedFeatures = &featuresFragmentShaderInterlock;
|
||||
}
|
||||
|
||||
PhysicalDeviceSubgroupSizeControlFeaturesEXT featuresSubgroupSizeControl;
|
||||
|
||||
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_subgroup_size_control"))
|
||||
{
|
||||
featuresSubgroupSizeControl = new PhysicalDeviceSubgroupSizeControlFeaturesEXT
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceSubgroupSizeControlFeaturesExt,
|
||||
PNext = pExtendedFeatures,
|
||||
SubgroupSizeControl = true,
|
||||
};
|
||||
|
||||
pExtendedFeatures = &featuresSubgroupSizeControl;
|
||||
}
|
||||
|
||||
PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor;
|
||||
|
||||
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color") &&
|
||||
|
@ -68,6 +68,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
internal HelperShader HelperShader { get; private set; }
|
||||
internal PipelineFull PipelineInternal => _pipeline;
|
||||
|
||||
internal BarrierBatch Barriers { get; private set; }
|
||||
|
||||
public IPipeline Pipeline => _pipeline;
|
||||
|
||||
public IWindow Window => _window;
|
||||
@ -76,10 +78,15 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private readonly Func<string[]> _getRequiredExtensions;
|
||||
private readonly string _preferredGpuId;
|
||||
|
||||
private int[] _pdReservedBindings;
|
||||
private readonly static int[] _pdReservedBindingsNvn = { 3, 18, 21, 36, 30 };
|
||||
private readonly static int[] _pdReservedBindingsOgl = { 17, 18, 34, 35, 36 };
|
||||
|
||||
internal Vendor Vendor { get; private set; }
|
||||
internal bool IsAmdWindows { get; private set; }
|
||||
internal bool IsIntelWindows { get; private set; }
|
||||
internal bool IsAmdGcn { get; private set; }
|
||||
internal bool IsNvidiaPreTuring { get; private set; }
|
||||
internal bool IsMoltenVk { get; private set; }
|
||||
internal bool IsTBDR { get; private set; }
|
||||
internal bool IsSharedMemory { get; private set; }
|
||||
@ -191,6 +198,19 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
SType = StructureType.PhysicalDevicePortabilitySubsetPropertiesKhr,
|
||||
};
|
||||
|
||||
bool supportsPushDescriptors = _physicalDevice.IsDeviceExtensionPresent(KhrPushDescriptor.ExtensionName);
|
||||
|
||||
PhysicalDevicePushDescriptorPropertiesKHR propertiesPushDescriptor = new PhysicalDevicePushDescriptorPropertiesKHR()
|
||||
{
|
||||
SType = StructureType.PhysicalDevicePushDescriptorPropertiesKhr
|
||||
};
|
||||
|
||||
if (supportsPushDescriptors)
|
||||
{
|
||||
propertiesPushDescriptor.PNext = properties2.PNext;
|
||||
properties2.PNext = &propertiesPushDescriptor;
|
||||
}
|
||||
|
||||
PhysicalDeviceFeatures2 features2 = new()
|
||||
{
|
||||
SType = StructureType.PhysicalDeviceFeatures2,
|
||||
@ -320,7 +340,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName),
|
||||
features2.Features.MultiViewport && !(IsMoltenVk && Vendor == Vendor.Amd), // Workaround for AMD on MoltenVK issue
|
||||
featuresRobustness2.NullDescriptor || IsMoltenVk,
|
||||
_physicalDevice.IsDeviceExtensionPresent(KhrPushDescriptor.ExtensionName),
|
||||
supportsPushDescriptors && !IsMoltenVk,
|
||||
propertiesPushDescriptor.MaxPushDescriptors,
|
||||
featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart,
|
||||
featuresPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart,
|
||||
supportsTransformFeedback,
|
||||
@ -362,6 +383,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
HelperShader = new HelperShader(this, _device);
|
||||
|
||||
Barriers = new BarrierBatch(this);
|
||||
|
||||
_counters = new Counters(this, _device, _pipeline);
|
||||
}
|
||||
|
||||
@ -400,6 +423,25 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_initialized = true;
|
||||
}
|
||||
|
||||
internal int[] GetPushDescriptorReservedBindings(bool isOgl)
|
||||
{
|
||||
// The first call of this method determines what push descriptor layout is used for all shaders on this renderer.
|
||||
// This is chosen to minimize shaders that can't fit their uniforms on the device's max number of push descriptors.
|
||||
if (_pdReservedBindings == null)
|
||||
{
|
||||
if (Capabilities.MaxPushDescriptors <= Constants.MaxUniformBuffersPerStage * 2)
|
||||
{
|
||||
_pdReservedBindings = isOgl ? _pdReservedBindingsOgl : _pdReservedBindingsNvn;
|
||||
}
|
||||
else
|
||||
{
|
||||
_pdReservedBindings = Array.Empty<int>();
|
||||
}
|
||||
}
|
||||
|
||||
return _pdReservedBindings;
|
||||
}
|
||||
|
||||
public BufferHandle CreateBuffer(int size, BufferAccess access)
|
||||
{
|
||||
return BufferManager.CreateWithHandle(this, size, access.HasFlag(BufferAccess.SparseCompatible), access.Convert(), default, access == BufferAccess.Stream);
|
||||
@ -716,6 +758,20 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
IsAmdGcn = !IsMoltenVk && Vendor == Vendor.Amd && VendorUtils.AmdGcnRegex().IsMatch(GpuRenderer);
|
||||
|
||||
if (Vendor == Vendor.Nvidia)
|
||||
{
|
||||
var match = VendorUtils.NvidiaConsumerClassRegex().Match(GpuRenderer);
|
||||
|
||||
if (match != null && int.TryParse(match.Groups[2].Value, out int gpuNumber))
|
||||
{
|
||||
IsNvidiaPreTuring = gpuNumber < 2000;
|
||||
}
|
||||
else if (GpuDriver.Contains("TITAN") && !GpuDriver.Contains("RTX"))
|
||||
{
|
||||
IsNvidiaPreTuring = true;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
|
||||
}
|
||||
|
||||
@ -862,6 +918,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
BufferManager.Dispose();
|
||||
DescriptorSetManager.Dispose();
|
||||
PipelineLayoutCache.Dispose();
|
||||
Barriers.Dispose();
|
||||
|
||||
MemoryAllocator.Dispose();
|
||||
|
||||
|
@ -7,7 +7,7 @@ using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using Ryujinx.HLE.Ui;
|
||||
using Ryujinx.HLE.UI;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE
|
||||
@ -63,7 +63,7 @@ namespace Ryujinx.HLE
|
||||
/// The handler for various UI related operations needed outside of HLE.
|
||||
/// </summary>
|
||||
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
|
||||
internal readonly IHostUiHandler HostUiHandler;
|
||||
internal readonly IHostUIHandler HostUIHandler;
|
||||
|
||||
/// <summary>
|
||||
/// Control the memory configuration used by the emulation context.
|
||||
@ -177,7 +177,7 @@ namespace Ryujinx.HLE
|
||||
IRenderer gpuRenderer,
|
||||
IHardwareDeviceDriver audioDeviceDriver,
|
||||
MemoryConfiguration memoryConfiguration,
|
||||
IHostUiHandler hostUiHandler,
|
||||
IHostUIHandler hostUIHandler,
|
||||
SystemLanguage systemLanguage,
|
||||
RegionCode region,
|
||||
bool enableVsync,
|
||||
@ -204,7 +204,7 @@ namespace Ryujinx.HLE
|
||||
GpuRenderer = gpuRenderer;
|
||||
AudioDeviceDriver = audioDeviceDriver;
|
||||
MemoryConfiguration = memoryConfiguration;
|
||||
HostUiHandler = hostUiHandler;
|
||||
HostUIHandler = hostUIHandler;
|
||||
SystemLanguage = systemLanguage;
|
||||
Region = region;
|
||||
EnableVsync = enableVsync;
|
||||
|
@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Applets
|
||||
PlayerIndex primaryIndex;
|
||||
while (!_system.Device.Hid.Npads.Validate(playerMin, playerMax, (ControllerType)privateArg.NpadStyleSet, out configuredCount, out primaryIndex))
|
||||
{
|
||||
ControllerAppletUiArgs uiArgs = new()
|
||||
ControllerAppletUIArgs uiArgs = new()
|
||||
{
|
||||
PlayerCountMin = playerMin,
|
||||
PlayerCountMax = playerMax,
|
||||
@ -95,7 +95,7 @@ namespace Ryujinx.HLE.HOS.Applets
|
||||
IsDocked = _system.State.DockedMode,
|
||||
};
|
||||
|
||||
if (!_system.Device.UiHandler.DisplayMessageDialog(uiArgs))
|
||||
if (!_system.Device.UIHandler.DisplayMessageDialog(uiArgs))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Applets
|
||||
{
|
||||
public struct ControllerAppletUiArgs
|
||||
public struct ControllerAppletUIArgs
|
||||
{
|
||||
public int PlayerCountMin;
|
||||
public int PlayerCountMax;
|
@ -166,13 +166,13 @@ namespace Ryujinx.HLE.HOS.Applets.Error
|
||||
|
||||
string[] buttons = GetButtonsText(module, description, "DlgBtn");
|
||||
|
||||
bool showDetails = _horizon.Device.UiHandler.DisplayErrorAppletDialog($"Error Code: {module}-{description:0000}", "\n" + message, buttons);
|
||||
bool showDetails = _horizon.Device.UIHandler.DisplayErrorAppletDialog($"Error Code: {module}-{description:0000}", "\n" + message, buttons);
|
||||
if (showDetails)
|
||||
{
|
||||
message = GetMessageText(module, description, "FlvMsg");
|
||||
buttons = GetButtonsText(module, description, "FlvBtn");
|
||||
|
||||
_horizon.Device.UiHandler.DisplayErrorAppletDialog($"Details: {module}-{description:0000}", "\n" + message, buttons);
|
||||
_horizon.Device.UIHandler.DisplayErrorAppletDialog($"Details: {module}-{description:0000}", "\n" + message, buttons);
|
||||
}
|
||||
}
|
||||
|
||||
@ -200,12 +200,12 @@ namespace Ryujinx.HLE.HOS.Applets.Error
|
||||
|
||||
buttons.Add("OK");
|
||||
|
||||
bool showDetails = _horizon.Device.UiHandler.DisplayErrorAppletDialog($"Error Number: {applicationErrorArg.ErrorNumber}", "\n" + messageText, buttons.ToArray());
|
||||
bool showDetails = _horizon.Device.UIHandler.DisplayErrorAppletDialog($"Error Number: {applicationErrorArg.ErrorNumber}", "\n" + messageText, buttons.ToArray());
|
||||
if (showDetails)
|
||||
{
|
||||
buttons.RemoveAt(0);
|
||||
|
||||
_horizon.Device.UiHandler.DisplayErrorAppletDialog($"Error Number: {applicationErrorArg.ErrorNumber} (Details)", "\n" + detailsText, buttons.ToArray());
|
||||
_horizon.Device.UIHandler.DisplayErrorAppletDialog($"Error Number: {applicationErrorArg.ErrorNumber} (Details)", "\n" + detailsText, buttons.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user