SurfaceFlinger.cs 17 KB

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