Compare commits

..

7 Commits

Author SHA1 Message Date
TSRBerry
263eb97f79 Avoid race conditions while launching games directly from the command line (#7116)
* optimization: Load application metadata only for applications with IDs

* Load applications when necessary

This prevents loading applications when launching an application
directly from the command line (or a shortcut).
Instead, applications will be loaded after the emulation was stopped by the user.

* Show the title in the configured language when launching an application

* Rename DesiredTitleLanguage to DesiredLanguage
2024-08-03 22:31:34 +01:00
dependabot[bot]
3004902257 nuget: bump DynamicData from 8.4.1 to 9.0.1 (#7040)
Bumps [DynamicData](https://github.com/reactiveui/DynamicData) from 8.4.1 to 9.0.1.
- [Release notes](https://github.com/reactiveui/DynamicData/releases)
- [Changelog](https://github.com/reactivemarbles/DynamicData/blob/main/ReleaseNotes.md)
- [Commits](https://github.com/reactiveui/DynamicData/compare/8.4.1...9.0.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-08-03 22:34:41 +02:00
jhorv
59ddb26628 replace ByteMemoryPool usage in Ryujinx.Graphics (#7129)
* chore: replace `ByteMemoryPool` usage with `MemoryOwner<byte>`

* refactor: `PixelConverter.ConvertR4G4ToR4G4B4A4()` - rename old `outputSpan` to `outputSpanUInt16`, reuse same output `Span<byte>` as newly-freed name `outputSpan`

* eliminate temporary buffer allocations

* chore, perf: use MemoryOwner<byte> instead of IMemoryOwner<byte>
2024-08-03 19:50:53 +01:00
TSRBerry
83fda10f6e Fix FileNotFoundException in TryGetApplicationsFromFile() and improve loading applications (#7145)
* Don't load files from hidden subdirectories

* Catch FileNotFoundException in TryGetApplicationsFromFile()

* Skip non-existent files and bad symlinks when loading applications
2024-08-03 19:46:59 +02:00
gdkchan
d97e995e59 Fix off-by-one on audio renderer PerformanceManager.GetNextEntry (#7139) 2024-07-31 22:22:11 -03:00
gdkchan
56b2f84702 Fix shader RegisterUsage pass only taking first operation dest into account (#7131)
* Fix shader RegisterUsage pass only taking first operation dest into account

* Shader cache version bump
2024-07-30 21:57:55 -03:00
riperiperi
698e36bbd2 Vulkan: Force topology to PatchList for Tessellation (#7102)
Vulkan spec states that input topology should always be PatchList when a tessellation pipeline is present. The AMD GPU on windows crashes so hard it BSODs the machine if this isn't the case, so it's forced here just in case.

I'm not sure what providing a different topology here would even do, as you'd think it would always be a patch list input.
2024-07-30 21:48:30 -03:00
24 changed files with 174 additions and 131 deletions

View File

@@ -13,7 +13,7 @@
<PackageVersion Include="CommandLineParser" Version="2.9.1" /> <PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="Concentus" Version="2.2.0" /> <PackageVersion Include="Concentus" Version="2.2.0" />
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" /> <PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
<PackageVersion Include="DynamicData" Version="8.4.1" /> <PackageVersion Include="DynamicData" Version="9.0.1" />
<PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" /> <PackageVersion Include="FluentAvaloniaUI" Version="2.0.5" />
<PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" /> <PackageVersion Include="GtkSharp.Dependencies" Version="1.1.1" />
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" /> <PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />

View File

@@ -18,16 +18,12 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
if (version == 2) if (version == 2)
{ {
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion2, return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion2, PerformanceEntryVersion2, PerformanceDetailVersion2>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
PerformanceEntryVersion2,
PerformanceDetailVersion2>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
} }
if (version == 1) if (version == 1)
{ {
return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion1, return (ulong)PerformanceManagerGeneric<PerformanceFrameHeaderVersion1, PerformanceEntryVersion1, PerformanceDetailVersion1>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
PerformanceEntryVersion1,
PerformanceDetailVersion1>.GetRequiredBufferSizeForPerformanceMetricsPerFrame(ref parameter);
} }
throw new NotImplementedException($"Unknown Performance metrics data format version {version}"); throw new NotImplementedException($"Unknown Performance metrics data format version {version}");

View File

@@ -234,7 +234,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
{ {
performanceEntry = null; performanceEntry = null;
if (_entryDetailIndex > MaxFrameDetailCount) if (_entryDetailIndex >= MaxFrameDetailCount)
{ {
return false; return false;
} }
@@ -245,7 +245,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
EntryCountOffset = (uint)CurrentHeader.GetEntryCountOffset(), EntryCountOffset = (uint)CurrentHeader.GetEntryCountOffset(),
}; };
uint baseEntryOffset = (uint)(Unsafe.SizeOf<THeader>() + GetEntriesSize() + Unsafe.SizeOf<IPerformanceDetailEntry>() * _entryDetailIndex); uint baseEntryOffset = (uint)(Unsafe.SizeOf<THeader>() + GetEntriesSize() + Unsafe.SizeOf<TEntryDetail>() * _entryDetailIndex);
ref TEntryDetail entryDetail = ref EntriesDetail[_entryDetailIndex]; ref TEntryDetail entryDetail = ref EntriesDetail[_entryDetailIndex];

View File

@@ -1,7 +1,6 @@
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.Memory; using Ryujinx.Memory;
using System; using System;
using System.Buffers;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -145,9 +144,9 @@ namespace Ryujinx.Graphics.Device
} }
else else
{ {
IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(size); MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(size);
GetSpan(va, size).CopyTo(memoryOwner.Memory.Span); ReadImpl(va, memoryOwner.Span);
return new WritableRegion(this, va, memoryOwner, tracked: true); return new WritableRegion(this, va, memoryOwner, tracked: true);
} }

View File

@@ -199,7 +199,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
if (target != null) if (target != null)
{ {
target.SynchronizeMemory(); target.SynchronizeMemory();
var dataCopy = ByteMemoryPool.RentCopy(data); var dataCopy = MemoryOwner<byte>.RentCopy(data);
target.SetData(dataCopy, 0, 0, new GAL.Rectangle<int>(_dstX, _dstY, _lineLengthIn / target.Info.FormatInfo.BytesPerPixel, _lineCount)); target.SetData(dataCopy, 0, 0, new GAL.Rectangle<int>(_dstX, _dstY, _lineLengthIn / target.Info.FormatInfo.BytesPerPixel, _lineCount));
target.SignalModified(); target.SignalModified();

View File

@@ -1,4 +1,5 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Texture; using Ryujinx.Graphics.Texture;
@@ -805,7 +806,7 @@ namespace Ryujinx.Graphics.Gpu.Image
sliceDepth, sliceDepth,
levels, levels,
layers, layers,
out IMemoryOwner<byte> decoded)) out MemoryOwner<byte> decoded))
{ {
string texInfo = $"{Info.Target} {Info.FormatInfo.Format} {Info.Width}x{Info.Height}x{Info.DepthOrLayers} levels {Info.Levels}"; string texInfo = $"{Info.Target} {Info.FormatInfo.Format} {Info.Width}x{Info.Height}x{Info.DepthOrLayers} levels {Info.Levels}";

View File

@@ -2,7 +2,6 @@ using Ryujinx.Common.Memory;
using Ryujinx.Memory; using Ryujinx.Memory;
using Ryujinx.Memory.Range; using Ryujinx.Memory.Range;
using System; using System;
using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -242,9 +241,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
else else
{ {
IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(size); MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(size);
GetSpan(va, size).CopyTo(memoryOwner.Memory.Span); ReadImpl(va, memoryOwner.Span, tracked);
return new WritableRegion(this, va, memoryOwner, tracked); return new WritableRegion(this, va, memoryOwner, tracked);
} }

View File

@@ -192,9 +192,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
} }
else else
{ {
IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(range.GetSize()); MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(checked((int)range.GetSize()));
Memory<byte> memory = memoryOwner.Memory; Span<byte> memorySpan = memoryOwner.Span;
int offset = 0; int offset = 0;
for (int i = 0; i < range.Count; i++) for (int i = 0; i < range.Count; i++)
@@ -203,7 +203,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
int size = (int)currentRange.Size; int size = (int)currentRange.Size;
if (currentRange.Address != MemoryManager.PteUnmapped) if (currentRange.Address != MemoryManager.PteUnmapped)
{ {
GetSpan(currentRange.Address, size).CopyTo(memory.Span.Slice(offset, size)); GetSpan(currentRange.Address, size).CopyTo(memorySpan.Slice(offset, size));
} }
offset += size; offset += size;
} }

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1; private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2; private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor; private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 6921; private const uint CodeGenVersion = 7131;
private const string SharedTocFileName = "shared.toc"; private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data"; private const string SharedDataFileName = "shared.data";

View File

@@ -1,6 +1,5 @@
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using System; using System;
using System.Buffers;
using System.Numerics; using System.Numerics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
@@ -10,11 +9,11 @@ namespace Ryujinx.Graphics.OpenGL.Image
{ {
static class FormatConverter static class FormatConverter
{ {
public unsafe static IMemoryOwner<byte> ConvertS8D24ToD24S8(ReadOnlySpan<byte> data) public unsafe static MemoryOwner<byte> ConvertS8D24ToD24S8(ReadOnlySpan<byte> data)
{ {
IMemoryOwner<byte> outputMemory = ByteMemoryPool.Rent(data.Length); MemoryOwner<byte> outputMemory = MemoryOwner<byte>.Rent(data.Length);
Span<byte> output = outputMemory.Memory.Span; Span<byte> output = outputMemory.Span;
int start = 0; int start = 0;

View File

@@ -155,9 +155,14 @@ namespace Ryujinx.Graphics.Shader.Translation
localInputs[block.Index] |= GetMask(register) & ~localOutputs[block.Index]; localInputs[block.Index] |= GetMask(register) & ~localOutputs[block.Index];
} }
if (operation.Dest != null && operation.Dest.Type == OperandType.Register) for (int dstIndex = 0; dstIndex < operation.DestsCount; dstIndex++)
{ {
localOutputs[block.Index] |= GetMask(operation.Dest.GetRegister()); Operand dest = operation.GetDest(dstIndex);
if (dest != null && dest.Type == OperandType.Register)
{
localOutputs[block.Index] |= GetMask(dest.GetRegister());
}
} }
} }
} }

View File

@@ -1,7 +1,6 @@
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.Common.Utilities; using Ryujinx.Common.Utilities;
using System; using System;
using System.Buffers;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -293,9 +292,9 @@ namespace Ryujinx.Graphics.Texture.Astc
int depth, int depth,
int levels, int levels,
int layers, int layers,
out IMemoryOwner<byte> decoded) out MemoryOwner<byte> decoded)
{ {
decoded = ByteMemoryPool.Rent(QueryDecompressedSize(width, height, depth, levels, layers)); decoded = MemoryOwner<byte>.Rent(QueryDecompressedSize(width, height, depth, levels, layers));
AstcDecoder decoder = new(data, decoded.Memory, blockWidth, blockHeight, width, height, depth, levels, layers); AstcDecoder decoder = new(data, decoded.Memory, blockWidth, blockHeight, width, height, depth, levels, layers);

View File

@@ -1,7 +1,6 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using System; using System;
using System.Buffers;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
@@ -14,7 +13,7 @@ namespace Ryujinx.Graphics.Texture
private const int BlockWidth = 4; private const int BlockWidth = 4;
private const int BlockHeight = 4; private const int BlockHeight = 4;
public static IMemoryOwner<byte> DecodeBC1(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers) public static MemoryOwner<byte> DecodeBC1(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
{ {
int size = 0; int size = 0;
@@ -23,12 +22,12 @@ namespace Ryujinx.Graphics.Texture
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4; size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
} }
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size); MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4]; Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile); Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile);
Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span); Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Span);
Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile); Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile);
@@ -102,7 +101,7 @@ namespace Ryujinx.Graphics.Texture
return output; return output;
} }
public static IMemoryOwner<byte> DecodeBC2(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers) public static MemoryOwner<byte> DecodeBC2(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
{ {
int size = 0; int size = 0;
@@ -111,12 +110,12 @@ namespace Ryujinx.Graphics.Texture
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4; size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
} }
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size); MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4]; Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile); Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile);
Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span); Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Span);
Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile); Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile);
@@ -197,7 +196,7 @@ namespace Ryujinx.Graphics.Texture
return output; return output;
} }
public static IMemoryOwner<byte> DecodeBC3(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers) public static MemoryOwner<byte> DecodeBC3(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
{ {
int size = 0; int size = 0;
@@ -206,13 +205,13 @@ namespace Ryujinx.Graphics.Texture
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4; size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
} }
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size); MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4]; Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
Span<byte> rPal = stackalloc byte[8]; Span<byte> rPal = stackalloc byte[8];
Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile); Span<uint> tileAsUint = MemoryMarshal.Cast<byte, uint>(tile);
Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span); Span<uint> outputAsUint = MemoryMarshal.Cast<byte, uint>(output.Span);
Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile); Span<Vector128<byte>> tileAsVector128 = MemoryMarshal.Cast<byte, Vector128<byte>>(tile);
@@ -294,7 +293,7 @@ namespace Ryujinx.Graphics.Texture
return output; return output;
} }
public static IMemoryOwner<byte> DecodeBC4(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed) public static MemoryOwner<byte> DecodeBC4(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
{ {
int size = 0; int size = 0;
@@ -306,8 +305,8 @@ namespace Ryujinx.Graphics.Texture
// Backends currently expect a stride alignment of 4 bytes, so output width must be aligned. // Backends currently expect a stride alignment of 4 bytes, so output width must be aligned.
int alignedWidth = BitUtils.AlignUp(width, 4); int alignedWidth = BitUtils.AlignUp(width, 4);
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size); MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
Span<byte> outputSpan = output.Memory.Span; Span<byte> outputSpan = output.Span;
ReadOnlySpan<ulong> data64 = MemoryMarshal.Cast<byte, ulong>(data); ReadOnlySpan<ulong> data64 = MemoryMarshal.Cast<byte, ulong>(data);
@@ -402,7 +401,7 @@ namespace Ryujinx.Graphics.Texture
return output; return output;
} }
public static IMemoryOwner<byte> DecodeBC5(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed) public static MemoryOwner<byte> DecodeBC5(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
{ {
int size = 0; int size = 0;
@@ -414,7 +413,7 @@ namespace Ryujinx.Graphics.Texture
// Backends currently expect a stride alignment of 4 bytes, so output width must be aligned. // Backends currently expect a stride alignment of 4 bytes, so output width must be aligned.
int alignedWidth = BitUtils.AlignUp(width, 2); int alignedWidth = BitUtils.AlignUp(width, 2);
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size); MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
ReadOnlySpan<ulong> data64 = MemoryMarshal.Cast<byte, ulong>(data); ReadOnlySpan<ulong> data64 = MemoryMarshal.Cast<byte, ulong>(data);
@@ -423,7 +422,7 @@ namespace Ryujinx.Graphics.Texture
Span<byte> rPal = stackalloc byte[8]; Span<byte> rPal = stackalloc byte[8];
Span<byte> gPal = stackalloc byte[8]; Span<byte> gPal = stackalloc byte[8];
Span<ushort> outputAsUshort = MemoryMarshal.Cast<byte, ushort>(output.Memory.Span); Span<ushort> outputAsUshort = MemoryMarshal.Cast<byte, ushort>(output.Span);
Span<uint> rTileAsUint = MemoryMarshal.Cast<byte, uint>(rTile); Span<uint> rTileAsUint = MemoryMarshal.Cast<byte, uint>(rTile);
Span<uint> gTileAsUint = MemoryMarshal.Cast<byte, uint>(gTile); Span<uint> gTileAsUint = MemoryMarshal.Cast<byte, uint>(gTile);
@@ -527,7 +526,7 @@ namespace Ryujinx.Graphics.Texture
return output; return output;
} }
public static IMemoryOwner<byte> DecodeBC6(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed) public static MemoryOwner<byte> DecodeBC6(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers, bool signed)
{ {
int size = 0; int size = 0;
@@ -536,8 +535,8 @@ namespace Ryujinx.Graphics.Texture
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 8; size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 8;
} }
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size); MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
Span<byte> outputSpan = output.Memory.Span; Span<byte> outputSpan = output.Span;
int inputOffset = 0; int inputOffset = 0;
int outputOffset = 0; int outputOffset = 0;
@@ -566,7 +565,7 @@ namespace Ryujinx.Graphics.Texture
return output; return output;
} }
public static IMemoryOwner<byte> DecodeBC7(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers) public static MemoryOwner<byte> DecodeBC7(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
{ {
int size = 0; int size = 0;
@@ -575,8 +574,8 @@ namespace Ryujinx.Graphics.Texture
size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4; size += Math.Max(1, width >> l) * Math.Max(1, height >> l) * Math.Max(1, depth >> l) * layers * 4;
} }
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size); MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
Span<byte> outputSpan = output.Memory.Span; Span<byte> outputSpan = output.Span;
int inputOffset = 0; int inputOffset = 0;
int outputOffset = 0; int outputOffset = 0;

