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
58void psb_x11_freeWindowClipBoxList(psb_x11_clip_list_t * pHead);
59
60
61//X error trap
62static int x11_error_code = 0;
63static int (*old_error_handler)(Display *, XErrorEvent *);
64
65static struct timeval inter_period = {0};
66static 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
94static 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
104static 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
255out:
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
266void *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
348static int
349error_handler(Display *dpy, XErrorEvent *error)
350{
351    x11_error_code = error->error_code;
352    return 0;
353}
354
355void 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
383static void
384x11_trap_errors(void)
385{
386    x11_error_code = 0;
387    old_error_handler = XSetErrorHandler(error_handler);
388}
389
390static int
391x11_untrap_errors(void)
392{
393    XSetErrorHandler(old_error_handler);
394    return x11_error_code;
395}
396
397static int
398is_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
407static 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
455VAStatus 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