SurfaceFlinger.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  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 long _ticks;
  22. private long _ticksPerFrame;
  23. private int _swapInterval;
  24. private readonly object Lock = new object();
  25. public long LastId { get; private set; }
  26. private class Layer
  27. {
  28. public int ProducerBinderId;
  29. public IGraphicBufferProducer Producer;
  30. public BufferItemConsumer Consumer;
  31. public BufferQueueCore Core;
  32. public long 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(long pid, long layerId)
  67. {
  68. bool needCreate;
  69. lock (Lock)
  70. {
  71. needCreate = GetLayerByIdLocked(layerId) == null;
  72. }
  73. if (needCreate)
  74. {
  75. CreateLayerFromId(pid, layerId);
  76. }
  77. return GetProducerByLayerId(layerId);
  78. }
  79. public IGraphicBufferProducer CreateLayer(long pid, 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(pid, layerId);
  93. return GetProducerByLayerId(layerId);
  94. }
  95. private void CreateLayerFromId(long pid, long layerId)
  96. {
  97. lock (Lock)
  98. {
  99. Logger.Info?.Print(LogClass.SurfaceFlinger, $"Creating layer {layerId}");
  100. BufferQueueCore core = BufferQueue.CreateBufferQueue(_device, pid, 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. Core = core,
  107. Owner = pid
  108. });
  109. LastId = layerId;
  110. }
  111. }
  112. public bool CloseLayer(long layerId)
  113. {
  114. lock (Lock)
  115. {
  116. Layer layer = GetLayerByIdLocked(layerId);
  117. if (layer != null)
  118. {
  119. HOSBinderDriverServer.UnregisterBinderObject(layer.ProducerBinderId);
  120. }
  121. return _layers.Remove(layerId);
  122. }
  123. }
  124. private Layer GetLayerByIdLocked(long layerId)
  125. {
  126. foreach (KeyValuePair<long, Layer> pair in _layers)
  127. {
  128. if (pair.Key == layerId)
  129. {
  130. return pair.Value;
  131. }
  132. }
  133. return null;
  134. }
  135. public IGraphicBufferProducer GetProducerByLayerId(long layerId)
  136. {
  137. lock (Lock)
  138. {
  139. Layer layer = GetLayerByIdLocked(layerId);
  140. if (layer != null)
  141. {
  142. return layer.Producer;
  143. }
  144. }
  145. return null;
  146. }
  147. private void HandleComposition()
  148. {
  149. _isRunning = true;
  150. while (_isRunning)
  151. {
  152. _ticks += _chrono.ElapsedTicks;
  153. _chrono.Restart();
  154. if (_ticks >= _ticksPerFrame)
  155. {
  156. Compose();
  157. _device.System?.SignalVsync();
  158. _ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame);
  159. }
  160. // Sleep the minimal amount of time to avoid being too expensive.
  161. Thread.Sleep(1);
  162. }
  163. }
  164. public void Compose()
  165. {
  166. lock (Lock)
  167. {
  168. // TODO: support multilayers (& multidisplay ?)
  169. if (_layers.Count == 0)
  170. {
  171. return;
  172. }
  173. Layer layer = GetLayerByIdLocked(LastId);
  174. Status acquireStatus = layer.Consumer.AcquireBuffer(out BufferItem item, 0);
  175. if (acquireStatus == Status.Success)
  176. {
  177. // If device vsync is disabled, reflect the change.
  178. if (!_device.EnableDeviceVsync)
  179. {
  180. if (_swapInterval != 0)
  181. {
  182. UpdateSwapInterval(0);
  183. }
  184. }
  185. else if (item.SwapInterval != _swapInterval)
  186. {
  187. UpdateSwapInterval(item.SwapInterval);
  188. }
  189. PostFrameBuffer(layer, item);
  190. }
  191. else if (acquireStatus != Status.NoBufferAvailaible && acquireStatus != Status.InvalidOperation)
  192. {
  193. throw new InvalidOperationException();
  194. }
  195. }
  196. }
  197. private void PostFrameBuffer(Layer layer, BufferItem item)
  198. {
  199. int frameBufferWidth = item.GraphicBuffer.Object.Width;
  200. int frameBufferHeight = item.GraphicBuffer.Object.Height;
  201. int nvMapHandle = item.GraphicBuffer.Object.Buffer.Surfaces[0].NvMapHandle;
  202. if (nvMapHandle == 0)
  203. {
  204. nvMapHandle = item.GraphicBuffer.Object.Buffer.NvMapId;
  205. }
  206. int bufferOffset = item.GraphicBuffer.Object.Buffer.Surfaces[0].Offset;
  207. NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(layer.Owner, nvMapHandle);
  208. ulong frameBufferAddress = (ulong)(map.Address + bufferOffset);
  209. Format format = ConvertColorFormat(item.GraphicBuffer.Object.Buffer.Surfaces[0].ColorFormat);
  210. int bytesPerPixel =
  211. format == Format.B5G6R5Unorm ||
  212. format == Format.R4G4B4A4Unorm ? 2 : 4;
  213. int gobBlocksInY = 1 << item.GraphicBuffer.Object.Buffer.Surfaces[0].BlockHeightLog2;
  214. // Note: Rotation is being ignored.
  215. Rect cropRect = item.Crop;
  216. bool flipX = item.Transform.HasFlag(NativeWindowTransform.FlipX);
  217. bool flipY = item.Transform.HasFlag(NativeWindowTransform.FlipY);
  218. AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value;
  219. bool isStretched = aspectRatio == AspectRatio.Stretched;
  220. ImageCrop crop = new ImageCrop(
  221. cropRect.Left,
  222. cropRect.Right,
  223. cropRect.Top,
  224. cropRect.Bottom,
  225. flipX,
  226. flipY,
  227. isStretched,
  228. aspectRatio.ToFloatX(),
  229. aspectRatio.ToFloatY());
  230. TextureCallbackInformation textureCallbackInformation = new TextureCallbackInformation
  231. {
  232. Layer = layer,
  233. Item = item,
  234. };
  235. _device.Gpu.Window.EnqueueFrameThreadSafe(
  236. frameBufferAddress,
  237. frameBufferWidth,
  238. frameBufferHeight,
  239. 0,
  240. false,
  241. gobBlocksInY,
  242. format,
  243. bytesPerPixel,
  244. crop,
  245. AcquireBuffer,
  246. ReleaseBuffer,
  247. textureCallbackInformation);
  248. }
  249. private void ReleaseBuffer(object obj)
  250. {
  251. ReleaseBuffer((TextureCallbackInformation)obj);
  252. }
  253. private void ReleaseBuffer(TextureCallbackInformation information)
  254. {
  255. AndroidFence fence = AndroidFence.NoFence;
  256. information.Layer.Consumer.ReleaseBuffer(information.Item, ref fence);
  257. }
  258. private void AcquireBuffer(GpuContext ignored, object obj)
  259. {
  260. AcquireBuffer((TextureCallbackInformation)obj);
  261. }
  262. private void AcquireBuffer(TextureCallbackInformation information)
  263. {
  264. information.Item.Fence.WaitForever(_device.Gpu);
  265. }
  266. public static Format ConvertColorFormat(ColorFormat colorFormat)
  267. {
  268. return colorFormat switch
  269. {
  270. ColorFormat.A8B8G8R8 => Format.R8G8B8A8Unorm,
  271. ColorFormat.X8B8G8R8 => Format.R8G8B8A8Unorm,
  272. ColorFormat.R5G6B5 => Format.B5G6R5Unorm,
  273. ColorFormat.A8R8G8B8 => Format.B8G8R8A8Unorm,
  274. ColorFormat.A4B4G4R4 => Format.R4G4B4A4Unorm,
  275. _ => throw new NotImplementedException($"Color Format \"{colorFormat}\" not implemented!"),
  276. };
  277. }
  278. public void Dispose()
  279. {
  280. _isRunning = false;
  281. foreach (Layer layer in _layers.Values)
  282. {
  283. layer.Core.PrepareForExit();
  284. }
  285. }
  286. public void OnFrameAvailable(ref BufferItem item)
  287. {
  288. _device.Statistics.RecordGameFrameTime();
  289. }
  290. public void OnFrameReplaced(ref BufferItem item)
  291. {
  292. _device.Statistics.RecordGameFrameTime();
  293. }
  294. public void OnBuffersReleased() {}
  295. }
  296. }