Compare commits

..

9 Commits

Author SHA1 Message Date
b8556530f2 nuget: bump Microsoft.CodeAnalysis.CSharp from 4.4.0 to 4.5.0 (#4488)
Bumps [Microsoft.CodeAnalysis.CSharp](https://github.com/dotnet/roslyn) from 4.4.0 to 4.5.0.
- [Release notes](https://github.com/dotnet/roslyn/releases)
- [Changelog](https://github.com/dotnet/roslyn/blob/main/docs/Breaking%20API%20Changes.md)
- [Commits](https://github.com/dotnet/roslyn/commits)

---
updated-dependencies:
- dependency-name: Microsoft.CodeAnalysis.CSharp
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-04 15:37:08 +01:00
4f3af839be Minor code formatting (#4498) 2023-03-04 14:43:08 +01:00
155736c986 nuget: bump UnicornEngine.Unicorn (#4500)
Bumps [UnicornEngine.Unicorn](https://github.com/unicorn-engine/unicorn) from 2.0.2-rc1-a913199 to 2.0.2-rc1-f7c841d.
- [Release notes](https://github.com/unicorn-engine/unicorn/releases)
- [Changelog](https://github.com/unicorn-engine/unicorn/blob/master/ChangeLog)
- [Commits](https://github.com/unicorn-engine/unicorn/commits)

---
updated-dependencies:
- dependency-name: UnicornEngine.Unicorn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-04 11:32:30 +00:00
dba908dc78 Add post processing feature to the readme (#4499)
* Add post processing feature to the readme

Adds post processing information to the GPU section in the readme.

* correct "Anti-Aliasing"
2023-03-04 02:15:29 +00:00
ecee34a50c Update LibHac to 0.18.0 (#4414)
* Update LibHac to 0.18.0

* Change instance of AsBytes(CreateReadOnlySpan(...)) to AsReadOnlyByteSpan(...)
2023-03-01 23:42:27 -03:00
9b5a0c3889 Sockets: Properly convert error codes on MacOS (#4491)
* Sockets: Properly convert error codes on MacOS

The error codes for MacOS are very different to how they are on windows or linux. An alternate mapping is used when the host operating system is MacOS.

This PR also defaults IsDhcpEnabled to true when interfaceProperties.DhcpServerAddresses is not available.

This change was already in `macos1`.

* Address feedback
2023-02-28 03:41:44 +00:00
80b4972139 Add Support for Post Processing Effects (#3616)
* Add Post Processing Effects

* fix events and shader issues

* fix gtk upscale slider value

* fix bgra games

* don't swap swizzle if already swapped

* restore opengl texture state after effects run

* addressed review

* use single pipeline for smaa and fsr

* call finish on all pipelines

* addressed review

* attempt fix file case

* attempt fixing file case

* fix filter level tick frequency

* adjust filter slider margins

* replace fxaa shaders with original shader

* addressed review
2023-02-27 18:11:55 -03:00
5d85468302 Vulkan: Support list topology primitive restart (#4483) 2023-02-26 19:19:00 -03:00
9b1cc2cec6 Logging: Redirect StdErr into logging system (#4427)
* Logging: Redirect StdErr into logging system

* Remove Mono.Unix

* Apply suggestions from code review

Co-authored-by: riperiperi <rhy3756547@hotmail.com>

* Address comments

---------

Co-authored-by: Mary <thog@protonmail.com>
Co-authored-by: riperiperi <rhy3756547@hotmail.com>
Co-authored-by: Mary <mary@mary.zone>
2023-02-25 15:07:23 +00:00
154 changed files with 22639 additions and 347 deletions

View File

@ -1,9 +1,8 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.Instructions.InstEmitHashHelper;
using static ARMeilleure.Instructions.InstEmitHelper;
namespace ARMeilleure.Instructions
{

View File

@ -4,9 +4,8 @@ using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Translation;
using System;
using System.Diagnostics;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
using static ARMeilleure.Instructions.InstEmitSimdHelper;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
namespace ARMeilleure.Instructions
{

View File

@ -19,9 +19,9 @@
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
<PackageVersion Include="jp2masa.Avalonia.Flexbox" Version="0.2.0" />
<PackageVersion Include="LibHac" Version="0.17.0" />
<PackageVersion Include="LibHac" Version="0.18.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.4.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
<PackageVersion Include="NUnit" Version="3.13.3" />
@ -49,7 +49,7 @@
<PackageVersion Include="System.Management" Version="7.0.0" />
<PackageVersion Include="System.Net.NameResolution" Version="4.3.0" />
<PackageVersion Include="System.Threading.ThreadPool" Version="4.3.0" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-a913199" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-f7c841d" />
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" />
</ItemGroup>
</Project>
</Project>

View File

@ -96,7 +96,7 @@ Ryujinx system files are stored in the `Ryujinx` folder. This folder is located
- **GPU**
The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum), Vulkan, or Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively. There are currently four graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Aspect Ratio Adjustment, and Anisotropic Filtering. These enhancements can be adjusted or toggled as desired in the GUI.
The GPU emulator emulates the Switch's Maxwell GPU using either the OpenGL (version 4.5 minimum), Vulkan, or Metal (via MoltenVK) APIs through a custom build of OpenTK or Silk.NET respectively. There are currently six graphics enhancements available to the end user in Ryujinx: Disk Shader Caching, Resolution Scaling, Anti-Aliasing, Scaling Filters (including FSR), Anisotropic Filtering and Aspect Ratio Adjustment. These enhancements can be adjusted or toggled as desired in the GUI.
- **Input**

View File

@ -5,9 +5,8 @@ using Ryujinx.Memory;
using System;
using System.Collections.Concurrent;
using System.Threading;
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
using static Ryujinx.Audio.Backends.SoundIo.Native.SoundIo;
using static Ryujinx.Audio.Integration.IHardwareDeviceDriver;
namespace Ryujinx.Audio.Backends.SoundIo
{

View File

@ -1,8 +1,8 @@
using System;
using System.Diagnostics;
using Ryujinx.Audio.Renderer.Dsp.Effect;
using Ryujinx.Audio.Renderer.Dsp.Effect;
using Ryujinx.Audio.Renderer.Dsp.State;
using Ryujinx.Audio.Renderer.Parameter.Effect;
using System;
using System.Diagnostics;
namespace Ryujinx.Audio.Renderer.Dsp.Command
{

View File

@ -1,7 +1,7 @@
using Ryujinx.Audio.Renderer.Common;
using Ryujinx.Audio.Renderer.Dsp.State;
using Ryujinx.Audio.Renderer.Parameter.Effect;
using Ryujinx.Audio.Renderer.Parameter;
using Ryujinx.Audio.Renderer.Parameter.Effect;
using Ryujinx.Audio.Renderer.Server.MemoryPool;
using System;
using System.Diagnostics;

View File

@ -171,6 +171,11 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState;
ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState;
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();
}
@ -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()
{
@ -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)
{
Device?.System.ChangeDockedModeState(e.NewValue);
@ -411,6 +432,9 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState;
ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState;
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;
@ -788,6 +812,10 @@ namespace Ryujinx.Ava
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;
Height = (int)_rendererHost.Bounds.Height;

View File

@ -626,6 +626,16 @@
"Recover": "Recover",
"UserProfilesRecoverHeading" : "Saves were found for the following accounts",
"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",
"UserEditorTitleCreate" : "Create User"
}

View File

@ -193,7 +193,7 @@ namespace Ryujinx.Ava.Common
{
using var ncaFile = new UniqueRef<IFile>();
pfs.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage());
if (nca.Header.ContentType == NcaContentType.Program)
@ -249,8 +249,8 @@ namespace Ryujinx.Ava.Common
using var uniqueSourceFs = new UniqueRef<IFileSystem>(ncaFileSystem);
using var uniqueOutputFs = new UniqueRef<IFileSystem>(new LocalFileSystem(destination));
fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref());
fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref());
fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref);
fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref);
(Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/", cancellationToken.Token);

View File

@ -16,9 +16,9 @@ using Ryujinx.Ava.UI.Views.User;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;
namespace Ryujinx.Ava.UI.Controls
@ -121,7 +121,7 @@ namespace Ryujinx.Ava.UI.Controls
using var saveDataIterator = new UniqueRef<SaveDataIterator>();
HorizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref(), SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure();
HorizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref, SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure();
Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10];

View File

@ -6,8 +6,8 @@ namespace Ryujinx.Ava.UI.Helpers
{
using AvaLogger = Avalonia.Logging.Logger;
using AvaLogLevel = Avalonia.Logging.LogEventLevel;
using RyuLogger = Ryujinx.Common.Logging.Logger;
using RyuLogClass = Ryujinx.Common.Logging.LogClass;
using RyuLogger = Ryujinx.Common.Logging.Logger;
internal class LoggerAdapter : Avalonia.Logging.ILogSink
{

View File

@ -3,7 +3,6 @@ using Avalonia.Collections;
using Avalonia.Media.Imaging;
using Avalonia.Threading;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.Windows;
@ -17,7 +16,6 @@ using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
namespace Ryujinx.Ava.UI.ViewModels
@ -31,7 +29,7 @@ namespace Ryujinx.Ava.UI.ViewModels
private readonly byte[] _amiiboLogoBytes;
private readonly HttpClient _httpClient;
private readonly StyleableWindow _owner;
private Bitmap _amiiboImage;
private List<Amiibo.AmiiboApi> _amiiboList;
private AvaloniaList<Amiibo.AmiiboApi> _amiibos;

View File

@ -246,7 +246,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{
using var file = new UniqueRef<IFile>();
romfs.OpenFile(ref file.Ref(), ("/" + item.FullPath).ToU8Span(), OpenMode.Read)
romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read)
.ThrowIfFailure();
using (MemoryStream stream = new())

View File

@ -3,11 +3,8 @@ using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Svg.Skia;
using Avalonia.Threading;
using LibHac.Bcat;
using LibHac.Tools.Fs;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Input;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.Windows;

View File

@ -7,8 +7,7 @@ using DynamicData;
using DynamicData.Binding;
using LibHac.Common;
using LibHac.Fs;
using LibHac.FsSystem;
using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.Input;

View File

@ -45,6 +45,8 @@ namespace Ryujinx.Ava.UI.ViewModels
private KeyboardHotkeys _keyboardHotkeys;
private int _graphicsBackendIndex;
private string _customThemePath;
private int _scalingFilter;
private int _scalingFilterLevel;
public event Action CloseWindow;
public event Action SaveSettingsEvent;
@ -153,6 +155,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool IsSDL2Enabled { get; set; }
public bool EnableCustomTheme { get; set; }
public bool IsCustomResolutionScaleActive => _resolutionScale == 4;
public bool IsScalingFilterActive => _scalingFilter == (int)Ryujinx.Common.Configuration.ScalingFilter.Fsr;
public bool IsVulkanSelected => GraphicsBackendIndex == 0;
public bool UseHypervisor { get; set; }
@ -179,6 +183,18 @@ namespace Ryujinx.Ava.UI.ViewModels
public int AudioBackend { get; set; }
public int MaxAnisotropy { 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 MemoryMode { get; set; }
public int BaseStyleIndex { get; set; }
@ -192,6 +208,16 @@ namespace Ryujinx.Ava.UI.ViewModels
OnPropertyChanged(nameof(IsVulkanSelected));
}
}
public int ScalingFilter
{
get => _scalingFilter;
set
{
_scalingFilter = value;
OnPropertyChanged();
OnPropertyChanged(nameof(IsScalingFilterActive));
}
}
public int PreferredGpuIndex { get; set; }
@ -365,6 +391,9 @@ namespace Ryujinx.Ava.UI.ViewModels
AspectRatio = (int)config.Graphics.AspectRatio.Value;
GraphicsBackendMultithreadingIndex = (int)config.Graphics.BackendThreading.Value;
ShaderDumpPath = config.Graphics.ShadersDumpPath;
AntiAliasingEffect = (int)config.Graphics.AntiAliasing.Value;
ScalingFilter = (int)config.Graphics.ScalingFilter.Value;
ScalingFilterLevel = config.Graphics.ScalingFilterLevel.Value;
// Audio
AudioBackend = (int)config.System.AudioBackend.Value;
@ -447,6 +476,9 @@ namespace Ryujinx.Ava.UI.ViewModels
config.Graphics.ResScaleCustom.Value = CustomResolutionScale;
config.Graphics.MaxAnisotropy.Value = MaxAnisotropy == 0 ? -1 : MathF.Pow(2, MaxAnisotropy);
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)
{

View File

@ -170,7 +170,7 @@ public class TitleUpdateViewModel : BaseModel
using UniqueRef<IFile> nacpFile = new();
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref(), "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
TitleUpdates.Add(new TitleUpdateModel(controlData, path));

View File

@ -126,7 +126,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{
using var file = new UniqueRef<IFile>();
romfs.OpenFile(ref file.Ref(), ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure();
romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure();
using (MemoryStream stream = new())
using (MemoryStream streamPng = new())

View File

@ -1,8 +1,8 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using LibHac.FsSystem;
using LibHac.Ncm;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;

View File

@ -7,6 +7,7 @@
xmlns:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale"
xmlns:viewModels="clr-namespace:Ryujinx.Ava.UI.ViewModels"
Design.Width="1000"
mc:Ignorable="d"
x:CompileBindings="True"
x:DataType="viewModels:SettingsViewModel">
@ -111,6 +112,83 @@
Minimum="0.1"
Value="{Binding CustomResolutionScale}" />
</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">
<TextBlock VerticalAlignment="Center"
ToolTip.Tip="{locale:Locale AnisotropyTooltip}"

View File

@ -5,8 +5,8 @@ using FluentAvalonia.UI.Controls;
using FluentAvalonia.UI.Navigation;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Controls;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using System;
using UserProfile = Ryujinx.Ava.UI.Models.UserProfile;

View File

@ -76,7 +76,7 @@ namespace Ryujinx.Ava.UI.Views.User
using var saveDataIterator = new UniqueRef<SaveDataIterator>();
_horizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref(), SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure();
_horizonClient.Fs.OpenSaveDataIterator(ref saveDataIterator.Ref, SaveDataSpaceId.User, in saveDataFilter).ThrowIfFailure();
Span<SaveDataInfo> saveDataInfo = stackalloc SaveDataInfo[10];

View File

@ -105,7 +105,7 @@ namespace Ryujinx.Ava.UI.Windows
{
using UniqueRef<IFile> ncaFile = new();
partitionFileSystem.OpenFile(ref ncaFile.Ref(), downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
partitionFileSystem.OpenFile(ref ncaFile.Ref, downloadableContentNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), downloadableContentContainer.ContainerPath);
if (nca != null)
@ -158,7 +158,7 @@ namespace Ryujinx.Ava.UI.Windows
{
using var ncaFile = new UniqueRef<IFile>();
partitionFileSystem.OpenFile(ref ncaFile.Ref(), fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
partitionFileSystem.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
Nca nca = TryOpenNca(ncaFile.Get.AsStorage(), path);
if (nca == null)

View File

@ -0,0 +1,12 @@
namespace Ryujinx.Common.Configuration
{
public enum AntiAliasing
{
None,
Fxaa,
SmaaLow,
SmaaMedium,
SmaaHigh,
SmaaUltra
}
}

View File

@ -0,0 +1,9 @@
namespace Ryujinx.Common.Configuration
{
public enum ScalingFilter
{
Bilinear,
Nearest,
Fsr
}
}

View File

@ -1,3 +1,4 @@
using Ryujinx.Common.SystemInterop;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@ -14,6 +15,8 @@ namespace Ryujinx.Common.Logging
private static readonly List<ILogTarget> m_LogTargets;
private static readonly StdErrAdapter _stdErrAdapter;
public static event EventHandler<LogEventArgs> Updated;
public readonly struct Log
@ -77,7 +80,13 @@ namespace Ryujinx.Common.Logging
{
Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, "Stubbed. " + message), data));
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PrintRawMsg(string message)
{
Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, message));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
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);
Info = new Log(LogLevel.Info);
Trace = new Log(LogLevel.Trace);
_stdErrAdapter = new StdErrAdapter();
}
public static void RestartTime()
@ -164,6 +175,8 @@ namespace Ryujinx.Common.Logging
{
Updated = null;
_stdErrAdapter.Dispose();
foreach (var target in m_LogTargets)
{
target.Dispose();

View File

@ -1,9 +1,9 @@
using Ryujinx.Common.Logging;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Runtime.Versioning;
using Ryujinx.Common.Logging;
namespace Ryujinx.Common.SystemInfo
{

View File

@ -1,9 +1,9 @@
using Ryujinx.Common.Logging;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using Ryujinx.Common.Logging;
namespace Ryujinx.Common.SystemInfo
{

View File

@ -1,8 +1,8 @@
using System;
using Ryujinx.Common.Logging;
using System;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics.X86;
using System.Text;
using Ryujinx.Common.Logging;
namespace Ryujinx.Common.SystemInfo
{

View File

@ -1,8 +1,8 @@
using Ryujinx.Common.Logging;
using System;
using System.Management;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using Ryujinx.Common.Logging;
namespace Ryujinx.Common.SystemInfo
{

View File

@ -0,0 +1,93 @@
using Ryujinx.Common.Logging;
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading;
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();
}
}
}
}

View 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;
}
}
}

View File

@ -0,0 +1,12 @@
namespace Ryujinx.Graphics.GAL
{
public enum AntiAliasing
{
None,
Fxaa,
SmaaLow,
SmaaMedium,
SmaaHigh,
SmaaUltra
}
}

View File

@ -9,5 +9,9 @@ namespace Ryujinx.Graphics.GAL
void SetSize(int width, int height);
void ChangeVSyncMode(bool vsyncEnabled);
void SetAntiAliasing(AntiAliasing antialiasing);
void SetScalingFilter(ScalingFilter type);
void SetScalingFilterLevel(float level);
}
}

View File

@ -32,5 +32,11 @@ namespace Ryujinx.Graphics.GAL.Multithreading
}
public void ChangeVSyncMode(bool vsyncEnabled) { }
public void SetAntiAliasing(AntiAliasing effect) { }
public void SetScalingFilter(ScalingFilter type) { }
public void SetScalingFilterLevel(float level) { }
}
}

View File

@ -0,0 +1,9 @@
namespace Ryujinx.Graphics.GAL
{
public enum ScalingFilter
{
Bilinear,
Nearest,
Fsr
}
}

View File

@ -1,7 +1,7 @@
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Image;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Shader.DiskCache;
using Ryujinx.Graphics.Shader;
using System;

View File

@ -1,7 +1,7 @@
using System;
using Ryujinx.Graphics.Nvdec.Vp9.Common;
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using Ryujinx.Graphics.Nvdec.Vp9.Common;
using static Ryujinx.Graphics.Nvdec.Vp9.Dsp.TxfmCommon;
namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp

View File

@ -1,6 +1,6 @@
using System;
using Ryujinx.Common.Memory;
using System;
using System.Buffers.Binary;
using Ryujinx.Common.Memory;
namespace Ryujinx.Graphics.Nvdec.Vp9.Dsp
{

View 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);
}
}
}

View 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;
}
}
}

View 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);
}
}

View 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);
}
}

