Compare commits

...

5 Commits

Author SHA1 Message Date
TSRBerry
fe15c77d30 [Hotfix] hid: Prevent out of bounds array access (#5547)
* hid: Prevent out of bounds array access

* Cast player to uint

* Replace lambda function with local function
2023-08-10 00:29:15 -03:00
TSRBerry
5e9678c8fa Allow access to code memory for exefs mods (#5518)
* Allow access to code memory for exefs mods

* Add ASLR workaround for Skyline

* Hardcode allowCodeMemoryForJit to true
2023-08-09 18:27:45 -03:00
jcm
773e239db7 Implement color space passthrough option (#5531)
Co-authored-by: jcm <butt@butts.com>
2023-08-07 18:54:05 +01:00
gdkchan
42750a74f8 Do not add more code after alpha test discard on fragment shader (#5529)
* Do not add more code after alpha test discard on fragment shader

* Shader cache version bump
2023-08-07 12:20:37 -03:00
TSRBerry
3ab0a71c7b Fix PR build concurrency and stop auto assigning reviewers for draft PRs (#5519)
* build: Remove concurrency

It's called by checks anyway.

* Only assign reviewers for PRs that are ready for reviews
2023-08-06 23:25:02 +02:00
25 changed files with 206 additions and 59 deletions

View File

@@ -14,6 +14,13 @@ tab_width = 4
end_of_line = lf end_of_line = lf
insert_final_newline = true insert_final_newline = true
# JSON files
[*.json]
# Indentation and spacing
indent_size = 2
tab_width = 2
# C# files # C# files
[*.cs] [*.cs]

View File

@@ -3,10 +3,6 @@ name: Build job
on: on:
workflow_call: workflow_call:
concurrency:
group: pr-builds-${{ github.event.number }}
cancel-in-progress: true
env: env:
POWERSHELL_TELEMETRY_OPTOUT: 1 POWERSHELL_TELEMETRY_OPTOUT: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1 DOTNET_CLI_TELEMETRY_OPTOUT: 1

View File

@@ -28,6 +28,7 @@ jobs:
dot: true dot: true
- name: Assign reviewers - name: Assign reviewers
if: ! github.event.pull_request.draft
run: | run: |
pip3 install PyGithub pip3 install PyGithub
python3 .github/update_reviewers.py ${{ secrets.GITHUB_TOKEN }} ${{ github.repository }} ${{ github.event.pull_request.number }} .github/reviewers.yml python3 .github/update_reviewers.py ${{ secrets.GITHUB_TOKEN }} ${{ github.repository }} ${{ github.event.pull_request.number }} .github/reviewers.yml

View File

@@ -29,7 +29,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 5502; //! To be incremented manually for each change to the ARMeilleure project. private const uint InternalVersion = 5518; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0"; private const string ActualDir = "0";
private const string BackupDir = "1"; private const string BackupDir = "1";

View File

@@ -27,6 +27,26 @@ namespace ARMeilleure.Translation.PTC
return dictionary; return dictionary;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Dictionary<TKey, TValue> DeserializeAndUpdateDictionary<TKey, TValue>(Stream stream, Func<Stream, TValue> valueFunc, Func<TKey, TValue, (TKey, TValue)> updateFunc) where TKey : struct
{
Dictionary<TKey, TValue> dictionary = new();
int count = DeserializeStructure<int>(stream);
for (int i = 0; i < count; i++)
{
TKey key = DeserializeStructure<TKey>(stream);
TValue value = valueFunc(stream);
(key, value) = updateFunc(key, value);
dictionary.Add(key, value);
}
return dictionary;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static List<T> DeserializeList<T>(Stream stream) where T : struct public static List<T> DeserializeList<T>(Stream stream) where T : struct
{ {

View File

@@ -9,10 +9,13 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.IO.Compression; using System.IO.Compression;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using System.Timers;
using static ARMeilleure.Translation.PTC.PtcFormatter; using static ARMeilleure.Translation.PTC.PtcFormatter;
using Timer = System.Timers.Timer;
namespace ARMeilleure.Translation.PTC namespace ARMeilleure.Translation.PTC
{ {
@@ -20,7 +23,11 @@ namespace ARMeilleure.Translation.PTC
{ {
private const string OuterHeaderMagicString = "Pohd\0\0\0\0"; private const string OuterHeaderMagicString = "Pohd\0\0\0\0";
private const uint InternalVersion = 1866; //! Not to be incremented manually for each change to the ARMeilleure project. private const uint InternalVersion = 5518; //! Not to be incremented manually for each change to the ARMeilleure project.
private static readonly uint[] _migrateInternalVersions = {
1866,
};
private const int SaveInterval = 30; // Seconds. private const int SaveInterval = 30; // Seconds.
@@ -28,7 +35,7 @@ namespace ARMeilleure.Translation.PTC
private readonly Ptc _ptc; private readonly Ptc _ptc;
private readonly System.Timers.Timer _timer; private readonly Timer _timer;
private readonly ulong _outerHeaderMagic; private readonly ulong _outerHeaderMagic;
@@ -51,7 +58,7 @@ namespace ARMeilleure.Translation.PTC
{ {
_ptc = ptc; _ptc = ptc;
_timer = new System.Timers.Timer((double)SaveInterval * 1000d); _timer = new Timer(SaveInterval * 1000d);
_timer.Elapsed += PreSave; _timer.Elapsed += PreSave;
_outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan()); _outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan());
@@ -168,7 +175,7 @@ namespace ARMeilleure.Translation.PTC
return false; return false;
} }
if (outerHeader.InfoFileVersion != InternalVersion) if (outerHeader.InfoFileVersion != InternalVersion && !_migrateInternalVersions.Contains(outerHeader.InfoFileVersion))
{ {
InvalidateCompressedStream(compressedStream); InvalidateCompressedStream(compressedStream);
@@ -211,7 +218,19 @@ namespace ARMeilleure.Translation.PTC
return false; return false;
} }
ProfiledFuncs = Deserialize(stream); switch (outerHeader.InfoFileVersion)
{
case InternalVersion:
ProfiledFuncs = Deserialize(stream);
break;
case 1866:
ProfiledFuncs = Deserialize(stream, (address, profile) => (address + 0x500000UL, profile));
break;
default:
Logger.Error?.Print(LogClass.Ptc, $"No migration path for {nameof(outerHeader.InfoFileVersion)} '{outerHeader.InfoFileVersion}'. Discarding cache.");
InvalidateCompressedStream(compressedStream);
return false;
}
Debug.Assert(stream.Position == stream.Length); Debug.Assert(stream.Position == stream.Length);
@@ -225,9 +244,14 @@ namespace ARMeilleure.Translation.PTC
return true; return true;
} }
private static Dictionary<ulong, FuncProfile> Deserialize(Stream stream) private static Dictionary<ulong, FuncProfile> Deserialize(Stream stream, Func<ulong, FuncProfile, (ulong, FuncProfile)> migrateEntryFunc = null)
{ {
return DeserializeDictionary<ulong, FuncProfile>(stream, (stream) => DeserializeStructure<FuncProfile>(stream)); if (migrateEntryFunc != null)
{
return DeserializeAndUpdateDictionary(stream, DeserializeStructure<FuncProfile>, migrateEntryFunc);
}
return DeserializeDictionary<ulong, FuncProfile>(stream, DeserializeStructure<FuncProfile>);
} }
private static ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream) private static ReadOnlySpan<byte> GetReadOnlySpan(MemoryStream memoryStream)
@@ -240,7 +264,7 @@ namespace ARMeilleure.Translation.PTC
compressedStream.SetLength(0L); compressedStream.SetLength(0L);
} }
private void PreSave(object source, System.Timers.ElapsedEventArgs e) private void PreSave(object source, ElapsedEventArgs e)
{ {
_waitEvent.Reset(); _waitEvent.Reset();
@@ -277,7 +301,7 @@ namespace ARMeilleure.Translation.PTC
{ {
Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L); Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L);
stream.Seek((long)Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin); stream.Seek(Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin);
lock (_lock) lock (_lock)
{ {
@@ -288,7 +312,7 @@ namespace ARMeilleure.Translation.PTC
Debug.Assert(stream.Position == stream.Length); Debug.Assert(stream.Position == stream.Length);
stream.Seek((long)Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin); stream.Seek(Unsafe.SizeOf<Hash128>(), SeekOrigin.Begin);
Hash128 hash = XXHash128.ComputeHash(GetReadOnlySpan(stream)); Hash128 hash = XXHash128.ComputeHash(GetReadOnlySpan(stream));
stream.Seek(0L, SeekOrigin.Begin); stream.Seek(0L, SeekOrigin.Begin);
@@ -332,7 +356,7 @@ namespace ARMeilleure.Translation.PTC
private static void Serialize(Stream stream, Dictionary<ulong, FuncProfile> profiledFuncs) private static void Serialize(Stream stream, Dictionary<ulong, FuncProfile> profiledFuncs)
{ {
SerializeDictionary(stream, profiledFuncs, (stream, structure) => SerializeStructure(stream, structure)); SerializeDictionary(stream, profiledFuncs, SerializeStructure);
} }
[StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 29*/)] [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 29*/)]

View File

@@ -186,6 +186,7 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.Graphics.AntiAliasing.Event += UpdateAntiAliasing; ConfigurationState.Instance.Graphics.AntiAliasing.Event += UpdateAntiAliasing;
ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter; ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter;
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel; ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel;
ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event += UpdateColorSpacePassthrough;
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState; ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState;
@@ -229,6 +230,11 @@ namespace Ryujinx.Ava
_renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); _renderer.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value);
} }
private void UpdateColorSpacePassthrough(object sender, ReactiveEventArgs<bool> e)
{
_renderer.Window?.SetColorSpacePassthrough((bool)ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Value);
}
private void ShowCursor() private void ShowCursor()
{ {
Dispatcher.UIThread.Post(() => Dispatcher.UIThread.Post(() =>
@@ -461,6 +467,7 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.Graphics.ScalingFilter.Event -= UpdateScalingFilter; ConfigurationState.Instance.Graphics.ScalingFilter.Event -= UpdateScalingFilter;
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel; ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel;
ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing; ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing;
ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event -= UpdateColorSpacePassthrough;
_topLevel.PointerMoved -= TopLevel_PointerEnterOrMoved; _topLevel.PointerMoved -= TopLevel_PointerEnterOrMoved;
_topLevel.PointerEnter -= TopLevel_PointerEnterOrMoved; _topLevel.PointerEnter -= TopLevel_PointerEnterOrMoved;
@@ -887,6 +894,7 @@ namespace Ryujinx.Ava
_renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)ConfigurationState.Instance.Graphics.AntiAliasing.Value); _renderer?.Window?.SetAntiAliasing((Graphics.GAL.AntiAliasing)ConfigurationState.Instance.Graphics.AntiAliasing.Value);
_renderer?.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); _renderer?.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value);
_renderer?.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); _renderer?.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value);
_renderer?.Window?.SetColorSpacePassthrough(ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Value);
Width = (int)RendererHost.Bounds.Width; Width = (int)RendererHost.Bounds.Width;
Height = (int)RendererHost.Bounds.Height; Height = (int)RendererHost.Bounds.Height;

View File

@@ -620,6 +620,8 @@
"SettingsTabHotkeysVolumeDownHotkey": "Decrease Volume:", "SettingsTabHotkeysVolumeDownHotkey": "Decrease Volume:",
"SettingsEnableMacroHLE": "Enable Macro HLE", "SettingsEnableMacroHLE": "Enable Macro HLE",
"SettingsEnableMacroHLETooltip": "High-level emulation of GPU Macro code.\n\nImproves performance, but may cause graphical glitches in some games.\n\nLeave ON if unsure.", "SettingsEnableMacroHLETooltip": "High-level emulation of GPU Macro code.\n\nImproves performance, but may cause graphical glitches in some games.\n\nLeave ON if unsure.",
"SettingsEnableColorSpacePassthrough": "Color Space Passthrough",
"SettingsEnableColorSpacePassthroughTooltip": "Directs the Vulkan backend to pass through color information without specifying a color space. For users with wide gamut displays, this may result in more vibrant colors, at the cost of color correctness.",
"VolumeShort": "Vol", "VolumeShort": "Vol",
"UserProfilesManageSaves": "Manage Saves", "UserProfilesManageSaves": "Manage Saves",
"DeleteUserSave": "Do you want to delete user save for this game?", "DeleteUserSave": "Do you want to delete user save for this game?",

View File

@@ -145,6 +145,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool EnableShaderCache { get; set; } public bool EnableShaderCache { get; set; }
public bool EnableTextureRecompression { get; set; } public bool EnableTextureRecompression { get; set; }
public bool EnableMacroHLE { get; set; } public bool EnableMacroHLE { get; set; }
public bool EnableColorSpacePassthrough { get; set; }
public bool EnableFileLog { get; set; } public bool EnableFileLog { get; set; }
public bool EnableStub { get; set; } public bool EnableStub { get; set; }
public bool EnableInfo { get; set; } public bool EnableInfo { get; set; }
@@ -419,6 +420,7 @@ namespace Ryujinx.Ava.UI.ViewModels
EnableShaderCache = config.Graphics.EnableShaderCache; EnableShaderCache = config.Graphics.EnableShaderCache;
EnableTextureRecompression = config.Graphics.EnableTextureRecompression; EnableTextureRecompression = config.Graphics.EnableTextureRecompression;
EnableMacroHLE = config.Graphics.EnableMacroHLE; EnableMacroHLE = config.Graphics.EnableMacroHLE;
EnableColorSpacePassthrough = config.Graphics.EnableColorSpacePassthrough;
ResolutionScale = config.Graphics.ResScale == -1 ? 4 : config.Graphics.ResScale - 1; ResolutionScale = config.Graphics.ResScale == -1 ? 4 : config.Graphics.ResScale - 1;
CustomResolutionScale = config.Graphics.ResScaleCustom; CustomResolutionScale = config.Graphics.ResScaleCustom;
MaxAnisotropy = config.Graphics.MaxAnisotropy == -1 ? 0 : (int)(MathF.Log2(config.Graphics.MaxAnisotropy)); MaxAnisotropy = config.Graphics.MaxAnisotropy == -1 ? 0 : (int)(MathF.Log2(config.Graphics.MaxAnisotropy));
@@ -506,6 +508,7 @@ namespace Ryujinx.Ava.UI.ViewModels
config.Graphics.EnableShaderCache.Value = EnableShaderCache; config.Graphics.EnableShaderCache.Value = EnableShaderCache;
config.Graphics.EnableTextureRecompression.Value = EnableTextureRecompression; config.Graphics.EnableTextureRecompression.Value = EnableTextureRecompression;
config.Graphics.EnableMacroHLE.Value = EnableMacroHLE; config.Graphics.EnableMacroHLE.Value = EnableMacroHLE;
config.Graphics.EnableColorSpacePassthrough.Value = EnableColorSpacePassthrough;
config.Graphics.ResScale.Value = ResolutionScale == 4 ? -1 : ResolutionScale + 1; config.Graphics.ResScale.Value = ResolutionScale == 4 ? -1 : ResolutionScale + 1;
config.Graphics.ResScaleCustom.Value = CustomResolutionScale; config.Graphics.ResScaleCustom.Value = CustomResolutionScale;
config.Graphics.MaxAnisotropy.Value = MaxAnisotropy == 0 ? -1 : MathF.Pow(2, MaxAnisotropy); config.Graphics.MaxAnisotropy.Value = MaxAnisotropy == 0 ? -1 : MathF.Pow(2, MaxAnisotropy);

View File

@@ -73,6 +73,10 @@
ToolTip.Tip="{locale:Locale SettingsEnableMacroHLETooltip}"> ToolTip.Tip="{locale:Locale SettingsEnableMacroHLETooltip}">
<TextBlock Text="{locale:Locale SettingsEnableMacroHLE}" /> <TextBlock Text="{locale:Locale SettingsEnableMacroHLE}" />
</CheckBox> </CheckBox>
<CheckBox IsChecked="{Binding EnableColorSpacePassthrough}"
ToolTip.Tip="{locale:Locale SettingsEnableColorSpacePassthroughTooltip}">
<TextBlock Text="{locale:Locale SettingsEnableColorSpacePassthrough}" />
</CheckBox>
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock VerticalAlignment="Center" <TextBlock VerticalAlignment="Center"

View File

@@ -13,5 +13,6 @@ namespace Ryujinx.Graphics.GAL
void SetAntiAliasing(AntiAliasing antialiasing); void SetAntiAliasing(AntiAliasing antialiasing);
void SetScalingFilter(ScalingFilter type); void SetScalingFilter(ScalingFilter type);
void SetScalingFilterLevel(float level); void SetScalingFilterLevel(float level);
void SetColorSpacePassthrough(bool colorSpacePassThroughEnabled);
} }
} }

