From 08ad438e2793248452dab100822cbfcaa05b9938 Mon Sep 17 00:00:00 2001 From: Thomas Hellstrom Date: Thu, 23 Mar 2017 08:57:44 +0100 Subject: vmwgfx: Support DRI3 v2 Add server-side DRI3 support Currently DRI3 introduces extra latency with gnome-shell for the following reasons: 1) We enable GLX_EXT_buffer_age. Causes gnome-shell to post fullscreen damage. 2) We enable GLX_OML_sync_control. Cases additional slowdown. Not exactly sure why. Probably we want to implement workarounds in mesa so that we don't enable these extensions for gnome-shell. That can be done with driconf, using some trickery. v2: Verify that sharing an ARGB surface as XRGB works before enabling DRI3. Signed-off-by: Thomas Hellstrom Reviewed-by: Sinclair Yeh --- vmwgfx/Makefile.am | 1 + vmwgfx/vmwgfx_dri3.c | 310 +++++++++++++++++++++++++++++++++++++++++++++ vmwgfx/vmwgfx_driver.c | 34 ++++- vmwgfx/vmwgfx_driver.h | 13 ++ vmwgfx/vmwgfx_saa.c | 35 ++--- vmwgfx/vmwgfx_saa_priv.h | 6 +- vmwgfx/vmwgfx_xa_surface.c | 4 +- 7 files changed, 382 insertions(+), 21 deletions(-) create mode 100644 vmwgfx/vmwgfx_dri3.c diff --git a/vmwgfx/Makefile.am b/vmwgfx/Makefile.am index e486514..a47a5fe 100644 --- a/vmwgfx/Makefile.am +++ b/vmwgfx/Makefile.am @@ -32,5 +32,6 @@ libvmwgfx_la_SOURCES = \ vmwgfx_xwayland.c \ vmwgfx_layout.c \ vmwgfx_rr_inlines.h \ + vmwgfx_dri3.c \ wsbm_util.h endif diff --git a/vmwgfx/vmwgfx_dri3.c b/vmwgfx/vmwgfx_dri3.c new file mode 100644 index 0000000..0d4be5c --- /dev/null +++ b/vmwgfx/vmwgfx_dri3.c @@ -0,0 +1,310 @@ +/* + * Copyright 2017 VMWare, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. + * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Author: Thomas Hellstrom + * + */ +#ifdef _HAVE_CONFIG_H_ +#include "config.h" +#endif + +#include + +#ifdef DRI3 +#include "vmwgfx_driver.h" +#if (XA_TRACKER_VERSION_MAJOR == VMW_XA_VERSION_MAJOR_DRI3 && \ + XA_TRACKER_VERSION_MINOR >= VMW_XA_VERSION_MINOR_DRI3) + +#include "vmwgfx_driver.h" +#include "vmwgfx_saa_priv.h" +#include +#include +#include +#include + + +/** + * \brief DRI3 fd_from_pixmap callback. + * + */ +static int +vmwgfx_dri3_fd_from_pixmap(ScreenPtr screen, PixmapPtr pixmap, + CARD16 *stride, CARD32 *size) +{ + struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap); + uint32_t handle; + unsigned int byte_stride; + ScrnInfoPtr pScrn = xf86ScreenToScrn(screen); + + if (!vmwgfx_hw_dri2_validate(pixmap, 0)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "DRI3 pixmap export failed to create HW surface.\n"); + return -1; + } + + if (xa_surface_handle(vpix->hw, xa_handle_type_fd, &handle, + &byte_stride)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "DRI3 pixmap export failed to create handle.\n"); + return -1; + } + + *stride = byte_stride; + *size = byte_stride * pixmap->drawable.height; + + /* + * hw_is_dri2_fronts will make sure any software rendering to + * this pixmap is immediately flushed to the underlying surface. + * Strictly, we could wait for glxWaitX to do that, but alas, + * even the dri3 glxWaitX appears as broken as the dri2 version. + * If we, however, wanted to do that, we'd hook up a shm fence + * trigger callback. (Like glamor does). + */ + vpix->hw_is_dri2_fronts = 1; + + return handle; +} + +/** + * \brief DRI3 pixmap_from_fd callback. + * + */ +static PixmapPtr +vmwgfx_dri3_pixmap_from_fd(ScreenPtr screen, int fd, + CARD16 width, CARD16 height, CARD16 stride, + CARD8 depth, CARD8 bpp) +{ + struct vmwgfx_saa *vsaa = to_vmwgfx_saa(saa_get_driver(screen)); + struct xa_surface *srf; + struct vmwgfx_saa_pixmap *vpix; + ScrnInfoPtr pScrn = xf86ScreenToScrn(screen); + PixmapPtr pixmap; + + if (width == 0 || height == 0 || + depth < 15 || bpp != BitsPerPixel(depth) || stride < width * bpp / 8) + return NULL; + + pixmap = screen->CreatePixmap(screen, width, height, depth, 0); + if (!pixmap) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "DRI3 pixmap creation failed.\n"); + return NULL; + } + + vpix = vmwgfx_saa_pixmap(pixmap); + + if (!vmwgfx_hw_dri2_stage(pixmap, depth)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "DRI3 pixmap creation bad format.\n"); + goto out_bad_format; + } + + srf = xa_surface_from_handle2(vsaa->xat, width, height, depth, + xa_type_other, + vpix->staging_format, + vpix->staging_add_flags, + xa_handle_type_fd, + fd, stride); + if (!srf) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "DRI3 pixmap creation surface sharing failed.\n"); + goto out_bad_format; + } + + vpix->xa_flags = vpix->staging_add_flags; + vpix->hw = srf; + if (!vmwgfx_create_hw(vsaa, pixmap, TRUE)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "DRI3 pixmap creation failed SAA enabling.\n"); + goto out_no_damage; + } + + vpix->hw_is_dri2_fronts = 1; + return pixmap; + + out_no_damage: + xa_surface_unref(srf); + out_bad_format: + screen->DestroyPixmap(pixmap); + + return NULL; +} + +/** + * \brief Open a render node. + * + * \param screen[IN] Pointer to the screen + * \return A valid file descriptor or -1 on failure. + */ +static int +vmwgfx_dri3_open_render(ScreenPtr screen) +{ + ScrnInfoPtr pScrn = xf86ScreenToScrn(screen); + modesettingPtr ms = modesettingPTR(pScrn); + char bus_id[64]; + int fd; + + snprintf(bus_id, sizeof(bus_id), "PCI:%d:%d:%d", + ((ms->PciInfo->domain << 8) | ms->PciInfo->bus), + ms->PciInfo->dev, ms->PciInfo->func); + + /* Render nodes can't be opened by busid yet.. */ + fd = drmOpenWithType("vmwgfx", bus_id, DRM_NODE_RENDER); + if (fd < 0) + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "DRI3 client open busid \"%s\" failed.\n", bus_id); + + return fd; +} + +/** + * \brief DRI3 open_client callback. + * + */ +static int +vmwgfx_dri3_open_client(ClientPtr client, ScreenPtr screen, + RRProviderPtr provider, int *pfd) +{ + *pfd = vmwgfx_dri3_open_render(screen); + + return (*pfd >= 0) ? Success : BadAlloc; +} + +/** + * \brief Verify that surface sharing between render client and X server + * works. + * + * \param screen[IN,OUT] A pointer to the current screen. + * \return TRUE if successful, FALSE otherwise. + * + * Opens a render client, creates a surface and tries to share that surface + * with the X server. There is a vmwgfx kernel driver bug that, combined + * with a pre-guest-backed-surface svga mesa driver bug, + * prevents this sharing to happen and thus breaks dri3. + * + * Also, we need to make sure that we can share an XRGB surface as an + * ARGB surface since DRI3 does not appear to be as strict about internal + * surface formats as DRI2. + */ +static Bool +vmwgfx_dri3_verify_sharing(ScreenPtr screen) +{ + ScrnInfoPtr pScrn = xf86ScreenToScrn(screen); + modesettingPtr ms = modesettingPTR(pScrn); + int fd = vmwgfx_dri3_open_render(screen); + struct xa_tracker *xat; + struct xa_surface *srf1; + unsigned int stride; + uint32_t handle; + Bool ret = FALSE; + + if (fd < 0) + return FALSE; + + xat = xa_tracker_create(fd); + if (!xat) + goto out_no_xa; + + /* Here we're the render client (xat) */ + srf1 = xa_surface_create(xat, 16, 16, 32, xa_type_argb, + xa_format_unknown, + XA_FLAG_RENDER_TARGET | XA_FLAG_SHARED); + if (!srf1) + goto out_no_surface; + + if (xa_surface_handle(srf1, xa_handle_type_fd, &handle, &stride) != + XA_ERR_NONE) + goto out_no_handle; + + xa_surface_unref(srf1); + + /* Now we're the X server (ms->xat) */ + srf1 = xa_surface_from_handle2(ms->xat, 16, 16, 24, xa_type_argb, + xa_format_unknown, + XA_FLAG_RENDER_TARGET | XA_FLAG_SHARED, + xa_handle_type_fd, handle, stride); + if (!srf1) + goto out_no_surface; + + ret = TRUE; + close(handle); + + out_no_handle: + xa_surface_unref(srf1); + out_no_surface: + xa_tracker_destroy(xat); + out_no_xa: + close(fd); + + return ret; +} + +static dri3_screen_info_rec vmwgfx_dri3_info = { + .version = 1, + .open = NULL, + .pixmap_from_fd = vmwgfx_dri3_pixmap_from_fd, + .fd_from_pixmap = vmwgfx_dri3_fd_from_pixmap, + .open_client = vmwgfx_dri3_open_client, +}; + + +/** + * \brief Initialize dri3. + * + * \param screen[IN,OUT] A pointer to the current screen. + * \return TRUE if successful, FALSE otherwise. + */ +Bool +vmwgfx_dri3_init(ScreenPtr screen) +{ + ScrnInfoPtr pScrn = xf86ScreenToScrn(screen); + + if (!vmwgfx_dri3_verify_sharing(screen)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Failed to verify XA surface sharing for DRI3.\n"); + return FALSE; + } + + if (!miSyncShmScreenInit(screen)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Failed to initialize xshm sync for DRI3.\n"); + return FALSE; + } + + if (!dri3_screen_init(screen, &vmwgfx_dri3_info)) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Failed to initialize DRI3.\n"); + return FALSE; + } + + return TRUE; +} + +#else /* XA INCLUDES SUFFICIENT */ +Bool +vmwgfx_dri3_init(ScreenPtr screen) +{ + return FALSE; +} + +#endif /* !XA INCLUDES SUFFICIENT */ +#endif /* DRI3 */ diff --git a/vmwgfx/vmwgfx_driver.c b/vmwgfx/vmwgfx_driver.c index 967dec9..48d3d93 100644 --- a/vmwgfx/vmwgfx_driver.c +++ b/vmwgfx/vmwgfx_driver.c @@ -623,6 +623,11 @@ drv_pre_init(ScrnInfoPtr pScrn, int flags) goto out_modes; } + if (!xf86LoadSubModule(pScrn, "dri3")) { + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Failed to load dri3 module.\n"); + goto out_modes; + } + return TRUE; out_modes: @@ -1055,6 +1060,16 @@ drv_screen_init(SCREEN_INIT_ARGS_DECL) ms->xat = NULL; ms->from_render = X_PROBED; } +#ifdef DRI3 + if (major == VMW_XA_VERSION_MAJOR_DRI3 && + minor >= VMW_XA_VERSION_MINOR_DRI3) { + ms->xa_dri3 = TRUE; + } else { + ms->xa_dri3 = FALSE; + LogMessage(X_WARNING, + "Gallium3D XA version insufficient for dri3.\n"); + } +#endif } if (ms->xat == NULL && ms->rendercheck) { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, @@ -1079,12 +1094,21 @@ drv_screen_init(SCREEN_INIT_ARGS_DECL) } ms->dri2_available = FALSE; + ms->dri3_available = FALSE; if (ms->enable_dri) { if (ms->xat) { ms->dri2_available = xorg_dri2_init(pScreen); if (!ms->dri2_available) xf86DrvMsg(pScrn->scrnIndex, X_ERROR, - "Failed to initialize direct rendering.\n"); + "Failed to initialize direct rendering DRI2.\n"); +#ifdef DRI3 + if (ms->xa_dri3) { + ms->dri3_available = vmwgfx_dri3_init(pScreen); + if (!ms->dri3_available) + xf86DrvMsg(pScrn->scrnIndex, X_ERROR, + "Failed to initialize direct rendering DRI3.\n"); + } +#endif /* DRI3 */ } else { xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "Skipped initialization of direct rendering due " @@ -1100,8 +1124,14 @@ drv_screen_init(SCREEN_INIT_ARGS_DECL) "Rendercheck mode is %s.\n", (ms->rendercheck) ? "enabled" : "disabled"); - xf86DrvMsg(pScrn->scrnIndex, ms->from_dri, "Direct rendering (3D) is %s.\n", + xf86DrvMsg(pScrn->scrnIndex, ms->from_dri, + "Direct rendering (DRI2 3D) is %s.\n", (ms->dri2_available) ? "enabled" : "disabled"); +#ifdef DRI3 + xf86DrvMsg(pScrn->scrnIndex, ms->from_dri, + "Direct rendering (DRI3 3D) is %s.\n", + (ms->dri3_available) ? "enabled" : "disabled"); +#endif if (ms->xat != NULL) { xf86DrvMsg(pScrn->scrnIndex, ms->from_dp, "Direct presents are %s.\n", (ms->direct_presents) ? "enabled" : "disabled"); diff --git a/vmwgfx/vmwgfx_driver.h b/vmwgfx/vmwgfx_driver.h index 7c7177f..d5aea75 100644 --- a/vmwgfx/vmwgfx_driver.h +++ b/vmwgfx/vmwgfx_driver.h @@ -153,6 +153,10 @@ typedef struct _modesettingRec struct vmwgfx_layout *layout; #endif Bool autoLayout; +#ifdef DRI3 + Bool xa_dri3; + Bool dri3_available; +#endif } modesettingRec, *modesettingPtr; #define modesettingPTR(p) ((modesettingPtr)((p)->driverPrivate)) @@ -232,4 +236,13 @@ vmw_video_free_adaptor(XF86VideoAdaptorPtr adaptor, Bool free_ports); void vmw_ctrl_ext_init(ScrnInfoPtr pScrn); +/*********************************************************************** + * vmwgfx_dri3.c + */ +#define VMW_XA_VERSION_MAJOR_DRI3 2 +#define VMW_XA_VERSION_MINOR_DRI3 3 + +Bool +vmwgfx_dri3_init(ScreenPtr screen); + #endif /* _XORG_TRACKER_H_ */ diff --git a/vmwgfx/vmwgfx_saa.c b/vmwgfx/vmwgfx_saa.c index b8ad2a7..0881e2f 100644 --- a/vmwgfx/vmwgfx_saa.c +++ b/vmwgfx/vmwgfx_saa.c @@ -371,6 +371,7 @@ vmwgfx_download_from_hw(struct saa_driver *driver, PixmapPtr pixmap, goto out_err; REGION_SUBTRACT(vsaa->pScreen, &spix->dirty_hw, &spix->dirty_hw, readback); REGION_UNINIT(vsaa->pScreen, &intersection); + return TRUE; out_err: REGION_UNINIT(vsaa->pScreen, &intersection); @@ -522,9 +523,6 @@ vmwgfx_destroy_pixmap(struct saa_driver *driver, PixmapPtr pixmap) vmwgfx_pixmap_remove_present(vpix); WSBMLISTDELINIT(&vpix->pixmap_list); WSBMLISTDELINIT(&vpix->sync_x_head); - - if (vpix->hw_is_dri2_fronts) - LogMessage(X_ERROR, "Incorrect dri2 front count.\n"); } @@ -799,7 +797,8 @@ vmwgfx_prefer_gmr(struct vmwgfx_saa *vsaa, PixmapPtr pixmap) Bool vmwgfx_create_hw(struct vmwgfx_saa *vsaa, - PixmapPtr pixmap) + PixmapPtr pixmap, + Bool shared) { struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap); struct xa_surface *hw; @@ -808,19 +807,25 @@ vmwgfx_create_hw(struct vmwgfx_saa *vsaa, if (!vsaa->xat) return FALSE; - if (vpix->hw) - return TRUE; + if (!shared) { + if (vpix->hw) + return TRUE; - new_flags = (vpix->xa_flags & ~vpix->staging_remove_flags) | - vpix->staging_add_flags | XA_FLAG_SHARED; + new_flags = (vpix->xa_flags & ~vpix->staging_remove_flags) | + vpix->staging_add_flags | XA_FLAG_SHARED; + + hw = xa_surface_create(vsaa->xat, + pixmap->drawable.width, + pixmap->drawable.height, + 0, + xa_type_other, + vpix->staging_format, + new_flags); + } else { + new_flags = vpix->xa_flags; + hw = vpix->hw; + } - hw = xa_surface_create(vsaa->xat, - pixmap->drawable.width, - pixmap->drawable.height, - 0, - xa_type_other, - vpix->staging_format, - new_flags); if (hw == NULL) return FALSE; diff --git a/vmwgfx/vmwgfx_saa_priv.h b/vmwgfx/vmwgfx_saa_priv.h index 507833a..6ec32e9 100644 --- a/vmwgfx/vmwgfx_saa_priv.h +++ b/vmwgfx/vmwgfx_saa_priv.h @@ -77,7 +77,8 @@ vmwgfx_hw_kill(struct vmwgfx_saa *vsaa, struct saa_pixmap *spix); Bool vmwgfx_create_hw(struct vmwgfx_saa *vsaa, - PixmapPtr pixmap); + PixmapPtr pixmap, + Bool shared); /* @@ -88,7 +89,8 @@ enum xa_formats vmwgfx_xa_format(enum _PictFormatShort format); Bool vmwgfx_hw_validate(PixmapPtr pixmap, RegionPtr region); - +Bool +vmwgfx_hw_dri2_stage(PixmapPtr pixmap, unsigned int depth); Bool vmwgfx_hw_accel_stage(PixmapPtr pixmap, unsigned int depth, uint32_t add_flags, uint32_t remove_flags); diff --git a/vmwgfx/vmwgfx_xa_surface.c b/vmwgfx/vmwgfx_xa_surface.c index a30b41c..38f7473 100644 --- a/vmwgfx/vmwgfx_xa_surface.c +++ b/vmwgfx/vmwgfx_xa_surface.c @@ -114,7 +114,7 @@ vmwgfx_xa_format(enum _PictFormatShort format) /* * Choose formats and flags for a dri2 surface. */ -static Bool +Bool vmwgfx_hw_dri2_stage(PixmapPtr pixmap, unsigned int depth) { struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap); @@ -379,7 +379,7 @@ vmwgfx_hw_commit(PixmapPtr pixmap) new_flags, 1) != XA_ERR_NONE) return FALSE; vpix->xa_flags = new_flags; - } else if (!vmwgfx_create_hw(vsaa, pixmap)) + } else if (!vmwgfx_create_hw(vsaa, pixmap, FALSE)) return FALSE; return TRUE; -- cgit v1.2.3