diff options
| author | gdkchan <gab.dark.100@gmail.com> | 2018-08-16 20:47:36 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2018-08-16 20:47:36 -0300 |
| commit | 521751795a1c97c0d97f6f8904a3be69b13d3a9d (patch) | |
| tree | 942a05899c40e2de6d92a38b93a494bd96ee64b8 /Ryujinx.HLE/HOS/Services/Vi | |
| parent | 182d716867ae477c2b15a5332430dc2641fa1cc3 (diff) | |
Code style fixes and nits on the HLE project (#355)
* Some style fixes and nits on ITimeZoneService
* Remove some unneeded usings
* Remove the Ryujinx.HLE.OsHle.Handles namespace
* Remove hbmenu automatic load on process exit
* Rename Ns to Device, rename Os to System, rename SystemState to State
* Move Exceptions and Utilities out of OsHle
* Rename OsHle to HOS
* Rename OsHle folder to HOS
* IManagerDisplayService and ISystemDisplayService style fixes
* BsdError shouldn't be public
* Add a empty new line before using static
* Remove unused file
* Some style fixes on NPDM
* Exit gracefully when the application is closed
* Code style fixes on IGeneralService
* Add 0x prefix on values printed as hex
* Small improvements on finalization code
* Move ProcessId and ThreadId out of AThreadState
* Rename VFs to FileSystem
* FsAccessHeader shouldn't be public. Also fix file names casing
* More case changes on NPDM
* Remove unused files
* Move using to the correct place on NPDM
* Use properties on KernelAccessControlMmio
* Address PR feedback
Diffstat (limited to 'Ryujinx.HLE/HOS/Services/Vi')
| -rw-r--r-- | Ryujinx.HLE/HOS/Services/Vi/Display.cs | 12 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Services/Vi/GbpBuffer.cs | 60 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Services/Vi/IApplicationDisplayService.cs | 233 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs | 29 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Services/Vi/IHOSBinderDriver.cs | 100 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Services/Vi/IManagerDisplayService.cs | 54 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs | 29 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Services/Vi/ISystemDisplayService.cs | 48 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs | 29 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs | 416 | ||||
| -rw-r--r-- | Ryujinx.HLE/HOS/Services/Vi/Parcel.cs | 58 |
11 files changed, 1068 insertions, 0 deletions
diff --git a/Ryujinx.HLE/HOS/Services/Vi/Display.cs b/Ryujinx.HLE/HOS/Services/Vi/Display.cs new file mode 100644 index 00000000..50fe5e1d --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Vi/Display.cs @@ -0,0 +1,12 @@ +namespace Ryujinx.HLE.HOS.Services.Vi +{ + class Display + { + public string Name { get; private set; } + + public Display(string Name) + { + this.Name = Name; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Vi/GbpBuffer.cs b/Ryujinx.HLE/HOS/Services/Vi/GbpBuffer.cs new file mode 100644 index 00000000..4f00c3de --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Vi/GbpBuffer.cs @@ -0,0 +1,60 @@ +using System.IO; + +namespace Ryujinx.HLE.HOS.Services.Android +{ + struct GbpBuffer + { + public int Magic { get; private set; } + public int Width { get; private set; } + public int Height { get; private set; } + public int Stride { get; private set; } + public int Format { get; private set; } + public int Usage { get; private set; } + + public int Pid { get; private set; } + public int RefCount { get; private set; } + + public int FdsCount { get; private set; } + public int IntsCount { get; private set; } + + public byte[] RawData { get; private set; } + + public int Size => RawData.Length + 10 * 4; + + public GbpBuffer(BinaryReader Reader) + { + Magic = Reader.ReadInt32(); + Width = Reader.ReadInt32(); + Height = Reader.ReadInt32(); + Stride = Reader.ReadInt32(); + Format = Reader.ReadInt32(); + Usage = Reader.ReadInt32(); + + Pid = Reader.ReadInt32(); + RefCount = Reader.ReadInt32(); + + FdsCount = Reader.ReadInt32(); + IntsCount = Reader.ReadInt32(); + + RawData = Reader.ReadBytes((FdsCount + IntsCount) * 4); + } + + public void Write(BinaryWriter Writer) + { + Writer.Write(Magic); + Writer.Write(Width); + Writer.Write(Height); + Writer.Write(Stride); + Writer.Write(Format); + Writer.Write(Usage); + + Writer.Write(Pid); + Writer.Write(RefCount); + + Writer.Write(FdsCount); + Writer.Write(IntsCount); + + Writer.Write(RawData); + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Vi/IApplicationDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/IApplicationDisplayService.cs new file mode 100644 index 00000000..3006b73a --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Vi/IApplicationDisplayService.cs @@ -0,0 +1,233 @@ +using ChocolArm64.Memory; +using Ryujinx.HLE.HOS.Ipc; +using System.Collections.Generic; +using System.IO; +using System.Text; + +using static Ryujinx.HLE.HOS.Services.Android.Parcel; + +namespace Ryujinx.HLE.HOS.Services.Vi +{ + class IApplicationDisplayService : IpcService + { + private Dictionary<int, ServiceProcessRequest> m_Commands; + + public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; + + private IdDictionary Displays; + + public IApplicationDisplayService() + { + m_Commands = new Dictionary<int, ServiceProcessRequest>() + { + { 100, GetRelayService }, + { 101, GetSystemDisplayService }, + { 102, GetManagerDisplayService }, + { 103, GetIndirectDisplayTransactionService }, + { 1000, ListDisplays }, + { 1010, OpenDisplay }, + { 1020, CloseDisplay }, + { 1102, GetDisplayResolution }, + { 2020, OpenLayer }, + { 2021, CloseLayer }, + { 2030, CreateStrayLayer }, + { 2031, DestroyStrayLayer }, + { 2101, SetLayerScalingMode }, + { 5202, GetDisplayVSyncEvent } + }; + + Displays = new IdDictionary(); + } + + public long GetRelayService(ServiceCtx Context) + { + MakeObject(Context, new IHOSBinderDriver(Context.Device.Gpu.Renderer)); + + return 0; + } + + public long GetSystemDisplayService(ServiceCtx Context) + { + MakeObject(Context, new ISystemDisplayService()); + + return 0; + } + + public long GetManagerDisplayService(ServiceCtx Context) + { + MakeObject(Context, new IManagerDisplayService()); + + return 0; + } + + public long GetIndirectDisplayTransactionService(ServiceCtx Context) + { + MakeObject(Context, new IHOSBinderDriver(Context.Device.Gpu.Renderer)); + + return 0; + } + + public long ListDisplays(ServiceCtx Context) + { + long RecBuffPtr = Context.Request.ReceiveBuff[0].Position; + + AMemoryHelper.FillWithZeros(Context.Memory, RecBuffPtr, 0x60); + + //Add only the default display to buffer + Context.Memory.WriteBytes(RecBuffPtr, Encoding.ASCII.GetBytes("Default")); + Context.Memory.WriteInt64(RecBuffPtr + 0x40, 0x1L); + Context.Memory.WriteInt64(RecBuffPtr + 0x48, 0x1L); + Context.Memory.WriteInt64(RecBuffPtr + 0x50, 1920L); + Context.Memory.WriteInt64(RecBuffPtr + 0x58, 1080L); + + Context.ResponseData.Write(1L); + + return 0; + } + + public long OpenDisplay(ServiceCtx Context) + { + string Name = GetDisplayName(Context); + + long DisplayId = Displays.Add(new Display(Name)); + + Context.ResponseData.Write(DisplayId); + + return 0; + } + + public long CloseDisplay(ServiceCtx Context) + { + int DisplayId = Context.RequestData.ReadInt32(); + + Displays.Delete(DisplayId); + + return 0; + } + + public long GetDisplayResolution(ServiceCtx Context) + { + long DisplayId = Context.RequestData.ReadInt32(); + + Context.ResponseData.Write(1280); + Context.ResponseData.Write(720); + + return 0; + } + + public long OpenLayer(ServiceCtx Context) + { + long LayerId = Context.RequestData.ReadInt64(); + long UserId = Context.RequestData.ReadInt64(); + + long ParcelPtr = Context.Request.ReceiveBuff[0].Position; + + byte[] Parcel = MakeIGraphicsBufferProducer(ParcelPtr); + + Context.Memory.WriteBytes(ParcelPtr, Parcel); + + Context.ResponseData.Write((long)Parcel.Length); + + return 0; + } + + public long CloseLayer(ServiceCtx Context) + { + long LayerId = Context.RequestData.ReadInt64(); + + return 0; + } + + public long CreateStrayLayer(ServiceCtx Context) + { + long LayerFlags = Context.RequestData.ReadInt64(); + long DisplayId = Context.RequestData.ReadInt64(); + + long ParcelPtr = Context.Request.ReceiveBuff[0].Position; + + Display Disp = Displays.GetData<Display>((int)DisplayId); + + byte[] Parcel = MakeIGraphicsBufferProducer(ParcelPtr); + + Context.Memory.WriteBytes(ParcelPtr, Parcel); + + Context.ResponseData.Write(0L); + Context.ResponseData.Write((long)Parcel.Length); + + return 0; + } + + public long DestroyStrayLayer(ServiceCtx Context) + { + return 0; + } + + public long SetLayerScalingMode(ServiceCtx Context) + { + int ScalingMode = Context.RequestData.ReadInt32(); + long Unknown = Context.RequestData.ReadInt64(); + + return 0; + } + + public long GetDisplayVSyncEvent(ServiceCtx Context) + { + string Name = GetDisplayName(Context); + + int Handle = Context.Process.HandleTable.OpenHandle(Context.Device.System.VsyncEvent); + + Context.Response.HandleDesc = IpcHandleDesc.MakeCopy(Handle); + + return 0; + } + + private byte[] MakeIGraphicsBufferProducer(long BasePtr) + { + long Id = 0x20; + long CookiePtr = 0L; + + using (MemoryStream MS = new MemoryStream()) + { + BinaryWriter Writer = new BinaryWriter(MS); + + //flat_binder_object (size is 0x28) + Writer.Write(2); //Type (BINDER_TYPE_WEAK_BINDER) + Writer.Write(0); //Flags + Writer.Write((int)(Id >> 0)); + Writer.Write((int)(Id >> 32)); + Writer.Write((int)(CookiePtr >> 0)); + Writer.Write((int)(CookiePtr >> 32)); + Writer.Write((byte)'d'); + Writer.Write((byte)'i'); + Writer.Write((byte)'s'); + Writer.Write((byte)'p'); + Writer.Write((byte)'d'); + Writer.Write((byte)'r'); + Writer.Write((byte)'v'); + Writer.Write((byte)'\0'); + Writer.Write(0L); //Pad + + return MakeParcel(MS.ToArray(), new byte[] { 0, 0, 0, 0 }); + } + } + + private string GetDisplayName(ServiceCtx Context) + { + string Name = string.Empty; + + 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 Name; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs b/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs new file mode 100644 index 00000000..e86734e6 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Vi/IApplicationRootService.cs @@ -0,0 +1,29 @@ +using Ryujinx.HLE.HOS.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Vi +{ + class IApplicationRootService : IpcService + { + private Dictionary<int, ServiceProcessRequest> m_Commands; + + public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; + + public IApplicationRootService() + { + m_Commands = new Dictionary<int, ServiceProcessRequest>() + { + { 0, GetDisplayService } + }; + } + + public long GetDisplayService(ServiceCtx Context) + { + int ServiceType = Context.RequestData.ReadInt32(); + + MakeObject(Context, new IApplicationDisplayService()); + + return 0; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Vi/IHOSBinderDriver.cs b/Ryujinx.HLE/HOS/Services/Vi/IHOSBinderDriver.cs new file mode 100644 index 00000000..bf5f20a2 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Vi/IHOSBinderDriver.cs @@ -0,0 +1,100 @@ +using Ryujinx.Graphics.Gal; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel; +using Ryujinx.HLE.HOS.Services.Android; +using System; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Vi +{ + class IHOSBinderDriver : IpcService, IDisposable + { + private Dictionary<int, ServiceProcessRequest> m_Commands; + + public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; + + private KEvent ReleaseEvent; + + private NvFlinger Flinger; + + public IHOSBinderDriver(IGalRenderer Renderer) + { + m_Commands = new Dictionary<int, ServiceProcessRequest>() + { + { 0, TransactParcel }, + { 1, AdjustRefcount }, + { 2, GetNativeHandle }, + { 3, TransactParcelAuto } + }; + + ReleaseEvent = new KEvent(); + + Flinger = new NvFlinger(Renderer, ReleaseEvent); + } + + public long TransactParcel(ServiceCtx Context) + { + int Id = Context.RequestData.ReadInt32(); + int Code = Context.RequestData.ReadInt32(); + + long DataPos = Context.Request.SendBuff[0].Position; + long DataSize = Context.Request.SendBuff[0].Size; + + byte[] Data = Context.Memory.ReadBytes(DataPos, DataSize); + + Data = Parcel.GetParcelData(Data); + + return Flinger.ProcessParcelRequest(Context, Data, Code); + } + + public long TransactParcelAuto(ServiceCtx Context) + { + int Id = Context.RequestData.ReadInt32(); + int Code = Context.RequestData.ReadInt32(); + + (long DataPos, long DataSize) = Context.Request.GetBufferType0x21(); + + byte[] Data = Context.Memory.ReadBytes(DataPos, DataSize); + + Data = Parcel.GetParcelData(Data); + + return Flinger.ProcessParcelRequest(Context, Data, Code); + } + + public long AdjustRefcount(ServiceCtx Context) + { + int Id = Context.RequestData.ReadInt32(); + int AddVal = Context.RequestData.ReadInt32(); + int Type = Context.RequestData.ReadInt32(); + + return 0; + } + + public long GetNativeHandle(ServiceCtx Context) + { + int Id = Context.RequestData.ReadInt32(); + uint Unk = Context.RequestData.ReadUInt32(); + + int Handle = Context.Process.HandleTable.OpenHandle(ReleaseEvent); + + Context.Response.HandleDesc = IpcHandleDesc.MakeMove(Handle); + + return 0; + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing) + { + ReleaseEvent.Dispose(); + + Flinger.Dispose(); + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Vi/IManagerDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/IManagerDisplayService.cs new file mode 100644 index 00000000..61f0ffaa --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Vi/IManagerDisplayService.cs @@ -0,0 +1,54 @@ +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.Logging; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Vi +{ + class IManagerDisplayService : IpcService + { + private Dictionary<int, ServiceProcessRequest> m_Commands; + + public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; + + public IManagerDisplayService() + { + m_Commands = new Dictionary<int, ServiceProcessRequest>() + { + { 2010, CreateManagedLayer }, + { 2011, DestroyManagedLayer }, + { 6000, AddToLayerStack }, + { 6002, SetLayerVisibility } + }; + } + + public static long CreateManagedLayer(ServiceCtx Context) + { + Context.Device.Log.PrintStub(LogClass.ServiceVi, "Stubbed."); + + Context.ResponseData.Write(0L); //LayerId + + return 0; + } + + public long DestroyManagedLayer(ServiceCtx Context) + { + Context.Device.Log.PrintStub(LogClass.ServiceVi, "Stubbed."); + + return 0; + } + + public static long AddToLayerStack(ServiceCtx Context) + { + Context.Device.Log.PrintStub(LogClass.ServiceVi, "Stubbed."); + + return 0; + } + + public static long SetLayerVisibility(ServiceCtx Context) + { + Context.Device.Log.PrintStub(LogClass.ServiceVi, "Stubbed."); + + return 0; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs b/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs new file mode 100644 index 00000000..14b8fc3c --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Vi/IManagerRootService.cs @@ -0,0 +1,29 @@ +using Ryujinx.HLE.HOS.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Vi +{ + class IManagerRootService : IpcService + { + private Dictionary<int, ServiceProcessRequest> m_Commands; + + public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; + + public IManagerRootService() + { + m_Commands = new Dictionary<int, ServiceProcessRequest>() + { + { 2, GetDisplayService } + }; + } + + public long GetDisplayService(ServiceCtx Context) + { + int ServiceType = Context.RequestData.ReadInt32(); + + MakeObject(Context, new IApplicationDisplayService()); + + return 0; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Vi/ISystemDisplayService.cs b/Ryujinx.HLE/HOS/Services/Vi/ISystemDisplayService.cs new file mode 100644 index 00000000..5657ba69 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Vi/ISystemDisplayService.cs @@ -0,0 +1,48 @@ +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.Logging; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Vi +{ + class ISystemDisplayService : IpcService + { + private Dictionary<int, ServiceProcessRequest> m_Commands; + + public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; + + public ISystemDisplayService() + { + m_Commands = new Dictionary<int, ServiceProcessRequest>() + { + { 2205, SetLayerZ }, + { 2207, SetLayerVisibility }, + { 3200, GetDisplayMode } + }; + } + + public static long SetLayerZ(ServiceCtx Context) + { + Context.Device.Log.PrintStub(LogClass.ServiceVi, "Stubbed."); + + return 0; + } + + public static long SetLayerVisibility(ServiceCtx Context) + { + Context.Device.Log.PrintStub(LogClass.ServiceVi, "Stubbed."); + + return 0; + } + + public static long GetDisplayMode(ServiceCtx Context) + { + //TODO: De-hardcode resolution. + Context.ResponseData.Write(1280); + Context.ResponseData.Write(720); + Context.ResponseData.Write(60.0f); + Context.ResponseData.Write(0); + + return 0; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs b/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs new file mode 100644 index 00000000..d9b7c220 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Vi/ISystemRootService.cs @@ -0,0 +1,29 @@ +using Ryujinx.HLE.HOS.Ipc; +using System.Collections.Generic; + +namespace Ryujinx.HLE.HOS.Services.Vi +{ + class ISystemRootService : IpcService + { + private Dictionary<int, ServiceProcessRequest> m_Commands; + + public override IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; + + public ISystemRootService() + { + m_Commands = new Dictionary<int, ServiceProcessRequest>() + { + { 1, GetDisplayService } + }; + } + + public long GetDisplayService(ServiceCtx Context) + { + int ServiceType = Context.RequestData.ReadInt32(); + + MakeObject(Context, new IApplicationDisplayService()); + + return 0; + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs new file mode 100644 index 00000000..12c17311 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs @@ -0,0 +1,416 @@ +using Ryujinx.Graphics.Gal; +using Ryujinx.HLE.Gpu.Texture; +using Ryujinx.HLE.HOS.Kernel; +using Ryujinx.HLE.HOS.Services.Nv.NvMap; +using Ryujinx.HLE.Logging; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; + +using static Ryujinx.HLE.HOS.Services.Android.Parcel; + +namespace Ryujinx.HLE.HOS.Services.Android +{ + class NvFlinger : IDisposable + { + private delegate long ServiceProcessParcel(ServiceCtx Context, BinaryReader ParcelReader); + + private Dictionary<(string, int), ServiceProcessParcel> Commands; + + private KEvent ReleaseEvent; + + private IGalRenderer Renderer; + + private const int BufferQueueCount = 0x40; + private const int BufferQueueMask = BufferQueueCount - 1; + + [Flags] + private enum HalTransform + { + FlipX = 1 << 0, + FlipY = 1 << 1, + Rotate90 = 1 << 2 + } + + private enum BufferState + { + Free, + Dequeued, + Queued, + Acquired + } + + private struct Rect + { + public int Top; + public int Left; + public int Right; + public int Bottom; + } + + private struct BufferEntry + { + public BufferState State; + + public HalTransform Transform; + + public Rect Crop; + + public GbpBuffer Data; + } + + private BufferEntry[] BufferQueue; + + private ManualResetEvent WaitBufferFree; + + private bool Disposed; + + public NvFlinger(IGalRenderer Renderer, KEvent ReleaseEvent) + { + Commands = new Dictionary<(string, int), ServiceProcessParcel>() + { + { ("android.gui.IGraphicBufferProducer", 0x1), GbpRequestBuffer }, + { ("android.gui.IGraphicBufferProducer", 0x3), GbpDequeueBuffer }, + { ("android.gui.IGraphicBufferProducer", 0x4), GbpDetachBuffer }, + { ("android.gui.IGraphicBufferProducer", 0x7), GbpQueueBuffer }, + { ("android.gui.IGraphicBufferProducer", 0x8), GbpCancelBuffer }, + { ("android.gui.IGraphicBufferProducer", 0x9), GbpQuery }, + { ("android.gui.IGraphicBufferProducer", 0xa), GbpConnect }, + { ("android.gui.IGraphicBufferProducer", 0xb), GbpDisconnect }, + { ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer } + }; + + this.Renderer = Renderer; + this.ReleaseEvent = ReleaseEvent; + + BufferQueue = new BufferEntry[0x40]; + + WaitBufferFree = new ManualResetEvent(false); + } + + public long ProcessParcelRequest(ServiceCtx Context, byte[] ParcelData, int Code) + { + using (MemoryStream MS = new MemoryStream(ParcelData)) + { + BinaryReader Reader = new BinaryReader(MS); + + MS.Seek(4, SeekOrigin.Current); + + int StrSize = Reader.ReadInt32(); + + string InterfaceName = Encoding.Unicode.GetString(Reader.ReadBytes(StrSize * 2)); + + long Remainder = MS.Position & 0xf; + + if (Remainder != 0) + { + MS.Seek(0x10 - Remainder, SeekOrigin.Current); + } + + MS.Seek(0x50, SeekOrigin.Begin); + + if (Commands.TryGetValue((InterfaceName, Code), out ServiceProcessParcel ProcReq)) + { + Context.Device.Log.PrintDebug(LogClass.ServiceVi, $"{InterfaceName} {ProcReq.Method.Name}"); + + return ProcReq(Context, Reader); + } + else + { + throw new NotImplementedException($"{InterfaceName} {Code}"); + } + } + } + + private long GbpRequestBuffer(ServiceCtx Context, BinaryReader ParcelReader) + { + int Slot = ParcelReader.ReadInt32(); + + using (MemoryStream MS = new MemoryStream()) + { + BinaryWriter Writer = new BinaryWriter(MS); + + BufferEntry Entry = BufferQueue[Slot]; + + int BufferCount = 1; //? + long BufferSize = Entry.Data.Size; + + Writer.Write(BufferCount); + Writer.Write(BufferSize); + + Entry.Data.Write(Writer); + + Writer.Write(0); + + return MakeReplyParcel(Context, MS.ToArray()); + } + } + + private long GbpDequeueBuffer(ServiceCtx Context, BinaryReader ParcelReader) + { + //TODO: Errors. + int Format = ParcelReader.ReadInt32(); + int Width = ParcelReader.ReadInt32(); + int Height = ParcelReader.ReadInt32(); + int GetTimestamps = ParcelReader.ReadInt32(); + int Usage = ParcelReader.ReadInt32(); + + int Slot = GetFreeSlotBlocking(Width, Height); + + return MakeReplyParcel(Context, Slot, 1, 0x24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + } + + private long GbpQueueBuffer(ServiceCtx Context, BinaryReader ParcelReader) + { + Context.Device.Statistics.RecordGameFrameTime(); + + //TODO: Errors. + int Slot = ParcelReader.ReadInt32(); + int Unknown4 = ParcelReader.ReadInt32(); + int Unknown8 = ParcelReader.ReadInt32(); + int Unknownc = ParcelReader.ReadInt32(); + int Timestamp = ParcelReader.ReadInt32(); + int IsAutoTimestamp = ParcelReader.ReadInt32(); + int CropTop = ParcelReader.ReadInt32(); + int CropLeft = ParcelReader.ReadInt32(); + int CropRight = ParcelReader.ReadInt32(); + int CropBottom = ParcelReader.ReadInt32(); + int ScalingMode = ParcelReader.ReadInt32(); + int Transform = ParcelReader.ReadInt32(); + int StickyTransform = ParcelReader.ReadInt32(); + int Unknown34 = ParcelReader.ReadInt32(); + int Unknown38 = ParcelReader.ReadInt32(); + int IsFenceValid = ParcelReader.ReadInt32(); + int Fence0Id = ParcelReader.ReadInt32(); + int Fence0Value = ParcelReader.ReadInt32(); + int Fence1Id = ParcelReader.ReadInt32(); + int Fence1Value = ParcelReader.ReadInt32(); + + BufferQueue[Slot].Transform = (HalTransform)Transform; + + BufferQueue[Slot].Crop.Top = CropTop; + BufferQueue[Slot].Crop.Left = CropLeft; + BufferQueue[Slot].Crop.Right = CropRight; + BufferQueue[Slot].Crop.Bottom = CropBottom; + + BufferQueue[Slot].State = BufferState.Queued; + + SendFrameBuffer(Context, Slot); + + return MakeReplyParcel(Context, 1280, 720, 0, 0, 0); + } + + private long GbpDetachBuffer(ServiceCtx Context, BinaryReader ParcelReader) + { + return MakeReplyParcel(Context, 0); + } + + private long GbpCancelBuffer(ServiceCtx Context, BinaryReader ParcelReader) + { + //TODO: Errors. + int Slot = ParcelReader.ReadInt32(); + + BufferQueue[Slot].State = BufferState.Free; + + return MakeReplyParcel(Context, 0); + } + + private long GbpQuery(ServiceCtx Context, BinaryReader ParcelReader) + { + return MakeReplyParcel(Context, 0, 0); + } + + private long GbpConnect(ServiceCtx Context, BinaryReader ParcelReader) + { + return MakeReplyParcel(Context, 1280, 720, 0, 0, 0); + } + + private long GbpDisconnect(ServiceCtx Context, BinaryReader ParcelReader) + { + return MakeReplyParcel(Context, 0); + } + + private long GbpPreallocBuffer(ServiceCtx Context, BinaryReader ParcelReader) + { + int Slot = ParcelReader.ReadInt32(); + + int BufferCount = ParcelReader.ReadInt32(); + + if (BufferCount > 0) + { + long BufferSize = ParcelReader.ReadInt64(); + + BufferQueue[Slot].State = BufferState.Free; + + BufferQueue[Slot].Data = new GbpBuffer(ParcelReader); + } + + return MakeReplyParcel(Context, 0); + } + + private long MakeReplyParcel(ServiceCtx Context, params int[] Ints) + { + using (MemoryStream MS = new MemoryStream()) + { + BinaryWriter Writer = new BinaryWriter(MS); + + foreach (int Int in Ints) + { + Writer.Write(Int); + } + + return MakeReplyParcel(Context, MS.ToArray()); + } + } + + private long MakeReplyParcel(ServiceCtx Context, byte[] Data) + { + long ReplyPos = Context.Request.ReceiveBuff[0].Position; + long ReplySize = Context.Request.ReceiveBuff[0].Size; + + byte[] Reply = MakeParcel(Data, new byte[0]); + + Context.Memory.WriteBytes(ReplyPos, Reply); + + return 0; + } + + private void SendFrameBuffer(ServiceCtx Context, int Slot) + { + int FbWidth = BufferQueue[Slot].Data.Width; + int FbHeight = BufferQueue[Slot].Data.Height; + + int NvMapHandle = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x4c); + int BufferOffset = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x50); + + NvMapHandle Map = NvMapIoctl.GetNvMap(Context, NvMapHandle);; + + long FbAddr = Map.Address + BufferOffset; + + BufferQueue[Slot].State = BufferState.Acquired; + + Rect Crop = BufferQueue[Slot].Crop; + + bool FlipX = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipX); + bool FlipY = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipY); + + //Rotation is being ignored + + int Top = Crop.Top; + int Left = Crop.Left; + int Right = Crop.Right; + int Bottom = Crop.Bottom; + + Renderer.QueueAction(() => Renderer.FrameBuffer.SetTransform(FlipX, FlipY, Top, Left, Right, Bottom)); + + //TODO: Support double buffering here aswell, it is broken for GPU + //frame buffers because it seems to be completely out of sync. + if (Context.Device.Gpu.Engine3d.IsFrameBufferPosition(FbAddr)) + { + //Frame buffer is rendered to by the GPU, we can just + //bind the frame buffer texture, it's not necessary to read anything. + Renderer.QueueAction(() => Renderer.FrameBuffer.Set(FbAddr)); + } + else + { + //Frame buffer is not set on the GPU registers, in this case + //assume that the app is manually writing to it. + TextureInfo Texture = new TextureInfo(FbAddr, FbWidth, FbHeight); + + byte[] Data = TextureReader.Read(Context.Memory, Texture); + + Renderer.QueueAction(() => Renderer.FrameBuffer.Set(Data, FbWidth, FbHeight)); + } + + Context.Device.Gpu.Renderer.QueueAction(() => ReleaseBuffer(Slot)); + } + + private void ReleaseBuffer(int Slot) + { + BufferQueue[Slot].State = BufferState.Free; + + ReleaseEvent.WaitEvent.Set(); + + lock (WaitBufferFree) + { + WaitBufferFree.Set(); + } + } + + private int GetFreeSlotBlocking(int Width, int Height) + { + int Slot; + + do + { + lock (WaitBufferFree) + { + if ((Slot = GetFreeSlot(Width, Height)) != -1) + { + break; + } + + if (Disposed) + { + break; + } + + WaitBufferFree.Reset(); + } + + WaitBufferFree.WaitOne(); + } + while (!Disposed); + + return Slot; + } + + private int GetFreeSlot(int Width, int Height) + { + lock (BufferQueue) + { + for (int Slot = 0; Slot < BufferQueue.Length; Slot++) + { + if (BufferQueue[Slot].State != BufferState.Free) + { + continue; + } + + GbpBuffer Data = BufferQueue[Slot].Data; + + if (Data.Width == Width && + Data.Height == Height) + { + BufferQueue[Slot].State = BufferState.Dequeued; + + return Slot; + } + } + } + + return -1; + } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool Disposing) + { + if (Disposing && !Disposed) + { + Disposed = true; + + lock (WaitBufferFree) + { + WaitBufferFree.Set(); + } + + WaitBufferFree.Dispose(); + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Vi/Parcel.cs b/Ryujinx.HLE/HOS/Services/Vi/Parcel.cs new file mode 100644 index 00000000..bb842795 --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/Vi/Parcel.cs @@ -0,0 +1,58 @@ +using System; +using System.IO; + +namespace Ryujinx.HLE.HOS.Services.Android +{ + static class Parcel + { + public static byte[] GetParcelData(byte[] Parcel) + { + if (Parcel == null) + { + throw new ArgumentNullException(nameof(Parcel)); + } + + using (MemoryStream MS = new MemoryStream(Parcel)) + { + BinaryReader Reader = new BinaryReader(MS); + + int DataSize = Reader.ReadInt32(); + int DataOffset = Reader.ReadInt32(); + int ObjsSize = Reader.ReadInt32(); + int ObjsOffset = Reader.ReadInt32(); + + MS.Seek(DataOffset - 0x10, SeekOrigin.Current); + + return Reader.ReadBytes(DataSize); + } + } + + public static byte[] MakeParcel(byte[] Data, byte[] Objs) + { + if (Data == null) + { + throw new ArgumentNullException(nameof(Data)); + } + + if (Objs == null) + { + throw new ArgumentNullException(nameof(Objs)); + } + + using (MemoryStream MS = new MemoryStream()) + { + BinaryWriter Writer = new BinaryWriter(MS); + + Writer.Write(Data.Length); + Writer.Write(0x10); + Writer.Write(Objs.Length); + Writer.Write(Data.Length + 0x10); + + Writer.Write(Data); + Writer.Write(Objs); + + return MS.ToArray(); + } + } + } +}
\ No newline at end of file |
