diff options
| author | TSR Berry <20988865+TSRBerry@users.noreply.github.com> | 2023-04-08 01:22:00 +0200 |
|---|---|---|
| committer | Mary <thog@protonmail.com> | 2023-04-27 23:51:14 +0200 |
| commit | cee712105850ac3385cd0091a923438167433f9f (patch) | |
| tree | 4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/Ryujinx.HLE/HOS/Services/Vi | |
| parent | cd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff) | |
Move solution and projects to src
Diffstat (limited to 'src/Ryujinx.HLE/HOS/Services/Vi')
12 files changed, 792 insertions, 0 deletions
diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs new file mode 100644 index 00000000..526cecf8 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs @@ -0,0 +1,27 @@ +using Ryujinx.HLE.HOS.Services.Vi.RootService; +using Ryujinx.HLE.HOS.Services.Vi.Types; + +namespace Ryujinx.HLE.HOS.Services.Vi +{ + [Service("vi:u")] + class IApplicationRootService : IpcService + { + public IApplicationRootService(ServiceCtx context) : base(context.Device.System.ViServer) { } + + [CommandCmif(0)] + // GetDisplayService(u32) -> object<nn::visrv::sf::IApplicationDisplayService> + public ResultCode GetDisplayService(ServiceCtx context) + { + ViServiceType serviceType = (ViServiceType)context.RequestData.ReadInt32(); + + if (serviceType != ViServiceType.Application) + { + return ResultCode.PermissionDenied; + } + + MakeObject(context, new IApplicationDisplayService(serviceType)); + + return ResultCode.Success; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs new file mode 100644 index 00000000..d564dabe --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs @@ -0,0 +1,28 @@ +using Ryujinx.HLE.HOS.Services.Vi.RootService; +using Ryujinx.HLE.HOS.Services.Vi.Types; + +namespace Ryujinx.HLE.HOS.Services.Vi +{ + [Service("vi:m")] + class IManagerRootService : IpcService + { + // vi:u/m/s aren't on 3 separate threads but we can't put them together with the current ServerBase + public IManagerRootService(ServiceCtx context) : base(context.Device.System.ViServerM) { } + + [CommandCmif(2)] + // GetDisplayService(u32) -> object<nn::visrv::sf::IApplicationDisplayService> + public ResultCode GetDisplayService(ServiceCtx context) + { + ViServiceType serviceType = (ViServiceType)context.RequestData.ReadInt32(); + + if (serviceType != ViServiceType.Manager) + { + return ResultCode.PermissionDenied; + } + + MakeObject(context, new IApplicationDisplayService(serviceType)); + + return ResultCode.Success; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs new file mode 100644 index 00000000..0dfd84f5 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs @@ -0,0 +1,28 @@ +using Ryujinx.HLE.HOS.Services.Vi.RootService; +using Ryujinx.HLE.HOS.Services.Vi.Types; + +namespace Ryujinx.HLE.HOS.Services.Vi +{ + [Service("vi:s")] + class ISystemRootService : IpcService + { + // vi:u/m/s aren't on 3 separate threads but we can't put them together with the current ServerBase + public ISystemRootService(ServiceCtx context) : base(context.Device.System.ViServerS) { } + + [CommandCmif(1)] + // GetDisplayService(u32) -> object<nn::visrv::sf::IApplicationDisplayService> + public ResultCode GetDisplayService(ServiceCtx context) + { + ViServiceType serviceType = (ViServiceType)context.RequestData.ReadInt32(); + + if (serviceType != ViServiceType.System) + { + return ResultCode.PermissionDenied; + } + + MakeObject(context, new IApplicationDisplayService(serviceType)); + + return ResultCode.Success; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/ResultCode.cs b/src/Ryujinx.HLE/HOS/Services/Vi/ResultCode.cs new file mode 100644 index 00000000..c64339c9 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Vi/ResultCode.cs @@ -0,0 +1,17 @@ +namespace Ryujinx.HLE.HOS.Services.Vi +{ + enum ResultCode + { + ModuleId = 114, + ErrorCodeShift = 9, + + Success = 0, + + InvalidArguments = (1 << ErrorCodeShift) | ModuleId, + InvalidLayerSize = (4 << ErrorCodeShift) | ModuleId, + PermissionDenied = (5 << ErrorCodeShift) | ModuleId, + InvalidScalingMode = (6 << ErrorCodeShift) | ModuleId, + InvalidValue = (7 << ErrorCodeShift) | ModuleId, + AlreadyOpened = (9 << ErrorCodeShift) | ModuleId + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/AndroidSurfaceComposerClient.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/AndroidSurfaceComposerClient.cs new file mode 100644 index 00000000..1fa99e65 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/AndroidSurfaceComposerClient.cs @@ -0,0 +1,19 @@ +namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService +{ + static class AndroidSurfaceComposerClient + { + // NOTE: This is android::SurfaceComposerClient::getDisplayInfo. + public static (ulong, ulong) GetDisplayInfo(ServiceCtx context, ulong displayId = 0) + { + // TODO: This need to be REd, it should returns the driver resolution and more. + if (context.Device.System.State.DockedMode) + { + return (1920, 1080); + } + else + { + return (1280, 720); + } + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs new file mode 100644 index 00000000..6093381c --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs @@ -0,0 +1,80 @@ +using Ryujinx.Common.Logging; + +namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService +{ + class IManagerDisplayService : IpcService + { + private IApplicationDisplayService _applicationDisplayService; + + public IManagerDisplayService(IApplicationDisplayService applicationDisplayService) + { + _applicationDisplayService = applicationDisplayService; + } + + [CommandCmif(1102)] + // GetDisplayResolution(u64 display_id) -> (u64 width, u64 height) + public ResultCode GetDisplayResolution(ServiceCtx context) + { + ulong displayId = context.RequestData.ReadUInt64(); + + (ulong width, ulong height) = AndroidSurfaceComposerClient.GetDisplayInfo(context, displayId); + + context.ResponseData.Write(width); + context.ResponseData.Write(height); + + return ResultCode.Success; + } + + [CommandCmif(2010)] + // CreateManagedLayer(u32, u64, nn::applet::AppletResourceUserId) -> u64 + public ResultCode CreateManagedLayer(ServiceCtx context) + { + long layerFlags = context.RequestData.ReadInt64(); + long displayId = context.RequestData.ReadInt64(); + long appletResourceUserId = context.RequestData.ReadInt64(); + + ulong pid = context.Device.System.AppletState.AppletResourceUserIds.GetData<ulong>((int)appletResourceUserId); + + context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, pid); + context.Device.System.SurfaceFlinger.SetRenderLayer(layerId); + + context.ResponseData.Write(layerId); + + return ResultCode.Success; + } + + [CommandCmif(2011)] + // DestroyManagedLayer(u64) + public ResultCode DestroyManagedLayer(ServiceCtx context) + { + long layerId = context.RequestData.ReadInt64(); + + return context.Device.System.SurfaceFlinger.DestroyManagedLayer(layerId); + } + + [CommandCmif(2012)] // 7.0.0+ + // CreateStrayLayer(u32, u64) -> (u64, u64, buffer<bytes, 6>) + public ResultCode CreateStrayLayer(ServiceCtx context) + { + return _applicationDisplayService.CreateStrayLayer(context); + } + + [CommandCmif(6000)] + // AddToLayerStack(u32, u64) + public ResultCode AddToLayerStack(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceVi); + + return ResultCode.Success; + } + + [CommandCmif(6002)] + // SetLayerVisibility(b8, u64) + public ResultCode SetLayerVisibility(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceVi); + + return ResultCode.Success; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs new file mode 100644 index 00000000..a24aa079 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/ISystemDisplayService.cs @@ -0,0 +1,59 @@ +using Ryujinx.Common.Logging; + +namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService +{ + class ISystemDisplayService : IpcService + { + private IApplicationDisplayService _applicationDisplayService; + + public ISystemDisplayService(IApplicationDisplayService applicationDisplayService) + { + _applicationDisplayService = applicationDisplayService; + } + + [CommandCmif(2205)] + // SetLayerZ(u64, u64) + public ResultCode SetLayerZ(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceVi); + + return ResultCode.Success; + } + + [CommandCmif(2207)] + // SetLayerVisibility(b8, u64) + public ResultCode SetLayerVisibility(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceVi); + + return ResultCode.Success; + } + + [CommandCmif(2312)] // 1.0.0-6.2.0 + // CreateStrayLayer(u32, u64) -> (u64, u64, buffer<bytes, 6>) + public ResultCode CreateStrayLayer(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceVi); + + return _applicationDisplayService.CreateStrayLayer(context); + } + + [CommandCmif(3200)] + // GetDisplayMode(u64) -> nn::vi::DisplayModeInfo + public ResultCode GetDisplayMode(ServiceCtx context) + { + ulong displayId = context.RequestData.ReadUInt64(); + + (ulong width, ulong height) = AndroidSurfaceComposerClient.GetDisplayInfo(context, displayId); + + context.ResponseData.Write((uint)width); + context.ResponseData.Write((uint)height); + context.ResponseData.Write(60.0f); + context.ResponseData.Write(0); + + Logger.Stub?.PrintStub(LogClass.ServiceVi); + + return ResultCode.Success; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DestinationScalingMode.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DestinationScalingMode.cs new file mode 100644 index 00000000..cf459cb2 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DestinationScalingMode.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService +{ + enum DestinationScalingMode + { + Freeze, + ScaleToWindow, + ScaleAndCrop, + None, + PreserveAspectRatio + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DisplayInfo.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DisplayInfo.cs new file mode 100644 index 00000000..d46206d4 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/DisplayInfo.cs @@ -0,0 +1,16 @@ +using Ryujinx.Common.Memory; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService.Types +{ + [StructLayout(LayoutKind.Sequential, Size = 0x60)] + struct DisplayInfo + { + public Array64<byte> Name; + public bool LayerLimitEnabled; + public Array7<byte> Padding; + public ulong LayerLimitMax; + public ulong Width; + public ulong Height; + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/SourceScalingMode.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/SourceScalingMode.cs new file mode 100644 index 00000000..ac8c3e02 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/Types/SourceScalingMode.cs @@ -0,0 +1,11 @@ +namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService +{ + enum SourceScalingMode + { + None, + Freeze, + ScaleToWindow, + ScaleAndCrop, + PreserveAspectRatio + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs new file mode 100644 index 00000000..52ed5222 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs @@ -0,0 +1,487 @@ +using Ryujinx.Common; +using Ryujinx.Common.Logging; +using Ryujinx.Common.Memory; +using Ryujinx.HLE.HOS.Applets; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Common; +using Ryujinx.HLE.HOS.Services.SurfaceFlinger; +using Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService; +using Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService.Types; +using Ryujinx.HLE.HOS.Services.Vi.Types; +using Ryujinx.HLE.Ui; +using Ryujinx.Horizon.Common; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Text; + +namespace Ryujinx.HLE.HOS.Services.Vi.RootService +{ + class IApplicationDisplayService : IpcService + { + private readonly ViServiceType _serviceType; + + private class DisplayState + { + public int RetrievedEventsCount; + } + + private readonly List<DisplayInfo> _displayInfo; + private readonly Dictionary<ulong, DisplayState> _openDisplays; + + private int _vsyncEventHandle; + + public IApplicationDisplayService(ViServiceType serviceType) + { + _serviceType = serviceType; + _displayInfo = new List<DisplayInfo>(); + _openDisplays = new Dictionary<ulong, DisplayState>(); + + void AddDisplayInfo(string name, bool layerLimitEnabled, ulong layerLimitMax, ulong width, ulong height) + { + DisplayInfo displayInfo = new DisplayInfo() + { + Name = new Array64<byte>(), + LayerLimitEnabled = layerLimitEnabled, + Padding = new Array7<byte>(), + LayerLimitMax = layerLimitMax, + Width = width, + Height = height + }; + + Encoding.ASCII.GetBytes(name).AsSpan().CopyTo(displayInfo.Name.AsSpan()); + + _displayInfo.Add(displayInfo); + } + + AddDisplayInfo("Default", true, 1, 1920, 1080); + AddDisplayInfo("External", true, 1, 1920, 1080); + AddDisplayInfo("Edid", true, 1, 0, 0); + AddDisplayInfo("Internal", true, 1, 1920, 1080); + AddDisplayInfo("Null", false, 0, 1920, 1080); + } + + [CommandCmif(100)] + // GetRelayService() -> object<nns::hosbinder::IHOSBinderDriver> + public ResultCode GetRelayService(ServiceCtx context) + { + // FIXME: Should be _serviceType != ViServiceType.Application but guests crashes if we do this check. + if (_serviceType > ViServiceType.System) + { + return ResultCode.PermissionDenied; + } + + MakeObject(context, new HOSBinderDriverServer()); + + return ResultCode.Success; + } + + [CommandCmif(101)] + // GetSystemDisplayService() -> object<nn::visrv::sf::ISystemDisplayService> + public ResultCode GetSystemDisplayService(ServiceCtx context) + { + // FIXME: Should be _serviceType == ViServiceType.System but guests crashes if we do this check. + if (_serviceType > ViServiceType.System) + { + return ResultCode.PermissionDenied; + } + + MakeObject(context, new ISystemDisplayService(this)); + + return ResultCode.Success; + } + + [CommandCmif(102)] + // GetManagerDisplayService() -> object<nn::visrv::sf::IManagerDisplayService> + public ResultCode GetManagerDisplayService(ServiceCtx context) + { + if (_serviceType > ViServiceType.System) + { + return ResultCode.PermissionDenied; + } + + MakeObject(context, new IManagerDisplayService(this)); + + return ResultCode.Success; + } + + [CommandCmif(103)] // 2.0.0+ + // GetIndirectDisplayTransactionService() -> object<nns::hosbinder::IHOSBinderDriver> + public ResultCode GetIndirectDisplayTransactionService(ServiceCtx context) + { + if (_serviceType > ViServiceType.System) + { + return ResultCode.PermissionDenied; + } + + MakeObject(context, new HOSBinderDriverServer()); + + return ResultCode.Success; + } + + [CommandCmif(1000)] + // ListDisplays() -> (u64 count, buffer<nn::vi::DisplayInfo, 6>) + public ResultCode ListDisplays(ServiceCtx context) + { + ulong displayInfoBuffer = context.Request.ReceiveBuff[0].Position; + + // TODO: Determine when more than one display is needed. + ulong displayCount = 1; + + for (int i = 0; i < (int)displayCount; i++) + { + context.Memory.Write(displayInfoBuffer + (ulong)(i * Unsafe.SizeOf<DisplayInfo>()), _displayInfo[i]); + } + + context.ResponseData.Write(displayCount); + + return ResultCode.Success; + } + + [CommandCmif(1010)] + // OpenDisplay(nn::vi::DisplayName) -> u64 display_id + public ResultCode OpenDisplay(ServiceCtx context) + { + string name = ""; + + for (int index = 0; index < 8 && context.RequestData.BaseStream.Position < context.RequestData.BaseStream.Length; index++) + { + byte chr = context.RequestData.ReadByte(); + + if (chr >= 0x20 && chr < 0x7f) + { + name += (char)chr; + } + } + + return OpenDisplayImpl(context, name); + } + + [CommandCmif(1011)] + // OpenDefaultDisplay() -> u64 display_id + public ResultCode OpenDefaultDisplay(ServiceCtx context) + { + return OpenDisplayImpl(context, "Default"); + } + + private ResultCode OpenDisplayImpl(ServiceCtx context, string name) + { + if (name == "") + { + return ResultCode.InvalidValue; + } + + int displayId = _displayInfo.FindIndex(display => Encoding.ASCII.GetString(display.Name.AsSpan()).Trim('\0') == name); + + if (displayId == -1) + { + return ResultCode.InvalidValue; + } + + if (!_openDisplays.TryAdd((ulong)displayId, new DisplayState())) + { + return ResultCode.AlreadyOpened; + } + + context.ResponseData.Write((ulong)displayId); + + return ResultCode.Success; + } + + [CommandCmif(1020)] + // CloseDisplay(u64 display_id) + public ResultCode CloseDisplay(ServiceCtx context) + { + ulong displayId = context.RequestData.ReadUInt64(); + + if (!_openDisplays.Remove(displayId)) + { + return ResultCode.InvalidValue; + } + + return ResultCode.Success; + } + + [CommandCmif(1101)] + // SetDisplayEnabled(u32 enabled_bool, u64 display_id) + public ResultCode SetDisplayEnabled(ServiceCtx context) + { + // NOTE: Stubbed in original service. + return ResultCode.Success; + } + + [CommandCmif(1102)] + // GetDisplayResolution(u64 display_id) -> (u64 width, u64 height) + public ResultCode GetDisplayResolution(ServiceCtx context) + { + // NOTE: Not used in original service. + // ulong displayId = context.RequestData.ReadUInt64(); + + // NOTE: Returns ResultCode.InvalidArguments if width and height pointer are null, doesn't occur in our case. + + // NOTE: Values are hardcoded in original service. + context.ResponseData.Write(1280UL); // Width + context.ResponseData.Write(720UL); // Height + + return ResultCode.Success; + } + + [CommandCmif(2020)] + // OpenLayer(nn::vi::DisplayName, u64, nn::applet::AppletResourceUserId, pid) -> (u64, buffer<bytes, 6>) + public ResultCode OpenLayer(ServiceCtx context) + { + // TODO: support multi display. + byte[] displayName = context.RequestData.ReadBytes(0x40); + + long layerId = context.RequestData.ReadInt64(); + long userId = context.RequestData.ReadInt64(); + ulong parcelPtr = context.Request.ReceiveBuff[0].Position; + + ResultCode result = context.Device.System.SurfaceFlinger.OpenLayer(context.Request.HandleDesc.PId, layerId, out IBinder producer); + + if (result != ResultCode.Success) + { + return result; + } + + context.Device.System.SurfaceFlinger.SetRenderLayer(layerId); + + Parcel parcel = new Parcel(0x28, 0x4); + + parcel.WriteObject(producer, "dispdrv\0"); + + ReadOnlySpan<byte> parcelData = parcel.Finish(); + + context.Memory.Write(parcelPtr, parcelData); + + context.ResponseData.Write((long)parcelData.Length); + + return ResultCode.Success; + } + + [CommandCmif(2021)] + // CloseLayer(u64) + public ResultCode CloseLayer(ServiceCtx context) + { + long layerId = context.RequestData.ReadInt64(); + + return context.Device.System.SurfaceFlinger.CloseLayer(layerId); + } + + [CommandCmif(2030)] + // CreateStrayLayer(u32, u64) -> (u64, u64, buffer<bytes, 6>) + public ResultCode CreateStrayLayer(ServiceCtx context) + { + long layerFlags = context.RequestData.ReadInt64(); + long displayId = context.RequestData.ReadInt64(); + + ulong parcelPtr = context.Request.ReceiveBuff[0].Position; + + // TODO: support multi display. + IBinder producer = context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, 0, LayerState.Stray); + + context.Device.System.SurfaceFlinger.SetRenderLayer(layerId); + + Parcel parcel = new Parcel(0x28, 0x4); + + parcel.WriteObject(producer, "dispdrv\0"); + + ReadOnlySpan<byte> parcelData = parcel.Finish(); + + context.Memory.Write(parcelPtr, parcelData); + + context.ResponseData.Write(layerId); + context.ResponseData.Write((long)parcelData.Length); + + return ResultCode.Success; + } + + [CommandCmif(2031)] + // DestroyStrayLayer(u64) + public ResultCode DestroyStrayLayer(ServiceCtx context) + { + long layerId = context.RequestData.ReadInt64(); + + return context.Device.System.SurfaceFlinger.DestroyStrayLayer(layerId); + } + + [CommandCmif(2101)] + // SetLayerScalingMode(u32, u64) + public ResultCode SetLayerScalingMode(ServiceCtx context) + { + /* + uint sourceScalingMode = context.RequestData.ReadUInt32(); + ulong layerId = context.RequestData.ReadUInt64(); + */ + // NOTE: Original service converts SourceScalingMode to DestinationScalingMode but does nothing with the converted value. + + return ResultCode.Success; + } + + [CommandCmif(2102)] // 5.0.0+ + // ConvertScalingMode(u32 source_scaling_mode) -> u64 destination_scaling_mode + public ResultCode ConvertScalingMode(ServiceCtx context) + { + SourceScalingMode scalingMode = (SourceScalingMode)context.RequestData.ReadInt32(); + + DestinationScalingMode? convertedScalingMode = scalingMode switch + { + SourceScalingMode.None => DestinationScalingMode.None, + SourceScalingMode.Freeze => DestinationScalingMode.Freeze, + SourceScalingMode.ScaleAndCrop => DestinationScalingMode.ScaleAndCrop, + SourceScalingMode.ScaleToWindow => DestinationScalingMode.ScaleToWindow, + SourceScalingMode.PreserveAspectRatio => DestinationScalingMode.PreserveAspectRatio, + _ => null, + }; + + if (!convertedScalingMode.HasValue) + { + // Scaling mode out of the range of valid values. + return ResultCode.InvalidArguments; + } + + if (scalingMode != SourceScalingMode.ScaleToWindow && scalingMode != SourceScalingMode.PreserveAspectRatio) + { + // Invalid scaling mode specified. + return ResultCode.InvalidScalingMode; + } + + context.ResponseData.Write((ulong)convertedScalingMode); + + return ResultCode.Success; + } + + private ulong GetA8B8G8R8LayerSize(int width, int height, out int pitch, out int alignment) + { + const int defaultAlignment = 0x1000; + const ulong defaultSize = 0x20000; + + alignment = defaultAlignment; + pitch = BitUtils.AlignUp(BitUtils.DivRoundUp(width * 32, 8), 64); + + int memorySize = pitch * BitUtils.AlignUp(height, 64); + ulong requiredMemorySize = (ulong)BitUtils.AlignUp(memorySize, alignment); + + return (requiredMemorySize + defaultSize - 1) / defaultSize * defaultSize; + } + + [CommandCmif(2450)] + // GetIndirectLayerImageMap(s64 width, s64 height, u64 handle, nn::applet::AppletResourceUserId, pid) -> (s64, s64, buffer<bytes, 0x46>) + public ResultCode GetIndirectLayerImageMap(ServiceCtx context) + { + // The size of the layer buffer should be an aligned multiple of width * height + // because it was created using GetIndirectLayerImageRequiredMemoryInfo as a guide. + + long layerWidth = context.RequestData.ReadInt64(); + long layerHeight = context.RequestData.ReadInt64(); + long layerHandle = context.RequestData.ReadInt64(); + ulong layerBuffPosition = context.Request.ReceiveBuff[0].Position; + ulong layerBuffSize = context.Request.ReceiveBuff[0].Size; + + // Get the pitch of the layer that is necessary to render correctly. + ulong size = GetA8B8G8R8LayerSize((int)layerWidth, (int)layerHeight, out int pitch, out _); + + Debug.Assert(layerBuffSize == size); + + RenderingSurfaceInfo surfaceInfo = new RenderingSurfaceInfo(ColorFormat.A8B8G8R8, (uint)layerWidth, (uint)layerHeight, (uint)pitch, (uint)layerBuffSize); + + // Get the applet associated with the handle. + object appletObject = context.Device.System.AppletState.IndirectLayerHandles.GetData((int)layerHandle); + + if (appletObject == null) + { + Logger.Error?.Print(LogClass.ServiceVi, $"Indirect layer handle {layerHandle} does not match any applet"); + + return ResultCode.Success; + } + + Debug.Assert(appletObject is IApplet); + + IApplet applet = appletObject as IApplet; + + if (!applet.DrawTo(surfaceInfo, context.Memory, layerBuffPosition)) + { + Logger.Warning?.Print(LogClass.ServiceVi, $"Applet did not draw on indirect layer handle {layerHandle}"); + + return ResultCode.Success; + } + + context.ResponseData.Write(layerWidth); + context.ResponseData.Write(layerHeight); + + return ResultCode.Success; + } + + [CommandCmif(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; + } + */ + + // 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) + ulong size = GetA8B8G8R8LayerSize(width, height, out int pitch, out int alignment); + + context.ResponseData.Write(size); + context.ResponseData.Write(alignment); + } + + return ResultCode.Success; + } + + [CommandCmif(5202)] + // GetDisplayVsyncEvent(u64) -> handle<copy> + public ResultCode GetDisplayVSyncEvent(ServiceCtx context) + { + ulong displayId = context.RequestData.ReadUInt64(); + + if (!_openDisplays.TryGetValue(displayId, out DisplayState displayState)) + { + return ResultCode.InvalidValue; + } + + if (displayState.RetrievedEventsCount > 0) + { + return ResultCode.PermissionDenied; + } + + if (_vsyncEventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(context.Device.System.VsyncEvent.ReadableEvent, out _vsyncEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + displayState.RetrievedEventsCount++; + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_vsyncEventHandle); + + return ResultCode.Success; + } + } +}
\ No newline at end of file diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/Types/ViServiceType.cs b/src/Ryujinx.HLE/HOS/Services/Vi/Types/ViServiceType.cs new file mode 100644 index 00000000..ba6f8e5f --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Vi/Types/ViServiceType.cs @@ -0,0 +1,9 @@ +namespace Ryujinx.HLE.HOS.Services.Vi.Types +{ + enum ViServiceType + { + Application, + Manager, + System + } +}
\ No newline at end of file |
