1use core::{fmt, iter, ops::Range};
35
36use smallvec::SmallVec;
37
38mod buffer;
39mod texture;
40
41pub(crate) use buffer::{BufferInitTracker, BufferInitTrackerAction};
42pub(crate) use texture::{
43 has_copy_partial_init_tracker_coverage, TextureInitRange, TextureInitTracker,
44 TextureInitTrackerAction,
45};
46
47#[derive(Debug, Clone, Copy)]
48pub(crate) enum MemoryInitKind {
49 ImplicitlyInitialized,
52 NeedsInitializedMemory,
55}
56
57type UninitializedRangeVec<Idx> = SmallVec<[Range<Idx>; 1]>;
60
61#[derive(Debug, Clone)]
63pub(crate) struct InitTracker<Idx: Ord + Copy + Default> {
64 uninitialized_ranges: UninitializedRangeVec<Idx>,
67}
68
69pub(crate) struct UninitializedIter<'a, Idx: fmt::Debug + Ord + Copy> {
70 uninitialized_ranges: &'a UninitializedRangeVec<Idx>,
71 drain_range: Range<Idx>,
72 next_index: usize,
73}
74
75impl<'a, Idx> Iterator for UninitializedIter<'a, Idx>
76where
77 Idx: fmt::Debug + Ord + Copy,
78{
79 type Item = Range<Idx>;
80
81 fn next(&mut self) -> Option<Self::Item> {
82 self.uninitialized_ranges
83 .get(self.next_index)
84 .and_then(|range| {
85 if range.start < self.drain_range.end {
86 self.next_index += 1;
87 Some(
88 range.start.max(self.drain_range.start)
89 ..range.end.min(self.drain_range.end),
90 )
91 } else {
92 None
93 }
94 })
95 }
96}
97
98pub(crate) struct InitTrackerDrain<'a, Idx: fmt::Debug + Ord + Copy> {
99 uninitialized_ranges: &'a mut UninitializedRangeVec<Idx>,
100 drain_range: Range<Idx>,
101 first_index: usize,
102 next_index: usize,
103}
104
105impl<'a, Idx> Iterator for InitTrackerDrain<'a, Idx>
106where
107 Idx: fmt::Debug + Ord + Copy,
108{
109 type Item = Range<Idx>;
110
111 fn next(&mut self) -> Option<Self::Item> {
112 if let Some(r) = self
113 .uninitialized_ranges
114 .get(self.next_index)
115 .and_then(|range| {
116 if range.start < self.drain_range.end {
117 Some(range.clone())
118 } else {
119 None
120 }
121 })
122 {
123 self.next_index += 1;
124 Some(r.start.max(self.drain_range.start)..r.end.min(self.drain_range.end))
125 } else {
126 let num_affected = self.next_index - self.first_index;
127 if num_affected == 0 {
128 return None;
129 }
130 let first_range = &mut self.uninitialized_ranges[self.first_index];
131
132 if num_affected == 1
134 && first_range.start < self.drain_range.start
135 && first_range.end > self.drain_range.end
136 {
137 let old_start = first_range.start;
138 first_range.start = self.drain_range.end;
139 self.uninitialized_ranges
140 .insert(self.first_index, old_start..self.drain_range.start);
141 }
142 else {
144 let remove_start = if first_range.start >= self.drain_range.start {
145 self.first_index
146 } else {
147 first_range.end = self.drain_range.start;
148 self.first_index + 1
149 };
150
151 let last_range = &mut self.uninitialized_ranges[self.next_index - 1];
152 let remove_end = if last_range.end <= self.drain_range.end {
153 self.next_index
154 } else {
155 last_range.start = self.drain_range.end;
156 self.next_index - 1
157 };
158
159 self.uninitialized_ranges.drain(remove_start..remove_end);
160 }
161
162 None
163 }
164 }
165}
166
167impl<'a, Idx> Drop for InitTrackerDrain<'a, Idx>
168where
169 Idx: fmt::Debug + Ord + Copy,
170{
171 fn drop(&mut self) {
172 if self.next_index <= self.first_index {
173 for _ in self {}
174 }
175 }
176}
177
178impl<Idx> InitTracker<Idx>
179where
180 Idx: fmt::Debug + Ord + Copy + Default,
181{
182 pub(crate) fn new(size: Idx) -> Self {
183 Self {
184 uninitialized_ranges: iter::once(Idx::default()..size).collect(),
185 }
186 }
187
188 pub(crate) fn check(&self, query_range: Range<Idx>) -> Option<Range<Idx>> {
197 let index = self
198 .uninitialized_ranges
199 .partition_point(|r| r.end <= query_range.start);
200 self.uninitialized_ranges
201 .get(index)
202 .and_then(|start_range| {
203 if start_range.start < query_range.end {
204 let start = start_range.start.max(query_range.start);
205 match self.uninitialized_ranges.get(index + 1) {
206 Some(next_range) => {
207 if next_range.start < query_range.end {
208 Some(start..query_range.end)
211 } else {
212 Some(start..start_range.end.min(query_range.end))
213 }
214 }
215 None => Some(start..start_range.end.min(query_range.end)),
216 }
217 } else {
218 None
219 }
220 })
221 }
222
223 pub(crate) fn uninitialized(&mut self, drain_range: Range<Idx>) -> UninitializedIter<'_, Idx> {
225 let index = self
226 .uninitialized_ranges
227 .partition_point(|r| r.end <= drain_range.start);
228 UninitializedIter {
229 drain_range,
230 uninitialized_ranges: &self.uninitialized_ranges,
231 next_index: index,
232 }
233 }
234
235 pub(crate) fn drain(&mut self, drain_range: Range<Idx>) -> InitTrackerDrain<'_, Idx> {
237 if drain_range.is_empty() {
238 let len = self.uninitialized_ranges.len();
239 InitTrackerDrain {
240 drain_range,
241 uninitialized_ranges: &mut self.uninitialized_ranges,
242 first_index: len,
243 next_index: len,
244 }
245 } else {
246 let index = self
247 .uninitialized_ranges
248 .partition_point(|r| r.end <= drain_range.start);
249 InitTrackerDrain {
250 drain_range,
251 uninitialized_ranges: &mut self.uninitialized_ranges,
252 first_index: index,
253 next_index: index,
254 }
255 }
256 }
257}
258
259impl InitTracker<u32> {
260 pub(crate) fn discard(&mut self, pos: u32) {
262 let r_idx = self.uninitialized_ranges.partition_point(|r| r.end < pos);
264 if let Some(r) = self.uninitialized_ranges.get(r_idx) {
265 if r.end == pos {
267 if let Some(right) = self.uninitialized_ranges.get(r_idx + 1) {
269 if right.start == pos + 1 {
270 self.uninitialized_ranges[r_idx] = r.start..right.end;
271 self.uninitialized_ranges.remove(r_idx + 1);
272 return;
273 }
274 }
275 self.uninitialized_ranges[r_idx] = r.start..(pos + 1);
276 } else if r.start > pos {
277 if r.start == pos + 1 {
279 self.uninitialized_ranges[r_idx] = pos..r.end;
280 } else {
281 self.uninitialized_ranges.insert(r_idx, pos..(pos + 1));
283 }
284 }
285 } else {
286 self.uninitialized_ranges.push(pos..(pos + 1));
287 }
288 }
289}
290
291#[cfg(test)]
292mod test {
293 use alloc::{vec, vec::Vec};
294 use core::ops::Range;
295
296 type Tracker = super::InitTracker<u32>;
297
298 #[test]
299 fn check_for_newly_created_tracker() {
300 let tracker = Tracker::new(10);
301 assert_eq!(tracker.check(0..10), Some(0..10));
302 assert_eq!(tracker.check(0..3), Some(0..3));
303 assert_eq!(tracker.check(3..4), Some(3..4));
304 assert_eq!(tracker.check(4..10), Some(4..10));
305 }
306
307 #[test]
308 fn check_for_drained_tracker() {
309 let mut tracker = Tracker::new(10);
310 tracker.drain(0..10);
311 assert_eq!(tracker.check(0..10), None);
312 assert_eq!(tracker.check(0..3), None);
313 assert_eq!(tracker.check(3..4), None);
314 assert_eq!(tracker.check(4..10), None);
315 }
316
317 #[test]
318 fn check_for_partially_filled_tracker() {
319 let mut tracker = Tracker::new(25);
320 tracker.drain(0..5);
322 tracker.drain(10..15);
323 tracker.drain(20..25);
324
325 assert_eq!(tracker.check(0..25), Some(5..25)); assert_eq!(tracker.check(0..5), None); assert_eq!(tracker.check(3..8), Some(5..8)); assert_eq!(tracker.check(3..17), Some(5..17)); assert_eq!(tracker.check(8..22), Some(8..22));
333 assert_eq!(tracker.check(17..22), Some(17..20));
335 assert_eq!(tracker.check(20..25), None);
337 }
338
339 #[test]
340 fn drain_already_drained() {
341 let mut tracker = Tracker::new(30);
342 tracker.drain(10..20);
343
344 tracker.drain(5..15); tracker.drain(15..25); tracker.drain(0..30); tracker.drain(0..30);
351
352 assert_eq!(tracker.check(0..30), None);
353 }
354
355 #[test]
356 fn drain_never_returns_ranges_twice_for_same_range() {
357 let mut tracker = Tracker::new(19);
358 assert_eq!(tracker.drain(0..19).count(), 1);
359 assert_eq!(tracker.drain(0..19).count(), 0);
360
361 let mut tracker = Tracker::new(17);
362 assert_eq!(tracker.drain(5..8).count(), 1);
363 assert_eq!(tracker.drain(5..8).count(), 0);
364 assert_eq!(tracker.drain(1..3).count(), 1);
365 assert_eq!(tracker.drain(1..3).count(), 0);
366 assert_eq!(tracker.drain(7..13).count(), 1);
367 assert_eq!(tracker.drain(7..13).count(), 0);
368 }
369
370 #[test]
371 fn drain_splits_ranges_correctly() {
372 let mut tracker = Tracker::new(1337);
373 assert_eq!(
374 tracker.drain(21..42).collect::<Vec<Range<u32>>>(),
375 vec![21..42]
376 );
377 assert_eq!(
378 tracker.drain(900..1000).collect::<Vec<Range<u32>>>(),
379 vec![900..1000]
380 );
381
382 assert_eq!(
384 tracker.drain(5..1003).collect::<Vec<Range<u32>>>(),
385 vec![5..21, 42..900, 1000..1003]
386 );
387 assert_eq!(
388 tracker.drain(0..1337).collect::<Vec<Range<u32>>>(),
389 vec![0..5, 1003..1337]
390 );
391
392 let mut tracker = Tracker::new(2);
394 assert_eq!(tracker.drain(1..1).count(), 0, "{tracker:?}");
395 assert_eq!(tracker.uninitialized_ranges.len(), 1, "{tracker:?}");
396 assert_eq!(tracker.uninitialized_ranges[0], 0..2, "{tracker:?}");
397
398 tracker.discard(1);
401 assert_eq!(tracker.uninitialized_ranges.len(), 1, "{tracker:?}");
402 assert_eq!(tracker.uninitialized_ranges[0], 0..2, "{tracker:?}");
403 }
404
405 #[test]
406 fn discard_adds_range_on_cleared() {
407 let mut tracker = Tracker::new(10);
408 tracker.drain(0..10);
409 tracker.discard(0);
410 tracker.discard(5);
411 tracker.discard(9);
412 assert_eq!(tracker.check(0..1), Some(0..1), "{tracker:?}");
413 assert_eq!(tracker.check(1..5), None, "{tracker:?}");
414 assert_eq!(tracker.check(5..6), Some(5..6), "{tracker:?}");
415 assert_eq!(tracker.check(6..9), None, "{tracker:?}");
416 assert_eq!(tracker.check(9..10), Some(9..10), "{tracker:?}");
417
418 let mut tracker = Tracker::new(10);
419 tracker.drain(0..10);
420 tracker.discard(9);
421 tracker.discard(5);
422 tracker.discard(0);
423 assert_eq!(tracker.check(0..1), Some(0..1), "{tracker:?}");
424 assert_eq!(tracker.check(1..5), None, "{tracker:?}");
425 assert_eq!(tracker.check(5..6), Some(5..6), "{tracker:?}");
426 assert_eq!(tracker.check(6..9), None, "{tracker:?}");
427 assert_eq!(tracker.check(9..10), Some(9..10), "{tracker:?}");
428 }
429
430 #[test]
431 fn discard_does_nothing_on_uncleared() {
432 let mut tracker = Tracker::new(10);
433 tracker.discard(0);
434 tracker.discard(5);
435 tracker.discard(9);
436 assert_eq!(tracker.uninitialized_ranges.len(), 1);
437 assert_eq!(tracker.uninitialized_ranges[0], 0..10);
438 }
439
440 #[test]
441 fn discard_extends_ranges() {
442 let mut tracker = Tracker::new(10);
443 tracker.drain(3..7);
444 tracker.discard(2);
445 tracker.discard(7);
446 assert_eq!(tracker.uninitialized_ranges.len(), 2);
447 assert_eq!(tracker.uninitialized_ranges[0], 0..3);
448 assert_eq!(tracker.uninitialized_ranges[1], 7..10);
449 }
450
451 #[test]
452 fn discard_merges_ranges() {
453 let mut tracker = Tracker::new(10);
454 tracker.drain(3..4);
455 tracker.discard(3);
456 assert_eq!(tracker.uninitialized_ranges.len(), 1);
457 assert_eq!(tracker.uninitialized_ranges[0], 0..10);
458 }
459}
460
461#[cfg(all(test, not(target_family = "wasm")))]
462mod proptest {
463 use alloc::{vec, vec::Vec};
464 use core::ops::Range;
465
466 use proptest::prelude::*;
467
468 type Tracker = super::InitTracker<u32>;
469
470 #[derive(Debug, Clone)]
474 struct Model {
475 initialized: Vec<bool>,
476 }
477
478 impl Model {
479 fn new(size: u32) -> Self {
480 Model {
481 initialized: vec![false; size as usize],
482 }
483 }
484
485 fn drain(&mut self, range: Range<u32>) {
486 for i in range {
487 self.initialized[i as usize] = true;
488 }
489 }
490
491 fn discard(&mut self, pos: u32) {
492 self.initialized[pos as usize] = false;
493 }
494
495 fn size(&self) -> u32 {
496 self.initialized.len() as u32
497 }
498
499 fn uninitialized_positions(&self, query: Range<u32>) -> Vec<u32> {
501 query.filter(|&i| !self.initialized[i as usize]).collect()
502 }
503 }
504
505 #[derive(Debug, Clone)]
506 enum Op {
507 Drain(Range<u32>),
508 Discard(u32),
509 }
510
511 fn range_strategy(size: u32) -> impl Strategy<Value = Range<u32>> {
513 (0..=size, 0..=size).prop_map(|(a, b)| if a <= b { a..b } else { b..a })
514 }
515
516 fn op_strategy(size: u32) -> impl Strategy<Value = Op> {
517 prop_oneof![
518 3 => range_strategy(size).prop_map(Op::Drain),
519 1 => (0..size).prop_map(Op::Discard),
522 ]
523 }
524
525 fn tracker_uninit(tracker: &Tracker, size: u32) -> Vec<bool> {
528 let mut set = vec![false; size as usize];
529 for r in tracker.uninitialized_ranges.iter() {
530 for i in r.clone() {
531 set[i as usize] = true;
532 }
533 }
534 set
535 }
536
537 fn assert_state_matches_model(tracker: &Tracker, model: &Model) {
546 let ranges = &tracker.uninitialized_ranges;
547 let mut prev_end = None;
548 for (i, r) in ranges.iter().enumerate() {
549 assert!(r.start < r.end, "empty range {r:?} at index {i}");
550 assert!(
551 r.end <= model.size(),
552 "out-of-bounds range {r:?} at index {i}"
553 );
554 if let Some(prev_end) = prev_end {
555 assert!(
556 prev_end < r.start,
557 "range {r:?} at index {i} is not strictly after the previous range \
558 (previous end {prev_end}); the list must stay sorted by end and fully \
559 merged (no overlapping or adjacent ranges)",
560 );
561 }
562 prev_end = Some(r.end);
563 }
564
565 let tracker_uninit = tracker_uninit(tracker, model.size());
566 let model_uninit: Vec<bool> = model.initialized.iter().map(|&i| !i).collect();
567 assert_eq!(
568 tracker_uninit, model_uninit,
569 "tracker's uninitialized set diverged from the model",
570 );
571 }
572
573 fn assert_queries_match_model(tracker: &mut Tracker, model: &Model, query: Range<u32>) {
575 let uninit = model.uninitialized_positions(query.clone());
576
577 match tracker.check(query.clone()) {
583 None => assert!(
584 uninit.is_empty(),
585 "check({query:?}) returned None but positions {uninit:?} are uninitialized",
586 ),
587 Some(r) => {
588 assert!(
589 r.start >= query.start && r.end <= query.end,
590 "check({query:?}) returned {r:?} outside the query range",
591 );
592 if let (Some(&first), Some(&last)) = (uninit.first(), uninit.last()) {
593 assert!(
594 r.start <= first && r.end > last,
595 "check({query:?}) = {r:?} does not cover all uninitialized positions \
596 (first {first}, last {last})",
597 );
598 }
599 }
600 }
601
602 let iter_positions: Vec<u32> = tracker
605 .uninitialized(query.clone())
606 .inspect(|r| {
607 assert!(
608 r.start >= query.start && r.end <= query.end,
609 "uninitialized({query:?}) yielded out-of-range subrange {r:?}",
610 );
611 })
612 .flatten()
613 .collect();
614 assert_eq!(
615 iter_positions, uninit,
616 "uninitialized({query:?}) did not enumerate exactly the uninitialized positions",
617 );
618 }
619
620 proptest! {
621 #[test]
622 fn tracker_matches_model(
623 (size, ops, queries) in (1u32..=64).prop_flat_map(|size| {
624 (
625 Just(size),
626 proptest::collection::vec(op_strategy(size), 0..64),
627 proptest::collection::vec(range_strategy(size), 1..16),
628 )
629 })
630 ) {
631 let mut tracker = Tracker::new(size);
632 let mut model = Model::new(size);
633 assert_state_matches_model(&tracker, &model);
634
635 for op in ops {
636 match op {
637 Op::Drain(range) => {
638 tracker.drain(range.clone()).for_each(drop);
640 model.drain(range);
641 }
642 Op::Discard(pos) => {
643 tracker.discard(pos);
644 model.discard(pos);
645 }
646 }
647 assert_state_matches_model(&tracker, &model);
648 }
649
650 for query in queries {
651 assert_queries_match_model(&mut tracker, &model, query);
652 }
653 }
654 }
655}