private void PostFrameBuffer(Layer layer, BufferItem item) { int frameBufferWidth = item.GraphicBuffer.Object.Width; int frameBufferHeight = item.GraphicBuffer.Object.Height; int nvMapHandle = item.GraphicBuffer.Object.Buffer.Surfaces[0].NvMapHandle; if (nvMapHandle == 0) { nvMapHandle = item.GraphicBuffer.Object.Buffer.NvMapId; } ulong bufferOffset = (ulong)item.GraphicBuffer.Object.Buffer.Surfaces[0].Offset; NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(layer.Owner, nvMapHandle); ulong frameBufferAddress = map.Address + bufferOffset; Format format = ConvertColorFormat(item.GraphicBuffer.Object.Buffer.Surfaces[0].ColorFormat); int bytesPerPixel = format == Format.B5G6R5Unorm || format == Format.R4G4B4A4Unorm ? 2 : 4; int gobBlocksInY = 1 << item.GraphicBuffer.Object.Buffer.Surfaces[0].BlockHeightLog2; // Note: Rotation is being ignored. Rect cropRect = item.Crop; bool flipX = item.Transform.HasFlag(NativeWindowTransform.FlipX); bool flipY = item.Transform.HasFlag(NativeWindowTransform.FlipY); AspectRatio aspectRatio = _device.Configuration.AspectRatio; bool isStretched = aspectRatio == AspectRatio.Stretched; ImageCrop crop = new ImageCrop( cropRect.Left, cropRect.Right, cropRect.Top, cropRect.Bottom, flipX, flipY, isStretched, aspectRatio.ToFloatX(), aspectRatio.ToFloatY()); TextureCallbackInformation textureCallbackInformation = new TextureCallbackInformation { Layer = layer, Item = item }; _device.Gpu.Window.EnqueueFrameThreadSafe( layer.Owner, frameBufferAddress, frameBufferWidth, frameBufferHeight, 0, false, gobBlocksInY, format, bytesPerPixel, crop, AcquireBuffer, ReleaseBuffer, textureCallbackInformation); if (item.Fence.FenceCount == 0) { _device.Gpu.Window.SignalFrameReady(); _device.Gpu.GPFifo.Interrupt(); } else { item.Fence.RegisterCallback(_device.Gpu, (x) => { _device.Gpu.Window.SignalFrameReady(); _device.Gpu.GPFifo.Interrupt(); }); } }
public void OnFrameAvailable(ref BufferItem item) { _device.Statistics.RecordGameFrameTime(); }
public void OnFrameReplaced(ref BufferItem item) { _device.Statistics.RecordGameFrameTime(); }
private void PostFrameBuffer(Layer layer, BufferItem item) { int frameBufferWidth = item.GraphicBuffer.Object.Width; int frameBufferHeight = item.GraphicBuffer.Object.Height; int nvMapHandle = item.GraphicBuffer.Object.Buffer.Surfaces[0].NvMapHandle; if (nvMapHandle == 0) { nvMapHandle = item.GraphicBuffer.Object.Buffer.NvMapId; } int bufferOffset = item.GraphicBuffer.Object.Buffer.Surfaces[0].Offset; NvMapHandle map = NvMapDeviceFile.GetMapFromHandle(layer.Owner, nvMapHandle); ulong frameBufferAddress = (ulong)(map.Address + bufferOffset); Format format = ConvertColorFormat(item.GraphicBuffer.Object.Buffer.Surfaces[0].ColorFormat); int bytesPerPixel = format == Format.B5G6R5Unorm || format == Format.R4G4B4A4Unorm ? 2 : 4; int gobBlocksInY = 1 << item.GraphicBuffer.Object.Buffer.Surfaces[0].BlockHeightLog2; // Note: Rotation is being ignored. Rect cropRect = item.Crop; bool flipX = item.Transform.HasFlag(NativeWindowTransform.FlipX); bool flipY = item.Transform.HasFlag(NativeWindowTransform.FlipY); ImageCrop crop = new ImageCrop( cropRect.Left, cropRect.Right, cropRect.Top, cropRect.Bottom, flipX, flipY); // Enforce that dequeueBuffer wait for the next vblank _vblankFence.NvFences[0].Value++; TextureCallbackInformation textureCallbackInformation = new TextureCallbackInformation { Layer = layer, Item = item, Fence = _vblankFence }; _device.Gpu.Window.EnqueueFrameThreadSafe( frameBufferAddress, frameBufferWidth, frameBufferHeight, 0, false, gobBlocksInY, format, bytesPerPixel, crop, AcquireBuffer, ReleaseBuffer, textureCallbackInformation); }
public Status AcquireBuffer(out BufferItem bufferItem, ulong expectedPresent) { lock (Core.Lock) { int numAcquiredBuffers = 0; for (int i = 0; i < Core.MaxBufferCountCached; i++) { if (Core.Slots[i].BufferState == BufferState.Acquired) { numAcquiredBuffers++; } } if (numAcquiredBuffers > Core.MaxAcquiredBufferCount) { bufferItem = null; Logger.PrintDebug(LogClass.SurfaceFlinger, $"Max acquired buffer count reached: {numAcquiredBuffers} (max: {Core.MaxAcquiredBufferCount})"); return(Status.InvalidOperation); } if (Core.Queue.Count == 0) { bufferItem = null; return(Status.NoBufferAvailaible); } if (expectedPresent != 0) { // TODO: support this for advanced presenting. throw new NotImplementedException(); } bufferItem = Core.Queue[0]; if (Core.StillTracking(ref bufferItem)) { Core.Slots[bufferItem.Slot].AcquireCalled = true; Core.Slots[bufferItem.Slot].NeedsCleanupOnRelease = true; Core.Slots[bufferItem.Slot].BufferState = BufferState.Acquired; Core.Slots[bufferItem.Slot].Fence = AndroidFence.NoFence; ulong targetFrameNumber = Core.Slots[bufferItem.Slot].FrameNumber; for (int i = 0; i < Core.BufferHistory.Length; i++) { if (Core.BufferHistory[i].FrameNumber == targetFrameNumber) { Core.BufferHistory[i].State = BufferState.Acquired; break; } } } if (bufferItem.AcquireCalled) { bufferItem.GraphicBuffer.Reset(); } Core.Queue.RemoveAt(0); Core.CheckSystemEventsLocked(Core.GetMaxBufferCountLocked(true)); Core.SignalDequeueEvent(); } return(Status.Success); }
public virtual void OnFrameReplaced(ref BufferItem item) { _listener?.OnFrameReplaced(ref item); }
public virtual void OnFrameAvailable(ref BufferItem item) { _listener?.OnFrameAvailable(ref item); }
public override Status QueueBuffer(int slot, ref QueueBufferInput input, out QueueBufferOutput output) { output = default; switch (input.ScalingMode) { case NativeWindowScalingMode.Freeze: case NativeWindowScalingMode.ScaleToWindow: case NativeWindowScalingMode.ScaleCrop: case NativeWindowScalingMode.Unknown: case NativeWindowScalingMode.NoScaleCrop: break; default: return(Status.BadValue); } BufferItem item = new BufferItem(); IConsumerListener frameAvailableListener = null; IConsumerListener frameReplaceListener = null; lock (Core.Lock) { if (Core.IsAbandoned) { return(Status.NoInit); } int maxBufferCount = Core.GetMaxBufferCountLocked(input.Async != 0); if (input.Async != 0 && Core.OverrideMaxBufferCount != 0 && Core.OverrideMaxBufferCount < maxBufferCount) { return(Status.BadValue); } if (slot < 0 || slot >= Core.Slots.Length || !Core.IsOwnedByProducerLocked(slot)) { return(Status.BadValue); } if (!Core.Slots[slot].RequestBufferCalled) { Logger.PrintError(LogClass.SurfaceFlinger, $"Slot {slot} was queued without requesting a buffer"); return(Status.BadValue); } input.Crop.Intersect(Core.Slots[slot].GraphicBuffer.Object.ToRect(), out Rect croppedRect); if (croppedRect != input.Crop) { return(Status.BadValue); } Core.Slots[slot].Fence = input.Fence; Core.Slots[slot].BufferState = BufferState.Queued; Core.FrameCounter++; Core.Slots[slot].FrameNumber = Core.FrameCounter; Core.Slots[slot].QueueTime = TimeSpanType.FromTimeSpan(ARMeilleure.State.ExecutionContext.ElapsedTime); Core.Slots[slot].PresentationTime = TimeSpanType.Zero; item.AcquireCalled = Core.Slots[slot].AcquireCalled; item.Crop = input.Crop; item.Transform = input.Transform; item.TransformToDisplayInverse = (input.Transform & NativeWindowTransform.InverseDisplay) == NativeWindowTransform.InverseDisplay; item.ScalingMode = input.ScalingMode; item.Timestamp = input.Timestamp; item.IsAutoTimestamp = input.IsAutoTimestamp != 0; item.SwapInterval = input.SwapInterval; item.FrameNumber = Core.FrameCounter; item.Slot = slot; item.Fence = input.Fence; item.IsDroppable = Core.DequeueBufferCannotBlock || input.Async != 0; item.GraphicBuffer.Set(Core.Slots[slot].GraphicBuffer); item.GraphicBuffer.Object.IncrementNvMapHandleRefCount(Core.Owner); Core.BufferHistoryPosition = (Core.BufferHistoryPosition + 1) % BufferQueueCore.BufferHistoryArraySize; Core.BufferHistory[Core.BufferHistoryPosition] = new BufferInfo { FrameNumber = Core.FrameCounter, QueueTime = Core.Slots[slot].QueueTime, State = BufferState.Queued }; _stickyTransform = input.StickyTransform; if (Core.Queue.Count == 0) { Core.Queue.Add(item); frameAvailableListener = Core.ConsumerListener; } else { BufferItem frontItem = Core.Queue[0]; if (frontItem.IsDroppable) { if (Core.StillTracking(ref frontItem)) { Core.Slots[slot].BufferState = BufferState.Free; Core.Slots[slot].FrameNumber = 0; } Core.Queue.RemoveAt(0); Core.Queue.Insert(0, item); frameReplaceListener = Core.ConsumerListener; } else { Core.Queue.Add(item); frameAvailableListener = Core.ConsumerListener; } } Core.BufferHasBeenQueued = true; Core.SignalDequeueEvent(); Core.CheckSystemEventsLocked(maxBufferCount); output = new QueueBufferOutput { Width = (uint)Core.DefaultWidth, Height = (uint)Core.DefaultHeight, TransformHint = Core.TransformHint, NumPendingBuffers = (uint)Core.Queue.Count }; _callbackTicket = _nextCallbackTicket++; } lock (_callbackLock) { while (_callbackTicket != _currentCallbackTicket) { Monitor.Wait(_callbackLock); } frameAvailableListener?.OnFrameAvailable(ref item); frameReplaceListener?.OnFrameReplaced(ref item); _currentCallbackTicket++; Monitor.PulseAll(_callbackLock); } return(Status.Success); }