NvFlinger.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  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 long 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 long 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 long 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 long 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 long 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 long GbpDetachBuffer(ServiceCtx context, BinaryReader parcelReader)
  190. {
  191. return MakeReplyParcel(context, 0);
  192. }
  193. private long 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 long GbpQuery(ServiceCtx context, BinaryReader parcelReader)
  203. {
  204. return MakeReplyParcel(context, 0, 0);
  205. }
  206. private long GbpConnect(ServiceCtx context, BinaryReader parcelReader)
  207. {
  208. return MakeReplyParcel(context, 1280, 720, 0, 0, 0);
  209. }
  210. private long GbpDisconnect(ServiceCtx context, BinaryReader parcelReader)
  211. {
  212. return MakeReplyParcel(context, 0);
  213. }
  214. private long 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 long 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 long 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 0;
  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, BlockHeight,
  311. GalMemoryLayout.BlockLinear,
  312. imageFormat);
  313. }
  314. context.Device.Gpu.ResourceManager.ClearPbCache();
  315. context.Device.Gpu.ResourceManager.SendTexture(vmm, fbAddr, image);
  316. _renderer.RenderTarget.SetTransform(flipX, flipY, top, left, right, bottom);
  317. _renderer.RenderTarget.Present(fbAddr);
  318. ReleaseBuffer(slot);
  319. });
  320. }
  321. private void ReleaseBuffer(int slot)
  322. {
  323. _bufferQueue[slot].State = BufferState.Free;
  324. _binderEvent.ReadableEvent.Signal();
  325. _waitBufferFree.Set();
  326. }
  327. private int GetFreeSlotBlocking(int width, int height)
  328. {
  329. int slot;
  330. do
  331. {
  332. if ((slot = GetFreeSlot(width, height)) != -1)
  333. {
  334. break;
  335. }
  336. if (_disposed)
  337. {
  338. break;
  339. }
  340. _waitBufferFree.WaitOne();
  341. }
  342. while (!_disposed);
  343. return slot;
  344. }
  345. private int GetFreeSlot(int width, int height)
  346. {
  347. lock (_bufferQueue)
  348. {
  349. for (int slot = 0; slot < _bufferQueue.Length; slot++)
  350. {
  351. if (_bufferQueue[slot].State != BufferState.Free)
  352. {
  353. continue;
  354. }
  355. GbpBuffer data = _bufferQueue[slot].Data;
  356. if (data.Header.Width == width &&
  357. data.Header.Height == height)
  358. {
  359. _bufferQueue[slot].State = BufferState.Dequeued;
  360. return slot;
  361. }
  362. }
  363. }
  364. return -1;
  365. }
  366. public void Dispose()
  367. {
  368. Dispose(true);
  369. }
  370. protected virtual void Dispose(bool disposing)
  371. {
  372. if (disposing && !_disposed)
  373. {
  374. _disposed = true;
  375. _waitBufferFree.Set();
  376. _waitBufferFree.Dispose();
  377. }
  378. }
  379. }
  380. }