// Copyright 2018 The Chromium OS Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "sommelier.h" #include #include #include #include #include "drm-server-protocol.h" #include "linux-dmabuf-unstable-v1-client-protocol.h" struct sl_host_shm_pool { struct sl_shm* shm; struct wl_resource* resource; struct wl_shm_pool* proxy; int fd; }; struct sl_host_shm { struct sl_shm* shm; struct wl_resource* resource; struct wl_shm* shm_proxy; struct zwp_linux_dmabuf_v1* linux_dmabuf_proxy; }; size_t sl_shm_bpp_for_shm_format(uint32_t format) { switch (format) { case WL_SHM_FORMAT_NV12: return 1; case WL_SHM_FORMAT_RGB565: return 2; case WL_SHM_FORMAT_ARGB8888: case WL_SHM_FORMAT_ABGR8888: case WL_SHM_FORMAT_XRGB8888: case WL_SHM_FORMAT_XBGR8888: return 4; } assert(0); return 0; } size_t sl_shm_num_planes_for_shm_format(uint32_t format) { switch (format) { case WL_SHM_FORMAT_NV12: return 2; case WL_SHM_FORMAT_RGB565: case WL_SHM_FORMAT_ARGB8888: case WL_SHM_FORMAT_ABGR8888: case WL_SHM_FORMAT_XRGB8888: case WL_SHM_FORMAT_XBGR8888: return 1; } assert(0); return 0; } static size_t sl_y_subsampling_for_shm_format_plane(uint32_t format, size_t plane) { switch (format) { case WL_SHM_FORMAT_NV12: { const size_t subsampling[] = {1, 2}; assert(plane < ARRAY_SIZE(subsampling)); return subsampling[plane]; } case WL_SHM_FORMAT_RGB565: case WL_SHM_FORMAT_ARGB8888: case WL_SHM_FORMAT_ABGR8888: case WL_SHM_FORMAT_XRGB8888: case WL_SHM_FORMAT_XBGR8888: return 1; } assert(0); return 0; } static int sl_offset_for_shm_format_plane(uint32_t format, size_t height, size_t stride, size_t plane) { switch (format) { case WL_SHM_FORMAT_NV12: { const size_t offset[] = {0, 1}; assert(plane < ARRAY_SIZE(offset)); return offset[plane] * height * stride; } case WL_SHM_FORMAT_RGB565: case WL_SHM_FORMAT_ARGB8888: case WL_SHM_FORMAT_ABGR8888: case WL_SHM_FORMAT_XRGB8888: case WL_SHM_FORMAT_XBGR8888: return 0; } assert(0); return 0; } static size_t sl_size_for_shm_format_plane(uint32_t format, size_t height, size_t stride, size_t plane) { return height / sl_y_subsampling_for_shm_format_plane(format, plane) * stride; } static size_t sl_size_for_shm_format(uint32_t format, size_t height, size_t stride) { size_t i, num_planes = sl_shm_num_planes_for_shm_format(format); size_t total_size = 0; for (i = 0; i < num_planes; ++i) { size_t size = sl_size_for_shm_format_plane(format, height, stride, i); size_t offset = sl_offset_for_shm_format_plane(format, height, stride, i); total_size = MAX(total_size, size + offset); } return total_size; } static void sl_host_shm_pool_create_host_buffer(struct wl_client* client, struct wl_resource* resource, uint32_t id, int32_t offset, int32_t width, int32_t height, int32_t stride, uint32_t format) { struct sl_host_shm_pool* host = wl_resource_get_user_data(resource); if (host->shm->ctx->shm_driver == SHM_DRIVER_NOOP) { assert(host->proxy); sl_create_host_buffer(client, id, wl_shm_pool_create_buffer(host->proxy, offset, width, height, stride, format), width, height); } else { struct sl_host_buffer* host_buffer = sl_create_host_buffer(client, id, NULL, width, height); host_buffer->shm_format = format; host_buffer->shm_mmap = sl_mmap_create( dup(host->fd), sl_size_for_shm_format(format, height, stride), sl_shm_bpp_for_shm_format(format), sl_shm_num_planes_for_shm_format(format), offset, stride, offset + sl_offset_for_shm_format_plane(format, height, stride, 1), stride, sl_y_subsampling_for_shm_format_plane(format, 0), sl_y_subsampling_for_shm_format_plane(format, 1)); host_buffer->shm_mmap->buffer_resource = host_buffer->resource; } } static void sl_host_shm_pool_destroy(struct wl_client* client, struct wl_resource* resource) { wl_resource_destroy(resource); } static void sl_host_shm_pool_resize(struct wl_client* client, struct wl_resource* resource, int32_t size) { struct sl_host_shm_pool* host = wl_resource_get_user_data(resource); if (host->proxy) wl_shm_pool_resize(host->proxy, size); } static const struct wl_shm_pool_interface sl_shm_pool_implementation = { sl_host_shm_pool_create_host_buffer, sl_host_shm_pool_destroy, sl_host_shm_pool_resize}; static void sl_destroy_host_shm_pool(struct wl_resource* resource) { struct sl_host_shm_pool* host = wl_resource_get_user_data(resource); if (host->fd >= 0) close(host->fd); if (host->proxy) wl_shm_pool_destroy(host->proxy); wl_resource_set_user_data(resource, NULL); free(host); } static void sl_shm_create_host_pool(struct wl_client* client, struct wl_resource* resource, uint32_t id, int fd, int32_t size) { struct sl_host_shm* host = wl_resource_get_user_data(resource); struct sl_host_shm_pool* host_shm_pool; host_shm_pool = malloc(sizeof(*host_shm_pool)); assert(host_shm_pool); host_shm_pool->shm = host->shm; host_shm_pool->fd = -1; host_shm_pool->proxy = NULL; host_shm_pool->resource = wl_resource_create(client, &wl_shm_pool_interface, 1, id); wl_resource_set_implementation(host_shm_pool->resource, &sl_shm_pool_implementation, host_shm_pool, sl_destroy_host_shm_pool); switch (host->shm->ctx->shm_driver) { case SHM_DRIVER_NOOP: host_shm_pool->proxy = wl_shm_create_pool(host->shm_proxy, fd, size); wl_shm_pool_set_user_data(host_shm_pool->proxy, host_shm_pool); close(fd); break; case SHM_DRIVER_DMABUF: case SHM_DRIVER_VIRTWL: case SHM_DRIVER_VIRTWL_DMABUF: host_shm_pool->fd = fd; break; } } static const struct wl_shm_interface sl_shm_implementation = { sl_shm_create_host_pool}; static void sl_shm_format(void* data, struct wl_shm* shm, uint32_t format) { struct sl_host_shm* host = wl_shm_get_user_data(shm); switch (format) { case WL_SHM_FORMAT_RGB565: case WL_SHM_FORMAT_ARGB8888: case WL_SHM_FORMAT_ABGR8888: case WL_SHM_FORMAT_XRGB8888: case WL_SHM_FORMAT_XBGR8888: wl_shm_send_format(host->resource, format); default: break; } } static const struct wl_shm_listener sl_shm_listener = {sl_shm_format}; static void sl_drm_format(void* data, struct zwp_linux_dmabuf_v1* linux_dmabuf, uint32_t format) { struct sl_host_shm* host = zwp_linux_dmabuf_v1_get_user_data(linux_dmabuf); // Forward SHM versions of supported formats. switch (format) { case WL_DRM_FORMAT_NV12: wl_shm_send_format(host->resource, WL_SHM_FORMAT_NV12); break; case WL_DRM_FORMAT_RGB565: wl_shm_send_format(host->resource, WL_SHM_FORMAT_RGB565); break; case WL_DRM_FORMAT_ARGB8888: wl_shm_send_format(host->resource, WL_SHM_FORMAT_ARGB8888); break; case WL_DRM_FORMAT_ABGR8888: wl_shm_send_format(host->resource, WL_SHM_FORMAT_ABGR8888); break; case WL_DRM_FORMAT_XRGB8888: wl_shm_send_format(host->resource, WL_SHM_FORMAT_XRGB8888); break; case WL_DRM_FORMAT_XBGR8888: wl_shm_send_format(host->resource, WL_SHM_FORMAT_XBGR8888); break; } } static void sl_drm_modifier(void* data, struct zwp_linux_dmabuf_v1* linux_dmabuf, uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo) {} static const struct zwp_linux_dmabuf_v1_listener sl_linux_dmabuf_listener = { sl_drm_format, sl_drm_modifier}; static void sl_destroy_host_shm(struct wl_resource* resource) { struct sl_host_shm* host = wl_resource_get_user_data(resource); if (host->shm_proxy) wl_shm_destroy(host->shm_proxy); if (host->linux_dmabuf_proxy) zwp_linux_dmabuf_v1_destroy(host->linux_dmabuf_proxy); wl_resource_set_user_data(resource, NULL); free(host); } static void sl_bind_host_shm(struct wl_client* client, void* data, uint32_t version, uint32_t id) { struct sl_context* ctx = (struct sl_context*)data; struct sl_host_shm* host; host = malloc(sizeof(*host)); assert(host); host->shm = ctx->shm; host->shm_proxy = NULL; host->linux_dmabuf_proxy = NULL; host->resource = wl_resource_create(client, &wl_shm_interface, 1, id); wl_resource_set_implementation(host->resource, &sl_shm_implementation, host, sl_destroy_host_shm); switch (ctx->shm_driver) { case SHM_DRIVER_NOOP: case SHM_DRIVER_VIRTWL: host->shm_proxy = wl_registry_bind( wl_display_get_registry(ctx->display), ctx->shm->id, &wl_shm_interface, wl_resource_get_version(host->resource)); wl_shm_set_user_data(host->shm_proxy, host); wl_shm_add_listener(host->shm_proxy, &sl_shm_listener, host); break; case SHM_DRIVER_VIRTWL_DMABUF: case SHM_DRIVER_DMABUF: assert(ctx->linux_dmabuf); host->linux_dmabuf_proxy = wl_registry_bind( wl_display_get_registry(ctx->display), ctx->linux_dmabuf->id, &zwp_linux_dmabuf_v1_interface, wl_resource_get_version(host->resource)); zwp_linux_dmabuf_v1_set_user_data(host->linux_dmabuf_proxy, host); zwp_linux_dmabuf_v1_add_listener(host->linux_dmabuf_proxy, &sl_linux_dmabuf_listener, host); break; } } struct sl_global* sl_shm_global_create(struct sl_context* ctx) { return sl_global_create(ctx, &wl_shm_interface, 1, ctx, sl_bind_host_shm); }