Commit 9debd679 authored by Niklas Haas's avatar Niklas Haas
Browse files

utils/libav: add plane creation/download helpers

These helpers are useful when preparing render targets based on
AVFrames, such as when feeding the output of libplacebo into a further
filter chain (or encoder) with an already existing AVFrame.
parent 84e6848d
......@@ -2,7 +2,7 @@ project('libplacebo', ['c', 'cpp'],
license: 'LGPL2.1+',
default_options: ['c_std=c99', 'cpp_std=c++11', 'warning_level=2'],
meson_version: '>=0.51',
version: '3.115.0',
version: '3.116.0',
)
# Version number
......
......@@ -43,6 +43,16 @@ static void pl_frame_from_avframe(struct pl_frame *out_frame, const AVFrame *fra
// a given AVPixelFormat, without actually uploading or allocating anything.
static bool pl_test_pixfmt(const struct pl_gpu *gpu, enum AVPixelFormat pixfmt);
// Like `pl_frame_from_avframe`, but the texture pointers are also initialized
// to ensure they have the correct size and format to match the AVframe.
// Similar in spirit to `pl_recreate_plane`, and the same notes apply. `tex`
// must be an array of 4 pointers of type (const struct pl_tex *), each either
// pointing to a valid texture, or NULL. Returns whether successful.
static bool pl_frame_recreate_from_avframe(const struct pl_gpu *gpu,
struct pl_frame *out_frame,
const struct pl_tex *tex[4],
const AVFrame *frame);
// Very high level helper function to take an `AVFrame` and upload it to the
// GPU. Similar in spirit to `pl_upload_plane`, and the same notes apply. `tex`
// must be an array of 4 pointers of type (const struct pl_tex *), each either
......@@ -60,6 +70,16 @@ static bool pl_upload_avframe(const struct pl_gpu *gpu,
const struct pl_tex *tex[4],
const AVFrame *frame);
// Download the texture contents of a `pl_frame` back to a corresponding
// AVFrame. Blocks until completion.
//
// Note: This function performs minimal verification, so incorrect usage will
// likely result in broken frames. Use `pl_frame_recreate_from_avframe` to
// ensure matching formats.
static bool pl_download_avframe(const struct pl_gpu *gpu,
const struct pl_frame *frame,
AVFrame *out_frame);
// Helper functions to update the colorimetry data in an AVFrame based on
// the values specified in the given color space / color repr / profile.
//
......
......@@ -576,6 +576,31 @@ static inline void pl_frame_from_avframe(struct pl_frame *out,
}
}
static inline bool pl_frame_recreate_from_avframe(const struct pl_gpu *gpu,
struct pl_frame *out,
const struct pl_tex *tex[4],
const AVFrame *frame)
{
pl_frame_from_avframe(out, frame);
struct pl_plane_data data[4] = {0};
int planes = pl_plane_data_from_pixfmt(data, &out->repr.bits, frame->format);
if (!planes)
return false;
const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
for (int p = 0; p < planes; p++) {
bool is_chroma = p == 1 || p == 2; // matches lavu logic
data[p].width = frame->width >> (is_chroma ? desc->log2_chroma_w : 0);
data[p].height = frame->height >> (is_chroma ? desc->log2_chroma_h : 0);
if (!pl_recreate_plane(gpu, &out->planes[p], &tex[p], &data[p]))
return false;
}
return true;
}
static void pl_avbuffer_free(void *priv)
{
AVBufferRef *buf = priv;
......@@ -619,4 +644,41 @@ static inline bool pl_upload_avframe(const struct pl_gpu *gpu,
return true;
}
static void pl_done_cb(void *priv)
{
bool *status = priv;
*status = true;
}
static inline bool pl_download_avframe(const struct pl_gpu *gpu,
const struct pl_frame *frame,
AVFrame *out_frame)
{
bool done[4] = {0};
if (frame->num_planes != av_pix_fmt_count_planes(out_frame->format))
return false;
for (int p = 0; p < frame->num_planes; p++) {
size_t texel_size = frame->planes[p].texture->params.format->texel_size;
bool ok = pl_tex_download(gpu, &(struct pl_tex_transfer_params) {
.tex = frame->planes[p].texture,
.stride_w = out_frame->linesize[p] / texel_size,
.ptr = out_frame->data[p],
// Use synchronous transfer for the last plane
.callback = (p+1) < frame->num_planes ? pl_done_cb : NULL,
.priv = &done[p],
});
if (!ok)
return false;
}
for (int p = 0; p < frame->num_planes - 1; p++) {
while (!done[p])
pl_tex_poll(gpu, frame->planes[p].texture, UINT64_MAX);
}
return true;
}
#endif // LIBPLACEBO_LIBAV_H_
......@@ -129,4 +129,14 @@ const struct pl_fmt *pl_plane_find_fmt(const struct pl_gpu *gpu, int out_map[4],
bool pl_upload_plane(const struct pl_gpu *gpu, struct pl_plane *out_plane,
const struct pl_tex **tex, const struct pl_plane_data *data);
// Like `pl_upload_plane`, but only creates an uninitialized texture object
// rather than actually performing an upload. This can be useful to, for
// example, prepare textures to be used as the target of rendering.
//
// The resulting texture is guaranteed to be `renderable`, and it will also try
// to maximize compatibility with the other `pl_renderer` reequirements
// (blittable, storable, etc.).
bool pl_recreate_plane(const struct pl_gpu *gpu, struct pl_plane *out_plane,
const struct pl_tex **tex, const struct pl_plane_data *data);
#endif // LIBPLACEBO_UPLOAD_H_
......@@ -843,11 +843,8 @@ static const char *test_luts[] = {
static void pl_render_tests(const struct pl_gpu *gpu)
{
const struct pl_fmt *fbo_fmt = pl_find_fmt(gpu, PL_FMT_FLOAT, 4, 16, 32,
PL_FMT_CAP_RENDERABLE |
PL_FMT_CAP_BLITTABLE);
if (!fbo_fmt)
return;
const struct pl_tex *img5x5_tex = NULL, *fbo = NULL;
struct pl_renderer *rr = NULL;
float *fbo_data = NULL;
static float data_5x5[5][5] = {
......@@ -859,10 +856,8 @@ static void pl_render_tests(const struct pl_gpu *gpu)
};
const int width = 5, height = 5;
struct pl_plane img5x5 = {0};
const struct pl_tex *img5x5_tex = NULL;
bool ok = pl_upload_plane(gpu, &img5x5, &img5x5_tex, &(struct pl_plane_data) {
struct pl_plane_data img5x5_data = {
.type = PL_FMT_FLOAT,
.width = width,
.height = height,
......@@ -870,27 +865,15 @@ static void pl_render_tests(const struct pl_gpu *gpu)
.component_map = { 0 },
.pixel_stride = sizeof(float),
.pixels = &data_5x5,
});
};
if (!ok) {
pl_tex_destroy(gpu, &img5x5.texture);
if (!pl_recreate_plane(gpu, NULL, &fbo, &img5x5_data))
return;
}
const struct pl_tex *fbo = pl_tex_create(gpu, &(struct pl_tex_params) {
.w = 40,
.h = 40,
.format = fbo_fmt,
.renderable = true,
.blit_dst = true,
.storable = !!(fbo_fmt->caps & PL_FMT_CAP_STORABLE),
.host_readable = true,
});
struct pl_renderer *rr = pl_renderer_create(gpu->ctx, gpu);
if (!fbo || !rr)
if (!pl_upload_plane(gpu, &img5x5, &img5x5_tex, &img5x5_data))
goto error;
rr = pl_renderer_create(gpu->ctx, gpu);
pl_tex_clear(gpu, fbo, (float[4]){0});
struct pl_frame image = {
......
......@@ -233,7 +233,7 @@ bool pl_upload_plane(const struct pl_gpu *gpu, struct pl_plane *out_plane,
.format = fmt,
.sampleable = true,
.host_writable = true,
.blit_src = !!(fmt->caps & PL_FMT_CAP_BLITTABLE),
.blit_src = fmt->caps & PL_FMT_CAP_BLITTABLE,
});
if (!ok) {
......@@ -261,3 +261,41 @@ bool pl_upload_plane(const struct pl_gpu *gpu, struct pl_plane *out_plane,
.priv = data->priv,
});
}
bool pl_recreate_plane(const struct pl_gpu *gpu, struct pl_plane *out_plane,
const struct pl_tex **tex, const struct pl_plane_data *data)
{
int out_map[4];
const struct pl_fmt *fmt = pl_plane_find_fmt(gpu, out_map, data);
if (!fmt) {
PL_ERR(gpu, "Failed picking any compatible texture format for a plane!");
return false;
}
bool ok = pl_tex_recreate(gpu, tex, &(struct pl_tex_params) {
.w = data->width,
.h = data->height,
.format = fmt,
.renderable = true,
.host_readable = true,
.blit_dst = fmt->caps & PL_FMT_CAP_BLITTABLE,
.storable = fmt->caps & PL_FMT_CAP_STORABLE,
});
if (!ok) {
PL_ERR(gpu, "Failed initializing plane texture!");
return false;
}
if (out_plane) {
out_plane->texture = *tex;
out_plane->components = 0;
for (int i = 0; i < PL_ARRAY_SIZE(out_map); i++) {
out_plane->component_mapping[i] = out_map[i];
if (out_map[i] >= 0)
out_plane->components = i+1;
}
}
return true;
}
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