Compare commits
41 Commits
Author | SHA1 | Date | |
---|---|---|---|
af1906ea04 | |||
68848000f7 | |||
d98da47a0f | |||
306f7e93a0 | |||
8954ff3af2 | |||
d2f3adbf69 | |||
d511c845b7 | |||
21c9ac6240 | |||
81c9052847 | |||
9367e3c35d | |||
52cf141874 | |||
8a352df3c6 | |||
c545c59851 | |||
96ea4e8c8e | |||
b8f48bcf64 | |||
6966211e07 | |||
57524a4c8a | |||
f4539c49d8 | |||
12c62fdbc2 | |||
e3c6be5e29 | |||
4741a05df9 | |||
c6676007bf | |||
92b0b7d753 | |||
232237bf28 | |||
c27e453fd3 | |||
0e037d0213 | |||
0dca1fbe12 | |||
35d91a0e58 | |||
a73a5d7e85 | |||
832a5e8852 | |||
96d1f0da2d | |||
597388ecda | |||
1cf6d7b7bb | |||
7bc9d0cdad | |||
dc0dbc50ab | |||
994f4dc77d | |||
c9e297b74c | |||
dd514a115c | |||
7e0b4bd538 | |||
378080eb87 | |||
e8f5e97fa4 |
19
.github/ISSUE_TEMPLATE/missing_shader_instruction.yml
vendored
Normal file
19
.github/ISSUE_TEMPLATE/missing_shader_instruction.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
name: Missing Shader Instruction
|
||||
description: Shader Instruction is missing in Ryujinx.
|
||||
title: "[GPU]"
|
||||
labels: [gpu, not-implemented]
|
||||
body:
|
||||
- type: textarea
|
||||
id: instruction
|
||||
attributes:
|
||||
label: Shader instruction
|
||||
description: What shader instruction is missing?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: required
|
||||
attributes:
|
||||
label: Required by
|
||||
description: Add links to the [compatibility list page(s)](https://github.com/Ryujinx/Ryujinx-Games-List/issues) of the game(s) that require this instruction.
|
||||
validations:
|
||||
required: true
|
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
@ -18,6 +18,10 @@ on:
|
||||
- '*.yml'
|
||||
- 'README.md'
|
||||
|
||||
concurrency:
|
||||
group: pr-checks-${{ github.event.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
@ -27,7 +31,7 @@ jobs:
|
||||
build:
|
||||
name: ${{ matrix.OS_NAME }} (${{ matrix.configuration }})
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
||||
timeout-minutes: 45
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macOS-latest, windows-latest]
|
||||
@ -110,7 +114,7 @@ jobs:
|
||||
build_macos:
|
||||
name: macOS Universal (${{ matrix.configuration }})
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
||||
timeout-minutes: 45
|
||||
strategy:
|
||||
matrix:
|
||||
configuration: [ Debug, Release ]
|
||||
|
@ -3,17 +3,17 @@
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="Avalonia" Version="0.10.19" />
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.19" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="0.10.19" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.19" />
|
||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.19" />
|
||||
<PackageVersion Include="Avalonia" Version="0.10.21" />
|
||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.21" />
|
||||
<PackageVersion Include="Avalonia.Desktop" Version="0.10.21" />
|
||||
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.21" />
|
||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.21" />
|
||||
<PackageVersion Include="Avalonia.Svg" Version="0.10.18" />
|
||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" />
|
||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||
<PackageVersion Include="Concentus" Version="1.1.7" />
|
||||
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
|
||||
<PackageVersion Include="DynamicData" Version="7.13.8" />
|
||||
<PackageVersion Include="DynamicData" Version="7.14.2" />
|
||||
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
|
||||
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
||||
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
||||
@ -21,7 +21,7 @@
|
||||
<PackageVersion Include="LibHac" Version="0.18.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.1" />
|
||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
|
||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||
<PackageVersion Include="NUnit" Version="3.13.3" />
|
||||
|
@ -11,4 +11,10 @@ if [ -f "$SCRIPT_DIR/Ryujinx.Headless.SDL2" ]; then
|
||||
RYUJINX_BIN="Ryujinx.Headless.SDL2"
|
||||
fi
|
||||
|
||||
env DOTNET_EnableAlternateStackCheck=1 "$SCRIPT_DIR/$RYUJINX_BIN" "$@"
|
||||
COMMAND="env DOTNET_EnableAlternateStackCheck=1"
|
||||
|
||||
if command -v gamemoderun > /dev/null 2>&1; then
|
||||
COMMAND="$COMMAND gamemoderun"
|
||||
fi
|
||||
|
||||
$COMMAND "$SCRIPT_DIR/$RYUJINX_BIN" "$@"
|
@ -25,14 +25,27 @@ error_handler() {
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Wait for Ryujinx to exit
|
||||
# NOTE: in case no fds are open, lsof could be returning with a process still living.
|
||||
# We wait 1s and assume the process stopped after that
|
||||
lsof -p $APP_PID +r 1 &>/dev/null
|
||||
sleep 1
|
||||
|
||||
trap 'error_handler ${LINENO}' ERR
|
||||
|
||||
# Wait for Ryujinx to exit.
|
||||
# If the main process is still acitve, we wait for 1 second and check it again.
|
||||
# After the fifth time checking, this script exits with status 1.
|
||||
|
||||
attempt=0
|
||||
while true; do
|
||||
if lsof -p $APP_PID +r 1 &>/dev/null || ps -p "$APP_PID" &>/dev/null; then
|
||||
if [ "$attempt" -eq 4 ]; then
|
||||
exit 1
|
||||
fi
|
||||
sleep 1
|
||||
else
|
||||
break
|
||||
fi
|
||||
(( attempt++ ))
|
||||
done
|
||||
|
||||
sleep 1
|
||||
|
||||
# Now replace and reopen.
|
||||
rm -rf "$INSTALL_DIRECTORY"
|
||||
mv "$NEW_APP_DIRECTORY" "$INSTALL_DIRECTORY"
|
||||
|
@ -6,10 +6,11 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Versioning;
|
||||
|
||||
namespace ARMeilleure.Translation.Cache
|
||||
{
|
||||
static class JitCache
|
||||
static partial class JitCache
|
||||
{
|
||||
private const int PageSize = 4 * 1024;
|
||||
private const int PageMask = PageSize - 1;
|
||||
@ -27,6 +28,10 @@ namespace ARMeilleure.Translation.Cache
|
||||
private static readonly object _lock = new object();
|
||||
private static bool _initialized;
|
||||
|
||||
[SupportedOSPlatform("windows")]
|
||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||
public static partial IntPtr FlushInstructionCache(IntPtr hProcess, IntPtr lpAddress, UIntPtr dwSize);
|
||||
|
||||
public static void Initialize(IJitMemoryAllocator allocator)
|
||||
{
|
||||
if (_initialized) return;
|
||||
@ -36,7 +41,11 @@ namespace ARMeilleure.Translation.Cache
|
||||
if (_initialized) return;
|
||||
|
||||
_jitRegion = new ReservedRegion(allocator, CacheSize);
|
||||
_jitCacheInvalidator = new JitCacheInvalidation(allocator);
|
||||
|
||||
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
||||
{
|
||||
_jitCacheInvalidator = new JitCacheInvalidation(allocator);
|
||||
}
|
||||
|
||||
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
|
||||
|
||||
@ -77,7 +86,14 @@ namespace ARMeilleure.Translation.Cache
|
||||
Marshal.Copy(code, 0, funcPtr, code.Length);
|
||||
ReprotectAsExecutable(funcOffset, code.Length);
|
||||
|
||||
_jitCacheInvalidator.Invalidate(funcPtr, (ulong)code.Length);
|
||||
if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||
{
|
||||
FlushInstructionCache(Process.GetCurrentProcess().Handle, funcPtr, (UIntPtr)code.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
_jitCacheInvalidator?.Invalidate(funcPtr, (ulong)code.Length);
|
||||
}
|
||||
}
|
||||
|
||||
Add(funcOffset, code.Length, func.UnwindInfo);
|
||||
|
@ -47,8 +47,8 @@ namespace ARMeilleure.Translation.Cache
|
||||
|
||||
public JitCacheInvalidation(IJitMemoryAllocator allocator)
|
||||
{
|
||||
// On macOS, a different path is used to write to the JIT cache, which does the invalidation.
|
||||
if (!OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||
// On macOS and Windows, a different path is used to write to the JIT cache, which does the invalidation.
|
||||
if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||
{
|
||||
ulong size = (ulong)_invalidationCode.Length * sizeof(int);
|
||||
ulong mask = (ulong)ReservedRegion.DefaultGranularity - 1;
|
||||
|
@ -45,7 +45,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
||||
}
|
||||
else
|
||||
{
|
||||
_supportSurroundConfiguration = spec.channels == 6;
|
||||
_supportSurroundConfiguration = spec.channels >= 6;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,6 +92,8 @@ namespace Ryujinx.Ava
|
||||
private bool _isActive;
|
||||
private bool _renderingStarted;
|
||||
|
||||
private ManualResetEvent _gpuDoneEvent;
|
||||
|
||||
private IRenderer _renderer;
|
||||
private readonly Thread _renderingThread;
|
||||
private readonly CancellationTokenSource _gpuCancellationTokenSource;
|
||||
@ -183,6 +185,7 @@ namespace Ryujinx.Ava
|
||||
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState;
|
||||
|
||||
_gpuCancellationTokenSource = new CancellationTokenSource();
|
||||
_gpuDoneEvent = new ManualResetEvent(false);
|
||||
}
|
||||
|
||||
private void TopLevel_PointerEnterOrMoved(object sender, PointerEventArgs e)
|
||||
@ -270,7 +273,7 @@ namespace Ryujinx.Ava
|
||||
|
||||
string directory = AppDataManager.Mode switch
|
||||
{
|
||||
AppDataManager.LaunchMode.Portable => Path.Combine(AppDataManager.BaseDirPath, "screenshots"),
|
||||
AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => Path.Combine(AppDataManager.BaseDirPath, "screenshots"),
|
||||
_ => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx")
|
||||
};
|
||||
|
||||
@ -423,10 +426,10 @@ namespace Ryujinx.Ava
|
||||
|
||||
_isActive = false;
|
||||
|
||||
if (_renderingThread.IsAlive)
|
||||
{
|
||||
_renderingThread.Join();
|
||||
}
|
||||
// NOTE: The render loop is allowed to stay alive until the renderer itself is disposed, as it may handle resource dispose.
|
||||
// We only need to wait for all commands submitted during the main gpu loop to be processed.
|
||||
_gpuDoneEvent.WaitOne();
|
||||
_gpuDoneEvent.Dispose();
|
||||
|
||||
DisplaySleep.Restore();
|
||||
|
||||
@ -917,6 +920,14 @@ namespace Ryujinx.Ava
|
||||
UpdateStatus();
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure all commands in the run loop are fully executed before leaving the loop.
|
||||
if (Device.Gpu.Renderer is ThreadedRenderer threaded)
|
||||
{
|
||||
threaded.FlushThreadedCommands();
|
||||
}
|
||||
|
||||
_gpuDoneEvent.Set();
|
||||
});
|
||||
|
||||
(_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(null);
|
||||
|
@ -74,6 +74,13 @@
|
||||
"GameListContextMenuExtractDataLogoToolTip": "Extract the Logo section from Application's current config (including updates)",
|
||||
"StatusBarGamesLoaded": "{0}/{1} Games Loaded",
|
||||
"StatusBarSystemVersion": "System Version: {0}",
|
||||
"LinuxVmMaxMapCountDialogTitle": "Low limit for memory mappings detected",
|
||||
"LinuxVmMaxMapCountDialogTextPrimary": "Would you like to increase the value of vm.max_map_count to {0}",
|
||||
"LinuxVmMaxMapCountDialogTextSecondary": "Some games might try to create more memory mappings than currently allowed. Ryujinx will crash as soon as this limit gets exceeded.",
|
||||
"LinuxVmMaxMapCountDialogButtonUntilRestart": "Yes, until the next restart",
|
||||
"LinuxVmMaxMapCountDialogButtonPersistent": "Yes, permanently",
|
||||
"LinuxVmMaxMapCountWarningTextPrimary": "Max amount of memory mappings is lower than recommended.",
|
||||
"LinuxVmMaxMapCountWarningTextSecondary": "The current value of vm.max_map_count ({0}) is lower than {1}. Some games might try to create more memory mappings than currently allowed. Ryujinx will crash as soon as this limit gets exceeded.\n\nYou might want to either manually increase the limit or install pkexec, which allows Ryujinx to assist with that.",
|
||||
"Settings": "Settings",
|
||||
"SettingsTabGeneral": "User Interface",
|
||||
"SettingsTabGeneralGeneral": "General",
|
||||
@ -282,6 +289,7 @@
|
||||
"ControllerSettingsSaveProfileToolTip": "Save Profile",
|
||||
"MenuBarFileToolsTakeScreenshot": "Take Screenshot",
|
||||
"MenuBarFileToolsHideUi": "Hide UI",
|
||||
"GameListContextMenuRunApplication": "Run Application",
|
||||
"GameListContextMenuToggleFavorite": "Toggle Favorite",
|
||||
"GameListContextMenuToggleFavoriteToolTip": "Toggle Favorite status of Game",
|
||||
"SettingsTabGeneralTheme": "Theme",
|
||||
@ -536,6 +544,9 @@
|
||||
"SwkbdMinCharacters": "Must be at least {0} characters long",
|
||||
"SwkbdMinRangeCharacters": "Must be {0}-{1} characters long",
|
||||
"SoftwareKeyboard": "Software Keyboard",
|
||||
"SoftwareKeyboardModeNumbersOnly": "Must be numbers only",
|
||||
"SoftwareKeyboardModeAlphabet": "Must be alphabets only",
|
||||
"SoftwareKeyboardModeASCII": "Must be ASCII text only",
|
||||
"DialogControllerAppletMessagePlayerRange": "Application requests {0} player(s) with:\n\nTYPES: {1}\n\nPLAYERS: {2}\n\n{3}Please open Settings and reconfigure Input now or press Close.",
|
||||
"DialogControllerAppletMessage": "Application requests exactly {0} player(s) with:\n\nTYPES: {1}\n\nPLAYERS: {2}\n\n{3}Please open Settings and reconfigure Input now or press Close.",
|
||||
"DialogControllerAppletDockModeSet": "Docked mode set. Handheld is also invalid.\n\n",
|
||||
@ -620,7 +631,7 @@
|
||||
"Search": "Search",
|
||||
"UserProfilesRecoverLostAccounts": "Recover Lost Accounts",
|
||||
"Recover": "Recover",
|
||||
"UserProfilesRecoverHeading" : "Saves were found for the following accounts",
|
||||
"UserProfilesRecoverHeading": "Saves were found for the following accounts",
|
||||
"UserProfilesRecoverEmptyList": "No profiles to recover",
|
||||
"GraphicsAATooltip": "Applies anti-aliasing to the game render",
|
||||
"GraphicsAALabel": "Anti-Aliasing:",
|
||||
@ -632,10 +643,12 @@
|
||||
"SmaaMedium": "SMAA Medium",
|
||||
"SmaaHigh": "SMAA High",
|
||||
"SmaaUltra": "SMAA Ultra",
|
||||
"UserEditorTitle" : "Edit User",
|
||||
"UserEditorTitleCreate" : "Create User",
|
||||
"UserEditorTitle": "Edit User",
|
||||
"UserEditorTitleCreate": "Create User",
|
||||
"SettingsTabNetworkInterface": "Network Interface:",
|
||||
"NetworkInterfaceTooltip": "The network interface used for LAN features",
|
||||
"NetworkInterfaceDefault": "Default",
|
||||
"PackagingShaders": "Packaging Shaders"
|
||||
"PackagingShaders": "Packaging Shaders",
|
||||
"AboutChangelogButton": "View Changelog on GitHub",
|
||||
"AboutChangelogButtonTooltipMessage": "Click to open the changelog for this version in your default browser."
|
||||
}
|
@ -527,6 +527,9 @@
|
||||
"SwkbdMinCharacters": "至少应为 {0} 个字长",
|
||||
"SwkbdMinRangeCharacters": "必须为 {0}-{1} 个字长",
|
||||
"SoftwareKeyboard": "软件键盘",
|
||||
"SoftwareKeyboardModeNumbersOnly": "只接受数字",
|
||||
"SoftwareKeyboardModeAlphabet": "只接受英文字母",
|
||||
"SoftwareKeyboardModeASCII": "只接受 ASCII 符号",
|
||||
"DialogControllerAppletMessagePlayerRange": "游戏需要 {0} 个玩家并满足以下要求:\n\n手柄类型:{1}\n\n玩家类型:{2}\n\n{3}请打开设置窗口,重新配置手柄输入;或者关闭返回。",
|
||||
"DialogControllerAppletMessage": "游戏需要刚好 {0} 个玩家并满足以下要求:\n\n手柄类型:{1}\n\n玩家类型:{2}\n\n{3}请打开设置窗口,重新配置手柄输入;或者关闭返回。",
|
||||
"DialogControllerAppletDockModeSet": "目前处于主机模式,无法使用掌机操作方式",
|
||||
|
@ -527,6 +527,9 @@
|
||||
"SwkbdMinCharacters": "至少應為 {0} 個字長",
|
||||
"SwkbdMinRangeCharacters": "必須為 {0}-{1} 個字長",
|
||||
"SoftwareKeyboard": "軟體鍵盤",
|
||||
"SoftwareKeyboardModeNumbersOnly": "只接受數字",
|
||||
"SoftwareKeyboardModeAlphabet": "只接受英文字母",
|
||||
"SoftwareKeyboardModeASCII": "只接受 ASCII 符號",
|
||||
"DialogControllerAppletMessagePlayerRange": "本遊戲需要 {0} 個玩家持有:\n\n類型:{1}\n\n玩家:{2}\n\n{3}請打開設定畫面,配置手把,或者關閉本視窗。",
|
||||
"DialogControllerAppletMessage": "本遊戲需要剛好 {0} 個玩家持有:\n\n類型:{1}\n\n玩家:{2}\n\n{3}請打開設定畫面,配置手把,或者關閉本視窗。",
|
||||
"DialogControllerAppletDockModeSet": "現在處於主機模式,無法使用掌機操作方式\n\n",
|
||||
|
@ -1,7 +1,6 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using FluentAvalonia.Core;
|
||||
using Ryujinx.Input;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
@ -30,14 +29,14 @@ namespace Ryujinx.Ava.Input
|
||||
_window = window;
|
||||
|
||||
_widget.PointerMoved += Parent_PointerMovedEvent;
|
||||
_widget.PointerPressed += Parent_PointerPressEvent;
|
||||
_widget.PointerReleased += Parent_PointerReleaseEvent;
|
||||
_widget.PointerWheelChanged += Parent_ScrollEvent;
|
||||
_widget.PointerPressed += Parent_PointerPressedEvent;
|
||||
_widget.PointerReleased += Parent_PointerReleasedEvent;
|
||||
_widget.PointerWheelChanged += Parent_PointerWheelChanged;
|
||||
|
||||
_window.PointerMoved += Parent_PointerMovedEvent;
|
||||
_window.PointerPressed += Parent_PointerPressEvent;
|
||||
_window.PointerReleased += Parent_PointerReleaseEvent;
|
||||
_window.PointerWheelChanged += Parent_ScrollEvent;
|
||||
_window.PointerPressed += Parent_PointerPressedEvent;
|
||||
_window.PointerReleased += Parent_PointerReleasedEvent;
|
||||
_window.PointerWheelChanged += Parent_PointerWheelChanged;
|
||||
|
||||
PressedButtons = new bool[(int)MouseButton.Count];
|
||||
|
||||
@ -63,29 +62,25 @@ namespace Ryujinx.Ava.Input
|
||||
_size = new Size((int)rect.Width, (int)rect.Height);
|
||||
}
|
||||
|
||||
private void Parent_ScrollEvent(object o, PointerWheelEventArgs args)
|
||||
private void Parent_PointerWheelChanged(object o, PointerWheelEventArgs args)
|
||||
{
|
||||
Scroll = new Vector2((float)args.Delta.X, (float)args.Delta.Y);
|
||||
}
|
||||
|
||||
private void Parent_PointerReleaseEvent(object o, PointerReleasedEventArgs args)
|
||||
private void Parent_PointerReleasedEvent(object o, PointerReleasedEventArgs args)
|
||||
{
|
||||
if (args.InitialPressMouseButton != Avalonia.Input.MouseButton.None)
|
||||
{
|
||||
int button = (int)args.InitialPressMouseButton;
|
||||
uint button = (uint)args.InitialPressMouseButton - 1;
|
||||
|
||||
if (PressedButtons.Count() >= button)
|
||||
{
|
||||
PressedButtons[button] = false;
|
||||
}
|
||||
if ((uint)PressedButtons.Length > button)
|
||||
{
|
||||
PressedButtons[button] = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void Parent_PointerPressEvent(object o, PointerPressedEventArgs args)
|
||||
private void Parent_PointerPressedEvent(object o, PointerPressedEventArgs args)
|
||||
{
|
||||
int button = (int)args.GetCurrentPoint(_widget).Properties.PointerUpdateKind;
|
||||
uint button = (uint)args.GetCurrentPoint(_widget).Properties.PointerUpdateKind;
|
||||
|
||||
if (PressedButtons.Count() >= button)
|
||||
if ((uint)PressedButtons.Length > button)
|
||||
{
|
||||
PressedButtons[button] = true;
|
||||
}
|
||||
@ -100,17 +95,17 @@ namespace Ryujinx.Ava.Input
|
||||
|
||||
public void SetMousePressed(MouseButton button)
|
||||
{
|
||||
if (PressedButtons.Count() >= (int)button)
|
||||
if ((uint)PressedButtons.Length > (uint)button)
|
||||
{
|
||||
PressedButtons[(int)button] = true;
|
||||
PressedButtons[(uint)button] = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetMouseReleased(MouseButton button)
|
||||
{
|
||||
if (PressedButtons.Count() >= (int)button)
|
||||
if ((uint)PressedButtons.Length > (uint)button)
|
||||
{
|
||||
PressedButtons[(int)button] = false;
|
||||
PressedButtons[(uint)button] = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -121,9 +116,9 @@ namespace Ryujinx.Ava.Input
|
||||
|
||||
public bool IsButtonPressed(MouseButton button)
|
||||
{
|
||||
if (PressedButtons.Count() >= (int)button)
|
||||
if ((uint)PressedButtons.Length > (uint)button)
|
||||
{
|
||||
return PressedButtons[(int)button];
|
||||
return PressedButtons[(uint)button];
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -149,14 +144,14 @@ namespace Ryujinx.Ava.Input
|
||||
_isDisposed = true;
|
||||
|
||||
_widget.PointerMoved -= Parent_PointerMovedEvent;
|
||||
_widget.PointerPressed -= Parent_PointerPressEvent;
|
||||
_widget.PointerReleased -= Parent_PointerReleaseEvent;
|
||||
_widget.PointerWheelChanged -= Parent_ScrollEvent;
|
||||
_widget.PointerPressed -= Parent_PointerPressedEvent;
|
||||
_widget.PointerReleased -= Parent_PointerReleasedEvent;
|
||||
_widget.PointerWheelChanged -= Parent_PointerWheelChanged;
|
||||
|
||||
_window.PointerMoved -= Parent_PointerMovedEvent;
|
||||
_window.PointerPressed -= Parent_PointerPressEvent;
|
||||
_window.PointerReleased -= Parent_PointerReleaseEvent;
|
||||
_window.PointerWheelChanged -= Parent_ScrollEvent;
|
||||
_window.PointerPressed -= Parent_PointerPressedEvent;
|
||||
_window.PointerReleased -= Parent_PointerReleasedEvent;
|
||||
_window.PointerWheelChanged -= Parent_PointerWheelChanged;
|
||||
|
||||
_widget = null;
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
|
||||
<PropertyGroup Condition="'$(RuntimeIdentifier)' != ''">
|
||||
<PublishSingleFile>true</PublishSingleFile>
|
||||
<TrimmerSingleWarn>false</TrimmerSingleWarn>
|
||||
<PublishTrimmed>true</PublishTrimmed>
|
||||
<TrimMode>partial</TrimMode>
|
||||
</PropertyGroup>
|
||||
@ -147,4 +148,4 @@
|
||||
<ItemGroup>
|
||||
<AdditionalFiles Include="Assets\Locales\en_US.json" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
</Project>
|
@ -9,14 +9,17 @@ using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Helpers;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.HLE.HOS.Applets;
|
||||
using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
internal partial class SwkbdAppletDialog : UserControl
|
||||
{
|
||||
private Predicate<int> _checkLength;
|
||||
private Predicate<int> _checkLength = _ => true;
|
||||
private Predicate<string> _checkInput = _ => true;
|
||||
private int _inputMax;
|
||||
private int _inputMin;
|
||||
private string _placeholder;
|
||||
@ -35,8 +38,6 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
Input.Watermark = _placeholder;
|
||||
|
||||
Input.AddHandler(TextInputEvent, Message_TextInput, RoutingStrategies.Tunnel, true);
|
||||
|
||||
SetInputLengthValidation(0, int.MaxValue); // Disable by default.
|
||||
}
|
||||
|
||||
public SwkbdAppletDialog()
|
||||
@ -67,6 +68,7 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
string input = string.Empty;
|
||||
|
||||
content.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax);
|
||||
content.SetInputValidation(args.KeyboardMode);
|
||||
|
||||
content._host = contentDialog;
|
||||
contentDialog.Title = title;
|
||||
@ -91,6 +93,12 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
return (result, input);
|
||||
}
|
||||
|
||||
private void ApplyValidationInfo(string text)
|
||||
{
|
||||
Error.IsVisible = !string.IsNullOrEmpty(text);
|
||||
Error.Text = text;
|
||||
}
|
||||
|
||||
public void SetInputLengthValidation(int min, int max)
|
||||
{
|
||||
_inputMin = Math.Min(min, max);
|
||||
@ -99,6 +107,8 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
Error.IsVisible = false;
|
||||
Error.FontStyle = FontStyle.Italic;
|
||||
|
||||
string validationInfoText = "";
|
||||
|
||||
if (_inputMin <= 0 && _inputMax == int.MaxValue) // Disable.
|
||||
{
|
||||
Error.IsVisible = false;
|
||||
@ -107,21 +117,48 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
}
|
||||
else if (_inputMin > 0 && _inputMax == int.MaxValue)
|
||||
{
|
||||
Error.IsVisible = true;
|
||||
|
||||
Error.Text = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinCharacters, _inputMin);
|
||||
validationInfoText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinCharacters, _inputMin);
|
||||
|
||||
_checkLength = length => _inputMin <= length;
|
||||
}
|
||||
else
|
||||
{
|
||||
Error.IsVisible = true;
|
||||
|
||||
Error.Text = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinRangeCharacters, _inputMin, _inputMax);
|
||||
validationInfoText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinRangeCharacters, _inputMin, _inputMax);
|
||||
|
||||
_checkLength = length => _inputMin <= length && length <= _inputMax;
|
||||
}
|
||||
|
||||
ApplyValidationInfo(validationInfoText);
|
||||
Message_TextInput(this, new TextInputEventArgs());
|
||||
}
|
||||
|
||||
private void SetInputValidation(KeyboardMode mode)
|
||||
{
|
||||
string validationInfoText = Error.Text;
|
||||
string localeText;
|
||||
switch (mode)
|
||||
{
|
||||
case KeyboardMode.NumbersOnly:
|
||||
localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeNumbersOnly);
|
||||
validationInfoText = string.IsNullOrEmpty(validationInfoText) ? localeText : string.Join("\n", validationInfoText, localeText);
|
||||
_checkInput = text => text.All(char.IsDigit);
|
||||
break;
|
||||
case KeyboardMode.Alphabet:
|
||||
localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeAlphabet);
|
||||
validationInfoText = string.IsNullOrEmpty(validationInfoText) ? localeText : string.Join("\n", validationInfoText, localeText);
|
||||
_checkInput = text => text.All(char.IsAsciiLetter);
|
||||
break;
|
||||
case KeyboardMode.ASCII:
|
||||
localeText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SoftwareKeyboardModeASCII);
|
||||
validationInfoText = string.IsNullOrEmpty(validationInfoText) ? localeText : string.Join("\n", validationInfoText, localeText);
|
||||
_checkInput = text => text.All(char.IsAscii);
|
||||
break;
|
||||
default:
|
||||
_checkInput = _ => true;
|
||||
break;
|
||||
}
|
||||
|
||||
ApplyValidationInfo(validationInfoText);
|
||||
Message_TextInput(this, new TextInputEventArgs());
|
||||
}
|
||||
|
||||
@ -129,7 +166,7 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
{
|
||||
if (_host != null)
|
||||
{
|
||||
_host.IsPrimaryButtonEnabled = _checkLength(Message.Length);
|
||||
_host.IsPrimaryButtonEnabled = _checkLength(Message.Length) && _checkInput(Message);
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,7 +178,7 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
}
|
||||
else
|
||||
{
|
||||
_host.IsPrimaryButtonEnabled = _checkLength(Message.Length);
|
||||
_host.IsPrimaryButtonEnabled = _checkLength(Message.Length) && _checkInput(Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,9 @@
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale">
|
||||
<MenuItem
|
||||
Click="RunApplication_Click"
|
||||
Header="{locale:Locale GameListContextMenuRunApplication}" />
|
||||
<MenuItem
|
||||
Click="ToggleFavorite_Click"
|
||||
Header="{locale:Locale GameListContextMenuToggleFavorite}"
|
||||
|
@ -323,5 +323,15 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
await ApplicationHelper.ExtractSection(NcaSectionType.Logo, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName);
|
||||
}
|
||||
}
|
||||
|
||||
public void RunApplication_Click(object sender, RoutedEventArgs args)
|
||||
{
|
||||
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
|
||||
|
||||
if (viewModel?.SelectedApplication != null)
|
||||
{
|
||||
viewModel.LoadApplication(viewModel.SelectedApplication.Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -32,10 +32,10 @@
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<flex:FlexPanel
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Stretch"
|
||||
AlignContent="FlexStart"
|
||||
JustifyContent="Center" />
|
||||
JustifyContent="FlexStart" />
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
<ListBox.Styles>
|
||||
|
@ -21,6 +21,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
if (value is byte[] buffer && targetType == typeof(IImage))
|
||||
{
|
||||
MemoryStream mem = new(buffer);
|
||||
|
||||
return new Bitmap(mem);
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,6 @@ using Avalonia.Threading;
|
||||
using FluentAvalonia.Core;
|
||||
using FluentAvalonia.UI.Controls;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Ava.UI.Controls;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
@ -19,7 +18,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
{
|
||||
private static bool _isChoiceDialogOpen;
|
||||
|
||||
public async static Task<UserResult> ShowContentDialog(
|
||||
private async static Task<UserResult> ShowContentDialog(
|
||||
string title,
|
||||
object content,
|
||||
string primaryButton,
|
||||
@ -67,7 +66,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
return result;
|
||||
}
|
||||
|
||||
private async static Task<UserResult> ShowTextDialog(
|
||||
public async static Task<UserResult> ShowTextDialog(
|
||||
string title,
|
||||
string primaryText,
|
||||
string secondaryText,
|
||||
@ -319,7 +318,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
|
||||
Window parent = GetMainWindow();
|
||||
|
||||
if (parent != null && parent.IsActive && parent is MainWindow window && window.ViewModel.IsGameRunning)
|
||||
if (parent != null && parent.IsActive && (parent as MainWindow).ViewModel.IsGameRunning)
|
||||
{
|
||||
contentDialogOverlayWindow = new()
|
||||
{
|
||||
|
@ -257,6 +257,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
OnPropertyChanged();
|
||||
OnPropertyChanged(nameof(EnableNonGameRunningControls));
|
||||
OnPropertyChanged(nameof(IsAppletMenuActive));
|
||||
OnPropertyChanged(nameof(StatusBarVisible));
|
||||
OnPropertyChanged(nameof(ShowFirmwareStatus));
|
||||
}
|
||||
@ -1529,6 +1530,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
double sessionTimePlayed = DateTime.UtcNow.Subtract(appMetadata.LastPlayed.Value).TotalSeconds;
|
||||
appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero);
|
||||
}
|
||||
|
||||
appMetadata.LastPlayed = DateTime.UtcNow;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -9,9 +9,7 @@ using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.Modules;
|
||||
using Ryujinx.Ui.App.Common;
|
||||
using Ryujinx.Ui.Common;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
|
@ -16,6 +16,7 @@
|
||||
</Design.DataContext>
|
||||
<DockPanel
|
||||
Margin="0,0,0,5"
|
||||
Height="35"
|
||||
HorizontalAlignment="Stretch">
|
||||
<Button
|
||||
Width="40"
|
||||
|
@ -58,11 +58,20 @@
|
||||
JustifyContent="SpaceAround"
|
||||
RowSpacing="2">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="28"
|
||||
FontWeight="Bold"
|
||||
Text="Ryujinx"
|
||||
TextAlignment="Left" />
|
||||
<TextBlock Text="(REE-YOU-JINX)" TextAlignment="Left" />
|
||||
TextAlignment="Center"
|
||||
Width="100" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="11"
|
||||
Text="(REE-YOU-JINX)"
|
||||
TextAlignment="Center"
|
||||
Width="100" />
|
||||
</flex:FlexPanel>
|
||||
</Grid>
|
||||
<TextBlock
|
||||
@ -72,6 +81,18 @@
|
||||
LineHeight="12"
|
||||
Text="{Binding Version}"
|
||||
TextAlignment="Center" />
|
||||
<Button
|
||||
Padding="5"
|
||||
HorizontalAlignment="Center"
|
||||
Background="Transparent"
|
||||
Click="Button_OnClick"
|
||||
Tag="https://github.com/Ryujinx/Ryujinx/wiki/Changelog#ryujinx-changelog">
|
||||
<TextBlock
|
||||
FontSize="10"
|
||||
Text="{locale:Locale AboutChangelogButton}"
|
||||
TextAlignment="Center"
|
||||
ToolTip.Tip="{locale:Locale AboutChangelogButtonTooltipMessage}" />
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Grid.Row="2"
|
||||
|
@ -23,6 +23,7 @@ using Ryujinx.Ui.Common.Helper;
|
||||
using System;
|
||||
using System.ComponentModel;
|
||||
using System.IO;
|
||||
using System.Runtime.Versioning;
|
||||
using System.Threading.Tasks;
|
||||
using InputManager = Ryujinx.Input.HLE.InputManager;
|
||||
|
||||
@ -258,7 +259,64 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
ApplicationHelper.Initialize(VirtualFileSystem, AccountManager, LibHacHorizonManager.RyujinxClient, this);
|
||||
}
|
||||
|
||||
protected void CheckLaunchState()
|
||||
[SupportedOSPlatform("linux")]
|
||||
private static async void ShowVmMaxMapCountWarning()
|
||||
{
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LinuxVmMaxMapCountWarningTextSecondary,
|
||||
LinuxHelper.VmMaxMapCount, LinuxHelper.RecommendedVmMaxMapCount);
|
||||
|
||||
await ContentDialogHelper.CreateWarningDialog(
|
||||
LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountWarningTextPrimary],
|
||||
LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountWarningTextSecondary]
|
||||
);
|
||||
}
|
||||
|
||||
[SupportedOSPlatform("linux")]
|
||||
private static async void ShowVmMaxMapCountDialog()
|
||||
{
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LinuxVmMaxMapCountDialogTextPrimary,
|
||||
LinuxHelper.RecommendedVmMaxMapCount);
|
||||
|
||||
UserResult response = await ContentDialogHelper.ShowTextDialog(
|
||||
$"Ryujinx - {LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogTitle]}",
|
||||
LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogTextPrimary],
|
||||
LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogTextSecondary],
|
||||
LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogButtonUntilRestart],
|
||||
LocaleManager.Instance[LocaleKeys.LinuxVmMaxMapCountDialogButtonPersistent],
|
||||
LocaleManager.Instance[LocaleKeys.InputDialogNo],
|
||||
(int)Symbol.Help
|
||||
);
|
||||
|
||||
int rc;
|
||||
|
||||
switch (response)
|
||||
{
|
||||
case UserResult.Ok:
|
||||
rc = LinuxHelper.RunPkExec($"echo {LinuxHelper.RecommendedVmMaxMapCount} > {LinuxHelper.VmMaxMapCountPath}");
|
||||
if (rc == 0)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Application, $"vm.max_map_count set to {LinuxHelper.VmMaxMapCount} until the next restart.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Unable to change vm.max_map_count. Process exited with code: {rc}");
|
||||
}
|
||||
break;
|
||||
case UserResult.No:
|
||||
rc = LinuxHelper.RunPkExec($"echo \"vm.max_map_count = {LinuxHelper.RecommendedVmMaxMapCount}\" > {LinuxHelper.SysCtlConfigPath} && sysctl -p {LinuxHelper.SysCtlConfigPath}");
|
||||
if (rc == 0)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Application, $"vm.max_map_count set to {LinuxHelper.VmMaxMapCount}. Written to config: {LinuxHelper.SysCtlConfigPath}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application, $"Unable to write new value for vm.max_map_count to config. Process exited with code: {rc}");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void CheckLaunchState()
|
||||
{
|
||||
if (ShowKeyErrorOnLoad)
|
||||
{
|
||||
@ -268,6 +326,20 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys, this));
|
||||
}
|
||||
|
||||
if (OperatingSystem.IsLinux() && LinuxHelper.VmMaxMapCount < LinuxHelper.RecommendedVmMaxMapCount)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"The value of vm.max_map_count is lower than {LinuxHelper.RecommendedVmMaxMapCount}. ({LinuxHelper.VmMaxMapCount})");
|
||||
|
||||
if (LinuxHelper.PkExecPath is not null)
|
||||
{
|
||||
Dispatcher.UIThread.Post(ShowVmMaxMapCountDialog);
|
||||
}
|
||||
else
|
||||
{
|
||||
Dispatcher.UIThread.Post(ShowVmMaxMapCountWarning);
|
||||
}
|
||||
}
|
||||
|
||||
if (_deferLoad)
|
||||
{
|
||||
_deferLoad = false;
|
||||
@ -447,14 +519,14 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
private void ConfirmExit()
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
ViewModel.IsClosing = await ContentDialogHelper.CreateExitDialog();
|
||||
{
|
||||
ViewModel.IsClosing = await ContentDialogHelper.CreateExitDialog();
|
||||
|
||||
if (ViewModel.IsClosing)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
});
|
||||
if (ViewModel.IsClosing)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async void LoadApplications()
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System.Text;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.Common.Logging
|
||||
namespace Ryujinx.Common.Logging.Formatters
|
||||
{
|
||||
internal class DefaultLogFormatter : ILogFormatter
|
||||
{
|
||||
@ -27,6 +28,14 @@ namespace Ryujinx.Common.Logging
|
||||
|
||||
if (args.Data is not null)
|
||||
{
|
||||
if (args.Data is StackTrace trace)
|
||||
{
|
||||
sb.Append('\n');
|
||||
sb.Append(trace);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
sb.Append(' ');
|
||||
DynamicObjectFormatter.Format(sb, args.Data);
|
||||
}
|
||||
@ -39,4 +48,4 @@ namespace Ryujinx.Common.Logging
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,9 +3,9 @@ using System;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace Ryujinx.Common.Logging
|
||||
namespace Ryujinx.Common.Logging.Formatters
|
||||
{
|
||||
internal class DynamicObjectFormatter
|
||||
internal static class DynamicObjectFormatter
|
||||
{
|
||||
private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>();
|
||||
|
||||
@ -17,7 +17,7 @@ namespace Ryujinx.Common.Logging
|
||||
}
|
||||
|
||||
StringBuilder sb = StringBuilderPool.Allocate();
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
Format(sb, dynamicObject);
|
||||
|
@ -1,7 +1,7 @@
|
||||
namespace Ryujinx.Common.Logging
|
||||
namespace Ryujinx.Common.Logging.Formatters
|
||||
{
|
||||
interface ILogFormatter
|
||||
{
|
||||
string Format(LogEventArgs args);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Ryujinx.Common.Logging.Formatters;
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Logging
|
||||
|
@ -1,3 +1,4 @@
|
||||
using Ryujinx.Common.Logging.Targets;
|
||||
using Ryujinx.Common.SystemInterop;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@ -55,6 +56,16 @@ namespace Ryujinx.Common.Logging
|
||||
}
|
||||
}
|
||||
|
||||
[StackTraceHidden]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void PrintStack(LogClass logClass, string message, [CallerMemberName] string caller = "")
|
||||
{
|
||||
if (m_EnabledClasses[(int)logClass])
|
||||
{
|
||||
Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, message), new StackTrace(true)));
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void PrintStub(LogClass logClass, string message = "", [CallerMemberName] string caller = "")
|
||||
{
|
||||
@ -122,7 +133,7 @@ namespace Ryujinx.Common.Logging
|
||||
AsyncLogTargetOverflowAction.Discard));
|
||||
|
||||
Notice = new Log(LogLevel.Notice);
|
||||
|
||||
|
||||
// Enable important log levels before configuration is loaded
|
||||
Error = new Log(LogLevel.Error);
|
||||
Warning = new Log(LogLevel.Warning);
|
||||
@ -221,4 +232,4 @@ namespace Ryujinx.Common.Logging
|
||||
m_EnabledClasses[(int)logClass] = enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Common.Logging
|
||||
namespace Ryujinx.Common.Logging.Targets
|
||||
{
|
||||
public enum AsyncLogTargetOverflowAction
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using Ryujinx.Common.Logging.Formatters;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Common.Logging
|
||||
namespace Ryujinx.Common.Logging.Targets
|
||||
{
|
||||
public class ConsoleLogTarget : ILogTarget
|
||||
{
|
||||
@ -38,4 +39,4 @@ namespace Ryujinx.Common.Logging
|
||||
Console.ResetColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using Ryujinx.Common.Logging.Formatters;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.Common.Logging
|
||||
namespace Ryujinx.Common.Logging.Targets
|
||||
{
|
||||
public class FileLogTarget : ILogTarget
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Common.Logging
|
||||
namespace Ryujinx.Common.Logging.Targets
|
||||
{
|
||||
public interface ILogTarget : IDisposable
|
||||
{
|
||||
|
@ -1,7 +1,7 @@
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Common.Logging
|
||||
namespace Ryujinx.Common.Logging.Targets
|
||||
{
|
||||
public class JsonLogTarget : ILogTarget
|
||||
{
|
||||
|
@ -1,4 +1,5 @@
|
||||
#nullable enable
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
@ -18,12 +19,14 @@ namespace Ryujinx.Common.Utilities
|
||||
public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
var enumValue = reader.GetString();
|
||||
if (string.IsNullOrEmpty(enumValue))
|
||||
|
||||
if (Enum.TryParse(enumValue, out TEnum value))
|
||||
{
|
||||
return default;
|
||||
return value;
|
||||
}
|
||||
|
||||
return Enum.Parse<TEnum>(enumValue);
|
||||
Logger.Warning?.Print(LogClass.Configuration, $"Failed to parse enum value \"{enumValue}\" for {typeof(TEnum)}, using default \"{default(TEnum)}\"");
|
||||
return default;
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options)
|
||||
@ -31,4 +34,4 @@ namespace Ryujinx.Common.Utilities
|
||||
writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -39,6 +39,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
public readonly bool SupportsViewportMask;
|
||||
public readonly bool SupportsViewportSwizzle;
|
||||
public readonly bool SupportsIndirectParameters;
|
||||
public readonly bool SupportsDepthClipControl;
|
||||
|
||||
public readonly uint MaximumUniformBuffersPerStage;
|
||||
public readonly uint MaximumStorageBuffersPerStage;
|
||||
@ -85,6 +86,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
bool supportsViewportMask,
|
||||
bool supportsViewportSwizzle,
|
||||
bool supportsIndirectParameters,
|
||||
bool supportsDepthClipControl,
|
||||
uint maximumUniformBuffersPerStage,
|
||||
uint maximumStorageBuffersPerStage,
|
||||
uint maximumTexturesPerStage,
|
||||
@ -127,6 +129,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
SupportsViewportMask = supportsViewportMask;
|
||||
SupportsViewportSwizzle = supportsViewportSwizzle;
|
||||
SupportsIndirectParameters = supportsIndirectParameters;
|
||||
SupportsDepthClipControl = supportsDepthClipControl;
|
||||
MaximumUniformBuffersPerStage = maximumUniformBuffersPerStage;
|
||||
MaximumStorageBuffersPerStage = maximumStorageBuffersPerStage;
|
||||
MaximumTexturesPerStage = maximumTexturesPerStage;
|
||||
|
@ -383,6 +383,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
case Format.R10G10B10A2Unorm:
|
||||
case Format.R10G10B10A2Uint:
|
||||
case Format.R11G11B10Float:
|
||||
case Format.B8G8R8A8Unorm:
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Ryujinx.Common.Configuration;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
@ -52,7 +53,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
|
||||
void ResetCounter(CounterType type);
|
||||
|
||||
void RunLoop(Action gpuLoop)
|
||||
void RunLoop(ThreadStart gpuLoop)
|
||||
{
|
||||
gpuLoop();
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
private IRenderer _baseRenderer;
|
||||
private Thread _gpuThread;
|
||||
private Thread _backendThread;
|
||||
private bool _disposed;
|
||||
private bool _running;
|
||||
|
||||
private AutoResetEvent _frameComplete = new AutoResetEvent(true);
|
||||
@ -98,19 +97,17 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
_refQueue = new object[MaxRefsPerCommand * QueueCount];
|
||||
}
|
||||
|
||||
public void RunLoop(Action gpuLoop)
|
||||
public void RunLoop(ThreadStart gpuLoop)
|
||||
{
|
||||
_running = true;
|
||||
|
||||
_backendThread = Thread.CurrentThread;
|
||||
|
||||
_gpuThread = new Thread(() => {
|
||||
gpuLoop();
|
||||
_running = false;
|
||||
_galWorkAvailable.Set();
|
||||
});
|
||||
_gpuThread = new Thread(gpuLoop)
|
||||
{
|
||||
Name = "GPU.MainThread"
|
||||
};
|
||||
|
||||
_gpuThread.Name = "GPU.MainThread";
|
||||
_gpuThread.Start();
|
||||
|
||||
RenderLoop();
|
||||
@ -120,7 +117,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
{
|
||||
// Power through the render queue until the Gpu thread work is done.
|
||||
|
||||
while (_running && !_disposed)
|
||||
while (_running)
|
||||
{
|
||||
_galWorkAvailable.Wait();
|
||||
_galWorkAvailable.Reset();
|
||||
@ -488,12 +485,23 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
return _baseRenderer.PrepareHostMapping(address, size);
|
||||
}
|
||||
|
||||
public void FlushThreadedCommands()
|
||||
{
|
||||
SpinWait wait = new();
|
||||
|
||||
while (Volatile.Read(ref _commandCount) > 0)
|
||||
{
|
||||
wait.SpinOnce();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Dispose must happen from the render thread, after all commands have completed.
|
||||
|
||||
// Stop the GPU thread.
|
||||
_disposed = true;
|
||||
_running = false;
|
||||
_galWorkAvailable.Set();
|
||||
|
||||
if (_gpuThread != null && _gpuThread.IsAlive)
|
||||
{
|
||||
|
@ -63,6 +63,8 @@ namespace Ryujinx.Graphics.GAL
|
||||
public bool PrimitiveRestartEnable;
|
||||
public uint PatchControlPoints;
|
||||
|
||||
public DepthMode DepthMode;
|
||||
|
||||
public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
|
||||
{
|
||||
VertexAttribCount = vertexAttribs.Length;
|
||||
|
@ -80,11 +80,6 @@ namespace Ryujinx.Graphics.Gpu
|
||||
/// </summary>
|
||||
public const int GobAlignment = 64;
|
||||
|
||||
/// <summary>
|
||||
/// Expected byte alignment for storage buffers
|
||||
/// </summary>
|
||||
public const int StorageAlignment = 16;
|
||||
|
||||
/// <summary>
|
||||
/// Number of the uniform buffer reserved by the driver to store the storage buffer base addresses.
|
||||
/// </summary>
|
||||
|
@ -151,8 +151,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
||||
|
||||
ShaderProgramInfo info = cs.Shaders[0].Info;
|
||||
|
||||
bool hasUnaligned = _channel.BufferManager.HasUnalignedStorageBuffers;
|
||||
|
||||
for (int index = 0; index < info.SBuffers.Count; index++)
|
||||
{
|
||||
BufferDescriptor sb = info.SBuffers[index];
|
||||
@ -177,9 +175,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
||||
_channel.BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), size, sb.Flags);
|
||||
}
|
||||
|
||||
if ((_channel.BufferManager.HasUnalignedStorageBuffers) != hasUnaligned)
|
||||
if (_channel.BufferManager.HasUnalignedStorageBuffers != computeState.HasUnalignedStorageBuffer)
|
||||
{
|
||||
// Refetch the shader, as assumptions about storage buffer alignment have changed.
|
||||
computeState = new GpuChannelComputeState(
|
||||
qmd.CtaThreadDimension0,
|
||||
qmd.CtaThreadDimension1,
|
||||
qmd.CtaThreadDimension2,
|
||||
localMemorySize,
|
||||
sharedMemorySize,
|
||||
_channel.BufferManager.HasUnalignedStorageBuffers);
|
||||
|
||||
cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, poolState, computeState, shaderGpuVa);
|
||||
|
||||
_context.Renderer.Pipeline.SetProgram(cs.HostProgram);
|
||||
@ -187,30 +193,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
||||
info = cs.Shaders[0].Info;
|
||||
}
|
||||
|
||||
for (int index = 0; index < info.CBuffers.Count; index++)
|
||||
{
|
||||
BufferDescriptor cb = info.CBuffers[index];
|
||||
|
||||
// NVN uses the "hardware" constant buffer for anything that is less than 8,
|
||||
// and those are already bound above.
|
||||
// Anything greater than or equal to 8 uses the emulated constant buffers.
|
||||
// They are emulated using global memory loads.
|
||||
if (cb.Slot < 8)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ulong cbDescAddress = _channel.BufferManager.GetComputeUniformBufferAddress(0);
|
||||
|
||||
int cbDescOffset = 0x260 + (cb.Slot - 8) * 0x10;
|
||||
|
||||
cbDescAddress += (ulong)cbDescOffset;
|
||||
|
||||
SbDescriptor cbDescriptor = _channel.MemoryManager.Physical.Read<SbDescriptor>(cbDescAddress);
|
||||
|
||||
_channel.BufferManager.SetComputeUniformBuffer(cb.Slot, cbDescriptor.PackAddress(), (uint)cbDescriptor.Size);
|
||||
}
|
||||
|
||||
_channel.BufferManager.SetComputeBufferBindings(cs.Bindings);
|
||||
|
||||
_channel.TextureManager.SetComputeBindings(cs.Bindings);
|
||||
|
@ -771,7 +771,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
/// </summary>
|
||||
private void UpdateDepthMode()
|
||||
{
|
||||
_context.Renderer.Pipeline.SetDepthMode(GetDepthMode());
|
||||
DepthMode mode = GetDepthMode();
|
||||
|
||||
_pipeline.DepthMode = mode;
|
||||
|
||||
_context.Renderer.Pipeline.SetDepthMode(mode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -390,7 +390,6 @@ namespace Ryujinx.Graphics.Gpu
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Renderer.Dispose();
|
||||
GPFifo.Dispose();
|
||||
HostInitalized.Dispose();
|
||||
|
||||
@ -403,6 +402,8 @@ namespace Ryujinx.Graphics.Gpu
|
||||
PhysicalMemoryRegistry.Clear();
|
||||
|
||||
RunDeferredActions();
|
||||
|
||||
Renderer.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@ -541,7 +541,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
depth,
|
||||
lhs.FormatInfo.BlockHeight,
|
||||
lhs.GobBlocksInY,
|
||||
lhs.GobBlocksInZ);
|
||||
lhs.GobBlocksInZ,
|
||||
level);
|
||||
|
||||
return gobBlocksInY == rhs.GobBlocksInY &&
|
||||
gobBlocksInZ == rhs.GobBlocksInZ;
|
||||
@ -587,7 +588,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
lhsDepth,
|
||||
lhs.FormatInfo.BlockHeight,
|
||||
lhs.GobBlocksInY,
|
||||
lhs.GobBlocksInZ);
|
||||
lhs.GobBlocksInZ,
|
||||
lhsLevel);
|
||||
|
||||
int rhsHeight = Math.Max(1, rhs.Height >> rhsLevel);
|
||||
int rhsDepth = Math.Max(1, rhs.GetDepth() >> rhsLevel);
|
||||
@ -597,7 +599,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
rhsDepth,
|
||||
rhs.FormatInfo.BlockHeight,
|
||||
rhs.GobBlocksInY,
|
||||
rhs.GobBlocksInZ);
|
||||
rhs.GobBlocksInZ,
|
||||
rhsLevel);
|
||||
|
||||
return lhsGobBlocksInY == rhsGobBlocksInY &&
|
||||
lhsGobBlocksInZ == rhsGobBlocksInZ;
|
||||
|
@ -484,7 +484,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
depthOrLayers = Math.Max(1, depthOrLayers >> minLod);
|
||||
}
|
||||
|
||||
(gobBlocksInY, gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(height, depth, formatInfo.BlockHeight, gobBlocksInY, gobBlocksInZ);
|
||||
(gobBlocksInY, gobBlocksInZ) = SizeCalculator.GetMipGobBlockSizes(height, depth, formatInfo.BlockHeight, gobBlocksInY, gobBlocksInZ, minLod);
|
||||
}
|
||||
|
||||
levels = (maxLod - minLod) + 1;
|
||||
|
@ -222,7 +222,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
||||
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
|
||||
private void RecordStorageAlignment(BuffersPerStage buffers, int index, ulong gpuVa)
|
||||
{
|
||||
bool unaligned = (gpuVa & (Constants.StorageAlignment - 1)) != 0;
|
||||
bool unaligned = (gpuVa & ((ulong)_context.Capabilities.StorageBufferOffsetAlignment - 1)) != 0;
|
||||
|
||||
if (unaligned || HasUnalignedStorageBuffers)
|
||||
{
|
||||
|
@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
||||
private const ushort FileFormatVersionMajor = 1;
|
||||
private const ushort FileFormatVersionMinor = 2;
|
||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||
private const uint CodeGenVersion = 5110;
|
||||
private const uint CodeGenVersion = 4992;
|
||||
|
||||
private const string SharedTocFileName = "shared.toc";
|
||||
private const string SharedDataFileName = "shared.data";
|
||||
|
@ -17,8 +17,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
private readonly ResourceCounts _resourceCounts;
|
||||
private readonly int _stageIndex;
|
||||
|
||||
private readonly int[] _constantBufferBindings;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GPU accessor.
|
||||
/// </summary>
|
||||
@ -28,12 +26,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
_context = context;
|
||||
_resourceCounts = resourceCounts;
|
||||
_stageIndex = stageIndex;
|
||||
|
||||
if (context.Capabilities.Api != TargetApi.Vulkan)
|
||||
{
|
||||
_constantBufferBindings = new int[Constants.TotalGpUniformBuffers];
|
||||
_constantBufferBindings.AsSpan().Fill(-1);
|
||||
}
|
||||
}
|
||||
|
||||
public int QueryBindingConstantBuffer(int index)
|
||||
@ -45,15 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
}
|
||||
else
|
||||
{
|
||||
int binding = _constantBufferBindings[index];
|
||||
|
||||
if (binding < 0)
|
||||
{
|
||||
binding = _resourceCounts.UniformBuffersCount++;
|
||||
_constantBufferBindings[index] = binding;
|
||||
}
|
||||
|
||||
return binding;
|
||||
return _resourceCounts.UniformBuffersCount++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,6 +149,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
|
||||
public bool QueryHostSupportsViewportMask() => _context.Capabilities.SupportsViewportMask;
|
||||
|
||||
public bool QueryHostSupportsDepthClipControl() => _context.Capabilities.SupportsDepthClipControl;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a packed Maxwell texture format to the shader translator texture format.
|
||||
/// </summary>
|
||||
|
@ -92,7 +92,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
int imageBinding = stageIndex * imagesPerStage * 2;
|
||||
|
||||
AddDescriptor(stages, ResourceType.UniformBuffer, UniformSetIndex, uniformBinding, uniformsPerStage);
|
||||
AddArrayDescriptor(stages, ResourceType.StorageBuffer, StorageSetIndex, storageBinding, storagesPerStage);
|
||||
AddDescriptor(stages, ResourceType.StorageBuffer, StorageSetIndex, storageBinding, storagesPerStage);
|
||||
AddDualDescriptor(stages, ResourceType.TextureAndSampler, ResourceType.BufferTexture, TextureSetIndex, textureBinding, texturesPerStage);
|
||||
AddDualDescriptor(stages, ResourceType.Image, ResourceType.BufferImage, ImageSetIndex, imageBinding, imagesPerStage);
|
||||
|
||||
@ -133,19 +133,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
AddDescriptor(stages, type2, setIndex, binding + count, count);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an array resource to the list of descriptors.
|
||||
/// </summary>
|
||||
/// <param name="stages">Shader stages where the resource is used</param>
|
||||
/// <param name="type">Type of the resource</param>
|
||||
/// <param name="setIndex">Descriptor set number where the resource will be bound</param>
|
||||
/// <param name="binding">Binding number where the resource will be bound</param>
|
||||
/// <param name="count">Number of resources bound at the binding location</param>
|
||||
private void AddArrayDescriptor(ResourceStages stages, ResourceType type, int setIndex, int binding, int count)
|
||||
{
|
||||
_resourceDescriptors[setIndex].Add(new ResourceDescriptor(binding, count, type, stages));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds buffer usage information to the list of usages.
|
||||
/// </summary>
|
||||
|
@ -736,6 +736,19 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
return MatchesTexture(specializationState, descriptor);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Populates pipeline state that doesn't exist in older caches with default values
|
||||
/// based on specialization state.
|
||||
/// </summary>
|
||||
/// <param name="pipelineState">Pipeline state to prepare</param>
|
||||
private void PreparePipelineState(ref ProgramPipelineState pipelineState)
|
||||
{
|
||||
if (!_compute)
|
||||
{
|
||||
pipelineState.DepthMode = GraphicsState.DepthMode ? DepthMode.MinusOneToOne : DepthMode.ZeroToOne;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads shader specialization state that has been serialized.
|
||||
/// </summary>
|
||||
@ -776,6 +789,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
ProgramPipelineState pipelineState = default;
|
||||
dataReader.ReadWithMagicAndSize(ref pipelineState, PgpsMagic);
|
||||
|
||||
specState.PreparePipelineState(ref pipelineState);
|
||||
specState.PipelineState = pipelineState;
|
||||
}
|
||||
|
||||
|
@ -163,6 +163,7 @@ namespace Ryujinx.Graphics.OpenGL
|
||||
supportsViewportMask: HwCapabilities.SupportsViewportArray2,
|
||||
supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle,
|
||||
supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters,
|
||||
supportsDepthClipControl: true,
|
||||
maximumUniformBuffersPerStage: 13, // TODO: Avoid hardcoding those limits here and get from driver?
|
||||
maximumStorageBuffersPerStage: 16,
|
||||
maximumTexturesPerStage: 32,
|
||||
|
@ -104,14 +104,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
}
|
||||
|
||||
DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values);
|
||||
|
||||
var sBufferDescriptors = context.Config.GetStorageBufferDescriptors();
|
||||
if (sBufferDescriptors.Length != 0)
|
||||
{
|
||||
DeclareStorages(context, sBufferDescriptors);
|
||||
|
||||
context.AppendLine();
|
||||
}
|
||||
DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values);
|
||||
|
||||
var textureDescriptors = context.Config.GetTextureDescriptors();
|
||||
if (textureDescriptors.Length != 0)
|
||||
@ -250,11 +243,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Shared.glsl");
|
||||
}
|
||||
|
||||
if ((info.HelperFunctionsMask & HelperFunctionsMask.AtomicMinMaxS32Storage) != 0)
|
||||
{
|
||||
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Storage.glsl");
|
||||
}
|
||||
|
||||
if ((info.HelperFunctionsMask & HelperFunctionsMask.MultiplyHighS32) != 0)
|
||||
{
|
||||
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighS32.glsl");
|
||||
@ -290,11 +278,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreSharedSmallInt.glsl");
|
||||
}
|
||||
|
||||
if ((info.HelperFunctionsMask & HelperFunctionsMask.StoreStorageSmallInt) != 0)
|
||||
{
|
||||
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreStorageSmallInt.glsl");
|
||||
}
|
||||
|
||||
if ((info.HelperFunctionsMask & HelperFunctionsMask.SwizzleAdd) != 0)
|
||||
{
|
||||
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/SwizzleAdd.glsl");
|
||||
@ -356,6 +339,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
}
|
||||
|
||||
private static void DeclareConstantBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers)
|
||||
{
|
||||
DeclareBuffers(context, buffers, "uniform");
|
||||
}
|
||||
|
||||
private static void DeclareStorageBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers)
|
||||
{
|
||||
DeclareBuffers(context, buffers, "buffer");
|
||||
}
|
||||
|
||||
private static void DeclareBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers, string declType)
|
||||
{
|
||||
foreach (BufferDefinition buffer in buffers)
|
||||
{
|
||||
@ -365,7 +358,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
_ => "std430"
|
||||
};
|
||||
|
||||
context.AppendLine($"layout (binding = {buffer.Binding}, {layout}) uniform _{buffer.Name}");
|
||||
context.AppendLine($"layout (binding = {buffer.Binding}, {layout}) {declType} _{buffer.Name}");
|
||||
context.EnterScope();
|
||||
|
||||
foreach (StructureField field in buffer.Type.Fields)
|
||||
@ -373,9 +366,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
if (field.Type.HasFlag(AggregateType.Array))
|
||||
{
|
||||
string typeName = GetVarTypeName(context, field.Type & ~AggregateType.Array);
|
||||
string arraySize = field.ArrayLength.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
context.AppendLine($"{typeName} {field.Name}[{arraySize}];");
|
||||
if (field.ArrayLength > 0)
|
||||
{
|
||||
string arraySize = field.ArrayLength.ToString(CultureInfo.InvariantCulture);
|
||||
|
||||
context.AppendLine($"{typeName} {field.Name}[{arraySize}];");
|
||||
}
|
||||
else
|
||||
{
|
||||
context.AppendLine($"{typeName} {field.Name}[];");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -390,22 +391,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeclareStorages(CodeGenContext context, BufferDescriptor[] descriptors)
|
||||
{
|
||||
string sbName = OperandManager.GetShaderStagePrefix(context.Config.Stage);
|
||||
|
||||
sbName += "_" + DefaultNames.StorageNamePrefix;
|
||||
|
||||
string blockName = $"{sbName}_{DefaultNames.BlockSuffix}";
|
||||
|
||||
string layout = context.Config.Options.TargetApi == TargetApi.Vulkan ? ", set = 1" : string.Empty;
|
||||
|
||||
context.AppendLine($"layout (binding = {context.Config.FirstStorageBufferBinding}{layout}, std430) buffer {blockName}");
|
||||
context.EnterScope();
|
||||
context.AppendLine("uint " + DefaultNames.DataName + "[];");
|
||||
context.LeaveScope($" {sbName}[{NumberFormatter.FormatInt(descriptors.Max(x => x.Slot) + 1)}];");
|
||||
}
|
||||
|
||||
private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors)
|
||||
{
|
||||
int arraySize = 0;
|
||||
@ -733,7 +718,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
|
||||
code = code.Replace("\t", CodeGenContext.Tab);
|
||||
code = code.Replace("$SHARED_MEM$", DefaultNames.SharedMemoryName);
|
||||
code = code.Replace("$STORAGE_MEM$", OperandManager.GetShaderStagePrefix(context.Config.Stage) + "_" + DefaultNames.StorageNamePrefix);
|
||||
|
||||
if (context.Config.GpuAccessor.QueryHostSupportsShaderBallot())
|
||||
{
|
||||
|
@ -11,12 +11,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
public const string IAttributePrefix = "in_attr";
|
||||
public const string OAttributePrefix = "out_attr";
|
||||
|
||||
public const string StorageNamePrefix = "s";
|
||||
|
||||
public const string DataName = "data";
|
||||
|
||||
public const string BlockSuffix = "block";
|
||||
|
||||
public const string LocalMemoryName = "local_mem";
|
||||
public const string SharedMemoryName = "shared_mem";
|
||||
|
||||
|
@ -1,21 +0,0 @@
|
||||
int Helper_AtomicMaxS32(int index, int offset, int value)
|
||||
{
|
||||
uint oldValue, newValue;
|
||||
do
|
||||
{
|
||||
oldValue = $STORAGE_MEM$[index].data[offset];
|
||||
newValue = uint(max(int(oldValue), value));
|
||||
} while (atomicCompSwap($STORAGE_MEM$[index].data[offset], oldValue, newValue) != oldValue);
|
||||
return int(oldValue);
|
||||
}
|
||||
|
||||
int Helper_AtomicMinS32(int index, int offset, int value)
|
||||
{
|
||||
uint oldValue, newValue;
|
||||
do
|
||||
{
|
||||
oldValue = $STORAGE_MEM$[index].data[offset];
|
||||
newValue = uint(min(int(oldValue), value));
|
||||
} while (atomicCompSwap($STORAGE_MEM$[index].data[offset], oldValue, newValue) != oldValue);
|
||||
return int(oldValue);
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
void Helper_StoreStorage16(int index, int offset, uint value)
|
||||
{
|
||||
int wordOffset = offset >> 2;
|
||||
int bitOffset = (offset & 3) * 8;
|
||||
uint oldValue, newValue;
|
||||
do
|
||||
{
|
||||
oldValue = $STORAGE_MEM$[index].data[wordOffset];
|
||||
newValue = bitfieldInsert(oldValue, value, bitOffset, 16);
|
||||
} while (atomicCompSwap($STORAGE_MEM$[index].data[wordOffset], oldValue, newValue) != oldValue);
|
||||
}
|
||||
|
||||
void Helper_StoreStorage8(int index, int offset, uint value)
|
||||
{
|
||||
int wordOffset = offset >> 2;
|
||||
int bitOffset = (offset & 3) * 8;
|
||||
uint oldValue, newValue;
|
||||
do
|
||||
{
|
||||
oldValue = $STORAGE_MEM$[index].data[wordOffset];
|
||||
newValue = bitfieldInsert(oldValue, value, bitOffset, 8);
|
||||
} while (atomicCompSwap($STORAGE_MEM$[index].data[wordOffset], oldValue, newValue) != oldValue);
|
||||
}
|
@ -68,33 +68,45 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
|
||||
string args = string.Empty;
|
||||
|
||||
for (int argIndex = 0; argIndex < arity; argIndex++)
|
||||
if (atomic && operation.StorageKind == StorageKind.StorageBuffer)
|
||||
{
|
||||
args = GenerateLoadOrStore(context, operation, isStore: false);
|
||||
|
||||
AggregateType dstType = operation.Inst == Instruction.AtomicMaxS32 || operation.Inst == Instruction.AtomicMinS32
|
||||
? AggregateType.S32
|
||||
: AggregateType.U32;
|
||||
|
||||
for (int argIndex = operation.SourcesCount - arity + 2; argIndex < operation.SourcesCount; argIndex++)
|
||||
{
|
||||
args += ", " + GetSoureExpr(context, operation.GetSource(argIndex), dstType);
|
||||
}
|
||||
}
|
||||
else if (atomic && operation.StorageKind == StorageKind.SharedMemory)
|
||||
{
|
||||
args = LoadShared(context, operation);
|
||||
|
||||
// For shared memory access, the second argument is unused and should be ignored.
|
||||
// It is there to make both storage and shared access have the same number of arguments.
|
||||
// For storage, both inputs are consumed when the argument index is 0, so we should skip it here.
|
||||
if (argIndex == 1 && (atomic || operation.StorageKind == StorageKind.SharedMemory))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (argIndex != 0)
|
||||
for (int argIndex = 2; argIndex < arity; argIndex++)
|
||||
{
|
||||
args += ", ";
|
||||
}
|
||||
|
||||
if (argIndex == 0 && atomic)
|
||||
AggregateType dstType = GetSrcVarType(inst, argIndex);
|
||||
|
||||
args += GetSoureExpr(context, operation.GetSource(argIndex), dstType);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int argIndex = 0; argIndex < arity; argIndex++)
|
||||
{
|
||||
switch (operation.StorageKind)
|
||||
if (argIndex != 0)
|
||||
{
|
||||
case StorageKind.SharedMemory: args += LoadShared(context, operation); break;
|
||||
case StorageKind.StorageBuffer: args += LoadStorage(context, operation); break;
|
||||
|
||||
default: throw new InvalidOperationException($"Invalid storage kind \"{operation.StorageKind}\".");
|
||||
args += ", ";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
AggregateType dstType = GetSrcVarType(inst, argIndex);
|
||||
|
||||
args += GetSoureExpr(context, operation.GetSource(argIndex), dstType);
|
||||
@ -173,9 +185,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
case Instruction.LoadShared:
|
||||
return LoadShared(context, operation);
|
||||
|
||||
case Instruction.LoadStorage:
|
||||
return LoadStorage(context, operation);
|
||||
|
||||
case Instruction.Lod:
|
||||
return Lod(context, operation);
|
||||
|
||||
@ -203,15 +212,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
case Instruction.StoreShared8:
|
||||
return StoreShared8(context, operation);
|
||||
|
||||
case Instruction.StoreStorage:
|
||||
return StoreStorage(context, operation);
|
||||
|
||||
case Instruction.StoreStorage16:
|
||||
return StoreStorage16(context, operation);
|
||||
|
||||
case Instruction.StoreStorage8:
|
||||
return StoreStorage8(context, operation);
|
||||
|
||||
case Instruction.TextureSample:
|
||||
return TextureSample(context, operation);
|
||||
|
||||
|
@ -85,7 +85,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
Add(Instruction.Load, InstType.Special);
|
||||
Add(Instruction.LoadLocal, InstType.Special);
|
||||
Add(Instruction.LoadShared, InstType.Special);
|
||||
Add(Instruction.LoadStorage, InstType.Special);
|
||||
Add(Instruction.Lod, InstType.Special);
|
||||
Add(Instruction.LogarithmB2, InstType.CallUnary, "log2");
|
||||
Add(Instruction.LogicalAnd, InstType.OpBinaryCom, "&&", 9);
|
||||
@ -123,9 +122,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
Add(Instruction.StoreShared, InstType.Special);
|
||||
Add(Instruction.StoreShared16, InstType.Special);
|
||||
Add(Instruction.StoreShared8, InstType.Special);
|
||||
Add(Instruction.StoreStorage, InstType.Special);
|
||||
Add(Instruction.StoreStorage16, InstType.Special);
|
||||
Add(Instruction.StoreStorage8, InstType.Special);
|
||||
Add(Instruction.Subtract, InstType.OpBinary, "-", 2);
|
||||
Add(Instruction.SwizzleAdd, InstType.CallTernary, HelperFunctionNames.SwizzleAdd);
|
||||
Add(Instruction.TextureSample, InstType.Special);
|
||||
|
@ -210,17 +210,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
return $"{arrayName}[{offsetExpr}]";
|
||||
}
|
||||
|
||||
public static string LoadStorage(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
IAstNode src1 = operation.GetSource(0);
|
||||
IAstNode src2 = operation.GetSource(1);
|
||||
|
||||
string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
|
||||
string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
|
||||
|
||||
return GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage);
|
||||
}
|
||||
|
||||
public static string Lod(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||
@ -326,60 +315,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
return $"{HelperFunctionNames.StoreShared8}({offsetExpr}, {src})";
|
||||
}
|
||||
|
||||
public static string StoreStorage(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
IAstNode src1 = operation.GetSource(0);
|
||||
IAstNode src2 = operation.GetSource(1);
|
||||
IAstNode src3 = operation.GetSource(2);
|
||||
|
||||
string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
|
||||
string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
|
||||
|
||||
AggregateType srcType = OperandManager.GetNodeDestType(context, src3);
|
||||
|
||||
string src = TypeConversion.ReinterpretCast(context, src3, srcType, AggregateType.U32);
|
||||
|
||||
string sb = GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage);
|
||||
|
||||
return $"{sb} = {src}";
|
||||
}
|
||||
|
||||
public static string StoreStorage16(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
IAstNode src1 = operation.GetSource(0);
|
||||
IAstNode src2 = operation.GetSource(1);
|
||||
IAstNode src3 = operation.GetSource(2);
|
||||
|
||||
string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
|
||||
string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
|
||||
|
||||
AggregateType srcType = OperandManager.GetNodeDestType(context, src3);
|
||||
|
||||
string src = TypeConversion.ReinterpretCast(context, src3, srcType, AggregateType.U32);
|
||||
|
||||
string sb = GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage);
|
||||
|
||||
return $"{HelperFunctionNames.StoreStorage16}({indexExpr}, {offsetExpr}, {src})";
|
||||
}
|
||||
|
||||
public static string StoreStorage8(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
IAstNode src1 = operation.GetSource(0);
|
||||
IAstNode src2 = operation.GetSource(1);
|
||||
IAstNode src3 = operation.GetSource(2);
|
||||
|
||||
string indexExpr = GetSoureExpr(context, src1, GetSrcVarType(operation.Inst, 0));
|
||||
string offsetExpr = GetSoureExpr(context, src2, GetSrcVarType(operation.Inst, 1));
|
||||
|
||||
AggregateType srcType = OperandManager.GetNodeDestType(context, src3);
|
||||
|
||||
string src = TypeConversion.ReinterpretCast(context, src3, srcType, AggregateType.U32);
|
||||
|
||||
string sb = GetStorageBufferAccessor(indexExpr, offsetExpr, context.Config.Stage);
|
||||
|
||||
return $"{HelperFunctionNames.StoreStorage8}({indexExpr}, {offsetExpr}, {src})";
|
||||
}
|
||||
|
||||
public static string TextureSample(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||
@ -701,25 +636,34 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
}
|
||||
}
|
||||
|
||||
private static string GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore)
|
||||
public static string GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore)
|
||||
{
|
||||
StorageKind storageKind = operation.StorageKind;
|
||||
|
||||
string varName;
|
||||
AggregateType varType;
|
||||
int srcIndex = 0;
|
||||
int inputsCount = isStore ? operation.SourcesCount - 1 : operation.SourcesCount;
|
||||
bool isStoreOrAtomic = operation.Inst == Instruction.Store || operation.Inst.IsAtomic();
|
||||
int inputsCount = isStoreOrAtomic ? operation.SourcesCount - 1 : operation.SourcesCount;
|
||||
|
||||
if (operation.Inst == Instruction.AtomicCompareAndSwap)
|
||||
{
|
||||
inputsCount--;
|
||||
}
|
||||
|
||||
switch (storageKind)
|
||||
{
|
||||
case StorageKind.ConstantBuffer:
|
||||
case StorageKind.StorageBuffer:
|
||||
if (!(operation.GetSource(srcIndex++) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant)
|
||||
{
|
||||
throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand.");
|
||||
}
|
||||
|
||||
int binding = bindingIndex.Value;
|
||||
BufferDefinition buffer = context.Config.Properties.ConstantBuffers[binding];
|
||||
BufferDefinition buffer = storageKind == StorageKind.ConstantBuffer
|
||||
? context.Config.Properties.ConstantBuffers[binding]
|
||||
: context.Config.Properties.StorageBuffers[binding];
|
||||
|
||||
if (!(operation.GetSource(srcIndex++) is AstOperand fieldIndex) || fieldIndex.Type != OperandType.Constant)
|
||||
{
|
||||
@ -825,15 +769,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
||||
return varName;
|
||||
}
|
||||
|
||||
private static string GetStorageBufferAccessor(string slotExpr, string offsetExpr, ShaderStage stage)
|
||||
{
|
||||
string sbName = OperandManager.GetShaderStagePrefix(stage);
|
||||
|
||||
sbName += "_" + DefaultNames.StorageNamePrefix;
|
||||
|
||||
return $"{sbName}[{slotExpr}].{DefaultNames.DataName}[{offsetExpr}]";
|
||||
}
|
||||
|
||||
private static string GetMask(int index)
|
||||
{
|
||||
return $".{"rgba".AsSpan(index, 1)}";
|
||||
|
@ -118,6 +118,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
switch (operation.StorageKind)
|
||||
{
|
||||
case StorageKind.ConstantBuffer:
|
||||
case StorageKind.StorageBuffer:
|
||||
if (!(operation.GetSource(0) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant)
|
||||
{
|
||||
throw new InvalidOperationException($"First input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand.");
|
||||
@ -128,7 +129,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
||||
throw new InvalidOperationException($"Second input of {operation.Inst} with {operation.StorageKind} storage must be a constant operand.");
|
||||
}
|
||||
|
||||
BufferDefinition buffer = context.Config.Properties.ConstantBuffers[bindingIndex.Value];
|
||||
BufferDefinition buffer = operation.StorageKind == StorageKind.ConstantBuffer
|
||||
? context.Config.Properties.ConstantBuffers[bindingIndex.Value]
|
||||
: context.Config.Properties.StorageBuffers[bindingIndex.Value];
|
||||
StructureField field = buffer.Type.Fields[fieldIndex.Value];
|
||||
|
||||
return field.Type & AggregateType.ElementTypeMask;
|
||||
|
@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
public int InputVertices { get; }
|
||||
|
||||
public Dictionary<int, Instruction> ConstantBuffers { get; } = new Dictionary<int, Instruction>();
|
||||
public Instruction StorageBuffersArray { get; set; }
|
||||
public Dictionary<int, Instruction> StorageBuffers { get; } = new Dictionary<int, Instruction>();
|
||||
public Instruction LocalMemory { get; set; }
|
||||
public Instruction SharedMemory { get; set; }
|
||||
public Dictionary<TextureMeta, SamplerType> SamplersTypes { get; } = new Dictionary<TextureMeta, SamplerType>();
|
||||
@ -308,7 +308,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
{
|
||||
if ((type & AggregateType.Array) != 0)
|
||||
{
|
||||
return TypeArray(GetType(type & ~AggregateType.Array), Constant(TypeU32(), length));
|
||||
if (length > 0)
|
||||
{
|
||||
return TypeArray(GetType(type & ~AggregateType.Array), Constant(TypeU32(), length));
|
||||
}
|
||||
else
|
||||
{
|
||||
return TypeRuntimeArray(GetType(type & ~AggregateType.Array));
|
||||
}
|
||||
}
|
||||
else if ((type & AggregateType.ElementCountMask) != 0)
|
||||
{
|
||||
|
@ -5,6 +5,7 @@ using Ryujinx.Graphics.Shader.Translation;
|
||||
using Spv.Generator;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using static Spv.Specification;
|
||||
@ -99,7 +100,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
}
|
||||
|
||||
DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values);
|
||||
DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors());
|
||||
DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values);
|
||||
DeclareSamplers(context, context.Config.GetTextureDescriptors());
|
||||
DeclareImages(context, context.Config.GetImageDescriptors());
|
||||
DeclareInputsAndOutputs(context, info);
|
||||
@ -127,6 +128,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
}
|
||||
|
||||
private static void DeclareConstantBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers)
|
||||
{
|
||||
DeclareBuffers(context, buffers, isBuffer: false);
|
||||
}
|
||||
|
||||
private static void DeclareStorageBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers)
|
||||
{
|
||||
DeclareBuffers(context, buffers, isBuffer: true);
|
||||
}
|
||||
|
||||
private static void DeclareBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers, bool isBuffer)
|
||||
{
|
||||
HashSet<SpvInstruction> decoratedTypes = new HashSet<SpvInstruction>();
|
||||
|
||||
@ -155,6 +166,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
context.Decorate(structFieldTypes[fieldIndex], Decoration.ArrayStride, (LiteralInteger)fieldSize);
|
||||
}
|
||||
|
||||
// Zero lengths are assumed to be a "runtime array" (which does not have a explicit length
|
||||
// specified on the shader, and instead assumes the bound buffer length).
|
||||
// It is only valid as the last struct element.
|
||||
|
||||
Debug.Assert(field.ArrayLength > 0 || fieldIndex == buffer.Type.Fields.Length - 1);
|
||||
|
||||
offset += fieldSize * field.ArrayLength;
|
||||
}
|
||||
else
|
||||
@ -163,56 +180,37 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
}
|
||||
}
|
||||
|
||||
var ubStructType = context.TypeStruct(false, structFieldTypes);
|
||||
var structType = context.TypeStruct(false, structFieldTypes);
|
||||
|
||||
if (decoratedTypes.Add(ubStructType))
|
||||
if (decoratedTypes.Add(structType))
|
||||
{
|
||||
context.Decorate(ubStructType, Decoration.Block);
|
||||
context.Decorate(structType, isBuffer ? Decoration.BufferBlock : Decoration.Block);
|
||||
|
||||
for (int fieldIndex = 0; fieldIndex < structFieldOffsets.Length; fieldIndex++)
|
||||
{
|
||||
context.MemberDecorate(ubStructType, fieldIndex, Decoration.Offset, (LiteralInteger)structFieldOffsets[fieldIndex]);
|
||||
context.MemberDecorate(structType, fieldIndex, Decoration.Offset, (LiteralInteger)structFieldOffsets[fieldIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
var ubPointerType = context.TypePointer(StorageClass.Uniform, ubStructType);
|
||||
var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform);
|
||||
var pointerType = context.TypePointer(StorageClass.Uniform, structType);
|
||||
var variable = context.Variable(pointerType, StorageClass.Uniform);
|
||||
|
||||
context.Name(ubVariable, buffer.Name);
|
||||
context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)buffer.Set);
|
||||
context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)buffer.Binding);
|
||||
context.AddGlobalVariable(ubVariable);
|
||||
context.ConstantBuffers.Add(buffer.Binding, ubVariable);
|
||||
context.Name(variable, buffer.Name);
|
||||
context.Decorate(variable, Decoration.DescriptorSet, (LiteralInteger)buffer.Set);
|
||||
context.Decorate(variable, Decoration.Binding, (LiteralInteger)buffer.Binding);
|
||||
context.AddGlobalVariable(variable);
|
||||
|
||||
if (isBuffer)
|
||||
{
|
||||
context.StorageBuffers.Add(buffer.Binding, variable);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.ConstantBuffers.Add(buffer.Binding, variable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void DeclareStorageBuffers(CodeGenContext context, BufferDescriptor[] descriptors)
|
||||
{
|
||||
if (descriptors.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int setIndex = context.Config.Options.TargetApi == TargetApi.Vulkan ? 1 : 0;
|
||||
int count = descriptors.Max(x => x.Slot) + 1;
|
||||
|
||||
var sbArrayType = context.TypeRuntimeArray(context.TypeU32());
|
||||
context.Decorate(sbArrayType, Decoration.ArrayStride, (LiteralInteger)4);
|
||||
var sbStructType = context.TypeStruct(true, sbArrayType);
|
||||
context.Decorate(sbStructType, Decoration.BufferBlock);
|
||||
context.MemberDecorate(sbStructType, 0, Decoration.Offset, (LiteralInteger)0);
|
||||
var sbStructArrayType = context.TypeArray(sbStructType, context.Constant(context.TypeU32(), count));
|
||||
var sbPointerType = context.TypePointer(StorageClass.Uniform, sbStructArrayType);
|
||||
var sbVariable = context.Variable(sbPointerType, StorageClass.Uniform);
|
||||
|
||||
context.Name(sbVariable, $"{GetStagePrefix(context.Config.Stage)}_s");
|
||||
context.Decorate(sbVariable, Decoration.DescriptorSet, (LiteralInteger)setIndex);
|
||||
context.Decorate(sbVariable, Decoration.Binding, (LiteralInteger)context.Config.FirstStorageBufferBinding);
|
||||
context.AddGlobalVariable(sbVariable);
|
||||
|
||||
context.StorageBuffersArray = sbVariable;
|
||||
}
|
||||
|
||||
private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors)
|
||||
{
|
||||
foreach (var descriptor in descriptors)
|
||||
|
@ -99,7 +99,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
Add(Instruction.Load, GenerateLoad);
|
||||
Add(Instruction.LoadLocal, GenerateLoadLocal);
|
||||
Add(Instruction.LoadShared, GenerateLoadShared);
|
||||
Add(Instruction.LoadStorage, GenerateLoadStorage);
|
||||
Add(Instruction.Lod, GenerateLod);
|
||||
Add(Instruction.LogarithmB2, GenerateLogarithmB2);
|
||||
Add(Instruction.LogicalAnd, GenerateLogicalAnd);
|
||||
@ -137,9 +136,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
Add(Instruction.StoreShared, GenerateStoreShared);
|
||||
Add(Instruction.StoreShared16, GenerateStoreShared16);
|
||||
Add(Instruction.StoreShared8, GenerateStoreShared8);
|
||||
Add(Instruction.StoreStorage, GenerateStoreStorage);
|
||||
Add(Instruction.StoreStorage16, GenerateStoreStorage16);
|
||||
Add(Instruction.StoreStorage8, GenerateStoreStorage8);
|
||||
Add(Instruction.Subtract, GenerateSubtract);
|
||||
Add(Instruction.SwizzleAdd, GenerateSwizzleAdd);
|
||||
Add(Instruction.TextureSample, GenerateTextureSample);
|
||||
@ -889,14 +885,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
return new OperationResult(AggregateType.U32, value);
|
||||
}
|
||||
|
||||
private static OperationResult GenerateLoadStorage(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
var elemPointer = GetStorageElemPointer(context, operation);
|
||||
var value = context.Load(context.TypeU32(), elemPointer);
|
||||
|
||||
return new OperationResult(AggregateType.U32, value);
|
||||
}
|
||||
|
||||
private static OperationResult GenerateLod(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||
@ -1307,28 +1295,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
return OperationResult.Invalid;
|
||||
}
|
||||
|
||||
private static OperationResult GenerateStoreStorage(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
var elemPointer = GetStorageElemPointer(context, operation);
|
||||
context.Store(elemPointer, context.Get(AggregateType.U32, operation.GetSource(2)));
|
||||
|
||||
return OperationResult.Invalid;
|
||||
}
|
||||
|
||||
private static OperationResult GenerateStoreStorage16(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
GenerateStoreStorageSmallInt(context, operation, 16);
|
||||
|
||||
return OperationResult.Invalid;
|
||||
}
|
||||
|
||||
private static OperationResult GenerateStoreStorage8(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
GenerateStoreStorageSmallInt(context, operation, 8);
|
||||
|
||||
return OperationResult.Invalid;
|
||||
}
|
||||
|
||||
private static OperationResult GenerateSubtract(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
return GenerateBinary(context, operation, context.Delegates.FSub, context.Delegates.ISub);
|
||||
@ -1849,13 +1815,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
AstOperation operation,
|
||||
Func<SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction> emitU)
|
||||
{
|
||||
var value = context.GetU32(operation.GetSource(2));
|
||||
var value = context.GetU32(operation.GetSource(operation.SourcesCount - 1));
|
||||
|
||||
SpvInstruction elemPointer;
|
||||
|
||||
if (operation.StorageKind == StorageKind.StorageBuffer)
|
||||
{
|
||||
elemPointer = GetStorageElemPointer(context, operation);
|
||||
elemPointer = GetStoragePointer(context, operation, out _);
|
||||
}
|
||||
else if (operation.StorageKind == StorageKind.SharedMemory)
|
||||
{
|
||||
@ -1875,14 +1841,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
|
||||
private static OperationResult GenerateAtomicMemoryCas(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
var value0 = context.GetU32(operation.GetSource(2));
|
||||
var value1 = context.GetU32(operation.GetSource(3));
|
||||
var value0 = context.GetU32(operation.GetSource(operation.SourcesCount - 2));
|
||||
var value1 = context.GetU32(operation.GetSource(operation.SourcesCount - 1));
|
||||
|
||||
SpvInstruction elemPointer;
|
||||
|
||||
if (operation.StorageKind == StorageKind.StorageBuffer)
|
||||
{
|
||||
elemPointer = GetStorageElemPointer(context, operation);
|
||||
elemPointer = GetStoragePointer(context, operation, out _);
|
||||
}
|
||||
else if (operation.StorageKind == StorageKind.SharedMemory)
|
||||
{
|
||||
@ -1901,17 +1867,33 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
}
|
||||
|
||||
private static OperationResult GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore)
|
||||
{
|
||||
SpvInstruction pointer = GetStoragePointer(context, operation, out AggregateType varType);
|
||||
|
||||
if (isStore)
|
||||
{
|
||||
context.Store(pointer, context.Get(varType, operation.GetSource(operation.SourcesCount - 1)));
|
||||
return OperationResult.Invalid;
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = context.Load(context.GetType(varType), pointer);
|
||||
return new OperationResult(varType, result);
|
||||
}
|
||||
}
|
||||
|
||||
private static SpvInstruction GetStoragePointer(CodeGenContext context, AstOperation operation, out AggregateType varType)
|
||||
{
|
||||
StorageKind storageKind = operation.StorageKind;
|
||||
|
||||
StorageClass storageClass;
|
||||
SpvInstruction baseObj;
|
||||
AggregateType varType;
|
||||
int srcIndex = 0;
|
||||
|
||||
switch (storageKind)
|
||||
{
|
||||
case StorageKind.ConstantBuffer:
|
||||
case StorageKind.StorageBuffer:
|
||||
if (!(operation.GetSource(srcIndex++) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant)
|
||||
{
|
||||
throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand.");
|
||||
@ -1922,12 +1904,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
throw new InvalidOperationException($"Second input of {operation.Inst} with {storageKind} storage must be a constant operand.");
|
||||
}
|
||||
|
||||
BufferDefinition buffer = context.Config.Properties.ConstantBuffers[bindingIndex.Value];
|
||||
BufferDefinition buffer = storageKind == StorageKind.ConstantBuffer
|
||||
? context.Config.Properties.ConstantBuffers[bindingIndex.Value]
|
||||
: context.Config.Properties.StorageBuffers[bindingIndex.Value];
|
||||
StructureField field = buffer.Type.Fields[fieldIndex.Value];
|
||||
|
||||
storageClass = StorageClass.Uniform;
|
||||
varType = field.Type & AggregateType.ElementTypeMask;
|
||||
baseObj = context.ConstantBuffers[bindingIndex.Value];
|
||||
baseObj = storageKind == StorageKind.ConstantBuffer
|
||||
? context.ConstantBuffers[bindingIndex.Value]
|
||||
: context.StorageBuffers[bindingIndex.Value];
|
||||
break;
|
||||
|
||||
case StorageKind.Input:
|
||||
@ -1993,7 +1979,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
throw new InvalidOperationException($"Invalid storage kind {storageKind}.");
|
||||
}
|
||||
|
||||
int inputsCount = (isStore ? operation.SourcesCount - 1 : operation.SourcesCount) - srcIndex;
|
||||
bool isStoreOrAtomic = operation.Inst == Instruction.Store || operation.Inst.IsAtomic();
|
||||
int inputsCount = (isStoreOrAtomic ? operation.SourcesCount - 1 : operation.SourcesCount) - srcIndex;
|
||||
|
||||
if (operation.Inst == Instruction.AtomicCompareAndSwap)
|
||||
{
|
||||
inputsCount--;
|
||||
}
|
||||
|
||||
SpvInstruction e0, e1, e2;
|
||||
SpvInstruction pointer;
|
||||
|
||||
@ -2030,16 +2023,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
break;
|
||||
}
|
||||
|
||||
if (isStore)
|
||||
{
|
||||
context.Store(pointer, context.Get(varType, operation.GetSource(srcIndex)));
|
||||
return OperationResult.Invalid;
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = context.Load(context.GetType(varType), pointer);
|
||||
return new OperationResult(varType, result);
|
||||
}
|
||||
return pointer;
|
||||
}
|
||||
|
||||
private static SpvInstruction GetScalarInput(CodeGenContext context, IoVariable ioVariable)
|
||||
@ -2068,25 +2052,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
GenerateStoreSmallInt(context, elemPointer, bitOffset, value, bitSize);
|
||||
}
|
||||
|
||||
private static void GenerateStoreStorageSmallInt(CodeGenContext context, AstOperation operation, int bitSize)
|
||||
{
|
||||
var i0 = context.Get(AggregateType.S32, operation.GetSource(0));
|
||||
var offset = context.Get(AggregateType.U32, operation.GetSource(1));
|
||||
var value = context.Get(AggregateType.U32, operation.GetSource(2));
|
||||
|
||||
var wordOffset = context.ShiftRightLogical(context.TypeU32(), offset, context.Constant(context.TypeU32(), 2));
|
||||
var bitOffset = context.BitwiseAnd(context.TypeU32(), offset, context.Constant(context.TypeU32(), 3));
|
||||
bitOffset = context.ShiftLeftLogical(context.TypeU32(), bitOffset, context.Constant(context.TypeU32(), 3));
|
||||
|
||||
var sbVariable = context.StorageBuffersArray;
|
||||
|
||||
var i1 = context.Constant(context.TypeS32(), 0);
|
||||
|
||||
var elemPointer = context.AccessChain(context.TypePointer(StorageClass.Uniform, context.TypeU32()), sbVariable, i0, i1, wordOffset);
|
||||
|
||||
GenerateStoreSmallInt(context, elemPointer, bitOffset, value, bitSize);
|
||||
}
|
||||
|
||||
private static void GenerateStoreSmallInt(
|
||||
CodeGenContext context,
|
||||
SpvInstruction elemPointer,
|
||||
@ -2173,16 +2138,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
||||
}
|
||||
}
|
||||
|
||||
private static SpvInstruction GetStorageElemPointer(CodeGenContext context, AstOperation operation)
|
||||
{
|
||||
var sbVariable = context.StorageBuffersArray;
|
||||
var i0 = context.Get(AggregateType.S32, operation.GetSource(0));
|
||||
var i1 = context.Constant(context.TypeS32(), 0);
|
||||
var i2 = context.Get(AggregateType.S32, operation.GetSource(1));
|
||||
|
||||
return context.AccessChain(context.TypePointer(StorageClass.Uniform, context.TypeU32()), sbVariable, i0, i1, i2);
|
||||
}
|
||||
|
||||
private static OperationResult GenerateUnary(
|
||||
CodeGenContext context,
|
||||
AstOperation operation,
|
||||
|
@ -10,7 +10,5 @@ namespace Ryujinx.Graphics.Shader
|
||||
public const int NvnBaseVertexByteOffset = 0x640;
|
||||
public const int NvnBaseInstanceByteOffset = 0x644;
|
||||
public const int NvnDrawIndexByteOffset = 0x648;
|
||||
|
||||
public const int StorageAlignment = 16;
|
||||
}
|
||||
}
|
@ -79,7 +79,7 @@ namespace Ryujinx.Graphics.Shader
|
||||
/// <returns>Binding number</returns>
|
||||
int QueryBindingConstantBuffer(int index)
|
||||
{
|
||||
return index;
|
||||
return index + 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -367,6 +367,15 @@ namespace Ryujinx.Graphics.Shader
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries whether the host supports depth clip control.
|
||||
/// </summary>
|
||||
/// <returns>True if the GPU and driver supports depth clip control, false otherwise</returns>
|
||||
bool QueryHostSupportsDepthClipControl()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Queries the point size from the GPU state, used when it is not explicitly set on the shader.
|
||||
/// </summary>
|
||||
|
@ -164,6 +164,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||
|
||||
if (op.Ccc == Ccc.T)
|
||||
{
|
||||
context.PrepareForReturn();
|
||||
context.Return();
|
||||
}
|
||||
else
|
||||
@ -175,6 +176,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||
{
|
||||
Operand lblSkip = Label();
|
||||
context.BranchIfFalse(lblSkip, cond);
|
||||
context.PrepareForReturn();
|
||||
context.Return();
|
||||
context.MarkLabel(lblSkip);
|
||||
}
|
||||
|
@ -336,13 +336,12 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||
int offset,
|
||||
bool extended)
|
||||
{
|
||||
bool isSmallInt = size < LsSize.B32;
|
||||
|
||||
int count = GetVectorCount(size);
|
||||
StorageKind storageKind = GetStorageKind(size);
|
||||
|
||||
(Operand addrLow, Operand addrHigh) = Get40BitsAddress(context, new Register(ra, RegisterType.Gpr), extended, offset);
|
||||
(_, Operand addrHigh) = Get40BitsAddress(context, new Register(ra, RegisterType.Gpr), extended, offset);
|
||||
|
||||
Operand bitOffset = GetBitOffset(context, addrLow);
|
||||
Operand srcA = context.Copy(new Operand(new Register(ra, RegisterType.Gpr)));
|
||||
|
||||
for (int index = 0; index < count; index++)
|
||||
{
|
||||
@ -353,12 +352,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||
break;
|
||||
}
|
||||
|
||||
Operand value = context.LoadGlobal(context.IAdd(addrLow, Const(index * 4)), addrHigh);
|
||||
|
||||
if (isSmallInt)
|
||||
{
|
||||
value = ExtractSmallInt(context, size, bitOffset, value);
|
||||
}
|
||||
Operand value = context.Load(storageKind, context.IAdd(srcA, Const(offset + index * 4)), addrHigh);
|
||||
|
||||
context.Copy(Register(dest), value);
|
||||
}
|
||||
@ -445,10 +439,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||
}
|
||||
|
||||
int count = GetVectorCount((LsSize)size);
|
||||
StorageKind storageKind = GetStorageKind((LsSize)size);
|
||||
|
||||
(Operand addrLow, Operand addrHigh) = Get40BitsAddress(context, new Register(ra, RegisterType.Gpr), extended, offset);
|
||||
(_, Operand addrHigh) = Get40BitsAddress(context, new Register(ra, RegisterType.Gpr), extended, offset);
|
||||
|
||||
Operand bitOffset = GetBitOffset(context, addrLow);
|
||||
Operand srcA = context.Copy(new Operand(new Register(ra, RegisterType.Gpr)));
|
||||
|
||||
for (int index = 0; index < count; index++)
|
||||
{
|
||||
@ -456,23 +451,24 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
||||
|
||||
Operand value = Register(isRz ? rd : rd + index, RegisterType.Gpr);
|
||||
|
||||
Operand addrLowOffset = context.IAdd(addrLow, Const(index * 4));
|
||||
Operand addrLowOffset = context.IAdd(srcA, Const(offset + index * 4));
|
||||
|
||||
if (size == LsSize2.U8 || size == LsSize2.S8)
|
||||
{
|
||||
context.StoreGlobal8(addrLowOffset, addrHigh, value);
|
||||
}
|
||||
else if (size == LsSize2.U16 || size == LsSize2.S16)
|
||||
{
|
||||
context.StoreGlobal16(addrLowOffset, addrHigh, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.StoreGlobal(addrLowOffset, addrHigh, value);
|
||||
}
|
||||
context.Store(storageKind, addrLowOffset, addrHigh, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static StorageKind GetStorageKind(LsSize size)
|
||||
{
|
||||
return size switch
|
||||
{
|
||||
LsSize.U8 => StorageKind.GlobalMemoryU8,
|
||||
LsSize.S8 => StorageKind.GlobalMemoryS8,
|
||||
LsSize.U16 => StorageKind.GlobalMemoryU16,
|
||||
LsSize.S16 => StorageKind.GlobalMemoryS16,
|
||||
_ => StorageKind.GlobalMemory
|
||||
};
|
||||
}
|
||||
|
||||
private static int GetVectorCount(LsSize size)
|
||||
{
|
||||
switch (size)
|
||||
|
@ -79,10 +79,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||
ImageAtomic,
|
||||
IsNan,
|
||||
Load,
|
||||
LoadGlobal,
|
||||
LoadLocal,
|
||||
LoadShared,
|
||||
LoadStorage,
|
||||
Lod,
|
||||
LogarithmB2,
|
||||
LogicalAnd,
|
||||
@ -117,16 +115,10 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||
Sine,
|
||||
SquareRoot,
|
||||
Store,
|
||||
StoreGlobal,
|
||||
StoreGlobal16,
|
||||
StoreGlobal8,
|
||||
StoreLocal,
|
||||
StoreShared,
|
||||
StoreShared16,
|
||||
StoreShared8,
|
||||
StoreStorage,
|
||||
StoreStorage16,
|
||||
StoreStorage8,
|
||||
Subtract,
|
||||
SwizzleAdd,
|
||||
TextureSample,
|
||||
|
@ -11,7 +11,12 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
||||
StorageBuffer,
|
||||
LocalMemory,
|
||||
SharedMemory,
|
||||
GlobalMemory
|
||||
GlobalMemory,
|
||||
// TODO: Remove those and store type as a field on the Operation class itself.
|
||||
GlobalMemoryS8,
|
||||
GlobalMemoryS16,
|
||||
GlobalMemoryU8,
|
||||
GlobalMemoryU16
|
||||
}
|
||||
|
||||
static class StorageKindExtensions
|
||||
|
@ -11,7 +11,6 @@
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\AtomicMinMaxS32Shared.glsl" />
|
||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\AtomicMinMaxS32Storage.glsl" />
|
||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighS32.glsl" />
|
||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighU32.glsl" />
|
||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\Shuffle.glsl" />
|
||||
@ -19,7 +18,6 @@
|
||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleUp.glsl" />
|
||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleXor.glsl" />
|
||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreSharedSmallInt.glsl" />
|
||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreStorageSmallInt.glsl" />
|
||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\SwizzleAdd.glsl" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -6,7 +6,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
enum HelperFunctionsMask
|
||||
{
|
||||
AtomicMinMaxS32Shared = 1 << 0,
|
||||
AtomicMinMaxS32Storage = 1 << 1,
|
||||
MultiplyHighS32 = 1 << 2,
|
||||
MultiplyHighU32 = 1 << 3,
|
||||
Shuffle = 1 << 4,
|
||||
@ -14,7 +13,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
ShuffleUp = 1 << 6,
|
||||
ShuffleXor = 1 << 7,
|
||||
StoreSharedSmallInt = 1 << 8,
|
||||
StoreStorageSmallInt = 1 << 9,
|
||||
SwizzleAdd = 1 << 10,
|
||||
FSI = 1 << 11
|
||||
}
|
||||
|
@ -90,10 +90,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
Add(Instruction.ImageAtomic, AggregateType.S32);
|
||||
Add(Instruction.IsNan, AggregateType.Bool, AggregateType.Scalar);
|
||||
Add(Instruction.Load, AggregateType.FP32);
|
||||
Add(Instruction.LoadGlobal, AggregateType.U32, AggregateType.S32, AggregateType.S32);
|
||||
Add(Instruction.LoadLocal, AggregateType.U32, AggregateType.S32);
|
||||
Add(Instruction.LoadShared, AggregateType.U32, AggregateType.S32);
|
||||
Add(Instruction.LoadStorage, AggregateType.U32, AggregateType.S32, AggregateType.S32);
|
||||
Add(Instruction.Lod, AggregateType.FP32);
|
||||
Add(Instruction.LogarithmB2, AggregateType.Scalar, AggregateType.Scalar);
|
||||
Add(Instruction.LogicalAnd, AggregateType.Bool, AggregateType.Bool, AggregateType.Bool);
|
||||
@ -123,14 +121,10 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
Add(Instruction.Sine, AggregateType.Scalar, AggregateType.Scalar);
|
||||
Add(Instruction.SquareRoot, AggregateType.Scalar, AggregateType.Scalar);
|
||||
Add(Instruction.Store, AggregateType.Void);
|
||||
Add(Instruction.StoreGlobal, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32);
|
||||
Add(Instruction.StoreLocal, AggregateType.Void, AggregateType.S32, AggregateType.U32);
|
||||
Add(Instruction.StoreShared, AggregateType.Void, AggregateType.S32, AggregateType.U32);
|
||||
Add(Instruction.StoreShared16, AggregateType.Void, AggregateType.S32, AggregateType.U32);
|
||||
Add(Instruction.StoreShared8, AggregateType.Void, AggregateType.S32, AggregateType.U32);
|
||||
Add(Instruction.StoreStorage, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32);
|
||||
Add(Instruction.StoreStorage16, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32);
|
||||
Add(Instruction.StoreStorage8, AggregateType.Void, AggregateType.S32, AggregateType.S32, AggregateType.U32);
|
||||
Add(Instruction.Subtract, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
|
||||
Add(Instruction.SwizzleAdd, AggregateType.FP32, AggregateType.FP32, AggregateType.FP32, AggregateType.S32);
|
||||
Add(Instruction.TextureSample, AggregateType.FP32);
|
||||
@ -166,7 +160,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
{
|
||||
return AggregateType.FP32;
|
||||
}
|
||||
else if (inst == Instruction.Call || inst == Instruction.Load || inst == Instruction.Store)
|
||||
else if (inst == Instruction.Call || inst == Instruction.Load || inst == Instruction.Store || inst.IsAtomic())
|
||||
{
|
||||
return AggregateType.S32;
|
||||
}
|
||||
|
@ -5,17 +5,25 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
class ShaderProperties
|
||||
{
|
||||
private readonly Dictionary<int, BufferDefinition> _constantBuffers;
|
||||
private readonly Dictionary<int, BufferDefinition> _storageBuffers;
|
||||
|
||||
public IReadOnlyDictionary<int, BufferDefinition> ConstantBuffers => _constantBuffers;
|
||||
public IReadOnlyDictionary<int, BufferDefinition> StorageBuffers => _storageBuffers;
|
||||
|
||||
public ShaderProperties()
|
||||
{
|
||||
_constantBuffers = new Dictionary<int, BufferDefinition>();
|
||||
_storageBuffers = new Dictionary<int, BufferDefinition>();
|
||||
}
|
||||
|
||||
public void AddConstantBuffer(int binding, BufferDefinition definition)
|
||||
{
|
||||
_constantBuffers[binding] = definition;
|
||||
}
|
||||
|
||||
public void AddStorageBuffer(int binding, BufferDefinition definition)
|
||||
{
|
||||
_storageBuffers[binding] = definition;
|
||||
}
|
||||
}
|
||||
}
|
@ -280,10 +280,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
{
|
||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Shared;
|
||||
}
|
||||
else if (operation.StorageKind == StorageKind.StorageBuffer)
|
||||
{
|
||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.AtomicMinMaxS32Storage;
|
||||
}
|
||||
break;
|
||||
case Instruction.MultiplyHighS32:
|
||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.MultiplyHighS32;
|
||||
@ -307,10 +303,6 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
||||
case Instruction.StoreShared8:
|
||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.StoreSharedSmallInt;
|
||||
break;
|
||||
case Instruction.StoreStorage16:
|
||||
case Instruction.StoreStorage8:
|
||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.StoreStorageSmallInt;
|
||||
break;
|
||||
case Instruction.SwizzleAdd:
|
||||
context.Info.HelperFunctionsMask |= HelperFunctionsMask.SwizzleAdd;
|
||||
break;
|
||||
|
@ -246,7 +246,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
this.Store(StorageKind.Output, IoVariable.Position, null, Const(1), this.FPFusedMultiplyAdd(y, yScale, negativeOne));
|
||||
}
|
||||
|
||||
if (Config.Options.TargetApi == TargetApi.Vulkan && Config.GpuAccessor.QueryTransformDepthMinusOneToOne())
|
||||
if (Config.GpuAccessor.QueryTransformDepthMinusOneToOne() && !Config.GpuAccessor.QueryHostSupportsDepthClipControl())
|
||||
{
|
||||
Operand z = this.Load(StorageKind.Output, IoVariable.Position, null, Const(2));
|
||||
Operand w = this.Load(StorageKind.Output, IoVariable.Position, null, Const(3));
|
||||
@ -283,7 +283,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
oldYLocal = null;
|
||||
}
|
||||
|
||||
if (Config.Options.TargetApi == TargetApi.Vulkan && Config.GpuAccessor.QueryTransformDepthMinusOneToOne())
|
||||
if (Config.GpuAccessor.QueryTransformDepthMinusOneToOne() && !Config.GpuAccessor.QueryHostSupportsDepthClipControl())
|
||||
{
|
||||
oldZLocal = Local();
|
||||
this.Copy(oldZLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(2)));
|
||||
|
@ -57,6 +57,56 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
return context.Add(Instruction.AtomicXor, storageKind, Local(), a, b, c);
|
||||
}
|
||||
|
||||
public static Operand AtomicAdd(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value)
|
||||
{
|
||||
return context.Add(Instruction.AtomicAdd, storageKind, Local(), Const(binding), e0, e1, value);
|
||||
}
|
||||
|
||||
public static Operand AtomicAnd(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value)
|
||||
{
|
||||
return context.Add(Instruction.AtomicAnd, storageKind, Local(), Const(binding), e0, e1, value);
|
||||
}
|
||||
|
||||
public static Operand AtomicCompareAndSwap(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand compare, Operand value)
|
||||
{
|
||||
return context.Add(Instruction.AtomicCompareAndSwap, storageKind, Local(), Const(binding), e0, e1, compare, value);
|
||||
}
|
||||
|
||||
public static Operand AtomicMaxS32(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value)
|
||||
{
|
||||
return context.Add(Instruction.AtomicMaxS32, storageKind, Local(), Const(binding), e0, e1, value);
|
||||
}
|
||||
|
||||
public static Operand AtomicMaxU32(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value)
|
||||
{
|
||||
return context.Add(Instruction.AtomicMaxU32, storageKind, Local(), Const(binding), e0, e1, value);
|
||||
}
|
||||
|
||||
public static Operand AtomicMinS32(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value)
|
||||
{
|
||||
return context.Add(Instruction.AtomicMinS32, storageKind, Local(), Const(binding), e0, e1, value);
|
||||
}
|
||||
|
||||
public static Operand AtomicMinU32(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value)
|
||||
{
|
||||
return context.Add(Instruction.AtomicMinU32, storageKind, Local(), Const(binding), e0, e1, value);
|
||||
}
|
||||
|
||||
public static Operand AtomicOr(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value)
|
||||
{
|
||||
return context.Add(Instruction.AtomicOr, storageKind, Local(), Const(binding), e0, e1, value);
|
||||
}
|
||||
|
||||
public static Operand AtomicSwap(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value)
|
||||
{
|
||||
return context.Add(Instruction.AtomicSwap, storageKind, Local(), Const(binding), e0, e1, value);
|
||||
}
|
||||
|
||||
public static Operand AtomicXor(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value)
|
||||
{
|
||||
return context.Add(Instruction.AtomicXor, storageKind, Local(), Const(binding), e0, e1, value);
|
||||
}
|
||||
|
||||
public static Operand Ballot(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.Ballot, Local(), a);
|
||||
@ -554,6 +604,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
return context.Add(fpType | Instruction.IsNan, Local(), a);
|
||||
}
|
||||
|
||||
public static Operand Load(this EmitterContext context, StorageKind storageKind, Operand e0, Operand e1)
|
||||
{
|
||||
return context.Add(Instruction.Load, storageKind, Local(), e0, e1);
|
||||
}
|
||||
|
||||
public static Operand Load(this EmitterContext context, StorageKind storageKind, int binding)
|
||||
{
|
||||
return context.Add(Instruction.Load, storageKind, Local(), Const(binding));
|
||||
@ -606,11 +661,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
: context.Load(storageKind, (int)ioVariable, arrayIndex, elemIndex);
|
||||
}
|
||||
|
||||
public static Operand LoadGlobal(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.LoadGlobal, Local(), a, b);
|
||||
}
|
||||
|
||||
public static Operand LoadLocal(this EmitterContext context, Operand a)
|
||||
{
|
||||
return context.Add(Instruction.LoadLocal, Local(), a);
|
||||
@ -655,7 +705,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
|
||||
public static void Return(this EmitterContext context)
|
||||
{
|
||||
context.PrepareForReturn();
|
||||
context.Add(Instruction.Return);
|
||||
}
|
||||
|
||||
@ -699,6 +748,16 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
return context.Add(Instruction.ShuffleXor, (Local(), Local()), a, b, c);
|
||||
}
|
||||
|
||||
public static Operand Store(this EmitterContext context, StorageKind storageKind, Operand e0, Operand e1, Operand value)
|
||||
{
|
||||
return context.Add(Instruction.Store, storageKind, null, e0, e1, value);
|
||||
}
|
||||
|
||||
public static Operand Store(this EmitterContext context, StorageKind storageKind, int binding, Operand e0, Operand e1, Operand value)
|
||||
{
|
||||
return context.Add(Instruction.Store, storageKind, null, Const(binding), e0, e1, value);
|
||||
}
|
||||
|
||||
public static Operand Store(
|
||||
this EmitterContext context,
|
||||
StorageKind storageKind,
|
||||
@ -738,21 +797,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
: context.Add(Instruction.Store, storageKind, null, Const((int)ioVariable), arrayIndex, elemIndex, value);
|
||||
}
|
||||
|
||||
public static Operand StoreGlobal(this EmitterContext context, Operand a, Operand b, Operand c)
|
||||
{
|
||||
return context.Add(Instruction.StoreGlobal, null, a, b, c);
|
||||
}
|
||||
|
||||
public static Operand StoreGlobal16(this EmitterContext context, Operand a, Operand b, Operand c)
|
||||
{
|
||||
return context.Add(Instruction.StoreGlobal16, null, a, b, c);
|
||||
}
|
||||
|
||||
public static Operand StoreGlobal8(this EmitterContext context, Operand a, Operand b, Operand c)
|
||||
{
|
||||
return context.Add(Instruction.StoreGlobal8, null, a, b, c);
|
||||
}
|
||||
|
||||
public static Operand StoreLocal(this EmitterContext context, Operand a, Operand b)
|
||||
{
|
||||
return context.Add(Instruction.StoreLocal, null, a, b);
|
||||
|
@ -1,54 +0,0 @@
|
||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
static class GlobalMemory
|
||||
{
|
||||
private const int StorageDescsBaseOffset = 0x44; // In words.
|
||||
|
||||
public const int StorageDescSize = 4; // In words.
|
||||
public const int StorageMaxCount = 16;
|
||||
|
||||
public const int StorageDescsSize = StorageDescSize * StorageMaxCount;
|
||||
|
||||
public const int UbeBaseOffset = 0x98; // In words.
|
||||
public const int UbeMaxCount = 9;
|
||||
public const int UbeDescsSize = StorageDescSize * UbeMaxCount;
|
||||
public const int UbeFirstCbuf = 8;
|
||||
|
||||
public const int DriverReservedCb = 0;
|
||||
|
||||
public static bool UsesGlobalMemory(Instruction inst, StorageKind storageKind)
|
||||
{
|
||||
return (inst.IsAtomic() && storageKind == StorageKind.GlobalMemory) ||
|
||||
inst == Instruction.LoadGlobal ||
|
||||
inst == Instruction.StoreGlobal ||
|
||||
inst == Instruction.StoreGlobal16 ||
|
||||
inst == Instruction.StoreGlobal8;
|
||||
}
|
||||
|
||||
public static int GetStorageCbOffset(ShaderStage stage, int slot)
|
||||
{
|
||||
return GetStorageBaseCbOffset(stage) + slot * StorageDescSize;
|
||||
}
|
||||
|
||||
public static int GetStorageBaseCbOffset(ShaderStage stage)
|
||||
{
|
||||
return stage switch
|
||||
{
|
||||
ShaderStage.Compute => StorageDescsBaseOffset + 2 * StorageDescsSize,
|
||||
ShaderStage.Vertex => StorageDescsBaseOffset,
|
||||
ShaderStage.TessellationControl => StorageDescsBaseOffset + 1 * StorageDescsSize,
|
||||
ShaderStage.TessellationEvaluation => StorageDescsBaseOffset + 2 * StorageDescsSize,
|
||||
ShaderStage.Geometry => StorageDescsBaseOffset + 3 * StorageDescsSize,
|
||||
ShaderStage.Fragment => StorageDescsBaseOffset + 4 * StorageDescsSize,
|
||||
_ => 0
|
||||
};
|
||||
}
|
||||
|
||||
public static int GetConstantUbeOffset(int slot)
|
||||
{
|
||||
return UbeBaseOffset + slot * StorageDescSize;
|
||||
}
|
||||
}
|
||||
}
|
@ -19,6 +19,14 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
_stage = stage;
|
||||
}
|
||||
|
||||
public int AddFunction(Function function)
|
||||
{
|
||||
int functionId = _functionList.Count;
|
||||
_functionList.Add(function);
|
||||
|
||||
return functionId;
|
||||
}
|
||||
|
||||
public int GetOrCreateFunctionId(HelperFunctionName functionName)
|
||||
{
|
||||
if (_functionIds.TryGetValue(functionName, out int functionId))
|
||||
@ -27,8 +35,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
}
|
||||
|
||||
Function function = GenerateFunction(functionName);
|
||||
functionId = _functionList.Count;
|
||||
_functionList.Add(function);
|
||||
functionId = AddFunction(function);
|
||||
_functionIds.Add(functionName, functionId);
|
||||
|
||||
return functionId;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,17 +7,15 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
{
|
||||
static class Optimizer
|
||||
{
|
||||
public static void RunPass(BasicBlock[] blocks, ShaderConfig config)
|
||||
public static void RunPass(HelperFunctionManager hfm, BasicBlock[] blocks, ShaderConfig config)
|
||||
{
|
||||
RunOptimizationPasses(blocks, config);
|
||||
|
||||
int sbUseMask = 0;
|
||||
int ubeUseMask = 0;
|
||||
GlobalToStorage.RunPass(hfm, blocks, config);
|
||||
|
||||
// Those passes are looking for specific patterns and only needs to run once.
|
||||
for (int blkIndex = 0; blkIndex < blocks.Length; blkIndex++)
|
||||
{
|
||||
GlobalToStorage.RunPass(blocks[blkIndex], config, ref sbUseMask, ref ubeUseMask);
|
||||
BindlessToIndexed.RunPass(blocks[blkIndex], config);
|
||||
BindlessElimination.RunPass(blocks[blkIndex], config);
|
||||
|
||||
@ -28,8 +26,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
}
|
||||
}
|
||||
|
||||
config.SetAccessibleBufferMasks(sbUseMask, ubeUseMask);
|
||||
|
||||
// Run optimizations one last time to remove any code that is now optimizable after above passes.
|
||||
RunOptimizationPasses(blocks, config);
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
switch (operation.Inst)
|
||||
{
|
||||
case Instruction.Add:
|
||||
case Instruction.BitwiseExclusiveOr:
|
||||
TryEliminateBinaryOpCommutative(operation, 0);
|
||||
break;
|
||||
|
||||
@ -21,6 +20,13 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
TryEliminateBitwiseAnd(operation);
|
||||
break;
|
||||
|
||||
case Instruction.BitwiseExclusiveOr:
|
||||
if (!TryEliminateXorSwap(operation))
|
||||
{
|
||||
TryEliminateBinaryOpCommutative(operation, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case Instruction.BitwiseOr:
|
||||
TryEliminateBitwiseOr(operation);
|
||||
break;
|
||||
@ -49,8 +55,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
private static void TryEliminateBitwiseAnd(Operation operation)
|
||||
{
|
||||
// Try to recognize and optimize those 3 patterns (in order):
|
||||
// x & 0xFFFFFFFF == x, 0xFFFFFFFF & y == y,
|
||||
// x & 0x00000000 == 0x00000000, 0x00000000 & y == 0x00000000
|
||||
// x & 0xFFFFFFFF == x, 0xFFFFFFFF & y == y,
|
||||
// x & 0x00000000 == 0x00000000, 0x00000000 & y == 0x00000000
|
||||
|
||||
Operand x = operation.GetSource(0);
|
||||
Operand y = operation.GetSource(1);
|
||||
|
||||
@ -68,11 +75,62 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
}
|
||||
}
|
||||
|
||||
private static bool TryEliminateXorSwap(Operation xCopyOp)
|
||||
{
|
||||
// Try to recognize XOR swap pattern:
|
||||
// x = x ^ y
|
||||
// y = x ^ y
|
||||
// x = x ^ y
|
||||
// Or, in SSA:
|
||||
// x2 = x ^ y
|
||||
// y2 = x2 ^ y
|
||||
// x3 = x2 ^ y2
|
||||
// Transform it into something more sane:
|
||||
// temp = y
|
||||
// y = x
|
||||
// x = temp
|
||||
|
||||
// Note that because XOR is commutative, there are actually
|
||||
// multiple possible combinations of this pattern, for
|
||||
// simplicity this only catches one of them.
|
||||
|
||||
Operand x = xCopyOp.GetSource(0);
|
||||
Operand y = xCopyOp.GetSource(1);
|
||||
|
||||
if (x.AsgOp is not Operation tCopyOp || tCopyOp.Inst != Instruction.BitwiseExclusiveOr ||
|
||||
y.AsgOp is not Operation yCopyOp || yCopyOp.Inst != Instruction.BitwiseExclusiveOr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tCopyOp == yCopyOp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (yCopyOp.GetSource(0) != x ||
|
||||
yCopyOp.GetSource(1) != tCopyOp.GetSource(1) ||
|
||||
x.UseOps.Count != 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
x = tCopyOp.GetSource(0);
|
||||
y = tCopyOp.GetSource(1);
|
||||
|
||||
tCopyOp.TurnIntoCopy(y); // Temp = Y
|
||||
yCopyOp.TurnIntoCopy(x); // Y = X
|
||||
xCopyOp.TurnIntoCopy(tCopyOp.Dest); // X = Temp
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void TryEliminateBitwiseOr(Operation operation)
|
||||
{
|
||||
// Try to recognize and optimize those 3 patterns (in order):
|
||||
// x | 0x00000000 == x, 0x00000000 | y == y,
|
||||
// x | 0xFFFFFFFF == 0xFFFFFFFF, 0xFFFFFFFF | y == 0xFFFFFFFF
|
||||
// x | 0x00000000 == x, 0x00000000 | y == y,
|
||||
// x | 0xFFFFFFFF == 0xFFFFFFFF, 0xFFFFFFFF | y == 0xFFFFFFFF
|
||||
|
||||
Operand x = operation.GetSource(0);
|
||||
Operand y = operation.GetSource(1);
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
{
|
||||
@ -93,5 +94,17 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
public static void DeleteNode(LinkedListNode<INode> node, Operation operation)
|
||||
{
|
||||
node.List.Remove(node);
|
||||
|
||||
for (int srcIndex = 0; srcIndex < operation.SourcesCount; srcIndex++)
|
||||
{
|
||||
operation.SetSource(srcIndex, null);
|
||||
}
|
||||
|
||||
operation.Dest = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,9 +14,16 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
private readonly string _stagePrefix;
|
||||
|
||||
private readonly int[] _cbSlotToBindingMap;
|
||||
private readonly int[] _sbSlotToBindingMap;
|
||||
private uint _sbSlotWritten;
|
||||
|
||||
private readonly Dictionary<int, int> _sbSlots;
|
||||
private readonly Dictionary<int, int> _sbSlotsReverse;
|
||||
|
||||
private readonly HashSet<int> _usedConstantBufferBindings;
|
||||
|
||||
public ShaderProperties Properties => _properties;
|
||||
|
||||
public ResourceManager(ShaderStage stage, IGpuAccessor gpuAccessor, ShaderProperties properties)
|
||||
{
|
||||
_gpuAccessor = gpuAccessor;
|
||||
@ -24,7 +31,12 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
_stagePrefix = GetShaderStagePrefix(stage);
|
||||
|
||||
_cbSlotToBindingMap = new int[18];
|
||||
_sbSlotToBindingMap = new int[16];
|
||||
_cbSlotToBindingMap.AsSpan().Fill(-1);
|
||||
_sbSlotToBindingMap.AsSpan().Fill(-1);
|
||||
|
||||
_sbSlots = new Dictionary<int, int>();
|
||||
_sbSlotsReverse = new Dictionary<int, int>();
|
||||
|
||||
_usedConstantBufferBindings = new HashSet<int>();
|
||||
|
||||
@ -45,6 +57,52 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
return binding;
|
||||
}
|
||||
|
||||
public bool TryGetStorageBufferBinding(int sbCbSlot, int sbCbOffset, bool write, out int binding)
|
||||
{
|
||||
if (!TryGetSbSlot((byte)sbCbSlot, (ushort)sbCbOffset, out int slot))
|
||||
{
|
||||
binding = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
binding = _sbSlotToBindingMap[slot];
|
||||
|
||||
if (binding < 0)
|
||||
{
|
||||
binding = _gpuAccessor.QueryBindingStorageBuffer(slot);
|
||||
_sbSlotToBindingMap[slot] = binding;
|
||||
string slotNumber = slot.ToString(CultureInfo.InvariantCulture);
|
||||
AddNewStorageBuffer(binding, $"{_stagePrefix}_s{slotNumber}");
|
||||
}
|
||||
|
||||
if (write)
|
||||
{
|
||||
_sbSlotWritten |= 1u << slot;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryGetSbSlot(byte sbCbSlot, ushort sbCbOffset, out int slot)
|
||||
{
|
||||
int key = PackSbCbInfo(sbCbSlot, sbCbOffset);
|
||||
|
||||
if (!_sbSlots.TryGetValue(key, out slot))
|
||||
{
|
||||
slot = _sbSlots.Count;
|
||||
|
||||
if (slot >= _sbSlotToBindingMap.Length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_sbSlots.Add(key, slot);
|
||||
_sbSlotsReverse.Add(slot, key);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool TryGetConstantBufferSlot(int binding, out int slot)
|
||||
{
|
||||
for (slot = 0; slot < _cbSlotToBindingMap.Length; slot++)
|
||||
@ -88,6 +146,34 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
public BufferDescriptor[] GetStorageBufferDescriptors()
|
||||
{
|
||||
var descriptors = new BufferDescriptor[_sbSlots.Count];
|
||||
|
||||
int descriptorIndex = 0;
|
||||
|
||||
foreach ((int key, int slot) in _sbSlots)
|
||||
{
|
||||
int binding = _sbSlotToBindingMap[slot];
|
||||
|
||||
if (binding >= 0)
|
||||
{
|
||||
(int sbCbSlot, int sbCbOffset) = UnpackSbCbInfo(key);
|
||||
descriptors[descriptorIndex++] = new BufferDescriptor(binding, slot, sbCbSlot, sbCbOffset)
|
||||
{
|
||||
Flags = (_sbSlotWritten & (1u << slot)) != 0 ? BufferUsageFlags.Write : BufferUsageFlags.None
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (descriptors.Length != descriptorIndex)
|
||||
{
|
||||
Array.Resize(ref descriptors, descriptorIndex);
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
private void AddNewConstantBuffer(int binding, string name)
|
||||
{
|
||||
StructureType type = new StructureType(new[]
|
||||
@ -98,17 +184,14 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
_properties.AddConstantBuffer(binding, new BufferDefinition(BufferLayout.Std140, 0, binding, name, type));
|
||||
}
|
||||
|
||||
public void InheritFrom(ResourceManager other)
|
||||
private void AddNewStorageBuffer(int binding, string name)
|
||||
{
|
||||
for (int i = 0; i < other._cbSlotToBindingMap.Length; i++)
|
||||
StructureType type = new StructureType(new[]
|
||||
{
|
||||
int binding = other._cbSlotToBindingMap[i];
|
||||
new StructureField(AggregateType.Array | AggregateType.U32, "data", 0)
|
||||
});
|
||||
|
||||
if (binding >= 0)
|
||||
{
|
||||
_cbSlotToBindingMap[i] = binding;
|
||||
}
|
||||
}
|
||||
_properties.AddStorageBuffer(binding, new BufferDefinition(BufferLayout.Std430, 1, binding, name, type));
|
||||
}
|
||||
|
||||
public static string GetShaderStagePrefix(ShaderStage stage)
|
||||
@ -122,5 +205,15 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
|
||||
return _stagePrefixes[index];
|
||||
}
|
||||
|
||||
private static int PackSbCbInfo(int sbCbSlot, int sbCbOffset)
|
||||
{
|
||||
return sbCbOffset | ((int)sbCbSlot << 16);
|
||||
}
|
||||
|
||||
private static (int, int) UnpackSbCbInfo(int key)
|
||||
{
|
||||
return ((byte)(key >> 16), (ushort)key);
|
||||
}
|
||||
}
|
||||
}
|
@ -2,10 +2,8 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||
using static Ryujinx.Graphics.Shader.Translation.GlobalMemory;
|
||||
|
||||
namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
@ -23,11 +21,10 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
BasicBlock block = blocks[blkIndex];
|
||||
|
||||
for (LinkedListNode<INode> node = block.Operations.First; node != null;)
|
||||
for (LinkedListNode<INode> node = block.Operations.First; node != null; node = node.Next)
|
||||
{
|
||||
if (node.Value is not Operation operation)
|
||||
{
|
||||
node = node.Next;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -56,8 +53,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
InsertVectorComponentSelect(node, config);
|
||||
}
|
||||
|
||||
LinkedListNode<INode> nextNode = node.Next;
|
||||
|
||||
if (operation is TextureOperation texOp)
|
||||
{
|
||||
node = InsertTexelFetchScale(hfm, node, config);
|
||||
@ -74,15 +69,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
node = InsertSnormNormalization(node, config);
|
||||
}
|
||||
}
|
||||
|
||||
nextNode = node.Next;
|
||||
}
|
||||
else if (UsesGlobalMemory(operation.Inst, operation.StorageKind))
|
||||
{
|
||||
nextNode = RewriteGlobalAccess(node, config)?.Next ?? nextNode;
|
||||
}
|
||||
|
||||
node = nextNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -184,196 +171,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
operation.TurnIntoCopy(result);
|
||||
}
|
||||
|
||||
private static LinkedListNode<INode> RewriteGlobalAccess(LinkedListNode<INode> node, ShaderConfig config)
|
||||
{
|
||||
Operation operation = (Operation)node.Value;
|
||||
|
||||
bool isAtomic = operation.Inst.IsAtomic();
|
||||
bool isStg16Or8 = operation.Inst == Instruction.StoreGlobal16 || operation.Inst == Instruction.StoreGlobal8;
|
||||
bool isWrite = isAtomic || operation.Inst == Instruction.StoreGlobal || isStg16Or8;
|
||||
|
||||
Operation storageOp = null;
|
||||
|
||||
Operand PrependOperation(Instruction inst, params Operand[] sources)
|
||||
{
|
||||
Operand local = Local();
|
||||
|
||||
node.List.AddBefore(node, new Operation(inst, local, sources));
|
||||
|
||||
return local;
|
||||
}
|
||||
|
||||
Operand PrependStorageOperation(Instruction inst, StorageKind storageKind, params Operand[] sources)
|
||||
{
|
||||
Operand local = Local();
|
||||
|
||||
node.List.AddBefore(node, new Operation(inst, storageKind, local, sources));
|
||||
|
||||
return local;
|
||||
}
|
||||
|
||||
Operand PrependExistingOperation(Operation operation)
|
||||
{
|
||||
Operand local = Local();
|
||||
|
||||
operation.Dest = local;
|
||||
node.List.AddBefore(node, operation);
|
||||
|
||||
return local;
|
||||
}
|
||||
|
||||
Operand addrLow = operation.GetSource(0);
|
||||
Operand addrHigh = operation.GetSource(1);
|
||||
|
||||
Operand sbBaseAddrLow = Const(0);
|
||||
Operand sbSlot = Const(0);
|
||||
|
||||
Operand alignMask = Const(-config.GpuAccessor.QueryHostStorageBufferOffsetAlignment());
|
||||
|
||||
Operand BindingRangeCheck(int cbOffset, out Operand baseAddrLow)
|
||||
{
|
||||
baseAddrLow = Cbuf(DriverReservedCb, cbOffset);
|
||||
Operand baseAddrHigh = Cbuf(DriverReservedCb, cbOffset + 1);
|
||||
Operand size = Cbuf(DriverReservedCb, cbOffset + 2);
|
||||
|
||||
Operand offset = PrependOperation(Instruction.Subtract, addrLow, baseAddrLow);
|
||||
Operand borrow = PrependOperation(Instruction.CompareLessU32, addrLow, baseAddrLow);
|
||||
|
||||
Operand inRangeLow = PrependOperation(Instruction.CompareLessU32, offset, size);
|
||||
|
||||
Operand addrHighBorrowed = PrependOperation(Instruction.Add, addrHigh, borrow);
|
||||
|
||||
Operand inRangeHigh = PrependOperation(Instruction.CompareEqual, addrHighBorrowed, baseAddrHigh);
|
||||
|
||||
return PrependOperation(Instruction.BitwiseAnd, inRangeLow, inRangeHigh);
|
||||
}
|
||||
|
||||
int sbUseMask = config.AccessibleStorageBuffersMask;
|
||||
|
||||
while (sbUseMask != 0)
|
||||
{
|
||||
int slot = BitOperations.TrailingZeroCount(sbUseMask);
|
||||
|
||||
sbUseMask &= ~(1 << slot);
|
||||
|
||||
int cbOffset = GetStorageCbOffset(config.Stage, slot);
|
||||
slot = config.GetSbSlot(DriverReservedCb, (ushort)cbOffset);
|
||||
|
||||
config.SetUsedStorageBuffer(slot, isWrite);
|
||||
|
||||
Operand inRange = BindingRangeCheck(cbOffset, out Operand baseAddrLow);
|
||||
|
||||
sbBaseAddrLow = PrependOperation(Instruction.ConditionalSelect, inRange, baseAddrLow, sbBaseAddrLow);
|
||||
sbSlot = PrependOperation(Instruction.ConditionalSelect, inRange, Const(slot), sbSlot);
|
||||
}
|
||||
|
||||
if (config.AccessibleStorageBuffersMask != 0)
|
||||
{
|
||||
Operand baseAddrTrunc = PrependOperation(Instruction.BitwiseAnd, sbBaseAddrLow, alignMask);
|
||||
Operand byteOffset = PrependOperation(Instruction.Subtract, addrLow, baseAddrTrunc);
|
||||
|
||||
Operand[] sources = new Operand[operation.SourcesCount];
|
||||
|
||||
sources[0] = sbSlot;
|
||||
|
||||
if (isStg16Or8)
|
||||
{
|
||||
sources[1] = byteOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
sources[1] = PrependOperation(Instruction.ShiftRightU32, byteOffset, Const(2));
|
||||
}
|
||||
|
||||
for (int index = 2; index < operation.SourcesCount; index++)
|
||||
{
|
||||
sources[index] = operation.GetSource(index);
|
||||
}
|
||||
|
||||
if (isAtomic)
|
||||
{
|
||||
storageOp = new Operation(operation.Inst, StorageKind.StorageBuffer, operation.Dest, sources);
|
||||
}
|
||||
else if (operation.Inst == Instruction.LoadGlobal)
|
||||
{
|
||||
storageOp = new Operation(Instruction.LoadStorage, operation.Dest, sources);
|
||||
}
|
||||
else
|
||||
{
|
||||
Instruction storeInst = operation.Inst switch
|
||||
{
|
||||
Instruction.StoreGlobal16 => Instruction.StoreStorage16,
|
||||
Instruction.StoreGlobal8 => Instruction.StoreStorage8,
|
||||
_ => Instruction.StoreStorage
|
||||
};
|
||||
|
||||
storageOp = new Operation(storeInst, null, sources);
|
||||
}
|
||||
}
|
||||
else if (operation.Dest != null)
|
||||
{
|
||||
storageOp = new Operation(Instruction.Copy, operation.Dest, Const(0));
|
||||
}
|
||||
|
||||
if (operation.Inst == Instruction.LoadGlobal)
|
||||
{
|
||||
int cbeUseMask = config.AccessibleConstantBuffersMask;
|
||||
|
||||
while (cbeUseMask != 0)
|
||||
{
|
||||
int slot = BitOperations.TrailingZeroCount(cbeUseMask);
|
||||
int cbSlot = UbeFirstCbuf + slot;
|
||||
|
||||
cbeUseMask &= ~(1 << slot);
|
||||
|
||||
Operand previousResult = PrependExistingOperation(storageOp);
|
||||
|
||||
int cbOffset = GetConstantUbeOffset(slot);
|
||||
|
||||
Operand inRange = BindingRangeCheck(cbOffset, out Operand baseAddrLow);
|
||||
|
||||
Operand baseAddrTruncConst = PrependOperation(Instruction.BitwiseAnd, baseAddrLow, alignMask);
|
||||
Operand byteOffsetConst = PrependOperation(Instruction.Subtract, addrLow, baseAddrTruncConst);
|
||||
|
||||
Operand cbIndex = PrependOperation(Instruction.ShiftRightU32, byteOffsetConst, Const(2));
|
||||
Operand vecIndex = PrependOperation(Instruction.ShiftRightU32, cbIndex, Const(2));
|
||||
Operand elemIndex = PrependOperation(Instruction.BitwiseAnd, cbIndex, Const(3));
|
||||
|
||||
Operand[] sourcesCb = new Operand[4];
|
||||
|
||||
sourcesCb[0] = Const(config.ResourceManager.GetConstantBufferBinding(cbSlot));
|
||||
sourcesCb[1] = Const(0);
|
||||
sourcesCb[2] = vecIndex;
|
||||
sourcesCb[3] = elemIndex;
|
||||
|
||||
Operand ldcResult = PrependStorageOperation(Instruction.Load, StorageKind.ConstantBuffer, sourcesCb);
|
||||
|
||||
storageOp = new Operation(Instruction.ConditionalSelect, operation.Dest, inRange, ldcResult, previousResult);
|
||||
}
|
||||
}
|
||||
|
||||
for (int index = 0; index < operation.SourcesCount; index++)
|
||||
{
|
||||
operation.SetSource(index, null);
|
||||
}
|
||||
|
||||
LinkedListNode<INode> oldNode = node;
|
||||
LinkedList<INode> oldNodeList = oldNode.List;
|
||||
|
||||
if (storageOp != null)
|
||||
{
|
||||
node = node.List.AddBefore(node, storageOp);
|
||||
}
|
||||
else
|
||||
{
|
||||
node = null;
|
||||
}
|
||||
|
||||
oldNodeList.Remove(oldNode);
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
private static LinkedListNode<INode> InsertTexelFetchScale(HelperFunctionManager hfm, LinkedListNode<INode> node, ShaderConfig config)
|
||||
{
|
||||
TextureOperation texOp = (TextureOperation)node.Value;
|
||||
|
@ -39,9 +39,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
|
||||
public TranslationOptions Options { get; }
|
||||
|
||||
public ShaderProperties Properties { get; }
|
||||
public ShaderProperties Properties => ResourceManager.Properties;
|
||||
|
||||
public ResourceManager ResourceManager { get; }
|
||||
public ResourceManager ResourceManager { get; set; }
|
||||
|
||||
public bool TransformFeedbackEnabled { get; }
|
||||
|
||||
@ -110,12 +110,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
public UInt128 NextInputAttributesComponents { get; private set; }
|
||||
public UInt128 ThisInputAttributesComponents { get; private set; }
|
||||
|
||||
public int AccessibleStorageBuffersMask { get; private set; }
|
||||
public int AccessibleConstantBuffersMask { get; private set; }
|
||||
|
||||
private int _usedStorageBuffers;
|
||||
private int _usedStorageBuffersWrite;
|
||||
|
||||
private readonly record struct TextureInfo(int CbufSlot, int Handle, bool Indexed, TextureFormat Format);
|
||||
|
||||
private struct TextureMeta
|
||||
@ -127,18 +121,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
|
||||
private readonly Dictionary<TextureInfo, TextureMeta> _usedTextures;
|
||||
private readonly Dictionary<TextureInfo, TextureMeta> _usedImages;
|
||||
|
||||
private readonly Dictionary<int, int> _sbSlots;
|
||||
private readonly Dictionary<int, int> _sbSlotsReverse;
|
||||
|
||||
private BufferDescriptor[] _cachedStorageBufferDescriptors;
|
||||
private TextureDescriptor[] _cachedTextureDescriptors;
|
||||
private TextureDescriptor[] _cachedImageDescriptors;
|
||||
|
||||
private int _firstStorageBufferBinding;
|
||||
|
||||
public int FirstStorageBufferBinding => _firstStorageBufferBinding;
|
||||
|
||||
public ShaderConfig(ShaderStage stage, IGpuAccessor gpuAccessor, TranslationOptions options)
|
||||
{
|
||||
Stage = stage;
|
||||
@ -147,20 +132,13 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
|
||||
_transformFeedbackDefinitions = new Dictionary<TransformFeedbackVariable, TransformFeedbackOutput>();
|
||||
|
||||
AccessibleStorageBuffersMask = (1 << GlobalMemory.StorageMaxCount) - 1;
|
||||
AccessibleConstantBuffersMask = (1 << GlobalMemory.UbeMaxCount) - 1;
|
||||
|
||||
UsedInputAttributesPerPatch = new HashSet<int>();
|
||||
UsedOutputAttributesPerPatch = new HashSet<int>();
|
||||
|
||||
_usedTextures = new Dictionary<TextureInfo, TextureMeta>();
|
||||
_usedImages = new Dictionary<TextureInfo, TextureMeta>();
|
||||
|
||||
_sbSlots = new Dictionary<int, int>();
|
||||
_sbSlotsReverse = new Dictionary<int, int>();
|
||||
|
||||
Properties = new ShaderProperties();
|
||||
ResourceManager = new ResourceManager(stage, gpuAccessor, Properties);
|
||||
ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties());
|
||||
}
|
||||
|
||||
public ShaderConfig(
|
||||
@ -174,11 +152,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
OutputTopology = outputTopology;
|
||||
MaxOutputVertices = maxOutputVertices;
|
||||
TransformFeedbackEnabled = gpuAccessor.QueryTransformFeedbackEnabled();
|
||||
|
||||
if (Stage != ShaderStage.Compute)
|
||||
{
|
||||
AccessibleConstantBuffersMask = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public ShaderConfig(ShaderHeader header, IGpuAccessor gpuAccessor, TranslationOptions options) : this(header.Stage, gpuAccessor, options)
|
||||
@ -429,15 +402,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
|
||||
public void InheritFrom(ShaderConfig other)
|
||||
{
|
||||
ResourceManager.InheritFrom(other.ResourceManager);
|
||||
|
||||
ClipDistancesWritten |= other.ClipDistancesWritten;
|
||||
UsedFeatures |= other.UsedFeatures;
|
||||
|
||||
UsedInputAttributes |= other.UsedInputAttributes;
|
||||
UsedOutputAttributes |= other.UsedOutputAttributes;
|
||||
_usedStorageBuffers |= other._usedStorageBuffers;
|
||||
_usedStorageBuffersWrite |= other._usedStorageBuffersWrite;
|
||||
|
||||
foreach (var kv in other._usedTextures)
|
||||
{
|
||||
@ -637,23 +606,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
UsedFeatures |= flags;
|
||||
}
|
||||
|
||||
public void SetAccessibleBufferMasks(int sbMask, int ubeMask)
|
||||
{
|
||||
AccessibleStorageBuffersMask = sbMask;
|
||||
AccessibleConstantBuffersMask = ubeMask;
|
||||
}
|
||||
|
||||
public void SetUsedStorageBuffer(int slot, bool write)
|
||||
{
|
||||
int mask = 1 << slot;
|
||||
_usedStorageBuffers |= mask;
|
||||
|
||||
if (write)
|
||||
{
|
||||
_usedStorageBuffersWrite |= mask;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetUsedTexture(
|
||||
Instruction inst,
|
||||
SamplerType type,
|
||||
@ -759,76 +711,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
return meta;
|
||||
}
|
||||
|
||||
public BufferDescriptor[] GetStorageBufferDescriptors()
|
||||
{
|
||||
if (_cachedStorageBufferDescriptors != null)
|
||||
{
|
||||
return _cachedStorageBufferDescriptors;
|
||||
}
|
||||
|
||||
return _cachedStorageBufferDescriptors = GetStorageBufferDescriptors(
|
||||
_usedStorageBuffers,
|
||||
_usedStorageBuffersWrite,
|
||||
true,
|
||||
out _firstStorageBufferBinding,
|
||||
GpuAccessor.QueryBindingStorageBuffer);
|
||||
}
|
||||
|
||||
private BufferDescriptor[] GetStorageBufferDescriptors(
|
||||
int usedMask,
|
||||
int writtenMask,
|
||||
bool isArray,
|
||||
out int firstBinding,
|
||||
Func<int, int> getBindingCallback)
|
||||
{
|
||||
firstBinding = 0;
|
||||
bool hasFirstBinding = false;
|
||||
var descriptors = new BufferDescriptor[BitOperations.PopCount((uint)usedMask)];
|
||||
|
||||
int lastSlot = -1;
|
||||
|
||||
for (int i = 0; i < descriptors.Length; i++)
|
||||
{
|
||||
int slot = BitOperations.TrailingZeroCount(usedMask);
|
||||
|
||||
if (isArray)
|
||||
{
|
||||
// The next array entries also consumes bindings, even if they are unused.
|
||||
for (int j = lastSlot + 1; j < slot; j++)
|
||||
{
|
||||
int binding = getBindingCallback(j);
|
||||
|
||||
if (!hasFirstBinding)
|
||||
{
|
||||
firstBinding = binding;
|
||||
hasFirstBinding = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastSlot = slot;
|
||||
|
||||
(int sbCbSlot, int sbCbOffset) = GetSbCbInfo(slot);
|
||||
|
||||
descriptors[i] = new BufferDescriptor(getBindingCallback(slot), slot, sbCbSlot, sbCbOffset);
|
||||
|
||||
if (!hasFirstBinding)
|
||||
{
|
||||
firstBinding = descriptors[i].Binding;
|
||||
hasFirstBinding = true;
|
||||
}
|
||||
|
||||
if ((writtenMask & (1 << slot)) != 0)
|
||||
{
|
||||
descriptors[i].SetFlag(BufferUsageFlags.Write);
|
||||
}
|
||||
|
||||
usedMask &= ~(1 << slot);
|
||||
}
|
||||
|
||||
return descriptors;
|
||||
}
|
||||
|
||||
public TextureDescriptor[] GetTextureDescriptors()
|
||||
{
|
||||
return _cachedTextureDescriptors ??= GetTextureOrImageDescriptors(_usedTextures, GpuAccessor.QueryBindingTexture);
|
||||
@ -925,45 +807,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
return FindDescriptorIndex(GetImageDescriptors(), texOp);
|
||||
}
|
||||
|
||||
public int GetSbSlot(byte sbCbSlot, ushort sbCbOffset)
|
||||
{
|
||||
int key = PackSbCbInfo(sbCbSlot, sbCbOffset);
|
||||
|
||||
if (!_sbSlots.TryGetValue(key, out int slot))
|
||||
{
|
||||
slot = _sbSlots.Count;
|
||||
_sbSlots.Add(key, slot);
|
||||
_sbSlotsReverse.Add(slot, key);
|
||||
}
|
||||
|
||||
return slot;
|
||||
}
|
||||
|
||||
public (int, int) GetSbCbInfo(int slot)
|
||||
{
|
||||
if (_sbSlotsReverse.TryGetValue(slot, out int key))
|
||||
{
|
||||
return UnpackSbCbInfo(key);
|
||||
}
|
||||
|
||||
throw new ArgumentException($"Invalid slot {slot}.", nameof(slot));
|
||||
}
|
||||
|
||||
private static int PackSbCbInfo(int sbCbSlot, int sbCbOffset)
|
||||
{
|
||||
return sbCbOffset | ((int)sbCbSlot << 16);
|
||||
}
|
||||
|
||||
private static (int, int) UnpackSbCbInfo(int key)
|
||||
{
|
||||
return ((byte)(key >> 16), (ushort)key);
|
||||
}
|
||||
|
||||
public ShaderProgramInfo CreateProgramInfo(ShaderIdentification identification = ShaderIdentification.None)
|
||||
{
|
||||
return new ShaderProgramInfo(
|
||||
ResourceManager.GetConstantBufferDescriptors(),
|
||||
GetStorageBufferDescriptors(),
|
||||
ResourceManager.GetStorageBufferDescriptors(),
|
||||
GetTextureDescriptors(),
|
||||
GetImageDescriptors(),
|
||||
identification,
|
||||
|
@ -48,7 +48,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsResourceWrite(operation.Inst))
|
||||
if (IsResourceWrite(operation.Inst, operation.StorageKind))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -154,7 +154,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
return totalVerticesCount + verticesCount == 3 && writesLayer;
|
||||
}
|
||||
|
||||
private static bool IsResourceWrite(Instruction inst)
|
||||
private static bool IsResourceWrite(Instruction inst, StorageKind storageKind)
|
||||
{
|
||||
switch (inst)
|
||||
{
|
||||
@ -170,13 +170,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
case Instruction.AtomicXor:
|
||||
case Instruction.ImageAtomic:
|
||||
case Instruction.ImageStore:
|
||||
case Instruction.StoreGlobal:
|
||||
case Instruction.StoreGlobal16:
|
||||
case Instruction.StoreGlobal8:
|
||||
case Instruction.StoreStorage:
|
||||
case Instruction.StoreStorage16:
|
||||
case Instruction.StoreStorage8:
|
||||
return true;
|
||||
case Instruction.Store:
|
||||
return storageKind == StorageKind.StorageBuffer ||
|
||||
storageKind == StorageKind.SharedMemory ||
|
||||
storageKind == StorageKind.LocalMemory;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -78,7 +78,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
|
||||
Ssa.Rename(cfg.Blocks);
|
||||
|
||||
Optimizer.RunPass(cfg.Blocks, config);
|
||||
Optimizer.RunPass(hfm, cfg.Blocks, config);
|
||||
Rewriter.RunPass(hfm, cfg.Blocks, config);
|
||||
}
|
||||
|
||||
|
@ -155,6 +155,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
other._config.MergeOutputUserAttributes(_config.UsedOutputAttributes, Enumerable.Empty<int>());
|
||||
|
||||
// We need to share the resource manager since both shaders accesses the same constant buffers.
|
||||
other._config.ResourceManager = _config.ResourceManager;
|
||||
|
||||
FunctionCode[] otherCode = EmitShader(other._program, other._config, initializeOutputs: true, out int aStart);
|
||||
|
||||
code = Combine(otherCode, code, aStart);
|
||||
|
@ -143,7 +143,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
mipGobBlocksInY >>= 1;
|
||||
}
|
||||
|
||||
while (d <= (mipGobBlocksInZ >> 1) && mipGobBlocksInZ != 1)
|
||||
if (level > 0 && d <= (mipGobBlocksInZ >> 1) && mipGobBlocksInZ != 1)
|
||||
{
|
||||
mipGobBlocksInZ >>= 1;
|
||||
}
|
||||
@ -407,7 +407,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
mipGobBlocksInY >>= 1;
|
||||
}
|
||||
|
||||
while (d <= (mipGobBlocksInZ >> 1) && mipGobBlocksInZ != 1)
|
||||
if (level > 0 && d <= (mipGobBlocksInZ >> 1) && mipGobBlocksInZ != 1)
|
||||
{
|
||||
mipGobBlocksInZ >>= 1;
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
int gobBlocksInTileX,
|
||||
int gpuLayerSize = 0)
|
||||
{
|
||||
bool is3D = depth > 1;
|
||||
bool is3D = depth > 1 || gobBlocksInZ > 1;
|
||||
|
||||
int layerSize = 0;
|
||||
|
||||
@ -67,7 +67,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
mipGobBlocksInY >>= 1;
|
||||
}
|
||||
|
||||
while (d <= (mipGobBlocksInZ >> 1) && mipGobBlocksInZ != 1)
|
||||
if (level > 0 && d <= (mipGobBlocksInZ >> 1) && mipGobBlocksInZ != 1)
|
||||
{
|
||||
mipGobBlocksInZ >>= 1;
|
||||
}
|
||||
@ -88,6 +88,10 @@ namespace Ryujinx.Graphics.Texture
|
||||
|
||||
int robSize = widthInGobs * mipGobBlocksInY * mipGobBlocksInZ * GobSize;
|
||||
|
||||
mipOffsets[level] = layerSize;
|
||||
sliceSizes[level] = totalBlocksOfGobsInY * robSize;
|
||||
levelSizes[level] = totalBlocksOfGobsInZ * sliceSizes[level];
|
||||
|
||||
if (is3D)
|
||||
{
|
||||
int gobSize = mipGobBlocksInY * GobSize;
|
||||
@ -105,11 +109,22 @@ namespace Ryujinx.Graphics.Texture
|
||||
|
||||
allOffsets[z + depthLevelOffset] = baseOffset + zLow * gobSize + zHigh * sliceSize;
|
||||
}
|
||||
}
|
||||
|
||||
mipOffsets[level] = layerSize;
|
||||
sliceSizes[level] = totalBlocksOfGobsInY * robSize;
|
||||
levelSizes[level] = totalBlocksOfGobsInZ * sliceSizes[level];
|
||||
int gobRemainderZ = d % mipGobBlocksInZ;
|
||||
|
||||
if (gobRemainderZ != 0 && level == levels - 1)
|
||||
{
|
||||
// The slice only covers up to the end of this slice's depth, rather than the full aligned size.
|
||||
// Avoids size being too large on partial views of 3d textures.
|
||||
|
||||
levelSizes[level] -= gobSize * (mipGobBlocksInZ - gobRemainderZ);
|
||||
|
||||
if (sliceSizes[level] > levelSizes[level])
|
||||
{
|
||||
sliceSizes[level] = levelSizes[level];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layerSize += levelSizes[level];
|
||||
|
||||
@ -267,7 +282,8 @@ namespace Ryujinx.Graphics.Texture
|
||||
int depth,
|
||||
int blockHeight,
|
||||
int gobBlocksInY,
|
||||
int gobBlocksInZ)
|
||||
int gobBlocksInZ,
|
||||
int level = int.MaxValue)
|
||||
{
|
||||
height = BitUtils.DivRoundUp(height, blockHeight);
|
||||
|
||||
@ -276,7 +292,7 @@ namespace Ryujinx.Graphics.Texture
|
||||
gobBlocksInY >>= 1;
|
||||
}
|
||||
|
||||
while (depth <= (gobBlocksInZ >> 1) && gobBlocksInZ != 1)
|
||||
while (level-- > 0 && depth <= (gobBlocksInZ >> 1) && gobBlocksInZ != 1)
|
||||
{
|
||||
gobBlocksInZ >>= 1;
|
||||
}
|
||||
|
@ -115,8 +115,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
holder = Create(gd, size, baseType: baseType, storageHint: storageHint);
|
||||
if (holder == null)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X} and type \"{baseType}\".");
|
||||
|
||||
return BufferHandle.Null;
|
||||
}
|
||||
|
||||
@ -264,6 +262,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return holder;
|
||||
}
|
||||
|
||||
Logger.Error?.Print(LogClass.Gpu, $"Failed to create buffer with size 0x{size:X} and type \"{baseType}\".");
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -70,30 +70,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void UpdateStorageBuffers(int setIndex, int baseBinding, ReadOnlySpan<DescriptorBufferInfo> bufferInfo)
|
||||
{
|
||||
if (bufferInfo.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fixed (DescriptorBufferInfo* pBufferInfo = bufferInfo)
|
||||
{
|
||||
var writeDescriptorSet = new WriteDescriptorSet
|
||||
{
|
||||
SType = StructureType.WriteDescriptorSet,
|
||||
DstSet = _descriptorSets[setIndex],
|
||||
DstBinding = (uint)(baseBinding & ~(Constants.MaxStorageBuffersPerStage - 1)),
|
||||
DstArrayElement = (uint)(baseBinding & (Constants.MaxStorageBuffersPerStage - 1)),
|
||||
DescriptorType = DescriptorType.StorageBuffer,
|
||||
DescriptorCount = (uint)bufferInfo.Length,
|
||||
PBufferInfo = pBufferInfo
|
||||
};
|
||||
|
||||
_holder.Api.UpdateDescriptorSets(_holder.Device, 1, writeDescriptorSet, 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
public unsafe void UpdateImage(int setIndex, int bindingIndex, DescriptorImageInfo imageInfo, DescriptorType type)
|
||||
{
|
||||
if (imageInfo.ImageView.Handle != 0UL)
|
||||
|
@ -448,14 +448,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
|
||||
ReadOnlySpan<DescriptorBufferInfo> storageBuffers = _storageBuffers;
|
||||
if (program.HasMinimalLayout)
|
||||
{
|
||||
dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
dsc.UpdateStorageBuffers(0, binding, storageBuffers.Slice(binding, count));
|
||||
}
|
||||
dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer);
|
||||
}
|
||||
else if (setIndex == PipelineBase.TextureSetIndex)
|
||||
{
|
||||
|
@ -96,8 +96,6 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
{
|
||||
var originalInfo = view.Info;
|
||||
|
||||
var swapRB = originalInfo.Format.IsBgr() && originalInfo.SwizzleR == SwizzleComponent.Red;
|
||||
|
||||
var info = new TextureCreateInfo(
|
||||
width,
|
||||
height,
|
||||
@ -110,9 +108,9 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
originalInfo.Format,
|
||||
originalInfo.DepthStencilMode,
|
||||
originalInfo.Target,
|
||||
swapRB ? originalInfo.SwizzleB : originalInfo.SwizzleR,
|
||||
originalInfo.SwizzleR,
|
||||
originalInfo.SwizzleG,
|
||||
swapRB ? originalInfo.SwizzleR : originalInfo.SwizzleB,
|
||||
originalInfo.SwizzleB,
|
||||
originalInfo.SwizzleA);
|
||||
_intermediaryTexture?.Dispose();
|
||||
_intermediaryTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView;
|
||||
@ -155,7 +153,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
|
||||
var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize);
|
||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) });
|
||||
_pipeline.SetImage(0, _intermediaryTexture, GAL.Format.R8G8B8A8Unorm);
|
||||
_pipeline.SetImage(0, _intermediaryTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
_pipeline.ComputeBarrier();
|
||||
|
||||
|
@ -56,28 +56,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
if (_texture == null || _texture.Width != view.Width || _texture.Height != view.Height)
|
||||
{
|
||||
_texture?.Dispose();
|
||||
|
||||
var info = view.Info;
|
||||
|
||||
if (view.Info.Format.IsBgr())
|
||||
{
|
||||
info = new TextureCreateInfo(info.Width,
|
||||
info.Height,
|
||||
info.Depth,
|
||||
info.Levels,
|
||||
info.Samples,
|
||||
info.BlockWidth,
|
||||
info.BlockHeight,
|
||||
info.BytesPerPixel,
|
||||
info.Format,
|
||||
info.DepthStencilMode,
|
||||
info.Target,
|
||||
info.SwizzleB,
|
||||
info.SwizzleG,
|
||||
info.SwizzleR,
|
||||
info.SwizzleA);
|
||||
}
|
||||
_texture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView;
|
||||
_texture = _renderer.CreateTexture(view.Info, view.ScaleFactor) as TextureView;
|
||||
}
|
||||
|
||||
_pipeline.SetCommandBuffer(cbs);
|
||||
@ -96,7 +75,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize);
|
||||
var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize);
|
||||
|
||||
_pipeline.SetImage(0, _texture, GAL.Format.R8G8B8A8Unorm);
|
||||
_pipeline.SetImage(0, _texture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
|
||||
_renderer.BufferManager.Delete(bufferHandle);
|
||||
|
@ -4,7 +4,6 @@ using Ryujinx.Graphics.Shader;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using Format = Ryujinx.Graphics.GAL.Format;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
{
|
||||
@ -149,7 +148,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
Format.R8G8Unorm,
|
||||
GAL.Format.R8G8Unorm,
|
||||
DepthStencilMode.Depth,
|
||||
Target.Texture2D,
|
||||
SwizzleComponent.Red,
|
||||
@ -165,7 +164,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
Format.R8Unorm,
|
||||
GAL.Format.R8Unorm,
|
||||
DepthStencilMode.Depth,
|
||||
Target.Texture2D,
|
||||
SwizzleComponent.Red,
|
||||
@ -192,30 +191,9 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
_edgeOutputTexture?.Dispose();
|
||||
_blendOutputTexture?.Dispose();
|
||||
|
||||
var info = view.Info;
|
||||
|
||||
if (view.Info.Format.IsBgr())
|
||||
{
|
||||
info = new TextureCreateInfo(info.Width,
|
||||
info.Height,
|
||||
info.Depth,
|
||||
info.Levels,
|
||||
info.Samples,
|
||||
info.BlockWidth,
|
||||
info.BlockHeight,
|
||||
info.BytesPerPixel,
|
||||
info.Format,
|
||||
info.DepthStencilMode,
|
||||
info.Target,
|
||||
info.SwizzleB,
|
||||
info.SwizzleG,
|
||||
info.SwizzleR,
|
||||
info.SwizzleA);
|
||||
}
|
||||
|
||||
_outputTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView;
|
||||
_edgeOutputTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView;
|
||||
_blendOutputTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView;
|
||||
_outputTexture = _renderer.CreateTexture(view.Info, view.ScaleFactor) as TextureView;
|
||||
_edgeOutputTexture = _renderer.CreateTexture(view.Info, view.ScaleFactor) as TextureView;
|
||||
_blendOutputTexture = _renderer.CreateTexture(view.Info, view.ScaleFactor) as TextureView;
|
||||
}
|
||||
|
||||
_pipeline.SetCommandBuffer(cbs);
|
||||
@ -240,7 +218,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
_renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer);
|
||||
var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize);
|
||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) });
|
||||
_pipeline.SetImage(0, _edgeOutputTexture, GAL.Format.R8G8B8A8Unorm);
|
||||
_pipeline.SetImage(0, _edgeOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
_pipeline.ComputeBarrier();
|
||||
|
||||
@ -250,7 +228,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _edgeOutputTexture, _samplerLinear);
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _areaTexture, _samplerLinear);
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 4, _searchTexture, _samplerLinear);
|
||||
_pipeline.SetImage(0, _blendOutputTexture, GAL.Format.R8G8B8A8Unorm);
|
||||
_pipeline.SetImage(0, _blendOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
_pipeline.ComputeBarrier();
|
||||
|
||||
@ -259,7 +237,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
_pipeline.Specialize(_specConstants);
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _blendOutputTexture, _samplerLinear);
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear);
|
||||
_pipeline.SetImage(0, _outputTexture, GAL.Format.R8G8B8A8Unorm);
|
||||
_pipeline.SetImage(0, _outputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
_pipeline.ComputeBarrier();
|
||||
|
||||
|
@ -65,6 +65,13 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return (formatFeatureFlags & flags) == flags;
|
||||
}
|
||||
|
||||
public bool BufferFormatSupports(FormatFeatureFlags flags, VkFormat format)
|
||||
{
|
||||
_api.GetPhysicalDeviceFormatProperties(_physicalDevice, format, out var fp);
|
||||
|
||||
return (fp.BufferFeatures & flags) == flags;
|
||||
}
|
||||
|
||||
public bool OptimalFormatSupports(FormatFeatureFlags flags, GAL.Format format)
|
||||
{
|
||||
var formatFeatureFlags = _optimalTable[(int)format];
|
||||
|
@ -169,6 +169,16 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return _table[(int)format];
|
||||
}
|
||||
|
||||
public static Format ConvertRgba8SrgbToUnorm(Format format)
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
Format.R8G8B8A8Srgb => Format.R8G8B8A8Unorm,
|
||||
Format.B8G8R8A8Srgb => Format.B8G8R8A8Unorm,
|
||||
_ => format
|
||||
};
|
||||
}
|
||||
|
||||
public static int GetAttributeFormatSize(VkFormat format)
|
||||
{
|
||||
switch (format)
|
||||
|
@ -21,6 +21,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
public uint[] AttachmentSamples { get; }
|
||||
public VkFormat[] AttachmentFormats { get; }
|
||||
public int[] AttachmentIndices { get; }
|
||||
public uint AttachmentIntegerFormatMask { get; }
|
||||
|
||||
public int AttachmentsCount { get; }
|
||||
public int MaxColorAttachmentIndex => AttachmentIndices.Length > 0 ? AttachmentIndices[AttachmentIndices.Length - 1] : -1;
|
||||
@ -74,6 +75,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
int index = 0;
|
||||
int bindIndex = 0;
|
||||
uint attachmentIntegerFormatMask = 0;
|
||||
|
||||
foreach (ITexture color in colors)
|
||||
{
|
||||
@ -89,6 +91,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
AttachmentFormats[index] = texture.VkFormat;
|
||||
AttachmentIndices[index] = bindIndex;
|
||||
|
||||
if (texture.Info.Format.IsInteger())
|
||||
{
|
||||
attachmentIntegerFormatMask |= 1u << bindIndex;
|
||||
}
|
||||
|
||||
width = Math.Min(width, (uint)texture.Width);
|
||||
height = Math.Min(height, (uint)texture.Height);
|
||||
layers = Math.Min(layers, (uint)texture.Layers);
|
||||
@ -102,6 +109,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
bindIndex++;
|
||||
}
|
||||
|
||||
AttachmentIntegerFormatMask = attachmentIntegerFormatMask;
|
||||
|
||||
if (depthStencil is TextureView dsTexture && dsTexture.Valid)
|
||||
{
|
||||
_attachments[count - 1] = dsTexture.GetImageViewForAttachment();
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user