Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9b5a0c3889 | ||
|
80b4972139 | ||
|
5d85468302 | ||
|
9b1cc2cec6 | ||
|
e691622f0a | ||
|
f663a5cd38 | ||
|
f7c2e867f4 | ||
|
cedd200745 | ||
|
58207685c0 | ||
|
095ad923ad |
@@ -26,10 +26,10 @@
|
|||||||
<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" />
|
||||||
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
|
||||||
<PackageVersion Include="OpenTK.Core" Version="4.7.5" />
|
<PackageVersion Include="OpenTK.Core" Version="4.7.7" />
|
||||||
<PackageVersion Include="OpenTK.Graphics" Version="4.7.5" />
|
<PackageVersion Include="OpenTK.Graphics" Version="4.7.7" />
|
||||||
<PackageVersion Include="OpenTK.OpenAL" Version="4.7.5" />
|
<PackageVersion Include="OpenTK.OpenAL" Version="4.7.7" />
|
||||||
<PackageVersion Include="OpenTK.Windowing.GraphicsLibraryFramework" Version="4.7.5" />
|
<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.0" />
|
<PackageVersion Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Version="1.2.0" />
|
||||||
|
@@ -171,6 +171,11 @@ namespace Ryujinx.Ava
|
|||||||
ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState;
|
ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState;
|
||||||
ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState;
|
ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState;
|
||||||
ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState;
|
ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState;
|
||||||
|
ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState;
|
||||||
|
ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState;
|
||||||
|
ConfigurationState.Instance.Graphics.AntiAliasing.Event += UpdateAntiAliasing;
|
||||||
|
ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter;
|
||||||
|
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel;
|
||||||
|
|
||||||
_gpuCancellationTokenSource = new CancellationTokenSource();
|
_gpuCancellationTokenSource = new CancellationTokenSource();
|
||||||
}
|
}
|
||||||
@@ -193,6 +198,17 @@ namespace Ryujinx.Ava
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
private void UpdateScalingFilterLevel(object sender, ReactiveEventArgs<int> e)
|
||||||
|
{
|
||||||
|
_renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value);
|
||||||
|
_renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateScalingFilter(object sender, ReactiveEventArgs<Ryujinx.Common.Configuration.ScalingFilter> e)
|
||||||
|
{
|
||||||
|
_renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value);
|
||||||
|
_renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value);
|
||||||
|
}
|
||||||
|
|
||||||
private void ShowCursor()
|
private void ShowCursor()
|
||||||
{
|
{
|
||||||
@@ -345,6 +361,11 @@ namespace Ryujinx.Ava
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateAntiAliasing(object sender, ReactiveEventArgs<Ryujinx.Common.Configuration.AntiAliasing> e)
|
||||||
|
{
|
||||||
|
_renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)e.NewValue);
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateDockedModeState(object sender, ReactiveEventArgs<bool> e)
|
private void UpdateDockedModeState(object sender, ReactiveEventArgs<bool> e)
|
||||||
{
|
{
|
||||||
Device?.System.ChangeDockedModeState(e.NewValue);
|
Device?.System.ChangeDockedModeState(e.NewValue);
|
||||||
@@ -411,6 +432,9 @@ namespace Ryujinx.Ava
|
|||||||
ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState;
|
ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState;
|
||||||
ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState;
|
ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState;
|
||||||
ConfigurationState.Instance.System.AudioVolume.Event -= UpdateAudioVolumeState;
|
ConfigurationState.Instance.System.AudioVolume.Event -= UpdateAudioVolumeState;
|
||||||
|
ConfigurationState.Instance.Graphics.ScalingFilter.Event -= UpdateScalingFilter;
|
||||||
|
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel;
|
||||||
|
ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing;
|
||||||
|
|
||||||
_topLevel.PointerMoved -= TopLevel_PointerMoved;
|
_topLevel.PointerMoved -= TopLevel_PointerMoved;
|
||||||
|
|
||||||
@@ -788,6 +812,10 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
Device.Gpu.Renderer.Initialize(_glLogLevel);
|
Device.Gpu.Renderer.Initialize(_glLogLevel);
|
||||||
|
|
||||||
|
_renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)ConfigurationState.Instance.Graphics.AntiAliasing.Value);
|
||||||
|
_renderer?.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value);
|
||||||
|
_renderer?.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value);
|
||||||
|
|
||||||
Width = (int)_rendererHost.Bounds.Width;
|
Width = (int)_rendererHost.Bounds.Width;
|
||||||
Height = (int)_rendererHost.Bounds.Height;
|
Height = (int)_rendererHost.Bounds.Height;
|
||||||
|
|
||||||
|
@@ -626,6 +626,16 @@
|
|||||||
"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",
|
||||||
|
"GraphicsAALabel": "Anti-Aliasing:",
|
||||||
|
"GraphicsScalingFilterLabel": "Scaling Filter:",
|
||||||
|
"GraphicsScalingFilterTooltip": "Enables Framebuffer Scaling",
|
||||||
|
"GraphicsScalingFilterLevelLabel": "Level",
|
||||||
|
"GraphicsScalingFilterLevelTooltip": "Set Scaling Filter Level",
|
||||||
|
"SmaaLow": "SMAA Low",
|
||||||
|
"SmaaMedium": "SMAA Medium",
|
||||||
|
"SmaaHigh": "SMAA High",
|
||||||
|
"SmaaUltra": "SMAA Ultra",
|
||||||
"UserEditorTitle" : "Edit User",
|
"UserEditorTitle" : "Edit User",
|
||||||
"UserEditorTitleCreate" : "Create User"
|
"UserEditorTitleCreate" : "Create User"
|
||||||
}
|
}
|
||||||
|
@@ -21,6 +21,7 @@ using System.Net.Http;
|
|||||||
using System.Net.NetworkInformation;
|
using System.Net.NetworkInformation;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -57,7 +58,7 @@ namespace Ryujinx.Modules
|
|||||||
// Detect current platform
|
// Detect current platform
|
||||||
if (OperatingSystem.IsMacOS())
|
if (OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
_platformExt = "osx_x64.zip";
|
_platformExt = "macos_universal.app.tar.gz";
|
||||||
}
|
}
|
||||||
else if (OperatingSystem.IsWindows())
|
else if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
@@ -286,22 +287,40 @@ namespace Ryujinx.Modules
|
|||||||
|
|
||||||
if (_updateSuccessful)
|
if (_updateSuccessful)
|
||||||
{
|
{
|
||||||
var shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
|
bool shouldRestart = true;
|
||||||
|
|
||||||
|
if (!OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
shouldRestart = await ContentDialogHelper.CreateChoiceDialog(LocaleManager.Instance[LocaleKeys.RyujinxUpdater],
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterCompleteMessage],
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterCompleteMessage],
|
||||||
LocaleManager.Instance[LocaleKeys.DialogUpdaterRestartMessage]);
|
LocaleManager.Instance[LocaleKeys.DialogUpdaterRestartMessage]);
|
||||||
|
}
|
||||||
|
|
||||||
if (shouldRestart)
|
if (shouldRestart)
|
||||||
{
|
{
|
||||||
|
List<string> arguments = CommandLineState.Arguments.ToList();
|
||||||
string ryuName = Path.GetFileName(Environment.ProcessPath);
|
string ryuName = Path.GetFileName(Environment.ProcessPath);
|
||||||
string ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName);
|
string executableDirectory = AppDomain.CurrentDomain.BaseDirectory;
|
||||||
|
string executablePath = Path.Combine(executableDirectory, ryuName);
|
||||||
|
|
||||||
if (!Path.Exists(ryuExe))
|
if (!Path.Exists(executablePath))
|
||||||
{
|
{
|
||||||
ryuExe = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx");
|
executablePath = Path.Combine(executableDirectory, OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx");
|
||||||
}
|
}
|
||||||
|
|
||||||
Process.Start(ryuExe, CommandLineState.Arguments);
|
// On macOS we perform the update at relaunch.
|
||||||
|
if (OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
string baseBundlePath = Path.GetFullPath(Path.Combine(executableDirectory, "..", ".."));
|
||||||
|
string newBundlePath = Path.Combine(UpdateDir, "Ryujinx.app");
|
||||||
|
string updaterScriptPath = Path.Combine(newBundlePath, "Contents", "Resources", "updater.sh");
|
||||||
|
string currentPid = Process.GetCurrentProcess().Id.ToString();
|
||||||
|
|
||||||
|
executablePath = "/bin/bash";
|
||||||
|
arguments.InsertRange(0, new List<string> { updaterScriptPath, baseBundlePath, newBundlePath, currentPid });
|
||||||
|
}
|
||||||
|
|
||||||
|
Process.Start(executablePath, arguments);
|
||||||
Environment.Exit(0);
|
Environment.Exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -381,6 +400,15 @@ namespace Ryujinx.Modules
|
|||||||
|
|
||||||
File.WriteAllBytes(updateFile, mergedFileBytes);
|
File.WriteAllBytes(updateFile, mergedFileBytes);
|
||||||
|
|
||||||
|
// On macOS, ensure that we remove the quarantine bit to prevent Gatekeeper from blocking execution.
|
||||||
|
if (OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
using (Process xattrProcess = Process.Start("xattr", new List<string> { "-d", "com.apple.quarantine", updateFile }))
|
||||||
|
{
|
||||||
|
xattrProcess.WaitForExit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
InstallUpdate(taskDialog, updateFile);
|
InstallUpdate(taskDialog, updateFile);
|
||||||
@@ -470,29 +498,24 @@ namespace Ryujinx.Modules
|
|||||||
worker.Start();
|
worker.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async void InstallUpdate(TaskDialog taskDialog, string updateFile)
|
[SupportedOSPlatform("linux")]
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
|
private static void ExtractTarGzipFile(TaskDialog taskDialog, string archivePath, string outputDirectoryPath)
|
||||||
{
|
{
|
||||||
// Extract Update
|
using Stream inStream = File.OpenRead(archivePath);
|
||||||
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterExtracting];
|
|
||||||
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
|
||||||
|
|
||||||
if (OperatingSystem.IsLinux())
|
|
||||||
{
|
|
||||||
using Stream inStream = File.OpenRead(updateFile);
|
|
||||||
using GZipInputStream gzipStream = new(inStream);
|
using GZipInputStream gzipStream = new(inStream);
|
||||||
using TarInputStream tarStream = new(gzipStream, Encoding.ASCII);
|
using TarInputStream tarStream = new(gzipStream, Encoding.ASCII);
|
||||||
|
|
||||||
await Task.Run(() =>
|
|
||||||
{
|
|
||||||
TarEntry tarEntry;
|
TarEntry tarEntry;
|
||||||
|
|
||||||
if (!OperatingSystem.IsWindows())
|
|
||||||
{
|
|
||||||
while ((tarEntry = tarStream.GetNextEntry()) is not null)
|
while ((tarEntry = tarStream.GetNextEntry()) is not null)
|
||||||
{
|
{
|
||||||
if (tarEntry.IsDirectory) continue;
|
if (tarEntry.IsDirectory)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
string outPath = Path.Combine(UpdateDir, tarEntry.Name);
|
string outPath = Path.Combine(outputDirectoryPath, tarEntry.Name);
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
|
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
|
||||||
|
|
||||||
@@ -515,24 +538,19 @@ namespace Ryujinx.Modules
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
taskDialog.SetProgressBarState(100, TaskDialogProgressState.Normal);
|
private static void ExtractZipFile(TaskDialog taskDialog, string archivePath, string outputDirectoryPath)
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
using Stream inStream = File.OpenRead(updateFile);
|
using Stream inStream = File.OpenRead(archivePath);
|
||||||
using ZipFile zipFile = new(inStream);
|
using ZipFile zipFile = new(inStream);
|
||||||
|
|
||||||
await Task.Run(() =>
|
|
||||||
{
|
|
||||||
double count = 0;
|
double count = 0;
|
||||||
foreach (ZipEntry zipEntry in zipFile)
|
foreach (ZipEntry zipEntry in zipFile)
|
||||||
{
|
{
|
||||||
count++;
|
count++;
|
||||||
if (zipEntry.IsDirectory) continue;
|
if (zipEntry.IsDirectory) continue;
|
||||||
|
|
||||||
string outPath = Path.Combine(UpdateDir, zipEntry.Name);
|
string outPath = Path.Combine(outputDirectoryPath, zipEntry.Name);
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
|
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
|
||||||
|
|
||||||
@@ -549,9 +567,30 @@ namespace Ryujinx.Modules
|
|||||||
taskDialog.SetProgressBarState(GetPercentage(count, zipFile.Count), TaskDialogProgressState.Normal);
|
taskDialog.SetProgressBarState(GetPercentage(count, zipFile.Count), TaskDialogProgressState.Normal);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async void InstallUpdate(TaskDialog taskDialog, string updateFile)
|
||||||
|
{
|
||||||
|
// Extract Update
|
||||||
|
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterExtracting];
|
||||||
|
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
||||||
|
|
||||||
|
await Task.Run(() =>
|
||||||
|
{
|
||||||
|
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
ExtractTarGzipFile(taskDialog, updateFile, UpdateDir);
|
||||||
|
}
|
||||||
|
else if (OperatingSystem.IsWindows())
|
||||||
|
{
|
||||||
|
ExtractZipFile(taskDialog, updateFile, UpdateDir);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Delete downloaded zip
|
// Delete downloaded zip
|
||||||
File.Delete(updateFile);
|
File.Delete(updateFile);
|
||||||
|
|
||||||
@@ -560,6 +599,9 @@ namespace Ryujinx.Modules
|
|||||||
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterRenaming];
|
taskDialog.SubHeader = LocaleManager.Instance[LocaleKeys.UpdaterRenaming];
|
||||||
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
taskDialog.SetProgressBarState(0, TaskDialogProgressState.Normal);
|
||||||
|
|
||||||
|
// NOTE: On macOS, replacement is delayed to the restart phase.
|
||||||
|
if (!OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
// Replace old files
|
// Replace old files
|
||||||
await Task.Run(() =>
|
await Task.Run(() =>
|
||||||
{
|
{
|
||||||
@@ -592,6 +634,7 @@ namespace Ryujinx.Modules
|
|||||||
});
|
});
|
||||||
|
|
||||||
Directory.Delete(UpdateDir, true);
|
Directory.Delete(UpdateDir, true);
|
||||||
|
}
|
||||||
|
|
||||||
_updateSuccessful = true;
|
_updateSuccessful = true;
|
||||||
|
|
||||||
@@ -601,7 +644,7 @@ namespace Ryujinx.Modules
|
|||||||
public static bool CanUpdate(bool showWarnings)
|
public static bool CanUpdate(bool showWarnings)
|
||||||
{
|
{
|
||||||
#if !DISABLE_UPDATER
|
#if !DISABLE_UPDATER
|
||||||
if (RuntimeInformation.OSArchitecture != Architecture.X64)
|
if (RuntimeInformation.OSArchitecture != Architecture.X64 && !OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
if (showWarnings)
|
if (showWarnings)
|
||||||
{
|
{
|
||||||
@@ -674,7 +717,7 @@ namespace Ryujinx.Modules
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: This method should always reflect the latest build layout.s
|
// NOTE: This method should always reflect the latest build layout.
|
||||||
private static IEnumerable<string> EnumerateFilesToDelete()
|
private static IEnumerable<string> EnumerateFilesToDelete()
|
||||||
{
|
{
|
||||||
var files = Directory.EnumerateFiles(HomeDir); // All files directly in base dir.
|
var files = Directory.EnumerateFiles(HomeDir); // All files directly in base dir.
|
||||||
|
@@ -45,6 +45,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
private KeyboardHotkeys _keyboardHotkeys;
|
private KeyboardHotkeys _keyboardHotkeys;
|
||||||
private int _graphicsBackendIndex;
|
private int _graphicsBackendIndex;
|
||||||
private string _customThemePath;
|
private string _customThemePath;
|
||||||
|
private int _scalingFilter;
|
||||||
|
private int _scalingFilterLevel;
|
||||||
|
|
||||||
public event Action CloseWindow;
|
public event Action CloseWindow;
|
||||||
public event Action SaveSettingsEvent;
|
public event Action SaveSettingsEvent;
|
||||||
@@ -153,6 +155,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
public bool IsSDL2Enabled { get; set; }
|
public bool IsSDL2Enabled { get; set; }
|
||||||
public bool EnableCustomTheme { get; set; }
|
public bool EnableCustomTheme { get; set; }
|
||||||
public bool IsCustomResolutionScaleActive => _resolutionScale == 4;
|
public bool IsCustomResolutionScaleActive => _resolutionScale == 4;
|
||||||
|
public bool IsScalingFilterActive => _scalingFilter == (int)Ryujinx.Common.Configuration.ScalingFilter.Fsr;
|
||||||
|
|
||||||
public bool IsVulkanSelected => GraphicsBackendIndex == 0;
|
public bool IsVulkanSelected => GraphicsBackendIndex == 0;
|
||||||
public bool UseHypervisor { get; set; }
|
public bool UseHypervisor { get; set; }
|
||||||
|
|
||||||
@@ -179,6 +183,18 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
public int AudioBackend { get; set; }
|
public int AudioBackend { get; set; }
|
||||||
public int MaxAnisotropy { get; set; }
|
public int MaxAnisotropy { get; set; }
|
||||||
public int AspectRatio { get; set; }
|
public int AspectRatio { get; set; }
|
||||||
|
public int AntiAliasingEffect { get; set; }
|
||||||
|
public string ScalingFilterLevelText => ScalingFilterLevel.ToString("0");
|
||||||
|
public int ScalingFilterLevel
|
||||||
|
{
|
||||||
|
get => _scalingFilterLevel;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_scalingFilterLevel = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
OnPropertyChanged(nameof(ScalingFilterLevelText));
|
||||||
|
}
|
||||||
|
}
|
||||||
public int OpenglDebugLevel { get; set; }
|
public int OpenglDebugLevel { get; set; }
|
||||||
public int MemoryMode { get; set; }
|
public int MemoryMode { get; set; }
|
||||||
public int BaseStyleIndex { get; set; }
|
public int BaseStyleIndex { get; set; }
|
||||||
@@ -192,6 +208,16 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
OnPropertyChanged(nameof(IsVulkanSelected));
|
OnPropertyChanged(nameof(IsVulkanSelected));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public int ScalingFilter
|
||||||
|
{
|
||||||
|
get => _scalingFilter;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_scalingFilter = value;
|
||||||
|
OnPropertyChanged();
|
||||||
|
OnPropertyChanged(nameof(IsScalingFilterActive));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public int PreferredGpuIndex { get; set; }
|
public int PreferredGpuIndex { get; set; }
|
||||||
|
|
||||||
@@ -365,6 +391,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
AspectRatio = (int)config.Graphics.AspectRatio.Value;
|
AspectRatio = (int)config.Graphics.AspectRatio.Value;
|
||||||
GraphicsBackendMultithreadingIndex = (int)config.Graphics.BackendThreading.Value;
|
GraphicsBackendMultithreadingIndex = (int)config.Graphics.BackendThreading.Value;
|
||||||
ShaderDumpPath = config.Graphics.ShadersDumpPath;
|
ShaderDumpPath = config.Graphics.ShadersDumpPath;
|
||||||
|
AntiAliasingEffect = (int)config.Graphics.AntiAliasing.Value;
|
||||||
|
ScalingFilter = (int)config.Graphics.ScalingFilter.Value;
|
||||||
|
ScalingFilterLevel = config.Graphics.ScalingFilterLevel.Value;
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
AudioBackend = (int)config.System.AudioBackend.Value;
|
AudioBackend = (int)config.System.AudioBackend.Value;
|
||||||
@@ -447,6 +476,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
config.Graphics.ResScaleCustom.Value = CustomResolutionScale;
|
config.Graphics.ResScaleCustom.Value = CustomResolutionScale;
|
||||||
config.Graphics.MaxAnisotropy.Value = MaxAnisotropy == 0 ? -1 : MathF.Pow(2, MaxAnisotropy);
|
config.Graphics.MaxAnisotropy.Value = MaxAnisotropy == 0 ? -1 : MathF.Pow(2, MaxAnisotropy);
|
||||||
config.Graphics.AspectRatio.Value = (AspectRatio)AspectRatio;
|
config.Graphics.AspectRatio.Value = (AspectRatio)AspectRatio;
|
||||||
|
config.Graphics.AntiAliasing.Value = (AntiAliasing)AntiAliasingEffect;
|
||||||
|
config.Graphics.ScalingFilter.Value = (ScalingFilter)ScalingFilter;
|
||||||
|
config.Graphics.ScalingFilterLevel.Value = ScalingFilterLevel;
|
||||||
|
|
||||||
if (ConfigurationState.Instance.Graphics.BackendThreading != (BackendThreading)GraphicsBackendMultithreadingIndex)
|
if (ConfigurationState.Instance.Graphics.BackendThreading != (BackendThreading)GraphicsBackendMultithreadingIndex)
|
||||||
{
|
{
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
|
||||||
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
|
||||||
|
Design.Width="1000"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
x:CompileBindings="True"
|
x:CompileBindings="True"
|
||||||
x:DataType="viewModels:SettingsViewModel">
|
x:DataType="viewModels:SettingsViewModel">
|
||||||
@@ -111,6 +112,83 @@
|
|||||||
Minimum="0.1"
|
Minimum="0.1"
|
||||||
Value="{Binding CustomResolutionScale}" />
|
Value="{Binding CustomResolutionScale}" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
<StackPanel
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Orientation="Vertical"
|
||||||
|
Spacing="10">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock VerticalAlignment="Center"
|
||||||
|
ToolTip.Tip="{locale:Locale GraphicsAATooltip}"
|
||||||
|
Text="{locale:Locale GraphicsAALabel}"
|
||||||
|
Width="250" />
|
||||||
|
<ComboBox Width="350"
|
||||||
|
HorizontalContentAlignment="Left"
|
||||||
|
ToolTip.Tip="{locale:Locale GraphicsAATooltip}"
|
||||||
|
SelectedIndex="{Binding AntiAliasingEffect}">
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="{locale:Locale SettingsTabLoggingGraphicsBackendLogLevelNone}" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="FXAA" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="{locale:Locale SmaaLow}" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="{locale:Locale SmaaMedium}" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="{locale:Locale SmaaHigh}" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="{locale:Locale SmaaUltra}" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
</ComboBox>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
Orientation="Vertical"
|
||||||
|
Spacing="10">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock VerticalAlignment="Center"
|
||||||
|
ToolTip.Tip="{locale:Locale GraphicsScalingFilterTooltip}"
|
||||||
|
Text="{locale:Locale GraphicsScalingFilterLabel}"
|
||||||
|
Width="250" />
|
||||||
|
<ComboBox Width="350"
|
||||||
|
HorizontalContentAlignment="Left"
|
||||||
|
ToolTip.Tip="{locale:Locale GraphicsScalingFilterTooltip}"
|
||||||
|
SelectedIndex="{Binding ScalingFilter}">
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="Bilinear" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="Nearest" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
<ComboBoxItem>
|
||||||
|
<TextBlock Text="FSR" />
|
||||||
|
</ComboBoxItem>
|
||||||
|
</ComboBox>
|
||||||
|
<Slider Value="{Binding ScalingFilterLevel}"
|
||||||
|
ToolTip.Tip="{locale:Locale GraphicsScalingFilterLevelTooltip}"
|
||||||
|
MinWidth="150"
|
||||||
|
Margin="10,-3,0,0"
|
||||||
|
Height="32"
|
||||||
|
Padding="0,-5"
|
||||||
|
IsVisible="{Binding IsScalingFilterActive}"
|
||||||
|
TickFrequency="1"
|
||||||
|
IsSnapToTickEnabled="True"
|
||||||
|
LargeChange="10"
|
||||||
|
SmallChange="1"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Minimum="0"
|
||||||
|
Maximum="100" />
|
||||||
|
<TextBlock Margin="5,0"
|
||||||
|
Width="40"
|
||||||
|
IsVisible="{Binding IsScalingFilterActive}"
|
||||||
|
Text="{Binding ScalingFilterLevelText}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
<StackPanel Orientation="Horizontal">
|
<StackPanel Orientation="Horizontal">
|
||||||
<TextBlock VerticalAlignment="Center"
|
<TextBlock VerticalAlignment="Center"
|
||||||
ToolTip.Tip="{locale:Locale AnisotropyTooltip}"
|
ToolTip.Tip="{locale:Locale AnisotropyTooltip}"
|
||||||
|
12
Ryujinx.Common/Configuration/AntiAliasing.cs
Normal file
12
Ryujinx.Common/Configuration/AntiAliasing.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
|
{
|
||||||
|
public enum AntiAliasing
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Fxaa,
|
||||||
|
SmaaLow,
|
||||||
|
SmaaMedium,
|
||||||
|
SmaaHigh,
|
||||||
|
SmaaUltra
|
||||||
|
}
|
||||||
|
}
|
9
Ryujinx.Common/Configuration/ScalingFilter.cs
Normal file
9
Ryujinx.Common/Configuration/ScalingFilter.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Ryujinx.Common.Configuration
|
||||||
|
{
|
||||||
|
public enum ScalingFilter
|
||||||
|
{
|
||||||
|
Bilinear,
|
||||||
|
Nearest,
|
||||||
|
Fsr
|
||||||
|
}
|
||||||
|
}
|
@@ -1,3 +1,4 @@
|
|||||||
|
using Ryujinx.Common.SystemInterop;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@@ -14,6 +15,8 @@ namespace Ryujinx.Common.Logging
|
|||||||
|
|
||||||
private static readonly List<ILogTarget> m_LogTargets;
|
private static readonly List<ILogTarget> m_LogTargets;
|
||||||
|
|
||||||
|
private static readonly StdErrAdapter _stdErrAdapter;
|
||||||
|
|
||||||
public static event EventHandler<LogEventArgs> Updated;
|
public static event EventHandler<LogEventArgs> Updated;
|
||||||
|
|
||||||
public readonly struct Log
|
public readonly struct Log
|
||||||
@@ -79,6 +82,12 @@ namespace Ryujinx.Common.Logging
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void PrintRawMsg(string message)
|
||||||
|
{
|
||||||
|
Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, message));
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static string FormatMessage(LogClass Class, string Caller, string Message) => $"{Class} {Caller}: {Message}";
|
private static string FormatMessage(LogClass Class, string Caller, string Message) => $"{Class} {Caller}: {Message}";
|
||||||
}
|
}
|
||||||
@@ -119,6 +128,8 @@ namespace Ryujinx.Common.Logging
|
|||||||
Warning = new Log(LogLevel.Warning);
|
Warning = new Log(LogLevel.Warning);
|
||||||
Info = new Log(LogLevel.Info);
|
Info = new Log(LogLevel.Info);
|
||||||
Trace = new Log(LogLevel.Trace);
|
Trace = new Log(LogLevel.Trace);
|
||||||
|
|
||||||
|
_stdErrAdapter = new StdErrAdapter();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void RestartTime()
|
public static void RestartTime()
|
||||||
@@ -164,6 +175,8 @@ namespace Ryujinx.Common.Logging
|
|||||||
{
|
{
|
||||||
Updated = null;
|
Updated = null;
|
||||||
|
|
||||||
|
_stdErrAdapter.Dispose();
|
||||||
|
|
||||||
foreach (var target in m_LogTargets)
|
foreach (var target in m_LogTargets)
|
||||||
{
|
{
|
||||||
target.Dispose();
|
target.Dispose();
|
||||||
|
@@ -3,6 +3,7 @@
|
|||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
93
Ryujinx.Common/SystemInterop/StdErrAdapter.cs
Normal file
93
Ryujinx.Common/SystemInterop/StdErrAdapter.cs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
using System.Threading;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.SystemInterop
|
||||||
|
{
|
||||||
|
public partial class StdErrAdapter : IDisposable
|
||||||
|
{
|
||||||
|
private bool _disposable = false;
|
||||||
|
private UnixStream _pipeReader;
|
||||||
|
private UnixStream _pipeWriter;
|
||||||
|
private Thread _worker;
|
||||||
|
|
||||||
|
public StdErrAdapter()
|
||||||
|
{
|
||||||
|
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
RegisterPosix();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("linux")]
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
|
private void RegisterPosix()
|
||||||
|
{
|
||||||
|
const int stdErrFileno = 2;
|
||||||
|
|
||||||
|
(int readFd, int writeFd) = MakePipe();
|
||||||
|
dup2(writeFd, stdErrFileno);
|
||||||
|
|
||||||
|
_pipeReader = new UnixStream(readFd);
|
||||||
|
_pipeWriter = new UnixStream(writeFd);
|
||||||
|
|
||||||
|
_worker = new Thread(EventWorker);
|
||||||
|
_disposable = true;
|
||||||
|
_worker.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
[SupportedOSPlatform("linux")]
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
|
private void EventWorker()
|
||||||
|
{
|
||||||
|
TextReader reader = new StreamReader(_pipeReader);
|
||||||
|
string line;
|
||||||
|
while ((line = reader.ReadLine()) != null)
|
||||||
|
{
|
||||||
|
Logger.Error?.PrintRawMsg(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_disposable)
|
||||||
|
{
|
||||||
|
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
_pipeReader?.Close();
|
||||||
|
_pipeWriter?.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
[LibraryImport("libc", SetLastError = true)]
|
||||||
|
private static partial int dup2(int fd, int fd2);
|
||||||
|
|
||||||
|
[LibraryImport("libc", SetLastError = true)]
|
||||||
|
private static unsafe partial int pipe(int* pipefd);
|
||||||
|
|
||||||
|
private static unsafe (int, int) MakePipe()
|
||||||
|
{
|
||||||
|
int *pipefd = stackalloc int[2];
|
||||||
|
|
||||||
|
if (pipe(pipefd) == 0)
|
||||||
|
{
|
||||||
|
return (pipefd[0], pipefd[1]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
155
Ryujinx.Common/SystemInterop/UnixStream.cs
Normal file
155
Ryujinx.Common/SystemInterop/UnixStream.cs
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
|
||||||
|
namespace Ryujinx.Common.SystemInterop
|
||||||
|
{
|
||||||
|
[SupportedOSPlatform("linux")]
|
||||||
|
[SupportedOSPlatform("macos")]
|
||||||
|
public partial class UnixStream : Stream, IDisposable
|
||||||
|
{
|
||||||
|
private const int InvalidFd = -1;
|
||||||
|
|
||||||
|
private int _fd;
|
||||||
|
|
||||||
|
[LibraryImport("libc", SetLastError = true)]
|
||||||
|
private static partial long read(int fd, IntPtr buf, ulong count);
|
||||||
|
|
||||||
|
[LibraryImport("libc", SetLastError = true)]
|
||||||
|
private static partial long write(int fd, IntPtr buf, ulong count);
|
||||||
|
|
||||||
|
[LibraryImport("libc", SetLastError = true)]
|
||||||
|
private static partial int close(int fd);
|
||||||
|
|
||||||
|
public UnixStream(int fd)
|
||||||
|
{
|
||||||
|
if (InvalidFd == fd)
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Invalid file descriptor");
|
||||||
|
}
|
||||||
|
|
||||||
|
_fd = fd;
|
||||||
|
|
||||||
|
CanRead = read(fd, IntPtr.Zero, 0) != -1;
|
||||||
|
CanWrite = write(fd, IntPtr.Zero, 0) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
~UnixStream()
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanRead { get; }
|
||||||
|
public override bool CanWrite { get; }
|
||||||
|
public override bool CanSeek => false;
|
||||||
|
|
||||||
|
public override long Length => throw new NotSupportedException();
|
||||||
|
|
||||||
|
public override long Position
|
||||||
|
{
|
||||||
|
get => throw new NotSupportedException();
|
||||||
|
set => throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Flush()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override unsafe int Read([In, Out] byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
if (offset < 0 || offset > (buffer.Length - count) || count < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.Length == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
long r = 0;
|
||||||
|
fixed (byte* buf = &buffer[offset])
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
r = read(_fd, (IntPtr)buf, (ulong)count);
|
||||||
|
} while (ShouldRetry(r));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int)r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override unsafe void Write(byte[] buffer, int offset, int count)
|
||||||
|
{
|
||||||
|
if (offset < 0 || offset > (buffer.Length - count) || count < 0)
|
||||||
|
{
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer.Length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed (byte* buf = &buffer[offset])
|
||||||
|
{
|
||||||
|
long r = 0;
|
||||||
|
do {
|
||||||
|
r = write(_fd, (IntPtr)buf, (ulong)count);
|
||||||
|
} while (ShouldRetry(r));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override long Seek(long offset, SeekOrigin origin)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetLength(long value)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Close()
|
||||||
|
{
|
||||||
|
if (_fd == InvalidFd)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Flush();
|
||||||
|
|
||||||
|
int r;
|
||||||
|
do {
|
||||||
|
r = close(_fd);
|
||||||
|
} while (ShouldRetry(r));
|
||||||
|
|
||||||
|
_fd = InvalidFd;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IDisposable.Dispose()
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool ShouldRetry(long r)
|
||||||
|
{
|
||||||
|
if (r == -1)
|
||||||
|
{
|
||||||
|
const int eintr = 4;
|
||||||
|
|
||||||
|
int errno = Marshal.GetLastPInvokeError();
|
||||||
|
|
||||||
|
if (errno == eintr)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new SystemException($"Operation failed with error 0x{errno:X}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
Ryujinx.Graphics.GAL/AntiAliasing.cs
Normal file
12
Ryujinx.Graphics.GAL/AntiAliasing.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public enum AntiAliasing
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Fxaa,
|
||||||
|
SmaaLow,
|
||||||
|
SmaaMedium,
|
||||||
|
SmaaHigh,
|
||||||
|
SmaaUltra
|
||||||
|
}
|
||||||
|
}
|
@@ -26,6 +26,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
public readonly bool SupportsBlendEquationAdvanced;
|
public readonly bool SupportsBlendEquationAdvanced;
|
||||||
public readonly bool SupportsFragmentShaderInterlock;
|
public readonly bool SupportsFragmentShaderInterlock;
|
||||||
public readonly bool SupportsFragmentShaderOrderingIntel;
|
public readonly bool SupportsFragmentShaderOrderingIntel;
|
||||||
|
public readonly bool SupportsGeometryShader;
|
||||||
public readonly bool SupportsGeometryShaderPassthrough;
|
public readonly bool SupportsGeometryShaderPassthrough;
|
||||||
public readonly bool SupportsImageLoadFormatted;
|
public readonly bool SupportsImageLoadFormatted;
|
||||||
public readonly bool SupportsLayerVertexTessellation;
|
public readonly bool SupportsLayerVertexTessellation;
|
||||||
@@ -68,6 +69,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
bool supportsBlendEquationAdvanced,
|
bool supportsBlendEquationAdvanced,
|
||||||
bool supportsFragmentShaderInterlock,
|
bool supportsFragmentShaderInterlock,
|
||||||
bool supportsFragmentShaderOrderingIntel,
|
bool supportsFragmentShaderOrderingIntel,
|
||||||
|
bool supportsGeometryShader,
|
||||||
bool supportsGeometryShaderPassthrough,
|
bool supportsGeometryShaderPassthrough,
|
||||||
bool supportsImageLoadFormatted,
|
bool supportsImageLoadFormatted,
|
||||||
bool supportsLayerVertexTessellation,
|
bool supportsLayerVertexTessellation,
|
||||||
@@ -107,6 +109,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
SupportsBlendEquationAdvanced = supportsBlendEquationAdvanced;
|
SupportsBlendEquationAdvanced = supportsBlendEquationAdvanced;
|
||||||
SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock;
|
SupportsFragmentShaderInterlock = supportsFragmentShaderInterlock;
|
||||||
SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel;
|
SupportsFragmentShaderOrderingIntel = supportsFragmentShaderOrderingIntel;
|
||||||
|
SupportsGeometryShader = supportsGeometryShader;
|
||||||
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;
|
SupportsGeometryShaderPassthrough = supportsGeometryShaderPassthrough;
|
||||||
SupportsImageLoadFormatted = supportsImageLoadFormatted;
|
SupportsImageLoadFormatted = supportsImageLoadFormatted;
|
||||||
SupportsLayerVertexTessellation = supportsLayerVertexTessellation;
|
SupportsLayerVertexTessellation = supportsLayerVertexTessellation;
|
||||||
|
@@ -9,5 +9,9 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
void SetSize(int width, int height);
|
void SetSize(int width, int height);
|
||||||
|
|
||||||
void ChangeVSyncMode(bool vsyncEnabled);
|
void ChangeVSyncMode(bool vsyncEnabled);
|
||||||
|
|
||||||
|
void SetAntiAliasing(AntiAliasing antialiasing);
|
||||||
|
void SetScalingFilter(ScalingFilter type);
|
||||||
|
void SetScalingFilterLevel(float level);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -32,5 +32,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void ChangeVSyncMode(bool vsyncEnabled) { }
|
public void ChangeVSyncMode(bool vsyncEnabled) { }
|
||||||
|
|
||||||
|
public void SetAntiAliasing(AntiAliasing effect) { }
|
||||||
|
|
||||||
|
public void SetScalingFilter(ScalingFilter type) { }
|
||||||
|
|
||||||
|
public void SetScalingFilterLevel(float level) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
9
Ryujinx.Graphics.GAL/UpscaleType.cs
Normal file
9
Ryujinx.Graphics.GAL/UpscaleType.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Ryujinx.Graphics.GAL
|
||||||
|
{
|
||||||
|
public enum ScalingFilter
|
||||||
|
{
|
||||||
|
Bilinear,
|
||||||
|
Nearest,
|
||||||
|
Fsr
|
||||||
|
}
|
||||||
|
}
|
@@ -439,7 +439,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
int samplesInY = msaaMode.SamplesInY();
|
int samplesInY = msaaMode.SamplesInY();
|
||||||
|
|
||||||
var scissor = _state.State.ScreenScissorState;
|
var scissor = _state.State.ScreenScissorState;
|
||||||
Size sizeHint = new Size(scissor.X + scissor.Width, scissor.Y + scissor.Height, 1);
|
Size sizeHint = new Size((scissor.X + scissor.Width) * samplesInX, (scissor.Y + scissor.Height) * samplesInY, 1);
|
||||||
|
|
||||||
int clipRegionWidth = int.MaxValue;
|
int clipRegionWidth = int.MaxValue;
|
||||||
int clipRegionHeight = int.MaxValue;
|
int clipRegionHeight = int.MaxValue;
|
||||||
|
@@ -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 = 4369;
|
private const uint CodeGenVersion = 4368;
|
||||||
|
|
||||||
private const string SharedTocFileName = "shared.toc";
|
private const string SharedTocFileName = "shared.toc";
|
||||||
private const string SharedDataFileName = "shared.data";
|
private const string SharedDataFileName = "shared.data";
|
||||||
@@ -774,6 +774,8 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
sBuffers,
|
sBuffers,
|
||||||
textures,
|
textures,
|
||||||
images,
|
images,
|
||||||
|
ShaderIdentification.None,
|
||||||
|
0,
|
||||||
dataInfo.Stage,
|
dataInfo.Stage,
|
||||||
dataInfo.UsesInstanceId,
|
dataInfo.UsesInstanceId,
|
||||||
dataInfo.UsesDrawParameters,
|
dataInfo.UsesDrawParameters,
|
||||||
|
@@ -633,6 +633,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!_context.Capabilities.SupportsGeometryShader)
|
||||||
|
{
|
||||||
|
ShaderCache.TryRemoveGeometryStage(translatorContexts);
|
||||||
|
}
|
||||||
|
|
||||||
CachedShaderStage[] shaders = new CachedShaderStage[guestShaders.Length];
|
CachedShaderStage[] shaders = new CachedShaderStage[guestShaders.Length];
|
||||||
List<ShaderProgram> translatedStages = new List<ShaderProgram>();
|
List<ShaderProgram> translatedStages = new List<ShaderProgram>();
|
||||||
|
|
||||||
|
@@ -126,6 +126,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
|
|
||||||
public bool QueryHostSupportsFragmentShaderOrderingIntel() => _context.Capabilities.SupportsFragmentShaderOrderingIntel;
|
public bool QueryHostSupportsFragmentShaderOrderingIntel() => _context.Capabilities.SupportsFragmentShaderOrderingIntel;
|
||||||
|
|
||||||
|
public bool QueryHostSupportsGeometryShader() => _context.Capabilities.SupportsGeometryShader;
|
||||||
|
|
||||||
public bool QueryHostSupportsGeometryShaderPassthrough() => _context.Capabilities.SupportsGeometryShaderPassthrough;
|
public bool QueryHostSupportsGeometryShaderPassthrough() => _context.Capabilities.SupportsGeometryShaderPassthrough;
|
||||||
|
|
||||||
public bool QueryHostSupportsImageLoadFormatted() => _context.Capabilities.SupportsImageLoadFormatted;
|
public bool QueryHostSupportsImageLoadFormatted() => _context.Capabilities.SupportsImageLoadFormatted;
|
||||||
|
@@ -353,6 +353,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!_context.Capabilities.SupportsGeometryShader)
|
||||||
|
{
|
||||||
|
TryRemoveGeometryStage(translatorContexts);
|
||||||
|
}
|
||||||
|
|
||||||
CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1];
|
CachedShaderStage[] shaders = new CachedShaderStage[Constants.ShaderStages + 1];
|
||||||
List<ShaderSource> shaderSources = new List<ShaderSource>();
|
List<ShaderSource> shaderSources = new List<ShaderSource>();
|
||||||
|
|
||||||
@@ -421,6 +426,39 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
return gpShaders;
|
return gpShaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to eliminate the geometry stage from the array of translator contexts.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="translatorContexts">Array of translator contexts</param>
|
||||||
|
public static void TryRemoveGeometryStage(TranslatorContext[] translatorContexts)
|
||||||
|
{
|
||||||
|
if (translatorContexts[4] != null)
|
||||||
|
{
|
||||||
|
// We have a geometry shader, but geometry shaders are not supported.
|
||||||
|
// Try to eliminate the geometry shader.
|
||||||
|
|
||||||
|
ShaderProgramInfo info = translatorContexts[4].Translate().Info;
|
||||||
|
|
||||||
|
if (info.Identification == ShaderIdentification.GeometryLayerPassthrough)
|
||||||
|
{
|
||||||
|
// We managed to identify that this geometry shader is only used to set the output Layer value,
|
||||||
|
// we can set the Layer on the previous stage instead (usually the vertex stage) and eliminate it.
|
||||||
|
|
||||||
|
for (int i = 3; i >= 1; i--)
|
||||||
|
{
|
||||||
|
if (translatorContexts[i] != null)
|
||||||
|
{
|
||||||
|
translatorContexts[i].SetGeometryShaderLayerInputAttribute(info.GpLayerInputAttribute);
|
||||||
|
translatorContexts[i].SetLastInVertexPipeline(translatorContexts[5] != null);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
translatorContexts[4] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a shader source for use with the backend from a translated shader program.
|
/// Creates a shader source for use with the backend from a translated shader program.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
177
Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs
Normal file
177
Ryujinx.Graphics.OpenGL/Effects/FsrScalingFilter.cs
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.OpenGL.Image;
|
||||||
|
using System;
|
||||||
|
using static Ryujinx.Graphics.OpenGL.Effects.ShaderHelper;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.OpenGL.Effects
|
||||||
|
{
|
||||||
|
internal class FsrScalingFilter : IScalingFilter
|
||||||
|
{
|
||||||
|
private readonly OpenGLRenderer _renderer;
|
||||||
|
private int _inputUniform;
|
||||||
|
private int _outputUniform;
|
||||||
|
private int _sharpeningUniform;
|
||||||
|
private int _srcX0Uniform;
|
||||||
|
private int _srcX1Uniform;
|
||||||
|
private int _srcY0Uniform;
|
||||||
|
private int _scalingShaderProgram;
|
||||||
|
private int _sharpeningShaderProgram;
|
||||||
|
private float _scale = 1;
|
||||||
|
private int _srcY1Uniform;
|
||||||
|
private int _dstX0Uniform;
|
||||||
|
private int _dstX1Uniform;
|
||||||
|
private int _dstY0Uniform;
|
||||||
|
private int _dstY1Uniform;
|
||||||
|
private int _scaleXUniform;
|
||||||
|
private int _scaleYUniform;
|
||||||
|
private TextureStorage _intermediaryTexture;
|
||||||
|
|
||||||
|
public float Level
|
||||||
|
{
|
||||||
|
get => _scale;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_scale = MathF.Max(0.01f, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public FsrScalingFilter(OpenGLRenderer renderer, IPostProcessingEffect filter)
|
||||||
|
{
|
||||||
|
Initialize();
|
||||||
|
|
||||||
|
_renderer = renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_scalingShaderProgram != 0)
|
||||||
|
{
|
||||||
|
GL.DeleteProgram(_scalingShaderProgram);
|
||||||
|
GL.DeleteProgram(_sharpeningShaderProgram);
|
||||||
|
}
|
||||||
|
|
||||||
|
_intermediaryTexture?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Initialize()
|
||||||
|
{
|
||||||
|
var scalingShader = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_scaling.glsl");
|
||||||
|
var sharpeningShader = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_sharpening.glsl");
|
||||||
|
var fsrA = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_a.h");
|
||||||
|
var fsr1 = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_fsr1.h");
|
||||||
|
|
||||||
|
scalingShader = scalingShader.Replace("#include \"ffx_a.h\"", fsrA);
|
||||||
|
scalingShader = scalingShader.Replace("#include \"ffx_fsr1.h\"", fsr1);
|
||||||
|
sharpeningShader = sharpeningShader.Replace("#include \"ffx_a.h\"", fsrA);
|
||||||
|
sharpeningShader = sharpeningShader.Replace("#include \"ffx_fsr1.h\"", fsr1);
|
||||||
|
|
||||||
|
_scalingShaderProgram = CompileProgram(scalingShader, ShaderType.ComputeShader);
|
||||||
|
_sharpeningShaderProgram = CompileProgram(sharpeningShader, ShaderType.ComputeShader);
|
||||||
|
|
||||||
|
_inputUniform = GL.GetUniformLocation(_scalingShaderProgram, "Source");
|
||||||
|
_outputUniform = GL.GetUniformLocation(_scalingShaderProgram, "imgOutput");
|
||||||
|
_sharpeningUniform = GL.GetUniformLocation(_sharpeningShaderProgram, "sharpening");
|
||||||
|
|
||||||
|
_srcX0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcX0");
|
||||||
|
_srcX1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcX1");
|
||||||
|
_srcY0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcY0");
|
||||||
|
_srcY1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "srcY1");
|
||||||
|
_dstX0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstX0");
|
||||||
|
_dstX1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstX1");
|
||||||
|
_dstY0Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstY0");
|
||||||
|
_dstY1Uniform = GL.GetUniformLocation(_scalingShaderProgram, "dstY1");
|
||||||
|
_scaleXUniform = GL.GetUniformLocation(_scalingShaderProgram, "scaleX");
|
||||||
|
_scaleYUniform = GL.GetUniformLocation(_scalingShaderProgram, "scaleY");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run(
|
||||||
|
TextureView view,
|
||||||
|
TextureView destinationTexture,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
Extents2D source,
|
||||||
|
Extents2D destination)
|
||||||
|
{
|
||||||
|
if (_intermediaryTexture == null || _intermediaryTexture.Info.Width != width || _intermediaryTexture.Info.Height != height)
|
||||||
|
{
|
||||||
|
_intermediaryTexture?.Dispose();
|
||||||
|
var originalInfo = view.Info;
|
||||||
|
var info = new TextureCreateInfo(width,
|
||||||
|
height,
|
||||||
|
originalInfo.Depth,
|
||||||
|
originalInfo.Levels,
|
||||||
|
originalInfo.Samples,
|
||||||
|
originalInfo.BlockWidth,
|
||||||
|
originalInfo.BlockHeight,
|
||||||
|
originalInfo.BytesPerPixel,
|
||||||
|
originalInfo.Format,
|
||||||
|
originalInfo.DepthStencilMode,
|
||||||
|
originalInfo.Target,
|
||||||
|
originalInfo.SwizzleR,
|
||||||
|
originalInfo.SwizzleG,
|
||||||
|
originalInfo.SwizzleB,
|
||||||
|
originalInfo.SwizzleA);
|
||||||
|
|
||||||
|
_intermediaryTexture = new TextureStorage(_renderer, info, view.ScaleFactor);
|
||||||
|
_intermediaryTexture.CreateDefaultView();
|
||||||
|
}
|
||||||
|
|
||||||
|
var textureView = _intermediaryTexture.CreateView(_intermediaryTexture.Info, 0, 0) as TextureView;
|
||||||
|
|
||||||
|
int previousProgram = GL.GetInteger(GetPName.CurrentProgram);
|
||||||
|
int previousUnit = GL.GetInteger(GetPName.ActiveTexture);
|
||||||
|
GL.ActiveTexture(TextureUnit.Texture0);
|
||||||
|
int previousTextureBinding = GL.GetInteger(GetPName.TextureBinding2D);
|
||||||
|
|
||||||
|
GL.BindImageTexture(0, textureView.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
|
||||||
|
|
||||||
|
int threadGroupWorkRegionDim = 16;
|
||||||
|
int dispatchX = (width + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
||||||
|
int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
||||||
|
|
||||||
|
// Scaling pass
|
||||||
|
float srcWidth = Math.Abs(source.X2 - source.X1);
|
||||||
|
float srcHeight = Math.Abs(source.Y2 - source.Y1);
|
||||||
|
float scaleX = srcWidth / view.Width;
|
||||||
|
float scaleY = srcHeight / view.Height;
|
||||||
|
GL.UseProgram(_scalingShaderProgram);
|
||||||
|
view.Bind(0);
|
||||||
|
GL.Uniform1(_inputUniform, 0);
|
||||||
|
GL.Uniform1(_outputUniform, 0);
|
||||||
|
GL.Uniform1(_srcX0Uniform, (float)source.X1);
|
||||||
|
GL.Uniform1(_srcX1Uniform, (float)source.X2);
|
||||||
|
GL.Uniform1(_srcY0Uniform, (float)source.Y1);
|
||||||
|
GL.Uniform1(_srcY1Uniform, (float)source.Y2);
|
||||||
|
GL.Uniform1(_dstX0Uniform, (float)destination.X1);
|
||||||
|
GL.Uniform1(_dstX1Uniform, (float)destination.X2);
|
||||||
|
GL.Uniform1(_dstY0Uniform, (float)destination.Y1);
|
||||||
|
GL.Uniform1(_dstY1Uniform, (float)destination.Y2);
|
||||||
|
GL.Uniform1(_scaleXUniform, scaleX);
|
||||||
|
GL.Uniform1(_scaleYUniform, scaleY);
|
||||||
|
GL.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
|
|
||||||
|
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
||||||
|
|
||||||
|
// Sharpening Pass
|
||||||
|
GL.UseProgram(_sharpeningShaderProgram);
|
||||||
|
GL.BindImageTexture(0, destinationTexture.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
|
||||||
|
textureView.Bind(0);
|
||||||
|
GL.Uniform1(_inputUniform, 0);
|
||||||
|
GL.Uniform1(_outputUniform, 0);
|
||||||
|
GL.Uniform1(_sharpeningUniform, 1.5f - (Level * 0.01f * 1.5f));
|
||||||
|
GL.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
|
|
||||||
|
GL.UseProgram(previousProgram);
|
||||||
|
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
||||||
|
|
||||||
|
(_renderer.Pipeline as Pipeline).RestoreImages1And2();
|
||||||
|
|
||||||
|
GL.ActiveTexture(TextureUnit.Texture0);
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding);
|
||||||
|
|
||||||
|
GL.ActiveTexture((TextureUnit)previousUnit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
81
Ryujinx.Graphics.OpenGL/Effects/FxaaPostProcessingEffect.cs
Normal file
81
Ryujinx.Graphics.OpenGL/Effects/FxaaPostProcessingEffect.cs
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Graphics.OpenGL.Image;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.OpenGL.Effects
|
||||||
|
{
|
||||||
|
internal class FxaaPostProcessingEffect : IPostProcessingEffect
|
||||||
|
{
|
||||||
|
private readonly OpenGLRenderer _renderer;
|
||||||
|
private int _resolutionUniform;
|
||||||
|
private int _inputUniform;
|
||||||
|
private int _outputUniform;
|
||||||
|
private int _shaderProgram;
|
||||||
|
private TextureStorage _textureStorage;
|
||||||
|
|
||||||
|
public FxaaPostProcessingEffect(OpenGLRenderer renderer)
|
||||||
|
{
|
||||||
|
Initialize();
|
||||||
|
|
||||||
|
_renderer = renderer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_shaderProgram != 0)
|
||||||
|
{
|
||||||
|
GL.DeleteProgram(_shaderProgram);
|
||||||
|
_textureStorage?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Initialize()
|
||||||
|
{
|
||||||
|
_shaderProgram = ShaderHelper.CompileProgram(EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/fxaa.glsl"), ShaderType.ComputeShader);
|
||||||
|
|
||||||
|
_resolutionUniform = GL.GetUniformLocation(_shaderProgram, "invResolution");
|
||||||
|
_inputUniform = GL.GetUniformLocation(_shaderProgram, "inputTexture");
|
||||||
|
_outputUniform = GL.GetUniformLocation(_shaderProgram, "imgOutput");
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextureView Run(TextureView view, int width, int height)
|
||||||
|
{
|
||||||
|
if (_textureStorage == null || _textureStorage.Info.Width != view.Width || _textureStorage.Info.Height != view.Height)
|
||||||
|
{
|
||||||
|
_textureStorage?.Dispose();
|
||||||
|
_textureStorage = new TextureStorage(_renderer, view.Info, view.ScaleFactor);
|
||||||
|
_textureStorage.CreateDefaultView();
|
||||||
|
}
|
||||||
|
|
||||||
|
var textureView = _textureStorage.CreateView(view.Info, 0, 0) as TextureView;
|
||||||
|
|
||||||
|
int previousProgram = GL.GetInteger(GetPName.CurrentProgram);
|
||||||
|
int previousUnit = GL.GetInteger(GetPName.ActiveTexture);
|
||||||
|
GL.ActiveTexture(TextureUnit.Texture0);
|
||||||
|
int previousTextureBinding = GL.GetInteger(GetPName.TextureBinding2D);
|
||||||
|
|
||||||
|
GL.BindImageTexture(0, textureView.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
|
||||||
|
GL.UseProgram(_shaderProgram);
|
||||||
|
|
||||||
|
var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize);
|
||||||
|
var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize);
|
||||||
|
|
||||||
|
view.Bind(0);
|
||||||
|
GL.Uniform1(_inputUniform, 0);
|
||||||
|
GL.Uniform1(_outputUniform, 0);
|
||||||
|
GL.Uniform2(_resolutionUniform, (float)view.Width, (float)view.Height);
|
||||||
|
GL.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
|
GL.UseProgram(previousProgram);
|
||||||
|
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
||||||
|
|
||||||
|
(_renderer.Pipeline as Pipeline).RestoreImages1And2();
|
||||||
|
|
||||||
|
GL.ActiveTexture(TextureUnit.Texture0);
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding);
|
||||||
|
|
||||||
|
GL.ActiveTexture((TextureUnit)previousUnit);
|
||||||
|
|
||||||
|
return textureView;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
Ryujinx.Graphics.OpenGL/Effects/IPostProcessingEffect.cs
Normal file
11
Ryujinx.Graphics.OpenGL/Effects/IPostProcessingEffect.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using Ryujinx.Graphics.OpenGL.Image;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.OpenGL.Effects
|
||||||
|
{
|
||||||
|
internal interface IPostProcessingEffect : IDisposable
|
||||||
|
{
|
||||||
|
const int LocalGroupSize = 64;
|
||||||
|
TextureView Run(TextureView view, int width, int height);
|
||||||
|
}
|
||||||
|
}
|
18
Ryujinx.Graphics.OpenGL/Effects/IScalingFilter.cs
Normal file
18
Ryujinx.Graphics.OpenGL/Effects/IScalingFilter.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.OpenGL.Image;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.OpenGL.Effects
|
||||||
|
{
|
||||||
|
internal interface IScalingFilter : IDisposable
|
||||||
|
{
|
||||||
|
float Level { get; set; }
|
||||||
|
void Run(
|
||||||
|
TextureView view,
|
||||||
|
TextureView destinationTexture,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
Extents2D source,
|
||||||
|
Extents2D destination);
|
||||||
|
}
|
||||||
|
}
|
40
Ryujinx.Graphics.OpenGL/Effects/ShaderHelper.cs
Normal file
40
Ryujinx.Graphics.OpenGL/Effects/ShaderHelper.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.OpenGL.Effects
|
||||||
|
{
|
||||||
|
internal static class ShaderHelper
|
||||||
|
{
|
||||||
|
public static int CompileProgram(string shaderCode, ShaderType shaderType)
|
||||||
|
{
|
||||||
|
var shader = GL.CreateShader(shaderType);
|
||||||
|
GL.ShaderSource(shader, shaderCode);
|
||||||
|
GL.CompileShader(shader);
|
||||||
|
|
||||||
|
var program = GL.CreateProgram();
|
||||||
|
GL.AttachShader(program, shader);
|
||||||
|
GL.LinkProgram(program);
|
||||||
|
|
||||||
|
GL.DetachShader(program, shader);
|
||||||
|
GL.DeleteShader(shader);
|
||||||
|
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int CompileProgram(string[] shaders, ShaderType shaderType)
|
||||||
|
{
|
||||||
|
var shader = GL.CreateShader(shaderType);
|
||||||
|
GL.ShaderSource(shader, shaders.Length, shaders, (int[])null);
|
||||||
|
GL.CompileShader(shader);
|
||||||
|
|
||||||
|
var program = GL.CreateProgram();
|
||||||
|
GL.AttachShader(program, shader);
|
||||||
|
GL.LinkProgram(program);
|
||||||
|
|
||||||
|
GL.DetachShader(program, shader);
|
||||||
|
GL.DeleteShader(shader);
|
||||||
|
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2656
Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_a.h
Normal file
2656
Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_a.h
Normal file
File diff suppressed because it is too large
Load Diff
1199
Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_fsr1.h
Normal file
1199
Ryujinx.Graphics.OpenGL/Effects/Shaders/ffx_fsr1.h
Normal file
File diff suppressed because it is too large
Load Diff
88
Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_scaling.glsl
Normal file
88
Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_scaling.glsl
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
#version 430 core
|
||||||
|
precision mediump float;
|
||||||
|
layout (local_size_x = 64) in;
|
||||||
|
layout(rgba8, binding = 0, location=0) uniform image2D imgOutput;
|
||||||
|
layout( location=1 ) uniform sampler2D Source;
|
||||||
|
layout( location=2 ) uniform float srcX0;
|
||||||
|
layout( location=3 ) uniform float srcX1;
|
||||||
|
layout( location=4 ) uniform float srcY0;
|
||||||
|
layout( location=5 ) uniform float srcY1;
|
||||||
|
layout( location=6 ) uniform float dstX0;
|
||||||
|
layout( location=7 ) uniform float dstX1;
|
||||||
|
layout( location=8 ) uniform float dstY0;
|
||||||
|
layout( location=9 ) uniform float dstY1;
|
||||||
|
layout( location=10 ) uniform float scaleX;
|
||||||
|
layout( location=11 ) uniform float scaleY;
|
||||||
|
|
||||||
|
#define A_GPU 1
|
||||||
|
#define A_GLSL 1
|
||||||
|
#include "ffx_a.h"
|
||||||
|
|
||||||
|
#define FSR_EASU_F 1
|
||||||
|
AU4 con0, con1, con2, con3;
|
||||||
|
float srcW, srcH, dstW, dstH;
|
||||||
|
vec2 bLeft, tRight;
|
||||||
|
|
||||||
|
AF2 translate(AF2 pos) {
|
||||||
|
return AF2(pos.x * scaleX, pos.y * scaleY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setBounds(vec2 bottomLeft, vec2 topRight) {
|
||||||
|
bLeft = bottomLeft;
|
||||||
|
tRight = topRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
AF2 translateDest(AF2 pos) {
|
||||||
|
AF2 translatedPos = AF2(pos.x, pos.y);
|
||||||
|
translatedPos.x = dstX1 < dstX0 ? dstX1 - translatedPos.x : translatedPos.x;
|
||||||
|
translatedPos.y = dstY0 > dstY1 ? dstY0 + dstY1 - translatedPos.y - 1: translatedPos.y;
|
||||||
|
return translatedPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
AF4 FsrEasuRF(AF2 p) { AF4 res = textureGather(Source, translate(p), 0); return res; }
|
||||||
|
AF4 FsrEasuGF(AF2 p) { AF4 res = textureGather(Source, translate(p), 1); return res; }
|
||||||
|
AF4 FsrEasuBF(AF2 p) { AF4 res = textureGather(Source, translate(p), 2); return res; }
|
||||||
|
|
||||||
|
#include "ffx_fsr1.h"
|
||||||
|
|
||||||
|
float insideBox(vec2 v) {
|
||||||
|
vec2 s = step(bLeft, v) - step(tRight, v);
|
||||||
|
return s.x * s.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CurrFilter(AU2 pos)
|
||||||
|
{
|
||||||
|
if((insideBox(vec2(pos.x, pos.y))) == 0) {
|
||||||
|
imageStore(imgOutput, ASU2(pos.x, pos.y), AF4(0,0,0,1));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AF3 c;
|
||||||
|
FsrEasuF(c, AU2(pos.x - bLeft.x, pos.y - bLeft.y), con0, con1, con2, con3);
|
||||||
|
imageStore(imgOutput, ASU2(translateDest(pos)), AF4(c, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
srcW = abs(srcX1 - srcX0);
|
||||||
|
srcH = abs(srcY1 - srcY0);
|
||||||
|
dstW = abs(dstX1 - dstX0);
|
||||||
|
dstH = abs(dstY1 - dstY0);
|
||||||
|
|
||||||
|
AU2 gxy = ARmp8x8(gl_LocalInvocationID.x) + AU2(gl_WorkGroupID.x << 4u, gl_WorkGroupID.y << 4u);
|
||||||
|
|
||||||
|
setBounds(vec2(dstX0 < dstX1 ? dstX0 : dstX1, dstY0 < dstY1 ? dstY0 : dstY1),
|
||||||
|
vec2(dstX1 > dstX0 ? dstX1 : dstX0, dstY1 > dstY0 ? dstY1 : dstY0));
|
||||||
|
|
||||||
|
// Upscaling
|
||||||
|
FsrEasuCon(con0, con1, con2, con3,
|
||||||
|
srcW, srcH, // Viewport size (top left aligned) in the input image which is to be scaled.
|
||||||
|
srcW, srcH, // The size of the input image.
|
||||||
|
dstW, dstH); // The output resolution.
|
||||||
|
|
||||||
|
CurrFilter(gxy);
|
||||||
|
gxy.x += 8u;
|
||||||
|
CurrFilter(gxy);
|
||||||
|
gxy.y += 8u;
|
||||||
|
CurrFilter(gxy);
|
||||||
|
gxy.x -= 8u;
|
||||||
|
CurrFilter(gxy);
|
||||||
|
}
|
37
Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_sharpening.glsl
Normal file
37
Ryujinx.Graphics.OpenGL/Effects/Shaders/fsr_sharpening.glsl
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#version 430 core
|
||||||
|
precision mediump float;
|
||||||
|
layout (local_size_x = 64) in;
|
||||||
|
layout(rgba8, binding = 0, location=0) uniform image2D imgOutput;
|
||||||
|
layout( location=1 ) uniform sampler2D source;
|
||||||
|
layout( location=2 ) uniform float sharpening;
|
||||||
|
|
||||||
|
#define A_GPU 1
|
||||||
|
#define A_GLSL 1
|
||||||
|
#include "ffx_a.h"
|
||||||
|
|
||||||
|
#define FSR_RCAS_F 1
|
||||||
|
AU4 con0;
|
||||||
|
|
||||||
|
AF4 FsrRcasLoadF(ASU2 p) { return AF4(texelFetch(source, p, 0)); }
|
||||||
|
void FsrRcasInputF(inout AF1 r, inout AF1 g, inout AF1 b) {}
|
||||||
|
|
||||||
|
#include "ffx_fsr1.h"
|
||||||
|
|
||||||
|
void CurrFilter(AU2 pos)
|
||||||
|
{
|
||||||
|
AF3 c;
|
||||||
|
FsrRcasF(c.r, c.g, c.b, pos, con0);
|
||||||
|
imageStore(imgOutput, ASU2(pos), AF4(c, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
FsrRcasCon(con0, sharpening);
|
||||||
|
AU2 gxy = ARmp8x8(gl_LocalInvocationID.x) + AU2(gl_WorkGroupID.x << 4u, gl_WorkGroupID.y << 4u);
|
||||||
|
CurrFilter(gxy);
|
||||||
|
gxy.x += 8u;
|
||||||
|
CurrFilter(gxy);
|
||||||
|
gxy.y += 8u;
|
||||||
|
CurrFilter(gxy);
|
||||||
|
gxy.x -= 8u;
|
||||||
|
CurrFilter(gxy);
|
||||||
|
}
|
1174
Ryujinx.Graphics.OpenGL/Effects/Shaders/fxaa.glsl
Normal file
1174
Ryujinx.Graphics.OpenGL/Effects/Shaders/fxaa.glsl
Normal file
File diff suppressed because it is too large
Load Diff
1361
Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa.hlsl
Normal file
1361
Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa.hlsl
Normal file
File diff suppressed because it is too large
Load Diff
26
Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_blend.glsl
Normal file
26
Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_blend.glsl
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
layout(rgba8, binding = 0) uniform image2D imgOutput;
|
||||||
|
|
||||||
|
uniform sampler2D inputTexture;
|
||||||
|
layout( location=0 ) uniform vec2 invResolution;
|
||||||
|
uniform sampler2D samplerArea;
|
||||||
|
uniform sampler2D samplerSearch;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
ivec2 loc = ivec2(gl_GlobalInvocationID.x * 4, gl_GlobalInvocationID.y * 4);
|
||||||
|
for(int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
for(int j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
ivec2 texelCoord = ivec2(loc.x + i, loc.y + j);
|
||||||
|
vec2 coord = (texelCoord + vec2(0.5)) / invResolution;
|
||||||
|
vec2 pixCoord;
|
||||||
|
vec4 offset[3];
|
||||||
|
|
||||||
|
SMAABlendingWeightCalculationVS(coord, pixCoord, offset);
|
||||||
|
|
||||||
|
vec4 oColor = SMAABlendingWeightCalculationPS(coord, pixCoord, offset, inputTexture, samplerArea, samplerSearch, ivec4(0));
|
||||||
|
|
||||||
|
imageStore(imgOutput, texelCoord, oColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
24
Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_edge.glsl
Normal file
24
Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_edge.glsl
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
layout(rgba8, binding = 0) uniform image2D imgOutput;
|
||||||
|
|
||||||
|
uniform sampler2D inputTexture;
|
||||||
|
layout( location=0 ) uniform vec2 invResolution;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec2 loc = ivec2(gl_GlobalInvocationID.x * 4, gl_GlobalInvocationID.y * 4);
|
||||||
|
for(int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
for(int j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
ivec2 texelCoord = ivec2(loc.x + i, loc.y + j);
|
||||||
|
vec2 coord = (texelCoord + vec2(0.5)) / invResolution;
|
||||||
|
vec4 offset[3];
|
||||||
|
SMAAEdgeDetectionVS(coord, offset);
|
||||||
|
vec2 oColor = SMAAColorEdgeDetectionPS(coord, offset, inputTexture);
|
||||||
|
if (oColor != float2(-2.0, -2.0))
|
||||||
|
{
|
||||||
|
imageStore(imgOutput, texelCoord, vec4(oColor, 0.0, 1.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
26
Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_neighbour.glsl
Normal file
26
Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_neighbour.glsl
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
layout(rgba8, binding = 0) uniform image2D imgOutput;
|
||||||
|
|
||||||
|
uniform sampler2D inputTexture;
|
||||||
|
layout( location=0 ) uniform vec2 invResolution;
|
||||||
|
uniform sampler2D samplerBlend;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 loc = ivec2(gl_GlobalInvocationID.x * 4, gl_GlobalInvocationID.y * 4);
|
||||||
|
for(int i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
for(int j = 0; j < 4; j++)
|
||||||
|
{
|
||||||
|
ivec2 texelCoord = ivec2(loc.x + i, loc.y + j);
|
||||||
|
vec2 coord = (texelCoord + vec2(0.5)) / invResolution;
|
||||||
|
vec2 pixCoord;
|
||||||
|
vec4 offset;
|
||||||
|
|
||||||
|
SMAANeighborhoodBlendingVS(coord, offset);
|
||||||
|
|
||||||
|
vec4 oColor = SMAANeighborhoodBlendingPS(coord, offset, inputTexture, samplerBlend);
|
||||||
|
|
||||||
|
imageStore(imgOutput, texelCoord, oColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
261
Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs
Normal file
261
Ryujinx.Graphics.OpenGL/Effects/SmaaPostProcessingEffect.cs
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
using OpenTK.Graphics.OpenGL;
|
||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.OpenGL.Image;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.OpenGL.Effects.Smaa
|
||||||
|
{
|
||||||
|
internal partial class SmaaPostProcessingEffect : IPostProcessingEffect
|
||||||
|
{
|
||||||
|
public const int AreaWidth = 160;
|
||||||
|
public const int AreaHeight = 560;
|
||||||
|
public const int SearchWidth = 64;
|
||||||
|
public const int SearchHeight = 16;
|
||||||
|
|
||||||
|
private readonly OpenGLRenderer _renderer;
|
||||||
|
private TextureStorage _outputTexture;
|
||||||
|
private TextureStorage _searchTexture;
|
||||||
|
private TextureStorage _areaTexture;
|
||||||
|
private int[] _edgeShaderPrograms;
|
||||||
|
private int[] _blendShaderPrograms;
|
||||||
|
private int[] _neighbourShaderPrograms;
|
||||||
|
private TextureStorage _edgeOutputTexture;
|
||||||
|
private TextureStorage _blendOutputTexture;
|
||||||
|
private string[] _qualities;
|
||||||
|
private int _inputUniform;
|
||||||
|
private int _outputUniform;
|
||||||
|
private int _samplerAreaUniform;
|
||||||
|
private int _samplerSearchUniform;
|
||||||
|
private int _samplerBlendUniform;
|
||||||
|
private int _resolutionUniform;
|
||||||
|
private int _quality = 1;
|
||||||
|
|
||||||
|
public int Quality
|
||||||
|
{
|
||||||
|
get => _quality; set
|
||||||
|
{
|
||||||
|
_quality = Math.Clamp(value, 0, _qualities.Length - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public SmaaPostProcessingEffect(OpenGLRenderer renderer, int quality)
|
||||||
|
{
|
||||||
|
_renderer = renderer;
|
||||||
|
|
||||||
|
_edgeShaderPrograms = Array.Empty<int>();
|
||||||
|
_blendShaderPrograms = Array.Empty<int>();
|
||||||
|
_neighbourShaderPrograms = Array.Empty<int>();
|
||||||
|
|
||||||
|
_qualities = new string[] { "SMAA_PRESET_LOW", "SMAA_PRESET_MEDIUM", "SMAA_PRESET_HIGH", "SMAA_PRESET_ULTRA" };
|
||||||
|
|
||||||
|
Quality = quality;
|
||||||
|
|
||||||
|
Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_searchTexture?.Dispose();
|
||||||
|
_areaTexture?.Dispose();
|
||||||
|
_outputTexture?.Dispose();
|
||||||
|
_edgeOutputTexture?.Dispose();
|
||||||
|
_blendOutputTexture?.Dispose();
|
||||||
|
|
||||||
|
DeleteShaders();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DeleteShaders()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < _edgeShaderPrograms.Length; i++)
|
||||||
|
{
|
||||||
|
GL.DeleteProgram(_edgeShaderPrograms[i]);
|
||||||
|
GL.DeleteProgram(_blendShaderPrograms[i]);
|
||||||
|
GL.DeleteProgram(_neighbourShaderPrograms[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe void RecreateShaders(int width, int height)
|
||||||
|
{
|
||||||
|
string baseShader = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa.hlsl");
|
||||||
|
var pixelSizeDefine = $"#define SMAA_RT_METRICS float4(1.0 / {width}.0, 1.0 / {height}.0, {width}, {height}) \n";
|
||||||
|
|
||||||
|
_edgeShaderPrograms = new int[_qualities.Length];
|
||||||
|
_blendShaderPrograms = new int[_qualities.Length];
|
||||||
|
_neighbourShaderPrograms = new int[_qualities.Length];
|
||||||
|
|
||||||
|
for (int i = 0; i < +_edgeShaderPrograms.Length; i++)
|
||||||
|
{
|
||||||
|
var presets = $"#version 430 core \n#define {_qualities[i]} 1 \n{pixelSizeDefine}#define SMAA_GLSL_4 1 \nlayout (local_size_x = 16, local_size_y = 16) in;\n{baseShader}";
|
||||||
|
|
||||||
|
var edgeShaderData = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_edge.glsl");
|
||||||
|
var blendShaderData = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_blend.glsl");
|
||||||
|
var neighbourShaderData = EmbeddedResources.ReadAllText("Ryujinx.Graphics.OpenGL/Effects/Shaders/smaa_neighbour.glsl");
|
||||||
|
|
||||||
|
var shaders = new string[] { presets, edgeShaderData };
|
||||||
|
var edgeProgram = ShaderHelper.CompileProgram(shaders, ShaderType.ComputeShader);
|
||||||
|
|
||||||
|
shaders[1] = blendShaderData;
|
||||||
|
var blendProgram = ShaderHelper.CompileProgram(shaders, ShaderType.ComputeShader);
|
||||||
|
|
||||||
|
shaders[1] = neighbourShaderData;
|
||||||
|
var neighbourProgram = ShaderHelper.CompileProgram(shaders, ShaderType.ComputeShader);
|
||||||
|
|
||||||
|
_edgeShaderPrograms[i] = edgeProgram;
|
||||||
|
_blendShaderPrograms[i] = blendProgram;
|
||||||
|
_neighbourShaderPrograms[i] = neighbourProgram;
|
||||||
|
}
|
||||||
|
|
||||||
|
_inputUniform = GL.GetUniformLocation(_edgeShaderPrograms[0], "inputTexture");
|
||||||
|
_outputUniform = GL.GetUniformLocation(_edgeShaderPrograms[0], "imgOutput");
|
||||||
|
_samplerAreaUniform = GL.GetUniformLocation(_blendShaderPrograms[0], "samplerArea");
|
||||||
|
_samplerSearchUniform = GL.GetUniformLocation(_blendShaderPrograms[0], "samplerSearch");
|
||||||
|
_samplerBlendUniform = GL.GetUniformLocation(_neighbourShaderPrograms[0], "samplerBlend");
|
||||||
|
_resolutionUniform = GL.GetUniformLocation(_edgeShaderPrograms[0], "invResolution");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Initialize()
|
||||||
|
{
|
||||||
|
var areaInfo = new TextureCreateInfo(AreaWidth,
|
||||||
|
AreaHeight,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
Format.R8G8Unorm,
|
||||||
|
DepthStencilMode.Depth,
|
||||||
|
Target.Texture2D,
|
||||||
|
SwizzleComponent.Red,
|
||||||
|
SwizzleComponent.Green,
|
||||||
|
SwizzleComponent.Blue,
|
||||||
|
SwizzleComponent.Alpha);
|
||||||
|
|
||||||
|
var searchInfo = new TextureCreateInfo(SearchWidth,
|
||||||
|
SearchHeight,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
Format.R8Unorm,
|
||||||
|
DepthStencilMode.Depth,
|
||||||
|
Target.Texture2D,
|
||||||
|
SwizzleComponent.Red,
|
||||||
|
SwizzleComponent.Green,
|
||||||
|
SwizzleComponent.Blue,
|
||||||
|
SwizzleComponent.Alpha);
|
||||||
|
|
||||||
|
_areaTexture = new TextureStorage(_renderer, areaInfo, 1);
|
||||||
|
_searchTexture = new TextureStorage(_renderer, searchInfo, 1);
|
||||||
|
|
||||||
|
var areaTexture = EmbeddedResources.Read("Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaAreaTexture.bin");
|
||||||
|
var searchTexture = EmbeddedResources.Read("Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaSearchTexture.bin");
|
||||||
|
|
||||||
|
var areaView = _areaTexture.CreateDefaultView();
|
||||||
|
var searchView = _searchTexture.CreateDefaultView();
|
||||||
|
|
||||||
|
areaView.SetData(areaTexture);
|
||||||
|
searchView.SetData(searchTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextureView Run(TextureView view, int width, int height)
|
||||||
|
{
|
||||||
|
if (_outputTexture == null || _outputTexture.Info.Width != view.Width || _outputTexture.Info.Height != view.Height)
|
||||||
|
{
|
||||||
|
_outputTexture?.Dispose();
|
||||||
|
_outputTexture = new TextureStorage(_renderer, view.Info, view.ScaleFactor);
|
||||||
|
_outputTexture.CreateDefaultView();
|
||||||
|
_edgeOutputTexture = new TextureStorage(_renderer, view.Info, view.ScaleFactor);
|
||||||
|
_edgeOutputTexture.CreateDefaultView();
|
||||||
|
_blendOutputTexture = new TextureStorage(_renderer, view.Info, view.ScaleFactor);
|
||||||
|
_blendOutputTexture.CreateDefaultView();
|
||||||
|
|
||||||
|
DeleteShaders();
|
||||||
|
|
||||||
|
RecreateShaders(view.Width, view.Height);
|
||||||
|
}
|
||||||
|
|
||||||
|
var textureView = _outputTexture.CreateView(view.Info, 0, 0) as TextureView;
|
||||||
|
var edgeOutput = _edgeOutputTexture.DefaultView as TextureView;
|
||||||
|
var blendOutput = _blendOutputTexture.DefaultView as TextureView;
|
||||||
|
var areaTexture = _areaTexture.DefaultView as TextureView;
|
||||||
|
var searchTexture = _searchTexture.DefaultView as TextureView;
|
||||||
|
|
||||||
|
var previousFramebuffer = GL.GetInteger(GetPName.FramebufferBinding);
|
||||||
|
int previousUnit = GL.GetInteger(GetPName.ActiveTexture);
|
||||||
|
GL.ActiveTexture(TextureUnit.Texture0);
|
||||||
|
int previousTextureBinding0 = GL.GetInteger(GetPName.TextureBinding2D);
|
||||||
|
GL.ActiveTexture(TextureUnit.Texture1);
|
||||||
|
int previousTextureBinding1 = GL.GetInteger(GetPName.TextureBinding2D);
|
||||||
|
GL.ActiveTexture(TextureUnit.Texture2);
|
||||||
|
int previousTextureBinding2 = GL.GetInteger(GetPName.TextureBinding2D);
|
||||||
|
|
||||||
|
var framebuffer = new Framebuffer();
|
||||||
|
framebuffer.Bind();
|
||||||
|
framebuffer.AttachColor(0, edgeOutput);
|
||||||
|
GL.Clear(ClearBufferMask.ColorBufferBit);
|
||||||
|
GL.ClearColor(0, 0, 0, 0);
|
||||||
|
framebuffer.AttachColor(0, blendOutput);
|
||||||
|
GL.Clear(ClearBufferMask.ColorBufferBit);
|
||||||
|
GL.ClearColor(0, 0, 0, 0);
|
||||||
|
|
||||||
|
GL.BindFramebuffer(FramebufferTarget.Framebuffer, previousFramebuffer);
|
||||||
|
|
||||||
|
framebuffer.Dispose();
|
||||||
|
|
||||||
|
var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize);
|
||||||
|
var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize);
|
||||||
|
|
||||||
|
int previousProgram = GL.GetInteger(GetPName.CurrentProgram);
|
||||||
|
GL.BindImageTexture(0, edgeOutput.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
|
||||||
|
GL.UseProgram(_edgeShaderPrograms[Quality]);
|
||||||
|
view.Bind(0);
|
||||||
|
GL.Uniform1(_inputUniform, 0);
|
||||||
|
GL.Uniform1(_outputUniform, 0);
|
||||||
|
GL.Uniform2(_resolutionUniform, (float)view.Width, (float)view.Height);
|
||||||
|
GL.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
|
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
||||||
|
|
||||||
|
GL.BindImageTexture(0, blendOutput.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
|
||||||
|
GL.UseProgram(_blendShaderPrograms[Quality]);
|
||||||
|
edgeOutput.Bind(0);
|
||||||
|
areaTexture.Bind(1);
|
||||||
|
searchTexture.Bind(2);
|
||||||
|
GL.Uniform1(_inputUniform, 0);
|
||||||
|
GL.Uniform1(_outputUniform, 0);
|
||||||
|
GL.Uniform1(_samplerAreaUniform, 1);
|
||||||
|
GL.Uniform1(_samplerSearchUniform, 2);
|
||||||
|
GL.Uniform2(_resolutionUniform, (float)view.Width, (float)view.Height);
|
||||||
|
GL.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
|
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
||||||
|
|
||||||
|
GL.BindImageTexture(0, textureView.Handle, 0, false, 0, TextureAccess.ReadWrite, SizedInternalFormat.Rgba8);
|
||||||
|
GL.UseProgram(_neighbourShaderPrograms[Quality]);
|
||||||
|
view.Bind(0);
|
||||||
|
blendOutput.Bind(1);
|
||||||
|
GL.Uniform1(_inputUniform, 0);
|
||||||
|
GL.Uniform1(_outputUniform, 0);
|
||||||
|
GL.Uniform1(_samplerBlendUniform, 1);
|
||||||
|
GL.Uniform2(_resolutionUniform, (float)view.Width, (float)view.Height);
|
||||||
|
GL.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
|
GL.MemoryBarrier(MemoryBarrierFlags.ShaderImageAccessBarrierBit);
|
||||||
|
|
||||||
|
(_renderer.Pipeline as Pipeline).RestoreImages1And2();
|
||||||
|
|
||||||
|
GL.UseProgram(previousProgram);
|
||||||
|
|
||||||
|
GL.ActiveTexture(TextureUnit.Texture0);
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding0);
|
||||||
|
GL.ActiveTexture(TextureUnit.Texture1);
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding1);
|
||||||
|
GL.ActiveTexture(TextureUnit.Texture2);
|
||||||
|
GL.BindTexture(TextureTarget.Texture2D, previousTextureBinding2);
|
||||||
|
|
||||||
|
GL.ActiveTexture((TextureUnit)previousUnit);
|
||||||
|
|
||||||
|
return textureView;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaAreaTexture.bin
Normal file
BIN
Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaAreaTexture.bin
Normal file
Binary file not shown.
BIN
Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaSearchTexture.bin
Normal file
BIN
Ryujinx.Graphics.OpenGL/Effects/Textures/SmaaSearchTexture.bin
Normal file
Binary file not shown.
@@ -124,6 +124,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
supportsBlendEquationAdvanced: HwCapabilities.SupportsBlendEquationAdvanced,
|
supportsBlendEquationAdvanced: HwCapabilities.SupportsBlendEquationAdvanced,
|
||||||
supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock,
|
supportsFragmentShaderInterlock: HwCapabilities.SupportsFragmentShaderInterlock,
|
||||||
supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering,
|
supportsFragmentShaderOrderingIntel: HwCapabilities.SupportsFragmentShaderOrdering,
|
||||||
|
supportsGeometryShader: true,
|
||||||
supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough,
|
supportsGeometryShaderPassthrough: HwCapabilities.SupportsGeometryShaderPassthrough,
|
||||||
supportsImageLoadFormatted: HwCapabilities.SupportsImageLoadFormatted,
|
supportsImageLoadFormatted: HwCapabilities.SupportsImageLoadFormatted,
|
||||||
supportsLayerVertexTessellation: HwCapabilities.SupportsShaderViewportLayerArray,
|
supportsLayerVertexTessellation: HwCapabilities.SupportsShaderViewportLayerArray,
|
||||||
|
@@ -9,6 +9,20 @@
|
|||||||
<PackageReference Include="OpenTK.Graphics" />
|
<PackageReference Include="OpenTK.Graphics" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="Effects\Textures\SmaaAreaTexture.bin" />
|
||||||
|
<EmbeddedResource Include="Effects\Textures\SmaaSearchTexture.bin" />
|
||||||
|
<EmbeddedResource Include="Effects\Shaders\fsr_sharpening.glsl" />
|
||||||
|
<EmbeddedResource Include="Effects\Shaders\fxaa.glsl" />
|
||||||
|
<EmbeddedResource Include="Effects\Shaders\smaa.hlsl" />
|
||||||
|
<EmbeddedResource Include="Effects\Shaders\smaa_blend.glsl" />
|
||||||
|
<EmbeddedResource Include="Effects\Shaders\smaa_edge.glsl" />
|
||||||
|
<EmbeddedResource Include="Effects\Shaders\smaa_neighbour.glsl" />
|
||||||
|
<EmbeddedResource Include="Effects\Shaders\ffx_fsr1.h" />
|
||||||
|
<EmbeddedResource Include="Effects\Shaders\ffx_a.h" />
|
||||||
|
<EmbeddedResource Include="Effects\Shaders\fsr_scaling.glsl" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj" />
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
using OpenTK.Graphics.OpenGL;
|
using OpenTK.Graphics.OpenGL;
|
||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.OpenGL.Effects;
|
||||||
|
using Ryujinx.Graphics.OpenGL.Effects.Smaa;
|
||||||
using Ryujinx.Graphics.OpenGL.Image;
|
using Ryujinx.Graphics.OpenGL.Image;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
@@ -7,14 +9,24 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
{
|
{
|
||||||
class Window : IWindow, IDisposable
|
class Window : IWindow, IDisposable
|
||||||
{
|
{
|
||||||
private const int TextureCount = 3;
|
|
||||||
private readonly OpenGLRenderer _renderer;
|
private readonly OpenGLRenderer _renderer;
|
||||||
|
|
||||||
private bool _initialized;
|
private bool _initialized;
|
||||||
|
|
||||||
private int _width;
|
private int _width;
|
||||||
private int _height;
|
private int _height;
|
||||||
|
private bool _updateSize;
|
||||||
private int _copyFramebufferHandle;
|
private int _copyFramebufferHandle;
|
||||||
|
private IPostProcessingEffect _antiAliasing;
|
||||||
|
private IScalingFilter _scalingFilter;
|
||||||
|
private bool _isLinear;
|
||||||
|
private AntiAliasing _currentAntiAliasing;
|
||||||
|
private bool _updateEffect;
|
||||||
|
private ScalingFilter _currentScalingFilter;
|
||||||
|
private float _scalingFilterLevel;
|
||||||
|
private bool _updateScalingFilter;
|
||||||
|
private bool _isBgra;
|
||||||
|
private TextureView _upscaledTexture;
|
||||||
|
|
||||||
internal BackgroundContextWorker BackgroundContext { get; private set; }
|
internal BackgroundContextWorker BackgroundContext { get; private set; }
|
||||||
|
|
||||||
@@ -48,6 +60,8 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
{
|
{
|
||||||
_width = width;
|
_width = width;
|
||||||
_height = height;
|
_height = height;
|
||||||
|
|
||||||
|
_updateSize = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CopyTextureToFrameBufferRGB(int drawFramebuffer, int readFramebuffer, TextureView view, ImageCrop crop, Action swapBuffersCallback)
|
private void CopyTextureToFrameBufferRGB(int drawFramebuffer, int readFramebuffer, TextureView view, ImageCrop crop, Action swapBuffersCallback)
|
||||||
@@ -57,6 +71,32 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
|
|
||||||
TextureView viewConverted = view.Format.IsBgr() ? _renderer.TextureCopy.BgraSwap(view) : view;
|
TextureView viewConverted = view.Format.IsBgr() ? _renderer.TextureCopy.BgraSwap(view) : view;
|
||||||
|
|
||||||
|
UpdateEffect();
|
||||||
|
|
||||||
|
if (_antiAliasing != null)
|
||||||
|
{
|
||||||
|
var oldView = viewConverted;
|
||||||
|
|
||||||
|
viewConverted = _antiAliasing.Run(viewConverted, _width, _height);
|
||||||
|
|
||||||
|
if (viewConverted.Format.IsBgr())
|
||||||
|
{
|
||||||
|
var swappedView = _renderer.TextureCopy.BgraSwap(viewConverted);
|
||||||
|
|
||||||
|
viewConverted?.Dispose();
|
||||||
|
|
||||||
|
viewConverted = swappedView;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (viewConverted != oldView && oldView != view)
|
||||||
|
{
|
||||||
|
oldView.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, drawFramebuffer);
|
||||||
|
GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, readFramebuffer);
|
||||||
|
|
||||||
GL.FramebufferTexture(
|
GL.FramebufferTexture(
|
||||||
FramebufferTarget.ReadFramebuffer,
|
FramebufferTarget.ReadFramebuffer,
|
||||||
FramebufferAttachment.ColorAttachment0,
|
FramebufferAttachment.ColorAttachment0,
|
||||||
@@ -71,12 +111,12 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
GL.Clear(ClearBufferMask.ColorBufferBit);
|
GL.Clear(ClearBufferMask.ColorBufferBit);
|
||||||
|
|
||||||
int srcX0, srcX1, srcY0, srcY1;
|
int srcX0, srcX1, srcY0, srcY1;
|
||||||
float scale = view.ScaleFactor;
|
float scale = viewConverted.ScaleFactor;
|
||||||
|
|
||||||
if (crop.Left == 0 && crop.Right == 0)
|
if (crop.Left == 0 && crop.Right == 0)
|
||||||
{
|
{
|
||||||
srcX0 = 0;
|
srcX0 = 0;
|
||||||
srcX1 = (int)(view.Width / scale);
|
srcX1 = (int)(viewConverted.Width / scale);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -87,7 +127,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
if (crop.Top == 0 && crop.Bottom == 0)
|
if (crop.Top == 0 && crop.Bottom == 0)
|
||||||
{
|
{
|
||||||
srcY0 = 0;
|
srcY0 = 0;
|
||||||
srcY1 = (int)(view.Height / scale);
|
srcY1 = (int)(viewConverted.Height / scale);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -125,6 +165,42 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
ScreenCaptureRequested = false;
|
ScreenCaptureRequested = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_scalingFilter != null)
|
||||||
|
{
|
||||||
|
if (viewConverted.Format.IsBgr() && !_isBgra)
|
||||||
|
{
|
||||||
|
RecreateUpscalingTexture(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
_scalingFilter.Run(
|
||||||
|
viewConverted,
|
||||||
|
_upscaledTexture,
|
||||||
|
_width,
|
||||||
|
_height,
|
||||||
|
new Extents2D(
|
||||||
|
srcX0,
|
||||||
|
srcY0,
|
||||||
|
srcX1,
|
||||||
|
srcY1),
|
||||||
|
new Extents2D(
|
||||||
|
dstX0,
|
||||||
|
dstY0,
|
||||||
|
dstX1,
|
||||||
|
dstY1)
|
||||||
|
);
|
||||||
|
|
||||||
|
srcX0 = dstX0;
|
||||||
|
srcY0 = dstY0;
|
||||||
|
srcX1 = dstX1;
|
||||||
|
srcY1 = dstY1;
|
||||||
|
|
||||||
|
GL.FramebufferTexture(
|
||||||
|
FramebufferTarget.ReadFramebuffer,
|
||||||
|
FramebufferAttachment.ColorAttachment0,
|
||||||
|
_upscaledTexture.Handle,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
GL.BlitFramebuffer(
|
GL.BlitFramebuffer(
|
||||||
srcX0,
|
srcX0,
|
||||||
srcY0,
|
srcY0,
|
||||||
@@ -135,7 +211,7 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
dstX1,
|
dstX1,
|
||||||
dstY1,
|
dstY1,
|
||||||
ClearBufferMask.ColorBufferBit,
|
ClearBufferMask.ColorBufferBit,
|
||||||
BlitFramebufferFilter.Linear);
|
_isLinear ? BlitFramebufferFilter.Linear : BlitFramebufferFilter.Nearest);
|
||||||
|
|
||||||
// Remove Alpha channel
|
// Remove Alpha channel
|
||||||
GL.ColorMask(false, false, false, true);
|
GL.ColorMask(false, false, false, true);
|
||||||
@@ -209,6 +285,135 @@ namespace Ryujinx.Graphics.OpenGL
|
|||||||
|
|
||||||
_copyFramebufferHandle = 0;
|
_copyFramebufferHandle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_antiAliasing?.Dispose();
|
||||||
|
_scalingFilter?.Dispose();
|
||||||
|
_upscaledTexture?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetAntiAliasing(AntiAliasing effect)
|
||||||
|
{
|
||||||
|
if (_currentAntiAliasing == effect && _antiAliasing != null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentAntiAliasing = effect;
|
||||||
|
|
||||||
|
_updateEffect = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetScalingFilter(ScalingFilter type)
|
||||||
|
{
|
||||||
|
if (_currentScalingFilter == type && _antiAliasing != null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentScalingFilter = type;
|
||||||
|
|
||||||
|
_updateScalingFilter = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateEffect()
|
||||||
|
{
|
||||||
|
if (_updateEffect)
|
||||||
|
{
|
||||||
|
_updateEffect = false;
|
||||||
|
|
||||||
|
switch (_currentAntiAliasing)
|
||||||
|
{
|
||||||
|
case AntiAliasing.Fxaa:
|
||||||
|
_antiAliasing?.Dispose();
|
||||||
|
_antiAliasing = new FxaaPostProcessingEffect(_renderer);
|
||||||
|
break;
|
||||||
|
case AntiAliasing.None:
|
||||||
|
_antiAliasing?.Dispose();
|
||||||
|
_antiAliasing = null;
|
||||||
|
break;
|
||||||
|
case AntiAliasing.SmaaLow:
|
||||||
|
case AntiAliasing.SmaaMedium:
|
||||||
|
case AntiAliasing.SmaaHigh:
|
||||||
|
case AntiAliasing.SmaaUltra:
|
||||||
|
var quality = _currentAntiAliasing - AntiAliasing.SmaaLow;
|
||||||
|
if (_antiAliasing is SmaaPostProcessingEffect smaa)
|
||||||
|
{
|
||||||
|
smaa.Quality = quality;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_antiAliasing?.Dispose();
|
||||||
|
_antiAliasing = new SmaaPostProcessingEffect(_renderer, quality);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_updateSize && !_updateScalingFilter)
|
||||||
|
{
|
||||||
|
RecreateUpscalingTexture();
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateSize = false;
|
||||||
|
|
||||||
|
if (_updateScalingFilter)
|
||||||
|
{
|
||||||
|
_updateScalingFilter = false;
|
||||||
|
|
||||||
|
switch (_currentScalingFilter)
|
||||||
|
{
|
||||||
|
case ScalingFilter.Bilinear:
|
||||||
|
case ScalingFilter.Nearest:
|
||||||
|
_scalingFilter?.Dispose();
|
||||||
|
_scalingFilter = null;
|
||||||
|
_isLinear = _currentScalingFilter == ScalingFilter.Bilinear;
|
||||||
|
_upscaledTexture?.Dispose();
|
||||||
|
_upscaledTexture = null;
|
||||||
|
break;
|
||||||
|
case ScalingFilter.Fsr:
|
||||||
|
if (_scalingFilter is not FsrScalingFilter)
|
||||||
|
{
|
||||||
|
_scalingFilter?.Dispose();
|
||||||
|
_scalingFilter = new FsrScalingFilter(_renderer, _antiAliasing);
|
||||||
|
}
|
||||||
|
_isLinear = false;
|
||||||
|
_scalingFilter.Level = _scalingFilterLevel;
|
||||||
|
|
||||||
|
RecreateUpscalingTexture();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RecreateUpscalingTexture(bool forceBgra = false)
|
||||||
|
{
|
||||||
|
_upscaledTexture?.Dispose();
|
||||||
|
|
||||||
|
var info = new TextureCreateInfo(
|
||||||
|
_width,
|
||||||
|
_height,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
Format.R8G8B8A8Unorm,
|
||||||
|
DepthStencilMode.Depth,
|
||||||
|
Target.Texture2D,
|
||||||
|
forceBgra ? SwizzleComponent.Blue : SwizzleComponent.Red,
|
||||||
|
SwizzleComponent.Green,
|
||||||
|
forceBgra ? SwizzleComponent.Red : SwizzleComponent.Blue,
|
||||||
|
SwizzleComponent.Alpha);
|
||||||
|
|
||||||
|
_isBgra = forceBgra;
|
||||||
|
_upscaledTexture = _renderer.CreateTexture(info, 1) as TextureView;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetScalingFilterLevel(float level)
|
||||||
|
{
|
||||||
|
_scalingFilterLevel = level;
|
||||||
|
_updateScalingFilter = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -259,6 +259,15 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queries host GPU geometry shader support.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if the GPU and driver supports geometry shaders, false otherwise</returns>
|
||||||
|
bool QueryHostSupportsGeometryShader()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Queries host GPU geometry shader passthrough support.
|
/// Queries host GPU geometry shader passthrough support.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
8
Ryujinx.Graphics.Shader/ShaderIdentification.cs
Normal file
8
Ryujinx.Graphics.Shader/ShaderIdentification.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
namespace Ryujinx.Graphics.Shader
|
||||||
|
{
|
||||||
|
public enum ShaderIdentification
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
GeometryLayerPassthrough
|
||||||
|
}
|
||||||
|
}
|
@@ -10,6 +10,8 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
public ReadOnlyCollection<TextureDescriptor> Textures { get; }
|
public ReadOnlyCollection<TextureDescriptor> Textures { get; }
|
||||||
public ReadOnlyCollection<TextureDescriptor> Images { get; }
|
public ReadOnlyCollection<TextureDescriptor> Images { get; }
|
||||||
|
|
||||||
|
public ShaderIdentification Identification { get; }
|
||||||
|
public int GpLayerInputAttribute { get; }
|
||||||
public ShaderStage Stage { get; }
|
public ShaderStage Stage { get; }
|
||||||
public bool UsesInstanceId { get; }
|
public bool UsesInstanceId { get; }
|
||||||
public bool UsesDrawParameters { get; }
|
public bool UsesDrawParameters { get; }
|
||||||
@@ -22,6 +24,8 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
BufferDescriptor[] sBuffers,
|
BufferDescriptor[] sBuffers,
|
||||||
TextureDescriptor[] textures,
|
TextureDescriptor[] textures,
|
||||||
TextureDescriptor[] images,
|
TextureDescriptor[] images,
|
||||||
|
ShaderIdentification identification,
|
||||||
|
int gpLayerInputAttribute,
|
||||||
ShaderStage stage,
|
ShaderStage stage,
|
||||||
bool usesInstanceId,
|
bool usesInstanceId,
|
||||||
bool usesDrawParameters,
|
bool usesDrawParameters,
|
||||||
@@ -34,6 +38,8 @@ namespace Ryujinx.Graphics.Shader
|
|||||||
Textures = Array.AsReadOnly(textures);
|
Textures = Array.AsReadOnly(textures);
|
||||||
Images = Array.AsReadOnly(images);
|
Images = Array.AsReadOnly(images);
|
||||||
|
|
||||||
|
Identification = identification;
|
||||||
|
GpLayerInputAttribute = gpLayerInputAttribute;
|
||||||
Stage = stage;
|
Stage = stage;
|
||||||
UsesInstanceId = usesInstanceId;
|
UsesInstanceId = usesInstanceId;
|
||||||
UsesDrawParameters = usesDrawParameters;
|
UsesDrawParameters = usesDrawParameters;
|
||||||
|
@@ -241,6 +241,13 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
this.Copy(Attribute(AttributeConsts.PositionZ), this.FPFusedMultiplyAdd(z, ConstF(0.5f), halfW));
|
this.Copy(Attribute(AttributeConsts.PositionZ), this.FPFusedMultiplyAdd(z, ConstF(0.5f), halfW));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Config.Stage != ShaderStage.Geometry && Config.HasLayerInputAttribute)
|
||||||
|
{
|
||||||
|
Config.SetUsedFeature(FeatureFlags.RtLayer);
|
||||||
|
|
||||||
|
this.Copy(Attribute(AttributeConsts.Layer), Attribute(Config.GpLayerInputAttribute | AttributeConsts.LoadOutputMask));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PrepareForVertexReturn(out Operand oldXLocal, out Operand oldYLocal, out Operand oldZLocal)
|
public void PrepareForVertexReturn(out Operand oldXLocal, out Operand oldYLocal, out Operand oldZLocal)
|
||||||
|
@@ -20,6 +20,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
public bool LastInPipeline { get; private set; }
|
public bool LastInPipeline { get; private set; }
|
||||||
public bool LastInVertexPipeline { get; private set; }
|
public bool LastInVertexPipeline { get; private set; }
|
||||||
|
|
||||||
|
public bool HasLayerInputAttribute { get; private set; }
|
||||||
|
public int GpLayerInputAttribute { get; private set; }
|
||||||
public int ThreadsPerInputPrimitive { get; }
|
public int ThreadsPerInputPrimitive { get; }
|
||||||
|
|
||||||
public OutputTopology OutputTopology { get; }
|
public OutputTopology OutputTopology { get; }
|
||||||
@@ -245,6 +247,22 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
LayerOutputAttribute = attr;
|
LayerOutputAttribute = attr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetGeometryShaderLayerInputAttribute(int attr)
|
||||||
|
{
|
||||||
|
HasLayerInputAttribute = true;
|
||||||
|
GpLayerInputAttribute = attr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetLastInVertexPipeline(bool hasFragment)
|
||||||
|
{
|
||||||
|
if (!hasFragment)
|
||||||
|
{
|
||||||
|
LastInPipeline = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LastInVertexPipeline = true;
|
||||||
|
}
|
||||||
|
|
||||||
public void SetInputUserAttributeFixedFunc(int index)
|
public void SetInputUserAttributeFixedFunc(int index)
|
||||||
{
|
{
|
||||||
UsedInputAttributes |= 1 << index;
|
UsedInputAttributes |= 1 << index;
|
||||||
@@ -706,13 +724,15 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
return FindDescriptorIndex(GetImageDescriptors(), texOp);
|
return FindDescriptorIndex(GetImageDescriptors(), texOp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShaderProgramInfo CreateProgramInfo()
|
public ShaderProgramInfo CreateProgramInfo(ShaderIdentification identification = ShaderIdentification.None)
|
||||||
{
|
{
|
||||||
return new ShaderProgramInfo(
|
return new ShaderProgramInfo(
|
||||||
GetConstantBufferDescriptors(),
|
GetConstantBufferDescriptors(),
|
||||||
GetStorageBufferDescriptors(),
|
GetStorageBufferDescriptors(),
|
||||||
GetTextureDescriptors(),
|
GetTextureDescriptors(),
|
||||||
GetImageDescriptors(),
|
GetImageDescriptors(),
|
||||||
|
identification,
|
||||||
|
GpLayerInputAttribute,
|
||||||
Stage,
|
Stage,
|
||||||
UsedFeatures.HasFlag(FeatureFlags.InstanceId),
|
UsedFeatures.HasFlag(FeatureFlags.InstanceId),
|
||||||
UsedFeatures.HasFlag(FeatureFlags.DrawParameters),
|
UsedFeatures.HasFlag(FeatureFlags.DrawParameters),
|
||||||
|
145
Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs
Normal file
145
Ryujinx.Graphics.Shader/Translation/ShaderIdentifier.cs
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
using Ryujinx.Graphics.Shader.IntermediateRepresentation;
|
||||||
|
using static Ryujinx.Graphics.Shader.IntermediateRepresentation.OperandHelper;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Shader.Translation
|
||||||
|
{
|
||||||
|
static class ShaderIdentifier
|
||||||
|
{
|
||||||
|
public static ShaderIdentification Identify(Function[] functions, ShaderConfig config)
|
||||||
|
{
|
||||||
|
if (config.Stage == ShaderStage.Geometry &&
|
||||||
|
config.GpuAccessor.QueryPrimitiveTopology() == InputTopology.Triangles &&
|
||||||
|
!config.GpuAccessor.QueryHostSupportsGeometryShader() &&
|
||||||
|
IsLayerPassthroughGeometryShader(functions, out int layerInputAttr))
|
||||||
|
{
|
||||||
|
config.SetGeometryShaderLayerInputAttribute(layerInputAttr);
|
||||||
|
|
||||||
|
return ShaderIdentification.GeometryLayerPassthrough;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ShaderIdentification.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsLayerPassthroughGeometryShader(Function[] functions, out int layerInputAttr)
|
||||||
|
{
|
||||||
|
bool writesLayer = false;
|
||||||
|
layerInputAttr = 0;
|
||||||
|
|
||||||
|
if (functions.Length != 1)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int verticesCount = 0;
|
||||||
|
int totalVerticesCount = 0;
|
||||||
|
|
||||||
|
foreach (BasicBlock block in functions[0].Blocks)
|
||||||
|
{
|
||||||
|
// We are not expecting loops or any complex control flow here, so fail in those cases.
|
||||||
|
if (block.Branch != null && block.Branch.Index <= block.Index)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (INode node in block.Operations)
|
||||||
|
{
|
||||||
|
if (!(node is Operation operation))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsResourceWrite(operation.Inst))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation.Inst == Instruction.StoreAttribute)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation.Inst == Instruction.Copy && operation.Dest.Type == OperandType.Attribute)
|
||||||
|
{
|
||||||
|
Operand src = operation.GetSource(0);
|
||||||
|
|
||||||
|
if (src.Type == OperandType.LocalVariable && src.AsgOp is Operation asgOp && asgOp.Inst == Instruction.LoadAttribute)
|
||||||
|
{
|
||||||
|
src = Attribute(asgOp.GetSource(0).Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src.Type == OperandType.Attribute)
|
||||||
|
{
|
||||||
|
if (operation.Dest.Value == AttributeConsts.Layer)
|
||||||
|
{
|
||||||
|
if ((src.Value & AttributeConsts.LoadOutputMask) != 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
writesLayer = true;
|
||||||
|
layerInputAttr = src.Value;
|
||||||
|
}
|
||||||
|
else if (src.Value != operation.Dest.Value)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (src.Type == OperandType.Constant)
|
||||||
|
{
|
||||||
|
int dstComponent = (operation.Dest.Value >> 2) & 3;
|
||||||
|
float expectedValue = dstComponent == 3 ? 1f : 0f;
|
||||||
|
|
||||||
|
if (src.AsFloat() != expectedValue)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (operation.Inst == Instruction.EmitVertex)
|
||||||
|
{
|
||||||
|
verticesCount++;
|
||||||
|
}
|
||||||
|
else if (operation.Inst == Instruction.EndPrimitive)
|
||||||
|
{
|
||||||
|
totalVerticesCount += verticesCount;
|
||||||
|
verticesCount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalVerticesCount + verticesCount == 3 && writesLayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsResourceWrite(Instruction inst)
|
||||||
|
{
|
||||||
|
switch (inst)
|
||||||
|
{
|
||||||
|
case Instruction.AtomicAdd:
|
||||||
|
case Instruction.AtomicAnd:
|
||||||
|
case Instruction.AtomicCompareAndSwap:
|
||||||
|
case Instruction.AtomicMaxS32:
|
||||||
|
case Instruction.AtomicMaxU32:
|
||||||
|
case Instruction.AtomicMinS32:
|
||||||
|
case Instruction.AtomicMinU32:
|
||||||
|
case Instruction.AtomicOr:
|
||||||
|
case Instruction.AtomicSwap:
|
||||||
|
case Instruction.AtomicXor:
|
||||||
|
case Instruction.ImageAtomic:
|
||||||
|
case Instruction.ImageStore:
|
||||||
|
case Instruction.StoreGlobal:
|
||||||
|
case Instruction.StoreGlobal16:
|
||||||
|
case Instruction.StoreGlobal8:
|
||||||
|
case Instruction.StoreStorage:
|
||||||
|
case Instruction.StoreStorage16:
|
||||||
|
case Instruction.StoreStorage8:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -77,9 +77,11 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
funcs[i] = new Function(cfg.Blocks, $"fun{i}", false, inArgumentsCount, outArgumentsCount);
|
funcs[i] = new Function(cfg.Blocks, $"fun{i}", false, inArgumentsCount, outArgumentsCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var identification = ShaderIdentifier.Identify(funcs, config);
|
||||||
|
|
||||||
var sInfo = StructuredProgram.MakeStructuredProgram(funcs, config);
|
var sInfo = StructuredProgram.MakeStructuredProgram(funcs, config);
|
||||||
|
|
||||||
var info = config.CreateProgramInfo();
|
var info = config.CreateProgramInfo(identification);
|
||||||
|
|
||||||
return config.Options.TargetLanguage switch
|
return config.Options.TargetLanguage switch
|
||||||
{
|
{
|
||||||
|
@@ -138,6 +138,16 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
_config.MergeFromtNextStage(nextStage._config);
|
_config.MergeFromtNextStage(nextStage._config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetGeometryShaderLayerInputAttribute(int attr)
|
||||||
|
{
|
||||||
|
_config.SetGeometryShaderLayerInputAttribute(attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetLastInVertexPipeline(bool hasFragment)
|
||||||
|
{
|
||||||
|
_config.SetLastInVertexPipeline(hasFragment);
|
||||||
|
}
|
||||||
|
|
||||||
public ShaderProgram Translate(TranslatorContext other = null)
|
public ShaderProgram Translate(TranslatorContext other = null)
|
||||||
{
|
{
|
||||||
FunctionCode[] code = EmitShader(_program, _config, initializeOutputs: other == null, out _);
|
FunctionCode[] code = EmitShader(_program, _config, initializeOutputs: other == null, out _);
|
||||||
|
@@ -163,6 +163,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SignalDirty(DirtyFlags.Image);
|
SignalDirty(DirtyFlags.Image);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetImage(int binding, Auto<DisposableImageView> image)
|
||||||
|
{
|
||||||
|
_imageRefs[binding] = image;
|
||||||
|
|
||||||
|
SignalDirty(DirtyFlags.Image);
|
||||||
|
}
|
||||||
|
|
||||||
public void SetStorageBuffers(CommandBuffer commandBuffer, ReadOnlySpan<BufferAssignment> buffers)
|
public void SetStorageBuffers(CommandBuffer commandBuffer, ReadOnlySpan<BufferAssignment> buffers)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < buffers.Length; i++)
|
for (int i = 0; i < buffers.Length; i++)
|
||||||
|
208
Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs
Normal file
208
Ryujinx.Graphics.Vulkan/Effects/FsrScalingFilter.cs
Normal file
@@ -0,0 +1,208 @@
|
|||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Shader;
|
||||||
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
|
using Silk.NET.Vulkan;
|
||||||
|
using System;
|
||||||
|
using Extent2D = Ryujinx.Graphics.GAL.Extents2D;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Vulkan.Effects
|
||||||
|
{
|
||||||
|
internal partial class FsrScalingFilter : IScalingFilter
|
||||||
|
{
|
||||||
|
private readonly VulkanRenderer _renderer;
|
||||||
|
private PipelineHelperShader _pipeline;
|
||||||
|
private ISampler _sampler;
|
||||||
|
private ShaderCollection _scalingProgram;
|
||||||
|
private ShaderCollection _sharpeningProgram;
|
||||||
|
private float _sharpeningLevel = 1;
|
||||||
|
private Device _device;
|
||||||
|
private TextureView _intermediaryTexture;
|
||||||
|
|
||||||
|
public float Level
|
||||||
|
{
|
||||||
|
get => _sharpeningLevel;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_sharpeningLevel = MathF.Max(0.01f, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public FsrScalingFilter(VulkanRenderer renderer, Device device)
|
||||||
|
{
|
||||||
|
_device = device;
|
||||||
|
_renderer = renderer;
|
||||||
|
|
||||||
|
Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_pipeline.Dispose();
|
||||||
|
_scalingProgram.Dispose();
|
||||||
|
_sharpeningProgram.Dispose();
|
||||||
|
_sampler.Dispose();
|
||||||
|
_intermediaryTexture?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
_pipeline = new PipelineHelperShader(_renderer, _device);
|
||||||
|
|
||||||
|
_pipeline.Initialize();
|
||||||
|
|
||||||
|
var scalingShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.spv");
|
||||||
|
var sharpeningShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.spv");
|
||||||
|
|
||||||
|
var computeBindings = new ShaderBindings(
|
||||||
|
new[] { 2 },
|
||||||
|
Array.Empty<int>(),
|
||||||
|
new[] { 1 },
|
||||||
|
new[] { 0 });
|
||||||
|
|
||||||
|
var sharpeningBindings = new ShaderBindings(
|
||||||
|
new[] { 2, 3, 4 },
|
||||||
|
Array.Empty<int>(),
|
||||||
|
new[] { 1 },
|
||||||
|
new[] { 0 });
|
||||||
|
|
||||||
|
_sampler = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
||||||
|
|
||||||
|
_scalingProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
||||||
|
{
|
||||||
|
new ShaderSource(scalingShader, computeBindings, ShaderStage.Compute, TargetLanguage.Spirv)
|
||||||
|
});
|
||||||
|
|
||||||
|
_sharpeningProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
||||||
|
{
|
||||||
|
new ShaderSource(sharpeningShader, sharpeningBindings, ShaderStage.Compute, TargetLanguage.Spirv)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run(
|
||||||
|
TextureView view,
|
||||||
|
CommandBufferScoped cbs,
|
||||||
|
Auto<DisposableImageView> destinationTexture,
|
||||||
|
Silk.NET.Vulkan.Format format,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
Extent2D source,
|
||||||
|
Extent2D destination)
|
||||||
|
{
|
||||||
|
if (_intermediaryTexture == null
|
||||||
|
|| _intermediaryTexture.Info.Width != width
|
||||||
|
|| _intermediaryTexture.Info.Height != height
|
||||||
|
|| !_intermediaryTexture.Info.Equals(view.Info))
|
||||||
|
{
|
||||||
|
var originalInfo = view.Info;
|
||||||
|
|
||||||
|
var swapRB = originalInfo.Format.IsBgr() && originalInfo.SwizzleR == SwizzleComponent.Red;
|
||||||
|
|
||||||
|
var info = new TextureCreateInfo(
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
originalInfo.Depth,
|
||||||
|
originalInfo.Levels,
|
||||||
|
originalInfo.Samples,
|
||||||
|
originalInfo.BlockWidth,
|
||||||
|
originalInfo.BlockHeight,
|
||||||
|
originalInfo.BytesPerPixel,
|
||||||
|
originalInfo.Format,
|
||||||
|
originalInfo.DepthStencilMode,
|
||||||
|
originalInfo.Target,
|
||||||
|
swapRB ? originalInfo.SwizzleB : originalInfo.SwizzleR,
|
||||||
|
originalInfo.SwizzleG,
|
||||||
|
swapRB ? originalInfo.SwizzleR : originalInfo.SwizzleB,
|
||||||
|
originalInfo.SwizzleA);
|
||||||
|
_intermediaryTexture?.Dispose();
|
||||||
|
_intermediaryTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView;
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
|
||||||
|
Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1];
|
||||||
|
|
||||||
|
viewports[0] = new GAL.Viewport(
|
||||||
|
new Rectangle<float>(0, 0, view.Width, view.Height),
|
||||||
|
ViewportSwizzle.PositiveX,
|
||||||
|
ViewportSwizzle.PositiveY,
|
||||||
|
ViewportSwizzle.PositiveZ,
|
||||||
|
ViewportSwizzle.PositiveW,
|
||||||
|
0f,
|
||||||
|
1f);
|
||||||
|
|
||||||
|
scissors[0] = new Rectangle<int>(0, 0, view.Width, view.Height);
|
||||||
|
|
||||||
|
_pipeline.SetCommandBuffer(cbs);
|
||||||
|
_pipeline.SetProgram(_scalingProgram);
|
||||||
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _sampler);
|
||||||
|
|
||||||
|
float srcWidth = Math.Abs(source.X2 - source.X1);
|
||||||
|
float srcHeight = Math.Abs(source.Y2 - source.Y1);
|
||||||
|
float scaleX = srcWidth / view.Width;
|
||||||
|
float scaleY = srcHeight / view.Height;
|
||||||
|
|
||||||
|
ReadOnlySpan<float> dimensionsBuffer = stackalloc float[]
|
||||||
|
{
|
||||||
|
source.X1,
|
||||||
|
source.X2,
|
||||||
|
source.Y1,
|
||||||
|
source.Y2,
|
||||||
|
destination.X1,
|
||||||
|
destination.X2,
|
||||||
|
destination.Y1,
|
||||||
|
destination.Y2,
|
||||||
|
scaleX,
|
||||||
|
scaleY
|
||||||
|
};
|
||||||
|
|
||||||
|
int rangeSize = dimensionsBuffer.Length * sizeof(float);
|
||||||
|
var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize, false);
|
||||||
|
_renderer.BufferManager.SetData(bufferHandle, 0, dimensionsBuffer);
|
||||||
|
|
||||||
|
ReadOnlySpan<float> sharpeningBuffer = stackalloc float[] { 1.5f - (Level * 0.01f * 1.5f)};
|
||||||
|
var sharpeningBufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, sizeof(float), false);
|
||||||
|
_renderer.BufferManager.SetData(sharpeningBufferHandle, 0, sharpeningBuffer);
|
||||||
|
|
||||||
|
int threadGroupWorkRegionDim = 16;
|
||||||
|
int dispatchX = (width + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
||||||
|
int dispatchY = (height + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
|
||||||
|
|
||||||
|
var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize);
|
||||||
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) });
|
||||||
|
_pipeline.SetScissors(scissors);
|
||||||
|
_pipeline.SetViewports(viewports, false);
|
||||||
|
_pipeline.SetImage(0, _intermediaryTexture, GAL.Format.R8G8B8A8Unorm);
|
||||||
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
|
_pipeline.ComputeBarrier();
|
||||||
|
|
||||||
|
viewports[0] = new GAL.Viewport(
|
||||||
|
new Rectangle<float>(0, 0, width, height),
|
||||||
|
ViewportSwizzle.PositiveX,
|
||||||
|
ViewportSwizzle.PositiveY,
|
||||||
|
ViewportSwizzle.PositiveZ,
|
||||||
|
ViewportSwizzle.PositiveW,
|
||||||
|
0f,
|
||||||
|
1f);
|
||||||
|
|
||||||
|
scissors[0] = new Rectangle<int>(0, 0, width, height);
|
||||||
|
|
||||||
|
// Sharpening pass
|
||||||
|
_pipeline.SetCommandBuffer(cbs);
|
||||||
|
_pipeline.SetProgram(_sharpeningProgram);
|
||||||
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _intermediaryTexture, _sampler);
|
||||||
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) });
|
||||||
|
var sharpeningRange = new BufferRange(sharpeningBufferHandle, 0, sizeof(float));
|
||||||
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(4, sharpeningRange) });
|
||||||
|
_pipeline.SetScissors(scissors);
|
||||||
|
_pipeline.SetViewports(viewports, false);
|
||||||
|
_pipeline.SetImage(0, destinationTexture);
|
||||||
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
|
_pipeline.ComputeBarrier();
|
||||||
|
|
||||||
|
_pipeline.Finish();
|
||||||
|
|
||||||
|
_renderer.BufferManager.Delete(bufferHandle);
|
||||||
|
_renderer.BufferManager.Delete(sharpeningBufferHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
127
Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs
Normal file
127
Ryujinx.Graphics.Vulkan/Effects/FxaaPostProcessingEffect.cs
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Shader;
|
||||||
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
|
using Silk.NET.Vulkan;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Vulkan.Effects
|
||||||
|
{
|
||||||
|
internal partial class FxaaPostProcessingEffect : IPostProcessingEffect
|
||||||
|
{
|
||||||
|
private readonly VulkanRenderer _renderer;
|
||||||
|
private ISampler _samplerLinear;
|
||||||
|
private ShaderCollection _shaderProgram;
|
||||||
|
|
||||||
|
private PipelineHelperShader _pipeline;
|
||||||
|
private TextureView _texture;
|
||||||
|
|
||||||
|
public FxaaPostProcessingEffect(VulkanRenderer renderer, Device device)
|
||||||
|
{
|
||||||
|
_renderer = renderer;
|
||||||
|
_pipeline = new PipelineHelperShader(renderer, device);
|
||||||
|
|
||||||
|
Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_shaderProgram.Dispose();
|
||||||
|
_pipeline.Dispose();
|
||||||
|
_samplerLinear.Dispose();
|
||||||
|
_texture?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Initialize()
|
||||||
|
{
|
||||||
|
_pipeline.Initialize();
|
||||||
|
|
||||||
|
var shader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.spv");
|
||||||
|
|
||||||
|
var computeBindings = new ShaderBindings(
|
||||||
|
new[] { 2 },
|
||||||
|
Array.Empty<int>(),
|
||||||
|
new[] { 1 },
|
||||||
|
new[] { 0 });
|
||||||
|
|
||||||
|
_samplerLinear = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
||||||
|
|
||||||
|
_shaderProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
||||||
|
{
|
||||||
|
new ShaderSource(shader, computeBindings, ShaderStage.Compute, TargetLanguage.Spirv)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextureView Run(TextureView view, CommandBufferScoped cbs, int width, int height)
|
||||||
|
{
|
||||||
|
if (_texture == null || _texture.Width != view.Width || _texture.Height != view.Height)
|
||||||
|
{
|
||||||
|
_texture?.Dispose();
|
||||||
|
|
||||||
|
var info = view.Info;
|
||||||
|
|
||||||
|
if (view.Info.Format.IsBgr())
|
||||||
|
{
|
||||||
|
info = new TextureCreateInfo(info.Width,
|
||||||
|
info.Height,
|
||||||
|
info.Depth,
|
||||||
|
info.Levels,
|
||||||
|
info.Samples,
|
||||||
|
info.BlockWidth,
|
||||||
|
info.BlockHeight,
|
||||||
|
info.BytesPerPixel,
|
||||||
|
info.Format,
|
||||||
|
info.DepthStencilMode,
|
||||||
|
info.Target,
|
||||||
|
info.SwizzleB,
|
||||||
|
info.SwizzleG,
|
||||||
|
info.SwizzleR,
|
||||||
|
info.SwizzleA);
|
||||||
|
}
|
||||||
|
_texture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pipeline.SetCommandBuffer(cbs);
|
||||||
|
_pipeline.SetProgram(_shaderProgram);
|
||||||
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear);
|
||||||
|
|
||||||
|
ReadOnlySpan<float> resolutionBuffer = stackalloc float[] { view.Width, view.Height };
|
||||||
|
int rangeSize = resolutionBuffer.Length * sizeof(float);
|
||||||
|
var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize, false);
|
||||||
|
|
||||||
|
_renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer);
|
||||||
|
|
||||||
|
var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize);
|
||||||
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) });
|
||||||
|
|
||||||
|
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
|
||||||
|
|
||||||
|
viewports[0] = new GAL.Viewport(
|
||||||
|
new Rectangle<float>(0, 0, view.Width, view.Height),
|
||||||
|
ViewportSwizzle.PositiveX,
|
||||||
|
ViewportSwizzle.PositiveY,
|
||||||
|
ViewportSwizzle.PositiveZ,
|
||||||
|
ViewportSwizzle.PositiveW,
|
||||||
|
0f,
|
||||||
|
1f);
|
||||||
|
|
||||||
|
Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1];
|
||||||
|
|
||||||
|
var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize);
|
||||||
|
var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize);
|
||||||
|
|
||||||
|
_pipeline.SetScissors(stackalloc[] { new Rectangle<int>(0, 0, view.Width, view.Height) });
|
||||||
|
_pipeline.SetViewports(viewports, false);
|
||||||
|
|
||||||
|
_pipeline.SetImage(0, _texture, GAL.Format.R8G8B8A8Unorm);
|
||||||
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
|
|
||||||
|
_renderer.BufferManager.Delete(bufferHandle);
|
||||||
|
_pipeline.ComputeBarrier();
|
||||||
|
|
||||||
|
_pipeline.Finish();
|
||||||
|
|
||||||
|
return _texture;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
Ryujinx.Graphics.Vulkan/Effects/IPostProcessingEffect.cs
Normal file
10
Ryujinx.Graphics.Vulkan/Effects/IPostProcessingEffect.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Vulkan.Effects
|
||||||
|
{
|
||||||
|
internal interface IPostProcessingEffect : IDisposable
|
||||||
|
{
|
||||||
|
const int LocalGroupSize = 64;
|
||||||
|
TextureView Run(TextureView view, CommandBufferScoped cbs, int width, int height);
|
||||||
|
}
|
||||||
|
}
|
20
Ryujinx.Graphics.Vulkan/Effects/IScalingFilter.cs
Normal file
20
Ryujinx.Graphics.Vulkan/Effects/IScalingFilter.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using Silk.NET.Vulkan;
|
||||||
|
using System;
|
||||||
|
using Extent2D = Ryujinx.Graphics.GAL.Extents2D;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Vulkan.Effects
|
||||||
|
{
|
||||||
|
internal interface IScalingFilter : IDisposable
|
||||||
|
{
|
||||||
|
float Level { get; set; }
|
||||||
|
void Run(
|
||||||
|
TextureView view,
|
||||||
|
CommandBufferScoped cbs,
|
||||||
|
Auto<DisposableImageView> destinationTexture,
|
||||||
|
Format format,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
Extent2D source,
|
||||||
|
Extent2D destination);
|
||||||
|
}
|
||||||
|
}
|
3945
Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.glsl
Normal file
3945
Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.glsl
Normal file
File diff suppressed because it is too large
Load Diff
BIN
Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.spv
Normal file
BIN
Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrScaling.spv
Normal file
Binary file not shown.
3904
Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.glsl
Normal file
3904
Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.glsl
Normal file
File diff suppressed because it is too large
Load Diff
BIN
Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.spv
Normal file
BIN
Ryujinx.Graphics.Vulkan/Effects/Shaders/FsrSharpening.spv
Normal file
Binary file not shown.
1177
Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.glsl
Normal file
1177
Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.glsl
Normal file
File diff suppressed because it is too large
Load Diff
BIN
Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.spv
Normal file
BIN
Ryujinx.Graphics.Vulkan/Effects/Shaders/Fxaa.spv
Normal file
Binary file not shown.
1404
Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.glsl
Normal file
1404
Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.glsl
Normal file
File diff suppressed because it is too large
Load Diff
BIN
Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.spv
Normal file
BIN
Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.spv
Normal file
Binary file not shown.
1402
Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaEdge.glsl
Normal file
1402
Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaEdge.glsl
Normal file
File diff suppressed because it is too large
Load Diff
BIN
Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaEdge.spv
Normal file
BIN
Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaEdge.spv
Normal file
Binary file not shown.
1403
Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.glsl
Normal file
1403
Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.glsl
Normal file
File diff suppressed because it is too large
Load Diff
BIN
Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.spv
Normal file
BIN
Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.spv
Normal file
Binary file not shown.
15
Ryujinx.Graphics.Vulkan/Effects/SmaaConstants.cs
Normal file
15
Ryujinx.Graphics.Vulkan/Effects/SmaaConstants.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Vulkan.Effects
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||||
|
internal struct SmaaConstants
|
||||||
|
{
|
||||||
|
public int QualityLow;
|
||||||
|
public int QualityMedium;
|
||||||
|
public int QualityHigh;
|
||||||
|
public int QualityUltra;
|
||||||
|
public float Width;
|
||||||
|
public float Height;
|
||||||
|
}
|
||||||
|
}
|
314
Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs
Normal file
314
Ryujinx.Graphics.Vulkan/Effects/SmaaPostProcessingEffect.cs
Normal file
@@ -0,0 +1,314 @@
|
|||||||
|
using Ryujinx.Common;
|
||||||
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Shader;
|
||||||
|
using Ryujinx.Graphics.Shader.Translation;
|
||||||
|
using Silk.NET.Vulkan;
|
||||||
|
using System;
|
||||||
|
using Format = Ryujinx.Graphics.GAL.Format;
|
||||||
|
|
||||||
|
namespace Ryujinx.Graphics.Vulkan.Effects
|
||||||
|
{
|
||||||
|
internal partial class SmaaPostProcessingEffect : IPostProcessingEffect
|
||||||
|
{
|
||||||
|
public const int AreaWidth = 160;
|
||||||
|
public const int AreaHeight = 560;
|
||||||
|
public const int SearchWidth = 64;
|
||||||
|
public const int SearchHeight = 16;
|
||||||
|
|
||||||
|
private readonly VulkanRenderer _renderer;
|
||||||
|
private ISampler _samplerLinear;
|
||||||
|
private SmaaConstants _specConstants;
|
||||||
|
private ShaderCollection _edgeProgram;
|
||||||
|
private ShaderCollection _blendProgram;
|
||||||
|
private ShaderCollection _neighbourProgram;
|
||||||
|
|
||||||
|
private PipelineHelperShader _pipeline;
|
||||||
|
|
||||||
|
private TextureView _outputTexture;
|
||||||
|
private TextureView _edgeOutputTexture;
|
||||||
|
private TextureView _blendOutputTexture;
|
||||||
|
private TextureView _areaTexture;
|
||||||
|
private TextureView _searchTexture;
|
||||||
|
private Device _device;
|
||||||
|
private bool _recreatePipelines;
|
||||||
|
private int _quality;
|
||||||
|
|
||||||
|
public SmaaPostProcessingEffect(VulkanRenderer renderer, Device device, int quality)
|
||||||
|
{
|
||||||
|
_device = device;
|
||||||
|
_renderer = renderer;
|
||||||
|
_quality = quality;
|
||||||
|
|
||||||
|
Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Quality
|
||||||
|
{
|
||||||
|
get => _quality;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_quality = value;
|
||||||
|
|
||||||
|
_recreatePipelines = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
DeletePipelines();
|
||||||
|
_samplerLinear?.Dispose();
|
||||||
|
_outputTexture?.Dispose();
|
||||||
|
_edgeOutputTexture?.Dispose();
|
||||||
|
_blendOutputTexture?.Dispose();
|
||||||
|
_areaTexture?.Dispose();
|
||||||
|
_searchTexture?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe void RecreateShaders(int width, int height)
|
||||||
|
{
|
||||||
|
_recreatePipelines = false;
|
||||||
|
|
||||||
|
DeletePipelines();
|
||||||
|
_pipeline = new PipelineHelperShader(_renderer, _device);
|
||||||
|
|
||||||
|
_pipeline.Initialize();
|
||||||
|
|
||||||
|
var edgeShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaEdge.spv");
|
||||||
|
var blendShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaBlend.spv");
|
||||||
|
var neighbourShader = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Shaders/SmaaNeighbour.spv");
|
||||||
|
|
||||||
|
var edgeBindings = new ShaderBindings(
|
||||||
|
new[] { 2 },
|
||||||
|
Array.Empty<int>(),
|
||||||
|
new[] { 1 },
|
||||||
|
new[] { 0 });
|
||||||
|
|
||||||
|
var blendBindings = new ShaderBindings(
|
||||||
|
new[] { 2 },
|
||||||
|
Array.Empty<int>(),
|
||||||
|
new[] { 1, 3, 4 },
|
||||||
|
new[] { 0 });
|
||||||
|
|
||||||
|
var neighbourBindings = new ShaderBindings(
|
||||||
|
new[] { 2 },
|
||||||
|
Array.Empty<int>(),
|
||||||
|
new[] { 1, 3 },
|
||||||
|
new[] { 0 });
|
||||||
|
|
||||||
|
_samplerLinear = _renderer.CreateSampler(GAL.SamplerCreateInfo.Create(MinFilter.Linear, MagFilter.Linear));
|
||||||
|
|
||||||
|
_specConstants = new SmaaConstants()
|
||||||
|
{
|
||||||
|
Width = width,
|
||||||
|
Height = height,
|
||||||
|
QualityLow = Quality == 0 ? 1 : 0,
|
||||||
|
QualityMedium = Quality == 1 ? 1 : 0,
|
||||||
|
QualityHigh = Quality == 2 ? 1 : 0,
|
||||||
|
QualityUltra = Quality == 3 ? 1 : 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
var specInfo = new SpecDescription(
|
||||||
|
(0, SpecConstType.Int32),
|
||||||
|
(1, SpecConstType.Int32),
|
||||||
|
(2, SpecConstType.Int32),
|
||||||
|
(3, SpecConstType.Int32),
|
||||||
|
(4, SpecConstType.Float32),
|
||||||
|
(5, SpecConstType.Float32));
|
||||||
|
|
||||||
|
_edgeProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
||||||
|
{
|
||||||
|
new ShaderSource(edgeShader, edgeBindings, ShaderStage.Compute, TargetLanguage.Spirv)
|
||||||
|
}, new[] { specInfo });
|
||||||
|
|
||||||
|
_blendProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
||||||
|
{
|
||||||
|
new ShaderSource(blendShader, blendBindings, ShaderStage.Compute, TargetLanguage.Spirv)
|
||||||
|
}, new[] { specInfo });
|
||||||
|
|
||||||
|
_neighbourProgram = _renderer.CreateProgramWithMinimalLayout(new[]
|
||||||
|
{
|
||||||
|
new ShaderSource(neighbourShader, neighbourBindings, ShaderStage.Compute, TargetLanguage.Spirv)
|
||||||
|
}, new[] { specInfo });
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DeletePipelines()
|
||||||
|
{
|
||||||
|
_pipeline?.Dispose();
|
||||||
|
_edgeProgram?.Dispose();
|
||||||
|
_blendProgram?.Dispose();
|
||||||
|
_neighbourProgram?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Initialize()
|
||||||
|
{
|
||||||
|
var areaInfo = new TextureCreateInfo(AreaWidth,
|
||||||
|
AreaHeight,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
Format.R8G8Unorm,
|
||||||
|
DepthStencilMode.Depth,
|
||||||
|
Target.Texture2D,
|
||||||
|
SwizzleComponent.Red,
|
||||||
|
SwizzleComponent.Green,
|
||||||
|
SwizzleComponent.Blue,
|
||||||
|
SwizzleComponent.Alpha);
|
||||||
|
|
||||||
|
var searchInfo = new TextureCreateInfo(SearchWidth,
|
||||||
|
SearchHeight,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
Format.R8Unorm,
|
||||||
|
DepthStencilMode.Depth,
|
||||||
|
Target.Texture2D,
|
||||||
|
SwizzleComponent.Red,
|
||||||
|
SwizzleComponent.Green,
|
||||||
|
SwizzleComponent.Blue,
|
||||||
|
SwizzleComponent.Alpha);
|
||||||
|
|
||||||
|
var areaTexture = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaAreaTexture.bin");
|
||||||
|
var searchTexture = EmbeddedResources.Read("Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaSearchTexture.bin");
|
||||||
|
|
||||||
|
_areaTexture = _renderer.CreateTexture(areaInfo, 1) as TextureView;
|
||||||
|
_searchTexture = _renderer.CreateTexture(searchInfo, 1) as TextureView;
|
||||||
|
|
||||||
|
_areaTexture.SetData(areaTexture);
|
||||||
|
_searchTexture.SetData(searchTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TextureView Run(TextureView view, CommandBufferScoped cbs, int width, int height)
|
||||||
|
{
|
||||||
|
if (_recreatePipelines || _outputTexture == null || _outputTexture.Info.Width != view.Width || _outputTexture.Info.Height != view.Height)
|
||||||
|
{
|
||||||
|
RecreateShaders(view.Width, view.Height);
|
||||||
|
_outputTexture?.Dispose();
|
||||||
|
_edgeOutputTexture?.Dispose();
|
||||||
|
_blendOutputTexture?.Dispose();
|
||||||
|
|
||||||
|
var info = view.Info;
|
||||||
|
|
||||||
|
if (view.Info.Format.IsBgr())
|
||||||
|
{
|
||||||
|
info = new TextureCreateInfo(info.Width,
|
||||||
|
info.Height,
|
||||||
|
info.Depth,
|
||||||
|
info.Levels,
|
||||||
|
info.Samples,
|
||||||
|
info.BlockWidth,
|
||||||
|
info.BlockHeight,
|
||||||
|
info.BytesPerPixel,
|
||||||
|
info.Format,
|
||||||
|
info.DepthStencilMode,
|
||||||
|
info.Target,
|
||||||
|
info.SwizzleB,
|
||||||
|
info.SwizzleG,
|
||||||
|
info.SwizzleR,
|
||||||
|
info.SwizzleA);
|
||||||
|
}
|
||||||
|
|
||||||
|
_outputTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView;
|
||||||
|
_edgeOutputTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView;
|
||||||
|
_blendOutputTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView;
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<GAL.Viewport> viewports = stackalloc GAL.Viewport[1];
|
||||||
|
|
||||||
|
viewports[0] = new GAL.Viewport(
|
||||||
|
new Rectangle<float>(0, 0, view.Width, view.Height),
|
||||||
|
ViewportSwizzle.PositiveX,
|
||||||
|
ViewportSwizzle.PositiveY,
|
||||||
|
ViewportSwizzle.PositiveZ,
|
||||||
|
ViewportSwizzle.PositiveW,
|
||||||
|
0f,
|
||||||
|
1f);
|
||||||
|
|
||||||
|
Span<Rectangle<int>> scissors = stackalloc Rectangle<int>[1];
|
||||||
|
|
||||||
|
scissors[0] = new Rectangle<int>(0, 0, view.Width, view.Height);
|
||||||
|
|
||||||
|
_renderer.HelperShader.Clear(_renderer,
|
||||||
|
_edgeOutputTexture.GetImageView(),
|
||||||
|
new float[] { 0, 0, 0, 1 },
|
||||||
|
(uint)(ColorComponentFlags.RBit | ColorComponentFlags.GBit | ColorComponentFlags.BBit | ColorComponentFlags.ABit),
|
||||||
|
view.Width,
|
||||||
|
view.Height,
|
||||||
|
_edgeOutputTexture.VkFormat,
|
||||||
|
ComponentType.UnsignedInteger,
|
||||||
|
scissors[0]);
|
||||||
|
|
||||||
|
_renderer.HelperShader.Clear(_renderer,
|
||||||
|
_blendOutputTexture.GetImageView(),
|
||||||
|
new float[] { 0, 0, 0, 1 },
|
||||||
|
(uint)(ColorComponentFlags.RBit | ColorComponentFlags.GBit | ColorComponentFlags.BBit | ColorComponentFlags.ABit),
|
||||||
|
view.Width,
|
||||||
|
view.Height,
|
||||||
|
_blendOutputTexture.VkFormat,
|
||||||
|
ComponentType.UnsignedInteger,
|
||||||
|
scissors[0]);
|
||||||
|
|
||||||
|
_renderer.Pipeline.TextureBarrier();
|
||||||
|
|
||||||
|
var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize);
|
||||||
|
var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize);
|
||||||
|
|
||||||
|
// Edge pass
|
||||||
|
_pipeline.SetCommandBuffer(cbs);
|
||||||
|
_pipeline.SetProgram(_edgeProgram);
|
||||||
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear);
|
||||||
|
_pipeline.Specialize(_specConstants);
|
||||||
|
|
||||||
|
ReadOnlySpan<float> resolutionBuffer = stackalloc float[] { view.Width, view.Height };
|
||||||
|
int rangeSize = resolutionBuffer.Length * sizeof(float);
|
||||||
|
var bufferHandle = _renderer.BufferManager.CreateWithHandle(_renderer, rangeSize, false);
|
||||||
|
|
||||||
|
_renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer);
|
||||||
|
var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize);
|
||||||
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) });
|
||||||
|
_pipeline.SetScissors(scissors);
|
||||||
|
_pipeline.SetViewports(viewports, false);
|
||||||
|
_pipeline.SetImage(0, _edgeOutputTexture, GAL.Format.R8G8B8A8Unorm);
|
||||||
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
|
_pipeline.ComputeBarrier();
|
||||||
|
|
||||||
|
// Blend pass
|
||||||
|
_pipeline.SetCommandBuffer(cbs);
|
||||||
|
_pipeline.SetProgram(_blendProgram);
|
||||||
|
_pipeline.Specialize(_specConstants);
|
||||||
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _edgeOutputTexture, _samplerLinear);
|
||||||
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _areaTexture, _samplerLinear);
|
||||||
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 4, _searchTexture, _samplerLinear);
|
||||||
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) });
|
||||||
|
_pipeline.SetScissors(scissors);
|
||||||
|
_pipeline.SetViewports(viewports, false);
|
||||||
|
_pipeline.SetImage(0, _blendOutputTexture, GAL.Format.R8G8B8A8Unorm);
|
||||||
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
|
_pipeline.ComputeBarrier();
|
||||||
|
|
||||||
|
// Neighbour pass
|
||||||
|
_pipeline.SetCommandBuffer(cbs);
|
||||||
|
_pipeline.SetProgram(_neighbourProgram);
|
||||||
|
_pipeline.Specialize(_specConstants);
|
||||||
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _blendOutputTexture, _samplerLinear);
|
||||||
|
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear);
|
||||||
|
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) });
|
||||||
|
_pipeline.SetScissors(scissors);
|
||||||
|
_pipeline.SetViewports(viewports, false);
|
||||||
|
_pipeline.SetImage(0, _outputTexture, GAL.Format.R8G8B8A8Unorm);
|
||||||
|
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||||
|
_pipeline.ComputeBarrier();
|
||||||
|
|
||||||
|
_pipeline.Finish();
|
||||||
|
|
||||||
|
_renderer.BufferManager.Delete(bufferHandle);
|
||||||
|
|
||||||
|
return _outputTexture;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
BIN
Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaAreaTexture.bin
Normal file
BIN
Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaAreaTexture.bin
Normal file
Binary file not shown.
BIN
Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaSearchTexture.bin
Normal file
BIN
Ryujinx.Graphics.Vulkan/Effects/Textures/SmaaSearchTexture.bin
Normal file
Binary file not shown.
@@ -33,6 +33,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public readonly bool SupportsMultiView;
|
public readonly bool SupportsMultiView;
|
||||||
public readonly bool SupportsNullDescriptors;
|
public readonly bool SupportsNullDescriptors;
|
||||||
public readonly bool SupportsPushDescriptors;
|
public readonly bool SupportsPushDescriptors;
|
||||||
|
public readonly bool SupportsPrimitiveTopologyListRestart;
|
||||||
|
public readonly bool SupportsPrimitiveTopologyPatchListRestart;
|
||||||
public readonly bool SupportsTransformFeedback;
|
public readonly bool SupportsTransformFeedback;
|
||||||
public readonly bool SupportsTransformFeedbackQueries;
|
public readonly bool SupportsTransformFeedbackQueries;
|
||||||
public readonly bool SupportsPreciseOcclusionQueries;
|
public readonly bool SupportsPreciseOcclusionQueries;
|
||||||
@@ -63,6 +65,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
bool supportsMultiView,
|
bool supportsMultiView,
|
||||||
bool supportsNullDescriptors,
|
bool supportsNullDescriptors,
|
||||||
bool supportsPushDescriptors,
|
bool supportsPushDescriptors,
|
||||||
|
bool supportsPrimitiveTopologyListRestart,
|
||||||
|
bool supportsPrimitiveTopologyPatchListRestart,
|
||||||
bool supportsTransformFeedback,
|
bool supportsTransformFeedback,
|
||||||
bool supportsTransformFeedbackQueries,
|
bool supportsTransformFeedbackQueries,
|
||||||
bool supportsPreciseOcclusionQueries,
|
bool supportsPreciseOcclusionQueries,
|
||||||
@@ -92,6 +96,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SupportsMultiView = supportsMultiView;
|
SupportsMultiView = supportsMultiView;
|
||||||
SupportsNullDescriptors = supportsNullDescriptors;
|
SupportsNullDescriptors = supportsNullDescriptors;
|
||||||
SupportsPushDescriptors = supportsPushDescriptors;
|
SupportsPushDescriptors = supportsPushDescriptors;
|
||||||
|
SupportsPrimitiveTopologyListRestart = supportsPrimitiveTopologyListRestart;
|
||||||
|
SupportsPrimitiveTopologyPatchListRestart = supportsPrimitiveTopologyPatchListRestart;
|
||||||
SupportsTransformFeedback = supportsTransformFeedback;
|
SupportsTransformFeedback = supportsTransformFeedback;
|
||||||
SupportsTransformFeedbackQueries = supportsTransformFeedbackQueries;
|
SupportsTransformFeedbackQueries = supportsTransformFeedbackQueries;
|
||||||
SupportsPreciseOcclusionQueries = supportsPreciseOcclusionQueries;
|
SupportsPreciseOcclusionQueries = supportsPreciseOcclusionQueries;
|
||||||
|
@@ -79,10 +79,18 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
id--;
|
id--;
|
||||||
|
|
||||||
try
|
try
|
||||||
|
{
|
||||||
|
if ((uint)id < (uint)_list.Count)
|
||||||
{
|
{
|
||||||
value = _list[id];
|
value = _list[id];
|
||||||
return value != null;
|
return value != null;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
catch (ArgumentOutOfRangeException)
|
catch (ArgumentOutOfRangeException)
|
||||||
{
|
{
|
||||||
value = null;
|
value = null;
|
||||||
|
@@ -37,9 +37,12 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (Pointer != null)
|
||||||
{
|
{
|
||||||
Marshal.FreeHGlobal((IntPtr)Pointer);
|
Marshal.FreeHGlobal((IntPtr)Pointer);
|
||||||
Pointer = null;
|
Pointer = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -150,6 +150,28 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
null);
|
null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ComputeBarrier()
|
||||||
|
{
|
||||||
|
MemoryBarrier memoryBarrier = new MemoryBarrier()
|
||||||
|
{
|
||||||
|
SType = StructureType.MemoryBarrier,
|
||||||
|
SrcAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit,
|
||||||
|
DstAccessMask = AccessFlags.MemoryReadBit | AccessFlags.MemoryWriteBit
|
||||||
|
};
|
||||||
|
|
||||||
|
Gd.Api.CmdPipelineBarrier(
|
||||||
|
CommandBuffer,
|
||||||
|
PipelineStageFlags.ComputeShaderBit,
|
||||||
|
PipelineStageFlags.AllCommandsBit,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
new ReadOnlySpan<MemoryBarrier>(memoryBarrier),
|
||||||
|
0,
|
||||||
|
ReadOnlySpan<BufferMemoryBarrier>.Empty,
|
||||||
|
0,
|
||||||
|
ReadOnlySpan<ImageMemoryBarrier>.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
public void BeginTransformFeedback(GAL.PrimitiveTopology topology)
|
public void BeginTransformFeedback(GAL.PrimitiveTopology topology)
|
||||||
{
|
{
|
||||||
_tfEnabled = true;
|
_tfEnabled = true;
|
||||||
@@ -803,6 +825,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
_descriptorSetUpdater.SetImage(binding, image, imageFormat);
|
_descriptorSetUpdater.SetImage(binding, image, imageFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetImage(int binding, Auto<DisposableImageView> image)
|
||||||
|
{
|
||||||
|
_descriptorSetUpdater.SetImage(binding, image);
|
||||||
|
}
|
||||||
|
|
||||||
public void SetIndexBuffer(BufferRange buffer, GAL.IndexType type)
|
public void SetIndexBuffer(BufferRange buffer, GAL.IndexType type)
|
||||||
{
|
{
|
||||||
if (buffer.Handle != BufferHandle.Null)
|
if (buffer.Handle != BufferHandle.Null)
|
||||||
|
@@ -417,11 +417,22 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
bool primitiveRestartEnable = PrimitiveRestartEnable;
|
bool primitiveRestartEnable = PrimitiveRestartEnable;
|
||||||
|
|
||||||
primitiveRestartEnable &= Topology == PrimitiveTopology.LineStrip ||
|
bool topologySupportsRestart;
|
||||||
|
|
||||||
|
if (gd.Capabilities.SupportsPrimitiveTopologyListRestart)
|
||||||
|
{
|
||||||
|
topologySupportsRestart = gd.Capabilities.SupportsPrimitiveTopologyPatchListRestart || Topology != PrimitiveTopology.PatchList;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
topologySupportsRestart = Topology == PrimitiveTopology.LineStrip ||
|
||||||
Topology == PrimitiveTopology.TriangleStrip ||
|
Topology == PrimitiveTopology.TriangleStrip ||
|
||||||
Topology == PrimitiveTopology.TriangleFan ||
|
Topology == PrimitiveTopology.TriangleFan ||
|
||||||
Topology == PrimitiveTopology.LineStripWithAdjacency ||
|
Topology == PrimitiveTopology.LineStripWithAdjacency ||
|
||||||
Topology == PrimitiveTopology.TriangleStripWithAdjacency;
|
Topology == PrimitiveTopology.TriangleStripWithAdjacency;
|
||||||
|
}
|
||||||
|
|
||||||
|
primitiveRestartEnable &= topologySupportsRestart;
|
||||||
|
|
||||||
var inputAssemblyState = new PipelineInputAssemblyStateCreateInfo()
|
var inputAssemblyState = new PipelineInputAssemblyStateCreateInfo()
|
||||||
{
|
{
|
||||||
|
@@ -12,6 +12,17 @@
|
|||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<EmbeddedResource Include="Effects\Textures\SmaaAreaTexture.bin" />
|
||||||
|
<EmbeddedResource Include="Effects\Textures\SmaaSearchTexture.bin" />
|
||||||
|
<EmbeddedResource Include="Effects\Shaders\FsrScaling.spv" />
|
||||||
|
<EmbeddedResource Include="Effects\Shaders\FsrSharpening.spv" />
|
||||||
|
<EmbeddedResource Include="Effects\Shaders\Fxaa.spv" />
|
||||||
|
<EmbeddedResource Include="Effects\Shaders\SmaaBlend.spv" />
|
||||||
|
<EmbeddedResource Include="Effects\Shaders\SmaaEdge.spv" />
|
||||||
|
<EmbeddedResource Include="Effects\Shaders\SmaaNeighbour.spv" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="OpenTK.Windowing.GraphicsLibraryFramework" />
|
<PackageReference Include="OpenTK.Windowing.GraphicsLibraryFramework" />
|
||||||
<PackageReference Include="shaderc.net" />
|
<PackageReference Include="shaderc.net" />
|
||||||
|
@@ -32,6 +32,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
"VK_EXT_descriptor_indexing", // Enabling this works around an issue with disposed buffer bindings on RADV.
|
"VK_EXT_descriptor_indexing", // Enabling this works around an issue with disposed buffer bindings on RADV.
|
||||||
"VK_EXT_fragment_shader_interlock",
|
"VK_EXT_fragment_shader_interlock",
|
||||||
"VK_EXT_index_type_uint8",
|
"VK_EXT_index_type_uint8",
|
||||||
|
"VK_EXT_primitive_topology_list_restart",
|
||||||
"VK_EXT_robustness2",
|
"VK_EXT_robustness2",
|
||||||
"VK_EXT_shader_stencil_export",
|
"VK_EXT_shader_stencil_export",
|
||||||
"VK_KHR_shader_float16_int8",
|
"VK_KHR_shader_float16_int8",
|
||||||
@@ -429,6 +430,17 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
features2.PNext = &supportedFeaturesCustomBorderColor;
|
features2.PNext = &supportedFeaturesCustomBorderColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT supportedFeaturesPrimitiveTopologyListRestart = new PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT()
|
||||||
|
{
|
||||||
|
SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt,
|
||||||
|
PNext = features2.PNext
|
||||||
|
};
|
||||||
|
|
||||||
|
if (supportedExtensions.Contains("VK_EXT_primitive_topology_list_restart"))
|
||||||
|
{
|
||||||
|
features2.PNext = &supportedFeaturesPrimitiveTopologyListRestart;
|
||||||
|
}
|
||||||
|
|
||||||
PhysicalDeviceTransformFeedbackFeaturesEXT supportedFeaturesTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT()
|
PhysicalDeviceTransformFeedbackFeaturesEXT supportedFeaturesTransformFeedback = new PhysicalDeviceTransformFeedbackFeaturesEXT()
|
||||||
{
|
{
|
||||||
SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt,
|
SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt,
|
||||||
@@ -497,6 +509,21 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
pExtendedFeatures = &featuresTransformFeedback;
|
pExtendedFeatures = &featuresTransformFeedback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT featuresPrimitiveTopologyListRestart;
|
||||||
|
|
||||||
|
if (supportedExtensions.Contains("VK_EXT_primitive_topology_list_restart"))
|
||||||
|
{
|
||||||
|
featuresPrimitiveTopologyListRestart = new PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT()
|
||||||
|
{
|
||||||
|
SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt,
|
||||||
|
PNext = pExtendedFeatures,
|
||||||
|
PrimitiveTopologyListRestart = supportedFeaturesPrimitiveTopologyListRestart.PrimitiveTopologyListRestart,
|
||||||
|
PrimitiveTopologyPatchListRestart = supportedFeaturesPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart
|
||||||
|
};
|
||||||
|
|
||||||
|
pExtendedFeatures = &featuresPrimitiveTopologyListRestart;
|
||||||
|
}
|
||||||
|
|
||||||
PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2;
|
PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2;
|
||||||
|
|
||||||
if (supportedExtensions.Contains("VK_EXT_robustness2"))
|
if (supportedExtensions.Contains("VK_EXT_robustness2"))
|
||||||
|
@@ -195,6 +195,11 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SType = StructureType.PhysicalDeviceFeatures2
|
SType = StructureType.PhysicalDeviceFeatures2
|
||||||
};
|
};
|
||||||
|
|
||||||
|
PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT featuresPrimitiveTopologyListRestart = new PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT()
|
||||||
|
{
|
||||||
|
SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt
|
||||||
|
};
|
||||||
|
|
||||||
PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT()
|
PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT()
|
||||||
{
|
{
|
||||||
SType = StructureType.PhysicalDeviceRobustness2FeaturesExt
|
SType = StructureType.PhysicalDeviceRobustness2FeaturesExt
|
||||||
@@ -215,8 +220,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr
|
SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (supportedExtensions.Contains("VK_EXT_primitive_topology_list_restart"))
|
||||||
|
{
|
||||||
|
features2.PNext = &featuresPrimitiveTopologyListRestart;
|
||||||
|
}
|
||||||
|
|
||||||
if (supportedExtensions.Contains("VK_EXT_robustness2"))
|
if (supportedExtensions.Contains("VK_EXT_robustness2"))
|
||||||
{
|
{
|
||||||
|
featuresRobustness2.PNext = features2.PNext;
|
||||||
features2.PNext = &featuresRobustness2;
|
features2.PNext = &featuresRobustness2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,6 +299,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
features2.Features.MultiViewport,
|
features2.Features.MultiViewport,
|
||||||
featuresRobustness2.NullDescriptor || IsMoltenVk,
|
featuresRobustness2.NullDescriptor || IsMoltenVk,
|
||||||
supportedExtensions.Contains(KhrPushDescriptor.ExtensionName),
|
supportedExtensions.Contains(KhrPushDescriptor.ExtensionName),
|
||||||
|
featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart,
|
||||||
|
featuresPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart,
|
||||||
supportsTransformFeedback,
|
supportsTransformFeedback,
|
||||||
propertiesTransformFeedback.TransformFeedbackQueries,
|
propertiesTransformFeedback.TransformFeedbackQueries,
|
||||||
features2.Features.OcclusionQueryPrecise,
|
features2.Features.OcclusionQueryPrecise,
|
||||||
@@ -546,6 +559,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
supportsBlendEquationAdvanced: Capabilities.SupportsBlendEquationAdvanced,
|
supportsBlendEquationAdvanced: Capabilities.SupportsBlendEquationAdvanced,
|
||||||
supportsFragmentShaderInterlock: Capabilities.SupportsFragmentShaderInterlock,
|
supportsFragmentShaderInterlock: Capabilities.SupportsFragmentShaderInterlock,
|
||||||
supportsFragmentShaderOrderingIntel: false,
|
supportsFragmentShaderOrderingIntel: false,
|
||||||
|
supportsGeometryShader: Capabilities.SupportsGeometryShader,
|
||||||
supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough,
|
supportsGeometryShaderPassthrough: Capabilities.SupportsGeometryShaderPassthrough,
|
||||||
supportsImageLoadFormatted: features2.Features.ShaderStorageImageReadWithoutFormat,
|
supportsImageLoadFormatted: features2.Features.ShaderStorageImageReadWithoutFormat,
|
||||||
supportsLayerVertexTessellation: featuresVk12.ShaderOutputLayer,
|
supportsLayerVertexTessellation: featuresVk12.ShaderOutputLayer,
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.Graphics.GAL;
|
using Ryujinx.Graphics.GAL;
|
||||||
|
using Ryujinx.Graphics.Vulkan.Effects;
|
||||||
using Silk.NET.Vulkan;
|
using Silk.NET.Vulkan;
|
||||||
using Silk.NET.Vulkan.Extensions.KHR;
|
using Silk.NET.Vulkan.Extensions.KHR;
|
||||||
using System;
|
using System;
|
||||||
@@ -29,6 +30,14 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
private bool _vsyncEnabled;
|
private bool _vsyncEnabled;
|
||||||
private bool _vsyncModeChanged;
|
private bool _vsyncModeChanged;
|
||||||
private VkFormat _format;
|
private VkFormat _format;
|
||||||
|
private AntiAliasing _currentAntiAliasing;
|
||||||
|
private bool _updateEffect;
|
||||||
|
private IPostProcessingEffect _effect;
|
||||||
|
private IScalingFilter _scalingFilter;
|
||||||
|
private bool _isLinear;
|
||||||
|
private float _scalingFilterLevel;
|
||||||
|
private bool _updateScalingFilter;
|
||||||
|
private ScalingFilter _currentScalingFilter;
|
||||||
|
|
||||||
public unsafe Window(VulkanRenderer gd, SurfaceKHR surface, PhysicalDevice physicalDevice, Device device)
|
public unsafe Window(VulkanRenderer gd, SurfaceKHR surface, PhysicalDevice physicalDevice, Device device)
|
||||||
{
|
{
|
||||||
@@ -116,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
ImageFormat = surfaceFormat.Format,
|
ImageFormat = surfaceFormat.Format,
|
||||||
ImageColorSpace = surfaceFormat.ColorSpace,
|
ImageColorSpace = surfaceFormat.ColorSpace,
|
||||||
ImageExtent = extent,
|
ImageExtent = extent,
|
||||||
ImageUsage = ImageUsageFlags.ColorAttachmentBit | ImageUsageFlags.TransferDstBit,
|
ImageUsage = ImageUsageFlags.ColorAttachmentBit | ImageUsageFlags.TransferDstBit | ImageUsageFlags.StorageBit,
|
||||||
ImageSharingMode = SharingMode.Exclusive,
|
ImageSharingMode = SharingMode.Exclusive,
|
||||||
ImageArrayLayers = 1,
|
ImageArrayLayers = 1,
|
||||||
PreTransform = capabilities.CurrentTransform,
|
PreTransform = capabilities.CurrentTransform,
|
||||||
@@ -280,6 +289,13 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
var view = (TextureView)texture;
|
var view = (TextureView)texture;
|
||||||
|
|
||||||
|
UpdateEffect();
|
||||||
|
|
||||||
|
if (_effect != null)
|
||||||
|
{
|
||||||
|
view = _effect.Run(view, cbs, _width, _height);
|
||||||
|
}
|
||||||
|
|
||||||
int srcX0, srcX1, srcY0, srcY1;
|
int srcX0, srcX1, srcY0, srcY1;
|
||||||
float scale = view.ScaleFactor;
|
float scale = view.ScaleFactor;
|
||||||
|
|
||||||
@@ -315,6 +331,18 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
if (ScreenCaptureRequested)
|
if (ScreenCaptureRequested)
|
||||||
{
|
{
|
||||||
|
if (_effect != null)
|
||||||
|
{
|
||||||
|
_gd.CommandBufferPool.Return(
|
||||||
|
cbs,
|
||||||
|
null,
|
||||||
|
stackalloc[] { PipelineStageFlags.ColorAttachmentOutputBit },
|
||||||
|
null);
|
||||||
|
_gd.FlushAllCommands();
|
||||||
|
cbs.GetFence().Wait();
|
||||||
|
cbs = _gd.CommandBufferPool.Rent();
|
||||||
|
}
|
||||||
|
|
||||||
CaptureFrame(view, srcX0, srcY0, srcX1 - srcX0, srcY1 - srcY0, view.Info.Format.IsBgr(), crop.FlipX, crop.FlipY);
|
CaptureFrame(view, srcX0, srcY0, srcX1 - srcX0, srcY1 - srcY0, view.Info.Format.IsBgr(), crop.FlipX, crop.FlipY);
|
||||||
|
|
||||||
ScreenCaptureRequested = false;
|
ScreenCaptureRequested = false;
|
||||||
@@ -335,6 +363,21 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
int dstY0 = crop.FlipY ? dstPaddingY : _height - dstPaddingY;
|
int dstY0 = crop.FlipY ? dstPaddingY : _height - dstPaddingY;
|
||||||
int dstY1 = crop.FlipY ? _height - dstPaddingY : dstPaddingY;
|
int dstY1 = crop.FlipY ? _height - dstPaddingY : dstPaddingY;
|
||||||
|
|
||||||
|
if (_scalingFilter != null)
|
||||||
|
{
|
||||||
|
_scalingFilter.Run(
|
||||||
|
view,
|
||||||
|
cbs,
|
||||||
|
_swapchainImageViews[nextImage],
|
||||||
|
_format,
|
||||||
|
_width,
|
||||||
|
_height,
|
||||||
|
new Extents2D(srcX0, srcY0, srcX1, srcY1),
|
||||||
|
new Extents2D(dstX0, dstY0, dstX1, dstY1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
_gd.HelperShader.BlitColor(
|
_gd.HelperShader.BlitColor(
|
||||||
_gd,
|
_gd,
|
||||||
cbs,
|
cbs,
|
||||||
@@ -347,8 +390,9 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
false,
|
false,
|
||||||
new Extents2D(srcX0, srcY0, srcX1, srcY1),
|
new Extents2D(srcX0, srcY0, srcX1, srcY1),
|
||||||
new Extents2D(dstX0, dstY1, dstX1, dstY0),
|
new Extents2D(dstX0, dstY1, dstX1, dstY0),
|
||||||
true,
|
_isLinear,
|
||||||
true);
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
Transition(
|
Transition(
|
||||||
cbs.CommandBuffer,
|
cbs.CommandBuffer,
|
||||||
@@ -387,6 +431,95 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void SetAntiAliasing(AntiAliasing effect)
|
||||||
|
{
|
||||||
|
if (_currentAntiAliasing == effect && _effect != null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentAntiAliasing = effect;
|
||||||
|
|
||||||
|
_updateEffect = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetScalingFilter(ScalingFilter type)
|
||||||
|
{
|
||||||
|
if (_currentScalingFilter == type && _effect != null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentScalingFilter = type;
|
||||||
|
|
||||||
|
_updateScalingFilter = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateEffect()
|
||||||
|
{
|
||||||
|
if (_updateEffect)
|
||||||
|
{
|
||||||
|
_updateEffect = false;
|
||||||
|
|
||||||
|
switch (_currentAntiAliasing)
|
||||||
|
{
|
||||||
|
case AntiAliasing.Fxaa:
|
||||||
|
_effect?.Dispose();
|
||||||
|
_effect = new FxaaPostProcessingEffect(_gd, _device);
|
||||||
|
break;
|
||||||
|
case AntiAliasing.None:
|
||||||
|
_effect?.Dispose();
|
||||||
|
_effect = null;
|
||||||
|
break;
|
||||||
|
case AntiAliasing.SmaaLow:
|
||||||
|
case AntiAliasing.SmaaMedium:
|
||||||
|
case AntiAliasing.SmaaHigh:
|
||||||
|
case AntiAliasing.SmaaUltra:
|
||||||
|
var quality = _currentAntiAliasing - AntiAliasing.SmaaLow;
|
||||||
|
if (_effect is SmaaPostProcessingEffect smaa)
|
||||||
|
{
|
||||||
|
smaa.Quality = quality;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_effect?.Dispose();
|
||||||
|
_effect = new SmaaPostProcessingEffect(_gd, _device, quality);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_updateScalingFilter)
|
||||||
|
{
|
||||||
|
_updateScalingFilter = false;
|
||||||
|
|
||||||
|
switch (_currentScalingFilter)
|
||||||
|
{
|
||||||
|
case ScalingFilter.Bilinear:
|
||||||
|
case ScalingFilter.Nearest:
|
||||||
|
_scalingFilter?.Dispose();
|
||||||
|
_scalingFilter = null;
|
||||||
|
_isLinear = _currentScalingFilter == ScalingFilter.Bilinear;
|
||||||
|
break;
|
||||||
|
case ScalingFilter.Fsr:
|
||||||
|
if (_scalingFilter is not FsrScalingFilter)
|
||||||
|
{
|
||||||
|
_scalingFilter?.Dispose();
|
||||||
|
_scalingFilter = new FsrScalingFilter(_gd, _device);
|
||||||
|
}
|
||||||
|
|
||||||
|
_scalingFilter.Level = _scalingFilterLevel;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void SetScalingFilterLevel(float level)
|
||||||
|
{
|
||||||
|
_scalingFilterLevel = level;
|
||||||
|
_updateScalingFilter = true;
|
||||||
|
}
|
||||||
|
|
||||||
private unsafe void Transition(
|
private unsafe void Transition(
|
||||||
CommandBuffer commandBuffer,
|
CommandBuffer commandBuffer,
|
||||||
Image image,
|
Image image,
|
||||||
@@ -456,8 +589,10 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
}
|
}
|
||||||
|
|
||||||
_gd.SwapchainApi.DestroySwapchain(_device, _swapchain, null);
|
_gd.SwapchainApi.DestroySwapchain(_device, _swapchain, null);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_effect?.Dispose();
|
||||||
|
_scalingFilter?.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -11,5 +11,8 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
public abstract void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback);
|
public abstract void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback);
|
||||||
public abstract void SetSize(int width, int height);
|
public abstract void SetSize(int width, int height);
|
||||||
public abstract void ChangeVSyncMode(bool vsyncEnabled);
|
public abstract void ChangeVSyncMode(bool vsyncEnabled);
|
||||||
|
public abstract void SetAntiAliasing(AntiAliasing effect);
|
||||||
|
public abstract void SetScalingFilter(ScalingFilter scalerType);
|
||||||
|
public abstract void SetScalingFilterLevel(float scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm.StaticService.Types
|
|||||||
|
|
||||||
public IpAddressSetting(IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastIPAddressInformation)
|
public IpAddressSetting(IPInterfaceProperties interfaceProperties, UnicastIPAddressInformation unicastIPAddressInformation)
|
||||||
{
|
{
|
||||||
IsDhcpEnabled = !OperatingSystem.IsMacOS() && interfaceProperties.DhcpServerAddresses.Count != 0;
|
IsDhcpEnabled = OperatingSystem.IsMacOS() || interfaceProperties.DhcpServerAddresses.Count != 0;
|
||||||
Address = new IpV4Address(unicastIPAddressInformation.Address);
|
Address = new IpV4Address(unicastIPAddressInformation.Address);
|
||||||
IPv4Mask = new IpV4Address(unicastIPAddressInformation.IPv4Mask);
|
IPv4Mask = new IpV4Address(unicastIPAddressInformation.IPv4Mask);
|
||||||
GatewayAddress = new IpV4Address(interfaceProperties.GatewayAddresses[0].Address);
|
GatewayAddress = new IpV4Address(interfaceProperties.GatewayAddresses[0].Address);
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types;
|
using Ryujinx.HLE.HOS.Services.Sockets.Bsd.Types;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
|
|
||||||
@@ -9,85 +10,133 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
|||||||
private static readonly Dictionary<WsaError, LinuxError> _errorMap = new()
|
private static readonly Dictionary<WsaError, LinuxError> _errorMap = new()
|
||||||
{
|
{
|
||||||
// WSAEINTR
|
// WSAEINTR
|
||||||
{WsaError.WSAEINTR, LinuxError.EINTR},
|
{ WsaError.WSAEINTR, LinuxError.EINTR },
|
||||||
// WSAEWOULDBLOCK
|
// WSAEWOULDBLOCK
|
||||||
{WsaError.WSAEWOULDBLOCK, LinuxError.EWOULDBLOCK},
|
{ WsaError.WSAEWOULDBLOCK, LinuxError.EWOULDBLOCK },
|
||||||
// WSAEINPROGRESS
|
// WSAEINPROGRESS
|
||||||
{WsaError.WSAEINPROGRESS, LinuxError.EINPROGRESS},
|
{ WsaError.WSAEINPROGRESS, LinuxError.EINPROGRESS },
|
||||||
// WSAEALREADY
|
// WSAEALREADY
|
||||||
{WsaError.WSAEALREADY, LinuxError.EALREADY},
|
{ WsaError.WSAEALREADY, LinuxError.EALREADY },
|
||||||
// WSAENOTSOCK
|
// WSAENOTSOCK
|
||||||
{WsaError.WSAENOTSOCK, LinuxError.ENOTSOCK},
|
{ WsaError.WSAENOTSOCK, LinuxError.ENOTSOCK },
|
||||||
// WSAEDESTADDRREQ
|
// WSAEDESTADDRREQ
|
||||||
{WsaError.WSAEDESTADDRREQ, LinuxError.EDESTADDRREQ},
|
{ WsaError.WSAEDESTADDRREQ, LinuxError.EDESTADDRREQ },
|
||||||
// WSAEMSGSIZE
|
// WSAEMSGSIZE
|
||||||
{WsaError.WSAEMSGSIZE, LinuxError.EMSGSIZE},
|
{ WsaError.WSAEMSGSIZE, LinuxError.EMSGSIZE },
|
||||||
// WSAEPROTOTYPE
|
// WSAEPROTOTYPE
|
||||||
{WsaError.WSAEPROTOTYPE, LinuxError.EPROTOTYPE},
|
{ WsaError.WSAEPROTOTYPE, LinuxError.EPROTOTYPE },
|
||||||
// WSAENOPROTOOPT
|
// WSAENOPROTOOPT
|
||||||
{WsaError.WSAENOPROTOOPT, LinuxError.ENOPROTOOPT},
|
{ WsaError.WSAENOPROTOOPT, LinuxError.ENOPROTOOPT },
|
||||||
// WSAEPROTONOSUPPORT
|
// WSAEPROTONOSUPPORT
|
||||||
{WsaError.WSAEPROTONOSUPPORT, LinuxError.EPROTONOSUPPORT},
|
{ WsaError.WSAEPROTONOSUPPORT, LinuxError.EPROTONOSUPPORT },
|
||||||
// WSAESOCKTNOSUPPORT
|
// WSAESOCKTNOSUPPORT
|
||||||
{WsaError.WSAESOCKTNOSUPPORT, LinuxError.ESOCKTNOSUPPORT},
|
{ WsaError.WSAESOCKTNOSUPPORT, LinuxError.ESOCKTNOSUPPORT },
|
||||||
// WSAEOPNOTSUPP
|
// WSAEOPNOTSUPP
|
||||||
{WsaError.WSAEOPNOTSUPP, LinuxError.EOPNOTSUPP},
|
{ WsaError.WSAEOPNOTSUPP, LinuxError.EOPNOTSUPP },
|
||||||
// WSAEPFNOSUPPORT
|
// WSAEPFNOSUPPORT
|
||||||
{WsaError.WSAEPFNOSUPPORT, LinuxError.EPFNOSUPPORT},
|
{ WsaError.WSAEPFNOSUPPORT, LinuxError.EPFNOSUPPORT },
|
||||||
// WSAEAFNOSUPPORT
|
// WSAEAFNOSUPPORT
|
||||||
{WsaError.WSAEAFNOSUPPORT, LinuxError.EAFNOSUPPORT},
|
{ WsaError.WSAEAFNOSUPPORT, LinuxError.EAFNOSUPPORT },
|
||||||
// WSAEADDRINUSE
|
// WSAEADDRINUSE
|
||||||
{WsaError.WSAEADDRINUSE, LinuxError.EADDRINUSE},
|
{ WsaError.WSAEADDRINUSE, LinuxError.EADDRINUSE },
|
||||||
// WSAEADDRNOTAVAIL
|
// WSAEADDRNOTAVAIL
|
||||||
{WsaError.WSAEADDRNOTAVAIL, LinuxError.EADDRNOTAVAIL},
|
{ WsaError.WSAEADDRNOTAVAIL, LinuxError.EADDRNOTAVAIL },
|
||||||
// WSAENETDOWN
|
// WSAENETDOWN
|
||||||
{WsaError.WSAENETDOWN, LinuxError.ENETDOWN},
|
{ WsaError.WSAENETDOWN, LinuxError.ENETDOWN },
|
||||||
// WSAENETUNREACH
|
// WSAENETUNREACH
|
||||||
{WsaError.WSAENETUNREACH, LinuxError.ENETUNREACH},
|
{ WsaError.WSAENETUNREACH, LinuxError.ENETUNREACH },
|
||||||
// WSAENETRESET
|
// WSAENETRESET
|
||||||
{WsaError.WSAENETRESET, LinuxError.ENETRESET},
|
{ WsaError.WSAENETRESET, LinuxError.ENETRESET },
|
||||||
// WSAECONNABORTED
|
// WSAECONNABORTED
|
||||||
{WsaError.WSAECONNABORTED, LinuxError.ECONNABORTED},
|
{ WsaError.WSAECONNABORTED, LinuxError.ECONNABORTED },
|
||||||
// WSAECONNRESET
|
// WSAECONNRESET
|
||||||
{WsaError.WSAECONNRESET, LinuxError.ECONNRESET},
|
{ WsaError.WSAECONNRESET, LinuxError.ECONNRESET },
|
||||||
// WSAENOBUFS
|
// WSAENOBUFS
|
||||||
{WsaError.WSAENOBUFS, LinuxError.ENOBUFS},
|
{ WsaError.WSAENOBUFS, LinuxError.ENOBUFS },
|
||||||
// WSAEISCONN
|
// WSAEISCONN
|
||||||
{WsaError.WSAEISCONN, LinuxError.EISCONN},
|
{ WsaError.WSAEISCONN, LinuxError.EISCONN },
|
||||||
// WSAENOTCONN
|
// WSAENOTCONN
|
||||||
{WsaError.WSAENOTCONN, LinuxError.ENOTCONN},
|
{ WsaError.WSAENOTCONN, LinuxError.ENOTCONN },
|
||||||
// WSAESHUTDOWN
|
// WSAESHUTDOWN
|
||||||
{WsaError.WSAESHUTDOWN, LinuxError.ESHUTDOWN},
|
{ WsaError.WSAESHUTDOWN, LinuxError.ESHUTDOWN },
|
||||||
// WSAETOOMANYREFS
|
// WSAETOOMANYREFS
|
||||||
{WsaError.WSAETOOMANYREFS, LinuxError.ETOOMANYREFS},
|
{ WsaError.WSAETOOMANYREFS, LinuxError.ETOOMANYREFS },
|
||||||
// WSAETIMEDOUT
|
// WSAETIMEDOUT
|
||||||
{WsaError.WSAETIMEDOUT, LinuxError.ETIMEDOUT},
|
{ WsaError.WSAETIMEDOUT, LinuxError.ETIMEDOUT },
|
||||||
// WSAECONNREFUSED
|
// WSAECONNREFUSED
|
||||||
{WsaError.WSAECONNREFUSED, LinuxError.ECONNREFUSED},
|
{ WsaError.WSAECONNREFUSED, LinuxError.ECONNREFUSED },
|
||||||
// WSAELOOP
|
// WSAELOOP
|
||||||
{WsaError.WSAELOOP, LinuxError.ELOOP},
|
{ WsaError.WSAELOOP, LinuxError.ELOOP },
|
||||||
// WSAENAMETOOLONG
|
// WSAENAMETOOLONG
|
||||||
{WsaError.WSAENAMETOOLONG, LinuxError.ENAMETOOLONG},
|
{ WsaError.WSAENAMETOOLONG, LinuxError.ENAMETOOLONG },
|
||||||
// WSAEHOSTDOWN
|
// WSAEHOSTDOWN
|
||||||
{WsaError.WSAEHOSTDOWN, LinuxError.EHOSTDOWN},
|
{ WsaError.WSAEHOSTDOWN, LinuxError.EHOSTDOWN },
|
||||||
// WSAEHOSTUNREACH
|
// WSAEHOSTUNREACH
|
||||||
{WsaError.WSAEHOSTUNREACH, LinuxError.EHOSTUNREACH},
|
{ WsaError.WSAEHOSTUNREACH, LinuxError.EHOSTUNREACH },
|
||||||
// WSAENOTEMPTY
|
// WSAENOTEMPTY
|
||||||
{WsaError.WSAENOTEMPTY, LinuxError.ENOTEMPTY},
|
{ WsaError.WSAENOTEMPTY, LinuxError.ENOTEMPTY },
|
||||||
// WSAEUSERS
|
// WSAEUSERS
|
||||||
{WsaError.WSAEUSERS, LinuxError.EUSERS},
|
{ WsaError.WSAEUSERS, LinuxError.EUSERS },
|
||||||
// WSAEDQUOT
|
// WSAEDQUOT
|
||||||
{WsaError.WSAEDQUOT, LinuxError.EDQUOT},
|
{ WsaError.WSAEDQUOT, LinuxError.EDQUOT },
|
||||||
// WSAESTALE
|
// WSAESTALE
|
||||||
{WsaError.WSAESTALE, LinuxError.ESTALE},
|
{ WsaError.WSAESTALE, LinuxError.ESTALE },
|
||||||
// WSAEREMOTE
|
// WSAEREMOTE
|
||||||
{WsaError.WSAEREMOTE, LinuxError.EREMOTE},
|
{ WsaError.WSAEREMOTE, LinuxError.EREMOTE },
|
||||||
// WSAEINVAL
|
// WSAEINVAL
|
||||||
{WsaError.WSAEINVAL, LinuxError.EINVAL},
|
{ WsaError.WSAEINVAL, LinuxError.EINVAL },
|
||||||
// WSAEFAULT
|
// WSAEFAULT
|
||||||
{WsaError.WSAEFAULT, LinuxError.EFAULT},
|
{ WsaError.WSAEFAULT, LinuxError.EFAULT },
|
||||||
// NOERROR
|
// NOERROR
|
||||||
{0, 0}
|
{ 0, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
private static readonly Dictionary<int, LinuxError> _errorMapMacOs = new()
|
||||||
|
{
|
||||||
|
{ 35, LinuxError.EAGAIN },
|
||||||
|
{ 11, LinuxError.EDEADLOCK },
|
||||||
|
{ 91, LinuxError.ENOMSG },
|
||||||
|
{ 90, LinuxError.EIDRM },
|
||||||
|
{ 77, LinuxError.ENOLCK },
|
||||||
|
{ 70, LinuxError.ESTALE },
|
||||||
|
{ 36, LinuxError.EINPROGRESS },
|
||||||
|
{ 37, LinuxError.EALREADY },
|
||||||
|
{ 38, LinuxError.ENOTSOCK },
|
||||||
|
{ 39, LinuxError.EDESTADDRREQ },
|
||||||
|
{ 40, LinuxError.EMSGSIZE },
|
||||||
|
{ 41, LinuxError.EPROTOTYPE },
|
||||||
|
{ 42, LinuxError.ENOPROTOOPT },
|
||||||
|
{ 43, LinuxError.EPROTONOSUPPORT },
|
||||||
|
{ 44, LinuxError.ESOCKTNOSUPPORT },
|
||||||
|
{ 45, LinuxError.EOPNOTSUPP },
|
||||||
|
{ 46, LinuxError.EPFNOSUPPORT },
|
||||||
|
{ 47, LinuxError.EAFNOSUPPORT },
|
||||||
|
{ 48, LinuxError.EADDRINUSE },
|
||||||
|
{ 49, LinuxError.EADDRNOTAVAIL },
|
||||||
|
{ 50, LinuxError.ENETDOWN },
|
||||||
|
{ 51, LinuxError.ENETUNREACH },
|
||||||
|
{ 52, LinuxError.ENETRESET },
|
||||||
|
{ 53, LinuxError.ECONNABORTED },
|
||||||
|
{ 54, LinuxError.ECONNRESET },
|
||||||
|
{ 55, LinuxError.ENOBUFS },
|
||||||
|
{ 56, LinuxError.EISCONN },
|
||||||
|
{ 57, LinuxError.ENOTCONN },
|
||||||
|
{ 58, LinuxError.ESHUTDOWN },
|
||||||
|
{ 60, LinuxError.ETIMEDOUT },
|
||||||
|
{ 61, LinuxError.ECONNREFUSED },
|
||||||
|
{ 64, LinuxError.EHOSTDOWN },
|
||||||
|
{ 65, LinuxError.EHOSTUNREACH },
|
||||||
|
{ 68, LinuxError.EUSERS },
|
||||||
|
{ 62, LinuxError.ELOOP },
|
||||||
|
{ 63, LinuxError.ENAMETOOLONG },
|
||||||
|
{ 66, LinuxError.ENOTEMPTY },
|
||||||
|
{ 69, LinuxError.EDQUOT },
|
||||||
|
{ 71, LinuxError.EREMOTE },
|
||||||
|
{ 78, LinuxError.ENOSYS },
|
||||||
|
{ 59, LinuxError.ETOOMANYREFS },
|
||||||
|
{ 92, LinuxError.EILSEQ },
|
||||||
|
{ 89, LinuxError.ECANCELED },
|
||||||
|
{ 84, LinuxError.EOVERFLOW }
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly Dictionary<BsdSocketOption, SocketOptionName> _soSocketOptionMap = new()
|
private static readonly Dictionary<BsdSocketOption, SocketOptionName> _soSocketOptionMap = new()
|
||||||
@@ -136,12 +185,22 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
|||||||
|
|
||||||
public static LinuxError ConvertError(WsaError errorCode)
|
public static LinuxError ConvertError(WsaError errorCode)
|
||||||
{
|
{
|
||||||
if (!_errorMap.TryGetValue(errorCode, out LinuxError errno))
|
if (OperatingSystem.IsMacOS())
|
||||||
{
|
{
|
||||||
errno = (LinuxError)errorCode;
|
if (_errorMapMacOs.TryGetValue((int)errorCode, out LinuxError errno))
|
||||||
|
{
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (_errorMap.TryGetValue(errorCode, out LinuxError errno))
|
||||||
|
{
|
||||||
|
return errno;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return errno;
|
return (LinuxError)errorCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryConvertSocketOption(BsdSocketOption option, SocketOptionLevel level, out SocketOptionName name)
|
public static bool TryConvertSocketOption(BsdSocketOption option, SocketOptionLevel level, out SocketOptionName name)
|
||||||
|
@@ -14,7 +14,7 @@ namespace Ryujinx.Ui.Common.Configuration
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current version of the file format
|
/// The current version of the file format
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const int CurrentVersion = 43;
|
public const int CurrentVersion = 44;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Version of the configuration file format
|
/// Version of the configuration file format
|
||||||
@@ -51,6 +51,21 @@ namespace Ryujinx.Ui.Common.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public AspectRatio AspectRatio { get; set; }
|
public AspectRatio AspectRatio { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies anti-aliasing to the renderer.
|
||||||
|
/// </summary>
|
||||||
|
public AntiAliasing AntiAliasing { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the framebuffer upscaling type.
|
||||||
|
/// </summary>
|
||||||
|
public ScalingFilter ScalingFilter { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the framebuffer upscaling level.
|
||||||
|
/// </summary>
|
||||||
|
public int ScalingFilterLevel { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Dumps shaders in this local directory
|
/// Dumps shaders in this local directory
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@@ -433,6 +433,21 @@ namespace Ryujinx.Ui.Common.Configuration
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ReactiveObject<GraphicsBackend> GraphicsBackend { get; private set; }
|
public ReactiveObject<GraphicsBackend> GraphicsBackend { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies anti-aliasing to the renderer.
|
||||||
|
/// </summary>
|
||||||
|
public ReactiveObject<AntiAliasing> AntiAliasing { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the framebuffer upscaling type.
|
||||||
|
/// </summary>
|
||||||
|
public ReactiveObject<ScalingFilter> ScalingFilter { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the framebuffer upscaling level.
|
||||||
|
/// </summary>
|
||||||
|
public ReactiveObject<int> ScalingFilterLevel { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Preferred GPU
|
/// Preferred GPU
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -463,6 +478,12 @@ namespace Ryujinx.Ui.Common.Configuration
|
|||||||
PreferredGpu.Event += static (sender, e) => LogValueChange(sender, e, nameof(PreferredGpu));
|
PreferredGpu.Event += static (sender, e) => LogValueChange(sender, e, nameof(PreferredGpu));
|
||||||
EnableMacroHLE = new ReactiveObject<bool>();
|
EnableMacroHLE = new ReactiveObject<bool>();
|
||||||
EnableMacroHLE.Event += static (sender, e) => LogValueChange(sender, e, nameof(EnableMacroHLE));
|
EnableMacroHLE.Event += static (sender, e) => LogValueChange(sender, e, nameof(EnableMacroHLE));
|
||||||
|
AntiAliasing = new ReactiveObject<AntiAliasing>();
|
||||||
|
AntiAliasing.Event += static (sender, e) => LogValueChange(sender, e, nameof(AntiAliasing));
|
||||||
|
ScalingFilter = new ReactiveObject<ScalingFilter>();
|
||||||
|
ScalingFilter.Event += static (sender, e) => LogValueChange(sender, e, nameof(ScalingFilter));
|
||||||
|
ScalingFilterLevel = new ReactiveObject<int>();
|
||||||
|
ScalingFilterLevel.Event += static (sender, e) => LogValueChange(sender, e, nameof(ScalingFilterLevel));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -540,6 +561,9 @@ namespace Ryujinx.Ui.Common.Configuration
|
|||||||
ResScaleCustom = Graphics.ResScaleCustom,
|
ResScaleCustom = Graphics.ResScaleCustom,
|
||||||
MaxAnisotropy = Graphics.MaxAnisotropy,
|
MaxAnisotropy = Graphics.MaxAnisotropy,
|
||||||
AspectRatio = Graphics.AspectRatio,
|
AspectRatio = Graphics.AspectRatio,
|
||||||
|
AntiAliasing = Graphics.AntiAliasing,
|
||||||
|
ScalingFilter = Graphics.ScalingFilter,
|
||||||
|
ScalingFilterLevel = Graphics.ScalingFilterLevel,
|
||||||
GraphicsShadersDumpPath = Graphics.ShadersDumpPath,
|
GraphicsShadersDumpPath = Graphics.ShadersDumpPath,
|
||||||
LoggingEnableDebug = Logger.EnableDebug,
|
LoggingEnableDebug = Logger.EnableDebug,
|
||||||
LoggingEnableStub = Logger.EnableStub,
|
LoggingEnableStub = Logger.EnableStub,
|
||||||
@@ -651,6 +675,9 @@ namespace Ryujinx.Ui.Common.Configuration
|
|||||||
Graphics.EnableShaderCache.Value = true;
|
Graphics.EnableShaderCache.Value = true;
|
||||||
Graphics.EnableTextureRecompression.Value = false;
|
Graphics.EnableTextureRecompression.Value = false;
|
||||||
Graphics.EnableMacroHLE.Value = true;
|
Graphics.EnableMacroHLE.Value = true;
|
||||||
|
Graphics.AntiAliasing.Value = AntiAliasing.None;
|
||||||
|
Graphics.ScalingFilter.Value = ScalingFilter.Bilinear;
|
||||||
|
Graphics.ScalingFilterLevel.Value = 80;
|
||||||
System.EnablePtc.Value = true;
|
System.EnablePtc.Value = true;
|
||||||
System.EnableInternetAccess.Value = false;
|
System.EnableInternetAccess.Value = false;
|
||||||
System.EnableFsIntegrityChecks.Value = true;
|
System.EnableFsIntegrityChecks.Value = true;
|
||||||
@@ -1208,6 +1235,17 @@ namespace Ryujinx.Ui.Common.Configuration
|
|||||||
configurationFileFormat.UseHypervisor = true;
|
configurationFileFormat.UseHypervisor = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (configurationFileFormat.Version < 44)
|
||||||
|
{
|
||||||
|
Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 42.");
|
||||||
|
|
||||||
|
configurationFileFormat.AntiAliasing = AntiAliasing.None;
|
||||||
|
configurationFileFormat.ScalingFilter = ScalingFilter.Bilinear;
|
||||||
|
configurationFileFormat.ScalingFilterLevel = 80;
|
||||||
|
|
||||||
|
configurationFileUpdated = true;
|
||||||
|
}
|
||||||
|
|
||||||
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
|
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
|
||||||
Graphics.ResScale.Value = configurationFileFormat.ResScale;
|
Graphics.ResScale.Value = configurationFileFormat.ResScale;
|
||||||
Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom;
|
Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom;
|
||||||
@@ -1217,6 +1255,9 @@ namespace Ryujinx.Ui.Common.Configuration
|
|||||||
Graphics.BackendThreading.Value = configurationFileFormat.BackendThreading;
|
Graphics.BackendThreading.Value = configurationFileFormat.BackendThreading;
|
||||||
Graphics.GraphicsBackend.Value = configurationFileFormat.GraphicsBackend;
|
Graphics.GraphicsBackend.Value = configurationFileFormat.GraphicsBackend;
|
||||||
Graphics.PreferredGpu.Value = configurationFileFormat.PreferredGpu;
|
Graphics.PreferredGpu.Value = configurationFileFormat.PreferredGpu;
|
||||||
|
Graphics.AntiAliasing.Value = configurationFileFormat.AntiAliasing;
|
||||||
|
Graphics.ScalingFilter.Value = configurationFileFormat.ScalingFilter;
|
||||||
|
Graphics.ScalingFilterLevel.Value = configurationFileFormat.ScalingFilterLevel;
|
||||||
Logger.EnableDebug.Value = configurationFileFormat.LoggingEnableDebug;
|
Logger.EnableDebug.Value = configurationFileFormat.LoggingEnableDebug;
|
||||||
Logger.EnableStub.Value = configurationFileFormat.LoggingEnableStub;
|
Logger.EnableStub.Value = configurationFileFormat.LoggingEnableStub;
|
||||||
Logger.EnableInfo.Value = configurationFileFormat.LoggingEnableInfo;
|
Logger.EnableInfo.Value = configurationFileFormat.LoggingEnableInfo;
|
||||||
|
@@ -27,6 +27,7 @@ namespace Ryujinx.Ui
|
|||||||
using Image = SixLabors.ImageSharp.Image;
|
using Image = SixLabors.ImageSharp.Image;
|
||||||
using Key = Input.Key;
|
using Key = Input.Key;
|
||||||
using Switch = HLE.Switch;
|
using Switch = HLE.Switch;
|
||||||
|
using ScalingFilter = Graphics.GAL.ScalingFilter;
|
||||||
|
|
||||||
public abstract class RendererWidgetBase : DrawingArea
|
public abstract class RendererWidgetBase : DrawingArea
|
||||||
{
|
{
|
||||||
@@ -116,6 +117,21 @@ namespace Ryujinx.Ui
|
|||||||
_lastCursorMoveTime = Stopwatch.GetTimestamp();
|
_lastCursorMoveTime = Stopwatch.GetTimestamp();
|
||||||
|
|
||||||
ConfigurationState.Instance.HideCursorOnIdle.Event += HideCursorStateChanged;
|
ConfigurationState.Instance.HideCursorOnIdle.Event += HideCursorStateChanged;
|
||||||
|
ConfigurationState.Instance.Graphics.AntiAliasing.Event += UpdateAnriAliasing;
|
||||||
|
ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter;
|
||||||
|
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateScalingFilterLevel(object sender, ReactiveEventArgs<int> e)
|
||||||
|
{
|
||||||
|
Renderer.Window.SetScalingFilter((ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value);
|
||||||
|
Renderer.Window.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateScalingFilter(object sender, ReactiveEventArgs<Ryujinx.Common.Configuration.ScalingFilter> e)
|
||||||
|
{
|
||||||
|
Renderer.Window.SetScalingFilter((ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value);
|
||||||
|
Renderer.Window.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void InitializeRenderer();
|
public abstract void InitializeRenderer();
|
||||||
@@ -149,11 +165,19 @@ namespace Ryujinx.Ui
|
|||||||
private void Renderer_Destroyed(object sender, EventArgs e)
|
private void Renderer_Destroyed(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
ConfigurationState.Instance.HideCursorOnIdle.Event -= HideCursorStateChanged;
|
ConfigurationState.Instance.HideCursorOnIdle.Event -= HideCursorStateChanged;
|
||||||
|
ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAnriAliasing;
|
||||||
|
ConfigurationState.Instance.Graphics.ScalingFilter.Event -= UpdateScalingFilter;
|
||||||
|
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel;
|
||||||
|
|
||||||
NpadManager.Dispose();
|
NpadManager.Dispose();
|
||||||
Dispose();
|
Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateAnriAliasing(object sender, ReactiveEventArgs<Ryujinx.Common.Configuration.AntiAliasing> e)
|
||||||
|
{
|
||||||
|
Renderer?.Window.SetAntiAliasing((Graphics.GAL.AntiAliasing)e.NewValue);
|
||||||
|
}
|
||||||
|
|
||||||
protected override bool OnMotionNotifyEvent(EventMotion evnt)
|
protected override bool OnMotionNotifyEvent(EventMotion evnt)
|
||||||
{
|
{
|
||||||
if (_hideCursorOnIdle)
|
if (_hideCursorOnIdle)
|
||||||
@@ -394,6 +418,10 @@ namespace Ryujinx.Ui
|
|||||||
|
|
||||||
Device.Gpu.Renderer.Initialize(_glLogLevel);
|
Device.Gpu.Renderer.Initialize(_glLogLevel);
|
||||||
|
|
||||||
|
Renderer.Window.SetAntiAliasing((Graphics.GAL.AntiAliasing)ConfigurationState.Instance.Graphics.AntiAliasing.Value);
|
||||||
|
Renderer.Window.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value);
|
||||||
|
Renderer.Window.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value);
|
||||||
|
|
||||||
_gpuBackendName = GetGpuBackendName();
|
_gpuBackendName = GetGpuBackendName();
|
||||||
_gpuVendorName = GetGpuVendorName();
|
_gpuVendorName = GetGpuVendorName();
|
||||||
|
|
||||||
|
@@ -95,10 +95,14 @@ namespace Ryujinx.Ui.Windows
|
|||||||
[GUI] Entry _graphicsShadersDumpPath;
|
[GUI] Entry _graphicsShadersDumpPath;
|
||||||
[GUI] ComboBoxText _anisotropy;
|
[GUI] ComboBoxText _anisotropy;
|
||||||
[GUI] ComboBoxText _aspectRatio;
|
[GUI] ComboBoxText _aspectRatio;
|
||||||
|
[GUI] ComboBoxText _antiAliasing;
|
||||||
|
[GUI] ComboBoxText _scalingFilter;
|
||||||
[GUI] ComboBoxText _graphicsBackend;
|
[GUI] ComboBoxText _graphicsBackend;
|
||||||
[GUI] ComboBoxText _preferredGpu;
|
[GUI] ComboBoxText _preferredGpu;
|
||||||
[GUI] ComboBoxText _resScaleCombo;
|
[GUI] ComboBoxText _resScaleCombo;
|
||||||
[GUI] Entry _resScaleText;
|
[GUI] Entry _resScaleText;
|
||||||
|
[GUI] Adjustment _scalingFilterLevel;
|
||||||
|
[GUI] Scale _scalingFilterSlider;
|
||||||
[GUI] ToggleButton _configureController1;
|
[GUI] ToggleButton _configureController1;
|
||||||
[GUI] ToggleButton _configureController2;
|
[GUI] ToggleButton _configureController2;
|
||||||
[GUI] ToggleButton _configureController3;
|
[GUI] ToggleButton _configureController3;
|
||||||
@@ -139,6 +143,7 @@ namespace Ryujinx.Ui.Windows
|
|||||||
_systemTimeZoneEntry.FocusOutEvent += TimeZoneEntry_FocusOut;
|
_systemTimeZoneEntry.FocusOutEvent += TimeZoneEntry_FocusOut;
|
||||||
|
|
||||||
_resScaleCombo.Changed += (sender, args) => _resScaleText.Visible = _resScaleCombo.ActiveId == "-1";
|
_resScaleCombo.Changed += (sender, args) => _resScaleText.Visible = _resScaleCombo.ActiveId == "-1";
|
||||||
|
_scalingFilter.Changed += (sender, args) => _scalingFilterSlider.Visible = _scalingFilter.ActiveId == "2";
|
||||||
_galThreading.Changed += (sender, args) =>
|
_galThreading.Changed += (sender, args) =>
|
||||||
{
|
{
|
||||||
if (_galThreading.ActiveId != ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString())
|
if (_galThreading.ActiveId != ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString())
|
||||||
@@ -338,6 +343,8 @@ namespace Ryujinx.Ui.Windows
|
|||||||
_anisotropy.SetActiveId(ConfigurationState.Instance.Graphics.MaxAnisotropy.Value.ToString());
|
_anisotropy.SetActiveId(ConfigurationState.Instance.Graphics.MaxAnisotropy.Value.ToString());
|
||||||
_aspectRatio.SetActiveId(((int)ConfigurationState.Instance.Graphics.AspectRatio.Value).ToString());
|
_aspectRatio.SetActiveId(((int)ConfigurationState.Instance.Graphics.AspectRatio.Value).ToString());
|
||||||
_graphicsBackend.SetActiveId(((int)ConfigurationState.Instance.Graphics.GraphicsBackend.Value).ToString());
|
_graphicsBackend.SetActiveId(((int)ConfigurationState.Instance.Graphics.GraphicsBackend.Value).ToString());
|
||||||
|
_antiAliasing.SetActiveId(((int)ConfigurationState.Instance.Graphics.AntiAliasing.Value).ToString());
|
||||||
|
_scalingFilter.SetActiveId(((int)ConfigurationState.Instance.Graphics.ScalingFilter.Value).ToString());
|
||||||
|
|
||||||
UpdatePreferredGpuComboBox();
|
UpdatePreferredGpuComboBox();
|
||||||
|
|
||||||
@@ -345,7 +352,9 @@ namespace Ryujinx.Ui.Windows
|
|||||||
|
|
||||||
_custThemePath.Buffer.Text = ConfigurationState.Instance.Ui.CustomThemePath;
|
_custThemePath.Buffer.Text = ConfigurationState.Instance.Ui.CustomThemePath;
|
||||||
_resScaleText.Buffer.Text = ConfigurationState.Instance.Graphics.ResScaleCustom.Value.ToString();
|
_resScaleText.Buffer.Text = ConfigurationState.Instance.Graphics.ResScaleCustom.Value.ToString();
|
||||||
|
_scalingFilterLevel.Value = ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value;
|
||||||
_resScaleText.Visible = _resScaleCombo.ActiveId == "-1";
|
_resScaleText.Visible = _resScaleCombo.ActiveId == "-1";
|
||||||
|
_scalingFilterSlider.Visible = _scalingFilter.ActiveId == "2";
|
||||||
_graphicsShadersDumpPath.Buffer.Text = ConfigurationState.Instance.Graphics.ShadersDumpPath;
|
_graphicsShadersDumpPath.Buffer.Text = ConfigurationState.Instance.Graphics.ShadersDumpPath;
|
||||||
_fsLogSpinAdjustment.Value = ConfigurationState.Instance.System.FsGlobalAccessLogMode;
|
_fsLogSpinAdjustment.Value = ConfigurationState.Instance.System.FsGlobalAccessLogMode;
|
||||||
_systemTimeOffset = ConfigurationState.Instance.System.SystemTimeOffset;
|
_systemTimeOffset = ConfigurationState.Instance.System.SystemTimeOffset;
|
||||||
@@ -605,6 +614,9 @@ namespace Ryujinx.Ui.Windows
|
|||||||
ConfigurationState.Instance.Graphics.ResScale.Value = int.Parse(_resScaleCombo.ActiveId);
|
ConfigurationState.Instance.Graphics.ResScale.Value = int.Parse(_resScaleCombo.ActiveId);
|
||||||
ConfigurationState.Instance.Graphics.ResScaleCustom.Value = resScaleCustom;
|
ConfigurationState.Instance.Graphics.ResScaleCustom.Value = resScaleCustom;
|
||||||
ConfigurationState.Instance.System.AudioVolume.Value = (float)_audioVolumeSlider.Value / 100.0f;
|
ConfigurationState.Instance.System.AudioVolume.Value = (float)_audioVolumeSlider.Value / 100.0f;
|
||||||
|
ConfigurationState.Instance.Graphics.AntiAliasing.Value = Enum.Parse<AntiAliasing>(_antiAliasing.ActiveId);
|
||||||
|
ConfigurationState.Instance.Graphics.ScalingFilter.Value = Enum.Parse<ScalingFilter>(_scalingFilter.ActiveId);
|
||||||
|
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value = (int)_scalingFilterLevel.Value;
|
||||||
|
|
||||||
_previousVolumeLevel = ConfigurationState.Instance.System.AudioVolume.Value;
|
_previousVolumeLevel = ConfigurationState.Instance.System.AudioVolume.Value;
|
||||||
|
|
||||||
|
@@ -40,6 +40,13 @@
|
|||||||
<property name="inline-completion">True</property>
|
<property name="inline-completion">True</property>
|
||||||
<property name="inline-selection">True</property>
|
<property name="inline-selection">True</property>
|
||||||
</object>
|
</object>
|
||||||
|
<object class="GtkAdjustment" id="_scalingFilterLevel">
|
||||||
|
<property name="lower">0</property>
|
||||||
|
<property name="upper">101</property>
|
||||||
|
<property name="step-increment">1</property>
|
||||||
|
<property name="page-increment">5</property>
|
||||||
|
<property name="page-size">1</property>
|
||||||
|
</object>
|
||||||
<object class="GtkWindow" id="_settingsWin">
|
<object class="GtkWindow" id="_settingsWin">
|
||||||
<property name="can-focus">False</property>
|
<property name="can-focus">False</property>
|
||||||
<property name="title" translatable="yes">Ryujinx - Settings</property>
|
<property name="title" translatable="yes">Ryujinx - Settings</property>
|
||||||
@@ -2152,6 +2159,118 @@
|
|||||||
<property name="position">3</property>
|
<property name="position">3</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="margin-top">5</property>
|
||||||
|
<property name="margin-bottom">5</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="tooltip-text" translatable="yes">Applies a final effect to the game render</property>
|
||||||
|
<property name="label" translatable="yes">Post Processing Effect:</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">5</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkComboBoxText" id="_antiAliasing">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="tooltip-text" translatable="yes">Applies anti-aliasing to the game render</property>
|
||||||
|
<property name="active-id">1</property>
|
||||||
|
<items>
|
||||||
|
<item id="0" translatable="yes">None</item>
|
||||||
|
<item id="1" translatable="yes">FXAA</item>
|
||||||
|
<item id="2" translatable="yes">SMAA Low</item>
|
||||||
|
<item id="3" translatable="yes">SMAA Medium</item>
|
||||||
|
<item id="4" translatable="yes">SMAA High</item>
|
||||||
|
<item id="5" translatable="yes">SMAA Ultra</item>
|
||||||
|
</items>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">5</property>
|
||||||
|
<property name="position">4</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkBox">
|
||||||
|
<property name="width-request">100</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="margin-top">5</property>
|
||||||
|
<property name="margin-bottom">5</property>
|
||||||
|
<child>
|
||||||
|
<object class="GtkLabel">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="tooltip-text" translatable="yes">Enables Framebuffer Upscaling</property>
|
||||||
|
<property name="label" translatable="yes">Upscale: </property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">5</property>
|
||||||
|
<property name="position">0</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkComboBoxText" id="_scalingFilter">
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">False</property>
|
||||||
|
<property name="tooltip-text" translatable="yes">Enables Framebuffer Upscaling</property>
|
||||||
|
<property name="active-id">1</property>
|
||||||
|
<items>
|
||||||
|
<item id="0" translatable="yes">Bilinear</item>
|
||||||
|
<item id="1" translatable="yes">Nearest</item>
|
||||||
|
<item id="2" translatable="yes">FSR</item>
|
||||||
|
</items>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">1</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkScale" id="_scalingFilterSlider">
|
||||||
|
<property name="width-request">200</property>
|
||||||
|
<property name="visible">True</property>
|
||||||
|
<property name="can-focus">True</property>
|
||||||
|
<property name="margin-start">5</property>
|
||||||
|
<property name="adjustment">_scalingFilterLevel</property>
|
||||||
|
<property name="round-digits">1</property>
|
||||||
|
<property name="value-pos">right</property>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="position">3</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
<packing>
|
||||||
|
<property name="expand">False</property>
|
||||||
|
<property name="fill">True</property>
|
||||||
|
<property name="padding">5</property>
|
||||||
|
<property name="position">5</property>
|
||||||
|
</packing>
|
||||||
|
</child>
|
||||||
<child>
|
<child>
|
||||||
<object class="GtkBox">
|
<object class="GtkBox">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
@@ -2197,7 +2316,7 @@
|
|||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
<property name="fill">True</property>
|
<property name="fill">True</property>
|
||||||
<property name="padding">5</property>
|
<property name="padding">5</property>
|
||||||
<property name="position">4</property>
|
<property name="position">6</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
<child>
|
<child>
|
||||||
@@ -2246,7 +2365,7 @@
|
|||||||
<property name="expand">False</property>
|
<property name="expand">False</property>
|
||||||
<property name="fill">True</property>
|
<property name="fill">True</property>
|
||||||
<property name="padding">5</property>
|
<property name="padding">5</property>
|
||||||
<property name="position">5</property>
|
<property name="position">7</property>
|
||||||
</packing>
|
</packing>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
@@ -24,6 +24,7 @@ cp $PUBLISH_DIRECTORY/*.dylib $APP_BUNDLE_DIRECTORY/Contents/Frameworks
|
|||||||
# Then resources
|
# Then resources
|
||||||
cp Info.plist $APP_BUNDLE_DIRECTORY/Contents
|
cp Info.plist $APP_BUNDLE_DIRECTORY/Contents
|
||||||
cp Ryujinx.icns $APP_BUNDLE_DIRECTORY/Contents/Resources/Ryujinx.icns
|
cp Ryujinx.icns $APP_BUNDLE_DIRECTORY/Contents/Resources/Ryujinx.icns
|
||||||
|
cp updater.sh $APP_BUNDLE_DIRECTORY/Contents/Resources/updater.sh
|
||||||
cp -r $PUBLISH_DIRECTORY/THIRDPARTY.md $APP_BUNDLE_DIRECTORY/Contents/Resources
|
cp -r $PUBLISH_DIRECTORY/THIRDPARTY.md $APP_BUNDLE_DIRECTORY/Contents/Resources
|
||||||
|
|
||||||
echo -n "APPL????" > $APP_BUNDLE_DIRECTORY/Contents/PkgInfo
|
echo -n "APPL????" > $APP_BUNDLE_DIRECTORY/Contents/PkgInfo
|
||||||
|
@@ -27,7 +27,7 @@ EXECUTABLE_SUB_PATH=Contents/MacOS/Ryujinx
|
|||||||
rm -rf $TEMP_DIRECTORY
|
rm -rf $TEMP_DIRECTORY
|
||||||
mkdir -p $TEMP_DIRECTORY
|
mkdir -p $TEMP_DIRECTORY
|
||||||
|
|
||||||
DOTNET_COMMON_ARGS="-p:DebugType=embedded -p:Version=$VERSION -p:SourceRevisionId=$SOURCE_REVISION_ID -p:ExtraDefineConstants=DISABLE_UPDATER --self-contained true"
|
DOTNET_COMMON_ARGS="-p:DebugType=embedded -p:Version=$VERSION -p:SourceRevisionId=$SOURCE_REVISION_ID --self-contained true"
|
||||||
|
|
||||||
dotnet restore
|
dotnet restore
|
||||||
dotnet build -c Release Ryujinx.Ava
|
dotnet build -c Release Ryujinx.Ava
|
||||||
|
39
distribution/macos/updater.sh
Executable file
39
distribution/macos/updater.sh
Executable file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
INSTALL_DIRECTORY=$1
|
||||||
|
NEW_APP_DIRECTORY=$2
|
||||||
|
APP_PID=$3
|
||||||
|
APP_ARGUMENTS="${@:4}"
|
||||||
|
|
||||||
|
error_handler() {
|
||||||
|
local lineno="$1"
|
||||||
|
|
||||||
|
script="""
|
||||||
|
set alertTitle to \"Ryujinx - Updater error\"
|
||||||
|
set alertMessage to \"An error occurred during Ryujinx update (updater.sh:$lineno)\n\nPlease download the update manually from our website if the problem persists.\"
|
||||||
|
display dialog alertMessage with icon caution with title alertTitle buttons {\"Open Download Page\", \"Exit\"}
|
||||||
|
set the button_pressed to the button returned of the result
|
||||||
|
|
||||||
|
if the button_pressed is \"Open Download Page\" then
|
||||||
|
open location \"https://ryujinx.org/download\"
|
||||||
|
end if
|
||||||
|
"""
|
||||||
|
|
||||||
|
osascript -e "$script"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
trap 'error_handler ${LINENO}' ERR
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Now replace and reopen.
|
||||||
|
rm -rf "$INSTALL_DIRECTORY"
|
||||||
|
mv "$NEW_APP_DIRECTORY" "$INSTALL_DIRECTORY"
|
||||||
|
open -a "$INSTALL_DIRECTORY" --args "$APP_ARGUMENTS"
|
Reference in New Issue
Block a user