]> git.sesse.net Git - vlc/blob - src/misc/picture_pool.c
dac7ef67edb86c6f29f83b65d9068922403035db
[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     assert(master->pic_unlock == NULL);
212
213     picture_t *picture[count ? count : 1];
214     unsigned i;
215
216     for (i = 0; i < count; i++) {
217         picture[i] = picture_pool_Get(master);
218         if (picture[i] == NULL)
219             goto error;
220     }
221
222     picture_pool_t *pool = picture_pool_New(count, picture);
223     if (!pool)
224         goto error;
225
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             continue;
257         }
258
259         sys->tick = tick;
260
261         assert(atomic_load(&picture->gc.refcount) == 0);
262         atomic_init(&picture->gc.refcount, 1);
263         picture->p_next = NULL;
264         return picture;
265     }
266
267     vlc_mutex_unlock(&pool->lock);
268     return NULL;
269 }
270
271 unsigned picture_pool_Reset(picture_pool_t *pool)
272 {
273     unsigned ret = 0;
274 retry:
275     vlc_mutex_lock(&pool->lock);
276     assert(pool->refs > 0);
277
278     for (unsigned i = 0; i < pool->picture_count; i++) {
279         picture_t *picture = pool->picture[i];
280         picture_gc_sys_t *sys = picture->gc.p_sys;
281
282         if (sys->in_use) {
283             vlc_mutex_unlock(&pool->lock);
284             picture_Release(picture);
285             ret++;
286             goto retry;
287         }
288     }
289     vlc_mutex_unlock(&pool->lock);
290
291     return ret;
292 }
293
294 void picture_pool_NonEmpty(picture_pool_t *pool)
295 {
296     picture_t *oldest = NULL;
297     uint64_t tick = 0;
298
299     vlc_mutex_lock(&pool->lock);
300     assert(pool->refs > 0);
301
302     for (unsigned i = 0; i < pool->picture_count; i++) {
303         picture_t *picture = pool->picture[i];
304         picture_gc_sys_t *sys = picture->gc.p_sys;
305
306         if (!sys->in_use) {
307             vlc_mutex_unlock(&pool->lock);
308             return; /* Nothing to do */
309         }
310
311         if (picture->gc.p_sys->tick < tick) {
312             oldest = picture;
313             tick = picture->gc.p_sys->tick;
314         }
315     }
316
317     if (oldest != NULL) {
318         while (oldest->gc.p_sys->in_use) {
319             vlc_mutex_unlock(&pool->lock);
320             picture_Release(oldest);
321             vlc_mutex_lock(&pool->lock);
322         }
323     }
324
325     vlc_mutex_unlock(&pool->lock);
326 }
327
328 unsigned picture_pool_GetSize(const picture_pool_t *pool)
329 {
330     return pool->picture_count;
331 }
332
333 bool picture_pool_NeedsLocking(const picture_pool_t *pool)
334 {
335     return pool->pic_lock != NULL || pool->pic_unlock != NULL;
336 }