Compare commits

..

10 Commits

Author SHA1 Message Date
f4539c49d8 [Logger] Add print with stacktrace method (#5129)
* Add print with stacktrace method

* Adjust logging namespaces

* Add static keyword to DynamicObjectFormatter
2023-06-01 13:47:53 +00:00
12c62fdbc2 nuget: bump DynamicData from 7.13.8 to 7.14.2 (#5148)
Bumps [DynamicData](https://github.com/reactiveui/DynamicData) from 7.13.8 to 7.14.2.
- [Release notes](https://github.com/reactiveui/DynamicData/releases)
- [Changelog](https://github.com/reactivemarbles/DynamicData/blob/main/ReleaseNotes.md)
- [Commits](https://github.com/reactiveui/DynamicData/compare/7.13.8...7.14.2)

---
updated-dependencies:
- dependency-name: DynamicData
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-01 13:35:04 +00:00
e3c6be5e29 Only run one workflow for a PR at a time (#5137) 2023-06-01 09:42:49 +02:00
4741a05df9 Vulkan: Include DepthMode in ProgramPipelineState (#5185) 2023-06-01 09:05:39 +02:00
c6676007bf GPU: Dispose Renderer after running deferred actions (#5144)
* GAL: Dispose Renderer after running deferred actions

Deferred actions from disposing physical memory instances always dispose the resources in their caches. The renderer can't be disposed before these resources get disposed, otherwise the dispose actions will not actually run, and the ThreadedRenderer may get stuck trying to enqueue too many commands when there is nothing consuming them.

This should fix most instances of the emulator freezing on close.

* Wait for main render commands to finish, but keep RenderThread alive til dispose

* Address some feedback.

* No parameterize needed

* Set thread name as part of constructor

* Port to Ava and SDL2
2023-05-31 21:43:20 +00:00
92b0b7d753 Avalonia UI: Fix letter "x" in Ryujinx logo being cut off (#5176)
Also make the pronunciation center-aligned
2023-05-31 21:03:11 +00:00
232237bf28 Skip draws with zero vertex count (#5149) 2023-05-31 17:51:11 -03:00
c27e453fd3 Share ResourceManager vertex vertex A and B shaders (#5181) 2023-05-31 17:17:50 -03:00
0e037d0213 macOS Headless Fixes (#5167)
* Default hypervisor to disabled

* Include MVK on macOS

* Properly sign headless builds on macOS

* Force Vulkan on macOS

* Suggestions
2023-05-31 09:08:50 +02:00
0dca1fbe12 Add Context Menu Option to Run Application (#5154) 2023-05-30 19:51:03 +01:00
35 changed files with 195 additions and 86 deletions

View File

@ -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

View File

@ -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" />

View File

@ -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);

View File

@ -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",

View File

@ -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}"

View File

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

View File

@ -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

View File

@ -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
} }
} }
} }
} }

View File

@ -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);

View File

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

View File

@ -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

View File

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

View File

@ -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
{ {

View File

@ -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();
} }
} }
} }

View File

@ -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
{ {

View File

@ -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
{ {

View File

@ -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
{ {

View File

@ -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();
} }

View File

@ -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)
{ {

View File

@ -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;

View File

@ -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>

View File

@ -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();
} }
} }
} }

View File

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

View File

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

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

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

View File

@ -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();

View File

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

View File

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

View File

@ -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>

View File

@ -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();

View File

@ -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

View File

@ -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();