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
This commit is contained in:
parent
03711dd7b5
commit
36749c358d
50 changed files with 3416 additions and 831 deletions
|
@ -0,0 +1,276 @@
|
|||
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);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue