| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413 |
- using Ryujinx.Common.Logging;
- using Ryujinx.Graphics.Gal;
- using Ryujinx.Graphics.Memory;
- using Ryujinx.HLE.HOS.Kernel.Threading;
- using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvGpuAS;
- using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap;
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Runtime.InteropServices;
- using System.Text;
- using System.Threading;
- using static Ryujinx.HLE.HOS.Services.Android.Parcel;
- namespace Ryujinx.HLE.HOS.Services.Android
- {
- class NvFlinger : IDisposable
- {
- private delegate ResultCode ServiceProcessParcel(ServiceCtx context, BinaryReader parcelReader);
- private Dictionary<(string, int), ServiceProcessParcel> _commands;
- private KEvent _binderEvent;
- private IGalRenderer _renderer;
- private const int BufferQueueCount = 0x40;
- private const int BufferQueueMask = BufferQueueCount - 1;
- private BufferEntry[] _bufferQueue;
- private AutoResetEvent _waitBufferFree;
- private bool _disposed;
- public NvFlinger(IGalRenderer renderer, KEvent binderEvent)
- {
- _commands = new Dictionary<(string, int), ServiceProcessParcel>
- {
- { ("android.gui.IGraphicBufferProducer", 0x1), GbpRequestBuffer },
- { ("android.gui.IGraphicBufferProducer", 0x3), GbpDequeueBuffer },
- { ("android.gui.IGraphicBufferProducer", 0x4), GbpDetachBuffer },
- { ("android.gui.IGraphicBufferProducer", 0x7), GbpQueueBuffer },
- { ("android.gui.IGraphicBufferProducer", 0x8), GbpCancelBuffer },
- { ("android.gui.IGraphicBufferProducer", 0x9), GbpQuery },
- { ("android.gui.IGraphicBufferProducer", 0xa), GbpConnect },
- { ("android.gui.IGraphicBufferProducer", 0xb), GbpDisconnect },
- { ("android.gui.IGraphicBufferProducer", 0xe), GbpPreallocBuffer }
- };
- _renderer = renderer;
- _binderEvent = binderEvent;
- _bufferQueue = new BufferEntry[0x40];
- _waitBufferFree = new AutoResetEvent(false);
- }
- public ResultCode ProcessParcelRequest(ServiceCtx context, byte[] parcelData, int code)
- {
- using (MemoryStream ms = new MemoryStream(parcelData))
- {
- BinaryReader reader = new BinaryReader(ms);
- ms.Seek(4, SeekOrigin.Current);
- int strSize = reader.ReadInt32();
- string interfaceName = Encoding.Unicode.GetString(reader.ReadBytes(strSize * 2));
- long remainder = ms.Position & 0xf;
- if (remainder != 0)
- {
- ms.Seek(0x10 - remainder, SeekOrigin.Current);
- }
- ms.Seek(0x50, SeekOrigin.Begin);
- if (_commands.TryGetValue((interfaceName, code), out ServiceProcessParcel procReq))
- {
- Logger.PrintDebug(LogClass.ServiceVi, $"{interfaceName} {procReq.Method.Name}");
- return procReq(context, reader);
- }
- else
- {
- throw new NotImplementedException($"{interfaceName} {code}");
- }
- }
- }
- private ResultCode GbpRequestBuffer(ServiceCtx context, BinaryReader parcelReader)
- {
- int slot = parcelReader.ReadInt32();
- using (MemoryStream ms = new MemoryStream())
- {
- BinaryWriter writer = new BinaryWriter(ms);
- BufferEntry entry = _bufferQueue[slot];
- int bufferCount = 1; //?
- long bufferSize = entry.Data.Size;
- writer.Write(bufferCount);
- writer.Write(bufferSize);
- entry.Data.Write(writer);
- writer.Write(0);
- return MakeReplyParcel(context, ms.ToArray());
- }
- }
- private ResultCode GbpDequeueBuffer(ServiceCtx context, BinaryReader parcelReader)
- {
- // TODO: Errors.
- int format = parcelReader.ReadInt32();
- int width = parcelReader.ReadInt32();
- int height = parcelReader.ReadInt32();
- int getTimestamps = parcelReader.ReadInt32();
- int usage = parcelReader.ReadInt32();
- int slot = GetFreeSlotBlocking(width, height);
- return MakeReplyParcel(context, slot, 1, 0x24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
- }
- private ResultCode GbpQueueBuffer(ServiceCtx context, BinaryReader parcelReader)
- {
- context.Device.Statistics.RecordGameFrameTime();
- // TODO: Errors.
- int slot = parcelReader.ReadInt32();
- long Position = parcelReader.BaseStream.Position;
- QueueBufferObject queueBufferObject = ReadFlattenedObject<QueueBufferObject>(parcelReader);
- parcelReader.BaseStream.Position = Position;
- _bufferQueue[slot].Transform = queueBufferObject.Transform;
- _bufferQueue[slot].Crop = queueBufferObject.Crop;
- _bufferQueue[slot].State = BufferState.Queued;
- SendFrameBuffer(context, slot);
- if (context.Device.EnableDeviceVsync)
- {
- context.Device.VsyncEvent.WaitOne();
- }
- return MakeReplyParcel(context, 1280, 720, 0, 0, 0);
- }
- private ResultCode GbpDetachBuffer(ServiceCtx context, BinaryReader parcelReader)
- {
- return MakeReplyParcel(context, 0);
- }
- private ResultCode GbpCancelBuffer(ServiceCtx context, BinaryReader parcelReader)
- {
- // TODO: Errors.
- int slot = parcelReader.ReadInt32();
- MultiFence fence = ReadFlattenedObject<MultiFence>(parcelReader);
- _bufferQueue[slot].State = BufferState.Free;
- _waitBufferFree.Set();
- return MakeReplyParcel(context, 0);
- }
- private ResultCode GbpQuery(ServiceCtx context, BinaryReader parcelReader)
- {
- return MakeReplyParcel(context, 0, 0);
- }
- private ResultCode GbpConnect(ServiceCtx context, BinaryReader parcelReader)
- {
- return MakeReplyParcel(context, 1280, 720, 0, 0, 0);
- }
- private ResultCode GbpDisconnect(ServiceCtx context, BinaryReader parcelReader)
- {
- return MakeReplyParcel(context, 0);
- }
- private ResultCode GbpPreallocBuffer(ServiceCtx context, BinaryReader parcelReader)
- {
- int slot = parcelReader.ReadInt32();
- bool hasInput = parcelReader.ReadInt32() == 1;
- if (hasInput)
- {
- byte[] graphicBuffer = ReadFlattenedObject(parcelReader);
- _bufferQueue[slot].State = BufferState.Free;
- using (BinaryReader graphicBufferReader = new BinaryReader(new MemoryStream(graphicBuffer)))
- {
- _bufferQueue[slot].Data = new GbpBuffer(graphicBufferReader);
- }
- }
- return MakeReplyParcel(context, 0);
- }
- private byte[] ReadFlattenedObject(BinaryReader reader)
- {
- long flattenedObjectSize = reader.ReadInt64();
- return reader.ReadBytes((int)flattenedObjectSize);
- }
- private unsafe T ReadFlattenedObject<T>(BinaryReader reader) where T: struct
- {
- byte[] data = ReadFlattenedObject(reader);
- fixed (byte* ptr = data)
- {
- return Marshal.PtrToStructure<T>((IntPtr)ptr);
- }
- }
- private ResultCode MakeReplyParcel(ServiceCtx context, params int[] ints)
- {
- using (MemoryStream ms = new MemoryStream())
- {
- BinaryWriter writer = new BinaryWriter(ms);
- foreach (int Int in ints)
- {
- writer.Write(Int);
- }
- return MakeReplyParcel(context, ms.ToArray());
- }
- }
- private ResultCode MakeReplyParcel(ServiceCtx context, byte[] data)
- {
- (long replyPos, long replySize) = context.Request.GetBufferType0x22();
- byte[] reply = MakeParcel(data, new byte[0]);
- context.Memory.WriteBytes(replyPos, reply);
- return ResultCode.Success;
- }
- private GalImageFormat ConvertColorFormat(ColorFormat colorFormat)
- {
- switch (colorFormat)
- {
- case ColorFormat.A8B8G8R8:
- return GalImageFormat.Rgba8 | GalImageFormat.Unorm;
- case ColorFormat.X8B8G8R8:
- return GalImageFormat.Rgbx8 | GalImageFormat.Unorm;
- case ColorFormat.R5G6B5:
- return GalImageFormat.Bgr565 | GalImageFormat.Unorm;
- case ColorFormat.A8R8G8B8:
- return GalImageFormat.Bgra8 | GalImageFormat.Unorm;
- case ColorFormat.A4B4G4R4:
- return GalImageFormat.Rgba4 | GalImageFormat.Unorm;
- default:
- throw new NotImplementedException($"Color Format \"{colorFormat}\" not implemented!");
- }
- }
- // TODO: support multi surface
- private void SendFrameBuffer(ServiceCtx context, int slot)
- {
- int fbWidth = _bufferQueue[slot].Data.Header.Width;
- int fbHeight = _bufferQueue[slot].Data.Header.Height;
- int nvMapHandle = _bufferQueue[slot].Data.Buffer.Surfaces[0].NvMapHandle;
- if (nvMapHandle == 0)
- {
- nvMapHandle = _bufferQueue[slot].Data.Buffer.NvMapId;
- }
- int bufferOffset = _bufferQueue[slot].Data.Buffer.Surfaces[0].Offset;
- NvMapHandle map = NvMapIoctl.GetNvMap(context, nvMapHandle);
- long fbAddr = map.Address + bufferOffset;
- _bufferQueue[slot].State = BufferState.Acquired;
- Rect crop = _bufferQueue[slot].Crop;
- bool flipX = _bufferQueue[slot].Transform.HasFlag(HalTransform.FlipX);
- bool flipY = _bufferQueue[slot].Transform.HasFlag(HalTransform.FlipY);
- GalImageFormat imageFormat = ConvertColorFormat(_bufferQueue[slot].Data.Buffer.Surfaces[0].ColorFormat);
- int BlockHeight = 1 << _bufferQueue[slot].Data.Buffer.Surfaces[0].BlockHeightLog2;
- // Note: Rotation is being ignored.
- int top = crop.Top;
- int left = crop.Left;
- int right = crop.Right;
- int bottom = crop.Bottom;
- NvGpuVmm vmm = NvGpuASIoctl.GetASCtx(context).Vmm;
- _renderer.QueueAction(() =>
- {
- if (!_renderer.Texture.TryGetImage(fbAddr, out GalImage image))
- {
- image = new GalImage(
- fbWidth,
- fbHeight, 1, 1, 1, BlockHeight, 1,
- GalMemoryLayout.BlockLinear,
- imageFormat,
- GalTextureTarget.TwoD);
- }
- context.Device.Gpu.ResourceManager.ClearPbCache();
- context.Device.Gpu.ResourceManager.SendTexture(vmm, fbAddr, image);
- _renderer.RenderTarget.SetTransform(flipX, flipY, top, left, right, bottom);
- _renderer.RenderTarget.Present(fbAddr);
- ReleaseBuffer(slot);
- });
- }
- private void ReleaseBuffer(int slot)
- {
- _bufferQueue[slot].State = BufferState.Free;
- _binderEvent.ReadableEvent.Signal();
- _waitBufferFree.Set();
- }
- private int GetFreeSlotBlocking(int width, int height)
- {
- int slot;
- do
- {
- if ((slot = GetFreeSlot(width, height)) != -1)
- {
- break;
- }
- if (_disposed)
- {
- break;
- }
- _waitBufferFree.WaitOne();
- }
- while (!_disposed);
- return slot;
- }
- private int GetFreeSlot(int width, int height)
- {
- lock (_bufferQueue)
- {
- for (int slot = 0; slot < _bufferQueue.Length; slot++)
- {
- if (_bufferQueue[slot].State != BufferState.Free)
- {
- continue;
- }
- GbpBuffer data = _bufferQueue[slot].Data;
- if (data.Header.Width == width &&
- data.Header.Height == height)
- {
- _bufferQueue[slot].State = BufferState.Dequeued;
- return slot;
- }
- }
- }
- return -1;
- }
- public void Dispose()
- {
- Dispose(true);
- }
- protected virtual void Dispose(bool disposing)
- {
- if (disposing && !_disposed)
- {
- _disposed = true;
- _waitBufferFree.Set();
- _waitBufferFree.Dispose();
- }
- }
- }
- }
|