blob: 46b3aa1e8f870ecdff3226dd4dd9bad0cd4b619e [file] [log] [blame]
// 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 <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include <wayland-client.h>
#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(
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));
// In the case of mmaps created from the client buffer, we want to be able
// to close the FD when the client releases the shm pool (i.e. when it's
// done transferring) as opposed to when the pool is freed (i.e. when we're
// done drawing).
// We do this by removing the handle to the FD after it has been mmapped,
// which prevents a double-close.
host_buffer->shm_mmap->fd = -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);
break;
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);
}