Просмотр исходного кода

SurfaceFlinger: fix some bugs (#1262)

* SurfaceFlinger: fix some bugs

This fixes some bugs in the current implementation and make it closer to
the real implementation.

* Fix align of some variables
Thog 5 лет назад
Родитель
Сommit
bcb7761eac

+ 4 - 2
Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueConsumer.cs

@@ -20,7 +20,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
             {
             {
                 int numAcquiredBuffers = 0;
                 int numAcquiredBuffers = 0;
 
 
-                for (int i = 0; i < Core.Slots.Length; i++)
+                for (int i = 0; i < Core.MaxBufferCountCached; i++)
                 {
                 {
                     if (Core.Slots[i].BufferState == BufferState.Acquired)
                     if (Core.Slots[i].BufferState == BufferState.Acquired)
                     {
                     {
@@ -28,7 +28,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
                     }
                     }
                 }
                 }
 
 
-                if (numAcquiredBuffers >= Core.MaxAcquiredBufferCount + 1)
+                if (numAcquiredBuffers > Core.MaxAcquiredBufferCount)
                 {
                 {
                     bufferItem = null;
                     bufferItem = null;
 
 
@@ -153,6 +153,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
                     return Status.NoMemory;
                     return Status.NoMemory;
                 }
                 }
 
 
+                Core.UpdateMaxBufferCountCachedLocked(freeSlot);
+
                 slot = freeSlot;
                 slot = freeSlot;
 
 
                 Core.Slots[slot].GraphicBuffer.Set(graphicBuffer);
                 Core.Slots[slot].GraphicBuffer.Set(graphicBuffer);

+ 19 - 7
Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueCore.cs

@@ -33,6 +33,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
         public BufferInfo[]          BufferHistory;
         public BufferInfo[]          BufferHistory;
         public uint                  BufferHistoryPosition;
         public uint                  BufferHistoryPosition;
         public bool                  EnableExternalEvent;
         public bool                  EnableExternalEvent;
+        public int                   MaxBufferCountCached;
 
 
         public readonly object Lock = new object();
         public readonly object Lock = new object();
 
 
@@ -71,8 +72,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
 
 
             Owner = process;
             Owner = process;
 
 
-            BufferHistory       = new BufferInfo[BufferHistoryArraySize];
-            EnableExternalEvent = true;
+            BufferHistory        = new BufferInfo[BufferHistoryArraySize];
+            EnableExternalEvent  = true;
+            MaxBufferCountCached = 0;
         }
         }
 
 
         public int GetMinUndequeuedBufferCountLocked(bool async)
         public int GetMinUndequeuedBufferCountLocked(bool async)
@@ -95,6 +97,14 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
             return GetMinUndequeuedBufferCountLocked(async);
             return GetMinUndequeuedBufferCountLocked(async);
         }
         }
 
 
+        public void UpdateMaxBufferCountCachedLocked(int slot)
+        {
+            if (MaxBufferCountCached <= slot)
+            {
+                MaxBufferCountCached = slot + 1;
+            }
+        }
+
         public int GetMaxBufferCountLocked(bool async)
         public int GetMaxBufferCountLocked(bool async)
         {
         {
             int minMaxBufferCount = GetMinMaxBufferCountLocked(async);
             int minMaxBufferCount = GetMinMaxBufferCountLocked(async);
@@ -103,7 +113,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
 
 
             if (OverrideMaxBufferCount != 0)
             if (OverrideMaxBufferCount != 0)
             {
             {
-                maxBufferCount = OverrideMaxBufferCount;
+                return OverrideMaxBufferCount;
             }
             }
 
 
             // Preserve all buffers already in control of the producer and the consumer.
             // Preserve all buffers already in control of the producer and the consumer.
@@ -163,18 +173,20 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
             Monitor.Wait(Lock);
             Monitor.Wait(Lock);
         }
         }
 
 
-        public void SignalIsAbandonedEvent()
+        public void SignalIsAllocatingEvent()
         {
         {
             Monitor.PulseAll(Lock);
             Monitor.PulseAll(Lock);
         }
         }
 
 
