diff options
| author | gdkchan <gab.dark.100@gmail.com> | 2018-02-23 18:48:27 -0300 |
|---|---|---|
| committer | gdkchan <gab.dark.100@gmail.com> | 2018-02-23 18:48:27 -0300 |
| commit | 2ed733b1d5addad027f48acfdd407e64b71427fc (patch) | |
| tree | 1254df3ab2bace8de561b5a3549f668fabcf4aa9 /Ryujinx.Core | |
| parent | eafc58c9f2e2e0c19d22f0da2a93ab5372aeef29 (diff) | |
Somewhat better NvFlinger (I guess) (fixes #30)
Diffstat (limited to 'Ryujinx.Core')
| -rw-r--r-- | Ryujinx.Core/OsHle/Objects/Android/GbpBuffer.cs | 60 | ||||
| -rw-r--r-- | Ryujinx.Core/OsHle/Objects/Android/NvFlinger.cs | 392 | ||||
| -rw-r--r-- | Ryujinx.Core/OsHle/Objects/Android/Parcel.cs (renamed from Ryujinx.Core/OsHle/Objects/Parcel.cs) | 0 | ||||
| -rw-r--r-- | Ryujinx.Core/OsHle/Objects/ErrorCode.cs | 2 | ||||
| -rw-r--r-- | Ryujinx.Core/OsHle/Objects/Hid/IActiveVibrationDeviceList.cs | 1 | ||||
| -rw-r--r-- | Ryujinx.Core/OsHle/Objects/Vi/IHOSBinderDriver.cs | 178 |
6 files changed, 472 insertions, 161 deletions
diff --git a/Ryujinx.Core/OsHle/Objects/Android/GbpBuffer.cs b/Ryujinx.Core/OsHle/Objects/Android/GbpBuffer.cs new file mode 100644 index 00000000..edd11523 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Android/GbpBuffer.cs @@ -0,0 +1,60 @@ +using System.IO; + +namespace Ryujinx.Core.OsHle.Objects.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.Core/OsHle/Objects/Android/NvFlinger.cs b/Ryujinx.Core/OsHle/Objects/Android/NvFlinger.cs new file mode 100644 index 00000000..23863099 --- /dev/null +++ b/Ryujinx.Core/OsHle/Objects/Android/NvFlinger.cs @@ -0,0 +1,392 @@ +using ChocolArm64.Memory; +using Ryujinx.Core.OsHle.Handles; +using System; +using System.IO; +using System.Collections.Generic; +using System.Text; +using System.Threading; + +using static Ryujinx.Core.OsHle.Objects.Android.Parcel; + +namespace Ryujinx.Core.OsHle.Objects.Android +{ + class NvFlinger : IDisposable + { + private delegate long ServiceProcessParcel(ServiceCtx Context, BinaryReader ParcelReader); + + private Dictionary<(string, int), ServiceProcessParcel> Commands; + + 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 BufferEntry + { + public BufferState State; + + public HalTransform Transform; + + public GbpBuffer Data; + } + + private BufferEntry[] BufferQueue; + + private ManualResetEvent WaitBufferFree; + + private bool KeepRunning; + + public NvFlinger() + { + Commands = new Dictionary<(string, int), ServiceProcessParcel>() + { + { ("android.gui.IGraphicBufferProducer", 0x1), GbpRequestBuffer }, + { ("android.gui.IGraphicBufferProducer", 0x3), GbpDequeueBuffer }, + { ("android.gui.IGraphicBufferProducer", 0x7), GbpQueueBuffer }, + { ("android.gui.IGraphicBufferProducer", 0x8), GbpCancelBuffer }, + { ("android.gui.IGraphicBufferProducer", 0x9), GbpQuery }, + { ("android.gui.IGraphicBufferProducer", 0xa), GbpConnect }, + { ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer } + }; + + BufferQueue = new BufferEntry[0x40]; + + WaitBufferFree = new ManualResetEvent(false); + + KeepRunning = true; + } + + 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)) + { + Logging.Debug($"{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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + } + + private long GbpQueueBuffer(ServiceCtx Context, BinaryReader ParcelReader) + { + //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].State = BufferState.Queued; + + SendFrameBuffer(Context, Slot); + + return MakeReplyParcel(Context, 1280, 720, 0, 0, 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 GbpPreallocBuffer(ServiceCtx Context, BinaryReader ParcelReader) + { + int Slot = ParcelReader.ReadInt32(); + + int BufferCount = ParcelReader.ReadInt32(); + 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]); + + AMemoryHelper.WriteBytes(Context.Memory, ReplyPos, Reply); + + return 0; + } + + private unsafe void SendFrameBuffer(ServiceCtx Context, int Slot) + { + int FbWidth = BufferQueue[Slot].Data.Width; + int FbHeight = BufferQueue[Slot].Data.Height; + + int FbSize = FbWidth * FbHeight * 4; + + HNvMap NvMap = GetNvMap(Context, Slot); + + if (NvMap.Address < 0 || NvMap.Address + FbSize > AMemoryMgr.AddrSize) + { + Logging.Error($"Frame buffer address {NvMap.Address:x16} is invalid!"); + + BufferQueue[Slot].State = BufferState.Free; + + WaitBufferFree.Set(); + + return; + } + + BufferQueue[Slot].State = BufferState.Acquired; + + float ScaleX = 1; + float ScaleY = 1; + float Rotate = 0; + + if (BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipX)) + { + ScaleX = -1; + } + + if (BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipY)) + { + ScaleY = -1; + } + + if (BufferQueue[Slot].Transform.HasFlag(HalTransform.Rotate90)) + { + Rotate = MathF.PI * 0.5f; + } + + byte* Fb = (byte*)Context.Ns.Ram + NvMap.Address; + + Context.Ns.Gpu.Renderer.QueueAction(delegate() + { + Context.Ns.Gpu.Renderer.SetFrameBuffer( + Fb, + FbWidth, + FbHeight, + ScaleX, + ScaleY, + Rotate); + + BufferQueue[Slot].State = BufferState.Free; + + WaitBufferFree.Set(); + }); + } + + private HNvMap GetNvMap(ServiceCtx Context, int Slot) + { + int NvMapHandle = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x4c); + + if (!BitConverter.IsLittleEndian) + { + byte[] RawValue = BitConverter.GetBytes(NvMapHandle); + + Array.Reverse(RawValue); + + NvMapHandle = BitConverter.ToInt32(RawValue, 0); + } + + return Context.Ns.Os.Handles.GetData<HNvMap>(NvMapHandle); + } + + private int GetFreeSlotBlocking(int Width, int Height) + { + int Slot; + + do + { + if ((Slot = GetFreeSlot(Width, Height)) != -1) + { + break; + } + + Logging.Debug("Waiting for a free BufferQueue slot..."); + + lock (WaitBufferFree) + { + if (!KeepRunning) + { + break; + } + + WaitBufferFree.Reset(); + } + + WaitBufferFree.WaitOne(); + } + while (KeepRunning); + + Logging.Debug($"Found free BufferQueue slot {Slot}!"); + + 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) + { + lock (WaitBufferFree) + { + KeepRunning = false; + + WaitBufferFree.Set(); + } + + WaitBufferFree.Dispose(); + } + } + } +}
\ No newline at end of file diff --git a/Ryujinx.Core/OsHle/Objects/Parcel.cs b/Ryujinx.Core/OsHle/Objects/Android/Parcel.cs index cd7179e1..cd7179e1 100644 --- a/Ryujinx.Core/OsHle/Objects/Parcel.cs +++ b/Ryujinx.Core/OsHle/Objects/Android/Parcel.cs diff --git a/Ryujinx.Core/OsHle/Objects/ErrorCode.cs b/Ryujinx.Core/OsHle/Objects/ErrorCode.cs index 20f97f84..659ed8b9 100644 --- a/Ryujinx.Core/OsHle/Objects/ErrorCode.cs +++ b/Ryujinx.Core/OsHle/Objects/ErrorCode.cs @@ -1,5 +1,3 @@ -using System; - namespace Ryujinx.Core.OsHle.Objects { static class ErrorCode diff --git a/Ryujinx.Core/OsHle/Objects/Hid/IActiveVibrationDeviceList.cs b/Ryujinx.Core/OsHle/Objects/Hid/IActiveVibrationDeviceList.cs index 1f0c8592..aae3c38d 100644 --- a/Ryujinx.Core/OsHle/Objects/Hid/IActiveVibrationDeviceList.cs +++ b/Ryujinx.Core/OsHle/Objects/Hid/IActiveVibrationDeviceList.cs @@ -1,4 +1,3 @@ -using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Ipc; using System.Collections.Generic; diff --git a/Ryujinx.Core/OsHle/Objects/Vi/IHOSBinderDriver.cs b/Ryujinx.Core/OsHle/Objects/Vi/IHOSBinderDriver.cs index cfd271e8..bbea3368 100644 --- a/Ryujinx.Core/OsHle/Objects/Vi/IHOSBinderDriver.cs +++ b/Ryujinx.Core/OsHle/Objects/Vi/IHOSBinderDriver.cs @@ -1,34 +1,18 @@ using ChocolArm64.Memory; -using Ryujinx.Core.OsHle.Handles; using Ryujinx.Core.OsHle.Ipc; -using Ryujinx.Core.OsHle.Utilities; +using Ryujinx.Core.OsHle.Objects.Android; using System; using System.Collections.Generic; -using System.IO; -using System.Text; - -using static Ryujinx.Core.OsHle.Objects.Android.Parcel; namespace Ryujinx.Core.OsHle.Objects.Vi { - class IHOSBinderDriver : IIpcInterface + class IHOSBinderDriver : IIpcInterface, IDisposable { - private delegate long ServiceProcessParcel(ServiceCtx Context, byte[] ParcelData); - private Dictionary<int, ServiceProcessRequest> m_Commands; - private Dictionary<(string, int), ServiceProcessParcel> m_Methods; - - public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; + public IReadOnlyDictionary<int, ServiceProcessRequest> Commands => m_Commands; - private class BufferObj - { - - } - - private IdPoolWithObj BufferSlots; - - private byte[] Gbfr; + private NvFlinger Flinger; public IHOSBinderDriver() { @@ -39,18 +23,7 @@ namespace Ryujinx.Core.OsHle.Objects.Vi { 2, GetNativeHandle } }; - m_Methods = new Dictionary<(string, int), ServiceProcessParcel>() - { - { ("android.gui.IGraphicBufferProducer", 0x1), GraphicBufferProducerRequestBuffer }, - { ("android.gui.IGraphicBufferProducer", 0x3), GraphicBufferProducerDequeueBuffer }, - { ("android.gui.IGraphicBufferProducer", 0x7), GraphicBufferProducerQueueBuffer }, - { ("android.gui.IGraphicBufferProducer", 0x8), GraphicBufferProducerCancelBuffer }, - { ("android.gui.IGraphicBufferProducer", 0x9), GraphicBufferProducerQuery }, - { ("android.gui.IGraphicBufferProducer", 0xa), GraphicBufferProducerConnect }, - { ("android.gui.IGraphicBufferProducer", 0xe), GraphicBufferPreallocateBuffer } - }; - - BufferSlots = new IdPoolWithObj(); + Flinger = new NvFlinger(); } public long TransactParcel(ServiceCtx Context) @@ -63,133 +36,9 @@ namespace Ryujinx.Core.OsHle.Objects.Vi byte[] Data = AMemoryHelper.ReadBytes(Context.Memory, DataPos, (int)DataSize); - Data = GetParcelData(Data); - - using (MemoryStream MS = new MemoryStream(Data)) - { - BinaryReader Reader = new BinaryReader(MS); - - MS.Seek(4, SeekOrigin.Current); - - int StrSize = Reader.ReadInt32(); - - string InterfaceName = Encoding.Unicode.GetString(Data, 8, StrSize * 2); - - if (m_Methods.TryGetValue((InterfaceName, Code), out ServiceProcessParcel ProcReq)) - { - return ProcReq(Context, Data); - } - else - { - throw new NotImplementedException($"{InterfaceName} {Code}"); - } - } - } - - private long GraphicBufferProducerRequestBuffer(ServiceCtx Context, byte[] ParcelData) - { - int GbfrSize = Gbfr?.Length ?? 0; - - byte[] Data = new byte[GbfrSize + 4]; - - if (Gbfr != null) - { - Buffer.BlockCopy(Gbfr, 0, Data, 0, GbfrSize); - } - - return MakeReplyParcel(Context, Data); - } - - private long GraphicBufferProducerDequeueBuffer(ServiceCtx Context, byte[] ParcelData) - { - //Note: It seems that the maximum number of slots is 64, because if we return - //a Slot number > 63, it seems to cause a buffer overrun and it reads garbage. - //Note 2: The size of each object associated with the slot is 0x30. - int Slot = BufferSlots.GenerateId(new BufferObj()); - - return MakeReplyParcel(Context, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - } - - private long GraphicBufferProducerQueueBuffer(ServiceCtx Context, byte[] ParcelData) - { - return MakeReplyParcel(Context, 1280, 720, 0, 0, 0); - } - - private long GraphicBufferProducerCancelBuffer(ServiceCtx Context, byte[] ParcelData) - { - using (MemoryStream MS = new MemoryStream(ParcelData)) - { - BinaryReader Reader = new BinaryReader(MS); - - MS.Seek(0x50, SeekOrigin.Begin); - - int Slot = Reader.ReadInt32(); - - BufferSlots.Delete(Slot); - - return MakeReplyParcel(Context, 0); - } - } - - private long GraphicBufferProducerQuery(ServiceCtx Context, byte[] ParcelData) - { - return MakeReplyParcel(Context, 0, 0); - } + Data = Parcel.GetParcelData(Data); - private long GraphicBufferProducerConnect(ServiceCtx Context, byte[] ParcelData) - { - return MakeReplyParcel(Context, 1280, 720, 0, 0, 0); - } - - private long GraphicBufferPreallocateBuffer(ServiceCtx Context, byte[] ParcelData) - { - int GbfrSize = ParcelData.Length - 0x54; - - Gbfr = new byte[GbfrSize]; - - Buffer.BlockCopy(ParcelData, 0x54, Gbfr, 0, GbfrSize); - - using (MemoryStream MS = new MemoryStream(ParcelData)) - { - BinaryReader Reader = new BinaryReader(MS); - - MS.Seek(0xd4, SeekOrigin.Begin); - - int Handle = Reader.ReadInt32(); - - HNvMap NvMap = Context.Ns.Os.Handles.GetData<HNvMap>(Handle); - - Context.Ns.Gpu.Renderer.FrameBufferPtr = NvMap.Address; - } - - 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].Position; - - byte[] Reply = MakeParcel(Data, new byte[0]); - - AMemoryHelper.WriteBytes(Context.Memory, ReplyPos, Reply); - - return 0; + return Flinger.ProcessParcelRequest(Context, Data, Code); } public long AdjustRefcount(ServiceCtx Context) @@ -210,5 +59,18 @@ namespace Ryujinx.Core.OsHle.Objects.Vi return 0; } + + public void Dispose() + { + Dispose(true); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + Flinger.Dispose(); + } + } } }
\ No newline at end of file |
