radeon_glamor.c revision cd2eb4f7
1/*
2 * Copyright © 2011 Intel Corporation.
3 *             2012 Advanced Micro Devices, Inc.
4 *
5 * Permission is hereby granted, free of charge, to any person
6 * obtaining a copy of this software and associated documentation
7 * files (the "Software"), to deal in the Software without
8 * restriction, including without limitation the rights to use, copy,
9 * modify, merge, publish, distribute, sublicense, and/or sell copies
10 * of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including
14 * the next paragraph) shall be included in all copies or substantial
15 * portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
25 */
26
27#ifdef HAVE_CONFIG_H
28#include "config.h"
29#endif
30
31#include <xf86.h>
32
33#include "radeon.h"
34#include "radeon_bo_helper.h"
35#include "radeon_glamor.h"
36
37DevPrivateKeyRec glamor_pixmap_index;
38
39void
40radeon_glamor_exchange_buffers(PixmapPtr src,
41			       PixmapPtr dst)
42{
43	RADEONInfoPtr info = RADEONPTR(xf86ScreenToScrn(dst->drawable.pScreen));
44
45	if (!info->use_glamor)
46		return;
47	glamor_egl_exchange_buffers(src, dst);
48}
49
50Bool
51radeon_glamor_create_screen_resources(ScreenPtr screen)
52{
53	PixmapPtr screen_pixmap = screen->GetScreenPixmap(screen);
54	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
55	RADEONInfoPtr info = RADEONPTR(scrn);
56
57	if (!info->use_glamor)
58		return TRUE;
59
60#ifdef HAVE_GLAMOR_GLYPHS_INIT
61	if (!glamor_glyphs_init(screen))
62		return FALSE;
63#endif
64
65	return radeon_glamor_create_textured_pixmap(screen_pixmap,
66						    info->front_buffer);
67}
68
69
70Bool
71radeon_glamor_pre_init(ScrnInfoPtr scrn)
72{
73	RADEONEntPtr pRADEONEnt = RADEONEntPriv(scrn);
74	RADEONInfoPtr info = RADEONPTR(scrn);
75	pointer glamor_module;
76	CARD32 version;
77	const char *s;
78
79	if (!info->dri2.available)
80		return FALSE;
81
82	s = xf86GetOptValString(info->Options, OPTION_ACCELMETHOD);
83	if (!s) {
84#ifdef __NetBSD__
85		/*
86		 * glamor isn't working yet for GL.  disable where not
87		 * needed for anything at all.
88		 */
89		if (info->ChipFamily < CHIP_FAMILY_TAHITI)
90			return FALSE;
91#else
92		if (xorgGetVersion() >= XORG_VERSION_NUMERIC(1,18,3,0,0)) {
93			if (info->ChipFamily < CHIP_FAMILY_R600)
94				return FALSE;
95		} else {
96			if (info->ChipFamily < CHIP_FAMILY_TAHITI)
97				return FALSE;
98		}
99#endif
100	}
101
102	if (s && strcasecmp(s, "glamor") != 0) {
103		if (info->ChipFamily >= CHIP_FAMILY_TAHITI)
104			xf86DrvMsg(scrn->scrnIndex, X_WARNING,
105				   "EXA not supported, using glamor\n");
106		else
107			return FALSE;
108	}
109
110	if (info->ChipFamily < CHIP_FAMILY_R300) {
111		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
112			   "glamor requires R300 or higher GPU, disabling.\n");
113		return FALSE;
114	}
115
116	if (info->ChipFamily < CHIP_FAMILY_RV515) {
117		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
118			   "glamor may not work (well) with GPUs < RV515.\n");
119	}
120
121#if XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(1,20,99,0,0)
122	if (scrn->depth < 24) {
123#else
124	if (scrn->depth < 15) {
125#endif
126		xf86DrvMsg(scrn->scrnIndex, s ? X_ERROR : X_WARNING,
127			   "Depth %d not supported with glamor, disabling\n",
128			   scrn->depth);
129		return FALSE;
130	}
131
132	if (scrn->depth == 30 &&
133	    xorgGetVersion() < XORG_VERSION_NUMERIC(1,19,99,1,0)) {
134		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
135			   "Depth 30 is not supported by GLAMOR with Xorg < "
136			   "1.19.99.1\n");
137		return FALSE;
138	}
139
140#if XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(1,15,0,0,0)
141	if (!xf86LoaderCheckSymbol("glamor_egl_init")) {
142		xf86DrvMsg(scrn->scrnIndex, s ? X_ERROR : X_WARNING,
143			   "glamor requires Load \"glamoregl\" in "
144			   "Section \"Module\", disabling.\n");
145		return FALSE;
146	}
147#endif
148
149	info->gbm = gbm_create_device(pRADEONEnt->fd);
150	if (!info->gbm) {
151		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
152			   "gbm_create_device returned NULL\n");
153		return FALSE;
154	}
155
156	/* Load glamor module */
157	if ((glamor_module = xf86LoadSubModule(scrn, GLAMOR_EGL_MODULE_NAME))) {
158		version = xf86GetModuleVersion(glamor_module);
159		if (version < MODULE_VERSION_NUMERIC(0,3,1)) {
160			xf86DrvMsg(scrn->scrnIndex, X_ERROR,
161			"Incompatible glamor version, required >= 0.3.0.\n");
162			return FALSE;
163		} else {
164			if (glamor_egl_init(scrn, pRADEONEnt->fd)) {
165				xf86DrvMsg(scrn->scrnIndex, X_INFO,
166					   "glamor detected, initialising EGL layer.\n");
167			} else {
168				xf86DrvMsg(scrn->scrnIndex, X_ERROR,
169					   "glamor detected, failed to initialize EGL.\n");
170				return FALSE;
171			}
172		}
173	} else {
174		xf86DrvMsg(scrn->scrnIndex, X_ERROR, "glamor not available\n");
175		return FALSE;
176	}
177
178	info->use_glamor = TRUE;
179
180	return TRUE;
181}
182
183Bool
184radeon_glamor_create_textured_pixmap(PixmapPtr pixmap, struct radeon_buffer *bo)
185{
186	ScrnInfoPtr scrn = xf86ScreenToScrn(pixmap->drawable.pScreen);
187	RADEONInfoPtr info = RADEONPTR(scrn);
188
189	if (!info->use_glamor)
190		return TRUE;
191
192	if (bo->flags & RADEON_BO_FLAGS_GBM) {
193		return glamor_egl_create_textured_pixmap_from_gbm_bo(pixmap,
194								     bo->bo.gbm
195#if XORG_VERSION_CURRENT > XORG_VERSION_NUMERIC(1,19,99,903,0)
196								     , FALSE
197#endif
198								     );
199	} else {
200		return glamor_egl_create_textured_pixmap(pixmap,
201							 bo->bo.radeon->handle,
202							 pixmap->devKind);
203	}
204}
205
206static Bool radeon_glamor_destroy_pixmap(PixmapPtr pixmap)
207{
208#ifndef HAVE_GLAMOR_EGL_DESTROY_TEXTURED_PIXMAP
209	ScreenPtr screen = pixmap->drawable.pScreen;
210	RADEONInfoPtr info = RADEONPTR(xf86ScreenToScrn(screen));
211	Bool ret;
212#endif
213
214	if (pixmap->refcnt == 1) {
215#ifdef HAVE_GLAMOR_EGL_DESTROY_TEXTURED_PIXMAP
216		glamor_egl_destroy_textured_pixmap(pixmap);
217#endif
218		radeon_set_pixmap_bo(pixmap, NULL);
219	}
220
221#ifdef HAVE_GLAMOR_EGL_DESTROY_TEXTURED_PIXMAP
222	fbDestroyPixmap(pixmap);
223	return TRUE;
224#else
225	screen->DestroyPixmap = info->glamor.SavedDestroyPixmap;
226	ret = screen->DestroyPixmap(pixmap);
227	info->glamor.SavedDestroyPixmap = screen->DestroyPixmap;
228	screen->DestroyPixmap = radeon_glamor_destroy_pixmap;
229
230	return ret;
231#endif
232}
233
234static PixmapPtr
235radeon_glamor_create_pixmap(ScreenPtr screen, int w, int h, int depth,
236			unsigned usage)
237{
238	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
239	RADEONInfoPtr info = RADEONPTR(scrn);
240	struct radeon_pixmap *priv;
241	PixmapPtr pixmap, new_pixmap = NULL;
242
243	if (!xf86GetPixFormat(scrn, depth))
244		return NULL;
245
246	if (!RADEON_CREATE_PIXMAP_SHARED(usage)) {
247		if (info->shadow_primary) {
248			if (usage != CREATE_PIXMAP_USAGE_BACKING_PIXMAP)
249				return fbCreatePixmap(screen, w, h, depth, usage);
250		} else {
251			pixmap = glamor_create_pixmap(screen, w, h, depth, usage);
252			if (pixmap)
253			    return pixmap;
254		}
255	}
256
257	if (w > 32767 || h > 32767)
258		return NullPixmap;
259
260	if (depth == 1)
261		return fbCreatePixmap(screen, w, h, depth, usage);
262
263	if (usage == CREATE_PIXMAP_USAGE_GLYPH_PICTURE && w <= 32 && h <= 32)
264		return fbCreatePixmap(screen, w, h, depth, usage);
265
266	pixmap = fbCreatePixmap(screen, 0, 0, depth, usage);
267	if (pixmap == NullPixmap)
268		return pixmap;
269
270	if (w && h) {
271		int stride;
272
273		priv = calloc(1, sizeof (struct radeon_pixmap));
274		if (!priv)
275			goto fallback_pixmap;
276
277		priv->bo = radeon_alloc_pixmap_bo(scrn, w, h, depth, usage,
278						  pixmap->drawable.bitsPerPixel,
279						  &stride, NULL,
280						  &priv->tiling_flags);
281		if (!priv->bo)
282			goto fallback_priv;
283
284		radeon_set_pixmap_private(pixmap, priv);
285
286		screen->ModifyPixmapHeader(pixmap, w, h, 0, 0, stride, NULL);
287
288		if (!radeon_glamor_create_textured_pixmap(pixmap, priv->bo))
289			goto fallback_glamor;
290
291		pixmap->devPrivate.ptr = NULL;
292	}
293
294	return pixmap;
295
296fallback_glamor:
297	if (RADEON_CREATE_PIXMAP_SHARED(usage)) {
298	/* XXX need further work to handle the DRI2 failure case.
299	 * Glamor don't know how to handle a BO only pixmap. Put
300	 * a warning indicator here.
301	 */
302		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
303			   "Failed to create textured DRI2/PRIME pixmap.");
304
305		radeon_glamor_destroy_pixmap(pixmap);
306		return NullPixmap;
307	}
308	/* Create textured pixmap failed means glamor failed to
309	 * create a texture from current BO for some reasons. We turn
310	 * to create a new glamor pixmap and clean up current one.
311	 * One thing need to be noted, this new pixmap doesn't
312	 * has a priv and bo attached to it. It's glamor's responsbility
313	 * to take care of it. Glamor will mark this new pixmap as a
314	 * texture only pixmap and will never fallback to DDX layer
315	 * afterwards.
316	 */
317	new_pixmap = glamor_create_pixmap(screen, w, h,	depth, usage);
318	radeon_buffer_unref(&priv->bo);
319fallback_priv:
320	free(priv);
321fallback_pixmap:
322	fbDestroyPixmap(pixmap);
323	if (new_pixmap)
324		return new_pixmap;
325	else
326		return fbCreatePixmap(screen, w, h, depth, usage);
327}
328
329PixmapPtr
330radeon_glamor_set_pixmap_bo(DrawablePtr drawable, PixmapPtr pixmap)
331{
332	PixmapPtr old = get_drawable_pixmap(drawable);
333	ScreenPtr screen = drawable->pScreen;
334	struct radeon_pixmap *priv = radeon_get_pixmap_private(pixmap);
335	GCPtr gc;
336
337	/* With a glamor pixmap, 2D pixmaps are created in texture
338	 * and without a static BO attached to it. To support DRI,
339	 * we need to create a new textured-drm pixmap and
340	 * need to copy the original content to this new textured-drm
341	 * pixmap, and then convert the old pixmap to a coherent
342	 * textured-drm pixmap which has a valid BO attached to it
343	 * and also has a valid texture, thus both glamor and DRI2
344	 * can access it.
345	 *
346	 */
347
348	/* Copy the current contents of the pixmap to the bo. */
349	gc = GetScratchGC(drawable->depth, screen);
350	if (gc) {
351		ValidateGC(&pixmap->drawable, gc);
352		gc->ops->CopyArea(&old->drawable, &pixmap->drawable,
353				  gc,
354				  0, 0,
355				  old->drawable.width,
356				  old->drawable.height, 0, 0);
357		FreeScratchGC(gc);
358	}
359
360	/* And redirect the pixmap to the new bo (for 3D). */
361	glamor_egl_exchange_buffers(old, pixmap);
362	radeon_set_pixmap_private(pixmap, radeon_get_pixmap_private(old));
363	radeon_set_pixmap_private(old, priv);
364
365	screen->ModifyPixmapHeader(old,
366				   old->drawable.width,
367				   old->drawable.height,
368				   0, 0, pixmap->devKind, NULL);
369	old->devPrivate.ptr = NULL;
370
371	screen->DestroyPixmap(pixmap);
372
373	return old;
374}
375
376
377static Bool
378radeon_glamor_share_pixmap_backing(PixmapPtr pixmap, ScreenPtr secondary,
379				   void **handle_p)
380{
381	ScreenPtr screen = pixmap->drawable.pScreen;
382	CARD16 stride;
383	CARD32 size;
384	int fd;
385
386	if ((radeon_get_pixmap_tiling_flags(pixmap) &
387	     RADEON_TILING_MASK) != RADEON_TILING_LINEAR) {
388		PixmapPtr linear;
389
390		/* We don't want to re-allocate the screen pixmap as
391		 * linear, to avoid trouble with page flipping
392		 */
393		if (screen->GetScreenPixmap(screen) == pixmap)
394			return FALSE;
395
396		linear = screen->CreatePixmap(screen, pixmap->drawable.width,
397					      pixmap->drawable.height,
398					      pixmap->drawable.depth,
399					      CREATE_PIXMAP_USAGE_SHARED);
400		if (!linear)
401			return FALSE;
402
403		radeon_glamor_set_pixmap_bo(&pixmap->drawable, linear);
404	}
405
406	fd = glamor_fd_from_pixmap(screen, pixmap, &stride, &size);
407	if (fd < 0)
408		return FALSE;
409
410	*handle_p = (void *)(long)fd;
411	return TRUE;
412}
413
414static Bool
415radeon_glamor_set_shared_pixmap_backing(PixmapPtr pixmap, void *handle)
416{
417	ScreenPtr screen = pixmap->drawable.pScreen;
418	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
419	int ihandle = (int)(long)handle;
420
421	if (!radeon_set_shared_pixmap_backing(pixmap, handle, NULL))
422		return FALSE;
423
424	if (ihandle != -1 &&
425	    !radeon_glamor_create_textured_pixmap(pixmap,
426						  radeon_get_pixmap_bo(pixmap))) {
427		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
428			   "Failed to get PRIME drawable for glamor pixmap.\n");
429		return FALSE;
430	}
431
432	screen->ModifyPixmapHeader(pixmap,
433				   pixmap->drawable.width,
434				   pixmap->drawable.height,
435				   0, 0, 0, NULL);
436
437	return TRUE;
438}
439
440
441Bool
442radeon_glamor_init(ScreenPtr screen)
443{
444	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
445	RADEONInfoPtr info = RADEONPTR(scrn);
446#ifdef RENDER
447#ifdef HAVE_FBGLYPHS
448	UnrealizeGlyphProcPtr SavedUnrealizeGlyph = NULL;
449#endif
450	PictureScreenPtr ps = NULL;
451
452	if (info->shadow_primary) {
453		ps = GetPictureScreenIfSet(screen);
454
455		if (ps) {
456#ifdef HAVE_FBGLYPHS
457			SavedUnrealizeGlyph = ps->UnrealizeGlyph;
458#endif
459			info->glamor.SavedGlyphs = ps->Glyphs;
460			info->glamor.SavedTriangles = ps->Triangles;
461			info->glamor.SavedTrapezoids = ps->Trapezoids;
462		}
463	}
464#endif /* RENDER */
465
466	if (!glamor_init(screen, GLAMOR_USE_EGL_SCREEN | GLAMOR_USE_SCREEN |
467			 GLAMOR_USE_PICTURE_SCREEN | GLAMOR_INVERTED_Y_AXIS |
468			 GLAMOR_NO_DRI3)) {
469		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
470			   "Failed to initialize glamor.\n");
471		return FALSE;
472	}
473
474	if (!glamor_egl_init_textured_pixmap(screen)) {
475		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
476			   "Failed to initialize textured pixmap of screen for glamor.\n");
477		return FALSE;
478	}
479
480	if (!dixRegisterPrivateKey(&glamor_pixmap_index, PRIVATE_PIXMAP, 0))
481		return FALSE;
482
483	if (info->shadow_primary)
484		radeon_glamor_screen_init(screen);
485
486#if defined(RENDER) && defined(HAVE_FBGLYPHS)
487	/* For ShadowPrimary, we need fbUnrealizeGlyph instead of
488	 * glamor_unrealize_glyph
489	 */
490	if (ps)
491		ps->UnrealizeGlyph = SavedUnrealizeGlyph;
492#endif
493
494	info->glamor.SavedCreatePixmap = screen->CreatePixmap;
495	screen->CreatePixmap = radeon_glamor_create_pixmap;
496	info->glamor.SavedDestroyPixmap = screen->DestroyPixmap;
497	screen->DestroyPixmap = radeon_glamor_destroy_pixmap;
498	info->glamor.SavedSharePixmapBacking = screen->SharePixmapBacking;
499	screen->SharePixmapBacking = radeon_glamor_share_pixmap_backing;
500	info->glamor.SavedSetSharedPixmapBacking = screen->SetSharedPixmapBacking;
501	screen->SetSharedPixmapBacking = radeon_glamor_set_shared_pixmap_backing;
502
503	xf86DrvMsg(scrn->scrnIndex, X_INFO,
504		   "Use GLAMOR acceleration.\n");
505	return TRUE;
506}
507
508void
509radeon_glamor_fini(ScreenPtr screen)
510{
511	RADEONInfoPtr info = RADEONPTR(xf86ScreenToScrn(screen));
512
513	if (!info->use_glamor)
514		return;
515
516	screen->CreatePixmap = info->glamor.SavedCreatePixmap;
517	screen->DestroyPixmap = info->glamor.SavedDestroyPixmap;
518	screen->SharePixmapBacking = info->glamor.SavedSharePixmapBacking;
519	screen->SetSharedPixmapBacking = info->glamor.SavedSetSharedPixmapBacking;
520}
521
522XF86VideoAdaptorPtr radeon_glamor_xv_init(ScreenPtr pScreen, int num_adapt)
523{
524	return glamor_xv_init(pScreen, num_adapt);
525}
526