View 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;
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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);
}

View 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);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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);
}
}
}

View 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));
}
}
}
}

View 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);
}
}
}

View 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;
}
}
}

View File

@ -1,10 +1,10 @@
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using OpenTK.Graphics.OpenGL;
using OpenTK.Graphics.OpenGL;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.OpenGL.Image;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ryujinx.Graphics.OpenGL
{

View File

@ -9,6 +9,20 @@
<PackageReference Include="OpenTK.Graphics" />
</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>
<ProjectReference Include="..\Ryujinx.Common\Ryujinx.Common.csproj" />
<ProjectReference Include="..\Ryujinx.Graphics.GAL\Ryujinx.Graphics.GAL.csproj" />

View File

@ -1,5 +1,7 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.OpenGL.Effects;
using Ryujinx.Graphics.OpenGL.Effects.Smaa;
using Ryujinx.Graphics.OpenGL.Image;
using System;
@ -7,14 +9,24 @@ namespace Ryujinx.Graphics.OpenGL
{
class Window : IWindow, IDisposable
{
private const int TextureCount = 3;
private readonly OpenGLRenderer _renderer;
private bool _initialized;
private int _width;
private int _height;
private bool _updateSize;
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; }
@ -48,6 +60,8 @@ namespace Ryujinx.Graphics.OpenGL
{
_width = width;
_height = height;
_updateSize = true;
}
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;
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(
FramebufferTarget.ReadFramebuffer,
FramebufferAttachment.ColorAttachment0,
@ -71,12 +111,12 @@ namespace Ryujinx.Graphics.OpenGL
GL.Clear(ClearBufferMask.ColorBufferBit);
int srcX0, srcX1, srcY0, srcY1;
float scale = view.ScaleFactor;
float scale = viewConverted.ScaleFactor;
if (crop.Left == 0 && crop.Right == 0)
{
srcX0 = 0;
srcX1 = (int)(view.Width / scale);
srcX1 = (int)(viewConverted.Width / scale);
}
else
{
@ -87,7 +127,7 @@ namespace Ryujinx.Graphics.OpenGL
if (crop.Top == 0 && crop.Bottom == 0)
{
srcY0 = 0;
srcY1 = (int)(view.Height / scale);
srcY1 = (int)(viewConverted.Height / scale);
}
else
{
@ -125,6 +165,42 @@ namespace Ryujinx.Graphics.OpenGL
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(
srcX0,
srcY0,
@ -135,7 +211,7 @@ namespace Ryujinx.Graphics.OpenGL
dstX1,
dstY1,
ClearBufferMask.ColorBufferBit,
BlitFramebufferFilter.Linear);
_isLinear ? BlitFramebufferFilter.Linear : BlitFramebufferFilter.Nearest);
// Remove Alpha channel
GL.ColorMask(false, false, false, true);
@ -209,6 +285,135 @@ namespace Ryujinx.Graphics.OpenGL
_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;
}
}
}

View File

@ -1,7 +1,7 @@
using FuncUnaryInstruction = System.Func<Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction>;
using FuncBinaryInstruction = System.Func<Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction>;
using FuncTernaryInstruction = System.Func<Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction>;
using FuncBinaryInstruction = System.Func<Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction>;
using FuncQuaternaryInstruction = System.Func<Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction>;
using FuncTernaryInstruction = System.Func<Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction>;
using FuncUnaryInstruction = System.Func<Spv.Generator.Instruction, Spv.Generator.Instruction, Spv.Generator.Instruction>;
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{

View File

@ -9,9 +9,8 @@ using static Spv.Specification;
namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
{
using SpvInstruction = Spv.Generator.Instruction;
using SpvLiteralInteger = Spv.Generator.LiteralInteger;
using SpvInstructionPool = Spv.Generator.GeneratorPool<Spv.Generator.Instruction>;
using SpvLiteralInteger = Spv.Generator.LiteralInteger;
using SpvLiteralIntegerPool = Spv.Generator.GeneratorPool<Spv.Generator.LiteralInteger>;
static class SpirvGenerator

View File

@ -1,6 +1,6 @@
using Ryujinx.Graphics.Texture.Utils;
using System.Diagnostics;
using System;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.InteropServices;

View File

@ -1,7 +1,7 @@
using System.Threading;
using System.Collections.Generic;
using Silk.NET.Vulkan;
using System;
using Silk.NET.Vulkan;
using System.Collections.Generic;
using System.Threading;
namespace Ryujinx.Graphics.Vulkan
{

View File

@ -163,6 +163,13 @@ namespace Ryujinx.Graphics.Vulkan
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)
{
for (int i = 0; i < buffers.Length; i++)

View 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);
}
}
}

View 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;
}
}
}

View 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);
}
}

View 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);
}
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

View 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;
}
}

View 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;
}
}
}

View File

@ -33,6 +33,8 @@ namespace Ryujinx.Graphics.Vulkan
public readonly bool SupportsMultiView;
public readonly bool SupportsNullDescriptors;
public readonly bool SupportsPushDescriptors;
public readonly bool SupportsPrimitiveTopologyListRestart;
public readonly bool SupportsPrimitiveTopologyPatchListRestart;
public readonly bool SupportsTransformFeedback;
public readonly bool SupportsTransformFeedbackQueries;
public readonly bool SupportsPreciseOcclusionQueries;
@ -63,6 +65,8 @@ namespace Ryujinx.Graphics.Vulkan
bool supportsMultiView,
bool supportsNullDescriptors,
bool supportsPushDescriptors,
bool supportsPrimitiveTopologyListRestart,
bool supportsPrimitiveTopologyPatchListRestart,
bool supportsTransformFeedback,
bool supportsTransformFeedbackQueries,
bool supportsPreciseOcclusionQueries,
@ -92,6 +96,8 @@ namespace Ryujinx.Graphics.Vulkan
SupportsMultiView = supportsMultiView;
SupportsNullDescriptors = supportsNullDescriptors;
SupportsPushDescriptors = supportsPushDescriptors;
SupportsPrimitiveTopologyListRestart = supportsPrimitiveTopologyListRestart;
SupportsPrimitiveTopologyPatchListRestart = supportsPrimitiveTopologyPatchListRestart;
SupportsTransformFeedback = supportsTransformFeedback;
SupportsTransformFeedbackQueries = supportsTransformFeedbackQueries;
SupportsPreciseOcclusionQueries = supportsPreciseOcclusionQueries;

