]> git.sesse.net Git - vlc/blob - src/misc/picture_pool.c
3603887482e363505b98152bfc14482ad05475d7
[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     picture_t *picture;
43     bool in_use;
44     uint64_t tick;
45 };
46
47 struct picture_pool_t {
48     uint64_t       tick;
49     /* */
50     unsigned       picture_count;
51     picture_t      **picture;
52
53     int       (*pic_lock)(picture_t *);
54     void      (*pic_unlock)(picture_t *);
55     unsigned    refs;
56     vlc_mutex_t lock;
57 };
58
59 void picture_pool_Release(picture_pool_t *pool)
60 {
61     bool destroy;
62
63     vlc_mutex_lock(&pool->lock);
64     assert(pool->refs > 0);
65     destroy = --pool->refs == 0;
66     vlc_mutex_unlock(&pool->lock);
67
68     if (likely(!destroy))
69         return;
70
71     for (unsigned i = 0; i < pool->picture_count; i++) {
72         picture_t *picture = pool->picture[i];
73         picture_gc_sys_t *sys = picture->gc.p_sys;
74
75         picture_Release(sys->picture);
76         free(sys);
77         free(picture);
78     }
79
80     vlc_mutex_destroy(&pool->lock);
81     free(pool->picture);
82     free(pool);
83 }
84
85 static void picture_pool_ReleasePicture(picture_t *picture)
86 {
87     picture_gc_sys_t *sys = picture->gc.p_sys;
88     picture_pool_t *pool = sys->pool;
89
90     if (pool->pic_unlock != NULL)
91         pool->pic_unlock(picture);
92
93     vlc_mutex_lock(&pool->lock);
94     assert(sys->in_use);
95     sys->in_use = false;
96     vlc_mutex_unlock(&pool->lock);
97
98     picture_pool_Release(pool);
99 }
100
101 static picture_t *picture_pool_ClonePicture(picture_pool_t *pool,
102                                             picture_t *picture)
103 {
104     picture_gc_sys_t *sys = malloc(sizeof(*sys));
105     if (unlikely(sys == NULL))
106         return NULL;
107
108     sys->pool = pool;
109     sys->picture = picture;
110     sys->in_use = false;
111     sys->tick = 0;
112
113     picture_resource_t res = {
114         .p_sys = picture->p_sys,
115         .pf_destroy = picture_pool_ReleasePicture,
116     };
117
118     for (int i = 0; i < picture->i_planes; i++) {
119         res.p[i].p_pixels = picture->p[i].p_pixels;
120         res.p[i].i_lines = picture->p[i].i_lines;
121         res.p[i].i_pitch = picture->p[i].i_pitch;
122     }
123
124     picture_t *clone = picture_NewFromResource(&picture->format, &res);
125     if (likely(clone != NULL))
126         clone->gc.p_sys = sys;
127     else
128         free(sys);
129
130     return clone;
131 }
132
133 static picture_pool_t *Create(int picture_count)
134 {
135     picture_pool_t *pool = calloc(1, sizeof(*pool));
136     if (!pool)
137         return NULL;
138
139     pool->tick = 1;
140     pool->picture_count = picture_count;
141     pool->picture = calloc(pool->picture_count, sizeof(*pool->picture));
142     if (!pool->picture) {
143         free(pool->picture);
144         free(pool);
145         return NULL;
146     }
147     pool->refs = 1;
148     vlc_mutex_init(&pool->lock);
149     return pool;
150 }
151
152 picture_pool_t *picture_pool_NewExtended(const picture_pool_configuration_t *cfg)
153 {
154     picture_pool_t *pool = Create(cfg->picture_count);
155     if (!pool)
156         return NULL;
157
158     pool->pic_lock   = cfg->lock;
159     pool->pic_unlock = cfg->unlock;
160
161     for (unsigned i = 0; i < cfg->picture_count; i++) {
162         picture_t *picture = picture_pool_ClonePicture(pool, cfg->picture[i]);
163         if (unlikely(picture == NULL))
164             abort();
165
166         atomic_init(&picture->gc.refcount, 0);
167
168         pool->picture[i] = picture;
169     }
170     return pool;
171
172 }
173
174 picture_pool_t *picture_pool_New(unsigned count, picture_t *const *tab)
175 {
176     picture_pool_configuration_t cfg;
177
178     memset(&cfg, 0, sizeof(cfg));
179     cfg.picture_count = count;
180     cfg.picture       = tab;
181
182     return picture_pool_NewExtended(&cfg);
183 }
184
185 picture_pool_t *picture_pool_NewFromFormat(const video_format_t *fmt,
186                                            unsigned count)
187 {
188     picture_t *picture[count ? count : 1];
189     unsigned i;
190
191     for (i = 0; i < count; i++) {
192         picture[i] = picture_NewFromFormat(fmt);
193         if (picture[i] == NULL)
194             goto error;
195     }
196
197     picture_pool_t *pool = picture_pool_New(count, picture);
198     if (!pool)
199         goto error;
200
201     return pool;
202
203 error:
204     while (i > 0)
205         picture_Release(picture[--i]);
206     return NULL;
207 }
208
209 picture_pool_t *picture_pool_Reserve(picture_pool_t *master, unsigned count)
210 {
211     picture_t *picture[count ? count : 1];
212     unsigned i;
213
214     for (i = 0; i < count; i++) {
215         picture[i] = picture_pool_Get(master);
216         if (picture[i] == NULL)
217             goto error;
218     }
219
220     picture_pool_t *pool = picture_pool_New(count, picture);
221     if (!pool)
222         goto error;
223
224     pool->pic_lock   = master->pic_lock;
225     pool->pic_unlock = master->pic_unlock;
226     return pool;
227
228 error:
229     while (i > 0)
230         picture_Release(picture[--i]);
231     return NULL;
232 }
233
234 picture_t *picture_pool_Get(picture_pool_t *pool)
235 {
236     vlc_mutex_lock(&pool->lock);
237     assert(pool->refs > 0);
238
239     for (unsigned i = 0; i < pool->picture_count; i++) {
240         picture_t *picture = pool->picture[i];
241         picture_gc_sys_t *sys = picture->gc.p_sys;
242         uint64_t tick;
243
244         if (sys->in_use)
245             continue;
246
247         pool->refs++;
248         tick = ++pool->tick;
249         sys->in_use = true;
250         vlc_mutex_unlock(&pool->lock);
251
252         if (pool->pic_lock != NULL && pool->pic_lock(picture) != 0) {
253             vlc_mutex_lock(&pool->lock);
254             sys->in_use = false;
255             pool->refs--;
256             vlc_mutex_unlock(&pool->lock);
257             continue;
258         }
259
260         sys->tick = tick;
261
262         assert(atomic_load(&picture->gc.refcount) == 0);
263         atomic_init(&picture->gc.refcount, 1);
264         picture->p_next = NULL;
265         return picture;
266     }
267
268     vlc_mutex_unlock(&pool->lock);
269     return NULL;
270 }
271
272 unsigned picture_pool_Reset(picture_pool_t *pool)
273 {
274     unsigned ret = 0;
275 retry:
276     vlc_mutex_lock(&pool->lock);
277     assert(pool->refs > 0);
278
279     for (unsigned i = 0; i < pool->picture_count; i++) {
280         picture_t *picture = pool->picture[i];
281         picture_gc_sys_t *sys = picture->gc.p_sys;
282
283         if (sys->in_use) {
284             vlc_mutex_unlock(&pool->lock);
285             picture_Release(picture);
286             ret++;
287             goto retry;
288         }
289     }
290     vlc_mutex_unlock(&pool->lock);
291
292     return ret;
293 }
294
295 void picture_pool_NonEmpty(picture_pool_t *pool)
296 {
297     picture_t *oldest = NULL;
298     uint64_t tick = 0;
299
300     vlc_mutex_lock(&pool->lock);
301     assert(pool->refs > 0);
302
303     for (unsigned i = 0; i < pool->picture_count; i++) {
304         picture_t *picture = pool->picture[i];
305         picture_gc_sys_t *sys = picture->gc.p_sys;
306
307         if (!sys->in_use) {
308             vlc_mutex_unlock(&pool->lock);
309             return; /* Nothing to do */
310         }
311
312         if (picture->gc.p_sys->tick < tick) {
313             oldest = picture;
314             tick = picture->gc.p_sys->tick;
315         }
316     }
317
318     if (oldest != NULL) {
319         while (oldest->gc.p_sys->in_use) {
320             vlc_mutex_unlock(&pool->lock);
321             picture_Release(oldest);
322             vlc_mutex_lock(&pool->lock);
323         }
324     }
325
326     vlc_mutex_unlock(&pool->lock);
327 }
328
329 int picture_pool_GetSize(picture_pool_t *pool)
330 {
331     return pool->picture_count;
332 }