-        public void WaitIsAbandonedEvent()
+        public void WaitIsAllocatingEvent()
         {
         {
             Monitor.Wait(Lock);
             Monitor.Wait(Lock);
         }
         }
 
 
         public void FreeBufferLocked(int slot)
         public void FreeBufferLocked(int slot)
         {
         {
+            Slots[slot].GraphicBuffer.Reset();
+
             if (Slots[slot].BufferState == BufferState.Acquired)
             if (Slots[slot].BufferState == BufferState.Acquired)
             {
             {
                 Slots[slot].NeedsCleanupOnRelease = true;
                 Slots[slot].NeedsCleanupOnRelease = true;
@@ -206,9 +218,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
 
 
         public void WaitWhileAllocatingLocked()
         public void WaitWhileAllocatingLocked()
         {
         {
-            while (IsAbandoned)
+            while (IsAllocating)
             {
             {
-                WaitIsAbandonedEvent();
+                WaitIsAllocatingEvent();
             }
             }
         }
         }
 
 

+ 87 - 40
Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferQueueProducer.cs

@@ -1,5 +1,6 @@
 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.Settings;
 using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types;
 using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types;
 using Ryujinx.HLE.HOS.Services.Time.Clock;
 using Ryujinx.HLE.HOS.Services.Time.Clock;
 using System;
 using System;
@@ -92,9 +93,22 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
                     return Status.BadValue;
                     return Status.BadValue;
                 }
                 }
 
 
-                Core.Queue.Clear();
-                Core.FreeAllBuffersLocked();
+                int preallocatedBufferCount = GetPreallocatedBufferCountLocked();
+
+                if (preallocatedBufferCount <= 0)
+                {
+                    Core.Queue.Clear();
+                    Core.FreeAllBuffersLocked();
+                }
+                else if (preallocatedBufferCount < bufferCount)
+                {
+                    Logger.PrintError(LogClass.SurfaceFlinger, "Not enough buffers. Try with more pre-allocated buffers");
+
+                    return Status.Success;
+                }
+
                 Core.OverrideMaxBufferCount = bufferCount;
                 Core.OverrideMaxBufferCount = bufferCount;
+
                 Core.SignalDequeueEvent();
                 Core.SignalDequeueEvent();
                 Core.SignalWaitBufferFreeEvent();
                 Core.SignalWaitBufferFreeEvent();
 
 
@@ -162,8 +176,6 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
                     height = (uint)Core.DefaultHeight;
                     height = (uint)Core.DefaultHeight;
                 }
                 }
 
 
-                Core.Slots[slot].BufferState = BufferState.Dequeued;
-
                 GraphicBuffer graphicBuffer = Core.Slots[slot].GraphicBuffer.Object;
                 GraphicBuffer graphicBuffer = Core.Slots[slot].GraphicBuffer.Object;
 
 
                 if (Core.Slots[slot].GraphicBuffer.IsNull
                 if (Core.Slots[slot].GraphicBuffer.IsNull
@@ -172,7 +184,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
                     || graphicBuffer.Format != format
                     || graphicBuffer.Format != format
                     || (graphicBuffer.Usage & usage) != usage)
                     || (graphicBuffer.Usage & usage) != usage)
                 {
                 {
-                    if (Core.Slots[slot].GraphicBuffer.IsNull)
+                    if (!Core.Slots[slot].IsPreallocated)
                     {
                     {
                         slot  = BufferSlotArray.InvalidBufferSlot;
                         slot  = BufferSlotArray.InvalidBufferSlot;
                         fence = AndroidFence.NoFence;
                         fence = AndroidFence.NoFence;
@@ -194,9 +206,15 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
                     }
                     }
                 }
                 }
 
 
+                Core.Slots[slot].BufferState = BufferState.Dequeued;
+
+                Core.UpdateMaxBufferCountCachedLocked(slot);
+
                 fence = Core.Slots[slot].Fence;
                 fence = Core.Slots[slot].Fence;
 
 
-                Core.Slots[slot].Fence = AndroidFence.NoFence;
+                Core.Slots[slot].Fence            = AndroidFence.NoFence;
+                Core.Slots[slot].QueueTime        = TimeSpanType.Zero;
+                Core.Slots[slot].PresentationTime = TimeSpanType.Zero;
 
 
                 Core.CheckSystemEventsLocked(Core.GetMaxBufferCountLocked(async));
                 Core.CheckSystemEventsLocked(Core.GetMaxBufferCountLocked(async));
             }
             }
