| // 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<FrameReference<dynamic>> frameReferences = <FrameReference<dynamic>>[]; |
| |
| /// 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<V> { |
| /// 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<T> { |
| // Cached items in a scene. |
| Map<String, List<_CrossFrameCacheItem<T>>>? _cache; |
| |
| // Cached items that have been committed, ready for reuse on next frame. |
| Map<String, List<_CrossFrameCacheItem<T>>>? _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<T>> items in _reusablePool!.values) { |
| for (final _CrossFrameCacheItem<T> 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<T>? callback]) { |
| _addToCache(key, _CrossFrameCacheItem<T>(value, callback)); |
| } |
| |
| void _addToCache(String key, _CrossFrameCacheItem<T> item) { |
| _cache ??= <String, List<_CrossFrameCacheItem<T>>>{}; |
| (_cache![key] ??= <_CrossFrameCacheItem<T>>[]).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<T>>? items = _reusablePool![key]; |
| if (items == null || items.isEmpty) { |
| return null; |
| } |
| final _CrossFrameCacheItem<T> item = items.removeAt(0); |
| _addToCache(key, item); |
| return item.value; |
| } |
| } |
| |
| class _CrossFrameCacheItem<T> { |
| final T value; |
| final CrossFrameCacheEvictCallback<T>? evictCallback; |
| _CrossFrameCacheItem(this.value, this.evictCallback); |
| void evict() { |
| if (evictCallback != null) { |
| evictCallback!(value); |
| } |
| } |
| } |
| |
| typedef CrossFrameCacheEvictCallback<T> = void Function(T value); |