xref: /aosp_15_r20/external/crosvm/rutabaga_gfx/src/rutabaga_2d.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2020 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 //! rutabaga_2d: Handles 2D virtio-gpu hypercalls.
6 
7 use std::cmp::max;
8 use std::cmp::min;
9 use std::cmp::Ordering;
10 use std::io::IoSliceMut;
11 
12 use crate::rutabaga_core::Rutabaga2DInfo;
13 use crate::rutabaga_core::RutabagaComponent;
14 use crate::rutabaga_core::RutabagaResource;
15 use crate::rutabaga_utils::*;
16 
17 /// Transfers a resource from potentially many chunked src slices to a dst slice.
transfer_2d( resource_w: u32, resource_h: u32, rect_x: u32, rect_y: u32, rect_w: u32, rect_h: u32, dst_stride: u32, dst_offset: u64, mut dst: IoSliceMut, src_stride: u32, src_offset: u64, srcs: &[&[u8]], ) -> RutabagaResult<()>18 fn transfer_2d(
19     resource_w: u32,
20     resource_h: u32,
21     rect_x: u32,
22     rect_y: u32,
23     rect_w: u32,
24     rect_h: u32,
25     dst_stride: u32,
26     dst_offset: u64,
27     mut dst: IoSliceMut,
28     src_stride: u32,
29     src_offset: u64,
30     srcs: &[&[u8]],
31 ) -> RutabagaResult<()> {
32     if rect_w == 0 || rect_h == 0 {
33         return Ok(());
34     }
35 
36     checked_range!(checked_arithmetic!(rect_x + rect_w)?; <= resource_w)?;
37     checked_range!(checked_arithmetic!(rect_y + rect_h)?; <= resource_h)?;
38 
39     let bytes_per_pixel = 4u64;
40 
41     let rect_x = rect_x as u64;
42     let rect_y = rect_y as u64;
43     let rect_w = rect_w as u64;
44     let rect_h = rect_h as u64;
45 
46     let dst_stride = dst_stride as u64;
47     let dst_resource_offset = dst_offset + (rect_y * dst_stride) + (rect_x * bytes_per_pixel);
48 
49     let src_stride = src_stride as u64;
50     let src_resource_offset = src_offset + (rect_y * src_stride) + (rect_x * bytes_per_pixel);
51 
52     let mut next_src;
53     let mut next_line;
54     let mut current_height = 0u64;
55     let mut srcs = srcs.iter();
56     let mut src_opt = srcs.next();
57 
58     // Cumulative start offset of the current src.
59     let mut src_start_offset = 0u64;
60     while let Some(src) = src_opt {
61         if current_height >= rect_h {
62             break;
63         }
64 
65         let src_size = src.len() as u64;
66 
67         // Cumulative end offset of the current src.
68         let src_end_offset = checked_arithmetic!(src_start_offset + src_size)?;
69 
70         let src_line_vertical_offset = checked_arithmetic!(current_height * src_stride)?;
71         let src_line_horizontal_offset = checked_arithmetic!(rect_w * bytes_per_pixel)?;
72 
73         // Cumulative start/end offsets of the next line to copy within all srcs.
74         let src_line_start_offset =
75             checked_arithmetic!(src_resource_offset + src_line_vertical_offset)?;
76         let src_line_end_offset =
77             checked_arithmetic!(src_line_start_offset + src_line_horizontal_offset)?;
78 
79         // Clamp the line start/end offset to be inside the current src.
80         let src_copyable_start_offset = max(src_line_start_offset, src_start_offset);
81         let src_copyable_end_offset = min(src_line_end_offset, src_end_offset);
82 
83         if src_copyable_start_offset < src_copyable_end_offset {
84             let copyable_size =
85                 checked_arithmetic!(src_copyable_end_offset - src_copyable_start_offset)?;
86 
87             let offset_within_src = src_copyable_start_offset.saturating_sub(src_start_offset);
88 
89             match src_line_end_offset.cmp(&src_end_offset) {
90                 Ordering::Greater => {
91                     next_src = true;
92                     next_line = false;
93                 }
94                 Ordering::Equal => {
95                     next_src = true;
96                     next_line = true;
97                 }
98                 Ordering::Less => {
99                     next_src = false;
100                     next_line = true;
101                 }
102             }
103 
104             let src_end = offset_within_src + copyable_size;
105             let src_subslice = src
106                 .get(offset_within_src as usize..src_end as usize)
107                 .ok_or(RutabagaError::InvalidIovec)?;
108 
109             let dst_line_vertical_offset = checked_arithmetic!(current_height * dst_stride)?;
110             let dst_line_horizontal_offset =
111                 checked_arithmetic!(src_copyable_start_offset - src_line_start_offset)?;
112             let dst_line_offset =
113                 checked_arithmetic!(dst_line_vertical_offset + dst_line_horizontal_offset)?;
114             let dst_start_offset = checked_arithmetic!(dst_resource_offset + dst_line_offset)?;
115 
116             let dst_end_offset = dst_start_offset + copyable_size;
117             let dst_subslice = dst
118                 .get_mut(dst_start_offset as usize..dst_end_offset as usize)
119                 .ok_or(RutabagaError::InvalidIovec)?;
120 
121             dst_subslice.copy_from_slice(src_subslice);
122         } else if src_line_start_offset >= src_start_offset {
123             next_src = true;
124             next_line = false;
125         } else {
126             next_src = false;
127             next_line = true;
128         };
129 
130         if next_src {
131             src_start_offset = checked_arithmetic!(src_start_offset + src_size)?;
132             src_opt = srcs.next();
133         }
134 
135         if next_line {
136             current_height += 1;
137         }
138     }
139 
140     Ok(())
141 }
142 
143 pub struct Rutabaga2D {
144     fence_handler: RutabagaFenceHandler,
145 }
146 
147 impl Rutabaga2D {
init(fence_handler: RutabagaFenceHandler) -> RutabagaResult<Box<dyn RutabagaComponent>>148     pub fn init(fence_handler: RutabagaFenceHandler) -> RutabagaResult<Box<dyn RutabagaComponent>> {
149         Ok(Box::new(Rutabaga2D { fence_handler }))
150     }
151 }
152 
153 impl RutabagaComponent for Rutabaga2D {
create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()>154     fn create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> {
155         self.fence_handler.call(fence);
156         Ok(())
157     }
158 
create_3d( &self, resource_id: u32, resource_create_3d: ResourceCreate3D, ) -> RutabagaResult<RutabagaResource>159     fn create_3d(
160         &self,
161         resource_id: u32,
162         resource_create_3d: ResourceCreate3D,
163     ) -> RutabagaResult<RutabagaResource> {
164         // All virtio formats are 4 bytes per pixel.
165         let resource_bpp = 4;
166         let resource_stride = resource_bpp * resource_create_3d.width;
167         let resource_size = (resource_stride as usize) * (resource_create_3d.height as usize);
168         let info_2d = Rutabaga2DInfo {
169             width: resource_create_3d.width,
170             height: resource_create_3d.height,
171             host_mem: vec![0; resource_size],
172         };
173 
174         Ok(RutabagaResource {
175             resource_id,
176             handle: None,
177             blob: false,
178             blob_mem: 0,
179             blob_flags: 0,
180             map_info: None,
181             info_2d: Some(info_2d),
182             info_3d: None,
183             vulkan_info: None,
184             backing_iovecs: None,
185             component_mask: 1 << (RutabagaComponentType::Rutabaga2D as u8),
186             size: resource_size as u64,
187             mapping: None,
188         })
189     }
190 
transfer_write( &self, _ctx_id: u32, resource: &mut RutabagaResource, transfer: Transfer3D, ) -> RutabagaResult<()>191     fn transfer_write(
192         &self,
193         _ctx_id: u32,
194         resource: &mut RutabagaResource,
195         transfer: Transfer3D,
196     ) -> RutabagaResult<()> {
197         if transfer.is_empty() {
198             return Ok(());
199         }
200 
201         let mut info_2d = resource
202             .info_2d
203             .take()
204             .ok_or(RutabagaError::Invalid2DInfo)?;
205 
206         let iovecs = resource
207             .backing_iovecs
208             .take()
209             .ok_or(RutabagaError::InvalidIovec)?;
210 
211         // All offical virtio_gpu formats are 4 bytes per pixel.
212         let resource_bpp = 4;
213         let mut src_slices = Vec::with_capacity(iovecs.len());
214         for iovec in &iovecs {
215             // SAFETY:
216             // Safe because Rutabaga users should have already checked the iovecs.
217             let slice = unsafe { std::slice::from_raw_parts(iovec.base as *mut u8, iovec.len) };
218             src_slices.push(slice);
219         }
220 
221         let src_stride = resource_bpp * info_2d.width;
222         let src_offset = transfer.offset;
223 
224         let dst_stride = resource_bpp * info_2d.width;
225         let dst_offset = 0;
226 
227         transfer_2d(
228             info_2d.width,
229             info_2d.height,
230             transfer.x,
231             transfer.y,
232             transfer.w,
233             transfer.h,
234             dst_stride,
235             dst_offset,
236             IoSliceMut::new(info_2d.host_mem.as_mut_slice()),
237             src_stride,
238             src_offset,
239             &src_slices,
240         )?;
241 
242         resource.info_2d = Some(info_2d);
243         resource.backing_iovecs = Some(iovecs);
244         Ok(())
245     }
246 
transfer_read( &self, _ctx_id: u32, resource: &mut RutabagaResource, transfer: Transfer3D, buf: Option<IoSliceMut>, ) -> RutabagaResult<()>247     fn transfer_read(
248         &self,
249         _ctx_id: u32,
250         resource: &mut RutabagaResource,
251         transfer: Transfer3D,
252         buf: Option<IoSliceMut>,
253     ) -> RutabagaResult<()> {
254         let mut info_2d = resource
255             .info_2d
256             .take()
257             .ok_or(RutabagaError::Invalid2DInfo)?;
258 
259         // All offical virtio_gpu formats are 4 bytes per pixel.
260         let resource_bpp = 4;
261         let src_stride = resource_bpp * info_2d.width;
262         let src_offset = 0;
263         let dst_offset = 0;
264 
265         let dst_slice = buf.ok_or(RutabagaError::SpecViolation(
266             "need a destination slice for transfer read",
267         ))?;
268 
269         transfer_2d(
270             info_2d.width,
271             info_2d.height,
272             transfer.x,
273             transfer.y,
274             transfer.w,
275             transfer.h,
276             transfer.stride,
277             dst_offset,
278             dst_slice,
279             src_stride,
280             src_offset,
281             &[info_2d.host_mem.as_mut_slice()],
282         )?;
283 
284         resource.info_2d = Some(info_2d);
285         Ok(())
286     }
287 }
288