Compare commits

..

13 Commits

Author SHA1 Message Date
7969fb6bba Replace and remove obsolete ByteMemoryPool type (#7155)
* refactor: replace usage of ByteMemoryPool with MemoryOwner<byte>

* refactor: delete unused ByteMemoryPool and ByteMemoryPool.ByteMemoryPoolBuffer types

* refactor: change IMemoryOwner<byte> return types to MemoryOwner<byte>

* fix(perf): get span via `MemoryOwner<T>.Span` directly instead of `MemoryOwner<T>.Memory.Span`

* fix(perf): get span via MemoryOwner<T>.Span directly instead of `MemoryOwner<T>.Memory.Span`

* fix(perf): get span via MemoryOwner<T>.Span directly instead of `MemoryOwner<T>.Memory.Span`
2024-08-05 21:09:08 -03:00
4a4b11871e Fix same textures with unmapped start being considered different (#7141)
* Fix same textures with unmapped start being considered different

* Consolidate IsInvalid check

* InvalidAddress const

* Fix typo

Co-authored-by: riperiperi <rhy3756547@hotmail.com>

---------

Co-authored-by: riperiperi <rhy3756547@hotmail.com>
2024-08-05 11:00:41 -03:00
e85ee673b1 Fix LocaleExtension SetRawSource usages + language perf improvement (#7121)
* Avoid Avalonia CompiledBindingPathBuilder.SetRawSource

* Improve UI language change performance
2024-08-04 19:04:12 +01:00
42f22fe5d7 Infra: Update Microsoft.IdentityModel.JsonWebTokens (#7070)
* Update Microsoft.IdentityModel.JsonWebTokens

* Update
2024-08-04 18:56:27 +01:00
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
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
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
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
d97e995e59 Fix off-by-one on audio renderer PerformanceManager.GetNextEntry (#7139) 2024-07-31 22:22:11 -03:00
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
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
6ce49a2dc7 Ava UI: Handle updates containing non numeric characters (#7043)
* Handle updates containing non numeric characters

Smh

Dont be stupid

* Use Berry’s method

* Thanks gdk

* Remove using
2024-07-25 16:44:33 -03:00
ccd330ba0f Vulkan: Add missing barriers for texture to buffer copy (#7092)
This barrier has always been missing, but it only became apparent when #7012 merged.

I also added some barriers in case the target buffer used here is used by other commands, though right now it isn't.

Fixes a regression where water would turn white on AMD GPUs with the proprietary driver. May fix other issues on this driver.
2024-07-25 16:34:30 -03:00
39 changed files with 289 additions and 351 deletions

View File

@ -13,14 +13,14 @@
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="Concentus" Version="2.2.0" />
<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="GtkSharp.Dependencies" Version="1.1.1" />
<PackageVersion Include="GtkSharp.Dependencies.osx" Version="0.0.5" />
<PackageVersion Include="LibHac" Version="0.19.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.9.2" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="7.6.2" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.0.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />

View File

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

View File

@ -234,7 +234,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
{
performanceEntry = null;
if (_entryDetailIndex > MaxFrameDetailCount)
if (_entryDetailIndex >= MaxFrameDetailCount)
{
return false;
}
@ -245,7 +245,7 @@ namespace Ryujinx.Audio.Renderer.Server.Performance
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];

View File

@ -1,51 +0,0 @@
using System;
using System.Buffers;
using System.Threading;
namespace Ryujinx.Common.Memory
{
public partial class ByteMemoryPool
{
/// <summary>
/// Represents a <see cref="IMemoryOwner{Byte}"/> that wraps an array rented from
/// <see cref="ArrayPool{Byte}.Shared"/> and exposes it as <see cref="Memory{Byte}"/>
/// with a length of the requested size.
/// </summary>
private sealed class ByteMemoryPoolBuffer : IMemoryOwner<byte>
{
private byte[] _array;
private readonly int _length;
public ByteMemoryPoolBuffer(int length)
{
_array = ArrayPool<byte>.Shared.Rent(length);
_length = length;
}
/// <summary>
/// Returns a <see cref="Memory{Byte}"/> belonging to this owner.
/// </summary>
public Memory<byte> Memory
{
get
{
byte[] array = _array;
ObjectDisposedException.ThrowIf(array is null, this);
return new Memory<byte>(array, 0, _length);
}
}
public void Dispose()
{
var array = Interlocked.Exchange(ref _array, null);
if (array != null)
{
ArrayPool<byte>.Shared.Return(array);
}
}
}
}
}

View File

@ -1,106 +0,0 @@
using System;
using System.Buffers;
namespace Ryujinx.Common.Memory
{
/// <summary>
/// Provides a pool of re-usable byte array instances.
/// </summary>
public static partial class ByteMemoryPool
{
/// <summary>
/// Returns the maximum buffer size supported by this pool.
/// </summary>
public static int MaxBufferSize => Array.MaxLength;
/// <summary>
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
/// The buffer may contain data from a prior use.
/// </summary>
/// <param name="length">The buffer's required length in bytes</param>
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static IMemoryOwner<byte> Rent(long length)
=> RentImpl(checked((int)length));
/// <summary>
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
/// The buffer may contain data from a prior use.
/// </summary>
/// <param name="length">The buffer's required length in bytes</param>
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static IMemoryOwner<byte> Rent(ulong length)
=> RentImpl(checked((int)length));
/// <summary>
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
/// The buffer may contain data from a prior use.
/// </summary>
/// <param name="length">The buffer's required length in bytes</param>
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static IMemoryOwner<byte> Rent(int length)
=> RentImpl(length);
/// <summary>
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
/// The buffer's contents are cleared (set to all 0s) before returning.
/// </summary>
/// <param name="length">The buffer's required length in bytes</param>
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static IMemoryOwner<byte> RentCleared(long length)
=> RentCleared(checked((int)length));
/// <summary>
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
/// The buffer's contents are cleared (set to all 0s) before returning.
/// </summary>
/// <param name="length">The buffer's required length in bytes</param>
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static IMemoryOwner<byte> RentCleared(ulong length)
=> RentCleared(checked((int)length));
/// <summary>
/// Rents a byte memory buffer from <see cref="ArrayPool{Byte}.Shared"/>.
/// The buffer's contents are cleared (set to all 0s) before returning.
/// </summary>
/// <param name="length">The buffer's required length in bytes</param>
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception>
public static IMemoryOwner<byte> RentCleared(int length)
{
var buffer = RentImpl(length);
buffer.Memory.Span.Clear();
return buffer;
}
/// <summary>
/// Copies <paramref name="buffer"/> into a newly rented byte memory buffer.
/// </summary>
/// <param name="buffer">The byte buffer to copy</param>
/// <returns>A <see cref="IMemoryOwner{Byte}"/> wrapping the rented memory with <paramref name="buffer"/> copied to it</returns>
public static IMemoryOwner<byte> RentCopy(ReadOnlySpan<byte> buffer)
{
var copy = RentImpl(buffer.Length);
buffer.CopyTo(copy.Memory.Span);
return copy;
}
private static ByteMemoryPoolBuffer RentImpl(int length)
{
if ((uint)length > Array.MaxLength)
{
throw new ArgumentOutOfRangeException(nameof(length), length, null);
}
return new ByteMemoryPoolBuffer(length);
}
}
}

View File

@ -1,6 +1,6 @@
using Ryujinx.Common.Memory;
using Ryujinx.Common.Utilities;
using System;
using System.Buffers;
using System.IO;
using System.Linq;
using System.Reflection;
@ -42,14 +42,14 @@ namespace Ryujinx.Common
return StreamUtils.StreamToBytes(stream);
}
public static IMemoryOwner<byte> ReadFileToRentedMemory(string filename)
public static MemoryOwner<byte> ReadFileToRentedMemory(string filename)
{
var (assembly, path) = ResolveManifestPath(filename);
return ReadFileToRentedMemory(assembly, path);
}
public static IMemoryOwner<byte> ReadFileToRentedMemory(Assembly assembly, string filename)
public static MemoryOwner<byte> ReadFileToRentedMemory(Assembly assembly, string filename)
{
using var stream = GetStream(assembly, filename);

View File

@ -1,6 +1,5 @@
using Microsoft.IO;
using Ryujinx.Common.Memory;
using System.Buffers;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
@ -16,7 +15,7 @@ namespace Ryujinx.Common.Utilities
return output.ToArray();
}
public static IMemoryOwner<byte> StreamToRentedMemory(Stream input)
public static MemoryOwner<byte> StreamToRentedMemory(Stream input)
{
if (input is MemoryStream inputMemoryStream)
{
@ -26,9 +25,9 @@ namespace Ryujinx.Common.Utilities
{
long bytesExpected = input.Length;
IMemoryOwner<byte> ownedMemory = ByteMemoryPool.Rent(bytesExpected);
MemoryOwner<byte> ownedMemory = MemoryOwner<byte>.Rent(checked((int)bytesExpected));
var destSpan = ownedMemory.Memory.Span;
var destSpan = ownedMemory.Span;
int totalBytesRead = 0;
@ -66,14 +65,14 @@ namespace Ryujinx.Common.Utilities
return stream.ToArray();
}
private static IMemoryOwner<byte> MemoryStreamToRentedMemory(MemoryStream input)
private static MemoryOwner<byte> MemoryStreamToRentedMemory(MemoryStream input)
{
input.Position = 0;
IMemoryOwner<byte> ownedMemory = ByteMemoryPool.Rent(input.Length);
MemoryOwner<byte> ownedMemory = MemoryOwner<byte>.Rent(checked((int)input.Length));
// Discard the return value because we assume reading a MemoryStream always succeeds completely.
_ = input.Read(ownedMemory.Memory.Span);
_ = input.Read(ownedMemory.Span);
return ownedMemory;
}

View File

@ -303,9 +303,9 @@ namespace Ryujinx.Cpu.Jit
}
else
{
IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(size);
MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(size);
Read(va, memoryOwner.Memory.Span);
Read(va, memoryOwner.Span);
return new WritableRegion(this, va, memoryOwner);
}

View File

@ -1,7 +1,6 @@
using Ryujinx.Common.Memory;
using Ryujinx.Memory;
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -145,9 +144,9 @@ namespace Ryujinx.Graphics.Device
}
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);
}

View File

@ -199,7 +199,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.InlineToMemory
if (target != null)
{
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.SignalModified();

View File

@ -1,4 +1,5 @@
using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Texture;
@ -805,7 +806,7 @@ namespace Ryujinx.Graphics.Gpu.Image
sliceDepth,
levels,
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}";

View File

@ -2,7 +2,6 @@ using Ryujinx.Common.Memory;
using Ryujinx.Memory;
using Ryujinx.Memory.Range;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -242,9 +241,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
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);
}

View File

@ -192,9 +192,9 @@ namespace Ryujinx.Graphics.Gpu.Memory
}
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;
for (int i = 0; i < range.Count; i++)
@ -203,7 +203,7 @@ namespace Ryujinx.Graphics.Gpu.Memory
int size = (int)currentRange.Size;
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;
}

