vmwgfx_dri2.c revision 22f7e8e5
1/*
2 * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas.
3 * Copyright 2011 VMWare, Inc.
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21 * IN NO EVENT SHALL TUNGSTEN GRAPHICS AND/OR ITS SUPPLIERS BE LIABLE FOR
22 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 *
27 * Author: Alan Hourihane <alanh@tungstengraphics.com>
28 * Author: Jakob Bornecrantz <wallbraker@gmail.com>
29 * Author: Thomas Hellstrom <thellstrom@vmware.com>
30 *
31 */
32
33#include "xorg-server.h"
34#include "xf86.h"
35#include "xf86_OSproc.h"
36
37#include "vmwgfx_driver.h"
38#include "../saa/saa.h"
39
40#include "dri2.h"
41#include "gcstruct.h"
42#include "gc.h"
43#include "vmwgfx_saa.h"
44#include "wsbm_util.h"
45#include <unistd.h>
46#include "vmwgfx_hosted.h"
47
48#define VMWGFX_FD_PATH_LEN 80
49
50typedef struct {
51    int refcount;
52    PixmapPtr pPixmap;
53    struct xa_surface *srf;
54    unsigned int dri2_depth;
55} *BufferPrivatePtr;
56
57
58/*
59 * Attempt to guess what the dri state tracker is up to.
60 * Currently it sends only bpp as format.
61 */
62
63static unsigned int
64vmwgfx_color_format_to_depth(unsigned int format)
65{
66    return format;
67}
68
69static unsigned int
70vmwgfx_zs_format_to_depth(unsigned int format)
71{
72    if (format == 24)
73	return 32;
74    return format;
75}
76
77static unsigned int
78vmwgfx_z_format_to_depth(unsigned int format)
79{
80    return format;
81}
82
83static Bool
84dri2_do_create_buffer(DrawablePtr pDraw, DRI2Buffer2Ptr buffer, unsigned int format)
85{
86    ScreenPtr pScreen = pDraw->pScreen;
87    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
88    modesettingPtr ms = modesettingPTR(pScrn);
89    BufferPrivatePtr private = buffer->driverPrivate;
90    PixmapPtr pPixmap;
91    struct vmwgfx_saa_pixmap *vpix;
92    struct xa_surface *srf = NULL;
93    unsigned int depth;
94
95
96    if (pDraw->type == DRAWABLE_PIXMAP)
97	pPixmap = (PixmapPtr) pDraw;
98    else
99	pPixmap = (*pScreen->GetWindowPixmap)((WindowPtr) pDraw);
100
101    vpix = vmwgfx_saa_pixmap(pPixmap);
102    private->refcount = 0;
103
104    switch (buffer->attachment) {
105    default:
106	depth = (format) ? vmwgfx_color_format_to_depth(format) :
107	    pDraw->depth;
108
109	if (buffer->attachment != DRI2BufferFakeFrontLeft ||
110	    &pPixmap->drawable != pDraw) {
111
112	    pPixmap = (*pScreen->CreatePixmap)(pScreen,
113					       pDraw->width,
114					       pDraw->height,
115					       depth,
116					       0);
117	    if (pPixmap == NullPixmap)
118		return FALSE;
119
120	    private->pPixmap = pPixmap;
121	    private->dri2_depth = depth;
122	    vpix = vmwgfx_saa_pixmap(pPixmap);
123	}
124	break;
125    case DRI2BufferFrontLeft:
126      if (&pPixmap->drawable == pDraw)
127	  break;
128      buffer->name = 0;
129      buffer->pitch = 0;
130      buffer->cpp = pDraw->bitsPerPixel / 8;
131      buffer->driverPrivate = private;
132      buffer->flags = 0; /* not tiled */
133      buffer->format = pDraw->bitsPerPixel;
134      if (!private->pPixmap) {
135	private->dri2_depth = 0;
136	private->pPixmap = pPixmap;
137	pPixmap->refcnt++;
138      }
139      return TRUE;
140    case DRI2BufferStencil:
141    case DRI2BufferDepthStencil:
142	if (!pScrn->vtSema)
143	    return FALSE;
144
145	depth = (format) ? vmwgfx_zs_format_to_depth(format) : 32;
146
147	/*
148	 * The SVGA device uses the zs ordering.
149	 */
150
151	srf = xa_surface_create(ms->xat, pDraw->width, pDraw->height,
152				depth, xa_type_zs, xa_format_unknown,
153				XA_FLAG_SHARED );
154	if (!srf)
155	    return FALSE;
156
157	private->dri2_depth = depth;
158
159       break;
160    case DRI2BufferDepth:
161	if (!pScrn->vtSema)
162	    return FALSE;
163
164	depth = (format) ? vmwgfx_z_format_to_depth(format) :
165	    pDraw->bitsPerPixel;
166
167	if (depth == 24)
168	    srf = xa_surface_create(ms->xat, pDraw->width, pDraw->height,
169				    depth, xa_type_zs, xa_format_unknown,
170				    XA_FLAG_SHARED );
171	else
172	    srf = xa_surface_create(ms->xat, pDraw->width, pDraw->height,
173				    depth,
174				    xa_type_z, xa_format_unknown,
175				    XA_FLAG_SHARED);
176	if (!srf)
177	    return FALSE;
178
179	private->dri2_depth = depth;
180
181	break;
182    }
183
184    if (!private->pPixmap) {
185	private->pPixmap = pPixmap;
186	pPixmap->refcnt++;
187    }
188
189    if (!srf) {
190	depth = (format) ? vmwgfx_color_format_to_depth(format) :
191	    pDraw->depth;
192
193	if (!vmwgfx_hw_dri2_validate(pPixmap, depth))
194	    return FALSE;
195
196	srf = vpix->hw;
197	private->refcount++;
198	private->dri2_depth = depth;
199
200	/*
201	 * Compiz workaround. See vmwgfx_dirty();
202	 */
203
204	if (buffer->attachment == DRI2BufferFrontLeft ||
205	    buffer->attachment == DRI2BufferFakeFrontLeft)
206	    vpix->hw_is_dri2_fronts++;
207    }
208
209    private->srf = srf;
210    if (_xa_surface_handle(srf, &buffer->name, &buffer->pitch) != 0)
211	return FALSE;
212
213    buffer->cpp = xa_format_depth(xa_surface_format(srf)) / 8;
214    buffer->driverPrivate = private;
215    buffer->flags = 0; /* not tiled */
216    buffer->format = format;
217    private->refcount++;
218
219    return TRUE;
220}
221
222static void
223dri2_do_destroy_buffer(DrawablePtr pDraw, DRI2BufferPtr buffer)
224{
225    BufferPrivatePtr private = buffer->driverPrivate;
226    struct xa_surface *srf = private->srf;
227    ScreenPtr pScreen = pDraw->pScreen;
228    struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(private->pPixmap);
229
230    if (--private->refcount == 0 && srf) {
231	xa_surface_destroy(srf);
232    }
233
234    /*
235     * Compiz workaround. See vmwgfx_dirty();
236     */
237
238    if ((buffer->attachment == DRI2BufferFrontLeft ||
239	 buffer->attachment == DRI2BufferFakeFrontLeft) &&
240	private->refcount == 1 &&
241	--vpix->hw_is_dri2_fronts == 0)
242	WSBMLISTDELINIT(&vpix->sync_x_head);
243
244    private->srf = NULL;
245    pScreen->DestroyPixmap(private->pPixmap);
246}
247
248
249static DRI2Buffer2Ptr
250dri2_create_buffer(DrawablePtr pDraw, unsigned int attachment, unsigned int format)
251{
252    DRI2Buffer2Ptr buffer;
253    BufferPrivatePtr private;
254
255    buffer = calloc(1, sizeof *buffer);
256    if (!buffer)
257	return NULL;
258
259    private = calloc(1, sizeof *private);
260    if (!private) {
261	goto fail;
262    }
263
264    buffer->attachment = attachment;
265    buffer->driverPrivate = private;
266
267    if (dri2_do_create_buffer(pDraw, buffer, format))
268	return buffer;
269
270    free(private);
271fail:
272    free(buffer);
273    return NULL;
274}
275
276static void
277dri2_destroy_buffer(DrawablePtr pDraw, DRI2Buffer2Ptr buffer)
278{
279    /* So far it is safe to downcast a DRI2Buffer2Ptr to DRI2BufferPtr */
280    dri2_do_destroy_buffer(pDraw, (DRI2BufferPtr)buffer);
281
282    free(buffer->driverPrivate);
283    free(buffer);
284}
285
286static void
287dri2_copy_region(DrawablePtr pDraw, RegionPtr pRegion,
288                 DRI2Buffer2Ptr pDestBuffer, DRI2Buffer2Ptr pSrcBuffer)
289{
290
291
292    ScreenPtr pScreen = pDraw->pScreen;
293    BufferPrivatePtr dst_priv = pDestBuffer->driverPrivate;
294    BufferPrivatePtr src_priv = pSrcBuffer->driverPrivate;
295    DrawablePtr src_draw;
296    DrawablePtr dst_draw;
297    RegionPtr myClip;
298    GCPtr gc;
299    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
300
301    /*
302     * This is a fragile protection against HW operations when not master.
303     * Needs to be blocked higher up in the dri2 code.
304     */
305    if (!pScrn->vtSema)
306	return;
307
308    /*
309     * In driCreateBuffers we dewrap windows into the
310     * backing pixmaps in order to get to the texture.
311     * We need to use the real drawable in CopyArea
312     * so that cliprects and offsets are correct.
313     */
314    src_draw = (pSrcBuffer->attachment == DRI2BufferFrontLeft) ? pDraw :
315       &src_priv->pPixmap->drawable;
316    dst_draw = (pDestBuffer->attachment == DRI2BufferFrontLeft) ? pDraw :
317       &dst_priv->pPixmap->drawable;
318
319    /*
320     * The clients implements glXWaitX with a copy front to fake and then
321     * waiting on the server to signal its completion of it. While
322     * glXWaitGL is a client side flush and a copy from fake to front.
323     * This is how it is done in the DRI2 protocol, how ever depending
324     * which type of drawables the server does things a bit differently
325     * then what the protocol says as the fake and front are the same.
326     *
327     * for pixmaps glXWaitX is a server flush.
328     * for pixmaps glXWaitGL is a client flush.
329     * for windows glXWaitX is a copy from front to fake then a server flush.
330     * for windows glXWaitGL is a client flush then a copy from fake to front.
331     *
332     * XXX in the windows case this code always flushes but that isn't a
333     * must in the glXWaitGL case but we don't know if this is a glXWaitGL
334     * or a glFlush/glFinish call.
335     */
336    if (dst_priv->pPixmap == src_priv->pPixmap) {
337	/* pixmap glXWaitX */
338	if (pSrcBuffer->attachment == DRI2BufferFrontLeft &&
339	    pDestBuffer->attachment == DRI2BufferFakeFrontLeft) {
340
341	    if (!vmwgfx_hw_dri2_validate(dst_priv->pPixmap,
342					 dst_priv->dri2_depth))
343		return;
344	}
345	/* pixmap glXWaitGL */
346	if (pDestBuffer->attachment == DRI2BufferFrontLeft &&
347	    pSrcBuffer->attachment == DRI2BufferFakeFrontLeft) {
348	    return;
349	} else {
350	    vmwgfx_flush_dri2(pScreen);
351	    return;
352	}
353    }
354
355    gc = GetScratchGC(pDraw->depth, pScreen);
356    myClip = REGION_CREATE(pScreen, REGION_RECTS(pRegion),
357			   REGION_NUM_RECTS(pRegion));
358    (*gc->funcs->ChangeClip) (gc, CT_REGION, myClip, 0);
359    ValidateGC(dst_draw, gc);
360
361    /*
362     * Damage the src drawable in order for damageCopyArea to pick up
363     * that something changed.
364     */
365    DamageRegionAppend(src_draw, pRegion);
366    if (pSrcBuffer->attachment != DRI2BufferFrontLeft)
367	saa_drawable_dirty(src_draw, TRUE, pRegion);
368    DamageRegionProcessPending(src_draw);
369
370    /*
371     * Call CopyArea. This usually means a call to damageCopyArea that
372     * is wrapping saa_copy_area. The damageCopyArea function will make
373     * sure the destination drawable is appropriately damaged.
374     */
375    (*gc->ops->CopyArea)(src_draw, dst_draw, gc,
376			 0, 0, pDraw->width, pDraw->height, 0, 0);
377
378    /*
379     * FreeScratchGC will free myClip as well.
380     */
381    myClip = NULL;
382    FreeScratchGC(gc);
383}
384
385#if (DRI2INFOREC_VERSION >= 8 && DRI2INFOREC_VERSION < 10)
386static int vmw_dri_auth_magic2(ScreenPtr pScreen, uint32_t magic)
387{
388    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
389    modesettingPtr ms = modesettingPTR(pScrn);
390
391    return vmwgfx_hosted_dri_auth(ms->hdriver, ms->hosted, NULL, magic);
392}
393#endif
394
395#if (DRI2INFOREC_VERSION >= 10)
396static int vmw_dri_auth_magic3(ClientPtr client, ScreenPtr pScreen,
397			       uint32_t magic)
398{
399    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
400    modesettingPtr ms = modesettingPTR(pScrn);
401
402    return vmwgfx_hosted_dri_auth(ms->hdriver, ms->hosted, client, magic);
403}
404#endif
405
406Bool
407xorg_dri2_init(ScreenPtr pScreen)
408{
409    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
410    modesettingPtr ms = modesettingPTR(pScrn);
411    DRI2InfoRec dri2info;
412    int major, minor;
413    char fdPath[VMWGFX_FD_PATH_LEN];
414    ssize_t numChar;
415
416    memset(&dri2info, 0, sizeof(dri2info));
417
418    if (xf86LoaderCheckSymbol("DRI2Version")) {
419	DRI2Version(&major, &minor);
420    } else {
421	/* Assume version 1.0 */
422	major = 1;
423	minor = 0;
424    }
425
426    dri2info.version = min(DRI2INFOREC_VERSION, 3);
427    dri2info.fd = ms->fd;
428    dri2info.driverName = "vmwgfx";
429
430    /*
431     * This way of obtaining the DRM device name is a bit
432     * os-specific. It would be better to obtain it from
433     * drmOpen. Currently this works only for Linux.
434     */
435    memset(fdPath, 0, VMWGFX_FD_PATH_LEN);
436    snprintf(fdPath, VMWGFX_FD_PATH_LEN - 1, "/proc/self/fd/%d", ms->fd);
437    numChar = readlink(fdPath, ms->dri2_device_name, VMWGFX_DRI_DEVICE_LEN);
438    if (numChar <= 0 || numChar >= VMWGFX_DRI_DEVICE_LEN) {
439	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
440		   "Could not find the drm device name. Disabling dri2.\n");
441	return FALSE;
442    }
443    ms->dri2_device_name[numChar] = 0;
444    dri2info.deviceName = ms->dri2_device_name;
445    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
446	       "Path of drm device is \"%s\".\n", ms->dri2_device_name);
447
448    dri2info.CreateBuffer = dri2_create_buffer;
449    dri2info.DestroyBuffer = dri2_destroy_buffer;
450
451    dri2info.CopyRegion = dri2_copy_region;
452    dri2info.Wait = NULL;
453
454#if (DRI2INFOREC_VERSION >= 8 && DRI2INFOREC_VERSION < 10)
455    if (vmwgfx_is_hosted(ms->hdriver)) {
456	dri2info.version = 8;
457	dri2info.AuthMagic2 = vmw_dri_auth_magic2;
458    }
459#endif
460#if (DRI2INFOREC_VERSION >= 10)
461    if (vmwgfx_is_hosted(ms->hdriver)) {
462	dri2info.version = 10;
463	dri2info.AuthMagic3 = vmw_dri_auth_magic3;
464    }
465#endif
466
467    return DRI2ScreenInit(pScreen, &dri2info);
468}
469
470void
471xorg_dri2_close(ScreenPtr pScreen)
472{
473    DRI2CloseScreen(pScreen);
474}
475
476/* vim: set sw=4 ts=8 sts=4: */
477