diff --git a/Ryujinx.Graphics.Gpu/Image/Texture.cs b/Ryujinx.Graphics.Gpu/Image/Texture.cs index 2b1aaa0ad6..0161062948 100644 --- a/Ryujinx.Graphics.Gpu/Image/Texture.cs +++ b/Ryujinx.Graphics.Gpu/Image/Texture.cs @@ -181,7 +181,7 @@ namespace Ryujinx.Graphics.Gpu.Image { Debug.Assert(!isView); - TextureCreateInfo createInfo = TextureManager.GetCreateInfo(Info, _context.Capabilities); + TextureCreateInfo createInfo = TextureManager.GetCreateInfo(Info, _context.Capabilities, ScaleFactor); HostTexture = _context.Renderer.CreateTexture(createInfo, ScaleFactor); SynchronizeMemory(); // Load the data. @@ -204,7 +204,7 @@ namespace Ryujinx.Graphics.Gpu.Image ScaleFactor = GraphicsConfig.ResScale; } - TextureCreateInfo createInfo = TextureManager.GetCreateInfo(Info, _context.Capabilities); + TextureCreateInfo createInfo = TextureManager.GetCreateInfo(Info, _context.Capabilities, ScaleFactor); HostTexture = _context.Renderer.CreateTexture(createInfo, ScaleFactor); } } @@ -232,8 +232,7 @@ namespace Ryujinx.Graphics.Gpu.Image ScaleFactor, ScaleMode); - TextureCreateInfo createInfo = TextureManager.GetCreateInfo(info, _context.Capabilities); - + TextureCreateInfo createInfo = TextureManager.GetCreateInfo(info, _context.Capabilities, ScaleFactor); texture.HostTexture = HostTexture.CreateView(createInfo, firstLayer, firstLevel); _viewStorage.AddView(texture); @@ -375,7 +374,7 @@ namespace Ryujinx.Graphics.Gpu.Image Info.SwizzleB, Info.SwizzleA)); - TextureCreateInfo createInfo = TextureManager.GetCreateInfo(Info, _context.Capabilities); + TextureCreateInfo createInfo = TextureManager.GetCreateInfo(Info, _context.Capabilities, ScaleFactor); if (_viewStorage != this) { @@ -451,7 +450,7 @@ namespace Ryujinx.Graphics.Gpu.Image Info.SwizzleB, Info.SwizzleA); - TextureCreateInfo createInfo = TextureManager.GetCreateInfo(viewInfo, _context.Capabilities); + TextureCreateInfo createInfo = TextureManager.GetCreateInfo(viewInfo, _context.Capabilities, ScaleFactor); for (int i = 0; i < Info.DepthOrLayers; i++) { @@ -475,8 +474,7 @@ namespace Ryujinx.Graphics.Gpu.Image { if (storage == null) { - TextureCreateInfo createInfo = TextureManager.GetCreateInfo(Info, _context.Capabilities); - + TextureCreateInfo createInfo = TextureManager.GetCreateInfo(Info, _context.Capabilities, scale); storage = _context.Renderer.CreateTexture(createInfo, scale); } @@ -530,12 +528,10 @@ namespace Ryujinx.Graphics.Gpu.Image Logger.Debug?.Print(LogClass.Gpu, $" Recreating view {Info.Width}x{Info.Height} {Info.FormatInfo.Format.ToString()}."); view.ScaleFactor = scale; - TextureCreateInfo viewCreateInfo = TextureManager.GetCreateInfo(view.Info, _context.Capabilities); - + TextureCreateInfo viewCreateInfo = TextureManager.GetCreateInfo(view.Info, _context.Capabilities, scale); ITexture newView = HostTexture.CreateView(viewCreateInfo, view._firstLayer - _firstLayer, view._firstLevel - _firstLevel); view.ReplaceStorage(newView); - view.ScaleMode = newScaleMode; } } diff --git a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs index 27292f56c9..b0e715ea4c 100644 --- a/Ryujinx.Graphics.Gpu/Image/TextureManager.cs +++ b/Ryujinx.Graphics.Gpu/Image/TextureManager.cs @@ -754,7 +754,7 @@ namespace Ryujinx.Graphics.Gpu.Image } break; - } + } else if (overlapCompatibility == TextureViewCompatibility.CopyOnly) { // TODO: Copy rules for targets created after the container texture. See below. @@ -833,7 +833,7 @@ namespace Ryujinx.Graphics.Gpu.Image TextureInfo overlapInfo = AdjustSizes(texture, overlap.Info, oInfo.FirstLevel); - TextureCreateInfo createInfo = GetCreateInfo(overlapInfo, _context.Capabilities); + TextureCreateInfo createInfo = GetCreateInfo(overlapInfo, _context.Capabilities, overlap.ScaleFactor); if (texture.ScaleFactor != overlap.ScaleFactor) { @@ -944,7 +944,7 @@ namespace Ryujinx.Graphics.Gpu.Image } else { - // Bpp may be a mismatch between the target texture and the param. + // Bpp may be a mismatch between the target texture and the param. // Due to the way linear strided and block layouts work, widths can be multiplied by Bpp for comparison. // Note: tex.Width is the aligned texture size. Prefer param.XCount, as the destination should be a texture with that exact size. @@ -1054,8 +1054,9 @@ namespace Ryujinx.Graphics.Gpu.Image /// /// Texture information /// GPU capabilities + /// Texture scale factor, to be applied to the texture size /// The texture creation information - public static TextureCreateInfo GetCreateInfo(TextureInfo info, Capabilities caps) + public static TextureCreateInfo GetCreateInfo(TextureInfo info, Capabilities caps, float scale) { FormatInfo formatInfo = info.FormatInfo; @@ -1092,6 +1093,12 @@ namespace Ryujinx.Graphics.Gpu.Image int depth = info.GetDepth() * info.GetLayers(); + if (scale != 1f) + { + width = (int)MathF.Ceiling(width * scale); + height = (int)MathF.Ceiling(height * scale); + } + return new TextureCreateInfo( width, height, diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs b/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs index 5f786decc7..2e70fa82a0 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs @@ -1,6 +1,5 @@ using OpenTK.Graphics.OpenGL; using Ryujinx.Graphics.GAL; -using System; namespace Ryujinx.Graphics.OpenGL.Image { @@ -10,8 +9,8 @@ namespace Ryujinx.Graphics.OpenGL.Image public TextureCreateInfo Info { get; } - public int Width { get; } - public int Height { get; } + public int Width => Info.Width; + public int Height => Info.Height; public float ScaleFactor { get; } public Target Target => Info.Target; @@ -20,8 +19,6 @@ namespace Ryujinx.Graphics.OpenGL.Image public TextureBase(TextureCreateInfo info, float scaleFactor = 1f) { Info = info; - Width = (int)Math.Ceiling(Info.Width * scaleFactor); - Height = (int)Math.Ceiling(Info.Height * scaleFactor); ScaleFactor = scaleFactor; Handle = GL.GenTexture(); diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs b/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs index edfbec5f82..191e9b634c 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs @@ -129,7 +129,7 @@ namespace Ryujinx.Graphics.OpenGL.Image public TextureView BgraSwap(TextureView from) { - TextureView to = (TextureView)_renderer.CreateTexture(from.Info, 1f); + TextureView to = (TextureView)_renderer.CreateTexture(from.Info, from.ScaleFactor); EnsurePbo(from); diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureCopyUnscaled.cs b/Ryujinx.Graphics.OpenGL/Image/TextureCopyUnscaled.cs index 02ae3b581a..2840113854 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureCopyUnscaled.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureCopyUnscaled.cs @@ -15,16 +15,15 @@ namespace Ryujinx.Graphics.OpenGL.Image int srcLayer, int dstLayer, int srcLevel, - int dstLevel, - float scaleFactor = 1f) + int dstLevel) { - int srcWidth = (int)Math.Ceiling(srcInfo.Width * scaleFactor); - int srcHeight = (int)Math.Ceiling(srcInfo.Height * scaleFactor); + int srcWidth = srcInfo.Width; + int srcHeight = srcInfo.Height; int srcDepth = srcInfo.GetDepthOrLayers(); int srcLevels = srcInfo.Levels; - int dstWidth = (int)Math.Ceiling(dstInfo.Width * scaleFactor); - int dstHeight = (int)Math.Ceiling(dstInfo.Height * scaleFactor); + int dstWidth = dstInfo.Width; + int dstHeight = dstInfo.Height; int dstDepth = dstInfo.GetDepthOrLayers(); int dstLevels = dstInfo.Levels; diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs b/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs index 635b6c2ce8..b34b02bf78 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureStorage.cs @@ -37,9 +37,6 @@ namespace Ryujinx.Graphics.OpenGL.Image GL.BindTexture(target, Handle); - int width = (int)Math.Ceiling(Info.Width * ScaleFactor); - int height = (int)Math.Ceiling(Info.Height * ScaleFactor); - FormatInfo format = FormatTable.GetFormatInfo(Info.Format); SizedInternalFormat internalFormat; @@ -60,7 +57,7 @@ namespace Ryujinx.Graphics.OpenGL.Image TextureTarget1d.Texture1D, Info.Levels, internalFormat, - width); + Info.Width); break; case Target.Texture1DArray: @@ -68,8 +65,8 @@ namespace Ryujinx.Graphics.OpenGL.Image TextureTarget2d.Texture1DArray, Info.Levels, internalFormat, - width, - height); + Info.Width, + Info.Height); break; case Target.Texture2D: @@ -77,8 +74,8 @@ namespace Ryujinx.Graphics.OpenGL.Image TextureTarget2d.Texture2D, Info.Levels, internalFormat, - width, - height); + Info.Width, + Info.Height); break; case Target.Texture2DArray: @@ -86,8 +83,8 @@ namespace Ryujinx.Graphics.OpenGL.Image TextureTarget3d.Texture2DArray, Info.Levels, internalFormat, - width, - height, + Info.Width, + Info.Height, Info.Depth); break; @@ -96,8 +93,8 @@ namespace Ryujinx.Graphics.OpenGL.Image TextureTargetMultisample2d.Texture2DMultisample, Info.Samples, internalFormat, - width, - height, + Info.Width, + Info.Height, true); break; @@ -106,8 +103,8 @@ namespace Ryujinx.Graphics.OpenGL.Image TextureTargetMultisample3d.Texture2DMultisampleArray, Info.Samples, internalFormat, - width, - height, + Info.Width, + Info.Height, Info.Depth, true); break; @@ -117,8 +114,8 @@ namespace Ryujinx.Graphics.OpenGL.Image TextureTarget3d.Texture3D, Info.Levels, internalFormat, - width, - height, + Info.Width, + Info.Height, Info.Depth); break; @@ -127,8 +124,8 @@ namespace Ryujinx.Graphics.OpenGL.Image TextureTarget2d.TextureCubeMap, Info.Levels, internalFormat, - width, - height); + Info.Width, + Info.Height); break; case Target.CubemapArray: @@ -136,8 +133,8 @@ namespace Ryujinx.Graphics.OpenGL.Image (TextureTarget3d)All.TextureCubeMapArray, Info.Levels, internalFormat, - width, - height, + Info.Width, + Info.Height, Info.Depth); break; diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index 04cadae73a..449c18d4b6 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -134,7 +134,7 @@ namespace Ryujinx.Graphics.OpenGL.Image _incompatibleFormatView = (TextureView)_renderer.CreateTexture(Info, ScaleFactor); } - TextureCopyUnscaled.Copy(_parent.Info, _incompatibleFormatView.Info, _parent.Handle, _incompatibleFormatView.Handle, FirstLayer, 0, FirstLevel, 0, ScaleFactor); + TextureCopyUnscaled.Copy(_parent.Info, _incompatibleFormatView.Info, _parent.Handle, _incompatibleFormatView.Handle, FirstLayer, 0, FirstLevel, 0); return _incompatibleFormatView.Handle; } @@ -146,7 +146,7 @@ namespace Ryujinx.Graphics.OpenGL.Image { if (_incompatibleFormatView != null) { - TextureCopyUnscaled.Copy(_incompatibleFormatView.Info, _parent.Info, _incompatibleFormatView.Handle, _parent.Handle, 0, FirstLayer, 0, FirstLevel, ScaleFactor); + TextureCopyUnscaled.Copy(_incompatibleFormatView.Info, _parent.Info, _incompatibleFormatView.Handle, _parent.Handle, 0, FirstLayer, 0, FirstLevel); } } @@ -154,7 +154,7 @@ namespace Ryujinx.Graphics.OpenGL.Image { TextureView destinationView = (TextureView)destination; - TextureCopyUnscaled.Copy(Info, destinationView.Info, Handle, destinationView.Handle, 0, firstLayer, 0, firstLevel, ScaleFactor); + TextureCopyUnscaled.Copy(Info, destinationView.Info, Handle, destinationView.Handle, 0, firstLayer, 0, firstLevel); if (destinationView._emulatedViewParent != null) { @@ -166,8 +166,7 @@ namespace Ryujinx.Graphics.OpenGL.Image 0, destinationView.FirstLayer, 0, - destinationView.FirstLevel, - ScaleFactor); + destinationView.FirstLevel); } } diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index 6f5e75aa76..0250d8520a 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -317,7 +317,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl continue; } - string samplerTypeName = GetSamplerTypeName(texOp.Type); + string samplerTypeName = texOp.Type.ToGlslSamplerType(); context.AppendLine("uniform " + samplerTypeName + " " + samplerName + ";"); } @@ -382,7 +382,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl layout = "layout(" + layout + ") "; } - string imageTypeName = GetImageTypeName(texOp.Type, texOp.Format.GetComponentType()); + string imageTypeName = texOp.Type.ToGlslImageType(texOp.Format.GetComponentType()); context.AppendLine("uniform " + layout + imageTypeName + " " + imageName + ";"); } @@ -537,72 +537,5 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl context.AppendLine(code.Replace("\t", CodeGenContext.Tab)); context.AppendLine(); } - - private static string GetSamplerTypeName(SamplerType type) - { - string typeName; - - switch (type & SamplerType.Mask) - { - case SamplerType.Texture1D: typeName = "sampler1D"; break; - case SamplerType.TextureBuffer: typeName = "samplerBuffer"; break; - case SamplerType.Texture2D: typeName = "sampler2D"; break; - case SamplerType.Texture3D: typeName = "sampler3D"; break; - case SamplerType.TextureCube: typeName = "samplerCube"; break; - - default: throw new ArgumentException($"Invalid sampler type \"{type}\"."); - } - - if ((type & SamplerType.Multisample) != 0) - { - typeName += "MS"; - } - - if ((type & SamplerType.Array) != 0) - { - typeName += "Array"; - } - - if ((type & SamplerType.Shadow) != 0) - { - typeName += "Shadow"; - } - - return typeName; - } - - private static string GetImageTypeName(SamplerType type, VariableType componentType) - { - string typeName; - - switch (type & SamplerType.Mask) - { - case SamplerType.Texture1D: typeName = "image1D"; break; - case SamplerType.TextureBuffer: typeName = "imageBuffer"; break; - case SamplerType.Texture2D: typeName = "image2D"; break; - case SamplerType.Texture3D: typeName = "image3D"; break; - case SamplerType.TextureCube: typeName = "imageCube"; break; - - default: throw new ArgumentException($"Invalid sampler type \"{type}\"."); - } - - if ((type & SamplerType.Multisample) != 0) - { - typeName += "MS"; - } - - if ((type & SamplerType.Array) != 0) - { - typeName += "Array"; - } - - switch (componentType) - { - case VariableType.U32: typeName = 'u' + typeName; break; - case VariableType.S32: typeName = 'i' + typeName; break; - } - - return typeName; - } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs index 7fdca13810..456bfc4eb2 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Instructions/InstGenMemory.cs @@ -407,20 +407,25 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl.Instructions if ((context.Config.Stage == ShaderStage.Fragment || context.Config.Stage == ShaderStage.Compute) && (texOp.Flags & TextureFlags.Bindless) == 0 && - texOp.Type != SamplerType.Indexed && - pCount == 2) + texOp.Type != SamplerType.Indexed) { - return "Helper_TexelFetchScale(" + vector + ", " + index + ")"; + if (pCount == 3 && isArray) + { + // The array index is not scaled, just x and y. + return "ivec3(Helper_TexelFetchScale((" + vector + ").xy, " + index + "), (" + vector + ").z)"; + } + else if (pCount == 2 && !isArray) + { + return "Helper_TexelFetchScale(" + vector + ", " + index + ")"; + } } - else - { - // Resolution scaling cannot be applied to this texture right now. - // Flag so that we know to blacklist scaling on related textures when binding them. - TextureDescriptor descriptor = context.TextureDescriptors[index]; - descriptor.Flags |= TextureUsageFlags.ResScaleUnsupported; - context.TextureDescriptors[index] = descriptor; - } + // Resolution scaling cannot be applied to this texture right now. + // Flag so that we know to blacklist scaling on related textures when binding them. + + TextureDescriptor descriptor = context.TextureDescriptors[index]; + descriptor.Flags |= TextureUsageFlags.ResScaleUnsupported; + context.TextureDescriptors[index] = descriptor; } return vector; diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index 14ea7032b7..8e878b0d03 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -247,7 +247,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { AstOperand operand = texOp.GetSource(0) as AstOperand; - suffix = "_cb" + operand.CbufSlot + "_" + operand.CbufOffset; + suffix = $"_{texOp.Type.ToGlslSamplerType()}_cb{operand.CbufSlot}_{operand.CbufOffset}"; } else { diff --git a/Ryujinx.Graphics.Shader/SamplerType.cs b/Ryujinx.Graphics.Shader/SamplerType.cs index 9546efe494..286ae9d5d6 100644 --- a/Ryujinx.Graphics.Shader/SamplerType.cs +++ b/Ryujinx.Graphics.Shader/SamplerType.cs @@ -1,3 +1,4 @@ +using Ryujinx.Graphics.Shader.StructuredIr; using System; namespace Ryujinx.Graphics.Shader @@ -35,5 +36,72 @@ namespace Ryujinx.Graphics.Shader throw new ArgumentException($"Invalid sampler type \"{type}\"."); } + + public static string ToGlslSamplerType(this SamplerType type) + { + string typeName; + + switch (type & SamplerType.Mask) + { + case SamplerType.Texture1D: typeName = "sampler1D"; break; + case SamplerType.TextureBuffer: typeName = "samplerBuffer"; break; + case SamplerType.Texture2D: typeName = "sampler2D"; break; + case SamplerType.Texture3D: typeName = "sampler3D"; break; + case SamplerType.TextureCube: typeName = "samplerCube"; break; + + default: throw new ArgumentException($"Invalid sampler type \"{type}\"."); + } + + if ((type & SamplerType.Multisample) != 0) + { + typeName += "MS"; + } + + if ((type & SamplerType.Array) != 0) + { + typeName += "Array"; + } + + if ((type & SamplerType.Shadow) != 0) + { + typeName += "Shadow"; + } + + return typeName; + } + + public static string ToGlslImageType(this SamplerType type, VariableType componentType) + { + string typeName; + + switch (type & SamplerType.Mask) + { + case SamplerType.Texture1D: typeName = "image1D"; break; + case SamplerType.TextureBuffer: typeName = "imageBuffer"; break; + case SamplerType.Texture2D: typeName = "image2D"; break; + case SamplerType.Texture3D: typeName = "image3D"; break; + case SamplerType.TextureCube: typeName = "imageCube"; break; + + default: throw new ArgumentException($"Invalid sampler type \"{type}\"."); + } + + if ((type & SamplerType.Multisample) != 0) + { + typeName += "MS"; + } + + if ((type & SamplerType.Array) != 0) + { + typeName += "Array"; + } + + switch (componentType) + { + case VariableType.U32: typeName = 'u' + typeName; break; + case VariableType.S32: typeName = 'i' + typeName; break; + } + + return typeName; + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs index 0decbfea95..2150f278e8 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/HidDevices/NpadDevices.cs @@ -337,6 +337,11 @@ namespace Ryujinx.HLE.HOS.Services.Hid { i++; + if (i >= states.Count) + { + return; + } + SetSixAxisState(states[i], true); } } diff --git a/Ryujinx/Motion/Client.cs b/Ryujinx/Motion/Client.cs index 07241ecd8d..c1822bcda3 100644 --- a/Ryujinx/Motion/Client.cs +++ b/Ryujinx/Motion/Client.cs @@ -25,6 +25,7 @@ namespace Ryujinx.Motion private readonly Dictionary _clients; private bool[] _clientErrorStatus = new bool[Enum.GetValues(typeof(PlayerIndex)).Length]; + private long[] _clientRetryTimer = new long[Enum.GetValues(typeof(PlayerIndex)).Length]; public Client() { @@ -63,18 +64,25 @@ namespace Ryujinx.Motion public void RegisterClient(int player, string host, int port) { - if (_clients.ContainsKey(player)) + if (_clients.ContainsKey(player) || !CanConnect(player)) { return; } - try + lock (_clients) { - lock (_clients) + if (_clients.ContainsKey(player) || !CanConnect(player)) + { + return; + } + + UdpClient client = null; + + try { IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse(host), port); - UdpClient client = new UdpClient(host, port); + client = new UdpClient(host, port); _clients.Add(player, client); _hosts.Add(player, endPoint); @@ -86,23 +94,39 @@ namespace Ryujinx.Motion ReceiveLoop(player); }); } - } - catch (FormatException fex) - { - if (!_clientErrorStatus[player]) + catch (FormatException fex) { - Logger.Warning?.PrintMsg(LogClass.Hid, $"Unable to connect to motion source at {host}:{port}. Error {fex.Message}"); + if (!_clientErrorStatus[player]) + { + Logger.Warning?.PrintMsg(LogClass.Hid, $"Unable to connect to motion source at {host}:{port}. Error {fex.Message}"); - _clientErrorStatus[player] = true; + _clientErrorStatus[player] = true; + } } - } - catch (SocketException ex) - { - if (!_clientErrorStatus[player]) + catch (SocketException sex) { - Logger.Warning?.PrintMsg(LogClass.Hid, $"Unable to connect to motion source at {host}:{port}. Error code {ex.ErrorCode}"); + if (!_clientErrorStatus[player]) + { + Logger.Warning?.PrintMsg(LogClass.Hid, $"Unable to connect to motion source at {host}:{port}. Error code {sex.ErrorCode}"); + _clientErrorStatus[player] = true; + } + + RemoveClient(player); + + client?.Dispose(); + + SetRetryTimer(player); + } + catch (Exception ex) + { _clientErrorStatus[player] = true; + + RemoveClient(player); + + client?.Dispose(); + + SetRetryTimer(player); } } } @@ -113,9 +137,10 @@ namespace Ryujinx.Motion { if (_motionData.ContainsKey(player)) { - input = _motionData[player][slot]; - - return true; + if (_motionData[player].TryGetValue(slot, out input)) + { + return true; + } } } @@ -124,6 +149,13 @@ namespace Ryujinx.Motion return false; } + private void RemoveClient(int clientId) + { + _clients?.Remove(clientId); + + _hosts?.Remove(clientId); + } + private void Send(byte[] data, int clientId) { if (_clients.TryGetValue(clientId, out UdpClient _client)) @@ -143,146 +175,185 @@ namespace Ryujinx.Motion _clientErrorStatus[clientId] = true; - _clients.Remove(clientId); - - _hosts.Remove(clientId); + RemoveClient(clientId); _client?.Dispose(); + + SetRetryTimer(clientId); + } + catch (ObjectDisposedException dex) + { + _clientErrorStatus[clientId] = true; + + RemoveClient(clientId); + + _client?.Dispose(); + + SetRetryTimer(clientId); } } } } - private byte[] Receive(int clientId) + private byte[] Receive(int clientId, int timeout = 0) { - if (_hosts.TryGetValue(clientId, out IPEndPoint endPoint)) + if (_hosts.TryGetValue(clientId, out IPEndPoint endPoint) && _clients.TryGetValue(clientId, out UdpClient _client)) { - if (_clients.TryGetValue(clientId, out UdpClient _client)) + if (_client != null && _client.Client != null && _client.Client.Connected) { - if (_client != null && _client.Client != null) + _client.Client.ReceiveTimeout = timeout; + + var result = _client?.Receive(ref endPoint); + + if (result.Length > 0) { - if (_client.Client.Connected) - { - try - { - var result = _client?.Receive(ref endPoint); - - if (result.Length > 0) - { - _clientErrorStatus[clientId] = false; - } - - return result; - } - catch (SocketException ex) - { - if (!_clientErrorStatus[clientId]) - { - Logger.Warning?.PrintMsg(LogClass.Hid, $"Unable to receive data from motion source at {endPoint}. Error code {ex.ErrorCode}"); - } - - _clientErrorStatus[clientId] = true; - - _clients.Remove(clientId); - - _hosts.Remove(clientId); - - _client?.Dispose(); - } - } + _clientErrorStatus[clientId] = false; } + + return result; } } - - return new byte[0]; + + throw new Exception($"Client {clientId} is not registered."); + } + + private void SetRetryTimer(int clientId) + { + var elapsedMs = PerformanceCounter.ElapsedMilliseconds; + + _clientRetryTimer[clientId] = elapsedMs; + } + + private void ResetRetryTimer(int clientId) + { + _clientRetryTimer[clientId] = 0; + } + + private bool CanConnect(int clientId) + { + return _clientRetryTimer[clientId] == 0 ? true : PerformanceCounter.ElapsedMilliseconds - 5000 > _clientRetryTimer[clientId]; } public void ReceiveLoop(int clientId) { - while (_active) + if (_hosts.TryGetValue(clientId, out IPEndPoint endPoint) && _clients.TryGetValue(clientId, out UdpClient _client)) { - byte[] data = Receive(clientId); - - if (data.Length == 0) + if (_client != null && _client.Client != null && _client.Client.Connected) { - continue; - } + try + { + while (_active) + { + byte[] data = Receive(clientId); + + if (data.Length == 0) + { + continue; + } #pragma warning disable CS4014 - HandleResponse(data, clientId); + Task.Run(() => HandleResponse(data, clientId)); #pragma warning restore CS4014 + } + } + catch (SocketException ex) + { + if (!_clientErrorStatus[clientId]) + { + Logger.Warning?.PrintMsg(LogClass.Hid, $"Unable to receive data from motion source at {endPoint}. Error code {ex.ErrorCode}"); + } + + _clientErrorStatus[clientId] = true; + + RemoveClient(clientId); + + _client?.Dispose(); + + SetRetryTimer(clientId); + } + catch (ObjectDisposedException) + { + _clientErrorStatus[clientId] = true; + + RemoveClient(clientId); + + _client?.Dispose(); + + SetRetryTimer(clientId); + } + } } } #pragma warning disable CS1998 - public async Task HandleResponse(byte[] data, int clientId) + public void HandleResponse(byte[] data, int clientId) #pragma warning restore CS1998 { + ResetRetryTimer(clientId); + MessageType type = (MessageType)BitConverter.ToUInt32(data.AsSpan().Slice(16, 4)); data = data.AsSpan().Slice(16).ToArray(); - using (MemoryStream mem = new MemoryStream(data)) + using MemoryStream mem = new MemoryStream(data); + + using BinaryReader reader = new BinaryReader(mem); + + switch (type) { - using (BinaryReader reader = new BinaryReader(mem)) - { - switch (type) + case MessageType.Protocol: + break; + case MessageType.Info: + ControllerInfoResponse contollerInfo = reader.ReadStruct(); + break; + case MessageType.Data: + ControllerDataResponse inputData = reader.ReadStruct(); + + Vector3 accelerometer = new Vector3() { - case MessageType.Protocol: - break; - case MessageType.Info: - ControllerInfoResponse contollerInfo = reader.ReadStruct(); - break; - case MessageType.Data: - ControllerDataResponse inputData = reader.ReadStruct(); + X = -inputData.AccelerometerX, + Y = inputData.AccelerometerZ, + Z = -inputData.AccelerometerY + }; - Vector3 accelerometer = new Vector3() + Vector3 gyroscrope = new Vector3() + { + X = inputData.GyroscopePitch, + Y = inputData.GyroscopeRoll, + Z = -inputData.GyroscopeYaw + }; + + ulong timestamp = inputData.MotionTimestamp; + + InputConfig config = ConfigurationState.Instance.Hid.InputConfig.Value.Find(x => x.PlayerIndex == (PlayerIndex)clientId); + + lock (_motionData) + { + int slot = inputData.Shared.Slot; + + if (_motionData.ContainsKey(clientId)) + { + if (_motionData[clientId].ContainsKey(slot)) { - X = -inputData.AccelerometerX, - Y = inputData.AccelerometerZ, - Z = -inputData.AccelerometerY - }; + var previousData = _motionData[clientId][slot]; - Vector3 gyroscrope = new Vector3() - { - X = inputData.GyroscopePitch, - Y = inputData.GyroscopeRoll, - Z = -inputData.GyroscopeYaw - }; - - ulong timestamp = inputData.MotionTimestamp; - - InputConfig config = ConfigurationState.Instance.Hid.InputConfig.Value.Find(x => x.PlayerIndex == (PlayerIndex)clientId); - - lock (_motionData) - { - int slot = inputData.Shared.Slot; - - if (_motionData.ContainsKey(clientId)) - { - if (_motionData[clientId].ContainsKey(slot)) - { - var previousData = _motionData[clientId][slot]; - - previousData.Update(accelerometer, gyroscrope, timestamp, config.Sensitivity, (float)config.GyroDeadzone); - } - else - { - MotionInput input = new MotionInput(); - input.Update(accelerometer, gyroscrope, timestamp, config.Sensitivity, (float)config.GyroDeadzone); - _motionData[clientId].Add(slot, input); - } - } - else - { - MotionInput input = new MotionInput(); - input.Update(accelerometer, gyroscrope, timestamp, config.Sensitivity, (float)config.GyroDeadzone); - _motionData.Add(clientId, new Dictionary() { { slot, input } }); - } + previousData.Update(accelerometer, gyroscrope, timestamp, config.Sensitivity, (float)config.GyroDeadzone); } - break; + else + { + MotionInput input = new MotionInput(); + input.Update(accelerometer, gyroscrope, timestamp, config.Sensitivity, (float)config.GyroDeadzone); + _motionData[clientId].Add(slot, input); + } + } + else + { + MotionInput input = new MotionInput(); + input.Update(accelerometer, gyroscrope, timestamp, config.Sensitivity, (float)config.GyroDeadzone); + _motionData.Add(clientId, new Dictionary() { { slot, input } }); + } } - } + break; } } diff --git a/Ryujinx/Motion/MotionDevice.cs b/Ryujinx/Motion/MotionDevice.cs index 82d84eb017..2e2050bcff 100644 --- a/Ryujinx/Motion/MotionDevice.cs +++ b/Ryujinx/Motion/MotionDevice.cs @@ -38,13 +38,11 @@ namespace Ryujinx.Motion } } - public void Poll(PlayerIndex player, int slot) + public void Poll(InputConfig config, int slot) { - InputConfig config = ConfigurationState.Instance.Hid.InputConfig.Value.Find(x => x.PlayerIndex == player); - Orientation = new float[9]; - if (!config.EnableMotion || !_motionSource.TryGetData((int)player, slot, out MotionInput input)) + if (!config.EnableMotion || !_motionSource.TryGetData((int)config.PlayerIndex, slot, out MotionInput input)) { Accelerometer = new Vector3(); Gyroscope = new Vector3(); diff --git a/Ryujinx/Ui/ControllerWindow.cs b/Ryujinx/Ui/ControllerWindow.cs index 406128746a..77c8db0cff 100644 --- a/Ryujinx/Ui/ControllerWindow.cs +++ b/Ryujinx/Ui/ControllerWindow.cs @@ -447,6 +447,8 @@ namespace Ryujinx.Ui Enum.TryParse(_rSl.Label, out Key rButtonSl); Enum.TryParse(_rSr.Label, out Key rButtonSr); + int.TryParse(_dsuServerPort.Buffer.Text, out int port); + return new KeyboardConfig { Index = int.Parse(_inputDevice.ActiveId.Split("/")[1]), @@ -489,11 +491,11 @@ namespace Ryujinx.Ui EnableMotion = _enableMotion.Active, MirrorInput = _mirrorInput.Active, Slot = (int)_slotNumber.Value, - AltSlot = (int)_slotNumber.Value, + AltSlot = (int)_altSlotNumber.Value, Sensitivity = (int)_sensitivity.Value, GyroDeadzone = _gyroDeadzone.Value, DsuServerHost = _dsuServerHost.Buffer.Text, - DsuServerPort = int.Parse(_dsuServerPort.Buffer.Text) + DsuServerPort = port }; } @@ -525,6 +527,8 @@ namespace Ryujinx.Ui Enum.TryParse(_rSl.Label, out ControllerInputId rButtonSl); Enum.TryParse(_rSr.Label, out ControllerInputId rButtonSr); + int.TryParse(_dsuServerPort.Buffer.Text, out int port); + return new ControllerConfig { Index = int.Parse(_inputDevice.ActiveId.Split("/")[1]), @@ -570,11 +574,11 @@ namespace Ryujinx.Ui EnableMotion = _enableMotion.Active, MirrorInput = _mirrorInput.Active, Slot = (int)_slotNumber.Value, - AltSlot = (int)_slotNumber.Value, + AltSlot = (int)_altSlotNumber.Value, Sensitivity = (int)_sensitivity.Value, GyroDeadzone = _gyroDeadzone.Value, DsuServerHost = _dsuServerHost.Buffer.Text, - DsuServerPort = int.Parse(_dsuServerPort.Buffer.Text) + DsuServerPort = port }; } diff --git a/Ryujinx/Ui/ControllerWindow.glade b/Ryujinx/Ui/ControllerWindow.glade index d148cfaef8..2143e9de8e 100644 --- a/Ryujinx/Ui/ControllerWindow.glade +++ b/Ryujinx/Ui/ControllerWindow.glade @@ -1,5 +1,5 @@ - + @@ -9,28 +9,28 @@ 1 - 0.05 + 0.050000000000000003 0.01 - 0.1 + 0.10000000000000001 1 - 0.05 + 0.050000000000000003 0.01 - 0.1 + 0.10000000000000001 1 0.5 0.01 - 0.1 + 0.10000000000000001 100 0.01 0.01 - 0.1 - 0.1 + 0.10000000000000001 + 0.10000000000000001 1000 @@ -50,6 +50,9 @@ center 1100 600 + + + True @@ -1803,6 +1806,7 @@ True True 0 + _altSlotNumber 1 True True @@ -2030,8 +2034,5 @@ - - - diff --git a/Ryujinx/Ui/GLRenderer.cs b/Ryujinx/Ui/GLRenderer.cs index 9cf23695d3..b635ad1c54 100644 --- a/Ryujinx/Ui/GLRenderer.cs +++ b/Ryujinx/Ui/GLRenderer.cs @@ -512,7 +512,7 @@ namespace Ryujinx.Ui currentButton |= _device.Hid.UpdateStickButtons(leftJoystick, rightJoystick); - motionDevice.Poll(inputConfig.PlayerIndex, inputConfig.Slot); + motionDevice.Poll(inputConfig, inputConfig.Slot); SixAxisInput sixAxisInput = new SixAxisInput() { @@ -537,7 +537,7 @@ namespace Ryujinx.Ui { if (!inputConfig.MirrorInput) { - motionDevice.Poll(inputConfig.PlayerIndex, inputConfig.AltSlot); + motionDevice.Poll(inputConfig, inputConfig.AltSlot); sixAxisInput = new SixAxisInput() { diff --git a/Ryujinx/Updater/UpdateDialog.cs b/Ryujinx/Updater/UpdateDialog.cs index 5420baf23b..768479d99c 100644 --- a/Ryujinx/Updater/UpdateDialog.cs +++ b/Ryujinx/Updater/UpdateDialog.cs @@ -45,6 +45,7 @@ namespace Ryujinx.Ui { string ryuName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "Ryujinx.exe" : "Ryujinx"; string ryuExe = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ryuName); + string ryuArg = String.Join(" ", Environment.GetCommandLineArgs()); if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { @@ -52,7 +53,7 @@ namespace Ryujinx.Ui unixFileInfo.FileAccessPermissions |= FileAccessPermissions.UserExecute; } - Process.Start(ryuExe); + Process.Start(ryuExe, ryuArg); Environment.Exit(0); }