1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
//! Per-frame-slot memo of what a descriptor set already points at.
//!
//! Several sets are re-pointed at the top of every frame so a slot that missed a
//! rebuild still binds live handles rather than retired ones. The handles only
//! actually move on a rebuild, so the steady state is a run of byte-identical
//! writes. This records what each slot was last given and reports whether a new
//! value is a change, letting the caller skip the write while keeping the
//! guarantee: any handle that differs from the one the set holds still fires.
// One entry per frame in flight. `T` is whatever bundle of handles the set's
// dynamic bindings are written from.
pub(in crate::vulkan) struct WireCache<T> {
wired: Vec<Option<T>>,
}
impl<T: Copy + PartialEq> WireCache<T> {
pub(in crate::vulkan) fn new(frames: usize) -> Self {
Self {
wired: (0..frames).map(|_| None).collect(),
}
}
// True when `value` differs from what `frame_idx` was last wired with, which
// also records it as the slot's new contents. A frame index past the end
// reports stale and memoizes nothing, so an out-of-range caller degrades to
// the unconditional write rather than silently skipping one.
pub(in crate::vulkan) fn changed(&mut self, frame_idx: usize, value: T) -> bool {
match self.wired.get_mut(frame_idx) {
Some(slot) if *slot == Some(value) => false,
Some(slot) => {
*slot = Some(value);
true
}
None => true,
}
}
// Forget every slot, so the next frame rewires unconditionally. Called when
// the sets themselves are reallocated or their bindings rewritten out of
// band.
pub(in crate::vulkan) fn reset(&mut self) {
self.wired.fill(None);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn first_write_of_each_slot_is_a_change() {
let mut cache = WireCache::new(2);
assert!(cache.changed(0, 7u32));
assert!(cache.changed(1, 7u32));
}
#[test]
fn repeating_a_value_in_the_same_slot_is_not() {
let mut cache = WireCache::new(2);
assert!(cache.changed(0, 7u32));
assert!(!cache.changed(0, 7u32));
assert!(!cache.changed(0, 7u32));
}
#[test]
fn slots_memoize_independently() {
let mut cache = WireCache::new(2);
assert!(cache.changed(0, 7u32));
// Slot 1 has never seen the value even though slot 0 holds it.
assert!(cache.changed(1, 7u32));
assert!(!cache.changed(0, 7u32));
}
#[test]
fn a_new_value_fires_again() {
let mut cache = WireCache::new(1);
assert!(cache.changed(0, 7u32));
assert!(cache.changed(0, 8u32));
assert!(!cache.changed(0, 8u32));
assert!(cache.changed(0, 7u32));
}
#[test]
fn reset_forgets_every_slot() {
let mut cache = WireCache::new(2);
cache.changed(0, 7u32);
cache.changed(1, 9u32);
cache.reset();
assert!(cache.changed(0, 7u32));
assert!(cache.changed(1, 9u32));
}
#[test]
fn an_out_of_range_slot_always_reports_stale() {
let mut cache = WireCache::new(1);
assert!(cache.changed(4, 7u32));
assert!(cache.changed(4, 7u32));
}
}