mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2026-06-08 21:39:15 +00:00
Compare commits
13 Commits
Canary-1.3
...
09d4bfe6d8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
09d4bfe6d8 | ||
|
|
5ed94c365b | ||
|
|
fef93a453a | ||
|
|
2274a32813 | ||
|
|
34190c9184 | ||
|
|
18501d01f3 | ||
|
|
6780ff0d8a | ||
|
|
3cc02ebaef | ||
|
|
6938265651 | ||
|
|
f6328ebb69 | ||
|
|
445924102e | ||
|
|
cf72e189b7 | ||
|
|
8bd290cc57 |
@@ -168,7 +168,7 @@ namespace ARMeilleure.Common
|
||||
{
|
||||
_allocated.Dispose();
|
||||
|
||||
foreach (IntPtr page in _pages.Values)
|
||||
foreach (nint page in _pages.Values)
|
||||
{
|
||||
NativeAllocator.Instance.Free((void*)page);
|
||||
}
|
||||
|
||||
@@ -44,12 +44,12 @@ namespace Ryujinx.Audio.Backends.Apple
|
||||
|
||||
int result = AudioQueueNewOutput(
|
||||
ref format,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
nint.Zero,
|
||||
nint.Zero,
|
||||
nint.Zero,
|
||||
nint.Zero,
|
||||
0,
|
||||
out IntPtr testQueue);
|
||||
out nint testQueue);
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
@@ -95,12 +95,12 @@ namespace Ryujinx.Audio.Backends.Apple
|
||||
GetAudioFormat(SampleFormat.PcmInt16, Constants.TargetSampleRate, 2);
|
||||
int result = AudioQueueNewOutput(
|
||||
ref format,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
nint.Zero,
|
||||
nint.Zero,
|
||||
nint.Zero,
|
||||
nint.Zero,
|
||||
0,
|
||||
out IntPtr testQueue);
|
||||
out nint testQueue);
|
||||
|
||||
if (result == 0)
|
||||
{
|
||||
@@ -110,8 +110,9 @@ namespace Ryujinx.Audio.Backends.Apple
|
||||
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Audio, $"Failed to check if AudioToolbox is supported: {e.Message}\n{e.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
using Ryujinx.Audio.Backends.Common;
|
||||
using Ryujinx.Audio.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Runtime.Versioning;
|
||||
using Ryujinx.Audio.Backends.Apple.Native;
|
||||
using static Ryujinx.Audio.Backends.Apple.Native.AudioToolbox;
|
||||
using static Ryujinx.Audio.Backends.Apple.AppleHardwareDeviceDriver;
|
||||
|
||||
namespace Ryujinx.Audio.Backends.Apple
|
||||
{
|
||||
@@ -27,8 +24,8 @@ namespace Ryujinx.Audio.Backends.Apple
|
||||
private readonly AudioQueueOutputCallback _callbackDelegate;
|
||||
private readonly GCHandle _gcHandle;
|
||||
|
||||
private IntPtr _audioQueue;
|
||||
private readonly IntPtr[] _audioQueueBuffers = new IntPtr[NumBuffers];
|
||||
private nint _audioQueue;
|
||||
private readonly nint[] _audioQueueBuffers = new nint[NumBuffers];
|
||||
private readonly int[] _bufferBytesFilled = new int[NumBuffers];
|
||||
|
||||
private readonly int _bytesPerFrame;
|
||||
@@ -41,9 +38,9 @@ namespace Ryujinx.Audio.Backends.Apple
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
private delegate void AudioQueueOutputCallback(
|
||||
IntPtr userData,
|
||||
IntPtr audioQueue,
|
||||
IntPtr buffer);
|
||||
nint userData,
|
||||
nint audioQueue,
|
||||
nint buffer);
|
||||
|
||||
public AppleHardwareDeviceSession(
|
||||
AppleHardwareDeviceDriver driver,
|
||||
@@ -72,15 +69,15 @@ namespace Ryujinx.Audio.Backends.Apple
|
||||
RequestedSampleRate,
|
||||
RequestedChannelCount);
|
||||
|
||||
IntPtr callbackPtr = Marshal.GetFunctionPointerForDelegate(_callbackDelegate);
|
||||
IntPtr userData = GCHandle.ToIntPtr(_gcHandle);
|
||||
nint callbackPtr = Marshal.GetFunctionPointerForDelegate(_callbackDelegate);
|
||||
nint userData = GCHandle.ToIntPtr(_gcHandle);
|
||||
|
||||
int result = AudioQueueNewOutput(
|
||||
ref format,
|
||||
callbackPtr,
|
||||
userData,
|
||||
IntPtr.Zero,
|
||||
IntPtr.Zero,
|
||||
nint.Zero,
|
||||
nint.Zero,
|
||||
0,
|
||||
out _audioQueue);
|
||||
|
||||
@@ -102,7 +99,7 @@ namespace Ryujinx.Audio.Backends.Apple
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void PrimeBuffer(IntPtr bufferPtr, int bufferIndex)
|
||||
private unsafe void PrimeBuffer(nint bufferPtr, int bufferIndex)
|
||||
{
|
||||
AudioQueueBuffer* buffer = (AudioQueueBuffer*)bufferPtr;
|
||||
|
||||
@@ -126,12 +123,12 @@ namespace Ryujinx.Audio.Backends.Apple
|
||||
buffer->AudioDataByteSize = (uint)capacityBytes;
|
||||
_bufferBytesFilled[bufferIndex] = bytesToRead;
|
||||
|
||||
AudioQueueEnqueueBuffer(_audioQueue, bufferPtr, 0, IntPtr.Zero);
|
||||
AudioQueueEnqueueBuffer(_audioQueue, bufferPtr, 0, nint.Zero);
|
||||
}
|
||||
|
||||
private void OutputCallback(IntPtr userData, IntPtr audioQueue, IntPtr bufferPtr)
|
||||
private void OutputCallback(nint userData, nint audioQueue, nint bufferPtr)
|
||||
{
|
||||
if (!_started || bufferPtr == IntPtr.Zero)
|
||||
if (!_started || bufferPtr == nint.Zero)
|
||||
return;
|
||||
|
||||
int bufferIndex = Array.IndexOf(_audioQueueBuffers, bufferPtr);
|
||||
@@ -176,7 +173,7 @@ namespace Ryujinx.Audio.Backends.Apple
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void ApplyVolume(IntPtr dataPtr, int byteSize)
|
||||
private unsafe void ApplyVolume(nint dataPtr, int byteSize)
|
||||
{
|
||||
float volume = Math.Clamp(_volume * _driver.Volume, 0f, 1f);
|
||||
if (volume >= 0.999f)
|
||||
@@ -226,7 +223,7 @@ namespace Ryujinx.Audio.Backends.Apple
|
||||
return;
|
||||
|
||||
_started = true;
|
||||
AudioQueueStart(_audioQueue, IntPtr.Zero);
|
||||
AudioQueueStart(_audioQueue, nint.Zero);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,11 +262,11 @@ namespace Ryujinx.Audio.Backends.Apple
|
||||
{
|
||||
Stop();
|
||||
|
||||
if (_audioQueue != IntPtr.Zero)
|
||||
if (_audioQueue != nint.Zero)
|
||||
{
|
||||
AudioQueueStop(_audioQueue, true);
|
||||
AudioQueueDispose(_audioQueue, true);
|
||||
_audioQueue = IntPtr.Zero;
|
||||
_audioQueue = nint.Zero;
|
||||
}
|
||||
|
||||
if (_gcHandle.IsAllocated)
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Ryujinx.Audio.Backends.SoundIo.Native
|
||||
unsafe
|
||||
{
|
||||
int* frameCountPtr = &nativeFrameCount;
|
||||
IntPtr* arenasPtr = &arenas;
|
||||
nint* arenasPtr = &arenas;
|
||||
CheckError(soundio_outstream_begin_write(_context, (nint)arenasPtr, (nint)frameCountPtr));
|
||||
|
||||
frameCount = *frameCountPtr;
|
||||
|
||||
@@ -30,9 +30,9 @@ namespace ARMeilleure.Common
|
||||
/// <summary>
|
||||
/// Base address for the page.
|
||||
/// </summary>
|
||||
public readonly IntPtr Address;
|
||||
public readonly nint Address;
|
||||
|
||||
public AddressTablePage(bool isSparse, IntPtr address)
|
||||
public AddressTablePage(bool isSparse, nint address)
|
||||
{
|
||||
IsSparse = isSparse;
|
||||
Address = address;
|
||||
@@ -47,20 +47,20 @@ namespace ARMeilleure.Common
|
||||
public readonly SparseMemoryBlock Block;
|
||||
private readonly TrackingEventDelegate _trackingEvent;
|
||||
|
||||
public TableSparseBlock(ulong size, Action<IntPtr> ensureMapped, PageInitDelegate pageInit)
|
||||
public TableSparseBlock(ulong size, Action<nint> ensureMapped, PageInitDelegate pageInit)
|
||||
{
|
||||
SparseMemoryBlock block = new(size, pageInit, null);
|
||||
|
||||
_trackingEvent = (address, size, write) =>
|
||||
{
|
||||
ulong pointer = (ulong)block.Block.Pointer + address;
|
||||
ensureMapped((IntPtr)pointer);
|
||||
ensureMapped((nint)pointer);
|
||||
return pointer;
|
||||
};
|
||||
|
||||
bool added = NativeSignalHandler.AddTrackedRegion(
|
||||
(nuint)block.Block.Pointer,
|
||||
(nuint)(block.Block.Pointer + (IntPtr)block.Block.Size),
|
||||
(nuint)(block.Block.Pointer + (nint)block.Block.Size),
|
||||
Marshal.GetFunctionPointerForDelegate(_trackingEvent));
|
||||
|
||||
if (!added)
|
||||
@@ -116,7 +116,7 @@ namespace ARMeilleure.Common
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IntPtr Base
|
||||
public nint Base
|
||||
{
|
||||
get
|
||||
{
|
||||
@@ -124,7 +124,7 @@ namespace ARMeilleure.Common
|
||||
|
||||
lock (_pages)
|
||||
{
|
||||
return (IntPtr)GetRootPage();
|
||||
return (nint)GetRootPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -240,7 +240,7 @@ namespace ARMeilleure.Common
|
||||
|
||||
long index = Levels[^1].GetValue(address);
|
||||
|
||||
EnsureMapped((IntPtr)(page + index));
|
||||
EnsureMapped((nint)(page + index));
|
||||
|
||||
return ref page[index];
|
||||
}
|
||||
@@ -284,7 +284,7 @@ namespace ARMeilleure.Common
|
||||
/// Ensure the given pointer is mapped in any overlapping sparse reservations.
|
||||
/// </summary>
|
||||
/// <param name="ptr">Pointer to be mapped</param>
|
||||
private void EnsureMapped(IntPtr ptr)
|
||||
private void EnsureMapped(nint ptr)
|
||||
{
|
||||
if (Sparse)
|
||||
{
|
||||
@@ -299,7 +299,7 @@ namespace ARMeilleure.Common
|
||||
{
|
||||
SparseMemoryBlock sparse = reserved.Block;
|
||||
|
||||
if (ptr >= sparse.Block.Pointer && ptr < sparse.Block.Pointer + (IntPtr)sparse.Block.Size)
|
||||
if (ptr >= sparse.Block.Pointer && ptr < sparse.Block.Pointer + (nint)sparse.Block.Size)
|
||||
{
|
||||
sparse.EnsureMapped((ulong)(ptr - sparse.Block.Pointer));
|
||||
|
||||
@@ -319,15 +319,15 @@ namespace ARMeilleure.Common
|
||||
/// </summary>
|
||||
/// <param name="level">Level to get the fill value for</param>
|
||||
/// <returns>The fill value</returns>
|
||||
private IntPtr GetFillValue(int level)
|
||||
private nint GetFillValue(int level)
|
||||
{
|
||||
if (_fillBottomLevel != null && level == Levels.Length - 2)
|
||||
{
|
||||
return (IntPtr)_fillBottomLevelPtr;
|
||||
return (nint)_fillBottomLevelPtr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return IntPtr.Zero;
|
||||
return nint.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -379,7 +379,7 @@ namespace ARMeilleure.Common
|
||||
/// <param name="fill">Fill value</param>
|
||||
/// <param name="leaf"><see langword="true"/> if leaf; otherwise <see langword="false"/></param>
|
||||
/// <returns>Allocated block</returns>
|
||||
private IntPtr Allocate<T>(int length, T fill, bool leaf) where T : unmanaged
|
||||
private nint Allocate<T>(int length, T fill, bool leaf) where T : unmanaged
|
||||
{
|
||||
int size = sizeof(T) * length;
|
||||
|
||||
@@ -405,7 +405,7 @@ namespace ARMeilleure.Common
|
||||
}
|
||||
}
|
||||
|
||||
page = new AddressTablePage(true, block.Block.Pointer + (IntPtr)_sparseReservedOffset);
|
||||
page = new AddressTablePage(true, block.Block.Pointer + (nint)_sparseReservedOffset);
|
||||
|
||||
_sparseReservedOffset += (ulong)size;
|
||||
|
||||
@@ -413,7 +413,7 @@ namespace ARMeilleure.Common
|
||||
}
|
||||
else
|
||||
{
|
||||
IntPtr address = (IntPtr)NativeAllocator.Instance.Allocate((uint)size);
|
||||
nint address = (nint)NativeAllocator.Instance.Allocate((uint)size);
|
||||
page = new AddressTablePage(false, address);
|
||||
|
||||
Span<T> span = new((void*)page.Address, length);
|
||||
|
||||
@@ -658,7 +658,7 @@ namespace Ryujinx.Graphics.Gpu.Image
|
||||
|
||||
bool canImport = Storage.Info.IsLinear && Storage.Info.Stride >= Storage.Info.Width * Storage.Info.FormatInfo.BytesPerPixel;
|
||||
|
||||
IntPtr hostPointer = canImport ? _physicalMemory.GetHostPointer(Storage.Range) : 0;
|
||||
nint hostPointer = canImport ? _physicalMemory.GetHostPointer(Storage.Range) : 0;
|
||||
|
||||
if (hostPointer != 0 && _context.Renderer.PrepareHostMapping(hostPointer, Storage.Size))
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
@@ -114,7 +115,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
cbs.AddDependant(this);
|
||||
|
||||
// We need to add a dependency on the command buffer to all objects this object
|
||||
// references aswell.
|
||||
// references as well.
|
||||
if (_referencedObjs != null)
|
||||
{
|
||||
for (int i = 0; i < _referencedObjs.Length; i++)
|
||||
@@ -176,6 +177,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
// This can somehow become -1.
|
||||
// Logger.Info?.PrintMsg(LogClass.Gpu, $"_referenceCount: {_referenceCount}");
|
||||
Debug.Assert(_referenceCount >= 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Ryujinx.Graphics.Vulkan.MoltenVK
|
||||
|
||||
public static void Initialize()
|
||||
{
|
||||
IntPtr configSize = (nint)Marshal.SizeOf<MVKConfiguration>();
|
||||
nint configSize = (nint)Marshal.SizeOf<MVKConfiguration>();
|
||||
|
||||
vkGetMoltenVKConfigurationMVK(nint.Zero, out MVKConfiguration config, configSize);
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
enabledExtensions = enabledExtensions.Append(ExtDebugUtils.ExtensionName).ToArray();
|
||||
}
|
||||
|
||||
IntPtr appName = Marshal.StringToHGlobalAnsi(AppName);
|
||||
nint appName = Marshal.StringToHGlobalAnsi(AppName);
|
||||
|
||||
ApplicationInfo applicationInfo = new()
|
||||
{
|
||||
@@ -166,7 +166,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
internal static DeviceInfo[] GetSuitablePhysicalDevices(Vk api)
|
||||
{
|
||||
IntPtr appName = Marshal.StringToHGlobalAnsi(AppName);
|
||||
nint appName = Marshal.StringToHGlobalAnsi(AppName);
|
||||
|
||||
ApplicationInfo applicationInfo = new()
|
||||
{
|
||||
|
||||
@@ -1059,7 +1059,7 @@ namespace Ryujinx.HLE.FileSystem
|
||||
}
|
||||
}
|
||||
|
||||
public static bool AreKeysAlredyPresent(string pathToCheck)
|
||||
public static bool AreKeysAlreadyPresent(string pathToCheck)
|
||||
{
|
||||
string[] fileNames = ["prod.keys", "title.keys", "console.keys", "dev.keys"];
|
||||
foreach (string file in fileNames)
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd.Impl
|
||||
|
||||
public bool Blocking { get => Socket.Blocking; set => Socket.Blocking = value; }
|
||||
|
||||
public nint Handle => IntPtr.Zero;
|
||||
public nint Handle => nint.Zero;
|
||||
|
||||
public IPEndPoint RemoteEndPoint => Socket.RemoteEndPoint as IPEndPoint;
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ using Ryujinx.HLE.Loaders.Executables;
|
||||
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Path = System.IO.Path;
|
||||
|
||||
@@ -27,10 +28,15 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
|
||||
private ulong _latestPid;
|
||||
|
||||
public ProcessResult ActiveApplication
|
||||
public ProcessResult? ActiveApplication
|
||||
{
|
||||
get
|
||||
{
|
||||
return _processesByPid.GetValueOrDefault(_latestPid);
|
||||
|
||||
// Using this if statement locks up the UI and prevents a new game from loading.
|
||||
// Haven't quite deduced why yet.
|
||||
|
||||
if (!_processesByPid.TryGetValue(_latestPid, out ProcessResult value))
|
||||
throw new RyujinxException(
|
||||
$"The HLE Process map did not have a process with ID {_latestPid}. Are you missing firmware?");
|
||||
@@ -144,7 +150,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
public bool LoadUnpackedNca(string exeFsDirPath, string romFsPath = null)
|
||||
{
|
||||
ProcessResult processResult = new LocalFileSystem(exeFsDirPath).Load(_device, romFsPath);
|
||||
|
||||
|
||||
if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult))
|
||||
{
|
||||
if (processResult.Start(_device))
|
||||
|
||||
@@ -4,7 +4,6 @@ using LibHac.Ns;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Cpu;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
||||
using Ryujinx.Horizon.Common;
|
||||
|
||||
namespace Ryujinx.HLE.Loaders.Processes
|
||||
@@ -52,6 +51,7 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
|
||||
if (metaLoader is not null)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Application,$"metaLoader: {metaLoader}");
|
||||
ulong programId = metaLoader.ProgramId;
|
||||
|
||||
Name = ApplicationControlProperties.Title[(int)titleLanguage].NameString.ToString();
|
||||
@@ -71,8 +71,15 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
ProgramId = programId;
|
||||
ProgramIdText = $"{programId:x16}";
|
||||
Is64Bit = metaLoader.IsProgram64Bit;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Application,$"metaLoader is null !!!");
|
||||
ProcessId = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
DiskCacheEnabled = diskCacheEnabled;
|
||||
AllowCodeMemoryForJit = allowCodeMemoryForJit;
|
||||
}
|
||||
|
||||
@@ -159,7 +159,7 @@ namespace Ryujinx.Memory.WindowsShared
|
||||
{
|
||||
SplitForMap((ulong)location, (ulong)size, srcOffset);
|
||||
|
||||
IntPtr ptr = WindowsApi.MapViewOfFile3(
|
||||
nint ptr = WindowsApi.MapViewOfFile3(
|
||||
sharedMemory,
|
||||
WindowsApi.CurrentProcessHandle,
|
||||
location,
|
||||
|
||||
@@ -227,7 +227,7 @@ namespace Ryujinx.Tests.Memory
|
||||
|
||||
// Create some info to be used for managing the native writing loop.
|
||||
int stateSize = Unsafe.SizeOf<NativeWriteLoopState>();
|
||||
IntPtr statePtr = Marshal.AllocHGlobal(stateSize);
|
||||
nint statePtr = Marshal.AllocHGlobal(stateSize);
|
||||
Unsafe.InitBlockUnaligned((void*)statePtr, 0, (uint)stateSize);
|
||||
|
||||
ref NativeWriteLoopState writeLoopState = ref Unsafe.AsRef<NativeWriteLoopState>((void*)statePtr);
|
||||
|
||||
@@ -62,7 +62,7 @@ using VSyncMode = Ryujinx.Common.Configuration.VSyncMode;
|
||||
|
||||
namespace Ryujinx.Ava.Systems
|
||||
{
|
||||
internal class AppHost
|
||||
internal class AppHost : IDisposable
|
||||
{
|
||||
private const int CursorHideIdleTime = 5; // Hide Cursor seconds.
|
||||
private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping.
|
||||
@@ -438,7 +438,7 @@ namespace Ryujinx.Ava.Systems
|
||||
|
||||
SaveBitmapAsPng(bitmapToSave, path);
|
||||
|
||||
Logger.Notice.Print(LogClass.Application, $"Screenshot saved to {path}", "Screenshot");
|
||||
Logger.Notice.Print(LogClass.Application, $"Screenshot saved to '{path}'.", "Screenshot");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -611,27 +611,40 @@ namespace Ryujinx.Ava.Systems
|
||||
|
||||
_isActive = false;
|
||||
|
||||
// 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();
|
||||
|
||||
NpadManager.Dispose();
|
||||
TouchScreenManager.Dispose();
|
||||
Device.Dispose();
|
||||
|
||||
// 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.
|
||||
// If the GPU has no work and is cancelled, we need to handle that as well.
|
||||
|
||||
WaitHandle.WaitAny(new[] { _gpuDoneEvent, _gpuCancellationTokenSource.Token.WaitHandle });
|
||||
_gpuCancellationTokenSource.Dispose();
|
||||
|
||||
// Waiting for work to be finished before we dispose.
|
||||
if (_renderingStarted)
|
||||
{
|
||||
Device.Gpu.WaitUntilGpuReady();
|
||||
}
|
||||
|
||||
_gpuDoneEvent.Dispose();
|
||||
|
||||
DisposeGpu();
|
||||
|
||||
AppExit?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
private void Dispose()
|
||||
// MUST be public to inherit from IDisposable
|
||||
public void Dispose()
|
||||
{
|
||||
if (Device.Processes != null)
|
||||
MainWindowViewModel.UpdateGameMetadata(Device.Processes.ActiveApplication.ProgramIdText, _playTimer.Elapsed);
|
||||
|
||||
{
|
||||
MainWindowViewModel.UpdateGameMetadata(Device.Processes.ActiveApplication?.ProgramIdText,
|
||||
_playTimer.Elapsed);
|
||||
}
|
||||
|
||||
ConfigurationState.Instance.System.IgnoreMissingServices.Event -= UpdateIgnoreMissingServicesState;
|
||||
ConfigurationState.Instance.Graphics.AspectRatio.Event -= UpdateAspectRatioState;
|
||||
ConfigurationState.Instance.System.EnableDockedMode.Event -= UpdateDockedModeState;
|
||||
@@ -646,7 +659,6 @@ namespace Ryujinx.Ava.Systems
|
||||
_topLevel.PointerExited -= TopLevel_PointerExited;
|
||||
|
||||
_gpuCancellationTokenSource.Cancel();
|
||||
_gpuCancellationTokenSource.Dispose();
|
||||
|
||||
_chrono.Stop();
|
||||
_playTimer.Stop();
|
||||
@@ -672,6 +684,12 @@ namespace Ryujinx.Ava.Systems
|
||||
}
|
||||
else
|
||||
{
|
||||
// No use waiting on something that never started work
|
||||
if (_renderingStarted)
|
||||
{
|
||||
Device.Gpu.WaitUntilGpuReady();
|
||||
}
|
||||
|
||||
Device.DisposeGpu();
|
||||
}
|
||||
}
|
||||
@@ -686,7 +704,7 @@ namespace Ryujinx.Ava.Systems
|
||||
_cursorState = CursorStates.ForceChangeCursor;
|
||||
}
|
||||
|
||||
public async Task<bool> LoadGuestApplication(BlitStruct<ApplicationControlProperty>? customNacpData = null)
|
||||
public async Task LoadGuestApplication(CancellationTokenSource cts, BlitStruct<ApplicationControlProperty>? customNacpData = null)
|
||||
{
|
||||
DiscordIntegrationModule.GuestAppStartedAt = Timestamps.Now;
|
||||
|
||||
@@ -715,7 +733,8 @@ namespace Ryujinx.Ava.Systems
|
||||
await UserErrorDialog.ShowUserErrorDialog(userError);
|
||||
Device.Dispose();
|
||||
|
||||
return false;
|
||||
cts.Cancel();
|
||||
throw new OperationCanceledException(cts.Token);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -724,10 +743,11 @@ namespace Ryujinx.Ava.Systems
|
||||
await UserErrorDialog.ShowUserErrorDialog(userError);
|
||||
Device.Dispose();
|
||||
|
||||
return false;
|
||||
cts.Cancel();
|
||||
throw new OperationCanceledException(cts.Token);
|
||||
}
|
||||
|
||||
// Tell the user that we installed a firmware for them.
|
||||
// Tell the user that we installed firmware for them.
|
||||
if (userError is UserError.NoFirmware)
|
||||
{
|
||||
firmwareVersion = ContentManager.GetCurrentFirmwareVersion();
|
||||
@@ -747,7 +767,8 @@ namespace Ryujinx.Ava.Systems
|
||||
await UserErrorDialog.ShowUserErrorDialog(userError);
|
||||
Device.Dispose();
|
||||
|
||||
return false;
|
||||
cts.Cancel();
|
||||
throw new OperationCanceledException(cts.Token);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -762,7 +783,8 @@ namespace Ryujinx.Ava.Systems
|
||||
{
|
||||
Device.Dispose();
|
||||
|
||||
return false;
|
||||
cts.Cancel();
|
||||
throw new OperationCanceledException(cts.Token);
|
||||
}
|
||||
}
|
||||
else if (Directory.Exists(ApplicationPath))
|
||||
@@ -782,20 +804,24 @@ namespace Ryujinx.Ava.Systems
|
||||
|
||||
if (!Device.LoadCart(ApplicationPath, romFsFiles[0]))
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(
|
||||
"Please specify an unpacked game directory with a valid exefs or NSO/NRO.");
|
||||
Device.Dispose();
|
||||
|
||||
return false;
|
||||
cts.Cancel();
|
||||
throw new OperationCanceledException(cts.Token);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS.");
|
||||
|
||||
if (!Device.LoadCart(ApplicationPath))
|
||||
{
|
||||
await ContentDialogHelper.CreateErrorDialog(
|
||||
"Please specify an unpacked game directory with a valid exefs or NSO/NRO.");
|
||||
Device.Dispose();
|
||||
|
||||
return false;
|
||||
cts.Cancel();
|
||||
throw new OperationCanceledException(cts.Token);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -813,7 +839,8 @@ namespace Ryujinx.Ava.Systems
|
||||
{
|
||||
Device.Dispose();
|
||||
|
||||
return false;
|
||||
cts.Cancel();
|
||||
throw new OperationCanceledException(cts.Token);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -826,7 +853,8 @@ namespace Ryujinx.Ava.Systems
|
||||
{
|
||||
Device.Dispose();
|
||||
|
||||
return false;
|
||||
cts.Cancel();
|
||||
throw new OperationCanceledException(cts.Token);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -840,7 +868,8 @@ namespace Ryujinx.Ava.Systems
|
||||
{
|
||||
Device.Dispose();
|
||||
|
||||
return false;
|
||||
cts.Cancel();
|
||||
throw new OperationCanceledException(cts.Token);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -855,7 +884,8 @@ namespace Ryujinx.Ava.Systems
|
||||
{
|
||||
Device.Dispose();
|
||||
|
||||
return false;
|
||||
cts.Cancel();
|
||||
throw new OperationCanceledException(cts.Token);
|
||||
}
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
@@ -864,7 +894,8 @@ namespace Ryujinx.Ava.Systems
|
||||
|
||||
Device.Dispose();
|
||||
|
||||
return false;
|
||||
cts.Cancel();
|
||||
throw new OperationCanceledException(cts.Token);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -873,19 +904,18 @@ namespace Ryujinx.Ava.Systems
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file.");
|
||||
Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NSO/NRO file.");
|
||||
|
||||
Device.Dispose();
|
||||
|
||||
return false;
|
||||
cts.Cancel();
|
||||
throw new OperationCanceledException(cts.Token);
|
||||
}
|
||||
|
||||
ApplicationLibrary.LoadAndSaveMetaData(Device.Processes.ActiveApplication.ProgramIdText,
|
||||
appMetadata => appMetadata.UpdatePreGame()
|
||||
);
|
||||
_playTimer.Start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void Resume()
|
||||
@@ -895,7 +925,7 @@ namespace Ryujinx.Ava.Systems
|
||||
_viewModel.IsPaused = false;
|
||||
_playTimer.Start();
|
||||
_viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowOldUI);
|
||||
Logger.Info?.Print(LogClass.Emulation, "Emulation was resumed");
|
||||
Logger.Info?.Print(LogClass.Emulation, "Emulation was resumed.");
|
||||
}
|
||||
|
||||
internal void Pause()
|
||||
@@ -905,7 +935,7 @@ namespace Ryujinx.Ava.Systems
|
||||
_viewModel.IsPaused = true;
|
||||
_playTimer.Stop();
|
||||
_viewModel.Title = TitleHelper.ActiveApplicationTitle(Device?.Processes.ActiveApplication, Program.Version, !ConfigurationState.Instance.ShowOldUI, LocaleManager.Instance[LocaleKeys.Paused]);
|
||||
Logger.Info?.Print(LogClass.Emulation, "Emulation was paused");
|
||||
Logger.Info?.Print(LogClass.Emulation, "Emulation was paused.");
|
||||
}
|
||||
|
||||
private void InitEmulatedSwitch()
|
||||
@@ -1104,7 +1134,9 @@ namespace Ryujinx.Ava.Systems
|
||||
// Make sure all commands in the run loop are fully executed before leaving the loop.
|
||||
if (Device.Gpu.Renderer is ThreadedRenderer threaded)
|
||||
{
|
||||
Logger.Info?.PrintMsg(LogClass.Gpu, "Flushing threaded commands...");
|
||||
threaded.FlushThreadedCommands();
|
||||
Logger.Info?.PrintMsg(LogClass.Gpu, "Flushed!");
|
||||
}
|
||||
|
||||
_gpuDoneEvent.Set();
|
||||
|
||||
@@ -849,7 +849,8 @@ namespace Ryujinx.Ava.Systems.AppLibrary
|
||||
|
||||
foreach (ApplicationData installedApplication in Applications.Items)
|
||||
{
|
||||
temporary += LoadAndSaveMetaData(installedApplication.IdString).TimePlayed;
|
||||
// this should always exist... should...
|
||||
temporary += LoadAndSaveMetaData(installedApplication.IdString).Value.TimePlayed;
|
||||
}
|
||||
|
||||
TotalTimePlayed = temporary;
|
||||
@@ -1159,15 +1160,22 @@ namespace Ryujinx.Ava.Systems.AppLibrary
|
||||
ApplicationCountUpdated?.Invoke(null, e);
|
||||
}
|
||||
|
||||
public static ApplicationMetadata LoadAndSaveMetaData(string titleId, Action<ApplicationMetadata> modifyFunction = null)
|
||||
public static Gommon.Optional<ApplicationMetadata> LoadAndSaveMetaData(string titleId, Action<ApplicationMetadata> modifyFunction = null)
|
||||
{
|
||||
if (titleId is null)
|
||||
{
|
||||
Logger.Warning?.PrintMsg(LogClass.Application, "Cannot save metadata because title ID is invalid.");
|
||||
return null;
|
||||
}
|
||||
|
||||
string metadataFolder = Path.Combine(AppDataManager.GamesDirPath, titleId, "gui");
|
||||
string metadataFile = Path.Combine(metadataFolder, "metadata.json");
|
||||
|
||||
ApplicationMetadata appMetadata;
|
||||
|
||||
|
||||
if (!File.Exists(metadataFile))
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Application, $"Metadata file does not exist. Creating metadata for {titleId}...");
|
||||
Directory.CreateDirectory(metadataFolder);
|
||||
|
||||
appMetadata = new ApplicationMetadata();
|
||||
@@ -1177,12 +1185,12 @@ namespace Ryujinx.Ava.Systems.AppLibrary
|
||||
|
||||
try
|
||||
{
|
||||
Logger.Debug?.Print(LogClass.Application, $"Deserializing metadata for {titleId}...");
|
||||
appMetadata = JsonHelper.DeserializeFromFile(metadataFile, _serializerContext.ApplicationMetadata);
|
||||
}
|
||||
catch (JsonException)
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Application, $"Failed to parse metadata json for {titleId}. Loading defaults.");
|
||||
|
||||
appMetadata = new ApplicationMetadata();
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace Ryujinx.Ava.Systems
|
||||
|
||||
public static void Use(Optional<string> titleId)
|
||||
{
|
||||
if (titleId.TryGet(out string tid))
|
||||
if (titleId.TryGet(out string tid) && Switch.Shared.Processes.ActiveApplication is not null)
|
||||
SwitchToPlayingState(
|
||||
ApplicationLibrary.LoadAndSaveMetaData(tid),
|
||||
Switch.Shared.Processes.ActiveApplication
|
||||
|
||||
@@ -57,8 +57,15 @@ namespace Ryujinx.Ava.UI.Models
|
||||
}
|
||||
else
|
||||
{
|
||||
ApplicationMetadata appMetadata = ApplicationLibrary.LoadAndSaveMetaData(TitleIdString);
|
||||
Title = appMetadata.Title ?? TitleIdString;
|
||||
Gommon.Optional<ApplicationMetadata> appMetadata = ApplicationLibrary.LoadAndSaveMetaData(TitleIdString);
|
||||
if (appMetadata != null)
|
||||
{
|
||||
Title = appMetadata.Value.Title ?? TitleIdString;
|
||||
}
|
||||
else
|
||||
{
|
||||
Title = "<INVALID>";
|
||||
}
|
||||
}
|
||||
|
||||
Task.Run(() =>
|
||||
|
||||
@@ -1029,7 +1029,7 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
string dialogMessage =
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogKeysInstallerKeysInstallMessage);
|
||||
|
||||
if (ContentManager.AreKeysAlredyPresent(systemDirectory))
|
||||
if (ContentManager.AreKeysAlreadyPresent(systemDirectory))
|
||||
{
|
||||
dialogMessage +=
|
||||
LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys
|
||||
@@ -1703,11 +1703,6 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
|
||||
Logger.RestartTime();
|
||||
|
||||
SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(application.Path,
|
||||
ConfigurationState.Instance.System.Language, application.Id);
|
||||
|
||||
PrepareLoadScreen();
|
||||
|
||||
RendererHostControl = new RendererHost();
|
||||
|
||||
AppHost = new AppHost(
|
||||
@@ -1721,18 +1716,34 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
UserChannelPersistence,
|
||||
this,
|
||||
TopLevel);
|
||||
|
||||
CancellationTokenSource cts = new CancellationTokenSource();
|
||||
|
||||
if (!await AppHost.LoadGuestApplication(customNacpData))
|
||||
try
|
||||
{
|
||||
await AppHost.LoadGuestApplication(cts, customNacpData);
|
||||
}
|
||||
catch (OperationCanceledException exception)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Application,
|
||||
"LoadGuestApplication was interrupted !!! " + exception.Message);
|
||||
AppHost.DisposeContext();
|
||||
AppHost = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
finally
|
||||
{
|
||||
cts.Dispose();
|
||||
}
|
||||
|
||||
CanUpdate = false;
|
||||
|
||||
application.Name ??= AppHost.Device.Processes.ActiveApplication.Name;
|
||||
|
||||
SelectedIcon ??= ApplicationLibrary.GetApplicationIcon(application.Path,
|
||||
ConfigurationState.Instance.System.Language, application.Id);
|
||||
|
||||
PrepareLoadScreen();
|
||||
|
||||
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, application.Name);
|
||||
|
||||
@@ -1754,9 +1765,9 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
RendererHostControl.Focus();
|
||||
});
|
||||
|
||||
public static void UpdateGameMetadata(string titleId, TimeSpan playTime)
|
||||
public static void UpdateGameMetadata(string titleId, TimeSpan playTime)
|
||||
=> ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => appMetadata.UpdatePostGame(playTime));
|
||||
|
||||
|
||||
public void RefreshFirmwareStatus()
|
||||
{
|
||||
SystemVersion version = null;
|
||||
|
||||
Reference in New Issue
Block a user