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