SurfaceFlinger.cs 14 KB

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