/* Copyright (c) 2023 Kasm * * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ #ifdef HAVE_DIX_CONFIG_H #include #endif #ifdef DRI3 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern const char *driNode; static struct priv_t { struct gbm_device *gbm; int fd; } priv; struct gbm_pixmap { struct gbm_bo *bo; }; typedef struct gbm_pixmap gbm_pixmap; static DevPrivateKeyRec dri3_pixmap_private_key; static struct timeval start; static int xvnc_dri3_open_client(ClientPtr client, ScreenPtr screen, RRProviderPtr provider, int *pfd) { int fd = open(driNode, O_RDWR | O_CLOEXEC); if (fd < 0) return BadAlloc; *pfd = fd; return Success; } static uint32_t gbm_format_for_depth(CARD8 depth) { switch (depth) { case 16: return GBM_FORMAT_RGB565; case 24: return GBM_FORMAT_XRGB8888; case 30: return GBM_FORMAT_ARGB2101010; default: ErrorF("unexpected depth: %d\n", depth); /* fallthrough */ case 32: return GBM_FORMAT_ARGB8888; } } static void dri3_pixmap_set_private(PixmapPtr pixmap, gbm_pixmap *gp) { dixSetPrivate(&pixmap->devPrivates, &dri3_pixmap_private_key, gp); } static gbm_pixmap *gbm_pixmap_get(PixmapPtr pixmap) { return dixLookupPrivate(&pixmap->devPrivates, &dri3_pixmap_private_key); } static PixmapPtr create_pixmap_for_bo(ScreenPtr screen, struct gbm_bo *bo, CARD8 depth) { PixmapPtr pixmap; gbm_pixmap *gp = calloc(1, sizeof(gbm_pixmap)); if (!gp) return NULL; pixmap = screen->CreatePixmap(screen, gbm_bo_get_width(bo), gbm_bo_get_height(bo), depth, CREATE_PIXMAP_USAGE_SCRATCH); if (!pixmap) return NULL; gp->bo = bo; dri3_pixmap_set_private(pixmap, gp); return pixmap; } static PixmapPtr xvnc_pixmap_from_fds(ScreenPtr screen, CARD8 num_fds, const int *fds, CARD16 width, CARD16 height, const CARD32 *strides, const CARD32 *offsets, CARD8 depth, CARD8 bpp, uint64_t modifier) { struct gbm_bo *bo = NULL; PixmapPtr pixmap; if (width == 0 || height == 0 || num_fds == 0 || depth < 15 || bpp != BitsPerPixel(depth) || strides[0] < width * bpp / 8) return NULL; if (num_fds == 1) { struct gbm_import_fd_data data; data.fd = fds[0]; data.width = width; data.height = height; data.stride = strides[0]; data.format = gbm_format_for_depth(depth); bo = gbm_bo_import(priv.gbm, GBM_BO_IMPORT_FD, &data, GBM_BO_USE_RENDERING); if (!bo) return NULL; } else { return NULL; } pixmap = create_pixmap_for_bo(screen, bo, depth); if (pixmap == NULL) { gbm_bo_destroy(bo); return NULL; } return pixmap; } static int xvnc_fds_from_pixmap(ScreenPtr screen, PixmapPtr pixmap, int *fds, uint32_t *strides, uint32_t *offsets, uint64_t *modifier) { gbm_pixmap *gp = gbm_pixmap_get(pixmap); if (!gp) return 0; fds[0] = gbm_bo_get_fd(gp->bo); strides[0] = gbm_bo_get_stride(gp->bo); offsets[0] = 0; *modifier = DRM_FORMAT_MOD_INVALID; return 1; } static Bool xvnc_get_formats(ScreenPtr screen, CARD32 *num_formats, CARD32 **formats) { ErrorF("xvnc_get_formats\n"); return FALSE; } static Bool xvnc_get_modifiers(ScreenPtr screen, uint32_t format, uint32_t *num_modifiers, uint64_t **modifiers) { ErrorF("xvnc_get_modifiers\n"); return FALSE; } static Bool xvnc_get_drawable_modifiers(DrawablePtr draw, uint32_t format, uint32_t *num_modifiers, uint64_t **modifiers) { ErrorF("xvnc_get_drawable_modifiers\n"); return FALSE; } static const dri3_screen_info_rec xvnc_dri3_info = { .version = 2, .open = NULL, .pixmap_from_fds = xvnc_pixmap_from_fds, .fds_from_pixmap = xvnc_fds_from_pixmap, .open_client = xvnc_dri3_open_client, .get_formats = xvnc_get_formats, .get_modifiers = xvnc_get_modifiers, .get_drawable_modifiers = xvnc_get_drawable_modifiers, }; void xvnc_sync_dri3_pixmap(PixmapPtr pixmap) { // There doesn't seem to be a good hook or sync point, so we do it manually // here, right before Present copies from the pixmap DrawablePtr pDraw; GCPtr gc; void *ptr; uint32_t stride, w, h; void *opaque = NULL; // We may not be running on hw if there's a compositor using PRESENT on llvmpipe if (!driNode) return; gbm_pixmap *gp = gbm_pixmap_get(pixmap); if (!gp) { //ErrorF("Present tried to copy from a non-dri3 pixmap\n"); return; } w = gbm_bo_get_width(gp->bo); h = gbm_bo_get_height(gp->bo); ptr = gbm_bo_map(gp->bo, 0, 0, w, h, GBM_BO_TRANSFER_READ, &stride, &opaque); if (!ptr) { ErrorF("gbm map failed, errno %d\n", errno); return; } pDraw = &pixmap->drawable; if ((gc = GetScratchGC(pDraw->depth, pDraw->pScreen))) { ValidateGC(pDraw, gc); //gc->ops->PutImage(pDraw, gc, pDraw->depth, 0, 0, w, h, 0, ZPixmap, data); fbPutZImage(pDraw, fbGetCompositeClip(gc), gc->alu, fbGetGCPrivate(gc)->pm, 0, 0, w, h, ptr, stride / sizeof(FbStip)); FreeScratchGC(gc); } gbm_bo_unmap(gp->bo, opaque); } void xvnc_init_dri3(void) { memset(&priv, 0, sizeof(priv)); gettimeofday(&start, NULL); if (!dixRegisterPrivateKey(&dri3_pixmap_private_key, PRIVATE_PIXMAP, 0)) FatalError("dix\n"); if (!driNode) driNode = "/dev/dri/renderD128"; priv.fd = open(driNode, O_RDWR | O_CLOEXEC); if (!priv.fd) FatalError("Failed to open %s\n", driNode); priv.gbm = gbm_create_device(priv.fd); if (!priv.gbm) FatalError("Failed to create gbm\n"); if (!dri3_screen_init(screenInfo.screens[0], &xvnc_dri3_info)) FatalError("Couldn't init dri3\n"); } #endif // DRI3