]> git.sesse.net Git - vlc/blob - modules/hw/vdpau/display.c
68d84629a3ec8f8f08587c60d937f5bc9318e220
[vlc] / modules / hw / vdpau / display.c
1 /**
2  * @file display.c
3  * @brief VDPAU video display module for VLC media player
4  */
5 /*****************************************************************************
6  * Copyright © 2009-2013 Rémi Denis-Courmont
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26
27 #include <stdlib.h>
28 #include <assert.h>
29
30 #include <xcb/xcb.h>
31
32 #include <vlc_common.h>
33 #include <vlc_plugin.h>
34 #include <vlc_vout_display.h>
35 #include <vlc_picture_pool.h>
36 #include <vlc_xlib.h>
37
38 #include "vlc_vdpau.h"
39 #include "events.h"
40
41 static int Open(vlc_object_t *);
42 static void Close(vlc_object_t *);
43
44 vlc_module_begin()
45     set_shortname(N_("VDPAU"))
46     set_description(N_("VDPAU output"))
47     set_category(CAT_VIDEO)
48     set_subcategory(SUBCAT_VIDEO_VOUT)
49     set_capability("vout display", 300)
50     set_callbacks(Open, Close)
51
52     add_shortcut("vdpau", "xid")
53 vlc_module_end()
54
55 #define MAX_PICTURES (32)
56
57 struct vout_display_sys_t
58 {
59     xcb_connection_t *conn; /**< XCB connection */
60     vout_window_t *embed; /**< parent window */
61     vdp_t *vdp; /**< VDPAU back-end */
62     picture_t *current; /**< Currently visible picture */
63
64     xcb_window_t window; /**< target window (owned by VDPAU back-end) */
65     xcb_cursor_t cursor; /**< blank cursor */
66     VdpDevice device; /**< VDPAU device handle */
67     VdpPresentationQueueTarget target; /**< VDPAU presentation queue target */
68     VdpPresentationQueue queue; /**< VDPAU presentation queue */
69     VdpRGBAFormat rgb_fmt; /**< Output surface format */
70
71     picture_pool_t *pool; /**< pictures pool */
72     picture_sys_t *pics[MAX_PICTURES];
73 };
74
75 static picture_pool_t *PoolAlloc(vout_display_t *vd, unsigned requested_count)
76 {
77     vout_display_sys_t *sys = vd->sys;
78     const video_format_t *fmt = &vd->fmt;
79     picture_t *pics[MAX_PICTURES];
80     picture_resource_t res = { .p_sys = NULL };
81
82     if (requested_count > MAX_PICTURES)
83         requested_count = MAX_PICTURES;
84
85     unsigned count = 0;
86     while (count < requested_count)
87     {
88         picture_sys_t *psys = malloc(sizeof (*psys));
89         if (unlikely(psys == NULL))
90             break;
91
92         VdpStatus err = vdp_output_surface_create(sys->vdp, sys->device,
93                      sys->rgb_fmt, fmt->i_visible_width, fmt->i_visible_height,
94                                                   &psys->surface);
95         if (err != VDP_STATUS_OK)
96         {
97             free(psys);
98             msg_Err(vd, "%s creation failure: %s", "output surface",
99                     vdp_get_error_string(sys->vdp, err));
100             break;
101         }
102         psys->device = sys->device;
103         psys->vdp = sys->vdp;
104
105         res.p_sys = psys;
106         pics[count] = picture_NewFromResource(&vd->fmt, &res);
107         if (pics[count] == NULL)
108         {
109             free(psys);
110             break;
111         }
112         sys->pics[count++] = res.p_sys;
113     }
114     sys->current = NULL;
115     return count ? picture_pool_New(count, pics) : NULL;
116 }
117
118 static void PoolFree(vout_display_t *vd, picture_pool_t *pool)
119 {
120     vout_display_sys_t *sys = vd->sys;
121
122     for (unsigned count = 0; count < MAX_PICTURES; count++)
123     {
124         picture_sys_t *psys = sys->pics[count];
125         if (psys == NULL)
126             continue;
127
128         VdpStatus err = vdp_output_surface_destroy(sys->vdp, psys->surface);
129         if (err != VDP_STATUS_OK)
130             msg_Err(vd, "%s destruction failure: %s", "output surface",
131                     vdp_get_error_string(sys->vdp, err));
132     }
133     if (sys->current != NULL)
134         picture_Release(sys->current);
135     picture_pool_Delete(pool);
136 }
137
138 static picture_pool_t *Pool(vout_display_t *vd, unsigned requested_count)
139 {
140     vout_display_sys_t *sys = vd->sys;
141
142     if (sys->pool == NULL)
143         sys->pool = PoolAlloc(vd, requested_count);
144     return sys->pool;
145 }
146
147 static void RenderRegion(vout_display_t *vd, VdpOutputSurface target,
148                          const subpicture_region_t *reg, int alpha)
149 {
150     vout_display_sys_t *sys = vd->sys;
151     VdpBitmapSurface surface;
152     VdpRGBAFormat fmt = VDP_RGBA_FORMAT_R8G8B8A8; /* TODO? YUVA */
153     VdpStatus err;
154
155     /* Create GPU surface for sub-picture */
156     err = vdp_bitmap_surface_create(sys->vdp, sys->device, fmt,
157         reg->fmt.i_visible_width, reg->fmt.i_visible_height, VDP_FALSE,
158                                     &surface);
159     if (err != VDP_STATUS_OK)
160     {
161         msg_Err(vd, "%s creation failure: %s", "bitmap surface",
162                 vdp_get_error_string(sys->vdp, err));
163         return;
164     }
165
166     /* Upload sub-picture to GPU surface */
167     picture_t *subpic = reg->p_picture;
168     const void *data = subpic->p[0].p_pixels;
169     uint32_t pitch = subpic->p[0].i_pitch;
170
171     err = vdp_bitmap_surface_put_bits_native(sys->vdp, surface, &data, &pitch,
172                                              NULL);
173     if (err != VDP_STATUS_OK)
174     {
175         msg_Err(vd, "subpicture upload failure: %s",
176                 vdp_get_error_string(sys->vdp, err));
177         goto out;
178     }
179
180     /* Render onto main surface */
181     VdpRect area = {
182         reg->i_x,
183         reg->i_y,
184         reg->i_x + reg->fmt.i_visible_width,
185         reg->i_y + reg->fmt.i_visible_height,
186     };
187     VdpColor color = { 1.f, 1.f, 1.f, reg->i_alpha * alpha / 65535.f };
188     VdpOutputSurfaceRenderBlendState state = {
189         .struct_version = VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION,
190         .blend_factor_source_color =
191             VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA,
192         .blend_factor_destination_color =
193             VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
194         .blend_factor_source_alpha =
195             VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ZERO,
196         .blend_factor_destination_alpha =
197             VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE,
198         .blend_equation_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
199         .blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
200         .blend_constant = { 0.f, 0.f, 0.f, 0.f },
201     };
202
203     err = vdp_output_surface_render_bitmap_surface(sys->vdp, target, &area,
204                                              surface, NULL, &color, &state, 0);
205     if (err != VDP_STATUS_OK)
206         msg_Err(vd, "blending failure: %s",
207                 vdp_get_error_string(sys->vdp, err));
208
209 out:/* Destroy GPU surface */
210     vdp_bitmap_surface_destroy(sys->vdp, surface);
211 }
212
213 static void Queue(vout_display_t *vd, picture_t *pic, subpicture_t *subpic)
214 {
215     vout_display_sys_t *sys = vd->sys;
216     VdpOutputSurface surface = pic->p_sys->surface;
217     VdpStatus err;
218
219     VdpPresentationQueueStatus status;
220     VdpTime ts;
221     err = vdp_presentation_queue_query_surface_status(sys->vdp, sys->queue,
222                                                       surface, &status, &ts);
223     if (err == VDP_STATUS_OK && status != VDP_PRESENTATION_QUEUE_STATUS_IDLE)
224         msg_Dbg(vd, "surface status: %u", status);
225
226     if (subpic != NULL)
227         for (subpicture_region_t *r = subpic->p_region; r != NULL;
228              r = r->p_next)
229             RenderRegion(vd, surface, r, subpic->i_alpha);
230
231     /* Compute picture presentation time */
232     mtime_t now = mdate();
233     VdpTime pts;
234
235     err = vdp_presentation_queue_get_time(sys->vdp, sys->queue, &pts);
236     if (err != VDP_STATUS_OK)
237     {
238         msg_Err(vd, "presentation queue time failure: %s",
239                 vdp_get_error_string(sys->vdp, err));
240         return;
241     }
242
243     mtime_t delay = pic->date - now;
244     if (delay < 0)
245         delay = 0; /* core bug: date is not updated during pause */
246     if (unlikely(delay > CLOCK_FREQ))
247     {   /* We would get stuck if the delay was too long. */
248         msg_Dbg(vd, "picture date corrupt: delay of %"PRId64" us", delay);
249         delay = CLOCK_FREQ / 50;
250     }
251     pts += delay * 1000;
252
253     /* Queue picture */
254     err = vdp_presentation_queue_display(sys->vdp, sys->queue, surface, 0, 0,
255                                          pts);
256     if (err != VDP_STATUS_OK)
257         msg_Err(vd, "presentation queue display failure: %s",
258                 vdp_get_error_string(sys->vdp, err));
259 }
260
261 static void Wait(vout_display_t *vd, picture_t *pic, subpicture_t *subpicture)
262 {
263     vout_display_sys_t *sys = vd->sys;
264
265     picture_t *current = sys->current;
266     if (current != NULL)
267     {
268         picture_sys_t *psys = current->p_sys;
269         VdpTime pts;
270         VdpStatus err;
271
272         err = vdp_presentation_queue_block_until_surface_idle(sys->vdp,
273                                               sys->queue, psys->surface, &pts);
274         if (err != VDP_STATUS_OK)
275         {
276             msg_Err(vd, "presentation queue blocking error: %s",
277                     vdp_get_error_string(sys->vdp, err));
278             picture_Release(pic);
279             return;
280         }
281         picture_Release(current);
282     }
283
284     sys->current = pic;
285     (void) subpicture;
286 }
287
288 static int Control(vout_display_t *vd, int query, va_list ap)
289 {
290     vout_display_sys_t *sys = vd->sys;
291
292     switch (query)
293     {
294     case VOUT_DISPLAY_HIDE_MOUSE:
295         xcb_change_window_attributes(sys->conn, sys->embed->handle.xid,
296                                     XCB_CW_CURSOR, &(uint32_t){ sys->cursor });
297         break;
298     case VOUT_DISPLAY_RESET_PICTURES:
299     {
300         msg_Dbg(vd, "resetting pictures");
301         if (sys->pool != NULL)
302         {
303             PoolFree(vd, sys->pool);
304             sys->pool = NULL;
305         }
306
307         const video_format_t *src= &vd->source;
308         video_format_t *fmt = &vd->fmt;
309         vout_display_place_t place;
310
311         vout_display_PlacePicture(&place, src, vd->cfg, false);
312
313         fmt->i_width = src->i_width * place.width / src->i_visible_width;
314         fmt->i_height = src->i_height * place.height / src->i_visible_height;
315         fmt->i_visible_width  = place.width;
316         fmt->i_visible_height = place.height;
317         fmt->i_x_offset = src->i_x_offset * place.width / src->i_visible_width;
318         fmt->i_y_offset = src->i_y_offset * place.height / src->i_visible_height;
319
320         const uint32_t values[] = { place.x, place.y,
321                                     place.width, place.height, };
322         xcb_configure_window(sys->conn, sys->window,
323                              XCB_CONFIG_WINDOW_X|XCB_CONFIG_WINDOW_Y|
324                              XCB_CONFIG_WINDOW_WIDTH|XCB_CONFIG_WINDOW_HEIGHT,
325                              values);
326         break;
327     }
328     case VOUT_DISPLAY_CHANGE_FULLSCREEN:
329     {
330         const vout_display_cfg_t *c = va_arg(ap, const vout_display_cfg_t *);
331         return vout_window_SetFullScreen(sys->embed, c->is_fullscreen);
332     }
333     case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
334     {
335         unsigned state = va_arg(ap, unsigned);
336         return vout_window_SetState(sys->embed, state);
337     }
338     case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
339     {
340         const vout_display_cfg_t *cfg = va_arg(ap, const vout_display_cfg_t *);
341         bool forced = va_arg(ap, int);
342         if (forced)
343         {
344             vout_window_SetSize(sys->embed,
345                                 cfg->display.width, cfg->display.height);
346             return VLC_EGENERIC; /* Always fail. See x11.c for rationale. */
347         }
348
349         vout_display_place_t place;
350         vout_display_PlacePicture(&place, &vd->source, cfg, false);
351         if (place.width  != vd->fmt.i_visible_width
352          || place.height != vd->fmt.i_visible_height)
353         {
354             vout_display_SendEventPicturesInvalid(vd);
355             return VLC_SUCCESS;
356         }
357
358         const uint32_t values[] = { place.x, place.y,
359                                     place.width, place.height, };
360         xcb_configure_window(sys->conn, sys->window,
361                              XCB_CONFIG_WINDOW_X|XCB_CONFIG_WINDOW_Y|
362                              XCB_CONFIG_WINDOW_WIDTH|XCB_CONFIG_WINDOW_HEIGHT,
363                              values);
364         break;
365     }
366     case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
367     case VOUT_DISPLAY_CHANGE_ZOOM:
368     case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
369     case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
370         vout_display_SendEventPicturesInvalid (vd);
371         return VLC_SUCCESS;
372     default:
373         msg_Err(vd, "unknown control request %d", query);
374         return VLC_EGENERIC;
375     }
376     xcb_flush (sys->conn);
377     return VLC_SUCCESS;
378 }
379
380 static void Manage(vout_display_t *vd)
381 {
382     vout_display_sys_t *sys = vd->sys;
383     bool visible;
384
385     XCB_Manage(vd, sys->conn, &visible);
386 }
387
388 static int xcb_screen_num(xcb_connection_t *conn, const xcb_screen_t *screen)
389 {
390     const xcb_setup_t *setup = xcb_get_setup(conn);
391     unsigned snum = 0;
392
393     for (xcb_screen_iterator_t i = xcb_setup_roots_iterator(setup);
394          i.rem > 0; xcb_screen_next(&i))
395     {
396         if (i.data->root == screen->root)
397             return snum;
398         snum++;
399     }
400     return -1;
401 }
402
403 static int Open(vlc_object_t *obj)
404 {
405     if (!vlc_xlib_init(obj))
406         return VLC_EGENERIC;
407
408     vout_display_t *vd = (vout_display_t *)obj;
409     vout_display_sys_t *sys = malloc(sizeof (*sys));
410     if (unlikely(sys == NULL))
411         return VLC_ENOMEM;
412
413     const xcb_screen_t *screen;
414     uint16_t width, height;
415     sys->embed = XCB_parent_Create(vd, &sys->conn, &screen, &width, &height);
416     if (sys->embed == NULL)
417     {
418         free(sys);
419         return VLC_EGENERIC;
420     }
421
422     /* Load the VDPAU back-end and create a device instance */
423     VdpStatus err = vdp_get_x11(sys->embed->display.x11,
424                                 xcb_screen_num(sys->conn, screen),
425                                 &sys->vdp, &sys->device);
426     if (err != VDP_STATUS_OK)
427     {
428         msg_Dbg(obj, "device creation failure: error %d", (int)err);
429         xcb_disconnect(sys->conn);
430         vout_display_DeleteWindow(vd, sys->embed);
431         free(sys);
432         return VLC_EGENERIC;
433     }
434
435     const char *info;
436     if (vdp_get_information_string(sys->vdp, &info) == VDP_STATUS_OK)
437         msg_Dbg(vd, "using back-end %s", info);
438
439     /* Check source format */
440     VdpChromaType chroma;
441     VdpYCbCrFormat format;
442     if (vd->fmt.i_chroma == VLC_CODEC_VDPAU_VIDEO_420
443      || vd->fmt.i_chroma == VLC_CODEC_VDPAU_VIDEO_422)
444         ;
445     else
446     if (vlc_fourcc_to_vdp_ycc(vd->fmt.i_chroma, &chroma, &format))
447     {
448         uint32_t w, h;
449         VdpBool ok;
450
451         err = vdp_video_surface_query_capabilities(sys->vdp, sys->device,
452                                                    chroma, &ok, &w, &h);
453         if (err != VDP_STATUS_OK)
454         {
455             msg_Err(vd, "%s capabilities query failure: %s", "video surface",
456                     vdp_get_error_string(sys->vdp, err));
457             goto error;
458         }
459         if (!ok || w < vd->fmt.i_width || h < vd->fmt.i_height)
460         {
461             msg_Err(vd, "source video %s not supported", "chroma type");
462             goto error;
463         }
464
465         err = vdp_video_surface_query_get_put_bits_y_cb_cr_capabilities(
466                                    sys->vdp, sys->device, chroma, format, &ok);
467         if (err != VDP_STATUS_OK)
468         {
469             msg_Err(vd, "%s capabilities query failure: %s", "video surface",
470                     vdp_get_error_string(sys->vdp, err));
471             goto error;
472         }
473         if (!ok)
474         {
475             msg_Err(vd, "source video %s not supported", "YCbCr format");
476             goto error;
477         }
478     }
479     else
480         goto error;
481
482     /* Check video mixer capabilities */
483     {
484         uint32_t min, max;
485
486         err = vdp_video_mixer_query_parameter_value_range(sys->vdp,
487                     sys->device, VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_WIDTH,
488                                                           &min, &max);
489         if (err != VDP_STATUS_OK)
490         {
491             msg_Err(vd, "%s capabilities query failure: %s",
492                     "video mixer surface width",
493                     vdp_get_error_string(sys->vdp, err));
494             goto error;
495         }
496         if (min > vd->fmt.i_width || vd->fmt.i_width > max)
497         {
498             msg_Err(vd, "source video %s not supported", "width");
499             goto error;
500         }
501
502         err = vdp_video_mixer_query_parameter_value_range(sys->vdp,
503                    sys->device, VDP_VIDEO_MIXER_PARAMETER_VIDEO_SURFACE_HEIGHT,
504                                                           &min, &max);
505         if (err != VDP_STATUS_OK)
506         {
507             msg_Err(vd, "%s capabilities query failure: %s",
508                     "video mixer surface width",
509                     vdp_get_error_string(sys->vdp, err));
510             goto error;
511         }
512         if (min > vd->fmt.i_height || vd->fmt.i_height > max)
513         {
514             msg_Err(vd, "source video %s not supported", "height");
515             goto error;
516         }
517     }
518
519     /* Select surface format */
520     static const VdpRGBAFormat rgb_fmts[] = {
521         VDP_RGBA_FORMAT_R10G10B10A2, VDP_RGBA_FORMAT_B10G10R10A2,
522         VDP_RGBA_FORMAT_B8G8R8A8, VDP_RGBA_FORMAT_R8G8B8A8,
523     };
524     unsigned i;
525
526     for (i = 0; i < sizeof (rgb_fmts) / sizeof (rgb_fmts[0]); i++)
527     {
528         uint32_t w, h;
529         VdpBool ok;
530
531         err = vdp_output_surface_query_capabilities(sys->vdp, sys->device,
532                                                     rgb_fmts[i], &ok, &w, &h);
533         if (err != VDP_STATUS_OK)
534         {
535             msg_Err(vd, "%s capabilities query failure: %s", "output surface",
536                     vdp_get_error_string(sys->vdp, err));
537             continue;
538         }
539         /* NOTE: Wrong! No warranties that zoom <= 100%! */
540         if (!ok || w < vd->fmt.i_width || h < vd->fmt.i_height)
541             continue;
542
543         sys->rgb_fmt = rgb_fmts[i];
544         msg_Dbg(vd, "using RGBA format %u", sys->rgb_fmt);
545         break;
546     }
547     if (i == sizeof (rgb_fmts) / sizeof (rgb_fmts[0]))
548     {
549         msg_Err(vd, "no supported output surface format");
550         goto error;
551     }
552
553     /* VDPAU-X11 requires a window dedicated to the back-end */
554     {
555         xcb_pixmap_t pix = xcb_generate_id(sys->conn);
556         xcb_create_pixmap(sys->conn, screen->root_depth, pix,
557                           screen->root, 1, 1);
558
559         uint32_t mask =
560             XCB_CW_BACK_PIXMAP | XCB_CW_BACK_PIXEL |
561             XCB_CW_BORDER_PIXMAP | XCB_CW_BORDER_PIXEL |
562             XCB_CW_EVENT_MASK | XCB_CW_COLORMAP;
563         const uint32_t values[] = {
564             pix, screen->black_pixel, pix, screen->black_pixel,
565             XCB_EVENT_MASK_VISIBILITY_CHANGE, screen->default_colormap
566         };
567         vout_display_place_t place;
568
569         vout_display_PlacePicture (&place, &vd->source, vd->cfg, false);
570         sys->window = xcb_generate_id(sys->conn);
571
572         xcb_void_cookie_t c =
573             xcb_create_window_checked(sys->conn, screen->root_depth,
574                 sys->window, sys->embed->handle.xid, place.x, place.y,
575                 place.width, place.height, 0, XCB_WINDOW_CLASS_INPUT_OUTPUT,
576                 screen->root_visual, mask, values);
577         if (XCB_error_Check(vd, sys->conn, "window creation failure", c))
578             goto error;
579         msg_Dbg(vd, "using X11 window 0x%08"PRIx32, sys->window);
580         xcb_map_window(sys->conn, sys->window);
581     }
582
583     /* Check bitmap capabilities (for SPU) */
584     const vlc_fourcc_t *spu_chromas = NULL;
585     {
586         static const vlc_fourcc_t subpicture_chromas[] = { VLC_CODEC_RGBA, 0 };
587         uint32_t w, h;
588         VdpBool ok;
589
590         err = vdp_bitmap_surface_query_capabilities(sys->vdp, sys->device,
591                                         VDP_RGBA_FORMAT_R8G8B8A8, &ok, &w, &h);
592         if (err != VDP_STATUS_OK)
593         {
594             msg_Err(vd, "%s capabilities query failure: %s", "output surface",
595                     vdp_get_error_string(sys->vdp, err));
596             ok = VDP_FALSE;
597         }
598         if (ok)
599             spu_chromas = subpicture_chromas;
600     }
601
602     /* Initialize VDPAU queue */
603     err = vdp_presentation_queue_target_create_x11(sys->vdp, sys->device,
604                                                    sys->window, &sys->target);
605     if (err != VDP_STATUS_OK)
606     {
607         msg_Err(vd, "%s creation failure: %s", "presentation queue target",
608                 vdp_get_error_string(sys->vdp, err));
609         goto error;
610     }
611
612     err = vdp_presentation_queue_create(sys->vdp, sys->device, sys->target,
613                                         &sys->queue);
614     if (err != VDP_STATUS_OK)
615     {
616         msg_Err(vd, "%s creation failure: %s", "presentation queue",
617                 vdp_get_error_string(sys->vdp, err));
618         vdp_presentation_queue_target_destroy(sys->vdp, sys->target);
619         goto error;
620     }
621
622     VdpColor black = { 0.f, 0.f, 0.f, 1.f };
623     vdp_presentation_queue_set_background_color(sys->vdp, sys->queue, &black);
624
625     sys->cursor = XCB_cursor_Create(sys->conn, screen);
626     sys->pool = NULL;
627     for (unsigned count = 0; count < MAX_PICTURES; count++)
628         sys->pics[count] = NULL;
629
630     /* */
631     vd->sys = sys;
632     vd->info.has_pictures_invalid = true;
633     vd->info.has_event_thread = true;
634     vd->info.subpicture_chromas = spu_chromas;
635     vd->fmt.i_chroma = VLC_CODEC_VDPAU_OUTPUT;
636
637     vd->pool = Pool;
638     vd->prepare = Queue;
639     vd->display = Wait;
640     vd->control = Control;
641     vd->manage = Manage;
642
643     /* */
644     bool is_fullscreen = vd->cfg->is_fullscreen;
645     if (is_fullscreen && vout_window_SetFullScreen(sys->embed, true))
646         is_fullscreen = false;
647     vout_display_SendEventFullscreen(vd, is_fullscreen);
648     vout_display_SendEventDisplaySize(vd, width, height, is_fullscreen);
649
650     return VLC_SUCCESS;
651
652 error:
653     vdp_release_x11(sys->vdp);
654     xcb_disconnect(sys->conn);
655     vout_display_DeleteWindow(vd, sys->embed);
656     free(sys);
657     return VLC_EGENERIC;
658 }
659
660 static void Close(vlc_object_t *obj)
661 {
662     vout_display_t *vd = (vout_display_t *)obj;
663     vout_display_sys_t *sys = vd->sys;
664
665     /* Restore cursor explicitly (parent window connection will survive) */
666     xcb_change_window_attributes(sys->conn, sys->embed->handle.xid,
667                                XCB_CW_CURSOR, &(uint32_t) { XCB_CURSOR_NONE });
668     xcb_flush(sys->conn);
669
670     vdp_presentation_queue_destroy(sys->vdp, sys->queue);
671     vdp_presentation_queue_target_destroy(sys->vdp, sys->target);
672
673     if (sys->pool != NULL)
674         PoolFree(vd, sys->pool);
675
676     vdp_release_x11(sys->vdp);
677     xcb_disconnect(sys->conn);
678     vout_display_DeleteWindow(vd, sys->embed);
679     free(sys);
680 }