Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
12b235700c | ||
|
3be616207d | ||
|
791bf22109 | ||
|
66b1d59c66 | ||
|
c8bb05633e | ||
|
fb1171a21e | ||
|
22c0aa9c90 | ||
|
6d28b64312 | ||
|
05c041feeb | ||
|
8c2da1aa04 |
@@ -13,7 +13,7 @@
|
||||
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
|
||||
<PackageVersion Include="Concentus" Version="1.1.7" />
|
||||
<PackageVersion Include="DiscordRichPresence" Version="1.2.1.24" />
|
||||
<PackageVersion Include="DynamicData" Version="8.3.27" />
|
||||
<PackageVersion Include="DynamicData" Version="8.4.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" />
|
||||
|
@@ -33,8 +33,3 @@ Project Docs
|
||||
=================
|
||||
|
||||
To be added. Many project files will contain basic XML docs for key functions and classes in the meantime.
|
||||
|
||||
Other Information
|
||||
=================
|
||||
|
||||
- N/A
|
||||
|
@@ -1,5 +1,3 @@
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Collections;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
|
||||
@@ -7,175 +5,23 @@ namespace Ryujinx.Cpu
|
||||
{
|
||||
public class AddressSpace : IDisposable
|
||||
{
|
||||
private const int DefaultBlockAlignment = 1 << 20;
|
||||
|
||||
private enum MappingType : byte
|
||||
{
|
||||
None,
|
||||
Private,
|
||||
Shared,
|
||||
}
|
||||
|
||||
private class Mapping : IntrusiveRedBlackTreeNode<Mapping>, IComparable<Mapping>
|
||||
{
|
||||
public ulong Address { get; private set; }
|
||||
public ulong Size { get; private set; }
|
||||
public ulong EndAddress => Address + Size;
|
||||
public MappingType Type { get; private set; }
|
||||
|
||||
public Mapping(ulong address, ulong size, MappingType type)
|
||||
{
|
||||
Address = address;
|
||||
Size = size;
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public Mapping Split(ulong splitAddress)
|
||||
{
|
||||
ulong leftSize = splitAddress - Address;
|
||||
ulong rightSize = EndAddress - splitAddress;
|
||||
|
||||
Mapping left = new(Address, leftSize, Type);
|
||||
|
||||
Address = splitAddress;
|
||||
Size = rightSize;
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
public void UpdateState(MappingType newType)
|
||||
{
|
||||
Type = newType;
|
||||
}
|
||||
|
||||
public void Extend(ulong sizeDelta)
|
||||
{
|
||||
Size += sizeDelta;
|
||||
}
|
||||
|
||||
public int CompareTo(Mapping other)
|
||||
{
|
||||
if (Address < other.Address)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (Address <= other.EndAddress - 1UL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class PrivateMapping : IntrusiveRedBlackTreeNode<PrivateMapping>, IComparable<PrivateMapping>
|
||||
{
|
||||
public ulong Address { get; private set; }
|
||||
public ulong Size { get; private set; }
|
||||
public ulong EndAddress => Address + Size;
|
||||
public PrivateMemoryAllocation PrivateAllocation { get; private set; }
|
||||
|
||||
public PrivateMapping(ulong address, ulong size, PrivateMemoryAllocation privateAllocation)
|
||||
{
|
||||
Address = address;
|
||||
Size = size;
|
||||
PrivateAllocation = privateAllocation;
|
||||
}
|
||||
|
||||
public PrivateMapping Split(ulong splitAddress)
|
||||
{
|
||||
ulong leftSize = splitAddress - Address;
|
||||
ulong rightSize = EndAddress - splitAddress;
|
||||
|
||||
(var leftAllocation, PrivateAllocation) = PrivateAllocation.Split(leftSize);
|
||||
|
||||
PrivateMapping left = new(Address, leftSize, leftAllocation);
|
||||
|
||||
Address = splitAddress;
|
||||
Size = rightSize;
|
||||
|
||||
return left;
|
||||
}
|
||||
|
||||
public void Map(MemoryBlock baseBlock, MemoryBlock mirrorBlock, PrivateMemoryAllocation newAllocation)
|
||||
{
|
||||
baseBlock.MapView(newAllocation.Memory, newAllocation.Offset, Address, Size);
|
||||
mirrorBlock.MapView(newAllocation.Memory, newAllocation.Offset, Address, Size);
|
||||
PrivateAllocation = newAllocation;
|
||||
}
|
||||
|
||||
public void Unmap(MemoryBlock baseBlock, MemoryBlock mirrorBlock)
|
||||
{
|
||||
if (PrivateAllocation.IsValid)
|
||||
{
|
||||
baseBlock.UnmapView(PrivateAllocation.Memory, Address, Size);
|
||||
mirrorBlock.UnmapView(PrivateAllocation.Memory, Address, Size);
|
||||
PrivateAllocation.Dispose();
|
||||
}
|
||||
|
||||
PrivateAllocation = default;
|
||||
}
|
||||
|
||||
public void Extend(ulong sizeDelta)
|
||||
{
|
||||
Size += sizeDelta;
|
||||
}
|
||||
|
||||
public int CompareTo(PrivateMapping other)
|
||||
{
|
||||
if (Address < other.Address)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
else if (Address <= other.EndAddress - 1UL)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly MemoryBlock _backingMemory;
|
||||
private readonly PrivateMemoryAllocator _privateMemoryAllocator;
|
||||
private readonly IntrusiveRedBlackTree<Mapping> _mappingTree;
|
||||
private readonly IntrusiveRedBlackTree<PrivateMapping> _privateTree;
|
||||
|
||||
private readonly object _treeLock;
|
||||
|
||||
private readonly bool _supports4KBPages;
|
||||
|
||||
public MemoryBlock Base { get; }
|
||||
public MemoryBlock Mirror { get; }
|
||||
|
||||
public ulong AddressSpaceSize { get; }
|
||||
|
||||
public AddressSpace(MemoryBlock backingMemory, MemoryBlock baseMemory, MemoryBlock mirrorMemory, ulong addressSpaceSize, bool supports4KBPages)
|
||||
public AddressSpace(MemoryBlock backingMemory, MemoryBlock baseMemory, MemoryBlock mirrorMemory, ulong addressSpaceSize)
|
||||
{
|
||||
if (!supports4KBPages)
|
||||
{
|
||||
_privateMemoryAllocator = new PrivateMemoryAllocator(DefaultBlockAlignment, MemoryAllocationFlags.Mirrorable | MemoryAllocationFlags.NoMap);
|
||||
_mappingTree = new IntrusiveRedBlackTree<Mapping>();
|
||||
_privateTree = new IntrusiveRedBlackTree<PrivateMapping>();
|
||||
_treeLock = new object();
|
||||
|
||||
_mappingTree.Add(new Mapping(0UL, addressSpaceSize, MappingType.None));
|
||||
_privateTree.Add(new PrivateMapping(0UL, addressSpaceSize, default));
|
||||
}
|
||||
|
||||
_backingMemory = backingMemory;
|
||||
_supports4KBPages = supports4KBPages;
|
||||
|
||||
Base = baseMemory;
|
||||
Mirror = mirrorMemory;
|
||||
AddressSpaceSize = addressSpaceSize;
|
||||
}
|
||||
|
||||
public static bool TryCreate(MemoryBlock backingMemory, ulong asSize, bool supports4KBPages, out AddressSpace addressSpace)
|
||||
public static bool TryCreate(MemoryBlock backingMemory, ulong asSize, out AddressSpace addressSpace)
|
||||
{
|
||||
addressSpace = null;
|
||||
|
||||
@@ -193,7 +39,7 @@ namespace Ryujinx.Cpu
|
||||
{
|
||||
baseMemory = new MemoryBlock(addressSpaceSize, AsFlags);
|
||||
mirrorMemory = new MemoryBlock(addressSpaceSize, AsFlags);
|
||||
addressSpace = new AddressSpace(backingMemory, baseMemory, mirrorMemory, addressSpaceSize, supports4KBPages);
|
||||
addressSpace = new AddressSpace(backingMemory, baseMemory, mirrorMemory, addressSpaceSize);
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -209,289 +55,20 @@ namespace Ryujinx.Cpu
|
||||
|
||||
public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags)
|
||||
{
|
||||
if (_supports4KBPages)
|
||||
{
|
||||
Base.MapView(_backingMemory, pa, va, size);
|
||||
Mirror.MapView(_backingMemory, pa, va, size);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_treeLock)
|
||||
{
|
||||
ulong alignment = MemoryBlock.GetPageSize();
|
||||
bool isAligned = ((va | pa | size) & (alignment - 1)) == 0;
|
||||
|
||||
if (flags.HasFlag(MemoryMapFlags.Private) && !isAligned)
|
||||
{
|
||||
Update(va, pa, size, MappingType.Private);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The update method assumes that shared mappings are already aligned.
|
||||
|
||||
if (!flags.HasFlag(MemoryMapFlags.Private))
|
||||
{
|
||||
if ((va & (alignment - 1)) != (pa & (alignment - 1)))
|
||||
{
|
||||
throw new InvalidMemoryRegionException($"Virtual address 0x{va:X} and physical address 0x{pa:X} are misaligned and can't be aligned.");
|
||||
}
|
||||
|
||||
ulong endAddress = va + size;
|
||||
va = BitUtils.AlignDown(va, alignment);
|
||||
pa = BitUtils.AlignDown(pa, alignment);
|
||||
size = BitUtils.AlignUp(endAddress, alignment) - va;
|
||||
}
|
||||
|
||||
Update(va, pa, size, MappingType.Shared);
|
||||
}
|
||||
}
|
||||
Base.MapView(_backingMemory, pa, va, size);
|
||||
Mirror.MapView(_backingMemory, pa, va, size);
|
||||
}
|
||||
|
||||
public void Unmap(ulong va, ulong size)
|
||||
{
|
||||
if (_supports4KBPages)
|
||||
{
|
||||
Base.UnmapView(_backingMemory, va, size);
|
||||
Mirror.UnmapView(_backingMemory, va, size);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_treeLock)
|
||||
{
|
||||
Update(va, 0UL, size, MappingType.None);
|
||||
}
|
||||
}
|
||||
|
||||
private void Update(ulong va, ulong pa, ulong size, MappingType type)
|
||||
{
|
||||
Mapping map = _mappingTree.GetNode(new Mapping(va, 1UL, MappingType.None));
|
||||
|
||||
Update(map, va, pa, size, type);
|
||||
}
|
||||
|
||||
private Mapping Update(Mapping map, ulong va, ulong pa, ulong size, MappingType type)
|
||||
{
|
||||
ulong endAddress = va + size;
|
||||
|
||||
for (; map != null; map = map.Successor)
|
||||
{
|
||||
if (map.Address < va)
|
||||
{
|
||||
_mappingTree.Add(map.Split(va));
|
||||
}
|
||||
|
||||
if (map.EndAddress > endAddress)
|
||||
{
|
||||
Mapping newMap = map.Split(endAddress);
|
||||
_mappingTree.Add(newMap);
|
||||
map = newMap;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case MappingType.None:
|
||||
if (map.Type == MappingType.Shared)
|
||||
{
|
||||
ulong startOffset = map.Address - va;
|
||||
ulong mapVa = va + startOffset;
|
||||
ulong mapSize = Math.Min(size - startOffset, map.Size);
|
||||
ulong mapEndAddress = mapVa + mapSize;
|
||||
ulong alignment = MemoryBlock.GetPageSize();
|
||||
|
||||
mapVa = BitUtils.AlignDown(mapVa, alignment);
|
||||
mapEndAddress = BitUtils.AlignUp(mapEndAddress, alignment);
|
||||
|
||||
mapSize = mapEndAddress - mapVa;
|
||||
|
||||
Base.UnmapView(_backingMemory, mapVa, mapSize);
|
||||
Mirror.UnmapView(_backingMemory, mapVa, mapSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
UnmapPrivate(va, size);
|
||||
}
|
||||
break;
|
||||
case MappingType.Private:
|
||||
if (map.Type == MappingType.Shared)
|
||||
{
|
||||
throw new InvalidMemoryRegionException($"Private mapping request at 0x{va:X} with size 0x{size:X} overlaps shared mapping at 0x{map.Address:X} with size 0x{map.Size:X}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
MapPrivate(va, size);
|
||||
}
|
||||
break;
|
||||
case MappingType.Shared:
|
||||
if (map.Type != MappingType.None)
|
||||
{
|
||||
throw new InvalidMemoryRegionException($"Shared mapping request at 0x{va:X} with size 0x{size:X} overlaps mapping at 0x{map.Address:X} with size 0x{map.Size:X}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
ulong startOffset = map.Address - va;
|
||||
ulong mapPa = pa + startOffset;
|
||||
ulong mapVa = va + startOffset;
|
||||
ulong mapSize = Math.Min(size - startOffset, map.Size);
|
||||
|
||||
Base.MapView(_backingMemory, mapPa, mapVa, mapSize);
|
||||
Mirror.MapView(_backingMemory, mapPa, mapVa, mapSize);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
map.UpdateState(type);
|
||||
map = TryCoalesce(map);
|
||||
|
||||
if (map.EndAddress >= endAddress)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private Mapping TryCoalesce(Mapping map)
|
||||
{
|
||||
Mapping previousMap = map.Predecessor;
|
||||
Mapping nextMap = map.Successor;
|
||||
|
||||
if (previousMap != null && CanCoalesce(previousMap, map))
|
||||
{
|
||||
previousMap.Extend(map.Size);
|
||||
_mappingTree.Remove(map);
|
||||
map = previousMap;
|
||||
}
|
||||
|
||||
if (nextMap != null && CanCoalesce(map, nextMap))
|
||||
{
|
||||
map.Extend(nextMap.Size);
|
||||
_mappingTree.Remove(nextMap);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private static bool CanCoalesce(Mapping left, Mapping right)
|
||||
{
|
||||
return left.Type == right.Type;
|
||||
}
|
||||
|
||||
private void MapPrivate(ulong va, ulong size)
|
||||
{
|
||||
ulong endAddress = va + size;
|
||||
|
||||
ulong alignment = MemoryBlock.GetPageSize();
|
||||
|
||||
// Expand the range outwards based on page size to ensure that at least the requested region is mapped.
|
||||
ulong vaAligned = BitUtils.AlignDown(va, alignment);
|
||||
ulong endAddressAligned = BitUtils.AlignUp(endAddress, alignment);
|
||||
|
||||
PrivateMapping map = _privateTree.GetNode(new PrivateMapping(va, 1UL, default));
|
||||
|
||||
for (; map != null; map = map.Successor)
|
||||
{
|
||||
if (!map.PrivateAllocation.IsValid)
|
||||
{
|
||||
if (map.Address < vaAligned)
|
||||
{
|
||||
_privateTree.Add(map.Split(vaAligned));
|
||||
}
|
||||
|
||||
if (map.EndAddress > endAddressAligned)
|
||||
{
|
||||
PrivateMapping newMap = map.Split(endAddressAligned);
|
||||
_privateTree.Add(newMap);
|
||||
map = newMap;
|
||||
}
|
||||
|
||||
map.Map(Base, Mirror, _privateMemoryAllocator.Allocate(map.Size, MemoryBlock.GetPageSize()));
|
||||
}
|
||||
|
||||
if (map.EndAddress >= endAddressAligned)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UnmapPrivate(ulong va, ulong size)
|
||||
{
|
||||
ulong endAddress = va + size;
|
||||
|
||||
ulong alignment = MemoryBlock.GetPageSize();
|
||||
|
||||
// Shrink the range inwards based on page size to ensure we won't unmap memory that might be still in use.
|
||||
ulong vaAligned = BitUtils.AlignUp(va, alignment);
|
||||
ulong endAddressAligned = BitUtils.AlignDown(endAddress, alignment);
|
||||
|
||||
if (endAddressAligned <= vaAligned)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PrivateMapping map = _privateTree.GetNode(new PrivateMapping(va, 1UL, default));
|
||||
|
||||
for (; map != null; map = map.Successor)
|
||||
{
|
||||
if (map.PrivateAllocation.IsValid)
|
||||
{
|
||||
if (map.Address < vaAligned)
|
||||
{
|
||||
_privateTree.Add(map.Split(vaAligned));
|
||||
}
|
||||
|
||||
if (map.EndAddress > endAddressAligned)
|
||||
{
|
||||
PrivateMapping newMap = map.Split(endAddressAligned);
|
||||
_privateTree.Add(newMap);
|
||||
map = newMap;
|
||||
}
|
||||
|
||||
map.Unmap(Base, Mirror);
|
||||
map = TryCoalesce(map);
|
||||
}
|
||||
|
||||
if (map.EndAddress >= endAddressAligned)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private PrivateMapping TryCoalesce(PrivateMapping map)
|
||||
{
|
||||
PrivateMapping previousMap = map.Predecessor;
|
||||
PrivateMapping nextMap = map.Successor;
|
||||
|
||||
if (previousMap != null && CanCoalesce(previousMap, map))
|
||||
{
|
||||
previousMap.Extend(map.Size);
|
||||
_privateTree.Remove(map);
|
||||
map = previousMap;
|
||||
}
|
||||
|
||||
if (nextMap != null && CanCoalesce(map, nextMap))
|
||||
{
|
||||
map.Extend(nextMap.Size);
|
||||
_privateTree.Remove(nextMap);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private static bool CanCoalesce(PrivateMapping left, PrivateMapping right)
|
||||
{
|
||||
return !left.PrivateAllocation.IsValid && !right.PrivateAllocation.IsValid;
|
||||
Base.UnmapView(_backingMemory, va, size);
|
||||
Mirror.UnmapView(_backingMemory, va, size);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
GC.SuppressFinalize(this);
|
||||
|
||||
_privateMemoryAllocator?.Dispose();
|
||||
Base.Dispose();
|
||||
Mirror.Dispose();
|
||||
}
|
||||
|
@@ -28,7 +28,7 @@ namespace Ryujinx.Cpu.AppleHv
|
||||
|
||||
private readonly ManagedPageFlags _pages;
|
||||
|
||||
public bool Supports4KBPages => true;
|
||||
public bool UsesPrivateAllocations => false;
|
||||
|
||||
public int AddressSpaceBits { get; }
|
||||
|
||||
|
@@ -25,7 +25,7 @@ namespace Ryujinx.Cpu.Jit
|
||||
private readonly InvalidAccessHandler _invalidAccessHandler;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Supports4KBPages => true;
|
||||
public bool UsesPrivateAllocations => false;
|
||||
|
||||
/// <summary>
|
||||
/// Address space width in bits.
|
||||
|
@@ -27,7 +27,7 @@ namespace Ryujinx.Cpu.Jit
|
||||
private readonly ManagedPageFlags _pages;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Supports4KBPages => MemoryBlock.GetPageSize() == PageSize;
|
||||
public bool UsesPrivateAllocations => false;
|
||||
|
||||
public int AddressSpaceBits { get; }
|
||||
|
||||
|
@@ -33,7 +33,7 @@ namespace Ryujinx.Cpu.Jit
|
||||
protected override ulong AddressSpaceSize { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool Supports4KBPages => false;
|
||||
public bool UsesPrivateAllocations => true;
|
||||
|
||||
public IntPtr PageTablePointer => _nativePageTable.PageTablePointer;
|
||||
|
||||
|
@@ -981,6 +981,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_bindingBarriersDirty = true;
|
||||
|
||||
_newState.PipelineLayout = internalProgram.PipelineLayout;
|
||||
_newState.HasTessellationControlShader = internalProgram.HasTessellationControlShader;
|
||||
_newState.StagesCount = (uint)stages.Length;
|
||||
|
||||
stages.CopyTo(_newState.Stages.AsSpan()[..stages.Length]);
|
||||
|
@@ -311,6 +311,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
set => Internal.Id9 = (Internal.Id9 & 0xFFFFFFFFFFFFFFBF) | ((value ? 1UL : 0UL) << 6);
|
||||
}
|
||||
|
||||
public bool HasTessellationControlShader;
|
||||
public NativeArray<PipelineShaderStageCreateInfo> Stages;
|
||||
public PipelineLayout PipelineLayout;
|
||||
public SpecData SpecializationData;
|
||||
@@ -319,6 +320,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
HasTessellationControlShader = false;
|
||||
Stages = new NativeArray<PipelineShaderStageCreateInfo>(Constants.MaxShaderStages);
|
||||
|
||||
AdvancedBlendSrcPreMultiplied = true;
|
||||
@@ -419,6 +421,15 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
PVertexBindingDescriptions = pVertexBindingDescriptions,
|
||||
};
|
||||
|
||||
// Using patches topology without a tessellation shader is invalid.
|
||||
// If we find such a case, return null pipeline to skip the draw.
|
||||
if (Topology == PrimitiveTopology.PatchList && !HasTessellationControlShader)
|
||||
{
|
||||
program.AddGraphicsPipeline(ref Internal, null);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
bool primitiveRestartEnable = PrimitiveRestartEnable;
|
||||
|
||||
bool topologySupportsRestart;
|
||||
|
@@ -122,7 +122,6 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
gd.Api.CreateRenderPass(device, renderPassCreateInfo, null, out var renderPass).ThrowOnError();
|
||||
|
||||
_renderPass?.Dispose();
|
||||
_renderPass = new Auto<DisposableRenderPass>(new DisposableRenderPass(gd.Api, device, renderPass));
|
||||
}
|
||||
|
||||
@@ -162,7 +161,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Dispose all framebuffers
|
||||
// Dispose all framebuffers.
|
||||
|
||||
foreach (var fb in _framebuffers.Values)
|
||||
{
|
||||
@@ -175,6 +174,10 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
texture.RemoveRenderPass(_key);
|
||||
}
|
||||
|
||||
// Dispose render pass.
|
||||
|
||||
_renderPass.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -21,6 +21,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
public bool HasMinimalLayout { get; }
|
||||
public bool UsePushDescriptors { get; }
|
||||
public bool IsCompute { get; }
|
||||
public bool HasTessellationControlShader => (Stages & (1u << 3)) != 0;
|
||||
|
||||
public uint Stages { get; }
|
||||
|
||||
@@ -461,6 +462,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
stages[i] = _shaders[i].GetInfo();
|
||||
}
|
||||
|
||||
pipeline.HasTessellationControlShader = HasTessellationControlShader;
|
||||
pipeline.StagesCount = (uint)_shaders.Length;
|
||||
pipeline.PipelineLayout = PipelineLayout;
|
||||
|
||||
|
@@ -4,6 +4,7 @@ using Silk.NET.Vulkan;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Format = Ryujinx.Graphics.GAL.Format;
|
||||
using VkBuffer = Silk.NET.Vulkan.Buffer;
|
||||
using VkFormat = Silk.NET.Vulkan.Format;
|
||||
@@ -36,7 +37,8 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
public int FirstLayer { get; }
|
||||
public int FirstLevel { get; }
|
||||
public VkFormat VkFormat { get; }
|
||||
public bool Valid { get; private set; }
|
||||
private int _isValid;
|
||||
public bool Valid => Volatile.Read(ref _isValid) != 0;
|
||||
|
||||
public TextureView(
|
||||
VulkanRenderer gd,
|
||||
@@ -158,7 +160,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
}
|
||||
}
|
||||
|
||||
Valid = true;
|
||||
_isValid = 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -178,7 +180,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
|
||||
VkFormat = format;
|
||||
|
||||
Valid = true;
|
||||
_isValid = 1;
|
||||
}
|
||||
|
||||
public Auto<DisposableImage> GetImage()
|
||||
@@ -1017,10 +1019,11 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
Valid = false;
|
||||
|
||||
if (_gd.Textures.Remove(this))
|
||||
bool wasValid = Interlocked.Exchange(ref _isValid, 0) != 0;
|
||||
if (wasValid)
|
||||
{
|
||||
_gd.Textures.Remove(this);
|
||||
|
||||
_imageView.Dispose();
|
||||
_imageView2dArray?.Dispose();
|
||||
|
||||
@@ -1034,7 +1037,7 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
_imageViewDraw.Dispose();
|
||||
}
|
||||
|
||||
Storage.DecrementViewsCount();
|
||||
Storage?.DecrementViewsCount();
|
||||
|
||||
if (_renderPasses != null)
|
||||
{
|
||||
@@ -1045,22 +1048,22 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
pass.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
if (_selfManagedViews != null)
|
||||
{
|
||||
foreach (var view in _selfManagedViews.Values)
|
||||
{
|
||||
view.Dispose();
|
||||
}
|
||||
|
||||
_selfManagedViews = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_selfManagedViews != null)
|
||||
{
|
||||
foreach (var view in _selfManagedViews.Values)
|
||||
{
|
||||
view.Dispose();
|
||||
}
|
||||
|
||||
_selfManagedViews = null;
|
||||
}
|
||||
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
|
@@ -75,7 +75,7 @@ namespace Ryujinx.HLE.HOS
|
||||
// We want to use host tracked mode if the host page size is > 4KB.
|
||||
if ((mode == MemoryManagerMode.HostMapped || mode == MemoryManagerMode.HostMappedUnsafe) && MemoryBlock.GetPageSize() <= 0x1000)
|
||||
{
|
||||
if (!AddressSpace.TryCreate(context.Memory, addressSpaceSize, MemoryBlock.GetPageSize() == MemoryManagerHostMapped.PageSize, out addressSpace))
|
||||
if (!AddressSpace.TryCreate(context.Memory, addressSpaceSize, out addressSpace))
|
||||
{
|
||||
Logger.Warning?.Print(LogClass.Cpu, "Address space creation failed, falling back to software page table");
|
||||
|
||||
|
@@ -11,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
private readonly IVirtualMemoryManager _cpuMemory;
|
||||
|
||||
protected override bool Supports4KBPages => _cpuMemory.Supports4KBPages;
|
||||
protected override bool UsesPrivateAllocations => _cpuMemory.UsesPrivateAllocations;
|
||||
|
||||
public KPageTable(KernelContext context, IVirtualMemoryManager cpuMemory, ulong reservedAddressSpaceSize) : base(context, reservedAddressSpaceSize)
|
||||
{
|
||||
|
@@ -32,7 +32,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
private const int MaxBlocksNeededForInsertion = 2;
|
||||
|
||||
protected readonly KernelContext Context;
|
||||
protected virtual bool Supports4KBPages => true;
|
||||
protected virtual bool UsesPrivateAllocations => false;
|
||||
|
||||
public ulong AddrSpaceStart { get; private set; }
|
||||
public ulong AddrSpaceEnd { get; private set; }
|
||||
@@ -1947,17 +1947,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
|
||||
Result result;
|
||||
|
||||
if (srcPageTable.Supports4KBPages)
|
||||
if (srcPageTable.UsesPrivateAllocations)
|
||||
{
|
||||
result = MapForeign(srcPageTable.GetHostRegions(addressRounded, alignedSize), currentVa, alignedSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
KPageList pageList = new();
|
||||
srcPageTable.GetPhysicalRegions(addressRounded, alignedSize, pageList);
|
||||
|
||||
result = MapPages(currentVa, pageList, permission, MemoryMapFlags.None);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = MapForeign(srcPageTable.GetHostRegions(addressRounded, alignedSize), currentVa, alignedSize);
|
||||
}
|
||||
|
||||
if (result != Result.Success)
|
||||
{
|
||||
|
@@ -2,7 +2,6 @@ using Ryujinx.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Common;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Memory;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
{
|
||||
@@ -49,17 +48,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
||||
return KernelResult.InvalidPermission;
|
||||
}
|
||||
|
||||
// On platforms with page size > 4 KB, this can fail due to the address not being page aligned,
|
||||
// we can return an error to force the application to retry with a different address.
|
||||
|
||||
try
|
||||
{
|
||||
return memoryManager.MapPages(address, _pageList, MemoryState.SharedMemory, permission);
|
||||
}
|
||||
catch (InvalidMemoryRegionException)
|
||||
{
|
||||
return KernelResult.InvalidMemState;
|
||||
}
|
||||
return memoryManager.MapPages(address, _pageList, MemoryState.SharedMemory, permission);
|
||||
}
|
||||
|
||||
public Result UnmapFromProcess(KPageTableBase memoryManager, ulong address, ulong size, KProcess process)
|
||||
|
@@ -218,7 +218,7 @@ namespace Ryujinx.HLE.HOS
|
||||
|
||||
if (types.Length > 0)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.ModLoader, $"Found mod '{mod.Name}' [{types}]");
|
||||
Logger.Info?.Print(LogClass.ModLoader, $"Found {(mod.Enabled ? "enabled" : "disabled")} mod '{mod.Name}' [{types}]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,39 +0,0 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Services.Ptm.Ts.Types;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Ptm.Ts
|
||||
{
|
||||
[Service("ts")]
|
||||
class IMeasurementServer : IpcService
|
||||
{
|
||||
private const uint DefaultTemperature = 42u;
|
||||
|
||||
public IMeasurementServer(ServiceCtx context) { }
|
||||
|
||||
[CommandCmif(1)]
|
||||
// GetTemperature(Location location) -> u32
|
||||
public ResultCode GetTemperature(ServiceCtx context)
|
||||
{
|
||||
Location location = (Location)context.RequestData.ReadByte();
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServicePtm, new { location });
|
||||
|
||||
context.ResponseData.Write(DefaultTemperature);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(3)]
|
||||
// GetTemperatureMilliC(Location location) -> u32
|
||||
public ResultCode GetTemperatureMilliC(ServiceCtx context)
|
||||
{
|
||||
Location location = (Location)context.RequestData.ReadByte();
|
||||
|
||||
Logger.Stub?.PrintStub(LogClass.ServicePtm, new { location });
|
||||
|
||||
context.ResponseData.Write(DefaultTemperature * 1000);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
63
src/Ryujinx.Horizon/Ptm/Ipc/MeasurementServer.cs
Normal file
63
src/Ryujinx.Horizon/Ptm/Ipc/MeasurementServer.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
using Ryujinx.Horizon.Sdk.Ts;
|
||||
using Ryujinx.Horizon.Ts.Ipc;
|
||||
|
||||
namespace Ryujinx.Horizon.Ptm.Ipc
|
||||
{
|
||||
partial class MeasurementServer : IMeasurementServer
|
||||
{
|
||||
// NOTE: Values are randomly choosen.
|
||||
public const int DefaultTemperature = 42;
|
||||
public const int MinimumTemperature = 0;
|
||||
public const int MaximumTemperature = 100;
|
||||
|
||||
[CmifCommand(0)] // 1.0.0-16.1.0
|
||||
public Result GetTemperatureRange(out int minimumTemperature, out int maximumTemperature, Location location)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServicePtm, new { location });
|
||||
|
||||
minimumTemperature = MinimumTemperature;
|
||||
maximumTemperature = MaximumTemperature;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(1)] // 1.0.0-16.1.0
|
||||
public Result GetTemperature(out int temperature, Location location)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServicePtm, new { location });
|
||||
|
||||
temperature = DefaultTemperature;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(2)] // 1.0.0-13.2.1
|
||||
public Result SetMeasurementMode(Location location, byte measurementMode)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServicePtm, new { location, measurementMode });
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(3)] // 1.0.0-13.2.1
|
||||
public Result GetTemperatureMilliC(out int temperatureMilliC, Location location)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServicePtm, new { location });
|
||||
|
||||
temperatureMilliC = DefaultTemperature * 1000;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(4)] // 8.0.0+
|
||||
public Result OpenSession(out ISession session, DeviceCode deviceCode)
|
||||
{
|
||||
session = new Session(deviceCode);
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
47
src/Ryujinx.Horizon/Ptm/Ipc/Session.cs
Normal file
47
src/Ryujinx.Horizon/Ptm/Ipc/Session.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Ptm.Ipc;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
using Ryujinx.Horizon.Sdk.Ts;
|
||||
|
||||
namespace Ryujinx.Horizon.Ts.Ipc
|
||||
{
|
||||
partial class Session : ISession
|
||||
{
|
||||
private readonly DeviceCode _deviceCode;
|
||||
|
||||
public Session(DeviceCode deviceCode)
|
||||
{
|
||||
_deviceCode = deviceCode;
|
||||
}
|
||||
|
||||
[CmifCommand(0)]
|
||||
public Result GetTemperatureRange(out int minimumTemperature, out int maximumTemperature)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServicePtm, new { _deviceCode });
|
||||
|
||||
minimumTemperature = MeasurementServer.MinimumTemperature;
|
||||
maximumTemperature = MeasurementServer.MaximumTemperature;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(2)]
|
||||
public Result SetMeasurementMode(byte measurementMode)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServicePtm, new { _deviceCode, measurementMode });
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
|
||||
[CmifCommand(4)]
|
||||
public Result GetTemperature(out int temperature)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServicePtm, new { _deviceCode });
|
||||
|
||||
temperature = MeasurementServer.DefaultTemperature;
|
||||
|
||||
return Result.Success;
|
||||
}
|
||||
}
|
||||
}
|
44
src/Ryujinx.Horizon/Ptm/TsIpcServer.cs
Normal file
44
src/Ryujinx.Horizon/Ptm/TsIpcServer.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using Ryujinx.Horizon.Ptm.Ipc;
|
||||
using Ryujinx.Horizon.Sdk.Sf.Hipc;
|
||||
using Ryujinx.Horizon.Sdk.Sm;
|
||||
|
||||
namespace Ryujinx.Horizon.Ptm
|
||||
{
|
||||
class TsIpcServer
|
||||
{
|
||||
private const int MaxSessionsCount = 4;
|
||||
|
||||
private const int PointerBufferSize = 0;
|
||||
private const int MaxDomains = 0;
|
||||
private const int MaxDomainObjects = 0;
|
||||
private const int MaxPortsCount = 1;
|
||||
|
||||
private static readonly ManagerOptions _managerOptions = new(PointerBufferSize, MaxDomains, MaxDomainObjects, false);
|
||||
|
||||
private SmApi _sm;
|
||||
private ServerManager _serverManager;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
HeapAllocator allocator = new();
|
||||
|
||||
_sm = new SmApi();
|
||||
_sm.Initialize().AbortOnFailure();
|
||||
|
||||
_serverManager = new ServerManager(allocator, _sm, MaxPortsCount, _managerOptions, MaxSessionsCount);
|
||||
|
||||
_serverManager.RegisterObjectForServer(new MeasurementServer(), ServiceName.Encode("ts"), MaxSessionsCount);
|
||||
}
|
||||
|
||||
public void ServiceRequests()
|
||||
{
|
||||
_serverManager.ServiceRequests();
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
_serverManager.Dispose();
|
||||
_sm.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
17
src/Ryujinx.Horizon/Ptm/TsMain.cs
Normal file
17
src/Ryujinx.Horizon/Ptm/TsMain.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
namespace Ryujinx.Horizon.Ptm
|
||||
{
|
||||
class TsMain : IService
|
||||
{
|
||||
public static void Main(ServiceTable serviceTable)
|
||||
{
|
||||
TsIpcServer ipcServer = new();
|
||||
|
||||
ipcServer.Initialize();
|
||||
|
||||
serviceTable.SignalServiceReady();
|
||||
|
||||
ipcServer.ServiceRequests();
|
||||
ipcServer.Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
8
src/Ryujinx.Horizon/Sdk/Ts/DeviceCode.cs
Normal file
8
src/Ryujinx.Horizon/Sdk/Ts/DeviceCode.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.Horizon.Sdk.Ts
|
||||
{
|
||||
enum DeviceCode : uint
|
||||
{
|
||||
Internal = 0x41000001,
|
||||
External = 0x41000002,
|
||||
}
|
||||
}
|
14
src/Ryujinx.Horizon/Sdk/Ts/IMeasurementServer.cs
Normal file
14
src/Ryujinx.Horizon/Sdk/Ts/IMeasurementServer.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Ts
|
||||
{
|
||||
interface IMeasurementServer : IServiceObject
|
||||
{
|
||||
Result GetTemperatureRange(out int minimumTemperature, out int maximumTemperature, Location location);
|
||||
Result GetTemperature(out int temperature, Location location);
|
||||
Result SetMeasurementMode(Location location, byte measurementMode);
|
||||
Result GetTemperatureMilliC(out int temperatureMilliC, Location location);
|
||||
Result OpenSession(out ISession session, DeviceCode deviceCode);
|
||||
}
|
||||
}
|
12
src/Ryujinx.Horizon/Sdk/Ts/ISession.cs
Normal file
12
src/Ryujinx.Horizon/Sdk/Ts/ISession.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Ryujinx.Horizon.Common;
|
||||
using Ryujinx.Horizon.Sdk.Sf;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.Ts
|
||||
{
|
||||
interface ISession : IServiceObject
|
||||
{
|
||||
Result GetTemperatureRange(out int minimumTemperature, out int maximumTemperature);
|
||||
Result GetTemperature(out int temperature);
|
||||
Result SetMeasurementMode(byte measurementMode);
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Ptm.Ts.Types
|
||||
namespace Ryujinx.Horizon.Sdk.Ts
|
||||
{
|
||||
enum Location : byte
|
||||
{
|
@@ -11,6 +11,7 @@ using Ryujinx.Horizon.Ngc;
|
||||
using Ryujinx.Horizon.Ovln;
|
||||
using Ryujinx.Horizon.Prepo;
|
||||
using Ryujinx.Horizon.Psc;
|
||||
using Ryujinx.Horizon.Ptm;
|
||||
using Ryujinx.Horizon.Sdk.Arp;
|
||||
using Ryujinx.Horizon.Srepo;
|
||||
using Ryujinx.Horizon.Usb;
|
||||
@@ -54,6 +55,7 @@ namespace Ryujinx.Horizon
|
||||
RegisterService<PrepoMain>();
|
||||
RegisterService<PscMain>();
|
||||
RegisterService<SrepoMain>();
|
||||
RegisterService<TsMain>();
|
||||
RegisterService<UsbMain>();
|
||||
RegisterService<WlanMain>();
|
||||
|
||||
|
@@ -13,7 +13,7 @@ namespace Ryujinx.Memory
|
||||
public sealed class AddressSpaceManager : VirtualMemoryManagerBase, IVirtualMemoryManager
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public bool Supports4KBPages => true;
|
||||
public bool UsesPrivateAllocations => false;
|
||||
|
||||
/// <summary>
|
||||
/// Address space width in bits.
|
||||
|
@@ -8,10 +8,10 @@ namespace Ryujinx.Memory
|
||||
public interface IVirtualMemoryManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates whenever the memory manager supports aliasing pages at 4KB granularity.
|
||||
/// Indicates whether the memory manager creates private allocations when the <see cref="MemoryMapFlags.Private"/> flag is set on map.
|
||||
/// </summary>
|
||||
/// <returns>True if 4KB pages are supported by the memory manager, false otherwise</returns>
|
||||
bool Supports4KBPages { get; }
|
||||
/// <returns>True if private mappings might be used, false otherwise</returns>
|
||||
bool UsesPrivateAllocations { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Maps a virtual memory range into a physical memory range.
|
||||
|
@@ -8,7 +8,7 @@ namespace Ryujinx.Tests.Memory
|
||||
{
|
||||
public class MockVirtualMemoryManager : IVirtualMemoryManager
|
||||
{
|
||||
public bool Supports4KBPages => true;
|
||||
public bool UsesPrivateAllocations => false;
|
||||
|
||||
public bool NoMappings = false;
|
||||
|
||||
|
@@ -777,31 +777,31 @@ namespace Ryujinx.Ava
|
||||
var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value ? MemoryConfiguration.MemoryConfiguration6GiB : MemoryConfiguration.MemoryConfiguration4GiB;
|
||||
|
||||
HLEConfiguration configuration = new(VirtualFileSystem,
|
||||
_viewModel.LibHacHorizonManager,
|
||||
ContentManager,
|
||||
_accountManager,
|
||||
_userChannelPersistence,
|
||||
renderer,
|
||||
InitializeAudio(),
|
||||
memoryConfiguration,
|
||||
_viewModel.UiHandler,
|
||||
(SystemLanguage)ConfigurationState.Instance.System.Language.Value,
|
||||
(RegionCode)ConfigurationState.Instance.System.Region.Value,
|
||||
ConfigurationState.Instance.Graphics.EnableVsync,
|
||||
ConfigurationState.Instance.System.EnableDockedMode,
|
||||
ConfigurationState.Instance.System.EnablePtc,
|
||||
ConfigurationState.Instance.System.EnableInternetAccess,
|
||||
ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
|
||||
ConfigurationState.Instance.System.FsGlobalAccessLogMode,
|
||||
ConfigurationState.Instance.System.SystemTimeOffset,
|
||||
ConfigurationState.Instance.System.TimeZone,
|
||||
ConfigurationState.Instance.System.MemoryManagerMode,
|
||||
ConfigurationState.Instance.System.IgnoreMissingServices,
|
||||
ConfigurationState.Instance.Graphics.AspectRatio,
|
||||
ConfigurationState.Instance.System.AudioVolume,
|
||||
ConfigurationState.Instance.System.UseHypervisor,
|
||||
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value,
|
||||
ConfigurationState.Instance.Multiplayer.Mode);
|
||||
_viewModel.LibHacHorizonManager,
|
||||
ContentManager,
|
||||
_accountManager,
|
||||
_userChannelPersistence,
|
||||
renderer,
|
||||
InitializeAudio(),
|
||||
memoryConfiguration,
|
||||
_viewModel.UiHandler,
|
||||
(SystemLanguage)ConfigurationState.Instance.System.Language.Value,
|
||||
(RegionCode)ConfigurationState.Instance.System.Region.Value,
|
||||
ConfigurationState.Instance.Graphics.EnableVsync,
|
||||
ConfigurationState.Instance.System.EnableDockedMode,
|
||||
ConfigurationState.Instance.System.EnablePtc,
|
||||
ConfigurationState.Instance.System.EnableInternetAccess,
|
||||
ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None,
|
||||
ConfigurationState.Instance.System.FsGlobalAccessLogMode,
|
||||
ConfigurationState.Instance.System.SystemTimeOffset,
|
||||
ConfigurationState.Instance.System.TimeZone,
|
||||
ConfigurationState.Instance.System.MemoryManagerMode,
|
||||
ConfigurationState.Instance.System.IgnoreMissingServices,
|
||||
ConfigurationState.Instance.Graphics.AspectRatio,
|
||||
ConfigurationState.Instance.System.AudioVolume,
|
||||
ConfigurationState.Instance.System.UseHypervisor,
|
||||
ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value,
|
||||
ConfigurationState.Instance.Multiplayer.Mode);
|
||||
|
||||
Device = new Switch(configuration);
|
||||
}
|
||||
|
@@ -597,6 +597,7 @@
|
||||
"UserProfileWindowTitle": "User Profiles Manager",
|
||||
"CheatWindowTitle": "Cheats Manager",
|
||||
"DlcWindowTitle": "Manage Downloadable Content for {0} ({1})",
|
||||
"ModWindowTitle": "Manage Mods for {0} ({1})",
|
||||
"UpdateWindowTitle": "Title Update Manager",
|
||||
"CheatWindowHeading": "Cheats Available for {0} [{1}]",
|
||||
"BuildId": "BuildId:",
|
||||
|
@@ -36,6 +36,7 @@ using SixLabors.ImageSharp.PixelFormats;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -980,7 +981,14 @@ namespace Ryujinx.Ava.UI.ViewModels
|
||||
{
|
||||
if (arg is ApplicationData app)
|
||||
{
|
||||
return string.IsNullOrWhiteSpace(_searchText) || app.TitleName.ToLower().Contains(_searchText.ToLower());
|
||||
if (string.IsNullOrWhiteSpace(_searchText))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
CompareInfo compareInfo = CultureInfo.CurrentCulture.CompareInfo;
|
||||
|
||||
return compareInfo.IndexOf(app.TitleName, _searchText, CompareOptions.IgnoreCase | CompareOptions.IgnoreNonSpace) >= 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@@ -285,7 +285,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
{
|
||||
_deferLoad = false;
|
||||
|
||||
ViewModel.LoadApplication(_launchPath, _startFullscreen).Wait();
|
||||
await ViewModel.LoadApplication(_launchPath, _startFullscreen);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@@ -38,7 +38,7 @@ namespace Ryujinx.Ava.UI.Windows
|
||||
SecondaryButtonText = "",
|
||||
CloseButtonText = "",
|
||||
Content = new ModManagerWindow(titleId),
|
||||
Title = string.Format(LocaleManager.Instance[LocaleKeys.ModWindowHeading], titleName, titleId.ToString("X16")),
|
||||
Title = string.Format(LocaleManager.Instance[LocaleKeys.ModWindowTitle], titleName, titleId.ToString("X16")),
|
||||
};
|
||||
|
||||
Style bottomBorder = new(x => x.OfType<Grid>().Name("DialogSpace").Child().OfType<Border>());
|
||||
|
Reference in New Issue
Block a user