vmwgfx_dri2.c revision 3bfa90b6
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
47#define VMWGFX_FD_PATH_LEN 80
48
49typedef struct {
50    int refcount;
51    PixmapPtr pPixmap;
52    struct xa_surface *srf;
53    unsigned int dri2_depth;
54} *BufferPrivatePtr;
55
56
57/*
58 * Attempt to guess what the dri state tracker is up to.
59 * Currently it sends only bpp as format.
60 */
61
62static unsigned int
63vmwgfx_color_format_to_depth(unsigned int format)
64{
65    return format;
66}
67
68static unsigned int
69vmwgfx_zs_format_to_depth(unsigned int format)
70{
71    if (format == 24)
72	return 32;
73    return format;
74}
75
76static unsigned int
77vmwgfx_z_format_to_depth(unsigned int format)
78{
79    return format;
80}
81
82static Bool
83dri2_do_create_buffer(DrawablePtr pDraw, DRI2Buffer2Ptr buffer, unsigned int format)
84{
85    ScreenPtr pScreen = pDraw->pScreen;
86    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
87    modesettingPtr ms = modesettingPTR(pScrn);
88    BufferPrivatePtr private = buffer->driverPrivate;
89    PixmapPtr pPixmap;
90    struct vmwgfx_saa_pixmap *vpix;
91    struct xa_surface *srf = NULL;
92    unsigned int depth;
93
94
95    if (pDraw->type == DRAWABLE_PIXMAP)
96	pPixmap = (PixmapPtr) pDraw;
97    else
98	pPixmap = (*pScreen->GetWindowPixmap)((WindowPtr) pDraw);
99
100    vpix = vmwgfx_saa_pixmap(pPixmap);
101    private->refcount = 0;
102
103    switch (buffer->attachment) {
104    default:
105	depth = (format) ? vmwgfx_color_format_to_depth(format) :
106	    pDraw->depth;
107
108	if (buffer->attachment != DRI2BufferFakeFrontLeft ||
109	    &pPixmap->drawable != pDraw) {
110
111	    pPixmap = (*pScreen->CreatePixmap)(pScreen,
112					       pDraw->width,
113					       pDraw->height,
114					       depth,
115					       0);
116	    if (pPixmap == NullPixmap)
117		return FALSE;
118
119	    private->pPixmap = pPixmap;
120	    private->dri2_depth = depth;
121	    vpix = vmwgfx_saa_pixmap(pPixmap);
122	}
123	break;
124    case DRI2BufferFrontLeft:
125      if (&pPixmap->drawable == pDraw)
126	  break;
127      buffer->name = 0;
128      buffer->pitch = 0;
129      buffer->cpp = pDraw->bitsPerPixel / 8;
130      buffer->driverPrivate = private;
131      buffer->flags = 0; /* not tiled */
132      buffer->format = pDraw->bitsPerPixel;
133      if (!private->pPixmap) {
134	private->dri2_depth = 0;
135	private->pPixmap = pPixmap;
136	pPixmap->refcnt++;
137      }
138      return TRUE;
139    case DRI2BufferStencil:
140    case DRI2BufferDepthStencil:
141
142	depth = (format) ? vmwgfx_zs_format_to_depth(format) : 32;
143
144	/*
145	 * The SVGA device uses the zs ordering.
146	 */
147
148	srf = xa_surface_create(ms->xat, pDraw->width, pDraw->height,
149				depth, xa_type_zs, xa_format_unknown,
150				XA_FLAG_SHARED );
151	if (!srf)
152	    return FALSE;
153
154	private->dri2_depth = depth;
155
156       break;
157    case DRI2BufferDepth:
158	depth = (format) ? vmwgfx_z_format_to_depth(format) :
159	    pDraw->bitsPerPixel;
160
161	if (depth == 24)
162	    srf = xa_surface_create(ms->xat, pDraw->width, pDraw->height,
163				    depth, xa_type_zs, xa_format_unknown,
164				    XA_FLAG_SHARED );
165	else
166	    srf = xa_surface_create(ms->xat, pDraw->width, pDraw->height,
167				    depth,
168				    xa_type_z, xa_format_unknown,
169				    XA_FLAG_SHARED);
170	if (!srf)
171	    return FALSE;
172
173	private->dri2_depth = depth;
174
175	break;
176    }
177
178    if (!private->pPixmap) {
179	private->pPixmap = pPixmap;
180	pPixmap->refcnt++;
181    }
182
183    if (!srf) {
184	depth = (format) ? vmwgfx_color_format_to_depth(format) :
185	    pDraw->depth;
186
187	if (!vmwgfx_hw_dri2_validate(pPixmap, depth))
188	    return FALSE;
189
190	srf = vpix->hw;
191	private->refcount++;
192	private->dri2_depth = depth;
193
194	/*
195	 * Compiz workaround. See vmwgfx_dirty();
196	 */
197
198	if (buffer->attachment == DRI2BufferFrontLeft ||
199	    buffer->attachment == DRI2BufferFakeFrontLeft)
200	    vpix->hw_is_dri2_fronts++;
201    }
202
203    private->srf = srf;
204    if (xa_surface_handle(srf, &buffer->name, &buffer->pitch) != 0)
205	return FALSE;
206
207    buffer->cpp = xa_format_depth(xa_surface_format(srf)) / 8;
208    buffer->driverPrivate = private;
209    buffer->flags = 0; /* not tiled */
210    buffer->format = format;
211    private->refcount++;
212
213    return TRUE;
214}
215
216static void
217dri2_do_destroy_buffer(DrawablePtr pDraw, DRI2BufferPtr buffer)
218{
219    BufferPrivatePtr private = buffer->driverPrivate;
220    struct xa_surface *srf = private->srf;
221    ScreenPtr pScreen = pDraw->pScreen;
222    struct vmwgfx_saa_pixmap *vpix = vmwgfx_saa_pixmap(private->pPixmap);
223
224    if (--private->refcount == 0 && srf) {
225	xa_surface_destroy(srf);
226    }
227
228    /*
229     * Compiz workaround. See vmwgfx_dirty();
230     */
231
232    if ((buffer->attachment == DRI2BufferFrontLeft ||
233	 buffer->attachment == DRI2BufferFakeFrontLeft) &&
234	private->refcount == 1 &&
235	--vpix->hw_is_dri2_fronts == 0)
236	WSBMLISTDELINIT(&vpix->sync_x_head);
237
238    private->srf = NULL;
239    pScreen->DestroyPixmap(private->pPixmap);
240}
241
242
243static DRI2Buffer2Ptr
244dri2_create_buffer(DrawablePtr pDraw, unsigned int attachment, unsigned int format)
245{
246    DRI2Buffer2Ptr buffer;
247    BufferPrivatePtr private;
248
249    buffer = calloc(1, sizeof *buffer);
250    if (!buffer)
251	return NULL;
252
253    private = calloc(1, sizeof *private);
254    if (!private) {
255	goto fail;
256    }
257
258    buffer->attachment = attachment;
259    buffer->driverPrivate = private;
260
261    if (dri2_do_create_buffer(pDraw, buffer, format))
262	return buffer;
263
264    free(private);
265fail:
266    free(buffer);
267    return NULL;
268}
269
270static void
271dri2_destroy_buffer(DrawablePtr pDraw, DRI2Buffer2Ptr buffer)
272{
273    /* So far it is safe to downcast a DRI2Buffer2Ptr to DRI2BufferPtr */
274    dri2_do_destroy_buffer(pDraw, (DRI2BufferPtr)buffer);
275
276    free(buffer->driverPrivate);
277    free(buffer);
278}
279
280static void
281dri2_copy_region(DrawablePtr pDraw, RegionPtr pRegion,
282                 DRI2Buffer2Ptr pDestBuffer, DRI2Buffer2Ptr pSrcBuffer)
283{
284
285
286    ScreenPtr pScreen = pDraw->pScreen;
287    BufferPrivatePtr dst_priv = pDestBuffer->driverPrivate;
288    BufferPrivatePtr src_priv = pSrcBuffer->driverPrivate;
289    DrawablePtr src_draw;
290    DrawablePtr dst_draw;
291    RegionPtr myClip;
292    GCPtr gc;
293
294    /*
295     * In driCreateBuffers we dewrap windows into the
296     * backing pixmaps in order to get to the texture.
297     * We need to use the real drawable in CopyArea
298     * so that cliprects and offsets are correct.
299     */
300    src_draw = (pSrcBuffer->attachment == DRI2BufferFrontLeft) ? pDraw :
301       &src_priv->pPixmap->drawable;
302    dst_draw = (pDestBuffer->attachment == DRI2BufferFrontLeft) ? pDraw :
303       &dst_priv->pPixmap->drawable;
304
305    /*
306     * The clients implements glXWaitX with a copy front to fake and then
307     * waiting on the server to signal its completion of it. While
308     * glXWaitGL is a client side flush and a copy from fake to front.
309     * This is how it is done in the DRI2 protocol, how ever depending
310     * which type of drawables the server does things a bit differently
311     * then what the protocol says as the fake and front are the same.
312     *
313     * for pixmaps glXWaitX is a server flush.
314     * for pixmaps glXWaitGL is a client flush.
315     * for windows glXWaitX is a copy from front to fake then a server flush.
316     * for windows glXWaitGL is a client flush then a copy from fake to front.
317     *
318     * XXX in the windows case this code always flushes but that isn't a
319     * must in the glXWaitGL case but we don't know if this is a glXWaitGL
320     * or a glFlush/glFinish call.
321     */
322    if (dst_priv->pPixmap == src_priv->pPixmap) {
323	/* pixmap glXWaitX */
324	if (pSrcBuffer->attachment == DRI2BufferFrontLeft &&
325	    pDestBuffer->attachment == DRI2BufferFakeFrontLeft) {
326
327	    if (!vmwgfx_hw_dri2_validate(dst_priv->pPixmap,
328					 dst_priv->dri2_depth))
329		return;
330	}
331	/* pixmap glXWaitGL */
332	if (pDestBuffer->attachment == DRI2BufferFrontLeft &&
333	    pSrcBuffer->attachment == DRI2BufferFakeFrontLeft) {
334	    return;
335	} else {
336	    vmwgfx_flush_dri2(pScreen);
337	    return;
338	}
339    }
340
341    gc = GetScratchGC(pDraw->depth, pScreen);
342    myClip = REGION_CREATE(pScreen, REGION_RECTS(pRegion),
343			   REGION_NUM_RECTS(pRegion));
344    (*gc->funcs->ChangeClip) (gc, CT_REGION, myClip, 0);
345    ValidateGC(dst_draw, gc);
346
347    /*
348     * Damage the src drawable in order for damageCopyArea to pick up
349     * that something changed.
350     */
351    DamageRegionAppend(src_draw, pRegion);
352    if (pSrcBuffer->attachment != DRI2BufferFrontLeft)
353	saa_drawable_dirty(src_draw, TRUE, pRegion);
354    DamageRegionProcessPending(src_draw);
355
356    /*
357     * Call CopyArea. This usually means a call to damageCopyArea that
358     * is wrapping saa_copy_area. The damageCopyArea function will make
359     * sure the destination drawable is appropriately damaged.
360     */
361    (*gc->ops->CopyArea)(src_draw, dst_draw, gc,
362			 0, 0, pDraw->width, pDraw->height, 0, 0);
363
364    /*
365     * FreeScratchGC will free myClip as well.
366     */
367    myClip = NULL;
368    FreeScratchGC(gc);
369}
370
371Bool
372xorg_dri2_init(ScreenPtr pScreen)
373{
374    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
375    modesettingPtr ms = modesettingPTR(pScrn);
376    DRI2InfoRec dri2info;
377    int major, minor;
378    char fdPath[VMWGFX_FD_PATH_LEN];
379    ssize_t numChar;
380
381    if (xf86LoaderCheckSymbol("DRI2Version")) {
382	DRI2Version(&major, &minor);
383    } else {
384	/* Assume version 1.0 */
385	major = 1;
386	minor = 0;
387    }
388
389    dri2info.version = min(DRI2INFOREC_VERSION, 3);
390    dri2info.fd = ms->fd;
391    dri2info.driverName = "vmwgfx";
392
393    /*
394     * This way of obtaining the DRM device name is a bit
395     * os-specific. It would be better to obtain it from
396     * drmOpen. Currently this works only for Linux.
397     */
398    memset(fdPath, 0, VMWGFX_FD_PATH_LEN);
399    snprintf(fdPath, VMWGFX_FD_PATH_LEN - 1, "/proc/self/fd/%d", ms->fd);
400    numChar = readlink(fdPath, ms->dri2_device_name, VMWGFX_DRI_DEVICE_LEN);
401    if (numChar <= 0 || numChar >= VMWGFX_DRI_DEVICE_LEN) {
402	xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
403		   "Could not find the drm device name. Disabling dri2.\n");
404	return FALSE;
405    }
406    ms->dri2_device_name[numChar] = 0;
407    dri2info.deviceName = ms->dri2_device_name;
408    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
409	       "Path of drm device is \"%s\".\n", ms->dri2_device_name);
410
411    dri2info.CreateBuffer = dri2_create_buffer;
412    dri2info.DestroyBuffer = dri2_destroy_buffer;
413
414    dri2info.CopyRegion = dri2_copy_region;
415    dri2info.Wait = NULL;
416
417    return DRI2ScreenInit(pScreen, &dri2info);
418}
419
420void
421xorg_dri2_close(ScreenPtr pScreen)
422{
423    DRI2CloseScreen(pScreen);
424}
425
426/* vim: set sw=4 ts=8 sts=4: */
427