Compare commits

..

7 Commits

Author SHA1 Message Date
e43390c723 bsd: Check if socket is bound before calling RecvFrom() (#3761) 2022-10-15 20:52:49 +00:00
5af1327068 Vulkan: Fix sampler custom border color (#3751) 2022-10-10 08:35:44 +02:00
88a8d1e567 Fix disposed textures being updated on TextureBindingsManager (#3750)
* Fix disposed textures being updated on TextureBindingsManager

* PR feedback
2022-10-09 15:23:52 -03:00
bf77d1cab9 GPU: Pass SpanOrArray for Texture SetData to avoid copy (#3745)
* GPU: Pass SpanOrArray for Texture SetData to avoid copy

Texture data is often converted before upload, meaning that an array was allocated to perform the conversion into. However, the backend SetData methods were being passed a Span of that data, and the Multithreaded layer does `ToArray()` on it so that it can be stored for later! This method can't extract the original array, so it creates a copy.

This PR changes the type passed for textures to a new ref struct called SpanOrArray, which is backed by either a ReadOnlySpan or an array. The benefit here is that we can have a ToArray method that doesn't copy if it is originally backed by an array.

This will also avoid a copy when running the ASTC decoder.

On NieR this was taking 38% of texture upload time, which it does a _lot_ of when you move between areas, so there should be a 1.6x performance boost when strictly uploading textures. No doubt this will also improve texture streaming performance in UE4 games, and maybe a small reduction with video playback.

From the numbers, it's probably possible to improve the upload rate by a further 1.6x by performing layout conversion on GPU. I'm not sure if we could improve it further than that - multithreading conversion on CPU would probably result in memory bottleneck.

This doesn't extend to buffers, since we don't convert their data on the GPU emulator side.

* Remove implicit cast to array.
2022-10-08 12:04:47 -03:00
1ca0517c99 Vulkan: Fix some issues with CacheByRange (#3743)
* Fix some issues with CacheByRange

- Cache now clears under more circumstances, the most important being the fast path write.
- Cache supports partial clear which should help when more buffers join.
- Fixed an issue with I8->I16 conversion where it wouldn't register the buffer for use on dispose.

Should hopefully fix issues with https://github.com/Ryujinx/Ryujinx-Games-List/issues/4010 and maybe others.

* Fix collection modified exception

* Fix accidental use of parameterless constructor

* Replay DynamicState when restoring from helper shader
2022-10-08 11:28:27 -03:00
599d485bff Change NvMap ID allocation to match nvservices (#3741)
* Change NvMap ID allocation to match nvservices

* Move NvMapIdDictionary to Types
2022-10-05 17:49:18 -03:00
60e16c15b6 Fix memory corruption in BCAT and FS Read methods when buffer is larger than needed (#3739)
* Fix memory corruption in FS Read methods when buffer is larger than needed

* PR feedback

* nit: Don't move this around
2022-10-04 20:12:54 -03:00
31 changed files with 433 additions and 189 deletions

View File

@ -0,0 +1,89 @@
using System;
namespace Ryujinx.Common.Memory
{
/// <summary>
/// A struct that can represent both a Span and Array.
/// This is useful to keep the Array representation when possible to avoid copies.
/// </summary>
/// <typeparam name="T">Element Type</typeparam>
public ref struct SpanOrArray<T> where T : unmanaged
{
public readonly T[] Array;
public readonly ReadOnlySpan<T> Span;
/// <summary>
/// Create a new SpanOrArray from an array.
/// </summary>
/// <param name="array">Array to store</param>
public SpanOrArray(T[] array)
{
Array = array;
Span = ReadOnlySpan<T>.Empty;
}
/// <summary>
/// Create a new SpanOrArray from a readonly span.
/// </summary>
/// <param name="array">Span to store</param>
public SpanOrArray(ReadOnlySpan<T> span)
{
Array = null;
Span = span;
}
/// <summary>
/// Return the contained array, or convert the span if necessary.
/// </summary>
/// <returns>An array containing the data</returns>
public T[] ToArray()
{
return Array ?? Span.ToArray();
}
/// <summary>
/// Return a ReadOnlySpan from either the array or ReadOnlySpan.
/// </summary>
/// <returns>A ReadOnlySpan containing the data</returns>
public ReadOnlySpan<T> AsSpan()
{
return Array ?? Span;
}
/// <summary>
/// Cast an array to a SpanOrArray.
/// </summary>
/// <param name="array">Source array</param>
public static implicit operator SpanOrArray<T>(T[] array)
{
return new SpanOrArray<T>(array);
}
/// <summary>
/// Cast a ReadOnlySpan to a SpanOrArray.
/// </summary>
/// <param name="span">Source ReadOnlySpan</param>
public static implicit operator SpanOrArray<T>(ReadOnlySpan<T> span)
{
return new SpanOrArray<T>(span);
}
/// <summary>
/// Cast a Span to a SpanOrArray.
/// </summary>
/// <param name="span">Source Span</param>
public static implicit operator SpanOrArray<T>(Span<T> span)
{
return new SpanOrArray<T>(span);
}
/// <summary>
/// Cast a SpanOrArray to a ReadOnlySpan
/// </summary>
/// <param name="spanOrArray">Source SpanOrArray</param>
public static implicit operator ReadOnlySpan<T>(SpanOrArray<T> spanOrArray)
{
return spanOrArray.AsSpan();
}
}
}

View File

@ -1,3 +1,4 @@
using Ryujinx.Common.Memory;
using System;
namespace Ryujinx.Graphics.GAL
@ -17,9 +18,9 @@ namespace Ryujinx.Graphics.GAL
ReadOnlySpan<byte> GetData();
ReadOnlySpan<byte> GetData(int layer, int level);
void SetData(ReadOnlySpan<byte> data);
void SetData(ReadOnlySpan<byte> data, int layer, int level);
void SetData(ReadOnlySpan<byte> data, int layer, int level, Rectangle<int> region);
void SetData(SpanOrArray<byte> data);
void SetData(SpanOrArray<byte> data, int layer, int level);
void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region);
void SetStorage(BufferRange buffer);
void Release();
}

View File

@ -1,4 +1,5 @@
using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL.Multithreading.Commands.Texture;
using Ryujinx.Graphics.GAL.Multithreading.Model;
using System;
@ -107,19 +108,19 @@ namespace Ryujinx.Graphics.GAL.Multithreading.Resources
}
}
public void SetData(ReadOnlySpan<byte> data)
public void SetData(SpanOrArray<byte> data)
{
_renderer.New<TextureSetDataCommand>().Set(Ref(this), Ref(data.ToArray()));
_renderer.QueueCommand();
}
public void SetData(ReadOnlySpan<byte> data, int layer, int level)
public void SetData(SpanOrArray<byte> data, int layer, int level)
{
_renderer.New<TextureSetDataSliceCommand>().Set(Ref(this), Ref(data.ToArray()), layer, level);
_renderer.QueueCommand();
}
public void SetData(ReadOnlySpan<byte> data, int layer, int level, Rectangle<int> region)
public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region)
{
_renderer.New<TextureSetDataSliceRegionCommand>().Set(Ref(this), Ref(data.ToArray()), layer, level, region);
_renderer.QueueCommand();

View File

@ -229,7 +229,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.Dma
if (target != null)
{
ReadOnlySpan<byte> data;
byte[] data;
if (srcLinear)
{
data = LayoutConverter.ConvertLinearStridedToLinear(

View File

@ -1,5 +1,6 @@
using Ryujinx.Common;
using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Texture;
@ -136,11 +137,6 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
public LinkedListNode<Texture> CacheNode { get; set; }
/// <summary>
/// Event to fire when texture data is disposed.
/// </summary>
public event Action<Texture> Disposed;
/// <summary>
/// Physical memory ranges where the texture data is located.
/// </summary>
@ -720,9 +716,9 @@ namespace Ryujinx.Graphics.Gpu.Image
}
}
data = ConvertToHostCompatibleFormat(data);
SpanOrArray<byte> result = ConvertToHostCompatibleFormat(data);
HostTexture.SetData(data);
HostTexture.SetData(result);
_hasData = true;
}
@ -731,7 +727,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// Uploads new texture data to the host GPU.
/// </summary>
/// <param name="data">New data</param>
public void SetData(ReadOnlySpan<byte> data)
public void SetData(SpanOrArray<byte> data)
{
BlacklistScale();
@ -750,7 +746,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="data">New data</param>
/// <param name="layer">Target layer</param>
/// <param name="level">Target level</param>
public void SetData(ReadOnlySpan<byte> data, int layer, int level)
public void SetData(SpanOrArray<byte> data, int layer, int level)
{
BlacklistScale();
@ -786,7 +782,7 @@ namespace Ryujinx.Graphics.Gpu.Image
/// <param name="level">Mip level to convert</param>
/// <param name="single">True to convert a single slice</param>
/// <returns>Converted data</returns>
public ReadOnlySpan<byte> ConvertToHostCompatibleFormat(ReadOnlySpan<byte> data, int level = 0, bool single = false)
public SpanOrArray<byte> ConvertToHostCompatibleFormat(ReadOnlySpan<byte> data, int level = 0, bool single = false)
{
int width = Info.Width;
int height = Info.Height;
@ -799,9 +795,11 @@ namespace Ryujinx.Graphics.Gpu.Image
height = Math.Max(height >> level, 1);
depth = Math.Max(depth >> level, 1);
SpanOrArray<byte> result;
if (Info.IsLinear)
{
data = LayoutConverter.ConvertLinearStridedToLinear(
result = LayoutConverter.ConvertLinearStridedToLinear(
width,
height,
Info.FormatInfo.BlockWidth,
@ -813,7 +811,7 @@ namespace Ryujinx.Graphics.Gpu.Image
}
else
{
data = LayoutConverter.ConvertBlockLinearToLinear(
result = LayoutConverter.ConvertBlockLinearToLinear(
width,
height,
depth,
@ -836,7 +834,7 @@ namespace Ryujinx.Graphics.Gpu.Image
if (!_context.Capabilities.SupportsAstcCompression && Format.IsAstc())
{
if (!AstcDecoder.TryDecodeToRgba8P(
data.ToArray(),
result.ToArray(),
Info.FormatInfo.BlockWidth,
Info.FormatInfo.BlockHeight,
width,
@ -856,11 +854,11 @@ namespace Ryujinx.Graphics.Gpu.Image
decoded = BCnEncoder.EncodeBC7(decoded, width, height, depth, levels, layers);
}
data = decoded;
result = decoded;
}
else if (!_context.Capabilities.SupportsR4G4Format && Format == Format.R4G4Unorm)
{
data = PixelConverter.ConvertR4G4ToR4G4B4A4(data);
result = PixelConverter.ConvertR4G4ToR4G4B4A4(result);
}
else if (!TextureCompatibility.HostSupportsBcFormat(Format, Target, _context.Capabilities))
{
@ -868,36 +866,36 @@ namespace Ryujinx.Graphics.Gpu.Image
{
case Format.Bc1RgbaSrgb:
case Format.Bc1RgbaUnorm:
data = BCnDecoder.DecodeBC1(data, width, height, depth, levels, layers);
result = BCnDecoder.DecodeBC1(result, width, height, depth, levels, layers);
break;
case Format.Bc2Srgb:
case Format.Bc2Unorm:
data = BCnDecoder.DecodeBC2(data, width, height, depth, levels, layers);
result = BCnDecoder.DecodeBC2(result, width, height, depth, levels, layers);
break;
case Format.Bc3Srgb:
case Format.Bc3Unorm:
data = BCnDecoder.DecodeBC3(data, width, height, depth, levels, layers);
result = BCnDecoder.DecodeBC3(result, width, height, depth, levels, layers);
break;
case Format.Bc4Snorm:
case Format.Bc4Unorm:
data = BCnDecoder.DecodeBC4(data, width, height, depth, levels, layers, Format == Format.Bc4Snorm);
result = BCnDecoder.DecodeBC4(result, width, height, depth, levels, layers, Format == Format.Bc4Snorm);
break;
case Format.Bc5Snorm:
case Format.Bc5Unorm:
data = BCnDecoder.DecodeBC5(data, width, height, depth, levels, layers, Format == Format.Bc5Snorm);
result = BCnDecoder.DecodeBC5(result, width, height, depth, levels, layers, Format == Format.Bc5Snorm);
break;
case Format.Bc6HSfloat:
case Format.Bc6HUfloat:
data = BCnDecoder.DecodeBC6(data, width, height, depth, levels, layers, Format == Format.Bc6HSfloat);
result = BCnDecoder.DecodeBC6(result, width, height, depth, levels, layers, Format == Format.Bc6HSfloat);
break;
case Format.Bc7Srgb:
case Format.Bc7Unorm:
data = BCnDecoder.DecodeBC7(data, width, height, depth, levels, layers);
result = BCnDecoder.DecodeBC7(result, width, height, depth, levels, layers);
break;
}
}
return data;
return result;
}
/// <summary>
@ -1445,7 +1443,6 @@ namespace Ryujinx.Graphics.Gpu.Image
DisposeTextures();
HostTexture = hostTexture;
InvalidatedSequence++;
}
/// <summary>
@ -1600,6 +1597,8 @@ namespace Ryujinx.Graphics.Gpu.Image
/// </summary>
private void DisposeTextures()
{
InvalidatedSequence++;
_currentData = null;
HostTexture.Release();
@ -1634,8 +1633,6 @@ namespace Ryujinx.Graphics.Gpu.Image
{
DisposeTextures();
Disposed?.Invoke(this);
if (Group.Storage == this)
{
Group.Dispose();

View File

@ -1,4 +1,5 @@
using Ryujinx.Cpu.Tracking;
using Ryujinx.Common.Memory;
using Ryujinx.Cpu.Tracking;
using Ryujinx.Graphics.GAL;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Graphics.Texture;
@ -348,9 +349,9 @@ namespace Ryujinx.Graphics.Gpu.Image
ReadOnlySpan<byte> data = _physicalMemory.GetSpan(Storage.Range.GetSlice((ulong)offset, (ulong)size));
data = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel, true);
SpanOrArray<byte> result = Storage.ConvertToHostCompatibleFormat(data, info.BaseLevel, true);
Storage.SetData(data, info.BaseLayer, info.BaseLevel);
Storage.SetData(result, info.BaseLayer, info.BaseLevel);
offsetIndex++;
}

View File

@ -1,4 +1,5 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL;
using System;
@ -48,17 +49,19 @@ namespace Ryujinx.Graphics.OpenGL.Image
return GetData();
}
public void SetData(ReadOnlySpan<byte> data)
public void SetData(SpanOrArray<byte> data)
{
Buffer.SetData(_buffer, _bufferOffset, data.Slice(0, Math.Min(data.Length, _bufferSize)));
var dataSpan = data.AsSpan();
Buffer.SetData(_buffer, _bufferOffset, dataSpan.Slice(0, Math.Min(dataSpan.Length, _bufferSize)));
}
public void SetData(ReadOnlySpan<byte> data, int layer, int level)
public void SetData(SpanOrArray<byte> data, int layer, int level)
{
throw new NotSupportedException();
}
public void SetData(ReadOnlySpan<byte> data, int layer, int level, Rectangle<int> region)
public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region)
{
throw new NotSupportedException();
}

View File

@ -1,5 +1,6 @@
using OpenTK.Graphics.OpenGL;
using Ryujinx.Common;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL;
using System;
@ -317,32 +318,36 @@ namespace Ryujinx.Graphics.OpenGL.Image
}
}
public void SetData(ReadOnlySpan<byte> data)
public void SetData(SpanOrArray<byte> data)
{
var dataSpan = data.AsSpan();
if (Format == Format.S8UintD24Unorm)
{
data = FormatConverter.ConvertS8D24ToD24S8(data);
dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan);
}
unsafe
{
fixed (byte* ptr = data)
fixed (byte* ptr = dataSpan)
{
ReadFrom((IntPtr)ptr, data.Length);
ReadFrom((IntPtr)ptr, dataSpan.Length);
}
}
}
public void SetData(ReadOnlySpan<byte> data, int layer, int level)
public void SetData(SpanOrArray<byte> data, int layer, int level)
{
var dataSpan = data.AsSpan();
if (Format == Format.S8UintD24Unorm)
{
data = FormatConverter.ConvertS8D24ToD24S8(data);
dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan);
}
unsafe
{
fixed (byte* ptr = data)
fixed (byte* ptr = dataSpan)
{
int width = Math.Max(Info.Width >> level, 1);
int height = Math.Max(Info.Height >> level, 1);
@ -352,11 +357,13 @@ namespace Ryujinx.Graphics.OpenGL.Image
}
}
public void SetData(ReadOnlySpan<byte> data, int layer, int level, Rectangle<int> region)
public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region)
{
var dataSpan = data.AsSpan();
if (Format == Format.S8UintD24Unorm)
{
data = FormatConverter.ConvertS8D24ToD24S8(data);
dataSpan = FormatConverter.ConvertS8D24ToD24S8(dataSpan);
}
int wInBlocks = BitUtils.DivRoundUp(region.Width, Info.BlockWidth);
@ -364,7 +371,7 @@ namespace Ryujinx.Graphics.OpenGL.Image
unsafe
{
fixed (byte* ptr = data)
fixed (byte* ptr = dataSpan)
{
ReadFrom2D(
(IntPtr)ptr,

View File

@ -93,7 +93,7 @@ namespace Ryujinx.Graphics.Texture
};
}
public static Span<byte> ConvertBlockLinearToLinear(
public static byte[] ConvertBlockLinearToLinear(
int width,
int height,
int depth,
@ -119,7 +119,7 @@ namespace Ryujinx.Graphics.Texture
blockHeight,
bytesPerPixel);
Span<byte> output = new byte[outSize];
byte[] output = new byte[outSize];
int outOffs = 0;
@ -246,7 +246,7 @@ namespace Ryujinx.Graphics.Texture
return output;
}
public static Span<byte> ConvertLinearStridedToLinear(
public static byte[] ConvertLinearStridedToLinear(
int width,
int height,
int blockWidth,
@ -262,14 +262,15 @@ namespace Ryujinx.Graphics.Texture
int outStride = BitUtils.AlignUp(w * bytesPerPixel, HostStrideAlignment);
lineSize = Math.Min(lineSize, outStride);
Span<byte> output = new byte[h * outStride];
byte[] output = new byte[h * outStride];
Span<byte> outSpan = output;
int outOffs = 0;
int inOffs = 0;
for (int y = 0; y < h; y++)
{
data.Slice(inOffs, lineSize).CopyTo(output.Slice(outOffs, lineSize));
data.Slice(inOffs, lineSize).CopyTo(outSpan.Slice(outOffs, lineSize));
inOffs += stride;
outOffs += outStride;

View File

@ -109,12 +109,34 @@ namespace Ryujinx.Graphics.Vulkan
{
if (isWrite)
{
_cachedConvertedBuffers.Clear();
SignalWrite(0, Size);
}
return _buffer;
}
public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, int offset, int size, bool isWrite = false)
{
if (isWrite)
{
SignalWrite(offset, size);
}
return _buffer;
}
public void SignalWrite(int offset, int size)
{
if (offset == 0 && size == Size)
{
_cachedConvertedBuffers.Clear();
}
else
{
_cachedConvertedBuffers.ClearRange(offset, size);
}
}
public BufferHandle GetHandle()
{
var handle = _bufferHandle;
@ -183,6 +205,8 @@ namespace Ryujinx.Graphics.Vulkan
data.Slice(0, dataSize).CopyTo(new Span<byte>((void*)(_map + offset), dataSize));
SignalWrite(offset, dataSize);
return;
}
}
@ -240,7 +264,7 @@ namespace Ryujinx.Graphics.Vulkan
endRenderPass?.Invoke();
var dstBuffer = GetBuffer(cbs.CommandBuffer, true).Get(cbs, dstOffset, data.Length).Value;
var dstBuffer = GetBuffer(cbs.CommandBuffer, dstOffset, data.Length, true).Get(cbs, dstOffset, data.Length).Value;
InsertBufferBarrier(
_gd,
@ -364,7 +388,7 @@ namespace Ryujinx.Graphics.Vulkan
public Auto<DisposableBuffer> GetBufferI8ToI16(CommandBufferScoped cbs, int offset, int size)
{
var key = new I8ToI16CacheKey();
var key = new I8ToI16CacheKey(_gd);
if (!_cachedConvertedBuffers.TryGetValue(offset, size, key, out var holder))
{
@ -373,6 +397,8 @@ namespace Ryujinx.Graphics.Vulkan
_gd.PipelineInternal.EndRenderPass();
_gd.HelperShader.ConvertI8ToI16(_gd, cbs, this, holder, offset, size);
key.SetBuffer(holder.GetBuffer());
_cachedConvertedBuffers.Add(offset, size, key, holder);
}
@ -417,6 +443,8 @@ namespace Ryujinx.Graphics.Vulkan
_gd.PipelineInternal.EndRenderPass();
_gd.HelperShader.ConvertIndexBuffer(_gd, cbs, this, holder, pattern, indexSize, offset, indexCount);
key.SetBuffer(holder.GetBuffer());
_cachedConvertedBuffers.Add(offset, size, key, holder);
}

View File

@ -124,6 +124,16 @@ namespace Ryujinx.Graphics.Vulkan
return null;
}
public Auto<DisposableBuffer> GetBuffer(CommandBuffer commandBuffer, BufferHandle handle, int offset, int size, bool isWrite)
{
if (TryGetBuffer(handle, out var holder))
{
return holder.GetBuffer(commandBuffer, offset, size, isWrite);
}
return null;
}
public Auto<DisposableBuffer> GetBufferI8ToI16(CommandBufferScoped cbs, BufferHandle handle, int offset, int size)
{
if (TryGetBuffer(handle, out var holder))

View File

@ -25,6 +25,11 @@ namespace Ryujinx.Graphics.Vulkan
return other is I8ToI16CacheKey;
}
public void SetBuffer(Auto<DisposableBuffer> buffer)
{
_buffer = buffer;
}
public void Dispose()
{
_gd.PipelineInternal.DirtyIndexBuffer(_buffer);
@ -160,6 +165,44 @@ namespace Ryujinx.Graphics.Vulkan
}
}
public void ClearRange(int offset, int size)
{
if (_ranges != null && _ranges.Count > 0)
{
int end = offset + size;
List<ulong> toRemove = null;
foreach (KeyValuePair<ulong, List<Entry>> range in _ranges)
{
(int rOffset, int rSize) = UnpackRange(range.Key);
int rEnd = rOffset + rSize;
if (rEnd > offset && rOffset < end)
{
List<Entry> entries = range.Value;
foreach (Entry entry in entries)
{
entry.Key.Dispose();
entry.Value.Dispose();
}
(toRemove ??= new List<ulong>()).Add(range.Key);
}
}
if (toRemove != null)
{
foreach (ulong range in toRemove)
{
_ranges.Remove(range);
}
}
}
}
private List<Entry> GetEntries(int offset, int size)
{
if (_ranges == null)
@ -184,6 +227,11 @@ namespace Ryujinx.Graphics.Vulkan
return (uint)offset | ((ulong)size << 32);
}
private static (int offset, int size) UnpackRange(ulong range)
{
return ((int)range, (int)(range >> 32));
}
public void Dispose()
{
Clear();

View File

@ -21,7 +21,7 @@ namespace Ryujinx.Graphics.Vulkan
protected readonly AutoFlushCounter AutoFlush;
private PipelineDynamicState _dynamicState;
protected PipelineDynamicState DynamicState;
private PipelineState _newState;
private bool _stateDirty;
private GAL.PrimitiveTopology _topology;
@ -150,7 +150,7 @@ namespace Ryujinx.Graphics.Vulkan
{
EndRenderPass();
var dst = Gd.BufferManager.GetBuffer(CommandBuffer, destination, true).Get(Cbs, offset, size).Value;
var dst = Gd.BufferManager.GetBuffer(CommandBuffer, destination, offset, size, true).Get(Cbs, offset, size).Value;
BufferHolder.InsertBufferBarrier(
Gd,
@ -238,8 +238,8 @@ namespace Ryujinx.Graphics.Vulkan
{
EndRenderPass();
var src = Gd.BufferManager.GetBuffer(CommandBuffer, source, false);
var dst = Gd.BufferManager.GetBuffer(CommandBuffer, destination, true);
var src = Gd.BufferManager.GetBuffer(CommandBuffer, source, srcOffset, size, false);
var dst = Gd.BufferManager.GetBuffer(CommandBuffer, destination, dstOffset, size, true);
BufferHolder.Copy(Gd, Cbs, src, dst, srcOffset, dstOffset, size);
}
@ -388,7 +388,7 @@ namespace Ryujinx.Graphics.Vulkan
var oldDepthTestEnable = _newState.DepthTestEnable;
var oldDepthWriteEnable = _newState.DepthWriteEnable;
var oldTopology = _newState.Topology;
var oldViewports = _dynamicState.Viewports;
var oldViewports = DynamicState.Viewports;
var oldViewportsCount = _newState.ViewportsCount;
_newState.CullMode = CullModeFlags.CullModeNone;
@ -411,9 +411,9 @@ namespace Ryujinx.Graphics.Vulkan
_newState.DepthWriteEnable = oldDepthWriteEnable;
_newState.Topology = oldTopology;
_dynamicState.Viewports = oldViewports;
_dynamicState.ViewportsCount = (int)oldViewportsCount;
_dynamicState.SetViewportsDirty();
DynamicState.Viewports = oldViewports;
DynamicState.ViewportsCount = (int)oldViewportsCount;
DynamicState.SetViewportsDirty();
_newState.ViewportsCount = oldViewportsCount;
SignalStateChange();
@ -448,8 +448,13 @@ namespace Ryujinx.Graphics.Vulkan
ResumeTransformFeedbackInternal();
DrawCount++;
var buffer = Gd.BufferManager.GetBuffer(CommandBuffer, indirectBuffer.Handle, true).Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value;
var countBuffer = Gd.BufferManager.GetBuffer(CommandBuffer, parameterBuffer.Handle, true).Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value;
var buffer = Gd.BufferManager
.GetBuffer(CommandBuffer, indirectBuffer.Handle, indirectBuffer.Offset, indirectBuffer.Size, true)
.Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value;
var countBuffer = Gd.BufferManager
.GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, true)
.Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value;
Gd.DrawIndirectCountApi.CmdDrawIndirectCount(
CommandBuffer,
@ -478,8 +483,13 @@ namespace Ryujinx.Graphics.Vulkan
ResumeTransformFeedbackInternal();
DrawCount++;
var buffer = Gd.BufferManager.GetBuffer(CommandBuffer, indirectBuffer.Handle, true).Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value;
var countBuffer = Gd.BufferManager.GetBuffer(CommandBuffer, parameterBuffer.Handle, true).Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value;
var buffer = Gd.BufferManager
.GetBuffer(CommandBuffer, indirectBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, true)
.Get(Cbs, indirectBuffer.Offset, indirectBuffer.Size).Value;
var countBuffer = Gd.BufferManager
.GetBuffer(CommandBuffer, parameterBuffer.Handle, parameterBuffer.Offset, parameterBuffer.Size, true)
.Get(Cbs, parameterBuffer.Offset, parameterBuffer.Size).Value;
Gd.DrawIndirectCountApi.CmdDrawIndexedIndirectCount(
CommandBuffer,
@ -535,7 +545,7 @@ namespace Ryujinx.Graphics.Vulkan
public void SetDepthBias(PolygonModeMask enables, float factor, float units, float clamp)
{
_dynamicState.SetDepthBias(factor, units, clamp);
DynamicState.SetDepthBias(factor, units, clamp);
_newState.DepthBiasEnable = enables != 0;
SignalStateChange();
@ -753,10 +763,10 @@ namespace Ryujinx.Graphics.Vulkan
var offset = new Offset2D(region.X, region.Y);
var extent = new Extent2D((uint)region.Width, (uint)region.Height);
_dynamicState.SetScissor(i, new Rect2D(offset, extent));
DynamicState.SetScissor(i, new Rect2D(offset, extent));
}
_dynamicState.ScissorsCount = count;
DynamicState.ScissorsCount = count;
_newState.ScissorsCount = (uint)count;
SignalStateChange();
@ -764,7 +774,7 @@ namespace Ryujinx.Graphics.Vulkan
public void SetStencilTest(StencilTestDescriptor stencilTest)
{
_dynamicState.SetStencilMasks(
DynamicState.SetStencilMasks(
(uint)stencilTest.BackFuncMask,
(uint)stencilTest.BackMask,
(uint)stencilTest.BackFuncRef,
@ -813,7 +823,8 @@ namespace Ryujinx.Graphics.Vulkan
if (range.Handle != BufferHandle.Null)
{
_transformFeedbackBuffers[i] = new BufferState(Gd.BufferManager.GetBuffer(CommandBuffer, range.Handle, true), range.Offset, range.Size);
_transformFeedbackBuffers[i] =
new BufferState(Gd.BufferManager.GetBuffer(CommandBuffer, range.Handle, range.Offset, range.Size, true), range.Offset, range.Size);
_transformFeedbackBuffers[i].BindTransformFeedbackBuffer(Gd, Cbs, (uint)i);
}
else
@ -975,7 +986,7 @@ namespace Ryujinx.Graphics.Vulkan
{
var viewport = viewports[i];
_dynamicState.SetViewport(i, new Silk.NET.Vulkan.Viewport(
DynamicState.SetViewport(i, new Silk.NET.Vulkan.Viewport(
viewport.Region.X,
viewport.Region.Y,
viewport.Region.Width == 0f ? 1f : viewport.Region.Width,
@ -984,7 +995,7 @@ namespace Ryujinx.Graphics.Vulkan
Clamp(viewport.DepthFar)));
}
_dynamicState.ViewportsCount = count;
DynamicState.ViewportsCount = count;
float disableTransformF = disableTransform ? 1.0f : 0.0f;
if (SupportBufferUpdater.Data.ViewportInverse.W != disableTransformF || disableTransform)
@ -1063,7 +1074,7 @@ namespace Ryujinx.Graphics.Vulkan
_vertexBuffersDirty = ulong.MaxValue >> (64 - _vertexBuffers.Length);
_descriptorSetUpdater.SignalCommandBufferChange();
_dynamicState.ForceAllDirty();
DynamicState.ForceAllDirty();
_currentPipelineHandle = 0;
}
@ -1201,7 +1212,7 @@ namespace Ryujinx.Graphics.Vulkan
private void RecreatePipelineIfNeeded(PipelineBindPoint pbp)
{
_dynamicState.ReplayIfDirty(Gd.Api, CommandBuffer);
DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer);
// Commit changes to the support buffer before drawing.
SupportBufferUpdater.Commit();

View File

@ -204,6 +204,8 @@ namespace Ryujinx.Graphics.Vulkan
}
SignalCommandBufferChange();
DynamicState.ReplayIfDirty(Gd.Api, CommandBuffer);
}
public void FlushCommandsImpl()

View File

@ -64,6 +64,7 @@ namespace Ryujinx.Graphics.Vulkan
};
samplerCreateInfo.PNext = &customBorderColor;
samplerCreateInfo.BorderColor = BorderColor.FloatCustomExt;
}
gd.Api.CreateSampler(device, samplerCreateInfo, null, out var sampler).ThrowOnError();

View File

@ -87,7 +87,7 @@ namespace Ryujinx.Graphics.Vulkan
private void PushDataImpl(CommandBufferScoped cbs, BufferHolder dst, int dstOffset, ReadOnlySpan<byte> data)
{
var srcBuffer = _buffer.GetBuffer();
var dstBuffer = dst.GetBuffer();
var dstBuffer = dst.GetBuffer(cbs.CommandBuffer, dstOffset, data.Length, true);
int offset = _freeOffset;
int capacity = BufferSize - offset;

View File

@ -1,4 +1,5 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
@ -90,17 +91,17 @@ namespace Ryujinx.Graphics.Vulkan
_bufferView = null;
}
public void SetData(ReadOnlySpan<byte> data)
public void SetData(SpanOrArray<byte> data)
{
_gd.SetBufferData(_bufferHandle, _offset, data);
}
public void SetData(ReadOnlySpan<byte> data, int layer, int level)
public void SetData(SpanOrArray<byte> data, int layer, int level)
{
throw new NotSupportedException();
}
public void SetData(ReadOnlySpan<byte> data, int layer, int level, Rectangle<int> region)
public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region)
{
throw new NotSupportedException();
}

View File

@ -1,4 +1,5 @@
using Ryujinx.Graphics.GAL;
using Ryujinx.Common.Memory;
using Ryujinx.Graphics.GAL;
using Silk.NET.Vulkan;
using System;
using System.Collections.Generic;
@ -873,17 +874,17 @@ namespace Ryujinx.Graphics.Vulkan
return GetDataFromBuffer(result, size, result);
}
public void SetData(ReadOnlySpan<byte> data)
public void SetData(SpanOrArray<byte> data)
{
SetData(data, 0, 0, Info.GetLayers(), Info.Levels, singleSlice: false);
}
public void SetData(ReadOnlySpan<byte> data, int layer, int level)
public void SetData(SpanOrArray<byte> data, int layer, int level)
{
SetData(data, layer, level, 1, 1, singleSlice: true);
}
public void SetData(ReadOnlySpan<byte> data, int layer, int level, Rectangle<int> region)
public void SetData(SpanOrArray<byte> data, int layer, int level, Rectangle<int> region)
{
SetData(data, layer, level, 1, 1, singleSlice: true, region);
}

View File

@ -38,18 +38,17 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
// Read() -> (u32, buffer<nn::bcat::DeliveryCacheDirectoryEntry, 6>)
public ResultCode Read(ServiceCtx context)
{
ulong position = context.Request.ReceiveBuff[0].Position;
ulong size = context.Request.ReceiveBuff[0].Size;
ulong bufferAddress = context.Request.ReceiveBuff[0].Position;
ulong bufferLen = context.Request.ReceiveBuff[0].Size;
byte[] data = new byte[size];
using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true))
{
Result result = _base.Get.Read(out int entriesRead, MemoryMarshal.Cast<byte, DeliveryCacheDirectoryEntry>(region.Memory.Span));
Result result = _base.Get.Read(out int entriesRead, MemoryMarshal.Cast<byte, DeliveryCacheDirectoryEntry>(data));
context.ResponseData.Write(entriesRead);
context.Memory.Write(position, data);
context.ResponseData.Write(entriesRead);
return (ResultCode)result.Value;
return (ResultCode)result.Value;
}
}
[CommandHipc(2)]

View File

@ -38,20 +38,19 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
// Read(u64) -> (u64, buffer<bytes, 6>)
public ResultCode Read(ServiceCtx context)
{
ulong position = context.Request.ReceiveBuff[0].Position;
ulong size = context.Request.ReceiveBuff[0].Size;
ulong bufferAddress = context.Request.ReceiveBuff[0].Position;
ulong bufferLen = context.Request.ReceiveBuff[0].Size;
long offset = context.RequestData.ReadInt64();
byte[] data = new byte[size];
using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true))
{
Result result = _base.Get.Read(out long bytesRead, offset, region.Memory.Span);
Result result = _base.Get.Read(out long bytesRead, offset, data);
context.ResponseData.Write(bytesRead);
context.Memory.Write(position, data);
context.ResponseData.Write(bytesRead);
return (ResultCode)result.Value;
return (ResultCode)result.Value;
}
}
[CommandHipc(2)]

View File

@ -50,18 +50,17 @@ namespace Ryujinx.HLE.HOS.Services.Bcat.ServiceCreator
// EnumerateDeliveryCacheDirectory() -> (u32, buffer<nn::bcat::DirectoryName, 6>)
public ResultCode EnumerateDeliveryCacheDirectory(ServiceCtx context)
{
ulong position = context.Request.ReceiveBuff[0].Position;
ulong size = context.Request.ReceiveBuff[0].Size;
ulong bufferAddress = context.Request.ReceiveBuff[0].Position;
ulong bufferLen = context.Request.ReceiveBuff[0].Size;
byte[] data = new byte[size];
using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true))
{
Result result = _base.Get.EnumerateDeliveryCacheDirectory(out int count, MemoryMarshal.Cast<byte, DirectoryName>(region.Memory.Span));
Result result = _base.Get.EnumerateDeliveryCacheDirectory(out int count, MemoryMarshal.Cast<byte, DirectoryName>(data));
context.ResponseData.Write(count);
context.Memory.Write(position, data);
context.ResponseData.Write(count);
return (ResultCode)result.Value;
return (ResultCode)result.Value;
}
}
protected override void Dispose(bool isDisposing)

View File

@ -17,17 +17,17 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
// Read() -> (u64 count, buffer<nn::fssrv::sf::IDirectoryEntry, 6, 0> entries)
public ResultCode Read(ServiceCtx context)
{
ulong bufferPosition = context.Request.ReceiveBuff[0].Position;
ulong bufferAddress = context.Request.ReceiveBuff[0].Position;
ulong bufferLen = context.Request.ReceiveBuff[0].Size;
byte[] entryBuffer = new byte[bufferLen];
using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true))
{
Result result = _baseDirectory.Get.Read(out long entriesRead, new OutBuffer(region.Memory.Span));
Result result = _baseDirectory.Get.Read(out long entriesRead, new OutBuffer(entryBuffer));
context.ResponseData.Write(entriesRead);
context.Memory.Write(bufferPosition, entryBuffer);
context.ResponseData.Write(entriesRead);
return (ResultCode)result.Value;
return (ResultCode)result.Value;
}
}
[CommandHipc(1)]

View File

@ -19,7 +19,8 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
// Read(u32 readOption, u64 offset, u64 size) -> (u64 out_size, buffer<u8, 0x46, 0> out_buf)
public ResultCode Read(ServiceCtx context)
{
ulong position = context.Request.ReceiveBuff[0].Position;
ulong bufferAddress = context.Request.ReceiveBuff[0].Position;
ulong bufferLen = context.Request.ReceiveBuff[0].Size;
ReadOption readOption = context.RequestData.ReadStruct<ReadOption>();
context.RequestData.BaseStream.Position += 4;
@ -27,15 +28,14 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
long offset = context.RequestData.ReadInt64();
long size = context.RequestData.ReadInt64();
byte[] data = new byte[context.Request.ReceiveBuff[0].Size];
using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true))
{
Result result = _baseFile.Get.Read(out long bytesRead, offset, new OutBuffer(region.Memory.Span), size, readOption);
Result result = _baseFile.Get.Read(out long bytesRead, offset, new OutBuffer(data), size, readOption);
context.ResponseData.Write(bytesRead);
context.Memory.Write(position, data);
context.ResponseData.Write(bytesRead);
return (ResultCode)result.Value;
return (ResultCode)result.Value;
}
}
[CommandHipc(1)]