View File

@@ -2,7 +2,6 @@ using Ryujinx.Common;
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using Ryujinx.Graphics.Texture.Encoders; using Ryujinx.Graphics.Texture.Encoders;
using System; using System;
using System.Buffers;
namespace Ryujinx.Graphics.Texture namespace Ryujinx.Graphics.Texture
{ {
@@ -11,7 +10,7 @@ namespace Ryujinx.Graphics.Texture
private const int BlockWidth = 4; private const int BlockWidth = 4;
private const int BlockHeight = 4; private const int BlockHeight = 4;
public static IMemoryOwner<byte> EncodeBC7(Memory<byte> data, int width, int height, int depth, int levels, int layers) public static MemoryOwner<byte> EncodeBC7(Memory<byte> data, int width, int height, int depth, int levels, int layers)
{ {
int size = 0; int size = 0;
@@ -23,7 +22,7 @@ namespace Ryujinx.Graphics.Texture
size += w * h * 16 * Math.Max(1, depth >> l) * layers; size += w * h * 16 * Math.Max(1, depth >> l) * layers;
} }
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size); MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
Memory<byte> outputMemory = output.Memory; Memory<byte> outputMemory = output.Memory;
int imageBaseIOffs = 0; int imageBaseIOffs = 0;

View File

@@ -1,7 +1,6 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using System; using System;
using System.Buffers;
using System.Buffers.Binary; using System.Buffers.Binary;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -51,15 +50,15 @@ namespace Ryujinx.Graphics.Texture
new int[] { -3, -5, -7, -9, 2, 4, 6, 8 }, new int[] { -3, -5, -7, -9, 2, 4, 6, 8 },
}; };
public static IMemoryOwner<byte> DecodeRgb(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers) public static MemoryOwner<byte> DecodeRgb(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
{ {
ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data); ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
int inputOffset = 0; int inputOffset = 0;
IMemoryOwner<byte> output = ByteMemoryPool.Rent(CalculateOutputSize(width, height, depth, levels, layers)); MemoryOwner<byte> output = MemoryOwner<byte>.Rent(CalculateOutputSize(width, height, depth, levels, layers));
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span); Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Span);
Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight]; Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
int imageBaseOOffs = 0; int imageBaseOOffs = 0;
@@ -113,15 +112,15 @@ namespace Ryujinx.Graphics.Texture
return output; return output;
} }
public static IMemoryOwner<byte> DecodePta(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers) public static MemoryOwner<byte> DecodePta(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
{ {
ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data); ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
int inputOffset = 0; int inputOffset = 0;
IMemoryOwner<byte> output = ByteMemoryPool.Rent(CalculateOutputSize(width, height, depth, levels, layers)); MemoryOwner<byte> output = MemoryOwner<byte>.Rent(CalculateOutputSize(width, height, depth, levels, layers));
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span); Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Span);
Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight]; Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
int imageBaseOOffs = 0; int imageBaseOOffs = 0;
@@ -170,15 +169,15 @@ namespace Ryujinx.Graphics.Texture
return output; return output;
} }
public static IMemoryOwner<byte> DecodeRgba(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers) public static MemoryOwner<byte> DecodeRgba(ReadOnlySpan<byte> data, int width, int height, int depth, int levels, int layers)
{ {
ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data); ReadOnlySpan<ulong> dataUlong = MemoryMarshal.Cast<byte, ulong>(data);
int inputOffset = 0; int inputOffset = 0;
IMemoryOwner<byte> output = ByteMemoryPool.Rent(CalculateOutputSize(width, height, depth, levels, layers)); MemoryOwner<byte> output = MemoryOwner<byte>.Rent(CalculateOutputSize(width, height, depth, levels, layers));
Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Memory.Span); Span<uint> outputUint = MemoryMarshal.Cast<byte, uint>(output.Span);
Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight]; Span<uint> tile = stackalloc uint[BlockWidth * BlockHeight];
int imageBaseOOffs = 0; int imageBaseOOffs = 0;

