mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
impl-node: implement async scheduling
When node.async is set, make the node async. Advertize SPA_IO_AsyncBuffers on mixer ports when supported. Set a new port flag when AsyncBuffer is supported on the port. When making a link and if one of the nodes is async and the linked ports support AsyncBuffer, make the link async and set this as a property on the link. For async nodes we will use SPA_IO_AsyncBuffers on the mixer ports. Nodes that are async will not increment the peer required counters. This ensures that the peer can start immediately before the async node is ready. On an async link, writers will write to the (cycle+1 & 1) async buffers entry and readers will read from (cycle & 1). This makes the readers read from the previously filled area. We need to have two very controlled areas with specific rules for who reads and who writes where because the two nodes will run concurrently and no special synchronization is possible otherwise. These async nodes can be paused and blocked without blocking or xrunning the rest of graph. If the node didn't produce anything when the next cycle starts, the graph will run with silence. See #3509
This commit is contained in:
parent
e8ac4e6a34
commit
68916e062b
11 changed files with 324 additions and 112 deletions
|
|
@ -41,7 +41,7 @@ struct port {
|
|||
uint32_t direction;
|
||||
uint32_t id;
|
||||
|
||||
struct spa_io_buffers *io;
|
||||
struct spa_io_buffers *io[2];
|
||||
|
||||
uint64_t info_all;
|
||||
struct spa_port_info info;
|
||||
|
|
@ -68,6 +68,8 @@ struct impl {
|
|||
struct spa_node_info info;
|
||||
struct spa_param_info params[8];
|
||||
|
||||
struct spa_io_position *position;
|
||||
|
||||
struct spa_hook_list hooks;
|
||||
|
||||
uint32_t port_count;
|
||||
|
|
@ -111,7 +113,16 @@ static int impl_node_set_param(void *object, uint32_t id, uint32_t flags,
|
|||
|
||||
static int impl_node_set_io(void *object, uint32_t id, void *data, size_t size)
|
||||
{
|
||||
return -ENOTSUP;
|
||||
struct impl *this = object;
|
||||
|
||||
switch (id) {
|
||||
case SPA_IO_Position:
|
||||
this->position = data;
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int impl_node_send_command(void *object, const struct spa_command *command)
|
||||
|
|
@ -353,6 +364,12 @@ next:
|
|||
SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_Buffers),
|
||||
SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_buffers)));
|
||||
break;
|
||||
case 1:
|
||||
param = spa_pod_builder_add_object(&b,
|
||||
SPA_TYPE_OBJECT_ParamIO, id,
|
||||
SPA_PARAM_IO_id, SPA_POD_Id(SPA_IO_AsyncBuffers),
|
||||
SPA_PARAM_IO_size, SPA_POD_Int(sizeof(struct spa_io_async_buffers)));
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -531,13 +548,24 @@ impl_node_port_use_buffers(void *object,
|
|||
struct io_info {
|
||||
struct port *port;
|
||||
void *data;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
static int do_port_set_io(struct spa_loop *loop, bool async, uint32_t seq,
|
||||
const void *data, size_t size, void *user_data)
|
||||
{
|
||||
struct io_info *info = user_data;
|
||||
info->port->io = info->data;
|
||||
if (info->size >= sizeof(struct spa_io_async_buffers)) {
|
||||
struct spa_io_async_buffers *ab = info->data;
|
||||
info->port->io[0] = &ab->buffers[0];
|
||||
info->port->io[1] = &ab->buffers[1];
|
||||
} else if (info->size >= sizeof(struct spa_io_buffers)) {
|
||||
info->port->io[0] = info->data;
|
||||
info->port->io[1] = info->data;
|
||||
} else {
|
||||
info->port->io[0] = NULL;
|
||||
info->port->io[1] = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -560,9 +588,11 @@ impl_node_port_set_io(void *object,
|
|||
port = GET_PORT(this, direction, port_id);
|
||||
info.port = port;
|
||||
info.data = data;
|
||||
info.size = size;
|
||||
|
||||
switch (id) {
|
||||
case SPA_IO_Buffers:
|
||||
case SPA_IO_AsyncBuffers:
|
||||
spa_loop_invoke(this->data_loop,
|
||||
do_port_set_io, SPA_ID_INVALID, NULL, 0, true, &info);
|
||||
break;
|
||||
|
|
@ -631,11 +661,12 @@ static int impl_node_process(void *object)
|
|||
struct spa_pod_frame f;
|
||||
struct buffer *outb;
|
||||
struct spa_data *d;
|
||||
uint32_t cycle = this->position->clock.cycle & 1;
|
||||
|
||||
spa_return_val_if_fail(this != NULL, -EINVAL);
|
||||
|
||||
outport = GET_OUT_PORT(this, 0);
|
||||
if ((outio = outport->io) == NULL)
|
||||
if ((outio = outport->io[cycle]) == NULL)
|
||||
return -EIO;
|
||||
|
||||
spa_log_trace_fp(this->log, "%p: status %p %d %d",
|
||||
|
|
@ -668,16 +699,17 @@ static int impl_node_process(void *object)
|
|||
struct spa_io_buffers *inio = NULL;
|
||||
void *pod;
|
||||
|
||||
if (!inport->valid ||
|
||||
(inio = inport->io) == NULL ||
|
||||
inio->buffer_id >= inport->n_buffers ||
|
||||
if (SPA_UNLIKELY(!PORT_VALID(inport) || (inio = inport->io[cycle]) == NULL)) {
|
||||
spa_log_trace_fp(this->log, "%p: skip input idx:%d valid:%d io:%p/%p/%d",
|
||||
this, i, PORT_VALID(inport),
|
||||
inport->io[0], inport->io[1], cycle);
|
||||
continue;
|
||||
}
|
||||
if (inio->buffer_id >= inport->n_buffers ||
|
||||
inio->status != SPA_STATUS_HAVE_DATA) {
|
||||
spa_log_trace_fp(this->log, "%p: skip input idx:%d valid:%d "
|
||||
spa_log_trace_fp(this->log, "%p: skip input idx:%d "
|
||||
"io:%p status:%d buf_id:%d n_buffers:%d", this,
|
||||
i, inport->valid, inio,
|
||||
inio ? inio->status : -1,
|
||||
inio ? inio->buffer_id : SPA_ID_INVALID,
|
||||
inport->n_buffers);
|
||||
i, inio, inio->status, inio->buffer_id, inport->n_buffers);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue