Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
9b94662b4b | ||
|
216026c096 | ||
|
7070cf6ae5 | ||
|
9839cd56fb | ||
|
99f46e22e2 |
@@ -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);
|
||||
|
||||
|
@@ -212,7 +212,7 @@ namespace Ryujinx.Audio.Renderer.Server
|
||||
/// <summary>
|
||||
/// Check if the audio renderer should fix the GC-ADPCM context not being provided to the DSP.
|
||||
/// </summary>
|
||||
/// <returns>True if if the audio renderer should fix it.</returns>
|
||||
/// <returns>True if the audio renderer should fix it.</returns>
|
||||
public bool IsAdpcmLoopContextBugFixed()
|
||||
{
|
||||
return CheckFeatureSupported(UserRevision, BaseRevisionMagic + Revision2);
|
||||
|
@@ -44,7 +44,7 @@ namespace Ryujinx.Common.Extensions
|
||||
/// <remarks>
|
||||
/// DO NOT use <paramref name="copyDestinationIfRequiredDoNotUse"/> after calling this method, as it will only
|
||||
/// contain a value if the value couldn't be referenced directly because it spans multiple <see cref="ReadOnlyMemory{Byte}"/> segments.
|
||||
/// To discourage use, it is recommended to to call this method like the following:
|
||||
/// To discourage use, it is recommended to call this method like the following:
|
||||
/// <c>
|
||||
/// ref readonly MyStruct value = ref sequenceReader.GetRefOrRefToCopy{MyStruct}(out _);
|
||||
/// </c>
|
||||
|
@@ -85,6 +85,70 @@ namespace Ryujinx.Cpu.Jit
|
||||
_addressSpace = new(Tracking, backingMemory, _nativePageTable, useProtectionMirrors);
|
||||
}
|
||||
|
||||
public override ReadOnlySequence<byte> GetReadOnlySequence(ulong va, int size, bool tracked = false)
|
||||
{
|
||||
if (size == 0)
|
||||
{
|
||||
return ReadOnlySequence<byte>.Empty;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (tracked)
|
||||
{
|
||||
SignalMemoryTracking(va, (ulong)size, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertValidAddressAndSize(va, (ulong)size);
|
||||
}
|
||||
|
||||
ulong endVa = va + (ulong)size;
|
||||
int offset = 0;
|
||||
|
||||
BytesReadOnlySequenceSegment first = null, last = null;
|
||||
|
||||
while (va < endVa)
|
||||
{
|
||||
(MemoryBlock memory, ulong rangeOffset, ulong copySize) = GetMemoryOffsetAndSize(va, (ulong)(size - offset));
|
||||
|
||||
Memory<byte> physicalMemory = memory.GetMemory(rangeOffset, (int)copySize);
|
||||
|
||||
if (first is null)
|
||||
{
|
||||
first = last = new BytesReadOnlySequenceSegment(physicalMemory);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (last.IsContiguousWith(physicalMemory, out nuint contiguousStart, out int contiguousSize))
|
||||
{
|
||||
Memory<byte> contiguousPhysicalMemory = new NativeMemoryManager<byte>(contiguousStart, contiguousSize).Memory;
|
||||
|
||||
last.Replace(contiguousPhysicalMemory);
|
||||
}
|
||||
else
|
||||
{
|
||||
last = last.Append(physicalMemory);
|
||||
}
|
||||
}
|
||||
|
||||
va += copySize;
|
||||
offset += (int)copySize;
|
||||
}
|
||||
|
||||
return new ReadOnlySequence<byte>(first, 0, last, (int)(size - last.RunningIndex));
|
||||
}
|
||||
catch (InvalidMemoryRegionException)
|
||||
{
|
||||
if (_invalidAccessHandler == null || !_invalidAccessHandler(va))
|
||||
{
|
||||
throw;
|
||||
}
|
||||
|
||||
return ReadOnlySequence<byte>.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Map(ulong va, ulong pa, ulong size, MemoryMapFlags flags)
|
||||
{
|
||||
|
@@ -157,6 +157,9 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
||||
}
|
||||
else if (operation == SyncpointbOperation.Incr)
|
||||
{
|
||||
// "Unbind" render targets since a syncpoint increment might indicate future CPU access for the textures.
|
||||
_parent.TextureManager.RefreshModifiedTextures();
|
||||
|
||||
_context.CreateHostSyncIfNeeded(HostSyncFlags.StrictSyncpoint);
|
||||
_context.Synchronization.IncrementSyncpoint(syncpointId);
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ using Ryujinx.Graphics.Gpu.Engine.Dma;
|
||||
using Ryujinx.Graphics.Gpu.Engine.InlineToMemory;
|
||||
using Ryujinx.Graphics.Gpu.Engine.Threed;
|
||||
using Ryujinx.Graphics.Gpu.Engine.Twod;
|
||||
using Ryujinx.Graphics.Gpu.Image;
|
||||
using Ryujinx.Graphics.Gpu.Memory;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
@@ -28,6 +29,11 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo
|
||||
/// </summary>
|
||||
public MemoryManager MemoryManager => _channel.MemoryManager;
|
||||
|
||||
/// <summary>
|
||||
/// Channel texture manager.
|
||||
/// </summary>
|
||||
public TextureManager TextureManager => _channel.TextureManager;
|
||||
|
||||
/// <summary>
|
||||
/// 3D Engine.
|
||||
/// </summary>
|
||||
|
@@ -302,12 +302,12 @@ namespace Ryujinx.Graphics.Vulkan
|
||||
SubmitInfo sInfo = new()
|
||||
{
|
||||
SType = StructureType.SubmitInfo,
|
||||
WaitSemaphoreCount = waitSemaphores != null ? (uint)waitSemaphores.Length : 0,
|
||||
WaitSemaphoreCount = !waitSemaphores.IsEmpty ? (uint)waitSemaphores.Length : 0,
|
||||
PWaitSemaphores = pWaitSemaphores,
|
||||
PWaitDstStageMask = pWaitDstStageMask,
|
||||
CommandBufferCount = 1,
|
||||
PCommandBuffers = &commandBuffer,
|
||||
SignalSemaphoreCount = signalSemaphores != null ? (uint)signalSemaphores.Length : 0,
|
||||
SignalSemaphoreCount = !signalSemaphores.IsEmpty ? (uint)signalSemaphores.Length : 0,
|
||||
PSignalSemaphores = pSignalSemaphores,
|
||||
};
|
||||
|
||||
|
@@ -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>
|
||||
|
@@ -60,7 +60,7 @@ namespace Ryujinx.HLE.HOS.Services.Fatal
|
||||
errorReport.AppendLine($"\tResultCode: {((int)resultCode & 0x1FF) + 2000}-{((int)resultCode >> 9) & 0x3FFF:d4}");
|
||||
errorReport.AppendLine($"\tFatalPolicy: {fatalPolicy}");
|
||||
|
||||
if (cpuContext != null)
|
||||
if (!cpuContext.IsEmpty)
|
||||
{
|
||||
errorReport.AppendLine("CPU Context:");
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -8,6 +8,11 @@ namespace Ryujinx.Memory
|
||||
private readonly T* _pointer;
|
||||
private readonly int _length;
|
||||
|
||||
public NativeMemoryManager(nuint pointer, int length)
|
||||
: this((T*)pointer, length)
|
||||
{
|
||||
}
|
||||
|
||||
public NativeMemoryManager(T* pointer, int length)
|
||||
{
|
||||
_pointer = pointer;
|
||||
|
Reference in New Issue
Block a user