diff options
| author | TSR Berry <20988865+TSRBerry@users.noreply.github.com> | 2023-04-08 01:22:00 +0200 |
|---|---|---|
| committer | Mary <thog@protonmail.com> | 2023-04-27 23:51:14 +0200 |
| commit | cee712105850ac3385cd0091a923438167433f9f (patch) | |
| tree | 4a5274b21d8b7f938c0d0ce18736d3f2993b11b1 /src/Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs | |
| parent | cd124bda587ef09668a971fa1cac1c3f0cfc9f21 (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.cs | 341 |
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; + } + } +} |
