NvFlinger.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.Graphics.Gal;
  3. using Ryujinx.Graphics.Memory;
  4. using Ryujinx.HLE.HOS.Kernel.Threading;
  5. using Ryujinx.HLE.HOS.Services.Nv.NvGpuAS;
  6. using Ryujinx.HLE.HOS.Services.Nv.NvMap;
  7. using System;
  8. using System.Collections.Generic;
  9. using System.IO;
  10. using System.Runtime.InteropServices;
  11. using System.Text;
  12. using System.Threading;
  13. using static Ryujinx.HLE.HOS.Services.Android.Parcel;
  14. namespace Ryujinx.HLE.HOS.Services.Android
  15. {
  16. class NvFlinger : IDisposable
  17. {
  18. private delegate ResultCode ServiceProcessParcel(ServiceCtx context, BinaryReader parcelReader);
  19. private Dictionary<(string, int), ServiceProcessParcel> _commands;
  20. private KEvent _binderEvent;
  21. private IGalRenderer _renderer;
  22. private const int BufferQueueCount = 0x40;
  23. private const int BufferQueueMask = BufferQueueCount - 1;
  24. [Flags]
  25. private enum HalTransform
  26. {
  27. FlipX = 1,
  28. FlipY = 2,
  29. Rotate90 = 4,
  30. Rotate180 = FlipX | FlipY,
  31. Rotate270 = Rotate90 | Rotate180,
  32. }
  33. private enum BufferState
  34. {
  35. Free,
  36. Dequeued,
  37. Queued,
  38. Acquired
  39. }
  40. [StructLayout(LayoutKind.Sequential, Size = 0x8)]
  41. private struct Fence
  42. {
  43. public int id;
  44. public int value;
  45. }
  46. [StructLayout(LayoutKind.Explicit, Size = 0x24)]
  47. private struct MultiFence
  48. {
  49. [FieldOffset(0x0)]
  50. public int FenceCount;
  51. [FieldOffset(0x4)]
  52. public Fence Fence0;
  53. [FieldOffset(0xC)]
  54. public Fence Fence1;
  55. [FieldOffset(0x14)]
  56. public Fence Fence2;
  57. [FieldOffset(0x1C)]
  58. public Fence Fence3;
  59. }
  60. [StructLayout(LayoutKind.Sequential, Size = 0x10)]
  61. private struct Rect
  62. {
  63. public int Top;
  64. public int Left;
  65. public int Right;
  66. public int Bottom;
  67. }
  68. [StructLayout(LayoutKind.Explicit)]
  69. private struct QueueBufferObject
  70. {
  71. [FieldOffset(0x0)]
  72. public long Timestamp;
  73. [FieldOffset(0x8)]
  74. public int IsAutoTimestamp;
  75. [FieldOffset(0xC)]
  76. public Rect Crop;
  77. [FieldOffset(0x1C)]
  78. public int ScalingMode;
  79. [FieldOffset(0x20)]
  80. public HalTransform Transform;
  81. [FieldOffset(0x24)]
  82. public int StickyTransform;
  83. [FieldOffset(0x28)]
  84. public int Unknown;
  85. [FieldOffset(0x2C)]
  86. public int SwapInterval;
  87. [FieldOffset(0x30)]
  88. public MultiFence Fence;
  89. }
  90. private struct BufferEntry
  91. {
  92. public BufferState State;
  93. public HalTransform Transform;
  94. public Rect Crop;
  95. public GbpBuffer Data;
  96. }
  97. private BufferEntry[] _bufferQueue;
  98. private AutoResetEvent _waitBufferFree;
  99. private bool _disposed;
  100. public NvFlinger(IGalRenderer renderer, KEvent binderEvent)
  101. {
  102. _commands = new Dictionary<(string, int), ServiceProcessParcel>
  103. {
  104. { ("android.gui.IGraphicBufferProducer", 0x1), GbpRequestBuffer },
  105. { ("android.gui.IGraphicBufferProducer", 0x3), GbpDequeueBuffer },
  106. { ("android.gui.IGraphicBufferProducer", 0x4), GbpDetachBuffer },
  107. { ("android.gui.IGraphicBufferProducer", 0x7), GbpQueueBuffer },
  108. { ("android.gui.IGraphicBufferProducer", 0x8), GbpCancelBuffer },
  109. { ("android.gui.IGraphicBufferProducer", 0x9), GbpQuery },
  110. { ("android.gui.IGraphicBufferProducer", 0xa), GbpConnect },
  111. { ("android.gui.IGraphicBufferProducer", 0xb), GbpDisconnect },
  112. { ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer }
  113. };
  114. _renderer = renderer;
  115. _binderEvent = binderEvent;
  116. _bufferQueue = new BufferEntry[0x40];
  117. _waitBufferFree = new AutoResetEvent(false);
  118. }
  119. public ResultCode ProcessParcelRequest(ServiceCtx context, byte[] parcelData, int code)
  120. {
  121. using (MemoryStream ms = new MemoryStream(parcelData))
  122. {
  123. BinaryReader reader = new BinaryReader(ms);
  124. ms.Seek(4, SeekOrigin.Current);
  125. int strSize = reader.ReadInt32();
  126. string interfaceName = Encoding.Unicode.GetString(reader.ReadBytes(strSize * 2));
  127. long remainder = ms.Position & 0xf;
  128. if (remainder != 0)
  129. {
  130. ms.Seek(0x10 - remainder, SeekOrigin.Current);
  131. }
  132. ms.Seek(0x50, SeekOrigin.Begin);
  133. if (_commands.TryGetValue((interfaceName, code), out ServiceProcessParcel procReq))
  134. {
  135. Logger.PrintDebug(LogClass.ServiceVi, $"{interfaceName} {procReq.Method.Name}");
  136. return procReq(context, reader);
  137. }
  138. else
  139. {
  140. throw new NotImplementedException($"{interfaceName} {code}");
  141. }
  142. }
  143. }
  144. private ResultCode GbpRequestBuffer(ServiceCtx context, BinaryReader parcelReader)
  145. {
  146. int slot = parcelReader.ReadInt32();
  147. using (MemoryStream ms = new MemoryStream())
  148. {
  149. BinaryWriter writer = new BinaryWriter(ms);
  150. BufferEntry entry = _bufferQueue[slot];
  151. int bufferCount = 1; //?
  152. long bufferSize = entry.Data.Size;
  153. writer.Write(bufferCount);
  154. writer.Write(bufferSize);
  155. entry.Data.Write(writer);
  156. writer.Write(0);
  157. return MakeReplyParcel(context, ms.ToArray());
  158. }
  159. }
  160. private ResultCode GbpDequeueBuffer(ServiceCtx context, BinaryReader parcelReader)
  161. {
  162. // TODO: Errors.
  163. int format = parcelReader.ReadInt32();
  164. int width = parcelReader.ReadInt32();
  165. int height = parcelReader.ReadInt32();
  166. int getTimestamps = parcelReader.ReadInt32();
  167. int usage = parcelReader.ReadInt32();
  168. int slot = GetFreeSlotBlocking(width, height);
  169. return MakeReplyParcel(context, slot, 1, 0x24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
  170. }
  171. private ResultCode GbpQueueBuffer(ServiceCtx context, BinaryReader parcelReader)
  172. {
  173. context.Device.Statistics.RecordGameFrameTime();
  174. // TODO: Errors.
  175. int slot = parcelReader.ReadInt32();
  176. long Position = parcelReader.BaseStream.Position;
  177. QueueBufferObject queueBufferObject = ReadFlattenedObject<QueueBufferObject>(parcelReader);
  178. parcelReader.BaseStream.Position = Position;
  179. _bufferQueue[slot].Transform = queueBufferObject.Transform;
  180. _bufferQueue[slot].Crop = queueBufferObject.Crop;
  181. _bufferQueue[slot].State = BufferState.Queued;
  182. SendFrameBuffer(context, slot);
  183. if (context.Device.EnableDeviceVsync)
  184. {
  185. context.Device.VsyncEvent.WaitOne();
  186. }
  187. return MakeReplyParcel(context, 1280, 720, 0, 0, 0);
  188. }
  189. private ResultCode GbpDetachBuffer(ServiceCtx context, BinaryReader parcelReader)
  190. {
  191. return MakeReplyParcel(context, 0);
  192. }
  193. private ResultCode GbpCancelBuffer(ServiceCtx context, BinaryReader parcelReader)
  194. {
  195. // TODO: Errors.
  196. int slot = parcelReader.ReadInt32();
  197. MultiFence fence = ReadFlattenedObject<MultiFence>(parcelReader);
  198. _bufferQueue[slot].State = BufferState.Free;
  199. _waitBufferFree.Set();
  200. return MakeReplyParcel(context, 0);
  201. }
  202. private ResultCode GbpQuery(ServiceCtx context, BinaryReader parcelReader)
  203. {
  204. return MakeReplyParcel(context, 0, 0);
  205. }
  206. private ResultCode GbpConnect(ServiceCtx context, BinaryReader parcelReader)
  207. {
  208. return MakeReplyParcel(context, 1280, 720, 0, 0, 0);
  209. }
  210. private ResultCode GbpDisconnect(ServiceCtx context, BinaryReader parcelReader)
  211. {
  212. return MakeReplyParcel(context, 0);
  213. }
  214. private ResultCode GbpPreallocBuffer(ServiceCtx context, BinaryReader parcelReader)
  215. {
  216. int slot = parcelReader.ReadInt32();
  217. bool hasInput = parcelReader.ReadInt32() == 1;
  218. if (hasInput)
  219. {
  220. byte[] graphicBuffer = ReadFlattenedObject(parcelReader);
  221. _bufferQueue[slot].State = BufferState.Free;
  222. using (BinaryReader graphicBufferReader = new BinaryReader(new MemoryStream(graphicBuffer)))
  223. {
  224. _bufferQueue[slot].Data = new GbpBuffer(graphicBufferReader);
  225. }
  226. }
  227. return MakeReplyParcel(context, 0);
  228. }
  229. private byte[] ReadFlattenedObject(BinaryReader reader)
  230. {
  231. long flattenedObjectSize = reader.ReadInt64();
  232. return reader.ReadBytes((int)flattenedObjectSize);
  233. }
  234. private unsafe T ReadFlattenedObject<T>(BinaryReader reader) where T: struct
  235. {
  236. byte[] data = ReadFlattenedObject(reader);
  237. fixed (byte* ptr = data)
  238. {
  239. return Marshal.PtrToStructure<T>((IntPtr)ptr);
  240. }
  241. }
  242. private ResultCode MakeReplyParcel(ServiceCtx context, params int[] ints)
  243. {
  244. using (MemoryStream ms = new MemoryStream())
  245. {
  246. BinaryWriter writer = new BinaryWriter(ms);
  247. foreach (int Int in ints)
  248. {
  249. writer.Write(Int);
  250. }
  251. return MakeReplyParcel(context, ms.ToArray());
  252. }
  253. }
  254. private ResultCode MakeReplyParcel(ServiceCtx context, byte[] data)
  255. {
  256. (long replyPos, long replySize) = context.Request.GetBufferType0x22();
  257. byte[] reply = MakeParcel(data, new byte[0]);
  258. context.Memory.WriteBytes(replyPos, reply);
  259. return ResultCode.Success;
  260. }
  261. private GalImageFormat ConvertColorFormat(ColorFormat colorFormat)
  262. {
  263. switch (colorFormat)
  264. {
  265. case ColorFormat.A8B8G8R8:
  266. return GalImageFormat.Rgba8 | GalImageFormat.Unorm;
  267. case ColorFormat.X8B8G8R8:
  268. return GalImageFormat.Rgbx8 | GalImageFormat.Unorm;
  269. case ColorFormat.R5G6B5:
  270. return GalImageFormat.Bgr565 | GalImageFormat.Unorm;
  271. case ColorFormat.A8R8G8B8:
  272. return GalImageFormat.Bgra8 | GalImageFormat.Unorm;
  273. case ColorFormat.A4B4G4R4:
  274. return GalImageFormat.Rgba4 | GalImageFormat.Unorm;
  275. default:
  276. throw new NotImplementedException($"Color Format \"{colorFormat}\" not implemented!");
  277. }
  278. }
  279. // TODO: support multi surface
  280. private void SendFrameBuffer(ServiceCtx context, int slot)
  281. {
  282. int fbWidth = _bufferQueue[slot].Data.Header.Width;
  283. int fbHeight = _bufferQueue[slot].Data.Header.Height;
  284. int nvMapHandle = _bufferQueue[slot].Data.Buffer.Surfaces[0].NvMapHandle;
  285. if (nvMapHandle == 0)
  286. {
  287. nvMapHandle = _bufferQueue[slot].Data.Buffer.NvMapId;
  288. }
  289. int bufferOffset = _bufferQueue[slot].Data.Buffer.Surfaces[0].Offset;
  290. NvMapHandle map = NvMapIoctl.GetNvMap(context, nvMapHandle);
  291. long fbAddr = map.Address + bufferOffset;
  292. _bufferQueue[slot].State = BufferState.Acquired;
  293. Rect crop = _bufferQueue[slot].Crop;
  294. bool flipX = _bufferQueue[slot].Transform.HasFlag(HalTransform.FlipX);
  295. bool flipY = _bufferQueue[slot].Transform.HasFlag(HalTransform.FlipY);
  296. GalImageFormat imageFormat = ConvertColorFormat(_bufferQueue[slot].Data.Buffer.Surfaces[0].ColorFormat);
  297. int BlockHeight = 1 << _bufferQueue[slot].Data.Buffer.Surfaces[0].BlockHeightLog2;
  298. // Note: Rotation is being ignored.
  299. int top = crop.Top;
  300. int left = crop.Left;
  301. int right = crop.Right;
  302. int bottom = crop.Bottom;
  303. NvGpuVmm vmm = NvGpuASIoctl.GetASCtx(context).Vmm;
  304. _renderer.QueueAction(() =>
  305. {
  306. if (!_renderer.Texture.TryGetImage(fbAddr, out GalImage image))
  307. {
  308. image = new GalImage(
  309. fbWidth,
  310. fbHeight, 1, 1, 1, BlockHeight, 1,
  311. GalMemoryLayout.BlockLinear,
  312. imageFormat,
  313. GalTextureTarget.TwoD);
  314. }
  315. context.Device.Gpu.ResourceManager.ClearPbCache();
  316. context.Device.Gpu.ResourceManager.SendTexture(vmm, fbAddr, image);
  317. _renderer.RenderTarget.SetTransform(flipX, flipY, top, left, right, bottom);
  318. _renderer.RenderTarget.Present(fbAddr);
  319. ReleaseBuffer(slot);
  320. });
  321. }
  322. private void ReleaseBuffer(int slot)
  323. {
  324. _bufferQueue[slot].State = BufferState.Free;
  325. _binderEvent.ReadableEvent.Signal();
  326. _waitBufferFree.Set();
  327. }
  328. private int GetFreeSlotBlocking(int width, int height)
  329. {
  330. int slot;
  331. do
  332. {
  333. if ((slot = GetFreeSlot(width, height)) != -1)
  334. {
  335. break;
  336. }
  337. if (_disposed)
  338. {
  339. break;
  340. }
  341. _waitBufferFree.WaitOne();
  342. }
  343. while (!_disposed);
  344. return slot;
  345. }
  346. private int GetFreeSlot(int width, int height)
  347. {
  348. lock (_bufferQueue)
  349. {
  350. for (int slot = 0; slot < _bufferQueue.Length; slot++)
  351. {
  352. if (_bufferQueue[slot].State != BufferState.Free)
  353. {
  354. continue;
  355. }
  356. GbpBuffer data = _bufferQueue[slot].Data;
  357. if (data.Header.Width == width &&
  358. data.Header.Height == height)
  359. {
  360. _bufferQueue[slot].State = BufferState.Dequeued;
  361. return slot;
  362. }
  363. }
  364. }
  365. return -1;
  366. }
  367. public void Dispose()
  368. {
  369. Dispose(true);
  370. }
  371. protected virtual void Dispose(bool disposing)
  372. {
  373. if (disposing && !_disposed)
  374. {
  375. _disposed = true;
  376. _waitBufferFree.Set();
  377. _waitBufferFree.Dispose();
  378. }
  379. }
  380. }
  381. }