Compare commits

..

8 Commits

Author SHA1 Message Date
408bd63b08 Transform shader LDC into constant buffer access if offset is constant (#3672)
* Transform shader LDC into constant buffer access if offset is constant

* Shader cache version bump
2022-09-07 20:25:22 -03:00
df99257d7f bsd: improve socket poll
We should report errors even when not requested.

This also ensure we only clear the bits that were requested on the output.

Finally, this fix when input events is 0.
2022-09-07 22:58:41 +02:00
f3835dc78b bsd: implement SendMMsg and RecvMMsg (#3660)
* bsd: implement sendmmsg and recvmmsg

* Fix wrong increment of vlen
2022-09-07 22:37:15 +02:00
51bb8707ef Update bug report template (#3676)
Adds some verbiage to indicate that game-specific issues should be posted instead on the game compatibility list, unless it is a provable regression.
2022-09-06 22:30:07 +02:00
5ff5fe47ba Bsd: Fix NullReferenceException in BsdSockAddr.FromIPEndPoint() (#3652)
* Bsd: Fix NullReferenceException in BsdSockAddr.FromIPEndPoint()

Allows "Victor Vran Overkill Edition" to boot with guest internet access enabled.
Thanks to EmulationFanatic for testing this for me!

* Bsd: Return proper error code if RemoteEndPoint is null

* Remove whitespace from empty line

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2022-09-01 22:04:01 +00:00
38275f9056 Change vsync signal to happen at 60hz, regardless of swap interval (#3642)
* Change vsync signal to happen at 60hz, regardless of swap interval

* Update Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

* Fix softlock when toggling vsync

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2022-09-01 17:57:50 -03:00
67cbdc3a6a bsd: Fix Poll(0) returning ETIMEDOUT instead of SUCCESS
This was an oversight of the implementation.
2022-09-01 21:46:11 +02:00
131b43170e sfdsnres: fix endianess issue for port serialisation 2022-09-01 21:31:20 +02:00
12 changed files with 564 additions and 23 deletions

View File

@ -1,6 +1,6 @@
---
name: Bug Report
about: Something doesn't work correctly in Ryujinx.
about: Something doesn't work correctly in Ryujinx. Note that game-specific issues should be instead posted on the Game Compatibility List at https://github.com/Ryujinx/Ryujinx-Games-List, unless it is a provable regression.
#assignees:
---

View File

@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 3525;
private const uint CodeGenVersion = 3672;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";

View File

@ -153,6 +153,10 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
EvaluateFPUnary(operation, (x) => float.IsNaN(x));
break;
case Instruction.LoadConstant:
operation.TurnIntoCopy(Cbuf(operation.GetSource(0).Value, operation.GetSource(1).Value));
break;
case Instruction.Maximum:
EvaluateBinary(operation, (x, y) => Math.Max(x, y));
break;

View File

@ -336,6 +336,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
context.Memory.Write(outputBufferPosition + (ulong)(i * Unsafe.SizeOf<PollEventData>()), events[i].Data);
}
// In case of non blocking call timeout should not be returned.
if (timeout == 0 && errno == LinuxError.ETIMEDOUT)
{
errno = LinuxError.SUCCESS;
}
return WriteBsdResult(context, updateCount, errno);
}
@ -567,8 +573,11 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
LinuxError errno = LinuxError.EBADF;
ISocket socket = _context.RetrieveSocket(socketFd);
if (socket != null)
{
errno = LinuxError.ENOTCONN;
if (socket.RemoteEndPoint != null)
{
errno = LinuxError.SUCCESS;
@ -576,6 +585,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
WriteBsdResult(context, 0, errno);
context.ResponseData.Write(Unsafe.SizeOf<BsdSockAddr>());
}
}
return WriteBsdResult(context, 0, errno);
}
@ -876,6 +886,91 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
return WriteBsdResult(context, newSockFd, errno);
}
[CommandHipc(29)] // 7.0.0+
// RecvMMsg(u32 fd, u32 vlen, u32 flags, u32 reserved, nn::socket::TimeVal timeout) -> (i32 ret, u32 bsd_errno, buffer<bytes, 6> message);
public ResultCode RecvMMsg(ServiceCtx context)
{
int socketFd = context.RequestData.ReadInt32();
int vlen = context.RequestData.ReadInt32();
BsdSocketFlags socketFlags = (BsdSocketFlags)context.RequestData.ReadInt32();
uint reserved = context.RequestData.ReadUInt32();
TimeVal timeout = context.RequestData.ReadStruct<TimeVal>();
ulong receivePosition = context.Request.ReceiveBuff[0].Position;
ulong receiveLength = context.Request.ReceiveBuff[0].Size;
WritableRegion receiveRegion = context.Memory.GetWritableRegion(receivePosition, (int)receiveLength);
LinuxError errno = LinuxError.EBADF;
ISocket socket = _context.RetrieveSocket(socketFd);
int result = -1;
if (socket != null)
{
errno = BsdMMsgHdr.Deserialize(out BsdMMsgHdr message, receiveRegion.Memory.Span, vlen);
if (errno == LinuxError.SUCCESS)
{
errno = socket.RecvMMsg(out result, message, socketFlags, timeout);
if (errno == LinuxError.SUCCESS)
{
errno = BsdMMsgHdr.Serialize(receiveRegion.Memory.Span, message);
}
}
}
if (errno == LinuxError.SUCCESS)
{
SetResultErrno(socket, result);
receiveRegion.Dispose();
}
return WriteBsdResult(context, result, errno);
}
[CommandHipc(30)] // 7.0.0+
// SendMMsg(u32 fd, u32 vlen, u32 flags) -> (i32 ret, u32 bsd_errno, buffer<bytes, 6> message);
public ResultCode SendMMsg(ServiceCtx context)
{
int socketFd = context.RequestData.ReadInt32();
int vlen = context.RequestData.ReadInt32();
BsdSocketFlags socketFlags = (BsdSocketFlags)context.RequestData.ReadInt32();
ulong receivePosition = context.Request.ReceiveBuff[0].Position;
ulong receiveLength = context.Request.ReceiveBuff[0].Size;
WritableRegion receiveRegion = context.Memory.GetWritableRegion(receivePosition, (int)receiveLength);
LinuxError errno = LinuxError.EBADF;
ISocket socket = _context.RetrieveSocket(socketFd);
int result = -1;
if (socket != null)
{
errno = BsdMMsgHdr.Deserialize(out BsdMMsgHdr message, receiveRegion.Memory.Span, vlen);
if (errno == LinuxError.SUCCESS)
{
errno = socket.SendMMsg(out result, message, socketFlags);
if (errno == LinuxError.SUCCESS)
{
errno = BsdMMsgHdr.Serialize(receiveRegion.Memory.Span, message);
}
}
}
if (errno == LinuxError.SUCCESS)
{
SetResultErrno(socket, result);
receiveRegion.Dispose();
}
return WriteBsdResult(context, result, errno);
}
[CommandHipc(31)] // 7.0.0+
// EventFd(u64 initval, nn::socket::EventFdFlags flags) -> (i32 ret, u32 bsd_errno)
public ResultCode EventFd(ServiceCtx context)

View File

@ -25,7 +25,12 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
LinuxError SendTo(out int sendSize, ReadOnlySpan<byte> buffer, int size, BsdSocketFlags flags, IPEndPoint remoteEndPoint);
LinuxError RecvMMsg(out int vlen, BsdMMsgHdr message, BsdSocketFlags flags, TimeVal timeout);
LinuxError SendMMsg(out int vlen, BsdMMsgHdr message, BsdSocketFlags flags);
LinuxError GetSocketOption(BsdSocketOption option, SocketOptionLevel level, Span<byte> optionValue);
LinuxError SetSocketOption(BsdSocketOption option, SocketOptionLevel level, ReadOnlySpan<byte> optionValue);
bool Poll(int microSeconds, SelectMode mode);

View File

@ -1,5 +1,7 @@
using Ryujinx.Common.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
@ -356,5 +358,165 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
return Send(out writeSize, buffer, BsdSocketFlags.None);
}
private bool CanSupportMMsgHdr(BsdMMsgHdr message)
{
for (int i = 0; i < message.Messages.Length; i++)
{
if (message.Messages[i].Name != null ||
message.Messages[i].Control != null)
{
return false;
}
}
return true;
}
private static IList<ArraySegment<byte>> ConvertMessagesToBuffer(BsdMMsgHdr message)
{
int segmentCount = 0;
int index = 0;
foreach (BsdMsgHdr msgHeader in message.Messages)
{
segmentCount += msgHeader.Iov.Length;
}
ArraySegment<byte>[] buffers = new ArraySegment<byte>[segmentCount];
foreach (BsdMsgHdr msgHeader in message.Messages)
{
foreach (byte[] iov in msgHeader.Iov)
{
buffers[index++] = new ArraySegment<byte>(iov);
}
// Clear the length
msgHeader.Length = 0;
}
return buffers;
}
private static void UpdateMessages(out int vlen, BsdMMsgHdr message, int transferedSize)
{
int bytesLeft = transferedSize;
int index = 0;
while (bytesLeft > 0)
{
// First ensure we haven't finished all buffers
if (index >= message.Messages.Length)
{
break;
}
BsdMsgHdr msgHeader = message.Messages[index];
int possiblyTransferedBytes = 0;
foreach (byte[] iov in msgHeader.Iov)
{
possiblyTransferedBytes += iov.Length;
}
int storedBytes;
if (bytesLeft > possiblyTransferedBytes)
{
storedBytes = possiblyTransferedBytes;
index++;
}
else
{
storedBytes = bytesLeft;
}
msgHeader.Length = (uint)storedBytes;
bytesLeft -= storedBytes;
}
Debug.Assert(bytesLeft == 0);
vlen = index + 1;
}
// TODO: Find a way to support passing the timeout somehow without changing the socket ReceiveTimeout.
public LinuxError RecvMMsg(out int vlen, BsdMMsgHdr message, BsdSocketFlags flags, TimeVal timeout)
{
vlen = 0;
if (message.Messages.Length == 0)
{
return LinuxError.SUCCESS;
}
if (!CanSupportMMsgHdr(message))
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported BsdMMsgHdr");
return LinuxError.EOPNOTSUPP;
}
if (message.Messages.Length == 0)
{
return LinuxError.SUCCESS;
}
try
{
int receiveSize = Socket.Receive(ConvertMessagesToBuffer(message), ConvertBsdSocketFlags(flags), out SocketError socketError);
if (receiveSize > 0)
{
UpdateMessages(out vlen, message, receiveSize);
}
return WinSockHelper.ConvertError((WsaError)socketError);
}
catch (SocketException exception)
{
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
}
}
public LinuxError SendMMsg(out int vlen, BsdMMsgHdr message, BsdSocketFlags flags)
{
vlen = 0;
if (message.Messages.Length == 0)
{
return LinuxError.SUCCESS;
}
if (!CanSupportMMsgHdr(message))
{
Logger.Warning?.Print(LogClass.ServiceBsd, $"Unsupported BsdMMsgHdr");
return LinuxError.EOPNOTSUPP;
}
if (message.Messages.Length == 0)
{
return LinuxError.SUCCESS;
}
try
{
int sendSize = Socket.Send(ConvertMessagesToBuffer(message), ConvertBsdSocketFlags(flags), out SocketError socketError);
if (sendSize > 0)
{
UpdateMessages(out vlen, message, sendSize);
}
return WinSockHelper.ConvertError((WsaError)socketError);
}
catch (SocketException exception)
{
return WinSockHelper.ConvertError((WsaError)exception.ErrorCode);
}
}
}
}

View File

@ -38,12 +38,13 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
ManagedSocket socket = (ManagedSocket)evnt.FileDescriptor;
bool isValidEvent = false;
bool isValidEvent = evnt.Data.InputEvents == 0;
errorEvents.Add(socket.Socket);
if ((evnt.Data.InputEvents & PollEventTypeMask.Input) != 0)
{
readEvents.Add(socket.Socket);
errorEvents.Add(socket.Socket);
isValidEvent = true;
}
@ -51,7 +52,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
if ((evnt.Data.InputEvents & PollEventTypeMask.UrgentInput) != 0)
{
readEvents.Add(socket.Socket);
errorEvents.Add(socket.Socket);
isValidEvent = true;
}
@ -59,14 +59,6 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
if ((evnt.Data.InputEvents & PollEventTypeMask.Output) != 0)
{
writeEvents.Add(socket.Socket);
errorEvents.Add(socket.Socket);
isValidEvent = true;
}
if ((evnt.Data.InputEvents & PollEventTypeMask.Error) != 0)
{
errorEvents.Add(socket.Socket);
isValidEvent = true;
}
@ -93,7 +85,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
Socket socket = ((ManagedSocket)evnt.FileDescriptor).Socket;
PollEventTypeMask outputEvents = 0;
PollEventTypeMask outputEvents = evnt.Data.OutputEvents & ~evnt.Data.InputEvents;
if (errorEvents.Contains(socket))
{

View File

@ -0,0 +1,56 @@
using System;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
class BsdMMsgHdr
{
public BsdMsgHdr[] Messages { get; }
private BsdMMsgHdr(BsdMsgHdr[] messages)
{
Messages = messages;
}
public static LinuxError Serialize(Span<byte> rawData, BsdMMsgHdr message)
{
rawData[0] = 0x8;
rawData = rawData[1..];
for (int index = 0; index < message.Messages.Length; index++)
{
LinuxError res = BsdMsgHdr.Serialize(ref rawData, message.Messages[index]);
if (res != LinuxError.SUCCESS)
{
return res;
}
}
return LinuxError.SUCCESS;
}
public static LinuxError Deserialize(out BsdMMsgHdr message, ReadOnlySpan<byte> rawData, int vlen)
{
message = null;
BsdMsgHdr[] messages = new BsdMsgHdr[vlen];
// Skip "header" byte (Nintendo also ignore it)
rawData = rawData[1..];
for (int index = 0; index < messages.Length; index++)
{
LinuxError res = BsdMsgHdr.Deserialize(out messages[index], ref rawData);
if (res != LinuxError.SUCCESS)
{
return res;
}
}
message = new BsdMMsgHdr(messages);
return LinuxError.SUCCESS;
}
}
}

View File

@ -0,0 +1,212 @@
using System;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
class BsdMsgHdr
{
public byte[] Name { get; }
public byte[][] Iov { get; }
public byte[] Control { get; }
public BsdSocketFlags Flags { get; }
public uint Length;
private BsdMsgHdr(byte[] name, byte[][] iov, byte[] control, BsdSocketFlags flags, uint length)
{
Name = name;
Iov = iov;
Control = control;
Flags = flags;
Length = length;
}
public static LinuxError Serialize(ref Span<byte> rawData, BsdMsgHdr message)
{
int msgNameLength = message.Name == null ? 0 : message.Name.Length;
int iovCount = message.Iov == null ? 0 : message.Iov.Length;
int controlLength = message.Control == null ? 0 : message.Control.Length;
BsdSocketFlags flags = message.Flags;
if (!MemoryMarshal.TryWrite(rawData, ref msgNameLength))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(uint)..];
if (msgNameLength > 0)
{
if (rawData.Length < msgNameLength)
{
return LinuxError.EFAULT;
}
message.Name.CopyTo(rawData);
rawData = rawData[msgNameLength..];
}
if (!MemoryMarshal.TryWrite(rawData, ref iovCount))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(uint)..];
if (iovCount > 0)
{
for (int index = 0; index < iovCount; index++)
{
ulong iovLength = (ulong)message.Iov[index].Length;
if (!MemoryMarshal.TryWrite(rawData, ref iovLength))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(ulong)..];
if (iovLength > 0)
{
if ((ulong)rawData.Length < iovLength)
{
return LinuxError.EFAULT;
}
message.Iov[index].CopyTo(rawData);
rawData = rawData[(int)iovLength..];
}
}
}
if (!MemoryMarshal.TryWrite(rawData, ref controlLength))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(uint)..];
if (controlLength > 0)
{
if (rawData.Length < controlLength)
{
return LinuxError.EFAULT;
}
message.Control.CopyTo(rawData);
rawData = rawData[controlLength..];
}
if (!MemoryMarshal.TryWrite(rawData, ref flags))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(BsdSocketFlags)..];
if (!MemoryMarshal.TryWrite(rawData, ref message.Length))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(uint)..];
return LinuxError.SUCCESS;
}
public static LinuxError Deserialize(out BsdMsgHdr message, ref ReadOnlySpan<byte> rawData)
{
byte[] name = null;
byte[][] iov = null;
byte[] control = null;
message = null;
if (!MemoryMarshal.TryRead(rawData, out uint msgNameLength))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(uint)..];
if (msgNameLength > 0)
{
if (rawData.Length < msgNameLength)
{
return LinuxError.EFAULT;
}
name = rawData[..(int)msgNameLength].ToArray();
rawData = rawData[(int)msgNameLength..];
}
if (!MemoryMarshal.TryRead(rawData, out uint iovCount))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(uint)..];
if (iovCount > 0)
{
iov = new byte[iovCount][];
for (int index = 0; index < iov.Length; index++)
{
if (!MemoryMarshal.TryRead(rawData, out ulong iovLength))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(ulong)..];
if (iovLength > 0)
{
if ((ulong)rawData.Length < iovLength)
{
return LinuxError.EFAULT;
}
iov[index] = rawData[..(int)iovLength].ToArray();
rawData = rawData[(int)iovLength..];
}
}
}
if (!MemoryMarshal.TryRead(rawData, out uint controlLength))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(uint)..];
if (controlLength > 0)
{
if (rawData.Length < controlLength)
{
return LinuxError.EFAULT;
}
control = rawData[..(int)controlLength].ToArray();
rawData = rawData[(int)controlLength..];
}
if (!MemoryMarshal.TryRead(rawData, out BsdSocketFlags flags))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(BsdSocketFlags)..];
if (!MemoryMarshal.TryRead(rawData, out uint length))
{
return LinuxError.EFAULT;
}
rawData = rawData[sizeof(uint)..];
message = new BsdMsgHdr(name, iov, control, flags, length);
return LinuxError.SUCCESS;
}
}
}

