dvbpsi: add sys/types.h as appropriate
[vlc.git] / src / misc / picture_pool.c
blob294d671eedbf9b027775fd2c61ee0b38b6199ad9
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>
6 * $Id$
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 /*****************************************************************************
26 * Preamble
27 *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32 #include <assert.h>
34 #include <vlc_common.h>
35 #include <vlc_picture_pool.h>
37 /*****************************************************************************
39 *****************************************************************************/
40 struct picture_gc_sys_t {
41 /* Saved release */
42 void (*destroy)(picture_t *);
43 void *destroy_sys;
45 /* */
46 int (*lock)(picture_t *);
47 void (*unlock)(picture_t *);
49 /* */
50 atomic_bool zombie;
51 int64_t tick;
54 struct picture_pool_t {
55 /* */
56 picture_pool_t *master;
57 int64_t tick;
58 /* */
59 int picture_count;
60 picture_t **picture;
61 bool *picture_reserved;
64 static void Destroy(picture_t *);
65 static int Lock(picture_t *);
66 static void Unlock(picture_t *);
68 static picture_pool_t *Create(picture_pool_t *master, int picture_count)
70 picture_pool_t *pool = calloc(1, sizeof(*pool));
71 if (!pool)
72 return NULL;
74 pool->master = master;
75 pool->tick = master ? master->tick : 1;
76 pool->picture_count = picture_count;
77 pool->picture = calloc(pool->picture_count, sizeof(*pool->picture));
78 pool->picture_reserved = calloc(pool->picture_count, sizeof(*pool->picture_reserved));
79 if (!pool->picture || !pool->picture_reserved) {
80 free(pool->picture);
81 free(pool->picture_reserved);
82 free(pool);
83 return NULL;
85 return pool;
88 picture_pool_t *picture_pool_NewExtended(const picture_pool_configuration_t *cfg)
90 picture_pool_t *pool = Create(NULL, cfg->picture_count);
91 if (!pool)
92 return NULL;
95 * NOTE: When a pooled picture is released, it must be returned to the list
96 * of available pictures from its pool, rather than destroyed.
97 * This requires a dedicated release callback, a pointer to the pool and a
98 * reference count. For simplicity, rather than allocate a whole new
99 * picture_t structure, the pool overrides gc.pf_destroy and gc.p_sys when
100 * created, and restores them when destroyed.
101 * There are some implications to keep in mind:
102 * - The original creator of the picture (e.g. video output display) must
103 * not manipulate the gc parameters while the picture is pooled.
104 * - The picture cannot be pooled more than once, in other words, pools
105 * cannot be stacked/layered.
106 * - The picture must be available and its reference count equal to one
107 * when it gets pooled.
108 * - Picture plane pointers and sizes must not be mangled in any case.
110 for (int i = 0; i < cfg->picture_count; i++) {
111 picture_t *picture = cfg->picture[i];
113 /* Save the original garbage collector */
114 picture_gc_sys_t *gc_sys = malloc(sizeof(*gc_sys));
115 if (unlikely(gc_sys == NULL))
116 abort();
117 gc_sys->destroy = picture->gc.pf_destroy;
118 gc_sys->destroy_sys = picture->gc.p_sys;
119 gc_sys->lock = cfg->lock;
120 gc_sys->unlock = cfg->unlock;
121 atomic_init(&gc_sys->zombie, false);
122 gc_sys->tick = 0;
124 /* Override the garbage collector */
125 assert(atomic_load(&picture->gc.refcount) == 1);
126 atomic_init(&picture->gc.refcount, 0);
127 picture->gc.pf_destroy = Destroy;
128 picture->gc.p_sys = gc_sys;
130 /* */
131 pool->picture[i] = picture;
132 pool->picture_reserved[i] = false;
134 return pool;
138 picture_pool_t *picture_pool_New(int picture_count, picture_t *picture[])
140 picture_pool_configuration_t cfg;
142 memset(&cfg, 0, sizeof(cfg));
143 cfg.picture_count = picture_count;
144 cfg.picture = picture;
146 return picture_pool_NewExtended(&cfg);
149 picture_pool_t *picture_pool_NewFromFormat(const video_format_t *fmt, int picture_count)
151 picture_t *picture[picture_count];
153 for (int i = 0; i < picture_count; i++) {
154 picture[i] = picture_NewFromFormat(fmt);
155 if (!picture[i])
156 goto error;
158 picture_pool_t *pool = picture_pool_New(picture_count, picture);
159 if (!pool)
160 goto error;
162 return pool;
164 error:
165 for (int i = 0; i < picture_count; i++) {
166 if (!picture[i])
167 break;
168 picture_Release(picture[i]);
170 return NULL;
173 picture_pool_t *picture_pool_Reserve(picture_pool_t *master, int count)
175 picture_pool_t *pool = Create(master, count);
176 if (!pool)
177 return NULL;
179 int found = 0;
180 for (int i = 0; i < master->picture_count && found < count; i++) {
181 if (master->picture_reserved[i])
182 continue;
184 assert(atomic_load(&master->picture[i]->gc.refcount) == 0);
185 master->picture_reserved[i] = true;
187 pool->picture[found] = master->picture[i];
188 pool->picture_reserved[found] = false;
189 found++;
191 if (found < count) {
192 picture_pool_Delete(pool);
193 return NULL;
195 return pool;
198 void picture_pool_Delete(picture_pool_t *pool)
200 for (int i = 0; i < pool->picture_count; i++) {
201 picture_t *picture = pool->picture[i];
202 if (pool->master) {
203 for (int j = 0; j < pool->master->picture_count; j++) {
204 if (pool->master->picture[j] == picture)
205 pool->master->picture_reserved[j] = false;
207 } else {
208 picture_gc_sys_t *gc_sys = picture->gc.p_sys;
210 assert(!pool->picture_reserved[i]);
212 /* Restore the original garbage collector */
213 if (atomic_fetch_add(&picture->gc.refcount, 1) == 0)
214 { /* Simple case: the picture is not locked, destroy it now. */
215 picture->gc.pf_destroy = gc_sys->destroy;
216 picture->gc.p_sys = gc_sys->destroy_sys;
217 free(gc_sys);
219 else /* Intricate case: the picture is still locked and the gc
220 cannot be modified (w/o memory synchronization). */
221 atomic_store(&gc_sys->zombie, true);
223 picture_Release(picture);
226 free(pool->picture_reserved);
227 free(pool->picture);
228 free(pool);
231 picture_t *picture_pool_Get(picture_pool_t *pool)
233 for (int i = 0; i < pool->picture_count; i++) {
234 if (pool->picture_reserved[i])
235 continue;
237 picture_t *picture = pool->picture[i];
238 if (atomic_load(&picture->gc.refcount) > 0)
239 continue;
241 if (Lock(picture))
242 continue;
244 /* */
245 picture->p_next = NULL;
246 picture->gc.p_sys->tick = pool->tick++;
247 picture_Hold(picture);
248 return picture;
250 return NULL;
253 void picture_pool_NonEmpty(picture_pool_t *pool, bool reset)
255 picture_t *old = NULL;
257 for (int i = 0; i < pool->picture_count; i++) {
258 if (pool->picture_reserved[i])
259 continue;
261 picture_t *picture = pool->picture[i];
262 if (reset) {
263 if (atomic_load(&picture->gc.refcount) > 0)
264 Unlock(picture);
265 atomic_store(&picture->gc.refcount, 0);
266 } else if (atomic_load(&picture->gc.refcount) == 0) {
267 return;
268 } else if (!old || picture->gc.p_sys->tick < old->gc.p_sys->tick) {
269 old = picture;
272 if (!reset && old) {
273 if (atomic_load(&old->gc.refcount) > 0)
274 Unlock(old);
275 atomic_store(&old->gc.refcount, 0);
278 int picture_pool_GetSize(picture_pool_t *pool)
280 return pool->picture_count;
283 static void Destroy(picture_t *picture)
285 picture_gc_sys_t *gc_sys = picture->gc.p_sys;
287 Unlock(picture);
289 if (atomic_load(&gc_sys->zombie))
290 { /* Picture from an already destroyed pool */
291 picture->gc.pf_destroy = gc_sys->destroy;
292 picture->gc.p_sys = gc_sys->destroy_sys;
293 free(gc_sys);
295 picture->gc.pf_destroy(picture);
299 static int Lock(picture_t *picture)
301 picture_gc_sys_t *gc_sys = picture->gc.p_sys;
302 if (gc_sys->lock)
303 return gc_sys->lock(picture);
304 return VLC_SUCCESS;
307 static void Unlock(picture_t *picture)
309 picture_gc_sys_t *gc_sys = picture->gc.p_sys;
310 if (gc_sys->unlock)
311 gc_sys->unlock(picture);