View File

@@ -38,5 +38,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
public void SetScalingFilter(ScalingFilter type) { } public void SetScalingFilter(ScalingFilter type) { }
public void SetScalingFilterLevel(float level) { } public void SetScalingFilterLevel(float level) { }
public void SetColorSpacePassthrough(bool colorSpacePassthroughEnabled) { }
} }
} }

View File

@@ -67,6 +67,11 @@ namespace Ryujinx.Graphics.Gpu
/// Enables or disables recompression of compressed textures that are not natively supported by the host. /// Enables or disables recompression of compressed textures that are not natively supported by the host.
/// </summary> /// </summary>
public static bool EnableTextureRecompression = false; public static bool EnableTextureRecompression = false;
/// <summary>
/// Enables or disables color space passthrough, if available.
/// </summary>
public static bool EnableColorSpacePassthrough = false;
} }
#pragma warning restore CA2211 #pragma warning restore CA2211
} }

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2; private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 4675; private const uint CodeGenVersion = 5529;
private const string SharedTocFileName = "shared.toc"; private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data"; private const string SharedDataFileName = "shared.data";

View File

@@ -307,6 +307,8 @@ namespace Ryujinx.Graphics.OpenGL
_updateScalingFilter = true; _updateScalingFilter = true;
} }
public void SetColorSpacePassthrough(bool colorSpacePassthroughEnabled) { }
private void UpdateEffect() private void UpdateEffect()
{ {
if (_updateEffect) if (_updateEffect)

View File

@@ -162,8 +162,10 @@ namespace Ryujinx.Graphics.Shader.Instructions
if (op.Ccc == Ccc.T) if (op.Ccc == Ccc.T)
{ {
context.PrepareForReturn(); if (context.PrepareForReturn())
context.Return(); {
context.Return();
}
} }
else else
{ {
@@ -174,8 +176,12 @@ namespace Ryujinx.Graphics.Shader.Instructions
{ {
Operand lblSkip = Label(); Operand lblSkip = Label();
context.BranchIfFalse(lblSkip, cond); context.BranchIfFalse(lblSkip, cond);
context.PrepareForReturn();
context.Return(); if (context.PrepareForReturn())
{
context.Return();
}
context.MarkLabel(lblSkip); context.MarkLabel(lblSkip);
} }
} }

View File

@@ -304,11 +304,11 @@ namespace Ryujinx.Graphics.Shader.Translation
PrepareForVertexReturn(); PrepareForVertexReturn();
} }
public void PrepareForReturn() public bool PrepareForReturn()
{ {
if (IsNonMain) if (IsNonMain)
{ {
return; return true;
} }
if (Config.LastInVertexPipeline && if (Config.LastInVertexPipeline &&
@@ -383,13 +383,13 @@ namespace Ryujinx.Graphics.Shader.Translation
AlphaTestOp alphaTestOp = Config.GpuAccessor.QueryAlphaTestCompare(); AlphaTestOp alphaTestOp = Config.GpuAccessor.QueryAlphaTestCompare();
if (alphaTestOp != AlphaTestOp.Always && (Config.OmapTargets & 8) != 0) if (alphaTestOp != AlphaTestOp.Always)
{ {
if (alphaTestOp == AlphaTestOp.Never) if (alphaTestOp == AlphaTestOp.Never)
{ {
this.Discard(); this.Discard();
} }
else else if ((Config.OmapTargets & 8) != 0)
{ {
Instruction comparator = alphaTestOp switch Instruction comparator = alphaTestOp switch
{ {
@@ -415,6 +415,12 @@ namespace Ryujinx.Graphics.Shader.Translation
} }
} }
// We don't need to output anything if alpha test always fails.
if (alphaTestOp == AlphaTestOp.Never)
{
return false;
}
int regIndexBase = 0; int regIndexBase = 0;
for (int rtIndex = 0; rtIndex < 8; rtIndex++) for (int rtIndex = 0; rtIndex < 8; rtIndex++)
@@ -462,6 +468,8 @@ namespace Ryujinx.Graphics.Shader.Translation
} }
} }
} }
return true;
} }
private void GenerateAlphaToCoverageDitherDiscard() private void GenerateAlphaToCoverageDitherDiscard()