View File

@ -197,13 +197,7 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
context.ResponseData.Write(timestamp.Created);
context.ResponseData.Write(timestamp.Modified);
context.ResponseData.Write(timestamp.Accessed);
byte[] data = new byte[8];
// is valid?
data[0] = 1;
context.ResponseData.Write(data);
context.ResponseData.Write(1L); // Is valid?
return (ResultCode)result.Value;
}

View File

@ -23,21 +23,21 @@ namespace Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy
if (context.Request.ReceiveBuff.Count > 0)
{
IpcBuffDesc buffDesc = context.Request.ReceiveBuff[0];
ulong bufferAddress = context.Request.ReceiveBuff[0].Position;
ulong bufferLen = context.Request.ReceiveBuff[0].Size;
// Use smaller length to avoid overflows.
if (size > buffDesc.Size)
if (size > bufferLen)
{
size = buffDesc.Size;
size = bufferLen;
}
byte[] data = new byte[size];
using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true))
{
Result result = _baseStorage.Get.Read((long)offset, new OutBuffer(region.Memory.Span), (long)size);
Result result = _baseStorage.Get.Read((long)offset, new OutBuffer(data), (long)size);
context.Memory.Write(buffDesc.Position, data);
return (ResultCode)result.Value;
return (ResultCode)result.Value;
}
}
return ResultCode.Success;

