From c6e12949e5055dbb2fb1ff53050ab4f1d7962f08 Mon Sep 17 00:00:00 2001 From: Ac_K Date: Wed, 22 Jul 2020 06:56:00 +0200 Subject: [PATCH 01/12] Add multiple calls to am service (#1411) * Add multiple calls to am service This implement/stub some am calls: - SetAutoSleepDisabled - IsAutoSleepDisabled - SetAlbumImageTakenNotificationEnabled - EnableApplicationCrashReport - GetPreviousProgramIndex - NeedsToExitProcess - RequestForAppletToGetForeground - GetIndirectLayerConsumerHandle All checked by RE. Additionnaly to that, there is some cleanup here and there. Fix #1387, #1324, #1165, #1163, #1065 * Fix casting * Thread safe assign --- .../ILibraryAppletAccessor.cs | 37 +++++++ .../SystemAppletProxy/ISelfController.cs | 101 ++++++++++++++---- .../ApplicationProxy/IApplicationFunctions.cs | 29 ++++- Ryujinx.HLE/HOS/Services/Am/ResultCode.cs | 3 +- 4 files changed, 147 insertions(+), 23 deletions(-) diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs index aedec5501..68dadf3d6 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs @@ -161,5 +161,42 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib return ResultCode.Success; } + + [Command(110)] + // NeedsToExitProcess() + public ResultCode NeedsToExitProcess(ServiceCtx context) + { + return ResultCode.Stubbed; + } + + [Command(150)] + // RequestForAppletToGetForeground() + public ResultCode RequestForAppletToGetForeground(ServiceCtx context) + { + return ResultCode.Stubbed; + } + + [Command(160)] // 2.0.0+ + // GetIndirectLayerConsumerHandle() -> u64 indirect_layer_consumer_handle + public ResultCode GetIndirectLayerConsumerHandle(ServiceCtx context) + { + /* + if (indirectLayerConsumer == null) + { + return ResultCode.ObjectInvalid; + } + */ + + // TODO: Official sw uses this during LibraryApplet creation when LibraryAppletMode is 0x3. + // Since we don't support IndirectLayer and the handle couldn't be 0, it's fine to return 1. + + ulong indirectLayerConsumerHandle = 1; + + context.ResponseData.Write(indirectLayerConsumerHandle); + + Logger.PrintStub(LogClass.ServiceAm, new { indirectLayerConsumerHandle }); + + return ResultCode.Success; + } } } diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs index b34ecefbf..f1dab0e52 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ISelfController.cs @@ -19,7 +19,18 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // TODO: Set this when the game goes in suspension (go back to home menu ect), we currently don't support that so we can keep it set to 0. private ulong _accumulatedSuspendedTickValue = 0; - private int _idleTimeDetectionExtension; + // TODO: Determine where those fields are used. + private bool _screenShotPermission = false; + private bool _operationModeChangedNotification = false; + private bool _performanceModeChangedNotification = false; + private bool _restartMessageEnabled = false; + private bool _outOfFocusSuspendingEnabled = false; + private bool _handlesRequestToDisplay = false; + private bool _autoSleepDisabled = false; + private bool _albumImageTakenNotificationEnabled = false; + + private uint _screenShotImageOrientation = 0; + private uint _idleTimeDetectionExtension = 0; public ISelfController(Horizon system) { @@ -108,9 +119,11 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // SetScreenShotPermission(u32) public ResultCode SetScreenShotPermission(ServiceCtx context) { - bool enable = context.RequestData.ReadByte() != 0; + bool screenShotPermission = context.RequestData.ReadBoolean(); - Logger.PrintStub(LogClass.ServiceAm); + Logger.PrintStub(LogClass.ServiceAm, new { screenShotPermission }); + + _screenShotPermission = screenShotPermission; return ResultCode.Success; } @@ -119,9 +132,11 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // SetOperationModeChangedNotification(b8) public ResultCode SetOperationModeChangedNotification(ServiceCtx context) { - bool enable = context.RequestData.ReadByte() != 0; + bool operationModeChangedNotification = context.RequestData.ReadBoolean(); - Logger.PrintStub(LogClass.ServiceAm); + Logger.PrintStub(LogClass.ServiceAm, new { operationModeChangedNotification }); + + _operationModeChangedNotification = operationModeChangedNotification; return ResultCode.Success; } @@ -130,9 +145,11 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // SetPerformanceModeChangedNotification(b8) public ResultCode SetPerformanceModeChangedNotification(ServiceCtx context) { - bool enable = context.RequestData.ReadByte() != 0; + bool performanceModeChangedNotification = context.RequestData.ReadBoolean(); - Logger.PrintStub(LogClass.ServiceAm); + Logger.PrintStub(LogClass.ServiceAm, new { performanceModeChangedNotification }); + + _performanceModeChangedNotification = performanceModeChangedNotification; return ResultCode.Success; } @@ -141,11 +158,11 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // SetFocusHandlingMode(b8, b8, b8) public ResultCode SetFocusHandlingMode(ServiceCtx context) { - bool flag1 = context.RequestData.ReadByte() != 0; - bool flag2 = context.RequestData.ReadByte() != 0; - bool flag3 = context.RequestData.ReadByte() != 0; + bool unknownFlag1 = context.RequestData.ReadBoolean(); + bool unknownFlag2 = context.RequestData.ReadBoolean(); + bool unknownFlag3 = context.RequestData.ReadBoolean(); - Logger.PrintStub(LogClass.ServiceAm); + Logger.PrintStub(LogClass.ServiceAm, new { unknownFlag1, unknownFlag2, unknownFlag3 }); return ResultCode.Success; } @@ -154,9 +171,11 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // SetRestartMessageEnabled(b8) public ResultCode SetRestartMessageEnabled(ServiceCtx context) { - bool enable = context.RequestData.ReadByte() != 0; + bool restartMessageEnabled = context.RequestData.ReadBoolean(); - Logger.PrintStub(LogClass.ServiceAm); + Logger.PrintStub(LogClass.ServiceAm, new { restartMessageEnabled }); + + _restartMessageEnabled = restartMessageEnabled; return ResultCode.Success; } @@ -165,19 +184,24 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // SetOutOfFocusSuspendingEnabled(b8) public ResultCode SetOutOfFocusSuspendingEnabled(ServiceCtx context) { - bool enable = context.RequestData.ReadByte() != 0; + bool outOfFocusSuspendingEnabled = context.RequestData.ReadBoolean(); - Logger.PrintStub(LogClass.ServiceAm); + Logger.PrintStub(LogClass.ServiceAm, new { outOfFocusSuspendingEnabled }); + + _outOfFocusSuspendingEnabled = outOfFocusSuspendingEnabled; return ResultCode.Success; } [Command(19)] // 3.0.0+ + // SetScreenShotImageOrientation(u32) public ResultCode SetScreenShotImageOrientation(ServiceCtx context) { - int orientation = context.RequestData.ReadInt32(); + uint screenShotImageOrientation = context.RequestData.ReadUInt32(); - Logger.PrintStub(LogClass.ServiceAm); + Logger.PrintStub(LogClass.ServiceAm, new { screenShotImageOrientation }); + + _screenShotImageOrientation = screenShotImageOrientation; return ResultCode.Success; } @@ -186,9 +210,11 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // SetHandlesRequestToDisplay(b8) public ResultCode SetHandlesRequestToDisplay(ServiceCtx context) { - bool enable = context.RequestData.ReadByte() != 0; + bool handlesRequestToDisplay = context.RequestData.ReadBoolean(); - Logger.PrintStub(LogClass.ServiceAm); + Logger.PrintStub(LogClass.ServiceAm, new { handlesRequestToDisplay }); + + _handlesRequestToDisplay = handlesRequestToDisplay; return ResultCode.Success; } @@ -197,9 +223,11 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // SetIdleTimeDetectionExtension(u32) public ResultCode SetIdleTimeDetectionExtension(ServiceCtx context) { - _idleTimeDetectionExtension = context.RequestData.ReadInt32(); + uint idleTimeDetectionExtension = context.RequestData.ReadUInt32(); - Logger.PrintStub(LogClass.ServiceAm, new { _idleTimeDetectionExtension }); + Logger.PrintStub(LogClass.ServiceAm, new { idleTimeDetectionExtension }); + + _idleTimeDetectionExtension = idleTimeDetectionExtension; return ResultCode.Success; } @@ -215,6 +243,26 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } + [Command(68)] + // SetAutoSleepDisabled(u8) + public ResultCode SetAutoSleepDisabled(ServiceCtx context) + { + bool autoSleepDisabled = context.RequestData.ReadBoolean(); + + _autoSleepDisabled = autoSleepDisabled; + + return ResultCode.Success; + } + + [Command(69)] + // IsAutoSleepDisabled() -> u8 + public ResultCode IsAutoSleepDisabled(ServiceCtx context) + { + context.ResponseData.Write(_autoSleepDisabled); + + return ResultCode.Success; + } + [Command(90)] // 6.0.0+ // GetAccumulatedSuspendedTickValue() -> u64 public ResultCode GetAccumulatedSuspendedTickValue(ServiceCtx context) @@ -244,5 +292,16 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } + + [Command(100)] // 7.0.0+ + // SetAlbumImageTakenNotificationEnabled(u8) + public ResultCode SetAlbumImageTakenNotificationEnabled(ServiceCtx context) + { + bool albumImageTakenNotificationEnabled = context.RequestData.ReadBoolean(); + + _albumImageTakenNotificationEnabled = albumImageTakenNotificationEnabled; + + return ResultCode.Success; + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs index fb85d856b..ac24dfc97 100644 --- a/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs +++ b/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/ApplicationProxy/IApplicationFunctions.cs @@ -158,7 +158,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati // NotifyRunning() -> b8 public ResultCode NotifyRunning(ServiceCtx context) { - context.ResponseData.Write(1); + context.ResponseData.Write(true); return ResultCode.Success; } @@ -195,6 +195,17 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return ResultCode.Success; } + [Command(90)] // 4.0.0+ + // EnableApplicationCrashReport(u8) + public ResultCode EnableApplicationCrashReport(ServiceCtx context) + { + bool applicationCrashReportEnabled = context.RequestData.ReadBoolean(); + + Logger.PrintStub(LogClass.ServiceAm, new { applicationCrashReportEnabled }); + + return ResultCode.Success; + } + [Command(100)] // 5.0.0+ // InitializeApplicationCopyrightFrameBuffer(s32 width, s32 height, handle transfer_memory, u64 transfer_memory_size) public ResultCode InitializeApplicationCopyrightFrameBuffer(ServiceCtx context) @@ -319,6 +330,22 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.Applicati return (ResultCode)QueryPlayStatisticsManager.GetPlayStatistics(context, true); } + [Command(123)] // 5.0.0+ + // GetPreviousProgramIndex() -> s32 program_index + public ResultCode GetPreviousProgramIndex(ServiceCtx context) + { + // TODO: The output PreviousProgramIndex is -1 when there was no previous title. + // When multi-process will be supported, return the last program index. + + int previousProgramIndex = -1; + + context.ResponseData.Write(previousProgramIndex); + + Logger.PrintStub(LogClass.ServiceAm, new { previousProgramIndex }); + + return ResultCode.Success; + } + [Command(130)] // 8.0.0+ // GetGpuErrorDetectedSystemEvent() -> handle public ResultCode GetGpuErrorDetectedSystemEvent(ServiceCtx context) diff --git a/Ryujinx.HLE/HOS/Services/Am/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Am/ResultCode.cs index 73daf7003..422462e92 100644 --- a/Ryujinx.HLE/HOS/Services/Am/ResultCode.cs +++ b/Ryujinx.HLE/HOS/Services/Am/ResultCode.cs @@ -22,6 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Am StackPoolExhausted = (712 << ErrorCodeShift) | ModuleId, DebugModeNotEnabled = (974 << ErrorCodeShift) | ModuleId, DevFunctionNotEnabled = (980 << ErrorCodeShift) | ModuleId, - NotImplemented = (998 << ErrorCodeShift) | ModuleId + NotImplemented = (998 << ErrorCodeShift) | ModuleId, + Stubbed = (999 << ErrorCodeShift) | ModuleId } } From 9e141bc3daf86ceccc4eced78edbc9be2d2efe6e Mon Sep 17 00:00:00 2001 From: Ac_K Date: Thu, 23 Jul 2020 12:25:41 +0200 Subject: [PATCH 02/12] vi: Implement GetIndirectLayerImageRequiredMemoryInfo (#1415) This implement GetIndirectLayerImageRequiredMemoryInfo call from vi service, accordingly to RE. Thanks to Thog and gdkchan for helping me to understand some GPU things. Close #942 --- Ryujinx.HLE/HOS/Services/Vi/ResultCode.cs | 1 + .../RootService/IApplicationDisplayService.cs | 50 ++++++++++++++++++- 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/Ryujinx.HLE/HOS/Services/Vi/ResultCode.cs b/Ryujinx.HLE/HOS/Services/Vi/ResultCode.cs index 35ee6ae27..2c78c1a3d 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/ResultCode.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/ResultCode.cs @@ -8,6 +8,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi Success = 0, InvalidArguments = (1 << ErrorCodeShift) | ModuleId, + InvalidLayerSize = (4 << ErrorCodeShift) | ModuleId, InvalidScalingMode = (6 << ErrorCodeShift) | ModuleId } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs index 8180c284a..bd00fc02b 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs @@ -1,3 +1,4 @@ +using Ryujinx.Common; using Ryujinx.Cpu; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel.Common; @@ -190,7 +191,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService public ResultCode SetLayerScalingMode(ServiceCtx context) { int scalingMode = context.RequestData.ReadInt32(); - long unknown = context.RequestData.ReadInt64(); + long layerId = context.RequestData.ReadInt64(); return ResultCode.Success; } @@ -235,6 +236,53 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService return null; } + [Command(2460)] + // GetIndirectLayerImageRequiredMemoryInfo(u64 width, u64 height) -> (u64 size, u64 alignment) + public ResultCode GetIndirectLayerImageRequiredMemoryInfo(ServiceCtx context) + { + /* + // Doesn't occur in our case. + if (sizePtr == null || address_alignmentPtr == null) + { + return ResultCode.InvalidArguments; + } + */ + + int width = (int)context.RequestData.ReadUInt64(); + int height = (int)context.RequestData.ReadUInt64(); + + if (height < 0 || width < 0) + { + return ResultCode.InvalidLayerSize; + } + else + { + /* + // Doesn't occur in our case. + if (!service_initialized) + { + return ResultCode.InvalidArguments; + } + */ + + const ulong defaultAlignment = 0x1000; + const ulong defaultSize = 0x20000; + + // NOTE: The official service setup a A8B8G8R8 texture with a linear layout and then query its size. + // As we don't need this texture on the emulator, we can just simplify this logic and directly + // do a linear layout size calculation. (stride * height * bytePerPixel) + int pitch = BitUtils.AlignUp(BitUtils.DivRoundUp(width * 32, 8), 64); + int memorySize = pitch * BitUtils.AlignUp(height, 64); + ulong requiredMemorySize = (ulong)BitUtils.AlignUp(memorySize, (int)defaultAlignment); + ulong size = (requiredMemorySize + defaultSize - 1) / defaultSize * defaultSize; + + context.ResponseData.Write(size); + context.ResponseData.Write(defaultAlignment); + } + + return ResultCode.Success; + } + [Command(5202)] // GetDisplayVsyncEvent(u64) -> handle public ResultCode GetDisplayVSyncEvent(ServiceCtx context) From 3c1f220c5ec6ea824e6a0c12d77fd8ce01ee0d1b Mon Sep 17 00:00:00 2001 From: emmauss Date: Thu, 23 Jul 2020 13:12:19 +0000 Subject: [PATCH 03/12] fix fullscreen toggling (#1364) --- Ryujinx/Ui/MainWindow.cs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs index 27fcd3339..92e0c9567 100644 --- a/Ryujinx/Ui/MainWindow.cs +++ b/Ryujinx/Ui/MainWindow.cs @@ -94,6 +94,7 @@ namespace Ryujinx.Ui this.DefaultWidth = monitorWidth < 1280 ? monitorWidth : 1280; this.DefaultHeight = monitorHeight < 760 ? monitorHeight : 760; + this.WindowStateEvent += MainWindow_WindowStateEvent; this.DeleteEvent += Window_Close; _fullScreen.Activated += FullScreen_Toggled; @@ -192,6 +193,11 @@ namespace Ryujinx.Ui _statusBar.Hide(); } + private void MainWindow_WindowStateEvent(object o, WindowStateEventArgs args) + { + _fullScreen.Label = args.Event.NewWindowState.HasFlag(Gdk.WindowState.Fullscreen) ? "Exit Fullscreen" : "Enter Fullscreen"; + } + #if USE_DEBUGGING private void _openDebugger_Opened(object sender, EventArgs e) { @@ -505,6 +511,11 @@ namespace Ryujinx.Ui _glWidget.ShowAll(); EditFooterForGameRender(); + + if (this.Window.State.HasFlag(Gdk.WindowState.Fullscreen)) + { + ToggleExtraWidgets(false); + } }); _glWidget.WaitEvent.WaitOne(); @@ -520,6 +531,11 @@ namespace Ryujinx.Ui // NOTE: Everything that is here will not be executed when you close the UI. Application.Invoke(delegate { + if (this.Window.State.HasFlag(Gdk.WindowState.Fullscreen)) + { + ToggleExtraWidgets(true); + } + _viewBox.Remove(_glWidget); _glWidget.Exit(); @@ -583,10 +599,6 @@ namespace Ryujinx.Ui _footerBox.Hide(); } } - - bool fullScreenToggled = this.Window.State.HasFlag(Gdk.WindowState.Fullscreen); - - _fullScreen.Label = fullScreenToggled ? "Exit Fullscreen" : "Enter Fullscreen"; } private static void UpdateGameMetadata(string titleId) From 5a7df48975bcb04b1805031a26f5007211fe4c62 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 23 Jul 2020 23:53:25 -0300 Subject: [PATCH 04/12] New GPFifo and fast guest constant buffer updates (#1400) * Add new structures from official docs, start migrating GPFifo * Finish migration to new GPFifo processor * Implement fast constant buffer data upload * Migrate to new GPFifo class * XML docs --- Ryujinx.Graphics.Device/DeviceState.cs | 6 +- Ryujinx.Graphics.Gpu/DmaPusher.cs | 316 ------------------ .../Engine/GPFifo/CompressedMethod.cs | 39 +++ Ryujinx.Graphics.Gpu/Engine/GPFifo/GPEntry.cs | 51 +++ .../Engine/GPFifo/GPFifoClass.cs | 214 ++++++++++++ .../Engine/GPFifo/GPFifoClassState.cs | 186 +++++++++++ .../Engine/GPFifo/GPFifoDevice.cs | 188 +++++++++++ .../Engine/GPFifo/GPFifoProcessor.cs | 179 ++++++++++ Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs | 69 ++++ Ryujinx.Graphics.Gpu/Engine/MethodFifo.cs | 103 ------ .../Engine/MethodUniformBufferUpdate.cs | 18 + Ryujinx.Graphics.Gpu/Engine/Methods.cs | 14 - Ryujinx.Graphics.Gpu/GpuContext.cs | 15 +- Ryujinx.Graphics.Gpu/NvGpuFifo.cs | 220 ------------ .../Ryujinx.Graphics.Gpu.csproj | 1 + .../State/FenceActionOperation.cs | 11 - .../State/FifoSemaphoreOperation.cs | 9 - Ryujinx.Graphics.Gpu/State/MethodOffset.cs | 9 - .../NvHostChannel/NvHostChannelDeviceFile.cs | 8 +- Ryujinx.HLE/Switch.cs | 4 +- 20 files changed, 958 insertions(+), 702 deletions(-) delete mode 100644 Ryujinx.Graphics.Gpu/DmaPusher.cs create mode 100644 Ryujinx.Graphics.Gpu/Engine/GPFifo/CompressedMethod.cs create mode 100644 Ryujinx.Graphics.Gpu/Engine/GPFifo/GPEntry.cs create mode 100644 Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs create mode 100644 Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClassState.cs create mode 100644 Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs create mode 100644 Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs create mode 100644 Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs delete mode 100644 Ryujinx.Graphics.Gpu/Engine/MethodFifo.cs delete mode 100644 Ryujinx.Graphics.Gpu/NvGpuFifo.cs delete mode 100644 Ryujinx.Graphics.Gpu/State/FenceActionOperation.cs delete mode 100644 Ryujinx.Graphics.Gpu/State/FifoSemaphoreOperation.cs diff --git a/Ryujinx.Graphics.Device/DeviceState.cs b/Ryujinx.Graphics.Device/DeviceState.cs index ea6942ec2..740d8589c 100644 --- a/Ryujinx.Graphics.Device/DeviceState.cs +++ b/Ryujinx.Graphics.Device/DeviceState.cs @@ -90,14 +90,12 @@ namespace Ryujinx.Graphics.Device { int alignedOffset = Align(offset); + GetRef(alignedOffset) = data; + if (_writeCallbacks.TryGetValue(alignedOffset, out Action write)) { write(data); } - else - { - GetRef(alignedOffset) = data; - } } } diff --git a/Ryujinx.Graphics.Gpu/DmaPusher.cs b/Ryujinx.Graphics.Gpu/DmaPusher.cs deleted file mode 100644 index 3b5ac830d..000000000 --- a/Ryujinx.Graphics.Gpu/DmaPusher.cs +++ /dev/null @@ -1,316 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Runtime.InteropServices; -using System.Threading; - -namespace Ryujinx.Graphics.Gpu -{ - /// - /// GPU DMA pusher, used to push commands to the GPU. - /// - public class DmaPusher - { - private ConcurrentQueue _commandBufferQueue; - - private enum CommandBufferType - { - Prefetch, - NoPrefetch, - } - - private struct CommandBuffer - { - /// - /// The type of the command buffer. - /// - public CommandBufferType Type; - - /// - /// Fetched data. - /// - public int[] Words; - - /// - /// The GPFIFO entry address. (used in NoPrefetch mode) - /// - public ulong EntryAddress; - - /// - /// The count of entries inside this GPFIFO entry. - /// - public uint EntryCount; - - /// - /// Fetch the command buffer. - /// - public void Fetch(GpuContext context) - { - if (Words == null) - { - Words = MemoryMarshal.Cast(context.MemoryAccessor.GetSpan(EntryAddress, (int)EntryCount * 4)).ToArray(); - } - } - - /// - /// Read inside the command buffer. - /// - /// The GPU context - /// The index inside the command buffer - /// The value read - public int ReadAt(GpuContext context, int index) - { - return Words[index]; - } - } - - private CommandBuffer _currentCommandBuffer; - private int _wordsPosition; - - /// - /// Internal GPFIFO state. - /// - private struct DmaState - { - public int Method; - public int SubChannel; - public int MethodCount; - public bool NonIncrementing; - public bool IncrementOnce; - public int LengthPending; - } - - private DmaState _state; - - private bool _ibEnable; - - private GpuContext _context; - - private AutoResetEvent _event; - - /// - /// Creates a new instance of the GPU DMA pusher. - /// - /// GPU context that the pusher belongs to - internal DmaPusher(GpuContext context) - { - _context = context; - - _ibEnable = true; - - _commandBufferQueue = new ConcurrentQueue(); - - _event = new AutoResetEvent(false); - } - - /// - /// Signal the pusher that there are new entries to process. - /// - public void SignalNewEntries() - { - _event.Set(); - } - - /// - /// Push a GPFIFO entry in the form of a prefetched command buffer. - /// It is intended to be used by nvservices to handle special cases. - /// - /// The command buffer containing the prefetched commands - public void PushHostCommandBuffer(int[] commandBuffer) - { - _commandBufferQueue.Enqueue(new CommandBuffer - { - Type = CommandBufferType.Prefetch, - Words = commandBuffer, - EntryAddress = ulong.MaxValue, - EntryCount = (uint)commandBuffer.Length - }); - } - - /// - /// Create a CommandBuffer from a GPFIFO entry. - /// - /// The GPFIFO entry - /// A new CommandBuffer based on the GPFIFO entry - private CommandBuffer CreateCommandBuffer(ulong entry) - { - ulong length = (entry >> 42) & 0x1fffff; - ulong startAddress = entry & 0xfffffffffc; - - bool noPrefetch = (entry & (1UL << 63)) != 0; - - CommandBufferType type = CommandBufferType.Prefetch; - - if (noPrefetch) - { - type = CommandBufferType.NoPrefetch; - } - - return new CommandBuffer - { - Type = type, - Words = null, - EntryAddress = startAddress, - EntryCount = (uint)length - }; - } - - /// - /// Pushes GPFIFO entries. - /// - /// GPFIFO entries - public void PushEntries(ReadOnlySpan entries) - { - bool beforeBarrier = true; - - foreach (ulong entry in entries) - { - CommandBuffer commandBuffer = CreateCommandBuffer(entry); - - if (beforeBarrier && commandBuffer.Type == CommandBufferType.Prefetch) - { - commandBuffer.Fetch(_context); - } - - if (commandBuffer.Type == CommandBufferType.NoPrefetch) - { - beforeBarrier = false; - } - - _commandBufferQueue.Enqueue(commandBuffer); - } - } - - /// - /// Waits until commands are pushed to the FIFO. - /// - /// True if commands were received, false if wait timed out - public bool WaitForCommands() - { - return _event.WaitOne(8); - } - - /// - /// Processes commands pushed to the FIFO. - /// - public void DispatchCalls() - { - while (Step()); - } - - /// - /// Processes a single command on the FIFO. - /// - /// True if the FIFO still has commands to be processed, false otherwise - private bool Step() - { - if (_wordsPosition != _currentCommandBuffer.EntryCount) - { - int word = _currentCommandBuffer.ReadAt(_context, _wordsPosition++); - - if (_state.LengthPending != 0) - { - _state.LengthPending = 0; - _state.MethodCount = word & 0xffffff; - } - else if (_state.MethodCount != 0) - { - CallMethod(word); - - if (!_state.NonIncrementing) - { - _state.Method++; - } - - if (_state.IncrementOnce) - { - _state.NonIncrementing = true; - } - - _state.MethodCount--; - } - else - { - int submissionMode = (word >> 29) & 7; - - switch (submissionMode) - { - case 1: - // Incrementing. - SetNonImmediateState(word); - - _state.NonIncrementing = false; - _state.IncrementOnce = false; - - break; - - case 3: - // Non-incrementing. - SetNonImmediateState(word); - - _state.NonIncrementing = true; - _state.IncrementOnce = false; - - break; - - case 4: - // Immediate. - _state.Method = (word >> 0) & 0x1fff; - _state.SubChannel = (word >> 13) & 7; - _state.NonIncrementing = true; - _state.IncrementOnce = false; - - CallMethod((word >> 16) & 0x1fff); - - break; - - case 5: - // Increment-once. - SetNonImmediateState(word); - - _state.NonIncrementing = false; - _state.IncrementOnce = true; - - break; - } - } - } - else if (_ibEnable && _commandBufferQueue.TryDequeue(out CommandBuffer entry)) - { - _currentCommandBuffer = entry; - _wordsPosition = 0; - - _currentCommandBuffer.Fetch(_context); - } - else - { - return false; - } - - return true; - } - - /// - /// Sets current non-immediate method call state. - /// - /// Compressed method word - private void SetNonImmediateState(int word) - { - _state.Method = (word >> 0) & 0x1fff; - _state.SubChannel = (word >> 13) & 7; - _state.MethodCount = (word >> 16) & 0x1fff; - } - - /// - /// Forwards the method call to GPU engines. - /// - /// Call argument - private void CallMethod(int argument) - { - _context.Fifo.CallMethod(new MethodParams( - _state.Method, - argument, - _state.SubChannel, - _state.MethodCount)); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/CompressedMethod.cs b/Ryujinx.Graphics.Gpu/Engine/GPFifo/CompressedMethod.cs new file mode 100644 index 000000000..9a2134894 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/GPFifo/CompressedMethod.cs @@ -0,0 +1,39 @@ +// This file was auto-generated from NVIDIA official Maxwell definitions. + +namespace Ryujinx.Graphics.Gpu.Engine.GPFifo +{ + enum TertOp + { + Grp0IncMethod = 0, + Grp0SetSubDevMask = 1, + Grp0StoreSubDevMask = 2, + Grp0UseSubDevMask = 3, + Grp2NonIncMethod = 0 + } + + enum SecOp + { + Grp0UseTert = 0, + IncMethod = 1, + Grp2UseTert = 2, + NonIncMethod = 3, + ImmdDataMethod = 4, + OneInc = 5, + Reserved6 = 6, + EndPbSegment = 7 + } + + struct CompressedMethod + { + public uint Method; + public int MethodAddressOld => (int)((Method >> 2) & 0x7FF); + public int MethodAddress => (int)((Method >> 0) & 0xFFF); + public int SubdeviceMask => (int)((Method >> 4) & 0xFFF); + public int MethodSubchannel => (int)((Method >> 13) & 0x7); + public TertOp TertOp => (TertOp)((Method >> 16) & 0x3); + public int MethodCountOld => (int)((Method >> 18) & 0x7FF); + public int MethodCount => (int)((Method >> 16) & 0x1FFF); + public int ImmdData => (int)((Method >> 16) & 0x1FFF); + public SecOp SecOp => (SecOp)((Method >> 29) & 0x7); + } +} diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPEntry.cs b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPEntry.cs new file mode 100644 index 000000000..9866cd2e7 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPEntry.cs @@ -0,0 +1,51 @@ +// This file was auto-generated from NVIDIA official Maxwell definitions. + +namespace Ryujinx.Graphics.Gpu.Engine.GPFifo +{ + enum Entry0Fetch + { + Unconditional = 0, + Conditional = 1, + } + + enum Entry1Priv + { + User = 0, + Kernel = 1, + } + + enum Entry1Level + { + Main = 0, + Subroutine = 1, + } + + enum Entry1Sync + { + Proceed = 0, + Wait = 1, + } + + enum Entry1Opcode + { + Nop = 0, + Illegal = 1, + Crc = 2, + PbCrc = 3, + } + + struct GPEntry + { + public uint Entry0; + public Entry0Fetch Entry0Fetch => (Entry0Fetch)((Entry0 >> 0) & 0x1); + public int Entry0Get => (int)((Entry0 >> 2) & 0x3FFFFFFF); + public int Entry0Operand => (int)(Entry0); + public uint Entry1; + public int Entry1GetHi => (int)((Entry1 >> 0) & 0xFF); + public Entry1Priv Entry1Priv => (Entry1Priv)((Entry1 >> 8) & 0x1); + public Entry1Level Entry1Level => (Entry1Level)((Entry1 >> 9) & 0x1); + public int Entry1Length => (int)((Entry1 >> 10) & 0x1FFFFF); + public Entry1Sync Entry1Sync => (Entry1Sync)((Entry1 >> 31) & 0x1); + public Entry1Opcode Entry1Opcode => (Entry1Opcode)((Entry1 >> 0) & 0xFF); + } +} diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs new file mode 100644 index 000000000..ec2e4bdc1 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs @@ -0,0 +1,214 @@ +using Ryujinx.Graphics.Device; +using Ryujinx.Graphics.Gpu.Engine.MME; +using Ryujinx.Graphics.Gpu.State; +using System; +using System.Collections.Generic; +using System.Threading; + +namespace Ryujinx.Graphics.Gpu.Engine.GPFifo +{ + /// + /// Represents a GPU General Purpose FIFO class. + /// + class GPFifoClass : IDeviceState + { + private readonly GpuContext _context; + private readonly DeviceState _state; + + private const int MacrosCount = 0x80; + + // Note: The size of the macro memory is unknown, we just make + // a guess here and use 256kb as the size. Increase if needed. + private const int MacroCodeSize = 256 * 256; + + private readonly Macro[] _macros; + private readonly int[] _macroCode; + + /// + /// MME Shadow RAM Control. + /// + public ShadowRamControl ShadowCtrl { get; private set; } + + /// + /// Creates a new instance of the GPU General Purpose FIFO class. + /// + /// GPU context + public GPFifoClass(GpuContext context) + { + _context = context; + _state = new DeviceState(new Dictionary + { + { nameof(GPFifoClassState.Semaphored), new RwCallback(Semaphored, null) }, + { nameof(GPFifoClassState.Syncpointb), new RwCallback(Syncpointb, null) }, + { nameof(GPFifoClassState.WaitForIdle), new RwCallback(WaitForIdle, null) }, + { nameof(GPFifoClassState.LoadMmeInstructionRam), new RwCallback(LoadMmeInstructionRam, null) }, + { nameof(GPFifoClassState.LoadMmeStartAddressRam), new RwCallback(LoadMmeStartAddressRam, null) }, + { nameof(GPFifoClassState.SetMmeShadowRamControl), new RwCallback(SetMmeShadowRamControl, null) } + }); + + _macros = new Macro[MacrosCount]; + _macroCode = new int[MacroCodeSize]; + } + + /// + /// Reads data from the class registers. + /// + /// Register byte offset + /// Data at the specified offset + public int Read(int offset) => _state.Read(offset); + + /// + /// Writes data to the class registers. + /// + /// Register byte offset + /// Data to be written + public void Write(int offset, int data) => _state.Write(offset, data); + + /// + /// Writes a GPU counter to guest memory. + /// + /// Method call argument + public void Semaphored(int argument) + { + ulong address = ((ulong)_state.State.SemaphorebOffsetLower << 2) | + ((ulong)_state.State.SemaphoreaOffsetUpper << 32); + + int value = _state.State.SemaphorecPayload; + + SemaphoredOperation operation = _state.State.SemaphoredOperation; + + // TODO: Acquire operations (Wait), interrupts for invalid combinations. + if (operation == SemaphoredOperation.Release) + { + _context.MemoryAccessor.Write(address, value); + } + else if (operation == SemaphoredOperation.Reduction) + { + bool signed = _state.State.SemaphoredFormat == SemaphoredFormat.Signed; + + int mem = _context.MemoryAccessor.Read(address); + + switch (_state.State.SemaphoredReduction) + { + case SemaphoredReduction.Min: + value = signed ? Math.Min(mem, value) : (int)Math.Min((uint)mem, (uint)value); + break; + case SemaphoredReduction.Max: + value = signed ? Math.Max(mem, value) : (int)Math.Max((uint)mem, (uint)value); + break; + case SemaphoredReduction.Xor: + value ^= mem; + break; + case SemaphoredReduction.And: + value &= mem; + break; + case SemaphoredReduction.Or: + value |= mem; + break; + case SemaphoredReduction.Add: + value += mem; + break; + case SemaphoredReduction.Inc: + value = (uint)mem < (uint)value ? mem + 1 : 0; + break; + case SemaphoredReduction.Dec: + value = (uint)mem > 0 && (uint)mem <= (uint)value ? mem - 1 : value; + break; + } + + _context.MemoryAccessor.Write(address, value); + } + } + + /// + /// Apply a fence operation on a syncpoint. + /// + /// Method call argument + public void Syncpointb(int argument) + { + SyncpointbOperation operation = _state.State.SyncpointbOperation; + + uint syncpointId = (uint)_state.State.SyncpointbSyncptIndex; + + if (operation == SyncpointbOperation.Wait) + { + uint threshold = (uint)_state.State.SyncpointaPayload; + + _context.Synchronization.WaitOnSyncpoint(syncpointId, threshold, Timeout.InfiniteTimeSpan); + } + else if (operation == SyncpointbOperation.Incr) + { + _context.Synchronization.IncrementSyncpoint(syncpointId); + } + + _context.AdvanceSequence(); + } + + /// + /// Waits for the GPU to be idle. + /// + /// Method call argument + public void WaitForIdle(int argument) + { + _context.Methods.PerformDeferredDraws(); + _context.Renderer.Pipeline.Barrier(); + } + + /// + /// Send macro code/data to the MME + /// + /// Method call argument + public void LoadMmeInstructionRam(int argument) + { + _macroCode[_state.State.LoadMmeInstructionRamPointer++] = argument; + } + + /// + /// Bind a macro index to a position for the MME + /// + /// Method call argument + public void LoadMmeStartAddressRam(int argument) + { + _macros[_state.State.LoadMmeStartAddressRamPointer++] = new Macro(argument); + } + + /// + /// Change the shadow RAM setting + /// + /// Method call argument + public void SetMmeShadowRamControl(int argument) + { + ShadowCtrl = (ShadowRamControl)argument; + } + + /// + /// Pushes an argument to a macro. + /// + /// Index of the macro + /// Argument to be pushed to the macro + public void MmePushArgument(int index, int argument) + { + _macros[index].PushArgument(argument); + } + + /// + /// Prepares a macro for execution. + /// + /// Index of the macro + /// Initial argument passed to the macro + public void MmeStart(int index, int argument) + { + _macros[index].StartExecution(argument); + } + + /// + /// Executes a macro. + /// + /// Index of the macro + /// Current GPU state + public void CallMme(int index, GpuState state) + { + _macros[index].Execute(_macroCode, ShadowCtrl, state); + } + } +} diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClassState.cs b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClassState.cs new file mode 100644 index 000000000..3b2826685 --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClassState.cs @@ -0,0 +1,186 @@ +// This file was auto-generated from NVIDIA official Maxwell definitions. + +using Ryujinx.Common.Memory; + +namespace Ryujinx.Graphics.Gpu.Engine.GPFifo +{ + enum SemaphoredOperation + { + Acquire = 1, + Release = 2, + AcqGeq = 4, + AcqAnd = 8, + Reduction = 16 + } + + enum SemaphoredAcquireSwitch + { + Disabled = 0, + Enabled = 1 + } + + enum SemaphoredReleaseWfi + { + En = 0, + Dis = 1 + } + + enum SemaphoredReleaseSize + { + SixteenBytes = 0, + FourBytes = 1 + } + + enum SemaphoredReduction + { + Min = 0, + Max = 1, + Xor = 2, + And = 3, + Or = 4, + Add = 5, + Inc = 6, + Dec = 7 + } + + enum SemaphoredFormat + { + Signed = 0, + Unsigned = 1 + } + + enum MemOpCTlbInvalidatePdb + { + One = 0, + All = 1 + } + + enum MemOpCTlbInvalidateGpc + { + Enable = 0, + Disable = 1 + } + + enum MemOpCTlbInvalidateTarget + { + VidMem = 0, + SysMemCoherent = 2, + SysMemNoncoherent = 3 + } + + enum MemOpDOperation + { + Membar = 5, + MmuTlbInvalidate = 9, + L2PeermemInvalidate = 13, + L2SysmemInvalidate = 14, + L2CleanComptags = 15, + L2FlushDirty = 16 + } + + enum SyncpointbOperation + { + Wait = 0, + Incr = 1 + } + + enum SyncpointbWaitSwitch + { + Dis = 0, + En = 1 + } + + enum WfiScope + { + CurrentScgType = 0, + All = 1 + } + + enum YieldOp + { + Nop = 0, + PbdmaTimeslice = 1, + RunlistTimeslice = 2, + Tsg = 3 + } + + struct GPFifoClassState + { + public uint SetObject; + public int SetObjectNvclass => (int)((SetObject >> 0) & 0xFFFF); + public int SetObjectEngine => (int)((SetObject >> 16) & 0x1F); + public uint Illegal; + public int IllegalHandle => (int)(Illegal); + public uint Nop; + public int NopHandle => (int)(Nop); + public uint Reserved0C; + public uint Semaphorea; + public int SemaphoreaOffsetUpper => (int)((Semaphorea >> 0) & 0xFF); + public uint Semaphoreb; + public int SemaphorebOffsetLower => (int)((Semaphoreb >> 2) & 0x3FFFFFFF); + public uint Semaphorec; + public int SemaphorecPayload => (int)(Semaphorec); + public uint Semaphored; + public SemaphoredOperation SemaphoredOperation => (SemaphoredOperation)((Semaphored >> 0) & 0x1F); + public SemaphoredAcquireSwitch SemaphoredAcquireSwitch => (SemaphoredAcquireSwitch)((Semaphored >> 12) & 0x1); + public SemaphoredReleaseWfi SemaphoredReleaseWfi => (SemaphoredReleaseWfi)((Semaphored >> 20) & 0x1); + public SemaphoredReleaseSize SemaphoredReleaseSize => (SemaphoredReleaseSize)((Semaphored >> 24) & 0x1); + public SemaphoredReduction SemaphoredReduction => (SemaphoredReduction)((Semaphored >> 27) & 0xF); + public SemaphoredFormat SemaphoredFormat => (SemaphoredFormat)((Semaphored >> 31) & 0x1); + public uint NonStallInterrupt; + public int NonStallInterruptHandle => (int)(NonStallInterrupt); + public uint FbFlush; + public int FbFlushHandle => (int)(FbFlush); + public uint Reserved28; + public uint Reserved2C; + public uint MemOpC; + public int MemOpCOperandLow => (int)((MemOpC >> 2) & 0x3FFFFFFF); + public MemOpCTlbInvalidatePdb MemOpCTlbInvalidatePdb => (MemOpCTlbInvalidatePdb)((MemOpC >> 0) & 0x1); + public MemOpCTlbInvalidateGpc MemOpCTlbInvalidateGpc => (MemOpCTlbInvalidateGpc)((MemOpC >> 1) & 0x1); + public MemOpCTlbInvalidateTarget MemOpCTlbInvalidateTarget => (MemOpCTlbInvalidateTarget)((MemOpC >> 10) & 0x3); + public int MemOpCTlbInvalidateAddrLo => (int)((MemOpC >> 12) & 0xFFFFF); + public uint MemOpD; + public int MemOpDOperandHigh => (int)((MemOpD >> 0) & 0xFF); + public MemOpDOperation MemOpDOperation => (MemOpDOperation)((MemOpD >> 27) & 0x1F); + public int MemOpDTlbInvalidateAddrHi => (int)((MemOpD >> 0) & 0xFF); + public uint Reserved38; + public uint Reserved3C; + public uint Reserved40; + public uint Reserved44; + public uint Reserved48; + public uint Reserved4C; + public uint SetReference; + public int SetReferenceCount => (int)(SetReference); + public uint Reserved54; + public uint Reserved58; + public uint Reserved5C; + public uint Reserved60; + public uint Reserved64; + public uint Reserved68; + public uint Reserved6C; + public uint Syncpointa; + public int SyncpointaPayload => (int)(Syncpointa); + public uint Syncpointb; + public SyncpointbOperation SyncpointbOperation => (SyncpointbOperation)((Syncpointb >> 0) & 0x1); + public SyncpointbWaitSwitch SyncpointbWaitSwitch => (SyncpointbWaitSwitch)((Syncpointb >> 4) & 0x1); + public int SyncpointbSyncptIndex => (int)((Syncpointb >> 8) & 0xFFF); + public uint Wfi; + public WfiScope WfiScope => (WfiScope)((Wfi >> 0) & 0x1); + public uint CrcCheck; + public int CrcCheckValue => (int)(CrcCheck); + public uint Yield; + public YieldOp YieldOp => (YieldOp)((Yield >> 0) & 0x3); + // TODO: Eventually move this to per-engine state. + public Array31 Reserved84; + public uint NoOperation; + public uint SetNotifyA; + public uint SetNotifyB; + public uint Notify; + public uint WaitForIdle; + public uint LoadMmeInstructionRamPointer; + public uint LoadMmeInstructionRam; + public uint LoadMmeStartAddressRamPointer; + public uint LoadMmeStartAddressRam; + public uint SetMmeShadowRamControl; + } +} diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs new file mode 100644 index 000000000..466bff8fd --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Concurrent; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Ryujinx.Graphics.Gpu.Engine.GPFifo +{ + /// + /// Represents a GPU General Purpose FIFO device. + /// + public sealed class GPFifoDevice : IDisposable + { + /// + /// Indicates if the command buffer has pre-fetch enabled. + /// + private enum CommandBufferType + { + Prefetch, + NoPrefetch + } + + /// + /// Command buffer data. + /// + private struct CommandBuffer + { + /// + /// The type of the command buffer. + /// + public CommandBufferType Type; + + /// + /// Fetched data. + /// + public int[] Words; + + /// + /// The GPFIFO entry address (used in mode). + /// + public ulong EntryAddress; + + /// + /// The count of entries inside this GPFIFO entry. + /// + public uint EntryCount; + + /// + /// Fetch the command buffer. + /// + public void Fetch(GpuContext context) + { + if (Words == null) + { + Words = MemoryMarshal.Cast(context.MemoryAccessor.GetSpan(EntryAddress, (int)EntryCount * 4)).ToArray(); + } + } + } + + private readonly ConcurrentQueue _commandBufferQueue; + + private CommandBuffer _currentCommandBuffer; + + private readonly bool _ibEnable; + private readonly GpuContext _context; + private readonly AutoResetEvent _event; + private readonly GPFifoProcessor _processor; + + /// + /// Creates a new instance of the GPU General Purpose FIFO device. + /// + /// GPU context that the GPFIFO belongs to + internal GPFifoDevice(GpuContext context) + { + _commandBufferQueue = new ConcurrentQueue(); + _ibEnable = true; + _context = context; + _event = new AutoResetEvent(false); + + _processor = new GPFifoProcessor(context); + } + + /// + /// Signal the FIFO that there are new entries to process. + /// + public void SignalNewEntries() + { + _event.Set(); + } + + /// + /// Push a GPFIFO entry in the form of a prefetched command buffer. + /// It is intended to be used by nvservices to handle special cases. + /// + /// The command buffer containing the prefetched commands + public void PushHostCommandBuffer(int[] commandBuffer) + { + _commandBufferQueue.Enqueue(new CommandBuffer + { + Type = CommandBufferType.Prefetch, + Words = commandBuffer, + EntryAddress = ulong.MaxValue, + EntryCount = (uint)commandBuffer.Length + }); + } + + /// + /// Create a CommandBuffer from a GPFIFO entry. + /// + /// The GPFIFO entry + /// A new CommandBuffer based on the GPFIFO entry + private CommandBuffer CreateCommandBuffer(GPEntry entry) + { + CommandBufferType type = CommandBufferType.Prefetch; + + if (entry.Entry1Sync == Entry1Sync.Wait) + { + type = CommandBufferType.NoPrefetch; + } + + ulong startAddress = ((ulong)entry.Entry0Get << 2) | ((ulong)entry.Entry1GetHi << 32); + + return new CommandBuffer + { + Type = type, + Words = null, + EntryAddress = startAddress, + EntryCount = (uint)entry.Entry1Length + }; + } + + /// + /// Pushes GPFIFO entries. + /// + /// GPFIFO entries + public void PushEntries(ReadOnlySpan entries) + { + bool beforeBarrier = true; + + for (int index = 0; index < entries.Length; index++) + { + ulong entry = entries[index]; + + CommandBuffer commandBuffer = CreateCommandBuffer(Unsafe.As(ref entry)); + + if (beforeBarrier && commandBuffer.Type == CommandBufferType.Prefetch) + { + commandBuffer.Fetch(_context); + } + + if (commandBuffer.Type == CommandBufferType.NoPrefetch) + { + beforeBarrier = false; + } + + _commandBufferQueue.Enqueue(commandBuffer); + } + } + + /// + /// Waits until commands are pushed to the FIFO. + /// + /// True if commands were received, false if wait timed out + public bool WaitForCommands() + { + return _event.WaitOne(8); + } + + /// + /// Processes commands pushed to the FIFO. + /// + public void DispatchCalls() + { + while (_ibEnable && _commandBufferQueue.TryDequeue(out CommandBuffer entry)) + { + _currentCommandBuffer = entry; + _currentCommandBuffer.Fetch(_context); + + _processor.Process(_currentCommandBuffer.Words); + } + } + + /// + /// Disposes of resources used for GPFifo command processing. + /// + public void Dispose() => _event.Dispose(); + } +} diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs new file mode 100644 index 000000000..115361f3e --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoProcessor.cs @@ -0,0 +1,179 @@ +using Ryujinx.Graphics.Gpu.State; +using System; +using System.Runtime.CompilerServices; + +namespace Ryujinx.Graphics.Gpu.Engine.GPFifo +{ + /// + /// Represents a GPU General Purpose FIFO command processor. + /// + class GPFifoProcessor + { + private const int MacrosCount = 0x80; + private const int MacroIndexMask = MacrosCount - 1; + + private readonly GpuContext _context; + + /// + /// Internal GPFIFO state. + /// + private struct DmaState + { + public int Method; + public int SubChannel; + public int MethodCount; + public bool NonIncrementing; + public bool IncrementOnce; + } + + private DmaState _state; + + private readonly GpuState[] _subChannels; + private readonly GPFifoClass _fifoClass; + + /// + /// Creates a new instance of the GPU General Purpose FIFO command processor. + /// + /// GPU context + public GPFifoProcessor(GpuContext context) + { + _context = context; + + _fifoClass = new GPFifoClass(context); + + _subChannels = new GpuState[8]; + + for (int index = 0; index < _subChannels.Length; index++) + { + _subChannels[index] = new GpuState(); + + _context.Methods.RegisterCallbacks(_subChannels[index]); + } + } + + /// + /// Processes a command buffer. + /// + /// Command buffer + public void Process(ReadOnlySpan commandBuffer) + { + for (int index = 0; index < commandBuffer.Length; index++) + { + int command = commandBuffer[index]; + + if (_state.MethodCount != 0) + { + Send(new MethodParams(_state.Method, command, _state.SubChannel, _state.MethodCount)); + + if (!_state.NonIncrementing) + { + _state.Method++; + } + + if (_state.IncrementOnce) + { + _state.NonIncrementing = true; + } + + _state.MethodCount--; + } + else + { + CompressedMethod meth = Unsafe.As(ref command); + + if (TryFastUniformBufferUpdate(meth, commandBuffer, index)) + { + index += meth.MethodCount; + continue; + } + + switch (meth.SecOp) + { + case SecOp.IncMethod: + case SecOp.NonIncMethod: + case SecOp.OneInc: + _state.Method = meth.MethodAddress; + _state.SubChannel = meth.MethodSubchannel; + _state.MethodCount = meth.MethodCount; + _state.IncrementOnce = meth.SecOp == SecOp.OneInc; + _state.NonIncrementing = meth.SecOp == SecOp.NonIncMethod; + break; + case SecOp.ImmdDataMethod: + Send(new MethodParams(meth.MethodAddress, meth.ImmdData, meth.MethodSubchannel, 1)); + break; + } + } + } + } + + /// + /// Tries to perform a fast constant buffer data update. + /// If successful, all data will be copied at once, and + 1 + /// command buffer entries will be consumed. + /// + /// Compressed method to be checked + /// Command buffer where is contained + /// Offset at where is located + /// True if the fast copy was successful, false otherwise + private bool TryFastUniformBufferUpdate(CompressedMethod meth, ReadOnlySpan commandBuffer, int offset) + { + int availableCount = commandBuffer.Length - offset; + + if (meth.MethodCount < availableCount && + meth.SecOp == SecOp.NonIncMethod && + meth.MethodAddress == (int)MethodOffset.UniformBufferUpdateData) + { + GpuState state = _subChannels[meth.MethodSubchannel]; + + _context.Methods.UniformBufferUpdate(state, commandBuffer.Slice(offset + 1, meth.MethodCount)); + + return true; + } + + return false; + } + + /// + /// Sends a uncompressed method for processing by the graphics pipeline. + /// + /// Method to be processed + private void Send(MethodParams meth) + { + if ((MethodOffset)meth.Method == MethodOffset.BindChannel) + { + _subChannels[meth.SubChannel] = new GpuState(); + + _context.Methods.RegisterCallbacks(_subChannels[meth.SubChannel]); + } + else if (meth.Method < 0x60) + { + // TODO: check if macros are shared between subchannels or not. For now let's assume they are. + _fifoClass.Write(meth.Method * 4, meth.Argument); + } + else if (meth.Method < 0xe00) + { + _subChannels[meth.SubChannel].CallMethod(meth, _fifoClass.ShadowCtrl); + } + else + { + int macroIndex = (meth.Method >> 1) & MacroIndexMask; + + if ((meth.Method & 1) != 0) + { + _fifoClass.MmePushArgument(macroIndex, meth.Argument); + } + else + { + _fifoClass.MmeStart(macroIndex, meth.Argument); + } + + if (meth.IsLastCall) + { + _fifoClass.CallMme(macroIndex, _subChannels[meth.SubChannel]); + + _context.Methods.PerformDeferredDraws(); + } + } + } + } +} diff --git a/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs b/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs new file mode 100644 index 000000000..10127d11b --- /dev/null +++ b/Ryujinx.Graphics.Gpu/Engine/MME/Macro.cs @@ -0,0 +1,69 @@ +using Ryujinx.Graphics.Gpu.State; + +namespace Ryujinx.Graphics.Gpu.Engine.MME +{ + /// + /// GPU macro program. + /// + struct Macro + { + /// + /// Word offset of the code on the code memory. + /// + public int Position { get; } + + private bool _executionPending; + private int _argument; + + private readonly MacroInterpreter _interpreter; + + /// + /// Creates a new instance of the GPU cached macro program. + /// + /// Macro code start position + public Macro(int position) + { + Position = position; + + _executionPending = false; + _argument = 0; + + _interpreter = new MacroInterpreter(); + } + + /// + /// Sets the first argument for the macro call. + /// + /// First argument + public void StartExecution(int argument) + { + _argument = argument; + + _executionPending = true; + } + + /// + /// Starts executing the macro program code. + /// + /// Program code + /// Current GPU state + public void Execute(int[] mme, ShadowRamControl shadowCtrl, GpuState state) + { + if (_executionPending) + { + _executionPending = false; + + _interpreter?.Execute(mme, Position, _argument, shadowCtrl, state); + } + } + + /// + /// Pushes an argument to the macro call argument FIFO. + /// + /// Argument to be pushed + public void PushArgument(int argument) + { + _interpreter?.Fifo.Enqueue(argument); + } + } +} diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodFifo.cs b/Ryujinx.Graphics.Gpu/Engine/MethodFifo.cs deleted file mode 100644 index c1f45941c..000000000 --- a/Ryujinx.Graphics.Gpu/Engine/MethodFifo.cs +++ /dev/null @@ -1,103 +0,0 @@ -using Ryujinx.Graphics.Gpu.State; -using System.Threading; - -namespace Ryujinx.Graphics.Gpu.Engine -{ - partial class Methods - { - /// - /// Writes a GPU counter to guest memory. - /// - /// Current GPU state - /// Method call argument - public void Semaphore(GpuState state, int argument) - { - FifoSemaphoreOperation op = (FifoSemaphoreOperation)(argument & 3); - - var semaphore = state.Get(MethodOffset.Semaphore); - - int value = semaphore.Payload; - - if (op == FifoSemaphoreOperation.Counter) - { - // TODO: There's much more that should be done here. - // NVN only supports the "Accumulate" mode, so we - // can't currently guess which bits specify the - // reduction operation. - value += _context.MemoryAccessor.Read(semaphore.Address.Pack()); - } - - _context.MemoryAccessor.Write(semaphore.Address.Pack(), value); - - _context.AdvanceSequence(); - } - - /// - /// Waits for the GPU to be idle. - /// - /// Current GPU state - /// Method call argument - public void WaitForIdle(GpuState state, int argument) - { - PerformDeferredDraws(); - - _context.Renderer.Pipeline.Barrier(); - } - - /// - /// Send macro code/data to the MME. - /// - /// Current GPU state - /// Method call argument - public void SendMacroCodeData(GpuState state, int argument) - { - int macroUploadAddress = state.Get(MethodOffset.MacroUploadAddress); - - _context.Fifo.SendMacroCodeData(macroUploadAddress++, argument); - - state.Write((int)MethodOffset.MacroUploadAddress, macroUploadAddress); - } - - /// - /// Bind a macro index to a position for the MME. - /// - /// Current GPU state - /// Method call argument - public void BindMacro(GpuState state, int argument) - { - int macroBindingIndex = state.Get(MethodOffset.MacroBindingIndex); - - _context.Fifo.BindMacro(macroBindingIndex++, argument); - - state.Write((int)MethodOffset.MacroBindingIndex, macroBindingIndex); - } - - public void SetMmeShadowRamControl(GpuState state, int argument) - { - _context.Fifo.SetMmeShadowRamControl((ShadowRamControl)argument); - } - - /// - /// Apply a fence operation on a syncpoint. - /// - /// Current GPU state - /// Method call argument - public void FenceAction(GpuState state, int argument) - { - uint threshold = state.Get(MethodOffset.FenceValue); - - FenceActionOperation operation = (FenceActionOperation)(argument & 1); - - uint syncpointId = (uint)(argument >> 8) & 0xFF; - - if (operation == FenceActionOperation.Acquire) - { - _context.Synchronization.WaitOnSyncpoint(syncpointId, threshold, Timeout.InfiniteTimeSpan); - } - else if (operation == FenceActionOperation.Increment) - { - _context.Synchronization.IncrementSyncpoint(syncpointId); - } - } - } -} diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs index 524f5e039..032a58683 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs @@ -1,4 +1,6 @@ using Ryujinx.Graphics.Gpu.State; +using System; +using System.Runtime.InteropServices; namespace Ryujinx.Graphics.Gpu.Engine { @@ -19,5 +21,21 @@ namespace Ryujinx.Graphics.Gpu.Engine _context.AdvanceSequence(); } + + /// + /// Updates the uniform buffer data with inline data. + /// + /// Current GPU state + /// Data to be written to the uniform buffer + public void UniformBufferUpdate(GpuState state, ReadOnlySpan data) + { + var uniformBuffer = state.Get(MethodOffset.UniformBufferState); + + _context.MemoryAccessor.Write(uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset, MemoryMarshal.Cast(data)); + + state.SetUniformBufferOffset(uniformBuffer.Offset + data.Length * 4); + + _context.AdvanceSequence(); + } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs index df0e713df..e84687eff 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Methods.cs @@ -106,20 +106,6 @@ namespace Ryujinx.Graphics.Gpu.Engine state.RegisterCallback(MethodOffset.UniformBufferBindFragment, UniformBufferBindFragment); } - /// - /// Register callback for Fifo method calls that triggers an action on the GPFIFO. - /// - /// GPU state where the triggers will be registered - public void RegisterCallbacksForFifo(GpuState state) - { - state.RegisterCallback(MethodOffset.Semaphore, Semaphore); - state.RegisterCallback(MethodOffset.FenceAction, FenceAction); - state.RegisterCallback(MethodOffset.WaitForIdle, WaitForIdle); - state.RegisterCallback(MethodOffset.SendMacroCodeData, SendMacroCodeData); - state.RegisterCallback(MethodOffset.BindMacro, BindMacro); - state.RegisterCallback(MethodOffset.SetMmeShadowRamControl, SetMmeShadowRamControl); - } - /// /// Updates host state based on the current guest GPU state. /// diff --git a/Ryujinx.Graphics.Gpu/GpuContext.cs b/Ryujinx.Graphics.Gpu/GpuContext.cs index b07694b95..8e9f27329 100644 --- a/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -1,5 +1,6 @@ using Ryujinx.Graphics.GAL; using Ryujinx.Graphics.Gpu.Engine; +using Ryujinx.Graphics.Gpu.Engine.GPFifo; using Ryujinx.Graphics.Gpu.Memory; using Ryujinx.Graphics.Gpu.Synchronization; using System; @@ -37,14 +38,9 @@ namespace Ryujinx.Graphics.Gpu internal Methods Methods { get; } /// - /// GPU commands FIFO. + /// GPU General Purpose FIFO queue. /// - internal NvGpuFifo Fifo { get; } - - /// - /// DMA pusher. - /// - public DmaPusher DmaPusher { get; } + public GPFifoDevice GPFifo { get; } /// /// GPU synchronization manager. @@ -83,9 +79,7 @@ namespace Ryujinx.Graphics.Gpu Methods = new Methods(this); - Fifo = new NvGpuFifo(this); - - DmaPusher = new DmaPusher(this); + GPFifo = new GPFifoDevice(this); Synchronization = new SynchronizationManager(); @@ -125,6 +119,7 @@ namespace Ryujinx.Graphics.Gpu Methods.BufferManager.Dispose(); Methods.TextureManager.Dispose(); Renderer.Dispose(); + GPFifo.Dispose(); } } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/NvGpuFifo.cs b/Ryujinx.Graphics.Gpu/NvGpuFifo.cs deleted file mode 100644 index 36a275e29..000000000 --- a/Ryujinx.Graphics.Gpu/NvGpuFifo.cs +++ /dev/null @@ -1,220 +0,0 @@ -using Ryujinx.Graphics.Gpu.State; -using System.IO; - -namespace Ryujinx.Graphics.Gpu -{ - /// - /// GPU commands FIFO. - /// - class NvGpuFifo - { - private const int MacrosCount = 0x80; - private const int MacroIndexMask = MacrosCount - 1; - - // Note: The size of the macro memory is unknown, we just make - // a guess here and use 256kb as the size. Increase if needed. - private const int MmeWords = 256 * 256; - - private GpuContext _context; - - /// - /// Cached GPU macro program. - /// - private struct CachedMacro - { - /// - /// Word offset of the code on the code memory. - /// - public int Position { get; } - - private bool _executionPending; - private int _argument; - - private MacroInterpreter _interpreter; - - /// - /// Creates a new instance of the GPU cached macro program. - /// - /// Macro code start position - public CachedMacro(int position) - { - Position = position; - - _executionPending = false; - _argument = 0; - - _interpreter = new MacroInterpreter(); - } - - /// - /// Sets the first argument for the macro call. - /// - /// First argument - public void StartExecution(int argument) - { - _argument = argument; - - _executionPending = true; - } - - /// - /// Starts executing the macro program code. - /// - /// Program code - /// Current GPU state - public void Execute(int[] mme, ShadowRamControl shadowCtrl, GpuState state) - { - if (_executionPending) - { - _executionPending = false; - - _interpreter?.Execute(mme, Position, _argument, shadowCtrl, state); - } - } - - /// - /// Pushes an argument to the macro call argument FIFO. - /// - /// Argument to be pushed - public void PushArgument(int argument) - { - _interpreter?.Fifo.Enqueue(argument); - } - } - - private ShadowRamControl _shadowCtrl; - - private CachedMacro[] _macros; - - private int[] _mme; - - /// - /// GPU sub-channel information. - /// - private class SubChannel - { - /// - /// Sub-channel GPU state. - /// - public GpuState State { get; } - - /// - /// Engine bound to the sub-channel. - /// - public ClassId Class { get; set; } - - /// - /// Creates a new instance of the GPU sub-channel. - /// - public SubChannel() - { - State = new GpuState(); - } - } - - private SubChannel[] _subChannels; - - private SubChannel _fifoChannel; - - /// - /// Creates a new instance of the GPU commands FIFO. - /// - /// GPU emulation context - public NvGpuFifo(GpuContext context) - { - _context = context; - - _macros = new CachedMacro[MacrosCount]; - - _mme = new int[MmeWords]; - - _fifoChannel = new SubChannel(); - - _context.Methods.RegisterCallbacksForFifo(_fifoChannel.State); - - _subChannels = new SubChannel[8]; - - for (int index = 0; index < _subChannels.Length; index++) - { - _subChannels[index] = new SubChannel(); - - _context.Methods.RegisterCallbacks(_subChannels[index].State); - } - } - - /// - /// Send macro code/data to the MME - /// - /// The index in the MME - /// The data to use - public void SendMacroCodeData(int index, int data) - { - _mme[index] = data; - } - - /// - /// Bind a macro index to a position for the MME - /// - /// The macro index - /// The position of the macro - public void BindMacro(int index, int position) - { - _macros[index] = new CachedMacro(position); - } - - /// - /// Change the shadow RAM setting - /// - /// The new Shadow RAM setting - public void SetMmeShadowRamControl(ShadowRamControl shadowCtrl) - { - _shadowCtrl = shadowCtrl; - } - - /// - /// Calls a GPU method. - /// - /// GPU method call parameters - public void CallMethod(MethodParams meth) - { - if ((MethodOffset)meth.Method == MethodOffset.BindChannel) - { - _subChannels[meth.SubChannel] = new SubChannel - { - Class = (ClassId)meth.Argument - }; - - _context.Methods.RegisterCallbacks(_subChannels[meth.SubChannel].State); - } - else if (meth.Method < 0x60) - { - // TODO: check if macros are shared between subchannels or not. For now let's assume they are. - _fifoChannel.State.CallMethod(meth, _shadowCtrl); - } - else if (meth.Method < 0xe00) - { - _subChannels[meth.SubChannel].State.CallMethod(meth, _shadowCtrl); - } - else - { - int macroIndex = (meth.Method >> 1) & MacroIndexMask; - - if ((meth.Method & 1) != 0) - { - _macros[macroIndex].PushArgument(meth.Argument); - } - else - { - _macros[macroIndex].StartExecution(meth.Argument); - } - - if (meth.IsLastCall) - { - _macros[macroIndex].Execute(_mme, _shadowCtrl, _subChannels[meth.SubChannel].State); - - _context.Methods.PerformDeferredDraws(); - } - } - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj b/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj index 9348d04bb..a9e81be35 100644 --- a/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj +++ b/Ryujinx.Graphics.Gpu/Ryujinx.Graphics.Gpu.csproj @@ -2,6 +2,7 @@ + diff --git a/Ryujinx.Graphics.Gpu/State/FenceActionOperation.cs b/Ryujinx.Graphics.Gpu/State/FenceActionOperation.cs deleted file mode 100644 index c03443a8d..000000000 --- a/Ryujinx.Graphics.Gpu/State/FenceActionOperation.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Ryujinx.Graphics.Gpu.State -{ - /// - /// Fence action operations. - /// - enum FenceActionOperation - { - Acquire = 0, - Increment = 1 - } -} diff --git a/Ryujinx.Graphics.Gpu/State/FifoSemaphoreOperation.cs b/Ryujinx.Graphics.Gpu/State/FifoSemaphoreOperation.cs deleted file mode 100644 index a6ccdcfe4..000000000 --- a/Ryujinx.Graphics.Gpu/State/FifoSemaphoreOperation.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Ryujinx.Graphics.Gpu.State -{ - enum FifoSemaphoreOperation - { - Counter = 0, - Acquire = 1, - Release = 2 - } -} diff --git a/Ryujinx.Graphics.Gpu/State/MethodOffset.cs b/Ryujinx.Graphics.Gpu/State/MethodOffset.cs index d9e2ce930..505e3d89e 100644 --- a/Ryujinx.Graphics.Gpu/State/MethodOffset.cs +++ b/Ryujinx.Graphics.Gpu/State/MethodOffset.cs @@ -9,15 +9,6 @@ namespace Ryujinx.Graphics.Gpu.State enum MethodOffset { BindChannel = 0x0, - Semaphore = 0x4, - FenceValue = 0x1c, - FenceAction = 0x1d, - WaitForIdle = 0x44, - MacroUploadAddress = 0x45, - SendMacroCodeData = 0x46, - MacroBindingIndex = 0x47, - BindMacro = 0x48, - SetMmeShadowRamControl = 0x49, I2mParams = 0x60, LaunchDma = 0x6c, LoadInlineData = 0x6d, diff --git a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs index 70c9a47bf..b45d84018 100644 --- a/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs +++ b/Ryujinx.HLE/HOS/Services/Nv/NvDrvServices/NvHostChannel/NvHostChannelDeviceFile.cs @@ -414,10 +414,10 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceWait) && !_device.System.HostSyncpoint.IsSyncpointExpired(header.Fence.Id, header.Fence.Value)) { - _device.Gpu.DmaPusher.PushHostCommandBuffer(CreateWaitCommandBuffer(header.Fence)); + _device.Gpu.GPFifo.PushHostCommandBuffer(CreateWaitCommandBuffer(header.Fence)); } - _device.Gpu.DmaPusher.PushEntries(entries); + _device.Gpu.GPFifo.PushEntries(entries); header.Fence.Id = _channelSyncpoint.Id; @@ -439,12 +439,12 @@ namespace Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostChannel if (header.Flags.HasFlag(SubmitGpfifoFlags.FenceIncrement)) { - _device.Gpu.DmaPusher.PushHostCommandBuffer(CreateIncrementCommandBuffer(ref header.Fence, header.Flags)); + _device.Gpu.GPFifo.PushHostCommandBuffer(CreateIncrementCommandBuffer(ref header.Fence, header.Flags)); } header.Flags = SubmitGpfifoFlags.None; - _device.Gpu.DmaPusher.SignalNewEntries(); + _device.Gpu.GPFifo.SignalNewEntries(); return NvInternalResult.Success; } diff --git a/Ryujinx.HLE/Switch.cs b/Ryujinx.HLE/Switch.cs index 9defe25d5..2e1a4b66a 100644 --- a/Ryujinx.HLE/Switch.cs +++ b/Ryujinx.HLE/Switch.cs @@ -148,12 +148,12 @@ namespace Ryujinx.HLE public bool WaitFifo() { - return Gpu.DmaPusher.WaitForCommands(); + return Gpu.GPFifo.WaitForCommands(); } public void ProcessFrame() { - Gpu.DmaPusher.DispatchCalls(); + Gpu.GPFifo.DispatchCalls(); } public void PresentFrame(Action swapBuffersCallback) From 41ac8242733f761d01105fa5674007317010a22a Mon Sep 17 00:00:00 2001 From: Alex Barney Date: Thu, 23 Jul 2020 21:12:19 -0700 Subject: [PATCH 05/12] Update to LibHac 0.11.3 (#1414) Removes the timeout when deleting files from the local file system --- Ryujinx.HLE/Ryujinx.HLE.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Ryujinx.HLE/Ryujinx.HLE.csproj b/Ryujinx.HLE/Ryujinx.HLE.csproj index 01e20792f..bc599c05f 100644 --- a/Ryujinx.HLE/Ryujinx.HLE.csproj +++ b/Ryujinx.HLE/Ryujinx.HLE.csproj @@ -58,7 +58,7 @@ - + From 80d4199fb381a544786a6447adca998561d737dc Mon Sep 17 00:00:00 2001 From: gdkchan Date: Fri, 24 Jul 2020 16:13:14 -0300 Subject: [PATCH 06/12] Refactor NativeContext (#1410) * Refactor NativeContext * Fix bugs * Use correct counts * Check index using register count constants --- ARMeilleure/State/NativeContext.cs | 161 ++++++++++++----------------- 1 file changed, 67 insertions(+), 94 deletions(-) diff --git a/ARMeilleure/State/NativeContext.cs b/ARMeilleure/State/NativeContext.cs index 10c4c37f3..9df69a4ec 100644 --- a/ARMeilleure/State/NativeContext.cs +++ b/ARMeilleure/State/NativeContext.cs @@ -1,21 +1,23 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Memory; using System; -using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; namespace ARMeilleure.State { class NativeContext : IDisposable { - private const int IntSize = 8; - private const int VecSize = 16; - private const int FlagSize = 4; - private const int ExtraSize = 8; + private unsafe struct NativeCtxStorage + { + public fixed ulong X[RegisterConsts.IntRegsCount]; + public fixed ulong V[RegisterConsts.VecRegsCount * 2]; + public fixed uint Flags[RegisterConsts.FlagsCount]; + public fixed uint FpFlags[RegisterConsts.FpFlagsCount]; + public int Counter; + public ulong CallAddress; + } - private const int TotalSize = RegisterConsts.IntRegsCount * IntSize + - RegisterConsts.VecRegsCount * VecSize + - RegisterConsts.FlagsCount * FlagSize + - RegisterConsts.FpFlagsCount * FlagSize + ExtraSize; + private static NativeCtxStorage _dummyStorage = new NativeCtxStorage(); private readonly IJitMemoryBlock _block; @@ -23,179 +25,150 @@ namespace ARMeilleure.State public NativeContext(IJitMemoryAllocator allocator) { - _block = allocator.Allocate(TotalSize); + _block = allocator.Allocate((ulong)Unsafe.SizeOf()); } - public ulong GetX(int index) + public unsafe ulong GetX(int index) { if ((uint)index >= RegisterConsts.IntRegsCount) { throw new ArgumentOutOfRangeException(nameof(index)); } - return (ulong)Marshal.ReadInt64(BasePtr, index * IntSize); + return GetStorage().X[index]; } - public void SetX(int index, ulong value) + public unsafe void SetX(int index, ulong value) { if ((uint)index >= RegisterConsts.IntRegsCount) { throw new ArgumentOutOfRangeException(nameof(index)); } - Marshal.WriteInt64(BasePtr, index * IntSize, (long)value); + GetStorage().X[index] = value; } - public V128 GetV(int index) + public unsafe V128 GetV(int index) { - if ((uint)index >= RegisterConsts.IntRegsCount) + if ((uint)index >= RegisterConsts.VecRegsCount) { throw new ArgumentOutOfRangeException(nameof(index)); } - int offset = RegisterConsts.IntRegsCount * IntSize + index * VecSize; - - return new V128( - Marshal.ReadInt64(BasePtr, offset + 0), - Marshal.ReadInt64(BasePtr, offset + 8)); + return new V128(GetStorage().V[index * 2 + 0], GetStorage().V[index * 2 + 1]); } - public void SetV(int index, V128 value) + public unsafe void SetV(int index, V128 value) { - if ((uint)index >= RegisterConsts.IntRegsCount) + if ((uint)index >= RegisterConsts.VecRegsCount) { throw new ArgumentOutOfRangeException(nameof(index)); } - int offset = RegisterConsts.IntRegsCount * IntSize + index * VecSize; - - Marshal.WriteInt64(BasePtr, offset + 0, value.Extract(0)); - Marshal.WriteInt64(BasePtr, offset + 8, value.Extract(1)); + GetStorage().V[index * 2 + 0] = value.Extract(0); + GetStorage().V[index * 2 + 1] = value.Extract(1); } - public bool GetPstateFlag(PState flag) + public unsafe bool GetPstateFlag(PState flag) { if ((uint)flag >= RegisterConsts.FlagsCount) { throw new ArgumentException($"Invalid flag \"{flag}\" specified."); } - int offset = - RegisterConsts.IntRegsCount * IntSize + - RegisterConsts.VecRegsCount * VecSize + (int)flag * FlagSize; - - int value = Marshal.ReadInt32(BasePtr, offset); - - return value != 0; + return GetStorage().Flags[(int)flag] != 0; } - public void SetPstateFlag(PState flag, bool value) + public unsafe void SetPstateFlag(PState flag, bool value) { if ((uint)flag >= RegisterConsts.FlagsCount) { throw new ArgumentException($"Invalid flag \"{flag}\" specified."); } - int offset = - RegisterConsts.IntRegsCount * IntSize + - RegisterConsts.VecRegsCount * VecSize + (int)flag * FlagSize; - - Marshal.WriteInt32(BasePtr, offset, value ? 1 : 0); + GetStorage().Flags[(int)flag] = value ? 1u : 0u; } - public bool GetFPStateFlag(FPState flag) + public unsafe bool GetFPStateFlag(FPState flag) { - if ((uint)flag >= RegisterConsts.FlagsCount) + if ((uint)flag >= RegisterConsts.FpFlagsCount) { throw new ArgumentException($"Invalid flag \"{flag}\" specified."); } - int offset = - RegisterConsts.IntRegsCount * IntSize + - RegisterConsts.VecRegsCount * VecSize + - RegisterConsts.FlagsCount * FlagSize + (int)flag * FlagSize; - - int value = Marshal.ReadInt32(BasePtr, offset); - - return value != 0; + return GetStorage().FpFlags[(int)flag] != 0; } - public void SetFPStateFlag(FPState flag, bool value) + public unsafe void SetFPStateFlag(FPState flag, bool value) { - if ((uint)flag >= RegisterConsts.FlagsCount) + if ((uint)flag >= RegisterConsts.FpFlagsCount) { throw new ArgumentException($"Invalid flag \"{flag}\" specified."); } - int offset = - RegisterConsts.IntRegsCount * IntSize + - RegisterConsts.VecRegsCount * VecSize + - RegisterConsts.FlagsCount * FlagSize + (int)flag * FlagSize; - - Marshal.WriteInt32(BasePtr, offset, value ? 1 : 0); + GetStorage().FpFlags[(int)flag] = value ? 1u : 0u; } - public int GetCounter() - { - return Marshal.ReadInt32(BasePtr, GetCounterOffset()); - } + public int GetCounter() => GetStorage().Counter; + public void SetCounter(int value) => GetStorage().Counter = value; - public void SetCounter(int value) + public unsafe static int GetRegisterOffset(Register reg) { - Marshal.WriteInt32(BasePtr, GetCounterOffset(), value); - } - - public static int GetRegisterOffset(Register reg) - { - int offset, size; - if (reg.Type == RegisterType.Integer) { - offset = reg.Index * IntSize; + if ((uint)reg.Index >= RegisterConsts.IntRegsCount) + { + throw new ArgumentException("Invalid register."); + } - size = IntSize; + return StorageOffset(ref _dummyStorage, ref _dummyStorage.X[reg.Index]); } else if (reg.Type == RegisterType.Vector) { - offset = RegisterConsts.IntRegsCount * IntSize + reg.Index * VecSize; + if ((uint)reg.Index >= RegisterConsts.VecRegsCount) + { + throw new ArgumentException("Invalid register."); + } - size = VecSize; + return StorageOffset(ref _dummyStorage, ref _dummyStorage.V[reg.Index * 2]); } - else /* if (reg.Type == RegisterType.Flag) */ + else if (reg.Type == RegisterType.Flag) { - offset = RegisterConsts.IntRegsCount * IntSize + - RegisterConsts.VecRegsCount * VecSize + reg.Index * FlagSize; + if ((uint)reg.Index >= RegisterConsts.FlagsCount) + { + throw new ArgumentException("Invalid register."); + } - size = FlagSize; + return StorageOffset(ref _dummyStorage, ref _dummyStorage.Flags[reg.Index]); } - - if ((uint)(offset + size) > (uint)TotalSize) + else /* if (reg.Type == RegisterType.FpFlag) */ { - throw new ArgumentException("Invalid register."); - } + if ((uint)reg.Index >= RegisterConsts.FpFlagsCount) + { + throw new ArgumentException("Invalid register."); + } - return offset; + return StorageOffset(ref _dummyStorage, ref _dummyStorage.FpFlags[reg.Index]); + } } public static int GetCounterOffset() { - return RegisterConsts.IntRegsCount * IntSize + - RegisterConsts.VecRegsCount * VecSize + - RegisterConsts.FlagsCount * FlagSize + - RegisterConsts.FpFlagsCount * FlagSize; + return StorageOffset(ref _dummyStorage, ref _dummyStorage.Counter); } public static int GetCallAddressOffset() { - return RegisterConsts.IntRegsCount * IntSize + - RegisterConsts.VecRegsCount * VecSize + - RegisterConsts.FlagsCount * FlagSize + - RegisterConsts.FpFlagsCount * FlagSize + 4; + return StorageOffset(ref _dummyStorage, ref _dummyStorage.CallAddress); } - public void Dispose() + private static int StorageOffset(ref NativeCtxStorage storage, ref T target) { - _block.Dispose(); + return (int)Unsafe.ByteOffset(ref Unsafe.As(ref storage), ref target); } + + private unsafe ref NativeCtxStorage GetStorage() => ref Unsafe.AsRef((void*)_block.Pointer); + + public void Dispose() => _block.Dispose(); } } \ No newline at end of file From 111534a74e77664c2ab64c23e74d2173d6029b01 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 25 Jul 2020 03:39:45 -0300 Subject: [PATCH 07/12] Remove GPU MemoryAccessor (#1423) * Remove GPU MemoryAccessor * Update outdated XML doc * Update more outdated stuff --- Ryujinx.Graphics.Gpu/Engine/Compute.cs | 2 +- .../Engine/GPFifo/GPFifoClass.cs | 6 +- .../Engine/GPFifo/GPFifoDevice.cs | 2 +- .../Engine/MethodConditionalRendering.cs | 10 +-- Ryujinx.Graphics.Gpu/Engine/MethodReport.cs | 4 +- .../Engine/MethodUniformBufferUpdate.cs | 4 +- Ryujinx.Graphics.Gpu/GpuContext.cs | 7 -- Ryujinx.Graphics.Gpu/Memory/MemoryAccessor.cs | 85 ------------------- Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs | 14 ++- Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs | 2 +- Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs | 12 +-- 11 files changed, 34 insertions(+), 114 deletions(-) delete mode 100644 Ryujinx.Graphics.Gpu/Memory/MemoryAccessor.cs diff --git a/Ryujinx.Graphics.Gpu/Engine/Compute.cs b/Ryujinx.Graphics.Gpu/Engine/Compute.cs index 60fba0060..d718d4693 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Compute.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Compute.cs @@ -18,7 +18,7 @@ namespace Ryujinx.Graphics.Gpu.Engine { uint qmdAddress = (uint)state.Get(MethodOffset.DispatchParamsAddress); - var qmd = _context.MemoryAccessor.Read((ulong)qmdAddress << 8); + var qmd = _context.MemoryManager.Read((ulong)qmdAddress << 8); GpuVa shaderBaseAddress = state.Get(MethodOffset.ShaderBaseAddress); diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs index ec2e4bdc1..958253ecc 100644 --- a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs +++ b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoClass.cs @@ -80,13 +80,13 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo // TODO: Acquire operations (Wait), interrupts for invalid combinations. if (operation == SemaphoredOperation.Release) { - _context.MemoryAccessor.Write(address, value); + _context.MemoryManager.Write(address, value); } else if (operation == SemaphoredOperation.Reduction) { bool signed = _state.State.SemaphoredFormat == SemaphoredFormat.Signed; - int mem = _context.MemoryAccessor.Read(address); + int mem = _context.MemoryManager.Read(address); switch (_state.State.SemaphoredReduction) { @@ -116,7 +116,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo break; } - _context.MemoryAccessor.Write(address, value); + _context.MemoryManager.Write(address, value); } } diff --git a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs index 466bff8fd..c68a3ef2e 100644 --- a/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs +++ b/Ryujinx.Graphics.Gpu/Engine/GPFifo/GPFifoDevice.cs @@ -52,7 +52,7 @@ namespace Ryujinx.Graphics.Gpu.Engine.GPFifo { if (Words == null) { - Words = MemoryMarshal.Cast(context.MemoryAccessor.GetSpan(EntryAddress, (int)EntryCount * 4)).ToArray(); + Words = MemoryMarshal.Cast(context.MemoryManager.GetSpan(EntryAddress, (int)EntryCount * 4)).ToArray(); } } } diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodConditionalRendering.cs b/Ryujinx.Graphics.Gpu/Engine/MethodConditionalRendering.cs index 225c732e9..6bc1bef8a 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MethodConditionalRendering.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MethodConditionalRendering.cs @@ -63,7 +63,7 @@ namespace Ryujinx.Graphics.Gpu.Engine else { evt.Flush(); - return (_context.MemoryAccessor.Read(gpuVa) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False; + return (_context.MemoryManager.Read(gpuVa) != 0) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False; } } @@ -87,11 +87,11 @@ namespace Ryujinx.Graphics.Gpu.Engine if (evt != null && evt2 == null) { - useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt, _context.MemoryAccessor.Read(gpuVa + 16), isEqual); + useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt, _context.MemoryManager.Read(gpuVa + 16), isEqual); } else if (evt == null && evt2 != null) { - useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt2, _context.MemoryAccessor.Read(gpuVa), isEqual); + useHost = _context.Renderer.Pipeline.TryHostConditionalRendering(evt2, _context.MemoryManager.Read(gpuVa), isEqual); } else { @@ -107,8 +107,8 @@ namespace Ryujinx.Graphics.Gpu.Engine evt?.Flush(); evt2?.Flush(); - ulong x = _context.MemoryAccessor.Read(gpuVa); - ulong y = _context.MemoryAccessor.Read(gpuVa + 16); + ulong x = _context.MemoryManager.Read(gpuVa); + ulong y = _context.MemoryManager.Read(gpuVa + 16); return (isEqual ? x == y : x != y) ? ConditionalRenderEnabled.True : ConditionalRenderEnabled.False; } diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs b/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs index fcea43891..8320ba65d 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MethodReport.cs @@ -39,7 +39,7 @@ namespace Ryujinx.Graphics.Gpu.Engine { var rs = state.Get(MethodOffset.ReportState); - _context.MemoryAccessor.Write(rs.Address.Pack(), rs.Payload); + _context.MemoryManager.Write(rs.Address.Pack(), rs.Payload); _context.AdvanceSequence(); } @@ -85,7 +85,7 @@ namespace Ryujinx.Graphics.Gpu.Engine if (counter?.Invalid != true) { - _context.MemoryAccessor.Write(gpuVa, counterData); + _context.MemoryManager.Write(gpuVa, counterData); } }; diff --git a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs index 032a58683..61772327f 100644 --- a/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs +++ b/Ryujinx.Graphics.Gpu/Engine/MethodUniformBufferUpdate.cs @@ -15,7 +15,7 @@ namespace Ryujinx.Graphics.Gpu.Engine { var uniformBuffer = state.Get(MethodOffset.UniformBufferState); - _context.MemoryAccessor.Write(uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset, argument); + _context.MemoryManager.Write(uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset, argument); state.SetUniformBufferOffset(uniformBuffer.Offset + 4); @@ -31,7 +31,7 @@ namespace Ryujinx.Graphics.Gpu.Engine { var uniformBuffer = state.Get(MethodOffset.UniformBufferState); - _context.MemoryAccessor.Write(uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset, MemoryMarshal.Cast(data)); + _context.MemoryManager.Write(uniformBuffer.Address.Pack() + (uint)uniformBuffer.Offset, MemoryMarshal.Cast(data)); state.SetUniformBufferOffset(uniformBuffer.Offset + data.Length * 4); diff --git a/Ryujinx.Graphics.Gpu/GpuContext.cs b/Ryujinx.Graphics.Gpu/GpuContext.cs index 8e9f27329..bdbf77a6c 100644 --- a/Ryujinx.Graphics.Gpu/GpuContext.cs +++ b/Ryujinx.Graphics.Gpu/GpuContext.cs @@ -27,11 +27,6 @@ namespace Ryujinx.Graphics.Gpu /// public MemoryManager MemoryManager { get; } - /// - /// GPU memory accessor. - /// - public MemoryAccessor MemoryAccessor { get; } - /// /// GPU engine methods processing. /// @@ -75,8 +70,6 @@ namespace Ryujinx.Graphics.Gpu MemoryManager = new MemoryManager(this); - MemoryAccessor = new MemoryAccessor(this); - Methods = new Methods(this); GPFifo = new GPFifoDevice(this); diff --git a/Ryujinx.Graphics.Gpu/Memory/MemoryAccessor.cs b/Ryujinx.Graphics.Gpu/Memory/MemoryAccessor.cs deleted file mode 100644 index 2c53f6991..000000000 --- a/Ryujinx.Graphics.Gpu/Memory/MemoryAccessor.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace Ryujinx.Graphics.Gpu.Memory -{ - /// - /// GPU mapped memory accessor. - /// - public class MemoryAccessor - { - private GpuContext _context; - - /// - /// Creates a new instance of the GPU memory accessor. - /// - /// GPU context that the memory accessor belongs to - public MemoryAccessor(GpuContext context) - { - _context = context; - } - - /// - /// Reads a byte array from GPU mapped memory. - /// - /// GPU virtual address where the data is located - /// Size of the data in bytes - /// Byte array with the data - public byte[] ReadBytes(ulong gpuVa, int size) - { - return GetSpan(gpuVa, size).ToArray(); - } - - /// - /// Gets a read-only span of data from GPU mapped memory. - /// This reads as much data as possible, up to the specified maximum size. - /// - /// GPU virtual address where the data is located - /// Size of the data - /// The span of the data at the specified memory location - public ReadOnlySpan GetSpan(ulong gpuVa, int size) - { - ulong processVa = _context.MemoryManager.Translate(gpuVa); - - return _context.PhysicalMemory.GetSpan(processVa, size); - } - - /// - /// Reads data from GPU mapped memory. - /// - /// Type of the data - /// GPU virtual address where the data is located - /// The data at the specified memory location - public T Read(ulong gpuVa) where T : unmanaged - { - ulong processVa = _context.MemoryManager.Translate(gpuVa); - - return MemoryMarshal.Cast(_context.PhysicalMemory.GetSpan(processVa, Unsafe.SizeOf()))[0]; - } - - /// - /// Writes a 32-bits signed integer to GPU mapped memory. - /// - /// GPU virtual address to write the value into - /// The value to be written - public void Write(ulong gpuVa, T value) where T : unmanaged - { - ulong processVa = _context.MemoryManager.Translate(gpuVa); - - _context.PhysicalMemory.Write(processVa, MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref value, 1))); - } - - /// - /// Writes data to GPU mapped memory. - /// - /// GPU virtual address to write the data into - /// The data to be written - public void Write(ulong gpuVa, ReadOnlySpan data) - { - ulong processVa = _context.MemoryManager.Translate(gpuVa); - - _context.PhysicalMemory.Write(processVa, data); - } - } -} \ No newline at end of file diff --git a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs index 2d988f8d8..ace94442a 100644 --- a/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs +++ b/Ryujinx.Graphics.Gpu/Memory/MemoryManager.cs @@ -62,7 +62,6 @@ namespace Ryujinx.Graphics.Gpu.Memory /// /// Gets a read-only span of data from GPU mapped memory. - /// This reads as much data as possible, up to the specified maximum size. /// /// GPU virtual address where the data is located /// Size of the data @@ -87,6 +86,19 @@ namespace Ryujinx.Graphics.Gpu.Memory return _context.PhysicalMemory.GetWritableRegion(processVa, size); } + /// + /// Writes data to GPU mapped memory. + /// + /// Type of the data + /// GPU virtual address to write the value into + /// The value to be written + public void Write(ulong gpuVa, T value) where T : unmanaged + { + ulong processVa = Translate(gpuVa); + + _context.PhysicalMemory.Write(processVa, MemoryMarshal.Cast(MemoryMarshal.CreateSpan(ref value, 1))); + } + /// /// Writes data to GPU mapped memory. /// diff --git a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs index 2774b7d12..7c712a0f5 100644 --- a/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs +++ b/Ryujinx.Graphics.Gpu/Shader/GpuAccessor.cs @@ -81,7 +81,7 @@ namespace Ryujinx.Graphics.Gpu.Shader /// Data at the memory location public T MemoryRead(ulong address) where T : unmanaged { - return _context.MemoryAccessor.Read(address); + return _context.MemoryManager.Read(address); } /// diff --git a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs index d16f10578..ab777162b 100644 --- a/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs +++ b/Ryujinx.Graphics.Gpu/Shader/ShaderCache.cs @@ -262,13 +262,13 @@ namespace Ryujinx.Graphics.Gpu.Shader return true; } - ReadOnlySpan memoryCode = _context.MemoryAccessor.GetSpan(gpuVa, shader.Code.Length); + ReadOnlySpan memoryCode = _context.MemoryManager.GetSpan(gpuVa, shader.Code.Length); bool equals = memoryCode.SequenceEqual(shader.Code); if (equals && shader.Code2 != null) { - memoryCode = _context.MemoryAccessor.GetSpan(gpuVaA, shader.Code2.Length); + memoryCode = _context.MemoryManager.GetSpan(gpuVaA, shader.Code2.Length); equals = memoryCode.SequenceEqual(shader.Code2); } @@ -307,7 +307,7 @@ namespace Ryujinx.Graphics.Gpu.Shader program = Translator.Translate(gpuVa, gpuAccessor, DefaultFlags | TranslationFlags.Compute); - byte[] code = _context.MemoryAccessor.ReadBytes(gpuVa, program.Size); + byte[] code = _context.MemoryManager.GetSpan(gpuVa, program.Size).ToArray(); _dumper.Dump(code, compute: true, out string fullPath, out string codePath); @@ -344,8 +344,8 @@ namespace Ryujinx.Graphics.Gpu.Shader { ShaderProgram program = Translator.Translate(gpuVaA, gpuVa, gpuAccessor, DefaultFlags); - byte[] codeA = _context.MemoryAccessor.ReadBytes(gpuVaA, program.SizeA); - byte[] codeB = _context.MemoryAccessor.ReadBytes(gpuVa, program.Size); + byte[] codeA = _context.MemoryManager.GetSpan(gpuVaA, program.SizeA).ToArray(); + byte[] codeB = _context.MemoryManager.GetSpan(gpuVa, program.Size).ToArray(); _dumper.Dump(codeA, compute: false, out string fullPathA, out string codePathA); _dumper.Dump(codeB, compute: false, out string fullPathB, out string codePathB); @@ -364,7 +364,7 @@ namespace Ryujinx.Graphics.Gpu.Shader { ShaderProgram program = Translator.Translate(gpuVa, gpuAccessor, DefaultFlags); - byte[] code = _context.MemoryAccessor.ReadBytes(gpuVa, program.Size); + byte[] code = _context.MemoryManager.GetSpan(gpuVa, program.Size).ToArray(); _dumper.Dump(code, compute: false, out string fullPath, out string codePath); From 2678bf0010166e683364102221b52caebea8747e Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sat, 25 Jul 2020 15:01:57 -0300 Subject: [PATCH 08/12] PPTC version increment (#1427) --- ARMeilleure/Translation/PTC/Ptc.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index 032e111e7..ae92d4f39 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -20,7 +20,7 @@ namespace ARMeilleure.Translation.PTC { private const string HeaderMagic = "PTChd"; - private const int InternalVersion = 13; //! To be incremented manually for each change to the ARMeilleure project. + private const int InternalVersion = 14; //! To be incremented manually for each change to the ARMeilleure project. private const string BaseDir = "Ryujinx"; From 8dbcae1ff88927dc0734d5f0e24fbf8781d68590 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 26 Jul 2020 00:03:40 -0300 Subject: [PATCH 09/12] Implement BGRA texture support (#1418) * Implement BGRA texture support * Missing AppendLine * Remove empty lines * Address PR feedback --- Ryujinx.Graphics.GAL/Format.cs | 19 +++++ Ryujinx.Graphics.OpenGL/FormatTable.cs | 8 +-- Ryujinx.Graphics.OpenGL/Image/TextureBase.cs | 2 +- Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs | 65 ++++++++++++++++- Ryujinx.Graphics.OpenGL/Image/TextureView.cs | 44 +++++++++--- Ryujinx.Graphics.OpenGL/Pipeline.cs | 71 +++++++++++-------- Ryujinx.Graphics.OpenGL/Program.cs | 2 + Ryujinx.Graphics.OpenGL/Window.cs | 9 ++- .../CodeGen/Glsl/Declarations.cs | 6 ++ .../CodeGen/Glsl/DefaultNames.cs | 2 + .../CodeGen/Glsl/OperandManager.cs | 16 +++-- .../Translation/AttributeConsts.cs | 3 + .../Translation/EmitterContext.cs | 44 +++++++++--- .../Services/SurfaceFlinger/SurfaceFlinger.cs | 2 +- 14 files changed, 232 insertions(+), 61 deletions(-) diff --git a/Ryujinx.Graphics.GAL/Format.cs b/Ryujinx.Graphics.GAL/Format.cs index 19939c38a..a66673614 100644 --- a/Ryujinx.Graphics.GAL/Format.cs +++ b/Ryujinx.Graphics.GAL/Format.cs @@ -230,6 +230,25 @@ namespace Ryujinx.Graphics.GAL return false; } + /// + /// Checks if the texture format is a BGRA format with 8-bit components. + /// + /// Texture format + /// True if the texture format is a BGRA format with 8-bit components, false otherwise + public static bool IsBgra8(this Format format) + { + switch (format) + { + case Format.B8G8R8X8Unorm: + case Format.B8G8R8A8Unorm: + case Format.B8G8R8X8Srgb: + case Format.B8G8R8A8Srgb: + return true; + } + + return false; + } + /// /// Checks if the texture format is a depth, stencil or depth-stencil format. /// diff --git a/Ryujinx.Graphics.OpenGL/FormatTable.cs b/Ryujinx.Graphics.OpenGL/FormatTable.cs index b178553cd..4200ea7e0 100644 --- a/Ryujinx.Graphics.OpenGL/FormatTable.cs +++ b/Ryujinx.Graphics.OpenGL/FormatTable.cs @@ -164,10 +164,10 @@ namespace Ryujinx.Graphics.OpenGL Add(Format.B5G5R5X1Unorm, new FormatInfo(4, true, false, All.Rgb5, PixelFormat.Bgra, PixelType.UnsignedShort5551)); Add(Format.B5G5R5A1Unorm, new FormatInfo(4, true, false, All.Rgb5A1, PixelFormat.Bgra, PixelType.UnsignedShort5551)); Add(Format.A1B5G5R5Unorm, new FormatInfo(4, true, false, All.Rgb5A1, PixelFormat.Bgra, PixelType.UnsignedShort1555Reversed)); - Add(Format.B8G8R8X8Unorm, new FormatInfo(4, true, false, All.Rgba8, PixelFormat.Bgra, PixelType.UnsignedByte)); - Add(Format.B8G8R8A8Unorm, new FormatInfo(4, true, false, All.Rgba8, PixelFormat.Bgra, PixelType.UnsignedByte)); - Add(Format.B8G8R8X8Srgb, new FormatInfo(4, false, false, All.Srgb8, PixelFormat.BgraInteger, PixelType.UnsignedByte)); - Add(Format.B8G8R8A8Srgb, new FormatInfo(4, false, false, All.Srgb8Alpha8, PixelFormat.BgraInteger, PixelType.UnsignedByte)); + Add(Format.B8G8R8X8Unorm, new FormatInfo(4, true, false, All.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte)); + Add(Format.B8G8R8A8Unorm, new FormatInfo(4, true, false, All.Rgba8, PixelFormat.Rgba, PixelType.UnsignedByte)); + Add(Format.B8G8R8X8Srgb, new FormatInfo(4, false, false, All.Srgb8, PixelFormat.Rgba, PixelType.UnsignedByte)); + Add(Format.B8G8R8A8Srgb, new FormatInfo(4, false, false, All.Srgb8Alpha8, PixelFormat.Rgba, PixelType.UnsignedByte)); } private static void Add(Format format, FormatInfo info) diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs b/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs index dfb81642c..5f786decc 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureBase.cs @@ -8,7 +8,7 @@ namespace Ryujinx.Graphics.OpenGL.Image { public int Handle { get; protected set; } - protected TextureCreateInfo Info { get; } + public TextureCreateInfo Info { get; } public int Width { get; } public int Height { get; } diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs b/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs index e89d5614c..edfbec5f8 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureCopy.cs @@ -11,6 +11,9 @@ namespace Ryujinx.Graphics.OpenGL.Image private int _srcFramebuffer; private int _dstFramebuffer; + private int _copyPboHandle; + private int _copyPboSize; + public TextureCopy(Renderer renderer) { _renderer = renderer; @@ -23,12 +26,14 @@ namespace Ryujinx.Graphics.OpenGL.Image Extents2D dstRegion, bool linearFilter) { + TextureView srcConverted = src.Format.IsBgra8() != dst.Format.IsBgra8() ? BgraSwap(src) : src; + (int oldDrawFramebufferHandle, int oldReadFramebufferHandle) = ((Pipeline)_renderer.Pipeline).GetBoundFramebuffers(); GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, GetSrcFramebufferLazy()); GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, GetDstFramebufferLazy()); - Attach(FramebufferTarget.ReadFramebuffer, src.Format, src.Handle); + Attach(FramebufferTarget.ReadFramebuffer, src.Format, srcConverted.Handle); Attach(FramebufferTarget.DrawFramebuffer, dst.Format, dst.Handle); ClearBufferMask mask = GetMask(src.Format); @@ -68,6 +73,11 @@ namespace Ryujinx.Graphics.OpenGL.Image ((Pipeline)_renderer.Pipeline).RestoreScissor0Enable(); ((Pipeline)_renderer.Pipeline).RestoreRasterizerDiscard(); + + if (srcConverted != src) + { + srcConverted.Dispose(); + } } private static void Attach(FramebufferTarget target, Format format, int handle) @@ -117,6 +127,52 @@ namespace Ryujinx.Graphics.OpenGL.Image format == Format.D32Float; } + public TextureView BgraSwap(TextureView from) + { + TextureView to = (TextureView)_renderer.CreateTexture(from.Info, 1f); + + EnsurePbo(from); + + GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyPboHandle); + + from.WriteToPbo(0, forceBgra: true); + + GL.BindBuffer(BufferTarget.PixelPackBuffer, 0); + GL.BindBuffer(BufferTarget.PixelUnpackBuffer, _copyPboHandle); + + to.ReadFromPbo(0, _copyPboSize); + + GL.BindBuffer(BufferTarget.PixelUnpackBuffer, 0); + + return to; + } + + private void EnsurePbo(TextureView view) + { + int requiredSize = 0; + + for (int level = 0; level < view.Info.Levels; level++) + { + requiredSize += view.Info.GetMipSize(level); + } + + if (_copyPboSize < requiredSize && _copyPboHandle != 0) + { + GL.DeleteBuffer(_copyPboHandle); + + _copyPboHandle = 0; + } + + if (_copyPboHandle == 0) + { + _copyPboHandle = GL.GenBuffer(); + _copyPboSize = requiredSize; + + GL.BindBuffer(BufferTarget.PixelPackBuffer, _copyPboHandle); + GL.BufferData(BufferTarget.PixelPackBuffer, requiredSize, IntPtr.Zero, BufferUsageHint.DynamicCopy); + } + } + private int GetSrcFramebufferLazy() { if (_srcFramebuffer == 0) @@ -152,6 +208,13 @@ namespace Ryujinx.Graphics.OpenGL.Image _dstFramebuffer = 0; } + + if (_copyPboHandle != 0) + { + GL.DeleteBuffer(_copyPboHandle); + + _copyPboHandle = 0; + } } } } diff --git a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs index 02353ffaa..2d50eba5e 100644 --- a/Ryujinx.Graphics.OpenGL/Image/TextureView.cs +++ b/Ryujinx.Graphics.OpenGL/Image/TextureView.cs @@ -72,6 +72,15 @@ namespace Ryujinx.Graphics.OpenGL.Image (int)Info.SwizzleA.Convert() }; + if (Info.Format.IsBgra8()) + { + // Swap B <-> R for BGRA formats, as OpenGL has no support for them + // and we need to manually swap the components on read/write on the GPU. + int temp = swizzleRgba[0]; + swizzleRgba[0] = swizzleRgba[2]; + swizzleRgba[2] = temp; + } + GL.TexParameter(target, TextureParameterName.TextureSwizzleRgba, swizzleRgba); int maxLevel = Info.Levels - 1; @@ -189,7 +198,12 @@ namespace Ryujinx.Graphics.OpenGL.Image return data; } - private void WriteTo(IntPtr ptr) + public void WriteToPbo(int offset, bool forceBgra) + { + WriteTo(IntPtr.Zero + offset, forceBgra); + } + + private void WriteTo(IntPtr data, bool forceBgra = false) { TextureTarget target = Target.Convert(); @@ -197,6 +211,14 @@ namespace Ryujinx.Graphics.OpenGL.Image FormatInfo format = FormatTable.GetFormatInfo(Info.Format); + PixelFormat pixelFormat = format.PixelFormat; + PixelType pixelType = format.PixelType; + + if (forceBgra) + { + pixelFormat = PixelFormat.Bgra; + } + int faces = 1; if (target == TextureTarget.TextureCubeMap) @@ -214,20 +236,15 @@ namespace Ryujinx.Graphics.OpenGL.Image if (format.IsCompressed) { - GL.GetCompressedTexImage(target + face, level, ptr + faceOffset); + GL.GetCompressedTexImage(target + face, level, data + faceOffset); } else { - GL.GetTexImage( - target + face, - level, - format.PixelFormat, - format.PixelType, - ptr + faceOffset); + GL.GetTexImage(target + face, level, pixelFormat, pixelType, data + faceOffset); } } - ptr += Info.GetMipSize(level); + data += Info.GetMipSize(level); } } @@ -237,12 +254,17 @@ namespace Ryujinx.Graphics.OpenGL.Image { fixed (byte* ptr = data) { - SetData((IntPtr)ptr, data.Length); + ReadFrom((IntPtr)ptr, data.Length); } } } - private void SetData(IntPtr data, int size) + public void ReadFromPbo(int offset, int size) + { + ReadFrom(IntPtr.Zero + offset, size); + } + + private void ReadFrom(IntPtr data, int size) { TextureTarget target = Target.Convert(); diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index 2016d8524..09ba9be0b 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -31,6 +31,7 @@ namespace Ryujinx.Graphics.OpenGL private int _boundDrawFramebuffer; private int _boundReadFramebuffer; + private int[] _fpIsBgra = new int[8]; private float[] _fpRenderScale = new float[33]; private float[] _cpRenderScale = new float[32]; @@ -722,12 +723,12 @@ namespace Ryujinx.Graphics.OpenGL GL.Disable(EnableCap.ProgramPointSize); } - GL.PointParameter(origin == Origin.LowerLeft - ? PointSpriteCoordOriginParameter.LowerLeft + GL.PointParameter(origin == Origin.LowerLeft + ? PointSpriteCoordOriginParameter.LowerLeft : PointSpriteCoordOriginParameter.UpperLeft); // Games seem to set point size to 0 which generates a GL_INVALID_VALUE - // From the spec, GL_INVALID_VALUE is generated if size is less than or equal to 0. + // From the spec, GL_INVALID_VALUE is generated if size is less than or equal to 0. GL.PointSize(Math.Max(float.Epsilon, size)); } @@ -765,6 +766,7 @@ namespace Ryujinx.Graphics.OpenGL _program.Bind(); } + UpdateFpIsBgra(); SetRenderTargetScale(_fpRenderScale[0]); } @@ -814,12 +816,15 @@ namespace Ryujinx.Graphics.OpenGL TextureView color = (TextureView)colors[index]; _framebuffer.AttachColor(index, color); + + _fpIsBgra[index] = color != null && color.Format.IsBgra8() ? 1 : 0; } + UpdateFpIsBgra(); + TextureView depthStencilView = (TextureView)depthStencil; _framebuffer.AttachDepthStencil(depthStencilView); - _framebuffer.SetDrawBuffers(colors.Length); _hasDepthBuffer = depthStencil != null && depthStencilView.Format != Format.S8Uint; @@ -938,7 +943,9 @@ namespace Ryujinx.Graphics.OpenGL if (activeTarget != null && activeTarget.Width / (float)texture.Width == activeTarget.Height / (float)texture.Height) { - // If the texture's size is a multiple of the sampler size, enable interpolation using gl_FragCoord. (helps "invent" new integer values between scaled pixels) + // If the texture's size is a multiple of the sampler size, + // enable interpolation using gl_FragCoord. + // (helps "invent" new integer values between scaled pixels) interpolate = true; } } @@ -1112,6 +1119,14 @@ namespace Ryujinx.Graphics.OpenGL return (_boundDrawFramebuffer, _boundReadFramebuffer); } + private void UpdateFpIsBgra() + { + if (_program != null) + { + GL.Uniform1(_program.FragmentIsBgraUniform, 8, _fpIsBgra); + } + } + private void UpdateDepthTest() { // Enabling depth operations is only valid when we have @@ -1137,6 +1152,29 @@ namespace Ryujinx.Graphics.OpenGL } } + public void UpdateRenderScale(ShaderStage stage, int textureCount) + { + if (_program != null) + { + switch (stage) + { + case ShaderStage.Fragment: + if (_program.FragmentRenderScaleUniform != -1) + { + GL.Uniform1(_program.FragmentRenderScaleUniform, textureCount + 1, _fpRenderScale); + } + break; + + case ShaderStage.Compute: + if (_program.ComputeRenderScaleUniform != -1) + { + GL.Uniform1(_program.ComputeRenderScaleUniform, textureCount, _cpRenderScale); + } + break; + } + } + } + private void PrepareForDispatch() { if (_unit0Texture != null) @@ -1230,28 +1268,5 @@ namespace Ryujinx.Graphics.OpenGL _framebuffer?.Dispose(); _vertexArray?.Dispose(); } - - public void UpdateRenderScale(ShaderStage stage, int textureCount) - { - if (_program != null) - { - switch (stage) - { - case ShaderStage.Fragment: - if (_program.FragmentRenderScaleUniform != -1) - { - GL.Uniform1(_program.FragmentRenderScaleUniform, textureCount + 1, _fpRenderScale); - } - break; - - case ShaderStage.Compute: - if (_program.ComputeRenderScaleUniform != -1) - { - GL.Uniform1(_program.ComputeRenderScaleUniform, textureCount, _cpRenderScale); - } - break; - } - } - } } } diff --git a/Ryujinx.Graphics.OpenGL/Program.cs b/Ryujinx.Graphics.OpenGL/Program.cs index a0f8eb015..06f05f67c 100644 --- a/Ryujinx.Graphics.OpenGL/Program.cs +++ b/Ryujinx.Graphics.OpenGL/Program.cs @@ -25,6 +25,7 @@ namespace Ryujinx.Graphics.OpenGL public int Handle { get; private set; } + public int FragmentIsBgraUniform { get; } public int FragmentRenderScaleUniform { get; } public int ComputeRenderScaleUniform { get; } @@ -218,6 +219,7 @@ namespace Ryujinx.Graphics.OpenGL } } + FragmentIsBgraUniform = GL.GetUniformLocation(Handle, "is_bgra"); FragmentRenderScaleUniform = GL.GetUniformLocation(Handle, "fp_renderScale"); ComputeRenderScaleUniform = GL.GetUniformLocation(Handle, "cp_renderScale"); } diff --git a/Ryujinx.Graphics.OpenGL/Window.cs b/Ryujinx.Graphics.OpenGL/Window.cs index b7dc37843..a2f4e4ce9 100644 --- a/Ryujinx.Graphics.OpenGL/Window.cs +++ b/Ryujinx.Graphics.OpenGL/Window.cs @@ -51,10 +51,12 @@ namespace Ryujinx.Graphics.OpenGL GL.BindFramebuffer(FramebufferTarget.DrawFramebuffer, drawFramebuffer); GL.BindFramebuffer(FramebufferTarget.ReadFramebuffer, readFramebuffer); + TextureView viewConverted = view.Format.IsBgra8() ? _renderer.TextureCopy.BgraSwap(view) : view; + GL.FramebufferTexture( FramebufferTarget.ReadFramebuffer, FramebufferAttachment.ColorAttachment0, - view.Handle, + viewConverted.Handle, 0); GL.ReadBuffer(ReadBufferMode.ColorAttachment0); @@ -138,6 +140,11 @@ namespace Ryujinx.Graphics.OpenGL ((Pipeline)_renderer.Pipeline).RestoreScissor0Enable(); ((Pipeline)_renderer.Pipeline).RestoreRasterizerDiscard(); + + if (viewConverted != view) + { + viewConverted.Dispose(); + } } private int GetCopyFramebufferHandleLazy() diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs index efd30143b..40e277e06 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/Declarations.cs @@ -139,6 +139,12 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl if (context.Config.Stage == ShaderStage.Fragment || context.Config.Stage == ShaderStage.Compute) { + if (context.Config.Stage == ShaderStage.Fragment) + { + context.AppendLine($"uniform bool {DefaultNames.IsBgraName}[8];"); + context.AppendLine(); + } + if (DeclareRenderScale(context)) { context.AppendLine(); diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs index 4da38b2de..d1cf4636b 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/DefaultNames.cs @@ -23,5 +23,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl public const string SharedMemoryName = "shared_mem"; public const string UndefinedName = "undef"; + + public const string IsBgraName = "is_bgra"; } } \ No newline at end of file diff --git a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs index 8801fc111..4ae9a00ac 100644 --- a/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs +++ b/Ryujinx.Graphics.Shader/CodeGen/Glsl/OperandManager.cs @@ -64,6 +64,16 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl { AttributeConsts.GtMask, new BuiltInAttribute("unpackUint2x32(gl_SubGroupGtMaskARB).x", VariableType.U32) }, { AttributeConsts.LeMask, new BuiltInAttribute("unpackUint2x32(gl_SubGroupLeMaskARB).x", VariableType.U32) }, { AttributeConsts.LtMask, new BuiltInAttribute("unpackUint2x32(gl_SubGroupLtMaskARB).x", VariableType.U32) }, + + // Support uniforms. + { AttributeConsts.FragmentOutputIsBgraBase + 0, new BuiltInAttribute($"{DefaultNames.IsBgraName}[0]", VariableType.Bool) }, + { AttributeConsts.FragmentOutputIsBgraBase + 4, new BuiltInAttribute($"{DefaultNames.IsBgraName}[1]", VariableType.Bool) }, + { AttributeConsts.FragmentOutputIsBgraBase + 8, new BuiltInAttribute($"{DefaultNames.IsBgraName}[2]", VariableType.Bool) }, + { AttributeConsts.FragmentOutputIsBgraBase + 12, new BuiltInAttribute($"{DefaultNames.IsBgraName}[3]", VariableType.Bool) }, + { AttributeConsts.FragmentOutputIsBgraBase + 16, new BuiltInAttribute($"{DefaultNames.IsBgraName}[4]", VariableType.Bool) }, + { AttributeConsts.FragmentOutputIsBgraBase + 20, new BuiltInAttribute($"{DefaultNames.IsBgraName}[5]", VariableType.Bool) }, + { AttributeConsts.FragmentOutputIsBgraBase + 24, new BuiltInAttribute($"{DefaultNames.IsBgraName}[6]", VariableType.Bool) }, + { AttributeConsts.FragmentOutputIsBgraBase + 28, new BuiltInAttribute($"{DefaultNames.IsBgraName}[7]", VariableType.Bool) } }; private Dictionary _locals; @@ -149,8 +159,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl char swzMask = GetSwizzleMask((value >> 2) & 3); - if (value >= AttributeConsts.UserAttributeBase && - value < AttributeConsts.UserAttributeEnd) + if (value >= AttributeConsts.UserAttributeBase && value < AttributeConsts.UserAttributeEnd) { value -= AttributeConsts.UserAttributeBase; @@ -169,8 +178,7 @@ namespace Ryujinx.Graphics.Shader.CodeGen.Glsl } else { - if (value >= AttributeConsts.FragmentOutputColorBase && - value < AttributeConsts.FragmentOutputColorEnd) + if (value >= AttributeConsts.FragmentOutputColorBase && value < AttributeConsts.FragmentOutputColorEnd) { value -= AttributeConsts.FragmentOutputColorBase; diff --git a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs index 8ff37429a..c194b5c2e 100644 --- a/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs +++ b/Ryujinx.Graphics.Shader/Translation/AttributeConsts.cs @@ -35,6 +35,9 @@ namespace Ryujinx.Graphics.Shader.Translation public const int FragmentOutputColorBase = 0x1000010; public const int FragmentOutputColorEnd = FragmentOutputColorBase + 8 * 16; + public const int FragmentOutputIsBgraBase = 0x1000100; + public const int FragmentOutputIsBgraEnd = FragmentOutputIsBgraBase + 8 * 4; + public const int ThreadIdX = 0x2000000; public const int ThreadIdY = 0x2000004; public const int ThreadIdZ = 0x2000008; diff --git a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs index 39532a64f..9b26fa4a6 100644 --- a/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs +++ b/Ryujinx.Graphics.Shader/Translation/EmitterContext.cs @@ -115,22 +115,46 @@ namespace Ryujinx.Graphics.Shader.Translation int regIndex = 0; - for (int attachment = 0; attachment < 8; attachment++) + for (int rtIndex = 0; rtIndex < 8; rtIndex++) { - OmapTarget target = Config.OmapTargets[attachment]; + OmapTarget target = Config.OmapTargets[rtIndex]; for (int component = 0; component < 4; component++) { - if (target.ComponentEnabled(component)) + if (!target.ComponentEnabled(component)) { - Operand dest = Attribute(AttributeConsts.FragmentOutputColorBase + attachment * 16 + component * 4); - - Operand src = Register(regIndex, RegisterType.Gpr); - - this.Copy(dest, src); - - regIndex++; + continue; } + + int fragmentOutputColorAttr = AttributeConsts.FragmentOutputColorBase + rtIndex * 16; + + Operand src = Register(regIndex, RegisterType.Gpr); + + // Perform B <-> R swap if needed, for BGRA formats (not supported on OpenGL). + if (component == 0 || component == 2) + { + Operand isBgra = Attribute(AttributeConsts.FragmentOutputIsBgraBase + rtIndex * 4); + + Operand lblIsBgra = Label(); + Operand lblEnd = Label(); + + this.BranchIfTrue(lblIsBgra, isBgra); + + this.Copy(Attribute(fragmentOutputColorAttr + component * 4), src); + this.Branch(lblEnd); + + MarkLabel(lblIsBgra); + + this.Copy(Attribute(fragmentOutputColorAttr + (2 - component) * 4), src); + + MarkLabel(lblEnd); + } + else + { + this.Copy(Attribute(fragmentOutputColorAttr + component * 4), src); + } + + regIndex++; } } } diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs index e1eb55ade..dbb05c772 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs @@ -247,7 +247,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger } private void PostFrameBuffer(Layer layer, BufferItem item) - { + { int frameBufferWidth = item.GraphicBuffer.Object.Width; int frameBufferHeight = item.GraphicBuffer.Object.Height; From 51fbc1fde4363760bb47a2a5c960476ffceeac17 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Sun, 26 Jul 2020 18:11:28 -0300 Subject: [PATCH 10/12] Use polygon offset clamp if supported (#1429) --- Ryujinx.Graphics.Gpu/Engine/Methods.cs | 4 ++-- Ryujinx.Graphics.OpenGL/HwCapabilities.cs | 2 ++ Ryujinx.Graphics.OpenGL/Pipeline.cs | 11 ++++++++--- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Ryujinx.Graphics.Gpu/Engine/Methods.cs b/Ryujinx.Graphics.Gpu/Engine/Methods.cs index e84687eff..fbde0f0a3 100644 --- a/Ryujinx.Graphics.Gpu/Engine/Methods.cs +++ b/Ryujinx.Graphics.Gpu/Engine/Methods.cs @@ -568,7 +568,7 @@ namespace Ryujinx.Graphics.Gpu.Engine enables |= (depthBias.LineEnable ? PolygonModeMask.Line : 0); enables |= (depthBias.FillEnable ? PolygonModeMask.Fill : 0); - _context.Renderer.Pipeline.SetDepthBias(enables, factor, units, clamp); + _context.Renderer.Pipeline.SetDepthBias(enables, factor, units / 2f, clamp); } /// @@ -697,7 +697,7 @@ namespace Ryujinx.Graphics.Gpu.Engine float size = state.Get(MethodOffset.PointSize); bool isProgramPointSize = state.Get(MethodOffset.VertexProgramPointSize); bool enablePointSprite = state.Get(MethodOffset.PointSpriteEnable); - + // TODO: Need to figure out a way to map PointCoordReplace enable bit. Origin origin = (state.Get(MethodOffset.PointCoordReplace) & 4) == 0 ? Origin.LowerLeft : Origin.UpperLeft; diff --git a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs index 14515b618..9278c59ef 100644 --- a/Ryujinx.Graphics.OpenGL/HwCapabilities.cs +++ b/Ryujinx.Graphics.OpenGL/HwCapabilities.cs @@ -7,6 +7,7 @@ namespace Ryujinx.Graphics.OpenGL { private static readonly Lazy _supportsAstcCompression = new Lazy(() => HasExtension("GL_KHR_texture_compression_astc_ldr")); private static readonly Lazy _supportsImageLoadFormatted = new Lazy(() => HasExtension("GL_EXT_shader_image_load_formatted")); + private static readonly Lazy _supportsPolygonOffsetClamp = new Lazy(() => HasExtension("GL_EXT_polygon_offset_clamp")); private static readonly Lazy _supportsViewportSwizzle = new Lazy(() => HasExtension("GL_NV_viewport_swizzle")); private static readonly Lazy _maximumComputeSharedMemorySize = new Lazy(() => GetLimit(All.MaxComputeSharedMemorySize)); @@ -28,6 +29,7 @@ namespace Ryujinx.Graphics.OpenGL public static bool SupportsAstcCompression => _supportsAstcCompression.Value; public static bool SupportsImageLoadFormatted => _supportsImageLoadFormatted.Value; + public static bool SupportsPolygonOffsetClamp => _supportsPolygonOffsetClamp.Value; public static bool SupportsViewportSwizzle => _supportsViewportSwizzle.Value; public static bool SupportsNonConstantTextureOffset => _gpuVendor.Value == GpuVendor.Nvidia; diff --git a/Ryujinx.Graphics.OpenGL/Pipeline.cs b/Ryujinx.Graphics.OpenGL/Pipeline.cs index 09ba9be0b..4f3c2a29b 100644 --- a/Ryujinx.Graphics.OpenGL/Pipeline.cs +++ b/Ryujinx.Graphics.OpenGL/Pipeline.cs @@ -610,9 +610,14 @@ namespace Ryujinx.Graphics.OpenGL return; } - GL.PolygonOffset(factor, units / 2f); - // TODO: Enable when GL_EXT_polygon_offset_clamp is supported. - // GL.PolygonOffsetClamp(factor, units, clamp); + if (HwCapabilities.SupportsPolygonOffsetClamp) + { + GL.PolygonOffsetClamp(factor, units, clamp); + } + else + { + GL.PolygonOffset(factor, units); + } } public void SetDepthClamp(bool clamp) From d8515b603bff4712fe0e84113d7f7823a84c85eb Mon Sep 17 00:00:00 2001 From: Ac_K Date: Mon, 27 Jul 2020 00:25:04 +0200 Subject: [PATCH 11/12] audin: Implement IAudioInManager ListAudioIns (#1419) * audin: Implement IAudioInManager ListAudioIns This implement some calls of IAudioInManager: - ListAudioIns - ListAudioInsAuto - ListAudioInsAutoFiltered Accordingly to RE. Close #1056 * Fix count * Comment condition * Fix comment --- .../HOS/Services/Audio/IAudioInManager.cs | 80 ++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs b/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs index d8e1f468e..9b3b951cb 100644 --- a/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs +++ b/Ryujinx.HLE/HOS/Services/Audio/IAudioInManager.cs @@ -1,8 +1,86 @@ -namespace Ryujinx.HLE.HOS.Services.Audio +using Ryujinx.Cpu; +using System; +using System.Text; + +namespace Ryujinx.HLE.HOS.Services.Audio { [Service("audin:u")] class IAudioInManager : IpcService { + private const string DefaultAudioInsName = "BuiltInHeadset"; + public IAudioInManager(ServiceCtx context) { } + + [Command(0)] + // ListAudioIns() -> (u32 count, buffer names) + public ResultCode ListAudioIns(ServiceCtx context) + { + long bufferPosition = context.Request.ReceiveBuff[0].Position; + long bufferSize = context.Request.ReceiveBuff[0].Size; + + // NOTE: The service check if AudioInManager thread is started, if not it starts it. + + uint count = ListAudioInsImpl(context.Memory, bufferPosition, bufferSize, false); + + context.ResponseData.Write(count); + + return ResultCode.Success; + } + + [Command(3)] // 3.0.0+ + // ListAudioInsAuto() -> (u32 count, buffer names) + public ResultCode ListAudioInsAuto(ServiceCtx context) + { + (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x22(); + + // NOTE: The service check if AudioInManager thread is started, if not it starts it. + + uint count = ListAudioInsImpl(context.Memory, bufferPosition, bufferSize, false); + + context.ResponseData.Write(count); + + return ResultCode.Success; + } + + [Command(4)] // 3.0.0+ + // ListAudioInsAutoFiltered() -> (u32 count, buffer names) + public ResultCode ListAudioInsAutoFiltered(ServiceCtx context) + { + (long bufferPosition, long bufferSize) = context.Request.GetBufferType0x22(); + + // NOTE: The service check if AudioInManager thread is started, if not it starts it. + + uint count = ListAudioInsImpl(context.Memory, bufferPosition, bufferSize, true); + + context.ResponseData.Write(count); + + return ResultCode.Success; + } + + private uint ListAudioInsImpl(MemoryManager memory, long bufferPosition, long bufferSize, bool filtered = false) + { + uint count = 0; + + MemoryHelper.FillWithZeros(memory, bufferPosition, (int)bufferSize); + + if (bufferSize > 0) + { + // NOTE: The service also check that the input target is enabled when in filtering mode, as audctl and most of the audin logic isn't supported, we don't support it. + if (!filtered) + { + byte[] deviceNameBuffer = Encoding.ASCII.GetBytes(DefaultAudioInsName + "\0"); + + memory.Write((ulong)bufferPosition, deviceNameBuffer); + + count++; + } + + // NOTE: The service adds other input devices names available in the buffer, + // every name is aligned to 0x100 bytes. + // Since we don't support it for now, it's fine to do nothing here. + } + + return count; + } } } \ No newline at end of file From ca0d1f8205c3988d916033e2d950a421ddb995af Mon Sep 17 00:00:00 2001 From: Ac_K Date: Mon, 27 Jul 2020 01:04:08 +0200 Subject: [PATCH 12/12] ns/nim: Stub eShop related calls (#1420) * ns/nim: Stub eShop related calls As we aren't able to process purchase on the eShop throught the emulator, I have: - Stub IPurchaseEventManager::SetDefaultDeliveryTarget (with RE check). - Implement IPurchaseEventManager::GetPurchasedEventReadableHandle (with RE check). As we can't do any eShop async call throught the emulator, I have: - Stub IShopServiceAccessServerInterface::CreateServerInterface - Stub IShopServiceAccessServer::CreateAccessorInterface - Stub IShopServiceAccessor::IShopServiceAsync Close #1084 and #1322 * fix handle copy * Fix align * Fix readonly event --- Ryujinx.Common/Logging/LogClass.cs | 1 + .../Services/Nim/IShopServiceAccessServer.cs | 21 ++++++++ .../Nim/IShopServiceAccessServerInterface.cs | 16 ++++++- .../HOS/Services/Nim/IShopServiceAccessor.cs | 37 ++++++++++++++ .../HOS/Services/Nim/IShopServiceAsync.cs | 7 +++ .../HOS/Services/Ns/IAddOnContentManager.cs | 4 +- .../HOS/Services/Ns/IPurchaseEventManager.cs | 48 ++++++++++++++++++- 7 files changed, 129 insertions(+), 5 deletions(-) create mode 100644 Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServer.cs create mode 100644 Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs create mode 100644 Ryujinx.HLE/HOS/Services/Nim/IShopServiceAsync.cs diff --git a/Ryujinx.Common/Logging/LogClass.cs b/Ryujinx.Common/Logging/LogClass.cs index a35d01a54..1ea69b05b 100644 --- a/Ryujinx.Common/Logging/LogClass.cs +++ b/Ryujinx.Common/Logging/LogClass.cs @@ -37,6 +37,7 @@ namespace Ryujinx.Common.Logging ServiceMm, ServiceNfp, ServiceNifm, + ServiceNim, ServiceNs, ServiceNsd, ServiceNv, diff --git a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServer.cs b/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServer.cs new file mode 100644 index 000000000..ad41328e1 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServer.cs @@ -0,0 +1,21 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface.ShopServiceAccessServer; + +namespace Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface +{ + class IShopServiceAccessServer : IpcService + { + public IShopServiceAccessServer() { } + + [Command(0)] + // CreateAccessorInterface(u8) -> object + public ResultCode CreateAccessorInterface(ServiceCtx context) + { + MakeObject(context, new IShopServiceAccessor(context.Device.System)); + + Logger.PrintStub(LogClass.ServiceNim); + + return ResultCode.Success; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs b/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs index 9be84393f..b3438dd76 100644 --- a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs +++ b/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessServerInterface.cs @@ -1,8 +1,22 @@ -namespace Ryujinx.HLE.HOS.Services.Nim +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface; + +namespace Ryujinx.HLE.HOS.Services.Nim { [Service("nim:eca")] // 5.0.0+ class IShopServiceAccessServerInterface : IpcService { public IShopServiceAccessServerInterface(ServiceCtx context) { } + + [Command(0)] + // CreateServerInterface(pid, handle, u64) -> object + public ResultCode CreateServerInterface(ServiceCtx context) + { + MakeObject(context, new IShopServiceAccessServer()); + + Logger.PrintStub(LogClass.ServiceNim); + + return ResultCode.Success; + } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs b/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs new file mode 100644 index 000000000..6a9bdb069 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAccessor.cs @@ -0,0 +1,37 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface.ShopServiceAccessServer.ShopServiceAccessor; +using System; + +namespace Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface.ShopServiceAccessServer +{ + class IShopServiceAccessor : IpcService + { + private readonly KEvent _event; + + public IShopServiceAccessor(Horizon system) + { + _event = new KEvent(system.KernelContext); + } + + [Command(0)] + // CreateAsyncInterface(u64) -> (handle, object) + public ResultCode CreateAsyncInterface(ServiceCtx context) + { + MakeObject(context, new IShopServiceAsync()); + + if (context.Process.HandleTable.GenerateHandle(_event.ReadableEvent, out int handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + + Logger.PrintStub(LogClass.ServiceNim); + + return ResultCode.Success; + } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAsync.cs b/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAsync.cs new file mode 100644 index 000000000..81d892c5d --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Nim/IShopServiceAsync.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.HLE.HOS.Services.Nim.ShopServiceAccessServerInterface.ShopServiceAccessServer.ShopServiceAccessor +{ + class IShopServiceAsync : IpcService + { + public IShopServiceAsync() { } + } +} \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Ns/IAddOnContentManager.cs b/Ryujinx.HLE/HOS/Services/Ns/IAddOnContentManager.cs index 20d95cbbb..e6e42c41b 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/IAddOnContentManager.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/IAddOnContentManager.cs @@ -165,7 +165,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns // CreateEcPurchasedEventManager() -> object public ResultCode CreateEcPurchasedEventManager(ServiceCtx context) { - MakeObject(context, new IPurchaseEventManager()); + MakeObject(context, new IPurchaseEventManager(context.Device.System)); Logger.PrintStub(LogClass.ServiceNs); @@ -178,7 +178,7 @@ namespace Ryujinx.HLE.HOS.Services.Ns { // Very similar to CreateEcPurchasedEventManager but with some extra code - MakeObject(context, new IPurchaseEventManager()); + MakeObject(context, new IPurchaseEventManager(context.Device.System)); Logger.PrintStub(LogClass.ServiceNs); diff --git a/Ryujinx.HLE/HOS/Services/Ns/IPurchaseEventManager.cs b/Ryujinx.HLE/HOS/Services/Ns/IPurchaseEventManager.cs index 6a512b7b7..8bb05b04a 100644 --- a/Ryujinx.HLE/HOS/Services/Ns/IPurchaseEventManager.cs +++ b/Ryujinx.HLE/HOS/Services/Ns/IPurchaseEventManager.cs @@ -1,8 +1,52 @@ +using Ryujinx.Common; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Kernel.Threading; +using System; + namespace Ryujinx.HLE.HOS.Services.Ns { class IPurchaseEventManager : IpcService { - // TODO: Implement this - // Size seems to be atleast 0x7a8 + private readonly KEvent _purchasedEvent; + + public IPurchaseEventManager(Horizon system) + { + _purchasedEvent = new KEvent(system.KernelContext); + } + + [Command(0)] + // SetDefaultDeliveryTarget(pid, buffer unknown) + public ResultCode SetDefaultDeliveryTarget(ServiceCtx context) + { + long inBufferPosition = context.Request.SendBuff[0].Position; + long inBufferSize = context.Request.SendBuff[0].Size; + byte[] buffer = new byte[inBufferSize]; + + context.Memory.Read((ulong)inBufferPosition, buffer); + + // NOTE: Service use the pid to call arp:r GetApplicationLaunchProperty and store it in internal field. + // Then it seems to use the buffer content and compare it with a stored linked instrusive list. + // Since we don't support purchase from eShop, we can stub it. + + Logger.PrintStub(LogClass.ServiceNs); + + return ResultCode.Success; + } + + [Command(2)] + // GetPurchasedEventReadableHandle() -> handle + public ResultCode GetPurchasedEventReadableHandle(ServiceCtx context) + { + if (context.Process.HandleTable.GenerateHandle(_purchasedEvent.ReadableEvent, out int handle) != KernelResult.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(handle); + + return ResultCode.Success; + } } } \ No newline at end of file