View File

@@ -28,7 +28,7 @@ namespace Ryujinx.Graphics.Vulkan
private int _width; private int _width;
private int _height; private int _height;
private bool _vsyncEnabled; private bool _vsyncEnabled;
private bool _vsyncModeChanged; private bool _swapchainIsDirty;
private VkFormat _format; private VkFormat _format;
private AntiAliasing _currentAntiAliasing; private AntiAliasing _currentAntiAliasing;
private bool _updateEffect; private bool _updateEffect;
@@ -38,6 +38,7 @@ namespace Ryujinx.Graphics.Vulkan
private float _scalingFilterLevel; private float _scalingFilterLevel;
private bool _updateScalingFilter; private bool _updateScalingFilter;
private ScalingFilter _currentScalingFilter; private ScalingFilter _currentScalingFilter;
private bool _colorSpacePassthroughEnabled;
public unsafe Window(VulkanRenderer gd, SurfaceKHR surface, PhysicalDevice physicalDevice, Device device) public unsafe Window(VulkanRenderer gd, SurfaceKHR surface, PhysicalDevice physicalDevice, Device device)
{ {
@@ -60,7 +61,7 @@ namespace Ryujinx.Graphics.Vulkan
private void RecreateSwapchain() private void RecreateSwapchain()
{ {
var oldSwapchain = _swapchain; var oldSwapchain = _swapchain;
_vsyncModeChanged = false; _swapchainIsDirty = false;
for (int i = 0; i < _swapchainImageViews.Length; i++) for (int i = 0; i < _swapchainImageViews.Length; i++)
{ {
@@ -106,7 +107,7 @@ namespace Ryujinx.Graphics.Vulkan
imageCount = capabilities.MaxImageCount; imageCount = capabilities.MaxImageCount;
} }
var surfaceFormat = ChooseSwapSurfaceFormat(surfaceFormats); var surfaceFormat = ChooseSwapSurfaceFormat(surfaceFormats, _colorSpacePassthroughEnabled);
var extent = ChooseSwapExtent(capabilities); var extent = ChooseSwapExtent(capabilities);
@@ -178,22 +179,40 @@ namespace Ryujinx.Graphics.Vulkan
return new Auto<DisposableImageView>(new DisposableImageView(_gd.Api, _device, imageView)); return new Auto<DisposableImageView>(new DisposableImageView(_gd.Api, _device, imageView));
} }
private static SurfaceFormatKHR ChooseSwapSurfaceFormat(SurfaceFormatKHR[] availableFormats) private static SurfaceFormatKHR ChooseSwapSurfaceFormat(SurfaceFormatKHR[] availableFormats, bool colorSpacePassthroughEnabled)
{ {
if (availableFormats.Length == 1 && availableFormats[0].Format == VkFormat.Undefined) if (availableFormats.Length == 1 && availableFormats[0].Format == VkFormat.Undefined)
{ {
return new SurfaceFormatKHR(VkFormat.B8G8R8A8Unorm, ColorSpaceKHR.PaceSrgbNonlinearKhr); return new SurfaceFormatKHR(VkFormat.B8G8R8A8Unorm, ColorSpaceKHR.PaceSrgbNonlinearKhr);
} }
var formatToReturn = availableFormats[0];
foreach (var format in availableFormats) if (colorSpacePassthroughEnabled)
{ {
if (format.Format == VkFormat.B8G8R8A8Unorm && format.ColorSpace == ColorSpaceKHR.PaceSrgbNonlinearKhr) foreach (var format in availableFormats)
{ {
return format; if (format.Format == VkFormat.B8G8R8A8Unorm && format.ColorSpace == ColorSpaceKHR.SpacePassThroughExt)
{
formatToReturn = format;
break;
}
else if (format.Format == VkFormat.B8G8R8A8Unorm && format.ColorSpace == ColorSpaceKHR.PaceSrgbNonlinearKhr)
{
formatToReturn = format;
}
} }
} }
else
return availableFormats[0]; {
foreach (var format in availableFormats)
{
if (format.Format == VkFormat.B8G8R8A8Unorm && format.ColorSpace == ColorSpaceKHR.PaceSrgbNonlinearKhr)
{
formatToReturn = format;
break;
}
}
}
return formatToReturn;
} }
private static CompositeAlphaFlagsKHR ChooseCompositeAlpha(CompositeAlphaFlagsKHR supportedFlags) private static CompositeAlphaFlagsKHR ChooseCompositeAlpha(CompositeAlphaFlagsKHR supportedFlags)
@@ -259,7 +278,7 @@ namespace Ryujinx.Graphics.Vulkan
if (acquireResult == Result.ErrorOutOfDateKhr || if (acquireResult == Result.ErrorOutOfDateKhr ||
acquireResult == Result.SuboptimalKhr || acquireResult == Result.SuboptimalKhr ||
_vsyncModeChanged) _swapchainIsDirty)
{ {
RecreateSwapchain(); RecreateSwapchain();
} }
@@ -443,6 +462,12 @@ namespace Ryujinx.Graphics.Vulkan
_updateScalingFilter = true; _updateScalingFilter = true;
} }
public override void SetColorSpacePassthrough(bool colorSpacePassthroughEnabled)
{
_colorSpacePassthroughEnabled = colorSpacePassthroughEnabled;
_swapchainIsDirty = true;
}
private void UpdateEffect() private void UpdateEffect()
{ {
if (_updateEffect) if (_updateEffect)
@@ -559,7 +584,7 @@ namespace Ryujinx.Graphics.Vulkan
public override void ChangeVSyncMode(bool vsyncEnabled) public override void ChangeVSyncMode(bool vsyncEnabled)
{ {
_vsyncEnabled = vsyncEnabled; _vsyncEnabled = vsyncEnabled;
_vsyncModeChanged = true; _swapchainIsDirty = true;
} }
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)

View File

@@ -14,5 +14,6 @@ namespace Ryujinx.Graphics.Vulkan
public abstract void SetAntiAliasing(AntiAliasing effect); public abstract void SetAntiAliasing(AntiAliasing effect);
public abstract void SetScalingFilter(ScalingFilter scalerType); public abstract void SetScalingFilter(ScalingFilter scalerType);
public abstract void SetScalingFilterLevel(float scale); public abstract void SetScalingFilterLevel(float scale);
public abstract void SetColorSpacePassthrough(bool colorSpacePassthroughEnabled);
} }
} }

