rutabaga_gfx/
rutabaga_2d.rs
1use std::cmp::max;
8use std::cmp::min;
9use std::cmp::Ordering;
10use std::io::IoSliceMut;
11
12use crate::rutabaga_core::Rutabaga2DInfo;
13use crate::rutabaga_core::RutabagaComponent;
14use crate::rutabaga_core::RutabagaResource;
15use crate::rutabaga_utils::*;
16
17fn 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 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 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 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 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
143pub struct Rutabaga2D {
144 fence_handler: RutabagaFenceHandler,
145}
146
147impl Rutabaga2D {
148 pub fn init(fence_handler: RutabagaFenceHandler) -> RutabagaResult<Box<dyn RutabagaComponent>> {
149 Ok(Box::new(Rutabaga2D { fence_handler }))
150 }
151}
152
153impl RutabagaComponent for Rutabaga2D {
154 fn create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> {
155 self.fence_handler.call(fence);
156 Ok(())
157 }
158
159 fn create_3d(
160 &self,
161 resource_id: u32,
162 resource_create_3d: ResourceCreate3D,
163 ) -> RutabagaResult<RutabagaResource> {
164 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
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 let resource_bpp = 4;
213 let mut src_slices = Vec::with_capacity(iovecs.len());
214 for iovec in &iovecs {
215 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
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 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}