summaryrefslogtreecommitdiffstats
path: root/video/mp_image_pool.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2012-12-12 23:55:34 +0100
committerwm4 <wm4@nowhere>2013-01-13 17:39:32 +0100
commit1568161aadf24ee3a6d982612b7380f8b1dc4a58 (patch)
tree8f101603359615835d0994bf244c97496f615003 /video/mp_image_pool.c
parent881f82dd33fe8d13e731bfe04077d3abcd149fa3 (diff)
downloadmpv-1568161aadf24ee3a6d982612b7380f8b1dc4a58.tar.bz2
mpv-1568161aadf24ee3a6d982612b7380f8b1dc4a58.tar.xz
mp_image_pool: add pool to avoid frequent image reallocations
Refcounting will conceptually allocate and free images all the time when using the filter chain. Add a pool that makes these reallocations cheap. This only affects the image data, not mp_image structs and similar small allocations. Small allocations are always fast with reasonable memory managers, while large image data will trigger mmap/munmap calls each time.
Diffstat (limited to 'video/mp_image_pool.c')
-rw-r--r--video/mp_image_pool.c133
1 files changed, 133 insertions, 0 deletions
diff --git a/video/mp_image_pool.c b/video/mp_image_pool.c
new file mode 100644
index 0000000000..dd424ad2a4
--- /dev/null
+++ b/video/mp_image_pool.c
@@ -0,0 +1,133 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with mpv; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <assert.h>
+
+#include "talloc.h"
+
+#include "core/mp_common.h"
+#include "video/mp_image.h"
+
+#include "mp_image_pool.h"
+
+struct pool_trampoline {
+ struct mp_image_pool *pool;
+ int refcount;
+};
+
+static int image_pool_destructor(void *ptr)
+{
+ struct mp_image_pool *pool = ptr;
+ mp_image_pool_clear(pool);
+ pool->trampoline->pool = NULL;
+ if (!pool->trampoline->refcount)
+ talloc_free(pool->trampoline);
+ return 0;
+}
+
+struct mp_image_pool *mp_image_pool_new(int max_count)
+{
+ struct mp_image_pool *pool = talloc_ptrtype(NULL, pool);
+ talloc_set_destructor(pool, image_pool_destructor);
+ *pool = (struct mp_image_pool) {
+ .max_count = max_count,
+ .trampoline = talloc_struct(NULL, struct pool_trampoline, {
+ .pool = pool,
+ }),
+ };
+ return pool;
+}
+
+void mp_image_pool_clear(struct mp_image_pool *pool)
+{
+ for (int n = 0; n < pool->num_images; n++) {
+ struct mp_image *img = pool->images[n];
+ if (img->priv) {
+ // Image data is being used - detach from the pool, and make it
+ // free itself when pool_free_image() is called
+ img->priv = talloc_struct(NULL, struct pool_trampoline, {
+ .refcount = 1,
+ });
+ } else {
+ talloc_free(img);
+ }
+ }
+ pool->num_images = 0;
+ pool->trampoline->refcount = 0;
+}
+
+static void pool_free_image(void *ptr)
+{
+ struct mp_image *img = ptr;
+ struct pool_trampoline *trampoline = img->priv;
+ trampoline->refcount--;
+ img->priv = NULL;
+ if (!trampoline->pool) {
+ // pool was free'd while image reference was still held
+ if (trampoline->refcount == 0)
+ talloc_free(trampoline);
+ talloc_free(img);
+ }
+}
+
+// Return a new image of given format/size. The only difference to
+// mp_image_alloc() is that there is a transparent mechanism to recycle image
+// data allocations through this pool.
+// The image can be free'd with talloc_free().
+struct mp_image *mp_image_pool_get(struct mp_image_pool *pool, unsigned int fmt,
+ int w, int h)
+{
+ struct mp_image *new = NULL;
+ for (int n = 0; n < pool->num_images; n++) {
+ struct mp_image *img = pool->images[n];
+ if (!img->priv && img->imgfmt == fmt && img->w == w && img->h == h) {
+ new = img;
+ break;
+ }
+ }
+ if (!new) {
+ if (pool->num_images >= pool->max_count)
+ mp_image_pool_clear(pool);
+ new = mp_image_alloc(fmt, w, h);
+ MP_TARRAY_APPEND(pool, pool->images, pool->num_images, new);
+ }
+ pool->trampoline->refcount++;
+ new->priv = pool->trampoline;
+ return mp_image_new_custom_ref(new, new, pool_free_image);
+}
+
+// Like mp_image_new_copy(), but allocate the image out of the pool.
+struct mp_image *mp_image_pool_new_copy(struct mp_image_pool *pool,
+ struct mp_image *img)
+{
+ struct mp_image *new = mp_image_pool_get(pool, img->imgfmt, img->w, img->h);
+ mp_image_copy(new, img);
+ mp_image_copy_attributes(new, img);
+ return new;
+}
+
+// Like mp_image_make_writeable(), but if a copy has to be made, allocate it
+// out of the pool.
+void mp_image_pool_make_writeable(struct mp_image_pool *pool,
+ struct mp_image *img)
+{
+ if (mp_image_is_writeable(img))
+ return;
+ mp_image_steal_data(img, mp_image_pool_new_copy(pool, img));
+ assert(mp_image_is_writeable(img));
+}