View File

@@ -1,7 +1,6 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using System; using System;
using System.Buffers;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using static Ryujinx.Graphics.Texture.BlockLinearConstants; using static Ryujinx.Graphics.Texture.BlockLinearConstants;
@@ -95,7 +94,7 @@ namespace Ryujinx.Graphics.Texture
}; };
} }
public static IMemoryOwner<byte> ConvertBlockLinearToLinear( public static MemoryOwner<byte> ConvertBlockLinearToLinear(
int width, int width,
int height, int height,
int depth, int depth,
@@ -121,8 +120,8 @@ namespace Ryujinx.Graphics.Texture
blockHeight, blockHeight,
bytesPerPixel); bytesPerPixel);
IMemoryOwner<byte> outputOwner = ByteMemoryPool.Rent(outSize); MemoryOwner<byte> outputOwner = MemoryOwner<byte>.Rent(outSize);
Span<byte> output = outputOwner.Memory.Span; Span<byte> output = outputOwner.Span;
int outOffs = 0; int outOffs = 0;
@@ -249,7 +248,7 @@ namespace Ryujinx.Graphics.Texture
return outputOwner; return outputOwner;
} }
public static IMemoryOwner<byte> ConvertLinearStridedToLinear( public static MemoryOwner<byte> ConvertLinearStridedToLinear(
int width, int width,
int height, int height,
int blockWidth, int blockWidth,
@@ -265,8 +264,8 @@ namespace Ryujinx.Graphics.Texture
int outStride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment); int outStride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment);
lineSize = Math.Min(lineSize, outStride); lineSize = Math.Min(lineSize, outStride);
IMemoryOwner<byte> output = ByteMemoryPool.Rent(h * outStride); MemoryOwner<byte> output = MemoryOwner<byte>.Rent(h * outStride);
Span<byte> outSpan = output.Memory.Span; Span<byte> outSpan = output.Span;
int outOffs = 0; int outOffs = 0;
int inOffs = 0; int inOffs = 0;

