From 378259a40a938cad6a53b7d2910bb1d2d0fdb58a Mon Sep 17 00:00:00 2001 From: Thog Date: Fri, 15 May 2020 03:30:08 +0200 Subject: [PATCH] Surface Flinger: Implement GetBufferHistory (#1232) * Surface Flinger: Implement GetBufferHistory Also fix some bugs on the Surface Flinger implementation * Address Ac_K's comment --- ARMeilleure/State/ExecutionContext.cs | 2 + .../SurfaceFlinger/BufferQueueConsumer.cs | 46 ++++++++++++++++ .../SurfaceFlinger/BufferQueueCore.cs | 24 ++++++++- .../SurfaceFlinger/BufferQueueProducer.cs | 52 ++++++++++++++++++- .../HOS/Services/SurfaceFlinger/BufferSlot.cs | 9 +++- .../SurfaceFlinger/IGraphicBufferProducer.cs | 14 +++++ .../SurfaceFlinger/IHOSBinderDriver.cs | 2 +- .../HOS/Services/SurfaceFlinger/Parcel.cs | 5 ++ .../Services/SurfaceFlinger/SurfaceFlinger.cs | 2 +- .../SurfaceFlinger/Types/BufferInfo.cs | 14 +++++ .../SurfaceFlinger/{ => Types}/BufferItem.cs | 0 .../Services/Time/Clock/Types/TimeSpanType.cs | 5 ++ 12 files changed, 167 insertions(+), 8 deletions(-) create mode 100644 Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferInfo.cs rename Ryujinx.HLE/HOS/Services/SurfaceFlinger/{ => Types}/BufferItem.cs (100%) diff --git a/ARMeilleure/State/ExecutionContext.cs b/ARMeilleure/State/ExecutionContext.cs index 866eafbc..8593f41e 100644 --- a/ARMeilleure/State/ExecutionContext.cs +++ b/ARMeilleure/State/ExecutionContext.cs @@ -32,6 +32,8 @@ namespace ARMeilleure.State } } + public static TimeSpan ElapsedTime => _tickCounter.Elapsed; + public long TpidrEl0 { get; set; } public long Tpidr { get; set; } diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueConsumer.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueConsumer.cs index 54ba5670..fcbbf5f3 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueConsumer.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueConsumer.cs @@ -1,5 +1,6 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types; +using Ryujinx.HLE.HOS.Services.Time.Clock; using System; namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger @@ -57,6 +58,18 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger Core.Slots[bufferItem.Slot].NeedsCleanupOnRelease = true; Core.Slots[bufferItem.Slot].BufferState = BufferState.Acquired; Core.Slots[bufferItem.Slot].Fence = AndroidFence.NoFence; + + ulong targetFrameNumber = Core.Slots[bufferItem.Slot].FrameNumber; + + for (int i = 0; i < Core.BufferHistory.Length; i++) + { + if (Core.BufferHistory[i].FrameNumber == targetFrameNumber) + { + Core.BufferHistory[i].State = BufferState.Acquired; + + break; + } + } } if (bufferItem.AcquireCalled) @@ -368,5 +381,38 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger return Status.Success; } + + public Status SetPresentTime(int slot, ulong frameNumber, TimeSpanType presentationTime) + { + if (slot < 0 || slot >= Core.Slots.Length) + { + return Status.BadValue; + } + + lock (Core.Lock) + { + if (Core.Slots[slot].FrameNumber != frameNumber) + { + return Status.StaleBufferSlot; + } + + if (Core.Slots[slot].PresentationTime.NanoSeconds == 0) + { + Core.Slots[slot].PresentationTime = presentationTime; + } + + for (int i = 0; i < Core.BufferHistory.Length; i++) + { + if (Core.BufferHistory[i].FrameNumber == frameNumber) + { + Core.BufferHistory[i].PresentationTime = presentationTime; + + break; + } + } + } + + return Status.Success; + } } } diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs index efc25239..70d72c5a 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs @@ -1,6 +1,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types; using System; using System.Collections.Generic; using System.Threading; @@ -29,6 +30,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public bool ConsumerControlledByApp; public uint ConsumerUsageBits; public List Queue; + public BufferInfo[] BufferHistory; + public uint BufferHistoryPosition; + public bool EnableExternalEvent; public readonly object Lock = new object(); @@ -37,6 +41,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public KProcess Owner { get; } + public const int BufferHistoryArraySize = 8; + public BufferQueueCore(Switch device, KProcess process) { Slots = new BufferSlotArray(); @@ -64,6 +70,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger _frameAvailableEvent = new KEvent(device.System.KernelContext); Owner = process; + + BufferHistory = new BufferInfo[BufferHistoryArraySize]; + EnableExternalEvent = true; } public int GetMinUndequeuedBufferCountLocked(bool async) @@ -129,12 +138,18 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public void SignalWaitBufferFreeEvent() { - _waitBufferFreeEvent.WritableEvent.Signal(); + if (EnableExternalEvent) + { + _waitBufferFreeEvent.WritableEvent.Signal(); + } } public void SignalFrameAvailableEvent() { - _frameAvailableEvent.WritableEvent.Signal(); + if (EnableExternalEvent) + { + _frameAvailableEvent.WritableEvent.Signal(); + } } // TODO: Find an accurate way to handle a regular condvar here as this will wake up unwanted threads in some edge cases. @@ -201,6 +216,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public void CheckSystemEventsLocked(int maxBufferCount) { + if (!EnableExternalEvent) + { + return; + } + bool needBufferReleaseSignal = false; bool needFrameAvailableSignal = false; diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs index 333bffd1..fb5d2194 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs @@ -1,7 +1,7 @@ using Ryujinx.Common.Logging; using Ryujinx.HLE.HOS.Kernel.Threading; -using Ryujinx.HLE.HOS.Services.Nv.NvDrvServices.NvMap; using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types; +using Ryujinx.HLE.HOS.Services.Time.Clock; using System; using System.Threading; @@ -92,8 +92,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger return Status.BadValue; } + Core.Queue.Clear(); + Core.FreeAllBuffersLocked(); Core.OverrideMaxBufferCount = bufferCount; Core.SignalDequeueEvent(); + Core.SignalWaitBufferFreeEvent(); listener = Core.ConsumerListener; } @@ -363,7 +366,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger Core.Slots[slot].Fence = input.Fence; Core.Slots[slot].BufferState = BufferState.Queued; Core.FrameCounter++; - Core.Slots[slot].FrameNumber = Core.FrameCounter; + Core.Slots[slot].FrameNumber = Core.FrameCounter; + Core.Slots[slot].QueueTime = TimeSpanType.FromTimeSpan(ARMeilleure.State.ExecutionContext.ElapsedTime); + Core.Slots[slot].PresentationTime = TimeSpanType.Zero; item.AcquireCalled = Core.Slots[slot].AcquireCalled; item.Crop = input.Crop; @@ -381,6 +386,15 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger item.GraphicBuffer.Set(Core.Slots[slot].GraphicBuffer); item.GraphicBuffer.Object.IncrementNvMapHandleRefCount(Core.Owner); + Core.BufferHistoryPosition = (Core.BufferHistoryPosition + 1) % BufferQueueCore.BufferHistoryArraySize; + + Core.BufferHistory[Core.BufferHistoryPosition] = new BufferInfo + { + FrameNumber = Core.FrameCounter, + QueueTime = Core.Slots[slot].QueueTime, + State = BufferState.Queued + }; + _stickyTransform = input.StickyTransform; if (Core.Queue.Count == 0) @@ -488,6 +502,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger case NativeWindowAttribute.MinUnqueuedBuffers: outValue = Core.GetMinUndequeuedBufferCountLocked(false); return Status.Success; + case NativeWindowAttribute.ConsumerRunningBehind: + outValue = Core.Queue.Count > 1 ? 1 : 0; + return Status.Success; case NativeWindowAttribute.ConsumerUsageBits: outValue = (int)Core.ConsumerUsageBits; return Status.Success; @@ -561,6 +578,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger case NativeWindowApi.Camera: if (Core.ConnectedApi == api) { + Core.Queue.Clear(); Core.FreeAllBuffersLocked(); producerListener = Core.ProducerListener; @@ -762,5 +780,35 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { return Core.GetWaitBufferFreeEvent(); } + + public override Status GetBufferHistory(int bufferHistoryCount, out Span bufferInfos) + { + if (bufferHistoryCount <= 0) + { + bufferInfos = Span.Empty; + + return Status.BadValue; + } + + lock (Core.Lock) + { + bufferHistoryCount = Math.Min(bufferHistoryCount, Core.BufferHistory.Length); + + BufferInfo[] result = new BufferInfo[bufferHistoryCount]; + + uint position = Core.BufferHistoryPosition; + + for (uint i = 0; i < bufferHistoryCount; i++) + { + result[i] = Core.BufferHistory[(position - i) % Core.BufferHistory.Length]; + + position--; + } + + bufferInfos = result; + + return Status.Success; + } + } } } diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferSlot.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferSlot.cs index dbfba0ee..2f17f8a2 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferSlot.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferSlot.cs @@ -1,4 +1,5 @@ using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types; +using Ryujinx.HLE.HOS.Services.Time.Clock; namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { @@ -12,11 +13,15 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public bool AcquireCalled; public bool NeedsCleanupOnRelease; public bool AttachedByConsumer; + public TimeSpanType QueueTime; + public TimeSpanType PresentationTime; public BufferSlot() { - GraphicBuffer = new AndroidStrongPointer(); - BufferState = BufferState.Free; + GraphicBuffer = new AndroidStrongPointer(); + BufferState = BufferState.Free; + QueueTime = TimeSpanType.Zero; + PresentationTime = TimeSpanType.Zero; } } } diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IGraphicBufferProducer.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IGraphicBufferProducer.cs index 41e21648..3b4996c8 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IGraphicBufferProducer.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IGraphicBufferProducer.cs @@ -236,6 +236,18 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger outputParcel.WriteStatus(status); + break; + case TransactionCode.GetBufferHistory: + int bufferHistoryCount = inputParcel.ReadInt32(); + + status = GetBufferHistory(bufferHistoryCount, out Span bufferInfos); + + outputParcel.WriteStatus(status); + + outputParcel.WriteInt32(bufferInfos.Length); + + outputParcel.WriteUnmanagedSpan(bufferInfos); + break; default: throw new NotImplementedException($"Transaction {(TransactionCode)code} not implemented"); @@ -272,5 +284,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public abstract Status Disconnect(NativeWindowApi api); public abstract Status SetPreallocatedBuffer(int slot, AndroidStrongPointer graphicBuffer); + + public abstract Status GetBufferHistory(int bufferHistoryCount, out Span bufferInfos); } } diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs index 5546418f..8ee943df 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs @@ -74,7 +74,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public ResultCode TransactParcelAuto(ServiceCtx context) { int binderId = context.RequestData.ReadInt32(); - + uint code = context.RequestData.ReadUInt32(); uint flags = context.RequestData.ReadUInt32(); diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs index 3def026b..fbe38c08 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Parcel.cs @@ -173,6 +173,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger public void WriteInt64(long value) => WriteUnmanagedType(ref value); public void WriteUInt64(ulong value) => WriteUnmanagedType(ref value); + public void WriteUnmanagedSpan(ReadOnlySpan value) where T : unmanaged + { + WriteInplace(MemoryMarshal.Cast(value)); + } + public void WriteUnmanagedType(ref T value) where T : unmanaged { WriteInplace(SpanHelpers.AsByteSpan(ref value)); diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs index 784f8c46..e1eb55ad 100644 --- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/SurfaceFlinger.cs @@ -198,7 +198,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { Compose(); - _device.System.SignalVsync(); + _device.System?.SignalVsync(); _ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame); } diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferInfo.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferInfo.cs new file mode 100644 index 00000000..12c41b0d --- /dev/null +++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferInfo.cs @@ -0,0 +1,14 @@ +using Ryujinx.HLE.HOS.Services.Time.Clock; +using System.Runtime.InteropServices; + +namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types +{ + [StructLayout(LayoutKind.Sequential, Size = 0x1C, Pack = 1)] + struct BufferInfo + { + public ulong FrameNumber; + public TimeSpanType QueueTime; + public TimeSpanType PresentationTime; + public BufferState State; + } +} diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferItem.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferItem.cs similarity index 100% rename from Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferItem.cs rename to Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferItem.cs diff --git a/Ryujinx.HLE/HOS/Services/Time/Clock/Types/TimeSpanType.cs b/Ryujinx.HLE/HOS/Services/Time/Clock/Types/TimeSpanType.cs index 89497d82..94c1b488 100644 --- a/Ryujinx.HLE/HOS/Services/Time/Clock/Types/TimeSpanType.cs +++ b/Ryujinx.HLE/HOS/Services/Time/Clock/Types/TimeSpanType.cs @@ -39,6 +39,11 @@ namespace Ryujinx.HLE.HOS.Services.Time.Clock return new TimeSpanType(seconds * NanoSecondsPerSecond); } + public static TimeSpanType FromTimeSpan(TimeSpan timeSpan) + { + return new TimeSpanType((long)(timeSpan.TotalMilliseconds * 1000000)); + } + public static TimeSpanType FromTicks(ulong ticks, ulong frequency) { return FromSeconds((long)ticks / (long)frequency);