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'
|
||||
- 'README.md'
|
||||
|
||||
concurrency:
|
||||
group: pr-checks-${{ github.event.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
POWERSHELL_TELEMETRY_OPTOUT: 1
|
||||
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||
|
@ -13,7 +13,7 @@
|
||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||
<PackageVersion Include="Concentus" Version="1.1.7" />
|
||||
<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="GtkSharp.Dependencies" Version="1.1.1" />
|
||||
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
|
||||
|
@ -92,6 +92,8 @@ namespace Ryujinx.Ava
|
||||
private bool _isActive;
|
||||
private bool _renderingStarted;
|
||||
|
||||
private ManualResetEvent _gpuDoneEvent;
|
||||
|
||||
private IRenderer _renderer;
|
||||
private readonly Thread _renderingThread;
|
||||
private readonly CancellationTokenSource _gpuCancellationTokenSource;
|
||||
@ -183,6 +185,7 @@ namespace Ryujinx.Ava
|
||||
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateLanInterfaceIdState;
|
||||
|
||||
_gpuCancellationTokenSource = new CancellationTokenSource();
|
||||
_gpuDoneEvent = new ManualResetEvent(false);
|
||||
}
|
||||
|
||||
private void TopLevel_PointerEnterOrMoved(object sender, PointerEventArgs e)
|
||||
@ -423,10 +426,10 @@ namespace Ryujinx.Ava
|
||||
|
||||
_isActive = false;
|
||||
|
||||
if (_renderingThread.IsAlive)
|
||||
{
|
||||
_renderingThread.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();
|
||||
|
||||
DisplaySleep.Restore();
|
||||
|
||||
@ -917,6 +920,14 @@ namespace Ryujinx.Ava
|
||||
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);
|
||||
|
@ -289,6 +289,7 @@
|
||||
"ControllerSettingsSaveProfileToolTip": "Save Profile",
|
||||
"MenuBarFileToolsTakeScreenshot": "Take Screenshot",
|
||||
"MenuBarFileToolsHideUi": "Hide UI",
|
||||
"GameListContextMenuRunApplication": "Run Application",
|
||||
"GameListContextMenuToggleFavorite": "Toggle Favorite",
|
||||
"GameListContextMenuToggleFavoriteToolTip": "Toggle Favorite status of Game",
|
||||
"SettingsTabGeneralTheme": "Theme",
|
||||
@ -627,7 +628,7 @@
|
||||
"Search": "Search",
|
||||
"UserProfilesRecoverLostAccounts": "Recover Lost Accounts",
|
||||
"Recover": "Recover",
|
||||
"UserProfilesRecoverHeading" : "Saves were found for the following accounts",
|
||||
"UserProfilesRecoverHeading": "Saves were found for the following accounts",
|
||||
"UserProfilesRecoverEmptyList": "No profiles to recover",
|
||||
"GraphicsAATooltip": "Applies anti-aliasing to the game render",
|
||||
"GraphicsAALabel": "Anti-Aliasing:",
|
||||
@ -639,8 +640,8 @@
|
||||
"SmaaMedium": "SMAA Medium",
|
||||
"SmaaHigh": "SMAA High",
|
||||
"SmaaUltra": "SMAA Ultra",
|
||||
"UserEditorTitle" : "Edit User",
|
||||
"UserEditorTitleCreate" : "Create User",
|
||||
"UserEditorTitle": "Edit User",
|
||||
"UserEditorTitleCreate": "Create User",
|
||||
"SettingsTabNetworkInterface": "Network Interface:",
|
||||
"NetworkInterfaceTooltip": "The network interface used for LAN features",
|
||||
"NetworkInterfaceDefault": "Default",
|
||||
|
@ -3,6 +3,9 @@
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:locale="clr-namespace:Ryujinx.Ava.Common.Locale">
|
||||
<MenuItem
|
||||
Click="RunApplication_Click"
|
||||
Header="{locale:Locale GameListContextMenuRunApplication}" />
|
||||
<MenuItem
|
||||
Click="ToggleFavorite_Click"
|
||||
Header="{locale:Locale GameListContextMenuToggleFavorite}"
|
||||
|
@ -323,5 +323,15 @@ namespace Ryujinx.Ava.UI.Controls
|
||||
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"
|
||||
RowSpacing="2">
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="28"
|
||||
FontWeight="Bold"
|
||||
Text="Ryujinx"
|
||||
TextAlignment="Left" />
|
||||
<TextBlock Text="(REE-YOU-JINX)" TextAlignment="Left" />
|
||||
TextAlignment="Center"
|
||||
Width="100" />
|
||||
<TextBlock
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="11"
|
||||
Text="(REE-YOU-JINX)"
|
||||
TextAlignment="Center"
|
||||
Width="100" />
|
||||
</flex:FlexPanel>
|
||||
</Grid>
|
||||
<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
|
||||
{
|
||||
@ -27,6 +28,14 @@ namespace Ryujinx.Common.Logging
|
||||
|
||||
if (args.Data is not null)
|
||||
{
|
||||
if (args.Data is StackTrace trace)
|
||||
{
|
||||
sb.Append('\n');
|
||||
sb.Append(trace);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
sb.Append(' ');
|
||||
DynamicObjectFormatter.Format(sb, args.Data);
|
||||
}
|
||||
@ -39,4 +48,4 @@ namespace Ryujinx.Common.Logging
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -3,9 +3,9 @@ using System;
|
||||
using System.Reflection;
|
||||
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>();
|
||||
|
||||
@ -17,7 +17,7 @@ namespace Ryujinx.Common.Logging
|
||||
}
|
||||
|
||||
StringBuilder sb = StringBuilderPool.Allocate();
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
Format(sb, dynamicObject);
|
||||
|
@ -1,7 +1,7 @@
|
||||
namespace Ryujinx.Common.Logging
|
||||
namespace Ryujinx.Common.Logging.Formatters
|
||||
{
|
||||
interface ILogFormatter
|
||||
{
|
||||
string Format(LogEventArgs args);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using Ryujinx.Common.Logging.Formatters;
|
||||
using System;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Ryujinx.Common.Logging
|
||||
|
@ -1,3 +1,4 @@
|
||||
using Ryujinx.Common.Logging.Targets;
|
||||
using Ryujinx.Common.SystemInterop;
|
||||
using System;
|
||||
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)]
|
||||
public void PrintStub(LogClass logClass, string message = "", [CallerMemberName] string caller = "")
|
||||
{
|
||||
@ -122,7 +133,7 @@ namespace Ryujinx.Common.Logging
|
||||
AsyncLogTargetOverflowAction.Discard));
|
||||
|
||||
Notice = new Log(LogLevel.Notice);
|
||||
|
||||
|
||||
// Enable important log levels before configuration is loaded
|
||||
Error = new Log(LogLevel.Error);
|
||||
Warning = new Log(LogLevel.Warning);
|
||||
@ -221,4 +232,4 @@ namespace Ryujinx.Common.Logging
|
||||
m_EnabledClasses[(int)logClass] = enabled;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Common.Logging
|
||||
namespace Ryujinx.Common.Logging.Targets
|
||||
{
|
||||
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
|
||||
{
|
||||
@ -38,4 +39,4 @@ namespace Ryujinx.Common.Logging
|
||||
Console.ResetColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using Ryujinx.Common.Logging.Formatters;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.Common.Logging
|
||||
namespace Ryujinx.Common.Logging.Targets
|
||||
{
|
||||
public class FileLogTarget : ILogTarget
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Common.Logging
|
||||
namespace Ryujinx.Common.Logging.Targets
|
||||
{
|
||||
public interface ILogTarget : IDisposable
|
||||
{
|
||||
|
@ -1,7 +1,7 @@
|
||||
using Ryujinx.Common.Utilities;
|
||||
using System.IO;
|
||||
|
||||
namespace Ryujinx.Common.Logging
|
||||
namespace Ryujinx.Common.Logging.Targets
|
||||
{
|
||||
public class JsonLogTarget : ILogTarget
|
||||
{
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Ryujinx.Common.Configuration;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Graphics.GAL
|
||||
{
|
||||
@ -52,7 +53,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
|
||||
void ResetCounter(CounterType type);
|
||||
|
||||
void RunLoop(Action gpuLoop)
|
||||
void RunLoop(ThreadStart gpuLoop)
|
||||
{
|
||||
gpuLoop();
|
||||
}
|
||||
|
@ -30,7 +30,6 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
private IRenderer _baseRenderer;
|
||||
private Thread _gpuThread;
|
||||
private Thread _backendThread;
|
||||
private bool _disposed;
|
||||
private bool _running;
|
||||
|
||||
private AutoResetEvent _frameComplete = new AutoResetEvent(true);
|
||||
@ -98,19 +97,17 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
_refQueue = new object[MaxRefsPerCommand * QueueCount];
|
||||
}
|
||||
|
||||
public void RunLoop(Action gpuLoop)
|
||||
public void RunLoop(ThreadStart gpuLoop)
|
||||
{
|
||||
_running = true;
|
||||
|
||||
_backendThread = Thread.CurrentThread;
|
||||
|
||||
_gpuThread = new Thread(() => {
|
||||
gpuLoop();
|
||||
_running = false;
|
||||
_galWorkAvailable.Set();
|
||||
});
|
||||
_gpuThread = new Thread(gpuLoop)
|
||||
{
|
||||
Name = "GPU.MainThread"
|
||||
};
|
||||
|
||||
_gpuThread.Name = "GPU.MainThread";
|
||||
_gpuThread.Start();
|
||||
|
||||
RenderLoop();
|
||||
@ -120,7 +117,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
{
|
||||
// Power through the render queue until the Gpu thread work is done.
|
||||
|
||||
while (_running && !_disposed)
|
||||
while (_running)
|
||||
{
|
||||
_galWorkAvailable.Wait();
|
||||
_galWorkAvailable.Reset();
|
||||
@ -488,12 +485,23 @@ namespace Ryujinx.Graphics.GAL.Multithreading
|
||||
return _baseRenderer.PrepareHostMapping(address, size);
|
||||
}
|
||||
|
||||
public void FlushThreadedCommands()
|
||||
{
|
||||
SpinWait wait = new();
|
||||
|
||||
while (Volatile.Read(ref _commandCount) > 0)
|
||||
{
|
||||
wait.SpinOnce();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Dispose must happen from the render thread, after all commands have completed.
|
||||
|
||||
// Stop the GPU thread.
|
||||
_disposed = true;
|
||||
_running = false;
|
||||
_galWorkAvailable.Set();
|
||||
|
||||
if (_gpuThread != null && _gpuThread.IsAlive)
|
||||
{
|
||||
|
@ -63,6 +63,8 @@ namespace Ryujinx.Graphics.GAL
|
||||
public bool PrimitiveRestartEnable;
|
||||
public uint PatchControlPoints;
|
||||
|
||||
public DepthMode DepthMode;
|
||||
|
||||
public void SetVertexAttribs(ReadOnlySpan<VertexAttribDescriptor> vertexAttribs)
|
||||
{
|
||||
VertexAttribCount = vertexAttribs.Length;
|
||||
|
@ -771,7 +771,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.Threed
|
||||
/// </summary>
|
||||
private void UpdateDepthMode()
|
||||
{
|
||||
_context.Renderer.Pipeline.SetDepthMode(GetDepthMode());
|
||||
DepthMode mode = GetDepthMode();
|
||||
|
||||
_pipeline.DepthMode = mode;
|
||||
|
||||
_context.Renderer.Pipeline.SetDepthMode(mode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -390,7 +390,6 @@ namespace Ryujinx.Graphics.Gpu
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
Renderer.Dispose();
|
||||
GPFifo.Dispose();
|
||||
HostInitalized.Dispose();
|
||||
|
||||
@ -403,6 +402,8 @@ namespace Ryujinx.Graphics.Gpu
|
||||
PhysicalMemoryRegistry.Clear();
|
||||
|
||||
RunDeferredActions();
|
||||
|
||||
Renderer.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@ -17,8 +17,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
private readonly ResourceCounts _resourceCounts;
|
||||
private readonly int _stageIndex;
|
||||
|
||||
private readonly int[] _constantBufferBindings;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new GPU accessor.
|
||||
/// </summary>
|
||||
@ -28,12 +26,6 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
_context = context;
|
||||
_resourceCounts = resourceCounts;
|
||||
_stageIndex = stageIndex;
|
||||
|
||||
if (context.Capabilities.Api != TargetApi.Vulkan)
|
||||
{
|
||||
_constantBufferBindings = new int[Constants.TotalGpUniformBuffers];
|
||||
_constantBufferBindings.AsSpan().Fill(-1);
|
||||
}
|
||||
}
|
||||
|
||||
public int QueryBindingConstantBuffer(int index)
|
||||
@ -45,15 +37,7 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
}
|
||||
else
|
||||
{
|
||||
int binding = _constantBufferBindings[index];
|
||||
|
||||
if (binding < 0)
|
||||
{
|
||||
binding = _resourceCounts.UniformBuffersCount++;
|
||||
_constantBufferBindings[index] = binding;
|
||||
}
|
||||
|
||||
return binding;
|
||||
return _resourceCounts.UniformBuffersCount++;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -736,6 +736,19 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
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>
|
||||
/// Reads shader specialization state that has been serialized.
|
||||
/// </summary>
|
||||
@ -776,6 +789,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
{
|
||||
ProgramPipelineState pipelineState = default;
|
||||
dataReader.ReadWithMagicAndSize(ref pipelineState, PgpsMagic);
|
||||
|
||||
specState.PreparePipelineState(ref pipelineState);
|
||||
specState.PipelineState = pipelineState;
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,8 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
|
||||
private readonly HashSet<int> _usedConstantBufferBindings;
|
||||
|
||||
public ShaderProperties Properties => _properties;
|
||||
|
||||
public ResourceManager(ShaderStage stage, IGpuAccessor gpuAccessor, ShaderProperties properties)
|
||||
{
|
||||
_gpuAccessor = gpuAccessor;
|
||||
@ -98,19 +100,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
_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)
|
||||
{
|
||||
uint index = (uint)stage;
|
||||
|
@ -39,9 +39,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
|
||||
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; }
|
||||
|
||||
@ -159,8 +159,7 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
_sbSlots = new Dictionary<int, int>();
|
||||
_sbSlotsReverse = new Dictionary<int, int>();
|
||||
|
||||
Properties = new ShaderProperties();
|
||||
ResourceManager = new ResourceManager(stage, gpuAccessor, Properties);
|
||||
ResourceManager = new ResourceManager(stage, gpuAccessor, new ShaderProperties());
|
||||
}
|
||||
|
||||
public ShaderConfig(
|
||||
@ -429,8 +428,6 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
|
||||
public void InheritFrom(ShaderConfig other)
|
||||
{
|
||||
ResourceManager.InheritFrom(other.ResourceManager);
|
||||
|
||||
ClipDistancesWritten |= other.ClipDistancesWritten;
|
||||
UsedFeatures |= other.UsedFeatures;
|
||||
|
||||
|
@ -155,6 +155,9 @@ namespace Ryujinx.Graphics.Shader.Translation
|
||||
{
|
||||
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);
|
||||
|
||||
code = Combine(otherCode, code, aStart);
|
||||
|
@ -358,7 +358,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance)
|
||||
{
|
||||
if (!_program.IsLinked)
|
||||
if (!_program.IsLinked || vertexCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -422,7 +422,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance)
|
||||
{
|
||||
if (!_program.IsLinked)
|
||||
if (!_program.IsLinked || indexCount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -165,6 +165,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
pipeline.DepthTestEnable = state.DepthTest.TestEnable;
|
||||
pipeline.DepthWriteEnable = state.DepthTest.WriteEnable;
|
||||
pipeline.DepthCompareOp = state.DepthTest.Func.Convert();
|
||||
pipeline.DepthMode = state.DepthMode == DepthMode.MinusOneToOne;
|
||||
|
||||
pipeline.FrontFace = state.FrontFace.Convert();
|
||||
|
||||
|
@ -130,7 +130,7 @@ namespace Ryujinx.Headless.SDL2
|
||||
public float AudioVolume { get; set; }
|
||||
|
||||
[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.")]
|
||||
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.Keyboard;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Logging.Targets;
|
||||
using Ryujinx.Common.SystemInterop;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.Cpu;
|
||||
@ -339,6 +340,15 @@ namespace Ryujinx.Headless.SDL2
|
||||
|
||||
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;
|
||||
|
||||
if (option.ListInputIds)
|
||||
@ -550,7 +560,7 @@ namespace Ryujinx.Headless.SDL2
|
||||
options.IgnoreMissingServices,
|
||||
options.AspectRatio,
|
||||
options.AudioVolume,
|
||||
options.UseHypervisor,
|
||||
options.UseHypervisor ?? true,
|
||||
options.MultiplayerLanInterfaceId);
|
||||
|
||||
return new Switch(configuration);
|
||||
@ -703,4 +713,4 @@ namespace Ryujinx.Headless.SDL2
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Version>1.0.0-dirty</Version>
|
||||
<DefineConstants Condition=" '$(ExtraDefineConstants)' != '' ">$(DefineConstants);$(ExtraDefineConstants)</DefineConstants>
|
||||
<SigningCertificate Condition=" '$(SigningCertificate)' == '' ">-</SigningCertificate>
|
||||
<TieredPGO>true</TieredPGO>
|
||||
</PropertyGroup>
|
||||
|
||||
@ -15,6 +16,10 @@
|
||||
<PackageReference Include="Ryujinx.Graphics.Nvdec.Dependencies" />
|
||||
</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>
|
||||
<ProjectReference Include="..\Ryujinx.Graphics.Vulkan\Ryujinx.Graphics.Vulkan.csproj" />
|
||||
<ProjectReference Include="..\Ryujinx.Input\Ryujinx.Input.csproj" />
|
||||
@ -29,6 +34,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommandLineParser" />
|
||||
<PackageReference Include="Ryujinx.Graphics.Vulkan.Dependencies.MoltenVK" Condition="'$(RuntimeIdentifier)' != 'linux-x64' AND '$(RuntimeIdentifier)' != 'win10-x64'" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@ -63,4 +69,4 @@
|
||||
<PublishTrimmed>true</PublishTrimmed>
|
||||
<TrimMode>partial</TrimMode>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
</Project>
|
@ -62,6 +62,7 @@ namespace Ryujinx.Headless.SDL2
|
||||
private readonly long _ticksPerFrame;
|
||||
private readonly CancellationTokenSource _gpuCancellationTokenSource;
|
||||
private readonly ManualResetEvent _exitEvent;
|
||||
private readonly ManualResetEvent _gpuDoneEvent;
|
||||
|
||||
private long _ticks;
|
||||
private bool _isActive;
|
||||
@ -91,6 +92,7 @@ namespace Ryujinx.Headless.SDL2
|
||||
_ticksPerFrame = Stopwatch.Frequency / TargetFps;
|
||||
_gpuCancellationTokenSource = new CancellationTokenSource();
|
||||
_exitEvent = new ManualResetEvent(false);
|
||||
_gpuDoneEvent = new ManualResetEvent(false);
|
||||
_aspectRatio = aspectRatio;
|
||||
_enableMouse = enableMouse;
|
||||
HostUiTheme = new HeadlessHostUiTheme();
|
||||
@ -275,6 +277,14 @@ namespace Ryujinx.Headless.SDL2
|
||||
_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();
|
||||
@ -404,7 +414,10 @@ namespace Ryujinx.Headless.SDL2
|
||||
|
||||
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();
|
||||
|
||||
Exit();
|
||||
|
@ -1,5 +1,6 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Common.Logging.Targets;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.Ui.Common.Configuration
|
||||
|
@ -65,6 +65,7 @@ namespace Ryujinx.Ui
|
||||
private KeyboardHotkeyState _prevHotkeyState;
|
||||
|
||||
private readonly ManualResetEvent _exitEvent;
|
||||
private readonly ManualResetEvent _gpuDoneEvent;
|
||||
|
||||
private readonly CancellationTokenSource _gpuCancellationTokenSource;
|
||||
|
||||
@ -110,6 +111,7 @@ namespace Ryujinx.Ui
|
||||
| EventMask.KeyReleaseMask));
|
||||
|
||||
_exitEvent = new ManualResetEvent(false);
|
||||
_gpuDoneEvent = new ManualResetEvent(false);
|
||||
|
||||
_gpuCancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
@ -499,6 +501,14 @@ namespace Ryujinx.Ui
|
||||
_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();
|
||||
|
||||
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();
|
||||
|
||||
Exit();
|
||||
|
Reference in New Issue
Block a user