/* * Copyright (C) 2012 Intel Corporation. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sub license, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include #include #include #include #include "intel_driver.h" #include "i965_output_wayland.h" #include "i965_drv_video.h" #include "i965_defines.h" #include "dso_utils.h" /* We need mesa's libEGL, first try the soname of a glvnd enabled mesa build */ #define LIBEGL_NAME "libEGL_mesa.so.0" /* Then fallback to plain libEGL.so.1 (which might not be mesa) */ #define LIBEGL_NAME_FALLBACK "libEGL.so.1" #define LIBWAYLAND_CLIENT_NAME "libwayland-client.so.0" typedef uint32_t (*wl_display_get_global_func)(struct wl_display *display, const char *interface, uint32_t version); typedef struct wl_event_queue *(*wl_display_create_queue_func)(struct wl_display *display); typedef void (*wl_display_roundtrip_queue_func)(struct wl_display *display, struct wl_event_queue *queue); typedef void (*wl_event_queue_destroy_func)(struct wl_event_queue *queue); typedef void *(*wl_proxy_create_wrapper_func)(struct wl_proxy *proxy); typedef void(*wl_proxy_wrapper_destroy_func)(void *proxy); typedef struct wl_proxy *(*wl_proxy_create_func)(struct wl_proxy *factory, const struct wl_interface *interface); typedef void (*wl_proxy_destroy_func)(struct wl_proxy *proxy); typedef void (*wl_proxy_marshal_func)(struct wl_proxy *p, uint32_t opcode, ...); typedef int (*wl_proxy_add_listener_func)(struct wl_proxy *proxy, void (**implementation)(void), void *data); typedef void (*wl_proxy_set_queue_func)(struct wl_proxy *proxy, struct wl_event_queue *queue); struct wl_vtable { const struct wl_interface *buffer_interface; const struct wl_interface *drm_interface; const struct wl_interface *registry_interface; wl_display_create_queue_func display_create_queue; wl_display_roundtrip_queue_func display_roundtrip_queue; wl_event_queue_destroy_func event_queue_destroy; wl_proxy_create_wrapper_func proxy_create_wrapper; wl_proxy_wrapper_destroy_func proxy_wrapper_destroy; wl_proxy_create_func proxy_create; wl_proxy_destroy_func proxy_destroy; wl_proxy_marshal_func proxy_marshal; wl_proxy_add_listener_func proxy_add_listener; wl_proxy_set_queue_func proxy_set_queue; }; struct va_wl_output { struct dso_handle *libegl_handle; struct dso_handle *libwl_client_handle; struct wl_vtable vtable; struct wl_event_queue *queue; struct wl_drm *wl_drm; uint32_t wl_drm_name; struct wl_registry *wl_registry; }; /* These function are copied and adapted from the version inside * wayland-client-protocol.h */ static void * registry_bind( struct wl_vtable *wl_vtable, struct wl_registry *wl_registry, uint32_t name, const struct wl_interface *interface, uint32_t version ) { struct wl_proxy *id; id = wl_vtable->proxy_create((struct wl_proxy *) wl_registry, interface); if (!id) return NULL; wl_vtable->proxy_marshal((struct wl_proxy *) wl_registry, WL_REGISTRY_BIND, name, interface->name, version, id); return (void *) id; } static struct wl_registry * display_get_registry( struct wl_vtable *wl_vtable, struct wl_display *wl_display ) { struct wl_proxy *callback; callback = wl_vtable->proxy_create((struct wl_proxy *) wl_display, wl_vtable->registry_interface); if (!callback) return NULL; wl_vtable->proxy_marshal((struct wl_proxy *) wl_display, WL_DISPLAY_GET_REGISTRY, callback); return (struct wl_registry *) callback; } static int registry_add_listener( struct wl_vtable *wl_vtable, struct wl_registry *wl_registry, const struct wl_registry_listener *listener, void *data ) { return wl_vtable->proxy_add_listener((struct wl_proxy *) wl_registry, (void (**)(void)) listener, data); } static void registry_handle_global( void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version ) { VADriverContextP ctx = data; struct i965_driver_data * const i965 = i965_driver_data(ctx); struct va_wl_output * const wl_output = i965->wl_output; struct wl_vtable * const wl_vtable = &wl_output->vtable; if (strcmp(interface, "wl_drm") == 0) { wl_output->wl_drm_name = name; wl_output->wl_drm = registry_bind(wl_vtable, wl_output->wl_registry, name, wl_vtable->drm_interface, (version < 2) ? version : 2); } } static void registry_handle_global_remove( void *data, struct wl_registry *registry, uint32_t name ) { VADriverContextP ctx = data; struct i965_driver_data * const i965 = i965_driver_data(ctx); struct va_wl_output * const wl_output = i965->wl_output; if (wl_output->wl_drm && name == wl_output->wl_drm_name) { wl_output->vtable.proxy_destroy((struct wl_proxy *)wl_output->wl_drm); wl_output->wl_drm = NULL; } } static const struct wl_registry_listener registry_listener = { registry_handle_global, registry_handle_global_remove }; /* Ensure wl_drm instance is created */ static bool ensure_wl_output(VADriverContextP ctx) { struct i965_driver_data * const i965 = i965_driver_data(ctx); struct va_wl_output * const wl_output = i965->wl_output; struct wl_vtable * const wl_vtable = &wl_output->vtable; struct wl_display *display_wrapper; if (wl_output->wl_drm) return true; if (wl_output->queue) { wl_output->vtable.event_queue_destroy(wl_output->queue); wl_output->queue = NULL; } wl_output->queue = wl_vtable->display_create_queue(ctx->native_dpy); if (!wl_output->queue) return false; display_wrapper = wl_vtable->proxy_create_wrapper(ctx->native_dpy); if (!display_wrapper) return false; wl_vtable->proxy_set_queue((struct wl_proxy *) display_wrapper, wl_output->queue); if (wl_output->wl_registry) { wl_output->vtable.proxy_destroy((struct wl_proxy *)wl_output->wl_registry); wl_output->wl_registry = NULL; } wl_output->wl_registry = display_get_registry(wl_vtable, display_wrapper); wl_vtable->proxy_wrapper_destroy(display_wrapper); registry_add_listener(wl_vtable, wl_output->wl_registry, ®istry_listener, ctx); wl_vtable->display_roundtrip_queue(ctx->native_dpy, wl_output->queue); if (!wl_output->wl_drm) return false; return true; } /* Create planar/prime YUV buffer * Create a prime buffer if fd is not -1, otherwise a * planar buffer */ static struct wl_buffer * create_prime_or_planar_buffer( struct va_wl_output *wl_output, uint32_t name, int fd, int32_t width, int32_t height, uint32_t format, int32_t offsets[3], int32_t pitches[3] ) { struct wl_vtable * const wl_vtable = &wl_output->vtable; struct wl_proxy *id; id = wl_vtable->proxy_create( (struct wl_proxy *)wl_output->wl_drm, wl_vtable->buffer_interface ); if (!id) return NULL; wl_vtable->proxy_marshal( (struct wl_proxy *)wl_output->wl_drm, (fd != -1) ? WL_DRM_CREATE_PRIME_BUFFER : WL_DRM_CREATE_PLANAR_BUFFER, id, (fd != -1) ? fd : name, width, height, format, offsets[0], pitches[0], offsets[1], pitches[1], offsets[2], pitches[2] ); return (struct wl_buffer *)id; } /* Hook to return Wayland buffer associated with the VA surface */ static VAStatus va_GetSurfaceBufferWl( struct VADriverContext *ctx, VASurfaceID surface, unsigned int flags, struct wl_buffer **out_buffer ) { struct VADriverVTableWayland * const vtable = ctx->vtable_wayland; struct i965_driver_data * const i965 = i965_driver_data(ctx); struct object_surface *obj_surface; struct wl_buffer *buffer; uint32_t name, drm_format; int offsets[3], pitches[3]; int fd = -1; obj_surface = SURFACE(surface); if (!obj_surface) return VA_STATUS_ERROR_INVALID_SURFACE; if (flags != VA_FRAME_PICTURE) return VA_STATUS_ERROR_FLAG_NOT_SUPPORTED; if (!out_buffer) return VA_STATUS_ERROR_INVALID_PARAMETER; if (!ensure_wl_output(ctx)) return VA_STATUS_ERROR_INVALID_DISPLAY; if (!vtable->has_prime_sharing || (drm_intel_bo_gem_export_to_prime(obj_surface->bo, &fd) != 0)) { fd = -1; if (drm_intel_bo_flink(obj_surface->bo, &name) != 0) return VA_STATUS_ERROR_INVALID_SURFACE; } switch (obj_surface->fourcc) { case VA_FOURCC_NV12: drm_format = WL_DRM_FORMAT_NV12; offsets[0] = 0; pitches[0] = obj_surface->width; offsets[1] = obj_surface->width * obj_surface->y_cb_offset; pitches[1] = obj_surface->cb_cr_pitch; offsets[2] = 0; pitches[2] = 0; break; case VA_FOURCC_YV12: case VA_FOURCC_I420: case VA_FOURCC_IMC1: case VA_FOURCC_IMC3: case VA_FOURCC_422H: case VA_FOURCC_422V: case VA_FOURCC_411P: case VA_FOURCC_444P: switch (obj_surface->subsampling) { case SUBSAMPLE_YUV411: drm_format = WL_DRM_FORMAT_YUV411; break; case SUBSAMPLE_YUV420: drm_format = WL_DRM_FORMAT_YUV420; break; case SUBSAMPLE_YUV422H: case SUBSAMPLE_YUV422V: drm_format = WL_DRM_FORMAT_YUV422; break; case SUBSAMPLE_YUV444: drm_format = WL_DRM_FORMAT_YUV444; break; default: if (fd != -1) close(fd); return VA_STATUS_ERROR_INVALID_IMAGE_FORMAT; } offsets[0] = 0; pitches[0] = obj_surface->width; offsets[1] = obj_surface->width * obj_surface->y_cb_offset; pitches[1] = obj_surface->cb_cr_pitch; offsets[2] = obj_surface->width * obj_surface->y_cr_offset; pitches[2] = obj_surface->cb_cr_pitch; break; default: if (fd != -1) close(fd); return VA_STATUS_ERROR_INVALID_IMAGE_FORMAT; } buffer = create_prime_or_planar_buffer( i965->wl_output, name, fd, obj_surface->orig_width, obj_surface->orig_height, drm_format, offsets, pitches ); if (fd != -1) close(fd); if (!buffer) return VA_STATUS_ERROR_ALLOCATION_FAILED; *out_buffer = buffer; return VA_STATUS_SUCCESS; } /* Hook to return Wayland buffer associated with the VA image */ static VAStatus va_GetImageBufferWl( struct VADriverContext *ctx, VAImageID image, unsigned int flags, struct wl_buffer **out_buffer ) { return VA_STATUS_ERROR_UNIMPLEMENTED; } bool ensure_driver_vtable(VADriverContextP ctx) { struct VADriverVTableWayland * const vtable = ctx->vtable_wayland; if (!vtable) return false; vtable->vaGetSurfaceBufferWl = va_GetSurfaceBufferWl; vtable->vaGetImageBufferWl = va_GetImageBufferWl; return true; } bool i965_output_wayland_init(VADriverContextP ctx) { struct i965_driver_data * const i965 = i965_driver_data(ctx); struct dso_handle *dso_handle; struct wl_vtable *wl_vtable; struct VADriverVTableWayland * const vtable = ctx->vtable_wayland; static const struct dso_symbol libegl_symbols[] = { { "wl_drm_interface", offsetof(struct wl_vtable, drm_interface) }, { NULL, } }; static const struct dso_symbol libwl_client_symbols[] = { { "wl_buffer_interface", offsetof(struct wl_vtable, buffer_interface) }, { "wl_registry_interface", offsetof(struct wl_vtable, registry_interface) }, { "wl_display_create_queue", offsetof(struct wl_vtable, display_create_queue) }, { "wl_display_roundtrip_queue", offsetof(struct wl_vtable, display_roundtrip_queue) }, { "wl_event_queue_destroy", offsetof(struct wl_vtable, event_queue_destroy) }, { "wl_proxy_create_wrapper", offsetof(struct wl_vtable, proxy_create_wrapper) }, { "wl_proxy_wrapper_destroy", offsetof(struct wl_vtable, proxy_wrapper_destroy) }, { "wl_proxy_create", offsetof(struct wl_vtable, proxy_create) }, { "wl_proxy_destroy", offsetof(struct wl_vtable, proxy_destroy) }, { "wl_proxy_marshal", offsetof(struct wl_vtable, proxy_marshal) }, { "wl_proxy_add_listener", offsetof(struct wl_vtable, proxy_add_listener) }, { "wl_proxy_set_queue", offsetof(struct wl_vtable, proxy_set_queue) }, { NULL, } }; if (ctx->display_type != VA_DISPLAY_WAYLAND) return false; i965->wl_output = calloc(1, sizeof(struct va_wl_output)); if (!i965->wl_output) goto error; wl_vtable = &i965->wl_output->vtable; if (vtable->wl_interface) wl_vtable->drm_interface = vtable->wl_interface; else { i965->wl_output->libegl_handle = dso_open(LIBEGL_NAME); if (!i965->wl_output->libegl_handle) { i965->wl_output->libegl_handle = dso_open(LIBEGL_NAME_FALLBACK); if (!i965->wl_output->libegl_handle) goto error; } dso_handle = i965->wl_output->libegl_handle; if (!dso_get_symbols(dso_handle, wl_vtable, sizeof(*wl_vtable), libegl_symbols)) goto error; } i965->wl_output->libwl_client_handle = dso_open(LIBWAYLAND_CLIENT_NAME); if (!i965->wl_output->libwl_client_handle) goto error; dso_handle = i965->wl_output->libwl_client_handle; if (!dso_get_symbols(dso_handle, wl_vtable, sizeof(*wl_vtable), libwl_client_symbols)) goto error; if (!ensure_driver_vtable(ctx)) goto error; return true; error: i965_output_wayland_terminate(ctx); return false; } void i965_output_wayland_terminate(VADriverContextP ctx) { struct i965_driver_data * const i965 = i965_driver_data(ctx); struct va_wl_output *wl_output; if (ctx->display_type != VA_DISPLAY_WAYLAND) return; wl_output = i965->wl_output; if (!wl_output) return; if (wl_output->wl_drm) { wl_output->vtable.proxy_destroy((struct wl_proxy *)wl_output->wl_drm); wl_output->wl_drm = NULL; } if (wl_output->wl_registry) { wl_output->vtable.proxy_destroy((struct wl_proxy *)wl_output->wl_registry); wl_output->wl_registry = NULL; } if (wl_output->queue) { wl_output->vtable.event_queue_destroy(wl_output->queue); wl_output->queue = NULL; } if (wl_output->libegl_handle) { dso_close(wl_output->libegl_handle); wl_output->libegl_handle = NULL; } if (wl_output->libwl_client_handle) { dso_close(wl_output->libwl_client_handle); wl_output->libwl_client_handle = NULL; } free(wl_output); i965->wl_output = NULL; }