View File

@@ -70,6 +70,11 @@ namespace Ryujinx.HLE.HOS.Services.Hid
internal void SetSupportedPlayer(PlayerIndex player, bool supported = true) internal void SetSupportedPlayer(PlayerIndex player, bool supported = true)
{ {
if ((uint)player >= _supportedPlayers.Length)
{
return;
}
_supportedPlayers[(int)player] = supported; _supportedPlayers[(int)player] = supported;
} }

View File

@@ -89,9 +89,6 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
Logger.Warning?.Print(LogClass.Ptc, "Detected unsupported ExeFs modifications. PTC disabled."); Logger.Warning?.Print(LogClass.Ptc, "Detected unsupported ExeFs modifications. PTC disabled.");
} }
// We allow it for nx-hbloader because it can be used to launch homebrew.
bool allowCodeMemoryForJit = programId == 0x010000000000100DUL || isHomebrew;
string programName = ""; string programName = "";
if (!isHomebrew && programId > 0x010000000000FFFF) if (!isHomebrew && programId > 0x010000000000FFFF)
@@ -119,7 +116,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
metaLoader, metaLoader,
nacpData, nacpData,
enablePtc, enablePtc,
allowCodeMemoryForJit, true,
programName, programName,
metaLoader.GetProgramId(), metaLoader.GetProgramId(),
null, null,

