Compare commits

..

6 Commits

Author SHA1 Message Date
TSRBerry
c05c688ee8 Avoid copying more handles than we have space for (#4564)
* Avoid copying more handles than we have space for

* Use locks instead

* Reduce nesting by combining the lock statements

* Add locks for other uses of _sessionHandles and _portHandles

* Use one object to lock instead of locking twice

* Release the lock as soon as possible
2023-03-19 11:30:04 +01:00
riperiperi
b2623dc27d OpenGL: Fix inverted conditional for counter flush from #4471 (#4560)
Fixes OpenGL.
2023-03-18 20:39:05 -03:00
jhorv
5131b71437 Reducing memory allocations (#4537)
* add RecyclableMemoryStream dependency and MemoryStreamManager

* organize BinaryReader/BinaryWriter extensions

* add StreamExtensions to reduce need for BinaryWriter

* simple replacments of MemoryStream with RecyclableMemoryStream

* add write ReadOnlySequence<byte> support to IVirtualMemoryManager

* avoid 0-length array creation

* rework IpcMessage and related types to greatly reduce memory allocation by using RecylableMemoryStream, keeping streams around longer, avoiding their creation when possible, and avoiding creation of BinaryReader and BinaryWriter when possible

* reduce LINQ-induced memory allocations with custom methods to query KPriorityQueue

* use RecyclableMemoryStream in StreamUtils, and use StreamUtils in EmbeddedResources

* add constants for nanosecond/millisecond conversions

* code formatting

* XML doc adjustments

* fix: StreamExtension.WriteByte not writing non-zero values for lengths <= 16

* XML Doc improvements. Implement StreamExtensions.WriteByte() block writes for large-enough count values.

* add copyless path for StreamExtension.Write(ReadOnlySpan<int>)

* add default implementation of IVirtualMemoryManager.Write(ulong, ReadOnlySequence<byte>); remove previous explicit implementations

* code style fixes

* remove LINQ completely from KScheduler/KPriorityQueue by implementing a custom struct-based enumerator
2023-03-17 13:14:50 +01:00
TSRBerry
7870423671 Update syscall capabilites to include SVCs from FW 15.0.0 (#4530)
* Add CapabilityType enum

* Add SupervisorCallCount

* kernel: Add CapabilityExtensions & Change type of capabilities to uint

* Remove private setter from Mask arrays

* Pass ReadOnlySpan directly & Remove redundant type casts
2023-03-17 12:55:19 +01:00
dependabot[bot]
b72916fbc1 nuget: bump UnicornEngine.Unicorn (#4543)
Bumps [UnicornEngine.Unicorn](https://github.com/unicorn-engine/unicorn) from 2.0.2-rc1-f7c841d to 2.0.2-rc1-fb78016.
- [Release notes](https://github.com/unicorn-engine/unicorn/releases)
- [Changelog](https://github.com/unicorn-engine/unicorn/blob/master/ChangeLog)
- [Commits](https://github.com/unicorn-engine/unicorn/commits)

---
updated-dependencies:
- dependency-name: UnicornEngine.Unicorn
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-17 12:50:52 +01:00
riperiperi
da073fce61 GPU: Fast path for adding one texture view to a group (#4528)
* GPU: Fast path for adding one texture view to a group

Texture group handles must store a list of their overlapping views, so they can be properly notified when a write is detected, and a few other things relating to texture readback. This is generally created when the group is established, with each handle looping over all views to find its overlaps. This whole process was also done when only a single view was added (and no handles were changed), however...

Sonic Frontiers had a huge cubemap array with 7350 faces (175 cubemaps * 6 faces * 7 levels), so iterating over both handles and existing views added up very fast. Since we are only adding a single view, we only need to _add_ that view to the existing overlaps, rather than recalculate them all.

This greatly improves performance during loading screens and a few seconds into gameplay on the "open zone" sections of Sonic Frontiers. May improve loading times or stutters on some other games.

Note that the current texture cache rules will cause these views to fall out of the cache, as there are more than the hard cap, so the cost will be repaid when reloading the open zone.

I also added some code to properly remove overlaps when texture views are removed, since it seems that was missing.

This can be improved further by only iterating handles that overlap the view (filter by range), but so can a few places in TextureGroup, so better to do all at once. The full generation of overlaps could probably be improved in a similar way.

I recommend testing a few games to make sure nothing breaks.

* Address feedback
2023-03-14 17:33:44 -03:00
50 changed files with 1056 additions and 506 deletions

View File

@@ -1,6 +1,7 @@
using ARMeilleure.CodeGen.Linking;
using ARMeilleure.CodeGen.RegisterAllocators;
using ARMeilleure.IntermediateRepresentation;
using Ryujinx.Common.Memory;
using System;
using System.Collections.Generic;
using System.IO;
@@ -59,7 +60,7 @@ namespace ARMeilleure.CodeGen.Arm64
public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable)
{
_stream = new MemoryStream();
_stream = MemoryStreamManager.Shared.GetStream();
AllocResult = allocResult;

View File

@@ -1,5 +1,6 @@
using ARMeilleure.CodeGen.Linking;
using ARMeilleure.IntermediateRepresentation;
using Ryujinx.Common.Memory;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -1285,7 +1286,7 @@ namespace ARMeilleure.CodeGen.X86
// Write the code, ignoring the dummy bytes after jumps, into a new stream.
_stream.Seek(0, SeekOrigin.Begin);
using var codeStream = new MemoryStream();
using var codeStream = MemoryStreamManager.Shared.GetStream();
var assembler = new Assembler(codeStream, HasRelocs);
bool hasRelocs = HasRelocs;

View File

@@ -1,5 +1,6 @@
using ARMeilleure.CodeGen.RegisterAllocators;
using ARMeilleure.IntermediateRepresentation;
using Ryujinx.Common.Memory;
using System.IO;
using System.Numerics;
@@ -22,7 +23,7 @@ namespace ARMeilleure.CodeGen.X86
public CodeGenContext(AllocationResult allocResult, int maxCallArgs, int blocksCount, bool relocatable)
{
_stream = new MemoryStream();
_stream = MemoryStreamManager.Shared.GetStream();
_blockLabels = new Operand[blocksCount];
AllocResult = allocResult;

View File

@@ -6,6 +6,7 @@ using ARMeilleure.Memory;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using System;
using System.Buffers.Binary;
using System.Collections.Generic;
@@ -150,10 +151,10 @@ namespace ARMeilleure.Translation.PTC
private void InitializeCarriers()
{
_infosStream = new MemoryStream();
_infosStream = MemoryStreamManager.Shared.GetStream();
_codesList = new List<byte[]>();
_relocsStream = new MemoryStream();
_unwindInfosStream = new MemoryStream();
_relocsStream = MemoryStreamManager.Shared.GetStream();
_unwindInfosStream = MemoryStreamManager.Shared.GetStream();
}
private void DisposeCarriers()

View File

@@ -1,6 +1,7 @@
using ARMeilleure.State;
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using System;
using System.Buffers.Binary;
using System.Collections.Concurrent;
@@ -182,7 +183,7 @@ namespace ARMeilleure.Translation.PTC
return false;
}
using (MemoryStream stream = new MemoryStream())
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
{
Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L);
@@ -274,7 +275,7 @@ namespace ARMeilleure.Translation.PTC
outerHeader.SetHeaderHash();
using (MemoryStream stream = new MemoryStream())
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
{
Debug.Assert(stream.Seek(0L, SeekOrigin.Begin) == 0L && stream.Length == 0L);

View File

@@ -22,6 +22,7 @@
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageVersion Include="Microsoft.IO.RecyclableMemoryStream" Version="2.3.2" />
<PackageVersion Include="MsgPack.Cli" Version="1.0.1" />
<PackageVersion Include="NUnit" Version="3.13.3" />
<PackageVersion Include="NUnit3TestAdapter" Version="4.1.0" />
@@ -46,7 +47,7 @@
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="6.27.0" />
<PackageVersion Include="System.IO.Hashing" Version="7.0.0" />
<PackageVersion Include="System.Management" Version="7.0.0" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-f7c841d" />
<PackageVersion Include="UnicornEngine.Unicorn" Version="2.0.2-rc1-fb78016" />
<PackageVersion Include="XamlNameReferenceGenerator" Version="1.5.1" />
</ItemGroup>
</Project>

View File

@@ -12,19 +12,5 @@ namespace Ryujinx.Common
{
return MemoryMarshal.Cast<byte, T>(reader.ReadBytes(Unsafe.SizeOf<T>()))[0];
}
public unsafe static void WriteStruct<T>(this BinaryWriter writer, T value)
where T : unmanaged
{
ReadOnlySpan<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateReadOnlySpan(ref value, 1));
writer.Write(data);
}
public static void Write(this BinaryWriter writer, UInt128 value)
{
writer.Write((ulong)value);
writer.Write((ulong)(value >> 64));
}
}
}

View File

@@ -0,0 +1,28 @@
using System;
using System.IO;
using System.Runtime.InteropServices;
namespace Ryujinx.Common
{
public static class BinaryWriterExtensions
{
public unsafe static void WriteStruct<T>(this BinaryWriter writer, T value)
where T : unmanaged
{
ReadOnlySpan<byte> data = MemoryMarshal.Cast<T, byte>(MemoryMarshal.CreateReadOnlySpan(ref value, 1));
writer.Write(data);
}
public static void Write(this BinaryWriter writer, UInt128 value)
{
writer.Write((ulong)value);
writer.Write((ulong)(value >> 64));
}
public static void Write(this BinaryWriter writer, MemoryStream stream)
{
stream.CopyTo(writer.BaseStream);
}
}
}

View File

@@ -0,0 +1,138 @@
using System;
using System.Buffers.Binary;
using System.IO;
using System.Runtime.InteropServices;
namespace Ryujinx.Common
{
public static class StreamExtensions
{
/// <summary>
/// Writes a <cref="ReadOnlySpan<int>" /> to this stream.
///
/// This default implementation converts each buffer value to a stack-allocated
/// byte array, then writes it to the Stream using <cref="System.Stream.Write(byte[])" />.
/// </summary>
/// <param name="stream">The stream to be written to</param>
/// <param name="buffer">The buffer of values to be written</param>
public static void Write(this Stream stream, ReadOnlySpan<int> buffer)
{
if (buffer.Length == 0)
{
return;
}
if (BitConverter.IsLittleEndian)
{
ReadOnlySpan<byte> byteBuffer = MemoryMarshal.Cast<int, byte>(buffer);
stream.Write(byteBuffer);
}
else
{
Span<byte> byteBuffer = stackalloc byte[sizeof(int)];
foreach (int value in buffer)
{
BinaryPrimitives.WriteInt32LittleEndian(byteBuffer, value);
stream.Write(byteBuffer);
}
}
}
/// <summary>
/// Writes a four-byte signed integer to this stream. The current position
/// of the stream is advanced by four.
/// </summary>
/// <param name="stream">The stream to be written to</param>
/// <param name="value">The value to be written</param>
public static void Write(this Stream stream, int value)
{
Span<byte> buffer = stackalloc byte[sizeof(int)];
BinaryPrimitives.WriteInt32LittleEndian(buffer, value);
stream.Write(buffer);
}
/// <summary>
/// Writes an eight-byte signed integer to this stream. The current position
/// of the stream is advanced by eight.
/// </summary>
/// <param name="stream">The stream to be written to</param>
/// <param name="value">The value to be written</param>
public static void Write(this Stream stream, long value)
{
Span<byte> buffer = stackalloc byte[sizeof(long)];
BinaryPrimitives.WriteInt64LittleEndian(buffer, value);
stream.Write(buffer);
}
/// <summary>
// Writes a four-byte unsigned integer to this stream. The current position
// of the stream is advanced by four.
/// </summary>
/// <param name="stream">The stream to be written to</param>
/// <param name="value">The value to be written</param>
public static void Write(this Stream stream, uint value)
{
Span<byte> buffer = stackalloc byte[sizeof(uint)];
BinaryPrimitives.WriteUInt32LittleEndian(buffer, value);
stream.Write(buffer);
}
/// <summary>
/// Writes an eight-byte unsigned integer to this stream. The current
/// position of the stream is advanced by eight.
/// </summary>
/// <param name="stream">The stream to be written to</param>
/// <param name="value">The value to be written</param>
public static void Write(this Stream stream, ulong value)
{
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
BinaryPrimitives.WriteUInt64LittleEndian(buffer, value);
stream.Write(buffer);
}
/// <summary>
/// Writes the contents of source to stream by calling source.CopyTo(stream).
/// Provides consistency with other Stream.Write methods.
/// </summary>
/// <param name="stream">The stream to be written to</param>
/// <param name="source">The stream to be read from</param>
public static void Write(this Stream stream, Stream source)
{
source.CopyTo(stream);
}
/// <summary>
/// Writes a sequence of bytes to the Stream.
/// </summary>
/// <param name="stream">The stream to be written to.</param>
/// <param name="value">The byte to be written</param>
/// <param name="count">The number of times the value should be written</param>
public static void WriteByte(this Stream stream, byte value, int count)
{
if (count <= 0)
{
return;
}
const int BlockSize = 16;
int blockCount = count / BlockSize;
if (blockCount > 0)
{
Span<byte> span = stackalloc byte[BlockSize];
span.Fill(value);
for (int x = 0; x < blockCount; x++)
{
stream.Write(span);
}
}
int nonBlockBytes = count % BlockSize;
for (int x = 0; x < nonBlockBytes; x++)
{
stream.WriteByte(value);
}
}
}
}

View File

@@ -0,0 +1,99 @@
using Microsoft.IO;
using System;
namespace Ryujinx.Common.Memory
{
public static class MemoryStreamManager
{
private static readonly RecyclableMemoryStreamManager _shared = new RecyclableMemoryStreamManager();
/// <summary>
/// We don't expose the <c>RecyclableMemoryStreamManager</c> directly because version 2.x
/// returns them as <c>MemoryStream</c>. This Shared class is here to a) offer only the GetStream() versions we use
/// and b) return them as <c>RecyclableMemoryStream</c> so we don't have to cast.
/// </summary>
public static class Shared
{
/// <summary>
/// Retrieve a new <c>MemoryStream</c> object with no tag and a default initial capacity.
/// </summary>
/// <returns>A <c>RecyclableMemoryStream</c></returns>
public static RecyclableMemoryStream GetStream()
=> new RecyclableMemoryStream(_shared);
/// <summary>
/// Retrieve a new <c>MemoryStream</c> object with the contents copied from the provided
/// buffer. The provided buffer is not wrapped or used after construction.
/// </summary>
/// <remarks>The new stream's position is set to the beginning of the stream when returned.</remarks>
/// <param name="buffer">The byte buffer to copy data from</param>
/// <returns>A <c>RecyclableMemoryStream</c></returns>
public static RecyclableMemoryStream GetStream(byte[] buffer)
=> GetStream(Guid.NewGuid(), null, buffer, 0, buffer.Length);
/// <summary>
/// Retrieve a new <c>MemoryStream</c> object with the given tag and with contents copied from the provided
/// buffer. The provided buffer is not wrapped or used after construction.
/// </summary>
/// <remarks>The new stream's position is set to the beginning of the stream when returned.</remarks>
/// <param name="buffer">The byte buffer to copy data from</param>
/// <returns>A <c>RecyclableMemoryStream</c></returns>
public static RecyclableMemoryStream GetStream(ReadOnlySpan<byte> buffer)
=> GetStream(Guid.NewGuid(), null, buffer);
/// <summary>
/// Retrieve a new <c>RecyclableMemoryStream</c> object with the given tag and with contents copied from the provided
/// buffer. The provided buffer is not wrapped or used after construction.
/// </summary>
/// <remarks>The new stream's position is set to the beginning of the stream when returned.</remarks>
/// <param name="id">A unique identifier which can be used to trace usages of the stream</param>
/// <param name="tag">A tag which can be used to track the source of the stream</param>
/// <param name="buffer">The byte buffer to copy data from</param>
/// <returns>A <c>RecyclableMemoryStream</c></returns>
public static RecyclableMemoryStream GetStream(Guid id, string tag, ReadOnlySpan<byte> buffer)
{
RecyclableMemoryStream stream = null;
try
{
stream = new RecyclableMemoryStream(_shared, id, tag, buffer.Length);
stream.Write(buffer);
stream.Position = 0;
return stream;
}
catch
{
stream?.Dispose();
throw;
}
}
/// <summary>
/// Retrieve a new <c>RecyclableMemoryStream</c> object with the given tag and with contents copied from the provided
/// buffer. The provided buffer is not wrapped or used after construction.
/// </summary>
/// <remarks>The new stream's position is set to the beginning of the stream when returned</remarks>
/// <param name="id">A unique identifier which can be used to trace usages of the stream</param>
/// <param name="tag">A tag which can be used to track the source of the stream</param>
/// <param name="buffer">The byte buffer to copy data from</param>
/// <param name="offset">The offset from the start of the buffer to copy from</param>
/// <param name="count">The number of bytes to copy from the buffer</param>
/// <returns>A <c>RecyclableMemoryStream</c></returns>
public static RecyclableMemoryStream GetStream(Guid id, string tag, byte[] buffer, int offset, int count)
{
RecyclableMemoryStream stream = null;
try
{
stream = new RecyclableMemoryStream(_shared, id, tag, count);
stream.Write(buffer, offset, count);
stream.Position = 0;
return stream;
}
catch
{
stream?.Dispose();
throw;
}
}
}
}
}

View File

@@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" />
<PackageReference Include="MsgPack.Cli" />
<PackageReference Include="System.Management" />
</ItemGroup>

View File

@@ -1,3 +1,5 @@
using Ryujinx.Common.Memory;
using Ryujinx.Common.Utilities;
using System;
using System.IO;
using System.Linq;
@@ -38,12 +40,7 @@ namespace Ryujinx.Common
return null;
}
using (var mem = new MemoryStream())
{
stream.CopyTo(mem);
return mem.ToArray();
}
return StreamUtils.StreamToBytes(stream);
}
}
@@ -56,12 +53,7 @@ namespace Ryujinx.Common
return null;
}
using (var mem = new MemoryStream())
{
await stream.CopyToAsync(mem);
return mem.ToArray();
}
return await StreamUtils.StreamToBytesAsync(stream);
}
}

View File

@@ -1,4 +1,8 @@
using System.IO;
using Microsoft.IO;
using Ryujinx.Common.Memory;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace Ryujinx.Common.Utilities
{
@@ -6,12 +10,22 @@ namespace Ryujinx.Common.Utilities
{
public static byte[] StreamToBytes(Stream input)
{
using (MemoryStream stream = new MemoryStream())
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
{
input.CopyTo(stream);
return stream.ToArray();
}
}
public static async Task<byte[]> StreamToBytesAsync(Stream input, CancellationToken cancellationToken = default)
{
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
{
await input.CopyToAsync(stream, cancellationToken);
return stream.ToArray();
}
}
}
}

View File

@@ -1,4 +1,6 @@
using Ryujinx.Memory;
using Microsoft.IO;
using Ryujinx.Common.Memory;
using Ryujinx.Memory;
using System;
using System.IO;
using System.Runtime.CompilerServices;
@@ -40,7 +42,7 @@ namespace Ryujinx.Cpu
public static string ReadAsciiString(IVirtualMemoryManager memory, ulong position, long maxSize = -1)
{
using (MemoryStream ms = new MemoryStream())
using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream())
{
for (long offs = 0; offs < maxSize || maxSize == -1; offs++)
{
@@ -54,7 +56,7 @@ namespace Ryujinx.Cpu
ms.WriteByte(value);
}
return Encoding.ASCII.GetString(ms.ToArray());
return Encoding.ASCII.GetString(ms.GetReadOnlySequence());
}
}
}

