There is a behaviour with the QEMU virtio-gpu device that seems wrong to me: When copying data from the guest's backing store to the host's buffers it does account for the rectangle offset and size when drawing but it uses the stride (width) of the host's resource when calculating offsets in the backing store regardless of the width specified in the rectangle:
Code: Select all
static void virtio_gpu_transfer_to_host_2d(VirtIOGPU *g,
struct virtio_gpu_ctrl_command *cmd)
{
struct virtio_gpu_simple_resource *res;
int h;
uint32_t src_offset, dst_offset, stride;
int bpp;
pixman_format_code_t format;
struct virtio_gpu_transfer_to_host_2d t2d;
VIRTIO_GPU_FILL_CMD(t2d);
virtio_gpu_t2d_bswap(&t2d);
trace_virtio_gpu_cmd_res_xfer_toh_2d(t2d.resource_id);
res = virtio_gpu_find_check_resource(g, t2d.resource_id, true,
__func__, &cmd->error);
if (!res || res->blob) {
return;
}
if (t2d.r.x > res->width ||
t2d.r.y > res->height ||
t2d.r.width > res->width ||
t2d.r.height > res->height ||
t2d.r.x + t2d.r.width > res->width ||
t2d.r.y + t2d.r.height > res->height) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: transfer bounds outside resource"
" bounds for resource %d: %d %d %d %d vs %d %d\n",
__func__, t2d.resource_id, t2d.r.x, t2d.r.y,
t2d.r.width, t2d.r.height, res->width, res->height);
cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
return;
}
format = pixman_image_get_format(res->image);
bpp = DIV_ROUND_UP(PIXMAN_FORMAT_BPP(format), 8);
stride = pixman_image_get_stride(res->image);
if (t2d.offset || t2d.r.x || t2d.r.y ||
t2d.r.width != pixman_image_get_width(res->image)) {
void *img_data = pixman_image_get_data(res->image);
for (h = 0; h < t2d.r.height; h++) {
src_offset = t2d.offset + stride * h;
dst_offset = (t2d.r.y + h) * stride + (t2d.r.x * bpp);
iov_to_buf(res->iov, res->iov_cnt, src_offset,
(uint8_t *)img_data
+ dst_offset, t2d.r.width * bpp);
}
} else {
iov_to_buf(res->iov, res->iov_cnt, 0,
pixman_image_get_data(res->image),
pixman_image_get_stride(res->image)
* pixman_image_get_height(res->image));
}
}
Code: Select all
src_offset = t2d.offset + stride * h;
Code: Select all
src_offset = t2d.offset + t2d.r.width * h;
However, I'm unable to confirm as the latest specification doesn't seem to mention anything, I cannot find any other emulators implementing a virtio-gpu device and every virtio-gpu driver I find seems to transfer & flush the entire framebuffer (except Linux perhaps, but I'm still trying to interpret its driver).
I'm hoping someone here can confirm whether QEMU's behaviour is correct or not. If not, I'll ping the QEMU devs.