View File

@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
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 SharedDataFileName = "shared.data";

View File

@ -1,6 +1,5 @@
using Ryujinx.Common.Memory;
using System;
using System.Buffers;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
@ -10,11 +9,11 @@ namespace Ryujinx.Graphics.OpenGL.Image
{
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;

View File

@ -155,9 +155,14 @@ namespace Ryujinx.Graphics.Shader.Translation
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.Utilities;
using System;
using System.Buffers;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
@ -293,9 +292,9 @@ namespace Ryujinx.Graphics.Texture.Astc
int depth,
int levels,
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);

View File

@ -1,7 +1,6 @@
using Ryujinx.Common;
using Ryujinx.Common.Memory;
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
@ -14,7 +13,7 @@ namespace Ryujinx.Graphics.Texture
private const int BlockWidth = 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;
@ -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;
}
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
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);
@ -102,7 +101,7 @@ namespace Ryujinx.Graphics.Texture
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;
@ -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;
}
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
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);
@ -197,7 +196,7 @@ namespace Ryujinx.Graphics.Texture
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;
@ -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;
}
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
Span<byte> tile = stackalloc byte[BlockWidth * BlockHeight * 4];
Span<byte> rPal = stackalloc byte[8];
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);
@ -294,7 +293,7 @@ namespace Ryujinx.Graphics.Texture
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;
@ -306,8 +305,8 @@ namespace Ryujinx.Graphics.Texture
// Backends currently expect a stride alignment of 4 bytes, so output width must be aligned.
int alignedWidth = BitUtils.AlignUp(width, 4);
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
Span<byte> outputSpan = output.Memory.Span;
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
Span<byte> outputSpan = output.Span;
ReadOnlySpan<ulong> data64 = MemoryMarshal.Cast<byte, ulong>(data);
@ -402,7 +401,7 @@ namespace Ryujinx.Graphics.Texture
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;
@ -414,7 +413,7 @@ namespace Ryujinx.Graphics.Texture
// Backends currently expect a stride alignment of 4 bytes, so output width must be aligned.
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);
@ -423,7 +422,7 @@ namespace Ryujinx.Graphics.Texture
Span<byte> rPal = 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> gTileAsUint = MemoryMarshal.Cast<byte, uint>(gTile);
@ -527,7 +526,7 @@ namespace Ryujinx.Graphics.Texture
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;
@ -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;
}
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
Span<byte> outputSpan = output.Memory.Span;
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
Span<byte> outputSpan = output.Span;
int inputOffset = 0;
int outputOffset = 0;
@ -566,7 +565,7 @@ namespace Ryujinx.Graphics.Texture
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;
@ -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;
}
IMemoryOwner<byte> output = ByteMemoryPool.Rent(size);
Span<byte> outputSpan = output.Memory.Span;
MemoryOwner<byte> output = MemoryOwner<byte>.Rent(size);
Span<byte> outputSpan = output.Span;
int inputOffset = 0;
int outputOffset = 0;

