Compare commits

...

21 Commits

Author SHA1 Message Date
riperiperi
ba91f5d401 Vulkan: Properly reset barrier batch when splitting due to mismatching flags (#6345)
Forgot to set the end variable here. Should stop it from crashing when this path is taken.
2024-02-22 10:43:22 +01:00
riperiperi
79f6c18a9b Vulkan: Disable push descriptors on older NVIDIA GPUs (#6340)
Disables push descriptors on older NVIDIA GPUs (10xx and below), since it is clearly broken beyond comprehension. The existing workaround wasn't good enough and a more thorough one will probably cost more performance than the feature gains. The workaround has been removed.

Fixes #6331.
2024-02-21 23:52:13 -03:00
riperiperi
4f63782bac Vulkan: Fix barrier batching past limit (#6339)
If more than 16 barriers were queued at one time, the _queuedBarrierCount would no longer match the number of remaining barriers, because when breaking out of the loop consuming them it deleted all barriers, not just the 16 that were consumed.

Should fix freezes that started occurring with #6240. Fixes issue #6338.
2024-02-21 23:41:08 -03:00
MetrosexualGarbodor
6f5fcb7970 Avalonia UI: Update English tooltips (#6305)
* update English tooltips

* missed a dot
2024-02-19 23:27:15 +01:00
Mary Guillemard
6ef8946169 Avalonia: Fix gamescope once and for all (#6301)
Enable input focus proxy, makes WM_TAKE_FOCUS capability to be exposed.

This fix menu on gamescope, for real this time....

Signed-off-by: Mary Guillemard <mary@mary.zone>
2024-02-19 21:18:46 +01:00
gdkchan
42340fc743 LightningJit: Add a limit on the number of instructions per function for Arm64 (#6328) 2024-02-17 17:30:54 -03:00
Exhigh
103e7cb021 hid: Stub SetTouchScreenResolution (#6322)
* hid: Implement SetTouchScreenResolution

* Fix Tomb Raider I-III Remastered from crashing without enabling Ignore Missing Services

* PR Feedback: Update Comments
2024-02-17 14:49:50 -03:00
riperiperi
31ed061bea Vulkan: Improve texture barrier usage, timing and batching (#6240)
* WIP barrier batch

* Add store op to image usage barrier

* Dispose the barrier batch

* Fix encoding?

* Handle read and write on the load op barrier.

Load op consumes read accesses but does not add one, as the only other operation that can read is another load.

* Simplify null check

* Insert barriers on program change in case stale bindings are reintroduced

* Not sure how I messed this one up

* Improve location of bindings barrier update

This is also important for emergency deferred clear

* Update src/Ryujinx.Graphics.Vulkan/BarrierBatch.cs

Co-authored-by: Mary Guillemard <thog@protonmail.com>

---------

Co-authored-by: Mary Guillemard <thog@protonmail.com>
2024-02-17 00:21:37 -03:00
riperiperi
4218311e6a Vulkan: Use push descriptors for uniform bindings when possible (#6154)
* Fix Push Descriptors

* Use push descriptor templates

* Use reserved bindings

* Formatting

* Disable when using MVK

("my heart will go on" starts playing as thousands of mac users shed a tear in unison)

* Introduce limit on push descriptor binding number

The bitmask used for updating push descriptors is ulong, so only 64 bindings can be tracked for now.

* Address feedback

* Fix logic for binding rejection

Should only offset limit when reserved bindings are less than the requested one.

* Workaround pascal and older nv bug

* Add GPU number detection for nvidia

* Only do workaround if it's valid to do so.
2024-02-16 21:41:30 -03:00
gdkchan
e37735ed26 Implement X8Z24 texture format (#6315) 2024-02-15 19:06:26 -03:00
gdkchan
74a18b7c18 Fix PermissionLocked check on UnmapProcessCodeMemory (#6314) 2024-02-15 16:16:01 -03:00
gdkchan
74fe814329 Remove Vulkan SubgroupSizeControl enablement code (#6317) 2024-02-15 16:04:30 -03:00
gdkchan
d1a093e5ca Stub VSMS related ioctls (#6313)
* Stub VSMS related ioctls

* Clean up usings
2024-02-15 19:48:22 +01:00
Isaac Marovitz
dfb14a5607 Updaters: Fix ARM Linux Updater (#6316)
* Remove Arch Checks

* Fix ARM Linux updater
2024-02-15 10:41:43 +01:00
jcm
904a5ffcb4 Handle exceptions when checking user data directory for symlink (#6304)
Co-authored-by: jcm <butt@butts.com>
2024-02-12 00:10:21 +01:00
jcm
946633276b macOS: Stop storing user data in Documents for some users; fix symlinks (#6241)
* macOS: Stop storing user data in Documents for some users; fix symlinks

* Use SupportedOSPlatform tag, catch exceptions, log warning instead of error

* Provide best path hints to user if symlink fixup fails

---------

Co-authored-by: jcm <butt@butts.com>
2024-02-11 19:04:39 +01:00
Mary Guillemard
baf94e0e3e infra: Force add linux-x64 apphost in flathub nuget source (#6302)
Required when building on the arm64 runner.

Signed-off-by: Mary Guillemard <mary@mary.zone>
2024-02-11 16:45:58 +01:00
Mary Guillemard
cf6201a4a6 infra: Restore Nuget packages for linux-arm64 for Flatpak
Signed-off-by: Mary Guillemard <mary@mary.zone>
2024-02-11 13:21:54 +01:00
Isaac Marovitz
18909195d1 Old buttons (#6237) 2024-02-11 03:12:43 +01:00
Isaac Marovitz
f06d22d6f0 Infra: Capitalisation Consistency (#6296)
* Rename Ryujinx.UI.Common

* Rename Ryujinx.UI.LocaleGenerator

* Update in Files

AboutWindow

* Configuration State

* Rename projects

* Ryujinx/UI

* Fix build

* Main remaining inconsistencies

* HLE.UI Namespace

* HLE.UI Files

* Namespace

* Ryujinx.UI.Common.Configuration.UI

* Ryujinx.UI.Common,Configuration.UI Files

* More instances
2024-02-11 03:09:18 +01:00
jcm
84d6e8d121 Standardize logging locations across desktop platforms (#6238)
* Standardize logging locations across desktop platforms

* Return null instead of empty literal on exceptions

* Remove LogDirectoryPath from LoggerModule

* Catch exception when creating DirectoryInfo in FileLogTarget

* Remove redundant log path vars, handle exception better, add null check

* Address styling issues

* Remove extra newline, quote file path in log, move directory check to OpenHelper

* Add GetOrCreateLogsDir to get/create log directory during runtime

* misc format changes

* Update src/Ryujinx.Common/Configuration/AppDataManager.cs

---------

Co-authored-by: jcm <butt@butts.com>
Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com>
Co-authored-by: Ac_K <Acoustik666@gmail.com>
2024-02-11 02:17:19 +01:00
238 changed files with 1948 additions and 1026 deletions

2
.github/labeler.yml vendored
View File

@@ -20,7 +20,7 @@ gpu:
gui: gui:
- changed-files: - 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: horizon:
- changed-files: - changed-files:

View File

@@ -51,38 +51,76 @@ jobs:
- name: Restore Nuget packages - 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. # 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 # 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 - name: Generate nuget_sources.json
shell: python shell: python
run: | run: |
import hashlib
from pathlib import Path from pathlib import Path
import base64 import base64
import binascii import binascii
import json import json
import os import os
import urllib.request
sources = [] 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: def create_source_from_external(name, version):
sha512 = binascii.hexlify(base64.b64decode(fp.read())).decode('ascii') full_dir_path = Path(os.environ["NUGET_PACKAGES"]).joinpath(name).joinpath(version)
os.makedirs(full_dir_path, exist_ok=True)
sources.append({ filename = "{}.{}.nupkg".format(name, version)
'type': 'file', url = "https://api.nuget.org/v3-flatcontainer/{}/{}/{}".format(
'url': url, name, version, filename
'sha512': sha512, )
'dest': os.environ['NUGET_SOURCES_DESTDIR'],
'dest-filename': filename,
})
with open('flathub/nuget_sources.json', 'w') as fp: print(f"Processing {url}...")
json.dump(sources, fp, indent=4) 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 - name: Update flatpak metadata
id: metadata id: metadata

View File

@@ -71,7 +71,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Nvdec.FFmp
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ava", "src\Ryujinx.Ava\Ryujinx.Ava.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Ava", "src\Ryujinx.Ava\Ryujinx.Ava.csproj", "{7C1B2721-13DA-4B62-B046-C626605ECCE6}"
EndProject 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 EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Generators", "src\Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj", "{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Generators", "src\Ryujinx.Horizon.Generators\Ryujinx.Horizon.Generators.csproj", "{6AE2A5E8-4C5A-48B9-997B-E1455C0355C6}"
EndProject EndProject
@@ -79,7 +79,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics.Vulkan", "
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spv.Generator", "src\Spv.Generator\Spv.Generator.csproj", "{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spv.Generator", "src\Spv.Generator\Spv.Generator.csproj", "{2BCB3D7A-38C0-4FE7-8FDA-374C6AD56D0E}"
EndProject 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 EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Common", "src\Ryujinx.Horizon.Common\Ryujinx.Horizon.Common.csproj", "{77F96ECE-4952-42DB-A528-DED25572A573}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Horizon.Common", "src\Ryujinx.Horizon.Common\Ryujinx.Horizon.Common.csproj", "{77F96ECE-4952-42DB-A528-DED25572A573}"
EndProject EndProject

View File

@@ -8,8 +8,8 @@ using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.UI.Common.Configuration;
using Ryujinx.Ui.Common.Helper; using Ryujinx.UI.Common.Helper;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
@@ -42,9 +42,9 @@ namespace Ryujinx.Ava
{ {
ApplyConfiguredTheme(); ApplyConfiguredTheme();
ConfigurationState.Instance.Ui.BaseStyle.Event += ThemeChanged_Event; ConfigurationState.Instance.UI.BaseStyle.Event += ThemeChanged_Event;
ConfigurationState.Instance.Ui.CustomThemePath.Event += ThemeChanged_Event; ConfigurationState.Instance.UI.CustomThemePath.Event += ThemeChanged_Event;
ConfigurationState.Instance.Ui.EnableCustomTheme.Event += CustomThemeChanged_Event; ConfigurationState.Instance.UI.EnableCustomTheme.Event += CustomThemeChanged_Event;
} }
} }
@@ -88,13 +88,13 @@ namespace Ryujinx.Ava
{ {
try try
{ {
string baseStyle = ConfigurationState.Instance.Ui.BaseStyle; string baseStyle = ConfigurationState.Instance.UI.BaseStyle;
if (string.IsNullOrWhiteSpace(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 RequestedThemeVariant = baseStyle switch

View File

@@ -34,10 +34,10 @@ using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.Input; using Ryujinx.Input;
using Ryujinx.Input.HLE; using Ryujinx.Input.HLE;
using Ryujinx.Ui.App.Common; using Ryujinx.UI.App.Common;
using Ryujinx.Ui.Common; using Ryujinx.UI.Common;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.UI.Common.Configuration;
using Ryujinx.Ui.Common.Helper; using Ryujinx.UI.Common.Helper;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using SixLabors.ImageSharp; using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.Formats.Png;
@@ -1070,7 +1070,7 @@ namespace Ryujinx.Ava
case KeyboardHotkeyState.Screenshot: case KeyboardHotkeyState.Screenshot:
ScreenshotRequested = true; ScreenshotRequested = true;
break; break;
case KeyboardHotkeyState.ShowUi: case KeyboardHotkeyState.ShowUI:
_viewModel.ShowMenuAndStatusBar = !_viewModel.ShowMenuAndStatusBar; _viewModel.ShowMenuAndStatusBar = !_viewModel.ShowMenuAndStatusBar;
break; break;
case KeyboardHotkeyState.Pause: case KeyboardHotkeyState.Pause:
@@ -1160,9 +1160,9 @@ namespace Ryujinx.Ava
{ {
state = KeyboardHotkeyState.Screenshot; 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)) else if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause))
{ {

View File

@@ -73,6 +73,10 @@
"GameListContextMenuCreateShortcut": "Create Application Shortcut", "GameListContextMenuCreateShortcut": "Create Application Shortcut",
"GameListContextMenuCreateShortcutToolTip": "Create a Desktop Shortcut that launches the selected Application", "GameListContextMenuCreateShortcutToolTip": "Create a Desktop Shortcut that launches the selected Application",
"GameListContextMenuCreateShortcutToolTipMacOS": "Create a shortcut in macOS's Applications folder 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", "StatusBarGamesLoaded": "{0}/{1} Games Loaded",
"StatusBarSystemVersion": "System Version: {0}", "StatusBarSystemVersion": "System Version: {0}",
"LinuxVmMaxMapCountDialogTitle": "Low limit for memory mappings detected", "LinuxVmMaxMapCountDialogTitle": "Low limit for memory mappings detected",
@@ -151,7 +155,7 @@
"SettingsTabGraphicsResolutionScaleNative": "Native (720p/1080p)", "SettingsTabGraphicsResolutionScaleNative": "Native (720p/1080p)",
"SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)", "SettingsTabGraphicsResolutionScale2x": "2x (1440p/2160p)",
"SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)", "SettingsTabGraphicsResolutionScale3x": "3x (2160p/3240p)",
"SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p)", "SettingsTabGraphicsResolutionScale4x": "4x (2880p/4320p) (Not recommended)",
"SettingsTabGraphicsAspectRatio": "Aspect Ratio:", "SettingsTabGraphicsAspectRatio": "Aspect Ratio:",
"SettingsTabGraphicsAspectRatio4x3": "4:3", "SettingsTabGraphicsAspectRatio4x3": "4:3",
"SettingsTabGraphicsAspectRatio16x9": "16:9", "SettingsTabGraphicsAspectRatio16x9": "16:9",
@@ -329,8 +333,6 @@
"DialogUpdaterAddingFilesMessage": "Adding New Update...", "DialogUpdaterAddingFilesMessage": "Adding New Update...",
"DialogUpdaterCompleteMessage": "Update Complete!", "DialogUpdaterCompleteMessage": "Update Complete!",
"DialogUpdaterRestartMessage": "Do you want to restart Ryujinx now?", "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!", "DialogUpdaterNoInternetMessage": "You are not connected to the Internet!",
"DialogUpdaterNoInternetSubMessage": "Please verify that you have a working Internet connection!", "DialogUpdaterNoInternetSubMessage": "Please verify that you have a working Internet connection!",
"DialogUpdaterDirtyBuildMessage": "You Cannot update a Dirty build of Ryujinx!", "DialogUpdaterDirtyBuildMessage": "You Cannot update a Dirty build of Ryujinx!",
@@ -450,13 +452,13 @@
"CustomThemePathTooltip": "Path to custom GUI theme", "CustomThemePathTooltip": "Path to custom GUI theme",
"CustomThemeBrowseTooltip": "Browse for a custom GUI theme", "CustomThemeBrowseTooltip": "Browse for a custom GUI theme",
"DockModeToggleTooltip": "Docked mode makes the emulated system behave as a docked Nintendo Switch. This improves graphical fidelity in most games. Conversely, disabling this will make the emulated system behave as a handheld Nintendo Switch, reducing graphics quality.\n\nConfigure player 1 controls if planning to use docked mode; configure handheld controls if planning to use handheld mode.\n\nLeave ON if unsure.", "DockModeToggleTooltip": "Docked mode makes the emulated system behave as a docked Nintendo Switch. This improves graphical fidelity in most games. Conversely, disabling this will make the emulated system behave as a handheld Nintendo Switch, reducing graphics quality.\n\nConfigure player 1 controls if planning to use docked mode; configure handheld controls if planning to use handheld mode.\n\nLeave ON if unsure.",
"DirectKeyboardTooltip": "Direct keyboard access (HID) support. Provides games access to your keyboard as a text entry device.", "DirectKeyboardTooltip": "Direct keyboard access (HID) support. Provides games access to your keyboard as a text entry device.\n\nOnly works with games that natively support keyboard usage on Switch hardware.\n\nLeave OFF if unsure.",
"DirectMouseTooltip": "Direct mouse access (HID) support. Provides games access to your mouse as a pointing device.", "DirectMouseTooltip": "Direct mouse access (HID) support. Provides games access to your mouse as a pointing device.\n\nOnly works with games that natively support mouse controls on Switch hardware, which are few and far between.\n\nWhen enabled, touch screen functionality may not work.\n\nLeave OFF if unsure.",
"RegionTooltip": "Change System Region", "RegionTooltip": "Change System Region",
"LanguageTooltip": "Change System Language", "LanguageTooltip": "Change System Language",
"TimezoneTooltip": "Change System TimeZone", "TimezoneTooltip": "Change System TimeZone",
"TimeTooltip": "Change System Time", "TimeTooltip": "Change System Time",
"VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference. We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.", "VSyncToggleTooltip": "Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck.\n\nCan be toggled in-game with a hotkey of your preference (F1 by default). We recommend doing this if you plan on disabling it.\n\nLeave ON if unsure.",
"PptcToggleTooltip": "Saves translated JIT functions so that they do not need to be translated every time the game loads.\n\nReduces stuttering and significantly speeds up boot times after the first boot of a game.\n\nLeave ON if unsure.", "PptcToggleTooltip": "Saves translated JIT functions so that they do not need to be translated every time the game loads.\n\nReduces stuttering and significantly speeds up boot times after the first boot of a game.\n\nLeave ON if unsure.",
"FsIntegrityToggleTooltip": "Checks for corrupt files when booting a game, and if corrupt files are detected, displays a hash error in the log.\n\nHas no impact on performance and is meant to help troubleshooting.\n\nLeave ON if unsure.", "FsIntegrityToggleTooltip": "Checks for corrupt files when booting a game, and if corrupt files are detected, displays a hash error in the log.\n\nHas no impact on performance and is meant to help troubleshooting.\n\nLeave ON if unsure.",
"AudioBackendTooltip": "Changes the backend used to render audio.\n\nSDL2 is the preferred one, while OpenAL and SoundIO are used as fallbacks. Dummy will have no sound.\n\nSet to SDL2 if unsure.", "AudioBackendTooltip": "Changes the backend used to render audio.\n\nSDL2 is the preferred one, while OpenAL and SoundIO are used as fallbacks. Dummy will have no sound.\n\nSet to SDL2 if unsure.",
@@ -470,10 +472,10 @@
"GraphicsBackendThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.", "GraphicsBackendThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.",
"GalThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.", "GalThreadingTooltip": "Executes graphics backend commands on a second thread.\n\nSpeeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading.\n\nSet to AUTO if unsure.",
"ShaderCacheToggleTooltip": "Saves a disk shader cache which reduces stuttering in subsequent runs.\n\nLeave ON if unsure.", "ShaderCacheToggleTooltip": "Saves a disk shader cache which reduces stuttering in subsequent runs.\n\nLeave ON if unsure.",
"ResolutionScaleTooltip": "Resolution Scale applied to applicable render targets", "ResolutionScaleTooltip": "Multiplies the game's rendering resolution.\n\nA few games may not work with this and look pixelated even when the resolution is increased; for those games, you may need to find mods that remove anti-aliasing or that increase their internal rendering resolution. For using the latter, you'll likely want to select Native.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nKeep in mind 4x is overkill for virtually any setup.",
"ResolutionScaleEntryTooltip": "Floating point resolution scale, such as 1.5. Non-integral scales are more likely to cause issues or crash.", "ResolutionScaleEntryTooltip": "Floating point resolution scale, such as 1.5. Non-integral scales are more likely to cause issues or crash.",
"AnisotropyTooltip": "Level of Anisotropic Filtering (set to Auto to use the value requested by the game)", "AnisotropyTooltip": "Level of Anisotropic Filtering. Set to Auto to use the value requested by the game.",
"AspectRatioTooltip": "Aspect Ratio applied to the renderer window.", "AspectRatioTooltip": "Aspect Ratio applied to the renderer window.\n\nOnly change this if you're using an aspect ratio mod for your game, otherwise the graphics will be stretched.\n\nLeave on 16:9 if unsure.",
"ShaderDumpPathTooltip": "Graphics Shaders Dump Path", "ShaderDumpPathTooltip": "Graphics Shaders Dump Path",
"FileLogTooltip": "Saves console logging to a log file on disk. Does not affect performance.", "FileLogTooltip": "Saves console logging to a log file on disk. Does not affect performance.",
"StubLogTooltip": "Prints stub log messages in the console. Does not affect performance.", "StubLogTooltip": "Prints stub log messages in the console. Does not affect performance.",
@@ -614,9 +616,9 @@
"UserProfilesName": "Name:", "UserProfilesName": "Name:",
"UserProfilesUserId": "User ID:", "UserProfilesUserId": "User ID:",
"SettingsTabGraphicsBackend": "Graphics Backend", "SettingsTabGraphicsBackend": "Graphics Backend",
"SettingsTabGraphicsBackendTooltip": "Graphics Backend to use", "SettingsTabGraphicsBackendTooltip": "Select the graphics backend that will be used in the emulator.\n\nVulkan is overall better for all modern graphics cards, as long as their drivers are up to date. Vulkan also features faster shader compilation (less stuttering) on all GPU vendors.\n\nOpenGL may achieve better results on old Nvidia GPUs, on old AMD GPUs on Linux, or on GPUs with lower VRAM, though shader compilation stutters will be greater.\n\nSet to Vulkan if unsure. Set to OpenGL if your GPU does not support Vulkan even with the latest graphics drivers.",
"SettingsEnableTextureRecompression": "Enable Texture Recompression", "SettingsEnableTextureRecompression": "Enable Texture Recompression",
"SettingsEnableTextureRecompressionTooltip": "Compresses certain textures in order to reduce VRAM usage.\n\nRecommended for use with GPUs that have less than 4GiB VRAM.\n\nLeave OFF if unsure.", "SettingsEnableTextureRecompressionTooltip": "Compresses ASTC textures in order to reduce VRAM usage.\n\nGames using this texture format include Astral Chain, Bayonetta 3, Fire Emblem Engage, Metroid Prime Remastered, Super Mario Bros. Wonder and The Legend of Zelda: Tears of the Kingdom.\n\nGraphics cards with 4GiB VRAM or less will likely crash at some point while running these games.\n\nEnable only if you're running out of VRAM on the aforementioned games. Leave OFF if unsure.",
"SettingsTabGraphicsPreferredGpu": "Preferred GPU", "SettingsTabGraphicsPreferredGpu": "Preferred GPU",
"SettingsTabGraphicsPreferredGpuTooltip": "Select the graphics card that will be used with the Vulkan graphics backend.\n\nDoes not affect the GPU that OpenGL will use.\n\nSet to the GPU flagged as \"dGPU\" if unsure. If there isn't one, leave untouched.", "SettingsTabGraphicsPreferredGpuTooltip": "Select the graphics card that will be used with the Vulkan graphics backend.\n\nDoes not affect the GPU that OpenGL will use.\n\nSet to the GPU flagged as \"dGPU\" if unsure. If there isn't one, leave untouched.",
"SettingsAppRequiredRestartMessage": "Ryujinx Restart Required", "SettingsAppRequiredRestartMessage": "Ryujinx Restart Required",
@@ -642,12 +644,12 @@
"Recover": "Recover", "Recover": "Recover",
"UserProfilesRecoverHeading": "Saves were found for the following accounts", "UserProfilesRecoverHeading": "Saves were found for the following accounts",
"UserProfilesRecoverEmptyList": "No profiles to recover", "UserProfilesRecoverEmptyList": "No profiles to recover",
"GraphicsAATooltip": "Applies anti-aliasing to the game render", "GraphicsAATooltip": "Applies anti-aliasing to the game render.\n\nFXAA will blur most of the image, while SMAA will attempt to find jagged edges and smooth them out.\n\nNot recommended to use in conjunction with the FSR scaling filter.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on NONE if unsure.",
"GraphicsAALabel": "Anti-Aliasing:", "GraphicsAALabel": "Anti-Aliasing:",
"GraphicsScalingFilterLabel": "Scaling Filter:", "GraphicsScalingFilterLabel": "Scaling Filter:",
"GraphicsScalingFilterTooltip": "Enables Framebuffer Scaling", "GraphicsScalingFilterTooltip": "Choose the scaling filter that will be applied when using resolution scale.\n\nBilinear works well for 3D games and is a safe default option.\n\nNearest is recommended for pixel art games.\n\nFSR 1.0 is merely a sharpening filter, not recommended for use with FXAA or SMAA.\n\nThis option can be changed while a game is running by clicking \"Apply\" below; you can simply move the settings window aside and experiment until you find your preferred look for a game.\n\nLeave on BILINEAR if unsure.",
"GraphicsScalingFilterLevelLabel": "Level", "GraphicsScalingFilterLevelLabel": "Level",
"GraphicsScalingFilterLevelTooltip": "Set Scaling Filter Level", "GraphicsScalingFilterLevelTooltip": "Set FSR 1.0 sharpening level. Higher is sharper.",
"SmaaLow": "SMAA Low", "SmaaLow": "SMAA Low",
"SmaaMedium": "SMAA Medium", "SmaaMedium": "SMAA Medium",
"SmaaHigh": "SMAA High", "SmaaHigh": "SMAA High",
@@ -655,12 +657,12 @@
"UserEditorTitle": "Edit User", "UserEditorTitle": "Edit User",
"UserEditorTitleCreate": "Create User", "UserEditorTitleCreate": "Create User",
"SettingsTabNetworkInterface": "Network Interface:", "SettingsTabNetworkInterface": "Network Interface:",
"NetworkInterfaceTooltip": "The network interface used for LAN/LDN features", "NetworkInterfaceTooltip": "The network interface used for LAN/LDN features.\n\nIn conjunction with a VPN or XLink Kai and a game with LAN support, can be used to spoof a same-network connection over the Internet.\n\nLeave on DEFAULT if unsure.",
"NetworkInterfaceDefault": "Default", "NetworkInterfaceDefault": "Default",
"PackagingShaders": "Packaging Shaders", "PackagingShaders": "Packaging Shaders",
"AboutChangelogButton": "View Changelog on GitHub", "AboutChangelogButton": "View Changelog on GitHub",
"AboutChangelogButtonTooltipMessage": "Click to open the changelog for this version in your default browser.", "AboutChangelogButtonTooltipMessage": "Click to open the changelog for this version in your default browser.",
"SettingsTabNetworkMultiplayer": "Multiplayer", "SettingsTabNetworkMultiplayer": "Multiplayer",
"MultiplayerMode": "Mode:", "MultiplayerMode": "Mode:",
"MultiplayerModeTooltip": "Change multiplayer mode" "MultiplayerModeTooltip": "Change LDN multiplayer mode.\n\nLdnMitm will modify local wireless/local play functionality in games to function as if it were LAN, allowing for local, same-network connections with other Ryujinx instances and hacked Nintendo Switch consoles that have the ldn_mitm module installed.\n\nMultiplayer requires all players to be on the same game version (i.e. Super Smash Bros. Ultimate v13.0.1 can't connect to v13.0.0).\n\nLeave DISABLED if unsure."
} }

View File

@@ -18,8 +18,8 @@ using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.Ui.App.Common; using Ryujinx.UI.App.Common;
using Ryujinx.Ui.Common.Helper; using Ryujinx.UI.Common.Helper;
using System; using System;
using System.Buffers; using System.Buffers;
using System.IO; using System.IO;

View File

@@ -5,7 +5,7 @@ namespace Ryujinx.Ava.Common
None, None,
ToggleVSync, ToggleVSync,
Screenshot, Screenshot,
ShowUi, ShowUI,
Pause, Pause,
ToggleMute, ToggleMute,
ResScaleUp, ResScaleUp,

View File

@@ -1,7 +1,7 @@
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.UI.Common.Configuration;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; 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 the view is loaded with the UI Previewer detached, then override it with the saved one or default.
if (Program.PreviewerDetached) 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 else
{ {

View File

@@ -10,8 +10,8 @@ using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.Ui.Common.Helper; using Ryujinx.UI.Common.Helper;
using Ryujinx.Ui.Common.Models.Github; using Ryujinx.UI.Common.Models.Github;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@@ -68,7 +68,8 @@ namespace Ryujinx.Modules
} }
else if (OperatingSystem.IsLinux()) else if (OperatingSystem.IsLinux())
{ {
_platformExt = "linux_x64.tar.gz"; var arch = RuntimeInformation.OSArchitecture == Architecture.Arm64 ? "arm64" : "x64";
_platformExt = $"linux_{arch}.tar.gz";
} }
Version newVersion; Version newVersion;
@@ -637,20 +638,6 @@ namespace Ryujinx.Modules
public static bool CanUpdate(bool showWarnings) public static bool CanUpdate(bool showWarnings)
{ {
#if !DISABLE_UPDATER #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 (!NetworkInterface.GetIsNetworkAvailable())
{ {
if (showWarnings) if (showWarnings)

View File

@@ -9,10 +9,10 @@ using Ryujinx.Common.Logging;
using Ryujinx.Common.SystemInterop; using Ryujinx.Common.SystemInterop;
using Ryujinx.Modules; using Ryujinx.Modules;
using Ryujinx.SDL2.Common; using Ryujinx.SDL2.Common;
using Ryujinx.Ui.Common; using Ryujinx.UI.Common;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.UI.Common.Configuration;
using Ryujinx.Ui.Common.Helper; using Ryujinx.UI.Common.Helper;
using Ryujinx.Ui.Common.SystemInfo; using Ryujinx.UI.Common.SystemInfo;
using System; using System;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -59,6 +59,7 @@ namespace Ryujinx.Ava
{ {
EnableMultiTouch = true, EnableMultiTouch = true,
EnableIme = true, EnableIme = true,
EnableInputFocusProxy = true,
RenderingMode = new[] { X11RenderingMode.Glx, X11RenderingMode.Software }, RenderingMode = new[] { X11RenderingMode.Glx, X11RenderingMode.Software },
}) })
.With(new Win32PlatformOptions .With(new Win32PlatformOptions

View File

@@ -73,8 +73,8 @@
<ProjectReference Include="..\ARMeilleure\ARMeilleure.csproj" /> <ProjectReference Include="..\ARMeilleure\ARMeilleure.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj" /> <ProjectReference Include="..\Ryujinx.Graphics.OpenGL\Ryujinx.Graphics.OpenGL.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" /> <ProjectReference Include="..\Ryujinx.Graphics.Gpu\Ryujinx.Graphics.Gpu.csproj" />
<ProjectReference Include="..\Ryujinx.Ui.Common\Ryujinx.Ui.Common.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.LocaleGenerator\Ryujinx.UI.LocaleGenerator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -103,7 +103,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<AvaloniaResource Include="Ui\**\*.xaml"> <AvaloniaResource Include="UI\**\*.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
</AvaloniaResource> </AvaloniaResource>
<AvaloniaResource Include="Assets\Fonts\SegoeFluentIcons.ttf" /> <AvaloniaResource Include="Assets\Fonts\SegoeFluentIcons.ttf" />

View File

@@ -8,26 +8,26 @@ using Ryujinx.Ava.UI.Windows;
using Ryujinx.HLE; using Ryujinx.HLE;
using Ryujinx.HLE.HOS.Applets; using Ryujinx.HLE.HOS.Applets;
using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types; using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types;
using Ryujinx.HLE.Ui; using Ryujinx.HLE.UI;
using System; using System;
using System.Threading; using System.Threading;
namespace Ryujinx.Ava.UI.Applet namespace Ryujinx.Ava.UI.Applet
{ {
internal class AvaHostUiHandler : IHostUiHandler internal class AvaHostUIHandler : IHostUIHandler
{ {
private readonly MainWindow _parent; private readonly MainWindow _parent;
public IHostUiTheme HostUiTheme { get; } public IHostUITheme HostUITheme { get; }
public AvaHostUiHandler(MainWindow parent) public AvaHostUIHandler(MainWindow parent)
{ {
_parent = 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); ManualResetEvent dialogCloseEvent = new(false);
@@ -110,7 +110,7 @@ namespace Ryujinx.Ava.UI.Applet
return okPressed; return okPressed;
} }
public bool DisplayInputDialog(SoftwareKeyboardUiArgs args, out string userText) public bool DisplayInputDialog(SoftwareKeyboardUIArgs args, out string userText)
{ {
ManualResetEvent dialogCloseEvent = new(false); ManualResetEvent dialogCloseEvent = new(false);

View File

@@ -5,7 +5,7 @@ using Avalonia.Threading;
using Ryujinx.Ava.Input; using Ryujinx.Ava.Input;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.HLE.Ui; using Ryujinx.HLE.UI;
using System; using System;
using System.Threading; using System.Threading;
using HidKey = Ryujinx.Common.Configuration.Hid.Key; using HidKey = Ryujinx.Common.Configuration.Hid.Key;

View File

@@ -1,13 +1,13 @@
using Avalonia.Media; using Avalonia.Media;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.HLE.Ui; using Ryujinx.HLE.UI;
using System; using System;
namespace Ryujinx.Ava.UI.Applet 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; FontFamily = OperatingSystem.IsWindows() && OperatingSystem.IsWindowsVersionAtLeast(10, 0, 22000) ? "Segoe UI Variable" : parent.FontFamily.Name;
DefaultBackgroundColor = BrushToThemeColor(parent.Background); DefaultBackgroundColor = BrushToThemeColor(parent.Background);

View File

@@ -36,7 +36,7 @@ namespace Ryujinx.Ava.UI.Applet
private readonly MainWindow _mainWindow; private readonly MainWindow _mainWindow;
public ControllerAppletDialog(MainWindow mainWindow, ControllerAppletUiArgs args) public ControllerAppletDialog(MainWindow mainWindow, ControllerAppletUIArgs args)
{ {
if (args.PlayerCountMin == args.PlayerCountMax) if (args.PlayerCountMin == args.PlayerCountMax)
{ {
@@ -69,7 +69,7 @@ namespace Ryujinx.Ava.UI.Applet
InitializeComponent(); InitializeComponent();
} }
public static async Task<UserResult> ShowControllerAppletDialog(MainWindow window, ControllerAppletUiArgs args) public static async Task<UserResult> ShowControllerAppletDialog(MainWindow window, ControllerAppletUIArgs args)
{ {
ContentDialog contentDialog = new(); ContentDialog contentDialog = new();
UserResult result = UserResult.Cancel; UserResult result = UserResult.Cancel;

View File

@@ -34,7 +34,7 @@
Height="80" Height="80"
MinWidth="50" MinWidth="50"
Margin="5,10,20,10" 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 <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"

View File

@@ -31,7 +31,7 @@
MinWidth="50" MinWidth="50"
Margin="5,10,20,10" Margin="5,10,20,10"
VerticalAlignment="Center" 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 <TextBlock
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"

View File

@@ -54,7 +54,7 @@ namespace Ryujinx.Ava.UI.Controls
public string MainText { get; set; } = ""; public string MainText { get; set; } = "";
public string SecondaryText { 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(); ContentDialog contentDialog = new();

View File

@@ -51,6 +51,15 @@
Header="{locale:Locale GameListContextMenuManageMod}" Header="{locale:Locale GameListContextMenuManageMod}"
ToolTip.Tip="{locale:Locale GameListContextMenuManageModToolTip}" /> ToolTip.Tip="{locale:Locale GameListContextMenuManageModToolTip}" />
<Separator /> <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 Header="{locale:Locale GameListContextMenuCacheManagement}">
<MenuItem <MenuItem
Click="PurgePtcCache_Click" Click="PurgePtcCache_Click"

View File

@@ -11,8 +11,8 @@ using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
using Ryujinx.Ui.App.Common; using Ryujinx.UI.App.Common;
using Ryujinx.Ui.Common.Helper; using Ryujinx.UI.Common.Helper;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; 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) public async void OpenModManager_Click(object sender, RoutedEventArgs args)
{ {
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel; var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;

View File

@@ -3,7 +3,7 @@ using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ui.App.Common; using Ryujinx.UI.App.Common;
using System; using System;
namespace Ryujinx.Ava.UI.Controls namespace Ryujinx.Ava.UI.Controls

View File

@@ -3,7 +3,7 @@ using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ui.App.Common; using Ryujinx.UI.App.Common;
using System; using System;
namespace Ryujinx.Ava.UI.Controls namespace Ryujinx.Ava.UI.Controls

View File

@@ -26,7 +26,7 @@
Height="70" Height="70"
MinWidth="50" MinWidth="50"
Margin="5,10,20,10" 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 <StackPanel
Grid.Row="1" Grid.Row="1"
Grid.Column="1" Grid.Column="1"
@@ -39,4 +39,4 @@
VerticalAlignment="Center" /> VerticalAlignment="Center" />
</StackPanel> </StackPanel>
</Grid> </Grid>
</Window> </Window>

View File

@@ -1,5 +1,5 @@
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Ryujinx.Ui.App.Common; using Ryujinx.UI.App.Common;
namespace Ryujinx.Ava.UI.Helpers namespace Ryujinx.Ava.UI.Helpers
{ {

View File

@@ -383,7 +383,7 @@ namespace Ryujinx.Ava.UI.Helpers
{ {
result = ContentDialogResult.None; 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; return result;

View File

@@ -1,7 +1,7 @@
using Avalonia.Data.Converters; using Avalonia.Data.Converters;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ui.Common.Helper; using Ryujinx.UI.Common.Helper;
using System; using System;
using System.Globalization; using System.Globalization;

View File

@@ -39,12 +39,12 @@ namespace Ryujinx.Ava.UI.Helpers
public void Log(AvaLogLevel level, string area, object source, string messageTemplate) 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) 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) private static string Format(AvaLogLevel level, string area, string template, object source, object[] v)

View File

@@ -1,6 +1,6 @@
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ui.Common; using Ryujinx.UI.Common;
using Ryujinx.Ui.Common.Helper; using Ryujinx.UI.Common.Helper;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.Helpers namespace Ryujinx.Ava.UI.Helpers

View File

@@ -1,4 +1,4 @@
using Ryujinx.Ui.App.Common; using Ryujinx.UI.App.Common;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;

View File

@@ -1,4 +1,4 @@
using Ryujinx.Ui.App.Common; using Ryujinx.UI.App.Common;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;

View File

@@ -3,8 +3,8 @@ using LibHac.Ncm;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.Ui.App.Common; using Ryujinx.UI.App.Common;
using Ryujinx.Ui.Common.Helper; using Ryujinx.UI.Common.Helper;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;

View File

@@ -3,8 +3,8 @@ using Avalonia.Controls;
using Avalonia.Input; using Avalonia.Input;
using Avalonia.Platform; using Avalonia.Platform;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.UI.Common.Configuration;
using Ryujinx.Ui.Common.Helper; using Ryujinx.UI.Common.Helper;
using SPB.Graphics; using SPB.Graphics;
using SPB.Platform; using SPB.Platform;
using SPB.Platform.GLX; using SPB.Platform.GLX;

View File

@@ -3,7 +3,7 @@ using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.OpenGL; using Ryujinx.Graphics.OpenGL;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.UI.Common.Configuration;
using SPB.Graphics; using SPB.Graphics;
using SPB.Graphics.Exceptions; using SPB.Graphics.Exceptions;
using SPB.Graphics.OpenGL; using SPB.Graphics.OpenGL;
@@ -75,7 +75,7 @@ namespace Ryujinx.Ava.UI.Renderer
throw; 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}");
} }
} }

View File

@@ -1,7 +1,7 @@
using Avalonia; using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.UI.Common.Configuration;
using System; using System;
namespace Ryujinx.Ava.UI.Renderer namespace Ryujinx.Ava.UI.Renderer

View File

@@ -3,7 +3,7 @@ using Avalonia.Platform;
using Avalonia.Threading; using Avalonia.Threading;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.UI.Common.Configuration;
using System; using System;
using System.Net.Http; using System.Net.Http;
using System.Net.NetworkInformation; using System.Net.NetworkInformation;
@@ -87,19 +87,19 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
Version = Program.Version; 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"))); 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"))); 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"))); 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"))); TwitterLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Twitter_Light.png?assembly=Ryujinx.UI.Common")));
} }
else else
{ {
GithubLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.Ui.Common.Resources.Logo_GitHub_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"))); 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"))); 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"))); TwitterLogo = new Bitmap(AssetLoader.Open(new Uri("resm:Ryujinx.UI.Common.Resources.Logo_Twitter_Dark.png?assembly=Ryujinx.UI.Common")));
} }
Dispatcher.UIThread.InvokeAsync(DownloadPatronsJson); Dispatcher.UIThread.InvokeAsync(DownloadPatronsJson);

View File

@@ -9,7 +9,7 @@ using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.Ui.Common.Models.Amiibo; using Ryujinx.UI.Common.Models.Amiibo;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
@@ -65,7 +65,7 @@ namespace Ryujinx.Ava.UI.ViewModels
_amiiboSeries = new ObservableCollection<string>(); _amiiboSeries = new ObservableCollection<string>();
_amiibos = new AvaloniaList<AmiiboApi>(); _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(); _ = LoadContentAsync();
} }

View File

@@ -19,7 +19,7 @@ using Ryujinx.Common.Configuration.Hid.Keyboard;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.Input; using Ryujinx.Input;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.UI.Common.Configuration;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
@@ -35,10 +35,10 @@ namespace Ryujinx.Ava.UI.ViewModels
public class ControllerInputViewModel : BaseModel, IDisposable public class ControllerInputViewModel : BaseModel, IDisposable
{ {
private const string Disabled = "disabled"; private const string Disabled = "disabled";
private const string ProControllerResource = "Ryujinx.Ui.Common/Resources/Controller_ProCon.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 JoyConPairResource = "Ryujinx.UI.Common/Resources/Controller_JoyConPair.svg";
private const string JoyConLeftResource = "Ryujinx.Ui.Common/Resources/Controller_JoyConLeft.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 JoyConRightResource = "Ryujinx.UI.Common/Resources/Controller_JoyConRight.svg";
private const string KeyboardString = "keyboard"; private const string KeyboardString = "keyboard";
private const string ControllerString = "controller"; private const string ControllerString = "controller";
private readonly MainWindow _mainWindow; private readonly MainWindow _mainWindow;

View File

@@ -25,13 +25,13 @@ using Ryujinx.HLE;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.Ui; using Ryujinx.HLE.UI;
using Ryujinx.Input.HLE; using Ryujinx.Input.HLE;
using Ryujinx.Modules; using Ryujinx.Modules;
using Ryujinx.Ui.App.Common; using Ryujinx.UI.App.Common;
using Ryujinx.Ui.Common; using Ryujinx.UI.Common;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.UI.Common.Configuration;
using Ryujinx.Ui.Common.Helper; using Ryujinx.UI.Common.Helper;
using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.PixelFormats;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -138,7 +138,7 @@ namespace Ryujinx.Ava.UI.ViewModels
InputManager inputManager, InputManager inputManager,
UserChannelPersistence userChannelPersistence, UserChannelPersistence userChannelPersistence,
LibHacHorizonManager libHacHorizonManager, LibHacHorizonManager libHacHorizonManager,
IHostUiHandler uiHandler, IHostUIHandler uiHandler,
Action<bool> showLoading, Action<bool> showLoading,
Action<bool> switchToGameControl, Action<bool> switchToGameControl,
Action<Control> setMainContent, Action<Control> setMainContent,
@@ -685,10 +685,10 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool StartGamesInFullscreen public bool StartGamesInFullscreen
{ {
get => ConfigurationState.Instance.Ui.StartFullscreen; get => ConfigurationState.Instance.UI.StartFullscreen;
set set
{ {
ConfigurationState.Instance.Ui.StartFullscreen.Value = value; ConfigurationState.Instance.UI.StartFullscreen.Value = value;
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
@@ -698,10 +698,10 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool ShowConsole public bool ShowConsole
{ {
get => ConfigurationState.Instance.Ui.ShowConsole; get => ConfigurationState.Instance.UI.ShowConsole;
set set
{ {
ConfigurationState.Instance.Ui.ShowConsole.Value = value; ConfigurationState.Instance.UI.ShowConsole.Value = value;
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
@@ -743,10 +743,10 @@ namespace Ryujinx.Ava.UI.ViewModels
public Glyph Glyph public Glyph Glyph
{ {
get => (Glyph)ConfigurationState.Instance.Ui.GameListViewMode.Value; get => (Glyph)ConfigurationState.Instance.UI.GameListViewMode.Value;
set set
{ {
ConfigurationState.Instance.Ui.GameListViewMode.Value = (int)value; ConfigurationState.Instance.UI.GameListViewMode.Value = (int)value;
OnPropertyChanged(); OnPropertyChanged();
OnPropertyChanged(nameof(IsGrid)); OnPropertyChanged(nameof(IsGrid));
@@ -758,9 +758,9 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool ShowNames 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();
OnPropertyChanged(nameof(GridSizeScale)); OnPropertyChanged(nameof(GridSizeScale));
@@ -772,10 +772,10 @@ namespace Ryujinx.Ava.UI.ViewModels
internal ApplicationSort SortMode internal ApplicationSort SortMode
{ {
get => (ApplicationSort)ConfigurationState.Instance.Ui.ApplicationSort.Value; get => (ApplicationSort)ConfigurationState.Instance.UI.ApplicationSort.Value;
private set private set
{ {
ConfigurationState.Instance.Ui.ApplicationSort.Value = (int)value; ConfigurationState.Instance.UI.ApplicationSort.Value = (int)value;
OnPropertyChanged(); OnPropertyChanged();
OnPropertyChanged(nameof(SortName)); OnPropertyChanged(nameof(SortName));
@@ -788,7 +788,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
get get
{ {
return ConfigurationState.Instance.Ui.GridSize.Value switch return ConfigurationState.Instance.UI.GridSize.Value switch
{ {
1 => 78, 1 => 78,
2 => 100, 2 => 100,
@@ -803,7 +803,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{ {
get get
{ {
return ConfigurationState.Instance.Ui.GridSize.Value switch return ConfigurationState.Instance.UI.GridSize.Value switch
{ {
1 => 120, 1 => 120,
2 => ShowNames ? 210 : 150, 2 => ShowNames ? 210 : 150,
@@ -816,10 +816,10 @@ namespace Ryujinx.Ava.UI.ViewModels
public int GridSizeScale public int GridSizeScale
{ {
get => ConfigurationState.Instance.Ui.GridSize; get => ConfigurationState.Instance.UI.GridSize;
set set
{ {
ConfigurationState.Instance.Ui.GridSize.Value = value; ConfigurationState.Instance.UI.GridSize.Value = value;
if (value < 2) if (value < 2)
{ {
@@ -860,10 +860,10 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool IsAscending public bool IsAscending
{ {
get => ConfigurationState.Instance.Ui.IsAscendingOrder; get => ConfigurationState.Instance.UI.IsAscendingOrder;
private set private set
{ {
ConfigurationState.Instance.Ui.IsAscendingOrder.Value = value; ConfigurationState.Instance.UI.IsAscendingOrder.Value = value;
OnPropertyChanged(); OnPropertyChanged();
OnPropertyChanged(nameof(SortMode)); OnPropertyChanged(nameof(SortMode));
@@ -919,7 +919,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public RendererHost RendererHostControl { get; private set; } public RendererHost RendererHostControl { get; private set; }
public bool IsClosing { get; set; } public bool IsClosing { get; set; }
public LibHacHorizonManager LibHacHorizonManager { get; internal 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 IsSortedByFavorite => SortMode == ApplicationSort.Favorite;
public bool IsSortedByTitle => SortMode == ApplicationSort.Title; public bool IsSortedByTitle => SortMode == ApplicationSort.Title;
public bool IsSortedByDeveloper => SortMode == ApplicationSort.Developer; public bool IsSortedByDeveloper => SortMode == ApplicationSort.Developer;
@@ -928,10 +928,10 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool IsSortedByType => SortMode == ApplicationSort.FileType; public bool IsSortedByType => SortMode == ApplicationSort.FileType;
public bool IsSortedBySize => SortMode == ApplicationSort.FileSize; public bool IsSortedBySize => SortMode == ApplicationSort.FileSize;
public bool IsSortedByPath => SortMode == ApplicationSort.Path; public bool IsSortedByPath => SortMode == ApplicationSort.Path;
public bool IsGridSmall => ConfigurationState.Instance.Ui.GridSize == 1; public bool IsGridSmall => ConfigurationState.Instance.UI.GridSize == 1;
public bool IsGridMedium => ConfigurationState.Instance.Ui.GridSize == 2; public bool IsGridMedium => ConfigurationState.Instance.UI.GridSize == 2;
public bool IsGridLarge => ConfigurationState.Instance.Ui.GridSize == 3; public bool IsGridLarge => ConfigurationState.Instance.UI.GridSize == 3;
public bool IsGridHuge => ConfigurationState.Instance.Ui.GridSize == 4; public bool IsGridHuge => ConfigurationState.Instance.UI.GridSize == 4;
#endregion #endregion
@@ -1245,7 +1245,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public void LoadConfigurableHotKeys() 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); ShowUiKey = new KeyGesture(showUiKey);
} }
@@ -1350,16 +1350,11 @@ namespace Ryujinx.Ava.UI.ViewModels
public void OpenLogsFolder() public void OpenLogsFolder()
{ {
string logPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs"); string logPath = AppDataManager.GetOrCreateLogsDir();
if (!string.IsNullOrEmpty(logPath))
if (LoggerModule.LogDirectoryPath != null)
{ {
logPath = LoggerModule.LogDirectoryPath; OpenHelper.OpenFolder(logPath);
} }
new DirectoryInfo(logPath).Create();
OpenHelper.OpenFolder(logPath);
} }
public void ToggleDockMode() public void ToggleDockMode()
@@ -1390,7 +1385,7 @@ namespace Ryujinx.Ava.UI.ViewModels
if (Program.PreviewerDetached) if (Program.PreviewerDetached)
{ {
ConfigurationState.Instance.Ui.LanguageCode.Value = (string)languageCode; ConfigurationState.Instance.UI.LanguageCode.Value = (string)languageCode;
ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
} }
} }

View File

@@ -16,8 +16,8 @@ using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Vulkan; using Ryujinx.Graphics.Vulkan;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Services.Time.TimeZone; using Ryujinx.HLE.HOS.Services.Time.TimeZone;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.UI.Common.Configuration;
using Ryujinx.Ui.Common.Configuration.System; using Ryujinx.UI.Common.Configuration.System;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
@@ -408,9 +408,9 @@ namespace Ryujinx.Ava.UI.ViewModels
HideCursor = (int)config.HideCursor.Value; HideCursor = (int)config.HideCursor.Value;
GameDirectories.Clear(); 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 // Input
EnableDockedMode = config.System.EnableDockedMode; EnableDockedMode = config.System.EnableDockedMode;
@@ -494,10 +494,10 @@ namespace Ryujinx.Ava.UI.ViewModels
if (_directoryChanged) if (_directoryChanged)
{ {
List<string> gameDirs = new(GameDirectories); 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 // Input
config.System.EnableDockedMode.Value = EnableDockedMode; config.System.EnableDockedMode.Value = EnableDockedMode;

View File

@@ -17,7 +17,7 @@ using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.Ui.App.Common; using Ryujinx.UI.App.Common;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;

View File

@@ -10,9 +10,9 @@ using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using Ryujinx.Modules; using Ryujinx.Modules;
using Ryujinx.Ui.Common; using Ryujinx.UI.Common;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.UI.Common.Configuration;
using Ryujinx.Ui.Common.Helper; using Ryujinx.UI.Common.Helper;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@@ -43,7 +43,7 @@ namespace Ryujinx.Ava.UI.Views.Main
checkBoxes.Add(new CheckBox checkBoxes.Add(new CheckBox
{ {
Content = $".{fileName}", Content = $".{fileName}",
IsChecked = ((FileTypes)item).GetConfigValue(ConfigurationState.Instance.Ui.ShownFileTypes), IsChecked = ((FileTypes)item).GetConfigValue(ConfigurationState.Instance.UI.ShownFileTypes),
Command = MiniCommand.Create(() => Window.ToggleFileType(fileName)), Command = MiniCommand.Create(() => Window.ToggleFileType(fileName)),
}); });
} }

View File

@@ -5,7 +5,7 @@ using Avalonia.Interactivity;
using Ryujinx.Ava.UI.Windows; using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.UI.Common.Configuration;
using System; using System;
namespace Ryujinx.Ava.UI.Views.Main namespace Ryujinx.Ava.UI.Views.Main

View File

@@ -45,7 +45,7 @@
<TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysShowUiHotkey}" Width="230" /> <TextBlock VerticalAlignment="Center" Text="{locale:Locale SettingsTabHotkeysShowUiHotkey}" Width="230" />
<ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked"> <ToggleButton Width="90" Height="27" Checked="Button_Checked" Unchecked="Button_Unchecked">
<TextBlock <TextBlock
Text="{Binding KeyboardHotkeys.ShowUi, Mode=TwoWay, Converter={StaticResource Key}}" Text="{Binding KeyboardHotkeys.ShowUI, Mode=TwoWay, Converter={StaticResource Key}}"
TextAlignment="Center" /> TextAlignment="Center" />
</ToggleButton> </ToggleButton>
</StackPanel> </StackPanel>

View File

@@ -51,7 +51,7 @@
Spacing="10"> Spacing="10">
<Image <Image
Height="80" Height="80"
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"
HorizontalAlignment="Center" HorizontalAlignment="Center"
IsHitTestVisible="True" /> IsHitTestVisible="True" />
<WrapPanel <WrapPanel

View File

@@ -7,7 +7,7 @@ using FluentAvalonia.UI.Controls;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ui.Common.Helper; using Ryujinx.UI.Common.Helper;
using System.Threading.Tasks; using System.Threading.Tasks;
using Button = Avalonia.Controls.Button; using Button = Avalonia.Controls.Button;

View File

@@ -1,7 +1,7 @@
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ui.Common.Models.Amiibo; using Ryujinx.UI.Common.Models.Amiibo;
namespace Ryujinx.Ava.UI.Windows namespace Ryujinx.Ava.UI.Windows
{ {

View File

@@ -3,7 +3,7 @@ using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Models;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
using Ryujinx.Ui.App.Common; using Ryujinx.UI.App.Common;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;

View File

@@ -7,7 +7,7 @@ using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.Ui.Common.Helper; using Ryujinx.UI.Common.Helper;
using System.Threading.Tasks; using System.Threading.Tasks;
using Button = Avalonia.Controls.Button; using Button = Avalonia.Controls.Button;

View File

@@ -18,10 +18,10 @@ using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.Input.HLE; using Ryujinx.Input.HLE;
using Ryujinx.Input.SDL2; using Ryujinx.Input.SDL2;
using Ryujinx.Modules; using Ryujinx.Modules;
using Ryujinx.Ui.App.Common; using Ryujinx.UI.App.Common;
using Ryujinx.Ui.Common; using Ryujinx.UI.Common;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.UI.Common.Configuration;
using Ryujinx.Ui.Common.Helper; using Ryujinx.UI.Common.Helper;
using System; using System;
using System.IO; using System.IO;
using System.Runtime.Versioning; using System.Runtime.Versioning;
@@ -40,7 +40,7 @@ namespace Ryujinx.Ava.UI.Windows
private static bool _deferLoad; private static bool _deferLoad;
private static string _launchPath; private static string _launchPath;
private static bool _startFullscreen; private static bool _startFullscreen;
internal readonly AvaHostUiHandler UiHandler; internal readonly AvaHostUIHandler UiHandler;
public VirtualFileSystem VirtualFileSystem { get; private set; } public VirtualFileSystem VirtualFileSystem { get; private set; }
public ContentManager ContentManager { get; private set; } public ContentManager ContentManager { get; private set; }
@@ -69,7 +69,7 @@ namespace Ryujinx.Ava.UI.Windows
InitializeComponent(); InitializeComponent();
Load(); Load();
UiHandler = new AvaHostUiHandler(this); UiHandler = new AvaHostUIHandler(this);
ViewModel.Title = $"Ryujinx {Program.Version}"; ViewModel.Title = $"Ryujinx {Program.Version}";
@@ -319,13 +319,13 @@ namespace Ryujinx.Ava.UI.Windows
private void SetWindowSizePosition() private void SetWindowSizePosition()
{ {
PixelPoint savedPoint = new(ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX, PixelPoint savedPoint = new(ConfigurationState.Instance.UI.WindowStartup.WindowPositionX,
ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY); ConfigurationState.Instance.UI.WindowStartup.WindowPositionY);
ViewModel.WindowHeight = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight * Program.WindowScaleFactor; ViewModel.WindowHeight = ConfigurationState.Instance.UI.WindowStartup.WindowSizeHeight * Program.WindowScaleFactor;
ViewModel.WindowWidth = ConfigurationState.Instance.Ui.WindowStartup.WindowSizeWidth * 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)) if (CheckScreenBounds(savedPoint))
{ {
@@ -353,13 +353,13 @@ namespace Ryujinx.Ava.UI.Windows
private void SaveWindowSizePosition() private void SaveWindowSizePosition()
{ {
ConfigurationState.Instance.Ui.WindowStartup.WindowSizeHeight.Value = (int)Height; ConfigurationState.Instance.UI.WindowStartup.WindowSizeHeight.Value = (int)Height;
ConfigurationState.Instance.Ui.WindowStartup.WindowSizeWidth.Value = (int)Width; ConfigurationState.Instance.UI.WindowStartup.WindowSizeWidth.Value = (int)Width;
ConfigurationState.Instance.Ui.WindowStartup.WindowPositionX.Value = Position.X; ConfigurationState.Instance.UI.WindowStartup.WindowPositionX.Value = Position.X;
ConfigurationState.Instance.Ui.WindowStartup.WindowPositionY.Value = Position.Y; 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(); MainWindowViewModel.SaveConfig();
} }
@@ -512,12 +512,12 @@ namespace Ryujinx.Ava.UI.Windows
_ = fileType switch _ = fileType switch
{ {
#pragma warning disable IDE0055 // Disable formatting #pragma warning disable IDE0055 // Disable formatting
"NSP" => ConfigurationState.Instance.Ui.ShownFileTypes.NSP.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSP, "NSP" => ConfigurationState.Instance.UI.ShownFileTypes.NSP.Value = !ConfigurationState.Instance.UI.ShownFileTypes.NSP,
"PFS0" => ConfigurationState.Instance.Ui.ShownFileTypes.PFS0.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.PFS0, "PFS0" => ConfigurationState.Instance.UI.ShownFileTypes.PFS0.Value = !ConfigurationState.Instance.UI.ShownFileTypes.PFS0,
"XCI" => ConfigurationState.Instance.Ui.ShownFileTypes.XCI.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.XCI, "XCI" => ConfigurationState.Instance.UI.ShownFileTypes.XCI.Value = !ConfigurationState.Instance.UI.ShownFileTypes.XCI,
"NCA" => ConfigurationState.Instance.Ui.ShownFileTypes.NCA.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NCA, "NCA" => ConfigurationState.Instance.UI.ShownFileTypes.NCA.Value = !ConfigurationState.Instance.UI.ShownFileTypes.NCA,
"NRO" => ConfigurationState.Instance.Ui.ShownFileTypes.NRO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NRO, "NRO" => ConfigurationState.Instance.UI.ShownFileTypes.NRO.Value = !ConfigurationState.Instance.UI.ShownFileTypes.NRO,
"NSO" => ConfigurationState.Instance.Ui.ShownFileTypes.NSO.Value = !ConfigurationState.Instance.Ui.ShownFileTypes.NSO, "NSO" => ConfigurationState.Instance.UI.ShownFileTypes.NSO.Value = !ConfigurationState.Instance.UI.ShownFileTypes.NSO,
_ => throw new ArgumentOutOfRangeException(fileType), _ => throw new ArgumentOutOfRangeException(fileType),
#pragma warning restore IDE0055 #pragma warning restore IDE0055
}; };
@@ -537,7 +537,7 @@ namespace Ryujinx.Ava.UI.Windows
Thread applicationLibraryThread = new(() => 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; _isLoading = false;
}) })

View File

@@ -6,7 +6,7 @@ using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers; using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ui.Common.Helper; using Ryujinx.UI.Common.Helper;
using System.Threading.Tasks; using System.Threading.Tasks;
using Button = Avalonia.Controls.Button; using Button = Avalonia.Controls.Button;

View File

@@ -4,7 +4,7 @@ using Avalonia.Media;
using Avalonia.Media.Imaging; using Avalonia.Media.Imaging;
using Avalonia.Platform; using Avalonia.Platform;
using Ryujinx.Ava.Common.Locale; using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ui.Common.Configuration; using Ryujinx.UI.Common.Configuration;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
@@ -19,7 +19,7 @@ namespace Ryujinx.Ava.UI.Windows
WindowStartupLocation = WindowStartupLocation.CenterOwner; WindowStartupLocation = WindowStartupLocation.CenterOwner;
TransparencyLevelHint = new[] { WindowTransparencyLevel.None }; 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); Icon = new WindowIcon(stream);
stream.Position = 0; stream.Position = 0;

View File

@@ -7,7 +7,7 @@ using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models; using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.ViewModels; using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.Ui.Common.Helper; using Ryujinx.UI.Common.Helper;
using System.Threading.Tasks; using System.Threading.Tasks;
using Button = Avalonia.Controls.Button; using Button = Avalonia.Controls.Button;

View File

@@ -2,6 +2,7 @@ using Ryujinx.Common.Logging;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using System; using System;
using System.IO; using System.IO;
using System.Runtime.Versioning;
namespace Ryujinx.Common.Configuration namespace Ryujinx.Common.Configuration
{ {
@@ -30,6 +31,8 @@ namespace Ryujinx.Common.Configuration
public static string KeysDirPath { get; private set; } public static string KeysDirPath { get; private set; }
public static string KeysDirPathUser { get; } public static string KeysDirPathUser { get; }
public static string LogsDirPath { get; private set; }
public const string DefaultNandDir = "bis"; public const string DefaultNandDir = "bis";
public const string DefaultSdcardDir = "sdcard"; public const string DefaultSdcardDir = "sdcard";
private const string DefaultModsDir = "mods"; private const string DefaultModsDir = "mods";
@@ -46,15 +49,7 @@ namespace Ryujinx.Common.Configuration
public static void Initialize(string baseDirPath) public static void Initialize(string baseDirPath)
{ {
string appDataPath; string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
if (OperatingSystem.IsMacOS())
{
appDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support");
}
else
{
appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
}
if (appDataPath.Length == 0) if (appDataPath.Length == 0)
{ {
@@ -101,26 +96,134 @@ namespace Ryujinx.Common.Configuration
BaseDirPath = Path.GetFullPath(BaseDirPath); // convert relative paths BaseDirPath = Path.GetFullPath(BaseDirPath); // convert relative paths
// NOTE: Moves the Ryujinx folder in `~/.config` to `~/Library/Application Support` if one is found if (IsPathSymlink(BaseDirPath))
// 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)
{ {
string oldConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), DefaultBaseDir); Logger.Warning?.Print(LogClass.Application, $"Application data directory is a symlink. This may be unintended.");
if (Path.Exists(oldConfigPath) && !IsPathSymlink(oldConfigPath) && !Path.Exists(BaseDirPath))
{
FileSystemUtils.MoveDirectory(oldConfigPath, BaseDirPath);
Directory.CreateSymbolicLink(oldConfigPath, BaseDirPath);
}
} }
SetupBasePaths(); 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() private static void SetupBasePaths()
{ {
Directory.CreateDirectory(BaseDirPath); Directory.CreateDirectory(BaseDirPath);
LogsDirPath = SetUpLogsDir();
Directory.CreateDirectory(GamesDirPath = Path.Combine(BaseDirPath, GamesDir)); Directory.CreateDirectory(GamesDirPath = Path.Combine(BaseDirPath, GamesDir));
Directory.CreateDirectory(ProfilesDirPath = Path.Combine(BaseDirPath, ProfilesDir)); Directory.CreateDirectory(ProfilesDirPath = Path.Combine(BaseDirPath, ProfilesDir));
Directory.CreateDirectory(KeysDirPath = Path.Combine(BaseDirPath, KeysDir)); 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. // Should be removed, when the existence of the old directory isn't checked anymore.
private static bool IsPathSymlink(string path) private static bool IsPathSymlink(string path)
{ {
FileAttributes attributes = File.GetAttributes(path); try
return (attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint; {
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; public static string GetModsPath() => CustomModsPath ?? Directory.CreateDirectory(Path.Combine(BaseDirPath, DefaultModsDir)).FullName;

View File

@@ -6,7 +6,7 @@ namespace Ryujinx.Common.Configuration.Hid
{ {
public Key ToggleVsync { get; set; } public Key ToggleVsync { get; set; }
public Key Screenshot { get; set; } public Key Screenshot { get; set; }
public Key ShowUi { get; set; } public Key ShowUI { get; set; }
public Key Pause { get; set; } public Key Pause { get; set; }
public Key ToggleMute { get; set; } public Key ToggleMute { get; set; }
public Key ResScaleUp { get; set; } public Key ResScaleUp { get; set; }

View File

@@ -70,7 +70,7 @@ namespace Ryujinx.Common.Logging
ServiceVi, ServiceVi,
SurfaceFlinger, SurfaceFlinger,
TamperMachine, TamperMachine,
Ui, UI,
Vic, Vic,
} }
} }

View File

@@ -23,7 +23,18 @@ namespace Ryujinx.Common.Logging.Targets
public static FileStream PrepareLogFile(string path) public static FileStream PrepareLogFile(string path)
{ {
// Ensure directory is present // 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 try
{ {
logDir.Create(); logDir.Create();

View File

@@ -8,7 +8,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
{ {
static class Decoder static class Decoder
{ {
private const int MaxInstructionsPerBlock = 1000; private const int MaxInstructionsPerFunction = 10000;
private const uint NzcvFlags = 0xfu << 28; private const uint NzcvFlags = 0xfu << 28;
private const uint CFlag = 0x1u << 29; private const uint CFlag = 0x1u << 29;
@@ -22,10 +22,11 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
bool hasHostCall = false; bool hasHostCall = false;
bool hasMemoryInstruction = false; bool hasMemoryInstruction = false;
int totalInsts = 0;
while (true) while (true)
{ {
Block block = Decode(cpuPreset, memoryManager, address, ref useMask, ref hasHostCall, ref hasMemoryInstruction); Block block = Decode(cpuPreset, memoryManager, address, ref totalInsts, ref useMask, ref hasHostCall, ref hasMemoryInstruction);
if (!block.IsTruncated && TryGetBranchTarget(block, out ulong targetAddress)) if (!block.IsTruncated && TryGetBranchTarget(block, out ulong targetAddress))
{ {
@@ -230,6 +231,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
CpuPreset cpuPreset, CpuPreset cpuPreset,
IMemoryManager memoryManager, IMemoryManager memoryManager,
ulong address, ulong address,
ref int totalInsts,
ref RegisterMask useMask, ref RegisterMask useMask,
ref bool hasHostCall, ref bool hasHostCall,
ref bool hasMemoryInstruction) ref bool hasMemoryInstruction)
@@ -272,7 +274,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
uint tempGprUseMask = gprUseMask | instGprReadMask | instGprWriteMask; uint tempGprUseMask = gprUseMask | instGprReadMask | instGprWriteMask;
if (CalculateAvailableTemps(tempGprUseMask) < CalculateRequiredGprTemps(tempGprUseMask) || insts.Count >= MaxInstructionsPerBlock) if (CalculateAvailableTemps(tempGprUseMask) < CalculateRequiredGprTemps(tempGprUseMask) || totalInsts++ >= MaxInstructionsPerFunction)
{ {
isTruncated = true; isTruncated = true;
address -= 4UL; address -= 4UL;

View File

@@ -148,6 +148,7 @@ namespace Ryujinx.Graphics.GAL
B8G8R8A8Unorm, B8G8R8A8Unorm,
B8G8R8A8Srgb, B8G8R8A8Srgb,
B10G10R10A2Unorm, B10G10R10A2Unorm,
X8UintD24Unorm,
} }
public static class FormatExtensions public static class FormatExtensions
@@ -269,6 +270,7 @@ namespace Ryujinx.Graphics.GAL
case Format.D16Unorm: case Format.D16Unorm:
return 2; return 2;
case Format.S8UintD24Unorm: case Format.S8UintD24Unorm:
case Format.X8UintD24Unorm:
case Format.D32Float: case Format.D32Float:
case Format.D24UnormS8Uint: case Format.D24UnormS8Uint:
return 4; return 4;
@@ -349,6 +351,7 @@ namespace Ryujinx.Graphics.GAL
case Format.D16Unorm: case Format.D16Unorm:
case Format.D24UnormS8Uint: case Format.D24UnormS8Uint:
case Format.S8UintD24Unorm: case Format.S8UintD24Unorm:
case Format.X8UintD24Unorm:
case Format.D32Float: case Format.D32Float:
case Format.D32FloatS8Uint: case Format.D32FloatS8Uint:
return true; return true;
@@ -633,6 +636,7 @@ namespace Ryujinx.Graphics.GAL
case Format.D16Unorm: case Format.D16Unorm:
case Format.D24UnormS8Uint: case Format.D24UnormS8Uint:
case Format.S8UintD24Unorm: case Format.S8UintD24Unorm:
case Format.X8UintD24Unorm:
case Format.D32Float: case Format.D32Float:
case Format.D32FloatS8Uint: case Format.D32FloatS8Uint:
case Format.S8Uint: case Format.S8Uint:

View File

@@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.GAL
void SetIndexBuffer(BufferRange buffer, IndexType type); 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); void SetLineParameters(float width, bool smooth);

View File

@@ -1,17 +1,20 @@
using Ryujinx.Graphics.GAL.Multithreading.Model; using Ryujinx.Graphics.GAL.Multithreading.Model;
using Ryujinx.Graphics.GAL.Multithreading.Resources; using Ryujinx.Graphics.GAL.Multithreading.Resources;
using Ryujinx.Graphics.Shader;
namespace Ryujinx.Graphics.GAL.Multithreading.Commands namespace Ryujinx.Graphics.GAL.Multithreading.Commands
{ {
struct SetImageCommand : IGALCommand, IGALCommand<SetImageCommand> struct SetImageCommand : IGALCommand, IGALCommand<SetImageCommand>
{ {
public readonly CommandType CommandType => CommandType.SetImage; public readonly CommandType CommandType => CommandType.SetImage;
private ShaderStage _stage;
private int _binding; private int _binding;
private TableRef<ITexture> _texture; private TableRef<ITexture> _texture;
private Format _imageFormat; 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; _binding = binding;
_texture = texture; _texture = texture;
_imageFormat = imageFormat; _imageFormat = imageFormat;
@@ -19,7 +22,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Commands
public static void Run(ref SetImageCommand command, ThreadedRenderer threaded, IRenderer renderer) 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);
} }
} }
} }

View File

@@ -177,9 +177,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading
_renderer.QueueCommand(); _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(); _renderer.QueueCommand();
} }

View File

@@ -8,13 +8,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types
/// </summary> /// </summary>
enum ZetaFormat enum ZetaFormat
{ {
D32Float = 0xa, Zf32 = 0xa,
D16Unorm = 0x13, Z16 = 0x13,
D24UnormS8Uint = 0x14, Z24S8 = 0x14,
D24Unorm = 0x15, X8Z24 = 0x15,
S8UintD24Unorm = 0x16, S8Z24 = 0x16,
S8Uint = 0x17, S8Uint = 0x17,
D32FloatS8Uint = 0x19, Zf32X24S8 = 0x19,
} }
static class ZetaFormatConverter static class ZetaFormatConverter
@@ -29,14 +29,14 @@ namespace Ryujinx.Graphics.Gpu.Engine.Types
return format switch return format switch
{ {
#pragma warning disable IDE0055 // Disable formatting #pragma warning disable IDE0055 // Disable formatting
ZetaFormat.D32Float => new FormatInfo(Format.D32Float, 1, 1, 4, 1), ZetaFormat.Zf32 => new FormatInfo(Format.D32Float, 1, 1, 4, 1),
ZetaFormat.D16Unorm => new FormatInfo(Format.D16Unorm, 1, 1, 2, 1), ZetaFormat.Z16 => new FormatInfo(Format.D16Unorm, 1, 1, 2, 1),
ZetaFormat.D24UnormS8Uint => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2), ZetaFormat.Z24S8 => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2),
ZetaFormat.D24Unorm => new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 1), ZetaFormat.X8Z24 => new FormatInfo(Format.X8UintD24Unorm, 1, 1, 4, 1),
ZetaFormat.S8UintD24Unorm => new FormatInfo(Format.S8UintD24Unorm, 1, 1, 4, 2), ZetaFormat.S8Z24 => new FormatInfo(Format.S8UintD24Unorm, 1, 1, 4, 2),
ZetaFormat.S8Uint => new FormatInfo(Format.S8Uint, 1, 1, 1, 1), ZetaFormat.S8Uint => new FormatInfo(Format.S8Uint, 1, 1, 1, 1),
ZetaFormat.D32FloatS8Uint => new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2), ZetaFormat.Zf32X24S8 => new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2),
_ => FormatInfo.Default, _ => FormatInfo.Default,
#pragma warning restore IDE0055 #pragma warning restore IDE0055
}; };
} }

View File

@@ -185,6 +185,7 @@ namespace Ryujinx.Graphics.Gpu.Image
G24R8RUintGUnormBUnormAUnorm = G24R8 | RUint | GUnorm | BUnorm | AUnorm, // 0x24a0e G24R8RUintGUnormBUnormAUnorm = G24R8 | RUint | GUnorm | BUnorm | AUnorm, // 0x24a0e
Z24S8RUintGUnormBUnormAUnorm = Z24S8 | RUint | GUnorm | BUnorm | AUnorm, // 0x24a29 Z24S8RUintGUnormBUnormAUnorm = Z24S8 | RUint | GUnorm | BUnorm | AUnorm, // 0x24a29
Z24S8RUintGUnormBUintAUint = Z24S8 | RUint | GUnorm | BUint | AUint, // 0x48a29 Z24S8RUintGUnormBUintAUint = Z24S8 | RUint | GUnorm | BUint | AUint, // 0x48a29
X8Z24RUnormGUintBUintAUint = X8Z24 | RUnorm | GUint | BUint | AUint, // 0x4912a
S8Z24RUnormGUintBUintAUint = S8Z24 | RUnorm | GUint | BUint | AUint, // 0x4912b S8Z24RUnormGUintBUintAUint = S8Z24 | RUnorm | GUint | BUint | AUint, // 0x4912b
R32B24G8RFloatGUintBUnormAUnorm = R32B24G8 | RFloat | GUint | BUnorm | AUnorm, // 0x25385 R32B24G8RFloatGUintBUnormAUnorm = R32B24G8 | RFloat | GUint | BUnorm | AUnorm, // 0x25385
Zf32X24S8RFloatGUintBUnormAUnorm = Zf32X24S8 | RFloat | GUint | BUnorm | AUnorm, // 0x253b0 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.G24R8RUintGUnormBUnormAUnorm, new FormatInfo(Format.D24UnormS8Uint, 1, 1, 4, 2) },
{ TextureFormat.Z24S8RUintGUnormBUnormAUnorm, 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.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.S8Z24RUnormGUintBUintAUint, new FormatInfo(Format.S8UintD24Unorm, 1, 1, 4, 2) },
{ TextureFormat.R32B24G8RFloatGUintBUnormAUnorm, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) }, { TextureFormat.R32B24G8RFloatGUintBUnormAUnorm, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) },
{ TextureFormat.Zf32X24S8RFloatGUintBUnormAUnorm, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) }, { TextureFormat.Zf32X24S8RFloatGUintBUnormAUnorm, new FormatInfo(Format.D32FloatS8Uint, 1, 1, 8, 2) },

View File

@@ -634,7 +634,7 @@ namespace Ryujinx.Graphics.Gpu.Image
state.Texture = hostTextureRebind; state.Texture = hostTextureRebind;
state.ImageFormat = format; state.ImageFormat = format;
_context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTextureRebind, format); _context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTextureRebind, format);
} }
continue; continue;
@@ -692,7 +692,7 @@ namespace Ryujinx.Graphics.Gpu.Image
state.ImageFormat = format; state.ImageFormat = format;
_context.Renderer.Pipeline.SetImage(bindingInfo.Binding, hostTexture, format); _context.Renderer.Pipeline.SetImage(stage, bindingInfo.Binding, hostTexture, format);
} }
state.CachedTexture = texture; state.CachedTexture = texture;

View File

@@ -242,7 +242,8 @@ namespace Ryujinx.Graphics.Gpu.Image
return TextureMatchQuality.FormatAlias; return TextureMatchQuality.FormatAlias;
} }
else if ((lhs.FormatInfo.Format == Format.D24UnormS8Uint || 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; return TextureMatchQuality.FormatAlias;
} }

View File

@@ -484,7 +484,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
if (binding.IsImage) 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 else
{ {

View File

@@ -68,6 +68,7 @@ namespace Ryujinx.Graphics.OpenGL
Add(Format.S8Uint, new FormatInfo(1, false, false, All.StencilIndex8, PixelFormat.StencilIndex, PixelType.UnsignedByte)); 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.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.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.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.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)); 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]; 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;
}
} }
} }

View File

@@ -119,11 +119,11 @@ namespace Ryujinx.Graphics.OpenGL
private static FramebufferAttachment GetAttachment(Format format) private static FramebufferAttachment GetAttachment(Format format)
{ {
if (IsPackedDepthStencilFormat(format)) if (FormatTable.IsPackedDepthStencil(format))
{ {
return FramebufferAttachment.DepthStencilAttachment; return FramebufferAttachment.DepthStencilAttachment;
} }
else if (IsDepthOnlyFormat(format)) else if (FormatTable.IsDepthOnly(format))
{ {
return FramebufferAttachment.DepthAttachment; 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) public int GetColorLayerCount(int index)
{ {
return _colors[index]?.Info.GetDepthOrLayers() ?? 0; return _colors[index]?.Info.GetDepthOrLayers() ?? 0;

View File

@@ -294,7 +294,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
{ {
return FramebufferAttachment.DepthStencilAttachment; return FramebufferAttachment.DepthStencilAttachment;
} }
else if (IsDepthOnly(format)) else if (FormatTable.IsDepthOnly(format))
{ {
return FramebufferAttachment.DepthAttachment; return FramebufferAttachment.DepthAttachment;
} }
@@ -324,11 +324,11 @@ namespace Ryujinx.Graphics.OpenGL.Image
private static ClearBufferMask GetMask(Format format) 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; return ClearBufferMask.DepthBufferBit | ClearBufferMask.StencilBufferBit;
} }
else if (IsDepthOnly(format)) else if (FormatTable.IsDepthOnly(format))
{ {
return ClearBufferMask.DepthBufferBit; 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) public TextureView BgraSwap(TextureView from)
{ {
TextureView to = (TextureView)_renderer.CreateTexture(from.Info); TextureView to = (TextureView)_renderer.CreateTexture(from.Info);

View File

@@ -935,7 +935,7 @@ namespace Ryujinx.Graphics.OpenGL
SetFrontFace(_frontFace = frontFace.Convert()); 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) if ((uint)binding < SavedImages)
{ {

View File

@@ -0,0 +1,229 @@
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;
int end = list.Count;
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)
{
end = i + 1;
break;
}
}
else
{
if (flags.Equals(barrier.Flags))
{
target[count++] = barrier.Barrier;
queuedBarrierCount--;
if (firstMatch == -1)
{
firstMatch = i;
}
if (count >= target.Length)
{
end = i + 1;
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;
end = list.Count;
}
}
}
}
if (firstMatch == 0 && end == list.Count)
{
list.Clear();
}
else if (firstMatch != -1)
{
int deleteCount = end - 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();
}
}
}

View File

@@ -16,6 +16,7 @@ namespace Ryujinx.Graphics.Vulkan
public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages; public const int MaxStorageBufferBindings = MaxStorageBuffersPerStage * MaxShaderStages;
public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages; public const int MaxTextureBindings = MaxTexturesPerStage * MaxShaderStages;
public const int MaxImageBindings = MaxImagesPerStage * MaxShaderStages; public const int MaxImageBindings = MaxImagesPerStage * MaxShaderStages;
public const int MaxPushDescriptorBinding = 64;
public const ulong SparseBufferAlignment = 0x10000; public const ulong SparseBufferAlignment = 0x10000;
} }

View File

@@ -1,19 +1,32 @@
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Ryujinx.Graphics.Vulkan namespace Ryujinx.Graphics.Vulkan
{ {
class DescriptorSetTemplate : IDisposable 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 VulkanRenderer _gd;
private readonly Device _device; private readonly Device _device;
public readonly DescriptorUpdateTemplate Template; public readonly DescriptorUpdateTemplate Template;
public readonly int Size; 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; _gd = gd;
_device = device; _device = device;
@@ -137,6 +150,93 @@ namespace Ryujinx.Graphics.Vulkan
Template = result; 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() public unsafe void Dispose()
{ {
_gd.Api.DestroyDescriptorUpdateTemplate(_device, Template, null); _gd.Api.DestroyDescriptorUpdateTemplate(_device, Template, null);

View File

@@ -52,11 +52,23 @@ namespace Ryujinx.Graphics.Vulkan
return new DescriptorSetTemplateWriter(new Span<byte>(_data.Pointer, template.Size)); 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) public void Commit(VulkanRenderer gd, Device device, DescriptorSet set)
{ {
gd.Api.UpdateDescriptorSetWithTemplate(device, set, _activeTemplate.Template, _data.Pointer); 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() public void Dispose()
{ {
_data?.Dispose(); _data?.Dispose();

View File

@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Shader;
using Silk.NET.Vulkan; using Silk.NET.Vulkan;
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using CompareOp = Ryujinx.Graphics.GAL.CompareOp; using CompareOp = Ryujinx.Graphics.GAL.CompareOp;
using Format = Ryujinx.Graphics.GAL.Format; using Format = Ryujinx.Graphics.GAL.Format;
using SamplerCreateInfo = Ryujinx.Graphics.GAL.SamplerCreateInfo; 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 VulkanRenderer _gd;
private readonly Device _device; private readonly Device _device;
private readonly PipelineBase _pipeline; private readonly PipelineBase _pipeline;
@@ -41,9 +72,8 @@ namespace Ryujinx.Graphics.Vulkan
private readonly BufferRef[] _uniformBufferRefs; private readonly BufferRef[] _uniformBufferRefs;
private readonly BufferRef[] _storageBufferRefs; private readonly BufferRef[] _storageBufferRefs;
private readonly Auto<DisposableImageView>[] _textureRefs; private readonly TextureRef[] _textureRefs;
private readonly Auto<DisposableSampler>[] _samplerRefs; private readonly ImageRef[] _imageRefs;
private readonly Auto<DisposableImageView>[] _imageRefs;
private readonly TextureBuffer[] _bufferTextureRefs; private readonly TextureBuffer[] _bufferTextureRefs;
private readonly TextureBuffer[] _bufferImageRefs; private readonly TextureBuffer[] _bufferImageRefs;
private readonly Format[] _bufferImageFormats; private readonly Format[] _bufferImageFormats;
@@ -61,6 +91,8 @@ namespace Ryujinx.Graphics.Vulkan
private BitMapStruct<Array2<long>> _storageSet; private BitMapStruct<Array2<long>> _storageSet;
private BitMapStruct<Array2<long>> _uniformMirrored; private BitMapStruct<Array2<long>> _uniformMirrored;
private BitMapStruct<Array2<long>> _storageMirrored; private BitMapStruct<Array2<long>> _storageMirrored;
private readonly int[] _uniformSetPd;
private int _pdSequence = 1;
private bool _updateDescriptorCacheCbIndex; private bool _updateDescriptorCacheCbIndex;
@@ -92,9 +124,8 @@ namespace Ryujinx.Graphics.Vulkan
_uniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings]; _uniformBufferRefs = new BufferRef[Constants.MaxUniformBufferBindings];
_storageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings]; _storageBufferRefs = new BufferRef[Constants.MaxStorageBufferBindings];
_textureRefs = new Auto<DisposableImageView>[Constants.MaxTextureBindings * 2]; _textureRefs = new TextureRef[Constants.MaxTextureBindings * 2];
_samplerRefs = new Auto<DisposableSampler>[Constants.MaxTextureBindings * 2]; _imageRefs = new ImageRef[Constants.MaxImageBindings * 2];
_imageRefs = new Auto<DisposableImageView>[Constants.MaxImageBindings * 2];
_bufferTextureRefs = new TextureBuffer[Constants.MaxTextureBindings * 2]; _bufferTextureRefs = new TextureBuffer[Constants.MaxTextureBindings * 2];
_bufferImageRefs = new TextureBuffer[Constants.MaxImageBindings * 2]; _bufferImageRefs = new TextureBuffer[Constants.MaxImageBindings * 2];
_bufferImageFormats = new Format[Constants.MaxImageBindings * 2]; _bufferImageFormats = new Format[Constants.MaxImageBindings * 2];
@@ -106,6 +137,8 @@ namespace Ryujinx.Graphics.Vulkan
_bufferTextures = new BufferView[Constants.MaxTexturesPerStage]; _bufferTextures = new BufferView[Constants.MaxTexturesPerStage];
_bufferImages = new BufferView[Constants.MaxImagesPerStage]; _bufferImages = new BufferView[Constants.MaxImagesPerStage];
_uniformSetPd = new int[Constants.MaxUniformBufferBindings];
var initialImageInfo = new DescriptorImageInfo var initialImageInfo = new DescriptorImageInfo
{ {
ImageLayout = ImageLayout.General, ImageLayout = ImageLayout.General,
@@ -193,6 +226,7 @@ namespace Ryujinx.Graphics.Vulkan
if (BindingOverlaps(ref info, bindingOffset, offset, size)) if (BindingOverlaps(ref info, bindingOffset, offset, size))
{ {
_uniformSet.Clear(binding); _uniformSet.Clear(binding);
_uniformSetPd[binding] = 0;
SignalDirty(DirtyFlags.Uniform); SignalDirty(DirtyFlags.Uniform);
} }
} }
@@ -223,14 +257,61 @@ 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();
}
_program = program; _program = program;
_updateDescriptorCacheCbIndex = true; _updateDescriptorCacheCbIndex = true;
_dirty = DirtyFlags.All; _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) if (image is TextureBuffer imageBuffer)
{ {
@@ -239,11 +320,13 @@ namespace Ryujinx.Graphics.Vulkan
} }
else if (image is TextureView view) 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 else
{ {
_imageRefs[binding] = null; _imageRefs[binding] = default;
_bufferImageRefs[binding] = null; _bufferImageRefs[binding] = null;
_bufferImageFormats[binding] = default; _bufferImageFormats[binding] = default;
} }
@@ -253,7 +336,7 @@ namespace Ryujinx.Graphics.Vulkan
public void SetImage(int binding, Auto<DisposableImageView> image) public void SetImage(int binding, Auto<DisposableImageView> image)
{ {
_imageRefs[binding] = image; _imageRefs[binding] = new(ShaderStage.Compute, null, image);
SignalDirty(DirtyFlags.Image); SignalDirty(DirtyFlags.Image);
} }
@@ -338,15 +421,13 @@ namespace Ryujinx.Graphics.Vulkan
} }
else if (texture is TextureView view) 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(); _textureRefs[binding] = new(stage, view.Storage, view.GetImageView(), ((SamplerHolder)sampler)?.GetSampler());
_samplerRefs[binding] = ((SamplerHolder)sampler)?.GetSampler();
} }
else else
{ {
_textureRefs[binding] = null; _textureRefs[binding] = default;
_samplerRefs[binding] = null;
_bufferTextureRefs[binding] = null; _bufferTextureRefs[binding] = null;
} }
@@ -362,10 +443,9 @@ namespace Ryujinx.Graphics.Vulkan
{ {
if (texture is TextureView view) 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(); _textureRefs[binding] = new(stage, view.Storage, view.GetIdentityImageView(), ((SamplerHolder)sampler)?.GetSampler());
_samplerRefs[binding] = ((SamplerHolder)sampler)?.GetSampler();
SignalDirty(DirtyFlags.Texture); SignalDirty(DirtyFlags.Texture);
} }
@@ -402,6 +482,7 @@ namespace Ryujinx.Graphics.Vulkan
if (!currentBufferRef.Equals(newRef) || currentInfo.Range != info.Range) if (!currentBufferRef.Equals(newRef) || currentInfo.Range != info.Range)
{ {
_uniformSet.Clear(index); _uniformSet.Clear(index);
_uniformSetPd[index] = 0;
currentInfo = info; currentInfo = info;
currentBufferRef = newRef; currentBufferRef = newRef;
@@ -579,9 +660,10 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
ref var texture = ref textures[i]; ref var texture = ref textures[i];
ref var refs = ref _textureRefs[binding + i];
texture.ImageView = _textureRefs[binding + i]?.Get(cbs).Value ?? default; texture.ImageView = refs.View?.Get(cbs).Value ?? default;
texture.Sampler = _samplerRefs[binding + i]?.Get(cbs).Value ?? default; texture.Sampler = refs.Sampler?.Get(cbs).Value ?? default;
if (texture.ImageView.Handle == 0) if (texture.ImageView.Handle == 0)
{ {
@@ -616,7 +698,7 @@ namespace Ryujinx.Graphics.Vulkan
for (int i = 0; i < count; i++) 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]); tu.Push<DescriptorImageInfo>(images[..count]);
@@ -671,15 +753,19 @@ namespace Ryujinx.Graphics.Vulkan
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs, PipelineBindPoint pbp) private void UpdateAndBindUniformBufferPd(CommandBufferScoped cbs, PipelineBindPoint pbp)
{ {
int sequence = _pdSequence;
var bindingSegments = _program.BindingSegments[PipelineBase.UniformSetIndex]; var bindingSegments = _program.BindingSegments[PipelineBase.UniformSetIndex];
var dummyBuffer = _dummyBuffer?.GetBuffer(); var dummyBuffer = _dummyBuffer?.GetBuffer();
long updatedBindings = 0;
DescriptorSetTemplateWriter writer = _templateUpdater.Begin(32 * Unsafe.SizeOf<DescriptorBufferInfo>());
foreach (ResourceBindingSegment segment in bindingSegments) foreach (ResourceBindingSegment segment in bindingSegments)
{ {
int binding = segment.Binding; int binding = segment.Binding;
int count = segment.Count; int count = segment.Count;
bool doUpdate = false; ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
for (int i = 0; i < count; i++) for (int i = 0; i < count; i++)
{ {
@@ -688,16 +774,28 @@ namespace Ryujinx.Graphics.Vulkan
if (_uniformSet.Set(index)) if (_uniformSet.Set(index))
{ {
ref BufferRef buffer = ref _uniformBufferRefs[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)
{ {
ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers; DescriptorSetTemplate template = _program.GetPushDescriptorTemplate(updatedBindings);
UpdateBuffers(cbs, pbp, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer); _templateUpdater.CommitPushDescriptor(_gd, cbs, template, _program.PipelineLayout);
}
} }
} }
@@ -724,6 +822,7 @@ namespace Ryujinx.Graphics.Vulkan
_uniformSet.Clear(); _uniformSet.Clear();
_storageSet.Clear(); _storageSet.Clear();
AdvancePdSequence();
} }
private static void SwapBuffer(BufferRef[] list, Auto<DisposableBuffer> from, Auto<DisposableBuffer> to) private static void SwapBuffer(BufferRef[] list, Auto<DisposableBuffer> from, Auto<DisposableBuffer> to)

View File

@@ -154,7 +154,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim; int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) }); _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.DispatchCompute(dispatchX, dispatchY, 1);
_pipeline.ComputeBarrier(); _pipeline.ComputeBarrier();

View File

@@ -75,7 +75,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize); var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize);
var dispatchY = BitUtils.DivRoundUp(view.Height, 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.DispatchCompute(dispatchX, dispatchY, 1);
_pipeline.ComputeBarrier(); _pipeline.ComputeBarrier();

View File

@@ -219,7 +219,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
buffer.Holder.SetDataUnchecked(buffer.Offset, resolutionBuffer); buffer.Holder.SetDataUnchecked(buffer.Offset, resolutionBuffer);
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, buffer.Range) }); _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.DispatchCompute(dispatchX, dispatchY, 1);
_pipeline.ComputeBarrier(); _pipeline.ComputeBarrier();
@@ -229,7 +229,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _edgeOutputTexture, _samplerLinear); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _edgeOutputTexture, _samplerLinear);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _areaTexture, _samplerLinear); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _areaTexture, _samplerLinear);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 4, _searchTexture, _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.DispatchCompute(dispatchX, dispatchY, 1);
_pipeline.ComputeBarrier(); _pipeline.ComputeBarrier();
@@ -238,7 +238,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
_pipeline.Specialize(_specConstants); _pipeline.Specialize(_specConstants);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _blendOutputTexture, _samplerLinear); _pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _blendOutputTexture, _samplerLinear);
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _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.DispatchCompute(dispatchX, dispatchY, 1);
_pipeline.ComputeBarrier(); _pipeline.ComputeBarrier();

View File

@@ -376,7 +376,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
return format switch 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.S8Uint => ImageAspectFlags.StencilBit,
Format.D24UnormS8Uint or Format.D24UnormS8Uint or
Format.D32FloatS8Uint or Format.D32FloatS8Uint or
@@ -389,7 +389,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
return format switch 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.S8Uint => ImageAspectFlags.StencilBit,
Format.D24UnormS8Uint or Format.D24UnormS8Uint or
Format.D32FloatS8Uint or Format.D32FloatS8Uint or

View File

@@ -220,7 +220,7 @@ namespace Ryujinx.Graphics.Vulkan
public static bool IsD24S8(Format format) 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) private static bool IsRGB16IntFloat(Format format)

View File

@@ -67,6 +67,7 @@ namespace Ryujinx.Graphics.Vulkan
Add(Format.S8Uint, VkFormat.S8Uint); Add(Format.S8Uint, VkFormat.S8Uint);
Add(Format.D16Unorm, VkFormat.D16Unorm); Add(Format.D16Unorm, VkFormat.D16Unorm);
Add(Format.S8UintD24Unorm, VkFormat.D24UnormS8Uint); Add(Format.S8UintD24Unorm, VkFormat.D24UnormS8Uint);
Add(Format.X8UintD24Unorm, VkFormat.X8D24UnormPack32);
Add(Format.D32Float, VkFormat.D32Sfloat); Add(Format.D32Float, VkFormat.D32Sfloat);
Add(Format.D24UnormS8Uint, VkFormat.D24UnormS8Uint); Add(Format.D24UnormS8Uint, VkFormat.D24UnormS8Uint);
Add(Format.D32FloatS8Uint, VkFormat.D32SfloatS8Uint); Add(Format.D32FloatS8Uint, VkFormat.D32SfloatS8Uint);

View File

@@ -243,41 +243,6 @@ namespace Ryujinx.Graphics.Vulkan
return new Auto<DisposableFramebuffer>(new DisposableFramebuffer(api, _device, framebuffer), null, _attachments); 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() public TextureView[] GetAttachmentViews()
{ {
var result = new TextureView[_attachments.Length]; var result = new TextureView[_attachments.Length];
@@ -297,23 +262,20 @@ namespace Ryujinx.Graphics.Vulkan
return new RenderPassCacheKey(_depthStencil, _colorsCanonical); return new RenderPassCacheKey(_depthStencil, _colorsCanonical);
} }
public void InsertLoadOpBarriers(CommandBufferScoped cbs) public void InsertLoadOpBarriers(VulkanRenderer gd, CommandBufferScoped cbs)
{ {
if (_colors != null) if (_colors != null)
{ {
foreach (var color in _colors) foreach (var color in _colors)
{ {
// If Clear or DontCare were used, this would need to be write bit. // If Clear or DontCare were used, this would need to be write bit.
color.Storage?.InsertWriteToReadBarrier(cbs, AccessFlags.ColorAttachmentReadBit, PipelineStageFlags.ColorAttachmentOutputBit); color.Storage?.QueueLoadOpBarrier(cbs, false);
color.Storage?.SetModification(AccessFlags.ColorAttachmentWriteBit, PipelineStageFlags.ColorAttachmentOutputBit);
} }
} }
if (_depthStencil != null) _depthStencil?.Storage?.QueueLoadOpBarrier(cbs, true);
{
_depthStencil.Storage?.InsertWriteToReadBarrier(cbs, AccessFlags.DepthStencilAttachmentReadBit, PipelineStageFlags.EarlyFragmentTestsBit); gd.Barriers.Flush(cbs.CommandBuffer, false, null);
_depthStencil.Storage?.SetModification(AccessFlags.DepthStencilAttachmentWriteBit, PipelineStageFlags.LateFragmentTestsBit);
}
} }
public (Auto<DisposableRenderPass> renderPass, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer( public (Auto<DisposableRenderPass> renderPass, Auto<DisposableFramebuffer> framebuffer) GetPassAndFramebuffer(

View File

@@ -34,6 +34,7 @@ namespace Ryujinx.Graphics.Vulkan
public readonly bool SupportsMultiView; public readonly bool SupportsMultiView;
public readonly bool SupportsNullDescriptors; public readonly bool SupportsNullDescriptors;
public readonly bool SupportsPushDescriptors; public readonly bool SupportsPushDescriptors;
public readonly uint MaxPushDescriptors;
public readonly bool SupportsPrimitiveTopologyListRestart; public readonly bool SupportsPrimitiveTopologyListRestart;
public readonly bool SupportsPrimitiveTopologyPatchListRestart; public readonly bool SupportsPrimitiveTopologyPatchListRestart;
public readonly bool SupportsTransformFeedback; public readonly bool SupportsTransformFeedback;
@@ -71,6 +72,7 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsMultiView, bool supportsMultiView,
bool supportsNullDescriptors, bool supportsNullDescriptors,
bool supportsPushDescriptors, bool supportsPushDescriptors,
uint maxPushDescriptors,
bool supportsPrimitiveTopologyListRestart, bool supportsPrimitiveTopologyListRestart,
bool supportsPrimitiveTopologyPatchListRestart, bool supportsPrimitiveTopologyPatchListRestart,
bool supportsTransformFeedback, bool supportsTransformFeedback,
@@ -107,6 +109,7 @@ namespace Ryujinx.Graphics.Vulkan
SupportsMultiView = supportsMultiView; SupportsMultiView = supportsMultiView;
SupportsNullDescriptors = supportsNullDescriptors; SupportsNullDescriptors = supportsNullDescriptors;
SupportsPushDescriptors = supportsPushDescriptors; SupportsPushDescriptors = supportsPushDescriptors;
MaxPushDescriptors = maxPushDescriptors;
SupportsPrimitiveTopologyListRestart = supportsPrimitiveTopologyListRestart; SupportsPrimitiveTopologyListRestart = supportsPrimitiveTopologyListRestart;
SupportsPrimitiveTopologyPatchListRestart = supportsPrimitiveTopologyPatchListRestart; SupportsPrimitiveTopologyPatchListRestart = supportsPrimitiveTopologyPatchListRestart;
SupportsTransformFeedback = supportsTransformFeedback; SupportsTransformFeedback = supportsTransformFeedback;

View File

@@ -1039,7 +1039,7 @@ namespace Ryujinx.Graphics.Vulkan
var dstView = Create2DLayerView(dst, dstLayer + z, dstLevel + l); var dstView = Create2DLayerView(dst, dstLayer + z, dstLevel + l);
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null); _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 dispatchX = (Math.Min(srcView.Info.Width, dstView.Info.Width) + 31) / 32;
int dispatchY = (Math.Min(srcView.Info.Height, dstView.Info.Height) + 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); var dstView = Create2DLayerView(dst, dstLayer + z, 0);
_pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null); _pipeline.SetTextureAndSamplerIdentitySwizzle(ShaderStage.Compute, 0, srcView, null);
_pipeline.SetImage(0, dstView, format); _pipeline.SetImage(ShaderStage.Compute, 0, dstView, format);
_pipeline.DispatchCompute(dispatchX, dispatchY, 1); _pipeline.DispatchCompute(dispatchX, dispatchY, 1);

View File

@@ -36,6 +36,7 @@ namespace Ryujinx.Graphics.Vulkan
private PipelineState _newState; private PipelineState _newState;
private bool _graphicsStateDirty; private bool _graphicsStateDirty;
private bool _computeStateDirty; private bool _computeStateDirty;
private bool _bindingBarriersDirty;
private PrimitiveTopology _topology; private PrimitiveTopology _topology;
private ulong _currentPipelineHandle; private ulong _currentPipelineHandle;
@@ -248,14 +249,14 @@ namespace Ryujinx.Graphics.Vulkan
CreateRenderPass(); CreateRenderPass();
} }
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
BeginRenderPass(); BeginRenderPass();
var clearValue = new ClearValue(new ClearColorValue(color.Red, color.Green, color.Blue, color.Alpha)); var clearValue = new ClearValue(new ClearColorValue(color.Red, color.Green, color.Blue, color.Alpha));
var attachment = new ClearAttachment(ImageAspectFlags.ColorBit, (uint)index, clearValue); var attachment = new ClearAttachment(ImageAspectFlags.ColorBit, (uint)index, clearValue);
var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount); var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount);
FramebufferParams.InsertClearBarrier(Cbs, index);
Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect); Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect);
} }
@@ -286,13 +287,13 @@ namespace Ryujinx.Graphics.Vulkan
CreateRenderPass(); CreateRenderPass();
} }
Gd.Barriers.Flush(Cbs.CommandBuffer, RenderPassActive, EndRenderPassDelegate);
BeginRenderPass(); BeginRenderPass();
var attachment = new ClearAttachment(flags, 0, clearValue); var attachment = new ClearAttachment(flags, 0, clearValue);
var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount); var clearRect = FramebufferParams.GetClearRect(ClearScissor, layer, layerCount);
FramebufferParams.InsertClearBarrierDS(Cbs);
Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect); Gd.Api.CmdClearAttachments(CommandBuffer, 1, &attachment, 1, &clearRect);
} }
@@ -887,9 +888,9 @@ namespace Ryujinx.Graphics.Vulkan
SignalStateChange(); 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) public void SetImage(int binding, Auto<DisposableImageView> image)
@@ -976,7 +977,8 @@ namespace Ryujinx.Graphics.Vulkan
_program = internalProgram; _program = internalProgram;
_descriptorSetUpdater.SetProgram(internalProgram); _descriptorSetUpdater.SetProgram(Cbs, internalProgram, _currentPipelineHandle != 0);
_bindingBarriersDirty = true;
_newState.PipelineLayout = internalProgram.PipelineLayout; _newState.PipelineLayout = internalProgram.PipelineLayout;
_newState.StagesCount = (uint)stages.Length; _newState.StagesCount = (uint)stages.Length;
@@ -1066,7 +1068,6 @@ namespace Ryujinx.Graphics.Vulkan
private void SetRenderTargetsInternal(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked) private void SetRenderTargetsInternal(ITexture[] colors, ITexture depthStencil, bool filterWriteMasked)
{ {
CreateFramebuffer(colors, depthStencil, filterWriteMasked); CreateFramebuffer(colors, depthStencil, filterWriteMasked);
FramebufferParams?.UpdateModifications();
CreateRenderPass(); CreateRenderPass();
SignalStateChange(); SignalStateChange();
SignalAttachmentChange(); SignalAttachmentChange();
@@ -1520,8 +1521,18 @@ namespace Ryujinx.Graphics.Vulkan
CreatePipeline(PipelineBindPoint.Compute); CreatePipeline(PipelineBindPoint.Compute);
_computeStateDirty = false; _computeStateDirty = false;
Pbp = PipelineBindPoint.Compute; 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); _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Compute);
} }
@@ -1575,8 +1586,18 @@ namespace Ryujinx.Graphics.Vulkan
_graphicsStateDirty = false; _graphicsStateDirty = false;
Pbp = PipelineBindPoint.Graphics; 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); _descriptorSetUpdater.UpdateAndBindDescriptorSets(Cbs, PipelineBindPoint.Graphics);
return true; return true;
@@ -1630,6 +1651,8 @@ namespace Ryujinx.Graphics.Vulkan
{ {
if (!RenderPassActive) if (!RenderPassActive)
{ {
FramebufferParams.InsertLoadOpBarriers(Gd, Cbs);
var renderArea = new Rect2D(null, new Extent2D(FramebufferParams.Width, FramebufferParams.Height)); var renderArea = new Rect2D(null, new Extent2D(FramebufferParams.Width, FramebufferParams.Height));
var clearValue = new ClearValue(); var clearValue = new ClearValue();

View File

@@ -269,6 +269,7 @@ namespace Ryujinx.Graphics.Vulkan
PreloadCbs = null; PreloadCbs = null;
} }
Gd.Barriers.Flush(Cbs.CommandBuffer, false, null);
CommandBuffer = (Cbs = Gd.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer; CommandBuffer = (Cbs = Gd.CommandBufferPool.ReturnAndRent(Cbs)).CommandBuffer;
Gd.RegisterFlush(); Gd.RegisterFlush();

View File

@@ -31,6 +31,11 @@ namespace Ryujinx.Graphics.Vulkan
private int _dsLastCbIndex; private int _dsLastCbIndex;
private int _dsLastSubmissionCount; 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) private PipelineLayoutCacheEntry(VulkanRenderer gd, Device device, int setsCount)
{ {
_gd = gd; _gd = gd;
@@ -72,6 +77,12 @@ namespace Ryujinx.Graphics.Vulkan
_consumedDescriptorsPerSet[setIndex] = count; _consumedDescriptorsPerSet[setIndex] = count;
} }
if (usePushDescriptors)
{
_pdDescriptors = setDescriptors[0];
_pdTemplates = new();
}
} }
public void UpdateCommandBufferIndex(int commandBufferIndex) public void UpdateCommandBufferIndex(int commandBufferIndex)
@@ -143,10 +154,39 @@ namespace Ryujinx.Graphics.Vulkan
return output[..count]; 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) protected virtual unsafe void Dispose(bool disposing)
{ {
if (disposing) if (disposing)
{ {
if (_pdTemplates != null)
{
foreach (DescriptorSetTemplate template in _pdTemplates.Values)
{
template.Dispose();
}
}
for (int i = 0; i < _dsCache.Length; i++) for (int i = 0; i < _dsCache.Length; i++)
{ {
for (int j = 0; j < _dsCache[i].Length; j++) for (int j = 0; j < _dsCache[i].Length; j++)

View File

@@ -108,18 +108,26 @@ namespace Ryujinx.Graphics.Vulkan
_shaders = internalShaders; _shaders = internalShaders;
bool usePushDescriptors = !isMinimal && VulkanConfiguration.UsePushDescriptors && _gd.Capabilities.SupportsPushDescriptors; bool usePushDescriptors = !isMinimal &&
VulkanConfiguration.UsePushDescriptors &&
_gd.Capabilities.SupportsPushDescriptors &&
!_gd.IsNvidiaPreTuring &&
!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; HasMinimalLayout = isMinimal;
UsePushDescriptors = usePushDescriptors; UsePushDescriptors = usePushDescriptors;
Stages = stages; Stages = stages;
ClearSegments = BuildClearSegments(resourceLayout.Sets); ClearSegments = BuildClearSegments(sets);
BindingSegments = BuildBindingSegments(resourceLayout.SetUsages); BindingSegments = BuildBindingSegments(resourceLayout.SetUsages);
Templates = BuildTemplates(); Templates = BuildTemplates(usePushDescriptors);
_compileTask = Task.CompletedTask; _compileTask = Task.CompletedTask;
_firstBackgroundUse = false; _firstBackgroundUse = false;
@@ -139,6 +147,76 @@ namespace Ryujinx.Graphics.Vulkan
_firstBackgroundUse = !fromCache; _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) private static ResourceBindingSegment[][] BuildClearSegments(ReadOnlyCollection<ResourceDescriptorCollection> sets)
{ {
ResourceBindingSegment[][] segments = new ResourceBindingSegment[sets.Count][]; ResourceBindingSegment[][] segments = new ResourceBindingSegment[sets.Count][];
@@ -243,12 +321,18 @@ namespace Ryujinx.Graphics.Vulkan
return segments; return segments;
} }
private DescriptorSetTemplate[] BuildTemplates() private DescriptorSetTemplate[] BuildTemplates(bool usePushDescriptors)
{ {
var templates = new DescriptorSetTemplate[BindingSegments.Length]; var templates = new DescriptorSetTemplate[BindingSegments.Length];
for (int setIndex = 0; setIndex < BindingSegments.Length; setIndex++) 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]; ResourceBindingSegment[] segments = BindingSegments[setIndex];
if (segments != null && segments.Length > 0) if (segments != null && segments.Length > 0)
@@ -433,6 +517,11 @@ namespace Ryujinx.Graphics.Vulkan
return null; 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) public void AddComputePipeline(ref SpecData key, Auto<DisposablePipeline> pipeline)
{ {
(_computePipelineCache ??= new()).Add(ref key, pipeline); (_computePipelineCache ??= new()).Add(ref key, pipeline);
@@ -493,6 +582,11 @@ namespace Ryujinx.Graphics.Vulkan
return _plce.GetNewDescriptorSetCollection(setIndex, out isNew); return _plce.GetNewDescriptorSetCollection(setIndex, out isNew);
} }
public bool HasSameLayout(ShaderCollection other)
{
return other != null && _plce == other._plce;
}
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (disposing) if (disposing)

View File

@@ -154,9 +154,8 @@ namespace Ryujinx.Graphics.Vulkan
{ {
Format.S8Uint => Format.R8Unorm, Format.S8Uint => Format.R8Unorm,
Format.D16Unorm => Format.R16Unorm, Format.D16Unorm => Format.R16Unorm,
Format.S8UintD24Unorm => Format.R8G8B8A8Unorm, Format.D24UnormS8Uint or Format.S8UintD24Unorm or Format.X8UintD24Unorm => Format.R8G8B8A8Unorm,
Format.D32Float => Format.R32Float, Format.D32Float => Format.R32Float,
Format.D24UnormS8Uint => Format.R8G8B8A8Unorm,
Format.D32FloatS8Uint => Format.R32G32Float, Format.D32FloatS8Uint => Format.R32G32Float,
_ => throw new ArgumentException($"\"{format}\" is not a supported depth or stencil format."), _ => 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; return FormatCapabilities.IsD24S8(Info.Format) && VkFormat == VkFormat.D32SfloatS8Uint;
} }
public void SetModification(AccessFlags accessFlags, PipelineStageFlags stage) public void QueueLoadOpBarrier(CommandBufferScoped cbs, bool depthStencil)
{ {
_lastModificationAccess = accessFlags; PipelineStageFlags srcStageFlags = _lastReadStage | _lastModificationStage;
_lastModificationStage = stage; PipelineStageFlags dstStageFlags = depthStencil ?
} PipelineStageFlags.EarlyFragmentTestsBit | PipelineStageFlags.LateFragmentTestsBit :
PipelineStageFlags.ColorAttachmentOutputBit;
public void InsertReadToWriteBarrier(CommandBufferScoped cbs, AccessFlags dstAccessFlags, PipelineStageFlags dstStageFlags, bool insideRenderPass) AccessFlags srcAccessFlags = _lastModificationAccess | _lastReadAccess;
{ AccessFlags dstAccessFlags = depthStencil ?
var lastReadStage = _lastReadStage; 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, ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
// as it is invalid to specify compute in the subpass dependency stage mask. 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; _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; _lastReadAccess |= dstAccessFlags;
_lastReadStage |= dstStageFlags; _lastReadStage |= dstStageFlags;
if (_lastModificationAccess != AccessFlags.None) if (_lastModificationAccess != AccessFlags.None)
{ {
// This would result in a validation error, but is ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags();
// required on MoltenVK as the generic barrier results in ImageMemoryBarrier barrier = TextureView.GetImageBarrier(
// severe texture flickering in some scenarios. _imageAuto.Get(cbs).Value,
if (_gd.IsMoltenVk) _lastModificationAccess,
{ dstAccessFlags,
ImageAspectFlags aspectFlags = Info.Format.ConvertAspectFlags(); aspectFlags,
TextureView.InsertImageBarrier( 0,
_gd.Api, 0,
cbs.CommandBuffer, _info.GetLayers(),
_imageAuto.Get(cbs).Value, _info.Levels);
_lastModificationAccess,
dstAccessFlags, _gd.Barriers.QueueBarrier(barrier, _lastModificationStage, dstStageFlags);
_lastModificationStage,
dstStageFlags,
aspectFlags,
0,
0,
_info.GetLayers(),
_info.Levels);
}
else
{
TextureView.InsertMemoryBarrier(
_gd.Api,
cbs.CommandBuffer,
_lastModificationAccess,
dstAccessFlags,
_lastModificationStage,
dstStageFlags);
}
_lastModificationAccess = AccessFlags.None; _lastModificationAccess = AccessFlags.None;
} }

View File

@@ -497,6 +497,30 @@ namespace Ryujinx.Graphics.Vulkan
null); 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( public static unsafe void InsertImageBarrier(
Vk api, Vk api,
CommandBuffer commandBuffer, CommandBuffer commandBuffer,
@@ -511,18 +535,15 @@ namespace Ryujinx.Graphics.Vulkan
int layers, int layers,
int levels) int levels)
{ {
ImageMemoryBarrier memoryBarrier = new() ImageMemoryBarrier memoryBarrier = GetImageBarrier(
{ image,
SType = StructureType.ImageMemoryBarrier, srcAccessMask,
SrcAccessMask = srcAccessMask, dstAccessMask,
DstAccessMask = dstAccessMask, aspectFlags,
SrcQueueFamilyIndex = Vk.QueueFamilyIgnored, firstLayer,
DstQueueFamilyIndex = Vk.QueueFamilyIgnored, firstLevel,
Image = image, layers,
OldLayout = ImageLayout.General, levels);
NewLayout = ImageLayout.General,
SubresourceRange = new ImageSubresourceRange(aspectFlags, (uint)firstLevel, (uint)levels, (uint)firstLayer, (uint)layers),
};
api.CmdPipelineBarrier( api.CmdPipelineBarrier(
commandBuffer, commandBuffer,

View File

@@ -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)")] [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(); 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) public static Vendor FromId(uint id)
{ {
return id switch return id switch

View File

@@ -4,7 +4,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
public const bool UseFastBufferUpdates = true; public const bool UseFastBufferUpdates = true;
public const bool UseUnsafeBlit = true; public const bool UseUnsafeBlit = true;
public const bool UsePushDescriptors = false; public const bool UsePushDescriptors = true;
public const bool ForceD24S8Unsupported = false; public const bool ForceD24S8Unsupported = false;
public const bool ForceRGB16IntFloatUnsupported = false; public const bool ForceRGB16IntFloatUnsupported = false;

View File

@@ -486,20 +486,6 @@ namespace Ryujinx.Graphics.Vulkan
pExtendedFeatures = &featuresFragmentShaderInterlock; 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; PhysicalDeviceCustomBorderColorFeaturesEXT featuresCustomBorderColor;
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color") && if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_custom_border_color") &&

View File

@@ -68,6 +68,8 @@ namespace Ryujinx.Graphics.Vulkan
internal HelperShader HelperShader { get; private set; } internal HelperShader HelperShader { get; private set; }
internal PipelineFull PipelineInternal => _pipeline; internal PipelineFull PipelineInternal => _pipeline;
internal BarrierBatch Barriers { get; private set; }
public IPipeline Pipeline => _pipeline; public IPipeline Pipeline => _pipeline;
public IWindow Window => _window; public IWindow Window => _window;
@@ -76,10 +78,15 @@ namespace Ryujinx.Graphics.Vulkan
private readonly Func<string[]> _getRequiredExtensions; private readonly Func<string[]> _getRequiredExtensions;
private readonly string _preferredGpuId; 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 Vendor Vendor { get; private set; }
internal bool IsAmdWindows { get; private set; } internal bool IsAmdWindows { get; private set; }
internal bool IsIntelWindows { get; private set; } internal bool IsIntelWindows { get; private set; }
internal bool IsAmdGcn { get; private set; } internal bool IsAmdGcn { get; private set; }
internal bool IsNvidiaPreTuring { get; private set; }
internal bool IsMoltenVk { get; private set; } internal bool IsMoltenVk { get; private set; }
internal bool IsTBDR { get; private set; } internal bool IsTBDR { get; private set; }
internal bool IsSharedMemory { get; private set; } internal bool IsSharedMemory { get; private set; }
@@ -191,6 +198,19 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDevicePortabilitySubsetPropertiesKhr, 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() PhysicalDeviceFeatures2 features2 = new()
{ {
SType = StructureType.PhysicalDeviceFeatures2, SType = StructureType.PhysicalDeviceFeatures2,
@@ -320,7 +340,8 @@ namespace Ryujinx.Graphics.Vulkan
_physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName), _physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName),
features2.Features.MultiViewport && !(IsMoltenVk && Vendor == Vendor.Amd), // Workaround for AMD on MoltenVK issue features2.Features.MultiViewport && !(IsMoltenVk && Vendor == Vendor.Amd), // Workaround for AMD on MoltenVK issue
featuresRobustness2.NullDescriptor || IsMoltenVk, featuresRobustness2.NullDescriptor || IsMoltenVk,
_physicalDevice.IsDeviceExtensionPresent(KhrPushDescriptor.ExtensionName), supportsPushDescriptors && !IsMoltenVk,
propertiesPushDescriptor.MaxPushDescriptors,
featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart, featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart,
featuresPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart, featuresPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart,
supportsTransformFeedback, supportsTransformFeedback,
@@ -362,6 +383,8 @@ namespace Ryujinx.Graphics.Vulkan
HelperShader = new HelperShader(this, _device); HelperShader = new HelperShader(this, _device);
Barriers = new BarrierBatch(this);
_counters = new Counters(this, _device, _pipeline); _counters = new Counters(this, _device, _pipeline);
} }
@@ -400,6 +423,25 @@ namespace Ryujinx.Graphics.Vulkan
_initialized = true; _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) public BufferHandle CreateBuffer(int size, BufferAccess access)
{ {
return BufferManager.CreateWithHandle(this, size, access.HasFlag(BufferAccess.SparseCompatible), access.Convert(), default, access == BufferAccess.Stream); 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); 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})"); Logger.Notice.Print(LogClass.Gpu, $"{GpuVendor} {GpuRenderer} ({GpuVersion})");
} }
@@ -862,6 +918,7 @@ namespace Ryujinx.Graphics.Vulkan
BufferManager.Dispose(); BufferManager.Dispose();
DescriptorSetManager.Dispose(); DescriptorSetManager.Dispose();
PipelineLayoutCache.Dispose(); PipelineLayoutCache.Dispose();
Barriers.Dispose();
MemoryAllocator.Dispose(); MemoryAllocator.Dispose();

View File

@@ -7,7 +7,7 @@ using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.HOS.SystemState;
using Ryujinx.HLE.Ui; using Ryujinx.HLE.UI;
using System; using System;
namespace Ryujinx.HLE namespace Ryujinx.HLE
@@ -63,7 +63,7 @@ namespace Ryujinx.HLE
/// The handler for various UI related operations needed outside of HLE. /// The handler for various UI related operations needed outside of HLE.
/// </summary> /// </summary>
/// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks> /// <remarks>This cannot be changed after <see cref="Switch"/> instantiation.</remarks>
internal readonly IHostUiHandler HostUiHandler; internal readonly IHostUIHandler HostUIHandler;
/// <summary> /// <summary>
/// Control the memory configuration used by the emulation context. /// Control the memory configuration used by the emulation context.
@@ -177,7 +177,7 @@ namespace Ryujinx.HLE
IRenderer gpuRenderer, IRenderer gpuRenderer,
IHardwareDeviceDriver audioDeviceDriver, IHardwareDeviceDriver audioDeviceDriver,
MemoryConfiguration memoryConfiguration, MemoryConfiguration memoryConfiguration,
IHostUiHandler hostUiHandler, IHostUIHandler hostUIHandler,
SystemLanguage systemLanguage, SystemLanguage systemLanguage,
RegionCode region, RegionCode region,
bool enableVsync, bool enableVsync,
@@ -204,7 +204,7 @@ namespace Ryujinx.HLE
GpuRenderer = gpuRenderer; GpuRenderer = gpuRenderer;
AudioDeviceDriver = audioDeviceDriver; AudioDeviceDriver = audioDeviceDriver;
MemoryConfiguration = memoryConfiguration; MemoryConfiguration = memoryConfiguration;
HostUiHandler = hostUiHandler; HostUIHandler = hostUIHandler;
SystemLanguage = systemLanguage; SystemLanguage = systemLanguage;
Region = region; Region = region;
EnableVsync = enableVsync; EnableVsync = enableVsync;

View File

@@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Applets
PlayerIndex primaryIndex; PlayerIndex primaryIndex;
while (!_system.Device.Hid.Npads.Validate(playerMin, playerMax, (ControllerType)privateArg.NpadStyleSet, out configuredCount, out primaryIndex)) while (!_system.Device.Hid.Npads.Validate(playerMin, playerMax, (ControllerType)privateArg.NpadStyleSet, out configuredCount, out primaryIndex))
{ {
ControllerAppletUiArgs uiArgs = new() ControllerAppletUIArgs uiArgs = new()
{ {
PlayerCountMin = playerMin, PlayerCountMin = playerMin,
PlayerCountMax = playerMax, PlayerCountMax = playerMax,
@@ -95,7 +95,7 @@ namespace Ryujinx.HLE.HOS.Applets
IsDocked = _system.State.DockedMode, IsDocked = _system.State.DockedMode,
}; };
if (!_system.Device.UiHandler.DisplayMessageDialog(uiArgs)) if (!_system.Device.UIHandler.DisplayMessageDialog(uiArgs))
{ {
break; break;
} }

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Applets namespace Ryujinx.HLE.HOS.Applets
{ {
public struct ControllerAppletUiArgs public struct ControllerAppletUIArgs
{ {
public int PlayerCountMin; public int PlayerCountMin;
public int PlayerCountMax; public int PlayerCountMax;

View File

@@ -166,13 +166,13 @@ namespace Ryujinx.HLE.HOS.Applets.Error
string[] buttons = GetButtonsText(module, description, "DlgBtn"); 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) if (showDetails)
{ {
message = GetMessageText(module, description, "FlvMsg"); message = GetMessageText(module, description, "FlvMsg");
buttons = GetButtonsText(module, description, "FlvBtn"); 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"); 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) if (showDetails)
{ {
buttons.RemoveAt(0); 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());
} }
} }

View File

@@ -1,5 +1,5 @@
using Ryujinx.HLE.HOS.Services.Am.AppletAE; using Ryujinx.HLE.HOS.Services.Am.AppletAE;
using Ryujinx.HLE.Ui; using Ryujinx.HLE.UI;
using Ryujinx.Memory; using Ryujinx.Memory;
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;

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