SurfaceFlinger.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. using Ryujinx.Common.Logging;
  2. using Ryujinx.Graphics.GAL;
  3. using Ryujinx.Graphics.Gpu;
  4. using Ryujinx.HLE.HOS.Kernel.Process;
  5. using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvHostCtrl;
  6. using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
  7. using Ryujinx.HLE.HOS.Services.Nv.Types;
  8. using System;
  9. using System.Collections.Generic;
  10. using System.Diagnostics;
  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 long _ticks;
  23. private long _ticksPerFrame;
  24. private int _swapInterval;
  25. private readonly object Lock = new object();
  26. public long LastId { get; private set; }
  27. private class Layer
  28. {
  29. public int ProducerBinderId;
  30. public IGraphicBufferProducer Producer;
  31. public BufferItemConsumer Consumer;
  32. public KProcess Owner;
  33. }
  34. private class TextureCallbackInformation
  35. {
  36. public Layer Layer;
  37. public BufferItem Item;
  38. }
  39. public SurfaceFlinger(Switch device)
  40. {
  41. _device = device;
  42. _layers = new Dictionary<long, Layer>();
  43. LastId = 0;
  44. _composerThread = new Thread(HandleComposition)
  45. {
  46. Name = "SurfaceFlinger.Composer"
  47. };
  48. _chrono = new Stopwatch();
  49. _ticks = 0;
  50. UpdateSwapInterval(1);
  51. _composerThread.Start();
  52. }
  53. private void UpdateSwapInterval(int swapInterval)
  54. {
  55. _swapInterval = swapInterval;
  56. // If the swap interval is 0, Game VSync is disabled.
  57. if (_swapInterval == 0)
  58. {
  59. _ticksPerFrame = 1;
  60. }
  61. else
  62. {
  63. _ticksPerFrame = Stopwatch.Frequency / (TargetFps / _swapInterval);
  64. }
  65. }
  66. public IGraphicBufferProducer OpenLayer(KProcess process, long layerId)
  67. {
  68. bool needCreate;
  69. lock (Lock)
  70. {
  71. needCreate = GetLayerByIdLocked(layerId) == null;
  72. }
  73. if (needCreate)
  74. {
  75. CreateLayerFromId(process, layerId);
  76. }
  77. return GetProducerByLayerId(layerId);
  78. }
  79. public IGraphicBufferProducer CreateLayer(KProcess process, out long layerId)
  80. {
  81. layerId = 1;
  82. lock (Lock)
  83. {
  84. foreach (KeyValuePair<long, Layer> pair in _layers)
  85. {
  86. if (pair.Key >= layerId)
  87. {
  88. layerId = pair.Key + 1;
  89. }
  90. }
  91. }
  92. CreateLayerFromId(process, layerId);
  93. return GetProducerByLayerId(layerId);
  94. }
  95. private void CreateLayerFromId(KProcess process, long layerId)
  96. {
  97. lock (Lock)
  98. {
  99. Logger.Info?.Print(LogClass.SurfaceFlinger, $"Creating layer {layerId}");
  100. BufferQueue.CreateBufferQueue(_device, process, out BufferQueueProducer producer, out BufferQueueConsumer consumer);
  101. _layers.Add(layerId, new Layer
  102. {
  103. ProducerBinderId = HOSBinderDriverServer.RegisterBinderObject(producer),
  104. Producer = producer,
  105. Consumer = new BufferItemConsumer(_device, consumer, 0, -1, false, this),
  106. Owner = process
  107. });
  108. LastId = layerId;
  109. }
  110. }
  111. public bool CloseLayer(long layerId)
  112. {
  113. lock (Lock)
  114. {
  115. Layer layer = GetLayerByIdLocked(layerId);
  116. if (layer != null)
  117. {
  118. HOSBinderDriverServer.UnregisterBinderObject(layer.ProducerBinderId);
  119. }
  120. return _layers.Remove(layerId);
  121. }
  122. }
  123. private Layer GetLayerByIdLocked(long layerId)
  124. {
  125. foreach (KeyValuePair<long, Layer> pair in _layers)
  126. {
  127. if (pair.Key == layerId)
  128. {
  129. return pair.Value;
  130. }
  131. }
  132. return null;
  133. }
  134. public IGraphicBufferProducer GetProducerByLayerId(long layerId)
  135. {
  136. lock (Lock)
  137. {
  138. Layer layer = GetLayerByIdLocked(layerId);
  139. if (layer != null)
  140. {
  141. return layer.Producer;
  142. }
  143. }
  144. return null;
  145. }
  146. private void HandleComposition()
  147. {
  148. _isRunning = true;
  149. while (_isRunning)
  150. {
  151. _ticks += _chrono.ElapsedTicks;
  152. _chrono.Restart();
  153. if (_ticks >= _ticksPerFrame)
  154. {
  155. Compose();
  156. _device.System?.SignalVsync();
  157. _ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame);
  158. }
  159. // Sleep the minimal amount of time to avoid being too expensive.
  160. Thread.Sleep(1);
  161. }
  162. }
  163. public void Compose()
  164. {
  165. lock (Lock)
  166. {
  167. // TODO: support multilayers (& multidisplay ?)
  168. if (_layers.Count == 0)
  169. {
  170. return;
  171. }
  172. Layer layer = GetLayerByIdLocked(LastId);
  173. Status acquireStatus = layer.Consumer.AcquireBuffer(out BufferItem item, 0);
  174. if (acquireStatus == Status.Success)
  175. {
  176. // If device vsync is disabled, reflect the change.
  177. if (!_device.EnableDeviceVsync)
  178. {
  179. if (_swapInterval != 0)
  180. {
  181. UpdateSwapInterval(0);
  182. }
  183. }
  184. else if (item.SwapInterval != _swapInterval)
  185. {
  186. UpdateSwapInterval(item.SwapInterval);
  187. }
  188. PostFrameBuffer(layer, item);
  189. }
  190. else if (acquireStatus != Status.NoBufferAvailaible && acquireStatus != Status.InvalidOperation)
  191. {
  192. throw new InvalidOperationException();
  193. }
  194. }
  195. }
  196. private void PostFrameBuffer(Layer layer, BufferItem item)
  197. {
  198. int frameBufferWidth = item.GraphicBuffer.Object.Width;
  199. int frameBufferHeight = item.GraphicBuffer.Object.Height;
  200. int nvMapHandle = item.GraphicBuffer.Object.Buffer.Surfaces[0].NvMapHandle;
  201. if (nvMapHandle == 0)
  202. {
  203. nvMapHandle = item.GraphicBuffer.Object.Buffer.NvMapId;
  204. }
  205. int bufferOffset = item.GraphicBuffer.Object.Buffer.Surfaces[0].Offset;
  206. NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(layer.Owner, nvMapHandle);
  207. ulong frameBufferAddress = (ulong)(map.Address + bufferOffset);
  208. Format format = ConvertColorFormat(item.GraphicBuffer.Object.Buffer.Surfaces[0].ColorFormat);
  209. int bytesPerPixel =
  210. format == Format.B5G6R5Unorm ||
  211. format == Format.R4G4B4A4Unorm ? 2 : 4;
  212. int gobBlocksInY = 1 << item.GraphicBuffer.Object.Buffer.Surfaces[0].BlockHeightLog2;
  213. // Note: Rotation is being ignored.
  214. Rect cropRect = item.Crop;
  215. bool flipX = item.Transform.HasFlag(NativeWindowTransform.FlipX);
  216. bool flipY = item.Transform.HasFlag(NativeWindowTransform.FlipY);
  217. ImageCrop crop = new ImageCrop(
  218. cropRect.Left,
  219. cropRect.Right,
  220. cropRect.Top,
  221. cropRect.Bottom,
  222. flipX,
  223. flipY);
  224. TextureCallbackInformation textureCallbackInformation = new TextureCallbackInformation
  225. {
  226. Layer = layer,
  227. Item = item,
  228. };
  229. _device.Gpu.Window.EnqueueFrameThreadSafe(
  230. frameBufferAddress,
  231. frameBufferWidth,
  232. frameBufferHeight,
  233. 0,
  234. false,
  235. gobBlocksInY,
  236. format,
  237. bytesPerPixel,
  238. crop,
  239. AcquireBuffer,
  240. ReleaseBuffer,
  241. textureCallbackInformation);
  242. }
  243. private void ReleaseBuffer(object obj)
  244. {
  245. ReleaseBuffer((TextureCallbackInformation)obj);
  246. }
  247. private void ReleaseBuffer(TextureCallbackInformation information)
  248. {
  249. AndroidFence fence = AndroidFence.NoFence;
  250. information.Layer.Consumer.ReleaseBuffer(information.Item, ref fence);
  251. }
  252. private void AcquireBuffer(GpuContext ignored, object obj)
  253. {
  254. AcquireBuffer((TextureCallbackInformation)obj);
  255. }
  256. private void AcquireBuffer(TextureCallbackInformation information)
  257. {
  258. information.Item.Fence.WaitForever(_device.Gpu);
  259. }
  260. public static Format ConvertColorFormat(ColorFormat colorFormat)
  261. {
  262. return colorFormat switch
  263. {
  264. ColorFormat.A8B8G8R8 => Format.R8G8B8A8Unorm,
  265. ColorFormat.X8B8G8R8 => Format.R8G8B8A8Unorm,
  266. ColorFormat.R5G6B5 => Format.B5G6R5Unorm,
  267. ColorFormat.A8R8G8B8 => Format.B8G8R8A8Unorm,
  268. ColorFormat.A4B4G4R4 => Format.R4G4B4A4Unorm,
  269. _ => throw new NotImplementedException($"Color Format \"{colorFormat}\" not implemented!"),
  270. };
  271. }
  272. public void Dispose()
  273. {
  274. _isRunning = false;
  275. }
  276. public void OnFrameAvailable(ref BufferItem item)
  277. {
  278. _device.Statistics.RecordGameFrameTime();
  279. }
  280. public void OnFrameReplaced(ref BufferItem item)
  281. {
  282. _device.Statistics.RecordGameFrameTime();
  283. }
  284. public void OnBuffersReleased() {}
  285. }
  286. }