View File

@ -0,0 +1,8 @@
namespace Ryujinx.HLE.HOS.Services.Sockets.Bsd
{
public struct TimeVal
{
public ulong TvSec;
public ulong TvUsec;
}
}

View File

@ -19,7 +19,7 @@ namespace Ryujinx.HLE.HOS.Services.Sockets.Sfdnsres.Types
{
Length = (byte)Unsafe.SizeOf<Array4<byte>>();
Family = (byte)AddressFamily.InterNetwork;
Port = port;
Port = IPAddress.HostToNetworkOrder(port);
Address = new Array4<byte>();
address.TryWriteBytes(Address.AsSpan(), out _);

View File

@ -35,6 +35,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
private long _1msTicks;
private int _swapInterval;
private int _swapIntervalDelay;
private readonly object Lock = new object();
@ -91,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
}
else
{
_ticksPerFrame = Stopwatch.Frequency / (TargetFps / _swapInterval);
_ticksPerFrame = Stopwatch.Frequency / TargetFps;
}
}
@ -321,9 +322,15 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
lastTicks = ticks;
if (_ticks >= _ticksPerFrame)
{
if (_swapIntervalDelay-- == 0)
{
Compose();
// When a frame is presented, delay the next one by its swap interval value.
_swapIntervalDelay = Math.Max(0, _swapInterval - 1);
}
_device.System?.SignalVsync();
// Apply a maximum bound of 3 frames to the tick remainder, in case some event causes Ryujinx to pause for a long time or messes with the timer.