View File

@@ -360,7 +360,7 @@ namespace Ryujinx.Graphics.Gpu.Image
texture._viewStorage = this;
Group.UpdateViews(_views);
Group.UpdateViews(_views, texture);
if (texture.Group != null && texture.Group != Group)
{
@@ -384,6 +384,8 @@ namespace Ryujinx.Graphics.Gpu.Image
{
_views.Remove(texture);
Group.RemoveView(texture);
texture._viewStorage = texture;
DecrementReferenceCount();

View File

@@ -989,7 +989,8 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Update the views in this texture group, rebuilding the memory tracking if required.
/// </summary>
/// <param name="views">The views list of the storage texture</param>
public void UpdateViews(List<Texture> views)
/// <param name="texture">The texture that has been added, if that is the only change, otherwise null</param>
public void UpdateViews(List<Texture> views, Texture texture)
{
// This is saved to calculate overlapping views for each handle.
_views = views;
@@ -1026,6 +1027,17 @@ namespace Ryujinx.Graphics.Gpu.Image
}
if (!regionsRebuilt)
{
if (texture != null)
{
int offset = FindOffset(texture);
foreach (TextureGroupHandle handle in _handles)
{
handle.AddOverlap(offset, texture);
}
}
else
{
// Must update the overlapping views on all handles, but only if they were not just recreated.
@@ -1034,10 +1046,26 @@ namespace Ryujinx.Graphics.Gpu.Image
handle.RecalculateOverlaps(this, views);
}
}
}
SignalAllDirty();
}
/// <summary>
/// Removes a view from the group, removing it from all overlap lists.
/// </summary>
/// <param name="view">View to remove from the group</param>
public void RemoveView(Texture view)
{
int offset = FindOffset(view);
foreach (TextureGroupHandle handle in _handles)
{
handle.RemoveOverlap(offset, view);
}
}
/// <summary>
/// Inherit handle state from an old set of handles, such as modified and dirty flags.
/// </summary>

