From 95bad6995c110a662c8059e4f8035426303c0840 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Thu, 11 May 2023 17:41:34 +0100 Subject: [PATCH 01/21] GPU: Fix shader cache assuming past shader data was mapped (#4885) This fixes a potential issue where a shader lookup could match the address of a previous _different_ shader, but that shader is now partially unmapped. This would just crash with an invalid region exception. To compare a shader in the address cache with one in memory, we get the memory at the location with the previous shader's size. However, it's possible it has been unmapped and then remapped with a smaller size. In this case, we should just get back the mapped portion of the shader, which will then fail the comparison immediately and get to compile/lookup for the new one. This might fix a random crash in TOTK that was reported by Piplup. I don't know if it does, because I don't have the game yet. --- src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index e1ab93278c54..d543f42ad81c 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -591,7 +591,7 @@ private static bool IsShaderEqual(MemoryManager memoryManager, CachedShaderStage return true; } - ReadOnlySpan memoryCode = memoryManager.GetSpan(gpuVa, shader.Code.Length); + ReadOnlySpan memoryCode = memoryManager.GetSpanMapped(gpuVa, shader.Code.Length); return memoryCode.SequenceEqual(shader.Code); } From 42f7f986661ef7ba65996e56d8448cb8f8985515 Mon Sep 17 00:00:00 2001 From: 2435043xia <363032601@qq.com> Date: Fri, 12 May 2023 01:10:24 +0800 Subject: [PATCH 02/21] =?UTF-8?q?Fix=20the=20issue=20of=20unequal=20check?= =?UTF-8?q?=20for=20amiibo=20file=20date=20due=20to=20the=20lack=20o?= =?UTF-8?q?=E2=80=A6=20(#4832)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix the issue of unequal check for amiibo file date due to the lack of sub-second units in the header, causing slow opening of the amiibo interface. * Supplement the unrepaired. --- src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs | 2 +- src/Ryujinx/Ui/Windows/AmiiboWindow.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs index bb92798fae7e..ead1a1442451 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/AmiiboWindowViewModel.cs @@ -370,7 +370,7 @@ private async Task NeedsUpdate(DateTime oldLastModified) if (response.IsSuccessStatusCode) { - return response.Content.Headers.LastModified != oldLastModified; + return response.Content.Headers.LastModified != new DateTimeOffset(oldLastModified.Ticks - (oldLastModified.Ticks % TimeSpan.TicksPerSecond), TimeSpan.Zero); } return false; diff --git a/src/Ryujinx/Ui/Windows/AmiiboWindow.cs b/src/Ryujinx/Ui/Windows/AmiiboWindow.cs index 11a566d8d264..9cfb29427c03 100644 --- a/src/Ryujinx/Ui/Windows/AmiiboWindow.cs +++ b/src/Ryujinx/Ui/Windows/AmiiboWindow.cs @@ -180,7 +180,7 @@ private async Task NeedsUpdate(DateTime oldLastModified) if (response.IsSuccessStatusCode) { - return response.Content.Headers.LastModified != oldLastModified; + return response.Content.Headers.LastModified != new DateTimeOffset(oldLastModified.Ticks - (oldLastModified.Ticks % TimeSpan.TicksPerSecond), TimeSpan.Zero); } return false; From ec0bb74968de92230dd026faa35e5f8bd975ec35 Mon Sep 17 00:00:00 2001 From: John Date: Thu, 11 May 2023 11:13:01 -0700 Subject: [PATCH 03/21] Stop SDL from inhibiting sleep. (#4842) --- src/Ryujinx.SDL2.Common/SDL2Driver.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ryujinx.SDL2.Common/SDL2Driver.cs b/src/Ryujinx.SDL2.Common/SDL2Driver.cs index 970e287dec56..c41c6269d1ea 100644 --- a/src/Ryujinx.SDL2.Common/SDL2Driver.cs +++ b/src/Ryujinx.SDL2.Common/SDL2Driver.cs @@ -63,6 +63,7 @@ public void Initialize() SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_SWITCH_HOME_LED, "0"); SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, "1"); + SDL_SetHint(SDL_HINT_VIDEO_ALLOW_SCREENSAVER, "1"); // NOTE: As of SDL2 2.24.0, joycons are combined by default but the motion source only come from one of them. From 40d47b7aa235b464974480d09875eef0377bb261 Mon Sep 17 00:00:00 2001 From: Mary Date: Thu, 11 May 2023 20:14:02 +0200 Subject: [PATCH 04/21] amadeus: Fix wrong channel mapping check and an old typo (#4888) * amadeus: Fix wrong channel mapping check This was always going to happens, as a result quadratic would break and move index after the channel count point, effectively breaking input/output indices. * amadeus: Fix reverb 3d early delay wrong output index --- src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs | 4 ++-- src/Ryujinx.Audio/Renderer/Dsp/Command/Reverb3dCommand.cs | 6 +++--- src/Ryujinx.Audio/Renderer/Dsp/Command/ReverbCommand.cs | 4 ++-- src/Ryujinx.Audio/Renderer/Dsp/DataSourceHelper.cs | 8 ++++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs index cb5678c7b3dc..6dc76659401e 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/DelayCommand.cs @@ -49,8 +49,8 @@ public DelayCommand(uint bufferOffset, DelayParameter parameter, Memory output, ReadOnlySpan input, int sample } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void RemapLegacyChannelEffectMappingToChannelResourceMapping(bool isSupported, Span bufferIndices) + public static void RemapLegacyChannelEffectMappingToChannelResourceMapping(bool isSupported, Span bufferIndices, uint channelCount) { - if (!isSupported && bufferIndices.Length == 6) + if (!isSupported && channelCount == 6) { ushort backLeft = bufferIndices[2]; ushort backRight = bufferIndices[3]; @@ -447,9 +447,9 @@ public static void RemapLegacyChannelEffectMappingToChannelResourceMapping(bool } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void RemapChannelResourceMappingToLegacy(bool isSupported, Span bufferIndices) + public static void RemapChannelResourceMappingToLegacy(bool isSupported, Span bufferIndices, uint channelCount) { - if (isSupported && bufferIndices.Length == 6) + if (isSupported && channelCount == 6) { ushort frontCenter = bufferIndices[2]; ushort lowFrequency = bufferIndices[3]; From 0ed40c71754e194c42a5a2758c0db1a5fc1de50b Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 11 May 2023 15:47:55 -0300 Subject: [PATCH 05/21] Fix incorrect ASTC endpoint color when using LuminanceDelta mode (#4890) --- src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs b/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs index 08738583efce..80683d17b231 100644 --- a/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs +++ b/src/Ryujinx.Graphics.Texture/Astc/AstcDecoder.cs @@ -956,7 +956,7 @@ static Span ReadIntColorValues(int number, Span colorValues, ref int c { Span val = ReadUintColorValues(2, colorValues, ref colorValuesPosition); int l0 = (int)((val[0] >> 2) | (val[1] & 0xC0)); - int l1 = (int)Math.Max(l0 + (val[1] & 0x3F), 0xFFU); + int l1 = (int)Math.Min(l0 + (val[1] & 0x3F), 0xFFU); endPoints[0] = new AstcPixel(0xFF, (short)l0, (short)l0, (short)l0); endPoints[1] = new AstcPixel(0xFF, (short)l1, (short)l1, (short)l1); From 920507759052b1a3a04c598d46c9b20ce0988d99 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 11 May 2023 16:35:36 -0300 Subject: [PATCH 06/21] Enable explicit LOD for array textures with depth compare on SPIR-V (#4892) --- src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs index b6ffdb7ac5ca..5521fa5c93cf 100644 --- a/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs +++ b/src/Ryujinx.Graphics.Shader/CodeGen/Spirv/Instructions.cs @@ -1442,14 +1442,6 @@ private static OperationResult GenerateTextureSample(CodeGenContext context, Ast return GetZeroOperationResult(context, texOp, AggregateType.FP32, colorIsVector); } - // This combination is valid, but not available on GLSL. - // For now, ignore the LOD level and do a normal sample. - // TODO: How to implement it properly? - if (hasLodLevel && isArray && isShadow) - { - hasLodLevel = false; - } - int srcIndex = isBindless ? 1 : 0; SpvInstruction Src(AggregateType type) From aa784c3e5e900e376e0f5ccea87458905108485e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 May 2023 21:42:46 +0200 Subject: [PATCH 07/21] nuget: bump System.IdentityModel.Tokens.Jwt from 6.30.0 to 6.30.1 (#4886) Bumps [System.IdentityModel.Tokens.Jwt](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet) from 6.30.0 to 6.30.1. - [Release notes](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/releases) - [Changelog](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/blob/dev/CHANGELOG.md) - [Commits](https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/compare/6.30.0...6.30.1) --- updated-dependencies: - dependency-name: System.IdentityModel.Tokens.Jwt dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 4759c9ba57c7..f123ab7f0634 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -44,7 +44,7 @@ - + From e0544dd9c74e86cca6ee37c4d521d2d07f9ccdcc Mon Sep 17 00:00:00 2001 From: Nico Date: Thu, 11 May 2023 23:10:57 +0200 Subject: [PATCH 08/21] UI: Adjust input mapping view (#4866) * refactor: clean up controller settings ui - Remove inconsistencies between left and right side - Use style to set ToggleButton properties (since they are all the same) - Move topmost controller settings from one line to 2x2 grid for improved clarity - Properly adjust borders, text widths, etc. to neighboring elements to eliminate misaligned visual lines * fix: merge issues * fix: prevent sliders from jumping by giving text block fixed width * refactor: add more separators and increase margin * refactor: center deadzone and range descriptions * refactor: move rumble border top margin to -1 and prevent double border * refactor: remove margins & double borders + switch profile & input selection * style: apply suggestions from code review Co-authored-by: Ac_K --------- Co-authored-by: Ac_K --- .../UI/Windows/ControllerSettingsWindow.axaml | 657 ++++++++---------- 1 file changed, 305 insertions(+), 352 deletions(-) diff --git a/src/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml b/src/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml index 8a4d22ffa4ff..1a11a8ff3005 100644 --- a/src/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml +++ b/src/Ryujinx.Ava/UI/Windows/ControllerSettingsWindow.axaml @@ -21,35 +21,54 @@ + + + + - + - - - + + + + + + + - + + HorizontalAlignment="Stretch" + VerticalAlignment="Center"> + + + + @@ -59,69 +78,147 @@ - + - - + - + + VerticalAlignment="Center"> + + + + + + + - - - - - - - + - - + ToolTip.Tip="{locale:Locale ControllerSettingsSaveProfileToolTip}" + Command="{Binding SaveProfile}"> + + + + + + + Padding="2"> + + + + + + + + + + + + + + - - - - + + + + @@ -133,64 +230,6 @@ - - - - - - - - - - - @@ -206,7 +245,7 @@ @@ -221,7 +260,8 @@ Grid.Row="0" BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderThickness="1" - IsVisible="{Binding IsLeft}"> + IsVisible="{Binding IsLeft}" + MinHeight="90"> @@ -232,7 +272,6 @@ - + @@ -263,19 +299,15 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsTriggerL}" TextAlignment="Center" /> - + - + @@ -301,7 +330,8 @@ Grid.Row="1" BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderThickness="1" - IsVisible="{Binding IsLeft}"> + IsVisible="{Binding IsLeft}" + Margin="0,5,0,0"> - + @@ -339,10 +366,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsLStickUp}" TextAlignment="Center" /> - + @@ -358,10 +382,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsLStickDown}" TextAlignment="Center" /> - + @@ -377,10 +398,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsLStickLeft}" TextAlignment="Center" /> - + @@ -396,10 +414,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsLStickRight}" TextAlignment="Center" /> - + @@ -419,10 +434,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsLStickButton}" TextAlignment="Center" /> - + @@ -438,16 +450,13 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsLStickStick}" TextAlignment="Center" /> - + + @@ -457,9 +466,11 @@ - + - + - + @@ -502,7 +517,8 @@ BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderThickness="1" VerticalAlignment="Top" - IsVisible="{Binding IsLeft}"> + IsVisible="{Binding IsLeft}" + Margin="0,5,0,0"> - + - + BorderThickness="1" MinHeight="90"> + - + - + @@ -641,10 +648,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsLeftSL}" TextAlignment="Center" /> - + @@ -663,10 +667,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsRightSR}" TextAlignment="Center" /> - + @@ -685,10 +686,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsRightSL}" TextAlignment="Center" /> - + @@ -702,14 +700,15 @@ Margin="0,10,0,0" MaxHeight="250" HorizontalAlignment="Stretch" - VerticalAlignment="Top" + VerticalAlignment="Stretch" Source="{Binding Image}" /> - + @@ -733,7 +732,8 @@ BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderThickness="1" HorizontalAlignment="Stretch" - IsVisible="{Binding IsController}"> + IsVisible="{Binding IsController}" + Margin="0,-1,0,0"> @@ -756,7 +756,7 @@ @@ -770,192 +770,159 @@ Grid.Row="0" BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderThickness="1" - IsVisible="{Binding IsRight}"> - - - - - - - - - - - + IsVisible="{Binding IsRight}" + MinHeight="90"> + + + + + + + + + + + + - - - - - + + + + Text="{locale:Locale ControllerSettingsTriggerR}" + TextAlignment="Center" /> + - - - - - + + + + Text="{locale:Locale ControllerSettingsButtonPlus}" + TextAlignment="Center" /> + - - - - - - + + + + IsVisible="{Binding IsRight}" + Margin="0,5,0,0"> - - - - - - - - - - + + - + - + - + - + - + - + - + - + + IsVisible="{Binding IsRight}" + Margin="0,5,0,0"> - + @@ -1001,10 +966,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsRStickUp}" TextAlignment="Center" /> - + @@ -1020,10 +982,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsRStickDown}" TextAlignment="Center" /> - + @@ -1039,10 +998,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsRStickLeft}" TextAlignment="Center" /> - + @@ -1058,10 +1014,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsRStickRight}" TextAlignment="Center" /> - + @@ -1081,10 +1034,7 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsRStickButton}" TextAlignment="Center" /> - + @@ -1101,16 +1051,13 @@ VerticalAlignment="Center" Text="{locale:Locale ControllerSettingsRStickStick}" TextAlignment="Center" /> - + + @@ -1120,9 +1067,11 @@ - + - + - + From 5cbdfbc7a4b7413a4f633c77190a79bfc6520e98 Mon Sep 17 00:00:00 2001 From: Mary Date: Fri, 12 May 2023 00:19:19 +0200 Subject: [PATCH 09/21] amadeus: Allow 5.1 sink output (#4894) * amadeus: Allow 5.1 sink output Also add a simple Stereo to 5.1 change for device sink. Tested against NES - Nintendo Switch Online that output stereo on the audio renderer. * Remove outdated comment --- src/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs | 4 +--- .../Renderer/Dsp/Command/DeviceSinkCommand.cs | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs b/src/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs index 7bd0443c2e74..899c2ef97d42 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/AudioProcessor.cs @@ -65,9 +65,7 @@ public void Start(IHardwareDeviceDriver deviceDriver, float volume) { OutputDevices = new IHardwareDevice[Constants.AudioRendererSessionCountMax]; - // TODO: Before enabling this, we need up-mixing from stereo to 5.1. - // uint channelCount = GetHardwareChannelCount(deviceDriver); - uint channelCount = 2; + uint channelCount = GetHardwareChannelCount(deviceDriver); for (int i = 0; i < OutputDevices.Length; i++) { diff --git a/src/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs b/src/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs index 9c88a4e7f43f..27bb34bf3eff 100644 --- a/src/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs +++ b/src/Ryujinx.Audio/Renderer/Dsp/Command/DeviceSinkCommand.cs @@ -67,7 +67,19 @@ public void Process(CommandList context) const int sampleCount = Constants.TargetSampleCount; - short[] outputBuffer = new short[bufferCount * sampleCount]; + uint inputCount; + + // In case of upmixing to 5.1, we allocate the right amount. + if (bufferCount != channelCount && channelCount == 6) + { + inputCount = (uint)channelCount; + } + else + { + inputCount = bufferCount; + } + + short[] outputBuffer = new short[inputCount * sampleCount]; for (int i = 0; i < bufferCount; i++) { @@ -79,7 +91,7 @@ public void Process(CommandList context) } } - device.AppendBuffer(outputBuffer, InputCount); + device.AppendBuffer(outputBuffer, inputCount); } else { From 531da8a1c0760c8ebf121ac83ba4c840ead9e443 Mon Sep 17 00:00:00 2001 From: SamusAranX Date: Fri, 12 May 2023 01:56:37 +0200 Subject: [PATCH 10/21] Changed LastPlayed field from string to nullable DateTime (#4861) * Changed LastPlayed field from string to nullable DateTime Added ApplicationData.LastPlayedString property Added NullableDateTimeConverter for the DateTime->string conversion in Avalonia * Added migration from string-based last_played to DateTime-based last_played_utc * Updated comment style * Added MarkupExtension to NullableDateTimeConverter and changed its usage Cleaned up leftover usings * Missed one comment --- src/Ryujinx.Ava/AppHost.cs | 2 +- .../UI/Controls/ApplicationListView.axaml | 2 +- .../UI/Helpers/NullableDateTimeConverter.cs | 38 ++++++++++++++++ .../Models/Generic/LastPlayedSortComparer.cs | 15 +++---- .../UI/ViewModels/MainWindowViewModel.cs | 5 +-- src/Ryujinx.Ui.Common/App/ApplicationData.cs | 45 +++++++++++++------ .../App/ApplicationLibrary.cs | 23 ++++++---- .../App/ApplicationMetadata.cs | 13 +++++- src/Ryujinx/Ui/MainWindow.cs | 13 +++--- 9 files changed, 113 insertions(+), 43 deletions(-) create mode 100644 src/Ryujinx.Ava/UI/Helpers/NullableDateTimeConverter.cs diff --git a/src/Ryujinx.Ava/AppHost.cs b/src/Ryujinx.Ava/AppHost.cs index 0955fb270fbb..795c3f7a7036 100644 --- a/src/Ryujinx.Ava/AppHost.cs +++ b/src/Ryujinx.Ava/AppHost.cs @@ -671,7 +671,7 @@ public async Task LoadGuestApplication() _viewModel.ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText, appMetadata => { - appMetadata.LastPlayed = DateTime.UtcNow.ToString(); + appMetadata.LastPlayed = DateTime.UtcNow; }); return true; diff --git a/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml b/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml index fa8ebf627b76..227b4723bd48 100644 --- a/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml +++ b/src/Ryujinx.Ava/UI/Controls/ApplicationListView.axaml @@ -129,7 +129,7 @@ TextWrapping="Wrap" /> public int Compare(ApplicationData x, ApplicationData y) { - string aValue = x.LastPlayed; - string bValue = y.LastPlayed; + var aValue = x.LastPlayed; + var bValue = y.LastPlayed; - if (aValue == LocaleManager.Instance[LocaleKeys.Never]) + if (!aValue.HasValue) { - aValue = DateTime.UnixEpoch.ToString(); + aValue = DateTime.UnixEpoch; } - if (bValue == LocaleManager.Instance[LocaleKeys.Never]) + if (!bValue.HasValue) { - bValue = DateTime.UnixEpoch.ToString(); + bValue = DateTime.UnixEpoch; } - return (IsAscending ? 1 : -1) * DateTime.Compare(DateTime.Parse(bValue), DateTime.Parse(aValue)); + return (IsAscending ? 1 : -1) * DateTime.Compare(bValue.Value, aValue.Value); } } } \ No newline at end of file diff --git a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs index 4db78afeb04e..f8dd414358f5 100644 --- a/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx.Ava/UI/ViewModels/MainWindowViewModel.cs @@ -1524,10 +1524,9 @@ public void UpdateGameMetadata(string titleId) { ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => { - if (DateTime.TryParse(appMetadata.LastPlayed, out DateTime lastPlayedDateTime)) + if (appMetadata.LastPlayed.HasValue) { - double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds; - + double sessionTimePlayed = DateTime.UtcNow.Subtract(appMetadata.LastPlayed.Value).TotalSeconds; appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero); } }); diff --git a/src/Ryujinx.Ui.Common/App/ApplicationData.cs b/src/Ryujinx.Ui.Common/App/ApplicationData.cs index d9d3cf68537e..f0aa40be260d 100644 --- a/src/Ryujinx.Ui.Common/App/ApplicationData.cs +++ b/src/Ryujinx.Ui.Common/App/ApplicationData.cs @@ -10,27 +10,44 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.FileSystem; using System; +using System.Globalization; using System.IO; +using System.Text.Json.Serialization; namespace Ryujinx.Ui.App.Common { public class ApplicationData { - public bool Favorite { get; set; } - public byte[] Icon { get; set; } - public string TitleName { get; set; } - public string TitleId { get; set; } - public string Developer { get; set; } - public string Version { get; set; } - public string TimePlayed { get; set; } - public double TimePlayedNum { get; set; } - public string LastPlayed { get; set; } - public string FileExtension { get; set; } - public string FileSize { get; set; } - public double FileSizeBytes { get; set; } - public string Path { get; set; } + public bool Favorite { get; set; } + public byte[] Icon { get; set; } + public string TitleName { get; set; } + public string TitleId { get; set; } + public string Developer { get; set; } + public string Version { get; set; } + public string TimePlayed { get; set; } + public double TimePlayedNum { get; set; } + public DateTime? LastPlayed { get; set; } + public string FileExtension { get; set; } + public string FileSize { get; set; } + public double FileSizeBytes { get; set; } + public string Path { get; set; } public BlitStruct ControlHolder { get; set; } - + + [JsonIgnore] + public string LastPlayedString + { + get + { + if (!LastPlayed.HasValue) + { + // TODO: maybe put localized string here instead of just "Never" + return "Never"; + } + + return LastPlayed.Value.ToLocalTime().ToString(CultureInfo.CurrentCulture); + } + } + public static string GetApplicationBuildId(VirtualFileSystem virtualFileSystem, string titleFilePath) { using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read); diff --git a/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs b/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs index b7b57f1a2932..0407036a026c 100644 --- a/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs +++ b/src/Ryujinx.Ui.Common/App/ApplicationLibrary.cs @@ -414,21 +414,28 @@ byte[] Read(long position, int size) ApplicationMetadata appMetadata = LoadAndSaveMetaData(titleId, appMetadata => { appMetadata.Title = titleName; - }); - if (appMetadata.LastPlayed != "Never") - { - if (!DateTime.TryParse(appMetadata.LastPlayed, out _)) + if (appMetadata.LastPlayedOld == default || appMetadata.LastPlayed.HasValue) { - Logger.Warning?.Print(LogClass.Application, $"Last played datetime \"{appMetadata.LastPlayed}\" is invalid for current system culture, skipping (did current culture change?)"); + // Don't do the migration if last_played doesn't exist or last_played_utc already has a value. + return; + } - appMetadata.LastPlayed = "Never"; + // Migrate from string-based last_played to DateTime-based last_played_utc. + if (DateTime.TryParse(appMetadata.LastPlayedOld, out DateTime lastPlayedOldParsed)) + { + Logger.Info?.Print(LogClass.Application, $"last_played found: \"{appMetadata.LastPlayedOld}\", migrating to last_played_utc"); + appMetadata.LastPlayed = lastPlayedOldParsed; + + // Migration successful: deleting last_played from the metadata file. + appMetadata.LastPlayedOld = default; } else { - appMetadata.LastPlayed = appMetadata.LastPlayed[..^3]; + // Migration failed: emitting warning but leaving the unparsable value in the metadata file so the user can fix it. + Logger.Warning?.Print(LogClass.Application, $"Last played string \"{appMetadata.LastPlayedOld}\" is invalid for current system culture, skipping (did current culture change?)"); } - } + }); ApplicationData data = new() { diff --git a/src/Ryujinx.Ui.Common/App/ApplicationMetadata.cs b/src/Ryujinx.Ui.Common/App/ApplicationMetadata.cs index e19f7483fc4e..0abd4680daa6 100644 --- a/src/Ryujinx.Ui.Common/App/ApplicationMetadata.cs +++ b/src/Ryujinx.Ui.Common/App/ApplicationMetadata.cs @@ -1,10 +1,19 @@ -namespace Ryujinx.Ui.App.Common +using System; +using System.Text.Json.Serialization; + +namespace Ryujinx.Ui.App.Common { public class ApplicationMetadata { public string Title { get; set; } public bool Favorite { get; set; } public double TimePlayed { get; set; } - public string LastPlayed { get; set; } = "Never"; + + [JsonPropertyName("last_played_utc")] + public DateTime? LastPlayed { get; set; } = null; + + [JsonPropertyName("last_played")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + public string LastPlayedOld { get; set; } } } \ No newline at end of file diff --git a/src/Ryujinx/Ui/MainWindow.cs b/src/Ryujinx/Ui/MainWindow.cs index f4cb3d07276e..7cae62227dd7 100644 --- a/src/Ryujinx/Ui/MainWindow.cs +++ b/src/Ryujinx/Ui/MainWindow.cs @@ -876,7 +876,7 @@ public void RunApplication(string path, bool startFullscreen = false) _applicationLibrary.LoadAndSaveMetaData(_emulationContext.Processes.ActiveApplication.ProgramIdText, appMetadata => { - appMetadata.LastPlayed = DateTime.UtcNow.ToString(); + appMetadata.LastPlayed = DateTime.UtcNow; }); } } @@ -1019,10 +1019,11 @@ private void UpdateGameMetadata(string titleId) { _applicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => { - DateTime lastPlayedDateTime = DateTime.Parse(appMetadata.LastPlayed); - double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds; - - appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero); + if (appMetadata.LastPlayed.HasValue) + { + double sessionTimePlayed = DateTime.UtcNow.Subtract(appMetadata.LastPlayed.Value).TotalSeconds; + appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero); + } }); } } @@ -1089,7 +1090,7 @@ private void Application_Added(object sender, ApplicationAddedEventArgs args) args.AppData.Developer, args.AppData.Version, args.AppData.TimePlayed, - args.AppData.LastPlayed, + args.AppData.LastPlayedString, args.AppData.FileExtension, args.AppData.FileSize, args.AppData.Path, From 49c63ea07779eb27674ae8c4a14e1dcf4b794a95 Mon Sep 17 00:00:00 2001 From: John Date: Thu, 11 May 2023 17:14:29 -0700 Subject: [PATCH 11/21] Fix the restart after an update. (#4869) * Fix the restart after an update. * Fix the updater for the Ava UI too. * Fixing up the code after some change requests. Removed a line of code that was accidentally left in. * Fix restarting on Linux Avalonia. * Fix issues with escaped arguments. --- src/Ryujinx.Ava/Modules/Updater/Updater.cs | 42 ++++++++++++++++----- src/Ryujinx/Modules/Updater/UpdateDialog.cs | 15 +++++++- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/Ryujinx.Ava/Modules/Updater/Updater.cs b/src/Ryujinx.Ava/Modules/Updater/Updater.cs index 054299351380..77d77d7945f0 100644 --- a/src/Ryujinx.Ava/Modules/Updater/Updater.cs +++ b/src/Ryujinx.Ava/Modules/Updater/Updater.cs @@ -295,14 +295,7 @@ private static async void UpdateRyujinx(Window parent, string downloadUrl) if (shouldRestart) { List arguments = CommandLineState.Arguments.ToList(); - string ryuName = Path.GetFileName(Environment.ProcessPath); string executableDirectory = AppDomain.CurrentDomain.BaseDirectory; - string executablePath = Path.Combine(executableDirectory, ryuName); - - if (!Path.Exists(executablePath)) - { - executablePath = Path.Combine(executableDirectory, OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx"); - } // On macOS we perform the update at relaunch. if (OperatingSystem.IsMacOS()) @@ -310,13 +303,42 @@ private static async void UpdateRyujinx(Window parent, string downloadUrl) string baseBundlePath = Path.GetFullPath(Path.Combine(executableDirectory, "..", "..")); string newBundlePath = Path.Combine(UpdateDir, "Ryujinx.app"); string updaterScriptPath = Path.Combine(newBundlePath, "Contents", "Resources", "updater.sh"); - string currentPid = Process.GetCurrentProcess().Id.ToString(); + string currentPid = Environment.ProcessId.ToString(); - executablePath = "/bin/bash"; arguments.InsertRange(0, new List { updaterScriptPath, baseBundlePath, newBundlePath, currentPid }); + Process.Start("/bin/bash", arguments); + } + else + { + // Find the process name. + string ryuName = Path.GetFileName(Environment.ProcessPath); + + // Some operating systems can see the renamed executable, so strip off the .ryuold if found. + if (ryuName.EndsWith(".ryuold")) + { + ryuName = ryuName[..^7]; + } + + // Fallback if the executable could not be found. + if (!Path.Exists(Path.Combine(executableDirectory, ryuName))) + { + ryuName = OperatingSystem.IsWindows() ? "Ryujinx.Ava.exe" : "Ryujinx.Ava"; + } + + ProcessStartInfo processStart = new(ryuName) + { + UseShellExecute = true, + WorkingDirectory = executableDirectory + }; + + foreach (string argument in CommandLineState.Arguments) + { + processStart.ArgumentList.Add(argument); + } + + Process.Start(processStart); } - Process.Start(executablePath, arguments); Environment.Exit(0); } } diff --git a/src/Ryujinx/Modules/Updater/UpdateDialog.cs b/src/Ryujinx/Modules/Updater/UpdateDialog.cs index 4957b681b578..e0a257fd66ed 100644 --- a/src/Ryujinx/Modules/Updater/UpdateDialog.cs +++ b/src/Ryujinx/Modules/Updater/UpdateDialog.cs @@ -1,5 +1,6 @@ using Gdk; using Gtk; +using Ryujinx.Common; using Ryujinx.Ui; using Ryujinx.Ui.Common.Configuration; using Ryujinx.Ui.Common.Helper; @@ -47,9 +48,19 @@ private void YesButton_Clicked(object sender, EventArgs args) if (_restartQuery) { string ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx"; - string ryuExe = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName); - Process.Start(ryuExe, CommandLineState.Arguments); + ProcessStartInfo processStart = new(ryuName) + { + UseShellExecute = true, + WorkingDirectory = ReleaseInformation.GetBaseApplicationDirectory() + }; + + foreach (string argument in CommandLineState.Arguments) + { + processStart.ArgumentList.Add(argument); + } + + Process.Start(processStart); Environment.Exit(0); } From 95c06de4c16d292ad1cd962121762f83b9cce373 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Fri, 12 May 2023 01:30:47 +0100 Subject: [PATCH 12/21] GPU: Remove swizzle undefined matching and rework depth aliasing (#4896) * GPU: Remove swizzle undefined matching and rework depth aliasing @gdkchan pointed out that UI textures in TOTK seemed to be setting their texture swizzle incorrectly (texture was RGB but was sampling A, swizzle for A was wrong), so I determined that SwizzleComponentMatches was the problem and set on eliminating it. This PR combines existing work to select the most recently modified texture (now used when selecting which aliased texture to use) with some additional changes to remove the swizzle check and support aliased view creation. The original observation (#1538) was that we wanted to match depth textures for the purposes of aliasing with color textures, but they often had different swizzle from what was sampled (as it's generally the identity swizzle once rendered). At the time, I decided to allow swizzles to match if only the defined components matched, which fixed the issue in all known cases but could easily be broken by a game _expecting_ a given swizzle, such as a 1/0 value on a component. This error case could also occur in textures that don't even depth alias, such as R11G11B10, as the rule was created to generally apply to all cases. The solution is now to fail this exact match test, and allow the search for an R32 texture to create a swizzled view of a D32 texture (and other such cases). This allows the creation of a view that mismatches the requested format, which wasn't present before and was the reason for the swizzle matching approach. The exact match and view creation rules now follow the same rules over what textures to select when there are multiple options (such as a "perfect" match and an "aliased" match at the same time). It now selects the most recently modified texture, which is done with a new sequence number in the GpuContext (because we don't have enough of these). Reportedly fixes UI having weird coloured backgrounds in TOTK. This also fixes an issue in MK8D where returning from a race resulted in the character selection cubemaps being broken. May work around issues introduced by the "short texture cache" PR due to modification ordering, though they won't be truly fixed. Should allow (#4365) to avoid copies in more cases. Need to test that. I tested a bunch of games #1538 originally affected and they seem to be fine. This change affects all games so it would be good to get some wide testing on it. * Address feedback 1, fix an issue * Workaround: Do not allow copies for format alias. These should be removed when D32<->R32 copy dependencies become legal --- src/Ryujinx.Graphics.Gpu/GpuContext.cs | 11 +++ src/Ryujinx.Graphics.Gpu/Image/Texture.cs | 6 +- .../Image/TextureCache.cs | 92 +++++++++++-------- .../Image/TextureCompatibility.cs | 78 +++------------- .../Image/TextureGroup.cs | 9 ++ src/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs | 34 ++++++- .../Image/TextureViewCompatibility.cs | 1 + 7 files changed, 125 insertions(+), 106 deletions(-) diff --git a/src/Ryujinx.Graphics.Gpu/GpuContext.cs b/src/Ryujinx.Graphics.Gpu/GpuContext.cs index bab62b95257b..ccaabf70fb9e 100644 --- a/src/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/src/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -98,6 +98,8 @@ public sealed class GpuContext : IDisposable private Thread _gpuThread; private bool _pendingSync; + private long _modifiedSequence; + /// /// Creates a new instance of the GPU emulation context. /// @@ -200,6 +202,15 @@ private static ulong ConvertNanosecondsToTicks(ulong nanoseconds) return divided * NsToTicksFractionNumerator + errorBias; } + /// + /// Gets a sequence number for resource modification ordering. This increments on each call. + /// + /// A sequence number for resource modification ordering + public long GetModifiedSequence() + { + return _modifiedSequence++; + } + /// /// Gets the value of the GPU timer. /// diff --git a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs index 0427d09b20c9..a7af1aad7b04 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -1170,6 +1170,7 @@ public TextureMatchQuality IsExactMatch(TextureInfo info, TextureSearchFlags fla /// Host GPU capabilities /// Texture view initial layer on this texture /// Texture view first mipmap level on this texture + /// Texture search flags /// The level of compatiblilty a view with the given parameters created from this texture has public TextureViewCompatibility IsViewCompatible( TextureInfo info, @@ -1178,11 +1179,12 @@ public TextureMatchQuality IsExactMatch(TextureInfo info, TextureSearchFlags fla int layerSize, Capabilities caps, out int firstLayer, - out int firstLevel) + out int firstLevel, + TextureSearchFlags flags = TextureSearchFlags.None) { TextureViewCompatibility result = TextureViewCompatibility.Full; - result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps)); + result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewFormatCompatible(Info, info, caps, flags)); if (result != TextureViewCompatibility.Incompatible) { result = TextureCompatibility.PropagateViewCompatibility(result, TextureCompatibility.ViewTargetCompatible(Info, info, ref caps)); diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs index 49191239f09b..bccd3fd72316 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCache.cs @@ -569,7 +569,7 @@ private static int GetMinimumWidthInGob(int width, int minimumWidth, int bytesPe Texture texture = null; - TextureMatchQuality bestQuality = TextureMatchQuality.NoMatch; + long bestSequence = 0; for (int index = 0; index < sameAddressOverlapsCount; index++) { @@ -601,17 +601,12 @@ private static int GetMinimumWidthInGob(int width, int minimumWidth, int bytesPe continue; } } - } - if (matchQuality == TextureMatchQuality.Perfect) - { - texture = overlap; - break; - } - else if (matchQuality > bestQuality) - { - texture = overlap; - bestQuality = matchQuality; + if (texture == null || overlap.Group.ModifiedSequence - bestSequence > 0) + { + texture = overlap; + bestSequence = overlap.Group.ModifiedSequence; + } } } @@ -664,6 +659,7 @@ private static int GetMinimumWidthInGob(int width, int minimumWidth, int bytesPe int fullyCompatible = 0; // Evaluate compatibility of overlaps, add temporary references + int preferredOverlap = -1; for (int index = 0; index < overlapsCount; index++) { @@ -675,17 +671,26 @@ private static int GetMinimumWidthInGob(int width, int minimumWidth, int bytesPe sizeInfo.LayerSize, _context.Capabilities, out int firstLayer, - out int firstLevel); + out int firstLevel, + flags); - if (overlapCompatibility == TextureViewCompatibility.Full) + if (overlapCompatibility >= TextureViewCompatibility.FormatAlias) { if (overlap.IsView) { - overlapCompatibility = TextureViewCompatibility.CopyOnly; + overlapCompatibility = overlapCompatibility == TextureViewCompatibility.FormatAlias ? + TextureViewCompatibility.Incompatible : + TextureViewCompatibility.CopyOnly; } else { fullyCompatible++; + + if (preferredOverlap == -1 || overlap.Group.ModifiedSequence - bestSequence > 0) + { + preferredOverlap = index; + bestSequence = overlap.Group.ModifiedSequence; + } } } @@ -695,37 +700,50 @@ private static int GetMinimumWidthInGob(int width, int minimumWidth, int bytesPe // Search through the overlaps to find a compatible view and establish any copy dependencies. - for (int index = 0; index < overlapsCount; index++) + if (preferredOverlap != -1) { - Texture overlap = _textureOverlaps[index]; - OverlapInfo oInfo = _overlapInfo[index]; + Texture overlap = _textureOverlaps[preferredOverlap]; + OverlapInfo oInfo = _overlapInfo[preferredOverlap]; + + bool aliased = oInfo.Compatibility == TextureViewCompatibility.FormatAlias; - if (oInfo.Compatibility == TextureViewCompatibility.Full) + if (!isSamplerTexture) { - if (!isSamplerTexture) - { - // If this is not a sampler texture, the size might be different from the requested size, - // so we need to make sure the texture information has the correct size for this base texture, - // before creating the view. - info = info.CreateInfoForLevelView(overlap, oInfo.FirstLevel); - } + // If this is not a sampler texture, the size might be different from the requested size, + // so we need to make sure the texture information has the correct size for this base texture, + // before creating the view. - texture = overlap.CreateView(info, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel); - texture.SynchronizeMemory(); - break; + info = info.CreateInfoForLevelView(overlap, oInfo.FirstLevel, aliased); + } + else if (aliased) + { + // The format must be changed to match the parent. + info = info.CreateInfoWithFormat(overlap.Info.FormatInfo); } - else if (oInfo.Compatibility == TextureViewCompatibility.CopyOnly && fullyCompatible == 0) + + texture = overlap.CreateView(info, sizeInfo, range.Value, oInfo.FirstLayer, oInfo.FirstLevel); + texture.SynchronizeMemory(); + } + else + { + for (int index = 0; index < overlapsCount; index++) { - // Only copy compatible. If there's another choice for a FULLY compatible texture, choose that instead. + Texture overlap = _textureOverlaps[index]; + OverlapInfo oInfo = _overlapInfo[index]; + + if (oInfo.Compatibility == TextureViewCompatibility.CopyOnly && fullyCompatible == 0) + { + // Only copy compatible. If there's another choice for a FULLY compatible texture, choose that instead. - texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode); + texture = new Texture(_context, _physicalMemory, info, sizeInfo, range.Value, scaleMode); - texture.InitializeGroup(true, true, new List()); - texture.InitializeData(false, false); + texture.InitializeGroup(true, true, new List()); + texture.InitializeData(false, false); - overlap.SynchronizeMemory(); - overlap.CreateCopyDependency(texture, oInfo.FirstLayer, oInfo.FirstLevel, true); - break; + overlap.SynchronizeMemory(); + overlap.CreateCopyDependency(texture, oInfo.FirstLayer, oInfo.FirstLevel, true); + break; + } } } @@ -740,7 +758,7 @@ private static int GetMinimumWidthInGob(int width, int minimumWidth, int bytesPe Texture overlap = _textureOverlaps[index]; OverlapInfo oInfo = _overlapInfo[index]; - if (oInfo.Compatibility <= TextureViewCompatibility.LayoutIncompatible) + if (oInfo.Compatibility <= TextureViewCompatibility.LayoutIncompatible || oInfo.Compatibility == TextureViewCompatibility.FormatAlias) { if (!overlap.IsView && texture.DataOverlaps(overlap, oInfo.Compatibility)) { diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs index 5d8462220155..85ad0bb019a8 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureCompatibility.cs @@ -291,22 +291,7 @@ public static bool LayoutMatches(TextureInfo lhs, TextureInfo rhs) /// The minimum compatibility level of two provided view compatibility results public static TextureViewCompatibility PropagateViewCompatibility(TextureViewCompatibility first, TextureViewCompatibility second) { - if (first == TextureViewCompatibility.Incompatible || second == TextureViewCompatibility.Incompatible) - { - return TextureViewCompatibility.Incompatible; - } - else if (first == TextureViewCompatibility.LayoutIncompatible || second == TextureViewCompatibility.LayoutIncompatible) - { - return TextureViewCompatibility.LayoutIncompatible; - } - else if (first == TextureViewCompatibility.CopyOnly || second == TextureViewCompatibility.CopyOnly) - { - return TextureViewCompatibility.CopyOnly; - } - else - { - return TextureViewCompatibility.Full; - } + return (TextureViewCompatibility)Math.Min((int)first, (int)second); } /// @@ -628,15 +613,21 @@ public static bool ViewLayoutCompatible(TextureInfo lhs, TextureInfo rhs, int lh /// Texture information of the texture view /// Texture information of the texture view /// Host GPU capabilities + /// Texture search flags /// The view compatibility level of the texture formats - public static TextureViewCompatibility ViewFormatCompatible(TextureInfo lhs, TextureInfo rhs, Capabilities caps) + public static TextureViewCompatibility ViewFormatCompatible(TextureInfo lhs, TextureInfo rhs, Capabilities caps, TextureSearchFlags flags) { FormatInfo lhsFormat = lhs.FormatInfo; FormatInfo rhsFormat = rhs.FormatInfo; if (lhsFormat.Format.IsDepthOrStencil() || rhsFormat.Format.IsDepthOrStencil()) { - return lhsFormat.Format == rhsFormat.Format ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible; + return FormatMatches(lhs, rhs, flags.HasFlag(TextureSearchFlags.ForSampler), flags.HasFlag(TextureSearchFlags.DepthAlias)) switch + { + TextureMatchQuality.Perfect => TextureViewCompatibility.Full, + TextureMatchQuality.FormatAlias => TextureViewCompatibility.FormatAlias, + _ => TextureViewCompatibility.Incompatible + }; } if (IsFormatHostIncompatible(lhs, caps) || IsFormatHostIncompatible(rhs, caps)) @@ -754,49 +745,6 @@ public static TextureViewCompatibility ViewTargetCompatible(TextureInfo lhs, Tex return result ? TextureViewCompatibility.Full : TextureViewCompatibility.Incompatible; } - /// - /// Checks if a swizzle component in two textures functionally match, taking into account if the components are defined. - /// - /// Texture information to compare - /// Texture information to compare with - /// Swizzle component for the first texture - /// Swizzle component for the second texture - /// Component index, starting at 0 for red - /// True if the swizzle components functionally match, false othersize - private static bool SwizzleComponentMatches(TextureInfo lhs, TextureInfo rhs, SwizzleComponent swizzleLhs, SwizzleComponent swizzleRhs, int component) - { - int lhsComponents = lhs.FormatInfo.Components; - int rhsComponents = rhs.FormatInfo.Components; - - if (lhsComponents == 4 && rhsComponents == 4) - { - return swizzleLhs == swizzleRhs; - } - - // Swizzles after the number of components a format defines are "undefined". - // We allow these to not be equal under certain circumstances. - // This can only happen when there are less than 4 components in a format. - // It tends to happen when float depth textures are sampled. - - bool lhsDefined = (swizzleLhs - SwizzleComponent.Red) < lhsComponents; - bool rhsDefined = (swizzleRhs - SwizzleComponent.Red) < rhsComponents; - - if (lhsDefined == rhsDefined) - { - // If both are undefined, return true. Otherwise just check if they're equal. - return lhsDefined ? swizzleLhs == swizzleRhs : true; - } - else - { - SwizzleComponent defined = lhsDefined ? swizzleLhs : swizzleRhs; - SwizzleComponent undefined = lhsDefined ? swizzleRhs : swizzleLhs; - - // Undefined swizzle can be matched by a forced value (0, 1), exact equality, or expected value. - // For example, R___ matches R001, RGBA but not RBGA. - return defined == undefined || defined < SwizzleComponent.Red || defined == SwizzleComponent.Red + component; - } - } - /// /// Checks if the texture shader sampling parameters of two texture informations match. /// @@ -806,10 +754,10 @@ private static bool SwizzleComponentMatches(TextureInfo lhs, TextureInfo rhs, Sw public static bool SamplerParamsMatches(TextureInfo lhs, TextureInfo rhs) { return lhs.DepthStencilMode == rhs.DepthStencilMode && - SwizzleComponentMatches(lhs, rhs, lhs.SwizzleR, rhs.SwizzleR, 0) && - SwizzleComponentMatches(lhs, rhs, lhs.SwizzleG, rhs.SwizzleG, 1) && - SwizzleComponentMatches(lhs, rhs, lhs.SwizzleB, rhs.SwizzleB, 2) && - SwizzleComponentMatches(lhs, rhs, lhs.SwizzleA, rhs.SwizzleA, 3); + lhs.SwizzleR == rhs.SwizzleR && + lhs.SwizzleG == rhs.SwizzleG && + lhs.SwizzleB == rhs.SwizzleB && + lhs.SwizzleA == rhs.SwizzleA; } /// diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs index b36b16e94b5a..2fa1e79e5575 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureGroup.cs @@ -68,6 +68,11 @@ class TextureGroup : IDisposable /// public bool HasIncompatibleOverlaps => _incompatibleOverlaps.Count > 0; + /// + /// Number indicating the order this texture group was modified relative to others. + /// + public long ModifiedSequence { get; private set; } + private readonly GpuContext _context; private readonly PhysicalMemory _physicalMemory; @@ -664,6 +669,8 @@ private void ClearIncompatibleOverlaps(Texture texture) /// The texture that has been modified public void SignalModified(Texture texture) { + ModifiedSequence = _context.GetModifiedSequence(); + ClearIncompatibleOverlaps(texture); EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) => @@ -684,6 +691,8 @@ public void SignalModified(Texture texture) /// True if this texture is being bound, false if unbound public void SignalModifying(Texture texture, bool bound) { + ModifiedSequence = _context.GetModifiedSequence(); + ClearIncompatibleOverlaps(texture); EvaluateRelevantHandles(texture, (baseHandle, regionCount, split) => diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs index a7ee12bccd13..1994d22630da 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureInfo.cs @@ -300,8 +300,9 @@ public SizeInfo CalculateSizeInfo(int layerSize = 0) /// /// The parent texture /// The first level of the texture view + /// True if the parent format should be inherited /// The adjusted texture information with the new size - public TextureInfo CreateInfoForLevelView(Texture parent, int firstLevel) + public TextureInfo CreateInfoForLevelView(Texture parent, int firstLevel, bool parentFormat) { // When the texture is used as view of another texture, we must // ensure that the sizes are valid, otherwise data uploads would fail @@ -370,7 +371,36 @@ public TextureInfo CreateInfoForLevelView(Texture parent, int firstLevel) GobBlocksInZ, GobBlocksInTileX, target, - FormatInfo, + parentFormat ? parent.Info.FormatInfo : FormatInfo, + DepthStencilMode, + SwizzleR, + SwizzleG, + SwizzleB, + SwizzleA); + } + + /// + /// Creates texture information for a given format and this information. + /// + /// Format for the new texture info + /// New info with the specified format + public TextureInfo CreateInfoWithFormat(FormatInfo formatInfo) + { + return new TextureInfo( + GpuAddress, + Width, + Height, + DepthOrLayers, + Levels, + SamplesInX, + SamplesInY, + Stride, + IsLinear, + GobBlocksInY, + GobBlocksInZ, + GobBlocksInTileX, + Target, + formatInfo, DepthStencilMode, SwizzleR, SwizzleG, diff --git a/src/Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs b/src/Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs index b89936ebdab2..dfa688c451ca 100644 --- a/src/Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs +++ b/src/Ryujinx.Graphics.Gpu/Image/TextureViewCompatibility.cs @@ -9,6 +9,7 @@ enum TextureViewCompatibility Incompatible = 0, LayoutIncompatible, CopyOnly, + FormatAlias, Full } } From 5fda543f8478222ce80ab3e159fd480de80b2980 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Fri, 12 May 2023 02:06:15 +0100 Subject: [PATCH 13/21] Vulkan: Partially workaround MoltenVK InvalidResource error (#4880) * Add MVK stage flags workaround * Actually do the workaround * Remove GS on VS stuff * Address feedback --- .../PipelineLayoutFactory.cs | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs index 96b3b3b1ca0f..2d8814191caf 100644 --- a/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs +++ b/src/Ryujinx.Graphics.Vulkan/PipelineLayoutFactory.cs @@ -12,6 +12,28 @@ static class PipelineLayoutFactory ShaderStageFlags.FragmentBit | ShaderStageFlags.ComputeBit; + private static ShaderStageFlags ActiveStages(uint stages) + { + ShaderStageFlags stageFlags = 0; + + while (stages != 0) + { + int stage = BitOperations.TrailingZeroCount(stages); + stages &= ~(1u << stage); + + stageFlags |= stage switch + { + 1 => ShaderStageFlags.FragmentBit, + 2 => ShaderStageFlags.GeometryBit, + 3 => ShaderStageFlags.TessellationControlBit, + 4 => ShaderStageFlags.TessellationEvaluationBit, + _ => ShaderStageFlags.VertexBit | ShaderStageFlags.ComputeBit + }; + } + + return stageFlags; + } + public static unsafe DescriptorSetLayout[] Create(VulkanRenderer gd, Device device, uint stages, bool usePd, out PipelineLayout layout) { int stagesCount = BitOperations.PopCount(stages); @@ -34,6 +56,7 @@ public static unsafe DescriptorSetLayout[] Create(VulkanRenderer gd, Device devi }; int iter = 0; + var activeStages = ActiveStages(stages); while (stages != 0) { @@ -67,12 +90,16 @@ void Set(DescriptorSetLayoutBinding* bindings, int maxPerStage, DescriptorType t void SetStorage(DescriptorSetLayoutBinding* bindings, int maxPerStage, int start = 0) { + // There's a bug on MoltenVK where using the same buffer across different stages + // causes invalid resource errors, allow the binding on all active stages as workaround. + var flags = gd.IsMoltenVk ? activeStages : stageFlags; + bindings[start + iter] = new DescriptorSetLayoutBinding { Binding = (uint)(start + stage * maxPerStage), DescriptorType = DescriptorType.StorageBuffer, DescriptorCount = (uint)maxPerStage, - StageFlags = stageFlags + StageFlags = flags }; } From 7271f1b18e13d4188abe408443272da89e7dcacc Mon Sep 17 00:00:00 2001 From: Mary Date: Fri, 12 May 2023 18:53:14 +0200 Subject: [PATCH 14/21] Bump shader cache codegen version That was missing from #4892 --- .../Shader/DiskCache/DiskCacheHostStorage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs index f30f1ff1b012..71098efa813d 100644 --- a/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs +++ b/src/Ryujinx.Graphics.Gpu/Shader/DiskCache/DiskCacheHostStorage.cs @@ -22,7 +22,7 @@ class DiskCacheHostStorage private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMinor = 2; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; - private const uint CodeGenVersion = 4821; + private const uint CodeGenVersion = 4892; private const string SharedTocFileName = "shared.toc"; private const string SharedDataFileName = "shared.data"; From 2b6e81deea47c1546e44fdcb8702a7c667056fde Mon Sep 17 00:00:00 2001 From: Ac_K Date: Fri, 12 May 2023 21:39:42 +0200 Subject: [PATCH 15/21] Ava: Fix wrong MouseButton (#4900) --- src/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs b/src/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs index b3e1a21a1487..8f64aa28d4e4 100644 --- a/src/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs +++ b/src/Ryujinx.Ava/Input/AvaloniaMouseDriver.cs @@ -70,11 +70,14 @@ private void Parent_ScrollEvent(object o, PointerWheelEventArgs args) private void Parent_PointerReleaseEvent(object o, PointerReleasedEventArgs args) { - int button = (int)args.InitialPressMouseButton - 1; - - if (PressedButtons.Count() >= button) + if (args.InitialPressMouseButton != Avalonia.Input.MouseButton.None) { - PressedButtons[button] = false; + int button = (int)args.InitialPressMouseButton; + + if (PressedButtons.Count() >= button) + { + PressedButtons[button] = false; + } } } From c2709b3bddf94e61eaacfa1271494d16e5412b26 Mon Sep 17 00:00:00 2001 From: Isaac Marovitz <42140194+IsaacMarovitz@users.noreply.github.com> Date: Fri, 12 May 2023 23:53:52 +0100 Subject: [PATCH 16/21] macOS CI Adjustments (#4910) * Fix macOS build name in CI Fixes updater * Update build.yml Don't publish x86 Mac builds * Naming nitpick * Berry changes * Use the same prefix for PR and release build archives --------- Co-authored-by: TSR Berry <20988865+TSRBerry@users.noreply.github.com> --- .github/workflows/build.yml | 18 +++++++++--------- distribution/macos/create_macos_build.sh | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8bc55ec3e858..54389e55f18c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -38,7 +38,7 @@ jobs: RELEASE_ZIP_OS_NAME: linux_x64 - os: macOS-latest - OS_NAME: MacOS x64 + OS_NAME: macOS x64 DOTNET_RUNTIME_IDENTIFIER: osx-x64 RELEASE_ZIP_OS_NAME: osx_x64 @@ -68,15 +68,15 @@ jobs: - 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 - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest' - name: Publish Ryujinx.Headless.SDL2 run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_sdl2_headless -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Headless.SDL2 --self-contained true - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest' - name: Publish Ryujinx.Ava run: dotnet publish -c "${{ matrix.configuration }}" -r "${{ matrix.DOTNET_RUNTIME_IDENTIFIER }}" -o ./publish_ava -p:Version="${{ env.RYUJINX_BASE_VERSION }}" -p:DebugType=embedded -p:SourceRevisionId="${{ steps.git_short_hash.outputs.result }}" -p:ExtraDefineConstants=DISABLE_UPDATER src/Ryujinx.Ava --self-contained true - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest' - name: Set executable bit run: | @@ -90,24 +90,24 @@ jobs: with: name: ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }} path: publish - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest' - name: Upload Ryujinx.Headless.SDL2 artifact uses: actions/upload-artifact@v3 with: name: sdl2-ryujinx-headless-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }} path: publish_sdl2_headless - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest' - name: Upload Ryujinx.Ava artifact uses: actions/upload-artifact@v3 with: name: ava-ryujinx-${{ matrix.configuration }}-${{ env.RYUJINX_BASE_VERSION }}+${{ steps.git_short_hash.outputs.result }}-${{ matrix.RELEASE_ZIP_OS_NAME }} path: publish_ava - if: github.event_name == 'pull_request' + if: github.event_name == 'pull_request' && matrix.os != 'macOS-latest' build_macos: - name: MacOS universal (${{ matrix.configuration }}) + name: macOS Universal (${{ matrix.configuration }}) runs-on: ubuntu-latest strategy: matrix: @@ -150,4 +150,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' \ No newline at end of file + if: github.event_name == 'pull_request' diff --git a/distribution/macos/create_macos_build.sh b/distribution/macos/create_macos_build.sh index 1a5a387380cf..7405073d270d 100755 --- a/distribution/macos/create_macos_build.sh +++ b/distribution/macos/create_macos_build.sh @@ -22,9 +22,9 @@ EXTRA_ARGS=$8 if [ "$VERSION" == "1.1.0" ]; then - RELEASE_TAR_FILE_NAME=Ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.app.tar + RELEASE_TAR_FILE_NAME=test-ava-ryujinx-$CONFIGURATION-$VERSION+$SOURCE_REVISION_ID-macos_universal.app.tar else - RELEASE_TAR_FILE_NAME=Ryujinx-$VERSION-macos_universal.app.tar + RELEASE_TAR_FILE_NAME=test-ava-ryujinx-$VERSION-macos_universal.app.tar fi ARM64_APP_BUNDLE="$TEMP_DIRECTORY/output_arm64/Ryujinx.app" From f679f25e084b7196f1eabd6a0e9ea60bca679a75 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Fri, 12 May 2023 21:59:46 -0300 Subject: [PATCH 17/21] Set OpenGL PixelPackBuffer to 0 when done (#4921) --- src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs | 2 ++ src/Ryujinx.Graphics.OpenGL/Window.cs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index f24a58fc3c76..90a2936d06f3 100644 --- a/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/src/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -306,6 +306,8 @@ public void CopyTo(BufferRange range, int layer, int level, int stride) int offset = WriteToPbo2D(range.Offset, layer, level); Debug.Assert(offset == 0); + + GL.BindBuffer(BufferTarget.PixelPackBuffer, 0); } public void WriteToPbo(int offset, bool forceBgra) diff --git a/src/Ryujinx.Graphics.OpenGL/Window.cs b/src/Ryujinx.Graphics.OpenGL/Window.cs index b37ec375e4a8..cc9836e06a31 100644 --- a/src/Ryujinx.Graphics.OpenGL/Window.cs +++ b/src/Ryujinx.Graphics.OpenGL/Window.cs @@ -93,7 +93,7 @@ private void CopyTextureToFrameBufferRGB(int drawFramebuffer, int readFramebuffe oldView.Dispose(); } } - + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, drawFramebuffer); GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, readFramebuffer); From 880fd3cfcb1c394b06bdb4cd3433e23379b4fbe7 Mon Sep 17 00:00:00 2001 From: Mary Date: Sat, 13 May 2023 09:15:16 +0200 Subject: [PATCH 18/21] audio: sdl2: Do not report 5.1 if the device doesn't support it (#4908) * amadeus: adjust VirtualDevice channel configuration reporting with HardwareDevice * audio: sdl2: Do not report 5.1 if device doesn't support it SDL2 5.1 to Stereo conversion is terrible and make everything sound quiet. Let's not expose 5.1 if not truly supported by the device. --- .../SDL2HardwareDeviceDriver.cs | 27 +++++++++++++++++++ .../Renderer/Device/VirtualDevice.cs | 2 +- .../Device/VirtualDeviceSessionRegistry.cs | 19 ++++++++++++- src/Ryujinx.HLE/HOS/Horizon.cs | 2 +- 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs index b190b4c83dcb..d3a73cfcbfca 100644 --- a/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs +++ b/src/Ryujinx.Audio.Backends.SDL2/SDL2HardwareDeviceDriver.cs @@ -5,6 +5,7 @@ using Ryujinx.SDL2.Common; using System; using System.Collections.Concurrent; +using System.Runtime.InteropServices; using System.Threading; using static Ryujinx.Audio.Integration.IHardwareDeviceDriver; @@ -18,6 +19,13 @@ public class SDL2HardwareDeviceDriver : IHardwareDeviceDriver private readonly ManualResetEvent _pauseEvent; private readonly ConcurrentDictionary _sessions; + private bool _supportSurroundConfiguration; + + // TODO: Add this to SDL2-CS + // NOTE: We use a DllImport here because of marshaling issue for spec. + [DllImport("SDL2")] + private static extern int SDL_GetDefaultAudioInfo(IntPtr name, out SDL_AudioSpec spec, int isCapture); + public SDL2HardwareDeviceDriver() { _updateRequiredEvent = new ManualResetEvent(false); @@ -25,6 +33,20 @@ public SDL2HardwareDeviceDriver() _sessions = new ConcurrentDictionary(); SDL2Driver.Instance.Initialize(); + + int res = SDL_GetDefaultAudioInfo(IntPtr.Zero, out var spec, 0); + + if (res != 0) + { + Logger.Error?.Print(LogClass.Application, + $"SDL_GetDefaultAudioInfo failed with error \"{SDL_GetError()}\""); + + _supportSurroundConfiguration = true; + } + else + { + _supportSurroundConfiguration = spec.channels == 6; + } } public static bool IsSupported => IsSupportedInternal(); @@ -164,6 +186,11 @@ public bool SupportsSampleFormat(SampleFormat sampleFormat) public bool SupportsChannelCount(uint channelCount) { + if (channelCount == 6) + { + return _supportSurroundConfiguration; + } + return true; } diff --git a/src/Ryujinx.Audio/Renderer/Device/VirtualDevice.cs b/src/Ryujinx.Audio/Renderer/Device/VirtualDevice.cs index 2fa030a8f763..90692b004558 100644 --- a/src/Ryujinx.Audio/Renderer/Device/VirtualDevice.cs +++ b/src/Ryujinx.Audio/Renderer/Device/VirtualDevice.cs @@ -45,7 +45,7 @@ public class VirtualDevice /// The name of the . /// The count of channels supported by the . /// Indicate if the is provided by an external interface. - private VirtualDevice(string name, uint channelCount, bool isExternalOutput) + public VirtualDevice(string name, uint channelCount, bool isExternalOutput) { Name = name; ChannelCount = channelCount; diff --git a/src/Ryujinx.Audio/Renderer/Device/VirtualDeviceSessionRegistry.cs b/src/Ryujinx.Audio/Renderer/Device/VirtualDeviceSessionRegistry.cs index 927e45add9ea..696af90fa692 100644 --- a/src/Ryujinx.Audio/Renderer/Device/VirtualDeviceSessionRegistry.cs +++ b/src/Ryujinx.Audio/Renderer/Device/VirtualDeviceSessionRegistry.cs @@ -1,3 +1,4 @@ +using Ryujinx.Audio.Integration; using System.Collections.Generic; namespace Ryujinx.Audio.Renderer.Device @@ -22,7 +23,23 @@ public class VirtualDeviceSessionRegistry /// The current active . /// // TODO: make this configurable - public VirtualDevice ActiveDevice = VirtualDevice.Devices[2]; + public VirtualDevice ActiveDevice { get; } + + public VirtualDeviceSessionRegistry(IHardwareDeviceDriver driver) + { + uint channelCount; + + if (driver.GetRealDeviceDriver().SupportsChannelCount(6)) + { + channelCount = 6; + } + else + { + channelCount = 2; + } + + ActiveDevice = new VirtualDevice("AudioTvOutput", channelCount, false); + } /// /// Get the associated from an AppletResourceId. diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs index a71837cab6b3..166761c2cb6a 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -261,7 +261,7 @@ private void InitializeAudioRenderer(ITickSource tickSource) AudioInputManager = new AudioInputManager(); AudioRendererManager = new AudioRendererManager(tickSource); AudioRendererManager.SetVolume(Device.Configuration.AudioVolume); - AudioDeviceSessionRegistry = new VirtualDeviceSessionRegistry(); + AudioDeviceSessionRegistry = new VirtualDeviceSessionRegistry(Device.AudioDeviceDriver); IWritableEvent[] audioOutputRegisterBufferEvents = new IWritableEvent[Constants.AudioOutSessionCountMax]; From aae4595bdbf16ebfc73299fa63ae7dffb8300d56 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sat, 13 May 2023 13:24:43 +0200 Subject: [PATCH 19/21] Add timeout of 35 minutes to workflow jobs (#4928) --- .github/workflows/build.yml | 4 +++- .github/workflows/flatpak.yml | 1 + .github/workflows/nightly_pr_comment.yml | 3 ++- .github/workflows/release.yml | 6 ++++-- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 54389e55f18c..c6dc724c82f5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,6 +27,7 @@ jobs: build: name: ${{ matrix.OS_NAME }} (${{ matrix.configuration }}) runs-on: ${{ matrix.os }} + timeout-minutes: 35 strategy: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] @@ -109,6 +110,7 @@ jobs: build_macos: name: macOS Universal (${{ matrix.configuration }}) runs-on: ubuntu-latest + timeout-minutes: 35 strategy: matrix: configuration: [ Debug, Release ] @@ -150,4 +152,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' \ No newline at end of file diff --git a/.github/workflows/flatpak.yml b/.github/workflows/flatpak.yml index 86a80eabf8fe..04b917c34ced 100644 --- a/.github/workflows/flatpak.yml +++ b/.github/workflows/flatpak.yml @@ -12,6 +12,7 @@ concurrency: flatpak-release jobs: release: + timeout-minutes: 35 runs-on: ubuntu-latest env: diff --git a/.github/workflows/nightly_pr_comment.yml b/.github/workflows/nightly_pr_comment.yml index bc3d1c43f991..9ddd458c6ad8 100644 --- a/.github/workflows/nightly_pr_comment.yml +++ b/.github/workflows/nightly_pr_comment.yml @@ -7,6 +7,7 @@ jobs: pr_comment: if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' runs-on: ubuntu-latest + timeout-minutes: 35 steps: - uses: actions/github-script@v6 with: @@ -65,4 +66,4 @@ jobs: } else { core.info(`Creating a comment`); await github.rest.issues.createComment({repo, owner, issue_number, body}); - } + } \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 363e9cadda05..a320331909a8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -46,6 +46,7 @@ jobs: release: name: Release ${{ matrix.OS_NAME }} runs-on: ${{ matrix.os }} + timeout-minutes: 35 strategy: matrix: os: [ ubuntu-latest, windows-latest ] @@ -143,13 +144,14 @@ jobs: macos_release: name: Release MacOS universal runs-on: ubuntu-latest + timeout-minutes: 35 steps: - uses: actions/checkout@v3 - uses: actions/setup-dotnet@v3 with: global-json-file: global.json - + - name: Setup LLVM 14 run: | wget https://apt.llvm.org/llvm.sh @@ -205,4 +207,4 @@ jobs: needs: release with: ryujinx_version: "1.1.${{ github.run_number }}" - secrets: inherit + secrets: inherit \ No newline at end of file From 17ba217940aa287821bd8f1e5d93e22b58998617 Mon Sep 17 00:00:00 2001 From: riperiperi Date: Sat, 13 May 2023 14:15:05 +0100 Subject: [PATCH 20/21] Vulkan: Device map buffers written more than flushed (#4911) --- src/Ryujinx.Graphics.Vulkan/BufferHolder.cs | 24 ++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs index 9a23280d030d..6e10fad00a37 100644 --- a/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs +++ b/src/Ryujinx.Graphics.Vulkan/BufferHolder.cs @@ -56,6 +56,7 @@ class BufferHolder : IDisposable private int _writeCount; private int _flushCount; private int _flushTemp; + private int _lastFlushWrite = -1; private ReaderWriterLock _flushLock; private FenceHolder _flushFence; @@ -200,14 +201,21 @@ private void ConsiderBackingSwap() { if (_baseType == BufferAllocationType.Auto) { - if (_writeCount >= WriteCountThreshold || _setCount >= SetCountThreshold || _flushCount >= FlushCountThreshold) + // When flushed, wait for a bit more info to make a decision. + bool wasFlushed = _flushTemp > 0; + int multiplier = wasFlushed ? 2 : 0; + if (_writeCount >= (WriteCountThreshold << multiplier) || _setCount >= (SetCountThreshold << multiplier) || _flushCount >= (FlushCountThreshold << multiplier)) { if (_flushCount > 0 || _flushTemp-- > 0) { // Buffers that flush should ideally be mapped in host address space for easy copies. // If the buffer is large it will do better on GPU memory, as there will be more writes than data flushes (typically individual pages). // If it is small, then it's likely most of the buffer will be flushed so we want it on host memory, as access is cached. - DesiredType = Size > DeviceLocalSizeThreshold ? BufferAllocationType.DeviceLocalMapped : BufferAllocationType.HostMapped; + + bool hostMappingSensitive = _gd.Vendor == Vendor.Nvidia; + bool deviceLocalMapped = Size > DeviceLocalSizeThreshold || (wasFlushed && _writeCount > _flushCount * 10 && hostMappingSensitive) || _currentType == BufferAllocationType.DeviceLocalMapped; + + DesiredType = deviceLocalMapped ? BufferAllocationType.DeviceLocalMapped : BufferAllocationType.HostMapped; // It's harder for a buffer that is flushed to revert to another type of mapping. if (_flushCount > 0) @@ -215,17 +223,18 @@ private void ConsiderBackingSwap() _flushTemp = 1000; } } - else if (_writeCount >= WriteCountThreshold) + else if (_writeCount >= (WriteCountThreshold << multiplier)) { // Buffers that are written often should ideally be in the device local heap. (Storage buffers) DesiredType = BufferAllocationType.DeviceLocal; } - else if (_setCount > SetCountThreshold) + else if (_setCount > (SetCountThreshold << multiplier)) { // Buffers that have their data set often should ideally be host mapped. (Constant buffers) DesiredType = BufferAllocationType.HostMapped; } + _lastFlushWrite = -1; _flushCount = 0; _writeCount = 0; _setCount = 0; @@ -418,7 +427,12 @@ public unsafe PinnedSpan GetData(int offset, int size) WaitForFlushFence(); - _flushCount++; + if (_lastFlushWrite != _writeCount) + { + // If it's on the same page as the last flush, ignore it. + _lastFlushWrite = _writeCount; + _flushCount++; + } Span result; From 22202be3946b5cb65511059e717af449168812f3 Mon Sep 17 00:00:00 2001 From: TSRBerry <20988865+TSRBerry@users.noreply.github.com> Date: Sun, 14 May 2023 16:34:31 +0200 Subject: [PATCH 21/21] [GUI] Fix always hide cursor mode not hiding the cursor until it was moved (#4927) * gtk: Add missing isMouseInClient check for hide-cursor * ava: Add missing events and default isCursorInRenderer to true This is necessary because we don't receive a initial PointerEnter event for some reason. --- src/Ryujinx.Ava/AppHost.cs | 18 ++++++++++++---- src/Ryujinx/Ui/RendererWidgetBase.cs | 32 ++++++++++++++-------------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/src/Ryujinx.Ava/AppHost.cs b/src/Ryujinx.Ava/AppHost.cs index 795c3f7a7036..f3e90ef17c66 100644 --- a/src/Ryujinx.Ava/AppHost.cs +++ b/src/Ryujinx.Ava/AppHost.cs @@ -86,7 +86,7 @@ internal class AppHost private KeyboardHotkeyState _prevHotkeyState; private long _lastCursorMoveTime; - private bool _isCursorInRenderer; + private bool _isCursorInRenderer = true; private bool _isStopped; private bool _isActive; @@ -160,7 +160,9 @@ internal class AppHost ConfigurationState.Instance.HideCursor.Event += HideCursorState_Changed; - _topLevel.PointerMoved += TopLevel_PointerMoved; + _topLevel.PointerMoved += TopLevel_PointerEnterOrMoved; + _topLevel.PointerEnter += TopLevel_PointerEnterOrMoved; + _topLevel.PointerLeave += TopLevel_PointerLeave; if (OperatingSystem.IsWindows()) { @@ -183,7 +185,7 @@ internal class AppHost _gpuCancellationTokenSource = new CancellationTokenSource(); } - private void TopLevel_PointerMoved(object sender, PointerEventArgs e) + private void TopLevel_PointerEnterOrMoved(object sender, PointerEventArgs e) { if (sender is MainWindow window) { @@ -201,6 +203,12 @@ private void TopLevel_PointerMoved(object sender, PointerEventArgs e) } } } + + private void TopLevel_PointerLeave(object sender, PointerEventArgs e) + { + _isCursorInRenderer = false; + } + private void UpdateScalingFilterLevel(object sender, ReactiveEventArgs e) { _renderer.Window?.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); @@ -446,7 +454,9 @@ private void Dispose() ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel; ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAntiAliasing; - _topLevel.PointerMoved -= TopLevel_PointerMoved; + _topLevel.PointerMoved -= TopLevel_PointerEnterOrMoved; + _topLevel.PointerEnter -= TopLevel_PointerEnterOrMoved; + _topLevel.PointerLeave -= TopLevel_PointerLeave; _gpuCancellationTokenSource.Cancel(); _gpuCancellationTokenSource.Dispose(); diff --git a/src/Ryujinx/Ui/RendererWidgetBase.cs b/src/Ryujinx/Ui/RendererWidgetBase.cs index 0fa7240b8564..573b69b3568f 100644 --- a/src/Ryujinx/Ui/RendererWidgetBase.cs +++ b/src/Ryujinx/Ui/RendererWidgetBase.cs @@ -321,27 +321,27 @@ private void HandleScreenState(KeyboardStateSnapshot keyboard) _toggleDockedMode = toggleDockedMode; - if (ConfigurationState.Instance.Hid.EnableMouse.Value) + if (_isMouseInClient) { - if (_isMouseInClient) + if (ConfigurationState.Instance.Hid.EnableMouse.Value) { Window.Cursor = _invisibleCursor; } - } - else - { - switch (_hideCursorMode) + else { - case HideCursorMode.OnIdle: - long cursorMoveDelta = Stopwatch.GetTimestamp() - _lastCursorMoveTime; - Window.Cursor = (cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency) ? _invisibleCursor : null; - break; - case HideCursorMode.Always: - Window.Cursor = _invisibleCursor; - break; - case HideCursorMode.Never: - Window.Cursor = null; - break; + switch (_hideCursorMode) + { + case HideCursorMode.OnIdle: + long cursorMoveDelta = Stopwatch.GetTimestamp() - _lastCursorMoveTime; + Window.Cursor = (cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency) ? _invisibleCursor : null; + break; + case HideCursorMode.Always: + Window.Cursor = _invisibleCursor; + break; + case HideCursorMode.Never: + Window.Cursor = null; + break; + } } } }