Compare commits

..

11 Commits

Author SHA1 Message Date
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
riperiperi
6e784e0aca GPU: Don't sync/bind index buffer when it's not in use (#5526)
* GPU: Don't sync/bind index buffer when it's not in use

Sometimes draws don't use an index buffer. It's not necessary to check or upload data for the current index buffer binding as it won't be used.

This fixes Pokemon: Legends Arceus updating a stale index buffer for every draw during its TFB pass, which was all non-indexed draws.

This probably didn't cost much on normal PCs, but it had a large impact on MacOS, which the macos1 release build avoided by mirroring index buffers (the PR currently does not). Needs buffer mirrors still for the rest of the performance.

There are additional cases where index buffers are bound or checked with non-indexed draws on the backend, but this one was straightforward to fix and has the largest impact. Testing is welcome to ensure nothing weird broke.

* Fix case with _rebind
2023-08-06 16:29:20 -03:00
sunshineinabox
5a0aa074b6 Enable VK_EXT_4444_formats (#5525) 2023-08-03 17:46:23 -03:00
dependabot[bot]
93aa40f1fb nuget: bump DiscordRichPresence from 1.1.3.18 to 1.2.1.24 (#5515)
Bumps [DiscordRichPresence](https://github.com/Lachee/discord-rpc-csharp) from 1.1.3.18 to 1.2.1.24.
- [Release notes](https://github.com/Lachee/discord-rpc-csharp/releases)
- [Commits](https://github.com/Lachee/discord-rpc-csharp/commits)

---
updated-dependencies:
- dependency-name: DiscordRichPresence
  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-08-02 17:19:38 +02:00
TSRBerry
bedee64af5 Add slightly better workaround for current workflow issues (#5507)
* checks: Add retry logic to dotnet format style step as well

I can't imagine dotnet format whitespace ever segfaulting,
so hopefully it won't be needed there.

* checks: Replace bash scripts with unstable-commands action

* build: Add unstable-commands action for test step
2023-08-01 00:15:37 +02:00
Domenico V
86931cc3f1 (Graphics.Shader): Handle EmitSuatom constant dests and EmitSuld zero dest reg. (#5504)
* (Graphics.Shader): Handle EmitSuatom constant dests.

* Proper fix for EmitSuatom; fix EmitSuld.
2023-07-30 22:31:57 -03:00
Domenico V
2be8b6ea45 CPU (A64): Add Fmaxp & Fminp Scalar Inst.s, Fast & Slow Paths; with Tests. (#5502)
* Add Fmaxp & Fminp Scalar Inst.s, Fast & Slow Paths; with Tests.

* Ptc.InternalVersion = 5502
2023-07-30 20:57:37 -03:00
gdkchan
f95b7c5877 Fix incorrect fragment origin when YNegate is enabled (#4673)
* Fix incorrect fragment origin when YNegate is enabled

* Shader cache version bump

* Do not update support buffer if shader does not read gl_FragCoord

* Pass unscaled viewport size to the support buffer
2023-07-29 18:47:03 -03:00
48 changed files with 494 additions and 96 deletions

View File

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

View File

@@ -3,10 +3,6 @@ name: Build job
on:
workflow_call:
concurrency:
group: pr-builds-${{ github.event.number }}
cancel-in-progress: true
env:
POWERSHELL_TELEMETRY_OPTOUT: 1
DOTNET_CLI_TELEMETRY_OPTOUT: 1
@@ -54,7 +50,11 @@ jobs:
run: dotnet build -c "${{ matrix.configuration }}" -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER
- name: Test
run: dotnet test --no-build -c "${{ matrix.configuration }}"
uses: TSRBerry/unstable-commands@v1
with:
commands: dotnet test --no-build -c "${{ matrix.configuration }}"
timeout-minutes: 10
retry-codes: 139
- name: Publish Ryujinx
run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx --self-contained true
@@ -141,4 +141,4 @@ jobs:
with:
name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-macos_universal
path: "publish_ava/*.tar.gz"
if: github.event_name == 'pull_request'
if: github.event_name == 'pull_request'

View File

@@ -40,23 +40,23 @@ jobs:
run: |
dotnet format whitespace --verify-no-changes --report ./whitespace-report.json -v d
# For some unknown reason this step sometimes fails with exit code 139 (segfault?),
# so in that case we'll try again (3 tries max).
- name: Run dotnet format style
run: |
dotnet format style --severity info --verify-no-changes --report ./style-report.json -v d
uses: TSRBerry/unstable-commands@v1
with:
commands: dotnet format style --severity info --verify-no-changes --report ./style-report.json -v d
timeout-minutes: 5
retry-codes: 139
# For some reason this step sometimes fails with exit code 139 (segfault?),
# so should that be the case we'll try again (3 tries max).
# For some unknown reason this step sometimes fails with exit code 139 (segfault?),
# so in that case we'll try again (3 tries max).
- name: Run dotnet format analyzers
run: |
attempt=0
exit_code=139
until [ $attempt -ge 3 ] || [ $exit_code -ne 139 ]; do
((attempt+=1))
exit_code=0
echo "Attempt: ${attempt}/3"
dotnet format analyzers --severity info --verify-no-changes --report ./analyzers-report.json -v d || exit_code=$?
done
exit $exit_code
uses: TSRBerry/unstable-commands@v1
with:
commands: dotnet format analyzers --severity info --verify-no-changes --report ./analyzers-report.json -v d
timeout-minutes: 5
retry-codes: 139
- name: Upload report
if: failure()

View File

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

View File

@@ -12,7 +12,7 @@
<PackageVersion Include="Avalonia.Svg.Skia" Version="0.10.18" />
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="Concentus" Version="1.1.7" />
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
<PackageVersion Include="DynamicData" Version="7.14.2" />
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />

View File

@@ -330,6 +330,7 @@ namespace ARMeilleure.Decoders
SetA64("011111100x110000110010xxxxxxxxxx", InstName.Fmaxnmp_S, InstEmit.Fmaxnmp_S, OpCodeSimd.Create);
SetA64("0>1011100<1xxxxx110001xxxxxxxxxx", InstName.Fmaxnmp_V, InstEmit.Fmaxnmp_V, OpCodeSimdReg.Create);
SetA64("0110111000110000110010xxxxxxxxxx", InstName.Fmaxnmv_V, InstEmit.Fmaxnmv_V, OpCodeSimd.Create);
SetA64("011111100x110000111110xxxxxxxxxx", InstName.Fmaxp_S, InstEmit.Fmaxp_S, OpCodeSimd.Create);
SetA64("0>1011100<1xxxxx111101xxxxxxxxxx", InstName.Fmaxp_V, InstEmit.Fmaxp_V, OpCodeSimdReg.Create);
SetA64("0110111000110000111110xxxxxxxxxx", InstName.Fmaxv_V, InstEmit.Fmaxv_V, OpCodeSimd.Create);
SetA64("000111100x1xxxxx010110xxxxxxxxxx", InstName.Fmin_S, InstEmit.Fmin_S, OpCodeSimdReg.Create);
@@ -339,6 +340,7 @@ namespace ARMeilleure.Decoders
SetA64("011111101x110000110010xxxxxxxxxx", InstName.Fminnmp_S, InstEmit.Fminnmp_S, OpCodeSimd.Create);
SetA64("0>1011101<1xxxxx110001xxxxxxxxxx", InstName.Fminnmp_V, InstEmit.Fminnmp_V, OpCodeSimdReg.Create);
SetA64("0110111010110000110010xxxxxxxxxx", InstName.Fminnmv_V, InstEmit.Fminnmv_V, OpCodeSimd.Create);
SetA64("011111101x110000111110xxxxxxxxxx", InstName.Fminp_S, InstEmit.Fminp_S, OpCodeSimd.Create);
SetA64("0>1011101<1xxxxx111101xxxxxxxxxx", InstName.Fminp_V, InstEmit.Fminp_V, OpCodeSimdReg.Create);
SetA64("0110111010110000111110xxxxxxxxxx", InstName.Fminv_V, InstEmit.Fminv_V, OpCodeSimd.Create);
SetA64("010111111xxxxxxx0001x0xxxxxxxxxx", InstName.Fmla_Se, InstEmit.Fmla_Se, OpCodeSimdRegElemF.Create);

View File

@@ -883,6 +883,31 @@ namespace ARMeilleure.Instructions
}
}
public static void Fmaxp_S(ArmEmitterContext context)
{
if (Optimizations.UseAdvSimd)
{
InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FmaxpS);
}
else if (Optimizations.FastFP && Optimizations.UseSse41)
{
EmitSse2ScalarPairwiseOpF(context, (op1, op2) =>
{
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
{
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: true);
}, scalar: true, op1, op2);
});
}
else
{
EmitScalarPairwiseOpF(context, (op1, op2) =>
{
return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMax), op1, op2);
});
}
}
public static void Fmaxp_V(ArmEmitterContext context)
{
if (Optimizations.UseAdvSimd)
@@ -1081,6 +1106,31 @@ namespace ARMeilleure.Instructions
}
}
public static void Fminp_S(ArmEmitterContext context)
{
if (Optimizations.UseAdvSimd)
{
InstEmitSimdHelperArm64.EmitScalarUnaryOpF(context, Intrinsic.Arm64FminpS);
}
else if (Optimizations.FastFP && Optimizations.UseSse41)
{
EmitSse2ScalarPairwiseOpF(context, (op1, op2) =>
{
return EmitSse41ProcessNaNsOpF(context, (op1, op2) =>
{
return EmitSse2VectorMaxMinOpF(context, op1, op2, isMax: false);
}, scalar: true, op1, op2);
});
}
else
{
EmitScalarPairwiseOpF(context, (op1, op2) =>
{
return EmitSoftFloatCall(context, nameof(SoftFloat32.FPMin), op1, op2);
});
}
}
public static void Fminp_V(ArmEmitterContext context)
{
if (Optimizations.UseAdvSimd)

View File

@@ -228,6 +228,7 @@ namespace ARMeilleure.Instructions
Fmaxnmp_S,
Fmaxnmp_V,
Fmaxnmv_V,
Fmaxp_S,
Fmaxp_V,
Fmaxv_V,
Fmin_S,
@@ -237,6 +238,7 @@ namespace ARMeilleure.Instructions
Fminnmp_S,
Fminnmp_V,
Fminnmv_V,
Fminp_S,
Fminp_V,
Fminv_V,
Fmla_Se,

View File

@@ -29,7 +29,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 5343; //! 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 BackupDir = "1";

View File

@@ -27,6 +27,26 @@ namespace ARMeilleure.Translation.PTC
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)]
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.IO;
using System.IO.Compression;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Timers;
using static ARMeilleure.Translation.PTC.PtcFormatter;
using Timer = System.Timers.Timer;
namespace ARMeilleure.Translation.PTC
{
@@ -20,7 +23,11 @@ namespace ARMeilleure.Translation.PTC
{
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.
@@ -28,7 +35,7 @@ namespace ARMeilleure.Translation.PTC
private readonly Ptc _ptc;
private readonly System.Timers.Timer _timer;
private readonly Timer _timer;
private readonly ulong _outerHeaderMagic;
@@ -51,7 +58,7 @@ namespace ARMeilleure.Translation.PTC
{
_ptc = ptc;
_timer = new System.Timers.Timer((double)SaveInterval * 1000d);
_timer = new Timer(SaveInterval * 1000d);
_timer.Elapsed += PreSave;
_outerHeaderMagic = BinaryPrimitives.ReadUInt64LittleEndian(EncodingCache.UTF8NoBOM.GetBytes(OuterHeaderMagicString).AsSpan());
@@ -168,7 +175,7 @@ namespace ARMeilleure.Translation.PTC
return false;
}
if (outerHeader.InfoFileVersion != InternalVersion)
if (outerHeader.InfoFileVersion != InternalVersion && !_migrateInternalVersions.Contains(outerHeader.InfoFileVersion))
{
InvalidateCompressedStream(compressedStream);
@@ -211,7 +218,19 @@ namespace ARMeilleure.Translation.PTC
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);
@@ -225,9 +244,14 @@ namespace ARMeilleure.Translation.PTC
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)
@@ -240,7 +264,7 @@ namespace ARMeilleure.Translation.PTC
compressedStream.SetLength(0L);
}
private void PreSave(object source, System.Timers.ElapsedEventArgs e)
private void PreSave(object source, ElapsedEventArgs e)
{
_waitEvent.Reset();
@@ -277,7 +301,7 @@ namespace ARMeilleure.Translation.PTC
{
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)
{
@@ -288,7 +312,7 @@ namespace ARMeilleure.Translation.PTC
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));
stream.Seek(0L, SeekOrigin.Begin);
@@ -332,7 +356,7 @@ namespace ARMeilleure.Translation.PTC
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*/)]

