// Copyright (c) 2021 homuler // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. using System; using System.Collections; using System.Collections.Generic; using UnityEngine; namespace Mediapipe.Unity { public class TextureFramePool : MonoBehaviour { private const string _TAG = nameof(TextureFramePool); [SerializeField] private int _poolSize = 10; private readonly object _formatLock = new object(); private int _textureWidth = 0; private int _textureHeight = 0; private TextureFormat _format = TextureFormat.RGBA32; private Queue _availableTextureFrames; /// /// key: TextureFrame's instance ID /// private Dictionary _textureFramesInUse; /// /// The total number of texture frames in the pool. /// public int frameCount { get { var availableTextureFramesCount = _availableTextureFrames == null ? 0 : _availableTextureFrames.Count; var textureFramesInUseCount = _textureFramesInUse == null ? 0 : _textureFramesInUse.Count; return availableTextureFramesCount + textureFramesInUseCount; } } private void Start() { _availableTextureFrames = new Queue(_poolSize); _textureFramesInUse = new Dictionary(); } private void OnDestroy() { lock (((ICollection)_availableTextureFrames).SyncRoot) { _availableTextureFrames.Clear(); _availableTextureFrames = null; } lock (((ICollection)_textureFramesInUse).SyncRoot) { foreach (var textureFrame in _textureFramesInUse.Values) { textureFrame.OnRelease.RemoveListener(OnTextureFrameRelease); } _textureFramesInUse.Clear(); _textureFramesInUse = null; } } public void ResizeTexture(int textureWidth, int textureHeight, TextureFormat format) { lock (_formatLock) { _textureWidth = textureWidth; _textureHeight = textureHeight; _format = format; } } public void ResizeTexture(int textureWidth, int textureHeight) { ResizeTexture(textureWidth, textureHeight, _format); } public bool TryGetTextureFrame(out TextureFrame outFrame) { TextureFrame nextFrame = null; lock (((ICollection)_availableTextureFrames).SyncRoot) { if (_poolSize <= frameCount) { while (_availableTextureFrames.Count > 0) { var textureFrame = _availableTextureFrames.Dequeue(); if (!IsStale(textureFrame)) { nextFrame = textureFrame; break; } } } else { nextFrame = CreateNewTextureFrame(); } } if (nextFrame == null) { outFrame = null; return false; } lock (((ICollection)_textureFramesInUse).SyncRoot) { _textureFramesInUse.Add(nextFrame.GetInstanceID(), nextFrame); } nextFrame.WaitUntilReleased(); outFrame = nextFrame; return true; } private void OnTextureFrameRelease(TextureFrame textureFrame) { lock (((ICollection)_textureFramesInUse).SyncRoot) { if (!_textureFramesInUse.Remove(textureFrame.GetInstanceID())) { // won't be run Logger.LogWarning(_TAG, "The released texture does not belong to the pool"); return; } if (frameCount > _poolSize || IsStale(textureFrame)) { return; } _availableTextureFrames.Enqueue(textureFrame); } } private bool IsStale(TextureFrame textureFrame) { lock (_formatLock) { return textureFrame.width != _textureWidth || textureFrame.height != _textureHeight; } } private TextureFrame CreateNewTextureFrame() { var textureFrame = new TextureFrame(_textureWidth, _textureHeight, _format); textureFrame.OnRelease.AddListener(OnTextureFrameRelease); return textureFrame; } } }