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
This commit is contained in:
Thog 2020-06-02 17:58:19 +02:00 committed by GitHub
parent 44d7fcff39
commit bcb7761eac
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 138 additions and 59 deletions

View file

@ -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);

View file

@ -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]; BufferHistory = new BufferInfo[BufferHistoryArraySize];
EnableExternalEvent = true; 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();
} }
} }

View file

@ -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(); int preallocatedBufferCount = GetPreallocatedBufferCountLocked();
Core.FreeAllBuffersLocked();
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; Core.OverrideMaxBufferCount = GetPreallocatedBufferCountLocked();
for (int i = 0; i < Core.Slots.Length; i++)
{
if (!Core.Slots[i].GraphicBuffer.IsNull)
{
bufferCount++;
}
}
Core.OverrideMaxBufferCount = bufferCount;
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(); allBufferFreed = false;
Core.FreeAllBuffersLocked();
Core.SignalDequeueEvent();
Core.SignalWaitBufferFreeEvent();
Core.SignalFrameAvailableEvent();
cleared = true;
break; break;
} }
} }
if (allBufferFreed)
{
Core.Queue.Clear();
Core.FreeAllBuffersLocked();
Core.SignalDequeueEvent();
Core.SignalWaitBufferFreeEvent();
Core.SignalFrameAvailableEvent();
return Status.Success;
}
} }
// The dequeue event must not be signaled two times in case of clean up, Core.SignalDequeueEvent();
// but for some reason, it still signals the wait buffer free event two times...
if (!cleared)
{
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); if (Core.Slots[slot].BufferState == BufferState.Free && !Core.Slots[slot].GraphicBuffer.IsNull && !Core.Slots[slot].IsPreallocated)
returnStatus |= Status.ReleaseAllBuffers; {
Core.FreeBufferLocked(slot);
returnStatus |= Status.ReleaseAllBuffers;
}
} }
} }

View file

@ -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;
} }
} }
} }

View file

@ -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);

View file

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