aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ARMeilleure/CodeGen/Arm64/CodeGenContext.cs3
-rw-r--r--ARMeilleure/CodeGen/X86/Assembler.cs3
-rw-r--r--ARMeilleure/CodeGen/X86/CodeGenContext.cs3
-rw-r--r--ARMeilleure/Translation/PTC/Ptc.cs7
-rw-r--r--ARMeilleure/Translation/PTC/PtcProfiler.cs5
-rw-r--r--Directory.Packages.props1
-rw-r--r--Ryujinx.Common/Extensions/BinaryReaderExtensions.cs14
-rw-r--r--Ryujinx.Common/Extensions/BinaryWriterExtensions.cs28
-rw-r--r--Ryujinx.Common/Extensions/StreamExtensions.cs138
-rw-r--r--Ryujinx.Common/Memory/MemoryStreamManager.cs99
-rw-r--r--Ryujinx.Common/Ryujinx.Common.csproj1
-rw-r--r--Ryujinx.Common/Utilities/EmbeddedResources.cs16
-rw-r--r--Ryujinx.Common/Utilities/StreamUtils.cs18
-rw-r--r--Ryujinx.Cpu/MemoryHelper.cs8
-rw-r--r--Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs15
-rw-r--r--Ryujinx.HLE/FileSystem/ContentManager.cs11
-rw-r--r--Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs5
-rw-r--r--Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs5
-rw-r--r--Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs5
-rw-r--r--Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs61
-rw-r--r--Ryujinx.HLE/HOS/Ipc/IpcMessage.cs163
-rw-r--r--Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs8
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs25
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs2
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs137
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs52
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs5
-rw-r--r--Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs3
-rw-r--r--Ryujinx.HLE/HOS/Services/ServerBase.cs274
-rw-r--r--Ryujinx.HLE/Utilities/StringUtils.cs10
-rw-r--r--Ryujinx.Input/Motion/CemuHook/Client.cs5
-rw-r--r--Ryujinx.Memory/IVirtualMemoryManager.cs16
-rw-r--r--Ryujinx/Ui/Windows/AvatarWindow.cs7
-rw-r--r--Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs3
35 files changed, 785 insertions, 373 deletions
diff --git a/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs b/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs
index cebfbde1..0dd5355f 100644
--- a/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs
+++ b/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs
@@ -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;
diff --git a/ARMeilleure/CodeGen/X86/Assembler.cs b/ARMeilleure/CodeGen/X86/Assembler.cs
index c15deadc..2ea4208b 100644
--- a/ARMeilleure/CodeGen/X86/Assembler.cs
+++ b/ARMeilleure/CodeGen/X86/Assembler.cs
@@ -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;
diff --git a/ARMeilleure/CodeGen/X86/CodeGenContext.cs b/ARMeilleure/CodeGen/X86/CodeGenContext.cs
index eee71bd7..89948724 100644
--- a/ARMeilleure/CodeGen/X86/CodeGenContext.cs
+++ b/ARMeilleure/CodeGen/X86/CodeGenContext.cs
@@ -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;
diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs
index 276ec788..0b23fd04 100644
--- a/ARMeilleure/Translation/PTC/Ptc.cs
+++ b/ARMeilleure/Translation/PTC/Ptc.cs
@@ -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()
diff --git a/ARMeilleure/Translation/PTC/PtcProfiler.cs b/ARMeilleure/Translation/PTC/PtcProfiler.cs
index 030ccff5..391e29c7 100644
--- a/ARMeilleure/Translation/PTC/PtcProfiler.cs
+++ b/ARMeilleure/Translation/PTC/PtcProfiler.cs
@@ -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);
diff --git a/Directory.Packages.props b/Directory.Packages.props
index eda7d395..14d44075 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -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" />
diff --git a/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs b/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs
index ea404d2a..21da6fc0 100644
--- a/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs
+++ b/Ryujinx.Common/Extensions/BinaryReaderExtensions.cs
@@ -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));
- }
}
}
diff --git a/Ryujinx.Common/Extensions/BinaryWriterExtensions.cs b/Ryujinx.Common/Extensions/BinaryWriterExtensions.cs
new file mode 100644
index 00000000..fddc8c1b
--- /dev/null
+++ b/Ryujinx.Common/Extensions/BinaryWriterExtensions.cs
@@ -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);
+ }
+ }
+}
diff --git a/Ryujinx.Common/Extensions/StreamExtensions.cs b/Ryujinx.Common/Extensions/StreamExtensions.cs
new file mode 100644
index 00000000..f6fc870a
--- /dev/null
+++ b/Ryujinx.Common/Extensions/StreamExtensions.cs
@@ -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);
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Common/Memory/MemoryStreamManager.cs b/Ryujinx.Common/Memory/MemoryStreamManager.cs
new file mode 100644
index 00000000..68b82999
--- /dev/null
+++ b/Ryujinx.Common/Memory/MemoryStreamManager.cs
@@ -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;
+ }
+ }
+ }
+ }
+}
diff --git a/Ryujinx.Common/Ryujinx.Common.csproj b/Ryujinx.Common/Ryujinx.Common.csproj
index c307f524..c02b11e0 100644
--- a/Ryujinx.Common/Ryujinx.Common.csproj
+++ b/Ryujinx.Common/Ryujinx.Common.csproj
@@ -7,6 +7,7 @@
</PropertyGroup>
<ItemGroup>
+ <PackageReference Include="Microsoft.IO.RecyclableMemoryStream" />
<PackageReference Include="MsgPack.Cli" />
<PackageReference Include="System.Management" />
</ItemGroup>
diff --git a/Ryujinx.Common/Utilities/EmbeddedResources.cs b/Ryujinx.Common/Utilities/EmbeddedResources.cs
index e7c8d7d7..22b55f16 100644
--- a/Ryujinx.Common/Utilities/EmbeddedResources.cs
+++ b/Ryujinx.Common/Utilities/EmbeddedResources.cs
@@ -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);
}
}
diff --git a/Ryujinx.Common/Utilities/StreamUtils.cs b/Ryujinx.Common/Utilities/StreamUtils.cs
index 9bd03ec9..da97188d 100644
--- a/Ryujinx.Common/Utilities/StreamUtils.cs
+++ b/Ryujinx.Common/Utilities/StreamUtils.cs
@@ -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();
+ }
+ }
}
} \ No newline at end of file
diff --git a/Ryujinx.Cpu/MemoryHelper.cs b/Ryujinx.Cpu/MemoryHelper.cs
index 64ff360e..194a0c35 100644
--- a/Ryujinx.Cpu/MemoryHelper.cs
+++ b/Ryujinx.Cpu/MemoryHelper.cs
@@ -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());
}
}
}
diff --git a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs b/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs
index 5b430e1a..77e52667 100644
--- a/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs
+++ b/Ryujinx.Graphics.Gpu/Shader/DiskCache/ShaderBinarySerializer.cs
@@ -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();
diff --git a/Ryujinx.HLE/FileSystem/ContentManager.cs b/Ryujinx.HLE/FileSystem/ContentManager.cs
index 4e394008..9facdd0b 100644
--- a/Ryujinx.HLE/FileSystem/ContentManager.cs
+++ b/Ryujinx.HLE/FileSystem/ContentManager.cs
@@ -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();
+ MemoryStream dest = MemoryStreamManager.Shared.GetStream();
- Stream src = entry.Open();
-
- src.CopyTo(dest);
- src.Dispose();
+ using (Stream src = entry.Open())
+ {
+ src.CopyTo(dest);
+ }
return dest;
}
diff --git a/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs b/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs
index 2d5509ff..952afcd5 100644
--- a/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs
+++ b/Ryujinx.HLE/HOS/Applets/Browser/BrowserApplet.cs
@@ -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
diff --git a/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs b/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs
index 5cdfb314..5d5a26c2 100644
--- a/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs
+++ b/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs
@@ -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);
diff --git a/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs b/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs
index cec9f213..a8119a47 100644
--- a/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs
+++ b/Ryujinx.HLE/HOS/Applets/PlayerSelect/PlayerSelectApplet.cs
@@ -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);
diff --git a/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs b/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs
index e6ed4613..c7ef7e9c 100644
--- a/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs
+++ b/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs
@@ -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);
-
- int word = HasPId ? 1 : 0;
+ RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream();
- word |= (ToCopy.Length & 0xf) << 1;
- word |= (ToMove.Length & 0xf) << 5;
+ int word = HasPId ? 1 : 0;
- writer.Write(word);
+ word |= (ToCopy.Length & 0xf) << 1;
+ word |= (ToMove.Length & 0xf) << 5;
- if (HasPId)
- {
- writer.Write(PId);
- }
+ ms.Write(word);
- foreach (int handle in ToCopy)
- {
- writer.Write(handle);
- }
+ if (HasPId)
+ {
+ ms.Write(PId);
+ }
- foreach (int handle in ToMove)
- {
- writer.Write(handle);
- }
+ ms.Write(ToCopy);
+ ms.Write(ToMove);
- return ms.ToArray();
- }
+ ms.Position = 0;
+ return ms;
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs
index 55044da4..4e8f2fbf 100644
--- a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs
+++ b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs
@@ -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,124 +118,119 @@ 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;
+ int word0;
+ int word1;
- word0 = (int)Type;
- word0 |= (PtrBuff.Count & 0xf) << 16;
- word0 |= (SendBuff.Count & 0xf) << 20;
- word0 |= (ReceiveBuff.Count & 0xf) << 24;
- word0 |= (ExchangeBuff.Count & 0xf) << 28;
+ word0 = (int)Type;
+ word0 |= (PtrBuff.Count & 0xf) << 16;
+ word0 |= (SendBuff.Count & 0xf) << 20;
+ word0 |= (ReceiveBuff.Count & 0xf) << 24;
+ word0 |= (ExchangeBuff.Count & 0xf) << 28;
- byte[] handleData = Array.Empty<byte>();
+ using RecyclableMemoryStream handleDataStream = HandleDesc?.GetStream();
- if (HandleDesc != null)
- {
- handleData = HandleDesc.GetBytes();
- }
+ int dataLength = RawData?.Length ?? 0;
- int dataLength = RawData?.Length ?? 0;
+ dataLength = (dataLength + 3) & ~3;
- dataLength = (dataLength + 3) & ~3;
+ int rawLength = dataLength;
- int rawLength = dataLength;
+ int pad0 = (int)GetPadSize16(cmdPtr + 8 + (handleDataStream?.Length ?? 0) + PtrBuff.Count * 8);
- int pad0 = (int)GetPadSize16(cmdPtr + 8 + handleData.Length + 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.
+ // This is the weirdest padding I've seen so far...
+ int pad1 = 0x10 - pad0;
- // 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.
- // This is the weirdest padding I've seen so far...
- int pad1 = 0x10 - pad0;
+ dataLength = (dataLength + pad0 + pad1) / 4;
- dataLength = (dataLength + pad0 + pad1) / 4;
+ word1 = (dataLength & 0x3ff) | (2 << 10);
- word1 = (dataLength & 0x3ff) | (2 << 10);
+ if (HandleDesc != null)
+ {
+ word1 |= 1 << 31;
+ }
- if (HandleDesc != null)
- {
- word1 |= 1 << 31;
- }
+ ms.Write(word0);
+ ms.Write(word1);
- writer.Write(word0);
- writer.Write(word1);
- writer.Write(handleData);
+ if (handleDataStream != null)
+ {
+ ms.Write(handleDataStream);
+ }
- for (int index = 0; index < PtrBuff.Count; index++)
- {
- writer.Write(PtrBuff[index].GetWord0());
- writer.Write(PtrBuff[index].GetWord1());
- }
+ foreach (IpcPtrBuffDesc ptrBuffDesc in PtrBuff)
+ {
+ ms.Write(ptrBuffDesc.GetWord0());
+ ms.Write(ptrBuffDesc.GetWord1());
+ }
- ms.Seek(pad0, SeekOrigin.Current);
+ ms.WriteByte(0, pad0);
- if (RawData != null)
- {
- writer.Write(RawData);
- ms.Seek(rawLength - RawData.Length, SeekOrigin.Current);
- }
+ if (RawData != null)
+ {
+ 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;
+ int word0;
+ int word1;
- word0 = (int)Type;
- word0 |= (SendBuff.Count & 0xf) << 20;
- word0 |= (ReceiveBuff.Count & 0xf) << 24;
- word0 |= (ExchangeBuff.Count & 0xf) << 28;
+ word0 = (int)Type;
+ word0 |= (SendBuff.Count & 0xf) << 20;
+ word0 |= (ReceiveBuff.Count & 0xf) << 24;
+ word0 |= (ExchangeBuff.Count & 0xf) << 28;
- byte[] handleData = Array.Empty<byte>();
+ using RecyclableMemoryStream handleDataStream = HandleDesc?.GetStream();
- if (HandleDesc != null)
- {
- handleData = HandleDesc.GetBytes();
- }
-
- int dataLength = RawData?.Length ?? 0;
+ int dataLength = RawData?.Length ?? 0;
- dataLength = ((dataLength + 3) & ~3) / 4;
+ dataLength = ((dataLength + 3) & ~3) / 4;
- word1 = (dataLength & 0x3ff);
+ word1 = (dataLength & 0x3ff);
- if (HandleDesc != null)
- {
- word1 |= 1 << 31;
- }
+ if (HandleDesc != null)
+ {
+ word1 |= 1 << 31;
+ }
- writer.Write(word0);
- writer.Write(word1);
- writer.Write(handleData);
+ ms.Write(word0);
+ ms.Write(word1);
- if (RawData != null)
- {
- writer.Write(RawData);
- }
+ if (handleDataStream != null)
+ {
+ ms.Write(handleDataStream);
+ }
- return ms.ToArray();
+ if (RawData != null)
+ {
+ ms.Write(RawData);
}
+
+ return ms;
}
private long GetPadSize16(long position)
diff --git a/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs b/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs
index 10406ac7..bcc9d8f8 100644
--- a/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs
+++ b/Ryujinx.HLE/HOS/Ipc/IpcRecvListBuffDesc.cs
@@ -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);
}
}
} \ No newline at end of file
diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs b/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
index 030a314f..1af171b9 100644
--- a/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
@@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
public WaitingObject(IKFutureSchedulerObject schedulerObj, long timePoint)
{
- Object = schedulerObj;
+ Object = schedulerObj;
TimePoint = timePoint;
}
}
@@ -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);
}
@@ -142,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
private WaitingObject GetNextWaitingObject()
{
WaitingObject selected = null;
-
+
long lowestTimePoint = long.MaxValue;
for (int index = _waitingObjects.Count - 1; index >= 0; index--)
@@ -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)
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
index eef78e18..c6467208 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
@@ -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++)
{
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs
index 2c9d7574..14fba704 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KPriorityQueue.cs
@@ -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,41 +32,132 @@ 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 Enumerator GetEnumerator()
+ {
+ return new Enumerator(_listPerPrioPerCore, _prios, _core);
+ }
+
+ public ref struct Enumerator
+ {
+ private readonly LinkedList<KThread>[][] _listPerPrioPerCore;
+ private readonly int _core;
+ private long _prioMask;
+ private int _prio;
+ private LinkedList<KThread> _list;
+ private LinkedListNode<KThread> _node;
+
+ public Enumerator(LinkedList<KThread>[][] listPerPrioPerCore, long[] prios, int core)
+ {
+ _listPerPrioPerCore = listPerPrioPerCore;
+ _core = core;
+ _prioMask = prios[core];
+ _prio = BitOperations.TrailingZeroCount(_prioMask);
+ _prioMask &= ~(1L << _prio);
+ }
+
+ public KThread Current => _node?.Value;
+
+ 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 IEnumerable<KThread> ScheduledThreads(int core)
+ public KThreadEnumerable ScheduledThreads(int core)
{
- return Iterate(_scheduledThreadsPerPrioPerCore, _scheduledPrioritiesPerCore, core);
+ return new KThreadEnumerable(_scheduledThreadsPerPrioPerCore, _scheduledPrioritiesPerCore, core);
}
- private IEnumerable<KThread> Iterate(LinkedList<KThread>[][] listPerPrioPerCore, long[] prios, int core)
+ public KThreadEnumerable SuggestedThreads(int core)
{
- long prioMask = prios[core];
-
- int prio = BitOperations.TrailingZeroCount(prioMask);
+ return new KThreadEnumerable(_suggestedThreadsPerPrioPerCore, _suggestedPrioritiesPerCore, core);
+ }
- prioMask &= ~(1L << prio);
+ public KThread ScheduledThreadsFirstOrDefault(int core)
+ {
+ return ScheduledThreadsElementAtOrDefault(core, 0);
+ }
- while (prio < KScheduler.PrioritiesCount)
+ public KThread ScheduledThreadsElementAtOrDefault(int core, int index)
+ {
+ int currentIndex = 0;
+ foreach (var scheduledThread in ScheduledThreads(core))
{
- LinkedList<KThread> list = listPerPrioPerCore[prio][core];
+ if (currentIndex == index)
+ {
+ return scheduledThread;
+ }
+ else
+ {
+ currentIndex++;
+ }
+ }
- LinkedListNode<KThread> node = list.First;
+ return null;
+ }
- while (node != null)
+ public KThread ScheduledThreadsWithDynamicPriorityFirstOrDefault(int core, int dynamicPriority)
+ {
+ foreach (var scheduledThread in ScheduledThreads(core))
+ {
+ if (scheduledThread.DynamicPriority == dynamicPriority)
{
- yield return node.Value;
-
- node = node.Next;
+ return scheduledThread;
}
+ }
- prio = BitOperations.TrailingZeroCount(prioMask);
+ return null;
+ }
- prioMask &= ~(1L << prio);
- }
+ public bool HasScheduledThreads(int core)
+ {
+ return ScheduledThreadsFirstOrDefault(core) != null;
}
public void TransferToCore(int prio, int dstCore, KThread thread)
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
index 0c51b7b9..b9de7d9c 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KScheduler.cs
@@ -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;
+ }
}
}
+
+ return null;
}
- KThread dst = SuitableCandidates().FirstOrDefault(x => x.DynamicPriority <= prio);
+ 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)
{
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs
index 01b65f55..973d5f6a 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs
@@ -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++)
{
diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs
index 227cfdae..49e342f2 100644
--- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/Storage/StorageHelper.cs
@@ -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);
diff --git a/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs b/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs
index 66a69a8b..fef82cbc 100644
--- a/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs
+++ b/Ryujinx.HLE/HOS/Services/Sdb/Pl/SharedFontManager.cs
@@ -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)
diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/Ryujinx.HLE/HOS/Services/ServerBase.cs
index d4382a64..619f5448 100644
--- a/Ryujinx.HLE/HOS/Services/ServerBase.cs
+++ b/Ryujinx.HLE/HOS/Services/ServerBase.cs
@@ -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;
@@ -37,14 +40,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;
@@ -110,15 +126,15 @@ 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 = _portHandles.Count + _sessionHandles.Count;
- portHandles.CopyTo(handles, 0);
- sessionHandles.CopyTo(handles, portHandles.Length);
+ int[] handles = ArrayPool<int>.Shared.Rent(handleCount);
+
+ _portHandles.CopyTo(handles, 0);
+ _sessionHandles.CopyTo(handles, _portHandles.Count);
// 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 +145,7 @@ namespace Ryujinx.HLE.HOS.Services
replyTargetHandle = 0;
- if (rc == Result.Success && signaledIndex >= portHandles.Length)
+ if (rc == Result.Success && signaledIndex >= _portHandles.Count)
{
// We got a IPC request, process it, pass to the appropriate service if needed.
int signaledHandle = handles[signaledIndex];
@@ -156,6 +172,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 +184,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,158 +216,157 @@ namespace Ryujinx.HLE.HOS.Services
bool shouldReply = true;
bool isTipcCommunication = false;
- using (MemoryStream raw = new MemoryStream(request.RawData))
+ _requestDataStream.SetLength(0);
+ _requestDataStream.Write(request.RawData);
+ _requestDataStream.Position = 0;
+
+ if (request.Type == IpcMessageType.HipcRequest ||
+ request.Type == IpcMessageType.HipcRequestWithContext)
{
- BinaryReader reqReader = new BinaryReader(raw);
+ response.Type = IpcMessageType.HipcResponse;
- if (request.Type == IpcMessageType.HipcRequest ||
- request.Type == IpcMessageType.HipcRequestWithContext)
- {
- response.Type = IpcMessageType.HipcResponse;
+ _responseDataStream.SetLength(0);
- using (MemoryStream resMs = new MemoryStream())
- {
- BinaryWriter resWriter = new BinaryWriter(resMs);
+ ServiceCtx context = new ServiceCtx(
+ _context.Device,
+ process,
+ process.CpuMemory,
+ thread,
+ request,
+ response,
+ _requestDataReader,
+ _responseDataWriter);
- ServiceCtx context = new ServiceCtx(
- _context.Device,
- process,
- process.CpuMemory,
- thread,
- request,
- response,
- reqReader,
- resWriter);
+ _sessions[serverSessionHandle].CallHipcMethod(context);
- _sessions[serverSessionHandle].CallHipcMethod(context);
+ response.RawData = _responseDataStream.ToArray();
+ }
+ else if (request.Type == IpcMessageType.HipcControl ||
+ request.Type == IpcMessageType.HipcControlWithContext)
+ {
+ uint magic = (uint)_requestDataReader.ReadUInt64();
+ uint cmdId = (uint)_requestDataReader.ReadUInt64();
- response.RawData = resMs.ToArray();
- }
- }
- else if (request.Type == IpcMessageType.HipcControl ||
- request.Type == IpcMessageType.HipcControlWithContext)
+ switch (cmdId)
{
- uint magic = (uint)reqReader.ReadUInt64();
- uint cmdId = (uint)reqReader.ReadUInt64();
+ case 0:
+ FillHipcResponse(response, 0, _sessions[serverSessionHandle].ConvertToDomain());
+ break;
- switch (cmdId)
- {
- case 0:
- request = FillResponse(response, 0, _sessions[serverSessionHandle].ConvertToDomain());
- break;
-
- case 3:
- request = FillResponse(response, 0, PointerBufferSize);
- break;
+ case 3:
+ FillHipcResponse(response, 0, PointerBufferSize);
+ break;
- // TODO: Whats the difference between IpcDuplicateSession/Ex?
- case 2:
- case 4:
- int unknown = reqReader.ReadInt32();
+ // TODO: Whats the difference between IpcDuplicateSession/Ex?
+ case 2:
+ case 4:
+ int unknown = _requestDataReader.ReadInt32();
- _context.Syscall.CreateSession(out int dupServerSessionHandle, out int dupClientSessionHandle, false, 0);
+ _context.Syscall.CreateSession(out int dupServerSessionHandle, out int dupClientSessionHandle, false, 0);
- AddSessionObj(dupServerSessionHandle, _sessions[serverSessionHandle]);
+ AddSessionObj(dupServerSessionHandle, _sessions[serverSessionHandle]);
- response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle);
+ response.HandleDesc = IpcHandleDesc.MakeMove(dupClientSessionHandle);
- request = FillResponse(response, 0);
+ FillHipcResponse(response, 0);
- break;
+ break;
- default: throw new NotImplementedException(cmdId.ToString());
- }
- }
- 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)
- {
- disposableObj.Dispose();
- }
- _sessions.Remove(serverSessionHandle);
- shouldReply = false;
+ default: throw new NotImplementedException(cmdId.ToString());
}
- // If the type is past 0xF, we are using TIPC
- else if (request.Type > IpcMessageType.TipcCloseSession)
- {
- isTipcCommunication = true;
-
- // Response type is always the same as request on TIPC.
- response.Type = request.Type;
+ }
+ else if (request.Type == IpcMessageType.HipcCloseSession || request.Type == IpcMessageType.TipcCloseSession)
+ {
+ _context.Syscall.CloseHandle(serverSessionHandle);
+ _sessionHandles.Remove(serverSessionHandle);
+ IpcService service = _sessions[serverSessionHandle];
+ (service as IDisposable)?.Dispose();
+ _sessions.Remove(serverSessionHandle);
+ shouldReply = false;
+ }
+ // If the type is past 0xF, we are using TIPC
+ else if (request.Type > IpcMessageType.TipcCloseSession)
+ {
+ isTipcCommunication = true;
- using (MemoryStream resMs = new MemoryStream())
- {
- BinaryWriter resWriter = new BinaryWriter(resMs);
+ // Response type is always the same as request on TIPC.
+ response.Type = request.Type;
- ServiceCtx context = new ServiceCtx(
- _context.Device,
- process,
- process.CpuMemory,
- thread,
- request,
- response,
- reqReader,
- resWriter);
+ _responseDataStream.SetLength(0);
- _sessions[serverSessionHandle].CallTipcMethod(context);
+ ServiceCtx context = new ServiceCtx(
+ _context.Device,
+ process,
+ process.CpuMemory,
+ thread,
+ request,
+ response,
+ _requestDataReader,
+ _responseDataWriter);
- response.RawData = resMs.ToArray();
- }
+ _sessions[serverSessionHandle].CallTipcMethod(context);
- process.CpuMemory.Write(messagePtr, response.GetBytesTipc());
- }
- else
- {
- throw new NotImplementedException(request.Type.ToString());
- }
+ response.RawData = _responseDataStream.ToArray();
- if (!isTipcCommunication)
- {
- process.CpuMemory.Write(messagePtr, response.GetBytes((long)messagePtr, recvListAddr | ((ulong)PointerBufferSize << 48)));
- }
+ using var responseStream = response.GetStreamTipc();
+ process.CpuMemory.Write(messagePtr, responseStream.GetReadOnlySequence());
+ }
+ else
+ {
+ throw new NotImplementedException(request.Type.ToString());
+ }
- return shouldReply;
+ if (!isTipcCommunication)
+ {
+ 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())
- {
- BinaryWriter writer = new BinaryWriter(ms);
+ const int messageSize = 0x100;
- foreach (int value in values)
- {
- writer.Write(value);
- }
+ byte[] reqData = ArrayPool<byte>.Shared.Rent(messageSize);
- return FillResponse(response, result, ms.ToArray());
- }
+ 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 static IpcMessage FillResponse(IpcMessage response, long result, byte[] data = null)
+ private void FillHipcResponse(IpcMessage response, long result)
{
- response.Type = IpcMessageType.HipcResponse;
+ FillHipcResponse(response, result, ReadOnlySpan<byte>.Empty);
+ }
- using (MemoryStream ms = new MemoryStream())
- {
- BinaryWriter writer = new BinaryWriter(ms);
+ private void FillHipcResponse(IpcMessage response, long result, int value)
+ {
+ Span<byte> span = stackalloc byte[sizeof(int)];
+ BinaryPrimitives.WriteInt32LittleEndian(span, value);
+ FillHipcResponse(response, result, span);
+ }
- writer.Write(IpcMagic.Sfco);
- writer.Write(result);
+ private void FillHipcResponse(IpcMessage response, long result, ReadOnlySpan<byte> data)
+ {
+ response.Type = IpcMessageType.HipcResponse;
- if (data != null)
- {
- writer.Write(data);
- }
+ _responseDataStream.SetLength(0);
- response.RawData = ms.ToArray();
- }
+ _responseDataStream.Write(IpcMagic.Sfco);
+ _responseDataStream.Write(result);
- return response;
+ _responseDataStream.Write(data);
+
+ response.RawData = _responseDataStream.ToArray();
}
protected virtual void Dispose(bool disposing)
@@ -372,6 +385,11 @@ namespace Ryujinx.HLE.HOS.Services
_sessions.Clear();
+ _requestDataReader.Dispose();
+ _requestDataStream.Dispose();
+ _responseDataWriter.Dispose();
+ _responseDataStream.Dispose();
+
InitDone.Dispose();
}
}
diff --git a/Ryujinx.HLE/Utilities/StringUtils.cs b/Ryujinx.HLE/Utilities/StringUtils.cs
index a64d451c..1810b1ad 100644
--- a/Ryujinx.HLE/Utilities/StringUtils.cs
+++ b/Ryujinx.HLE/Utilities/StringUtils.cs
@@ -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());
}
}
diff --git a/Ryujinx.Input/Motion/CemuHook/Client.cs b/Ryujinx.Input/Motion/CemuHook/Client.cs
index f5f7b864..4498b8ca 100644
--- a/Ryujinx.Input/Motion/CemuHook/Client.cs
+++ b/Ryujinx.Input/Motion/CemuHook/Client.cs
@@ -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);
diff --git a/Ryujinx.Memory/IVirtualMemoryManager.cs b/Ryujinx.Memory/IVirtualMemoryManager.cs
index e1851d48..edbfc885 100644
--- a/Ryujinx.Memory/IVirtualMemoryManager.cs
+++ b/Ryujinx.Memory/IVirtualMemoryManager.cs
@@ -1,5 +1,6 @@
using Ryujinx.Memory.Range;
using System;
+using System.Buffers;
using System.Collections.Generic;
namespace Ryujinx.Memory
@@ -78,6 +79,21 @@ namespace Ryujinx.Memory
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.
/// </summary>
diff --git a/Ryujinx/Ui/Windows/AvatarWindow.cs b/Ryujinx/Ui/Windows/AvatarWindow.cs
index fc928bde..0cda890f 100644
--- a/Ryujinx/Ui/Windows/AvatarWindow.cs
+++ b/Ryujinx/Ui/Windows/AvatarWindow.cs
@@ -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());
diff --git a/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs b/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs
index 495c1d1f..a08b5dd1 100644
--- a/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs
+++ b/Ryujinx/Ui/Windows/UserProfilesManagerWindow.cs
@@ -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);