NvFlinger.cs 15 KB

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