Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
f4539c49d8 | |||
12c62fdbc2 | |||
e3c6be5e29 | |||
4741a05df9 | |||
c6676007bf | |||
92b0b7d753 | |||
232237bf28 | |||
c27e453fd3 | |||
0e037d0213 | |||
0dca1fbe12 |
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@ -18,6 +18,10 @@ on:
|
|||||||
- '*.yml'
|
- '*.yml'
|
||||||
- 'README.md'
|
- 'README.md'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: pr-checks-${{ github.event.number }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
env:
|
env:
|
||||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||||
<PackageVersion Include="Concentus" Version="1.1.7" />
|
<PackageVersion Include="Concentus" Version="1.1.7" />
|
||||||
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
|
<PackageVersion Include="DiscordRichPresence" Version="1.1.3.18" />
|
||||||
<PackageVersion Include="DynamicData" Version="7.13.8" />
|
<PackageVersion Include="DynamicData" Version="7.14.2" />
|
||||||
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
|
<PackageVersion Include="FluentAvaloniaUI" Version="1.4.5" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
|
||||||
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
||||||
|
@ -92,6 +92,8 @@ namespace Ryujinx.Ava
|
|||||||
private bool _isActive;
|
private bool _isActive;
|
||||||
private bool _renderingStarted;
|
private bool _renderingStarted;
|
||||||
|
|
||||||
|
private ManualResetEvent _gpuDoneEvent;
|
||||||
|
|
||||||
private IRenderer _renderer;
|
private IRenderer _renderer;
|
||||||
private readonly Thread _renderingThread;
|
private readonly Thread _renderingThread;
|
||||||
private readonly CancellationTokenSource _gpuCancellationTokenSource;
|
private readonly CancellationTokenSource _gpuCancellationTokenSource;
|
||||||
@ -183,6 +185,7 @@ namespace Ryujinx.Ava
|
|||||||
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState;
|
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState;
|
||||||
|
|
||||||
_gpuCancellationTokenSource = new CancellationTokenSource();
|
_gpuCancellationTokenSource = new CancellationTokenSource();
|
||||||
|
_gpuDoneEvent = new ManualResetEvent(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TopLevel_PointerEnterOrMoved(object sender, PointerEventArgs e)
|
private void TopLevel_PointerEnterOrMoved(object sender, PointerEventArgs e)
|
||||||
@ -423,10 +426,10 @@ namespace Ryujinx.Ava
|
|||||||
|
|
||||||
_isActive = false;
|
_isActive = false;
|
||||||
|
|
||||||
if (_renderingThread.IsAlive)
|
// NOTE: The render loop is allowed to stay alive until the renderer itself is disposed, as it may handle resource dispose.
|
||||||
{
|
// We only need to wait for all commands submitted during the main gpu loop to be processed.
|
||||||
_renderingThread.Join();
|
_gpuDoneEvent.WaitOne();
|
||||||
}
|
_gpuDoneEvent.Dispose();
|
||||||
|
|
||||||
DisplaySleep.Restore();
|
DisplaySleep.Restore();
|
||||||
|
|
||||||
@ -917,6 +920,14 @@ namespace Ryujinx.Ava
|
|||||||
UpdateStatus();
|
UpdateStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure all commands in the run loop are fully executed before leaving the loop.
|
||||||
|
if (Device.Gpu.Renderer is ThreadedRenderer threaded)
|
||||||
|
{
|
||||||
|
threaded.FlushThreadedCommands();
|
||||||
|
}
|
||||||
|
|
||||||
|
_gpuDoneEvent.Set();
|
||||||
});
|
});
|
||||||
|
|
||||||
(_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(null);
|
(_rendererHost.EmbeddedWindow as EmbeddedWindowOpenGL)?.MakeCurrent(null);
|
||||||
|
@ -289,6 +289,7 @@
|
|||||||
"ControllerSettingsSaveProfileToolTip": "Save Profile",
|
"ControllerSettingsSaveProfileToolTip": "Save Profile",
|
||||||
"MenuBarFileToolsTakeScreenshot": "Take Screenshot",
|
"MenuBarFileToolsTakeScreenshot": "Take Screenshot",
|
||||||
"MenuBarFileToolsHideUi": "Hide UI",
|
"MenuBarFileToolsHideUi": "Hide UI",
|
||||||
|
"GameListContextMenuRunApplication": "Run Application",
|
||||||
"GameListContextMenuToggleFavorite": "Toggle Favorite",
|
"GameListContextMenuToggleFavorite": "Toggle Favorite",
|
||||||
"GameListContextMenuToggleFavoriteToolTip": "Toggle Favorite status of Game",
|
"GameListContextMenuToggleFavoriteToolTip": "Toggle Favorite status of Game",
|
||||||
"SettingsTabGeneralTheme": "Theme",
|
"SettingsTabGeneralTheme": "Theme",
|
||||||
@ -627,7 +628,7 @@
|
|||||||
"Search": "Search",
|
"Search": "Search",
|
||||||
"UserProfilesRecoverLostAccounts": "Recover Lost Accounts",
|
"UserProfilesRecoverLostAccounts": "Recover Lost Accounts",
|
||||||
"Recover": "Recover",
|
"Recover": "Recover",
|
||||||
"UserProfilesRecoverHeading" : "Saves were found for the following accounts",
|
"UserProfilesRecoverHeading": "Saves were found for the following accounts",
|
||||||
"UserProfilesRecoverEmptyList": "No profiles to recover",
|
"UserProfilesRecoverEmptyList": "No profiles to recover",
|
||||||
"GraphicsAATooltip": "Applies anti-aliasing to the game render",
|
"GraphicsAATooltip": "Applies anti-aliasing to the game render",
|
||||||
"GraphicsAALabel": "Anti-Aliasing:",
|
"GraphicsAALabel": "Anti-Aliasing:",
|
||||||
@ -639,8 +640,8 @@
|
|||||||
"SmaaMedium": "SMAA Medium",
|
"SmaaMedium": "SMAA Medium",
|
||||||
"SmaaHigh": "SMAA High",
|
"SmaaHigh": "SMAA High",
|
||||||
"SmaaUltra": "SMAA Ultra",
|
"SmaaUltra": "SMAA Ultra",
|
||||||
"UserEditorTitle" : "Edit User",
|
"UserEditorTitle": "Edit User",
|
||||||
"UserEditorTitleCreate" : "Create User",
|
"UserEditorTitleCreate": "Create User",
|
||||||
"SettingsTabNetworkInterface": "Network Interface:",
|
"SettingsTabNetworkInterface": "Network Interface:",
|
||||||
"NetworkInterfaceTooltip": "The network interface used for LAN features",
|
"NetworkInterfaceTooltip": "The network interface used for LAN features",
|
||||||
"NetworkInterfaceDefault": "Default",
|
"NetworkInterfaceDefault": "Default",
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
xmlns="https://github.com/avaloniaui"
|
xmlns="https://github.com/avaloniaui"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale">
|
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale">
|
||||||
|
<MenuItem
|
||||||
|
Click="RunApplication_Click"
|
||||||
|
Header="{locale:Locale GameListContextMenuRunApplication}" />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
Click="ToggleFavorite_Click"
|
Click="ToggleFavorite_Click"
|
||||||
Header="{locale:Locale GameListContextMenuToggleFavorite}"
|
Header="{locale:Locale GameListContextMenuToggleFavorite}"
|
||||||
|
@ -323,5 +323,15 @@ namespace Ryujinx.Ava.UI.Controls
|
|||||||
await ApplicationHelper.ExtractSection(NcaSectionType.Logo, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName);
|
await ApplicationHelper.ExtractSection(NcaSectionType.Logo, viewModel.SelectedApplication.Path, viewModel.SelectedApplication.TitleName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void RunApplication_Click(object sender, RoutedEventArgs args)
|
||||||
|
{
|
||||||
|
var viewModel = (sender as MenuItem)?.DataContext as MainWindowViewModel;
|
||||||
|
|
||||||
|
if (viewModel?.SelectedApplication != null)
|
||||||
|
{
|
||||||
|
viewModel.LoadApplication(viewModel.SelectedApplication.Path);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -58,11 +58,20 @@
|
|||||||
JustifyContent="SpaceAround"
|
JustifyContent="SpaceAround"
|
||||||
RowSpacing="2">
|
RowSpacing="2">
|
||||||
<TextBlock
|
<TextBlock
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
FontSize="28"
|
FontSize="28"
|
||||||
FontWeight="Bold"
|
FontWeight="Bold"
|
||||||
Text="Ryujinx"
|
Text="Ryujinx"
|
||||||
TextAlignment="Left" />
|
TextAlignment="Center"
|
||||||
<TextBlock Text="(REE-YOU-JINX)" TextAlignment="Left" />
|
Width="100" />
|
||||||
|
<TextBlock
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
FontSize="11"
|
||||||
|
Text="(REE-YOU-JINX)"
|
||||||
|
TextAlignment="Center"
|
||||||
|
Width="100" />
|
||||||
</flex:FlexPanel>
|
</flex:FlexPanel>
|
||||||
</Grid>
|
</Grid>
|
||||||
<TextBlock
|
<TextBlock
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System.Text;
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging.Formatters
|
||||||
{
|
{
|
||||||
internal class DefaultLogFormatter : ILogFormatter
|
internal class DefaultLogFormatter : ILogFormatter
|
||||||
{
|
{
|
||||||
@ -27,6 +28,14 @@ namespace Ryujinx.Common.Logging
|
|||||||
|
|
||||||
if (args.Data is not null)
|
if (args.Data is not null)
|
||||||
{
|
{
|
||||||
|
if (args.Data is StackTrace trace)
|
||||||
|
{
|
||||||
|
sb.Append('\n');
|
||||||
|
sb.Append(trace);
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
sb.Append(' ');
|
sb.Append(' ');
|
||||||
DynamicObjectFormatter.Format(sb, args.Data);
|
DynamicObjectFormatter.Format(sb, args.Data);
|
||||||
}
|
}
|
||||||
@ -39,4 +48,4 @@ namespace Ryujinx.Common.Logging
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,9 +3,9 @@ using System;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging.Formatters
|
||||||
{
|
{
|
||||||
internal class DynamicObjectFormatter
|
internal static class DynamicObjectFormatter
|
||||||
{
|
{
|
||||||
private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>();
|
private static readonly ObjectPool<StringBuilder> StringBuilderPool = SharedPools.Default<StringBuilder>();
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ namespace Ryujinx.Common.Logging
|
|||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder sb = StringBuilderPool.Allocate();
|
StringBuilder sb = StringBuilderPool.Allocate();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Format(sb, dynamicObject);
|
Format(sb, dynamicObject);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging.Formatters
|
||||||
{
|
{
|
||||||
interface ILogFormatter
|
interface ILogFormatter
|
||||||
{
|
{
|
||||||
string Format(LogEventArgs args);
|
string Format(LogEventArgs args);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using Ryujinx.Common.Logging.Formatters;
|
||||||
|
using System;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
using Ryujinx.Common.Logging.Targets;
|
||||||
using Ryujinx.Common.SystemInterop;
|
using Ryujinx.Common.SystemInterop;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -55,6 +56,16 @@ namespace Ryujinx.Common.Logging
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[StackTraceHidden]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void PrintStack(LogClass logClass, string message, [CallerMemberName] string caller = "")
|
||||||
|
{
|
||||||
|
if (m_EnabledClasses[(int)logClass])
|
||||||
|
{
|
||||||
|
Updated?.Invoke(null, new LogEventArgs(Level, m_Time.Elapsed, Thread.CurrentThread.Name, FormatMessage(logClass, caller, message), new StackTrace(true)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void PrintStub(LogClass logClass, string message = "", [CallerMemberName] string caller = "")
|
public void PrintStub(LogClass logClass, string message = "", [CallerMemberName] string caller = "")
|
||||||
{
|
{
|
||||||
@ -122,7 +133,7 @@ namespace Ryujinx.Common.Logging
|
|||||||
AsyncLogTargetOverflowAction.Discard));
|
AsyncLogTargetOverflowAction.Discard));
|
||||||
|
|
||||||
Notice = new Log(LogLevel.Notice);
|
Notice = new Log(LogLevel.Notice);
|
||||||
|
|
||||||
// Enable important log levels before configuration is loaded
|
// Enable important log levels before configuration is loaded
|
||||||
Error = new Log(LogLevel.Error);
|
Error = new Log(LogLevel.Error);
|
||||||
Warning = new Log(LogLevel.Warning);
|
Warning = new Log(LogLevel.Warning);
|
||||||
@ -221,4 +232,4 @@ namespace Ryujinx.Common.Logging
|
|||||||
m_EnabledClasses[(int)logClass] = enabled;
|
m_EnabledClasses[(int)logClass] = enabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,7 +2,7 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging.Targets
|
||||||
{
|
{
|
||||||
public enum AsyncLogTargetOverflowAction
|
public enum AsyncLogTargetOverflowAction
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using Ryujinx.Common.Logging.Formatters;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging.Targets
|
||||||
{
|
{
|
||||||
public class ConsoleLogTarget : ILogTarget
|
public class ConsoleLogTarget : ILogTarget
|
||||||
{
|
{
|
||||||
@ -38,4 +39,4 @@ namespace Ryujinx.Common.Logging
|
|||||||
Console.ResetColor();
|
Console.ResetColor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,8 +1,9 @@
|
|||||||
using System;
|
using Ryujinx.Common.Logging.Formatters;
|
||||||
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging.Targets
|
||||||
{
|
{
|
||||||
public class FileLogTarget : ILogTarget
|
public class FileLogTarget : ILogTarget
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging.Targets
|
||||||
{
|
{
|
||||||
public interface ILogTarget : IDisposable
|
public interface ILogTarget : IDisposable
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace Ryujinx.Common.Logging
|
namespace Ryujinx.Common.Logging.Targets
|
||||||
{
|
{
|
||||||
public class JsonLogTarget : ILogTarget
|
public class JsonLogTarget : ILogTarget
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Common.Configuration;
|
using Ryujinx.Common.Configuration;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Graphics.GAL
|
namespace Ryujinx.Graphics.GAL
|
||||||
{
|
{
|
||||||
@ -52,7 +53,7 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
|
|
||||||
void ResetCounter(CounterType type);
|
void ResetCounter(CounterType type);
|
||||||
|
|
||||||
void RunLoop(Action gpuLoop)
|
void RunLoop(ThreadStart gpuLoop)
|
||||||
{
|
{
|
||||||
gpuLoop();
|
gpuLoop();
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
private IRenderer _baseRenderer;
|
private IRenderer _baseRenderer;
|
||||||
private Thread _gpuThread;
|
private Thread _gpuThread;
|
||||||
private Thread _backendThread;
|
private Thread _backendThread;
|
||||||
private bool _disposed;
|
|
||||||
private bool _running;
|
private bool _running;
|
||||||
|
|
||||||
private AutoResetEvent _frameComplete = new AutoResetEvent(true);
|
private AutoResetEvent _frameComplete = new AutoResetEvent(true);
|
||||||
@ -98,19 +97,17 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
_refQueue = new object[MaxRefsPerCommand * QueueCount];
|
_refQueue = new object[MaxRefsPerCommand * QueueCount];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RunLoop(Action gpuLoop)
|
public void RunLoop(ThreadStart gpuLoop)
|
||||||
{
|
{
|
||||||
_running = true;
|
_running = true;
|
||||||
|
|
||||||
_backendThread = Thread.CurrentThread;
|
_backendThread = Thread.CurrentThread;
|
||||||
|
|
||||||
_gpuThread = new Thread(() => {
|
_gpuThread = new Thread(gpuLoop)
|
||||||
gpuLoop();
|
{
|
||||||
_running = false;
|
Name = "GPU.MainThread"
|
||||||
_galWorkAvailable.Set();
|
};
|
||||||
});
|
|
||||||
|
|
||||||
_gpuThread.Name = "GPU.MainThread";
|
|
||||||
_gpuThread.Start();
|
_gpuThread.Start();
|
||||||
|
|
||||||
RenderLoop();
|
RenderLoop();
|
||||||
@ -120,7 +117,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
{
|
{
|
||||||
// Power through the render queue until the Gpu thread work is done.
|
// Power through the render queue until the Gpu thread work is done.
|
||||||
|
|
||||||
while (_running && !_disposed)
|
while (_running)
|
||||||
{
|
{
|
||||||
_galWorkAvailable.Wait();
|
_galWorkAvailable.Wait();
|
||||||
_galWorkAvailable.Reset();
|
_galWorkAvailable.Reset();
|
||||||
@ -488,12 +485,23 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
|||||||
return _baseRenderer.PrepareHostMapping(address, size);
|
return _baseRenderer.PrepareHostMapping(address, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void FlushThreadedCommands()
|
||||||
|
{
|
||||||
|
SpinWait wait = new();
|
||||||
|
|
||||||
|
while (Volatile.Read(ref _commandCount) > 0)
|
||||||
|
{
|
||||||
|
wait.SpinOnce();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
// Dispose must happen from the render thread, after all commands have completed.
|
// Dispose must happen from the render thread, after all commands have completed.
|
||||||
|
|
||||||
// Stop the GPU thread.
|
// Stop the GPU thread.
|
||||||
_disposed = true;
|
_running = false;
|
||||||
|
_galWorkAvailable.Set();
|
||||||
|
|
||||||
if (_gpuThread != null && _gpuThread.IsAlive)
|
if (_gpuThread != null && _gpuThread.IsAlive)
|
||||||
{
|
{
|
||||||
|
@ -63,6 +63,8 @@ namespace Ryujinx.Graphics.GAL
|
|||||||
public bool PrimitiveRestartEnable;
|
public bool PrimitiveRestartEnable;
|
||||||
public uint PatchControlPoints;
|
public uint PatchControlPoints;
|
||||||
|
|
||||||
|
public DepthMode DepthMode;
|
||||||
|
|
||||||
public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
|
public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
|
||||||
{
|
{
|
||||||
VertexAttribCount = vertexAttribs.Length;
|
VertexAttribCount = vertexAttribs.Length;
|
||||||
|
@ -771,7 +771,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateDepthMode()
|
private void UpdateDepthMode()
|
||||||
{
|
{
|
||||||
_context.Renderer.Pipeline.SetDepthMode(GetDepthMode());
|
DepthMode mode = GetDepthMode();
|
||||||
|
|
||||||
|
_pipeline.DepthMode = mode;
|
||||||
|
|
||||||
|
_context.Renderer.Pipeline.SetDepthMode(mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -390,7 +390,6 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Renderer.Dispose();
|
|
||||||
GPFifo.Dispose();
|
GPFifo.Dispose();
|
||||||
HostInitalized.Dispose();
|
HostInitalized.Dispose();
|
||||||
|
|
||||||
@ -403,6 +402,8 @@ namespace Ryujinx.Graphics.Gpu
|
|||||||
PhysicalMemoryRegistry.Clear();
|
PhysicalMemoryRegistry.Clear();
|
||||||
|
|
||||||
RunDeferredActions();
|
RunDeferredActions();
|
||||||
|
|
||||||
|
Renderer.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -17,8 +17,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
private readonly ResourceCounts _resourceCounts;
|
private readonly ResourceCounts _resourceCounts;
|
||||||
private readonly int _stageIndex;
|
private readonly int _stageIndex;
|
||||||
|
|
||||||
private readonly int[] _constantBufferBindings;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a new GPU accessor.
|
/// Creates a new GPU accessor.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -28,12 +26,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
_context = context;
|
_context = context;
|
||||||
_resourceCounts = resourceCounts;
|
_resourceCounts = resourceCounts;
|
||||||
_stageIndex = stageIndex;
|
_stageIndex = stageIndex;
|
||||||
|
|
||||||
if (context.Capabilities.Api != TargetApi.Vulkan)
|
|
||||||
{
|
|
||||||
_constantBufferBindings = new int[Constants.TotalGpUniformBuffers];
|
|
||||||
_constantBufferBindings.AsSpan().Fill(-1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int QueryBindingConstantBuffer(int index)
|
public int QueryBindingConstantBuffer(int index)
|
||||||
@ -45,15 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int binding = _constantBufferBindings[index];
|
return _resourceCounts.UniformBuffersCount++;
|
||||||
|
|
||||||
if (binding < 0)
|
|
||||||
{
|
|
||||||
binding = _resourceCounts.UniformBuffersCount++;
|
|
||||||
_constantBufferBindings[index] = binding;
|
|
||||||
}
|
|
||||||
|
|
||||||
return binding;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -736,6 +736,19 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
return MatchesTexture(specializationState, descriptor);
|
return MatchesTexture(specializationState, descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Populates pipeline state that doesn't exist in older caches with default values
|
||||||
|
/// based on specialization state.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pipelineState">Pipeline state to prepare</param>
|
||||||
|
private void PreparePipelineState(ref ProgramPipelineState pipelineState)
|
||||||
|
{
|
||||||
|
if (!_compute)
|
||||||
|
{
|
||||||
|
pipelineState.DepthMode = GraphicsState.DepthMode ? DepthMode.MinusOneToOne : DepthMode.ZeroToOne;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Reads shader specialization state that has been serialized.
|
/// Reads shader specialization state that has been serialized.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -776,6 +789,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
|||||||
{
|
{
|
||||||
ProgramPipelineState pipelineState = default;
|
ProgramPipelineState pipelineState = default;
|
||||||
dataReader.ReadWithMagicAndSize(ref pipelineState, PgpsMagic);
|
dataReader.ReadWithMagicAndSize(ref pipelineState, PgpsMagic);
|
||||||
|
|
||||||
|
specState.PreparePipelineState(ref pipelineState);
|
||||||
specState.PipelineState = pipelineState;
|
specState.PipelineState = pipelineState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
private readonly HashSet<int> _usedConstantBufferBindings;
|
private readonly HashSet<int> _usedConstantBufferBindings;
|
||||||
|
|
||||||
|
public ShaderProperties Properties => _properties;
|
||||||
|
|
||||||
public ResourceManager(ShaderStage stage, IGpuAccessor gpuAccessor, ShaderProperties properties)
|
public ResourceManager(ShaderStage stage, IGpuAccessor gpuAccessor, ShaderProperties properties)
|
||||||
{
|
{
|
||||||
_gpuAccessor = gpuAccessor;
|
_gpuAccessor = gpuAccessor;
|
||||||
@ -98,19 +100,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
_properties.AddConstantBuffer(binding, new BufferDefinition(BufferLayout.Std140, 0, binding, name, type));
|
_properties.AddConstantBuffer(binding, new BufferDefinition(BufferLayout.Std140, 0, binding, name, type));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InheritFrom(ResourceManager other)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < other._cbSlotToBindingMap.Length; i++)
|
|
||||||
{
|
|
||||||
int binding = other._cbSlotToBindingMap[i];
|
|
||||||
|
|
||||||
if (binding >= 0)
|
|
||||||
{
|
|
||||||
_cbSlotToBindingMap[i] = binding;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetShaderStagePrefix(ShaderStage stage)
|
public static string GetShaderStagePrefix(ShaderStage stage)
|
||||||
{
|
{
|
||||||
uint index = (uint)stage;
|
uint index = (uint)stage;
|
||||||
|
@ -39,9 +39,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
public TranslationOptions Options { get; }
|
public TranslationOptions Options { get; }
|
||||||
|
|
||||||
public ShaderProperties Properties { get; }
|
public ShaderProperties Properties => ResourceManager.Properties;
|
||||||
|
|
||||||
public ResourceManager ResourceManager { get; }
|
public ResourceManager ResourceManager { get; set; }
|
||||||
|
|
||||||
public bool TransformFeedbackEnabled { get; }
|
public bool TransformFeedbackEnabled { get; }
|
||||||
|
|
||||||
@ -159,8 +159,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
_sbSlots = new Dictionary<int, int>();
|
_sbSlots = new Dictionary<int, int>();
|
||||||
_sbSlotsReverse = new Dictionary<int, int>();
|
_sbSlotsReverse = new Dictionary<int, int>();
|
||||||
|
|
||||||
Properties = new ShaderProperties();
|
ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties());
|
||||||
ResourceManager = new ResourceManager(stage, gpuAccessor, Properties);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShaderConfig(
|
public ShaderConfig(
|
||||||
@ -429,8 +428,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
|
|
||||||
public void InheritFrom(ShaderConfig other)
|
public void InheritFrom(ShaderConfig other)
|
||||||
{
|
{
|
||||||
ResourceManager.InheritFrom(other.ResourceManager);
|
|
||||||
|
|
||||||
ClipDistancesWritten |= other.ClipDistancesWritten;
|
ClipDistancesWritten |= other.ClipDistancesWritten;
|
||||||
UsedFeatures |= other.UsedFeatures;
|
UsedFeatures |= other.UsedFeatures;
|
||||||
|
|
||||||
|
@ -155,6 +155,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
|||||||
{
|
{
|
||||||
other._config.MergeOutputUserAttributes(_config.UsedOutputAttributes, Enumerable.Empty<int>());
|
other._config.MergeOutputUserAttributes(_config.UsedOutputAttributes, Enumerable.Empty<int>());
|
||||||
|
|
||||||
|
// We need to share the resource manager since both shaders accesses the same constant buffers.
|
||||||
|
other._config.ResourceManager = _config.ResourceManager;
|
||||||
|
|
||||||
FunctionCode[] otherCode = EmitShader(other._program, other._config, initializeOutputs: true, out int aStart);
|
FunctionCode[] otherCode = EmitShader(other._program, other._config, initializeOutputs: true, out int aStart);
|
||||||
|
|
||||||
code = Combine(otherCode, code, aStart);
|
code = Combine(otherCode, code, aStart);
|
||||||
|
@ -358,7 +358,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance)
|
public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance)
|
||||||
{
|
{
|
||||||
if (!_program.IsLinked)
|
if (!_program.IsLinked || vertexCount == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -422,7 +422,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
|
|
||||||
public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance)
|
public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance)
|
||||||
{
|
{
|
||||||
if (!_program.IsLinked)
|
if (!_program.IsLinked || indexCount == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -165,6 +165,7 @@ namespace Ryujinx.Graphics.Vulkan
|
|||||||
pipeline.DepthTestEnable = state.DepthTest.TestEnable;
|
pipeline.DepthTestEnable = state.DepthTest.TestEnable;
|
||||||
pipeline.DepthWriteEnable = state.DepthTest.WriteEnable;
|
pipeline.DepthWriteEnable = state.DepthTest.WriteEnable;
|
||||||
pipeline.DepthCompareOp = state.DepthTest.Func.Convert();
|
pipeline.DepthCompareOp = state.DepthTest.Func.Convert();
|
||||||
|
pipeline.DepthMode = state.DepthMode == DepthMode.MinusOneToOne;
|
||||||
|
|
||||||
pipeline.FrontFace = state.FrontFace.Convert();
|
pipeline.FrontFace = state.FrontFace.Convert();
|
||||||
|
|
||||||
|
@ -130,7 +130,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
public float AudioVolume { get; set; }
|
public float AudioVolume { get; set; }
|
||||||
|
|
||||||
[Option("use-hypervisor", Required = false, Default = true, HelpText = "Uses Hypervisor over JIT if available.")]
|
[Option("use-hypervisor", Required = false, Default = true, HelpText = "Uses Hypervisor over JIT if available.")]
|
||||||
public bool UseHypervisor { get; set; }
|
public bool? UseHypervisor { get; set; }
|
||||||
|
|
||||||
[Option("lan-interface-id", Required = false, Default = "0", HelpText = "GUID for the network interface used by LAN.")]
|
[Option("lan-interface-id", Required = false, Default = "0", HelpText = "GUID for the network interface used by LAN.")]
|
||||||
public string MultiplayerLanInterfaceId { get; set; }
|
public string MultiplayerLanInterfaceId { get; set; }
|
||||||
|
@ -9,6 +9,7 @@ using Ryujinx.Common.Configuration.Hid.Controller;
|
|||||||
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
|
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
|
||||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Logging.Targets;
|
||||||
using Ryujinx.Common.SystemInterop;
|
using Ryujinx.Common.SystemInterop;
|
||||||
using Ryujinx.Common.Utilities;
|
using Ryujinx.Common.Utilities;
|
||||||
using Ryujinx.Cpu;
|
using Ryujinx.Cpu;
|
||||||
@ -339,6 +340,15 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
|
|
||||||
GraphicsConfig.EnableShaderCache = true;
|
GraphicsConfig.EnableShaderCache = true;
|
||||||
|
|
||||||
|
if (OperatingSystem.IsMacOS())
|
||||||
|
{
|
||||||
|
if (option.GraphicsBackend == GraphicsBackend.OpenGl)
|
||||||
|
{
|
||||||
|
option.GraphicsBackend = GraphicsBackend.Vulkan;
|
||||||
|
Logger.Warning?.Print(LogClass.Application, "OpenGL is not supported on macOS, switching to Vulkan!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IGamepad gamepad;
|
IGamepad gamepad;
|
||||||
|
|
||||||
if (option.ListInputIds)
|
if (option.ListInputIds)
|
||||||
@ -550,7 +560,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
options.IgnoreMissingServices,
|
options.IgnoreMissingServices,
|
||||||
options.AspectRatio,
|
options.AspectRatio,
|
||||||
options.AudioVolume,
|
options.AudioVolume,
|
||||||
options.UseHypervisor,
|
options.UseHypervisor ?? true,
|
||||||
options.MultiplayerLanInterfaceId);
|
options.MultiplayerLanInterfaceId);
|
||||||
|
|
||||||
return new Switch(configuration);
|
return new Switch(configuration);
|
||||||
@ -703,4 +713,4 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,6 +7,7 @@
|
|||||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
<Version>1.0.0-dirty</Version>
|
<Version>1.0.0-dirty</Version>
|
||||||
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
|
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
|
||||||
|
<SigningCertificate Condition=" '$(SigningCertificate)' == '' ">-</SigningCertificate>
|
||||||
<TieredPGO>true</TieredPGO>
|
<TieredPGO>true</TieredPGO>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
@ -15,6 +16,10 @@
|
|||||||
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" />
|
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="$([MSBuild]::IsOSPlatform('OSX'))">
|
||||||
|
<Exec Command="codesign --entitlements '$(ProjectDir)..\..\distribution\macos\entitlements.xml' -f --deep -s $(SigningCertificate) '$(TargetDir)$(TargetName)'" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" />
|
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" />
|
||||||
<ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" />
|
<ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" />
|
||||||
@ -29,6 +34,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommandLineParser" />
|
<PackageReference Include="CommandLineParser" />
|
||||||
|
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win10-x64'" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -63,4 +69,4 @@
|
|||||||
<PublishTrimmed>true</PublishTrimmed>
|
<PublishTrimmed>true</PublishTrimmed>
|
||||||
<TrimMode>partial</TrimMode>
|
<TrimMode>partial</TrimMode>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
@ -62,6 +62,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
private readonly long _ticksPerFrame;
|
private readonly long _ticksPerFrame;
|
||||||
private readonly CancellationTokenSource _gpuCancellationTokenSource;
|
private readonly CancellationTokenSource _gpuCancellationTokenSource;
|
||||||
private readonly ManualResetEvent _exitEvent;
|
private readonly ManualResetEvent _exitEvent;
|
||||||
|
private readonly ManualResetEvent _gpuDoneEvent;
|
||||||
|
|
||||||
private long _ticks;
|
private long _ticks;
|
||||||
private bool _isActive;
|
private bool _isActive;
|
||||||
@ -91,6 +92,7 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
_ticksPerFrame = Stopwatch.Frequency / TargetFps;
|
_ticksPerFrame = Stopwatch.Frequency / TargetFps;
|
||||||
_gpuCancellationTokenSource = new CancellationTokenSource();
|
_gpuCancellationTokenSource = new CancellationTokenSource();
|
||||||
_exitEvent = new ManualResetEvent(false);
|
_exitEvent = new ManualResetEvent(false);
|
||||||
|
_gpuDoneEvent = new ManualResetEvent(false);
|
||||||
_aspectRatio = aspectRatio;
|
_aspectRatio = aspectRatio;
|
||||||
_enableMouse = enableMouse;
|
_enableMouse = enableMouse;
|
||||||
HostUiTheme = new HeadlessHostUiTheme();
|
HostUiTheme = new HeadlessHostUiTheme();
|
||||||
@ -275,6 +277,14 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
_ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame);
|
_ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure all commands in the run loop are fully executed before leaving the loop.
|
||||||
|
if (Device.Gpu.Renderer is ThreadedRenderer threaded)
|
||||||
|
{
|
||||||
|
threaded.FlushThreadedCommands();
|
||||||
|
}
|
||||||
|
|
||||||
|
_gpuDoneEvent.Set();
|
||||||
});
|
});
|
||||||
|
|
||||||
FinalizeWindowRenderer();
|
FinalizeWindowRenderer();
|
||||||
@ -404,7 +414,10 @@ namespace Ryujinx.Headless.SDL2
|
|||||||
|
|
||||||
MainLoop();
|
MainLoop();
|
||||||
|
|
||||||
renderLoopThread.Join();
|
// NOTE: The render loop is allowed to stay alive until the renderer itself is disposed, as it may handle resource dispose.
|
||||||
|
// We only need to wait for all commands submitted during the main gpu loop to be processed.
|
||||||
|
_gpuDoneEvent.WaitOne();
|
||||||
|
_gpuDoneEvent.Dispose();
|
||||||
nvStutterWorkaround?.Join();
|
nvStutterWorkaround?.Join();
|
||||||
|
|
||||||
Exit();
|
Exit();
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Common.Logging.Targets;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.Ui.Common.Configuration
|
namespace Ryujinx.Ui.Common.Configuration
|
||||||
|
@ -65,6 +65,7 @@ namespace Ryujinx.Ui
|
|||||||
private KeyboardHotkeyState _prevHotkeyState;
|
private KeyboardHotkeyState _prevHotkeyState;
|
||||||
|
|
||||||
private readonly ManualResetEvent _exitEvent;
|
private readonly ManualResetEvent _exitEvent;
|
||||||
|
private readonly ManualResetEvent _gpuDoneEvent;
|
||||||
|
|
||||||
private readonly CancellationTokenSource _gpuCancellationTokenSource;
|
private readonly CancellationTokenSource _gpuCancellationTokenSource;
|
||||||
|
|
||||||
@ -110,6 +111,7 @@ namespace Ryujinx.Ui
|
|||||||
| EventMask.KeyReleaseMask));
|
| EventMask.KeyReleaseMask));
|
||||||
|
|
||||||
_exitEvent = new ManualResetEvent(false);
|
_exitEvent = new ManualResetEvent(false);
|
||||||
|
_gpuDoneEvent = new ManualResetEvent(false);
|
||||||
|
|
||||||
_gpuCancellationTokenSource = new CancellationTokenSource();
|
_gpuCancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
@ -499,6 +501,14 @@ namespace Ryujinx.Ui
|
|||||||
_ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame);
|
_ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure all commands in the run loop are fully executed before leaving the loop.
|
||||||
|
if (Device.Gpu.Renderer is ThreadedRenderer threaded)
|
||||||
|
{
|
||||||
|
threaded.FlushThreadedCommands();
|
||||||
|
}
|
||||||
|
|
||||||
|
_gpuDoneEvent.Set();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -542,7 +552,10 @@ namespace Ryujinx.Ui
|
|||||||
|
|
||||||
MainLoop();
|
MainLoop();
|
||||||
|
|
||||||
renderLoopThread.Join();
|
// NOTE: The render loop is allowed to stay alive until the renderer itself is disposed, as it may handle resource dispose.
|
||||||
|
// We only need to wait for all commands submitted during the main gpu loop to be processed.
|
||||||
|
_gpuDoneEvent.WaitOne();
|
||||||
|
_gpuDoneEvent.Dispose();
|
||||||
nvStutterWorkaround?.Join();
|
nvStutterWorkaround?.Join();
|
||||||
|
|
||||||
Exit();
|
Exit();
|
||||||
|
Reference in New Issue
Block a user