1 /*****************************************************************************
2 * picture_pool.c : picture pool functions
3 *****************************************************************************
4 * Copyright (C) 2009 VLC authors and VideoLAN
5 * Copyright (C) 2009 Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
8 * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
34 #include <vlc_common.h>
35 #include <vlc_picture_pool.h>
37 /*****************************************************************************
39 *****************************************************************************/
40 struct picture_gc_sys_t {
43 void (*destroy)(picture_t *);
50 struct picture_pool_t {
52 picture_pool_t *master;
55 unsigned picture_count;
57 bool *picture_reserved;
59 int (*pic_lock)(picture_t *);
60 void (*pic_unlock)(picture_t *);
65 static void Release(picture_pool_t *pool)
69 vlc_mutex_lock(&pool->lock);
70 assert(pool->refs > 0);
71 destroy = !--pool->refs;
72 vlc_mutex_unlock(&pool->lock);
77 vlc_mutex_destroy(&pool->lock);
78 free(pool->picture_reserved);
83 static void DestroyPicture(picture_t *picture)
85 picture_gc_sys_t *gc_sys = picture->gc.p_sys;
86 picture_pool_t *pool = gc_sys->pool;
88 if (pool->pic_unlock != NULL)
89 pool->pic_unlock(picture);
91 if (!atomic_load(&gc_sys->zombie))
94 /* Picture from an already destroyed pool */
95 picture->gc.pf_destroy = gc_sys->destroy;
96 picture->gc.p_sys = gc_sys->destroy_sys;
99 picture->gc.pf_destroy(picture);
103 static picture_pool_t *Create(picture_pool_t *master, int picture_count)
105 picture_pool_t *pool = calloc(1, sizeof(*pool));
109 pool->master = master;
110 pool->tick = master ? master->tick : 1;
111 pool->picture_count = picture_count;
112 pool->picture = calloc(pool->picture_count, sizeof(*pool->picture));
113 pool->picture_reserved = calloc(pool->picture_count, sizeof(*pool->picture_reserved));
114 if (!pool->picture || !pool->picture_reserved) {
116 free(pool->picture_reserved);
121 vlc_mutex_init(&pool->lock);
125 picture_pool_t *picture_pool_NewExtended(const picture_pool_configuration_t *cfg)
127 picture_pool_t *pool = Create(NULL, cfg->picture_count);
131 pool->pic_lock = cfg->lock;
132 pool->pic_unlock = cfg->unlock;
135 * NOTE: When a pooled picture is released, it must be returned to the list
136 * of available pictures from its pool, rather than destroyed.
137 * This requires a dedicated release callback, a pointer to the pool and a
138 * reference count. For simplicity, rather than allocate a whole new
139 * picture_t structure, the pool overrides gc.pf_destroy and gc.p_sys when
140 * created, and restores them when destroyed.
141 * There are some implications to keep in mind:
142 * - The original creator of the picture (e.g. video output display) must
143 * not manipulate the gc parameters while the picture is pooled.
144 * - The picture cannot be pooled more than once, in other words, pools
145 * cannot be stacked/layered.
146 * - The picture must be available and its reference count equal to one
147 * when it gets pooled.
148 * - Picture plane pointers and sizes must not be mangled in any case.
150 for (unsigned i = 0; i < cfg->picture_count; i++) {
151 picture_t *picture = cfg->picture[i];
153 /* Save the original garbage collector */
154 picture_gc_sys_t *gc_sys = malloc(sizeof(*gc_sys));
155 if (unlikely(gc_sys == NULL))
158 gc_sys->destroy = picture->gc.pf_destroy;
159 gc_sys->destroy_sys = picture->gc.p_sys;
160 atomic_init(&gc_sys->zombie, false);
163 /* Override the garbage collector */
164 assert(atomic_load(&picture->gc.refcount) == 1);
165 atomic_init(&picture->gc.refcount, 0);
166 picture->gc.pf_destroy = DestroyPicture;
167 picture->gc.p_sys = gc_sys;
170 pool->picture[i] = picture;
171 pool->picture_reserved[i] = false;
178 picture_pool_t *picture_pool_New(unsigned count, picture_t *const *tab)
180 picture_pool_configuration_t cfg;
182 memset(&cfg, 0, sizeof(cfg));
183 cfg.picture_count = count;
186 return picture_pool_NewExtended(&cfg);
189 picture_pool_t *picture_pool_NewFromFormat(const video_format_t *fmt,
192 picture_t *picture[count ? count : 1];
195 for (i = 0; i < count; i++) {
196 picture[i] = picture_NewFromFormat(fmt);
197 if (picture[i] == NULL)
201 picture_pool_t *pool = picture_pool_New(count, picture);
209 picture_Release(picture[--i]);
213 picture_pool_t *picture_pool_Reserve(picture_pool_t *master, unsigned count)
215 picture_pool_t *pool = Create(master, count);
219 pool->pic_lock = master->pic_lock;
220 pool->pic_unlock = master->pic_unlock;
223 for (unsigned i = 0; i < master->picture_count && found < count; i++) {
224 if (master->picture_reserved[i])
227 assert(atomic_load(&master->picture[i]->gc.refcount) == 0);
228 master->picture_reserved[i] = true;
230 pool->picture[found] = master->picture[i];
231 pool->picture_reserved[found] = false;
235 picture_pool_Delete(pool);
241 void picture_pool_Delete(picture_pool_t *pool)
243 for (unsigned i = 0; i < pool->picture_count; i++) {
244 picture_t *picture = pool->picture[i];
246 for (unsigned j = 0; j < pool->master->picture_count; j++) {
247 if (pool->master->picture[j] == picture)
248 pool->master->picture_reserved[j] = false;
251 picture_gc_sys_t *gc_sys = picture->gc.p_sys;
253 assert(!pool->picture_reserved[i]);
255 /* Restore the initial reference that was cloberred in
256 * picture_pool_NewExtended(). */
257 atomic_fetch_add(&picture->gc.refcount, 1);
258 /* The picture might still locked and then the G.C. state cannot be
259 * modified (w/o memory synchronization). */
260 atomic_store(&gc_sys->zombie, true);
262 picture_Release(picture);
268 picture_t *picture_pool_Get(picture_pool_t *pool)
270 for (unsigned i = 0; i < pool->picture_count; i++) {
271 if (pool->picture_reserved[i])
274 picture_t *picture = pool->picture[i];
277 if (!atomic_compare_exchange_strong(&picture->gc.refcount, &refs, 1))
280 if (pool->pic_lock != NULL && pool->pic_lock(picture) != 0) {
281 atomic_store(&picture->gc.refcount, 0);
286 picture->p_next = NULL;
287 picture->gc.p_sys->tick = pool->tick++;
293 void picture_pool_Reset(picture_pool_t *pool)
295 for (unsigned i = 0; i < pool->picture_count; i++) {
296 if (pool->picture_reserved[i])
299 picture_t *picture = pool->picture[i];
300 if (atomic_load(&picture->gc.refcount) > 0) {
301 if (pool->pic_unlock != NULL)
302 pool->pic_unlock(picture);
304 atomic_store(&picture->gc.refcount, 0);
308 void picture_pool_NonEmpty(picture_pool_t *pool)
310 picture_t *oldest = NULL;
312 for (unsigned i = 0; i < pool->picture_count; i++) {
313 if (pool->picture_reserved[i])
316 picture_t *picture = pool->picture[i];
317 if (atomic_load(&picture->gc.refcount) == 0)
318 return; /* Nothing to do */
320 if (oldest == NULL || picture->gc.p_sys->tick < oldest->gc.p_sys->tick)
325 return; /* Cannot fix! */
327 if (atomic_load(&oldest->gc.refcount) > 0) {
328 if (pool->pic_unlock != NULL)
329 pool->pic_unlock(oldest);
331 atomic_store(&oldest->gc.refcount, 0);
334 int picture_pool_GetSize(picture_pool_t *pool)
336 return pool->picture_count;