Skip to content

Commit

Permalink
add support for DRI_PRIME with virtio-gpu
Browse files Browse the repository at this point in the history
Virtio-gpu cannot import dma_bufs but it can export them.
For dri3, initialize both drivers and allocate shareable linear buffer on
display GPU and import them, into render GPU.

For Vulkan, detect that the display driver is virtio-gpu and allocate shared
linear buffers from it and import them into render driver.

Signed-off-by: Dominik Behr <[email protected]>
  • Loading branch information
crdbehr committed Mar 20, 2024
1 parent 9d76ba3 commit affd358
Show file tree
Hide file tree
Showing 12 changed files with 387 additions and 29 deletions.
9 changes: 7 additions & 2 deletions src/gallium/drivers/virgl/virgl_resource.c
Original file line number Diff line number Diff line change
Expand Up @@ -616,8 +616,13 @@ static void virgl_resource_layout(struct pipe_resource *pt,
slices = pt->array_size;

nblocksy = util_format_get_nblocksy(pt->format, height);
metadata->stride[level] = winsys_stride ? winsys_stride :
util_format_get_stride(pt->format, width);
if ((pt->bind & (PIPE_BIND_SCANOUT | PIPE_BIND_SHARED)) == (PIPE_BIND_SCANOUT | PIPE_BIND_SHARED)) {
/* Shared scanout buffers need to be aligned to 256 bytes */
metadata->stride[level] = ALIGN(util_format_get_stride(pt->format, width), 256);
} else {
metadata->stride[level] = winsys_stride ? winsys_stride :
util_format_get_stride(pt->format, width);
}
metadata->layer_stride[level] = nblocksy * metadata->stride[level];
metadata->level_offset[level] = buffer_size;

Expand Down
2 changes: 2 additions & 0 deletions src/gallium/drivers/virgl/virgl_resource.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ static inline unsigned pipe_to_virgl_bind(const struct virgl_screen *vs,
if (pbind & PIPE_BIND_COMMAND_ARGS_BUFFER)
if (vs->caps.caps.v2.capability_bits & VIRGL_CAP_BIND_COMMAND_ARGS)
outbind |= VIRGL_BIND_COMMAND_ARGS;
if (pbind & PIPE_BIND_LINEAR)
outbind |= VIRGL_BIND_LINEAR;

/* Staging resources should only be created directly through the winsys,
* not using pipe_resources.
Expand Down
111 changes: 110 additions & 1 deletion src/gallium/winsys/virgl/drm/virgl_drm_winsys.c
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,110 @@ virgl_drm_winsys_resource_create_blob(struct virgl_winsys *qws,
return res;
}


static struct virgl_hw_res *
virgl_drm_winsys_resource_create_shared_scanout(struct virgl_winsys *qws,
enum pipe_texture_target target,
uint32_t format,
uint32_t bind,
uint32_t width,
uint32_t height,
uint32_t depth,
uint32_t array_size,
uint32_t last_level,
uint32_t nr_samples,
uint32_t flags,
uint32_t size)
{
int ret;
int32_t blob_id;
uint32_t cmd[VIRGL_PIPE_RES_CREATE_SIZE + 1] = { 0 };
struct virgl_drm_winsys *qdws = virgl_drm_winsys(qws);
struct drm_virtgpu_resource_create_blob drm_rc_blob = { 0 };
struct drm_virtgpu_execbuffer eb = { 0 };
struct drm_virtgpu_3d_wait waitcmd = { 0 };
struct virgl_hw_res *res;
struct virgl_resource_params params = { .size = size,
.bind = bind,
.format = format,
.flags = flags,
.nr_samples = nr_samples,
.width = width,
.height = height,
.depth = depth,
.array_size = array_size,
.last_level = last_level,
.target = target };

res = CALLOC_STRUCT(virgl_hw_res);
if (!res)
return NULL;

/* We assume here the allocator on the host is going to align linear shared scanout buffers allocations to 256 bytes. */

size = ALIGN(size, getpagesize());

blob_id = p_atomic_inc_return(&qdws->blob_id);
cmd[0] = VIRGL_CMD0(VIRGL_CCMD_PIPE_RESOURCE_CREATE, 0, VIRGL_PIPE_RES_CREATE_SIZE);
cmd[VIRGL_PIPE_RES_CREATE_FORMAT] = pipe_to_virgl_format(format);
cmd[VIRGL_PIPE_RES_CREATE_BIND] = bind;
cmd[VIRGL_PIPE_RES_CREATE_TARGET] = target;
cmd[VIRGL_PIPE_RES_CREATE_WIDTH] = width;
cmd[VIRGL_PIPE_RES_CREATE_HEIGHT] = height;
cmd[VIRGL_PIPE_RES_CREATE_DEPTH] = depth;
cmd[VIRGL_PIPE_RES_CREATE_ARRAY_SIZE] = array_size;
cmd[VIRGL_PIPE_RES_CREATE_LAST_LEVEL] = last_level;
cmd[VIRGL_PIPE_RES_CREATE_NR_SAMPLES] = nr_samples;
cmd[VIRGL_PIPE_RES_CREATE_FLAGS] = flags;
cmd[VIRGL_PIPE_RES_CREATE_BLOB_ID] = blob_id;

drm_rc_blob.cmd = (uintptr_t)cmd;
drm_rc_blob.cmd_size = 4 * (VIRGL_PIPE_RES_CREATE_SIZE + 1);
drm_rc_blob.size = size;
drm_rc_blob.blob_mem = VIRTGPU_BLOB_MEM_HOST3D;
drm_rc_blob.blob_flags = VIRTGPU_BLOB_FLAG_USE_MAPPABLE | VIRTGPU_BLOB_FLAG_USE_CROSS_DEVICE;
drm_rc_blob.blob_id = (uint64_t) blob_id;

ret = drmIoctl(qdws->fd, DRM_IOCTL_VIRTGPU_RESOURCE_CREATE_BLOB, &drm_rc_blob);
if (ret != 0) {
FREE(res);
return NULL;
}
/* WORKAROUND
* send empty execbuffer and a wait, to prevent race between virtio-gpu and virtio-iommu in crosvm resulting in
* [devices/src/virtio/iommu.rs:625] execute_request failed: memory mapper failed: failed to find host address
*/
cmd[0] = 0;
eb.command = (uintptr_t)cmd;
eb.size = 0;
eb.num_bo_handles = 1;
eb.bo_handles = (uintptr_t)&drm_rc_blob.bo_handle;

ret = drmIoctl(qdws->fd, DRM_IOCTL_VIRTGPU_EXECBUFFER, &eb);
if (ret == -1)
_debug_printf("failed to send execbuffer: %s", strerror(errno));

waitcmd.handle = drm_rc_blob.bo_handle;

ret = drmIoctl(qdws->fd, DRM_IOCTL_VIRTGPU_WAIT, &waitcmd);
if (ret)
_debug_printf("waiting got error - %d, slow gpu or hang?\n", errno);

res->bind = bind;
res->res_handle = drm_rc_blob.res_handle;
res->bo_handle = drm_rc_blob.bo_handle;
res->size = size;
res->flags = flags;
res->maybe_untyped = false;
pipe_reference_init(&res->reference, 1);
p_atomic_set(&res->external, false);
p_atomic_set(&res->num_cs_references, 0);
virgl_resource_cache_entry_init(&res->cache_entry, params);

return res;
}


static struct virgl_hw_res *
virgl_drm_winsys_resource_create(struct virgl_winsys *qws,
enum pipe_texture_target target,
Expand Down Expand Up @@ -454,7 +558,12 @@ virgl_drm_winsys_resource_cache_create(struct virgl_winsys *qws,
if (target == PIPE_BUFFER && (bind & VIRGL_BIND_CUSTOM))
need_sync = true;

if (flags & (VIRGL_RESOURCE_FLAG_MAP_PERSISTENT |
if ((bind & (VIRGL_BIND_SCANOUT | VIRGL_BIND_SHARED)) == (VIRGL_BIND_SCANOUT | VIRGL_BIND_SHARED))
res = virgl_drm_winsys_resource_create_shared_scanout(qws, target, format, bind,
width, height, depth,
array_size, last_level,
nr_samples, flags, size);
else if (flags & (VIRGL_RESOURCE_FLAG_MAP_PERSISTENT |
VIRGL_RESOURCE_FLAG_MAP_COHERENT))
res = virgl_drm_winsys_resource_create_blob(qws, target, format, bind,
width, height, depth,
Expand Down
41 changes: 33 additions & 8 deletions src/glx/dri3_glx.c
Original file line number Diff line number Diff line change
Expand Up @@ -591,7 +591,7 @@ dri3_destroy_screen(struct glx_screen *base)
/* Free the direct rendering per screen data */
if (psc->fd_render_gpu != psc->fd_display_gpu && psc->driScreenDisplayGPU) {
loader_dri3_close_screen(psc->driScreenDisplayGPU);
psc->core->destroyScreen(psc->driScreenDisplayGPU);
psc->display_core->destroyScreen(psc->driScreenDisplayGPU);
}
if (psc->fd_render_gpu != psc->fd_display_gpu)
close(psc->fd_display_gpu);
Expand Down Expand Up @@ -758,6 +758,18 @@ dri3_bind_extensions(struct dri3_screen *psc, struct glx_display * priv,

if (psc->interop)
__glXEnableDirectExtension(&psc->base, "GLX_MESA_gl_interop");

if (psc->display_image_driver == psc->image_driver) {
psc->display_image = psc->image;
} else {
const __DRIextension **disp_extensions;
disp_extensions = psc->display_core->getExtensions(psc->driScreenDisplayGPU);

static const struct dri_extension_match disp_exts[] = {
{ __DRI_IMAGE, 1, offsetof(struct dri3_screen, display_image), true },
};
loader_bind_extensions(psc, disp_exts, ARRAY_SIZE(disp_exts), disp_extensions);
}
}

static char *
Expand Down Expand Up @@ -793,6 +805,7 @@ dri3_create_screen(int screen, struct glx_display * priv)
{
xcb_connection_t *c = XGetXCBConnection(priv->dpy);
const __DRIconfig **driver_configs;
const __DRIconfig **disp_driver_configs;
const __DRIextension **extensions;
const struct dri3_display *const pdp = (struct dri3_display *)
priv->dri3Display;
Expand Down Expand Up @@ -853,23 +866,34 @@ dri3_create_screen(int screen, struct glx_display * priv)
if (!loader_bind_extensions(psc, exts, ARRAY_SIZE(exts), extensions))
goto handle_error;

psc->display_image_driver = psc->image_driver;
psc->display_core = psc->core;

if (psc->fd_render_gpu != psc->fd_display_gpu) {
driverNameDisplayGPU = loader_get_driver_for_fd(psc->fd_display_gpu);
if (driverNameDisplayGPU) {
const __DRIextension **disp_extensions;
static const struct dri_extension_match disp_exts[] = {
{ __DRI_CORE, 1, offsetof(struct dri3_screen, display_core), false },
{ __DRI_IMAGE_DRIVER, 1, offsetof(struct dri3_screen, display_image_driver), false },
};

/* check if driver name is matching so that non mesa drivers
* will not crash. Also need this check since image extension
* pointer from render gpu is shared with display gpu. Image
* extension pointer is shared because it keeps things simple.
*/
if (strcmp(driverName, driverNameDisplayGPU) == 0) {
if (strcmp(driverName, driverNameDisplayGPU))
{
disp_extensions = driOpenDriver(driverNameDisplayGPU, &psc->display_driver);
if (!loader_bind_extensions(psc, disp_exts, ARRAY_SIZE(disp_exts), disp_extensions))
goto handle_error;
}
psc->driScreenDisplayGPU =
psc->image_driver->createNewScreen2(screen, psc->fd_display_gpu,
psc->display_image_driver->createNewScreen2(screen, psc->fd_display_gpu,
pdp->loader_extensions,
extensions,
&driver_configs, psc);
}

disp_extensions,
&disp_driver_configs, psc);
free(driverNameDisplayGPU);
}
}
Expand Down Expand Up @@ -923,6 +947,7 @@ dri3_create_screen(int screen, struct glx_display * priv)
psc->loader_dri3_ext.flush = psc->f;
psc->loader_dri3_ext.tex_buffer = psc->texBuffer;
psc->loader_dri3_ext.image = psc->image;
psc->loader_dri3_ext.display_image = psc->display_image;
psc->loader_dri3_ext.config = psc->config;

configs = driConvertConfigs(psc->core, psc->base.configs, driver_configs);
Expand Down Expand Up @@ -1026,7 +1051,7 @@ dri3_create_screen(int screen, struct glx_display * priv)
psc->core->destroyScreen(psc->driScreenRenderGPU);
psc->driScreenRenderGPU = NULL;
if (psc->fd_render_gpu != psc->fd_display_gpu && psc->driScreenDisplayGPU)
psc->core->destroyScreen(psc->driScreenDisplayGPU);
psc->display_core->destroyScreen(psc->driScreenDisplayGPU);
psc->driScreenDisplayGPU = NULL;
if (psc->fd_display_gpu >= 0 && psc->fd_render_gpu != psc->fd_display_gpu)
close(psc->fd_display_gpu);
Expand Down
6 changes: 6 additions & 0 deletions src/glx/dri3_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,13 @@ struct dri3_screen {
const __DRI2interopExtension *interop;
const __DRIconfig **driver_configs;

/* display driver extensions */
const __DRIimageExtension *display_image;
const __DRIimageDriverExtension *display_image_driver;
const __DRIcoreExtension *display_core;

void *driver;
void *display_driver;
/* fd of the GPU used for rendering. */
int fd_render_gpu;
/* fd of the GPU used for display. If the same GPU is used for display
Expand Down
36 changes: 23 additions & 13 deletions src/loader/loader_dri3_helper.c
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ dri3_free_render_buffer(struct loader_dri3_drawable *draw,
xshmfence_unmap_shm(buffer->shm_fence);
draw->ext->image->destroyImage(buffer->image);
if (buffer->linear_buffer)
draw->ext->image->destroyImage(buffer->linear_buffer);
draw->ext->display_image->destroyImage(buffer->linear_buffer);
free(buffer);

draw->buffers[buf_id] = NULL;
Expand Down Expand Up @@ -958,12 +958,13 @@ loader_dri3_wait_gl(struct loader_dri3_drawable *draw)
/* In the psc->is_different_gpu case, we update the linear_buffer
* before updating the real front.
*/
if (draw->dri_screen_render_gpu != draw->dri_screen_display_gpu)
if (draw->dri_screen_render_gpu != draw->dri_screen_display_gpu) {
(void) loader_dri3_blit_image(draw,
front->linear_buffer,
front->image,
0, 0, front->width, front->height,
0, 0, __BLIT_FLAG_FLUSH);
}
loader_dri3_swapbuffer_barrier(draw);
loader_dri3_copy_drawable(draw, draw->drawable, front->pixmap);
}
Expand Down Expand Up @@ -1427,6 +1428,11 @@ dri3_alloc_render_buffer(struct loader_dri3_drawable *draw, unsigned int format,
{
struct loader_dri3_buffer *buffer;
__DRIimage *pixmap_buffer = NULL, *linear_buffer_display_gpu = NULL;
/**
* Used to interact with pixmap_buffer which may come from render or display
* GPU.
*/
const __DRIimageExtension *image_ext = draw->ext->image;
xcb_pixmap_t pixmap;
xcb_sync_fence_t sync_fence;
struct xshmfence *shm_fence;
Expand Down Expand Up @@ -1544,7 +1550,7 @@ dri3_alloc_render_buffer(struct loader_dri3_drawable *draw, unsigned int format,
*/
if (draw->dri_screen_display_gpu) {
linear_buffer_display_gpu =
draw->ext->image->createImage(draw->dri_screen_display_gpu,
draw->ext->display_image->createImage(draw->dri_screen_display_gpu,
width, height,
dri3_linear_format_for_format(draw, format),
__DRI_IMAGE_USE_SHARE |
Expand All @@ -1553,9 +1559,13 @@ dri3_alloc_render_buffer(struct loader_dri3_drawable *draw, unsigned int format,
__DRI_IMAGE_USE_SCANOUT,
buffer);
pixmap_buffer = linear_buffer_display_gpu;
if (pixmap_buffer) {
image_ext = draw->ext->display_image;
}
}

if (!pixmap_buffer) {
image_ext = draw->ext->image;
buffer->linear_buffer =
draw->ext->image->createImage(draw->dri_screen_render_gpu,
width, height,
Expand All @@ -1576,12 +1586,12 @@ dri3_alloc_render_buffer(struct loader_dri3_drawable *draw, unsigned int format,

/* X want some information about the planes, so ask the image for it
*/
if (!draw->ext->image->queryImage(pixmap_buffer, __DRI_IMAGE_ATTRIB_NUM_PLANES,
if (!image_ext->queryImage(pixmap_buffer, __DRI_IMAGE_ATTRIB_NUM_PLANES,
&num_planes))
num_planes = 1;

for (i = 0; i < num_planes; i++) {
__DRIimage *image = draw->ext->image->fromPlanar(pixmap_buffer, i, NULL);
__DRIimage *image = image_ext->fromPlanar(pixmap_buffer, i, NULL);

if (!image) {
assert(i == 0);
Expand All @@ -1590,23 +1600,23 @@ dri3_alloc_render_buffer(struct loader_dri3_drawable *draw, unsigned int format,

buffer_fds[i] = -1;

ret = draw->ext->image->queryImage(image, __DRI_IMAGE_ATTRIB_FD,
ret = image_ext->queryImage(image, __DRI_IMAGE_ATTRIB_FD,
&buffer_fds[i]);
ret &= draw->ext->image->queryImage(image, __DRI_IMAGE_ATTRIB_STRIDE,
ret &= image_ext->queryImage(image, __DRI_IMAGE_ATTRIB_STRIDE,
&buffer->strides[i]);
ret &= draw->ext->image->queryImage(image, __DRI_IMAGE_ATTRIB_OFFSET,
ret &= image_ext->queryImage(image, __DRI_IMAGE_ATTRIB_OFFSET,
&buffer->offsets[i]);
if (image != pixmap_buffer)
draw->ext->image->destroyImage(image);
image_ext->destroyImage(image);

if (!ret)
goto no_buffer_attrib;
}

ret = draw->ext->image->queryImage(pixmap_buffer,
ret = image_ext->queryImage(pixmap_buffer,
__DRI_IMAGE_ATTRIB_MODIFIER_UPPER, &mod);
buffer->modifier = (uint64_t) mod << 32;
ret &= draw->ext->image->queryImage(pixmap_buffer,
ret &= image_ext->queryImage(pixmap_buffer,
__DRI_IMAGE_ATTRIB_MODIFIER_LOWER, &mod);
buffer->modifier |= (uint64_t)(mod & 0xffffffff);

Expand Down Expand Up @@ -1642,7 +1652,7 @@ dri3_alloc_render_buffer(struct loader_dri3_drawable *draw, unsigned int format,
if (!buffer->linear_buffer)
goto no_buffer_attrib;

draw->ext->image->destroyImage(linear_buffer_display_gpu);
draw->ext->display_image->destroyImage(linear_buffer_display_gpu);
}

pixmap = xcb_generate_id(draw->conn);
Expand Down Expand Up @@ -1697,7 +1707,7 @@ dri3_alloc_render_buffer(struct loader_dri3_drawable *draw, unsigned int format,
if (buffer_fds[i] != -1)
close(buffer_fds[i]);
} while (--i >= 0);
draw->ext->image->destroyImage(pixmap_buffer);
image_ext->destroyImage(pixmap_buffer);
no_linear_buffer:
if (draw->dri_screen_render_gpu != draw->dri_screen_display_gpu)
draw->ext->image->destroyImage(buffer->image);
Expand Down
1 change: 1 addition & 0 deletions src/loader/loader_dri3_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ struct loader_dri3_extensions {
const __DRI2configQueryExtension *config;
const __DRItexBufferExtension *tex_buffer;
const __DRIimageExtension *image;
const __DRIimageExtension *display_image;
};

struct loader_dri3_drawable;
Expand Down
Loading

0 comments on commit affd358

Please sign in to comment.