View File

@@ -159,6 +159,42 @@ namespace Ryujinx.Graphics.Gpu.Image
}
}
/// <summary>
/// Adds a single texture view as an overlap if its range overlaps.
/// </summary>
/// <param name="offset">The offset of the view in the group</param>
/// <param name="view">The texture to add as an overlap</param>
public void AddOverlap(int offset, Texture view)
{
// Overlaps can be accessed from the memory tracking signal handler, so access must be atomic.
if (OverlapsWith(offset, (int)view.Size))
{
lock (Overlaps)
{
Overlaps.Add(view);
}
}
}
/// <summary>
/// Removes a single texture view as an overlap if its range overlaps.
/// </summary>
/// <param name="offset">The offset of the view in the group</param>
/// <param name="view">The texture to add as an overlap</param>
public void RemoveOverlap(int offset, Texture view)
{
// Overlaps can be accessed from the memory tracking signal handler, so access must be atomic.
if (OverlapsWith(offset, (int)view.Size))
{
lock (Overlaps)
{
Overlaps.Remove(view);
}
}
}
/// <summary>
/// Registers a sync action to happen for this handle, and an interim flush action on the tracking handle.
/// </summary>

View File

@@ -1,3 +1,5 @@
using Ryujinx.Common;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Shader;
using Ryujinx.Graphics.Shader.Translation;
@@ -11,16 +13,15 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
{
public static byte[] Pack(ShaderSource[] sources)
{
using MemoryStream output = new MemoryStream();
using BinaryWriter writer = new BinaryWriter(output);
using MemoryStream output = MemoryStreamManager.Shared.GetStream();
writer.Write(sources.Length);
output.Write(sources.Length);
for (int i = 0; i < sources.Length; i++)
foreach (ShaderSource source in sources)
{
writer.Write((int)sources[i].Stage);
writer.Write(sources[i].BinaryCode.Length);
writer.Write(sources[i].BinaryCode);
output.Write((int)source.Stage);
output.Write(source.BinaryCode.Length);
output.Write(source.BinaryCode);
}
return output.ToArray();

View File

@@ -74,7 +74,7 @@ namespace Ryujinx.Graphics.OpenGL.Queries
{
result = Marshal.ReadInt64(_bufferMap);
return WaitingForValue(result);
return !WaitingForValue(result);
}
public long AwaitResult(AutoResetEvent wakeSignal = null)

View File

@@ -9,6 +9,7 @@ using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using LibHac.Tools.Ncm;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.HOS.Services.Ssl;
@@ -637,12 +638,12 @@ namespace Ryujinx.HLE.FileSystem
private Stream GetZipStream(ZipArchiveEntry entry)
{
MemoryStream dest = new MemoryStream();
Stream src = entry.Open();
MemoryStream dest = MemoryStreamManager.Shared.GetStream();
using (Stream src = entry.Open())
{
src.CopyTo(dest);
src.Dispose();
}
return dest;
}

View File

@@ -1,5 +1,6 @@
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
using System;
using System.Collections.Generic;
@@ -70,7 +71,7 @@ namespace Ryujinx.HLE.HOS.Applets.Browser
private byte[] BuildResponseOld(WebCommonReturnValue result)
{
using (MemoryStream stream = new MemoryStream())
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.WriteStruct(result);
@@ -80,7 +81,7 @@ namespace Ryujinx.HLE.HOS.Applets.Browser
}
private byte[] BuildResponseNew(List<BrowserOutput> outputArguments)
{
using (MemoryStream stream = new MemoryStream())
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.WriteStruct(new WebArgHeader

View File

@@ -1,4 +1,5 @@
using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
using Ryujinx.HLE.HOS.Services.Hid;
using Ryujinx.HLE.HOS.Services.Hid.Types;
@@ -123,7 +124,7 @@ namespace Ryujinx.HLE.HOS.Applets
private byte[] BuildResponse(ControllerSupportResultInfo result)
{
using (MemoryStream stream = new MemoryStream())
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.Write(MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref result, Unsafe.SizeOf<ControllerSupportResultInfo>())));
@@ -134,7 +135,7 @@ namespace Ryujinx.HLE.HOS.Applets
private byte[] BuildResponse()
{
using (MemoryStream stream = new MemoryStream())
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.Write((ulong)ResultCode.Success);

View File

@@ -1,4 +1,5 @@
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
using System;
using System.IO;
@@ -43,7 +44,7 @@ namespace Ryujinx.HLE.HOS.Applets
{
UserProfile currentUser = _system.AccountManager.LastOpenedUser;
using (MemoryStream stream = new MemoryStream())
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.Write((ulong)PlayerSelectResult.Success);

View File

@@ -338,7 +338,7 @@ namespace Ryujinx.HLE.HOS
ProcessCreationInfo creationInfo = new ProcessCreationInfo("Service", 1, 0, 0x8000000, 1, flags, 0, 0);
int[] defaultCapabilities = new int[]
uint[] defaultCapabilities = new uint[]
{
0x030363F7,
0x1FFFFFCF,

View File

@@ -1,3 +1,6 @@
using Microsoft.IO;
using Ryujinx.Common;
using Ryujinx.Common.Memory;
using System;
using System.IO;
@@ -18,20 +21,27 @@ namespace Ryujinx.HLE.HOS.Ipc
HasPId = (word & 1) != 0;
ToCopy = new int[(word >> 1) & 0xf];
ToMove = new int[(word >> 5) & 0xf];
PId = HasPId ? reader.ReadUInt64() : 0;
for (int index = 0; index < ToCopy.Length; index++)
int toCopySize = (word >> 1) & 0xf;
int[] toCopy = toCopySize == 0 ? Array.Empty<int>() : new int[toCopySize];
for (int index = 0; index < toCopy.Length; index++)
{
ToCopy[index] = reader.ReadInt32();
toCopy[index] = reader.ReadInt32();
}
for (int index = 0; index < ToMove.Length; index++)
ToCopy = toCopy;
int toMoveSize = (word >> 5) & 0xf;
int[] toMove = toMoveSize == 0 ? Array.Empty<int>() : new int[toMoveSize];
for (int index = 0; index < toMove.Length; index++)
{
ToMove[index] = reader.ReadInt32();
toMove[index] = reader.ReadInt32();
}
ToMove = toMove;
}
public IpcHandleDesc(int[] copy, int[] move)
@@ -57,36 +67,27 @@ namespace Ryujinx.HLE.HOS.Ipc
return new IpcHandleDesc(Array.Empty<int>(), handles);
}
public byte[] GetBytes()
public RecyclableMemoryStream GetStream()
{
using (MemoryStream ms = new MemoryStream())
{
BinaryWriter writer = new BinaryWriter(ms);
RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream();
int word = HasPId ? 1 : 0;
word |= (ToCopy.Length & 0xf) << 1;
word |= (ToMove.Length & 0xf) << 5;
writer.Write(word);
ms.Write(word);
if (HasPId)
{
writer.Write(PId);
ms.Write(PId);
}
foreach (int handle in ToCopy)
{
writer.Write(handle);
}
ms.Write(ToCopy);
ms.Write(ToMove);
foreach (int handle in ToMove)
{
writer.Write(handle);
}
return ms.ToArray();
}
ms.Position = 0;
return ms;
}
}
}

View File

@@ -1,4 +1,8 @@
using Microsoft.IO;
using Ryujinx.Common;
using Ryujinx.Common.Memory;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
@@ -32,9 +36,9 @@ namespace Ryujinx.HLE.HOS.Ipc
ObjectIds = new List<int>();
}
public IpcMessage(byte[] data, long cmdPtr) : this()
public IpcMessage(ReadOnlySpan<byte> data, long cmdPtr) : this()
{
using (MemoryStream ms = new MemoryStream(data))
using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(data))
{
BinaryReader reader = new BinaryReader(ms);
@@ -114,15 +118,13 @@ namespace Ryujinx.HLE.HOS.Ipc
for (int index = 0; index < recvListCount; index++)
{
RecvListBuff.Add(new IpcRecvListBuffDesc(reader));
RecvListBuff.Add(new IpcRecvListBuffDesc(reader.ReadUInt64()));
}
}
public byte[] GetBytes(long cmdPtr, ulong recvListAddr)
public RecyclableMemoryStream GetStream(long cmdPtr, ulong recvListAddr)
{
using (MemoryStream ms = new MemoryStream())
{
BinaryWriter writer = new BinaryWriter(ms);
RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream();
int word0;
int word1;
@@ -133,12 +135,7 @@ namespace Ryujinx.HLE.HOS.Ipc
word0 |= (ReceiveBuff.Count & 0xf) << 24;
word0 |= (ExchangeBuff.Count & 0xf) << 28;
byte[] handleData = Array.Empty<byte>();
if (HandleDesc != null)
{
handleData = HandleDesc.GetBytes();
}
using RecyclableMemoryStream handleDataStream = HandleDesc?.GetStream();
int dataLength = RawData?.Length ?? 0;
@@ -146,7 +143,7 @@ namespace Ryujinx.HLE.HOS.Ipc
int rawLength = dataLength;
int pad0 = (int)GetPadSize16(cmdPtr + 8 + handleData.Length + PtrBuff.Count * 8);
int pad0 = (int)GetPadSize16(cmdPtr + 8 + (handleDataStream?.Length ?? 0) + PtrBuff.Count * 8);
// Apparently, padding after Raw Data is 16 bytes, however when there is
// padding before Raw Data too, we need to subtract the size of this padding.
@@ -162,38 +159,42 @@ namespace Ryujinx.HLE.HOS.Ipc
word1 |= 1 << 31;
}
writer.Write(word0);
writer.Write(word1);
writer.Write(handleData);
ms.Write(word0);
ms.Write(word1);
for (int index = 0; index < PtrBuff.Count; index++)
if (handleDataStream != null)
{
writer.Write(PtrBuff[index].GetWord0());
writer.Write(PtrBuff[index].GetWord1());
ms.Write(handleDataStream);
}
ms.Seek(pad0, SeekOrigin.Current);
foreach (IpcPtrBuffDesc ptrBuffDesc in PtrBuff)
{
ms.Write(ptrBuffDesc.GetWord0());
ms.Write(ptrBuffDesc.GetWord1());
}
ms.WriteByte(0, pad0);
if (RawData != null)
{
writer.Write(RawData);
ms.Seek(rawLength - RawData.Length, SeekOrigin.Current);
ms.Write(RawData);
ms.WriteByte(0, rawLength - RawData.Length);
}
writer.Write(new byte[pad1]);
writer.Write(recvListAddr);
ms.WriteByte(0, pad1);
return ms.ToArray();
}
ms.Write(recvListAddr);
ms.Position = 0;
return ms;
}
public byte[] GetBytesTipc()
public RecyclableMemoryStream GetStreamTipc()
{
Debug.Assert(PtrBuff.Count == 0);
using (MemoryStream ms = new MemoryStream())
{
BinaryWriter writer = new BinaryWriter(ms);
RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream();
int word0;
int word1;
@@ -203,12 +204,7 @@ namespace Ryujinx.HLE.HOS.Ipc
word0 |= (ReceiveBuff.Count & 0xf) << 24;
word0 |= (ExchangeBuff.Count & 0xf) << 28;
byte[] handleData = Array.Empty<byte>();
if (HandleDesc != null)
{
handleData = HandleDesc.GetBytes();
}
using RecyclableMemoryStream handleDataStream = HandleDesc?.GetStream();
int dataLength = RawData?.Length ?? 0;
@@ -221,17 +217,20 @@ namespace Ryujinx.HLE.HOS.Ipc
word1 |= 1 << 31;
}
writer.Write(word0);
writer.Write(word1);
writer.Write(handleData);
ms.Write(word0);
ms.Write(word1);
if (handleDataStream != null)
{
ms.Write(handleDataStream);
}
if (RawData != null)
{
writer.Write(RawData);
ms.Write(RawData);
}
return ms.ToArray();
}
return ms;
}
private long GetPadSize16(long position)

