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 #ifdef USE_GLAMOR
32 
33 #include <xf86.h>
34 
35 #include "amdgpu_bo_helper.h"
36 #include "amdgpu_pixmap.h"
37 #include "amdgpu_glamor.h"
38 
39 #include <gbm.h>
40 
41 #ifndef HAVE_GLAMOR_FINISH
42 #include <GL/gl.h>
43 #endif
44 
45 DevPrivateKeyRec amdgpu_pixmap_index;
46 
amdgpu_glamor_exchange_buffers(PixmapPtr src,PixmapPtr dst)47 void amdgpu_glamor_exchange_buffers(PixmapPtr src, PixmapPtr dst)
48 {
49 	AMDGPUInfoPtr info = AMDGPUPTR(xf86ScreenToScrn(dst->drawable.pScreen));
50 
51 	if (!info->use_glamor)
52 		return;
53 	glamor_egl_exchange_buffers(src, dst);
54 }
55 
amdgpu_glamor_create_screen_resources(ScreenPtr screen)56 Bool amdgpu_glamor_create_screen_resources(ScreenPtr screen)
57 {
58 	PixmapPtr screen_pixmap = screen->GetScreenPixmap(screen);
59 	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
60 	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
61 
62 	if (!info->use_glamor)
63 		return TRUE;
64 
65 #ifdef HAVE_GLAMOR_GLYPHS_INIT
66 	if (!glamor_glyphs_init(screen))
67 		return FALSE;
68 #endif
69 
70 	return amdgpu_glamor_create_textured_pixmap(screen_pixmap,
71 						    info->front_buffer);
72 }
73 
amdgpu_glamor_pre_init(ScrnInfoPtr scrn)74 Bool amdgpu_glamor_pre_init(ScrnInfoPtr scrn)
75 {
76 	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
77 	pointer glamor_module;
78 	CARD32 version;
79 
80 #if XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(1,20,99,0,0)
81 	if (scrn->depth < 24) {
82 #else
83 	if (scrn->depth < 15) {
84 #endif
85 		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
86 			   "Depth %d not supported with glamor, disabling\n",
87 			   scrn->depth);
88 		return FALSE;
89 	}
90 
91 #if XORG_VERSION_CURRENT < XORG_VERSION_NUMERIC(1,15,0,0,0)
92 	if (!xf86LoaderCheckSymbol("glamor_egl_init")) {
93 		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
94 			   "glamor requires Load \"glamoregl\" in "
95 			   "Section \"Module\", disabling.\n");
96 		return FALSE;
97 	}
98 #endif
99 
100 	/* Load glamor module */
101 	if ((glamor_module = xf86LoadSubModule(scrn, GLAMOR_EGL_MODULE_NAME))) {
102 		version = xf86GetModuleVersion(glamor_module);
103 		if (version < MODULE_VERSION_NUMERIC(0, 3, 1)) {
104 			xf86DrvMsg(scrn->scrnIndex, X_ERROR,
105 				   "Incompatible glamor version, required >= 0.3.0.\n");
106 			return FALSE;
107 		} else {
108 			AMDGPUEntPtr pAMDGPUEnt = AMDGPUEntPriv(scrn);
109 
110 			if (scrn->depth == 30 &&
111 			    version < MODULE_VERSION_NUMERIC(1, 0, 1)) {
112 				xf86DrvMsg(scrn->scrnIndex, X_WARNING,
113 					   "Depth 30 requires glamor >= 1.0.1 (xserver 1.20),"
114 					   " can't enable glamor\n");
115 				return FALSE;
116 			}
117 
118 			if (glamor_egl_init(scrn, pAMDGPUEnt->fd)) {
119 				xf86DrvMsg(scrn->scrnIndex, X_INFO,
120 					   "glamor detected, initialising EGL layer.\n");
121 			} else {
122 				xf86DrvMsg(scrn->scrnIndex, X_ERROR,
123 					   "glamor detected, failed to initialize EGL.\n");
124 				return FALSE;
125 			}
126 		}
127 	} else {
128 		xf86DrvMsg(scrn->scrnIndex, X_ERROR, "glamor not available\n");
129 		return FALSE;
130 	}
131 
132 	info->use_glamor = TRUE;
133 
134 	return TRUE;
135 }
136 
137 Bool
138 amdgpu_glamor_create_textured_pixmap(PixmapPtr pixmap, struct amdgpu_buffer *bo)
139 {
140 	ScrnInfoPtr scrn = xf86ScreenToScrn(pixmap->drawable.pScreen);
141 	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
142 
143 	if ((info->use_glamor) == 0)
144 		return TRUE;
145 
146 	if (bo->flags & AMDGPU_BO_FLAGS_GBM) {
147 		return glamor_egl_create_textured_pixmap_from_gbm_bo(pixmap,
148 								     bo->bo.gbm
149 #if XORG_VERSION_CURRENT > XORG_VERSION_NUMERIC(1,19,99,903,0)
150 								     , FALSE
151 #endif
152 								     );
153 	} else {
154 		uint32_t bo_handle;
155 
156 		if (!amdgpu_bo_get_handle(bo, &bo_handle))
157 			return FALSE;
158 
159 		return glamor_egl_create_textured_pixmap(pixmap, bo_handle,
160 							 pixmap->devKind);
161 	}
162 }
163 
164 static Bool amdgpu_glamor_destroy_pixmap(PixmapPtr pixmap)
165 {
166 #ifndef HAVE_GLAMOR_EGL_DESTROY_TEXTURED_PIXMAP
167 	ScreenPtr screen = pixmap->drawable.pScreen;
168 	AMDGPUInfoPtr info = AMDGPUPTR(xf86ScreenToScrn(screen));
169 	Bool ret;
170 #endif
171 
172 	if (pixmap->refcnt == 1) {
173 		if (pixmap->devPrivate.ptr) {
174 			struct amdgpu_buffer *bo = amdgpu_get_pixmap_bo(pixmap);
175 
176 			if (bo)
177 				amdgpu_bo_unmap(bo);
178 		}
179 
180 #ifdef HAVE_GLAMOR_EGL_DESTROY_TEXTURED_PIXMAP
181 		glamor_egl_destroy_textured_pixmap(pixmap);
182 #endif
183 		amdgpu_set_pixmap_bo(pixmap, NULL);
184 	}
185 
186 #ifdef HAVE_GLAMOR_EGL_DESTROY_TEXTURED_PIXMAP
187 	fbDestroyPixmap(pixmap);
188 	return TRUE;
189 #else
190 	screen->DestroyPixmap = info->glamor.SavedDestroyPixmap;
191 	ret = screen->DestroyPixmap(pixmap);
192 	info->glamor.SavedDestroyPixmap = screen->DestroyPixmap;
193 	screen->DestroyPixmap = amdgpu_glamor_destroy_pixmap;
194 
195 	return ret;
196 #endif
197 }
198 
199 static PixmapPtr
200 amdgpu_glamor_create_pixmap(ScreenPtr screen, int w, int h, int depth,
201 			    unsigned usage)
202 {
203 	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
204 	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
205 	struct amdgpu_pixmap *priv;
206 	PixmapPtr pixmap, new_pixmap = NULL;
207 
208 	if (!xf86GetPixFormat(scrn, depth))
209 		return NULL;
210 
211 	if (!AMDGPU_CREATE_PIXMAP_SHARED(usage)) {
212 		if (info->shadow_primary) {
213 			if (usage != CREATE_PIXMAP_USAGE_BACKING_PIXMAP)
214 				return fbCreatePixmap(screen, w, h, depth, usage);
215 
216 			usage |= AMDGPU_CREATE_PIXMAP_LINEAR |
217 				 AMDGPU_CREATE_PIXMAP_GTT;
218 		} else if (usage != CREATE_PIXMAP_USAGE_BACKING_PIXMAP) {
219 			pixmap = glamor_create_pixmap(screen, w, h, depth, usage);
220 			if (pixmap)
221 				return pixmap;
222 		}
223 	}
224 
225 	if (w > 32767 || h > 32767)
226 		return NullPixmap;
227 
228 	if (depth == 1)
229 		return fbCreatePixmap(screen, w, h, depth, usage);
230 
231 	if (usage == CREATE_PIXMAP_USAGE_GLYPH_PICTURE && w <= 32 && h <= 32)
232 		return fbCreatePixmap(screen, w, h, depth, usage);
233 
234 	pixmap = fbCreatePixmap(screen, 0, 0, depth, usage);
235 	if (pixmap == NullPixmap)
236 		return pixmap;
237 
238 	if (w && h) {
239 		int stride;
240 
241 		priv = calloc(1, sizeof(struct amdgpu_pixmap));
242 		if (!priv)
243 			goto fallback_pixmap;
244 
245 		priv->bo = amdgpu_alloc_pixmap_bo(scrn, w, h, depth, usage,
246 						  pixmap->drawable.bitsPerPixel,
247 						  &stride);
248 		if (!priv->bo)
249 			goto fallback_priv;
250 
251 		amdgpu_set_pixmap_private(pixmap, priv);
252 
253 		screen->ModifyPixmapHeader(pixmap, w, h, 0, 0, stride, NULL);
254 
255 		pixmap->devPrivate.ptr = NULL;
256 
257 		if (!amdgpu_glamor_create_textured_pixmap(pixmap, priv->bo))
258 			goto fallback_glamor;
259 	}
260 
261 	return pixmap;
262 
263 fallback_glamor:
264 	if (AMDGPU_CREATE_PIXMAP_SHARED(usage)) {
265 		/* XXX need further work to handle the DRI2 failure case.
266 		 * Glamor don't know how to handle a BO only pixmap. Put
267 		 * a warning indicator here.
268 		 */
269 		xf86DrvMsg(scrn->scrnIndex, X_WARNING,
270 			   "Failed to create textured DRI2/PRIME pixmap.");
271 
272 		amdgpu_glamor_destroy_pixmap(pixmap);
273 		return NullPixmap;
274 	}
275 	/* Create textured pixmap failed means glamor failed to
276 	 * create a texture from current BO for some reasons. We turn
277 	 * to create a new glamor pixmap and clean up current one.
278 	 * One thing need to be noted, this new pixmap doesn't
279 	 * has a priv and bo attached to it. It's glamor's responsbility
280 	 * to take care of it. Glamor will mark this new pixmap as a
281 	 * texture only pixmap and will never fallback to DDX layer
282 	 * afterwards.
283 	 */
284 	new_pixmap = glamor_create_pixmap(screen, w, h, depth, usage);
285 	amdgpu_bo_unref(&priv->bo);
286 fallback_priv:
287 	free(priv);
288 fallback_pixmap:
289 	fbDestroyPixmap(pixmap);
290 	if (new_pixmap)
291 		return new_pixmap;
292 	else
293 		return fbCreatePixmap(screen, w, h, depth, usage);
294 }
295 
296 PixmapPtr
297 amdgpu_glamor_set_pixmap_bo(DrawablePtr drawable, PixmapPtr pixmap)
298 {
299 	PixmapPtr old = get_drawable_pixmap(drawable);
300 	ScreenPtr screen = drawable->pScreen;
301 	struct amdgpu_pixmap *priv = amdgpu_get_pixmap_private(pixmap);
302 	GCPtr gc;
303 
304 	/* With a glamor pixmap, 2D pixmaps are created in texture
305 	 * and without a static BO attached to it. To support DRI,
306 	 * we need to create a new textured-drm pixmap and
307 	 * need to copy the original content to this new textured-drm
308 	 * pixmap, and then convert the old pixmap to a coherent
309 	 * textured-drm pixmap which has a valid BO attached to it
310 	 * and also has a valid texture, thus both glamor and DRI2
311 	 * can access it.
312 	 *
313 	 */
314 
315 	/* Copy the current contents of the pixmap to the bo. */
316 	gc = GetScratchGC(drawable->depth, screen);
317 	if (gc) {
318 		ValidateGC(&pixmap->drawable, gc);
319 		gc->ops->CopyArea(&old->drawable, &pixmap->drawable,
320 				  gc,
321 				  0, 0,
322 				  old->drawable.width,
323 				  old->drawable.height, 0, 0);
324 		FreeScratchGC(gc);
325 	}
326 
327 	/* And redirect the pixmap to the new bo (for 3D). */
328 	glamor_egl_exchange_buffers(old, pixmap);
329 	amdgpu_set_pixmap_private(pixmap, amdgpu_get_pixmap_private(old));
330 	amdgpu_set_pixmap_private(old, priv);
331 
332 	screen->ModifyPixmapHeader(old,
333 				   old->drawable.width,
334 				   old->drawable.height,
335 				   0, 0, pixmap->devKind, NULL);
336 	old->devPrivate.ptr = NULL;
337 
338 	screen->DestroyPixmap(pixmap);
339 
340 	return old;
341 }
342 
343 
344 static Bool
345 amdgpu_glamor_share_pixmap_backing(PixmapPtr pixmap, ScreenPtr slave,
346 				   void **handle_p)
347 {
348 	ScreenPtr screen = pixmap->drawable.pScreen;
349 	AMDGPUInfoPtr info = AMDGPUPTR(xf86ScreenToScrn(screen));
350 	uint64_t tiling_info;
351 	CARD16 stride;
352 	CARD32 size;
353 	Bool is_linear;
354 	int fd;
355 
356 	tiling_info = amdgpu_pixmap_get_tiling_info(pixmap);
357 
358 	if (info->family >= AMDGPU_FAMILY_AI)
359 		is_linear = AMDGPU_TILING_GET(tiling_info, SWIZZLE_MODE) == 0;
360 	else
361 		is_linear = AMDGPU_TILING_GET(tiling_info, ARRAY_MODE) == 1;
362 
363 	if (!is_linear) {
364 		PixmapPtr linear;
365 
366 		/* We don't want to re-allocate the screen pixmap as
367 		 * linear, to avoid trouble with page flipping
368 		 */
369 		if (screen->GetScreenPixmap(screen) == pixmap)
370 			return FALSE;
371 
372 		linear = screen->CreatePixmap(screen, pixmap->drawable.width,
373 					      pixmap->drawable.height,
374 					      pixmap->drawable.depth,
375 					      CREATE_PIXMAP_USAGE_SHARED);
376 		if (!linear)
377 			return FALSE;
378 
379 		amdgpu_glamor_set_pixmap_bo(&pixmap->drawable, linear);
380 	}
381 
382 	fd = glamor_fd_from_pixmap(screen, pixmap, &stride, &size);
383 	if (fd < 0)
384 		return FALSE;
385 
386 	*handle_p = (void *)(long)fd;
387 	return TRUE;
388 }
389 
390 static Bool
391 amdgpu_glamor_set_shared_pixmap_backing(PixmapPtr pixmap, void *handle)
392 {
393 	ScreenPtr screen = pixmap->drawable.pScreen;
394 	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
395 	int ihandle = (int)(long)handle;
396 	struct amdgpu_pixmap *priv;
397 
398 	if (!amdgpu_set_shared_pixmap_backing(pixmap, handle))
399 		return FALSE;
400 
401 	priv = amdgpu_get_pixmap_private(pixmap);
402 
403 	if (ihandle != -1 &&
404 	    !amdgpu_glamor_create_textured_pixmap(pixmap, priv->bo)) {
405 		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
406 			   "Failed to get PRIME drawable for glamor pixmap.\n");
407 		return FALSE;
408 	}
409 
410 	screen->ModifyPixmapHeader(pixmap,
411 				   pixmap->drawable.width,
412 				   pixmap->drawable.height,
413 				   0, 0, 0, NULL);
414 
415 	return TRUE;
416 }
417 
418 
419 Bool amdgpu_glamor_init(ScreenPtr screen)
420 {
421 	ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
422 	AMDGPUInfoPtr info = AMDGPUPTR(scrn);
423 #ifdef RENDER
424 #ifdef HAVE_FBGLYPHS
425 	UnrealizeGlyphProcPtr SavedUnrealizeGlyph = NULL;
426 #endif
427 	PictureScreenPtr ps = NULL;
428 
429 	if (info->shadow_primary) {
430 		ps = GetPictureScreenIfSet(screen);
431 
432 		if (ps) {
433 #ifdef HAVE_FBGLYPHS
434 			SavedUnrealizeGlyph = ps->UnrealizeGlyph;
435 #endif
436 			info->glamor.SavedGlyphs = ps->Glyphs;
437 			info->glamor.SavedTriangles = ps->Triangles;
438 			info->glamor.SavedTrapezoids = ps->Trapezoids;
439 		}
440 	}
441 #endif /* RENDER */
442 
443 	if (!glamor_init(screen, GLAMOR_USE_EGL_SCREEN | GLAMOR_USE_SCREEN |
444 			 GLAMOR_USE_PICTURE_SCREEN | GLAMOR_INVERTED_Y_AXIS |
445 			 GLAMOR_NO_DRI3)) {
446 		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
447 			   "Failed to initialize glamor.\n");
448 		return FALSE;
449 	}
450 
451 	if (!glamor_egl_init_textured_pixmap(screen)) {
452 		xf86DrvMsg(scrn->scrnIndex, X_ERROR,
453 			   "Failed to initialize textured pixmap of screen for glamor.\n");
454 		return FALSE;
455 	}
456 	if (!dixRegisterPrivateKey(&amdgpu_pixmap_index, PRIVATE_PIXMAP, 0))
457 		return FALSE;
458 
459 	if (info->shadow_primary)
460 		amdgpu_glamor_screen_init(screen);
461 
462 #if defined(RENDER) && defined(HAVE_FBGLYPHS)
463 	/* For ShadowPrimary, we need fbUnrealizeGlyph instead of
464 	 * glamor_unrealize_glyph
465 	 */
466 	if (ps)
467 		ps->UnrealizeGlyph = SavedUnrealizeGlyph;
468 #endif
469 
470 	info->glamor.SavedCreatePixmap = screen->CreatePixmap;
471 	screen->CreatePixmap = amdgpu_glamor_create_pixmap;
472 	info->glamor.SavedDestroyPixmap = screen->DestroyPixmap;
473 	screen->DestroyPixmap = amdgpu_glamor_destroy_pixmap;
474 	info->glamor.SavedSharePixmapBacking = screen->SharePixmapBacking;
475 	screen->SharePixmapBacking = amdgpu_glamor_share_pixmap_backing;
476 	info->glamor.SavedSetSharedPixmapBacking = screen->SetSharedPixmapBacking;
477 	screen->SetSharedPixmapBacking =
478 	    amdgpu_glamor_set_shared_pixmap_backing;
479 
480 	xf86DrvMsg(scrn->scrnIndex, X_INFO, "Use GLAMOR acceleration.\n");
481 	return TRUE;
482 }
483 
484 void amdgpu_glamor_flush(ScrnInfoPtr pScrn)
485 {
486 	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
487 
488 	if (info->use_glamor) {
489 		glamor_block_handler(pScrn->pScreen);
490 	}
491 
492 	info->gpu_flushed++;
493 }
494 
495 void amdgpu_glamor_finish(ScrnInfoPtr pScrn)
496 {
497 	AMDGPUInfoPtr info = AMDGPUPTR(pScrn);
498 
499 	if (info->use_glamor) {
500 #if HAVE_GLAMOR_FINISH
501 		glamor_finish(pScrn->pScreen);
502 		info->gpu_flushed++;
503 #else
504 		amdgpu_glamor_flush(pScrn);
505 		glFinish();
506 #endif
507 	}
508 }
509 
510 void
511 amdgpu_glamor_fini(ScreenPtr screen)
512 {
513 	AMDGPUInfoPtr info = AMDGPUPTR(xf86ScreenToScrn(screen));
514 
515 	if (!info->use_glamor)
516 		return;
517 
518 	screen->CreatePixmap = info->glamor.SavedCreatePixmap;
519 	screen->DestroyPixmap = info->glamor.SavedDestroyPixmap;
520 	screen->SharePixmapBacking = info->glamor.SavedSharePixmapBacking;
521 	screen->SetSharedPixmapBacking = info->glamor.SavedSetSharedPixmapBacking;
522 }
523 
524 XF86VideoAdaptorPtr amdgpu_glamor_xv_init(ScreenPtr pScreen, int num_adapt)
525 {
526 	return glamor_xv_init(pScreen, num_adapt);
527 }
528 
529 #endif /* USE_GLAMOR */
530