View File

@ -500,16 +500,16 @@ namespace Ryujinx.HLE.HOS.Services.Fs
SaveDataSpaceId spaceId = (SaveDataSpaceId)context.RequestData.ReadInt64();
SaveDataFilter filter = context.RequestData.ReadStruct<SaveDataFilter>();
ulong bufferPosition = context.Request.ReceiveBuff[0].Position;
ulong bufferAddress = context.Request.ReceiveBuff[0].Position;
ulong bufferLen = context.Request.ReceiveBuff[0].Size;
byte[] infoBuffer = new byte[bufferLen];
using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true))
{
Result result = _baseFileSystemProxy.Get.FindSaveDataWithFilter(out long count, new OutBuffer(region.Memory.Span), spaceId, in filter);
if (result.IsFailure()) return (ResultCode)result.Value;
Result result = _baseFileSystemProxy.Get.FindSaveDataWithFilter(out long count, new OutBuffer(infoBuffer), spaceId, in filter);
if (result.IsFailure()) return (ResultCode)result.Value;
context.Memory.Write(bufferPosition, infoBuffer);
context.ResponseData.Write(count);
context.ResponseData.Write(count);
}
return ResultCode.Success;
}

View File

@ -17,17 +17,17 @@ namespace Ryujinx.HLE.HOS.Services.Fs
// ReadSaveDataInfo() -> (u64, buffer<unknown, 6>)
public ResultCode ReadSaveDataInfo(ServiceCtx context)
{
ulong bufferPosition = context.Request.ReceiveBuff[0].Position;
ulong bufferAddress = context.Request.ReceiveBuff[0].Position;
ulong bufferLen = context.Request.ReceiveBuff[0].Size;
byte[] infoBuffer = new byte[bufferLen];
using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true))
{
Result result = _baseReader.Get.Read(out long readCount, new OutBuffer(region.Memory.Span));
Result result = _baseReader.Get.Read(out long readCount, new OutBuffer(infoBuffer));
context.ResponseData.Write(readCount);
context.Memory.Write(bufferPosition, infoBuffer);
context.ResponseData.Write(readCount);
return (ResultCode)result.Value;
return (ResultCode)result.Value;
}
}
protected override void Dispose(bool isDisposing)

