]> git.sesse.net Git - vlc/blob - src/misc/picture_pool.c
picture_pool: remove unnecessary special case
[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 initial reference that was cloberred in
213              * picture_pool_NewExtended(). */
214             atomic_fetch_add(&picture->gc.refcount, 1);
215             /* The picture might still locked and then the G.C. state cannot be
216              * modified (w/o memory synchronization). */
217             atomic_store(&gc_sys->zombie, true);
218
219             picture_Release(picture);
220         }
221     }
222     free(pool->picture_reserved);
223     free(pool->picture);
224     free(pool);
225 }
226
227 picture_t *picture_pool_Get(picture_pool_t *pool)
228 {
229     for (int i = 0; i < pool->picture_count; i++) {
230         if (pool->picture_reserved[i])
231             continue;
232
233         picture_t *picture = pool->picture[i];
234         if (atomic_load(&picture->gc.refcount) > 0)
235             continue;
236
237         if (Lock(picture))
238             continue;
239
240         /* */
241         picture->p_next = NULL;
242         picture->gc.p_sys->tick = pool->tick++;
243         picture_Hold(picture);
244         return picture;
245     }
246     return NULL;
247 }
248
249 void picture_pool_NonEmpty(picture_pool_t *pool, bool reset)
250 {
251     picture_t *old = NULL;
252
253     for (int i = 0; i < pool->picture_count; i++) {
254         if (pool->picture_reserved[i])
255             continue;
256
257         picture_t *picture = pool->picture[i];
258         if (reset) {
259             if (atomic_load(&picture->gc.refcount) > 0)
260                 Unlock(picture);
261             atomic_store(&picture->gc.refcount, 0);
262         } else if (atomic_load(&picture->gc.refcount) == 0) {
263             return;
264         } else if (!old || picture->gc.p_sys->tick < old->gc.p_sys->tick) {
265             old = picture;
266         }
267     }
268     if (!reset && old) {
269         if (atomic_load(&old->gc.refcount) > 0)
270             Unlock(old);
271         atomic_store(&old->gc.refcount, 0);
272     }
273 }
274 int picture_pool_GetSize(picture_pool_t *pool)
275 {
276     return pool->picture_count;
277 }
278
279 static void Destroy(picture_t *picture)
280 {
281     picture_gc_sys_t *gc_sys = picture->gc.p_sys;
282
283     Unlock(picture);
284
285     if (atomic_load(&gc_sys->zombie))
286     {   /* Picture from an already destroyed pool */
287         picture->gc.pf_destroy = gc_sys->destroy;
288         picture->gc.p_sys      = gc_sys->destroy_sys;
289         free(gc_sys);
290
291         picture->gc.pf_destroy(picture);
292     }
293 }
294
295 static int Lock(picture_t *picture)
296 {
297     picture_gc_sys_t *gc_sys = picture->gc.p_sys;
298     if (gc_sys->lock)
299         return gc_sys->lock(picture);
300     return VLC_SUCCESS;
301 }
302
303 static void Unlock(picture_t *picture)
304 {
305     picture_gc_sys_t *gc_sys = picture->gc.p_sys;
306     if (gc_sys->unlock)
307         gc_sys->unlock(picture);
308 }