aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs
diff options
context:
space:
mode:
authorTSR Berry <20988865+TSRBerry@users.noreply.github.com>2023-04-08 01:22:00 +0200
committerMary <thog@protonmail.com>2023-04-27 23:51:14 +0200
commitcee712105850ac3385cd0091a923438167433f9f (patch)
tree4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs
parentcd124bda587ef09668a971fa1cac1c3f0cfc9f21 (diff)
Move solution and projects to src
Diffstat (limited to 'src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs')
-rw-r--r--src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs341
1 files changed, 341 insertions, 0 deletions
diff --git a/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs
new file mode 100644
index 00000000..1efd37f4
--- /dev/null
+++ b/src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs
@@ -0,0 +1,341 @@
+using Ryujinx.Common.Logging;
+using Ryujinx.HLE.HOS.Kernel.Threading;
+using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
+{
+ class BufferQueueCore
+ {
+ public BufferSlotArray Slots;
+ public int OverrideMaxBufferCount;
+ public bool UseAsyncBuffer;
+ public bool DequeueBufferCannotBlock;
+ public PixelFormat DefaultBufferFormat;
+ public int DefaultWidth;
+ public int DefaultHeight;
+ public int DefaultMaxBufferCount;
+ public int MaxAcquiredBufferCount;
+ public bool BufferHasBeenQueued;
+ public ulong FrameCounter;
+ public NativeWindowTransform TransformHint;
+ public bool IsAbandoned;
+ public NativeWindowApi ConnectedApi;
+ public bool IsAllocating;
+ public IProducerListener ProducerListener;
+ public IConsumerListener ConsumerListener;
+ public bool ConsumerControlledByApp;
+ public uint ConsumerUsageBits;
+ public List<BufferItem> Queue;
+ public BufferInfo[] BufferHistory;
+ public uint BufferHistoryPosition;
+ public bool EnableExternalEvent;
+ public int MaxBufferCountCached;
+
+ public readonly object Lock = new object();
+
+ private KEvent _waitBufferFreeEvent;
+ private KEvent _frameAvailableEvent;
+
+ public ulong Owner { get; }
+
+ public bool Active { get; private set; }
+
+ public const int BufferHistoryArraySize = 8;
+
+ public event Action BufferQueued;
+
+ public BufferQueueCore(Switch device, ulong pid)
+ {
+ Slots = new BufferSlotArray();
+ IsAbandoned = false;
+ OverrideMaxBufferCount = 0;
+ DequeueBufferCannotBlock = false;
+ UseAsyncBuffer = false;
+ DefaultWidth = 1;
+ DefaultHeight = 1;
+ DefaultMaxBufferCount = 2;
+ MaxAcquiredBufferCount = 1;
+ FrameCounter = 0;
+ TransformHint = 0;
+ DefaultBufferFormat = PixelFormat.Rgba8888;
+ IsAllocating = false;
+ ProducerListener = null;
+ ConsumerListener = null;
+ ConsumerUsageBits = 0;
+
+ Queue = new List<BufferItem>();
+
+ // TODO: CreateGraphicBufferAlloc?
+
+ _waitBufferFreeEvent = new KEvent(device.System.KernelContext);
+ _frameAvailableEvent = new KEvent(device.System.KernelContext);
+
+ Owner = pid;
+
+ Active = true;
+
+ BufferHistory = new BufferInfo[BufferHistoryArraySize];
+ EnableExternalEvent = true;
+ MaxBufferCountCached = 0;
+ }
+
+ public int GetMinUndequeuedBufferCountLocked(bool async)
+ {
+ if (!UseAsyncBuffer)
+ {
+ return 0;
+ }
+
+ if (DequeueBufferCannotBlock || async)
+ {
+ return MaxAcquiredBufferCount + 1;
+ }
+
+ return MaxAcquiredBufferCount;
+ }
+
+ public int GetMinMaxBufferCountLocked(bool async)
+ {
+ return GetMinUndequeuedBufferCountLocked(async);
+ }
+
+ public void UpdateMaxBufferCountCachedLocked(int slot)
+ {
+ if (MaxBufferCountCached <= slot)
+ {
+ MaxBufferCountCached = slot + 1;
+ }
+ }
+
+ public int GetMaxBufferCountLocked(bool async)
+ {
+ int minMaxBufferCount = GetMinMaxBufferCountLocked(async);
+
+ int maxBufferCount = Math.Max(DefaultMaxBufferCount, minMaxBufferCount);
+
+ if (OverrideMaxBufferCount != 0)
+ {
+ return OverrideMaxBufferCount;
+ }
+
+ // Preserve all buffers already in control of the producer and the consumer.
+ for (int slot = maxBufferCount; slot < Slots.Length; slot++)
+ {
+ BufferState state = Slots[slot].BufferState;
+
+ if (state == BufferState.Queued || state == BufferState.Dequeued)
+ {
+ maxBufferCount = slot + 1;
+ }
+ }
+
+ return maxBufferCount;
+ }
+
+ public Status SetDefaultMaxBufferCountLocked(int count)
+ {
+ int minBufferCount = UseAsyncBuffer ? 2 : 1;
+
+ if (count < minBufferCount || count > Slots.Length)
+ {
+ return Status.BadValue;
+ }
+
+ DefaultMaxBufferCount = count;
+
+ SignalDequeueEvent();
+
+ return Status.Success;
+ }
+
+ public void SignalWaitBufferFreeEvent()
+ {
+ if (EnableExternalEvent)
+ {
+ _waitBufferFreeEvent.WritableEvent.Signal();
+ }
+ }
+
+ public void SignalFrameAvailableEvent()
+ {
+ if (EnableExternalEvent)
+ {
+ _frameAvailableEvent.WritableEvent.Signal();
+ }
+ }
+
+ public void PrepareForExit()
+ {
+ lock (Lock)
+ {
+ Active = false;
+
+ Monitor.PulseAll(Lock);
+ }
+ }
+
+ // 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()
+ {
+ Monitor.PulseAll(Lock);
+ }
+
+ public void WaitDequeueEvent()
+ {
+ WaitForLock();
+ }
+
+ public void SignalIsAllocatingEvent()
+ {
+ Monitor.PulseAll(Lock);
+ }
+
+ public void WaitIsAllocatingEvent()
+ {
+ WaitForLock();
+ }
+
+ public void SignalQueueEvent()
+ {
+ BufferQueued?.Invoke();
+ }
+
+ private void WaitForLock()
+ {
+ if (Active)
+ {
+ Monitor.Wait(Lock);
+ }
+ }
+
+ public void FreeBufferLocked(int slot)
+ {
+ Slots[slot].GraphicBuffer.Reset();
+
+ if (Slots[slot].BufferState == BufferState.Acquired)
+ {
+ Slots[slot].NeedsCleanupOnRelease = true;
+ }
+
+ Slots[slot].BufferState = BufferState.Free;
+ Slots[slot].FrameNumber = uint.MaxValue;
+ Slots[slot].AcquireCalled = false;
+ Slots[slot].Fence.FenceCount = 0;
+ }
+
+ public void FreeAllBuffersLocked()
+ {
+ BufferHasBeenQueued = false;
+
+ for (int slot = 0; slot < Slots.Length; slot++)
+ {
+ FreeBufferLocked(slot);
+ }
+ }
+
+ public bool StillTracking(ref BufferItem item)
+ {
+ BufferSlot slot = Slots[item.Slot];
+
+ // 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 == item.GraphicBuffer.Object.Buffer.Surfaces[0].NvMapHandle;
+ }
+
+ public void WaitWhileAllocatingLocked()
+ {
+ while (IsAllocating)
+ {
+ WaitIsAllocatingEvent();
+ }
+ }
+
+ public void CheckSystemEventsLocked(int maxBufferCount)
+ {
+ if (!EnableExternalEvent)
+ {
+ return;
+ }
+
+ bool needBufferReleaseSignal = false;
+ bool needFrameAvailableSignal = false;
+
+ if (maxBufferCount > 1)
+ {
+ for (int i = 0; i < maxBufferCount; i++)
+ {
+ if (Slots[i].BufferState == BufferState.Queued)
+ {
+ needFrameAvailableSignal = true;
+ }
+ else if (Slots[i].BufferState == BufferState.Free)
+ {
+ needBufferReleaseSignal = true;
+ }
+ }
+ }
+
+ if (needBufferReleaseSignal)
+ {
+ SignalWaitBufferFreeEvent();
+ }
+ else
+ {
+ _waitBufferFreeEvent.WritableEvent.Clear();
+ }
+
+ if (needFrameAvailableSignal)
+ {
+ SignalFrameAvailableEvent();
+ }
+ else
+ {
+ _frameAvailableEvent.WritableEvent.Clear();
+ }
+ }
+
+ public bool IsProducerConnectedLocked()
+ {
+ return ConnectedApi != NativeWindowApi.NoApi;
+ }
+
+ public bool IsConsumerConnectedLocked()
+ {
+ return ConsumerListener != null;
+ }
+
+ public KReadableEvent GetWaitBufferFreeEvent()
+ {
+ lock (Lock)
+ {
+ return _waitBufferFreeEvent.ReadableEvent;
+ }
+ }
+
+ public bool IsOwnedByConsumerLocked(int slot)
+ {
+ if (Slots[slot].BufferState != BufferState.Acquired)
+ {
+ Logger.Error?.Print(LogClass.SurfaceFlinger, $"Slot {slot} is not owned by the consumer (state = {Slots[slot].BufferState})");
+
+ return false;
+ }
+
+ return true;
+ }
+
+ public bool IsOwnedByProducerLocked(int slot)
+ {
+ if (Slots[slot].BufferState != BufferState.Dequeued)
+ {
+ Logger.Error?.Print(LogClass.SurfaceFlinger, $"Slot {slot} is not owned by the producer (state = {Slots[slot].BufferState})");
+
+ return false;
+ }
+
+ return true;
+ }
+ }
+}