*****************************************************************************/
struct picture_gc_sys_t {
picture_pool_t *pool;
- /* Saved release */
- void (*destroy)(picture_t *);
- void *destroy_sys;
- /* */
- atomic_bool zombie;
- int64_t tick;
+ picture_t *picture;
+ bool in_use;
+ uint64_t tick;
};
struct picture_pool_t {
+ uint64_t tick;
/* */
- picture_pool_t *master;
- int64_t tick;
- /* */
- int picture_count;
+ unsigned picture_count;
picture_t **picture;
- bool *picture_reserved;
int (*pic_lock)(picture_t *);
void (*pic_unlock)(picture_t *);
vlc_mutex_t lock;
};
-static void Release(picture_pool_t *pool)
+void picture_pool_Release(picture_pool_t *pool)
{
bool destroy;
vlc_mutex_lock(&pool->lock);
assert(pool->refs > 0);
- destroy = !--pool->refs;
+ destroy = --pool->refs == 0;
vlc_mutex_unlock(&pool->lock);
- if (!destroy)
+ if (likely(!destroy))
return;
+ for (unsigned i = 0; i < pool->picture_count; i++) {
+ picture_t *picture = pool->picture[i];
+ picture_gc_sys_t *sys = picture->gc.p_sys;
+
+ picture_Release(sys->picture);
+ free(sys);
+ free(picture);
+ }
+
vlc_mutex_destroy(&pool->lock);
- free(pool->picture_reserved);
free(pool->picture);
free(pool);
}
-static void DestroyPicture(picture_t *picture)
+static void picture_pool_ReleasePicture(picture_t *picture)
{
- picture_gc_sys_t *gc_sys = picture->gc.p_sys;
- picture_pool_t *pool = gc_sys->pool;
+ picture_gc_sys_t *sys = picture->gc.p_sys;
+ picture_pool_t *pool = sys->pool;
if (pool->pic_unlock != NULL)
pool->pic_unlock(picture);
- if (!atomic_load(&gc_sys->zombie))
- return;
+ vlc_mutex_lock(&pool->lock);
+ assert(sys->in_use);
+ sys->in_use = false;
+ vlc_mutex_unlock(&pool->lock);
+
+ picture_pool_Release(pool);
+}
+
+static picture_t *picture_pool_ClonePicture(picture_pool_t *pool,
+ picture_t *picture)
+{
+ picture_gc_sys_t *sys = malloc(sizeof(*sys));
+ if (unlikely(sys == NULL))
+ return NULL;
+
+ sys->pool = pool;
+ sys->picture = picture;
+ sys->in_use = false;
+ sys->tick = 0;
+
+ picture_resource_t res = {
+ .p_sys = picture->p_sys,
+ .pf_destroy = picture_pool_ReleasePicture,
+ };
+
+ for (int i = 0; i < picture->i_planes; i++) {
+ res.p[i].p_pixels = picture->p[i].p_pixels;
+ res.p[i].i_lines = picture->p[i].i_lines;
+ res.p[i].i_pitch = picture->p[i].i_pitch;
+ }
- /* Picture from an already destroyed pool */
- picture->gc.pf_destroy = gc_sys->destroy;
- picture->gc.p_sys = gc_sys->destroy_sys;
- free(gc_sys);
+ picture_t *clone = picture_NewFromResource(&picture->format, &res);
+ if (likely(clone != NULL))
+ clone->gc.p_sys = sys;
+ else
+ free(sys);
- picture->gc.pf_destroy(picture);
- Release(pool);
+ return clone;
}
-static picture_pool_t *Create(picture_pool_t *master, int picture_count)
+static picture_pool_t *Create(int picture_count)
{
picture_pool_t *pool = calloc(1, sizeof(*pool));
if (!pool)
return NULL;
- pool->master = master;
- pool->tick = master ? master->tick : 1;
+ pool->tick = 1;
pool->picture_count = picture_count;
pool->picture = calloc(pool->picture_count, sizeof(*pool->picture));
- pool->picture_reserved = calloc(pool->picture_count, sizeof(*pool->picture_reserved));
- if (!pool->picture || !pool->picture_reserved) {
+ if (!pool->picture) {
free(pool->picture);
- free(pool->picture_reserved);
free(pool);
return NULL;
}
picture_pool_t *picture_pool_NewExtended(const picture_pool_configuration_t *cfg)
{
- picture_pool_t *pool = Create(NULL, cfg->picture_count);
+ picture_pool_t *pool = Create(cfg->picture_count);
if (!pool)
return NULL;
pool->pic_lock = cfg->lock;
pool->pic_unlock = cfg->unlock;
- /*
- * NOTE: When a pooled picture is released, it must be returned to the list
- * of available pictures from its pool, rather than destroyed.
- * This requires a dedicated release callback, a pointer to the pool and a
- * reference count. For simplicity, rather than allocate a whole new
- * picture_t structure, the pool overrides gc.pf_destroy and gc.p_sys when
- * created, and restores them when destroyed.
- * There are some implications to keep in mind:
- * - The original creator of the picture (e.g. video output display) must
- * not manipulate the gc parameters while the picture is pooled.
- * - The picture cannot be pooled more than once, in other words, pools
- * cannot be stacked/layered.
- * - The picture must be available and its reference count equal to one
- * when it gets pooled.
- * - Picture plane pointers and sizes must not be mangled in any case.
- */
- for (int i = 0; i < cfg->picture_count; i++) {
- picture_t *picture = cfg->picture[i];
-
- /* Save the original garbage collector */
- picture_gc_sys_t *gc_sys = malloc(sizeof(*gc_sys));
- if (unlikely(gc_sys == NULL))
+ for (unsigned i = 0; i < cfg->picture_count; i++) {
+ picture_t *picture = picture_pool_ClonePicture(pool, cfg->picture[i]);
+ if (unlikely(picture == NULL))
abort();
- gc_sys->pool = pool;
- gc_sys->destroy = picture->gc.pf_destroy;
- gc_sys->destroy_sys = picture->gc.p_sys;
- atomic_init(&gc_sys->zombie, false);
- gc_sys->tick = 0;
-
- /* Override the garbage collector */
- assert(atomic_load(&picture->gc.refcount) == 1);
+
atomic_init(&picture->gc.refcount, 0);
- picture->gc.pf_destroy = DestroyPicture;
- picture->gc.p_sys = gc_sys;
- /* */
pool->picture[i] = picture;
- pool->picture_reserved[i] = false;
- pool->refs++;
}
return pool;
}
-picture_pool_t *picture_pool_New(int picture_count, picture_t *picture[])
+picture_pool_t *picture_pool_New(unsigned count, picture_t *const *tab)
{
picture_pool_configuration_t cfg;
memset(&cfg, 0, sizeof(cfg));
- cfg.picture_count = picture_count;
- cfg.picture = picture;
+ cfg.picture_count = count;
+ cfg.picture = tab;
return picture_pool_NewExtended(&cfg);
}
-picture_pool_t *picture_pool_NewFromFormat(const video_format_t *fmt, int picture_count)
+picture_pool_t *picture_pool_NewFromFormat(const video_format_t *fmt,
+ unsigned count)
{
- picture_t *picture[picture_count];
+ picture_t *picture[count ? count : 1];
+ unsigned i;
- for (int i = 0; i < picture_count; i++) {
+ for (i = 0; i < count; i++) {
picture[i] = picture_NewFromFormat(fmt);
- if (!picture[i])
+ if (picture[i] == NULL)
goto error;
}
- picture_pool_t *pool = picture_pool_New(picture_count, picture);
+
+ picture_pool_t *pool = picture_pool_New(count, picture);
if (!pool)
goto error;
return pool;
error:
- for (int i = 0; i < picture_count; i++) {
- if (!picture[i])
- break;
- picture_Release(picture[i]);
- }
+ while (i > 0)
+ picture_Release(picture[--i]);
return NULL;
}
-picture_pool_t *picture_pool_Reserve(picture_pool_t *master, int count)
+picture_pool_t *picture_pool_Reserve(picture_pool_t *master, unsigned count)
{
- picture_pool_t *pool = Create(master, count);
+ picture_t *picture[count ? count : 1];
+ unsigned i;
+
+ for (i = 0; i < count; i++) {
+ picture[i] = picture_pool_Get(master);
+ if (picture[i] == NULL)
+ goto error;
+ }
+
+ picture_pool_t *pool = picture_pool_New(count, picture);
if (!pool)
- return NULL;
+ goto error;
pool->pic_lock = master->pic_lock;
pool->pic_unlock = master->pic_unlock;
-
- int found = 0;
- for (int i = 0; i < master->picture_count && found < count; i++) {
- if (master->picture_reserved[i])
- continue;
-
- assert(atomic_load(&master->picture[i]->gc.refcount) == 0);
- master->picture_reserved[i] = true;
-
- pool->picture[found] = master->picture[i];
- pool->picture_reserved[found] = false;
- found++;
- }
- if (found < count) {
- picture_pool_Delete(pool);
- return NULL;
- }
return pool;
-}
-void picture_pool_Delete(picture_pool_t *pool)
-{
- for (int i = 0; i < pool->picture_count; i++) {
- picture_t *picture = pool->picture[i];
- if (pool->master) {
- for (int j = 0; j < pool->master->picture_count; j++) {
- if (pool->master->picture[j] == picture)
- pool->master->picture_reserved[j] = false;
- }
- } else {
- picture_gc_sys_t *gc_sys = picture->gc.p_sys;
-
- assert(!pool->picture_reserved[i]);
-
- /* Restore the initial reference that was cloberred in
- * picture_pool_NewExtended(). */
- atomic_fetch_add(&picture->gc.refcount, 1);
- /* The picture might still locked and then the G.C. state cannot be
- * modified (w/o memory synchronization). */
- atomic_store(&gc_sys->zombie, true);
-
- picture_Release(picture);
- }
- }
- Release(pool);
+error:
+ while (i > 0)
+ picture_Release(picture[--i]);
+ return NULL;
}
picture_t *picture_pool_Get(picture_pool_t *pool)
{
- for (int i = 0; i < pool->picture_count; i++) {
- if (pool->picture_reserved[i])
- continue;
+ vlc_mutex_lock(&pool->lock);
+ assert(pool->refs > 0);
+ for (unsigned i = 0; i < pool->picture_count; i++) {
picture_t *picture = pool->picture[i];
- uintptr_t refs = 0;
+ picture_gc_sys_t *sys = picture->gc.p_sys;
+ uint64_t tick;
- if (!atomic_compare_exchange_strong(&picture->gc.refcount, &refs, 1))
+ if (sys->in_use)
continue;
+ pool->refs++;
+ tick = ++pool->tick;
+ sys->in_use = true;
+ vlc_mutex_unlock(&pool->lock);
+
if (pool->pic_lock != NULL && pool->pic_lock(picture) != 0) {
- atomic_store(&picture->gc.refcount, 0);
+ vlc_mutex_lock(&pool->lock);
+ sys->in_use = false;
+ pool->refs--;
+ vlc_mutex_unlock(&pool->lock);
continue;
}
- /* */
+ sys->tick = tick;
+
+ assert(atomic_load(&picture->gc.refcount) == 0);
+ atomic_init(&picture->gc.refcount, 1);
picture->p_next = NULL;
- picture->gc.p_sys->tick = pool->tick++;
return picture;
}
+
+ vlc_mutex_unlock(&pool->lock);
return NULL;
}
-void picture_pool_Reset(picture_pool_t *pool)
+unsigned picture_pool_Reset(picture_pool_t *pool)
{
- for (int i = 0; i < pool->picture_count; i++) {
- if (pool->picture_reserved[i])
- continue;
+ unsigned ret = 0;
+retry:
+ vlc_mutex_lock(&pool->lock);
+ assert(pool->refs > 0);
+ for (unsigned i = 0; i < pool->picture_count; i++) {
picture_t *picture = pool->picture[i];
- if (atomic_load(&picture->gc.refcount) > 0) {
- if (pool->pic_unlock != NULL)
- pool->pic_unlock(picture);
+ picture_gc_sys_t *sys = picture->gc.p_sys;
+
+ if (sys->in_use) {
+ vlc_mutex_unlock(&pool->lock);
+ picture_Release(picture);
+ ret++;
+ goto retry;
}
- atomic_store(&picture->gc.refcount, 0);
}
+ vlc_mutex_unlock(&pool->lock);
+
+ return ret;
}
void picture_pool_NonEmpty(picture_pool_t *pool)
{
picture_t *oldest = NULL;
+ uint64_t tick = 0;
- for (int i = 0; i < pool->picture_count; i++) {
- if (pool->picture_reserved[i])
- continue;
+ vlc_mutex_lock(&pool->lock);
+ assert(pool->refs > 0);
+ for (unsigned i = 0; i < pool->picture_count; i++) {
picture_t *picture = pool->picture[i];
- if (atomic_load(&picture->gc.refcount) == 0)
+ picture_gc_sys_t *sys = picture->gc.p_sys;
+
+ if (!sys->in_use) {
+ vlc_mutex_unlock(&pool->lock);
return; /* Nothing to do */
+ }
- if (oldest == NULL || picture->gc.p_sys->tick < oldest->gc.p_sys->tick)
+ if (picture->gc.p_sys->tick < tick) {
oldest = picture;
+ tick = picture->gc.p_sys->tick;
+ }
}
- if (oldest == NULL)
- return; /* Cannot fix! */
-
- if (atomic_load(&oldest->gc.refcount) > 0) {
- if (pool->pic_unlock != NULL)
- pool->pic_unlock(oldest);
+ if (oldest != NULL) {
+ while (oldest->gc.p_sys->in_use) {
+ vlc_mutex_unlock(&pool->lock);
+ picture_Release(oldest);
+ vlc_mutex_lock(&pool->lock);
+ }
}
- atomic_store(&oldest->gc.refcount, 0);
+
+ vlc_mutex_unlock(&pool->lock);
}
-int picture_pool_GetSize(picture_pool_t *pool)
+unsigned picture_pool_GetSize(const picture_pool_t *pool)
{
return pool->picture_count;
}