1 /*
2  * Copyright 2017 VMWare, Inc.
3  * All Rights Reserved.
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the
7  * "Software"), to deal in the Software without restriction, including
8  * without limitation the rights to use, copy, modify, merge, publish,
9  * distribute, sub license, and/or sell copies of the Software, and to
10  * permit persons to whom the Software is furnished to do so, subject to
11  * the following conditions:
12  *
13  * The above copyright notice and this permission notice (including the
14  * next paragraph) shall be included in all copies or substantial portions
15  * of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
18  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
20  * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
21  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  *
25  * Author: Thomas Hellstrom <thellstrom@vmware.com>
26  *
27  */
28 #ifdef _HAVE_CONFIG_H_
29 #include "config.h"
30 #endif
31 
32 #include <xorg-server.h>
33 
34 #ifdef DRI3
35 #include "vmwgfx_driver.h"
36 #if (XA_TRACKER_VERSION_MAJOR == VMW_XA_VERSION_MAJOR_DRI3 &&   \
37      XA_TRACKER_VERSION_MINOR >= VMW_XA_VERSION_MINOR_DRI3)
38 
39 #include "vmwgfx_driver.h"
40 #include "vmwgfx_saa_priv.h"
41 #include <dri3.h>
42 #include <misyncshm.h>
43 #include <xf86drm.h>
44 #include <unistd.h>
45 
46 
47 /**
48  * \brief DRI3 fd_from_pixmap callback.
49  *
50  */
51 static int
vmwgfx_dri3_fd_from_pixmap(ScreenPtr screen,PixmapPtr pixmap,CARD16 * stride,CARD32 * size)52 vmwgfx_dri3_fd_from_pixmap(ScreenPtr screen, PixmapPtr pixmap,
53                            CARD16 *stride, CARD32 *size)
54 {
55     struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(pixmap);
56     uint32_t handle;
57     unsigned int byte_stride;
58     ScrnInfoPtr pScrn = xf86ScreenToScrn(screen);
59 
60     if (!vmwgfx_hw_dri2_validate(pixmap, 0)) {
61         xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
62                    "DRI3 pixmap export failed to create HW surface.\n");
63         return -1;
64     }
65 
66     if (xa_surface_handle(vpix->hw, xa_handle_type_fd, &handle,
67                           &byte_stride)) {
68         xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
69                    "DRI3 pixmap export failed to create handle.\n");
70         return -1;
71     }
72 
73     *stride = byte_stride;
74     *size = byte_stride * pixmap->drawable.height;
75 
76     /*
77      * hw_is_dri2_fronts will make sure any software rendering to
78      * this pixmap is immediately flushed to the underlying surface.
79      * Strictly, we could wait for glxWaitX to do that, but alas,
80      * even the dri3 glxWaitX appears as broken as the dri2 version.
81      * If we, however, wanted to do that, we'd hook up a shm fence
82      * trigger callback. (Like glamor does).
83      */
84     vpix->hw_is_dri2_fronts = 1;
85 
86     return handle;
87 }
88 
89 /**
90  * \brief DRI3 pixmap_from_fd callback.
91  *
92  */
93 static PixmapPtr
vmwgfx_dri3_pixmap_from_fd(ScreenPtr screen,int fd,CARD16 width,CARD16 height,CARD16 stride,CARD8 depth,CARD8 bpp)94 vmwgfx_dri3_pixmap_from_fd(ScreenPtr screen, int fd,
95                            CARD16 width, CARD16 height, CARD16 stride,
96                            CARD8 depth, CARD8 bpp)
97 {
98     struct vmwgfx_saa *vsaa = to_vmwgfx_saa(saa_get_driver(screen));
99     struct xa_surface *srf;
100     struct vmwgfx_saa_pixmap *vpix;
101     ScrnInfoPtr pScrn = xf86ScreenToScrn(screen);
102     PixmapPtr pixmap;
103 
104     if (width == 0 || height == 0 ||
105         depth < 15 || bpp != BitsPerPixel(depth) || stride < width * bpp / 8)
106         return NULL;
107 
108     pixmap = screen->CreatePixmap(screen, width, height, depth, 0);
109     if (!pixmap) {
110         xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "DRI3 pixmap creation failed.\n");
111         return NULL;
112     }
113 
114     vpix = vmwgfx_saa_pixmap(pixmap);
115 
116     if (!vmwgfx_hw_dri2_stage(pixmap, depth)) {
117         xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
118                    "DRI3 pixmap creation bad format.\n");
119         goto out_bad_format;
120     }
121 
122     srf = xa_surface_from_handle2(vsaa->xat, width, height, depth,
123                                   xa_type_other,
124                                   vpix->staging_format,
125                                   vpix->staging_add_flags,
126                                   xa_handle_type_fd,
127                                   fd, stride);
128     if (!srf) {
129         xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
130                    "DRI3 pixmap creation surface sharing failed.\n");
131         goto out_bad_format;
132     }
133 
134     vpix->xa_flags = vpix->staging_add_flags;
135     vpix->hw = srf;
136     if (!vmwgfx_create_hw(vsaa, pixmap, TRUE)) {
137         xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
138                    "DRI3 pixmap creation failed SAA enabling.\n");
139         goto out_no_damage;
140     }
141 
142     vpix->hw_is_dri2_fronts = 1;
143     return pixmap;
144 
145   out_no_damage:
146     xa_surface_unref(srf);
147   out_bad_format:
148     screen->DestroyPixmap(pixmap);
149 
150     return NULL;
151 }
152 
153 /**
154  * \brief Open a render node.
155  *
156  * \param screen[IN]  Pointer to the screen
157  * \return  A valid file descriptor or -1 on failure.
158  */
159 static int
vmwgfx_dri3_open_render(ScreenPtr screen)160 vmwgfx_dri3_open_render(ScreenPtr screen)
161 {
162     ScrnInfoPtr pScrn = xf86ScreenToScrn(screen);
163     modesettingPtr ms = modesettingPTR(pScrn);
164     char bus_id[64];
165     int fd;
166 
167     snprintf(bus_id, sizeof(bus_id), "PCI:%d:%d:%d",
168              ((ms->PciInfo->domain << 8) | ms->PciInfo->bus),
169              ms->PciInfo->dev, ms->PciInfo->func);
170 
171     /* Render nodes can't be opened by busid yet.. */
172     fd = drmOpenWithType("vmwgfx", bus_id, DRM_NODE_RENDER);
173     if (fd < 0)
174         xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
175                    "DRI3 client open busid \"%s\" failed.\n", bus_id);
176 
177     return fd;
178 }
179 
180 /**
181  * \brief DRI3 open_client callback.
182  *
183  */
184 static int
vmwgfx_dri3_open_client(ClientPtr client,ScreenPtr screen,RRProviderPtr provider,int * pfd)185 vmwgfx_dri3_open_client(ClientPtr client, ScreenPtr screen,
186                         RRProviderPtr provider, int *pfd)
187 {
188     *pfd = vmwgfx_dri3_open_render(screen);
189 
190     return (*pfd >= 0) ? Success : BadAlloc;
191 }
192 
193 /**
194  * \brief Verify that surface sharing between render client and X server
195  * works.
196  *
197  * \param screen[IN,OUT]  A pointer to the current screen.
198  * \return TRUE if successful, FALSE otherwise.
199  *
200  * Opens a render client, creates a surface and tries to share that surface
201  * with the X server. There is a vmwgfx kernel driver bug that, combined
202  * with a pre-guest-backed-surface svga mesa driver bug,
203  * prevents this sharing to happen and thus breaks dri3.
204  *
205  * Also, we need to make sure that we can share an XRGB surface as an
206  * ARGB surface since DRI3 does not appear to be as strict about internal
207  * surface formats as DRI2.
208  */
209 static Bool
vmwgfx_dri3_verify_sharing(ScreenPtr screen)210 vmwgfx_dri3_verify_sharing(ScreenPtr screen)
211 {
212     ScrnInfoPtr pScrn = xf86ScreenToScrn(screen);
213     modesettingPtr ms = modesettingPTR(pScrn);
214     int fd = vmwgfx_dri3_open_render(screen);
215     struct xa_tracker *xat;
216     struct xa_surface *srf1;
217     unsigned int stride;
218     uint32_t handle;
219     Bool ret = FALSE;
220 
221     if (fd < 0)
222         return FALSE;
223 
224     xat = xa_tracker_create(fd);
225     if (!xat)
226         goto out_no_xa;
227 
228     /* Here we're the render client (xat) */
229     srf1 = xa_surface_create(xat, 16, 16, 32, xa_type_argb,
230                              xa_format_unknown,
231                              XA_FLAG_RENDER_TARGET | XA_FLAG_SHARED);
232     if (!srf1)
233         goto out_no_surface;
234 
235     if (xa_surface_handle(srf1, xa_handle_type_fd, &handle, &stride) !=
236         XA_ERR_NONE)
237         goto out_no_handle;
238 
239     xa_surface_unref(srf1);
240 
241     /* Now we're the X server (ms->xat) */
242     srf1 = xa_surface_from_handle2(ms->xat, 16, 16, 24, xa_type_argb,
243                                    xa_format_unknown,
244                                    XA_FLAG_RENDER_TARGET | XA_FLAG_SHARED,
245                                    xa_handle_type_fd, handle, stride);
246     if (!srf1)
247         goto out_no_surface;
248 
249     ret = TRUE;
250     close(handle);
251 
252   out_no_handle:
253     xa_surface_unref(srf1);
254   out_no_surface:
255     xa_tracker_destroy(xat);
256   out_no_xa:
257     close(fd);
258 
259     return ret;
260 }
261 
262 static dri3_screen_info_rec vmwgfx_dri3_info = {
263     .version = 1,
264     .open = NULL,
265     .pixmap_from_fd = vmwgfx_dri3_pixmap_from_fd,
266     .fd_from_pixmap = vmwgfx_dri3_fd_from_pixmap,
267     .open_client = vmwgfx_dri3_open_client,
268 };
269 
270 
271 /**
272  * \brief Initialize dri3.
273  *
274  * \param screen[IN,OUT]  A pointer to the current screen.
275  * \return TRUE if successful, FALSE otherwise.
276  */
277 Bool
vmwgfx_dri3_init(ScreenPtr screen)278 vmwgfx_dri3_init(ScreenPtr screen)
279 {
280     ScrnInfoPtr pScrn = xf86ScreenToScrn(screen);
281 
282     if (!vmwgfx_dri3_verify_sharing(screen)) {
283         xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
284                    "Failed to verify XA surface sharing for DRI3.\n");
285         return FALSE;
286     }
287 
288     if (!miSyncShmScreenInit(screen)) {
289         xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
290                    "Failed to initialize xshm sync for DRI3.\n");
291         return FALSE;
292     }
293 
294     if (!dri3_screen_init(screen, &vmwgfx_dri3_info)) {
295         xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Failed to initialize DRI3.\n");
296         return FALSE;
297     }
298 
299     return TRUE;
300 }
301 
302 #else /* XA INCLUDES SUFFICIENT */
303 Bool
vmwgfx_dri3_init(ScreenPtr screen)304 vmwgfx_dri3_init(ScreenPtr screen)
305 {
306     return FALSE;
307 }
308 
309 #endif /* !XA INCLUDES SUFFICIENT */
310 #endif /* DRI3 */
311