Skip to content
Commits on Source (30)
......@@ -8,7 +8,7 @@ project(
'warning_level=3',
'werror=true',
],
version: '0.7.1',
version: '0.7.2',
)
# DEFAULT_SOURCE implies POSIX_C_SOURCE 200809L + extras like CMSG_LEN
......@@ -62,7 +62,7 @@ rt = cc.find_library('rt')
# XXX dtrace -G (Solaris, FreeBSD, NetBSD) isn't supported yet
is_linux = host_machine.system() == 'linux'
is_darwin = host_machine.system() == 'darwin'
if (is_linux or is_darwin) and cc.has_header('sys/sdt.h')
if (is_linux or is_darwin) and get_option('with_systemtap') and cc.has_header('sys/sdt.h')
config_data.set('HAS_USDT', 1, description: 'Enable static trace probes')
endif
liblz4 = dependency('liblz4', version: '>=1.7.0', required: get_option('with_lz4'))
......
......@@ -4,3 +4,4 @@ option('with_dmabuf', type : 'feature', value : 'auto', description : 'Support D
option('with_lz4', type : 'feature', value : 'auto', description : 'Support LZ4 as a compression mechanism')
option('with_zstd', type : 'feature', value : 'auto', description : 'Support ZStandard as a compression mechanism')
option('with_vaapi', type : 'feature', value : 'auto', description : 'Link with libva and use VAAPI to perform hardware video output color space conversions on GPU')
option('with_systemtap', type: 'boolean', value: true, description: 'Enable tracing using sdt and provide static tracepoints for profiling')
......@@ -31,14 +31,14 @@ foreach xml : protocols
'@0@ code'.format(xml.underscorify()),
output: '@BASENAME@-data.c',
input: xml,
depend_files: fn_list,
depend_files: [fn_list, symgen_path],
command: [python3, symgen_path, 'data', fn_list, '@INPUT@', '@OUTPUT@'],
)
protocols_headers += custom_target(
'@0@ client-header'.format(xml.underscorify()),
output: '@BASENAME@-defs.h',
input: xml,
depend_files: fn_list,
depend_files: [fn_list, symgen_path],
command: [python3, symgen_path, 'header', fn_list, '@INPUT@', '@OUTPUT@'],
)
endforeach
......
......@@ -238,6 +238,10 @@ def write_func(is_header, ostream, iface_name, func, is_request, export_list):
msg_data_args.append("0")
msg_data_args.append("NULL")
msg_data_args.append("NULL")
if "type" in func.attrib and func.attrib["type"] == "destructor":
msg_data_args.append("true")
else:
msg_data_args.append("false")
return (is_request, func_name, func.attrib["name"], msg_data_args)
......
......@@ -16,6 +16,7 @@ struct msg_data {
const int new_vec_len;
const unsigned int *new_obj_idxs;/* there are special 'spacer' fields at each string/arr, =-1*/
const struct wp_interface **new_obj_types;
const bool is_destructor;
};
struct wp_interface {
const char *name;
......
......@@ -183,16 +183,16 @@ static int run_single_client_reconnector(
if (send_one_fd(linkfd, newclient) == -1) {
wp_error("Failed to get connection id");
retcode = EXIT_FAILURE;
close(newclient);
checked_close(newclient);
break;
}
done:
close(newclient);
checked_close(newclient);
}
}
}
close(channelsock);
close(linkfd);
checked_close(channelsock);
checked_close(linkfd);
return retcode;
}
......@@ -262,7 +262,7 @@ static int run_single_client(int channelsock, pid_t *eol_pid,
break;
fail_cc:
retcode = EXIT_FAILURE;
close(chanclient);
checked_close(chanclient);
chanclient = -1;
break;
}
......@@ -282,69 +282,55 @@ static int run_single_client(int channelsock, pid_t *eol_pid,
if (socketpair(AF_UNIX, SOCK_STREAM, 0, linkfds) == -1) {
wp_error("Failed to create socketpair: %s",
strerror(errno));
close(chanclient);
checked_close(chanclient);
return EXIT_FAILURE;
}
pid_t reco_pid = fork();
if (reco_pid == -1) {
wp_debug("Fork failure");
close(chanclient);
checked_close(chanclient);
return EXIT_FAILURE;
} else if (reco_pid == 0) {
if (linkfds[0] != -1) {
close(linkfds[0]);
checked_close(linkfds[0]);
}
close(chanclient);
close(disp_fd);
checked_close(chanclient);
checked_close(disp_fd);
int rc = run_single_client_reconnector(
channelsock, linkfds[1], conn_id);
exit(rc);
}
close(linkfds[1]);
checked_close(linkfds[1]);
}
close(channelsock);
checked_close(channelsock);
return main_interface_loop(
chanclient, disp_fd, linkfds[0], config, true);
}
static void handle_new_client_connection(int channelsock, int chanclient,
struct conn_map *connmap, const struct main_config *config,
const char disp_path[static MAX_SOCKETPATH_LEN])
void send_new_connection_fd(
struct conn_map *connmap, uint32_t key[static 3], int new_fd)
{
struct connection_token conn_id;
if (read(chanclient, &conn_id.header, sizeof(conn_id.header)) !=
sizeof(conn_id.header)) {
wp_error("Failed to get connection id header");
goto fail_cc;
}
if (check_conn_header(conn_id.header) < 0) {
goto fail_cc;
}
if (read(chanclient, &conn_id.key, sizeof(conn_id.key)) !=
sizeof(conn_id.key)) {
wp_error("Failed to get connection id key");
goto fail_cc;
}
if (conn_id.header & CONN_UPDATE_BIT) {
for (int i = 0; i < connmap->count; i++) {
if (key_match(connmap->data[i].token.key,
conn_id.key)) {
if (send_one_fd(connmap->data[i].linkfd,
chanclient) == -1) {
wp_error("Failed to send new connection fd to subprocess: %s",
strerror(errno));
goto fail_cc;
}
break;
for (int i = 0; i < connmap->count; i++) {
if (key_match(connmap->data[i].token.key, key)) {
if (send_one_fd(connmap->data[i].linkfd, new_fd) ==
-1) {
wp_error("Failed to send new connection fd to subprocess: %s",
strerror(errno));
}
break;
}
close(chanclient);
return;
}
bool reconnectable = conn_id.header & CONN_RECONNECTABLE_BIT;
}
static void handle_new_client_connection(struct pollfd *other_fds,
int n_other_fds, int chanclient, struct conn_map *connmap,
const struct main_config *config,
const char disp_path[static MAX_SOCKETPATH_LEN],
const struct connection_token *conn_id)
{
bool reconnectable = conn_id->header & CONN_RECONNECTABLE_BIT;
if (reconnectable && buf_ensure_size(connmap->count + 1,
sizeof(struct conn_addr),
......@@ -365,45 +351,67 @@ static void handle_new_client_connection(int channelsock, int chanclient,
if (npid == 0) {
// Run forked process, with the only shared
// state being the new channel socket
close(channelsock);
for (int i = 0; i < n_other_fds; i++) {
if (other_fds[i].fd != chanclient) {
checked_close(other_fds[i].fd);
}
}
if (reconnectable) {
close(linkfds[0]);
checked_close(linkfds[0]);
}
for (int i = 0; i < connmap->count; i++) {
close(connmap->data[i].linkfd);
checked_close(connmap->data[i].linkfd);
}
int dfd = connect_to_socket(disp_path);
if (dfd == -1) {
exit(EXIT_FAILURE);
}
// ignore retcode ?
main_interface_loop(chanclient, dfd, linkfds[1], config, true);
close(dfd);
exit(EXIT_SUCCESS);
int rc = main_interface_loop(
chanclient, dfd, linkfds[1], config, true);
check_unclosed_fds();
exit(rc);
} else if (npid == -1) {
wp_debug("Fork failure");
goto fail_ps;
}
// Remove connection from this process
close(chanclient);
if (reconnectable) {
close(linkfds[1]);
checked_close(linkfds[1]);
connmap->data[connmap->count++] =
(struct conn_addr){.linkfd = linkfds[0],
.token = conn_id,
.token = *conn_id,
.pid = npid};
}
return;
fail_ps:
close(linkfds[0]);
checked_close(linkfds[0]);
fail_cc:
close(chanclient);
checked_close(chanclient);
return;
}
#define NUM_INCOMPLETE_CONNECTIONS 63
static void drop_incoming_connection(struct pollfd *fds,
struct connection_token *tokens, uint8_t *bytes_read, int index,
int incomplete)
{
checked_close(fds[index].fd);
if (index != incomplete - 1) {
size_t shift = (size_t)(incomplete - 1 - index);
memmove(fds + index, fds + index + 1,
sizeof(struct pollfd) * shift);
memmove(tokens + index, tokens + index + 1,
sizeof(struct connection_token) * shift);
memmove(bytes_read + index, bytes_read + index + 1,
sizeof(uint8_t) * shift);
}
memset(&fds[incomplete - 1], 0, sizeof(struct pollfd));
memset(&tokens[incomplete - 1], 0, sizeof(struct connection_token));
bytes_read[incomplete - 1] = 0;
}
static int run_multi_client(int channelsock, pid_t *eol_pid,
const struct main_config *config,
......@@ -411,10 +419,20 @@ static int run_multi_client(int channelsock, pid_t *eol_pid,
{
struct conn_map connmap = {.data = NULL, .count = 0, .size = 0};
struct pollfd cs;
cs.fd = channelsock;
cs.events = POLLIN;
cs.revents = 0;
/* Keep track of the main socket, and all connections which have not
* yet fully provided their connection token. If we run out of space,
* the oldest incomplete connection gets dropped */
struct pollfd fds[NUM_INCOMPLETE_CONNECTIONS + 1];
struct connection_token tokens[NUM_INCOMPLETE_CONNECTIONS];
uint8_t bytes_read[NUM_INCOMPLETE_CONNECTIONS];
int incomplete = 0;
memset(fds, 0, sizeof(fds));
memset(tokens, 0, sizeof(tokens));
memset(bytes_read, 0, sizeof(bytes_read));
fds[0].fd = channelsock;
fds[0].events = POLLIN;
fds[0].revents = 0;
int retcode = EXIT_SUCCESS;
while (!shutdown_flag) {
int status = -1;
......@@ -426,7 +444,7 @@ static int run_multi_client(int channelsock, pid_t *eol_pid,
break;
}
int r = poll(&cs, 1, -1);
int r = poll(fds, 1 + (nfds_t)incomplete, -1);
if (r == -1) {
if (errno == EINTR) {
// If SIGCHLD, we will check the child.
......@@ -440,28 +458,113 @@ static int run_multi_client(int channelsock, pid_t *eol_pid,
continue;
}
int chanclient = accept(channelsock, NULL, NULL);
if (chanclient == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// The wakeup may have been spurious
for (int i = 0; i < incomplete; i++) {
if (!(fds[i + 1].revents & POLLIN)) {
continue;
}
wp_error("Connection failure: %s", strerror(errno));
retcode = EXIT_FAILURE;
break;
} else {
/* Failures here are logged, but should not affect this
* process' ability to e.g. handle reconnections. */
handle_new_client_connection(channelsock, chanclient,
&connmap, config, disp_path);
int cur_fd = fds[i + 1].fd;
char *dest = ((char *)&tokens[i]) + bytes_read[i];
ssize_t s = read(cur_fd, dest, 16 - bytes_read[i]);
if (s == -1) {
wp_error("Failed to read from connection: %s",
strerror(errno));
drop_incoming_connection(fds + 1, tokens,
bytes_read, i, incomplete);
incomplete--;
continue;
} else if (s == 0) {
/* connection closed */
wp_error("Connection closed early");
drop_incoming_connection(fds + 1, tokens,
bytes_read, i, incomplete);
incomplete--;
continue;
}
bytes_read[i] += (uint8_t)s;
if (bytes_read[i] - (uint8_t)s < 4 &&
bytes_read[i] >= 4) {
/* Validate connection token header */
if (check_conn_header(tokens[i].header) < 0) {
drop_incoming_connection(fds + 1,
tokens, bytes_read, i,
incomplete);
incomplete--;
continue;
}
}
if (bytes_read[i] < 16) {
continue;
}
/* Validate connection token key */
if (tokens[i].header & CONN_UPDATE_BIT) {
send_new_connection_fd(&connmap, tokens[i].key,
cur_fd);
drop_incoming_connection(fds + 1, tokens,
bytes_read, i, incomplete);
incomplete--;
continue;
}
/* Failures here are logged, but should not
* affect this process' ability to e.g. handle
* reconnections. */
handle_new_client_connection(fds, 1 + incomplete,
cur_fd, &connmap, config, disp_path,
&tokens[i]);
drop_incoming_connection(fds + 1, tokens, bytes_read, i,
incomplete);
incomplete--;
}
/* Process new connections second, to give incomplete
* connections a chance to clear first */
if (fds[0].revents & POLLIN) {
int chanclient = accept(channelsock, NULL, NULL);
if (chanclient == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
// The wakeup may have been spurious
continue;
}
// should errors like econnaborted exit?
wp_error("Connection failure: %s",
strerror(errno));
retcode = EXIT_FAILURE;
break;
} else {
if (set_nonblocking(chanclient) == -1) {
wp_error("Error making new connection nonblocking: %s",
strerror(errno));
checked_close(chanclient);
continue;
}
if (incomplete == NUM_INCOMPLETE_CONNECTIONS) {
wp_error("Dropping oldest incomplete connection (out of %d)",
NUM_INCOMPLETE_CONNECTIONS);
drop_incoming_connection(fds + 1,
tokens, bytes_read, 0,
incomplete);
incomplete--;
}
fds[1 + incomplete].fd = chanclient;
fds[1 + incomplete].events = POLLIN;
fds[1 + incomplete].revents = 0;
memset(&tokens[incomplete], 0,
sizeof(struct connection_token));
bytes_read[incomplete] = 0;
incomplete++;
}
}
}
for (int i = 0; i < incomplete; i++) {
checked_close(fds[i + 1].fd);
}
for (int i = 0; i < connmap.count; i++) {
close(connmap.data[i].linkfd);
checked_close(connmap.data[i].linkfd);
}
free(connmap.data);
close(channelsock);
checked_close(channelsock);
return retcode;
}
......@@ -473,6 +576,8 @@ int run_client(const char *socket_path, const struct main_config *config,
* and older Wayland versions have edge cases */
int dispfd = -1;
char disp_path[MAX_SOCKETPATH_LEN];
memset(disp_path, 0, sizeof(disp_path));
if (via_socket) {
dispfd = get_inherited_socket();
if (dispfd == -1) {
......@@ -481,6 +586,10 @@ int run_client(const char *socket_path, const struct main_config *config,
}
return EXIT_FAILURE;
}
/* This socket is inherited and meant to be closed by Waypipe */
if (dispfd >= 0 && dispfd < 256) {
inherited_fds[dispfd / 64] &= ~(1uLL << (dispfd % 64));
}
} else {
if (get_display_path(disp_path) == -1) {
if (eol_pid) {
......@@ -502,7 +611,7 @@ int run_client(const char *socket_path, const struct main_config *config,
}
return EXIT_FAILURE;
}
close(test_conn);
checked_close(test_conn);
}
wp_debug("A wayland compositor is available. Proceeding.");
......@@ -514,7 +623,7 @@ int run_client(const char *socket_path, const struct main_config *config,
waitpid(eol_pid, NULL, 0);
}
if (dispfd != -1) {
close(dispfd);
checked_close(dispfd);
}
return EXIT_FAILURE;
}
......
......@@ -140,7 +140,7 @@ int init_render_data(struct render_data *data)
struct gbm_device *dev = gbm_create_device(drm_fd);
if (!dev) {
data->disabled = true;
close(drm_fd);
checked_close(drm_fd);
wp_error("Failed to create gbm device from drm_fd");
return -1;
}
......@@ -158,12 +158,16 @@ void cleanup_render_data(struct render_data *data)
{
if (data->drm_fd != -1) {
gbm_device_destroy(data->dev);
close(data->drm_fd);
checked_close(data->drm_fd);
data->dev = NULL;
data->drm_fd = -1;
}
}
/**
* Estimate the mappable size of a DMABUF. This may be an overestimate,
* and should only be used if there is no other information about the DMABUF.
*/
static ssize_t get_dmabuf_fd_size(int fd)
{
ssize_t endp = lseek(fd, 0, SEEK_END);
......@@ -183,15 +187,15 @@ static ssize_t get_dmabuf_fd_size(int fd)
struct gbm_bo *import_dmabuf(struct render_data *rd, int fd, size_t *size,
struct dmabuf_slice_data *info, bool read_modifier)
{
ssize_t endp = get_dmabuf_fd_size(fd);
if (endp == -1) {
return NULL;
}
*size = (size_t)endp;
struct gbm_bo *bo;
if (!info) {
/* No protocol info, so guess the dimensions */
ssize_t endp = get_dmabuf_fd_size(fd);
if (endp == -1) {
return NULL;
}
struct gbm_import_fd_data data;
data.fd = fd;
data.width = 256;
......@@ -238,7 +242,6 @@ struct gbm_bo *import_dmabuf(struct render_data *rd, int fd, size_t *size,
data.width = info->width;
data.height = info->height;
data.format = simple_format;
bo = gbm_bo_import(rd->dev, GBM_BO_IMPORT_FD_MODIFIER, &data,
GBM_BO_USE_RENDERING);
}
......@@ -256,6 +259,9 @@ struct gbm_bo *import_dmabuf(struct render_data *rd, int fd, size_t *size,
}
}
/* todo: find out how to correctly map multiplanar formats */
*size = gbm_bo_get_stride(bo) * gbm_bo_get_height(bo);
return bo;
}
......@@ -342,14 +348,6 @@ retry:
gbm_bo_destroy(bo);
return NULL;
}
int tfd = gbm_bo_get_fd(bo);
ssize_t csize = get_dmabuf_fd_size(tfd);
close(tfd);
if (csize != (ssize_t)size) {
wp_error("Created DMABUF size (%zd disagrees with original size (%zu), giving up");
gbm_bo_destroy(bo);
return NULL;
}
} else {
uint64_t modifiers[2] = {info->modifier, GBM_BO_USE_RENDERING};
uint32_t simple_format = dmabuf_get_simple_format_for_plane(
......@@ -381,42 +379,6 @@ retry:
info->modifier, strerror(errno));
return NULL;
}
int tfd = gbm_bo_get_fd(bo);
ssize_t csize = get_dmabuf_fd_size(tfd);
close(tfd);
if (csize != (ssize_t)size) {
wp_error("Created DMABUF size (%zd disagrees with original size (%zu), %s",
csize, size,
(csize > (ssize_t)size)
? "keeping anyway"
: "attempting taller");
if (csize < (ssize_t)size) {
// Retry, with height increased to hopefully
// contain enough bytes
uint32_t nheight =
((uint32_t)size +
info->strides[0] -
1) /
info->strides[0];
gbm_bo_destroy(bo);
bo = gbm_bo_create_with_modifiers(rd->dev,
info->width, nheight,
simple_format, modifiers, 2);
if (!bo) {
wp_error("Failed to make extra-sized dmabuf (with modifier %lx): %s",
info->modifier,
strerror(errno));
return NULL;
}
int nfd = gbm_bo_get_fd(bo);
ssize_t nsize = get_dmabuf_fd_size(nfd);
close(nfd);
if (nsize < (ssize_t)size) {
wp_error("Trying to fudge dmabuf height to reach target size of %zu bytes; failed, got %zd",
size, nsize);
}
}
}
}
return bo;
}
......
......@@ -40,6 +40,8 @@ struct render_data {
bool supports_modifiers;
/* video hardware context */
bool av_disabled;
int av_bpf;
int av_video_fmt;
struct AVBufferRef *av_hwdevice_ref;
struct AVBufferRef *av_drmdevice_ref;
VADisplay av_vadisplay;
......
......@@ -77,6 +77,8 @@ struct wp_buffer {
uint32_t dmabuf_offsets[MAX_DMABUF_PLANES];
uint32_t dmabuf_strides[MAX_DMABUF_PLANES];
uint64_t dmabuf_modifiers[MAX_DMABUF_PLANES];
uint64_t unique_id;
};
struct damage_record {
......@@ -84,14 +86,23 @@ struct damage_record {
bool buffer_coordinates;
};
struct damage_list {
struct damage_record *list;
int len;
int size;
};
#define SURFACE_DAMAGE_BACKLOG 7
struct wp_surface {
struct wp_object base;
struct damage_record *damage_list;
int damage_list_len;
int damage_list_size;
/* The zeroth list is the "current" one, 1st was damage provided at last
* commit, etc. */
struct damage_list damage_lists[SURFACE_DAMAGE_BACKLOG];
/* Unique buffer identifiers to which the above damage lists apply */
uint64_t attached_buffer_uids[SURFACE_DAMAGE_BACKLOG];
uint32_t attached_buffer_id;
uint32_t attached_buffer_id; /* protocol object id */
int32_t scale;
int32_t transform;
};
......@@ -181,7 +192,9 @@ void destroy_wp_object(struct fd_translation_map *map, struct wp_object *object)
}
} else if (object->type == &intf_wl_surface) {
struct wp_surface *r = (struct wp_surface *)object;
free(r->damage_list);
for (int i = 0; i < SURFACE_DAMAGE_BACKLOG; i++) {
free(r->damage_lists[i].list);
}
} else if (object->type == &intf_zwlr_screencopy_frame_v1) {
struct wp_wlr_screencopy_frame *r =
(struct wp_wlr_screencopy_frame *)object;
......@@ -197,11 +210,7 @@ void destroy_wp_object(struct fd_translation_map *map, struct wp_object *object)
}
// Sometimes multiple entries point to the same buffer
if (r->add[i].fd != -1) {
if (close(r->add[i].fd) == -1) {
wp_error("Incorrect close(%d): %s",
r->add[i].fd,
strerror(errno));
}
checked_close(r->add[i].fd);
for (int k = 0; k < MAX_DMABUF_PLANES; k++) {
if (r->add[i].fd == r->add[k].fd) {
......@@ -329,8 +338,6 @@ void do_wl_registry_evt_global(struct context *ctx, uint32_t name,
}
bool unsupported = false;
// deprecated, and waypipe doesn't have logic for it anyway
unsupported |= !strcmp(interface, "wl_shell");
// requires novel fd translation, not yet supported
unsupported |= !strcmp(
interface, "zwp_linux_explicit_synchronization_v1");
......@@ -468,20 +475,10 @@ static int get_shm_bytes_per_pixel(uint32_t format)
return -1;
}
}
static int compute_damage_coordinates(int *xlow, int *xhigh, int *ylow,
static void compute_damage_coordinates(int *xlow, int *xhigh, int *ylow,
int *yhigh, const struct damage_record *rec, int buf_width,
int buf_height, int transform, int scale)
{
if (scale <= 0) {
wp_error("Not applying damage due to invalid buffer scale (%d)",
scale);
return -1;
}
if (transform < 0 || transform >= 8) {
wp_error("Not applying damage due to invalid buffer transform (%d)",
transform);
return -1;
}
if (rec->buffer_coordinates) {
*xlow = rec->x;
*xhigh = rec->x + rec->width;
......@@ -528,7 +525,6 @@ static int compute_damage_coordinates(int *xlow, int *xhigh, int *ylow,
*yhigh = yh;
}
}
return 0;
}
void do_wl_surface_req_attach(struct context *ctx, struct wp_object *buffer,
int32_t x, int32_t y)
......@@ -550,6 +546,18 @@ void do_wl_surface_req_attach(struct context *ctx, struct wp_object *buffer,
struct wp_surface *surface = (struct wp_surface *)ctx->obj;
surface->attached_buffer_id = bufobj->obj_id;
}
static void rotate_damage_lists(struct wp_surface *surface)
{
free(surface->damage_lists[SURFACE_DAMAGE_BACKLOG - 1].list);
memmove(surface->damage_lists + 1, surface->damage_lists,
(SURFACE_DAMAGE_BACKLOG - 1) *
sizeof(struct damage_list));
memset(surface->damage_lists, 0, sizeof(struct damage_list));
memmove(surface->attached_buffer_uids + 1,
surface->attached_buffer_uids,
(SURFACE_DAMAGE_BACKLOG - 1) * sizeof(uint64_t));
surface->attached_buffer_uids[0] = 0;
}
void do_wl_surface_req_commit(struct context *ctx)
{
struct wp_surface *surface = (struct wp_surface *)ctx->obj;
......@@ -576,16 +584,10 @@ void do_wl_surface_req_commit(struct context *ctx)
wp_error("Buffer to commit has the wrong type, and may have been recycled");
return;
}
if (surface->damage_list_len == 0) {
// No damage to report
return;
}
struct wp_buffer *buf = (struct wp_buffer *)obj;
surface->attached_buffer_uids[0] = buf->unique_id;
if (buf->type == BUF_DMA) {
free(surface->damage_list);
surface->damage_list = NULL;
surface->damage_list_len = 0;
surface->damage_list_size = 0;
rotate_damage_lists(surface);
for (int i = 0; i < buf->dmabuf_nplanes; i++) {
struct shadow_fd *sfd = buf->dmabuf_buffers[i];
......@@ -610,6 +612,7 @@ void do_wl_surface_req_commit(struct context *ctx)
wp_error("wp_buffer is backed neither by DMA nor SHM, not yet supported");
return;
}
struct shadow_fd *sfd = buf->shm_buffer;
if (!sfd) {
wp_error("wp_buffer to be committed has no fd");
......@@ -622,32 +625,58 @@ void do_wl_surface_req_commit(struct context *ctx)
sfd->is_dirty = true;
int bpp = get_shm_bytes_per_pixel(buf->shm_format);
if (bpp == -1) {
damage_everything(&sfd->damage);
free(surface->damage_list);
surface->damage_list = NULL;
surface->damage_list_len = 0;
surface->damage_list_size = 0;
return;
goto backup;
}
if (surface->scale <= 0) {
wp_error("Invalid buffer scale during commit (%d), assuming everything damaged",
surface->scale);
goto backup;
}
if (surface->transform < 0 || surface->transform >= 8) {
wp_error("Invalid buffer transform during commit (%d), assuming everything damaged",
surface->transform);
goto backup;
}
/* The damage specified as of wl_surface commit indicates which region
* of the surface has changed between the last commit and the current
* one. However, the last time the attached buffer was used may have
* been several commits ago, so we need to replay all the damage up
* to the current point. */
int age = -1;
int n_damaged_rects = surface->damage_lists[0].len;
for (int j = 1; j < SURFACE_DAMAGE_BACKLOG; j++) {
if (surface->attached_buffer_uids[0] ==
surface->attached_buffer_uids[j]) {
age = j;
break;
}
n_damaged_rects += surface->damage_lists[j].len;
}
if (age == -1) {
/* cannot find last time buffer+surface combo was used */
goto backup;
}
struct ext_interval *damage_array =
malloc(sizeof(struct ext_interval) *
(size_t)surface->damage_list_len);
struct ext_interval *damage_array = malloc(
sizeof(struct ext_interval) * (size_t)n_damaged_rects);
if (!damage_array) {
wp_error("Failed to allocate damage array");
damage_everything(&sfd->damage);
return;
goto backup;
}
int i = 0;
// Translate damage stack into damage records for the fd buffer
for (int j = 0; j < surface->damage_list_len; j++) {
int xlow, xhigh, ylow, yhigh;
int r = compute_damage_coordinates(&xlow, &xhigh, &ylow, &yhigh,
&surface->damage_list[j], buf->shm_width,
buf->shm_height, surface->transform,
surface->scale);
if (r != -1) {
for (int k = 0; k < age; k++) {
const struct damage_list *frame_damage =
&surface->damage_lists[k];
for (int j = 0; j < frame_damage->len; j++) {
int xlow, xhigh, ylow, yhigh;
compute_damage_coordinates(&xlow, &xhigh, &ylow, &yhigh,
&frame_damage->list[j], buf->shm_width,
buf->shm_height, surface->transform,
surface->scale);
/* Clip the damage rectangle to the containing
* buffer. */
xlow = clamp(xlow, 0, buf->shm_width);
......@@ -668,27 +697,26 @@ void do_wl_surface_req_commit(struct context *ctx)
merge_damage_records(&sfd->damage, i, damage_array,
ctx->g->threads.diff_alignment_bits);
free(damage_array);
free(surface->damage_list);
surface->damage_list = NULL;
surface->damage_list_len = 0;
surface->damage_list_size = 0;
rotate_damage_lists(surface);
backup:
damage_everything(&sfd->damage);
rotate_damage_lists(surface);
return;
}
static void append_damage_record(struct wp_surface *surface, int32_t x,
int32_t y, int32_t width, int32_t height,
bool in_buffer_coordinates)
{
if (buf_ensure_size(surface->damage_list_len + 1,
sizeof(struct damage_record),
&surface->damage_list_size,
(void **)&surface->damage_list) == -1) {
struct damage_list *current = &surface->damage_lists[0];
if (buf_ensure_size(current->len + 1, sizeof(struct damage_record),
&current->size, (void **)&current->list) == -1) {
wp_error("Failed to allocate space for damage list, dropping damage record");
return;
}
// A rectangle of the buffer was damaged, hence backing buffers
// may be updated.
struct damage_record *damage =
&surface->damage_list[surface->damage_list_len++];
struct damage_record *damage = &current->list[current->len++];
damage->buffer_coordinates = in_buffer_coordinates;
damage->x = x;
damage->y = y;
......@@ -740,6 +768,10 @@ void do_wl_keyboard_evt_keymap(
struct shadow_fd *sfd = translate_fd(&ctx->g->map, &ctx->g->render, fd,
fdtype, fdsz, NULL, false, false);
if (!sfd) {
wp_error("Failed to create shadow for keymap fd=%d", fd);
return;
}
/* The keyboard file descriptor is never changed after being sent.
* Mark the shadow structure as owned by the protocol, so it can be
* automatically deleted as soon as the fd has been transferred. */
......@@ -773,6 +805,9 @@ void do_wl_shm_req_create_pool(
struct shadow_fd *sfd = translate_fd(&ctx->g->map, &ctx->g->render, fd,
fdtype, fdsz, NULL, false, false);
if (!sfd) {
return;
}
the_shm_pool->owned_buffer = shadow_incref_protocol(sfd);
}
......@@ -819,6 +854,7 @@ void do_wl_shm_pool_req_create_buffer(struct context *ctx, struct wp_object *id,
the_buffer->shm_height = height;
the_buffer->shm_stride = stride;
the_buffer->shm_format = format;
the_buffer->unique_id = ctx->g->tracker.buffer_seqno++;
}
void do_zwlr_screencopy_frame_v1_evt_ready(struct context *ctx,
......@@ -1024,6 +1060,9 @@ void do_wl_drm_req_create_prime_buffer(struct context *ctx,
struct shadow_fd *sfd = translate_fd(&ctx->g->map, &ctx->g->render,
name, FDC_DMABUF, 0, &info, true, false);
if (!sfd) {
return;
}
buf->type = BUF_DMA;
buf->dmabuf_nplanes = 1;
buf->dmabuf_buffers[0] = shadow_incref_protocol(sfd);
......@@ -1033,6 +1072,7 @@ void do_wl_drm_req_create_prime_buffer(struct context *ctx,
// handling multiple offsets (?)
buf->dmabuf_offsets[0] = (uint32_t)offset0;
buf->dmabuf_strides[0] = (uint32_t)stride0;
buf->unique_id = ctx->g->tracker.buffer_seqno++;
}
void do_zwp_linux_dmabuf_v1_evt_modifier(struct context *ctx, uint32_t format,
......@@ -1040,10 +1080,12 @@ void do_zwp_linux_dmabuf_v1_evt_modifier(struct context *ctx, uint32_t format,
{
(void)format;
uint64_t modifier = modifier_hi * 0x100000000uL * modifier_lo;
uint64_t modifier = modifier_hi * 0x100000000uLL + modifier_lo;
// Prevent all advertisements for dmabufs with modifiers
if (modifier && ctx->g->config->only_linear_dmabuf) {
ctx->drop_this_msg = true;
if (ctx->g->config->only_linear_dmabuf) {
if (modifier != 0 && modifier != ((1uLL << 56) - 1)) {
ctx->drop_this_msg = true;
}
}
}
void do_zwp_linux_buffer_params_v1_evt_created(
......@@ -1070,6 +1112,7 @@ void do_zwp_linux_buffer_params_v1_evt_created(
buf->dmabuf_width = params->create_width;
buf->dmabuf_height = params->create_height;
buf->dmabuf_format = params->create_format;
buf->unique_id = ctx->g->tracker.buffer_seqno++;
}
void do_zwp_linux_buffer_params_v1_req_add(struct context *ctx, int fd,
uint32_t plane_idx, uint32_t offset, uint32_t stride,
......@@ -1091,7 +1134,7 @@ void do_zwp_linux_buffer_params_v1_req_add(struct context *ctx, int fd,
params->add[plane_idx].offset = offset;
params->add[plane_idx].stride = stride;
params->add[plane_idx].modifier =
modifier_lo + modifier_hi * 0x100000000uL;
modifier_lo + modifier_hi * 0x100000000uLL;
// Only perform rearrangement on the client side, for now
if (!ctx->on_display_side) {
params->add[plane_idx].msg =
......@@ -1185,11 +1228,7 @@ static void deduplicate_dmabuf_fds(
}
if (lowest != i &&
params->add[i].fd != params->add[lowest].fd) {
if (close(params->add[i].fd) == -1) {
wp_error("Incorrect close(%d): %s",
params->add[i].fd,
strerror(errno));
}
checked_close(params->add[i].fd);
}
params->add[i].fd = params->add[lowest].fd;
}
......@@ -1259,6 +1298,9 @@ void do_zwp_linux_buffer_params_v1_req_create(struct context *ctx,
struct shadow_fd *sfd = translate_fd(&ctx->g->map,
&ctx->g->render, params->add[i].fd, res_type, 0,
&info, false, false);
if (!sfd) {
continue;
}
/* increment for each extra time this fd will be sent */
if (sfd->has_owner) {
shadow_incref_transfer(sfd);
......@@ -1299,7 +1341,7 @@ void do_zwlr_export_dmabuf_frame_v1_evt_frame(struct context *ctx,
(void)flags;
(void)buffer_flags;
frame->format = format;
frame->modifier = mod_high * 0x100000000uL + mod_low;
frame->modifier = mod_high * 0x100000000uLL + mod_low;
frame->nobjects = num_objects;
if (frame->nobjects > MAX_DMABUF_PLANES) {
wp_error("Too many (%u) frame objects required",
......@@ -1357,6 +1399,9 @@ void do_zwlr_export_dmabuf_frame_v1_evt_object(struct context *ctx,
struct shadow_fd *sfd = translate_fd(&ctx->g->map, &ctx->g->render, fd,
FDC_DMABUF, 0, &info, false, false);
if (!sfd) {
return;
}
if (sfd->buffer_size < size) {
wp_error("Frame object %u has a dmabuf with less (%u) than the advertised (%u) size",
index, (uint32_t)sfd->buffer_size, size);
......@@ -1396,8 +1441,8 @@ static void translate_data_transfer_fd(struct context *context, int32_t fd)
* socketpair, with additional properties. The fd being sent
* around should be, according to the protocol, only written into and
* closed */
translate_fd(&context->g->map, &context->g->render, fd, FDC_PIPE, 0,
NULL, false, true);
(void)translate_fd(&context->g->map, &context->g->render, fd, FDC_PIPE,
0, NULL, false, true);
}
void do_gtk_primary_selection_offer_req_receive(
struct context *ctx, const char *mime_type, int fd)
......@@ -1459,6 +1504,9 @@ void do_zwlr_gamma_control_v1_req_set_gamma(struct context *ctx, int fd)
}
struct shadow_fd *sfd = translate_fd(&ctx->g->map, &ctx->g->render, fd,
fdtype, fdsz, NULL, false, false);
if (!sfd) {
return;
}
/* Mark the shadow structure as owned by the protocol, but do not
* increase the protocol refcount, so that as soon as it gets
* transferred it is destroyed */
......
......@@ -37,6 +37,8 @@ struct main_config {
bool no_gpu;
bool only_linear_dmabuf;
bool video_if_possible;
int video_bpf;
enum video_coding_fmt video_fmt;
bool prefer_hwvideo;
};
struct globals {
......@@ -60,8 +62,8 @@ int main_interface_loop(int chanfd, int progfd, int linkfd,
/** Act as a Wayland server */
int run_server(const char *socket_path, const char *display_path,
const char *control_path, const struct main_config *config,
bool oneshot, bool unlink_at_end, const char *application,
char *const app_argv[]);
bool oneshot, bool unlink_at_end, char *const app_argv[],
bool login_shell_if_backup);
/** Act as a Wayland client */
int run_client(const char *socket_path, const struct main_config *config,
bool oneshot, bool via_socket, pid_t eol_pid);
......
......@@ -134,7 +134,7 @@ static ssize_t iovec_write(int conn, const char *buf, size_t buflen,
return ret;
}
static void translate_fds(struct fd_translation_map *map,
static int translate_fds(struct fd_translation_map *map,
struct render_data *render, int nfds, const int fds[],
int ids[])
{
......@@ -145,7 +145,11 @@ static void translate_fds(struct fd_translation_map *map,
ids[i] = translate_fd(map, render, fds[i], fdtype, fdsz, NULL,
false, false)
->remote_id;
if (!ids[i]) {
return -1;
}
}
return 0;
}
/** Given a list of global ids, and an up-to-date translation map, produce local
* file descriptors */
......@@ -966,8 +970,11 @@ static int advance_waymsg_progread(struct way_msg_state *wmsg,
int32_t *rbuffer = (int32_t *)(msg + 1);
/* Translate and adjust refcounts */
translate_fds(&g->map, &g->render, wmsg->fds.zone_start,
wmsg->fds.data, rbuffer);
if (translate_fds(&g->map, &g->render,
wmsg->fds.zone_start,
wmsg->fds.data, rbuffer) == -1) {
return ERR_FATAL;
}
decref_transferred_rids(
&g->map, wmsg->fds.zone_start, rbuffer);
memmove(wmsg->fds.data,
......@@ -1057,7 +1064,7 @@ static int read_new_chanfd(int linkfd, struct int_window *recon_fds)
return -1;
}
for (int i = 0; i < recon_fds->zone_end - 1; i++) {
close(recon_fds->data[i]);
checked_close(recon_fds->data[i]);
}
int ret_fd = -1;
if (recon_fds->zone_end > 0) {
......@@ -1165,10 +1172,10 @@ int main_interface_loop(int chanfd, int progfd, int linkfd,
if (set_connections_nonblocking(chanfd, progfd, linkfd, display_side) ==
-1) {
if (linkfd != -1) {
close(linkfd);
checked_close(linkfd);
}
close(chanfd);
close(progfd);
checked_close(chanfd);
checked_close(progfd);
return EXIT_FAILURE;
}
const char *progdesc = display_side ? "compositor" : "application";
......@@ -1225,6 +1232,8 @@ int main_interface_loop(int chanfd, int progfd, int linkfd,
.disabled = config->no_gpu,
.av_disabled = config->no_gpu ||
!config->prefer_hwvideo,
.av_bpf = config->video_bpf,
.av_video_fmt = (int)config->video_fmt,
.av_hwdevice_ref = NULL,
.av_drmdevice_ref = NULL,
.av_vadisplay = NULL,
......@@ -1337,7 +1346,7 @@ int main_interface_loop(int chanfd, int progfd, int linkfd,
int new_fd = read_new_chanfd(linkfd, &recon_fds);
if (new_fd >= 0) {
if (chanfd != -1) {
close(chanfd);
checked_close(chanfd);
}
chanfd = new_fd;
reset_connection(&cross_data, &chan_msg,
......@@ -1345,7 +1354,7 @@ int main_interface_loop(int chanfd, int progfd, int linkfd,
needs_new_channel = false;
} else if (new_fd == -2) {
wp_error("Link to root process hang-up detected");
close(linkfd);
checked_close(linkfd);
linkfd = -1;
}
}
......@@ -1360,7 +1369,7 @@ int main_interface_loop(int chanfd, int progfd, int linkfd,
/* Actually handle the reconnection/reset state
*/
if (chanfd != -1) {
close(chanfd);
checked_close(chanfd);
}
chanfd = new_fd;
reset_connection(&cross_data, &chan_msg,
......@@ -1394,7 +1403,7 @@ int main_interface_loop(int chanfd, int progfd, int linkfd,
/* Channel connection has at least
* partially been shut down, so close it
* fully. */
close(chanfd);
checked_close(chanfd);
chanfd = -1;
if (linkfd == -1) {
wp_error("Channel hang up detected, no reconnection link, fatal");
......@@ -1407,11 +1416,11 @@ int main_interface_loop(int chanfd, int progfd, int linkfd,
/* Stop returned while writing: Wayland
* connection has at least partially
* shut down, so close it fully. */
close(progfd);
checked_close(progfd);
progfd = -1;
} else {
/* Stop returned while reading */
close(progfd);
checked_close(progfd);
progfd = -1;
if (way_msg.state ==
WM_WAITING_FOR_PROGRAM) {
......@@ -1538,13 +1547,13 @@ init_failure_cleanup:
free(chan_msg.proto_write.data);
if (chanfd != -1) {
close(chanfd);
checked_close(chanfd);
}
if (progfd != -1) {
close(progfd);
checked_close(progfd);
}
if (linkfd != -1) {
close(linkfd);
checked_close(linkfd);
}
return EXIT_SUCCESS;
}
......@@ -353,7 +353,7 @@ enum parse_state handle_message(struct globals *g, bool display_side,
fds_used += msg->n_fds;
if (objh->obj_id >= 0xff000000 && !strcmp(msg->name, "destroy")) {
if (objh->obj_id >= 0xff000000 && msg->is_destructor) {
/* Unfortunately, the wayland server library does not explicitly
* acknowledge the client requested deletion of objects that the
* wayland server has created; the client assumes success,
......
......@@ -60,6 +60,9 @@ struct message_tracker {
// creating a new type <-> binding it in the 'interface' list, via
// registry. each type produces 'callbacks'
struct obj_list objects;
/* sequence number to discriminate between wl_buffer objects; object ids
* and pointers are not guaranteed to be unique */
uint64_t buffer_seqno;
};
/** Context object, to be passed to the protocol handler functions */
struct context {
......
......@@ -32,6 +32,7 @@
#include <fcntl.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
......@@ -59,11 +60,17 @@ int create_anon_file(void)
#elif defined(SHM_ANON)
new_fileno = shm_open(SHM_ANON, O_RDWR, 0600);
#else
/* WARNING: this can be rather file-system
* intensive */
char template[256] = "/tmp/waypipe_XXXXXX";
new_fileno = mkstemp(template);
unlink(template);
// Fallback code. Should not be used from multiple threads
static int counter = 0;
int pid = getpid();
counter++;
char tmp_name[64];
sprintf(tmp_name, "/waypipe%d-data_%d", pid, counter);
new_fileno = shm_open(tmp_name, O_EXCL | O_RDWR | O_CREAT, 0644);
if (new_fileno == -1) {
return -1;
}
(void)shm_unlink(tmp_name);
#endif
return new_fileno;
}
......
......@@ -65,7 +65,7 @@ static void fill_random_key(struct connection_token *token)
if (devrand != -1) {
errno = 0;
(void)read(devrand, token->key, sizeof(token->key));
close(devrand);
checked_close(devrand);
}
}
......@@ -124,7 +124,7 @@ static int run_single_server_reconnector(int control_pipe, int linkfd,
sizeof(*flagged_token)) {
wp_error("Failed to write to new connection: %s",
strerror(errno));
close(new_conn);
checked_close(new_conn);
continue;
}
......@@ -132,12 +132,12 @@ static int run_single_server_reconnector(int control_pipe, int linkfd,
wp_error("Failed to send new connection to subprocess: %s",
strerror(errno));
}
close(new_conn);
checked_close(new_conn);
}
}
}
close(control_pipe);
close(linkfd);
checked_close(control_pipe);
checked_close(linkfd);
return retcode;
}
......@@ -176,13 +176,13 @@ static int run_single_server(int control_pipe, const char *socket_path,
pid_t reco_pid = fork();
if (reco_pid == -1) {
wp_debug("Fork failure");
close(linkfds[0]);
close(linkfds[1]);
checked_close(linkfds[0]);
checked_close(linkfds[1]);
goto fail_cfd;
} else if (reco_pid == 0) {
close(chanfd);
close(linkfds[0]);
close(server_link);
checked_close(chanfd);
checked_close(linkfds[0]);
checked_close(server_link);
/* Further uses of the token will be to reconnect */
token.header |= CONN_UPDATE_BIT;
......@@ -190,8 +190,8 @@ static int run_single_server(int control_pipe, const char *socket_path,
control_pipe, linkfds[1], &token);
exit(rc);
}
close(control_pipe);
close(linkfds[1]);
checked_close(control_pipe);
checked_close(linkfds[1]);
}
int ret = main_interface_loop(
......@@ -199,9 +199,9 @@ static int run_single_server(int control_pipe, const char *socket_path,
return ret;
fail_cfd:
close(chanfd);
checked_close(chanfd);
fail_srv:
close(server_link);
checked_close(server_link);
return EXIT_FAILURE;
}
......@@ -243,33 +243,34 @@ static int handle_new_server_connection(const char *current_sockpath,
if (npid == 0) {
// Run forked process, with the only shared state being the
// new channel socket
close(wdisplay_socket);
checked_close(wdisplay_socket);
if (reconnectable) {
close(control_pipe);
close(linksocks[0]);
checked_close(control_pipe);
checked_close(linksocks[0]);
}
for (int i = 0; i < connmap->count; i++) {
if (connmap->data[i].linkfd != -1) {
close(connmap->data[i].linkfd);
checked_close(connmap->data[i].linkfd);
}
}
int rc = main_interface_loop(
chanfd, appfd, linksocks[1], config, false);
check_unclosed_fds();
exit(rc);
} else if (npid == -1) {
wp_debug("Fork failure");
if (reconnectable) {
close(linksocks[0]);
close(linksocks[1]);
checked_close(linksocks[0]);
checked_close(linksocks[1]);
}
goto fail_chanfd;
}
// This process no longer needs the application connection
close(chanfd);
close(appfd);
checked_close(chanfd);
checked_close(appfd);
if (reconnectable) {
close(linksocks[1]);
checked_close(linksocks[1]);
connmap->data[connmap->count++] = (struct conn_addr){
.token = *new_token,
......@@ -280,9 +281,9 @@ static int handle_new_server_connection(const char *current_sockpath,
return 0;
fail_chanfd:
close(chanfd);
checked_close(chanfd);
fail_appfd:
close(appfd);
checked_close(appfd);
return -1;
}
......@@ -303,15 +304,16 @@ static int update_connections(char current_sockpath[static 110],
sizeof(flagged_token)) {
wp_error("Failed to write token to replacement connection: %s",
strerror(errno));
close(chanfd);
checked_close(chanfd);
return -1;
}
if (send_one_fd(connmap->data[i].linkfd, chanfd) == -1) {
// TODO: what happens if data has changed?
close(chanfd);
checked_close(chanfd);
return -1;
}
checked_close(chanfd);
}
/* If switching connections succeeded, adopt the new socket */
if (unlink_at_end && strcmp(current_sockpath, path)) {
......@@ -411,19 +413,58 @@ static int run_multi_server(int control_pipe, const char *socket_path,
if (unlink_at_end) {
unlink(current_sockpath);
}
close(wdisplay_socket);
checked_close(wdisplay_socket);
if (control_pipe != -1) {
checked_close(control_pipe);
}
for (int i = 0; i < connmap.count; i++) {
close(connmap.data[i].linkfd);
checked_close(connmap.data[i].linkfd);
}
free(connmap.data);
return retcode;
}
/* requires >=256 byte shell/shellname buffers */
static void setup_login_shell_command(char shell[static 256],
char shellname[static 256], bool login_shell)
{
strcpy(shellname, "-sh");
strcpy(shell, "/bin/sh");
// Select the preferred shell on the system
char *shell_env = getenv("SHELL");
if (!shell_env) {
return;
}
int len = (int)strlen(shell_env);
if (len >= 254) {
wp_error("Environment variable $SHELL is too long at %d bytes, falling back to %s",
len, shell);
return;
}
strcpy(shell, shell_env);
if (login_shell) {
/* Create a login shell. The convention for this is to prefix
* the name of the shell with a single hyphen */
int start = len;
for (; start-- > 0;) {
if (shell[start] == '/') {
start++;
break;
}
}
shellname[0] = '-';
strcpy(shellname + 1, shell + start);
} else {
strcpy(shellname, shell);
}
}
int run_server(const char *socket_path, const char *wayland_display,
const char *control_path, const struct main_config *config,
bool oneshot, bool unlink_at_end, const char *application,
char *const app_argv[])
bool oneshot, bool unlink_at_end, char *const app_argv[],
bool login_shell_if_backup)
{
wp_debug("I'm a server on %s, running: %s", socket_path, app_argv[0]);
......@@ -434,6 +475,7 @@ int run_server(const char *socket_path, const char *wayland_display,
return EXIT_FAILURE;
}
char display_path[256];
memset(display_path, 0, sizeof(display_path));
if (!oneshot) {
if (wayland_display[0] == '/') {
snprintf(display_path, 256, "%s", wayland_display);
......@@ -486,13 +528,24 @@ int run_server(const char *socket_path, const char *wayland_display,
// application
unsetenv("WAYLAND_DISPLAY");
setenv("WAYLAND_SOCKET", bufs2, 1);
close(server_link);
checked_close(server_link);
} else {
// Since Wayland 1.15, absolute paths are supported in
// WAYLAND_DISPLAY
unsetenv("WAYLAND_SOCKET");
setenv("WAYLAND_DISPLAY", wayland_display, 1);
close(wdisplay_socket);
checked_close(wdisplay_socket);
}
const char *application = app_argv[0];
char shell[256];
char shellname[256];
char *shellcmd[2] = {shellname, NULL};
if (!application) {
setup_login_shell_command(shell, shellname,
login_shell_if_backup);
application = shell;
app_argv = shellcmd;
}
execvp(application, app_argv);
......@@ -502,7 +555,7 @@ int run_server(const char *socket_path, const char *wayland_display,
}
if (oneshot) {
// We no longer need to see this side
close(wayland_socket);
checked_close(wayland_socket);
}
int control_pipe = -1;
......
......@@ -85,16 +85,13 @@ static void destroy_unlinked_sfd(
free(sfd->mem_mirror);
} else if (sfd->type == FDC_PIPE) {
if (sfd->pipe.fd != sfd->fd_local && sfd->pipe.fd != -1) {
close(sfd->pipe.fd);
checked_close(sfd->pipe.fd);
}
free(sfd->pipe.recv.data);
free(sfd->pipe.send.data);
}
if (sfd->fd_local != -1) {
if (close(sfd->fd_local) == -1) {
wp_error("Incorrect close(%d): %s", sfd->fd_local,
strerror(errno));
}
checked_close(sfd->fd_local);
}
free(sfd);
(void)map;
......@@ -303,8 +300,8 @@ void cleanup_thread_pool(struct thread_pool *pool)
free(pool->threads);
free(pool->stack);
close(pool->selfpipe_r);
close(pool->selfpipe_w);
checked_close(pool->selfpipe_r);
checked_close(pool->selfpipe_w);
}
const char *fdcat_to_str(enum fdcat cat)
......@@ -523,6 +520,10 @@ struct shadow_fd *translate_fd(struct fd_translation_map *map,
// Create a new translation map.
sfd = calloc(1, sizeof(struct shadow_fd));
if (!sfd) {
wp_error("Failed to allocate shadow_fd structure");
return NULL;
}
sfd->next = map->list;
map->list = sfd;
sfd->fd_local = fd;
......@@ -553,13 +554,26 @@ struct shadow_fd *translate_fd(struct fd_translation_map *map,
return sfd;
}
sfd->buffer_size = file_sz;
// both r/w permissions, because the size the allocates
// the memory does not always have to be the size that
// modifies it
sfd->file_readonly = false;
// both r/w permissions, because the side which allocates the
// memory does not always have to be the side that modifies it
sfd->mem_local = mmap(NULL, sfd->buffer_size,
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (!sfd->mem_local) {
wp_error("Mmap failed!");
if (sfd->mem_local == MAP_FAILED && errno == EPERM) {
wp_debug("Initial mmap for RID=%d failed, trying private+readonly",
sfd->remote_id);
// Some files are memfds that are sealed
// to be read-only
sfd->mem_local = mmap(NULL, sfd->buffer_size, PROT_READ,
MAP_PRIVATE, fd, 0);
if (sfd->mem_local != MAP_FAILED) {
sfd->file_readonly = true;
}
}
if (sfd->mem_local == MAP_FAILED) {
wp_error("Mmap failed when creating shadow RID=%d: %s",
sfd->remote_id, strerror(errno));
return sfd;
}
// This will be created at the first transfer
......@@ -601,13 +615,10 @@ struct shadow_fd *translate_fd(struct fd_translation_map *map,
if (!sfd->dmabuf_bo) {
return sfd;
}
int mirror_size = 0;
get_video_mirror_size(&sfd->dmabuf_info, NULL, NULL, NULL, NULL,
&mirror_size);
sfd->mem_mirror = calloc(
(size_t)max((int)sfd->buffer_size, mirror_size),
1);
(void)setup_video_encode(sfd, render);
if (setup_video_encode(sfd, render) == -1) {
wp_error("Video encoding setup failed for RID=%d",
sfd->remote_id);
}
} else if (sfd->type == FDC_DMAVID_IW) {
memcpy(&sfd->dmabuf_info, info,
sizeof(struct dmabuf_slice_data));
......@@ -619,7 +630,10 @@ struct shadow_fd *translate_fd(struct fd_translation_map *map,
if (!sfd->dmabuf_bo) {
return sfd;
}
(void)setup_video_decode(sfd, render);
if (setup_video_decode(sfd, render) == -1) {
wp_error("Video decoding setup failed for RID=%d",
sfd->remote_id);
}
} else if (sfd->type == FDC_DMABUF) {
sfd->buffer_size = 0;
......@@ -1266,27 +1280,15 @@ static int create_from_update(struct fd_translation_map *map,
sfd->mem_mirror = aligned_alloc(
alignment, alignz(sfd->buffer_size, alignment));
// The PID should be unique during the lifetime of the
// program
char file_shm_buf_name[256];
sprintf(file_shm_buf_name, "/waypipe%d-data_%d", getpid(),
sfd->remote_id);
sfd->fd_local = shm_open(file_shm_buf_name,
O_RDWR | O_CREAT | O_TRUNC, 0644);
sfd->fd_local = create_anon_file();
if (sfd->fd_local == -1) {
wp_error("Failed to create shm file for object %d: %s",
wp_error("Failed to create anon file for object %d: %s",
sfd->remote_id, strerror(errno));
return 0;
}
if (shm_unlink(file_shm_buf_name) == -1) {
wp_error("Failed to unlink new shm file for object %d: %s",
sfd->remote_id, strerror(errno));
}
if (ftruncate(sfd->fd_local, (off_t)sfd->buffer_size) == -1) {
wp_error("Failed to resize shm file %s to size %zu for reason: %s",
file_shm_buf_name, sfd->buffer_size,
strerror(errno));
wp_error("Failed to resize anon file to size %zu for reason: %s",
sfd->buffer_size, strerror(errno));
return 0;
}
sfd->mem_local = mmap(NULL, sfd->buffer_size,
......@@ -1356,7 +1358,7 @@ static int create_from_update(struct fd_translation_map *map,
sfd->buffer_size = header.file_size;
if (init_render_data(render) == 1) {
if (init_render_data(render) == -1) {
sfd->fd_local = -1;
return 0;
}
......@@ -1373,13 +1375,10 @@ static int create_from_update(struct fd_translation_map *map,
}
sfd->fd_local = export_dmabuf(sfd->dmabuf_bo);
int mirror_size = 0;
get_video_mirror_size(&sfd->dmabuf_info, NULL, NULL, NULL, NULL,
&mirror_size);
sfd->mem_mirror = calloc(
(size_t)max((int)sfd->buffer_size, mirror_size),
1);
(void)setup_video_decode(sfd, render);
if (setup_video_decode(sfd, render) == -1) {
wp_error("Video decoding setup failed for RID=%d",
sfd->remote_id);
}
} else if (type == WMSG_OPEN_DMAVID_SRC) {
/* remote writes data, this side reads data */
sfd->type = FDC_DMAVID_IR;
......@@ -1404,13 +1403,10 @@ static int create_from_update(struct fd_translation_map *map,
}
sfd->fd_local = export_dmabuf(sfd->dmabuf_bo);
int mirror_size = 0;
get_video_mirror_size(&sfd->dmabuf_info, NULL, NULL, NULL, NULL,
&mirror_size);
sfd->mem_mirror = calloc(
(size_t)max((int)sfd->buffer_size, mirror_size),
1);
(void)setup_video_encode(sfd, render);
if (setup_video_encode(sfd, render) == -1) {
wp_error("Video encoding setup failed for RID=%d",
sfd->remote_id);
}
} else if (type == WMSG_OPEN_DMABUF) {
sfd->type = FDC_DMABUF;
const struct wmsg_open_dmabuf header =
......@@ -1456,8 +1452,9 @@ static void increase_buffer_sizes(struct shadow_fd *sfd,
sfd->buffer_size = new_size;
sfd->mem_local = mmap(NULL, sfd->buffer_size, PROT_READ | PROT_WRITE,
MAP_SHARED, sfd->fd_local, 0);
if (!sfd->mem_local) {
wp_error("Mmap failed!");
if (sfd->mem_local == MAP_FAILED) {
wp_error("Mmap failed to remap increased buffer for RID=%d: %s",
sfd->remote_id, strerror(errno));
return;
}
// todo: handle allocation failures
......@@ -1483,7 +1480,7 @@ static void pipe_close_write(struct shadow_fd *sfd)
*/
shutdown(sfd->pipe.fd, SHUT_WR);
} else {
close(sfd->pipe.fd);
checked_close(sfd->pipe.fd);
if (sfd->fd_local == sfd->pipe.fd) {
sfd->fd_local = -1;
}
......@@ -1503,7 +1500,7 @@ static void pipe_close_read(struct shadow_fd *sfd)
*/
shutdown(sfd->pipe.fd, SHUT_RD);
} else {
close(sfd->pipe.fd);
checked_close(sfd->pipe.fd);
if (sfd->fd_local == sfd->pipe.fd) {
sfd->fd_local = -1;
}
......@@ -1578,6 +1575,12 @@ int apply_update(struct fd_translation_map *map, struct thread_pool *threads,
remote_id, msg->size);
return ERR_FATAL;
}
if (sfd->type == FDC_FILE && sfd->file_readonly) {
wp_debug("Ignoring a fill update to readonly file at RID=%d",
remote_id);
return 0;
}
const struct wmsg_buffer_fill *header =
(const struct wmsg_buffer_fill *)msg->data;
......@@ -1642,6 +1645,11 @@ int apply_update(struct fd_translation_map *map, struct thread_pool *threads,
remote_id, msg->size);
return ERR_FATAL;
}
if (sfd->type == FDC_FILE && sfd->file_readonly) {
wp_debug("Ignoring a diff update to readonly file at RID=%d",
remote_id);
return 0;
}
const struct wmsg_buffer_diff *header =
(const struct wmsg_buffer_diff *)msg->data;
......@@ -1792,7 +1800,7 @@ bool shadow_decref_transfer(
* it and make it match pipe.fd, just as on the side where
* the original pipe was introduced */
if (sfd->pipe.fd != sfd->fd_local) {
close(sfd->fd_local);
checked_close(sfd->fd_local);
sfd->fd_local = sfd->pipe.fd;
}
}
......
......@@ -208,6 +208,7 @@ struct shadow_fd {
// File data
size_t remote_bufsize; // used to check for and send file extensions
bool file_readonly;
// Pipe data
struct pipe_state pipe;
......@@ -223,6 +224,7 @@ struct shadow_fd {
struct AVFrame *video_tmp_frame; /* To hold intermediate copies */
struct AVFrame *video_yuv_frame; /* In enc/dec preferred format */
void *video_yuv_frame_data;
void *video_local_frame_data;
struct AVPacket *video_packet;
struct SwsContext *video_color_context;
int64_t video_frameno;
......@@ -249,7 +251,11 @@ const char *fdcat_to_str(enum fdcat cat);
* produce matching global id, and register it into the translation map if
* not already done. The function can also be provided with optional extra
* information (*info). If `read_modifier` is true, then the modifier for
* a DMABUF should be automatically detected. */
* a DMABUF should be automatically detected.
*
* This may return NULL on allocation failure; other failures will in general
* warn and disable replication features.
**/
struct shadow_fd *translate_fd(struct fd_translation_map *map,
struct render_data *render, int fd, enum fdcat type, size_t sz,
const struct dmabuf_slice_data *info, bool read_modifier,
......@@ -333,6 +339,10 @@ bool request_work_task(struct thread_pool *pool, struct task_data *task,
void run_task(struct task_data *task, struct thread_data *local);
// video.c
enum video_coding_fmt {
VIDEO_H264,
VIDEO_VP9,
};
void cleanup_hwcontext(struct render_data *rd);
bool video_supports_dmabuf_format(uint32_t format, uint64_t modifier);
bool video_supports_shm_format(uint32_t format);
......@@ -350,21 +360,4 @@ void collect_video_from_mirror(
void apply_video_packet(struct shadow_fd *sfd, struct render_data *rd,
const struct bytebuf *data);
/**
* Video encoding generally has stronger shape constraints than
* dmabufs do; for example, width/height need to be divisible by 2, and
* strides divisible by e.g. 16 in some cases.
*/
void get_video_mirror_size(const struct dmabuf_slice_data *info, int *new_width,
int *new_height, int *new_strides, int *new_offsets,
int *new_min_size);
/**
* Copy the buffer data onto the mirror, slightly shifting lines of the
* image to match video encoding shape constraints.
*/
void copy_onto_video_mirror(char *buffer, char *mirror,
const struct dmabuf_slice_data *info);
void copy_from_video_mirror(char *buffer, char *mirror,
const struct dmabuf_slice_data *info);
#endif // WAYPIPE_SHADOW_H
......@@ -28,6 +28,7 @@
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <poll.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
......@@ -41,6 +42,7 @@
#include <unistd.h>
bool shutdown_flag = false;
uint64_t inherited_fds[4] = {0, 0, 0, 0};
void handle_sigint(int sig)
{
(void)sig;
......@@ -87,19 +89,19 @@ int setup_nb_socket(const char *socket_path, int nmaxclients)
if (set_nonblocking(sock) == -1) {
wp_error("Error making socket nonblocking: %s",
strerror(errno));
close(sock);
checked_close(sock);
return -1;
}
if (bind(sock, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) {
wp_error("Error binding socket at %s: %s", socket_path,
strerror(errno));
close(sock);
checked_close(sock);
return -1;
}
if (listen(sock, nmaxclients) == -1) {
wp_error("Error listening to socket at %s: %s", socket_path,
strerror(errno));
close(sock);
checked_close(sock);
unlink(socket_path);
return -1;
}
......@@ -128,12 +130,63 @@ int connect_to_socket(const char *socket_path)
if (connect(chanfd, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) {
wp_error("Error connecting to socket (%s): %s", socket_path,
strerror(errno));
close(chanfd);
checked_close(chanfd);
return -1;
}
return chanfd;
}
void set_initial_fds(void)
{
struct pollfd checklist[256];
for (int i = 0; i < 256; i++) {
checklist[i].fd = i;
checklist[i].events = 0;
checklist[i].revents = 0;
}
if (poll(checklist, 256, 0) == -1) {
wp_error("fd-checking poll failed: %s", strerror(errno));
return;
}
for (int i = 0; i < 256; i++) {
if (!(checklist[i].revents & POLLNVAL)) {
inherited_fds[i / 64] |= (1uLL << (i % 64));
}
}
}
void check_unclosed_fds(void)
{
/* Verify that all file descriptors have been closed. Since most
* instances have <<256 file descriptors open at a given time, it is
* safe to only check up to that level */
struct pollfd checklist[256];
for (int i = 0; i < 256; i++) {
checklist[i].fd = i;
checklist[i].events = 0;
checklist[i].revents = 0;
}
if (poll(checklist, 256, 0) == -1) {
wp_error("fd-checking poll failed: %s", strerror(errno));
return;
}
for (int i = 0; i < 256; i++) {
bool initial_fd = (inherited_fds[i / 64] &
(1uLL << (i % 64))) != 0;
if (initial_fd) {
if (checklist[i].revents & POLLNVAL) {
wp_error("Unexpected closed fd %d",
checklist[i].fd);
}
} else {
if (!(checklist[i].revents & POLLNVAL)) {
wp_error("Unexpected open fd %d",
checklist[i].fd);
}
}
}
}
int send_one_fd(int socket, int fd)
{
union {
......@@ -222,7 +275,7 @@ bool wait_for_pid_and_clean(pid_t *target_pid, int *status, int options,
if (map->data[ir].pid != r) {
iw++;
} else {
close(map->data[ir].linkfd);
checked_close(map->data[ir].linkfd);
}
}
map->count = iw;
......
......@@ -47,6 +47,7 @@
// On SIGINT, this is set to true. The main program should then cleanup ASAP
extern bool shutdown_flag;
extern uint64_t inherited_fds[4];
void handle_sigint(int sig);
......@@ -83,6 +84,15 @@ int setup_nb_socket(const char *socket_path, int nmaxclients);
/** Connect to the socket at the given path, returning created fd if
* successful, else -1.*/
int connect_to_socket(const char *socket_path);
/** Call close(fd), logging error when fd is invalid */
#define checked_close(fd) \
if (close(fd) == -1) { \
wp_error("close(%d) failed: %s", fd, strerror(errno)); \
}
/** Set the list of initially available fds (typically stdin/out/errno) */
void set_initial_fds(void);
/** Verify that all file descriptors (except for the initial ones) are closed */
void check_unclosed_fds(void);
#define WAYPIPE_PROTOCOL_VERSION 0x1u
/** If the byte order is wrong, the fixed set/unset bits are swapped */
......
......@@ -40,18 +40,6 @@ bool video_supports_shm_format(uint32_t format)
return false;
}
void get_video_mirror_size(const struct dmabuf_slice_data *info, int *new_width,
int *new_height, int *new_strides, int *new_offsets,
int *new_min_size)
{
(void)info;
(void)new_width;
(void)new_height;
(void)new_strides;
(void)new_offsets;
(void)new_min_size;
}
void copy_onto_video_mirror(char *buffer, char *mirror,
const struct dmabuf_slice_data *info)
{
......@@ -120,9 +108,13 @@ void apply_video_packet(struct shadow_fd *sfd, struct render_data *rd,
/* these are equivalent to the GBM formats */
#include <libdrm/drm_fourcc.h>
#define VIDEO_HW_ENCODER "h264_vaapi"
#define VIDEO_SW_ENCODER "libx264"
#define VIDEO_DECODER "h264"
#define VIDEO_H264_HW_ENCODER "h264_vaapi"
#define VIDEO_H264_SW_ENCODER "libx264"
#define VIDEO_H264_DECODER "h264"
#define VIDEO_VP9_HW_ENCODER "vp9_vaapi"
#define VIDEO_VP9_SW_ENCODER "libvpx-vp9"
#define VIDEO_VP9_DECODER "vp9"
static enum AVPixelFormat drm_to_av(uint32_t format)
{
......@@ -473,6 +465,9 @@ void destroy_video_data(struct shadow_fd *sfd)
if (sfd->video_yuv_frame_data) {
av_freep(sfd->video_yuv_frame_data);
}
if (sfd->video_local_frame_data) {
av_freep(sfd->video_local_frame_data);
}
av_frame_free(&sfd->video_local_frame);
av_frame_free(&sfd->video_tmp_frame);
av_frame_free(&sfd->video_yuv_frame);
......@@ -480,107 +475,37 @@ void destroy_video_data(struct shadow_fd *sfd)
}
}
/* see AVFrame::video documentation; needed due to overreads with SIMD */
#define VIDEO_MIRROR_EXTRA_BYTES 16
void get_video_mirror_size(const struct dmabuf_slice_data *info, int *new_width,
int *new_height, int *new_strides, int *new_offsets,
int *new_min_size)
static void copy_onto_video_mirror(const char *buffer, AVFrame *frame,
const struct dmabuf_slice_data *info)
{
/* Encoding video with YUV420P is significantly faster than encoding
* video with the YUV444P format. However, when using YUV420P, x264
* imposes an additional condition, that the image width and height
* be divisible by 2, so that there are no UV entries covering less than
* a 2x2 field. Furthermore, if the image sizes for sws_scale disagree,
* then the function becomes significantly more expensive.
*
* This isn't the only constraint; with recent ffmpeg, YUV->RGB
* conversions crash if the strides of the output are not divisible
* by 16.
*
* One solution for this problem is to copy the buffer onto a temporary
* space (which is necessary anyway, because scaling may overread by
* e.g. 16 bytes; it is not safe to overshoot the end of a mapped
* dmabuf). Since we're already copying the data, we may as well shift
* the image lines slightly to ensure good alignment. This probably is
* not more more expensive than memcpy as we may still be bandwidth
* limited.
**/
int nwidth = align((int)info->width, 2);
int nheight = align((int)info->height, 2);
if (new_width) {
*new_width = nwidth;
}
if (new_height) {
*new_height = nheight;
}
int base_offset = 0;
for (int i = 0; i < info->num_planes; i++) {
int nstride = align((int)info->strides[i], 16);
if (new_strides) {
new_strides[i] = nstride;
int j = i;
if (needs_vu_flip(info->format) && (i == 1 || i == 2)) {
j = 3 - i;
}
if (new_offsets) {
new_offsets[i] = base_offset;
for (size_t r = 0; r < info->height; r++) {
uint8_t *dst = frame->data[j] +
frame->linesize[j] * (int)r;
const char *src = buffer + (size_t)info->offsets[i] +
(size_t)info->strides[i] * r;
memcpy(dst, src, (size_t)info->strides[i]);
}
// TODO: handle subsampled planes efficiently, switch by format
base_offset += nstride * nheight;
}
if (new_min_size) {
*new_min_size = base_offset;
}
}
void copy_onto_video_mirror(char *buffer, char *mirror,
static void copy_from_video_mirror(char *buffer, const AVFrame *frame,
const struct dmabuf_slice_data *info)
{
int new_strides[4], new_offsets[4], new_min_size, new_width, new_height;
get_video_mirror_size(info, &new_width, &new_height, new_strides,
new_offsets, &new_min_size);
for (int i = 0; i < info->num_planes; i++) {
if (new_strides[i] == (int)info->strides[i] &&
new_height == (int)info->height) {
/* Fast case: no structural changes */
memcpy(mirror + (size_t)new_offsets[i],
buffer + (size_t)info->offsets[i],
(size_t)(new_strides[i] * new_height));
} else {
for (size_t r = 0; r < (size_t)new_height; r++) {
memcpy(mirror + new_offsets[i] +
(size_t)new_strides[i] *
r,
buffer + (size_t)info->offsets[i] +
(size_t)info->strides[i] *
r,
(size_t)info->strides[i]);
}
int j = i;
if (needs_vu_flip(info->format) && (i == 1 || i == 2)) {
j = 3 - i;
}
}
}
void copy_from_video_mirror(char *buffer, char *mirror,
const struct dmabuf_slice_data *info)
{
int new_strides[4], new_offsets[4], new_min_size, new_width, new_height;
get_video_mirror_size(info, &new_width, &new_height, new_strides,
new_offsets, &new_min_size);
for (int i = 0; i < info->num_planes; i++) {
if (new_strides[i] == (int)info->strides[i] &&
new_height == (int)info->height) {
/* Fast case: no structural changes */
memcpy(buffer + (size_t)info->offsets[i],
mirror + (size_t)new_offsets[i],
(size_t)(new_strides[i] * new_height));
} else {
for (size_t r = 0; r < (size_t)new_height; r++) {
memcpy(buffer + (size_t)info->offsets[i] +
(size_t)info->strides[i] *
r,
mirror + (size_t)new_offsets[i] +
(size_t)new_strides[i] *
r,
(size_t)info->strides[i]);
}
for (size_t r = 0; r < info->height; r++) {
const uint8_t *src = frame->data[j] +
frame->linesize[j] * (int)r;
char *dst = buffer + (size_t)info->offsets[i] +
(size_t)info->strides[i] * r;
memcpy(dst, src, (size_t)info->strides[i]);
}
}
}
......@@ -690,12 +615,13 @@ void cleanup_hwcontext(struct render_data *rd)
}
}
static void configure_low_latency_enc_context(
struct AVCodecContext *ctx, bool sw)
static void configure_low_latency_enc_context(struct AVCodecContext *ctx,
bool sw, enum video_coding_fmt fmt, int bpf)
{
// "time" is only meaningful in terms of the frames provided
ctx->time_base = (AVRational){1, 25};
ctx->framerate = (AVRational){25, 1};
int nom_fps = 25;
ctx->time_base = (AVRational){1, nom_fps};
ctx->framerate = (AVRational){nom_fps, 1};
/* B-frames are directly tied to latency, since each one
* is predicted using its preceding and following
......@@ -707,22 +633,42 @@ static void configure_low_latency_enc_context(
ctx->thread_count = 1;
if (sw) {
ctx->bit_rate = 3000000;
if (av_opt_set(ctx->priv_data, "preset", "ultrafast", 0) != 0) {
wp_error("Failed to set x264 encode ultrafast preset");
}
if (av_opt_set(ctx->priv_data, "tune", "zerolatency", 0) != 0) {
wp_error("Failed to set x264 encode zerolatency");
ctx->bit_rate = bpf * nom_fps;
if (fmt == VIDEO_H264) {
if (av_opt_set(ctx->priv_data, "preset", "ultrafast",
0) != 0) {
wp_error("Failed to set x264 encode ultrafast preset");
}
if (av_opt_set(ctx->priv_data, "tune", "zerolatency",
0) != 0) {
wp_error("Failed to set x264 encode zerolatency");
}
} else if (fmt == VIDEO_VP9) {
if (av_opt_set(ctx->priv_data, "lag-in-frames", "0",
0) != 0) {
wp_error("Failed to set vp9 encode lag");
}
if (av_opt_set(ctx->priv_data, "quality", "realtime",
0) != 0) {
wp_error("Failed to set vp9 quality");
}
if (av_opt_set(ctx->priv_data, "speed", "8", 0) != 0) {
wp_error("Failed to set vp9 speed");
}
}
} else {
/* with i965/gen8, hardware encoding is faster but has
* significantly worse quality per bitrate than x264 */
ctx->bit_rate = 9000000;
if (av_opt_set(ctx->priv_data, "quality", "7", 0) != 0) {
wp_error("Failed to set h264 encode quality");
}
if (av_opt_set(ctx->priv_data, "profile", "main", 0) != 0) {
wp_error("Failed to set h264 encode main profile");
ctx->bit_rate = bpf * nom_fps;
if (fmt == VIDEO_H264) {
/* with i965/gen8, hardware encoding is faster but has
* significantly worse quality per bitrate than x264 */
if (av_opt_set(ctx->priv_data, "quality", "7", 0) !=
0) {
wp_error("Failed to set h264 encode quality");
}
if (av_opt_set(ctx->priv_data, "profile", "main", 0) !=
0) {
wp_error("Failed to set h264 encode main profile");
}
}
}
}
......@@ -733,13 +679,17 @@ static int setup_hwvideo_encode(struct shadow_fd *sfd, struct render_data *rd)
* intel-vaapi-driver/src/i965_drv_video.c . Packed formats like
* YUV420P typically don't work. */
const enum AVPixelFormat videofmt = AV_PIX_FMT_NV12;
struct AVCodec *codec = avcodec_find_encoder_by_name(VIDEO_HW_ENCODER);
const char *hw_encoder = rd->av_video_fmt == VIDEO_H264
? VIDEO_H264_HW_ENCODER
: VIDEO_VP9_HW_ENCODER;
struct AVCodec *codec = avcodec_find_encoder_by_name(hw_encoder);
if (!codec) {
wp_error("Failed to find encoder \"" VIDEO_HW_ENCODER "\"");
wp_error("Failed to find encoder \"%s\"", hw_encoder);
return -1;
}
struct AVCodecContext *ctx = avcodec_alloc_context3(codec);
configure_low_latency_enc_context(ctx, false);
configure_low_latency_enc_context(
ctx, false, rd->av_video_fmt, rd->av_bpf);
if (!pad_hardware_size((int)sfd->dmabuf_info.width,
(int)sfd->dmabuf_info.height, &ctx->width,
&ctx->height)) {
......@@ -892,28 +842,6 @@ fail_alignment:
return -1;
}
static void setup_avframe_entries(struct AVFrame *frame,
struct dmabuf_slice_data *info, uint8_t *buffer)
{
int new_strides[4], new_offsets[4], new_min_size, new_width, new_height;
get_video_mirror_size(info, &new_width, &new_height, new_strides,
new_offsets, &new_min_size);
bool vu_flip = needs_vu_flip(info->format);
for (int i = 0; i < (int)info->num_planes; i++) {
int j = i;
if (vu_flip && i == 1)
j = 2;
if (vu_flip && i == 2)
j = 1;
frame->linesize[i] = new_strides[j];
frame->data[i] = buffer + new_offsets[j];
}
for (int i = (int)info->num_planes; i < AV_NUM_DATA_POINTERS; i++) {
frame->data[i] = NULL;
}
}
int setup_video_encode(struct shadow_fd *sfd, struct render_data *rd)
{
bool has_hw = init_hwcontext(rd) == 0;
......@@ -940,38 +868,49 @@ int setup_video_encode(struct shadow_fd *sfd, struct render_data *rd)
av_get_pix_fmt_name(videofmt));
return -1;
}
struct AVCodec *codec = avcodec_find_encoder_by_name(VIDEO_SW_ENCODER);
const char *sw_encoder = rd->av_video_fmt == VIDEO_H264
? VIDEO_H264_SW_ENCODER
: VIDEO_VP9_SW_ENCODER;
struct AVCodec *codec = avcodec_find_encoder_by_name(sw_encoder);
if (!codec) {
wp_error("Failed to find encoder for h264");
wp_error("Failed to find encoder \"%s\"", sw_encoder);
return -1;
}
struct AVCodecContext *ctx = avcodec_alloc_context3(codec);
ctx->pix_fmt = videofmt;
configure_low_latency_enc_context(
ctx, true, rd->av_video_fmt, rd->av_bpf);
struct AVPacket *pkt = av_packet_alloc();
/* Increase image sizes as needed to ensure codec can run */
ctx->width = (int)sfd->dmabuf_info.width;
ctx->height = (int)sfd->dmabuf_info.height;
int linesize_align[AV_NUM_DATA_POINTERS];
avcodec_align_dimensions2(
ctx, &ctx->width, &ctx->height, linesize_align);
get_video_mirror_size(&sfd->dmabuf_info, &ctx->width, &ctx->height,
NULL, NULL, NULL);
ctx->pix_fmt = videofmt;
configure_low_latency_enc_context(ctx, true);
struct AVPacket *pkt = av_packet_alloc();
if (avcodec_open2(ctx, codec, NULL) < 0) {
wp_error("Failed to open codec");
return -1;
}
struct AVFrame *frame = av_frame_alloc();
if (!frame) {
struct AVFrame *local_frame = av_frame_alloc();
if (!local_frame) {
wp_error("Could not allocate video frame");
return -1;
}
setup_avframe_entries(
frame, &sfd->dmabuf_info, (uint8_t *)sfd->mem_mirror);
frame->format = avpixfmt;
local_frame->format = avpixfmt;
/* adopt padded sizes */
frame->width = ctx->width;
frame->height = ctx->height;
local_frame->width = ctx->width;
local_frame->height = ctx->height;
if (av_image_alloc(local_frame->data, local_frame->linesize,
local_frame->width, local_frame->height, avpixfmt,
64) < 0) {
wp_error("Failed to allocate temp image");
return -1;
}
struct AVFrame *yuv_frame = av_frame_alloc();
yuv_frame->width = ctx->width;
......@@ -983,9 +922,10 @@ int setup_video_encode(struct shadow_fd *sfd, struct render_data *rd)
wp_error("Failed to allocate temp image");
return -1;
}
struct SwsContext *sws = sws_getContext(frame->width, frame->height,
avpixfmt, yuv_frame->width, yuv_frame->height, videofmt,
SWS_BILINEAR, NULL, NULL, NULL);
struct SwsContext *sws = sws_getContext(local_frame->width,
local_frame->height, avpixfmt, yuv_frame->width,
yuv_frame->height, videofmt, SWS_BILINEAR, NULL, NULL,
NULL);
if (!sws) {
wp_error("Could not create software color conversion context");
return -1;
......@@ -994,7 +934,8 @@ int setup_video_encode(struct shadow_fd *sfd, struct render_data *rd)
sfd->video_yuv_frame = yuv_frame;
/* recorded pointer to be freed to match av_image_alloc */
sfd->video_yuv_frame_data = &yuv_frame->data[0];
sfd->video_local_frame = frame;
sfd->video_local_frame = local_frame;
sfd->video_local_frame_data = &local_frame->data[0];
sfd->video_packet = pkt;
sfd->video_context = ctx;
sfd->video_color_context = sws;
......@@ -1038,9 +979,12 @@ int setup_video_decode(struct shadow_fd *sfd, struct render_data *rd)
return -1;
}
struct AVCodec *codec = avcodec_find_decoder_by_name(VIDEO_DECODER);
const char *decoder = rd->av_video_fmt == VIDEO_H264
? VIDEO_H264_DECODER
: VIDEO_VP9_DECODER;
struct AVCodec *codec = avcodec_find_decoder_by_name(decoder);
if (!codec) {
wp_error("Failed to find decoder \"" VIDEO_DECODER "\"");
wp_error("Failed to find decoder \"%s\"", decoder);
return -1;
}
......@@ -1067,9 +1011,13 @@ int setup_video_decode(struct shadow_fd *sfd, struct render_data *rd)
ctx->get_format = get_decode_format;
} else {
ctx->pix_fmt = videofmt;
/* set context dimensions */
get_video_mirror_size(&sfd->dmabuf_info, &ctx->width,
&ctx->height, NULL, NULL, NULL);
/* set context dimensions, and allocate buffer to write into */
ctx->width = (int)sfd->dmabuf_info.width;
ctx->height = (int)sfd->dmabuf_info.height;
int linesize_align[AV_NUM_DATA_POINTERS];
avcodec_align_dimensions2(
ctx, &ctx->width, &ctx->height, linesize_align);
}
if (avcodec_open2(ctx, codec, NULL) < 0) {
wp_error("Failed to open codec");
......@@ -1116,8 +1064,8 @@ void collect_video_from_mirror(
if (!data) {
return;
}
copy_onto_video_mirror(
data, sfd->mem_mirror, &sfd->dmabuf_info);
copy_onto_video_mirror(data, sfd->video_local_frame,
&sfd->dmabuf_info);
unmap_dmabuf(sfd->dmabuf_bo, handle);
if (sws_scale(sfd->video_color_context,
......@@ -1174,29 +1122,36 @@ static int setup_color_conv(struct shadow_fd *sfd, struct AVFrame *cpu_frame)
enum AVPixelFormat avpixfmt = drm_to_av(sfd->dmabuf_info.format);
struct AVFrame *frame = av_frame_alloc();
if (!frame) {
struct AVFrame *local_frame = av_frame_alloc();
if (!local_frame) {
wp_error("Could not allocate video frame");
return -1;
}
frame->format = avpixfmt;
local_frame->format = avpixfmt;
/* adopt padded sizes */
frame->width = ctx->width;
frame->height = ctx->height;
setup_avframe_entries(
frame, &sfd->dmabuf_info, (uint8_t *)sfd->mem_mirror);
local_frame->width = ctx->width;
local_frame->height = ctx->height;
if (av_image_alloc(local_frame->data, local_frame->linesize,
local_frame->width, local_frame->height, avpixfmt,
64) < 0) {
wp_error("Failed to allocate local image");
av_frame_free(&local_frame);
return -1;
}
struct SwsContext *sws = sws_getContext(cpu_frame->width,
cpu_frame->height, cpu_frame->format, frame->width,
frame->height, avpixfmt, SWS_BILINEAR, NULL, NULL,
NULL);
cpu_frame->height, cpu_frame->format,
local_frame->width, local_frame->height, avpixfmt,
SWS_BILINEAR, NULL, NULL, NULL);
if (!sws) {
wp_error("Could not create software color conversion context");
av_frame_free(&frame);
av_freep(&local_frame->data[0]);
av_frame_free(&local_frame);
return -1;
}
sfd->video_local_frame = frame;
sfd->video_local_frame = local_frame;
sfd->video_local_frame_data = &local_frame->data[0];
sfd->video_color_context = sws;
return 0;
}
......@@ -1204,7 +1159,6 @@ static int setup_color_conv(struct shadow_fd *sfd, struct AVFrame *cpu_frame)
void apply_video_packet(struct shadow_fd *sfd, struct render_data *rd,
const struct bytebuf *msg)
{
// padding, requires zerod overflow for read
sfd->video_packet->data = (uint8_t *)msg->data;
sfd->video_packet->size = (int)msg->size;
......@@ -1283,7 +1237,7 @@ void apply_video_packet(struct shadow_fd *sfd, struct render_data *rd,
if (!data) {
return;
}
copy_from_video_mirror(data, sfd->mem_mirror,
copy_from_video_mirror(data, sfd->video_local_frame,
&sfd->dmabuf_info);
unmap_dmabuf(sfd->dmabuf_bo, handle);
} else {
......