View File

@@ -186,6 +186,7 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.Graphics.AntiAliasing.Event += UpdateAntiAliasing;
ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter;
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel;
ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event += UpdateColorSpacePassthrough;
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState;
@@ -229,6 +230,11 @@ namespace Ryujinx.Ava
_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()
{
Dispatcher.UIThread.Post(() =>
@@ -461,6 +467,7 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.Graphics.ScalingFilter.Event -= UpdateScalingFilter;
ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel;
ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing;
ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Event -= UpdateColorSpacePassthrough;
_topLevel.PointerMoved -= 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?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value);
_renderer?.Window?.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value);
_renderer?.Window?.SetColorSpacePassthrough(ConfigurationState.Instance.Graphics.EnableColorSpacePassthrough.Value);
Width = (int)RendererHost.Bounds.Width;
Height = (int)RendererHost.Bounds.Height;

View File

@@ -620,6 +620,8 @@
"SettingsTabHotkeysVolumeDownHotkey": "Decrease Volume:",
"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.",
"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",
"UserProfilesManageSaves": "Manage Saves",
"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 EnableTextureRecompression { get; set; }
public bool EnableMacroHLE { get; set; }
public bool EnableColorSpacePassthrough { get; set; }
public bool EnableFileLog { get; set; }
public bool EnableStub { get; set; }
public bool EnableInfo { get; set; }
@@ -419,6 +420,7 @@ namespace Ryujinx.Ava.UI.ViewModels
EnableShaderCache = config.Graphics.EnableShaderCache;
EnableTextureRecompression = config.Graphics.EnableTextureRecompression;
EnableMacroHLE = config.Graphics.EnableMacroHLE;
EnableColorSpacePassthrough = config.Graphics.EnableColorSpacePassthrough;
ResolutionScale = config.Graphics.ResScale == -1 ? 4 : config.Graphics.ResScale - 1;
CustomResolutionScale = config.Graphics.ResScaleCustom;
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.EnableTextureRecompression.Value = EnableTextureRecompression;
config.Graphics.EnableMacroHLE.Value = EnableMacroHLE;
config.Graphics.EnableColorSpacePassthrough.Value = EnableColorSpacePassthrough;
config.Graphics.ResScale.Value = ResolutionScale == 4 ? -1 : ResolutionScale + 1;
config.Graphics.ResScaleCustom.Value = CustomResolutionScale;
config.Graphics.MaxAnisotropy.Value = MaxAnisotropy == 0 ? -1 : MathF.Pow(2, MaxAnisotropy);

View File

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

View File

@@ -13,5 +13,6 @@ namespace Ryujinx.Graphics.GAL
void SetAntiAliasing(AntiAliasing antialiasing);
void SetScalingFilter(ScalingFilter type);
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 SetScalingFilterLevel(float level) { }
public void SetColorSpacePassthrough(bool colorSpacePassthroughEnabled) { }
}
}

View File

@@ -342,5 +342,19 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
Signal();
}
}
/// <summary>
/// Sets the Y negate enabled state.
/// </summary>
/// <param name="enabled">True if Y negate of the fragment coordinates is enabled</param>
public void SetYNegateEnabled(bool enabled)
{
if (enabled != _graphics.YNegateEnabled)
{
_graphics.YNegateEnabled = enabled;
Signal();
}
}
}
}

View File

@@ -37,6 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
private ProgramPipelineState _pipeline;
private bool _fsReadsFragCoord;
private bool _vsUsesDrawParameters;
private bool _vtgWritesRtLayer;
private byte _vsClipDistancesWritten;
@@ -330,7 +331,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
UpdateShaderState();
}
_channel.BufferManager.CommitGraphicsBindings();
_channel.BufferManager.CommitGraphicsBindings(_drawState.DrawIndexed);
}
/// <summary>
@@ -692,12 +693,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
var face = _state.State.FaceState;
bool disableTransform = _state.State.ViewportTransformEnable == 0;
bool yNegate = yControl.HasFlag(YControl.NegateY);
UpdateFrontFace(yControl, face.FrontFace);
UpdateDepthMode();
bool flipY = yControl.HasFlag(YControl.NegateY);
Span<Viewport> viewports = stackalloc Viewport[Constants.TotalViewports];
for (int index = 0; index < Constants.TotalViewports; index++)
@@ -719,7 +719,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
float scaleX = MathF.Abs(transform.ScaleX);
float scaleY = transform.ScaleY;
if (flipY)
if (yNegate)
{
scaleY = -scaleY;
}
@@ -771,8 +771,17 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_channel.TextureManager.RenderTargetScale,
disableTransform);
// Viewport size is only used on the shader when YNegate is enabled,
// and if the fragment shader accesses gl_FragCoord,
// so there's no need to update it in other cases.
if (yNegate && _fsReadsFragCoord)
{
UpdateSupportBufferViewportSize();
}
_currentSpecState.SetViewportTransformDisable(disableTransform);
_currentSpecState.SetDepthMode(GetDepthMode() == DepthMode.MinusOneToOne);
_currentSpecState.SetYNegateEnabled(yNegate);
}
/// <summary>
@@ -1415,9 +1424,41 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
_currentProgramInfo[stageIndex] = info;
}
if (gs.Shaders[5]?.Info.UsesFragCoord == true)
{
// Make sure we update the viewport size on the support buffer if it will be consumed on the new shader.
if (!_fsReadsFragCoord && _state.State.YControl.HasFlag(YControl.NegateY))
{
UpdateSupportBufferViewportSize();
}
_fsReadsFragCoord = true;
}
else
{
_fsReadsFragCoord = false;
}
_context.Renderer.Pipeline.SetProgram(gs.HostProgram);
}
/// <summary>
/// Updates the viewport size on the support buffer for fragment shader access.
/// </summary>
private void UpdateSupportBufferViewportSize()
{
ref var transform = ref _state.State.ViewportTransform[0];
float scaleX = MathF.Abs(transform.ScaleX);
float scaleY = transform.ScaleY;
float width = scaleX * 2;
float height = scaleY * 2;
_context.SupportBufferUpdater.SetViewportSize(width, MathF.Abs(height));
}
/// <summary>
/// Updates bindings consumed by the shader on the texture and buffer managers.
/// </summary>

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.
/// </summary>
public static bool EnableTextureRecompression = false;
/// <summary>
/// Enables or disables color space passthrough, if available.
/// </summary>
public static bool EnableColorSpacePassthrough = false;
}
#pragma warning restore CA2211
}

View File

@@ -515,24 +515,32 @@ namespace Ryujinx.Graphics.Gpu.Memory
/// Ensures that the graphics engine bindings are visible to the host GPU.
/// Note: this actually performs the binding using the host graphics API.
/// </summary>
public void CommitGraphicsBindings()
/// <param name="indexed">True if the index buffer is in use</param>
public void CommitGraphicsBindings(bool indexed)
{
var bufferCache = _channel.MemoryManager.Physical.BufferCache;
if (_indexBufferDirty || _rebind)
if (indexed)
{
_indexBufferDirty = false;
if (_indexBuffer.Address != 0)
if (_indexBufferDirty || _rebind)
{
BufferRange buffer = bufferCache.GetBufferRange(_indexBuffer.Address, _indexBuffer.Size);
_indexBufferDirty = false;
_context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
if (_indexBuffer.Address != 0)
{
BufferRange buffer = bufferCache.GetBufferRange(_indexBuffer.Address, _indexBuffer.Size);
_context.Renderer.Pipeline.SetIndexBuffer(buffer, _indexBuffer.Type);
}
}
else if (_indexBuffer.Address != 0)
{
bufferCache.SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size);
}
}
else if (_indexBuffer.Address != 0)
else if (_rebind)
{
bufferCache.SynchronizeBufferRange(_indexBuffer.Address, _indexBuffer.Size);
_indexBufferDirty = true;
}
uint vbEnableMask = _vertexBuffersEnableMask;

View File

@@ -112,6 +112,17 @@ namespace Ryujinx.Graphics.Gpu.Memory
MarkDirty(SupportBuffer.ViewportInverseOffset, SupportBuffer.FieldSize);
}
/// <summary>
/// Updates the viewport size vector.
/// </summary>
/// <param name="data">Viewport size vector</param>
private void UpdateViewportSize(Vector4<float> data)
{
_data.ViewportSize = data;
MarkDirty(SupportBuffer.ViewportSizeOffset, SupportBuffer.FieldSize);
}
/// <summary>
/// Sets the scale of all output render targets (they should all have the same scale).
/// </summary>
@@ -192,6 +203,25 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
}
/// <summary>
/// Sets the viewport size, used to invert the fragment coordinates Y value.
/// </summary>
/// <param name="viewportWidth">Value used as viewport width</param>
/// <param name="viewportHeight">Value used as viewport height</param>
public void SetViewportSize(float viewportWidth, float viewportHeight)
{
if (_data.ViewportSize.X != viewportWidth || _data.ViewportSize.Y != viewportHeight)
{
UpdateViewportSize(new Vector4<float>
{
X = viewportWidth,
Y = viewportHeight,
Z = 1,
W = 0
});
}
}
/// <summary>
/// Submits all pending buffer updates to the GPU.
/// </summary>

View File

