Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
331c07807f | ||
|
a772b073ec | ||
|
870d9599cc | ||
|
2dbbc9bc05 |
@@ -4,7 +4,5 @@ namespace ARMeilleure.Memory
|
||||
{
|
||||
IJitMemoryBlock Allocate(ulong size);
|
||||
IJitMemoryBlock Reserve(ulong size);
|
||||
|
||||
ulong GetPageSize();
|
||||
}
|
||||
}
|
||||
|
@@ -31,7 +31,7 @@ namespace ARMeilleure.Memory
|
||||
HostMappedUnsafe,
|
||||
}
|
||||
|
||||
static class MemoryManagerTypeExtensions
|
||||
public static class MemoryManagerTypeExtensions
|
||||
{
|
||||
public static bool IsHostMapped(this MemoryManagerType type)
|
||||
{
|
||||
|
@@ -1,63 +1,14 @@
|
||||
using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.Translation;
|
||||
using ARMeilleure.Translation.Cache;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Signal
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct SignalHandlerRange
|
||||
public static class NativeSignalHandlerGenerator
|
||||
{
|
||||
public int IsActive;
|
||||
public nuint RangeAddress;
|
||||
public nuint RangeEndAddress;
|
||||
public IntPtr ActionPointer;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct SignalHandlerConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// The byte offset of the faulting address in the SigInfo or ExceptionRecord struct.
|
||||
/// </summary>
|
||||
public int StructAddressOffset;
|
||||
|
||||
/// <summary>
|
||||
/// The byte offset of the write flag in the SigInfo or ExceptionRecord struct.
|
||||
/// </summary>
|
||||
public int StructWriteOffset;
|
||||
|
||||
/// <summary>
|
||||
/// The sigaction handler that was registered before this one. (unix only)
|
||||
/// </summary>
|
||||
public nuint UnixOldSigaction;
|
||||
|
||||
/// <summary>
|
||||
/// The type of the previous sigaction. True for the 3 argument variant. (unix only)
|
||||
/// </summary>
|
||||
public int UnixOldSigaction3Arg;
|
||||
|
||||
public SignalHandlerRange Range0;
|
||||
public SignalHandlerRange Range1;
|
||||
public SignalHandlerRange Range2;
|
||||
public SignalHandlerRange Range3;
|
||||
public SignalHandlerRange Range4;
|
||||
public SignalHandlerRange Range5;
|
||||
public SignalHandlerRange Range6;
|
||||
public SignalHandlerRange Range7;
|
||||
}
|
||||
|
||||
public static class NativeSignalHandler
|
||||
{
|
||||
private delegate void UnixExceptionHandler(int sig, IntPtr info, IntPtr ucontext);
|
||||
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
|
||||
private delegate int VectoredExceptionHandler(IntPtr exceptionInfo);
|
||||
|
||||
private const int MaxTrackedRanges = 8;
|
||||
public const int MaxTrackedRanges = 8;
|
||||
|
||||
private const int StructAddressOffset = 0;
|
||||
private const int StructWriteOffset = 4;
|
||||
@@ -70,125 +21,10 @@ namespace ARMeilleure.Signal
|
||||
|
||||
private const uint EXCEPTION_ACCESS_VIOLATION = 0xc0000005;
|
||||
|
||||
private static ulong _pageSize;
|
||||
private static ulong _pageMask;
|
||||
|
||||
private static readonly IntPtr _handlerConfig;
|
||||
private static IntPtr _signalHandlerPtr;
|
||||
private static IntPtr _signalHandlerHandle;
|
||||
|
||||
private static readonly object _lock = new();
|
||||
private static bool _initialized;
|
||||
|
||||
static NativeSignalHandler()
|
||||
private static Operand EmitGenericRegionCheck(EmitterContext context, IntPtr signalStructPtr, Operand faultAddress, Operand isWrite, int rangeStructSize, ulong pageSize)
|
||||
{
|
||||
_handlerConfig = Marshal.AllocHGlobal(Unsafe.SizeOf<SignalHandlerConfig>());
|
||||
ref SignalHandlerConfig config = ref GetConfigRef();
|
||||
ulong pageMask = pageSize - 1;
|
||||
|
||||
config = new SignalHandlerConfig();
|
||||
}
|
||||
|
||||
public static void Initialize(IJitMemoryAllocator allocator)
|
||||
{
|
||||
JitCache.Initialize(allocator);
|
||||
}
|
||||
|
||||
public static void InitializeSignalHandler(ulong pageSize, Func<IntPtr, IntPtr, IntPtr> customSignalHandlerFactory = null)
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_pageSize = pageSize;
|
||||
_pageMask = pageSize - 1;
|
||||
|
||||
ref SignalHandlerConfig config = ref GetConfigRef();
|
||||
|
||||
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||
{
|
||||
_signalHandlerPtr = Marshal.GetFunctionPointerForDelegate(GenerateUnixSignalHandler(_handlerConfig));
|
||||
|
||||
if (customSignalHandlerFactory != null)
|
||||
{
|
||||
_signalHandlerPtr = customSignalHandlerFactory(UnixSignalHandlerRegistration.GetSegfaultExceptionHandler().sa_handler, _signalHandlerPtr);
|
||||
}
|
||||
|
||||
var old = UnixSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr);
|
||||
|
||||
config.UnixOldSigaction = (nuint)(ulong)old.sa_handler;
|
||||
config.UnixOldSigaction3Arg = old.sa_flags & 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
config.StructAddressOffset = 40; // ExceptionInformation1
|
||||
config.StructWriteOffset = 32; // ExceptionInformation0
|
||||
|
||||
_signalHandlerPtr = Marshal.GetFunctionPointerForDelegate(GenerateWindowsSignalHandler(_handlerConfig));
|
||||
|
||||
if (customSignalHandlerFactory != null)
|
||||
{
|
||||
_signalHandlerPtr = customSignalHandlerFactory(IntPtr.Zero, _signalHandlerPtr);
|
||||
}
|
||||
|
||||
_signalHandlerHandle = WindowsSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr);
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe ref SignalHandlerConfig GetConfigRef()
|
||||
{
|
||||
return ref Unsafe.AsRef<SignalHandlerConfig>((void*)_handlerConfig);
|
||||
}
|
||||
|
||||
public static unsafe bool AddTrackedRegion(nuint address, nuint endAddress, IntPtr action)
|
||||
{
|
||||
var ranges = &((SignalHandlerConfig*)_handlerConfig)->Range0;
|
||||
|
||||
for (int i = 0; i < MaxTrackedRanges; i++)
|
||||
{
|
||||
if (ranges[i].IsActive == 0)
|
||||
{
|
||||
ranges[i].RangeAddress = address;
|
||||
ranges[i].RangeEndAddress = endAddress;
|
||||
ranges[i].ActionPointer = action;
|
||||
ranges[i].IsActive = 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static unsafe bool RemoveTrackedRegion(nuint address)
|
||||
{
|
||||
var ranges = &((SignalHandlerConfig*)_handlerConfig)->Range0;
|
||||
|
||||
for (int i = 0; i < MaxTrackedRanges; i++)
|
||||
{
|
||||
if (ranges[i].IsActive == 1 && ranges[i].RangeAddress == address)
|
||||
{
|
||||
ranges[i].IsActive = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Operand EmitGenericRegionCheck(EmitterContext context, IntPtr signalStructPtr, Operand faultAddress, Operand isWrite)
|
||||
{
|
||||
Operand inRegionLocal = context.AllocateLocal(OperandType.I32);
|
||||
context.Copy(inRegionLocal, Const(0));
|
||||
|
||||
@@ -196,7 +32,7 @@ namespace ARMeilleure.Signal
|
||||
|
||||
for (int i = 0; i < MaxTrackedRanges; i++)
|
||||
{
|
||||
ulong rangeBaseOffset = (ulong)(RangeOffset + i * Unsafe.SizeOf<SignalHandlerRange>());
|
||||
ulong rangeBaseOffset = (ulong)(RangeOffset + i * rangeStructSize);
|
||||
|
||||
Operand nextLabel = Label();
|
||||
|
||||
@@ -210,13 +46,12 @@ namespace ARMeilleure.Signal
|
||||
// Is the fault address within this tracked region?
|
||||
Operand inRange = context.BitwiseAnd(
|
||||
context.ICompare(faultAddress, rangeAddress, Comparison.GreaterOrEqualUI),
|
||||
context.ICompare(faultAddress, rangeEndAddress, Comparison.LessUI)
|
||||
);
|
||||
context.ICompare(faultAddress, rangeEndAddress, Comparison.LessUI));
|
||||
|
||||
// Only call tracking if in range.
|
||||
context.BranchIfFalse(nextLabel, inRange, BasicBlockFrequency.Cold);
|
||||
|
||||
Operand offset = context.BitwiseAnd(context.Subtract(faultAddress, rangeAddress), Const(~_pageMask));
|
||||
Operand offset = context.BitwiseAnd(context.Subtract(faultAddress, rangeAddress), Const(~pageMask));
|
||||
|
||||
// Call the tracking action, with the pointer's relative offset to the base address.
|
||||
Operand trackingActionPtr = context.Load(OperandType.I64, Const((ulong)signalStructPtr + rangeBaseOffset + 20));
|
||||
@@ -227,7 +62,7 @@ namespace ARMeilleure.Signal
|
||||
|
||||
// Tracking action should be non-null to call it, otherwise assume false return.
|
||||
context.BranchIfFalse(skipActionLabel, trackingActionPtr);
|
||||
Operand result = context.Call(trackingActionPtr, OperandType.I32, offset, Const(_pageSize), isWrite);
|
||||
Operand result = context.Call(trackingActionPtr, OperandType.I32, offset, Const(pageSize), isWrite);
|
||||
context.Copy(inRegionLocal, result);
|
||||
|
||||
context.MarkLabel(skipActionLabel);
|
||||
@@ -269,8 +104,7 @@ namespace ARMeilleure.Signal
|
||||
Operand esr = context.Load(OperandType.I64, context.Add(ctxPtr, Const(EsrOffset)));
|
||||
return context.BitwiseAnd(esr, Const(0x40ul));
|
||||
}
|
||||
|
||||
if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
||||
else if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
||||
{
|
||||
const ulong ErrOffset = 4; // __es.__err
|
||||
Operand err = context.Load(OperandType.I64, context.Add(ctxPtr, Const(ErrOffset)));
|
||||
@@ -310,8 +144,7 @@ namespace ARMeilleure.Signal
|
||||
Operand esr = context.Load(OperandType.I64, context.Add(auxPtr, Const(8ul)));
|
||||
return context.BitwiseAnd(esr, Const(0x40ul));
|
||||
}
|
||||
|
||||
if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
||||
else if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
||||
{
|
||||
const int ErrOffset = 192; // uc_mcontext.gregs[REG_ERR]
|
||||
Operand err = context.Load(OperandType.I64, context.Add(ucontextPtr, Const(ErrOffset)));
|
||||
@@ -322,7 +155,7 @@ namespace ARMeilleure.Signal
|
||||
throw new PlatformNotSupportedException();
|
||||
}
|
||||
|
||||
private static UnixExceptionHandler GenerateUnixSignalHandler(IntPtr signalStructPtr)
|
||||
public static byte[] GenerateUnixSignalHandler(IntPtr signalStructPtr, int rangeStructSize, ulong pageSize)
|
||||
{
|
||||
EmitterContext context = new();
|
||||
|
||||
@@ -335,7 +168,7 @@ namespace ARMeilleure.Signal
|
||||
|
||||
Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1.
|
||||
|
||||
Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite);
|
||||
Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite, rangeStructSize, pageSize);
|
||||
|
||||
Operand endLabel = Label();
|
||||
|
||||
@@ -367,10 +200,10 @@ namespace ARMeilleure.Signal
|
||||
|
||||
OperandType[] argTypes = new OperandType[] { OperandType.I32, OperandType.I64, OperandType.I64 };
|
||||
|
||||
return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<UnixExceptionHandler>();
|
||||
return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Code;
|
||||
}
|
||||
|
||||
private static VectoredExceptionHandler GenerateWindowsSignalHandler(IntPtr signalStructPtr)
|
||||
public static byte[] GenerateWindowsSignalHandler(IntPtr signalStructPtr, int rangeStructSize, ulong pageSize)
|
||||
{
|
||||
EmitterContext context = new();
|
||||
|
||||
@@ -399,7 +232,7 @@ namespace ARMeilleure.Signal
|
||||
|
||||
Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1.
|
||||
|
||||
Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite);
|
||||
Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite, rangeStructSize, pageSize);
|
||||
|
||||
Operand endLabel = Label();
|
||||
|
||||
@@ -421,7 +254,7 @@ namespace ARMeilleure.Signal
|
||||
|
||||
OperandType[] argTypes = new OperandType[] { OperandType.I64 };
|
||||
|
||||
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<VectoredExceptionHandler>();
|
||||
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Code;
|
||||
}
|
||||
}
|
||||
}
|
@@ -2,7 +2,7 @@ using ARMeilleure.IntermediateRepresentation;
|
||||
using ARMeilleure.Translation;
|
||||
using Ryujinx.Common.Memory.PartialUnmaps;
|
||||
using System;
|
||||
|
||||
using System.Runtime.InteropServices;
|
||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||
|
||||
namespace ARMeilleure.Signal
|
||||
@@ -10,8 +10,28 @@ namespace ARMeilleure.Signal
|
||||
/// <summary>
|
||||
/// Methods to handle signals caused by partial unmaps. See the structs for C# implementations of the methods.
|
||||
/// </summary>
|
||||
internal static class WindowsPartialUnmapHandler
|
||||
internal static partial class WindowsPartialUnmapHandler
|
||||
{
|
||||
[LibraryImport("kernel32.dll", SetLastError = true, EntryPoint = "LoadLibraryA")]
|
||||
private static partial IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);
|
||||
|
||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||
private static partial IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string procName);
|
||||
|
||||
private static IntPtr _getCurrentThreadIdPtr;
|
||||
|
||||
public static IntPtr GetCurrentThreadIdFunc()
|
||||
{
|
||||
if (_getCurrentThreadIdPtr == IntPtr.Zero)
|
||||
{
|
||||
IntPtr handle = LoadLibrary("kernel32.dll");
|
||||
|
||||
_getCurrentThreadIdPtr = GetProcAddress(handle, "GetCurrentThreadId");
|
||||
}
|
||||
|
||||
return _getCurrentThreadIdPtr;
|
||||
}
|
||||
|
||||
public static Operand EmitRetryFromAccessViolation(EmitterContext context)
|
||||
{
|
||||
IntPtr partialRemapStatePtr = PartialUnmapState.GlobalState;
|
||||
@@ -20,7 +40,7 @@ namespace ARMeilleure.Signal
|
||||
// Get the lock first.
|
||||
EmitNativeReaderLockAcquire(context, IntPtr.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapLockOffset));
|
||||
|
||||
IntPtr getCurrentThreadId = WindowsSignalHandlerRegistration.GetCurrentThreadIdFunc();
|
||||
IntPtr getCurrentThreadId = GetCurrentThreadIdFunc();
|
||||
Operand threadId = context.Call(Const((ulong)getCurrentThreadId), OperandType.I32);
|
||||
Operand threadIndex = EmitThreadLocalMapIntGetOrReserve(context, localCountsPtr, threadId, Const(0));
|
||||
|
||||
@@ -137,17 +157,6 @@ namespace ARMeilleure.Signal
|
||||
return context.Add(structsPtr, context.SignExtend32(OperandType.I64, offset));
|
||||
}
|
||||
|
||||
#pragma warning disable IDE0051 // Remove unused private member
|
||||
private static void EmitThreadLocalMapIntRelease(EmitterContext context, IntPtr threadLocalMapPtr, Operand threadId, Operand index)
|
||||
{
|
||||
Operand offset = context.Multiply(index, Const(sizeof(int)));
|
||||
Operand idsPtr = Const((ulong)IntPtr.Add(threadLocalMapPtr, ThreadLocalMap<int>.ThreadIdsOffset));
|
||||
Operand idPtr = context.Add(idsPtr, context.SignExtend32(OperandType.I64, offset));
|
||||
|
||||
context.CompareAndSwap(idPtr, threadId, Const(0));
|
||||
}
|
||||
#pragma warning restore IDE0051
|
||||
|
||||
private static void EmitAtomicAddI32(EmitterContext context, Operand ptr, Operand additive)
|
||||
{
|
||||
Operand loop = Label();
|
||||
|
@@ -1,44 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ARMeilleure.Signal
|
||||
{
|
||||
unsafe partial class WindowsSignalHandlerRegistration
|
||||
{
|
||||
[LibraryImport("kernel32.dll")]
|
||||
private static partial IntPtr AddVectoredExceptionHandler(uint first, IntPtr handler);
|
||||
|
||||
[LibraryImport("kernel32.dll")]
|
||||
private static partial ulong RemoveVectoredExceptionHandler(IntPtr handle);
|
||||
|
||||
[LibraryImport("kernel32.dll", SetLastError = true, EntryPoint = "LoadLibraryA")]
|
||||
private static partial IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName);
|
||||
|
||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||
private static partial IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string procName);
|
||||
|
||||
private static IntPtr _getCurrentThreadIdPtr;
|
||||
|
||||
public static IntPtr RegisterExceptionHandler(IntPtr action)
|
||||
{
|
||||
return AddVectoredExceptionHandler(1, action);
|
||||
}
|
||||
|
||||
public static bool RemoveExceptionHandler(IntPtr handle)
|
||||
{
|
||||
return RemoveVectoredExceptionHandler(handle) != 0;
|
||||
}
|
||||
|
||||
public static IntPtr GetCurrentThreadIdFunc()
|
||||
{
|
||||
if (_getCurrentThreadIdPtr == IntPtr.Zero)
|
||||
{
|
||||
IntPtr handle = LoadLibrary("kernel32.dll");
|
||||
|
||||
_getCurrentThreadIdPtr = GetProcAddress(handle, "GetCurrentThreadId");
|
||||
}
|
||||
|
||||
return _getCurrentThreadIdPtr;
|
||||
}
|
||||
}
|
||||
}
|
@@ -57,9 +57,6 @@ namespace ARMeilleure.Translation
|
||||
private Thread[] _backgroundTranslationThreads;
|
||||
private volatile int _threadCount;
|
||||
|
||||
// FIXME: Remove this once the init logic of the emulator will be redone.
|
||||
public static readonly ManualResetEvent IsReadyForTranslation = new(false);
|
||||
|
||||
public Translator(IJitMemoryAllocator allocator, IMemoryManager memory, bool for64Bits)
|
||||
{
|
||||
_allocator = allocator;
|
||||
@@ -79,11 +76,6 @@ namespace ARMeilleure.Translation
|
||||
Stubs = new TranslatorStubs(this);
|
||||
|
||||
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
||||
|
||||
if (memory.Type.IsHostMapped())
|
||||
{
|
||||
NativeSignalHandler.InitializeSignalHandler(allocator.GetPageSize());
|
||||
}
|
||||
}
|
||||
|
||||
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
||||
@@ -105,8 +97,6 @@ namespace ARMeilleure.Translation
|
||||
{
|
||||
if (Interlocked.Increment(ref _threadCount) == 1)
|
||||
{
|
||||
IsReadyForTranslation.WaitOne();
|
||||
|
||||
if (_ptc.State == PtcState.Enabled)
|
||||
{
|
||||
Debug.Assert(Functions.Count == 0);
|
||||
|
@@ -1,4 +1,3 @@
|
||||
using ARMeilleure.Translation;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Controls.ApplicationLifetimes;
|
||||
@@ -916,7 +915,6 @@ namespace Ryujinx.Ava
|
||||
{
|
||||
Device.Gpu.SetGpuThread();
|
||||
Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token);
|
||||
Translator.IsReadyForTranslation.Set();
|
||||
|
||||
_renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync);
|
||||
|
||||
|
@@ -63,6 +63,17 @@ namespace Ryujinx.Common.Configuration
|
||||
string userProfilePath = Path.Combine(appDataPath, DefaultBaseDir);
|
||||
string portablePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DefaultPortableDir);
|
||||
|
||||
// On macOS, check for a portable directory next to the app bundle as well.
|
||||
if (OperatingSystem.IsMacOS() && !Directory.Exists(portablePath))
|
||||
{
|
||||
string bundlePath = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..", ".."));
|
||||
// Make sure we're actually running within an app bundle.
|
||||
if (bundlePath.EndsWith(".app"))
|
||||
{
|
||||
portablePath = Path.GetFullPath(Path.Combine(bundlePath, "..", DefaultPortableDir));
|
||||
}
|
||||
}
|
||||
|
||||
if (Directory.Exists(portablePath))
|
||||
{
|
||||
BaseDirPath = portablePath;
|
||||
|
@@ -1,5 +1,7 @@
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.Translation;
|
||||
using Ryujinx.Cpu.Signal;
|
||||
using Ryujinx.Memory;
|
||||
|
||||
namespace Ryujinx.Cpu.Jit
|
||||
{
|
||||
@@ -12,6 +14,12 @@ namespace Ryujinx.Cpu.Jit
|
||||
{
|
||||
_tickSource = tickSource;
|
||||
_translator = new Translator(new JitMemoryAllocator(), memory, for64Bit);
|
||||
|
||||
if (memory.Type.IsHostMapped())
|
||||
{
|
||||
NativeSignalHandler.InitializeSignalHandler(MemoryBlock.GetPageSize());
|
||||
}
|
||||
|
||||
memory.UnmapEvent += UnmapHandler;
|
||||
}
|
||||
|
||||
|
@@ -7,7 +7,5 @@ namespace Ryujinx.Cpu.Jit
|
||||
{
|
||||
public IJitMemoryBlock Allocate(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.None);
|
||||
public IJitMemoryBlock Reserve(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.Jit);
|
||||
|
||||
public ulong GetPageSize() => MemoryBlock.GetPageSize();
|
||||
}
|
||||
}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
using ARMeilleure.Signal;
|
||||
using Ryujinx.Cpu.Signal;
|
||||
using Ryujinx.Memory;
|
||||
using Ryujinx.Memory.Tracking;
|
||||
using System;
|
||||
|
179
src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs
Normal file
179
src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs
Normal file
@@ -0,0 +1,179 @@
|
||||
using ARMeilleure.Signal;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Signal
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct SignalHandlerRange
|
||||
{
|
||||
public int IsActive;
|
||||
public nuint RangeAddress;
|
||||
public nuint RangeEndAddress;
|
||||
public IntPtr ActionPointer;
|
||||
}
|
||||
|
||||
[InlineArray(NativeSignalHandlerGenerator.MaxTrackedRanges)]
|
||||
struct SignalHandlerRangeArray
|
||||
{
|
||||
public SignalHandlerRange Range0;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
struct SignalHandlerConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// The byte offset of the faulting address in the SigInfo or ExceptionRecord struct.
|
||||
/// </summary>
|
||||
public int StructAddressOffset;
|
||||
|
||||
/// <summary>
|
||||
/// The byte offset of the write flag in the SigInfo or ExceptionRecord struct.
|
||||
/// </summary>
|
||||
public int StructWriteOffset;
|
||||
|
||||
/// <summary>
|
||||
/// The sigaction handler that was registered before this one. (unix only)
|
||||
/// </summary>
|
||||
public nuint UnixOldSigaction;
|
||||
|
||||
/// <summary>
|
||||
/// The type of the previous sigaction. True for the 3 argument variant. (unix only)
|
||||
/// </summary>
|
||||
public int UnixOldSigaction3Arg;
|
||||
|
||||
/// <summary>
|
||||
/// Fixed size array of tracked ranges.
|
||||
/// </summary>
|
||||
public SignalHandlerRangeArray Ranges;
|
||||
}
|
||||
|
||||
static class NativeSignalHandler
|
||||
{
|
||||
private static readonly IntPtr _handlerConfig;
|
||||
private static IntPtr _signalHandlerPtr;
|
||||
|
||||
private static MemoryBlock _codeBlock;
|
||||
|
||||
private static readonly object _lock = new();
|
||||
private static bool _initialized;
|
||||
|
||||
static NativeSignalHandler()
|
||||
{
|
||||
_handlerConfig = Marshal.AllocHGlobal(Unsafe.SizeOf<SignalHandlerConfig>());
|
||||
ref SignalHandlerConfig config = ref GetConfigRef();
|
||||
|
||||
config = new SignalHandlerConfig();
|
||||
}
|
||||
|
||||
public static void InitializeSignalHandler(ulong pageSize, Func<IntPtr, IntPtr, IntPtr> customSignalHandlerFactory = null)
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int rangeStructSize = Unsafe.SizeOf<SignalHandlerRange>();
|
||||
|
||||
ref SignalHandlerConfig config = ref GetConfigRef();
|
||||
|
||||
if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
|
||||
{
|
||||
_signalHandlerPtr = MapCode(NativeSignalHandlerGenerator.GenerateUnixSignalHandler(_handlerConfig, rangeStructSize, pageSize));
|
||||
|
||||
if (customSignalHandlerFactory != null)
|
||||
{
|
||||
_signalHandlerPtr = customSignalHandlerFactory(UnixSignalHandlerRegistration.GetSegfaultExceptionHandler().sa_handler, _signalHandlerPtr);
|
||||
}
|
||||
|
||||
var old = UnixSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr);
|
||||
|
||||
config.UnixOldSigaction = (nuint)(ulong)old.sa_handler;
|
||||
config.UnixOldSigaction3Arg = old.sa_flags & 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
config.StructAddressOffset = 40; // ExceptionInformation1
|
||||
config.StructWriteOffset = 32; // ExceptionInformation0
|
||||
|
||||
_signalHandlerPtr = MapCode(NativeSignalHandlerGenerator.GenerateWindowsSignalHandler(_handlerConfig, rangeStructSize, pageSize));
|
||||
|
||||
if (customSignalHandlerFactory != null)
|
||||
{
|
||||
_signalHandlerPtr = customSignalHandlerFactory(IntPtr.Zero, _signalHandlerPtr);
|
||||
}
|
||||
|
||||
WindowsSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr);
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static IntPtr MapCode(ReadOnlySpan<byte> code)
|
||||
{
|
||||
Debug.Assert(_codeBlock == null);
|
||||
|
||||
ulong codeSizeAligned = BitUtils.AlignUp((ulong)code.Length, MemoryBlock.GetPageSize());
|
||||
|
||||
_codeBlock = new MemoryBlock(codeSizeAligned);
|
||||
_codeBlock.Write(0, code);
|
||||
_codeBlock.Reprotect(0, codeSizeAligned, MemoryPermission.ReadAndExecute);
|
||||
|
||||
return _codeBlock.Pointer;
|
||||
}
|
||||
|
||||
private static unsafe ref SignalHandlerConfig GetConfigRef()
|
||||
{
|
||||
return ref Unsafe.AsRef<SignalHandlerConfig>((void*)_handlerConfig);
|
||||
}
|
||||
|
||||
public static bool AddTrackedRegion(nuint address, nuint endAddress, IntPtr action)
|
||||
{
|
||||
Span<SignalHandlerRange> ranges = GetConfigRef().Ranges;
|
||||
|
||||
for (int i = 0; i < NativeSignalHandlerGenerator.MaxTrackedRanges; i++)
|
||||
{
|
||||
if (ranges[i].IsActive == 0)
|
||||
{
|
||||
ranges[i].RangeAddress = address;
|
||||
ranges[i].RangeEndAddress = endAddress;
|
||||
ranges[i].ActionPointer = action;
|
||||
ranges[i].IsActive = 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool RemoveTrackedRegion(nuint address)
|
||||
{
|
||||
Span<SignalHandlerRange> ranges = GetConfigRef().Ranges;
|
||||
|
||||
for (int i = 0; i < NativeSignalHandlerGenerator.MaxTrackedRanges; i++)
|
||||
{
|
||||
if (ranges[i].IsActive == 1 && ranges[i].RangeAddress == address)
|
||||
{
|
||||
ranges[i].IsActive = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ARMeilleure.Signal
|
||||
namespace Ryujinx.Cpu.Signal
|
||||
{
|
||||
static partial class UnixSignalHandlerRegistration
|
||||
{
|
24
src/Ryujinx.Cpu/Signal/WindowsSignalHandlerRegistration.cs
Normal file
24
src/Ryujinx.Cpu/Signal/WindowsSignalHandlerRegistration.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Cpu.Signal
|
||||
{
|
||||
static partial class WindowsSignalHandlerRegistration
|
||||
{
|
||||
[LibraryImport("kernel32.dll")]
|
||||
private static partial IntPtr AddVectoredExceptionHandler(uint first, IntPtr handler);
|
||||
|
||||
[LibraryImport("kernel32.dll")]
|
||||
private static partial ulong RemoveVectoredExceptionHandler(IntPtr handle);
|
||||
|
||||
public static IntPtr RegisterExceptionHandler(IntPtr action)
|
||||
{
|
||||
return AddVectoredExceptionHandler(1, action);
|
||||
}
|
||||
|
||||
public static bool RemoveExceptionHandler(IntPtr handle)
|
||||
{
|
||||
return RemoveVectoredExceptionHandler(handle) != 0;
|
||||
}
|
||||
}
|
||||
}
|
@@ -106,6 +106,8 @@ namespace Ryujinx.Graphics.Gpu
|
||||
private long _modifiedSequence;
|
||||
private readonly ulong _firstTimestamp;
|
||||
|
||||
private readonly ManualResetEvent _gpuReadyEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the GPU emulation context.
|
||||
/// </summary>
|
||||
@@ -121,6 +123,7 @@ namespace Ryujinx.Graphics.Gpu
|
||||
Window = new Window(this);
|
||||
|
||||
HostInitalized = new ManualResetEvent(false);
|
||||
_gpuReadyEvent = new ManualResetEvent(false);
|
||||
|
||||
SyncActions = new List<ISyncActionHandler>();
|
||||
SyncpointActions = new List<ISyncActionHandler>();
|
||||
@@ -216,7 +219,7 @@ namespace Ryujinx.Graphics.Gpu
|
||||
/// Gets a sequence number for resource modification ordering. This increments on each call.
|
||||
/// </summary>
|
||||
/// <returns>A sequence number for resource modification ordering</returns>
|
||||
public long GetModifiedSequence()
|
||||
internal long GetModifiedSequence()
|
||||
{
|
||||
return _modifiedSequence++;
|
||||
}
|
||||
@@ -225,7 +228,7 @@ namespace Ryujinx.Graphics.Gpu
|
||||
/// Gets the value of the GPU timer.
|
||||
/// </summary>
|
||||
/// <returns>The current GPU timestamp</returns>
|
||||
public ulong GetTimestamp()
|
||||
internal ulong GetTimestamp()
|
||||
{
|
||||
// Guest timestamp will start at 0, instead of host value.
|
||||
ulong ticks = ConvertNanosecondsToTicks((ulong)PerformanceCounter.ElapsedNanoseconds) - _firstTimestamp;
|
||||
@@ -262,6 +265,16 @@ namespace Ryujinx.Graphics.Gpu
|
||||
{
|
||||
physicalMemory.ShaderCache.Initialize(cancellationToken);
|
||||
}
|
||||
|
||||
_gpuReadyEvent.Set();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Waits until the GPU is ready to receive commands.
|
||||
/// </summary>
|
||||
public void WaitUntilGpuReady()
|
||||
{
|
||||
_gpuReadyEvent.WaitOne();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -399,6 +412,7 @@ namespace Ryujinx.Graphics.Gpu
|
||||
{
|
||||
GPFifo.Dispose();
|
||||
HostInitalized.Dispose();
|
||||
_gpuReadyEvent.Dispose();
|
||||
|
||||
// Has to be disposed before processing deferred actions, as it will produce some.
|
||||
foreach (var physicalMemory in PhysicalMemoryRegistry.Values)
|
||||
|
@@ -161,7 +161,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
|
||||
_graphicsShaderCache,
|
||||
_computeShaderCache,
|
||||
_diskCacheHostStorage,
|
||||
ShaderCacheStateUpdate, cancellationToken);
|
||||
ShaderCacheStateUpdate,
|
||||
cancellationToken);
|
||||
|
||||
loader.LoadShaders();
|
||||
|
||||
|
145
src/Ryujinx.Graphics.Vulkan/DescriptorSetTemplate.cs
Normal file
145
src/Ryujinx.Graphics.Vulkan/DescriptorSetTemplate.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
using Ryujinx.Graphics.GAL;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
class DescriptorSetTemplate : IDisposable
|
||||
{
|
||||
private readonly VulkanRenderer _gd;
|
||||
private readonly Device _device;
|
||||
|
||||
public readonly DescriptorUpdateTemplate Template;
|
||||
public readonly int Size;
|
||||
|
||||
public unsafe DescriptorSetTemplate(VulkanRenderer gd, Device device, ResourceBindingSegment[] segments, PipelineLayoutCacheEntry plce, PipelineBindPoint pbp, int setIndex)
|
||||
{
|
||||
_gd = gd;
|
||||
_device = device;
|
||||
|
||||
// Create a template from the set usages. Assumes the descriptor set is updated in segment order then binding order.
|
||||
|
||||
DescriptorUpdateTemplateEntry* entries = stackalloc DescriptorUpdateTemplateEntry[segments.Length];
|
||||
nuint structureOffset = 0;
|
||||
|
||||
for (int seg = 0; seg < segments.Length; seg++)
|
||||
{
|
||||
ResourceBindingSegment segment = segments[seg];
|
||||
|
||||
int binding = segment.Binding;
|
||||
int count = segment.Count;
|
||||
|
||||
if (setIndex == PipelineBase.UniformSetIndex)
|
||||
{
|
||||
entries[seg] = new DescriptorUpdateTemplateEntry()
|
||||
{
|
||||
DescriptorType = DescriptorType.UniformBuffer,
|
||||
DstBinding = (uint)binding,
|
||||
DescriptorCount = (uint)count,
|
||||
Offset = structureOffset,
|
||||
Stride = (nuint)Unsafe.SizeOf<DescriptorBufferInfo>()
|
||||
};
|
||||
|
||||
structureOffset += (nuint)(Unsafe.SizeOf<DescriptorBufferInfo>() * count);
|
||||
}
|
||||
else if (setIndex == PipelineBase.StorageSetIndex)
|
||||
{
|
||||
entries[seg] = new DescriptorUpdateTemplateEntry()
|
||||
{
|
||||
DescriptorType = DescriptorType.StorageBuffer,
|
||||
DstBinding = (uint)binding,
|
||||
DescriptorCount = (uint)count,
|
||||
Offset = structureOffset,
|
||||
Stride = (nuint)Unsafe.SizeOf<DescriptorBufferInfo>()
|
||||
};
|
||||
|
||||
structureOffset += (nuint)(Unsafe.SizeOf<DescriptorBufferInfo>() * count);
|
||||
}
|
||||
else if (setIndex == PipelineBase.TextureSetIndex)
|
||||
{
|
||||
if (segment.Type != ResourceType.BufferTexture)
|
||||
{
|
||||
entries[seg] = new DescriptorUpdateTemplateEntry()
|
||||
{
|
||||
DescriptorType = DescriptorType.CombinedImageSampler,
|
||||
DstBinding = (uint)binding,
|
||||
DescriptorCount = (uint)count,
|
||||
Offset = structureOffset,
|
||||
Stride = (nuint)Unsafe.SizeOf<DescriptorImageInfo>()
|
||||
};
|
||||
|
||||
structureOffset += (nuint)(Unsafe.SizeOf<DescriptorImageInfo>() * count);
|
||||
}
|
||||
else
|
||||
{
|
||||
entries[seg] = new DescriptorUpdateTemplateEntry()
|
||||
{
|
||||
DescriptorType = DescriptorType.UniformTexelBuffer,
|
||||
DstBinding = (uint)binding,
|
||||
DescriptorCount = (uint)count,
|
||||
Offset = structureOffset,
|
||||
Stride = (nuint)Unsafe.SizeOf<BufferView>()
|
||||
};
|
||||
|
||||
structureOffset += (nuint)(Unsafe.SizeOf<BufferView>() * count);
|
||||
}
|
||||
}
|
||||
else if (setIndex == PipelineBase.ImageSetIndex)
|
||||
{
|
||||
if (segment.Type != ResourceType.BufferImage)
|
||||
{
|
||||
entries[seg] = new DescriptorUpdateTemplateEntry()
|
||||
{
|
||||
DescriptorType = DescriptorType.StorageImage,
|
||||
DstBinding = (uint)binding,
|
||||
DescriptorCount = (uint)count,
|
||||
Offset = structureOffset,
|
||||
Stride = (nuint)Unsafe.SizeOf<DescriptorImageInfo>()
|
||||
};
|
||||
|
||||
structureOffset += (nuint)(Unsafe.SizeOf<DescriptorImageInfo>() * count);
|
||||
}
|
||||
else
|
||||
{
|
||||
entries[seg] = new DescriptorUpdateTemplateEntry()
|
||||
{
|
||||
DescriptorType = DescriptorType.StorageTexelBuffer,
|
||||
DstBinding = (uint)binding,
|
||||
DescriptorCount = (uint)count,
|
||||
Offset = structureOffset,
|
||||
Stride = (nuint)Unsafe.SizeOf<BufferView>()
|
||||
};
|
||||
|
||||
structureOffset += (nuint)(Unsafe.SizeOf<BufferView>() * count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Size = (int)structureOffset;
|
||||
|
||||
var info = new DescriptorUpdateTemplateCreateInfo()
|
||||
{
|
||||
SType = StructureType.DescriptorUpdateTemplateCreateInfo,
|
||||
DescriptorUpdateEntryCount = (uint)segments.Length,
|
||||
PDescriptorUpdateEntries = entries,
|
||||
|
||||
TemplateType = DescriptorUpdateTemplateType.DescriptorSet,
|
||||
DescriptorSetLayout = plce.DescriptorSetLayouts[setIndex],
|
||||
PipelineBindPoint = pbp,
|
||||
PipelineLayout = plce.PipelineLayout,
|
||||
Set = (uint)setIndex,
|
||||
};
|
||||
|
||||
DescriptorUpdateTemplate result;
|
||||
gd.Api.CreateDescriptorUpdateTemplate(device, &info, null, &result).ThrowOnError();
|
||||
|
||||
Template = result;
|
||||
}
|
||||
|
||||
public unsafe void Dispose()
|
||||
{
|
||||
_gd.Api.DestroyDescriptorUpdateTemplate(_device, Template, null);
|
||||
}
|
||||
}
|
||||
}
|
65
src/Ryujinx.Graphics.Vulkan/DescriptorSetTemplateUpdater.cs
Normal file
65
src/Ryujinx.Graphics.Vulkan/DescriptorSetTemplateUpdater.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using Ryujinx.Common;
|
||||
using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
ref struct DescriptorSetTemplateWriter
|
||||
{
|
||||
private Span<byte> _data;
|
||||
|
||||
public DescriptorSetTemplateWriter(Span<byte> data)
|
||||
{
|
||||
_data = data;
|
||||
}
|
||||
|
||||
public void Push<T>(ReadOnlySpan<T> values) where T : unmanaged
|
||||
{
|
||||
Span<T> target = MemoryMarshal.Cast<byte, T>(_data);
|
||||
|
||||
values.CopyTo(target);
|
||||
|
||||
_data = _data[(Unsafe.SizeOf<T>() * values.Length)..];
|
||||
}
|
||||
}
|
||||
|
||||
unsafe class DescriptorSetTemplateUpdater : IDisposable
|
||||
{
|
||||
private const int SizeGranularity = 512;
|
||||
|
||||
private DescriptorSetTemplate _activeTemplate;
|
||||
private NativeArray<byte> _data;
|
||||
|
||||
private void EnsureSize(int size)
|
||||
{
|
||||
if (_data == null || _data.Length < size)
|
||||
{
|
||||
_data?.Dispose();
|
||||
|
||||
int dataSize = BitUtils.AlignUp(size, SizeGranularity);
|
||||
_data = new NativeArray<byte>(dataSize);
|
||||
}
|
||||
}
|
||||
|
||||
public DescriptorSetTemplateWriter Begin(DescriptorSetTemplate template)
|
||||
{
|
||||
_activeTemplate = template;
|
||||
|
||||
EnsureSize(template.Size);
|
||||
|
||||
return new DescriptorSetTemplateWriter(new Span<byte>(_data.Pointer, template.Size));
|
||||
}
|
||||
|
||||
public void Commit(VulkanRenderer gd, Device device, DescriptorSet set)
|
||||
{
|
||||
gd.Api.UpdateDescriptorSetWithTemplate(device, set, _activeTemplate.Template, _data.Pointer);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_data?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
@@ -35,6 +35,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
|
||||
private readonly VulkanRenderer _gd;
|
||||
private readonly Device _device;
|
||||
private readonly PipelineBase _pipeline;
|
||||
private ShaderCollection _program;
|
||||
|
||||
@@ -54,6 +55,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private readonly BufferView[] _bufferTextures;
|
||||
private readonly BufferView[] _bufferImages;
|
||||
|
||||
private readonly DescriptorSetTemplateUpdater _templateUpdater;
|
||||
|
||||
private BitMapStruct<Array2<long>> _uniformSet;
|
||||
private BitMapStruct<Array2<long>> _storageSet;
|
||||
private BitMapStruct<Array2<long>> _uniformMirrored;
|
||||
@@ -78,9 +81,10 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
private readonly TextureView _dummyTexture;
|
||||
private readonly SamplerHolder _dummySampler;
|
||||
|
||||
public DescriptorSetUpdater(VulkanRenderer gd, PipelineBase pipeline)
|
||||
public DescriptorSetUpdater(VulkanRenderer gd, Device device, PipelineBase pipeline)
|
||||
{
|
||||
_gd = gd;
|
||||
_device = device;
|
||||
_pipeline = pipeline;
|
||||
|
||||
// Some of the bindings counts needs to be multiplied by 2 because we have buffer and
|
||||
@@ -152,6 +156,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
0,
|
||||
0,
|
||||
1f));
|
||||
|
||||
_templateUpdater = new();
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
@@ -509,6 +515,10 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
DescriptorSetTemplate template = program.Templates[setIndex];
|
||||
|
||||
DescriptorSetTemplateWriter tu = _templateUpdater.Begin(template);
|
||||
|
||||
foreach (ResourceBindingSegment segment in bindingSegments)
|
||||
{
|
||||
int binding = segment.Binding;
|
||||
@@ -531,7 +541,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
|
||||
ReadOnlySpan<DescriptorBufferInfo> uniformBuffers = _uniformBuffers;
|
||||
dsc.UpdateBuffers(0, binding, uniformBuffers.Slice(binding, count), DescriptorType.UniformBuffer);
|
||||
|
||||
tu.Push(uniformBuffers.Slice(binding, count));
|
||||
}
|
||||
else if (setIndex == PipelineBase.StorageSetIndex)
|
||||
{
|
||||
@@ -556,7 +567,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
|
||||
ReadOnlySpan<DescriptorBufferInfo> storageBuffers = _storageBuffers;
|
||||
dsc.UpdateBuffers(0, binding, storageBuffers.Slice(binding, count), DescriptorType.StorageBuffer);
|
||||
|
||||
tu.Push(storageBuffers.Slice(binding, count));
|
||||
}
|
||||
else if (setIndex == PipelineBase.TextureSetIndex)
|
||||
{
|
||||
@@ -582,7 +594,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
dsc.UpdateImages(0, binding, textures[..count], DescriptorType.CombinedImageSampler);
|
||||
tu.Push<DescriptorImageInfo>(textures[..count]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -593,7 +605,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
bufferTextures[i] = _bufferTextureRefs[binding + i]?.GetBufferView(cbs, false) ?? default;
|
||||
}
|
||||
|
||||
dsc.UpdateBufferImages(0, binding, bufferTextures[..count], DescriptorType.UniformTexelBuffer);
|
||||
tu.Push<BufferView>(bufferTextures[..count]);
|
||||
}
|
||||
}
|
||||
else if (setIndex == PipelineBase.ImageSetIndex)
|
||||
@@ -607,7 +619,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
images[i].ImageView = _imageRefs[binding + i]?.Get(cbs).Value ?? default;
|
||||
}
|
||||
|
||||
dsc.UpdateImages(0, binding, images[..count], DescriptorType.StorageImage);
|
||||
tu.Push<DescriptorImageInfo>(images[..count]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -618,12 +630,13 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
bufferImages[i] = _bufferImageRefs[binding + i]?.GetBufferView(cbs, _bufferImageFormats[binding + i], true) ?? default;
|
||||
}
|
||||
|
||||
dsc.UpdateBufferImages(0, binding, bufferImages[..count], DescriptorType.StorageTexelBuffer);
|
||||
tu.Push<BufferView>(bufferImages[..count]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var sets = dsc.GetSets();
|
||||
_templateUpdater.Commit(_gd, _device, sets[0]);
|
||||
|
||||
_gd.Api.CmdBindDescriptorSets(cbs.CommandBuffer, pbp, _program.PipelineLayout, (uint)setIndex, 1, sets, 0, ReadOnlySpan<uint>.Empty);
|
||||
}
|
||||
@@ -736,6 +749,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
_dummyTexture.Dispose();
|
||||
_dummySampler.Dispose();
|
||||
_templateUpdater.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -102,7 +102,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
gd.Api.CreatePipelineCache(device, pipelineCacheCreateInfo, null, out PipelineCache).ThrowOnError();
|
||||
|
||||
_descriptorSetUpdater = new DescriptorSetUpdater(gd, this);
|
||||
_descriptorSetUpdater = new DescriptorSetUpdater(gd, device, this);
|
||||
_vertexBufferUpdater = new VertexBufferUpdater(gd);
|
||||
|
||||
_transformFeedbackBuffers = new BufferState[Constants.MaxTransformFeedbackBuffers];
|
||||
|
@@ -26,6 +26,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
public ResourceBindingSegment[][] ClearSegments { get; }
|
||||
public ResourceBindingSegment[][] BindingSegments { get; }
|
||||
public DescriptorSetTemplate[] Templates { get; }
|
||||
|
||||
public ProgramLinkStatus LinkStatus { get; private set; }
|
||||
|
||||
@@ -118,6 +119,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
ClearSegments = BuildClearSegments(resourceLayout.Sets);
|
||||
BindingSegments = BuildBindingSegments(resourceLayout.SetUsages);
|
||||
Templates = BuildTemplates();
|
||||
|
||||
_compileTask = Task.CompletedTask;
|
||||
_firstBackgroundUse = false;
|
||||
@@ -241,6 +243,23 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
return segments;
|
||||
}
|
||||
|
||||
private DescriptorSetTemplate[] BuildTemplates()
|
||||
{
|
||||
var templates = new DescriptorSetTemplate[BindingSegments.Length];
|
||||
|
||||
for (int setIndex = 0; setIndex < BindingSegments.Length; setIndex++)
|
||||
{
|
||||
ResourceBindingSegment[] segments = BindingSegments[setIndex];
|
||||
|
||||
if (segments != null && segments.Length > 0)
|
||||
{
|
||||
templates[setIndex] = new DescriptorSetTemplate(_gd, _device, segments, _plce, IsCompute ? PipelineBindPoint.Compute : PipelineBindPoint.Graphics, setIndex);
|
||||
}
|
||||
}
|
||||
|
||||
return templates;
|
||||
}
|
||||
|
||||
private async Task BackgroundCompilation()
|
||||
{
|
||||
await Task.WhenAll(_shaders.Select(shader => shader.CompileTask));
|
||||
@@ -504,6 +523,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < Templates.Length; i++)
|
||||
{
|
||||
Templates[i]?.Dispose();
|
||||
}
|
||||
|
||||
if (_dummyRenderPass.Value.Handle != 0)
|
||||
{
|
||||
_dummyRenderPass.Dispose();
|
||||
|
@@ -57,6 +57,8 @@ namespace Ryujinx.HLE.HOS
|
||||
|
||||
public void Execute(IExecutionContext context, ulong codeAddress)
|
||||
{
|
||||
// We must wait until shader cache is loaded, among other things, before executing CPU code.
|
||||
_gpuContext.WaitUntilGpuReady();
|
||||
_cpuContext.Execute(context, codeAddress);
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,3 @@
|
||||
using ARMeilleure.Translation;
|
||||
using CommandLine;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using Ryujinx.Audio.Backends.SDL2;
|
||||
@@ -710,9 +709,6 @@ namespace Ryujinx.Headless.SDL2
|
||||
}
|
||||
|
||||
SetupProgressHandler();
|
||||
|
||||
Translator.IsReadyForTranslation.Reset();
|
||||
|
||||
ExecutionEntrypoint();
|
||||
|
||||
return true;
|
||||
|
@@ -1,4 +1,3 @@
|
||||
using ARMeilleure.Translation;
|
||||
using Ryujinx.Common.Configuration;
|
||||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Logging;
|
||||
@@ -276,7 +275,6 @@ namespace Ryujinx.Headless.SDL2
|
||||
{
|
||||
Device.Gpu.SetGpuThread();
|
||||
Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token);
|
||||
Translator.IsReadyForTranslation.Set();
|
||||
|
||||
while (_isActive)
|
||||
{
|
||||
|
@@ -61,7 +61,6 @@ namespace Ryujinx.Tests.Cpu
|
||||
_memory.Map(DataBaseAddress, Size, Size, MemoryMapFlags.Private);
|
||||
|
||||
_context = CpuContext.CreateExecutionContext();
|
||||
Translator.IsReadyForTranslation.Set();
|
||||
|
||||
_cpuContext = new CpuContext(_memory, for64Bit: true);
|
||||
|
||||
|
@@ -56,7 +56,6 @@ namespace Ryujinx.Tests.Cpu
|
||||
|
||||
_context = CpuContext.CreateExecutionContext();
|
||||
_context.IsAarch32 = true;
|
||||
Translator.IsReadyForTranslation.Set();
|
||||
|
||||
_cpuContext = new CpuContext(_memory, for64Bit: false);
|
||||
|
||||
|
@@ -1,4 +1,3 @@
|
||||
using ARMeilleure.Translation;
|
||||
using Gtk;
|
||||
using LibHac.Common;
|
||||
using LibHac.Common.Keys;
|
||||
@@ -931,8 +930,6 @@ namespace Ryujinx.Ui
|
||||
|
||||
_deviceExitStatus.Reset();
|
||||
|
||||
Translator.IsReadyForTranslation.Reset();
|
||||
|
||||
Thread windowThread = new(CreateGameWindow)
|
||||
{
|
||||
Name = "GUI.WindowThread",
|
||||
|
@@ -1,4 +1,3 @@
|
||||
using ARMeilleure.Translation;
|
||||
using Gdk;
|
||||
using Gtk;
|
||||
using Ryujinx.Common;
|
||||
@@ -450,7 +449,6 @@ namespace Ryujinx.Ui
|
||||
{
|
||||
Device.Gpu.SetGpuThread();
|
||||
Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token);
|
||||
Translator.IsReadyForTranslation.Set();
|
||||
|
||||
Renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync);
|
||||
|
||||
|
Reference in New Issue
Block a user