module-client-node: allocate port mix buffers dynamically

Each port mix embedded its buffer table in struct mix as a fixed array
buffers[MAX_BUFFERS] (MAX_BUFFERS == 64), and every struct buffer in turn
embedded datas[MAX_DATAS] (MAX_DATAS == 256) and metas[MAX_METAS]. This
reserved ~674 KB per mix in create_mix() regardless of the actual buffer,
data and meta counts, multiplied by every port mix (one per link end).

Turn buffers, datas and metas into pointers and allocate the buffer table
together with the per-buffer data and meta pools in a single calloc in
do_port_use_buffers(), sized to the real number of buffers and their
n_datas/n_metas, and release it in clear_buffers(). create_mix() now starts
with an empty (NULL) table, so an idle mix costs only the struct header.
This mirrors the dynamic-allocation approach already used for the
audioconvert and audiomixer port buffers and removes the dominant heap and
mmap consumer in the client-node graph.

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
Torkel Niklasson 2026-06-16 15:56:24 +02:00
parent 8b926ff084
commit 650a96b8aa

View file

@ -44,8 +44,8 @@ PW_LOG_TOPIC_EXTERN(mod_topic);
struct buffer {
struct spa_buffer *outbuf;
struct spa_buffer buffer;
struct spa_meta metas[MAX_METAS];
struct spa_data datas[MAX_DATAS];
struct spa_meta *metas;
struct spa_data *datas;
struct pw_memblock *mem;
};
@ -55,7 +55,7 @@ struct mix {
uint32_t peer_id;
uint32_t n_buffers;
uint32_t impl_mix_id;
struct buffer buffers[MAX_BUFFERS];
struct buffer *buffers;
};
struct params {
@ -286,6 +286,8 @@ static int clear_buffers(struct impl *impl, struct mix *mix)
clear_buffer(impl, &b->buffer);
pw_memblock_unref(b->mem);
}
free(mix->buffers);
mix->buffers = NULL;
mix->n_buffers = 0;
return 0;
}
@ -807,6 +809,37 @@ do_port_use_buffers(struct impl *impl,
if (p->destroyed)
return 0;
if (n_buffers > 0) {
size_t buffers_size = n_buffers * sizeof(struct buffer);
uint32_t n_datas = 0, n_metas = 0;
void *ptr;
/* size the data/meta pools to the buffers actually in use
* instead of reserving MAX_DATAS/MAX_METAS per buffer */
for (i = 0; i < n_buffers; i++) {
n_datas += SPA_MIN(buffers[i]->n_datas, MAX_DATAS);
n_metas += SPA_MIN(buffers[i]->n_metas, MAX_METAS);
}
mix->buffers = calloc(1, buffers_size +
n_datas * sizeof(struct spa_data) +
n_metas * sizeof(struct spa_meta));
if (mix->buffers == NULL)
return -errno;
ptr = SPA_PTROFF(mix->buffers, buffers_size, void);
for (i = 0; i < n_buffers; i++) {
mix->buffers[i].datas = ptr;
ptr = SPA_PTROFF(ptr,
SPA_MIN(buffers[i]->n_datas, MAX_DATAS) * sizeof(struct spa_data), void);
}
for (i = 0; i < n_buffers; i++) {
mix->buffers[i].metas = ptr;
ptr = SPA_PTROFF(ptr,
SPA_MIN(buffers[i]->n_metas, MAX_METAS) * sizeof(struct spa_meta), void);
}
}
for (i = 0; i < n_buffers; i++) {
struct buffer *b = &mix->buffers[i];
struct pw_memblock *mem, *m;