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:
Thog 2020-05-15 03:30:08 +02:00 committed by GitHub
parent b2e5855928
commit 378259a40a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 167 additions and 8 deletions

View file

@ -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; }

View file

@ -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;
}
} }
} }

View file

@ -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;

View file

@ -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;
}
}
} }
} }

View file

@ -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;
} }
} }
} }

View file

@ -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);
} }
} }

View file

@ -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();

View file

@ -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));

View file

@ -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);
} }

View 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;
}
}

View file

@ -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);