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 {
44 void (*destroy)(picture_t *);
48 int (*lock)(picture_t *);
49 void (*unlock)(picture_t *);
56 struct picture_pool_t {
58 picture_pool_t *master;
63 bool *picture_reserved;
69 static int Lock(picture_t *);
70 static void Unlock(picture_t *);
72 static void Release(picture_pool_t *pool)
76 vlc_mutex_lock(&pool->lock);
77 assert(pool->refs > 0);
78 destroy = !--pool->refs;
79 vlc_mutex_unlock(&pool->lock);
84 vlc_mutex_destroy(&pool->lock);
85 free(pool->picture_reserved);
90 static void DestroyPicture(picture_t *picture)
92 picture_gc_sys_t *gc_sys = picture->gc.p_sys;
93 picture_pool_t *pool = gc_sys->pool;
97 if (!atomic_load(&gc_sys->zombie))
100 /* Picture from an already destroyed pool */
101 picture->gc.pf_destroy = gc_sys->destroy;
102 picture->gc.p_sys = gc_sys->destroy_sys;
105 picture->gc.pf_destroy(picture);
109 static picture_pool_t *Create(picture_pool_t *master, int picture_count)
111 picture_pool_t *pool = calloc(1, sizeof(*pool));
115 pool->master = master;
116 pool->tick = master ? master->tick : 1;
117 pool->picture_count = picture_count;
118 pool->picture = calloc(pool->picture_count, sizeof(*pool->picture));
119 pool->picture_reserved = calloc(pool->picture_count, sizeof(*pool->picture_reserved));
120 if (!pool->picture || !pool->picture_reserved) {
122 free(pool->picture_reserved);
127 vlc_mutex_init(&pool->lock);
131 picture_pool_t *picture_pool_NewExtended(const picture_pool_configuration_t *cfg)
133 picture_pool_t *pool = Create(NULL, cfg->picture_count);
138 * NOTE: When a pooled picture is released, it must be returned to the list
139 * of available pictures from its pool, rather than destroyed.
140 * This requires a dedicated release callback, a pointer to the pool and a
141 * reference count. For simplicity, rather than allocate a whole new
142 * picture_t structure, the pool overrides gc.pf_destroy and gc.p_sys when
143 * created, and restores them when destroyed.
144 * There are some implications to keep in mind:
145 * - The original creator of the picture (e.g. video output display) must
146 * not manipulate the gc parameters while the picture is pooled.
147 * - The picture cannot be pooled more than once, in other words, pools
148 * cannot be stacked/layered.
149 * - The picture must be available and its reference count equal to one
150 * when it gets pooled.
151 * - Picture plane pointers and sizes must not be mangled in any case.
153 for (int i = 0; i < cfg->picture_count; i++) {
154 picture_t *picture = cfg->picture[i];
156 /* Save the original garbage collector */
157 picture_gc_sys_t *gc_sys = malloc(sizeof(*gc_sys));
158 if (unlikely(gc_sys == NULL))
161 gc_sys->destroy = picture->gc.pf_destroy;
162 gc_sys->destroy_sys = picture->gc.p_sys;
163 gc_sys->lock = cfg->lock;
164 gc_sys->unlock = cfg->unlock;
165 atomic_init(&gc_sys->zombie, false);
168 /* Override the garbage collector */
169 assert(atomic_load(&picture->gc.refcount) == 1);
170 atomic_init(&picture->gc.refcount, 0);
171 picture->gc.pf_destroy = DestroyPicture;
172 picture->gc.p_sys = gc_sys;
175 pool->picture[i] = picture;
176 pool->picture_reserved[i] = false;
183 picture_pool_t *picture_pool_New(int picture_count, picture_t *picture[])
185 picture_pool_configuration_t cfg;
187 memset(&cfg, 0, sizeof(cfg));
188 cfg.picture_count = picture_count;
189 cfg.picture = picture;
191 return picture_pool_NewExtended(&cfg);
194 picture_pool_t *picture_pool_NewFromFormat(const video_format_t *fmt, int picture_count)
196 picture_t *picture[picture_count];
198 for (int i = 0; i < picture_count; i++) {
199 picture[i] = picture_NewFromFormat(fmt);
203 picture_pool_t *pool = picture_pool_New(picture_count, picture);
210 for (int i = 0; i < picture_count; i++) {
213 picture_Release(picture[i]);
218 picture_pool_t *picture_pool_Reserve(picture_pool_t *master, int count)
220 picture_pool_t *pool = Create(master, count);
225 for (int i = 0; i < master->picture_count && found < count; i++) {
226 if (master->picture_reserved[i])
229 assert(atomic_load(&master->picture[i]->gc.refcount) == 0);
230 master->picture_reserved[i] = true;
232 pool->picture[found] = master->picture[i];
233 pool->picture_reserved[found] = false;
237 picture_pool_Delete(pool);
243 void picture_pool_Delete(picture_pool_t *pool)
245 for (int i = 0; i < pool->picture_count; i++) {
246 picture_t *picture = pool->picture[i];
248 for (int j = 0; j < pool->master->picture_count; j++) {
249 if (pool->master->picture[j] == picture)
250 pool->master->picture_reserved[j] = false;
253 picture_gc_sys_t *gc_sys = picture->gc.p_sys;
255 assert(!pool->picture_reserved[i]);
257 /* Restore the initial reference that was cloberred in
258 * picture_pool_NewExtended(). */
259 atomic_fetch_add(&picture->gc.refcount, 1);
260 /* The picture might still locked and then the G.C. state cannot be
261 * modified (w/o memory synchronization). */
262 atomic_store(&gc_sys->zombie, true);
264 picture_Release(picture);
270 picture_t *picture_pool_Get(picture_pool_t *pool)
272 for (int i = 0; i < pool->picture_count; i++) {
273 if (pool->picture_reserved[i])
276 picture_t *picture = pool->picture[i];
277 if (atomic_load(&picture->gc.refcount) > 0)
284 picture->p_next = NULL;
285 picture->gc.p_sys->tick = pool->tick++;
286 picture_Hold(picture);
292 void picture_pool_NonEmpty(picture_pool_t *pool, bool reset)
294 picture_t *old = NULL;
296 for (int i = 0; i < pool->picture_count; i++) {
297 if (pool->picture_reserved[i])
300 picture_t *picture = pool->picture[i];
302 if (atomic_load(&picture->gc.refcount) > 0)
304 atomic_store(&picture->gc.refcount, 0);
305 } else if (atomic_load(&picture->gc.refcount) == 0) {
307 } else if (!old || picture->gc.p_sys->tick < old->gc.p_sys->tick) {
312 if (atomic_load(&old->gc.refcount) > 0)
314 atomic_store(&old->gc.refcount, 0);
317 int picture_pool_GetSize(picture_pool_t *pool)
319 return pool->picture_count;
322 static int Lock(picture_t *picture)
324 picture_gc_sys_t *gc_sys = picture->gc.p_sys;
326 return gc_sys->lock(picture);
330 static void Unlock(picture_t *picture)
332 picture_gc_sys_t *gc_sys = picture->gc.p_sys;
334 gc_sys->unlock(picture);