picture_pool.c 8.85 KB
Newer Older
1 2 3
/*****************************************************************************
 * picture_pool.c : picture pool functions
 *****************************************************************************
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
4
 * Copyright (C) 2009 VLC authors and VideoLAN
5
 * Copyright (C) 2009 Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
6
 * Copyright (C) 2013-2015 Rémi Denis-Courmont
7 8 9
 *
 * Authors: Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
10 11 12
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
13 14 15 16
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
17 18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
19
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
20 21 22
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 24 25 26 27 28
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <assert.h>
29 30
#include <limits.h>
#include <stdlib.h>
31 32 33

#include <vlc_common.h>
#include <vlc_picture_pool.h>
34
#include <vlc_atomic.h>
35
#include "picture.h"
36

37 38 39
#define POOL_MAX (CHAR_BIT * sizeof (unsigned long long))

static_assert ((POOL_MAX & (POOL_MAX - 1)) == 0, "Not a power of two");
40

41
struct picture_pool_t {
42 43
    int       (*pic_lock)(picture_t *);
    void      (*pic_unlock)(picture_t *);
44
    vlc_mutex_t lock;
45
    vlc_cond_t  wait;
46

47
    bool               canceled;
48
    unsigned long long available;
49 50
    atomic_ushort      refs;
    unsigned short     picture_count;
51
    picture_t  *picture[];
52 53
};

54
static void picture_pool_Destroy(picture_pool_t *pool)
55
{
56
    if (atomic_fetch_sub(&pool->refs, 1) != 1)
57 58
        return;

59
    vlc_cond_destroy(&pool->wait);
60
    vlc_mutex_destroy(&pool->lock);
61
    aligned_free(pool);
62 63
}

64
void picture_pool_Release(picture_pool_t *pool)
65
{
66 67 68 69 70 71 72 73
    for (unsigned i = 0; i < pool->picture_count; i++)
        picture_Release(pool->picture[i]);
    picture_pool_Destroy(pool);
}

static void picture_pool_ReleasePicture(picture_t *clone)
{
    picture_priv_t *priv = (picture_priv_t *)clone;
74
    uintptr_t sys = (uintptr_t)priv->gc.opaque;
75 76
    picture_pool_t *pool = (void *)(sys & ~(POOL_MAX - 1));
    unsigned offset = sys & (POOL_MAX - 1);
77 78 79
    picture_t *picture = pool->picture[offset];

    free(clone);
80

81
    if (pool->pic_unlock != NULL)
82 83
        pool->pic_unlock(picture);
    picture_Release(picture);
84

85
    vlc_mutex_lock(&pool->lock);
86 87
    assert(!(pool->available & (1ULL << offset)));
    pool->available |= 1ULL << offset;
88
    vlc_cond_signal(&pool->wait);
89 90
    vlc_mutex_unlock(&pool->lock);

91
    picture_pool_Destroy(pool);
92 93
}

94
static picture_t *picture_pool_ClonePicture(picture_pool_t *pool,
95
                                            unsigned offset)
96
{
97
    picture_t *picture = pool->picture[offset];
98
    uintptr_t sys = ((uintptr_t)pool) + offset;
99 100 101 102 103 104 105 106 107 108 109 110
    picture_resource_t res = {
        .p_sys = picture->p_sys,
        .pf_destroy = picture_pool_ReleasePicture,
    };

    for (int i = 0; i < picture->i_planes; i++) {
        res.p[i].p_pixels = picture->p[i].p_pixels;
        res.p[i].i_lines = picture->p[i].i_lines;
        res.p[i].i_pitch = picture->p[i].i_pitch;
    }

    picture_t *clone = picture_NewFromResource(&picture->format, &res);
111
    if (likely(clone != NULL)) {
112
        ((picture_priv_t *)clone)->gc.opaque = (void *)sys;
113 114
        picture_Hold(picture);
    }
115 116 117
    return clone;
}

118
picture_pool_t *picture_pool_NewExtended(const picture_pool_configuration_t *cfg)
119
{
120
    if (unlikely(cfg->picture_count > POOL_MAX))
121 122
        return NULL;

123 124 125 126 127
    picture_pool_t *pool;
    size_t size = sizeof (*pool) + cfg->picture_count * sizeof (picture_t *);

    size += (-size) & (POOL_MAX - 1);
    pool = aligned_alloc(POOL_MAX, size);
128
    if (unlikely(pool == NULL))
129 130
        return NULL;

131 132
    pool->pic_lock   = cfg->lock;
    pool->pic_unlock = cfg->unlock;
133
    vlc_mutex_init(&pool->lock);
134
    vlc_cond_init(&pool->wait);
135
    if (cfg->picture_count == POOL_MAX)
136 137 138
        pool->available = ~0ULL;
    else
        pool->available = (1ULL << cfg->picture_count) - 1;
139
    atomic_init(&pool->refs,  1);
140
    pool->picture_count = cfg->picture_count;
141 142
    memcpy(pool->picture, cfg->picture,
           cfg->picture_count * sizeof (picture_t *));
143
    pool->canceled = false;
144
    return pool;
145 146
}

147
picture_pool_t *picture_pool_New(unsigned count, picture_t *const *tab)
148
{
149 150 151 152
    picture_pool_configuration_t cfg = {
        .picture_count = count,
        .picture = tab,
    };
153

154
    return picture_pool_NewExtended(&cfg);
155 156
}

157 158
picture_pool_t *picture_pool_NewFromFormat(const video_format_t *fmt,
                                           unsigned count)
159
{
160 161
    picture_t *picture[count ? count : 1];
    unsigned i;
162

163
    for (i = 0; i < count; i++) {
164
        picture[i] = picture_NewFromFormat(fmt);
165
        if (picture[i] == NULL)
166 167
            goto error;
    }
168

169
    picture_pool_t *pool = picture_pool_New(count, picture);
170
    if (!pool)
171 172
        goto error;

173
    return pool;
174 175

error:
176 177
    while (i > 0)
        picture_Release(picture[--i]);
178 179 180
    return NULL;
}

181
picture_pool_t *picture_pool_Reserve(picture_pool_t *master, unsigned count)
182
{
183 184 185 186 187 188 189 190 191 192
    picture_t *picture[count ? count : 1];
    unsigned i;

    for (i = 0; i < count; i++) {
        picture[i] = picture_pool_Get(master);
        if (picture[i] == NULL)
            goto error;
    }

    picture_pool_t *pool = picture_pool_New(count, picture);
193
    if (!pool)
194
        goto error;
195 196

    return pool;
197 198 199 200 201

error:
    while (i > 0)
        picture_Release(picture[--i]);
    return NULL;
202 203
}

204 205 206 207 208 209 210 211
/** Find next (bit) set */
static int fnsll(unsigned long long x, unsigned i)
{
    if (i >= CHAR_BIT * sizeof (x))
        return 0;
    return ffsll(x & ~((1ULL << i) - 1));
}

