SurfaceFlinger.cs 13 KB

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