epoch_cache.rs 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2026 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. use std::sync::{
  19. atomic::{AtomicU32, Ordering},
  20. Arc,
  21. };
  22. use parking_lot::Mutex as SyncMutex;
  23. use super::{EpochIndex, Renderer};
  24. /// Cache for draw outputs holding epoch-scoped GPU resources (buffers,
  25. /// textures, anims). Such resources only exist for the current UI epoch;
  26. /// when the epoch is bumped (e.g. the GL context is recreated on Android)
  27. /// every resource from the dead epoch is gone, so stale entries must
  28. /// never be served.
  29. pub struct EpochCache<T> {
  30. /// Shared with the `Renderer`; bumped on UI restart
  31. epoch: Arc<AtomicU32>,
  32. inner: SyncMutex<Option<(EpochIndex, T)>>,
  33. }
  34. impl<T: Clone> EpochCache<T> {
  35. pub fn new(renderer: &Renderer) -> Self {
  36. Self { epoch: renderer.epoch.clone(), inner: SyncMutex::new(None) }
  37. }
  38. /// Returns the cached value, or None when empty, cleared, or built
  39. /// under a dead epoch.
  40. pub fn get(&self) -> Option<T> {
  41. let mut cache = self.inner.lock();
  42. // A stale entry belongs to a dead epoch and can never become
  43. // valid again, so evict it here instead of holding it in memory
  44. // until the next set().
  45. match cache.take() {
  46. Some((e, v)) if e == self.epoch.load(Ordering::Relaxed) => {
  47. *cache = Some((e, v.clone()));
  48. Some(v)
  49. }
  50. _ => None,
  51. }
  52. }
  53. /// Returns the cached value, computing and storing it via `f` when
  54. /// absent, cleared, or stale. `f` runs under the lock so a concurrent
  55. /// `clear()` cannot be lost between the read and the store.
  56. pub fn get_or_insert_with(&self, f: impl FnOnce() -> T) -> T {
  57. let mut cache = self.inner.lock();
  58. let cur = self.epoch.load(Ordering::Relaxed);
  59. if let Some((e, v)) = &*cache {
  60. if *e == cur {
  61. return v.clone()
  62. }
  63. }
  64. let v = f();
  65. *cache = Some((cur, v.clone()));
  66. v
  67. }
  68. pub fn set(&self, value: T) {
  69. let e = self.epoch.load(Ordering::Relaxed);
  70. *self.inner.lock() = Some((e, value));
  71. }
  72. pub fn clear(&self) {
  73. *self.inner.lock() = None;
  74. }
  75. }
  76. /// Remembers the UI epoch last seen and reports when it has changed, e.g.
  77. /// to drop caches holding epoch-scoped GPU resources.
  78. pub struct EpochTracker {
  79. epoch: Arc<AtomicU32>,
  80. cached: EpochIndex,
  81. }
  82. impl EpochTracker {
  83. pub fn new(renderer: &Renderer) -> Self {
  84. Self { epoch: renderer.epoch.clone(), cached: renderer.epoch.load(Ordering::Relaxed) }
  85. }
  86. /// Returns true when the epoch changed since the last call
  87. pub fn changed(&mut self) -> bool {
  88. let cur = self.epoch.load(Ordering::Relaxed);
  89. if cur == self.cached {
  90. return false
  91. }
  92. self.cached = cur;
  93. true
  94. }
  95. }