212
picture_t *picture_pool_Get(picture_pool_t *pool)
213
{
214 215 216
    vlc_mutex_lock(&pool->lock);
    assert(pool->refs > 0);

217 218 219 220 221 222
    if (pool->canceled)
    {
        vlc_mutex_unlock(&pool->lock);
        return NULL;
    }

223 224 225
    for (unsigned i = ffsll(pool->available); i; i = fnsll(pool->available, i))
    {
        pool->available &= ~(1ULL << (i - 1));
226 227
        vlc_mutex_unlock(&pool->lock);

228
        picture_t *picture = pool->picture[i - 1];
229

230
        if (pool->pic_lock != NULL && pool->pic_lock(picture) != VLC_SUCCESS) {
231
            vlc_mutex_lock(&pool->lock);
232
            pool->available |= 1ULL << (i - 1);
233
            continue;
234
        }
235

236
        picture_t *clone = picture_pool_ClonePicture(pool, i - 1);
237 238 239 240
        if (clone != NULL) {
            assert(clone->p_next == NULL);
            atomic_fetch_add(&pool->refs, 1);
        }
241
        return clone;
242
    }
243 244

    vlc_mutex_unlock(&pool->lock);
245 246 247
    return NULL;
}

248 249 250 251 252 253 254
picture_t *picture_pool_Wait(picture_pool_t *pool)
{
    unsigned i;

    vlc_mutex_lock(&pool->lock);
    assert(pool->refs > 0);

255
    while (pool->available == 0)
256
    {
257 258 259 260 261 262
        if (pool->canceled)
        {
            vlc_mutex_unlock(&pool->lock);
            return NULL;
        }
        vlc_cond_wait(&pool->wait, &pool->lock);
263 264
    }

265 266 267 268 269 270 271
    i = ffsll(pool->available);
    assert(i > 0);
    pool->available &= ~(1ULL << (i - 1));
    vlc_mutex_unlock(&pool->lock);

    picture_t *picture = pool->picture[i - 1];

272
    if (pool->pic_lock != NULL && pool->pic_lock(picture) != VLC_SUCCESS) {
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
        vlc_mutex_lock(&pool->lock);
        pool->available |= 1ULL << (i - 1);
        vlc_cond_signal(&pool->wait);
        vlc_mutex_unlock(&pool->lock);
        return NULL;
    }

    picture_t *clone = picture_pool_ClonePicture(pool, i - 1);
    if (clone != NULL) {
        assert(clone->p_next == NULL);
        atomic_fetch_add(&pool->refs, 1);
    }
    return clone;
}

288
void picture_pool_Cancel(picture_pool_t *pool, bool canceled)
289 290 291 292
{
    vlc_mutex_lock(&pool->lock);
    assert(pool->refs > 0);

293 294 295
    pool->canceled = canceled;
    if (canceled)
        vlc_cond_broadcast(&pool->wait);
296 297 298
    vlc_mutex_unlock(&pool->lock);
}

299 300 301
bool picture_pool_OwnsPic(picture_pool_t *pool, picture_t *pic)
{
    picture_priv_t *priv = (picture_priv_t *)pic;
302 303 304 305 306 307

    while (priv->gc.destroy != picture_pool_ReleasePicture) {
        pic = priv->gc.opaque;
        priv = (picture_priv_t *)pic;
    }

308 309 310 311 312
    uintptr_t sys = (uintptr_t)priv->gc.opaque;
    picture_pool_t *picpool = (void *)(sys & ~(POOL_MAX - 1));
    return pool == picpool;
}

313
unsigned picture_pool_GetSize(const picture_pool_t *pool)
314 315 316
{
    return pool->picture_count;
}
317

318 319 320 321 322 323 324 325
void picture_pool_Enum(picture_pool_t *pool, void (*cb)(void *, picture_t *),
                       void *opaque)
{
    /* NOTE: So far, the pictures table cannot change after the pool is created
     * so there is no need to lock the pool mutex here. */
    for (unsigned i = 0; i < pool->picture_count; i++)
        cb(opaque, pool->picture[i]);
}