@@ -285,6 +303,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
         {
         {
             lock (Core.Lock)
             lock (Core.Lock)
             {
             {
+                Core.WaitWhileAllocatingLocked();
+
                 Status status = WaitForFreeSlotThenRelock(false, out slot, out Status returnFlags);
                 Status status = WaitForFreeSlotThenRelock(false, out slot, out Status returnFlags);
 
 
                 if (status != Status.Success)
                 if (status != Status.Success)
@@ -299,6 +319,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
                     return Status.Busy;
                     return Status.Busy;
                 }
                 }
 
 
+                Core.UpdateMaxBufferCountCachedLocked(slot);
+
                 Core.Slots[slot].GraphicBuffer.Set(graphicBuffer);
                 Core.Slots[slot].GraphicBuffer.Set(graphicBuffer);
 
 
                 Core.Slots[slot].BufferState         = BufferState.Dequeued;
                 Core.Slots[slot].BufferState         = BufferState.Dequeued;
@@ -441,6 +463,12 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
                     NumPendingBuffers = (uint)Core.Queue.Count
                     NumPendingBuffers = (uint)Core.Queue.Count
                 };
                 };
 
 
+                if ((input.StickyTransform & 8) != 0)
+                {
+                    output.TransformHint |= NativeWindowTransform.ReturnFrameNumber;
+                    output.FrameNumber = Core.Slots[slot].FrameNumber;
+                }
+
                 _callbackTicket = _nextCallbackTicket++;
                 _callbackTicket = _nextCallbackTicket++;
             }
             }
 
 
@@ -475,6 +503,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
                 Core.Slots[slot].FrameNumber = 0;
                 Core.Slots[slot].FrameNumber = 0;
                 Core.Slots[slot].Fence       = fence;
                 Core.Slots[slot].Fence       = fence;
                 Core.SignalDequeueEvent();
                 Core.SignalDequeueEvent();
+                Core.SignalWaitBufferFreeEvent();
             }
             }
         }
         }
 
 
@@ -550,6 +579,12 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
                         output.Height            = (uint)Core.DefaultHeight;
                         output.Height            = (uint)Core.DefaultHeight;
                         output.TransformHint     = Core.TransformHint;
                         output.TransformHint     = Core.TransformHint;
                         output.NumPendingBuffers = (uint)Core.Queue.Count;
                         output.NumPendingBuffers = (uint)Core.Queue.Count;
+
+                        if (NxSettings.Settings.TryGetValue("nv!nvn_no_vsync_capability", out object noVSyncCapability) && (bool)noVSyncCapability)
+                        {
+                            output.TransformHint |= NativeWindowTransform.NoVSyncCapability;
+                        }
+
                         return Status.Success;
                         return Status.Success;
                     default:
                     default:
                         return Status.BadValue;
                         return Status.BadValue;
