mirror of
				https://github.com/ryujinx-mirror/ryujinx.git
				synced 2025-11-04 08:18:58 -06:00 
			
		
		
		
	Use pooled memory and avoid memory copies (#6691)
* perf: use ByteMemoryPool * feat: KPageTableBase/KPageTable new methods to read and write `ReadOnlySequence<byte>` * new: add IWritableBlock.Write(ulong, ReadOnlySequence<byte>) with default impl * perf: use GetReadOnlySequence() instead of GetSpan() * perf: make `Parcel` IDisposable, use `ByteMemoryPool` for internal allocation, and make Parcel consumers dispose of it * remove comment about copySize * remove unnecessary Clear()
This commit is contained in:
		@@ -1,8 +1,10 @@
 | 
			
		||||
using Ryujinx.Audio.Backends.Common;
 | 
			
		||||
using Ryujinx.Audio.Common;
 | 
			
		||||
using Ryujinx.Common.Logging;
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using Ryujinx.Memory;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
 | 
			
		||||
@@ -87,7 +89,9 @@ namespace Ryujinx.Audio.Backends.SDL2
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            byte[] samples = new byte[frameCount * _bytesPerFrame];
 | 
			
		||||
            using IMemoryOwner<byte> samplesOwner = ByteMemoryPool.Rent(frameCount * _bytesPerFrame);
 | 
			
		||||
 | 
			
		||||
            Span<byte> samples = samplesOwner.Memory.Span;
 | 
			
		||||
 | 
			
		||||
            _ringBuffer.Read(samples, 0, samples.Length);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,10 @@
 | 
			
		||||
using Ryujinx.Audio.Backends.Common;
 | 
			
		||||
using Ryujinx.Audio.Backends.SoundIo.Native;
 | 
			
		||||
using Ryujinx.Audio.Common;
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using Ryujinx.Memory;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.Collections.Concurrent;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
using System.Threading;
 | 
			
		||||
@@ -37,7 +39,7 @@ namespace Ryujinx.Audio.Backends.SoundIo
 | 
			
		||||
            _outputStream = _driver.OpenStream(RequestedSampleFormat, RequestedSampleRate, RequestedChannelCount);
 | 
			
		||||
            _outputStream.WriteCallback += Update;
 | 
			
		||||
            _outputStream.Volume = requestedVolume;
 | 
			
		||||
            // TODO: Setup other callbacks (errors, ect).
 | 
			
		||||
            // TODO: Setup other callbacks (errors, etc.)
 | 
			
		||||
 | 
			
		||||
            _outputStream.Open();
 | 
			
		||||
        }
 | 
			
		||||
@@ -120,7 +122,9 @@ namespace Ryujinx.Audio.Backends.SoundIo
 | 
			
		||||
 | 
			
		||||
            int channelCount = areas.Length;
 | 
			
		||||
 | 
			
		||||
            byte[] samples = new byte[frameCount * bytesPerFrame];
 | 
			
		||||
            using IMemoryOwner<byte> samplesOwner = ByteMemoryPool.Rent(frameCount * bytesPerFrame);
 | 
			
		||||
 | 
			
		||||
            Span<byte> samples = samplesOwner.Memory.Span;
 | 
			
		||||
 | 
			
		||||
            _ringBuffer.Read(samples, 0, samples.Length);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -570,7 +570,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    serverProcess.CpuMemory.Write(copyDst, clientProcess.CpuMemory.GetSpan(copySrc, (int)copySize));
 | 
			
		||||
                    serverProcess.CpuMemory.Write(copyDst, clientProcess.CpuMemory.GetReadOnlySequence(copySrc, (int)copySize));
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (clientResult != Result.Success)
 | 
			
		||||
@@ -858,7 +858,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Ipc
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    clientProcess.CpuMemory.Write(copyDst, serverProcess.CpuMemory.GetSpan(copySrc, (int)copySize));
 | 
			
		||||
                    clientProcess.CpuMemory.Write(copyDst, serverProcess.CpuMemory.GetReadOnlySequence(copySrc, (int)copySize));
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ using Ryujinx.Horizon.Common;
 | 
			
		||||
using Ryujinx.Memory;
 | 
			
		||||
using Ryujinx.Memory.Range;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
 | 
			
		||||
