Compare commits
59 Commits
Author | SHA1 | Date | |
---|---|---|---|
0003a7c118 | |||
2cdcfe46d8 | |||
fe30c03cac | |||
5813b2e354 | |||
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 | |||
f3873620a3 | |||
986ac9ff83 | |||
42b9c1e8fe | |||
3b375525fb | |||
e6658c133c | |||
5b42a4d2c4 | |||
8f0c89ffd6 | |||
2c9715acf6 | |||
274af65f69 | |||
4ca78eded5 | |||
6cb6b15612 | |||
2725e40838 | |||
c2e4c8f98e | |||
b53e7ffd46 |
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'
|
- '*.yml'
|
||||||
- 'README.md'
|
- 'README.md'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: pr-checks-${{ github.event.number }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
env:
|
env:
|
||||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
@ -27,7 +31,7 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
name: ${{ matrix.OS_NAME }} (${{ matrix.configuration }})
|
name: ${{ matrix.OS_NAME }} (${{ matrix.configuration }})
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
timeout-minutes: 35
|
timeout-minutes: 45
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, macOS-latest, windows-latest]
|
os: [ubuntu-latest, macOS-latest, windows-latest]
|
||||||
@ -110,7 +114,7 @@ jobs:
|
|||||||
build_macos:
|
build_macos:
|
||||||
name: macOS Universal (${{ matrix.configuration }})
|
name: macOS Universal (${{ matrix.configuration }})
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 35
|
timeout-minutes: 45
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
configuration: [ Debug, Release ]
|
configuration: [ Debug, Release ]
|
||||||
|
2
.github/workflows/flatpak.yml
vendored
2
.github/workflows/flatpak.yml
vendored
@ -12,7 +12,7 @@ concurrency: flatpak-release
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
release:
|
release:
|
||||||
timeout-minutes: 35
|
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
2
.github/workflows/nightly_pr_comment.yml
vendored
2
.github/workflows/nightly_pr_comment.yml
vendored
@ -7,7 +7,7 @@ jobs:
|
|||||||
pr_comment:
|
pr_comment:
|
||||||
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
|
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 35
|
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/github-script@v6
|
- uses: actions/github-script@v6
|
||||||
with:
|
with:
|
||||||
|
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@ -33,7 +33,7 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
|
|
||||||
- name: Create tag
|
- name: Create tag
|
||||||
uses: actions/github-script@v5
|
uses: actions/github-script@v6
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
github.rest.git.createRef({
|
github.rest.git.createRef({
|
||||||
@ -46,7 +46,7 @@ jobs:
|
|||||||
release:
|
release:
|
||||||
name: Release ${{ matrix.OS_NAME }}
|
name: Release ${{ matrix.OS_NAME }}
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
timeout-minutes: 35
|
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ ubuntu-latest, windows-latest ]
|
os: [ ubuntu-latest, windows-latest ]
|
||||||
@ -144,7 +144,7 @@ jobs:
|
|||||||
macos_release:
|
macos_release:
|
||||||
name: Release MacOS universal
|
name: Release MacOS universal
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
timeout-minutes: 35
|
timeout-minutes: ${{ fromJSON(vars.JOB_TIMEOUT) }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
@ -3,17 +3,17 @@
|
|||||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageVersion Include="Avalonia" Version="0.10.19" />
|
<PackageVersion Include="Avalonia" Version="0.10.21" />
|
||||||
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.19" />
|
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="0.10.21" />
|
||||||
<PackageVersion Include="Avalonia.Desktop" Version="0.10.19" />
|
<PackageVersion Include="Avalonia.Desktop" Version="0.10.21" />
|
||||||
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.19" />
|
<PackageVersion Include="Avalonia.Diagnostics" Version="0.10.21" />
|
||||||
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.19" />
|
<PackageVersion Include="Avalonia.Markup.Xaml.Loader" Version="0.10.21" />
|
||||||
<PackageVersion Include="Avalonia.Svg" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Svg" Version="0.10.18" />
|
||||||
<PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" />
|
<PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" />
|
||||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageVersion Include="Concentus" Version="1.1.7" />
|
<PackageVersion Include="Concentus" Version="1.1.7" />
|
||||||
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
|
<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="FluentAvaloniaUI" Version="1.4.5" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
||||||
@ -21,7 +21,7 @@
|
|||||||
<PackageVersion Include="LibHac" Version="0.18.0" />
|
<PackageVersion Include="LibHac" Version="0.18.0" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" />
|
<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="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
|
||||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||||
<PackageVersion Include="NUnit" Version="3.13.3" />
|
<PackageVersion Include="NUnit" Version="3.13.3" />
|
||||||
|
@ -11,4 +11,10 @@ if [ -f "$SCRIPT_DIR/Ryujinx.Headless.SDL2" ]; then
|
|||||||
RYUJINX_BIN="Ryujinx.Headless.SDL2"
|
RYUJINX_BIN="Ryujinx.Headless.SDL2"
|
||||||
fi
|
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
|
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
|
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.
|
# Now replace and reopen.
|
||||||
rm -rf "$INSTALL_DIRECTORY"
|
rm -rf "$INSTALL_DIRECTORY"
|
||||||
mv "$NEW_APP_DIRECTORY" "$INSTALL_DIRECTORY"
|
mv "$NEW_APP_DIRECTORY" "$INSTALL_DIRECTORY"
|
||||||
|
@ -6,10 +6,11 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace ARMeilleure.Translation.Cache
|
namespace ARMeilleure.Translation.Cache
|
||||||
{
|
{
|
||||||
static class JitCache
|
static partial class JitCache
|
||||||
{
|
{
|
||||||
private const int PageSize = 4 * 1024;
|
private const int PageSize = 4 * 1024;
|
||||||
private const int PageMask = PageSize - 1;
|
private const int PageMask = PageSize - 1;
|
||||||
@ -27,6 +28,10 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
private static readonly object _lock = new object();
|
private static readonly object _lock = new object();
|
||||||
private static bool _initialized;
|
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)
|
public static void Initialize(IJitMemoryAllocator allocator)
|
||||||
{
|
{
|
||||||
if (_initialized) return;
|
if (_initialized) return;
|
||||||
@ -36,7 +41,11 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
if (_initialized) return;
|
if (_initialized) return;
|
||||||
|
|
||||||
_jitRegion = new ReservedRegion(allocator, CacheSize);
|
_jitRegion = new ReservedRegion(allocator, CacheSize);
|
||||||
_jitCacheInvalidator = new JitCacheInvalidation(allocator);
|
|
||||||
|
if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
_jitCacheInvalidator = new JitCacheInvalidation(allocator);
|
||||||
|
}
|
||||||
|
|
||||||
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
|
_cacheAllocator = new CacheMemoryAllocator(CacheSize);
|
||||||
|
|
||||||
@ -77,7 +86,14 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
Marshal.Copy(code, 0, funcPtr, code.Length);
|
Marshal.Copy(code, 0, funcPtr, code.Length);
|
||||||
ReprotectAsExecutable(funcOffset, 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);
|
Add(funcOffset, code.Length, func.UnwindInfo);
|
||||||
|
@ -47,8 +47,8 @@ namespace ARMeilleure.Translation.Cache
|
|||||||
|
|
||||||
public JitCacheInvalidation(IJitMemoryAllocator allocator)
|
public JitCacheInvalidation(IJitMemoryAllocator allocator)
|
||||||
{
|
{
|
||||||
// On macOS, a different path is used to write to the JIT cache, which does the invalidation.
|
// On macOS and Windows, a different path is used to write to the JIT cache, which does the invalidation.
|
||||||
if (!OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||||||
{
|
{
|
||||||
ulong size = (ulong)_invalidationCode.Length * sizeof(int);
|
ulong size = (ulong)_invalidationCode.Length * sizeof(int);
|
||||||
ulong mask = (ulong)ReservedRegion.DefaultGranularity - 1;
|
ulong mask = (ulong)ReservedRegion.DefaultGranularity - 1;
|
||||||
|
@ -45,7 +45,7 @@ namespace Ryujinx.Audio.Backends.SDL2
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_supportSurroundConfiguration = spec.channels == 6;
|
_supportSurroundConfiguration = spec.channels >= 6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,6 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
private AudioRendererRenderingDevice _renderingDevice;
|
private AudioRendererRenderingDevice _renderingDevice;
|
||||||
private AudioRendererExecutionMode _executionMode;
|
private AudioRendererExecutionMode _executionMode;
|
||||||
private IWritableEvent _systemEvent;
|
private IWritableEvent _systemEvent;
|
||||||
private ManualResetEvent _terminationEvent;
|
|
||||||
private MemoryPoolState _dspMemoryPoolState;
|
private MemoryPoolState _dspMemoryPoolState;
|
||||||
private VoiceContext _voiceContext;
|
private VoiceContext _voiceContext;
|
||||||
private MixContext _mixContext;
|
private MixContext _mixContext;
|
||||||
@ -83,7 +82,6 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
public AudioRenderSystem(AudioRendererManager manager, IWritableEvent systemEvent)
|
public AudioRenderSystem(AudioRendererManager manager, IWritableEvent systemEvent)
|
||||||
{
|
{
|
||||||
_manager = manager;
|
_manager = manager;
|
||||||
_terminationEvent = new ManualResetEvent(false);
|
|
||||||
_dspMemoryPoolState = MemoryPoolState.Create(MemoryPoolState.LocationType.Dsp);
|
_dspMemoryPoolState = MemoryPoolState.Create(MemoryPoolState.LocationType.Dsp);
|
||||||
_voiceContext = new VoiceContext();
|
_voiceContext = new VoiceContext();
|
||||||
_mixContext = new MixContext();
|
_mixContext = new MixContext();
|
||||||
@ -387,11 +385,6 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
_isActive = false;
|
_isActive = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_executionMode == AudioRendererExecutionMode.Auto)
|
|
||||||
{
|
|
||||||
_terminationEvent.WaitOne();
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Info?.Print(LogClass.AudioRenderer, $"Stopped renderer id {_sessionId}");
|
Logger.Info?.Print(LogClass.AudioRenderer, $"Stopped renderer id {_sessionId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -668,8 +661,6 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
{
|
{
|
||||||
if (_isActive)
|
if (_isActive)
|
||||||
{
|
{
|
||||||
_terminationEvent.Reset();
|
|
||||||
|
|
||||||
if (!_manager.Processor.HasRemainingCommands(_sessionId))
|
if (!_manager.Processor.HasRemainingCommands(_sessionId))
|
||||||
{
|
{
|
||||||
GenerateCommandList(out CommandList commands);
|
GenerateCommandList(out CommandList commands);
|
||||||
@ -686,10 +677,6 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
_isDspRunningBehind = true;
|
_isDspRunningBehind = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
_terminationEvent.Set();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -857,7 +844,6 @@ namespace Ryujinx.Audio.Renderer.Server
|
|||||||
}
|
}
|
||||||
|
|
||||||
_manager.Unregister(this);
|
_manager.Unregister(this);
|
||||||
_terminationEvent.Dispose();
|
|
||||||
_workBufferMemoryPin.Dispose();
|
_workBufferMemoryPin.Dispose();
|
||||||
|
|
||||||
if (MemoryManager is IRefCounted rc)
|
if (MemoryManager is IRefCounted rc)
|
||||||
|
@ -92,6 +92,8 @@ namespace Ryujinx.Ava
|
|||||||
private bool _isActive;
|
private bool _isActive;
|
||||||
private bool _renderingStarted;
|
private bool _renderingStarted;
|
||||||
|
|
||||||
|
private ManualResetEvent _gpuDoneEvent;
|
||||||
|
|
||||||
private IRenderer _renderer;
|
private IRenderer _renderer;
|
||||||
private readonly Thread _renderingThread;
|
private readonly Thread _renderingThread;
|
||||||
private readonly CancellationTokenSource _gpuCancellationTokenSource;
|
private readonly CancellationTokenSource _gpuCancellationTokenSource;
|
||||||
@ -183,6 +185,7 @@ namespace Ryujinx.Ava
|
|||||||
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState;
|
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState;
|
||||||
|
|
||||||
_gpuCancellationTokenSource = new CancellationTokenSource();
|
_gpuCancellationTokenSource = new CancellationTokenSource();
|
||||||
|
_gpuDoneEvent = new ManualResetEvent(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TopLevel_PointerEnterOrMoved(object sender, PointerEventArgs e)
|
private void TopLevel_PointerEnterOrMoved(object sender, PointerEventArgs e)
|
||||||
@ -270,7 +273,7 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
string directory = AppDataManager.Mode switch
|
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")
|
_ => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx")
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -423,10 +426,10 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
_isActive = false;
|
_isActive = false;
|
||||||
|
|
||||||
if (_renderingThread.IsAlive)
|
// 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.
|
||||||
_renderingThread.Join();
|
_gpuDoneEvent.WaitOne();
|
||||||
}
|
_gpuDoneEvent.Dispose();
|
||||||
|
|
||||||
DisplaySleep.Restore();
|
DisplaySleep.Restore();
|
||||||
|
|
||||||
@ -917,6 +920,14 @@ namespace Ryujinx.Ava
|
|||||||
UpdateStatus();
|
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);
|
(_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(null);
|
||||||
|
@ -74,6 +74,13 @@
|
|||||||
"GameListContextMenuExtractDataLogoToolTip": "Extract the Logo section from Application's current config (including updates)",
|
"GameListContextMenuExtractDataLogoToolTip": "Extract the Logo section from Application's current config (including updates)",
|
||||||
"StatusBarGamesLoaded": "{0}/{1} Games Loaded",
|
"StatusBarGamesLoaded": "{0}/{1} Games Loaded",
|
||||||
"StatusBarSystemVersion": "System Version: {0}",
|
"StatusBarSystemVersion": "System Version: {0}",
|
||||||
|
"LinuxVmMaxMapCountDialogTitle": "Low limit for memory mappings detected",
|
||||||
|
"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",
|
"Settings": "Settings",
|
||||||
"SettingsTabGeneral": "User Interface",
|
"SettingsTabGeneral": "User Interface",
|
||||||
"SettingsTabGeneralGeneral": "General",
|
"SettingsTabGeneralGeneral": "General",
|
||||||
@ -216,26 +223,17 @@
|
|||||||
"ControllerSettingsDPadDown": "Down",
|
"ControllerSettingsDPadDown": "Down",
|
||||||
"ControllerSettingsDPadLeft": "Left",
|
"ControllerSettingsDPadLeft": "Left",
|
||||||
"ControllerSettingsDPadRight": "Right",
|
"ControllerSettingsDPadRight": "Right",
|
||||||
|
"ControllerSettingsStickButton": "Button",
|
||||||
|
"ControllerSettingsStickUp": "Up",
|
||||||
|
"ControllerSettingsStickDown": "Down",
|
||||||
|
"ControllerSettingsStickLeft": "Left",
|
||||||
|
"ControllerSettingsStickRight": "Right",
|
||||||
|
"ControllerSettingsStickStick": "Stick",
|
||||||
|
"ControllerSettingsStickInvertXAxis": "Invert Stick X",
|
||||||
|
"ControllerSettingsStickInvertYAxis": "Invert Stick Y",
|
||||||
|
"ControllerSettingsStickDeadzone": "Deadzone:",
|
||||||
"ControllerSettingsLStick": "Left Stick",
|
"ControllerSettingsLStick": "Left Stick",
|
||||||
"ControllerSettingsLStickButton": "Button",
|
|
||||||
"ControllerSettingsLStickUp": "Up",
|
|
||||||
"ControllerSettingsLStickDown": "Down",
|
|
||||||
"ControllerSettingsLStickLeft": "Left",
|
|
||||||
"ControllerSettingsLStickRight": "Right",
|
|
||||||
"ControllerSettingsLStickStick": "Stick",
|
|
||||||
"ControllerSettingsLStickInvertXAxis": "Invert Stick X",
|
|
||||||
"ControllerSettingsLStickInvertYAxis": "Invert Stick Y",
|
|
||||||
"ControllerSettingsLStickDeadzone": "Deadzone:",
|
|
||||||
"ControllerSettingsRStick": "Right Stick",
|
"ControllerSettingsRStick": "Right Stick",
|
||||||
"ControllerSettingsRStickButton": "Button",
|
|
||||||
"ControllerSettingsRStickUp": "Up",
|
|
||||||
"ControllerSettingsRStickDown": "Down",
|
|
||||||
"ControllerSettingsRStickLeft": "Left",
|
|
||||||
"ControllerSettingsRStickRight": "Right",
|
|
||||||
"ControllerSettingsRStickStick": "Stick",
|
|
||||||
"ControllerSettingsRStickInvertXAxis": "Invert Stick X",
|
|
||||||
"ControllerSettingsRStickInvertYAxis": "Invert Stick Y",
|
|
||||||
"ControllerSettingsRStickDeadzone": "Deadzone:",
|
|
||||||
"ControllerSettingsTriggersLeft": "Triggers Left",
|
"ControllerSettingsTriggersLeft": "Triggers Left",
|
||||||
"ControllerSettingsTriggersRight": "Triggers Right",
|
"ControllerSettingsTriggersRight": "Triggers Right",
|
||||||
"ControllerSettingsTriggersButtonsLeft": "Trigger Buttons Left",
|
"ControllerSettingsTriggersButtonsLeft": "Trigger Buttons Left",
|
||||||
@ -291,6 +289,7 @@
|
|||||||
"ControllerSettingsSaveProfileToolTip": "Save Profile",
|
"ControllerSettingsSaveProfileToolTip": "Save Profile",
|
||||||
"MenuBarFileToolsTakeScreenshot": "Take Screenshot",
|
"MenuBarFileToolsTakeScreenshot": "Take Screenshot",
|
||||||
"MenuBarFileToolsHideUi": "Hide UI",
|
"MenuBarFileToolsHideUi": "Hide UI",
|
||||||
|
"GameListContextMenuRunApplication": "Run Application",
|
||||||
"GameListContextMenuToggleFavorite": "Toggle Favorite",
|
"GameListContextMenuToggleFavorite": "Toggle Favorite",
|
||||||
"GameListContextMenuToggleFavoriteToolTip": "Toggle Favorite status of Game",
|
"GameListContextMenuToggleFavoriteToolTip": "Toggle Favorite status of Game",
|
||||||
"SettingsTabGeneralTheme": "Theme",
|
"SettingsTabGeneralTheme": "Theme",
|
||||||
@ -545,6 +544,9 @@
|
|||||||
"SwkbdMinCharacters": "Must be at least {0} characters long",
|
"SwkbdMinCharacters": "Must be at least {0} characters long",
|
||||||
"SwkbdMinRangeCharacters": "Must be {0}-{1} characters long",
|
"SwkbdMinRangeCharacters": "Must be {0}-{1} characters long",
|
||||||
"SoftwareKeyboard": "Software Keyboard",
|
"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.",
|
"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.",
|
"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",
|
"DialogControllerAppletDockModeSet": "Docked mode set. Handheld is also invalid.\n\n",
|
||||||
@ -629,7 +631,7 @@
|
|||||||
"Search": "Search",
|
"Search": "Search",
|
||||||
"UserProfilesRecoverLostAccounts": "Recover Lost Accounts",
|
"UserProfilesRecoverLostAccounts": "Recover Lost Accounts",
|
||||||
"Recover": "Recover",
|
"Recover": "Recover",
|
||||||
"UserProfilesRecoverHeading" : "Saves were found for the following accounts",
|
"UserProfilesRecoverHeading": "Saves were found for the following accounts",
|
||||||
"UserProfilesRecoverEmptyList": "No profiles to recover",
|
"UserProfilesRecoverEmptyList": "No profiles to recover",
|
||||||
"GraphicsAATooltip": "Applies anti-aliasing to the game render",
|
"GraphicsAATooltip": "Applies anti-aliasing to the game render",
|
||||||
"GraphicsAALabel": "Anti-Aliasing:",
|
"GraphicsAALabel": "Anti-Aliasing:",
|
||||||
@ -641,10 +643,12 @@
|
|||||||
"SmaaMedium": "SMAA Medium",
|
"SmaaMedium": "SMAA Medium",
|
||||||
"SmaaHigh": "SMAA High",
|
"SmaaHigh": "SMAA High",
|
||||||
"SmaaUltra": "SMAA Ultra",
|
"SmaaUltra": "SMAA Ultra",
|
||||||
"UserEditorTitle" : "Edit User",
|
"UserEditorTitle": "Edit User",
|
||||||
"UserEditorTitleCreate" : "Create User",
|
"UserEditorTitleCreate": "Create User",
|
||||||
"SettingsTabNetworkInterface": "Network Interface:",
|
"SettingsTabNetworkInterface": "Network Interface:",
|
||||||
"NetworkInterfaceTooltip": "The network interface used for LAN features",
|
"NetworkInterfaceTooltip": "The network interface used for LAN features",
|
||||||
"NetworkInterfaceDefault": "Default",
|
"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} 个字长",
|
"SwkbdMinCharacters": "至少应为 {0} 个字长",
|
||||||
"SwkbdMinRangeCharacters": "必须为 {0}-{1} 个字长",
|
"SwkbdMinRangeCharacters": "必须为 {0}-{1} 个字长",
|
||||||
"SoftwareKeyboard": "软件键盘",
|
"SoftwareKeyboard": "软件键盘",
|
||||||
|
"SoftwareKeyboardModeNumbersOnly": "只接受数字",
|
||||||
|
"SoftwareKeyboardModeAlphabet": "只接受英文字母",
|
||||||
|
"SoftwareKeyboardModeASCII": "只接受 ASCII 符号",
|
||||||
"DialogControllerAppletMessagePlayerRange": "游戏需要 {0} 个玩家并满足以下要求:\n\n手柄类型:{1}\n\n玩家类型:{2}\n\n{3}请打开设置窗口,重新配置手柄输入;或者关闭返回。",
|
"DialogControllerAppletMessagePlayerRange": "游戏需要 {0} 个玩家并满足以下要求:\n\n手柄类型:{1}\n\n玩家类型:{2}\n\n{3}请打开设置窗口,重新配置手柄输入;或者关闭返回。",
|
||||||
"DialogControllerAppletMessage": "游戏需要刚好 {0} 个玩家并满足以下要求:\n\n手柄类型:{1}\n\n玩家类型:{2}\n\n{3}请打开设置窗口,重新配置手柄输入;或者关闭返回。",
|
"DialogControllerAppletMessage": "游戏需要刚好 {0} 个玩家并满足以下要求:\n\n手柄类型:{1}\n\n玩家类型:{2}\n\n{3}请打开设置窗口,重新配置手柄输入;或者关闭返回。",
|
||||||
"DialogControllerAppletDockModeSet": "目前处于主机模式,无法使用掌机操作方式",
|
"DialogControllerAppletDockModeSet": "目前处于主机模式,无法使用掌机操作方式",
|
||||||
|
@ -527,6 +527,9 @@
|
|||||||
"SwkbdMinCharacters": "至少應為 {0} 個字長",
|
"SwkbdMinCharacters": "至少應為 {0} 個字長",
|
||||||
"SwkbdMinRangeCharacters": "必須為 {0}-{1} 個字長",
|
"SwkbdMinRangeCharacters": "必須為 {0}-{1} 個字長",
|
||||||
"SoftwareKeyboard": "軟體鍵盤",
|
"SoftwareKeyboard": "軟體鍵盤",
|
||||||
|
"SoftwareKeyboardModeNumbersOnly": "只接受數字",
|
||||||
|
"SoftwareKeyboardModeAlphabet": "只接受英文字母",
|
||||||
|
"SoftwareKeyboardModeASCII": "只接受 ASCII 符號",
|
||||||
"DialogControllerAppletMessagePlayerRange": "本遊戲需要 {0} 個玩家持有:\n\n類型:{1}\n\n玩家:{2}\n\n{3}請打開設定畫面,配置手把,或者關閉本視窗。",
|
"DialogControllerAppletMessagePlayerRange": "本遊戲需要 {0} 個玩家持有:\n\n類型:{1}\n\n玩家:{2}\n\n{3}請打開設定畫面,配置手把,或者關閉本視窗。",
|
||||||
"DialogControllerAppletMessage": "本遊戲需要剛好 {0} 個玩家持有:\n\n類型:{1}\n\n玩家:{2}\n\n{3}請打開設定畫面,配置手把,或者關閉本視窗。",
|
"DialogControllerAppletMessage": "本遊戲需要剛好 {0} 個玩家持有:\n\n類型:{1}\n\n玩家:{2}\n\n{3}請打開設定畫面,配置手把,或者關閉本視窗。",
|
||||||
"DialogControllerAppletDockModeSet": "現在處於主機模式,無法使用掌機操作方式\n\n",
|
"DialogControllerAppletDockModeSet": "現在處於主機模式,無法使用掌機操作方式\n\n",
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using FluentAvalonia.Core;
|
|
||||||
using Ryujinx.Input;
|
using Ryujinx.Input;
|
||||||
using System;
|
using System;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
@ -30,14 +29,14 @@ namespace Ryujinx.Ava.Input
|
|||||||
_window = window;
|
_window = window;
|
||||||
|
|
||||||
_widget.PointerMoved += Parent_PointerMovedEvent;
|
_widget.PointerMoved += Parent_PointerMovedEvent;
|
||||||
_widget.PointerPressed += Parent_PointerPressEvent;
|
_widget.PointerPressed += Parent_PointerPressedEvent;
|
||||||
_widget.PointerReleased += Parent_PointerReleaseEvent;
|
_widget.PointerReleased += Parent_PointerReleasedEvent;
|
||||||
_widget.PointerWheelChanged += Parent_ScrollEvent;
|
_widget.PointerWheelChanged += Parent_PointerWheelChanged;
|
||||||
|
|
||||||
_window.PointerMoved += Parent_PointerMovedEvent;
|
_window.PointerMoved += Parent_PointerMovedEvent;
|
||||||
_window.PointerPressed += Parent_PointerPressEvent;
|
_window.PointerPressed += Parent_PointerPressedEvent;
|
||||||
_window.PointerReleased += Parent_PointerReleaseEvent;
|
_window.PointerReleased += Parent_PointerReleasedEvent;
|
||||||
_window.PointerWheelChanged += Parent_ScrollEvent;
|
_window.PointerWheelChanged += Parent_PointerWheelChanged;
|
||||||
|
|
||||||
PressedButtons = new bool[(int)MouseButton.Count];
|
PressedButtons = new bool[(int)MouseButton.Count];
|
||||||
|
|
||||||
@ -63,29 +62,25 @@ namespace Ryujinx.Ava.Input
|
|||||||
_size = new Size((int)rect.Width, (int)rect.Height);
|
_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);
|
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)
|
uint button = (uint)args.InitialPressMouseButton - 1;
|
||||||
{
|
|
||||||
int button = (int)args.InitialPressMouseButton;
|
|
||||||
|
|
||||||
if (PressedButtons.Count() >= button)
|
if ((uint)PressedButtons.Length > button)
|
||||||
{
|
{
|
||||||
PressedButtons[button] = false;
|
PressedButtons[button] = false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private void Parent_PointerPressedEvent(object o, PointerPressedEventArgs args)
|
||||||
private void Parent_PointerPressEvent(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;
|
PressedButtons[button] = true;
|
||||||
}
|
}
|
||||||
@ -100,17 +95,17 @@ namespace Ryujinx.Ava.Input
|
|||||||
|
|
||||||
public void SetMousePressed(MouseButton button)
|
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)
|
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)
|
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;
|
return false;
|
||||||
@ -149,14 +144,14 @@ namespace Ryujinx.Ava.Input
|
|||||||
_isDisposed = true;
|
_isDisposed = true;
|
||||||
|
|
||||||
_widget.PointerMoved -= Parent_PointerMovedEvent;
|
_widget.PointerMoved -= Parent_PointerMovedEvent;
|
||||||
_widget.PointerPressed -= Parent_PointerPressEvent;
|
_widget.PointerPressed -= Parent_PointerPressedEvent;
|
||||||
_widget.PointerReleased -= Parent_PointerReleaseEvent;
|
_widget.PointerReleased -= Parent_PointerReleasedEvent;
|
||||||
_widget.PointerWheelChanged -= Parent_ScrollEvent;
|
_widget.PointerWheelChanged -= Parent_PointerWheelChanged;
|
||||||
|
|
||||||
_window.PointerMoved -= Parent_PointerMovedEvent;
|
_window.PointerMoved -= Parent_PointerMovedEvent;
|
||||||
_window.PointerPressed -= Parent_PointerPressEvent;
|
_window.PointerPressed -= Parent_PointerPressedEvent;
|
||||||
_window.PointerReleased -= Parent_PointerReleaseEvent;
|
_window.PointerReleased -= Parent_PointerReleasedEvent;
|
||||||
_window.PointerWheelChanged -= Parent_ScrollEvent;
|
_window.PointerWheelChanged -= Parent_PointerWheelChanged;
|
||||||
|
|
||||||
_widget = null;
|
_widget = null;
|
||||||
}
|
}
|
||||||
|
@ -740,6 +740,18 @@ namespace Ryujinx.Modules
|
|||||||
{
|
{
|
||||||
var files = Directory.EnumerateFiles(HomeDir); // All files directly in base dir.
|
var files = Directory.EnumerateFiles(HomeDir); // All files directly in base dir.
|
||||||
|
|
||||||
|
// Determine and exclude user files only when the updater is running, not when cleaning old files
|
||||||
|
if (_running)
|
||||||
|
{
|
||||||
|
// Compare the loose files in base directory against the loose files from the incoming update, and store foreign ones in a user list.
|
||||||
|
var oldFiles = Directory.EnumerateFiles(HomeDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName);
|
||||||
|
var newFiles = Directory.EnumerateFiles(UpdatePublishDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName);
|
||||||
|
var userFiles = oldFiles.Except(newFiles).Select(filename => Path.Combine(HomeDir, filename));
|
||||||
|
|
||||||
|
// Remove user files from the paths in files.
|
||||||
|
files = files.Except(userFiles);
|
||||||
|
}
|
||||||
|
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
foreach (string dir in WindowsDependencyDirs)
|
foreach (string dir in WindowsDependencyDirs)
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
<PropertyGroup Condition="'$(RuntimeIdentifier)' != ''">
|
<PropertyGroup Condition="'$(RuntimeIdentifier)' != ''">
|
||||||
<PublishSingleFile>true</PublishSingleFile>
|
<PublishSingleFile>true</PublishSingleFile>
|
||||||
|
<TrimmerSingleWarn>false</TrimmerSingleWarn>
|
||||||
<PublishTrimmed>true</PublishTrimmed>
|
<PublishTrimmed>true</PublishTrimmed>
|
||||||
<TrimMode>partial</TrimMode>
|
<TrimMode>partial</TrimMode>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@ -147,4 +148,4 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<AdditionalFiles Include="Assets\Locales\en_US.json" />
|
<AdditionalFiles Include="Assets\Locales\en_US.json" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -9,14 +9,17 @@ using Ryujinx.Ava.Common.Locale;
|
|||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.HLE.HOS.Applets;
|
using Ryujinx.HLE.HOS.Applets;
|
||||||
|
using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Controls
|
namespace Ryujinx.Ava.UI.Controls
|
||||||
{
|
{
|
||||||
internal partial class SwkbdAppletDialog : UserControl
|
internal partial class SwkbdAppletDialog : UserControl
|
||||||
{
|
{
|
||||||
private Predicate<int> _checkLength;
|
private Predicate<int> _checkLength = _ => true;
|
||||||
|
private Predicate<string> _checkInput = _ => true;
|
||||||
private int _inputMax;
|
private int _inputMax;
|
||||||
private int _inputMin;
|
private int _inputMin;
|
||||||
private string _placeholder;
|
private string _placeholder;
|
||||||
@ -35,8 +38,6 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
Input.Watermark = _placeholder;
|
Input.Watermark = _placeholder;
|
||||||
|
|
||||||
Input.AddHandler(TextInputEvent, Message_TextInput, RoutingStrategies.Tunnel, true);
|
Input.AddHandler(TextInputEvent, Message_TextInput, RoutingStrategies.Tunnel, true);
|
||||||
|
|
||||||
SetInputLengthValidation(0, int.MaxValue); // Disable by default.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SwkbdAppletDialog()
|
public SwkbdAppletDialog()
|
||||||
@ -67,6 +68,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
string input = string.Empty;
|
string input = string.Empty;
|
||||||
|
|
||||||
content.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax);
|
content.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax);
|
||||||
|
content.SetInputValidation(args.KeyboardMode);
|
||||||
|
|
||||||
content._host = contentDialog;
|
content._host = contentDialog;
|
||||||
contentDialog.Title = title;
|
contentDialog.Title = title;
|
||||||
@ -91,6 +93,12 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
return (result, input);
|
return (result, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ApplyValidationInfo(string text)
|
||||||
|
{
|
||||||
|
Error.IsVisible = !string.IsNullOrEmpty(text);
|
||||||
|
Error.Text = text;
|
||||||
|
}
|
||||||
|
|
||||||
public void SetInputLengthValidation(int min, int max)
|
public void SetInputLengthValidation(int min, int max)
|
||||||
{
|
{
|
||||||
_inputMin = Math.Min(min, max);
|
_inputMin = Math.Min(min, max);
|
||||||
@ -99,6 +107,8 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
Error.IsVisible = false;
|
Error.IsVisible = false;
|
||||||
Error.FontStyle = FontStyle.Italic;
|
Error.FontStyle = FontStyle.Italic;
|
||||||
|
|
||||||
|
string validationInfoText = "";
|
||||||
|
|
||||||
if (_inputMin <= 0 && _inputMax == int.MaxValue) // Disable.
|
if (_inputMin <= 0 && _inputMax == int.MaxValue) // Disable.
|
||||||
{
|
{
|
||||||
Error.IsVisible = false;
|
Error.IsVisible = false;
|
||||||
@ -107,21 +117,48 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
}
|
}
|
||||||
else if (_inputMin > 0 && _inputMax == int.MaxValue)
|
else if (_inputMin > 0 && _inputMax == int.MaxValue)
|
||||||
{
|
{
|
||||||
Error.IsVisible = true;
|
validationInfoText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinCharacters, _inputMin);
|
||||||
|
|
||||||
Error.Text = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinCharacters, _inputMin);
|
|
||||||
|
|
||||||
_checkLength = length => _inputMin <= length;
|
_checkLength = length => _inputMin <= length;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Error.IsVisible = true;
|
validationInfoText = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinRangeCharacters, _inputMin, _inputMax);
|
||||||
|
|
||||||
Error.Text = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.SwkbdMinRangeCharacters, _inputMin, _inputMax);
|
|
||||||
|
|
||||||
_checkLength = length => _inputMin <= length && length <= _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());
|
Message_TextInput(this, new TextInputEventArgs());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,7 +166,7 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
{
|
{
|
||||||
if (_host != null)
|
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
|
else
|
||||||
{
|
{
|
||||||
_host.IsPrimaryButtonEnabled = _checkLength(Message.Length);
|
_host.IsPrimaryButtonEnabled = _checkLength(Message.Length) && _checkInput(Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale">
|
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale">
|
||||||
|
<MenuItem
|
||||||
|
Click="RunApplication_Click"
|
||||||
|
Header="{locale:Locale GameListContextMenuRunApplication}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="ToggleFavorite_Click"
|
Click="ToggleFavorite_Click"
|
||||||
Header="{locale:Locale GameListContextMenuToggleFavorite}"
|
Header="{locale:Locale GameListContextMenuToggleFavorite}"
|
||||||
|
@ -323,5 +323,15 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
await ApplicationHelper.ExtractSection(NcaSectionType.Logo, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName);
|
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>
|
<ListBox.ItemsPanel>
|
||||||
<ItemsPanelTemplate>
|
<ItemsPanelTemplate>
|
||||||
<flex:FlexPanel
|
<flex:FlexPanel
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
AlignContent="FlexStart"
|
AlignContent="FlexStart"
|
||||||
JustifyContent="Center" />
|
JustifyContent="FlexStart" />
|
||||||
</ItemsPanelTemplate>
|
</ItemsPanelTemplate>
|
||||||
</ListBox.ItemsPanel>
|
</ListBox.ItemsPanel>
|
||||||
<ListBox.Styles>
|
<ListBox.Styles>
|
||||||
|
@ -21,6 +21,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
if (value is byte[] buffer && targetType == typeof(IImage))
|
if (value is byte[] buffer && targetType == typeof(IImage))
|
||||||
{
|
{
|
||||||
MemoryStream mem = new(buffer);
|
MemoryStream mem = new(buffer);
|
||||||
|
|
||||||
return new Bitmap(mem);
|
return new Bitmap(mem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,6 @@ using Avalonia.Threading;
|
|||||||
using FluentAvalonia.Core;
|
using FluentAvalonia.Core;
|
||||||
using FluentAvalonia.UI.Controls;
|
using FluentAvalonia.UI.Controls;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.UI.Controls;
|
|
||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
@ -19,7 +18,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
{
|
{
|
||||||
private static bool _isChoiceDialogOpen;
|
private static bool _isChoiceDialogOpen;
|
||||||
|
|
||||||
public async static Task<UserResult> ShowContentDialog(
|
private async static Task<UserResult> ShowContentDialog(
|
||||||
string title,
|
string title,
|
||||||
object content,
|
object content,
|
||||||
string primaryButton,
|
string primaryButton,
|
||||||
@ -67,7 +66,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async static Task<UserResult> ShowTextDialog(
|
public async static Task<UserResult> ShowTextDialog(
|
||||||
string title,
|
string title,
|
||||||
string primaryText,
|
string primaryText,
|
||||||
string secondaryText,
|
string secondaryText,
|
||||||
@ -319,7 +318,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
|
|
||||||
Window parent = GetMainWindow();
|
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()
|
contentDialogOverlayWindow = new()
|
||||||
{
|
{
|
||||||
|
@ -3,21 +3,20 @@ using Avalonia.Controls;
|
|||||||
using Avalonia.Controls.Notifications;
|
using Avalonia.Controls.Notifications;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
|
using Ryujinx.Common;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Helpers
|
namespace Ryujinx.Ava.UI.Helpers
|
||||||
{
|
{
|
||||||
public static class NotificationHelper
|
public static class NotificationHelper
|
||||||
{
|
{
|
||||||
private const int MaxNotifications = 4;
|
private const int MaxNotifications = 4;
|
||||||
private const int NotificationDelayInMs = 5000;
|
private const int NotificationDelayInMs = 5000;
|
||||||
|
|
||||||
private static WindowNotificationManager _notificationManager;
|
private static WindowNotificationManager _notificationManager;
|
||||||
|
|
||||||
private static readonly ManualResetEvent _templateAppliedEvent = new(false);
|
|
||||||
private static readonly BlockingCollection<Notification> _notifications = new();
|
private static readonly BlockingCollection<Notification> _notifications = new();
|
||||||
|
|
||||||
public static void SetNotificationManager(Window host)
|
public static void SetNotificationManager(Window host)
|
||||||
@ -29,25 +28,31 @@ namespace Ryujinx.Ava.UI.Helpers
|
|||||||
Margin = new Thickness(0, 0, 15, 40)
|
Margin = new Thickness(0, 0, 15, 40)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var maybeAsyncWorkQueue = new Lazy<AsyncWorkQueue<Notification>>(
|
||||||
|
() => new AsyncWorkQueue<Notification>(notification =>
|
||||||
|
{
|
||||||
|
Dispatcher.UIThread.Post(() =>
|
||||||
|
{
|
||||||
|
_notificationManager.Show(notification);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
"UI.NotificationThread",
|
||||||
|
_notifications),
|
||||||
|
LazyThreadSafetyMode.ExecutionAndPublication);
|
||||||
|
|
||||||
_notificationManager.TemplateApplied += (sender, args) =>
|
_notificationManager.TemplateApplied += (sender, args) =>
|
||||||
{
|
{
|
||||||
_templateAppliedEvent.Set();
|
// NOTE: Force creation of the AsyncWorkQueue.
|
||||||
|
_ = maybeAsyncWorkQueue.Value;
|
||||||
};
|
};
|
||||||
|
|
||||||
Task.Run(async () =>
|
host.Closing += (sender, args) =>
|
||||||
{
|
{
|
||||||
_templateAppliedEvent.WaitOne();
|
if (maybeAsyncWorkQueue.IsValueCreated)
|
||||||
|
|
||||||
foreach (var notification in _notifications.GetConsumingEnumerable())
|
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.Post(() =>
|
maybeAsyncWorkQueue.Value.Dispose();
|
||||||
{
|
|
||||||
_notificationManager.Show(notification);
|
|
||||||
});
|
|
||||||
|
|
||||||
await Task.Delay(NotificationDelayInMs / MaxNotifications);
|
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Show(string title, string text, NotificationType type, bool waitingExit = false, Action onClick = null, Action onClose = null)
|
public static void Show(string title, string text, NotificationType type, bool waitingExit = false, Action onClick = null, Action onClose = null)
|
||||||
|
@ -7,6 +7,7 @@ using Ryujinx.Ava.Common.Locale;
|
|||||||
using Ryujinx.Ava.Input;
|
using Ryujinx.Ava.Input;
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.Models;
|
using Ryujinx.Ava.UI.Models;
|
||||||
|
using Ryujinx.Ava.UI.Views.Input;
|
||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
@ -30,7 +31,7 @@ using Key = Ryujinx.Common.Configuration.Hid.Key;
|
|||||||
|
|
||||||
namespace Ryujinx.Ava.UI.ViewModels
|
namespace Ryujinx.Ava.UI.ViewModels
|
||||||
{
|
{
|
||||||
public class ControllerSettingsViewModel : BaseModel, IDisposable
|
public class ControllerInputViewModel : BaseModel, IDisposable
|
||||||
{
|
{
|
||||||
private const string Disabled = "disabled";
|
private const string Disabled = "disabled";
|
||||||
private const string ProControllerResource = "Ryujinx.Ui.Common/Resources/Controller_ProCon.svg";
|
private const string ProControllerResource = "Ryujinx.Ui.Common/Resources/Controller_ProCon.svg";
|
||||||
@ -231,7 +232,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
public InputConfig Config { get; set; }
|
public InputConfig Config { get; set; }
|
||||||
|
|
||||||
public ControllerSettingsViewModel(UserControl owner) : this()
|
public ControllerInputViewModel(UserControl owner) : this()
|
||||||
{
|
{
|
||||||
_owner = owner;
|
_owner = owner;
|
||||||
|
|
||||||
@ -258,7 +259,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ControllerSettingsViewModel()
|
public ControllerInputViewModel()
|
||||||
{
|
{
|
||||||
PlayerIndexes = new ObservableCollection<PlayerModel>();
|
PlayerIndexes = new ObservableCollection<PlayerModel>();
|
||||||
Controllers = new ObservableCollection<ControllerModel>();
|
Controllers = new ObservableCollection<ControllerModel>();
|
||||||
@ -328,12 +329,12 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
public async void ShowMotionConfig()
|
public async void ShowMotionConfig()
|
||||||
{
|
{
|
||||||
await MotionSettingsWindow.Show(this);
|
await MotionInputView.Show(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void ShowRumbleConfig()
|
public async void ShowRumbleConfig()
|
||||||
{
|
{
|
||||||
await RumbleSettingsWindow.Show(this);
|
await RumbleInputView.Show(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LoadInputDriver()
|
private void LoadInputDriver()
|
@ -257,6 +257,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
OnPropertyChanged();
|
OnPropertyChanged();
|
||||||
OnPropertyChanged(nameof(EnableNonGameRunningControls));
|
OnPropertyChanged(nameof(EnableNonGameRunningControls));
|
||||||
|
OnPropertyChanged(nameof(IsAppletMenuActive));
|
||||||
OnPropertyChanged(nameof(StatusBarVisible));
|
OnPropertyChanged(nameof(StatusBarVisible));
|
||||||
OnPropertyChanged(nameof(ShowFirmwareStatus));
|
OnPropertyChanged(nameof(ShowFirmwareStatus));
|
||||||
}
|
}
|
||||||
@ -1529,6 +1530,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
double sessionTimePlayed = DateTime.UtcNow.Subtract(appMetadata.LastPlayed.Value).TotalSeconds;
|
double sessionTimePlayed = DateTime.UtcNow.Subtract(appMetadata.LastPlayed.Value).TotalSeconds;
|
||||||
appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero);
|
appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
appMetadata.LastPlayed = DateTime.UtcNow;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
93
src/Ryujinx.Ava/UI/ViewModels/MotionInputViewModel.cs
Normal file
93
src/Ryujinx.Ava/UI/ViewModels/MotionInputViewModel.cs
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
{
|
||||||
|
public class MotionInputViewModel : BaseModel
|
||||||
|
{
|
||||||
|
private int _slot;
|
||||||
|
public int Slot
|
||||||
|
{
|
||||||
|
get => _slot;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_slot = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _altSlot;
|
||||||
|
public int AltSlot
|
||||||
|
{
|
||||||
|
get => _altSlot;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_altSlot = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _dsuServerHost;
|
||||||
|
public string DsuServerHost
|
||||||
|
{
|
||||||
|
get => _dsuServerHost;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_dsuServerHost = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _dsuServerPort;
|
||||||
|
public int DsuServerPort
|
||||||
|
{
|
||||||
|
get => _dsuServerPort;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_dsuServerPort = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _mirrorInput;
|
||||||
|
public bool MirrorInput
|
||||||
|
{
|
||||||
|
get => _mirrorInput;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_mirrorInput = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _sensitivity;
|
||||||
|
public int Sensitivity
|
||||||
|
{
|
||||||
|
get => _sensitivity;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_sensitivity = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private double _gryoDeadzone;
|
||||||
|
public double GyroDeadzone
|
||||||
|
{
|
||||||
|
get => _gryoDeadzone;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_gryoDeadzone = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _enableCemuHookMotion;
|
||||||
|
public bool EnableCemuHookMotion
|
||||||
|
{
|
||||||
|
get => _enableCemuHookMotion;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_enableCemuHookMotion = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
src/Ryujinx.Ava/UI/ViewModels/RumbleInputViewModel.cs
Normal file
27
src/Ryujinx.Ava/UI/ViewModels/RumbleInputViewModel.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
namespace Ryujinx.Ava.UI.ViewModels
|
||||||
|
{
|
||||||
|
public class RumbleInputViewModel : BaseModel
|
||||||
|
{
|
||||||
|
private float _strongRumble;
|
||||||
|
public float StrongRumble
|
||||||
|
{
|
||||||
|
get => _strongRumble;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_strongRumble = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float _weakRumble;
|
||||||
|
public float WeakRumble
|
||||||
|
{
|
||||||
|
get => _weakRumble;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_weakRumble = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -4,7 +4,6 @@ using Avalonia.Input;
|
|||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.LogicalTree;
|
using Avalonia.LogicalTree;
|
||||||
using Ryujinx.Ava.Common.Locale;
|
using Ryujinx.Ava.Common.Locale;
|
||||||
using Ryujinx.Ava.UI.Controls;
|
|
||||||
using Ryujinx.Ava.UI.Helpers;
|
using Ryujinx.Ava.UI.Helpers;
|
||||||
using Ryujinx.Ava.UI.Models;
|
using Ryujinx.Ava.UI.Models;
|
||||||
using Ryujinx.Ava.UI.ViewModels;
|
using Ryujinx.Ava.UI.ViewModels;
|
||||||
@ -13,18 +12,18 @@ using Ryujinx.Input;
|
|||||||
using Ryujinx.Input.Assigner;
|
using Ryujinx.Input.Assigner;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Windows
|
namespace Ryujinx.Ava.UI.Views.Input
|
||||||
{
|
{
|
||||||
public partial class ControllerSettingsWindow : UserControl
|
public partial class ControllerInputView : UserControl
|
||||||
{
|
{
|
||||||
private bool _dialogOpen;
|
private bool _dialogOpen;
|
||||||
|
|
||||||
private ButtonKeyAssigner _currentAssigner;
|
private ButtonKeyAssigner _currentAssigner;
|
||||||
internal ControllerSettingsViewModel ViewModel { get; set; }
|
internal ControllerInputViewModel ViewModel { get; set; }
|
||||||
|
|
||||||
public ControllerSettingsWindow()
|
public ControllerInputView()
|
||||||
{
|
{
|
||||||
DataContext = ViewModel = new ControllerSettingsViewModel(this);
|
DataContext = ViewModel = new ControllerInputViewModel(this);
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
@ -1,12 +1,15 @@
|
|||||||
<UserControl
|
<UserControl
|
||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||||
|
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
x:Class="Ryujinx.Ava.UI.Windows.MotionSettingsWindow"
|
x:Class="Ryujinx.Ava.UI.Views.Input.MotionInputView"
|
||||||
|
x:CompileBindings="True"
|
||||||
|
x:DataType="viewModels:MotionInputViewModel"
|
||||||
Focusable="True">
|
Focusable="True">
|
||||||
<Grid Margin="10">
|
<Grid Margin="10">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
@ -14,7 +17,9 @@
|
|||||||
<RowDefinition />
|
<RowDefinition />
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<StackPanel Orientation="Vertical">
|
<StackPanel Orientation="Vertical">
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
|
<StackPanel
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Center">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="0"
|
Margin="0"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
@ -28,11 +33,14 @@
|
|||||||
Maximum="100"
|
Maximum="100"
|
||||||
Minimum="0"
|
Minimum="0"
|
||||||
Value="{Binding Sensitivity, Mode=TwoWay}" />
|
Value="{Binding Sensitivity, Mode=TwoWay}" />
|
||||||
<TextBlock HorizontalAlignment="Center"
|
<TextBlock
|
||||||
Margin="5, 0"
|
HorizontalAlignment="Center"
|
||||||
Text="{Binding Sensitivity, StringFormat=\{0:0\}%}" />
|
Margin="5, 0"
|
||||||
|
Text="{Binding Sensitivity, StringFormat=\{0:0\}%}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
|
<StackPanel
|
||||||
|
Orientation="Horizontal"
|
||||||
|
HorizontalAlignment="Center">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="0"
|
Margin="0"
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
@ -51,17 +59,25 @@
|
|||||||
Margin="5, 0"
|
Margin="5, 0"
|
||||||
Text="{Binding GyroDeadzone, StringFormat=\{0:0.00\}}" />
|
Text="{Binding GyroDeadzone, StringFormat=\{0:0.00\}}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Separator Height="1" Margin="0,5" />
|
<Separator
|
||||||
<CheckBox Margin="5" IsChecked="{Binding EnableCemuHookMotion}">
|
Height="1"
|
||||||
<TextBlock Margin="0,3,0,0" VerticalAlignment="Center"
|
Margin="0,5" />
|
||||||
Text="{locale:Locale ControllerSettingsMotionUseCemuhookCompatibleMotion}" />
|
<CheckBox
|
||||||
|
Margin="5"
|
||||||
|
IsChecked="{Binding EnableCemuHookMotion}">
|
||||||
|
<TextBlock
|
||||||
|
Margin="0,3,0,0"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Text="{locale:Locale ControllerSettingsMotionUseCemuhookCompatibleMotion}" />
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<Border Grid.Row="1"
|
<Border
|
||||||
Padding="20,5"
|
Grid.Row="1"
|
||||||
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
Padding="20,5"
|
||||||
BorderThickness="1"
|
BorderBrush="{DynamicResource ThemeControlBorderColor}"
|
||||||
HorizontalAlignment="Stretch">
|
BorderThickness="1"
|
||||||
|
CornerRadius="5"
|
||||||
|
HorizontalAlignment="Stretch">
|
||||||
<Grid VerticalAlignment="Top">
|
<Grid VerticalAlignment="Top">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto" />
|
<RowDefinition Height="Auto" />
|
||||||
@ -109,30 +125,42 @@
|
|||||||
<ColumnDefinition />
|
<ColumnDefinition />
|
||||||
<ColumnDefinition />
|
<ColumnDefinition />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock Margin="0,10,0,0" VerticalAlignment="Center"
|
<TextBlock
|
||||||
Text="{locale:Locale ControllerSettingsMotionControllerSlot}" />
|
Margin="0,10,0,0"
|
||||||
<ui:NumberBox Grid.Row="0" Grid.Column="1"
|
VerticalAlignment="Center"
|
||||||
Name="CemuHookSlotUpDown"
|
Text="{locale:Locale ControllerSettingsMotionControllerSlot}" />
|
||||||
SmallChange="1"
|
<ui:NumberBox
|
||||||
LargeChange="1"
|
Grid.Row="0"
|
||||||
Maximum="4"
|
Grid.Column="1"
|
||||||
Minimum="0"
|
Name="CemuHookSlotUpDown"
|
||||||
Value="{Binding Slot}" />
|
SmallChange="1"
|
||||||
<TextBlock Margin="0,10,0,0" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center"
|
LargeChange="1"
|
||||||
Text="{locale:Locale ControllerSettingsMotionRightJoyConSlot}" />
|
Maximum="4"
|
||||||
<ui:NumberBox Grid.Row="1" Grid.Column="1"
|
Minimum="0"
|
||||||
Name="CemuHookRightJoyConSlotUpDown"
|
Value="{Binding Slot}" />
|
||||||
SmallChange="1"
|
<TextBlock
|
||||||
LargeChange="1"
|
Margin="0,10,0,0"
|
||||||
Maximum="4"
|
Grid.Row="1"
|
||||||
Minimum="0"
|
Grid.Column="0"
|
||||||
Value="{Binding AltSlot}" />
|
VerticalAlignment="Center"
|
||||||
|
Text="{locale:Locale ControllerSettingsMotionRightJoyConSlot}" />
|
||||||
|
<ui:NumberBox
|
||||||
|
Grid.Row="1"
|
||||||
|
Grid.Column="1"
|
||||||
|
Name="CemuHookRightJoyConSlotUpDown"
|
||||||
|
SmallChange="1"
|
||||||
|
LargeChange="1"
|
||||||
|
Maximum="4"
|
||||||
|
Minimum="0"
|
||||||
|
Value="{Binding AltSlot}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<CheckBox HorizontalAlignment="Center"
|
<CheckBox
|
||||||
IsChecked="{Binding MirrorInput, Mode=TwoWay}">
|
HorizontalAlignment="Center"
|
||||||
<TextBlock HorizontalAlignment="Center"
|
IsChecked="{Binding MirrorInput, Mode=TwoWay}">
|
||||||
Text="{locale:Locale ControllerSettingsMotionMirrorInput}" />
|
<TextBlock
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Text="{locale:Locale ControllerSettingsMotionMirrorInput}" />
|
||||||
</CheckBox>
|
</CheckBox>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Grid>
|
</Grid>
|
@ -6,44 +6,42 @@ using Ryujinx.Ava.UI.ViewModels;
|
|||||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Windows
|
namespace Ryujinx.Ava.UI.Views.Input
|
||||||
{
|
{
|
||||||
public partial class MotionSettingsWindow : UserControl
|
public partial class MotionInputView : UserControl
|
||||||
{
|
{
|
||||||
private readonly InputConfiguration<GamepadInputId, StickInputId> _viewmodel;
|
private MotionInputViewModel _viewModel;
|
||||||
|
|
||||||
public MotionSettingsWindow()
|
public MotionInputView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
DataContext = _viewmodel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public MotionSettingsWindow(ControllerSettingsViewModel viewmodel)
|
public MotionInputView(ControllerInputViewModel viewModel)
|
||||||
{
|
{
|
||||||
var config = viewmodel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
|
var config = viewModel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
|
||||||
|
|
||||||
_viewmodel = new InputConfiguration<GamepadInputId, StickInputId>()
|
_viewModel = new MotionInputViewModel
|
||||||
{
|
{
|
||||||
Slot = config.Slot,
|
Slot = config.Slot,
|
||||||
AltSlot = config.AltSlot,
|
AltSlot = config.AltSlot,
|
||||||
DsuServerHost = config.DsuServerHost,
|
DsuServerHost = config.DsuServerHost,
|
||||||
DsuServerPort = config.DsuServerPort,
|
DsuServerPort = config.DsuServerPort,
|
||||||
MirrorInput = config.MirrorInput,
|
MirrorInput = config.MirrorInput,
|
||||||
EnableMotion = config.EnableMotion,
|
|
||||||
Sensitivity = config.Sensitivity,
|
Sensitivity = config.Sensitivity,
|
||||||
GyroDeadzone = config.GyroDeadzone,
|
GyroDeadzone = config.GyroDeadzone,
|
||||||
EnableCemuHookMotion = config.EnableCemuHookMotion
|
EnableCemuHookMotion = config.EnableCemuHookMotion
|
||||||
};
|
};
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
DataContext = _viewmodel;
|
DataContext = _viewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task Show(ControllerSettingsViewModel viewmodel)
|
public static async Task Show(ControllerInputViewModel viewModel)
|
||||||
{
|
{
|
||||||
MotionSettingsWindow content = new MotionSettingsWindow(viewmodel);
|
MotionInputView content = new(viewModel);
|
||||||
|
|
||||||
ContentDialog contentDialog = new ContentDialog
|
ContentDialog contentDialog = new()
|
||||||
{
|
{
|
||||||
Title = LocaleManager.Instance[LocaleKeys.ControllerMotionTitle],
|
Title = LocaleManager.Instance[LocaleKeys.ControllerMotionTitle],
|
||||||
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsSave],
|
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsSave],
|
||||||
@ -53,16 +51,15 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
};
|
};
|
||||||
contentDialog.PrimaryButtonClick += (sender, args) =>
|
contentDialog.PrimaryButtonClick += (sender, args) =>
|
||||||
{
|
{
|
||||||
var config = viewmodel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
|
var config = viewModel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
|
||||||
config.Slot = content._viewmodel.Slot;
|
config.Slot = content._viewModel.Slot;
|
||||||
config.EnableMotion = content._viewmodel.EnableMotion;
|
config.Sensitivity = content._viewModel.Sensitivity;
|
||||||
config.Sensitivity = content._viewmodel.Sensitivity;
|
config.GyroDeadzone = content._viewModel.GyroDeadzone;
|
||||||
config.GyroDeadzone = content._viewmodel.GyroDeadzone;
|
config.AltSlot = content._viewModel.AltSlot;
|
||||||
config.AltSlot = content._viewmodel.AltSlot;
|
config.DsuServerHost = content._viewModel.DsuServerHost;
|
||||||
config.DsuServerHost = content._viewmodel.DsuServerHost;
|
config.DsuServerPort = content._viewModel.DsuServerPort;
|
||||||
config.DsuServerPort = content._viewmodel.DsuServerPort;
|
config.EnableCemuHookMotion = content._viewModel.EnableCemuHookMotion;
|
||||||
config.EnableCemuHookMotion = content._viewmodel.EnableCemuHookMotion;
|
config.MirrorInput = content._viewModel.MirrorInput;
|
||||||
config.MirrorInput = content._viewmodel.MirrorInput;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
await contentDialog.ShowAsync();
|
await contentDialog.ShowAsync();
|
@ -1,11 +1,14 @@
|
|||||||
<UserControl
|
<UserControl
|
||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||||
|
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
x:Class="Ryujinx.Ava.UI.Windows.RumbleSettingsWindow"
|
x:Class="Ryujinx.Ava.UI.Views.Input.RumbleInputView"
|
||||||
|
x:DataType="viewModels:RumbleInputViewModel"
|
||||||
|
x:CompileBindings="True"
|
||||||
Focusable="True">
|
Focusable="True">
|
||||||
<Grid Margin="10">
|
<Grid Margin="10">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
@ -6,36 +6,37 @@ using Ryujinx.Ava.UI.ViewModels;
|
|||||||
using Ryujinx.Common.Configuration.Hid.Controller;
|
using Ryujinx.Common.Configuration.Hid.Controller;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Ryujinx.Ava.UI.Windows
|
namespace Ryujinx.Ava.UI.Views.Input
|
||||||
{
|
{
|
||||||
public partial class RumbleSettingsWindow : UserControl
|
public partial class RumbleInputView : UserControl
|
||||||
{
|
{
|
||||||
private readonly InputConfiguration<GamepadInputId, StickInputId> _viewmodel;
|
private RumbleInputViewModel _viewModel;
|
||||||
|
|
||||||
public RumbleSettingsWindow()
|
public RumbleInputView()
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
DataContext = _viewmodel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public RumbleSettingsWindow(ControllerSettingsViewModel viewmodel)
|
public RumbleInputView(ControllerInputViewModel viewModel)
|
||||||
{
|
{
|
||||||
var config = viewmodel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
|
var config = viewModel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
|
||||||
|
|
||||||
_viewmodel = new InputConfiguration<GamepadInputId, StickInputId>()
|
_viewModel = new RumbleInputViewModel
|
||||||
{
|
{
|
||||||
StrongRumble = config.StrongRumble, WeakRumble = config.WeakRumble
|
StrongRumble = config.StrongRumble,
|
||||||
|
WeakRumble = config.WeakRumble
|
||||||
};
|
};
|
||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
DataContext = _viewmodel;
|
|
||||||
|
DataContext = _viewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task Show(ControllerSettingsViewModel viewmodel)
|
public static async Task Show(ControllerInputViewModel viewModel)
|
||||||
{
|
{
|
||||||
RumbleSettingsWindow content = new RumbleSettingsWindow(viewmodel);
|
RumbleInputView content = new(viewModel);
|
||||||
|
|
||||||
ContentDialog contentDialog = new ContentDialog
|
ContentDialog contentDialog = new()
|
||||||
{
|
{
|
||||||
Title = LocaleManager.Instance[LocaleKeys.ControllerRumbleTitle],
|
Title = LocaleManager.Instance[LocaleKeys.ControllerRumbleTitle],
|
||||||
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsSave],
|
PrimaryButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsSave],
|
||||||
@ -43,14 +44,14 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose],
|
CloseButtonText = LocaleManager.Instance[LocaleKeys.ControllerSettingsClose],
|
||||||
Content = content,
|
Content = content,
|
||||||
};
|
};
|
||||||
|
|
||||||
contentDialog.PrimaryButtonClick += (sender, args) =>
|
contentDialog.PrimaryButtonClick += (sender, args) =>
|
||||||
{
|
{
|
||||||
var config = viewmodel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
|
var config = viewModel.Configuration as InputConfiguration<GamepadInputId, StickInputId>;
|
||||||
config.StrongRumble = content._viewmodel.StrongRumble;
|
config.StrongRumble = content._viewModel.StrongRumble;
|
||||||
config.WeakRumble = content._viewmodel.WeakRumble;
|
config.WeakRumble = content._viewModel.WeakRumble;
|
||||||
};
|
};
|
||||||
|
|
||||||
await contentDialog.ShowAsync();
|
await contentDialog.ShowAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -9,9 +9,7 @@ using Ryujinx.Ava.UI.ViewModels;
|
|||||||
using Ryujinx.Ava.UI.Windows;
|
using Ryujinx.Ava.UI.Windows;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.HLE.HOS;
|
|
||||||
using Ryujinx.Modules;
|
using Ryujinx.Modules;
|
||||||
using Ryujinx.Ui.App.Common;
|
|
||||||
using Ryujinx.Ui.Common;
|
using Ryujinx.Ui.Common;
|
||||||
using Ryujinx.Ui.Common.Configuration;
|
using Ryujinx.Ui.Common.Configuration;
|
||||||
using Ryujinx.Ui.Common.Helper;
|
using Ryujinx.Ui.Common.Helper;
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
</Design.DataContext>
|
</Design.DataContext>
|
||||||
<DockPanel
|
<DockPanel
|
||||||
Margin="0,0,0,5"
|
Margin="0,0,0,5"
|
||||||
|
Height="35"
|
||||||
HorizontalAlignment="Stretch">
|
HorizontalAlignment="Stretch">
|
||||||
<Button
|
<Button
|
||||||
Width="40"
|
Width="40"
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<UserControl
|
<UserControl
|
||||||
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsInputView"
|
x:Class="Ryujinx.Ava.UI.Views.Settings.SettingsInputView"
|
||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||||
xmlns:window="clr-namespace:Ryujinx.Ava.UI.Windows"
|
xmlns:views="clr-namespace:Ryujinx.Ava.UI.Views.Input"
|
||||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
x:CompileBindings="True"
|
x:CompileBindings="True"
|
||||||
@ -13,34 +13,56 @@
|
|||||||
<Design.DataContext>
|
<Design.DataContext>
|
||||||
<viewModels:SettingsViewModel />
|
<viewModels:SettingsViewModel />
|
||||||
</Design.DataContext>
|
</Design.DataContext>
|
||||||
<ScrollViewer
|
<ScrollViewer
|
||||||
Name="InputPage"
|
Name="InputPage"
|
||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Stretch"
|
VerticalAlignment="Stretch"
|
||||||
HorizontalScrollBarVisibility="Disabled"
|
HorizontalScrollBarVisibility="Disabled"
|
||||||
VerticalScrollBarVisibility="Auto">
|
VerticalScrollBarVisibility="Auto">
|
||||||
<Border Classes="settings">
|
<Border Classes="settings">
|
||||||
<StackPanel Margin="4" Orientation="Vertical">
|
<Panel
|
||||||
<StackPanel Orientation="Horizontal">
|
Margin="10">
|
||||||
<CheckBox Margin="5,0"
|
<Grid>
|
||||||
ToolTip.Tip="{locale:Locale DockModeToggleTooltip}"
|
<Grid.RowDefinitions>
|
||||||
IsChecked="{Binding EnableDockedMode}">
|
<RowDefinition Height="Auto"/>
|
||||||
<TextBlock VerticalAlignment="Center"
|
<RowDefinition Height="*" />
|
||||||
Text="{locale:Locale SettingsTabInputEnableDockedMode}" />
|
<RowDefinition Height="Auto" />
|
||||||
</CheckBox>
|
</Grid.RowDefinitions>
|
||||||
<CheckBox Margin="5,0"
|
<views:ControllerInputView
|
||||||
ToolTip.Tip="{locale:Locale DirectKeyboardTooltip}"
|
Grid.Row="0"
|
||||||
IsChecked="{Binding EnableKeyboard}">
|
Name="ControllerSettings" />
|
||||||
<TextBlock Text="{locale:Locale SettingsTabInputDirectKeyboardAccess}" />
|
<StackPanel
|
||||||
</CheckBox>
|
Orientation="Vertical"
|
||||||
<CheckBox Margin="5,0"
|
Grid.Row="2">
|
||||||
ToolTip.Tip="{locale:Locale DirectMouseTooltip}"
|
<Separator
|
||||||
IsChecked="{Binding EnableMouse}">
|
Margin="0 10"
|
||||||
<TextBlock Text="{locale:Locale SettingsTabInputDirectMouseAccess}" />
|
Height="1" />
|
||||||
</CheckBox>
|
<StackPanel
|
||||||
</StackPanel>
|
Orientation="Horizontal"
|
||||||
<window:ControllerSettingsWindow Name="ControllerSettings" Margin="0" MinHeight="600" />
|
Spacing="10">
|
||||||
</StackPanel>
|
<CheckBox
|
||||||
|
ToolTip.Tip="{locale:Locale DockModeToggleTooltip}"
|
||||||
|
MinWidth="0"
|
||||||
|
IsChecked="{Binding EnableDockedMode}">
|
||||||
|
<TextBlock
|
||||||
|
Text="{locale:Locale SettingsTabInputEnableDockedMode}" />
|
||||||
|
</CheckBox>
|
||||||
|
<CheckBox
|
||||||
|
ToolTip.Tip="{locale:Locale DirectKeyboardTooltip}"
|
||||||
|
IsChecked="{Binding EnableKeyboard}">
|
||||||
|
<TextBlock
|
||||||
|
Text="{locale:Locale SettingsTabInputDirectKeyboardAccess}" />
|
||||||
|
</CheckBox>
|
||||||
|
<CheckBox
|
||||||
|
ToolTip.Tip="{locale:Locale DirectMouseTooltip}"
|
||||||
|
IsChecked="{Binding EnableMouse}">
|
||||||
|
<TextBlock
|
||||||
|
Text="{locale:Locale SettingsTabInputDirectMouseAccess}" />
|
||||||
|
</CheckBox>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</Panel>
|
||||||
</Border>
|
</Border>
|
||||||
</ScrollViewer>
|
</ScrollViewer>
|
||||||
</UserControl>
|
</UserControl>
|
@ -58,11 +58,20 @@
|
|||||||
JustifyContent="SpaceAround"
|
JustifyContent="SpaceAround"
|
||||||
RowSpacing="2">
|
RowSpacing="2">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
FontSize="28"
|
FontSize="28"
|
||||||
FontWeight="Bold"
|
FontWeight="Bold"
|
||||||
Text="Ryujinx"
|
Text="Ryujinx"
|
||||||
TextAlignment="Left" />
|
TextAlignment="Center"
|
||||||
<TextBlock Text="(REE-YOU-JINX)" TextAlignment="Left" />
|
Width="100" />
|
||||||
|
<TextBlock
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontSize="11"
|
||||||
|
Text="(REE-YOU-JINX)"
|
||||||
|
TextAlignment="Center"
|
||||||
|
Width="100" />
|
||||||
</flex:FlexPanel>
|
</flex:FlexPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
@ -72,6 +81,18 @@
|
|||||||
LineHeight="12"
|
LineHeight="12"
|
||||||
Text="{Binding Version}"
|
Text="{Binding Version}"
|
||||||
TextAlignment="Center" />
|
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>
|
||||||
<StackPanel
|
<StackPanel
|
||||||
Grid.Row="2"
|
Grid.Row="2"
|
||||||
|
@ -23,6 +23,7 @@ using Ryujinx.Ui.Common.Helper;
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using InputManager = Ryujinx.Input.HLE.InputManager;
|
using InputManager = Ryujinx.Input.HLE.InputManager;
|
||||||
|
|
||||||
@ -258,7 +259,64 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
ApplicationHelper.Initialize(VirtualFileSystem, AccountManager, LibHacHorizonManager.RyujinxClient, this);
|
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)
|
if (ShowKeyErrorOnLoad)
|
||||||
{
|
{
|
||||||
@ -268,6 +326,20 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
UserErrorDialog.ShowUserErrorDialog(UserError.NoKeys, this));
|
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)
|
if (_deferLoad)
|
||||||
{
|
{
|
||||||
_deferLoad = false;
|
_deferLoad = false;
|
||||||
@ -447,14 +519,14 @@ namespace Ryujinx.Ava.UI.Windows
|
|||||||
private void ConfirmExit()
|
private void ConfirmExit()
|
||||||
{
|
{
|
||||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||||
{
|
{
|
||||||
ViewModel.IsClosing = await ContentDialogHelper.CreateExitDialog();
|
ViewModel.IsClosing = await ContentDialogHelper.CreateExitDialog();
|
||||||
|
|
||||||
if (ViewModel.IsClosing)
|
if (ViewModel.IsClosing)
|
||||||
{
|
{
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async void LoadApplications()
|
public async void LoadApplications()
|
||||||
|
@ -22,9 +22,11 @@ namespace Ryujinx.Common
|
|||||||
_cts = new CancellationTokenSource();
|
_cts = new CancellationTokenSource();
|
||||||
_queue = collection;
|
_queue = collection;
|
||||||
_workerAction = callback;
|
_workerAction = callback;
|
||||||
_workerThread = new Thread(DoWork) { Name = name };
|
_workerThread = new Thread(DoWork)
|
||||||
|
{
|
||||||
_workerThread.IsBackground = true;
|
Name = name,
|
||||||
|
IsBackground = true
|
||||||
|
};
|
||||||
_workerThread.Start();
|
_workerThread.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
internal class DefaultLogFormatter : ILogFormatter
|
||||||
{
|
{
|
||||||
@ -27,6 +28,14 @@ namespace Ryujinx.Common.Logging
|
|||||||
|
|
||||||
if (args.Data is not null)
|
if (args.Data is not null)
|
||||||
{
|
{
|
||||||
|
if (args.Data is StackTrace trace)
|
||||||
|
{
|
||||||
|
sb.Append('\n');
|
||||||
|
sb.Append(trace);
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
DynamicObjectFormatter.Format(sb, args.Data);
|
DynamicObjectFormatter.Format(sb, args.Data);
|
||||||
}
|
}
|
||||||
@ -39,4 +48,4 @@ namespace Ryujinx.Common.Logging
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,9 +3,9 @@ using System;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
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>();
|
private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>();
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ namespace Ryujinx.Common.Logging
|
|||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder sb = StringBuilderPool.Allocate();
|
StringBuilder sb = StringBuilderPool.Allocate();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Format(sb, dynamicObject);
|
Format(sb, dynamicObject);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging.Formatters
|
||||||
{
|
{
|
||||||
interface ILogFormatter
|
interface ILogFormatter
|
||||||
{
|
{
|
||||||
string Format(LogEventArgs args);
|
string Format(LogEventArgs args);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using Ryujinx.Common.Logging.Formatters;
|
||||||
|
using System;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using Ryujinx.Common.Logging.Targets;
|
||||||
using Ryujinx.Common.SystemInterop;
|
using Ryujinx.Common.SystemInterop;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
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)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void PrintStub(LogClass logClass, string message = "", [CallerMemberName] string caller = "")
|
public void PrintStub(LogClass logClass, string message = "", [CallerMemberName] string caller = "")
|
||||||
{
|
{
|
||||||
@ -122,7 +133,7 @@ namespace Ryujinx.Common.Logging
|
|||||||
AsyncLogTargetOverflowAction.Discard));
|
AsyncLogTargetOverflowAction.Discard));
|
||||||
|
|
||||||
Notice = new Log(LogLevel.Notice);
|
Notice = new Log(LogLevel.Notice);
|
||||||
|
|
||||||
// Enable important log levels before configuration is loaded
|
// Enable important log levels before configuration is loaded
|
||||||
Error = new Log(LogLevel.Error);
|
Error = new Log(LogLevel.Error);
|
||||||
Warning = new Log(LogLevel.Warning);
|
Warning = new Log(LogLevel.Warning);
|
||||||
@ -221,4 +232,4 @@ namespace Ryujinx.Common.Logging
|
|||||||
m_EnabledClasses[(int)logClass] = enabled;
|
m_EnabledClasses[(int)logClass] = enabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,7 +2,7 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging.Targets
|
||||||
{
|
{
|
||||||
public enum AsyncLogTargetOverflowAction
|
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
|
public class ConsoleLogTarget : ILogTarget
|
||||||
{
|
{
|
||||||
@ -38,4 +39,4 @@ namespace Ryujinx.Common.Logging
|
|||||||
Console.ResetColor();
|
Console.ResetColor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,8 +1,9 @@
|
|||||||
using System;
|
using Ryujinx.Common.Logging.Formatters;
|
||||||
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging.Targets
|
||||||
{
|
{
|
||||||
public class FileLogTarget : ILogTarget
|
public class FileLogTarget : ILogTarget
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging.Targets
|
||||||
{
|
{
|
||||||
public interface ILogTarget : IDisposable
|
public interface ILogTarget : IDisposable
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging.Targets
|
||||||
{
|
{
|
||||||
public class JsonLogTarget : ILogTarget
|
public class JsonLogTarget : ILogTarget
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using System;
|
using System;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
@ -18,12 +19,14 @@ namespace Ryujinx.Common.Utilities
|
|||||||
public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
var enumValue = reader.GetString();
|
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)
|
public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options)
|
||||||
@ -31,4 +34,4 @@ namespace Ryujinx.Common.Utilities
|
|||||||
writer.WriteStringValue(value.ToString());
|
writer.WriteStringValue(value.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -34,11 +34,14 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
public readonly bool SupportsCubemapView;
|
public readonly bool SupportsCubemapView;
|
||||||
public readonly bool SupportsNonConstantTextureOffset;
|
public readonly bool SupportsNonConstantTextureOffset;
|
||||||
public readonly bool SupportsShaderBallot;
|
public readonly bool SupportsShaderBallot;
|
||||||
|
public readonly bool SupportsShaderBarrierDivergence;
|
||||||
|
public readonly bool SupportsShaderFloat64;
|
||||||
public readonly bool SupportsTextureShadowLod;
|
public readonly bool SupportsTextureShadowLod;
|
||||||
public readonly bool SupportsViewportIndexVertexTessellation;
|
public readonly bool SupportsViewportIndexVertexTessellation;
|
||||||
public readonly bool SupportsViewportMask;
|
public readonly bool SupportsViewportMask;
|
||||||
public readonly bool SupportsViewportSwizzle;
|
public readonly bool SupportsViewportSwizzle;
|
||||||
public readonly bool SupportsIndirectParameters;
|
public readonly bool SupportsIndirectParameters;
|
||||||
|
public readonly bool SupportsDepthClipControl;
|
||||||
|
|
||||||
public readonly uint MaximumUniformBuffersPerStage;
|
public readonly uint MaximumUniformBuffersPerStage;
|
||||||
public readonly uint MaximumStorageBuffersPerStage;
|
public readonly uint MaximumStorageBuffersPerStage;
|
||||||
@ -80,11 +83,14 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
bool supportsCubemapView,
|
bool supportsCubemapView,
|
||||||
bool supportsNonConstantTextureOffset,
|
bool supportsNonConstantTextureOffset,
|
||||||
bool supportsShaderBallot,
|
bool supportsShaderBallot,
|
||||||
|
bool supportsShaderBarrierDivergence,
|
||||||
|
bool supportsShaderFloat64,
|
||||||
bool supportsTextureShadowLod,
|
bool supportsTextureShadowLod,
|
||||||
bool supportsViewportIndexVertexTessellation,
|
bool supportsViewportIndexVertexTessellation,
|
||||||
bool supportsViewportMask,
|
bool supportsViewportMask,
|
||||||
bool supportsViewportSwizzle,
|
bool supportsViewportSwizzle,
|
||||||
bool supportsIndirectParameters,
|
bool supportsIndirectParameters,
|
||||||
|
bool supportsDepthClipControl,
|
||||||
uint maximumUniformBuffersPerStage,
|
uint maximumUniformBuffersPerStage,
|
||||||
uint maximumStorageBuffersPerStage,
|
uint maximumStorageBuffersPerStage,
|
||||||
uint maximumTexturesPerStage,
|
uint maximumTexturesPerStage,
|
||||||
@ -122,11 +128,14 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
SupportsCubemapView = supportsCubemapView;
|
SupportsCubemapView = supportsCubemapView;
|
||||||
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
|
SupportsNonConstantTextureOffset = supportsNonConstantTextureOffset;
|
||||||
SupportsShaderBallot = supportsShaderBallot;
|
SupportsShaderBallot = supportsShaderBallot;
|
||||||
|
SupportsShaderBarrierDivergence = supportsShaderBarrierDivergence;
|
||||||
|
SupportsShaderFloat64 = supportsShaderFloat64;
|
||||||
SupportsTextureShadowLod = supportsTextureShadowLod;
|
SupportsTextureShadowLod = supportsTextureShadowLod;
|
||||||
SupportsViewportIndexVertexTessellation = supportsViewportIndexVertexTessellation;
|
SupportsViewportIndexVertexTessellation = supportsViewportIndexVertexTessellation;
|
||||||
SupportsViewportMask = supportsViewportMask;
|
SupportsViewportMask = supportsViewportMask;
|
||||||
SupportsViewportSwizzle = supportsViewportSwizzle;
|
SupportsViewportSwizzle = supportsViewportSwizzle;
|
||||||
SupportsIndirectParameters = supportsIndirectParameters;
|
SupportsIndirectParameters = supportsIndirectParameters;
|
||||||
|
SupportsDepthClipControl = supportsDepthClipControl;
|
||||||
MaximumUniformBuffersPerStage = maximumUniformBuffersPerStage;
|
MaximumUniformBuffersPerStage = maximumUniformBuffersPerStage;
|
||||||
MaximumStorageBuffersPerStage = maximumStorageBuffersPerStage;
|
MaximumStorageBuffersPerStage = maximumStorageBuffersPerStage;
|
||||||
MaximumTexturesPerStage = maximumTexturesPerStage;
|
MaximumTexturesPerStage = maximumTexturesPerStage;
|
||||||
|
@ -383,6 +383,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
case Format.R10G10B10A2Unorm:
|
case Format.R10G10B10A2Unorm:
|
||||||
case Format.R10G10B10A2Uint:
|
case Format.R10G10B10A2Uint:
|
||||||
case Format.R11G11B10Float:
|
case Format.R11G11B10Float:
|
||||||
|
case Format.B8G8R8A8Unorm:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL
|
namespace Ryujinx.Graphics.GAL
|
||||||
{
|
{
|
||||||
@ -52,7 +53,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
|
|
||||||
void ResetCounter(CounterType type);
|
void ResetCounter(CounterType type);
|
||||||
|
|
||||||
void RunLoop(Action gpuLoop)
|
void RunLoop(ThreadStart gpuLoop)
|
||||||
{
|
{
|
||||||
gpuLoop();
|
gpuLoop();
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
private IRenderer _baseRenderer;
|
private IRenderer _baseRenderer;
|
||||||
private Thread _gpuThread;
|
private Thread _gpuThread;
|
||||||
private Thread _backendThread;
|
private Thread _backendThread;
|
||||||
private bool _disposed;
|
|
||||||
private bool _running;
|
private bool _running;
|
||||||
|
|
||||||
private AutoResetEvent _frameComplete = new AutoResetEvent(true);
|
private AutoResetEvent _frameComplete = new AutoResetEvent(true);
|
||||||
@ -98,19 +97,17 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
_refQueue = new object[MaxRefsPerCommand * QueueCount];
|
_refQueue = new object[MaxRefsPerCommand * QueueCount];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RunLoop(Action gpuLoop)
|
public void RunLoop(ThreadStart gpuLoop)
|
||||||
{
|
{
|
||||||
_running = true;
|
_running = true;
|
||||||
|
|
||||||
_backendThread = Thread.CurrentThread;
|
_backendThread = Thread.CurrentThread;
|
||||||
|
|
||||||
_gpuThread = new Thread(() => {
|
_gpuThread = new Thread(gpuLoop)
|
||||||
gpuLoop();
|
{
|
||||||
_running = false;
|
Name = "GPU.MainThread"
|
||||||
_galWorkAvailable.Set();
|
};
|
||||||
});
|
|
||||||
|
|
||||||
_gpuThread.Name = "GPU.MainThread";
|
|
||||||
_gpuThread.Start();
|
_gpuThread.Start();
|
||||||
|
|
||||||
RenderLoop();
|
RenderLoop();
|
||||||
@ -120,7 +117,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
{
|
{
|
||||||
// Power through the render queue until the Gpu thread work is done.
|
// Power through the render queue until the Gpu thread work is done.
|
||||||
|
|
||||||
while (_running && !_disposed)
|
while (_running)
|
||||||
{
|
{
|
||||||
_galWorkAvailable.Wait();
|
_galWorkAvailable.Wait();
|
||||||
_galWorkAvailable.Reset();
|
_galWorkAvailable.Reset();
|
||||||
@ -488,12 +485,23 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
return _baseRenderer.PrepareHostMapping(address, size);
|
return _baseRenderer.PrepareHostMapping(address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void FlushThreadedCommands()
|
||||||
|
{
|
||||||
|
SpinWait wait = new();
|
||||||
|
|
||||||
|
while (Volatile.Read(ref _commandCount) > 0)
|
||||||
|
{
|
||||||
|
wait.SpinOnce();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
// Dispose must happen from the render thread, after all commands have completed.
|
// Dispose must happen from the render thread, after all commands have completed.
|
||||||
|
|
||||||
// Stop the GPU thread.
|
// Stop the GPU thread.
|
||||||
_disposed = true;
|
_running = false;
|
||||||
|
_galWorkAvailable.Set();
|
||||||
|
|
||||||
if (_gpuThread != null && _gpuThread.IsAlive)
|
if (_gpuThread != null && _gpuThread.IsAlive)
|
||||||
{
|
{
|
||||||
|
@ -63,6 +63,8 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
public bool PrimitiveRestartEnable;
|
public bool PrimitiveRestartEnable;
|
||||||
public uint PatchControlPoints;
|
public uint PatchControlPoints;
|
||||||
|
|
||||||
|
public DepthMode DepthMode;
|
||||||
|
|
||||||
public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
|
public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
|
||||||
{
|
{
|
||||||
VertexAttribCount = vertexAttribs.Length;
|
VertexAttribCount = vertexAttribs.Length;
|
||||||
|
@ -80,11 +80,6 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const int GobAlignment = 64;
|
public const int GobAlignment = 64;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Expected byte alignment for storage buffers
|
|
||||||
/// </summary>
|
|
||||||
public const int StorageAlignment = 16;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Number of the uniform buffer reserved by the driver to store the storage buffer base addresses.
|
/// Number of the uniform buffer reserved by the driver to store the storage buffer base addresses.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -151,8 +151,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
|||||||
|
|
||||||
ShaderProgramInfo info = cs.Shaders[0].Info;
|
ShaderProgramInfo info = cs.Shaders[0].Info;
|
||||||
|
|
||||||
bool hasUnaligned = _channel.BufferManager.HasUnalignedStorageBuffers;
|
|
||||||
|
|
||||||
for (int index = 0; index < info.SBuffers.Count; index++)
|
for (int index = 0; index < info.SBuffers.Count; index++)
|
||||||
{
|
{
|
||||||
BufferDescriptor sb = info.SBuffers[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);
|
_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.
|
// 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);
|
cs = memoryManager.Physical.ShaderCache.GetComputeShader(_channel, poolState, computeState, shaderGpuVa);
|
||||||
|
|
||||||
_context.Renderer.Pipeline.SetProgram(cs.HostProgram);
|
_context.Renderer.Pipeline.SetProgram(cs.HostProgram);
|
||||||
@ -187,30 +193,6 @@ namespace Ryujinx.Graphics.Gpu.Engine.Compute
|
|||||||
info = cs.Shaders[0].Info;
|
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.BufferManager.SetComputeBufferBindings(cs.Bindings);
|
||||||
|
|
||||||
_channel.TextureManager.SetComputeBindings(cs.Bindings);
|
_channel.TextureManager.SetComputeBindings(cs.Bindings);
|
||||||
|
@ -771,7 +771,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateDepthMode()
|
private void UpdateDepthMode()
|
||||||
{
|
{
|
||||||
_context.Renderer.Pipeline.SetDepthMode(GetDepthMode());
|
DepthMode mode = GetDepthMode();
|
||||||
|
|
||||||
|
_pipeline.DepthMode = mode;
|
||||||
|
|
||||||
|
_context.Renderer.Pipeline.SetDepthMode(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -390,7 +390,6 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Renderer.Dispose();
|
|
||||||
GPFifo.Dispose();
|
GPFifo.Dispose();
|
||||||
HostInitalized.Dispose();
|
HostInitalized.Dispose();
|
||||||
|
|
||||||
@ -403,6 +402,8 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
PhysicalMemoryRegistry.Clear();
|
PhysicalMemoryRegistry.Clear();
|
||||||
|
|
||||||
RunDeferredActions();
|
RunDeferredActions();
|
||||||
|
|
||||||
|
Renderer.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -541,7 +541,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
depth,
|
depth,
|
||||||
lhs.FormatInfo.BlockHeight,
|
lhs.FormatInfo.BlockHeight,
|
||||||
lhs.GobBlocksInY,
|
lhs.GobBlocksInY,
|
||||||
lhs.GobBlocksInZ);
|
lhs.GobBlocksInZ,
|
||||||
|
level);
|
||||||
|
|
||||||
return gobBlocksInY == rhs.GobBlocksInY &&
|
return gobBlocksInY == rhs.GobBlocksInY &&
|
||||||
gobBlocksInZ == rhs.GobBlocksInZ;
|
gobBlocksInZ == rhs.GobBlocksInZ;
|
||||||
@ -587,7 +588,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
lhsDepth,
|
lhsDepth,
|
||||||
lhs.FormatInfo.BlockHeight,
|
lhs.FormatInfo.BlockHeight,
|
||||||
lhs.GobBlocksInY,
|
lhs.GobBlocksInY,
|
||||||
lhs.GobBlocksInZ);
|
lhs.GobBlocksInZ,
|
||||||
|
lhsLevel);
|
||||||
|
|
||||||
int rhsHeight = Math.Max(1, rhs.Height >> rhsLevel);
|
int rhsHeight = Math.Max(1, rhs.Height >> rhsLevel);
|
||||||
int rhsDepth = Math.Max(1, rhs.GetDepth() >> rhsLevel);
|
int rhsDepth = Math.Max(1, rhs.GetDepth() >> rhsLevel);
|
||||||
@ -597,7 +599,8 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
rhsDepth,
|
rhsDepth,
|
||||||
rhs.FormatInfo.BlockHeight,
|
rhs.FormatInfo.BlockHeight,
|
||||||
rhs.GobBlocksInY,
|
rhs.GobBlocksInY,
|
||||||
rhs.GobBlocksInZ);
|
rhs.GobBlocksInZ,
|
||||||
|
rhsLevel);
|
||||||
|
|
||||||
return lhsGobBlocksInY == rhsGobBlocksInY &&
|
return lhsGobBlocksInY == rhsGobBlocksInY &&
|
||||||
lhsGobBlocksInZ == rhsGobBlocksInZ;
|
lhsGobBlocksInZ == rhsGobBlocksInZ;
|
||||||
|
@ -484,7 +484,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
|||||||
depthOrLayers = Math.Max(1, depthOrLayers >> minLod);
|
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;
|
levels = (maxLod - minLod) + 1;
|
||||||
|
@ -222,7 +222,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
|
|||||||
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
|
/// <param name="gpuVa">Start GPU virtual address of the buffer</param>
|
||||||
private void RecordStorageAlignment(BuffersPerStage buffers, int index, ulong gpuVa)
|
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)
|
if (unaligned || HasUnalignedStorageBuffers)
|
||||||
{
|
{
|
||||||
|
@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
private const ushort FileFormatVersionMajor = 1;
|
private const ushort FileFormatVersionMajor = 1;
|
||||||
private const ushort FileFormatVersionMinor = 2;
|
private const ushort FileFormatVersionMinor = 2;
|
||||||
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
|
||||||
private const uint CodeGenVersion = 4646;
|
private const uint CodeGenVersion = 5044;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
|
@ -17,8 +17,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
private readonly ResourceCounts _resourceCounts;
|
private readonly ResourceCounts _resourceCounts;
|
||||||
private readonly int _stageIndex;
|
private readonly int _stageIndex;
|
||||||
|
|
||||||
private readonly int[] _constantBufferBindings;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new GPU accessor.
|
/// Creates a new GPU accessor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -28,12 +26,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
_context = context;
|
_context = context;
|
||||||
_resourceCounts = resourceCounts;
|
_resourceCounts = resourceCounts;
|
||||||
_stageIndex = stageIndex;
|
_stageIndex = stageIndex;
|
||||||
|
|
||||||
if (context.Capabilities.Api != TargetApi.Vulkan)
|
|
||||||
{
|
|
||||||
_constantBufferBindings = new int[Constants.TotalGpUniformBuffers];
|
|
||||||
_constantBufferBindings.AsSpan().Fill(-1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int QueryBindingConstantBuffer(int index)
|
public int QueryBindingConstantBuffer(int index)
|
||||||
@ -45,15 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int binding = _constantBufferBindings[index];
|
return _resourceCounts.UniformBuffersCount++;
|
||||||
|
|
||||||
if (binding < 0)
|
|
||||||
{
|
|
||||||
binding = _resourceCounts.UniformBuffersCount++;
|
|
||||||
_constantBufferBindings[index] = binding;
|
|
||||||
}
|
|
||||||
|
|
||||||
return binding;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,6 +141,10 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
|
|
||||||
public bool QueryHostSupportsShaderBallot() => _context.Capabilities.SupportsShaderBallot;
|
public bool QueryHostSupportsShaderBallot() => _context.Capabilities.SupportsShaderBallot;
|
||||||
|
|
||||||
|
public bool QueryHostSupportsShaderBarrierDivergence() => _context.Capabilities.SupportsShaderBarrierDivergence;
|
||||||
|
|
||||||
|
public bool QueryHostSupportsShaderFloat64() => _context.Capabilities.SupportsShaderFloat64;
|
||||||
|
|
||||||
public bool QueryHostSupportsSnormBufferTextureFormat() => _context.Capabilities.SupportsSnormBufferTextureFormat;
|
public bool QueryHostSupportsSnormBufferTextureFormat() => _context.Capabilities.SupportsSnormBufferTextureFormat;
|
||||||
|
|
||||||
public bool QueryHostSupportsTextureShadowLod() => _context.Capabilities.SupportsTextureShadowLod;
|
public bool QueryHostSupportsTextureShadowLod() => _context.Capabilities.SupportsTextureShadowLod;
|
||||||
@ -165,6 +153,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
|
|
||||||
public bool QueryHostSupportsViewportMask() => _context.Capabilities.SupportsViewportMask;
|
public bool QueryHostSupportsViewportMask() => _context.Capabilities.SupportsViewportMask;
|
||||||
|
|
||||||
|
public bool QueryHostSupportsDepthClipControl() => _context.Capabilities.SupportsDepthClipControl;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Converts a packed Maxwell texture format to the shader translator texture format.
|
/// Converts a packed Maxwell texture format to the shader translator texture format.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -92,7 +92,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
int imageBinding = stageIndex * imagesPerStage * 2;
|
int imageBinding = stageIndex * imagesPerStage * 2;
|
||||||
|
|
||||||
AddDescriptor(stages, ResourceType.UniformBuffer, UniformSetIndex, uniformBinding, uniformsPerStage);
|
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.TextureAndSampler, ResourceType.BufferTexture, TextureSetIndex, textureBinding, texturesPerStage);
|
||||||
AddDualDescriptor(stages, ResourceType.Image, ResourceType.BufferImage, ImageSetIndex, imageBinding, imagesPerStage);
|
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);
|
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>
|
/// <summary>
|
||||||
/// Adds buffer usage information to the list of usages.
|
/// Adds buffer usage information to the list of usages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -736,6 +736,19 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
return MatchesTexture(specializationState, descriptor);
|
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>
|
/// <summary>
|
||||||
/// Reads shader specialization state that has been serialized.
|
/// Reads shader specialization state that has been serialized.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -776,6 +789,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
ProgramPipelineState pipelineState = default;
|
ProgramPipelineState pipelineState = default;
|
||||||
dataReader.ReadWithMagicAndSize(ref pipelineState, PgpsMagic);
|
dataReader.ReadWithMagicAndSize(ref pipelineState, PgpsMagic);
|
||||||
|
|
||||||
|
specState.PreparePipelineState(ref pipelineState);
|
||||||
specState.PipelineState = pipelineState;
|
specState.PipelineState = pipelineState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,6 +127,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
public Capabilities GetCapabilities()
|
public Capabilities GetCapabilities()
|
||||||
{
|
{
|
||||||
bool intelWindows = HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows;
|
bool intelWindows = HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelWindows;
|
||||||
|
bool intelUnix = HwCapabilities.Vendor == HwCapabilities.GpuVendor.IntelUnix;
|
||||||
bool amdWindows = HwCapabilities.Vendor == HwCapabilities.GpuVendor.AmdWindows;
|
bool amdWindows = HwCapabilities.Vendor == HwCapabilities.GpuVendor.AmdWindows;
|
||||||
|
|
||||||
return new Capabilities(
|
return new Capabilities(
|
||||||
@ -158,11 +159,14 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
supportsCubemapView: true,
|
supportsCubemapView: true,
|
||||||
supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset,
|
supportsNonConstantTextureOffset: HwCapabilities.SupportsNonConstantTextureOffset,
|
||||||
supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
|
supportsShaderBallot: HwCapabilities.SupportsShaderBallot,
|
||||||
|
supportsShaderBarrierDivergence: !(intelWindows || intelUnix),
|
||||||
|
supportsShaderFloat64: true,
|
||||||
supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod,
|
supportsTextureShadowLod: HwCapabilities.SupportsTextureShadowLod,
|
||||||
supportsViewportIndexVertexTessellation: HwCapabilities.SupportsShaderViewportLayerArray,
|
supportsViewportIndexVertexTessellation: HwCapabilities.SupportsShaderViewportLayerArray,
|
||||||
supportsViewportMask: HwCapabilities.SupportsViewportArray2,
|
supportsViewportMask: HwCapabilities.SupportsViewportArray2,
|
||||||
supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle,
|
supportsViewportSwizzle: HwCapabilities.SupportsViewportSwizzle,
|
||||||
supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters,
|
supportsIndirectParameters: HwCapabilities.SupportsIndirectParameters,
|
||||||
|
supportsDepthClipControl: true,
|
||||||
maximumUniformBuffersPerStage: 13, // TODO: Avoid hardcoding those limits here and get from driver?
|
maximumUniformBuffersPerStage: 13, // TODO: Avoid hardcoding those limits here and get from driver?
|
||||||
maximumStorageBuffersPerStage: 16,
|
maximumStorageBuffersPerStage: 16,
|
||||||
maximumTexturesPerStage: 32,
|
maximumTexturesPerStage: 32,
|
||||||
|
@ -104,14 +104,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
}
|
}
|
||||||
|
|
||||||
DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values);
|
DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values);
|
||||||
|
DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values);
|
||||||
var sBufferDescriptors = context.Config.GetStorageBufferDescriptors();
|
|
||||||
if (sBufferDescriptors.Length != 0)
|
|
||||||
{
|
|
||||||
DeclareStorages(context, sBufferDescriptors);
|
|
||||||
|
|
||||||
context.AppendLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
var textureDescriptors = context.Config.GetTextureDescriptors();
|
var textureDescriptors = context.Config.GetTextureDescriptors();
|
||||||
if (textureDescriptors.Length != 0)
|
if (textureDescriptors.Length != 0)
|
||||||
@ -239,33 +232,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
context.AppendLine();
|
context.AppendLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isFragment = context.Config.Stage == ShaderStage.Fragment;
|
if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryEarlyZForce())
|
||||||
|
|
||||||
if (isFragment || context.Config.Stage == ShaderStage.Compute || context.Config.Stage == ShaderStage.Vertex)
|
|
||||||
{
|
{
|
||||||
if (isFragment && context.Config.GpuAccessor.QueryEarlyZForce())
|
context.AppendLine("layout(early_fragment_tests) in;");
|
||||||
{
|
context.AppendLine();
|
||||||
context.AppendLine("layout(early_fragment_tests) in;");
|
|
||||||
context.AppendLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((context.Config.UsedFeatures & (FeatureFlags.FragCoordXY | FeatureFlags.IntegerSampling)) != 0)
|
|
||||||
{
|
|
||||||
string stage = OperandManager.GetShaderStagePrefix(context.Config.Stage);
|
|
||||||
|
|
||||||
int scaleElements = context.Config.GetTextureDescriptors().Length + context.Config.GetImageDescriptors().Length;
|
|
||||||
|
|
||||||
if (isFragment)
|
|
||||||
{
|
|
||||||
scaleElements++; // Also includes render target scale, for gl_FragCoord.
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.Config.UsedFeatures.HasFlag(FeatureFlags.IntegerSampling) && scaleElements != 0)
|
|
||||||
{
|
|
||||||
AppendHelperFunction(context, $"Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/TexelFetchScale_{stage}.glsl");
|
|
||||||
context.AppendLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((info.HelperFunctionsMask & HelperFunctionsMask.AtomicMinMaxS32Shared) != 0)
|
if ((info.HelperFunctionsMask & HelperFunctionsMask.AtomicMinMaxS32Shared) != 0)
|
||||||
@ -273,11 +243,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/AtomicMinMaxS32Shared.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)
|
if ((info.HelperFunctionsMask & HelperFunctionsMask.MultiplyHighS32) != 0)
|
||||||
{
|
{
|
||||||
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighS32.glsl");
|
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/MultiplyHighS32.glsl");
|
||||||
@ -313,11 +278,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/StoreSharedSmallInt.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)
|
if ((info.HelperFunctionsMask & HelperFunctionsMask.SwizzleAdd) != 0)
|
||||||
{
|
{
|
||||||
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/SwizzleAdd.glsl");
|
AppendHelperFunction(context, "Ryujinx.Graphics.Shader/CodeGen/Glsl/HelperFunctions/SwizzleAdd.glsl");
|
||||||
@ -379,6 +339,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void DeclareConstantBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers)
|
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)
|
foreach (BufferDefinition buffer in buffers)
|
||||||
{
|
{
|
||||||
@ -388,7 +358,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
_ => "std430"
|
_ => "std430"
|
||||||
};
|
};
|
||||||
|
|
||||||
context.AppendLine($"layout (binding = {buffer.Binding}, {layout}) uniform _{buffer.Name}");
|
context.AppendLine($"layout (binding = {buffer.Binding}, {layout}) {declType} _{buffer.Name}");
|
||||||
context.EnterScope();
|
context.EnterScope();
|
||||||
|
|
||||||
foreach (StructureField field in buffer.Type.Fields)
|
foreach (StructureField field in buffer.Type.Fields)
|
||||||
@ -396,9 +366,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
if (field.Type.HasFlag(AggregateType.Array))
|
if (field.Type.HasFlag(AggregateType.Array))
|
||||||
{
|
{
|
||||||
string typeName = GetVarTypeName(context, field.Type & ~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
|
else
|
||||||
{
|
{
|
||||||
@ -413,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)
|
private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors)
|
||||||
{
|
{
|
||||||
int arraySize = 0;
|
int arraySize = 0;
|
||||||
@ -756,7 +718,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
|
|
||||||
code = code.Replace("\t", CodeGenContext.Tab);
|
code = code.Replace("\t", CodeGenContext.Tab);
|
||||||
code = code.Replace("$SHARED_MEM$", DefaultNames.SharedMemoryName);
|
code = code.Replace("$SHARED_MEM$", DefaultNames.SharedMemoryName);
|
||||||
code = code.Replace("$STORAGE_MEM$", OperandManager.GetShaderStagePrefix(context.Config.Stage) + "_" + DefaultNames.StorageNamePrefix);
|
|
||||||
|
|
||||||
if (context.Config.GpuAccessor.QueryHostSupportsShaderBallot())
|
if (context.Config.GpuAccessor.QueryHostSupportsShaderBallot())
|
||||||
{
|
{
|
||||||
|
@ -11,12 +11,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
public const string IAttributePrefix = "in_attr";
|
public const string IAttributePrefix = "in_attr";
|
||||||
public const string OAttributePrefix = "out_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 LocalMemoryName = "local_mem";
|
||||||
public const string SharedMemoryName = "shared_mem";
|
public const string SharedMemoryName = "shared_mem";
|
||||||
|
|
||||||
|
@ -28,18 +28,18 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
|
|
||||||
for (int i = 1; i < info.Functions.Count; i++)
|
for (int i = 1; i < info.Functions.Count; i++)
|
||||||
{
|
{
|
||||||
PrintFunction(context, info, info.Functions[i]);
|
PrintFunction(context, info.Functions[i]);
|
||||||
|
|
||||||
context.AppendLine();
|
context.AppendLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PrintFunction(context, info, info.Functions[0], MainFunctionName);
|
PrintFunction(context, info.Functions[0], MainFunctionName);
|
||||||
|
|
||||||
return context.GetCode();
|
return context.GetCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void PrintFunction(CodeGenContext context, StructuredProgramInfo info, StructuredFunction function, string funcName = null)
|
private static void PrintFunction(CodeGenContext context, StructuredFunction function, string funcName = null)
|
||||||
{
|
{
|
||||||
context.CurrentFunction = function;
|
context.CurrentFunction = function;
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
|
|
||||||
Declarations.DeclareLocals(context, function);
|
Declarations.DeclareLocals(context, function);
|
||||||
|
|
||||||
PrintBlock(context, function.MainBlock);
|
PrintBlock(context, function.MainBlock, funcName == MainFunctionName);
|
||||||
|
|
||||||
context.LeaveScope();
|
context.LeaveScope();
|
||||||
}
|
}
|
||||||
@ -72,7 +72,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
return $"{Declarations.GetVarTypeName(context, function.ReturnType)} {funcName ?? function.Name}({string.Join(", ", args)})";
|
return $"{Declarations.GetVarTypeName(context, function.ReturnType)} {funcName ?? function.Name}({string.Join(", ", args)})";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void PrintBlock(CodeGenContext context, AstBlock block)
|
private static void PrintBlock(CodeGenContext context, AstBlock block, bool isMainFunction)
|
||||||
{
|
{
|
||||||
AstBlockVisitor visitor = new AstBlockVisitor(block);
|
AstBlockVisitor visitor = new AstBlockVisitor(block);
|
||||||
|
|
||||||
@ -112,10 +112,32 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool supportsBarrierDivergence = context.Config.GpuAccessor.QueryHostSupportsShaderBarrierDivergence();
|
||||||
|
bool mayHaveReturned = false;
|
||||||
|
|
||||||
foreach (IAstNode node in visitor.Visit())
|
foreach (IAstNode node in visitor.Visit())
|
||||||
{
|
{
|
||||||
if (node is AstOperation operation)
|
if (node is AstOperation operation)
|
||||||
{
|
{
|
||||||
|
if (!supportsBarrierDivergence)
|
||||||
|
{
|
||||||
|
if (operation.Inst == IntermediateRepresentation.Instruction.Barrier)
|
||||||
|
{
|
||||||
|
// Barrier on divergent control flow paths may cause the GPU to hang,
|
||||||
|
// so skip emitting the barrier for those cases.
|
||||||
|
if (visitor.Block.Type != AstBlockType.Main || mayHaveReturned || !isMainFunction)
|
||||||
|
{
|
||||||
|
context.Config.GpuAccessor.Log($"Shader has barrier on potentially divergent block, the barrier will be removed.");
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (operation.Inst == IntermediateRepresentation.Instruction.Return)
|
||||||
|
{
|
||||||
|
mayHaveReturned = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
string expr = InstGen.GetExpression(context, operation);
|
string expr = InstGen.GetExpression(context, operation);
|
||||||
|
|
||||||
if (expr != null)
|
if (expr != null)
|
||||||
|
@ -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);
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
|
|
||||||
{
|
|
||||||
float scale = support_buffer.s_render_scale[1 + samplerIndex];
|
|
||||||
if (scale == 1.0)
|
|
||||||
{
|
|
||||||
return inputVec;
|
|
||||||
}
|
|
||||||
return ivec2(vec2(inputVec) * scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Helper_TextureSizeUnscale(int size, int samplerIndex)
|
|
||||||
{
|
|
||||||
float scale = support_buffer.s_render_scale[1 + samplerIndex];
|
|
||||||
if (scale == 1.0)
|
|
||||||
{
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
return int(float(size) / scale);
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
|
|
||||||
{
|
|
||||||
float scale = support_buffer.s_render_scale[1 + samplerIndex];
|
|
||||||
if (scale == 1.0)
|
|
||||||
{
|
|
||||||
return inputVec;
|
|
||||||
}
|
|
||||||
if (scale < 0.0) // If less than 0, try interpolate between texels by using the screen position.
|
|
||||||
{
|
|
||||||
return ivec2(vec2(inputVec) * (-scale) + mod(gl_FragCoord.xy, 0.0 - scale));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return ivec2(vec2(inputVec) * scale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int Helper_TextureSizeUnscale(int size, int samplerIndex)
|
|
||||||
{
|
|
||||||
float scale = abs(support_buffer.s_render_scale[1 + samplerIndex]);
|
|
||||||
if (scale == 1.0)
|
|
||||||
{
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
return int(float(size) / scale);
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
ivec2 Helper_TexelFetchScale(ivec2 inputVec, int samplerIndex)
|
|
||||||
{
|
|
||||||
float scale = abs(support_buffer.s_render_scale[1 + samplerIndex + support_buffer.s_frag_scale_count]);
|
|
||||||
if (scale == 1.0)
|
|
||||||
{
|
|
||||||
return inputVec;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ivec2(vec2(inputVec) * scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
int Helper_TextureSizeUnscale(int size, int samplerIndex)
|
|
||||||
{
|
|
||||||
float scale = abs(support_buffer.s_render_scale[1 + samplerIndex + support_buffer.s_frag_scale_count]);
|
|
||||||
if (scale == 1.0)
|
|
||||||
{
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
return int(float(size) / scale);
|
|
||||||
}
|
|
@ -68,33 +68,45 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
|
|
||||||
string args = string.Empty;
|
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.
|
// 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.
|
// 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.
|
// 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 += ", ";
|
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;
|
args += ", ";
|
||||||
case StorageKind.StorageBuffer: args += LoadStorage(context, operation); break;
|
|
||||||
|
|
||||||
default: throw new InvalidOperationException($"Invalid storage kind \"{operation.StorageKind}\".");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
AggregateType dstType = GetSrcVarType(inst, argIndex);
|
AggregateType dstType = GetSrcVarType(inst, argIndex);
|
||||||
|
|
||||||
args += GetSoureExpr(context, operation.GetSource(argIndex), dstType);
|
args += GetSoureExpr(context, operation.GetSource(argIndex), dstType);
|
||||||
@ -173,9 +185,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
case Instruction.LoadShared:
|
case Instruction.LoadShared:
|
||||||
return LoadShared(context, operation);
|
return LoadShared(context, operation);
|
||||||
|
|
||||||
case Instruction.LoadStorage:
|
|
||||||
return LoadStorage(context, operation);
|
|
||||||
|
|
||||||
case Instruction.Lod:
|
case Instruction.Lod:
|
||||||
return Lod(context, operation);
|
return Lod(context, operation);
|
||||||
|
|
||||||
@ -203,15 +212,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
case Instruction.StoreShared8:
|
case Instruction.StoreShared8:
|
||||||
return StoreShared8(context, operation);
|
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:
|
case Instruction.TextureSample:
|
||||||
return TextureSample(context, operation);
|
return TextureSample(context, operation);
|
||||||
|
|
||||||
|
@ -85,7 +85,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
Add(Instruction.Load, InstType.Special);
|
Add(Instruction.Load, InstType.Special);
|
||||||
Add(Instruction.LoadLocal, InstType.Special);
|
Add(Instruction.LoadLocal, InstType.Special);
|
||||||
Add(Instruction.LoadShared, InstType.Special);
|
Add(Instruction.LoadShared, InstType.Special);
|
||||||
Add(Instruction.LoadStorage, InstType.Special);
|
|
||||||
Add(Instruction.Lod, InstType.Special);
|
Add(Instruction.Lod, InstType.Special);
|
||||||
Add(Instruction.LogarithmB2, InstType.CallUnary, "log2");
|
Add(Instruction.LogarithmB2, InstType.CallUnary, "log2");
|
||||||
Add(Instruction.LogicalAnd, InstType.OpBinaryCom, "&&", 9);
|
Add(Instruction.LogicalAnd, InstType.OpBinaryCom, "&&", 9);
|
||||||
@ -101,6 +100,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
Add(Instruction.MemoryBarrier, InstType.CallNullary, "memoryBarrier");
|
Add(Instruction.MemoryBarrier, InstType.CallNullary, "memoryBarrier");
|
||||||
Add(Instruction.Minimum, InstType.CallBinary, "min");
|
Add(Instruction.Minimum, InstType.CallBinary, "min");
|
||||||
Add(Instruction.MinimumU32, InstType.CallBinary, "min");
|
Add(Instruction.MinimumU32, InstType.CallBinary, "min");
|
||||||
|
Add(Instruction.Modulo, InstType.CallBinary, "mod");
|
||||||
Add(Instruction.Multiply, InstType.OpBinaryCom, "*", 1);
|
Add(Instruction.Multiply, InstType.OpBinaryCom, "*", 1);
|
||||||
Add(Instruction.MultiplyHighS32, InstType.CallBinary, HelperFunctionNames.MultiplyHighS32);
|
Add(Instruction.MultiplyHighS32, InstType.CallBinary, HelperFunctionNames.MultiplyHighS32);
|
||||||
Add(Instruction.MultiplyHighU32, InstType.CallBinary, HelperFunctionNames.MultiplyHighU32);
|
Add(Instruction.MultiplyHighU32, InstType.CallBinary, HelperFunctionNames.MultiplyHighU32);
|
||||||
@ -122,9 +122,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
Add(Instruction.StoreShared, InstType.Special);
|
Add(Instruction.StoreShared, InstType.Special);
|
||||||
Add(Instruction.StoreShared16, InstType.Special);
|
Add(Instruction.StoreShared16, InstType.Special);
|
||||||
Add(Instruction.StoreShared8, 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.Subtract, InstType.OpBinary, "-", 2);
|
||||||
Add(Instruction.SwizzleAdd, InstType.CallTernary, HelperFunctionNames.SwizzleAdd);
|
Add(Instruction.SwizzleAdd, InstType.CallTernary, HelperFunctionNames.SwizzleAdd);
|
||||||
Add(Instruction.TextureSample, InstType.Special);
|
Add(Instruction.TextureSample, InstType.Special);
|
||||||
|
@ -97,30 +97,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
texCallBuilder.Append(str);
|
texCallBuilder.Append(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
string ApplyScaling(string vector)
|
|
||||||
{
|
|
||||||
if (context.Config.Stage.SupportsRenderScale() &&
|
|
||||||
texOp.Inst == Instruction.ImageLoad &&
|
|
||||||
!isBindless &&
|
|
||||||
!isIndexed)
|
|
||||||
{
|
|
||||||
// Image scales start after texture ones.
|
|
||||||
int scaleIndex = context.Config.GetTextureDescriptors().Length + context.Config.FindImageDescriptorIndex(texOp);
|
|
||||||
|
|
||||||
if (pCount == 3 && isArray)
|
|
||||||
{
|
|
||||||
// The array index is not scaled, just x and y.
|
|
||||||
vector = $"ivec3(Helper_TexelFetchScale(({vector}).xy, {scaleIndex}), ({vector}).z)";
|
|
||||||
}
|
|
||||||
else if (pCount == 2 && !isArray)
|
|
||||||
{
|
|
||||||
vector = $"Helper_TexelFetchScale({vector}, {scaleIndex})";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return vector;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pCount > 1)
|
if (pCount > 1)
|
||||||
{
|
{
|
||||||
string[] elems = new string[pCount];
|
string[] elems = new string[pCount];
|
||||||
@ -130,7 +106,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
elems[index] = Src(AggregateType.S32);
|
elems[index] = Src(AggregateType.S32);
|
||||||
}
|
}
|
||||||
|
|
||||||
Append(ApplyScaling($"ivec{pCount}({string.Join(", ", elems)})"));
|
Append($"ivec{pCount}({string.Join(", ", elems)})");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -234,17 +210,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
return $"{arrayName}[{offsetExpr}]";
|
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)
|
public static string Lod(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||||
@ -350,60 +315,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
return $"{HelperFunctionNames.StoreShared8}({offsetExpr}, {src})";
|
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)
|
public static string TextureSample(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||||
@ -584,53 +495,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
string ApplyScaling(string vector)
|
Append(AssemblePVector(pCount));
|
||||||
{
|
|
||||||
if (intCoords)
|
|
||||||
{
|
|
||||||
if (context.Config.Stage.SupportsRenderScale() &&
|
|
||||||
!isBindless &&
|
|
||||||
!isIndexed)
|
|
||||||
{
|
|
||||||
int index = context.Config.FindTextureDescriptorIndex(texOp);
|
|
||||||
|
|
||||||
if (pCount == 3 && isArray)
|
|
||||||
{
|
|
||||||
// The array index is not scaled, just x and y.
|
|
||||||
vector = "ivec3(Helper_TexelFetchScale((" + vector + ").xy, " + index + "), (" + vector + ").z)";
|
|
||||||
}
|
|
||||||
else if (pCount == 2 && !isArray)
|
|
||||||
{
|
|
||||||
vector = "Helper_TexelFetchScale(" + vector + ", " + index + ")";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return vector;
|
|
||||||
}
|
|
||||||
|
|
||||||
string ApplyBias(string vector)
|
|
||||||
{
|
|
||||||
int gatherBiasPrecision = context.Config.GpuAccessor.QueryHostGatherBiasPrecision();
|
|
||||||
if (isGather && gatherBiasPrecision != 0)
|
|
||||||
{
|
|
||||||
// GPU requires texture gather to be slightly offset to match NVIDIA behaviour when point is exactly between two texels.
|
|
||||||
// Offset by the gather precision divided by 2 to correct for rounding.
|
|
||||||
|
|
||||||
if (pCount == 1)
|
|
||||||
{
|
|
||||||
vector = $"{vector} + (1.0 / (float(textureSize({samplerName}, 0)) * float({1 << (gatherBiasPrecision + 1)})))";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
vector = $"{vector} + (1.0 / (vec{pCount}(textureSize({samplerName}, 0).{"xyz".Substring(0, pCount)}) * float({1 << (gatherBiasPrecision + 1)})))";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return vector;
|
|
||||||
}
|
|
||||||
|
|
||||||
Append(ApplyBias(ApplyScaling(AssemblePVector(pCount))));
|
|
||||||
|
|
||||||
string AssembleDerivativesVector(int count)
|
string AssembleDerivativesVector(int count)
|
||||||
{
|
{
|
||||||
@ -750,7 +615,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
(TextureDescriptor descriptor, int descriptorIndex) = context.Config.FindTextureDescriptor(texOp);
|
TextureDescriptor descriptor = context.Config.FindTextureDescriptor(texOp);
|
||||||
bool hasLod = !descriptor.Type.HasFlag(SamplerType.Multisample) && descriptor.Type != SamplerType.TextureBuffer;
|
bool hasLod = !descriptor.Type.HasFlag(SamplerType.Multisample) && descriptor.Type != SamplerType.TextureBuffer;
|
||||||
string texCall;
|
string texCall;
|
||||||
|
|
||||||
@ -767,37 +632,38 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
texCall = $"textureSize({samplerName}){GetMask(texOp.Index)}";
|
texCall = $"textureSize({samplerName}){GetMask(texOp.Index)}";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context.Config.Stage.SupportsRenderScale() &&
|
|
||||||
(texOp.Index < 2 || (texOp.Type & SamplerType.Mask) == SamplerType.Texture3D) &&
|
|
||||||
!isBindless &&
|
|
||||||
!isIndexed)
|
|
||||||
{
|
|
||||||
texCall = $"Helper_TextureSizeUnscale({texCall}, {descriptorIndex})";
|
|
||||||
}
|
|
||||||
|
|
||||||
return texCall;
|
return texCall;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore)
|
public static string GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore)
|
||||||
{
|
{
|
||||||
StorageKind storageKind = operation.StorageKind;
|
StorageKind storageKind = operation.StorageKind;
|
||||||
|
|
||||||
string varName;
|
string varName;
|
||||||
AggregateType varType;
|
AggregateType varType;
|
||||||
int srcIndex = 0;
|
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)
|
switch (storageKind)
|
||||||
{
|
{
|
||||||
case StorageKind.ConstantBuffer:
|
case StorageKind.ConstantBuffer:
|
||||||
|
case StorageKind.StorageBuffer:
|
||||||
if (!(operation.GetSource(srcIndex++) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant)
|
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.");
|
throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand.");
|
||||||
}
|
}
|
||||||
|
|
||||||
int binding = bindingIndex.Value;
|
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)
|
if (!(operation.GetSource(srcIndex++) is AstOperand fieldIndex) || fieldIndex.Type != OperandType.Constant)
|
||||||
{
|
{
|
||||||
@ -903,15 +769,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions
|
|||||||
return varName;
|
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)
|
private static string GetMask(int index)
|
||||||
{
|
{
|
||||||
return $".{"rgba".AsSpan(index, 1)}";
|
return $".{"rgba".AsSpan(index, 1)}";
|
||||||
|
@ -118,6 +118,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
|
|||||||
switch (operation.StorageKind)
|
switch (operation.StorageKind)
|
||||||
{
|
{
|
||||||
case StorageKind.ConstantBuffer:
|
case StorageKind.ConstantBuffer:
|
||||||
|
case StorageKind.StorageBuffer:
|
||||||
if (!(operation.GetSource(0) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant)
|
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.");
|
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.");
|
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];
|
StructureField field = buffer.Type.Fields[fieldIndex.Value];
|
||||||
|
|
||||||
return field.Type & AggregateType.ElementTypeMask;
|
return field.Type & AggregateType.ElementTypeMask;
|
||||||
|
@ -24,7 +24,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
public int InputVertices { get; }
|
public int InputVertices { get; }
|
||||||
|
|
||||||
public Dictionary<int, Instruction> ConstantBuffers { get; } = new Dictionary<int, Instruction>();
|
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 LocalMemory { get; set; }
|
||||||
public Instruction SharedMemory { get; set; }
|
public Instruction SharedMemory { get; set; }
|
||||||
public Dictionary<TextureMeta, SamplerType> SamplersTypes { get; } = new Dictionary<TextureMeta, SamplerType>();
|
public Dictionary<TextureMeta, SamplerType> SamplersTypes { get; } = new Dictionary<TextureMeta, SamplerType>();
|
||||||
@ -36,6 +36,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
public Dictionary<IoDefinition, Instruction> OutputsPerPatch { get; } = new Dictionary<IoDefinition, Instruction>();
|
public Dictionary<IoDefinition, Instruction> OutputsPerPatch { get; } = new Dictionary<IoDefinition, Instruction>();
|
||||||
|
|
||||||
public Instruction CoordTemp { get; set; }
|
public Instruction CoordTemp { get; set; }
|
||||||
|
public StructuredFunction CurrentFunction { get; set; }
|
||||||
private readonly Dictionary<AstOperand, Instruction> _locals = new Dictionary<AstOperand, Instruction>();
|
private readonly Dictionary<AstOperand, Instruction> _locals = new Dictionary<AstOperand, Instruction>();
|
||||||
private readonly Dictionary<int, Instruction[]> _localForArgs = new Dictionary<int, Instruction[]>();
|
private readonly Dictionary<int, Instruction[]> _localForArgs = new Dictionary<int, Instruction[]>();
|
||||||
private readonly Dictionary<int, Instruction> _funcArgs = new Dictionary<int, Instruction>();
|
private readonly Dictionary<int, Instruction> _funcArgs = new Dictionary<int, Instruction>();
|
||||||
@ -75,6 +76,9 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
public SpirvDelegates Delegates { get; }
|
public SpirvDelegates Delegates { get; }
|
||||||
|
|
||||||
|
public bool IsMainFunction { get; private set; }
|
||||||
|
public bool MayHaveReturned { get; set; }
|
||||||
|
|
||||||
public CodeGenContext(
|
public CodeGenContext(
|
||||||
StructuredProgramInfo info,
|
StructuredProgramInfo info,
|
||||||
ShaderConfig config,
|
ShaderConfig config,
|
||||||
@ -107,8 +111,10 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
Delegates = new SpirvDelegates(this);
|
Delegates = new SpirvDelegates(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StartFunction()
|
public void StartFunction(bool isMainFunction)
|
||||||
{
|
{
|
||||||
|
IsMainFunction = isMainFunction;
|
||||||
|
MayHaveReturned = false;
|
||||||
_locals.Clear();
|
_locals.Clear();
|
||||||
_localForArgs.Clear();
|
_localForArgs.Clear();
|
||||||
_funcArgs.Clear();
|
_funcArgs.Clear();
|
||||||
@ -307,7 +313,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
{
|
{
|
||||||
if ((type & AggregateType.Array) != 0)
|
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)
|
else if ((type & AggregateType.ElementCountMask) != 0)
|
||||||
{
|
{
|
||||||
|
@ -5,6 +5,7 @@ using Ryujinx.Graphics.Shader.Translation;
|
|||||||
using Spv.Generator;
|
using Spv.Generator;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using static Spv.Specification;
|
using static Spv.Specification;
|
||||||
@ -99,7 +100,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
}
|
}
|
||||||
|
|
||||||
DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values);
|
DeclareConstantBuffers(context, context.Config.Properties.ConstantBuffers.Values);
|
||||||
DeclareStorageBuffers(context, context.Config.GetStorageBufferDescriptors());
|
DeclareStorageBuffers(context, context.Config.Properties.StorageBuffers.Values);
|
||||||
DeclareSamplers(context, context.Config.GetTextureDescriptors());
|
DeclareSamplers(context, context.Config.GetTextureDescriptors());
|
||||||
DeclareImages(context, context.Config.GetImageDescriptors());
|
DeclareImages(context, context.Config.GetImageDescriptors());
|
||||||
DeclareInputsAndOutputs(context, info);
|
DeclareInputsAndOutputs(context, info);
|
||||||
@ -127,6 +128,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void DeclareConstantBuffers(CodeGenContext context, IEnumerable<BufferDefinition> buffers)
|
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>();
|
HashSet<SpvInstruction> decoratedTypes = new HashSet<SpvInstruction>();
|
||||||
|
|
||||||
@ -155,6 +166,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
context.Decorate(structFieldTypes[fieldIndex], Decoration.ArrayStride, (LiteralInteger)fieldSize);
|
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;
|
offset += fieldSize * field.ArrayLength;
|
||||||
}
|
}
|
||||||
else
|
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++)
|
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 pointerType = context.TypePointer(StorageClass.Uniform, structType);
|
||||||
var ubVariable = context.Variable(ubPointerType, StorageClass.Uniform);
|
var variable = context.Variable(pointerType, StorageClass.Uniform);
|
||||||
|
|
||||||
context.Name(ubVariable, buffer.Name);
|
context.Name(variable, buffer.Name);
|
||||||
context.Decorate(ubVariable, Decoration.DescriptorSet, (LiteralInteger)buffer.Set);
|
context.Decorate(variable, Decoration.DescriptorSet, (LiteralInteger)buffer.Set);
|
||||||
context.Decorate(ubVariable, Decoration.Binding, (LiteralInteger)buffer.Binding);
|
context.Decorate(variable, Decoration.Binding, (LiteralInteger)buffer.Binding);
|
||||||
context.AddGlobalVariable(ubVariable);
|
context.AddGlobalVariable(variable);
|
||||||
context.ConstantBuffers.Add(buffer.Binding, ubVariable);
|
|
||||||
|
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)
|
private static void DeclareSamplers(CodeGenContext context, TextureDescriptor[] descriptors)
|
||||||
{
|
{
|
||||||
foreach (var descriptor in descriptors)
|
foreach (var descriptor in descriptors)
|
||||||
|
@ -4,7 +4,6 @@ using Ryujinx.Graphics.Shader.Translation;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using static Spv.Specification;
|
using static Spv.Specification;
|
||||||
|
|
||||||
@ -100,7 +99,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
Add(Instruction.Load, GenerateLoad);
|
Add(Instruction.Load, GenerateLoad);
|
||||||
Add(Instruction.LoadLocal, GenerateLoadLocal);
|
Add(Instruction.LoadLocal, GenerateLoadLocal);
|
||||||
Add(Instruction.LoadShared, GenerateLoadShared);
|
Add(Instruction.LoadShared, GenerateLoadShared);
|
||||||
Add(Instruction.LoadStorage, GenerateLoadStorage);
|
|
||||||
Add(Instruction.Lod, GenerateLod);
|
Add(Instruction.Lod, GenerateLod);
|
||||||
Add(Instruction.LogarithmB2, GenerateLogarithmB2);
|
Add(Instruction.LogarithmB2, GenerateLogarithmB2);
|
||||||
Add(Instruction.LogicalAnd, GenerateLogicalAnd);
|
Add(Instruction.LogicalAnd, GenerateLogicalAnd);
|
||||||
@ -114,6 +112,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
Add(Instruction.MemoryBarrier, GenerateMemoryBarrier);
|
Add(Instruction.MemoryBarrier, GenerateMemoryBarrier);
|
||||||
Add(Instruction.Minimum, GenerateMinimum);
|
Add(Instruction.Minimum, GenerateMinimum);
|
||||||
Add(Instruction.MinimumU32, GenerateMinimumU32);
|
Add(Instruction.MinimumU32, GenerateMinimumU32);
|
||||||
|
Add(Instruction.Modulo, GenerateModulo);
|
||||||
Add(Instruction.Multiply, GenerateMultiply);
|
Add(Instruction.Multiply, GenerateMultiply);
|
||||||
Add(Instruction.MultiplyHighS32, GenerateMultiplyHighS32);
|
Add(Instruction.MultiplyHighS32, GenerateMultiplyHighS32);
|
||||||
Add(Instruction.MultiplyHighU32, GenerateMultiplyHighU32);
|
Add(Instruction.MultiplyHighU32, GenerateMultiplyHighU32);
|
||||||
@ -137,9 +136,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
Add(Instruction.StoreShared, GenerateStoreShared);
|
Add(Instruction.StoreShared, GenerateStoreShared);
|
||||||
Add(Instruction.StoreShared16, GenerateStoreShared16);
|
Add(Instruction.StoreShared16, GenerateStoreShared16);
|
||||||
Add(Instruction.StoreShared8, GenerateStoreShared8);
|
Add(Instruction.StoreShared8, GenerateStoreShared8);
|
||||||
Add(Instruction.StoreStorage, GenerateStoreStorage);
|
|
||||||
Add(Instruction.StoreStorage16, GenerateStoreStorage16);
|
|
||||||
Add(Instruction.StoreStorage8, GenerateStoreStorage8);
|
|
||||||
Add(Instruction.Subtract, GenerateSubtract);
|
Add(Instruction.Subtract, GenerateSubtract);
|
||||||
Add(Instruction.SwizzleAdd, GenerateSwizzleAdd);
|
Add(Instruction.SwizzleAdd, GenerateSwizzleAdd);
|
||||||
Add(Instruction.TextureSample, GenerateTextureSample);
|
Add(Instruction.TextureSample, GenerateTextureSample);
|
||||||
@ -246,6 +242,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
private static OperationResult GenerateBarrier(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateBarrier(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
|
// Barrier on divergent control flow paths may cause the GPU to hang,
|
||||||
|
// so skip emitting the barrier for those cases.
|
||||||
|
if (!context.Config.GpuAccessor.QueryHostSupportsShaderBarrierDivergence() &&
|
||||||
|
(context.CurrentBlock.Type != AstBlockType.Main || context.MayHaveReturned || !context.IsMainFunction))
|
||||||
|
{
|
||||||
|
context.Config.GpuAccessor.Log($"Shader has barrier on potentially divergent block, the barrier will be removed.");
|
||||||
|
|
||||||
|
return OperationResult.Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
context.ControlBarrier(
|
context.ControlBarrier(
|
||||||
context.Constant(context.TypeU32(), Scope.Workgroup),
|
context.Constant(context.TypeU32(), Scope.Workgroup),
|
||||||
context.Constant(context.TypeU32(), Scope.Workgroup),
|
context.Constant(context.TypeU32(), Scope.Workgroup),
|
||||||
@ -744,8 +750,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
pCoords = Src(AggregateType.S32);
|
pCoords = Src(AggregateType.S32);
|
||||||
}
|
}
|
||||||
|
|
||||||
pCoords = ScalingHelpers.ApplyScaling(context, texOp, pCoords, intCoords: true, isBindless, isIndexed, isArray, pCount);
|
|
||||||
|
|
||||||
(var imageType, var imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)];
|
(var imageType, var imageVariable) = context.Images[new TextureMeta(texOp.CbufSlot, texOp.Handle, texOp.Format)];
|
||||||
|
|
||||||
var image = context.Load(imageType, imageVariable);
|
var image = context.Load(imageType, imageVariable);
|
||||||
@ -891,14 +895,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
return new OperationResult(AggregateType.U32, value);
|
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)
|
private static OperationResult GenerateLod(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
AstTextureOperation texOp = (AstTextureOperation)operation;
|
AstTextureOperation texOp = (AstTextureOperation)operation;
|
||||||
@ -1040,6 +1036,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
return GenerateBinaryU32(context, operation, context.Delegates.GlslUMin);
|
return GenerateBinaryU32(context, operation, context.Delegates.GlslUMin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static OperationResult GenerateModulo(CodeGenContext context, AstOperation operation)
|
||||||
|
{
|
||||||
|
return GenerateBinary(context, operation, context.Delegates.FMod, null);
|
||||||
|
}
|
||||||
|
|
||||||
private static OperationResult GenerateMultiply(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateMultiply(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
return GenerateBinary(context, operation, context.Delegates.FMul, context.Delegates.IMul);
|
return GenerateBinary(context, operation, context.Delegates.FMul, context.Delegates.IMul);
|
||||||
@ -1101,7 +1102,17 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
private static OperationResult GenerateReturn(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateReturn(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
context.Return();
|
context.MayHaveReturned = true;
|
||||||
|
|
||||||
|
if (operation.SourcesCount != 0)
|
||||||
|
{
|
||||||
|
context.ReturnValue(context.Get(context.CurrentFunction.ReturnType, operation.GetSource(0)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Return();
|
||||||
|
}
|
||||||
|
|
||||||
return OperationResult.Invalid;
|
return OperationResult.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1296,28 +1307,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
return OperationResult.Invalid;
|
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)
|
private static OperationResult GenerateSubtract(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
return GenerateBinary(context, operation, context.Delegates.FSub, context.Delegates.ISub);
|
return GenerateBinary(context, operation, context.Delegates.FSub, context.Delegates.ISub);
|
||||||
@ -1439,35 +1428,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SpvInstruction ApplyBias(SpvInstruction vector, SpvInstruction image)
|
|
||||||
{
|
|
||||||
int gatherBiasPrecision = context.Config.GpuAccessor.QueryHostGatherBiasPrecision();
|
|
||||||
if (isGather && gatherBiasPrecision != 0)
|
|
||||||
{
|
|
||||||
// GPU requires texture gather to be slightly offset to match NVIDIA behaviour when point is exactly between two texels.
|
|
||||||
// Offset by the gather precision divided by 2 to correct for rounding.
|
|
||||||
var sizeType = pCount == 1 ? context.TypeS32() : context.TypeVector(context.TypeS32(), pCount);
|
|
||||||
var pVectorType = pCount == 1 ? context.TypeFP32() : context.TypeVector(context.TypeFP32(), pCount);
|
|
||||||
|
|
||||||
var bias = context.Constant(context.TypeFP32(), (float)(1 << (gatherBiasPrecision + 1)));
|
|
||||||
var biasVector = context.CompositeConstruct(pVectorType, Enumerable.Repeat(bias, pCount).ToArray());
|
|
||||||
|
|
||||||
var one = context.Constant(context.TypeFP32(), 1f);
|
|
||||||
var oneVector = context.CompositeConstruct(pVectorType, Enumerable.Repeat(one, pCount).ToArray());
|
|
||||||
|
|
||||||
var divisor = context.FMul(
|
|
||||||
pVectorType,
|
|
||||||
context.ConvertSToF(pVectorType, context.ImageQuerySize(sizeType, image)),
|
|
||||||
biasVector);
|
|
||||||
|
|
||||||
vector = context.FAdd(pVectorType, vector, context.FDiv(pVectorType, oneVector, divisor));
|
|
||||||
}
|
|
||||||
|
|
||||||
return vector;
|
|
||||||
}
|
|
||||||
|
|
||||||
SpvInstruction pCoords = AssemblePVector(pCount);
|
SpvInstruction pCoords = AssemblePVector(pCount);
|
||||||
pCoords = ScalingHelpers.ApplyScaling(context, texOp, pCoords, intCoords, isBindless, isIndexed, isArray, pCount);
|
|
||||||
|
|
||||||
SpvInstruction AssembleDerivativesVector(int count)
|
SpvInstruction AssembleDerivativesVector(int count)
|
||||||
{
|
{
|
||||||
@ -1638,8 +1599,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
image = context.Image(imageType, image);
|
image = context.Image(imageType, image);
|
||||||
}
|
}
|
||||||
|
|
||||||
pCoords = ApplyBias(pCoords, image);
|
|
||||||
|
|
||||||
var operands = operandsList.ToArray();
|
var operands = operandsList.ToArray();
|
||||||
|
|
||||||
SpvInstruction result;
|
SpvInstruction result;
|
||||||
@ -1755,11 +1714,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
result = context.CompositeExtract(context.TypeS32(), result, (SpvLiteralInteger)texOp.Index);
|
result = context.CompositeExtract(context.TypeS32(), result, (SpvLiteralInteger)texOp.Index);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (texOp.Index < 2 || (type & SamplerType.Mask) == SamplerType.Texture3D)
|
|
||||||
{
|
|
||||||
result = ScalingHelpers.ApplyUnscaling(context, texOp.WithType(type), result, isBindless, isIndexed);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new OperationResult(AggregateType.S32, result);
|
return new OperationResult(AggregateType.S32, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1873,13 +1827,13 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
AstOperation operation,
|
AstOperation operation,
|
||||||
Func<SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction, SpvInstruction> emitU)
|
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;
|
SpvInstruction elemPointer;
|
||||||
|
|
||||||
if (operation.StorageKind == StorageKind.StorageBuffer)
|
if (operation.StorageKind == StorageKind.StorageBuffer)
|
||||||
{
|
{
|
||||||
elemPointer = GetStorageElemPointer(context, operation);
|
elemPointer = GetStoragePointer(context, operation, out _);
|
||||||
}
|
}
|
||||||
else if (operation.StorageKind == StorageKind.SharedMemory)
|
else if (operation.StorageKind == StorageKind.SharedMemory)
|
||||||
{
|
{
|
||||||
@ -1899,14 +1853,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
private static OperationResult GenerateAtomicMemoryCas(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateAtomicMemoryCas(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
var value0 = context.GetU32(operation.GetSource(2));
|
var value0 = context.GetU32(operation.GetSource(operation.SourcesCount - 2));
|
||||||
var value1 = context.GetU32(operation.GetSource(3));
|
var value1 = context.GetU32(operation.GetSource(operation.SourcesCount - 1));
|
||||||
|
|
||||||
SpvInstruction elemPointer;
|
SpvInstruction elemPointer;
|
||||||
|
|
||||||
if (operation.StorageKind == StorageKind.StorageBuffer)
|
if (operation.StorageKind == StorageKind.StorageBuffer)
|
||||||
{
|
{
|
||||||
elemPointer = GetStorageElemPointer(context, operation);
|
elemPointer = GetStoragePointer(context, operation, out _);
|
||||||
}
|
}
|
||||||
else if (operation.StorageKind == StorageKind.SharedMemory)
|
else if (operation.StorageKind == StorageKind.SharedMemory)
|
||||||
{
|
{
|
||||||
@ -1925,17 +1879,33 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static OperationResult GenerateLoadOrStore(CodeGenContext context, AstOperation operation, bool isStore)
|
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;
|
StorageKind storageKind = operation.StorageKind;
|
||||||
|
|
||||||
StorageClass storageClass;
|
StorageClass storageClass;
|
||||||
SpvInstruction baseObj;
|
SpvInstruction baseObj;
|
||||||
AggregateType varType;
|
|
||||||
int srcIndex = 0;
|
int srcIndex = 0;
|
||||||
|
|
||||||
switch (storageKind)
|
switch (storageKind)
|
||||||
{
|
{
|
||||||
case StorageKind.ConstantBuffer:
|
case StorageKind.ConstantBuffer:
|
||||||
|
case StorageKind.StorageBuffer:
|
||||||
if (!(operation.GetSource(srcIndex++) is AstOperand bindingIndex) || bindingIndex.Type != OperandType.Constant)
|
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.");
|
throw new InvalidOperationException($"First input of {operation.Inst} with {storageKind} storage must be a constant operand.");
|
||||||
@ -1946,12 +1916,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
throw new InvalidOperationException($"Second input of {operation.Inst} with {storageKind} storage must be a constant operand.");
|
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];
|
StructureField field = buffer.Type.Fields[fieldIndex.Value];
|
||||||
|
|
||||||
storageClass = StorageClass.Uniform;
|
storageClass = StorageClass.Uniform;
|
||||||
varType = field.Type & AggregateType.ElementTypeMask;
|
varType = field.Type & AggregateType.ElementTypeMask;
|
||||||
baseObj = context.ConstantBuffers[bindingIndex.Value];
|
baseObj = storageKind == StorageKind.ConstantBuffer
|
||||||
|
? context.ConstantBuffers[bindingIndex.Value]
|
||||||
|
: context.StorageBuffers[bindingIndex.Value];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case StorageKind.Input:
|
case StorageKind.Input:
|
||||||
@ -2017,7 +1991,14 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
throw new InvalidOperationException($"Invalid storage kind {storageKind}.");
|
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 e0, e1, e2;
|
||||||
SpvInstruction pointer;
|
SpvInstruction pointer;
|
||||||
|
|
||||||
@ -2054,16 +2035,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isStore)
|
return pointer;
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SpvInstruction GetScalarInput(CodeGenContext context, IoVariable ioVariable)
|
private static SpvInstruction GetScalarInput(CodeGenContext context, IoVariable ioVariable)
|
||||||
@ -2092,25 +2064,6 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
GenerateStoreSmallInt(context, elemPointer, bitOffset, value, bitSize);
|
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(
|
private static void GenerateStoreSmallInt(
|
||||||
CodeGenContext context,
|
CodeGenContext context,
|
||||||
SpvInstruction elemPointer,
|
SpvInstruction elemPointer,
|
||||||
@ -2197,16 +2150,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(
|
private static OperationResult GenerateUnary(
|
||||||
CodeGenContext context,
|
CodeGenContext context,
|
||||||
AstOperation operation,
|
AstOperation operation,
|
||||||
@ -2269,7 +2212,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
{
|
{
|
||||||
var result = emitF(context.TypeFP64(), context.GetFP64(src1), context.GetFP64(src2));
|
var result = emitF(context.TypeFP64(), context.GetFP64(src1), context.GetFP64(src2));
|
||||||
|
|
||||||
if (!context.Config.GpuAccessor.QueryHostReducedPrecision())
|
if (!context.Config.GpuAccessor.QueryHostReducedPrecision() || operation.ForcePrecise)
|
||||||
{
|
{
|
||||||
context.Decorate(result, Decoration.NoContraction);
|
context.Decorate(result, Decoration.NoContraction);
|
||||||
}
|
}
|
||||||
@ -2280,7 +2223,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
{
|
{
|
||||||
var result = emitF(context.TypeFP32(), context.GetFP32(src1), context.GetFP32(src2));
|
var result = emitF(context.TypeFP32(), context.GetFP32(src1), context.GetFP32(src2));
|
||||||
|
|
||||||
if (!context.Config.GpuAccessor.QueryHostReducedPrecision())
|
if (!context.Config.GpuAccessor.QueryHostReducedPrecision() || operation.ForcePrecise)
|
||||||
{
|
{
|
||||||
context.Decorate(result, Decoration.NoContraction);
|
context.Decorate(result, Decoration.NoContraction);
|
||||||
}
|
}
|
||||||
@ -2340,7 +2283,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
{
|
{
|
||||||
var result = emitF(context.TypeFP64(), context.GetFP64(src1), context.GetFP64(src2), context.GetFP64(src3));
|
var result = emitF(context.TypeFP64(), context.GetFP64(src1), context.GetFP64(src2), context.GetFP64(src3));
|
||||||
|
|
||||||
if (!context.Config.GpuAccessor.QueryHostReducedPrecision())
|
if (!context.Config.GpuAccessor.QueryHostReducedPrecision() || operation.ForcePrecise)
|
||||||
{
|
{
|
||||||
context.Decorate(result, Decoration.NoContraction);
|
context.Decorate(result, Decoration.NoContraction);
|
||||||
}
|
}
|
||||||
@ -2351,7 +2294,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
{
|
{
|
||||||
var result = emitF(context.TypeFP32(), context.GetFP32(src1), context.GetFP32(src2), context.GetFP32(src3));
|
var result = emitF(context.TypeFP32(), context.GetFP32(src1), context.GetFP32(src2), context.GetFP32(src3));
|
||||||
|
|
||||||
if (!context.Config.GpuAccessor.QueryHostReducedPrecision())
|
if (!context.Config.GpuAccessor.QueryHostReducedPrecision() || operation.ForcePrecise)
|
||||||
{
|
{
|
||||||
context.Decorate(result, Decoration.NoContraction);
|
context.Decorate(result, Decoration.NoContraction);
|
||||||
}
|
}
|
||||||
|
@ -1,227 +0,0 @@
|
|||||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
|
||||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
|
||||||
using Ryujinx.Graphics.Shader.Translation;
|
|
||||||
using static Spv.Specification;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|
||||||
{
|
|
||||||
using SpvInstruction = Spv.Generator.Instruction;
|
|
||||||
|
|
||||||
static class ScalingHelpers
|
|
||||||
{
|
|
||||||
public static SpvInstruction ApplyScaling(
|
|
||||||
CodeGenContext context,
|
|
||||||
AstTextureOperation texOp,
|
|
||||||
SpvInstruction vector,
|
|
||||||
bool intCoords,
|
|
||||||
bool isBindless,
|
|
||||||
bool isIndexed,
|
|
||||||
bool isArray,
|
|
||||||
int pCount)
|
|
||||||
{
|
|
||||||
if (intCoords)
|
|
||||||
{
|
|
||||||
if (context.Config.Stage.SupportsRenderScale() &&
|
|
||||||
!isBindless &&
|
|
||||||
!isIndexed)
|
|
||||||
{
|
|
||||||
int index = texOp.Inst == Instruction.ImageLoad
|
|
||||||
? context.Config.GetTextureDescriptors().Length + context.Config.FindImageDescriptorIndex(texOp)
|
|
||||||
: context.Config.FindTextureDescriptorIndex(texOp);
|
|
||||||
|
|
||||||
if (pCount == 3 && isArray)
|
|
||||||
{
|
|
||||||
return ApplyScaling2DArray(context, vector, index);
|
|
||||||
}
|
|
||||||
else if (pCount == 2 && !isArray)
|
|
||||||
{
|
|
||||||
return ApplyScaling2D(context, vector, index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return vector;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SpvInstruction ApplyScaling2DArray(CodeGenContext context, SpvInstruction vector, int index)
|
|
||||||
{
|
|
||||||
// The array index is not scaled, just x and y.
|
|
||||||
var vectorXY = context.VectorShuffle(context.TypeVector(context.TypeS32(), 2), vector, vector, 0, 1);
|
|
||||||
var vectorZ = context.CompositeExtract(context.TypeS32(), vector, 2);
|
|
||||||
var vectorXYScaled = ApplyScaling2D(context, vectorXY, index);
|
|
||||||
var vectorScaled = context.CompositeConstruct(context.TypeVector(context.TypeS32(), 3), vectorXYScaled, vectorZ);
|
|
||||||
|
|
||||||
return vectorScaled;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static SpvInstruction ApplyScaling2D(CodeGenContext context, SpvInstruction vector, int index)
|
|
||||||
{
|
|
||||||
var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32());
|
|
||||||
var fieldIndex = context.Constant(context.TypeU32(), 4);
|
|
||||||
var scaleIndex = context.Constant(context.TypeU32(), index);
|
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.Vertex)
|
|
||||||
{
|
|
||||||
var scaleCountPointerType = context.TypePointer(StorageClass.Uniform, context.TypeS32());
|
|
||||||
var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.ConstantBuffers[0], context.Constant(context.TypeU32(), 3));
|
|
||||||
var scaleCount = context.Load(context.TypeS32(), scaleCountElemPointer);
|
|
||||||
|
|
||||||
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, scaleCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, context.Constant(context.TypeU32(), 1));
|
|
||||||
|
|
||||||
var scaleElemPointer = context.AccessChain(pointerType, context.ConstantBuffers[0], fieldIndex, scaleIndex);
|
|
||||||
var scale = context.Load(context.TypeFP32(), scaleElemPointer);
|
|
||||||
|
|
||||||
var ivector2Type = context.TypeVector(context.TypeS32(), 2);
|
|
||||||
var localVector = context.CoordTemp;
|
|
||||||
|
|
||||||
var passthrough = context.FOrdEqual(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 1f));
|
|
||||||
|
|
||||||
var mergeLabel = context.Label();
|
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.Fragment)
|
|
||||||
{
|
|
||||||
var scaledInterpolatedLabel = context.Label();
|
|
||||||
var scaledNoInterpolationLabel = context.Label();
|
|
||||||
|
|
||||||
var needsInterpolation = context.FOrdLessThan(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 0f));
|
|
||||||
|
|
||||||
context.SelectionMerge(mergeLabel, SelectionControlMask.MaskNone);
|
|
||||||
context.BranchConditional(needsInterpolation, scaledInterpolatedLabel, scaledNoInterpolationLabel);
|
|
||||||
|
|
||||||
// scale < 0.0
|
|
||||||
context.AddLabel(scaledInterpolatedLabel);
|
|
||||||
|
|
||||||
ApplyScalingInterpolated(context, localVector, vector, scale);
|
|
||||||
context.Branch(mergeLabel);
|
|
||||||
|
|
||||||
// scale >= 0.0
|
|
||||||
context.AddLabel(scaledNoInterpolationLabel);
|
|
||||||
|
|
||||||
ApplyScalingNoInterpolation(context, localVector, vector, scale);
|
|
||||||
context.Branch(mergeLabel);
|
|
||||||
|
|
||||||
context.AddLabel(mergeLabel);
|
|
||||||
|
|
||||||
var passthroughLabel = context.Label();
|
|
||||||
var finalMergeLabel = context.Label();
|
|
||||||
|
|
||||||
context.SelectionMerge(finalMergeLabel, SelectionControlMask.MaskNone);
|
|
||||||
context.BranchConditional(passthrough, passthroughLabel, finalMergeLabel);
|
|
||||||
|
|
||||||
context.AddLabel(passthroughLabel);
|
|
||||||
|
|
||||||
context.Store(localVector, vector);
|
|
||||||
context.Branch(finalMergeLabel);
|
|
||||||
|
|
||||||
context.AddLabel(finalMergeLabel);
|
|
||||||
|
|
||||||
return context.Load(ivector2Type, localVector);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var passthroughLabel = context.Label();
|
|
||||||
var scaledLabel = context.Label();
|
|
||||||
|
|
||||||
context.SelectionMerge(mergeLabel, SelectionControlMask.MaskNone);
|
|
||||||
context.BranchConditional(passthrough, passthroughLabel, scaledLabel);
|
|
||||||
|
|
||||||
// scale == 1.0
|
|
||||||
context.AddLabel(passthroughLabel);
|
|
||||||
|
|
||||||
context.Store(localVector, vector);
|
|
||||||
context.Branch(mergeLabel);
|
|
||||||
|
|
||||||
// scale != 1.0
|
|
||||||
context.AddLabel(scaledLabel);
|
|
||||||
|
|
||||||
ApplyScalingNoInterpolation(context, localVector, vector, scale);
|
|
||||||
context.Branch(mergeLabel);
|
|
||||||
|
|
||||||
context.AddLabel(mergeLabel);
|
|
||||||
|
|
||||||
return context.Load(ivector2Type, localVector);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ApplyScalingInterpolated(CodeGenContext context, SpvInstruction output, SpvInstruction vector, SpvInstruction scale)
|
|
||||||
{
|
|
||||||
var vector2Type = context.TypeVector(context.TypeFP32(), 2);
|
|
||||||
|
|
||||||
var scaleNegated = context.FNegate(context.TypeFP32(), scale);
|
|
||||||
var scaleVector = context.CompositeConstruct(vector2Type, scaleNegated, scaleNegated);
|
|
||||||
|
|
||||||
var vectorFloat = context.ConvertSToF(vector2Type, vector);
|
|
||||||
var vectorScaled = context.VectorTimesScalar(vector2Type, vectorFloat, scaleNegated);
|
|
||||||
|
|
||||||
var fragCoordPointer = context.Inputs[new IoDefinition(StorageKind.Input, IoVariable.FragmentCoord)];
|
|
||||||
var fragCoord = context.Load(context.TypeVector(context.TypeFP32(), 4), fragCoordPointer);
|
|
||||||
var fragCoordXY = context.VectorShuffle(vector2Type, fragCoord, fragCoord, 0, 1);
|
|
||||||
|
|
||||||
var scaleMod = context.FMod(vector2Type, fragCoordXY, scaleVector);
|
|
||||||
var vectorInterpolated = context.FAdd(vector2Type, vectorScaled, scaleMod);
|
|
||||||
|
|
||||||
context.Store(output, context.ConvertFToS(context.TypeVector(context.TypeS32(), 2), vectorInterpolated));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ApplyScalingNoInterpolation(CodeGenContext context, SpvInstruction output, SpvInstruction vector, SpvInstruction scale)
|
|
||||||
{
|
|
||||||
if (context.Config.Stage == ShaderStage.Vertex)
|
|
||||||
{
|
|
||||||
scale = context.GlslFAbs(context.TypeFP32(), scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
var vector2Type = context.TypeVector(context.TypeFP32(), 2);
|
|
||||||
|
|
||||||
var vectorFloat = context.ConvertSToF(vector2Type, vector);
|
|
||||||
var vectorScaled = context.VectorTimesScalar(vector2Type, vectorFloat, scale);
|
|
||||||
|
|
||||||
context.Store(output, context.ConvertFToS(context.TypeVector(context.TypeS32(), 2), vectorScaled));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SpvInstruction ApplyUnscaling(
|
|
||||||
CodeGenContext context,
|
|
||||||
AstTextureOperation texOp,
|
|
||||||
SpvInstruction size,
|
|
||||||
bool isBindless,
|
|
||||||
bool isIndexed)
|
|
||||||
{
|
|
||||||
if (context.Config.Stage.SupportsRenderScale() &&
|
|
||||||
!isBindless &&
|
|
||||||
!isIndexed)
|
|
||||||
{
|
|
||||||
int index = context.Config.FindTextureDescriptorIndex(texOp);
|
|
||||||
|
|
||||||
var pointerType = context.TypePointer(StorageClass.Uniform, context.TypeFP32());
|
|
||||||
var fieldIndex = context.Constant(context.TypeU32(), 4);
|
|
||||||
var scaleIndex = context.Constant(context.TypeU32(), index);
|
|
||||||
|
|
||||||
if (context.Config.Stage == ShaderStage.Vertex)
|
|
||||||
{
|
|
||||||
var scaleCountPointerType = context.TypePointer(StorageClass.Uniform, context.TypeS32());
|
|
||||||
var scaleCountElemPointer = context.AccessChain(scaleCountPointerType, context.ConstantBuffers[0], context.Constant(context.TypeU32(), 3));
|
|
||||||
var scaleCount = context.Load(context.TypeS32(), scaleCountElemPointer);
|
|
||||||
|
|
||||||
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, scaleCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
scaleIndex = context.IAdd(context.TypeU32(), scaleIndex, context.Constant(context.TypeU32(), 1));
|
|
||||||
|
|
||||||
var scaleElemPointer = context.AccessChain(pointerType, context.ConstantBuffers[0], fieldIndex, scaleIndex);
|
|
||||||
var scale = context.GlslFAbs(context.TypeFP32(), context.Load(context.TypeFP32(), scaleElemPointer));
|
|
||||||
|
|
||||||
var passthrough = context.FOrdEqual(context.TypeBool(), scale, context.Constant(context.TypeFP32(), 1f));
|
|
||||||
|
|
||||||
var sizeFloat = context.ConvertSToF(context.TypeFP32(), size);
|
|
||||||
var sizeUnscaled = context.FDiv(context.TypeFP32(), sizeFloat, scale);
|
|
||||||
var sizeUnscaledInt = context.ConvertFToS(context.TypeS32(), sizeUnscaled);
|
|
||||||
|
|
||||||
return context.Select(context.TypeS32(), passthrough, size, sizeUnscaledInt);
|
|
||||||
}
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -67,6 +67,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
public readonly FuncBinaryInstruction GlslSMax;
|
public readonly FuncBinaryInstruction GlslSMax;
|
||||||
public readonly FuncBinaryInstruction GlslFMin;
|
public readonly FuncBinaryInstruction GlslFMin;
|
||||||
public readonly FuncBinaryInstruction GlslSMin;
|
public readonly FuncBinaryInstruction GlslSMin;
|
||||||
|
public readonly FuncBinaryInstruction FMod;
|
||||||
public readonly FuncBinaryInstruction FMul;
|
public readonly FuncBinaryInstruction FMul;
|
||||||
public readonly FuncBinaryInstruction IMul;
|
public readonly FuncBinaryInstruction IMul;
|
||||||
public readonly FuncBinaryInstruction FSub;
|
public readonly FuncBinaryInstruction FSub;
|
||||||
@ -174,6 +175,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
GlslSMax = context.GlslSMax;
|
GlslSMax = context.GlslSMax;
|
||||||
GlslFMin = context.GlslFMin;
|
GlslFMin = context.GlslFMin;
|
||||||
GlslSMin = context.GlslSMin;
|
GlslSMin = context.GlslSMin;
|
||||||
|
FMod = context.FMod;
|
||||||
FMul = context.FMul;
|
FMul = context.FMul;
|
||||||
IMul = context.IMul;
|
IMul = context.IMul;
|
||||||
FSub = context.FSub;
|
FSub = context.FSub;
|
||||||
|
@ -144,12 +144,11 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
private static void Generate(CodeGenContext context, StructuredProgramInfo info, int funcIndex)
|
private static void Generate(CodeGenContext context, StructuredProgramInfo info, int funcIndex)
|
||||||
{
|
{
|
||||||
var function = info.Functions[funcIndex];
|
(var function, var spvFunc) = context.GetFunction(funcIndex);
|
||||||
|
|
||||||
(_, var spvFunc) = context.GetFunction(funcIndex);
|
|
||||||
|
|
||||||
|
context.CurrentFunction = function;
|
||||||
context.AddFunction(spvFunc);
|
context.AddFunction(spvFunc);
|
||||||
context.StartFunction();
|
context.StartFunction(isMainFunction: funcIndex == 0);
|
||||||
|
|
||||||
Declarations.DeclareParameters(context, function);
|
Declarations.DeclareParameters(context, function);
|
||||||
|
|
||||||
|
@ -10,7 +10,5 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
public const int NvnBaseVertexByteOffset = 0x640;
|
public const int NvnBaseVertexByteOffset = 0x640;
|
||||||
public const int NvnBaseInstanceByteOffset = 0x644;
|
public const int NvnBaseInstanceByteOffset = 0x644;
|
||||||
public const int NvnDrawIndexByteOffset = 0x648;
|
public const int NvnDrawIndexByteOffset = 0x648;
|
||||||
|
|
||||||
public const int StorageAlignment = 16;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -79,7 +79,7 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
/// <returns>Binding number</returns>
|
/// <returns>Binding number</returns>
|
||||||
int QueryBindingConstantBuffer(int index)
|
int QueryBindingConstantBuffer(int index)
|
||||||
{
|
{
|
||||||
return index;
|
return index + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -331,6 +331,24 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queries host GPU shader support for barrier instructions on divergent control flow paths.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if the GPU supports barriers on divergent control flow paths, false otherwise</returns>
|
||||||
|
bool QueryHostSupportsShaderBarrierDivergence()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queries host GPU support for 64-bit floating point (double precision) operations on the shader.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if the GPU and driver supports double operations, false otherwise</returns>
|
||||||
|
bool QueryHostSupportsShaderFloat64()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queries host GPU support for signed normalized buffer texture formats.
|
/// Queries host GPU support for signed normalized buffer texture formats.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -367,6 +385,15 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
return true;
|
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>
|
/// <summary>
|
||||||
/// Queries the point size from the GPU state, used when it is not explicitly set on the shader.
|
/// Queries the point size from the GPU state, used when it is not explicitly set on the shader.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -187,27 +187,6 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
context.Config.GpuAccessor.Log("Shader instruction Longjmp is not implemented.");
|
context.Config.GpuAccessor.Log("Shader instruction Longjmp is not implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void P2rR(EmitterContext context)
|
|
||||||
{
|
|
||||||
InstP2rR op = context.GetOp<InstP2rR>();
|
|
||||||
|
|
||||||
context.Config.GpuAccessor.Log("Shader instruction P2rR is not implemented.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void P2rI(EmitterContext context)
|
|
||||||
{
|
|
||||||
InstP2rI op = context.GetOp<InstP2rI>();
|
|
||||||
|
|
||||||
context.Config.GpuAccessor.Log("Shader instruction P2rI is not implemented.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void P2rC(EmitterContext context)
|
|
||||||
{
|
|
||||||
InstP2rC op = context.GetOp<InstP2rC>();
|
|
||||||
|
|
||||||
context.Config.GpuAccessor.Log("Shader instruction P2rC is not implemented.");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Pexit(EmitterContext context)
|
public static void Pexit(EmitterContext context)
|
||||||
{
|
{
|
||||||
InstPexit op = context.GetOp<InstPexit>();
|
InstPexit op = context.GetOp<InstPexit>();
|
||||||
|
@ -164,6 +164,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
|
|
||||||
if (op.Ccc == Ccc.T)
|
if (op.Ccc == Ccc.T)
|
||||||
{
|
{
|
||||||
|
context.PrepareForReturn();
|
||||||
context.Return();
|
context.Return();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -175,6 +176,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
{
|
{
|
||||||
Operand lblSkip = Label();
|
Operand lblSkip = Label();
|
||||||
context.BranchIfFalse(lblSkip, cond);
|
context.BranchIfFalse(lblSkip, cond);
|
||||||
|
context.PrepareForReturn();
|
||||||
context.Return();
|
context.Return();
|
||||||
context.MarkLabel(lblSkip);
|
context.MarkLabel(lblSkip);
|
||||||
}
|
}
|
||||||
|
@ -336,13 +336,12 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
int offset,
|
int offset,
|
||||||
bool extended)
|
bool extended)
|
||||||
{
|
{
|
||||||
bool isSmallInt = size < LsSize.B32;
|
|
||||||
|
|
||||||
int count = GetVectorCount(size);
|
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++)
|
for (int index = 0; index < count; index++)
|
||||||
{
|
{
|
||||||
@ -353,12 +352,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Operand value = context.LoadGlobal(context.IAdd(addrLow, Const(index * 4)), addrHigh);
|
Operand value = context.Load(storageKind, context.IAdd(srcA, Const(offset + index * 4)), addrHigh);
|
||||||
|
|
||||||
if (isSmallInt)
|
|
||||||
{
|
|
||||||
value = ExtractSmallInt(context, size, bitOffset, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Copy(Register(dest), value);
|
context.Copy(Register(dest), value);
|
||||||
}
|
}
|
||||||
@ -445,10 +439,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
}
|
}
|
||||||
|
|
||||||
int count = GetVectorCount((LsSize)size);
|
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++)
|
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 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.Store(storageKind, addrLowOffset, addrHigh, value);
|
||||||
{
|
|
||||||
context.StoreGlobal8(addrLowOffset, addrHigh, value);
|
|
||||||
}
|
|
||||||
else if (size == LsSize2.U16 || size == LsSize2.S16)
|
|
||||||
{
|
|
||||||
context.StoreGlobal16(addrLowOffset, addrHigh, value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
context.StoreGlobal(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)
|
private static int GetVectorCount(LsSize size)
|
||||||
{
|
{
|
||||||
switch (size)
|
switch (size)
|
||||||
|
@ -209,21 +209,15 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
return context.ICompareNotEqual(context.BitwiseAnd(value, Const(1 << bit)), Const(0));
|
return context.ICompareNotEqual(context.BitwiseAnd(value, Const(1 << bit)), Const(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ccpr)
|
int count = ccpr ? RegisterConsts.FlagsCount : RegisterConsts.PredsCount;
|
||||||
|
RegisterType type = ccpr ? RegisterType.Flag : RegisterType.Predicate;
|
||||||
|
int shift = (int)byteSel * 8;
|
||||||
|
|
||||||
|
for (int bit = 0; bit < count; bit++)
|
||||||
{
|
{
|
||||||
// TODO: Support Register to condition code flags copy.
|
Operand flag = Register(bit, type);
|
||||||
context.Config.GpuAccessor.Log("R2P.CC not implemented.");
|
Operand res = context.ConditionalSelect(Test(mask, bit), Test(value, bit + shift), flag);
|
||||||
}
|
context.Copy(flag, res);
|
||||||
else
|
|
||||||
{
|
|
||||||
int shift = (int)byteSel * 8;
|
|
||||||
|
|
||||||
for (int bit = 0; bit < RegisterConsts.PredsCount; bit++)
|
|
||||||
{
|
|
||||||
Operand pred = Register(bit, RegisterType.Predicate);
|
|
||||||
Operand res = context.ConditionalSelect(Test(mask, bit), Test(value, bit + shift), pred);
|
|
||||||
context.Copy(pred, res);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using Ryujinx.Graphics.Shader.Decoders;
|
using Ryujinx.Graphics.Shader.Decoders;
|
||||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||||
using Ryujinx.Graphics.Shader.Translation;
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
|
|
||||||
using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper;
|
using static Ryujinx.Graphics.Shader.Instructions.InstEmitAluHelper;
|
||||||
using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
|
using static Ryujinx.Graphics.Shader.Instructions.InstEmitHelper;
|
||||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||||
@ -50,5 +49,68 @@ namespace Ryujinx.Graphics.Shader.Instructions
|
|||||||
context.Copy(Register(op.DestPred, RegisterType.Predicate), p0Res);
|
context.Copy(Register(op.DestPred, RegisterType.Predicate), p0Res);
|
||||||
context.Copy(Register(op.DestPredInv, RegisterType.Predicate), p1Res);
|
context.Copy(Register(op.DestPredInv, RegisterType.Predicate), p1Res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void P2rC(EmitterContext context)
|
||||||
|
{
|
||||||
|
InstP2rC op = context.GetOp<InstP2rC>();
|
||||||
|
|
||||||
|
Operand srcA = GetSrcReg(context, op.SrcA);
|
||||||
|
Operand dest = GetSrcReg(context, op.Dest);
|
||||||
|
Operand mask = GetSrcCbuf(context, op.CbufSlot, op.CbufOffset);
|
||||||
|
|
||||||
|
EmitP2r(context, srcA, dest, mask, op.ByteSel, op.Ccpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void P2rI(EmitterContext context)
|
||||||
|
{
|
||||||
|
InstP2rI op = context.GetOp<InstP2rI>();
|
||||||
|
|
||||||
|
Operand srcA = GetSrcReg(context, op.SrcA);
|
||||||
|
Operand dest = GetSrcReg(context, op.Dest);
|
||||||
|
Operand mask = GetSrcImm(context, op.Imm20);
|
||||||
|
|
||||||
|
EmitP2r(context, srcA, dest, mask, op.ByteSel, op.Ccpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void P2rR(EmitterContext context)
|
||||||
|
{
|
||||||
|
InstP2rR op = context.GetOp<InstP2rR>();
|
||||||
|
|
||||||
|
Operand srcA = GetSrcReg(context, op.SrcA);
|
||||||
|
Operand dest = GetSrcReg(context, op.Dest);
|
||||||
|
Operand mask = GetSrcReg(context, op.SrcB);
|
||||||
|
|
||||||
|
EmitP2r(context, srcA, dest, mask, op.ByteSel, op.Ccpr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void EmitP2r(
|
||||||
|
EmitterContext context,
|
||||||
|
Operand srcA,
|
||||||
|
Operand dest,
|
||||||
|
Operand mask,
|
||||||
|
ByteSel byteSel,
|
||||||
|
bool ccpr)
|
||||||
|
{
|
||||||
|
int count = ccpr ? RegisterConsts.FlagsCount : RegisterConsts.PredsCount;
|
||||||
|
int shift = (int)byteSel * 8;
|
||||||
|
mask = context.BitwiseAnd(mask, Const(0xff));
|
||||||
|
|
||||||
|
Operand insert = Const(0);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
Operand condition = ccpr
|
||||||
|
? Register(i, RegisterType.Flag)
|
||||||
|
: Register(i, RegisterType.Predicate);
|
||||||
|
|
||||||
|
Operand bit = context.ConditionalSelect(condition, Const(1 << (i + shift)), Const(0));
|
||||||
|
insert = context.BitwiseOr(insert, bit);
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand maskShifted = context.ShiftLeft(mask, Const(shift));
|
||||||
|
Operand masked = context.BitwiseAnd(srcA, context.BitwiseNot(maskShifted));
|
||||||
|
Operand res = context.BitwiseOr(masked, context.BitwiseAnd(insert, maskShifted));
|
||||||
|
|
||||||
|
context.Copy(dest, res);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -79,10 +79,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||||||
ImageAtomic,
|
ImageAtomic,
|
||||||
IsNan,
|
IsNan,
|
||||||
Load,
|
Load,
|
||||||
LoadGlobal,
|
|
||||||
LoadLocal,
|
LoadLocal,
|
||||||
LoadShared,
|
LoadShared,
|
||||||
LoadStorage,
|
|
||||||
Lod,
|
Lod,
|
||||||
LogarithmB2,
|
LogarithmB2,
|
||||||
LogicalAnd,
|
LogicalAnd,
|
||||||
@ -97,6 +95,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||||||
MemoryBarrier,
|
MemoryBarrier,
|
||||||
Minimum,
|
Minimum,
|
||||||
MinimumU32,
|
MinimumU32,
|
||||||
|
Modulo,
|
||||||
Multiply,
|
Multiply,
|
||||||
MultiplyHighS32,
|
MultiplyHighS32,
|
||||||
MultiplyHighU32,
|
MultiplyHighU32,
|
||||||
@ -116,16 +115,10 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||||||
Sine,
|
Sine,
|
||||||
SquareRoot,
|
SquareRoot,
|
||||||
Store,
|
Store,
|
||||||
StoreGlobal,
|
|
||||||
StoreGlobal16,
|
|
||||||
StoreGlobal8,
|
|
||||||
StoreLocal,
|
StoreLocal,
|
||||||
StoreShared,
|
StoreShared,
|
||||||
StoreShared16,
|
StoreShared16,
|
||||||
StoreShared8,
|
StoreShared8,
|
||||||
StoreStorage,
|
|
||||||
StoreStorage16,
|
|
||||||
StoreStorage8,
|
|
||||||
Subtract,
|
Subtract,
|
||||||
SwizzleAdd,
|
SwizzleAdd,
|
||||||
TextureSample,
|
TextureSample,
|
||||||
|
@ -8,6 +8,8 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||||||
public Instruction Inst { get; private set; }
|
public Instruction Inst { get; private set; }
|
||||||
public StorageKind StorageKind { get; }
|
public StorageKind StorageKind { get; }
|
||||||
|
|
||||||
|
public bool ForcePrecise { get; set; }
|
||||||
|
|
||||||
private Operand[] _dests;
|
private Operand[] _dests;
|
||||||
|
|
||||||
public Operand Dest
|
public Operand Dest
|
||||||
@ -253,5 +255,35 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||||||
|
|
||||||
_sources = new Operand[] { source };
|
_sources = new Operand[] { source };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void TurnDoubleIntoFloat()
|
||||||
|
{
|
||||||
|
if ((Inst & ~Instruction.Mask) == Instruction.FP64)
|
||||||
|
{
|
||||||
|
Inst = (Inst & Instruction.Mask) | Instruction.FP32;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch (Inst)
|
||||||
|
{
|
||||||
|
case Instruction.ConvertFP32ToFP64:
|
||||||
|
case Instruction.ConvertFP64ToFP32:
|
||||||
|
Inst = Instruction.Copy;
|
||||||
|
break;
|
||||||
|
case Instruction.ConvertFP64ToS32:
|
||||||
|
Inst = Instruction.ConvertFP32ToS32;
|
||||||
|
break;
|
||||||
|
case Instruction.ConvertFP64ToU32:
|
||||||
|
Inst = Instruction.ConvertFP32ToU32;
|
||||||
|
break;
|
||||||
|
case Instruction.ConvertS32ToFP64:
|
||||||
|
Inst = Instruction.ConvertS32ToFP32;
|
||||||
|
break;
|
||||||
|
case Instruction.ConvertU32ToFP64:
|
||||||
|
Inst = Instruction.ConvertU32ToFP32;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,7 +11,12 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||||||
StorageBuffer,
|
StorageBuffer,
|
||||||
LocalMemory,
|
LocalMemory,
|
||||||
SharedMemory,
|
SharedMemory,
|
||||||
GlobalMemory
|
GlobalMemory,
|
||||||
|
// TODO: Remove those and store type as a field on the Operation class itself.
|
||||||
|
GlobalMemoryS8,
|
||||||
|
GlobalMemoryS16,
|
||||||
|
GlobalMemoryU8,
|
||||||
|
GlobalMemoryU16
|
||||||
}
|
}
|
||||||
|
|
||||||
static class StorageKindExtensions
|
static class StorageKindExtensions
|
||||||
|
@ -4,10 +4,6 @@
|
|||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<None Remove="CodeGen\Glsl\HelperFunctions\TexelFetchScale_vp.glsl" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
<ProjectReference Include="..\Spv.Generator\Spv.Generator.csproj" />
|
<ProjectReference Include="..\Spv.Generator\Spv.Generator.csproj" />
|
||||||
@ -15,7 +11,6 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\AtomicMinMaxS32Shared.glsl" />
|
<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\MultiplyHighS32.glsl" />
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighU32.glsl" />
|
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\MultiplyHighU32.glsl" />
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\Shuffle.glsl" />
|
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\Shuffle.glsl" />
|
||||||
@ -23,11 +18,7 @@
|
|||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleUp.glsl" />
|
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleUp.glsl" />
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleXor.glsl" />
|
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\ShuffleXor.glsl" />
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreSharedSmallInt.glsl" />
|
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreSharedSmallInt.glsl" />
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\StoreStorageSmallInt.glsl" />
|
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\SwizzleAdd.glsl" />
|
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\SwizzleAdd.glsl" />
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\TexelFetchScale_vp.glsl" />
|
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\TexelFetchScale_fp.glsl" />
|
|
||||||
<EmbeddedResource Include="CodeGen\Glsl\HelperFunctions\TexelFetchScale_cp.glsl" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -10,6 +10,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
{
|
{
|
||||||
public Instruction Inst { get; }
|
public Instruction Inst { get; }
|
||||||
public StorageKind StorageKind { get; }
|
public StorageKind StorageKind { get; }
|
||||||
|
public bool ForcePrecise { get; }
|
||||||
|
|
||||||
public int Index { get; }
|
public int Index { get; }
|
||||||
|
|
||||||
@ -17,10 +18,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
|
|
||||||
public int SourcesCount => _sources.Length;
|
public int SourcesCount => _sources.Length;
|
||||||
|
|
||||||
public AstOperation(Instruction inst, StorageKind storageKind, IAstNode[] sources, int sourcesCount)
|
public AstOperation(Instruction inst, StorageKind storageKind, bool forcePrecise, IAstNode[] sources, int sourcesCount)
|
||||||
{
|
{
|
||||||
Inst = inst;
|
Inst = inst;
|
||||||
StorageKind = storageKind;
|
StorageKind = storageKind;
|
||||||
|
ForcePrecise = forcePrecise;
|
||||||
_sources = sources;
|
_sources = sources;
|
||||||
|
|
||||||
for (int index = 0; index < sources.Length; index++)
|
for (int index = 0; index < sources.Length; index++)
|
||||||
@ -38,12 +40,18 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
Index = 0;
|
Index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AstOperation(Instruction inst, StorageKind storageKind, int index, IAstNode[] sources, int sourcesCount) : this(inst, storageKind, sources, sourcesCount)
|
public AstOperation(
|
||||||
|
Instruction inst,
|
||||||
|
StorageKind storageKind,
|
||||||
|
bool forcePrecise,
|
||||||
|
int index,
|
||||||
|
IAstNode[] sources,
|
||||||
|
int sourcesCount) : this(inst, storageKind, forcePrecise, sources, sourcesCount)
|
||||||
{
|
{
|
||||||
Index = index;
|
Index = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AstOperation(Instruction inst, params IAstNode[] sources) : this(inst, StorageKind.None, sources, sources.Length)
|
public AstOperation(Instruction inst, params IAstNode[] sources) : this(inst, StorageKind.None, false, sources, sources.Length)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user