part of dart._engine; // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. /// A monotonically increasing frame number being rendered. /// /// Used for debugging only. int debugFrameNumber = 1; List> frameReferences = >[]; /// A temporary reference to a value of type [V]. /// /// The value automatically gets set to null after the current frame is /// rendered. /// /// It is useful to think of this as a weak reference that's scoped to a /// single frame. class FrameReference { /// Creates a frame reference to a value. FrameReference([this.value]) { frameReferences.add(this); } /// The current value of this reference. V? value; } /// Cache where items cached before frame(N) is committed, can be reused in /// frame(N+1) and are evicted if not. /// /// A typical use case is image elements. As images are created and added to /// DOM when painting a scene they are cached and if possible reused on next /// update. If the next update does not reuse the element, it is evicted. /// /// Maps are lazily allocated since many pictures don't contain cachable images /// at all. class CrossFrameCache { // Cached items in a scene. Map>>? _cache; // Cached items that have been committed, ready for reuse on next frame. Map>>? _reusablePool; // Called when a scene or picture update is committed. void commitFrame() { // Evict unused items from prior frame. if (_reusablePool != null) { for (final List<_CrossFrameCacheItem> items in _reusablePool!.values) { for (final _CrossFrameCacheItem item in items) { item.evict(); } } } // Move cached items to reusable pool. _reusablePool = _cache; _cache = null; } /// Caches an item for reuse on next update frame. /// /// Duplicate keys are allowed. For example the same image url may be used /// to create multiple instances of [ImageElement] to be reused in the future. void cache(String key, T value, [CrossFrameCacheEvictCallback? callback]) { _addToCache(key, _CrossFrameCacheItem(value, callback)); } void _addToCache(String key, _CrossFrameCacheItem item) { _cache ??= >>{}; (_cache![key] ??= <_CrossFrameCacheItem>[]).add(item); } /// Given a key, consumes an item that has been cached in a prior frame. T? reuse(String key) { if (_reusablePool == null) { return null; } final List<_CrossFrameCacheItem>? items = _reusablePool![key]; if (items == null || items.isEmpty) { return null; } final _CrossFrameCacheItem item = items.removeAt(0); _addToCache(key, item); return item.value; } } class _CrossFrameCacheItem { _CrossFrameCacheItem(this.value, this.evictCallback); final T value; final CrossFrameCacheEvictCallback? evictCallback; void evict() { if (evictCallback != null) { evictCallback!(value); } } } typedef CrossFrameCacheEvictCallback = void Function(T value);