@@ -565,6 +600,8 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
 
 
             lock (Core.Lock)
             lock (Core.Lock)
             {
             {
+                Core.WaitWhileAllocatingLocked();
+
                 if (Core.IsAbandoned)
                 if (Core.IsAbandoned)
                 {
                 {
                     return Status.Success;
                     return Status.Success;
@@ -580,13 +617,15 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
                         {
                         {
                             Core.Queue.Clear();
                             Core.Queue.Clear();
                             Core.FreeAllBuffersLocked();
                             Core.FreeAllBuffersLocked();
+                            Core.SignalDequeueEvent();
 
 
                             producerListener = Core.ProducerListener;
                             producerListener = Core.ProducerListener;
 
 
                             Core.ProducerListener = null;
                             Core.ProducerListener = null;
                             Core.ConnectedApi     = NativeWindowApi.NoApi;
                             Core.ConnectedApi     = NativeWindowApi.NoApi;
 
 
-                            Core.SignalDequeueEvent();
+                            Core.SignalWaitBufferFreeEvent();
+                            Core.SignalFrameAvailableEvent();
 
 
                             status = Status.Success;
                             status = Status.Success;
                         }
                         }
@@ -599,6 +638,21 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
             return status;
             return status;
         }
         }
 
 
+        private int GetPreallocatedBufferCountLocked()
+        {
+            int bufferCount = 0;
+
+            for (int i = 0; i < Core.Slots.Length; i++)
+            {
+                if (Core.Slots[i].IsPreallocated)
+                {
+                    bufferCount++;
+                }
+            }
+
+            return bufferCount;
+        }
+
         public override Status SetPreallocatedBuffer(int slot, AndroidStrongPointer<GraphicBuffer> graphicBuffer)
         public override Status SetPreallocatedBuffer(int slot, AndroidStrongPointer<GraphicBuffer> graphicBuffer)
         {
         {
             if (slot < 0 || slot >= Core.Slots.Length)
             if (slot < 0 || slot >= Core.Slots.Length)
@@ -613,6 +667,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
                 Core.Slots[slot].RequestBufferCalled   = false;
                 Core.Slots[slot].RequestBufferCalled   = false;
                 Core.Slots[slot].AcquireCalled         = false;
                 Core.Slots[slot].AcquireCalled         = false;
                 Core.Slots[slot].NeedsCleanupOnRelease = false;
                 Core.Slots[slot].NeedsCleanupOnRelease = false;
+                Core.Slots[slot].IsPreallocated        = !graphicBuffer.IsNull;
                 Core.Slots[slot].FrameNumber           = 0;
                 Core.Slots[slot].FrameNumber           = 0;
 
 
                 Core.Slots[slot].GraphicBuffer.Set(graphicBuffer);
                 Core.Slots[slot].GraphicBuffer.Set(graphicBuffer);
@@ -622,21 +677,9 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
                     Core.Slots[slot].GraphicBuffer.Object.Buffer.Usage &= (int)Core.ConsumerUsageBits;
                     Core.Slots[slot].GraphicBuffer.Object.Buffer.Usage &= (int)Core.ConsumerUsageBits;
                 }
                 }
 
 
-                int bufferCount = 0;
-
-                for (int i = 0; i < Core.Slots.Length; i++)
-                {
-                    if (!Core.Slots[i].GraphicBuffer.IsNull)
-                    {
-                        bufferCount++;
-                    }
-                }
-
-                Core.OverrideMaxBufferCount = bufferCount;
+                Core.OverrideMaxBufferCount = GetPreallocatedBufferCountLocked();
                 Core.UseAsyncBuffer = false;
                 Core.UseAsyncBuffer = false;
 
 
-                bool cleared = false;
-
                 if (!graphicBuffer.IsNull)
                 if (!graphicBuffer.IsNull)
                 {
                 {
                     // NOTE: Nintendo set the default width, height and format from the GraphicBuffer..
                     // NOTE: Nintendo set the default width, height and format from the GraphicBuffer..
@@ -647,30 +690,30 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
                 }
                 }
                 else
                 else
                 {
                 {
-                    foreach (BufferItem item in Core.Queue)
+                    bool allBufferFreed = true;
+
+                    for (int i = 0; i < Core.Slots.Length; i++)
                     {
                     {
-                        if (item.Slot >= BufferSlotArray.NumBufferSlots)
+                        if (!Core.Slots[i].GraphicBuffer.IsNull)
                         {
                         {
-                            Core.Queue.Clear();
-                            Core.FreeAllBuffersLocked();
-                            Core.SignalDequeueEvent();
-                            Core.SignalWaitBufferFreeEvent();
-                            Core.SignalFrameAvailableEvent();
-
-                            cleared = true;
-
+                            allBufferFreed = false;
                             break;
                             break;
                         }
                         }
                     }
                     }
-                }
 
 
-                // The dequeue event must not be signaled two times in case of clean up,
-                // but for some reason, it still signals the wait buffer free event two times...
-                if (!cleared)
-                {
-                    Core.SignalDequeueEvent();
+                    if (allBufferFreed)
+                    {
+                        Core.Queue.Clear();
+                        Core.FreeAllBuffersLocked();
+                        Core.SignalDequeueEvent();
+                        Core.SignalWaitBufferFreeEvent();
+                        Core.SignalFrameAvailableEvent();
+
+                        return Status.Success;
+                    }
                 }
                 }
 
 
+                Core.SignalDequeueEvent();
                 Core.SignalWaitBufferFreeEvent();
                 Core.SignalWaitBufferFreeEvent();
 
 
                 return Status.Success;
                 return Status.Success;
@@ -702,12 +745,16 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
                     return Status.BadValue;
                     return Status.BadValue;
                 }
                 }
 
 
