]> git.sesse.net Git - vlc/blob - src/misc/picture_pool.c
pool: keep reference to pool in each picture
[vlc] / src / misc / picture_pool.c
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$
7  *
8  * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
9  *
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.
14  *
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.
19  *
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  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32 #include <assert.h>
33
34 #include <vlc_common.h>
35 #include <vlc_picture_pool.h>
36
37 /*****************************************************************************
38  *
39  *****************************************************************************/
40 struct picture_gc_sys_t {
41     picture_pool_t *pool;
42
43     /* Saved release */
44     void (*destroy)(picture_t *);
45     void *destroy_sys;
46
47     /* */
48     int  (*lock)(picture_t *);
49     void (*unlock)(picture_t *);
50
51     /* */
52     atomic_bool zombie;
53     int64_t tick;
54 };
55
56 struct picture_pool_t {
57     /* */
58     picture_pool_t *master;
59     int64_t        tick;
60     /* */
61     int            picture_count;
62     picture_t      **picture;
63     bool           *picture_reserved;
64
65     unsigned    refs;
66     vlc_mutex_t lock;
67 };
68
69 static int  Lock(picture_t *);
70 static void Unlock(picture_t *);
71
72 static void Release(picture_pool_t *pool)
73 {
74     bool destroy;
75
76     vlc_mutex_lock(&pool->lock);
77     assert(pool->refs > 0);
78     destroy = !--pool->refs;
79     vlc_mutex_unlock(&pool->lock);
80
81     if (!destroy)
82         return;
83
84     vlc_mutex_destroy(&pool->lock);
85     free(pool->picture_reserved);
86     free(pool->picture);
87     free(pool);
88 }
89
90 static void DestroyPicture(picture_t *picture)
91 {
92     picture_gc_sys_t *gc_sys = picture->gc.p_sys;
93     picture_pool_t *pool = gc_sys->pool;
94
95     Unlock(picture);
96
97     if (!atomic_load(&gc_sys->zombie))
98         return;
99
100     /* Picture from an already destroyed pool */
101     picture->gc.pf_destroy = gc_sys->destroy;
102     picture->gc.p_sys      = gc_sys->destroy_sys;
103     free(gc_sys);
104
105     picture->gc.pf_destroy(picture);
106     Release(pool);
107 }
108
109 static picture_pool_t *Create(picture_pool_t *master, int picture_count)
110 {
111     picture_pool_t *pool = calloc(1, sizeof(*pool));
112     if (!pool)
113         return NULL;
114
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) {
121         free(pool->picture);
122         free(pool->picture_reserved);
123         free(pool);
124         return NULL;
125     }
126     pool->refs = 1;
127     vlc_mutex_init(&pool->lock);
128     return pool;
129 }
130
131 picture_pool_t *picture_pool_NewExtended(const picture_pool_configuration_t *cfg)
132 {
133     picture_pool_t *pool = Create(NULL, cfg->picture_count);
134     if (!pool)
135         return NULL;
136
137     /*
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.
152      */
153     for (int i = 0; i < cfg->picture_count; i++) {
154         picture_t *picture = cfg->picture[i];
155
156         /* Save the original garbage collector */
157         picture_gc_sys_t *gc_sys = malloc(sizeof(*gc_sys));
158         if (unlikely(gc_sys == NULL))
159             abort();
160         gc_sys->pool        = pool;
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);
166         gc_sys->tick        = 0;
167
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;
173
174         /* */
175         pool->picture[i] = picture;
176         pool->picture_reserved[i] = false;
177         pool->refs++;
178     }
179     return pool;
180
181 }
182
183 picture_pool_t *picture_pool_New(int picture_count, picture_t *picture[])
184 {
185     picture_pool_configuration_t cfg;
186
187     memset(&cfg, 0, sizeof(cfg));
188     cfg.picture_count = picture_count;
189     cfg.picture       = picture;
190
191     return picture_pool_NewExtended(&cfg);
192 }
193
194 picture_pool_t *picture_pool_NewFromFormat(const video_format_t *fmt, int picture_count)
195 {
196     picture_t *picture[picture_count];
197
198     for (int i = 0; i < picture_count; i++) {
199         picture[i] = picture_NewFromFormat(fmt);
200         if (!picture[i])
201             goto error;
202     }
203     picture_pool_t *pool = picture_pool_New(picture_count, picture);
204     if (!pool)
205         goto error;
206
207     return pool;
208
209 error:
210     for (int i = 0; i < picture_count; i++) {
211         if (!picture[i])
212             break;
213         picture_Release(picture[i]);
214     }
215     return NULL;
216 }
217
218 picture_pool_t *picture_pool_Reserve(picture_pool_t *master, int count)
219 {
220     picture_pool_t *pool = Create(master, count);
221     if (!pool)
222         return NULL;
223
224     int found = 0;
225     for (int i = 0; i < master->picture_count && found < count; i++) {
226         if (master->picture_reserved[i])
227             continue;
228
229         assert(atomic_load(&master->picture[i]->gc.refcount) == 0);
230         master->picture_reserved[i] = true;
231
232         pool->picture[found]          = master->picture[i];
233         pool->picture_reserved[found] = false;
234         found++;
235     }
236     if (found < count) {
237         picture_pool_Delete(pool);
238         return NULL;
239     }
240     return pool;
241 }
242
243 void picture_pool_Delete(picture_pool_t *pool)
244 {
245     for (int i = 0; i < pool->picture_count; i++) {
246         picture_t *picture = pool->picture[i];
247         if (pool->master) {
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;
251             }
252         } else {
253             picture_gc_sys_t *gc_sys = picture->gc.p_sys;
254
255             assert(!pool->picture_reserved[i]);
256
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);
263
264             picture_Release(picture);
265         }
266     }
267     Release(pool);
268 }
269
270 picture_t *picture_pool_Get(picture_pool_t *pool)
271 {
272     for (int i = 0; i < pool->picture_count; i++) {
273         if (pool->picture_reserved[i])
274             continue;
275
276         picture_t *picture = pool->picture[i];
277         if (atomic_load(&picture->gc.refcount) > 0)
278             continue;
279
280         if (Lock(picture))
281             continue;
282
283         /* */
284         picture->p_next = NULL;
285         picture->gc.p_sys->tick = pool->tick++;
286         picture_Hold(picture);
287         return picture;
288     }
289     return NULL;
290 }
291
292 void picture_pool_NonEmpty(picture_pool_t *pool, bool reset)
293 {
294     picture_t *old = NULL;
295
296     for (int i = 0; i < pool->picture_count; i++) {
297         if (pool->picture_reserved[i])
298             continue;
299
300         picture_t *picture = pool->picture[i];
301         if (reset) {
302             if (atomic_load(&picture->gc.refcount) > 0)
303                 Unlock(picture);
304             atomic_store(&picture->gc.refcount, 0);
305         } else if (atomic_load(&picture->gc.refcount) == 0) {
306             return;
307         } else if (!old || picture->gc.p_sys->tick < old->gc.p_sys->tick) {
308             old = picture;
309         }
310     }
311     if (!reset && old) {
312         if (atomic_load(&old->gc.refcount) > 0)
313             Unlock(old);
314         atomic_store(&old->gc.refcount, 0);
315     }
316 }
317 int picture_pool_GetSize(picture_pool_t *pool)
318 {
319     return pool->picture_count;
320 }
321
322 static int Lock(picture_t *picture)
323 {
324     picture_gc_sys_t *gc_sys = picture->gc.p_sys;
325     if (gc_sys->lock)
326         return gc_sys->lock(picture);
327     return VLC_SUCCESS;
328 }
329
330 static void Unlock(picture_t *picture)
331 {
332     picture_gc_sys_t *gc_sys = picture->gc.p_sys;
333     if (gc_sys->unlock)
334         gc_sys->unlock(picture);
335 }