]> git.sesse.net Git - vlc/blobdiff - src/misc/picture_pool.c
picture_pool: fix prototype, fix warnings
[vlc] / src / misc / picture_pool.c
index 5d5430122a04dc8c14c62df8548326b18b142f2b..e899f9eabbc5e46ea945822500a438a384c83dfc 100644 (file)
  *****************************************************************************/
 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 *);
@@ -62,58 +56,91 @@ struct picture_pool_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;
     }
@@ -124,214 +151,182 @@ static picture_pool_t *Create(picture_pool_t *master, int picture_count)
 
 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;
 }