@@ -247,6 +247,12 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
return _oldSpecState.GraphicsState.ViewportTransformDisable;
}
/// <inheritdoc/>
public bool QueryYNegateEnabled()
{
return _oldSpecState.GraphicsState.YNegateEnabled;
}
/// <inheritdoc/>
public void RegisterTexture(int handle, int cbufSlot)
{

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 5266;
private const uint CodeGenVersion = 5529;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";
@@ -140,6 +140,11 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
/// </summary>
public ShaderStage Stage;
/// <summary>
/// Indicates if the fragment shader accesses the fragment coordinate built-in variable.
/// </summary>
public bool UsesFragCoord;
/// <summary>
/// Indicates if the shader accesses the Instance ID built-in variable.
/// </summary>
@@ -781,6 +786,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
ShaderIdentification.None,
0,
dataInfo.Stage,
dataInfo.UsesFragCoord,
dataInfo.UsesInstanceId,
dataInfo.UsesDrawParameters,
dataInfo.UsesRtLayer,
@@ -807,6 +813,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
TexturesCount = (ushort)info.Textures.Count,
ImagesCount = (ushort)info.Images.Count,
Stage = info.Stage,
UsesFragCoord = info.UsesFragCoord,
UsesInstanceId = info.UsesInstanceId,
UsesDrawParameters = info.UsesDrawParameters,
UsesRtLayer = info.UsesRtLayer,

View File

@@ -113,6 +113,13 @@ namespace Ryujinx.Graphics.Gpu.Shader
return _state.GraphicsState.AttributeTypes[location];
}
/// <inheritdoc/>
public bool QueryEarlyZForce()
{
_state.SpecializationState?.RecordEarlyZForce();
return _state.GraphicsState.EarlyZForce;
}
/// <inheritdoc/>
public AttributeType QueryFragmentOutputType(int location)
{
@@ -275,19 +282,18 @@ namespace Ryujinx.Graphics.Gpu.Shader
return _state.TransformFeedbackDescriptors[bufferIndex].Stride;
}
/// <inheritdoc/>
public bool QueryEarlyZForce()
{
_state.SpecializationState?.RecordEarlyZForce();
return _state.GraphicsState.EarlyZForce;
}
/// <inheritdoc/>
public bool QueryViewportTransformDisable()
{
return _state.GraphicsState.ViewportTransformDisable;
}
/// <inheritdoc/>
public bool QueryYNegateEnabled()
{
return _state.GraphicsState.YNegateEnabled;
}
/// <inheritdoc/>
public void RegisterTexture(int handle, int cbufSlot)
{

View File

@@ -97,6 +97,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// </summary>
public bool DualSourceBlendEnable;
/// <summary>
/// Indicates whether Y negate of the fragment coordinates is enabled.
/// </summary>
public bool YNegateEnabled;
/// <summary>
/// Creates a new GPU graphics state.
/// </summary>
@@ -116,7 +121,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
/// <param name="hasConstantBufferDrawParameters">Indicates that the draw is writing the base vertex, base instance and draw index to Constant Buffer 0</param>
/// <param name="hasUnalignedStorageBuffer">Indicates that any storage buffer use is unaligned</param>
/// <param name="fragmentOutputTypes">Type of the fragment shader outputs</param>
/// <param name="dualSourceBlendEnable">Type of the vertex attributes consumed by the shader</param>
/// <param name="dualSourceBlendEnable">Indicates whether dual source blend is enabled</param>
/// <param name="yNegateEnabled">Indicates whether Y negate of the fragment coordinates is enabled</param>
public GpuChannelGraphicsState(
bool earlyZForce,
PrimitiveTopology topology,
@@ -134,7 +140,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
bool hasConstantBufferDrawParameters,
bool hasUnalignedStorageBuffer,
ref Array8<AttributeType> fragmentOutputTypes,
bool dualSourceBlendEnable)
bool dualSourceBlendEnable,
bool yNegateEnabled)
{
EarlyZForce = earlyZForce;
Topology = topology;
@@ -153,6 +160,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
HasUnalignedStorageBuffer = hasUnalignedStorageBuffer;
FragmentOutputTypes = fragmentOutputTypes;
DualSourceBlendEnable = dualSourceBlendEnable;
YNegateEnabled = yNegateEnabled;
}
}
}

View File

@@ -540,6 +540,11 @@ namespace Ryujinx.Graphics.Gpu.Shader
return false;
}
if (graphicsState.YNegateEnabled != GraphicsState.YNegateEnabled)
{
return false;
}
return Matches(channel, ref poolState, checkTextures, isCompute: false);
}

View File

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

View File

@@ -188,10 +188,19 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl
context.AppendLine();
}
if (context.Config.Stage == ShaderStage.Fragment && context.Config.GpuAccessor.QueryEarlyZForce())
if (context.Config.Stage == ShaderStage.Fragment)
{
context.AppendLine("layout(early_fragment_tests) in;");
context.AppendLine();
if (context.Config.GpuAccessor.QueryEarlyZForce())
{
context.AppendLine("layout (early_fragment_tests) in;");
context.AppendLine();
}
if (context.Config.Properties.OriginUpperLeft)
{
context.AppendLine("layout (origin_upper_left) in vec4 gl_FragCoord;");
context.AppendLine();
}
}
if ((info.HelperFunctionsMask & HelperFunctionsMask.MultiplyHighS32) != 0)

View File

@@ -251,7 +251,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Spirv
}
else if (context.Config.Stage == ShaderStage.Fragment)
{
context.AddExecutionMode(spvFunc, context.Config.Options.TargetApi == TargetApi.Vulkan
context.AddExecutionMode(spvFunc, context.Config.Properties.OriginUpperLeft
? ExecutionMode.OriginUpperLeft
: ExecutionMode.OriginLowerLeft);

View File

@@ -178,6 +178,15 @@ namespace Ryujinx.Graphics.Shader
return 0;
}
/// <summary>
/// Queries if host state forces early depth testing.
/// </summary>
/// <returns>True if early depth testing is forced</returns>
bool QueryEarlyZForce()
{
return false;
}
/// <summary>
/// Queries whenever the current draw has written the base vertex and base instance into Constant Buffer 0.
/// </summary>
@@ -534,19 +543,19 @@ namespace Ryujinx.Graphics.Shader
}
/// <summary>
/// Queries if host state forces early depth testing.
/// Queries if host state disables the viewport transform.
/// </summary>
/// <returns>True if early depth testing is forced</returns>
bool QueryEarlyZForce()
/// <returns>True if the viewport transform is disabled</returns>
bool QueryViewportTransformDisable()
{
return false;
}
/// <summary>
/// Queries if host state disables the viewport transform.
/// Queries Y negate enable state.
/// </summary>
/// <returns>True if the viewport transform is disabled</returns>
bool QueryViewportTransformDisable()
/// <returns>True if Y negate of the fragment coordinates is enabled, false otherwise</returns>
bool QueryYNegateEnabled()
{
return false;
}

View File

