Compare commits
42 Commits
Author | SHA1 | Date | |
---|---|---|---|
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 |
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" />
|
||||||
@ -32,7 +32,7 @@
|
|||||||
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.7" />
|
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.7" />
|
||||||
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
<PackageVersion Include="Ryujinx.Audio.OpenAL.Dependencies" Version="1.21.0.1" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
|
<PackageVersion Include="Ryujinx.Graphics.Nvdec.Dependencies" Version="5.0.1-build13" />
|
||||||
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.3" />
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||||
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
|
<PackageVersion Include="Ryujinx.GtkSharp" Version="3.24.24.59-ryujinx" />
|
||||||
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.26.3-build25" />
|
<PackageVersion Include="Ryujinx.SDL2-CS" Version="2.26.3-build25" />
|
||||||
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
<PackageVersion Include="shaderc.net" Version="0.1.0" />
|
||||||
|
@ -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"
|
||||||
|
@ -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",
|
||||||
@ -282,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",
|
||||||
@ -620,7 +628,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:",
|
||||||
@ -632,10 +640,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."
|
||||||
}
|
}
|
@ -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>
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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)
|
||||||
|
@ -1529,6 +1529,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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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"
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
@ -39,6 +39,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
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;
|
||||||
@ -85,6 +86,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
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,
|
||||||
@ -127,6 +129,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
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;
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -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 = 5027;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,6 +149,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>
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,6 +163,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
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,
|
||||||
|
@ -239,33 +239,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)
|
||||||
|
@ -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);
|
|
||||||
}
|
|
@ -101,6 +101,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);
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
@ -584,53 +560,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 +680,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,14 +697,6 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>();
|
||||||
|
@ -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;
|
||||||
|
|
||||||
@ -114,6 +113,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);
|
||||||
@ -744,8 +744,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);
|
||||||
@ -1040,6 +1038,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 +1104,15 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
|
|||||||
|
|
||||||
private static OperationResult GenerateReturn(CodeGenContext context, AstOperation operation)
|
private static OperationResult GenerateReturn(CodeGenContext context, AstOperation operation)
|
||||||
{
|
{
|
||||||
context.Return();
|
if (operation.SourcesCount != 0)
|
||||||
|
{
|
||||||
|
context.ReturnValue(context.Get(context.CurrentFunction.ReturnType, operation.GetSource(0)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.Return();
|
||||||
|
}
|
||||||
|
|
||||||
return OperationResult.Invalid;
|
return OperationResult.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1439,35 +1450,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 +1621,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 +1736,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2269,7 +2245,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 +2256,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 +2316,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 +2327,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,10 +144,9 @@ 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();
|
||||||
|
|
||||||
|
@ -367,6 +367,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>();
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -97,6 +97,7 @@ namespace Ryujinx.Graphics.Shader.IntermediateRepresentation
|
|||||||
MemoryBarrier,
|
MemoryBarrier,
|
||||||
Minimum,
|
Minimum,
|
||||||
MinimumU32,
|
MinimumU32,
|
||||||
|
Modulo,
|
||||||
Multiply,
|
Multiply,
|
||||||
MultiplyHighS32,
|
MultiplyHighS32,
|
||||||
MultiplyHighU32,
|
MultiplyHighU32,
|
||||||
|
@ -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
|
||||||
|
@ -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" />
|
||||||
@ -25,9 +21,6 @@
|
|||||||
<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\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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
int cbufSlot,
|
int cbufSlot,
|
||||||
int handle,
|
int handle,
|
||||||
int index,
|
int index,
|
||||||
params IAstNode[] sources) : base(inst, StorageKind.None, index, sources, sources.Length)
|
params IAstNode[] sources) : base(inst, StorageKind.None, false, index, sources, sources.Length)
|
||||||
{
|
{
|
||||||
Type = type;
|
Type = type;
|
||||||
Format = format;
|
Format = format;
|
||||||
@ -27,10 +27,5 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
CbufSlot = cbufSlot;
|
CbufSlot = cbufSlot;
|
||||||
Handle = handle;
|
Handle = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AstTextureOperation WithType(SamplerType type)
|
|
||||||
{
|
|
||||||
return new AstTextureOperation(Inst, type, Format, Flags, CbufSlot, Handle, Index);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -104,6 +104,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
Add(Instruction.MaximumU32, AggregateType.U32, AggregateType.U32, AggregateType.U32);
|
Add(Instruction.MaximumU32, AggregateType.U32, AggregateType.U32, AggregateType.U32);
|
||||||
Add(Instruction.Minimum, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
|
Add(Instruction.Minimum, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
|
||||||
Add(Instruction.MinimumU32, AggregateType.U32, AggregateType.U32, AggregateType.U32);
|
Add(Instruction.MinimumU32, AggregateType.U32, AggregateType.U32, AggregateType.U32);
|
||||||
|
Add(Instruction.Modulo, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
|
||||||
Add(Instruction.Multiply, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
|
Add(Instruction.Multiply, AggregateType.Scalar, AggregateType.Scalar, AggregateType.Scalar);
|
||||||
Add(Instruction.MultiplyHighS32, AggregateType.S32, AggregateType.S32, AggregateType.S32);
|
Add(Instruction.MultiplyHighS32, AggregateType.S32, AggregateType.S32, AggregateType.S32);
|
||||||
Add(Instruction.MultiplyHighU32, AggregateType.U32, AggregateType.U32, AggregateType.U32);
|
Add(Instruction.MultiplyHighU32, AggregateType.U32, AggregateType.U32, AggregateType.U32);
|
||||||
|
@ -8,11 +8,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
{
|
{
|
||||||
static class StructuredProgram
|
static class StructuredProgram
|
||||||
{
|
{
|
||||||
public static StructuredProgramInfo MakeStructuredProgram(Function[] functions, ShaderConfig config)
|
public static StructuredProgramInfo MakeStructuredProgram(IReadOnlyList<Function> functions, ShaderConfig config)
|
||||||
{
|
{
|
||||||
StructuredProgramContext context = new StructuredProgramContext(config);
|
StructuredProgramContext context = new StructuredProgramContext(config);
|
||||||
|
|
||||||
for (int funcIndex = 0; funcIndex < functions.Length; funcIndex++)
|
for (int funcIndex = 0; funcIndex < functions.Count; funcIndex++)
|
||||||
{
|
{
|
||||||
Function function = functions[funcIndex];
|
Function function = functions[funcIndex];
|
||||||
|
|
||||||
@ -156,7 +156,13 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
source = new AstOperation(inst, operation.StorageKind, operation.Index, sources, operation.SourcesCount);
|
source = new AstOperation(
|
||||||
|
inst,
|
||||||
|
operation.StorageKind,
|
||||||
|
operation.ForcePrecise,
|
||||||
|
operation.Index,
|
||||||
|
sources,
|
||||||
|
operation.SourcesCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
AggregateType destElemType = destType;
|
AggregateType destElemType = destType;
|
||||||
@ -179,7 +185,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
|
|
||||||
dest.VarType = destElemType;
|
dest.VarType = destElemType;
|
||||||
|
|
||||||
context.AddNode(new AstAssignment(dest, new AstOperation(Instruction.VectorExtract, StorageKind.None, new[] { destVec, index }, 2)));
|
context.AddNode(new AstAssignment(dest, new AstOperation(Instruction.VectorExtract, StorageKind.None, false, new[] { destVec, index }, 2)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (operation.Dest != null)
|
else if (operation.Dest != null)
|
||||||
@ -227,7 +233,13 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
}
|
}
|
||||||
else if (!isCopy)
|
else if (!isCopy)
|
||||||
{
|
{
|
||||||
source = new AstOperation(inst, operation.StorageKind, operation.Index, sources, operation.SourcesCount);
|
source = new AstOperation(
|
||||||
|
inst,
|
||||||
|
operation.StorageKind,
|
||||||
|
operation.ForcePrecise,
|
||||||
|
operation.Index,
|
||||||
|
sources,
|
||||||
|
operation.SourcesCount);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -248,7 +260,13 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
context.AddNode(new AstOperation(inst, operation.StorageKind, operation.Index, sources, operation.SourcesCount));
|
context.AddNode(new AstOperation(
|
||||||
|
inst,
|
||||||
|
operation.StorageKind,
|
||||||
|
operation.ForcePrecise,
|
||||||
|
operation.Index,
|
||||||
|
sources,
|
||||||
|
operation.SourcesCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Those instructions needs to be emulated by using helper functions,
|
// Those instructions needs to be emulated by using helper functions,
|
||||||
|
@ -319,7 +319,7 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
|
|||||||
new AstOperand(OperandType.Constant, elemIndex)
|
new AstOperand(OperandType.Constant, elemIndex)
|
||||||
};
|
};
|
||||||
|
|
||||||
return new AstOperation(Instruction.Load, StorageKind.ConstantBuffer, sources, sources.Length);
|
return new AstOperation(Instruction.Load, StorageKind.ConstantBuffer, false, sources, sources.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetOperand(operand);
|
return GetOperand(operand);
|
||||||
|
@ -49,13 +49,17 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
private readonly List<Operation> _operations;
|
private readonly List<Operation> _operations;
|
||||||
private readonly Dictionary<ulong, BlockLabel> _labels;
|
private readonly Dictionary<ulong, BlockLabel> _labels;
|
||||||
|
|
||||||
public EmitterContext(DecodedProgram program, ShaderConfig config, bool isNonMain)
|
public EmitterContext()
|
||||||
|
{
|
||||||
|
_operations = new List<Operation>();
|
||||||
|
_labels = new Dictionary<ulong, BlockLabel>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public EmitterContext(DecodedProgram program, ShaderConfig config, bool isNonMain) : this()
|
||||||
{
|
{
|
||||||
Program = program;
|
Program = program;
|
||||||
Config = config;
|
Config = config;
|
||||||
IsNonMain = isNonMain;
|
IsNonMain = isNonMain;
|
||||||
_operations = new List<Operation>();
|
|
||||||
_labels = new Dictionary<ulong, BlockLabel>();
|
|
||||||
|
|
||||||
EmitStart();
|
EmitStart();
|
||||||
}
|
}
|
||||||
@ -242,7 +246,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
this.Store(StorageKind.Output, IoVariable.Position, null, Const(1), this.FPFusedMultiplyAdd(y, yScale, negativeOne));
|
this.Store(StorageKind.Output, IoVariable.Position, null, Const(1), this.FPFusedMultiplyAdd(y, yScale, negativeOne));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.Options.TargetApi == TargetApi.Vulkan && Config.GpuAccessor.QueryTransformDepthMinusOneToOne())
|
if (Config.GpuAccessor.QueryTransformDepthMinusOneToOne() && !Config.GpuAccessor.QueryHostSupportsDepthClipControl())
|
||||||
{
|
{
|
||||||
Operand z = this.Load(StorageKind.Output, IoVariable.Position, null, Const(2));
|
Operand z = this.Load(StorageKind.Output, IoVariable.Position, null, Const(2));
|
||||||
Operand w = this.Load(StorageKind.Output, IoVariable.Position, null, Const(3));
|
Operand w = this.Load(StorageKind.Output, IoVariable.Position, null, Const(3));
|
||||||
@ -279,7 +283,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
oldYLocal = null;
|
oldYLocal = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Config.Options.TargetApi == TargetApi.Vulkan && Config.GpuAccessor.QueryTransformDepthMinusOneToOne())
|
if (Config.GpuAccessor.QueryTransformDepthMinusOneToOne() && !Config.GpuAccessor.QueryHostSupportsDepthClipControl())
|
||||||
{
|
{
|
||||||
oldZLocal = Local();
|
oldZLocal = Local();
|
||||||
this.Copy(oldZLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(2)));
|
this.Copy(oldZLocal, this.Load(StorageKind.Output, IoVariable.Position, null, Const(2)));
|
||||||
|
@ -307,6 +307,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
return context.Add(fpType | Instruction.Minimum, Local(), a, b);
|
return context.Add(fpType | Instruction.Minimum, Local(), a, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Operand FPModulo(this EmitterContext context, Operand a, Operand b, Instruction fpType = Instruction.FP32)
|
||||||
|
{
|
||||||
|
return context.Add(fpType | Instruction.Modulo, Local(), a, b);
|
||||||
|
}
|
||||||
|
|
||||||
public static Operand FPMultiply(this EmitterContext context, Operand a, Operand b, Instruction fpType = Instruction.FP32)
|
public static Operand FPMultiply(this EmitterContext context, Operand a, Operand b, Instruction fpType = Instruction.FP32)
|
||||||
{
|
{
|
||||||
return context.Add(fpType | Instruction.Multiply, Local(), a, b);
|
return context.Add(fpType | Instruction.Multiply, Local(), a, b);
|
||||||
@ -656,7 +661,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
public static void Return(this EmitterContext context, Operand returnValue)
|
public static void Return(this EmitterContext context, Operand returnValue)
|
||||||
{
|
{
|
||||||
context.PrepareForReturn();
|
|
||||||
context.Add(Instruction.Return, null, returnValue);
|
context.Add(Instruction.Return, null, returnValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
134
src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs
Normal file
134
src/Ryujinx.Graphics.Shader/Translation/HelperFunctionManager.cs
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
{
|
||||||
|
class HelperFunctionManager
|
||||||
|
{
|
||||||
|
private readonly List<Function> _functionList;
|
||||||
|
private readonly Dictionary<HelperFunctionName, int> _functionIds;
|
||||||
|
private readonly ShaderStage _stage;
|
||||||
|
|
||||||
|
public HelperFunctionManager(List<Function> functionList, ShaderStage stage)
|
||||||
|
{
|
||||||
|
_functionList = functionList;
|
||||||
|
_functionIds = new Dictionary<HelperFunctionName, int>();
|
||||||
|
_stage = stage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int GetOrCreateFunctionId(HelperFunctionName functionName)
|
||||||
|
{
|
||||||
|
if (_functionIds.TryGetValue(functionName, out int functionId))
|
||||||
|
{
|
||||||
|
return functionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
Function function = GenerateFunction(functionName);
|
||||||
|
functionId = _functionList.Count;
|
||||||
|
_functionList.Add(function);
|
||||||
|
_functionIds.Add(functionName, functionId);
|
||||||
|
|
||||||
|
return functionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function GenerateFunction(HelperFunctionName functionName)
|
||||||
|
{
|
||||||
|
return functionName switch
|
||||||
|
{
|
||||||
|
HelperFunctionName.TexelFetchScale => GenerateTexelFetchScaleFunction(),
|
||||||
|
HelperFunctionName.TextureSizeUnscale => GenerateTextureSizeUnscaleFunction(),
|
||||||
|
_ => throw new ArgumentException($"Invalid function name {functionName}")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function GenerateTexelFetchScaleFunction()
|
||||||
|
{
|
||||||
|
EmitterContext context = new EmitterContext();
|
||||||
|
|
||||||
|
Operand input = Argument(0);
|
||||||
|
Operand samplerIndex = Argument(1);
|
||||||
|
Operand index = GetScaleIndex(context, samplerIndex);
|
||||||
|
|
||||||
|
Operand scale = context.Load(StorageKind.ConstantBuffer, 0, Const((int)SupportBufferField.RenderScale), index);
|
||||||
|
|
||||||
|
Operand scaleIsOne = context.FPCompareEqual(scale, ConstF(1f));
|
||||||
|
Operand lblScaleNotOne = Label();
|
||||||
|
|
||||||
|
context.BranchIfFalse(lblScaleNotOne, scaleIsOne);
|
||||||
|
context.Return(input);
|
||||||
|
context.MarkLabel(lblScaleNotOne);
|
||||||
|
|
||||||
|
int inArgumentsCount;
|
||||||
|
|
||||||
|
if (_stage == ShaderStage.Fragment)
|
||||||
|
{
|
||||||
|
Operand scaleIsLessThanZero = context.FPCompareLess(scale, ConstF(0f));
|
||||||
|
Operand lblScaleGreaterOrEqualZero = Label();
|
||||||
|
|
||||||
|
context.BranchIfFalse(lblScaleGreaterOrEqualZero, scaleIsLessThanZero);
|
||||||
|
|
||||||
|
Operand negScale = context.FPNegate(scale);
|
||||||
|
Operand inputScaled = context.FPMultiply(context.IConvertS32ToFP32(input), negScale);
|
||||||
|
Operand fragCoordX = context.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(0));
|
||||||
|
Operand fragCoordY = context.Load(StorageKind.Input, IoVariable.FragmentCoord, null, Const(1));
|
||||||
|
Operand fragCoord = context.ConditionalSelect(Argument(2), fragCoordY, fragCoordX);
|
||||||
|
Operand inputBias = context.FPModulo(fragCoord, negScale);
|
||||||
|
Operand inputWithBias = context.FPAdd(inputScaled, inputBias);
|
||||||
|
|
||||||
|
context.Return(context.FP32ConvertToS32(inputWithBias));
|
||||||
|
context.MarkLabel(lblScaleGreaterOrEqualZero);
|
||||||
|
|
||||||
|
inArgumentsCount = 3;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inArgumentsCount = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand inputScaled2 = context.FPMultiply(context.IConvertS32ToFP32(input), scale);
|
||||||
|
|
||||||
|
context.Return(context.FP32ConvertToS32(inputScaled2));
|
||||||
|
|
||||||
|
return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, "TexelFetchScale", true, inArgumentsCount, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function GenerateTextureSizeUnscaleFunction()
|
||||||
|
{
|
||||||
|
EmitterContext context = new EmitterContext();
|
||||||
|
|
||||||
|
Operand input = Argument(0);
|
||||||
|
Operand samplerIndex = Argument(1);
|
||||||
|
Operand index = GetScaleIndex(context, samplerIndex);
|
||||||
|
|
||||||
|
Operand scale = context.FPAbsolute(context.Load(StorageKind.ConstantBuffer, 0, Const((int)SupportBufferField.RenderScale), index));
|
||||||
|
|
||||||
|
Operand scaleIsOne = context.FPCompareEqual(scale, ConstF(1f));
|
||||||
|
Operand lblScaleNotOne = Label();
|
||||||
|
|
||||||
|
context.BranchIfFalse(lblScaleNotOne, scaleIsOne);
|
||||||
|
context.Return(input);
|
||||||
|
context.MarkLabel(lblScaleNotOne);
|
||||||
|
|
||||||
|
Operand inputUnscaled = context.FPDivide(context.IConvertS32ToFP32(input), scale);
|
||||||
|
|
||||||
|
context.Return(context.FP32ConvertToS32(inputUnscaled));
|
||||||
|
|
||||||
|
return new Function(ControlFlowGraph.Create(context.GetOperations()).Blocks, "TextureSizeUnscale", true, 2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Operand GetScaleIndex(EmitterContext context, Operand index)
|
||||||
|
{
|
||||||
|
switch (_stage)
|
||||||
|
{
|
||||||
|
case ShaderStage.Vertex:
|
||||||
|
Operand fragScaleCount = context.Load(StorageKind.ConstantBuffer, 0, Const((int)SupportBufferField.FragmentRenderScaleCount));
|
||||||
|
return context.IAdd(Const(1), context.IAdd(index, fragScaleCount));
|
||||||
|
default:
|
||||||
|
return context.IAdd(Const(1), index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
{
|
||||||
|
enum HelperFunctionName
|
||||||
|
{
|
||||||
|
TexelFetchScale,
|
||||||
|
TextureSizeUnscale
|
||||||
|
}
|
||||||
|
}
|
@ -17,6 +17,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
private readonly HashSet<int> _usedConstantBufferBindings;
|
private readonly HashSet<int> _usedConstantBufferBindings;
|
||||||
|
|
||||||
|
public ShaderProperties Properties => _properties;
|
||||||
|
|
||||||
public ResourceManager(ShaderStage stage, IGpuAccessor gpuAccessor, ShaderProperties properties)
|
public ResourceManager(ShaderStage stage, IGpuAccessor gpuAccessor, ShaderProperties properties)
|
||||||
{
|
{
|
||||||
_gpuAccessor = gpuAccessor;
|
_gpuAccessor = gpuAccessor;
|
||||||
@ -98,19 +100,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
_properties.AddConstantBuffer(binding, new BufferDefinition(BufferLayout.Std140, 0, binding, name, type));
|
_properties.AddConstantBuffer(binding, new BufferDefinition(BufferLayout.Std140, 0, binding, name, type));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InheritFrom(ResourceManager other)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < other._cbSlotToBindingMap.Length; i++)
|
|
||||||
{
|
|
||||||
int binding = other._cbSlotToBindingMap[i];
|
|
||||||
|
|
||||||
if (binding >= 0)
|
|
||||||
{
|
|
||||||
_cbSlotToBindingMap[i] = binding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetShaderStagePrefix(ShaderStage stage)
|
public static string GetShaderStagePrefix(ShaderStage stage)
|
||||||
{
|
{
|
||||||
uint index = (uint)stage;
|
uint index = (uint)stage;
|
||||||
|
@ -11,9 +11,10 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
{
|
{
|
||||||
static class Rewriter
|
static class Rewriter
|
||||||
{
|
{
|
||||||
public static void RunPass(BasicBlock[] blocks, ShaderConfig config)
|
public static void RunPass(HelperFunctionManager hfm, BasicBlock[] blocks, ShaderConfig config)
|
||||||
{
|
{
|
||||||
bool isVertexShader = config.Stage == ShaderStage.Vertex;
|
bool isVertexShader = config.Stage == ShaderStage.Vertex;
|
||||||
|
bool isImpreciseFragmentShader = config.Stage == ShaderStage.Fragment && config.GpuAccessor.QueryHostReducedPrecision();
|
||||||
bool hasConstantBufferDrawParameters = config.GpuAccessor.QueryHasConstantBufferDrawParameters();
|
bool hasConstantBufferDrawParameters = config.GpuAccessor.QueryHasConstantBufferDrawParameters();
|
||||||
bool hasVectorIndexingBug = config.GpuAccessor.QueryHostHasVectorIndexingBug();
|
bool hasVectorIndexingBug = config.GpuAccessor.QueryHostHasVectorIndexingBug();
|
||||||
bool supportsSnormBufferTextureFormat = config.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat();
|
bool supportsSnormBufferTextureFormat = config.GpuAccessor.QueryHostSupportsSnormBufferTextureFormat();
|
||||||
@ -45,6 +46,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isImpreciseFragmentShader)
|
||||||
|
{
|
||||||
|
EnableForcePreciseIfNeeded(operation);
|
||||||
|
}
|
||||||
|
|
||||||
if (hasVectorIndexingBug)
|
if (hasVectorIndexingBug)
|
||||||
{
|
{
|
||||||
InsertVectorComponentSelect(node, config);
|
InsertVectorComponentSelect(node, config);
|
||||||
@ -54,9 +60,14 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
if (operation is TextureOperation texOp)
|
if (operation is TextureOperation texOp)
|
||||||
{
|
{
|
||||||
|
node = InsertTexelFetchScale(hfm, node, config);
|
||||||
|
node = InsertTextureSizeUnscale(hfm, node, config);
|
||||||
|
|
||||||
if (texOp.Inst == Instruction.TextureSample)
|
if (texOp.Inst == Instruction.TextureSample)
|
||||||
{
|
{
|
||||||
node = RewriteTextureSample(node, config);
|
node = InsertCoordNormalization(node, config);
|
||||||
|
node = InsertCoordGatherBias(node, config);
|
||||||
|
node = InsertConstOffsets(node, config);
|
||||||
|
|
||||||
if (texOp.Type == SamplerType.TextureBuffer && !supportsSnormBufferTextureFormat)
|
if (texOp.Type == SamplerType.TextureBuffer && !supportsSnormBufferTextureFormat)
|
||||||
{
|
{
|
||||||
@ -76,6 +87,25 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void EnableForcePreciseIfNeeded(Operation operation)
|
||||||
|
{
|
||||||
|
// There are some cases where a small bias is added to values to prevent division by zero.
|
||||||
|
// When operating with reduced precision, it is possible for this bias to get rounded to 0
|
||||||
|
// and cause a division by zero.
|
||||||
|
// To prevent that, we force those operations to be precise even if the host wants
|
||||||
|
// imprecise operations for performance.
|
||||||
|
|
||||||
|
if (operation.Inst == (Instruction.FP32 | Instruction.Divide) &&
|
||||||
|
operation.GetSource(0).Type == OperandType.Constant &&
|
||||||
|
operation.GetSource(0).AsFloat() == 1f &&
|
||||||
|
operation.GetSource(1).AsgOp is Operation addOp &&
|
||||||
|
addOp.Inst == (Instruction.FP32 | Instruction.Add) &&
|
||||||
|
addOp.GetSource(1).Type == OperandType.Constant)
|
||||||
|
{
|
||||||
|
addOp.ForcePrecise = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void InsertVectorComponentSelect(LinkedListNode<INode> node, ShaderConfig config)
|
private static void InsertVectorComponentSelect(LinkedListNode<INode> node, ShaderConfig config)
|
||||||
{
|
{
|
||||||
Operation operation = (Operation)node.Value;
|
Operation operation = (Operation)node.Value;
|
||||||
@ -344,10 +374,279 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static LinkedListNode<INode> RewriteTextureSample(LinkedListNode<INode> node, ShaderConfig config)
|
private static LinkedListNode<INode> InsertTexelFetchScale(HelperFunctionManager hfm, LinkedListNode<INode> node, ShaderConfig config)
|
||||||
{
|
{
|
||||||
TextureOperation texOp = (TextureOperation)node.Value;
|
TextureOperation texOp = (TextureOperation)node.Value;
|
||||||
|
|
||||||
|
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||||
|
bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0;
|
||||||
|
|
||||||
|
bool isArray = (texOp.Type & SamplerType.Array) != 0;
|
||||||
|
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
||||||
|
|
||||||
|
int coordsCount = texOp.Type.GetDimensions();
|
||||||
|
|
||||||
|
int coordsIndex = isBindless || isIndexed ? 1 : 0;
|
||||||
|
|
||||||
|
bool isImage = IsImageInstructionWithScale(texOp.Inst);
|
||||||
|
|
||||||
|
if ((texOp.Inst == Instruction.TextureSample || isImage) &&
|
||||||
|
(intCoords || isImage) &&
|
||||||
|
!isBindless &&
|
||||||
|
!isIndexed &&
|
||||||
|
config.Stage.SupportsRenderScale() &&
|
||||||
|
TypeSupportsScale(texOp.Type))
|
||||||
|
{
|
||||||
|
int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.TexelFetchScale);
|
||||||
|
int samplerIndex = isImage
|
||||||
|
? config.GetTextureDescriptors().Length + config.FindImageDescriptorIndex(texOp)
|
||||||
|
: config.FindTextureDescriptorIndex(texOp);
|
||||||
|
|
||||||
|
for (int index = 0; index < coordsCount; index++)
|
||||||
|
{
|
||||||
|
Operand scaledCoord = Local();
|
||||||
|
Operand[] callArgs;
|
||||||
|
|
||||||
|
if (config.Stage == ShaderStage.Fragment)
|
||||||
|
{
|
||||||
|
callArgs = new Operand[] { Const(functionId), texOp.GetSource(coordsIndex + index), Const(samplerIndex), Const(index) };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
callArgs = new Operand[] { Const(functionId), texOp.GetSource(coordsIndex + index), Const(samplerIndex) };
|
||||||
|
}
|
||||||
|
|
||||||
|
node.List.AddBefore(node, new Operation(Instruction.Call, 0, scaledCoord, callArgs));
|
||||||
|
|
||||||
|
texOp.SetSource(coordsIndex + index, scaledCoord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LinkedListNode<INode> InsertTextureSizeUnscale(HelperFunctionManager hfm, LinkedListNode<INode> node, ShaderConfig config)
|
||||||
|
{
|
||||||
|
TextureOperation texOp = (TextureOperation)node.Value;
|
||||||
|
|
||||||
|
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||||
|
bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0;
|
||||||
|
|
||||||
|
bool isArray = (texOp.Type & SamplerType.Array) != 0;
|
||||||
|
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
||||||
|
|
||||||
|
if (texOp.Inst == Instruction.TextureSize &&
|
||||||
|
texOp.Index < 2 &&
|
||||||
|
!isBindless &&
|
||||||
|
!isIndexed &&
|
||||||
|
config.Stage.SupportsRenderScale() &&
|
||||||
|
TypeSupportsScale(texOp.Type))
|
||||||
|
{
|
||||||
|
int functionId = hfm.GetOrCreateFunctionId(HelperFunctionName.TextureSizeUnscale);
|
||||||
|
int samplerIndex = config.FindTextureDescriptorIndex(texOp, ignoreType: true);
|
||||||
|
|
||||||
|
for (int index = texOp.DestsCount - 1; index >= 0; index--)
|
||||||
|
{
|
||||||
|
Operand dest = texOp.GetDest(index);
|
||||||
|
|
||||||
|
Operand unscaledSize = Local();
|
||||||
|
|
||||||
|
// Replace all uses with the unscaled size value.
|
||||||
|
// This must be done before the call is added, since it also is a use of the original size.
|
||||||
|
foreach (INode useOp in dest.UseOps)
|
||||||
|
{
|
||||||
|
for (int srcIndex = 0; srcIndex < useOp.SourcesCount; srcIndex++)
|
||||||
|
{
|
||||||
|
if (useOp.GetSource(srcIndex) == dest)
|
||||||
|
{
|
||||||
|
useOp.SetSource(srcIndex, unscaledSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Operand[] callArgs = new Operand[] { Const(functionId), dest, Const(samplerIndex) };
|
||||||
|
|
||||||
|
node.List.AddAfter(node, new Operation(Instruction.Call, 0, unscaledSize, callArgs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsImageInstructionWithScale(Instruction inst)
|
||||||
|
{
|
||||||
|
// Currently, we don't support scaling images that are modified,
|
||||||
|
// so we only need to care about the load instruction.
|
||||||
|
return inst == Instruction.ImageLoad;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TypeSupportsScale(SamplerType type)
|
||||||
|
{
|
||||||
|
return (type & SamplerType.Mask) == SamplerType.Texture2D;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LinkedListNode<INode> InsertCoordNormalization(LinkedListNode<INode> node, ShaderConfig config)
|
||||||
|
{
|
||||||
|
// Emulate non-normalized coordinates by normalizing the coordinates on the shader.
|
||||||
|
// Without normalization, the coordinates are expected to the in the [0, W or H] range,
|
||||||
|
// and otherwise, it is expected to be in the [0, 1] range.
|
||||||
|
// We normalize by dividing the coords by the texture size.
|
||||||
|
|
||||||
|
TextureOperation texOp = (TextureOperation)node.Value;
|
||||||
|
|
||||||
|
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||||
|
bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0;
|
||||||
|
|
||||||
|
bool isCoordNormalized = isBindless || config.GpuAccessor.QueryTextureCoordNormalized(texOp.Handle, texOp.CbufSlot);
|
||||||
|
|
||||||
|
if (isCoordNormalized || intCoords)
|
||||||
|
{
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isArray = (texOp.Type & SamplerType.Array) != 0;
|
||||||
|
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
||||||
|
|
||||||
|
int coordsCount = texOp.Type.GetDimensions();
|
||||||
|
int coordsIndex = isBindless || isIndexed ? 1 : 0;
|
||||||
|
|
||||||
|
config.SetUsedFeature(FeatureFlags.IntegerSampling);
|
||||||
|
|
||||||
|
int normCoordsCount = (texOp.Type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : coordsCount;
|
||||||
|
|
||||||
|
for (int index = 0; index < normCoordsCount; index++)
|
||||||
|
{
|
||||||
|
Operand coordSize = Local();
|
||||||
|
|
||||||
|
Operand[] texSizeSources;
|
||||||
|
|
||||||
|
if (isBindless || isIndexed)
|
||||||
|
{
|
||||||
|
texSizeSources = new Operand[] { texOp.GetSource(0), Const(0) };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
texSizeSources = new Operand[] { Const(0) };
|
||||||
|
}
|
||||||
|
|
||||||
|
node.List.AddBefore(node, new TextureOperation(
|
||||||
|
Instruction.TextureSize,
|
||||||
|
texOp.Type,
|
||||||
|
texOp.Format,
|
||||||
|
texOp.Flags,
|
||||||
|
texOp.CbufSlot,
|
||||||
|
texOp.Handle,
|
||||||
|
index,
|
||||||
|
new[] { coordSize },
|
||||||
|
texSizeSources));
|
||||||
|
|
||||||
|
config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle);
|
||||||
|
|
||||||
|
Operand source = texOp.GetSource(coordsIndex + index);
|
||||||
|
|
||||||
|
Operand coordNormalized = Local();
|
||||||
|
|
||||||
|
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, coordNormalized, source, GenerateI2f(node, coordSize)));
|
||||||
|
|
||||||
|
texOp.SetSource(coordsIndex + index, coordNormalized);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LinkedListNode<INode> InsertCoordGatherBias(LinkedListNode<INode> node, ShaderConfig config)
|
||||||
|
{
|
||||||
|
// The gather behavior when the coordinate sits right in the middle of two texels is not well defined.
|
||||||
|
// To ensure the correct texel is sampled, we add a small bias value to the coordinate.
|
||||||
|
// This value is calculated as the minimum value required to change the texel it will sample from,
|
||||||
|
// and is 0 if the host does not require the bias.
|
||||||
|
|
||||||
|
TextureOperation texOp = (TextureOperation)node.Value;
|
||||||
|
|
||||||
|
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||||
|
bool isGather = (texOp.Flags & TextureFlags.Gather) != 0;
|
||||||
|
|
||||||
|
int gatherBiasPrecision = config.GpuAccessor.QueryHostGatherBiasPrecision();
|
||||||
|
|
||||||
|
if (!isGather || gatherBiasPrecision == 0)
|
||||||
|
{
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool intCoords = (texOp.Flags & TextureFlags.IntCoords) != 0;
|
||||||
|
|
||||||
|
bool isArray = (texOp.Type & SamplerType.Array) != 0;
|
||||||
|
bool isIndexed = (texOp.Type & SamplerType.Indexed) != 0;
|
||||||
|
|
||||||
|
int coordsCount = texOp.Type.GetDimensions();
|
||||||
|
int coordsIndex = isBindless || isIndexed ? 1 : 0;
|
||||||
|
|
||||||
|
config.SetUsedFeature(FeatureFlags.IntegerSampling);
|
||||||
|
|
||||||
|
int normCoordsCount = (texOp.Type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : coordsCount;
|
||||||
|
|
||||||
|
for (int index = 0; index < normCoordsCount; index++)
|
||||||
|
{
|
||||||
|
Operand coordSize = Local();
|
||||||
|
Operand scaledSize = Local();
|
||||||
|
Operand bias = Local();
|
||||||
|
|
||||||
|
Operand[] texSizeSources;
|
||||||
|
|
||||||
|
if (isBindless || isIndexed)
|
||||||
|
{
|
||||||
|
texSizeSources = new Operand[] { texOp.GetSource(0), Const(0) };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
texSizeSources = new Operand[] { Const(0) };
|
||||||
|
}
|
||||||
|
|
||||||
|
node.List.AddBefore(node, new TextureOperation(
|
||||||
|
Instruction.TextureSize,
|
||||||
|
texOp.Type,
|
||||||
|
texOp.Format,
|
||||||
|
texOp.Flags,
|
||||||
|
texOp.CbufSlot,
|
||||||
|
texOp.Handle,
|
||||||
|
index,
|
||||||
|
new[] { coordSize },
|
||||||
|
texSizeSources));
|
||||||
|
|
||||||
|
config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle);
|
||||||
|
|
||||||
|
node.List.AddBefore(node, new Operation(
|
||||||
|
Instruction.FP32 | Instruction.Multiply,
|
||||||
|
scaledSize,
|
||||||
|
GenerateI2f(node, coordSize),
|
||||||
|
ConstF((float)(1 << (gatherBiasPrecision + 1)))));
|
||||||
|
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, bias, ConstF(1f), scaledSize));
|
||||||
|
|
||||||
|
Operand source = texOp.GetSource(coordsIndex + index);
|
||||||
|
|
||||||
|
Operand coordBiased = Local();
|
||||||
|
|
||||||
|
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Add, coordBiased, source, bias));
|
||||||
|
|
||||||
|
texOp.SetSource(coordsIndex + index, coordBiased);
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LinkedListNode<INode> InsertConstOffsets(LinkedListNode<INode> node, ShaderConfig config)
|
||||||
|
{
|
||||||
|
// Non-constant texture offsets are not allowed (according to the spec),
|
||||||
|
// however some GPUs does support that.
|
||||||
|
// For GPUs where it is not supported, we can replace the instruction with the following:
|
||||||
|
// For texture*Offset, we replace it by texture*, and add the offset to the P coords.
|
||||||
|
// The offset can be calculated as offset / textureSize(lod), where lod = textureQueryLod(coords).
|
||||||
|
// For texelFetchOffset, we replace it by texelFetch and add the offset to the P coords directly.
|
||||||
|
// For textureGatherOffset, we split the operation into up to 4 operations, one for each component
|
||||||
|
// that is accessed, where each textureGather operation has a different offset for each pixel.
|
||||||
|
|
||||||
|
TextureOperation texOp = (TextureOperation)node.Value;
|
||||||
|
|
||||||
bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0;
|
bool hasOffset = (texOp.Flags & TextureFlags.Offset) != 0;
|
||||||
bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0;
|
bool hasOffsets = (texOp.Flags & TextureFlags.Offsets) != 0;
|
||||||
|
|
||||||
@ -355,9 +654,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
bool isBindless = (texOp.Flags & TextureFlags.Bindless) != 0;
|
||||||
|
|
||||||
bool isCoordNormalized = isBindless || config.GpuAccessor.QueryTextureCoordNormalized(texOp.Handle, texOp.CbufSlot);
|
if (!hasInvalidOffset)
|
||||||
|
|
||||||
if (!hasInvalidOffset && isCoordNormalized)
|
|
||||||
{
|
{
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@ -454,7 +751,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
hasInvalidOffset &= !areAllOffsetsConstant;
|
hasInvalidOffset &= !areAllOffsetsConstant;
|
||||||
|
|
||||||
if (!hasInvalidOffset && isCoordNormalized)
|
if (!hasInvalidOffset)
|
||||||
{
|
{
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
@ -473,63 +770,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
int componentIndex = texOp.Index;
|
int componentIndex = texOp.Index;
|
||||||
|
|
||||||
Operand Float(Operand value)
|
|
||||||
{
|
|
||||||
Operand res = Local();
|
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(Instruction.ConvertS32ToFP32, res, value));
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emulate non-normalized coordinates by normalizing the coordinates on the shader.
|
|
||||||
// Without normalization, the coordinates are expected to the in the [0, W or H] range,
|
|
||||||
// and otherwise, it is expected to be in the [0, 1] range.
|
|
||||||
// We normalize by dividing the coords by the texture size.
|
|
||||||
if (!isCoordNormalized && !intCoords)
|
|
||||||
{
|
|
||||||
config.SetUsedFeature(FeatureFlags.IntegerSampling);
|
|
||||||
|
|
||||||
int normCoordsCount = (texOp.Type & SamplerType.Mask) == SamplerType.TextureCube ? 2 : coordsCount;
|
|
||||||
|
|
||||||
for (int index = 0; index < normCoordsCount; index++)
|
|
||||||
{
|
|
||||||
Operand coordSize = Local();
|
|
||||||
|
|
||||||
Operand[] texSizeSources;
|
|
||||||
|
|
||||||
if (isBindless || isIndexed)
|
|
||||||
{
|
|
||||||
texSizeSources = new Operand[] { sources[0], Const(0) };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
texSizeSources = new Operand[] { Const(0) };
|
|
||||||
}
|
|
||||||
|
|
||||||
node.List.AddBefore(node, new TextureOperation(
|
|
||||||
Instruction.TextureSize,
|
|
||||||
texOp.Type,
|
|
||||||
texOp.Format,
|
|
||||||
texOp.Flags,
|
|
||||||
texOp.CbufSlot,
|
|
||||||
texOp.Handle,
|
|
||||||
index,
|
|
||||||
new[] { coordSize },
|
|
||||||
texSizeSources));
|
|
||||||
|
|
||||||
config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle);
|
|
||||||
|
|
||||||
Operand source = sources[coordsIndex + index];
|
|
||||||
|
|
||||||
Operand coordNormalized = Local();
|
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, coordNormalized, source, Float(coordSize)));
|
|
||||||
|
|
||||||
sources[coordsIndex + index] = coordNormalized;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand[] dests = new Operand[texOp.DestsCount];
|
Operand[] dests = new Operand[texOp.DestsCount];
|
||||||
|
|
||||||
for (int i = 0; i < texOp.DestsCount; i++)
|
for (int i = 0; i < texOp.DestsCount; i++)
|
||||||
@ -541,15 +781,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
LinkedListNode<INode> oldNode = node;
|
LinkedListNode<INode> oldNode = node;
|
||||||
|
|
||||||
// Technically, non-constant texture offsets are not allowed (according to the spec),
|
if (isGather && !isShadow)
|
||||||
// however some GPUs does support that.
|
|
||||||
// For GPUs where it is not supported, we can replace the instruction with the following:
|
|
||||||
// For texture*Offset, we replace it by texture*, and add the offset to the P coords.
|
|
||||||
// The offset can be calculated as offset / textureSize(lod), where lod = textureQueryLod(coords).
|
|
||||||
// For texelFetchOffset, we replace it by texelFetch and add the offset to the P coords directly.
|
|
||||||
// For textureGatherOffset, we split the operation into up to 4 operations, one for each component
|
|
||||||
// that is accessed, where each textureGather operation has a different offset for each pixel.
|
|
||||||
if (hasInvalidOffset && isGather && !isShadow)
|
|
||||||
{
|
{
|
||||||
config.SetUsedFeature(FeatureFlags.IntegerSampling);
|
config.SetUsedFeature(FeatureFlags.IntegerSampling);
|
||||||
|
|
||||||
@ -557,7 +789,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
sources.CopyTo(newSources, 0);
|
sources.CopyTo(newSources, 0);
|
||||||
|
|
||||||
Operand[] texSizes = InsertTextureSize(node, texOp, lodSources, bindlessHandle, coordsCount);
|
Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount);
|
||||||
|
|
||||||
int destIndex = 0;
|
int destIndex = 0;
|
||||||
|
|
||||||
@ -576,7 +808,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
Operand intOffset = offsets[index + (hasOffsets ? compIndex * coordsCount : 0)];
|
Operand intOffset = offsets[index + (hasOffsets ? compIndex * coordsCount : 0)];
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, offset, Float(intOffset), Float(texSizes[index])));
|
node.List.AddBefore(node, new Operation(
|
||||||
|
Instruction.FP32 | Instruction.Divide,
|
||||||
|
offset,
|
||||||
|
GenerateI2f(node, intOffset),
|
||||||
|
GenerateI2f(node, texSizes[index])));
|
||||||
|
|
||||||
Operand source = sources[coordsIndex + index];
|
Operand source = sources[coordsIndex + index];
|
||||||
|
|
||||||
@ -603,45 +839,46 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (hasInvalidOffset)
|
if (intCoords)
|
||||||
{
|
{
|
||||||
if (intCoords)
|
for (int index = 0; index < coordsCount; index++)
|
||||||
{
|
{
|
||||||
for (int index = 0; index < coordsCount; index++)
|
Operand source = sources[coordsIndex + index];
|
||||||
{
|
|
||||||
Operand source = sources[coordsIndex + index];
|
|
||||||
|
|
||||||
Operand coordPlusOffset = Local();
|
Operand coordPlusOffset = Local();
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(Instruction.Add, coordPlusOffset, source, offsets[index]));
|
node.List.AddBefore(node, new Operation(Instruction.Add, coordPlusOffset, source, offsets[index]));
|
||||||
|
|
||||||
sources[coordsIndex + index] = coordPlusOffset;
|
sources[coordsIndex + index] = coordPlusOffset;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
config.SetUsedFeature(FeatureFlags.IntegerSampling);
|
||||||
|
|
||||||
|
Operand[] texSizes = InsertTextureLod(node, texOp, lodSources, bindlessHandle, coordsCount);
|
||||||
|
|
||||||
|
for (int index = 0; index < coordsCount; index++)
|
||||||
{
|
{
|
||||||
config.SetUsedFeature(FeatureFlags.IntegerSampling);
|
config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle);
|
||||||
|
|
||||||
Operand[] texSizes = InsertTextureSize(node, texOp, lodSources, bindlessHandle, coordsCount);
|
Operand offset = Local();
|
||||||
|
|
||||||
for (int index = 0; index < coordsCount; index++)
|
Operand intOffset = offsets[index];
|
||||||
{
|
|
||||||
config.SetUsedTexture(Instruction.TextureSize, texOp.Type, texOp.Format, texOp.Flags, texOp.CbufSlot, texOp.Handle);
|
|
||||||
|
|
||||||
Operand offset = Local();
|
node.List.AddBefore(node, new Operation(
|
||||||
|
Instruction.FP32 | Instruction.Divide,
|
||||||
|
offset,
|
||||||
|
GenerateI2f(node, intOffset),
|
||||||
|
GenerateI2f(node, texSizes[index])));
|
||||||
|
|
||||||
Operand intOffset = offsets[index];
|
Operand source = sources[coordsIndex + index];
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Divide, offset, Float(intOffset), Float(texSizes[index])));
|
Operand coordPlusOffset = Local();
|
||||||
|
|
||||||
Operand source = sources[coordsIndex + index];
|
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Add, coordPlusOffset, source, offset));
|
||||||
|
|
||||||
Operand coordPlusOffset = Local();
|
sources[coordsIndex + index] = coordPlusOffset;
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(Instruction.FP32 | Instruction.Add, coordPlusOffset, source, offset));
|
|
||||||
|
|
||||||
sources[coordsIndex + index] = coordPlusOffset;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -669,22 +906,13 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Operand[] InsertTextureSize(
|
private static Operand[] InsertTextureLod(
|
||||||
LinkedListNode<INode> node,
|
LinkedListNode<INode> node,
|
||||||
TextureOperation texOp,
|
TextureOperation texOp,
|
||||||
Operand[] lodSources,
|
Operand[] lodSources,
|
||||||
Operand bindlessHandle,
|
Operand bindlessHandle,
|
||||||
int coordsCount)
|
int coordsCount)
|
||||||
{
|
{
|
||||||
Operand Int(Operand value)
|
|
||||||
{
|
|
||||||
Operand res = Local();
|
|
||||||
|
|
||||||
node.List.AddBefore(node, new Operation(Instruction.ConvertFP32ToS32, res, value));
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
Operand[] texSizes = new Operand[coordsCount];
|
Operand[] texSizes = new Operand[coordsCount];
|
||||||
|
|
||||||
Operand lod = Local();
|
Operand lod = Local();
|
||||||
@ -708,11 +936,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
if (bindlessHandle != null)
|
if (bindlessHandle != null)
|
||||||
{
|
{
|
||||||
texSizeSources = new Operand[] { bindlessHandle, Int(lod) };
|
texSizeSources = new Operand[] { bindlessHandle, GenerateF2i(node, lod) };
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
texSizeSources = new Operand[] { Int(lod) };
|
texSizeSources = new Operand[] { GenerateF2i(node, lod) };
|
||||||
}
|
}
|
||||||
|
|
||||||
node.List.AddBefore(node, new TextureOperation(
|
node.List.AddBefore(node, new TextureOperation(
|
||||||
@ -796,6 +1024,24 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Operand GenerateI2f(LinkedListNode<INode> node, Operand value)
|
||||||
|
{
|
||||||
|
Operand res = Local();
|
||||||
|
|
||||||
|
node.List.AddBefore(node, new Operation(Instruction.ConvertS32ToFP32, res, value));
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Operand GenerateF2i(LinkedListNode<INode> node, Operand value)
|
||||||
|
{
|
||||||
|
Operand res = Local();
|
||||||
|
|
||||||
|
node.List.AddBefore(node, new Operation(Instruction.ConvertFP32ToS32, res, value));
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
private static bool ReplaceConstantBufferWithDrawParameters(LinkedListNode<INode> node, Operation operation)
|
private static bool ReplaceConstantBufferWithDrawParameters(LinkedListNode<INode> node, Operation operation)
|
||||||
{
|
{
|
||||||
Operand GenerateLoad(IoVariable ioVariable)
|
Operand GenerateLoad(IoVariable ioVariable)
|
||||||
|
@ -39,9 +39,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
public TranslationOptions Options { get; }
|
public TranslationOptions Options { get; }
|
||||||
|
|
||||||
public ShaderProperties Properties { get; }
|
public ShaderProperties Properties => ResourceManager.Properties;
|
||||||
|
|
||||||
public ResourceManager ResourceManager { get; }
|
public ResourceManager ResourceManager { get; set; }
|
||||||
|
|
||||||
public bool TransformFeedbackEnabled { get; }
|
public bool TransformFeedbackEnabled { get; }
|
||||||
|
|
||||||
@ -159,8 +159,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
_sbSlots = new Dictionary<int, int>();
|
_sbSlots = new Dictionary<int, int>();
|
||||||
_sbSlotsReverse = new Dictionary<int, int>();
|
_sbSlotsReverse = new Dictionary<int, int>();
|
||||||
|
|
||||||
Properties = new ShaderProperties();
|
ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties());
|
||||||
ResourceManager = new ResourceManager(stage, gpuAccessor, Properties);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShaderConfig(
|
public ShaderConfig(
|
||||||
@ -429,8 +428,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
public void InheritFrom(ShaderConfig other)
|
public void InheritFrom(ShaderConfig other)
|
||||||
{
|
{
|
||||||
ResourceManager.InheritFrom(other.ResourceManager);
|
|
||||||
|
|
||||||
ClipDistancesWritten |= other.ClipDistancesWritten;
|
ClipDistancesWritten |= other.ClipDistancesWritten;
|
||||||
UsedFeatures |= other.UsedFeatures;
|
UsedFeatures |= other.UsedFeatures;
|
||||||
|
|
||||||
@ -860,7 +857,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
return descriptors;
|
return descriptors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public (TextureDescriptor, int) FindTextureDescriptor(AstTextureOperation texOp)
|
public TextureDescriptor FindTextureDescriptor(AstTextureOperation texOp)
|
||||||
{
|
{
|
||||||
TextureDescriptor[] descriptors = GetTextureDescriptors();
|
TextureDescriptor[] descriptors = GetTextureDescriptors();
|
||||||
|
|
||||||
@ -872,11 +869,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
descriptor.HandleIndex == texOp.Handle &&
|
descriptor.HandleIndex == texOp.Handle &&
|
||||||
descriptor.Format == texOp.Format)
|
descriptor.Format == texOp.Format)
|
||||||
{
|
{
|
||||||
return (descriptor, i);
|
return descriptor;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (default, -1);
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int FindDescriptorIndex(TextureDescriptor[] array, AstTextureOperation texOp)
|
private static int FindDescriptorIndex(TextureDescriptor[] array, AstTextureOperation texOp)
|
||||||
@ -897,12 +894,30 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int FindTextureDescriptorIndex(AstTextureOperation texOp)
|
private static int FindDescriptorIndex(TextureDescriptor[] array, TextureOperation texOp, bool ignoreType = false)
|
||||||
{
|
{
|
||||||
return FindDescriptorIndex(GetTextureDescriptors(), texOp);
|
for (int i = 0; i < array.Length; i++)
|
||||||
|
{
|
||||||
|
var descriptor = array[i];
|
||||||
|
|
||||||
|
if ((descriptor.Type == texOp.Type || ignoreType) &&
|
||||||
|
descriptor.CbufSlot == texOp.CbufSlot &&
|
||||||
|
descriptor.HandleIndex == texOp.Handle &&
|
||||||
|
descriptor.Format == texOp.Format)
|
||||||
|
{
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int FindImageDescriptorIndex(AstTextureOperation texOp)
|
public int FindTextureDescriptorIndex(TextureOperation texOp, bool ignoreType = false)
|
||||||
|
{
|
||||||
|
return FindDescriptorIndex(GetTextureDescriptors(), texOp, ignoreType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int FindImageDescriptorIndex(TextureOperation texOp)
|
||||||
{
|
{
|
||||||
return FindDescriptorIndex(GetImageDescriptors(), texOp);
|
return FindDescriptorIndex(GetImageDescriptors(), texOp);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Shader.Translation
|
namespace Ryujinx.Graphics.Shader.Translation
|
||||||
{
|
{
|
||||||
static class ShaderIdentifier
|
static class ShaderIdentifier
|
||||||
{
|
{
|
||||||
public static ShaderIdentification Identify(Function[] functions, ShaderConfig config)
|
public static ShaderIdentification Identify(IReadOnlyList<Function> functions, ShaderConfig config)
|
||||||
{
|
{
|
||||||
if (config.Stage == ShaderStage.Geometry &&
|
if (config.Stage == ShaderStage.Geometry &&
|
||||||
config.GpuAccessor.QueryPrimitiveTopology() == InputTopology.Triangles &&
|
config.GpuAccessor.QueryPrimitiveTopology() == InputTopology.Triangles &&
|
||||||
@ -20,12 +20,12 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
return ShaderIdentification.None;
|
return ShaderIdentification.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsLayerPassthroughGeometryShader(Function[] functions, out int layerInputAttr)
|
private static bool IsLayerPassthroughGeometryShader(IReadOnlyList<Function> functions, out int layerInputAttr)
|
||||||
{
|
{
|
||||||
bool writesLayer = false;
|
bool writesLayer = false;
|
||||||
layerInputAttr = 0;
|
layerInputAttr = 0;
|
||||||
|
|
||||||
if (functions.Length != 1)
|
if (functions.Count != 1)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
|||||||
using Ryujinx.Graphics.Shader.StructuredIr;
|
using Ryujinx.Graphics.Shader.StructuredIr;
|
||||||
using Ryujinx.Graphics.Shader.Translation.Optimizations;
|
using Ryujinx.Graphics.Shader.Translation.Optimizations;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||||
|
|
||||||
@ -44,7 +45,14 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Function[] funcs = new Function[functions.Length];
|
List<Function> funcs = new List<Function>(functions.Length);
|
||||||
|
|
||||||
|
for (int i = 0; i < functions.Length; i++)
|
||||||
|
{
|
||||||
|
funcs.Add(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
HelperFunctionManager hfm = new HelperFunctionManager(funcs, config.Stage);
|
||||||
|
|
||||||
for (int i = 0; i < functions.Length; i++)
|
for (int i = 0; i < functions.Length; i++)
|
||||||
{
|
{
|
||||||
@ -71,7 +79,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
Ssa.Rename(cfg.Blocks);
|
Ssa.Rename(cfg.Blocks);
|
||||||
|
|
||||||
Optimizer.RunPass(cfg.Blocks, config);
|
Optimizer.RunPass(cfg.Blocks, config);
|
||||||
Rewriter.RunPass(cfg.Blocks, config);
|
Rewriter.RunPass(hfm, cfg.Blocks, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
funcs[i] = new Function(cfg.Blocks, $"fun{i}", false, inArgumentsCount, outArgumentsCount);
|
funcs[i] = new Function(cfg.Blocks, $"fun{i}", false, inArgumentsCount, outArgumentsCount);
|
||||||
|
@ -155,6 +155,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
{
|
{
|
||||||
other._config.MergeOutputUserAttributes(_config.UsedOutputAttributes, Enumerable.Empty<int>());
|
other._config.MergeOutputUserAttributes(_config.UsedOutputAttributes, Enumerable.Empty<int>());
|
||||||
|
|
||||||
|
// We need to share the resource manager since both shaders accesses the same constant buffers.
|
||||||
|
other._config.ResourceManager = _config.ResourceManager;
|
||||||
|
|
||||||
FunctionCode[] otherCode = EmitShader(other._program, other._config, initializeOutputs: true, out int aStart);
|
FunctionCode[] otherCode = EmitShader(other._program, other._config, initializeOutputs: true, out int aStart);
|
||||||
|
|
||||||
code = Combine(otherCode, code, aStart);
|
code = Combine(otherCode, code, aStart);
|
||||||
|
@ -96,8 +96,6 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
{
|
{
|
||||||
var originalInfo = view.Info;
|
var originalInfo = view.Info;
|
||||||
|
|
||||||
var swapRB = originalInfo.Format.IsBgr() && originalInfo.SwizzleR == SwizzleComponent.Red;
|
|
||||||
|
|
||||||
var info = new TextureCreateInfo(
|
var info = new TextureCreateInfo(
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
@ -110,9 +108,9 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
originalInfo.Format,
|
originalInfo.Format,
|
||||||
originalInfo.DepthStencilMode,
|
originalInfo.DepthStencilMode,
|
||||||
originalInfo.Target,
|
originalInfo.Target,
|
||||||
swapRB ? originalInfo.SwizzleB : originalInfo.SwizzleR,
|
originalInfo.SwizzleR,
|
||||||
originalInfo.SwizzleG,
|
originalInfo.SwizzleG,
|
||||||
swapRB ? originalInfo.SwizzleR : originalInfo.SwizzleB,
|
originalInfo.SwizzleB,
|
||||||
originalInfo.SwizzleA);
|
originalInfo.SwizzleA);
|
||||||
_intermediaryTexture?.Dispose();
|
_intermediaryTexture?.Dispose();
|
||||||
_intermediaryTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView;
|
_intermediaryTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView;
|
||||||
@ -155,7 +153,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
|
|
||||||
var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize);
|
var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize);
|
||||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) });
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) });
|
||||||
_pipeline.SetImage(0, _intermediaryTexture, GAL.Format.R8G8B8A8Unorm);
|
_pipeline.SetImage(0, _intermediaryTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
_pipeline.ComputeBarrier();
|
_pipeline.ComputeBarrier();
|
||||||
|
|
||||||
|
@ -56,28 +56,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
if (_texture == null || _texture.Width != view.Width || _texture.Height != view.Height)
|
if (_texture == null || _texture.Width != view.Width || _texture.Height != view.Height)
|
||||||
{
|
{
|
||||||
_texture?.Dispose();
|
_texture?.Dispose();
|
||||||
|
_texture = _renderer.CreateTexture(view.Info, view.ScaleFactor) as TextureView;
|
||||||
var info = view.Info;
|
|
||||||
|
|
||||||
if (view.Info.Format.IsBgr())
|
|
||||||
{
|
|
||||||
info = new TextureCreateInfo(info.Width,
|
|
||||||
info.Height,
|
|
||||||
info.Depth,
|
|
||||||
info.Levels,
|
|
||||||
info.Samples,
|
|
||||||
info.BlockWidth,
|
|
||||||
info.BlockHeight,
|
|
||||||
info.BytesPerPixel,
|
|
||||||
info.Format,
|
|
||||||
info.DepthStencilMode,
|
|
||||||
info.Target,
|
|
||||||
info.SwizzleB,
|
|
||||||
info.SwizzleG,
|
|
||||||
info.SwizzleR,
|
|
||||||
info.SwizzleA);
|
|
||||||
}
|
|
||||||
_texture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_pipeline.SetCommandBuffer(cbs);
|
_pipeline.SetCommandBuffer(cbs);
|
||||||
@ -96,7 +75,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize);
|
var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize);
|
||||||
var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize);
|
var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize);
|
||||||
|
|
||||||
_pipeline.SetImage(0, _texture, GAL.Format.R8G8B8A8Unorm);
|
_pipeline.SetImage(0, _texture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
|
|
||||||
_renderer.BufferManager.Delete(bufferHandle);
|
_renderer.BufferManager.Delete(bufferHandle);
|
||||||
|
@ -4,7 +4,6 @@ using Ryujinx.Graphics.Shader;
|
|||||||
using Ryujinx.Graphics.Shader.Translation;
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
using Format = Ryujinx.Graphics.GAL.Format;
|
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan.Effects
|
namespace Ryujinx.Graphics.Vulkan.Effects
|
||||||
{
|
{
|
||||||
@ -149,7 +148,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
Format.R8G8Unorm,
|
GAL.Format.R8G8Unorm,
|
||||||
DepthStencilMode.Depth,
|
DepthStencilMode.Depth,
|
||||||
Target.Texture2D,
|
Target.Texture2D,
|
||||||
SwizzleComponent.Red,
|
SwizzleComponent.Red,
|
||||||
@ -165,7 +164,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
Format.R8Unorm,
|
GAL.Format.R8Unorm,
|
||||||
DepthStencilMode.Depth,
|
DepthStencilMode.Depth,
|
||||||
Target.Texture2D,
|
Target.Texture2D,
|
||||||
SwizzleComponent.Red,
|
SwizzleComponent.Red,
|
||||||
@ -192,30 +191,9 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
_edgeOutputTexture?.Dispose();
|
_edgeOutputTexture?.Dispose();
|
||||||
_blendOutputTexture?.Dispose();
|
_blendOutputTexture?.Dispose();
|
||||||
|
|
||||||
var info = view.Info;
|
_outputTexture = _renderer.CreateTexture(view.Info, view.ScaleFactor) as TextureView;
|
||||||
|
_edgeOutputTexture = _renderer.CreateTexture(view.Info, view.ScaleFactor) as TextureView;
|
||||||
if (view.Info.Format.IsBgr())
|
_blendOutputTexture = _renderer.CreateTexture(view.Info, view.ScaleFactor) as TextureView;
|
||||||
{
|
|
||||||
info = new TextureCreateInfo(info.Width,
|
|
||||||
info.Height,
|
|
||||||
info.Depth,
|
|
||||||
info.Levels,
|
|
||||||
info.Samples,
|
|
||||||
info.BlockWidth,
|
|
||||||
info.BlockHeight,
|
|
||||||
info.BytesPerPixel,
|
|
||||||
info.Format,
|
|
||||||
info.DepthStencilMode,
|
|
||||||
info.Target,
|
|
||||||
info.SwizzleB,
|
|
||||||
info.SwizzleG,
|
|
||||||
info.SwizzleR,
|
|
||||||
info.SwizzleA);
|
|
||||||
}
|
|
||||||
|
|
||||||
_outputTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView;
|
|
||||||
_edgeOutputTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView;
|
|
||||||
_blendOutputTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_pipeline.SetCommandBuffer(cbs);
|
_pipeline.SetCommandBuffer(cbs);
|
||||||
@ -240,7 +218,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
_renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer);
|
_renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer);
|
||||||
var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize);
|
var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize);
|
||||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) });
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) });
|
||||||
_pipeline.SetImage(0, _edgeOutputTexture, GAL.Format.R8G8B8A8Unorm);
|
_pipeline.SetImage(0, _edgeOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
_pipeline.ComputeBarrier();
|
_pipeline.ComputeBarrier();
|
||||||
|
|
||||||
@ -250,7 +228,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _edgeOutputTexture, _samplerLinear);
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _edgeOutputTexture, _samplerLinear);
|
||||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _areaTexture, _samplerLinear);
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _areaTexture, _samplerLinear);
|
||||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 4, _searchTexture, _samplerLinear);
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 4, _searchTexture, _samplerLinear);
|
||||||
_pipeline.SetImage(0, _blendOutputTexture, GAL.Format.R8G8B8A8Unorm);
|
_pipeline.SetImage(0, _blendOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
_pipeline.ComputeBarrier();
|
_pipeline.ComputeBarrier();
|
||||||
|
|
||||||
@ -259,7 +237,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
|||||||
_pipeline.Specialize(_specConstants);
|
_pipeline.Specialize(_specConstants);
|
||||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _blendOutputTexture, _samplerLinear);
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _blendOutputTexture, _samplerLinear);
|
||||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear);
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear);
|
||||||
_pipeline.SetImage(0, _outputTexture, GAL.Format.R8G8B8A8Unorm);
|
_pipeline.SetImage(0, _outputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
_pipeline.ComputeBarrier();
|
_pipeline.ComputeBarrier();
|
||||||
|
|
||||||
|
@ -65,6 +65,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
return (formatFeatureFlags & flags) == flags;
|
return (formatFeatureFlags & flags) == flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool BufferFormatSupports(FormatFeatureFlags flags, VkFormat format)
|
||||||
|
{
|
||||||
|
_api.GetPhysicalDeviceFormatProperties(_physicalDevice, format, out var fp);
|
||||||
|
|
||||||
|
return (fp.BufferFeatures & flags) == flags;
|
||||||
|
}
|
||||||
|
|
||||||
public bool OptimalFormatSupports(FormatFeatureFlags flags, GAL.Format format)
|
public bool OptimalFormatSupports(FormatFeatureFlags flags, GAL.Format format)
|
||||||
{
|
{
|
||||||
var formatFeatureFlags = _optimalTable[(int)format];
|
var formatFeatureFlags = _optimalTable[(int)format];
|
||||||
|
@ -168,5 +168,233 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
return _table[(int)format];
|
return _table[(int)format];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Format ConvertRgba8SrgbToUnorm(Format format)
|
||||||
|
{
|
||||||
|
return format switch
|
||||||
|
{
|
||||||
|
Format.R8G8B8A8Srgb => Format.R8G8B8A8Unorm,
|
||||||
|
Format.B8G8R8A8Srgb => Format.B8G8R8A8Unorm,
|
||||||
|
_ => format
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int GetAttributeFormatSize(VkFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case VkFormat.R8Unorm:
|
||||||
|
case VkFormat.R8SNorm:
|
||||||
|
case VkFormat.R8Uint:
|
||||||
|
case VkFormat.R8Sint:
|
||||||
|
case VkFormat.R8Uscaled:
|
||||||
|
case VkFormat.R8Sscaled:
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
case VkFormat.R8G8Unorm:
|
||||||
|
case VkFormat.R8G8SNorm:
|
||||||
|
case VkFormat.R8G8Uint:
|
||||||
|
case VkFormat.R8G8Sint:
|
||||||
|
case VkFormat.R8G8Uscaled:
|
||||||
|
case VkFormat.R8G8Sscaled:
|
||||||
|
case VkFormat.R16Sfloat:
|
||||||
|
case VkFormat.R16Unorm:
|
||||||
|
case VkFormat.R16SNorm:
|
||||||
|
case VkFormat.R16Uint:
|
||||||
|
case VkFormat.R16Sint:
|
||||||
|
case VkFormat.R16Uscaled:
|
||||||
|
case VkFormat.R16Sscaled:
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case VkFormat.R8G8B8Unorm:
|
||||||
|
case VkFormat.R8G8B8SNorm:
|
||||||
|
case VkFormat.R8G8B8Uint:
|
||||||
|
case VkFormat.R8G8B8Sint:
|
||||||
|
case VkFormat.R8G8B8Uscaled:
|
||||||
|
case VkFormat.R8G8B8Sscaled:
|
||||||
|
return 3;
|
||||||
|
|
||||||
|
case VkFormat.R8G8B8A8Unorm:
|
||||||
|
case VkFormat.R8G8B8A8SNorm:
|
||||||
|
case VkFormat.R8G8B8A8Uint:
|
||||||
|
case VkFormat.R8G8B8A8Sint:
|
||||||
|
case VkFormat.R8G8B8A8Srgb:
|
||||||
|
case VkFormat.R8G8B8A8Uscaled:
|
||||||
|
case VkFormat.R8G8B8A8Sscaled:
|
||||||
|
case VkFormat.B8G8R8A8Unorm:
|
||||||
|
case VkFormat.B8G8R8A8Srgb:
|
||||||
|
case VkFormat.R16G16Sfloat:
|
||||||
|
case VkFormat.R16G16Unorm:
|
||||||
|
case VkFormat.R16G16SNorm:
|
||||||
|
case VkFormat.R16G16Uint:
|
||||||
|
case VkFormat.R16G16Sint:
|
||||||
|
case VkFormat.R16G16Uscaled:
|
||||||
|
case VkFormat.R16G16Sscaled:
|
||||||
|
case VkFormat.R32Sfloat:
|
||||||
|
case VkFormat.R32Uint:
|
||||||
|
case VkFormat.R32Sint:
|
||||||
|
case VkFormat.A2B10G10R10UnormPack32:
|
||||||
|
case VkFormat.A2B10G10R10UintPack32:
|
||||||
|
case VkFormat.B10G11R11UfloatPack32:
|
||||||
|
case VkFormat.E5B9G9R9UfloatPack32:
|
||||||
|
case VkFormat.A2B10G10R10SNormPack32:
|
||||||
|
case VkFormat.A2B10G10R10SintPack32:
|
||||||
|
case VkFormat.A2B10G10R10UscaledPack32:
|
||||||
|
case VkFormat.A2B10G10R10SscaledPack32:
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case VkFormat.R16G16B16Sfloat:
|
||||||
|
case VkFormat.R16G16B16Unorm:
|
||||||
|
case VkFormat.R16G16B16SNorm:
|
||||||
|
case VkFormat.R16G16B16Uint:
|
||||||
|
case VkFormat.R16G16B16Sint:
|
||||||
|
case VkFormat.R16G16B16Uscaled:
|
||||||
|
case VkFormat.R16G16B16Sscaled:
|
||||||
|
return 6;
|
||||||
|
|
||||||
|
case VkFormat.R16G16B16A16Sfloat:
|
||||||
|
case VkFormat.R16G16B16A16Unorm:
|
||||||
|
case VkFormat.R16G16B16A16SNorm:
|
||||||
|
case VkFormat.R16G16B16A16Uint:
|
||||||
|
case VkFormat.R16G16B16A16Sint:
|
||||||
|
case VkFormat.R16G16B16A16Uscaled:
|
||||||
|
case VkFormat.R16G16B16A16Sscaled:
|
||||||
|
case VkFormat.R32G32Sfloat:
|
||||||
|
case VkFormat.R32G32Uint:
|
||||||
|
case VkFormat.R32G32Sint:
|
||||||
|
return 8;
|
||||||
|
|
||||||
|
case VkFormat.R32G32B32Sfloat:
|
||||||
|
case VkFormat.R32G32B32Uint:
|
||||||
|
case VkFormat.R32G32B32Sint:
|
||||||
|
return 12;
|
||||||
|
|
||||||
|
case VkFormat.R32G32B32A32Sfloat:
|
||||||
|
case VkFormat.R32G32B32A32Uint:
|
||||||
|
case VkFormat.R32G32B32A32Sint:
|
||||||
|
return 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static VkFormat DropLastComponent(VkFormat format)
|
||||||
|
{
|
||||||
|
switch (format)
|
||||||
|
{
|
||||||
|
case VkFormat.R8G8Unorm:
|
||||||
|
return VkFormat.R8Unorm;
|
||||||
|
case VkFormat.R8G8SNorm:
|
||||||
|
return VkFormat.R8SNorm;
|
||||||
|
case VkFormat.R8G8Uint:
|
||||||
|
return VkFormat.R8Uint;
|
||||||
|
case VkFormat.R8G8Sint:
|
||||||
|
return VkFormat.R8Sint;
|
||||||
|
case VkFormat.R8G8Uscaled:
|
||||||
|
return VkFormat.R8Uscaled;
|
||||||
|
case VkFormat.R8G8Sscaled:
|
||||||
|
return VkFormat.R8Sscaled;
|
||||||
|
|
||||||
|
case VkFormat.R8G8B8Unorm:
|
||||||
|
return VkFormat.R8G8Unorm;
|
||||||
|
case VkFormat.R8G8B8SNorm:
|
||||||
|
return VkFormat.R8G8SNorm;
|
||||||
|
case VkFormat.R8G8B8Uint:
|
||||||
|
return VkFormat.R8G8Uint;
|
||||||
|
case VkFormat.R8G8B8Sint:
|
||||||
|
return VkFormat.R8G8Sint;
|
||||||
|
case VkFormat.R8G8B8Uscaled:
|
||||||
|
return VkFormat.R8G8Uscaled;
|
||||||
|
case VkFormat.R8G8B8Sscaled:
|
||||||
|
return VkFormat.R8G8Sscaled;
|
||||||
|
|
||||||
|
case VkFormat.R8G8B8A8Unorm:
|
||||||
|
return VkFormat.R8G8B8Unorm;
|
||||||
|
case VkFormat.R8G8B8A8SNorm:
|
||||||
|
return VkFormat.R8G8B8SNorm;
|
||||||
|
case VkFormat.R8G8B8A8Uint:
|
||||||
|
return VkFormat.R8G8B8Uint;
|
||||||
|
case VkFormat.R8G8B8A8Sint:
|
||||||
|
return VkFormat.R8G8B8Sint;
|
||||||
|
case VkFormat.R8G8B8A8Srgb:
|
||||||
|
return VkFormat.R8G8B8Srgb;
|
||||||
|
case VkFormat.R8G8B8A8Uscaled:
|
||||||
|
return VkFormat.R8G8B8Uscaled;
|
||||||
|
case VkFormat.R8G8B8A8Sscaled:
|
||||||
|
return VkFormat.R8G8B8Sscaled;
|
||||||
|
case VkFormat.B8G8R8A8Unorm:
|
||||||
|
return VkFormat.B8G8R8Unorm;
|
||||||
|
case VkFormat.B8G8R8A8Srgb:
|
||||||
|
return VkFormat.B8G8R8Srgb;
|
||||||
|
|
||||||
|
case VkFormat.R16G16Sfloat:
|
||||||
|
return VkFormat.R16Sfloat;
|
||||||
|
case VkFormat.R16G16Unorm:
|
||||||
|
return VkFormat.R16Unorm;
|
||||||
|
case VkFormat.R16G16SNorm:
|
||||||
|
return VkFormat.R16SNorm;
|
||||||
|
case VkFormat.R16G16Uint:
|
||||||
|
return VkFormat.R16Uint;
|
||||||
|
case VkFormat.R16G16Sint:
|
||||||
|
return VkFormat.R16Sint;
|
||||||
|
case VkFormat.R16G16Uscaled:
|
||||||
|
return VkFormat.R16Uscaled;
|
||||||
|
case VkFormat.R16G16Sscaled:
|
||||||
|
return VkFormat.R16Sscaled;
|
||||||
|
|
||||||
|
case VkFormat.R16G16B16Sfloat:
|
||||||
|
return VkFormat.R16G16Sfloat;
|
||||||
|
case VkFormat.R16G16B16Unorm:
|
||||||
|
return VkFormat.R16G16Unorm;
|
||||||
|
case VkFormat.R16G16B16SNorm:
|
||||||
|
return VkFormat.R16G16SNorm;
|
||||||
|
case VkFormat.R16G16B16Uint:
|
||||||
|
return VkFormat.R16G16Uint;
|
||||||
|
case VkFormat.R16G16B16Sint:
|
||||||
|
return VkFormat.R16G16Sint;
|
||||||
|
case VkFormat.R16G16B16Uscaled:
|
||||||
|
return VkFormat.R16G16Uscaled;
|
||||||
|
case VkFormat.R16G16B16Sscaled:
|
||||||
|
return VkFormat.R16G16Sscaled;
|
||||||
|
|
||||||
|
case VkFormat.R16G16B16A16Sfloat:
|
||||||
|
return VkFormat.R16G16B16Sfloat;
|
||||||
|
case VkFormat.R16G16B16A16Unorm:
|
||||||
|
return VkFormat.R16G16B16Unorm;
|
||||||
|
case VkFormat.R16G16B16A16SNorm:
|
||||||
|
return VkFormat.R16G16B16SNorm;
|
||||||
|
case VkFormat.R16G16B16A16Uint:
|
||||||
|
return VkFormat.R16G16B16Uint;
|
||||||
|
case VkFormat.R16G16B16A16Sint:
|
||||||
|
return VkFormat.R16G16B16Sint;
|
||||||
|
case VkFormat.R16G16B16A16Uscaled:
|
||||||
|
return VkFormat.R16G16B16Uscaled;
|
||||||
|
case VkFormat.R16G16B16A16Sscaled:
|
||||||
|
return VkFormat.R16G16B16Sscaled;
|
||||||
|
|
||||||
|
case VkFormat.R32G32Sfloat:
|
||||||
|
return VkFormat.R32Sfloat;
|
||||||
|
case VkFormat.R32G32Uint:
|
||||||
|
return VkFormat.R32Uint;
|
||||||
|
case VkFormat.R32G32Sint:
|
||||||
|
return VkFormat.R32Sint;
|
||||||
|
|
||||||
|
case VkFormat.R32G32B32Sfloat:
|
||||||
|
return VkFormat.R32G32Sfloat;
|
||||||
|
case VkFormat.R32G32B32Uint:
|
||||||
|
return VkFormat.R32G32Uint;
|
||||||
|
case VkFormat.R32G32B32Sint:
|
||||||
|
return VkFormat.R32G32Sint;
|
||||||
|
|
||||||
|
case VkFormat.R32G32B32A32Sfloat:
|
||||||
|
return VkFormat.R32G32B32Sfloat;
|
||||||
|
case VkFormat.R32G32B32A32Uint:
|
||||||
|
return VkFormat.R32G32B32Uint;
|
||||||
|
case VkFormat.R32G32B32A32Sint:
|
||||||
|
return VkFormat.R32G32B32Sint;
|
||||||
|
}
|
||||||
|
|
||||||
|
return format;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public uint[] AttachmentSamples { get; }
|
public uint[] AttachmentSamples { get; }
|
||||||
public VkFormat[] AttachmentFormats { get; }
|
public VkFormat[] AttachmentFormats { get; }
|
||||||
public int[] AttachmentIndices { get; }
|
public int[] AttachmentIndices { get; }
|
||||||
|
public uint AttachmentIntegerFormatMask { get; }
|
||||||
|
|
||||||
public int AttachmentsCount { get; }
|
public int AttachmentsCount { get; }
|
||||||
public int MaxColorAttachmentIndex => AttachmentIndices.Length > 0 ? AttachmentIndices[AttachmentIndices.Length - 1] : -1;
|
public int MaxColorAttachmentIndex => AttachmentIndices.Length > 0 ? AttachmentIndices[AttachmentIndices.Length - 1] : -1;
|
||||||
@ -74,6 +75,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
int bindIndex = 0;
|
int bindIndex = 0;
|
||||||
|
uint attachmentIntegerFormatMask = 0;
|
||||||
|
|
||||||
foreach (ITexture color in colors)
|
foreach (ITexture color in colors)
|
||||||
{
|
{
|
||||||
@ -89,6 +91,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
AttachmentFormats[index] = texture.VkFormat;
|
AttachmentFormats[index] = texture.VkFormat;
|
||||||
AttachmentIndices[index] = bindIndex;
|
AttachmentIndices[index] = bindIndex;
|
||||||
|
|
||||||
|
if (texture.Info.Format.IsInteger())
|
||||||
|
{
|
||||||
|
attachmentIntegerFormatMask |= 1u << bindIndex;
|
||||||
|
}
|
||||||
|
|
||||||
width = Math.Min(width, (uint)texture.Width);
|
width = Math.Min(width, (uint)texture.Width);
|
||||||
height = Math.Min(height, (uint)texture.Height);
|
height = Math.Min(height, (uint)texture.Height);
|
||||||
layers = Math.Min(layers, (uint)texture.Layers);
|
layers = Math.Min(layers, (uint)texture.Layers);
|
||||||
@ -102,6 +109,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
bindIndex++;
|
bindIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AttachmentIntegerFormatMask = attachmentIntegerFormatMask;
|
||||||
|
|
||||||
if (depthStencil is TextureView dsTexture && dsTexture.Valid)
|
if (depthStencil is TextureView dsTexture && dsTexture.Valid)
|
||||||
{
|
{
|
||||||
_attachments[count - 1] = dsTexture.GetImageViewForAttachment();
|
_attachments[count - 1] = dsTexture.GetImageViewForAttachment();
|
||||||
|
@ -28,6 +28,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public readonly bool SupportsSubgroupSizeControl;
|
public readonly bool SupportsSubgroupSizeControl;
|
||||||
public readonly bool SupportsShaderInt8;
|
public readonly bool SupportsShaderInt8;
|
||||||
public readonly bool SupportsShaderStencilExport;
|
public readonly bool SupportsShaderStencilExport;
|
||||||
|
public readonly bool SupportsShaderStorageImageMultisample;
|
||||||
public readonly bool SupportsConditionalRendering;
|
public readonly bool SupportsConditionalRendering;
|
||||||
public readonly bool SupportsExtendedDynamicState;
|
public readonly bool SupportsExtendedDynamicState;
|
||||||
public readonly bool SupportsMultiView;
|
public readonly bool SupportsMultiView;
|
||||||
@ -42,6 +43,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public readonly bool SupportsGeometryShader;
|
public readonly bool SupportsGeometryShader;
|
||||||
public readonly bool SupportsViewportArray2;
|
public readonly bool SupportsViewportArray2;
|
||||||
public readonly bool SupportsHostImportedMemory;
|
public readonly bool SupportsHostImportedMemory;
|
||||||
|
public readonly bool SupportsDepthClipControl;
|
||||||
public readonly uint MinSubgroupSize;
|
public readonly uint MinSubgroupSize;
|
||||||
public readonly uint MaxSubgroupSize;
|
public readonly uint MaxSubgroupSize;
|
||||||
public readonly ShaderStageFlags RequiredSubgroupSizeStages;
|
public readonly ShaderStageFlags RequiredSubgroupSizeStages;
|
||||||
@ -63,6 +65,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
bool supportsSubgroupSizeControl,
|
bool supportsSubgroupSizeControl,
|
||||||
bool supportsShaderInt8,
|
bool supportsShaderInt8,
|
||||||
bool supportsShaderStencilExport,
|
bool supportsShaderStencilExport,
|
||||||
|
bool supportsShaderStorageImageMultisample,
|
||||||
bool supportsConditionalRendering,
|
bool supportsConditionalRendering,
|
||||||
bool supportsExtendedDynamicState,
|
bool supportsExtendedDynamicState,
|
||||||
bool supportsMultiView,
|
bool supportsMultiView,
|
||||||
@ -77,6 +80,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
bool supportsGeometryShader,
|
bool supportsGeometryShader,
|
||||||
bool supportsViewportArray2,
|
bool supportsViewportArray2,
|
||||||
bool supportsHostImportedMemory,
|
bool supportsHostImportedMemory,
|
||||||
|
bool supportsDepthClipControl,
|
||||||
uint minSubgroupSize,
|
uint minSubgroupSize,
|
||||||
uint maxSubgroupSize,
|
uint maxSubgroupSize,
|
||||||
ShaderStageFlags requiredSubgroupSizeStages,
|
ShaderStageFlags requiredSubgroupSizeStages,
|
||||||
@ -97,6 +101,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SupportsSubgroupSizeControl = supportsSubgroupSizeControl;
|
SupportsSubgroupSizeControl = supportsSubgroupSizeControl;
|
||||||
SupportsShaderInt8 = supportsShaderInt8;
|
SupportsShaderInt8 = supportsShaderInt8;
|
||||||
SupportsShaderStencilExport = supportsShaderStencilExport;
|
SupportsShaderStencilExport = supportsShaderStencilExport;
|
||||||
|
SupportsShaderStorageImageMultisample = supportsShaderStorageImageMultisample;
|
||||||
SupportsConditionalRendering = supportsConditionalRendering;
|
SupportsConditionalRendering = supportsConditionalRendering;
|
||||||
SupportsExtendedDynamicState = supportsExtendedDynamicState;
|
SupportsExtendedDynamicState = supportsExtendedDynamicState;
|
||||||
SupportsMultiView = supportsMultiView;
|
SupportsMultiView = supportsMultiView;
|
||||||
@ -111,6 +116,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SupportsGeometryShader = supportsGeometryShader;
|
SupportsGeometryShader = supportsGeometryShader;
|
||||||
SupportsViewportArray2 = supportsViewportArray2;
|
SupportsViewportArray2 = supportsViewportArray2;
|
||||||
SupportsHostImportedMemory = supportsHostImportedMemory;
|
SupportsHostImportedMemory = supportsHostImportedMemory;
|
||||||
|
SupportsDepthClipControl = supportsDepthClipControl;
|
||||||
MinSubgroupSize = minSubgroupSize;
|
MinSubgroupSize = minSubgroupSize;
|
||||||
MaxSubgroupSize = maxSubgroupSize;
|
MaxSubgroupSize = maxSubgroupSize;
|
||||||
RequiredSubgroupSizeStages = requiredSubgroupSizeStages;
|
RequiredSubgroupSizeStages = requiredSubgroupSizeStages;
|
||||||
|
@ -80,6 +80,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
private PipelineColorBlendAttachmentState[] _storedBlend;
|
private PipelineColorBlendAttachmentState[] _storedBlend;
|
||||||
|
|
||||||
|
private ulong _drawCountSinceBarrier;
|
||||||
public ulong DrawCount { get; private set; }
|
public ulong DrawCount { get; private set; }
|
||||||
public bool RenderPassActive { get; private set; }
|
public bool RenderPassActive { get; private set; }
|
||||||
|
|
||||||
@ -133,6 +134,18 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public unsafe void Barrier()
|
public unsafe void Barrier()
|
||||||
{
|
{
|
||||||
|
if (_drawCountSinceBarrier != DrawCount)
|
||||||
|
{
|
||||||
|
_drawCountSinceBarrier = DrawCount;
|
||||||
|
|
||||||
|
// Barriers apparently have no effect inside a render pass on MoltenVK.
|
||||||
|
// As a workaround, end the render pass.
|
||||||
|
if (Gd.IsMoltenVk)
|
||||||
|
{
|
||||||
|
EndRenderPass();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MemoryBarrier memoryBarrier = new MemoryBarrier()
|
MemoryBarrier memoryBarrier = new MemoryBarrier()
|
||||||
{
|
{
|
||||||
SType = StructureType.MemoryBarrier,
|
SType = StructureType.MemoryBarrier,
|
||||||
@ -345,7 +358,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance)
|
public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance)
|
||||||
{
|
{
|
||||||
if (!_program.IsLinked)
|
if (!_program.IsLinked || vertexCount == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -409,7 +422,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance)
|
public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance)
|
||||||
{
|
{
|
||||||
if (!_program.IsLinked)
|
if (!_program.IsLinked || indexCount == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -551,7 +564,6 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
(uint)maxDrawCount,
|
(uint)maxDrawCount,
|
||||||
(uint)stride);
|
(uint)stride);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -813,8 +825,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public void SetDepthMode(DepthMode mode)
|
public void SetDepthMode(DepthMode mode)
|
||||||
{
|
{
|
||||||
// Currently this is emulated on the shader, because Vulkan had no support for changing the depth mode.
|
bool oldMode = _newState.DepthMode;
|
||||||
// In the future, we may want to use the VK_EXT_depth_clip_control extension to change it here.
|
_newState.DepthMode = mode == DepthMode.MinusOneToOne;
|
||||||
|
if (_newState.DepthMode != oldMode)
|
||||||
|
{
|
||||||
|
SignalStateChange();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetDepthTest(DepthTestDescriptor depthTest)
|
public void SetDepthTest(DepthTestDescriptor depthTest)
|
||||||
@ -1471,6 +1487,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
var dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan();
|
var dstAttachmentFormats = _newState.Internal.AttachmentFormats.AsSpan();
|
||||||
FramebufferParams.AttachmentFormats.CopyTo(dstAttachmentFormats);
|
FramebufferParams.AttachmentFormats.CopyTo(dstAttachmentFormats);
|
||||||
|
_newState.Internal.AttachmentIntegerFormatMask = FramebufferParams.AttachmentIntegerFormatMask;
|
||||||
|
|
||||||
for (int i = FramebufferParams.AttachmentFormats.Length; i < dstAttachmentFormats.Length; i++)
|
for (int i = FramebufferParams.AttachmentFormats.Length; i < dstAttachmentFormats.Length; i++)
|
||||||
{
|
{
|
||||||
|
@ -165,6 +165,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
pipeline.DepthTestEnable = state.DepthTest.TestEnable;
|
pipeline.DepthTestEnable = state.DepthTest.TestEnable;
|
||||||
pipeline.DepthWriteEnable = state.DepthTest.WriteEnable;
|
pipeline.DepthWriteEnable = state.DepthTest.WriteEnable;
|
||||||
pipeline.DepthCompareOp = state.DepthTest.Func.Convert();
|
pipeline.DepthCompareOp = state.DepthTest.Func.Convert();
|
||||||
|
pipeline.DepthMode = state.DepthMode == DepthMode.MinusOneToOne;
|
||||||
|
|
||||||
pipeline.FrontFace = state.FrontFace.Convert();
|
pipeline.FrontFace = state.FrontFace.Convert();
|
||||||
|
|
||||||
@ -294,6 +295,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
int attachmentCount = 0;
|
int attachmentCount = 0;
|
||||||
int maxColorAttachmentIndex = -1;
|
int maxColorAttachmentIndex = -1;
|
||||||
|
uint attachmentIntegerFormatMask = 0;
|
||||||
|
|
||||||
for (int i = 0; i < Constants.MaxRenderTargets; i++)
|
for (int i = 0; i < Constants.MaxRenderTargets; i++)
|
||||||
{
|
{
|
||||||
@ -301,6 +303,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]);
|
pipeline.Internal.AttachmentFormats[attachmentCount++] = gd.FormatCapabilities.ConvertToVkFormat(state.AttachmentFormats[i]);
|
||||||
maxColorAttachmentIndex = i;
|
maxColorAttachmentIndex = i;
|
||||||
|
|
||||||
|
if (state.AttachmentFormats[i].IsInteger())
|
||||||
|
{
|
||||||
|
attachmentIntegerFormatMask |= 1u << i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,6 +318,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
pipeline.ColorBlendAttachmentStateCount = (uint)(maxColorAttachmentIndex + 1);
|
pipeline.ColorBlendAttachmentStateCount = (uint)(maxColorAttachmentIndex + 1);
|
||||||
pipeline.VertexAttributeDescriptionsCount = (uint)Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount);
|
pipeline.VertexAttributeDescriptionsCount = (uint)Math.Min(Constants.MaxVertexAttributes, state.VertexAttribCount);
|
||||||
|
pipeline.Internal.AttachmentIntegerFormatMask = attachmentIntegerFormatMask;
|
||||||
|
|
||||||
return pipeline;
|
return pipeline;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
using Silk.NET.Vulkan;
|
using Ryujinx.Common.Memory;
|
||||||
|
using Silk.NET.Vulkan;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Numerics;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.Vulkan
|
namespace Ryujinx.Graphics.Vulkan
|
||||||
{
|
{
|
||||||
@ -303,11 +305,19 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFCF) | ((ulong)value << 4);
|
set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFCF) | ((ulong)value << 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool DepthMode
|
||||||
|
{
|
||||||
|
get => ((Internal.Id9 >> 6) & 0x1) != 0UL;
|
||||||
|
set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6);
|
||||||
|
}
|
||||||
|
|
||||||
public NativeArray<PipelineShaderStageCreateInfo> Stages;
|
public NativeArray<PipelineShaderStageCreateInfo> Stages;
|
||||||
public NativeArray<PipelineShaderStageRequiredSubgroupSizeCreateInfoEXT> StageRequiredSubgroupSizes;
|
public NativeArray<PipelineShaderStageRequiredSubgroupSizeCreateInfoEXT> StageRequiredSubgroupSizes;
|
||||||
public PipelineLayout PipelineLayout;
|
public PipelineLayout PipelineLayout;
|
||||||
public SpecData SpecializationData;
|
public SpecData SpecializationData;
|
||||||
|
|
||||||
|
private Array32<VertexInputAttributeDescription> _vertexAttributeDescriptions2;
|
||||||
|
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
Stages = new NativeArray<PipelineShaderStageCreateInfo>(Constants.MaxShaderStages);
|
Stages = new NativeArray<PipelineShaderStageCreateInfo>(Constants.MaxShaderStages);
|
||||||
@ -328,6 +338,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
LineWidth = 1f;
|
LineWidth = 1f;
|
||||||
SamplesCount = 1;
|
SamplesCount = 1;
|
||||||
|
DepthMode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsafe Auto<DisposablePipeline> CreateComputePipeline(
|
public unsafe Auto<DisposablePipeline> CreateComputePipeline(
|
||||||
@ -400,7 +411,15 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
Pipeline pipelineHandle = default;
|
Pipeline pipelineHandle = default;
|
||||||
|
|
||||||
|
bool isMoltenVk = gd.IsMoltenVk;
|
||||||
|
|
||||||
|
if (isMoltenVk)
|
||||||
|
{
|
||||||
|
UpdateVertexAttributeDescriptions(gd);
|
||||||
|
}
|
||||||
|
|
||||||
fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions = &Internal.VertexAttributeDescriptions[0])
|
fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions = &Internal.VertexAttributeDescriptions[0])
|
||||||
|
fixed (VertexInputAttributeDescription* pVertexAttributeDescriptions2 = &_vertexAttributeDescriptions2[0])
|
||||||
fixed (VertexInputBindingDescription* pVertexBindingDescriptions = &Internal.VertexBindingDescriptions[0])
|
fixed (VertexInputBindingDescription* pVertexBindingDescriptions = &Internal.VertexBindingDescriptions[0])
|
||||||
fixed (Viewport* pViewports = &Internal.Viewports[0])
|
fixed (Viewport* pViewports = &Internal.Viewports[0])
|
||||||
fixed (Rect2D* pScissors = &Internal.Scissors[0])
|
fixed (Rect2D* pScissors = &Internal.Scissors[0])
|
||||||
@ -410,7 +429,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
{
|
{
|
||||||
SType = StructureType.PipelineVertexInputStateCreateInfo,
|
SType = StructureType.PipelineVertexInputStateCreateInfo,
|
||||||
VertexAttributeDescriptionCount = VertexAttributeDescriptionsCount,
|
VertexAttributeDescriptionCount = VertexAttributeDescriptionsCount,
|
||||||
PVertexAttributeDescriptions = pVertexAttributeDescriptions,
|
PVertexAttributeDescriptions = isMoltenVk ? pVertexAttributeDescriptions2 : pVertexAttributeDescriptions,
|
||||||
VertexBindingDescriptionCount = VertexBindingDescriptionsCount,
|
VertexBindingDescriptionCount = VertexBindingDescriptionsCount,
|
||||||
PVertexBindingDescriptions = pVertexBindingDescriptions
|
PVertexBindingDescriptions = pVertexBindingDescriptions
|
||||||
};
|
};
|
||||||
@ -471,6 +490,17 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
PScissors = pScissors
|
PScissors = pScissors
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (gd.Capabilities.SupportsDepthClipControl)
|
||||||
|
{
|
||||||
|
var viewportDepthClipControlState = new PipelineViewportDepthClipControlCreateInfoEXT()
|
||||||
|
{
|
||||||
|
SType = StructureType.PipelineViewportDepthClipControlCreateInfoExt,
|
||||||
|
NegativeOneToOne = DepthMode
|
||||||
|
};
|
||||||
|
|
||||||
|
viewportState.PNext = &viewportDepthClipControlState;
|
||||||
|
}
|
||||||
|
|
||||||
var multisampleState = new PipelineMultisampleStateCreateInfo
|
var multisampleState = new PipelineMultisampleStateCreateInfo
|
||||||
{
|
{
|
||||||
SType = StructureType.PipelineMultisampleStateCreateInfo,
|
SType = StructureType.PipelineMultisampleStateCreateInfo,
|
||||||
@ -513,6 +543,27 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
MaxDepthBounds = MaxDepthBounds
|
MaxDepthBounds = MaxDepthBounds
|
||||||
};
|
};
|
||||||
|
|
||||||
|
uint blendEnables = 0;
|
||||||
|
|
||||||
|
if (gd.IsMoltenVk && Internal.AttachmentIntegerFormatMask != 0)
|
||||||
|
{
|
||||||
|
// Blend can't be enabled for integer formats, so let's make sure it is disabled.
|
||||||
|
uint attachmentIntegerFormatMask = Internal.AttachmentIntegerFormatMask;
|
||||||
|
|
||||||
|
while (attachmentIntegerFormatMask != 0)
|
||||||
|
{
|
||||||
|
int i = BitOperations.TrailingZeroCount(attachmentIntegerFormatMask);
|
||||||
|
|
||||||
|
if (Internal.ColorBlendAttachmentState[i].BlendEnable)
|
||||||
|
{
|
||||||
|
blendEnables |= 1u << i;
|
||||||
|
}
|
||||||
|
|
||||||
|
Internal.ColorBlendAttachmentState[i].BlendEnable = false;
|
||||||
|
attachmentIntegerFormatMask &= ~(1u << i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var colorBlendState = new PipelineColorBlendStateCreateInfo()
|
var colorBlendState = new PipelineColorBlendStateCreateInfo()
|
||||||
{
|
{
|
||||||
SType = StructureType.PipelineColorBlendStateCreateInfo,
|
SType = StructureType.PipelineColorBlendStateCreateInfo,
|
||||||
@ -590,6 +641,15 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
};
|
};
|
||||||
|
|
||||||
gd.Api.CreateGraphicsPipelines(device, cache, 1, &pipelineCreateInfo, null, &pipelineHandle).ThrowOnError();
|
gd.Api.CreateGraphicsPipelines(device, cache, 1, &pipelineCreateInfo, null, &pipelineHandle).ThrowOnError();
|
||||||
|
|
||||||
|
// Restore previous blend enable values if we changed it.
|
||||||
|
while (blendEnables != 0)
|
||||||
|
{
|
||||||
|
int i = BitOperations.TrailingZeroCount(blendEnables);
|
||||||
|
|
||||||
|
Internal.ColorBlendAttachmentState[i].BlendEnable = true;
|
||||||
|
blendEnables &= ~(1u << i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pipeline = new Auto<DisposablePipeline>(new DisposablePipeline(gd.Api, device, pipelineHandle));
|
pipeline = new Auto<DisposablePipeline>(new DisposablePipeline(gd.Api, device, pipelineHandle));
|
||||||
@ -612,6 +672,62 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateVertexAttributeDescriptions(VulkanRenderer gd)
|
||||||
|
{
|
||||||
|
// Vertex attributes exceeding the stride are invalid.
|
||||||
|
// In metal, they cause glitches with the vertex shader fetching incorrect values.
|
||||||
|
// To work around this, we reduce the format to something that doesn't exceed the stride if possible.
|
||||||
|
// The assumption is that the exceeding components are not actually accessed on the shader.
|
||||||
|
|
||||||
|
for (int index = 0; index < VertexAttributeDescriptionsCount; index++)
|
||||||
|
{
|
||||||
|
var attribute = Internal.VertexAttributeDescriptions[index];
|
||||||
|
int vbIndex = GetVertexBufferIndex(attribute.Binding);
|
||||||
|
|
||||||
|
if (vbIndex >= 0)
|
||||||
|
{
|
||||||
|
ref var vb = ref Internal.VertexBindingDescriptions[vbIndex];
|
||||||
|
|
||||||
|
Format format = attribute.Format;
|
||||||
|
|
||||||
|
while (vb.Stride != 0 && attribute.Offset + FormatTable.GetAttributeFormatSize(format) > vb.Stride)
|
||||||
|
{
|
||||||
|
Format newFormat = FormatTable.DropLastComponent(format);
|
||||||
|
|
||||||
|
if (newFormat == format)
|
||||||
|
{
|
||||||
|
// That case means we failed to find a format that fits within the stride,
|
||||||
|
// so just restore the original format and give up.
|
||||||
|
format = attribute.Format;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
format = newFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attribute.Format != format && gd.FormatCapabilities.BufferFormatSupports(FormatFeatureFlags.VertexBufferBit, format))
|
||||||
|
{
|
||||||
|
attribute.Format = format;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_vertexAttributeDescriptions2[index] = attribute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetVertexBufferIndex(uint binding)
|
||||||
|
{
|
||||||
|
for (int index = 0; index < VertexBindingDescriptionsCount; index++)
|
||||||
|
{
|
||||||
|
if (Internal.VertexBindingDescriptions[index].Binding == binding)
|
||||||
|
{
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Stages.Dispose();
|
Stages.Dispose();
|
||||||
|
@ -35,6 +35,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public Array16<Rect2D> Scissors;
|
public Array16<Rect2D> Scissors;
|
||||||
public Array8<PipelineColorBlendAttachmentState> ColorBlendAttachmentState;
|
public Array8<PipelineColorBlendAttachmentState> ColorBlendAttachmentState;
|
||||||
public Array9<Format> AttachmentFormats;
|
public Array9<Format> AttachmentFormats;
|
||||||
|
public uint AttachmentIntegerFormatMask;
|
||||||
|
|
||||||
public override bool Equals(object obj)
|
public override bool Equals(object obj)
|
||||||
{
|
{
|
||||||
|
@ -80,7 +80,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples);
|
var sampleCountFlags = ConvertToSampleCountFlags(gd.Capabilities.SupportedSampleCounts, (uint)info.Samples);
|
||||||
|
|
||||||
var usage = GetImageUsageFromFormat(info.Format);
|
var usage = GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample);
|
||||||
|
|
||||||
var flags = ImageCreateFlags.CreateMutableFormatBit;
|
var flags = ImageCreateFlags.CreateMutableFormatBit;
|
||||||
|
|
||||||
@ -293,7 +293,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ImageUsageFlags GetImageUsageFromFormat(GAL.Format format)
|
public static ImageUsageFlags GetImageUsage(GAL.Format format, Target target, bool supportsMsStorage)
|
||||||
{
|
{
|
||||||
var usage = DefaultUsageFlags;
|
var usage = DefaultUsageFlags;
|
||||||
|
|
||||||
@ -306,7 +306,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
usage |= ImageUsageFlags.ColorAttachmentBit;
|
usage |= ImageUsageFlags.ColorAttachmentBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format.IsImageCompatible())
|
if (format.IsImageCompatible() && (supportsMsStorage || !target.IsMultisample()))
|
||||||
{
|
{
|
||||||
usage |= ImageUsageFlags.StorageBit;
|
usage |= ImageUsageFlags.StorageBit;
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
gd.Textures.Add(this);
|
gd.Textures.Add(this);
|
||||||
|
|
||||||
var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format);
|
var format = _gd.FormatCapabilities.ConvertToVkFormat(info.Format);
|
||||||
var usage = TextureStorage.GetImageUsageFromFormat(info.Format);
|
var usage = TextureStorage.GetImageUsage(info.Format, info.Target, gd.Capabilities.SupportsShaderStorageImageMultisample);
|
||||||
var levels = (uint)info.Levels;
|
var levels = (uint)info.Levels;
|
||||||
var layers = (uint)info.GetLayers();
|
var layers = (uint)info.GetLayers();
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
"VK_EXT_subgroup_size_control",
|
"VK_EXT_subgroup_size_control",
|
||||||
"VK_NV_geometry_shader_passthrough",
|
"VK_NV_geometry_shader_passthrough",
|
||||||
"VK_NV_viewport_array2",
|
"VK_NV_viewport_array2",
|
||||||
|
"VK_EXT_depth_clip_control",
|
||||||
"VK_KHR_portability_subset" // As per spec, we should enable this if present.
|
"VK_KHR_portability_subset" // As per spec, we should enable this if present.
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -345,6 +346,17 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
features2.PNext = &supportedFeaturesRobustness2;
|
features2.PNext = &supportedFeaturesRobustness2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PhysicalDeviceDepthClipControlFeaturesEXT supportedFeaturesDepthClipControl = new PhysicalDeviceDepthClipControlFeaturesEXT()
|
||||||
|
{
|
||||||
|
SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt,
|
||||||
|
PNext = features2.PNext
|
||||||
|
};
|
||||||
|
|
||||||
|
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_depth_clip_control"))
|
||||||
|
{
|
||||||
|
features2.PNext = &supportedFeaturesDepthClipControl;
|
||||||
|
}
|
||||||
|
|
||||||
api.GetPhysicalDeviceFeatures2(physicalDevice.PhysicalDevice, &features2);
|
api.GetPhysicalDeviceFeatures2(physicalDevice.PhysicalDevice, &features2);
|
||||||
|
|
||||||
var supportedFeatures = features2.Features;
|
var supportedFeatures = features2.Features;
|
||||||
@ -507,6 +519,21 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
pExtendedFeatures = &featuresCustomBorderColor;
|
pExtendedFeatures = &featuresCustomBorderColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PhysicalDeviceDepthClipControlFeaturesEXT featuresDepthClipControl;
|
||||||
|
|
||||||
|
if (physicalDevice.IsDeviceExtensionPresent("VK_EXT_depth_clip_control") &&
|
||||||
|
supportedFeaturesDepthClipControl.DepthClipControl)
|
||||||
|
{
|
||||||
|
featuresDepthClipControl = new PhysicalDeviceDepthClipControlFeaturesEXT()
|
||||||
|
{
|
||||||
|
SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt,
|
||||||
|
PNext = pExtendedFeatures,
|
||||||
|
DepthClipControl = true
|
||||||
|
};
|
||||||
|
|
||||||
|
pExtendedFeatures = &featuresDepthClipControl;
|
||||||
|
}
|
||||||
|
|
||||||
var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray();
|
var enabledExtensions = _requiredExtensions.Union(_desirableExtensions.Intersect(physicalDevice.DeviceExtensions)).ToArray();
|
||||||
|
|
||||||
IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];
|
IntPtr* ppEnabledExtensions = stackalloc IntPtr[enabledExtensions.Length];
|
||||||
|
@ -216,6 +216,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt
|
SType = StructureType.PhysicalDeviceCustomBorderColorFeaturesExt
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PhysicalDeviceDepthClipControlFeaturesEXT featuresDepthClipControl = new PhysicalDeviceDepthClipControlFeaturesEXT()
|
||||||
|
{
|
||||||
|
SType = StructureType.PhysicalDeviceDepthClipControlFeaturesExt
|
||||||
|
};
|
||||||
|
|
||||||
PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new PhysicalDevicePortabilitySubsetFeaturesKHR()
|
PhysicalDevicePortabilitySubsetFeaturesKHR featuresPortabilitySubset = new PhysicalDevicePortabilitySubsetFeaturesKHR()
|
||||||
{
|
{
|
||||||
SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr
|
SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr
|
||||||
@ -244,6 +249,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
features2.PNext = &featuresCustomBorderColor;
|
features2.PNext = &featuresCustomBorderColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool supportsDepthClipControl = _physicalDevice.IsDeviceExtensionPresent("VK_EXT_depth_clip_control");
|
||||||
|
|
||||||
|
if (supportsDepthClipControl)
|
||||||
|
{
|
||||||
|
featuresDepthClipControl.PNext = features2.PNext;
|
||||||
|
features2.PNext = &featuresDepthClipControl;
|
||||||
|
}
|
||||||
|
|
||||||
bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset");
|
bool usePortability = _physicalDevice.IsDeviceExtensionPresent("VK_KHR_portability_subset");
|
||||||
|
|
||||||
if (usePortability)
|
if (usePortability)
|
||||||
@ -295,6 +308,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
supportsSubgroupSizeControl,
|
supportsSubgroupSizeControl,
|
||||||
featuresShaderInt8.ShaderInt8,
|
featuresShaderInt8.ShaderInt8,
|
||||||
_physicalDevice.IsDeviceExtensionPresent("VK_EXT_shader_stencil_export"),
|
_physicalDevice.IsDeviceExtensionPresent("VK_EXT_shader_stencil_export"),
|
||||||
|
features2.Features.ShaderStorageImageMultisample,
|
||||||
_physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName),
|
_physicalDevice.IsDeviceExtensionPresent(ExtConditionalRendering.ExtensionName),
|
||||||
_physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName),
|
_physicalDevice.IsDeviceExtensionPresent(ExtExtendedDynamicState.ExtensionName),
|
||||||
features2.Features.MultiViewport,
|
features2.Features.MultiViewport,
|
||||||
@ -309,6 +323,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_physicalDevice.PhysicalDeviceFeatures.GeometryShader,
|
_physicalDevice.PhysicalDeviceFeatures.GeometryShader,
|
||||||
_physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"),
|
_physicalDevice.IsDeviceExtensionPresent("VK_NV_viewport_array2"),
|
||||||
_physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName),
|
_physicalDevice.IsDeviceExtensionPresent(ExtExternalMemoryHost.ExtensionName),
|
||||||
|
supportsDepthClipControl && featuresDepthClipControl.DepthClipControl,
|
||||||
propertiesSubgroupSizeControl.MinSubgroupSize,
|
propertiesSubgroupSizeControl.MinSubgroupSize,
|
||||||
propertiesSubgroupSizeControl.MaxSubgroupSize,
|
propertiesSubgroupSizeControl.MaxSubgroupSize,
|
||||||
propertiesSubgroupSizeControl.RequiredSubgroupSizeStages,
|
propertiesSubgroupSizeControl.RequiredSubgroupSizeStages,
|
||||||
@ -584,6 +599,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
supportsViewportMask: Capabilities.SupportsViewportArray2,
|
supportsViewportMask: Capabilities.SupportsViewportArray2,
|
||||||
supportsViewportSwizzle: false,
|
supportsViewportSwizzle: false,
|
||||||
supportsIndirectParameters: true,
|
supportsIndirectParameters: true,
|
||||||
|
supportsDepthClipControl: Capabilities.SupportsDepthClipControl,
|
||||||
maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage,
|
maximumUniformBuffersPerStage: Constants.MaxUniformBuffersPerStage,
|
||||||
maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage,
|
maximumStorageBuffersPerStage: Constants.MaxStorageBuffersPerStage,
|
||||||
maximumTexturesPerStage: Constants.MaxTexturesPerStage,
|
maximumTexturesPerStage: Constants.MaxTexturesPerStage,
|
||||||
|
@ -167,12 +167,12 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
if (StrEquals(RomfsDir, modDir.Name))
|
if (StrEquals(RomfsDir, modDir.Name))
|
||||||
{
|
{
|
||||||
mods.RomfsDirs.Add(mod = new Mod<DirectoryInfo>($"<{titleId} RomFs>", modDir));
|
mods.RomfsDirs.Add(mod = new Mod<DirectoryInfo>(dir.Name, modDir));
|
||||||
types.Append('R');
|
types.Append('R');
|
||||||
}
|
}
|
||||||
else if (StrEquals(ExefsDir, modDir.Name))
|
else if (StrEquals(ExefsDir, modDir.Name))
|
||||||
{
|
{
|
||||||
mods.ExefsDirs.Add(mod = new Mod<DirectoryInfo>($"<{titleId} ExeFs>", modDir));
|
mods.ExefsDirs.Add(mod = new Mod<DirectoryInfo>(dir.Name, modDir));
|
||||||
types.Append('E');
|
types.Append('E');
|
||||||
}
|
}
|
||||||
else if (StrEquals(CheatDir, modDir.Name))
|
else if (StrEquals(CheatDir, modDir.Name))
|
||||||
|
@ -130,7 +130,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
public float AudioVolume { get; set; }
|
public float AudioVolume { get; set; }
|
||||||
|
|
||||||
[Option("use-hypervisor", Required = false, Default = true, HelpText = "Uses Hypervisor over JIT if available.")]
|
[Option("use-hypervisor", Required = false, Default = true, HelpText = "Uses Hypervisor over JIT if available.")]
|
||||||
public bool UseHypervisor { get; set; }
|
public bool? UseHypervisor { get; set; }
|
||||||
|
|
||||||
[Option("lan-interface-id", Required = false, Default = "0", HelpText = "GUID for the network interface used by LAN.")]
|
[Option("lan-interface-id", Required = false, Default = "0", HelpText = "GUID for the network interface used by LAN.")]
|
||||||
public string MultiplayerLanInterfaceId { get; set; }
|
public string MultiplayerLanInterfaceId { get; set; }
|
||||||
|
@ -9,6 +9,7 @@ using Ryujinx.Common.Configuration.Hid.Controller;
|
|||||||
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
|
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
|
||||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Logging.Targets;
|
||||||
using Ryujinx.Common.SystemInterop;
|
using Ryujinx.Common.SystemInterop;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.Cpu;
|
using Ryujinx.Cpu;
|
||||||
@ -339,6 +340,15 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
|
|
||||||
GraphicsConfig.EnableShaderCache = true;
|
GraphicsConfig.EnableShaderCache = true;
|
||||||
|
|
||||||
|
if (OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
if (option.GraphicsBackend == GraphicsBackend.OpenGl)
|
||||||
|
{
|
||||||
|
option.GraphicsBackend = GraphicsBackend.Vulkan;
|
||||||
|
Logger.Warning?.Print(LogClass.Application, "OpenGL is not supported on macOS, switching to Vulkan!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IGamepad gamepad;
|
IGamepad gamepad;
|
||||||
|
|
||||||
if (option.ListInputIds)
|
if (option.ListInputIds)
|
||||||
@ -550,7 +560,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
options.IgnoreMissingServices,
|
options.IgnoreMissingServices,
|
||||||
options.AspectRatio,
|
options.AspectRatio,
|
||||||
options.AudioVolume,
|
options.AudioVolume,
|
||||||
options.UseHypervisor,
|
options.UseHypervisor ?? true,
|
||||||
options.MultiplayerLanInterfaceId);
|
options.MultiplayerLanInterfaceId);
|
||||||
|
|
||||||
return new Switch(configuration);
|
return new Switch(configuration);
|
||||||
@ -703,4 +713,4 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,6 +7,7 @@
|
|||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<Version>1.0.0-dirty</Version>
|
<Version>1.0.0-dirty</Version>
|
||||||
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
|
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
|
||||||
|
<SigningCertificate Condition=" '$(SigningCertificate)' == '' ">-</SigningCertificate>
|
||||||
<TieredPGO>true</TieredPGO>
|
<TieredPGO>true</TieredPGO>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
@ -15,6 +16,10 @@
|
|||||||
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" />
|
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="$([MSBuild]::IsOSPlatform('OSX'))">
|
||||||
|
<Exec Command="codesign --entitlements '$(ProjectDir)..\..\distribution\macos\entitlements.xml' -f --deep -s $(SigningCertificate) '$(TargetDir)$(TargetName)'" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" />
|
<ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" />
|
||||||
@ -29,6 +34,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommandLineParser" />
|
<PackageReference Include="CommandLineParser" />
|
||||||
|
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win10-x64'" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -63,4 +69,4 @@
|
|||||||
<PublishTrimmed>true</PublishTrimmed>
|
<PublishTrimmed>true</PublishTrimmed>
|
||||||
<TrimMode>partial</TrimMode>
|
<TrimMode>partial</TrimMode>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
@ -62,6 +62,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
private readonly long _ticksPerFrame;
|
private readonly long _ticksPerFrame;
|
||||||
private readonly CancellationTokenSource _gpuCancellationTokenSource;
|
private readonly CancellationTokenSource _gpuCancellationTokenSource;
|
||||||
private readonly ManualResetEvent _exitEvent;
|
private readonly ManualResetEvent _exitEvent;
|
||||||
|
private readonly ManualResetEvent _gpuDoneEvent;
|
||||||
|
|
||||||
private long _ticks;
|
private long _ticks;
|
||||||
private bool _isActive;
|
private bool _isActive;
|
||||||
@ -91,6 +92,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
_ticksPerFrame = Stopwatch.Frequency / TargetFps;
|
_ticksPerFrame = Stopwatch.Frequency / TargetFps;
|
||||||
_gpuCancellationTokenSource = new CancellationTokenSource();
|
_gpuCancellationTokenSource = new CancellationTokenSource();
|
||||||
_exitEvent = new ManualResetEvent(false);
|
_exitEvent = new ManualResetEvent(false);
|
||||||
|
_gpuDoneEvent = new ManualResetEvent(false);
|
||||||
_aspectRatio = aspectRatio;
|
_aspectRatio = aspectRatio;
|
||||||
_enableMouse = enableMouse;
|
_enableMouse = enableMouse;
|
||||||
HostUiTheme = new HeadlessHostUiTheme();
|
HostUiTheme = new HeadlessHostUiTheme();
|
||||||
@ -275,6 +277,14 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
_ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame);
|
_ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
});
|
});
|
||||||
|
|
||||||
FinalizeWindowRenderer();
|
FinalizeWindowRenderer();
|
||||||
@ -404,7 +414,10 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
|
|
||||||
MainLoop();
|
MainLoop();
|
||||||
|
|
||||||
renderLoopThread.Join();
|
// NOTE: The render loop is allowed to stay alive until the renderer itself is disposed, as it may handle resource dispose.
|
||||||
|
// We only need to wait for all commands submitted during the main gpu loop to be processed.
|
||||||
|
_gpuDoneEvent.WaitOne();
|
||||||
|
_gpuDoneEvent.Dispose();
|
||||||
nvStutterWorkaround?.Join();
|
nvStutterWorkaround?.Join();
|
||||||
|
|
||||||
Exit();
|
Exit();
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
@ -53,9 +54,9 @@ namespace Ryujinx.Memory
|
|||||||
|
|
||||||
IntPtr ptr = mmap(IntPtr.Zero, size, prot, flags, -1, 0);
|
IntPtr ptr = mmap(IntPtr.Zero, size, prot, flags, -1, 0);
|
||||||
|
|
||||||
if (ptr == new IntPtr(-1L))
|
if (ptr == MAP_FAILED)
|
||||||
{
|
{
|
||||||
throw new OutOfMemoryException();
|
throw new SystemException(Marshal.GetLastPInvokeErrorMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_allocations.TryAdd(ptr, size))
|
if (!_allocations.TryAdd(ptr, size))
|
||||||
@ -76,17 +77,33 @@ namespace Ryujinx.Memory
|
|||||||
prot |= MmapProts.PROT_EXEC;
|
prot |= MmapProts.PROT_EXEC;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mprotect(address, size, prot) == 0;
|
if (mprotect(address, size, prot) != 0)
|
||||||
|
{
|
||||||
|
throw new SystemException(Marshal.GetLastPInvokeErrorMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool Decommit(IntPtr address, ulong size)
|
public static bool Decommit(IntPtr address, ulong size)
|
||||||
{
|
{
|
||||||
// Must be writable for madvise to work properly.
|
// Must be writable for madvise to work properly.
|
||||||
mprotect(address, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE);
|
if (mprotect(address, size, MmapProts.PROT_READ | MmapProts.PROT_WRITE) != 0)
|
||||||
|
{
|
||||||
|
throw new SystemException(Marshal.GetLastPInvokeErrorMessage());
|
||||||
|
}
|
||||||
|
|
||||||
madvise(address, size, MADV_REMOVE);
|
if (madvise(address, size, MADV_REMOVE) != 0)
|
||||||
|
{
|
||||||
|
throw new SystemException(Marshal.GetLastPInvokeErrorMessage());
|
||||||
|
}
|
||||||
|
|
||||||
return mprotect(address, size, MmapProts.PROT_NONE) == 0;
|
if (mprotect(address, size, MmapProts.PROT_NONE) != 0)
|
||||||
|
{
|
||||||
|
throw new SystemException(Marshal.GetLastPInvokeErrorMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool Reprotect(IntPtr address, ulong size, MemoryPermission permission)
|
public static bool Reprotect(IntPtr address, ulong size, MemoryPermission permission)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Memory.WindowsShared;
|
using Ryujinx.Memory.WindowsShared;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
namespace Ryujinx.Memory
|
namespace Ryujinx.Memory
|
||||||
@ -36,7 +37,7 @@ namespace Ryujinx.Memory
|
|||||||
|
|
||||||
if (ptr == IntPtr.Zero)
|
if (ptr == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
throw new OutOfMemoryException();
|
throw new SystemException(Marshal.GetLastPInvokeErrorMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return ptr;
|
return ptr;
|
||||||
@ -48,7 +49,7 @@ namespace Ryujinx.Memory
|
|||||||
|
|
||||||
if (ptr == IntPtr.Zero)
|
if (ptr == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
throw new OutOfMemoryException();
|
throw new SystemException(Marshal.GetLastPInvokeErrorMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
return ptr;
|
return ptr;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user