View File

@@ -28,6 +28,11 @@ namespace Ryujinx.HLE.Loaders.Processes
{ {
static class ProcessLoaderHelper static class ProcessLoaderHelper
{ {
// NOTE: If you want to change this value make sure to increment the InternalVersion of Ptc and PtcProfiler.
// You also need to add a new migration path and adjust the existing ones.
// TODO: Remove this workaround when ASLR is implemented.
private const ulong CodeStartOffset = 0x500000UL;
public static LibHac.Result RegisterProgramMapInfo(Switch device, PartitionFileSystem partitionFileSystem) public static LibHac.Result RegisterProgramMapInfo(Switch device, PartitionFileSystem partitionFileSystem)
{ {
ulong applicationId = 0; ulong applicationId = 0;
@@ -242,7 +247,7 @@ namespace Ryujinx.HLE.Loaders.Processes
ulong argsStart = 0; ulong argsStart = 0;
uint argsSize = 0; uint argsSize = 0;
ulong codeStart = (meta.Flags & 1) != 0 ? 0x8000000UL : 0x200000UL; ulong codeStart = ((meta.Flags & 1) != 0 ? 0x8000000UL : 0x200000UL) + CodeStartOffset;
uint codeSize = 0; uint codeSize = 0;
var buildIds = executables.Select(e => (e switch var buildIds = executables.Select(e => (e switch

View File

@@ -48,7 +48,22 @@ namespace Ryujinx.Tests.Cpu
bool methodCalled = false; bool methodCalled = false;
bool isFz = false; bool isFz = false;
var managedMethod = () => var method = TranslatorTestMethods.GenerateFpFlagsPInvokeTest();
// This method sets flush-to-zero and then calls the managed method.
// Before and after setting the flags, it ensures subnormal addition works as expected.
// It returns a positive result if any tests fail, and 0 on success (or if the platform cannot change FP flags)
int result = method(Marshal.GetFunctionPointerForDelegate(ManagedMethod));
// Subnormal results are not flushed to zero by default, which we should have returned to exiting the method.
Assert.AreNotEqual(GetDenormal() + GetZero(), 0f);
Assert.True(result == 0);
Assert.True(methodCalled);
Assert.True(isFz);
return;
void ManagedMethod()
{ {
// Floating point math should not modify fp flags. // Floating point math should not modify fp flags.
float test = 2f * 3.5f; float test = 2f * 3.5f;
@@ -73,21 +88,7 @@ namespace Ryujinx.Tests.Cpu
methodCalled = true; methodCalled = true;
} }
}; }
var method = TranslatorTestMethods.GenerateFpFlagsPInvokeTest();
// This method sets flush-to-zero and then calls the managed method.
// Before and after setting the flags, it ensures subnormal addition works as expected.
// It returns a positive result if any tests fail, and 0 on success (or if the platform cannot change FP flags)
int result = method(Marshal.GetFunctionPointerForDelegate(managedMethod));
// Subnormal results are not flushed to zero by default, which we should have returned to exiting the method.
Assert.AreNotEqual(GetDenormal() + GetZero(), 0f);
Assert.True(result == 0);
Assert.True(methodCalled);
Assert.True(isFz);
} }
} }
} }

View File

@@ -14,7 +14,7 @@ namespace Ryujinx.Ui.Common.Configuration
/// <summary> /// <summary>
/// The current version of the file format /// The current version of the file format
/// </summary> /// </summary>
public const int CurrentVersion = 47; public const int CurrentVersion = 48;
/// <summary> /// <summary>
/// Version of the configuration file format /// Version of the configuration file format
@@ -186,6 +186,11 @@ namespace Ryujinx.Ui.Common.Configuration
/// </summary> /// </summary>
public bool EnableMacroHLE { get; set; } public bool EnableMacroHLE { get; set; }
/// <summary>
/// Enables or disables color space passthrough, if available.
/// </summary>
public bool EnableColorSpacePassthrough { get; set; }
/// <summary> /// <summary>
/// Enables or disables profiled translation cache persistency /// Enables or disables profiled translation cache persistency
/// </summary> /// </summary>

View File

@@ -485,6 +485,11 @@ namespace Ryujinx.Ui.Common.Configuration
/// </summary> /// </summary>
public ReactiveObject<bool> EnableMacroHLE { get; private set; } public ReactiveObject<bool> EnableMacroHLE { get; private set; }
/// <summary>
/// Enables or disables color space passthrough, if available.
/// </summary>
public ReactiveObject<bool> EnableColorSpacePassthrough { get; private set; }
/// <summary> /// <summary>
/// Graphics backend /// Graphics backend
/// </summary> /// </summary>
@@ -535,6 +540,8 @@ namespace Ryujinx.Ui.Common.Configuration
PreferredGpu.Event += static (sender, e) => LogValueChange(e, nameof(PreferredGpu)); PreferredGpu.Event += static (sender, e) => LogValueChange(e, nameof(PreferredGpu));
EnableMacroHLE = new ReactiveObject<bool>(); EnableMacroHLE = new ReactiveObject<bool>();
EnableMacroHLE.Event += static (sender, e) => LogValueChange(e, nameof(EnableMacroHLE)); EnableMacroHLE.Event += static (sender, e) => LogValueChange(e, nameof(EnableMacroHLE));
EnableColorSpacePassthrough = new ReactiveObject<bool>();
EnableColorSpacePassthrough.Event += static (sender, e) => LogValueChange(e, nameof(EnableColorSpacePassthrough));
AntiAliasing = new ReactiveObject<AntiAliasing>(); AntiAliasing = new ReactiveObject<AntiAliasing>();
AntiAliasing.Event += static (sender, e) => LogValueChange(e, nameof(AntiAliasing)); AntiAliasing.Event += static (sender, e) => LogValueChange(e, nameof(AntiAliasing));
ScalingFilter = new ReactiveObject<ScalingFilter>(); ScalingFilter = new ReactiveObject<ScalingFilter>();
@@ -667,6 +674,7 @@ namespace Ryujinx.Ui.Common.Configuration
EnableShaderCache = Graphics.EnableShaderCache, EnableShaderCache = Graphics.EnableShaderCache,
EnableTextureRecompression = Graphics.EnableTextureRecompression, EnableTextureRecompression = Graphics.EnableTextureRecompression,
EnableMacroHLE = Graphics.EnableMacroHLE, EnableMacroHLE = Graphics.EnableMacroHLE,
EnableColorSpacePassthrough = Graphics.EnableColorSpacePassthrough,
EnablePtc = System.EnablePtc, EnablePtc = System.EnablePtc,
EnableInternetAccess = System.EnableInternetAccess, EnableInternetAccess = System.EnableInternetAccess,
EnableFsIntegrityChecks = System.EnableFsIntegrityChecks, EnableFsIntegrityChecks = System.EnableFsIntegrityChecks,
@@ -772,6 +780,7 @@ namespace Ryujinx.Ui.Common.Configuration
Graphics.EnableShaderCache.Value = true; Graphics.EnableShaderCache.Value = true;
Graphics.EnableTextureRecompression.Value = false; Graphics.EnableTextureRecompression.Value = false;
Graphics.EnableMacroHLE.Value = true; Graphics.EnableMacroHLE.Value = true;
Graphics.EnableColorSpacePassthrough.Value = false;
Graphics.AntiAliasing.Value = AntiAliasing.None; Graphics.AntiAliasing.Value = AntiAliasing.None;
Graphics.ScalingFilter.Value = ScalingFilter.Bilinear; Graphics.ScalingFilter.Value = ScalingFilter.Bilinear;
Graphics.ScalingFilterLevel.Value = 80; Graphics.ScalingFilterLevel.Value = 80;
@@ -1391,6 +1400,15 @@ namespace Ryujinx.Ui.Common.Configuration
configurationFileUpdated = true; configurationFileUpdated = true;
} }
if (configurationFileFormat.Version < 48)
{
Ryujinx.Common.Logging.Logger.Warning?.Print(LogClass.Application, $"Outdated configuration version {configurationFileFormat.Version}, migrating to version 48.");
configurationFileFormat.EnableColorSpacePassthrough = false;
configurationFileUpdated = true;
}
Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog; Logger.EnableFileLog.Value = configurationFileFormat.EnableFileLog;
Graphics.ResScale.Value = configurationFileFormat.ResScale; Graphics.ResScale.Value = configurationFileFormat.ResScale;
Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom; Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom;
@@ -1426,6 +1444,7 @@ namespace Ryujinx.Ui.Common.Configuration
Graphics.EnableShaderCache.Value = configurationFileFormat.EnableShaderCache; Graphics.EnableShaderCache.Value = configurationFileFormat.EnableShaderCache;
Graphics.EnableTextureRecompression.Value = configurationFileFormat.EnableTextureRecompression; Graphics.EnableTextureRecompression.Value = configurationFileFormat.EnableTextureRecompression;
Graphics.EnableMacroHLE.Value = configurationFileFormat.EnableMacroHLE; Graphics.EnableMacroHLE.Value = configurationFileFormat.EnableMacroHLE;
Graphics.EnableColorSpacePassthrough.Value = configurationFileFormat.EnableColorSpacePassthrough;
System.EnablePtc.Value = configurationFileFormat.EnablePtc; System.EnablePtc.Value = configurationFileFormat.EnablePtc;
System.EnableInternetAccess.Value = configurationFileFormat.EnableInternetAccess; System.EnableInternetAccess.Value = configurationFileFormat.EnableInternetAccess;
System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks; System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks;