SurfaceFlinger.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. using Ryujinx.Common.Configuration;
  2. using Ryujinx.Common.Logging;
  3. using Ryujinx.Graphics.GAL;
  4. using Ryujinx.Graphics.Gpu;
  5. using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
  6. using System;
  7. using System.Collections.Generic;
  8. using System.Diagnostics;
  9. using System.Linq;
  10. using System.Threading;
  11. namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
  12. {
  13. using ResultCode = Ryujinx.HLE.HOS.Services.Vi.ResultCode;
  14. class SurfaceFlinger : IConsumerListener, IDisposable
  15. {
  16. private const int TargetFps = 60;
  17. private Switch _device;
  18. private Dictionary<long, Layer> _layers;
  19. private bool _isRunning;
  20. private Thread _composerThread;
  21. private Stopwatch _chrono;
  22. private ManualResetEvent _event = new ManualResetEvent(false);
  23. private AutoResetEvent _nextFrameEvent = new AutoResetEvent(true);
  24. private long _ticks;
  25. private long _ticksPerFrame;
  26. private long _spinTicks;
  27. private long _1msTicks;
  28. private int _swapInterval;
  29. private readonly object Lock = new object();
  30. public long RenderLayerId { get; private set; }
  31. private class Layer
  32. {
  33. public int ProducerBinderId;
  34. public IGraphicBufferProducer Producer;
  35. public BufferItemConsumer Consumer;
  36. public BufferQueueCore Core;
  37. public ulong Owner;
  38. public LayerState State;
  39. }
  40. private class TextureCallbackInformation
  41. {
  42. public Layer Layer;
  43. public BufferItem Item;
  44. }
  45. public SurfaceFlinger(Switch device)
  46. {
  47. _device = device;
  48. _layers = new Dictionary<long, Layer>();
  49. RenderLayerId = 0;
  50. _composerThread = new Thread(HandleComposition)
  51. {
  52. Name = "SurfaceFlinger.Composer"
  53. };
  54. _chrono = new Stopwatch();
  55. _chrono.Start();
  56. _ticks = 0;
  57. _spinTicks = Stopwatch.Frequency / 500;
  58. _1msTicks = Stopwatch.Frequency / 1000;
  59. UpdateSwapInterval(1);
  60. _composerThread.Start();
  61. }
  62. private void UpdateSwapInterval(int swapInterval)
  63. {
  64. _swapInterval = swapInterval;
  65. // If the swap interval is 0, Game VSync is disabled.
  66. if (_swapInterval == 0)
  67. {
  68. _nextFrameEvent.Set();
  69. _ticksPerFrame = 1;
  70. }
  71. else
  72. {
  73. _ticksPerFrame = Stopwatch.Frequency / (TargetFps / _swapInterval);
  74. }
  75. }
  76. public IGraphicBufferProducer CreateLayer(out long layerId, ulong pid, LayerState initialState = LayerState.ManagedClosed)
  77. {
  78. layerId = 1;
  79. lock (Lock)
  80. {
  81. foreach (KeyValuePair<long, Layer> pair in _layers)
  82. {
  83. if (pair.Key >= layerId)
  84. {
  85. layerId = pair.Key + 1;
  86. }
  87. }
  88. }
  89. CreateLayerFromId(pid, layerId, initialState);
  90. return GetProducerByLayerId(layerId);
  91. }
  92. private void CreateLayerFromId(ulong pid, long layerId, LayerState initialState)
  93. {
  94. lock (Lock)
  95. {
  96. Logger.Info?.Print(LogClass.SurfaceFlinger, $"Creating layer {layerId}");
  97. BufferQueueCore core = BufferQueue.CreateBufferQueue(_device, pid, out BufferQueueProducer producer, out BufferQueueConsumer consumer);
  98. core.BufferQueued += () =>
  99. {
  100. _nextFrameEvent.Set();
  101. };
  102. _layers.Add(layerId, new Layer
  103. {
  104. ProducerBinderId = HOSBinderDriverServer.RegisterBinderObject(producer),
  105. Producer = producer,
  106. Consumer = new BufferItemConsumer(_device, consumer, 0, -1, false, this),
  107. Core = core,
  108. Owner = pid,
  109. State = initialState
  110. });
  111. }
  112. }
  113. public ResultCode OpenLayer(ulong pid, long layerId, out IBinder producer)
  114. {
  115. Layer layer = GetLayerByIdLocked(layerId);
  116. if (layer == null || layer.State != LayerState.ManagedClosed)
  117. {
  118. producer = null;
  119. return ResultCode.InvalidArguments;
  120. }
  121. layer.State = LayerState.ManagedOpened;
  122. producer = layer.Producer;
  123. return ResultCode.Success;
  124. }
  125. public ResultCode CloseLayer(long layerId)
  126. {
  127. lock (Lock)
  128. {
  129. Layer layer = GetLayerByIdLocked(layerId);
  130. if (layer == null)
  131. {
  132. Logger.Error?.Print(LogClass.SurfaceFlinger, $"Failed to close layer {layerId}");
  133. return ResultCode.InvalidValue;
  134. }
  135. CloseLayer(layerId, layer);
  136. return ResultCode.Success;
  137. }
  138. }
  139. public ResultCode DestroyManagedLayer(long layerId)
  140. {
  141. lock (Lock)
  142. {
  143. Layer layer = GetLayerByIdLocked(layerId);
  144. if (layer == null)
  145. {
  146. Logger.Error?.Print(LogClass.SurfaceFlinger, $"Failed to destroy managed layer {layerId} (not found)");
  147. return ResultCode.InvalidValue;
  148. }
  149. if (layer.State != LayerState.ManagedClosed && layer.State != LayerState.ManagedOpened)
  150. {
  151. Logger.Error?.Print(LogClass.SurfaceFlinger, $"Failed to destroy managed layer {layerId} (permission denied)");
  152. return ResultCode.PermissionDenied;
  153. }
  154. HOSBinderDriverServer.UnregisterBinderObject(layer.ProducerBinderId);
  155. if (_layers.Remove(layerId) && layer.State == LayerState.ManagedOpened)
  156. {
  157. CloseLayer(layerId, layer);
  158. }
  159. return ResultCode.Success;
  160. }
  161. }
  162. public ResultCode DestroyStrayLayer(long layerId)
  163. {
  164. lock (Lock)
  165. {
  166. Layer layer = GetLayerByIdLocked(layerId);
  167. if (layer == null)
  168. {
  169. Logger.Error?.Print(LogClass.SurfaceFlinger, $"Failed to destroy stray layer {layerId} (not found)");
  170. return ResultCode.InvalidValue;
  171. }
  172. if (layer.State != LayerState.Stray)
  173. {
  174. Logger.Error?.Print(LogClass.SurfaceFlinger, $"Failed to destroy stray layer {layerId} (permission denied)");
  175. return ResultCode.PermissionDenied;
  176. }
  177. HOSBinderDriverServer.UnregisterBinderObject(layer.ProducerBinderId);
  178. if (_layers.Remove(layerId))
  179. {
  180. CloseLayer(layerId, layer);
  181. }
  182. return ResultCode.Success;
  183. }
  184. }
  185. private void CloseLayer(long layerId, Layer layer)
  186. {
  187. // If the layer was removed and the current in use, we need to change the current layer in use.
  188. if (RenderLayerId == layerId)
  189. {
  190. // If no layer is availaible, reset to default value.
  191. if (_layers.Count == 0)
  192. {
  193. SetRenderLayer(0);
  194. }
  195. else
  196. {
  197. SetRenderLayer(_layers.Last().Key);
  198. }
  199. }
  200. if (layer.State == LayerState.ManagedOpened)
  201. {
  202. layer.State = LayerState.ManagedClosed;
  203. }
  204. }
  205. public void SetRenderLayer(long layerId)
  206. {
  207. lock (Lock)
  208. {
  209. RenderLayerId = layerId;
  210. }
  211. }
  212. private Layer GetLayerByIdLocked(long layerId)
  213. {
  214. foreach (KeyValuePair<long, Layer> pair in _layers)
  215. {
  216. if (pair.Key == layerId)
  217. {
  218. return pair.Value;
  219. }
  220. }
  221. return null;
  222. }
  223. public IGraphicBufferProducer GetProducerByLayerId(long layerId)
  224. {
  225. lock (Lock)
  226. {
  227. Layer layer = GetLayerByIdLocked(layerId);
  228. if (layer != null)
  229. {
  230. return layer.Producer;
  231. }
  232. }
  233. return null;
  234. }
  235. private void HandleComposition()
  236. {
  237. _isRunning = true;
  238. long lastTicks = _chrono.ElapsedTicks;
  239. while (_isRunning)
  240. {
  241. long ticks = _chrono.ElapsedTicks;
  242. if (_swapInterval == 0)
  243. {
  244. Compose();
  245. _device.System?.SignalVsync();
  246. _nextFrameEvent.WaitOne(17);
  247. lastTicks = ticks;
  248. }
  249. else
  250. {
  251. _ticks += ticks - lastTicks;
  252. lastTicks = ticks;
  253. if (_ticks >= _ticksPerFrame)
  254. {
  255. Compose();
  256. _device.System?.SignalVsync();
  257. // Apply a maximum bound of 3 frames to the tick remainder, in case some event causes Ryujinx to pause for a long time or messes with the timer.
  258. _ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame * 3);
  259. }
  260. // Sleep if possible. If the time til the next frame is too low, spin wait instead.
  261. long diff = _ticksPerFrame - (_ticks + _chrono.ElapsedTicks - ticks);
  262. if (diff > 0)
  263. {
  264. if (diff < _spinTicks)
  265. {
  266. do
  267. {
  268. // SpinWait is a little more HT/SMT friendly than aggressively updating/checking ticks.
  269. // The value of 5 still gives us quite a bit of precision (~0.0003ms variance at worst) while waiting a reasonable amount of time.
  270. Thread.SpinWait(5);
  271. ticks = _chrono.ElapsedTicks;
  272. _ticks += ticks - lastTicks;
  273. lastTicks = ticks;
  274. } while (_ticks < _ticksPerFrame);
  275. }
  276. else
  277. {
  278. _event.WaitOne((int)(diff / _1msTicks));
  279. }
  280. }
  281. }
  282. }
  283. }
  284. public void Compose()
  285. {
  286. lock (Lock)
  287. {
  288. // TODO: support multilayers (& multidisplay ?)
  289. if (RenderLayerId == 0)
  290. {
  291. return;
  292. }
  293. Layer layer = GetLayerByIdLocked(RenderLayerId);
  294. Status acquireStatus = layer.Consumer.AcquireBuffer(out BufferItem item, 0);
  295. if (acquireStatus == Status.Success)
  296. {
  297. // If device vsync is disabled, reflect the change.
  298. if (!_device.EnableDeviceVsync)
  299. {
  300. if (_swapInterval != 0)
  301. {
  302. UpdateSwapInterval(0);
  303. }
  304. }
  305. else if (item.SwapInterval != _swapInterval)
  306. {
  307. UpdateSwapInterval(item.SwapInterval);
  308. }
  309. PostFrameBuffer(layer, item);
  310. }
  311. else if (acquireStatus != Status.NoBufferAvailaible && acquireStatus != Status.InvalidOperation)
  312. {
  313. throw new InvalidOperationException();
  314. }
  315. }
  316. }
  317. private void PostFrameBuffer(Layer layer, BufferItem item)
  318. {
  319. int frameBufferWidth = item.GraphicBuffer.Object.Width;
  320. int frameBufferHeight = item.GraphicBuffer.Object.Height;
  321. int nvMapHandle = item.GraphicBuffer.Object.Buffer.Surfaces[0].NvMapHandle;
  322. if (nvMapHandle == 0)
  323. {
  324. nvMapHandle = item.GraphicBuffer.Object.Buffer.NvMapId;
  325. }
  326. ulong bufferOffset = (ulong)item.GraphicBuffer.Object.Buffer.Surfaces[0].Offset;
  327. NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(layer.Owner, nvMapHandle);
  328. ulong frameBufferAddress = map.Address + bufferOffset;
  329. Format format = ConvertColorFormat(item.GraphicBuffer.Object.Buffer.Surfaces[0].ColorFormat);
  330. int bytesPerPixel =
  331. format == Format.B5G6R5Unorm ||
  332. format == Format.R4G4B4A4Unorm ? 2 : 4;
  333. int gobBlocksInY = 1 << item.GraphicBuffer.Object.Buffer.Surfaces[0].BlockHeightLog2;
  334. // Note: Rotation is being ignored.
  335. Rect cropRect = item.Crop;
  336. bool flipX = item.Transform.HasFlag(NativeWindowTransform.FlipX);
  337. bool flipY = item.Transform.HasFlag(NativeWindowTransform.FlipY);
  338. AspectRatio aspectRatio = _device.Configuration.AspectRatio;
  339. bool isStretched = aspectRatio == AspectRatio.Stretched;
  340. ImageCrop crop = new ImageCrop(
  341. cropRect.Left,
  342. cropRect.Right,
  343. cropRect.Top,
  344. cropRect.Bottom,
  345. flipX,
  346. flipY,
  347. isStretched,
  348. aspectRatio.ToFloatX(),
  349. aspectRatio.ToFloatY());
  350. TextureCallbackInformation textureCallbackInformation = new TextureCallbackInformation
  351. {
  352. Layer = layer,
  353. Item = item
  354. };
  355. _device.Gpu.Window.EnqueueFrameThreadSafe(
  356. layer.Owner,
  357. frameBufferAddress,
  358. frameBufferWidth,
  359. frameBufferHeight,
  360. 0,
  361. false,
  362. gobBlocksInY,
  363. format,
  364. bytesPerPixel,
  365. crop,
  366. AcquireBuffer,
  367. ReleaseBuffer,
  368. textureCallbackInformation);
  369. if (item.Fence.FenceCount == 0)
  370. {
  371. _device.Gpu.Window.SignalFrameReady();
  372. _device.Gpu.GPFifo.Interrupt();
  373. }
  374. else
  375. {
  376. item.Fence.RegisterCallback(_device.Gpu, (x) =>
  377. {
  378. _device.Gpu.Window.SignalFrameReady();
  379. _device.Gpu.GPFifo.Interrupt();
  380. });
  381. }
  382. }
  383. private void ReleaseBuffer(object obj)
  384. {
  385. ReleaseBuffer((TextureCallbackInformation)obj);
  386. }
  387. private void ReleaseBuffer(TextureCallbackInformation information)
  388. {
  389. AndroidFence fence = AndroidFence.NoFence;
  390. information.Layer.Consumer.ReleaseBuffer(information.Item, ref fence);
  391. }
  392. private void AcquireBuffer(GpuContext ignored, object obj)
  393. {
  394. AcquireBuffer((TextureCallbackInformation)obj);
  395. }
  396. private void AcquireBuffer(TextureCallbackInformation information)
  397. {
  398. information.Item.Fence.WaitForever(_device.Gpu);
  399. }
  400. public static Format ConvertColorFormat(ColorFormat colorFormat)
  401. {
  402. return colorFormat switch
  403. {
  404. ColorFormat.A8B8G8R8 => Format.R8G8B8A8Unorm,
  405. ColorFormat.X8B8G8R8 => Format.R8G8B8A8Unorm,
  406. ColorFormat.R5G6B5 => Format.B5G6R5Unorm,
  407. ColorFormat.A8R8G8B8 => Format.B8G8R8A8Unorm,
  408. ColorFormat.A4B4G4R4 => Format.R4G4B4A4Unorm,
  409. _ => throw new NotImplementedException($"Color Format \"{colorFormat}\" not implemented!"),
  410. };
  411. }
  412. public void Dispose()
  413. {
  414. _isRunning = false;
  415. foreach (Layer layer in _layers.Values)
  416. {
  417. layer.Core.PrepareForExit();
  418. }
  419. }
  420. public void OnFrameAvailable(ref BufferItem item)
  421. {
  422. _device.Statistics.RecordGameFrameTime();
  423. }
  424. public void OnFrameReplaced(ref BufferItem item)
  425. {
  426. _device.Statistics.RecordGameFrameTime();
  427. }
  428. public void OnBuffersReleased() {}
  429. }
  430. }