Skip to main content

fuchsia_framebuffer/
lib.rs

1// Copyright 2018 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::collections::BTreeSet;
6
7pub mod sysmem;
8
9#[cfg(test)]
10use std::ops::Range;
11
12// TODO(https://fxbug.dev/42080389): Use display_utils structs instead.
13pub type ImageId = u64;
14
15/// A pool of reusable framebuffer image IDs allocated for a buffer collection.
16#[derive(Debug)]
17pub struct FrameSet {
18    image_count: usize,
19    available: BTreeSet<ImageId>,
20    taken: BTreeSet<ImageId>,
21}
22
23impl FrameSet {
24    pub fn new(available: BTreeSet<ImageId>) -> FrameSet {
25        FrameSet { image_count: available.len(), available, taken: BTreeSet::new() }
26    }
27
28    #[cfg(test)]
29    pub fn new_with_range(r: Range<ImageId>) -> FrameSet {
30        let mut available = BTreeSet::new();
31        for image_id in r {
32            available.insert(image_id);
33        }
34        Self::new(available)
35    }
36
37    /// Removes and returns the first available image ID, or `None` if empty.
38    pub fn take_image(&mut self) -> Option<ImageId> {
39        if let Some(first) = self.available.pop_first() {
40            self.taken.insert(first);
41            Some(first)
42        } else {
43            None
44        }
45    }
46
47    /// Inserts the returned image ID back into `self.available`.
48    ///
49    /// `image_id` must be one of the IDs previously returned by `take_image`.
50    pub fn return_image(&mut self, image_id: ImageId) {
51        assert!(
52            self.taken.remove(&image_id),
53            "Attempted to return image {} which was not currently taken",
54            image_id
55        );
56        self.available.insert(image_id);
57    }
58
59    /// Returns `true` if all images allocated for this pool are available.
60    pub fn no_images_in_use(&self) -> bool {
61        self.taken.is_empty()
62    }
63
64    /// Returns the total number of images in the pool.
65    pub fn image_count_for_testing(&self) -> usize {
66        self.image_count
67    }
68
69    /// Returns the number of currently available images in the pool.
70    pub fn available_count_for_testing(&self) -> usize {
71        self.available.len()
72    }
73}
74
75#[cfg(test)]
76mod frameset_tests {
77    use crate::{FrameSet, ImageId};
78    use std::collections::BTreeSet;
79    use std::ops::Range;
80
81    const IMAGE_RANGE: Range<ImageId> = 200..203;
82
83    #[fuchsia::test]
84    fn test_initial_state() {
85        let fs = FrameSet::new_with_range(IMAGE_RANGE);
86        assert_eq!(fs.image_count_for_testing(), 3);
87        assert_eq!(fs.available_count_for_testing(), 3);
88        assert!(fs.no_images_in_use());
89    }
90
91    #[fuchsia::test]
92    fn test_take_and_return_image() {
93        let mut fs = FrameSet::new_with_range(IMAGE_RANGE);
94
95        let img1 = fs.take_image();
96        assert_eq!(img1, Some(200));
97        assert_eq!(fs.available_count_for_testing(), 2);
98        assert!(!fs.no_images_in_use());
99
100        let img2 = fs.take_image();
101        assert_eq!(img2, Some(201));
102        assert_eq!(fs.available_count_for_testing(), 1);
103
104        fs.return_image(img1.unwrap());
105        assert_eq!(fs.available_count_for_testing(), 2);
106        assert!(!fs.no_images_in_use());
107
108        fs.return_image(img2.unwrap());
109        assert_eq!(fs.available_count_for_testing(), 3);
110        assert!(fs.no_images_in_use());
111    }
112
113    #[fuchsia::test]
114    #[should_panic(expected = "Attempted to return image 100 which was not currently taken")]
115    fn test_return_untracked_image_panics() {
116        let mut fs = FrameSet::new_with_range(IMAGE_RANGE);
117        fs.return_image(100);
118    }
119
120    #[fuchsia::test]
121    #[should_panic(expected = "Attempted to return image 200 which was not currently taken")]
122    fn test_return_already_available_image_panics() {
123        let mut fs = FrameSet::new_with_range(IMAGE_RANGE);
124        // Image 200 is available, not taken. Returning it should panic.
125        fs.return_image(200);
126    }
127
128    #[fuchsia::test]
129    #[should_panic(expected = "Attempted to return image 200 which was not currently taken")]
130    fn test_double_return_panics() {
131        let mut fs = FrameSet::new_with_range(IMAGE_RANGE);
132        let img = fs.take_image().expect("image");
133        fs.return_image(img);
134        fs.return_image(img);
135    }
136
137    #[fuchsia::test]
138    fn test_take_image_from_empty_pool() {
139        let mut fs = FrameSet::new_with_range(200..201);
140
141        let img = fs.take_image();
142        assert_eq!(img, Some(200));
143        // Pool is empty now.
144        assert_eq!(fs.take_image(), None);
145
146        fs.return_image(img.unwrap());
147        let img_again = fs.take_image();
148        assert_eq!(img_again, Some(200));
149    }
150
151    #[fuchsia::test]
152    fn test_empty_initial_set() {
153        let mut fs = FrameSet::new(BTreeSet::new());
154        assert_eq!(fs.image_count_for_testing(), 0);
155        assert_eq!(fs.available_count_for_testing(), 0);
156        assert!(fs.no_images_in_use());
157        assert_eq!(fs.take_image(), None);
158    }
159}
160
161// TODO: this should eventually be removed in favor of client adding
162// CPU access requirements if needed
163#[derive(Debug, Clone, Copy, PartialEq)]
164pub enum FrameUsage {
165    Cpu,
166    Gpu,
167}