-                for (int slot = maxBufferCount; slot < Core.Slots.Length; slot++)
+
+                if (maxBufferCount < Core.MaxBufferCountCached)
                 {
                 {
-                    if (!Core.Slots[slot].GraphicBuffer.IsNull)
+                    for (int slot = maxBufferCount; slot < Core.MaxBufferCountCached; slot++)
                     {
                     {
-                        Core.FreeBufferLocked(slot);
-                        returnStatus |= Status.ReleaseAllBuffers;
+                        if (Core.Slots[slot].BufferState == BufferState.Free && !Core.Slots[slot].GraphicBuffer.IsNull && !Core.Slots[slot].IsPreallocated)
+                        {
+                            Core.FreeBufferLocked(slot);
+                            returnStatus |= Status.ReleaseAllBuffers;
+                        }
                     }
                     }
                 }
                 }
 
 

+ 2 - 0
Ryujinx.HLE/HOS/Services/SurfaceFlinger/BufferSlot.cs

@@ -15,6 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
         public bool                                AttachedByConsumer;
         public bool                                AttachedByConsumer;
         public TimeSpanType                        QueueTime;
         public TimeSpanType                        QueueTime;
         public TimeSpanType                        PresentationTime;
         public TimeSpanType                        PresentationTime;
+        public bool                                IsPreallocated;
 
 
         public BufferSlot()
         public BufferSlot()
         {
         {
@@ -22,6 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
             BufferState      = BufferState.Free;
             BufferState      = BufferState.Free;
             QueueTime        = TimeSpanType.Zero;
             QueueTime        = TimeSpanType.Zero;
             PresentationTime = TimeSpanType.Zero;
             PresentationTime = TimeSpanType.Zero;
+            IsPreallocated   = false;
         }
         }
     }
     }
 }
 }

+ 16 - 2
Ryujinx.HLE/HOS/Services/SurfaceFlinger/IGraphicBufferProducer.cs

@@ -72,6 +72,20 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
             public uint                  Height;
             public uint                  Height;
             public NativeWindowTransform TransformHint;
             public NativeWindowTransform TransformHint;
             public uint                  NumPendingBuffers;
             public uint                  NumPendingBuffers;
+            public ulong                 FrameNumber;
+
+            public void WriteToParcel(Parcel parcel)
+            {
+                parcel.WriteUInt32(Width);
+                parcel.WriteUInt32(Height);
+                parcel.WriteUnmanagedType(ref TransformHint);
+                parcel.WriteUInt32(NumPendingBuffers);
+
+                if (TransformHint.HasFlag(NativeWindowTransform.ReturnFrameNumber))
+                {
+                    parcel.WriteUInt64(FrameNumber);
+                }
+            }
         }
         }
 
 
         public ResultCode AdjustRefcount(int addVal, int type)
         public ResultCode AdjustRefcount(int addVal, int type)
@@ -174,7 +188,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
 
 
                     status = QueueBuffer(slot, ref queueInput, out queueOutput);
                     status = QueueBuffer(slot, ref queueInput, out queueOutput);
 
 
-                    outputParcel.WriteUnmanagedType(ref queueOutput);
+                    queueOutput.WriteToParcel(outputParcel);
 
 
                     outputParcel.WriteStatus(status);
                     outputParcel.WriteStatus(status);
 
 
@@ -214,7 +228,7 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
 
 
                     status = Connect(listener, api, producerControlledByApp, out queueOutput);
                     status = Connect(listener, api, producerControlledByApp, out queueOutput);
 
 
-                    outputParcel.WriteUnmanagedType(ref queueOutput);
+                    queueOutput.WriteToParcel(outputParcel);
 
 
                     outputParcel.WriteStatus(status);
                     outputParcel.WriteStatus(status);
 
 

+ 9 - 7
Ryujinx.HLE/HOS/Services/SurfaceFlinger/NativeWindowTransform.cs

@@ -5,12 +5,14 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
     [Flags]
     [Flags]
     enum NativeWindowTransform : uint
     enum NativeWindowTransform : uint
     {
     {
-        None           = 0,
-        FlipX          = 1,
-        FlipY          = 2,
-        Rotate90       = 4,
-        Rotate180      = FlipX | FlipY,
-        Rotate270      = Rotate90 | Rotate180,
-        InverseDisplay = 8
+        None              = 0,
+        FlipX             = 1,
+        FlipY             = 2,
+        Rotate90          = 4,
+        Rotate180         = FlipX | FlipY,
+        Rotate270         = Rotate90 | Rotate180,
+        InverseDisplay    = 8,
+        NoVSyncCapability = 0x10,
+        ReturnFrameNumber = 0x20
     }
     }
 }
 }