View File

@ -2,7 +2,6 @@ using Ryujinx.Common;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.Texture.Encoders;
using System;
using System.Buffers;
namespace Ryujinx.Graphics.Texture
{
@ -11,7 +10,7 @@ namespace Ryujinx.Graphics.Texture
private const int BlockWidth = 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;
@ -23,7 +22,7 @@ namespace Ryujinx.Graphics.Texture
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;
int imageBaseIOffs = 0;

View File

@ -1,7 +1,6 @@
using Ryujinx.Common;
using Ryujinx.Common.Memory;
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Runtime.InteropServices;
@ -51,15 +50,15 @@ namespace Ryujinx.Graphics.Texture
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);
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];
int imageBaseOOffs = 0;
@ -113,15 +112,15 @@ namespace Ryujinx.Graphics.Texture
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);
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];
int imageBaseOOffs = 0;
@ -170,15 +169,15 @@ namespace Ryujinx.Graphics.Texture
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);
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];
int imageBaseOOffs = 0;

View File

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

View File

@ -1,7 +1,6 @@
using Ryujinx.Common;
using Ryujinx.Common.Memory;
using System;
using System.Buffers;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
@ -21,13 +20,14 @@ namespace Ryujinx.Graphics.Texture
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);
Span<ushort> outputSpan = MemoryMarshal.Cast<byte, ushort>(output.Memory.Span);
Span<ushort> outputSpanUInt16 = MemoryMarshal.Cast<byte, ushort>(outputSpan);
if (remainder == 0)
{
@ -38,7 +38,7 @@ namespace Ryujinx.Graphics.Texture
int sizeTrunc = data.Length & ~7;
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)
{
@ -49,7 +49,7 @@ namespace Ryujinx.Graphics.Texture
for (int i = start; i < data.Length; i++)
{
outputSpan[i] = data[i];
outputSpanUInt16[i] = data[i];
}
}
else
@ -61,7 +61,7 @@ namespace Ryujinx.Graphics.Texture
{
for (int x = 0; x < width; x++)
{
outputSpan[outOffset++] = data[offset++];
outputSpanUInt16[outOffset++] = data[offset++];
}
offset += remainder;
@ -72,16 +72,16 @@ namespace Ryujinx.Graphics.Texture
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 outOffset = 0;
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
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++)
{
@ -109,16 +109,16 @@ namespace Ryujinx.Graphics.Texture
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 outOffset = 0;
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
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++)
{
@ -146,16 +146,16 @@ namespace Ryujinx.Graphics.Texture
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 outOffset = 0;
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
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++)
{
@ -183,16 +183,16 @@ namespace Ryujinx.Graphics.Texture
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 outOffset = 0;
(int remainder, int outRemainder, int height) = GetLineRemainders(data.Length, width, 2, 4);
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++)
{

View File

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

View File

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

View File

@ -667,8 +667,36 @@ namespace Ryujinx.Graphics.Vulkan
if (PrepareOutputBuffer(cbs, hostSize, buffer, out VkBuffer copyToBuffer, out BufferHolder tempCopyHolder))
{
// No barrier necessary, as this is a temporary copy buffer.
offset = 0;
}
else
{
BufferHolder.InsertBufferBarrier(
_gd,
cbs.CommandBuffer,
copyToBuffer,
BufferHolder.DefaultAccessFlags,
AccessFlags.TransferWriteBit,
PipelineStageFlags.AllCommandsBit,
PipelineStageFlags.TransferBit,
offset,
outSize);
}
InsertImageBarrier(
_gd.Api,
cbs.CommandBuffer,
image,
TextureStorage.DefaultAccessMask,
AccessFlags.TransferReadBit,
PipelineStageFlags.AllCommandsBit,
PipelineStageFlags.TransferBit,
Info.Format.ConvertAspectFlags(),
FirstLayer + layer,
FirstLevel + level,
1,
1);
CopyFromOrToBuffer(cbs.CommandBuffer, copyToBuffer, image, hostSize, true, layer, level, 1, 1, singleSlice: true, offset, stride);
@ -677,6 +705,19 @@ namespace Ryujinx.Graphics.Vulkan
CopyDataToOutputBuffer(cbs, tempCopyHolder, autoBuffer, hostSize, range.Offset);
tempCopyHolder.Dispose();
}
else
{
BufferHolder.InsertBufferBarrier(
_gd,
cbs.CommandBuffer,
copyToBuffer,
AccessFlags.TransferWriteBit,
BufferHolder.DefaultAccessFlags,
PipelineStageFlags.TransferBit,
PipelineStageFlags.AllCommandsBit,
offset,
outSize);
}
}
private ReadOnlySpan<byte> GetData(CommandBufferPool cbp, PersistentFlushBuffer flushBuffer)

