NvFlinger.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  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.NvDrvServices.NvGpuAS;
  6. using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.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. private BufferEntry[] _bufferQueue;
  25. private AutoResetEvent _waitBufferFree;
  26. private bool _disposed;
  27. public NvFlinger(IGalRenderer renderer, KEvent binderEvent)
  28. {
  29. _commands = new Dictionary<(string, int), ServiceProcessParcel>
  30. {
  31. { ("android.gui.IGraphicBufferProducer", 0x1), GbpRequestBuffer },
  32. { ("android.gui.IGraphicBufferProducer", 0x3), GbpDequeueBuffer },
  33. { ("android.gui.IGraphicBufferProducer", 0x4), GbpDetachBuffer },
  34. { ("android.gui.IGraphicBufferProducer", 0x7), GbpQueueBuffer },
  35. { ("android.gui.IGraphicBufferProducer", 0x8), GbpCancelBuffer },
  36. { ("android.gui.IGraphicBufferProducer", 0x9), GbpQuery },
  37. { ("android.gui.IGraphicBufferProducer", 0xa), GbpConnect },
  38. { ("android.gui.IGraphicBufferProducer", 0xb), GbpDisconnect },
  39. { ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer }
  40. };
  41. _renderer = renderer;
  42. _binderEvent = binderEvent;
  43. _bufferQueue = new BufferEntry[0x40];
  44. _waitBufferFree = new AutoResetEvent(false);
  45. }
  46. public ResultCode ProcessParcelRequest(ServiceCtx context, byte[] parcelData, int code)
  47. {
  48. using (MemoryStream ms = new MemoryStream(parcelData))
  49. {
  50. BinaryReader reader = new BinaryReader(ms);
  51. ms.Seek(4, SeekOrigin.Current);
  52. int strSize = reader.ReadInt32();
  53. string interfaceName = Encoding.Unicode.GetString(reader.ReadBytes(strSize * 2));
  54. long remainder = ms.Position & 0xf;
  55. if (remainder != 0)
  56. {
  57. ms.Seek(0x10 - remainder, SeekOrigin.Current);
  58. }
  59. ms.Seek(0x50, SeekOrigin.Begin);
  60. if (_commands.TryGetValue((interfaceName, code), out ServiceProcessParcel procReq))
  61. {
  62. Logger.PrintDebug(LogClass.ServiceVi, $"{interfaceName} {procReq.Method.Name}");
  63. return procReq(context, reader);
  64. }
  65. else
  66. {
  67. throw new NotImplementedException($"{interfaceName} {code}");
  68. }
  69. }
  70. }
  71. private ResultCode GbpRequestBuffer(ServiceCtx context, BinaryReader parcelReader)
  72. {
  73. int slot = parcelReader.ReadInt32();
  74. using (MemoryStream ms = new MemoryStream())
  75. {
  76. BinaryWriter writer = new BinaryWriter(ms);
  77. BufferEntry entry = _bufferQueue[slot];
  78. int bufferCount = 1; //?
  79. long bufferSize = entry.Data.Size;
  80. writer.Write(bufferCount);
  81. writer.Write(bufferSize);
  82. entry.Data.Write(writer);
  83. writer.Write(0);
  84. return MakeReplyParcel(context, ms.ToArray());
  85. }
  86. }
  87. private ResultCode GbpDequeueBuffer(ServiceCtx context, BinaryReader parcelReader)
  88. {
  89. // TODO: Errors.
  90. int format = parcelReader.ReadInt32();
  91. int width = parcelReader.ReadInt32();
  92. int height = parcelReader.ReadInt32();
  93. int getTimestamps = parcelReader.ReadInt32();
  94. int usage = parcelReader.ReadInt32();
  95. int slot = GetFreeSlotBlocking(width, height);
  96. return MakeReplyParcel(context, slot, 1, 0x24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
  97. }
  98. private ResultCode GbpQueueBuffer(ServiceCtx context, BinaryReader parcelReader)
  99. {
  100. context.Device.Statistics.RecordGameFrameTime();
  101. // TODO: Errors.
  102. int slot = parcelReader.ReadInt32();
  103. long Position = parcelReader.BaseStream.Position;
  104. QueueBufferObject queueBufferObject = ReadFlattenedObject<QueueBufferObject>(parcelReader);
  105. parcelReader.BaseStream.Position = Position;
  106. _bufferQueue[slot].Transform = queueBufferObject.Transform;
  107. _bufferQueue[slot].Crop = queueBufferObject.Crop;
  108. _bufferQueue[slot].State = BufferState.Queued;
  109. SendFrameBuffer(context, slot);
  110. if (context.Device.EnableDeviceVsync)
  111. {
  112. context.Device.VsyncEvent.WaitOne();
  113. }
  114. return MakeReplyParcel(context, 1280, 720, 0, 0, 0);
  115. }
  116. private ResultCode GbpDetachBuffer(ServiceCtx context, BinaryReader parcelReader)
  117. {
  118. return MakeReplyParcel(context, 0);
  119. }
  120. private ResultCode GbpCancelBuffer(ServiceCtx context, BinaryReader parcelReader)
  121. {
  122. // TODO: Errors.
  123. int slot = parcelReader.ReadInt32();
  124. MultiFence fence = ReadFlattenedObject<MultiFence>(parcelReader);
  125. _bufferQueue[slot].State = BufferState.Free;
  126. _waitBufferFree.Set();
  127. return MakeReplyParcel(context, 0);
  128. }
  129. private ResultCode GbpQuery(ServiceCtx context, BinaryReader parcelReader)
  130. {
  131. return MakeReplyParcel(context, 0, 0);
  132. }
  133. private ResultCode GbpConnect(ServiceCtx context, BinaryReader parcelReader)
  134. {
  135. return MakeReplyParcel(context, 1280, 720, 0, 0, 0);
  136. }
  137. private ResultCode GbpDisconnect(ServiceCtx context, BinaryReader parcelReader)
  138. {
  139. return MakeReplyParcel(context, 0);
  140. }
  141. private ResultCode GbpPreallocBuffer(ServiceCtx context, BinaryReader parcelReader)
  142. {
  143. int slot = parcelReader.ReadInt32();
  144. bool hasInput = parcelReader.ReadInt32() == 1;
  145. if (hasInput)
  146. {
  147. byte[] graphicBuffer = ReadFlattenedObject(parcelReader);
  148. _bufferQueue[slot].State = BufferState.Free;
  149. using (BinaryReader graphicBufferReader = new BinaryReader(new MemoryStream(graphicBuffer)))
  150. {
  151. _bufferQueue[slot].Data = new GbpBuffer(graphicBufferReader);
  152. }
  153. }
  154. return MakeReplyParcel(context, 0);
  155. }
  156. private byte[] ReadFlattenedObject(BinaryReader reader)
  157. {
  158. long flattenedObjectSize = reader.ReadInt64();
  159. return reader.ReadBytes((int)flattenedObjectSize);
  160. }
  161. private unsafe T ReadFlattenedObject<T>(BinaryReader reader) where T: struct
  162. {
  163. byte[] data = ReadFlattenedObject(reader);
  164. fixed (byte* ptr = data)
  165. {
  166. return Marshal.PtrToStructure<T>((IntPtr)ptr);
  167. }
  168. }
  169. private ResultCode MakeReplyParcel(ServiceCtx context, params int[] ints)
  170. {
  171. using (MemoryStream ms = new MemoryStream())
  172. {
  173. BinaryWriter writer = new BinaryWriter(ms);
  174. foreach (int Int in ints)
  175. {
  176. writer.Write(Int);
  177. }
  178. return MakeReplyParcel(context, ms.ToArray());
  179. }
  180. }
  181. private ResultCode MakeReplyParcel(ServiceCtx context, byte[] data)
  182. {
  183. (long replyPos, long replySize) = context.Request.GetBufferType0x22();
  184. byte[] reply = MakeParcel(data, new byte[0]);
  185. context.Memory.WriteBytes(replyPos, reply);
  186. return ResultCode.Success;
  187. }
  188. private GalImageFormat ConvertColorFormat(ColorFormat colorFormat)
  189. {
  190. switch (colorFormat)
  191. {
  192. case ColorFormat.A8B8G8R8:
  193. return GalImageFormat.Rgba8 | GalImageFormat.Unorm;
  194. case ColorFormat.X8B8G8R8:
  195. return GalImageFormat.Rgbx8 | GalImageFormat.Unorm;
  196. case ColorFormat.R5G6B5:
  197. return GalImageFormat.Bgr565 | GalImageFormat.Unorm;
  198. case ColorFormat.A8R8G8B8:
  199. return GalImageFormat.Bgra8 | GalImageFormat.Unorm;
  200. case ColorFormat.A4B4G4R4:
  201. return GalImageFormat.Rgba4 | GalImageFormat.Unorm;
  202. default:
  203. throw new NotImplementedException($"Color Format \"{colorFormat}\" not implemented!");
  204. }
  205. }
  206. // TODO: support multi surface
  207. private void SendFrameBuffer(ServiceCtx context, int slot)
  208. {
  209. int fbWidth = _bufferQueue[slot].Data.Header.Width;
  210. int fbHeight = _bufferQueue[slot].Data.Header.Height;
  211. int nvMapHandle = _bufferQueue[slot].Data.Buffer.Surfaces[0].NvMapHandle;
  212. if (nvMapHandle == 0)
  213. {
  214. nvMapHandle = _bufferQueue[slot].Data.Buffer.NvMapId;
  215. }
  216. int bufferOffset = _bufferQueue[slot].Data.Buffer.Surfaces[0].Offset;
  217. NvMapHandle map = NvMapIoctl.GetNvMap(context, nvMapHandle);
  218. long fbAddr = map.Address + bufferOffset;
  219. _bufferQueue[slot].State = BufferState.Acquired;
  220. Rect crop = _bufferQueue[slot].Crop;
  221. bool flipX = _bufferQueue[slot].Transform.HasFlag(HalTransform.FlipX);
  222. bool flipY = _bufferQueue[slot].Transform.HasFlag(HalTransform.FlipY);
  223. GalImageFormat imageFormat = ConvertColorFormat(_bufferQueue[slot].Data.Buffer.Surfaces[0].ColorFormat);
  224. int BlockHeight = 1 << _bufferQueue[slot].Data.Buffer.Surfaces[0].BlockHeightLog2;
  225. // Note: Rotation is being ignored.
  226. int top = crop.Top;
  227. int left = crop.Left;
  228. int right = crop.Right;
  229. int bottom = crop.Bottom;
  230. NvGpuVmm vmm = NvGpuASIoctl.GetASCtx(context).Vmm;
  231. _renderer.QueueAction(() =>
  232. {
  233. if (!_renderer.Texture.TryGetImage(fbAddr, out GalImage image))
  234. {
  235. image = new GalImage(
  236. fbWidth,
  237. fbHeight, 1, 1, 1, BlockHeight, 1,
  238. GalMemoryLayout.BlockLinear,
  239. imageFormat,
  240. GalTextureTarget.TwoD);
  241. }
  242. context.Device.Gpu.ResourceManager.ClearPbCache();
  243. context.Device.Gpu.ResourceManager.SendTexture(vmm, fbAddr, image);
  244. _renderer.RenderTarget.SetTransform(flipX, flipY, top, left, right, bottom);
  245. _renderer.RenderTarget.Present(fbAddr);
  246. ReleaseBuffer(slot);
  247. });
  248. }
  249. private void ReleaseBuffer(int slot)
  250. {
  251. _bufferQueue[slot].State = BufferState.Free;
  252. _binderEvent.ReadableEvent.Signal();
  253. _waitBufferFree.Set();
  254. }
  255. private int GetFreeSlotBlocking(int width, int height)
  256. {
  257. int slot;
  258. do
  259. {
  260. if ((slot = GetFreeSlot(width, height)) != -1)
  261. {
  262. break;
  263. }
  264. if (_disposed)
  265. {
  266. break;
  267. }
  268. _waitBufferFree.WaitOne();
  269. }
  270. while (!_disposed);
  271. return slot;
  272. }
  273. private int GetFreeSlot(int width, int height)
  274. {
  275. lock (_bufferQueue)
  276. {
  277. for (int slot = 0; slot < _bufferQueue.Length; slot++)
  278. {
  279. if (_bufferQueue[slot].State != BufferState.Free)
  280. {
  281. continue;
  282. }
  283. GbpBuffer data = _bufferQueue[slot].Data;
  284. if (data.Header.Width == width &&
  285. data.Header.Height == height)
  286. {
  287. _bufferQueue[slot].State = BufferState.Dequeued;
  288. return slot;
  289. }
  290. }
  291. }
  292. return -1;
  293. }
  294. public void Dispose()
  295. {
  296. Dispose(true);
  297. }
  298. protected virtual void Dispose(bool disposing)
  299. {
  300. if (disposing && !_disposed)
  301. {
  302. _disposed = true;
  303. _waitBufferFree.Set();
  304. _waitBufferFree.Dispose();
  305. }
  306. }
  307. }
  308. }