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 TpidrEl0 { get; set; }
|
||||||
public long Tpidr { get; set; }
|
public long Tpidr { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types;
|
using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
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].NeedsCleanupOnRelease = true;
|
||||||
Core.Slots[bufferItem.Slot].BufferState = BufferState.Acquired;
|
Core.Slots[bufferItem.Slot].BufferState = BufferState.Acquired;
|
||||||
Core.Slots[bufferItem.Slot].Fence = AndroidFence.NoFence;
|
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)
|
if (bufferItem.AcquireCalled)
|
||||||
|
@ -368,5 +381,38 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
|
|
||||||
return Status.Success;
|
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.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
|
using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
@ -29,6 +30,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
public bool ConsumerControlledByApp;
|
public bool ConsumerControlledByApp;
|
||||||
public uint ConsumerUsageBits;
|
public uint ConsumerUsageBits;
|
||||||
public List<BufferItem> Queue;
|
public List<BufferItem> Queue;
|
||||||
|
public BufferInfo[] BufferHistory;
|
||||||
|
public uint BufferHistoryPosition;
|
||||||
|
public bool EnableExternalEvent;
|
||||||
|
|
||||||
public readonly object Lock = new object();
|
public readonly object Lock = new object();
|
||||||
|
|
||||||
|
@ -37,6 +41,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
|
|
||||||
public KProcess Owner { get; }
|
public KProcess Owner { get; }
|
||||||
|
|
||||||
|
public const int BufferHistoryArraySize = 8;
|
||||||
|
|
||||||
public BufferQueueCore(Switch device, KProcess process)
|
public BufferQueueCore(Switch device, KProcess process)
|
||||||
{
|
{
|
||||||
Slots = new BufferSlotArray();
|
Slots = new BufferSlotArray();
|
||||||
|
@ -64,6 +70,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
_frameAvailableEvent = new KEvent(device.System.KernelContext);
|
_frameAvailableEvent = new KEvent(device.System.KernelContext);
|
||||||
|
|
||||||
Owner = process;
|
Owner = process;
|
||||||
|
|
||||||
|
BufferHistory = new BufferInfo[BufferHistoryArraySize];
|
||||||
|
EnableExternalEvent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetMinUndequeuedBufferCountLocked(bool async)
|
public int GetMinUndequeuedBufferCountLocked(bool async)
|
||||||
|
@ -129,12 +138,18 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
|
|
||||||
public void SignalWaitBufferFreeEvent()
|
public void SignalWaitBufferFreeEvent()
|
||||||
{
|
{
|
||||||
_waitBufferFreeEvent.WritableEvent.Signal();
|
if (EnableExternalEvent)
|
||||||
|
{
|
||||||
|
_waitBufferFreeEvent.WritableEvent.Signal();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SignalFrameAvailableEvent()
|
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.
|
// 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)
|
public void CheckSystemEventsLocked(int maxBufferCount)
|
||||||
{
|
{
|
||||||
|
if (!EnableExternalEvent)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bool needBufferReleaseSignal = false;
|
bool needBufferReleaseSignal = false;
|
||||||
bool needFrameAvailableSignal = false;
|
bool needFrameAvailableSignal = false;
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
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.SurfaceFlinger.Types;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
||||||
using System;
|
using System;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
|
@ -92,8 +92,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
return Status.BadValue;
|
return Status.BadValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Core.Queue.Clear();
|
||||||
|
Core.FreeAllBuffersLocked();
|
||||||
Core.OverrideMaxBufferCount = bufferCount;
|
Core.OverrideMaxBufferCount = bufferCount;
|
||||||
Core.SignalDequeueEvent();
|
Core.SignalDequeueEvent();
|
||||||
|
Core.SignalWaitBufferFreeEvent();
|
||||||
|
|
||||||
listener = Core.ConsumerListener;
|
listener = Core.ConsumerListener;
|
||||||
}
|
}
|
||||||
|
@ -363,7 +366,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
Core.Slots[slot].Fence = input.Fence;
|
Core.Slots[slot].Fence = input.Fence;
|
||||||
Core.Slots[slot].BufferState = BufferState.Queued;
|
Core.Slots[slot].BufferState = BufferState.Queued;
|
||||||
Core.FrameCounter++;
|
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.AcquireCalled = Core.Slots[slot].AcquireCalled;
|
||||||
item.Crop = input.Crop;
|
item.Crop = input.Crop;
|
||||||
|
@ -381,6 +386,15 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
item.GraphicBuffer.Set(Core.Slots[slot].GraphicBuffer);
|
item.GraphicBuffer.Set(Core.Slots[slot].GraphicBuffer);
|
||||||
item.GraphicBuffer.Object.IncrementNvMapHandleRefCount(Core.Owner);
|
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;
|
_stickyTransform = input.StickyTransform;
|
||||||
|
|
||||||
if (Core.Queue.Count == 0)
|
if (Core.Queue.Count == 0)
|
||||||
|
@ -488,6 +502,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
case NativeWindowAttribute.MinUnqueuedBuffers:
|
case NativeWindowAttribute.MinUnqueuedBuffers:
|
||||||
outValue = Core.GetMinUndequeuedBufferCountLocked(false);
|
outValue = Core.GetMinUndequeuedBufferCountLocked(false);
|
||||||
return Status.Success;
|
return Status.Success;
|
||||||
|
case NativeWindowAttribute.ConsumerRunningBehind:
|
||||||
|
outValue = Core.Queue.Count > 1 ? 1 : 0;
|
||||||
|
return Status.Success;
|
||||||
case NativeWindowAttribute.ConsumerUsageBits:
|
case NativeWindowAttribute.ConsumerUsageBits:
|
||||||
outValue = (int)Core.ConsumerUsageBits;
|
outValue = (int)Core.ConsumerUsageBits;
|
||||||
return Status.Success;
|
return Status.Success;
|
||||||
|
@ -561,6 +578,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
case NativeWindowApi.Camera:
|
case NativeWindowApi.Camera:
|
||||||
if (Core.ConnectedApi == api)
|
if (Core.ConnectedApi == api)
|
||||||
{
|
{
|
||||||
|
Core.Queue.Clear();
|
||||||
Core.FreeAllBuffersLocked();
|
Core.FreeAllBuffersLocked();
|
||||||
|
|
||||||
producerListener = Core.ProducerListener;
|
producerListener = Core.ProducerListener;
|
||||||
|
@ -762,5 +780,35 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
{
|
{
|
||||||
return Core.GetWaitBufferFreeEvent();
|
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.SurfaceFlinger.Types;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Time.Clock;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
{
|
{
|
||||||
|
@ -12,11 +13,15 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
public bool AcquireCalled;
|
public bool AcquireCalled;
|
||||||
public bool NeedsCleanupOnRelease;
|
public bool NeedsCleanupOnRelease;
|
||||||
public bool AttachedByConsumer;
|
public bool AttachedByConsumer;
|
||||||
|
public TimeSpanType QueueTime;
|
||||||
|
public TimeSpanType PresentationTime;
|
||||||
|
|
||||||
public BufferSlot()
|
public BufferSlot()
|
||||||
{
|
{
|
||||||
GraphicBuffer = new AndroidStrongPointer<GraphicBuffer>();
|
GraphicBuffer = new AndroidStrongPointer<GraphicBuffer>();
|
||||||
BufferState = BufferState.Free;
|
BufferState = BufferState.Free;
|
||||||
|
QueueTime = TimeSpanType.Zero;
|
||||||
|
PresentationTime = TimeSpanType.Zero;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -236,6 +236,18 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
|
|
||||||
outputParcel.WriteStatus(status);
|
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;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new NotImplementedException($"Transaction {(TransactionCode)code} not implemented");
|
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 Disconnect(NativeWindowApi api);
|
||||||
|
|
||||||
public abstract Status SetPreallocatedBuffer(int slot, AndroidStrongPointer<GraphicBuffer> graphicBuffer);
|
public abstract Status SetPreallocatedBuffer(int slot, AndroidStrongPointer<GraphicBuffer> graphicBuffer);
|
||||||
|
|
||||||
|
public abstract Status GetBufferHistory(int bufferHistoryCount, out Span<BufferInfo> bufferInfos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
public ResultCode TransactParcelAuto(ServiceCtx context)
|
public ResultCode TransactParcelAuto(ServiceCtx context)
|
||||||
{
|
{
|
||||||
int binderId = context.RequestData.ReadInt32();
|
int binderId = context.RequestData.ReadInt32();
|
||||||
|
|
||||||
uint code = context.RequestData.ReadUInt32();
|
uint code = context.RequestData.ReadUInt32();
|
||||||
uint flags = context.RequestData.ReadUInt32();
|
uint flags = context.RequestData.ReadUInt32();
|
||||||
|
|
||||||
|
|
|
@ -173,6 +173,11 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
public void WriteInt64(long value) => WriteUnmanagedType(ref value);
|
public void WriteInt64(long value) => WriteUnmanagedType(ref value);
|
||||||
public void WriteUInt64(ulong 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
|
public void WriteUnmanagedType<T>(ref T value) where T : unmanaged
|
||||||
{
|
{
|
||||||
WriteInplace(SpanHelpers.AsByteSpan(ref value));
|
WriteInplace(SpanHelpers.AsByteSpan(ref value));
|
||||||
|
|
|
@ -198,7 +198,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
|
||||||
{
|
{
|
||||||
Compose();
|
Compose();
|
||||||
|
|
||||||
_device.System.SignalVsync();
|
_device.System?.SignalVsync();
|
||||||
|
|
||||||
_ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame);
|
_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);
|
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)
|
public static TimeSpanType FromTicks(ulong ticks, ulong frequency)
|
||||||
{
|
{
|
||||||
return FromSeconds((long)ticks / (long)frequency);
|
return FromSeconds((long)ticks / (long)frequency);
|
||||||
|
|
Reference in a new issue