View File

@ -3,7 +3,6 @@ using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gpu.Memory;
using Ryujinx.Memory;
using System;
using System.Collections.Concurrent;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
{
@ -11,13 +10,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
{
private const int FlagNotFreedYet = 1;
private static ConcurrentDictionary<ulong, IdDictionary> _maps = new ConcurrentDictionary<ulong, IdDictionary>();
private static NvMapIdDictionary _maps = new NvMapIdDictionary();
public NvMapDeviceFile(ServiceCtx context, IVirtualMemoryManager memory, ulong owner) : base(context, owner)
{
IdDictionary dict = _maps.GetOrAdd(Owner, (key) => new IdDictionary());
dict.Add(0, new NvMapHandle());
}
public override NvInternalResult Ioctl(NvIoctl command, Span<byte> arguments)
@ -232,19 +228,12 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
private int CreateHandleFromMap(NvMapHandle map)
{
IdDictionary dict = _maps.GetOrAdd(Owner, (key) => new IdDictionary());
return dict.Add(map);
return _maps.Add(map);
}
private static bool DeleteMapWithHandle(ulong pid, int handle)
{
if (_maps.TryGetValue(pid, out IdDictionary dict))
{
return dict.Delete(handle) != null;
}
return false;
return _maps.Delete(handle) != null;
}
public static void IncrementMapRefCount(ulong pid, int handle)
@ -277,12 +266,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
public static NvMapHandle GetMapFromHandle(ulong pid, int handle)
{
if (_maps.TryGetValue(pid, out IdDictionary dict))
{
return dict.GetData<NvMapHandle>(handle);
}
return null;
return _maps.Get(handle);
}
}
}

