Commit 1c42f8bf authored by Steve Lhomme's avatar Steve Lhomme Committed by Ronald S. Bultje

Add support for external picture buffer allocation

parent 793c5048
Pipeline #1141 passed with stage
in 2 minutes and 1 second
......@@ -38,6 +38,7 @@ typedef struct Dav1dRef Dav1dRef;
typedef struct Dav1dSettings {
int n_frame_threads;
int n_tile_threads;
Dav1dPicAllocator allocator;
} Dav1dSettings;
/**
......
......@@ -143,8 +143,43 @@ typedef struct Dav1dPicture {
Dav1dPictureParameters p;
int poc; ///< frame number
void *allocator_data; ///< pointer managed by the allocator
} Dav1dPicture;
typedef struct Dav1dPicAllocator {
void *cookie; ///< custom data to pass to the allocator callbacks.
/**
* Allocate the picture buffer based on the Dav1dPictureParameters.
*
* The data[0], data[1] and data[2] must be 32 bits aligned and with a
* pixel width/height multiple of 128 pixels.
* data[1] and data[2] must share the same stride[1].
*
* @param pic The picture to allocate the buffer for. The callback needs to
* fill the picture data[0], data[1], data[2], stride[0] and
* stride[1].
* The allocator can fill the pic allocator_data pointer with
* a custom pointer that will be passed to
* release_picture_callback().
* @param cookie Custom pointer passed to all calls.
*
* @return 0 on success. A negative errno value on error.
*/
int (*alloc_picture_callback)(Dav1dPicture *pic, void *cookie);
/**
* Release the picture buffer.
*
* @param buf The buffer that was returned by
* alloc_picture_callback().
* @param allocator_tag The Dav1dPicture.allocator_data that was filled by
* alloc_picture_callback()
* @param cookie Custom pointer passed to all calls.
*/
void (*release_picture_callback)(uint8_t *buf, void *allocator_data,
void *cookie);
} Dav1dPicAllocator;
/**
* Release reference to a picture.
*/
......
......@@ -56,7 +56,7 @@ int dav1d_data_wrap(Dav1dData *const buf, uint8_t *const ptr, const size_t sz,
validate_input_or_ret(ptr != NULL, -EINVAL);
validate_input_or_ret(free_callback != NULL, -EINVAL);
buf->ref = dav1d_ref_wrap(ptr, sz, free_callback, user_data);
buf->ref = dav1d_ref_wrap(ptr, free_callback, user_data);
if (!buf->ref) return -ENOMEM;
buf->data = ptr;
buf->sz = sz;
......
......@@ -2891,7 +2891,8 @@ int dav1d_submit_frame(Dav1dContext *const c) {
f->frame_hdr.height,
f->seq_hdr.layout, f->seq_hdr.bpc,
c->n_fc > 1 ? &f->frame_thread.td : NULL,
f->frame_hdr.show_frame)) < 0)
f->frame_hdr.show_frame,
&c->allocator)) < 0)
{
if (f->frame_hdr.refresh_context)
dav1d_cdf_thread_unref(&f->out_cdf);
......
......@@ -110,6 +110,8 @@ struct Dav1dContext {
EdgeTip tip_sb128[256];
EdgeTip tip_sb64[64];
} intra_edge;
Dav1dPicAllocator allocator;
};
struct Dav1dFrameContext {
......
......@@ -57,6 +57,9 @@ const char *dav1d_version(void) {
void dav1d_default_settings(Dav1dSettings *const s) {
s->n_frame_threads = 1;
s->n_tile_threads = 1;
s->allocator.cookie = NULL;
s->allocator.alloc_picture_callback = default_picture_allocator;
s->allocator.release_picture_callback = default_picture_release;
}
int dav1d_open(Dav1dContext **const c_out,
......@@ -71,11 +74,16 @@ int dav1d_open(Dav1dContext **const c_out,
s->n_tile_threads <= 64, -EINVAL);
validate_input_or_ret(s->n_frame_threads >= 1 &&
s->n_frame_threads <= 256, -EINVAL);
validate_input_or_ret(s->allocator.alloc_picture_callback != NULL,
-EINVAL);
validate_input_or_ret(s->allocator.release_picture_callback != NULL,
-EINVAL);
Dav1dContext *const c = *c_out = dav1d_alloc_aligned(sizeof(*c), 32);
if (!c) goto error;
memset(c, 0, sizeof(*c));
c->allocator = s->allocator;
c->n_fc = s->n_frame_threads;
c->fc = dav1d_alloc_aligned(sizeof(*c->fc) * s->n_frame_threads, 32);
if (!c->fc) goto error;
......
......@@ -41,50 +41,108 @@
#include "src/ref.h"
#include "src/thread.h"
int default_picture_allocator(Dav1dPicture *const p, void *cookie) {
assert(cookie == NULL);
const int hbd = p->p.bpc > 8;
const int aligned_w = (p->p.w + 127) & ~127;
const int aligned_h = (p->p.h + 127) & ~127;
const int has_chroma = p->p.layout != DAV1D_PIXEL_LAYOUT_I400;
const int ss_ver = p->p.layout == DAV1D_PIXEL_LAYOUT_I420;
const int ss_hor = p->p.layout != DAV1D_PIXEL_LAYOUT_I444;
p->stride[0] = aligned_w << hbd;
p->stride[1] = has_chroma ? (aligned_w >> ss_hor) << hbd : 0;
const size_t y_sz = p->stride[0] * aligned_h;
const size_t uv_sz = p->stride[1] * (aligned_h >> ss_ver);
const size_t pic_size = y_sz + 2 * uv_sz;
uint8_t *data = dav1d_alloc_aligned(pic_size, 32);
if (data == NULL) {
fprintf(stderr, "Failed to allocate memory of size %zu: %s\n",
pic_size, strerror(errno));
return -1;
}
p->data[0] = data;
p->data[1] = has_chroma ? data + y_sz : NULL;
p->data[2] = has_chroma ? data + y_sz + uv_sz : NULL;
#ifndef NDEBUG /* safety check */
p->allocator_data = data;
#endif
return 0;
}
void default_picture_release(uint8_t *const data, void *const allocator_data,
void *cookie)
{
assert(cookie == NULL);
#ifndef NDEBUG /* safety check */
assert(allocator_data == data);
#endif
dav1d_free_aligned(data);
}
struct pic_ctx_context {
Dav1dPicAllocator allocator;
void *allocator_data;
void *extra_ptr; /* MUST BE AT THE END */
};
static void free_buffer(uint8_t *data, void *user_data)
{
struct pic_ctx_context *pic_ctx = user_data;
pic_ctx->allocator.release_picture_callback(data,
pic_ctx->allocator_data,
pic_ctx->allocator.cookie);
free(pic_ctx);
}
static int picture_alloc_with_edges(Dav1dPicture *const p,
const int w, const int h,
const enum Dav1dPixelLayout layout,
const int bpc,
const int extra, void **const extra_ptr)
Dav1dPicAllocator *const p_allocator,
const size_t extra, void **const extra_ptr)
{
int aligned_h;
if (p->data[0]) {
fprintf(stderr, "Picture already allocated!\n");
return -1;
}
assert(bpc > 0 && bpc <= 16);
const int hbd = bpc > 8;
const int aligned_w = (w + 127) & ~127;
const int has_chroma = layout != DAV1D_PIXEL_LAYOUT_I400;
const int ss_ver = layout == DAV1D_PIXEL_LAYOUT_I420;
const int ss_hor = layout != DAV1D_PIXEL_LAYOUT_I444;
p->stride[0] = aligned_w << hbd;
p->stride[1] = has_chroma ? (aligned_w >> ss_hor) << hbd : 0;
struct pic_ctx_context *pic_ctx = malloc(extra + sizeof(struct pic_ctx_context));
if (pic_ctx == NULL) {
return -ENOMEM;
}
p->p.w = w;
p->p.h = h;
p->p.pri = DAV1D_COLOR_PRI_UNKNOWN;
p->p.trc = DAV1D_TRC_UNKNOWN;
p->p.mtrx = DAV1D_MC_UNKNOWN;
p->p.chr = DAV1D_CHR_UNKNOWN;
aligned_h = (h + 127) & ~127;
p->p.layout = layout;
p->p.bpc = bpc;
const size_t y_sz = p->stride[0] * aligned_h;
const size_t uv_sz = p->stride[1] * (aligned_h >> ss_ver);
if (!(p->ref = dav1d_ref_create(y_sz + 2 * uv_sz + extra))) {
fprintf(stderr, "Failed to allocate memory of size %zu: %s\n",
y_sz + 2 * uv_sz + extra, strerror(errno));
int res = p_allocator->alloc_picture_callback(p, p_allocator->cookie);
if (res < 0) {
free(pic_ctx);
return -ENOMEM;
}
pic_ctx->allocator = *p_allocator;
pic_ctx->allocator_data = p->allocator_data;
if (!(p->ref = dav1d_ref_wrap(p->data[0], free_buffer, pic_ctx))) {
p_allocator->release_picture_callback(p->data[0], p->allocator_data,
p_allocator->cookie);
fprintf(stderr, "Failed to wrap picture: %s\n", strerror(errno));
return -ENOMEM;
}
uint8_t *data = p->ref->data;
p->data[0] = data;
p->data[1] = has_chroma ? data + y_sz : NULL;
p->data[2] = has_chroma ? data + y_sz + uv_sz : NULL;
if (extra)
*extra_ptr = &data[y_sz + uv_sz * 2];
if (extra && extra_ptr)
*extra_ptr = &pic_ctx->extra_ptr;
return 0;
}
......@@ -92,12 +150,13 @@ static int picture_alloc_with_edges(Dav1dPicture *const p,
int dav1d_thread_picture_alloc(Dav1dThreadPicture *const p,
const int w, const int h,
const enum Dav1dPixelLayout layout, const int bpc,
struct thread_data *const t, const int visible)
struct thread_data *const t, const int visible,
Dav1dPicAllocator *const p_allocator)
{
p->t = t;
const int res =
picture_alloc_with_edges(&p->p, w, h, layout, bpc,
picture_alloc_with_edges(&p->p, w, h, layout, bpc, p_allocator,
t != NULL ? sizeof(atomic_int) * 2 : 0,
(void **) &p->progress);
......
......@@ -56,7 +56,8 @@ typedef struct Dav1dThreadPicture {
*/
int dav1d_thread_picture_alloc(Dav1dThreadPicture *p, int w, int h,
enum Dav1dPixelLayout layout, int bpc,
struct thread_data *t, int visible);
struct thread_data *t, int visible,
Dav1dPicAllocator *);
/**
* Create a copy of a picture.
......@@ -88,4 +89,7 @@ void dav1d_thread_picture_wait(const Dav1dThreadPicture *p, int y,
void dav1d_thread_picture_signal(const Dav1dThreadPicture *p, int y,
enum PlaneType plane_type);
int default_picture_allocator(Dav1dPicture *, void *cookie);
void default_picture_release(uint8_t *, void *allocator_data, void *cookie);
#endif /* __DAV1D_SRC_PICTURE_H__ */
......@@ -36,24 +36,21 @@ static void default_free_callback(uint8_t *const data, void *const user_data) {
}
Dav1dRef *dav1d_ref_create(const size_t size) {
Dav1dRef *res = malloc(sizeof(Dav1dRef));
Dav1dRef *res;
void *data = dav1d_alloc_aligned(size, 32);
if (!res || !data) {
if (res) free(res);
if (data) free(data);
if (!data) {
return NULL;
}
res->size = size;
atomic_init(&res->ref_cnt, 1);
res->data = data;
res->free_callback = default_free_callback;
res = dav1d_ref_wrap(data, default_free_callback, NULL);
if (!res) {
free(data);
}
return res;
}
Dav1dRef *dav1d_ref_wrap(uint8_t *const ptr, const size_t sz,
Dav1dRef *dav1d_ref_wrap(uint8_t *const ptr,
void (*free_callback)(uint8_t *data, void *user_data),
void *user_data)
{
......@@ -61,7 +58,6 @@ Dav1dRef *dav1d_ref_wrap(uint8_t *const ptr, const size_t sz,
if (!res) return NULL;
res->data = ptr;
res->size = sz;
atomic_init(&res->ref_cnt, 1);
res->free_callback = free_callback;
res->user_data = user_data;
......
......@@ -35,14 +35,13 @@
struct Dav1dRef {
void *data;
size_t size;
atomic_int ref_cnt;
void (*free_callback)(uint8_t *data, void *user_data);
void *user_data;
};
Dav1dRef *dav1d_ref_create(size_t size);
Dav1dRef *dav1d_ref_wrap(uint8_t *ptr, size_t sz,
Dav1dRef *dav1d_ref_wrap(uint8_t *ptr,
void (*free_callback)(uint8_t *data, void *user_data),
void *user_data);
void dav1d_ref_inc(Dav1dRef *ref);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment