From e77c1560e67f3787269ae71c90baac42cdf0c422 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Fri, 21 Sep 2018 02:08:35 +0200 Subject: [PATCH 01/30] Full 5.X stubbed IHidServer (#428) * Full 5.X stubbed IHidServer Since we can't support all those Hid calls in the right way, we can stub them with more information as possible in the logs. I have added all symbols in it to be more revelant as possible. It's remove some Hid spam in few games too, because we assign some var as the game want. * Fix issues * Fix code according to review --- Ryujinx.HLE/HOS/Services/Hid/HidNpad.cs | 41 + Ryujinx.HLE/HOS/Services/Hid/HidSixAxis.cs | 21 + Ryujinx.HLE/HOS/Services/Hid/HidVibration.cs | 29 + Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs | 1432 ++++++++++++++++-- 4 files changed, 1422 insertions(+), 101 deletions(-) create mode 100644 Ryujinx.HLE/HOS/Services/Hid/HidNpad.cs create mode 100644 Ryujinx.HLE/HOS/Services/Hid/HidSixAxis.cs create mode 100644 Ryujinx.HLE/HOS/Services/Hid/HidVibration.cs diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidNpad.cs b/Ryujinx.HLE/HOS/Services/Hid/HidNpad.cs new file mode 100644 index 0000000000..d273185733 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/HidNpad.cs @@ -0,0 +1,41 @@ +using System; + +namespace Ryujinx.HLE.HOS.Services.Hid +{ + public enum HidNpadJoyAssignmentMode + { + Dual, + Single, + } + + public enum HidNpadHandheldActivationMode + { + Dual, + Single, + None, + } + + public enum HidNpadJoyDeviceType + { + Left, + Right, + } + + public enum HidNpadJoyHoldType + { + Vertical, + Horizontal, + } + + [Flags] + public enum HidNpadStyle + { + None, + FullKey = 1 << 0, + Handheld = 1 << 1, + Dual = 1 << 2, + Left = 1 << 3, + Right = 1 << 4, + Invalid = 1 << 5, + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidSixAxis.cs b/Ryujinx.HLE/HOS/Services/Hid/HidSixAxis.cs new file mode 100644 index 0000000000..bd0647621e --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/HidSixAxis.cs @@ -0,0 +1,21 @@ +namespace Ryujinx.HLE.HOS.Services.Hid +{ + public struct HidSensorFusionParameters + { + public float RevisePower; + public float ReviseRange; + } + + public struct HidAccelerometerParameters + { + public float X; + public float Y; + } + + public enum HidGyroscopeZeroDriftMode + { + Loose, + Standard, + Tight + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/HidVibration.cs b/Ryujinx.HLE/HOS/Services/Hid/HidVibration.cs new file mode 100644 index 0000000000..cb2427e732 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Hid/HidVibration.cs @@ -0,0 +1,29 @@ +namespace Ryujinx.HLE.HOS.Services.Hid +{ + public enum HidVibrationDeviceType + { + None, + LinearResonantActuator + } + + public enum HidVibrationDevicePosition + { + None, + Left, + Right, + } + + public struct HidVibrationDeviceValue + { + public HidVibrationDeviceType DeviceType; + public HidVibrationDevicePosition Position; + } + + public struct HidVibrationValue + { + public float AmplitudeLow; + public float FrequencyLow; + public float AmplitudeHigh; + public float FrequencyHigh; + } +} diff --git a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs index 2fd07ec768..0b511072d0 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs @@ -2,6 +2,8 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.Input; using Ryujinx.HLE.Logging; +using Ryujinx.HLE.Utilities; +using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Hid @@ -11,6 +13,30 @@ namespace Ryujinx.HLE.HOS.Services.Hid private Dictionary m_Commands; private KEvent NpadStyleSetUpdateEvent; + private KEvent XpadIdEvent; + private KEvent PalmaOperationCompleteEvent; + + private int XpadIdEventHandle; + + private bool SixAxisSensorFusionEnabled; + private bool UnintendedHomeButtonInputProtectionEnabled; + private bool VibrationPermitted; + private bool UsbFullKeyControllerEnabled; + + private HidNpadJoyHoldType NpadJoyHoldType; + private HidNpadStyle NpadStyleTag; + private HidNpadJoyAssignmentMode NpadJoyAssignmentMode; + private HidNpadHandheldActivationMode NpadHandheldActivationMode; + private HidGyroscopeZeroDriftMode GyroscopeZeroDriftMode; + + private long NpadCommunicationMode; + private uint AccelerometerPlayMode; + private long VibrationGcErmCommand; + private float SevenSixAxisSensorFusionStrength; + + private HidSensorFusionParameters SensorFusionParams; + private HidAccelerometerParameters AccelerometerParams; + private HidVibrationValue VibrationValue; public override IReadOnlyDictionary Commands => m_Commands; @@ -18,256 +44,986 @@ namespace Ryujinx.HLE.HOS.Services.Hid { m_Commands = new Dictionary() { - { 0, CreateAppletResource }, - { 1, ActivateDebugPad }, - { 11, ActivateTouchScreen }, - { 21, ActivateMouse }, - { 31, ActivateKeyboard }, - { 66, StartSixAxisSensor }, - { 79, SetGyroscopeZeroDriftMode }, - { 100, SetSupportedNpadStyleSet }, - { 101, GetSupportedNpadStyleSet }, - { 102, SetSupportedNpadIdType }, - { 103, ActivateNpad }, - { 106, AcquireNpadStyleSetUpdateEventHandle }, - { 108, GetPlayerLedPattern }, - { 120, SetNpadJoyHoldType }, - { 121, GetNpadJoyHoldType }, - { 122, SetNpadJoyAssignmentModeSingleByDefault }, - { 123, SetNpadJoyAssignmentModeSingle }, - { 124, SetNpadJoyAssignmentModeDual }, - { 125, MergeSingleJoyAsDualJoy }, - { 128, SetNpadHandheldActivationMode }, - { 200, GetVibrationDeviceInfo }, - { 201, SendVibrationValue }, - { 203, CreateActiveVibrationDeviceList }, - { 206, SendVibrationValues } + { 0, CreateAppletResource }, + { 1, ActivateDebugPad }, + { 11, ActivateTouchScreen }, + { 21, ActivateMouse }, + { 31, ActivateKeyboard }, + { 40, AcquireXpadIdEventHandle }, + { 41, ReleaseXpadIdEventHandle }, + { 51, ActivateXpad }, + { 55, GetXpadIds }, + { 56, ActivateJoyXpad }, + { 58, GetJoyXpadLifoHandle }, + { 59, GetJoyXpadIds }, + { 60, ActivateSixAxisSensor }, + { 61, DeactivateSixAxisSensor }, + { 62, GetSixAxisSensorLifoHandle }, + { 63, ActivateJoySixAxisSensor }, + { 64, DeactivateJoySixAxisSensor }, + { 65, GetJoySixAxisSensorLifoHandle }, + { 66, StartSixAxisSensor }, + { 67, StopSixAxisSensor }, + { 68, IsSixAxisSensorFusionEnabled }, + { 69, EnableSixAxisSensorFusion }, + { 70, SetSixAxisSensorFusionParameters }, + { 71, GetSixAxisSensorFusionParameters }, + { 72, ResetSixAxisSensorFusionParameters }, + { 73, SetAccelerometerParameters }, + { 74, GetAccelerometerParameters }, + { 75, ResetAccelerometerParameters }, + { 76, SetAccelerometerPlayMode }, + { 77, GetAccelerometerPlayMode }, + { 78, ResetAccelerometerPlayMode }, + { 79, SetGyroscopeZeroDriftMode }, + { 80, GetGyroscopeZeroDriftMode }, + { 81, ResetGyroscopeZeroDriftMode }, + { 82, IsSixAxisSensorAtRest }, + { 91, ActivateGesture }, + { 100, SetSupportedNpadStyleSet }, + { 101, GetSupportedNpadStyleSet }, + { 102, SetSupportedNpadIdType }, + { 103, ActivateNpad }, + { 104, DeactivateNpad }, + { 106, AcquireNpadStyleSetUpdateEventHandle }, + { 107, DisconnectNpad }, + { 108, GetPlayerLedPattern }, + { 109, ActivateNpadWithRevision }, + { 120, SetNpadJoyHoldType }, + { 121, GetNpadJoyHoldType }, + { 122, SetNpadJoyAssignmentModeSingleByDefault }, + { 123, SetNpadJoyAssignmentModeSingle }, + { 124, SetNpadJoyAssignmentModeDual }, + { 125, MergeSingleJoyAsDualJoy }, + { 126, StartLrAssignmentMode }, + { 127, StopLrAssignmentMode }, + { 128, SetNpadHandheldActivationMode }, + { 129, GetNpadHandheldActivationMode }, + { 130, SwapNpadAssignment }, + { 131, IsUnintendedHomeButtonInputProtectionEnabled }, + { 132, EnableUnintendedHomeButtonInputProtection }, + { 133, SetNpadJoyAssignmentModeSingleWithDestination }, + { 200, GetVibrationDeviceInfo }, + { 201, SendVibrationValue }, + { 202, GetActualVibrationValue }, + { 203, CreateActiveVibrationDeviceList }, + { 204, PermitVibration }, + { 205, IsVibrationPermitted }, + { 206, SendVibrationValues }, + { 207, SendVibrationGcErmCommand }, + { 208, GetActualVibrationGcErmCommand }, + { 209, BeginPermitVibrationSession }, + { 210, EndPermitVibrationSession }, + { 300, ActivateConsoleSixAxisSensor }, + { 301, StartConsoleSixAxisSensor }, + { 302, StopConsoleSixAxisSensor }, + { 303, ActivateSevenSixAxisSensor }, + { 304, StartSevenSixAxisSensor }, + { 305, StopSevenSixAxisSensor }, + { 306, InitializeSevenSixAxisSensor }, + { 307, FinalizeSevenSixAxisSensor }, + { 308, SetSevenSixAxisSensorFusionStrength }, + { 309, GetSevenSixAxisSensorFusionStrength }, + { 400, IsUsbFullKeyControllerEnabled }, + { 401, EnableUsbFullKeyController }, + { 402, IsUsbFullKeyControllerConnected }, + { 403, HasBattery }, + { 404, HasLeftRightBattery }, + { 405, GetNpadInterfaceType }, + { 406, GetNpadLeftRightInterfaceType }, + { 500, GetPalmaConnectionHandle }, + { 501, InitializePalma }, + { 502, AcquirePalmaOperationCompleteEvent }, + { 503, GetPalmaOperationInfo }, + { 504, PlayPalmaActivity }, + { 505, SetPalmaFrModeType }, + { 506, ReadPalmaStep }, + { 507, EnablePalmaStep }, + { 508, SuspendPalmaStep }, + { 509, ResetPalmaStep }, + { 510, ReadPalmaApplicationSection }, + { 511, WritePalmaApplicationSection }, + { 512, ReadPalmaUniqueCode }, + { 513, SetPalmaUniqueCodeInvalid }, + { 1000, SetNpadCommunicationMode }, + { 1001, GetNpadCommunicationMode }, }; - NpadStyleSetUpdateEvent = new KEvent(System); + NpadStyleSetUpdateEvent = new KEvent(System); + XpadIdEvent = new KEvent(System); + PalmaOperationCompleteEvent = new KEvent(System); + + NpadJoyHoldType = HidNpadJoyHoldType.Vertical; + NpadStyleTag = HidNpadStyle.FullKey | HidNpadStyle.Dual | HidNpadStyle.Left | HidNpadStyle.Right; + NpadJoyAssignmentMode = HidNpadJoyAssignmentMode.Dual; + NpadHandheldActivationMode = HidNpadHandheldActivationMode.Dual; + GyroscopeZeroDriftMode = HidGyroscopeZeroDriftMode.Standard; + + SensorFusionParams = new HidSensorFusionParameters(); + AccelerometerParams = new HidAccelerometerParameters(); + VibrationValue = new HidVibrationValue(); } + // CreateAppletResource(nn::applet::AppletResourceUserId) -> object public long CreateAppletResource(ServiceCtx Context) { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + MakeObject(Context, new IAppletResource(Context.Device.System.HidSharedMem)); return 0; } + // ActivateDebugPad(nn::applet::AppletResourceUserId) public long ActivateDebugPad(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } + // ActivateTouchScreen(nn::applet::AppletResourceUserId) public long ActivateTouchScreen(ServiceCtx Context) { long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } + // ActivateMouse(nn::applet::AppletResourceUserId) public long ActivateMouse(ServiceCtx Context) { long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } + // ActivateKeyboard(nn::applet::AppletResourceUserId) public long ActivateKeyboard(ServiceCtx Context) { long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } + // AcquireXpadIdEventHandle(ulong XpadId) -> nn::sf::NativeHandle + public long AcquireXpadIdEventHandle(ServiceCtx Context) + { + long XpadId = Context.RequestData.ReadInt64(); + + XpadIdEventHandle = Context.Process.HandleTable.OpenHandle(XpadIdEvent); + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(XpadIdEventHandle); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. XpadId: {XpadId}"); + + return 0; + } + + // ReleaseXpadIdEventHandle(ulong XpadId) + public long ReleaseXpadIdEventHandle(ServiceCtx Context) + { + long XpadId = Context.RequestData.ReadInt64(); + + Context.Process.HandleTable.CloseHandle(XpadIdEventHandle); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. XpadId: {XpadId}"); + + return 0; + } + + // ActivateXpad(nn::hid::BasicXpadId, nn::applet::AppletResourceUserId) + public long ActivateXpad(ServiceCtx Context) + { + int BasicXpadId = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"BasicXpadId: {BasicXpadId}"); + + return 0; + } + + // GetXpadIds() -> long IdsCount, buffer, type: 0xa> + public long GetXpadIds(ServiceCtx Context) + { + // There is any Xpad, so we return 0 and write nothing inside the type-0xa buffer. + Context.ResponseData.Write(0L); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed."); + + return 0; + } + + // ActivateJoyXpad(nn::hid::JoyXpadId) + public long ActivateJoyXpad(ServiceCtx Context) + { + int JoyXpadId = Context.RequestData.ReadInt32(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. JoyXpadId: {JoyXpadId}"); + + return 0; + } + + // GetJoyXpadLifoHandle(nn::hid::JoyXpadId) -> nn::sf::NativeHandle + public long GetJoyXpadLifoHandle(ServiceCtx Context) + { + int JoyXpadId = Context.RequestData.ReadInt32(); + + int Handle = 0; + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. JoyXpadId: {JoyXpadId}"); + + return 0; + } + + // GetJoyXpadIds() -> long IdsCount, buffer, type: 0xa> + public long GetJoyXpadIds(ServiceCtx Context) + { + // There is any JoyXpad, so we return 0 and write nothing inside the type-0xa buffer. + Context.ResponseData.Write(0L); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed."); + + return 0; + } + + // ActivateSixAxisSensor(nn::hid::BasicXpadId) + public long ActivateSixAxisSensor(ServiceCtx Context) + { + int BasicXpadId = Context.RequestData.ReadInt32(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. BasicXpadId: {BasicXpadId}"); + + return 0; + } + + // DeactivateSixAxisSensor(nn::hid::BasicXpadId) + public long DeactivateSixAxisSensor(ServiceCtx Context) + { + int BasicXpadId = Context.RequestData.ReadInt32(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. BasicXpadId: {BasicXpadId}"); + + return 0; + } + + // GetSixAxisSensorLifoHandle(nn::hid::BasicXpadId) -> nn::sf::NativeHandle + public long GetSixAxisSensorLifoHandle(ServiceCtx Context) + { + int BasicXpadId = Context.RequestData.ReadInt32(); + + int Handle = 0; + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. BasicXpadId: {BasicXpadId}"); + + return 0; + } + + // ActivateJoySixAxisSensor(nn::hid::JoyXpadId) + public long ActivateJoySixAxisSensor(ServiceCtx Context) + { + int JoyXpadId = Context.RequestData.ReadInt32(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. JoyXpadId: {JoyXpadId}"); + + return 0; + } + + // DeactivateJoySixAxisSensor(nn::hid::JoyXpadId) + public long DeactivateJoySixAxisSensor(ServiceCtx Context) + { + int JoyXpadId = Context.RequestData.ReadInt32(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. JoyXpadId: {JoyXpadId}"); + + return 0; + } + + // GetJoySixAxisSensorLifoHandle(nn::hid::JoyXpadId) -> nn::sf::NativeHandle + public long GetJoySixAxisSensorLifoHandle(ServiceCtx Context) + { + int JoyXpadId = Context.RequestData.ReadInt32(); + + int Handle = 0; + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. JoyXpadId: {JoyXpadId}"); + + return 0; + } + + // StartSixAxisSensor(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) public long StartSixAxisSensor(ServiceCtx Context) { - int Handle = Context.RequestData.ReadInt32(); - + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle}"); return 0; } + // StopSixAxisSensor(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) + public long StopSixAxisSensor(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle}"); + + return 0; + } + + // IsSixAxisSensorFusionEnabled(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> bool IsEnabled + public long IsSixAxisSensorFusionEnabled(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write(SixAxisSensorFusionEnabled); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"SixAxisSensorFusionEnabled: {SixAxisSensorFusionEnabled}"); + + return 0; + } + + // EnableSixAxisSensorFusion(bool Enabled, nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) + public long EnableSixAxisSensorFusion(ServiceCtx Context) + { + SixAxisSensorFusionEnabled = Context.RequestData.ReadBoolean(); + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"SixAxisSensorFusionEnabled: {SixAxisSensorFusionEnabled}"); + + return 0; + } + + // SetSixAxisSensorFusionParameters(nn::hid::SixAxisSensorHandle, float RevisePower, float ReviseRange, nn::applet::AppletResourceUserId) + public long SetSixAxisSensorFusionParameters(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + + SensorFusionParams = new HidSensorFusionParameters() + { + RevisePower = Context.RequestData.ReadInt32(), + ReviseRange = Context.RequestData.ReadInt32(), + }; + + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"RevisePower: {SensorFusionParams.RevisePower} - " + + $"ReviseRange: {SensorFusionParams.ReviseRange}"); + + return 0; + } + + // GetSixAxisSensorFusionParameters(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> float RevisePower, float ReviseRange) + public long GetSixAxisSensorFusionParameters(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write(SensorFusionParams.RevisePower); + Context.ResponseData.Write(SensorFusionParams.ReviseRange); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"RevisePower: {SensorFusionParams.RevisePower} - " + + $"ReviseRange: {SensorFusionParams.ReviseRange}"); + + return 0; + } + + // ResetSixAxisSensorFusionParameters(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) + public long ResetSixAxisSensorFusionParameters(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + SensorFusionParams.RevisePower = 0; + SensorFusionParams.ReviseRange = 0; + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"RevisePower: {SensorFusionParams.RevisePower} - " + + $"ReviseRange: {SensorFusionParams.ReviseRange}"); + + return 0; + } + + // SetAccelerometerParameters(nn::hid::SixAxisSensorHandle, float X, float Y, nn::applet::AppletResourceUserId) + public long SetAccelerometerParameters(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + + AccelerometerParams = new HidAccelerometerParameters() + { + X = Context.RequestData.ReadInt32(), + Y = Context.RequestData.ReadInt32(), + }; + + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"X: {AccelerometerParams.X} - " + + $"Y: {AccelerometerParams.Y}"); + + return 0; + } + + // GetAccelerometerParameters(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> float X, float Y + public long GetAccelerometerParameters(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write(AccelerometerParams.X); + Context.ResponseData.Write(AccelerometerParams.Y); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"X: {AccelerometerParams.X} - " + + $"Y: {AccelerometerParams.Y}"); + + return 0; + } + + // ResetAccelerometerParameters(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) + public long ResetAccelerometerParameters(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + AccelerometerParams.X = 0; + AccelerometerParams.Y = 0; + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"X: {AccelerometerParams.X} - " + + $"Y: {AccelerometerParams.Y}"); + + return 0; + } + + // SetAccelerometerPlayMode(nn::hid::SixAxisSensorHandle, uint PlayMode, nn::applet::AppletResourceUserId) + public long SetAccelerometerPlayMode(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + AccelerometerPlayMode = Context.RequestData.ReadUInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"PlayMode: {AccelerometerPlayMode}"); + + return 0; + } + + // GetAccelerometerPlayMode(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> uint PlayMode + public long GetAccelerometerPlayMode(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write(AccelerometerPlayMode); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"PlayMode: {AccelerometerPlayMode}"); + + return 0; + } + + // ResetAccelerometerPlayMode(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) + public long ResetAccelerometerPlayMode(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + AccelerometerPlayMode = 0; + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"PlayMode: {AccelerometerPlayMode}"); + + return 0; + } + + // SetGyroscopeZeroDriftMode(nn::hid::SixAxisSensorHandle, uint GyroscopeZeroDriftMode, nn::applet::AppletResourceUserId) public long SetGyroscopeZeroDriftMode(ServiceCtx Context) { - int Handle = Context.RequestData.ReadInt32(); - int Unknown = Context.RequestData.ReadInt32(); - long AppletResourceUserId = Context.RequestData.ReadInt64(); + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + GyroscopeZeroDriftMode = (HidGyroscopeZeroDriftMode)Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"GyroscopeZeroDriftMode: {GyroscopeZeroDriftMode}"); return 0; } + // GetGyroscopeZeroDriftMode(nn::applet::AppletResourceUserId, nn::hid::SixAxisSensorHandle) -> int GyroscopeZeroDriftMode + public long GetGyroscopeZeroDriftMode(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write((int)GyroscopeZeroDriftMode); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"GyroscopeZeroDriftMode: {GyroscopeZeroDriftMode}"); + + return 0; + } + + // ResetGyroscopeZeroDriftMode(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) + public long ResetGyroscopeZeroDriftMode(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + GyroscopeZeroDriftMode = HidGyroscopeZeroDriftMode.Standard; + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"GyroscopeZeroDriftMode: {GyroscopeZeroDriftMode}"); + + return 0; + } + + // IsSixAxisSensorAtRest(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> bool IsAsRest + public long IsSixAxisSensorAtRest(ServiceCtx Context) + { + int SixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + bool IsAtRest = true; + + Context.ResponseData.Write(IsAtRest); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"IsAtRest: {IsAtRest}"); + + return 0; + } + + // ActivateGesture(nn::applet::AppletResourceUserId, int Unknown0) + public long ActivateGesture(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + int Unknown0 = Context.RequestData.ReadInt32(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"Unknown0: {Unknown0}"); + + return 0; + } + + + // SetSupportedNpadStyleSet(nn::applet::AppletResourceUserId, nn::hid::NpadStyleTag) + public long SetSupportedNpadStyleSet(ServiceCtx Context) + { + NpadStyleTag = (HidNpadStyle)Context.RequestData.ReadInt32(); + + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadStyleTag: {NpadStyleTag}"); + + return 0; + } + + // GetSupportedNpadStyleSet(nn::applet::AppletResourceUserId) -> uint nn::hid::NpadStyleTag + public long GetSupportedNpadStyleSet(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write((int)NpadStyleTag); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadStyleTag: {NpadStyleTag}"); + + return 0; + } + + // SetSupportedNpadIdType(nn::applet::AppletResourceUserId, array) + public long SetSupportedNpadIdType(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + HidControllerId NpadIdType = (HidControllerId)Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadIdType: {NpadIdType}"); + + return 0; + } + + // ActivateNpad(nn::applet::AppletResourceUserId) + public long ActivateNpad(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + + // DeactivateNpad(nn::applet::AppletResourceUserId) + public long DeactivateNpad(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + + // AcquireNpadStyleSetUpdateEventHandle(nn::applet::AppletResourceUserId, uint, ulong) -> nn::sf::NativeHandle public long AcquireNpadStyleSetUpdateEventHandle(ServiceCtx Context) { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + int NpadId = Context.RequestData.ReadInt32(); + long NpadStyleSet = Context.RequestData.ReadInt64(); + int Handle = Context.Process.HandleTable.OpenHandle(NpadStyleSetUpdateEvent); Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadId: {NpadId} - " + + $"NpadStyleSet: {NpadStyleSet}"); + return 0; } - public long GetSupportedNpadStyleSet(ServiceCtx Context) + // DisconnectNpad(nn::applet::AppletResourceUserId, uint NpadIdType) + public long DisconnectNpad(ServiceCtx Context) { - Context.ResponseData.Write(0); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + int NpadIdType = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); - - return 0; - } - - public long SetSupportedNpadStyleSet(ServiceCtx Context) - { - long Unknown0 = Context.RequestData.ReadInt64(); - long Unknown8 = Context.RequestData.ReadInt64(); - - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); - - return 0; - } - - public long SetSupportedNpadIdType(ServiceCtx Context) - { - long Unknown = Context.RequestData.ReadInt64(); - - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); - - return 0; - } - - public long ActivateNpad(ServiceCtx Context) - { - long Unknown = Context.RequestData.ReadInt64(); - - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadIdType: {NpadIdType}"); return 0; } + // GetPlayerLedPattern(uint NpadId) -> ulong LedPattern public long GetPlayerLedPattern(ServiceCtx Context) { - long Unknown = Context.RequestData.ReadInt32(); + int NpadId = Context.RequestData.ReadInt32(); - Context.ResponseData.Write(0L); + long LedPattern = 0; - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Context.ResponseData.Write(LedPattern); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. NpadId: {NpadId} - Pattern: {LedPattern}"); return 0; } + // ActivateNpadWithRevision(nn::applet::AppletResourceUserId, int Unknown) + public long ActivateNpadWithRevision(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + int Unknown = Context.RequestData.ReadInt32(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - Unknown: {Unknown}"); + + return 0; + } + + // SetNpadJoyHoldType(nn::applet::AppletResourceUserId, long NpadJoyHoldType) public long SetNpadJoyHoldType(ServiceCtx Context) { - long Unknown0 = Context.RequestData.ReadInt64(); - long Unknown8 = Context.RequestData.ReadInt64(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + NpadJoyHoldType = (HidNpadJoyHoldType)Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadJoyHoldType: {NpadJoyHoldType}"); return 0; } + // GetNpadJoyHoldType(nn::applet::AppletResourceUserId) -> long NpadJoyHoldType public long GetNpadJoyHoldType(ServiceCtx Context) { - Context.ResponseData.Write(0L); + long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Context.ResponseData.Write((long)NpadJoyHoldType); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadJoyHoldTypeValue: {NpadJoyHoldType}"); return 0; } + // SetNpadJoyAssignmentModeSingleByDefault(uint HidControllerId, nn::applet::AppletResourceUserId) public long SetNpadJoyAssignmentModeSingleByDefault(ServiceCtx Context) { - HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32(); + HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); - long AppletUserResourceId = Context.RequestData.ReadInt64(); + NpadJoyAssignmentMode = HidNpadJoyAssignmentMode.Single; - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"HidControllerId: {HidControllerId} - " + + $"NpadJoyAssignmentModeValue: {NpadJoyAssignmentMode}"); return 0; } + // SetNpadJoyAssignmentModeSingle(uint HidControllerId, nn::applet::AppletResourceUserId, long HidNpadJoyDeviceType) public long SetNpadJoyAssignmentModeSingle(ServiceCtx Context) { - HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32(); + HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + HidNpadJoyDeviceType HidNpadJoyDeviceType = (HidNpadJoyDeviceType)Context.RequestData.ReadInt64(); - long AppletUserResourceId = Context.RequestData.ReadInt64(); - long NpadJoyDeviceType = Context.RequestData.ReadInt64(); + NpadJoyAssignmentMode = HidNpadJoyAssignmentMode.Single; - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"HidControllerId: {HidControllerId} - " + + $"HidNpadJoyDeviceType: {HidNpadJoyDeviceType} - " + + $"NpadJoyAssignmentModeValue: {NpadJoyAssignmentMode}"); return 0; } + // SetNpadJoyAssignmentModeDual(uint HidControllerId, nn::applet::AppletResourceUserId) public long SetNpadJoyAssignmentModeDual(ServiceCtx Context) { - HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32(); + HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); - long AppletUserResourceId = Context.RequestData.ReadInt64(); + NpadJoyAssignmentMode = HidNpadJoyAssignmentMode.Dual; - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"HidControllerId: {HidControllerId} - " + + $"NpadJoyAssignmentModeValue: {NpadJoyAssignmentMode}"); return 0; } + // MergeSingleJoyAsDualJoy(uint SingleJoyId0, uint SingleJoyId1, nn::applet::AppletResourceUserId) public long MergeSingleJoyAsDualJoy(ServiceCtx Context) { - long Unknown0 = Context.RequestData.ReadInt32(); - long Unknown8 = Context.RequestData.ReadInt32(); - long AppletUserResourceId = Context.RequestData.ReadInt64(); + long SingleJoyId0 = Context.RequestData.ReadInt32(); + long SingleJoyId1 = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SingleJoyId0: {SingleJoyId0} - " + + $"SingleJoyId1: {SingleJoyId1}"); return 0; } + // StartLrAssignmentMode(nn::applet::AppletResourceUserId) + public long StartLrAssignmentMode(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + + // StopLrAssignmentMode(nn::applet::AppletResourceUserId) + public long StopLrAssignmentMode(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + + // SetNpadHandheldActivationMode(nn::applet::AppletResourceUserId, long HidNpadHandheldActivationMode) public long SetNpadHandheldActivationMode(ServiceCtx Context) { - long AppletUserResourceId = Context.RequestData.ReadInt64(); - long Unknown = Context.RequestData.ReadInt64(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + NpadHandheldActivationMode = (HidNpadHandheldActivationMode)Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadHandheldActivationMode: {NpadHandheldActivationMode}"); return 0; } + // GetNpadHandheldActivationMode(nn::applet::AppletResourceUserId) -> long HidNpadHandheldActivationMode + public long GetNpadHandheldActivationMode(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write((long)NpadHandheldActivationMode); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadHandheldActivationMode: {NpadHandheldActivationMode}"); + + return 0; + } + + // SwapNpadAssignment(uint OldNpadAssignment, uint NewNpadAssignment, nn::applet::AppletResourceUserId) + public long SwapNpadAssignment(ServiceCtx Context) + { + int OldNpadAssignment = Context.RequestData.ReadInt32(); + int NewNpadAssignment = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"OldNpadAssignment: {OldNpadAssignment} - " + + $"NewNpadAssignment: {NewNpadAssignment}"); + + return 0; + } + + // IsUnintendedHomeButtonInputProtectionEnabled(uint Unknown0, nn::applet::AppletResourceUserId) -> bool IsEnabled + public long IsUnintendedHomeButtonInputProtectionEnabled(ServiceCtx Context) + { + uint Unknown0 = Context.RequestData.ReadUInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write(UnintendedHomeButtonInputProtectionEnabled); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"Unknown0: {Unknown0} - " + + $"UnintendedHomeButtonInputProtectionEnabled: {UnintendedHomeButtonInputProtectionEnabled}"); + + return 0; + } + + // EnableUnintendedHomeButtonInputProtection(bool Enable, uint Unknown0, nn::applet::AppletResourceUserId) + public long EnableUnintendedHomeButtonInputProtection(ServiceCtx Context) + { + UnintendedHomeButtonInputProtectionEnabled = Context.RequestData.ReadBoolean(); + uint Unknown0 = Context.RequestData.ReadUInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"Unknown0: {Unknown0} - " + + $"UnintendedHomeButtonInputProtectionEnable: {UnintendedHomeButtonInputProtectionEnabled}"); + + return 0; + } + + // SetNpadJoyAssignmentModeSingleWithDestination(uint HidControllerId, long HidNpadJoyDeviceType, nn::applet::AppletResourceUserId) -> bool Unknown0, uint Unknown1 + public long SetNpadJoyAssignmentModeSingleWithDestination(ServiceCtx Context) + { + HidControllerId HidControllerId = (HidControllerId)Context.RequestData.ReadInt32(); + HidNpadJoyDeviceType HidNpadJoyDeviceType = (HidNpadJoyDeviceType)Context.RequestData.ReadInt64(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + NpadJoyAssignmentMode = HidNpadJoyAssignmentMode.Single; + + Context.ResponseData.Write(0); //Unknown0 + Context.ResponseData.Write(0); //Unknown1 + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"HidControllerId: {HidControllerId} - " + + $"HidNpadJoyDeviceType: {HidNpadJoyDeviceType} - " + + $"NpadJoyAssignmentModeValue: {NpadJoyAssignmentMode} - " + + $"Unknown0: 0 - " + + $"Unknown1: 0"); + + return 0; + } + + // GetVibrationDeviceInfo(nn::hid::VibrationDeviceHandle) -> nn::hid::VibrationDeviceInfo public long GetVibrationDeviceInfo(ServiceCtx Context) { int VibrationDeviceHandle = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + HidVibrationDeviceValue DeviceInfo = new HidVibrationDeviceValue + { + DeviceType = HidVibrationDeviceType.None, + Position = HidVibrationDevicePosition.None + }; - Context.ResponseData.Write(0L); //VibrationDeviceInfoForIpc + Context.ResponseData.Write((int)DeviceInfo.DeviceType); + Context.ResponseData.Write((int)DeviceInfo.Position); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. VibrationDeviceHandle: {VibrationDeviceHandle} - " + + $"DeviceType: {DeviceInfo.DeviceType} - " + + $"Position: {DeviceInfo.Position}"); return 0; } + // SendVibrationValue(nn::hid::VibrationDeviceHandle, nn::hid::VibrationValue, nn::applet::AppletResourceUserId) public long SendVibrationValue(ServiceCtx Context) { int VibrationDeviceHandle = Context.RequestData.ReadInt32(); - int VibrationValue1 = Context.RequestData.ReadInt32(); - int VibrationValue2 = Context.RequestData.ReadInt32(); - int VibrationValue3 = Context.RequestData.ReadInt32(); - int VibrationValue4 = Context.RequestData.ReadInt32(); + VibrationValue = new HidVibrationValue + { + AmplitudeLow = Context.RequestData.ReadSingle(), + FrequencyLow = Context.RequestData.ReadSingle(), + AmplitudeHigh = Context.RequestData.ReadSingle(), + FrequencyHigh = Context.RequestData.ReadSingle() + }; - long AppletUserResourceId = Context.RequestData.ReadInt64(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"VibrationDeviceHandle: {VibrationDeviceHandle} - " + + $"AmplitudeLow: {VibrationValue.AmplitudeLow} - " + + $"FrequencyLow: {VibrationValue.FrequencyLow} - " + + $"AmplitudeHigh: {VibrationValue.AmplitudeHigh} - " + + $"FrequencyHigh: {VibrationValue.FrequencyHigh}"); return 0; } + // GetActualVibrationValue(nn::hid::VibrationDeviceHandle, nn::applet::AppletResourceUserId) -> nn::hid::VibrationValue + public long GetActualVibrationValue(ServiceCtx Context) + { + int VibrationDeviceHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write(VibrationValue.AmplitudeLow); + Context.ResponseData.Write(VibrationValue.FrequencyLow); + Context.ResponseData.Write(VibrationValue.AmplitudeHigh); + Context.ResponseData.Write(VibrationValue.FrequencyHigh); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"VibrationDeviceHandle: {VibrationDeviceHandle} - " + + $"AmplitudeLow: {VibrationValue.AmplitudeLow} - " + + $"FrequencyLow: {VibrationValue.FrequencyLow} - " + + $"AmplitudeHigh: {VibrationValue.AmplitudeHigh} - " + + $"FrequencyHigh: {VibrationValue.FrequencyHigh}"); + + return 0; + } + + // CreateActiveVibrationDeviceList() -> object public long CreateActiveVibrationDeviceList(ServiceCtx Context) { MakeObject(Context, new IActiveApplicationDeviceList()); @@ -275,9 +1031,483 @@ namespace Ryujinx.HLE.HOS.Services.Hid return 0; } + // PermitVibration(bool Enable) + public long PermitVibration(ServiceCtx Context) + { + VibrationPermitted = Context.RequestData.ReadBoolean(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. VibrationPermitted: {VibrationPermitted}"); + + return 0; + } + + // IsVibrationPermitted() -> bool IsEnabled + public long IsVibrationPermitted(ServiceCtx Context) + { + Context.ResponseData.Write(VibrationPermitted); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. VibrationPermitted: {VibrationPermitted}"); + + return 0; + } + + // SendVibrationValues(nn::applet::AppletResourceUserId, buffer, type: 9>, buffer, type: 9>) public long SendVibrationValues(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceHid, "Stubbed."); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + byte[] VibrationDeviceHandleBuffer = Context.Memory.ReadBytes( + Context.Request.PtrBuff[0].Position, + Context.Request.PtrBuff[0].Size); + + byte[] VibrationValueBuffer = Context.Memory.ReadBytes( + Context.Request.PtrBuff[1].Position, + Context.Request.PtrBuff[1].Size); + + //Todo: Read all handles and values from buffer. + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"VibrationDeviceHandleBufferLength: {VibrationDeviceHandleBuffer.Length} - " + + $"VibrationValueBufferLength: {VibrationValueBuffer.Length}"); + + return 0; + } + + // SendVibrationGcErmCommand(nn::hid::VibrationDeviceHandle, nn::hid::VibrationGcErmCommand, nn::applet::AppletResourceUserId) + public long SendVibrationGcErmCommand(ServiceCtx Context) + { + int VibrationDeviceHandle = Context.RequestData.ReadInt32(); + long VibrationGcErmCommand = Context.RequestData.ReadInt64(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"VibrationDeviceHandle: {VibrationDeviceHandle} - " + + $"VibrationGcErmCommand: {VibrationGcErmCommand}"); + + return 0; + } + + // GetActualVibrationGcErmCommand(nn::hid::VibrationDeviceHandle, nn::applet::AppletResourceUserId) -> nn::hid::VibrationGcErmCommand + public long GetActualVibrationGcErmCommand(ServiceCtx Context) + { + int VibrationDeviceHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write(VibrationGcErmCommand); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"VibrationDeviceHandle: {VibrationDeviceHandle} - " + + $"VibrationGcErmCommand: {VibrationGcErmCommand}"); + + return 0; + } + + // BeginPermitVibrationSession(nn::applet::AppletResourceUserId) + public long BeginPermitVibrationSession(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + + // EndPermitVibrationSession() + public long EndPermitVibrationSession(ServiceCtx Context) + { + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed."); + + return 0; + } + + // ActivateConsoleSixAxisSensor(nn::applet::AppletResourceUserId) + public long ActivateConsoleSixAxisSensor(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + + // StartConsoleSixAxisSensor(nn::hid::ConsoleSixAxisSensorHandle, nn::applet::AppletResourceUserId) + public long StartConsoleSixAxisSensor(ServiceCtx Context) + { + int ConsoleSixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"ConsoleSixAxisSensorHandle: {ConsoleSixAxisSensorHandle}"); + + return 0; + } + + // StopConsoleSixAxisSensor(nn::hid::ConsoleSixAxisSensorHandle, nn::applet::AppletResourceUserId) + public long StopConsoleSixAxisSensor(ServiceCtx Context) + { + int ConsoleSixAxisSensorHandle = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"ConsoleSixAxisSensorHandle: {ConsoleSixAxisSensorHandle}"); + + return 0; + } + + // ActivateSevenSixAxisSensor(nn::applet::AppletResourceUserId) + public long ActivateSevenSixAxisSensor(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + + // StartSevenSixAxisSensor(nn::applet::AppletResourceUserId) + public long StartSevenSixAxisSensor(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + + // StopSevenSixAxisSensor(nn::applet::AppletResourceUserId) + public long StopSevenSixAxisSensor(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + + // InitializeSevenSixAxisSensor(array, ulong Counter0, array, ulong Counter1, nn::applet::AppletResourceUserId) + public long InitializeSevenSixAxisSensor(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + long Counter0 = Context.RequestData.ReadInt64(); + long Counter1 = Context.RequestData.ReadInt64(); + + // Todo: Determine if array is a buffer or not... + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"Counter0: {Counter0} - " + + $"Counter1: {Counter1}"); + + return 0; + } + + // FinalizeSevenSixAxisSensor(nn::applet::AppletResourceUserId) + public long FinalizeSevenSixAxisSensor(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + + // SetSevenSixAxisSensorFusionStrength(float Strength, nn::applet::AppletResourceUserId) + public long SetSevenSixAxisSensorFusionStrength(ServiceCtx Context) + { + SevenSixAxisSensorFusionStrength = Context.RequestData.ReadSingle(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SevenSixAxisSensorFusionStrength: {SevenSixAxisSensorFusionStrength}"); + + return 0; + } + + // GetSevenSixAxisSensorFusionStrength(nn::applet::AppletResourceUserId) -> float Strength + public long GetSevenSixAxisSensorFusionStrength(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.ResponseData.Write(SevenSixAxisSensorFusionStrength); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SevenSixAxisSensorFusionStrength: {SevenSixAxisSensorFusionStrength}"); + + return 0; + } + + // IsUsbFullKeyControllerEnabled() -> bool IsEnabled + public long IsUsbFullKeyControllerEnabled(ServiceCtx Context) + { + Context.ResponseData.Write(UsbFullKeyControllerEnabled); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. UsbFullKeyControllerEnabled: {UsbFullKeyControllerEnabled}"); + + return 0; + } + + // EnableUsbFullKeyController(bool Enable) + public long EnableUsbFullKeyController(ServiceCtx Context) + { + UsbFullKeyControllerEnabled = Context.RequestData.ReadBoolean(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. UsbFullKeyControllerEnabled: {UsbFullKeyControllerEnabled}"); + + return 0; + } + + // IsUsbFullKeyControllerConnected(uint Unknown0) -> bool Connected + public long IsUsbFullKeyControllerConnected(ServiceCtx Context) + { + int Unknown0 = Context.RequestData.ReadInt32(); + + Context.ResponseData.Write((byte)0x1); //FullKeyController is always connected ? + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. Unknown0: {Unknown0} - Connected: true"); + + return 0; + } + + // HasBattery(uint NpadId) -> bool HasBattery + public long HasBattery(ServiceCtx Context) + { + int NpadId = Context.RequestData.ReadInt32(); + + Context.ResponseData.Write((byte)0x1); //Npad always got a battery ? + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. NpadId: {NpadId} - HasBattery: true"); + + return 0; + } + + // HasLeftRightBattery(uint NpadId) -> bool HasLeftBattery, bool HasRightBattery + public long HasLeftRightBattery(ServiceCtx Context) + { + int NpadId = Context.RequestData.ReadInt32(); + + Context.ResponseData.Write((byte)0x1); //Npad always got a left battery ? + Context.ResponseData.Write((byte)0x1); //Npad always got a right battery ? + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. NpadId: {NpadId} - HasLeftBattery: true - HasRightBattery: true"); + + return 0; + } + + // GetNpadInterfaceType(uint NpadId) -> uchar InterfaceType + public long GetNpadInterfaceType(ServiceCtx Context) + { + int NpadId = Context.RequestData.ReadInt32(); + + Context.ResponseData.Write((byte)0); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. NpadId: {NpadId} - NpadInterfaceType: 0"); + + return 0; + } + + // GetNpadLeftRightInterfaceType(uint NpadId) -> uchar LeftInterfaceType, uchar RightInterfaceType + public long GetNpadLeftRightInterfaceType(ServiceCtx Context) + { + int NpadId = Context.RequestData.ReadInt32(); + + Context.ResponseData.Write((byte)0); + Context.ResponseData.Write((byte)0); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. NpadId: {NpadId} - " + + $"LeftInterfaceType: 0 - " + + $"RightInterfaceType: 0"); + + return 0; + } + + // GetPalmaConnectionHandle(uint Unknown0, nn::applet::AppletResourceUserId) -> nn::hid::PalmaConnectionHandle + public long GetPalmaConnectionHandle(ServiceCtx Context) + { + int Unknown0 = Context.RequestData.ReadInt32(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + int PalmaConnectionHandle = 0; + + Context.ResponseData.Write(PalmaConnectionHandle); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"Unknown0: {Unknown0} - " + + $"PalmaConnectionHandle: {PalmaConnectionHandle}"); + + return 0; + } + + // InitializePalma(nn::hid::PalmaConnectionHandle) + public long InitializePalma(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); + + return 0; + } + + // AcquirePalmaOperationCompleteEvent(nn::hid::PalmaConnectionHandle) -> nn::sf::NativeHandle + public long AcquirePalmaOperationCompleteEvent(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + + int Handle = Context.Process.HandleTable.OpenHandle(PalmaOperationCompleteEvent); + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); + + return 0; + } + + // GetPalmaOperationInfo(nn::hid::PalmaConnectionHandle) -> long Unknown0, buffer + public long GetPalmaOperationInfo(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + + long Unknown0 = 0; //Counter? + + Context.ResponseData.Write(Unknown0); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + + $"Unknown0: {Unknown0}"); + + return 0; + } + + // PlayPalmaActivity(nn::hid::PalmaConnectionHandle, ulong Unknown0) + public long PlayPalmaActivity(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + long Unknown0 = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + + $"Unknown0: {Unknown0}"); + + return 0; + } + + // SetPalmaFrModeType(nn::hid::PalmaConnectionHandle, ulong FrModeType) + public long SetPalmaFrModeType(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + long FrModeType = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + + $"FrModeType: {FrModeType}"); + + return 0; + } + + // ReadPalmaStep(nn::hid::PalmaConnectionHandle) + public long ReadPalmaStep(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); + + return 0; + } + + // EnablePalmaStep(nn::hid::PalmaConnectionHandle, bool Enable) + public long EnablePalmaStep(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + bool EnabledPalmaStep = Context.RequestData.ReadBoolean(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + + $"EnabledPalmaStep: {EnabledPalmaStep}"); + + return 0; + } + + // SuspendPalmaStep(nn::hid::PalmaConnectionHandle) + public long SuspendPalmaStep(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); + + return 0; + } + + // ResetPalmaStep(nn::hid::PalmaConnectionHandle) + public long ResetPalmaStep(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); + + return 0; + } + + // ReadPalmaApplicationSection(nn::hid::PalmaConnectionHandle, ulong Unknown0, ulong Unknown1) + public long ReadPalmaApplicationSection(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + long Unknown0 = Context.RequestData.ReadInt64(); + long Unknown1 = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + + $"Unknown0: {Unknown0} - " + + $"Unknown1: {Unknown1}"); + + return 0; + } + + // WritePalmaApplicationSection(nn::hid::PalmaConnectionHandle, ulong Unknown0, ulong Unknown1, nn::hid::PalmaApplicationSectionAccessBuffer) + public long WritePalmaApplicationSection(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + long Unknown0 = Context.RequestData.ReadInt64(); + long Unknown1 = Context.RequestData.ReadInt64(); + // nn::hid::PalmaApplicationSectionAccessBuffer cast is unknown + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + + $"Unknown0: {Unknown0} - " + + $"Unknown1: {Unknown1}"); + + return 0; + } + + // ReadPalmaUniqueCode(nn::hid::PalmaConnectionHandle) + public long ReadPalmaUniqueCode(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); + + return 0; + } + + // SetPalmaUniqueCodeInvalid(nn::hid::PalmaConnectionHandle) + public long SetPalmaUniqueCodeInvalid(ServiceCtx Context) + { + int PalmaConnectionHandle = Context.RequestData.ReadInt32(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); + + return 0; + } + + // SetNpadCommunicationMode(long CommunicationMode, nn::applet::AppletResourceUserId) + public long SetNpadCommunicationMode(ServiceCtx Context) + { + NpadCommunicationMode = Context.RequestData.ReadInt64(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadCommunicationMode: {NpadCommunicationMode}"); + + return 0; + } + + // GetNpadCommunicationMode() -> long CommunicationMode + public long GetNpadCommunicationMode(ServiceCtx Context) + { + Context.ResponseData.Write(NpadCommunicationMode); + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. CommunicationMode: {NpadCommunicationMode}"); return 0; } From a76660eac58d6f3cc7ad3118edab39e42467909f Mon Sep 17 00:00:00 2001 From: Ac_K Date: Fri, 21 Sep 2018 06:30:58 +0200 Subject: [PATCH 02/30] Fix IHidServer code style --- Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs index 0b511072d0..b42f76fa3b 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs @@ -1260,7 +1260,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { int Unknown0 = Context.RequestData.ReadInt32(); - Context.ResponseData.Write((byte)0x1); //FullKeyController is always connected ? + Context.ResponseData.Write(true); //FullKeyController is always connected ? Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. Unknown0: {Unknown0} - Connected: true"); @@ -1272,7 +1272,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { int NpadId = Context.RequestData.ReadInt32(); - Context.ResponseData.Write((byte)0x1); //Npad always got a battery ? + Context.ResponseData.Write(true); //Npad always got a battery ? Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. NpadId: {NpadId} - HasBattery: true"); @@ -1284,8 +1284,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid { int NpadId = Context.RequestData.ReadInt32(); - Context.ResponseData.Write((byte)0x1); //Npad always got a left battery ? - Context.ResponseData.Write((byte)0x1); //Npad always got a right battery ? + Context.ResponseData.Write(true); //Npad always got a left battery ? + Context.ResponseData.Write(true); //Npad always got a right battery ? Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. NpadId: {NpadId} - HasLeftBattery: true - HasRightBattery: true"); From 54ed9096bd4add5cf2ca320123f551f60c06a57f Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 22 Sep 2018 17:26:18 -0300 Subject: [PATCH 03/30] Add FMAXP and FMINP (Vector) instructions on the CPU (#412) * Add FMAXP and FMINP (Vector) instructions on the CPU * Address PR feedback --- ChocolArm64/AOpCodeTable.cs | 2 + .../Instruction/AInstEmitSimdArithmetic.cs | 303 +++++++++--------- .../Instruction/AInstEmitSimdHelper.cs | 36 +++ 3 files changed, 183 insertions(+), 158 deletions(-) diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs index fe3dce41df..6404e14f90 100644 --- a/ChocolArm64/AOpCodeTable.cs +++ b/ChocolArm64/AOpCodeTable.cs @@ -278,10 +278,12 @@ namespace ChocolArm64 SetA64("0>0011100<1xxxxx111101xxxxxxxxxx", AInstEmit.Fmax_V, typeof(AOpCodeSimdReg)); SetA64("000111100x1xxxxx011010xxxxxxxxxx", AInstEmit.Fmaxnm_S, typeof(AOpCodeSimdReg)); SetA64("0>0011100<1xxxxx110001xxxxxxxxxx", AInstEmit.Fmaxnm_V, typeof(AOpCodeSimdReg)); + SetA64("0>1011100<1xxxxx111101xxxxxxxxxx", AInstEmit.Fmaxp_V, typeof(AOpCodeSimdReg)); SetA64("000111100x1xxxxx010110xxxxxxxxxx", AInstEmit.Fmin_S, typeof(AOpCodeSimdReg)); SetA64("0>0011101<1xxxxx111101xxxxxxxxxx", AInstEmit.Fmin_V, typeof(AOpCodeSimdReg)); SetA64("000111100x1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S, typeof(AOpCodeSimdReg)); SetA64("0>0011101<1xxxxx110001xxxxxxxxxx", AInstEmit.Fminnm_V, typeof(AOpCodeSimdReg)); + SetA64("0>1011101<1xxxxx111101xxxxxxxxxx", AInstEmit.Fminp_V, typeof(AOpCodeSimdReg)); SetA64("010111111<0011100<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg)); SetA64("0x0011111<> 4; - int Pairs = Words >> Op.Size; - - for (int Index = 0; Index < Pairs; Index++) - { - int Idx = Index << 1; - - EmitVectorExtract(Context, Op.Rn, Idx, Op.Size, Signed); - EmitVectorExtract(Context, Op.Rn, Idx + 1, Op.Size, Signed); - - Context.Emit(OpCodes.Add); - - if (Accumulate) - { - EmitVectorExtract(Context, Op.Rd, Index, Op.Size + 1, Signed); - - Context.Emit(OpCodes.Add); - } - - EmitVectorInsertTmp(Context, Index, Op.Size + 1); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - - private static void EmitDoublingMultiplyHighHalf(AILEmitterCtx Context, bool Round) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int ESize = 8 << Op.Size; - - Context.Emit(OpCodes.Mul); - - if (!Round) - { - Context.EmitAsr(ESize - 1); - } - else - { - long RoundConst = 1L << (ESize - 1); - - AILLabel LblTrue = new AILLabel(); - - Context.EmitLsl(1); - - Context.EmitLdc_I8(RoundConst); - - Context.Emit(OpCodes.Add); - - Context.EmitAsr(ESize); - - Context.Emit(OpCodes.Dup); - Context.EmitLdc_I8((long)int.MinValue); - Context.Emit(OpCodes.Bne_Un_S, LblTrue); - - Context.Emit(OpCodes.Neg); - - Context.MarkLabel(LblTrue); - } - } - - private static void EmitHighNarrow(AILEmitterCtx Context, Action Emit, bool Round) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int Elems = 8 >> Op.Size; - - int ESize = 8 << Op.Size; - - int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; - - long RoundConst = 1L << (ESize - 1); - - if (Part != 0) - { - Context.EmitLdvec(Op.Rd); - Context.EmitStvectmp(); - } - - for (int Index = 0; Index < Elems; Index++) - { - EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1); - EmitVectorExtractZx(Context, Op.Rm, Index, Op.Size + 1); - - Emit(); - - if (Round) - { - Context.EmitLdc_I8(RoundConst); - - Context.Emit(OpCodes.Add); - } - - Context.EmitLsr(ESize); - - EmitVectorInsertTmp(Context, Part + Index, Op.Size); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Part == 0) - { - EmitVectorZeroUpper(Context, Op.Rd); - } - } - public static void Fabd_S(AILEmitterCtx Context) { EmitScalarBinaryOpF(Context, () => @@ -341,34 +211,7 @@ namespace ChocolArm64.Instruction public static void Faddp_V(AILEmitterCtx Context) { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int SizeF = Op.Size & 1; - - int Bytes = Op.GetBitsCount() >> 3; - - int Elems = Bytes >> SizeF + 2; - int Half = Elems >> 1; - - for (int Index = 0; Index < Elems; Index++) - { - int Elem = (Index & (Half - 1)) << 1; - - EmitVectorExtractF(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 0, SizeF); - EmitVectorExtractF(Context, Index < Half ? Op.Rn : Op.Rm, Elem + 1, SizeF); - - Context.Emit(OpCodes.Add); - - EmitVectorInsertTmpF(Context, Index, SizeF); - } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); - } + EmitVectorPairwiseOpF(Context, () => Context.Emit(OpCodes.Add)); } public static void Fdiv_S(AILEmitterCtx Context) @@ -436,6 +279,11 @@ namespace ChocolArm64.Instruction }); } + public static void Fmaxp_V(AILEmitterCtx Context) + { + EmitVectorPairwiseOpF(Context, () => EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.Max))); + } + public static void Fmin_S(AILEmitterCtx Context) { EmitScalarBinaryOpF(Context, () => @@ -468,6 +316,11 @@ namespace ChocolArm64.Instruction }); } + public static void Fminp_V(AILEmitterCtx Context) + { + EmitVectorPairwiseOpF(Context, () => EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.Min))); + } + public static void Fmla_Se(AILEmitterCtx Context) { EmitScalarTernaryOpByElemF(Context, () => @@ -1278,6 +1131,7 @@ namespace ChocolArm64.Instruction EmitVectorTernaryOpZx(Context, () => { Context.Emit(OpCodes.Sub); + EmitAbs(Context); Context.Emit(OpCodes.Add); @@ -1289,6 +1143,7 @@ namespace ChocolArm64.Instruction EmitVectorWidenRnRmTernaryOpZx(Context, () => { Context.Emit(OpCodes.Sub); + EmitAbs(Context); Context.Emit(OpCodes.Add); @@ -1300,6 +1155,7 @@ namespace ChocolArm64.Instruction EmitVectorBinaryOpZx(Context, () => { Context.Emit(OpCodes.Sub); + EmitAbs(Context); }); } @@ -1309,6 +1165,7 @@ namespace ChocolArm64.Instruction EmitVectorWidenRnRmBinaryOpZx(Context, () => { Context.Emit(OpCodes.Sub); + EmitAbs(Context); }); } @@ -1496,5 +1353,135 @@ namespace ChocolArm64.Instruction { EmitVectorWidenRmBinaryOpZx(Context, () => Context.Emit(OpCodes.Sub)); } + + private static void EmitAbs(AILEmitterCtx Context) + { + AILLabel LblTrue = new AILLabel(); + + Context.Emit(OpCodes.Dup); + Context.Emit(OpCodes.Ldc_I4_0); + Context.Emit(OpCodes.Bge_S, LblTrue); + + Context.Emit(OpCodes.Neg); + + Context.MarkLabel(LblTrue); + } + + private static void EmitAddLongPairwise(AILEmitterCtx Context, bool Signed, bool Accumulate) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Words = Op.GetBitsCount() >> 4; + int Pairs = Words >> Op.Size; + + for (int Index = 0; Index < Pairs; Index++) + { + int Idx = Index << 1; + + EmitVectorExtract(Context, Op.Rn, Idx, Op.Size, Signed); + EmitVectorExtract(Context, Op.Rn, Idx + 1, Op.Size, Signed); + + Context.Emit(OpCodes.Add); + + if (Accumulate) + { + EmitVectorExtract(Context, Op.Rd, Index, Op.Size + 1, Signed); + + Context.Emit(OpCodes.Add); + } + + EmitVectorInsertTmp(Context, Index, Op.Size + 1); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + private static void EmitDoublingMultiplyHighHalf(AILEmitterCtx Context, bool Round) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int ESize = 8 << Op.Size; + + Context.Emit(OpCodes.Mul); + + if (!Round) + { + Context.EmitAsr(ESize - 1); + } + else + { + long RoundConst = 1L << (ESize - 1); + + AILLabel LblTrue = new AILLabel(); + + Context.EmitLsl(1); + + Context.EmitLdc_I8(RoundConst); + + Context.Emit(OpCodes.Add); + + Context.EmitAsr(ESize); + + Context.Emit(OpCodes.Dup); + Context.EmitLdc_I8((long)int.MinValue); + Context.Emit(OpCodes.Bne_Un_S, LblTrue); + + Context.Emit(OpCodes.Neg); + + Context.MarkLabel(LblTrue); + } + } + + private static void EmitHighNarrow(AILEmitterCtx Context, Action Emit, bool Round) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int Elems = 8 >> Op.Size; + + int ESize = 8 << Op.Size; + + int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; + + long RoundConst = 1L << (ESize - 1); + + if (Part != 0) + { + Context.EmitLdvec(Op.Rd); + Context.EmitStvectmp(); + } + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1); + EmitVectorExtractZx(Context, Op.Rm, Index, Op.Size + 1); + + Emit(); + + if (Round) + { + Context.EmitLdc_I8(RoundConst); + + Context.Emit(OpCodes.Add); + } + + Context.EmitLsr(ESize); + + EmitVectorInsertTmp(Context, Part + Index, Op.Size); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + + if (Part == 0) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } } } diff --git a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs index 75a5a0d092..171de43be5 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs @@ -813,6 +813,42 @@ namespace ChocolArm64.Instruction } } + public static void EmitVectorPairwiseOpF(AILEmitterCtx Context, Action Emit) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int SizeF = Op.Size & 1; + + int Words = Op.GetBitsCount() >> 4; + int Pairs = Words >> SizeF + 2; + + for (int Index = 0; Index < Pairs; Index++) + { + int Idx = Index << 1; + + EmitVectorExtractF(Context, Op.Rn, Idx, SizeF); + EmitVectorExtractF(Context, Op.Rn, Idx + 1, SizeF); + + Emit(); + + EmitVectorExtractF(Context, Op.Rm, Idx, SizeF); + EmitVectorExtractF(Context, Op.Rm, Idx + 1, SizeF); + + Emit(); + + EmitVectorInsertTmpF(Context, Pairs + Index, SizeF); + EmitVectorInsertTmpF(Context, Index, SizeF); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + [Flags] public enum SaturatingFlags { From 7de7b559adc1924d3ff31cc58b281f70e468155f Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 23 Sep 2018 15:11:46 -0300 Subject: [PATCH 04/30] Improve kernel events implementation (#430) * Improve kernel events implementation * Some cleanup * Address PR feedback --- Ryujinx.HLE/HOS/Horizon.cs | 6 +- Ryujinx.HLE/HOS/Ipc/IpcHandler.cs | 5 +- Ryujinx.HLE/HOS/Kernel/HleScheduler.cs | 9 + Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs | 4 +- Ryujinx.HLE/HOS/Kernel/KEvent.cs | 36 +--- Ryujinx.HLE/HOS/Kernel/KHandleEntry.cs | 17 ++ Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs | 173 ++++++++++++++++-- Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs | 62 +++++++ Ryujinx.HLE/HOS/Kernel/KScheduler.cs | 7 - Ryujinx.HLE/HOS/Kernel/KWritableEvent.cs | 22 +++ Ryujinx.HLE/HOS/Kernel/KernelErr.cs | 1 + Ryujinx.HLE/HOS/Kernel/KernelResult.cs | 10 + Ryujinx.HLE/HOS/Kernel/SvcHandler.cs | 23 +-- Ryujinx.HLE/HOS/Kernel/SvcMemory.cs | 8 +- Ryujinx.HLE/HOS/Kernel/SvcSystem.cs | 140 ++++++++++++-- Ryujinx.HLE/HOS/Kernel/SvcThread.cs | 22 +-- Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs | 16 +- Ryujinx.HLE/HOS/Process.cs | 29 ++- .../HOS/Services/Am/ICommonStateGetter.cs | 11 +- .../HOS/Services/Am/IHomeMenuFunctions.cs | 6 +- .../HOS/Services/Am/ILibraryAppletAccessor.cs | 8 +- .../HOS/Services/Am/ISelfController.cs | 8 +- .../HOS/Services/Aud/AudioOut/IAudioOut.cs | 5 +- .../Aud/AudioRenderer/IAudioRenderer.cs | 7 +- Ryujinx.HLE/HOS/Services/Aud/IAudioDevice.cs | 18 +- .../HOS/Services/Aud/IAudioOutManager.cs | 2 +- .../HOS/Services/Hid/IAppletResource.cs | 6 +- Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs | 24 ++- Ryujinx.HLE/HOS/Services/IpcService.cs | 7 +- Ryujinx.HLE/HOS/Services/Nfp/IUser.cs | 16 +- Ryujinx.HLE/HOS/Services/Nifm/IRequest.cs | 12 +- Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs | 5 +- .../HOS/Services/Pl/ISharedFontManager.cs | 7 +- Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs | 6 +- .../Services/Vi/IApplicationDisplayService.cs | 7 +- .../HOS/Services/Vi/IHOSBinderDriver.cs | 7 +- Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs | 2 +- Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs | 4 +- 38 files changed, 597 insertions(+), 161 deletions(-) create mode 100644 Ryujinx.HLE/HOS/Kernel/KHandleEntry.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KWritableEvent.cs create mode 100644 Ryujinx.HLE/HOS/Kernel/KernelResult.cs diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index f8ec89140e..d52c8af0a2 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -71,6 +71,8 @@ namespace Ryujinx.HLE.HOS Withholders = new LinkedList(); + Scheduler.StartAutoPreemptionThread(); + if (!Device.Memory.Allocator.TryAllocate(HidSize, out long HidPA) || !Device.Memory.Allocator.TryAllocate(FontSize, out long FontPA)) { @@ -212,7 +214,7 @@ namespace Ryujinx.HLE.HOS } MainNca.SetBaseNca(PatchNca); - + if (ControlNca != null) { ReadControlData(ControlNca); @@ -466,7 +468,7 @@ namespace Ryujinx.HLE.HOS public void SignalVsync() { - VsyncEvent.Signal(); + VsyncEvent.ReadableEvent.Signal(); } private Process MakeProcess(Npdm MetaData = null) diff --git a/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs b/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs index 08a4cdb5d0..fca995c08a 100644 --- a/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs +++ b/Ryujinx.HLE/HOS/Ipc/IpcHandler.cs @@ -73,7 +73,10 @@ namespace Ryujinx.HLE.HOS.Ipc { int Unknown = ReqReader.ReadInt32(); - int Handle = Process.HandleTable.OpenHandle(Session); + if (Process.HandleTable.GenerateHandle(Session, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Response.HandleDesc = IpcHandleDesc.MakeMove(Handle); diff --git a/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs b/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs index 42caeca2d5..e0cb158c98 100644 --- a/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs +++ b/Ryujinx.HLE/HOS/Kernel/HleScheduler.cs @@ -15,6 +15,15 @@ namespace Ryujinx.HLE.HOS.Kernel private bool KeepPreempting; + public void StartAutoPreemptionThread() + { + Thread PreemptionThread = new Thread(PreemptCurrentThread); + + KeepPreempting = true; + + PreemptionThread.Start(); + } + public void ContextSwitch() { lock (CoreContexts) diff --git a/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs b/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs index f2156a5c20..73309e1e26 100644 --- a/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs +++ b/Ryujinx.HLE/HOS/Kernel/KAddressArbiter.cs @@ -51,7 +51,7 @@ namespace Ryujinx.HLE.HOS.Kernel return 0; } - KThread MutexOwner = Process.HandleTable.GetData(OwnerHandle); + KThread MutexOwner = Process.HandleTable.GetObject(OwnerHandle); if (MutexOwner == null) { @@ -282,7 +282,7 @@ namespace Ryujinx.HLE.HOS.Kernel MutexValue &= ~HasListenersMask; - KThread MutexOwner = Process.HandleTable.GetData(MutexValue); + KThread MutexOwner = Process.HandleTable.GetObject(MutexValue); if (MutexOwner != null) { diff --git a/Ryujinx.HLE/HOS/Kernel/KEvent.cs b/Ryujinx.HLE/HOS/Kernel/KEvent.cs index 1a865aa202..106d1b4092 100644 --- a/Ryujinx.HLE/HOS/Kernel/KEvent.cs +++ b/Ryujinx.HLE/HOS/Kernel/KEvent.cs @@ -1,38 +1,14 @@ namespace Ryujinx.HLE.HOS.Kernel { - class KEvent : KSynchronizationObject + class KEvent { - private bool Signaled; + public KReadableEvent ReadableEvent { get; private set; } + public KWritableEvent WritableEvent { get; private set; } - public string Name { get; private set; } - - public KEvent(Horizon System, string Name = "") : base(System) + public KEvent(Horizon System) { - this.Name = Name; - } - - public override void Signal() - { - System.CriticalSectionLock.Lock(); - - if (!Signaled) - { - Signaled = true; - - base.Signal(); - } - - System.CriticalSectionLock.Unlock(); - } - - public void Reset() - { - Signaled = false; - } - - public override bool IsSignaled() - { - return Signaled; + ReadableEvent = new KReadableEvent(System, this); + WritableEvent = new KWritableEvent(this); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KHandleEntry.cs b/Ryujinx.HLE/HOS/Kernel/KHandleEntry.cs new file mode 100644 index 0000000000..9863a374bd --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KHandleEntry.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + class KHandleEntry + { + public KHandleEntry Next { get; set; } + + public int Index { get; private set; } + + public ushort HandleId { get; set; } + public object Obj { get; set; } + + public KHandleEntry(int Index) + { + this.Index = Index; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs b/Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs index db0eaa44f9..682f08d4ff 100644 --- a/Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs +++ b/Ryujinx.HLE/HOS/Kernel/KProcessHandleTable.cs @@ -1,34 +1,183 @@ -using System.Collections.Generic; +using System; namespace Ryujinx.HLE.HOS.Kernel { class KProcessHandleTable { - private IdDictionary Handles; + private const int SelfThreadHandle = (0x1ffff << 15) | 0; + private const int SelfProcessHandle = (0x1ffff << 15) | 1; - public KProcessHandleTable() + private Horizon System; + + private KHandleEntry[] Table; + + private KHandleEntry TableHead; + private KHandleEntry NextFreeEntry; + + private int ActiveSlotsCount; + + private int Size; + + private ushort IdCounter; + + private object LockObj; + + public KProcessHandleTable(Horizon System, int Size = 1024) { - Handles = new IdDictionary(); + this.System = System; + this.Size = Size; + + IdCounter = 1; + + Table = new KHandleEntry[Size]; + + TableHead = new KHandleEntry(0); + + KHandleEntry Entry = TableHead; + + for (int Index = 0; Index < Size; Index++) + { + Table[Index] = Entry; + + Entry.Next = new KHandleEntry(Index + 1); + + Entry = Entry.Next; + } + + Table[Size - 1].Next = null; + + NextFreeEntry = TableHead; + + LockObj = new object(); } - public int OpenHandle(object Obj) + public KernelResult GenerateHandle(object Obj, out int Handle) { - return Handles.Add(Obj); + Handle = 0; + + lock (LockObj) + { + if (ActiveSlotsCount >= Size) + { + return KernelResult.HandleTableFull; + } + + KHandleEntry Entry = NextFreeEntry; + + NextFreeEntry = Entry.Next; + + Entry.Obj = Obj; + Entry.HandleId = IdCounter; + + ActiveSlotsCount++; + + Handle = (int)((IdCounter << 15) & (uint)0xffff8000) | Entry.Index; + + if ((short)(IdCounter + 1) >= 0) + { + IdCounter++; + } + else + { + IdCounter = 1; + } + } + + return KernelResult.Success; } - public T GetData(int Handle) + public bool CloseHandle(int Handle) { - return Handles.GetData(Handle); + if ((Handle >> 30) != 0 || + Handle == SelfThreadHandle || + Handle == SelfProcessHandle) + { + return false; + } + + int Index = (Handle >> 0) & 0x7fff; + int HandleId = (Handle >> 15); + + bool Result = false; + + lock (LockObj) + { + if (HandleId != 0 && Index < Size) + { + KHandleEntry Entry = Table[Index]; + + if (Entry.Obj != null && Entry.HandleId == HandleId) + { + Entry.Obj = null; + Entry.Next = NextFreeEntry; + + NextFreeEntry = Entry; + + ActiveSlotsCount--; + + Result = true; + } + } + } + + return Result; } - public object CloseHandle(int Handle) + public T GetObject(int Handle) { - return Handles.Delete(Handle); + int Index = (Handle >> 0) & 0x7fff; + int HandleId = (Handle >> 15); + + lock (LockObj) + { + if ((Handle >> 30) == 0 && HandleId != 0) + { + KHandleEntry Entry = Table[Index]; + + if (Entry.HandleId == HandleId && Entry.Obj is T Obj) + { + return Obj; + } + } + } + + return default(T); } - public ICollection Clear() + public KThread GetKThread(int Handle) { - return Handles.Clear(); + if (Handle == SelfThreadHandle) + { + return System.Scheduler.GetCurrentThread(); + } + else + { + return GetObject(Handle); + } + } + + public void Destroy() + { + lock (LockObj) + { + for (int Index = 0; Index < Size; Index++) + { + KHandleEntry Entry = Table[Index]; + + if (Entry.Obj != null) + { + if (Entry.Obj is IDisposable DisposableObj) + { + DisposableObj.Dispose(); + } + + Entry.Obj = null; + Entry.Next = NextFreeEntry; + + NextFreeEntry = Entry; + } + } + } } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs b/Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs new file mode 100644 index 0000000000..d43fe8249e --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KReadableEvent.cs @@ -0,0 +1,62 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + class KReadableEvent : KSynchronizationObject + { + private KEvent Parent; + + private bool Signaled; + + public KReadableEvent(Horizon System, KEvent Parent) : base(System) + { + this.Parent = Parent; + } + + public override void Signal() + { + System.CriticalSectionLock.Lock(); + + if (!Signaled) + { + Signaled = true; + + base.Signal(); + } + + System.CriticalSectionLock.Unlock(); + } + + public KernelResult Clear() + { + Signaled = false; + + return KernelResult.Success; + } + + public KernelResult ClearIfSignaled() + { + KernelResult Result; + + System.CriticalSectionLock.Lock(); + + if (Signaled) + { + Signaled = false; + + Result = KernelResult.Success; + } + else + { + Result = KernelResult.InvalidState; + } + + System.CriticalSectionLock.Unlock(); + + return Result; + } + + public override bool IsSignaled() + { + return Signaled; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KScheduler.cs b/Ryujinx.HLE/HOS/Kernel/KScheduler.cs index f6382d05d6..3cfda4197f 100644 --- a/Ryujinx.HLE/HOS/Kernel/KScheduler.cs +++ b/Ryujinx.HLE/HOS/Kernel/KScheduler.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Threading; namespace Ryujinx.HLE.HOS.Kernel { @@ -35,12 +34,6 @@ namespace Ryujinx.HLE.HOS.Kernel { CoreContexts[Core] = new KCoreContext(this, CoreManager); } - - Thread PreemptionThread = new Thread(PreemptCurrentThread); - - KeepPreempting = true; - - PreemptionThread.Start(); } private void PreemptThreads() diff --git a/Ryujinx.HLE/HOS/Kernel/KWritableEvent.cs b/Ryujinx.HLE/HOS/Kernel/KWritableEvent.cs new file mode 100644 index 0000000000..1721ed0011 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KWritableEvent.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + class KWritableEvent + { + private KEvent Parent; + + public KWritableEvent(KEvent Parent) + { + this.Parent = Parent; + } + + public void Signal() + { + Parent.ReadableEvent.Signal(); + } + + public KernelResult Clear() + { + return Parent.ReadableEvent.Clear(); + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/KernelErr.cs b/Ryujinx.HLE/HOS/Kernel/KernelErr.cs index 0749f3fd08..e0b196f412 100644 --- a/Ryujinx.HLE/HOS/Kernel/KernelErr.cs +++ b/Ryujinx.HLE/HOS/Kernel/KernelErr.cs @@ -6,6 +6,7 @@ namespace Ryujinx.HLE.HOS.Kernel public const int InvalidSize = 101; public const int InvalidAddress = 102; public const int OutOfMemory = 104; + public const int HandleTableFull = 105; public const int NoAccessPerm = 106; public const int InvalidPermission = 108; public const int InvalidMemRange = 110; diff --git a/Ryujinx.HLE/HOS/Kernel/KernelResult.cs b/Ryujinx.HLE/HOS/Kernel/KernelResult.cs new file mode 100644 index 0000000000..d9cbfc6735 --- /dev/null +++ b/Ryujinx.HLE/HOS/Kernel/KernelResult.cs @@ -0,0 +1,10 @@ +namespace Ryujinx.HLE.HOS.Kernel +{ + enum KernelResult + { + Success = 0, + HandleTableFull = 0xd201, + InvalidHandle = 0xe401, + InvalidState = 0xfa01 + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs b/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs index a12a0ba0f3..b678037b9e 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs @@ -39,9 +39,6 @@ namespace Ryujinx.HLE.HOS.Kernel } } - private const uint SelfThreadHandle = 0xffff8000; - private const uint SelfProcessHandle = 0xffff8001; - private static Random Rng; public SvcHandler(Switch Device, Process Process) @@ -63,12 +60,13 @@ namespace Ryujinx.HLE.HOS.Kernel { 0x0e, SvcGetThreadCoreMask }, { 0x0f, SvcSetThreadCoreMask }, { 0x10, SvcGetCurrentProcessorNumber }, - { 0x12, SvcClearEvent }, + { 0x11, SignalEvent64 }, + { 0x12, ClearEvent64 }, { 0x13, SvcMapSharedMemory }, { 0x14, SvcUnmapSharedMemory }, { 0x15, SvcCreateTransferMemory }, { 0x16, SvcCloseHandle }, - { 0x17, SvcResetSignal }, + { 0x17, ResetSignal64 }, { 0x18, SvcWaitSynchronization }, { 0x19, SvcCancelSynchronization }, { 0x1a, SvcArbitrateLock }, @@ -88,7 +86,8 @@ namespace Ryujinx.HLE.HOS.Kernel { 0x32, SvcSetThreadActivity }, { 0x33, SvcGetThreadContext3 }, { 0x34, SvcWaitForAddress }, - { 0x35, SvcSignalToAddress } + { 0x35, SvcSignalToAddress }, + { 0x45, CreateEvent64 } }; this.Device = Device; @@ -123,17 +122,5 @@ namespace Ryujinx.HLE.HOS.Kernel throw new NotImplementedException($"0x{e.Id:x4}"); } } - - private KThread GetThread(long Tpidr, int Handle) - { - if ((uint)Handle == SelfThreadHandle) - { - return Process.GetThread(Tpidr); - } - else - { - return Process.HandleTable.GetData(Handle); - } - } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs b/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs index b9e71b183c..e3c0cf5b5b 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs @@ -276,7 +276,7 @@ namespace Ryujinx.HLE.HOS.Kernel return; } - KSharedMemory SharedMemory = Process.HandleTable.GetData(Handle); + KSharedMemory SharedMemory = Process.HandleTable.GetObject(Handle); if (SharedMemory == null) { @@ -348,7 +348,7 @@ namespace Ryujinx.HLE.HOS.Kernel return; } - KSharedMemory SharedMemory = Process.HandleTable.GetData(Handle); + KSharedMemory SharedMemory = Process.HandleTable.GetObject(Handle); if (SharedMemory == null) { @@ -425,9 +425,9 @@ namespace Ryujinx.HLE.HOS.Kernel KTransferMemory TransferMemory = new KTransferMemory(Position, Size); - int Handle = Process.HandleTable.OpenHandle(TransferMemory); + KernelResult Result = Process.HandleTable.GenerateHandle(TransferMemory, out int Handle); - ThreadState.X0 = 0; + ThreadState.X0 = (uint)Result; ThreadState.X1 = (ulong)Handle; } diff --git a/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs b/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs index 60ccf7f7f8..8bcea55077 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs @@ -22,20 +22,73 @@ namespace Ryujinx.HLE.HOS.Kernel Device.System.ExitProcess(Process.ProcessId); } - private void SvcClearEvent(AThreadState ThreadState) + private void SignalEvent64(AThreadState ThreadState) { - int Handle = (int)ThreadState.X0; + ThreadState.X0 = (ulong)SignalEvent((int)ThreadState.X0); + } - //TODO: Implement events. + private KernelResult SignalEvent(int Handle) + { + KWritableEvent WritableEvent = Process.HandleTable.GetObject(Handle); - ThreadState.X0 = 0; + KernelResult Result; + + if (WritableEvent != null) + { + WritableEvent.Signal(); + + Result = KernelResult.Success; + } + else + { + Result = KernelResult.InvalidHandle; + } + + if (Result != KernelResult.Success) + { + Device.Log.PrintWarning(LogClass.KernelSvc, "Operation failed with error: " + Result + "!"); + } + + return Result; + } + + private void ClearEvent64(AThreadState ThreadState) + { + ThreadState.X0 = (ulong)ClearEvent((int)ThreadState.X0); + } + + private KernelResult ClearEvent(int Handle) + { + KernelResult Result; + + KWritableEvent WritableEvent = Process.HandleTable.GetObject(Handle); + + if (WritableEvent == null) + { + KReadableEvent ReadableEvent = Process.HandleTable.GetObject(Handle); + + Result = ReadableEvent?.Clear() ?? KernelResult.InvalidHandle; + } + else + { + Result = WritableEvent.Clear(); + } + + if (Result != KernelResult.Success) + { + Device.Log.PrintWarning(LogClass.KernelSvc, "Operation failed with error: " + Result + "!"); + } + + return Result; } private void SvcCloseHandle(AThreadState ThreadState) { int Handle = (int)ThreadState.X0; - object Obj = Process.HandleTable.CloseHandle(Handle); + object Obj = Process.HandleTable.GetObject(Handle); + + Process.HandleTable.CloseHandle(Handle); if (Obj == null) { @@ -60,24 +113,37 @@ namespace Ryujinx.HLE.HOS.Kernel ThreadState.X0 = 0; } - private void SvcResetSignal(AThreadState ThreadState) + private void ResetSignal64(AThreadState ThreadState) { - int Handle = (int)ThreadState.X0; + ThreadState.X0 = (ulong)ResetSignal((int)ThreadState.X0); + } - KEvent Event = Process.HandleTable.GetData(Handle); + private KernelResult ResetSignal(int Handle) + { + KReadableEvent ReadableEvent = Process.HandleTable.GetObject(Handle); - if (Event != null) + KernelResult Result; + + //TODO: KProcess support. + if (ReadableEvent != null) { - Event.Reset(); - - ThreadState.X0 = 0; + Result = ReadableEvent.ClearIfSignaled(); } else { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid event handle 0x{Handle:x8}!"); - - ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); + Result = KernelResult.InvalidHandle; } + + if (Result == KernelResult.InvalidState) + { + Device.Log.PrintDebug(LogClass.KernelSvc, "Operation failed with error: " + Result + "!"); + } + else if (Result != KernelResult.Success) + { + Device.Log.PrintWarning(LogClass.KernelSvc, "Operation failed with error: " + Result + "!"); + } + + return Result; } private void SvcGetSystemTick(AThreadState ThreadState) @@ -96,10 +162,13 @@ namespace Ryujinx.HLE.HOS.Kernel //actually exists, return error codes otherwise. KSession Session = new KSession(ServiceFactory.MakeService(System, Name), Name); - ulong Handle = (ulong)Process.HandleTable.OpenHandle(Session); + if (Process.HandleTable.GenerateHandle(Session, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } ThreadState.X0 = 0; - ThreadState.X1 = Handle; + ThreadState.X1 = (uint)Handle; } private void SvcSendSyncRequest(AThreadState ThreadState) @@ -122,7 +191,7 @@ namespace Ryujinx.HLE.HOS.Kernel byte[] MessageData = Memory.ReadBytes(MessagePtr, Size); - KSession Session = Process.HandleTable.GetData(Handle); + KSession Session = Process.HandleTable.GetObject(Handle); if (Session != null) { @@ -206,7 +275,8 @@ namespace Ryujinx.HLE.HOS.Kernel if (InfoType == 18 || InfoType == 19 || InfoType == 20 || - InfoType == 21) + InfoType == 21 || + InfoType == 22) { ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidEnumValue); @@ -287,5 +357,37 @@ namespace Ryujinx.HLE.HOS.Kernel ThreadState.X0 = 0; } + + private void CreateEvent64(AThreadState State) + { + KernelResult Result = CreateEvent(out int WEventHandle, out int REventHandle); + + State.X0 = (ulong)Result; + State.X1 = (ulong)WEventHandle; + State.X2 = (ulong)REventHandle; + } + + private KernelResult CreateEvent(out int WEventHandle, out int REventHandle) + { + KEvent Event = new KEvent(System); + + KernelResult Result = Process.HandleTable.GenerateHandle(Event.WritableEvent, out WEventHandle); + + if (Result == KernelResult.Success) + { + Result = Process.HandleTable.GenerateHandle(Event.ReadableEvent, out REventHandle); + + if (Result != KernelResult.Success) + { + Process.HandleTable.CloseHandle(WEventHandle); + } + } + else + { + REventHandle = 0; + } + + return Result; + } } } diff --git a/Ryujinx.HLE/HOS/Kernel/SvcThread.cs b/Ryujinx.HLE/HOS/Kernel/SvcThread.cs index dc296b060e..d9273e23a2 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcThread.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcThread.cs @@ -53,7 +53,7 @@ namespace Ryujinx.HLE.HOS.Kernel { int Handle = (int)ThreadState.X0; - KThread Thread = Process.HandleTable.GetData(Handle); + KThread Thread = Process.HandleTable.GetObject(Handle); if (Thread != null) { @@ -112,7 +112,7 @@ namespace Ryujinx.HLE.HOS.Kernel { int Handle = (int)ThreadState.X1; - KThread Thread = GetThread(ThreadState.Tpidr, Handle); + KThread Thread = Process.HandleTable.GetKThread(Handle); if (Thread != null) { @@ -138,7 +138,7 @@ namespace Ryujinx.HLE.HOS.Kernel //TODO: NPDM check. - KThread Thread = GetThread(ThreadState.Tpidr, Handle); + KThread Thread = Process.HandleTable.GetKThread(Handle); if (Thread == null) { @@ -160,7 +160,7 @@ namespace Ryujinx.HLE.HOS.Kernel Device.Log.PrintDebug(LogClass.KernelSvc, "Handle = 0x" + Handle.ToString("x8")); - KThread Thread = GetThread(ThreadState.Tpidr, Handle); + KThread Thread = Process.HandleTable.GetKThread(Handle); if (Thread != null) { @@ -178,12 +178,12 @@ namespace Ryujinx.HLE.HOS.Kernel private void SvcSetThreadCoreMask(AThreadState ThreadState) { - int ThreadHandle = (int)ThreadState.X0; + int Handle = (int)ThreadState.X0; int PrefferedCore = (int)ThreadState.X1; long AffinityMask = (long)ThreadState.X2; Device.Log.PrintDebug(LogClass.KernelSvc, - "ThreadHandle = 0x" + ThreadHandle .ToString("x8") + ", " + + "Handle = 0x" + Handle .ToString("x8") + ", " + "PrefferedCore = 0x" + PrefferedCore.ToString("x8") + ", " + "AffinityMask = 0x" + AffinityMask .ToString("x16")); @@ -219,11 +219,11 @@ namespace Ryujinx.HLE.HOS.Kernel } } - KThread Thread = GetThread(ThreadState.Tpidr, ThreadHandle); + KThread Thread = Process.HandleTable.GetKThread(Handle); if (Thread == null) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{ThreadHandle:x8}!"); + Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); @@ -249,7 +249,7 @@ namespace Ryujinx.HLE.HOS.Kernel { int Handle = (int)ThreadState.X1; - KThread Thread = GetThread(ThreadState.Tpidr, Handle); + KThread Thread = Process.HandleTable.GetKThread(Handle); if (Thread != null) { @@ -269,7 +269,7 @@ namespace Ryujinx.HLE.HOS.Kernel int Handle = (int)ThreadState.X0; bool Pause = (int)ThreadState.X1 == 1; - KThread Thread = Process.HandleTable.GetData(Handle); + KThread Thread = Process.HandleTable.GetObject(Handle); if (Thread == null) { @@ -304,7 +304,7 @@ namespace Ryujinx.HLE.HOS.Kernel long Position = (long)ThreadState.X0; int Handle = (int)ThreadState.X1; - KThread Thread = Process.HandleTable.GetData(Handle); + KThread Thread = Process.HandleTable.GetObject(Handle); if (Thread == null) { diff --git a/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs b/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs index 868e017292..db9f6fb48b 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs @@ -1,5 +1,6 @@ using ChocolArm64.State; using Ryujinx.HLE.Logging; +using System.Collections.Generic; using static Ryujinx.HLE.HOS.ErrorCode; @@ -25,22 +26,27 @@ namespace Ryujinx.HLE.HOS.Kernel return; } - KSynchronizationObject[] SyncObjs = new KSynchronizationObject[HandlesCount]; + List SyncObjs = new List(); for (int Index = 0; Index < HandlesCount; Index++) { int Handle = Memory.ReadInt32(HandlesPtr + Index * 4); - KSynchronizationObject SyncObj = Process.HandleTable.GetData(Handle); + KSynchronizationObject SyncObj = Process.HandleTable.GetObject(Handle); - SyncObjs[Index] = SyncObj; + if (SyncObj == null) + { + break; + } + + SyncObjs.Add(SyncObj); } int HndIndex = (int)ThreadState.X1; ulong High = ThreadState.X1 & (0xffffffffUL << 32); - long Result = System.Synchronization.WaitFor(SyncObjs, Timeout, ref HndIndex); + long Result = System.Synchronization.WaitFor(SyncObjs.ToArray(), Timeout, ref HndIndex); if (Result != 0) { @@ -65,7 +71,7 @@ namespace Ryujinx.HLE.HOS.Kernel Device.Log.PrintDebug(LogClass.KernelSvc, "ThreadHandle = 0x" + ThreadHandle.ToString("x8")); - KThread Thread = Process.HandleTable.GetData(ThreadHandle); + KThread Thread = Process.HandleTable.GetKThread(ThreadHandle); if (Thread == null) { diff --git a/Ryujinx.HLE/HOS/Process.cs b/Ryujinx.HLE/HOS/Process.cs index f7ec2604cc..3817f56195 100644 --- a/Ryujinx.HLE/HOS/Process.cs +++ b/Ryujinx.HLE/HOS/Process.cs @@ -71,7 +71,22 @@ namespace Ryujinx.HLE.HOS TlsPages = new List(); - HandleTable = new KProcessHandleTable(); + int HandleTableSize = 1024; + + if (MetaData != null) + { + foreach (KernelAccessControlItem Item in MetaData.ACI0.KernelAccessControl.Items) + { + if (Item.HasHandleTableSize) + { + HandleTableSize = Item.HandleTableSize; + + break; + } + } + } + + HandleTable = new KProcessHandleTable(Device.System, HandleTableSize); AppletState = new AppletStateMgr(Device.System); @@ -139,7 +154,7 @@ namespace Ryujinx.HLE.HOS return false; } - KThread MainThread = HandleTable.GetData(Handle); + KThread MainThread = HandleTable.GetKThread(Handle); if (NeedsHbAbi) { @@ -190,7 +205,7 @@ namespace Ryujinx.HLE.HOS Thread.LastPc = EntryPoint; - int Handle = HandleTable.OpenHandle(Thread); + HandleTable.GenerateHandle(Thread, out int Handle); CpuThread.ThreadState.CntfrqEl0 = TickFreq; CpuThread.ThreadState.Tpidr = Tpidr; @@ -427,13 +442,7 @@ namespace Ryujinx.HLE.HOS Disposed = true; - foreach (object Obj in HandleTable.Clear()) - { - if (Obj is KSession Session) - { - Session.Dispose(); - } - } + HandleTable.Destroy(); INvDrvServices.UnloadProcess(this); diff --git a/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs b/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs index 72049d6f44..4ea18d3298 100644 --- a/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs +++ b/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs @@ -1,6 +1,7 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.Logging; +using System; using System.Collections.Generic; using static Ryujinx.HLE.HOS.ErrorCode; @@ -36,7 +37,10 @@ namespace Ryujinx.HLE.HOS.Services.Am { KEvent Event = Context.Process.AppletState.MessageEvent; - int Handle = Context.Process.HandleTable.OpenHandle(Event); + if (Context.Process.HandleTable.GenerateHandle(Event.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); @@ -103,7 +107,10 @@ namespace Ryujinx.HLE.HOS.Services.Am public long GetDefaultDisplayResolutionChangeEvent(ServiceCtx Context) { - int Handle = Context.Process.HandleTable.OpenHandle(DisplayResolutionChangeEvent); + if (Context.Process.HandleTable.GenerateHandle(DisplayResolutionChangeEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); diff --git a/Ryujinx.HLE/HOS/Services/Am/IHomeMenuFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/IHomeMenuFunctions.cs index 0c271796e8..a476aff97a 100644 --- a/Ryujinx.HLE/HOS/Services/Am/IHomeMenuFunctions.cs +++ b/Ryujinx.HLE/HOS/Services/Am/IHomeMenuFunctions.cs @@ -1,6 +1,7 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.Logging; +using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Am @@ -34,7 +35,10 @@ namespace Ryujinx.HLE.HOS.Services.Am public long GetPopFromGeneralChannelEvent(ServiceCtx Context) { - int Handle = Context.Process.HandleTable.OpenHandle(ChannelEvent); + if (Context.Process.HandleTable.GenerateHandle(ChannelEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); diff --git a/Ryujinx.HLE/HOS/Services/Am/ILibraryAppletAccessor.cs b/Ryujinx.HLE/HOS/Services/Am/ILibraryAppletAccessor.cs index a9de3ebd45..07b8d97108 100644 --- a/Ryujinx.HLE/HOS/Services/Am/ILibraryAppletAccessor.cs +++ b/Ryujinx.HLE/HOS/Services/Am/ILibraryAppletAccessor.cs @@ -1,6 +1,7 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.Logging; +using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Am @@ -29,9 +30,12 @@ namespace Ryujinx.HLE.HOS.Services.Am public long GetAppletStateChangedEvent(ServiceCtx Context) { - StateChangedEvent.Signal(); + StateChangedEvent.ReadableEvent.Signal(); - int Handle = Context.Process.HandleTable.OpenHandle(StateChangedEvent); + if (Context.Process.HandleTable.GenerateHandle(StateChangedEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); diff --git a/Ryujinx.HLE/HOS/Services/Am/ISelfController.cs b/Ryujinx.HLE/HOS/Services/Am/ISelfController.cs index fe8822735b..2c1d0c3bcc 100644 --- a/Ryujinx.HLE/HOS/Services/Am/ISelfController.cs +++ b/Ryujinx.HLE/HOS/Services/Am/ISelfController.cs @@ -1,6 +1,7 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.Logging; +using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Am @@ -57,9 +58,12 @@ namespace Ryujinx.HLE.HOS.Services.Am public long GetLibraryAppletLaunchableEvent(ServiceCtx Context) { - LaunchableEvent.Signal(); + LaunchableEvent.ReadableEvent.Signal(); - int Handle = Context.Process.HandleTable.OpenHandle(LaunchableEvent); + if (Context.Process.HandleTable.GenerateHandle(LaunchableEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); diff --git a/Ryujinx.HLE/HOS/Services/Aud/AudioOut/IAudioOut.cs b/Ryujinx.HLE/HOS/Services/Aud/AudioOut/IAudioOut.cs index 2b0b5293ed..cd3d6e4902 100644 --- a/Ryujinx.HLE/HOS/Services/Aud/AudioOut/IAudioOut.cs +++ b/Ryujinx.HLE/HOS/Services/Aud/AudioOut/IAudioOut.cs @@ -67,7 +67,10 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioOut public long RegisterBufferEvent(ServiceCtx Context) { - int Handle = Context.Process.HandleTable.OpenHandle(ReleaseEvent); + if (Context.Process.HandleTable.GenerateHandle(ReleaseEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); diff --git a/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs b/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs index ae85bf0189..85f82622f9 100644 --- a/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs +++ b/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs @@ -72,7 +72,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer private void AudioCallback() { - UpdateEvent.Signal(); + UpdateEvent.ReadableEvent.Signal(); } private static T[] CreateArray(int Size) where T : new() @@ -218,7 +218,10 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer public long QuerySystemEvent(ServiceCtx Context) { - int Handle = Context.Process.HandleTable.OpenHandle(UpdateEvent); + if (Context.Process.HandleTable.GenerateHandle(UpdateEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); diff --git a/Ryujinx.HLE/HOS/Services/Aud/IAudioDevice.cs b/Ryujinx.HLE/HOS/Services/Aud/IAudioDevice.cs index adecc7210a..a1a228ed1e 100644 --- a/Ryujinx.HLE/HOS/Services/Aud/IAudioDevice.cs +++ b/Ryujinx.HLE/HOS/Services/Aud/IAudioDevice.cs @@ -2,6 +2,7 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Logging; +using System; using System.Collections.Generic; using System.Text; @@ -35,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud SystemEvent = new KEvent(System); //TODO: We shouldn't be signaling this here. - SystemEvent.Signal(); + SystemEvent.ReadableEvent.Signal(); } public long ListAudioDeviceName(ServiceCtx Context) @@ -107,7 +108,10 @@ namespace Ryujinx.HLE.HOS.Services.Aud public long QueryAudioDeviceSystemEvent(ServiceCtx Context) { - int Handle = Context.Process.HandleTable.OpenHandle(SystemEvent); + if (Context.Process.HandleTable.GenerateHandle(SystemEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); @@ -200,7 +204,10 @@ namespace Ryujinx.HLE.HOS.Services.Aud public long QueryAudioDeviceInputEvent(ServiceCtx Context) { - int Handle = Context.Process.HandleTable.OpenHandle(SystemEvent); + if (Context.Process.HandleTable.GenerateHandle(SystemEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); @@ -211,7 +218,10 @@ namespace Ryujinx.HLE.HOS.Services.Aud public long QueryAudioDeviceOutputEvent(ServiceCtx Context) { - int Handle = Context.Process.HandleTable.OpenHandle(SystemEvent); + if (Context.Process.HandleTable.GenerateHandle(SystemEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); diff --git a/Ryujinx.HLE/HOS/Services/Aud/IAudioOutManager.cs b/Ryujinx.HLE/HOS/Services/Aud/IAudioOutManager.cs index ef9250d926..44b856cda6 100644 --- a/Ryujinx.HLE/HOS/Services/Aud/IAudioOutManager.cs +++ b/Ryujinx.HLE/HOS/Services/Aud/IAudioOutManager.cs @@ -150,7 +150,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud ReleaseCallback Callback = () => { - ReleaseEvent.Signal(); + ReleaseEvent.ReadableEvent.Signal(); }; IAalOutput AudioOut = Context.Device.AudioOut; diff --git a/Ryujinx.HLE/HOS/Services/Hid/IAppletResource.cs b/Ryujinx.HLE/HOS/Services/Hid/IAppletResource.cs index 012ccb4053..89a17acf7b 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/IAppletResource.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/IAppletResource.cs @@ -1,5 +1,6 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; +using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Hid @@ -24,7 +25,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid public long GetSharedMemoryHandle(ServiceCtx Context) { - int Handle = Context.Process.HandleTable.OpenHandle(HidSharedMem); + if (Context.Process.HandleTable.GenerateHandle(HidSharedMem, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); diff --git a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs index b42f76fa3b..e88ca7e4db 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs @@ -2,7 +2,6 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.Input; using Ryujinx.HLE.Logging; -using Ryujinx.HLE.Utilities; using System; using System.Collections.Generic; @@ -219,7 +218,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid { long XpadId = Context.RequestData.ReadInt64(); - XpadIdEventHandle = Context.Process.HandleTable.OpenHandle(XpadIdEvent); + if (Context.Process.HandleTable.GenerateHandle(XpadIdEvent, out XpadIdEventHandle) == 0) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(XpadIdEventHandle); @@ -411,7 +413,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid SixAxisSensorFusionEnabled = Context.RequestData.ReadBoolean(); int SixAxisSensorHandle = Context.RequestData.ReadInt32(); long AppletResourceUserId = Context.RequestData.ReadInt64(); - + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + $"SixAxisSensorFusionEnabled: {SixAxisSensorFusionEnabled}"); @@ -619,7 +621,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid // IsSixAxisSensorAtRest(nn::hid::SixAxisSensorHandle, nn::applet::AppletResourceUserId) -> bool IsAsRest public long IsSixAxisSensorAtRest(ServiceCtx Context) - { + { int SixAxisSensorHandle = Context.RequestData.ReadInt32(); long AppletResourceUserId = Context.RequestData.ReadInt64(); @@ -712,7 +714,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid int NpadId = Context.RequestData.ReadInt32(); long NpadStyleSet = Context.RequestData.ReadInt64(); - int Handle = Context.Process.HandleTable.OpenHandle(NpadStyleSetUpdateEvent); + if (Context.Process.HandleTable.GenerateHandle(NpadStyleSetUpdateEvent, out int Handle) == 0) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); @@ -1135,7 +1140,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { int ConsoleSixAxisSensorHandle = Context.RequestData.ReadInt32(); long AppletResourceUserId = Context.RequestData.ReadInt64(); - + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + $"ConsoleSixAxisSensorHandle: {ConsoleSixAxisSensorHandle}"); @@ -1351,7 +1356,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid { int PalmaConnectionHandle = Context.RequestData.ReadInt32(); - int Handle = Context.Process.HandleTable.OpenHandle(PalmaOperationCompleteEvent); + if (Context.Process.HandleTable.GenerateHandle(PalmaOperationCompleteEvent, out int Handle) == 0) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); @@ -1393,7 +1401,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid int PalmaConnectionHandle = Context.RequestData.ReadInt32(); long FrModeType = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + + Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + $"FrModeType: {FrModeType}"); return 0; diff --git a/Ryujinx.HLE/HOS/Services/IpcService.cs b/Ryujinx.HLE/HOS/Services/IpcService.cs index 8e487d55c1..e9d820001d 100644 --- a/Ryujinx.HLE/HOS/Services/IpcService.cs +++ b/Ryujinx.HLE/HOS/Services/IpcService.cs @@ -132,7 +132,10 @@ namespace Ryujinx.HLE.HOS.Services { KSession Session = new KSession(Obj, Context.Session.ServiceName); - int Handle = Context.Process.HandleTable.OpenHandle(Session); + if (Context.Process.HandleTable.GenerateHandle(Session, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle); } @@ -146,7 +149,7 @@ namespace Ryujinx.HLE.HOS.Services { int Handle = Context.Request.HandleDesc.ToMove[Index]; - KSession Session = Context.Process.HandleTable.GetData(Handle); + KSession Session = Context.Process.HandleTable.GetObject(Handle); return Session?.Service is T ? (T)Session.Service : null; } diff --git a/Ryujinx.HLE/HOS/Services/Nfp/IUser.cs b/Ryujinx.HLE/HOS/Services/Nfp/IUser.cs index 33f739677f..72a385d2ff 100644 --- a/Ryujinx.HLE/HOS/Services/Nfp/IUser.cs +++ b/Ryujinx.HLE/HOS/Services/Nfp/IUser.cs @@ -2,6 +2,7 @@ using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.Input; using Ryujinx.HLE.Logging; +using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Nfp @@ -55,7 +56,10 @@ namespace Ryujinx.HLE.HOS.Services.Nfp { Context.Device.Log.PrintStub(LogClass.ServiceNfp, "Stubbed."); - int Handle = Context.Process.HandleTable.OpenHandle(ActivateEvent); + if (Context.Process.HandleTable.GenerateHandle(ActivateEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle);; @@ -66,7 +70,10 @@ namespace Ryujinx.HLE.HOS.Services.Nfp { Context.Device.Log.PrintStub(LogClass.ServiceNfp, "Stubbed."); - int Handle = Context.Process.HandleTable.OpenHandle(DeactivateEvent); + if (Context.Process.HandleTable.GenerateHandle(DeactivateEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); @@ -104,7 +111,10 @@ namespace Ryujinx.HLE.HOS.Services.Nfp { Context.Device.Log.PrintStub(LogClass.ServiceNfp, "Stubbed."); - int Handle = Context.Process.HandleTable.OpenHandle(AvailabilityChangeEvent); + if (Context.Process.HandleTable.GenerateHandle(AvailabilityChangeEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); diff --git a/Ryujinx.HLE/HOS/Services/Nifm/IRequest.cs b/Ryujinx.HLE/HOS/Services/Nifm/IRequest.cs index 3f4df719cf..9b501d7ca7 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/IRequest.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/IRequest.cs @@ -1,6 +1,7 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.Logging; +using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Nifm @@ -48,8 +49,15 @@ namespace Ryujinx.HLE.HOS.Services.Nifm public long GetSystemEventReadableHandles(ServiceCtx Context) { - int Handle0 = Context.Process.HandleTable.OpenHandle(Event0); - int Handle1 = Context.Process.HandleTable.OpenHandle(Event1); + if (Context.Process.HandleTable.GenerateHandle(Event0.ReadableEvent, out int Handle0) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + if (Context.Process.HandleTable.GenerateHandle(Event1.ReadableEvent, out int Handle1) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle0, Handle1); diff --git a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs index 7d5589920f..96de8cab4e 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs @@ -123,7 +123,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv int EventId = Context.RequestData.ReadInt32(); //TODO: Use Fd/EventId, different channels have different events. - int Handle = Context.Process.HandleTable.OpenHandle(Event); + if (Context.Process.HandleTable.GenerateHandle(Event.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); diff --git a/Ryujinx.HLE/HOS/Services/Pl/ISharedFontManager.cs b/Ryujinx.HLE/HOS/Services/Pl/ISharedFontManager.cs index 92821217ba..d73adc0efd 100644 --- a/Ryujinx.HLE/HOS/Services/Pl/ISharedFontManager.cs +++ b/Ryujinx.HLE/HOS/Services/Pl/ISharedFontManager.cs @@ -1,5 +1,7 @@ using Ryujinx.HLE.HOS.Font; using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel; +using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Pl @@ -65,7 +67,10 @@ namespace Ryujinx.HLE.HOS.Services.Pl { Context.Device.System.Font.EnsureInitialized(); - int Handle = Context.Process.HandleTable.OpenHandle(Context.Device.System.FontSharedMem); + if (Context.Process.HandleTable.GenerateHandle(Context.Device.System.FontSharedMem, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); diff --git a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs index c56d65dbc0..0c26b7d9dd 100644 --- a/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Sm/IUserInterface.cs @@ -1,5 +1,6 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; +using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Sm @@ -59,7 +60,10 @@ namespace Ryujinx.HLE.HOS.Services.Sm KSession Session = new KSession(ServiceFactory.MakeService(Context.Device.System, Name), Name); - int Handle = Context.Process.HandleTable.OpenHandle(Session); + if (Context.Process.HandleTable.GenerateHandle(Session, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle); diff --git a/Ryujinx.HLE/HOS/Services/Vi/IApplicationDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/IApplicationDisplayService.cs index 5423827932..33a1dee974 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/IApplicationDisplayService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/IApplicationDisplayService.cs @@ -1,6 +1,8 @@ using ChocolArm64.Memory; using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel; using System.Collections.Generic; +using System; using System.IO; using System.Text; @@ -178,7 +180,10 @@ namespace Ryujinx.HLE.HOS.Services.Vi { string Name = GetDisplayName(Context); - int Handle = Context.Process.HandleTable.OpenHandle(Context.Device.System.VsyncEvent); + if (Context.Process.HandleTable.GenerateHandle(Context.Device.System.VsyncEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); diff --git a/Ryujinx.HLE/HOS/Services/Vi/IHOSBinderDriver.cs b/Ryujinx.HLE/HOS/Services/Vi/IHOSBinderDriver.cs index d47fc30a7d..09a37b0f7b 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/IHOSBinderDriver.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/IHOSBinderDriver.cs @@ -29,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi BinderEvent = new KEvent(System); - BinderEvent.Signal(); + BinderEvent.ReadableEvent.Signal(); Flinger = new NvFlinger(Renderer, BinderEvent); } @@ -77,7 +77,10 @@ namespace Ryujinx.HLE.HOS.Services.Vi int Id = Context.RequestData.ReadInt32(); uint Unk = Context.RequestData.ReadUInt32(); - int Handle = Context.Process.HandleTable.OpenHandle(BinderEvent); + if (Context.Process.HandleTable.GenerateHandle(BinderEvent.ReadableEvent, out int Handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle); diff --git a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs index dcdf5d1747..c5f38211b9 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs @@ -338,7 +338,7 @@ namespace Ryujinx.HLE.HOS.Services.Android { BufferQueue[Slot].State = BufferState.Free; - BinderEvent.Signal(); + BinderEvent.ReadableEvent.Signal(); WaitBufferFree.Set(); } diff --git a/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs b/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs index b537b06acf..274892c08a 100644 --- a/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs +++ b/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs @@ -32,14 +32,14 @@ namespace Ryujinx.HLE.HOS.SystemState { Messages.Enqueue(Message); - MessageEvent.Signal(); + MessageEvent.ReadableEvent.Signal(); } public bool TryDequeueMessage(out MessageInfo Message) { if (Messages.Count < 2) { - MessageEvent.Reset(); + MessageEvent.ReadableEvent.Clear(); } return Messages.TryDequeue(out Message); From 2562ca6c3fe6ef328e0926c9cbcd6bb52abb328f Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Tue, 25 Sep 2018 19:55:30 -0300 Subject: [PATCH 05/30] Fix multiple rendertargets (#427) * Simplify render target bindings * Implement multiple viewports * Pack glViewportIndexed calls into a single glViewportArray * Use ARB_viewport_array when available * Cache framebuffer attachments * Use get accessors in OGLExtension * Address feedback --- Ryujinx.Graphics/Gal/IGalRenderTarget.cs | 10 +- .../Gal/OpenGL/OGLEnumConverter.cs | 2 +- Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs | 37 +-- .../Gal/OpenGL/OGLRenderTarget.cs | 257 ++++++++++-------- Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs | 2 +- Ryujinx.Graphics/GpuResourceManager.cs | 4 +- Ryujinx.Graphics/NvGpuEngine3d.cs | 26 +- Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs | 2 +- 8 files changed, 185 insertions(+), 155 deletions(-) diff --git a/Ryujinx.Graphics/Gal/IGalRenderTarget.cs b/Ryujinx.Graphics/Gal/IGalRenderTarget.cs index 7ccf0981d1..f941ccd584 100644 --- a/Ryujinx.Graphics/Gal/IGalRenderTarget.cs +++ b/Ryujinx.Graphics/Gal/IGalRenderTarget.cs @@ -2,15 +2,17 @@ namespace Ryujinx.Graphics.Gal { public interface IGalRenderTarget { - void BindColor(long Key, int Attachment, GalImage Image); + void Bind(); + + void BindColor(long Key, int Attachment); void UnbindColor(int Attachment); - void BindZeta(long Key, GalImage Image); + void BindZeta(long Key); void UnbindZeta(); - void Set(long Key); + void Present(long Key); void SetMap(int[] Map); @@ -18,7 +20,7 @@ namespace Ryujinx.Graphics.Gal void SetWindowSize(int Width, int Height); - void SetViewport(int X, int Y, int Width, int Height); + void SetViewport(int Attachment, int X, int Y, int Width, int Height); void Render(); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs index 187d1eece7..7f9e9fbe3d 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs @@ -225,7 +225,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalTextureWrap.Clamp: return TextureWrapMode.Clamp; } - if (OGLExtension.HasTextureMirrorClamp()) + if (OGLExtension.TextureMirrorClamp) { switch (Wrap) { diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs index 5ad422980c..11daeb593c 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLExtension.cs @@ -1,40 +1,17 @@ using OpenTK.Graphics.OpenGL; +using System; namespace Ryujinx.Graphics.Gal.OpenGL { static class OGLExtension { - private static bool Initialized = false; + private static Lazy s_EnhancedLayouts = new Lazy(() => HasExtension("GL_ARB_enhanced_layouts")); + private static Lazy s_TextureMirrorClamp = new Lazy(() => HasExtension("GL_EXT_texture_mirror_clamp")); + private static Lazy s_ViewportArray = new Lazy(() => HasExtension("GL_ARB_viewport_array")); - private static bool EnhancedLayouts; - - private static bool TextureMirrorClamp; - - public static bool HasEnhancedLayouts() - { - EnsureInitialized(); - - return EnhancedLayouts; - } - - public static bool HasTextureMirrorClamp() - { - EnsureInitialized(); - - return TextureMirrorClamp; - } - - private static void EnsureInitialized() - { - if (Initialized) - { - return; - } - - EnhancedLayouts = HasExtension("GL_ARB_enhanced_layouts"); - - TextureMirrorClamp = HasExtension("GL_EXT_texture_mirror_clamp"); - } + public static bool EnhancedLayouts => s_EnhancedLayouts.Value; + public static bool TextureMirrorClamp => s_TextureMirrorClamp.Value; + public static bool ViewportArray => s_ViewportArray.Value; private static bool HasExtension(string Name) { diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs index ff5dc1b895..78cf5d2fa9 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs @@ -22,16 +22,52 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } + private class FrameBufferAttachments + { + public long[] Colors; + public long Zeta; + + public int MapCount; + public DrawBuffersEnum[] Map; + + public FrameBufferAttachments() + { + Colors = new long[RenderTargetsCount]; + + Map = new DrawBuffersEnum[RenderTargetsCount]; + } + + public void SetAndClear(FrameBufferAttachments Source) + { + Zeta = Source.Zeta; + MapCount = Source.MapCount; + + Source.Zeta = 0; + Source.MapCount = 0; + + for (int i = 0; i < RenderTargetsCount; i++) + { + Colors[i] = Source.Colors[i]; + Map[i] = Source.Map[i]; + + Source.Colors[i] = 0; + Source.Map[i] = 0; + } + } + } + private const int NativeWidth = 1280; private const int NativeHeight = 720; + private const int RenderTargetsCount = 8; + private const GalImageFormat RawFormat = GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm; private OGLTexture Texture; private ImageHandler ReadTex; - private Rect Viewport; + private float[] Viewports; private Rect Window; private bool FlipX; @@ -50,138 +86,164 @@ namespace Ryujinx.Graphics.Gal.OpenGL private int SrcFb; private int DstFb; - //Holds current attachments, used to avoid unnecesary calls to OpenGL - private int[] ColorAttachments; - - private int DepthAttachment; - private int StencilAttachment; + private FrameBufferAttachments Attachments; + private FrameBufferAttachments OldAttachments; private int CopyPBO; public OGLRenderTarget(OGLTexture Texture) { - ColorAttachments = new int[8]; + Attachments = new FrameBufferAttachments(); + + OldAttachments = new FrameBufferAttachments(); + + Viewports = new float[RenderTargetsCount * 4]; this.Texture = Texture; } - public void BindColor(long Key, int Attachment, GalImage Image) + public void Bind() { - if (Texture.TryGetImageHandler(Key, out ImageHandler CachedImage)) + if (DummyFrameBuffer == 0) { - EnsureFrameBuffer(); - - Attach(ref ColorAttachments[Attachment], CachedImage.Handle, FramebufferAttachment.ColorAttachment0 + Attachment); + DummyFrameBuffer = GL.GenFramebuffer(); } - else + + GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DummyFrameBuffer); + + ImageHandler CachedImage; + + for (int Attachment = 0; Attachment < RenderTargetsCount; Attachment++) { - UnbindColor(Attachment); - } - } - - public void UnbindColor(int Attachment) - { - EnsureFrameBuffer(); - - Attach(ref ColorAttachments[Attachment], 0, FramebufferAttachment.ColorAttachment0 + Attachment); - } - - public void BindZeta(long Key, GalImage Image) - { - if (Texture.TryGetImageHandler(Key, out ImageHandler CachedImage)) - { - EnsureFrameBuffer(); - - if (CachedImage.HasDepth && CachedImage.HasStencil) + if (Attachments.Colors[Attachment] == OldAttachments.Colors[Attachment]) { - if (DepthAttachment != CachedImage.Handle || - StencilAttachment != CachedImage.Handle) + continue; + } + + int Handle = 0; + + if (Attachments.Colors[Attachment] != 0 && + Texture.TryGetImageHandler(Attachments.Colors[Attachment], out CachedImage)) + { + Handle = CachedImage.Handle; + } + + GL.FramebufferTexture( + FramebufferTarget.DrawFramebuffer, + FramebufferAttachment.ColorAttachment0 + Attachment, + Handle, + 0); + } + + if (Attachments.Zeta != OldAttachments.Zeta) + { + if (Attachments.Zeta != 0 && Texture.TryGetImageHandler(Attachments.Zeta, out CachedImage)) + { + if (CachedImage.HasDepth && CachedImage.HasStencil) { GL.FramebufferTexture( FramebufferTarget.DrawFramebuffer, FramebufferAttachment.DepthStencilAttachment, CachedImage.Handle, 0); - - DepthAttachment = CachedImage.Handle; - StencilAttachment = CachedImage.Handle; } - } - else if (CachedImage.HasDepth) - { - Attach(ref DepthAttachment, CachedImage.Handle, FramebufferAttachment.DepthAttachment); + else if (CachedImage.HasDepth) + { + GL.FramebufferTexture( + FramebufferTarget.DrawFramebuffer, + FramebufferAttachment.DepthAttachment, + CachedImage.Handle, + 0); - Attach(ref StencilAttachment, 0, FramebufferAttachment.StencilAttachment); - } - else if (CachedImage.HasStencil) - { - Attach(ref DepthAttachment, 0, FramebufferAttachment.DepthAttachment); - - Attach(ref StencilAttachment, CachedImage.Handle, FramebufferAttachment.StencilAttachment); + GL.FramebufferTexture( + FramebufferTarget.DrawFramebuffer, + FramebufferAttachment.StencilAttachment, + 0, + 0); + } + else + { + throw new NotImplementedException(); + } } else { - throw new InvalidOperationException(); + GL.FramebufferTexture( + FramebufferTarget.DrawFramebuffer, + FramebufferAttachment.DepthStencilAttachment, + 0, + 0); } } + + if (OGLExtension.ViewportArray) + { + GL.ViewportArray(0, 8, Viewports); + } else { - UnbindZeta(); + GL.Viewport( + (int)Viewports[0], + (int)Viewports[1], + (int)Viewports[2], + (int)Viewports[3]); } - } - private void Attach(ref int OldHandle, int NewHandle, FramebufferAttachment FbAttachment) - { - if (OldHandle != NewHandle) + if (Attachments.MapCount > 1) { - GL.FramebufferTexture( - FramebufferTarget.DrawFramebuffer, - FbAttachment, - NewHandle, - 0); - - OldHandle = NewHandle; + GL.DrawBuffers(Attachments.MapCount, Attachments.Map); } + else if (Attachments.MapCount == 1) + { + GL.DrawBuffer((DrawBufferMode)Attachments.Map[0]); + } + else + { + GL.DrawBuffer(DrawBufferMode.None); + } + + OldAttachments.SetAndClear(Attachments); } + public void BindColor(long Key, int Attachment) + { + Attachments.Colors[Attachment] = Key; + } + + public void UnbindColor(int Attachment) + { + Attachments.Colors[Attachment] = 0; + } + + public void BindZeta(long Key) + { + Attachments.Zeta = Key; + } + public void UnbindZeta() { - EnsureFrameBuffer(); - - if (DepthAttachment != 0 || StencilAttachment != 0) - { - GL.FramebufferTexture( - FramebufferTarget.DrawFramebuffer, - FramebufferAttachment.DepthStencilAttachment, - 0, - 0); - - DepthAttachment = 0; - StencilAttachment = 0; - } + Attachments.Zeta = 0; } - public void Set(long Key) + public void Present(long Key) { Texture.TryGetImageHandler(Key, out ReadTex); } public void SetMap(int[] Map) { - if (Map != null && Map.Length > 0) + if (Map != null) { - DrawBuffersEnum[] Mode = new DrawBuffersEnum[Map.Length]; + Attachments.MapCount = Map.Length; - for (int i = 0; i < Map.Length; i++) + for (int Attachment = 0; Attachment < Attachments.MapCount; Attachment++) { - Mode[i] = DrawBuffersEnum.ColorAttachment0 + Map[i]; + Attachments.Map[Attachment] = DrawBuffersEnum.ColorAttachment0 + Map[Attachment]; } - - GL.DrawBuffers(Mode.Length, Mode); } else { - GL.DrawBuffer(DrawBufferMode.ColorAttachment0); + Attachments.MapCount = 0; } } @@ -201,20 +263,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL Window = new Rect(0, 0, Width, Height); } - public void SetViewport(int X, int Y, int Width, int Height) + public void SetViewport(int Attachment, int X, int Y, int Width, int Height) { - Viewport = new Rect(X, Y, Width, Height); + int Offset = Attachment * 4; - SetViewport(Viewport); - } - - private void SetViewport(Rect Viewport) - { - GL.Viewport( - Viewport.X, - Viewport.Y, - Viewport.Width, - Viewport.Height); + Viewports[Offset + 0] = X; + Viewports[Offset + 1] = Y; + Viewports[Offset + 2] = Width; + Viewports[Offset + 3] = Height; } public void Render() @@ -276,7 +332,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.FramebufferTexture(FramebufferTarget.ReadFramebuffer, FramebufferAttachment.ColorAttachment0, ReadTex.Handle, 0); GL.ReadBuffer(ReadBufferMode.ColorAttachment0); - GL.DrawBuffer(DrawBufferMode.ColorAttachment0); GL.Clear(ClearBufferMask.ColorBufferBit); @@ -285,8 +340,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL DstX0, DstY0, DstX1, DstY1, ClearBufferMask.ColorBufferBit, BlitFramebufferFilter.Linear); - - EnsureFrameBuffer(); } public void Copy( @@ -343,8 +396,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.Clear(Mask); GL.BlitFramebuffer(SrcX0, SrcY0, SrcX1, SrcY1, DstX0, DstY0, DstX1, DstY1, Mask, Filter); - - EnsureFrameBuffer(); } } @@ -419,15 +470,5 @@ namespace Ryujinx.Graphics.Gal.OpenGL (CachedImage.HasDepth ? ClearBufferMask.DepthBufferBit : 0) | (CachedImage.HasStencil ? ClearBufferMask.StencilBufferBit : 0); } - - private void EnsureFrameBuffer() - { - if (DummyFrameBuffer == 0) - { - DummyFrameBuffer = GL.GenFramebuffer(); - } - - GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, DummyFrameBuffer); - } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs index 73d37b8791..efcb7e3471 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs @@ -133,7 +133,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { //Enhanced layouts are required for Geometry shaders //skip this stage if current driver has no ARB_enhanced_layouts - if (!OGLExtension.HasEnhancedLayouts()) + if (!OGLExtension.EnhancedLayouts) { return; } diff --git a/Ryujinx.Graphics/GpuResourceManager.cs b/Ryujinx.Graphics/GpuResourceManager.cs index 0a8d201452..71390a83ac 100644 --- a/Ryujinx.Graphics/GpuResourceManager.cs +++ b/Ryujinx.Graphics/GpuResourceManager.cs @@ -46,7 +46,7 @@ namespace Ryujinx.Graphics Gpu.Renderer.Texture.Create(Position, (int)Size, NewImage); } - Gpu.Renderer.RenderTarget.BindColor(Position, Attachment, NewImage); + Gpu.Renderer.RenderTarget.BindColor(Position, Attachment); } public void SendZetaBuffer(NvGpuVmm Vmm, long Position, GalImage NewImage) @@ -60,7 +60,7 @@ namespace Ryujinx.Graphics Gpu.Renderer.Texture.Create(Position, (int)Size, NewImage); } - Gpu.Renderer.RenderTarget.BindZeta(Position, NewImage); + Gpu.Renderer.RenderTarget.BindZeta(Position); } public void SendTexture(NvGpuVmm Vmm, long Position, GalImage NewImage, int TexIndex = -1) diff --git a/Ryujinx.Graphics/NvGpuEngine3d.cs b/Ryujinx.Graphics/NvGpuEngine3d.cs index b19f3063bf..22c0937770 100644 --- a/Ryujinx.Graphics/NvGpuEngine3d.cs +++ b/Ryujinx.Graphics/NvGpuEngine3d.cs @@ -100,7 +100,10 @@ namespace Ryujinx.Graphics SetAlphaBlending(State); SetPrimitiveRestart(State); - SetFrameBuffer(Vmm, 0); + for (int FbIndex = 0; FbIndex < 8; FbIndex++) + { + SetFrameBuffer(Vmm, FbIndex); + } SetZeta(Vmm); @@ -154,6 +157,10 @@ namespace Ryujinx.Graphics SetZeta(Vmm); + SetRenderTargets(); + + Gpu.Renderer.RenderTarget.Bind(); + Gpu.Renderer.Rasterizer.ClearBuffers( Flags, FbIndex, @@ -204,7 +211,7 @@ namespace Ryujinx.Graphics Gpu.ResourceManager.SendColorBuffer(Vmm, Key, FbIndex, Image); - Gpu.Renderer.RenderTarget.SetViewport(VpX, VpY, VpW, VpH); + Gpu.Renderer.RenderTarget.SetViewport(FbIndex, VpX, VpY, VpW, VpH); } private void SetFrameBuffer(GalPipelineState State) @@ -426,14 +433,15 @@ namespace Ryujinx.Graphics private void SetRenderTargets() { - bool SeparateFragData = ReadRegisterBool(NvGpuEngine3dReg.RTSeparateFragData); + //Commercial games do not seem to + //bool SeparateFragData = ReadRegisterBool(NvGpuEngine3dReg.RTSeparateFragData); - if (SeparateFragData) + uint Control = (uint)(ReadRegister(NvGpuEngine3dReg.RTControl)); + + uint Count = Control & 0xf; + + if (Count > 0) { - uint Control = (uint)(ReadRegister(NvGpuEngine3dReg.RTControl)); - - uint Count = Control & 0xf; - int[] Map = new int[Count]; for (int i = 0; i < Count; i++) @@ -702,6 +710,8 @@ namespace Ryujinx.Graphics Gpu.Renderer.Pipeline.Bind(State); + Gpu.Renderer.RenderTarget.Bind(); + if (IndexCount != 0) { int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); diff --git a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs index c5f38211b9..191537b1da 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs @@ -328,7 +328,7 @@ namespace Ryujinx.HLE.HOS.Services.Android Context.Device.Gpu.ResourceManager.SendTexture(Vmm, FbAddr, Image); Renderer.RenderTarget.SetTransform(FlipX, FlipY, Top, Left, Right, Bottom); - Renderer.RenderTarget.Set(FbAddr); + Renderer.RenderTarget.Present(FbAddr); ReleaseBuffer(Slot); }); From 40282da93a45c90b3d5a696199ee353a1ae8c730 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Wed, 26 Sep 2018 00:59:29 +0200 Subject: [PATCH 06/30] Implement friend:a IFriendService: 10101 (#429) * Implement friend:a IFriendService: 10101 * Update Profile Picture * Update IFriendServiceTypes.cs * Update IFriendServiceTypes.cs * Update IFriendService.cs * Update IFriendServiceTypes.cs * Update IFriendServiceTypes.cs * Update IFriendService.cs --- .../HOS/Services/Friend/IFriendService.cs | 56 +++++++++++++++++- .../Services/Friend/IFriendServiceTypes.cs | 20 +++++++ Ryujinx.HLE/RyujinxProfileImage.jpg | Bin 5394 -> 52969 bytes 3 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 Ryujinx.HLE/HOS/Services/Friend/IFriendServiceTypes.cs diff --git a/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs b/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs index 3b06ba8afc..d476f5d027 100644 --- a/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs +++ b/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs @@ -15,11 +15,50 @@ namespace Ryujinx.HLE.HOS.Services.Friend { m_Commands = new Dictionary() { + { 10101, GetFriendList }, { 10601, DeclareCloseOnlinePlaySession }, { 10610, UpdateUserPresence } }; } + // nn::friends::GetFriendListGetFriendListIds(nn::account::Uid, int Unknown0, nn::friends::detail::ipc::SizedFriendFilter, ulong Unknown1) -> int CounterIds, array + public long GetFriendList(ServiceCtx Context) + { + UserId Uuid = new UserId( + Context.RequestData.ReadInt64(), + Context.RequestData.ReadInt64()); + + int Unknown0 = Context.RequestData.ReadInt32(); + + FriendFilter Filter = new FriendFilter() + { + PresenceStatus = (PresenceStatusFilter)Context.RequestData.ReadInt32(), + IsFavoriteOnly = Context.RequestData.ReadBoolean(), + IsSameAppPresenceOnly = Context.RequestData.ReadBoolean(), + IsSameAppPlayedOnly = Context.RequestData.ReadBoolean(), + IsArbitraryAppPlayedOnly = Context.RequestData.ReadBoolean(), + PresenceGroupId = Context.RequestData.ReadInt64() + }; + + long Unknown1 = Context.RequestData.ReadInt64(); + + // There are no friends online, so we return 0 because the nn::account::NetworkServiceAccountId array is empty. + Context.ResponseData.Write(0); + + Context.Device.Log.PrintStub(LogClass.ServiceFriend, $"Stubbed. UserId: {Uuid.UserIdHex} - " + + $"Unknown0: {Unknown0} - " + + $"PresenceStatus: {Filter.PresenceStatus} - " + + $"IsFavoriteOnly: {Filter.IsFavoriteOnly} - " + + $"IsSameAppPresenceOnly: {Filter.IsSameAppPresenceOnly} - " + + $"IsSameAppPlayedOnly: {Filter.IsSameAppPlayedOnly} - " + + $"IsArbitraryAppPlayedOnly: {Filter.IsArbitraryAppPlayedOnly} - " + + $"PresenceGroupId: {Filter.PresenceGroupId} - " + + $"Unknown1: {Unknown1}"); + + return 0; + } + + // DeclareCloseOnlinePlaySession(nn::account::Uid) public long DeclareCloseOnlinePlaySession(ServiceCtx Context) { UserId Uuid = new UserId( @@ -31,19 +70,30 @@ namespace Ryujinx.HLE.HOS.Services.Friend Profile.OnlinePlayState = OpenCloseState.Closed; } + Context.Device.Log.PrintStub(LogClass.ServiceFriend, $"Stubbed. Uuid: {Uuid.UserIdHex} - " + + $"OnlinePlayState: {Profile.OnlinePlayState}"); + return 0; } + // UpdateUserPresence(nn::account::Uid, ulong Unknown0) -> buffer public long UpdateUserPresence(ServiceCtx Context) { UserId Uuid = new UserId( Context.RequestData.ReadInt64(), Context.RequestData.ReadInt64()); - //TODO. - Context.Device.Log.PrintStub(LogClass.ServiceFriend, "Stubbed."); + long Unknown0 = Context.RequestData.ReadInt64(); + + long Position = Context.Request.PtrBuff[0].Position; + long Size = Context.Request.PtrBuff[0].Size; + + //Todo: Write the buffer content. + + Context.Device.Log.PrintStub(LogClass.ServiceFriend, $"Stubbed. Uuid: {Uuid.UserIdHex} - " + + $"Unknown0: {Unknown0}"); return 0; } } -} \ No newline at end of file +} diff --git a/Ryujinx.HLE/HOS/Services/Friend/IFriendServiceTypes.cs b/Ryujinx.HLE/HOS/Services/Friend/IFriendServiceTypes.cs new file mode 100644 index 0000000000..31459f7d68 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Friend/IFriendServiceTypes.cs @@ -0,0 +1,20 @@ +namespace Ryujinx.HLE.HOS.Services.Friend +{ + enum PresenceStatusFilter + { + None, + Online, + OnlinePlay, + OnlineOrOnlinePlay + } + + struct FriendFilter + { + public PresenceStatusFilter PresenceStatus; + public bool IsFavoriteOnly; + public bool IsSameAppPresenceOnly; + public bool IsSameAppPlayedOnly; + public bool IsArbitraryAppPlayedOnly; + public long PresenceGroupId; + } +} diff --git a/Ryujinx.HLE/RyujinxProfileImage.jpg b/Ryujinx.HLE/RyujinxProfileImage.jpg index fe9ec2a9590d2d53ef1dcffc8078f5bd04e7b00f..55e4c43cf2c110b7a9fd17177e0056b15f0ac47e 100644 GIT binary patch literal 52969 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 From 0b52ee66272b673cecebcf9ae9baaf03899e0ee3 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 26 Sep 2018 23:30:21 -0300 Subject: [PATCH 07/30] Optimize BIC, BSL, BIT, BIF, XTN, ZIP, DUP (Gp), FMADD (Scalar) and FCVT (Scalar) using SSE intrinsics (#405) * Optimize BIC, BSL, BIT, BIF, XTN, ZIP, DUP (Gp), FMADD (Scalar) and FCVT (Scalar) using SSE intrinsics, some CQ improvements * Remove useless space * Address PR feedback * Revert EmitVectorZero32_128 changes --- .../Instruction/AInstEmitSimdArithmetic.cs | 67 ++- ChocolArm64/Instruction/AInstEmitSimdCmp.cs | 20 +- ChocolArm64/Instruction/AInstEmitSimdCvt.cs | 45 +- .../Instruction/AInstEmitSimdHelper.cs | 228 +++++++--- .../Instruction/AInstEmitSimdLogical.cs | 170 +++++-- ChocolArm64/Instruction/AInstEmitSimdMove.cs | 204 +++++++-- ChocolArm64/Instruction/AVectorHelper.cs | 430 +++++++++++++----- ChocolArm64/Memory/AMemory.cs | 8 +- 8 files changed, 896 insertions(+), 276 deletions(-) diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs index be549875b9..811730fc4a 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs @@ -4,6 +4,7 @@ using ChocolArm64.Translation; using System; using System.Reflection; using System.Reflection.Emit; +using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; using static ChocolArm64.Instruction.AInstEmitSimdHelper; @@ -31,7 +32,7 @@ namespace ChocolArm64.Instruction { if (AOptimizations.UseSse2) { - EmitSse2Call(Context, nameof(Sse2.Add)); + EmitSse2Op(Context, nameof(Sse2.Add)); } else { @@ -175,7 +176,7 @@ namespace ChocolArm64.Instruction { if (AOptimizations.UseSse && AOptimizations.UseSse2) { - EmitScalarSseOrSse2CallF(Context, nameof(Sse.AddScalar)); + EmitScalarSseOrSse2OpF(Context, nameof(Sse.AddScalar)); } else { @@ -187,7 +188,7 @@ namespace ChocolArm64.Instruction { if (AOptimizations.UseSse && AOptimizations.UseSse2) { - EmitVectorSseOrSse2CallF(Context, nameof(Sse.Add)); + EmitVectorSseOrSse2OpF(Context, nameof(Sse.Add)); } else { @@ -218,7 +219,7 @@ namespace ChocolArm64.Instruction { if (AOptimizations.UseSse && AOptimizations.UseSse2) { - EmitScalarSseOrSse2CallF(Context, nameof(Sse.DivideScalar)); + EmitScalarSseOrSse2OpF(Context, nameof(Sse.DivideScalar)); } else { @@ -230,7 +231,7 @@ namespace ChocolArm64.Instruction { if (AOptimizations.UseSse && AOptimizations.UseSse2) { - EmitVectorSseOrSse2CallF(Context, nameof(Sse.Divide)); + EmitVectorSseOrSse2OpF(Context, nameof(Sse.Divide)); } else { @@ -240,11 +241,49 @@ namespace ChocolArm64.Instruction public static void Fmadd_S(AILEmitterCtx Context) { - EmitScalarTernaryRaOpF(Context, () => + if (AOptimizations.UseSse2) { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Add); - }); + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + if (Op.Size == 0) + { + Context.EmitLdvec(Op.Ra); + Context.EmitLdvec(Op.Rn); + Context.EmitLdvec(Op.Rm); + + Type[] Types = new Type[] { typeof(Vector128), typeof(Vector128) }; + + Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), Types)); + Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.AddScalar), Types)); + + Context.EmitStvec(Op.Rd); + + EmitVectorZero32_128(Context, Op.Rd); + } + else /* if (Op.Size == 1) */ + { + EmitLdvecWithCastToDouble(Context, Op.Ra); + EmitLdvecWithCastToDouble(Context, Op.Rn); + EmitLdvecWithCastToDouble(Context, Op.Rm); + + Type[] Types = new Type[] { typeof(Vector128), typeof(Vector128) }; + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), Types)); + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AddScalar), Types)); + + EmitStvecWithCastFromDouble(Context, Op.Rd); + + EmitVectorZeroUpper(Context, Op.Rd); + } + } + else + { + EmitScalarTernaryRaOpF(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Add); + }); + } } public static void Fmax_S(AILEmitterCtx Context) @@ -379,7 +418,7 @@ namespace ChocolArm64.Instruction { if (AOptimizations.UseSse && AOptimizations.UseSse2) { - EmitScalarSseOrSse2CallF(Context, nameof(Sse.MultiplyScalar)); + EmitScalarSseOrSse2OpF(Context, nameof(Sse.MultiplyScalar)); } else { @@ -396,7 +435,7 @@ namespace ChocolArm64.Instruction { if (AOptimizations.UseSse && AOptimizations.UseSse2) { - EmitVectorSseOrSse2CallF(Context, nameof(Sse.Multiply)); + EmitVectorSseOrSse2OpF(Context, nameof(Sse.Multiply)); } else { @@ -763,7 +802,7 @@ namespace ChocolArm64.Instruction { if (AOptimizations.UseSse && AOptimizations.UseSse2) { - EmitScalarSseOrSse2CallF(Context, nameof(Sse.SubtractScalar)); + EmitScalarSseOrSse2OpF(Context, nameof(Sse.SubtractScalar)); } else { @@ -775,7 +814,7 @@ namespace ChocolArm64.Instruction { if (AOptimizations.UseSse && AOptimizations.UseSse2) { - EmitVectorSseOrSse2CallF(Context, nameof(Sse.Subtract)); + EmitVectorSseOrSse2OpF(Context, nameof(Sse.Subtract)); } else { @@ -1103,7 +1142,7 @@ namespace ChocolArm64.Instruction { if (AOptimizations.UseSse2) { - EmitSse2Call(Context, nameof(Sse2.Subtract)); + EmitSse2Op(Context, nameof(Sse2.Subtract)); } else { diff --git a/ChocolArm64/Instruction/AInstEmitSimdCmp.cs b/ChocolArm64/Instruction/AInstEmitSimdCmp.cs index 6357396d3c..97f7623fa3 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdCmp.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdCmp.cs @@ -23,11 +23,11 @@ namespace ChocolArm64.Instruction { if (Op.Size < 3 && AOptimizations.UseSse2) { - EmitSse2Call(Context, nameof(Sse2.CompareEqual)); + EmitSse2Op(Context, nameof(Sse2.CompareEqual)); } else if (Op.Size == 3 && AOptimizations.UseSse41) { - EmitSse41Call(Context, nameof(Sse41.CompareEqual)); + EmitSse41Op(Context, nameof(Sse41.CompareEqual)); } else { @@ -61,11 +61,11 @@ namespace ChocolArm64.Instruction { if (Op.Size < 3 && AOptimizations.UseSse2) { - EmitSse2Call(Context, nameof(Sse2.CompareGreaterThan)); + EmitSse2Op(Context, nameof(Sse2.CompareGreaterThan)); } else if (Op.Size == 3 && AOptimizations.UseSse42) { - EmitSse42Call(Context, nameof(Sse42.CompareGreaterThan)); + EmitSse42Op(Context, nameof(Sse42.CompareGreaterThan)); } else { @@ -158,7 +158,7 @@ namespace ChocolArm64.Instruction if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse && AOptimizations.UseSse2) { - EmitScalarSseOrSse2CallF(Context, nameof(Sse.CompareEqualScalar)); + EmitScalarSseOrSse2OpF(Context, nameof(Sse.CompareEqualScalar)); } else { @@ -171,7 +171,7 @@ namespace ChocolArm64.Instruction if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse && AOptimizations.UseSse2) { - EmitVectorSseOrSse2CallF(Context, nameof(Sse.CompareEqual)); + EmitVectorSseOrSse2OpF(Context, nameof(Sse.CompareEqual)); } else { @@ -184,7 +184,7 @@ namespace ChocolArm64.Instruction if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse && AOptimizations.UseSse2) { - EmitScalarSseOrSse2CallF(Context, nameof(Sse.CompareGreaterThanOrEqualScalar)); + EmitScalarSseOrSse2OpF(Context, nameof(Sse.CompareGreaterThanOrEqualScalar)); } else { @@ -197,7 +197,7 @@ namespace ChocolArm64.Instruction if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse && AOptimizations.UseSse2) { - EmitVectorSseOrSse2CallF(Context, nameof(Sse.CompareGreaterThanOrEqual)); + EmitVectorSseOrSse2OpF(Context, nameof(Sse.CompareGreaterThanOrEqual)); } else { @@ -210,7 +210,7 @@ namespace ChocolArm64.Instruction if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse && AOptimizations.UseSse2) { - EmitScalarSseOrSse2CallF(Context, nameof(Sse.CompareGreaterThanScalar)); + EmitScalarSseOrSse2OpF(Context, nameof(Sse.CompareGreaterThanScalar)); } else { @@ -223,7 +223,7 @@ namespace ChocolArm64.Instruction if (Context.CurrOp is AOpCodeSimdReg && AOptimizations.UseSse && AOptimizations.UseSse2) { - EmitVectorSseOrSse2CallF(Context, nameof(Sse.CompareGreaterThan)); + EmitVectorSseOrSse2OpF(Context, nameof(Sse.CompareGreaterThan)); } else { diff --git a/ChocolArm64/Instruction/AInstEmitSimdCvt.cs b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs index 231de0aff7..76d984a23b 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdCvt.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdCvt.cs @@ -3,6 +3,8 @@ using ChocolArm64.State; using ChocolArm64.Translation; using System; using System.Reflection.Emit; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; using static ChocolArm64.Instruction.AInstEmitSimdHelper; @@ -14,11 +16,48 @@ namespace ChocolArm64.Instruction { AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + if (AOptimizations.UseSse2) + { + if (Op.Size == 1 && Op.Opc == 0) + { + //Double -> Single. + AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleZero)); - EmitFloatCast(Context, Op.Opc); + EmitLdvecWithCastToDouble(Context, Op.Rn); - EmitScalarSetF(Context, Op.Rd, Op.Opc); + Type[] Types = new Type[] { typeof(Vector128), typeof(Vector128) }; + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertScalarToVector128Single), Types)); + + Context.EmitStvec(Op.Rd); + } + else if (Op.Size == 0 && Op.Opc == 1) + { + //Single -> Double. + AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorDoubleZero)); + + Context.EmitLdvec(Op.Rn); + + Type[] Types = new Type[] { typeof(Vector128), typeof(Vector128) }; + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ConvertScalarToVector128Double), Types)); + + EmitStvecWithCastFromDouble(Context, Op.Rd); + } + else + { + //Invalid encoding. + throw new InvalidOperationException(); + } + } + else + { + EmitVectorExtractF(Context, Op.Rn, 0, Op.Size); + + EmitFloatCast(Context, Op.Opc); + + EmitScalarSetF(Context, Op.Rd, Op.Opc); + } } public static void Fcvtas_Gp(AILEmitterCtx Context) diff --git a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs index 171de43be5..381fc46ac9 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs @@ -4,7 +4,6 @@ using ChocolArm64.Translation; using System; using System.Reflection; using System.Reflection.Emit; -using System.Runtime.CompilerServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; @@ -12,6 +11,38 @@ namespace ChocolArm64.Instruction { static class AInstEmitSimdHelper { + public static readonly Type[] IntTypesPerSizeLog2 = new Type[] + { + typeof(sbyte), + typeof(short), + typeof(int), + typeof(long) + }; + + public static readonly Type[] UIntTypesPerSizeLog2 = new Type[] + { + typeof(byte), + typeof(ushort), + typeof(uint), + typeof(ulong) + }; + + public static readonly Type[] VectorIntTypesPerSizeLog2 = new Type[] + { + typeof(Vector128), + typeof(Vector128), + typeof(Vector128), + typeof(Vector128) + }; + + public static readonly Type[] VectorUIntTypesPerSizeLog2 = new Type[] + { + typeof(Vector128), + typeof(Vector128), + typeof(Vector128), + typeof(Vector128) + }; + [Flags] public enum OperFlags { @@ -36,56 +67,32 @@ namespace ChocolArm64.Instruction return (8 << (Op.Size + 1)) - Op.Imm; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void EmitSse2Call(AILEmitterCtx Context, string Name) + public static void EmitSse2Op(AILEmitterCtx Context, string Name) { - EmitSseCall(Context, Name, typeof(Sse2)); + EmitSseOp(Context, Name, typeof(Sse2)); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void EmitSse41Call(AILEmitterCtx Context, string Name) + public static void EmitSse41Op(AILEmitterCtx Context, string Name) { - EmitSseCall(Context, Name, typeof(Sse41)); + EmitSseOp(Context, Name, typeof(Sse41)); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void EmitSse42Call(AILEmitterCtx Context, string Name) + public static void EmitSse42Op(AILEmitterCtx Context, string Name) { - EmitSseCall(Context, Name, typeof(Sse42)); + EmitSseOp(Context, Name, typeof(Sse42)); } - private static void EmitSseCall(AILEmitterCtx Context, string Name, Type Type) + private static void EmitSseOp(AILEmitterCtx Context, string Name, Type Type) { AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - void Ldvec(int Reg) - { - Context.EmitLdvec(Reg); + EmitLdvecWithSignedCast(Context, Op.Rn, Op.Size); - switch (Op.Size) - { - case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToSByte)); break; - case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToInt16)); break; - case 2: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToInt32)); break; - case 3: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToInt64)); break; - } - } - - Ldvec(Op.Rn); - - Type BaseType = null; - - switch (Op.Size) - { - case 0: BaseType = typeof(Vector128); break; - case 1: BaseType = typeof(Vector128); break; - case 2: BaseType = typeof(Vector128); break; - case 3: BaseType = typeof(Vector128); break; - } + Type BaseType = VectorIntTypesPerSizeLog2[Op.Size]; if (Op is AOpCodeSimdReg BinOp) { - Ldvec(BinOp.Rm); + EmitLdvecWithSignedCast(Context, BinOp.Rm, Op.Size); Context.EmitCall(Type.GetMethod(Name, new Type[] { BaseType, BaseType })); } @@ -94,15 +101,7 @@ namespace ChocolArm64.Instruction Context.EmitCall(Type.GetMethod(Name, new Type[] { BaseType })); } - switch (Op.Size) - { - case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSByteToSingle)); break; - case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt16ToSingle)); break; - case 2: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt32ToSingle)); break; - case 3: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt64ToSingle)); break; - } - - Context.EmitStvec(Op.Rd); + EmitStvecWithSignedCast(Context, Op.Rd, Op.Size); if (Op.RegisterSize == ARegisterSize.SIMD64) { @@ -110,17 +109,91 @@ namespace ChocolArm64.Instruction } } - public static void EmitScalarSseOrSse2CallF(AILEmitterCtx Context, string Name) + public static void EmitLdvecWithSignedCast(AILEmitterCtx Context, int Reg, int Size) { - EmitSseOrSse2CallF(Context, Name, true); + Context.EmitLdvec(Reg); + + switch (Size) + { + case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToSByte)); break; + case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToInt16)); break; + case 2: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToInt32)); break; + case 3: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToInt64)); break; + + default: throw new ArgumentOutOfRangeException(nameof(Size)); + } } - public static void EmitVectorSseOrSse2CallF(AILEmitterCtx Context, string Name) + public static void EmitLdvecWithCastToDouble(AILEmitterCtx Context, int Reg) { - EmitSseOrSse2CallF(Context, Name, false); + Context.EmitLdvec(Reg); + + AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToDouble)); } - public static void EmitSseOrSse2CallF(AILEmitterCtx Context, string Name, bool Scalar) + public static void EmitStvecWithCastFromDouble(AILEmitterCtx Context, int Reg) + { + AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorDoubleToSingle)); + + Context.EmitStvec(Reg); + } + + public static void EmitLdvecWithUnsignedCast(AILEmitterCtx Context, int Reg, int Size) + { + Context.EmitLdvec(Reg); + + switch (Size) + { + case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToByte)); break; + case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToUInt16)); break; + case 2: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToUInt32)); break; + case 3: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleToUInt64)); break; + + default: throw new ArgumentOutOfRangeException(nameof(Size)); + } + } + + public static void EmitStvecWithSignedCast(AILEmitterCtx Context, int Reg, int Size) + { + switch (Size) + { + case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSByteToSingle)); break; + case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt16ToSingle)); break; + case 2: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt32ToSingle)); break; + case 3: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt64ToSingle)); break; + + default: throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.EmitStvec(Reg); + } + + public static void EmitStvecWithUnsignedCast(AILEmitterCtx Context, int Reg, int Size) + { + switch (Size) + { + case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorByteToSingle)); break; + case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorUInt16ToSingle)); break; + case 2: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorUInt32ToSingle)); break; + case 3: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorUInt64ToSingle)); break; + + default: throw new ArgumentOutOfRangeException(nameof(Size)); + } + + Context.EmitStvec(Reg); + } + + public static void EmitScalarSseOrSse2OpF(AILEmitterCtx Context, string Name) + { + EmitSseOrSse2OpF(Context, Name, true); + } + + public static void EmitVectorSseOrSse2OpF(AILEmitterCtx Context, string Name) + { + EmitSseOrSse2OpF(Context, Name, false); + } + + public static void EmitSseOrSse2OpF(AILEmitterCtx Context, string Name, bool Scalar) { AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; @@ -1183,8 +1256,21 @@ namespace ChocolArm64.Instruction public static void EmitScalarSetF(AILEmitterCtx Context, int Reg, int Size) { - EmitVectorZeroAll(Context, Reg); - EmitVectorInsertF(Context, Reg, 0, Size); + if (AOptimizations.UseSse41 && Size == 0) + { + //If the type is float, we can perform insertion and + //zero the upper bits with a single instruction (INSERTPS); + Context.EmitLdvec(Reg); + + AVectorHelper.EmitCall(Context, nameof(AVectorHelper.Sse41VectorInsertScalarSingle)); + + Context.EmitStvec(Reg); + } + else + { + EmitVectorZeroAll(Context, Reg); + EmitVectorInsertF(Context, Reg, 0, Size); + } } public static void EmitVectorExtractSx(AILEmitterCtx Context, int Reg, int Index, int Size) @@ -1235,8 +1321,17 @@ namespace ChocolArm64.Instruction public static void EmitVectorZeroAll(AILEmitterCtx Context, int Rd) { - EmitVectorZeroLower(Context, Rd); - EmitVectorZeroUpper(Context, Rd); + if (AOptimizations.UseSse2) + { + AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorSingleZero)); + + Context.EmitStvec(Rd); + } + else + { + EmitVectorZeroLower(Context, Rd); + EmitVectorZeroUpper(Context, Rd); + } } public static void EmitVectorZeroLower(AILEmitterCtx Context, int Rd) @@ -1249,9 +1344,32 @@ namespace ChocolArm64.Instruction EmitVectorInsertTmp(Context, 0, 3, 0); } - public static void EmitVectorZeroUpper(AILEmitterCtx Context, int Rd) + public static void EmitVectorZeroUpper(AILEmitterCtx Context, int Reg) { - EmitVectorInsert(Context, Rd, 1, 3, 0); + if (AOptimizations.UseSse2) + { + //TODO: Use MoveScalar once it is fixed, as of the + //time of writing it just crashes the JIT. + EmitLdvecWithUnsignedCast(Context, Reg, 3); + + Type[] Types = new Type[] { typeof(Vector128), typeof(byte) }; + + //Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MoveScalar), Types)); + + Context.EmitLdc_I4(8); + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical128BitLane), Types)); + + Context.EmitLdc_I4(8); + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), Types)); + + EmitStvecWithUnsignedCast(Context, Reg, 3); + } + else + { + EmitVectorInsert(Context, Reg, 1, 3, 0); + } } public static void EmitVectorZero32_128(AILEmitterCtx Context, int Reg) diff --git a/ChocolArm64/Instruction/AInstEmitSimdLogical.cs b/ChocolArm64/Instruction/AInstEmitSimdLogical.cs index 9f5af96cb4..1aa8981f54 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdLogical.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdLogical.cs @@ -15,7 +15,7 @@ namespace ChocolArm64.Instruction { if (AOptimizations.UseSse2) { - EmitSse2Call(Context, nameof(Sse2.And)); + EmitSse2Op(Context, nameof(Sse2.And)); } else { @@ -25,11 +25,36 @@ namespace ChocolArm64.Instruction public static void Bic_V(AILEmitterCtx Context) { - EmitVectorBinaryOpZx(Context, () => + if (AOptimizations.UseSse2) { - Context.Emit(OpCodes.Not); - Context.Emit(OpCodes.And); - }); + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size); + EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); + + Type[] Types = new Type[] + { + VectorUIntTypesPerSizeLog2[Op.Size], + VectorUIntTypesPerSizeLog2[Op.Size] + }; + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AndNot), Types)); + + EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + else + { + EmitVectorBinaryOpZx(Context, () => + { + Context.Emit(OpCodes.Not); + Context.Emit(OpCodes.And); + }); + } } public static void Bic_Vi(AILEmitterCtx Context) @@ -55,59 +80,124 @@ namespace ChocolArm64.Instruction { AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - for (int Index = 0; Index < Elems; Index++) + if (AOptimizations.UseSse2) { - EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size); - EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); - - Context.Emit(OpCodes.Xor); - - EmitVectorExtractZx(Context, Op.Rm, Index, Op.Size); - - if (NotRm) + Type[] Types = new Type[] { - Context.Emit(OpCodes.Not); + VectorUIntTypesPerSizeLog2[Op.Size], + VectorUIntTypesPerSizeLog2[Op.Size] + }; + + EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size); + EmitLdvecWithUnsignedCast(Context, Op.Rd, Op.Size); + EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), Types)); + + string Name = NotRm ? nameof(Sse2.AndNot) : nameof(Sse2.And); + + Context.EmitCall(typeof(Sse2).GetMethod(Name, Types)); + + EmitLdvecWithUnsignedCast(Context, Op.Rd, Op.Size); + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), Types)); + + EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + else + { + int Bytes = Op.GetBitsCount() >> 3; + int Elems = Bytes >> Op.Size; + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size); + EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); + + Context.Emit(OpCodes.Xor); + + EmitVectorExtractZx(Context, Op.Rm, Index, Op.Size); + + if (NotRm) + { + Context.Emit(OpCodes.Not); + } + + Context.Emit(OpCodes.And); + + EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size); + + Context.Emit(OpCodes.Xor); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); } - Context.Emit(OpCodes.And); - - EmitVectorExtractZx(Context, Op.Rd, Index, Op.Size); - - Context.Emit(OpCodes.Xor); - - EmitVectorInsert(Context, Op.Rd, Index, Op.Size); - } - - if (Op.RegisterSize == ARegisterSize.SIMD64) - { - EmitVectorZeroUpper(Context, Op.Rd); + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } } } public static void Bsl_V(AILEmitterCtx Context) { - EmitVectorTernaryOpZx(Context, () => + if (AOptimizations.UseSse2) { - Context.EmitSttmp(); - Context.EmitLdtmp(); + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - Context.Emit(OpCodes.Xor); - Context.Emit(OpCodes.And); + Type[] Types = new Type[] + { + VectorUIntTypesPerSizeLog2[Op.Size], + VectorUIntTypesPerSizeLog2[Op.Size] + }; - Context.EmitLdtmp(); + EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); + EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size); - Context.Emit(OpCodes.Xor); - }); + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), Types)); + + EmitLdvecWithUnsignedCast(Context, Op.Rd, Op.Size); + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), Types)); + + EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size); + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Xor), Types)); + + EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + else + { + EmitVectorTernaryOpZx(Context, () => + { + Context.EmitSttmp(); + Context.EmitLdtmp(); + + Context.Emit(OpCodes.Xor); + Context.Emit(OpCodes.And); + + Context.EmitLdtmp(); + + Context.Emit(OpCodes.Xor); + }); + } } public static void Eor_V(AILEmitterCtx Context) { if (AOptimizations.UseSse2) { - EmitSse2Call(Context, nameof(Sse2.Xor)); + EmitSse2Op(Context, nameof(Sse2.Xor)); } else { @@ -133,7 +223,7 @@ namespace ChocolArm64.Instruction { if (AOptimizations.UseSse2) { - EmitSse2Call(Context, nameof(Sse2.Or)); + EmitSse2Op(Context, nameof(Sse2.Or)); } else { diff --git a/ChocolArm64/Instruction/AInstEmitSimdMove.cs b/ChocolArm64/Instruction/AInstEmitSimdMove.cs index 3bf1e4635b..94097f4806 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdMove.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdMove.cs @@ -3,6 +3,7 @@ using ChocolArm64.State; using ChocolArm64.Translation; using System; using System.Reflection.Emit; +using System.Runtime.Intrinsics.X86; using static ChocolArm64.Instruction.AInstEmitSimdHelper; @@ -14,19 +15,44 @@ namespace ChocolArm64.Instruction { AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; - int Bytes = Op.GetBitsCount() >> 3; - int Elems = Bytes >> Op.Size; - - for (int Index = 0; Index < Elems; Index++) + if (AOptimizations.UseSse2) { Context.EmitLdintzr(Op.Rn); - EmitVectorInsert(Context, Op.Rd, Index, Op.Size); - } + switch (Op.Size) + { + case 0: Context.Emit(OpCodes.Conv_U1); break; + case 1: Context.Emit(OpCodes.Conv_U2); break; + case 2: Context.Emit(OpCodes.Conv_U4); break; + } - if (Op.RegisterSize == ARegisterSize.SIMD64) + Type[] Types = new Type[] { UIntTypesPerSizeLog2[Op.Size] }; + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), Types)); + + EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + else { - EmitVectorZeroUpper(Context, Op.Rd); + int Bytes = Op.GetBitsCount() >> 3; + int Elems = Bytes >> Op.Size; + + for (int Index = 0; Index < Elems; Index++) + { + Context.EmitLdintzr(Op.Rn); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } } } @@ -295,25 +321,91 @@ namespace ChocolArm64.Instruction int Part = Op.RegisterSize == ARegisterSize.SIMD128 ? Elems : 0; - if (Part != 0) + if (AOptimizations.UseSse41 && Op.Size < 2) { - Context.EmitLdvec(Op.Rd); - Context.EmitStvectmp(); + void EmitZeroVector() + { + switch (Op.Size) + { + case 0: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt16Zero)); break; + case 1: AVectorHelper.EmitCall(Context, nameof(AVectorHelper.VectorInt32Zero)); break; + } + } + + //For XTN, first operand is source, second operand is 0. + //For XTN2, first operand is 0, second operand is source. + if (Part != 0) + { + EmitZeroVector(); + } + + EmitLdvecWithSignedCast(Context, Op.Rn, Op.Size + 1); + + //Set mask to discard the upper half of the wide elements. + switch (Op.Size) + { + case 0: Context.EmitLdc_I4(0x00ff); break; + case 1: Context.EmitLdc_I4(0x0000ffff); break; + } + + Type WideType = IntTypesPerSizeLog2[Op.Size + 1]; + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), new Type[] { WideType })); + + WideType = VectorIntTypesPerSizeLog2[Op.Size + 1]; + + Type[] WideTypes = new Type[] { WideType, WideType }; + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.And), WideTypes)); + + if (Part == 0) + { + EmitZeroVector(); + } + + //Pack values with signed saturation, the signed saturation shouldn't + //saturate anything since the upper bits were masked off. + Type SseType = Op.Size == 0 ? typeof(Sse2) : typeof(Sse41); + + Context.EmitCall(SseType.GetMethod(nameof(Sse2.PackUnsignedSaturate), WideTypes)); + + if (Part != 0) + { + //For XTN2, we additionally need to discard the upper bits + //of the target register and OR the result with it. + EmitVectorZeroUpper(Context, Op.Rd); + + EmitLdvecWithUnsignedCast(Context, Op.Rd, Op.Size); + + Type NarrowType = VectorUIntTypesPerSizeLog2[Op.Size]; + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Or), new Type[] { NarrowType, NarrowType })); + } + + EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); } - - for (int Index = 0; Index < Elems; Index++) + else { - EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1); + if (Part != 0) + { + Context.EmitLdvec(Op.Rd); + Context.EmitStvectmp(); + } - EmitVectorInsertTmp(Context, Part + Index, Op.Size); - } + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size + 1); - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); + EmitVectorInsertTmp(Context, Part + Index, Op.Size); + } - if (Part == 0) - { - EmitVectorZeroUpper(Context, Op.Rd); + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + + if (Part == 0) + { + EmitVectorZeroUpper(Context, Op.Rd); + } } } @@ -394,28 +486,64 @@ namespace ChocolArm64.Instruction { AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - int Words = Op.GetBitsCount() >> 4; - int Pairs = Words >> Op.Size; - - int Base = Part != 0 ? Pairs : 0; - - for (int Index = 0; Index < Pairs; Index++) + if (AOptimizations.UseSse2) { - int Idx = Index << 1; + EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); + EmitLdvecWithUnsignedCast(Context, Op.Rm, Op.Size); - EmitVectorExtractZx(Context, Op.Rn, Base + Index, Op.Size); - EmitVectorExtractZx(Context, Op.Rm, Base + Index, Op.Size); + Type[] Types = new Type[] + { + VectorUIntTypesPerSizeLog2[Op.Size], + VectorUIntTypesPerSizeLog2[Op.Size] + }; - EmitVectorInsertTmp(Context, Idx + 1, Op.Size); - EmitVectorInsertTmp(Context, Idx, Op.Size); + string Name = Part == 0 || (Part != 0 && Op.RegisterSize == ARegisterSize.SIMD64) + ? nameof(Sse2.UnpackLow) + : nameof(Sse2.UnpackHigh); + + Context.EmitCall(typeof(Sse2).GetMethod(Name, Types)); + + if (Op.RegisterSize == ARegisterSize.SIMD64 && Part != 0) + { + Context.EmitLdc_I4(8); + + Type[] ShTypes = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], typeof(byte) }; + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical128BitLane), ShTypes)); + } + + EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); + + if (Op.RegisterSize == ARegisterSize.SIMD64 && Part == 0) + { + EmitVectorZeroUpper(Context, Op.Rd); + } } - - Context.EmitLdvectmp(); - Context.EmitStvec(Op.Rd); - - if (Op.RegisterSize == ARegisterSize.SIMD64) + else { - EmitVectorZeroUpper(Context, Op.Rd); + int Words = Op.GetBitsCount() >> 4; + int Pairs = Words >> Op.Size; + + int Base = Part != 0 ? Pairs : 0; + + for (int Index = 0; Index < Pairs; Index++) + { + int Idx = Index << 1; + + EmitVectorExtractZx(Context, Op.Rn, Base + Index, Op.Size); + EmitVectorExtractZx(Context, Op.Rm, Base + Index, Op.Size); + + EmitVectorInsertTmp(Context, Idx + 1, Op.Size); + EmitVectorInsertTmp(Context, Idx, Op.Size); + } + + Context.EmitLdvectmp(); + Context.EmitStvec(Op.Rd); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } } } } diff --git a/ChocolArm64/Instruction/AVectorHelper.cs b/ChocolArm64/Instruction/AVectorHelper.cs index 3e4452abbc..7f9d98cd83 100644 --- a/ChocolArm64/Instruction/AVectorHelper.cs +++ b/ChocolArm64/Instruction/AVectorHelper.cs @@ -227,7 +227,16 @@ namespace ChocolArm64.Instruction [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double VectorExtractDouble(Vector128 Vector, byte Index) { - return BitConverter.Int64BitsToDouble(VectorExtractIntSx(Vector, Index, 3)); + if (Sse41.IsSupported) + { + return BitConverter.Int64BitsToDouble(Sse41.Extract(Sse.StaticCast(Vector), Index)); + } + else if (Sse2.IsSupported) + { + return BitConverter.Int64BitsToDouble((long)VectorExtractIntZx(Vector, Index, 3)); + } + + throw new PlatformNotSupportedException(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -235,41 +244,49 @@ namespace ChocolArm64.Instruction { if (Sse41.IsSupported) { - switch (Size) + if (Size == 0) { - case 0: - return (sbyte)Sse41.Extract(Sse.StaticCast(Vector), Index); - - case 1: - return (short)Sse2.Extract(Sse.StaticCast(Vector), Index); - - case 2: - return Sse41.Extract(Sse.StaticCast(Vector), Index); - - case 3: - return Sse41.Extract(Sse.StaticCast(Vector), Index); + return (sbyte)Sse41.Extract(Sse.StaticCast(Vector), Index); + } + else if (Size == 1) + { + return (short)Sse2.Extract(Sse.StaticCast(Vector), Index); + } + else if (Size == 2) + { + return Sse41.Extract(Sse.StaticCast(Vector), Index); + } + else if (Size == 3) + { + return Sse41.Extract(Sse.StaticCast(Vector), Index); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); } - - throw new ArgumentOutOfRangeException(nameof(Size)); } else if (Sse2.IsSupported) { - switch (Size) + if (Size == 0) { - case 0: - return (sbyte)VectorExtractIntZx(Vector, Index, Size); - - case 1: - return (short)VectorExtractIntZx(Vector, Index, Size); - - case 2: - return (int)VectorExtractIntZx(Vector, Index, Size); - - case 3: - return (long)VectorExtractIntZx(Vector, Index, Size); + return (sbyte)VectorExtractIntZx(Vector, Index, Size); + } + else if (Size == 1) + { + return (short)VectorExtractIntZx(Vector, Index, Size); + } + else if (Size == 2) + { + return (int)VectorExtractIntZx(Vector, Index, Size); + } + else if (Size == 3) + { + return (long)VectorExtractIntZx(Vector, Index, Size); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); } - - throw new ArgumentOutOfRangeException(nameof(Size)); } throw new PlatformNotSupportedException(); @@ -280,22 +297,26 @@ namespace ChocolArm64.Instruction { if (Sse41.IsSupported) { - switch (Size) + if (Size == 0) { - case 0: - return Sse41.Extract(Sse.StaticCast(Vector), Index); - - case 1: - return Sse2.Extract(Sse.StaticCast(Vector), Index); - - case 2: - return Sse41.Extract(Sse.StaticCast(Vector), Index); - - case 3: - return Sse41.Extract(Sse.StaticCast(Vector), Index); + return Sse41.Extract(Sse.StaticCast(Vector), Index); + } + else if (Size == 1) + { + return Sse2.Extract(Sse.StaticCast(Vector), Index); + } + else if (Size == 2) + { + return Sse41.Extract(Sse.StaticCast(Vector), Index); + } + else if (Size == 3) + { + return Sse41.Extract(Sse.StaticCast(Vector), Index); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); } - - throw new ArgumentOutOfRangeException(nameof(Size)); } else if (Sse2.IsSupported) { @@ -305,35 +326,35 @@ namespace ChocolArm64.Instruction ushort Value = Sse2.Extract(Sse.StaticCast(Vector), (byte)ShortIdx); - switch (Size) + if (Size == 0) { - case 0: - return (byte)(Value >> (Index & 1) * 8); - - case 1: - return Value; - - case 2: - case 3: - { - ushort Value1 = Sse2.Extract(Sse.StaticCast(Vector), (byte)(ShortIdx + 1)); - - if (Size == 2) - { - return (uint)(Value | (Value1 << 16)); - } - - ushort Value2 = Sse2.Extract(Sse.StaticCast(Vector), (byte)(ShortIdx + 2)); - ushort Value3 = Sse2.Extract(Sse.StaticCast(Vector), (byte)(ShortIdx + 3)); - - return ((ulong)Value << 0) | - ((ulong)Value1 << 16) | - ((ulong)Value2 << 32) | - ((ulong)Value3 << 48); - } + return (byte)(Value >> (Index & 1) * 8); } + else if (Size == 1) + { + return Value; + } + else if (Size == 2 || Size == 3) + { + ushort Value1 = Sse2.Extract(Sse.StaticCast(Vector), (byte)(ShortIdx + 1)); - throw new ArgumentOutOfRangeException(nameof(Size)); + if (Size == 2) + { + return (uint)(Value | (Value1 << 16)); + } + + ushort Value2 = Sse2.Extract(Sse.StaticCast(Vector), (byte)(ShortIdx + 2)); + ushort Value3 = Sse2.Extract(Sse.StaticCast(Vector), (byte)(ShortIdx + 3)); + + return ((ulong)Value << 0) | + ((ulong)Value1 << 16) | + ((ulong)Value2 << 32) | + ((ulong)Value3 << 48); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } } throw new PlatformNotSupportedException(); @@ -370,22 +391,26 @@ namespace ChocolArm64.Instruction { if (Sse41.IsSupported) { - switch (Size) + if (Size == 0) { - case 0: - return Sse.StaticCast(Sse41.Insert(Sse.StaticCast(Vector), (byte)Value, Index)); - - case 1: - return Sse.StaticCast(Sse2.Insert(Sse.StaticCast(Vector), (ushort)Value, Index)); - - case 2: - return Sse.StaticCast(Sse41.Insert(Sse.StaticCast(Vector), (uint)Value, Index)); - - case 3: - return Sse.StaticCast(Sse41.Insert(Sse.StaticCast(Vector), Value, Index)); + return Sse.StaticCast(Sse41.Insert(Sse.StaticCast(Vector), (byte)Value, Index)); + } + else if (Size == 1) + { + return Sse.StaticCast(Sse2.Insert(Sse.StaticCast(Vector), (ushort)Value, Index)); + } + else if (Size == 2) + { + return Sse.StaticCast(Sse41.Insert(Sse.StaticCast(Vector), (uint)Value, Index)); + } + else if (Size == 3) + { + return Sse.StaticCast(Sse41.Insert(Sse.StaticCast(Vector), Value, Index)); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); } - - throw new ArgumentOutOfRangeException(nameof(Size)); } else if (Sse2.IsSupported) { @@ -395,41 +420,39 @@ namespace ChocolArm64.Instruction ? Index >> 1 : Index << (Size - 1); - switch (Size) + if (Size == 0) { - case 0: - { - ushort ShortVal = Sse2.Extract(Sse.StaticCast(Vector), (byte)ShortIdx); + ushort ShortVal = Sse2.Extract(Sse.StaticCast(Vector), (byte)ShortIdx); - int Shift = (Index & 1) * 8; + int Shift = (Index & 1) * 8; - ShortVal &= (ushort)(0xff00 >> Shift); + ShortVal &= (ushort)(0xff00 >> Shift); - ShortVal |= (ushort)((byte)Value << Shift); + ShortVal |= (ushort)((byte)Value << Shift); - return Sse.StaticCast(Sse2.Insert(ShortVector, ShortVal, (byte)ShortIdx)); - } - - case 1: - return Sse.StaticCast(Sse2.Insert(Sse.StaticCast(Vector), (ushort)Value, Index)); - - case 2: - case 3: - { - ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 0), (byte)(ShortIdx + 0)); - ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 16), (byte)(ShortIdx + 1)); - - if (Size == 3) - { - ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 32), (byte)(ShortIdx + 2)); - ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 48), (byte)(ShortIdx + 3)); - } - - return Sse.StaticCast(ShortVector); - } + return Sse.StaticCast(Sse2.Insert(ShortVector, ShortVal, (byte)ShortIdx)); } + else if (Size == 1) + { + return Sse.StaticCast(Sse2.Insert(Sse.StaticCast(Vector), (ushort)Value, Index)); + } + else if (Size == 2 || Size == 3) + { + ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 0), (byte)(ShortIdx + 0)); + ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 16), (byte)(ShortIdx + 1)); - throw new ArgumentOutOfRangeException(nameof(Size)); + if (Size == 3) + { + ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 32), (byte)(ShortIdx + 2)); + ShortVector = Sse2.Insert(ShortVector, (ushort)(Value >> 48), (byte)(ShortIdx + 3)); + } + + return Sse.StaticCast(ShortVector); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Size)); + } } throw new PlatformNotSupportedException(); @@ -440,7 +463,29 @@ namespace ChocolArm64.Instruction { if (Sse41.IsSupported) { - return Sse41.Insert(Vector, Value, (byte)(Index << 4)); + //Note: The if/else if is necessary to enable the JIT to + //produce a single INSERTPS instruction instead of the + //jump table fallback. + if (Index == 0) + { + return Sse41.Insert(Vector, Value, 0x00); + } + else if (Index == 1) + { + return Sse41.Insert(Vector, Value, 0x10); + } + else if (Index == 2) + { + return Sse41.Insert(Vector, Value, 0x20); + } + else if (Index == 3) + { + return Sse41.Insert(Vector, Value, 0x30); + } + else + { + throw new ArgumentOutOfRangeException(nameof(Index)); + } } else if (Sse2.IsSupported) { @@ -460,6 +505,79 @@ namespace ChocolArm64.Instruction throw new PlatformNotSupportedException(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Sse41VectorInsertScalarSingle(float Value, Vector128 Vector) + { + //Note: 0b1110 is the mask to zero the upper bits. + return Sse41.Insert(Vector, Value, 0b1110); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorSByteZero() + { + if (Sse2.IsSupported) + { + return Sse2.SetZeroVector128(); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorInt16Zero() + { + if (Sse2.IsSupported) + { + return Sse2.SetZeroVector128(); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorInt32Zero() + { + if (Sse2.IsSupported) + { + return Sse2.SetZeroVector128(); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorInt64Zero() + { + if (Sse2.IsSupported) + { + return Sse2.SetZeroVector128(); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorSingleZero() + { + if (Sse.IsSupported) + { + return Sse.SetZeroVector128(); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorDoubleZero() + { + if (Sse2.IsSupported) + { + return Sse2.SetZeroVector128(); + } + + throw new PlatformNotSupportedException(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 VectorZero32_128(Vector128 Vector) { @@ -515,6 +633,50 @@ namespace ChocolArm64.Instruction throw new PlatformNotSupportedException(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorSingleToByte(Vector128 Vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(Vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorSingleToUInt16(Vector128 Vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(Vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorSingleToUInt32(Vector128 Vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(Vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorSingleToUInt64(Vector128 Vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(Vector); + } + + throw new PlatformNotSupportedException(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 VectorSingleToDouble(Vector128 Vector) { @@ -570,6 +732,50 @@ namespace ChocolArm64.Instruction throw new PlatformNotSupportedException(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorByteToSingle(Vector128 Vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(Vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorUInt16ToSingle(Vector128 Vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(Vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorUInt32ToSingle(Vector128 Vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(Vector); + } + + throw new PlatformNotSupportedException(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 VectorUInt64ToSingle(Vector128 Vector) + { + if (Sse.IsSupported) + { + return Sse.StaticCast(Vector); + } + + throw new PlatformNotSupportedException(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 VectorDoubleToSingle(Vector128 Vector) { diff --git a/ChocolArm64/Memory/AMemory.cs b/ChocolArm64/Memory/AMemory.cs index 2cb9b16c26..bb6a2b549c 100644 --- a/ChocolArm64/Memory/AMemory.cs +++ b/ChocolArm64/Memory/AMemory.cs @@ -232,7 +232,7 @@ namespace ChocolArm64.Memory } } - [MethodImpl(MethodImplOptions.NoInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector128 ReadVector32(long Position) { if (Sse.IsSupported) @@ -245,7 +245,7 @@ namespace ChocolArm64.Memory } } - [MethodImpl(MethodImplOptions.NoInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector128 ReadVector64(long Position) { if (Sse2.IsSupported) @@ -365,7 +365,7 @@ namespace ChocolArm64.Memory } } - [MethodImpl(MethodImplOptions.NoInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector32(long Position, Vector128 Value) { if (Sse.IsSupported) @@ -378,7 +378,7 @@ namespace ChocolArm64.Memory } } - [MethodImpl(MethodImplOptions.NoInlining)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void WriteVector64(long Position, Vector128 Value) { if (Sse2.IsSupported) From 0ada047eafc1286ee1306930aabe699460566723 Mon Sep 17 00:00:00 2001 From: emmaus Date: Fri, 28 Sep 2018 10:47:53 +0000 Subject: [PATCH 08/30] fix out of handles error --- Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs index e88ca7e4db..0202bd3084 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs @@ -218,7 +218,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { long XpadId = Context.RequestData.ReadInt64(); - if (Context.Process.HandleTable.GenerateHandle(XpadIdEvent, out XpadIdEventHandle) == 0) + if (Context.Process.HandleTable.GenerateHandle(XpadIdEvent, out XpadIdEventHandle) != KernelResult.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -714,7 +714,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid int NpadId = Context.RequestData.ReadInt32(); long NpadStyleSet = Context.RequestData.ReadInt64(); - if (Context.Process.HandleTable.GenerateHandle(NpadStyleSetUpdateEvent, out int Handle) == 0) + if (Context.Process.HandleTable.GenerateHandle(NpadStyleSetUpdateEvent, out int Handle) != KernelResult.Success) { throw new InvalidOperationException("Out of handles!"); } @@ -1356,7 +1356,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { int PalmaConnectionHandle = Context.RequestData.ReadInt32(); - if (Context.Process.HandleTable.GenerateHandle(PalmaOperationCompleteEvent, out int Handle) == 0) + if (Context.Process.HandleTable.GenerateHandle(PalmaOperationCompleteEvent, out int Handle) != KernelResult.Success) { throw new InvalidOperationException("Out of handles!"); } From bba9bf97d03596b89972cc77390311b9e9472688 Mon Sep 17 00:00:00 2001 From: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com> Date: Sat, 6 Oct 2018 03:45:59 +0200 Subject: [PATCH 09/30] Add 9+7 fast/slow FP inst. impls.; add 14 FP Tests. (#437) * Update CpuTest.cs * Delete CpuTestSimdCmp.cs Obsolete. * Update CpuTestSimdArithmetic.cs Superseded. * Update CpuTestSimd.cs * Update CpuTestSimdReg.cs * Update AInstEmitSimdArithmetic.cs * Update AInstEmitSimdHelper.cs * Update ASoftFloat.cs * Nit. * Update AOpCodeTable.cs * Update AOptimizations.cs * Update AInstEmitSimdArithmetic.cs * Update ASoftFloat.cs * Update CpuTest.cs * Update CpuTestSimd.cs * Update CpuTestSimdReg.cs * Update AOpCodeTable.cs * Update AInstEmitSimdArithmetic.cs * Update ASoftFloat.cs * Update CpuTestSimdReg.cs * Update AOpCodeTable.cs * Update AInstEmitSimdArithmetic.cs * Update ASoftFloat.cs * Update CpuTestSimd.cs * Update CpuTestSimdReg.cs --- ChocolArm64/AOpCodeTable.cs | 4 + ChocolArm64/AOptimizations.cs | 4 +- .../Instruction/AInstEmitSimdArithmetic.cs | 530 +++-- .../Instruction/AInstEmitSimdHelper.cs | 33 +- ChocolArm64/Instruction/ASoftFloat.cs | 1732 ++++++++++++++--- Ryujinx.Tests/Cpu/CpuTest.cs | 189 +- Ryujinx.Tests/Cpu/CpuTestSimd.cs | 188 +- Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs | 152 -- Ryujinx.Tests/Cpu/CpuTestSimdCmp.cs | 407 ---- Ryujinx.Tests/Cpu/CpuTestSimdReg.cs | 394 +++- 10 files changed, 2471 insertions(+), 1162 deletions(-) delete mode 100644 Ryujinx.Tests/Cpu/CpuTestSimdCmp.cs diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs index 6404e14f90..3002571226 100644 --- a/ChocolArm64/AOpCodeTable.cs +++ b/ChocolArm64/AOpCodeTable.cs @@ -301,6 +301,8 @@ namespace ChocolArm64 SetA64("010111111<1011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V, typeof(AOpCodeSimdReg)); SetA64("0x0011111<0011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmulx_V, typeof(AOpCodeSimdReg)); SetA64("000111100x100001010000xxxxxxxxxx", AInstEmit.Fneg_S, typeof(AOpCodeSimd)); SetA64("0>1011101<100000111110xxxxxxxxxx", AInstEmit.Fneg_V, typeof(AOpCodeSimd)); SetA64("000111110x1xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Fnmadd_S, typeof(AOpCodeSimdReg)); @@ -310,6 +312,7 @@ namespace ChocolArm64 SetA64("0>0011101<100001110110xxxxxxxxxx", AInstEmit.Frecpe_V, typeof(AOpCodeSimd)); SetA64("010111100x1xxxxx111111xxxxxxxxxx", AInstEmit.Frecps_S, typeof(AOpCodeSimdReg)); SetA64("0>0011100<1xxxxx111111xxxxxxxxxx", AInstEmit.Frecps_V, typeof(AOpCodeSimdReg)); + SetA64("010111101x100001111110xxxxxxxxxx", AInstEmit.Frecpx_S, typeof(AOpCodeSimd)); SetA64("000111100x100110010000xxxxxxxxxx", AInstEmit.Frinta_S, typeof(AOpCodeSimd)); SetA64("0>1011100<100001100010xxxxxxxxxx", AInstEmit.Frinta_V, typeof(AOpCodeSimd)); SetA64("000111100x100111110000xxxxxxxxxx", AInstEmit.Frinti_S, typeof(AOpCodeSimd)); @@ -327,6 +330,7 @@ namespace ChocolArm64 SetA64("010111101x1xxxxx111111xxxxxxxxxx", AInstEmit.Frsqrts_S, typeof(AOpCodeSimdReg)); SetA64("0>0011101<1xxxxx111111xxxxxxxxxx", AInstEmit.Frsqrts_V, typeof(AOpCodeSimdReg)); SetA64("000111100x100001110000xxxxxxxxxx", AInstEmit.Fsqrt_S, typeof(AOpCodeSimd)); + SetA64("0>1011101<100001111110xxxxxxxxxx", AInstEmit.Fsqrt_V, typeof(AOpCodeSimd)); SetA64("000111100x1xxxxx001110xxxxxxxxxx", AInstEmit.Fsub_S, typeof(AOpCodeSimdReg)); SetA64("0>0011101<1xxxxx110101xxxxxxxxxx", AInstEmit.Fsub_V, typeof(AOpCodeSimdReg)); SetA64("01001110000xxxxx000111xxxxxxxxxx", AInstEmit.Ins_Gp, typeof(AOpCodeSimdIns)); diff --git a/ChocolArm64/AOptimizations.cs b/ChocolArm64/AOptimizations.cs index 40e1674a41..17205489a6 100644 --- a/ChocolArm64/AOptimizations.cs +++ b/ChocolArm64/AOptimizations.cs @@ -2,6 +2,8 @@ using System.Runtime.Intrinsics.X86; public static class AOptimizations { + internal static bool FastFP = true; + private static bool UseAllSseIfAvailable = true; private static bool UseSseIfAvailable = true; @@ -13,4 +15,4 @@ public static class AOptimizations internal static bool UseSse2 = (UseAllSseIfAvailable && UseSse2IfAvailable) && Sse2.IsSupported; internal static bool UseSse41 = (UseAllSseIfAvailable && UseSse41IfAvailable) && Sse41.IsSupported; internal static bool UseSse42 = (UseAllSseIfAvailable && UseSse42IfAvailable) && Sse42.IsSupported; -} \ No newline at end of file +} diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs index 811730fc4a..d11a0b8477 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs @@ -174,25 +174,33 @@ namespace ChocolArm64.Instruction public static void Fadd_S(AILEmitterCtx Context) { - if (AOptimizations.UseSse && AOptimizations.UseSse2) + if (AOptimizations.FastFP && AOptimizations.UseSse + && AOptimizations.UseSse2) { EmitScalarSseOrSse2OpF(Context, nameof(Sse.AddScalar)); } else { - EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Add)); + EmitScalarBinaryOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPAdd)); + }); } } public static void Fadd_V(AILEmitterCtx Context) { - if (AOptimizations.UseSse && AOptimizations.UseSse2) + if (AOptimizations.FastFP && AOptimizations.UseSse + && AOptimizations.UseSse2) { EmitVectorSseOrSse2OpF(Context, nameof(Sse.Add)); } else { - EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Add)); + EmitVectorBinaryOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPAdd)); + }); } } @@ -217,42 +225,50 @@ namespace ChocolArm64.Instruction public static void Fdiv_S(AILEmitterCtx Context) { - if (AOptimizations.UseSse && AOptimizations.UseSse2) + if (AOptimizations.FastFP && AOptimizations.UseSse + && AOptimizations.UseSse2) { EmitScalarSseOrSse2OpF(Context, nameof(Sse.DivideScalar)); } else { - EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Div)); + EmitScalarBinaryOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPDiv)); + }); } } public static void Fdiv_V(AILEmitterCtx Context) { - if (AOptimizations.UseSse && AOptimizations.UseSse2) + if (AOptimizations.FastFP && AOptimizations.UseSse + && AOptimizations.UseSse2) { EmitVectorSseOrSse2OpF(Context, nameof(Sse.Divide)); } else { - EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Div)); + EmitVectorBinaryOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPDiv)); + }); } } public static void Fmadd_S(AILEmitterCtx Context) { - if (AOptimizations.UseSse2) + if (AOptimizations.FastFP && AOptimizations.UseSse2) { AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; if (Op.Size == 0) { + Type[] Types = new Type[] { typeof(Vector128), typeof(Vector128) }; + Context.EmitLdvec(Op.Ra); Context.EmitLdvec(Op.Rn); Context.EmitLdvec(Op.Rm); - Type[] Types = new Type[] { typeof(Vector128), typeof(Vector128) }; - Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), Types)); Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.AddScalar), Types)); @@ -262,12 +278,12 @@ namespace ChocolArm64.Instruction } else /* if (Op.Size == 1) */ { + Type[] Types = new Type[] { typeof(Vector128), typeof(Vector128) }; + EmitLdvecWithCastToDouble(Context, Op.Ra); EmitLdvecWithCastToDouble(Context, Op.Rn); EmitLdvecWithCastToDouble(Context, Op.Rm); - Type[] Types = new Type[] { typeof(Vector128), typeof(Vector128) }; - Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), Types)); Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.AddScalar), Types)); @@ -280,33 +296,48 @@ namespace ChocolArm64.Instruction { EmitScalarTernaryRaOpF(Context, () => { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Add); + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMulAdd)); }); } } public static void Fmax_S(AILEmitterCtx Context) { - EmitScalarBinaryOpF(Context, () => + if (AOptimizations.FastFP && AOptimizations.UseSse + && AOptimizations.UseSse2) { - EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.Max)); - }); + EmitScalarSseOrSse2OpF(Context, nameof(Sse.MaxScalar)); + } + else + { + EmitScalarBinaryOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMax)); + }); + } } public static void Fmax_V(AILEmitterCtx Context) { - EmitVectorBinaryOpF(Context, () => + if (AOptimizations.FastFP && AOptimizations.UseSse + && AOptimizations.UseSse2) { - EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.Max)); - }); + EmitVectorSseOrSse2OpF(Context, nameof(Sse.Max)); + } + else + { + EmitVectorBinaryOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMax)); + }); + } } public static void Fmaxnm_S(AILEmitterCtx Context) { EmitScalarBinaryOpF(Context, () => { - EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.MaxNum)); + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMaxNum)); }); } @@ -314,36 +345,55 @@ namespace ChocolArm64.Instruction { EmitVectorBinaryOpF(Context, () => { - EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.MaxNum)); + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMaxNum)); }); } public static void Fmaxp_V(AILEmitterCtx Context) { - EmitVectorPairwiseOpF(Context, () => EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.Max))); + EmitVectorPairwiseOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMax)); + }); } public static void Fmin_S(AILEmitterCtx Context) { - EmitScalarBinaryOpF(Context, () => + if (AOptimizations.FastFP && AOptimizations.UseSse + && AOptimizations.UseSse2) { - EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.Min)); - }); + EmitScalarSseOrSse2OpF(Context, nameof(Sse.MinScalar)); + } + else + { + EmitScalarBinaryOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMin)); + }); + } } public static void Fmin_V(AILEmitterCtx Context) { - EmitVectorBinaryOpF(Context, () => + if (AOptimizations.FastFP && AOptimizations.UseSse + && AOptimizations.UseSse2) { - EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.Min)); - }); + EmitVectorSseOrSse2OpF(Context, nameof(Sse.Min)); + } + else + { + EmitVectorBinaryOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMin)); + }); + } } public static void Fminnm_S(AILEmitterCtx Context) { EmitScalarBinaryOpF(Context, () => { - EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.MinNum)); + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMinNum)); }); } @@ -351,13 +401,16 @@ namespace ChocolArm64.Instruction { EmitVectorBinaryOpF(Context, () => { - EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.MinNum)); + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMinNum)); }); } public static void Fminp_V(AILEmitterCtx Context) { - EmitVectorPairwiseOpF(Context, () => EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.Min))); + EmitVectorPairwiseOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMin)); + }); } public static void Fmla_Se(AILEmitterCtx Context) @@ -407,22 +460,63 @@ namespace ChocolArm64.Instruction public static void Fmsub_S(AILEmitterCtx Context) { - EmitScalarTernaryRaOpF(Context, () => + if (AOptimizations.FastFP && AOptimizations.UseSse2) { - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Sub); - }); + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + if (Op.Size == 0) + { + Type[] Types = new Type[] { typeof(Vector128), typeof(Vector128) }; + + Context.EmitLdvec(Op.Ra); + Context.EmitLdvec(Op.Rn); + Context.EmitLdvec(Op.Rm); + + Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), Types)); + Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SubtractScalar), Types)); + + Context.EmitStvec(Op.Rd); + + EmitVectorZero32_128(Context, Op.Rd); + } + else /* if (Op.Size == 1) */ + { + Type[] Types = new Type[] { typeof(Vector128), typeof(Vector128) }; + + EmitLdvecWithCastToDouble(Context, Op.Ra); + EmitLdvecWithCastToDouble(Context, Op.Rn); + EmitLdvecWithCastToDouble(Context, Op.Rm); + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), Types)); + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SubtractScalar), Types)); + + EmitStvecWithCastFromDouble(Context, Op.Rd); + + EmitVectorZeroUpper(Context, Op.Rd); + } + } + else + { + EmitScalarTernaryRaOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMulSub)); + }); + } } public static void Fmul_S(AILEmitterCtx Context) { - if (AOptimizations.UseSse && AOptimizations.UseSse2) + if (AOptimizations.FastFP && AOptimizations.UseSse + && AOptimizations.UseSse2) { EmitScalarSseOrSse2OpF(Context, nameof(Sse.MultiplyScalar)); } else { - EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Mul)); + EmitScalarBinaryOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMul)); + }); } } @@ -433,13 +527,17 @@ namespace ChocolArm64.Instruction public static void Fmul_V(AILEmitterCtx Context) { - if (AOptimizations.UseSse && AOptimizations.UseSse2) + if (AOptimizations.FastFP && AOptimizations.UseSse + && AOptimizations.UseSse2) { EmitVectorSseOrSse2OpF(Context, nameof(Sse.Multiply)); } else { - EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Mul)); + EmitVectorBinaryOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMul)); + }); } } @@ -448,6 +546,22 @@ namespace ChocolArm64.Instruction EmitVectorBinaryOpByElemF(Context, () => Context.Emit(OpCodes.Mul)); } + public static void Fmulx_S(AILEmitterCtx Context) + { + EmitScalarBinaryOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMulX)); + }); + } + + public static void Fmulx_V(AILEmitterCtx Context) + { + EmitVectorBinaryOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMulX)); + }); + } + public static void Fneg_S(AILEmitterCtx Context) { EmitScalarUnaryOpF(Context, () => Context.Emit(OpCodes.Neg)); @@ -524,17 +638,122 @@ namespace ChocolArm64.Instruction public static void Frecps_S(AILEmitterCtx Context) { - EmitScalarBinaryOpF(Context, () => + if (AOptimizations.FastFP && AOptimizations.UseSse2) { - EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.RecipStep)); - }); + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int SizeF = Op.Size & 1; + + if (SizeF == 0) + { + Type[] Types = new Type[] { typeof(float) }; + + Context.EmitLdc_R4(2f); + Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetScalarVector128), Types)); + + Types = new Type[] { typeof(Vector128), typeof(Vector128) }; + + Context.EmitLdvec(Op.Rn); + Context.EmitLdvec(Op.Rm); + + Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), Types)); + Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SubtractScalar), Types)); + + Context.EmitStvec(Op.Rd); + + EmitVectorZero32_128(Context, Op.Rd); + } + else /* if (SizeF == 1) */ + { + Type[] Types = new Type[] { typeof(double) }; + + Context.EmitLdc_R8(2d); + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetScalarVector128), Types)); + + Types = new Type[] { typeof(Vector128), typeof(Vector128) }; + + EmitLdvecWithCastToDouble(Context, Op.Rn); + EmitLdvecWithCastToDouble(Context, Op.Rm); + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), Types)); + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SubtractScalar), Types)); + + EmitStvecWithCastFromDouble(Context, Op.Rd); + + EmitVectorZeroUpper(Context, Op.Rd); + } + } + else + { + EmitScalarBinaryOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPRecipStepFused)); + }); + } } public static void Frecps_V(AILEmitterCtx Context) { - EmitVectorBinaryOpF(Context, () => + if (AOptimizations.FastFP && AOptimizations.UseSse2) { - EmitBinarySoftFloatCall(Context, nameof(ASoftFloat.RecipStep)); + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int SizeF = Op.Size & 1; + + if (SizeF == 0) + { + Type[] Types = new Type[] { typeof(float) }; + + Context.EmitLdc_R4(2f); + Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetAllVector128), Types)); + + Types = new Type[] { typeof(Vector128), typeof(Vector128) }; + + Context.EmitLdvec(Op.Rn); + Context.EmitLdvec(Op.Rm); + + Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), Types)); + Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Subtract), Types)); + + Context.EmitStvec(Op.Rd); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + else /* if (SizeF == 1) */ + { + Type[] Types = new Type[] { typeof(double) }; + + Context.EmitLdc_R8(2d); + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), Types)); + + Types = new Type[] { typeof(Vector128), typeof(Vector128) }; + + EmitLdvecWithCastToDouble(Context, Op.Rn); + EmitLdvecWithCastToDouble(Context, Op.Rm); + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), Types)); + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), Types)); + + EmitStvecWithCastFromDouble(Context, Op.Rd); + } + } + else + { + EmitVectorBinaryOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPRecipStepFused)); + }); + } + } + + public static void Frecpx_S(AILEmitterCtx Context) + { + EmitScalarUnaryOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPRecpX)); }); } @@ -728,97 +947,194 @@ namespace ChocolArm64.Instruction public static void Frsqrts_S(AILEmitterCtx Context) { - EmitFrsqrts(Context, 0, Scalar: true); + if (AOptimizations.FastFP && AOptimizations.UseSse2) + { + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; + + int SizeF = Op.Size & 1; + + if (SizeF == 0) + { + Type[] Types = new Type[] { typeof(float) }; + + Context.EmitLdc_R4(0.5f); + Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetScalarVector128), Types)); + + Context.EmitLdc_R4(3f); + Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetScalarVector128), Types)); + + Types = new Type[] { typeof(Vector128), typeof(Vector128) }; + + Context.EmitLdvec(Op.Rn); + Context.EmitLdvec(Op.Rm); + + Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), Types)); + Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SubtractScalar), Types)); + Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.MultiplyScalar), Types)); + + Context.EmitStvec(Op.Rd); + + EmitVectorZero32_128(Context, Op.Rd); + } + else /* if (SizeF == 1) */ + { + Type[] Types = new Type[] { typeof(double) }; + + Context.EmitLdc_R8(0.5d); + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetScalarVector128), Types)); + + Context.EmitLdc_R8(3d); + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetScalarVector128), Types)); + + Types = new Type[] { typeof(Vector128), typeof(Vector128) }; + + EmitLdvecWithCastToDouble(Context, Op.Rn); + EmitLdvecWithCastToDouble(Context, Op.Rm); + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), Types)); + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SubtractScalar), Types)); + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.MultiplyScalar), Types)); + + EmitStvecWithCastFromDouble(Context, Op.Rd); + + EmitVectorZeroUpper(Context, Op.Rd); + } + } + else + { + EmitScalarBinaryOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPRSqrtStepFused)); + }); + } } public static void Frsqrts_V(AILEmitterCtx Context) { - AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; - - int SizeF = Op.Size & 1; - - int Bytes = Op.GetBitsCount() >> 3; - - for (int Index = 0; Index < Bytes >> SizeF + 2; Index++) + if (AOptimizations.FastFP && AOptimizations.UseSse2) { - EmitFrsqrts(Context, Index, Scalar: false); - } + AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - if (Op.RegisterSize == ARegisterSize.SIMD64) + int SizeF = Op.Size & 1; + + if (SizeF == 0) + { + Type[] Types = new Type[] { typeof(float) }; + + Context.EmitLdc_R4(0.5f); + Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetAllVector128), Types)); + + Context.EmitLdc_R4(3f); + Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.SetAllVector128), Types)); + + Types = new Type[] { typeof(Vector128), typeof(Vector128) }; + + Context.EmitLdvec(Op.Rn); + Context.EmitLdvec(Op.Rm); + + Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), Types)); + Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Subtract), Types)); + Context.EmitCall(typeof(Sse).GetMethod(nameof(Sse.Multiply), Types)); + + Context.EmitStvec(Op.Rd); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + else /* if (SizeF == 1) */ + { + Type[] Types = new Type[] { typeof(double) }; + + Context.EmitLdc_R8(0.5d); + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), Types)); + + Context.EmitLdc_R8(3d); + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.SetAllVector128), Types)); + + Types = new Type[] { typeof(Vector128), typeof(Vector128) }; + + EmitLdvecWithCastToDouble(Context, Op.Rn); + EmitLdvecWithCastToDouble(Context, Op.Rm); + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), Types)); + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Subtract), Types)); + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Multiply), Types)); + + EmitStvecWithCastFromDouble(Context, Op.Rd); + } + } + else { - EmitVectorZeroUpper(Context, Op.Rd); + EmitVectorBinaryOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPRSqrtStepFused)); + }); } } - private static void EmitFrsqrts(AILEmitterCtx Context, int Index, bool Scalar) - { - AOpCodeSimdReg Op = (AOpCodeSimdReg)Context.CurrOp; - - int SizeF = Op.Size & 1; - - if (SizeF == 0) - { - Context.EmitLdc_R4(3); - } - else /* if (SizeF == 1) */ - { - Context.EmitLdc_R8(3); - } - - EmitVectorExtractF(Context, Op.Rn, Index, SizeF); - EmitVectorExtractF(Context, Op.Rm, Index, SizeF); - - Context.Emit(OpCodes.Mul); - Context.Emit(OpCodes.Sub); - - if (SizeF == 0) - { - Context.EmitLdc_R4(0.5f); - } - else /* if (SizeF == 1) */ - { - Context.EmitLdc_R8(0.5); - } - - Context.Emit(OpCodes.Mul); - - if (Scalar) - { - EmitVectorZeroAll(Context, Op.Rd); - } - - EmitVectorInsertF(Context, Op.Rd, Index, SizeF); - } - public static void Fsqrt_S(AILEmitterCtx Context) { - EmitScalarUnaryOpF(Context, () => + if (AOptimizations.FastFP && AOptimizations.UseSse + && AOptimizations.UseSse2) { - EmitUnaryMathCall(Context, nameof(Math.Sqrt)); - }); + EmitScalarSseOrSse2OpF(Context, nameof(Sse.SqrtScalar)); + } + else + { + EmitScalarUnaryOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPSqrt)); + }); + } + } + + public static void Fsqrt_V(AILEmitterCtx Context) + { + if (AOptimizations.FastFP && AOptimizations.UseSse + && AOptimizations.UseSse2) + { + EmitVectorSseOrSse2OpF(Context, nameof(Sse.Sqrt)); + } + else + { + EmitVectorUnaryOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPSqrt)); + }); + } } public static void Fsub_S(AILEmitterCtx Context) { - if (AOptimizations.UseSse && AOptimizations.UseSse2) + if (AOptimizations.FastFP && AOptimizations.UseSse + && AOptimizations.UseSse2) { EmitScalarSseOrSse2OpF(Context, nameof(Sse.SubtractScalar)); } else { - EmitScalarBinaryOpF(Context, () => Context.Emit(OpCodes.Sub)); + EmitScalarBinaryOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPSub)); + }); } } public static void Fsub_V(AILEmitterCtx Context) { - if (AOptimizations.UseSse && AOptimizations.UseSse2) + if (AOptimizations.FastFP && AOptimizations.UseSse + && AOptimizations.UseSse2) { EmitVectorSseOrSse2OpF(Context, nameof(Sse.Subtract)); } else { - EmitVectorBinaryOpF(Context, () => Context.Emit(OpCodes.Sub)); + EmitVectorBinaryOpF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPSub)); + }); } } @@ -1170,7 +1486,6 @@ namespace ChocolArm64.Instruction EmitVectorTernaryOpZx(Context, () => { Context.Emit(OpCodes.Sub); - EmitAbs(Context); Context.Emit(OpCodes.Add); @@ -1182,7 +1497,6 @@ namespace ChocolArm64.Instruction EmitVectorWidenRnRmTernaryOpZx(Context, () => { Context.Emit(OpCodes.Sub); - EmitAbs(Context); Context.Emit(OpCodes.Add); @@ -1194,7 +1508,6 @@ namespace ChocolArm64.Instruction EmitVectorBinaryOpZx(Context, () => { Context.Emit(OpCodes.Sub); - EmitAbs(Context); }); } @@ -1204,7 +1517,6 @@ namespace ChocolArm64.Instruction EmitVectorWidenRnRmBinaryOpZx(Context, () => { Context.Emit(OpCodes.Sub); - EmitAbs(Context); }); } diff --git a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs index 381fc46ac9..dd39f52d50 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdHelper.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdHelper.cs @@ -306,25 +306,19 @@ namespace ChocolArm64.Instruction int SizeF = Op.Size & 1; - Context.EmitLdc_I4((int)RoundMode); - MethodInfo MthdInfo; - Type[] Types = new Type[] { null, typeof(MidpointRounding) }; - - Types[0] = SizeF == 0 - ? typeof(float) - : typeof(double); - if (SizeF == 0) { - MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Round), Types); + MthdInfo = typeof(MathF).GetMethod(nameof(MathF.Round), new Type[] { typeof(float), typeof(MidpointRounding) }); } else /* if (SizeF == 1) */ { - MthdInfo = typeof(Math).GetMethod(nameof(Math.Round), Types); + MthdInfo = typeof(Math).GetMethod(nameof(Math.Round), new Type[] { typeof(double), typeof(MidpointRounding) }); } + Context.EmitLdc_I4((int)RoundMode); + Context.EmitCall(MthdInfo); } @@ -348,24 +342,17 @@ namespace ChocolArm64.Instruction Context.EmitCall(MthdInfo); } - public static void EmitBinarySoftFloatCall(AILEmitterCtx Context, string Name) + public static void EmitSoftFloatCall(AILEmitterCtx Context, string Name) { IAOpCodeSimd Op = (IAOpCodeSimd)Context.CurrOp; - int SizeF = Op.Size & 1; + Type Type = (Op.Size & 1) == 0 + ? typeof(ASoftFloat_32) + : typeof(ASoftFloat_64); - MethodInfo MthdInfo; + Context.EmitLdarg(ATranslatedSub.StateArgIdx); - if (SizeF == 0) - { - MthdInfo = typeof(ASoftFloat).GetMethod(Name, new Type[] { typeof(float), typeof(float) }); - } - else /* if (SizeF == 1) */ - { - MthdInfo = typeof(ASoftFloat).GetMethod(Name, new Type[] { typeof(double), typeof(double) }); - } - - Context.EmitCall(MthdInfo); + Context.EmitCall(Type, Name); } public static void EmitScalarBinaryOpByElemF(AILEmitterCtx Context, Action Emit) diff --git a/ChocolArm64/Instruction/ASoftFloat.cs b/ChocolArm64/Instruction/ASoftFloat.cs index e3f067ed50..7412c976fd 100644 --- a/ChocolArm64/Instruction/ASoftFloat.cs +++ b/ChocolArm64/Instruction/ASoftFloat.cs @@ -1,4 +1,7 @@ +using ChocolArm64.State; using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; namespace ChocolArm64.Instruction { @@ -6,13 +9,29 @@ namespace ChocolArm64.Instruction { static ASoftFloat() { + RecipEstimateTable = BuildRecipEstimateTable(); InvSqrtEstimateTable = BuildInvSqrtEstimateTable(); - RecipEstimateTable = BuildRecipEstimateTable(); } private static readonly byte[] RecipEstimateTable; private static readonly byte[] InvSqrtEstimateTable; + private static byte[] BuildRecipEstimateTable() + { + byte[] Table = new byte[256]; + for (ulong index = 0; index < 256; index++) + { + ulong a = index | 0x100; + + a = (a << 1) + 1; + ulong b = 0x80000 / a; + b = (b + 1) >> 1; + + Table[index] = (byte)(b & 0xFF); + } + return Table; + } + private static byte[] BuildInvSqrtEstimateTable() { byte[] Table = new byte[512]; @@ -40,22 +59,75 @@ namespace ChocolArm64.Instruction return Table; } - private static byte[] BuildRecipEstimateTable() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float RecipEstimate(float x) { - byte[] Table = new byte[256]; - for (ulong index = 0; index < 256; index++) - { - ulong a = index | 0x100; - - a = (a << 1) + 1; - ulong b = 0x80000 / a; - b = (b + 1) >> 1; - - Table[index] = (byte)(b & 0xFF); - } - return Table; + return (float)RecipEstimate((double)x); } + public static double RecipEstimate(double x) + { + ulong x_bits = (ulong)BitConverter.DoubleToInt64Bits(x); + ulong x_sign = x_bits & 0x8000000000000000; + ulong x_exp = (x_bits >> 52) & 0x7FF; + ulong scaled = x_bits & ((1ul << 52) - 1); + + if (x_exp >= 2045) + { + if (x_exp == 0x7ff && scaled != 0) + { + // NaN + return BitConverter.Int64BitsToDouble((long)(x_bits | 0x0008000000000000)); + } + + // Infinity, or Out of range -> Zero + return BitConverter.Int64BitsToDouble((long)x_sign); + } + + if (x_exp == 0) + { + if (scaled == 0) + { + // Zero -> Infinity + return BitConverter.Int64BitsToDouble((long)(x_sign | 0x7FF0000000000000)); + } + + // Denormal + if ((scaled & (1ul << 51)) == 0) + { + x_exp = ~0ul; + scaled <<= 2; + } + else + { + scaled <<= 1; + } + } + + scaled >>= 44; + scaled &= 0xFF; + + ulong result_exp = (2045 - x_exp) & 0x7FF; + ulong estimate = (ulong)RecipEstimateTable[scaled]; + ulong fraction = estimate << 44; + + if (result_exp == 0) + { + fraction >>= 1; + fraction |= 1ul << 51; + } + else if (result_exp == 0x7FF) + { + result_exp = 0; + fraction >>= 2; + fraction |= 1ul << 50; + } + + ulong result = x_sign | (result_exp << 52) | fraction; + return BitConverter.Int64BitsToDouble((long)result); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static float InvSqrtEstimate(float x) { return (float)InvSqrtEstimate((double)x); @@ -124,108 +196,6 @@ namespace ChocolArm64.Instruction return BitConverter.Int64BitsToDouble((long)result); } - public static float RecipEstimate(float x) - { - return (float)RecipEstimate((double)x); - } - - public static double RecipEstimate(double x) - { - ulong x_bits = (ulong)BitConverter.DoubleToInt64Bits(x); - ulong x_sign = x_bits & 0x8000000000000000; - ulong x_exp = (x_bits >> 52) & 0x7FF; - ulong scaled = x_bits & ((1ul << 52) - 1); - - if (x_exp >= 2045) - { - if (x_exp == 0x7ff && scaled != 0) - { - // NaN - return BitConverter.Int64BitsToDouble((long)(x_bits | 0x0008000000000000)); - } - - // Infinity, or Out of range -> Zero - return BitConverter.Int64BitsToDouble((long)x_sign); - } - - if (x_exp == 0) - { - if (scaled == 0) - { - // Zero -> Infinity - return BitConverter.Int64BitsToDouble((long)(x_sign | 0x7FF0000000000000)); - } - - // Denormal - if ((scaled & (1ul << 51)) == 0) - { - x_exp = ~0ul; - scaled <<= 2; - } - else - { - scaled <<= 1; - } - } - - scaled >>= 44; - scaled &= 0xFF; - - ulong result_exp = (2045 - x_exp) & 0x7FF; - ulong estimate = (ulong)RecipEstimateTable[scaled]; - ulong fraction = estimate << 44; - - if (result_exp == 0) - { - fraction >>= 1; - fraction |= 1ul << 51; - } - else if (result_exp == 0x7FF) - { - result_exp = 0; - fraction >>= 2; - fraction |= 1ul << 50; - } - - ulong result = x_sign | (result_exp << 52) | fraction; - return BitConverter.Int64BitsToDouble((long)result); - } - - public static float RecipStep(float op1, float op2) - { - return (float)RecipStep((double)op1, (double)op2); - } - - public static double RecipStep(double op1, double op2) - { - op1 = -op1; - - ulong op1_bits = (ulong)BitConverter.DoubleToInt64Bits(op1); - ulong op2_bits = (ulong)BitConverter.DoubleToInt64Bits(op2); - - ulong op1_sign = op1_bits & 0x8000000000000000; - ulong op2_sign = op2_bits & 0x8000000000000000; - ulong op1_other = op1_bits & 0x7FFFFFFFFFFFFFFF; - ulong op2_other = op2_bits & 0x7FFFFFFFFFFFFFFF; - - bool inf1 = op1_other == 0x7FF0000000000000; - bool inf2 = op2_other == 0x7FF0000000000000; - bool zero1 = op1_other == 0; - bool zero2 = op2_other == 0; - - if ((inf1 && zero2) || (zero1 && inf2)) - { - return 2.0; - } - else if (inf1 || inf2) - { - // Infinity - return BitConverter.Int64BitsToDouble((long)(0x7FF0000000000000 | (op1_sign ^ op2_sign))); - } - - return 2.0 + op1 * op2; - } - public static float ConvertHalfToSingle(ushort x) { uint x_sign = (uint)(x >> 15) & 0x0001; @@ -261,277 +231,1457 @@ namespace ChocolArm64.Instruction uint new_exp = (uint)((exponent + 127) & 0xFF) << 23; return BitConverter.Int32BitsToSingle((int)((x_sign << 31) | new_exp | (x_mantissa << 13))); } + } - public static float MaxNum(float op1, float op2) + static class ASoftFloat_32 + { + public static float FPAdd(float Value1, float Value2, AThreadState State) { - uint op1_bits = (uint)BitConverter.SingleToInt32Bits(op1); - uint op2_bits = (uint)BitConverter.SingleToInt32Bits(op2); + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPAdd: State.Fpcr = 0x{State.Fpcr:X8}"); - if (IsQNaN(op1_bits) && !IsQNaN(op2_bits)) + Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); + Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); + + float Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); + + if (!Done) { - op1 = float.NegativeInfinity; - } - else if (!IsQNaN(op1_bits) && IsQNaN(op2_bits)) - { - op2 = float.NegativeInfinity; + bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; + bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; + + if (Inf1 && Inf2 && Sign1 == !Sign2) + { + Result = FPDefaultNaN(); + + FPProcessException(FPExc.InvalidOp, State); + } + else if ((Inf1 && !Sign1) || (Inf2 && !Sign2)) + { + Result = FPInfinity(false); + } + else if ((Inf1 && Sign1) || (Inf2 && Sign2)) + { + Result = FPInfinity(true); + } + else if (Zero1 && Zero2 && Sign1 == Sign2) + { + Result = FPZero(Sign1); + } + else + { + Result = Value1 + Value2; + } } - return Max(op1, op2); + return Result; } - public static double MaxNum(double op1, double op2) + public static float FPDiv(float Value1, float Value2, AThreadState State) { - ulong op1_bits = (ulong)BitConverter.DoubleToInt64Bits(op1); - ulong op2_bits = (ulong)BitConverter.DoubleToInt64Bits(op2); + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPDiv: State.Fpcr = 0x{State.Fpcr:X8}"); - if (IsQNaN(op1_bits) && !IsQNaN(op2_bits)) + Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); + Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); + + float Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); + + if (!Done) { - op1 = double.NegativeInfinity; - } - else if (!IsQNaN(op1_bits) && IsQNaN(op2_bits)) - { - op2 = double.NegativeInfinity; + bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; + bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; + + if ((Inf1 && Inf2) || (Zero1 && Zero2)) + { + Result = FPDefaultNaN(); + + FPProcessException(FPExc.InvalidOp, State); + } + else if (Inf1 || Zero2) + { + Result = FPInfinity(Sign1 ^ Sign2); + + if (!Inf1) FPProcessException(FPExc.DivideByZero, State); + } + else if (Zero1 || Inf2) + { + Result = FPZero(Sign1 ^ Sign2); + } + else + { + Result = Value1 / Value2; + } } - return Max(op1, op2); + return Result; } - public static float Max(float op1, float op2) + public static float FPMax(float Value1, float Value2, AThreadState State) { - // Fast path - if (op1 > op2) + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPMax: State.Fpcr = 0x{State.Fpcr:X8}"); + + Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); + Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); + + float Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); + + if (!Done) { - return op1; + if (Value1 > Value2) + { + if (Type1 == FPType.Infinity) + { + Result = FPInfinity(Sign1); + } + else if (Type1 == FPType.Zero) + { + Result = FPZero(Sign1 && Sign2); + } + else + { + Result = Value1; + } + } + else + { + if (Type2 == FPType.Infinity) + { + Result = FPInfinity(Sign2); + } + else if (Type2 == FPType.Zero) + { + Result = FPZero(Sign1 && Sign2); + } + else + { + Result = Value2; + } + } } - if (op1 < op2 || (op1 == op2 && op2 != 0)) - { - return op2; - } - - uint op1_bits = (uint)BitConverter.SingleToInt32Bits(op1); - uint op2_bits = (uint)BitConverter.SingleToInt32Bits(op2); - - // Handle NaN cases - if (ProcessNaNs(op1_bits, op2_bits, out uint op_bits)) - { - return BitConverter.Int32BitsToSingle((int)op_bits); - } - - // Return the most positive zero - if ((op1_bits & op2_bits) == 0x80000000u) - { - return BitConverter.Int32BitsToSingle(int.MinValue); - } - - return 0; + return Result; } - public static double Max(double op1, double op2) + public static float FPMaxNum(float Value1, float Value2, AThreadState State) { - // Fast path - if (op1 > op2) + Debug.WriteIf(State.Fpcr != 0, "ASoftFloat_32.FPMaxNum: "); + + Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); + Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); + + if (Type1 == FPType.QNaN && Type2 != FPType.QNaN) { - return op1; + Value1 = FPInfinity(true); + } + else if (Type1 != FPType.QNaN && Type2 == FPType.QNaN) + { + Value2 = FPInfinity(true); } - if (op1 < op2 || (op1 == op2 && op2 != 0)) - { - return op2; - } - - ulong op1_bits = (ulong)BitConverter.DoubleToInt64Bits(op1); - ulong op2_bits = (ulong)BitConverter.DoubleToInt64Bits(op2); - - // Handle NaN cases - if (ProcessNaNs(op1_bits, op2_bits, out ulong op_bits)) - { - return BitConverter.Int64BitsToDouble((long)op_bits); - } - - // Return the most positive zero - if ((op1_bits & op2_bits) == 0x8000000000000000ul) - { - return BitConverter.Int64BitsToDouble(long.MinValue); - } - - return 0; + return FPMax(Value1, Value2, State); } - public static float MinNum(float op1, float op2) + public static float FPMin(float Value1, float Value2, AThreadState State) { - uint op1_bits = (uint)BitConverter.SingleToInt32Bits(op1); - uint op2_bits = (uint)BitConverter.SingleToInt32Bits(op2); + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPMin: State.Fpcr = 0x{State.Fpcr:X8}"); - if (IsQNaN(op1_bits) && !IsQNaN(op2_bits)) + Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); + Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); + + float Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); + + if (!Done) { - op1 = float.PositiveInfinity; - } - else if (!IsQNaN(op1_bits) && IsQNaN(op2_bits)) - { - op2 = float.PositiveInfinity; + if (Value1 < Value2) + { + if (Type1 == FPType.Infinity) + { + Result = FPInfinity(Sign1); + } + else if (Type1 == FPType.Zero) + { + Result = FPZero(Sign1 || Sign2); + } + else + { + Result = Value1; + } + } + else + { + if (Type2 == FPType.Infinity) + { + Result = FPInfinity(Sign2); + } + else if (Type2 == FPType.Zero) + { + Result = FPZero(Sign1 || Sign2); + } + else + { + Result = Value2; + } + } } - return Min(op1, op2); + return Result; } - public static double MinNum(double op1, double op2) + public static float FPMinNum(float Value1, float Value2, AThreadState State) { - ulong op1_bits = (ulong)BitConverter.DoubleToInt64Bits(op1); - ulong op2_bits = (ulong)BitConverter.DoubleToInt64Bits(op2); + Debug.WriteIf(State.Fpcr != 0, "ASoftFloat_32.FPMinNum: "); - if (IsQNaN(op1_bits) && !IsQNaN(op2_bits)) + Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); + Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); + + if (Type1 == FPType.QNaN && Type2 != FPType.QNaN) { - op1 = double.PositiveInfinity; + Value1 = FPInfinity(false); } - else if (!IsQNaN(op1_bits) && IsQNaN(op2_bits)) + else if (Type1 != FPType.QNaN && Type2 == FPType.QNaN) { - op2 = double.PositiveInfinity; + Value2 = FPInfinity(false); } - return Min(op1, op2); + return FPMin(Value1, Value2, State); } - public static float Min(float op1, float op2) + public static float FPMul(float Value1, float Value2, AThreadState State) { - // Fast path - if (op1 < op2) + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPMul: State.Fpcr = 0x{State.Fpcr:X8}"); + + Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); + Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); + + float Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); + + if (!Done) { - return op1; + bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; + bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; + + if ((Inf1 && Zero2) || (Zero1 && Inf2)) + { + Result = FPDefaultNaN(); + + FPProcessException(FPExc.InvalidOp, State); + } + else if (Inf1 || Inf2) + { + Result = FPInfinity(Sign1 ^ Sign2); + } + else if (Zero1 || Zero2) + { + Result = FPZero(Sign1 ^ Sign2); + } + else + { + Result = Value1 * Value2; + } } - if (op1 > op2 || (op1 == op2 && op2 != 0)) - { - return op2; - } - - uint op1_bits = (uint)BitConverter.SingleToInt32Bits(op1); - uint op2_bits = (uint)BitConverter.SingleToInt32Bits(op2); - - // Handle NaN cases - if (ProcessNaNs(op1_bits, op2_bits, out uint op_bits)) - { - return BitConverter.Int32BitsToSingle((int)op_bits); - } - - // Return the most negative zero - if ((op1_bits | op2_bits) == 0x80000000u) - { - return BitConverter.Int32BitsToSingle(int.MinValue); - } - - return 0; + return Result; } - public static double Min(double op1, double op2) + public static float FPMulAdd(float ValueA, float Value1, float Value2, AThreadState State) { - // Fast path - if (op1 < op2) + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPMulAdd: State.Fpcr = 0x{State.Fpcr:X8}"); + + ValueA = ValueA.FPUnpack(out FPType TypeA, out bool SignA, out uint Addend); + Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); + Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); + + bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; + bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; + + float Result = FPProcessNaNs3(TypeA, Type1, Type2, Addend, Op1, Op2, State, out bool Done); + + if (TypeA == FPType.QNaN && ((Inf1 && Zero2) || (Zero1 && Inf2))) { - return op1; + Result = FPDefaultNaN(); + + FPProcessException(FPExc.InvalidOp, State); } - if (op1 > op2 || (op1 == op2 && op2 != 0)) + if (!Done) { - return op2; + bool InfA = TypeA == FPType.Infinity; bool ZeroA = TypeA == FPType.Zero; + + bool SignP = Sign1 ^ Sign2; + bool InfP = Inf1 || Inf2; + bool ZeroP = Zero1 || Zero2; + + if ((Inf1 && Zero2) || (Zero1 && Inf2) || (InfA && InfP && SignA != SignP)) + { + Result = FPDefaultNaN(); + + FPProcessException(FPExc.InvalidOp, State); + } + else if ((InfA && !SignA) || (InfP && !SignP)) + { + Result = FPInfinity(false); + } + else if ((InfA && SignA) || (InfP && SignP)) + { + Result = FPInfinity(true); + } + else if (ZeroA && ZeroP && SignA == SignP) + { + Result = FPZero(SignA); + } + else + { + // TODO: When available, use: T MathF.FusedMultiplyAdd(T, T, T); + // https://github.com/dotnet/corefx/issues/31903 + + Result = ValueA + (Value1 * Value2); + } } - ulong op1_bits = (ulong)BitConverter.DoubleToInt64Bits(op1); - ulong op2_bits = (ulong)BitConverter.DoubleToInt64Bits(op2); - - // Handle NaN cases - if (ProcessNaNs(op1_bits, op2_bits, out ulong op_bits)) - { - return BitConverter.Int64BitsToDouble((long)op_bits); - } - - // Return the most negative zero - if ((op1_bits | op2_bits) == 0x8000000000000000ul) - { - return BitConverter.Int64BitsToDouble(long.MinValue); - } - - return 0; + return Result; } - private static bool ProcessNaNs(uint op1_bits, uint op2_bits, out uint op_bits) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float FPMulSub(float ValueA, float Value1, float Value2, AThreadState State) { - if (IsSNaN(op1_bits)) + Debug.WriteIf(State.Fpcr != 0, "ASoftFloat_32.FPMulSub: "); + + Value1 = Value1.FPNeg(); + + return FPMulAdd(ValueA, Value1, Value2, State); + } + + public static float FPMulX(float Value1, float Value2, AThreadState State) + { + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPMulX: State.Fpcr = 0x{State.Fpcr:X8}"); + + Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); + Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); + + float Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); + + if (!Done) { - op_bits = op1_bits | (1u << 22); // op1 is SNaN, return QNaN op1 + bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; + bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; + + if ((Inf1 && Zero2) || (Zero1 && Inf2)) + { + Result = FPTwo(Sign1 ^ Sign2); + } + else if (Inf1 || Inf2) + { + Result = FPInfinity(Sign1 ^ Sign2); + } + else if (Zero1 || Zero2) + { + Result = FPZero(Sign1 ^ Sign2); + } + else + { + Result = Value1 * Value2; + } } - else if (IsSNaN(op2_bits)) + + return Result; + } + + public static float FPRecipStepFused(float Value1, float Value2, AThreadState State) + { + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPRecipStepFused: State.Fpcr = 0x{State.Fpcr:X8}"); + + Value1 = Value1.FPNeg(); + + Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); + Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); + + float Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); + + if (!Done) { - op_bits = op2_bits | (1u << 22); // op2 is SNaN, return QNaN op2 + bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; + bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; + + if ((Inf1 && Zero2) || (Zero1 && Inf2)) + { + Result = FPTwo(false); + } + else if (Inf1 || Inf2) + { + Result = FPInfinity(Sign1 ^ Sign2); + } + else + { + // TODO: When available, use: T MathF.FusedMultiplyAdd(T, T, T); + // https://github.com/dotnet/corefx/issues/31903 + + Result = 2f + (Value1 * Value2); + } } - else if (IsQNaN(op1_bits)) + + return Result; + } + + public static float FPRecpX(float Value, AThreadState State) + { + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPRecpX: State.Fpcr = 0x{State.Fpcr:X8}"); + + Value.FPUnpack(out FPType Type, out bool Sign, out uint Op); + + float Result; + + if (Type == FPType.SNaN || Type == FPType.QNaN) { - op_bits = op1_bits; // op1 is QNaN, return QNaN op1 - } - else if (IsQNaN(op2_bits)) - { - op_bits = op2_bits; // op2 is QNaN, return QNaN op2 + Result = FPProcessNaN(Type, Op, State); } else { - op_bits = 0; + uint NotExp = (~Op >> 23) & 0xFFu; + uint MaxExp = 0xFEu; - return false; + Result = BitConverter.Int32BitsToSingle( + (int)((Sign ? 1u : 0u) << 31 | (NotExp == 0xFFu ? MaxExp : NotExp) << 23)); } - return true; + return Result; } - private static bool ProcessNaNs(ulong op1_bits, ulong op2_bits, out ulong op_bits) + public static float FPRSqrtStepFused(float Value1, float Value2, AThreadState State) { - if (IsSNaN(op1_bits)) + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPRSqrtStepFused: State.Fpcr = 0x{State.Fpcr:X8}"); + + Value1 = Value1.FPNeg(); + + Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); + Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); + + float Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); + + if (!Done) { - op_bits = op1_bits | (1ul << 51); // op1 is SNaN, return QNaN op1 + bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; + bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; + + if ((Inf1 && Zero2) || (Zero1 && Inf2)) + { + Result = FPOnePointFive(false); + } + else if (Inf1 || Inf2) + { + Result = FPInfinity(Sign1 ^ Sign2); + } + else + { + // TODO: When available, use: T MathF.FusedMultiplyAdd(T, T, T); + // https://github.com/dotnet/corefx/issues/31903 + + Result = (3f + (Value1 * Value2)) / 2f; + } } - else if (IsSNaN(op2_bits)) + + return Result; + } + + public static float FPSqrt(float Value, AThreadState State) + { + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPSqrt: State.Fpcr = 0x{State.Fpcr:X8}"); + + Value = Value.FPUnpack(out FPType Type, out bool Sign, out uint Op); + + float Result; + + if (Type == FPType.SNaN || Type == FPType.QNaN) { - op_bits = op2_bits | (1ul << 51); // op2 is SNaN, return QNaN op2 + Result = FPProcessNaN(Type, Op, State); } - else if (IsQNaN(op1_bits)) + else if (Type == FPType.Zero) { - op_bits = op1_bits; // op1 is QNaN, return QNaN op1 + Result = FPZero(Sign); } - else if (IsQNaN(op2_bits)) + else if (Type == FPType.Infinity && !Sign) { - op_bits = op2_bits; // op2 is QNaN, return QNaN op2 + Result = FPInfinity(Sign); + } + else if (Sign) + { + Result = FPDefaultNaN(); + + FPProcessException(FPExc.InvalidOp, State); } else { - op_bits = 0; - - return false; + Result = MathF.Sqrt(Value); } - return true; + return Result; } - private static bool IsQNaN(uint op_bits) + public static float FPSub(float Value1, float Value2, AThreadState State) { - return (op_bits & 0x007FFFFF) != 0 && - (op_bits & 0x7FC00000) == 0x7FC00000; + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_32.FPSub: State.Fpcr = 0x{State.Fpcr:X8}"); + + Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); + Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); + + float Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); + + if (!Done) + { + bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; + bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; + + if (Inf1 && Inf2 && Sign1 == Sign2) + { + Result = FPDefaultNaN(); + + FPProcessException(FPExc.InvalidOp, State); + } + else if ((Inf1 && !Sign1) || (Inf2 && Sign2)) + { + Result = FPInfinity(false); + } + else if ((Inf1 && Sign1) || (Inf2 && !Sign2)) + { + Result = FPInfinity(true); + } + else if (Zero1 && Zero2 && Sign1 == !Sign2) + { + Result = FPZero(Sign1); + } + else + { + Result = Value1 - Value2; + } + } + + return Result; } - private static bool IsQNaN(ulong op_bits) + private enum FPType { - return (op_bits & 0x000FFFFFFFFFFFFF) != 0 && - (op_bits & 0x7FF8000000000000) == 0x7FF8000000000000; + Nonzero, + Zero, + Infinity, + QNaN, + SNaN } - private static bool IsSNaN(uint op_bits) + private enum FPExc { - return (op_bits & 0x007FFFFF) != 0 && - (op_bits & 0x7FC00000) == 0x7F800000; + InvalidOp, + DivideByZero, + Overflow, + Underflow, + Inexact, + InputDenorm = 7 } - private static bool IsSNaN(ulong op_bits) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float FPDefaultNaN() { - return (op_bits & 0x000FFFFFFFFFFFFF) != 0 && - (op_bits & 0x7FF8000000000000) == 0x7FF0000000000000; + return -float.NaN; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float FPInfinity(bool Sign) + { + return Sign ? float.NegativeInfinity : float.PositiveInfinity; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float FPZero(bool Sign) + { + return Sign ? -0f : +0f; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float FPTwo(bool Sign) + { + return Sign ? -2f : +2f; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float FPOnePointFive(bool Sign) + { + return Sign ? -1.5f : +1.5f; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float FPNeg(this float Value) + { + return -Value; + } + + private static float FPUnpack(this float Value, out FPType Type, out bool Sign, out uint ValueBits) + { + ValueBits = (uint)BitConverter.SingleToInt32Bits(Value); + + Sign = (~ValueBits & 0x80000000u) == 0u; + + if ((ValueBits & 0x7F800000u) == 0u) + { + if ((ValueBits & 0x007FFFFFu) == 0u) + { + Type = FPType.Zero; + } + else + { + Type = FPType.Nonzero; + } + } + else if ((~ValueBits & 0x7F800000u) == 0u) + { + if ((ValueBits & 0x007FFFFFu) == 0u) + { + Type = FPType.Infinity; + } + else + { + Type = (~ValueBits & 0x00400000u) == 0u + ? FPType.QNaN + : FPType.SNaN; + + return FPZero(Sign); + } + } + else + { + Type = FPType.Nonzero; + } + + return Value; + } + + private static float FPProcessNaNs( + FPType Type1, + FPType Type2, + uint Op1, + uint Op2, + AThreadState State, + out bool Done) + { + Done = true; + + if (Type1 == FPType.SNaN) + { + return FPProcessNaN(Type1, Op1, State); + } + else if (Type2 == FPType.SNaN) + { + return FPProcessNaN(Type2, Op2, State); + } + else if (Type1 == FPType.QNaN) + { + return FPProcessNaN(Type1, Op1, State); + } + else if (Type2 == FPType.QNaN) + { + return FPProcessNaN(Type2, Op2, State); + } + + Done = false; + + return FPZero(false); + } + + private static float FPProcessNaNs3( + FPType Type1, + FPType Type2, + FPType Type3, + uint Op1, + uint Op2, + uint Op3, + AThreadState State, + out bool Done) + { + Done = true; + + if (Type1 == FPType.SNaN) + { + return FPProcessNaN(Type1, Op1, State); + } + else if (Type2 == FPType.SNaN) + { + return FPProcessNaN(Type2, Op2, State); + } + else if (Type3 == FPType.SNaN) + { + return FPProcessNaN(Type3, Op3, State); + } + else if (Type1 == FPType.QNaN) + { + return FPProcessNaN(Type1, Op1, State); + } + else if (Type2 == FPType.QNaN) + { + return FPProcessNaN(Type2, Op2, State); + } + else if (Type3 == FPType.QNaN) + { + return FPProcessNaN(Type3, Op3, State); + } + + Done = false; + + return FPZero(false); + } + + private static float FPProcessNaN(FPType Type, uint Op, AThreadState State) + { + const int DNBit = 25; // Default NaN mode control bit. + + if (Type == FPType.SNaN) + { + Op |= 1u << 22; + + FPProcessException(FPExc.InvalidOp, State); + } + + if ((State.Fpcr & (1 << DNBit)) != 0) + { + return FPDefaultNaN(); + } + + return BitConverter.Int32BitsToSingle((int)Op); + } + + private static void FPProcessException(FPExc Exc, AThreadState State) + { + int Enable = (int)Exc + 8; + + if ((State.Fpcr & (1 << Enable)) != 0) + { + throw new NotImplementedException("floating-point trap handling"); + } + else + { + State.Fpsr |= 1 << (int)Exc; + } } } -} \ No newline at end of file + + static class ASoftFloat_64 + { + public static double FPAdd(double Value1, double Value2, AThreadState State) + { + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPAdd: State.Fpcr = 0x{State.Fpcr:X8}"); + + Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); + Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); + + double Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); + + if (!Done) + { + bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; + bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; + + if (Inf1 && Inf2 && Sign1 == !Sign2) + { + Result = FPDefaultNaN(); + + FPProcessException(FPExc.InvalidOp, State); + } + else if ((Inf1 && !Sign1) || (Inf2 && !Sign2)) + { + Result = FPInfinity(false); + } + else if ((Inf1 && Sign1) || (Inf2 && Sign2)) + { + Result = FPInfinity(true); + } + else if (Zero1 && Zero2 && Sign1 == Sign2) + { + Result = FPZero(Sign1); + } + else + { + Result = Value1 + Value2; + } + } + + return Result; + } + + public static double FPDiv(double Value1, double Value2, AThreadState State) + { + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPDiv: State.Fpcr = 0x{State.Fpcr:X8}"); + + Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); + Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); + + double Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); + + if (!Done) + { + bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; + bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; + + if ((Inf1 && Inf2) || (Zero1 && Zero2)) + { + Result = FPDefaultNaN(); + + FPProcessException(FPExc.InvalidOp, State); + } + else if (Inf1 || Zero2) + { + Result = FPInfinity(Sign1 ^ Sign2); + + if (!Inf1) FPProcessException(FPExc.DivideByZero, State); + } + else if (Zero1 || Inf2) + { + Result = FPZero(Sign1 ^ Sign2); + } + else + { + Result = Value1 / Value2; + } + } + + return Result; + } + + public static double FPMax(double Value1, double Value2, AThreadState State) + { + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPMax: State.Fpcr = 0x{State.Fpcr:X8}"); + + Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); + Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); + + double Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); + + if (!Done) + { + if (Value1 > Value2) + { + if (Type1 == FPType.Infinity) + { + Result = FPInfinity(Sign1); + } + else if (Type1 == FPType.Zero) + { + Result = FPZero(Sign1 && Sign2); + } + else + { + Result = Value1; + } + } + else + { + if (Type2 == FPType.Infinity) + { + Result = FPInfinity(Sign2); + } + else if (Type2 == FPType.Zero) + { + Result = FPZero(Sign1 && Sign2); + } + else + { + Result = Value2; + } + } + } + + return Result; + } + + public static double FPMaxNum(double Value1, double Value2, AThreadState State) + { + Debug.WriteIf(State.Fpcr != 0, "ASoftFloat_64.FPMaxNum: "); + + Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); + Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); + + if (Type1 == FPType.QNaN && Type2 != FPType.QNaN) + { + Value1 = FPInfinity(true); + } + else if (Type1 != FPType.QNaN && Type2 == FPType.QNaN) + { + Value2 = FPInfinity(true); + } + + return FPMax(Value1, Value2, State); + } + + public static double FPMin(double Value1, double Value2, AThreadState State) + { + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPMin: State.Fpcr = 0x{State.Fpcr:X8}"); + + Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); + Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); + + double Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); + + if (!Done) + { + if (Value1 < Value2) + { + if (Type1 == FPType.Infinity) + { + Result = FPInfinity(Sign1); + } + else if (Type1 == FPType.Zero) + { + Result = FPZero(Sign1 || Sign2); + } + else + { + Result = Value1; + } + } + else + { + if (Type2 == FPType.Infinity) + { + Result = FPInfinity(Sign2); + } + else if (Type2 == FPType.Zero) + { + Result = FPZero(Sign1 || Sign2); + } + else + { + Result = Value2; + } + } + } + + return Result; + } + + public static double FPMinNum(double Value1, double Value2, AThreadState State) + { + Debug.WriteIf(State.Fpcr != 0, "ASoftFloat_64.FPMinNum: "); + + Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); + Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); + + if (Type1 == FPType.QNaN && Type2 != FPType.QNaN) + { + Value1 = FPInfinity(false); + } + else if (Type1 != FPType.QNaN && Type2 == FPType.QNaN) + { + Value2 = FPInfinity(false); + } + + return FPMin(Value1, Value2, State); + } + + public static double FPMul(double Value1, double Value2, AThreadState State) + { + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPMul: State.Fpcr = 0x{State.Fpcr:X8}"); + + Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); + Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); + + double Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); + + if (!Done) + { + bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; + bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; + + if ((Inf1 && Zero2) || (Zero1 && Inf2)) + { + Result = FPDefaultNaN(); + + FPProcessException(FPExc.InvalidOp, State); + } + else if (Inf1 || Inf2) + { + Result = FPInfinity(Sign1 ^ Sign2); + } + else if (Zero1 || Zero2) + { + Result = FPZero(Sign1 ^ Sign2); + } + else + { + Result = Value1 * Value2; + } + } + + return Result; + } + + public static double FPMulAdd(double ValueA, double Value1, double Value2, AThreadState State) + { + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPMulAdd: State.Fpcr = 0x{State.Fpcr:X8}"); + + ValueA = ValueA.FPUnpack(out FPType TypeA, out bool SignA, out ulong Addend); + Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); + Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); + + bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; + bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; + + double Result = FPProcessNaNs3(TypeA, Type1, Type2, Addend, Op1, Op2, State, out bool Done); + + if (TypeA == FPType.QNaN && ((Inf1 && Zero2) || (Zero1 && Inf2))) + { + Result = FPDefaultNaN(); + + FPProcessException(FPExc.InvalidOp, State); + } + + if (!Done) + { + bool InfA = TypeA == FPType.Infinity; bool ZeroA = TypeA == FPType.Zero; + + bool SignP = Sign1 ^ Sign2; + bool InfP = Inf1 || Inf2; + bool ZeroP = Zero1 || Zero2; + + if ((Inf1 && Zero2) || (Zero1 && Inf2) || (InfA && InfP && SignA != SignP)) + { + Result = FPDefaultNaN(); + + FPProcessException(FPExc.InvalidOp, State); + } + else if ((InfA && !SignA) || (InfP && !SignP)) + { + Result = FPInfinity(false); + } + else if ((InfA && SignA) || (InfP && SignP)) + { + Result = FPInfinity(true); + } + else if (ZeroA && ZeroP && SignA == SignP) + { + Result = FPZero(SignA); + } + else + { + // TODO: When available, use: T Math.FusedMultiplyAdd(T, T, T); + // https://github.com/dotnet/corefx/issues/31903 + + Result = ValueA + (Value1 * Value2); + } + } + + return Result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double FPMulSub(double ValueA, double Value1, double Value2, AThreadState State) + { + Debug.WriteIf(State.Fpcr != 0, "ASoftFloat_64.FPMulSub: "); + + Value1 = Value1.FPNeg(); + + return FPMulAdd(ValueA, Value1, Value2, State); + } + + public static double FPMulX(double Value1, double Value2, AThreadState State) + { + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPMulX: State.Fpcr = 0x{State.Fpcr:X8}"); + + Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); + Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); + + double Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); + + if (!Done) + { + bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; + bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; + + if ((Inf1 && Zero2) || (Zero1 && Inf2)) + { + Result = FPTwo(Sign1 ^ Sign2); + } + else if (Inf1 || Inf2) + { + Result = FPInfinity(Sign1 ^ Sign2); + } + else if (Zero1 || Zero2) + { + Result = FPZero(Sign1 ^ Sign2); + } + else + { + Result = Value1 * Value2; + } + } + + return Result; + } + + public static double FPRecipStepFused(double Value1, double Value2, AThreadState State) + { + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPRecipStepFused: State.Fpcr = 0x{State.Fpcr:X8}"); + + Value1 = Value1.FPNeg(); + + Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); + Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); + + double Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); + + if (!Done) + { + bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; + bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; + + if ((Inf1 && Zero2) || (Zero1 && Inf2)) + { + Result = FPTwo(false); + } + else if (Inf1 || Inf2) + { + Result = FPInfinity(Sign1 ^ Sign2); + } + else + { + // TODO: When available, use: T Math.FusedMultiplyAdd(T, T, T); + // https://github.com/dotnet/corefx/issues/31903 + + Result = 2d + (Value1 * Value2); + } + } + + return Result; + } + + public static double FPRecpX(double Value, AThreadState State) + { + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPRecpX: State.Fpcr = 0x{State.Fpcr:X8}"); + + Value.FPUnpack(out FPType Type, out bool Sign, out ulong Op); + + double Result; + + if (Type == FPType.SNaN || Type == FPType.QNaN) + { + Result = FPProcessNaN(Type, Op, State); + } + else + { + ulong NotExp = (~Op >> 52) & 0x7FFul; + ulong MaxExp = 0x7FEul; + + Result = BitConverter.Int64BitsToDouble( + (long)((Sign ? 1ul : 0ul) << 63 | (NotExp == 0x7FFul ? MaxExp : NotExp) << 52)); + } + + return Result; + } + + public static double FPRSqrtStepFused(double Value1, double Value2, AThreadState State) + { + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPRSqrtStepFused: State.Fpcr = 0x{State.Fpcr:X8}"); + + Value1 = Value1.FPNeg(); + + Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); + Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); + + double Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); + + if (!Done) + { + bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; + bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; + + if ((Inf1 && Zero2) || (Zero1 && Inf2)) + { + Result = FPOnePointFive(false); + } + else if (Inf1 || Inf2) + { + Result = FPInfinity(Sign1 ^ Sign2); + } + else + { + // TODO: When available, use: T Math.FusedMultiplyAdd(T, T, T); + // https://github.com/dotnet/corefx/issues/31903 + + Result = (3d + (Value1 * Value2)) / 2d; + } + } + + return Result; + } + + public static double FPSqrt(double Value, AThreadState State) + { + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPSqrt: State.Fpcr = 0x{State.Fpcr:X8}"); + + Value = Value.FPUnpack(out FPType Type, out bool Sign, out ulong Op); + + double Result; + + if (Type == FPType.SNaN || Type == FPType.QNaN) + { + Result = FPProcessNaN(Type, Op, State); + } + else if (Type == FPType.Zero) + { + Result = FPZero(Sign); + } + else if (Type == FPType.Infinity && !Sign) + { + Result = FPInfinity(Sign); + } + else if (Sign) + { + Result = FPDefaultNaN(); + + FPProcessException(FPExc.InvalidOp, State); + } + else + { + Result = Math.Sqrt(Value); + } + + return Result; + } + + public static double FPSub(double Value1, double Value2, AThreadState State) + { + Debug.WriteLineIf(State.Fpcr != 0, $"ASoftFloat_64.FPSub: State.Fpcr = 0x{State.Fpcr:X8}"); + + Value1 = Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); + Value2 = Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); + + double Result = FPProcessNaNs(Type1, Type2, Op1, Op2, State, out bool Done); + + if (!Done) + { + bool Inf1 = Type1 == FPType.Infinity; bool Zero1 = Type1 == FPType.Zero; + bool Inf2 = Type2 == FPType.Infinity; bool Zero2 = Type2 == FPType.Zero; + + if (Inf1 && Inf2 && Sign1 == Sign2) + { + Result = FPDefaultNaN(); + + FPProcessException(FPExc.InvalidOp, State); + } + else if ((Inf1 && !Sign1) || (Inf2 && Sign2)) + { + Result = FPInfinity(false); + } + else if ((Inf1 && Sign1) || (Inf2 && !Sign2)) + { + Result = FPInfinity(true); + } + else if (Zero1 && Zero2 && Sign1 == !Sign2) + { + Result = FPZero(Sign1); + } + else + { + Result = Value1 - Value2; + } + } + + return Result; + } + + private enum FPType + { + Nonzero, + Zero, + Infinity, + QNaN, + SNaN + } + + private enum FPExc + { + InvalidOp, + DivideByZero, + Overflow, + Underflow, + Inexact, + InputDenorm = 7 + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static double FPDefaultNaN() + { + return -double.NaN; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static double FPInfinity(bool Sign) + { + return Sign ? double.NegativeInfinity : double.PositiveInfinity; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static double FPZero(bool Sign) + { + return Sign ? -0d : +0d; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static double FPTwo(bool Sign) + { + return Sign ? -2d : +2d; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static double FPOnePointFive(bool Sign) + { + return Sign ? -1.5d : +1.5d; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static double FPNeg(this double Value) + { + return -Value; + } + + private static double FPUnpack(this double Value, out FPType Type, out bool Sign, out ulong ValueBits) + { + ValueBits = (ulong)BitConverter.DoubleToInt64Bits(Value); + + Sign = (~ValueBits & 0x8000000000000000ul) == 0ul; + + if ((ValueBits & 0x7FF0000000000000ul) == 0ul) + { + if ((ValueBits & 0x000FFFFFFFFFFFFFul) == 0ul) + { + Type = FPType.Zero; + } + else + { + Type = FPType.Nonzero; + } + } + else if ((~ValueBits & 0x7FF0000000000000ul) == 0ul) + { + if ((ValueBits & 0x000FFFFFFFFFFFFFul) == 0ul) + { + Type = FPType.Infinity; + } + else + { + Type = (~ValueBits & 0x0008000000000000ul) == 0ul + ? FPType.QNaN + : FPType.SNaN; + + return FPZero(Sign); + } + } + else + { + Type = FPType.Nonzero; + } + + return Value; + } + + private static double FPProcessNaNs( + FPType Type1, + FPType Type2, + ulong Op1, + ulong Op2, + AThreadState State, + out bool Done) + { + Done = true; + + if (Type1 == FPType.SNaN) + { + return FPProcessNaN(Type1, Op1, State); + } + else if (Type2 == FPType.SNaN) + { + return FPProcessNaN(Type2, Op2, State); + } + else if (Type1 == FPType.QNaN) + { + return FPProcessNaN(Type1, Op1, State); + } + else if (Type2 == FPType.QNaN) + { + return FPProcessNaN(Type2, Op2, State); + } + + Done = false; + + return FPZero(false); + } + + private static double FPProcessNaNs3( + FPType Type1, + FPType Type2, + FPType Type3, + ulong Op1, + ulong Op2, + ulong Op3, + AThreadState State, + out bool Done) + { + Done = true; + + if (Type1 == FPType.SNaN) + { + return FPProcessNaN(Type1, Op1, State); + } + else if (Type2 == FPType.SNaN) + { + return FPProcessNaN(Type2, Op2, State); + } + else if (Type3 == FPType.SNaN) + { + return FPProcessNaN(Type3, Op3, State); + } + else if (Type1 == FPType.QNaN) + { + return FPProcessNaN(Type1, Op1, State); + } + else if (Type2 == FPType.QNaN) + { + return FPProcessNaN(Type2, Op2, State); + } + else if (Type3 == FPType.QNaN) + { + return FPProcessNaN(Type3, Op3, State); + } + + Done = false; + + return FPZero(false); + } + + private static double FPProcessNaN(FPType Type, ulong Op, AThreadState State) + { + const int DNBit = 25; // Default NaN mode control bit. + + if (Type == FPType.SNaN) + { + Op |= 1ul << 51; + + FPProcessException(FPExc.InvalidOp, State); + } + + if ((State.Fpcr & (1 << DNBit)) != 0) + { + return FPDefaultNaN(); + } + + return BitConverter.Int64BitsToDouble((long)Op); + } + + private static void FPProcessException(FPExc Exc, AThreadState State) + { + int Enable = (int)Exc + 8; + + if ((State.Fpcr & (1 << Enable)) != 0) + { + throw new NotImplementedException("floating-point trap handling"); + } + else + { + State.Fpsr |= 1 << (int)Exc; + } + } + } +} diff --git a/Ryujinx.Tests/Cpu/CpuTest.cs b/Ryujinx.Tests/Cpu/CpuTest.cs index c69c7a02f9..24585fe78f 100644 --- a/Ryujinx.Tests/Cpu/CpuTest.cs +++ b/Ryujinx.Tests/Cpu/CpuTest.cs @@ -178,8 +178,15 @@ namespace Ryujinx.Tests.Cpu return GetThreadState(); } - [Flags] - protected enum FPSR + /// Floating-point Control Register. + protected enum FPCR + { + /// Default NaN mode control bit. + DN = 25 + } + + /// Floating-point Status Register. + [Flags] protected enum FPSR { None = 0, @@ -195,32 +202,43 @@ namespace Ryujinx.Tests.Cpu IXC = 1 << 4, /// Input Denormal cumulative floating-point exception bit. IDC = 1 << 7, + /// Cumulative saturation bit. - QC = 1 << 27 + QC = 1 << 27 } - protected enum FpSkips { None, IfNaN_S, IfNaN_D }; + [Flags] protected enum FpSkips + { + None = 0, - protected enum FpUseTolerance { None, OneUlps_S, OneUlps_D }; + IfNaN_S = 1, + IfNaN_D = 2, + + IfUnderflow = 4, + IfOverflow = 8 + } + + protected enum FpTolerances + { + None, + + UpToOneUlps_S, + UpToOneUlps_D + } protected void CompareAgainstUnicorn( - FPSR FpsrMask = FPSR.None, - FpSkips FpSkips = FpSkips.None, - FpUseTolerance FpUseTolerance = FpUseTolerance.None) + FPSR FpsrMask = FPSR.None, + FpSkips FpSkips = FpSkips.None, + FpTolerances FpTolerances = FpTolerances.None) { if (!UnicornAvailable) { return; } - if (FpSkips == FpSkips.IfNaN_S && float.IsNaN(VectorExtractSingle(UnicornEmu.Q[0], (byte)0))) + if (FpSkips != FpSkips.None) { - Assert.Ignore("NaN test."); - } - - if (FpSkips == FpSkips.IfNaN_D && double.IsNaN(VectorExtractDouble(UnicornEmu.Q[0], (byte)0))) - { - Assert.Ignore("NaN test."); + ManageFpSkips(FpSkips); } Assert.That(Thread.ThreadState.X0, Is.EqualTo(UnicornEmu.X[0])); @@ -257,50 +275,13 @@ namespace Ryujinx.Tests.Cpu Assert.That(Thread.ThreadState.X31, Is.EqualTo(UnicornEmu.SP)); - if (FpUseTolerance == FpUseTolerance.None) + if (FpTolerances == FpTolerances.None) { Assert.That(Thread.ThreadState.V0, Is.EqualTo(UnicornEmu.Q[0])); } else { - if (!Is.EqualTo(UnicornEmu.Q[0]).ApplyTo(Thread.ThreadState.V0).IsSuccess) - { - if (FpUseTolerance == FpUseTolerance.OneUlps_S) - { - if (float.IsNormal (VectorExtractSingle(UnicornEmu.Q[0], (byte)0)) || - float.IsSubnormal(VectorExtractSingle(UnicornEmu.Q[0], (byte)0))) - { - Assert.That (VectorExtractSingle(Thread.ThreadState.V0, (byte)0), - Is.EqualTo(VectorExtractSingle(UnicornEmu.Q[0], (byte)0)).Within(1).Ulps); - Assert.That (VectorExtractSingle(Thread.ThreadState.V0, (byte)1), - Is.EqualTo(VectorExtractSingle(UnicornEmu.Q[0], (byte)1)).Within(1).Ulps); - Assert.That (VectorExtractSingle(Thread.ThreadState.V0, (byte)2), - Is.EqualTo(VectorExtractSingle(UnicornEmu.Q[0], (byte)2)).Within(1).Ulps); - Assert.That (VectorExtractSingle(Thread.ThreadState.V0, (byte)3), - Is.EqualTo(VectorExtractSingle(UnicornEmu.Q[0], (byte)3)).Within(1).Ulps); - } - else - { - Assert.That(Thread.ThreadState.V0, Is.EqualTo(UnicornEmu.Q[0])); - } - } - - if (FpUseTolerance == FpUseTolerance.OneUlps_D) - { - if (double.IsNormal (VectorExtractDouble(UnicornEmu.Q[0], (byte)0)) || - double.IsSubnormal(VectorExtractDouble(UnicornEmu.Q[0], (byte)0))) - { - Assert.That (VectorExtractDouble(Thread.ThreadState.V0, (byte)0), - Is.EqualTo(VectorExtractDouble(UnicornEmu.Q[0], (byte)0)).Within(1).Ulps); - Assert.That (VectorExtractDouble(Thread.ThreadState.V0, (byte)1), - Is.EqualTo(VectorExtractDouble(UnicornEmu.Q[0], (byte)1)).Within(1).Ulps); - } - else - { - Assert.That(Thread.ThreadState.V0, Is.EqualTo(UnicornEmu.Q[0])); - } - } - } + ManageFpTolerances(FpTolerances); } Assert.That(Thread.ThreadState.V1, Is.EqualTo(UnicornEmu.Q[1])); Assert.That(Thread.ThreadState.V2, Is.EqualTo(UnicornEmu.Q[2])); @@ -344,6 +325,90 @@ namespace Ryujinx.Tests.Cpu Assert.That(Thread.ThreadState.Negative, Is.EqualTo(UnicornEmu.NegativeFlag)); } + private void ManageFpSkips(FpSkips FpSkips) + { + if (FpSkips.HasFlag(FpSkips.IfNaN_S)) + { + if (float.IsNaN(VectorExtractSingle(UnicornEmu.Q[0], (byte)0))) + { + Assert.Ignore("NaN test."); + } + } + else if (FpSkips.HasFlag(FpSkips.IfNaN_D)) + { + if (double.IsNaN(VectorExtractDouble(UnicornEmu.Q[0], (byte)0))) + { + Assert.Ignore("NaN test."); + } + } + + if (FpSkips.HasFlag(FpSkips.IfUnderflow)) + { + if ((UnicornEmu.Fpsr & (int)FPSR.UFC) != 0) + { + Assert.Ignore("Underflow test."); + } + } + + if (FpSkips.HasFlag(FpSkips.IfOverflow)) + { + if ((UnicornEmu.Fpsr & (int)FPSR.OFC) != 0) + { + Assert.Ignore("Overflow test."); + } + } + } + + private void ManageFpTolerances(FpTolerances FpTolerances) + { + if (!Is.EqualTo(UnicornEmu.Q[0]).ApplyTo(Thread.ThreadState.V0).IsSuccess) + { + if (FpTolerances == FpTolerances.UpToOneUlps_S) + { + if (IsNormalOrSubnormal_S(VectorExtractSingle(UnicornEmu.Q[0], (byte)0)) && + IsNormalOrSubnormal_S(VectorExtractSingle(Thread.ThreadState.V0, (byte)0))) + { + Assert.That (VectorExtractSingle(Thread.ThreadState.V0, (byte)0), + Is.EqualTo(VectorExtractSingle(UnicornEmu.Q[0], (byte)0)).Within(1).Ulps); + Assert.That (VectorExtractSingle(Thread.ThreadState.V0, (byte)1), + Is.EqualTo(VectorExtractSingle(UnicornEmu.Q[0], (byte)1)).Within(1).Ulps); + Assert.That (VectorExtractSingle(Thread.ThreadState.V0, (byte)2), + Is.EqualTo(VectorExtractSingle(UnicornEmu.Q[0], (byte)2)).Within(1).Ulps); + Assert.That (VectorExtractSingle(Thread.ThreadState.V0, (byte)3), + Is.EqualTo(VectorExtractSingle(UnicornEmu.Q[0], (byte)3)).Within(1).Ulps); + + Console.WriteLine(FpTolerances); + } + else + { + Assert.That(Thread.ThreadState.V0, Is.EqualTo(UnicornEmu.Q[0])); + } + } + + if (FpTolerances == FpTolerances.UpToOneUlps_D) + { + if (IsNormalOrSubnormal_D(VectorExtractDouble(UnicornEmu.Q[0], (byte)0)) && + IsNormalOrSubnormal_D(VectorExtractDouble(Thread.ThreadState.V0, (byte)0))) + { + Assert.That (VectorExtractDouble(Thread.ThreadState.V0, (byte)0), + Is.EqualTo(VectorExtractDouble(UnicornEmu.Q[0], (byte)0)).Within(1).Ulps); + Assert.That (VectorExtractDouble(Thread.ThreadState.V0, (byte)1), + Is.EqualTo(VectorExtractDouble(UnicornEmu.Q[0], (byte)1)).Within(1).Ulps); + + Console.WriteLine(FpTolerances); + } + else + { + Assert.That(Thread.ThreadState.V0, Is.EqualTo(UnicornEmu.Q[0])); + } + } + } + + bool IsNormalOrSubnormal_S(float f) => float.IsNormal(f) || float.IsSubnormal(f); + + bool IsNormalOrSubnormal_D(double d) => double.IsNormal(d) || double.IsSubnormal(d); + } + protected static Vector128 MakeVectorE0(double E0) { if (!Sse2.IsSupported) @@ -453,14 +518,14 @@ namespace Ryujinx.Tests.Cpu { uint Rnd; - do Rnd = TestContext.CurrentContext.Random.NextUInt(); - while ((Rnd & 0x7F800000u) == 0u || - (Rnd & 0x7F800000u) == 0x7F800000u); + do Rnd = TestContext.CurrentContext.Random.NextUInt(); + while (( Rnd & 0x7F800000u) == 0u || + (~Rnd & 0x7F800000u) == 0u); return Rnd; } - protected static uint GenSubNormal_S() + protected static uint GenSubnormal_S() { uint Rnd; @@ -474,14 +539,14 @@ namespace Ryujinx.Tests.Cpu { ulong Rnd; - do Rnd = TestContext.CurrentContext.Random.NextULong(); - while ((Rnd & 0x7FF0000000000000ul) == 0ul || - (Rnd & 0x7FF0000000000000ul) == 0x7FF0000000000000ul); + do Rnd = TestContext.CurrentContext.Random.NextULong(); + while (( Rnd & 0x7FF0000000000000ul) == 0ul || + (~Rnd & 0x7FF0000000000000ul) == 0ul); return Rnd; } - protected static ulong GenSubNormal_D() + protected static ulong GenSubnormal_D() { ulong Rnd; diff --git a/Ryujinx.Tests/Cpu/CpuTestSimd.cs b/Ryujinx.Tests/Cpu/CpuTestSimd.cs index b423b4de73..2075ccf2b0 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimd.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimd.cs @@ -81,14 +81,14 @@ namespace Ryujinx.Tests.Cpu private static IEnumerable _1S_F_() { - yield return 0x00000000FF7FFFFFul; // -Max Normal (float.MinValue) + yield return 0x00000000FF7FFFFFul; // -Max Normal (float.MinValue) yield return 0x0000000080800000ul; // -Min Normal - yield return 0x00000000807FFFFFul; // -Max SubNormal - yield return 0x0000000080000001ul; // -Min SubNormal - yield return 0x000000007F7FFFFFul; // +Max Normal (float.MaxValue) + yield return 0x00000000807FFFFFul; // -Max Subnormal + yield return 0x0000000080000001ul; // -Min Subnormal (-float.Epsilon) + yield return 0x000000007F7FFFFFul; // +Max Normal (float.MaxValue) yield return 0x0000000000800000ul; // +Min Normal - yield return 0x00000000007FFFFFul; // +Max SubNormal - yield return 0x0000000000000001ul; // +Min SubNormal + yield return 0x00000000007FFFFFul; // +Max Subnormal + yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon) if (!NoZeros) { @@ -104,17 +104,17 @@ namespace Ryujinx.Tests.Cpu if (!NoNaNs) { - yield return 0x00000000FFFFFFFFul; // -QNaN (all ones payload) - yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) - yield return 0x000000007FFFFFFFul; // +QNaN (all ones payload) - yield return 0x000000007FBFFFFFul; // +SNaN (all ones payload) + yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) + yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) + yield return 0x000000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN) + yield return 0x000000007FBFFFFFul; // +SNaN (all ones payload) } for (int Cnt = 1; Cnt <= RndCnt; Cnt++) { ulong Grbg = TestContext.CurrentContext.Random.NextUInt(); ulong Rnd1 = GenNormal_S(); - ulong Rnd2 = GenSubNormal_S(); + ulong Rnd2 = GenSubnormal_S(); yield return (Grbg << 32) | Rnd1; yield return (Grbg << 32) | Rnd2; @@ -123,14 +123,14 @@ namespace Ryujinx.Tests.Cpu private static IEnumerable _2S_F_() { - yield return 0xFF7FFFFFFF7FFFFFul; // -Max Normal (float.MinValue) + yield return 0xFF7FFFFFFF7FFFFFul; // -Max Normal (float.MinValue) yield return 0x8080000080800000ul; // -Min Normal - yield return 0x807FFFFF807FFFFFul; // -Max SubNormal - yield return 0x8000000180000001ul; // -Min SubNormal - yield return 0x7F7FFFFF7F7FFFFFul; // +Max Normal (float.MaxValue) + yield return 0x807FFFFF807FFFFFul; // -Max Subnormal + yield return 0x8000000180000001ul; // -Min Subnormal (-float.Epsilon) + yield return 0x7F7FFFFF7F7FFFFFul; // +Max Normal (float.MaxValue) yield return 0x0080000000800000ul; // +Min Normal - yield return 0x007FFFFF007FFFFFul; // +Max SubNormal - yield return 0x0000000100000001ul; // +Min SubNormal + yield return 0x007FFFFF007FFFFFul; // +Max Subnormal + yield return 0x0000000100000001ul; // +Min Subnormal (float.Epsilon) if (!NoZeros) { @@ -146,16 +146,16 @@ namespace Ryujinx.Tests.Cpu if (!NoNaNs) { - yield return 0xFFFFFFFFFFFFFFFFul; // -QNaN (all ones payload) - yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload) - yield return 0x7FFFFFFF7FFFFFFFul; // +QNaN (all ones payload) - yield return 0x7FBFFFFF7FBFFFFFul; // +SNaN (all ones payload) + yield return 0xFFC00000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) + yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload) + yield return 0x7FC000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN) + yield return 0x7FBFFFFF7FBFFFFFul; // +SNaN (all ones payload) } for (int Cnt = 1; Cnt <= RndCnt; Cnt++) { ulong Rnd1 = GenNormal_S(); - ulong Rnd2 = GenSubNormal_S(); + ulong Rnd2 = GenSubnormal_S(); yield return (Rnd1 << 32) | Rnd1; yield return (Rnd2 << 32) | Rnd2; @@ -164,14 +164,14 @@ namespace Ryujinx.Tests.Cpu private static IEnumerable _1D_F_() { - yield return 0xFFEFFFFFFFFFFFFFul; // -Max Normal (double.MinValue) + yield return 0xFFEFFFFFFFFFFFFFul; // -Max Normal (double.MinValue) yield return 0x8010000000000000ul; // -Min Normal - yield return 0x800FFFFFFFFFFFFFul; // -Max SubNormal - yield return 0x8000000000000001ul; // -Min SubNormal - yield return 0x7FEFFFFFFFFFFFFFul; // +Max Normal (double.MaxValue) + yield return 0x800FFFFFFFFFFFFFul; // -Max Subnormal + yield return 0x8000000000000001ul; // -Min Subnormal (-double.Epsilon) + yield return 0x7FEFFFFFFFFFFFFFul; // +Max Normal (double.MaxValue) yield return 0x0010000000000000ul; // +Min Normal - yield return 0x000FFFFFFFFFFFFFul; // +Max SubNormal - yield return 0x0000000000000001ul; // +Min SubNormal + yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal + yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon) if (!NoZeros) { @@ -187,16 +187,16 @@ namespace Ryujinx.Tests.Cpu if (!NoNaNs) { - yield return 0xFFFFFFFFFFFFFFFFul; // -QNaN (all ones payload) - yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) - yield return 0x7FFFFFFFFFFFFFFFul; // +QNaN (all ones payload) - yield return 0x7FF7FFFFFFFFFFFFul; // +SNaN (all ones payload) + yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN) + yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) + yield return 0x7FF8000000000000ul; // +QNaN (all zeros payload) (-double.NaN) (DefaultNaN) + yield return 0x7FF7FFFFFFFFFFFFul; // +SNaN (all ones payload) } for (int Cnt = 1; Cnt <= RndCnt; Cnt++) { ulong Rnd1 = GenNormal_D(); - ulong Rnd2 = GenSubNormal_D(); + ulong Rnd2 = GenSubnormal_D(); yield return Rnd1; yield return Rnd2; @@ -248,6 +248,40 @@ namespace Ryujinx.Tests.Cpu 0x6EE1B800u // FCVTZU V0.2D, V0.2D }; } + + private static uint[] _F_RecpX_Sqrt_S_S_() + { + return new uint[] + { + 0x5EA1F820u, // FRECPX S0, S1 + 0x1E21C020u // FSQRT S0, S1 + }; + } + + private static uint[] _F_RecpX_Sqrt_S_D_() + { + return new uint[] + { + 0x5EE1F820u, // FRECPX D0, D1 + 0x1E61C020u // FSQRT D0, D1 + }; + } + + private static uint[] _F_Sqrt_V_2S_4S_() + { + return new uint[] + { + 0x2EA1F800u // FSQRT V0.2S, V0.2S + }; + } + + private static uint[] _F_Sqrt_V_2D_() + { + return new uint[] + { + 0x6EE1F800u // FSQRT V0.2D, V0.2D + }; + } #endregion private const int RndCnt = 2; @@ -754,21 +788,15 @@ namespace Ryujinx.Tests.Cpu [Test, Pairwise, Description("FCVT
, ")] public void Fcvt_S_SD([ValueSource("_1S_F_")] ulong A) { - //const int DNFlagBit = 25; // Default NaN mode control bit. - //const int FZFlagBit = 24; // Flush-to-zero mode control bit. - uint Opcode = 0x1E22C020; // FCVT D0, S1 ulong Z = TestContext.CurrentContext.Random.NextULong(); Vector128 V0 = MakeVectorE1(Z); Vector128 V1 = MakeVectorE0(A); - //int Fpcr = 1 << DNFlagBit; // Any operation involving one or more NaNs returns the Default NaN. - //Fpcr |= 1 << FZFlagBit; // Flush-to-zero mode enabled. + AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1/*, Fpcr: Fpcr*/); - - CompareAgainstUnicorn(/*FpsrMask: FPSR.IDC | FPSR.IOC*/); + CompareAgainstUnicorn(); } [Test, Pairwise, Description("FCVT , ")] @@ -789,17 +817,13 @@ namespace Ryujinx.Tests.Cpu public void F_Cvt_NZ_SU_S_S([ValueSource("_F_Cvt_NZ_SU_S_S_")] uint Opcodes, [ValueSource("_1S_F_")] ulong A) { - //const int FZFlagBit = 24; // Flush-to-zero mode control bit. - ulong Z = TestContext.CurrentContext.Random.NextULong(); Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); - //int Fpcr = 1 << FZFlagBit; // Flush-to-zero mode enabled. + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1); - AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1/*, Fpcr: Fpcr*/); - - CompareAgainstUnicorn(/*FpsrMask: FPSR.IDC | FPSR.IXC | FPSR.IOC*/); + CompareAgainstUnicorn(); } [Test, Pairwise] @@ -851,6 +875,76 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } + [Test, Pairwise] + public void F_RecpX_Sqrt_S_S([ValueSource("_F_RecpX_Sqrt_S_S_")] uint Opcodes, + [ValueSource("_1S_F_")] ulong A) + { + ulong Z = TestContext.CurrentContext.Random.NextULong(); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, Fpcr: Fpcr); + + CompareAgainstUnicorn(FpsrMask: FPSR.IOC); + } + + [Test, Pairwise] + public void F_RecpX_Sqrt_S_D([ValueSource("_F_RecpX_Sqrt_S_D_")] uint Opcodes, + [ValueSource("_1D_F_")] ulong A) + { + ulong Z = TestContext.CurrentContext.Random.NextULong(); + Vector128 V0 = MakeVectorE1(Z); + Vector128 V1 = MakeVectorE0(A); + + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, Fpcr: Fpcr); + + CompareAgainstUnicorn(FpsrMask: FPSR.IOC); + } + + [Test, Pairwise] + public void F_Sqrt_V_2S_4S([ValueSource("_F_Sqrt_V_2S_4S_")] uint Opcodes, + [Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_2S_F_")] ulong Z, + [ValueSource("_2S_F_")] ulong A, + [Values(0b0u, 0b1u)] uint Q) // <2S, 4S> + { + Opcodes |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcodes |= ((Q & 1) << 30); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A * Q); + + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, Fpcr: Fpcr); + + CompareAgainstUnicorn(FpsrMask: FPSR.IOC); + } + + [Test, Pairwise] + public void F_Sqrt_V_2D([ValueSource("_F_Sqrt_V_2D_")] uint Opcodes, + [Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [ValueSource("_1D_F_")] ulong Z, + [ValueSource("_1D_F_")] ulong A) + { + Opcodes |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, Fpcr: Fpcr); + + CompareAgainstUnicorn(FpsrMask: FPSR.IOC); + } + [Test, Pairwise, Description("NEG , ")] public void Neg_S_D([Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs b/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs index 7a67d53b91..a5ae1a5f76 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs @@ -9,126 +9,6 @@ namespace Ryujinx.Tests.Cpu { public class CpuTestSimdArithmetic : CpuTest { - [TestCase(0x1E224820u, 0x0000000000000000ul, 0x0000000080000000ul, 0x0000000000000000ul)] // FMAX S0, S1, S2 - [TestCase(0x1E224820u, 0x0000000080000000ul, 0x0000000000000000ul, 0x0000000000000000ul)] - [TestCase(0x1E224820u, 0x0000000080000000ul, 0x0000000080000000ul, 0x0000000080000000ul)] - [TestCase(0x1E224820u, 0x0000000080000000ul, 0x000000003DCCCCCDul, 0x000000003DCCCCCDul)] - [TestCase(0x1E224820u, 0x000000003DCCCCCDul, 0x000000003C9623B1ul, 0x000000003DCCCCCDul)] - [TestCase(0x1E224820u, 0x000000008BA98D27ul, 0x0000000000000076ul, 0x0000000000000076ul)] - [TestCase(0x1E224820u, 0x00000000807FFFFFul, 0x000000007F7FFFFFul, 0x000000007F7FFFFFul)] - [TestCase(0x1E224820u, 0x000000007F7FFFFFul, 0x00000000807FFFFFul, 0x000000007F7FFFFFul)] - [TestCase(0x1E224820u, 0x000000007FC00000ul, 0x000000003F800000ul, 0x000000007FC00000ul)] - [TestCase(0x1E224820u, 0x000000003F800000ul, 0x000000007FC00000ul, 0x000000007FC00000ul)] - [TestCase(0x1E224820u, 0x000000007F800001ul, 0x000000007FC00042ul, 0x000000007FC00001ul)] - [TestCase(0x1E224820u, 0x000000007FC00042ul, 0x000000007F800001ul, 0x000000007FC00001ul)] - [TestCase(0x1E224820u, 0x000000007FC0000Aul, 0x000000007FC0000Bul, 0x000000007FC0000Aul)] - [TestCase(0x1E624820u, 0x0000000000000000ul, 0x8000000000000000ul, 0x0000000000000000ul)] // FMAX D0, D1, D2 - [TestCase(0x1E624820u, 0x8000000000000000ul, 0x0000000000000000ul, 0x0000000000000000ul)] - [TestCase(0x1E624820u, 0x8000000000000000ul, 0x8000000000000000ul, 0x8000000000000000ul)] - [TestCase(0x1E624820u, 0x8000000000000000ul, 0x3FF3333333333333ul, 0x3FF3333333333333ul)] - public void Fmax_S(uint Opcode, ulong A, ulong B, ulong Result) - { - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); - - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); - - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result)); - - CompareAgainstUnicorn(); - } - - [TestCase(0x80000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x00000000u, 0x00000000u)] - [TestCase(0x00000000u, 0x00000000u, 0x80000000u, 0x80000000u, 0x00000000u, 0x00000000u)] - [TestCase(0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u)] - [TestCase(0x80000000u, 0x80000000u, 0x3DCCCCCDu, 0x3DCCCCCDu, 0x3DCCCCCDu, 0x3DCCCCCDu)] - [TestCase(0x3DCCCCCDu, 0x3DCCCCCDu, 0x3C9623B1u, 0x3C9623B1u, 0x3DCCCCCDu, 0x3DCCCCCDu)] - [TestCase(0x8BA98D27u, 0x8BA98D27u, 0x00000076u, 0x00000076u, 0x00000076u, 0x00000076u)] - [TestCase(0x807FFFFFu, 0x807FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu)] - [TestCase(0x7F7FFFFFu, 0x7F7FFFFFu, 0x807FFFFFu, 0x807FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu)] - [TestCase(0x7FC00000u, 0x7FC00000u, 0x3F800000u, 0x3F800000u, 0x7FC00000u, 0x7FC00000u)] - [TestCase(0x3F800000u, 0x3F800000u, 0x7FC00000u, 0x7FC00000u, 0x7FC00000u, 0x7FC00000u)] - [TestCase(0x7F800001u, 0x7F800001u, 0x7FC00042u, 0x7FC00042u, 0x7FC00001u, 0x7FC00001u)] - [TestCase(0x7FC00042u, 0x7FC00042u, 0x7F800001u, 0x7F800001u, 0x7FC00001u, 0x7FC00001u)] - [TestCase(0x7FC0000Au, 0x7FC0000Au, 0x7FC0000Bu, 0x7FC0000Bu, 0x7FC0000Au, 0x7FC0000Au)] - public void Fmax_V(uint A, uint B, uint C, uint D, uint Result0, uint Result1) - { - uint Opcode = 0x4E22F420; // FMAX V0.4S, V1.4S, V2.4S - - Vector128 V1 = MakeVectorE0E1(A, B); - Vector128 V2 = MakeVectorE0E1(C, D); - - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); - - Assert.Multiple(() => - { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result0)); - Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(Result1)); - }); - - CompareAgainstUnicorn(); - } - - [TestCase(0x1E225820u, 0x0000000000000000ul, 0x0000000080000000ul, 0x0000000080000000ul)] // FMIN S0, S1, S2 - [TestCase(0x1E225820u, 0x0000000080000000ul, 0x0000000000000000ul, 0x0000000080000000ul)] - [TestCase(0x1E225820u, 0x0000000080000000ul, 0x0000000080000000ul, 0x0000000080000000ul)] - [TestCase(0x1E225820u, 0x0000000080000000ul, 0x000000003DCCCCCDul, 0x0000000080000000ul)] - [TestCase(0x1E225820u, 0x000000003DCCCCCDul, 0x000000003C9623B1ul, 0x000000003C9623B1ul)] - [TestCase(0x1E225820u, 0x000000008BA98D27ul, 0x0000000000000076ul, 0x000000008BA98D27ul)] - [TestCase(0x1E225820u, 0x00000000807FFFFFul, 0x000000007F7FFFFFul, 0x00000000807FFFFFul)] - [TestCase(0x1E225820u, 0x000000007F7FFFFFul, 0x00000000807FFFFFul, 0x00000000807FFFFFul)] - [TestCase(0x1E225820u, 0x000000007FC00000ul, 0x000000003F800000ul, 0x000000007FC00000ul)] - [TestCase(0x1E225820u, 0x000000003F800000ul, 0x000000007FC00000ul, 0x000000007FC00000ul)] - [TestCase(0x1E225820u, 0x000000007F800001ul, 0x000000007FC00042ul, 0x000000007FC00001ul)] - [TestCase(0x1E225820u, 0x000000007FC00042ul, 0x000000007F800001ul, 0x000000007FC00001ul)] - [TestCase(0x1E225820u, 0x000000007FC0000Aul, 0x000000007FC0000Bul, 0x000000007FC0000Aul)] - [TestCase(0x1E625820u, 0x0000000000000000ul, 0x8000000000000000ul, 0x8000000000000000ul)] // FMIN D0, D1, D2 - [TestCase(0x1E625820u, 0x8000000000000000ul, 0x0000000000000000ul, 0x8000000000000000ul)] - [TestCase(0x1E625820u, 0x8000000000000000ul, 0x8000000000000000ul, 0x8000000000000000ul)] - [TestCase(0x1E625820u, 0x8000000000000000ul, 0x3FF3333333333333ul, 0x8000000000000000ul)] - public void Fmin_S(uint Opcode, ulong A, ulong B, ulong Result) - { - Vector128 V1 = MakeVectorE0(A); - Vector128 V2 = MakeVectorE0(B); - - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); - - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result)); - - CompareAgainstUnicorn(); - } - - [TestCase(0x80000000u, 0x80000000u, 0x00000000u, 0x00000000u, 0x80000000u, 0x80000000u)] - [TestCase(0x00000000u, 0x00000000u, 0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u)] - [TestCase(0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u, 0x80000000u)] - [TestCase(0x80000000u, 0x80000000u, 0x3DCCCCCDu, 0x3DCCCCCDu, 0x80000000u, 0x80000000u)] - [TestCase(0x3DCCCCCDu, 0x3DCCCCCDu, 0x3C9623B1u, 0x3C9623B1u, 0x3C9623B1u, 0x3C9623B1u)] - [TestCase(0x8BA98D27u, 0x8BA98D27u, 0x00000076u, 0x00000076u, 0x8BA98D27u, 0x8BA98D27u)] - [TestCase(0x807FFFFFu, 0x807FFFFFu, 0x7F7FFFFFu, 0x7F7FFFFFu, 0x807FFFFFu, 0x807FFFFFu)] - [TestCase(0x7F7FFFFFu, 0x7F7FFFFFu, 0x807FFFFFu, 0x807FFFFFu, 0x807FFFFFu, 0x807FFFFFu)] - [TestCase(0x7FC00000u, 0x7FC00000u, 0x3F800000u, 0x3F800000u, 0x7FC00000u, 0x7FC00000u)] - [TestCase(0x3F800000u, 0x3F800000u, 0x7FC00000u, 0x7FC00000u, 0x7FC00000u, 0x7FC00000u)] - [TestCase(0x7F800001u, 0x7F800001u, 0x7FC00042u, 0x7FC00042u, 0x7FC00001u, 0x7FC00001u)] - [TestCase(0x7FC00042u, 0x7FC00042u, 0x7F800001u, 0x7F800001u, 0x7FC00001u, 0x7FC00001u)] - [TestCase(0x7FC0000Au, 0x7FC0000Au, 0x7FC0000Bu, 0x7FC0000Bu, 0x7FC0000Au, 0x7FC0000Au)] - public void Fmin_V(uint A, uint B, uint C, uint D, uint Result0, uint Result1) - { - uint Opcode = 0x4EA2F420; // FMIN V0.4S, V1.4S, V2.4S - - Vector128 V1 = MakeVectorE0E1(A, B); - Vector128 V2 = MakeVectorE0E1(C, D); - - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); - - Assert.Multiple(() => - { - Assert.That(GetVectorE0(ThreadState.V0), Is.EqualTo(Result0)); - Assert.That(GetVectorE1(ThreadState.V0), Is.EqualTo(Result1)); - }); - - CompareAgainstUnicorn(); - } - [Test, Description("FMUL S6, S1, V0.S[2]")] public void Fmul_Se([Random(10)] float A, [Random(10)] float B) { @@ -161,38 +41,6 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Description("FRECPS D0, D1, D2"), Ignore("Not accurate enough.")] - public void Frecps_S([Random(10)] double A, [Random(10)] double B) - { - AThreadState ThreadState = SingleOpcode(0x5E62FC20, - V1: MakeVectorE0(A), - V2: MakeVectorE0(B)); - - Assert.That(VectorExtractDouble(ThreadState.V0, (byte)0), Is.EqualTo(2 - (A * B))); - - CompareAgainstUnicorn(); - } - - [Test, Description("FRECPS V4.4S, V2.4S, V0.4S")] - public void Frecps_V([Random(10)] float A, [Random(10)] float B) - { - AThreadState ThreadState = SingleOpcode(0x4E20FC44, - V2: Sse.SetAllVector128(A), - V0: Sse.SetAllVector128(B)); - - float Result = (float)(2 - ((double)A * (double)B)); - - Assert.Multiple(() => - { - Assert.That(Sse41.Extract(ThreadState.V4, (byte)0), Is.EqualTo(Result)); - Assert.That(Sse41.Extract(ThreadState.V4, (byte)1), Is.EqualTo(Result)); - Assert.That(Sse41.Extract(ThreadState.V4, (byte)2), Is.EqualTo(Result)); - Assert.That(Sse41.Extract(ThreadState.V4, (byte)3), Is.EqualTo(Result)); - }); - - CompareAgainstUnicorn(); - } - [TestCase(0x3FE66666u, false, 0x40000000u)] [TestCase(0x3F99999Au, false, 0x3F800000u)] [TestCase(0x404CCCCDu, false, 0x40400000u)] diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdCmp.cs b/Ryujinx.Tests/Cpu/CpuTestSimdCmp.cs deleted file mode 100644 index a1558b05af..0000000000 --- a/Ryujinx.Tests/Cpu/CpuTestSimdCmp.cs +++ /dev/null @@ -1,407 +0,0 @@ -using ChocolArm64.State; - -using NUnit.Framework; - -using System; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -namespace Ryujinx.Tests.Cpu -{ - public class CpuTestSimdCmp : CpuTest - { -#region "ValueSource" - private static float[] _floats_() - { - return new float[] { float.NegativeInfinity, float.MinValue, -1f, -0f, - +0f, +1f, float.MaxValue, float.PositiveInfinity }; - } - - private static double[] _doubles_() - { - return new double[] { double.NegativeInfinity, double.MinValue, -1d, -0d, - +0d, +1d, double.MaxValue, double.PositiveInfinity }; - } -#endregion - - private const int RndCnt = 2; - - [Test, Description("FCMEQ D0, D1, D2 | FCMGE D0, D1, D2 | FCMGT D0, D1, D2")] - public void Fcmeq_Fcmge_Fcmgt_Reg_S_D([ValueSource("_doubles_")] [Random(RndCnt)] double A, - [ValueSource("_doubles_")] [Random(RndCnt)] double B, - [Values(0u, 1u, 3u)] uint EU) // EQ, GE, GT - { - uint Opcode = 0x5E62E420 | ((EU & 1) << 29) | ((EU >> 1) << 23); - - Vector128 V0 = Sse.StaticCast(Sse2.SetAllVector128(TestContext.CurrentContext.Random.NextDouble())); - Vector128 V1 = Sse.StaticCast(Sse2.SetScalarVector128(A)); - Vector128 V2 = Sse.StaticCast(Sse2.SetScalarVector128(B)); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - byte[] Exp = default(byte[]); - byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - switch (EU) - { - case 0: Exp = (A == B ? Ones : Zeros); break; - case 1: Exp = (A >= B ? Ones : Zeros); break; - case 3: Exp = (A > B ? Ones : Zeros); break; - } - - Assert.Multiple(() => - { - Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp)); - Assert.That(VectorExtractDouble(ThreadState.V0, (byte)1), Is.Zero); - }); - - CompareAgainstUnicorn(); - } - - [Test, Description("FCMEQ S0, S1, S2 | FCMGE S0, S1, S2 | FCMGT S0, S1, S2")] - public void Fcmeq_Fcmge_Fcmgt_Reg_S_S([ValueSource("_floats_")] [Random(RndCnt)] float A, - [ValueSource("_floats_")] [Random(RndCnt)] float B, - [Values(0u, 1u, 3u)] uint EU) // EQ, GE, GT - { - uint Opcode = 0x5E22E420 | ((EU & 1) << 29) | ((EU >> 1) << 23); - - Vector128 V0 = Sse.SetAllVector128(TestContext.CurrentContext.Random.NextFloat()); - Vector128 V1 = Sse.SetScalarVector128(A); - Vector128 V2 = Sse.SetScalarVector128(B); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - byte[] Exp = default(byte[]); - byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF}; - byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00}; - - switch (EU) - { - case 0: Exp = (A == B ? Ones : Zeros); break; - case 1: Exp = (A >= B ? Ones : Zeros); break; - case 3: Exp = (A > B ? Ones : Zeros); break; - } - - Assert.Multiple(() => - { - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp)); - Assert.That(Sse41.Extract(ThreadState.V0, (byte)1), Is.Zero); - Assert.That(Sse41.Extract(ThreadState.V0, (byte)2), Is.Zero); - Assert.That(Sse41.Extract(ThreadState.V0, (byte)3), Is.Zero); - }); - - CompareAgainstUnicorn(); - } - - [Test, Description("FCMEQ V0.2D, V1.2D, V2.2D | FCMGE V0.2D, V1.2D, V2.2D | FCMGT V0.2D, V1.2D, V2.2D")] - public void Fcmeq_Fcmge_Fcmgt_Reg_V_2D([ValueSource("_doubles_")] [Random(RndCnt)] double A, - [ValueSource("_doubles_")] [Random(RndCnt)] double B, - [Values(0u, 1u, 3u)] uint EU) // EQ, GE, GT - { - uint Opcode = 0x4E62E420 | ((EU & 1) << 29) | ((EU >> 1) << 23); - - Vector128 V1 = Sse.StaticCast(Sse2.SetAllVector128(A)); - Vector128 V2 = Sse.StaticCast(Sse2.SetAllVector128(B)); - - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); - - byte[] Exp = default(byte[]); - byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - switch (EU) - { - case 0: Exp = (A == B ? Ones : Zeros); break; - case 1: Exp = (A >= B ? Ones : Zeros); break; - case 3: Exp = (A > B ? Ones : Zeros); break; - } - - Assert.Multiple(() => - { - Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp)); - Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)1)), Is.EquivalentTo(Exp)); - }); - - CompareAgainstUnicorn(); - } - - [Test, Description("FCMEQ V0.2S, V1.2S, V2.2S | FCMGE V0.2S, V1.2S, V2.2S | FCMGT V0.2S, V1.2S, V2.2S")] - public void Fcmeq_Fcmge_Fcmgt_Reg_V_2S([ValueSource("_floats_")] [Random(RndCnt)] float A, - [ValueSource("_floats_")] [Random(RndCnt)] float B, - [Values(0u, 1u, 3u)] uint EU) // EQ, GE, GT - { - uint Opcode = 0x0E22E420 | ((EU & 1) << 29) | ((EU >> 1) << 23); - - Vector128 V0 = Sse.SetAllVector128(TestContext.CurrentContext.Random.NextFloat()); - Vector128 V1 = Sse.SetVector128(0, 0, A, A); - Vector128 V2 = Sse.SetVector128(0, 0, B, B); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - byte[] Exp = default(byte[]); - byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF}; - byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00}; - - switch (EU) - { - case 0: Exp = (A == B ? Ones : Zeros); break; - case 1: Exp = (A >= B ? Ones : Zeros); break; - case 3: Exp = (A > B ? Ones : Zeros); break; - } - - Assert.Multiple(() => - { - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp)); - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)1)), Is.EquivalentTo(Exp)); - Assert.That(Sse41.Extract(ThreadState.V0, (byte)2), Is.Zero); - Assert.That(Sse41.Extract(ThreadState.V0, (byte)3), Is.Zero); - }); - - CompareAgainstUnicorn(); - } - - [Test, Description("FCMEQ V0.4S, V1.4S, V2.4S | FCMGE V0.4S, V1.4S, V2.4S | FCMGT V0.4S, V1.4S, V2.4S")] - public void Fcmeq_Fcmge_Fcmgt_Reg_V_4S([ValueSource("_floats_")] [Random(RndCnt)] float A, - [ValueSource("_floats_")] [Random(RndCnt)] float B, - [Values(0u, 1u, 3u)] uint EU) // EQ, GE, GT - { - uint Opcode = 0x4E22E420 | ((EU & 1) << 29) | ((EU >> 1) << 23); - - Vector128 V1 = Sse.SetAllVector128(A); - Vector128 V2 = Sse.SetAllVector128(B); - - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1, V2: V2); - - byte[] Exp = default(byte[]); - byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF}; - byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00}; - - switch (EU) - { - case 0: Exp = (A == B ? Ones : Zeros); break; - case 1: Exp = (A >= B ? Ones : Zeros); break; - case 3: Exp = (A > B ? Ones : Zeros); break; - } - - Assert.Multiple(() => - { - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp)); - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)1)), Is.EquivalentTo(Exp)); - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)2)), Is.EquivalentTo(Exp)); - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)3)), Is.EquivalentTo(Exp)); - }); - - CompareAgainstUnicorn(); - } - - [Test, Description("FCMGT D0, D1, #0.0 | FCMGE D0, D1, #0.0 | FCMEQ D0, D1, #0.0 | FCMLE D0, D1, #0.0 | FCMLT D0, D1, #0.0")] - public void Fcmgt_Fcmge_Fcmeq_Fcmle_Fcmlt_Zero_S_D([ValueSource("_doubles_")] [Random(RndCnt)] double A, - [Values(0u, 1u, 2u, 3u)] uint opU, // GT, GE, EQ, LE - [Values(0u, 1u)] uint bit13) // "LT" - { - uint Opcode = 0x5EE0C820 | (((opU & 1) & ~bit13) << 29) | (bit13 << 13) | (((opU >> 1) & ~bit13) << 12); - - Vector128 V0 = Sse.StaticCast(Sse2.SetAllVector128(TestContext.CurrentContext.Random.NextDouble())); - Vector128 V1 = Sse.StaticCast(Sse2.SetScalarVector128(A)); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - - double Zero = +0d; - byte[] Exp = default(byte[]); - byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - if (bit13 == 0) - { - switch (opU) - { - case 0: Exp = (A > Zero ? Ones : Zeros); break; - case 1: Exp = (A >= Zero ? Ones : Zeros); break; - case 2: Exp = (A == Zero ? Ones : Zeros); break; - case 3: Exp = (Zero >= A ? Ones : Zeros); break; - } - } - else - { - Exp = (Zero > A ? Ones : Zeros); - } - - Assert.Multiple(() => - { - Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp)); - Assert.That(VectorExtractDouble(ThreadState.V0, (byte)1), Is.Zero); - }); - - CompareAgainstUnicorn(); - } - - [Test, Description("FCMGT S0, S1, #0.0 | FCMGE S0, S1, #0.0 | FCMEQ S0, S1, #0.0 | FCMLE S0, S1, #0.0 | FCMLT S0, S1, #0.0")] - public void Fcmgt_Fcmge_Fcmeq_Fcmle_Fcmlt_Zero_S_S([ValueSource("_floats_")] [Random(RndCnt)] float A, - [Values(0u, 1u, 2u, 3u)] uint opU, // GT, GE, EQ, LE - [Values(0u, 1u)] uint bit13) // "LT" - { - uint Opcode = 0x5EA0C820 | (((opU & 1) & ~bit13) << 29) | (bit13 << 13) | (((opU >> 1) & ~bit13) << 12); - - Vector128 V0 = Sse.SetAllVector128(TestContext.CurrentContext.Random.NextFloat()); - Vector128 V1 = Sse.SetScalarVector128(A); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - - float Zero = +0f; - byte[] Exp = default(byte[]); - byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF}; - byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00}; - - if (bit13 == 0) - { - switch (opU) - { - case 0: Exp = (A > Zero ? Ones : Zeros); break; - case 1: Exp = (A >= Zero ? Ones : Zeros); break; - case 2: Exp = (A == Zero ? Ones : Zeros); break; - case 3: Exp = (Zero >= A ? Ones : Zeros); break; - } - } - else - { - Exp = (Zero > A ? Ones : Zeros); - } - - Assert.Multiple(() => - { - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp)); - Assert.That(Sse41.Extract(ThreadState.V0, (byte)1), Is.Zero); - Assert.That(Sse41.Extract(ThreadState.V0, (byte)2), Is.Zero); - Assert.That(Sse41.Extract(ThreadState.V0, (byte)3), Is.Zero); - }); - - CompareAgainstUnicorn(); - } - - [Test, Description("FCMGT V0.2D, V1.2D, #0.0 | FCMGE V0.2D, V1.2D, #0.0 | FCMEQ V0.2D, V1.2D, #0.0 | FCMLE V0.2D, V1.2D, #0.0 | FCMLT V0.2D, V1.2D, #0.0")] - public void Fcmgt_Fcmge_Fcmeq_Fcmle_Fcmlt_Zero_V_2D([ValueSource("_doubles_")] [Random(RndCnt)] double A, - [Values(0u, 1u, 2u, 3u)] uint opU, // GT, GE, EQ, LE - [Values(0u, 1u)] uint bit13) // "LT" - { - uint Opcode = 0x4EE0C820 | (((opU & 1) & ~bit13) << 29) | (bit13 << 13) | (((opU >> 1) & ~bit13) << 12); - - Vector128 V1 = Sse.StaticCast(Sse2.SetAllVector128(A)); - - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); - - double Zero = +0d; - byte[] Exp = default(byte[]); - byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - if (bit13 == 0) - { - switch (opU) - { - case 0: Exp = (A > Zero ? Ones : Zeros); break; - case 1: Exp = (A >= Zero ? Ones : Zeros); break; - case 2: Exp = (A == Zero ? Ones : Zeros); break; - case 3: Exp = (Zero >= A ? Ones : Zeros); break; - } - } - else - { - Exp = (Zero > A ? Ones : Zeros); - } - - Assert.Multiple(() => - { - Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp)); - Assert.That(BitConverter.GetBytes(VectorExtractDouble(ThreadState.V0, (byte)1)), Is.EquivalentTo(Exp)); - }); - - CompareAgainstUnicorn(); - } - - [Test, Description("FCMGT V0.2S, V1.2S, #0.0 | FCMGE V0.2S, V1.2S, #0.0 | FCMEQ V0.2S, V1.2S, #0.0 | FCMLE V0.2S, V1.2S, #0.0 | FCMLT V0.2S, V1.2S, #0.0")] - public void Fcmgt_Fcmge_Fcmeq_Fcmle_Fcmlt_Zero_V_2S([ValueSource("_floats_")] [Random(RndCnt)] float A, - [Values(0u, 1u, 2u, 3u)] uint opU, // GT, GE, EQ, LE - [Values(0u, 1u)] uint bit13) // "LT" - { - uint Opcode = 0x0EA0C820 | (((opU & 1) & ~bit13) << 29) | (bit13 << 13) | (((opU >> 1) & ~bit13) << 12); - - Vector128 V0 = Sse.SetAllVector128(TestContext.CurrentContext.Random.NextFloat()); - Vector128 V1 = Sse.SetVector128(0, 0, A, A); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); - - float Zero = +0f; - byte[] Exp = default(byte[]); - byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF}; - byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00}; - - if (bit13 == 0) - { - switch (opU) - { - case 0: Exp = (A > Zero ? Ones : Zeros); break; - case 1: Exp = (A >= Zero ? Ones : Zeros); break; - case 2: Exp = (A == Zero ? Ones : Zeros); break; - case 3: Exp = (Zero >= A ? Ones : Zeros); break; - } - } - else - { - Exp = (Zero > A ? Ones : Zeros); - } - - Assert.Multiple(() => - { - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp)); - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)1)), Is.EquivalentTo(Exp)); - Assert.That(Sse41.Extract(ThreadState.V0, (byte)2), Is.Zero); - Assert.That(Sse41.Extract(ThreadState.V0, (byte)3), Is.Zero); - }); - - CompareAgainstUnicorn(); - } - - [Test, Description("FCMGT V0.4S, V1.4S, #0.0 | FCMGE V0.4S, V1.4S, #0.0 | FCMEQ V0.4S, V1.4S, #0.0 | FCMLE V0.4S, V1.4S, #0.0 | FCMLT V0.4S, V1.4S, #0.0")] - public void Fcmgt_Fcmge_Fcmeq_Fcmle_Fcmlt_Zero_V_4S([ValueSource("_floats_")] [Random(RndCnt)] float A, - [Values(0u, 1u, 2u, 3u)] uint opU, // GT, GE, EQ, LE - [Values(0u, 1u)] uint bit13) // "LT" - { - uint Opcode = 0x4EA0C820 | (((opU & 1) & ~bit13) << 29) | (bit13 << 13) | (((opU >> 1) & ~bit13) << 12); - - Vector128 V1 = Sse.SetAllVector128(A); - - AThreadState ThreadState = SingleOpcode(Opcode, V1: V1); - - float Zero = +0f; - byte[] Exp = default(byte[]); - byte[] Ones = new byte[] {0xFF, 0xFF, 0xFF, 0xFF}; - byte[] Zeros = new byte[] {0x00, 0x00, 0x00, 0x00}; - - if (bit13 == 0) - { - switch (opU) - { - case 0: Exp = (A > Zero ? Ones : Zeros); break; - case 1: Exp = (A >= Zero ? Ones : Zeros); break; - case 2: Exp = (A == Zero ? Ones : Zeros); break; - case 3: Exp = (Zero >= A ? Ones : Zeros); break; - } - } - else - { - Exp = (Zero > A ? Ones : Zeros); - } - - Assert.Multiple(() => - { - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)0)), Is.EquivalentTo(Exp)); - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)1)), Is.EquivalentTo(Exp)); - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)2)), Is.EquivalentTo(Exp)); - Assert.That(BitConverter.GetBytes(Sse41.Extract(ThreadState.V0, (byte)3)), Is.EquivalentTo(Exp)); - }); - - CompareAgainstUnicorn(); - } - } -} diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs index ae409a6d85..7d47416f0c 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs @@ -80,14 +80,14 @@ namespace Ryujinx.Tests.Cpu private static IEnumerable _1S_F_() { - yield return 0x00000000FF7FFFFFul; // -Max Normal (float.MinValue) + yield return 0x00000000FF7FFFFFul; // -Max Normal (float.MinValue) yield return 0x0000000080800000ul; // -Min Normal - yield return 0x00000000807FFFFFul; // -Max SubNormal - yield return 0x0000000080000001ul; // -Min SubNormal - yield return 0x000000007F7FFFFFul; // +Max Normal (float.MaxValue) + yield return 0x00000000807FFFFFul; // -Max Subnormal + yield return 0x0000000080000001ul; // -Min Subnormal (-float.Epsilon) + yield return 0x000000007F7FFFFFul; // +Max Normal (float.MaxValue) yield return 0x0000000000800000ul; // +Min Normal - yield return 0x00000000007FFFFFul; // +Max SubNormal - yield return 0x0000000000000001ul; // +Min SubNormal + yield return 0x00000000007FFFFFul; // +Max Subnormal + yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon) if (!NoZeros) { @@ -103,17 +103,17 @@ namespace Ryujinx.Tests.Cpu if (!NoNaNs) { - yield return 0x00000000FFFFFFFFul; // -QNaN (all ones payload) - yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) - yield return 0x000000007FFFFFFFul; // +QNaN (all ones payload) - yield return 0x000000007FBFFFFFul; // +SNaN (all ones payload) + yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) + yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) + yield return 0x000000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN) + yield return 0x000000007FBFFFFFul; // +SNaN (all ones payload) } for (int Cnt = 1; Cnt <= RndCnt; Cnt++) { ulong Grbg = TestContext.CurrentContext.Random.NextUInt(); ulong Rnd1 = GenNormal_S(); - ulong Rnd2 = GenSubNormal_S(); + ulong Rnd2 = GenSubnormal_S(); yield return (Grbg << 32) | Rnd1; yield return (Grbg << 32) | Rnd2; @@ -122,14 +122,14 @@ namespace Ryujinx.Tests.Cpu private static IEnumerable _2S_F_() { - yield return 0xFF7FFFFFFF7FFFFFul; // -Max Normal (float.MinValue) + yield return 0xFF7FFFFFFF7FFFFFul; // -Max Normal (float.MinValue) yield return 0x8080000080800000ul; // -Min Normal - yield return 0x807FFFFF807FFFFFul; // -Max SubNormal - yield return 0x8000000180000001ul; // -Min SubNormal - yield return 0x7F7FFFFF7F7FFFFFul; // +Max Normal (float.MaxValue) + yield return 0x807FFFFF807FFFFFul; // -Max Subnormal + yield return 0x8000000180000001ul; // -Min Subnormal (-float.Epsilon) + yield return 0x7F7FFFFF7F7FFFFFul; // +Max Normal (float.MaxValue) yield return 0x0080000000800000ul; // +Min Normal - yield return 0x007FFFFF007FFFFFul; // +Max SubNormal - yield return 0x0000000100000001ul; // +Min SubNormal + yield return 0x007FFFFF007FFFFFul; // +Max Subnormal + yield return 0x0000000100000001ul; // +Min Subnormal (float.Epsilon) if (!NoZeros) { @@ -145,16 +145,16 @@ namespace Ryujinx.Tests.Cpu if (!NoNaNs) { - yield return 0xFFFFFFFFFFFFFFFFul; // -QNaN (all ones payload) - yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload) - yield return 0x7FFFFFFF7FFFFFFFul; // +QNaN (all ones payload) - yield return 0x7FBFFFFF7FBFFFFFul; // +SNaN (all ones payload) + yield return 0xFFC00000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) + yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload) + yield return 0x7FC000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN) + yield return 0x7FBFFFFF7FBFFFFFul; // +SNaN (all ones payload) } for (int Cnt = 1; Cnt <= RndCnt; Cnt++) { ulong Rnd1 = GenNormal_S(); - ulong Rnd2 = GenSubNormal_S(); + ulong Rnd2 = GenSubnormal_S(); yield return (Rnd1 << 32) | Rnd1; yield return (Rnd2 << 32) | Rnd2; @@ -163,14 +163,14 @@ namespace Ryujinx.Tests.Cpu private static IEnumerable _1D_F_() { - yield return 0xFFEFFFFFFFFFFFFFul; // -Max Normal (double.MinValue) + yield return 0xFFEFFFFFFFFFFFFFul; // -Max Normal (double.MinValue) yield return 0x8010000000000000ul; // -Min Normal - yield return 0x800FFFFFFFFFFFFFul; // -Max SubNormal - yield return 0x8000000000000001ul; // -Min SubNormal - yield return 0x7FEFFFFFFFFFFFFFul; // +Max Normal (double.MaxValue) + yield return 0x800FFFFFFFFFFFFFul; // -Max Subnormal + yield return 0x8000000000000001ul; // -Min Subnormal (-double.Epsilon) + yield return 0x7FEFFFFFFFFFFFFFul; // +Max Normal (double.MaxValue) yield return 0x0010000000000000ul; // +Min Normal - yield return 0x000FFFFFFFFFFFFFul; // +Max SubNormal - yield return 0x0000000000000001ul; // +Min SubNormal + yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal + yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon) if (!NoZeros) { @@ -186,16 +186,16 @@ namespace Ryujinx.Tests.Cpu if (!NoNaNs) { - yield return 0xFFFFFFFFFFFFFFFFul; // -QNaN (all ones payload) - yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) - yield return 0x7FFFFFFFFFFFFFFFul; // +QNaN (all ones payload) - yield return 0x7FF7FFFFFFFFFFFFul; // +SNaN (all ones payload) + yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN) + yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) + yield return 0x7FF8000000000000ul; // +QNaN (all zeros payload) (-double.NaN) (DefaultNaN) + yield return 0x7FF7FFFFFFFFFFFFul; // +SNaN (all ones payload) } for (int Cnt = 1; Cnt <= RndCnt; Cnt++) { ulong Rnd1 = GenNormal_D(); - ulong Rnd2 = GenSubNormal_D(); + ulong Rnd2 = GenSubnormal_D(); yield return Rnd1; yield return Rnd2; @@ -204,6 +204,72 @@ namespace Ryujinx.Tests.Cpu #endregion #region "ValueSource (Opcodes)" + private static uint[] _F_Add_Div_Mul_MulX_Sub_S_S_() + { + return new uint[] + { + 0x1E222820u, // FADD S0, S1, S2 + 0x1E221820u, // FDIV S0, S1, S2 + 0x1E220820u, // FMUL S0, S1, S2 + 0x5E22DC20u, // FMULX S0, S1, S2 + 0x1E223820u // FSUB S0, S1, S2 + }; + } + + private static uint[] _F_Add_Div_Mul_MulX_Sub_S_D_() + { + return new uint[] + { + 0x1E622820u, // FADD D0, D1, D2 + 0x1E621820u, // FDIV D0, D1, D2 + 0x1E620820u, // FMUL D0, D1, D2 + 0x5E62DC20u, // FMULX D0, D1, D2 + 0x1E623820u // FSUB D0, D1, D2 + }; + } + + private static uint[] _F_Add_Div_Mul_MulX_Sub_V_2S_4S_() + { + return new uint[] + { + 0x0E20D400u, // FADD V0.2S, V0.2S, V0.2S + 0x2E20FC00u, // FDIV V0.2S, V0.2S, V0.2S + 0x2E20DC00u, // FMUL V0.2S, V0.2S, V0.2S + 0x0E20DC00u, // FMULX V0.2S, V0.2S, V0.2S + 0x0EA0D400u // FSUB V0.2S, V0.2S, V0.2S + }; + } + + private static uint[] _F_Add_Div_Mul_MulX_Sub_V_2D_() + { + return new uint[] + { + 0x4E60D400u, // FADD V0.2D, V0.2D, V0.2D + 0x6E60FC00u, // FDIV V0.2D, V0.2D, V0.2D + 0x6E60DC00u, // FMUL V0.2D, V0.2D, V0.2D + 0x4E60DC00u, // FMULX V0.2D, V0.2D, V0.2D + 0x4EE0D400u // FSUB V0.2D, V0.2D, V0.2D + }; + } + + private static uint[] _Fmadd_Fmsub_S_S_() + { + return new uint[] + { + 0x1F020C20u, // FMADD S0, S1, S2, S3 + 0x1F028C20u // FMSUB S0, S1, S2, S3 + }; + } + + private static uint[] _Fmadd_Fmsub_S_D_() + { + return new uint[] + { + 0x1F420C20u, // FMADD D0, D1, D2, D3 + 0x1F428C20u // FMSUB D0, D1, D2, D3 + }; + } + private static uint[] _F_Max_Min_Nm_S_S_() { return new uint[] @@ -251,6 +317,42 @@ namespace Ryujinx.Tests.Cpu 0x6EE0F400u // FMINP V0.2D, V0.2D, V0.2D }; } + + private static uint[] _Frecps_Frsqrts_S_S_() + { + return new uint[] + { + 0x5E22FC20u, // FRECPS S0, S1, S2 + 0x5EA2FC20u // FRSQRTS S0, S1, S2 + }; + } + + private static uint[] _Frecps_Frsqrts_S_D_() + { + return new uint[] + { + 0x5E62FC20u, // FRECPS D0, D1, D2 + 0x5EE2FC20u // FRSQRTS D0, D1, D2 + }; + } + + private static uint[] _Frecps_Frsqrts_V_2S_4S_() + { + return new uint[] + { + 0x0E20FC00u, // FRECPS V0.2S, V0.2S, V0.2S + 0x0EA0FC00u // FRSQRTS V0.2S, V0.2S, V0.2S + }; + } + + private static uint[] _Frecps_Frsqrts_V_2D_() + { + return new uint[] + { + 0x4E60FC00u, // FRECPS V0.2D, V0.2D, V0.2D + 0x4EE0FC00u // FRSQRTS V0.2D, V0.2D, V0.2D + }; + } #endregion private const int RndCnt = 2; @@ -1035,46 +1137,122 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise, Description("FMADD , , , ")] - public void Fmadd_S_S([ValueSource("_1S_F_")] ulong A, - [ValueSource("_1S_F_")] ulong B, - [ValueSource("_1S_F_")] ulong C) + [Test, Pairwise] + public void F_Add_Div_Mul_MulX_Sub_S_S([ValueSource("_F_Add_Div_Mul_MulX_Sub_S_S_")] uint Opcodes, + [ValueSource("_1S_F_")] ulong A, + [ValueSource("_1S_F_")] ulong B) { - //const int DNFlagBit = 25; // Default NaN mode control bit. - //const int FZFlagBit = 24; // Flush-to-zero mode control bit. + ulong Z = TestContext.CurrentContext.Random.NextULong(); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); - uint Opcode = 0x1F020C20; // FMADD S0, S1, S2, S3 + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + CompareAgainstUnicorn(FpsrMask: FPSR.IOC | FPSR.DZC); + } + + [Test, Pairwise] + public void F_Add_Div_Mul_MulX_Sub_S_D([ValueSource("_F_Add_Div_Mul_MulX_Sub_S_D_")] uint Opcodes, + [ValueSource("_1D_F_")] ulong A, + [ValueSource("_1D_F_")] ulong B) + { + ulong Z = TestContext.CurrentContext.Random.NextULong(); + Vector128 V0 = MakeVectorE1(Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + CompareAgainstUnicorn(FpsrMask: FPSR.IOC | FPSR.DZC); + } + + [Test, Pairwise] + public void F_Add_Div_Mul_MulX_Sub_V_2S_4S([ValueSource("_F_Add_Div_Mul_MulX_Sub_V_2S_4S_")] uint Opcodes, + [Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_2S_F_")] ulong Z, + [ValueSource("_2S_F_")] ulong A, + [ValueSource("_2S_F_")] ulong B, + [Values(0b0u, 0b1u)] uint Q) // <2S, 4S> + { + Opcodes |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcodes |= ((Q & 1) << 30); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A * Q); + Vector128 V2 = MakeVectorE0E1(B, B * Q); + + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + CompareAgainstUnicorn(FpsrMask: FPSR.IOC | FPSR.DZC); + } + + [Test, Pairwise] + public void F_Add_Div_Mul_MulX_Sub_V_2D([ValueSource("_F_Add_Div_Mul_MulX_Sub_V_2D_")] uint Opcodes, + [Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_1D_F_")] ulong Z, + [ValueSource("_1D_F_")] ulong A, + [ValueSource("_1D_F_")] ulong B) + { + Opcodes |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); + + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + CompareAgainstUnicorn(FpsrMask: FPSR.IOC | FPSR.DZC); + } + + [Test, Pairwise] // Fused. + public void Fmadd_Fmsub_S_S([ValueSource("_Fmadd_Fmsub_S_S_")] uint Opcodes, + [ValueSource("_1S_F_")] ulong A, + [ValueSource("_1S_F_")] ulong B, + [ValueSource("_1S_F_")] ulong C) + { ulong Z = TestContext.CurrentContext.Random.NextULong(); Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); Vector128 V3 = MakeVectorE0(C); - //int Fpcr = 1 << DNFlagBit; // Any operation involving one or more NaNs returns the Default NaN. - //Fpcr |= 1 << FZFlagBit; // Flush-to-zero mode enabled. + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, V3: V3/*, Fpcr: Fpcr*/); + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, V3: V3, Fpcr: Fpcr); - CompareAgainstUnicorn(/*FpsrMask: FPSR.IDC | FPSR.IOC, */FpSkips: FpSkips.IfNaN_S/*, FpUseTolerance: FpUseTolerance.OneUlps_S*/); + CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_S); } - [Test, Pairwise, Description("FMADD
, , , ")] - public void Fmadd_S_D([ValueSource("_1D_F_")] ulong A, - [ValueSource("_1D_F_")] ulong B, - [ValueSource("_1D_F_")] ulong C) + [Test, Pairwise] // Fused. + public void Fmadd_Fmsub_S_D([ValueSource("_Fmadd_Fmsub_S_D_")] uint Opcodes, + [ValueSource("_1D_F_")] ulong A, + [ValueSource("_1D_F_")] ulong B, + [ValueSource("_1D_F_")] ulong C) { - uint Opcode = 0x1F420C20; // FMADD D0, D1, D2, D3 - ulong Z = TestContext.CurrentContext.Random.NextULong(); Vector128 V0 = MakeVectorE1(Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); Vector128 V3 = MakeVectorE0(C); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2, V3: V3); + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); - CompareAgainstUnicorn(FpSkips: FpSkips.IfNaN_D/*, FpUseTolerance: FpUseTolerance.OneUlps_D*/); + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, V3: V3, Fpcr: Fpcr); + + CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_D); } [Test, Pairwise] @@ -1082,20 +1260,16 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_1S_F_")] ulong A, [ValueSource("_1S_F_")] ulong B) { - //const int DNFlagBit = 25; // Default NaN mode control bit. - //const int FZFlagBit = 24; // Flush-to-zero mode control bit. - ulong Z = TestContext.CurrentContext.Random.NextULong(); Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); - //int Fpcr = 1 << DNFlagBit; // Any operation involving one or more NaNs returns the Default NaN. - //Fpcr |= 1 << FZFlagBit; // Flush-to-zero mode enabled. + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); - AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2/*, Fpcr: Fpcr*/); + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); - CompareAgainstUnicorn(/*FpsrMask: FPSR.IDC | FPSR.IOC*/); + CompareAgainstUnicorn(FpsrMask: FPSR.IOC); } [Test, Pairwise] @@ -1108,9 +1282,11 @@ namespace Ryujinx.Tests.Cpu Vector128 V1 = MakeVectorE0(A); Vector128 V2 = MakeVectorE0(B); - AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2); + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); - CompareAgainstUnicorn(); + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + CompareAgainstUnicorn(FpsrMask: FPSR.IOC); } [Test, Pairwise] @@ -1123,9 +1299,6 @@ namespace Ryujinx.Tests.Cpu [ValueSource("_2S_F_")] ulong B, [Values(0b0u, 0b1u)] uint Q) // <2S, 4S> { - //const int DNFlagBit = 25; // Default NaN mode control bit. - //const int FZFlagBit = 24; // Flush-to-zero mode control bit. - Opcodes |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); Opcodes |= ((Q & 1) << 30); @@ -1133,12 +1306,11 @@ namespace Ryujinx.Tests.Cpu Vector128 V1 = MakeVectorE0E1(A, A * Q); Vector128 V2 = MakeVectorE0E1(B, B * Q); - //int Fpcr = 1 << DNFlagBit; // Any operation involving one or more NaNs returns the Default NaN. - //Fpcr |= 1 << FZFlagBit; // Flush-to-zero mode enabled. + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); - AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2/*, Fpcr: Fpcr*/); + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); - CompareAgainstUnicorn(/*FpsrMask: FPSR.IDC | FPSR.IOC*/); + CompareAgainstUnicorn(FpsrMask: FPSR.IOC); } [Test, Pairwise] @@ -1156,9 +1328,91 @@ namespace Ryujinx.Tests.Cpu Vector128 V1 = MakeVectorE0E1(A, A); Vector128 V2 = MakeVectorE0E1(B, B); - AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2); + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); - CompareAgainstUnicorn(); + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + CompareAgainstUnicorn(FpsrMask: FPSR.IOC); + } + + [Test, Pairwise] // Fused. + public void Frecps_Frsqrts_S_S([ValueSource("_Frecps_Frsqrts_S_S_")] uint Opcodes, + [ValueSource("_1S_F_")] ulong A, + [ValueSource("_1S_F_")] ulong B) + { + ulong Z = TestContext.CurrentContext.Random.NextULong(); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_S); + } + + [Test, Pairwise] // Fused. + public void Frecps_Frsqrts_S_D([ValueSource("_Frecps_Frsqrts_S_D_")] uint Opcodes, + [ValueSource("_1D_F_")] ulong A, + [ValueSource("_1D_F_")] ulong B) + { + ulong Z = TestContext.CurrentContext.Random.NextULong(); + Vector128 V0 = MakeVectorE1(Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0(B); + + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_D); + } + + [Test, Pairwise] // Fused. + public void Frecps_Frsqrts_V_2S_4S([ValueSource("_Frecps_Frsqrts_V_2S_4S_")] uint Opcodes, + [Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_2S_F_")] ulong Z, + [ValueSource("_2S_F_")] ulong A, + [ValueSource("_2S_F_")] ulong B, + [Values(0b0u, 0b1u)] uint Q) // <2S, 4S> + { + Opcodes |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcodes |= ((Q & 1) << 30); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A * Q); + Vector128 V2 = MakeVectorE0E1(B, B * Q); + + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_S); + } + + [Test, Pairwise] // Fused. + public void Frecps_Frsqrts_V_2D([ValueSource("_Frecps_Frsqrts_V_2D_")] uint Opcodes, + [Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_1D_F_")] ulong Z, + [ValueSource("_1D_F_")] ulong A, + [ValueSource("_1D_F_")] ulong B) + { + Opcodes |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B); + + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_D); } [Test, Pairwise, Description("ORN ., ., .")] From 5821ff675dba32458193ea0a4d9b4657dfe949c2 Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Sat, 6 Oct 2018 09:11:47 -0600 Subject: [PATCH 10/30] Misc content loading improvements (#432) * Misc content loading improvements --- Ryujinx.HLE/HOS/Horizon.cs | 32 ++++++++++++++++++++------------ Ryujinx.HLE/Ryujinx.HLE.csproj | 2 +- Ryujinx/Config.cs | 2 ++ Ryujinx/Ryujinx.conf | 3 +++ 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index d52c8af0a2..a3eaae4fcd 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -51,6 +51,8 @@ namespace Ryujinx.HLE.HOS public string CurrentTitle { get; private set; } + public bool EnableFsIntegrityChecks { get; set; } + public Horizon(Switch Device) { this.Device = Device; @@ -220,12 +222,19 @@ namespace Ryujinx.HLE.HOS ReadControlData(ControlNca); } + if (PatchNca != null) + { + PatchNca.SetBaseNca(MainNca); + + return (PatchNca, ControlNca); + } + return (MainNca, ControlNca); } public void ReadControlData(Nca ControlNca) { - Romfs ControlRomfs = new Romfs(ControlNca.OpenSection(0, false)); + Romfs ControlRomfs = new Romfs(ControlNca.OpenSection(0, false, EnableFsIntegrityChecks)); byte[] ControlFile = ControlRomfs.GetFile("/control.nacp"); @@ -254,8 +263,7 @@ namespace Ryujinx.HLE.HOS // Load title key from the NSP's ticket in case the user doesn't have a title key file if (TicketFile != null) { - // todo Change when Ticket(Stream) overload is added - Ticket Ticket = new Ticket(new BinaryReader(Nsp.OpenFile(TicketFile))); + Ticket Ticket = new Ticket(Nsp.OpenFile(TicketFile)); KeySet.TitleKeys[Ticket.RightsId] = Ticket.GetTitleKey(KeySet); } @@ -289,7 +297,7 @@ namespace Ryujinx.HLE.HOS public void LoadNca(Nca MainNca, Nca ControlNca) { - NcaSection RomfsSection = MainNca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs); + NcaSection RomfsSection = MainNca.Sections.FirstOrDefault(x => x?.Type == SectionType.Romfs || x?.Type == SectionType.Bktr); NcaSection ExefsSection = MainNca.Sections.FirstOrDefault(x => x?.IsExefs == true); if (ExefsSection == null) @@ -301,16 +309,16 @@ namespace Ryujinx.HLE.HOS if (RomfsSection == null) { - Device.Log.PrintError(LogClass.Loader, "No RomFS found in NCA"); + Device.Log.PrintWarning(LogClass.Loader, "No RomFS found in NCA"); + } + else + { + Stream RomfsStream = MainNca.OpenSection(RomfsSection.SectionNum, false, EnableFsIntegrityChecks); - return; + Device.FileSystem.SetRomFs(RomfsStream); } - Stream RomfsStream = MainNca.OpenSection(RomfsSection.SectionNum, false); - - Device.FileSystem.SetRomFs(RomfsStream); - - Stream ExefsStream = MainNca.OpenSection(ExefsSection.SectionNum, false); + Stream ExefsStream = MainNca.OpenSection(ExefsSection.SectionNum, false, EnableFsIntegrityChecks); Pfs Exefs = new Pfs(ExefsStream); @@ -350,7 +358,7 @@ namespace Ryujinx.HLE.HOS Nacp ReadControlData() { - Romfs ControlRomfs = new Romfs(ControlNca.OpenSection(0, false)); + Romfs ControlRomfs = new Romfs(ControlNca.OpenSection(0, false, EnableFsIntegrityChecks)); byte[] ControlFile = ControlRomfs.GetFile("/control.nacp"); diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj index cd1bb03458..d24cd83311 100644 --- a/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/Ryujinx.HLE/Ryujinx.HLE.csproj @@ -25,7 +25,7 @@ - + diff --git a/Ryujinx/Config.cs b/Ryujinx/Config.cs index f4dd77ba4e..197a8cad65 100644 --- a/Ryujinx/Config.cs +++ b/Ryujinx/Config.cs @@ -68,6 +68,8 @@ namespace Ryujinx Device.System.EnableMultiCoreScheduling(); } + Device.System.EnableFsIntegrityChecks = Convert.ToBoolean(Parser.Value("Enable_FS_Integrity_Checks")); + JoyConKeyboard = new JoyConKeyboard( new JoyConKeyboardLeft diff --git a/Ryujinx/Ryujinx.conf b/Ryujinx/Ryujinx.conf index bf361db3e5..45f545d00e 100644 --- a/Ryujinx/Ryujinx.conf +++ b/Ryujinx/Ryujinx.conf @@ -31,6 +31,9 @@ Enable_Vsync = true #Enable or Disable Multi-core scheduling of threads Enable_MultiCore_Scheduling = false +#Enable integrity checks on Switch content files +Enable_FS_Integrity_Checks = true + #Controller Device Index GamePad_Index = 0 From 5b8ccb717f225234ae97a2ef1673ca42833bd836 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Sun, 7 Oct 2018 00:16:42 +0200 Subject: [PATCH 11/30] Implement ISslContext (#440) This PR implement an empty `ISslContext` and update `CreateContext` and `SetInterfaceVersion` inside `ISslService` --- Ryujinx.HLE/HOS/Services/Ssl/ISslContext.cs | 20 ++++++++++++++++++++ Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs | 17 ++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 Ryujinx.HLE/HOS/Services/Ssl/ISslContext.cs diff --git a/Ryujinx.HLE/HOS/Services/Ssl/ISslContext.cs b/Ryujinx.HLE/HOS/Services/Ssl/ISslContext.cs new file mode 100644 index 0000000000..8f3a0649b2 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Ssl/ISslContext.cs @@ -0,0 +1,20 @@ +using Ryujinx.HLE.HOS.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Ssl +{ + class ISslContext : IpcService + { + private Dictionary m_Commands; + + public override IReadOnlyDictionary Commands => m_Commands; + + public ISslContext() + { + m_Commands = new Dictionary() + { + //... + }; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs b/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs index b59527f707..5affc63689 100644 --- a/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs +++ b/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs @@ -14,15 +14,30 @@ namespace Ryujinx.HLE.HOS.Services.Ssl { m_Commands = new Dictionary() { + { 0, CreateContext }, { 5, SetInterfaceVersion } }; } + // CreateContext(nn::ssl::sf::SslVersion, u64, pid) -> object + public long CreateContext(ServiceCtx Context) + { + int SslVersion = Context.RequestData.ReadInt32(); + long Unknown = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceSsl, $"Stubbed. SslVersion: {SslVersion} - Unknown: {Unknown}"); + + MakeObject(Context, new ISslContext()); + + return 0; + } + + // SetInterfaceVersion(u32) public long SetInterfaceVersion(ServiceCtx Context) { int Version = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceSsl, "Stubbed."); + Context.Device.Log.PrintStub(LogClass.ServiceSsl, $"Stubbed. Version: {Version}"); return 0; } From caa181edf2c956a1433d3c2f8678231af52f9dad Mon Sep 17 00:00:00 2001 From: emmauss Date: Sun, 7 Oct 2018 16:13:46 +0300 Subject: [PATCH 12/30] Save Common implementation (#434) * save common implementation * remove zero userid check * Renamed UserId to UInt128 * fix index in hex conversion --- Ryujinx.HLE/FileSystem/SaveHelper.cs | 5 +- Ryujinx.HLE/FileSystem/SaveInfo.cs | 6 +- .../HOS/Services/Acc/IAccountService.cs | 13 ++-- .../HOS/Services/Friend/IFriendService.cs | 13 ++-- .../HOS/Services/FspSrv/IFileSystemProxy.cs | 4 +- Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs | 20 ++--- Ryujinx.HLE/HOS/SystemState/UserId.cs | 76 ------------------- Ryujinx.HLE/HOS/SystemState/UserProfile.cs | 7 +- Ryujinx.HLE/Utilities/UInt128.cs | 61 +++++++++++++++ 9 files changed, 97 insertions(+), 108 deletions(-) delete mode 100644 Ryujinx.HLE/HOS/SystemState/UserId.cs create mode 100644 Ryujinx.HLE/Utilities/UInt128.cs diff --git a/Ryujinx.HLE/FileSystem/SaveHelper.cs b/Ryujinx.HLE/FileSystem/SaveHelper.cs index 67f010169c..087156d2a0 100644 --- a/Ryujinx.HLE/FileSystem/SaveHelper.cs +++ b/Ryujinx.HLE/FileSystem/SaveHelper.cs @@ -1,5 +1,6 @@ using Ryujinx.HLE.HOS; using System.IO; +using System.Linq; using static Ryujinx.HLE.FileSystem.VirtualFileSystem; @@ -35,9 +36,11 @@ namespace Ryujinx.HLE.FileSystem } } + string SaveAccount = SaveMetaData.UserId.IsZero() ? "savecommon" : SaveMetaData.UserId.ToString(); + string SavePath = Path.Combine(BaseSavePath, SaveMetaData.SaveId.ToString("x16"), - SaveMetaData.UserId.ToString(), + SaveAccount, SaveMetaData.SaveDataType == SaveDataType.SaveData ? CurrentTitleId.ToString("x16") : string.Empty); return SavePath; diff --git a/Ryujinx.HLE/FileSystem/SaveInfo.cs b/Ryujinx.HLE/FileSystem/SaveInfo.cs index f3790ec787..3acd33fdae 100644 --- a/Ryujinx.HLE/FileSystem/SaveInfo.cs +++ b/Ryujinx.HLE/FileSystem/SaveInfo.cs @@ -1,4 +1,4 @@ -using Ryujinx.HLE.HOS.SystemState; +using Ryujinx.HLE.Utilities; namespace Ryujinx.HLE.FileSystem { @@ -6,7 +6,7 @@ namespace Ryujinx.HLE.FileSystem { public long TitleId { get; private set; } public long SaveId { get; private set; } - public UserId UserId { get; private set; } + public UInt128 UserId { get; private set; } public SaveDataType SaveDataType { get; private set; } public SaveSpaceId SaveSpaceId { get; private set; } @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.FileSystem long TitleId, long SaveId, SaveDataType SaveDataType, - UserId UserId, + UInt128 UserId, SaveSpaceId SaveSpaceId) { this.TitleId = TitleId; diff --git a/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs b/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs index 8fd7bfeafd..347f2e20c7 100644 --- a/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs +++ b/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs @@ -1,6 +1,7 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Logging; +using Ryujinx.HLE.Utilities; using System.Collections.Generic; using static Ryujinx.HLE.HOS.ErrorCode; @@ -37,7 +38,7 @@ namespace Ryujinx.HLE.HOS.Services.Acc public long GetUserExistence(ServiceCtx Context) { - UserId Uuid = new UserId( + UInt128 Uuid = new UInt128( Context.RequestData.ReadInt64(), Context.RequestData.ReadInt64()); @@ -70,12 +71,8 @@ namespace Ryujinx.HLE.HOS.Services.Acc break; } - byte[] Uuid = Profile.Uuid.Bytes; - - for (int Index = Uuid.Length - 1; Index >= 0; Index--) - { - Context.Memory.WriteByte(OutputPosition + Offset++, Uuid[Index]); - } + Context.Memory.WriteInt64(OutputPosition, Profile.Uuid.High); + Context.Memory.WriteInt64(OutputPosition + 8, Profile.Uuid.Low); } return 0; @@ -92,7 +89,7 @@ namespace Ryujinx.HLE.HOS.Services.Acc public long GetProfile(ServiceCtx Context) { - UserId Uuid = new UserId( + UInt128 Uuid = new UInt128( Context.RequestData.ReadInt64(), Context.RequestData.ReadInt64()); diff --git a/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs b/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs index d476f5d027..ebbe5fca03 100644 --- a/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs +++ b/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs @@ -1,6 +1,7 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Logging; +using Ryujinx.HLE.Utilities; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Friend @@ -24,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend // nn::friends::GetFriendListGetFriendListIds(nn::account::Uid, int Unknown0, nn::friends::detail::ipc::SizedFriendFilter, ulong Unknown1) -> int CounterIds, array public long GetFriendList(ServiceCtx Context) { - UserId Uuid = new UserId( + UInt128 Uuid = new UInt128( Context.RequestData.ReadInt64(), Context.RequestData.ReadInt64()); @@ -45,7 +46,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend // There are no friends online, so we return 0 because the nn::account::NetworkServiceAccountId array is empty. Context.ResponseData.Write(0); - Context.Device.Log.PrintStub(LogClass.ServiceFriend, $"Stubbed. UserId: {Uuid.UserIdHex} - " + + Context.Device.Log.PrintStub(LogClass.ServiceFriend, $"Stubbed. UserId: {Uuid.ToString()} - " + $"Unknown0: {Unknown0} - " + $"PresenceStatus: {Filter.PresenceStatus} - " + $"IsFavoriteOnly: {Filter.IsFavoriteOnly} - " + @@ -61,7 +62,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend // DeclareCloseOnlinePlaySession(nn::account::Uid) public long DeclareCloseOnlinePlaySession(ServiceCtx Context) { - UserId Uuid = new UserId( + UInt128 Uuid = new UInt128( Context.RequestData.ReadInt64(), Context.RequestData.ReadInt64()); @@ -70,7 +71,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend Profile.OnlinePlayState = OpenCloseState.Closed; } - Context.Device.Log.PrintStub(LogClass.ServiceFriend, $"Stubbed. Uuid: {Uuid.UserIdHex} - " + + Context.Device.Log.PrintStub(LogClass.ServiceFriend, $"Stubbed. Uuid: {Uuid.ToString()} - " + $"OnlinePlayState: {Profile.OnlinePlayState}"); return 0; @@ -79,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend // UpdateUserPresence(nn::account::Uid, ulong Unknown0) -> buffer public long UpdateUserPresence(ServiceCtx Context) { - UserId Uuid = new UserId( + UInt128 Uuid = new UInt128( Context.RequestData.ReadInt64(), Context.RequestData.ReadInt64()); @@ -90,7 +91,7 @@ namespace Ryujinx.HLE.HOS.Services.Friend //Todo: Write the buffer content. - Context.Device.Log.PrintStub(LogClass.ServiceFriend, $"Stubbed. Uuid: {Uuid.UserIdHex} - " + + Context.Device.Log.PrintStub(LogClass.ServiceFriend, $"Stubbed. Uuid: {Uuid.ToString()} - " + $"Unknown0: {Unknown0}"); return 0; diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs index 937ea6d6bf..daf5e0b269 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystemProxy.cs @@ -1,6 +1,6 @@ using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.SystemState; +using Ryujinx.HLE.Utilities; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.FspSrv @@ -78,7 +78,7 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv long TitleId = Context.RequestData.ReadInt64(); - UserId UserId = new UserId( + UInt128 UserId = new UInt128( Context.RequestData.ReadInt64(), Context.RequestData.ReadInt64()); diff --git a/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs b/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs index 2a3c8288b2..3833ce9ec9 100644 --- a/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs +++ b/Ryujinx.HLE/HOS/SystemState/SystemStateMgr.cs @@ -1,3 +1,4 @@ +using Ryujinx.HLE.Utilities; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -57,9 +58,10 @@ namespace Ryujinx.HLE.HOS.SystemState Profiles = new ConcurrentDictionary(); - UserId DefaultUuid = new UserId("00000000000000000000000000000001"); + UInt128 DefaultUuid = new UInt128("00000000000000000000000000000001"); AddUser(DefaultUuid, "Player"); + OpenUser(DefaultUuid); } @@ -85,24 +87,24 @@ namespace Ryujinx.HLE.HOS.SystemState ActiveAudioOutput = AudioOutputs[2]; } - public void AddUser(UserId Uuid, string Name) + public void AddUser(UInt128 Uuid, string Name) { UserProfile Profile = new UserProfile(Uuid, Name); - Profiles.AddOrUpdate(Uuid.UserIdHex, Profile, (Key, Old) => Profile); + Profiles.AddOrUpdate(Uuid.ToString(), Profile, (Key, Old) => Profile); } - public void OpenUser(UserId Uuid) + public void OpenUser(UInt128 Uuid) { - if (Profiles.TryGetValue(Uuid.UserIdHex, out UserProfile Profile)) + if (Profiles.TryGetValue(Uuid.ToString(), out UserProfile Profile)) { (LastOpenUser = Profile).AccountState = OpenCloseState.Open; } } - public void CloseUser(UserId Uuid) + public void CloseUser(UInt128 Uuid) { - if (Profiles.TryGetValue(Uuid.UserIdHex, out UserProfile Profile)) + if (Profiles.TryGetValue(Uuid.ToString(), out UserProfile Profile)) { Profile.AccountState = OpenCloseState.Closed; } @@ -113,9 +115,9 @@ namespace Ryujinx.HLE.HOS.SystemState return Profiles.Count; } - internal bool TryGetUser(UserId Uuid, out UserProfile Profile) + internal bool TryGetUser(UInt128 Uuid, out UserProfile Profile) { - return Profiles.TryGetValue(Uuid.UserIdHex, out Profile); + return Profiles.TryGetValue(Uuid.ToString(), out Profile); } internal IEnumerable GetAllUsers() diff --git a/Ryujinx.HLE/HOS/SystemState/UserId.cs b/Ryujinx.HLE/HOS/SystemState/UserId.cs deleted file mode 100644 index 1e7c53dd00..0000000000 --- a/Ryujinx.HLE/HOS/SystemState/UserId.cs +++ /dev/null @@ -1,76 +0,0 @@ -using Ryujinx.HLE.Utilities; -using System; -using System.IO; -using System.Linq; - -namespace Ryujinx.HLE.HOS.SystemState -{ - public struct UserId - { - public string UserIdHex { get; private set; } - - public byte[] Bytes { get; private set; } - - public UserId(long Low, long High) - { - if ((Low | High) == 0) - { - throw new ArgumentException("Zero is not a valid user id!"); - } - - byte[] Bytes = new byte[16]; - - int Index = Bytes.Length; - - void WriteBytes(long Value) - { - for (int Byte = 0; Byte < 8; Byte++) - { - Bytes[--Index] = (byte)(Value >> Byte * 8); - } - } - - WriteBytes(Low); - WriteBytes(High); - - UserIdHex = string.Empty; - - foreach (byte Byte in Bytes) - { - UserIdHex += Byte.ToString("X2"); - } - - this.Bytes = Bytes; - } - - public UserId(string UserIdHex) - { - if (UserIdHex == null || UserIdHex.Length != 32 || !UserIdHex.All("0123456789abcdefABCDEF".Contains)) - { - throw new ArgumentException("Invalid user id!", nameof(UserIdHex)); - } - - if (UserIdHex == "00000000000000000000000000000000") - { - throw new ArgumentException("Zero is not a valid user id!", nameof(UserIdHex)); - } - - this.UserIdHex = UserIdHex.ToUpper(); - - Bytes = StringUtils.HexToBytes(UserIdHex); - } - - internal void Write(BinaryWriter Writer) - { - for (int Index = Bytes.Length - 1; Index >= 0; Index--) - { - Writer.Write(Bytes[Index]); - } - } - - public override string ToString() - { - return UserIdHex; - } - } -} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/SystemState/UserProfile.cs b/Ryujinx.HLE/HOS/SystemState/UserProfile.cs index 63852cdf4f..e08bc48aa3 100644 --- a/Ryujinx.HLE/HOS/SystemState/UserProfile.cs +++ b/Ryujinx.HLE/HOS/SystemState/UserProfile.cs @@ -1,4 +1,5 @@ -using System; +using Ryujinx.HLE.Utilities; +using System; namespace Ryujinx.HLE.HOS.SystemState { @@ -6,7 +7,7 @@ namespace Ryujinx.HLE.HOS.SystemState { private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - public UserId Uuid { get; private set; } + public UInt128 Uuid { get; private set; } public string Name { get; private set; } @@ -15,7 +16,7 @@ namespace Ryujinx.HLE.HOS.SystemState public OpenCloseState AccountState { get; set; } public OpenCloseState OnlinePlayState { get; set; } - public UserProfile(UserId Uuid, string Name) + public UserProfile(UInt128 Uuid, string Name) { this.Uuid = Uuid; this.Name = Name; diff --git a/Ryujinx.HLE/Utilities/UInt128.cs b/Ryujinx.HLE/Utilities/UInt128.cs new file mode 100644 index 0000000000..a2546dd3cd --- /dev/null +++ b/Ryujinx.HLE/Utilities/UInt128.cs @@ -0,0 +1,61 @@ +using Ryujinx.HLE.Utilities; +using System; +using System.IO; +using System.Linq; + +namespace Ryujinx.HLE.Utilities +{ + public struct UInt128 + { + public long High { get; private set; } + public long Low { get; private set; } + + public UInt128(long Low, long High) + { + this.Low = Low; + this.High = High; + + byte[] Bytes = new byte[16]; + + int Index = Bytes.Length; + + void WriteBytes(long Value) + { + for (int Byte = 0; Byte < 8; Byte++) + { + Bytes[--Index] = (byte)(Value >> Byte * 8); + } + } + + WriteBytes(Low); + WriteBytes(High); + } + + public UInt128(string UInt128Hex) + { + if (UInt128Hex == null || UInt128Hex.Length != 32 || !UInt128Hex.All("0123456789abcdefABCDEF".Contains)) + { + throw new ArgumentException("Invalid Hex value!", nameof(UInt128Hex)); + } + + Low = Convert.ToInt64(UInt128Hex.Substring(16),16); + High = Convert.ToInt64(UInt128Hex.Substring(0, 16), 16); + } + + public void Write(BinaryWriter BinaryWriter) + { + BinaryWriter.Write(High); + BinaryWriter.Write(Low); + } + + public override string ToString() + { + return High.ToString("x16") + Low.ToString("x16"); + } + + public bool IsZero() + { + return (Low | High) == 0; + } + } +} \ No newline at end of file From 625fc8c0e0f7a10bbdabe86f9cc86b16d1a62be3 Mon Sep 17 00:00:00 2001 From: emmauss Date: Sun, 7 Oct 2018 18:12:11 +0300 Subject: [PATCH 13/30] Stubs Again (#439) * stub/implement audren commands * stub ISelfController get/set IdleTimeDetectonExtension * stub irs * add irs logclass, stub mmu:u irequest 1 * style fixes, addressed comments --- .../HOS/Services/Am/ISelfController.cs | 26 ++++++++++- .../Aud/AudioRenderer/IAudioRenderer.cs | 46 +++++++++++++++++++ .../HOS/Services/Aud/IAudioRendererManager.cs | 25 ++++++++-- .../HOS/Services/Irs/IIrSensorServer.cs | 46 +++++++++++++++++++ Ryujinx.HLE/HOS/Services/Mm/IRequest.cs | 20 ++++++-- Ryujinx.HLE/HOS/Services/ServiceFactory.cs | 4 ++ Ryujinx.HLE/Logging/LogClass.cs | 1 + 7 files changed, 159 insertions(+), 9 deletions(-) create mode 100644 Ryujinx.HLE/HOS/Services/Irs/IIrSensorServer.cs diff --git a/Ryujinx.HLE/HOS/Services/Am/ISelfController.cs b/Ryujinx.HLE/HOS/Services/Am/ISelfController.cs index 2c1d0c3bcc..ef69f598c6 100644 --- a/Ryujinx.HLE/HOS/Services/Am/ISelfController.cs +++ b/Ryujinx.HLE/HOS/Services/Am/ISelfController.cs @@ -14,6 +14,8 @@ namespace Ryujinx.HLE.HOS.Services.Am private KEvent LaunchableEvent; + private int IdleTimeDetectionExtension; + public ISelfController(Horizon System) { m_Commands = new Dictionary() @@ -29,7 +31,9 @@ namespace Ryujinx.HLE.HOS.Services.Am { 14, SetRestartMessageEnabled }, { 16, SetOutOfFocusSuspendingEnabled }, { 19, SetScreenShotImageOrientation }, - { 50, SetHandlesRequestToDisplay } + { 50, SetHandlesRequestToDisplay }, + { 62, SetIdleTimeDetectionExtension }, + { 63, GetIdleTimeDetectionExtension } }; LaunchableEvent = new KEvent(System); @@ -145,5 +149,25 @@ namespace Ryujinx.HLE.HOS.Services.Am return 0; } + + // SetIdleTimeDetectionExtension(u32) + public long SetIdleTimeDetectionExtension(ServiceCtx Context) + { + IdleTimeDetectionExtension = Context.RequestData.ReadInt32(); + + Context.Device.Log.PrintStub(LogClass.ServiceAm, $"Stubbed. IdleTimeDetectionExtension: {IdleTimeDetectionExtension}"); + + return 0; + } + + // GetIdleTimeDetectionExtension() -> u32 + public long GetIdleTimeDetectionExtension(ServiceCtx Context) + { + Context.ResponseData.Write(IdleTimeDetectionExtension); + + Context.Device.Log.PrintStub(LogClass.ServiceAm, $"Stubbed. IdleTimeDetectionExtension: {IdleTimeDetectionExtension}"); + + return 0; + } } } diff --git a/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs b/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs index 85f82622f9..13225951e1 100644 --- a/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs +++ b/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs @@ -38,6 +38,8 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer private int Track; + private PlayState PlayState; + public IAudioRenderer( Horizon System, AMemory Memory, @@ -46,6 +48,10 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer { m_Commands = new Dictionary() { + { 0, GetSampleRate }, + { 1, GetSampleCount }, + { 2, GetMixBufferCount }, + { 3, GetState }, { 4, RequestUpdateAudioRenderer }, { 5, StartAudioRenderer }, { 6, StopAudioRenderer }, @@ -68,6 +74,42 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer Voices = CreateArray(Params.VoiceCount); InitializeAudioOut(); + + PlayState = PlayState.Stopped; + } + + // GetSampleRate() -> u32 + public long GetSampleRate(ServiceCtx Context) + { + Context.ResponseData.Write(Params.SampleRate); + + return 0; + } + + // GetSampleCount() -> u32 + public long GetSampleCount(ServiceCtx Context) + { + Context.ResponseData.Write(Params.SampleCount); + + return 0; + } + + // GetMixBufferCount() -> u32 + public long GetMixBufferCount(ServiceCtx Context) + { + Context.ResponseData.Write(Params.MixCount); + + return 0; + } + + // GetState() -> u32 + private long GetState(ServiceCtx Context) + { + Context.ResponseData.Write((int)PlayState); + + Context.Device.Log.PrintStub(LogClass.ServiceAudio, $"Stubbed. Renderer State: {Enum.GetName(typeof(PlayState), PlayState)}"); + + return 0; } private void AudioCallback() @@ -206,6 +248,8 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer { Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + PlayState = PlayState.Playing; + return 0; } @@ -213,6 +257,8 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer { Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + PlayState = PlayState.Stopped; + return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Aud/IAudioRendererManager.cs b/Ryujinx.HLE/HOS/Services/Aud/IAudioRendererManager.cs index 7ebe2b5873..7859e0135d 100644 --- a/Ryujinx.HLE/HOS/Services/Aud/IAudioRendererManager.cs +++ b/Ryujinx.HLE/HOS/Services/Aud/IAudioRendererManager.cs @@ -3,6 +3,7 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Services.Aud.AudioRenderer; using Ryujinx.HLE.Logging; using Ryujinx.HLE.Utilities; +using System; using System.Collections.Generic; using static Ryujinx.HLE.HOS.ErrorCode; @@ -28,9 +29,10 @@ namespace Ryujinx.HLE.HOS.Services.Aud { m_Commands = new Dictionary() { - { 0, OpenAudioRenderer }, - { 1, GetAudioRendererWorkBufferSize }, - { 2, GetAudioDevice } + { 0, OpenAudioRenderer }, + { 1, GetAudioRendererWorkBufferSize }, + { 2, GetAudioDeviceService }, + { 4, GetAudioDeviceServiceWithRevisionInfo } }; } @@ -161,13 +163,26 @@ namespace Ryujinx.HLE.HOS.Services.Aud return Result / 8; } - public long GetAudioDevice(ServiceCtx Context) + // GetAudioDeviceService(nn::applet::AppletResourceUserId) -> object + public long GetAudioDeviceService(ServiceCtx Context) { - long UserId = Context.RequestData.ReadInt64(); + long AppletResourceUserId = Context.RequestData.ReadInt64(); MakeObject(Context, new IAudioDevice(Context.Device.System)); return 0; } + + // GetAudioDeviceServiceWithRevisionInfo(nn::applet::AppletResourceUserId, u32) -> object + private long GetAudioDeviceServiceWithRevisionInfo(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + int RevisionInfo = Context.RequestData.ReadInt32(); + + Context.Device.Log.PrintStub(LogClass.ServiceAudio, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"RevisionInfo: {RevisionInfo}"); + + return GetAudioDeviceService(Context); + } } } diff --git a/Ryujinx.HLE/HOS/Services/Irs/IIrSensorServer.cs b/Ryujinx.HLE/HOS/Services/Irs/IIrSensorServer.cs new file mode 100644 index 0000000000..c635431405 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Irs/IIrSensorServer.cs @@ -0,0 +1,46 @@ +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel; +using Ryujinx.HLE.Logging; +using System; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Irs +{ + class IIrSensorServer : IpcService + { + private Dictionary m_Commands; + + public override IReadOnlyDictionary Commands => m_Commands; + + private bool Activated; + + public IIrSensorServer() + { + m_Commands = new Dictionary() + { + { 302, ActivateIrsensor }, + { 303, DeactivateIrsensor } + }; + } + + // ActivateIrsensor(nn::applet::AppletResourceUserId, pid) + public long ActivateIrsensor(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceIrs, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + + // DeactivateIrsensor(nn::applet::AppletResourceUserId, pid) + public long DeactivateIrsensor(ServiceCtx Context) + { + long AppletResourceUserId = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceIrs, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + + return 0; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs b/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs index e65ef08679..2a92cf76c5 100644 --- a/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs +++ b/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs @@ -1,5 +1,6 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.Logging; +using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Mm @@ -14,12 +15,25 @@ namespace Ryujinx.HLE.HOS.Services.Mm { m_Commands = new Dictionary() { - { 4, Initialize }, - { 6, SetAndWait }, - { 7, Get } + { 1, InitializeOld }, + { 4, Initialize }, + { 6, SetAndWait }, + { 7, Get } }; } + // InitializeOld(u32, u32, u32) + public long InitializeOld(ServiceCtx Context) + { + int Unknown0 = Context.RequestData.ReadInt32(); + int Unknown1 = Context.RequestData.ReadInt32(); + int Unknown2 = Context.RequestData.ReadInt32(); + + Context.Device.Log.PrintStub(LogClass.ServiceMm, "Stubbed."); + + return 0; + } + public long Initialize(ServiceCtx Context) { Context.Device.Log.PrintStub(LogClass.ServiceMm, "Stubbed."); diff --git a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs index 5e1e780aac..fd5a06e6d9 100644 --- a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs +++ b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs @@ -6,6 +6,7 @@ using Ryujinx.HLE.HOS.Services.Bsd; using Ryujinx.HLE.HOS.Services.Caps; using Ryujinx.HLE.HOS.Services.FspSrv; using Ryujinx.HLE.HOS.Services.Hid; +using Ryujinx.HLE.HOS.Services.Irs; using Ryujinx.HLE.HOS.Services.Lm; using Ryujinx.HLE.HOS.Services.Mm; using Ryujinx.HLE.HOS.Services.Nfp; @@ -96,6 +97,9 @@ namespace Ryujinx.HLE.HOS.Services case "hid": return new IHidServer(System); + case "irs": + return new IIrSensorServer(); + case "lm": return new ILogService(); diff --git a/Ryujinx.HLE/Logging/LogClass.cs b/Ryujinx.HLE/Logging/LogClass.cs index 95cae7e0ab..4905013411 100644 --- a/Ryujinx.HLE/Logging/LogClass.cs +++ b/Ryujinx.HLE/Logging/LogClass.cs @@ -22,6 +22,7 @@ namespace Ryujinx.HLE.Logging ServiceFriend, ServiceFs, ServiceHid, + ServiceIrs, ServiceLm, ServiceMm, ServiceNfp, From 74ca82c5346e341b34909fcf0d2911c6bc9d33c1 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 7 Oct 2018 23:40:37 -0300 Subject: [PATCH 14/30] Tweak cpu cache deletion policy values (#433) * Tweak cpu cache deletion policy values * Address PR feedback --- ChocolArm64/ATranslatorCache.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ChocolArm64/ATranslatorCache.cs b/ChocolArm64/ATranslatorCache.cs index e34cc397b6..3e3c5ab654 100644 --- a/ChocolArm64/ATranslatorCache.cs +++ b/ChocolArm64/ATranslatorCache.cs @@ -8,9 +8,14 @@ namespace ChocolArm64 { class ATranslatorCache { - private const int MaxTotalSize = 2 * 1024 * 256; - private const int MaxTimeDelta = 30000; - private const int MinCallCountForUpdate = 1000; + //Maximum size of the cache, in bytes, measured in ARM code size. + private const int MaxTotalSize = 4 * 1024 * 256; + + //Minimum time required in milliseconds for a method to be eligible for deletion. + private const int MinTimeDelta = 2 * 60000; + + //Minimum number of calls required to update the timestamp. + private const int MinCallCountForUpdate = 250; private class CacheBucket { @@ -134,7 +139,7 @@ namespace ChocolArm64 int TimeDelta = RingDelta(Bucket.Timestamp, Timestamp); - if ((uint)TimeDelta <= (uint)MaxTimeDelta) + if ((uint)TimeDelta <= (uint)MinTimeDelta) { break; } From 65c67bb4a5d19d81048fd84c3ec599ff531629fd Mon Sep 17 00:00:00 2001 From: Thomas Guillemard Date: Mon, 8 Oct 2018 20:53:08 +0200 Subject: [PATCH 15/30] Break: don't crash when bit 31 is set (#444) --- Ryujinx.HLE/HOS/Kernel/SvcSystem.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs b/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs index 8bcea55077..396519fb29 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs @@ -247,9 +247,17 @@ namespace Ryujinx.HLE.HOS.Kernel long Unknown = (long)ThreadState.X1; long Info = (long)ThreadState.X2; - Process.PrintStackTrace(ThreadState); + if ((Reason & (1 << 31)) == 0) + { + Process.PrintStackTrace(ThreadState); - throw new GuestBrokeExecutionException(); + throw new GuestBrokeExecutionException(); + } + else + { + Device.Log.PrintInfo(LogClass.KernelSvc, "Debugger triggered"); + Process.PrintStackTrace(ThreadState); + } } private void SvcOutputDebugString(AThreadState ThreadState) From d5c0de8362941f957846a71d09a40a6eb77a22fa Mon Sep 17 00:00:00 2001 From: Thomas Guillemard Date: Wed, 10 Oct 2018 01:01:49 +0200 Subject: [PATCH 16/30] Implement IRoInterface (#445) * Implement IRoInterface This is required by Super Mario Party. This commit also adds MapProcessCodeMemory and UnmapProcessCodeMemory functions in KMemoryManager. Those two calls might not reflect what the SVC of the same names do. * Fix some code style issues * Use MakeError to clarify error code * Add NRR and NRO constants * Fix some codestyle issues * Fix InvalidMemoryState error code --- Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs | 98 ++++ Ryujinx.HLE/HOS/Process.cs | 30 +- Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs | 457 ++++++++++++++++++ Ryujinx.HLE/HOS/Services/Ldr/LoaderErr.cs | 18 + Ryujinx.HLE/HOS/Services/ServiceFactory.cs | 4 + Ryujinx.HLE/Loaders/Executable.cs | 44 +- .../Loaders/Executables/IExecutable.cs | 3 + Ryujinx.HLE/Loaders/Executables/Nro.cs | 9 +- Ryujinx.HLE/Loaders/Executables/Nso.cs | 6 + Ryujinx.HLE/Logging/LogClass.cs | 1 + 10 files changed, 657 insertions(+), 13 deletions(-) create mode 100644 Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs create mode 100644 Ryujinx.HLE/HOS/Services/Ldr/LoaderErr.cs diff --git a/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs b/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs index 3afdb57022..8871cbe7d6 100644 --- a/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs +++ b/Ryujinx.HLE/HOS/Kernel/KMemoryManager.cs @@ -148,6 +148,92 @@ namespace Ryujinx.HLE.HOS.Kernel } } + public long MapProcessCodeMemory(long Dst, long Src, long Size) + { + lock (Blocks) + { + long PagesCount = Size / PageSize; + + bool Success = IsUnmapped(Dst, Size); + + Success &= CheckRange( + Src, + Size, + MemoryState.Mask, + MemoryState.Heap, + MemoryPermission.Mask, + MemoryPermission.ReadAndWrite, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _); + + if (Success) + { + long PA = CpuMemory.GetPhysicalAddress(Src); + + InsertBlock(Dst, PagesCount, MemoryState.CodeStatic, MemoryPermission.ReadAndExecute); + InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.None); + + CpuMemory.Map(Dst, PA, Size); + + return 0; + } + } + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + + public long UnmapProcessCodeMemory(long Dst, long Src, long Size) + { + lock (Blocks) + { + long PagesCount = Size / PageSize; + + bool Success = CheckRange( + Dst, + Size, + MemoryState.Mask, + MemoryState.CodeStatic, + MemoryPermission.None, + MemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _); + + Success &= CheckRange( + Src, + Size, + MemoryState.Mask, + MemoryState.Heap, + MemoryPermission.Mask, + MemoryPermission.None, + MemoryAttribute.Mask, + MemoryAttribute.None, + MemoryAttribute.IpcAndDeviceMapped, + out _, + out _, + out _); + + if (Success) + { + InsertBlock(Dst, PagesCount, MemoryState.Unmapped); + InsertBlock(Src, PagesCount, MemoryState.Heap, MemoryPermission.ReadAndWrite); + + CpuMemory.Unmap(Dst, Size); + + return 0; + } + } + + return MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); + } + public void HleMapCustom(long Position, long Size, MemoryState State, MemoryPermission Permission) { long PagesCount = Size / PageSize; @@ -755,6 +841,18 @@ namespace Ryujinx.HLE.HOS.Kernel } } + public bool HleIsUnmapped(long Position, long Size) + { + bool Result = false; + + lock (Blocks) + { + Result = IsUnmapped(Position, Size); + } + + return Result; + } + private bool IsUnmapped(long Position, long Size) { return CheckRange( diff --git a/Ryujinx.HLE/HOS/Process.cs b/Ryujinx.HLE/HOS/Process.cs index 3817f56195..ab0ab18ba1 100644 --- a/Ryujinx.HLE/HOS/Process.cs +++ b/Ryujinx.HLE/HOS/Process.cs @@ -106,13 +106,37 @@ namespace Ryujinx.HLE.HOS throw new ObjectDisposedException(nameof(Process)); } - Device.Log.PrintInfo(LogClass.Loader, $"Image base at 0x{ImageBase:x16}."); + long ImageEnd = LoadProgram(Program, ImageBase); - Executable Executable = new Executable(Program, MemoryManager, Memory, ImageBase); + ImageBase = IntUtils.AlignUp(ImageEnd, KMemoryManager.PageSize); + } + + public long LoadProgram(IExecutable Program, long ExecutableBase) + { + if (Disposed) + { + throw new ObjectDisposedException(nameof(Process)); + } + + Device.Log.PrintInfo(LogClass.Loader, $"Image base at 0x{ExecutableBase:x16}."); + + Executable Executable = new Executable(Program, MemoryManager, Memory, ExecutableBase); Executables.Add(Executable); - ImageBase = IntUtils.AlignUp(Executable.ImageEnd, KMemoryManager.PageSize); + return Executable.ImageEnd; + } + + public void RemoveProgram(long ExecutableBase) + { + foreach (Executable Executable in Executables) + { + if (Executable.ImageBase == ExecutableBase) + { + Executables.Remove(Executable); + break; + } + } } public void SetEmptyArgs() diff --git a/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs b/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs new file mode 100644 index 0000000000..4d595fde27 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Ldr/IRoInterface.cs @@ -0,0 +1,457 @@ +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.Loaders.Executables; +using Ryujinx.HLE.Utilities; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Cryptography; + +using static Ryujinx.HLE.HOS.ErrorCode; + +namespace Ryujinx.HLE.HOS.Services.Ldr +{ + [StructLayout(LayoutKind.Explicit, Size = 0x350)] + unsafe struct NrrHeader + { + [FieldOffset(0)] + public uint Magic; + + [FieldOffset(0x10)] + public ulong TitleIdMask; + + [FieldOffset(0x18)] + public ulong TitleIdPattern; + + [FieldOffset(0x30)] + public fixed byte Modulus[0x100]; + + [FieldOffset(0x130)] + public fixed byte FixedKeySignature[0x100]; + + [FieldOffset(0x230)] + public fixed byte NrrSignature[0x100]; + + [FieldOffset(0x330)] + public ulong TitleIdMin; + + [FieldOffset(0x338)] + public uint NrrSize; + + [FieldOffset(0x340)] + public uint HashOffset; + + [FieldOffset(0x344)] + public uint HashCount; + } + + class NrrInfo + { + public NrrHeader Header { get; private set; } + public List Hashes { get; private set; } + public long NrrAddress { get; private set; } + + public NrrInfo(long NrrAddress, NrrHeader Header, List Hashes) + { + this.NrrAddress = NrrAddress; + this.Header = Header; + this.Hashes = Hashes; + } + } + + class NroInfo + { + public Nro Executable { get; private set; } + public byte[] Hash { get; private set; } + public long NroAddress { get; private set; } + public long TotalSize { get; private set; } + public long NroMappedAddress { get; set; } + + public NroInfo(Nro Executable, byte[] Hash, long TotalSize) + { + this.Executable = Executable; + this.Hash = Hash; + this.TotalSize = TotalSize; + } + } + + class IRoInterface : IpcService + { + private Dictionary m_Commands; + + public override IReadOnlyDictionary Commands => m_Commands; + + private const int MaxNrr = 0x40; + private const int MaxNro = 0x40; + + private const uint NrrMagic = 0x3052524E; + private const uint NroMagic = 0x304F524E; + + private List NrrInfos; + private List NroInfos; + + private bool IsInitialized; + + public IRoInterface() + { + m_Commands = new Dictionary() + { + { 0, LoadNro }, + { 1, UnloadNro }, + { 2, LoadNrr }, + { 3, UnloadNrr }, + { 4, Initialize }, + }; + + NrrInfos = new List(MaxNrr); + NroInfos = new List(MaxNro); + } + + private long ParseNrr(out NrrInfo NrrInfo, ServiceCtx Context, long NrrAddress, long NrrSize) + { + NrrInfo = null; + + if (NrrSize == 0 || NrrAddress + NrrSize <= NrrAddress || (NrrSize & 0xFFF) != 0) + { + return MakeError(ErrorModule.Loader, LoaderErr.BadSize); + } + else if ((NrrAddress & 0xFFF) != 0) + { + return MakeError(ErrorModule.Loader, LoaderErr.UnalignedAddress); + } + + StructReader Reader = new StructReader(Context.Memory, NrrAddress); + NrrHeader Header = Reader.Read(); + + if (Header.Magic != NrrMagic) + { + return MakeError(ErrorModule.Loader, LoaderErr.InvalidNrr); + } + else if (Header.NrrSize != NrrSize) + { + return MakeError(ErrorModule.Loader, LoaderErr.BadSize); + } + + List Hashes = new List(); + + for (int i = 0; i < Header.HashCount; i++) + { + Hashes.Add(Context.Memory.ReadBytes(NrrAddress + Header.HashOffset + (i * 0x20), 0x20)); + } + + NrrInfo = new NrrInfo(NrrAddress, Header, Hashes); + + return 0; + } + + public bool IsNroHashPresent(byte[] NroHash) + { + foreach (NrrInfo Info in NrrInfos) + { + foreach (byte[] Hash in Info.Hashes) + { + if (Hash.SequenceEqual(NroHash)) + { + return true; + } + } + } + + return false; + } + + public bool IsNroLoaded(byte[] NroHash) + { + foreach (NroInfo Info in NroInfos) + { + if (Info.Hash.SequenceEqual(NroHash)) + { + return true; + } + } + + return false; + } + + public long ParseNro(out NroInfo Res, ServiceCtx Context, long NroHeapAddress, long NroSize, long BssHeapAddress, long BssSize) + { + Res = null; + + if (NroInfos.Count >= MaxNro) + { + return MakeError(ErrorModule.Loader, LoaderErr.MaxNro); + } + else if (NroSize == 0 || NroHeapAddress + NroSize <= NroHeapAddress || (NroSize & 0xFFF) != 0) + { + return MakeError(ErrorModule.Loader, LoaderErr.BadSize); + } + else if (BssSize != 0 && (BssHeapAddress + BssSize) <= BssHeapAddress) + { + return MakeError(ErrorModule.Loader, LoaderErr.BadSize); + } + else if ((NroHeapAddress & 0xFFF) != 0) + { + return MakeError(ErrorModule.Loader, LoaderErr.UnalignedAddress); + } + + uint Magic = Context.Memory.ReadUInt32(NroHeapAddress + 0x10); + uint NroFileSize = Context.Memory.ReadUInt32(NroHeapAddress + 0x18); + + if (Magic != NroMagic || NroSize != NroFileSize) + { + return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro); + } + + byte[] NroData = Context.Memory.ReadBytes(NroHeapAddress, NroSize); + byte[] NroHash = null; + + MemoryStream Stream = new MemoryStream(NroData); + + using (SHA256 Hasher = SHA256.Create()) + { + NroHash = Hasher.ComputeHash(Stream); + } + + if (!IsNroHashPresent(NroHash)) + { + return MakeError(ErrorModule.Loader, LoaderErr.NroHashNotPresent); + } + + if (IsNroLoaded(NroHash)) + { + return MakeError(ErrorModule.Loader, LoaderErr.NroAlreadyLoaded); + } + + Stream.Position = 0; + + Nro Executable = new Nro(Stream, "memory", NroHeapAddress, BssHeapAddress); + + // check if everything is page align. + if ((Executable.Text.Length & 0xFFF) != 0 || (Executable.RO.Length & 0xFFF) != 0 + || (Executable.Data.Length & 0xFFF) != 0 || (Executable.BssSize & 0xFFF) != 0) + { + return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro); + } + + // check if everything is contiguous. + if (Executable.ROOffset != Executable.TextOffset + Executable.Text.Length + || Executable.DataOffset != Executable.ROOffset + Executable.RO.Length + || NroFileSize != Executable.DataOffset + Executable.Data.Length) + { + return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro); + } + + // finally check the bss size match. + if (Executable.BssSize != BssSize) + { + return MakeError(ErrorModule.Loader, LoaderErr.InvalidNro); + } + + Res = new NroInfo(Executable, NroHash, Executable.Text.Length + Executable.RO.Length + Executable.Data.Length + Executable.BssSize); + + return 0; + } + + private long MapNro(ServiceCtx Context, NroInfo Info, out long NroMappedAddress) + { + NroMappedAddress = 0; + long TargetAddress = Context.Process.MemoryManager.AddrSpaceStart; + + long HeapRegionStart = Context.Process.MemoryManager.HeapRegionStart; + long HeapRegionEnd = Context.Process.MemoryManager.HeapRegionEnd; + + long MapRegionStart = Context.Process.MemoryManager.MapRegionStart; + long MapRegionEnd = Context.Process.MemoryManager.MapRegionEnd; + + while (true) + { + if (TargetAddress + Info.TotalSize >= Context.Process.MemoryManager.AddrSpaceEnd) + { + return MakeError(ErrorModule.Loader, LoaderErr.InvalidMemoryState); + } + + bool IsValidAddress = !(HeapRegionStart > 0 && HeapRegionStart <= TargetAddress + Info.TotalSize - 1 + && TargetAddress <= HeapRegionEnd - 1) + && !(MapRegionStart > 0 + && MapRegionStart <= TargetAddress + Info.TotalSize - 1 + && TargetAddress <= MapRegionEnd - 1); + + if (IsValidAddress && Context.Process.MemoryManager.HleIsUnmapped(TargetAddress, Info.TotalSize)) + { + break; + } + + TargetAddress += 0x1000; + } + + Context.Process.LoadProgram(Info.Executable, TargetAddress); + + Info.NroMappedAddress = TargetAddress; + NroMappedAddress = TargetAddress; + + return 0; + } + + private long RemoveNrrInfo(long NrrAddress) + { + foreach (NrrInfo Info in NrrInfos) + { + if (Info.NrrAddress == NrrAddress) + { + NrrInfos.Remove(Info); + + return 0; + } + } + + return MakeError(ErrorModule.Loader, LoaderErr.BadNrrAddress); + } + + private long RemoveNroInfo(ServiceCtx Context, long NroMappedAddress, long NroHeapAddress) + { + foreach (NroInfo Info in NroInfos) + { + if (Info.NroMappedAddress == NroMappedAddress && Info.Executable.SourceAddress == NroHeapAddress) + { + NroInfos.Remove(Info); + + Context.Process.RemoveProgram(Info.NroMappedAddress); + + long Result = Context.Process.MemoryManager.UnmapProcessCodeMemory(Info.NroMappedAddress, Info.Executable.SourceAddress, Info.TotalSize - Info.Executable.BssSize); + + if (Result == 0 && Info.Executable.BssSize != 0) + { + Result = Context.Process.MemoryManager.UnmapProcessCodeMemory(Info.NroMappedAddress + Info.TotalSize - Info.Executable.BssSize, Info.Executable.BssAddress, Info.Executable.BssSize); + } + + return Result; + } + } + + return MakeError(ErrorModule.Loader, LoaderErr.BadNroAddress); + } + + // LoadNro(u64, u64, u64, u64, u64, pid) -> u64 + public long LoadNro(ServiceCtx Context) + { + long Result = MakeError(ErrorModule.Loader, LoaderErr.BadInitialization); + + // Zero + Context.RequestData.ReadUInt64(); + + long NroHeapAddress = Context.RequestData.ReadInt64(); + long NroSize = Context.RequestData.ReadInt64(); + long BssHeapAddress = Context.RequestData.ReadInt64(); + long BssSize = Context.RequestData.ReadInt64(); + + long NroMappedAddress = 0; + + if (IsInitialized) + { + NroInfo Info; + + Result = ParseNro(out Info, Context, NroHeapAddress, NroSize, BssHeapAddress, BssSize); + + if (Result == 0) + { + Result = MapNro(Context, Info, out NroMappedAddress); + + if (Result == 0) + { + NroInfos.Add(Info); + } + } + } + + Context.ResponseData.Write(NroMappedAddress); + + return Result; + } + + // UnloadNro(u64, u64, pid) + public long UnloadNro(ServiceCtx Context) + { + long Result = MakeError(ErrorModule.Loader, LoaderErr.BadInitialization); + + long NroMappedAddress = Context.RequestData.ReadInt64(); + long NroHeapAddress = Context.RequestData.ReadInt64(); + + if (IsInitialized) + { + if ((NroMappedAddress & 0xFFF) != 0 || (NroHeapAddress & 0xFFF) != 0) + { + return MakeError(ErrorModule.Loader, LoaderErr.UnalignedAddress); + } + + Result = RemoveNroInfo(Context, NroMappedAddress, NroHeapAddress); + } + + return Result; + } + + // LoadNrr(u64, u64, u64, pid) + public long LoadNrr(ServiceCtx Context) + { + long Result = MakeError(ErrorModule.Loader, LoaderErr.BadInitialization); + + // Zero + Context.RequestData.ReadUInt64(); + + long NrrAddress = Context.RequestData.ReadInt64(); + long NrrSize = Context.RequestData.ReadInt64(); + + if (IsInitialized) + { + NrrInfo Info; + Result = ParseNrr(out Info, Context, NrrAddress, NrrSize); + + if(Result == 0) + { + if (NrrInfos.Count >= MaxNrr) + { + Result = MakeError(ErrorModule.Loader, LoaderErr.MaxNrr); + } + else + { + NrrInfos.Add(Info); + } + } + } + + return Result; + } + + // UnloadNrr(u64, u64, pid) + public long UnloadNrr(ServiceCtx Context) + { + long Result = MakeError(ErrorModule.Loader, LoaderErr.BadInitialization); + + // Zero + Context.RequestData.ReadUInt64(); + + long NrrHeapAddress = Context.RequestData.ReadInt64(); + + if (IsInitialized) + { + if ((NrrHeapAddress & 0xFFF) != 0) + { + return MakeError(ErrorModule.Loader, LoaderErr.UnalignedAddress); + } + + Result = RemoveNrrInfo(NrrHeapAddress); + } + + return Result; + } + + // Initialize(u64, pid, KObject) + public long Initialize(ServiceCtx Context) + { + // TODO: we actually ignore the pid and process handle receive, we will need to use them when we will have multi process support. + IsInitialized = true; + + return 0; + } + } +} diff --git a/Ryujinx.HLE/HOS/Services/Ldr/LoaderErr.cs b/Ryujinx.HLE/HOS/Services/Ldr/LoaderErr.cs new file mode 100644 index 0000000000..ba77a5cc13 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Ldr/LoaderErr.cs @@ -0,0 +1,18 @@ +namespace Ryujinx.HLE.HOS.Services.Ldr +{ + static class LoaderErr + { + public const int InvalidMemoryState = 51; + public const int InvalidNro = 52; + public const int InvalidNrr = 53; + public const int MaxNro = 55; + public const int MaxNrr = 56; + public const int NroAlreadyLoaded = 57; + public const int NroHashNotPresent = 54; + public const int UnalignedAddress = 81; + public const int BadSize = 82; + public const int BadNroAddress = 84; + public const int BadNrrAddress = 85; + public const int BadInitialization = 87; + } +} diff --git a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs index fd5a06e6d9..f701dd0537 100644 --- a/Ryujinx.HLE/HOS/Services/ServiceFactory.cs +++ b/Ryujinx.HLE/HOS/Services/ServiceFactory.cs @@ -7,6 +7,7 @@ using Ryujinx.HLE.HOS.Services.Caps; using Ryujinx.HLE.HOS.Services.FspSrv; using Ryujinx.HLE.HOS.Services.Hid; using Ryujinx.HLE.HOS.Services.Irs; +using Ryujinx.HLE.HOS.Services.Ldr; using Ryujinx.HLE.HOS.Services.Lm; using Ryujinx.HLE.HOS.Services.Mm; using Ryujinx.HLE.HOS.Services.Nfp; @@ -100,6 +101,9 @@ namespace Ryujinx.HLE.HOS.Services case "irs": return new IIrSensorServer(); + case "ldr:ro": + return new IRoInterface(); + case "lm": return new ILogService(); diff --git a/Ryujinx.HLE/Loaders/Executable.cs b/Ryujinx.HLE/Loaders/Executable.cs index a9850e4af9..3c63af141b 100644 --- a/Ryujinx.HLE/Loaders/Executable.cs +++ b/Ryujinx.HLE/Loaders/Executable.cs @@ -49,21 +49,49 @@ namespace Ryujinx.HLE.Loaders long DataPosition = ImageBase + (uint)Exe.DataOffset; long TextSize = (uint)IntUtils.AlignUp(Exe.Text.Length, KMemoryManager.PageSize); - long ROSize = (uint)IntUtils.AlignUp(Exe.RO.Length, KMemoryManager.PageSize); + long ROSize = (uint)IntUtils.AlignUp(Exe.RO.Length, KMemoryManager.PageSize); long DataSize = (uint)IntUtils.AlignUp(Exe.Data.Length, KMemoryManager.PageSize); + long BssSize = (uint)IntUtils.AlignUp(Exe.BssSize, KMemoryManager.PageSize); - long DataAndBssSize = (uint)IntUtils.AlignUp(Exe.BssSize, KMemoryManager.PageSize) + DataSize; + long DataAndBssSize = BssSize + DataSize; ImageEnd = DataPosition + DataAndBssSize; - MemoryManager.HleMapProcessCode(TextPosition, TextSize + ROSize + DataAndBssSize); + if (Exe.SourceAddress == 0) + { + MemoryManager.HleMapProcessCode(TextPosition, TextSize + ROSize + DataAndBssSize); - MemoryManager.SetProcessMemoryPermission(ROPosition, ROSize, MemoryPermission.Read); - MemoryManager.SetProcessMemoryPermission(DataPosition, DataAndBssSize, MemoryPermission.ReadAndWrite); + MemoryManager.SetProcessMemoryPermission(ROPosition, ROSize, MemoryPermission.Read); + MemoryManager.SetProcessMemoryPermission(DataPosition, DataAndBssSize, MemoryPermission.ReadAndWrite); - Memory.WriteBytes(TextPosition, Exe.Text); - Memory.WriteBytes(ROPosition, Exe.RO); - Memory.WriteBytes(DataPosition, Exe.Data); + Memory.WriteBytes(TextPosition, Exe.Text); + Memory.WriteBytes(ROPosition, Exe.RO); + Memory.WriteBytes(DataPosition, Exe.Data); + } + else + { + long Result = MemoryManager.MapProcessCodeMemory(TextPosition, Exe.SourceAddress, TextSize + ROSize + DataSize); + + if (Result != 0) + { + throw new InvalidOperationException(); + } + + MemoryManager.SetProcessMemoryPermission(ROPosition, ROSize, MemoryPermission.Read); + MemoryManager.SetProcessMemoryPermission(DataPosition, DataSize, MemoryPermission.ReadAndWrite); + + if (Exe.BssAddress != 0 && Exe.BssSize != 0) + { + Result = MemoryManager.MapProcessCodeMemory(DataPosition + DataSize, Exe.BssAddress, BssSize); + + if (Result != 0) + { + throw new InvalidOperationException(); + } + + MemoryManager.SetProcessMemoryPermission(DataPosition + DataSize, BssSize, MemoryPermission.ReadAndWrite); + } + } if (Exe.Mod0Offset == 0) { diff --git a/Ryujinx.HLE/Loaders/Executables/IExecutable.cs b/Ryujinx.HLE/Loaders/Executables/IExecutable.cs index 44bad61497..6f0952abdb 100644 --- a/Ryujinx.HLE/Loaders/Executables/IExecutable.cs +++ b/Ryujinx.HLE/Loaders/Executables/IExecutable.cs @@ -8,6 +8,9 @@ namespace Ryujinx.HLE.Loaders.Executables byte[] RO { get; } byte[] Data { get; } + long SourceAddress { get; } + long BssAddress { get; } + int Mod0Offset { get; } int TextOffset { get; } int ROOffset { get; } diff --git a/Ryujinx.HLE/Loaders/Executables/Nro.cs b/Ryujinx.HLE/Loaders/Executables/Nro.cs index 0b5068d7b9..6015da2132 100644 --- a/Ryujinx.HLE/Loaders/Executables/Nro.cs +++ b/Ryujinx.HLE/Loaders/Executables/Nro.cs @@ -16,9 +16,14 @@ namespace Ryujinx.HLE.Loaders.Executables public int DataOffset { get; private set; } public int BssSize { get; private set; } - public Nro(Stream Input, string FilePath) + public long SourceAddress { get; private set; } + public long BssAddress { get; private set; } + + public Nro(Stream Input, string FilePath, long SourceAddress = 0, long BssAddress = 0) { - this.FilePath = FilePath; + this.FilePath = FilePath; + this.SourceAddress = SourceAddress; + this.BssAddress = BssAddress; BinaryReader Reader = new BinaryReader(Input); diff --git a/Ryujinx.HLE/Loaders/Executables/Nso.cs b/Ryujinx.HLE/Loaders/Executables/Nso.cs index fef9c4b853..c7b48a5f35 100644 --- a/Ryujinx.HLE/Loaders/Executables/Nso.cs +++ b/Ryujinx.HLE/Loaders/Executables/Nso.cs @@ -18,6 +18,9 @@ namespace Ryujinx.HLE.Loaders.Executables public int DataOffset { get; private set; } public int BssSize { get; private set; } + public long SourceAddress { get; private set; } + public long BssAddress { get; private set; } + [Flags] private enum NsoFlags { @@ -33,6 +36,9 @@ namespace Ryujinx.HLE.Loaders.Executables { this.FilePath = FilePath; + SourceAddress = 0; + BssAddress = 0; + BinaryReader Reader = new BinaryReader(Input); Input.Seek(0, SeekOrigin.Begin); diff --git a/Ryujinx.HLE/Logging/LogClass.cs b/Ryujinx.HLE/Logging/LogClass.cs index 4905013411..0458c75f8a 100644 --- a/Ryujinx.HLE/Logging/LogClass.cs +++ b/Ryujinx.HLE/Logging/LogClass.cs @@ -23,6 +23,7 @@ namespace Ryujinx.HLE.Logging ServiceFs, ServiceHid, ServiceIrs, + ServiceLdr, ServiceLm, ServiceMm, ServiceNfp, From b2063be59747e65434b3b99a379566d640f05995 Mon Sep 17 00:00:00 2001 From: Thomas Guillemard Date: Wed, 10 Oct 2018 01:15:07 +0200 Subject: [PATCH 17/30] Implement IResolver (#442) * Implement IResolver - GetAddrInfo & GetNameInfo still need to be implemented. * Address comments * Use MakeError --- Ryujinx.HLE/HOS/Services/Sfdnsres/GaiError.cs | 22 + .../HOS/Services/Sfdnsres/IResolver.cs | 380 +++++++++++++++++- .../HOS/Services/Sfdnsres/NetDBError.cs | 13 + 3 files changed, 414 insertions(+), 1 deletion(-) create mode 100644 Ryujinx.HLE/HOS/Services/Sfdnsres/GaiError.cs create mode 100644 Ryujinx.HLE/HOS/Services/Sfdnsres/NetDBError.cs diff --git a/Ryujinx.HLE/HOS/Services/Sfdnsres/GaiError.cs b/Ryujinx.HLE/HOS/Services/Sfdnsres/GaiError.cs new file mode 100644 index 0000000000..65d5457707 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Sfdnsres/GaiError.cs @@ -0,0 +1,22 @@ +namespace Ryujinx.HLE.HOS.Services.Sfdnsres +{ + enum GaiError + { + Success, + AddressFamily, + Again, + BadFlags, + Fail, + Family, + Memory, + NoData, + NoName, + Service, + SocketType, + System, + BadHints, + Protocol, + Overflow, + Max, + } +} diff --git a/Ryujinx.HLE/HOS/Services/Sfdnsres/IResolver.cs b/Ryujinx.HLE/HOS/Services/Sfdnsres/IResolver.cs index 26dbedf441..0ca43eda4f 100644 --- a/Ryujinx.HLE/HOS/Services/Sfdnsres/IResolver.cs +++ b/Ryujinx.HLE/HOS/Services/Sfdnsres/IResolver.cs @@ -1,5 +1,11 @@ using Ryujinx.HLE.HOS.Ipc; +using System; using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Text; + +using static Ryujinx.HLE.HOS.ErrorCode; namespace Ryujinx.HLE.HOS.Services.Sfdnsres { @@ -13,8 +19,380 @@ namespace Ryujinx.HLE.HOS.Services.Sfdnsres { m_Commands = new Dictionary() { - //... + { 0, SetDnsAddressesPrivate }, + { 1, GetDnsAddressesPrivate }, + { 2, GetHostByName }, + { 3, GetHostByAddress }, + { 4, GetHostStringError }, + { 5, GetGaiStringError }, + { 8, RequestCancelHandle }, + { 9, CancelSocketCall }, + { 11, ClearDnsAddresses }, }; } + + private long SerializeHostEnt(ServiceCtx Context, IPHostEntry HostEntry, List Addresses = null) + { + long OriginalBufferPosition = Context.Request.ReceiveBuff[0].Position; + long BufferPosition = OriginalBufferPosition; + long BufferSize = Context.Request.ReceiveBuff[0].Size; + + string HostName = HostEntry.HostName + '\0'; + + // h_name + Context.Memory.WriteBytes(BufferPosition, Encoding.ASCII.GetBytes(HostName)); + BufferPosition += HostName.Length; + + // h_aliases list size + Context.Memory.WriteInt32(BufferPosition, IPAddress.HostToNetworkOrder(HostEntry.Aliases.Length)); + BufferPosition += 4; + + // Actual aliases + foreach (string Alias in HostEntry.Aliases) + { + Context.Memory.WriteBytes(BufferPosition, Encoding.ASCII.GetBytes(Alias + '\0')); + BufferPosition += Alias.Length + 1; + } + + // h_addrtype but it's a short (also only support IPv4) + Context.Memory.WriteInt16(BufferPosition, IPAddress.HostToNetworkOrder((short)2)); + BufferPosition += 2; + + // h_length but it's a short + Context.Memory.WriteInt16(BufferPosition, IPAddress.HostToNetworkOrder((short)4)); + BufferPosition += 2; + + // Ip address count, we can only support ipv4 (blame Nintendo) + Context.Memory.WriteInt32(BufferPosition, Addresses != null ? IPAddress.HostToNetworkOrder(Addresses.Count) : 0); + BufferPosition += 4; + + if (Addresses != null) + { + foreach (IPAddress Ip in Addresses) + { + Context.Memory.WriteInt32(BufferPosition, IPAddress.HostToNetworkOrder(BitConverter.ToInt32(Ip.GetAddressBytes(), 0))); + BufferPosition += 4; + } + } + + return BufferPosition - OriginalBufferPosition; + } + + private string GetGaiStringErrorFromErrorCode(GaiError ErrorCode) + { + if (ErrorCode > GaiError.Max) + { + ErrorCode = GaiError.Max; + } + + switch (ErrorCode) + { + case GaiError.AddressFamily: + return "Address family for hostname not supported"; + case GaiError.Again: + return "Temporary failure in name resolution"; + case GaiError.BadFlags: + return "Invalid value for ai_flags"; + case GaiError.Fail: + return "Non-recoverable failure in name resolution"; + case GaiError.Family: + return "ai_family not supported"; + case GaiError.Memory: + return "Memory allocation failure"; + case GaiError.NoData: + return "No address associated with hostname"; + case GaiError.NoName: + return "hostname nor servname provided, or not known"; + case GaiError.Service: + return "servname not supported for ai_socktype"; + case GaiError.SocketType: + return "ai_socktype not supported"; + case GaiError.System: + return "System error returned in errno"; + case GaiError.BadHints: + return "Invalid value for hints"; + case GaiError.Protocol: + return "Resolved protocol is unknown"; + case GaiError.Overflow: + return "Argument buffer overflow"; + case GaiError.Max: + return "Unknown error"; + default: + return "Success"; + } + } + + private string GetHostStringErrorFromErrorCode(NetDBError ErrorCode) + { + if (ErrorCode <= NetDBError.Internal) + { + return "Resolver internal error"; + } + + switch (ErrorCode) + { + case NetDBError.Success: + return "Resolver Error 0 (no error)"; + case NetDBError.HostNotFound: + return "Unknown host"; + case NetDBError.TryAgain: + return "Host name lookup failure"; + case NetDBError.NoRecovery: + return "Unknown server error"; + case NetDBError.NoData: + return "No address associated with name"; + default: + return "Unknown resolver error"; + } + } + + private List GetIPV4Addresses(IPHostEntry HostEntry) + { + List Result = new List(); + foreach (IPAddress Ip in HostEntry.AddressList) + { + if (Ip.AddressFamily == AddressFamily.InterNetwork) + Result.Add(Ip); + } + return Result; + } + + // SetDnsAddressesPrivate(u32, buffer) + public long SetDnsAddressesPrivate(ServiceCtx Context) + { + uint Unknown0 = Context.RequestData.ReadUInt32(); + long BufferPosition = Context.Request.SendBuff[0].Position; + long BufferSize = Context.Request.SendBuff[0].Size; + + // TODO: This is stubbed in 2.0.0+, reverse 1.0.0 version for the sake completeness. + Context.Device.Log.PrintStub(Logging.LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}"); + + return MakeError(ErrorModule.Os, 1023); + } + + // GetDnsAddressPrivate(u32) -> buffer + public long GetDnsAddressesPrivate(ServiceCtx Context) + { + uint Unknown0 = Context.RequestData.ReadUInt32(); + + // TODO: This is stubbed in 2.0.0+, reverse 1.0.0 version for the sake completeness. + Context.Device.Log.PrintStub(Logging.LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}"); + + return MakeError(ErrorModule.Os, 1023); + } + + // GetHostByName(u8, u32, u64, pid, buffer) -> (u32, u32, u32, buffer) + public long GetHostByName(ServiceCtx Context) + { + byte[] RawName = Context.Memory.ReadBytes(Context.Request.SendBuff[0].Position, Context.Request.SendBuff[0].Size); + string Name = Encoding.ASCII.GetString(RawName).TrimEnd('\0'); + + // TODO: use params + bool EnableNsdResolve = Context.RequestData.ReadInt32() == 1; + int TimeOut = Context.RequestData.ReadInt32(); + ulong PidPlaceholder = Context.RequestData.ReadUInt64(); + + IPHostEntry HostEntry = null; + + NetDBError NetDBErrorCode = NetDBError.Success; + GaiError Errno = GaiError.Overflow; + long SerializedSize = 0; + + if (Name.Length <= 255) + { + try + { + HostEntry = Dns.GetHostEntry(Name); + } + catch (SocketException Exception) + { + NetDBErrorCode = NetDBError.Internal; + + if (Exception.ErrorCode == 11001) + { + NetDBErrorCode = NetDBError.HostNotFound; + Errno = GaiError.NoData; + } + else if (Exception.ErrorCode == 11002) + { + NetDBErrorCode = NetDBError.TryAgain; + } + else if (Exception.ErrorCode == 11003) + { + NetDBErrorCode = NetDBError.NoRecovery; + } + else if (Exception.ErrorCode == 11004) + { + NetDBErrorCode = NetDBError.NoData; + } + else if (Exception.ErrorCode == 10060) + { + Errno = GaiError.Again; + } + } + } + else + { + NetDBErrorCode = NetDBError.HostNotFound; + } + + if (HostEntry != null) + { + Errno = GaiError.Success; + + List Addresses = GetIPV4Addresses(HostEntry); + + if (Addresses.Count == 0) + { + Errno = GaiError.NoData; + NetDBErrorCode = NetDBError.NoAddress; + } + else + { + SerializedSize = SerializeHostEnt(Context, HostEntry, Addresses); + } + } + + Context.ResponseData.Write((int)NetDBErrorCode); + Context.ResponseData.Write((int)Errno); + Context.ResponseData.Write(SerializedSize); + + return 0; + } + + // GetHostByAddr(u32, u32, u32, u64, pid, buffer) -> (u32, u32, u32, buffer) + public long GetHostByAddress(ServiceCtx Context) + { + byte[] RawIp = Context.Memory.ReadBytes(Context.Request.SendBuff[0].Position, Context.Request.SendBuff[0].Size); + + // TODO: use params + uint SocketLength = Context.RequestData.ReadUInt32(); + uint Type = Context.RequestData.ReadUInt32(); + int TimeOut = Context.RequestData.ReadInt32(); + ulong PidPlaceholder = Context.RequestData.ReadUInt64(); + + IPHostEntry HostEntry = null; + + NetDBError NetDBErrorCode = NetDBError.Success; + GaiError Errno = GaiError.AddressFamily; + long SerializedSize = 0; + + if (RawIp.Length == 4) + { + try + { + IPAddress Address = new IPAddress(RawIp); + + HostEntry = Dns.GetHostEntry(Address); + } + catch (SocketException Exception) + { + NetDBErrorCode = NetDBError.Internal; + if (Exception.ErrorCode == 11001) + { + NetDBErrorCode = NetDBError.HostNotFound; + Errno = GaiError.NoData; + } + else if (Exception.ErrorCode == 11002) + { + NetDBErrorCode = NetDBError.TryAgain; + } + else if (Exception.ErrorCode == 11003) + { + NetDBErrorCode = NetDBError.NoRecovery; + } + else if (Exception.ErrorCode == 11004) + { + NetDBErrorCode = NetDBError.NoData; + } + else if (Exception.ErrorCode == 10060) + { + Errno = GaiError.Again; + } + } + } + else + { + NetDBErrorCode = NetDBError.NoAddress; + } + + if (HostEntry != null) + { + Errno = GaiError.Success; + SerializedSize = SerializeHostEnt(Context, HostEntry, GetIPV4Addresses(HostEntry)); + } + + Context.ResponseData.Write((int)NetDBErrorCode); + Context.ResponseData.Write((int)Errno); + Context.ResponseData.Write(SerializedSize); + + return 0; + } + + // GetHostStringError(u32) -> buffer + public long GetHostStringError(ServiceCtx Context) + { + long ResultCode = MakeError(ErrorModule.Os, 1023); + NetDBError ErrorCode = (NetDBError)Context.RequestData.ReadInt32(); + string ErrorString = GetHostStringErrorFromErrorCode(ErrorCode); + + if (ErrorString.Length + 1 <= Context.Request.ReceiveBuff[0].Size) + { + ResultCode = 0; + Context.Memory.WriteBytes(Context.Request.ReceiveBuff[0].Position, Encoding.ASCII.GetBytes(ErrorString + '\0')); + } + + return ResultCode; + } + + // GetGaiStringError(u32) -> buffer + public long GetGaiStringError(ServiceCtx Context) + { + long ResultCode = MakeError(ErrorModule.Os, 1023); + GaiError ErrorCode = (GaiError)Context.RequestData.ReadInt32(); + string ErrorString = GetGaiStringErrorFromErrorCode(ErrorCode); + + if (ErrorString.Length + 1 <= Context.Request.ReceiveBuff[0].Size) + { + ResultCode = 0; + Context.Memory.WriteBytes(Context.Request.ReceiveBuff[0].Position, Encoding.ASCII.GetBytes(ErrorString + '\0')); + } + + return ResultCode; + } + + // RequestCancelHandle(u64, pid) -> u32 + public long RequestCancelHandle(ServiceCtx Context) + { + ulong Unknown0 = Context.RequestData.ReadUInt64(); + + Context.ResponseData.Write(0); + + Context.Device.Log.PrintStub(Logging.LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}"); + + return 0; + } + + // CancelSocketCall(u32, u64, pid) + public long CancelSocketCall(ServiceCtx Context) + { + uint Unknown0 = Context.RequestData.ReadUInt32(); + ulong Unknown1 = Context.RequestData.ReadUInt64(); + + Context.Device.Log.PrintStub(Logging.LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0} - " + + $"Unknown1: {Unknown1}"); + + return 0; + } + + // ClearDnsAddresses(u32) + public long ClearDnsAddresses(ServiceCtx Context) + { + uint Unknown0 = Context.RequestData.ReadUInt32(); + + Context.Device.Log.PrintStub(Logging.LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}"); + + return 0; + } } } diff --git a/Ryujinx.HLE/HOS/Services/Sfdnsres/NetDBError.cs b/Ryujinx.HLE/HOS/Services/Sfdnsres/NetDBError.cs new file mode 100644 index 0000000000..6c1b7825d2 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Sfdnsres/NetDBError.cs @@ -0,0 +1,13 @@ +namespace Ryujinx.HLE.HOS.Services.Sfdnsres +{ + enum NetDBError + { + Internal = -1, + Success, + HostNotFound, + TryAgain, + NoRecovery, + NoData, + NoAddress = NoData, + } +} From dd3cb33c9f43412617e2b2b76d9e2a4dec9a2a23 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Fri, 12 Oct 2018 18:47:53 -0300 Subject: [PATCH 18/30] Fix race condition on OpenALAudioOut (#448) --- Ryujinx.Audio/OpenAL/OpenALAudioOut.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs b/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs index d7a2a77751..9a75c5685e 100644 --- a/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs +++ b/Ryujinx.Audio/OpenAL/OpenALAudioOut.cs @@ -182,7 +182,10 @@ namespace Ryujinx.Audio.OpenAL { foreach (Track Td in Tracks.Values) { - Td.CallReleaseCallbackIfNeeded(); + lock (Td) + { + Td.CallReleaseCallbackIfNeeded(); + } } //If it's not slept it will waste cycles. From aa1cd849cfe254dc7c8c9a0783a546a4b9b3c0ab Mon Sep 17 00:00:00 2001 From: gdkchan Date: Fri, 12 Oct 2018 22:37:01 -0300 Subject: [PATCH 19/30] Quads, QuadStrip, const attributes and half-float attributes support (#447) * Quads, QuadStrip and const attributes support * Add support for half float attributes and fix texture pitch alignment * Throw when an unsupported float type is used as const attribute aswell --- Ryujinx.Graphics/Gal/GalPipelineState.cs | 12 -- Ryujinx.Graphics/Gal/GalVertexAttrib.cs | 11 +- Ryujinx.Graphics/Gal/GalVertexBinding.cs | 14 ++ Ryujinx.Graphics/Gal/IGalRasterizer.cs | 1 + .../Gal/OpenGL/OGLEnumConverter.cs | 28 ++- Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs | 193 ++++++++++++++++-- Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs | 33 ++- .../Gal/OpenGL/OGLRenderTarget.cs | 2 +- Ryujinx.Graphics/NvGpuEngine3d.cs | 83 +++++++- Ryujinx.Graphics/QuadHelper.cs | 81 ++++++++ Ryujinx.Graphics/Texture/ImageUtils.cs | 6 +- Ryujinx.Graphics/Texture/TextureFactory.cs | 9 +- 12 files changed, 420 insertions(+), 53 deletions(-) create mode 100644 Ryujinx.Graphics/Gal/GalVertexBinding.cs create mode 100644 Ryujinx.Graphics/QuadHelper.cs diff --git a/Ryujinx.Graphics/Gal/GalPipelineState.cs b/Ryujinx.Graphics/Gal/GalPipelineState.cs index 7b0f17d177..8837eb8c4b 100644 --- a/Ryujinx.Graphics/Gal/GalPipelineState.cs +++ b/Ryujinx.Graphics/Gal/GalPipelineState.cs @@ -1,17 +1,5 @@ namespace Ryujinx.Graphics.Gal { - public struct GalVertexBinding - { - //VboKey shouldn't be here, but ARB_vertex_attrib_binding is core since 4.3 - - public bool Enabled; - public int Stride; - public long VboKey; - public bool Instanced; - public int Divisor; - public GalVertexAttrib[] Attribs; - } - public class GalPipelineState { public const int Stages = 5; diff --git a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs index dd04006025..fa9a391f71 100644 --- a/Ryujinx.Graphics/Gal/GalVertexAttrib.cs +++ b/Ryujinx.Graphics/Gal/GalVertexAttrib.cs @@ -1,10 +1,13 @@ +using System; + namespace Ryujinx.Graphics.Gal { public struct GalVertexAttrib { - public int Index { get; private set; } - public bool IsConst { get; private set; } - public int Offset { get; private set; } + public int Index { get; private set; } + public bool IsConst { get; private set; } + public int Offset { get; private set; } + public IntPtr Pointer { get; private set; } public GalVertexAttribSize Size { get; private set; } public GalVertexAttribType Type { get; private set; } @@ -15,12 +18,14 @@ namespace Ryujinx.Graphics.Gal int Index, bool IsConst, int Offset, + IntPtr Pointer, GalVertexAttribSize Size, GalVertexAttribType Type, bool IsBgra) { this.Index = Index; this.IsConst = IsConst; + this.Pointer = Pointer; this.Offset = Offset; this.Size = Size; this.Type = Type; diff --git a/Ryujinx.Graphics/Gal/GalVertexBinding.cs b/Ryujinx.Graphics/Gal/GalVertexBinding.cs new file mode 100644 index 0000000000..0c3c845d4f --- /dev/null +++ b/Ryujinx.Graphics/Gal/GalVertexBinding.cs @@ -0,0 +1,14 @@ +namespace Ryujinx.Graphics.Gal +{ + public struct GalVertexBinding + { + //VboKey shouldn't be here, but ARB_vertex_attrib_binding is core since 4.3 + + public bool Enabled; + public int Stride; + public long VboKey; + public bool Instanced; + public int Divisor; + public GalVertexAttrib[] Attribs; + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalRasterizer.cs b/Ryujinx.Graphics/Gal/IGalRasterizer.cs index 1ee630e23b..1572efa8f8 100644 --- a/Ryujinx.Graphics/Gal/IGalRasterizer.cs +++ b/Ryujinx.Graphics/Gal/IGalRasterizer.cs @@ -21,6 +21,7 @@ namespace Ryujinx.Graphics.Gal void CreateVbo(long Key, int DataSize, IntPtr HostAddress); void CreateIbo(long Key, int DataSize, IntPtr HostAddress); + void CreateIbo(long Key, int DataSize, byte[] Buffer); void SetIndexArray(int Size, GalIndexFormat Format); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs index 7f9e9fbe3d..388e06b2dc 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalFrontFace.CCW: return FrontFaceDirection.Ccw; } - throw new ArgumentException(nameof(FrontFace)); + throw new ArgumentException(nameof(FrontFace) + " \"" + FrontFace + "\" is not valid!"); } public static CullFaceMode GetCullFace(GalCullFace CullFace) @@ -25,7 +25,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalCullFace.FrontAndBack: return CullFaceMode.FrontAndBack; } - throw new ArgumentException(nameof(CullFace)); + throw new ArgumentException(nameof(CullFace) + " \"" + CullFace + "\" is not valid!"); } public static StencilOp GetStencilOp(GalStencilOp Op) @@ -42,7 +42,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalStencilOp.DecrWrap: return StencilOp.DecrWrap; } - throw new ArgumentException(nameof(Op)); + throw new ArgumentException(nameof(Op) + " \"" + Op + "\" is not valid!"); } public static DepthFunction GetDepthFunc(GalComparisonOp Func) @@ -66,7 +66,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalComparisonOp.Always: return DepthFunction.Always; } - throw new ArgumentException(nameof(Func)); + throw new ArgumentException(nameof(Func) + " \"" + Func + "\" is not valid!"); } public static StencilFunction GetStencilFunc(GalComparisonOp Func) @@ -84,7 +84,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalIndexFormat.Int32: return DrawElementsType.UnsignedInt; } - throw new ArgumentException(nameof(Format)); + throw new ArgumentException(nameof(Format) + " \"" + Format + "\" is not valid!"); } public static PrimitiveType GetPrimitiveType(GalPrimitiveType Type) @@ -98,8 +98,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalPrimitiveType.Triangles: return PrimitiveType.Triangles; case GalPrimitiveType.TriangleStrip: return PrimitiveType.TriangleStrip; case GalPrimitiveType.TriangleFan: return PrimitiveType.TriangleFan; - case GalPrimitiveType.Quads: return PrimitiveType.Quads; - case GalPrimitiveType.QuadStrip: return PrimitiveType.QuadStrip; case GalPrimitiveType.Polygon: return PrimitiveType.Polygon; case GalPrimitiveType.LinesAdjacency: return PrimitiveType.LinesAdjacency; case GalPrimitiveType.LineStripAdjacency: return PrimitiveType.LineStripAdjacency; @@ -108,7 +106,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalPrimitiveType.Patches: return PrimitiveType.Patches; } - throw new ArgumentException(nameof(Type)); + throw new ArgumentException(nameof(Type) + " \"" + Type + "\" is not valid!"); } public static ShaderType GetShaderType(GalShaderType Type) @@ -122,7 +120,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalShaderType.Fragment: return ShaderType.FragmentShader; } - throw new ArgumentException(nameof(Type)); + throw new ArgumentException(nameof(Type) + " \"" + Type + "\" is not valid!"); } public static (PixelInternalFormat, PixelFormat, PixelType) GetImageFormat(GalImageFormat Format) @@ -211,7 +209,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalTextureSource.OneFloat: return All.One; } - throw new ArgumentException(nameof(Source)); + throw new ArgumentException(nameof(Source) + " \"" + Source + "\" is not valid!"); } public static TextureWrapMode GetTextureWrapMode(GalTextureWrap Wrap) @@ -245,7 +243,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } - throw new ArgumentException(nameof(Wrap)); + throw new ArgumentException(nameof(Wrap) + " \"" + Wrap + "\" is not valid!"); } public static TextureMinFilter GetTextureMinFilter( @@ -259,7 +257,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalTextureFilter.Linear: return TextureMinFilter.Linear; } - throw new ArgumentException(nameof(MinFilter)); + throw new ArgumentException(nameof(MinFilter) + " \"" + MinFilter + "\" is not valid!"); } public static TextureMagFilter GetTextureMagFilter(GalTextureFilter Filter) @@ -270,7 +268,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalTextureFilter.Linear: return TextureMagFilter.Linear; } - throw new ArgumentException(nameof(Filter)); + throw new ArgumentException(nameof(Filter) + " \"" + Filter + "\" is not valid!"); } public static BlendEquationMode GetBlendEquation(GalBlendEquation BlendEquation) @@ -284,7 +282,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalBlendEquation.Max: return BlendEquationMode.Max; } - throw new ArgumentException(nameof(BlendEquation)); + throw new ArgumentException(nameof(BlendEquation) + " \"" + BlendEquation + "\" is not valid!"); } public static BlendingFactor GetBlendFactor(GalBlendFactor BlendFactor) @@ -315,7 +313,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL return BlendingFactor.ConstantColor; } - throw new ArgumentException(nameof(BlendFactor)); + throw new ArgumentException(nameof(BlendFactor) + " \"" + BlendFactor + "\" is not valid!"); } } } diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs index 20e92ff2c6..96da17f8e5 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs @@ -25,6 +25,19 @@ namespace Ryujinx.Graphics.Gal.OpenGL { GalVertexAttribSize._11_11_10, 3 } }; + private static Dictionary FloatAttribTypes = + new Dictionary() + { + { GalVertexAttribSize._32_32_32_32, VertexAttribPointerType.Float }, + { GalVertexAttribSize._32_32_32, VertexAttribPointerType.Float }, + { GalVertexAttribSize._16_16_16_16, VertexAttribPointerType.HalfFloat }, + { GalVertexAttribSize._32_32, VertexAttribPointerType.Float }, + { GalVertexAttribSize._16_16_16, VertexAttribPointerType.HalfFloat }, + { GalVertexAttribSize._16_16, VertexAttribPointerType.HalfFloat }, + { GalVertexAttribSize._32, VertexAttribPointerType.Float }, + { GalVertexAttribSize._16, VertexAttribPointerType.HalfFloat } + }; + private static Dictionary SignedAttribTypes = new Dictionary() { @@ -356,8 +369,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL continue; } - GL.EnableVertexAttribArray(Attrib.Index); - GL.BindBuffer(BufferTarget.ArrayBuffer, VboHandle); bool Unsigned = @@ -373,35 +384,50 @@ namespace Ryujinx.Graphics.Gal.OpenGL if (Attrib.Type == GalVertexAttribType.Float) { - Type = VertexAttribPointerType.Float; + Type = GetType(FloatAttribTypes, Attrib); } else { if (Unsigned) { - Type = UnsignedAttribTypes[Attrib.Size]; + Type = GetType(UnsignedAttribTypes, Attrib); } else { - Type = SignedAttribTypes[Attrib.Size]; + Type = GetType(SignedAttribTypes, Attrib); } } - int Size = AttribElements[Attrib.Size]; + if (!AttribElements.TryGetValue(Attrib.Size, out int Size)) + { + throw new InvalidOperationException("Invalid attribute size \"" + Attrib.Size + "\"!"); + } + int Offset = Attrib.Offset; - if (Attrib.Type == GalVertexAttribType.Sint || - Attrib.Type == GalVertexAttribType.Uint) + if (Binding.Stride != 0) { - IntPtr Pointer = new IntPtr(Offset); + GL.EnableVertexAttribArray(Attrib.Index); - VertexAttribIntegerType IType = (VertexAttribIntegerType)Type; + if (Attrib.Type == GalVertexAttribType.Sint || + Attrib.Type == GalVertexAttribType.Uint) + { + IntPtr Pointer = new IntPtr(Offset); - GL.VertexAttribIPointer(Attrib.Index, Size, IType, Binding.Stride, Pointer); + VertexAttribIntegerType IType = (VertexAttribIntegerType)Type; + + GL.VertexAttribIPointer(Attrib.Index, Size, IType, Binding.Stride, Pointer); + } + else + { + GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Binding.Stride, Offset); + } } else { - GL.VertexAttribPointer(Attrib.Index, Size, Type, Normalize, Binding.Stride, Offset); + GL.DisableVertexAttribArray(Attrib.Index); + + SetConstAttrib(Attrib); } if (Binding.Instanced && Binding.Divisor != 0) @@ -416,6 +442,149 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } + private static VertexAttribPointerType GetType(Dictionary Dict, GalVertexAttrib Attrib) + { + if (!Dict.TryGetValue(Attrib.Size, out VertexAttribPointerType Type)) + { + throw new NotImplementedException("Unsupported size \"" + Attrib.Size + "\" on type \"" + Attrib.Type + "\"!"); + } + + return Type; + } + + private unsafe static void SetConstAttrib(GalVertexAttrib Attrib) + { + void Unsupported() + { + throw new NotImplementedException("Constant attribute " + Attrib.Size + " not implemented!"); + } + + if (Attrib.Size == GalVertexAttribSize._10_10_10_2 || + Attrib.Size == GalVertexAttribSize._11_11_10) + { + Unsupported(); + } + + if (Attrib.Type == GalVertexAttribType.Unorm) + { + switch (Attrib.Size) + { + case GalVertexAttribSize._8: + case GalVertexAttribSize._8_8: + case GalVertexAttribSize._8_8_8: + case GalVertexAttribSize._8_8_8_8: + GL.VertexAttrib4N((uint)Attrib.Index, (byte*)Attrib.Pointer); + break; + + case GalVertexAttribSize._16: + case GalVertexAttribSize._16_16: + case GalVertexAttribSize._16_16_16: + case GalVertexAttribSize._16_16_16_16: + GL.VertexAttrib4N((uint)Attrib.Index, (ushort*)Attrib.Pointer); + break; + + case GalVertexAttribSize._32: + case GalVertexAttribSize._32_32: + case GalVertexAttribSize._32_32_32: + case GalVertexAttribSize._32_32_32_32: + GL.VertexAttrib4N((uint)Attrib.Index, (uint*)Attrib.Pointer); + break; + } + } + else if (Attrib.Type == GalVertexAttribType.Snorm) + { + switch (Attrib.Size) + { + case GalVertexAttribSize._8: + case GalVertexAttribSize._8_8: + case GalVertexAttribSize._8_8_8: + case GalVertexAttribSize._8_8_8_8: + GL.VertexAttrib4N((uint)Attrib.Index, (sbyte*)Attrib.Pointer); + break; + + case GalVertexAttribSize._16: + case GalVertexAttribSize._16_16: + case GalVertexAttribSize._16_16_16: + case GalVertexAttribSize._16_16_16_16: + GL.VertexAttrib4N((uint)Attrib.Index, (short*)Attrib.Pointer); + break; + + case GalVertexAttribSize._32: + case GalVertexAttribSize._32_32: + case GalVertexAttribSize._32_32_32: + case GalVertexAttribSize._32_32_32_32: + GL.VertexAttrib4N((uint)Attrib.Index, (int*)Attrib.Pointer); + break; + } + } + else if (Attrib.Type == GalVertexAttribType.Uint) + { + switch (Attrib.Size) + { + case GalVertexAttribSize._8: + case GalVertexAttribSize._8_8: + case GalVertexAttribSize._8_8_8: + case GalVertexAttribSize._8_8_8_8: + GL.VertexAttribI4((uint)Attrib.Index, (byte*)Attrib.Pointer); + break; + + case GalVertexAttribSize._16: + case GalVertexAttribSize._16_16: + case GalVertexAttribSize._16_16_16: + case GalVertexAttribSize._16_16_16_16: + GL.VertexAttribI4((uint)Attrib.Index, (ushort*)Attrib.Pointer); + break; + + case GalVertexAttribSize._32: + case GalVertexAttribSize._32_32: + case GalVertexAttribSize._32_32_32: + case GalVertexAttribSize._32_32_32_32: + GL.VertexAttribI4((uint)Attrib.Index, (uint*)Attrib.Pointer); + break; + } + } + else if (Attrib.Type == GalVertexAttribType.Sint) + { + switch (Attrib.Size) + { + case GalVertexAttribSize._8: + case GalVertexAttribSize._8_8: + case GalVertexAttribSize._8_8_8: + case GalVertexAttribSize._8_8_8_8: + GL.VertexAttribI4((uint)Attrib.Index, (sbyte*)Attrib.Pointer); + break; + + case GalVertexAttribSize._16: + case GalVertexAttribSize._16_16: + case GalVertexAttribSize._16_16_16: + case GalVertexAttribSize._16_16_16_16: + GL.VertexAttribI4((uint)Attrib.Index, (short*)Attrib.Pointer); + break; + + case GalVertexAttribSize._32: + case GalVertexAttribSize._32_32: + case GalVertexAttribSize._32_32_32: + case GalVertexAttribSize._32_32_32_32: + GL.VertexAttribI4((uint)Attrib.Index, (int*)Attrib.Pointer); + break; + } + } + else if (Attrib.Type == GalVertexAttribType.Float) + { + switch (Attrib.Size) + { + case GalVertexAttribSize._32: + case GalVertexAttribSize._32_32: + case GalVertexAttribSize._32_32_32: + case GalVertexAttribSize._32_32_32_32: + GL.VertexAttrib4(Attrib.Index, (float*)Attrib.Pointer); + break; + + default: Unsupported(); break; + } + } + } + private void Enable(EnableCap Cap, bool Enabled) { if (Enabled) diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs index a74aee0773..7b435c455f 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs @@ -119,6 +119,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.BufferData(BufferTarget.ElementArrayBuffer, Length, HostAddress, BufferUsageHint.StreamDraw); } + public void CreateIbo(long Key, int DataSize, byte[] Buffer) + { + int Handle = GL.GenBuffer(); + + IboCache.AddOrUpdate(Key, Handle, (uint)DataSize); + + IntPtr Length = new IntPtr(Buffer.Length); + + GL.BindBuffer(BufferTarget.ElementArrayBuffer, Handle); + GL.BufferData(BufferTarget.ElementArrayBuffer, Length, Buffer, BufferUsageHint.StreamDraw); + } + public void SetIndexArray(int Size, GalIndexFormat Format) { IndexBuffer.Type = OGLEnumConverter.GetDrawElementsType(Format); @@ -135,7 +147,26 @@ namespace Ryujinx.Graphics.Gal.OpenGL return; } - GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, Count); + if (PrimType == GalPrimitiveType.Quads) + { + for (int Offset = 0; Offset < Count; Offset += 4) + { + GL.DrawArrays(PrimitiveType.TriangleFan, First + Offset, 4); + } + } + else if (PrimType == GalPrimitiveType.QuadStrip) + { + GL.DrawArrays(PrimitiveType.TriangleFan, First, 4); + + for (int Offset = 2; Offset < Count; Offset += 2) + { + GL.DrawArrays(PrimitiveType.TriangleFan, First + Offset, 4); + } + } + else + { + GL.DrawArrays(OGLEnumConverter.GetPrimitiveType(PrimType), First, Count); + } } public void DrawElements(long IboKey, int First, int VertexBase, GalPrimitiveType PrimType) diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs index 78cf5d2fa9..0de070b545 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs @@ -219,7 +219,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL { Attachments.Zeta = Key; } - + public void UnbindZeta() { Attachments.Zeta = 0; diff --git a/Ryujinx.Graphics/NvGpuEngine3d.cs b/Ryujinx.Graphics/NvGpuEngine3d.cs index 22c0937770..3dd7746dae 100644 --- a/Ryujinx.Graphics/NvGpuEngine3d.cs +++ b/Ryujinx.Graphics/NvGpuEngine3d.cs @@ -433,7 +433,7 @@ namespace Ryujinx.Graphics private void SetRenderTargets() { - //Commercial games do not seem to + //Commercial games do not seem to //bool SeparateFragData = ReadRegisterBool(NvGpuEngine3dReg.RTSeparateFragData); uint Control = (uint)(ReadRegister(NvGpuEngine3dReg.RTControl)); @@ -568,12 +568,15 @@ namespace Ryujinx.Graphics private void UploadVertexArrays(NvGpuVmm Vmm, GalPipelineState State) { - long IndexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); + long IbPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.IndexArrayAddress); - long IboKey = Vmm.GetPhysicalAddress(IndexPosition); + long IboKey = Vmm.GetPhysicalAddress(IbPosition); int IndexEntryFmt = ReadRegister(NvGpuEngine3dReg.IndexArrayFormat); int IndexCount = ReadRegister(NvGpuEngine3dReg.IndexBatchCount); + int PrimCtrl = ReadRegister(NvGpuEngine3dReg.VertexBeginGl); + + GalPrimitiveType PrimType = (GalPrimitiveType)(PrimCtrl & 0xffff); GalIndexFormat IndexFormat = (GalIndexFormat)IndexEntryFmt; @@ -590,14 +593,50 @@ namespace Ryujinx.Graphics bool IboCached = Gpu.Renderer.Rasterizer.IsIboCached(IboKey, (uint)IbSize); + bool UsesLegacyQuads = + PrimType == GalPrimitiveType.Quads || + PrimType == GalPrimitiveType.QuadStrip; + if (!IboCached || QueryKeyUpload(Vmm, IboKey, (uint)IbSize, NvGpuBufferType.Index)) { - IntPtr DataAddress = Vmm.GetHostAddress(IndexPosition, IbSize); + if (!UsesLegacyQuads) + { + IntPtr DataAddress = Vmm.GetHostAddress(IbPosition, IbSize); - Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, DataAddress); + Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, DataAddress); + } + else + { + byte[] Buffer = Vmm.ReadBytes(IbPosition, IbSize); + + if (PrimType == GalPrimitiveType.Quads) + { + Buffer = QuadHelper.ConvertIbQuadsToTris(Buffer, IndexEntrySize, IndexCount); + } + else /* if (PrimType == GalPrimitiveType.QuadStrip) */ + { + Buffer = QuadHelper.ConvertIbQuadStripToTris(Buffer, IndexEntrySize, IndexCount); + } + + Gpu.Renderer.Rasterizer.CreateIbo(IboKey, IbSize, Buffer); + } } - Gpu.Renderer.Rasterizer.SetIndexArray(IbSize, IndexFormat); + if (!UsesLegacyQuads) + { + Gpu.Renderer.Rasterizer.SetIndexArray(IbSize, IndexFormat); + } + else + { + if (PrimType == GalPrimitiveType.Quads) + { + Gpu.Renderer.Rasterizer.SetIndexArray(QuadHelper.ConvertIbSizeQuadsToTris(IbSize), IndexFormat); + } + else /* if (PrimType == GalPrimitiveType.QuadStrip) */ + { + Gpu.Renderer.Rasterizer.SetIndexArray(QuadHelper.ConvertIbSizeQuadStripToTris(IbSize), IndexFormat); + } + } } List[] Attribs = new List[32]; @@ -613,10 +652,19 @@ namespace Ryujinx.Graphics Attribs[ArrayIndex] = new List(); } + long VertexPosition = MakeInt64From2xInt32(NvGpuEngine3dReg.VertexArrayNAddress + ArrayIndex * 4); + + int Offset = (Packed >> 7) & 0x3fff; + + //Note: 16 is the maximum size of an attribute, + //having a component size of 32-bits with 4 elements (a vec4). + IntPtr Pointer = Vmm.GetHostAddress(VertexPosition + Offset, 16); + Attribs[ArrayIndex].Add(new GalVertexAttrib( Attr, ((Packed >> 6) & 0x1) != 0, - (Packed >> 7) & 0x3fff, + Offset, + Pointer, (GalVertexAttribSize)((Packed >> 21) & 0x3f), (GalVertexAttribType)((Packed >> 27) & 0x7), ((Packed >> 31) & 0x1) != 0)); @@ -722,6 +770,27 @@ namespace Ryujinx.Graphics long IboKey = Vmm.GetPhysicalAddress(IndexPosition); + //Quad primitive types were deprecated on OpenGL 3.x, + //they are converted to a triangles index buffer on IB creation, + //so we should use the triangles type here too. + if (PrimType == GalPrimitiveType.Quads || + PrimType == GalPrimitiveType.QuadStrip) + { + PrimType = GalPrimitiveType.Triangles; + + //Note: We assume that index first points to the first + //vertex of a quad, if it points to the middle of a + //quad (First % 4 != 0 for Quads) then it will not work properly. + if (PrimType == GalPrimitiveType.Quads) + { + IndexFirst = QuadHelper.ConvertIbSizeQuadsToTris(IndexFirst); + } + else /* if (PrimType == GalPrimitiveType.QuadStrip) */ + { + IndexFirst = QuadHelper.ConvertIbSizeQuadStripToTris(IndexFirst); + } + } + Gpu.Renderer.Rasterizer.DrawElements(IboKey, IndexFirst, VertexBase, PrimType); } else diff --git a/Ryujinx.Graphics/QuadHelper.cs b/Ryujinx.Graphics/QuadHelper.cs new file mode 100644 index 0000000000..0dfffce0bc --- /dev/null +++ b/Ryujinx.Graphics/QuadHelper.cs @@ -0,0 +1,81 @@ +using System; + +namespace Ryujinx.Graphics +{ + static class QuadHelper + { + public static int ConvertIbSizeQuadsToTris(int Size) + { + return Size <= 0 ? 0 : (Size / 4) * 6; + } + + public static int ConvertIbSizeQuadStripToTris(int Size) + { + return Size <= 1 ? 0 : ((Size - 2) / 2) * 6; + } + + public static byte[] ConvertIbQuadsToTris(byte[] Data, int EntrySize, int Count) + { + int PrimitivesCount = Count / 4; + + int QuadPrimSize = 4 * EntrySize; + int TrisPrimSize = 6 * EntrySize; + + byte[] Output = new byte[PrimitivesCount * 6 * EntrySize]; + + for (int Prim = 0; Prim < PrimitivesCount; Prim++) + { + void AssignIndex(int Src, int Dst, int CopyCount = 1) + { + Src = Prim * QuadPrimSize + Src * EntrySize; + Dst = Prim * TrisPrimSize + Dst * EntrySize; + + Buffer.BlockCopy(Data, Src, Output, Dst, CopyCount * EntrySize); + } + + //0 1 2 -> 0 1 2. + AssignIndex(0, 0, 3); + + //2 3 -> 3 4. + AssignIndex(2, 3, 2); + + //0 -> 5. + AssignIndex(0, 5); + } + + return Output; + } + + public static byte[] ConvertIbQuadStripToTris(byte[] Data, int EntrySize, int Count) + { + int PrimitivesCount = (Count - 2) / 2; + + int QuadPrimSize = 2 * EntrySize; + int TrisPrimSize = 6 * EntrySize; + + byte[] Output = new byte[PrimitivesCount * 6 * EntrySize]; + + for (int Prim = 0; Prim < PrimitivesCount; Prim++) + { + void AssignIndex(int Src, int Dst, int CopyCount = 1) + { + Src = Prim * QuadPrimSize + Src * EntrySize + 2 * EntrySize; + Dst = Prim * TrisPrimSize + Dst * EntrySize; + + Buffer.BlockCopy(Data, Src, Output, Dst, CopyCount * EntrySize); + } + + //-2 -1 0 -> 0 1 2. + AssignIndex(-2, 0, 3); + + //0 1 -> 3 4. + AssignIndex(0, 3, 2); + + //-2 -> 5. + AssignIndex(-2, 5); + } + + return Output; + } + } +} \ No newline at end of file diff --git a/Ryujinx.Graphics/Texture/ImageUtils.cs b/Ryujinx.Graphics/Texture/ImageUtils.cs index 18a179fbfa..1b043245ef 100644 --- a/Ryujinx.Graphics/Texture/ImageUtils.cs +++ b/Ryujinx.Graphics/Texture/ImageUtils.cs @@ -289,7 +289,11 @@ namespace Ryujinx.Graphics.Texture { ImageDescriptor Desc = GetImageDescriptor(Format); - return Desc.BytesPerPixel * DivRoundUp(Width, Desc.BlockWidth); + int Pitch = Desc.BytesPerPixel * DivRoundUp(Width, Desc.BlockWidth); + + Pitch = (Pitch + 0x1f) & ~0x1f; + + return Pitch; } public static int GetBlockWidth(GalImageFormat Format) diff --git a/Ryujinx.Graphics/Texture/TextureFactory.cs b/Ryujinx.Graphics/Texture/TextureFactory.cs index 766c53da46..c0c53b06ef 100644 --- a/Ryujinx.Graphics/Texture/TextureFactory.cs +++ b/Ryujinx.Graphics/Texture/TextureFactory.cs @@ -40,7 +40,7 @@ namespace Ryujinx.Graphics.Texture int Width = (Tic[4] & 0xffff) + 1; int Height = (Tic[5] & 0xffff) + 1; - return new GalImage( + GalImage Image = new GalImage( Width, Height, TileWidth, @@ -51,6 +51,13 @@ namespace Ryujinx.Graphics.Texture YSource, ZSource, WSource); + + if (Layout == GalMemoryLayout.Pitch) + { + Image.Pitch = (Tic[3] & 0xffff) << 5; + } + + return Image; } public static GalTextureSampler MakeSampler(NvGpu Gpu, NvGpuVmm Vmm, long TscPosition) From 824d4b74d068b4dbdd167c37efe1df9afa493822 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Sat, 13 Oct 2018 21:29:49 +0000 Subject: [PATCH 20/30] Update UInt128.cs (#453) Fix invalid Write implementation. --- Ryujinx.HLE/Utilities/UInt128.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Ryujinx.HLE/Utilities/UInt128.cs b/Ryujinx.HLE/Utilities/UInt128.cs index a2546dd3cd..54c0c35fec 100644 --- a/Ryujinx.HLE/Utilities/UInt128.cs +++ b/Ryujinx.HLE/Utilities/UInt128.cs @@ -44,8 +44,8 @@ namespace Ryujinx.HLE.Utilities public void Write(BinaryWriter BinaryWriter) { - BinaryWriter.Write(High); BinaryWriter.Write(Low); + BinaryWriter.Write(High); } public override string ToString() @@ -58,4 +58,4 @@ namespace Ryujinx.HLE.Utilities return (Low | High) == 0; } } -} \ No newline at end of file +} From 3561062bc67cde7423d64237170845a206a441c6 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Sat, 13 Oct 2018 23:16:02 +0000 Subject: [PATCH 21/30] Update IAccountService and IManagerForApplication (#454) * Update IAccountService and IManagerForApplication `IAccountService`: - Add symbols. - Fix some mistake. - Add `IsUserRegistrationRequestPermitted` and `TrySelectUserWithoutInteraction`. `IManagerForApplication`: - Add symbols. - Add Uuid args. - Little improvement of `GetAccountId` --- .../HOS/Services/Acc/IAccountService.cs | 50 +++++++++++++++++-- .../Services/Acc/IManagerForApplication.cs | 15 ++++-- 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs b/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs index 347f2e20c7..08c4c88cd7 100644 --- a/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs +++ b/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs @@ -24,11 +24,14 @@ namespace Ryujinx.HLE.HOS.Services.Acc { 3, ListOpenUsers }, { 4, GetLastOpenedUser }, { 5, GetProfile }, + { 50, IsUserRegistrationRequestPermitted }, + { 51, TrySelectUserWithoutInteraction }, { 100, InitializeApplicationInfo }, { 101, GetBaasAccountManagerForApplication } }; } + // GetUserCount() -> i32 public long GetUserCount(ServiceCtx Context) { Context.ResponseData.Write(Context.Device.System.State.GetUserCount()); @@ -36,22 +39,25 @@ namespace Ryujinx.HLE.HOS.Services.Acc return 0; } + // GetUserExistence(nn::account::Uid) -> bool public long GetUserExistence(ServiceCtx Context) { UInt128 Uuid = new UInt128( Context.RequestData.ReadInt64(), Context.RequestData.ReadInt64()); - Context.ResponseData.Write(Context.Device.System.State.TryGetUser(Uuid, out _) ? 1 : 0); + Context.ResponseData.Write(Context.Device.System.State.TryGetUser(Uuid, out _)); return 0; } + // ListAllUsers() -> array public long ListAllUsers(ServiceCtx Context) { return WriteUserList(Context, Context.Device.System.State.GetAllUsers()); } + // ListOpenUsers() -> array public long ListOpenUsers(ServiceCtx Context) { return WriteUserList(Context, Context.Device.System.State.GetOpenUsers()); @@ -78,6 +84,7 @@ namespace Ryujinx.HLE.HOS.Services.Acc return 0; } + // GetLastOpenedUser() -> nn::account::Uid public long GetLastOpenedUser(ServiceCtx Context) { UserProfile LastOpened = Context.Device.System.State.LastOpenUser; @@ -87,6 +94,7 @@ namespace Ryujinx.HLE.HOS.Services.Acc return 0; } + // GetProfile(nn::account::Uid) -> object public long GetProfile(ServiceCtx Context) { UInt128 Uuid = new UInt128( @@ -105,16 +113,50 @@ namespace Ryujinx.HLE.HOS.Services.Acc return 0; } - public long InitializeApplicationInfo(ServiceCtx Context) + // IsUserRegistrationRequestPermitted(u64, pid) -> bool + public long IsUserRegistrationRequestPermitted(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAcc, "Stubbed."); + long Unknown = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceAcc, $"Stubbed. Unknown: {Unknown}"); + + Context.ResponseData.Write(false); return 0; } + // TrySelectUserWithoutInteraction(bool) -> nn::account::Uid + public long TrySelectUserWithoutInteraction(ServiceCtx Context) + { + bool Unknown = Context.RequestData.ReadBoolean(); + + Context.Device.Log.PrintStub(LogClass.ServiceAcc, $"Stubbed. Unknown: {Unknown}"); + + UserProfile Profile = Context.Device.System.State.LastOpenUser; + + Profile.Uuid.Write(Context.ResponseData); + + return 0; + } + + // InitializeApplicationInfo(u64, pid) + public long InitializeApplicationInfo(ServiceCtx Context) + { + long Unknown = Context.RequestData.ReadInt64(); + + Context.Device.Log.PrintStub(LogClass.ServiceAcc, $"Stubbed. Unknown: {Unknown}"); + + return 0; + } + + // GetBaasAccountManagerForApplication(nn::account::Uid) -> object public long GetBaasAccountManagerForApplication(ServiceCtx Context) { - MakeObject(Context, new IManagerForApplication()); + UInt128 Uuid = new UInt128( + Context.RequestData.ReadInt64(), + Context.RequestData.ReadInt64()); + + MakeObject(Context, new IManagerForApplication(Uuid)); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Acc/IManagerForApplication.cs b/Ryujinx.HLE/HOS/Services/Acc/IManagerForApplication.cs index 813a1b17e5..ed0e6efb69 100644 --- a/Ryujinx.HLE/HOS/Services/Acc/IManagerForApplication.cs +++ b/Ryujinx.HLE/HOS/Services/Acc/IManagerForApplication.cs @@ -1,24 +1,30 @@ using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.Logging; +using Ryujinx.HLE.Utilities; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Acc { class IManagerForApplication : IpcService { + private UInt128 Uuid; + private Dictionary m_Commands; public override IReadOnlyDictionary Commands => m_Commands; - public IManagerForApplication() + public IManagerForApplication(UInt128 Uuid) { m_Commands = new Dictionary() { { 0, CheckAvailability }, { 1, GetAccountId } }; + + this.Uuid = Uuid; } + // CheckAvailability() public long CheckAvailability(ServiceCtx Context) { Context.Device.Log.PrintStub(LogClass.ServiceAcc, "Stubbed."); @@ -26,11 +32,14 @@ namespace Ryujinx.HLE.HOS.Services.Acc return 0; } + // GetAccountId() -> nn::account::NetworkServiceAccountId public long GetAccountId(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAcc, "Stubbed."); + long NetworkServiceAccountId = 0xcafe; - Context.ResponseData.Write(0xcafeL); + Context.Device.Log.PrintStub(LogClass.ServiceAcc, $"Stubbed. NetworkServiceAccountId: {NetworkServiceAccountId}"); + + Context.ResponseData.Write(NetworkServiceAccountId); return 0; } From ac1a379265d0c02a8bd4a146c205f21e2d00f3ab Mon Sep 17 00:00:00 2001 From: Thomas Guillemard Date: Sun, 14 Oct 2018 04:07:56 +0200 Subject: [PATCH 22/30] Fix some issues with UserId (#455) --- Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs | 4 ++-- Ryujinx.HLE/Utilities/UInt128.cs | 17 +---------------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs b/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs index 08c4c88cd7..d866a85359 100644 --- a/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs +++ b/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs @@ -77,8 +77,8 @@ namespace Ryujinx.HLE.HOS.Services.Acc break; } - Context.Memory.WriteInt64(OutputPosition, Profile.Uuid.High); - Context.Memory.WriteInt64(OutputPosition + 8, Profile.Uuid.Low); + Context.Memory.WriteInt64(OutputPosition, Profile.Uuid.Low); + Context.Memory.WriteInt64(OutputPosition + 8, Profile.Uuid.High); } return 0; diff --git a/Ryujinx.HLE/Utilities/UInt128.cs b/Ryujinx.HLE/Utilities/UInt128.cs index 54c0c35fec..95b3d624dd 100644 --- a/Ryujinx.HLE/Utilities/UInt128.cs +++ b/Ryujinx.HLE/Utilities/UInt128.cs @@ -14,21 +14,6 @@ namespace Ryujinx.HLE.Utilities { this.Low = Low; this.High = High; - - byte[] Bytes = new byte[16]; - - int Index = Bytes.Length; - - void WriteBytes(long Value) - { - for (int Byte = 0; Byte < 8; Byte++) - { - Bytes[--Index] = (byte)(Value >> Byte * 8); - } - } - - WriteBytes(Low); - WriteBytes(High); } public UInt128(string UInt128Hex) @@ -38,7 +23,7 @@ namespace Ryujinx.HLE.Utilities throw new ArgumentException("Invalid Hex value!", nameof(UInt128Hex)); } - Low = Convert.ToInt64(UInt128Hex.Substring(16),16); + Low = Convert.ToInt64(UInt128Hex.Substring(16), 16); High = Convert.ToInt64(UInt128Hex.Substring(0, 16), 16); } From 894459fcd7797b1e38f2448797d83856d11b6e23 Mon Sep 17 00:00:00 2001 From: LDj3SNuD <35856442+LDj3SNuD@users.noreply.github.com> Date: Sun, 14 Oct 2018 04:35:16 +0200 Subject: [PATCH 23/30] Add Fmls_Se, Fmulx_Se/Ve, Smov_S Inst.; Opt. Clz/Clz_V, Cnt_V, Shl_V, S/Ushr_V, S/Usra_V Inst.; Add 11 Tests. Some fixes. (#449) * Update AOpCodeTable.cs * Update AInstEmitSimdMove.cs * Update AInstEmitSimdArithmetic.cs * Update AInstEmitSimdShift.cs * Update ASoftFallback.cs * Update ASoftFloat.cs * Update AOpCodeSimdRegElemF.cs * Update CpuTestSimdIns.cs * Update CpuTestSimdRegElem.cs * Create CpuTestSimdRegElemF.cs * Update CpuTestSimd.cs * Update CpuTestSimdReg.cs * Superseded Fmul_Se Test. Nit. * Address PR feedback. * Address PR feedback. * Update AInstEmitSimdArithmetic.cs * Update ASoftFallback.cs * Update AInstEmitAlu.cs * Update AInstEmitSimdShift.cs --- ChocolArm64/AOpCodeTable.cs | 14 +- ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs | 27 +- ChocolArm64/Instruction/AInstEmitAlu.cs | 14 +- .../Instruction/AInstEmitSimdArithmetic.cs | 91 +++- ChocolArm64/Instruction/AInstEmitSimdMove.cs | 14 +- ChocolArm64/Instruction/AInstEmitSimdShift.cs | 131 +++++- ChocolArm64/Instruction/ASoftFallback.cs | 19 +- ChocolArm64/Instruction/ASoftFloat.cs | 16 +- Ryujinx.Tests/Cpu/CpuTestSimd.cs | 58 ++- Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs | 36 +- Ryujinx.Tests/Cpu/CpuTestSimdIns.cs | 109 ++++- Ryujinx.Tests/Cpu/CpuTestSimdReg.cs | 68 +-- Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs | 145 +++--- Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs | 424 ++++++++++++++++++ 14 files changed, 938 insertions(+), 228 deletions(-) create mode 100644 Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs diff --git a/ChocolArm64/AOpCodeTable.cs b/ChocolArm64/AOpCodeTable.cs index 3002571226..44493298bc 100644 --- a/ChocolArm64/AOpCodeTable.cs +++ b/ChocolArm64/AOpCodeTable.cs @@ -284,11 +284,12 @@ namespace ChocolArm64 SetA64("000111100x1xxxxx011110xxxxxxxxxx", AInstEmit.Fminnm_S, typeof(AOpCodeSimdReg)); SetA64("0>0011101<1xxxxx110001xxxxxxxxxx", AInstEmit.Fminnm_V, typeof(AOpCodeSimdReg)); SetA64("0>1011101<1xxxxx111101xxxxxxxxxx", AInstEmit.Fminp_V, typeof(AOpCodeSimdReg)); - SetA64("010111111<0011100<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmla_V, typeof(AOpCodeSimdReg)); - SetA64("0x0011111<00111110011101<1xxxxx110011xxxxxxxxxx", AInstEmit.Fmls_V, typeof(AOpCodeSimdReg)); - SetA64("0x0011111<00111111011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmul_V, typeof(AOpCodeSimdReg)); - SetA64("0x0011111<00111110011100<1xxxxx110111xxxxxxxxxx", AInstEmit.Fmulx_V, typeof(AOpCodeSimdReg)); + SetA64("0>10111111011101<100000111110xxxxxxxxxx", AInstEmit.Fneg_V, typeof(AOpCodeSimd)); SetA64("000111110x1xxxxx0xxxxxxxxxxxxxxx", AInstEmit.Fnmadd_S, typeof(AOpCodeSimdReg)); @@ -401,6 +404,7 @@ namespace ChocolArm64 SetA64("0x001110<<1xxxxx101011xxxxxxxxxx", AInstEmit.Sminp_V, typeof(AOpCodeSimdReg)); SetA64("0x001110<<1xxxxx100000xxxxxxxxxx", AInstEmit.Smlal_V, typeof(AOpCodeSimdReg)); SetA64("0x001110<<1xxxxx101000xxxxxxxxxx", AInstEmit.Smlsl_V, typeof(AOpCodeSimdReg)); + SetA64("0x001110000xxxxx001011xxxxxxxxxx", AInstEmit.Smov_S, typeof(AOpCodeSimdIns)); SetA64("0x001110<<1xxxxx110000xxxxxxxxxx", AInstEmit.Smull_V, typeof(AOpCodeSimdReg)); SetA64("01011110xx100000011110xxxxxxxxxx", AInstEmit.Sqabs_S, typeof(AOpCodeSimd)); SetA64("0>001110<<100000011110xxxxxxxxxx", AInstEmit.Sqabs_V, typeof(AOpCodeSimd)); diff --git a/ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs b/ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs index e61d7093a7..e0670def45 100644 --- a/ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs +++ b/ChocolArm64/Decoder/AOpCodeSimdRegElemF.cs @@ -8,15 +8,26 @@ namespace ChocolArm64.Decoder public AOpCodeSimdRegElemF(AInst Inst, long Position, int OpCode) : base(Inst, Position, OpCode) { - if ((Size & 1) != 0) + switch ((OpCode >> 21) & 3) // sz:L { - Index = (OpCode >> 11) & 1; - } - else - { - Index = (OpCode >> 21) & 1 | - (OpCode >> 10) & 2; + case 0: // H:0 + Index = (OpCode >> 10) & 2; // 0, 2 + + break; + + case 1: // H:1 + Index = (OpCode >> 10) & 2; + Index++; // 1, 3 + + break; + + case 2: // H + Index = (OpCode >> 11) & 1; // 0, 1 + + break; + + default: Emitter = AInstEmit.Und; return; } } } -} \ No newline at end of file +} diff --git a/ChocolArm64/Instruction/AInstEmitAlu.cs b/ChocolArm64/Instruction/AInstEmitAlu.cs index 490387e129..4551346bd9 100644 --- a/ChocolArm64/Instruction/AInstEmitAlu.cs +++ b/ChocolArm64/Instruction/AInstEmitAlu.cs @@ -4,6 +4,7 @@ using ChocolArm64.Translation; using System; using System.Reflection; using System.Reflection.Emit; +using System.Runtime.Intrinsics.X86; using static ChocolArm64.Instruction.AInstEmitAluHelper; @@ -117,9 +118,18 @@ namespace ChocolArm64.Instruction Context.EmitLdintzr(Op.Rn); - Context.EmitLdc_I4(Op.RegisterSize == ARegisterSize.Int32 ? 32 : 64); + if (Lzcnt.IsSupported) + { + Type TValue = Op.RegisterSize == ARegisterSize.Int32 ? typeof(uint) : typeof(ulong); - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingZeros)); + Context.EmitCall(typeof(Lzcnt).GetMethod(nameof(Lzcnt.LeadingZeroCount), new Type[] { TValue })); + } + else + { + Context.EmitLdc_I4(Op.RegisterSize == ARegisterSize.Int32 ? 32 : 64); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingZeros)); + } Context.EmitStintzr(Op.Rd); } diff --git a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs index d11a0b8477..7ba08f5e22 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdArithmetic.cs @@ -82,20 +82,6 @@ namespace ChocolArm64.Instruction } public static void Cls_V(AILEmitterCtx Context) - { - MethodInfo MthdInfo = typeof(ASoftFallback).GetMethod(nameof(ASoftFallback.CountLeadingSigns)); - - EmitCountLeadingBits(Context, () => Context.EmitCall(MthdInfo)); - } - - public static void Clz_V(AILEmitterCtx Context) - { - MethodInfo MthdInfo = typeof(ASoftFallback).GetMethod(nameof(ASoftFallback.CountLeadingZeros)); - - EmitCountLeadingBits(Context, () => Context.EmitCall(MthdInfo)); - } - - private static void EmitCountLeadingBits(AILEmitterCtx Context, Action Emit) { AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; @@ -110,7 +96,44 @@ namespace ChocolArm64.Instruction Context.EmitLdc_I4(ESize); - Emit(); + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingSigns)); + + EmitVectorInsert(Context, Op.Rd, Index, Op.Size); + } + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + + public static void Clz_V(AILEmitterCtx Context) + { + AOpCodeSimd Op = (AOpCodeSimd)Context.CurrOp; + + int Bytes = Op.GetBitsCount() >> 3; + int Elems = Bytes >> Op.Size; + + int ESize = 8 << Op.Size; + + for (int Index = 0; Index < Elems; Index++) + { + EmitVectorExtractZx(Context, Op.Rn, Index, Op.Size); + + if (Lzcnt.IsSupported && ESize == 32) + { + Context.Emit(OpCodes.Conv_U4); + + Context.EmitCall(typeof(Lzcnt).GetMethod(nameof(Lzcnt.LeadingZeroCount), new Type[] { typeof(uint) })); + + Context.Emit(OpCodes.Conv_U8); + } + else + { + Context.EmitLdc_I4(ESize); + + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountLeadingZeros)); + } EmitVectorInsert(Context, Op.Rd, Index, Op.Size); } @@ -131,11 +154,14 @@ namespace ChocolArm64.Instruction { EmitVectorExtractZx(Context, Op.Rn, Index, 0); - Context.Emit(OpCodes.Conv_U4); - - ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountSetBits8)); - - Context.Emit(OpCodes.Conv_U8); + if (Popcnt.IsSupported) + { + Context.EmitCall(typeof(Popcnt).GetMethod(nameof(Popcnt.PopCount), new Type[] { typeof(ulong) })); + } + else + { + ASoftFallback.EmitCall(Context, nameof(ASoftFallback.CountSetBits8)); + } EmitVectorInsert(Context, Op.Rd, Index, 0); } @@ -440,6 +466,15 @@ namespace ChocolArm64.Instruction }); } + public static void Fmls_Se(AILEmitterCtx Context) + { + EmitScalarTernaryOpByElemF(Context, () => + { + Context.Emit(OpCodes.Mul); + Context.Emit(OpCodes.Sub); + }); + } + public static void Fmls_V(AILEmitterCtx Context) { EmitVectorTernaryOpF(Context, () => @@ -554,6 +589,14 @@ namespace ChocolArm64.Instruction }); } + public static void Fmulx_Se(AILEmitterCtx Context) + { + EmitScalarBinaryOpByElemF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMulX)); + }); + } + public static void Fmulx_V(AILEmitterCtx Context) { EmitVectorBinaryOpF(Context, () => @@ -562,6 +605,14 @@ namespace ChocolArm64.Instruction }); } + public static void Fmulx_Ve(AILEmitterCtx Context) + { + EmitVectorBinaryOpByElemF(Context, () => + { + EmitSoftFloatCall(Context, nameof(ASoftFloat_32.FPMulX)); + }); + } + public static void Fneg_S(AILEmitterCtx Context) { EmitScalarUnaryOpF(Context, () => Context.Emit(OpCodes.Neg)); diff --git a/ChocolArm64/Instruction/AInstEmitSimdMove.cs b/ChocolArm64/Instruction/AInstEmitSimdMove.cs index 94097f4806..6001f48caf 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdMove.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdMove.cs @@ -249,6 +249,17 @@ namespace ChocolArm64.Instruction EmitVectorImmUnaryOp(Context, () => Context.Emit(OpCodes.Not)); } + public static void Smov_S(AILEmitterCtx Context) + { + AOpCodeSimdIns Op = (AOpCodeSimdIns)Context.CurrOp; + + EmitVectorExtractSx(Context, Op.Rn, Op.DstIndex, Op.Size); + + EmitIntZeroUpperIfNeeded(Context); + + Context.EmitStintzr(Op.Rd); + } + public static void Tbl_V(AILEmitterCtx Context) { AOpCodeSimdTbl Op = (AOpCodeSimdTbl)Context.CurrOp; @@ -421,7 +432,8 @@ namespace ChocolArm64.Instruction private static void EmitIntZeroUpperIfNeeded(AILEmitterCtx Context) { - if (Context.CurrOp.RegisterSize == ARegisterSize.Int32) + if (Context.CurrOp.RegisterSize == ARegisterSize.Int32 || + Context.CurrOp.RegisterSize == ARegisterSize.SIMD64) { Context.Emit(OpCodes.Conv_U4); Context.Emit(OpCodes.Conv_U8); diff --git a/ChocolArm64/Instruction/AInstEmitSimdShift.cs b/ChocolArm64/Instruction/AInstEmitSimdShift.cs index 127abf1df8..8918c0e1ba 100644 --- a/ChocolArm64/Instruction/AInstEmitSimdShift.cs +++ b/ChocolArm64/Instruction/AInstEmitSimdShift.cs @@ -3,6 +3,7 @@ using ChocolArm64.State; using ChocolArm64.Translation; using System; using System.Reflection.Emit; +using System.Runtime.Intrinsics.X86; using static ChocolArm64.Instruction.AInstEmitSimdHelper; @@ -31,12 +32,32 @@ namespace ChocolArm64.Instruction { AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; - EmitVectorUnaryOpZx(Context, () => + if (AOptimizations.UseSse2 && Op.Size > 0) { + Type[] Types = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], typeof(byte) }; + + EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); + Context.EmitLdc_I4(GetImmShl(Op)); - Context.Emit(OpCodes.Shl); - }); + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftLeftLogical), Types)); + + EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + else + { + EmitVectorUnaryOpZx(Context, () => + { + Context.EmitLdc_I4(GetImmShl(Op)); + + Context.Emit(OpCodes.Shl); + }); + } } public static void Shll_V(AILEmitterCtx Context) @@ -167,7 +188,30 @@ namespace ChocolArm64.Instruction public static void Sshr_V(AILEmitterCtx Context) { - EmitShrImmOp(Context, ShrImmFlags.VectorSx); + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + if (AOptimizations.UseSse2 && Op.Size > 0 + && Op.Size < 3) + { + Type[] Types = new Type[] { VectorIntTypesPerSizeLog2[Op.Size], typeof(byte) }; + + EmitLdvecWithSignedCast(Context, Op.Rn, Op.Size); + + Context.EmitLdc_I4(GetImmShr(Op)); + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), Types)); + + EmitStvecWithSignedCast(Context, Op.Rd, Op.Size); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + else + { + EmitShrImmOp(Context, ShrImmFlags.VectorSx); + } } public static void Ssra_S(AILEmitterCtx Context) @@ -177,7 +221,33 @@ namespace ChocolArm64.Instruction public static void Ssra_V(AILEmitterCtx Context) { - EmitVectorShrImmOpSx(Context, ShrImmFlags.Accumulate); + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + if (AOptimizations.UseSse2 && Op.Size > 0 + && Op.Size < 3) + { + Type[] TypesSra = new Type[] { VectorIntTypesPerSizeLog2[Op.Size], typeof(byte) }; + Type[] TypesAdd = new Type[] { VectorIntTypesPerSizeLog2[Op.Size], VectorIntTypesPerSizeLog2[Op.Size] }; + + EmitLdvecWithSignedCast(Context, Op.Rd, Op.Size); + EmitLdvecWithSignedCast(Context, Op.Rn, Op.Size); + + Context.EmitLdc_I4(GetImmShr(Op)); + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightArithmetic), TypesSra)); + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), TypesAdd)); + + EmitStvecWithSignedCast(Context, Op.Rd, Op.Size); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + else + { + EmitVectorShrImmOpSx(Context, ShrImmFlags.Accumulate); + } } public static void Uqrshrn_S(AILEmitterCtx Context) @@ -239,7 +309,29 @@ namespace ChocolArm64.Instruction public static void Ushr_V(AILEmitterCtx Context) { - EmitShrImmOp(Context, ShrImmFlags.VectorZx); + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + if (AOptimizations.UseSse2 && Op.Size > 0) + { + Type[] Types = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], typeof(byte) }; + + EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); + + Context.EmitLdc_I4(GetImmShr(Op)); + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), Types)); + + EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + else + { + EmitShrImmOp(Context, ShrImmFlags.VectorZx); + } } public static void Usra_S(AILEmitterCtx Context) @@ -249,7 +341,32 @@ namespace ChocolArm64.Instruction public static void Usra_V(AILEmitterCtx Context) { - EmitVectorShrImmOpZx(Context, ShrImmFlags.Accumulate); + AOpCodeSimdShImm Op = (AOpCodeSimdShImm)Context.CurrOp; + + if (AOptimizations.UseSse2 && Op.Size > 0) + { + Type[] TypesSrl = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], typeof(byte) }; + Type[] TypesAdd = new Type[] { VectorUIntTypesPerSizeLog2[Op.Size], VectorUIntTypesPerSizeLog2[Op.Size] }; + + EmitLdvecWithUnsignedCast(Context, Op.Rd, Op.Size); + EmitLdvecWithUnsignedCast(Context, Op.Rn, Op.Size); + + Context.EmitLdc_I4(GetImmShr(Op)); + + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.ShiftRightLogical), TypesSrl)); + Context.EmitCall(typeof(Sse2).GetMethod(nameof(Sse2.Add), TypesAdd)); + + EmitStvecWithUnsignedCast(Context, Op.Rd, Op.Size); + + if (Op.RegisterSize == ARegisterSize.SIMD64) + { + EmitVectorZeroUpper(Context, Op.Rd); + } + } + else + { + EmitVectorShrImmOpZx(Context, ShrImmFlags.Accumulate); + } } private static void EmitVectorShl(AILEmitterCtx Context, bool Signed) diff --git a/ChocolArm64/Instruction/ASoftFallback.cs b/ChocolArm64/Instruction/ASoftFallback.cs index a7bc108591..3c5c5c4d9e 100644 --- a/ChocolArm64/Instruction/ASoftFallback.cs +++ b/ChocolArm64/Instruction/ASoftFallback.cs @@ -386,7 +386,7 @@ namespace ChocolArm64.Instruction #endregion #region "Count" - public static ulong CountLeadingSigns(ulong Value, int Size) + public static ulong CountLeadingSigns(ulong Value, int Size) // Size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.). { Value ^= Value >> 1; @@ -405,9 +405,9 @@ namespace ChocolArm64.Instruction private static readonly byte[] ClzNibbleTbl = { 4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }; - public static ulong CountLeadingZeros(ulong Value, int Size) + public static ulong CountLeadingZeros(ulong Value, int Size) // Size is 8, 16, 32 or 64 (SIMD&FP or Base Inst.). { - if (Value == 0) + if (Value == 0ul) { return (ulong)Size; } @@ -426,12 +426,17 @@ namespace ChocolArm64.Instruction return (ulong)Count; } - public static uint CountSetBits8(uint Value) + public static ulong CountSetBits8(ulong Value) // "Size" is 8 (SIMD&FP Inst.). { - Value = ((Value >> 1) & 0x55) + (Value & 0x55); - Value = ((Value >> 2) & 0x33) + (Value & 0x33); + if (Value == 0xfful) + { + return 8ul; + } - return (Value >> 4) + (Value & 0x0f); + Value = ((Value >> 1) & 0x55ul) + (Value & 0x55ul); + Value = ((Value >> 2) & 0x33ul) + (Value & 0x33ul); + + return (Value >> 4) + (Value & 0x0ful); } #endregion diff --git a/ChocolArm64/Instruction/ASoftFloat.cs b/ChocolArm64/Instruction/ASoftFloat.cs index 7412c976fd..2d9a9f0ebe 100644 --- a/ChocolArm64/Instruction/ASoftFloat.cs +++ b/ChocolArm64/Instruction/ASoftFloat.cs @@ -365,8 +365,8 @@ namespace ChocolArm64.Instruction { Debug.WriteIf(State.Fpcr != 0, "ASoftFloat_32.FPMaxNum: "); - Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); - Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); + Value1.FPUnpack(out FPType Type1, out _, out _); + Value2.FPUnpack(out FPType Type2, out _, out _); if (Type1 == FPType.QNaN && Type2 != FPType.QNaN) { @@ -430,8 +430,8 @@ namespace ChocolArm64.Instruction { Debug.WriteIf(State.Fpcr != 0, "ASoftFloat_32.FPMinNum: "); - Value1.FPUnpack(out FPType Type1, out bool Sign1, out uint Op1); - Value2.FPUnpack(out FPType Type2, out bool Sign2, out uint Op2); + Value1.FPUnpack(out FPType Type1, out _, out _); + Value2.FPUnpack(out FPType Type2, out _, out _); if (Type1 == FPType.QNaN && Type2 != FPType.QNaN) { @@ -1091,8 +1091,8 @@ namespace ChocolArm64.Instruction { Debug.WriteIf(State.Fpcr != 0, "ASoftFloat_64.FPMaxNum: "); - Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); - Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); + Value1.FPUnpack(out FPType Type1, out _, out _); + Value2.FPUnpack(out FPType Type2, out _, out _); if (Type1 == FPType.QNaN && Type2 != FPType.QNaN) { @@ -1156,8 +1156,8 @@ namespace ChocolArm64.Instruction { Debug.WriteIf(State.Fpcr != 0, "ASoftFloat_64.FPMinNum: "); - Value1.FPUnpack(out FPType Type1, out bool Sign1, out ulong Op1); - Value2.FPUnpack(out FPType Type2, out bool Sign2, out ulong Op2); + Value1.FPUnpack(out FPType Type1, out _, out _); + Value2.FPUnpack(out FPType Type2, out _, out _); if (Type1 == FPType.QNaN && Type2 != FPType.QNaN) { diff --git a/Ryujinx.Tests/Cpu/CpuTestSimd.cs b/Ryujinx.Tests/Cpu/CpuTestSimd.cs index 2075ccf2b0..279f9f0c3e 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimd.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimd.cs @@ -205,6 +205,22 @@ namespace Ryujinx.Tests.Cpu #endregion #region "ValueSource (Opcodes)" + private static uint[] _F_Cvt_S_SD_() + { + return new uint[] + { + 0x1E22C020u // FCVT D0, S1 + }; + } + + private static uint[] _F_Cvt_S_DS_() + { + return new uint[] + { + 0x1E624020u // FCVT S0, D1 + }; + } + private static uint[] _F_Cvt_NZ_SU_S_S_() { return new uint[] @@ -249,7 +265,7 @@ namespace Ryujinx.Tests.Cpu }; } - private static uint[] _F_RecpX_Sqrt_S_S_() + private static uint[] _F_Recpx_Sqrt_S_S_() { return new uint[] { @@ -258,7 +274,7 @@ namespace Ryujinx.Tests.Cpu }; } - private static uint[] _F_RecpX_Sqrt_S_D_() + private static uint[] _F_Recpx_Sqrt_S_D_() { return new uint[] { @@ -785,35 +801,33 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise, Description("FCVT
, ")] - public void Fcvt_S_SD([ValueSource("_1S_F_")] ulong A) + [Test, Pairwise] [Explicit] + public void F_Cvt_S_SD([ValueSource("_F_Cvt_S_SD_")] uint Opcodes, + [ValueSource("_1S_F_")] ulong A) { - uint Opcode = 0x1E22C020; // FCVT D0, S1 - ulong Z = TestContext.CurrentContext.Random.NextULong(); Vector128 V0 = MakeVectorE1(Z); Vector128 V1 = MakeVectorE0(A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1); CompareAgainstUnicorn(); } - [Test, Pairwise, Description("FCVT , ")] - public void Fcvt_S_DS([ValueSource("_1D_F_")] ulong A) + [Test, Pairwise] [Explicit] + public void F_Cvt_S_DS([ValueSource("_F_Cvt_S_DS_")] uint Opcodes, + [ValueSource("_1D_F_")] ulong A) { - uint Opcode = 0x1E624020; // FCVT S0, D1 - ulong Z = TestContext.CurrentContext.Random.NextULong(); Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0(A); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1); + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1); CompareAgainstUnicorn(); } - [Test, Pairwise] + [Test, Pairwise] [Explicit] public void F_Cvt_NZ_SU_S_S([ValueSource("_F_Cvt_NZ_SU_S_S_")] uint Opcodes, [ValueSource("_1S_F_")] ulong A) { @@ -826,7 +840,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] + [Test, Pairwise] [Explicit] public void F_Cvt_NZ_SU_S_D([ValueSource("_F_Cvt_NZ_SU_S_D_")] uint Opcodes, [ValueSource("_1D_F_")] ulong A) { @@ -839,7 +853,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] + [Test, Pairwise] [Explicit] public void F_Cvt_NZ_SU_V_2S_4S([ValueSource("_F_Cvt_NZ_SU_V_2S_4S_")] uint Opcodes, [Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, @@ -858,7 +872,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] + [Test, Pairwise] [Explicit] public void F_Cvt_NZ_SU_V_2D([ValueSource("_F_Cvt_NZ_SU_V_2D_")] uint Opcodes, [Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, @@ -875,8 +889,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] - public void F_RecpX_Sqrt_S_S([ValueSource("_F_RecpX_Sqrt_S_S_")] uint Opcodes, + [Test, Pairwise] [Explicit] + public void F_Recpx_Sqrt_S_S([ValueSource("_F_Recpx_Sqrt_S_S_")] uint Opcodes, [ValueSource("_1S_F_")] ulong A) { ulong Z = TestContext.CurrentContext.Random.NextULong(); @@ -890,8 +904,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(FpsrMask: FPSR.IOC); } - [Test, Pairwise] - public void F_RecpX_Sqrt_S_D([ValueSource("_F_RecpX_Sqrt_S_D_")] uint Opcodes, + [Test, Pairwise] [Explicit] + public void F_Recpx_Sqrt_S_D([ValueSource("_F_Recpx_Sqrt_S_D_")] uint Opcodes, [ValueSource("_1D_F_")] ulong A) { ulong Z = TestContext.CurrentContext.Random.NextULong(); @@ -905,7 +919,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(FpsrMask: FPSR.IOC); } - [Test, Pairwise] + [Test, Pairwise] [Explicit] public void F_Sqrt_V_2S_4S([ValueSource("_F_Sqrt_V_2S_4S_")] uint Opcodes, [Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, @@ -926,7 +940,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(FpsrMask: FPSR.IOC); } - [Test, Pairwise] + [Test, Pairwise] [Explicit] public void F_Sqrt_V_2D([ValueSource("_F_Sqrt_V_2D_")] uint Opcodes, [Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs b/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs index a5ae1a5f76..5afeab315c 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs @@ -9,18 +9,6 @@ namespace Ryujinx.Tests.Cpu { public class CpuTestSimdArithmetic : CpuTest { - [Test, Description("FMUL S6, S1, V0.S[2]")] - public void Fmul_Se([Random(10)] float A, [Random(10)] float B) - { - AThreadState ThreadState = SingleOpcode(0x5F809826, - V1: Sse.SetVector128(0, 0, 0, A), - V0: Sse.SetVector128(0, B, 0, 0)); - - Assert.That(Sse41.Extract(ThreadState.V6, (byte)0), Is.EqualTo(A * B)); - - CompareAgainstUnicorn(); - } - [TestCase(0x00000000u, 0x7F800000u)] [TestCase(0x80000000u, 0xFF800000u)] [TestCase(0x00FFF000u, 0x7E000000u)] @@ -86,7 +74,7 @@ namespace Ryujinx.Tests.Cpu Vector128 V1 = MakeVectorE0(A); int FpcrTemp = 0x0; - if(DefaultNaN) + if (DefaultNaN) { FpcrTemp = 0x2000000; } @@ -115,7 +103,7 @@ namespace Ryujinx.Tests.Cpu Vector128 V1 = MakeVectorE0E1(A, B); int FpcrTemp = 0x0; - if(DefaultNaN) + if (DefaultNaN) { FpcrTemp = 0x2000000; } @@ -185,7 +173,7 @@ namespace Ryujinx.Tests.Cpu case 'M': FpcrTemp = 0x800000; break; case 'Z': FpcrTemp = 0xC00000; break; } - if(DefaultNaN) + if (DefaultNaN) { FpcrTemp |= 1 << 25; } @@ -241,7 +229,7 @@ namespace Ryujinx.Tests.Cpu case 'M': FpcrTemp = 0x800000; break; case 'Z': FpcrTemp = 0xC00000; break; } - if(DefaultNaN) + if (DefaultNaN) { FpcrTemp |= 1 << 25; } @@ -302,7 +290,7 @@ namespace Ryujinx.Tests.Cpu Vector128 V1 = MakeVectorE0(A); int FpcrTemp = 0x0; - if(DefaultNaN) + if (DefaultNaN) { FpcrTemp = 0x2000000; } @@ -327,7 +315,7 @@ namespace Ryujinx.Tests.Cpu Vector128 V1 = MakeVectorE0E1(A, B); int FpcrTemp = 0x0; - if(DefaultNaN) + if (DefaultNaN) { FpcrTemp = 0x2000000; } @@ -389,7 +377,7 @@ namespace Ryujinx.Tests.Cpu Vector128 V1 = MakeVectorE0(A); int FpcrTemp = 0x0; - if(DefaultNaN) + if (DefaultNaN) { FpcrTemp = 0x2000000; } @@ -417,7 +405,7 @@ namespace Ryujinx.Tests.Cpu Vector128 V1 = MakeVectorE0E1(A, B); int FpcrTemp = 0x0; - if(DefaultNaN) + if (DefaultNaN) { FpcrTemp = 0x2000000; } @@ -478,7 +466,7 @@ namespace Ryujinx.Tests.Cpu Vector128 V1 = MakeVectorE0(A); int FpcrTemp = 0x0; - if(DefaultNaN) + if (DefaultNaN) { FpcrTemp = 0x2000000; } @@ -503,7 +491,7 @@ namespace Ryujinx.Tests.Cpu Vector128 V1 = MakeVectorE0E1(A, B); int FpcrTemp = 0x0; - if(DefaultNaN) + if (DefaultNaN) { FpcrTemp = 0x2000000; } @@ -573,7 +561,7 @@ namespace Ryujinx.Tests.Cpu case 'M': FpcrTemp = 0x800000; break; case 'Z': FpcrTemp = 0xC00000; break; } - if(DefaultNaN) + if (DefaultNaN) { FpcrTemp |= 1 << 25; } @@ -629,7 +617,7 @@ namespace Ryujinx.Tests.Cpu case 'M': FpcrTemp = 0x800000; break; case 'Z': FpcrTemp = 0xC00000; break; } - if(DefaultNaN) + if (DefaultNaN) { FpcrTemp |= 1 << 25; } diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs b/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs index 387cdf5dd9..0b227edbb6 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdIns.cs @@ -14,6 +14,27 @@ namespace Ryujinx.Tests.Cpu #if SimdIns #region "ValueSource" + private static ulong[] _1D_() + { + return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFFFFFFFFFFul, + 0x8000000000000000ul, 0xFFFFFFFFFFFFFFFFul }; + } + + private static ulong[] _8B4H_() + { + return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0xFFFFFFFFFFFFFFFFul }; + } + + private static ulong[] _8B4H2S_() + { + return new ulong[] { 0x0000000000000000ul, 0x7F7F7F7F7F7F7F7Ful, + 0x8080808080808080ul, 0x7FFF7FFF7FFF7FFFul, + 0x8000800080008000ul, 0x7FFFFFFF7FFFFFFFul, + 0x8000000080000000ul, 0xFFFFFFFFFFFFFFFFul }; + } + private static uint[] _W_() { return new uint[] { 0x00000000u, 0x0000007Fu, @@ -39,7 +60,7 @@ namespace Ryujinx.Tests.Cpu [Values(0, 1, 2)] int Size, // Q0: <8B, 4H, 2S> [Values(0b0u, 0b1u)] uint Q) // Q1: <16B, 8H, 4S> { - uint Imm5 = (1U << Size) & 0x1F; + uint Imm5 = (1u << Size) & 0x1Fu; uint Opcode = 0x0E000C00; // RESERVED Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); @@ -69,6 +90,92 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } + + [Test, Pairwise, Description("SMOV , .[]")] + public void Smov_S_W([Values(0u, 31u)] uint Rd, + [Values(1u)] uint Rn, + [ValueSource("_8B4H_")] [Random(RndCnt)] ulong A, + [Values(0, 1)] int Size, // + [Values(0u, 1u, 2u, 3u)] uint Index) + { + uint Imm5 = (Index << (Size + 1) | 1u << Size) & 0x1Fu; + + uint Opcode = 0x0E002C00; // RESERVED + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= (Imm5 << 16); + + ulong _X0 = (ulong)TestContext.CurrentContext.Random.NextUInt() << 32; + uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + Vector128 V1 = MakeVectorE0(A); + + AThreadState ThreadState = SingleOpcode(Opcode, X0: _X0, X31: _W31, V1: V1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("SMOV , .[]")] + public void Smov_S_X([Values(0u, 31u)] uint Rd, + [Values(1u)] uint Rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [Values(0, 1, 2)] int Size, // + [Values(0u, 1u)] uint Index) + { + uint Imm5 = (Index << (Size + 1) | 1u << Size) & 0x1Fu; + + uint Opcode = 0x4E002C00; // RESERVED + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= (Imm5 << 16); + + ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + Vector128 V1 = MakeVectorE0(A); + + AThreadState ThreadState = SingleOpcode(Opcode, X31: _X31, V1: V1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("UMOV , .[]")] + public void Umov_S_W([Values(0u, 31u)] uint Rd, + [Values(1u)] uint Rn, + [ValueSource("_8B4H2S_")] [Random(RndCnt)] ulong A, + [Values(0, 1, 2)] int Size, // + [Values(0u, 1u)] uint Index) + { + uint Imm5 = (Index << (Size + 1) | 1u << Size) & 0x1Fu; + + uint Opcode = 0x0E003C00; // RESERVED + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= (Imm5 << 16); + + ulong _X0 = (ulong)TestContext.CurrentContext.Random.NextUInt() << 32; + uint _W31 = TestContext.CurrentContext.Random.NextUInt(); + Vector128 V1 = MakeVectorE0(A); + + AThreadState ThreadState = SingleOpcode(Opcode, X0: _X0, X31: _W31, V1: V1); + + CompareAgainstUnicorn(); + } + + [Test, Pairwise, Description("UMOV , .[]")] + public void Umov_S_X([Values(0u, 31u)] uint Rd, + [Values(1u)] uint Rn, + [ValueSource("_1D_")] [Random(RndCnt)] ulong A, + [Values(3)] int Size, // + [Values(0u)] uint Index) + { + uint Imm5 = (Index << (Size + 1) | 1u << Size) & 0x1Fu; + + uint Opcode = 0x4E003C00; // RESERVED + Opcode |= ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcode |= (Imm5 << 16); + + ulong _X31 = TestContext.CurrentContext.Random.NextULong(); + Vector128 V1 = MakeVectorE0(A); + + AThreadState ThreadState = SingleOpcode(Opcode, X31: _X31, V1: V1); + + CompareAgainstUnicorn(); + } #endif } } diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs index 7d47416f0c..1ea017c807 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdReg.cs @@ -204,7 +204,7 @@ namespace Ryujinx.Tests.Cpu #endregion #region "ValueSource (Opcodes)" - private static uint[] _F_Add_Div_Mul_MulX_Sub_S_S_() + private static uint[] _F_Add_Div_Mul_Mulx_Sub_S_S_() { return new uint[] { @@ -216,7 +216,7 @@ namespace Ryujinx.Tests.Cpu }; } - private static uint[] _F_Add_Div_Mul_MulX_Sub_S_D_() + private static uint[] _F_Add_Div_Mul_Mulx_Sub_S_D_() { return new uint[] { @@ -228,7 +228,7 @@ namespace Ryujinx.Tests.Cpu }; } - private static uint[] _F_Add_Div_Mul_MulX_Sub_V_2S_4S_() + private static uint[] _F_Add_Div_Mul_Mulx_Sub_V_2S_4S_() { return new uint[] { @@ -240,7 +240,7 @@ namespace Ryujinx.Tests.Cpu }; } - private static uint[] _F_Add_Div_Mul_MulX_Sub_V_2D_() + private static uint[] _F_Add_Div_Mul_Mulx_Sub_V_2D_() { return new uint[] { @@ -252,7 +252,7 @@ namespace Ryujinx.Tests.Cpu }; } - private static uint[] _Fmadd_Fmsub_S_S_() + private static uint[] _F_Madd_Msub_S_S_() { return new uint[] { @@ -261,7 +261,7 @@ namespace Ryujinx.Tests.Cpu }; } - private static uint[] _Fmadd_Fmsub_S_D_() + private static uint[] _F_Madd_Msub_S_D_() { return new uint[] { @@ -318,7 +318,7 @@ namespace Ryujinx.Tests.Cpu }; } - private static uint[] _Frecps_Frsqrts_S_S_() + private static uint[] _F_Recps_Rsqrts_S_S_() { return new uint[] { @@ -327,7 +327,7 @@ namespace Ryujinx.Tests.Cpu }; } - private static uint[] _Frecps_Frsqrts_S_D_() + private static uint[] _F_Recps_Rsqrts_S_D_() { return new uint[] { @@ -336,7 +336,7 @@ namespace Ryujinx.Tests.Cpu }; } - private static uint[] _Frecps_Frsqrts_V_2S_4S_() + private static uint[] _F_Recps_Rsqrts_V_2S_4S_() { return new uint[] { @@ -345,7 +345,7 @@ namespace Ryujinx.Tests.Cpu }; } - private static uint[] _Frecps_Frsqrts_V_2D_() + private static uint[] _F_Recps_Rsqrts_V_2D_() { return new uint[] { @@ -1137,8 +1137,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(); } - [Test, Pairwise] - public void F_Add_Div_Mul_MulX_Sub_S_S([ValueSource("_F_Add_Div_Mul_MulX_Sub_S_S_")] uint Opcodes, + [Test, Pairwise] [Explicit] + public void F_Add_Div_Mul_Mulx_Sub_S_S([ValueSource("_F_Add_Div_Mul_Mulx_Sub_S_S_")] uint Opcodes, [ValueSource("_1S_F_")] ulong A, [ValueSource("_1S_F_")] ulong B) { @@ -1154,8 +1154,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(FpsrMask: FPSR.IOC | FPSR.DZC); } - [Test, Pairwise] - public void F_Add_Div_Mul_MulX_Sub_S_D([ValueSource("_F_Add_Div_Mul_MulX_Sub_S_D_")] uint Opcodes, + [Test, Pairwise] [Explicit] + public void F_Add_Div_Mul_Mulx_Sub_S_D([ValueSource("_F_Add_Div_Mul_Mulx_Sub_S_D_")] uint Opcodes, [ValueSource("_1D_F_")] ulong A, [ValueSource("_1D_F_")] ulong B) { @@ -1171,8 +1171,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(FpsrMask: FPSR.IOC | FPSR.DZC); } - [Test, Pairwise] - public void F_Add_Div_Mul_MulX_Sub_V_2S_4S([ValueSource("_F_Add_Div_Mul_MulX_Sub_V_2S_4S_")] uint Opcodes, + [Test, Pairwise] [Explicit] + public void F_Add_Div_Mul_Mulx_Sub_V_2S_4S([ValueSource("_F_Add_Div_Mul_Mulx_Sub_V_2S_4S_")] uint Opcodes, [Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, @@ -1195,8 +1195,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(FpsrMask: FPSR.IOC | FPSR.DZC); } - [Test, Pairwise] - public void F_Add_Div_Mul_MulX_Sub_V_2D([ValueSource("_F_Add_Div_Mul_MulX_Sub_V_2D_")] uint Opcodes, + [Test, Pairwise] [Explicit] + public void F_Add_Div_Mul_Mulx_Sub_V_2D([ValueSource("_F_Add_Div_Mul_Mulx_Sub_V_2D_")] uint Opcodes, [Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, @@ -1217,8 +1217,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(FpsrMask: FPSR.IOC | FPSR.DZC); } - [Test, Pairwise] // Fused. - public void Fmadd_Fmsub_S_S([ValueSource("_Fmadd_Fmsub_S_S_")] uint Opcodes, + [Test, Pairwise] [Explicit] // Fused. + public void F_Madd_Msub_S_S([ValueSource("_F_Madd_Msub_S_S_")] uint Opcodes, [ValueSource("_1S_F_")] ulong A, [ValueSource("_1S_F_")] ulong B, [ValueSource("_1S_F_")] ulong C) @@ -1236,8 +1236,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_S); } - [Test, Pairwise] // Fused. - public void Fmadd_Fmsub_S_D([ValueSource("_Fmadd_Fmsub_S_D_")] uint Opcodes, + [Test, Pairwise] [Explicit] // Fused. + public void F_Madd_Msub_S_D([ValueSource("_F_Madd_Msub_S_D_")] uint Opcodes, [ValueSource("_1D_F_")] ulong A, [ValueSource("_1D_F_")] ulong B, [ValueSource("_1D_F_")] ulong C) @@ -1255,7 +1255,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_D); } - [Test, Pairwise] + [Test, Pairwise] [Explicit] public void F_Max_Min_Nm_S_S([ValueSource("_F_Max_Min_Nm_S_S_")] uint Opcodes, [ValueSource("_1S_F_")] ulong A, [ValueSource("_1S_F_")] ulong B) @@ -1272,7 +1272,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(FpsrMask: FPSR.IOC); } - [Test, Pairwise] + [Test, Pairwise] [Explicit] public void F_Max_Min_Nm_S_D([ValueSource("_F_Max_Min_Nm_S_D_")] uint Opcodes, [ValueSource("_1D_F_")] ulong A, [ValueSource("_1D_F_")] ulong B) @@ -1289,7 +1289,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(FpsrMask: FPSR.IOC); } - [Test, Pairwise] + [Test, Pairwise] [Explicit] public void F_Max_Min_Nm_P_V_2S_4S([ValueSource("_F_Max_Min_Nm_P_V_2S_4S_")] uint Opcodes, [Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, @@ -1313,7 +1313,7 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(FpsrMask: FPSR.IOC); } - [Test, Pairwise] + [Test, Pairwise] [Explicit] public void F_Max_Min_Nm_P_V_2D([ValueSource("_F_Max_Min_Nm_P_V_2D_")] uint Opcodes, [Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, @@ -1335,8 +1335,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(FpsrMask: FPSR.IOC); } - [Test, Pairwise] // Fused. - public void Frecps_Frsqrts_S_S([ValueSource("_Frecps_Frsqrts_S_S_")] uint Opcodes, + [Test, Pairwise] [Explicit] // Fused. + public void F_Recps_Rsqrts_S_S([ValueSource("_F_Recps_Rsqrts_S_S_")] uint Opcodes, [ValueSource("_1S_F_")] ulong A, [ValueSource("_1S_F_")] ulong B) { @@ -1352,8 +1352,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_S); } - [Test, Pairwise] // Fused. - public void Frecps_Frsqrts_S_D([ValueSource("_Frecps_Frsqrts_S_D_")] uint Opcodes, + [Test, Pairwise] [Explicit] // Fused. + public void F_Recps_Rsqrts_S_D([ValueSource("_F_Recps_Rsqrts_S_D_")] uint Opcodes, [ValueSource("_1D_F_")] ulong A, [ValueSource("_1D_F_")] ulong B) { @@ -1369,8 +1369,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_D); } - [Test, Pairwise] // Fused. - public void Frecps_Frsqrts_V_2S_4S([ValueSource("_Frecps_Frsqrts_V_2S_4S_")] uint Opcodes, + [Test, Pairwise] [Explicit] // Fused. + public void F_Recps_Rsqrts_V_2S_4S([ValueSource("_F_Recps_Rsqrts_V_2S_4S_")] uint Opcodes, [Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, @@ -1393,8 +1393,8 @@ namespace Ryujinx.Tests.Cpu CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_S); } - [Test, Pairwise] // Fused. - public void Frecps_Frsqrts_V_2D([ValueSource("_Frecps_Frsqrts_V_2D_")] uint Opcodes, + [Test, Pairwise] [Explicit] // Fused. + public void F_Recps_Rsqrts_V_2D([ValueSource("_F_Recps_Rsqrts_V_2D_")] uint Opcodes, [Values(0u)] uint Rd, [Values(1u, 0u)] uint Rn, [Values(2u, 0u)] uint Rm, diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs b/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs index 4d14ab4859..615520625a 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdRegElem.cs @@ -13,7 +13,7 @@ namespace Ryujinx.Tests.Cpu { #if SimdRegElem -#region "ValueSource" +#region "ValueSource (Types)" private static ulong[] _2S_() { return new ulong[] { 0x0000000000000000ul, 0x7FFFFFFF7FFFFFFFul, @@ -27,114 +27,81 @@ namespace Ryujinx.Tests.Cpu } #endregion +#region "ValueSource (Opcodes)" + private static uint[] _Mla_Mls_Mul_Ve_4H_8H_() + { + return new uint[] + { + 0x2F400000u, // MLA V0.4H, V0.4H, V0.H[0] + 0x2F404000u, // MLS V0.4H, V0.4H, V0.H[0] + 0x0F408000u // MUL V0.4H, V0.4H, V0.H[0] + }; + } + + private static uint[] _Mla_Mls_Mul_Ve_2S_4S_() + { + return new uint[] + { + 0x2F800000u, // MLA V0.2S, V0.2S, V0.S[0] + 0x2F804000u, // MLS V0.2S, V0.2S, V0.S[0] + 0x0F808000u // MUL V0.2S, V0.2S, V0.S[0] + }; + } +#endregion + private const int RndCnt = 2; - [Test, Pairwise, Description("MLA ., ., .[]")] - public void Mla_Ve_4H_8H([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_4H_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H_")] [Random(RndCnt)] ulong A, - [ValueSource("_4H_")] [Random(RndCnt)] ulong B, - [Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint Index, - [Values(0b0u, 0b1u)] uint Q) // <4H, 8H> + [Test, Pairwise] + public void Mla_Mls_Mul_Ve_4H_8H([ValueSource("_Mla_Mls_Mul_Ve_4H_8H_")] uint Opcodes, + [Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_4H_")] [Random(RndCnt)] ulong Z, + [ValueSource("_4H_")] [Random(RndCnt)] ulong A, + [ValueSource("_4H_")] [Random(RndCnt)] ulong B, + [Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint Index, + [Values(0b0u, 0b1u)] uint Q) // <4H, 8H> { - uint H = (Index & 4) >> 2; - uint L = (Index & 2) >> 1; - uint M = (Index & 1) >> 0; + uint H = (Index >> 2) & 1; + uint L = (Index >> 1) & 1; + uint M = Index & 1; - uint Opcode = 0x2F400000; // MLA V0.4H, V0.4H, V0.H[0] - Opcode |= ((Rm & 15) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= (L << 21) | (M << 20) | (H << 11); - Opcode |= ((Q & 1) << 30); + Opcodes |= ((Rm & 15) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcodes |= (L << 21) | (M << 20) | (H << 11); + Opcodes |= ((Q & 1) << 30); Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0E1(A, A * Q); Vector128 V2 = MakeVectorE0E1(B, B * H); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2); CompareAgainstUnicorn(); } - [Test, Pairwise, Description("MLA ., ., .[]")] - public void Mla_Ve_2S_4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_2S_")] [Random(RndCnt)] ulong B, - [Values(0u, 1u, 2u, 3u)] uint Index, - [Values(0b0u, 0b1u)] uint Q) // <2S, 4S> + [Test, Pairwise] + public void Mla_Mls_Mul_Ve_2S_4S([ValueSource("_Mla_Mls_Mul_Ve_2S_4S_")] uint Opcodes, + [Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_2S_")] [Random(RndCnt)] ulong Z, + [ValueSource("_2S_")] [Random(RndCnt)] ulong A, + [ValueSource("_2S_")] [Random(RndCnt)] ulong B, + [Values(0u, 1u, 2u, 3u)] uint Index, + [Values(0b0u, 0b1u)] uint Q) // <2S, 4S> { - uint H = (Index & 2) >> 1; - uint L = (Index & 1) >> 0; + uint H = (Index >> 1) & 1; + uint L = Index & 1; - uint Opcode = 0x2F800000; // MLA V0.2S, V0.2S, V0.S[0] - Opcode |= ((Rm & 15) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= (L << 21) | (H << 11); - Opcode |= ((Q & 1) << 30); + Opcodes |= ((Rm & 15) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcodes |= (L << 21) | (H << 11); + Opcodes |= ((Q & 1) << 30); Vector128 V0 = MakeVectorE0E1(Z, Z); Vector128 V1 = MakeVectorE0E1(A, A * Q); Vector128 V2 = MakeVectorE0E1(B, B * H); - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - CompareAgainstUnicorn(); - } - - [Test, Pairwise, Description("MLS ., ., .[]")] - public void Mls_Ve_4H_8H([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_4H_")] [Random(RndCnt)] ulong Z, - [ValueSource("_4H_")] [Random(RndCnt)] ulong A, - [ValueSource("_4H_")] [Random(RndCnt)] ulong B, - [Values(0u, 1u, 2u, 3u, 4u, 5u, 6u, 7u)] uint Index, - [Values(0b0u, 0b1u)] uint Q) // <4H, 8H> - { - uint H = (Index & 4) >> 2; - uint L = (Index & 2) >> 1; - uint M = (Index & 1) >> 0; - - uint Opcode = 0x2F404000; // MLS V0.4H, V0.4H, V0.H[0] - Opcode |= ((Rm & 15) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= (L << 21) | (M << 20) | (H << 11); - Opcode |= ((Q & 1) << 30); - - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A * Q); - Vector128 V2 = MakeVectorE0E1(B, B * H); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); - - CompareAgainstUnicorn(); - } - - [Test, Pairwise, Description("MLS ., ., .[]")] - public void Mls_Ve_2S_4S([Values(0u)] uint Rd, - [Values(1u, 0u)] uint Rn, - [Values(2u, 0u)] uint Rm, - [ValueSource("_2S_")] [Random(RndCnt)] ulong Z, - [ValueSource("_2S_")] [Random(RndCnt)] ulong A, - [ValueSource("_2S_")] [Random(RndCnt)] ulong B, - [Values(0u, 1u, 2u, 3u)] uint Index, - [Values(0b0u, 0b1u)] uint Q) // <2S, 4S> - { - uint H = (Index & 2) >> 1; - uint L = (Index & 1) >> 0; - - uint Opcode = 0x2F804000; // MLS V0.2S, V0.2S, V0.S[0] - Opcode |= ((Rm & 15) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); - Opcode |= (L << 21) | (H << 11); - Opcode |= ((Q & 1) << 30); - - Vector128 V0 = MakeVectorE0E1(Z, Z); - Vector128 V1 = MakeVectorE0E1(A, A * Q); - Vector128 V2 = MakeVectorE0E1(B, B * H); - - AThreadState ThreadState = SingleOpcode(Opcode, V0: V0, V1: V1, V2: V2); + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2); CompareAgainstUnicorn(); } diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs b/Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs new file mode 100644 index 0000000000..3945cce1a7 --- /dev/null +++ b/Ryujinx.Tests/Cpu/CpuTestSimdRegElemF.cs @@ -0,0 +1,424 @@ +#define SimdRegElemF + +using ChocolArm64.State; + +using NUnit.Framework; + +using System.Collections.Generic; +using System.Runtime.Intrinsics; + +namespace Ryujinx.Tests.Cpu +{ + [Category("SimdRegElemF")] // Tested: second half of 2018. + public sealed class CpuTestSimdRegElemF : CpuTest + { +#if SimdRegElemF + +#region "ValueSource (Types)" + private static IEnumerable _1S_F_() + { + yield return 0x00000000FF7FFFFFul; // -Max Normal (float.MinValue) + yield return 0x0000000080800000ul; // -Min Normal + yield return 0x00000000807FFFFFul; // -Max Subnormal + yield return 0x0000000080000001ul; // -Min Subnormal (-float.Epsilon) + yield return 0x000000007F7FFFFFul; // +Max Normal (float.MaxValue) + yield return 0x0000000000800000ul; // +Min Normal + yield return 0x00000000007FFFFFul; // +Max Subnormal + yield return 0x0000000000000001ul; // +Min Subnormal (float.Epsilon) + + if (!NoZeros) + { + yield return 0x0000000080000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0x00000000FF800000ul; // -Infinity + yield return 0x000000007F800000ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0x00000000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) + yield return 0x00000000FFBFFFFFul; // -SNaN (all ones payload) + yield return 0x000000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN) + yield return 0x000000007FBFFFFFul; // +SNaN (all ones payload) + } + + for (int Cnt = 1; Cnt <= RndCnt; Cnt++) + { + ulong Grbg = TestContext.CurrentContext.Random.NextUInt(); + ulong Rnd1 = GenNormal_S(); + ulong Rnd2 = GenSubnormal_S(); + + yield return (Grbg << 32) | Rnd1; + yield return (Grbg << 32) | Rnd2; + } + } + + private static IEnumerable _2S_F_() + { + yield return 0xFF7FFFFFFF7FFFFFul; // -Max Normal (float.MinValue) + yield return 0x8080000080800000ul; // -Min Normal + yield return 0x807FFFFF807FFFFFul; // -Max Subnormal + yield return 0x8000000180000001ul; // -Min Subnormal (-float.Epsilon) + yield return 0x7F7FFFFF7F7FFFFFul; // +Max Normal (float.MaxValue) + yield return 0x0080000000800000ul; // +Min Normal + yield return 0x007FFFFF007FFFFFul; // +Max Subnormal + yield return 0x0000000100000001ul; // +Min Subnormal (float.Epsilon) + + if (!NoZeros) + { + yield return 0x8000000080000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0xFF800000FF800000ul; // -Infinity + yield return 0x7F8000007F800000ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0xFFC00000FFC00000ul; // -QNaN (all zeros payload) (float.NaN) + yield return 0xFFBFFFFFFFBFFFFFul; // -SNaN (all ones payload) + yield return 0x7FC000007FC00000ul; // +QNaN (all zeros payload) (-float.NaN) (DefaultNaN) + yield return 0x7FBFFFFF7FBFFFFFul; // +SNaN (all ones payload) + } + + for (int Cnt = 1; Cnt <= RndCnt; Cnt++) + { + ulong Rnd1 = GenNormal_S(); + ulong Rnd2 = GenSubnormal_S(); + + yield return (Rnd1 << 32) | Rnd1; + yield return (Rnd2 << 32) | Rnd2; + } + } + + private static IEnumerable _1D_F_() + { + yield return 0xFFEFFFFFFFFFFFFFul; // -Max Normal (double.MinValue) + yield return 0x8010000000000000ul; // -Min Normal + yield return 0x800FFFFFFFFFFFFFul; // -Max Subnormal + yield return 0x8000000000000001ul; // -Min Subnormal (-double.Epsilon) + yield return 0x7FEFFFFFFFFFFFFFul; // +Max Normal (double.MaxValue) + yield return 0x0010000000000000ul; // +Min Normal + yield return 0x000FFFFFFFFFFFFFul; // +Max Subnormal + yield return 0x0000000000000001ul; // +Min Subnormal (double.Epsilon) + + if (!NoZeros) + { + yield return 0x8000000000000000ul; // -Zero + yield return 0x0000000000000000ul; // +Zero + } + + if (!NoInfs) + { + yield return 0xFFF0000000000000ul; // -Infinity + yield return 0x7FF0000000000000ul; // +Infinity + } + + if (!NoNaNs) + { + yield return 0xFFF8000000000000ul; // -QNaN (all zeros payload) (double.NaN) + yield return 0xFFF7FFFFFFFFFFFFul; // -SNaN (all ones payload) + yield return 0x7FF8000000000000ul; // +QNaN (all zeros payload) (-double.NaN) (DefaultNaN) + yield return 0x7FF7FFFFFFFFFFFFul; // +SNaN (all ones payload) + } + + for (int Cnt = 1; Cnt <= RndCnt; Cnt++) + { + ulong Rnd1 = GenNormal_D(); + ulong Rnd2 = GenSubnormal_D(); + + yield return Rnd1; + yield return Rnd2; + } + } +#endregion + +#region "ValueSource (Opcodes)" + private static uint[] _F_Mla_Mls_Se_S_() + { + return new uint[] + { + 0x5F821020u, // FMLA S0, S1, V2.S[0] + 0x5F825020u // FMLS S0, S1, V2.S[0] + }; + } + + private static uint[] _F_Mla_Mls_Se_D_() + { + return new uint[] + { + 0x5FC21020u, // FMLA D0, D1, V2.D[0] + 0x5FC25020u // FMLS D0, D1, V2.D[0] + }; + } + + private static uint[] _F_Mla_Mls_Ve_2S_4S_() + { + return new uint[] + { + 0x0F801000u, // FMLA V0.2S, V0.2S, V0.S[0] + 0x0F805000u // FMLS V0.2S, V0.2S, V0.S[0] + }; + } + + private static uint[] _F_Mla_Mls_Ve_2D_() + { + return new uint[] + { + 0x4FC01000u, // FMLA V0.2D, V0.2D, V0.D[0] + 0x4FC05000u // FMLS V0.2D, V0.2D, V0.D[0] + }; + } + + private static uint[] _F_Mul_Mulx_Se_S_() + { + return new uint[] + { + 0x5F829020u, // FMUL S0, S1, V2.S[0] + 0x7F829020u // FMULX S0, S1, V2.S[0] + }; + } + + private static uint[] _F_Mul_Mulx_Se_D_() + { + return new uint[] + { + 0x5FC29020u, // FMUL D0, D1, V2.D[0] + 0x7FC29020u // FMULX D0, D1, V2.D[0] + }; + } + + private static uint[] _F_Mul_Mulx_Ve_2S_4S_() + { + return new uint[] + { + 0x0F809000u, // FMUL V0.2S, V0.2S, V0.S[0] + 0x2F809000u // FMULX V0.2S, V0.2S, V0.S[0] + }; + } + + private static uint[] _F_Mul_Mulx_Ve_2D_() + { + return new uint[] + { + 0x4FC09000u, // FMUL V0.2D, V0.2D, V0.D[0] + 0x6FC09000u // FMULX V0.2D, V0.2D, V0.D[0] + }; + } +#endregion + + private const int RndCnt = 2; + + private static readonly bool NoZeros = false; + private static readonly bool NoInfs = false; + private static readonly bool NoNaNs = false; + + [Test, Pairwise] [Explicit] // Fused. + public void F_Mla_Mls_Se_S([ValueSource("_F_Mla_Mls_Se_S_")] uint Opcodes, + [ValueSource("_1S_F_")] ulong Z, + [ValueSource("_1S_F_")] ulong A, + [ValueSource("_2S_F_")] ulong B, + [Values(0u, 1u, 2u, 3u)] uint Index) + { + uint H = (Index >> 1) & 1; + uint L = Index & 1; + + Opcodes |= (L << 21) | (H << 11); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0E1(B, B * H); + + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_S); + } + + [Test, Pairwise] [Explicit] // Fused. + public void F_Mla_Mls_Se_D([ValueSource("_F_Mla_Mls_Se_D_")] uint Opcodes, + [ValueSource("_1D_F_")] ulong Z, + [ValueSource("_1D_F_")] ulong A, + [ValueSource("_1D_F_")] ulong B, + [Values(0u, 1u)] uint Index) + { + uint H = Index & 1; + + Opcodes |= H << 11; + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0E1(B, B * H); + + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_D); + } + + [Test, Pairwise] [Explicit] // Fused. + public void F_Mla_Mls_Ve_2S_4S([ValueSource("_F_Mla_Mls_Ve_2S_4S_")] uint Opcodes, + [Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_2S_F_")] ulong Z, + [ValueSource("_2S_F_")] ulong A, + [ValueSource("_2S_F_")] ulong B, + [Values(0u, 1u, 2u, 3u)] uint Index, + [Values(0b0u, 0b1u)] uint Q) // <2S, 4S> + { + uint H = (Index >> 1) & 1; + uint L = Index & 1; + + Opcodes |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcodes |= (L << 21) | (H << 11); + Opcodes |= ((Q & 1) << 30); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A * Q); + Vector128 V2 = MakeVectorE0E1(B, B * H); + + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_S); + } + + [Test, Pairwise] [Explicit] // Fused. + public void F_Mla_Mls_Ve_2D([ValueSource("_F_Mla_Mls_Ve_2D_")] uint Opcodes, + [Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_1D_F_")] ulong Z, + [ValueSource("_1D_F_")] ulong A, + [ValueSource("_1D_F_")] ulong B, + [Values(0u, 1u)] uint Index) + { + uint H = Index & 1; + + Opcodes |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcodes |= H << 11; + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B * H); + + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + CompareAgainstUnicorn(FPSR.IOC, FpSkips.IfUnderflow, FpTolerances.UpToOneUlps_D); + } + + [Test, Pairwise] [Explicit] + public void F_Mul_Mulx_Se_S([ValueSource("_F_Mul_Mulx_Se_S_")] uint Opcodes, + [ValueSource("_1S_F_")] ulong A, + [ValueSource("_2S_F_")] ulong B, + [Values(0u, 1u, 2u, 3u)] uint Index) + { + uint H = (Index >> 1) & 1; + uint L = Index & 1; + + Opcodes |= (L << 21) | (H << 11); + + ulong Z = TestContext.CurrentContext.Random.NextULong(); + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0E1(B, B * H); + + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + CompareAgainstUnicorn(FpsrMask: FPSR.IOC); + } + + [Test, Pairwise] [Explicit] + public void F_Mul_Mulx_Se_D([ValueSource("_F_Mul_Mulx_Se_D_")] uint Opcodes, + [ValueSource("_1D_F_")] ulong A, + [ValueSource("_1D_F_")] ulong B, + [Values(0u, 1u)] uint Index) + { + uint H = Index & 1; + + Opcodes |= H << 11; + + ulong Z = TestContext.CurrentContext.Random.NextULong(); + Vector128 V0 = MakeVectorE1(Z); + Vector128 V1 = MakeVectorE0(A); + Vector128 V2 = MakeVectorE0E1(B, B * H); + + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + CompareAgainstUnicorn(FpsrMask: FPSR.IOC); + } + + [Test, Pairwise] [Explicit] + public void F_Mul_Mulx_Ve_2S_4S([ValueSource("_F_Mul_Mulx_Ve_2S_4S_")] uint Opcodes, + [Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_2S_F_")] ulong Z, + [ValueSource("_2S_F_")] ulong A, + [ValueSource("_2S_F_")] ulong B, + [Values(0u, 1u, 2u, 3u)] uint Index, + [Values(0b0u, 0b1u)] uint Q) // <2S, 4S> + { + uint H = (Index >> 1) & 1; + uint L = Index & 1; + + Opcodes |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcodes |= (L << 21) | (H << 11); + Opcodes |= ((Q & 1) << 30); + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A * Q); + Vector128 V2 = MakeVectorE0E1(B, B * H); + + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + CompareAgainstUnicorn(FpsrMask: FPSR.IOC); + } + + [Test, Pairwise] [Explicit] + public void F_Mul_Mulx_Ve_2D([ValueSource("_F_Mul_Mulx_Ve_2D_")] uint Opcodes, + [Values(0u)] uint Rd, + [Values(1u, 0u)] uint Rn, + [Values(2u, 0u)] uint Rm, + [ValueSource("_1D_F_")] ulong Z, + [ValueSource("_1D_F_")] ulong A, + [ValueSource("_1D_F_")] ulong B, + [Values(0u, 1u)] uint Index) + { + uint H = Index & 1; + + Opcodes |= ((Rm & 31) << 16) | ((Rn & 31) << 5) | ((Rd & 31) << 0); + Opcodes |= H << 11; + + Vector128 V0 = MakeVectorE0E1(Z, Z); + Vector128 V1 = MakeVectorE0E1(A, A); + Vector128 V2 = MakeVectorE0E1(B, B * H); + + int Fpcr = (int)TestContext.CurrentContext.Random.NextUInt() & (1 << (int)FPCR.DN); + + AThreadState ThreadState = SingleOpcode(Opcodes, V0: V0, V1: V1, V2: V2, Fpcr: Fpcr); + + CompareAgainstUnicorn(FpsrMask: FPSR.IOC); + } +#endif + } +} From 72317d777734155881081d5989aabd80146e1197 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 13 Oct 2018 23:54:14 -0300 Subject: [PATCH 24/30] Add support for saturation on some shader instructions, fix ReadTexture alignment and add ColorMask support (#451) * Add support for saturation on some shader instructions, fix ReadTexture alignment * Add ColorMask support, other tweaks --- Ryujinx.Graphics/Gal/GalPipelineState.cs | 6 ++++++ Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs | 17 +++++++++++++++++ Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs | 2 +- Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs | 9 ++++++--- .../Gal/Shader/ShaderDecodeHelper.cs | 8 ++++++++ Ryujinx.Graphics/NvGpuEngine3d.cs | 15 +++++++++++++-- Ryujinx.Graphics/NvGpuEngine3dReg.cs | 1 + Ryujinx.Graphics/Texture/ImageUtils.cs | 17 +++++++++++------ 8 files changed, 63 insertions(+), 12 deletions(-) diff --git a/Ryujinx.Graphics/Gal/GalPipelineState.cs b/Ryujinx.Graphics/Gal/GalPipelineState.cs index 8837eb8c4b..1bfe2684f0 100644 --- a/Ryujinx.Graphics/Gal/GalPipelineState.cs +++ b/Ryujinx.Graphics/Gal/GalPipelineState.cs @@ -26,6 +26,7 @@ public GalComparisonOp DepthFunc; public bool StencilTestEnabled; + public bool StencilTwoSideEnabled; public GalComparisonOp StencilBackFuncFunc; public int StencilBackFuncRef; @@ -52,6 +53,11 @@ public GalBlendFactor BlendFuncSrcAlpha; public GalBlendFactor BlendFuncDstAlpha; + public bool ColorMaskR; + public bool ColorMaskG; + public bool ColorMaskB; + public bool ColorMaskA; + public bool PrimitiveRestartEnabled; public uint PrimitiveRestartIndex; diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs index 96da17f8e5..b7825996e5 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs @@ -195,6 +195,11 @@ namespace Ryujinx.Graphics.Gal.OpenGL Enable(EnableCap.StencilTest, New.StencilTestEnabled); } + if (New.StencilTwoSideEnabled != Old.StencilTwoSideEnabled) + { + Enable((EnableCap)All.StencilTestTwoSideExt, New.StencilTwoSideEnabled); + } + if (New.StencilTestEnabled) { if (New.StencilBackFuncFunc != Old.StencilBackFuncFunc || @@ -298,6 +303,18 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } + if (New.ColorMaskR != Old.ColorMaskR || + New.ColorMaskG != Old.ColorMaskG || + New.ColorMaskB != Old.ColorMaskB || + New.ColorMaskA != Old.ColorMaskA) + { + GL.ColorMask( + New.ColorMaskR, + New.ColorMaskG, + New.ColorMaskB, + New.ColorMaskA); + } + if (New.PrimitiveRestartEnabled != Old.PrimitiveRestartEnabled) { Enable(EnableCap.PrimitiveRestart, New.PrimitiveRestartEnabled); diff --git a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs index 11f0444948..d0f9223b91 100644 --- a/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs +++ b/Ryujinx.Graphics/Gal/Shader/GlslDecompiler.cs @@ -360,7 +360,7 @@ namespace Ryujinx.Graphics.Gal.Shader private void PrintDeclSsy() { - SB.AppendLine("uint " + GlslDecl.SsyCursorName + "= 0;"); + SB.AppendLine("uint " + GlslDecl.SsyCursorName + " = 0;"); SB.AppendLine("uint " + GlslDecl.SsyStackName + "[" + GlslDecl.SsyStackSize + "];" + Environment.NewLine); } diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs index 0a3c0da98a..d4a76bc936 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeAlu.cs @@ -585,6 +585,7 @@ namespace Ryujinx.Graphics.Gal.Shader bool AbsA = OpCode.Read(46); bool NegA = OpCode.Read(48); bool AbsB = OpCode.Read(49); + bool Sat = OpCode.Read(50); ShaderIrNode OperA = OpCode.Gpr8(), OperB; @@ -603,12 +604,13 @@ namespace Ryujinx.Graphics.Gal.Shader ShaderIrNode Op = new ShaderIrOp(ShaderIrInst.Fadd, OperA, OperB); - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); + Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), GetAluFsat(Op, Sat)))); } private static void EmitFmul(ShaderIrBlock Block, long OpCode, ShaderOper Oper) { bool NegB = OpCode.Read(48); + bool Sat = OpCode.Read(50); ShaderIrNode OperA = OpCode.Gpr8(), OperB; @@ -625,13 +627,14 @@ namespace Ryujinx.Graphics.Gal.Shader ShaderIrNode Op = new ShaderIrOp(ShaderIrInst.Fmul, OperA, OperB); - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); + Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), GetAluFsat(Op, Sat)))); } private static void EmitFfma(ShaderIrBlock Block, long OpCode, ShaderOper Oper) { bool NegB = OpCode.Read(48); bool NegC = OpCode.Read(49); + bool Sat = OpCode.Read(50); ShaderIrNode OperA = OpCode.Gpr8(), OperB, OperC; @@ -658,7 +661,7 @@ namespace Ryujinx.Graphics.Gal.Shader ShaderIrOp Op = new ShaderIrOp(ShaderIrInst.Ffma, OperA, OperB, OperC); - Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), Op))); + Block.AddNode(OpCode.PredNode(new ShaderIrAsg(OpCode.Gpr0(), GetAluFsat(Op, Sat)))); } private static void EmitIadd(ShaderIrBlock Block, long OpCode, ShaderOper Oper) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs index ebacd53ab4..d07bcd9171 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecodeHelper.cs @@ -2,6 +2,9 @@ namespace Ryujinx.Graphics.Gal.Shader { static class ShaderDecodeHelper { + private static readonly ShaderIrOperImmf ImmfZero = new ShaderIrOperImmf(0); + private static readonly ShaderIrOperImmf ImmfOne = new ShaderIrOperImmf(1); + public static ShaderIrNode GetAluFabsFneg(ShaderIrNode Node, bool Abs, bool Neg) { return GetAluFneg(GetAluFabs(Node, Abs), Neg); @@ -17,6 +20,11 @@ namespace Ryujinx.Graphics.Gal.Shader return Neg ? new ShaderIrOp(ShaderIrInst.Fneg, Node) : Node; } + public static ShaderIrNode GetAluFsat(ShaderIrNode Node, bool Sat) + { + return Sat ? new ShaderIrOp(ShaderIrInst.Fclamp, Node, ImmfZero, ImmfOne) : Node; + } + public static ShaderIrNode GetAluIabsIneg(ShaderIrNode Node, bool Abs, bool Neg) { return GetAluIneg(GetAluIabs(Node, Abs), Neg); diff --git a/Ryujinx.Graphics/NvGpuEngine3d.cs b/Ryujinx.Graphics/NvGpuEngine3d.cs index 3dd7746dae..a2a9692802 100644 --- a/Ryujinx.Graphics/NvGpuEngine3d.cs +++ b/Ryujinx.Graphics/NvGpuEngine3d.cs @@ -97,7 +97,8 @@ namespace Ryujinx.Graphics SetCullFace(State); SetDepth(State); SetStencil(State); - SetAlphaBlending(State); + SetBlending(State); + SetColorMask(State); SetPrimitiveRestart(State); for (int FbIndex = 0; FbIndex < 8; FbIndex++) @@ -403,7 +404,7 @@ namespace Ryujinx.Graphics } } - private void SetAlphaBlending(GalPipelineState State) + private void SetBlending(GalPipelineState State) { //TODO: Support independent blend properly. State.BlendEnabled = ReadRegisterBool(NvGpuEngine3dReg.IBlendNEnable); @@ -421,6 +422,16 @@ namespace Ryujinx.Graphics } } + private void SetColorMask(GalPipelineState State) + { + int ColorMask = ReadRegister(NvGpuEngine3dReg.ColorMask); + + State.ColorMaskR = ((ColorMask >> 0) & 0xf) != 0; + State.ColorMaskG = ((ColorMask >> 4) & 0xf) != 0; + State.ColorMaskB = ((ColorMask >> 8) & 0xf) != 0; + State.ColorMaskA = ((ColorMask >> 12) & 0xf) != 0; + } + private void SetPrimitiveRestart(GalPipelineState State) { State.PrimitiveRestartEnabled = ReadRegisterBool(NvGpuEngine3dReg.PrimRestartEnable); diff --git a/Ryujinx.Graphics/NvGpuEngine3dReg.cs b/Ryujinx.Graphics/NvGpuEngine3dReg.cs index 418e5b6b40..ba211313e7 100644 --- a/Ryujinx.Graphics/NvGpuEngine3dReg.cs +++ b/Ryujinx.Graphics/NvGpuEngine3dReg.cs @@ -78,6 +78,7 @@ namespace Ryujinx.Graphics CullFaceEnable = 0x646, FrontFace = 0x647, CullFace = 0x648, + ColorMask = 0x680, QueryAddress = 0x6c0, QuerySequence = 0x6c2, QueryControl = 0x6c3, diff --git a/Ryujinx.Graphics/Texture/ImageUtils.cs b/Ryujinx.Graphics/Texture/ImageUtils.cs index 1b043245ef..e1f370cda9 100644 --- a/Ryujinx.Graphics/Texture/ImageUtils.cs +++ b/Ryujinx.Graphics/Texture/ImageUtils.cs @@ -235,18 +235,23 @@ namespace Ryujinx.Graphics.Texture int BytesPerPixel = Desc.BytesPerPixel; - int OutOffs = 0; + //Note: Each row of the texture needs to be aligned to 4 bytes. + int Pitch = (Width * BytesPerPixel + 3) & ~3; - byte[] Data = new byte[Width * Height * BytesPerPixel]; + byte[] Data = new byte[Height * Pitch]; for (int Y = 0; Y < Height; Y++) - for (int X = 0; X < Width; X++) { - long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); + int OutOffs = Y * Pitch; - CpuMemory.ReadBytes(Position + Offset, Data, OutOffs, BytesPerPixel); + for (int X = 0; X < Width; X++) + { + long Offset = (uint)Swizzle.GetSwizzleOffset(X, Y); - OutOffs += BytesPerPixel; + CpuMemory.ReadBytes(Position + Offset, Data, OutOffs, BytesPerPixel); + + OutOffs += BytesPerPixel; + } } return Data; From b0cfe2fb1f129e56714ee94ccf20f0937dd1a6cd Mon Sep 17 00:00:00 2001 From: HorrorTroll Date: Mon, 15 Oct 2018 04:20:10 +0700 Subject: [PATCH 25/30] Add RG16Unorm & R16Uint (#456) * Implement B5G6R5Unorm & BGR5A1Unorm * Add RG16Unorm, R16Uint --- Ryujinx.Graphics/Texture/ImageUtils.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Ryujinx.Graphics/Texture/ImageUtils.cs b/Ryujinx.Graphics/Texture/ImageUtils.cs index e1f370cda9..68045de521 100644 --- a/Ryujinx.Graphics/Texture/ImageUtils.cs +++ b/Ryujinx.Graphics/Texture/ImageUtils.cs @@ -52,7 +52,7 @@ namespace Ryujinx.Graphics.Texture { GalTextureFormat.G8R8, GalImageFormat.G8R8 | Snorm | Unorm | Sint | Uint }, { GalTextureFormat.R16, GalImageFormat.R16 | Snorm | Unorm | Sint | Uint | Sfloat }, { GalTextureFormat.R8, GalImageFormat.R8 | Snorm | Unorm | Sint | Uint }, - { GalTextureFormat.R16G16, GalImageFormat.R16G16 | Snorm | Sfloat }, + { GalTextureFormat.R16G16, GalImageFormat.R16G16 | Snorm | Unorm | Sfloat }, { GalTextureFormat.R32, GalImageFormat.R32 | Sint | Uint | Sfloat }, { GalTextureFormat.A4B4G4R4, GalImageFormat.A4B4G4R4 | Unorm }, { GalTextureFormat.A1B5G5R5, GalImageFormat.A1R5G5B5 | Unorm }, @@ -185,6 +185,7 @@ namespace Ryujinx.Graphics.Texture case GalSurfaceFormat.RGBA8Srgb: return GalImageFormat.A8B8G8R8_SRGB; case GalSurfaceFormat.RGBA8Snorm: return GalImageFormat.A8B8G8R8 | Snorm; case GalSurfaceFormat.RG16Snorm: return GalImageFormat.R16G16 | Snorm; + case GalSurfaceFormat.RG16Unorm: return GalImageFormat.R16G16 | Unorm; case GalSurfaceFormat.RG16Float: return GalImageFormat.R16G16 | Sfloat; case GalSurfaceFormat.R11G11B10Float: return GalImageFormat.B10G11R11 | Sfloat; case GalSurfaceFormat.R32Float: return GalImageFormat.R32 | Sfloat; @@ -192,6 +193,7 @@ namespace Ryujinx.Graphics.Texture case GalSurfaceFormat.RG8Snorm: return GalImageFormat.R8 | Snorm; case GalSurfaceFormat.R16Float: return GalImageFormat.R16 | Sfloat; case GalSurfaceFormat.R16Unorm: return GalImageFormat.R16 | Unorm; + case GalSurfaceFormat.R16Uint: return GalImageFormat.R16 | Uint; case GalSurfaceFormat.R8Unorm: return GalImageFormat.R8 | Unorm; case GalSurfaceFormat.B5G6R5Unorm: return GalImageFormat.B5G6R5 | Unorm; case GalSurfaceFormat.BGR5A1Unorm: return GalImageFormat.A1R5G5B5 | Unorm; From 76330b10b4bc56a1143aac92b14d855aec2e1dbb Mon Sep 17 00:00:00 2001 From: HorrorTroll Date: Mon, 15 Oct 2018 04:23:23 +0700 Subject: [PATCH 26/30] Add G8R8Unorm, G8R8Snorm, B8G8R8A8, D24_S8 Uint, R8Uint, R32Uint (#426) * Implement B5G6R5Unorm & BGR5A1Unorm * Fix R8G8 Unorm to G8R8 Unorm * Added back R8G8 Unorm * Fix G8R8Unorm, add R8G8B8A8 * Add D24_S8 Uint * Add R8Uint & R32Uint * Another fixed * Reverting back * R8G8B8A8 change to B8G8R8A8 * Add G8R8 Snorm --- Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs | 4 ++++ Ryujinx.Graphics/Texture/ImageUtils.cs | 9 ++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs index 388e06b2dc..9a3a1a98f6 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs @@ -141,6 +141,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalImageFormat.A8B8G8R8 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba8i, PixelFormat.RgbaInteger, PixelType.Byte); case GalImageFormat.A8B8G8R8 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba8ui, PixelFormat.RgbaInteger, PixelType.UnsignedByte); case GalImageFormat.A8B8G8R8_SRGB: return (PixelInternalFormat.Srgb8Alpha8, PixelFormat.Rgba, PixelType.UnsignedByte); + case GalImageFormat.B8G8R8A8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Bgra, PixelType.UnsignedByte); case GalImageFormat.A4B4G4R4 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba4, PixelFormat.Rgba, PixelType.UnsignedShort4444Reversed); case GalImageFormat.A2B10G10R10 | GalImageFormat.Uint: return (PixelInternalFormat.Rgb10A2ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt2101010Reversed); case GalImageFormat.A2B10G10R10 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb10A2, PixelFormat.Rgba, PixelType.UnsignedInt2101010Reversed); @@ -157,6 +158,8 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalImageFormat.R8G8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg8Snorm, PixelFormat.Rg, PixelType.Byte); case GalImageFormat.R8G8 | GalImageFormat.Uint: return (PixelInternalFormat.Rg8ui, PixelFormat.RgInteger, PixelType.UnsignedByte); case GalImageFormat.R8G8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg8, PixelFormat.Rg, PixelType.UnsignedByte); + case GalImageFormat.G8R8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg8Snorm, PixelFormat.Rg, PixelType.Byte); + case GalImageFormat.G8R8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg8, PixelFormat.Rg, PixelType.UnsignedByte); case GalImageFormat.R16 | GalImageFormat.Sfloat: return (PixelInternalFormat.R16f, PixelFormat.Red, PixelType.HalfFloat); case GalImageFormat.R16 | GalImageFormat.Sint: return (PixelInternalFormat.R16i, PixelFormat.RedInteger, PixelType.Short); case GalImageFormat.R16 | GalImageFormat.Snorm: return (PixelInternalFormat.R16Snorm, PixelFormat.Red, PixelType.Byte); @@ -168,6 +171,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL case GalImageFormat.R8 | GalImageFormat.Unorm: return (PixelInternalFormat.R8, PixelFormat.Red, PixelType.UnsignedByte); case GalImageFormat.B10G11R11 | GalImageFormat.Sfloat: return (PixelInternalFormat.R11fG11fB10f, PixelFormat.Rgb, PixelType.UnsignedInt10F11F11FRev); + case GalImageFormat.D24_S8 | GalImageFormat.Uint: return (PixelInternalFormat.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248); case GalImageFormat.D24_S8 | GalImageFormat.Unorm: return (PixelInternalFormat.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248); case GalImageFormat.D32 | GalImageFormat.Sfloat: return (PixelInternalFormat.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float); case GalImageFormat.D16 | GalImageFormat.Unorm: return (PixelInternalFormat.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort); diff --git a/Ryujinx.Graphics/Texture/ImageUtils.cs b/Ryujinx.Graphics/Texture/ImageUtils.cs index 68045de521..2c4e7b4b0a 100644 --- a/Ryujinx.Graphics/Texture/ImageUtils.cs +++ b/Ryujinx.Graphics/Texture/ImageUtils.cs @@ -58,7 +58,7 @@ namespace Ryujinx.Graphics.Texture { GalTextureFormat.A1B5G5R5, GalImageFormat.A1R5G5B5 | Unorm }, { GalTextureFormat.B5G6R5, GalImageFormat.B5G6R5 | Unorm }, { GalTextureFormat.BF10GF11RF11, GalImageFormat.B10G11R11 | Sfloat }, - { GalTextureFormat.Z24S8, GalImageFormat.D24_S8 | Unorm }, + { GalTextureFormat.Z24S8, GalImageFormat.D24_S8 | Unorm | Uint }, { GalTextureFormat.ZF32, GalImageFormat.D32 | Sfloat }, { GalTextureFormat.ZF32_X24S8, GalImageFormat.D32_S8 | Unorm }, { GalTextureFormat.Z16, GalImageFormat.D16 | Unorm }, @@ -94,6 +94,7 @@ namespace Ryujinx.Graphics.Texture { GalImageFormat.R32G32B32A32, new ImageDescriptor(16, 1, 1, TargetBuffer.Color) }, { GalImageFormat.R16G16B16A16, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) }, { GalImageFormat.R32G32, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.B8G8R8A8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, { GalImageFormat.A8B8G8R8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, { GalImageFormat.A2B10G10R10, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, { GalImageFormat.R32, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, @@ -178,7 +179,7 @@ namespace Ryujinx.Graphics.Texture case GalSurfaceFormat.RG32Float: return GalImageFormat.R32G32 | Sfloat; case GalSurfaceFormat.RG32Sint: return GalImageFormat.R32G32 | Sint; case GalSurfaceFormat.RG32Uint: return GalImageFormat.R32G32 | Uint; - case GalSurfaceFormat.BGRA8Unorm: return GalImageFormat.R8G8B8A8 | Unorm; //Is this right? + case GalSurfaceFormat.BGRA8Unorm: return GalImageFormat.B8G8R8A8 | Unorm; case GalSurfaceFormat.BGRA8Srgb: return GalImageFormat.A8B8G8R8_SRGB; //This one might be wrong case GalSurfaceFormat.RGB10A2Unorm: return GalImageFormat.A2B10G10R10 | Unorm; case GalSurfaceFormat.RGBA8Unorm: return GalImageFormat.A8B8G8R8 | Unorm; @@ -189,12 +190,14 @@ namespace Ryujinx.Graphics.Texture case GalSurfaceFormat.RG16Float: return GalImageFormat.R16G16 | Sfloat; case GalSurfaceFormat.R11G11B10Float: return GalImageFormat.B10G11R11 | Sfloat; case GalSurfaceFormat.R32Float: return GalImageFormat.R32 | Sfloat; + case GalSurfaceFormat.R32Uint: return GalImageFormat.R32 | Uint; case GalSurfaceFormat.RG8Unorm: return GalImageFormat.R8G8 | Unorm; - case GalSurfaceFormat.RG8Snorm: return GalImageFormat.R8 | Snorm; + case GalSurfaceFormat.RG8Snorm: return GalImageFormat.R8G8 | Snorm; case GalSurfaceFormat.R16Float: return GalImageFormat.R16 | Sfloat; case GalSurfaceFormat.R16Unorm: return GalImageFormat.R16 | Unorm; case GalSurfaceFormat.R16Uint: return GalImageFormat.R16 | Uint; case GalSurfaceFormat.R8Unorm: return GalImageFormat.R8 | Unorm; + case GalSurfaceFormat.R8Uint: return GalImageFormat.R8 | Uint; case GalSurfaceFormat.B5G6R5Unorm: return GalImageFormat.B5G6R5 | Unorm; case GalSurfaceFormat.BGR5A1Unorm: return GalImageFormat.A1R5G5B5 | Unorm; } From 9b19ea3c87518234fd72a6dead727eb5d8379c0d Mon Sep 17 00:00:00 2001 From: Thomas Guillemard Date: Wed, 17 Oct 2018 01:55:10 +0200 Subject: [PATCH 27/30] Remove IFileSystem::OpenDirectory extraneous check (#459) A directory can be open more than one time. This fix issues with homebrews opening the same directory multiple time. --- Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs index b77043bddb..bd249e5084 100644 --- a/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs +++ b/Ryujinx.HLE/HOS/Services/FspSrv/IFileSystem.cs @@ -281,11 +281,6 @@ namespace Ryujinx.HLE.HOS.Services.FspSrv return MakeError(ErrorModule.Fs, FsErr.PathDoesNotExist); } - if (IsPathAlreadyInUse(DirName)) - { - return MakeError(ErrorModule.Fs, FsErr.PathAlreadyInUse); - } - IDirectory DirInterface = new IDirectory(DirName, FilterFlags); DirInterface.Disposed += RemoveDirectoryInUse; From b3a4662be15bd63d5b70cf4be21d79959e11ccfc Mon Sep 17 00:00:00 2001 From: ReinUsesLisp Date: Wed, 17 Oct 2018 14:15:50 -0300 Subject: [PATCH 28/30] Move logging to Ryujinx.Common and make it a static class (#413) --- Ryujinx.Audio/Ryujinx.Audio.csproj | 4 + .../Logging/LogClass.cs | 2 +- .../Logging/LogEventArgs.cs | 2 +- .../Logging/LogLevel.cs | 2 +- .../Logging/Logger.cs | 34 +- Ryujinx.Common/Ryujinx.Common.csproj | 16 + Ryujinx.Graphics/Ryujinx.Graphics.csproj | 1 + Ryujinx.HLE/HOS/Horizon.cs | 24 +- Ryujinx.HLE/HOS/Kernel/SvcHandler.cs | 6 +- Ryujinx.HLE/HOS/Kernel/SvcMemory.cs | 94 ++-- Ryujinx.HLE/HOS/Kernel/SvcSystem.cs | 18 +- Ryujinx.HLE/HOS/Kernel/SvcThread.cs | 44 +- Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs | 56 +-- Ryujinx.HLE/HOS/Process.cs | 10 +- .../HOS/Services/Acc/IAccountService.cs | 10 +- .../Services/Acc/IManagerForApplication.cs | 6 +- Ryujinx.HLE/HOS/Services/Acc/IProfile.cs | 4 +- .../HOS/Services/Am/IApplicationFunctions.cs | 12 +- .../HOS/Services/Am/IAudioController.cs | 12 +- .../HOS/Services/Am/ICommonStateGetter.cs | 6 +- .../HOS/Services/Am/IHomeMenuFunctions.cs | 6 +- .../HOS/Services/Am/ILibraryAppletAccessor.cs | 12 +- .../HOS/Services/Am/ISelfController.cs | 30 +- .../HOS/Services/Am/IWindowController.cs | 6 +- Ryujinx.HLE/HOS/Services/Apm/ISession.cs | 4 +- .../Aud/AudioRenderer/IAudioRenderer.cs | 8 +- Ryujinx.HLE/HOS/Services/Aud/IAudioDevice.cs | 24 +- .../HOS/Services/Aud/IAudioOutManager.cs | 10 +- .../HOS/Services/Aud/IAudioRendererManager.cs | 8 +- .../HOS/Services/Friend/IFriendService.cs | 28 +- Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs | 418 +++++++++--------- Ryujinx.HLE/HOS/Services/IpcService.cs | 4 +- .../HOS/Services/Irs/IIrSensorServer.cs | 8 +- Ryujinx.HLE/HOS/Services/Lm/ILogger.cs | 12 +- Ryujinx.HLE/HOS/Services/Mm/IRequest.cs | 10 +- Ryujinx.HLE/HOS/Services/Nfp/IUser.cs | 18 +- .../HOS/Services/Nifm/IGeneralService.cs | 6 +- Ryujinx.HLE/HOS/Services/Nifm/IRequest.cs | 12 +- .../HOS/Services/Ns/IAddOnContentManager.cs | 6 +- Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs | 8 +- .../HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs | 28 +- .../HOS/Services/Nv/NvGpuGpu/NvGpuGpuIoctl.cs | 10 +- .../Nv/NvHostChannel/NvHostChannelIoctl.cs | 16 +- .../Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs | 12 +- .../HOS/Services/Nv/NvMap/NvMapIoctl.cs | 22 +- .../Services/Pctl/IParentalControlService.cs | 4 +- .../HOS/Services/Prepo/IPrepoService.cs | 4 +- .../HOS/Services/Set/ISystemSettingsServer.cs | 7 +- .../HOS/Services/Sfdnsres/IResolver.cs | 13 +- Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs | 6 +- .../HOS/Services/Time/ITimeZoneService.cs | 14 +- .../HOS/Services/Vi/IManagerDisplayService.cs | 10 +- .../HOS/Services/Vi/ISystemDisplayService.cs | 6 +- Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs | 4 +- Ryujinx.HLE/Ryujinx.HLE.csproj | 1 + Ryujinx.HLE/Switch.cs | 5 - Ryujinx.sln | 10 +- Ryujinx/Config.cs | 16 +- Ryujinx/Ryujinx.csproj | 3 +- Ryujinx/Ui/ConsoleLog.cs | 2 +- Ryujinx/Ui/Program.cs | 3 +- 61 files changed, 612 insertions(+), 585 deletions(-) rename {Ryujinx.HLE => Ryujinx.Common}/Logging/LogClass.cs (95%) rename {Ryujinx.HLE => Ryujinx.Common}/Logging/LogEventArgs.cs (92%) rename {Ryujinx.HLE => Ryujinx.Common}/Logging/LogLevel.cs (77%) rename {Ryujinx.HLE => Ryujinx.Common}/Logging/Logger.cs (57%) create mode 100644 Ryujinx.Common/Ryujinx.Common.csproj diff --git a/Ryujinx.Audio/Ryujinx.Audio.csproj b/Ryujinx.Audio/Ryujinx.Audio.csproj index 2cd38add9b..3fc6117096 100644 --- a/Ryujinx.Audio/Ryujinx.Audio.csproj +++ b/Ryujinx.Audio/Ryujinx.Audio.csproj @@ -9,4 +9,8 @@ + + + + diff --git a/Ryujinx.HLE/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs similarity index 95% rename from Ryujinx.HLE/Logging/LogClass.cs rename to Ryujinx.Common/Logging/LogClass.cs index 0458c75f8a..97ffb31477 100644 --- a/Ryujinx.HLE/Logging/LogClass.cs +++ b/Ryujinx.Common/Logging/LogClass.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.HLE.Logging +namespace Ryujinx.Common.Logging { public enum LogClass { diff --git a/Ryujinx.HLE/Logging/LogEventArgs.cs b/Ryujinx.Common/Logging/LogEventArgs.cs similarity index 92% rename from Ryujinx.HLE/Logging/LogEventArgs.cs rename to Ryujinx.Common/Logging/LogEventArgs.cs index 647cf71319..7a479b71c7 100644 --- a/Ryujinx.HLE/Logging/LogEventArgs.cs +++ b/Ryujinx.Common/Logging/LogEventArgs.cs @@ -1,6 +1,6 @@ using System; -namespace Ryujinx.HLE.Logging +namespace Ryujinx.Common.Logging { public class LogEventArgs : EventArgs { diff --git a/Ryujinx.HLE/Logging/LogLevel.cs b/Ryujinx.Common/Logging/LogLevel.cs similarity index 77% rename from Ryujinx.HLE/Logging/LogLevel.cs rename to Ryujinx.Common/Logging/LogLevel.cs index 971333e60a..ba3fa99f4d 100644 --- a/Ryujinx.HLE/Logging/LogLevel.cs +++ b/Ryujinx.Common/Logging/LogLevel.cs @@ -1,4 +1,4 @@ -namespace Ryujinx.HLE.Logging +namespace Ryujinx.Common.Logging { public enum LogLevel { diff --git a/Ryujinx.HLE/Logging/Logger.cs b/Ryujinx.Common/Logging/Logger.cs similarity index 57% rename from Ryujinx.HLE/Logging/Logger.cs rename to Ryujinx.Common/Logging/Logger.cs index 5376b253a3..6422f11313 100644 --- a/Ryujinx.HLE/Logging/Logger.cs +++ b/Ryujinx.Common/Logging/Logger.cs @@ -2,18 +2,18 @@ using System; using System.Diagnostics; using System.Runtime.CompilerServices; -namespace Ryujinx.HLE.Logging +namespace Ryujinx.Common.Logging { - public class Logger + public static class Logger { - private bool[] EnabledLevels; - private bool[] EnabledClasses; + private static bool[] EnabledLevels; + private static bool[] EnabledClasses; - public event EventHandler Updated; + public static event EventHandler Updated; - private Stopwatch Time; + private static Stopwatch Time; - public Logger() + static Logger() { EnabledLevels = new bool[Enum.GetNames(typeof(LogLevel)).Length]; EnabledClasses = new bool[Enum.GetNames(typeof(LogClass)).Length]; @@ -33,50 +33,50 @@ namespace Ryujinx.HLE.Logging Time.Start(); } - public void SetEnable(LogLevel Level, bool Enabled) + public static void SetEnable(LogLevel Level, bool Enabled) { EnabledLevels[(int)Level] = Enabled; } - public void SetEnable(LogClass Class, bool Enabled) + public static void SetEnable(LogClass Class, bool Enabled) { EnabledClasses[(int)Class] = Enabled; } - internal void PrintDebug(LogClass Class, string Message, [CallerMemberName] string Caller = "") + public static void PrintDebug(LogClass Class, string Message, [CallerMemberName] string Caller = "") { Print(LogLevel.Debug, Class, GetFormattedMessage(Class, Message, Caller)); } - internal void PrintStub(LogClass Class, string Message, [CallerMemberName] string Caller = "") + public static void PrintStub(LogClass Class, string Message, [CallerMemberName] string Caller = "") { Print(LogLevel.Stub, Class, GetFormattedMessage(Class, Message, Caller)); } - internal void PrintInfo(LogClass Class, string Message, [CallerMemberName] string Caller = "") + public static void PrintInfo(LogClass Class, string Message, [CallerMemberName] string Caller = "") { Print(LogLevel.Info, Class, GetFormattedMessage(Class, Message, Caller)); } - internal void PrintWarning(LogClass Class, string Message, [CallerMemberName] string Caller = "") + public static void PrintWarning(LogClass Class, string Message, [CallerMemberName] string Caller = "") { Print(LogLevel.Warning, Class, GetFormattedMessage(Class, Message, Caller)); } - internal void PrintError(LogClass Class, string Message, [CallerMemberName] string Caller = "") + public static void PrintError(LogClass Class, string Message, [CallerMemberName] string Caller = "") { Print(LogLevel.Error, Class, GetFormattedMessage(Class, Message, Caller)); } - private void Print(LogLevel Level, LogClass Class, string Message) + private static void Print(LogLevel Level, LogClass Class, string Message) { if (EnabledLevels[(int)Level] && EnabledClasses[(int)Class]) { - Updated?.Invoke(this, new LogEventArgs(Level, Time.Elapsed, Message)); + Updated?.Invoke(null, new LogEventArgs(Level, Time.Elapsed, Message)); } } - private string GetFormattedMessage(LogClass Class, string Message, string Caller) + private static string GetFormattedMessage(LogClass Class, string Message, string Caller) { return $"{Class} {Caller}: {Message}"; } diff --git a/Ryujinx.Common/Ryujinx.Common.csproj b/Ryujinx.Common/Ryujinx.Common.csproj new file mode 100644 index 0000000000..5c9293b707 --- /dev/null +++ b/Ryujinx.Common/Ryujinx.Common.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp2.1 + win10-x64;osx-x64;linux-x64 + + + + true + + + + true + + + diff --git a/Ryujinx.Graphics/Ryujinx.Graphics.csproj b/Ryujinx.Graphics/Ryujinx.Graphics.csproj index 7d86cbe134..be113a21bf 100644 --- a/Ryujinx.Graphics/Ryujinx.Graphics.csproj +++ b/Ryujinx.Graphics/Ryujinx.Graphics.csproj @@ -19,6 +19,7 @@ + diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs index a3eaae4fcd..1cb419b984 100644 --- a/Ryujinx.HLE/HOS/Horizon.cs +++ b/Ryujinx.HLE/HOS/Horizon.cs @@ -1,10 +1,10 @@ using LibHac; +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Font; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Loaders.Npdm; -using Ryujinx.HLE.Logging; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -104,7 +104,7 @@ namespace Ryujinx.HLE.HOS if (File.Exists(NpdmFileName)) { - Device.Log.PrintInfo(LogClass.Loader, $"Loading main.npdm..."); + Logger.PrintInfo(LogClass.Loader, $"Loading main.npdm..."); using (FileStream Input = new FileStream(NpdmFileName, FileMode.Open)) { @@ -113,7 +113,7 @@ namespace Ryujinx.HLE.HOS } else { - Device.Log.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!"); + Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!"); } Process MainProcess = MakeProcess(MetaData); @@ -127,7 +127,7 @@ namespace Ryujinx.HLE.HOS continue; } - Device.Log.PrintInfo(LogClass.Loader, $"Loading {Path.GetFileNameWithoutExtension(File)}..."); + Logger.PrintInfo(LogClass.Loader, $"Loading {Path.GetFileNameWithoutExtension(File)}..."); using (FileStream Input = new FileStream(File, FileMode.Open)) { @@ -168,7 +168,7 @@ namespace Ryujinx.HLE.HOS if (MainNca == null) { - Device.Log.PrintError(LogClass.Loader, "Unable to load XCI"); + Logger.PrintError(LogClass.Loader, "Unable to load XCI"); return; } @@ -212,7 +212,7 @@ namespace Ryujinx.HLE.HOS if (MainNca == null) { - Device.Log.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided XCI file"); + Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided XCI file"); } MainNca.SetBaseNca(PatchNca); @@ -292,7 +292,7 @@ namespace Ryujinx.HLE.HOS return; } - Device.Log.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided NSP file"); + Logger.PrintError(LogClass.Loader, "Could not find an Application NCA in the provided NSP file"); } public void LoadNca(Nca MainNca, Nca ControlNca) @@ -302,14 +302,14 @@ namespace Ryujinx.HLE.HOS if (ExefsSection == null) { - Device.Log.PrintError(LogClass.Loader, "No ExeFS found in NCA"); + Logger.PrintError(LogClass.Loader, "No ExeFS found in NCA"); return; } if (RomfsSection == null) { - Device.Log.PrintWarning(LogClass.Loader, "No RomFS found in NCA"); + Logger.PrintWarning(LogClass.Loader, "No RomFS found in NCA"); } else { @@ -326,13 +326,13 @@ namespace Ryujinx.HLE.HOS if (Exefs.FileExists("main.npdm")) { - Device.Log.PrintInfo(LogClass.Loader, "Loading main.npdm..."); + Logger.PrintInfo(LogClass.Loader, "Loading main.npdm..."); MetaData = new Npdm(Exefs.OpenFile("main.npdm")); } else { - Device.Log.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!"); + Logger.PrintWarning(LogClass.Loader, $"NPDM file not found, using default values!"); } Process MainProcess = MakeProcess(MetaData); @@ -346,7 +346,7 @@ namespace Ryujinx.HLE.HOS continue; } - Device.Log.PrintInfo(LogClass.Loader, $"Loading {Filename}..."); + Logger.PrintInfo(LogClass.Loader, $"Loading {Filename}..."); string Name = Path.GetFileNameWithoutExtension(File.Name); diff --git a/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs b/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs index b678037b9e..d098aab0e5 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcHandler.cs @@ -1,8 +1,8 @@ using ChocolArm64.Events; using ChocolArm64.Memory; using ChocolArm64.State; +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; @@ -109,11 +109,11 @@ namespace Ryujinx.HLE.HOS.Kernel if (SvcFuncs.TryGetValue(e.Id, out SvcFunc Func)) { - Device.Log.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} called."); + Logger.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} called."); Func(ThreadState); - Device.Log.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} ended."); + Logger.PrintDebug(LogClass.KernelSvc, $"{Func.Method.Name} ended."); } else { diff --git a/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs b/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs index e3c0cf5b5b..07bc474b80 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcMemory.cs @@ -1,5 +1,5 @@ using ChocolArm64.State; -using Ryujinx.HLE.Logging; +using Ryujinx.Common.Logging; using static Ryujinx.HLE.HOS.ErrorCode; @@ -13,7 +13,7 @@ namespace Ryujinx.HLE.HOS.Kernel if ((Size & 0xFFFFFFFE001FFFFF) != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Heap size 0x{Size:x16} is not aligned!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Heap size 0x{Size:x16} is not aligned!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); @@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Kernel } else { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } } @@ -41,7 +41,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Position)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -50,7 +50,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Size) || Size == 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); @@ -65,7 +65,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Attributes != AttributeMask || (Attributes | MemoryAttribute.Uncached) != MemoryAttribute.Uncached) { - Device.Log.PrintWarning(LogClass.KernelSvc, "Invalid memory attributes!"); + Logger.PrintWarning(LogClass.KernelSvc, "Invalid memory attributes!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue); @@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } else { @@ -98,7 +98,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Src | Dst)) { - Device.Log.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!"); + Logger.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -107,7 +107,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Size) || Size == 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); @@ -116,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Kernel if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst) { - Device.Log.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!"); + Logger.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -125,7 +125,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!InsideAddrSpace(Src, Size)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -134,7 +134,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!InsideNewMapRegion(Dst, Size)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); @@ -145,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } ThreadState.X0 = (ulong)Result; @@ -159,7 +159,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Src | Dst)) { - Device.Log.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!"); + Logger.PrintWarning(LogClass.KernelSvc, "Addresses are not page aligned!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -168,7 +168,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Size) || Size == 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); @@ -177,7 +177,7 @@ namespace Ryujinx.HLE.HOS.Kernel if ((ulong)(Src + Size) <= (ulong)Src || (ulong)(Dst + Size) <= (ulong)Dst) { - Device.Log.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!"); + Logger.PrintWarning(LogClass.KernelSvc, "Addresses outside of range!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -186,7 +186,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!InsideAddrSpace(Src, Size)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Src address 0x{Src:x16} out of range!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -195,7 +195,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!InsideNewMapRegion(Dst, Size)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Dst address 0x{Dst:x16} out of range!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMemRange); @@ -206,7 +206,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } ThreadState.X0 = (ulong)Result; @@ -240,7 +240,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Position)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -249,7 +249,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Size) || Size == 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); @@ -258,7 +258,7 @@ namespace Ryujinx.HLE.HOS.Kernel if ((ulong)(Position + Size) <= (ulong)Position) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -269,7 +269,7 @@ namespace Ryujinx.HLE.HOS.Kernel if ((Permission | MemoryPermission.Write) != MemoryPermission.ReadAndWrite) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPermission); @@ -280,7 +280,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (SharedMemory == null) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); @@ -289,7 +289,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -298,7 +298,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (SharedMemory.Size != Size) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} does not match shared memory size 0x{SharedMemory.Size:16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} does not match shared memory size 0x{SharedMemory.Size:16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); @@ -309,7 +309,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } ThreadState.X0 = (ulong)Result; @@ -323,7 +323,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Position)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -332,7 +332,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Size) || Size == 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); @@ -341,7 +341,7 @@ namespace Ryujinx.HLE.HOS.Kernel if ((ulong)(Position + Size) <= (ulong)Position) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -352,7 +352,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (SharedMemory == null) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid shared memory handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); @@ -361,7 +361,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!InsideAddrSpace(Position, Size) || InsideMapRegion(Position, Size) || InsideHeapRegion(Position, Size)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} out of range!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -372,7 +372,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } ThreadState.X0 = (ulong)Result; @@ -385,7 +385,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Position)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -394,7 +394,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Size) || Size == 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -403,7 +403,7 @@ namespace Ryujinx.HLE.HOS.Kernel if ((ulong)(Position + Size) <= (ulong)Position) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -414,7 +414,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Permission > MemoryPermission.ReadAndWrite || Permission == MemoryPermission.Write) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid permission {Permission}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPermission); @@ -438,7 +438,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Position)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -447,7 +447,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Size) || Size == 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); @@ -456,7 +456,7 @@ namespace Ryujinx.HLE.HOS.Kernel if ((ulong)(Position + Size) <= (ulong)Position) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -465,7 +465,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!InsideAddrSpace(Position, Size)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -476,7 +476,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } ThreadState.X0 = (ulong)Result; @@ -489,7 +489,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Position)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Address 0x{Position:x16} is not page aligned!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -498,7 +498,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!PageAligned(Size) || Size == 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Size 0x{Size:x16} is not page aligned or is zero!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidSize); @@ -507,7 +507,7 @@ namespace Ryujinx.HLE.HOS.Kernel if ((ulong)(Position + Size) <= (ulong)Position) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid region address 0x{Position:x16} / size 0x{Size:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -516,7 +516,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (!InsideAddrSpace(Position, Size)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address {Position:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -527,7 +527,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } ThreadState.X0 = (ulong)Result; diff --git a/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs b/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs index 396519fb29..6eb8419cb2 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcSystem.cs @@ -1,9 +1,9 @@ using ChocolArm64.Memory; using ChocolArm64.State; +using Ryujinx.Common.Logging; using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Services; -using Ryujinx.HLE.Logging; using System; using System.Threading; @@ -46,7 +46,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != KernelResult.Success) { - Device.Log.PrintWarning(LogClass.KernelSvc, "Operation failed with error: " + Result + "!"); + Logger.PrintWarning(LogClass.KernelSvc, "Operation failed with error: " + Result + "!"); } return Result; @@ -76,7 +76,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != KernelResult.Success) { - Device.Log.PrintWarning(LogClass.KernelSvc, "Operation failed with error: " + Result + "!"); + Logger.PrintWarning(LogClass.KernelSvc, "Operation failed with error: " + Result + "!"); } return Result; @@ -92,7 +92,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Obj == null) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); @@ -136,11 +136,11 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result == KernelResult.InvalidState) { - Device.Log.PrintDebug(LogClass.KernelSvc, "Operation failed with error: " + Result + "!"); + Logger.PrintDebug(LogClass.KernelSvc, "Operation failed with error: " + Result + "!"); } else if (Result != KernelResult.Success) { - Device.Log.PrintWarning(LogClass.KernelSvc, "Operation failed with error: " + Result + "!"); + Logger.PrintWarning(LogClass.KernelSvc, "Operation failed with error: " + Result + "!"); } return Result; @@ -220,7 +220,7 @@ namespace Ryujinx.HLE.HOS.Kernel } else { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid session handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid session handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); } @@ -255,7 +255,7 @@ namespace Ryujinx.HLE.HOS.Kernel } else { - Device.Log.PrintInfo(LogClass.KernelSvc, "Debugger triggered"); + Logger.PrintInfo(LogClass.KernelSvc, "Debugger triggered"); Process.PrintStackTrace(ThreadState); } } @@ -267,7 +267,7 @@ namespace Ryujinx.HLE.HOS.Kernel string Str = AMemoryHelper.ReadAsciiString(Memory, Position, Size); - Device.Log.PrintWarning(LogClass.KernelSvc, Str); + Logger.PrintWarning(LogClass.KernelSvc, Str); ThreadState.X0 = 0; } diff --git a/Ryujinx.HLE/HOS/Kernel/SvcThread.cs b/Ryujinx.HLE/HOS/Kernel/SvcThread.cs index d9273e23a2..4c1744e6e3 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcThread.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcThread.cs @@ -1,5 +1,5 @@ using ChocolArm64.State; -using Ryujinx.HLE.Logging; +using Ryujinx.Common.Logging; using static Ryujinx.HLE.HOS.ErrorCode; @@ -17,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Kernel if ((uint)Priority > 0x3f) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid priority 0x{Priority:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid priority 0x{Priority:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidPriority); @@ -31,7 +31,7 @@ namespace Ryujinx.HLE.HOS.Kernel } else if ((uint)ProcessorId > 3) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{ProcessorId:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{ProcessorId:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId); @@ -61,14 +61,14 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } ThreadState.X0 = (ulong)Result; } else { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); } @@ -87,7 +87,7 @@ namespace Ryujinx.HLE.HOS.Kernel { long Timeout = (long)ThreadState.X0; - Device.Log.PrintDebug(LogClass.KernelSvc, "Timeout = 0x" + Timeout.ToString("x16")); + Logger.PrintDebug(LogClass.KernelSvc, "Timeout = 0x" + Timeout.ToString("x16")); KThread CurrentThread = System.Scheduler.GetCurrentThread(); @@ -121,7 +121,7 @@ namespace Ryujinx.HLE.HOS.Kernel } else { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); } @@ -132,7 +132,7 @@ namespace Ryujinx.HLE.HOS.Kernel int Handle = (int)ThreadState.X0; int Priority = (int)ThreadState.X1; - Device.Log.PrintDebug(LogClass.KernelSvc, + Logger.PrintDebug(LogClass.KernelSvc, "Handle = 0x" + Handle .ToString("x8") + ", " + "Priority = 0x" + Priority.ToString("x8")); @@ -142,7 +142,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Thread == null) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); @@ -158,7 +158,7 @@ namespace Ryujinx.HLE.HOS.Kernel { int Handle = (int)ThreadState.X2; - Device.Log.PrintDebug(LogClass.KernelSvc, "Handle = 0x" + Handle.ToString("x8")); + Logger.PrintDebug(LogClass.KernelSvc, "Handle = 0x" + Handle.ToString("x8")); KThread Thread = Process.HandleTable.GetKThread(Handle); @@ -170,7 +170,7 @@ namespace Ryujinx.HLE.HOS.Kernel } else { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); } @@ -182,7 +182,7 @@ namespace Ryujinx.HLE.HOS.Kernel int PrefferedCore = (int)ThreadState.X1; long AffinityMask = (long)ThreadState.X2; - Device.Log.PrintDebug(LogClass.KernelSvc, + Logger.PrintDebug(LogClass.KernelSvc, "Handle = 0x" + Handle .ToString("x8") + ", " + "PrefferedCore = 0x" + PrefferedCore.ToString("x8") + ", " + "AffinityMask = 0x" + AffinityMask .ToString("x16")); @@ -202,7 +202,7 @@ namespace Ryujinx.HLE.HOS.Kernel { if ((PrefferedCore | 2) != -1) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{PrefferedCore:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core id 0x{PrefferedCore:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidCoreId); @@ -211,7 +211,7 @@ namespace Ryujinx.HLE.HOS.Kernel } else if ((AffinityMask & (1 << PrefferedCore)) == 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{AffinityMask:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid core mask 0x{AffinityMask:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidMaskValue); @@ -223,7 +223,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Thread == null) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); @@ -234,7 +234,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } ThreadState.X0 = (ulong)Result; @@ -258,7 +258,7 @@ namespace Ryujinx.HLE.HOS.Kernel } else { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); } @@ -273,7 +273,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Thread == null) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); @@ -282,7 +282,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Thread.Owner != Process) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread owner process!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread owner process!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); @@ -293,7 +293,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } ThreadState.X0 = (ulong)Result; @@ -308,7 +308,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Thread == null) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{Handle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); @@ -317,7 +317,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Process.GetThread(ThreadState.Tpidr) == Thread) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Thread handle 0x{Handle:x8} is current thread!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Thread handle 0x{Handle:x8} is current thread!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidThread); diff --git a/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs b/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs index db9f6fb48b..73719c19f7 100644 --- a/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs +++ b/Ryujinx.HLE/HOS/Kernel/SvcThreadSync.cs @@ -1,5 +1,5 @@ using ChocolArm64.State; -using Ryujinx.HLE.Logging; +using Ryujinx.Common.Logging; using System.Collections.Generic; using static Ryujinx.HLE.HOS.ErrorCode; @@ -14,7 +14,7 @@ namespace Ryujinx.HLE.HOS.Kernel int HandlesCount = (int)ThreadState.X2; long Timeout = (long)ThreadState.X3; - Device.Log.PrintDebug(LogClass.KernelSvc, + Logger.PrintDebug(LogClass.KernelSvc, "HandlesPtr = 0x" + HandlesPtr .ToString("x16") + ", " + "HandlesCount = 0x" + HandlesCount.ToString("x8") + ", " + "Timeout = 0x" + Timeout .ToString("x16")); @@ -53,11 +53,11 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result == MakeError(ErrorModule.Kernel, KernelErr.Timeout) || Result == MakeError(ErrorModule.Kernel, KernelErr.Cancelled)) { - Device.Log.PrintDebug(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintDebug(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } else { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } } @@ -69,13 +69,13 @@ namespace Ryujinx.HLE.HOS.Kernel { int ThreadHandle = (int)ThreadState.X0; - Device.Log.PrintDebug(LogClass.KernelSvc, "ThreadHandle = 0x" + ThreadHandle.ToString("x8")); + Logger.PrintDebug(LogClass.KernelSvc, "ThreadHandle = 0x" + ThreadHandle.ToString("x8")); KThread Thread = Process.HandleTable.GetKThread(ThreadHandle); if (Thread == null) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{ThreadHandle:x8}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid thread handle 0x{ThreadHandle:x8}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidHandle); @@ -93,14 +93,14 @@ namespace Ryujinx.HLE.HOS.Kernel long MutexAddress = (long)ThreadState.X1; int RequesterHandle = (int)ThreadState.X2; - Device.Log.PrintDebug(LogClass.KernelSvc, + Logger.PrintDebug(LogClass.KernelSvc, "OwnerHandle = 0x" + OwnerHandle .ToString("x8") + ", " + "MutexAddress = 0x" + MutexAddress .ToString("x16") + ", " + "RequesterHandle = 0x" + RequesterHandle.ToString("x8")); if (IsPointingInsideKernel(MutexAddress)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -109,7 +109,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (IsAddressNotWordAligned(MutexAddress)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -125,7 +125,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } ThreadState.X0 = (ulong)Result; @@ -135,11 +135,11 @@ namespace Ryujinx.HLE.HOS.Kernel { long MutexAddress = (long)ThreadState.X0; - Device.Log.PrintDebug(LogClass.KernelSvc, "MutexAddress = 0x" + MutexAddress.ToString("x16")); + Logger.PrintDebug(LogClass.KernelSvc, "MutexAddress = 0x" + MutexAddress.ToString("x16")); if (IsPointingInsideKernel(MutexAddress)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -148,7 +148,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (IsAddressNotWordAligned(MutexAddress)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -159,7 +159,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } ThreadState.X0 = (ulong)Result; @@ -172,7 +172,7 @@ namespace Ryujinx.HLE.HOS.Kernel int ThreadHandle = (int)ThreadState.X2; long Timeout = (long)ThreadState.X3; - Device.Log.PrintDebug(LogClass.KernelSvc, + Logger.PrintDebug(LogClass.KernelSvc, "MutexAddress = 0x" + MutexAddress .ToString("x16") + ", " + "CondVarAddress = 0x" + CondVarAddress.ToString("x16") + ", " + "ThreadHandle = 0x" + ThreadHandle .ToString("x8") + ", " + @@ -180,7 +180,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (IsPointingInsideKernel(MutexAddress)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid mutex address 0x{MutexAddress:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -189,7 +189,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (IsAddressNotWordAligned(MutexAddress)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned mutex address 0x{MutexAddress:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -207,11 +207,11 @@ namespace Ryujinx.HLE.HOS.Kernel { if (Result == MakeError(ErrorModule.Kernel, KernelErr.Timeout)) { - Device.Log.PrintDebug(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintDebug(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } else { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } } @@ -223,7 +223,7 @@ namespace Ryujinx.HLE.HOS.Kernel long Address = (long)ThreadState.X0; int Count = (int)ThreadState.X1; - Device.Log.PrintDebug(LogClass.KernelSvc, + Logger.PrintDebug(LogClass.KernelSvc, "Address = 0x" + Address.ToString("x16") + ", " + "Count = 0x" + Count .ToString("x8")); @@ -239,7 +239,7 @@ namespace Ryujinx.HLE.HOS.Kernel int Value = (int)ThreadState.X2; long Timeout = (long)ThreadState.X3; - Device.Log.PrintDebug(LogClass.KernelSvc, + Logger.PrintDebug(LogClass.KernelSvc, "Address = 0x" + Address.ToString("x16") + ", " + "Type = " + Type .ToString() + ", " + "Value = 0x" + Value .ToString("x8") + ", " + @@ -247,7 +247,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (IsPointingInsideKernel(Address)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -256,7 +256,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (IsAddressNotWordAligned(Address)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -286,7 +286,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } ThreadState.X0 = (ulong)Result; @@ -299,7 +299,7 @@ namespace Ryujinx.HLE.HOS.Kernel int Value = (int)ThreadState.X2; int Count = (int)ThreadState.X3; - Device.Log.PrintDebug(LogClass.KernelSvc, + Logger.PrintDebug(LogClass.KernelSvc, "Address = 0x" + Address.ToString("x16") + ", " + "Type = " + Type .ToString() + ", " + "Value = 0x" + Value .ToString("x8") + ", " + @@ -307,7 +307,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (IsPointingInsideKernel(Address)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Invalid address 0x{Address:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.NoAccessPerm); @@ -316,7 +316,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (IsAddressNotWordAligned(Address)) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Unaligned address 0x{Address:x16}!"); ThreadState.X0 = MakeError(ErrorModule.Kernel, KernelErr.InvalidAddress); @@ -346,7 +346,7 @@ namespace Ryujinx.HLE.HOS.Kernel if (Result != 0) { - Device.Log.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); + Logger.PrintWarning(LogClass.KernelSvc, $"Operation failed with error 0x{Result:x}!"); } ThreadState.X0 = (ulong)Result; diff --git a/Ryujinx.HLE/HOS/Process.cs b/Ryujinx.HLE/HOS/Process.cs index ab0ab18ba1..448f1afac8 100644 --- a/Ryujinx.HLE/HOS/Process.cs +++ b/Ryujinx.HLE/HOS/Process.cs @@ -3,6 +3,7 @@ using ChocolArm64.Events; using ChocolArm64.Memory; using ChocolArm64.State; using LibHac; +using Ryujinx.Common.Logging; using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.HOS.Diagnostics.Demangler; using Ryujinx.HLE.HOS.Kernel; @@ -11,7 +12,6 @@ using Ryujinx.HLE.HOS.SystemState; using Ryujinx.HLE.Loaders; using Ryujinx.HLE.Loaders.Executables; using Ryujinx.HLE.Loaders.Npdm; -using Ryujinx.HLE.Logging; using Ryujinx.HLE.Utilities; using System; using System.Collections.Concurrent; @@ -118,7 +118,7 @@ namespace Ryujinx.HLE.HOS throw new ObjectDisposedException(nameof(Process)); } - Device.Log.PrintInfo(LogClass.Loader, $"Image base at 0x{ExecutableBase:x16}."); + Logger.PrintInfo(LogClass.Loader, $"Image base at 0x{ExecutableBase:x16}."); Executable Executable = new Executable(Program, MemoryManager, Memory, ExecutableBase); @@ -319,7 +319,7 @@ namespace Ryujinx.HLE.HOS string ExeNameWithAddr = $"{Exe.Name}:0x{Offset:x8}"; - Device.Log.PrintDebug(LogClass.Cpu, ExeNameWithAddr + " " + SubName); + Logger.PrintDebug(LogClass.Cpu, ExeNameWithAddr + " " + SubName); } private ATranslator GetTranslator() @@ -374,7 +374,7 @@ namespace Ryujinx.HLE.HOS FramePointer = Memory.ReadInt64(FramePointer); } - Device.Log.PrintInfo(LogClass.Cpu, Trace.ToString()); + Logger.PrintInfo(LogClass.Cpu, Trace.ToString()); } private bool TryGetSubName(Executable Exe, long Position, out string Name) @@ -475,7 +475,7 @@ namespace Ryujinx.HLE.HOS File.Delete(Executables[0].FilePath); } - Device.Log.PrintInfo(LogClass.Loader, $"Process {ProcessId} exiting..."); + Logger.PrintInfo(LogClass.Loader, $"Process {ProcessId} exiting..."); } public void Dispose() diff --git a/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs b/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs index d866a85359..f920c00ba0 100644 --- a/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs +++ b/Ryujinx.HLE/HOS/Services/Acc/IAccountService.cs @@ -1,6 +1,6 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.SystemState; -using Ryujinx.HLE.Logging; using Ryujinx.HLE.Utilities; using System.Collections.Generic; @@ -103,7 +103,7 @@ namespace Ryujinx.HLE.HOS.Services.Acc if (!Context.Device.System.State.TryGetUser(Uuid, out UserProfile Profile)) { - Context.Device.Log.PrintWarning(LogClass.ServiceAcc, $"User 0x{Uuid} not found!"); + Logger.PrintWarning(LogClass.ServiceAcc, $"User 0x{Uuid} not found!"); return MakeError(ErrorModule.Account, AccErr.UserNotFound); } @@ -118,7 +118,7 @@ namespace Ryujinx.HLE.HOS.Services.Acc { long Unknown = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceAcc, $"Stubbed. Unknown: {Unknown}"); + Logger.PrintStub(LogClass.ServiceAcc, $"Stubbed. Unknown: {Unknown}"); Context.ResponseData.Write(false); @@ -130,7 +130,7 @@ namespace Ryujinx.HLE.HOS.Services.Acc { bool Unknown = Context.RequestData.ReadBoolean(); - Context.Device.Log.PrintStub(LogClass.ServiceAcc, $"Stubbed. Unknown: {Unknown}"); + Logger.PrintStub(LogClass.ServiceAcc, $"Stubbed. Unknown: {Unknown}"); UserProfile Profile = Context.Device.System.State.LastOpenUser; @@ -144,7 +144,7 @@ namespace Ryujinx.HLE.HOS.Services.Acc { long Unknown = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceAcc, $"Stubbed. Unknown: {Unknown}"); + Logger.PrintStub(LogClass.ServiceAcc, $"Stubbed. Unknown: {Unknown}"); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Acc/IManagerForApplication.cs b/Ryujinx.HLE/HOS/Services/Acc/IManagerForApplication.cs index ed0e6efb69..9312b2bc82 100644 --- a/Ryujinx.HLE/HOS/Services/Acc/IManagerForApplication.cs +++ b/Ryujinx.HLE/HOS/Services/Acc/IManagerForApplication.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using Ryujinx.HLE.Utilities; using System.Collections.Generic; @@ -27,7 +27,7 @@ namespace Ryujinx.HLE.HOS.Services.Acc // CheckAvailability() public long CheckAvailability(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAcc, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAcc, "Stubbed."); return 0; } @@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.Acc { long NetworkServiceAccountId = 0xcafe; - Context.Device.Log.PrintStub(LogClass.ServiceAcc, $"Stubbed. NetworkServiceAccountId: {NetworkServiceAccountId}"); + Logger.PrintStub(LogClass.ServiceAcc, $"Stubbed. NetworkServiceAccountId: {NetworkServiceAccountId}"); Context.ResponseData.Write(NetworkServiceAccountId); diff --git a/Ryujinx.HLE/HOS/Services/Acc/IProfile.cs b/Ryujinx.HLE/HOS/Services/Acc/IProfile.cs index f68c819178..1776b37b2c 100644 --- a/Ryujinx.HLE/HOS/Services/Acc/IProfile.cs +++ b/Ryujinx.HLE/HOS/Services/Acc/IProfile.cs @@ -1,7 +1,7 @@ using ChocolArm64.Memory; +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.SystemState; -using Ryujinx.HLE.Logging; using Ryujinx.HLE.Utilities; using System.Collections.Generic; using System.IO; @@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.Acc public long Get(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAcc, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAcc, "Stubbed."); long Position = Context.Request.ReceiveBuff[0].Position; diff --git a/Ryujinx.HLE/HOS/Services/Am/IApplicationFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/IApplicationFunctions.cs index 0a10d2a66c..1934798b2d 100644 --- a/Ryujinx.HLE/HOS/Services/Am/IApplicationFunctions.cs +++ b/Ryujinx.HLE/HOS/Services/Am/IApplicationFunctions.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Am @@ -39,7 +39,7 @@ namespace Ryujinx.HLE.HOS.Services.Am long UIdLow = Context.RequestData.ReadInt64(); long UIdHigh = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); Context.ResponseData.Write(0L); @@ -59,7 +59,7 @@ namespace Ryujinx.HLE.HOS.Services.Am string Result = GetFormattedErrorCode(ErrorCode); - Context.Device.Log.PrintInfo(LogClass.ServiceAm, $"Result = 0x{ErrorCode:x8} ({Result})."); + Logger.PrintInfo(LogClass.ServiceAm, $"Result = 0x{ErrorCode:x8} ({Result})."); return 0; } @@ -90,7 +90,7 @@ namespace Ryujinx.HLE.HOS.Services.Am public long GetPseudoDeviceId(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); Context.ResponseData.Write(0L); Context.ResponseData.Write(0L); @@ -100,7 +100,7 @@ namespace Ryujinx.HLE.HOS.Services.Am public long InitializeGamePlayRecording(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -109,7 +109,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { int State = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Am/IAudioController.cs b/Ryujinx.HLE/HOS/Services/Am/IAudioController.cs index 8968ad72c3..062f2d8651 100644 --- a/Ryujinx.HLE/HOS/Services/Am/IAudioController.cs +++ b/Ryujinx.HLE/HOS/Services/Am/IAudioController.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Am @@ -27,7 +27,7 @@ namespace Ryujinx.HLE.HOS.Services.Am float AppletVolume = Context.RequestData.ReadSingle(); float LibraryAppletVolume = Context.RequestData.ReadSingle(); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -36,7 +36,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { Context.ResponseData.Write(1f); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -45,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { Context.ResponseData.Write(1f); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -55,7 +55,7 @@ namespace Ryujinx.HLE.HOS.Services.Am float Unknown0 = Context.RequestData.ReadSingle(); long Unknown1 = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -64,7 +64,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { float Unknown0 = Context.RequestData.ReadSingle(); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs b/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs index 4ea18d3298..6b012689c4 100644 --- a/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs +++ b/Ryujinx.HLE/HOS/Services/Am/ICommonStateGetter.cs @@ -1,6 +1,6 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; @@ -85,7 +85,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { Context.ResponseData.Write((byte)0); //Unknown value. - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -114,7 +114,7 @@ namespace Ryujinx.HLE.HOS.Services.Am Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Am/IHomeMenuFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/IHomeMenuFunctions.cs index a476aff97a..3f026e2fe6 100644 --- a/Ryujinx.HLE/HOS/Services/Am/IHomeMenuFunctions.cs +++ b/Ryujinx.HLE/HOS/Services/Am/IHomeMenuFunctions.cs @@ -1,6 +1,6 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; @@ -28,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Services.Am public long RequestToGetForeground(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -42,7 +42,7 @@ namespace Ryujinx.HLE.HOS.Services.Am Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Am/ILibraryAppletAccessor.cs b/Ryujinx.HLE/HOS/Services/Am/ILibraryAppletAccessor.cs index 07b8d97108..9e0d0e7075 100644 --- a/Ryujinx.HLE/HOS/Services/Am/ILibraryAppletAccessor.cs +++ b/Ryujinx.HLE/HOS/Services/Am/ILibraryAppletAccessor.cs @@ -1,6 +1,6 @@ -using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; @@ -39,28 +39,28 @@ namespace Ryujinx.HLE.HOS.Services.Am Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } public long Start(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } public long GetResult(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } public long PushInData(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Am/ISelfController.cs b/Ryujinx.HLE/HOS/Services/Am/ISelfController.cs index ef69f598c6..2abaee2e7b 100644 --- a/Ryujinx.HLE/HOS/Services/Am/ISelfController.cs +++ b/Ryujinx.HLE/HOS/Services/Am/ISelfController.cs @@ -1,6 +1,6 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; @@ -41,21 +41,21 @@ namespace Ryujinx.HLE.HOS.Services.Am public long Exit(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } public long LockExit(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } public long UnlockExit(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -71,7 +71,7 @@ namespace Ryujinx.HLE.HOS.Services.Am Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { bool Enable = Context.RequestData.ReadByte() != 0 ? true : false; - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -89,7 +89,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { bool Enable = Context.RequestData.ReadByte() != 0 ? true : false; - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -98,7 +98,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { bool Enable = Context.RequestData.ReadByte() != 0 ? true : false; - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -109,7 +109,7 @@ namespace Ryujinx.HLE.HOS.Services.Am bool Flag2 = Context.RequestData.ReadByte() != 0 ? true : false; bool Flag3 = Context.RequestData.ReadByte() != 0 ? true : false; - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -118,7 +118,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { bool Enable = Context.RequestData.ReadByte() != 0 ? true : false; - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -127,7 +127,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { bool Enable = Context.RequestData.ReadByte() != 0 ? true : false; - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -136,7 +136,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { int Orientation = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -145,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { bool Enable = Context.RequestData.ReadByte() != 0 ? true : false; - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } @@ -155,7 +155,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { IdleTimeDetectionExtension = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceAm, $"Stubbed. IdleTimeDetectionExtension: {IdleTimeDetectionExtension}"); + Logger.PrintStub(LogClass.ServiceAm, $"Stubbed. IdleTimeDetectionExtension: {IdleTimeDetectionExtension}"); return 0; } @@ -165,7 +165,7 @@ namespace Ryujinx.HLE.HOS.Services.Am { Context.ResponseData.Write(IdleTimeDetectionExtension); - Context.Device.Log.PrintStub(LogClass.ServiceAm, $"Stubbed. IdleTimeDetectionExtension: {IdleTimeDetectionExtension}"); + Logger.PrintStub(LogClass.ServiceAm, $"Stubbed. IdleTimeDetectionExtension: {IdleTimeDetectionExtension}"); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Am/IWindowController.cs b/Ryujinx.HLE/HOS/Services/Am/IWindowController.cs index 1a5a716f9a..de5137d120 100644 --- a/Ryujinx.HLE/HOS/Services/Am/IWindowController.cs +++ b/Ryujinx.HLE/HOS/Services/Am/IWindowController.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Am @@ -21,7 +21,7 @@ namespace Ryujinx.HLE.HOS.Services.Am public long GetAppletResourceUserId(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); Context.ResponseData.Write(0L); @@ -30,7 +30,7 @@ namespace Ryujinx.HLE.HOS.Services.Am public long AcquireForegroundRights(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAm, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Apm/ISession.cs b/Ryujinx.HLE/HOS/Services/Apm/ISession.cs index 739e264d58..d04bcfc973 100644 --- a/Ryujinx.HLE/HOS/Services/Apm/ISession.cs +++ b/Ryujinx.HLE/HOS/Services/Apm/ISession.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Apm @@ -33,7 +33,7 @@ namespace Ryujinx.HLE.HOS.Services.Apm Context.ResponseData.Write((uint)PerformanceConfiguration.PerformanceConfiguration1); - Context.Device.Log.PrintStub(LogClass.ServiceApm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceApm, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs b/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs index 13225951e1..7963cbb47d 100644 --- a/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs +++ b/Ryujinx.HLE/HOS/Services/Aud/AudioRenderer/IAudioRenderer.cs @@ -1,9 +1,9 @@ using ChocolArm64.Memory; using Ryujinx.Audio; using Ryujinx.Audio.Adpcm; +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.Logging; using Ryujinx.HLE.Utilities; using System; using System.Collections.Generic; @@ -107,7 +107,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer { Context.ResponseData.Write((int)PlayState); - Context.Device.Log.PrintStub(LogClass.ServiceAudio, $"Stubbed. Renderer State: {Enum.GetName(typeof(PlayState), PlayState)}"); + Logger.PrintStub(LogClass.ServiceAudio, $"Stubbed. Renderer State: {Enum.GetName(typeof(PlayState), PlayState)}"); return 0; } @@ -246,7 +246,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer public long StartAudioRenderer(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); PlayState = PlayState.Playing; @@ -255,7 +255,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud.AudioRenderer public long StopAudioRenderer(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); PlayState = PlayState.Stopped; diff --git a/Ryujinx.HLE/HOS/Services/Aud/IAudioDevice.cs b/Ryujinx.HLE/HOS/Services/Aud/IAudioDevice.cs index a1a228ed1e..f9c0d315a3 100644 --- a/Ryujinx.HLE/HOS/Services/Aud/IAudioDevice.cs +++ b/Ryujinx.HLE/HOS/Services/Aud/IAudioDevice.cs @@ -1,7 +1,7 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.SystemState; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; using System.Text; @@ -56,7 +56,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud if ((Position - BasePosition) + Buffer.Length > Size) { - Context.Device.Log.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!"); + Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!"); break; } @@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud string DeviceName = Encoding.ASCII.GetString(DeviceNameBuffer); - Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); return 0; } @@ -100,7 +100,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud } else { - Context.Device.Log.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!"); + Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!"); } return 0; @@ -115,7 +115,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); - Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); return 0; } @@ -124,7 +124,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud { Context.ResponseData.Write(2); - Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); return 0; } @@ -145,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud if ((Position - BasePosition) + Buffer.Length > Size) { - Context.Device.Log.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!"); + Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!"); break; } @@ -168,7 +168,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud string DeviceName = Encoding.UTF8.GetString(DeviceNameBuffer); - Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); return 0; } @@ -177,7 +177,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud { Context.ResponseData.Write(1f); - Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); return 0; } @@ -196,7 +196,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud } else { - Context.Device.Log.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!"); + Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!"); } return 0; @@ -211,7 +211,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); - Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); return 0; } @@ -225,7 +225,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); - Context.Device.Log.PrintStub(LogClass.ServiceAudio, "Stubbed."); + Logger.PrintStub(LogClass.ServiceAudio, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Aud/IAudioOutManager.cs b/Ryujinx.HLE/HOS/Services/Aud/IAudioOutManager.cs index 44b856cda6..2bc2d82068 100644 --- a/Ryujinx.HLE/HOS/Services/Aud/IAudioOutManager.cs +++ b/Ryujinx.HLE/HOS/Services/Aud/IAudioOutManager.cs @@ -1,9 +1,9 @@ using ChocolArm64.Memory; using Ryujinx.Audio; +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Services.Aud.AudioOut; -using Ryujinx.HLE.Logging; using System.Collections.Generic; using System.Text; @@ -86,7 +86,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud } else { - Context.Device.Log.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!"); + Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {Size} too small!"); } Context.ResponseData.Write(NameCount); @@ -108,7 +108,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud if (DeviceName != DefaultAudioOutput) { - Context.Device.Log.PrintWarning(LogClass.Audio, "Invalid device name!"); + Logger.PrintWarning(LogClass.Audio, "Invalid device name!"); return MakeError(ErrorModule.Audio, AudErr.DeviceNotFound); } @@ -121,7 +121,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud } else { - Context.Device.Log.PrintError(LogClass.ServiceAudio, $"Output buffer size {ReceiveSize} too small!"); + Logger.PrintError(LogClass.ServiceAudio, $"Output buffer size {ReceiveSize} too small!"); } int SampleRate = Context.RequestData.ReadInt32(); @@ -134,7 +134,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud if (SampleRate != DefaultSampleRate) { - Context.Device.Log.PrintWarning(LogClass.Audio, "Invalid sample rate!"); + Logger.PrintWarning(LogClass.Audio, "Invalid sample rate!"); return MakeError(ErrorModule.Audio, AudErr.UnsupportedSampleRate); } diff --git a/Ryujinx.HLE/HOS/Services/Aud/IAudioRendererManager.cs b/Ryujinx.HLE/HOS/Services/Aud/IAudioRendererManager.cs index 7859e0135d..49bbbd5a8d 100644 --- a/Ryujinx.HLE/HOS/Services/Aud/IAudioRendererManager.cs +++ b/Ryujinx.HLE/HOS/Services/Aud/IAudioRendererManager.cs @@ -1,7 +1,7 @@ using Ryujinx.Audio; +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Services.Aud.AudioRenderer; -using Ryujinx.HLE.Logging; using Ryujinx.HLE.Utilities; using System; using System.Collections.Generic; @@ -104,7 +104,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud Context.ResponseData.Write(Size); - Context.Device.Log.PrintDebug(LogClass.ServiceAudio, $"WorkBufferSize is 0x{Size:x16}."); + Logger.PrintDebug(LogClass.ServiceAudio, $"WorkBufferSize is 0x{Size:x16}."); return 0; } @@ -112,7 +112,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud { Context.ResponseData.Write(0L); - Context.Device.Log.PrintWarning(LogClass.ServiceAudio, $"Library Revision 0x{Params.Revision:x8} is not supported!"); + Logger.PrintWarning(LogClass.ServiceAudio, $"Library Revision 0x{Params.Revision:x8} is not supported!"); return MakeError(ErrorModule.Audio, AudErr.UnsupportedRevision); } @@ -179,7 +179,7 @@ namespace Ryujinx.HLE.HOS.Services.Aud long AppletResourceUserId = Context.RequestData.ReadInt64(); int RevisionInfo = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceAudio, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + Logger.PrintStub(LogClass.ServiceAudio, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + $"RevisionInfo: {RevisionInfo}"); return GetAudioDeviceService(Context); diff --git a/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs b/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs index ebbe5fca03..e20de267ff 100644 --- a/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs +++ b/Ryujinx.HLE/HOS/Services/Friend/IFriendService.cs @@ -1,6 +1,6 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.SystemState; -using Ryujinx.HLE.Logging; using Ryujinx.HLE.Utilities; using System.Collections.Generic; @@ -46,15 +46,15 @@ namespace Ryujinx.HLE.HOS.Services.Friend // There are no friends online, so we return 0 because the nn::account::NetworkServiceAccountId array is empty. Context.ResponseData.Write(0); - Context.Device.Log.PrintStub(LogClass.ServiceFriend, $"Stubbed. UserId: {Uuid.ToString()} - " + - $"Unknown0: {Unknown0} - " + - $"PresenceStatus: {Filter.PresenceStatus} - " + - $"IsFavoriteOnly: {Filter.IsFavoriteOnly} - " + - $"IsSameAppPresenceOnly: {Filter.IsSameAppPresenceOnly} - " + - $"IsSameAppPlayedOnly: {Filter.IsSameAppPlayedOnly} - " + - $"IsArbitraryAppPlayedOnly: {Filter.IsArbitraryAppPlayedOnly} - " + - $"PresenceGroupId: {Filter.PresenceGroupId} - " + - $"Unknown1: {Unknown1}"); + Logger.PrintStub(LogClass.ServiceFriend, $"Stubbed. UserId: {Uuid.ToString()} - " + + $"Unknown0: {Unknown0} - " + + $"PresenceStatus: {Filter.PresenceStatus} - " + + $"IsFavoriteOnly: {Filter.IsFavoriteOnly} - " + + $"IsSameAppPresenceOnly: {Filter.IsSameAppPresenceOnly} - " + + $"IsSameAppPlayedOnly: {Filter.IsSameAppPlayedOnly} - " + + $"IsArbitraryAppPlayedOnly: {Filter.IsArbitraryAppPlayedOnly} - " + + $"PresenceGroupId: {Filter.PresenceGroupId} - " + + $"Unknown1: {Unknown1}"); return 0; } @@ -71,8 +71,8 @@ namespace Ryujinx.HLE.HOS.Services.Friend Profile.OnlinePlayState = OpenCloseState.Closed; } - Context.Device.Log.PrintStub(LogClass.ServiceFriend, $"Stubbed. Uuid: {Uuid.ToString()} - " + - $"OnlinePlayState: {Profile.OnlinePlayState}"); + Logger.PrintStub(LogClass.ServiceFriend, $"Stubbed. Uuid: {Uuid.ToString()} - " + + $"OnlinePlayState: {Profile.OnlinePlayState}"); return 0; } @@ -91,8 +91,8 @@ namespace Ryujinx.HLE.HOS.Services.Friend //Todo: Write the buffer content. - Context.Device.Log.PrintStub(LogClass.ServiceFriend, $"Stubbed. Uuid: {Uuid.ToString()} - " + - $"Unknown0: {Unknown0}"); + Logger.PrintStub(LogClass.ServiceFriend, $"Stubbed. Uuid: {Uuid.ToString()} - " + + $"Unknown0: {Unknown0}"); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs index 0202bd3084..e54ca8125d 100644 --- a/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs +++ b/Ryujinx.HLE/HOS/Services/Hid/IHidServer.cs @@ -1,7 +1,7 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.Input; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; @@ -178,7 +178,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } @@ -188,7 +188,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } @@ -198,7 +198,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } @@ -208,7 +208,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } @@ -225,7 +225,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(XpadIdEventHandle); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. XpadId: {XpadId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. XpadId: {XpadId}"); return 0; } @@ -237,7 +237,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.Process.HandleTable.CloseHandle(XpadIdEventHandle); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. XpadId: {XpadId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. XpadId: {XpadId}"); return 0; } @@ -248,8 +248,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid int BasicXpadId = Context.RequestData.ReadInt32(); long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"BasicXpadId: {BasicXpadId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"BasicXpadId: {BasicXpadId}"); return 0; } @@ -260,7 +260,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid // There is any Xpad, so we return 0 and write nothing inside the type-0xa buffer. Context.ResponseData.Write(0L); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed."); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed."); return 0; } @@ -270,7 +270,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { int JoyXpadId = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. JoyXpadId: {JoyXpadId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. JoyXpadId: {JoyXpadId}"); return 0; } @@ -284,7 +284,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. JoyXpadId: {JoyXpadId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. JoyXpadId: {JoyXpadId}"); return 0; } @@ -295,7 +295,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid // There is any JoyXpad, so we return 0 and write nothing inside the type-0xa buffer. Context.ResponseData.Write(0L); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed."); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed."); return 0; } @@ -305,7 +305,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { int BasicXpadId = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. BasicXpadId: {BasicXpadId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. BasicXpadId: {BasicXpadId}"); return 0; } @@ -315,7 +315,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { int BasicXpadId = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. BasicXpadId: {BasicXpadId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. BasicXpadId: {BasicXpadId}"); return 0; } @@ -329,7 +329,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. BasicXpadId: {BasicXpadId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. BasicXpadId: {BasicXpadId}"); return 0; } @@ -339,7 +339,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { int JoyXpadId = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. JoyXpadId: {JoyXpadId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. JoyXpadId: {JoyXpadId}"); return 0; } @@ -349,7 +349,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { int JoyXpadId = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. JoyXpadId: {JoyXpadId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. JoyXpadId: {JoyXpadId}"); return 0; } @@ -363,7 +363,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. JoyXpadId: {JoyXpadId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. JoyXpadId: {JoyXpadId}"); return 0; } @@ -374,8 +374,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid int SixAxisSensorHandle = Context.RequestData.ReadInt32(); long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"SixAxisSensorHandle: {SixAxisSensorHandle}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle}"); return 0; } @@ -386,8 +386,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid int SixAxisSensorHandle = Context.RequestData.ReadInt32(); long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"SixAxisSensorHandle: {SixAxisSensorHandle}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle}"); return 0; } @@ -400,9 +400,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write(SixAxisSensorFusionEnabled); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + - $"SixAxisSensorFusionEnabled: {SixAxisSensorFusionEnabled}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"SixAxisSensorFusionEnabled: {SixAxisSensorFusionEnabled}"); return 0; } @@ -414,9 +414,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid int SixAxisSensorHandle = Context.RequestData.ReadInt32(); long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + - $"SixAxisSensorFusionEnabled: {SixAxisSensorFusionEnabled}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"SixAxisSensorFusionEnabled: {SixAxisSensorFusionEnabled}"); return 0; } @@ -434,10 +434,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + - $"RevisePower: {SensorFusionParams.RevisePower} - " + - $"ReviseRange: {SensorFusionParams.ReviseRange}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"RevisePower: {SensorFusionParams.RevisePower} - " + + $"ReviseRange: {SensorFusionParams.ReviseRange}"); return 0; } @@ -451,10 +451,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write(SensorFusionParams.RevisePower); Context.ResponseData.Write(SensorFusionParams.ReviseRange); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + - $"RevisePower: {SensorFusionParams.RevisePower} - " + - $"ReviseRange: {SensorFusionParams.ReviseRange}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"RevisePower: {SensorFusionParams.RevisePower} - " + + $"ReviseRange: {SensorFusionParams.ReviseRange}"); return 0; } @@ -468,10 +468,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid SensorFusionParams.RevisePower = 0; SensorFusionParams.ReviseRange = 0; - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + - $"RevisePower: {SensorFusionParams.RevisePower} - " + - $"ReviseRange: {SensorFusionParams.ReviseRange}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"RevisePower: {SensorFusionParams.RevisePower} - " + + $"ReviseRange: {SensorFusionParams.ReviseRange}"); return 0; } @@ -489,10 +489,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + - $"X: {AccelerometerParams.X} - " + - $"Y: {AccelerometerParams.Y}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"X: {AccelerometerParams.X} - " + + $"Y: {AccelerometerParams.Y}"); return 0; } @@ -506,10 +506,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write(AccelerometerParams.X); Context.ResponseData.Write(AccelerometerParams.Y); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + - $"X: {AccelerometerParams.X} - " + - $"Y: {AccelerometerParams.Y}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"X: {AccelerometerParams.X} - " + + $"Y: {AccelerometerParams.Y}"); return 0; } @@ -523,10 +523,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid AccelerometerParams.X = 0; AccelerometerParams.Y = 0; - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + - $"X: {AccelerometerParams.X} - " + - $"Y: {AccelerometerParams.Y}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"X: {AccelerometerParams.X} - " + + $"Y: {AccelerometerParams.Y}"); return 0; } @@ -538,9 +538,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid AccelerometerPlayMode = Context.RequestData.ReadUInt32(); long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + - $"PlayMode: {AccelerometerPlayMode}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"PlayMode: {AccelerometerPlayMode}"); return 0; } @@ -553,9 +553,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write(AccelerometerPlayMode); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + - $"PlayMode: {AccelerometerPlayMode}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"PlayMode: {AccelerometerPlayMode}"); return 0; } @@ -568,9 +568,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid AccelerometerPlayMode = 0; - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + - $"PlayMode: {AccelerometerPlayMode}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"PlayMode: {AccelerometerPlayMode}"); return 0; } @@ -582,9 +582,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid GyroscopeZeroDriftMode = (HidGyroscopeZeroDriftMode)Context.RequestData.ReadInt32(); long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + - $"GyroscopeZeroDriftMode: {GyroscopeZeroDriftMode}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"GyroscopeZeroDriftMode: {GyroscopeZeroDriftMode}"); return 0; } @@ -597,9 +597,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write((int)GyroscopeZeroDriftMode); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + - $"GyroscopeZeroDriftMode: {GyroscopeZeroDriftMode}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"GyroscopeZeroDriftMode: {GyroscopeZeroDriftMode}"); return 0; } @@ -612,9 +612,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid GyroscopeZeroDriftMode = HidGyroscopeZeroDriftMode.Standard; - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + - $"GyroscopeZeroDriftMode: {GyroscopeZeroDriftMode}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"GyroscopeZeroDriftMode: {GyroscopeZeroDriftMode}"); return 0; } @@ -629,9 +629,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write(IsAtRest); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + - $"IsAtRest: {IsAtRest}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SixAxisSensorHandle: {SixAxisSensorHandle} - " + + $"IsAtRest: {IsAtRest}"); return 0; } @@ -642,8 +642,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid long AppletResourceUserId = Context.RequestData.ReadInt64(); int Unknown0 = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"Unknown0: {Unknown0}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"Unknown0: {Unknown0}"); return 0; } @@ -656,8 +656,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"NpadStyleTag: {NpadStyleTag}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadStyleTag: {NpadStyleTag}"); return 0; } @@ -669,8 +669,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write((int)NpadStyleTag); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"NpadStyleTag: {NpadStyleTag}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadStyleTag: {NpadStyleTag}"); return 0; } @@ -681,8 +681,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid long AppletResourceUserId = Context.RequestData.ReadInt64(); HidControllerId NpadIdType = (HidControllerId)Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"NpadIdType: {NpadIdType}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadIdType: {NpadIdType}"); return 0; } @@ -692,7 +692,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } @@ -702,7 +702,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } @@ -721,9 +721,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"NpadId: {NpadId} - " + - $"NpadStyleSet: {NpadStyleSet}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadId: {NpadId} - " + + $"NpadStyleSet: {NpadStyleSet}"); return 0; } @@ -734,8 +734,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid long AppletResourceUserId = Context.RequestData.ReadInt64(); int NpadIdType = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"NpadIdType: {NpadIdType}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadIdType: {NpadIdType}"); return 0; } @@ -749,7 +749,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write(LedPattern); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. NpadId: {NpadId} - Pattern: {LedPattern}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. NpadId: {NpadId} - Pattern: {LedPattern}"); return 0; } @@ -760,7 +760,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid long AppletResourceUserId = Context.RequestData.ReadInt64(); int Unknown = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - Unknown: {Unknown}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - Unknown: {Unknown}"); return 0; } @@ -771,8 +771,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid long AppletResourceUserId = Context.RequestData.ReadInt64(); NpadJoyHoldType = (HidNpadJoyHoldType)Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"NpadJoyHoldType: {NpadJoyHoldType}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadJoyHoldType: {NpadJoyHoldType}"); return 0; } @@ -784,8 +784,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write((long)NpadJoyHoldType); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"NpadJoyHoldTypeValue: {NpadJoyHoldType}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadJoyHoldTypeValue: {NpadJoyHoldType}"); return 0; } @@ -798,9 +798,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid NpadJoyAssignmentMode = HidNpadJoyAssignmentMode.Single; - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"HidControllerId: {HidControllerId} - " + - $"NpadJoyAssignmentModeValue: {NpadJoyAssignmentMode}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"HidControllerId: {HidControllerId} - " + + $"NpadJoyAssignmentModeValue: {NpadJoyAssignmentMode}"); return 0; } @@ -814,10 +814,10 @@ namespace Ryujinx.HLE.HOS.Services.Hid NpadJoyAssignmentMode = HidNpadJoyAssignmentMode.Single; - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"HidControllerId: {HidControllerId} - " + - $"HidNpadJoyDeviceType: {HidNpadJoyDeviceType} - " + - $"NpadJoyAssignmentModeValue: {NpadJoyAssignmentMode}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"HidControllerId: {HidControllerId} - " + + $"HidNpadJoyDeviceType: {HidNpadJoyDeviceType} - " + + $"NpadJoyAssignmentModeValue: {NpadJoyAssignmentMode}"); return 0; } @@ -830,9 +830,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid NpadJoyAssignmentMode = HidNpadJoyAssignmentMode.Dual; - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"HidControllerId: {HidControllerId} - " + - $"NpadJoyAssignmentModeValue: {NpadJoyAssignmentMode}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"HidControllerId: {HidControllerId} - " + + $"NpadJoyAssignmentModeValue: {NpadJoyAssignmentMode}"); return 0; } @@ -844,9 +844,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid long SingleJoyId1 = Context.RequestData.ReadInt32(); long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"SingleJoyId0: {SingleJoyId0} - " + - $"SingleJoyId1: {SingleJoyId1}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SingleJoyId0: {SingleJoyId0} - " + + $"SingleJoyId1: {SingleJoyId1}"); return 0; } @@ -856,7 +856,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } @@ -866,7 +866,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } @@ -877,8 +877,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid long AppletResourceUserId = Context.RequestData.ReadInt64(); NpadHandheldActivationMode = (HidNpadHandheldActivationMode)Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"NpadHandheldActivationMode: {NpadHandheldActivationMode}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadHandheldActivationMode: {NpadHandheldActivationMode}"); return 0; } @@ -890,8 +890,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write((long)NpadHandheldActivationMode); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"NpadHandheldActivationMode: {NpadHandheldActivationMode}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadHandheldActivationMode: {NpadHandheldActivationMode}"); return 0; } @@ -903,9 +903,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid int NewNpadAssignment = Context.RequestData.ReadInt32(); long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"OldNpadAssignment: {OldNpadAssignment} - " + - $"NewNpadAssignment: {NewNpadAssignment}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"OldNpadAssignment: {OldNpadAssignment} - " + + $"NewNpadAssignment: {NewNpadAssignment}"); return 0; } @@ -918,9 +918,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write(UnintendedHomeButtonInputProtectionEnabled); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"Unknown0: {Unknown0} - " + - $"UnintendedHomeButtonInputProtectionEnabled: {UnintendedHomeButtonInputProtectionEnabled}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"Unknown0: {Unknown0} - " + + $"UnintendedHomeButtonInputProtectionEnabled: {UnintendedHomeButtonInputProtectionEnabled}"); return 0; } @@ -932,9 +932,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid uint Unknown0 = Context.RequestData.ReadUInt32(); long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"Unknown0: {Unknown0} - " + - $"UnintendedHomeButtonInputProtectionEnable: {UnintendedHomeButtonInputProtectionEnabled}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"Unknown0: {Unknown0} - " + + $"UnintendedHomeButtonInputProtectionEnable: {UnintendedHomeButtonInputProtectionEnabled}"); return 0; } @@ -951,12 +951,12 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write(0); //Unknown0 Context.ResponseData.Write(0); //Unknown1 - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"HidControllerId: {HidControllerId} - " + - $"HidNpadJoyDeviceType: {HidNpadJoyDeviceType} - " + - $"NpadJoyAssignmentModeValue: {NpadJoyAssignmentMode} - " + - $"Unknown0: 0 - " + - $"Unknown1: 0"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"HidControllerId: {HidControllerId} - " + + $"HidNpadJoyDeviceType: {HidNpadJoyDeviceType} - " + + $"NpadJoyAssignmentModeValue: {NpadJoyAssignmentMode} - " + + $"Unknown0: 0 - " + + $"Unknown1: 0"); return 0; } @@ -975,9 +975,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write((int)DeviceInfo.DeviceType); Context.ResponseData.Write((int)DeviceInfo.Position); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. VibrationDeviceHandle: {VibrationDeviceHandle} - " + - $"DeviceType: {DeviceInfo.DeviceType} - " + - $"Position: {DeviceInfo.Position}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. VibrationDeviceHandle: {VibrationDeviceHandle} - " + + $"DeviceType: {DeviceInfo.DeviceType} - " + + $"Position: {DeviceInfo.Position}"); return 0; } @@ -997,12 +997,12 @@ namespace Ryujinx.HLE.HOS.Services.Hid long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"VibrationDeviceHandle: {VibrationDeviceHandle} - " + - $"AmplitudeLow: {VibrationValue.AmplitudeLow} - " + - $"FrequencyLow: {VibrationValue.FrequencyLow} - " + - $"AmplitudeHigh: {VibrationValue.AmplitudeHigh} - " + - $"FrequencyHigh: {VibrationValue.FrequencyHigh}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"VibrationDeviceHandle: {VibrationDeviceHandle} - " + + $"AmplitudeLow: {VibrationValue.AmplitudeLow} - " + + $"FrequencyLow: {VibrationValue.FrequencyLow} - " + + $"AmplitudeHigh: {VibrationValue.AmplitudeHigh} - " + + $"FrequencyHigh: {VibrationValue.FrequencyHigh}"); return 0; } @@ -1018,12 +1018,12 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write(VibrationValue.AmplitudeHigh); Context.ResponseData.Write(VibrationValue.FrequencyHigh); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"VibrationDeviceHandle: {VibrationDeviceHandle} - " + - $"AmplitudeLow: {VibrationValue.AmplitudeLow} - " + - $"FrequencyLow: {VibrationValue.FrequencyLow} - " + - $"AmplitudeHigh: {VibrationValue.AmplitudeHigh} - " + - $"FrequencyHigh: {VibrationValue.FrequencyHigh}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"VibrationDeviceHandle: {VibrationDeviceHandle} - " + + $"AmplitudeLow: {VibrationValue.AmplitudeLow} - " + + $"FrequencyLow: {VibrationValue.FrequencyLow} - " + + $"AmplitudeHigh: {VibrationValue.AmplitudeHigh} - " + + $"FrequencyHigh: {VibrationValue.FrequencyHigh}"); return 0; } @@ -1041,7 +1041,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { VibrationPermitted = Context.RequestData.ReadBoolean(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. VibrationPermitted: {VibrationPermitted}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. VibrationPermitted: {VibrationPermitted}"); return 0; } @@ -1051,7 +1051,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { Context.ResponseData.Write(VibrationPermitted); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. VibrationPermitted: {VibrationPermitted}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. VibrationPermitted: {VibrationPermitted}"); return 0; } @@ -1071,9 +1071,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid //Todo: Read all handles and values from buffer. - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"VibrationDeviceHandleBufferLength: {VibrationDeviceHandleBuffer.Length} - " + - $"VibrationValueBufferLength: {VibrationValueBuffer.Length}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"VibrationDeviceHandleBufferLength: {VibrationDeviceHandleBuffer.Length} - " + + $"VibrationValueBufferLength: {VibrationValueBuffer.Length}"); return 0; } @@ -1085,9 +1085,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid long VibrationGcErmCommand = Context.RequestData.ReadInt64(); long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"VibrationDeviceHandle: {VibrationDeviceHandle} - " + - $"VibrationGcErmCommand: {VibrationGcErmCommand}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"VibrationDeviceHandle: {VibrationDeviceHandle} - " + + $"VibrationGcErmCommand: {VibrationGcErmCommand}"); return 0; } @@ -1100,9 +1100,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write(VibrationGcErmCommand); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"VibrationDeviceHandle: {VibrationDeviceHandle} - " + - $"VibrationGcErmCommand: {VibrationGcErmCommand}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"VibrationDeviceHandle: {VibrationDeviceHandle} - " + + $"VibrationGcErmCommand: {VibrationGcErmCommand}"); return 0; } @@ -1112,7 +1112,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } @@ -1120,7 +1120,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid // EndPermitVibrationSession() public long EndPermitVibrationSession(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed."); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed."); return 0; } @@ -1130,7 +1130,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } @@ -1141,8 +1141,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid int ConsoleSixAxisSensorHandle = Context.RequestData.ReadInt32(); long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"ConsoleSixAxisSensorHandle: {ConsoleSixAxisSensorHandle}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"ConsoleSixAxisSensorHandle: {ConsoleSixAxisSensorHandle}"); return 0; } @@ -1153,8 +1153,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid int ConsoleSixAxisSensorHandle = Context.RequestData.ReadInt32(); long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"ConsoleSixAxisSensorHandle: {ConsoleSixAxisSensorHandle}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"ConsoleSixAxisSensorHandle: {ConsoleSixAxisSensorHandle}"); return 0; } @@ -1164,7 +1164,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } @@ -1174,7 +1174,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } @@ -1184,7 +1184,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } @@ -1198,9 +1198,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid // Todo: Determine if array is a buffer or not... - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"Counter0: {Counter0} - " + - $"Counter1: {Counter1}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"Counter0: {Counter0} - " + + $"Counter1: {Counter1}"); return 0; } @@ -1210,7 +1210,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } @@ -1221,8 +1221,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid SevenSixAxisSensorFusionStrength = Context.RequestData.ReadSingle(); long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"SevenSixAxisSensorFusionStrength: {SevenSixAxisSensorFusionStrength}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SevenSixAxisSensorFusionStrength: {SevenSixAxisSensorFusionStrength}"); return 0; } @@ -1234,8 +1234,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write(SevenSixAxisSensorFusionStrength); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"SevenSixAxisSensorFusionStrength: {SevenSixAxisSensorFusionStrength}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"SevenSixAxisSensorFusionStrength: {SevenSixAxisSensorFusionStrength}"); return 0; } @@ -1245,7 +1245,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { Context.ResponseData.Write(UsbFullKeyControllerEnabled); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. UsbFullKeyControllerEnabled: {UsbFullKeyControllerEnabled}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. UsbFullKeyControllerEnabled: {UsbFullKeyControllerEnabled}"); return 0; } @@ -1255,7 +1255,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { UsbFullKeyControllerEnabled = Context.RequestData.ReadBoolean(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. UsbFullKeyControllerEnabled: {UsbFullKeyControllerEnabled}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. UsbFullKeyControllerEnabled: {UsbFullKeyControllerEnabled}"); return 0; } @@ -1267,7 +1267,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write(true); //FullKeyController is always connected ? - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. Unknown0: {Unknown0} - Connected: true"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. Unknown0: {Unknown0} - Connected: true"); return 0; } @@ -1279,7 +1279,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write(true); //Npad always got a battery ? - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. NpadId: {NpadId} - HasBattery: true"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. NpadId: {NpadId} - HasBattery: true"); return 0; } @@ -1292,7 +1292,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write(true); //Npad always got a left battery ? Context.ResponseData.Write(true); //Npad always got a right battery ? - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. NpadId: {NpadId} - HasLeftBattery: true - HasRightBattery: true"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. NpadId: {NpadId} - HasLeftBattery: true - HasRightBattery: true"); return 0; } @@ -1304,7 +1304,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write((byte)0); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. NpadId: {NpadId} - NpadInterfaceType: 0"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. NpadId: {NpadId} - NpadInterfaceType: 0"); return 0; } @@ -1317,9 +1317,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write((byte)0); Context.ResponseData.Write((byte)0); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. NpadId: {NpadId} - " + - $"LeftInterfaceType: 0 - " + - $"RightInterfaceType: 0"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. NpadId: {NpadId} - " + + $"LeftInterfaceType: 0 - " + + $"RightInterfaceType: 0"); return 0; } @@ -1334,9 +1334,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write(PalmaConnectionHandle); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"Unknown0: {Unknown0} - " + - $"PalmaConnectionHandle: {PalmaConnectionHandle}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"Unknown0: {Unknown0} - " + + $"PalmaConnectionHandle: {PalmaConnectionHandle}"); return 0; } @@ -1346,7 +1346,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { int PalmaConnectionHandle = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); return 0; } @@ -1363,7 +1363,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); return 0; } @@ -1377,8 +1377,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid Context.ResponseData.Write(Unknown0); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + - $"Unknown0: {Unknown0}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + + $"Unknown0: {Unknown0}"); return 0; } @@ -1389,8 +1389,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid int PalmaConnectionHandle = Context.RequestData.ReadInt32(); long Unknown0 = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + - $"Unknown0: {Unknown0}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + + $"Unknown0: {Unknown0}"); return 0; } @@ -1401,8 +1401,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid int PalmaConnectionHandle = Context.RequestData.ReadInt32(); long FrModeType = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + - $"FrModeType: {FrModeType}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + + $"FrModeType: {FrModeType}"); return 0; } @@ -1412,7 +1412,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { int PalmaConnectionHandle = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); return 0; } @@ -1423,8 +1423,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid int PalmaConnectionHandle = Context.RequestData.ReadInt32(); bool EnabledPalmaStep = Context.RequestData.ReadBoolean(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + - $"EnabledPalmaStep: {EnabledPalmaStep}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + + $"EnabledPalmaStep: {EnabledPalmaStep}"); return 0; } @@ -1434,7 +1434,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { int PalmaConnectionHandle = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); return 0; } @@ -1444,7 +1444,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { int PalmaConnectionHandle = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); return 0; } @@ -1456,9 +1456,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid long Unknown0 = Context.RequestData.ReadInt64(); long Unknown1 = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + - $"Unknown0: {Unknown0} - " + - $"Unknown1: {Unknown1}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + + $"Unknown0: {Unknown0} - " + + $"Unknown1: {Unknown1}"); return 0; } @@ -1471,9 +1471,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid long Unknown1 = Context.RequestData.ReadInt64(); // nn::hid::PalmaApplicationSectionAccessBuffer cast is unknown - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + - $"Unknown0: {Unknown0} - " + - $"Unknown1: {Unknown1}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle} - " + + $"Unknown0: {Unknown0} - " + + $"Unknown1: {Unknown1}"); return 0; } @@ -1483,7 +1483,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { int PalmaConnectionHandle = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); return 0; } @@ -1493,7 +1493,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { int PalmaConnectionHandle = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. PalmaConnectionHandle: {PalmaConnectionHandle}"); return 0; } @@ -1504,8 +1504,8 @@ namespace Ryujinx.HLE.HOS.Services.Hid NpadCommunicationMode = Context.RequestData.ReadInt64(); long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + - $"NpadCommunicationMode: {NpadCommunicationMode}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. AppletResourceUserId: {AppletResourceUserId} - " + + $"NpadCommunicationMode: {NpadCommunicationMode}"); return 0; } @@ -1515,7 +1515,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid { Context.ResponseData.Write(NpadCommunicationMode); - Context.Device.Log.PrintStub(LogClass.ServiceHid, $"Stubbed. CommunicationMode: {NpadCommunicationMode}"); + Logger.PrintStub(LogClass.ServiceHid, $"Stubbed. CommunicationMode: {NpadCommunicationMode}"); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/IpcService.cs b/Ryujinx.HLE/HOS/Services/IpcService.cs index e9d820001d..60a4431e9e 100644 --- a/Ryujinx.HLE/HOS/Services/IpcService.cs +++ b/Ryujinx.HLE/HOS/Services/IpcService.cs @@ -1,6 +1,6 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; using System.IO; @@ -91,7 +91,7 @@ namespace Ryujinx.HLE.HOS.Services { Context.ResponseData.BaseStream.Seek(IsDomain ? 0x20 : 0x10, SeekOrigin.Begin); - Context.Device.Log.PrintDebug(LogClass.KernelIpc, $"{Service.GetType().Name}: {ProcessRequest.Method.Name}"); + Logger.PrintDebug(LogClass.KernelIpc, $"{Service.GetType().Name}: {ProcessRequest.Method.Name}"); long Result = ProcessRequest(Context); diff --git a/Ryujinx.HLE/HOS/Services/Irs/IIrSensorServer.cs b/Ryujinx.HLE/HOS/Services/Irs/IIrSensorServer.cs index c635431405..254fdae42a 100644 --- a/Ryujinx.HLE/HOS/Services/Irs/IIrSensorServer.cs +++ b/Ryujinx.HLE/HOS/Services/Irs/IIrSensorServer.cs @@ -1,6 +1,6 @@ -using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; @@ -28,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Services.Irs { long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceIrs, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + Logger.PrintStub(LogClass.ServiceIrs, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } @@ -38,7 +38,7 @@ namespace Ryujinx.HLE.HOS.Services.Irs { long AppletResourceUserId = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceIrs, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); + Logger.PrintStub(LogClass.ServiceIrs, $"Stubbed. AppletResourceUserId: {AppletResourceUserId}"); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Lm/ILogger.cs b/Ryujinx.HLE/HOS/Services/Lm/ILogger.cs index 5d99970990..8a630ce515 100644 --- a/Ryujinx.HLE/HOS/Services/Lm/ILogger.cs +++ b/Ryujinx.HLE/HOS/Services/Lm/ILogger.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; using System.IO; using System.Text; @@ -88,11 +88,11 @@ namespace Ryujinx.HLE.HOS.Services.Lm switch((LmLogLevel)Level) { - case LmLogLevel.Trace: Context.Device.Log.PrintDebug (LogClass.ServiceLm, Text); break; - case LmLogLevel.Info: Context.Device.Log.PrintInfo (LogClass.ServiceLm, Text); break; - case LmLogLevel.Warning: Context.Device.Log.PrintWarning(LogClass.ServiceLm, Text); break; - case LmLogLevel.Error: Context.Device.Log.PrintError (LogClass.ServiceLm, Text); break; - case LmLogLevel.Critical: Context.Device.Log.PrintError (LogClass.ServiceLm, Text); break; + case LmLogLevel.Trace: Logger.PrintDebug (LogClass.ServiceLm, Text); break; + case LmLogLevel.Info: Logger.PrintInfo (LogClass.ServiceLm, Text); break; + case LmLogLevel.Warning: Logger.PrintWarning(LogClass.ServiceLm, Text); break; + case LmLogLevel.Error: Logger.PrintError (LogClass.ServiceLm, Text); break; + case LmLogLevel.Critical: Logger.PrintError (LogClass.ServiceLm, Text); break; } } diff --git a/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs b/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs index 2a92cf76c5..be2aa4e441 100644 --- a/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs +++ b/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; @@ -29,21 +29,21 @@ namespace Ryujinx.HLE.HOS.Services.Mm int Unknown1 = Context.RequestData.ReadInt32(); int Unknown2 = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceMm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceMm, "Stubbed."); return 0; } public long Initialize(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceMm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceMm, "Stubbed."); return 0; } public long SetAndWait(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceMm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceMm, "Stubbed."); return 0; } @@ -52,7 +52,7 @@ namespace Ryujinx.HLE.HOS.Services.Mm { Context.ResponseData.Write(0); - Context.Device.Log.PrintStub(LogClass.ServiceMm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceMm, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Nfp/IUser.cs b/Ryujinx.HLE/HOS/Services/Nfp/IUser.cs index 72a385d2ff..f0dc825b08 100644 --- a/Ryujinx.HLE/HOS/Services/Nfp/IUser.cs +++ b/Ryujinx.HLE/HOS/Services/Nfp/IUser.cs @@ -1,7 +1,7 @@ -using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.Input; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; @@ -45,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfp public long Initialize(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceNfp, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNfp, "Stubbed."); State = State.Initialized; @@ -54,7 +54,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfp public long AttachActivateEvent(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceNfp, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNfp, "Stubbed."); if (Context.Process.HandleTable.GenerateHandle(ActivateEvent.ReadableEvent, out int Handle) != KernelResult.Success) { @@ -68,7 +68,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfp public long AttachDeactivateEvent(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceNfp, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNfp, "Stubbed."); if (Context.Process.HandleTable.GenerateHandle(DeactivateEvent.ReadableEvent, out int Handle) != KernelResult.Success) { @@ -84,7 +84,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfp { Context.ResponseData.Write((int)State); - Context.Device.Log.PrintStub(LogClass.ServiceNfp, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNfp, "Stubbed."); return 0; } @@ -93,7 +93,7 @@ namespace Ryujinx.HLE.HOS.Services.Nfp { Context.ResponseData.Write((int)DeviceState); - Context.Device.Log.PrintStub(LogClass.ServiceNfp, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNfp, "Stubbed."); return 0; } @@ -102,14 +102,14 @@ namespace Ryujinx.HLE.HOS.Services.Nfp { Context.ResponseData.Write((int)NpadId); - Context.Device.Log.PrintStub(LogClass.ServiceNfp, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNfp, "Stubbed."); return 0; } public long AttachAvailabilityChangeEvent(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceNfp, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNfp, "Stubbed."); if (Context.Process.HandleTable.GenerateHandle(AvailabilityChangeEvent.ReadableEvent, out int Handle) != KernelResult.Success) { diff --git a/Ryujinx.HLE/HOS/Services/Nifm/IGeneralService.cs b/Ryujinx.HLE/HOS/Services/Nifm/IGeneralService.cs index 6adbf00a15..bc23ea9100 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/IGeneralService.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/IGeneralService.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; using System.Linq; @@ -32,7 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm MakeObject(Context, new IRequest(Context.Device.System)); - Context.Device.Log.PrintStub(LogClass.ServiceNifm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNifm, "Stubbed."); return 0; } @@ -50,7 +50,7 @@ namespace Ryujinx.HLE.HOS.Services.Nifm Context.ResponseData.Write(BitConverter.ToUInt32(Address.GetAddressBytes())); - Context.Device.Log.PrintInfo(LogClass.ServiceNifm, $"Console's local IP is \"{Address}\"."); + Logger.PrintInfo(LogClass.ServiceNifm, $"Console's local IP is \"{Address}\"."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Nifm/IRequest.cs b/Ryujinx.HLE/HOS/Services/Nifm/IRequest.cs index 9b501d7ca7..9832786040 100644 --- a/Ryujinx.HLE/HOS/Services/Nifm/IRequest.cs +++ b/Ryujinx.HLE/HOS/Services/Nifm/IRequest.cs @@ -1,6 +1,6 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; @@ -35,14 +35,14 @@ namespace Ryujinx.HLE.HOS.Services.Nifm { Context.ResponseData.Write(1); - Context.Device.Log.PrintStub(LogClass.ServiceNifm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNifm, "Stubbed."); return 0; } public long GetResult(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceNifm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNifm, "Stubbed."); return 0; } @@ -66,21 +66,21 @@ namespace Ryujinx.HLE.HOS.Services.Nifm public long Cancel(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceNifm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNifm, "Stubbed."); return 0; } public long Submit(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceNifm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNifm, "Stubbed."); return 0; } public long SetConnectionConfirmationOption(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceNifm, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNifm, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Ns/IAddOnContentManager.cs b/Ryujinx.HLE/HOS/Services/Ns/IAddOnContentManager.cs index b8455d4144..82fce6b983 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/IAddOnContentManager.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/IAddOnContentManager.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Ns @@ -23,14 +23,14 @@ namespace Ryujinx.HLE.HOS.Services.Ns { Context.ResponseData.Write(0); - Context.Device.Log.PrintStub(LogClass.ServiceNs, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNs, "Stubbed."); return 0; } public static long ListAddOnContent(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceNs, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNs, "Stubbed."); //TODO: This is supposed to write a u32 array aswell. //It's unknown what it contains. diff --git a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs index 96de8cab4e..4f4c8add17 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/INvDrvServices.cs @@ -1,4 +1,5 @@ using ChocolArm64.Memory; +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS; @@ -6,7 +7,6 @@ using Ryujinx.HLE.HOS.Services.Nv.NvGpuGpu; using Ryujinx.HLE.HOS.Services.Nv.NvHostChannel; using Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl; using Ryujinx.HLE.HOS.Services.Nv.NvMap; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; @@ -146,7 +146,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv public long FinishInitialize(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return 0; } @@ -180,14 +180,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv { if (CmdIn(Cmd) && Context.Request.GetBufferType0x21().Position == 0) { - Context.Device.Log.PrintError(LogClass.ServiceNv, "Input buffer is null!"); + Logger.PrintError(LogClass.ServiceNv, "Input buffer is null!"); return NvResult.InvalidInput; } if (CmdOut(Cmd) && Context.Request.GetBufferType0x22().Position == 0) { - Context.Device.Log.PrintError(LogClass.ServiceNv, "Output buffer is null!"); + Logger.PrintError(LogClass.ServiceNv, "Output buffer is null!"); return NvResult.InvalidInput; } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs index 95eb5b9860..0ccc1949b7 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvGpuAS/NvGpuASIoctl.cs @@ -1,7 +1,7 @@ using ChocolArm64.Memory; +using Ryujinx.Common.Logging; using Ryujinx.Graphics.Memory; using Ryujinx.HLE.HOS.Services.Nv.NvMap; -using Ryujinx.HLE.Logging; using System; using System.Collections.Concurrent; @@ -42,7 +42,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -78,7 +78,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS { Args.Offset = 0; - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Failed to allocate size {Size:x16}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Failed to allocate size {Size:x16}!"); Result = NvResult.OutOfMemory; } @@ -115,7 +115,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS } else { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, + Logger.PrintWarning(LogClass.ServiceNv, $"Failed to free offset 0x{Args.Offset:x16} size 0x{Size:x16}!"); Result = NvResult.InvalidInput; @@ -145,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS } else { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Invalid buffer offset {Args.Offset:x16}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid buffer offset {Args.Offset:x16}!"); } } @@ -167,7 +167,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS if (Map == null) { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{Args.NvMapHandle:x8}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{Args.NvMapHandle:x8}!"); return NvResult.InvalidInput; } @@ -188,7 +188,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS { string Msg = string.Format(MapErrorMsg, VA, Args.MappingSize); - Context.Device.Log.PrintWarning(LogClass.ServiceNv, Msg); + Logger.PrintWarning(LogClass.ServiceNv, Msg); return NvResult.InvalidInput; } @@ -197,7 +197,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS } else { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Address 0x{Args.Offset:x16} not mapped!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Address 0x{Args.Offset:x16} not mapped!"); return NvResult.InvalidInput; } @@ -231,7 +231,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS { string Msg = string.Format(MapErrorMsg, Args.Offset, Size); - Context.Device.Log.PrintWarning(LogClass.ServiceNv, Msg); + Logger.PrintWarning(LogClass.ServiceNv, Msg); Result = NvResult.InvalidInput; } @@ -245,7 +245,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS { Args.Offset = 0; - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Failed to map size 0x{Size:x16}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Failed to map size 0x{Size:x16}!"); Result = NvResult.InvalidInput; } @@ -265,7 +265,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -275,7 +275,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -296,7 +296,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS if (Map == null) { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{Args.NvMapHandle:x8}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid NvMap handle 0x{Args.NvMapHandle:x8}!"); return NvResult.InvalidInput; } @@ -306,7 +306,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuAS if (Result < 0) { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, + Logger.PrintWarning(LogClass.ServiceNv, $"Page 0x{Args.Offset:x16} size 0x{Args.Pages:x16} not allocated!"); return NvResult.InvalidInput; diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvGpuGpu/NvGpuGpuIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvGpuGpu/NvGpuGpuIoctl.cs index 5ae45c1e9d..387fe5b416 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvGpuGpu/NvGpuGpuIoctl.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvGpuGpu/NvGpuGpuIoctl.cs @@ -1,5 +1,5 @@ using ChocolArm64.Memory; -using Ryujinx.HLE.Logging; +using Ryujinx.Common.Logging; using System; using System.Diagnostics; @@ -46,7 +46,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuGpu AMemoryHelper.Write(Context.Memory, OutputPosition, Args); - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -70,7 +70,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuGpu AMemoryHelper.Write(Context.Memory, OutputPosition, Args); - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -80,7 +80,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuGpu long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -163,7 +163,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvGpuGpu AMemoryHelper.Write(Context.Memory, OutputPosition, Args); - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs index f4ed48217b..d2dd089da5 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvHostChannel/NvHostChannelIoctl.cs @@ -1,7 +1,7 @@ using ChocolArm64.Memory; +using Ryujinx.Common.Logging; using Ryujinx.Graphics.Memory; using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS; -using Ryujinx.HLE.Logging; using System; using System.Collections.Concurrent; @@ -57,7 +57,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -67,7 +67,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -110,7 +110,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -120,7 +120,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -130,7 +130,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -140,7 +140,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -150,7 +150,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostChannel long InputPosition = Context.Request.GetBufferType0x21().Position; long OutputPosition = Context.Request.GetBufferType0x22().Position; - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs index 2bfe88821b..0b70928ea2 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvHostCtrl/NvHostCtrlIoctl.cs @@ -1,5 +1,5 @@ using ChocolArm64.Memory; -using Ryujinx.HLE.Logging; +using Ryujinx.Common.Logging; using System; using System.Collections.Concurrent; using System.Text; @@ -95,7 +95,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl { if (StringValue.Length > 0x100) { - Context.Device.Log.PrintError(Logging.LogClass.ServiceNv, $"{Domain}!{Name} String value size is too big!"); + Logger.PrintError(LogClass.ServiceNv, $"{Domain}!{Name} String value size is too big!"); } else { @@ -118,7 +118,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl Context.Memory.WriteBytes(OutputPosition + 0x82, SettingBuffer); - Context.Device.Log.PrintDebug(Logging.LogClass.ServiceNv, $"Got setting {Domain}!{Name}"); + Logger.PrintDebug(LogClass.ServiceNv, $"Got setting {Domain}!{Name}"); } return NvResult.Success; @@ -144,7 +144,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl int EventId = Context.Memory.ReadInt32(InputPosition); - Context.Device.Log.PrintStub(LogClass.ServiceNv, "Stubbed."); + Logger.PrintStub(LogClass.ServiceNv, "Stubbed."); return NvResult.Success; } @@ -201,7 +201,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl } else { - Context.Device.Log.PrintDebug(LogClass.ServiceNv, "Waiting syncpt with timeout of " + Args.Timeout + "ms..."); + Logger.PrintDebug(LogClass.ServiceNv, "Waiting syncpt with timeout of " + Args.Timeout + "ms..."); using (ManualResetEvent WaitEvent = new ManualResetEvent(false)) { @@ -232,7 +232,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvHostCtrl } } - Context.Device.Log.PrintDebug(LogClass.ServiceNv, "Resuming..."); + Logger.PrintDebug(LogClass.ServiceNv, "Resuming..."); } if (Extended) diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs b/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs index 38da2889eb..7953d66542 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvMap/NvMapIoctl.cs @@ -1,6 +1,6 @@ using ChocolArm64.Memory; +using Ryujinx.Common.Logging; using Ryujinx.Graphics.Memory; -using Ryujinx.HLE.Logging; using Ryujinx.HLE.Utilities; using System.Collections.Concurrent; @@ -29,7 +29,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap case 0x010e: return GetId (Context); } - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Unsupported Ioctl command 0x{Cmd:x8}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Unsupported Ioctl command 0x{Cmd:x8}!"); return NvResult.NotSupported; } @@ -43,7 +43,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap if (Args.Size == 0) { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Invalid size 0x{Args.Size:x8}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid size 0x{Args.Size:x8}!"); return NvResult.InvalidInput; } @@ -52,7 +52,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap Args.Handle = AddNvMap(Context, new NvMapHandle(Size)); - Context.Device.Log.PrintInfo(LogClass.ServiceNv, $"Created map {Args.Handle} with size 0x{Size:x8}!"); + Logger.PrintInfo(LogClass.ServiceNv, $"Created map {Args.Handle} with size 0x{Size:x8}!"); AMemoryHelper.Write(Context.Memory, OutputPosition, Args); @@ -70,7 +70,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap if (Map == null) { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); return NvResult.InvalidInput; } @@ -95,14 +95,14 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap if (Map == null) { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); return NvResult.InvalidInput; } if ((Args.Align & (Args.Align - 1)) != 0) { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Invalid alignment 0x{Args.Align:x8}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid alignment 0x{Args.Align:x8}!"); return NvResult.InvalidInput; } @@ -159,7 +159,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap if (Map == null) { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); return NvResult.InvalidInput; } @@ -168,7 +168,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap { DeleteNvMap(Context, Args.Handle); - Context.Device.Log.PrintInfo(LogClass.ServiceNv, $"Deleted map {Args.Handle}!"); + Logger.PrintInfo(LogClass.ServiceNv, $"Deleted map {Args.Handle}!"); Args.Address = Map.Address; Args.Flags = 0; @@ -197,7 +197,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap if (Map == null) { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); return NvResult.InvalidInput; } @@ -231,7 +231,7 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvMap if (Map == null) { - Context.Device.Log.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); + Logger.PrintWarning(LogClass.ServiceNv, $"Invalid handle 0x{Args.Handle:x8}!"); return NvResult.InvalidInput; } diff --git a/Ryujinx.HLE/HOS/Services/Pctl/IParentalControlService.cs b/Ryujinx.HLE/HOS/Services/Pctl/IParentalControlService.cs index d484d312f3..3e9d276ae2 100644 --- a/Ryujinx.HLE/HOS/Services/Pctl/IParentalControlService.cs +++ b/Ryujinx.HLE/HOS/Services/Pctl/IParentalControlService.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Pctl @@ -32,7 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.Pctl } else { - Context.Device.Log.PrintWarning(LogClass.ServicePctl, "Service is already initialized!"); + Logger.PrintWarning(LogClass.ServicePctl, "Service is already initialized!"); } return 0; diff --git a/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs b/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs index fa39854d1d..cc96a5c920 100644 --- a/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs +++ b/Ryujinx.HLE/HOS/Services/Prepo/IPrepoService.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Prepo @@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.Prepo public static long SaveReportWithUser(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServicePrepo, "Stubbed."); + Logger.PrintStub(LogClass.ServicePrepo, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Set/ISystemSettingsServer.cs b/Ryujinx.HLE/HOS/Services/Set/ISystemSettingsServer.cs index dc1469674c..070a4d5e4d 100644 --- a/Ryujinx.HLE/HOS/Services/Set/ISystemSettingsServer.cs +++ b/Ryujinx.HLE/HOS/Services/Set/ISystemSettingsServer.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.SystemState; using System; @@ -115,7 +116,7 @@ namespace Ryujinx.HLE.HOS.Services.Set { if (StringValue.Length + 1 > ReplySize) { - Context.Device.Log.PrintError(Logging.LogClass.ServiceSet, $"{AskedSetting} String value size is too big!"); + Logger.PrintError(LogClass.ServiceSet, $"{AskedSetting} String value size is too big!"); } else { @@ -138,11 +139,11 @@ namespace Ryujinx.HLE.HOS.Services.Set Context.Memory.WriteBytes(ReplyPos, SettingBuffer); - Context.Device.Log.PrintDebug(Logging.LogClass.ServiceSet, $"{AskedSetting} set value: {NxSetting} as {NxSetting.GetType()}"); + Logger.PrintDebug(LogClass.ServiceSet, $"{AskedSetting} set value: {NxSetting} as {NxSetting.GetType()}"); } else { - Context.Device.Log.PrintError(Logging.LogClass.ServiceSet, $"{AskedSetting} not found!"); + Logger.PrintError(LogClass.ServiceSet, $"{AskedSetting} not found!"); } return 0; diff --git a/Ryujinx.HLE/HOS/Services/Sfdnsres/IResolver.cs b/Ryujinx.HLE/HOS/Services/Sfdnsres/IResolver.cs index 0ca43eda4f..a351e3deca 100644 --- a/Ryujinx.HLE/HOS/Services/Sfdnsres/IResolver.cs +++ b/Ryujinx.HLE/HOS/Services/Sfdnsres/IResolver.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using System; using System.Collections.Generic; @@ -165,7 +166,7 @@ namespace Ryujinx.HLE.HOS.Services.Sfdnsres long BufferSize = Context.Request.SendBuff[0].Size; // TODO: This is stubbed in 2.0.0+, reverse 1.0.0 version for the sake completeness. - Context.Device.Log.PrintStub(Logging.LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}"); + Logger.PrintStub(LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}"); return MakeError(ErrorModule.Os, 1023); } @@ -176,7 +177,7 @@ namespace Ryujinx.HLE.HOS.Services.Sfdnsres uint Unknown0 = Context.RequestData.ReadUInt32(); // TODO: This is stubbed in 2.0.0+, reverse 1.0.0 version for the sake completeness. - Context.Device.Log.PrintStub(Logging.LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}"); + Logger.PrintStub(LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}"); return MakeError(ErrorModule.Os, 1023); } @@ -368,7 +369,7 @@ namespace Ryujinx.HLE.HOS.Services.Sfdnsres Context.ResponseData.Write(0); - Context.Device.Log.PrintStub(Logging.LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}"); + Logger.PrintStub(LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}"); return 0; } @@ -379,8 +380,8 @@ namespace Ryujinx.HLE.HOS.Services.Sfdnsres uint Unknown0 = Context.RequestData.ReadUInt32(); ulong Unknown1 = Context.RequestData.ReadUInt64(); - Context.Device.Log.PrintStub(Logging.LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0} - " + - $"Unknown1: {Unknown1}"); + Logger.PrintStub(LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0} - " + + $"Unknown1: {Unknown1}"); return 0; } @@ -390,7 +391,7 @@ namespace Ryujinx.HLE.HOS.Services.Sfdnsres { uint Unknown0 = Context.RequestData.ReadUInt32(); - Context.Device.Log.PrintStub(Logging.LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}"); + Logger.PrintStub(LogClass.ServiceSfdnsres, $"Stubbed. Unknown0: {Unknown0}"); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs b/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs index 5affc63689..3046aab7a3 100644 --- a/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs +++ b/Ryujinx.HLE/HOS/Services/Ssl/ISslService.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Ssl @@ -25,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl int SslVersion = Context.RequestData.ReadInt32(); long Unknown = Context.RequestData.ReadInt64(); - Context.Device.Log.PrintStub(LogClass.ServiceSsl, $"Stubbed. SslVersion: {SslVersion} - Unknown: {Unknown}"); + Logger.PrintStub(LogClass.ServiceSsl, $"Stubbed. SslVersion: {SslVersion} - Unknown: {Unknown}"); MakeObject(Context, new ISslContext()); @@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.Ssl { int Version = Context.RequestData.ReadInt32(); - Context.Device.Log.PrintStub(LogClass.ServiceSsl, $"Stubbed. Version: {Version}"); + Logger.PrintStub(LogClass.ServiceSsl, $"Stubbed. Version: {Version}"); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Time/ITimeZoneService.cs b/Ryujinx.HLE/HOS/Services/Time/ITimeZoneService.cs index 6df28659d1..0e321e44c2 100644 --- a/Ryujinx.HLE/HOS/Services/Time/ITimeZoneService.cs +++ b/Ryujinx.HLE/HOS/Services/Time/ITimeZoneService.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; using System.Text; @@ -110,7 +110,7 @@ namespace Ryujinx.HLE.HOS.Services.Time if (BufferSize != 0x4000) { - Context.Device.Log.PrintWarning(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{BufferSize:x} (expected 0x4000)"); + Logger.PrintWarning(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{BufferSize:x} (expected 0x4000)"); } long ResultCode = 0; @@ -132,7 +132,7 @@ namespace Ryujinx.HLE.HOS.Services.Time } catch (TimeZoneNotFoundException) { - Context.Device.Log.PrintWarning(LogClass.ServiceTime, $"Timezone not found for string: {TzID} (len: {TzID.Length})"); + Logger.PrintWarning(LogClass.ServiceTime, $"Timezone not found for string: {TzID} (len: {TzID.Length})"); ResultCode = MakeError(ErrorModule.Time, 0x3dd); } @@ -170,7 +170,7 @@ namespace Ryujinx.HLE.HOS.Services.Time if (BufferSize != 0x4000) { - Context.Device.Log.PrintWarning(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{BufferSize:x} (expected 0x4000)"); + Logger.PrintWarning(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{BufferSize:x} (expected 0x4000)"); } // TODO: Reverse the TZif2 conversion in PCV to make this match with real hardware. @@ -189,7 +189,7 @@ namespace Ryujinx.HLE.HOS.Services.Time } catch (TimeZoneNotFoundException) { - Context.Device.Log.PrintWarning(LogClass.ServiceTime, $"Timezone not found for string: {TzID} (len: {TzID.Length})"); + Logger.PrintWarning(LogClass.ServiceTime, $"Timezone not found for string: {TzID} (len: {TzID.Length})"); ResultCode = MakeError(ErrorModule.Time, 0x3dd); } @@ -220,7 +220,7 @@ namespace Ryujinx.HLE.HOS.Services.Time if (BufferSize != 0x4000) { - Context.Device.Log.PrintWarning(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{BufferSize:x} (expected 0x4000)"); + Logger.PrintWarning(LogClass.ServiceTime, $"TimeZoneRule buffer size is 0x{BufferSize:x} (expected 0x4000)"); } // TODO: Reverse the TZif2 conversion in PCV to make this match with real hardware. @@ -239,7 +239,7 @@ namespace Ryujinx.HLE.HOS.Services.Time } catch (TimeZoneNotFoundException) { - Context.Device.Log.PrintWarning(LogClass.ServiceTime, $"Timezone not found for string: {TzID} (len: {TzID.Length})"); + Logger.PrintWarning(LogClass.ServiceTime, $"Timezone not found for string: {TzID} (len: {TzID.Length})"); ResultCode = MakeError(ErrorModule.Time, 0x3dd); } diff --git a/Ryujinx.HLE/HOS/Services/Vi/IManagerDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/IManagerDisplayService.cs index 61f0ffaa51..a78f1812f0 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/IManagerDisplayService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/IManagerDisplayService.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Vi @@ -23,7 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi public static long CreateManagedLayer(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceVi, "Stubbed."); + Logger.PrintStub(LogClass.ServiceVi, "Stubbed."); Context.ResponseData.Write(0L); //LayerId @@ -32,21 +32,21 @@ namespace Ryujinx.HLE.HOS.Services.Vi public long DestroyManagedLayer(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceVi, "Stubbed."); + Logger.PrintStub(LogClass.ServiceVi, "Stubbed."); return 0; } public static long AddToLayerStack(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceVi, "Stubbed."); + Logger.PrintStub(LogClass.ServiceVi, "Stubbed."); return 0; } public static long SetLayerVisibility(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceVi, "Stubbed."); + Logger.PrintStub(LogClass.ServiceVi, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Vi/ISystemDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/ISystemDisplayService.cs index 5657ba69f3..4545579bef 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/ISystemDisplayService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/ISystemDisplayService.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.Logging; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Vi @@ -22,14 +22,14 @@ namespace Ryujinx.HLE.HOS.Services.Vi public static long SetLayerZ(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceVi, "Stubbed."); + Logger.PrintStub(LogClass.ServiceVi, "Stubbed."); return 0; } public static long SetLayerVisibility(ServiceCtx Context) { - Context.Device.Log.PrintStub(LogClass.ServiceVi, "Stubbed."); + Logger.PrintStub(LogClass.ServiceVi, "Stubbed."); return 0; } diff --git a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs index 191537b1da..64cbad5c24 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs @@ -1,9 +1,9 @@ +using Ryujinx.Common.Logging; using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Memory; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS; using Ryujinx.HLE.HOS.Services.Nv.NvMap; -using Ryujinx.HLE.Logging; using System; using System.Collections.Generic; using System.IO; @@ -114,7 +114,7 @@ namespace Ryujinx.HLE.HOS.Services.Android if (Commands.TryGetValue((InterfaceName, Code), out ServiceProcessParcel ProcReq)) { - Context.Device.Log.PrintDebug(LogClass.ServiceVi, $"{InterfaceName} {ProcReq.Method.Name}"); + Logger.PrintDebug(LogClass.ServiceVi, $"{InterfaceName} {ProcReq.Method.Name}"); return ProcReq(Context, Reader); } diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj index d24cd83311..6625136a1e 100644 --- a/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/Ryujinx.HLE/Ryujinx.HLE.csproj @@ -24,6 +24,7 @@ + diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index a3f874ee59..fe0be6cef6 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -4,7 +4,6 @@ using Ryujinx.Graphics.Gal; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.HLE.Input; -using Ryujinx.HLE.Logging; using Ryujinx.HLE.Memory; using System; using System.Threading; @@ -15,8 +14,6 @@ namespace Ryujinx.HLE { internal IAalOutput AudioOut { get; private set; } - public Logger Log { get; private set; } - internal DeviceMemory Memory { get; private set; } internal NvGpu Gpu { get; private set; } @@ -49,8 +46,6 @@ namespace Ryujinx.HLE this.AudioOut = AudioOut; - Log = new Logger(); - Memory = new DeviceMemory(); Gpu = new NvGpu(Renderer); diff --git a/Ryujinx.sln b/Ryujinx.sln index 79af0ed344..148224faa4 100644 --- a/Ryujinx.sln +++ b/Ryujinx.sln @@ -17,9 +17,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Graphics", "Ryujinx EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Audio", "Ryujinx.Audio\Ryujinx.Audio.csproj", "{5C1D818E-682A-46A5-9D54-30006E26C270}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ryujinx.ShaderTools", "Ryujinx.ShaderTools\Ryujinx.ShaderTools.csproj", "{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.ShaderTools", "Ryujinx.ShaderTools\Ryujinx.ShaderTools.csproj", "{3AB294D0-2230-468F-9EB3-BDFCAEAE99A5}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Luea", "Ryujinx.LLE\Luea.csproj", "{8E7D36DD-9626-47E2-8EF5-8F2F66751C9C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Luea", "Ryujinx.LLE\Luea.csproj", "{8E7D36DD-9626-47E2-8EF5-8F2F66751C9C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ryujinx.Common", "Ryujinx.Common\Ryujinx.Common.csproj", "{5FD4E4F6-8928-4B3C-BE07-28A675C17226}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -63,6 +65,10 @@ Global {8E7D36DD-9626-47E2-8EF5-8F2F66751C9C}.Debug|Any CPU.Build.0 = Debug|Any CPU {8E7D36DD-9626-47E2-8EF5-8F2F66751C9C}.Release|Any CPU.ActiveCfg = Release|Any CPU {8E7D36DD-9626-47E2-8EF5-8F2F66751C9C}.Release|Any CPU.Build.0 = Release|Any CPU + {5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5FD4E4F6-8928-4B3C-BE07-28A675C17226}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Ryujinx/Config.cs b/Ryujinx/Config.cs index 197a8cad65..3e4d4dd3f4 100644 --- a/Ryujinx/Config.cs +++ b/Ryujinx/Config.cs @@ -1,5 +1,5 @@ +using Ryujinx.Common.Logging; using Ryujinx.HLE; -using Ryujinx.HLE.Logging; using Ryujinx.UI.Input; using System; using System.Globalization; @@ -25,11 +25,11 @@ namespace Ryujinx GraphicsConfig.ShadersDumpPath = Parser.Value("Graphics_Shaders_Dump_Path"); - Device.Log.SetEnable(LogLevel.Debug, Convert.ToBoolean(Parser.Value("Logging_Enable_Debug"))); - Device.Log.SetEnable(LogLevel.Stub, Convert.ToBoolean(Parser.Value("Logging_Enable_Stub"))); - Device.Log.SetEnable(LogLevel.Info, Convert.ToBoolean(Parser.Value("Logging_Enable_Info"))); - Device.Log.SetEnable(LogLevel.Warning, Convert.ToBoolean(Parser.Value("Logging_Enable_Warn"))); - Device.Log.SetEnable(LogLevel.Error, Convert.ToBoolean(Parser.Value("Logging_Enable_Error"))); + Logger.SetEnable(LogLevel.Debug, Convert.ToBoolean(Parser.Value("Logging_Enable_Debug"))); + Logger.SetEnable(LogLevel.Stub, Convert.ToBoolean(Parser.Value("Logging_Enable_Stub"))); + Logger.SetEnable(LogLevel.Info, Convert.ToBoolean(Parser.Value("Logging_Enable_Info"))); + Logger.SetEnable(LogLevel.Warning, Convert.ToBoolean(Parser.Value("Logging_Enable_Warn"))); + Logger.SetEnable(LogLevel.Error, Convert.ToBoolean(Parser.Value("Logging_Enable_Error"))); string[] FilteredLogClasses = Parser.Value("Logging_Filtered_Classes").Split(',', StringSplitOptions.RemoveEmptyEntries); @@ -41,7 +41,7 @@ namespace Ryujinx { foreach (LogClass Class in Enum.GetValues(typeof(LogClass))) { - Device.Log.SetEnable(Class, false); + Logger.SetEnable(Class, false); } } @@ -53,7 +53,7 @@ namespace Ryujinx { if (Class.ToString().ToLower().Contains(LogClass.Trim().ToLower())) { - Device.Log.SetEnable(Class, true); + Logger.SetEnable(Class, true); } } } diff --git a/Ryujinx/Ryujinx.csproj b/Ryujinx/Ryujinx.csproj index ad132ada4a..7bc30d104e 100644 --- a/Ryujinx/Ryujinx.csproj +++ b/Ryujinx/Ryujinx.csproj @@ -1,4 +1,4 @@ - + netcoreapp2.1 @@ -15,6 +15,7 @@ + diff --git a/Ryujinx/Ui/ConsoleLog.cs b/Ryujinx/Ui/ConsoleLog.cs index 6fb92e333f..5ae13817e4 100644 --- a/Ryujinx/Ui/ConsoleLog.cs +++ b/Ryujinx/Ui/ConsoleLog.cs @@ -1,4 +1,4 @@ -using Ryujinx.HLE.Logging; +using Ryujinx.Common.Logging; using System; using System.Collections.Concurrent; using System.Collections.Generic; diff --git a/Ryujinx/Ui/Program.cs b/Ryujinx/Ui/Program.cs index 053cf1be4b..145ff33fd8 100644 --- a/Ryujinx/Ui/Program.cs +++ b/Ryujinx/Ui/Program.cs @@ -1,5 +1,6 @@ using Ryujinx.Audio; using Ryujinx.Audio.OpenAL; +using Ryujinx.Common.Logging; using Ryujinx.Graphics.Gal; using Ryujinx.Graphics.Gal.OpenGL; using Ryujinx.HLE; @@ -22,7 +23,7 @@ namespace Ryujinx Config.Read(Device); - Device.Log.Updated += ConsoleLog.Log; + Logger.Updated += ConsoleLog.Log; if (args.Length == 1) { From 02a8e7fc9369d7882db08a69d108beefb0f98677 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 17 Oct 2018 14:55:10 -0300 Subject: [PATCH 29/30] Remove unnecessary usings (#463) * Remove unnecessary usings * Fix CastExpression while I'm at it --- Ryujinx.HLE/FileSystem/SaveHelper.cs | 1 - Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs | 1 - .../Demangler/Ast/{CasExpression.cs => CastExpression.cs} | 4 +--- .../HOS/Diagnostics/Demangler/Ast/InitListExpression.cs | 1 - Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs | 1 - .../HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs | 1 - Ryujinx.HLE/HOS/Services/Aud/IAudioRendererManager.cs | 1 - Ryujinx.HLE/HOS/Services/Irs/IIrSensorServer.cs | 2 -- Ryujinx.HLE/HOS/Services/Mm/IRequest.cs | 1 - Ryujinx.HLE/Utilities/UInt128.cs | 1 - Ryujinx.Tests.Unicorn/MemoryPermission.cs | 2 -- Ryujinx.Tests.Unicorn/Native/ArmRegister.cs | 2 -- Ryujinx.Tests.Unicorn/Native/Interface.cs | 1 - Ryujinx.Tests.Unicorn/Native/UnicornArch.cs | 2 -- Ryujinx.Tests.Unicorn/Native/UnicornMode.cs | 2 -- Ryujinx.Tests.Unicorn/UnicornAArch64.cs | 1 - Ryujinx.Tests.Unicorn/UnicornError.cs | 2 -- Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs | 1 - 18 files changed, 1 insertion(+), 26 deletions(-) rename Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/{CasExpression.cs => CastExpression.cs} (92%) diff --git a/Ryujinx.HLE/FileSystem/SaveHelper.cs b/Ryujinx.HLE/FileSystem/SaveHelper.cs index 087156d2a0..b74d853c79 100644 --- a/Ryujinx.HLE/FileSystem/SaveHelper.cs +++ b/Ryujinx.HLE/FileSystem/SaveHelper.cs @@ -1,6 +1,5 @@ using Ryujinx.HLE.HOS; using System.IO; -using System.Linq; using static Ryujinx.HLE.FileSystem.VirtualFileSystem; diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs index 7e59ab09df..ae43fcdb21 100644 --- a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CallExpression.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.IO; diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CasExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CastExpression.cs similarity index 92% rename from Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CasExpression.cs rename to Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CastExpression.cs index 2415c6c000..c02e9e6555 100644 --- a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CasExpression.cs +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/CastExpression.cs @@ -1,7 +1,5 @@ -using System; using System.IO; - namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast { public class CastExpression : BaseNode @@ -24,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Diagnostics.Demangler.Ast To.PrintLeft(Writer); Writer.Write(">("); From.PrintLeft(Writer); - Writer.Write(")"); + Writer.Write(")"); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs index cd534590b9..2ed4daa46e 100644 --- a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/InitListExpression.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.IO; diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs index 9720a8e49d..f7bfa194fb 100644 --- a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/NodeArray.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.IO; diff --git a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs index 4de66e000b..d6efbd0fc3 100644 --- a/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs +++ b/Ryujinx.HLE/HOS/Diagnostics/Demangler/Ast/TemplateArguments.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.IO; diff --git a/Ryujinx.HLE/HOS/Services/Aud/IAudioRendererManager.cs b/Ryujinx.HLE/HOS/Services/Aud/IAudioRendererManager.cs index 49bbbd5a8d..48a449ccc9 100644 --- a/Ryujinx.HLE/HOS/Services/Aud/IAudioRendererManager.cs +++ b/Ryujinx.HLE/HOS/Services/Aud/IAudioRendererManager.cs @@ -3,7 +3,6 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Services.Aud.AudioRenderer; using Ryujinx.HLE.Utilities; -using System; using System.Collections.Generic; using static Ryujinx.HLE.HOS.ErrorCode; diff --git a/Ryujinx.HLE/HOS/Services/Irs/IIrSensorServer.cs b/Ryujinx.HLE/HOS/Services/Irs/IIrSensorServer.cs index 254fdae42a..155faea3e6 100644 --- a/Ryujinx.HLE/HOS/Services/Irs/IIrSensorServer.cs +++ b/Ryujinx.HLE/HOS/Services/Irs/IIrSensorServer.cs @@ -1,7 +1,5 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using Ryujinx.HLE.HOS.Kernel; -using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Irs diff --git a/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs b/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs index be2aa4e441..88cd57cff7 100644 --- a/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs +++ b/Ryujinx.HLE/HOS/Services/Mm/IRequest.cs @@ -1,6 +1,5 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Ipc; -using System; using System.Collections.Generic; namespace Ryujinx.HLE.HOS.Services.Mm diff --git a/Ryujinx.HLE/Utilities/UInt128.cs b/Ryujinx.HLE/Utilities/UInt128.cs index 95b3d624dd..14e04e4a6a 100644 --- a/Ryujinx.HLE/Utilities/UInt128.cs +++ b/Ryujinx.HLE/Utilities/UInt128.cs @@ -1,4 +1,3 @@ -using Ryujinx.HLE.Utilities; using System; using System.IO; using System.Linq; diff --git a/Ryujinx.Tests.Unicorn/MemoryPermission.cs b/Ryujinx.Tests.Unicorn/MemoryPermission.cs index d79b2adda1..a14c4e9cf1 100644 --- a/Ryujinx.Tests.Unicorn/MemoryPermission.cs +++ b/Ryujinx.Tests.Unicorn/MemoryPermission.cs @@ -1,5 +1,3 @@ -using System; - namespace Ryujinx.Tests.Unicorn { public enum MemoryPermission diff --git a/Ryujinx.Tests.Unicorn/Native/ArmRegister.cs b/Ryujinx.Tests.Unicorn/Native/ArmRegister.cs index 3554480c13..cf110598eb 100644 --- a/Ryujinx.Tests.Unicorn/Native/ArmRegister.cs +++ b/Ryujinx.Tests.Unicorn/Native/ArmRegister.cs @@ -1,5 +1,3 @@ -using System; - namespace Ryujinx.Tests.Unicorn.Native { public enum ArmRegister diff --git a/Ryujinx.Tests.Unicorn/Native/Interface.cs b/Ryujinx.Tests.Unicorn/Native/Interface.cs index a6563220fe..b2786d1436 100644 --- a/Ryujinx.Tests.Unicorn/Native/Interface.cs +++ b/Ryujinx.Tests.Unicorn/Native/Interface.cs @@ -1,6 +1,5 @@ using System; using System.Runtime.InteropServices; -using Ryujinx.Tests.Unicorn; namespace Ryujinx.Tests.Unicorn.Native { diff --git a/Ryujinx.Tests.Unicorn/Native/UnicornArch.cs b/Ryujinx.Tests.Unicorn/Native/UnicornArch.cs index be088366da..73710faa89 100644 --- a/Ryujinx.Tests.Unicorn/Native/UnicornArch.cs +++ b/Ryujinx.Tests.Unicorn/Native/UnicornArch.cs @@ -1,5 +1,3 @@ -using System; - namespace Ryujinx.Tests.Unicorn.Native { public enum UnicornArch diff --git a/Ryujinx.Tests.Unicorn/Native/UnicornMode.cs b/Ryujinx.Tests.Unicorn/Native/UnicornMode.cs index 950583bdc0..a5040518dd 100644 --- a/Ryujinx.Tests.Unicorn/Native/UnicornMode.cs +++ b/Ryujinx.Tests.Unicorn/Native/UnicornMode.cs @@ -1,5 +1,3 @@ -using System; - namespace Ryujinx.Tests.Unicorn.Native { public enum UnicornMode diff --git a/Ryujinx.Tests.Unicorn/UnicornAArch64.cs b/Ryujinx.Tests.Unicorn/UnicornAArch64.cs index 1cd3671fe1..8ee4e76db9 100644 --- a/Ryujinx.Tests.Unicorn/UnicornAArch64.cs +++ b/Ryujinx.Tests.Unicorn/UnicornAArch64.cs @@ -1,6 +1,5 @@ using System; using System.Diagnostics.Contracts; -using System.Runtime.InteropServices; using System.Runtime.Intrinsics; using System.Runtime.Intrinsics.X86; diff --git a/Ryujinx.Tests.Unicorn/UnicornError.cs b/Ryujinx.Tests.Unicorn/UnicornError.cs index db56c615d1..85833ea008 100644 --- a/Ryujinx.Tests.Unicorn/UnicornError.cs +++ b/Ryujinx.Tests.Unicorn/UnicornError.cs @@ -1,5 +1,3 @@ -using System; - namespace Ryujinx.Tests.Unicorn { public enum UnicornError diff --git a/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs b/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs index 5afeab315c..08e2894ca9 100644 --- a/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs +++ b/Ryujinx.Tests/Cpu/CpuTestSimdArithmetic.cs @@ -3,7 +3,6 @@ using ChocolArm64.State; using NUnit.Framework; using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; namespace Ryujinx.Tests.Cpu { From 0e1e094b7a8f0134831fc4cebdb0841b9c10fe6a Mon Sep 17 00:00:00 2001 From: gdkchan Date: Wed, 17 Oct 2018 18:02:23 -0300 Subject: [PATCH 30/30] Improve texture tables (#457) * Improve texture tables * More renaming and other tweaks * Minor tweaks --- Ryujinx.Graphics/Gal/GalImageFormat.cs | 120 +++----- Ryujinx.Graphics/Gal/GalPipelineState.cs | 29 +- Ryujinx.Graphics/Gal/GalTextureFormat.cs | 80 ++--- Ryujinx.Graphics/Gal/GalZetaFormat.cs | 20 +- Ryujinx.Graphics/Gal/IGalPipeline.cs | 3 + Ryujinx.Graphics/Gal/IGalRasterizer.cs | 5 +- .../Gal/OpenGL/OGLEnumConverter.cs | 119 ++++---- Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs | 38 ++- Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs | 26 +- .../Gal/OpenGL/OGLRenderTarget.cs | 13 +- Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs | 21 +- Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs | 75 ++--- Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs | 4 +- Ryujinx.Graphics/Gal/ShaderDumper.cs | 14 +- Ryujinx.Graphics/NvGpuEngine3d.cs | 47 +-- Ryujinx.Graphics/NvGpuEngine3dReg.cs | 3 +- Ryujinx.Graphics/Texture/ImageConverter.cs | 24 -- Ryujinx.Graphics/Texture/ImageUtils.cs | 275 +++++++++--------- Ryujinx.Graphics/Texture/TextureFactory.cs | 4 +- Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs | 2 +- 20 files changed, 445 insertions(+), 477 deletions(-) delete mode 100644 Ryujinx.Graphics/Texture/ImageConverter.cs diff --git a/Ryujinx.Graphics/Gal/GalImageFormat.cs b/Ryujinx.Graphics/Gal/GalImageFormat.cs index ba555684f2..2712cbc253 100644 --- a/Ryujinx.Graphics/Gal/GalImageFormat.cs +++ b/Ryujinx.Graphics/Gal/GalImageFormat.cs @@ -5,89 +5,61 @@ namespace Ryujinx.Graphics.Gal [Flags] public enum GalImageFormat { - Snorm = 1 << 27, - Unorm = 1 << 28, - Sint = 1 << 29, - Uint = 1 << 30, - Sfloat = 1 << 31, + Astc2DStart, + Astc2D4x4, + Astc2D5x4, + Astc2D5x5, + Astc2D6x5, + Astc2D6x6, + Astc2D8x5, + Astc2D8x6, + Astc2D8x8, + Astc2D10x5, + Astc2D10x6, + Astc2D10x8, + Astc2D10x10, + Astc2D12x10, + Astc2D12x12, + Astc2DEnd, - TypeMask = Snorm | Unorm | Sint | Uint | Sfloat, - - FormatMask = ~TypeMask, - - ASTC_BEGIN = ASTC_4x4, - - ASTC_4x4 = 1, - ASTC_5x4, - ASTC_5x5, - ASTC_6x5, - ASTC_6x6, - ASTC_8x5, - ASTC_8x6, - ASTC_8x8, - ASTC_10x5, - ASTC_10x6, - ASTC_10x8, - ASTC_10x10, - ASTC_12x10, - ASTC_12x12, - - ASTC_END = ASTC_12x12, - - R4G4, - R4G4B4A4, - B4G4R4A4, - A4B4G4R4, - R5G6B5, - B5G6R5, - R5G5B5A1, - B5G5R5A1, - A1R5G5B5, + RGBA4, + RGB565, + BGR5A1, + RGB5A1, R8, - R8G8, - G8R8, - R8G8B8, - B8G8R8, - R8G8B8A8, - B8G8R8A8, - A8B8G8R8, - A8B8G8R8_SRGB, - A2R10G10B10, - A2B10G10R10, + RG8, + RGBA8, + BGRA8, + RGB10A2, R16, - R16G16, - R16G16B16, - R16G16B16A16, + RG16, + RGBA16, R32, - R32G32, - R32G32B32, - R32G32B32A32, - R64, - R64G64, - R64G64B64, - R64G64B64A64, - B10G11R11, - E5B9G9R9, + RG32, + RGBA32, + R11G11B10, D16, - X8_D24, D32, - S8, - D16_S8, - D24_S8, - D32_S8, - BC1_RGB, - BC1_RGBA, + D24S8, + D32S8, + BC1, BC2, BC3, BC4, BC5, - BC6H_SF16, - BC6H_UF16, - BC7, - ETC2_R8G8B8, - ETC2_R8G8B8A1, - ETC2_R8G8B8A8, - EAC_R11, - EAC_R11G11, + BptcSfloat, + BptcUfloat, + BptcUnorm, + + Snorm = 1 << 26, + Unorm = 1 << 27, + Sint = 1 << 28, + Uint = 1 << 39, + Float = 1 << 30, + Srgb = 1 << 31, + + TypeMask = Snorm | Unorm | Sint | Uint | Float | Srgb, + + FormatMask = ~TypeMask } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalPipelineState.cs b/Ryujinx.Graphics/Gal/GalPipelineState.cs index 1bfe2684f0..56b0fbde33 100644 --- a/Ryujinx.Graphics/Gal/GalPipelineState.cs +++ b/Ryujinx.Graphics/Gal/GalPipelineState.cs @@ -1,9 +1,28 @@ namespace Ryujinx.Graphics.Gal { + public struct ColorMaskRgba + { + private static readonly ColorMaskRgba _Default = new ColorMaskRgba() + { + Red = true, + Green = true, + Blue = true, + Alpha = true + }; + + public static ColorMaskRgba Default => _Default; + + public bool Red; + public bool Green; + public bool Blue; + public bool Alpha; + } + public class GalPipelineState { - public const int Stages = 5; + public const int Stages = 5; public const int ConstBuffersPerStage = 18; + public const int RenderTargetsCount = 8; public long[][] ConstBufferKeys; @@ -53,10 +72,8 @@ public GalBlendFactor BlendFuncSrcAlpha; public GalBlendFactor BlendFuncDstAlpha; - public bool ColorMaskR; - public bool ColorMaskG; - public bool ColorMaskB; - public bool ColorMaskA; + public ColorMaskRgba ColorMask; + public ColorMaskRgba[] ColorMasks; public bool PrimitiveRestartEnabled; public uint PrimitiveRestartIndex; @@ -69,6 +86,8 @@ { ConstBufferKeys[Stage] = new long[ConstBuffersPerStage]; } + + ColorMasks = new ColorMaskRgba[RenderTargetsCount]; } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/GalTextureFormat.cs b/Ryujinx.Graphics/Gal/GalTextureFormat.cs index 5ad769437c..51ce577970 100644 --- a/Ryujinx.Graphics/Gal/GalTextureFormat.cs +++ b/Ryujinx.Graphics/Gal/GalTextureFormat.cs @@ -2,45 +2,45 @@ namespace Ryujinx.Graphics.Gal { public enum GalTextureFormat { - R32G32B32A32 = 0x1, - R16G16B16A16 = 0x3, - R32G32 = 0x4, - A8B8G8R8 = 0x8, - A2B10G10R10 = 0x9, - R16G16 = 0xc, - R32 = 0xf, - BC6H_SF16 = 0x10, - BC6H_UF16 = 0x11, - A4B4G4R4 = 0x12, - A1B5G5R5 = 0x14, - B5G6R5 = 0x15, - BC7U = 0x17, - G8R8 = 0x18, - R16 = 0x1b, - R8 = 0x1d, - BF10GF11RF11 = 0x21, - BC1 = 0x24, - BC2 = 0x25, - BC3 = 0x26, - BC4 = 0x27, - BC5 = 0x28, - Z24S8 = 0x29, - ZF32 = 0x2f, - ZF32_X24S8 = 0x30, - Z16 = 0x3a, - Astc2D4x4 = 0x40, - Astc2D5x5 = 0x41, - Astc2D6x6 = 0x42, - Astc2D8x8 = 0x44, - Astc2D10x10 = 0x45, - Astc2D12x12 = 0x46, - Astc2D5x4 = 0x50, - Astc2D6x5 = 0x51, - Astc2D8x6 = 0x52, - Astc2D10x8 = 0x53, - Astc2D12x10 = 0x54, - Astc2D8x5 = 0x55, - Astc2D10x5 = 0x56, - Astc2D10x6 = 0x57 + RGBA32 = 0x1, + RGBA16 = 0x3, + RG32 = 0x4, + RGBA8 = 0x8, + RGB10A2 = 0x9, + RG16 = 0xc, + R32 = 0xf, + BptcSfloat = 0x10, + BptcUfloat = 0x11, + RGBA4 = 0x12, + RGB5A1 = 0x14, + RGB565 = 0x15, + BptcUnorm = 0x17, + RG8 = 0x18, + R16 = 0x1b, + R8 = 0x1d, + R11G11B10F = 0x21, + BC1 = 0x24, + BC2 = 0x25, + BC3 = 0x26, + BC4 = 0x27, + BC5 = 0x28, + D24S8 = 0x29, + D32F = 0x2f, + D32FX24S8 = 0x30, + D16 = 0x3a, + Astc2D4x4 = 0x40, + Astc2D5x5 = 0x41, + Astc2D6x6 = 0x42, + Astc2D8x8 = 0x44, + Astc2D10x10 = 0x45, + Astc2D12x12 = 0x46, + Astc2D5x4 = 0x50, + Astc2D6x5 = 0x51, + Astc2D8x6 = 0x52, + Astc2D10x8 = 0x53, + Astc2D12x10 = 0x54, + Astc2D8x5 = 0x55, + Astc2D10x5 = 0x56, + Astc2D10x6 = 0x57 } } diff --git a/Ryujinx.Graphics/Gal/GalZetaFormat.cs b/Ryujinx.Graphics/Gal/GalZetaFormat.cs index 759e312170..2429249e54 100644 --- a/Ryujinx.Graphics/Gal/GalZetaFormat.cs +++ b/Ryujinx.Graphics/Gal/GalZetaFormat.cs @@ -2,15 +2,15 @@ { public enum GalZetaFormat { - Z32Float = 0x0a, - Z16Unorm = 0x13, - S8Z24Unorm = 0x14, - Z24X8Unorm = 0x15, - Z24S8Unorm = 0x16, - Z24C8Unorm = 0x18, - Z32S8X24Float = 0x19, - Z24X8S8C8X16Unorm = 0x1d, - Z32X8C8X16Float = 0x1e, - Z32S8C8X16Float = 0x1f + D32Float = 0x0a, + D16Unorm = 0x13, + S8D24Unorm = 0x14, + D24X8Unorm = 0x15, + D24S8Unorm = 0x16, + D24C8Unorm = 0x18, + D32S8X24Float = 0x19, + D24X8S8C8X16Unorm = 0x1d, + D32X8C8X16Float = 0x1e, + D32S8C8X16Float = 0x1f } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalPipeline.cs b/Ryujinx.Graphics/Gal/IGalPipeline.cs index d8cf266af4..cba4e7dcc6 100644 --- a/Ryujinx.Graphics/Gal/IGalPipeline.cs +++ b/Ryujinx.Graphics/Gal/IGalPipeline.cs @@ -3,5 +3,8 @@ public interface IGalPipeline { void Bind(GalPipelineState State); + + void ResetDepthMask(); + void ResetColorMask(int Index); } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/IGalRasterizer.cs b/Ryujinx.Graphics/Gal/IGalRasterizer.cs index 1572efa8f8..052e3f35f0 100644 --- a/Ryujinx.Graphics/Gal/IGalRasterizer.cs +++ b/Ryujinx.Graphics/Gal/IGalRasterizer.cs @@ -10,7 +10,10 @@ namespace Ryujinx.Graphics.Gal void ClearBuffers( GalClearBufferFlags Flags, int Attachment, - float Red, float Green, float Blue, float Alpha, + float Red, + float Green, + float Blue, + float Alpha, float Depth, int Stencil); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs index 9a3a1a98f6..6b3d4fd691 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLEnumConverter.cs @@ -127,55 +127,54 @@ namespace Ryujinx.Graphics.Gal.OpenGL { switch (Format) { - case GalImageFormat.R32G32B32A32 | GalImageFormat.Sfloat: return (PixelInternalFormat.Rgba32f, PixelFormat.Rgba, PixelType.Float); - case GalImageFormat.R32G32B32A32 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba32i, PixelFormat.RgbaInteger, PixelType.Int); - case GalImageFormat.R32G32B32A32 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba32ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt); - case GalImageFormat.R16G16B16A16 | GalImageFormat.Sfloat: return (PixelInternalFormat.Rgba16f, PixelFormat.Rgba, PixelType.HalfFloat); - case GalImageFormat.R16G16B16A16 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba16i, PixelFormat.RgbaInteger, PixelType.Short); - case GalImageFormat.R16G16B16A16 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba16ui, PixelFormat.RgbaInteger, PixelType.UnsignedShort); - case GalImageFormat.R32G32 | GalImageFormat.Sfloat: return (PixelInternalFormat.Rg32f, PixelFormat.Rg, PixelType.Float); - case GalImageFormat.R32G32 | GalImageFormat.Sint: return (PixelInternalFormat.Rg32i, PixelFormat.RgInteger, PixelType.Int); - case GalImageFormat.R32G32 | GalImageFormat.Uint: return (PixelInternalFormat.Rg32ui, PixelFormat.RgInteger, PixelType.UnsignedInt); - case GalImageFormat.A8B8G8R8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rgba8Snorm, PixelFormat.Rgba, PixelType.Byte); - case GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte); - case GalImageFormat.A8B8G8R8 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba8i, PixelFormat.RgbaInteger, PixelType.Byte); - case GalImageFormat.A8B8G8R8 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba8ui, PixelFormat.RgbaInteger, PixelType.UnsignedByte); - case GalImageFormat.A8B8G8R8_SRGB: return (PixelInternalFormat.Srgb8Alpha8, PixelFormat.Rgba, PixelType.UnsignedByte); - case GalImageFormat.B8G8R8A8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Bgra, PixelType.UnsignedByte); - case GalImageFormat.A4B4G4R4 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba4, PixelFormat.Rgba, PixelType.UnsignedShort4444Reversed); - case GalImageFormat.A2B10G10R10 | GalImageFormat.Uint: return (PixelInternalFormat.Rgb10A2ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt2101010Reversed); - case GalImageFormat.A2B10G10R10 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb10A2, PixelFormat.Rgba, PixelType.UnsignedInt2101010Reversed); - case GalImageFormat.R32 | GalImageFormat.Sfloat: return (PixelInternalFormat.R32f, PixelFormat.Red, PixelType.Float); - case GalImageFormat.R32 | GalImageFormat.Sint: return (PixelInternalFormat.R32i, PixelFormat.Red, PixelType.Int); - case GalImageFormat.R32 | GalImageFormat.Uint: return (PixelInternalFormat.R32ui, PixelFormat.Red, PixelType.UnsignedInt); - case GalImageFormat.A1R5G5B5 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort1555Reversed); - case GalImageFormat.B5G6R5 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba, PixelFormat.Rgb, PixelType.UnsignedShort565Reversed); - case GalImageFormat.R16G16 | GalImageFormat.Sfloat: return (PixelInternalFormat.Rg16f, PixelFormat.Rg, PixelType.HalfFloat); - case GalImageFormat.R16G16 | GalImageFormat.Sint: return (PixelInternalFormat.Rg16i, PixelFormat.RgInteger, PixelType.Short); - case GalImageFormat.R16G16 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg16Snorm, PixelFormat.Rg, PixelType.Byte); - case GalImageFormat.R16G16 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg16, PixelFormat.Rg, PixelType.UnsignedShort); - case GalImageFormat.R8G8 | GalImageFormat.Sint: return (PixelInternalFormat.Rg8i, PixelFormat.RgInteger, PixelType.Byte); - case GalImageFormat.R8G8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg8Snorm, PixelFormat.Rg, PixelType.Byte); - case GalImageFormat.R8G8 | GalImageFormat.Uint: return (PixelInternalFormat.Rg8ui, PixelFormat.RgInteger, PixelType.UnsignedByte); - case GalImageFormat.R8G8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg8, PixelFormat.Rg, PixelType.UnsignedByte); - case GalImageFormat.G8R8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg8Snorm, PixelFormat.Rg, PixelType.Byte); - case GalImageFormat.G8R8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg8, PixelFormat.Rg, PixelType.UnsignedByte); - case GalImageFormat.R16 | GalImageFormat.Sfloat: return (PixelInternalFormat.R16f, PixelFormat.Red, PixelType.HalfFloat); - case GalImageFormat.R16 | GalImageFormat.Sint: return (PixelInternalFormat.R16i, PixelFormat.RedInteger, PixelType.Short); - case GalImageFormat.R16 | GalImageFormat.Snorm: return (PixelInternalFormat.R16Snorm, PixelFormat.Red, PixelType.Byte); - case GalImageFormat.R16 | GalImageFormat.Uint: return (PixelInternalFormat.R16ui, PixelFormat.RedInteger, PixelType.UnsignedShort); - case GalImageFormat.R16 | GalImageFormat.Unorm: return (PixelInternalFormat.R16, PixelFormat.Red, PixelType.UnsignedShort); - case GalImageFormat.R8 | GalImageFormat.Sint: return (PixelInternalFormat.R8i, PixelFormat.RedInteger, PixelType.Byte); - case GalImageFormat.R8 | GalImageFormat.Snorm: return (PixelInternalFormat.R8Snorm, PixelFormat.Red, PixelType.Byte); - case GalImageFormat.R8 | GalImageFormat.Uint: return (PixelInternalFormat.R8ui, PixelFormat.RedInteger, PixelType.UnsignedByte); - case GalImageFormat.R8 | GalImageFormat.Unorm: return (PixelInternalFormat.R8, PixelFormat.Red, PixelType.UnsignedByte); - case GalImageFormat.B10G11R11 | GalImageFormat.Sfloat: return (PixelInternalFormat.R11fG11fB10f, PixelFormat.Rgb, PixelType.UnsignedInt10F11F11FRev); + case GalImageFormat.RGBA32 | GalImageFormat.Float: return (PixelInternalFormat.Rgba32f, PixelFormat.Rgba, PixelType.Float); + case GalImageFormat.RGBA32 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba32i, PixelFormat.RgbaInteger, PixelType.Int); + case GalImageFormat.RGBA32 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba32ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt); + case GalImageFormat.RGBA16 | GalImageFormat.Float: return (PixelInternalFormat.Rgba16f, PixelFormat.Rgba, PixelType.HalfFloat); + case GalImageFormat.RGBA16 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba16i, PixelFormat.RgbaInteger, PixelType.Short); + case GalImageFormat.RGBA16 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba16ui, PixelFormat.RgbaInteger, PixelType.UnsignedShort); + case GalImageFormat.RG32 | GalImageFormat.Float: return (PixelInternalFormat.Rg32f, PixelFormat.Rg, PixelType.Float); + case GalImageFormat.RG32 | GalImageFormat.Sint: return (PixelInternalFormat.Rg32i, PixelFormat.RgInteger, PixelType.Int); + case GalImageFormat.RG32 | GalImageFormat.Uint: return (PixelInternalFormat.Rg32ui, PixelFormat.RgInteger, PixelType.UnsignedInt); + case GalImageFormat.RGBA8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rgba8Snorm, PixelFormat.Rgba, PixelType.Byte); + case GalImageFormat.RGBA8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte); + case GalImageFormat.RGBA8 | GalImageFormat.Sint: return (PixelInternalFormat.Rgba8i, PixelFormat.RgbaInteger, PixelType.Byte); + case GalImageFormat.RGBA8 | GalImageFormat.Uint: return (PixelInternalFormat.Rgba8ui, PixelFormat.RgbaInteger, PixelType.UnsignedByte); + case GalImageFormat.RGBA8 | GalImageFormat.Srgb: return (PixelInternalFormat.Srgb8Alpha8, PixelFormat.Rgba, PixelType.UnsignedByte); + case GalImageFormat.BGRA8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba8, PixelFormat.Bgra, PixelType.UnsignedByte); + case GalImageFormat.RGBA4 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba4, PixelFormat.Rgba, PixelType.UnsignedShort4444Reversed); + case GalImageFormat.RGB10A2 | GalImageFormat.Uint: return (PixelInternalFormat.Rgb10A2ui, PixelFormat.RgbaInteger, PixelType.UnsignedInt2101010Reversed); + case GalImageFormat.RGB10A2 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb10A2, PixelFormat.Rgba, PixelType.UnsignedInt2101010Reversed); + case GalImageFormat.R32 | GalImageFormat.Float: return (PixelInternalFormat.R32f, PixelFormat.Red, PixelType.Float); + case GalImageFormat.R32 | GalImageFormat.Sint: return (PixelInternalFormat.R32i, PixelFormat.Red, PixelType.Int); + case GalImageFormat.R32 | GalImageFormat.Uint: return (PixelInternalFormat.R32ui, PixelFormat.Red, PixelType.UnsignedInt); + case GalImageFormat.RGB5A1 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgb5A1, PixelFormat.Rgba, PixelType.UnsignedShort1555Reversed); + case GalImageFormat.RGB565 | GalImageFormat.Unorm: return (PixelInternalFormat.Rgba, PixelFormat.Rgb, PixelType.UnsignedShort565Reversed); + case GalImageFormat.RG16 | GalImageFormat.Float: return (PixelInternalFormat.Rg16f, PixelFormat.Rg, PixelType.HalfFloat); + case GalImageFormat.RG16 | GalImageFormat.Sint: return (PixelInternalFormat.Rg16i, PixelFormat.RgInteger, PixelType.Short); + case GalImageFormat.RG16 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg16Snorm, PixelFormat.Rg, PixelType.Short); + case GalImageFormat.RG16 | GalImageFormat.Uint: return (PixelInternalFormat.Rg16ui, PixelFormat.RgInteger, PixelType.UnsignedShort); + case GalImageFormat.RG16 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg16, PixelFormat.Rg, PixelType.UnsignedShort); + case GalImageFormat.RG8 | GalImageFormat.Sint: return (PixelInternalFormat.Rg8i, PixelFormat.RgInteger, PixelType.Byte); + case GalImageFormat.RG8 | GalImageFormat.Snorm: return (PixelInternalFormat.Rg8Snorm, PixelFormat.Rg, PixelType.Byte); + case GalImageFormat.RG8 | GalImageFormat.Uint: return (PixelInternalFormat.Rg8ui, PixelFormat.RgInteger, PixelType.UnsignedByte); + case GalImageFormat.RG8 | GalImageFormat.Unorm: return (PixelInternalFormat.Rg8, PixelFormat.Rg, PixelType.UnsignedByte); + case GalImageFormat.R16 | GalImageFormat.Float: return (PixelInternalFormat.R16f, PixelFormat.Red, PixelType.HalfFloat); + case GalImageFormat.R16 | GalImageFormat.Sint: return (PixelInternalFormat.R16i, PixelFormat.RedInteger, PixelType.Short); + case GalImageFormat.R16 | GalImageFormat.Snorm: return (PixelInternalFormat.R16Snorm, PixelFormat.Red, PixelType.Short); + case GalImageFormat.R16 | GalImageFormat.Uint: return (PixelInternalFormat.R16ui, PixelFormat.RedInteger, PixelType.UnsignedShort); + case GalImageFormat.R16 | GalImageFormat.Unorm: return (PixelInternalFormat.R16, PixelFormat.Red, PixelType.UnsignedShort); + case GalImageFormat.R8 | GalImageFormat.Sint: return (PixelInternalFormat.R8i, PixelFormat.RedInteger, PixelType.Byte); + case GalImageFormat.R8 | GalImageFormat.Snorm: return (PixelInternalFormat.R8Snorm, PixelFormat.Red, PixelType.Byte); + case GalImageFormat.R8 | GalImageFormat.Uint: return (PixelInternalFormat.R8ui, PixelFormat.RedInteger, PixelType.UnsignedByte); + case GalImageFormat.R8 | GalImageFormat.Unorm: return (PixelInternalFormat.R8, PixelFormat.Red, PixelType.UnsignedByte); + case GalImageFormat.R11G11B10 | GalImageFormat.Float: return (PixelInternalFormat.R11fG11fB10f, PixelFormat.Rgb, PixelType.UnsignedInt10F11F11FRev); - case GalImageFormat.D24_S8 | GalImageFormat.Uint: return (PixelInternalFormat.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248); - case GalImageFormat.D24_S8 | GalImageFormat.Unorm: return (PixelInternalFormat.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248); - case GalImageFormat.D32 | GalImageFormat.Sfloat: return (PixelInternalFormat.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float); - case GalImageFormat.D16 | GalImageFormat.Unorm: return (PixelInternalFormat.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort); - case GalImageFormat.D32_S8 | GalImageFormat.Sfloat: return (PixelInternalFormat.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev); + case GalImageFormat.D24S8 | GalImageFormat.Uint: return (PixelInternalFormat.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248); + case GalImageFormat.D24S8 | GalImageFormat.Unorm: return (PixelInternalFormat.Depth24Stencil8, PixelFormat.DepthStencil, PixelType.UnsignedInt248); + case GalImageFormat.D32 | GalImageFormat.Float: return (PixelInternalFormat.DepthComponent32f, PixelFormat.DepthComponent, PixelType.Float); + case GalImageFormat.D16 | GalImageFormat.Unorm: return (PixelInternalFormat.DepthComponent16, PixelFormat.DepthComponent, PixelType.UnsignedShort); + case GalImageFormat.D32S8 | GalImageFormat.Float: return (PixelInternalFormat.Depth32fStencil8, PixelFormat.DepthStencil, PixelType.Float32UnsignedInt248Rev); } throw new NotImplementedException($"{Format & GalImageFormat.FormatMask} {Format & GalImageFormat.TypeMask}"); @@ -185,16 +184,20 @@ namespace Ryujinx.Graphics.Gal.OpenGL { switch (Format) { - case GalImageFormat.BC6H_UF16 | GalImageFormat.Sfloat: return InternalFormat.CompressedRgbBptcUnsignedFloat; - case GalImageFormat.BC6H_SF16 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbBptcSignedFloat; - case GalImageFormat.BC7 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaBptcUnorm; - case GalImageFormat.BC1_RGBA | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt1Ext; - case GalImageFormat.BC2 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt3Ext; - case GalImageFormat.BC3 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt5Ext; - case GalImageFormat.BC4 | GalImageFormat.Snorm: return InternalFormat.CompressedSignedRedRgtc1; - case GalImageFormat.BC4 | GalImageFormat.Unorm: return InternalFormat.CompressedRedRgtc1; - case GalImageFormat.BC5 | GalImageFormat.Snorm: return InternalFormat.CompressedSignedRgRgtc2; - case GalImageFormat.BC5 | GalImageFormat.Unorm: return InternalFormat.CompressedRgRgtc2; + case GalImageFormat.BptcSfloat | GalImageFormat.Float: return InternalFormat.CompressedRgbBptcSignedFloat; + case GalImageFormat.BptcUfloat | GalImageFormat.Float: return InternalFormat.CompressedRgbBptcUnsignedFloat; + case GalImageFormat.BptcUnorm | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaBptcUnorm; + case GalImageFormat.BptcUnorm | GalImageFormat.Srgb: return InternalFormat.CompressedSrgbAlphaBptcUnorm; + case GalImageFormat.BC1 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt1Ext; + case GalImageFormat.BC1 | GalImageFormat.Srgb: return InternalFormat.CompressedSrgbAlphaS3tcDxt1Ext; + case GalImageFormat.BC2 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt3Ext; + case GalImageFormat.BC2 | GalImageFormat.Srgb: return InternalFormat.CompressedSrgbAlphaS3tcDxt3Ext; + case GalImageFormat.BC3 | GalImageFormat.Unorm: return InternalFormat.CompressedRgbaS3tcDxt5Ext; + case GalImageFormat.BC3 | GalImageFormat.Srgb: return InternalFormat.CompressedSrgbAlphaS3tcDxt5Ext; + case GalImageFormat.BC4 | GalImageFormat.Snorm: return InternalFormat.CompressedSignedRedRgtc1; + case GalImageFormat.BC4 | GalImageFormat.Unorm: return InternalFormat.CompressedRedRgtc1; + case GalImageFormat.BC5 | GalImageFormat.Snorm: return InternalFormat.CompressedSignedRgRgtc2; + case GalImageFormat.BC5 | GalImageFormat.Unorm: return InternalFormat.CompressedRgRgtc2; } throw new NotImplementedException($"{Format & GalImageFormat.FormatMask} {Format & GalImageFormat.TypeMask}"); diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs index b7825996e5..00699641fd 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLPipeline.cs @@ -129,9 +129,16 @@ namespace Ryujinx.Graphics.Gal.OpenGL BlendFuncSrcAlpha = GalBlendFactor.One, BlendFuncDstAlpha = GalBlendFactor.Zero, + ColorMask = ColorMaskRgba.Default, + PrimitiveRestartEnabled = false, PrimitiveRestartIndex = 0 }; + + for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++) + { + Old.ColorMasks[Index] = ColorMaskRgba.Default; + } } public void Bind(GalPipelineState New) @@ -177,8 +184,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL if (New.DepthWriteEnabled != Old.DepthWriteEnabled) { - Rasterizer.DepthWriteEnabled = New.DepthWriteEnabled; - GL.DepthMask(New.DepthWriteEnabled); } @@ -303,16 +308,17 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } - if (New.ColorMaskR != Old.ColorMaskR || - New.ColorMaskG != Old.ColorMaskG || - New.ColorMaskB != Old.ColorMaskB || - New.ColorMaskA != Old.ColorMaskA) + for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++) { - GL.ColorMask( - New.ColorMaskR, - New.ColorMaskG, - New.ColorMaskB, - New.ColorMaskA); + if (!New.ColorMasks[Index].Equals(Old.ColorMasks[Index])) + { + GL.ColorMask( + Index, + New.ColorMasks[Index].Red, + New.ColorMasks[Index].Green, + New.ColorMasks[Index].Blue, + New.ColorMasks[Index].Alpha); + } } if (New.PrimitiveRestartEnabled != Old.PrimitiveRestartEnabled) @@ -613,5 +619,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.Disable(Cap); } } + + public void ResetDepthMask() + { + Old.DepthWriteEnabled = true; + } + + public void ResetColorMask(int Index) + { + Old.ColorMasks[Index] = ColorMaskRgba.Default; + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs index 7b435c455f..cefbb2d2a1 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRasterizer.cs @@ -5,8 +5,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL { class OGLRasterizer : IGalRasterizer { - public bool DepthWriteEnabled { set; private get; } - private int[] VertexBuffers; private OGLCachedResource VboCache; @@ -30,8 +28,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL IboCache = new OGLCachedResource(GL.DeleteBuffer); IndexBuffer = new IbInfo(); - - DepthWriteEnabled = true; } public void LockCaches() @@ -49,17 +45,15 @@ namespace Ryujinx.Graphics.Gal.OpenGL public void ClearBuffers( GalClearBufferFlags Flags, int Attachment, - float Red, float Green, float Blue, float Alpha, + float Red, + float Green, + float Blue, + float Alpha, float Depth, int Stencil) { - //OpenGL needs glDepthMask to be enabled to clear it - if (!DepthWriteEnabled) - { - GL.DepthMask(true); - } - GL.ColorMask( + Attachment, Flags.HasFlag(GalClearBufferFlags.ColorRed), Flags.HasFlag(GalClearBufferFlags.ColorGreen), Flags.HasFlag(GalClearBufferFlags.ColorBlue), @@ -67,6 +61,9 @@ namespace Ryujinx.Graphics.Gal.OpenGL GL.ClearBuffer(ClearBuffer.Color, Attachment, new float[] { Red, Green, Blue, Alpha }); + GL.ColorMask(Attachment, true, true, true, true); + GL.DepthMask(true); + if (Flags.HasFlag(GalClearBufferFlags.Depth)) { GL.ClearBuffer(ClearBuffer.Depth, 0, ref Depth); @@ -76,13 +73,6 @@ namespace Ryujinx.Graphics.Gal.OpenGL { GL.ClearBuffer(ClearBuffer.Stencil, 0, ref Stencil); } - - GL.ColorMask(true, true, true, true); - - if (!DepthWriteEnabled) - { - GL.DepthMask(false); - } } public bool IsVboCached(long Key, long DataSize) diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs index 0de070b545..e0c4854ed2 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLRenderTarget.cs @@ -59,9 +59,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL private const int NativeWidth = 1280; private const int NativeHeight = 720; - private const int RenderTargetsCount = 8; - - private const GalImageFormat RawFormat = GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm; + private const int RenderTargetsCount = GalPipelineState.RenderTargetsCount; private OGLTexture Texture; @@ -115,15 +113,16 @@ namespace Ryujinx.Graphics.Gal.OpenGL for (int Attachment = 0; Attachment < RenderTargetsCount; Attachment++) { - if (Attachments.Colors[Attachment] == OldAttachments.Colors[Attachment]) + long Key = Attachments.Colors[Attachment]; + + if (Key == OldAttachments.Colors[Attachment]) { continue; } int Handle = 0; - if (Attachments.Colors[Attachment] != 0 && - Texture.TryGetImageHandler(Attachments.Colors[Attachment], out CachedImage)) + if (Key != 0 && Texture.TryGetImageHandler(Key, out CachedImage)) { Handle = CachedImage.Handle; } @@ -178,7 +177,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL if (OGLExtension.ViewportArray) { - GL.ViewportArray(0, 8, Viewports); + GL.ViewportArray(0, RenderTargetsCount, Viewports); } else { diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs index efcb7e3471..b45a3a3a5a 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLShader.cs @@ -55,16 +55,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL GlslDecompiler Decompiler = new GlslDecompiler(); + int ShaderDumpIndex = ShaderDumper.DumpIndex; + if (IsDualVp) { ShaderDumper.Dump(Memory, Position, Type, "a"); ShaderDumper.Dump(Memory, PositionB, Type, "b"); - Program = Decompiler.Decompile( - Memory, - Position, - PositionB, - Type); + Program = Decompiler.Decompile(Memory, Position, PositionB, Type); } else { @@ -73,11 +71,14 @@ namespace Ryujinx.Graphics.Gal.OpenGL Program = Decompiler.Decompile(Memory, Position, Type); } - return new OGLShaderStage( - Type, - Program.Code, - Program.Uniforms, - Program.Textures); + string Code = Program.Code; + + if (ShaderDumper.IsDumpEnabled()) + { + Code = "//Shader " + ShaderDumpIndex + Environment.NewLine + Code; + } + + return new OGLShaderStage(Type, Code, Program.Uniforms, Program.Textures); } public IEnumerable GetConstBufferUsage(long Key) diff --git a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs index 3347afbd1c..6c60852801 100644 --- a/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs +++ b/Ryujinx.Graphics/Gal/OpenGL/OGLTexture.cs @@ -39,41 +39,25 @@ namespace Ryujinx.Graphics.Gal.OpenGL TextureCache.AddOrUpdate(Key, new ImageHandler(Handle, Image), (uint)Size); - GalImageFormat TypeLess = Image.Format & GalImageFormat.FormatMask; - - bool IsASTC = TypeLess >= GalImageFormat.ASTC_BEGIN && TypeLess <= GalImageFormat.ASTC_END; - - if (ImageUtils.IsCompressed(Image.Format) && !IsASTC) + if (ImageUtils.IsCompressed(Image.Format)) { - InternalFormat InternalFmt = OGLEnumConverter.GetCompressedImageFormat(Image.Format); - - GL.CompressedTexImage2D( - TextureTarget.Texture2D, - Level, - InternalFmt, - Image.Width, - Image.Height, - Border, - Size, - IntPtr.Zero); + throw new InvalidOperationException("Surfaces with compressed formats are not supported!"); } - else - { - (PixelInternalFormat InternalFmt, - PixelFormat Format, - PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format); - GL.TexImage2D( - TextureTarget.Texture2D, - Level, - InternalFmt, - Image.Width, - Image.Height, - Border, - Format, - Type, - IntPtr.Zero); - } + (PixelInternalFormat InternalFmt, + PixelFormat Format, + PixelType Type) = OGLEnumConverter.GetImageFormat(Image.Format); + + GL.TexImage2D( + TextureTarget.Texture2D, + Level, + InternalFmt, + Image.Width, + Image.Height, + Border, + Format, + Type, + IntPtr.Zero); } public void Create(long Key, byte[] Data, GalImage Image) @@ -87,11 +71,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL TextureCache.AddOrUpdate(Key, new ImageHandler(Handle, Image), (uint)Data.Length); - GalImageFormat TypeLess = Image.Format & GalImageFormat.FormatMask; - - bool IsASTC = TypeLess >= GalImageFormat.ASTC_BEGIN && TypeLess <= GalImageFormat.ASTC_END; - - if (ImageUtils.IsCompressed(Image.Format) && !IsASTC) + if (ImageUtils.IsCompressed(Image.Format) && !IsAstc(Image.Format)) { InternalFormat InternalFmt = OGLEnumConverter.GetCompressedImageFormat(Image.Format); @@ -108,7 +88,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL else { //TODO: Use KHR_texture_compression_astc_hdr when available - if (IsASTC) + if (IsAstc(Image.Format)) { int TextureBlockWidth = ImageUtils.GetBlockWidth(Image.Format); int TextureBlockHeight = ImageUtils.GetBlockHeight(Image.Format); @@ -120,17 +100,7 @@ namespace Ryujinx.Graphics.Gal.OpenGL Image.Width, Image.Height, 1); - Image.Format = GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm; - } - else if (TypeLess == GalImageFormat.G8R8) - { - Data = ImageConverter.G8R8ToR8G8( - Data, - Image.Width, - Image.Height, - 1); - - Image.Format = GalImageFormat.R8G8 | (Image.Format & GalImageFormat.TypeMask); + Image.Format = GalImageFormat.RGBA8 | GalImageFormat.Unorm; } (PixelInternalFormat InternalFmt, @@ -150,6 +120,13 @@ namespace Ryujinx.Graphics.Gal.OpenGL } } + private static bool IsAstc(GalImageFormat Format) + { + Format &= GalImageFormat.FormatMask; + + return Format > GalImageFormat.Astc2DStart && Format < GalImageFormat.Astc2DEnd; + } + public bool TryGetImage(long Key, out GalImage Image) { if (TextureCache.TryGetValue(Key, out ImageHandler CachedImage)) diff --git a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs index ac6ae8d5d1..f8c07f31ab 100644 --- a/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs +++ b/Ryujinx.Graphics/Gal/Shader/ShaderDecoder.cs @@ -69,7 +69,7 @@ namespace Ryujinx.Graphics.Gal.Shader { int Target = ((ShaderIrOperImm)CurrOp.OperandA).Value; - Current.Branch = Enqueue(Target, Current); + Enqueue(Target, Current); } } @@ -165,7 +165,7 @@ namespace Ryujinx.Graphics.Gal.Shader DbgOpCode += (Decode?.Method.Name ?? "???"); - if (Decode == ShaderDecode.Bra) + if (Decode == ShaderDecode.Bra || Decode == ShaderDecode.Ssy) { int Offset = ((int)(OpCode >> 20) << 8) >> 8; diff --git a/Ryujinx.Graphics/Gal/ShaderDumper.cs b/Ryujinx.Graphics/Gal/ShaderDumper.cs index 541368e89b..d3bcbf0d87 100644 --- a/Ryujinx.Graphics/Gal/ShaderDumper.cs +++ b/Ryujinx.Graphics/Gal/ShaderDumper.cs @@ -7,11 +7,11 @@ namespace Ryujinx.Graphics.Gal { private static string RuntimeDir; - private static int DumpIndex = 1; + public static int DumpIndex { get; private set; } = 1; public static void Dump(IGalMemory Memory, long Position, GalShaderType Type, string ExtSuffix = "") { - if (string.IsNullOrWhiteSpace(GraphicsConfig.ShadersDumpPath)) + if (!IsDumpEnabled()) { return; } @@ -25,9 +25,10 @@ namespace Ryujinx.Graphics.Gal using (FileStream FullFile = File.Create(FullPath)) using (FileStream CodeFile = File.Create(CodePath)) - using (BinaryWriter FullWriter = new BinaryWriter(FullFile)) - using (BinaryWriter CodeWriter = new BinaryWriter(CodeFile)) { + BinaryWriter FullWriter = new BinaryWriter(FullFile); + BinaryWriter CodeWriter = new BinaryWriter(CodeFile); + for (long i = 0; i < 0x50; i += 4) { FullWriter.Write(Memory.ReadInt32(Position + i)); @@ -69,6 +70,11 @@ namespace Ryujinx.Graphics.Gal } } + public static bool IsDumpEnabled() + { + return !string.IsNullOrWhiteSpace(GraphicsConfig.ShadersDumpPath); + } + private static string FullDir() { return CreateAndReturn(Path.Combine(DumpDir(), "Full")); diff --git a/Ryujinx.Graphics/NvGpuEngine3d.cs b/Ryujinx.Graphics/NvGpuEngine3d.cs index a2a9692802..fd15d0b6a2 100644 --- a/Ryujinx.Graphics/NvGpuEngine3d.cs +++ b/Ryujinx.Graphics/NvGpuEngine3d.cs @@ -141,7 +141,7 @@ namespace Ryujinx.Graphics { int Arg0 = PBEntry.Arguments[0]; - int FbIndex = (Arg0 >> 6) & 0xf; + int Attachment = (Arg0 >> 6) & 0xf; GalClearBufferFlags Flags = (GalClearBufferFlags)(Arg0 & 0x3f); @@ -154,7 +154,7 @@ namespace Ryujinx.Graphics int Stencil = ReadRegister(NvGpuEngine3dReg.ClearStencil); - SetFrameBuffer(Vmm, FbIndex); + SetFrameBuffer(Vmm, Attachment); SetZeta(Vmm); @@ -162,12 +162,10 @@ namespace Ryujinx.Graphics Gpu.Renderer.RenderTarget.Bind(); - Gpu.Renderer.Rasterizer.ClearBuffers( - Flags, - FbIndex, - Red, Green, Blue, Alpha, - Depth, - Stencil); + Gpu.Renderer.Rasterizer.ClearBuffers(Flags, Attachment, Red, Green, Blue, Alpha, Depth, Stencil); + + Gpu.Renderer.Pipeline.ResetDepthMask(); + Gpu.Renderer.Pipeline.ResetColorMask(Attachment); } private void SetFrameBuffer(NvGpuVmm Vmm, int FbIndex) @@ -345,13 +343,8 @@ namespace Ryujinx.Graphics { switch (FrontFace) { - case GalFrontFace.CW: - FrontFace = GalFrontFace.CCW; - break; - - case GalFrontFace.CCW: - FrontFace = GalFrontFace.CW; - break; + case GalFrontFace.CW: FrontFace = GalFrontFace.CCW; break; + case GalFrontFace.CCW: FrontFace = GalFrontFace.CW; break; } } @@ -426,10 +419,20 @@ namespace Ryujinx.Graphics { int ColorMask = ReadRegister(NvGpuEngine3dReg.ColorMask); - State.ColorMaskR = ((ColorMask >> 0) & 0xf) != 0; - State.ColorMaskG = ((ColorMask >> 4) & 0xf) != 0; - State.ColorMaskB = ((ColorMask >> 8) & 0xf) != 0; - State.ColorMaskA = ((ColorMask >> 12) & 0xf) != 0; + State.ColorMask.Red = ((ColorMask >> 0) & 0xf) != 0; + State.ColorMask.Green = ((ColorMask >> 4) & 0xf) != 0; + State.ColorMask.Blue = ((ColorMask >> 8) & 0xf) != 0; + State.ColorMask.Alpha = ((ColorMask >> 12) & 0xf) != 0; + + for (int Index = 0; Index < GalPipelineState.RenderTargetsCount; Index++) + { + ColorMask = ReadRegister(NvGpuEngine3dReg.ColorMaskN + Index); + + State.ColorMasks[Index].Red = ((ColorMask >> 0) & 0xf) != 0; + State.ColorMasks[Index].Green = ((ColorMask >> 4) & 0xf) != 0; + State.ColorMasks[Index].Blue = ((ColorMask >> 8) & 0xf) != 0; + State.ColorMasks[Index].Alpha = ((ColorMask >> 12) & 0xf) != 0; + } } private void SetPrimitiveRestart(GalPipelineState State) @@ -455,11 +458,11 @@ namespace Ryujinx.Graphics { int[] Map = new int[Count]; - for (int i = 0; i < Count; i++) + for (int Index = 0; Index < Count; Index++) { - int Shift = 4 + i * 3; + int Shift = 4 + Index * 3; - Map[i] = (int)((Control >> Shift) & 7); + Map[Index] = (int)((Control >> Shift) & 7); } Gpu.Renderer.RenderTarget.SetMap(Map); diff --git a/Ryujinx.Graphics/NvGpuEngine3dReg.cs b/Ryujinx.Graphics/NvGpuEngine3dReg.cs index ba211313e7..ef74e4f675 100644 --- a/Ryujinx.Graphics/NvGpuEngine3dReg.cs +++ b/Ryujinx.Graphics/NvGpuEngine3dReg.cs @@ -23,6 +23,7 @@ namespace Ryujinx.Graphics StencilBackFuncRef = 0x3d5, StencilBackMask = 0x3d6, StencilBackFuncMask = 0x3d7, + ColorMask = 0x3e4, RTSeparateFragData = 0x3eb, ZetaAddress = 0x3f8, ZetaFormat = 0x3fa, @@ -78,7 +79,7 @@ namespace Ryujinx.Graphics CullFaceEnable = 0x646, FrontFace = 0x647, CullFace = 0x648, - ColorMask = 0x680, + ColorMaskN = 0x680, QueryAddress = 0x6c0, QuerySequence = 0x6c2, QueryControl = 0x6c3, diff --git a/Ryujinx.Graphics/Texture/ImageConverter.cs b/Ryujinx.Graphics/Texture/ImageConverter.cs deleted file mode 100644 index 89529061fd..0000000000 --- a/Ryujinx.Graphics/Texture/ImageConverter.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace Ryujinx.Graphics.Texture -{ - static class ImageConverter - { - public static byte[] G8R8ToR8G8( - byte[] Data, - int Width, - int Height, - int Depth) - { - int Texels = Width * Height * Depth; - - byte[] Output = new byte[Texels * 2]; - - for (int Texel = 0; Texel < Texels; Texel++) - { - Output[Texel * 2 + 0] = Data[Texel * 2 + 1]; - Output[Texel * 2 + 1] = Data[Texel * 2 + 0]; - } - - return Output; - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics/Texture/ImageUtils.cs b/Ryujinx.Graphics/Texture/ImageUtils.cs index 2c4e7b4b0a..d2172c2ef8 100644 --- a/Ryujinx.Graphics/Texture/ImageUtils.cs +++ b/Ryujinx.Graphics/Texture/ImageUtils.cs @@ -35,106 +35,105 @@ namespace Ryujinx.Graphics.Texture } } - private const GalImageFormat Snorm = GalImageFormat.Snorm; - private const GalImageFormat Unorm = GalImageFormat.Unorm; - private const GalImageFormat Sint = GalImageFormat.Sint; - private const GalImageFormat Uint = GalImageFormat.Uint; - private const GalImageFormat Sfloat = GalImageFormat.Sfloat; + private const GalImageFormat Snorm = GalImageFormat.Snorm; + private const GalImageFormat Unorm = GalImageFormat.Unorm; + private const GalImageFormat Sint = GalImageFormat.Sint; + private const GalImageFormat Uint = GalImageFormat.Uint; + private const GalImageFormat Float = GalImageFormat.Float; + private const GalImageFormat Srgb = GalImageFormat.Srgb; private static readonly Dictionary s_TextureTable = new Dictionary() - { - { GalTextureFormat.R32G32B32A32, GalImageFormat.R32G32B32A32 | Sint | Uint | Sfloat }, - { GalTextureFormat.R16G16B16A16, GalImageFormat.R16G16B16A16 | Snorm | Unorm | Sint | Uint | Sfloat }, - { GalTextureFormat.R32G32, GalImageFormat.R32G32 | Sint | Uint | Sfloat }, - { GalTextureFormat.A8B8G8R8, GalImageFormat.A8B8G8R8 | Snorm | Unorm | Sint | Uint }, - { GalTextureFormat.A2B10G10R10, GalImageFormat.A2B10G10R10 | Snorm | Unorm | Sint | Uint }, - { GalTextureFormat.G8R8, GalImageFormat.G8R8 | Snorm | Unorm | Sint | Uint }, - { GalTextureFormat.R16, GalImageFormat.R16 | Snorm | Unorm | Sint | Uint | Sfloat }, - { GalTextureFormat.R8, GalImageFormat.R8 | Snorm | Unorm | Sint | Uint }, - { GalTextureFormat.R16G16, GalImageFormat.R16G16 | Snorm | Unorm | Sfloat }, - { GalTextureFormat.R32, GalImageFormat.R32 | Sint | Uint | Sfloat }, - { GalTextureFormat.A4B4G4R4, GalImageFormat.A4B4G4R4 | Unorm }, - { GalTextureFormat.A1B5G5R5, GalImageFormat.A1R5G5B5 | Unorm }, - { GalTextureFormat.B5G6R5, GalImageFormat.B5G6R5 | Unorm }, - { GalTextureFormat.BF10GF11RF11, GalImageFormat.B10G11R11 | Sfloat }, - { GalTextureFormat.Z24S8, GalImageFormat.D24_S8 | Unorm | Uint }, - { GalTextureFormat.ZF32, GalImageFormat.D32 | Sfloat }, - { GalTextureFormat.ZF32_X24S8, GalImageFormat.D32_S8 | Unorm }, - { GalTextureFormat.Z16, GalImageFormat.D16 | Unorm }, + { + { GalTextureFormat.RGBA32, GalImageFormat.RGBA32 | Sint | Uint | Float }, + { GalTextureFormat.RGBA16, GalImageFormat.RGBA16 | Snorm | Unorm | Sint | Uint | Float }, + { GalTextureFormat.RG32, GalImageFormat.RG32 | Sint | Uint | Float }, + { GalTextureFormat.RGBA8, GalImageFormat.RGBA8 | Snorm | Unorm | Sint | Uint | Srgb }, + { GalTextureFormat.RGB10A2, GalImageFormat.RGB10A2 | Snorm | Unorm | Sint | Uint }, + { GalTextureFormat.RG8, GalImageFormat.RG8 | Snorm | Unorm | Sint | Uint }, + { GalTextureFormat.R16, GalImageFormat.R16 | Snorm | Unorm | Sint | Uint | Float }, + { GalTextureFormat.R8, GalImageFormat.R8 | Snorm | Unorm | Sint | Uint }, + { GalTextureFormat.RG16, GalImageFormat.RG16 | Snorm | Unorm | Float }, + { GalTextureFormat.R32, GalImageFormat.R32 | Sint | Uint | Float }, + { GalTextureFormat.RGBA4, GalImageFormat.RGBA4 | Unorm }, + { GalTextureFormat.RGB5A1, GalImageFormat.RGB5A1 | Unorm }, + { GalTextureFormat.RGB565, GalImageFormat.RGB565 | Unorm }, + { GalTextureFormat.R11G11B10F, GalImageFormat.R11G11B10 | Float }, + { GalTextureFormat.D24S8, GalImageFormat.D24S8 | Unorm | Uint }, + { GalTextureFormat.D32F, GalImageFormat.D32 | Float }, + { GalTextureFormat.D32FX24S8, GalImageFormat.D32S8 | Unorm }, + { GalTextureFormat.D16, GalImageFormat.D16 | Unorm }, - //Compressed formats - { GalTextureFormat.BC6H_SF16, GalImageFormat.BC6H_SF16 | Unorm }, - { GalTextureFormat.BC6H_UF16, GalImageFormat.BC6H_UF16 | Sfloat }, - { GalTextureFormat.BC7U, GalImageFormat.BC7 | Unorm }, - { GalTextureFormat.BC1, GalImageFormat.BC1_RGBA | Unorm }, - { GalTextureFormat.BC2, GalImageFormat.BC2 | Unorm }, - { GalTextureFormat.BC3, GalImageFormat.BC3 | Unorm }, - { GalTextureFormat.BC4, GalImageFormat.BC4 | Unorm | Snorm }, - { GalTextureFormat.BC5, GalImageFormat.BC5 | Unorm | Snorm }, - { GalTextureFormat.Astc2D4x4, GalImageFormat.ASTC_4x4 | Unorm }, - { GalTextureFormat.Astc2D5x5, GalImageFormat.ASTC_5x5 | Unorm }, - { GalTextureFormat.Astc2D6x6, GalImageFormat.ASTC_6x6 | Unorm }, - { GalTextureFormat.Astc2D8x8, GalImageFormat.ASTC_8x8 | Unorm }, - { GalTextureFormat.Astc2D10x10, GalImageFormat.ASTC_10x10 | Unorm }, - { GalTextureFormat.Astc2D12x12, GalImageFormat.ASTC_12x12 | Unorm }, - { GalTextureFormat.Astc2D5x4, GalImageFormat.ASTC_5x4 | Unorm }, - { GalTextureFormat.Astc2D6x5, GalImageFormat.ASTC_6x5 | Unorm }, - { GalTextureFormat.Astc2D8x6, GalImageFormat.ASTC_8x6 | Unorm }, - { GalTextureFormat.Astc2D10x8, GalImageFormat.ASTC_10x8 | Unorm }, - { GalTextureFormat.Astc2D12x10, GalImageFormat.ASTC_12x10 | Unorm }, - { GalTextureFormat.Astc2D8x5, GalImageFormat.ASTC_8x5 | Unorm }, - { GalTextureFormat.Astc2D10x5, GalImageFormat.ASTC_10x5 | Unorm }, - { GalTextureFormat.Astc2D10x6, GalImageFormat.ASTC_10x6 | Unorm } - }; + //Compressed formats + { GalTextureFormat.BptcSfloat, GalImageFormat.BptcSfloat | Float }, + { GalTextureFormat.BptcUfloat, GalImageFormat.BptcUfloat | Float }, + { GalTextureFormat.BptcUnorm, GalImageFormat.BptcUnorm | Unorm | Srgb }, + { GalTextureFormat.BC1, GalImageFormat.BC1 | Unorm | Srgb }, + { GalTextureFormat.BC2, GalImageFormat.BC2 | Unorm | Srgb }, + { GalTextureFormat.BC3, GalImageFormat.BC3 | Unorm | Srgb }, + { GalTextureFormat.BC4, GalImageFormat.BC4 | Unorm | Snorm }, + { GalTextureFormat.BC5, GalImageFormat.BC5 | Unorm | Snorm }, + { GalTextureFormat.Astc2D4x4, GalImageFormat.Astc2D4x4 | Unorm | Srgb }, + { GalTextureFormat.Astc2D5x5, GalImageFormat.Astc2D5x5 | Unorm | Srgb }, + { GalTextureFormat.Astc2D6x6, GalImageFormat.Astc2D6x6 | Unorm | Srgb }, + { GalTextureFormat.Astc2D8x8, GalImageFormat.Astc2D8x8 | Unorm | Srgb }, + { GalTextureFormat.Astc2D10x10, GalImageFormat.Astc2D10x10 | Unorm | Srgb }, + { GalTextureFormat.Astc2D12x12, GalImageFormat.Astc2D12x12 | Unorm | Srgb }, + { GalTextureFormat.Astc2D5x4, GalImageFormat.Astc2D5x4 | Unorm | Srgb }, + { GalTextureFormat.Astc2D6x5, GalImageFormat.Astc2D6x5 | Unorm | Srgb }, + { GalTextureFormat.Astc2D8x6, GalImageFormat.Astc2D8x6 | Unorm | Srgb }, + { GalTextureFormat.Astc2D10x8, GalImageFormat.Astc2D10x8 | Unorm | Srgb }, + { GalTextureFormat.Astc2D12x10, GalImageFormat.Astc2D12x10 | Unorm | Srgb }, + { GalTextureFormat.Astc2D8x5, GalImageFormat.Astc2D8x5 | Unorm | Srgb }, + { GalTextureFormat.Astc2D10x5, GalImageFormat.Astc2D10x5 | Unorm | Srgb }, + { GalTextureFormat.Astc2D10x6, GalImageFormat.Astc2D10x6 | Unorm | Srgb } + }; private static readonly Dictionary s_ImageTable = new Dictionary() { - { GalImageFormat.R32G32B32A32, new ImageDescriptor(16, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.R16G16B16A16, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.R32G32, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.B8G8R8A8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.A8B8G8R8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.A2B10G10R10, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.R32, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.A4B4G4R4, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.BC6H_SF16, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.BC6H_UF16, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.A1R5G5B5, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.B5G6R5, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.BC7, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.R16G16, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.R8G8, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.G8R8, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.R16, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.R8, new ImageDescriptor(1, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.B10G11R11, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.A8B8G8R8_SRGB, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, - { GalImageFormat.BC1_RGBA, new ImageDescriptor(8, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.BC2, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.BC3, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.BC4, new ImageDescriptor(8, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.BC5, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.ASTC_4x4, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, - { GalImageFormat.ASTC_5x5, new ImageDescriptor(16, 5, 5, TargetBuffer.Color) }, - { GalImageFormat.ASTC_6x6, new ImageDescriptor(16, 6, 6, TargetBuffer.Color) }, - { GalImageFormat.ASTC_8x8, new ImageDescriptor(16, 8, 8, TargetBuffer.Color) }, - { GalImageFormat.ASTC_10x10, new ImageDescriptor(16, 10, 10, TargetBuffer.Color) }, - { GalImageFormat.ASTC_12x12, new ImageDescriptor(16, 12, 12, TargetBuffer.Color) }, - { GalImageFormat.ASTC_5x4, new ImageDescriptor(16, 5, 4, TargetBuffer.Color) }, - { GalImageFormat.ASTC_6x5, new ImageDescriptor(16, 6, 5, TargetBuffer.Color) }, - { GalImageFormat.ASTC_8x6, new ImageDescriptor(16, 8, 6, TargetBuffer.Color) }, - { GalImageFormat.ASTC_10x8, new ImageDescriptor(16, 10, 8, TargetBuffer.Color) }, - { GalImageFormat.ASTC_12x10, new ImageDescriptor(16, 12, 10, TargetBuffer.Color) }, - { GalImageFormat.ASTC_8x5, new ImageDescriptor(16, 8, 5, TargetBuffer.Color) }, - { GalImageFormat.ASTC_10x5, new ImageDescriptor(16, 10, 5, TargetBuffer.Color) }, - { GalImageFormat.ASTC_10x6, new ImageDescriptor(16, 10, 6, TargetBuffer.Color) }, + { GalImageFormat.RGBA32, new ImageDescriptor(16, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RGBA16, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RG32, new ImageDescriptor(8, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RGBA8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.BGRA8, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RGB10A2, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.R32, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RGBA4, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.BptcSfloat, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, + { GalImageFormat.BptcUfloat, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, + { GalImageFormat.RGB5A1, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RGB565, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.BptcUnorm, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, + { GalImageFormat.RG16, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.RG8, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.R16, new ImageDescriptor(2, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.R8, new ImageDescriptor(1, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.R11G11B10, new ImageDescriptor(4, 1, 1, TargetBuffer.Color) }, + { GalImageFormat.BC1, new ImageDescriptor(8, 4, 4, TargetBuffer.Color) }, + { GalImageFormat.BC2, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, + { GalImageFormat.BC3, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, + { GalImageFormat.BC4, new ImageDescriptor(8, 4, 4, TargetBuffer.Color) }, + { GalImageFormat.BC5, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, + { GalImageFormat.Astc2D4x4, new ImageDescriptor(16, 4, 4, TargetBuffer.Color) }, + { GalImageFormat.Astc2D5x5, new ImageDescriptor(16, 5, 5, TargetBuffer.Color) }, + { GalImageFormat.Astc2D6x6, new ImageDescriptor(16, 6, 6, TargetBuffer.Color) }, + { GalImageFormat.Astc2D8x8, new ImageDescriptor(16, 8, 8, TargetBuffer.Color) }, + { GalImageFormat.Astc2D10x10, new ImageDescriptor(16, 10, 10, TargetBuffer.Color) }, + { GalImageFormat.Astc2D12x12, new ImageDescriptor(16, 12, 12, TargetBuffer.Color) }, + { GalImageFormat.Astc2D5x4, new ImageDescriptor(16, 5, 4, TargetBuffer.Color) }, + { GalImageFormat.Astc2D6x5, new ImageDescriptor(16, 6, 5, TargetBuffer.Color) }, + { GalImageFormat.Astc2D8x6, new ImageDescriptor(16, 8, 6, TargetBuffer.Color) }, + { GalImageFormat.Astc2D10x8, new ImageDescriptor(16, 10, 8, TargetBuffer.Color) }, + { GalImageFormat.Astc2D12x10, new ImageDescriptor(16, 12, 10, TargetBuffer.Color) }, + { GalImageFormat.Astc2D8x5, new ImageDescriptor(16, 8, 5, TargetBuffer.Color) }, + { GalImageFormat.Astc2D10x5, new ImageDescriptor(16, 10, 5, TargetBuffer.Color) }, + { GalImageFormat.Astc2D10x6, new ImageDescriptor(16, 10, 6, TargetBuffer.Color) }, - { GalImageFormat.D24_S8, new ImageDescriptor(4, 1, 1, TargetBuffer.DepthStencil) }, - { GalImageFormat.D32, new ImageDescriptor(4, 1, 1, TargetBuffer.Depth) }, - { GalImageFormat.D16, new ImageDescriptor(2, 1, 1, TargetBuffer.Depth) }, - { GalImageFormat.D32_S8, new ImageDescriptor(8, 1, 1, TargetBuffer.DepthStencil) }, + { GalImageFormat.D24S8, new ImageDescriptor(4, 1, 1, TargetBuffer.DepthStencil) }, + { GalImageFormat.D32, new ImageDescriptor(4, 1, 1, TargetBuffer.Depth) }, + { GalImageFormat.D16, new ImageDescriptor(2, 1, 1, TargetBuffer.Depth) }, + { GalImageFormat.D32S8, new ImageDescriptor(8, 1, 1, TargetBuffer.DepthStencil) } }; public static GalImageFormat ConvertTexture( @@ -142,64 +141,62 @@ namespace Ryujinx.Graphics.Texture GalTextureType RType, GalTextureType GType, GalTextureType BType, - GalTextureType AType) + GalTextureType AType, + bool ConvSrgb) { if (RType != GType || RType != BType || RType != AType) { - throw new NotImplementedException("Per component types are not implemented"); + throw new NotImplementedException("Per component types are not implemented!"); } if (!s_TextureTable.TryGetValue(Format, out GalImageFormat ImageFormat)) { - throw new NotImplementedException("Texture with format " + ((int)Format).ToString("x2") + " not implemented"); + throw new NotImplementedException($"Format 0x{((int)Format):x} not implemented!"); } - GalTextureType Type = RType; + GalImageFormat FormatType = ConvSrgb ? Srgb : GetFormatType(RType); - GalImageFormat FormatType = GetFormatType(RType); + GalImageFormat CombinedFormat = (ImageFormat & GalImageFormat.FormatMask) | FormatType; - if (ImageFormat.HasFlag(FormatType)) + if (!ImageFormat.HasFlag(FormatType)) { - return (ImageFormat & GalImageFormat.FormatMask) | FormatType; - } - else - { - throw new NotImplementedException("Texture with format " + Format + - " and component type " + Type + " is not implemented"); + throw new NotImplementedException($"Format \"{CombinedFormat}\" not implemented!"); } + + return CombinedFormat; } public static GalImageFormat ConvertSurface(GalSurfaceFormat Format) { switch (Format) { - case GalSurfaceFormat.RGBA32Float: return GalImageFormat.R32G32B32A32 | Sfloat; - case GalSurfaceFormat.RGBA32Uint: return GalImageFormat.R32G32B32A32 | Uint; - case GalSurfaceFormat.RGBA16Float: return GalImageFormat.R16G16B16A16 | Sfloat; - case GalSurfaceFormat.RG32Float: return GalImageFormat.R32G32 | Sfloat; - case GalSurfaceFormat.RG32Sint: return GalImageFormat.R32G32 | Sint; - case GalSurfaceFormat.RG32Uint: return GalImageFormat.R32G32 | Uint; - case GalSurfaceFormat.BGRA8Unorm: return GalImageFormat.B8G8R8A8 | Unorm; - case GalSurfaceFormat.BGRA8Srgb: return GalImageFormat.A8B8G8R8_SRGB; //This one might be wrong - case GalSurfaceFormat.RGB10A2Unorm: return GalImageFormat.A2B10G10R10 | Unorm; - case GalSurfaceFormat.RGBA8Unorm: return GalImageFormat.A8B8G8R8 | Unorm; - case GalSurfaceFormat.RGBA8Srgb: return GalImageFormat.A8B8G8R8_SRGB; - case GalSurfaceFormat.RGBA8Snorm: return GalImageFormat.A8B8G8R8 | Snorm; - case GalSurfaceFormat.RG16Snorm: return GalImageFormat.R16G16 | Snorm; - case GalSurfaceFormat.RG16Unorm: return GalImageFormat.R16G16 | Unorm; - case GalSurfaceFormat.RG16Float: return GalImageFormat.R16G16 | Sfloat; - case GalSurfaceFormat.R11G11B10Float: return GalImageFormat.B10G11R11 | Sfloat; - case GalSurfaceFormat.R32Float: return GalImageFormat.R32 | Sfloat; - case GalSurfaceFormat.R32Uint: return GalImageFormat.R32 | Uint; - case GalSurfaceFormat.RG8Unorm: return GalImageFormat.R8G8 | Unorm; - case GalSurfaceFormat.RG8Snorm: return GalImageFormat.R8G8 | Snorm; - case GalSurfaceFormat.R16Float: return GalImageFormat.R16 | Sfloat; - case GalSurfaceFormat.R16Unorm: return GalImageFormat.R16 | Unorm; - case GalSurfaceFormat.R16Uint: return GalImageFormat.R16 | Uint; - case GalSurfaceFormat.R8Unorm: return GalImageFormat.R8 | Unorm; - case GalSurfaceFormat.R8Uint: return GalImageFormat.R8 | Uint; - case GalSurfaceFormat.B5G6R5Unorm: return GalImageFormat.B5G6R5 | Unorm; - case GalSurfaceFormat.BGR5A1Unorm: return GalImageFormat.A1R5G5B5 | Unorm; + case GalSurfaceFormat.RGBA32Float: return GalImageFormat.RGBA32 | Float; + case GalSurfaceFormat.RGBA32Uint: return GalImageFormat.RGBA32 | Uint; + case GalSurfaceFormat.RGBA16Float: return GalImageFormat.RGBA16 | Float; + case GalSurfaceFormat.RG32Float: return GalImageFormat.RG32 | Float; + case GalSurfaceFormat.RG32Sint: return GalImageFormat.RG32 | Sint; + case GalSurfaceFormat.RG32Uint: return GalImageFormat.RG32 | Uint; + case GalSurfaceFormat.BGRA8Unorm: return GalImageFormat.BGRA8 | Unorm; + case GalSurfaceFormat.BGRA8Srgb: return GalImageFormat.BGRA8 | Srgb; + case GalSurfaceFormat.RGB10A2Unorm: return GalImageFormat.RGB10A2 | Unorm; + case GalSurfaceFormat.RGBA8Unorm: return GalImageFormat.RGBA8 | Unorm; + case GalSurfaceFormat.RGBA8Srgb: return GalImageFormat.RGBA8 | Srgb; + case GalSurfaceFormat.RGBA8Snorm: return GalImageFormat.RGBA8 | Snorm; + case GalSurfaceFormat.RG16Snorm: return GalImageFormat.RG16 | Snorm; + case GalSurfaceFormat.RG16Unorm: return GalImageFormat.RG16 | Unorm; + case GalSurfaceFormat.RG16Float: return GalImageFormat.RG16 | Float; + case GalSurfaceFormat.R11G11B10Float: return GalImageFormat.R11G11B10 | Float; + case GalSurfaceFormat.R32Float: return GalImageFormat.R32 | Float; + case GalSurfaceFormat.R32Uint: return GalImageFormat.R32 | Uint; + case GalSurfaceFormat.RG8Unorm: return GalImageFormat.RG8 | Unorm; + case GalSurfaceFormat.RG8Snorm: return GalImageFormat.RG8 | Snorm; + case GalSurfaceFormat.R16Float: return GalImageFormat.R16 | Float; + case GalSurfaceFormat.R16Unorm: return GalImageFormat.R16 | Unorm; + case GalSurfaceFormat.R16Uint: return GalImageFormat.R16 | Uint; + case GalSurfaceFormat.R8Unorm: return GalImageFormat.R8 | Unorm; + case GalSurfaceFormat.R8Uint: return GalImageFormat.R8 | Uint; + case GalSurfaceFormat.B5G6R5Unorm: return GalImageFormat.RGB565 | Unorm; + case GalSurfaceFormat.BGR5A1Unorm: return GalImageFormat.BGR5A1 | Unorm; } throw new NotImplementedException(Format.ToString()); @@ -209,11 +206,11 @@ namespace Ryujinx.Graphics.Texture { switch (Format) { - case GalZetaFormat.Z32Float: return GalImageFormat.D32 | Sfloat; - case GalZetaFormat.S8Z24Unorm: return GalImageFormat.D24_S8 | Unorm; - case GalZetaFormat.Z16Unorm: return GalImageFormat.D16 | Unorm; - case GalZetaFormat.Z24S8Unorm: return GalImageFormat.D24_S8 | Unorm; - case GalZetaFormat.Z32S8X24Float: return GalImageFormat.D32_S8 | Sfloat; + case GalZetaFormat.D32Float: return GalImageFormat.D32 | Float; + case GalZetaFormat.S8D24Unorm: return GalImageFormat.D24S8 | Unorm; + case GalZetaFormat.D16Unorm: return GalImageFormat.D16 | Unorm; + case GalZetaFormat.D24S8Unorm: return GalImageFormat.D24S8 | Unorm; + case GalZetaFormat.D32S8X24Float: return GalImageFormat.D32S8 | Float; } throw new NotImplementedException(Format.ToString()); @@ -376,14 +373,14 @@ namespace Ryujinx.Graphics.Texture private static ImageDescriptor GetImageDescriptor(GalImageFormat Format) { - GalImageFormat TypeLess = (Format & GalImageFormat.FormatMask); + GalImageFormat PixelFormat = Format & GalImageFormat.FormatMask; - if (s_ImageTable.TryGetValue(TypeLess, out ImageDescriptor Descriptor)) + if (s_ImageTable.TryGetValue(PixelFormat, out ImageDescriptor Descriptor)) { return Descriptor; } - throw new NotImplementedException("Image with format " + TypeLess.ToString() + " not implemented"); + throw new NotImplementedException($"Format \"{PixelFormat}\" not implemented!"); } private static GalImageFormat GetFormatType(GalTextureType Type) @@ -394,7 +391,7 @@ namespace Ryujinx.Graphics.Texture case GalTextureType.Unorm: return Unorm; case GalTextureType.Sint: return Sint; case GalTextureType.Uint: return Uint; - case GalTextureType.Float: return Sfloat; + case GalTextureType.Float: return Float; default: throw new NotImplementedException(((int)Type).ToString()); } diff --git a/Ryujinx.Graphics/Texture/TextureFactory.cs b/Ryujinx.Graphics/Texture/TextureFactory.cs index c0c53b06ef..1f2d625ec4 100644 --- a/Ryujinx.Graphics/Texture/TextureFactory.cs +++ b/Ryujinx.Graphics/Texture/TextureFactory.cs @@ -97,7 +97,9 @@ namespace Ryujinx.Graphics.Texture GalTextureFormat Format = (GalTextureFormat)(Tic[0] & 0x7f); - return ImageUtils.ConvertTexture(Format, RType, GType, BType, AType); + bool ConvSrgb = ((Tic[4] >> 22) & 1) != 0; + + return ImageUtils.ConvertTexture(Format, RType, GType, BType, AType, ConvSrgb); } private static int[] ReadWords(NvGpuVmm Vmm, long Position, int Count) diff --git a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs index 64cbad5c24..64e0b4a96d 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs @@ -321,7 +321,7 @@ namespace Ryujinx.HLE.HOS.Services.Android FbWidth, FbHeight, 1, 16, GalMemoryLayout.BlockLinear, - GalImageFormat.A8B8G8R8 | GalImageFormat.Unorm); + GalImageFormat.RGBA8 | GalImageFormat.Unorm); } Context.Device.Gpu.ResourceManager.ClearPbCache();