radeon_glamor.c revision 7314432e
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	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
54	RADEONInfoPtr info = RADEONPTR(scrn);
55
56	if (!info->use_glamor)
57		return TRUE;
58
59#ifdef HAVE_GLAMOR_GLYPHS_INIT
60	if (!glamor_glyphs_init(screen))
61		return FALSE;
62#endif
63
64	if (!glamor_egl_create_textured_screen_ext(screen,
65						   info->front_bo->handle,
66						   scrn->displayWidth *
67						   info->pixel_bytes,
68						   NULL))
69		return FALSE;
70
71	return TRUE;
72}
73
74
75Bool
76radeon_glamor_pre_init(ScrnInfoPtr scrn)
77{
78	RADEONInfoPtr info = RADEONPTR(scrn);
79	pointer glamor_module;
80	CARD32 version;
81	const char *s;
82
83	if (!info->dri2.available)
84		return FALSE;
85
86	s = xf86GetOptValString(info->Options, OPTION_ACCELMETHOD);
87	if (s == NULL && info->ChipFamily < CHIP_FAMILY_TAHITI)
88		return FALSE;
89
90	if (s && strcasecmp(s, "glamor") != 0) {
91		if (info->ChipFamily >= CHIP_FAMILY_TAHITI)
92			xf86DrvMsg(scrn->scrnIndex, X_WARNING,
93				   "EXA not supported, using glamor\n");
94		else
95			return FALSE;
96	}
97
98	if (info->ChipFamily < CHIP_FAMILY_R300) {
99		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
100			   "glamor requires R300 or higher GPU, disabling.\n");
101		return FALSE;
102	}
103
104	if (info->ChipFamily < CHIP_FAMILY_RV515) {
105		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
106			   "glamor may not work (well) with GPUs < RV515.\n");
107	}
108
109	if (scrn->depth < 24) {
110		xf86DrvMsg(scrn->scrnIndex, s ? X_ERROR : X_WARNING,
111			   "glamor requires depth >= 24, disabling.\n");
112		return FALSE;
113	}
114
115#if XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(1,15,0,0,0)
116	if (!xf86LoaderCheckSymbol("glamor_egl_init")) {
117		xf86DrvMsg(scrn->scrnIndex, s ? X_ERROR : X_WARNING,
118			   "glamor requires Load \"glamoregl\" in "
119			   "Section \"Module\", disabling.\n");
120		return FALSE;
121	}
122#endif
123
124	/* Load glamor module */
125	if ((glamor_module = xf86LoadSubModule(scrn, GLAMOR_EGL_MODULE_NAME))) {
126		version = xf86GetModuleVersion(glamor_module);
127		if (version < MODULE_VERSION_NUMERIC(0,3,1)) {
128			xf86DrvMsg(scrn->scrnIndex, X_ERROR,
129			"Incompatible glamor version, required >= 0.3.0.\n");
130			return FALSE;
131		} else {
132			if (glamor_egl_init(scrn, info->dri2.drm_fd)) {
133				xf86DrvMsg(scrn->scrnIndex, X_INFO,
134					   "glamor detected, initialising EGL layer.\n");
135			} else {
136				xf86DrvMsg(scrn->scrnIndex, X_ERROR,
137					   "glamor detected, failed to initialize EGL.\n");
138				return FALSE;
139			}
140		}
141	} else {
142		xf86DrvMsg(scrn->scrnIndex, X_ERROR, "glamor not available\n");
143		return FALSE;
144	}
145
146	info->use_glamor = TRUE;
147
148	return TRUE;
149}
150
151Bool
152radeon_glamor_create_textured_pixmap(PixmapPtr pixmap, struct radeon_pixmap *priv)
153{
154	return glamor_egl_create_textured_pixmap(pixmap, priv->bo->handle,
155						 pixmap->devKind);
156}
157
158static Bool radeon_glamor_destroy_pixmap(PixmapPtr pixmap)
159{
160#ifndef HAVE_GLAMOR_EGL_DESTROY_TEXTURED_PIXMAP
161	ScreenPtr screen = pixmap->drawable.pScreen;
162	RADEONInfoPtr info = RADEONPTR(xf86ScreenToScrn(screen));
163	Bool ret;
164#endif
165
166	if (pixmap->refcnt == 1) {
167		if (pixmap->devPrivate.ptr) {
168			struct radeon_bo *bo = radeon_get_pixmap_bo(pixmap);
169
170			if (bo)
171				radeon_bo_unmap(bo);
172		}
173
174#ifdef HAVE_GLAMOR_EGL_DESTROY_TEXTURED_PIXMAP
175		glamor_egl_destroy_textured_pixmap(pixmap);
176#endif
177		radeon_set_pixmap_bo(pixmap, NULL);
178	}
179
180#ifdef HAVE_GLAMOR_EGL_DESTROY_TEXTURED_PIXMAP
181	fbDestroyPixmap(pixmap);
182	return TRUE;
183#else
184	screen->DestroyPixmap = info->glamor.SavedDestroyPixmap;
185	ret = screen->DestroyPixmap(pixmap);
186	info->glamor.SavedDestroyPixmap = screen->DestroyPixmap;
187	screen->DestroyPixmap = radeon_glamor_destroy_pixmap;
188
189	return ret;
190#endif
191}
192
193static PixmapPtr
194radeon_glamor_create_pixmap(ScreenPtr screen, int w, int h, int depth,
195			unsigned usage)
196{
197	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
198	RADEONInfoPtr info = RADEONPTR(scrn);
199	struct radeon_pixmap *priv;
200	PixmapPtr pixmap, new_pixmap = NULL;
201
202	if (!RADEON_CREATE_PIXMAP_SHARED(usage)) {
203		if (info->shadow_primary) {
204			if (usage != CREATE_PIXMAP_USAGE_BACKING_PIXMAP)
205				return fbCreatePixmap(screen, w, h, depth, usage);
206		} else {
207			pixmap = glamor_create_pixmap(screen, w, h, depth, usage);
208			if (pixmap)
209			    return pixmap;
210		}
211	}
212
213	if (w > 32767 || h > 32767)
214		return NullPixmap;
215
216	if (depth == 1)
217		return fbCreatePixmap(screen, w, h, depth, usage);
218
219	if (usage == CREATE_PIXMAP_USAGE_GLYPH_PICTURE && w <= 32 && h <= 32)
220		return fbCreatePixmap(screen, w, h, depth, usage);
221
222	pixmap = fbCreatePixmap(screen, 0, 0, depth, usage);
223	if (pixmap == NullPixmap)
224		return pixmap;
225
226	if (w && h) {
227		int stride;
228
229		priv = calloc(1, sizeof (struct radeon_pixmap));
230		if (priv == NULL)
231			goto fallback_pixmap;
232
233		priv->bo = radeon_alloc_pixmap_bo(scrn, w, h, depth, usage,
234						  pixmap->drawable.bitsPerPixel,
235						  &stride,
236						  &priv->surface,
237						  &priv->tiling_flags);
238		if (!priv->bo)
239			goto fallback_priv;
240
241		radeon_set_pixmap_private(pixmap, priv);
242
243		screen->ModifyPixmapHeader(pixmap, w, h, 0, 0, stride, NULL);
244
245		if (!radeon_glamor_create_textured_pixmap(pixmap, priv))
246			goto fallback_glamor;
247
248		pixmap->devPrivate.ptr = NULL;
249	}
250
251	return pixmap;
252
253fallback_glamor:
254	if (RADEON_CREATE_PIXMAP_SHARED(usage)) {
255	/* XXX need further work to handle the DRI2 failure case.
256	 * Glamor don't know how to handle a BO only pixmap. Put
257	 * a warning indicator here.
258	 */
259		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
260			   "Failed to create textured DRI2/PRIME pixmap.");
261
262		radeon_glamor_destroy_pixmap(pixmap);
263		return NullPixmap;
264	}
265	/* Create textured pixmap failed means glamor failed to
266	 * create a texture from current BO for some reasons. We turn
267	 * to create a new glamor pixmap and clean up current one.
268	 * One thing need to be noted, this new pixmap doesn't
269	 * has a priv and bo attached to it. It's glamor's responsbility
270	 * to take care of it. Glamor will mark this new pixmap as a
271	 * texture only pixmap and will never fallback to DDX layer
272	 * afterwards.
273	 */
274	new_pixmap = glamor_create_pixmap(screen, w, h,	depth, usage);
275	radeon_bo_unref(priv->bo);
276fallback_priv:
277	free(priv);
278fallback_pixmap:
279	fbDestroyPixmap(pixmap);
280	if (new_pixmap)
281		return new_pixmap;
282	else
283		return fbCreatePixmap(screen, w, h, depth, usage);
284}
285
286PixmapPtr
287radeon_glamor_set_pixmap_bo(DrawablePtr drawable, PixmapPtr pixmap)
288{
289	PixmapPtr old = get_drawable_pixmap(drawable);
290	ScreenPtr screen = drawable->pScreen;
291	struct radeon_pixmap *priv = radeon_get_pixmap_private(pixmap);
292	GCPtr gc;
293
294	/* With a glamor pixmap, 2D pixmaps are created in texture
295	 * and without a static BO attached to it. To support DRI,
296	 * we need to create a new textured-drm pixmap and
297	 * need to copy the original content to this new textured-drm
298	 * pixmap, and then convert the old pixmap to a coherent
299	 * textured-drm pixmap which has a valid BO attached to it
300	 * and also has a valid texture, thus both glamor and DRI2
301	 * can access it.
302	 *
303	 */
304
305	/* Copy the current contents of the pixmap to the bo. */
306	gc = GetScratchGC(drawable->depth, screen);
307	if (gc) {
308		ValidateGC(&pixmap->drawable, gc);
309		gc->ops->CopyArea(&old->drawable, &pixmap->drawable,
310				  gc,
311				  0, 0,
312				  old->drawable.width,
313				  old->drawable.height, 0, 0);
314		FreeScratchGC(gc);
315	}
316
317	/* And redirect the pixmap to the new bo (for 3D). */
318	glamor_egl_exchange_buffers(old, pixmap);
319	radeon_set_pixmap_private(pixmap, radeon_get_pixmap_private(old));
320	radeon_set_pixmap_private(old, priv);
321
322	screen->ModifyPixmapHeader(old,
323				   old->drawable.width,
324				   old->drawable.height,
325				   0, 0, pixmap->devKind, NULL);
326	old->devPrivate.ptr = NULL;
327
328	screen->DestroyPixmap(pixmap);
329
330	return old;
331}
332
333#ifdef RADEON_PIXMAP_SHARING
334
335static Bool
336radeon_glamor_share_pixmap_backing(PixmapPtr pixmap, ScreenPtr slave,
337				   void **handle_p)
338{
339	ScreenPtr screen = pixmap->drawable.pScreen;
340	CARD16 stride;
341	CARD32 size;
342	int fd;
343
344	if ((radeon_get_pixmap_tiling_flags(pixmap) &
345	     RADEON_TILING_MASK) != RADEON_TILING_LINEAR) {
346		PixmapPtr linear;
347
348		/* We don't want to re-allocate the screen pixmap as
349		 * linear, to avoid trouble with page flipping
350		 */
351		if (screen->GetScreenPixmap(screen) == pixmap)
352			return FALSE;
353
354		linear = screen->CreatePixmap(screen, pixmap->drawable.width,
355					      pixmap->drawable.height,
356					      pixmap->drawable.depth,
357					      CREATE_PIXMAP_USAGE_SHARED);
358		if (!linear)
359			return FALSE;
360
361		radeon_glamor_set_pixmap_bo(&pixmap->drawable, linear);
362	}
363
364	fd = glamor_fd_from_pixmap(screen, pixmap, &stride, &size);
365	if (fd < 0)
366		return FALSE;
367
368	*handle_p = (void *)(long)fd;
369	return TRUE;
370}
371
372static Bool
373radeon_glamor_set_shared_pixmap_backing(PixmapPtr pixmap, void *handle)
374{
375	ScreenPtr screen = pixmap->drawable.pScreen;
376	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
377	struct radeon_surface surface;
378	struct radeon_pixmap *priv;
379
380	if (!radeon_set_shared_pixmap_backing(pixmap, handle, &surface))
381		return FALSE;
382
383	priv = radeon_get_pixmap_private(pixmap);
384	priv->surface = surface;
385
386	if (!radeon_glamor_create_textured_pixmap(pixmap, priv)) {
387		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
388			   "Failed to get PRIME drawable for glamor pixmap.\n");
389		return FALSE;
390	}
391
392	screen->ModifyPixmapHeader(pixmap,
393				   pixmap->drawable.width,
394				   pixmap->drawable.height,
395				   0, 0, 0, NULL);
396
397	return TRUE;
398}
399
400#endif /* RADEON_PIXMAP_SHARING */
401
402Bool
403radeon_glamor_init(ScreenPtr screen)
404{
405	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
406	RADEONInfoPtr info = RADEONPTR(scrn);
407#ifdef RENDER
408#ifdef HAVE_FBGLYPHS
409	UnrealizeGlyphProcPtr SavedUnrealizeGlyph = NULL;
410#endif
411	PictureScreenPtr ps = NULL;
412
413	if (info->shadow_primary) {
414		ps = GetPictureScreenIfSet(screen);
415
416		if (ps) {
417#ifdef HAVE_FBGLYPHS
418			SavedUnrealizeGlyph = ps->UnrealizeGlyph;
419#endif
420			info->glamor.SavedGlyphs = ps->Glyphs;
421			info->glamor.SavedTriangles = ps->Triangles;
422			info->glamor.SavedTrapezoids = ps->Trapezoids;
423		}
424	}
425#endif /* RENDER */
426
427	if (!glamor_init(screen, GLAMOR_USE_EGL_SCREEN | GLAMOR_USE_SCREEN |
428			 GLAMOR_USE_PICTURE_SCREEN | GLAMOR_INVERTED_Y_AXIS |
429			 GLAMOR_NO_DRI3)) {
430		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
431			   "Failed to initialize glamor.\n");
432		return FALSE;
433	}
434
435	if (!glamor_egl_init_textured_pixmap(screen)) {
436		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
437			   "Failed to initialize textured pixmap of screen for glamor.\n");
438		return FALSE;
439	}
440
441	if (!dixRegisterPrivateKey(&glamor_pixmap_index, PRIVATE_PIXMAP, 0))
442		return FALSE;
443
444	if (info->shadow_primary)
445		radeon_glamor_screen_init(screen);
446
447#if defined(RENDER) && defined(HAVE_FBGLYPHS)
448	/* For ShadowPrimary, we need fbUnrealizeGlyph instead of
449	 * glamor_unrealize_glyph
450	 */
451	if (ps)
452		ps->UnrealizeGlyph = SavedUnrealizeGlyph;
453#endif
454
455	info->glamor.SavedCreatePixmap = screen->CreatePixmap;
456	screen->CreatePixmap = radeon_glamor_create_pixmap;
457	info->glamor.SavedDestroyPixmap = screen->DestroyPixmap;
458	screen->DestroyPixmap = radeon_glamor_destroy_pixmap;
459#ifdef RADEON_PIXMAP_SHARING
460	info->glamor.SavedSharePixmapBacking = screen->SharePixmapBacking;
461	screen->SharePixmapBacking = radeon_glamor_share_pixmap_backing;
462	info->glamor.SavedSetSharedPixmapBacking = screen->SetSharedPixmapBacking;
463	screen->SetSharedPixmapBacking = radeon_glamor_set_shared_pixmap_backing;
464#endif
465
466	xf86DrvMsg(scrn->scrnIndex, X_INFO,
467		   "Use GLAMOR acceleration.\n");
468	return TRUE;
469}
470
471void
472radeon_glamor_fini(ScreenPtr screen)
473{
474	RADEONInfoPtr info = RADEONPTR(xf86ScreenToScrn(screen));
475
476	if (!info->use_glamor)
477		return;
478
479	screen->CreatePixmap = info->glamor.SavedCreatePixmap;
480	screen->DestroyPixmap = info->glamor.SavedDestroyPixmap;
481#ifdef RADEON_PIXMAP_SHARING
482	screen->SharePixmapBacking = info->glamor.SavedSharePixmapBacking;
483	screen->SetSharedPixmapBacking = info->glamor.SavedSetSharedPixmapBacking;
484#endif
485}
486
487XF86VideoAdaptorPtr radeon_glamor_xv_init(ScreenPtr pScreen, int num_adapt)
488{
489	return glamor_xv_init(pScreen, num_adapt);
490}
491