diff --git a/src/virgl_gbm.c b/src/virgl_gbm.c index 4a5879a..d445044 100644 --- a/src/virgl_gbm.c +++ b/src/virgl_gbm.c @@ -34,6 +34,9 @@ #include #include +#include "util/u_math.h" +#include "pipe/p_state.h" + #include "virgl_gbm.h" #include "virgl_hw.h" #include "vrend_debug.h" @@ -139,6 +142,70 @@ static const struct planar_layout *layout_from_format(uint32_t format) } } +static void virgl_gbm_transfer_internal(uint32_t planar_bytes_per_pixel, + uint32_t subsampled_width, + uint32_t subsampled_height, + uint32_t guest_plane_stride, + uint32_t guest_resource_offset, + uint32_t host_plane_stride, uint8_t *host_address, + struct iovec *iovecs, uint32_t num_iovecs, + uint32_t direction) +{ + bool next_iovec, next_line; + uint32_t current_height, current_iovec, iovec_start_offset; + current_height = current_iovec = iovec_start_offset = 0; + + while (current_height < subsampled_height && current_iovec < num_iovecs) { + uint32_t iovec_size = iovecs[current_iovec].iov_len; + uint32_t iovec_end_offset = iovec_start_offset + iovec_size; + + uint32_t box_start_offset = guest_resource_offset + current_height * guest_plane_stride; + uint32_t box_end_offset = box_start_offset + subsampled_width * planar_bytes_per_pixel; + + uint32_t max_start = MAX2(iovec_start_offset, box_start_offset); + uint32_t min_end = MIN2(iovec_end_offset, box_end_offset); + + if (max_start < min_end) { + uint32_t offset_in_iovec = (max_start > iovec_start_offset) ? + (max_start - iovec_start_offset) : 0; + + uint32_t copy_iovec_size = min_end - max_start; + if (min_end >= iovec_end_offset) { + next_iovec = true; + next_line = false; + } else { + next_iovec = false; + next_line = true; + } + + uint8_t *guest_start = (uint8_t*)iovecs[current_iovec].iov_base + offset_in_iovec; + uint8_t *host_start = host_address + (current_height * host_plane_stride) + + (max_start - box_start_offset); + + if (direction == VIRGL_TRANSFER_TO_HOST) + memcpy(host_start, guest_start, copy_iovec_size); + else + memcpy(guest_start, host_start, copy_iovec_size); + } else { + if (box_start_offset >= iovec_start_offset) { + next_iovec = true; + next_line = false; + } else { + next_iovec = false; + next_line = true; + } + } + + if (next_iovec) { + iovec_start_offset += iovec_size; + current_iovec++; + } + + if (next_line) + current_height++; + } +} + struct virgl_gbm *virgl_gbm_init(int fd) { struct virgl_gbm *gbm = calloc(1, sizeof(struct virgl_gbm)); @@ -200,3 +267,69 @@ uint32_t virgl_gbm_convert_format(uint32_t virgl_format) return 0; } } + +int virgl_gbm_transfer(struct gbm_bo *bo, uint32_t direction, struct iovec *iovecs, + uint32_t num_iovecs, const struct vrend_transfer_info *info) +{ + void *map_data; + uint32_t host_plane_offset, guest_plane_offset, guest_stride0, calc_stride0, host_map_stride0; + + uint32_t width = gbm_bo_get_width(bo); + uint32_t height = gbm_bo_get_height(bo); + uint32_t format = gbm_bo_get_format(bo); + int plane_count = gbm_bo_get_plane_count(bo); + const struct planar_layout *layout = layout_from_format(format); + if (!layout) + return -1; + + host_plane_offset = guest_plane_offset = host_map_stride0 = guest_stride0 = 0; + uint32_t map_flags = (direction == VIRGL_TRANSFER_TO_HOST) ? GBM_BO_TRANSFER_WRITE : + GBM_BO_TRANSFER_READ; + void *addr = gbm_bo_map(bo, 0, 0, width, height, map_flags, &host_map_stride0, &map_data); + if (!addr) + return -1; + + /* + * Unfortunately, the kernel doesn't actually pass the guest layer_stride and + * guest stride to the host (compare virtio_gpu.h and virtgpu_drm.h). We can use + * the level (always zero for 2D images) to work around this. + */ + guest_stride0 = info->stride; + calc_stride0 = width * layout->bytes_per_pixel[0]; + if (!guest_stride0) + guest_stride0 = (info->level > 0) ? (uint32_t)info->level : calc_stride0; + + if (guest_stride0 < calc_stride0) + return -1; + + if (guest_stride0 > host_map_stride0) + return -1; + + for (int plane = 0; plane < plane_count; plane++) { + host_plane_offset += gbm_bo_get_offset(bo, plane); + + uint32_t subsampled_x = info->box->x / layout->horizontal_subsampling[plane]; + uint32_t subsampled_y = info->box->y / layout->vertical_subsampling[plane]; + uint32_t subsampled_width = info->box->width / layout->horizontal_subsampling[plane]; + uint32_t subsampled_height = info->box->height / layout->vertical_subsampling[plane]; + uint32_t plane_height = height / layout->vertical_subsampling[plane]; + uint32_t guest_plane_stride = guest_stride0 / layout->horizontal_subsampling[plane]; + uint32_t host_plane_stride = host_map_stride0 / layout->horizontal_subsampling[plane]; + + uint32_t guest_resource_offset = guest_plane_offset + (subsampled_y * guest_plane_stride) + + subsampled_x * layout->bytes_per_pixel[plane]; + uint32_t host_resource_offset = host_plane_offset + (subsampled_y * host_plane_stride) + + subsampled_x * layout->bytes_per_pixel[plane]; + + uint8_t *host_address = (uint8_t*)addr + host_resource_offset; + + virgl_gbm_transfer_internal(layout->bytes_per_pixel[plane], subsampled_width, + subsampled_height, guest_plane_stride, guest_resource_offset, + host_plane_stride, host_address, iovecs, num_iovecs, direction); + + guest_plane_offset += plane_height * guest_plane_stride; + } + + gbm_bo_unmap(bo, map_data); + return 0; +} diff --git a/src/virgl_gbm.h b/src/virgl_gbm.h index e626913..02646b5 100644 --- a/src/virgl_gbm.h +++ b/src/virgl_gbm.h @@ -26,6 +26,7 @@ #define VIRGL_GBM_H #include +#include "vrend_iov.h" /* * If fd >= 0, virglrenderer owns the fd since it was opened via a rendernode @@ -43,4 +44,6 @@ void virgl_gbm_fini(struct virgl_gbm *gbm); uint32_t virgl_gbm_convert_format(uint32_t virgl_format); +int virgl_gbm_transfer(struct gbm_bo *bo, uint32_t direction, struct iovec *iovecs, + uint32_t num_iovecs, const struct vrend_transfer_info *info); #endif diff --git a/src/vrend_iov.h b/src/vrend_iov.h index 31341ca..555b29b 100644 --- a/src/vrend_iov.h +++ b/src/vrend_iov.h @@ -24,6 +24,8 @@ #ifndef VREND_IOV_H #define VREND_IOV_H +#include +#include #include "config.h" #ifdef HAVE_SYS_UIO_H @@ -35,6 +37,20 @@ struct iovec { }; #endif +struct vrend_transfer_info { + uint32_t handle; + uint32_t ctx_id; + int level; + uint32_t stride; + uint32_t layer_stride; + unsigned int iovec_cnt; + struct iovec *iovec; + uint64_t offset; + bool context0; + struct pipe_box *box; + bool synchronized; +}; + typedef void (*iov_cb)(void *cookie, unsigned int doff, void *src, int len); size_t vrend_get_iovec_size(const struct iovec *iov, int iovlen); diff --git a/src/vrend_renderer.c b/src/vrend_renderer.c index 9fd6df4..dc4497d 100644 --- a/src/vrend_renderer.c +++ b/src/vrend_renderer.c @@ -7302,6 +7302,11 @@ int vrend_renderer_transfer_iov(const struct vrend_transfer_info *info, return EINVAL; } +#ifdef ENABLE_GBM_ALLOCATION + if (res->gbm_bo) + return virgl_gbm_transfer(res->gbm_bo, transfer_mode, iov, num_iovs, info); +#endif + if (!check_transfer_bounds(res, info)) return EINVAL; diff --git a/src/vrend_renderer.h b/src/vrend_renderer.h index b1b0ce8..f8c53d9 100644 --- a/src/vrend_renderer.h +++ b/src/vrend_renderer.h @@ -100,20 +100,6 @@ struct vrend_format_table { uint32_t flags; }; -struct vrend_transfer_info { - uint32_t handle; - uint32_t ctx_id; - int level; - uint32_t stride; - uint32_t layer_stride; - unsigned int iovec_cnt; - struct iovec *iovec; - uint64_t offset; - bool context0; - struct pipe_box *box; - bool synchronized; -}; - struct vrend_if_cbs { void (*write_fence)(unsigned fence_id);