View File

@ -0,0 +1,61 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap
{
class NvMapIdDictionary
{
private readonly ConcurrentDictionary<int, NvMapHandle> _nvmapHandles;
private int _id;
public ICollection<NvMapHandle> Values => _nvmapHandles.Values;
public NvMapIdDictionary()
{
_nvmapHandles = new ConcurrentDictionary<int, NvMapHandle>();
}
public int Add(NvMapHandle handle)
{
int id = Interlocked.Add(ref _id, 4);
if (id != 0 && _nvmapHandles.TryAdd(id, handle))
{
return id;
}
throw new InvalidOperationException("NvMap ID overflow.");
}
public NvMapHandle Get(int id)
{
if (_nvmapHandles.TryGetValue(id, out NvMapHandle handle))
{
return handle;
}
return null;
}
public NvMapHandle Delete(int id)
{
if (_nvmapHandles.TryRemove(id, out NvMapHandle handle))
{
return handle;
}
return null;
}
public ICollection<NvMapHandle> Clear()
{
ICollection<NvMapHandle> values = _nvmapHandles.Values;
_nvmapHandles.Clear();
return values;
}
}
}

View File

@ -235,6 +235,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
shouldBlockAfterOperation = true;
}
if (!Socket.IsBound)
{
receiveSize = -1;
return LinuxError.EOPNOTSUPP;
}
receiveSize = Socket.ReceiveFrom(buffer[..size], ConvertBsdSocketFlags(flags), ref temp);
remoteEndPoint = (IPEndPoint)temp;
@ -519,4 +526,4 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
}
}
}
}
}

View File

@ -142,14 +142,13 @@ namespace Ryujinx.HLE.HOS.Services.Ssl.SslService
// GetHostName(buffer<bytes, 6>) -> u32
public ResultCode GetHostName(ServiceCtx context)
{
ulong hostNameDataPosition = context.Request.ReceiveBuff[0].Position;
ulong hostNameDataSize = context.Request.ReceiveBuff[0].Size;
ulong bufferAddress = context.Request.ReceiveBuff[0].Position;
ulong bufferLen = context.Request.ReceiveBuff[0].Size;
byte[] hostNameData = new byte[hostNameDataSize];
Encoding.ASCII.GetBytes(_hostName, hostNameData);
context.Memory.Write(hostNameDataPosition, hostNameData);
using (var region = context.Memory.GetWritableRegion(bufferAddress, (int)bufferLen, true))
{
Encoding.ASCII.GetBytes(_hostName, region.Memory.Span);
}
context.ResponseData.Write((uint)_hostName.Length);