@@ -161,6 +161,18 @@ namespace Ryujinx.Graphics.Shader.Instructions
// FragCoord X/Y must be divided by the render target scale, if resolution scaling is active,
// because the shader code is not expecting scaled values.
res = context.FPDivide(res, context.Load(StorageKind.ConstantBuffer, SupportBuffer.Binding, Const((int)SupportBufferField.RenderScale), Const(0)));
if (op.Imm10 == AttributeConsts.PositionY && context.Config.Options.TargetApi != TargetApi.OpenGL)
{
// If YNegate is enabled, we need to flip the fragment coordinates vertically, unless
// the API supports changing the origin (only OpenGL does).
if (context.Config.GpuAccessor.QueryYNegateEnabled())
{
Operand viewportHeight = context.Load(StorageKind.ConstantBuffer, 0, Const((int)SupportBufferField.ViewportSize), Const(1));
res = context.FPSubtract(viewportHeight, res);
}
}
}
else if (op.Imm10 == AttributeConsts.FrontFacing && context.Config.GpuAccessor.QueryHostHasFrontFacingBug())
{

View File

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

View File

@@ -218,7 +218,7 @@ namespace Ryujinx.Graphics.Shader.Instructions
return context.Copy(Register(srcB++, RegisterType.Gpr));
}
Operand d = dest != RegisterConsts.RegisterZeroIndex ? Register(dest, RegisterType.Gpr) : null;
Operand d = Register(dest, RegisterType.Gpr);
List<Operand> sourcesList = new();
@@ -304,6 +304,11 @@ namespace Ryujinx.Graphics.Shader.Instructions
bool byteAddress,
bool isBindless)
{
if (srcB == RegisterConsts.RegisterZeroIndex)
{
return;
}
context.Config.SetUsedFeature(FeatureFlags.IntegerSampling);
SamplerType type = ConvertSamplerType(dimensions);

View File

@@ -13,6 +13,7 @@ namespace Ryujinx.Graphics.Shader
public ShaderIdentification Identification { get; }
public int GpLayerInputAttribute { get; }
public ShaderStage Stage { get; }
public bool UsesFragCoord { get; }
public bool UsesInstanceId { get; }
public bool UsesDrawParameters { get; }
public bool UsesRtLayer { get; }
@@ -27,6 +28,7 @@ namespace Ryujinx.Graphics.Shader
ShaderIdentification identification,
int gpLayerInputAttribute,
ShaderStage stage,
bool usesFragCoord,
bool usesInstanceId,
bool usesDrawParameters,
bool usesRtLayer,
@@ -41,6 +43,7 @@ namespace Ryujinx.Graphics.Shader
Identification = identification;
GpLayerInputAttribute = gpLayerInputAttribute;
Stage = stage;
UsesFragCoord = usesFragCoord;
UsesInstanceId = usesInstanceId;
UsesDrawParameters = usesDrawParameters;
UsesRtLayer = usesRtLayer;

View File

@@ -18,6 +18,8 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
public IReadOnlyDictionary<int, MemoryDefinition> LocalMemories => _localMemories;
public IReadOnlyDictionary<int, MemoryDefinition> SharedMemories => _sharedMemories;
public readonly bool OriginUpperLeft;
public ShaderProperties()
{
_constantBuffers = new Dictionary<int, BufferDefinition>();
@@ -28,6 +30,11 @@ namespace Ryujinx.Graphics.Shader.StructuredIr
_sharedMemories = new Dictionary<int, MemoryDefinition>();
}
public ShaderProperties(bool originUpperLeft) : this()
{
OriginUpperLeft = originUpperLeft;
}
public void AddOrUpdateConstantBuffer(int binding, BufferDefinition definition)
{
_constantBuffers[binding] = definition;

View File

@@ -19,6 +19,7 @@ namespace Ryujinx.Graphics.Shader
FragmentAlphaTest,
FragmentIsBgra,
ViewportInverse,
ViewportSize,
FragmentRenderScaleCount,
RenderScale,
}
@@ -33,6 +34,7 @@ namespace Ryujinx.Graphics.Shader
public static readonly int FragmentAlphaTestOffset;
public static readonly int FragmentIsBgraOffset;
public static readonly int ViewportInverseOffset;
public static readonly int ViewportSizeOffset;
public static readonly int FragmentRenderScaleCountOffset;
public static readonly int GraphicsRenderScaleOffset;
public static readonly int ComputeRenderScaleOffset;
@@ -56,6 +58,7 @@ namespace Ryujinx.Graphics.Shader
FragmentAlphaTestOffset = OffsetOf(ref instance, ref instance.FragmentAlphaTest);
FragmentIsBgraOffset = OffsetOf(ref instance, ref instance.FragmentIsBgra);
ViewportInverseOffset = OffsetOf(ref instance, ref instance.ViewportInverse);
ViewportSizeOffset = OffsetOf(ref instance, ref instance.ViewportSize);
FragmentRenderScaleCountOffset = OffsetOf(ref instance, ref instance.FragmentRenderScaleCount);
GraphicsRenderScaleOffset = OffsetOf(ref instance, ref instance.RenderScale);
ComputeRenderScaleOffset = GraphicsRenderScaleOffset + FieldSize;
@@ -68,6 +71,7 @@ namespace Ryujinx.Graphics.Shader
new StructureField(AggregateType.U32, "s_alpha_test"),
new StructureField(AggregateType.Array | AggregateType.U32, "s_is_bgra", FragmentIsBgraCount),
new StructureField(AggregateType.Vector4 | AggregateType.FP32, "s_viewport_inverse"),
new StructureField(AggregateType.Vector4 | AggregateType.FP32, "s_viewport_size"),
new StructureField(AggregateType.S32, "s_frag_scale_count"),
new StructureField(AggregateType.Array | AggregateType.FP32, "s_render_scale", RenderScaleMaxCount),
});
@@ -76,6 +80,7 @@ namespace Ryujinx.Graphics.Shader
public Vector4<int> FragmentAlphaTest;
public Array8<Vector4<int>> FragmentIsBgra;
public Vector4<float> ViewportInverse;
public Vector4<float> ViewportSize;
public Vector4<int> FragmentRenderScaleCount;
// Render scale max count: 1 + 64 + 8. First scale is fragment output scale, others are textures/image inputs.

View File

