using Ryujinx.Common.Logging; using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu.Synchronization; using Ryujinx.HLE.HOS.Services.Nv.Types; using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger { [StructLayout(LayoutKind.Sequential, Pack = 1, Size = 0x24)] struct AndroidFence : IFlattenable { public int FenceCount; private byte _fenceStorageStart; private Span _storage => MemoryMarshal.CreateSpan(ref _fenceStorageStart, Unsafe.SizeOf() * 4); public Span NvFences => MemoryMarshal.Cast(_storage); public static AndroidFence NoFence { get { AndroidFence fence = new AndroidFence { FenceCount = 0 }; fence.NvFences[0].Id = NvFence.InvalidSyncPointId; return fence; } } public void AddFence(NvFence fence) { NvFences[FenceCount++] = fence; } public void WaitForever(GpuContext gpuContext) { bool hasTimeout = Wait(gpuContext, TimeSpan.FromMilliseconds(3000)); if (hasTimeout) { Logger.Error?.Print(LogClass.SurfaceFlinger, "Android fence didn't signal in 3000 ms"); Wait(gpuContext, Timeout.InfiniteTimeSpan); } } public bool Wait(GpuContext gpuContext, TimeSpan timeout) { for (int i = 0; i < FenceCount; i++) { bool hasTimeout = NvFences[i].Wait(gpuContext, timeout); if (hasTimeout) { return true; } } return false; } public void RegisterCallback(GpuContext gpuContext, Action callback) { ref NvFence fence = ref NvFences[FenceCount - 1]; if (fence.IsValid()) { gpuContext.Synchronization.RegisterCallbackOnSyncpoint(fence.Id, fence.Value, callback); } else { callback(null); } } public uint GetFlattenedSize() { return (uint)Unsafe.SizeOf(); } public uint GetFdCount() { return 0; } public void Flatten(Parcel parcel) { parcel.WriteUnmanagedType(ref this); } public void Unflatten(Parcel parcel) { this = parcel.ReadUnmanagedType(); } } }