Ryujinx/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IGraphicBufferProducer.cs
Thog 36749c358d
SurfaceFlinger v2 (#981)
* Rewrite SurfaceFlinger

Reimplement accurately SurfaceFlinger (based on my 8.1.0 reversing of it)

TODO: support swap interval properly and reintroduce disabled "game vsync" support.

* Some fixes for SetBufferCount

* uncomment a test from last commit

* SurfaceFlinger: don't free the graphic buffer in SetBufferCount

* SurfaceFlinger: Implement swap interval correctly

* SurfaceFlinger: Reintegrate Game VSync toggle

* SurfaceFlinger: do not push a fence on buffer release on the consumer side

* Revert "SurfaceFlinger: do not push a fence on buffer release on the consumer side"

This reverts commit 586b52b0bfab2d11f361f4b59ab7b7141020bbad.

* Make the game vsync toggle work dynamically again

* Unregister producer's Binder object when closing layer

* Address ripinperi's comments

* Add a timeout on syncpoint wait operation

Syncpoint aren't supposed to be waited on for more than a second.

This effectively workaround issues caused by not having a channel
scheduling in place yet.

PS: Also introduce Android WaitForever warning about fence being not
signaled for 3s

* Fix a print of previous commit

* Address Ac_K's comments

* Address gdkchan's comments

* Address final comments
2020-04-22 14:10:27 +10:00

276 lines
9.7 KiB
C#

using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.SurfaceFlinger.Types;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
{
abstract class IGraphicBufferProducer : IBinder
{
public string InterfaceToken => "android.gui.IGraphicBufferProducer";
enum TransactionCode : uint
{
RequestBuffer = 1,
SetBufferCount,
DequeueBuffer,
DetachBuffer,
DetachNextBuffer,
AttachBuffer,
QueueBuffer,
CancelBuffer,
Query,
Connect,
Disconnect,
SetSidebandStream,
AllocateBuffers,
SetPreallocatedBuffer,
Reserved15,
GetBufferInfo,
GetBufferHistory
}
[StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x54)]
public struct QueueBufferInput : IFlattenable
{
public long Timestamp;
public int IsAutoTimestamp;
public Rect Crop;
public NativeWindowScalingMode ScalingMode;
public NativeWindowTransform Transform;
public uint StickyTransform;
public int Async;
public int SwapInterval;
public AndroidFence Fence;
public void Flatten(Parcel parcel)
{
parcel.WriteUnmanagedType(ref this);
}
public uint GetFdCount()
{
return 0;
}
public uint GetFlattenedSize()
{
return (uint)Unsafe.SizeOf<QueueBufferInput>();
}
public void Unflatten(Parcel parcel)
{
this = parcel.ReadUnmanagedType<QueueBufferInput>();
}
}
public struct QueueBufferOutput
{
public uint Width;
public uint Height;
public NativeWindowTransform TransformHint;
public uint NumPendingBuffers;
}
public ResultCode AdjustRefcount(int addVal, int type)
{
// TODO?
return ResultCode.Success;
}
public void GetNativeHandle(uint typeId, out KReadableEvent readableEvent)
{
if (typeId == 0xF)
{
readableEvent = GetWaitBufferFreeEvent();
}
else
{
throw new NotImplementedException($"Unimplemented native event type {typeId}!");
}
}
public void OnTransact(uint code, uint flags, Parcel inputParcel, Parcel outputParcel)
{
Status status = Status.Success;
int slot;
AndroidFence fence;
QueueBufferInput queueInput;
QueueBufferOutput queueOutput;
NativeWindowApi api;
AndroidStrongPointer<GraphicBuffer> graphicBuffer;
AndroidStrongPointer<AndroidFence> strongFence;
switch ((TransactionCode)code)
{
case TransactionCode.RequestBuffer:
slot = inputParcel.ReadInt32();
status = RequestBuffer(slot, out graphicBuffer);
outputParcel.WriteStrongPointer(ref graphicBuffer);
outputParcel.WriteStatus(status);
break;
case TransactionCode.SetBufferCount:
int bufferCount = inputParcel.ReadInt32();
status = SetBufferCount(bufferCount);
outputParcel.WriteStatus(status);
break;
case TransactionCode.DequeueBuffer:
bool async = inputParcel.ReadBoolean();
uint width = inputParcel.ReadUInt32();
uint height = inputParcel.ReadUInt32();
PixelFormat format = inputParcel.ReadUnmanagedType<PixelFormat>();
uint usage = inputParcel.ReadUInt32();
status = DequeueBuffer(out int dequeueSlot, out fence, async, width, height, format, usage);
strongFence = new AndroidStrongPointer<AndroidFence>(fence);
outputParcel.WriteInt32(dequeueSlot);
outputParcel.WriteStrongPointer(ref strongFence);
outputParcel.WriteStatus(status);
break;
case TransactionCode.DetachBuffer:
slot = inputParcel.ReadInt32();
status = DetachBuffer(slot);
outputParcel.WriteStatus(status);
break;
case TransactionCode.DetachNextBuffer:
status = DetachNextBuffer(out graphicBuffer, out fence);
strongFence = new AndroidStrongPointer<AndroidFence>(fence);
outputParcel.WriteStrongPointer(ref graphicBuffer);
outputParcel.WriteStrongPointer(ref strongFence);
outputParcel.WriteStatus(status);
break;
case TransactionCode.AttachBuffer:
graphicBuffer = inputParcel.ReadStrongPointer<GraphicBuffer>();
status = AttachBuffer(out slot, graphicBuffer);
outputParcel.WriteInt32(slot);
outputParcel.WriteStatus(status);
break;
case TransactionCode.QueueBuffer:
slot = inputParcel.ReadInt32();
queueInput = inputParcel.ReadFlattenable<QueueBufferInput>();
status = QueueBuffer(slot, ref queueInput, out queueOutput);
outputParcel.WriteUnmanagedType(ref queueOutput);
outputParcel.WriteStatus(status);
break;
case TransactionCode.CancelBuffer:
slot = inputParcel.ReadInt32();
fence = inputParcel.ReadFlattenable<AndroidFence>();
CancelBuffer(slot, ref fence);
outputParcel.WriteStatus(Status.Success);
break;
case TransactionCode.Query:
NativeWindowAttribute what = inputParcel.ReadUnmanagedType<NativeWindowAttribute>();
status = Query(what, out int outValue);
outputParcel.WriteInt32(outValue);
outputParcel.WriteStatus(status);
break;
case TransactionCode.Connect:
bool hasListener = inputParcel.ReadBoolean();
IProducerListener listener = null;
if (hasListener)
{
throw new NotImplementedException("Connect with a strong binder listener isn't implemented");
}
api = inputParcel.ReadUnmanagedType<NativeWindowApi>();
bool producerControlledByApp = inputParcel.ReadBoolean();
status = Connect(listener, api, producerControlledByApp, out queueOutput);
outputParcel.WriteUnmanagedType(ref queueOutput);
outputParcel.WriteStatus(status);
break;
case TransactionCode.Disconnect:
api = inputParcel.ReadUnmanagedType<NativeWindowApi>();
status = Disconnect(api);
outputParcel.WriteStatus(status);
break;
case TransactionCode.SetPreallocatedBuffer:
slot = inputParcel.ReadInt32();
graphicBuffer = inputParcel.ReadStrongPointer<GraphicBuffer>();
status = SetPreallocatedBuffer(slot, graphicBuffer);
outputParcel.WriteStatus(status);
break;
default:
throw new NotImplementedException($"Transaction {(TransactionCode)code} not implemented");
}
if (status != Status.Success)
{
Logger.PrintError(LogClass.SurfaceFlinger, $"Error returned by transaction {(TransactionCode)code}: {status}");
}
}
protected abstract KReadableEvent GetWaitBufferFreeEvent();
public abstract Status RequestBuffer(int slot, out AndroidStrongPointer<GraphicBuffer> graphicBuffer);
public abstract Status SetBufferCount(int bufferCount);
public abstract Status DequeueBuffer(out int slot, out AndroidFence fence, bool async, uint width, uint height, PixelFormat format, uint usage);
public abstract Status DetachBuffer(int slot);
public abstract Status DetachNextBuffer(out AndroidStrongPointer<GraphicBuffer> graphicBuffer, out AndroidFence fence);
public abstract Status AttachBuffer(out int slot, AndroidStrongPointer<GraphicBuffer> graphicBuffer);
public abstract Status QueueBuffer(int slot, ref QueueBufferInput input, out QueueBufferOutput output);
public abstract void CancelBuffer(int slot, ref AndroidFence fence);
public abstract Status Query(NativeWindowAttribute what, out int outValue);
public abstract Status Connect(IProducerListener listener, NativeWindowApi api, bool producerControlledByApp, out QueueBufferOutput output);
public abstract Status Disconnect(NativeWindowApi api);
public abstract Status SetPreallocatedBuffer(int slot, AndroidStrongPointer<GraphicBuffer> graphicBuffer);
}
}