View File

@@ -13,13 +13,11 @@ namespace Ryujinx.HLE.HOS.Ipc
Size = size;
}
public IpcRecvListBuffDesc(BinaryReader reader)
public IpcRecvListBuffDesc(ulong packedValue)
{
ulong value = reader.ReadUInt64();
Position = packedValue & 0xffffffffffff;
Position = value & 0xffffffffffff;
Size = (ushort)(value >> 48);
Size = (ushort)(packedValue >> 48);
}
}
}

View File

@@ -27,6 +27,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
private bool _keepRunning;
private long _enforceWakeupFromSpinWait;
private const long NanosecondsPerSecond = 1000000000L;
private const long NanosecondsPerMillisecond = 1000000L;
public KTimeManager(KernelContext context)
{
_context = context;
@@ -55,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
{
_waitingObjects.Add(new WaitingObject(schedulerObj, timePoint));
if (timeout < 1000000)
if (timeout < NanosecondsPerMillisecond)
{
Interlocked.Exchange(ref _enforceWakeupFromSpinWait, 1);
}
@@ -161,7 +164,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
public static long ConvertNanosecondsToMilliseconds(long time)
{
time /= 1000000;
time /= NanosecondsPerMillisecond;
if ((ulong)time > int.MaxValue)
{
@@ -173,18 +176,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
public static long ConvertMillisecondsToNanoseconds(long time)
{
return time * 1000000;
return time * NanosecondsPerMillisecond;
}
public static long ConvertNanosecondsToHostTicks(long ns)
{
long nsDiv = ns / 1000000000;
long nsMod = ns % 1000000000;
long tickDiv = PerformanceCounter.TicksPerSecond / 1000000000;
long tickMod = PerformanceCounter.TicksPerSecond % 1000000000;
long nsDiv = ns / NanosecondsPerSecond;
long nsMod = ns % NanosecondsPerSecond;
long tickDiv = PerformanceCounter.TicksPerSecond / NanosecondsPerSecond;
long tickMod = PerformanceCounter.TicksPerSecond % NanosecondsPerSecond;
long baseTicks = (nsMod * tickMod + PerformanceCounter.TicksPerSecond - 1) / 1000000000;
return (nsDiv * tickDiv) * 1000000000 + nsDiv * tickMod + nsMod * tickDiv + baseTicks;
long baseTicks = (nsMod * tickMod + PerformanceCounter.TicksPerSecond - 1) / NanosecondsPerSecond;
return (nsDiv * tickDiv) * NanosecondsPerSecond + nsDiv * tickMod + nsMod * tickDiv + baseTicks;
}
public static long ConvertGuestTicksToNanoseconds(long ticks)

View File

@@ -7,6 +7,8 @@ namespace Ryujinx.HLE.HOS.Kernel
public const int InitialKipId = 1;
public const int InitialProcessId = 0x51;
public const int SupervisorCallCount = 0xC0;
public const int MemoryBlockAllocatorSize = 0x2710;
public const ulong UserSlabHeapBase = DramMemoryMap.SlabHeapBase;

View File

@@ -18,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Kernel
public static Result StartInitialProcess(
KernelContext context,
ProcessCreationInfo creationInfo,
ReadOnlySpan<int> capabilities,
ReadOnlySpan<uint> capabilities,
int mainThreadPriority,
ThreadStart customThreadStart)
{

View File

@@ -0,0 +1,22 @@
using System.Numerics;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
static class CapabilityExtensions
{
public static CapabilityType GetCapabilityType(this uint cap)
{
return (CapabilityType)(((cap + 1) & ~cap) - 1);
}
public static uint GetFlag(this CapabilityType type)
{
return (uint)type + 1;
}
public static uint GetId(this CapabilityType type)
{
return (uint)BitOperations.TrailingZeroCount(type.GetFlag());
}
}
}

View File

@@ -0,0 +1,19 @@
namespace Ryujinx.HLE.HOS.Kernel.Process
{
enum CapabilityType : uint
{
CorePriority = (1u << 3) - 1,
SyscallMask = (1u << 4) - 1,
MapRange = (1u << 6) - 1,
MapIoPage = (1u << 7) - 1,
MapRegion = (1u << 10) - 1,
InterruptPair = (1u << 11) - 1,
ProgramType = (1u << 13) - 1,
KernelVersion = (1u << 14) - 1,
HandleTable = (1u << 15) - 1,
DebugFlags = (1u << 16) - 1,
Invalid = 0u,
Padding = ~0u
}
}

View File

@@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
private int _activeSlotsCount;
private int _size;
private uint _size;
private ushort _idCounter;
@@ -28,9 +28,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
_context = context;
}
public Result Initialize(int size)
public Result Initialize(uint size)
{
if ((uint)size > 1024)
if (size > 1024)
{
return KernelResult.OutOfMemory;
}

View File

@@ -16,11 +16,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
class KProcess : KSynchronizationObject
{
public const int KernelVersionMajor = 10;
public const int KernelVersionMinor = 4;
public const int KernelVersionRevision = 0;
public const uint KernelVersionMajor = 10;
public const uint KernelVersionMinor = 4;
public const uint KernelVersionRevision = 0;
public const int KernelVersionPacked =
public const uint KernelVersionPacked =
(KernelVersionMajor << 19) |
(KernelVersionMinor << 15) |
(KernelVersionRevision << 0);
@@ -119,7 +119,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
public Result InitializeKip(
ProcessCreationInfo creationInfo,
ReadOnlySpan<int> capabilities,
ReadOnlySpan<uint> capabilities,
KPageList pageList,
KResourceLimit resourceLimit,
MemoryRegion memRegion,
@@ -190,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
public Result Initialize(
ProcessCreationInfo creationInfo,
ReadOnlySpan<int> capabilities,
ReadOnlySpan<uint> capabilities,
KResourceLimit resourceLimit,
MemoryRegion memRegion,
IProcessContextFactory contextFactory,

View File

@@ -1,4 +1,3 @@
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Horizon.Common;
@@ -9,48 +8,49 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
{
class KProcessCapabilities
{
public byte[] SvcAccessMask { get; private set; }
public byte[] IrqAccessMask { get; private set; }
public byte[] SvcAccessMask { get; }
public byte[] IrqAccessMask { get; }
public ulong AllowedCpuCoresMask { get; private set; }
public ulong AllowedThreadPriosMask { get; private set; }
public int DebuggingFlags { get; private set; }
public int HandleTableSize { get; private set; }
public int KernelReleaseVersion { get; private set; }
public int ApplicationType { get; private set; }
public uint DebuggingFlags { get; private set; }
public uint HandleTableSize { get; private set; }
public uint KernelReleaseVersion { get; private set; }
public uint ApplicationType { get; private set; }
public KProcessCapabilities()
{
SvcAccessMask = new byte[0x10];
// length / number of bits of the underlying type
SvcAccessMask = new byte[KernelConstants.SupervisorCallCount / 8];
IrqAccessMask = new byte[0x80];
}
public Result InitializeForKernel(ReadOnlySpan<int> capabilities, KPageTableBase memoryManager)
public Result InitializeForKernel(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager)
{
AllowedCpuCoresMask = 0xf;
AllowedThreadPriosMask = ulong.MaxValue;
DebuggingFlags &= ~3;
DebuggingFlags &= ~3u;
KernelReleaseVersion = KProcess.KernelVersionPacked;
return Parse(capabilities, memoryManager);
}
public Result InitializeForUser(ReadOnlySpan<int> capabilities, KPageTableBase memoryManager)
public Result InitializeForUser(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager)
{
return Parse(capabilities, memoryManager);
}
private Result Parse(ReadOnlySpan<int> capabilities, KPageTableBase memoryManager)
private Result Parse(ReadOnlySpan<uint> capabilities, KPageTableBase memoryManager)
{
int mask0 = 0;
int mask1 = 0;
for (int index = 0; index < capabilities.Length; index++)
{
int cap = capabilities[index];
uint cap = capabilities[index];
if (((cap + 1) & ~cap) != 0x40)
if (cap.GetCapabilityType() != CapabilityType.MapRange)
{
Result result = ParseCapability(cap, ref mask0, ref mask1, memoryManager);
@@ -66,7 +66,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return KernelResult.InvalidCombination;
}
int prevCap = cap;
uint prevCap = cap;
cap = capabilities[++index];
@@ -85,8 +85,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return KernelResult.InvalidSize;
}
long address = ((long)(uint)prevCap << 5) & 0xffffff000;
long size = ((long)(uint)cap << 5) & 0xfffff000;
long address = ((long)prevCap << 5) & 0xffffff000;
long size = ((long)cap << 5) & 0xfffff000;
if (((ulong)(address + size - 1) >> 36) != 0)
{
@@ -118,20 +118,20 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return Result.Success;
}
private Result ParseCapability(int cap, ref int mask0, ref int mask1, KPageTableBase memoryManager)
private Result ParseCapability(uint cap, ref int mask0, ref int mask1, KPageTableBase memoryManager)
{
int code = (cap + 1) & ~cap;
CapabilityType code = cap.GetCapabilityType();
if (code == 1)
if (code == CapabilityType.Invalid)
{
return KernelResult.InvalidCapability;
}
else if (code == 0)
else if (code == CapabilityType.Padding)
{
return Result.Success;
}
int codeMask = 1 << (32 - BitOperations.LeadingZeroCount((uint)code + 1));
int codeMask = 1 << (32 - BitOperations.LeadingZeroCount(code.GetFlag() + 1));
// Check if the property was already set.
if (((mask0 & codeMask) & 0x1e008) != 0)
@@ -143,23 +143,23 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
switch (code)
{
case 8:
case CapabilityType.CorePriority:
{
if (AllowedCpuCoresMask != 0 || AllowedThreadPriosMask != 0)
{
return KernelResult.InvalidCapability;
}
int lowestCpuCore = (cap >> 16) & 0xff;
int highestCpuCore = (cap >> 24) & 0xff;
uint lowestCpuCore = (cap >> 16) & 0xff;
uint highestCpuCore = (cap >> 24) & 0xff;
if (lowestCpuCore > highestCpuCore)
{
return KernelResult.InvalidCombination;
}
int highestThreadPrio = (cap >> 4) & 0x3f;
int lowestThreadPrio = (cap >> 10) & 0x3f;
uint highestThreadPrio = (cap >> 4) & 0x3f;
uint lowestThreadPrio = (cap >> 10) & 0x3f;
if (lowestThreadPrio > highestThreadPrio)
{
@@ -177,9 +177,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
break;
}
case 0x10:
case CapabilityType.SyscallMask:
{
int slot = (cap >> 29) & 7;
int slot = ((int)cap >> 29) & 7;
int svcSlotMask = 1 << slot;
@@ -190,7 +190,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
mask1 |= svcSlotMask;
int svcMask = (cap >> 5) & 0xffffff;
uint svcMask = (cap >> 5) & 0xffffff;
int baseSvc = slot * 24;
@@ -203,7 +203,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
int svcId = baseSvc + index;
if (svcId > 0x7f)
if (svcId >= KernelConstants.SupervisorCallCount)
{
return KernelResult.MaximumExceeded;
}
@@ -214,20 +214,27 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
break;
}
case 0x80:
case CapabilityType.MapIoPage:
{
long address = ((long)(uint)cap << 4) & 0xffffff000;
long address = ((long)cap << 4) & 0xffffff000;
memoryManager.MapIoMemory(address, KPageTableBase.PageSize, KMemoryPermission.ReadAndWrite);
break;
}
case 0x800:
case CapabilityType.MapRegion:
{
// TODO: Implement capabilities for MapRegion
break;
}
case CapabilityType.InterruptPair:
{
// TODO: GIC distributor check.
int irq0 = (cap >> 12) & 0x3ff;
int irq1 = (cap >> 22) & 0x3ff;
int irq0 = ((int)cap >> 12) & 0x3ff;
int irq1 = ((int)cap >> 22) & 0x3ff;
if (irq0 != 0x3ff)
{
@@ -242,11 +249,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
break;
}
case 0x2000:
case CapabilityType.ProgramType:
{
int applicationType = cap >> 14;
uint applicationType = (cap >> 14);
if ((uint)applicationType > 7)
if (applicationType > 7)
{
return KernelResult.ReservedValue;
}
@@ -256,7 +263,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
break;
}
case 0x4000:
case CapabilityType.KernelVersion:
{
// Note: This check is bugged on kernel too, we are just replicating the bug here.
if ((KernelReleaseVersion >> 17) != 0 || cap < 0x80000)
@@ -269,11 +276,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
break;
}
case 0x8000:
case CapabilityType.HandleTable:
{
int handleTableSize = cap >> 26;
uint handleTableSize = cap >> 26;
if ((uint)handleTableSize > 0x3ff)
if (handleTableSize > 0x3ff)
{
return KernelResult.ReservedValue;
}
@@ -283,16 +290,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
break;
}
case 0x10000:
case CapabilityType.DebugFlags:
{
int debuggingFlags = cap >> 19;
uint debuggingFlags = cap >> 19;
if ((uint)debuggingFlags > 3)
if (debuggingFlags > 3)
{
return KernelResult.ReservedValue;
}
DebuggingFlags &= ~3;
DebuggingFlags &= ~3u;
DebuggingFlags |= debuggingFlags;
break;
@@ -304,18 +311,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
return Result.Success;
}
private static ulong GetMaskFromMinMax(int min, int max)
private static ulong GetMaskFromMinMax(uint min, uint max)
{
int range = max - min + 1;
uint range = max - min + 1;
if (range == 64)
{
return ulong.MaxValue;
}
ulong mask = (1UL << range) - 1;
ulong mask = (1UL << (int)range) - 1;
return mask << min;
return mask << (int)min;
}
}
}

View File

@@ -1,5 +1,8 @@
namespace Ryujinx.HLE.HOS.Kernel.Process
using System;
namespace Ryujinx.HLE.HOS.Kernel.Process
{
[Flags]
enum ProcessCreationFlags
{
Is64Bit = 1 << 0,

View File

@@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
public Result CreateProcess(
out int handle,
ProcessCreationInfo info,
ReadOnlySpan<int> capabilities,
ReadOnlySpan<uint> capabilities,
IProcessContextFactory contextFactory,
ThreadStart customThreadStart = null)
{
@@ -553,7 +553,7 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
KProcess currentProcess = KernelStatic.GetCurrentProcess();
KSynchronizationObject[] syncObjs = new KSynchronizationObject[handles.Length];
KSynchronizationObject[] syncObjs = handles.Length == 0 ? Array.Empty<KSynchronizationObject>() : new KSynchronizationObject[handles.Length];
for (int index = 0; index < handles.Length; index++)
{

View File

@@ -5,11 +5,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
class KPriorityQueue
{
private LinkedList<KThread>[][] _scheduledThreadsPerPrioPerCore;
private LinkedList<KThread>[][] _suggestedThreadsPerPrioPerCore;
private readonly LinkedList<KThread>[][] _scheduledThreadsPerPrioPerCore;
private readonly LinkedList<KThread>[][] _suggestedThreadsPerPrioPerCore;
private long[] _scheduledPrioritiesPerCore;
private long[] _suggestedPrioritiesPerCore;
private readonly long[] _scheduledPrioritiesPerCore;
private readonly long[] _suggestedPrioritiesPerCore;
public KPriorityQueue()
{
@@ -32,43 +32,134 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
_suggestedPrioritiesPerCore = new long[KScheduler.CpuCoresCount];
}
public IEnumerable<KThread> SuggestedThreads(int core)
public readonly ref struct KThreadEnumerable
{
return Iterate(_suggestedThreadsPerPrioPerCore, _suggestedPrioritiesPerCore, core);
readonly LinkedList<KThread>[][] _listPerPrioPerCore;
readonly long[] _prios;
readonly int _core;
public KThreadEnumerable(LinkedList<KThread>[][] listPerPrioPerCore, long[] prios, int core)
{
_listPerPrioPerCore = listPerPrioPerCore;
_prios = prios;
_core = core;
}
public IEnumerable<KThread> ScheduledThreads(int core)
public Enumerator GetEnumerator()
{
return Iterate(_scheduledThreadsPerPrioPerCore, _scheduledPrioritiesPerCore, core);
return new Enumerator(_listPerPrioPerCore, _prios, _core);
}
private IEnumerable<KThread> Iterate(LinkedList<KThread>[][] listPerPrioPerCore, long[] prios, int core)
public ref struct Enumerator
{
long prioMask = prios[core];
private readonly LinkedList<KThread>[][] _listPerPrioPerCore;
private readonly int _core;
private long _prioMask;
private int _prio;
private LinkedList<KThread> _list;
private LinkedListNode<KThread> _node;
int prio = BitOperations.TrailingZeroCount(prioMask);
prioMask &= ~(1L << prio);
while (prio < KScheduler.PrioritiesCount)
public Enumerator(LinkedList<KThread>[][] listPerPrioPerCore, long[] prios, int core)
{
LinkedList<KThread> list = listPerPrioPerCore[prio][core];
LinkedListNode<KThread> node = list.First;
while (node != null)
{
yield return node.Value;
node = node.Next;
_listPerPrioPerCore = listPerPrioPerCore;
_core = core;
_prioMask = prios[core];
_prio = BitOperations.TrailingZeroCount(_prioMask);
_prioMask &= ~(1L << _prio);
}
prio = BitOperations.TrailingZeroCount(prioMask);
public KThread Current => _node?.Value;
prioMask &= ~(1L << prio);
public bool MoveNext()
{
_node = _node?.Next;
if (_node == null)
{
if (!MoveNextListAndFirstNode())
{
return false;
}
}
return _node != null;
}
private bool MoveNextListAndFirstNode()
{
if (_prio < KScheduler.PrioritiesCount)
{
_list = _listPerPrioPerCore[_prio][_core];
_node = _list.First;
_prio = BitOperations.TrailingZeroCount(_prioMask);
_prioMask &= ~(1L << _prio);
return true;
}
else
{
_list = null;
_node = null;
return false;
}
}
}
}
public KThreadEnumerable ScheduledThreads(int core)
{
return new KThreadEnumerable(_scheduledThreadsPerPrioPerCore, _scheduledPrioritiesPerCore, core);
}
public KThreadEnumerable SuggestedThreads(int core)
{
return new KThreadEnumerable(_suggestedThreadsPerPrioPerCore, _suggestedPrioritiesPerCore, core);
}
public KThread ScheduledThreadsFirstOrDefault(int core)
{
return ScheduledThreadsElementAtOrDefault(core, 0);
}
public KThread ScheduledThreadsElementAtOrDefault(int core, int index)
{
int currentIndex = 0;
foreach (var scheduledThread in ScheduledThreads(core))
{
if (currentIndex == index)
{
return scheduledThread;
}
else
{
currentIndex++;
}
}
return null;
}
public KThread ScheduledThreadsWithDynamicPriorityFirstOrDefault(int core, int dynamicPriority)
{
foreach (var scheduledThread in ScheduledThreads(core))
{
if (scheduledThread.DynamicPriority == dynamicPriority)
{
return scheduledThread;
}
}
return null;
}
public bool HasScheduledThreads(int core)
{
return ScheduledThreadsFirstOrDefault(core) != null;
}
public void TransferToCore(int prio, int dstCore, KThread thread)
{
int srcCore = thread.ActiveCore;

View File

@@ -1,8 +1,6 @@
using Ryujinx.Common;
using Ryujinx.HLE.HOS.Kernel.Process;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Threading;
@@ -17,6 +15,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
private static readonly int[] PreemptionPriorities = new int[] { 59, 59, 59, 63 };
private static readonly int[] _srcCoresHighestPrioThreads = new int[CpuCoresCount];
private readonly KernelContext _context;
private readonly int _coreId;
@@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
for (int core = 0; core < CpuCoresCount; core++)
{
KThread thread = context.PriorityQueue.ScheduledThreads(core).FirstOrDefault();
KThread thread = context.PriorityQueue.ScheduledThreadsFirstOrDefault(core);
if (thread != null &&
thread.Owner != null &&
@@ -115,12 +115,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{
// If the core is not idle (there's already a thread running on it),
// then we don't need to attempt load balancing.
if (context.PriorityQueue.ScheduledThreads(core).Any())
if (context.PriorityQueue.HasScheduledThreads(core))
{
continue;
}
int[] srcCoresHighestPrioThreads = new int[CpuCoresCount];
Array.Fill(_srcCoresHighestPrioThreads, 0);
int srcCoresHighestPrioThreadsCount = 0;
@@ -136,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
break;
}
srcCoresHighestPrioThreads[srcCoresHighestPrioThreadsCount++] = suggested.ActiveCore;
_srcCoresHighestPrioThreads[srcCoresHighestPrioThreadsCount++] = suggested.ActiveCore;
}
// Not yet selected candidate found.
@@ -158,9 +158,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
// (the first one that doesn't make the source core idle if moved).
for (int index = 0; index < srcCoresHighestPrioThreadsCount; index++)
{
int srcCore = srcCoresHighestPrioThreads[index];
int srcCore = _srcCoresHighestPrioThreads[index];
KThread src = context.PriorityQueue.ScheduledThreads(srcCore).ElementAtOrDefault(1);
KThread src = context.PriorityQueue.ScheduledThreadsElementAtOrDefault(srcCore, 1);
if (src != null)
{
@@ -422,9 +422,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
private static void RotateScheduledQueue(KernelContext context, int core, int prio)
{
IEnumerable<KThread> scheduledThreads = context.PriorityQueue.ScheduledThreads(core);
KThread selectedThread = scheduledThreads.FirstOrDefault(x => x.DynamicPriority == prio);
KThread selectedThread = context.PriorityQueue.ScheduledThreadsWithDynamicPriorityFirstOrDefault(core, prio);
KThread nextThread = null;
// Yield priority queue.
@@ -433,14 +431,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
nextThread = context.PriorityQueue.Reschedule(prio, core, selectedThread);
}
IEnumerable<KThread> SuitableCandidates()
static KThread FirstSuitableCandidateOrDefault(KernelContext context, int core, KThread selectedThread, KThread nextThread, Predicate< KThread> predicate)
{
foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core))
{
int suggestedCore = suggested.ActiveCore;
if (suggestedCore >= 0)
{
KThread selectedSuggestedCore = context.PriorityQueue.ScheduledThreads(suggestedCore).FirstOrDefault();
KThread selectedSuggestedCore = context.PriorityQueue.ScheduledThreadsFirstOrDefault(suggestedCore);
if (selectedSuggestedCore == suggested || (selectedSuggestedCore != null && selectedSuggestedCore.DynamicPriority < 2))
{
@@ -453,14 +451,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
nextThread == null ||
nextThread.LastScheduledTime >= suggested.LastScheduledTime)
{
yield return suggested;
if (predicate(suggested))
{
return suggested;
}
}
}
return null;
}
// Select candidate threads that could run on this core.
// Only take into account threads that are not yet selected.
KThread dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority == prio);
KThread dst = FirstSuitableCandidateOrDefault(context, core, selectedThread, nextThread, x => x.DynamicPriority == prio);
if (dst != null)
{
@@ -469,11 +472,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
// If the priority of the currently selected thread is lower or same as the preemption priority,
// then try to migrate a thread with lower priority.
KThread bestCandidate = context.PriorityQueue.ScheduledThreads(core).FirstOrDefault();
KThread bestCandidate = context.PriorityQueue.ScheduledThreadsFirstOrDefault(core);
if (bestCandidate != null && bestCandidate.DynamicPriority >= prio)
{
dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority < bestCandidate.DynamicPriority);
dst = FirstSuitableCandidateOrDefault(context, core, selectedThread, nextThread, x => x.DynamicPriority < bestCandidate.DynamicPriority);
if (dst != null)
{
@@ -534,7 +537,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
// Move current thread to the end of the queue.
KThread nextThread = context.PriorityQueue.Reschedule(prio, core, currentThread);
IEnumerable<KThread> SuitableCandidates()
static KThread FirstSuitableCandidateOrDefault(KernelContext context, int core, KThread nextThread, int lessThanOrEqualPriority)
{
foreach (KThread suggested in context.PriorityQueue.SuggestedThreads(core))
{
@@ -554,12 +557,17 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
if (suggested.LastScheduledTime <= nextThread.LastScheduledTime ||
suggested.DynamicPriority < nextThread.DynamicPriority)
{
yield return suggested;
if (suggested.DynamicPriority <= lessThanOrEqualPriority)
{
return suggested;
}
}
}
KThread dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority <= prio);
return null;
}
KThread dst = FirstSuitableCandidateOrDefault(context, core, nextThread, prio);
if (dst != null)
{
@@ -596,7 +604,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
context.PriorityQueue.TransferToCore(currentThread.DynamicPriority, -1, currentThread);
if (!context.PriorityQueue.ScheduledThreads(core).Any())
if (!context.PriorityQueue.HasScheduledThreads(core))
{
KThread selectedThread = null;
@@ -609,7 +617,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
continue;
}
KThread firstCandidate = context.PriorityQueue.ScheduledThreads(suggestedCore).FirstOrDefault();
KThread firstCandidate = context.PriorityQueue.ScheduledThreadsFirstOrDefault(suggestedCore);
if (firstCandidate == suggested)
{

View File

@@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
}
else
{
LinkedListNode<KThread>[] syncNodes = new LinkedListNode<KThread>[syncObjs.Length];
LinkedListNode<KThread>[] syncNodes = syncObjs.Length == 0 ? Array.Empty<LinkedListNode<KThread>>() : new LinkedListNode<KThread>[syncObjs.Length];
for (int index = 0; index < syncObjs.Length; index++)
{

View File

@@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS
ulong codeBaseAddress = kip.Is64BitAddressSpace ? 0x8000000UL : 0x200000UL;
ulong codeAddress = codeBaseAddress + (ulong)kip.TextOffset;
ulong codeAddress = codeBaseAddress + kip.TextOffset;
ProcessCreationFlags flags = 0;
@@ -231,13 +231,13 @@ namespace Ryujinx.HLE.HOS
nsoSize = BitUtils.AlignUp<uint>(nsoSize, KPageTableBase.PageSize);
nsoBase[index] = codeStart + (ulong)codeSize;
nsoBase[index] = codeStart + codeSize;
codeSize += nsoSize;
if (arguments != null && argsSize == 0)
{
argsStart = (ulong)codeSize;
argsStart = codeSize;
argsSize = (uint)BitUtils.AlignDown(arguments.Length * 2 + ArgsTotalSize - 1, KPageTableBase.PageSize);
@@ -318,7 +318,7 @@ namespace Ryujinx.HLE.HOS
result = process.Initialize(
creationInfo,
MemoryMarshal.Cast<byte, int>(npdm.KernelCapabilityData).ToArray(),
MemoryMarshal.Cast<byte, uint>(npdm.KernelCapabilityData),
resourceLimit,
memoryRegion,
processContextFactory);

View File

@@ -1,4 +1,5 @@
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using System.IO;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage
@@ -10,7 +11,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.Storage
public static byte[] MakeLaunchParams(UserProfile userProfile)
{
// Size needs to be at least 0x88 bytes otherwise application errors.
using (MemoryStream ms = new MemoryStream())
using (MemoryStream ms = MemoryStreamManager.Shared.GetStream())
{
BinaryWriter writer = new BinaryWriter(ms);

View File

@@ -5,6 +5,7 @@ using LibHac.FsSystem;
using LibHac.Ncm;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Memory;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Kernel.Memory;
@@ -160,7 +161,7 @@ namespace Ryujinx.HLE.HOS.Services.Sdb.Pl
static uint KXor(uint data) => data ^ FontKey;
using (BinaryReader reader = new BinaryReader(bfttfStream))
using (MemoryStream ttfStream = new MemoryStream())
using (MemoryStream ttfStream = MemoryStreamManager.Shared.GetStream())
using (BinaryWriter output = new BinaryWriter(ttfStream))
{
if (KXor(reader.ReadUInt32()) != BFTTFMagic)

View File

@@ -1,3 +1,5 @@
using Ryujinx.Common;
using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Kernel.Ipc;
@@ -5,6 +7,7 @@ using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Horizon.Common;
using System;
using System.Buffers;
using System.Buffers.Binary;
using System.Collections.Generic;
using System.IO;
@@ -19,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Services
// not large enough.
private const int PointerBufferSize = 0x8000;
private readonly static int[] DefaultCapabilities = new int[]
private readonly static uint[] DefaultCapabilities = new uint[]
{
0x030363F7,
0x1FFFFFCF,
@@ -29,6 +32,8 @@ namespace Ryujinx.HLE.HOS.Services
0x01007FFF
};
private readonly object _handleLock = new();
private readonly KernelContext _context;
private KProcess _selfProcess;
@@ -37,14 +42,27 @@ namespace Ryujinx.HLE.HOS.Services
private readonly Dictionary<int, IpcService> _sessions = new Dictionary<int, IpcService>();
private readonly Dictionary<int, Func<IpcService>> _ports = new Dictionary<int, Func<IpcService>>();
private readonly MemoryStream _requestDataStream;
private readonly BinaryReader _requestDataReader;
private readonly MemoryStream _responseDataStream;
private readonly BinaryWriter _responseDataWriter;
public ManualResetEvent InitDone { get; }
public string Name { get; }
public Func<IpcService> SmObjectFactory { get; }
public ServerBase(KernelContext context, string name, Func<IpcService> smObjectFactory = null)
{
InitDone = new ManualResetEvent(false);
_context = context;
_requestDataStream = MemoryStreamManager.Shared.GetStream();
_requestDataReader = new BinaryReader(_requestDataStream);
_responseDataStream = MemoryStreamManager.Shared.GetStream();
_responseDataWriter = new BinaryWriter(_responseDataStream);
InitDone = new ManualResetEvent(false);
Name = name;
SmObjectFactory = smObjectFactory;
@@ -60,8 +78,11 @@ namespace Ryujinx.HLE.HOS.Services
}
private void AddPort(int serverPortHandle, Func<IpcService> objectFactory)
{
lock (_handleLock)
{
_portHandles.Add(serverPortHandle);
}
_ports.Add(serverPortHandle, objectFactory);
}
@@ -75,8 +96,11 @@ namespace Ryujinx.HLE.HOS.Services
}
public void AddSessionObj(int serverSessionHandle, IpcService obj)
{
lock (_handleLock)
{
_sessionHandles.Add(serverSessionHandle);
}
_sessions.Add(serverSessionHandle, obj);
}
@@ -110,15 +134,23 @@ namespace Ryujinx.HLE.HOS.Services
while (true)
{
int[] portHandles = _portHandles.ToArray();
int[] sessionHandles = _sessionHandles.ToArray();
int[] handles = new int[portHandles.Length + sessionHandles.Length];
int handleCount;
int portHandleCount;
int[] handles;
portHandles.CopyTo(handles, 0);
sessionHandles.CopyTo(handles, portHandles.Length);
lock (_handleLock)
{
portHandleCount = _portHandles.Count;
handleCount = portHandleCount + _sessionHandles.Count;
handles = ArrayPool<int>.Shared.Rent(handleCount);
_portHandles.CopyTo(handles, 0);
_sessionHandles.CopyTo(handles, portHandleCount);
}
// We still need a timeout here to allow the service to pick up and listen new sessions...
var rc = _context.Syscall.ReplyAndReceive(out int signaledIndex, handles, replyTargetHandle, 1000000L);
var rc = _context.Syscall.ReplyAndReceive(out int signaledIndex, handles.AsSpan(0, handleCount), replyTargetHandle, 1000000L);
thread.HandlePostSyscall();
@@ -129,7 +161,7 @@ namespace Ryujinx.HLE.HOS.Services
replyTargetHandle = 0;
if (rc == Result.Success && signaledIndex >= portHandles.Length)
if (rc == Result.Success && signaledIndex >= portHandleCount)
{
// We got a IPC request, process it, pass to the appropriate service if needed.
int signaledHandle = handles[signaledIndex];
@@ -156,6 +188,8 @@ namespace Ryujinx.HLE.HOS.Services
_selfProcess.CpuMemory.Write(messagePtr + 0x4, 2 << 10);
_selfProcess.CpuMemory.Write(messagePtr + 0x8, heapAddr | ((ulong)PointerBufferSize << 48));
}
ArrayPool<int>.Shared.Return(handles);
}
Dispose();
@@ -166,13 +200,9 @@ namespace Ryujinx.HLE.HOS.Services
KProcess process = KernelStatic.GetCurrentProcess();
KThread thread = KernelStatic.GetCurrentThread();
ulong messagePtr = thread.TlsAddress;
ulong messageSize = 0x100;
byte[] reqData = new byte[messageSize];
IpcMessage request = ReadRequest(process, messagePtr);
process.CpuMemory.Read(messagePtr, reqData);
IpcMessage request = new IpcMessage(reqData, (long)messagePtr);
IpcMessage response = new IpcMessage();
ulong tempAddr = recvListAddr;
@@ -202,18 +232,16 @@ namespace Ryujinx.HLE.HOS.Services
bool shouldReply = true;
bool isTipcCommunication = false;
using (MemoryStream raw = new MemoryStream(request.RawData))
{
BinaryReader reqReader = new BinaryReader(raw);
_requestDataStream.SetLength(0);
_requestDataStream.Write(request.RawData);
_requestDataStream.Position = 0;
if (request.Type == IpcMessageType.HipcRequest ||
request.Type == IpcMessageType.HipcRequestWithContext)
{
response.Type = IpcMessageType.HipcResponse;
using (MemoryStream resMs = new MemoryStream())
{
BinaryWriter resWriter = new BinaryWriter(resMs);
_responseDataStream.SetLength(0);
ServiceCtx context = new ServiceCtx(
_context.Device,
@@ -222,34 +250,33 @@ namespace Ryujinx.HLE.HOS.Services
thread,
request,
response,
reqReader,
resWriter);
_requestDataReader,
_responseDataWriter);
_sessions[serverSessionHandle].CallHipcMethod(context);
response.RawData = resMs.ToArray();
}
response.RawData = _responseDataStream.ToArray();
}
else if (request.Type == IpcMessageType.HipcControl ||
request.Type == IpcMessageType.HipcControlWithContext)
{
uint magic = (uint)reqReader.ReadUInt64();
uint cmdId = (uint)reqReader.ReadUInt64();
uint magic = (uint)_requestDataReader.ReadUInt64();
uint cmdId = (uint)_requestDataReader.ReadUInt64();
switch (cmdId)
{
case 0:
request = FillResponse(response, 0, _sessions[serverSessionHandle].ConvertToDomain());
FillHipcResponse(response, 0, _sessions[serverSessionHandle].ConvertToDomain());
break;
case 3:
request = FillResponse(response, 0, PointerBufferSize);
FillHipcResponse(response, 0, PointerBufferSize);
break;
// TODO: Whats the difference between IpcDuplicateSession/Ex?
case 2:
case 4:
int unknown = reqReader.ReadInt32();
int unknown = _requestDataReader.ReadInt32();
_context.Syscall.CreateSession(out int dupServerSessionHandle, out int dupClientSessionHandle, false, 0);
@@ -257,7 +284,7 @@ namespace Ryujinx.HLE.HOS.Services
response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle);
request = FillResponse(response, 0);
FillHipcResponse(response, 0);
break;
@@ -267,12 +294,12 @@ namespace Ryujinx.HLE.HOS.Services
else if (request.Type == IpcMessageType.HipcCloseSession || request.Type == IpcMessageType.TipcCloseSession)
{
_context.Syscall.CloseHandle(serverSessionHandle);
_sessionHandles.Remove(serverSessionHandle);
IpcService service = _sessions[serverSessionHandle];
if (service is IDisposable disposableObj)
lock (_handleLock)
{
disposableObj.Dispose();
_sessionHandles.Remove(serverSessionHandle);
}
IpcService service = _sessions[serverSessionHandle];
(service as IDisposable)?.Dispose();
_sessions.Remove(serverSessionHandle);
shouldReply = false;
}
@@ -284,9 +311,7 @@ namespace Ryujinx.HLE.HOS.Services
// Response type is always the same as request on TIPC.
response.Type = request.Type;
using (MemoryStream resMs = new MemoryStream())
{
BinaryWriter resWriter = new BinaryWriter(resMs);
_responseDataStream.SetLength(0);
ServiceCtx context = new ServiceCtx(
_context.Device,
@@ -295,15 +320,15 @@ namespace Ryujinx.HLE.HOS.Services
thread,
request,
response,
reqReader,
resWriter);
_requestDataReader,
_responseDataWriter);
_sessions[serverSessionHandle].CallTipcMethod(context);
response.RawData = resMs.ToArray();
}
response.RawData = _responseDataStream.ToArray();
process.CpuMemory.Write(messagePtr, response.GetBytesTipc());
using var responseStream = response.GetStreamTipc();
process.CpuMemory.Write(messagePtr, responseStream.GetReadOnlySequence());
}
else
{
@@ -312,48 +337,55 @@ namespace Ryujinx.HLE.HOS.Services
if (!isTipcCommunication)
{
process.CpuMemory.Write(messagePtr, response.GetBytes((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48)));
using var responseStream = response.GetStream((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48));
process.CpuMemory.Write(messagePtr, responseStream.GetReadOnlySequence());
}
return shouldReply;
}
}
private static IpcMessage FillResponse(IpcMessage response, long result, params int[] values)
private static IpcMessage ReadRequest(KProcess process, ulong messagePtr)
{
using (MemoryStream ms = new MemoryStream())
const int messageSize = 0x100;
byte[] reqData = ArrayPool<byte>.Shared.Rent(messageSize);
Span<byte> reqDataSpan = reqData.AsSpan(0, messageSize);
reqDataSpan.Clear();
process.CpuMemory.Read(messagePtr, reqDataSpan);
IpcMessage request = new IpcMessage(reqDataSpan, (long)messagePtr);
ArrayPool<byte>.Shared.Return(reqData);
return request;
}
private void FillHipcResponse(IpcMessage response, long result)
{
BinaryWriter writer = new BinaryWriter(ms);
FillHipcResponse(response, result, ReadOnlySpan<byte>.Empty);
}
foreach (int value in values)
private void FillHipcResponse(IpcMessage response, long result, int value)
{
writer.Write(value);
Span<byte> span = stackalloc byte[sizeof(int)];
BinaryPrimitives.WriteInt32LittleEndian(span, value);
FillHipcResponse(response, result, span);
}
return FillResponse(response, result, ms.ToArray());
}
}
private static IpcMessage FillResponse(IpcMessage response, long result, byte[] data = null)
private void FillHipcResponse(IpcMessage response, long result, ReadOnlySpan<byte> data)
{
response.Type = IpcMessageType.HipcResponse;
using (MemoryStream ms = new MemoryStream())
{
BinaryWriter writer = new BinaryWriter(ms);
_responseDataStream.SetLength(0);
writer.Write(IpcMagic.Sfco);
writer.Write(result);
_responseDataStream.Write(IpcMagic.Sfco);
_responseDataStream.Write(result);
if (data != null)
{
writer.Write(data);
}
_responseDataStream.Write(data);
response.RawData = ms.ToArray();
}
return response;
response.RawData = _responseDataStream.ToArray();
}
protected virtual void Dispose(bool disposing)
@@ -372,6 +404,11 @@ namespace Ryujinx.HLE.HOS.Services
_sessions.Clear();
_requestDataReader.Dispose();
_requestDataStream.Dispose();
_responseDataWriter.Dispose();
_responseDataStream.Dispose();
InitDone.Dispose();
}
}

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.HLE.Loaders.Executables
public uint DataSize { get; }
public uint BssSize { get; }
public int[] Capabilities { get; }
public uint[] Capabilities { get; }
public bool UsesSecureMemory { get; }
public bool Is64BitAddressSpace { get; }
public bool Is64Bit { get; }
@@ -57,11 +57,11 @@ namespace Ryujinx.HLE.Loaders.Executables
Version = reader.Version;
Name = reader.Name.ToString();
Capabilities = new int[32];
Capabilities = new uint[32];
for (int index = 0; index < Capabilities.Length; index++)
{
Capabilities[index] = (int)reader.Capabilities[index];
Capabilities[index] = reader.Capabilities[index];
}
reader.GetSegmentSize(KipReader.SegmentType.Data, out int uncompressedSize).ThrowIfFailure();

View File

@@ -1,4 +1,6 @@
using LibHac.Common;
using Microsoft.IO;
using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS;
using System;
using System.Globalization;
@@ -77,7 +79,7 @@ namespace Ryujinx.HLE.Utilities
ulong position = context.Request.PtrBuff[index].Position;
ulong size = context.Request.PtrBuff[index].Size;
using (MemoryStream ms = new MemoryStream())
using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream())
{
while (size-- > 0)
{
@@ -91,7 +93,7 @@ namespace Ryujinx.HLE.Utilities
ms.WriteByte(value);
}
return Encoding.UTF8.GetString(ms.ToArray());
return Encoding.UTF8.GetString(ms.GetReadOnlySequence());
}
}
@@ -110,7 +112,7 @@ namespace Ryujinx.HLE.Utilities
ulong position = context.Request.SendBuff[index].Position;
ulong size = context.Request.SendBuff[index].Size;
using (MemoryStream ms = new MemoryStream())
using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream())
{
while (size-- > 0)
{
@@ -124,7 +126,7 @@ namespace Ryujinx.HLE.Utilities
ms.WriteByte(value);
}
return Encoding.UTF8.GetString(ms.ToArray());
return Encoding.UTF8.GetString(ms.GetReadOnlySequence());
}
}

View File

@@ -3,6 +3,7 @@ using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.Input.HLE;
using Ryujinx.Input.Motion.CemuHook.Protocol;
using System;
@@ -381,7 +382,7 @@ namespace Ryujinx.Input.Motion.CemuHook
Header header = GenerateHeader(clientId);
using (MemoryStream stream = new MemoryStream())
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.WriteStruct(header);
@@ -421,7 +422,7 @@ namespace Ryujinx.Input.Motion.CemuHook
Header header = GenerateHeader(clientId);
using (MemoryStream stream = new MemoryStream())
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.WriteStruct(header);

View File

@@ -1,5 +1,6 @@
using Ryujinx.Memory.Range;
using System;
using System.Buffers;
using System.Collections.Generic;
namespace Ryujinx.Memory
@@ -77,6 +78,21 @@ namespace Ryujinx.Memory
/// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
void Write(ulong va, ReadOnlySpan<byte> data);
/// <summary>
/// Writes data to CPU mapped memory, with write tracking.
/// </summary>
/// <param name="va">Virtual address to write the data into</param>
/// <param name="data">Data to be written</param>
/// <exception cref="InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
public void Write(ulong va, ReadOnlySequence<byte> data)
{
foreach (ReadOnlyMemory<byte> segment in data)
{
Write(va, segment.Span);
va += (ulong)segment.Length;
}
}
/// <summary>
/// Writes data to the application process, returning false if the data was not changed.
/// This triggers read memory tracking, as a redundancy check would be useless if the data is not up to date.

View File

@@ -6,6 +6,7 @@ using LibHac.FsSystem;
using LibHac.Ncm;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Memory;
using Ryujinx.HLE.FileSystem;
using Ryujinx.Ui.Common.Configuration;
using SixLabors.ImageSharp;
@@ -136,8 +137,8 @@ namespace Ryujinx.Ui.Windows
romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure();
using (MemoryStream stream = new MemoryStream())
using (MemoryStream streamPng = new MemoryStream())
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
using (MemoryStream streamPng = MemoryStreamManager.Shared.GetStream())
{
file.Get.AsStream().CopyTo(stream);
@@ -169,7 +170,7 @@ namespace Ryujinx.Ui.Windows
private byte[] ProcessImage(byte[] data)
{
using (MemoryStream streamJpg = new MemoryStream())
using (MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream())
{
Image avatarImage = Image.Load(data, new PngDecoder());

View File

@@ -1,4 +1,5 @@
using Gtk;
using Ryujinx.Common.Memory;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.Ui.Common.Configuration;
@@ -181,7 +182,7 @@ namespace Ryujinx.Ui.Windows
{
image.Mutate(x => x.Resize(256, 256));
using (MemoryStream streamJpg = new MemoryStream())
using (MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream())
{
image.SaveAsJpeg(streamJpg);