Ryujinx/Ryujinx.HLE/HOS/Applets/Controller/ControllerApplet.cs
jhorv 5131b71437
Reducing memory allocations (#4537)
* add RecyclableMemoryStream dependency and MemoryStreamManager

* organize BinaryReader/BinaryWriter extensions

* add StreamExtensions to reduce need for BinaryWriter

* simple replacments of MemoryStream with RecyclableMemoryStream

* add write ReadOnlySequence<byte> support to IVirtualMemoryManager

* avoid 0-length array creation

* rework IpcMessage and related types to greatly reduce memory allocation by using RecylableMemoryStream, keeping streams around longer, avoiding their creation when possible, and avoiding creation of BinaryReader and BinaryWriter when possible

* reduce LINQ-induced memory allocations with custom methods to query KPriorityQueue

* use RecyclableMemoryStream in StreamUtils, and use StreamUtils in EmbeddedResources

* add constants for nanosecond/millisecond conversions

* code formatting

* XML doc adjustments

* fix: StreamExtension.WriteByte not writing non-zero values for lengths <= 16

* XML Doc improvements. Implement StreamExtensions.WriteByte() block writes for large-enough count values.

* add copyless path for StreamExtension.Write(ReadOnlySpan<int>)

* add default implementation of IVirtualMemoryManager.Write(ulong, ReadOnlySequence<byte>); remove previous explicit implementations

* code style fixes

* remove LINQ completely from KScheduler/KPriorityQueue by implementing a custom struct-based enumerator
2023-03-17 13:14:50 +01:00

147 lines
5.7 KiB
C#

using Ryujinx.Common.Logging;
using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
using Ryujinx.HLE.HOS.Services.Hid;
using Ryujinx.HLE.HOS.Services.Hid.Types;
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using static Ryujinx.HLE.HOS.Services.Hid.HidServer.HidUtils;
namespace Ryujinx.HLE.HOS.Applets
{
internal class ControllerApplet : IApplet
{
private Horizon _system;
private AppletSession _normalSession;
public event EventHandler AppletStateChanged;
public ControllerApplet(Horizon system)
{
_system = system;
}
public ResultCode Start(AppletSession normalSession, AppletSession interactiveSession)
{
_normalSession = normalSession;
byte[] launchParams = _normalSession.Pop();
byte[] controllerSupportArgPrivate = _normalSession.Pop();
ControllerSupportArgPrivate privateArg = IApplet.ReadStruct<ControllerSupportArgPrivate>(controllerSupportArgPrivate);
Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerApplet ArgPriv {privateArg.PrivateSize} {privateArg.ArgSize} {privateArg.Mode} " +
$"HoldType:{(NpadJoyHoldType)privateArg.NpadJoyHoldType} StyleSets:{(ControllerType)privateArg.NpadStyleSet}");
if (privateArg.Mode != ControllerSupportMode.ShowControllerSupport)
{
_normalSession.Push(BuildResponse()); // Dummy response for other modes
AppletStateChanged?.Invoke(this, null);
return ResultCode.Success;
}
byte[] controllerSupportArg = _normalSession.Pop();
ControllerSupportArgHeader argHeader;
if (privateArg.ArgSize == Marshal.SizeOf<ControllerSupportArgV7>())
{
ControllerSupportArgV7 arg = IApplet.ReadStruct<ControllerSupportArgV7>(controllerSupportArg);
argHeader = arg.Header;
Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerSupportArg Version 7 EnableExplainText={arg.EnableExplainText != 0}");
// Read enable text here?
}
else if (privateArg.ArgSize == Marshal.SizeOf<ControllerSupportArgVPre7>())
{
ControllerSupportArgVPre7 arg = IApplet.ReadStruct<ControllerSupportArgVPre7>(controllerSupportArg);
argHeader = arg.Header;
Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerSupportArg Version Pre-7 EnableExplainText={arg.EnableExplainText != 0}");
// Read enable text here?
}
else
{
Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerSupportArg Version Unknown");
argHeader = IApplet.ReadStruct<ControllerSupportArgHeader>(controllerSupportArg); // Read just the header
}
int playerMin = argHeader.PlayerCountMin;
int playerMax = argHeader.PlayerCountMax;
bool singleMode = argHeader.EnableSingleMode != 0;
Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerApplet Arg {playerMin} {playerMax} {argHeader.EnableTakeOverConnection} {argHeader.EnableSingleMode}");
if (singleMode)
{
// Applications can set an arbitrary player range even with SingleMode, so clamp it
playerMin = playerMax = 1;
}
int configuredCount = 0;
PlayerIndex primaryIndex = PlayerIndex.Unknown;
while (!_system.Device.Hid.Npads.Validate(playerMin, playerMax, (ControllerType)privateArg.NpadStyleSet, out configuredCount, out primaryIndex))
{
ControllerAppletUiArgs uiArgs = new ControllerAppletUiArgs
{
PlayerCountMin = playerMin,
PlayerCountMax = playerMax,
SupportedStyles = (ControllerType)privateArg.NpadStyleSet,
SupportedPlayers = _system.Device.Hid.Npads.GetSupportedPlayers(),
IsDocked = _system.State.DockedMode
};
if (!_system.Device.UiHandler.DisplayMessageDialog(uiArgs))
{
break;
}
}
ControllerSupportResultInfo result = new ControllerSupportResultInfo
{
PlayerCount = (sbyte)configuredCount,
SelectedId = (uint)GetNpadIdTypeFromIndex(primaryIndex)
};
Logger.Stub?.PrintStub(LogClass.ServiceHid, $"ControllerApplet ReturnResult {result.PlayerCount} {result.SelectedId}");
_normalSession.Push(BuildResponse(result));
AppletStateChanged?.Invoke(this, null);
_system.ReturnFocus();
return ResultCode.Success;
}
public ResultCode GetResult()
{
return ResultCode.Success;
}
private byte[] BuildResponse(ControllerSupportResultInfo result)
{
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.Write(MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref result, Unsafe.SizeOf<ControllerSupportResultInfo>())));
return stream.ToArray();
}
}
private byte[] BuildResponse()
{
using (MemoryStream stream = MemoryStreamManager.Shared.GetStream())
using (BinaryWriter writer = new BinaryWriter(stream))
{
writer.Write((ulong)ResultCode.Success);
return stream.ToArray();
}
}
}
}