View File

@@ -1,7 +1,6 @@
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Memory; using Ryujinx.Common.Memory;
using System; using System;
using System.Buffers;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Runtime.Intrinsics; using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86; using System.Runtime.Intrinsics.X86;
@@ -21,13 +20,14 @@ namespace Ryujinx.Graphics.Texture
return (remainder, outRemainder, length / stride); return (remainder, outRemainder, length / stride);
} }
public unsafe static IMemoryOwner<byte> ConvertR4G4ToR4G4B4A4(ReadOnlySpan<byte> data, int width) public unsafe static MemoryOwner<byte> ConvertR4G4ToR4G4B4A4(ReadOnlySpan<byte> data, int width)
{ {
IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2); MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
Span<byte> outputSpan = output.Span;
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 1, 2); (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 1, 2);
Span<ushort> outputSpan = MemoryMarshal.Cast<byte, ushort>(output.Memory.Span); Span<ushort> outputSpanUInt16 = MemoryMarshal.Cast<byte, ushort>(outputSpan);
if (remainder == 0) if (remainder == 0)
{ {
@@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Texture
int sizeTrunc = data.Length & ~7; int sizeTrunc = data.Length & ~7;
start = sizeTrunc; start = sizeTrunc;
fixed (byte* inputPtr = data, outputPtr = output.Memory.Span) fixed (byte* inputPtr = data, outputPtr = outputSpan)
{ {
for (ulong offset = 0; offset < (ulong)sizeTrunc; offset += 8) for (ulong offset = 0; offset < (ulong)sizeTrunc; offset += 8)
{ {
@@ -49,7 +49,7 @@ namespace Ryujinx.Graphics.Texture
for (int i = start; i < data.Length; i++) for (int i = start; i < data.Length; i++)
{ {
outputSpan[i] = data[i]; outputSpanUInt16[i] = data[i];
} }
} }
else else
@@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Texture
{ {
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
{ {
outputSpan[outOffset++] = data[offset++]; outputSpanUInt16[outOffset++] = data[offset++];
} }
offset += remainder; offset += remainder;
@@ -72,16 +72,16 @@ namespace Ryujinx.Graphics.Texture
return output; return output;
} }
public static IMemoryOwner<byte> ConvertR5G6B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width) public static MemoryOwner<byte> ConvertR5G6B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
{ {
IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2); MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
int offset = 0; int offset = 0;
int outOffset = 0; int outOffset = 0;
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4); (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data); ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Memory.Span); Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Span);
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
@@ -109,16 +109,16 @@ namespace Ryujinx.Graphics.Texture
return output; return output;
} }
public static IMemoryOwner<byte> ConvertR5G5B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width, bool forceAlpha) public static MemoryOwner<byte> ConvertR5G5B5ToR8G8B8A8(ReadOnlySpan<byte> data, int width, bool forceAlpha)
{ {
IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2); MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
int offset = 0; int offset = 0;
int outOffset = 0; int outOffset = 0;
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4); (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data); ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Memory.Span); Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Span);
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
@@ -146,16 +146,16 @@ namespace Ryujinx.Graphics.Texture
return output; return output;
} }
public static IMemoryOwner<byte> ConvertA1B5G5R5ToR8G8B8A8(ReadOnlySpan<byte> data, int width) public static MemoryOwner<byte> ConvertA1B5G5R5ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
{ {
IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2); MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
int offset = 0; int offset = 0;
int outOffset = 0; int outOffset = 0;
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4); (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data); ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Memory.Span); Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Span);
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
@@ -183,16 +183,16 @@ namespace Ryujinx.Graphics.Texture
return output; return output;
} }
public static IMemoryOwner<byte> ConvertR4G4B4A4ToR8G8B8A8(ReadOnlySpan<byte> data, int width) public static MemoryOwner<byte> ConvertR4G4B4A4ToR8G8B8A8(ReadOnlySpan<byte> data, int width)
{ {
IMemoryOwner<byte> output = ByteMemoryPool.Rent(data.Length * 2); MemoryOwner<byte> output = MemoryOwner<byte>.Rent(data.Length * 2);
int offset = 0; int offset = 0;
int outOffset = 0; int outOffset = 0;
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4); (int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data); ReadOnlySpan<ushort> inputSpan = MemoryMarshal.Cast<byte, ushort>(data);
Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Memory.Span); Span<uint> outputSpan = MemoryMarshal.Cast<byte, uint>(output.Span);
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {

View File

@@ -211,7 +211,7 @@ namespace Ryujinx.Graphics.Vulkan
public void Initialize() public void Initialize()
{ {
IMemoryOwner<byte> dummyTextureData = ByteMemoryPool.RentCleared(4); MemoryOwner<byte> dummyTextureData = MemoryOwner<byte>.RentCleared(4);
_dummyTexture.SetData(dummyTextureData); _dummyTexture.SetData(dummyTextureData);
} }

View File

@@ -439,7 +439,7 @@ namespace Ryujinx.Graphics.Vulkan
{ {
SType = StructureType.PipelineInputAssemblyStateCreateInfo, SType = StructureType.PipelineInputAssemblyStateCreateInfo,
PrimitiveRestartEnable = primitiveRestartEnable, PrimitiveRestartEnable = primitiveRestartEnable,
Topology = Topology, Topology = HasTessellationControlShader ? PrimitiveTopology.PatchList : Topology,
}; };
var tessellationState = new PipelineTessellationStateCreateInfo var tessellationState = new PipelineTessellationStateCreateInfo

View File

@@ -256,6 +256,12 @@ namespace Ryujinx
MainWindow mainWindow = new(); MainWindow mainWindow = new();
mainWindow.Show(); mainWindow.Show();
// Load the game table if no application was requested by the command line
if (CommandLineState.LaunchPathArg == null)
{
mainWindow.UpdateGameTable();
}
if (OperatingSystem.IsLinux()) if (OperatingSystem.IsLinux())
{ {
int currentVmMaxMapCount = LinuxHelper.VmMaxMapCount; int currentVmMaxMapCount = LinuxHelper.VmMaxMapCount;

View File

@@ -187,7 +187,10 @@ namespace Ryujinx.UI
: IntegrityCheckLevel.None; : IntegrityCheckLevel.None;
// Instantiate GUI objects. // Instantiate GUI objects.
ApplicationLibrary = new ApplicationLibrary(_virtualFileSystem, checkLevel); ApplicationLibrary = new ApplicationLibrary(_virtualFileSystem, checkLevel)
{
DesiredLanguage = ConfigurationState.Instance.System.Language,
};
_uiHandler = new GtkHostUIHandler(this); _uiHandler = new GtkHostUIHandler(this);
_deviceExitStatus = new AutoResetEvent(false); _deviceExitStatus = new AutoResetEvent(false);
@@ -325,7 +328,6 @@ namespace Ryujinx.UI
_hideUI.Label = _hideUI.Label.Replace("SHOWUIKEY", ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUI.ToString()); _hideUI.Label = _hideUI.Label.Replace("SHOWUIKEY", ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUI.ToString());
UpdateColumns(); UpdateColumns();
UpdateGameTable();
ConfigurationState.Instance.UI.GameDirs.Event += (sender, args) => ConfigurationState.Instance.UI.GameDirs.Event += (sender, args) =>
{ {
@@ -738,7 +740,8 @@ namespace Ryujinx.UI
Thread applicationLibraryThread = new(() => Thread applicationLibraryThread = new(() =>
{ {
ApplicationLibrary.LoadApplications(ConfigurationState.Instance.UI.GameDirs, ConfigurationState.Instance.System.Language); ApplicationLibrary.DesiredLanguage = ConfigurationState.Instance.System.Language;
ApplicationLibrary.LoadApplications(ConfigurationState.Instance.UI.GameDirs);
_updatingGameTable = false; _updatingGameTable = false;
}) })

View File

@@ -9,5 +9,6 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrlGpu.Types
public byte Sm0TpcIndex; public byte Sm0TpcIndex;
public byte Sm1GpcIndex; public byte Sm1GpcIndex;
public byte Sm1TpcIndex; public byte Sm1TpcIndex;
public uint Reserved;
} }
} }

View File

@@ -34,6 +34,7 @@ namespace Ryujinx.UI.App.Common
{ {
public class ApplicationLibrary public class ApplicationLibrary
{ {
public Language DesiredLanguage { get; set; }
public event EventHandler<ApplicationAddedEventArgs> ApplicationAdded; public event EventHandler<ApplicationAddedEventArgs> ApplicationAdded;
public event EventHandler<ApplicationCountUpdatedEventArgs> ApplicationCountUpdated; public event EventHandler<ApplicationCountUpdatedEventArgs> ApplicationCountUpdated;
@@ -45,7 +46,6 @@ namespace Ryujinx.UI.App.Common
private readonly VirtualFileSystem _virtualFileSystem; private readonly VirtualFileSystem _virtualFileSystem;
private readonly IntegrityCheckLevel _checkLevel; private readonly IntegrityCheckLevel _checkLevel;
private Language _desiredTitleLanguage;
private CancellationTokenSource _cancellationToken; private CancellationTokenSource _cancellationToken;
private static readonly ApplicationJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); private static readonly ApplicationJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
@@ -221,7 +221,7 @@ namespace Ryujinx.UI.App.Common
{ {
using UniqueRef<IFile> icon = new(); using UniqueRef<IFile> icon = new();
controlFs.OpenFile(ref icon.Ref, $"/icon_{_desiredTitleLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure(); controlFs.OpenFile(ref icon.Ref, $"/icon_{DesiredLanguage}.dat".ToU8Span(), OpenMode.Read).ThrowIfFailure();
using MemoryStream stream = new(); using MemoryStream stream = new();
@@ -266,8 +266,18 @@ namespace Ryujinx.UI.App.Common
public bool TryGetApplicationsFromFile(string applicationPath, out List<ApplicationData> applications) public bool TryGetApplicationsFromFile(string applicationPath, out List<ApplicationData> applications)
{ {
applications = []; applications = [];
long fileSize;
long fileSize = new FileInfo(applicationPath).Length; try
{
fileSize = new FileInfo(applicationPath).Length;
}
catch (FileNotFoundException)
{
Logger.Warning?.Print(LogClass.Application, $"The file was not found: '{applicationPath}'");
return false;
}
BlitStruct<ApplicationControlProperty> controlHolder = new(1); BlitStruct<ApplicationControlProperty> controlHolder = new(1);
@@ -421,6 +431,9 @@ namespace Ryujinx.UI.App.Common
} }
foreach (var data in applications) foreach (var data in applications)
{
// Only load metadata for applications with an ID
if (data.Id != 0)
{ {
ApplicationMetadata appMetadata = LoadAndSaveMetaData(data.IdString, appMetadata => ApplicationMetadata appMetadata = LoadAndSaveMetaData(data.IdString, appMetadata =>
{ {
@@ -451,6 +464,8 @@ namespace Ryujinx.UI.App.Common
data.Favorite = appMetadata.Favorite; data.Favorite = appMetadata.Favorite;
data.TimePlayed = appMetadata.TimePlayed; data.TimePlayed = appMetadata.TimePlayed;
data.LastPlayed = appMetadata.LastPlayed; data.LastPlayed = appMetadata.LastPlayed;
}
data.FileExtension = Path.GetExtension(applicationPath).TrimStart('.').ToUpper(); data.FileExtension = Path.GetExtension(applicationPath).TrimStart('.').ToUpper();
data.FileSize = fileSize; data.FileSize = fileSize;
data.Path = applicationPath; data.Path = applicationPath;
@@ -472,13 +487,11 @@ namespace Ryujinx.UI.App.Common
controlFile.Get.Read(out _, 0, outProperty, ReadOption.None).ThrowIfFailure(); controlFile.Get.Read(out _, 0, outProperty, ReadOption.None).ThrowIfFailure();
} }
public void LoadApplications(List<string> appDirs, Language desiredTitleLanguage) public void LoadApplications(List<string> appDirs)
{ {
int numApplicationsFound = 0; int numApplicationsFound = 0;
int numApplicationsLoaded = 0; int numApplicationsLoaded = 0;
_desiredTitleLanguage = desiredTitleLanguage;
_cancellationToken = new CancellationTokenSource(); _cancellationToken = new CancellationTokenSource();
// Builds the applications list with paths to found applications // Builds the applications list with paths to found applications
@@ -502,7 +515,13 @@ namespace Ryujinx.UI.App.Common
try try
{ {
IEnumerable<string> files = Directory.EnumerateFiles(appDir, "*", SearchOption.AllDirectories).Where(file => EnumerationOptions options = new()
{
RecurseSubdirectories = true,
IgnoreInaccessible = false,
};
IEnumerable<string> files = Directory.EnumerateFiles(appDir, "*", options).Where(file =>
{ {
return return
(Path.GetExtension(file).ToLower() is ".nsp" && ConfigurationState.Instance.UI.ShownFileTypes.NSP.Value) || (Path.GetExtension(file).ToLower() is ".nsp" && ConfigurationState.Instance.UI.ShownFileTypes.NSP.Value) ||
@@ -521,14 +540,18 @@ namespace Ryujinx.UI.App.Common
} }
var fileInfo = new FileInfo(app); var fileInfo = new FileInfo(app);
string extension = fileInfo.Extension.ToLower();
if (!fileInfo.Attributes.HasFlag(FileAttributes.Hidden) && extension is ".nsp" or ".pfs0" or ".xci" or ".nca" or ".nro" or ".nso") try
{ {
var fullPath = fileInfo.ResolveLinkTarget(true)?.FullName ?? fileInfo.FullName; var fullPath = fileInfo.ResolveLinkTarget(true)?.FullName ?? fileInfo.FullName;
applicationPaths.Add(fullPath); applicationPaths.Add(fullPath);
numApplicationsFound++; numApplicationsFound++;
} }
catch (IOException exception)
{
Logger.Warning?.Print(LogClass.Application, $"Failed to resolve the full path to file: \"{app}\" Error: {exception}");
}
} }
} }
catch (UnauthorizedAccessException) catch (UnauthorizedAccessException)
@@ -827,7 +850,7 @@ namespace Ryujinx.UI.App.Common
private void GetApplicationInformation(ref ApplicationControlProperty controlData, ref ApplicationData data) private void GetApplicationInformation(ref ApplicationControlProperty controlData, ref ApplicationData data)
{ {
_ = Enum.TryParse(_desiredTitleLanguage.ToString(), out TitleLanguage desiredTitleLanguage); _ = Enum.TryParse(DesiredLanguage.ToString(), out TitleLanguage desiredTitleLanguage);
if (controlData.Title.ItemsRo.Length > (int)desiredTitleLanguage) if (controlData.Title.ItemsRo.Length > (int)desiredTitleLanguage)
{ {

View File

@@ -37,6 +37,7 @@ namespace Ryujinx.Ava.UI.Windows
internal static MainWindowViewModel MainWindowViewModel { get; private set; } internal static MainWindowViewModel MainWindowViewModel { get; private set; }
private bool _isLoading; private bool _isLoading;
private bool _applicationsLoadedOnce;
private UserChannelPersistence _userChannelPersistence; private UserChannelPersistence _userChannelPersistence;
private static bool _deferLoad; private static bool _deferLoad;
@@ -224,7 +225,10 @@ namespace Ryujinx.Ava.UI.Windows
? IntegrityCheckLevel.ErrorOnInvalid ? IntegrityCheckLevel.ErrorOnInvalid
: IntegrityCheckLevel.None; : IntegrityCheckLevel.None;
ApplicationLibrary = new ApplicationLibrary(VirtualFileSystem, checkLevel); ApplicationLibrary = new ApplicationLibrary(VirtualFileSystem, checkLevel)
{
DesiredLanguage = ConfigurationState.Instance.System.Language,
};
// Save data created before we supported extra data in directory save data will not work properly if // Save data created before we supported extra data in directory save data will not work properly if
// given empty extra data. Luckily some of that extra data can be created using the data from the // given empty extra data. Luckily some of that extra data can be created using the data from the
@@ -472,7 +476,11 @@ namespace Ryujinx.Ava.UI.Windows
ViewModel.RefreshFirmwareStatus(); ViewModel.RefreshFirmwareStatus();
// Load applications if no application was requested by the command line
if (!_deferLoad)
{
LoadApplications(); LoadApplications();
}
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
CheckLaunchState(); CheckLaunchState();
@@ -485,6 +493,12 @@ namespace Ryujinx.Ava.UI.Windows
if (MainContent.Content != content) if (MainContent.Content != content)
{ {
// Load applications while switching to the GameLibrary if we haven't done that yet
if (!_applicationsLoadedOnce && content == GameLibrary)
{
LoadApplications();
}
MainContent.Content = content; MainContent.Content = content;
} }
} }
@@ -581,6 +595,7 @@ namespace Ryujinx.Ava.UI.Windows
public void LoadApplications() public void LoadApplications()
{ {
_applicationsLoadedOnce = true;
ViewModel.Applications.Clear(); ViewModel.Applications.Clear();
StatusBarView.LoadProgressBar.IsVisible = true; StatusBarView.LoadProgressBar.IsVisible = true;
@@ -622,7 +637,8 @@ namespace Ryujinx.Ava.UI.Windows
Thread applicationLibraryThread = new(() => Thread applicationLibraryThread = new(() =>
{ {
ApplicationLibrary.LoadApplications(ConfigurationState.Instance.UI.GameDirs, ConfigurationState.Instance.System.Language); ApplicationLibrary.DesiredLanguage = ConfigurationState.Instance.System.Language;
ApplicationLibrary.LoadApplications(ConfigurationState.Instance.UI.GameDirs);
_isLoading = false; _isLoading = false;
}) })