aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueConsumer.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueConsumer.cs')
-rw-r--r--src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueConsumer.cs420
1 files changed, 420 insertions, 0 deletions
diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueConsumer.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueConsumer.cs
new file mode 100644
index 00000000..c9bb0a65
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueConsumer.cs
@@ -0,0 +1,420 @@
+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
+{
+ class BufferQueueConsumer
+ {
+ public BufferQueueCore Core { get; }
+
+ public BufferQueueConsumer(BufferQueueCore core)
+ {
+ Core = core;
+ }
+
+ public Status AcquireBuffer(out BufferItem bufferItem, ulong expectedPresent)
+ {
+ lock (Core.Lock)
+ {
+ int numAcquiredBuffers = 0;
+
+ for (int i = 0; i < Core.MaxBufferCountCached; i++)
+ {
+ if (Core.Slots[i].BufferState == BufferState.Acquired)
+ {
+ numAcquiredBuffers++;
+ }
+ }
+
+ if (numAcquiredBuffers > Core.MaxAcquiredBufferCount)
+ {
+ bufferItem = null;
+
+ Logger.Debug?.Print(LogClass.SurfaceFlinger, $"Max acquired buffer count reached: {numAcquiredBuffers} (max: {Core.MaxAcquiredBufferCount})");
+
+ return Status.InvalidOperation;
+ }
+
+ if (Core.Queue.Count == 0)
+ {
+ bufferItem = null;
+
+ return Status.NoBufferAvailaible;
+ }
+
+ if (expectedPresent != 0)
+ {
+ // TODO: support this for advanced presenting.
+ throw new NotImplementedException();
+ }
+
+ bufferItem = Core.Queue[0];
+
+ if (Core.StillTracking(ref bufferItem))
+ {
+ Core.Slots[bufferItem.Slot].AcquireCalled = true;
+ 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)
+ {
+ bufferItem.GraphicBuffer.Reset();
+ }
+
+ Core.Queue.RemoveAt(0);
+
+ Core.CheckSystemEventsLocked(Core.GetMaxBufferCountLocked(true));
+ Core.SignalDequeueEvent();
+ }
+
+ return Status.Success;
+ }
+
+ public Status DetachBuffer(int slot)
+ {
+ lock (Core.Lock)
+ {
+ if (Core.IsAbandoned)
+ {
+ return Status.NoInit;
+ }
+
+ if (slot < 0 || slot >= Core.Slots.Length || !Core.IsOwnedByConsumerLocked(slot))
+ {
+ return Status.BadValue;
+ }
+
+ if (!Core.Slots[slot].RequestBufferCalled)
+ {
+ Logger.Error?.Print(LogClass.SurfaceFlinger, $"Slot {slot} was detached without requesting a buffer");
+
+ return Status.BadValue;
+ }
+
+ Core.FreeBufferLocked(slot);
+ Core.SignalDequeueEvent();
+
+ return Status.Success;
+ }
+ }
+
+ public Status AttachBuffer(out int slot, ref AndroidStrongPointer<GraphicBuffer> graphicBuffer)
+ {
+ lock (Core.Lock)
+ {
+ int numAcquiredBuffers = 0;
+
+ int freeSlot = BufferSlotArray.InvalidBufferSlot;
+
+ for (int i = 0; i < Core.Slots.Length; i++)
+ {
+ if (Core.Slots[i].BufferState == BufferState.Acquired)
+ {
+ numAcquiredBuffers++;
+ }
+ else if (Core.Slots[i].BufferState == BufferState.Free)
+ {
+ if (freeSlot == BufferSlotArray.InvalidBufferSlot || Core.Slots[i].FrameNumber < Core.Slots[freeSlot].FrameNumber)
+ {
+ freeSlot = i;
+ }
+ }
+ }
+
+ if (numAcquiredBuffers > Core.MaxAcquiredBufferCount + 1)
+ {
+ slot = BufferSlotArray.InvalidBufferSlot;
+
+ Logger.Error?.Print(LogClass.SurfaceFlinger, $"Max acquired buffer count reached: {numAcquiredBuffers} (max: {Core.MaxAcquiredBufferCount})");
+
+ return Status.InvalidOperation;
+ }
+
+ if (freeSlot == BufferSlotArray.InvalidBufferSlot)
+ {
+ slot = BufferSlotArray.InvalidBufferSlot;
+
+ return Status.NoMemory;
+ }
+
+ Core.UpdateMaxBufferCountCachedLocked(freeSlot);
+
+ slot = freeSlot;
+
+ Core.Slots[slot].GraphicBuffer.Set(graphicBuffer);
+
+ Core.Slots[slot].BufferState = BufferState.Acquired;
+ Core.Slots[slot].AttachedByConsumer = true;
+ Core.Slots[slot].NeedsCleanupOnRelease = false;
+ Core.Slots[slot].Fence = AndroidFence.NoFence;
+ Core.Slots[slot].FrameNumber = 0;
+ Core.Slots[slot].AcquireCalled = false;
+ }
+
+ return Status.Success;
+ }
+
+ public Status ReleaseBuffer(int slot, ulong frameNumber, ref AndroidFence fence)
+ {
+ if (slot < 0 || slot >= Core.Slots.Length)
+ {
+ return Status.BadValue;
+ }
+
+ IProducerListener listener = null;
+
+ lock (Core.Lock)
+ {
+ if (Core.Slots[slot].FrameNumber != frameNumber)
+ {
+ return Status.StaleBufferSlot;
+ }
+
+ foreach (BufferItem item in Core.Queue)
+ {
+ if (item.Slot == slot)
+ {
+ return Status.BadValue;
+ }
+ }
+
+ if (Core.Slots[slot].BufferState == BufferState.Acquired)
+ {
+ Core.Slots[slot].BufferState = BufferState.Free;
+ Core.Slots[slot].Fence = fence;
+
+ listener = Core.ProducerListener;
+ }
+ else if (Core.Slots[slot].NeedsCleanupOnRelease)
+ {
+ Core.Slots[slot].NeedsCleanupOnRelease = false;
+
+ return Status.StaleBufferSlot;
+ }
+ else
+ {
+ return Status.BadValue;
+ }
+
+ Core.Slots[slot].GraphicBuffer.Object.DecrementNvMapHandleRefCount(Core.Owner);
+
+ Core.CheckSystemEventsLocked(Core.GetMaxBufferCountLocked(true));
+ Core.SignalDequeueEvent();
+ }
+
+ listener?.OnBufferReleased();
+
+ return Status.Success;
+ }
+
+ public Status Connect(IConsumerListener consumerListener, bool controlledByApp)
+ {
+ if (consumerListener == null)
+ {
+ return Status.BadValue;
+ }
+
+ lock (Core.Lock)
+ {
+ if (Core.IsAbandoned)
+ {
+ return Status.NoInit;
+ }
+
+ Core.ConsumerListener = consumerListener;
+ Core.ConsumerControlledByApp = controlledByApp;
+ }
+
+ return Status.Success;
+ }
+
+ public Status Disconnect()
+ {
+ lock (Core.Lock)
+ {
+ if (!Core.IsConsumerConnectedLocked())
+ {
+ return Status.BadValue;
+ }
+
+ Core.IsAbandoned = true;
+ Core.ConsumerListener = null;
+
+ Core.Queue.Clear();
+ Core.FreeAllBuffersLocked();
+ Core.SignalDequeueEvent();
+ }
+
+ return Status.Success;
+ }
+
+ public Status GetReleasedBuffers(out ulong slotMask)
+ {
+ slotMask = 0;
+
+ lock (Core.Lock)
+ {
+ if (Core.IsAbandoned)
+ {
+ return Status.BadValue;
+ }
+
+ for (int slot = 0; slot < Core.Slots.Length; slot++)
+ {
+ if (!Core.Slots[slot].AcquireCalled)
+ {
+ slotMask |= 1UL << slot;
+ }
+ }
+
+ for (int i = 0; i < Core.Queue.Count; i++)
+ {
+ if (Core.Queue[i].AcquireCalled)
+ {
+ slotMask &= ~(1UL << i);
+ }
+ }
+ }
+
+ return Status.Success;
+ }
+
+ public Status SetDefaultBufferSize(uint width, uint height)
+ {
+ if (width == 0 || height == 0)
+ {
+ return Status.BadValue;
+ }
+
+ lock (Core.Lock)
+ {
+ Core.DefaultWidth = (int)width;
+ Core.DefaultHeight = (int)height;
+ }
+
+ return Status.Success;
+ }
+
+ public Status SetDefaultMaxBufferCount(int bufferMaxCount)
+ {
+ lock (Core.Lock)
+ {
+ return Core.SetDefaultMaxBufferCountLocked(bufferMaxCount);
+ }
+ }
+
+ public Status DisableAsyncBuffer()
+ {
+ lock (Core.Lock)
+ {
+ if (Core.IsConsumerConnectedLocked())
+ {
+ return Status.InvalidOperation;
+ }
+
+ Core.UseAsyncBuffer = false;
+ }
+
+ return Status.Success;
+ }
+
+ public Status SetMaxAcquiredBufferCount(int maxAcquiredBufferCount)
+ {
+ if (maxAcquiredBufferCount < 0 || maxAcquiredBufferCount > BufferSlotArray.MaxAcquiredBuffers)
+ {
+ return Status.BadValue;
+ }
+
+ lock (Core.Lock)
+ {
+ if (Core.IsProducerConnectedLocked())
+ {
+ return Status.InvalidOperation;
+ }
+
+ Core.MaxAcquiredBufferCount = maxAcquiredBufferCount;
+ }
+
+ return Status.Success;
+ }
+
+ public Status SetDefaultBufferFormat(PixelFormat defaultFormat)
+ {
+ lock (Core.Lock)
+ {
+ Core.DefaultBufferFormat = defaultFormat;
+ }
+
+ return Status.Success;
+ }
+
+ public Status SetConsumerUsageBits(uint usage)
+ {
+ lock (Core.Lock)
+ {
+ Core.ConsumerUsageBits = usage;
+ }
+
+ return Status.Success;
+ }
+
+ public Status SetTransformHint(NativeWindowTransform transformHint)
+ {
+ lock (Core.Lock)
+ {
+ Core.TransformHint = transformHint;
+ }
+
+ 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;
+ }
+ }
+}