]> git.sesse.net Git - vlc/blob - src/misc/picture_pool.c
avcodec: map EA TGQ
[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     /* Saved release */
42     void (*destroy)(picture_t *);
43     void *destroy_sys;
44
45     /* */
46     int  (*lock)(picture_t *);
47     void (*unlock)(picture_t *);
48
49     /* */
50     atomic_bool zombie;
51     int64_t tick;
52 };
53
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;
62 };
63
64 static void Destroy(picture_t *);
65 static int  Lock(picture_t *);
66 static void Unlock(picture_t *);
67
68 static picture_pool_t *Create(picture_pool_t *master, int picture_count)
69 {
70     picture_pool_t *pool = calloc(1, sizeof(*pool));
71     if (!pool)
72         return NULL;
73
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;
84     }
85     return pool;
86 }
87
88 picture_pool_t *picture_pool_NewExtended(const picture_pool_configuration_t *cfg)
89 {
90     picture_pool_t *pool = Create(NULL, cfg->picture_count);
91     if (!pool)
92         return NULL;
93
94     /*
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.
109      */
110     for (int i = 0; i < cfg->picture_count; i++) {
111         picture_t *picture = cfg->picture[i];
112
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;
123
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;
129
130         /* */
131         pool->picture[i] = picture;
132         pool->picture_reserved[i] = false;
133     }
134     return pool;
135
136 }
137
138 picture_pool_t *picture_pool_New(int picture_count, picture_t *picture[])
139 {
140     picture_pool_configuration_t cfg;
141
142     memset(&cfg, 0, sizeof(cfg));
143     cfg.picture_count = picture_count;
144     cfg.picture       = picture;
145
146     return picture_pool_NewExtended(&cfg);
147 }
148
149 picture_pool_t *picture_pool_NewFromFormat(const video_format_t *fmt, int picture_count)
150 {
151     picture_t *picture[picture_count];
152
153     for (int i = 0; i < picture_count; i++) {
154         picture[i] = picture_NewFromFormat(fmt);
155         if (!picture[i])
156             goto error;
157     }
158     picture_pool_t *pool = picture_pool_New(picture_count, picture);
159     if (!pool)
160         goto error;
161
162     return pool;
163
164 error:
165     for (int i = 0; i < picture_count; i++) {
166         if (!picture[i])
167             break;
168         picture_Release(picture[i]);
169     }
170     return NULL;
171 }
172
173 picture_pool_t *picture_pool_Reserve(picture_pool_t *master, int count)
174 {
175     picture_pool_t *pool = Create(master, count);
176     if (!pool)
177         return NULL;
178
179     int found = 0;
180     for (int i = 0; i < master->picture_count && found < count; i++) {
181         if (master->picture_reserved[i])
182             continue;
183
184         assert(atomic_load(&master->picture[i]->gc.refcount) == 0);
185         master->picture_reserved[i] = true;
186
187         pool->picture[found]          = master->picture[i];
188         pool->picture_reserved[found] = false;
189         found++;
190     }
191     if (found < count) {
192         picture_pool_Delete(pool);
193         return NULL;
194     }
195     return pool;
196 }
197
198 void picture_pool_Delete(picture_pool_t *pool)
199 {
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;
206             }
207         } else {
208             picture_gc_sys_t *gc_sys = picture->gc.p_sys;
209
210             assert(!pool->picture_reserved[i]);
211
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);
218             }
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);
222
223             picture_Release(picture);
224         }
225     }
226     free(pool->picture_reserved);
227     free(pool->picture);
228     free(pool);
229 }
230
231 picture_t *picture_pool_Get(picture_pool_t *pool)
232 {
233     for (int i = 0; i < pool->picture_count; i++) {
234         if (pool->picture_reserved[i])
235             continue;
236
237         picture_t *picture = pool->picture[i];
238         if (atomic_load(&picture->gc.refcount) > 0)
239             continue;
240
241         if (Lock(picture))
242             continue;
243
244         /* */
245         picture->p_next = NULL;
246         picture->gc.p_sys->tick = pool->tick++;
247         picture_Hold(picture);
248         return picture;
249     }
250     return NULL;
251 }
252
253 void picture_pool_NonEmpty(picture_pool_t *pool, bool reset)
254 {
255     picture_t *old = NULL;
256
257     for (int i = 0; i < pool->picture_count; i++) {
258         if (pool->picture_reserved[i])
259             continue;
260
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;
270         }
271     }
272     if (!reset && old) {
273         if (atomic_load(&old->gc.refcount) > 0)
274             Unlock(old);
275         atomic_store(&old->gc.refcount, 0);
276     }
277 }
278 int picture_pool_GetSize(picture_pool_t *pool)
279 {
280     return pool->picture_count;
281 }
282
283 static void Destroy(picture_t *picture)
284 {
285     picture_gc_sys_t *gc_sys = picture->gc.p_sys;
286
287     Unlock(picture);
288
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);
294
295         picture->gc.pf_destroy(picture);
296     }
297 }
298
299 static int Lock(picture_t *picture)
300 {
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;
305 }
306
307 static void Unlock(picture_t *picture)
308 {
309     picture_gc_sys_t *gc_sys = picture->gc.p_sys;
310     if (gc_sys->unlock)
311         gc_sys->unlock(picture);
312 }