Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
8a352df3c6 | |||
c545c59851 | |||
96ea4e8c8e | |||
b8f48bcf64 | |||
6966211e07 | |||
57524a4c8a | |||
f4539c49d8 | |||
12c62fdbc2 | |||
e3c6be5e29 | |||
4741a05df9 | |||
c6676007bf | |||
92b0b7d753 | |||
232237bf28 | |||
c27e453fd3 |
19
.github/ISSUE_TEMPLATE/missing_shader_instruction.yml
vendored
Normal file
19
.github/ISSUE_TEMPLATE/missing_shader_instruction.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
name: Missing Shader Instruction
|
||||
description: Shader Instruction is missing in Ryujinx.
|
||||
title: "[GPU]"
|
||||
labels: [gpu, not-implemented]
|
||||
body:
|
||||
- type: textarea
|
||||
id: instruction
|
||||
attributes:
|
||||
label: Shader instruction
|
||||
description: What shader instruction is missing?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: required
|
||||
attributes:
|
||||
label: Required by
|
||||
description: Add links to the [compatibility list page(s)](https://github.com/Ryujinx/Ryujinx-Games-List/issues) of the game(s) that require this instruction.
|
||||
validations:
|
||||
required: true
|
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" />
|
||||
@ -21,7 +21,7 @@
|
||||
<PackageVersion Include="LibHac" Version="0.18.0" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
|
||||
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
|
||||
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.6.1" />
|
||||
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
|
||||
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
|
||||
<PackageVersion Include="NUnit" Version="3.13.3" />
|
||||
|
@ -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);
|
||||
|
@ -21,6 +21,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
if (value is byte[] buffer && targetType == typeof(IImage))
|
||||
{
|
||||
MemoryStream mem = new(buffer);
|
||||
|
||||
return new Bitmap(mem);
|
||||
}
|
||||
|
||||
|
@ -318,7 +318,7 @@ namespace Ryujinx.Ava.UI.Helpers
|
||||
|
||||
Window parent = GetMainWindow();
|
||||
|
||||
if (parent is { IsActive: true } and MainWindow window && window.ViewModel.IsGameRunning)
|
||||
if (parent != null && parent.IsActive && (parent as MainWindow).ViewModel.IsGameRunning)
|
||||
{
|
||||
contentDialogOverlayWindow = new()
|
||||
{
|
||||
|
@ -9,9 +9,7 @@ using Ryujinx.Ava.UI.ViewModels;
|
||||
using Ryujinx.Ava.UI.Windows;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Utilities;
|
||||
using Ryujinx.HLE.HOS;
|
||||
using Ryujinx.Modules;
|
||||
using Ryujinx.Ui.App.Common;
|
||||
using Ryujinx.Ui.Common;
|
||||
using Ryujinx.Ui.Common.Configuration;
|
||||
using Ryujinx.Ui.Common.Helper;
|
||||
|
@ -16,6 +16,7 @@
|
||||
</Design.DataContext>
|
||||
<DockPanel
|
||||
Margin="0,0,0,5"
|
||||
Height="35"
|
||||
HorizontalAlignment="Stretch">
|
||||
<Button
|
||||
Width="40"
|
||||
|
@ -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
|
||||
|
@ -519,14 +519,14 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
private void ConfirmExit()
|
||||
{
|
||||
Dispatcher.UIThread.InvokeAsync(async () =>
|
||||
{
|
||||
ViewModel.IsClosing = await ContentDialogHelper.CreateExitDialog();
|
||||
{
|
||||
ViewModel.IsClosing = await ContentDialogHelper.CreateExitDialog();
|
||||
|
||||
if (ViewModel.IsClosing)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
});
|
||||
if (ViewModel.IsClosing)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async void LoadApplications()
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -383,6 +383,7 @@ namespace Ryujinx.Graphics.GAL
|
||||
case Format.R10G10B10A2Unorm:
|
||||
case Format.R10G10B10A2Uint:
|
||||
case Format.R11G11B10Float:
|
||||
case Format.B8G8R8A8Unorm:
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -96,8 +96,6 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
{
|
||||
var originalInfo = view.Info;
|
||||
|
||||
var swapRB = originalInfo.Format.IsBgr() && originalInfo.SwizzleR == SwizzleComponent.Red;
|
||||
|
||||
var info = new TextureCreateInfo(
|
||||
width,
|
||||
height,
|
||||
@ -110,9 +108,9 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
originalInfo.Format,
|
||||
originalInfo.DepthStencilMode,
|
||||
originalInfo.Target,
|
||||
swapRB ? originalInfo.SwizzleB : originalInfo.SwizzleR,
|
||||
originalInfo.SwizzleR,
|
||||
originalInfo.SwizzleG,
|
||||
swapRB ? originalInfo.SwizzleR : originalInfo.SwizzleB,
|
||||
originalInfo.SwizzleB,
|
||||
originalInfo.SwizzleA);
|
||||
_intermediaryTexture?.Dispose();
|
||||
_intermediaryTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView;
|
||||
@ -155,7 +153,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
|
||||
var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize);
|
||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) });
|
||||
_pipeline.SetImage(0, _intermediaryTexture, GAL.Format.R8G8B8A8Unorm);
|
||||
_pipeline.SetImage(0, _intermediaryTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
_pipeline.ComputeBarrier();
|
||||
|
||||
|
@ -56,28 +56,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
if (_texture == null || _texture.Width != view.Width || _texture.Height != view.Height)
|
||||
{
|
||||
_texture?.Dispose();
|
||||
|
||||
var info = view.Info;
|
||||
|
||||
if (view.Info.Format.IsBgr())
|
||||
{
|
||||
info = new TextureCreateInfo(info.Width,
|
||||
info.Height,
|
||||
info.Depth,
|
||||
info.Levels,
|
||||
info.Samples,
|
||||
info.BlockWidth,
|
||||
info.BlockHeight,
|
||||
info.BytesPerPixel,
|
||||
info.Format,
|
||||
info.DepthStencilMode,
|
||||
info.Target,
|
||||
info.SwizzleB,
|
||||
info.SwizzleG,
|
||||
info.SwizzleR,
|
||||
info.SwizzleA);
|
||||
}
|
||||
_texture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView;
|
||||
_texture = _renderer.CreateTexture(view.Info, view.ScaleFactor) as TextureView;
|
||||
}
|
||||
|
||||
_pipeline.SetCommandBuffer(cbs);
|
||||
@ -96,7 +75,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
var dispatchX = BitUtils.DivRoundUp(view.Width, IPostProcessingEffect.LocalGroupSize);
|
||||
var dispatchY = BitUtils.DivRoundUp(view.Height, IPostProcessingEffect.LocalGroupSize);
|
||||
|
||||
_pipeline.SetImage(0, _texture, GAL.Format.R8G8B8A8Unorm);
|
||||
_pipeline.SetImage(0, _texture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
|
||||
_renderer.BufferManager.Delete(bufferHandle);
|
||||
|
@ -4,7 +4,6 @@ using Ryujinx.Graphics.Shader;
|
||||
using Ryujinx.Graphics.Shader.Translation;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using Format = Ryujinx.Graphics.GAL.Format;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
{
|
||||
@ -149,7 +148,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
Format.R8G8Unorm,
|
||||
GAL.Format.R8G8Unorm,
|
||||
DepthStencilMode.Depth,
|
||||
Target.Texture2D,
|
||||
SwizzleComponent.Red,
|
||||
@ -165,7 +164,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
Format.R8Unorm,
|
||||
GAL.Format.R8Unorm,
|
||||
DepthStencilMode.Depth,
|
||||
Target.Texture2D,
|
||||
SwizzleComponent.Red,
|
||||
@ -192,30 +191,9 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
_edgeOutputTexture?.Dispose();
|
||||
_blendOutputTexture?.Dispose();
|
||||
|
||||
var info = view.Info;
|
||||
|
||||
if (view.Info.Format.IsBgr())
|
||||
{
|
||||
info = new TextureCreateInfo(info.Width,
|
||||
info.Height,
|
||||
info.Depth,
|
||||
info.Levels,
|
||||
info.Samples,
|
||||
info.BlockWidth,
|
||||
info.BlockHeight,
|
||||
info.BytesPerPixel,
|
||||
info.Format,
|
||||
info.DepthStencilMode,
|
||||
info.Target,
|
||||
info.SwizzleB,
|
||||
info.SwizzleG,
|
||||
info.SwizzleR,
|
||||
info.SwizzleA);
|
||||
}
|
||||
|
||||
_outputTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView;
|
||||
_edgeOutputTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView;
|
||||
_blendOutputTexture = _renderer.CreateTexture(info, view.ScaleFactor) as TextureView;
|
||||
_outputTexture = _renderer.CreateTexture(view.Info, view.ScaleFactor) as TextureView;
|
||||
_edgeOutputTexture = _renderer.CreateTexture(view.Info, view.ScaleFactor) as TextureView;
|
||||
_blendOutputTexture = _renderer.CreateTexture(view.Info, view.ScaleFactor) as TextureView;
|
||||
}
|
||||
|
||||
_pipeline.SetCommandBuffer(cbs);
|
||||
@ -240,7 +218,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
_renderer.BufferManager.SetData(bufferHandle, 0, resolutionBuffer);
|
||||
var bufferRanges = new BufferRange(bufferHandle, 0, rangeSize);
|
||||
_pipeline.SetUniformBuffers(stackalloc[] { new BufferAssignment(2, bufferRanges) });
|
||||
_pipeline.SetImage(0, _edgeOutputTexture, GAL.Format.R8G8B8A8Unorm);
|
||||
_pipeline.SetImage(0, _edgeOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
_pipeline.ComputeBarrier();
|
||||
|
||||
@ -250,7 +228,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, _edgeOutputTexture, _samplerLinear);
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _areaTexture, _samplerLinear);
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 4, _searchTexture, _samplerLinear);
|
||||
_pipeline.SetImage(0, _blendOutputTexture, GAL.Format.R8G8B8A8Unorm);
|
||||
_pipeline.SetImage(0, _blendOutputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
_pipeline.ComputeBarrier();
|
||||
|
||||
@ -259,7 +237,7 @@ namespace Ryujinx.Graphics.Vulkan.Effects
|
||||
_pipeline.Specialize(_specConstants);
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 3, _blendOutputTexture, _samplerLinear);
|
||||
_pipeline.SetTextureAndSampler(ShaderStage.Compute, 1, view, _samplerLinear);
|
||||
_pipeline.SetImage(0, _outputTexture, GAL.Format.R8G8B8A8Unorm);
|
||||
_pipeline.SetImage(0, _outputTexture, FormatTable.ConvertRgba8SrgbToUnorm(view.Info.Format));
|
||||
_pipeline.DispatchCompute(dispatchX, dispatchY, 1);
|
||||
_pipeline.ComputeBarrier();
|
||||
|
||||
|
@ -169,6 +169,16 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return _table[(int)format];
|
||||
}
|
||||
|
||||
public static Format ConvertRgba8SrgbToUnorm(Format format)
|
||||
{
|
||||
return format switch
|
||||
{
|
||||
Format.R8G8B8A8Srgb => Format.R8G8B8A8Unorm,
|
||||
Format.B8G8R8A8Srgb => Format.B8G8R8A8Unorm,
|
||||
_ => format
|
||||
};
|
||||
}
|
||||
|
||||
public static int GetAttributeFormatSize(VkFormat format)
|
||||
{
|
||||
switch (format)
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -343,7 +343,14 @@ namespace Ryujinx.Ui.App.Common
|
||||
ulong nacpSize = reader.ReadUInt64();
|
||||
|
||||
// Reads and stores game icon as byte array
|
||||
applicationIcon = Read(assetOffset + iconOffset, (int)iconSize);
|
||||
if (iconSize > 0)
|
||||
{
|
||||
applicationIcon = Read(assetOffset + iconOffset, (int)iconSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
applicationIcon = _nroIcon;
|
||||
}
|
||||
|
||||
// Read the NACP data
|
||||
Read(assetOffset + (int)nacpOffset, (int)nacpSize).AsSpan().CopyTo(controlHolder.ByteSpan);
|
||||
@ -666,7 +673,14 @@ namespace Ryujinx.Ui.App.Common
|
||||
long iconSize = BitConverter.ToInt64(iconSectionInfo, 8);
|
||||
|
||||
// Reads and stores game icon as byte array
|
||||
applicationIcon = Read(assetOffset + iconOffset, (int)iconSize);
|
||||
if (iconSize > 0)
|
||||
{
|
||||
applicationIcon = Read(assetOffset + iconOffset, (int)iconSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
applicationIcon = _nroIcon;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -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