View File

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

View File

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

View File

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

View File

@ -4,6 +4,22 @@ namespace Ryujinx.Memory.Range
{
MultiRange Range { get; }
ulong BaseAddress => Range.GetSubRange(0).Address;
ulong BaseAddress
{
get
{
for (int index = 0; index < Range.Count; index++)
{
MemoryRange subRange = Range.GetSubRange(index);
if (!MemoryRange.IsInvalid(ref subRange))
{
return subRange.Address;
}
}
return MemoryRange.InvalidAddress;
}
}
}
}

View File

@ -5,6 +5,11 @@ namespace Ryujinx.Memory.Range
/// </summary>
public readonly record struct MemoryRange
{
/// <summary>
/// Special address value used to indicate than an address is invalid.
/// </summary>
internal const ulong InvalidAddress = ulong.MaxValue;
/// <summary>
/// An empty memory range, with a null address and zero size.
/// </summary>
@ -58,13 +63,24 @@ namespace Ryujinx.Memory.Range
return thisAddress < otherEndAddress && otherAddress < thisEndAddress;
}
/// <summary>
/// Checks if a given sub-range of memory is invalid.
/// Those are used to represent unmapped memory regions (holes in the region mapping).
/// </summary>
/// <param name="subRange">Memory range to check</param>
/// <returns>True if the memory range is considered invalid, false otherwise</returns>
internal static bool IsInvalid(ref MemoryRange subRange)
{
return subRange.Address == InvalidAddress;
}
/// <summary>
/// Returns a string summary of the memory range.
/// </summary>
/// <returns>A string summary of the memory range</returns>
public override string ToString()
{
if (Address == ulong.MaxValue)
if (Address == InvalidAddress)
{
return $"[Unmapped 0x{Size:X}]";
}

View File

@ -30,7 +30,7 @@ namespace Ryujinx.Memory.Range
{
var subrange = range.GetSubRange(i);
if (IsInvalid(ref subrange))
if (MemoryRange.IsInvalid(ref subrange))
{
continue;
}
@ -56,7 +56,7 @@ namespace Ryujinx.Memory.Range
{
var subrange = range.GetSubRange(i);
if (IsInvalid(ref subrange))
if (MemoryRange.IsInvalid(ref subrange))
{
continue;
}
@ -99,7 +99,7 @@ namespace Ryujinx.Memory.Range
{
var subrange = range.GetSubRange(i);
if (IsInvalid(ref subrange))
if (MemoryRange.IsInvalid(ref subrange))
{
continue;
}
@ -142,17 +142,6 @@ namespace Ryujinx.Memory.Range
return overlapCount;
}
/// <summary>
/// Checks if a given sub-range of memory is invalid.
/// Those are used to represent unmapped memory regions (holes in the region mapping).
/// </summary>
/// <param name="subRange">Memory range to checl</param>
/// <returns>True if the memory range is considered invalid, false otherwise</returns>
private static bool IsInvalid(ref MemoryRange subRange)
{
return subRange.Address == ulong.MaxValue;
}
/// <summary>
/// Gets all items on the list starting at the specified memory address.
/// </summary>

View File

@ -130,9 +130,9 @@ namespace Ryujinx.Memory
}
else
{
IMemoryOwner<byte> memoryOwner = ByteMemoryPool.Rent(size);
MemoryOwner<byte> memoryOwner = MemoryOwner<byte>.Rent(size);
Read(va, memoryOwner.Memory.Span);
Read(va, memoryOwner.Span);
return new WritableRegion(this, va, memoryOwner);
}

View File

@ -34,6 +34,7 @@ namespace Ryujinx.UI.App.Common
{
public class ApplicationLibrary
{
public Language DesiredLanguage { get; set; }
public event EventHandler<ApplicationAddedEventArgs> ApplicationAdded;
public event EventHandler<ApplicationCountUpdatedEventArgs> ApplicationCountUpdated;
@ -45,7 +46,6 @@ namespace Ryujinx.UI.App.Common
private readonly VirtualFileSystem _virtualFileSystem;
private readonly IntegrityCheckLevel _checkLevel;
private Language _desiredTitleLanguage;
private CancellationTokenSource _cancellationToken;
private static readonly ApplicationJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions());
@ -221,7 +221,7 @@ namespace Ryujinx.UI.App.Common
{
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();
@ -266,8 +266,18 @@ namespace Ryujinx.UI.App.Common
public bool TryGetApplicationsFromFile(string applicationPath, out List<ApplicationData> 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);
@ -422,35 +432,40 @@ namespace Ryujinx.UI.App.Common
foreach (var data in applications)
{
ApplicationMetadata appMetadata = LoadAndSaveMetaData(data.IdString, appMetadata =>
// Only load metadata for applications with an ID
if (data.Id != 0)
{
appMetadata.Title = data.Name;
// Only do the migration if time_played has a value and timespan_played hasn't been updated yet.
if (appMetadata.TimePlayedOld != default && appMetadata.TimePlayed == TimeSpan.Zero)
ApplicationMetadata appMetadata = LoadAndSaveMetaData(data.IdString, appMetadata =>
{
appMetadata.TimePlayed = TimeSpan.FromSeconds(appMetadata.TimePlayedOld);
appMetadata.TimePlayedOld = default;
}
appMetadata.Title = data.Name;
// Only do the migration if last_played has a value and last_played_utc doesn't exist yet.
if (appMetadata.LastPlayedOld != default && !appMetadata.LastPlayed.HasValue)
{
// Migrate from string-based last_played to DateTime-based last_played_utc.
if (DateTime.TryParse(appMetadata.LastPlayedOld, out DateTime lastPlayedOldParsed))
// Only do the migration if time_played has a value and timespan_played hasn't been updated yet.
if (appMetadata.TimePlayedOld != default && appMetadata.TimePlayed == TimeSpan.Zero)
{
appMetadata.LastPlayed = lastPlayedOldParsed;
// Migration successful: deleting last_played from the metadata file.
appMetadata.LastPlayedOld = default;
appMetadata.TimePlayed = TimeSpan.FromSeconds(appMetadata.TimePlayedOld);
appMetadata.TimePlayedOld = default;
}
}
});
// Only do the migration if last_played has a value and last_played_utc doesn't exist yet.
if (appMetadata.LastPlayedOld != default && !appMetadata.LastPlayed.HasValue)
{
// Migrate from string-based last_played to DateTime-based last_played_utc.
if (DateTime.TryParse(appMetadata.LastPlayedOld, out DateTime lastPlayedOldParsed))
{
appMetadata.LastPlayed = lastPlayedOldParsed;
// Migration successful: deleting last_played from the metadata file.
appMetadata.LastPlayedOld = default;
}
}
});
data.Favorite = appMetadata.Favorite;
data.TimePlayed = appMetadata.TimePlayed;
data.LastPlayed = appMetadata.LastPlayed;
}
data.Favorite = appMetadata.Favorite;
data.TimePlayed = appMetadata.TimePlayed;
data.LastPlayed = appMetadata.LastPlayed;
data.FileExtension = Path.GetExtension(applicationPath).TrimStart('.').ToUpper();
data.FileSize = fileSize;
data.Path = applicationPath;
@ -472,13 +487,11 @@ namespace Ryujinx.UI.App.Common
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 numApplicationsLoaded = 0;
_desiredTitleLanguage = desiredTitleLanguage;
_cancellationToken = new CancellationTokenSource();
// Builds the applications list with paths to found applications
@ -502,7 +515,13 @@ namespace Ryujinx.UI.App.Common
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
(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);
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;
applicationPaths.Add(fullPath);
numApplicationsFound++;
}
catch (IOException exception)
{
Logger.Warning?.Print(LogClass.Application, $"Failed to resolve the full path to file: \"{app}\" Error: {exception}");
}
}
}
catch (UnauthorizedAccessException)
@ -827,7 +850,7 @@ namespace Ryujinx.UI.App.Common
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)
{

View File

@ -21,7 +21,7 @@ namespace Ryujinx.Ava.Common.Locale
var builder = new CompiledBindingPathBuilder();
builder.SetRawSource(LocaleManager.Instance)
builder
.Property(new ClrPropertyInfo("Item",
obj => (LocaleManager.Instance[keyToUse]),
null,
@ -32,7 +32,10 @@ namespace Ryujinx.Ava.Common.Locale
var path = builder.Build();
var binding = new CompiledBindingExtension(path);
var binding = new CompiledBindingExtension(path)
{
Source = LocaleManager.Instance
};
return binding.ProvideValue(serviceProvider);
}

View File

@ -139,9 +139,11 @@ namespace Ryujinx.Ava.Common.Locale
foreach (var item in locale)
{
this[item.Key] = item.Value;
_localeStrings[item.Key] = item.Value;
}
OnPropertyChanged("Item");
LocaleChanged?.Invoke();
}

View File

@ -1,21 +1,20 @@
using LibHac.Ns;
using Ryujinx.Ava.Common.Locale;
namespace Ryujinx.Ava.UI.Models
{
public class TitleUpdateModel
{
public ApplicationControlProperty Control { get; }
public uint Version { get; }
public string Path { get; }
public string Label { get; }
public string Label => LocaleManager.Instance.UpdateAndGetDynamicValue(
System.IO.Path.GetExtension(Path)?.ToLower() == ".xci" ? LocaleKeys.TitleBundledUpdateVersionLabel : LocaleKeys.TitleUpdateVersionLabel,
Control.DisplayVersionString.ToString()
);
public TitleUpdateModel(ApplicationControlProperty control, string path)
public TitleUpdateModel(uint version, string displayVersion, string path)
{
Control = control;
Version = version;
Label = LocaleManager.Instance.UpdateAndGetDynamicValue(
System.IO.Path.GetExtension(path)?.ToLower() == ".xci" ? LocaleKeys.TitleBundledUpdateVersionLabel : LocaleKeys.TitleUpdateVersionLabel,
displayVersion
);
Path = path;
}
}

View File

@ -131,26 +131,11 @@ namespace Ryujinx.Ava.UI.ViewModels
public void SortUpdates()
{
var list = TitleUpdates.ToList();
list.Sort((first, second) =>
{
if (string.IsNullOrEmpty(first.Control.DisplayVersionString.ToString()))
{
return -1;
}
if (string.IsNullOrEmpty(second.Control.DisplayVersionString.ToString()))
{
return 1;
}
return Version.Parse(first.Control.DisplayVersionString.ToString()).CompareTo(Version.Parse(second.Control.DisplayVersionString.ToString())) * -1;
});
var sortedUpdates = TitleUpdates.OrderByDescending(update => update.Version);
Views.Clear();
Views.Add(new BaseModel());
Views.AddRange(list);
Views.AddRange(sortedUpdates);
if (SelectedUpdate == null)
{
@ -204,7 +189,9 @@ namespace Ryujinx.Ava.UI.ViewModels
controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure();
nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure();
var update = new TitleUpdateModel(controlData, path);
var displayVersion = controlData.DisplayVersionString.ToString();
var update = new TitleUpdateModel(content.Version.Version, displayVersion, path);
TitleUpdates.Add(update);
if (selected)

View File

@ -1,7 +1,7 @@
using Microsoft.IdentityModel.Tokens;
using Ryujinx.Ava.UI.Models;
using System;
using System.Collections.ObjectModel;
using System.Linq;
namespace Ryujinx.Ava.UI.ViewModels
{
@ -11,7 +11,7 @@ namespace Ryujinx.Ava.UI.ViewModels
{
Profiles = new ObservableCollection<BaseModel>();
LostProfiles = new ObservableCollection<UserProfile>();
IsEmpty = LostProfiles.IsNullOrEmpty();
IsEmpty = !LostProfiles.Any();
}
public ObservableCollection<BaseModel> Profiles { get; set; }

View File

@ -37,6 +37,7 @@ namespace Ryujinx.Ava.UI.Windows
internal static MainWindowViewModel MainWindowViewModel { get; private set; }
private bool _isLoading;
private bool _applicationsLoadedOnce;
private UserChannelPersistence _userChannelPersistence;
private static bool _deferLoad;
@ -224,7 +225,10 @@ namespace Ryujinx.Ava.UI.Windows
? IntegrityCheckLevel.ErrorOnInvalid
: 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
// 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();
LoadApplications();
// Load applications if no application was requested by the command line
if (!_deferLoad)
{
LoadApplications();
}
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
CheckLaunchState();
@ -485,6 +493,12 @@ namespace Ryujinx.Ava.UI.Windows
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;
}
}
@ -581,6 +595,7 @@ namespace Ryujinx.Ava.UI.Windows
public void LoadApplications()
{
_applicationsLoadedOnce = true;
ViewModel.Applications.Clear();
StatusBarView.LoadProgressBar.IsVisible = true;
@ -622,7 +637,8 @@ namespace Ryujinx.Ava.UI.Windows
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;
})