| @@ -1,393 +0,0 @@ |
| 1 | /* | | |
| 2 | * This file is part of mpv video player. | | |
| 3 | * | | |
| 4 | * mpv is free software; you can redistribute it and/or | | |
| 5 | * modify it under the terms of the GNU Lesser General Public | | |
| 6 | * License as published by the Free Software Foundation; either | | |
| 7 | * version 2.1 of the License, or (at your option) any later version. | | |
| 8 | * | | |
| 9 | * mpv is distributed in the hope that it will be useful, | | |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | | |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | | |
| 12 | * GNU Lesser General Public License for more details. | | |
| 13 | * | | |
| 14 | * You should have received a copy of the GNU Lesser General Public | | |
| 15 | * License along with mpv. If not, see <http://www.gnu.org/licenses/>. | | |
| 16 | */ | | |
| 17 | | | |
| 18 | #include <fcntl.h> | | |
| 19 | #include <sys/mman.h> | | |
| 20 | #include <unistd.h> | | |
| 21 | #include <va/va_wayland.h> | | |
| 22 | #include <va/va_drmcommon.h> | | |
| 23 | | | |
| 24 | #include "present_sync.h" | | |
| 25 | #include "sub/osd.h" | | |
| 26 | #include "video/vaapi.h" | | |
| 27 | #include "wayland_common.h" | | |
| 28 | | | |
| 29 | #include "generated/wayland/linux-dmabuf-unstable-v1.h" | | |
| 30 | #include "generated/wayland/viewporter.h" | | |
| 31 | | | |
| 32 | #define VA_POOL_NUM_ALLOCATED_INIT 30 | | |
| 33 | | | |
| 34 | struct va_pool_entry { | | |
| 35 | /* key */ | | |
| 36 | VASurfaceID surface; | | |
| 37 | | | |
| 38 | VADRMPRIMESurfaceDescriptor desc; | | |
| 39 | struct wl_buffer *buffer; | | |
| 40 | struct zwp_linux_buffer_params_v1 *params; | | |
| 41 | uint32_t drm_format; | | |
| 42 | }; | | |
| 43 | | | |
| 44 | struct va_pool { | | |
| 45 | struct vo *vo; | | |
| 46 | struct va_pool_entry **entries; | | |
| 47 | int num_entries; | | |
| 48 | int num_allocated; | | |
| 49 | }; | | |
| 50 | | | |
| 51 | struct priv { | | |
| 52 | struct vo *vo; | | |
| 53 | struct mp_rect src; | | |
| 54 | struct mp_rect dst; | | |
| 55 | struct mp_osd_res osd; | | |
| 56 | struct mp_log *log; | | |
| 57 | | | |
| 58 | VADisplay display; | | |
| 59 | struct mp_vaapi_ctx *mpvaapi; | | |
| 60 | struct wl_shm_pool *solid_buffer_pool; | | |
| 61 | struct wl_buffer *solid_buffer; | | |
| 62 | struct va_pool *va_pool; | | |
| 63 | }; | | |
| 64 | | | |
| 65 | static void va_close_surface_descriptor(VADRMPRIMESurfaceDescriptor desc) | | |
| 66 | { | | |
| 67 | for (int i = 0; i < desc.num_objects; i++) { | | |
| 68 | close(desc.objects[i].fd); | | |
| 69 | desc.objects[i].fd = 0; | | |
| 70 | } | | |
| 71 | } | | |
| 72 | | | |
| 73 | static void va_free_entry(struct va_pool_entry *entry) | | |
| 74 | { | | |
| 75 | if (!entry) | | |
| 76 | return; | | |
| 77 | va_close_surface_descriptor(entry->desc); | | |
| 78 | if (entry->buffer) | | |
| 79 | wl_buffer_destroy(entry->buffer); | | |
| 80 | if (entry->params) | | |
| 81 | zwp_linux_buffer_params_v1_destroy(entry->params); | | |
| 82 | talloc_free(entry); | | |
| 83 | } | | |
| 84 | | | |
| 85 | static VAStatus va_export_surface_handle(VADisplay display, VASurfaceID surface, | | |
| 86 | VADRMPRIMESurfaceDescriptor *desc) | | |
| 87 | { | | |
| 88 | return vaExportSurfaceHandle(display, surface, | | |
| 89 | VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME_2, | | |
| 90 | VA_EXPORT_SURFACE_COMPOSED_LAYERS | | | |
| 91 | VA_EXPORT_SURFACE_READ_ONLY, | | |
| 92 | desc); | | |
| 93 | } | | |
| 94 | | | |
| 95 | static struct va_pool_entry *va_alloc_entry(struct vo *vo, struct mp_image *src) | | |
| 96 | { | | |
| 97 | struct priv *p = vo->priv; | | |
| 98 | struct vo_wayland_state *wl = vo->wl; | | |
| 99 | VAStatus status; | | |
| 100 | struct va_pool_entry *entry = talloc(NULL, struct va_pool_entry); | | |
| 101 | memset(entry, 0, sizeof(struct va_pool_entry)); | | |
| 102 | | | |
| 103 | /* extract dmabuf surface descriptor */ | | |
| 104 | entry->surface = va_surface_id(src); | | |
| 105 | status = va_export_surface_handle(p->display, entry->surface, &entry->desc); | | |
| 106 | if (status == VA_STATUS_ERROR_INVALID_SURFACE) { | | |
| 107 | MP_VERBOSE(vo, "VA export to composed layers not supported.\n"); | | |
| 108 | va_free_entry(entry); | | |
| 109 | return NULL; | | |
| 110 | } else if (!vo_wayland_supported_format(vo, entry->desc.layers[0].drm_format)) { | | |
| 111 | MP_VERBOSE(vo, "%s is not supported.\n", | | |
| 112 | mp_tag_str(entry->desc.layers[0].drm_format)); | | |
| 113 | va_free_entry(entry); | | |
| 114 | return NULL; | | |
| 115 | } else if (!CHECK_VA_STATUS(vo, "vaExportSurfaceHandle()")) { | | |
| 116 | va_free_entry(entry); | | |
| 117 | return NULL; | | |
| 118 | } else { | | |
| 119 | int i, j, plane = 0; | | |
| 120 | | | |
| 121 | entry->params = zwp_linux_dmabuf_v1_create_params(wl->dmabuf); | | |
| 122 | for (i = 0; i < entry->desc.num_layers; i++) { | | |
| 123 | entry->drm_format = entry->desc.layers[i].drm_format; | | |
| 124 | for (j = 0; j < entry->desc.layers[i].num_planes; ++j) { | | |
| 125 | int object = entry->desc.layers[i].object_index[j]; | | |
| 126 | uint64_t modifier = entry->desc.objects[object].drm_format_modifier; | | |
| 127 | zwp_linux_buffer_params_v1_add(entry->params, | | |
| 128 | entry->desc.objects[object].fd, plane++, | | |
| 129 | entry->desc.layers[i].offset[j], | | |
| 130 | entry->desc.layers[i].pitch[j], | | |
| 131 | modifier >> 32, | | |
| 132 | modifier & 0xffffffff); | | |
| 133 | } | | |
| 134 | } | | |
| 135 | } | | |
| 136 | | | |
| 137 | entry->buffer = zwp_linux_buffer_params_v1_create_immed(entry->params, | | |
| 138 | src->params.w, | | |
| 139 | src->params.h, | | |
| 140 | entry->drm_format, 0); | | |
| 141 | | | |
| 142 | return entry; | | |
| 143 | } | | |
| 144 | static void va_pool_clean(struct va_pool *pool) | | |
| 145 | { | | |
| 146 | if (!pool) | | |
| 147 | return; | | |
| 148 | | | |
| 149 | for (int i = 0; i < pool->num_entries; ++i) | | |
| 150 | va_free_entry(pool->entries[i]); | | |
| 151 | pool->num_entries = 0; | | |
| 152 | } | | |
| 153 | | | |
| 154 | static void va_pool_free(struct va_pool *pool) | | |
| 155 | { | | |
| 156 | if (!pool) | | |
| 157 | return; | | |
| 158 | | | |
| 159 | va_pool_clean(pool); | | |
| 160 | talloc_free(pool); | | |
| 161 | } | | |
| 162 | | | |
| 163 | static struct va_pool *va_pool_alloc(struct vo *vo) | | |
| 164 | { | | |
| 165 | struct va_pool *pool = talloc(NULL, struct va_pool); | | |
| 166 | memset(pool, 0, sizeof(struct va_pool)); | | |
| 167 | pool->num_allocated = VA_POOL_NUM_ALLOCATED_INIT; | | |
| 168 | pool->entries = talloc_array(pool, struct va_pool_entry *, pool->num_allocated); | | |
| 169 | memset(pool->entries, 0, pool->num_allocated * sizeof(struct va_pool_entry *)); | | |
| 170 | pool->vo = vo; | | |
| 171 | | | |
| 172 | return pool; | | |
| 173 | } | | |
| 174 | | | |
| 175 | static struct va_pool_entry *va_pool_alloc_entry(struct vo *vo, struct va_pool *pool, | | |
| 176 | struct mp_image *src) | | |
| 177 | { | | |
| 178 | VASurfaceID surface; | | |
| 179 | | | |
| 180 | if (!pool) | | |
| 181 | return NULL; | | |
| 182 | | | |
| 183 | surface = va_surface_id(src); | | |
| 184 | for (int i = 0; i < pool->num_entries; ++i) { | | |
| 185 | struct va_pool_entry *item = pool->entries[i]; | | |
| 186 | if (item->surface == surface) | | |
| 187 | return pool->entries[i]; | | |
| 188 | } | | |
| 189 | | | |
| 190 | struct va_pool_entry *entry = va_alloc_entry(pool->vo, src); | | |
| 191 | if (!entry) | | |
| 192 | return NULL; | | |
| 193 | | | |
| 194 | if (pool->num_entries == pool->num_allocated) { | | |
| 195 | int current_num_allocated = pool->num_allocated; | | |
| 196 | pool->num_allocated *= 2; | | |
| 197 | pool->entries = talloc_realloc(pool, pool->entries, struct va_pool_entry *, | | |
| 198 | pool->num_allocated); | | |
| 199 | for (int i = current_num_allocated; i < pool->num_allocated; ++i) | | |
| 200 | pool->entries[i] = NULL; | | |
| 201 | } | | |
| 202 | pool->entries[pool->num_entries++] = entry; | | |
| 203 | | | |
| 204 | return entry; | | |
| 205 | } | | |
| 206 | | | |
| 207 | static void uninit(struct vo *vo) | | |
| 208 | { | | |
| 209 | struct priv *p = vo->priv; | | |
| 210 | | | |
| 211 | va_pool_free(p->va_pool); | | |
| 212 | | | |
| 213 | if (p->solid_buffer_pool) | | |
| 214 | wl_shm_pool_destroy(p->solid_buffer_pool); | | |
| 215 | if (p->solid_buffer) | | |
| 216 | wl_buffer_destroy(p->solid_buffer); | | |
| 217 | | | |
| 218 | vo_wayland_uninit(vo); | | |
| 219 | | | |
| 220 | if (vo->hwdec_devs) { | | |
| 221 | hwdec_devices_remove(vo->hwdec_devs, &p->mpvaapi->hwctx); | | |
| 222 | hwdec_devices_destroy(vo->hwdec_devs); | | |
| 223 | } | | |
| 224 | if (p->mpvaapi) | | |
| 225 | va_destroy(p->mpvaapi); | | |
| 226 | } | | |
| 227 | | | |
| 228 | static int preinit(struct vo *vo) | | |
| 229 | { | | |
| 230 | struct priv *p = vo->priv; | | |
| 231 | | | |
| 232 | p->vo = vo; | | |
| 233 | p->log = vo->log; | | |
| 234 | if (!vo_wayland_init(vo)) | | |
| 235 | return VO_ERROR; | | |
| 236 | | | |
| 237 | p->display = vaGetDisplayWl(vo->wl->display); | | |
| 238 | if (!p->display) { | | |
| 239 | MP_ERR(vo, "Unable to get the VA Display.\n"); | | |
| 240 | return VO_ERROR; | | |
| 241 | } | | |
| 242 | | | |
| 243 | p->mpvaapi = va_initialize(p->display, p->log, false); | | |
| 244 | if (!p->mpvaapi) { | | |
| 245 | vaTerminate(p->display); | | |
| 246 | p->display = NULL; | | |
| 247 | goto fail; | | |
| 248 | } | | |
| 249 | | | |
| 250 | vo->hwdec_devs = hwdec_devices_create(); | | |
| 251 | hwdec_devices_add(vo->hwdec_devs, &p->mpvaapi->hwctx); | | |
| 252 | p->va_pool = va_pool_alloc(vo); | | |
| 253 | | | |
| 254 | return 0; | | |
| 255 | | | |
| 256 | fail: | | |
| 257 | uninit(vo); | | |
| 258 | | | |
| 259 | return VO_ERROR; | | |
| 260 | } | | |
| 261 | | | |
| 262 | static int query_format(struct vo *vo, int format) | | |
| 263 | { | | |
| 264 | return format == IMGFMT_VAAPI; | | |
| 265 | } | | |
| 266 | | | |
| 267 | static int reconfig(struct vo *vo, struct mp_image_params *params) | | |
| 268 | { | | |
| 269 | struct priv *p = vo->priv; | | |
| 270 | struct vo_wayland_state *wl = vo->wl; | | |
| 271 | | | |
| 272 | if (!p->solid_buffer_pool) { | | |
| 273 | int width = 1; | | |
| 274 | int height = 1; | | |
| 275 | int stride = MP_ALIGN_UP(width * 4, 16); | | |
| 276 | int fd = vo_wayland_allocate_memfd(vo, stride); | | |
| 277 | if (fd < 0) | | |
| 278 | return VO_ERROR; | | |
| 279 | p->solid_buffer_pool = wl_shm_create_pool(wl->shm, fd, height * stride); | | |
| 280 | if (!p->solid_buffer_pool) | | |
| 281 | return VO_ERROR; | | |
| 282 | p->solid_buffer = wl_shm_pool_create_buffer(p->solid_buffer_pool, 0, | | |
| 283 | width, height, stride, | | |
| 284 | WL_SHM_FORMAT_XRGB8888); | | |
| 285 | if (!p->solid_buffer) | | |
| 286 | return VO_ERROR; | | |
| 287 | wl_surface_attach(wl->surface, p->solid_buffer, 0, 0); | | |
| 288 | } | | |
| 289 | | | |
| 290 | if (!vo_wayland_reconfig(vo)) | | |
| 291 | return VO_ERROR; | | |
| 292 | | | |
| 293 | return 0; | | |
| 294 | } | | |
| 295 | | | |
| 296 | static int resize(struct vo *vo) | | |
| 297 | { | | |
| 298 | struct vo_wayland_state *wl = vo->wl; | | |
| 299 | struct priv *p = vo->priv; | | |
| 300 | | | |
| 301 | vo_wayland_set_opaque_region(wl, 0); | | |
| 302 | const int width = wl->scaling * mp_rect_w(wl->geometry); | | |
| 303 | const int height = wl->scaling * mp_rect_h(wl->geometry); | | |
| 304 | vo->dwidth = width; | | |
| 305 | vo->dheight = height; | | |
| 306 | vo_get_src_dst_rects(vo, &p->src, &p->dst, &p->osd); | | |
| 307 | | | |
| 308 | wp_viewport_set_destination(wl->viewport, | | |
| 309 | (p->dst.x0 << 1) + mp_rect_w(p->dst), | | |
| 310 | (p->dst.y0 << 1) + mp_rect_h(p->dst)); | | |
| 311 | wp_viewport_set_destination(wl->video_viewport, mp_rect_w(p->dst), | | |
| 312 | mp_rect_h(p->dst)); | | |
| 313 | wl_subsurface_set_position(wl->video_subsurface, p->dst.x0, p->dst.y0); | | |
| 314 | | | |
| 315 | vo->want_redraw = true; | | |
| 316 | | | |
| 317 | return VO_TRUE; | | |
| 318 | } | | |
| 319 | | | |
| 320 | static int control(struct vo *vo, uint32_t request, void *data) | | |
| 321 | { | | |
| 322 | struct priv *p = vo->priv; | | |
| 323 | int events = 0; | | |
| 324 | int ret; | | |
| 325 | | | |
| 326 | switch (request) { | | |
| 327 | /* need to clean pool after seek to avoid artifacts */ | | |
| 328 | case VOCTRL_RESET: | | |
| 329 | va_pool_clean(p->va_pool); | | |
| 330 | break; | | |
| 331 | default: | | |
| 332 | break; | | |
| 333 | } | | |
| 334 | ret = vo_wayland_control(vo, &events, request, data); | | |
| 335 | if (events & VO_EVENT_RESIZE) | | |
| 336 | ret = resize(vo); | | |
| 337 | if (events & VO_EVENT_EXPOSE) | | |
| 338 | vo->want_redraw = true; | | |
| 339 | vo_event(vo, events); | | |
| 340 | | | |
| 341 | return ret; | | |
| 342 | } | | |
| 343 | | | |
| 344 | static void draw_frame(struct vo *vo, struct vo_frame *frame) | | |
| 345 | { | | |
| 346 | struct priv *p = vo->priv; | | |
| 347 | struct vo_wayland_state *wl = vo->wl; | | |
| 348 | | | |
| 349 | if (!vo_wayland_check_visible(vo)) | | |
| 350 | return; | | |
| 351 | | | |
| 352 | struct va_pool_entry *entry = va_pool_alloc_entry(vo, p->va_pool, | | |
| 353 | frame->current); | | |
| 354 | if (!entry) | | |
| 355 | return; | | |
| 356 | | | |
| 357 | wl_surface_attach(wl->video_surface, entry->buffer, 0, 0); | | |
| 358 | wl_surface_damage_buffer(wl->video_surface, 0, 0, INT32_MAX, INT32_MAX); | | |
| 359 | wl_surface_commit(wl->video_surface); | | |
| 360 | wl_surface_commit(wl->surface); | | |
| 361 | | | |
| 362 | if (!wl->opts->disable_vsync) | | |
| 363 | vo_wayland_wait_frame(wl); | | |
| 364 | if (wl->use_present) | | |
| 365 | present_sync_swap(wl->present); | | |
| 366 | } | | |
| 367 | static void flip_page(struct vo *vo) | | |
| 368 | { | | |
| 369 | /* no-op */ | | |
| 370 | } | | |
| 371 | | | |
| 372 | static void get_vsync(struct vo *vo, struct vo_vsync_info *info) | | |
| 373 | { | | |
| 374 | struct vo_wayland_state *wl = vo->wl; | | |
| 375 | if (wl->use_present) | | |
| 376 | present_sync_get_info(wl->present, info); | | |
| 377 | } | | |
| 378 | | | |
| 379 | const struct vo_driver video_out_vaapi_wayland = { | | |
| 380 | .description = "VA API with Wayland video output", | | |
| 381 | .name = "vaapi-wayland", | | |
| 382 | .preinit = preinit, | | |
| 383 | .query_format = query_format, | | |
| 384 | .reconfig = reconfig, | | |
| 385 | .control = control, | | |
| 386 | .draw_frame = draw_frame, | | |
| 387 | .flip_page = flip_page, | | |
| 388 | .get_vsync = get_vsync, | | |
| 389 | .wakeup = vo_wayland_wakeup, | | |
| 390 | .wait_events = vo_wayland_wait_events, | | |
| 391 | .uninit = uninit, | | |
| 392 | .priv_size = sizeof(struct priv) | | |
| 393 | }; | | |