View File

@ -1,5 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace Ryujinx.Graphics.Vulkan
{

View File

@ -1,7 +1,7 @@
using Silk.NET.Vulkan;
using System;
using System.Runtime.Versioning;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
namespace Ryujinx.Graphics.Vulkan.MoltenVK
{

View File

@ -38,8 +38,11 @@ namespace Ryujinx.Graphics.Vulkan
public void Dispose()
{
Marshal.FreeHGlobal((IntPtr)Pointer);
Pointer = null;
if (Pointer != null)
{
Marshal.FreeHGlobal((IntPtr)Pointer);
Pointer = null;
}
}
}
}

View File

@ -150,6 +150,28 @@ namespace Ryujinx.Graphics.Vulkan
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)
{
_tfEnabled = true;
@ -803,6 +825,11 @@ namespace Ryujinx.Graphics.Vulkan
_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)
{
if (buffer.Handle != BufferHandle.Null)

View File

@ -417,11 +417,22 @@ namespace Ryujinx.Graphics.Vulkan
bool primitiveRestartEnable = PrimitiveRestartEnable;
primitiveRestartEnable &= Topology == PrimitiveTopology.LineStrip ||
Topology == PrimitiveTopology.TriangleStrip ||
Topology == PrimitiveTopology.TriangleFan ||
Topology == PrimitiveTopology.LineStripWithAdjacency ||
Topology == PrimitiveTopology.TriangleStripWithAdjacency;
bool topologySupportsRestart;
if (gd.Capabilities.SupportsPrimitiveTopologyListRestart)
{
topologySupportsRestart = gd.Capabilities.SupportsPrimitiveTopologyPatchListRestart || Topology != PrimitiveTopology.PatchList;
}
else
{
topologySupportsRestart = Topology == PrimitiveTopology.LineStrip ||
Topology == PrimitiveTopology.TriangleStrip ||
Topology == PrimitiveTopology.TriangleFan ||
Topology == PrimitiveTopology.LineStripWithAdjacency ||
Topology == PrimitiveTopology.TriangleStripWithAdjacency;
}
primitiveRestartEnable &= topologySupportsRestart;
var inputAssemblyState = new PipelineInputAssemblyStateCreateInfo()
{

View File

@ -2,8 +2,8 @@
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Linq;
using System.Threading;
namespace Ryujinx.Graphics.Vulkan.Queries
{

View File

@ -12,6 +12,17 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</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>
<PackageReference Include="OpenTK.Windowing.GraphicsLibraryFramework" />
<PackageReference Include="shaderc.net" />

View File

@ -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_fragment_shader_interlock",
"VK_EXT_index_type_uint8",
"VK_EXT_primitive_topology_list_restart",
"VK_EXT_robustness2",
"VK_EXT_shader_stencil_export",
"VK_KHR_shader_float16_int8",
@ -429,6 +430,17 @@ namespace Ryujinx.Graphics.Vulkan
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()
{
SType = StructureType.PhysicalDeviceTransformFeedbackFeaturesExt,
@ -497,6 +509,21 @@ namespace Ryujinx.Graphics.Vulkan
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;
if (supportedExtensions.Contains("VK_EXT_robustness2"))

View File

@ -195,6 +195,11 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDeviceFeatures2
};
PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT featuresPrimitiveTopologyListRestart = new PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT()
{
SType = StructureType.PhysicalDevicePrimitiveTopologyListRestartFeaturesExt
};
PhysicalDeviceRobustness2FeaturesEXT featuresRobustness2 = new PhysicalDeviceRobustness2FeaturesEXT()
{
SType = StructureType.PhysicalDeviceRobustness2FeaturesExt
@ -215,8 +220,14 @@ namespace Ryujinx.Graphics.Vulkan
SType = StructureType.PhysicalDevicePortabilitySubsetFeaturesKhr
};
if (supportedExtensions.Contains("VK_EXT_primitive_topology_list_restart"))
{
features2.PNext = &featuresPrimitiveTopologyListRestart;
}
if (supportedExtensions.Contains("VK_EXT_robustness2"))
{
featuresRobustness2.PNext = features2.PNext;
features2.PNext = &featuresRobustness2;
}
@ -288,6 +299,8 @@ namespace Ryujinx.Graphics.Vulkan
features2.Features.MultiViewport,
featuresRobustness2.NullDescriptor || IsMoltenVk,
supportedExtensions.Contains(KhrPushDescriptor.ExtensionName),
featuresPrimitiveTopologyListRestart.PrimitiveTopologyListRestart,
featuresPrimitiveTopologyListRestart.PrimitiveTopologyPatchListRestart,
supportsTransformFeedback,
propertiesTransformFeedback.TransformFeedbackQueries,
features2.Features.OcclusionQueryPrecise,

View File

@ -1,4 +1,5 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Vulkan.Effects;
using Silk.NET.Vulkan;
using Silk.NET.Vulkan.Extensions.KHR;
using System;
@ -29,6 +30,14 @@ namespace Ryujinx.Graphics.Vulkan
private bool _vsyncEnabled;
private bool _vsyncModeChanged;
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)
{
@ -116,7 +125,7 @@ namespace Ryujinx.Graphics.Vulkan
ImageFormat = surfaceFormat.Format,
ImageColorSpace = surfaceFormat.ColorSpace,
ImageExtent = extent,
ImageUsage = ImageUsageFlags.ColorAttachmentBit | ImageUsageFlags.TransferDstBit,
ImageUsage = ImageUsageFlags.ColorAttachmentBit | ImageUsageFlags.TransferDstBit | ImageUsageFlags.StorageBit,
ImageSharingMode = SharingMode.Exclusive,
ImageArrayLayers = 1,
PreTransform = capabilities.CurrentTransform,
@ -280,6 +289,13 @@ namespace Ryujinx.Graphics.Vulkan
var view = (TextureView)texture;
UpdateEffect();
if (_effect != null)
{
view = _effect.Run(view, cbs, _width, _height);
}
int srcX0, srcX1, srcY0, srcY1;
float scale = view.ScaleFactor;
@ -315,6 +331,18 @@ namespace Ryujinx.Graphics.Vulkan
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);
ScreenCaptureRequested = false;
@ -335,20 +363,36 @@ namespace Ryujinx.Graphics.Vulkan
int dstY0 = crop.FlipY ? dstPaddingY : _height - dstPaddingY;
int dstY1 = crop.FlipY ? _height - dstPaddingY : dstPaddingY;
_gd.HelperShader.BlitColor(
_gd,
cbs,
view,
_swapchainImageViews[nextImage],
_width,
_height,
1,
_format,
false,
new Extents2D(srcX0, srcY0, srcX1, srcY1),
new Extents2D(dstX0, dstY1, dstX1, dstY0),
true,
true);
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,
cbs,
view,
_swapchainImageViews[nextImage],
_width,
_height,
1,
_format,
false,
new Extents2D(srcX0, srcY0, srcX1, srcY1),
new Extents2D(dstX0, dstY1, dstX1, dstY0),
_isLinear,
true);
}
Transition(
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(
CommandBuffer commandBuffer,
Image image,
@ -456,8 +589,10 @@ namespace Ryujinx.Graphics.Vulkan
}
_gd.SwapchainApi.DestroySwapchain(_device, _swapchain, null);
}
_effect?.Dispose();
_scalingFilter?.Dispose();
}
}

