Surface Flinger: Implement GetBufferHistory (#1232)
* Surface Flinger: Implement GetBufferHistory Also fix some bugs on the Surface Flinger implementation * Address Ac_K's comment
This commit is contained in:
parent
b2e5855928
commit
378259a40a
12 changed files with 167 additions and 8 deletions
|
@ -32,6 +32,8 @@ namespace ARMeilleure.State
|
|||
}
|
||||
}
|
||||
|
||||
public static TimeSpan ElapsedTime => _tickCounter.Elapsed;
|
||||
|
||||
public long TpidrEl0 { get; set; }
|
||||
public long Tpidr { get; set; }
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<BufferItem> 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)
|
||||
|
@ -128,14 +137,20 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||
}
|
||||
|
||||
public void SignalWaitBufferFreeEvent()
|
||||
{
|
||||
if (EnableExternalEvent)
|
||||
{
|
||||
_waitBufferFreeEvent.WritableEvent.Signal();
|
||||
}
|
||||
}
|
||||
|
||||
public void SignalFrameAvailableEvent()
|
||||
{
|
||||
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.
|
||||
public void SignalDequeueEvent()
|
||||
|
@ -201,6 +216,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||
|
||||
public void CheckSystemEventsLocked(int maxBufferCount)
|
||||
{
|
||||
if (!EnableExternalEvent)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool needBufferReleaseSignal = false;
|
||||
bool needFrameAvailableSignal = false;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
@ -364,6 +367,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||
Core.Slots[slot].BufferState = BufferState.Queued;
|
||||
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<BufferInfo> bufferInfos)
|
||||
{
|
||||
if (bufferHistoryCount <= 0)
|
||||
{
|
||||
bufferInfos = Span<BufferInfo>.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<GraphicBuffer>();
|
||||
BufferState = BufferState.Free;
|
||||
QueueTime = TimeSpanType.Zero;
|
||||
PresentationTime = TimeSpanType.Zero;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<BufferInfo> bufferInfos);
|
||||
|
||||
outputParcel.WriteStatus(status);
|
||||
|
||||
outputParcel.WriteInt32(bufferInfos.Length);
|
||||
|
||||
outputParcel.WriteUnmanagedSpan<BufferInfo>(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> graphicBuffer);
|
||||
|
||||
public abstract Status GetBufferHistory(int bufferHistoryCount, out Span<BufferInfo> bufferInfos);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<T>(ReadOnlySpan<T> value) where T : unmanaged
|
||||
{
|
||||
WriteInplace(MemoryMarshal.Cast<T, byte>(value));
|
||||
}
|
||||
|
||||
public void WriteUnmanagedType<T>(ref T value) where T : unmanaged
|
||||
{
|
||||
WriteInplace(SpanHelpers.AsByteSpan(ref value));
|
||||
|
|
|
@ -198,7 +198,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
|||
{
|
||||
Compose();
|
||||
|
||||
_device.System.SignalVsync();
|
||||
_device.System?.SignalVsync();
|
||||
|
||||
_ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame);
|
||||
}
|
||||
|
|
14
Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferInfo.cs
Normal file
14
Ryujinx.HLE/HOS/Services/SurfaceFlinger/Types/BufferInfo.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
Reference in a new issue