NvFlinger.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. using ChocolArm64.Memory;
  2. using Ryujinx.Core.OsHle.Handles;
  3. using Ryujinx.Core.OsHle.Services.Nv;
  4. using Ryujinx.Graphics.Gal;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.IO;
  8. using System.Text;
  9. using System.Threading;
  10. using static Ryujinx.Core.OsHle.Services.Android.Parcel;
  11. namespace Ryujinx.Core.OsHle.Services.Android
  12. {
  13. class NvFlinger : IDisposable
  14. {
  15. private delegate long ServiceProcessParcel(ServiceCtx Context, BinaryReader ParcelReader);
  16. private Dictionary<(string, int), ServiceProcessParcel> Commands;
  17. private KEvent ReleaseEvent;
  18. private IGalRenderer Renderer;
  19. private const int BufferQueueCount = 0x40;
  20. private const int BufferQueueMask = BufferQueueCount - 1;
  21. [Flags]
  22. private enum HalTransform
  23. {
  24. FlipX = 1 << 0,
  25. FlipY = 1 << 1,
  26. Rotate90 = 1 << 2
  27. }
  28. private enum BufferState
  29. {
  30. Free,
  31. Dequeued,
  32. Queued,
  33. Acquired
  34. }
  35. private struct Rect
  36. {
  37. public int Top;
  38. public int Left;
  39. public int Right;
  40. public int Bottom;
  41. }
  42. private struct BufferEntry
  43. {
  44. public BufferState State;
  45. public HalTransform Transform;
  46. public Rect Crop;
  47. public GbpBuffer Data;
  48. }
  49. private BufferEntry[] BufferQueue;
  50. private ManualResetEvent WaitBufferFree;
  51. private object RenderQueueLock;
  52. private int RenderQueueCount;
  53. private bool NvFlingerDisposed;
  54. private bool KeepRunning;
  55. public NvFlinger(IGalRenderer Renderer, KEvent ReleaseEvent)
  56. {
  57. Commands = new Dictionary<(string, int), ServiceProcessParcel>()
  58. {
  59. { ("android.gui.IGraphicBufferProducer", 0x1), GbpRequestBuffer },
  60. { ("android.gui.IGraphicBufferProducer", 0x3), GbpDequeueBuffer },
  61. { ("android.gui.IGraphicBufferProducer", 0x4), GbpDetachBuffer },
  62. { ("android.gui.IGraphicBufferProducer", 0x7), GbpQueueBuffer },
  63. { ("android.gui.IGraphicBufferProducer", 0x8), GbpCancelBuffer },
  64. { ("android.gui.IGraphicBufferProducer", 0x9), GbpQuery },
  65. { ("android.gui.IGraphicBufferProducer", 0xa), GbpConnect },
  66. { ("android.gui.IGraphicBufferProducer", 0xb), GbpDisconnect },
  67. { ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer }
  68. };
  69. this.Renderer = Renderer;
  70. this.ReleaseEvent = ReleaseEvent;
  71. BufferQueue = new BufferEntry[0x40];
  72. WaitBufferFree = new ManualResetEvent(false);
  73. RenderQueueLock = new object();
  74. KeepRunning = true;
  75. }
  76. public long ProcessParcelRequest(ServiceCtx Context, byte[] ParcelData, int Code)
  77. {
  78. using (MemoryStream MS = new MemoryStream(ParcelData))
  79. {
  80. BinaryReader Reader = new BinaryReader(MS);
  81. MS.Seek(4, SeekOrigin.Current);
  82. int StrSize = Reader.ReadInt32();
  83. string InterfaceName = Encoding.Unicode.GetString(Reader.ReadBytes(StrSize * 2));
  84. long Remainder = MS.Position & 0xf;
  85. if (Remainder != 0)
  86. {
  87. MS.Seek(0x10 - Remainder, SeekOrigin.Current);
  88. }
  89. MS.Seek(0x50, SeekOrigin.Begin);
  90. if (Commands.TryGetValue((InterfaceName, Code), out ServiceProcessParcel ProcReq))
  91. {
  92. Logging.Debug($"{InterfaceName} {ProcReq.Method.Name}");
  93. return ProcReq(Context, Reader);
  94. }
  95. else
  96. {
  97. throw new NotImplementedException($"{InterfaceName} {Code}");
  98. }
  99. }
  100. }
  101. private long GbpRequestBuffer(ServiceCtx Context, BinaryReader ParcelReader)
  102. {
  103. int Slot = ParcelReader.ReadInt32();
  104. using (MemoryStream MS = new MemoryStream())
  105. {
  106. BinaryWriter Writer = new BinaryWriter(MS);
  107. BufferEntry Entry = BufferQueue[Slot];
  108. int BufferCount = 1; //?
  109. long BufferSize = Entry.Data.Size;
  110. Writer.Write(BufferCount);
  111. Writer.Write(BufferSize);
  112. Entry.Data.Write(Writer);
  113. Writer.Write(0);
  114. return MakeReplyParcel(Context, MS.ToArray());
  115. }
  116. }
  117. private long GbpDequeueBuffer(ServiceCtx Context, BinaryReader ParcelReader)
  118. {
  119. //TODO: Errors.
  120. int Format = ParcelReader.ReadInt32();
  121. int Width = ParcelReader.ReadInt32();
  122. int Height = ParcelReader.ReadInt32();
  123. int GetTimestamps = ParcelReader.ReadInt32();
  124. int Usage = ParcelReader.ReadInt32();
  125. int Slot = GetFreeSlotBlocking(Width, Height);
  126. return MakeReplyParcel(Context, Slot, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
  127. }
  128. private long GbpQueueBuffer(ServiceCtx Context, BinaryReader ParcelReader)
  129. {
  130. Context.Ns.Statistics.RecordGameFrameTime();
  131. //TODO: Errors.
  132. int Slot = ParcelReader.ReadInt32();
  133. int Unknown4 = ParcelReader.ReadInt32();
  134. int Unknown8 = ParcelReader.ReadInt32();
  135. int Unknownc = ParcelReader.ReadInt32();
  136. int Timestamp = ParcelReader.ReadInt32();
  137. int IsAutoTimestamp = ParcelReader.ReadInt32();
  138. int CropTop = ParcelReader.ReadInt32();
  139. int CropLeft = ParcelReader.ReadInt32();
  140. int CropRight = ParcelReader.ReadInt32();
  141. int CropBottom = ParcelReader.ReadInt32();
  142. int ScalingMode = ParcelReader.ReadInt32();
  143. int Transform = ParcelReader.ReadInt32();
  144. int StickyTransform = ParcelReader.ReadInt32();
  145. int Unknown34 = ParcelReader.ReadInt32();
  146. int Unknown38 = ParcelReader.ReadInt32();
  147. int IsFenceValid = ParcelReader.ReadInt32();
  148. int Fence0Id = ParcelReader.ReadInt32();
  149. int Fence0Value = ParcelReader.ReadInt32();
  150. int Fence1Id = ParcelReader.ReadInt32();
  151. int Fence1Value = ParcelReader.ReadInt32();
  152. BufferQueue[Slot].Transform = (HalTransform)Transform;
  153. BufferQueue[Slot].Crop.Top = CropTop;
  154. BufferQueue[Slot].Crop.Left = CropLeft;
  155. BufferQueue[Slot].Crop.Right = CropRight;
  156. BufferQueue[Slot].Crop.Bottom = CropBottom;
  157. BufferQueue[Slot].State = BufferState.Queued;
  158. SendFrameBuffer(Context, Slot);
  159. return MakeReplyParcel(Context, 1280, 720, 0, 0, 0);
  160. }
  161. private long GbpDetachBuffer(ServiceCtx Context, BinaryReader ParcelReader)
  162. {
  163. return MakeReplyParcel(Context, 0);
  164. }
  165. private long GbpCancelBuffer(ServiceCtx Context, BinaryReader ParcelReader)
  166. {
  167. //TODO: Errors.
  168. int Slot = ParcelReader.ReadInt32();
  169. BufferQueue[Slot].State = BufferState.Free;
  170. return MakeReplyParcel(Context, 0);
  171. }
  172. private long GbpQuery(ServiceCtx Context, BinaryReader ParcelReader)
  173. {
  174. return MakeReplyParcel(Context, 0, 0);
  175. }
  176. private long GbpConnect(ServiceCtx Context, BinaryReader ParcelReader)
  177. {
  178. return MakeReplyParcel(Context, 1280, 720, 0, 0, 0);
  179. }
  180. private long GbpDisconnect(ServiceCtx Context, BinaryReader ParcelReader)
  181. {
  182. return MakeReplyParcel(Context, 0);
  183. }
  184. private long GbpPreallocBuffer(ServiceCtx Context, BinaryReader ParcelReader)
  185. {
  186. int Slot = ParcelReader.ReadInt32();
  187. int BufferCount = ParcelReader.ReadInt32();
  188. long BufferSize = ParcelReader.ReadInt64();
  189. BufferQueue[Slot].State = BufferState.Free;
  190. BufferQueue[Slot].Data = new GbpBuffer(ParcelReader);
  191. return MakeReplyParcel(Context, 0);
  192. }
  193. private long MakeReplyParcel(ServiceCtx Context, params int[] Ints)
  194. {
  195. using (MemoryStream MS = new MemoryStream())
  196. {
  197. BinaryWriter Writer = new BinaryWriter(MS);
  198. foreach (int Int in Ints)
  199. {
  200. Writer.Write(Int);
  201. }
  202. return MakeReplyParcel(Context, MS.ToArray());
  203. }
  204. }
  205. private long MakeReplyParcel(ServiceCtx Context, byte[] Data)
  206. {
  207. long ReplyPos = Context.Request.ReceiveBuff[0].Position;
  208. long ReplySize = Context.Request.ReceiveBuff[0].Size;
  209. byte[] Reply = MakeParcel(Data, new byte[0]);
  210. AMemoryHelper.WriteBytes(Context.Memory, ReplyPos, Reply);
  211. return 0;
  212. }
  213. private unsafe void SendFrameBuffer(ServiceCtx Context, int Slot)
  214. {
  215. int FbWidth = BufferQueue[Slot].Data.Width;
  216. int FbHeight = BufferQueue[Slot].Data.Height;
  217. long FbSize = (uint)FbWidth * FbHeight * 4;
  218. NvMap Map = GetNvMap(Context, Slot);
  219. NvMapFb MapFb = (NvMapFb)ServiceNvDrv.NvMapsFb.GetData(Context.Process, 0);
  220. long Address = Map.CpuAddress;
  221. if (MapFb.HasBufferOffset(Slot))
  222. {
  223. Address += MapFb.GetBufferOffset(Slot);
  224. }
  225. if ((ulong)(Address + FbSize) > AMemoryMgr.AddrSize)
  226. {
  227. Logging.Error($"Frame buffer address {Address:x16} is invalid!");
  228. BufferQueue[Slot].State = BufferState.Free;
  229. ReleaseEvent.Handle.Set();
  230. WaitBufferFree.Set();
  231. return;
  232. }
  233. BufferQueue[Slot].State = BufferState.Acquired;
  234. Rect Crop = BufferQueue[Slot].Crop;
  235. int RealWidth = FbWidth;
  236. int RealHeight = FbHeight;
  237. float XSign = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipX) ? -1 : 1;
  238. float YSign = BufferQueue[Slot].Transform.HasFlag(HalTransform.FlipY) ? -1 : 1;
  239. float ScaleX = 1;
  240. float ScaleY = 1;
  241. float OffsX = 0;
  242. float OffsY = 0;
  243. if (Crop.Right != 0 &&
  244. Crop.Bottom != 0)
  245. {
  246. //Who knows if this is right, I was never good with math...
  247. RealWidth = Crop.Right - Crop.Left;
  248. RealHeight = Crop.Bottom - Crop.Top;
  249. if (BufferQueue[Slot].Transform.HasFlag(HalTransform.Rotate90))
  250. {
  251. ScaleY = (float)FbHeight / RealHeight;
  252. ScaleX = (float)FbWidth / RealWidth;
  253. OffsY = ((-(float)Crop.Left / Crop.Right) + ScaleX - 1) * -XSign;
  254. OffsX = ((-(float)Crop.Top / Crop.Bottom) + ScaleY - 1) * -YSign;
  255. }
  256. else
  257. {
  258. ScaleX = (float)FbWidth / RealWidth;
  259. ScaleY = (float)FbHeight / RealHeight;
  260. OffsX = ((-(float)Crop.Left / Crop.Right) + ScaleX - 1) * XSign;
  261. OffsY = ((-(float)Crop.Top / Crop.Bottom) + ScaleY - 1) * -YSign;
  262. }
  263. }
  264. ScaleX *= XSign;
  265. ScaleY *= YSign;
  266. float Rotate = 0;
  267. if (BufferQueue[Slot].Transform.HasFlag(HalTransform.Rotate90))
  268. {
  269. Rotate = -MathF.PI * 0.5f;
  270. }
  271. lock (RenderQueueLock)
  272. {
  273. if (NvFlingerDisposed)
  274. {
  275. return;
  276. }
  277. Interlocked.Increment(ref RenderQueueCount);
  278. }
  279. byte* Fb = (byte*)Context.Memory.Ram + Address;
  280. Context.Ns.Gpu.Renderer.QueueAction(delegate()
  281. {
  282. Context.Ns.Gpu.Renderer.SetFrameBuffer(
  283. Fb,
  284. FbWidth,
  285. FbHeight,
  286. ScaleX,
  287. ScaleY,
  288. OffsX,
  289. OffsY,
  290. Rotate);
  291. BufferQueue[Slot].State = BufferState.Free;
  292. Interlocked.Decrement(ref RenderQueueCount);
  293. ReleaseEvent.Handle.Set();
  294. lock (WaitBufferFree)
  295. {
  296. WaitBufferFree.Set();
  297. }
  298. });
  299. }
  300. private NvMap GetNvMap(ServiceCtx Context, int Slot)
  301. {
  302. int NvMapHandle = BitConverter.ToInt32(BufferQueue[Slot].Data.RawData, 0x4c);
  303. if (!BitConverter.IsLittleEndian)
  304. {
  305. byte[] RawValue = BitConverter.GetBytes(NvMapHandle);
  306. Array.Reverse(RawValue);
  307. NvMapHandle = BitConverter.ToInt32(RawValue, 0);
  308. }
  309. return ServiceNvDrv.NvMaps.GetData<NvMap>(Context.Process, NvMapHandle);
  310. }
  311. private int GetFreeSlotBlocking(int Width, int Height)
  312. {
  313. int Slot;
  314. do
  315. {
  316. lock (WaitBufferFree)
  317. {
  318. if ((Slot = GetFreeSlot(Width, Height)) != -1)
  319. {
  320. break;
  321. }
  322. Logging.Debug("Waiting for a free BufferQueue slot...");
  323. if (!KeepRunning)
  324. {
  325. break;
  326. }
  327. WaitBufferFree.Reset();
  328. }
  329. WaitBufferFree.WaitOne();
  330. }
  331. while (KeepRunning);
  332. Logging.Debug($"Found free BufferQueue slot {Slot}!");
  333. return Slot;
  334. }
  335. private int GetFreeSlot(int Width, int Height)
  336. {
  337. lock (BufferQueue)
  338. {
  339. for (int Slot = 0; Slot < BufferQueue.Length; Slot++)
  340. {
  341. if (BufferQueue[Slot].State != BufferState.Free)
  342. {
  343. continue;
  344. }
  345. GbpBuffer Data = BufferQueue[Slot].Data;
  346. if (Data.Width == Width &&
  347. Data.Height == Height)
  348. {
  349. BufferQueue[Slot].State = BufferState.Dequeued;
  350. return Slot;
  351. }
  352. }
  353. }
  354. return -1;
  355. }
  356. public void Dispose()
  357. {
  358. Dispose(true);
  359. }
  360. protected virtual void Dispose(bool Disposing)
  361. {
  362. if (Disposing && !NvFlingerDisposed)
  363. {
  364. lock (RenderQueueLock)
  365. {
  366. NvFlingerDisposed = true;
  367. }
  368. //Ensure that all pending actions was sent before
  369. //we can safely assume that the class was disposed.
  370. while (RenderQueueCount > 0)
  371. {
  372. Thread.Yield();
  373. }
  374. Renderer.ResetFrameBuffer();
  375. lock (WaitBufferFree)
  376. {
  377. KeepRunning = false;
  378. WaitBufferFree.Set();
  379. }
  380. WaitBufferFree.Dispose();
  381. }
  382. }
  383. }
  384. }