View File

@ -11,5 +11,8 @@ namespace Ryujinx.Graphics.Vulkan
public abstract void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback);
public abstract void SetSize(int width, int height);
public abstract void ChangeVSyncMode(bool vsyncEnabled);
public abstract void SetAntiAliasing(AntiAliasing effect);
public abstract void SetScalingFilter(ScalingFilter scalerType);
public abstract void SetScalingFilterLevel(float scale);
}
}

View File

@ -209,7 +209,7 @@ namespace Ryujinx.HLE.FileSystem
{
using var ncaFile = new UniqueRef<IFile>();
fs.OpenFile(ref ncaFile.Ref(), ncaPath.FullPath.ToU8Span(), OpenMode.Read);
fs.OpenFile(ref ncaFile.Ref, ncaPath.FullPath.ToU8Span(), OpenMode.Read);
var nca = new Nca(_virtualFileSystem.KeySet, ncaFile.Get.AsStorage());
if (nca.Header.ContentType != NcaContentType.Meta)
{
@ -221,7 +221,7 @@ namespace Ryujinx.HLE.FileSystem
using var pfs0 = nca.OpenFileSystem(0, integrityCheckLevel);
using var cnmtFile = new UniqueRef<IFile>();
pfs0.OpenFile(ref cnmtFile.Ref(), pfs0.EnumerateEntries().Single().FullPath.ToU8Span(), OpenMode.Read);
pfs0.OpenFile(ref cnmtFile.Ref, pfs0.EnumerateEntries().Single().FullPath.ToU8Span(), OpenMode.Read);
var cnmt = new Cnmt(cnmtFile.Get.AsStream());
if (cnmt.Type != ContentMetaType.AddOnContent || (cnmt.TitleId & 0xFFFFFFFFFFFFE000) != aocBaseId)
@ -276,11 +276,11 @@ namespace Ryujinx.HLE.FileSystem
{
case ".xci":
pfs = new Xci(_virtualFileSystem.KeySet, file.AsStorage()).OpenPartition(XciPartitionType.Secure);
pfs.OpenFile(ref ncaFile.Ref(), aoc.NcaPath.ToU8Span(), OpenMode.Read);
pfs.OpenFile(ref ncaFile.Ref, aoc.NcaPath.ToU8Span(), OpenMode.Read);
break;
case ".nsp":
pfs = new PartitionFileSystem(file.AsStorage());
pfs.OpenFile(ref ncaFile.Ref(), aoc.NcaPath.ToU8Span(), OpenMode.Read);
pfs.OpenFile(ref ncaFile.Ref, aoc.NcaPath.ToU8Span(), OpenMode.Read);
break;
default:
return false; // Print error?
@ -625,11 +625,11 @@ namespace Ryujinx.HLE.FileSystem
if (filesystem.FileExists($"{path}/00"))
{
filesystem.OpenFile(ref file.Ref(), $"{path}/00".ToU8Span(), mode);
filesystem.OpenFile(ref file.Ref, $"{path}/00".ToU8Span(), mode);
}
else
{
filesystem.OpenFile(ref file.Ref(), path.ToU8Span(), mode);
filesystem.OpenFile(ref file.Ref, path.ToU8Span(), mode);
}
return file.Release();
@ -751,7 +751,7 @@ namespace Ryujinx.HLE.FileSystem
using var metaFile = new UniqueRef<IFile>();
if (fs.OpenFile(ref metaFile.Ref(), cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess())
if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess())
{
var meta = new Cnmt(metaFile.Get.AsStream());
@ -781,7 +781,7 @@ namespace Ryujinx.HLE.FileSystem
using var systemVersionFile = new UniqueRef<IFile>();
if (romfs.OpenFile(ref systemVersionFile.Ref(), "/file".ToU8Span(), OpenMode.Read).IsSuccess())
if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess())
{
systemVersion = new SystemVersion(systemVersionFile.Get.AsStream());
}
@ -820,7 +820,7 @@ namespace Ryujinx.HLE.FileSystem
using var metaFile = new UniqueRef<IFile>();
if (fs.OpenFile(ref metaFile.Ref(), cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess())
if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess())
{
var meta = new Cnmt(metaFile.Get.AsStream());
@ -891,7 +891,7 @@ namespace Ryujinx.HLE.FileSystem
using var metaFile = new UniqueRef<IFile>();
if (fs.OpenFile(ref metaFile.Ref(), cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess())
if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess())
{
var meta = new Cnmt(metaFile.Get.AsStream());
@ -909,7 +909,7 @@ namespace Ryujinx.HLE.FileSystem
using var systemVersionFile = new UniqueRef<IFile>();
if (romfs.OpenFile(ref systemVersionFile.Ref(), "/file".ToU8Span(), OpenMode.Read).IsSuccess())
if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess())
{
systemVersion = new SystemVersion(systemVersionFile.Get.AsStream());
}
@ -960,7 +960,7 @@ namespace Ryujinx.HLE.FileSystem
using var metaFile = new UniqueRef<IFile>();
if (fs.OpenFile(ref metaFile.Ref(), cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess())
if (fs.OpenFile(ref metaFile.Ref, cnmtPath.ToU8Span(), OpenMode.Read).IsSuccess())
{
var meta = new Cnmt(metaFile.Get.AsStream());
@ -1030,7 +1030,7 @@ namespace Ryujinx.HLE.FileSystem
using var systemVersionFile = new UniqueRef<IFile>();
if (romfs.OpenFile(ref systemVersionFile.Ref(), "/file".ToU8Span(), OpenMode.Read).IsSuccess())
if (romfs.OpenFile(ref systemVersionFile.Ref, "/file".ToU8Span(), OpenMode.Read).IsSuccess())
{
return new SystemVersion(systemVersionFile.Get.AsStream());
}

View File

@ -1,4 +1,4 @@
using LibHac.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
namespace Ryujinx.HLE.FileSystem
{

View File

@ -260,15 +260,16 @@ namespace Ryujinx.HLE.FileSystem
{
using var ticketFile = new UniqueRef<IFile>();
Result result = fs.OpenFile(ref ticketFile.Ref(), ticketEntry.FullPath.ToU8Span(), OpenMode.Read);
Result result = fs.OpenFile(ref ticketFile.Ref, ticketEntry.FullPath.ToU8Span(), OpenMode.Read);
if (result.IsSuccess())
{
Ticket ticket = new Ticket(ticketFile.Get.AsStream());
var titleKey = ticket.GetTitleKey(KeySet);
if (ticket.TitleKeyType == TitleKeyType.Common)
if (titleKey != null)
{
KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(ticket.GetTitleKey(KeySet)));
KeySet.ExternalKeySet.Add(new RightsId(ticket.RightsId), new AccessKey(titleKey));
}
}
}
@ -302,7 +303,7 @@ namespace Ryujinx.HLE.FileSystem
using var iterator = new UniqueRef<SaveDataIterator>();
Result rc = hos.Fs.OpenSaveDataIterator(ref iterator.Ref(), spaceId);
Result rc = hos.Fs.OpenSaveDataIterator(ref iterator.Ref, spaceId);
if (rc.IsFailure()) return rc;
while (true)

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