SurfaceFlinger.cs 11 KB

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