Improve code reaadability in NvFlinger parsing

This commit is contained in:
Thog 2019-01-03 17:52:10 +01:00
commit f647647c43
No known key found for this signature in database
GPG key ID: 0CD291558FAFDBC6
2 changed files with 251 additions and 77 deletions

View file

@ -1,60 +1,140 @@
using System.IO; using System.IO;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.Android 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 struct GbpBuffer
{ {
public int Magic { get; private set; } public GraphicBufferHeader Header { get; private set; }
public int Width { get; private set; } public NvGraphicBuffer Buffer { 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 Size => Marshal.SizeOf<NvGraphicBuffer>() + Marshal.SizeOf<GraphicBufferHeader>();
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) public GbpBuffer(BinaryReader reader)
{ {
Magic = reader.ReadInt32(); Header = NvFlinger.ReadStruct<GraphicBufferHeader>(reader);
Width = reader.ReadInt32();
Height = reader.ReadInt32();
Stride = reader.ReadInt32();
Format = reader.ReadInt32();
Usage = reader.ReadInt32();
Pid = reader.ReadInt32(); // ignore fds
RefCount = reader.ReadInt32(); // TODO: check if that is used in official implementation
reader.BaseStream.Position += Header.FdsCount * 4;
FdsCount = reader.ReadInt32(); if (Header.IntsCount != 0x51)
IntsCount = reader.ReadInt32(); {
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<NvGraphicBuffer>(reader);
} }
public void Write(BinaryWriter writer) public void Write(BinaryWriter writer)
{ {
writer.Write(Magic); NvFlinger.WriteStruct(writer, Header);
writer.Write(Width); NvFlinger.WriteStruct(writer, Buffer);
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);
} }
} }
} }

View file

@ -7,6 +7,7 @@ using Ryujinx.HLE.HOS.Services.Nv.NvMap;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
@ -30,9 +31,11 @@ namespace Ryujinx.HLE.HOS.Services.Android
[Flags] [Flags]
private enum HalTransform private enum HalTransform
{ {
FlipX = 1 << 0, FlipX = 1,
FlipY = 1 << 1, FlipY = 2,
Rotate90 = 1 << 2 Rotate90 = 4,
Rotate180 = FlipX | FlipY,
Rotate270 = Rotate90 | Rotate180,
} }
private enum BufferState private enum BufferState
@ -43,6 +46,33 @@ namespace Ryujinx.HLE.HOS.Services.Android
Acquired 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 private struct Rect
{ {
public int Top; public int Top;
@ -51,6 +81,37 @@ namespace Ryujinx.HLE.HOS.Services.Android
public int Bottom; 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 private struct BufferEntry
{ {
public BufferState State; public BufferState State;
@ -170,34 +231,14 @@ namespace Ryujinx.HLE.HOS.Services.Android
//TODO: Errors. //TODO: Errors.
int slot = parcelReader.ReadInt32(); int slot = parcelReader.ReadInt32();
int flatternedObjSize = parcelReader.ReadInt32(); long Position = parcelReader.BaseStream.Position;
int fdCount = parcelReader.ReadInt32();
long timestamp = parcelReader.ReadInt64(); QueueBufferObject queueBufferObject = ReadFlattenedObject<QueueBufferObject>(parcelReader);
int isAutoTimestamp = parcelReader.ReadInt32();
int cropTop = parcelReader.ReadInt32(); parcelReader.BaseStream.Position = Position;
int cropLeft = parcelReader.ReadInt32();
int cropRight = parcelReader.ReadInt32();
int cropBottom = parcelReader.ReadInt32();
int scalingMode = parcelReader.ReadInt32(); _bufferQueue[slot].Transform = queueBufferObject.Transform;
int transform = parcelReader.ReadInt32(); _bufferQueue[slot].Crop = queueBufferObject.Crop;
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].State = BufferState.Queued; _bufferQueue[slot].State = BufferState.Queued;
@ -221,6 +262,8 @@ namespace Ryujinx.HLE.HOS.Services.Android
//TODO: Errors. //TODO: Errors.
int slot = parcelReader.ReadInt32(); int slot = parcelReader.ReadInt32();
MultiFence fence = ReadFlattenedObject<MultiFence>(parcelReader);
_bufferQueue[slot].State = BufferState.Free; _bufferQueue[slot].State = BufferState.Free;
_waitBufferFree.Set(); _waitBufferFree.Set();
@ -251,16 +294,65 @@ namespace Ryujinx.HLE.HOS.Services.Android
if (hasInput) if (hasInput)
{ {
long bufferSize = parcelReader.ReadInt64(); byte[] graphicBuffer = ReadFlattenedObject(parcelReader);
_bufferQueue[slot].State = BufferState.Free; _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); return MakeReplyParcel(context, 0);
} }
private byte[] ReadFlattenedObject(BinaryReader reader)
{
long flattenedObjectSize = reader.ReadInt64();
return reader.ReadBytes((int)flattenedObjectSize);
}
private unsafe T ReadFlattenedObject<T>(BinaryReader reader) where T: struct
{
byte[] data = ReadFlattenedObject(reader);
fixed (byte* ptr = data)
{
return Marshal.PtrToStructure<T>((IntPtr)ptr);
}
}
// FIXME: move this (extension?)
public unsafe static T ReadStruct<T>(BinaryReader reader) where T : struct
{
int size = Marshal.SizeOf<T>();
byte[] data = reader.ReadBytes(size);
fixed (byte* ptr = data)
{
return Marshal.PtrToStructure<T>((IntPtr)ptr);
}
}
// FIXME: move this (extension?)
public unsafe static void WriteStruct<T>(BinaryWriter writer, T value) where T : struct
{
long size = Marshal.SizeOf<T>();
byte[] data = new byte[size];
fixed (byte* ptr = data)
{
Marshal.StructureToPtr<T>(value, (IntPtr)ptr, false);
}
writer.Write(data);
}
private long MakeReplyParcel(ServiceCtx context, params int[] ints) private long MakeReplyParcel(ServiceCtx context, params int[] ints)
{ {
using (MemoryStream ms = new MemoryStream()) using (MemoryStream ms = new MemoryStream())
@ -287,18 +379,20 @@ namespace Ryujinx.HLE.HOS.Services.Android
return 0; return 0;
} }
// TODO: support multi surface
private void SendFrameBuffer(ServiceCtx context, int slot) private void SendFrameBuffer(ServiceCtx context, int slot)
{ {
int fbWidth = _bufferQueue[slot].Data.Width; int fbWidth = _bufferQueue[slot].Data.Header.Width;
int fbHeight = _bufferQueue[slot].Data.Height; 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) 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); NvMapHandle map = NvMapIoctl.GetNvMap(context, nvMapHandle);
@ -386,8 +480,8 @@ namespace Ryujinx.HLE.HOS.Services.Android
GbpBuffer data = _bufferQueue[slot].Data; GbpBuffer data = _bufferQueue[slot].Data;
if (data.Width == width && if (data.Header.Width == width &&
data.Height == height) data.Header.Height == height)
{ {
_bufferQueue[slot].State = BufferState.Dequeued; _bufferQueue[slot].State = BufferState.Dequeued;