blob: b0e59ce90b424c5d0c2b0cc2723f38c28dba283c [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! rutabaga_2d: Handles 2D virtio-gpu hypercalls.
use std::cmp::{max, min};
use data_model::*;
use crate::rutabaga_core::{Rutabaga2DInfo, RutabagaComponent, RutabagaResource};
use crate::rutabaga_utils::*;
/// Transfers a resource from potentially many chunked src VolatileSlices to a dst VolatileSlice.
pub fn transfer_2d<'a, S: Iterator<Item = VolatileSlice<'a>>>(
resource_w: u32,
resource_h: u32,
rect_x: u32,
rect_y: u32,
rect_w: u32,
rect_h: u32,
dst_stride: u32,
dst_offset: u64,
dst: VolatileSlice,
src_stride: u32,
src_offset: u64,
mut srcs: S,
) -> 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_offset = dst_offset 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_offset = src_offset 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 src_opt = srcs.next();
// Cumulative start offset of the current src.
let mut src_start_offset = 0u64;
while let Some(src) = src_opt {
if current_height >= rect_h {
break;
}
let src_size = src.size() as u64;
// Cumulative end offset of the current src.
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)?;
// Cumulative start/end offsets of the next line to copy within all srcs.
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)?;
// Clamp the line start/end offset to be inside the current src.
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);
if src_line_end_offset > src_end_offset {
next_src = true;
next_line = false;
} else if src_line_end_offset == src_end_offset {
next_src = true;
next_line = true;
} else {
next_src = false;
next_line = true;
}
let src_subslice = src.get_slice(offset_within_src as usize, copyable_size as usize)?;
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_subslice = dst.get_slice(dst_start_offset as usize, copyable_size as usize)?;
src_subslice.copy_to_volatile_slice(dst_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 {
latest_created_fence_id: u32,
}
impl Rutabaga2D {
pub fn init() -> RutabagaResult<Box<dyn RutabagaComponent>> {
Ok(Box::new(Rutabaga2D {
latest_created_fence_id: 0,
}))
}
}
impl RutabagaComponent for Rutabaga2D {
fn create_fence(&mut self, fence_data: RutabagaFenceData) -> RutabagaResult<()> {
self.latest_created_fence_id = fence_data.fence_id as u32;
Ok(())
}
fn poll(&self) -> u32 {
self.latest_created_fence_id
}
fn create_3d(
&self,
resource_id: u32,
resource_create_3d: ResourceCreate3D,
) -> RutabagaResult<RutabagaResource> {
// All virtio formats are 4 bytes per pixel.
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,
})
}
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::Unsupported)?;
let iovecs = resource
.backing_iovecs
.take()
.ok_or(RutabagaError::Unsupported)?;
// All offical virtio_gpu formats are 4 bytes per pixel.
let resource_bpp = 4;
let mut src_slices = Vec::with_capacity(iovecs.len());
for iovec in &iovecs {
// Safe because Rutabaga users should have already checked the iovecs.
let slice = unsafe { VolatileSlice::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,
VolatileSlice::new(info_2d.host_mem.as_mut_slice()),
src_stride,
src_offset,
src_slices.iter().cloned(),
)?;
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<VolatileSlice>,
) -> RutabagaResult<()> {
let mut info_2d = resource.info_2d.take().ok_or(RutabagaError::Unsupported)?;
// All offical virtio_gpu formats are 4 bytes per pixel.
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::Unsupported)?;
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,
[VolatileSlice::new(info_2d.host_mem.as_mut_slice())]
.iter()
.cloned(),
)?;
resource.info_2d = Some(info_2d);
Ok(())
}
}