1 /*
2  * Copyright (c) 2011 Intel Corporation. All Rights Reserved.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sub license, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the
13  * next paragraph) shall be included in all copies or substantial portions
14  * of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
19  * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
20  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  *
24  * Authors:
25  *    Shengquan Yuan  <shengquan.yuan@intel.com>
26  *    Zhaohan Ren  <zhaohan.ren@intel.com>
27  *
28  */
29 
30 #include <X11/Xutil.h>
31 #include <X11/extensions/Xrandr.h>
32 #include <X11/extensions/dpms.h>
33 #include <va/va_dricommon.h>
34 #include <va/va_backend.h>
35 #include "psb_output.h"
36 #include "psb_surface.h"
37 #include "psb_buffer.h"
38 #include "psb_x11.h"
39 #include "psb_surface_ext.h"
40 #include "psb_drv_debug.h"
41 
42 #include <stdio.h>
43 #include <string.h>
44 #include <stdarg.h>
45 #include "psb_surface_ext.h"
46 #include <wsbm/wsbm_manager.h>
47 
48 #define INIT_DRIVER_DATA    psb_driver_data_p driver_data = (psb_driver_data_p) ctx->pDriverData
49 #define INIT_OUTPUT_PRIV    psb_x11_output_p output = (psb_x11_output_p)(((psb_driver_data_p)ctx->pDriverData)->ws_priv)
50 
51 #define SURFACE(id)     ((object_surface_p) object_heap_lookup( &driver_data->surface_heap, id ))
52 #define BUFFER(id)  ((object_buffer_p) object_heap_lookup( &driver_data->buffer_heap, id ))
53 #define IMAGE(id)  ((object_image_p) object_heap_lookup( &driver_data->image_heap, id ))
54 #define SUBPIC(id)  ((object_subpic_p) object_heap_lookup( &driver_data->subpic_heap, id ))
55 #define CONTEXT(id) ((object_context_p) object_heap_lookup( &driver_data->context_heap, id ))
56 
57 
58 void psb_x11_freeWindowClipBoxList(psb_x11_clip_list_t * pHead);
59 
60 
61 //X error trap
62 static int x11_error_code = 0;
63 static int (*old_error_handler)(Display *, XErrorEvent *);
64 
65 static struct timeval inter_period = {0};
psb_doframerate(int fps)66 static void psb_doframerate(int fps)
67 {
68     struct timeval time_deta;
69 
70     inter_period.tv_usec += 1000000 / fps;
71 
72     /*recording how long it passed*/
73     if (inter_period.tv_usec >= 1000000) {
74         inter_period.tv_usec -= 1000000;
75         inter_period.tv_sec++;
76     }
77 
78     gettimeofday(&time_deta, (struct timezone *)NULL);
79 
80     time_deta.tv_usec = inter_period.tv_usec - time_deta.tv_usec;
81     time_deta.tv_sec  = inter_period.tv_sec  - time_deta.tv_sec;
82 
83     if (time_deta.tv_usec < 0) {
84         time_deta.tv_usec += 1000000;
85         time_deta.tv_sec--;
86     }
87 
88     if (time_deta.tv_sec < 0 || (time_deta.tv_sec == 0 && time_deta.tv_usec <= 0))
89         return;
90 
91     select(0, NULL, NULL, NULL, &time_deta);
92 }
93 
mask2shift(uint32_t mask)94 static uint32_t mask2shift(uint32_t mask)
95 {
96     uint32_t shift = 0;
97     while ((mask & 0x1) == 0) {
98         mask = mask >> 1;
99         shift++;
100     }
101     return shift;
102 }
103 
psb_putsurface_x11(VADriverContextP ctx,VASurfaceID surface,Drawable draw,short srcx,short srcy,unsigned short srcw,unsigned short srch,short destx,short desty,unsigned short destw,unsigned short desth,unsigned int flags)104 static VAStatus psb_putsurface_x11(
105     VADriverContextP ctx,
106     VASurfaceID surface,
107     Drawable draw, /* X Drawable */
108     short srcx,
109     short srcy,
110     unsigned short srcw,
111     unsigned short srch,
112     short destx,
113     short desty,
114     unsigned short destw,
115     unsigned short desth,
116     unsigned int flags /* de-interlacing flags */
117 )
118 {
119     INIT_DRIVER_DATA;
120     GC gc;
121     XImage *ximg = NULL;
122     Visual *visual;
123     unsigned short width, height;
124     int depth;
125     int x = 0, y = 0;
126     VAStatus vaStatus = VA_STATUS_SUCCESS;
127     void *surface_data = NULL;
128     int ret;
129 
130     uint32_t rmask = 0;
131     uint32_t gmask = 0;
132     uint32_t bmask = 0;
133 
134     uint32_t rshift = 0;
135     uint32_t gshift = 0;
136     uint32_t bshift = 0;
137 
138 
139     if (srcw <= destw)
140         width = srcw;
141     else
142         width = destw;
143 
144     if (srch <= desth)
145         height = srch;
146     else
147         height = desth;
148 
149     object_surface_p obj_surface = SURFACE(surface);
150     if (NULL == obj_surface) {
151         vaStatus = VA_STATUS_ERROR_INVALID_SURFACE;
152         DEBUG_FAILURE;
153         return vaStatus;
154     }
155 
156     psb_surface_p psb_surface = obj_surface->psb_surface;
157 
158     drv_debug_msg(VIDEO_DEBUG_GENERAL, "PutSurface: src	  w x h = %d x %d\n", srcw, srch);
159     drv_debug_msg(VIDEO_DEBUG_GENERAL, "PutSurface: dest 	  w x h = %d x %d\n", destw, desth);
160     drv_debug_msg(VIDEO_DEBUG_GENERAL, "PutSurface: clipped w x h = %d x %d\n", width, height);
161 
162     visual = DefaultVisual((Display *)ctx->native_dpy, ctx->x11_screen);
163     gc = XCreateGC((Display *)ctx->native_dpy, draw, 0, NULL);
164     depth = DefaultDepth((Display *)ctx->native_dpy, ctx->x11_screen);
165 
166     if (TrueColor != visual->class) {
167         drv_debug_msg(VIDEO_DEBUG_ERROR, "PutSurface: Default visual of X display must be TrueColor.\n");
168         vaStatus = VA_STATUS_ERROR_UNKNOWN;
169         goto out;
170     }
171 
172     ret = psb_buffer_map(&psb_surface->buf, &surface_data);
173     if (ret) {
174         vaStatus = VA_STATUS_ERROR_UNKNOWN;
175         goto out;
176     }
177 
178     rmask = visual->red_mask;
179     gmask = visual->green_mask;
180     bmask = visual->blue_mask;
181 
182     rshift = mask2shift(rmask);
183     gshift = mask2shift(gmask);
184     bshift = mask2shift(bmask);
185 
186     drv_debug_msg(VIDEO_DEBUG_GENERAL, "PutSurface: Pixel masks: R = %08x G = %08x B = %08x\n", rmask, gmask, bmask);
187     drv_debug_msg(VIDEO_DEBUG_GENERAL, "PutSurface: Pixel shifts: R = %d G = %d B = %d\n", rshift, gshift, bshift);
188 
189     ximg = XCreateImage((Display *)ctx->native_dpy, visual, depth, ZPixmap, 0, NULL, width, height, 32, 0);
190 
191     if (ximg->byte_order == MSBFirst)
192         drv_debug_msg(VIDEO_DEBUG_GENERAL, "PutSurface: XImage pixels has MSBFirst, %d bits / pixel\n", ximg->bits_per_pixel);
193     else
194         drv_debug_msg(VIDEO_DEBUG_GENERAL, "PutSurface: XImage pixels has LSBFirst, %d bits / pixel\n", ximg->bits_per_pixel);
195 
196     if (ximg->bits_per_pixel != 32) {
197         drv_debug_msg(VIDEO_DEBUG_ERROR, "PutSurface: Display uses %d bits/pixel which is not supported\n");
198         vaStatus = VA_STATUS_ERROR_UNKNOWN;
199         goto out;
200     }
201 
202     void yuv2pixel(uint32_t * pixel, int y, int u, int v) {
203         int r, g, b;
204         /* Warning, magic values ahead */
205         r = y + ((351 * (v - 128)) >> 8);
206         g = y - (((179 * (v - 128)) + (86 * (u - 128))) >> 8);
207         b = y + ((444 * (u - 128)) >> 8);
208 
209         if (r > 255) r = 255;
210         if (g > 255) g = 255;
211         if (b > 255) b = 255;
212         if (r < 0) r = 0;
213         if (g < 0) g = 0;
214         if (b < 0) b = 0;
215 
216         *pixel = ((r << rshift) & rmask) | ((g << gshift) & gmask) | ((b << bshift) & bmask);
217     }
218     ximg->data = (char *) malloc(ximg->bytes_per_line * height);
219     if (NULL == ximg->data) {
220         vaStatus = VA_STATUS_ERROR_ALLOCATION_FAILED;
221         goto out;
222     }
223 
224     uint8_t *src_y = surface_data + psb_surface->stride * srcy;
225     uint8_t *src_uv = surface_data + psb_surface->stride * (obj_surface->height + srcy / 2);
226 
227     for (y = srcy; y < (srcy + height); y += 2) {
228         uint32_t *dest_even = (uint32_t *)(ximg->data + y * ximg->bytes_per_line);
229         uint32_t *dest_odd = (uint32_t *)(ximg->data + (y + 1) * ximg->bytes_per_line);
230         for (x = srcx; x < (srcx + width); x += 2) {
231             /* Y1 Y2 */
232             /* Y3 Y4 */
233             int y1 = *(src_y + x);
234             int y2 = *(src_y + x + 1);
235             int y3 = *(src_y + x + psb_surface->stride);
236             int y4 = *(src_y + x + psb_surface->stride + 1);
237 
238             /* U V */
239             int u = *(src_uv + x);
240             int v = *(src_uv + x + 1);
241 
242             yuv2pixel(dest_even++, y1, u, v);
243             yuv2pixel(dest_even++, y2, u, v);
244 
245             yuv2pixel(dest_odd++, y3, u, v);
246             yuv2pixel(dest_odd++, y4, u, v);
247         }
248         src_y += psb_surface->stride * 2;
249         src_uv += psb_surface->stride;
250     }
251 
252     XPutImage((Display *)ctx->native_dpy, draw, gc, ximg, 0, 0, destx, desty, width, height);
253     XFlush((Display *)ctx->native_dpy);
254 
255 out:
256     if (NULL != ximg)
257         XDestroyImage(ximg);
258     if (NULL != surface_data)
259         psb_buffer_unmap(&psb_surface->buf);
260 
261     XFreeGC((Display *)ctx->native_dpy, gc);
262 
263     return vaStatus;
264 }
265 
psb_x11_output_init(VADriverContextP ctx)266 void *psb_x11_output_init(VADriverContextP ctx)
267 {
268     INIT_DRIVER_DATA;
269     psb_x11_output_p output = calloc(1, sizeof(psb_x11_output_s));
270 
271     if (output == NULL) {
272         drv_debug_msg(VIDEO_DEBUG_ERROR, "Can't malloc memory\n");
273         return NULL;
274     }
275 
276     if (getenv("PSB_VIDEO_EXTEND_FULLSCREEN"))
277         driver_data->extend_fullscreen = 1;
278 
279     if (getenv("PSB_VIDEO_PUTSURFACE_X11")) {
280         drv_debug_msg(VIDEO_DEBUG_GENERAL, "Putsurface force to SW rendering\n");
281         driver_data->output_method = PSB_PUTSURFACE_X11;
282 
283         return output;
284     }
285 
286     psb_init_xvideo(ctx, output);
287 
288     output->output_drawable = 0;
289     output->extend_drawable = 0;
290     output->pClipBoxList = NULL;
291     output->ui32NumClipBoxList = 0;
292     output->frame_count = 0;
293     output->bIsVisible = 0;
294 
295     /* always init CTEXTURE and COVERLAY */
296     driver_data->coverlay = 1;
297     driver_data->color_key = 0x11;
298     driver_data->ctexture = 1;
299 
300     driver_data->xrandr_dirty = 0;
301     driver_data->xrandr_update = 0;
302 
303     if (getenv("PSB_VIDEO_EXTEND_FULLSCREEN")) {
304         driver_data->extend_fullscreen = 1;
305     }
306 
307     driver_data->xrandr_thread_id = 0;
308     if (getenv("PSB_VIDEO_NOTRD") || IS_MRST(driver_data)) {
309         drv_debug_msg(VIDEO_DEBUG_GENERAL, "Force not to start psb xrandr thread.\n");
310         driver_data->use_xrandr_thread = 0;
311     } else {
312         drv_debug_msg(VIDEO_DEBUG_GENERAL, "By default, use psb xrandr thread.\n");
313         driver_data->use_xrandr_thread = 1;
314     }
315 
316     if (IS_MFLD(driver_data) && /* force MFLD to use COVERLAY */
317         (driver_data->output_method == PSB_PUTSURFACE_OVERLAY)) {
318         drv_debug_msg(VIDEO_DEBUG_GENERAL, "Use client overlay mode for post-processing\n");
319 
320         driver_data->output_method = PSB_PUTSURFACE_COVERLAY;
321     }
322 
323     if (getenv("PSB_VIDEO_TEXTURE") && output->textured_portID) {
324         drv_debug_msg(VIDEO_DEBUG_GENERAL, "Putsurface force to use Textured Xvideo\n");
325         driver_data->output_method = PSB_PUTSURFACE_FORCE_TEXTURE;
326     }
327 
328     if (getenv("PSB_VIDEO_OVERLAY") && output->overlay_portID) {
329         drv_debug_msg(VIDEO_DEBUG_GENERAL, "Putsurface force to use Overlay Xvideo\n");
330         driver_data->output_method = PSB_PUTSURFACE_FORCE_OVERLAY;
331     }
332 
333     if (getenv("PSB_VIDEO_CTEXTURE")) {
334         drv_debug_msg(VIDEO_DEBUG_GENERAL, "Putsurface force to use Client Texture\n");
335         driver_data->output_method = PSB_PUTSURFACE_FORCE_CTEXTURE;
336     }
337 
338     if (getenv("PSB_VIDEO_COVERLAY")) {
339         drv_debug_msg(VIDEO_DEBUG_GENERAL, "Putsurface force to use Client Overlay\n");
340 
341         driver_data->coverlay = 1;
342         driver_data->output_method = PSB_PUTSURFACE_FORCE_COVERLAY;
343     }
344 
345     return output;
346 }
347 
348 static int
error_handler(Display * dpy,XErrorEvent * error)349 error_handler(Display *dpy, XErrorEvent *error)
350 {
351     x11_error_code = error->error_code;
352     return 0;
353 }
354 
psb_x11_output_deinit(VADriverContextP ctx)355 void psb_x11_output_deinit(VADriverContextP ctx)
356 {
357 #ifdef _FOR_FPGA_
358     if (getenv("PSB_VIDEO_PUTSURFACE_X11"))
359         return;
360     else
361         psb_deinit_xvideo(ctx);
362 #else
363     INIT_DRIVER_DATA;
364     INIT_OUTPUT_PRIV;
365     struct dri_state *dri_state = (struct dri_state *)ctx->dri_state;
366 
367     psb_x11_freeWindowClipBoxList(output->pClipBoxList);
368     output->pClipBoxList = NULL;
369 
370     if (output->extend_drawable) {
371         XDestroyWindow(ctx->native_dpy, output->extend_drawable);
372         output->extend_drawable = 0;
373     }
374 
375     psb_deinit_xvideo(ctx);
376 
377     /* close dri fd and release all drawable buffer */
378     if (driver_data->ctexture == 1)
379         (*dri_state->close)(ctx);
380 #endif
381 }
382 
383 static void
x11_trap_errors(void)384 x11_trap_errors(void)
385 {
386     x11_error_code = 0;
387     old_error_handler = XSetErrorHandler(error_handler);
388 }
389 
390 static int
x11_untrap_errors(void)391 x11_untrap_errors(void)
392 {
393     XSetErrorHandler(old_error_handler);
394     return x11_error_code;
395 }
396 
397 static int
is_window(Display * dpy,Drawable drawable)398 is_window(Display *dpy, Drawable drawable)
399 {
400     XWindowAttributes wattr;
401 
402     x11_trap_errors();
403     XGetWindowAttributes(dpy, drawable, &wattr);
404     return x11_untrap_errors() == 0;
405 }
406 
pnw_check_output_method(VADriverContextP ctx,object_surface_p obj_surface,int width,int height,int destw,int desth,Drawable draw)407 static int pnw_check_output_method(VADriverContextP ctx, object_surface_p obj_surface, int width, int height, int destw, int desth, Drawable draw)
408 {
409     INIT_DRIVER_DATA;
410     INIT_OUTPUT_PRIV;
411 
412     if (driver_data->output_method == PSB_PUTSURFACE_FORCE_TEXTURE ||
413         driver_data->output_method == PSB_PUTSURFACE_FORCE_OVERLAY ||
414         driver_data->output_method == PSB_PUTSURFACE_FORCE_CTEXTURE ||
415         driver_data->output_method == PSB_PUTSURFACE_FORCE_COVERLAY) {
416         drv_debug_msg(VIDEO_DEBUG_GENERAL, "Force to use %08x for PutSurface\n", driver_data->output_method);
417         return 0;
418     }
419 
420     driver_data->output_method = PSB_PUTSURFACE_COVERLAY;
421 
422     if (driver_data->overlay_auto_paint_color_key)
423         driver_data->output_method = PSB_PUTSURFACE_COVERLAY;
424 
425     /* Avoid call is_window()/XGetWindowAttributes() every frame */
426     if (output->output_drawable_save != draw) {
427         output->output_drawable_save = draw;
428         if (!is_window(ctx->native_dpy, draw))
429             output->is_pixmap = 1;
430         else
431             output->is_pixmap = 0;
432     }
433 
434     /*FIXME: overlay path can't handle subpicture scaling. when surface size > dest box, fallback to texblit.*/
435     if ((output->is_pixmap == 1)
436         || (IS_MRST(driver_data) && obj_surface->subpic_count > 0)
437         || (obj_surface->subpic_count && ((width > destw) || (height > desth)))
438         || (width >= 2048)
439         || (height >= 2048)
440        ) {
441         drv_debug_msg(VIDEO_DEBUG_GENERAL, "Putsurface fall back to use Client Texture\n");
442 
443         driver_data->output_method = PSB_PUTSURFACE_CTEXTURE;
444     }
445 
446     if (IS_MFLD(driver_data) &&
447         (driver_data->xrandr_dirty & PSB_NEW_ROTATION)) {
448         psb_RecalcRotate(ctx);
449         driver_data->xrandr_dirty &= ~PSB_NEW_ROTATION;
450     }
451 
452     return 0;
453 }
454 
psb_PutSurface(VADriverContextP ctx,VASurfaceID surface,void * drawable,short srcx,short srcy,unsigned short srcw,unsigned short srch,short destx,short desty,unsigned short destw,unsigned short desth,VARectangle * cliprects,unsigned int number_cliprects,unsigned int flags)455 VAStatus psb_PutSurface(
456     VADriverContextP ctx,
457     VASurfaceID surface,
458     void *drawable, /* X Drawable */
459     short srcx,
460     short srcy,
461     unsigned short srcw,
462     unsigned short srch,
463     short destx,
464     short desty,
465     unsigned short destw,
466     unsigned short desth,
467     VARectangle *cliprects, /* client supplied clip list */
468     unsigned int number_cliprects, /* number of clip rects in the clip list */
469     unsigned int flags /* de-interlacing flags */
470 )
471 {
472     INIT_DRIVER_DATA;
473     object_surface_p obj_surface;
474     VAStatus vaStatus = VA_STATUS_SUCCESS;
475     Drawable draw = (Drawable)drawable;
476     obj_surface = SURFACE(surface);
477 
478     if (NULL == obj_surface) {
479         vaStatus = VA_STATUS_ERROR_INVALID_SURFACE;
480         DEBUG_FAILURE;
481         return vaStatus;
482     }
483 
484     if (driver_data->dummy_putsurface) {
485         drv_debug_msg(VIDEO_DEBUG_GENERAL, "vaPutSurface: dummy mode, return directly\n");
486         return VA_STATUS_SUCCESS;
487     }
488 
489     if (driver_data->output_method == PSB_PUTSURFACE_X11) {
490         psb_putsurface_x11(ctx, surface, draw, srcx, srcy, srcw, srch,
491                            destx, desty, destw, desth, flags);
492         return VA_STATUS_SUCCESS;
493     }
494 
495     if (driver_data->fixed_fps > 0) {
496         if ((inter_period.tv_sec == 0) && (inter_period.tv_usec == 0))
497             gettimeofday(&inter_period, (struct timezone *)NULL);
498 
499         psb_doframerate(driver_data->fixed_fps);
500     }
501 
502     pnw_check_output_method(ctx, obj_surface, srcw, srch, destw, desth, draw);
503 
504     pthread_mutex_lock(&driver_data->output_mutex);
505 
506     if ((driver_data->output_method == PSB_PUTSURFACE_CTEXTURE) ||
507         (driver_data->output_method == PSB_PUTSURFACE_FORCE_CTEXTURE)) {
508         drv_debug_msg(VIDEO_DEBUG_GENERAL, "Using client Texture for PutSurface\n");
509         psb_putsurface_ctexture(ctx, surface, draw,
510                                 srcx, srcy, srcw, srch,
511                                 destx, desty, destw, desth,
512                                 flags);
513     } else if ((driver_data->output_method == PSB_PUTSURFACE_COVERLAY) ||
514                (driver_data->output_method == PSB_PUTSURFACE_FORCE_COVERLAY)) {
515         drv_debug_msg(VIDEO_DEBUG_GENERAL, "Using client Overlay for PutSurface\n");
516 
517         srcw = srcw <= 1920 ? srcw : 1920;
518         /* init overlay*/
519         if (!driver_data->coverlay_init) {
520             psb_coverlay_init(ctx);
521             driver_data->coverlay_init = 1;
522         }
523 
524         psb_putsurface_coverlay(
525             ctx, surface, draw,
526             srcx, srcy, srcw, srch,
527             destx, desty, destw, desth,
528             cliprects, number_cliprects, flags);
529     } else
530         psb_putsurface_xvideo(
531             ctx, surface, draw,
532             srcx, srcy, srcw, srch,
533             destx, desty, destw, desth,
534             cliprects, number_cliprects, flags);
535     pthread_mutex_unlock(&driver_data->output_mutex);
536 
537     driver_data->frame_count++;
538 
539     return VA_STATUS_SUCCESS;
540 }
541