diff --git a/Ryujinx.HLE/HOS/Services/Vi/GbpBuffer.cs b/Ryujinx.HLE/HOS/Services/Vi/GbpBuffer.cs index 41cbacf962..9b0bd87f49 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/GbpBuffer.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/GbpBuffer.cs @@ -1,60 +1,140 @@ using System.IO; +using System.Runtime.InteropServices; namespace Ryujinx.HLE.HOS.Services.Android { + [StructLayout(LayoutKind.Sequential, Size = 0x28)] + struct GraphicBufferHeader + { + public int Magic; + public int Width; + public int Height; + public int Stride; + public int Format; + public int Usage; + + public int Pid; + public int RefCount; + + public int FdsCount; + public int IntsCount; + } + + [StructLayout(LayoutKind.Explicit, Size = 0x58)] + struct NvGraphicBufferSurface + { + [FieldOffset(0)] + public uint Width; + + [FieldOffset(0x4)] + public uint Height; + + [FieldOffset(0x8)] + public ulong ColorFormat; + + [FieldOffset(0x10)] + public int Layout; + + [FieldOffset(0x14)] + public int Pitch; + + [FieldOffset(0x18)] + public int NvMapHandle; + + [FieldOffset(0x1C)] + public int Offset; + + [FieldOffset(0x20)] + public int Kind; + + [FieldOffset(0x24)] + public int BlockHeightLog2; + + [FieldOffset(0x28)] + public int ScanFormat; + + [FieldOffset(0x30)] + public long Flags; + + [FieldOffset(0x38)] + public long Size; + } + + [StructLayout(LayoutKind.Explicit, Size = 0x144)] + struct NvGraphicBuffer + { + [FieldOffset(0x4)] + public int NvMapId; + + [FieldOffset(0xC)] + public int Magic; + + [FieldOffset(0x10)] + public int Pid; + + [FieldOffset(0x14)] + public int Type; + + [FieldOffset(0x18)] + public int Usage; + + [FieldOffset(0x1C)] + public int PixelFormat; + + [FieldOffset(0x20)] + public int ExternalPixelFormat; + + [FieldOffset(0x24)] + public int Stride; + + [FieldOffset(0x28)] + public int FrameBufferSize; + + [FieldOffset(0x2C)] + public int PlanesCount; + + // This throw an exception because C# is dumb and want to make every entries of the array appears at the FieldOffset + /*[FieldOffset(0x34)] + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public NvGraphicBufferSurface[] Surfaces;*/ + + [FieldOffset(0x34)] + public NvGraphicBufferSurface Surface0; + + [FieldOffset(0x8c)] + public NvGraphicBufferSurface Surface1; + + [FieldOffset(0xe4)] + public NvGraphicBufferSurface Surface2; + } + 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 GraphicBufferHeader Header { get; private set; } + public NvGraphicBuffer Buffer { 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 int Size => Marshal.SizeOf() + Marshal.SizeOf(); public GbpBuffer(BinaryReader reader) { - Magic = reader.ReadInt32(); - Width = reader.ReadInt32(); - Height = reader.ReadInt32(); - Stride = reader.ReadInt32(); - Format = reader.ReadInt32(); - Usage = reader.ReadInt32(); + Header = NvFlinger.ReadStruct(reader); - Pid = reader.ReadInt32(); - RefCount = reader.ReadInt32(); + // ignore fds + // TODO: check if that is used in official implementation + reader.BaseStream.Position += Header.FdsCount * 4; - FdsCount = reader.ReadInt32(); - IntsCount = reader.ReadInt32(); + if (Header.IntsCount != 0x51) + { + throw new System.NotImplementedException($"Unexpected Graphic Buffer ints count (expected 0x51, found 0x{Header.IntsCount:x}"); + } - RawData = reader.ReadBytes((FdsCount + IntsCount) * 4); + Buffer = NvFlinger.ReadStruct(reader); } 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); + NvFlinger.WriteStruct(writer, Header); + NvFlinger.WriteStruct(writer, Buffer); } } } \ No newline at end of file diff --git a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs index 0eab5e31c7..7233e7889a 100644 --- a/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs +++ b/Ryujinx.HLE/HOS/Services/Vi/NvFlinger.cs @@ -7,6 +7,7 @@ using Ryujinx.HLE.HOS.Services.Nv.NvMap; using System; using System.Collections.Generic; using System.IO; +using System.Runtime.InteropServices; using System.Text; using System.Threading; @@ -30,9 +31,11 @@ namespace Ryujinx.HLE.HOS.Services.Android [Flags] private enum HalTransform { - FlipX = 1 << 0, - FlipY = 1 << 1, - Rotate90 = 1 << 2 + FlipX = 1, + FlipY = 2, + Rotate90 = 4, + Rotate180 = FlipX | FlipY, + Rotate270 = Rotate90 | Rotate180, } private enum BufferState @@ -43,6 +46,33 @@ namespace Ryujinx.HLE.HOS.Services.Android Acquired } + [StructLayout(LayoutKind.Sequential, Size = 0x8)] + private struct Fence + { + public int id; + public int value; + } + + [StructLayout(LayoutKind.Explicit, Size = 0x24)] + private struct MultiFence + { + [FieldOffset(0x0)] + public int FenceCount; + + [FieldOffset(0x4)] + public Fence Fence0; + + [FieldOffset(0xC)] + public Fence Fence1; + + [FieldOffset(0x14)] + public Fence Fence2; + + [FieldOffset(0x1C)] + public Fence Fence3; + } + + [StructLayout(LayoutKind.Sequential, Size = 0x10)] private struct Rect { public int Top; @@ -51,6 +81,37 @@ namespace Ryujinx.HLE.HOS.Services.Android public int Bottom; } + [StructLayout(LayoutKind.Explicit)] + private struct QueueBufferObject + { + [FieldOffset(0x0)] + public long Timestamp; + + [FieldOffset(0x8)] + public int IsAutoTimestamp; + + [FieldOffset(0xC)] + public Rect Crop; + + [FieldOffset(0x1C)] + public int ScalingMode; + + [FieldOffset(0x20)] + public HalTransform Transform; + + [FieldOffset(0x24)] + public int StickyTransform; + + [FieldOffset(0x28)] + public int Unknown; + + [FieldOffset(0x2C)] + public int SwapInterval; + + [FieldOffset(0x30)] + public MultiFence Fence; + } + private struct BufferEntry { public BufferState State; @@ -170,34 +231,14 @@ namespace Ryujinx.HLE.HOS.Services.Android //TODO: Errors. int slot = parcelReader.ReadInt32(); - int flatternedObjSize = parcelReader.ReadInt32(); - int fdCount = parcelReader.ReadInt32(); + long Position = parcelReader.BaseStream.Position; - long timestamp = parcelReader.ReadInt64(); - int isAutoTimestamp = parcelReader.ReadInt32(); + QueueBufferObject queueBufferObject = ReadFlattenedObject(parcelReader); - int cropTop = parcelReader.ReadInt32(); - int cropLeft = parcelReader.ReadInt32(); - int cropRight = parcelReader.ReadInt32(); - int cropBottom = parcelReader.ReadInt32(); + parcelReader.BaseStream.Position = Position; - int scalingMode = parcelReader.ReadInt32(); - int transform = parcelReader.ReadInt32(); - int stickyTransform = parcelReader.ReadInt32(); - int unknown34 = parcelReader.ReadInt32(); - int swapInterval = 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].Transform = queueBufferObject.Transform; + _bufferQueue[slot].Crop = queueBufferObject.Crop; _bufferQueue[slot].State = BufferState.Queued; @@ -221,6 +262,8 @@ namespace Ryujinx.HLE.HOS.Services.Android //TODO: Errors. int slot = parcelReader.ReadInt32(); + MultiFence fence = ReadFlattenedObject(parcelReader); + _bufferQueue[slot].State = BufferState.Free; _waitBufferFree.Set(); @@ -251,16 +294,65 @@ namespace Ryujinx.HLE.HOS.Services.Android if (hasInput) { - long bufferSize = parcelReader.ReadInt64(); + byte[] graphicBuffer = ReadFlattenedObject(parcelReader); _bufferQueue[slot].State = BufferState.Free; - _bufferQueue[slot].Data = new GbpBuffer(parcelReader); + using (BinaryReader graphicBufferReader = new BinaryReader(new MemoryStream(graphicBuffer))) + { + _bufferQueue[slot].Data = new GbpBuffer(graphicBufferReader); + } + } return MakeReplyParcel(context, 0); } + private byte[] ReadFlattenedObject(BinaryReader reader) + { + long flattenedObjectSize = reader.ReadInt64(); + + return reader.ReadBytes((int)flattenedObjectSize); + } + + private unsafe T ReadFlattenedObject(BinaryReader reader) where T: struct + { + byte[] data = ReadFlattenedObject(reader); + + fixed (byte* ptr = data) + { + return Marshal.PtrToStructure((IntPtr)ptr); + } + } + + // FIXME: move this (extension?) + public unsafe static T ReadStruct(BinaryReader reader) where T : struct + { + int size = Marshal.SizeOf(); + + byte[] data = reader.ReadBytes(size); + + fixed (byte* ptr = data) + { + return Marshal.PtrToStructure((IntPtr)ptr); + } + } + + // FIXME: move this (extension?) + public unsafe static void WriteStruct(BinaryWriter writer, T value) where T : struct + { + long size = Marshal.SizeOf(); + + byte[] data = new byte[size]; + + fixed (byte* ptr = data) + { + Marshal.StructureToPtr(value, (IntPtr)ptr, false); + } + + writer.Write(data); + } + private long MakeReplyParcel(ServiceCtx context, params int[] ints) { using (MemoryStream ms = new MemoryStream()) @@ -287,18 +379,20 @@ namespace Ryujinx.HLE.HOS.Services.Android return 0; } + // TODO: support multi surface private void SendFrameBuffer(ServiceCtx context, int slot) { - int fbWidth = _bufferQueue[slot].Data.Width; - int fbHeight = _bufferQueue[slot].Data.Height; + int fbWidth = _bufferQueue[slot].Data.Header.Width; + int fbHeight = _bufferQueue[slot].Data.Header.Height; + + int nvMapHandle = _bufferQueue[slot].Data.Buffer.Surface0.NvMapHandle; - int nvMapHandle = BitConverter.ToInt32(_bufferQueue[slot].Data.RawData, 0x4c); if (nvMapHandle == 0) { - nvMapHandle = BitConverter.ToInt32(_bufferQueue[slot].Data.RawData, 0x4); + nvMapHandle = _bufferQueue[slot].Data.Buffer.NvMapId; } - int bufferOffset = BitConverter.ToInt32(_bufferQueue[slot].Data.RawData, 0x50); + int bufferOffset = _bufferQueue[slot].Data.Buffer.Surface0.Offset; NvMapHandle map = NvMapIoctl.GetNvMap(context, nvMapHandle); @@ -386,8 +480,8 @@ namespace Ryujinx.HLE.HOS.Services.Android GbpBuffer data = _bufferQueue[slot].Data; - if (data.Width == width && - data.Height == height) + if (data.Header.Width == width && + data.Header.Height == height) { _bufferQueue[slot].State = BufferState.Dequeued;