ryujinx/Ryujinx.HLE/HOS/Services/SurfaceFlinger/ConsumerBase.cs
Thog 36749c358d
SurfaceFlinger v2 (#981)
* Rewrite SurfaceFlinger

Reimplement accurately SurfaceFlinger (based on my 8.1.0 reversing of it)

TODO: support swap interval properly and reintroduce disabled "game vsync" support.

* Some fixes for SetBufferCount

* uncomment a test from last commit

* SurfaceFlinger: don't free the graphic buffer in SetBufferCount

* SurfaceFlinger: Implement swap interval correctly

* SurfaceFlinger: Reintegrate Game VSync toggle

* SurfaceFlinger: do not push a fence on buffer release on the consumer side

* Revert "SurfaceFlinger: do not push a fence on buffer release on the consumer side"

This reverts commit 586b52b0bfab2d11f361f4b59ab7b7141020bbad.

* Make the game vsync toggle work dynamically again

* Unregister producer's Binder object when closing layer

* Address ripinperi's comments

* Add a timeout on syncpoint wait operation

Syncpoint aren't supposed to be waited on for more than a second.

This effectively workaround issues caused by not having a channel
scheduling in place yet.

PS: Also introduce Android WaitForever warning about fence being not
signaled for 3s

* Fix a print of previous commit

* Address Ac_K's comments

* Address gdkchan's comments

* Address final comments
2020-04-22 14:10:27 +10:00

176 lines
5.0 KiB
C#

using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types;
using System;
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
{
class ConsumerBase : IConsumerListener
{
public class Slot
{
public AndroidStrongPointer<GraphicBuffer> GraphicBuffer;
public AndroidFence Fence;
public ulong FrameNumber;
public Slot()
{
GraphicBuffer = new AndroidStrongPointer<GraphicBuffer>();
}
}
protected Slot[] Slots = new Slot[BufferSlotArray.NumBufferSlots];
protected bool IsAbandoned;
protected BufferQueueConsumer Consumer;
protected readonly object Lock = new object();
private IConsumerListener _listener;
public ConsumerBase(BufferQueueConsumer consumer, bool controlledByApp, IConsumerListener listener)
{
for (int i = 0; i < Slots.Length; i++)
{
Slots[i] = new Slot();
}
IsAbandoned = false;
Consumer = consumer;
_listener = listener;
Status connectStatus = consumer.Connect(this, controlledByApp);
if (connectStatus != Status.Success)
{
throw new InvalidOperationException();
}
}
public virtual void OnBuffersReleased()
{
lock (Lock)
{
if (IsAbandoned)
{
return;
}
Consumer.GetReleasedBuffers(out ulong slotMask);
for (int i = 0; i < Slots.Length; i++)
{
if ((slotMask & (1UL << i)) != 0)
{
FreeBufferLocked(i);
}
}
}
}
public virtual void OnFrameAvailable(ref BufferItem item)
{
_listener?.OnFrameAvailable(ref item);
}
public virtual void OnFrameReplaced(ref BufferItem item)
{
_listener?.OnFrameReplaced(ref item);
}
protected virtual void FreeBufferLocked(int slotIndex)
{
Slots[slotIndex].GraphicBuffer.Reset();
Slots[slotIndex].Fence = AndroidFence.NoFence;
Slots[slotIndex].FrameNumber = 0;
}
public void Abandon()
{
lock (Lock)
{
if (!IsAbandoned)
{
AbandonLocked();
IsAbandoned = true;
}
}
}
protected virtual void AbandonLocked()
{
for (int i = 0; i < Slots.Length; i++)
{
FreeBufferLocked(i);
}
Consumer.Disconnect();
}
protected virtual Status AcquireBufferLocked(out BufferItem bufferItem, ulong expectedPresent)
{
Status acquireStatus = Consumer.AcquireBuffer(out bufferItem, expectedPresent);
if (acquireStatus != Status.Success)
{
return acquireStatus;
}
if (!bufferItem.GraphicBuffer.IsNull)
{
Slots[bufferItem.Slot].GraphicBuffer.Set(bufferItem.GraphicBuffer.Object);
}
Slots[bufferItem.Slot].FrameNumber = bufferItem.FrameNumber;
Slots[bufferItem.Slot].Fence = bufferItem.Fence;
return Status.Success;
}
protected virtual Status AddReleaseFenceLocked(int slot, ref AndroidStrongPointer<GraphicBuffer> graphicBuffer, ref AndroidFence fence)
{
if (!StillTracking(slot, ref graphicBuffer))
{
return Status.Success;
}
Slots[slot].Fence = fence;
return Status.Success;
}
protected virtual Status ReleaseBufferLocked(int slot, ref AndroidStrongPointer<GraphicBuffer> graphicBuffer)
{
if (!StillTracking(slot, ref graphicBuffer))
{
return Status.Success;
}
Status result = Consumer.ReleaseBuffer(slot, Slots[slot].FrameNumber, ref Slots[slot].Fence);
if (result == Status.StaleBufferSlot)
{
FreeBufferLocked(slot);
}
Slots[slot].Fence = AndroidFence.NoFence;
return result;
}
protected virtual bool StillTracking(int slotIndex, ref AndroidStrongPointer<GraphicBuffer> graphicBuffer)
{
if (slotIndex < 0 || slotIndex >= Slots.Length)
{
return false;
}
Slot slot = Slots[slotIndex];
// TODO: Check this. On Android, this checks the "handle". I assume NvMapHandle is the handle, but it might not be.
return !slot.GraphicBuffer.IsNull && slot.GraphicBuffer.Object.Buffer.Surfaces[0].NvMapHandle == graphicBuffer.Object.Buffer.Surfaces[0].NvMapHandle;
}
}
}