NvFlinger.cs 13 KB

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