From f98af9e175c08c9eb8284ed2d6a82a76710ead8e Mon Sep 17 00:00:00 2001 From: LotP <22-lotp@users.noreply.git.ryujinx.app> Date: Sun, 26 Oct 2025 14:14:51 -0500 Subject: [PATCH] gpu allocation optimizations ObjectPool now uses ConcurrentBag instead if ConcurrentStack, as it has a smaller memory footprint. Fix compiler warnings related to Audio Command Pools. Switch gpu command initialization to use pointers, that way skipping the allocation of the command which is unnecessary. Skip byte array allocation in Ioctl2/3 if it isn't needed (if the source data is all continuous we don't need to copy it to make it continuous). --- .../Renderer/Server/CommandBuffer.cs | 8 - src/Ryujinx.Common/Pools/ObjectPool.cs | 6 +- src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs | 5 + src/Ryujinx.Cpu/Jit/MemoryManager.cs | 5 + .../Jit/MemoryManagerHostMapped.cs | 5 + .../Jit/MemoryManagerHostTracked.cs | 5 + .../Resources/ThreadedCounterEvent.cs | 4 +- .../Resources/ThreadedImageArray.cs | 8 +- .../Resources/ThreadedProgram.cs | 12 +- .../Resources/ThreadedSampler.cs | 4 +- .../Resources/ThreadedTexture.cs | 48 ++-- .../Resources/ThreadedTextureArray.cs | 12 +- .../Multithreading/ThreadedPipeline.cs | 222 +++++++++--------- .../Multithreading/ThreadedRenderer.cs | 81 +++---- .../Multithreading/ThreadedWindow.cs | 4 +- .../Memory/MemoryManager.cs | 17 +- .../HOS/Services/Nv/INvDrvServices.cs | 38 +-- src/Ryujinx.Memory/IVirtualMemoryManager.cs | 9 + .../VirtualMemoryManagerBase.cs | 29 +++ .../MockVirtualMemoryManager.cs | 5 + 20 files changed, 295 insertions(+), 232 deletions(-) diff --git a/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs b/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs index 1a20f9751..b6f435860 100644 --- a/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs +++ b/src/Ryujinx.Audio/Renderer/Server/CommandBuffer.cs @@ -100,7 +100,6 @@ namespace Ryujinx.Audio.Renderer.Server break; case CommandType.BiquadFilterFloatCoeff: throw new NotImplementedException(); - break; case CommandType.Mix: _mixCommandPool.Release((MixCommand)command); break; @@ -160,7 +159,6 @@ namespace Ryujinx.Audio.Renderer.Server break; case CommandType.MultiTapBiquadFilterFloatCoeff: throw new NotImplementedException(); - break; case CommandType.CaptureBuffer: _captureBufferCommandPool.Release((CaptureBufferCommand)command); break; @@ -172,25 +170,19 @@ namespace Ryujinx.Audio.Renderer.Server break; case CommandType.BiquadFilterAndMixFloatCoeff: throw new NotImplementedException(); - break; case CommandType.MultiTapBiquadFilterAndMix: _multiTapBiquadFilterAndMixCommandPool.Release((MultiTapBiquadFilterAndMixCommand)command); break; case CommandType.MultiTapBiquadFilterAndMixFloatCoef: throw new NotImplementedException(); - break; case CommandType.AuxiliaryBufferGrouped: throw new NotImplementedException(); - break; case CommandType.FillMixBuffer: throw new NotImplementedException(); - break; case CommandType.BiquadFilterCrossFade: throw new NotImplementedException(); - break; case CommandType.MultiTapBiquadFilterCrossFade: throw new NotImplementedException(); - break; case CommandType.FillBuffer: _fillBufferCommandPool.Release((FillBufferCommand)command); break; diff --git a/src/Ryujinx.Common/Pools/ObjectPool.cs b/src/Ryujinx.Common/Pools/ObjectPool.cs index 65baa00f9..dd2a708ea 100644 --- a/src/Ryujinx.Common/Pools/ObjectPool.cs +++ b/src/Ryujinx.Common/Pools/ObjectPool.cs @@ -8,11 +8,11 @@ namespace Ryujinx.Common where T : class { private int _size = size; - private readonly ConcurrentStack _items = new(); + private readonly ConcurrentBag _items = new(); public T Allocate() { - bool success = _items.TryPop(out T instance); + bool success = _items.TryTake(out T instance); if (!success) { @@ -26,7 +26,7 @@ namespace Ryujinx.Common { if (_size < 0 || _items.Count < _size) { - _items.Push(obj); + _items.Add(obj); } } diff --git a/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs b/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs index c58e87d44..58066b46d 100644 --- a/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs +++ b/src/Ryujinx.Cpu/AppleHv/HvMemoryManager.cs @@ -151,6 +151,11 @@ namespace Ryujinx.Cpu.AppleHv } } + public override bool TryReadUnsafe(ulong va, int length, out Span data) + { + throw new NotImplementedException(); + } + public override void Write(ulong va, ReadOnlySpan data) { try diff --git a/src/Ryujinx.Cpu/Jit/MemoryManager.cs b/src/Ryujinx.Cpu/Jit/MemoryManager.cs index 8d41be736..bd7e03558 100644 --- a/src/Ryujinx.Cpu/Jit/MemoryManager.cs +++ b/src/Ryujinx.Cpu/Jit/MemoryManager.cs @@ -175,6 +175,11 @@ namespace Ryujinx.Cpu.Jit } } + public override bool TryReadUnsafe(ulong va, int length, out Span data) + { + throw new NotImplementedException(); + } + public override void Write(ulong va, ReadOnlySpan data) { try diff --git a/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs b/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs index 4639ab913..81ae985d6 100644 --- a/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs +++ b/src/Ryujinx.Cpu/Jit/MemoryManagerHostMapped.cs @@ -152,6 +152,11 @@ namespace Ryujinx.Cpu.Jit } } + public override bool TryReadUnsafe(ulong va, int length, out Span data) + { + throw new NotImplementedException(); + } + public override T ReadTracked(ulong va) { try diff --git a/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs b/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs index 3065ec82c..a8ae0b444 100644 --- a/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs +++ b/src/Ryujinx.Cpu/Jit/MemoryManagerHostTracked.cs @@ -233,6 +233,11 @@ namespace Ryujinx.Cpu.Jit } } + public override bool TryReadUnsafe(ulong va, int length, out Span data) + { + throw new NotImplementedException(); + } + public override bool WriteWithRedundancyCheck(ulong va, ReadOnlySpan data) { if (data.Length == 0) diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs index 15db2b9fa..557c2ae04 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedCounterEvent.cs @@ -29,9 +29,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources return new TableRef(_renderer, reference); } - public void Dispose() + public unsafe void Dispose() { - _renderer.New().Set(Ref(this)); + _renderer.New()->Set(Ref(this)); _renderer.QueueCommand(); } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedImageArray.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedImageArray.cs index 82587c189..a415dbbf0 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedImageArray.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedImageArray.cs @@ -21,15 +21,15 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources return new TableRef(_renderer, reference); } - public void Dispose() + public unsafe void Dispose() { - _renderer.New().Set(Ref(this)); + _renderer.New()->Set(Ref(this)); _renderer.QueueCommand(); } - public void SetImages(int index, ITexture[] images) + public unsafe void SetImages(int index, ITexture[] images) { - _renderer.New().Set(Ref(this), index, Ref(images)); + _renderer.New()->Set(Ref(this), index, Ref(images)); _renderer.QueueCommand(); } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedProgram.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedProgram.cs index d5a22f669..9573bbaac 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedProgram.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedProgram.cs @@ -21,25 +21,25 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources return new TableRef(_renderer, reference); } - public void Dispose() + public unsafe void Dispose() { - _renderer.New().Set(Ref(this)); + _renderer.New()->Set(Ref(this)); _renderer.QueueCommand(); } - public byte[] GetBinary() + public unsafe byte[] GetBinary() { ResultBox box = new(); - _renderer.New().Set(Ref(this), Ref(box)); + _renderer.New()->Set(Ref(this), Ref(box)); _renderer.InvokeCommand(); return box.Result; } - public ProgramLinkStatus CheckProgramLink(bool blocking) + public unsafe ProgramLinkStatus CheckProgramLink(bool blocking) { ResultBox box = new(); - _renderer.New().Set(Ref(this), blocking, Ref(box)); + _renderer.New()->Set(Ref(this), blocking, Ref(box)); _renderer.InvokeCommand(); return box.Result; diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedSampler.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedSampler.cs index ae3d993a3..8dc9647ab 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedSampler.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedSampler.cs @@ -13,9 +13,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources _renderer = renderer; } - public void Dispose() + public unsafe void Dispose() { - _renderer.New().Set(new TableRef(_renderer, this)); + _renderer.New()->Set(new TableRef(_renderer, this)); _renderer.QueueCommand(); } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs index fa71d20b3..e33b58745 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTexture.cs @@ -28,25 +28,25 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources return new TableRef(_renderer, reference); } - public void CopyTo(ITexture destination, int firstLayer, int firstLevel) + public unsafe void CopyTo(ITexture destination, int firstLayer, int firstLevel) { - _renderer.New().Set(Ref(this), Ref((ThreadedTexture)destination), firstLayer, firstLevel); + _renderer.New()->Set(Ref(this), Ref((ThreadedTexture)destination), firstLayer, firstLevel); _renderer.QueueCommand(); } - public void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) + public unsafe void CopyTo(ITexture destination, int srcLayer, int dstLayer, int srcLevel, int dstLevel) { - _renderer.New().Set(Ref(this), Ref((ThreadedTexture)destination), srcLayer, dstLayer, srcLevel, dstLevel); + _renderer.New()->Set(Ref(this), Ref((ThreadedTexture)destination), srcLayer, dstLayer, srcLevel, dstLevel); _renderer.QueueCommand(); } - public void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) + public unsafe void CopyTo(ITexture destination, Extents2D srcRegion, Extents2D dstRegion, bool linearFilter) { ThreadedTexture dest = (ThreadedTexture)destination; if (_renderer.IsGpuThread()) { - _renderer.New().Set(Ref(this), Ref(dest), srcRegion, dstRegion, linearFilter); + _renderer.New()->Set(Ref(this), Ref(dest), srcRegion, dstRegion, linearFilter); _renderer.QueueCommand(); } else @@ -59,21 +59,21 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources } } - public ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) + public unsafe ITexture CreateView(TextureCreateInfo info, int firstLayer, int firstLevel) { ThreadedTexture newTex = new(_renderer, info); - _renderer.New().Set(Ref(this), Ref(newTex), info, firstLayer, firstLevel); + _renderer.New()->Set(Ref(this), Ref(newTex), info, firstLayer, firstLevel); _renderer.QueueCommand(); return newTex; } - public PinnedSpan GetData() + public unsafe PinnedSpan GetData() { if (_renderer.IsGpuThread()) { ResultBox> box = new(); - _renderer.New().Set(Ref(this), Ref(box)); + _renderer.New()->Set(Ref(this), Ref(box)); _renderer.InvokeCommand(); return box.Result; @@ -86,12 +86,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources } } - public PinnedSpan GetData(int layer, int level) + public unsafe PinnedSpan GetData(int layer, int level) { if (_renderer.IsGpuThread()) { ResultBox> box = new(); - _renderer.New().Set(Ref(this), Ref(box), layer, level); + _renderer.New()->Set(Ref(this), Ref(box), layer, level); _renderer.InvokeCommand(); return box.Result; @@ -104,42 +104,42 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources } } - public void CopyTo(BufferRange range, int layer, int level, int stride) + public unsafe void CopyTo(BufferRange range, int layer, int level, int stride) { - _renderer.New().Set(Ref(this), range, layer, level, stride); + _renderer.New()->Set(Ref(this), range, layer, level, stride); _renderer.QueueCommand(); } /// - public void SetData(MemoryOwner data) + public unsafe void SetData(MemoryOwner data) { - _renderer.New().Set(Ref(this), Ref(data)); + _renderer.New()->Set(Ref(this), Ref(data)); _renderer.QueueCommand(); } /// - public void SetData(MemoryOwner data, int layer, int level) + public unsafe void SetData(MemoryOwner data, int layer, int level) { - _renderer.New().Set(Ref(this), Ref(data), layer, level); + _renderer.New()->Set(Ref(this), Ref(data), layer, level); _renderer.QueueCommand(); } /// - public void SetData(MemoryOwner data, int layer, int level, Rectangle region) + public unsafe void SetData(MemoryOwner data, int layer, int level, Rectangle region) { - _renderer.New().Set(Ref(this), Ref(data), layer, level, region); + _renderer.New()->Set(Ref(this), Ref(data), layer, level, region); _renderer.QueueCommand(); } - public void SetStorage(BufferRange buffer) + public unsafe void SetStorage(BufferRange buffer) { - _renderer.New().Set(Ref(this), buffer); + _renderer.New()->Set(Ref(this), buffer); _renderer.QueueCommand(); } - public void Release() + public unsafe void Release() { - _renderer.New().Set(Ref(this)); + _renderer.New()->Set(Ref(this)); _renderer.QueueCommand(); } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTextureArray.cs b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTextureArray.cs index 4334c7048..56b0e0d34 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTextureArray.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/Resources/ThreadedTextureArray.cs @@ -22,21 +22,21 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources return new TableRef(_renderer, reference); } - public void Dispose() + public unsafe void Dispose() { - _renderer.New().Set(Ref(this)); + _renderer.New()->Set(Ref(this)); _renderer.QueueCommand(); } - public void SetSamplers(int index, ISampler[] samplers) + public unsafe void SetSamplers(int index, ISampler[] samplers) { - _renderer.New().Set(Ref(this), index, Ref(samplers.ToArray())); + _renderer.New()->Set(Ref(this), index, Ref(samplers.ToArray())); _renderer.QueueCommand(); } - public void SetTextures(int index, ITexture[] textures) + public unsafe void SetTextures(int index, ITexture[] textures) { - _renderer.New().Set(Ref(this), index, Ref(textures.ToArray())); + _renderer.New()->Set(Ref(this), index, Ref(textures.ToArray())); _renderer.QueueCommand(); } } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs index deec36648..1c9b962f9 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedPipeline.cs @@ -21,343 +21,343 @@ namespace Ryujinx.Graphics.GAL.Multithreading return new TableRef(_renderer, reference); } - public void Barrier() + public unsafe void Barrier() { _renderer.New(); _renderer.QueueCommand(); } - public void BeginTransformFeedback(PrimitiveTopology topology) + public unsafe void BeginTransformFeedback(PrimitiveTopology topology) { - _renderer.New().Set(topology); + _renderer.New()->Set(topology); _renderer.QueueCommand(); } - public void ClearBuffer(BufferHandle destination, int offset, int size, uint value) + public unsafe void ClearBuffer(BufferHandle destination, int offset, int size, uint value) { - _renderer.New().Set(destination, offset, size, value); + _renderer.New()->Set(destination, offset, size, value); _renderer.QueueCommand(); } - public void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color) + public unsafe void ClearRenderTargetColor(int index, int layer, int layerCount, uint componentMask, ColorF color) { - _renderer.New().Set(index, layer, layerCount, componentMask, color); + _renderer.New()->Set(index, layer, layerCount, componentMask, color); _renderer.QueueCommand(); } - public void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) + public unsafe void ClearRenderTargetDepthStencil(int layer, int layerCount, float depthValue, bool depthMask, int stencilValue, int stencilMask) { - _renderer.New().Set(layer, layerCount, depthValue, depthMask, stencilValue, stencilMask); + _renderer.New()->Set(layer, layerCount, depthValue, depthMask, stencilValue, stencilMask); _renderer.QueueCommand(); } - public void CommandBufferBarrier() + public unsafe void CommandBufferBarrier() { _renderer.New(); _renderer.QueueCommand(); } - public void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) + public unsafe void CopyBuffer(BufferHandle source, BufferHandle destination, int srcOffset, int dstOffset, int size) { - _renderer.New().Set(source, destination, srcOffset, dstOffset, size); + _renderer.New()->Set(source, destination, srcOffset, dstOffset, size); _renderer.QueueCommand(); } - public void DispatchCompute(int groupsX, int groupsY, int groupsZ) + public unsafe void DispatchCompute(int groupsX, int groupsY, int groupsZ) { - _renderer.New().Set(groupsX, groupsY, groupsZ); + _renderer.New()->Set(groupsX, groupsY, groupsZ); _renderer.QueueCommand(); } - public void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) + public unsafe void Draw(int vertexCount, int instanceCount, int firstVertex, int firstInstance) { - _renderer.New().Set(vertexCount, instanceCount, firstVertex, firstInstance); + _renderer.New()->Set(vertexCount, instanceCount, firstVertex, firstInstance); _renderer.QueueCommand(); } - public void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) + public unsafe void DrawIndexed(int indexCount, int instanceCount, int firstIndex, int firstVertex, int firstInstance) { - _renderer.New().Set(indexCount, instanceCount, firstIndex, firstVertex, firstInstance); + _renderer.New()->Set(indexCount, instanceCount, firstIndex, firstVertex, firstInstance); _renderer.QueueCommand(); } - public void DrawIndexedIndirect(BufferRange indirectBuffer) + public unsafe void DrawIndexedIndirect(BufferRange indirectBuffer) { - _renderer.New().Set(indirectBuffer); + _renderer.New()->Set(indirectBuffer); _renderer.QueueCommand(); } - public void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) + public unsafe void DrawIndexedIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) { - _renderer.New().Set(indirectBuffer, parameterBuffer, maxDrawCount, stride); + _renderer.New()->Set(indirectBuffer, parameterBuffer, maxDrawCount, stride); _renderer.QueueCommand(); } - public void DrawIndirect(BufferRange indirectBuffer) + public unsafe void DrawIndirect(BufferRange indirectBuffer) { - _renderer.New().Set(indirectBuffer); + _renderer.New()->Set(indirectBuffer); _renderer.QueueCommand(); } - public void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) + public unsafe void DrawIndirectCount(BufferRange indirectBuffer, BufferRange parameterBuffer, int maxDrawCount, int stride) { - _renderer.New().Set(indirectBuffer, parameterBuffer, maxDrawCount, stride); + _renderer.New()->Set(indirectBuffer, parameterBuffer, maxDrawCount, stride); _renderer.QueueCommand(); } - public void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion) + public unsafe void DrawTexture(ITexture texture, ISampler sampler, Extents2DF srcRegion, Extents2DF dstRegion) { - _renderer.New().Set(Ref(texture), Ref(sampler), srcRegion, dstRegion); + _renderer.New()->Set(Ref(texture), Ref(sampler), srcRegion, dstRegion); _renderer.QueueCommand(); } - public void EndHostConditionalRendering() + public unsafe void EndHostConditionalRendering() { _renderer.New(); _renderer.QueueCommand(); } - public void EndTransformFeedback() + public unsafe void EndTransformFeedback() { _renderer.New(); _renderer.QueueCommand(); } - public void SetAlphaTest(bool enable, float reference, CompareOp op) + public unsafe void SetAlphaTest(bool enable, float reference, CompareOp op) { - _renderer.New().Set(enable, reference, op); + _renderer.New()->Set(enable, reference, op); _renderer.QueueCommand(); } - public void SetBlendState(AdvancedBlendDescriptor blend) + public unsafe void SetBlendState(AdvancedBlendDescriptor blend) { - _renderer.New().Set(blend); + _renderer.New()->Set(blend); _renderer.QueueCommand(); } - public void SetBlendState(int index, BlendDescriptor blend) + public unsafe void SetBlendState(int index, BlendDescriptor blend) { - _renderer.New().Set(index, blend); + _renderer.New()->Set(index, blend); _renderer.QueueCommand(); } - public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp) + public unsafe void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp) { - _renderer.New().Set(enables, factor, units, clamp); + _renderer.New()->Set(enables, factor, units, clamp); _renderer.QueueCommand(); } - public void SetDepthClamp(bool clamp) + public unsafe void SetDepthClamp(bool clamp) { - _renderer.New().Set(clamp); + _renderer.New()->Set(clamp); _renderer.QueueCommand(); } - public void SetDepthMode(DepthMode mode) + public unsafe void SetDepthMode(DepthMode mode) { - _renderer.New().Set(mode); + _renderer.New()->Set(mode); _renderer.QueueCommand(); } - public void SetDepthTest(DepthTestDescriptor depthTest) + public unsafe void SetDepthTest(DepthTestDescriptor depthTest) { - _renderer.New().Set(depthTest); + _renderer.New()->Set(depthTest); _renderer.QueueCommand(); } - public void SetFaceCulling(bool enable, Face face) + public unsafe void SetFaceCulling(bool enable, Face face) { - _renderer.New().Set(enable, face); + _renderer.New()->Set(enable, face); _renderer.QueueCommand(); } - public void SetFrontFace(FrontFace frontFace) + public unsafe void SetFrontFace(FrontFace frontFace) { - _renderer.New().Set(frontFace); + _renderer.New()->Set(frontFace); _renderer.QueueCommand(); } - public void SetImage(ShaderStage stage, int binding, ITexture texture) + public unsafe void SetImage(ShaderStage stage, int binding, ITexture texture) { - _renderer.New().Set(stage, binding, Ref(texture)); + _renderer.New()->Set(stage, binding, Ref(texture)); _renderer.QueueCommand(); } - public void SetImageArray(ShaderStage stage, int binding, IImageArray array) + public unsafe void SetImageArray(ShaderStage stage, int binding, IImageArray array) { - _renderer.New().Set(stage, binding, Ref(array)); + _renderer.New()->Set(stage, binding, Ref(array)); _renderer.QueueCommand(); } - public void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array) + public unsafe void SetImageArraySeparate(ShaderStage stage, int setIndex, IImageArray array) { - _renderer.New().Set(stage, setIndex, Ref(array)); + _renderer.New()->Set(stage, setIndex, Ref(array)); _renderer.QueueCommand(); } - public void SetIndexBuffer(BufferRange buffer, IndexType type) + public unsafe void SetIndexBuffer(BufferRange buffer, IndexType type) { - _renderer.New().Set(buffer, type); + _renderer.New()->Set(buffer, type); _renderer.QueueCommand(); } - public void SetLineParameters(float width, bool smooth) + public unsafe void SetLineParameters(float width, bool smooth) { - _renderer.New().Set(width, smooth); + _renderer.New()->Set(width, smooth); _renderer.QueueCommand(); } - public void SetLogicOpState(bool enable, LogicalOp op) + public unsafe void SetLogicOpState(bool enable, LogicalOp op) { - _renderer.New().Set(enable, op); + _renderer.New()->Set(enable, op); _renderer.QueueCommand(); } - public void SetMultisampleState(MultisampleDescriptor multisample) + public unsafe void SetMultisampleState(MultisampleDescriptor multisample) { - _renderer.New().Set(multisample); + _renderer.New()->Set(multisample); _renderer.QueueCommand(); } - public void SetPatchParameters(int vertices, ReadOnlySpan defaultOuterLevel, ReadOnlySpan defaultInnerLevel) + public unsafe void SetPatchParameters(int vertices, ReadOnlySpan defaultOuterLevel, ReadOnlySpan defaultInnerLevel) { - _renderer.New().Set(vertices, defaultOuterLevel, defaultInnerLevel); + _renderer.New()->Set(vertices, defaultOuterLevel, defaultInnerLevel); _renderer.QueueCommand(); } - public void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin) + public unsafe void SetPointParameters(float size, bool isProgramPointSize, bool enablePointSprite, Origin origin) { - _renderer.New().Set(size, isProgramPointSize, enablePointSprite, origin); + _renderer.New()->Set(size, isProgramPointSize, enablePointSprite, origin); _renderer.QueueCommand(); } - public void SetPolygonMode(PolygonMode frontMode, PolygonMode backMode) + public unsafe void SetPolygonMode(PolygonMode frontMode, PolygonMode backMode) { - _renderer.New().Set(frontMode, backMode); + _renderer.New()->Set(frontMode, backMode); _renderer.QueueCommand(); } - public void SetPrimitiveRestart(bool enable, int index) + public unsafe void SetPrimitiveRestart(bool enable, int index) { - _renderer.New().Set(enable, index); + _renderer.New()->Set(enable, index); _renderer.QueueCommand(); } - public void SetPrimitiveTopology(PrimitiveTopology topology) + public unsafe void SetPrimitiveTopology(PrimitiveTopology topology) { - _renderer.New().Set(topology); + _renderer.New()->Set(topology); _renderer.QueueCommand(); } - public void SetProgram(IProgram program) + public unsafe void SetProgram(IProgram program) { - _renderer.New().Set(Ref(program)); + _renderer.New()->Set(Ref(program)); _renderer.QueueCommand(); } - public void SetRasterizerDiscard(bool discard) + public unsafe void SetRasterizerDiscard(bool discard) { - _renderer.New().Set(discard); + _renderer.New()->Set(discard); _renderer.QueueCommand(); } - public void SetRenderTargetColorMasks(ReadOnlySpan componentMask) + public unsafe void SetRenderTargetColorMasks(ReadOnlySpan componentMask) { - _renderer.New().Set(_renderer.CopySpan(componentMask)); + _renderer.New()->Set(_renderer.CopySpan(componentMask)); _renderer.QueueCommand(); } - public void SetRenderTargets(ITexture[] colors, ITexture depthStencil) + public unsafe void SetRenderTargets(ITexture[] colors, ITexture depthStencil) { - _renderer.New().Set(Ref(colors.ToArray()), Ref(depthStencil)); + _renderer.New()->Set(Ref(colors.ToArray()), Ref(depthStencil)); _renderer.QueueCommand(); } - public void SetScissors(ReadOnlySpan> scissors) + public unsafe void SetScissors(ReadOnlySpan> scissors) { - _renderer.New().Set(_renderer.CopySpan(scissors)); + _renderer.New()->Set(_renderer.CopySpan(scissors)); _renderer.QueueCommand(); } - public void SetStencilTest(StencilTestDescriptor stencilTest) + public unsafe void SetStencilTest(StencilTestDescriptor stencilTest) { - _renderer.New().Set(stencilTest); + _renderer.New()->Set(stencilTest); _renderer.QueueCommand(); } - public void SetStorageBuffers(ReadOnlySpan buffers) + public unsafe void SetStorageBuffers(ReadOnlySpan buffers) { - _renderer.New().Set(_renderer.CopySpan(buffers)); + _renderer.New()->Set(_renderer.CopySpan(buffers)); _renderer.QueueCommand(); } - public void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler) + public unsafe void SetTextureAndSampler(ShaderStage stage, int binding, ITexture texture, ISampler sampler) { - _renderer.New().Set(stage, binding, Ref(texture), Ref(sampler)); + _renderer.New()->Set(stage, binding, Ref(texture), Ref(sampler)); _renderer.QueueCommand(); } - public void SetTextureArray(ShaderStage stage, int binding, ITextureArray array) + public unsafe void SetTextureArray(ShaderStage stage, int binding, ITextureArray array) { - _renderer.New().Set(stage, binding, Ref(array)); + _renderer.New()->Set(stage, binding, Ref(array)); _renderer.QueueCommand(); } - public void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array) + public unsafe void SetTextureArraySeparate(ShaderStage stage, int setIndex, ITextureArray array) { - _renderer.New().Set(stage, setIndex, Ref(array)); + _renderer.New()->Set(stage, setIndex, Ref(array)); _renderer.QueueCommand(); } - public void SetTransformFeedbackBuffers(ReadOnlySpan buffers) + public unsafe void SetTransformFeedbackBuffers(ReadOnlySpan buffers) { - _renderer.New().Set(_renderer.CopySpan(buffers)); + _renderer.New()->Set(_renderer.CopySpan(buffers)); _renderer.QueueCommand(); } - public void SetUniformBuffers(ReadOnlySpan buffers) + public unsafe void SetUniformBuffers(ReadOnlySpan buffers) { - _renderer.New().Set(_renderer.CopySpan(buffers)); + _renderer.New()->Set(_renderer.CopySpan(buffers)); _renderer.QueueCommand(); } - public void SetUserClipDistance(int index, bool enableClip) + public unsafe void SetUserClipDistance(int index, bool enableClip) { - _renderer.New().Set(index, enableClip); + _renderer.New()->Set(index, enableClip); _renderer.QueueCommand(); } - public void SetVertexAttribs(ReadOnlySpan vertexAttribs) + public unsafe void SetVertexAttribs(ReadOnlySpan vertexAttribs) { - _renderer.New().Set(_renderer.CopySpan(vertexAttribs)); + _renderer.New()->Set(_renderer.CopySpan(vertexAttribs)); _renderer.QueueCommand(); } - public void SetVertexBuffers(ReadOnlySpan vertexBuffers) + public unsafe void SetVertexBuffers(ReadOnlySpan vertexBuffers) { - _renderer.New().Set(_renderer.CopySpan(vertexBuffers)); + _renderer.New()->Set(_renderer.CopySpan(vertexBuffers)); _renderer.QueueCommand(); } - public void SetViewports(ReadOnlySpan viewports) + public unsafe void SetViewports(ReadOnlySpan viewports) { - _renderer.New().Set(_renderer.CopySpan(viewports)); + _renderer.New()->Set(_renderer.CopySpan(viewports)); _renderer.QueueCommand(); } - public void TextureBarrier() + public unsafe void TextureBarrier() { _renderer.New(); _renderer.QueueCommand(); } - public void TextureBarrierTiled() + public unsafe void TextureBarrierTiled() { _renderer.New(); _renderer.QueueCommand(); } - public bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual) + public unsafe bool TryHostConditionalRendering(ICounterEvent value, ulong compare, bool isEqual) { var evt = value as ThreadedCounterEvent; if (evt != null) @@ -369,20 +369,20 @@ namespace Ryujinx.Graphics.GAL.Multithreading return false; } - _renderer.New().Set(Ref(evt), compare, isEqual); + _renderer.New()->Set(Ref(evt), compare, isEqual); _renderer.QueueCommand(); return true; } } - _renderer.New().Set(Ref(evt), Ref(null), isEqual); + _renderer.New()->Set(Ref(evt), Ref(null), isEqual); _renderer.QueueCommand(); return false; } - public bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual) + public unsafe bool TryHostConditionalRendering(ICounterEvent value, ICounterEvent compare, bool isEqual) { - _renderer.New().Set(Ref(value as ThreadedCounterEvent), Ref(compare as ThreadedCounterEvent), isEqual); + _renderer.New()->Set(Ref(value as ThreadedCounterEvent), Ref(compare as ThreadedCounterEvent), isEqual); _renderer.QueueCommand(); return false; } diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs index 1750e348f..a144ba602 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedRenderer.cs @@ -167,7 +167,7 @@ namespace Ryujinx.Graphics.GAL.Multithreading return new TableRef(this, reference); } - internal ref T New() where T : struct + internal unsafe T* New() where T : unmanaged, IGALCommand { while (_producerPtr == (Volatile.Read(ref _consumerPtr) + QueueCount - 1) % QueueCount) { @@ -183,11 +183,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading _producerPtr = (_producerPtr + 1) % QueueCount; Span memory = new(_commandQueue, taken * _elementSize, _elementSize); - ref T result = ref Unsafe.As(ref MemoryMarshal.GetReference(memory)); + T* result = (T*)Unsafe.AsPointer(ref memory.GetPinnableReference()); + // ref T result = ref Unsafe.As(ref MemoryMarshal.GetReference(memory)); - memory[^1] = (byte)((IGALCommand)result).CommandType; + memory[^1] = (byte)(result)->CommandType; - return ref result; + return result; } internal int AddTableRef(object obj) @@ -251,12 +252,12 @@ namespace Ryujinx.Graphics.GAL.Multithreading return Thread.CurrentThread == _gpuThread; } - public void BackgroundContextAction(Action action, bool alwaysBackground = false) + public unsafe void BackgroundContextAction(Action action, bool alwaysBackground = false) { if (IsGpuThread() && !alwaysBackground) { // The action must be performed on the render thread. - New().Set(Ref(action)); + New()->Set(Ref(action)); InvokeCommand(); } else @@ -265,43 +266,43 @@ namespace Ryujinx.Graphics.GAL.Multithreading } } - public BufferHandle CreateBuffer(int size, BufferAccess access) + public unsafe BufferHandle CreateBuffer(int size, BufferAccess access) { BufferHandle handle = Buffers.CreateBufferHandle(); - New().Set(handle, size, access); + New()->Set(handle, size, access); QueueCommand(); return handle; } - public BufferHandle CreateBuffer(nint pointer, int size) + public unsafe BufferHandle CreateBuffer(nint pointer, int size) { BufferHandle handle = Buffers.CreateBufferHandle(); - New().Set(handle, pointer, size); + New()->Set(handle, pointer, size); QueueCommand(); return handle; } - public BufferHandle CreateBufferSparse(ReadOnlySpan storageBuffers) + public unsafe BufferHandle CreateBufferSparse(ReadOnlySpan storageBuffers) { BufferHandle handle = Buffers.CreateBufferHandle(); - New().Set(handle, CopySpan(storageBuffers)); + New()->Set(handle, CopySpan(storageBuffers)); QueueCommand(); return handle; } - public IImageArray CreateImageArray(int size, bool isBuffer) + public unsafe IImageArray CreateImageArray(int size, bool isBuffer) { var imageArray = new ThreadedImageArray(this); - New().Set(Ref(imageArray), size, isBuffer); + New()->Set(Ref(imageArray), size, isBuffer); QueueCommand(); return imageArray; } - public IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) + public unsafe IProgram CreateProgram(ShaderSource[] shaders, ShaderInfo info) { var program = new ThreadedProgram(this); @@ -311,34 +312,34 @@ namespace Ryujinx.Graphics.GAL.Multithreading ProgramCount++; - New().Set(Ref((IProgramRequest)request)); + New()->Set(Ref((IProgramRequest)request)); QueueCommand(); return program; } - public ISampler CreateSampler(SamplerCreateInfo info) + public unsafe ISampler CreateSampler(SamplerCreateInfo info) { var sampler = new ThreadedSampler(this); - New().Set(Ref(sampler), info); + New()->Set(Ref(sampler), info); QueueCommand(); return sampler; } - public void CreateSync(ulong id, bool strict) + public unsafe void CreateSync(ulong id, bool strict) { Sync.CreateSyncHandle(id); - New().Set(id, strict); + New()->Set(id, strict); QueueCommand(); } - public ITexture CreateTexture(TextureCreateInfo info) + public unsafe ITexture CreateTexture(TextureCreateInfo info) { if (IsGpuThread()) { var texture = new ThreadedTexture(this, info); - New().Set(Ref(texture), info); + New()->Set(Ref(texture), info); QueueCommand(); return texture; @@ -353,27 +354,27 @@ namespace Ryujinx.Graphics.GAL.Multithreading return texture; } } - public ITextureArray CreateTextureArray(int size, bool isBuffer) + public unsafe ITextureArray CreateTextureArray(int size, bool isBuffer) { var textureArray = new ThreadedTextureArray(this); - New().Set(Ref(textureArray), size, isBuffer); + New()->Set(Ref(textureArray), size, isBuffer); QueueCommand(); return textureArray; } - public void DeleteBuffer(BufferHandle buffer) + public unsafe void DeleteBuffer(BufferHandle buffer) { - New().Set(buffer); + New()->Set(buffer); QueueCommand(); } - public PinnedSpan GetBufferData(BufferHandle buffer, int offset, int size) + public unsafe PinnedSpan GetBufferData(BufferHandle buffer, int offset, int size) { if (IsGpuThread()) { ResultBox> box = new(); - New().Set(buffer, offset, size, Ref(box)); + New()->Set(buffer, offset, size, Ref(box)); InvokeCommand(); return box.Result; @@ -384,10 +385,10 @@ namespace Ryujinx.Graphics.GAL.Multithreading } } - public Capabilities GetCapabilities() + public unsafe Capabilities GetCapabilities() { ResultBox box = new(); - New().Set(Ref(box)); + New()->Set(Ref(box)); InvokeCommand(); return box.Result; @@ -412,29 +413,29 @@ namespace Ryujinx.Graphics.GAL.Multithreading _baseRenderer.Initialize(logLevel); } - public IProgram LoadProgramBinary(byte[] programBinary, bool hasFragmentShader, ShaderInfo info) + public unsafe IProgram LoadProgramBinary(byte[] programBinary, bool hasFragmentShader, ShaderInfo info) { var program = new ThreadedProgram(this); BinaryProgramRequest request = new(program, programBinary, hasFragmentShader, info); Programs.Add(request); - New().Set(Ref((IProgramRequest)request)); + New()->Set(Ref((IProgramRequest)request)); QueueCommand(); return program; } - public void PreFrame() + public unsafe void PreFrame() { New(); QueueCommand(); } - public ICounterEvent ReportCounter(CounterType type, EventHandler resultHandler, float divisor, bool hostReserved) + public unsafe ICounterEvent ReportCounter(CounterType type, EventHandler resultHandler, float divisor, bool hostReserved) { ThreadedCounterEvent evt = new(this, type, _lastSampleCounterClear); - New().Set(Ref(evt), type, Ref(resultHandler), divisor, hostReserved); + New()->Set(Ref(evt), type, Ref(resultHandler), divisor, hostReserved); QueueCommand(); if (type == CounterType.SamplesPassed) @@ -445,9 +446,9 @@ namespace Ryujinx.Graphics.GAL.Multithreading return evt; } - public void ResetCounter(CounterType type) + public unsafe void ResetCounter(CounterType type) { - New().Set(type); + New()->Set(type); QueueCommand(); _lastSampleCounterClear = true; } @@ -457,13 +458,13 @@ namespace Ryujinx.Graphics.GAL.Multithreading _baseRenderer.Screenshot(); } - public void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan data) + public unsafe void SetBufferData(BufferHandle buffer, int offset, ReadOnlySpan data) { - New().Set(buffer, offset, CopySpan(data)); + New()->Set(buffer, offset, CopySpan(data)); QueueCommand(); } - public void UpdateCounters() + public unsafe void UpdateCounters() { New(); QueueCommand(); diff --git a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs index 102fdb1bb..297527593 100644 --- a/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs +++ b/src/Ryujinx.Graphics.GAL/Multithreading/ThreadedWindow.cs @@ -16,13 +16,13 @@ namespace Ryujinx.Graphics.GAL.Multithreading _impl = impl; } - public void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) + public unsafe void Present(ITexture texture, ImageCrop crop, Action swapBuffersCallback) { // If there's already a frame in the pipeline, wait for it to be presented first. // This is a multithread rate limit - we can't be more than one frame behind the command queue. _renderer.WaitForFrame(); - _renderer.New().Set(new TableRef(_renderer, texture as ThreadedTexture), crop, new TableRef(_renderer, swapBuffersCallback)); + _renderer.New()->Set(new TableRef(_renderer, texture as ThreadedTexture), crop, new TableRef(_renderer, swapBuffersCallback)); _renderer.QueueCommand(); } diff --git a/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs index 85bdb3cb3..33f9d591d 100644 --- a/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs +++ b/src/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs @@ -50,6 +50,12 @@ namespace Ryujinx.Graphics.Gpu.Memory /// internal CounterCache CounterCache { get; } + private delegate void WriteCallback(ulong address, ReadOnlySpan data); + + private WriteCallback _write; + private WriteCallback _writeTrackedResource; + private WriteCallback _writeUntracked; + /// /// Creates a new instance of the GPU memory manager. /// @@ -58,6 +64,9 @@ namespace Ryujinx.Graphics.Gpu.Memory internal MemoryManager(PhysicalMemory physicalMemory, ulong cpuMemorySize) { Physical = physicalMemory; + _write = physicalMemory.Write; + _writeTrackedResource = physicalMemory.WriteTrackedResource; + _writeUntracked = physicalMemory.WriteUntracked; VirtualRangeCache = new VirtualRangeCache(this); CounterCache = new CounterCache(); _pageTable = new ulong[PtLvl0Size][]; @@ -269,7 +278,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// The data to be written public void Write(ulong va, ReadOnlySpan data) { - WriteImpl(va, data, Physical.Write); + WriteImpl(va, data, _write); } /// @@ -279,7 +288,7 @@ namespace Ryujinx.Graphics.Gpu.Memory /// The data to be written public void WriteTrackedResource(ulong va, ReadOnlySpan data) { - WriteImpl(va, data, Physical.WriteTrackedResource); + WriteImpl(va, data, _writeTrackedResource); } /// @@ -289,11 +298,9 @@ namespace Ryujinx.Graphics.Gpu.Memory /// The data to be written public void WriteUntracked(ulong va, ReadOnlySpan data) { - WriteImpl(va, data, Physical.WriteUntracked); + WriteImpl(va, data, _writeUntracked); } - private delegate void WriteCallback(ulong address, ReadOnlySpan data); - /// /// Writes data to possibly non-contiguous GPU mapped memory. /// diff --git a/src/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs b/src/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs index fea09ef47..6d716f92d 100644 --- a/src/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs +++ b/src/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs @@ -123,15 +123,15 @@ namespace Ryujinx.HLE.HOS.Services.Nv return NvResult.InvalidSize; } - byte[] outputData = new byte[outputDataSize]; - - byte[] temp = new byte[inputDataSize]; - - context.Memory.Read(inputDataPosition, temp); - - Buffer.BlockCopy(temp, 0, outputData, 0, temp.Length); - - arguments = new Span(outputData); + if (!context.Memory.TryReadUnsafe(inputDataPosition, (int)inputDataSize, out arguments)) + { + arguments = new byte[inputDataSize]; + context.Memory.Read(inputDataPosition, arguments); + } + else + { + arguments = arguments.ToArray(); + } } else if (isWrite) { @@ -471,11 +471,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv errorCode = GetIoctlArgument(context, ioctlCommand, out Span arguments); - byte[] temp = new byte[inlineInBufferSize]; - - context.Memory.Read(inlineInBufferPosition, temp); - - Span inlineInBuffer = new(temp); + if (!context.Memory.TryReadUnsafe(inlineInBufferPosition, (int)inlineInBufferSize, out Span inlineInBuffer)) + { + inlineInBuffer = new byte[inlineInBufferSize]; + context.Memory.Read(inlineInBufferPosition, inlineInBuffer); + } if (errorCode == NvResult.Success) { @@ -520,11 +520,11 @@ namespace Ryujinx.HLE.HOS.Services.Nv errorCode = GetIoctlArgument(context, ioctlCommand, out Span arguments); - byte[] temp = new byte[inlineOutBufferSize]; - - context.Memory.Read(inlineOutBufferPosition, temp); - - Span inlineOutBuffer = new(temp); + if (!context.Memory.TryReadUnsafe(inlineOutBufferPosition, (int)inlineOutBufferSize, out Span inlineOutBuffer)) + { + inlineOutBuffer = new byte[inlineOutBufferSize]; + context.Memory.Read(inlineOutBufferPosition, inlineOutBuffer); + } if (errorCode == NvResult.Success) { diff --git a/src/Ryujinx.Memory/IVirtualMemoryManager.cs b/src/Ryujinx.Memory/IVirtualMemoryManager.cs index 102cedc94..050c85862 100644 --- a/src/Ryujinx.Memory/IVirtualMemoryManager.cs +++ b/src/Ryujinx.Memory/IVirtualMemoryManager.cs @@ -61,6 +61,15 @@ namespace Ryujinx.Memory /// Throw for unhandled invalid or unmapped memory accesses void Read(ulong va, Span data); + /// + /// Gets a span of CPU mapped memory. + /// + /// Virtual address of the data in memory + /// Length of the data in memory + /// Span that references the data being read + /// Throw for unhandled invalid or unmapped memory accesses + bool TryReadUnsafe(ulong va, int length, out Span data); + /// /// Writes data to CPU mapped memory. /// diff --git a/src/Ryujinx.Memory/VirtualMemoryManagerBase.cs b/src/Ryujinx.Memory/VirtualMemoryManagerBase.cs index f41072244..44d8ec134 100644 --- a/src/Ryujinx.Memory/VirtualMemoryManagerBase.cs +++ b/src/Ryujinx.Memory/VirtualMemoryManagerBase.cs @@ -159,6 +159,13 @@ namespace Ryujinx.Memory AssertValidAddressAndSize(va, data.Length); + if (IsContiguousAndMapped(va, data.Length)) + { + nuint pa = TranslateVirtualAddressChecked(va); + + GetPhysicalAddressSpan(pa, data.Length).CopyTo(data); + } + int offset = 0, size; if ((va & PageMask) != 0) @@ -182,6 +189,28 @@ namespace Ryujinx.Memory } } + public virtual bool TryReadUnsafe(ulong va, int length, out Span data) + { + if (!IsContiguousAndMapped(va, length)) + { + data = default; + return false; + } + + if (length == 0) + { + data = Span.Empty; + return true; + } + + AssertValidAddressAndSize(va, length); + + nuint pa = TranslateVirtualAddressChecked(va); + + data = GetPhysicalAddressSpan(pa, length); + return true; + } + public virtual T ReadTracked(ulong va) where T : unmanaged { SignalMemoryTracking(va, (ulong)Unsafe.SizeOf(), false); diff --git a/src/Ryujinx.Tests.Memory/MockVirtualMemoryManager.cs b/src/Ryujinx.Tests.Memory/MockVirtualMemoryManager.cs index a01521c8f..cef55f485 100644 --- a/src/Ryujinx.Tests.Memory/MockVirtualMemoryManager.cs +++ b/src/Ryujinx.Tests.Memory/MockVirtualMemoryManager.cs @@ -43,6 +43,11 @@ namespace Ryujinx.Tests.Memory throw new NotImplementedException(); } + public bool TryReadUnsafe(ulong va, int lenfth, out Span data) + { + throw new NotImplementedException(); + } + public void Write(ulong va, T value) where T : unmanaged { throw new NotImplementedException();