@@ -34,6 +35,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        protected override ReadOnlySequence<byte> GetReadOnlySequence(ulong va, int size)
 | 
			
		||||
        {
 | 
			
		||||
            return _cpuMemory.GetReadOnlySequence(va, size);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        protected override ReadOnlySpan<byte> GetSpan(ulong va, int size)
 | 
			
		||||
        {
 | 
			
		||||
@@ -247,6 +254,12 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 | 
			
		||||
            _cpuMemory.SignalMemoryTracking(va, size, write);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        protected override void Write(ulong va, ReadOnlySequence<byte> data)
 | 
			
		||||
        {
 | 
			
		||||
            _cpuMemory.Write(va, data);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <inheritdoc/>
 | 
			
		||||
        protected override void Write(ulong va, ReadOnlySpan<byte> data)
 | 
			
		||||
        {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ using Ryujinx.Horizon.Common;
 | 
			
		||||
using Ryujinx.Memory;
 | 
			
		||||
using Ryujinx.Memory.Range;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
 | 
			
		||||
@@ -1568,7 +1569,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 | 
			
		||||
 | 
			
		||||
                    while (size > 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        ulong copySize = 0x100000; // Copy chunck size. Any value will do, moderate sizes are recommended.
 | 
			
		||||
                        ulong copySize = int.MaxValue;
 | 
			
		||||
 | 
			
		||||
                        if (copySize > size)
 | 
			
		||||
                        {
 | 
			
		||||
@@ -1577,11 +1578,11 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 | 
			
		||||
 | 
			
		||||
                        if (toServer)
 | 
			
		||||
                        {
 | 
			
		||||
                            currentProcess.CpuMemory.Write(serverAddress, GetSpan(clientAddress, (int)copySize));
 | 
			
		||||
                            currentProcess.CpuMemory.Write(serverAddress, GetReadOnlySequence(clientAddress, (int)copySize));
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            Write(clientAddress, currentProcess.CpuMemory.GetSpan(serverAddress, (int)copySize));
 | 
			
		||||
                            Write(clientAddress, currentProcess.CpuMemory.GetReadOnlySequence(serverAddress, (int)copySize));
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        serverAddress += copySize;
 | 
			
		||||
@@ -1911,9 +1912,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 | 
			
		||||
                    Context.Memory.Fill(GetDramAddressFromPa(dstFirstPagePa), unusedSizeBefore, (byte)_ipcFillValue);
 | 
			
		||||
 | 
			
		||||
                    ulong copySize = addressRounded <= endAddr ? addressRounded - address : size;
 | 
			
		||||
                    var data = srcPageTable.GetSpan(addressTruncated + unusedSizeBefore, (int)copySize);
 | 
			
		||||
                    var data = srcPageTable.GetReadOnlySequence(addressTruncated + unusedSizeBefore, (int)copySize);
 | 
			
		||||
 | 
			
		||||
                    Context.Memory.Write(GetDramAddressFromPa(dstFirstPagePa + unusedSizeBefore), data);
 | 
			
		||||
                    ((IWritableBlock)Context.Memory).Write(GetDramAddressFromPa(dstFirstPagePa + unusedSizeBefore), data);
 | 
			
		||||
 | 
			
		||||
                    firstPageFillAddress += unusedSizeBefore + copySize;
 | 
			
		||||
 | 
			
		||||
@@ -1977,9 +1978,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 | 
			
		||||
                if (send)
 | 
			
		||||
                {
 | 
			
		||||
                    ulong copySize = endAddr - endAddrTruncated;
 | 
			
		||||
                    var data = srcPageTable.GetSpan(endAddrTruncated, (int)copySize);
 | 
			
		||||
                    var data = srcPageTable.GetReadOnlySequence(endAddrTruncated, (int)copySize);
 | 
			
		||||
 | 
			
		||||
                    Context.Memory.Write(GetDramAddressFromPa(dstLastPagePa), data);
 | 
			
		||||
                    ((IWritableBlock)Context.Memory).Write(GetDramAddressFromPa(dstLastPagePa), data);
 | 
			
		||||
 | 
			
		||||
                    lastPageFillAddr += copySize;
 | 
			
		||||
 | 
			
		||||
@@ -2943,6 +2944,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 | 
			
		||||
        /// <param name="pageList">Page list where the ranges will be added</param>
 | 
			
		||||
        protected abstract void GetPhysicalRegions(ulong va, ulong size, KPageList pageList);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets a read-only sequence of data from CPU mapped memory.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <remarks>
 | 
			
		||||
        /// Allows reading non-contiguous memory without first copying it to a newly allocated single contiguous block.
 | 
			
		||||
        /// </remarks>
 | 
			
		||||
        /// <param name="va">Virtual address of the data</param>
 | 
			
		||||
        /// <param name="size">Size of the data</param>
 | 
			
		||||
        /// <returns>A read-only sequence of the data</returns>
 | 
			
		||||
        /// <exception cref="Ryujinx.Memory.InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
 | 
			
		||||
        protected abstract ReadOnlySequence<byte> GetReadOnlySequence(ulong va, int size);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets a read-only span of data from CPU mapped memory.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
@@ -2952,7 +2965,6 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 | 
			
		||||
        /// </remarks>
 | 
			
		||||
        /// <param name="va">Virtual address of the data</param>
 | 
			
		||||
        /// <param name="size">Size of the data</param>
 | 
			
		||||
        /// <param name="tracked">True if read tracking is triggered on the span</param>
 | 
			
		||||
        /// <returns>A read-only span of the data</returns>
 | 
			
		||||
        /// <exception cref="Ryujinx.Memory.InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
 | 
			
		||||
        protected abstract ReadOnlySpan<byte> GetSpan(ulong va, int size);
 | 
			
		||||
@@ -3060,6 +3072,14 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
 | 
			
		||||
        /// <param name="size">Size of the region</param>
 | 
			
		||||
        protected abstract void SignalMemoryTracking(ulong va, ulong size, bool write);
 | 
			
		||||
 | 
			
		||||
        /// <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="Ryujinx.Memory.InvalidMemoryRegionException">Throw for unhandled invalid or unmapped memory accesses</exception>
 | 
			
		||||
        protected abstract void Write(ulong va, ReadOnlySequence<byte> data);
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Writes data to CPU mapped memory, with write tracking.
 | 
			
		||||
        /// </summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -13,10 +13,10 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
 | 
			
		||||
 | 
			
		||||
        ResultCode OnTransact(uint code, uint flags, ReadOnlySpan<byte> inputParcel, Span<byte> outputParcel)
 | 
			
		||||
        {
 | 
			
		||||
            Parcel inputParcelReader = new(inputParcel.ToArray());
 | 
			
		||||
            using Parcel inputParcelReader = new(inputParcel);
 | 
			
		||||
 | 
			
		||||
            // TODO: support objects?
 | 
			
		||||
            Parcel outputParcelWriter = new((uint)(outputParcel.Length - Unsafe.SizeOf<ParcelHeader>()), 0);
 | 
			
		||||
            using Parcel outputParcelWriter = new((uint)(outputParcel.Length - Unsafe.SizeOf<ParcelHeader>()), 0);
 | 
			
		||||
 | 
			
		||||
            string inputInterfaceToken = inputParcelReader.ReadInterfaceToken();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,9 @@
 | 
			
		||||
using Ryujinx.Common;
 | 
			
		||||
using Ryujinx.Common.Memory;
 | 
			
		||||
using Ryujinx.Common.Utilities;
 | 
			
		||||
using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
using System.Diagnostics;
 | 
			
		||||
using System.Runtime.CompilerServices;
 | 
			
		||||
using System.Runtime.InteropServices;
 | 
			
		||||
@@ -9,13 +11,13 @@ using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
 | 
			
		||||
{
 | 
			
		||||
    class Parcel
 | 
			
		||||
    sealed class Parcel : IDisposable
 | 
			
		||||
    {
 | 
			
		||||
        private readonly byte[] _rawData;
 | 
			
		||||
        private readonly IMemoryOwner<byte> _rawDataOwner;
 | 
			
		||||
 | 
			
		||||
        private Span<byte> Raw => new(_rawData);
 | 
			
		||||
        private Span<byte> Raw => _rawDataOwner.Memory.Span;
 | 
			
		||||
 | 
			
		||||
        private ref ParcelHeader Header => ref MemoryMarshal.Cast<byte, ParcelHeader>(_rawData)[0];
 | 
			
		||||
        private ref ParcelHeader Header => ref MemoryMarshal.Cast<byte, ParcelHeader>(Raw)[0];
 | 
			
		||||
 | 
			
		||||
        private Span<byte> Payload => Raw.Slice((int)Header.PayloadOffset, (int)Header.PayloadSize);
 | 
			
		||||
 | 
			
		||||
@@ -24,9 +26,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
 | 
			
		||||
        private int _payloadPosition;
 | 
			
		||||
        private int _objectPosition;
 | 
			
		||||
 | 
			
		||||
        public Parcel(byte[] rawData)
 | 
			
		||||
        private bool _isDisposed;
 | 
			
		||||
 | 
			
		||||
        public Parcel(ReadOnlySpan<byte> data)
 | 
			
		||||
        {
 | 
			
		||||
            _rawData = rawData;
 | 
			
		||||
            _rawDataOwner = ByteMemoryPool.RentCopy(data);
 | 
			
		||||
 | 
			
		||||
            _payloadPosition = 0;
 | 
			
		||||
            _objectPosition = 0;
 | 
			
		||||
@@ -36,7 +40,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
 | 
			
		||||
        {
 | 
			
		||||
            uint headerSize = (uint)Unsafe.SizeOf<ParcelHeader>();
 | 
			
		||||
 | 
			
		||||
            _rawData = new byte[BitUtils.AlignUp<uint>(headerSize + payloadSize + objectsSize, 4)];
 | 
			
		||||
            _rawDataOwner = ByteMemoryPool.RentCleared(BitUtils.AlignUp<uint>(headerSize + payloadSize + objectsSize, 4));
 | 
			
		||||
 | 
			
		||||
            Header.PayloadSize = payloadSize;
 | 
			
		||||
            Header.ObjectsSize = objectsSize;
 | 
			
		||||
@@ -132,7 +136,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
 | 
			
		||||
 | 
			
		||||
            // TODO: figure out what this value is
 | 
			
		||||
 | 
			
		||||
            WriteInplaceObject(new byte[4] { 0, 0, 0, 0 });
 | 
			
		||||
            Span<byte> fourBytes = stackalloc byte[4];
 | 
			
		||||
 | 
			
		||||
            WriteInplaceObject(fourBytes);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public AndroidStrongPointer<T> ReadStrongPointer<T>() where T : unmanaged, IFlattenable
 | 
			
		||||
@@ -219,5 +225,15 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
 | 
			
		||||
 | 
			
		||||
            return Raw[..(int)(Header.PayloadSize + Header.ObjectsSize + Unsafe.SizeOf<ParcelHeader>())];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Dispose()
 | 
			
		||||
        {
 | 
			
		||||
            if (!_isDisposed)
 | 
			
		||||
            {
 | 
			
		||||
                _isDisposed = true;
 | 
			
		||||
 | 
			
		||||
                _rawDataOwner.Dispose();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -250,7 +250,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
 | 
			
		||||
 | 
			
		||||
            context.Device.System.SurfaceFlinger.SetRenderLayer(layerId);
 | 
			
		||||
 | 
			
		||||
            Parcel parcel = new(0x28, 0x4);
 | 
			
		||||
            using Parcel parcel = new(0x28, 0x4);
 | 
			
		||||
 | 
			
		||||
            parcel.WriteObject(producer, "dispdrv\0");
 | 
			
		||||
 | 
			
		||||
@@ -288,7 +288,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
 | 
			
		||||
 | 
			
		||||
            context.Device.System.SurfaceFlinger.SetRenderLayer(layerId);
 | 
			
		||||
 | 
			
		||||
            Parcel parcel = new(0x28, 0x4);
 | 
			
		||||
            using Parcel parcel = new(0x28, 0x4);
 | 
			
		||||
 | 
			
		||||
            parcel.WriteObject(producer, "dispdrv\0");
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,25 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Buffers;
 | 
			
		||||
 | 
			
		||||
namespace Ryujinx.Memory
 | 
			
		||||
{
 | 
			
		||||
    public interface IWritableBlock
 | 
			
		||||
    {
 | 
			
		||||
        /// <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>
 | 
			
		||||
        void Write(ulong va, ReadOnlySequence<byte> data)
 | 
			
		||||
        {
 | 
			
		||||
            foreach (ReadOnlyMemory<byte> segment in data)
 | 
			
		||||
            {
 | 
			
		||||
                Write(va, segment.Span);
 | 
			
		||||
                va += (ulong)segment.Length;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void Write(ulong va, ReadOnlySpan<byte> data);
 | 
			
		||||
 | 
			
		||||
        void WriteUntracked(ulong va, ReadOnlySpan<byte> data) => Write(va, data);
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user