1#![allow(dead_code)]
19
20pub struct GpuReadback;
28
29impl GpuReadback {
30 #[must_use]
44 pub fn download(width: u32, height: u32, gpu_data: &[u8]) -> Vec<u8> {
45 let expected = (width as usize) * (height as usize) * 4;
46 let len = expected.min(gpu_data.len());
49 gpu_data[..len].to_vec()
50 }
51
52 #[must_use]
61 pub fn download_region(
62 src_width: u32,
63 x: u32,
64 y: u32,
65 w: u32,
66 h: u32,
67 gpu_data: &[u8],
68 ) -> Vec<u8> {
69 if w == 0 || h == 0 {
70 return Vec::new();
71 }
72 let stride = (src_width as usize) * 4;
73 let mut out = Vec::with_capacity((w as usize) * (h as usize) * 4);
74 for row in 0..h {
75 let src_y = (y + row) as usize;
76 let src_x = x as usize;
77 let row_start = src_y * stride + src_x * 4;
78 let row_end = row_start + (w as usize) * 4;
79 if row_end > gpu_data.len() {
80 break;
81 }
82 out.extend_from_slice(&gpu_data[row_start..row_end]);
83 }
84 out
85 }
86
87 #[must_use]
90 pub fn expected_len(width: u32, height: u32) -> usize {
91 (width as usize) * (height as usize) * 4
92 }
93
94 #[must_use]
97 pub fn validate_size(width: u32, height: u32, gpu_data: &[u8]) -> bool {
98 gpu_data.len() == Self::expected_len(width, height)
99 }
100
101 #[must_use]
105 pub fn split_channels(gpu_data: &[u8]) -> (Vec<u8>, Vec<u8>, Vec<u8>, Vec<u8>) {
106 let pixels = gpu_data.len() / 4;
107 let mut r = Vec::with_capacity(pixels);
108 let mut g = Vec::with_capacity(pixels);
109 let mut b = Vec::with_capacity(pixels);
110 let mut a = Vec::with_capacity(pixels);
111 for chunk in gpu_data.chunks_exact(4) {
112 r.push(chunk[0]);
113 g.push(chunk[1]);
114 b.push(chunk[2]);
115 a.push(chunk[3]);
116 }
117 (r, g, b, a)
118 }
119}
120
121#[cfg(test)]
124mod tests {
125 use super::*;
126
127 #[test]
128 fn test_download_identity() {
129 let data: Vec<u8> = (0..64).collect();
130 let result = GpuReadback::download(4, 4, &data);
131 assert_eq!(result, data);
132 }
133
134 #[test]
135 fn test_download_truncates_to_expected_len() {
136 let data = vec![0xAAu8; 20];
138 let result = GpuReadback::download(2, 2, &data);
139 assert_eq!(result.len(), 16);
140 }
141
142 #[test]
143 fn test_download_short_slice_returns_available_bytes() {
144 let data = vec![0xBBu8; 8];
146 let result = GpuReadback::download(2, 2, &data);
147 assert_eq!(result.len(), 8);
148 }
149
150 #[test]
151 fn test_download_region_basic() {
152 let mut frame = vec![0u8; 4 * 4 * 4];
154 for row in 0..4usize {
155 for col in 0..4usize {
156 let idx = (row * 4 + col) * 4;
157 frame[idx] = row as u8;
158 frame[idx + 1] = row as u8;
159 frame[idx + 2] = row as u8;
160 frame[idx + 3] = 0xFF;
161 }
162 }
163 let region = GpuReadback::download_region(4, 0, 1, 2, 1, &frame);
165 assert_eq!(region.len(), 8); assert_eq!(region[0], 1u8); }
168
169 #[test]
170 fn test_download_region_zero_dimensions() {
171 let frame = vec![0u8; 64];
172 assert!(GpuReadback::download_region(4, 0, 0, 0, 4, &frame).is_empty());
173 assert!(GpuReadback::download_region(4, 0, 0, 4, 0, &frame).is_empty());
174 }
175
176 #[test]
177 fn test_expected_len() {
178 assert_eq!(GpuReadback::expected_len(1920, 1080), 1920 * 1080 * 4);
179 }
180
181 #[test]
182 fn test_validate_size_correct() {
183 let data = vec![0u8; 4 * 4 * 4];
184 assert!(GpuReadback::validate_size(4, 4, &data));
185 }
186
187 #[test]
188 fn test_validate_size_wrong() {
189 let data = vec![0u8; 10];
190 assert!(!GpuReadback::validate_size(4, 4, &data));
191 }
192
193 #[test]
194 fn test_split_channels_correctness() {
195 let data = vec![1u8, 2, 3, 255];
197 let (r, g, b, a) = GpuReadback::split_channels(&data);
198 assert_eq!(r, [1]);
199 assert_eq!(g, [2]);
200 assert_eq!(b, [3]);
201 assert_eq!(a, [255]);
202 }
203
204 #[test]
205 fn test_split_channels_multiple_pixels() {
206 let mut data = Vec::new();
207 for i in 0u8..4 {
208 data.extend_from_slice(&[i, i + 10, i + 20, 0xFF]);
209 }
210 let (r, g, b, a) = GpuReadback::split_channels(&data);
211 assert_eq!(r, [0, 1, 2, 3]);
212 assert_eq!(g, [10, 11, 12, 13]);
213 assert_eq!(b, [20, 21, 22, 23]);
214 assert_eq!(a, [0xFF, 0xFF, 0xFF, 0xFF]);
215 }
216}