Improve code reaadability in NvFlinger parsing
This commit is contained in:
parent
e016da0402
commit
f647647c43
2 changed files with 251 additions and 77 deletions
|
@ -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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue