1 /*
2  * VDPAU video output driver
3  *
4  * Copyright (C) 2008 NVIDIA (Rajib Mahapatra <rmahapatra@nvidia.com>)
5  * Copyright (C) 2009 Uoti Urpala
6  *
7  * This file is part of mpv.
8  *
9  * mpv is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * mpv is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with mpv.  If not, see <http://www.gnu.org/licenses/>.
21  */
22 
23 /*
24  * Actual decoding is done in video/decode/vdpau.c
25  */
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdint.h>
30 #include <stdbool.h>
31 #include <limits.h>
32 #include <assert.h>
33 
34 #include "config.h"
35 #include "video/vdpau.h"
36 #include "video/vdpau_mixer.h"
37 #include "video/hwdec.h"
38 #include "common/msg.h"
39 #include "options/options.h"
40 #include "mpv_talloc.h"
41 #include "vo.h"
42 #include "x11_common.h"
43 #include "video/csputils.h"
44 #include "sub/osd.h"
45 #include "options/m_option.h"
46 #include "video/mp_image.h"
47 #include "osdep/timer.h"
48 
49 // Returns x + a, but wrapped around to the range [0, m)
50 // a must be within [-m, m], x within [0, m)
51 #define WRAP_ADD(x, a, m) ((a) < 0 \
52                            ? ((x)+(a)+(m) < (m) ? (x)+(a)+(m) : (x)+(a)) \
53                            : ((x)+(a) < (m) ? (x)+(a) : (x)+(a)-(m)))
54 
55 
56 /* number of video and output surfaces */
57 #define MAX_OUTPUT_SURFACES                15
58 
59 /* Pixelformat used for output surfaces */
60 #define OUTPUT_RGBA_FORMAT VDP_RGBA_FORMAT_B8G8R8A8
61 
62 /*
63  * Global variable declaration - VDPAU specific
64  */
65 
66 struct vdpctx {
67     struct mp_vdpau_ctx               *mpvdp;
68     struct vdp_functions              *vdp;
69     VdpDevice                          vdp_device;
70     uint64_t                           preemption_counter;
71 
72     struct m_color                     colorkey;
73 
74     VdpPresentationQueueTarget         flip_target;
75     VdpPresentationQueue               flip_queue;
76 
77     VdpOutputSurface                   output_surfaces[MAX_OUTPUT_SURFACES];
78     int                                num_output_surfaces;
79     VdpOutputSurface                   black_pixel;
80     VdpOutputSurface                   rotation_surface;
81 
82     struct mp_image                   *current_image;
83     int64_t                            current_pts;
84     int                                current_duration;
85 
86     int                                output_surface_w, output_surface_h;
87     int                                rotation;
88 
89     int                                force_yuv;
90     struct mp_vdpau_mixer             *video_mixer;
91     int                                pullup;
92     float                              denoise;
93     float                              sharpen;
94     int                                hqscaling;
95     int                                chroma_deint;
96     int                                flip_offset_window;
97     int                                flip_offset_fs;
98     int64_t                            flip_offset_us;
99 
100     VdpRect                            src_rect_vid;
101     VdpRect                            out_rect_vid;
102     struct mp_osd_res                  osd_rect;
103     VdpBool                            supports_a8;
104 
105     int                                surface_num; // indexes output_surfaces
106     int                                query_surface_num;
107     VdpTime                            recent_vsync_time;
108     float                              user_fps;
109     int                                composite_detect;
110     int                                vsync_interval;
111     uint64_t                           last_queue_time;
112     uint64_t                           queue_time[MAX_OUTPUT_SURFACES];
113     uint64_t                           last_ideal_time;
114     bool                               dropped_frame;
115     uint64_t                           dropped_time;
116     uint32_t                           vid_width, vid_height;
117     uint32_t                           image_format;
118     VdpYCbCrFormat                     vdp_pixel_format;
119     bool                               rgb_mode;
120 
121     // OSD
122     struct osd_bitmap_surface {
123         VdpRGBAFormat format;
124         VdpBitmapSurface surface;
125         uint32_t surface_w, surface_h;
126         // List of surfaces to be rendered
127         struct osd_target {
128             VdpRect source;
129             VdpRect dest;
130             VdpColor color;
131         } *targets;
132         int targets_size;
133         int render_count;
134         int change_id;
135     } osd_surfaces[MAX_OSD_PARTS];
136 };
137 
138 static bool status_ok(struct vo *vo);
139 
video_to_output_surface(struct vo * vo,struct mp_image * mpi)140 static int video_to_output_surface(struct vo *vo, struct mp_image *mpi)
141 {
142     struct vdpctx *vc = vo->priv;
143     struct vdp_functions *vdp = vc->vdp;
144     VdpTime dummy;
145     VdpStatus vdp_st;
146 
147     VdpOutputSurface output_surface = vc->output_surfaces[vc->surface_num];
148     VdpRect *output_rect = &vc->out_rect_vid;
149     VdpRect *video_rect = &vc->src_rect_vid;
150 
151     vdp_st = vdp->presentation_queue_block_until_surface_idle(vc->flip_queue,
152                                                               output_surface,
153                                                               &dummy);
154     CHECK_VDP_WARNING(vo, "Error when calling "
155                       "vdp_presentation_queue_block_until_surface_idle");
156 
157     // Clear the borders between video and window (if there are any).
158     // For some reason, video_mixer_render doesn't need it for YUV.
159     // Also, if there is nothing to render, at least clear the screen.
160     if (vc->rgb_mode || !mpi || mpi->params.rotate != 0) {
161         int flags = VDP_OUTPUT_SURFACE_RENDER_ROTATE_0;
162         vdp_st = vdp->output_surface_render_output_surface(output_surface,
163                                                            NULL, vc->black_pixel,
164                                                            NULL, NULL, NULL,
165                                                            flags);
166         CHECK_VDP_WARNING(vo, "Error clearing screen");
167     }
168 
169     if (!mpi)
170         return -1;
171 
172     struct mp_vdpau_mixer_frame *frame = mp_vdpau_mixed_frame_get(mpi);
173     struct mp_vdpau_mixer_opts opts = {0};
174     if (frame)
175         opts = frame->opts;
176 
177     // Apply custom vo_vdpau suboptions.
178     opts.chroma_deint |= vc->chroma_deint;
179     opts.pullup |= vc->pullup;
180     opts.denoise = MPCLAMP(opts.denoise + vc->denoise, 0, 1);
181     opts.sharpen = MPCLAMP(opts.sharpen + vc->sharpen, -1, 1);
182     if (vc->hqscaling)
183         opts.hqscaling = vc->hqscaling;
184 
185     if (mpi->params.rotate != 0) {
186         int flags;
187         VdpRect r_rect;
188         switch (mpi->params.rotate) {
189         case 90:
190             r_rect.y0 = output_rect->x0;
191             r_rect.y1 = output_rect->x1;
192             r_rect.x0 = output_rect->y0;
193             r_rect.x1 = output_rect->y1;
194             flags = VDP_OUTPUT_SURFACE_RENDER_ROTATE_90;
195             break;
196         case 180:
197             r_rect.x0 = output_rect->x0;
198             r_rect.x1 = output_rect->x1;
199             r_rect.y0 = output_rect->y0;
200             r_rect.y1 = output_rect->y1;
201             flags = VDP_OUTPUT_SURFACE_RENDER_ROTATE_180;
202             break;
203         case 270:
204             r_rect.y0 = output_rect->x0;
205             r_rect.y1 = output_rect->x1;
206             r_rect.x0 = output_rect->y0;
207             r_rect.x1 = output_rect->y1;
208             flags = VDP_OUTPUT_SURFACE_RENDER_ROTATE_270;
209             break;
210         default:
211             MP_ERR(vo, "Unsupported rotation angle: %u\n", mpi->params.rotate);
212             return -1;
213         }
214 
215         mp_vdpau_mixer_render(vc->video_mixer, &opts, vc->rotation_surface,
216                               &r_rect, mpi, video_rect);
217         vdp_st = vdp->output_surface_render_output_surface(output_surface,
218                                                            output_rect,
219                                                            vc->rotation_surface,
220                                                            &r_rect,
221                                                            NULL,
222                                                            NULL,
223                                                            flags);
224         CHECK_VDP_WARNING(vo, "Error rendering rotated frame");
225     } else {
226         mp_vdpau_mixer_render(vc->video_mixer, &opts, output_surface,
227                               output_rect, mpi, video_rect);
228     }
229     return 0;
230 }
231 
forget_frames(struct vo * vo,bool seek_reset)232 static void forget_frames(struct vo *vo, bool seek_reset)
233 {
234     struct vdpctx *vc = vo->priv;
235 
236     if (!seek_reset)
237         mp_image_unrefp(&vc->current_image);
238 
239     vc->dropped_frame = false;
240 }
241 
s_size(int max,int s,int disp)242 static int s_size(int max, int s, int disp)
243 {
244     disp = MPMAX(1, disp);
245     return MPMIN(max, MPMAX(s, disp));
246 }
247 
resize(struct vo * vo)248 static void resize(struct vo *vo)
249 {
250     struct vdpctx *vc = vo->priv;
251     struct vdp_functions *vdp = vc->vdp;
252     VdpStatus vdp_st;
253     struct mp_rect src_rect;
254     struct mp_rect dst_rect;
255     vo_get_src_dst_rects(vo, &src_rect, &dst_rect, &vc->osd_rect);
256     vc->out_rect_vid.x0 = dst_rect.x0;
257     vc->out_rect_vid.x1 = dst_rect.x1;
258     vc->out_rect_vid.y0 = dst_rect.y0;
259     vc->out_rect_vid.y1 = dst_rect.y1;
260     if (vo->params->rotate == 90 || vo->params->rotate == 270) {
261         vc->src_rect_vid.y0 = src_rect.x0;
262         vc->src_rect_vid.y1 = src_rect.x1;
263         vc->src_rect_vid.x0 = src_rect.y0;
264         vc->src_rect_vid.x1 = src_rect.y1;
265     } else {
266         vc->src_rect_vid.x0 = src_rect.x0;
267         vc->src_rect_vid.x1 = src_rect.x1;
268         vc->src_rect_vid.y0 = src_rect.y0;
269         vc->src_rect_vid.y1 = src_rect.y1;
270     }
271 
272     VdpBool ok;
273     uint32_t max_w, max_h;
274     vdp_st = vdp->output_surface_query_capabilities(vc->vdp_device,
275                                                     OUTPUT_RGBA_FORMAT,
276                                                     &ok, &max_w, &max_h);
277     if (vdp_st != VDP_STATUS_OK || !ok)
278         return;
279 
280     vc->flip_offset_us = vo->opts->fullscreen ?
281                          1000LL * vc->flip_offset_fs :
282                          1000LL * vc->flip_offset_window;
283     vo_set_queue_params(vo, vc->flip_offset_us, 1);
284 
285     if (vc->output_surface_w < vo->dwidth || vc->output_surface_h < vo->dheight ||
286         vc->rotation != vo->params->rotate)
287     {
288         vc->output_surface_w = s_size(max_w, vc->output_surface_w, vo->dwidth);
289         vc->output_surface_h = s_size(max_h, vc->output_surface_h, vo->dheight);
290         // Creation of output_surfaces
291         for (int i = 0; i < vc->num_output_surfaces; i++)
292             if (vc->output_surfaces[i] != VDP_INVALID_HANDLE) {
293                 vdp_st = vdp->output_surface_destroy(vc->output_surfaces[i]);
294                 CHECK_VDP_WARNING(vo, "Error when calling "
295                                   "vdp_output_surface_destroy");
296             }
297         for (int i = 0; i < vc->num_output_surfaces; i++) {
298             vdp_st = vdp->output_surface_create(vc->vdp_device,
299                                                 OUTPUT_RGBA_FORMAT,
300                                                 vc->output_surface_w,
301                                                 vc->output_surface_h,
302                                                 &vc->output_surfaces[i]);
303             CHECK_VDP_WARNING(vo, "Error when calling vdp_output_surface_create");
304             MP_DBG(vo, "vdpau out create: %u\n",
305                    vc->output_surfaces[i]);
306         }
307         if (vc->rotation_surface != VDP_INVALID_HANDLE) {
308             vdp_st = vdp->output_surface_destroy(vc->rotation_surface);
309             CHECK_VDP_WARNING(vo, "Error when calling "
310                               "vdp_output_surface_destroy");
311             vc->rotation_surface = VDP_INVALID_HANDLE;
312         }
313         if (vo->params->rotate == 90 || vo->params->rotate == 270) {
314             vdp_st = vdp->output_surface_create(vc->vdp_device,
315                                                 OUTPUT_RGBA_FORMAT,
316                                                 vc->output_surface_h,
317                                                 vc->output_surface_w,
318                                                 &vc->rotation_surface);
319         } else if (vo->params->rotate == 180) {
320             vdp_st = vdp->output_surface_create(vc->vdp_device,
321                                                 OUTPUT_RGBA_FORMAT,
322                                                 vc->output_surface_w,
323                                                 vc->output_surface_h,
324                                                 &vc->rotation_surface);
325         }
326         CHECK_VDP_WARNING(vo, "Error when calling vdp_output_surface_create");
327         MP_DBG(vo, "vdpau rotation surface create: %u\n",
328                vc->rotation_surface);
329     }
330     vc->rotation = vo->params->rotate;
331     vo->want_redraw = true;
332 }
333 
win_x11_init_vdpau_flip_queue(struct vo * vo)334 static int win_x11_init_vdpau_flip_queue(struct vo *vo)
335 {
336     struct vdpctx *vc = vo->priv;
337     struct vdp_functions *vdp = vc->vdp;
338     struct vo_x11_state *x11 = vo->x11;
339     VdpStatus vdp_st;
340 
341     if (vc->flip_target == VDP_INVALID_HANDLE) {
342         vdp_st = vdp->presentation_queue_target_create_x11(vc->vdp_device,
343                                                            x11->window,
344                                                            &vc->flip_target);
345         CHECK_VDP_ERROR(vo, "Error when calling "
346                         "vdp_presentation_queue_target_create_x11");
347     }
348 
349     /* Emperically this seems to be the first call which fails when we
350      * try to reinit after preemption while the user is still switched
351      * from X to a virtual terminal (creating the vdp_device initially
352      * succeeds, as does creating the flip_target above). This is
353      * probably not guaranteed behavior.
354      */
355     if (vc->flip_queue == VDP_INVALID_HANDLE) {
356         vdp_st = vdp->presentation_queue_create(vc->vdp_device, vc->flip_target,
357                                                 &vc->flip_queue);
358         CHECK_VDP_ERROR(vo, "Error when calling vdp_presentation_queue_create");
359     }
360 
361     if (vc->colorkey.a > 0) {
362         VdpColor color = {
363             .red = vc->colorkey.r / 255.0,
364             .green = vc->colorkey.g / 255.0,
365             .blue = vc->colorkey.b / 255.0,
366             .alpha = 0,
367         };
368         vdp_st = vdp->presentation_queue_set_background_color(vc->flip_queue,
369                                                               &color);
370         CHECK_VDP_WARNING(vo, "Error setting colorkey");
371     }
372 
373     if (vc->composite_detect && vo_x11_screen_is_composited(vo)) {
374         MP_INFO(vo, "Compositing window manager detected. Assuming timing info "
375                 "is inaccurate.\n");
376         vc->user_fps = -1;
377     }
378 
379     return 0;
380 }
381 
382 // Free everything specific to a certain video file
free_video_specific(struct vo * vo)383 static void free_video_specific(struct vo *vo)
384 {
385     struct vdpctx *vc = vo->priv;
386     struct vdp_functions *vdp = vc->vdp;
387     VdpStatus vdp_st;
388 
389     forget_frames(vo, false);
390 
391     if (vc->black_pixel != VDP_INVALID_HANDLE) {
392         vdp_st = vdp->output_surface_destroy(vc->black_pixel);
393         CHECK_VDP_WARNING(vo, "Error when calling vdp_output_surface_destroy");
394     }
395     vc->black_pixel = VDP_INVALID_HANDLE;
396 }
397 
initialize_vdpau_objects(struct vo * vo)398 static int initialize_vdpau_objects(struct vo *vo)
399 {
400     struct vdpctx *vc = vo->priv;
401     struct vdp_functions *vdp = vc->vdp;
402     VdpStatus vdp_st;
403 
404     mp_vdpau_get_format(vc->image_format, NULL, &vc->vdp_pixel_format);
405 
406     vc->video_mixer->initialized = false;
407 
408     if (win_x11_init_vdpau_flip_queue(vo) < 0)
409         return -1;
410 
411     if (vc->black_pixel == VDP_INVALID_HANDLE) {
412         vdp_st = vdp->output_surface_create(vc->vdp_device, OUTPUT_RGBA_FORMAT,
413                                             1, 1, &vc->black_pixel);
414         CHECK_VDP_ERROR(vo, "Allocating clearing surface");
415         const char data[4] = {0};
416         vdp_st = vdp->output_surface_put_bits_native(vc->black_pixel,
417                                                      (const void*[]){data},
418                                                      (uint32_t[]){4}, NULL);
419         CHECK_VDP_ERROR(vo, "Initializing clearing surface");
420     }
421 
422     forget_frames(vo, false);
423     resize(vo);
424     return 0;
425 }
426 
mark_vdpau_objects_uninitialized(struct vo * vo)427 static void mark_vdpau_objects_uninitialized(struct vo *vo)
428 {
429     struct vdpctx *vc = vo->priv;
430 
431     forget_frames(vo, false);
432     vc->black_pixel = VDP_INVALID_HANDLE;
433     vc->flip_queue = VDP_INVALID_HANDLE;
434     vc->flip_target = VDP_INVALID_HANDLE;
435     for (int i = 0; i < MAX_OUTPUT_SURFACES; i++)
436         vc->output_surfaces[i] = VDP_INVALID_HANDLE;
437     vc->rotation_surface = VDP_INVALID_HANDLE;
438     vc->vdp_device = VDP_INVALID_HANDLE;
439     for (int i = 0; i < MAX_OSD_PARTS; i++) {
440         struct osd_bitmap_surface *sfc = &vc->osd_surfaces[i];
441         sfc->change_id = 0;
442         *sfc = (struct osd_bitmap_surface){
443             .surface = VDP_INVALID_HANDLE,
444         };
445     }
446     vc->output_surface_w = vc->output_surface_h = -1;
447 }
448 
check_preemption(struct vo * vo)449 static bool check_preemption(struct vo *vo)
450 {
451     struct vdpctx *vc = vo->priv;
452 
453     int r = mp_vdpau_handle_preemption(vc->mpvdp, &vc->preemption_counter);
454     if (r < 1) {
455         mark_vdpau_objects_uninitialized(vo);
456         if (r < 0)
457             return false;
458         vc->vdp_device = vc->mpvdp->vdp_device;
459         if (initialize_vdpau_objects(vo) < 0)
460             return false;
461     }
462     return true;
463 }
464 
status_ok(struct vo * vo)465 static bool status_ok(struct vo *vo)
466 {
467     return vo->config_ok && check_preemption(vo);
468 }
469 
470 /*
471  * connect to X server, create and map window, initialize all
472  * VDPAU objects, create different surfaces etc.
473  */
reconfig(struct vo * vo,struct mp_image_params * params)474 static int reconfig(struct vo *vo, struct mp_image_params *params)
475 {
476     struct vdpctx *vc = vo->priv;
477     struct vdp_functions *vdp = vc->vdp;
478     VdpStatus vdp_st;
479 
480     if (!check_preemption(vo))
481     {
482         /*
483          * When prempted, leave the reconfig() immediately
484          * without reconfiguring the vo_window and without
485          * initializing the vdpau objects. When recovered
486          * from preemption, if there is a difference between
487          * the VD thread parameters and the VO thread parameters
488          * the reconfig() is triggered again.
489          */
490         return 0;
491     }
492 
493     VdpChromaType chroma_type = VDP_CHROMA_TYPE_420;
494     mp_vdpau_get_format(params->imgfmt, &chroma_type, NULL);
495 
496     VdpBool ok;
497     uint32_t max_w, max_h;
498     vdp_st = vdp->video_surface_query_capabilities(vc->vdp_device, chroma_type,
499                                                    &ok, &max_w, &max_h);
500     CHECK_VDP_ERROR(vo, "Error when calling vdp_video_surface_query_capabilities");
501 
502     if (!ok)
503         return -1;
504     if (params->w > max_w || params->h > max_h) {
505         if (ok)
506             MP_ERR(vo, "Video too large for vdpau.\n");
507         return -1;
508     }
509 
510     vc->image_format = params->imgfmt;
511     vc->vid_width    = params->w;
512     vc->vid_height   = params->h;
513 
514     vc->rgb_mode = mp_vdpau_get_rgb_format(params->imgfmt, NULL);
515 
516     free_video_specific(vo);
517 
518     vo_x11_config_vo_window(vo);
519 
520     if (initialize_vdpau_objects(vo) < 0)
521         return -1;
522 
523     return 0;
524 }
525 
draw_osd_part(struct vo * vo,int index)526 static void draw_osd_part(struct vo *vo, int index)
527 {
528     struct vdpctx *vc = vo->priv;
529     struct vdp_functions *vdp = vc->vdp;
530     VdpStatus vdp_st;
531     struct osd_bitmap_surface *sfc = &vc->osd_surfaces[index];
532     VdpOutputSurface output_surface = vc->output_surfaces[vc->surface_num];
533     int i;
534 
535     VdpOutputSurfaceRenderBlendState blend_state = {
536         .struct_version = VDP_OUTPUT_SURFACE_RENDER_BLEND_STATE_VERSION,
537         .blend_factor_source_color =
538             VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_SRC_ALPHA,
539         .blend_factor_source_alpha =
540             VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ZERO,
541         .blend_factor_destination_color =
542             VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA,
543         .blend_factor_destination_alpha =
544             VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ZERO,
545         .blend_equation_color = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
546         .blend_equation_alpha = VDP_OUTPUT_SURFACE_RENDER_BLEND_EQUATION_ADD,
547     };
548 
549     VdpOutputSurfaceRenderBlendState blend_state_premultiplied = blend_state;
550     blend_state_premultiplied.blend_factor_source_color =
551             VDP_OUTPUT_SURFACE_RENDER_BLEND_FACTOR_ONE;
552 
553     for (i = 0; i < sfc->render_count; i++) {
554         VdpOutputSurfaceRenderBlendState *blend = &blend_state;
555         if (sfc->format == VDP_RGBA_FORMAT_B8G8R8A8)
556             blend = &blend_state_premultiplied;
557         vdp_st = vdp->
558             output_surface_render_bitmap_surface(output_surface,
559                                                  &sfc->targets[i].dest,
560                                                  sfc->surface,
561                                                  &sfc->targets[i].source,
562                                                  &sfc->targets[i].color,
563                                                  blend,
564                                                  VDP_OUTPUT_SURFACE_RENDER_ROTATE_0);
565         CHECK_VDP_WARNING(vo, "OSD: Error when rendering");
566     }
567 }
568 
next_pow2(int v)569 static int next_pow2(int v)
570 {
571     for (int x = 0; x < 30; x++) {
572         if ((1 << x) >= v)
573             return 1 << x;
574     }
575     return INT_MAX;
576 }
577 
generate_osd_part(struct vo * vo,struct sub_bitmaps * imgs)578 static void generate_osd_part(struct vo *vo, struct sub_bitmaps *imgs)
579 {
580     struct vdpctx *vc = vo->priv;
581     struct vdp_functions *vdp = vc->vdp;
582     VdpStatus vdp_st;
583     struct osd_bitmap_surface *sfc = &vc->osd_surfaces[imgs->render_index];
584 
585     if (imgs->change_id == sfc->change_id)
586         return; // Nothing changed and we still have the old data
587 
588     sfc->change_id = imgs->change_id;
589     sfc->render_count = 0;
590 
591     if (imgs->format == SUBBITMAP_EMPTY || imgs->num_parts == 0)
592         return;
593 
594     VdpRGBAFormat format;
595     switch (imgs->format) {
596     case SUBBITMAP_LIBASS:
597         format = VDP_RGBA_FORMAT_A8;
598         break;
599     case SUBBITMAP_RGBA:
600         format = VDP_RGBA_FORMAT_B8G8R8A8;
601         break;
602     default:
603         abort();
604     };
605 
606     assert(imgs->packed);
607 
608     int r_w = next_pow2(imgs->packed_w);
609     int r_h = next_pow2(imgs->packed_h);
610 
611     if (sfc->format != format || sfc->surface == VDP_INVALID_HANDLE ||
612         sfc->surface_w < r_w || sfc->surface_h < r_h)
613     {
614         MP_VERBOSE(vo, "Allocating a %dx%d surface for OSD bitmaps.\n", r_w, r_h);
615 
616         uint32_t m_w = 0, m_h = 0;
617         vdp_st = vdp->bitmap_surface_query_capabilities(vc->vdp_device, format,
618                                                         &(VdpBool){0}, &m_w, &m_h);
619         CHECK_VDP_WARNING(vo, "Query to get max OSD surface size failed");
620 
621         if (r_w > m_w || r_h > m_h) {
622             MP_ERR(vo, "OSD bitmaps do not fit on a surface with the maximum "
623                    "supported size\n");
624             return;
625         }
626 
627         if (sfc->surface != VDP_INVALID_HANDLE) {
628             vdp_st = vdp->bitmap_surface_destroy(sfc->surface);
629             CHECK_VDP_WARNING(vo, "Error when calling vdp_bitmap_surface_destroy");
630         }
631 
632         VdpBitmapSurface surface;
633         vdp_st = vdp->bitmap_surface_create(vc->vdp_device, format,
634                                             r_w, r_h, true, &surface);
635         CHECK_VDP_WARNING(vo, "OSD: error when creating surface");
636         if (vdp_st != VDP_STATUS_OK)
637             return;
638 
639         sfc->surface = surface;
640         sfc->surface_w = r_w;
641         sfc->surface_h = r_h;
642         sfc->format = format;
643     }
644 
645     void *data = imgs->packed->planes[0];
646     int stride = imgs->packed->stride[0];
647     VdpRect rc = {0, 0, imgs->packed_w, imgs->packed_h};
648     vdp_st = vdp->bitmap_surface_put_bits_native(sfc->surface,
649                                                  &(const void *){data},
650                                                  &(uint32_t){stride},
651                                                  &rc);
652     CHECK_VDP_WARNING(vo, "OSD: putbits failed");
653 
654     MP_TARRAY_GROW(vc, sfc->targets, imgs->num_parts);
655     sfc->render_count = imgs->num_parts;
656 
657     for (int i = 0; i < imgs->num_parts; i++) {
658         struct sub_bitmap *b = &imgs->parts[i];
659         struct osd_target *target = &sfc->targets[i];
660         target->source = (VdpRect){b->src_x, b->src_y,
661                                    b->src_x + b->w, b->src_y + b->h};
662         target->dest = (VdpRect){b->x, b->y, b->x + b->dw, b->y + b->dh};
663         target->color = (VdpColor){1, 1, 1, 1};
664         if (imgs->format == SUBBITMAP_LIBASS) {
665             uint32_t color = b->libass.color;
666             target->color.alpha = 1.0 - ((color >> 0) & 0xff) / 255.0;
667             target->color.blue  = ((color >>  8) & 0xff) / 255.0;
668             target->color.green = ((color >> 16) & 0xff) / 255.0;
669             target->color.red   = ((color >> 24) & 0xff) / 255.0;
670         }
671     }
672 }
673 
draw_osd_cb(void * ctx,struct sub_bitmaps * imgs)674 static void draw_osd_cb(void *ctx, struct sub_bitmaps *imgs)
675 {
676     struct vo *vo = ctx;
677     generate_osd_part(vo, imgs);
678     draw_osd_part(vo, imgs->render_index);
679 }
680 
draw_osd(struct vo * vo)681 static void draw_osd(struct vo *vo)
682 {
683     struct vdpctx *vc = vo->priv;
684 
685     if (!status_ok(vo))
686         return;
687 
688     bool formats[SUBBITMAP_COUNT] = {
689         [SUBBITMAP_LIBASS] = vc->supports_a8,
690         [SUBBITMAP_RGBA] = true,
691     };
692 
693     double pts = vc->current_image ? vc->current_image->pts : 0;
694     osd_draw(vo->osd, vc->osd_rect, pts, 0, formats, draw_osd_cb, vo);
695 }
696 
update_presentation_queue_status(struct vo * vo)697 static int update_presentation_queue_status(struct vo *vo)
698 {
699     struct vdpctx *vc = vo->priv;
700     struct vdp_functions *vdp = vc->vdp;
701     VdpStatus vdp_st;
702 
703     while (vc->query_surface_num != vc->surface_num) {
704         VdpTime vtime;
705         VdpPresentationQueueStatus status;
706         VdpOutputSurface surface = vc->output_surfaces[vc->query_surface_num];
707         vdp_st = vdp->presentation_queue_query_surface_status(vc->flip_queue,
708                                                               surface,
709                                                               &status, &vtime);
710         CHECK_VDP_WARNING(vo, "Error calling "
711                          "presentation_queue_query_surface_status");
712         if (mp_msg_test(vo->log, MSGL_TRACE)) {
713             VdpTime current;
714             vdp_st = vdp->presentation_queue_get_time(vc->flip_queue, &current);
715             CHECK_VDP_WARNING(vo, "Error when calling "
716                               "vdp_presentation_queue_get_time");
717             MP_TRACE(vo, "Vdpau time: %"PRIu64"\n", (uint64_t)current);
718             MP_TRACE(vo, "Surface %d status: %d time: %"PRIu64"\n",
719                      (int)surface, (int)status, (uint64_t)vtime);
720         }
721         if (status == VDP_PRESENTATION_QUEUE_STATUS_QUEUED)
722             break;
723         if (vc->vsync_interval > 1) {
724             uint64_t qtime = vc->queue_time[vc->query_surface_num];
725             int diff = ((int64_t)vtime - (int64_t)qtime) / 1e6;
726             MP_TRACE(vo, "Queue time difference: %d ms\n", diff);
727             if (vtime < qtime + vc->vsync_interval / 2)
728                 MP_VERBOSE(vo, "Frame shown too early (%d ms)\n", diff);
729             if (vtime > qtime + vc->vsync_interval)
730                 MP_VERBOSE(vo, "Frame shown late (%d ms)\n", diff);
731         }
732         vc->query_surface_num = WRAP_ADD(vc->query_surface_num, 1,
733                                          vc->num_output_surfaces);
734         vc->recent_vsync_time = vtime;
735     }
736     int num_queued = WRAP_ADD(vc->surface_num, -vc->query_surface_num,
737                               vc->num_output_surfaces);
738     MP_DBG(vo, "Queued surface count (before add): %d\n", num_queued);
739     return num_queued;
740 }
741 
742 // Return the timestamp of the vsync that must have happened before ts.
prev_vsync(struct vdpctx * vc,uint64_t ts)743 static inline uint64_t prev_vsync(struct vdpctx *vc, uint64_t ts)
744 {
745     int64_t diff = (int64_t)(ts - vc->recent_vsync_time);
746     int64_t offset = diff % vc->vsync_interval;
747     if (offset < 0)
748         offset += vc->vsync_interval;
749     return ts - offset;
750 }
751 
flip_page(struct vo * vo)752 static void flip_page(struct vo *vo)
753 {
754     struct vdpctx *vc = vo->priv;
755     struct vdp_functions *vdp = vc->vdp;
756     VdpStatus vdp_st;
757 
758     int64_t pts_us = vc->current_pts;
759     int duration = vc->current_duration;
760 
761     vc->dropped_frame = true; // changed at end if false
762 
763     if (!check_preemption(vo))
764         goto drop;
765 
766     vc->vsync_interval = 1;
767     if (vc->user_fps > 0) {
768         vc->vsync_interval = 1e9 / vc->user_fps;
769     } else if (vc->user_fps == 0) {
770         vc->vsync_interval = vo_get_vsync_interval(vo) * 1000;
771     }
772     vc->vsync_interval = MPMAX(vc->vsync_interval, 1);
773 
774     if (duration > INT_MAX / 1000)
775         duration = -1;
776     else
777         duration *= 1000;
778 
779     if (vc->vsync_interval == 1)
780         duration = -1;  // Make sure drop logic is disabled
781 
782     VdpTime vdp_time = 0;
783     vdp_st = vdp->presentation_queue_get_time(vc->flip_queue, &vdp_time);
784     CHECK_VDP_WARNING(vo, "Error when calling vdp_presentation_queue_get_time");
785 
786     int64_t rel_pts_ns = (pts_us - mp_time_us()) * 1000;
787     if (!pts_us || rel_pts_ns < 0)
788         rel_pts_ns = 0;
789 
790     uint64_t now = vdp_time;
791     uint64_t pts = now + rel_pts_ns;
792     uint64_t ideal_pts = pts;
793     uint64_t npts = duration >= 0 ? pts + duration : UINT64_MAX;
794 
795     /* This should normally never happen.
796      * - The last queued frame can't have a PTS that goes more than 50ms in the
797      *   future. This is guaranteed by vo.c, which currently actually queues
798      *   ahead by roughly the flip queue offset. Just to be sure
799      *   give some additional room by doubling the time.
800      * - The last vsync can never be in the future.
801      */
802     int64_t max_pts_ahead = vc->flip_offset_us * 1000 * 2;
803     if (vc->last_queue_time > now + max_pts_ahead ||
804         vc->recent_vsync_time > now)
805     {
806         vc->last_queue_time = 0;
807         vc->recent_vsync_time = 0;
808         MP_WARN(vo, "Inconsistent timing detected.\n");
809     }
810 
811 #define PREV_VSYNC(ts) prev_vsync(vc, ts)
812 
813     /* We hope to be here at least one vsync before the frame should be shown.
814      * If we are running late then don't drop the frame unless there is
815      * already one queued for the next vsync; even if we _hope_ to show the
816      * next frame soon enough to mean this one should be dropped we might
817      * not make the target time in reality. Without this check we could drop
818      * every frame, freezing the display completely if video lags behind.
819      */
820     if (now > PREV_VSYNC(MPMAX(pts, vc->last_queue_time + vc->vsync_interval)))
821         npts = UINT64_MAX;
822 
823     /* Allow flipping a frame at a vsync if its presentation time is a
824      * bit after that vsync and the change makes the flip time delta
825      * from previous frame better match the target timestamp delta.
826      * This avoids instability with frame timestamps falling near vsyncs.
827      * For example if the frame timestamps were (with vsyncs at
828      * integer values) 0.01, 1.99, 4.01, 5.99, 8.01, ... then
829      * straightforward timing at next vsync would flip the frames at
830      * 1, 2, 5, 6, 9; this changes it to 1, 2, 4, 6, 8 and so on with
831      * regular 2-vsync intervals.
832      *
833      * Also allow moving the frame forward if it looks like we dropped
834      * the previous frame incorrectly (now that we know better after
835      * having final exact timestamp information for this frame) and
836      * there would unnecessarily be a vsync without a frame change.
837      */
838     uint64_t vsync = PREV_VSYNC(pts);
839     if (pts < vsync + vc->vsync_interval / 4
840         && (vsync - PREV_VSYNC(vc->last_queue_time)
841             > pts - vc->last_ideal_time + vc->vsync_interval / 2
842             || (vc->dropped_frame && vsync > vc->dropped_time)))
843         pts -= vc->vsync_interval / 2;
844 
845     vc->dropped_time = ideal_pts;
846 
847     pts = MPMAX(pts, vc->last_queue_time + vc->vsync_interval);
848     pts = MPMAX(pts, now);
849     if (npts < PREV_VSYNC(pts) + vc->vsync_interval)
850         goto drop;
851 
852     int num_flips = update_presentation_queue_status(vo);
853     vsync = vc->recent_vsync_time + num_flips * vc->vsync_interval;
854     pts = MPMAX(pts, now);
855     pts = MPMAX(pts, vsync + (vc->vsync_interval >> 2));
856     vsync = PREV_VSYNC(pts);
857     if (npts < vsync + vc->vsync_interval)
858         goto drop;
859     pts = vsync + (vc->vsync_interval >> 2);
860     VdpOutputSurface frame = vc->output_surfaces[vc->surface_num];
861     vdp_st = vdp->presentation_queue_display(vc->flip_queue, frame,
862                                              vo->dwidth, vo->dheight, pts);
863     CHECK_VDP_WARNING(vo, "Error when calling vdp_presentation_queue_display");
864 
865     MP_TRACE(vo, "Queue new surface %d: Vdpau time: %"PRIu64" "
866              "pts: %"PRIu64"\n", (int)frame, now, pts);
867 
868     vc->last_queue_time = pts;
869     vc->queue_time[vc->surface_num] = pts;
870     vc->last_ideal_time = ideal_pts;
871     vc->dropped_frame = false;
872     vc->surface_num = WRAP_ADD(vc->surface_num, 1, vc->num_output_surfaces);
873     return;
874 
875 drop:
876     vo_increment_drop_count(vo, 1);
877 }
878 
draw_frame(struct vo * vo,struct vo_frame * frame)879 static void draw_frame(struct vo *vo, struct vo_frame *frame)
880 {
881     struct vdpctx *vc = vo->priv;
882 
883     check_preemption(vo);
884 
885     if (frame->current && !frame->redraw) {
886         struct mp_image *vdp_mpi =
887             mp_vdpau_upload_video_surface(vc->mpvdp, frame->current);
888         if (!vdp_mpi)
889             MP_ERR(vo, "Could not upload image.\n");
890 
891         talloc_free(vc->current_image);
892         vc->current_image = vdp_mpi;
893     }
894 
895     vc->current_pts = frame->pts;
896     vc->current_duration = frame->duration;
897 
898     if (status_ok(vo)) {
899         video_to_output_surface(vo, vc->current_image);
900         draw_osd(vo);
901     }
902 }
903 
904 // warning: the size and pixel format of surface must match that of the
905 //          surfaces in vc->output_surfaces
read_output_surface(struct vo * vo,VdpOutputSurface surface)906 static struct mp_image *read_output_surface(struct vo *vo,
907                                             VdpOutputSurface surface)
908 {
909     struct vdpctx *vc = vo->priv;
910     VdpStatus vdp_st;
911     struct vdp_functions *vdp = vc->vdp;
912     if (!vo->params)
913         return NULL;
914 
915     VdpRGBAFormat fmt;
916     uint32_t w, h;
917     vdp_st = vdp->output_surface_get_parameters(surface, &fmt, &w, &h);
918     if (vdp_st != VDP_STATUS_OK)
919         return NULL;
920 
921     assert(fmt == OUTPUT_RGBA_FORMAT);
922 
923     struct mp_image *image = mp_image_alloc(IMGFMT_BGR0, w, h);
924     if (!image)
925         return NULL;
926 
927     void *dst_planes[] = { image->planes[0] };
928     uint32_t dst_pitches[] = { image->stride[0] };
929     vdp_st = vdp->output_surface_get_bits_native(surface, NULL, dst_planes,
930                                                  dst_pitches);
931     CHECK_VDP_WARNING(vo, "Error when calling vdp_output_surface_get_bits_native");
932 
933     return image;
934 }
935 
get_window_screenshot(struct vo * vo)936 static struct mp_image *get_window_screenshot(struct vo *vo)
937 {
938     struct vdpctx *vc = vo->priv;
939     int last_surface = WRAP_ADD(vc->surface_num, -1, vc->num_output_surfaces);
940     VdpOutputSurface screen = vc->output_surfaces[last_surface];
941     struct mp_image *image = read_output_surface(vo, screen);
942     if (image && image->w >= vo->dwidth && image->h >= vo->dheight)
943         mp_image_set_size(image, vo->dwidth, vo->dheight);
944     return image;
945 }
946 
query_format(struct vo * vo,int format)947 static int query_format(struct vo *vo, int format)
948 {
949     struct vdpctx *vc = vo->priv;
950 
951     if (mp_vdpau_get_format(format, NULL, NULL))
952         return 1;
953     if (!vc->force_yuv && mp_vdpau_get_rgb_format(format, NULL))
954         return 1;
955     return 0;
956 }
957 
destroy_vdpau_objects(struct vo * vo)958 static void destroy_vdpau_objects(struct vo *vo)
959 {
960     struct vdpctx *vc = vo->priv;
961     struct vdp_functions *vdp = vc->vdp;
962 
963     VdpStatus vdp_st;
964 
965     free_video_specific(vo);
966 
967     if (vc->flip_queue != VDP_INVALID_HANDLE) {
968         vdp_st = vdp->presentation_queue_destroy(vc->flip_queue);
969         CHECK_VDP_WARNING(vo, "Error when calling vdp_presentation_queue_destroy");
970     }
971 
972     if (vc->flip_target != VDP_INVALID_HANDLE) {
973         vdp_st = vdp->presentation_queue_target_destroy(vc->flip_target);
974         CHECK_VDP_WARNING(vo, "Error when calling "
975                          "vdp_presentation_queue_target_destroy");
976     }
977 
978     for (int i = 0; i < vc->num_output_surfaces; i++) {
979         if (vc->output_surfaces[i] == VDP_INVALID_HANDLE)
980             continue;
981         vdp_st = vdp->output_surface_destroy(vc->output_surfaces[i]);
982         CHECK_VDP_WARNING(vo, "Error when calling vdp_output_surface_destroy");
983     }
984     if (vc->rotation_surface != VDP_INVALID_HANDLE) {
985         vdp_st = vdp->output_surface_destroy(vc->rotation_surface);
986         CHECK_VDP_WARNING(vo, "Error when calling vdp_output_surface_destroy");
987     }
988 
989     for (int i = 0; i < MAX_OSD_PARTS; i++) {
990         struct osd_bitmap_surface *sfc = &vc->osd_surfaces[i];
991         if (sfc->surface != VDP_INVALID_HANDLE) {
992             vdp_st = vdp->bitmap_surface_destroy(sfc->surface);
993             CHECK_VDP_WARNING(vo, "Error when calling vdp_bitmap_surface_destroy");
994         }
995     }
996 
997     mp_vdpau_destroy(vc->mpvdp);
998     vc->mpvdp = NULL;
999 }
1000 
uninit(struct vo * vo)1001 static void uninit(struct vo *vo)
1002 {
1003     struct vdpctx *vc = vo->priv;
1004 
1005     hwdec_devices_remove(vo->hwdec_devs, &vc->mpvdp->hwctx);
1006     hwdec_devices_destroy(vo->hwdec_devs);
1007 
1008     /* Destroy all vdpau objects */
1009     mp_vdpau_mixer_destroy(vc->video_mixer);
1010     destroy_vdpau_objects(vo);
1011 
1012     vo_x11_uninit(vo);
1013 }
1014 
preinit(struct vo * vo)1015 static int preinit(struct vo *vo)
1016 {
1017     struct vdpctx *vc = vo->priv;
1018 
1019     if (!vo_x11_init(vo))
1020         return -1;
1021 
1022     if (!vo_x11_create_vo_window(vo, NULL, "vdpau")) {
1023         vo_x11_uninit(vo);
1024         return -1;
1025     }
1026 
1027     vc->mpvdp = mp_vdpau_create_device_x11(vo->log, vo->x11->display, false);
1028     if (!vc->mpvdp) {
1029         vo_x11_uninit(vo);
1030         return -1;
1031     }
1032 
1033     vo->hwdec_devs = hwdec_devices_create();
1034     hwdec_devices_add(vo->hwdec_devs, &vc->mpvdp->hwctx);
1035 
1036     vc->video_mixer = mp_vdpau_mixer_create(vc->mpvdp, vo->log);
1037     vc->video_mixer->video_eq = mp_csp_equalizer_create(vo, vo->global);
1038 
1039     if (mp_vdpau_guess_if_emulated(vc->mpvdp)) {
1040         MP_WARN(vo, "VDPAU is most likely emulated via VA-API.\n"
1041                     "This is inefficient. Use --vo=gpu instead.\n");
1042     }
1043 
1044     // Mark everything as invalid first so uninit() can tell what has been
1045     // allocated
1046     mark_vdpau_objects_uninitialized(vo);
1047 
1048     mp_vdpau_handle_preemption(vc->mpvdp, &vc->preemption_counter);
1049 
1050     vc->vdp_device = vc->mpvdp->vdp_device;
1051     vc->vdp = &vc->mpvdp->vdp;
1052 
1053     vc->vdp->bitmap_surface_query_capabilities(vc->vdp_device, VDP_RGBA_FORMAT_A8,
1054                             &vc->supports_a8, &(uint32_t){0}, &(uint32_t){0});
1055 
1056     MP_WARN(vo, "Warning: this compatibility VO is low quality and may "
1057                 "have issues with OSD, scaling, screenshots and more.\n"
1058                 "vo=gpu is the preferred choice in any case and "
1059                 "includes VDPAU support via hwdec=vdpau or vdpau-copy.\n");
1060 
1061     return 0;
1062 }
1063 
checked_resize(struct vo * vo)1064 static void checked_resize(struct vo *vo)
1065 {
1066     if (!status_ok(vo))
1067         return;
1068     resize(vo);
1069 }
1070 
control(struct vo * vo,uint32_t request,void * data)1071 static int control(struct vo *vo, uint32_t request, void *data)
1072 {
1073     check_preemption(vo);
1074 
1075     switch (request) {
1076     case VOCTRL_SET_PANSCAN:
1077         checked_resize(vo);
1078         return VO_TRUE;
1079     case VOCTRL_SET_EQUALIZER:
1080         vo->want_redraw = true;
1081         return true;
1082     case VOCTRL_RESET:
1083         forget_frames(vo, true);
1084         return true;
1085     case VOCTRL_SCREENSHOT_WIN:
1086         if (!status_ok(vo))
1087             return false;
1088         *(struct mp_image **)data = get_window_screenshot(vo);
1089         return true;
1090     }
1091 
1092     int events = 0;
1093     int r = vo_x11_control(vo, &events, request, data);
1094 
1095     if (events & VO_EVENT_RESIZE) {
1096         checked_resize(vo);
1097     } else if (events & VO_EVENT_EXPOSE) {
1098         vo->want_redraw = true;
1099     }
1100     vo_event(vo, events);
1101 
1102     return r;
1103 }
1104 
1105 #define OPT_BASE_STRUCT struct vdpctx
1106 
1107 const struct vo_driver video_out_vdpau = {
1108     .description = "VDPAU with X11",
1109     .name = "vdpau",
1110     .caps = VO_CAP_FRAMEDROP | VO_CAP_ROTATE90,
1111     .preinit = preinit,
1112     .query_format = query_format,
1113     .reconfig = reconfig,
1114     .control = control,
1115     .draw_frame = draw_frame,
1116     .flip_page = flip_page,
1117     .wakeup = vo_x11_wakeup,
1118     .wait_events = vo_x11_wait_events,
1119     .uninit = uninit,
1120     .priv_size = sizeof(struct vdpctx),
1121     .options = (const struct m_option []){
1122         {"chroma-deint", OPT_FLAG(chroma_deint), OPTDEF_INT(1)},
1123         {"pullup", OPT_FLAG(pullup)},
1124         {"denoise", OPT_FLOAT(denoise), M_RANGE(0, 1)},
1125         {"sharpen", OPT_FLOAT(sharpen), M_RANGE(-1, 1)},
1126         {"hqscaling", OPT_INT(hqscaling), M_RANGE(0, 9)},
1127         {"fps", OPT_FLOAT(user_fps)},
1128         {"composite-detect", OPT_FLAG(composite_detect), OPTDEF_INT(1)},
1129         {"queuetime-windowed", OPT_INT(flip_offset_window), OPTDEF_INT(50)},
1130         {"queuetime-fs", OPT_INT(flip_offset_fs), OPTDEF_INT(50)},
1131         {"output-surfaces", OPT_INT(num_output_surfaces),
1132             M_RANGE(2, MAX_OUTPUT_SURFACES), OPTDEF_INT(3)},
1133         {"colorkey", OPT_COLOR(colorkey),
1134             .defval = &(const struct m_color){.r = 2, .g = 5, .b = 7, .a = 255}},
1135         {"force-yuv", OPT_FLAG(force_yuv)},
1136         {"queuetime_windowed", OPT_REPLACED("queuetime-windowed")},
1137         {"queuetime_fs", OPT_REPLACED("queuetime-fs")},
1138         {"output_surfaces", OPT_REPLACED("output-surfaces")},
1139         {NULL},
1140     },
1141     .options_prefix = "vo-vdpau",
1142 };
1143