use std::cmp::max;
use std::cmp::min;
use std::cmp::Ordering;
use std::io::IoSliceMut;
use crate::rutabaga_core::Rutabaga2DInfo;
use crate::rutabaga_core::RutabagaComponent;
use crate::rutabaga_core::RutabagaResource;
use crate::rutabaga_utils::*;
fn 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<()> {
if rect_w == 0 || rect_h == 0 {
return Ok(());
}
checked_range!(checked_arithmetic!(rect_x + rect_w)?; <= resource_w)?;
checked_range!(checked_arithmetic!(rect_y + rect_h)?; <= resource_h)?;
let bytes_per_pixel = 4u64;
let rect_x = rect_x as u64;
let rect_y = rect_y as u64;
let rect_w = rect_w as u64;
let rect_h = rect_h as u64;
let dst_stride = dst_stride as u64;
let dst_resource_offset = dst_offset + (rect_y * dst_stride) + (rect_x * bytes_per_pixel);
let src_stride = src_stride as u64;
let src_resource_offset = src_offset + (rect_y * src_stride) + (rect_x * bytes_per_pixel);
let mut next_src;
let mut next_line;
let mut current_height = 0u64;
let mut srcs = srcs.iter();
let mut src_opt = srcs.next();
let mut src_start_offset = 0u64;
while let Some(src) = src_opt {
if current_height >= rect_h {
break;
}
let src_size = src.len() as u64;
let src_end_offset = checked_arithmetic!(src_start_offset + src_size)?;
let src_line_vertical_offset = checked_arithmetic!(current_height * src_stride)?;
let src_line_horizontal_offset = checked_arithmetic!(rect_w * bytes_per_pixel)?;
let src_line_start_offset =
checked_arithmetic!(src_resource_offset + src_line_vertical_offset)?;
let src_line_end_offset =
checked_arithmetic!(src_line_start_offset + src_line_horizontal_offset)?;
let src_copyable_start_offset = max(src_line_start_offset, src_start_offset);
let src_copyable_end_offset = min(src_line_end_offset, src_end_offset);
if src_copyable_start_offset < src_copyable_end_offset {
let copyable_size =
checked_arithmetic!(src_copyable_end_offset - src_copyable_start_offset)?;
let offset_within_src = src_copyable_start_offset.saturating_sub(src_start_offset);
match src_line_end_offset.cmp(&src_end_offset) {
Ordering::Greater => {
next_src = true;
next_line = false;
}
Ordering::Equal => {
next_src = true;
next_line = true;
}
Ordering::Less => {
next_src = false;
next_line = true;
}
}
let src_end = offset_within_src + copyable_size;
let src_subslice = src
.get(offset_within_src as usize..src_end as usize)
.ok_or(RutabagaError::InvalidIovec)?;
let dst_line_vertical_offset = checked_arithmetic!(current_height * dst_stride)?;
let dst_line_horizontal_offset =
checked_arithmetic!(src_copyable_start_offset - src_line_start_offset)?;
let dst_line_offset =
checked_arithmetic!(dst_line_vertical_offset + dst_line_horizontal_offset)?;
let dst_start_offset = checked_arithmetic!(dst_resource_offset + dst_line_offset)?;
let dst_end_offset = dst_start_offset + copyable_size;
let dst_subslice = dst
.get_mut(dst_start_offset as usize..dst_end_offset as usize)
.ok_or(RutabagaError::InvalidIovec)?;
dst_subslice.copy_from_slice(src_subslice);
} else if src_line_start_offset >= src_start_offset {
next_src = true;
next_line = false;
} else {
next_src = false;
next_line = true;
};
if next_src {
src_start_offset = checked_arithmetic!(src_start_offset + src_size)?;
src_opt = srcs.next();
}
if next_line {
current_height += 1;
}
}
Ok(())
}
pub struct Rutabaga2D {
fence_handler: RutabagaFenceHandler,
}
impl Rutabaga2D {
pub fn init(fence_handler: RutabagaFenceHandler) -> RutabagaResult<Box<dyn RutabagaComponent>> {
Ok(Box::new(Rutabaga2D { fence_handler }))
}
}
impl RutabagaComponent for Rutabaga2D {
fn create_fence(&mut self, fence: RutabagaFence) -> RutabagaResult<()> {
self.fence_handler.call(fence);
Ok(())
}
fn create_3d(
&self,
resource_id: u32,
resource_create_3d: ResourceCreate3D,
) -> RutabagaResult<RutabagaResource> {
let resource_bpp = 4;
let resource_stride = resource_bpp * resource_create_3d.width;
let resource_size = (resource_stride as usize) * (resource_create_3d.height as usize);
let info_2d = Rutabaga2DInfo {
width: resource_create_3d.width,
height: resource_create_3d.height,
host_mem: vec![0; resource_size],
};
Ok(RutabagaResource {
resource_id,
handle: None,
blob: false,
blob_mem: 0,
blob_flags: 0,
map_info: None,
info_2d: Some(info_2d),
info_3d: None,
vulkan_info: None,
backing_iovecs: None,
component_mask: 1 << (RutabagaComponentType::Rutabaga2D as u8),
size: resource_size as u64,
mapping: None,
})
}
fn transfer_write(
&self,
_ctx_id: u32,
resource: &mut RutabagaResource,
transfer: Transfer3D,
) -> RutabagaResult<()> {
if transfer.is_empty() {
return Ok(());
}
let mut info_2d = resource
.info_2d
.take()
.ok_or(RutabagaError::Invalid2DInfo)?;
let iovecs = resource
.backing_iovecs
.take()
.ok_or(RutabagaError::InvalidIovec)?;
let resource_bpp = 4;
let mut src_slices = Vec::with_capacity(iovecs.len());
for iovec in &iovecs {
let slice = unsafe { std::slice::from_raw_parts(iovec.base as *mut u8, iovec.len) };
src_slices.push(slice);
}
let src_stride = resource_bpp * info_2d.width;
let src_offset = transfer.offset;
let dst_stride = resource_bpp * info_2d.width;
let dst_offset = 0;
transfer_2d(
info_2d.width,
info_2d.height,
transfer.x,
transfer.y,
transfer.w,
transfer.h,
dst_stride,
dst_offset,
IoSliceMut::new(info_2d.host_mem.as_mut_slice()),
src_stride,
src_offset,
&src_slices,
)?;
resource.info_2d = Some(info_2d);
resource.backing_iovecs = Some(iovecs);
Ok(())
}
fn transfer_read(
&self,
_ctx_id: u32,
resource: &mut RutabagaResource,
transfer: Transfer3D,
buf: Option<IoSliceMut>,
) -> RutabagaResult<()> {
let mut info_2d = resource
.info_2d
.take()
.ok_or(RutabagaError::Invalid2DInfo)?;
let resource_bpp = 4;
let src_stride = resource_bpp * info_2d.width;
let src_offset = 0;
let dst_offset = 0;
let dst_slice = buf.ok_or(RutabagaError::SpecViolation(
"need a destination slice for transfer read",
))?;
transfer_2d(
info_2d.width,
info_2d.height,
transfer.x,
transfer.y,
transfer.w,
transfer.h,
transfer.stride,
dst_offset,
dst_slice,
src_stride,
src_offset,
&[info_2d.host_mem.as_mut_slice()],
)?;
resource.info_2d = Some(info_2d);
Ok(())
}
}