@@ -304,11 +304,11 @@ namespace Ryujinx.Graphics.Shader.Translation
PrepareForVertexReturn();
}
public void PrepareForReturn()
public bool PrepareForReturn()
{
if (IsNonMain)
{
return;
return true;
}
if (Config.LastInVertexPipeline &&
@@ -383,13 +383,13 @@ namespace Ryujinx.Graphics.Shader.Translation
AlphaTestOp alphaTestOp = Config.GpuAccessor.QueryAlphaTestCompare();
if (alphaTestOp != AlphaTestOp.Always && (Config.OmapTargets & 8) != 0)
if (alphaTestOp != AlphaTestOp.Always)
{
if (alphaTestOp == AlphaTestOp.Never)
{
this.Discard();
}
else
else if ((Config.OmapTargets & 8) != 0)
{
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;
for (int rtIndex = 0; rtIndex < 8; rtIndex++)
@@ -462,6 +468,8 @@ namespace Ryujinx.Graphics.Shader.Translation
}
}
}
return true;
}
private void GenerateAlphaToCoverageDitherDiscard()

View File

@@ -429,6 +429,11 @@ namespace Ryujinx.Graphics.Shader.Translation
return context.Add(Instruction.FP32 | Instruction.SquareRoot, Local(), a);
}
public static Operand FPSubtract(this EmitterContext context, Operand a, Operand b, Instruction fpType = Instruction.FP32)
{
return context.Add(fpType | Instruction.Subtract, Local(), a, b);
}
public static Operand FPTruncate(this EmitterContext context, Operand a, Instruction fpType = Instruction.FP32)
{
return context.Add(fpType | Instruction.Truncate, Local(), a);

View File

@@ -123,7 +123,20 @@ namespace Ryujinx.Graphics.Shader.Translation
UsedInputAttributesPerPatch = new HashSet<int>();
UsedOutputAttributesPerPatch = new HashSet<int>();
ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties());
ShaderProperties properties;
switch (stage)
{
case ShaderStage.Fragment:
bool originUpperLeft = options.TargetApi == TargetApi.Vulkan || gpuAccessor.QueryYNegateEnabled();
properties = new ShaderProperties(originUpperLeft);
break;
default:
properties = new ShaderProperties();
break;
}
ResourceManager = new ResourceManager(stage, gpuAccessor, properties);
if (!gpuAccessor.QueryHostSupportsTransformFeedback() && gpuAccessor.QueryTransformFeedbackEnabled())
{
@@ -135,7 +148,7 @@ namespace Ryujinx.Graphics.Shader.Translation
BufferDefinition tfeInfoBuffer = new(BufferLayout.Std430, 1, Constants.TfeInfoBinding, "tfe_info", tfeInfoStruct);
Properties.AddOrUpdateStorageBuffer(Constants.TfeInfoBinding, tfeInfoBuffer);
properties.AddOrUpdateStorageBuffer(Constants.TfeInfoBinding, tfeInfoBuffer);
StructureType tfeDataStruct = new(new StructureField[]
{
@@ -146,7 +159,7 @@ namespace Ryujinx.Graphics.Shader.Translation
{
int binding = Constants.TfeBufferBaseBinding + i;
BufferDefinition tfeDataBuffer = new(BufferLayout.Std430, 1, binding, $"tfe_data{i}", tfeDataStruct);
Properties.AddOrUpdateStorageBuffer(binding, tfeDataBuffer);
properties.AddOrUpdateStorageBuffer(binding, tfeDataBuffer);
}
}
}
@@ -615,6 +628,7 @@ namespace Ryujinx.Graphics.Shader.Translation
identification,
GpLayerInputAttribute,
Stage,
UsedFeatures.HasFlag(FeatureFlags.FragCoordXY),
UsedFeatures.HasFlag(FeatureFlags.InstanceId),
UsedFeatures.HasFlag(FeatureFlags.DrawParameters),
UsedFeatures.HasFlag(FeatureFlags.RtLayer),

View File

@@ -42,6 +42,7 @@ namespace Ryujinx.Graphics.Vulkan
"VK_NV_viewport_array2",
"VK_EXT_depth_clip_control",
"VK_KHR_portability_subset", // As per spec, we should enable this if present.
"VK_EXT_4444_formats",
};
private static readonly string[] _requiredExtensions = {

View File

@@ -28,7 +28,7 @@ namespace Ryujinx.Graphics.Vulkan
private int _width;
private int _height;
private bool _vsyncEnabled;
private bool _vsyncModeChanged;
private bool _swapchainIsDirty;
private VkFormat _format;
private AntiAliasing _currentAntiAliasing;
private bool _updateEffect;
@@ -38,6 +38,7 @@ namespace Ryujinx.Graphics.Vulkan
private float _scalingFilterLevel;
private bool _updateScalingFilter;
private ScalingFilter _currentScalingFilter;
private bool _colorSpacePassthroughEnabled;
public unsafe Window(VulkanRenderer gd, SurfaceKHR surface, PhysicalDevice physicalDevice, Device device)
{
@@ -60,7 +61,7 @@ namespace Ryujinx.Graphics.Vulkan
private void RecreateSwapchain()
{
var oldSwapchain = _swapchain;
_vsyncModeChanged = false;
_swapchainIsDirty = false;
for (int i = 0; i < _swapchainImageViews.Length; i++)
{
@@ -106,7 +107,7 @@ namespace Ryujinx.Graphics.Vulkan
imageCount = capabilities.MaxImageCount;
}
var surfaceFormat = ChooseSwapSurfaceFormat(surfaceFormats);
var surfaceFormat = ChooseSwapSurfaceFormat(surfaceFormats, _colorSpacePassthroughEnabled);
var extent = ChooseSwapExtent(capabilities);
@@ -178,22 +179,40 @@ namespace Ryujinx.Graphics.Vulkan
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)
{
return new SurfaceFormatKHR(VkFormat.B8G8R8A8Unorm, ColorSpaceKHR.PaceSrgbNonlinearKhr);
}
foreach (var format in availableFormats)
var formatToReturn = availableFormats[0];
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;
}
}
}
return availableFormats[0];
else
{
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)
@@ -259,7 +278,7 @@ namespace Ryujinx.Graphics.Vulkan
if (acquireResult == Result.ErrorOutOfDateKhr ||
acquireResult == Result.SuboptimalKhr ||
_vsyncModeChanged)
_swapchainIsDirty)
{
RecreateSwapchain();
}
@@ -443,6 +462,12 @@ namespace Ryujinx.Graphics.Vulkan
_updateScalingFilter = true;
}
public override void SetColorSpacePassthrough(bool colorSpacePassthroughEnabled)
{
_colorSpacePassthroughEnabled = colorSpacePassthroughEnabled;
_swapchainIsDirty = true;
}
private void UpdateEffect()
{
if (_updateEffect)
@@ -559,7 +584,7 @@ namespace Ryujinx.Graphics.Vulkan
public override void ChangeVSyncMode(bool vsyncEnabled)
{
_vsyncEnabled = vsyncEnabled;
_vsyncModeChanged = true;
_swapchainIsDirty = true;
}
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 SetScalingFilter(ScalingFilter scalerType);
public abstract void SetScalingFilterLevel(float scale);
public abstract void SetColorSpacePassthrough(bool colorSpacePassthroughEnabled);
}
}

View File

@@ -89,9 +89,6 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
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 = "";
if (!isHomebrew && programId > 0x010000000000FFFF)
@@ -119,7 +116,7 @@ namespace Ryujinx.HLE.Loaders.Processes.Extensions
metaLoader,
nacpData,
enablePtc,
allowCodeMemoryForJit,
true,
programName,
metaLoader.GetProgramId(),
null,

View File

@@ -28,6 +28,11 @@ namespace Ryujinx.HLE.Loaders.Processes
{
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)
{
ulong applicationId = 0;
@@ -242,7 +247,7 @@ namespace Ryujinx.HLE.Loaders.Processes
ulong argsStart = 0;
uint argsSize = 0;
ulong codeStart = (meta.Flags & 1) != 0 ? 0x8000000UL : 0x200000UL;
ulong codeStart = ((meta.Flags & 1) != 0 ? 0x8000000UL : 0x200000UL) + CodeStartOffset;
uint codeSize = 0;
var buildIds = executables.Select(e => (e switch

View File

@@ -764,7 +764,9 @@ namespace Ryujinx.Tests.Cpu
{
0x7E30D820u, // FADDP S0, V1.2S
0x7E30C820u, // FMAXNMP S0, V1.2S
0x7E30F820u, // FMAXP S0, V1.2S
0x7EB0C820u, // FMINNMP S0, V1.2S
0x7EB0F820u, // FMINP S0, V1.2S
};
}
@@ -774,7 +776,9 @@ namespace Ryujinx.Tests.Cpu
{
0x7E70D820u, // FADDP D0, V1.2D
0x7E70C820u, // FMAXNMP D0, V1.2D
0x7E70F820u, // FMAXP D0, V1.2D
0x7EF0C820u, // FMINNMP D0, V1.2D
0x7EF0F820u, // FMINP D0, V1.2D
};
}

View File

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

View File

@@ -485,6 +485,11 @@ namespace Ryujinx.Ui.Common.Configuration
/// </summary>
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>
/// Graphics backend
/// </summary>
@@ -535,6 +540,8 @@ namespace Ryujinx.Ui.Common.Configuration
PreferredGpu.Event += static (sender, e) => LogValueChange(e, nameof(PreferredGpu));
EnableMacroHLE = new ReactiveObject<bool>();
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.Event += static (sender, e) => LogValueChange(e, nameof(AntiAliasing));
ScalingFilter = new ReactiveObject<ScalingFilter>();
@@ -667,6 +674,7 @@ namespace Ryujinx.Ui.Common.Configuration
EnableShaderCache = Graphics.EnableShaderCache,
EnableTextureRecompression = Graphics.EnableTextureRecompression,
EnableMacroHLE = Graphics.EnableMacroHLE,
EnableColorSpacePassthrough = Graphics.EnableColorSpacePassthrough,
EnablePtc = System.EnablePtc,
EnableInternetAccess = System.EnableInternetAccess,
EnableFsIntegrityChecks = System.EnableFsIntegrityChecks,
@@ -772,6 +780,7 @@ namespace Ryujinx.Ui.Common.Configuration
Graphics.EnableShaderCache.Value = true;
Graphics.EnableTextureRecompression.Value = false;
Graphics.EnableMacroHLE.Value = true;
Graphics.EnableColorSpacePassthrough.Value = false;
Graphics.AntiAliasing.Value = AntiAliasing.None;
Graphics.ScalingFilter.Value = ScalingFilter.Bilinear;
Graphics.ScalingFilterLevel.Value = 80;
@@ -1391,6 +1400,15 @@ namespace Ryujinx.Ui.Common.Configuration
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;
Graphics.ResScale.Value = configurationFileFormat.ResScale;
Graphics.ResScaleCustom.Value = configurationFileFormat.ResScaleCustom;
@@ -1426,6 +1444,7 @@ namespace Ryujinx.Ui.Common.Configuration
Graphics.EnableShaderCache.Value = configurationFileFormat.EnableShaderCache;
Graphics.EnableTextureRecompression.Value = configurationFileFormat.EnableTextureRecompression;
Graphics.EnableMacroHLE.Value = configurationFileFormat.EnableMacroHLE;
Graphics.EnableColorSpacePassthrough.Value = configurationFileFormat.EnableColorSpacePassthrough;
System.EnablePtc.Value = configurationFileFormat.EnablePtc;
System.EnableInternetAccess.Value = configurationFileFormat.EnableInternetAccess;
System.EnableFsIntegrityChecks.Value = configurationFileFormat.EnableFsIntegrityChecks;