/*
* This file is part of libplacebo.
*
* libplacebo 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 (at your option) any later version.
*
* libplacebo 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with libplacebo. If not, see .
*/
#ifndef LIBPLACEBO_DAV1D_H_
#error This header should be included as part of
#elif defined(__cplusplus)
#error This header cannot be included from C++ define PL_DAV1D_IMPLEMENTATION appropriately
#else
#include
#include
#include
PL_DAV1D_API enum pl_color_system pl_system_from_dav1d(enum Dav1dMatrixCoefficients mc)
{
switch (mc) {
case DAV1D_MC_IDENTITY: return PL_COLOR_SYSTEM_RGB; // or XYZ (unlikely)
case DAV1D_MC_BT709: return PL_COLOR_SYSTEM_BT_709;
case DAV1D_MC_UNKNOWN: return PL_COLOR_SYSTEM_UNKNOWN;
case DAV1D_MC_FCC: return PL_COLOR_SYSTEM_UNKNOWN; // missing
case DAV1D_MC_BT470BG: return PL_COLOR_SYSTEM_BT_601;
case DAV1D_MC_BT601: return PL_COLOR_SYSTEM_BT_601;
case DAV1D_MC_SMPTE240: return PL_COLOR_SYSTEM_SMPTE_240M;
case DAV1D_MC_SMPTE_YCGCO: return PL_COLOR_SYSTEM_YCGCO;
case DAV1D_MC_BT2020_NCL: return PL_COLOR_SYSTEM_BT_2020_NC;
case DAV1D_MC_BT2020_CL: return PL_COLOR_SYSTEM_BT_2020_C;
case DAV1D_MC_SMPTE2085: return PL_COLOR_SYSTEM_UNKNOWN; // missing
case DAV1D_MC_CHROMAT_NCL: return PL_COLOR_SYSTEM_UNKNOWN; // missing
case DAV1D_MC_CHROMAT_CL: return PL_COLOR_SYSTEM_UNKNOWN; // missing
// Note: this colorspace is confused between PQ and HLG, which dav1d
// requires inferring from other sources, but libplacebo makes
// explicit. Default to PQ as it's the more common scenario.
case DAV1D_MC_ICTCP: return PL_COLOR_SYSTEM_BT_2100_PQ;
case DAV1D_MC_RESERVED: abort();
}
return PL_COLOR_SYSTEM_UNKNOWN;
}
PL_DAV1D_API enum Dav1dMatrixCoefficients pl_system_to_dav1d(enum pl_color_system sys)
{
switch (sys) {
case PL_COLOR_SYSTEM_UNKNOWN: return DAV1D_MC_UNKNOWN;
case PL_COLOR_SYSTEM_BT_601: return DAV1D_MC_BT601;
case PL_COLOR_SYSTEM_BT_709: return DAV1D_MC_BT709;
case PL_COLOR_SYSTEM_SMPTE_240M: return DAV1D_MC_SMPTE240;
case PL_COLOR_SYSTEM_BT_2020_NC: return DAV1D_MC_BT2020_NCL;
case PL_COLOR_SYSTEM_BT_2020_C: return DAV1D_MC_BT2020_CL;
case PL_COLOR_SYSTEM_BT_2100_PQ: return DAV1D_MC_ICTCP;
case PL_COLOR_SYSTEM_BT_2100_HLG: return DAV1D_MC_ICTCP;
case PL_COLOR_SYSTEM_DOLBYVISION: return DAV1D_MC_UNKNOWN; // missing
case PL_COLOR_SYSTEM_YCGCO: return DAV1D_MC_SMPTE_YCGCO;
case PL_COLOR_SYSTEM_YCGCO_RE: return DAV1D_MC_UNKNOWN; // missing
case PL_COLOR_SYSTEM_YCGCO_RO: return DAV1D_MC_UNKNOWN; // missing
case PL_COLOR_SYSTEM_RGB: return DAV1D_MC_IDENTITY;
case PL_COLOR_SYSTEM_XYZ: return DAV1D_MC_IDENTITY;
case PL_COLOR_SYSTEM_COUNT: abort();
}
return DAV1D_MC_UNKNOWN;
}
PL_DAV1D_API enum pl_color_levels pl_levels_from_dav1d(int color_range)
{
return color_range ? PL_COLOR_LEVELS_FULL : PL_COLOR_LEVELS_LIMITED;
}
PL_DAV1D_API int pl_levels_to_dav1d(enum pl_color_levels levels)
{
return levels == PL_COLOR_LEVELS_FULL;
}
PL_DAV1D_API enum pl_color_primaries pl_primaries_from_dav1d(enum Dav1dColorPrimaries prim)
{
switch (prim) {
case DAV1D_COLOR_PRI_BT709: return PL_COLOR_PRIM_BT_709;
case DAV1D_COLOR_PRI_UNKNOWN: return PL_COLOR_PRIM_UNKNOWN;
case DAV1D_COLOR_PRI_RESERVED: return PL_COLOR_PRIM_UNKNOWN;
case DAV1D_COLOR_PRI_BT470M: return PL_COLOR_PRIM_BT_470M;
case DAV1D_COLOR_PRI_BT470BG: return PL_COLOR_PRIM_BT_601_625;
case DAV1D_COLOR_PRI_BT601: return PL_COLOR_PRIM_BT_601_525;
case DAV1D_COLOR_PRI_SMPTE240: return PL_COLOR_PRIM_BT_601_525;
case DAV1D_COLOR_PRI_FILM: return PL_COLOR_PRIM_FILM_C;
case DAV1D_COLOR_PRI_BT2020: return PL_COLOR_PRIM_BT_2020;
case DAV1D_COLOR_PRI_XYZ: return PL_COLOR_PRIM_UNKNOWN;
case DAV1D_COLOR_PRI_SMPTE431: return PL_COLOR_PRIM_DCI_P3;
case DAV1D_COLOR_PRI_SMPTE432: return PL_COLOR_PRIM_DISPLAY_P3;
case DAV1D_COLOR_PRI_EBU3213: return PL_COLOR_PRIM_EBU_3213;
}
return PL_COLOR_PRIM_UNKNOWN;
}
PL_DAV1D_API enum Dav1dColorPrimaries pl_primaries_to_dav1d(enum pl_color_primaries prim)
{
switch (prim) {
case PL_COLOR_PRIM_UNKNOWN: return DAV1D_COLOR_PRI_UNKNOWN;
case PL_COLOR_PRIM_BT_601_525: return DAV1D_COLOR_PRI_BT601;
case PL_COLOR_PRIM_BT_601_625: return DAV1D_COLOR_PRI_BT470BG;
case PL_COLOR_PRIM_BT_709: return DAV1D_COLOR_PRI_BT709;
case PL_COLOR_PRIM_BT_470M: return DAV1D_COLOR_PRI_BT470M;
case PL_COLOR_PRIM_EBU_3213: return DAV1D_COLOR_PRI_EBU3213;
case PL_COLOR_PRIM_BT_2020: return DAV1D_COLOR_PRI_BT2020;
case PL_COLOR_PRIM_APPLE: return DAV1D_COLOR_PRI_UNKNOWN; // missing
case PL_COLOR_PRIM_ADOBE: return DAV1D_COLOR_PRI_UNKNOWN; // missing
case PL_COLOR_PRIM_PRO_PHOTO: return DAV1D_COLOR_PRI_UNKNOWN; // missing
case PL_COLOR_PRIM_CIE_1931: return DAV1D_COLOR_PRI_UNKNOWN; // missing
case PL_COLOR_PRIM_DCI_P3: return DAV1D_COLOR_PRI_SMPTE431;
case PL_COLOR_PRIM_DISPLAY_P3: return DAV1D_COLOR_PRI_SMPTE432;
case PL_COLOR_PRIM_V_GAMUT: return DAV1D_COLOR_PRI_UNKNOWN; // missing
case PL_COLOR_PRIM_S_GAMUT: return DAV1D_COLOR_PRI_UNKNOWN; // missing
case PL_COLOR_PRIM_FILM_C: return DAV1D_COLOR_PRI_FILM;
case PL_COLOR_PRIM_ACES_AP0: return DAV1D_COLOR_PRI_UNKNOWN; // missing
case PL_COLOR_PRIM_ACES_AP1: return DAV1D_COLOR_PRI_UNKNOWN; // missing
case PL_COLOR_PRIM_COUNT: abort();
}
return DAV1D_COLOR_PRI_UNKNOWN;
}
PL_DAV1D_API enum pl_color_transfer pl_transfer_from_dav1d(enum Dav1dTransferCharacteristics trc)
{
switch (trc) {
case DAV1D_TRC_BT709: return PL_COLOR_TRC_BT_1886; // EOTF != OETF
case DAV1D_TRC_UNKNOWN: return PL_COLOR_TRC_UNKNOWN;
case DAV1D_TRC_BT470M: return PL_COLOR_TRC_GAMMA22;
case DAV1D_TRC_BT470BG: return PL_COLOR_TRC_GAMMA28;
case DAV1D_TRC_BT601: return PL_COLOR_TRC_BT_1886; // EOTF != OETF
case DAV1D_TRC_SMPTE240: return PL_COLOR_TRC_BT_1886; // EOTF != OETF
case DAV1D_TRC_LINEAR: return PL_COLOR_TRC_LINEAR;
case DAV1D_TRC_LOG100: return PL_COLOR_TRC_UNKNOWN; // missing
case DAV1D_TRC_LOG100_SQRT10: return PL_COLOR_TRC_UNKNOWN; // missing
case DAV1D_TRC_IEC61966: return PL_COLOR_TRC_BT_1886; // EOTF != OETF
case DAV1D_TRC_BT1361: return PL_COLOR_TRC_BT_1886; // ETOF != OETF
case DAV1D_TRC_SRGB: return PL_COLOR_TRC_SRGB;
case DAV1D_TRC_BT2020_10BIT: return PL_COLOR_TRC_BT_1886; // EOTF != OETF
case DAV1D_TRC_BT2020_12BIT: return PL_COLOR_TRC_BT_1886; // EOTF != OETF
case DAV1D_TRC_SMPTE2084: return PL_COLOR_TRC_PQ;
case DAV1D_TRC_SMPTE428: return PL_COLOR_TRC_ST428;
case DAV1D_TRC_HLG: return PL_COLOR_TRC_HLG;
case DAV1D_TRC_RESERVED: abort();
}
return PL_COLOR_TRC_UNKNOWN;
}
PL_DAV1D_API enum Dav1dTransferCharacteristics pl_transfer_to_dav1d(enum pl_color_transfer trc)
{
switch (trc) {
case PL_COLOR_TRC_UNKNOWN: return DAV1D_TRC_UNKNOWN;
case PL_COLOR_TRC_BT_1886: return DAV1D_TRC_BT709; // EOTF != OETF
case PL_COLOR_TRC_SRGB: return DAV1D_TRC_SRGB;
case PL_COLOR_TRC_LINEAR: return DAV1D_TRC_LINEAR;
case PL_COLOR_TRC_GAMMA18: return DAV1D_TRC_UNKNOWN; // missing
case PL_COLOR_TRC_GAMMA20: return DAV1D_TRC_UNKNOWN; // missing
case PL_COLOR_TRC_GAMMA22: return DAV1D_TRC_BT470M;
case PL_COLOR_TRC_GAMMA24: return DAV1D_TRC_UNKNOWN; // missing
case PL_COLOR_TRC_GAMMA26: return DAV1D_TRC_UNKNOWN; // missing
case PL_COLOR_TRC_GAMMA28: return DAV1D_TRC_BT470BG;
case PL_COLOR_TRC_ST428: return DAV1D_TRC_SMPTE428;
case PL_COLOR_TRC_PRO_PHOTO: return DAV1D_TRC_UNKNOWN; // missing
case PL_COLOR_TRC_PQ: return DAV1D_TRC_SMPTE2084;
case PL_COLOR_TRC_HLG: return DAV1D_TRC_HLG;
case PL_COLOR_TRC_V_LOG: return DAV1D_TRC_UNKNOWN; // missing
case PL_COLOR_TRC_S_LOG1: return DAV1D_TRC_UNKNOWN; // missing
case PL_COLOR_TRC_S_LOG2: return DAV1D_TRC_UNKNOWN; // missing
case PL_COLOR_TRC_COUNT: abort();
}
return DAV1D_TRC_UNKNOWN;
}
PL_DAV1D_API enum pl_chroma_location pl_chroma_from_dav1d(enum Dav1dChromaSamplePosition loc)
{
switch (loc) {
case DAV1D_CHR_UNKNOWN: return PL_CHROMA_UNKNOWN;
case DAV1D_CHR_VERTICAL: return PL_CHROMA_LEFT;
case DAV1D_CHR_COLOCATED: return PL_CHROMA_TOP_LEFT;
}
return PL_CHROMA_UNKNOWN;
}
PL_DAV1D_API enum Dav1dChromaSamplePosition pl_chroma_to_dav1d(enum pl_chroma_location loc)
{
switch (loc) {
case PL_CHROMA_UNKNOWN: return DAV1D_CHR_UNKNOWN;
case PL_CHROMA_LEFT: return DAV1D_CHR_VERTICAL;
case PL_CHROMA_CENTER: return DAV1D_CHR_UNKNOWN; // missing
case PL_CHROMA_TOP_LEFT: return DAV1D_CHR_COLOCATED;
case PL_CHROMA_TOP_CENTER: return DAV1D_CHR_UNKNOWN; // missing
case PL_CHROMA_BOTTOM_LEFT: return DAV1D_CHR_UNKNOWN; // missing
case PL_CHROMA_BOTTOM_CENTER: return DAV1D_CHR_UNKNOWN; // missing
case PL_CHROMA_COUNT: abort();
}
return DAV1D_CHR_UNKNOWN;
}
static inline float pl_fixed24_8(uint32_t n)
{
return (float) n / (1 << 8);
}
static inline float pl_fixed18_14(uint32_t n)
{
return (float) n / (1 << 14);
}
static inline float pl_fixed0_16(uint16_t n)
{
return (float) n / (1 << 16);
}
// Align to a power of 2
#define PL_ALIGN2(x, align) (((x) + (align) - 1) & ~((align) - 1))
PL_DAV1D_API void pl_frame_from_dav1dpicture(struct pl_frame *out,
const Dav1dPicture *picture)
{
const Dav1dSequenceHeader *seq_hdr = picture->seq_hdr;
int num_planes;
switch (picture->p.layout) {
case DAV1D_PIXEL_LAYOUT_I400:
num_planes = 1;
break;
case DAV1D_PIXEL_LAYOUT_I420:
case DAV1D_PIXEL_LAYOUT_I422:
case DAV1D_PIXEL_LAYOUT_I444:
num_planes = 3;
break;
default: abort();
}
*out = (struct pl_frame) {
.num_planes = num_planes,
.planes = {
// Components are always in order, which makes things easy
{
.components = 1,
.component_mapping = {0},
}, {
.components = 1,
.component_mapping = {1},
}, {
.components = 1,
.component_mapping = {2},
},
},
.crop = {
0, 0, picture->p.w, picture->p.h,
},
.color = {
.primaries = pl_primaries_from_dav1d(seq_hdr->pri),
.transfer = pl_transfer_from_dav1d(seq_hdr->trc),
},
.repr = {
.sys = pl_system_from_dav1d(seq_hdr->mtrx),
.levels = pl_levels_from_dav1d(seq_hdr->color_range),
.bits = {
.sample_depth = PL_ALIGN2(picture->p.bpc, 8),
.color_depth = picture->p.bpc,
},
},
};
if (seq_hdr->mtrx == DAV1D_MC_ICTCP && seq_hdr->trc == DAV1D_TRC_HLG) {
// dav1d makes no distinction between PQ and HLG ICtCp, so we need
// to manually fix it in the case that we have HLG ICtCp data.
out->repr.sys = PL_COLOR_SYSTEM_BT_2100_HLG;
} else if (seq_hdr->mtrx == DAV1D_MC_IDENTITY &&
seq_hdr->pri == DAV1D_COLOR_PRI_XYZ)
{
// dav1d handles this as a special case, but doesn't provide an
// explicit flag for it either, so we have to resort to this ugly hack,
// even though CIE 1931 RGB *is* a valid thing in principle!
out->repr.sys= PL_COLOR_SYSTEM_XYZ;
} else if (!out->repr.sys) {
// PL_COLOR_SYSTEM_UNKNOWN maps to RGB, so hard-code this one
out->repr.sys = pl_color_system_guess_ycbcr(picture->p.w, picture->p.h);
}
const Dav1dContentLightLevel *cll = picture->content_light;
if (cll) {
out->color.hdr.max_cll = cll->max_content_light_level;
out->color.hdr.max_fall = cll->max_frame_average_light_level;
}
// This overrides the CLL values above, if both are present
const Dav1dMasteringDisplay *md = picture->mastering_display;
if (md) {
out->color.hdr.max_luma = pl_fixed24_8(md->max_luminance);
out->color.hdr.min_luma = pl_fixed18_14(md->min_luminance);
out->color.hdr.prim = (struct pl_raw_primaries) {
.red.x = pl_fixed0_16(md->primaries[0][0]),
.red.y = pl_fixed0_16(md->primaries[0][1]),
.green.x = pl_fixed0_16(md->primaries[1][0]),
.green.y = pl_fixed0_16(md->primaries[1][1]),
.blue.x = pl_fixed0_16(md->primaries[2][0]),
.blue.y = pl_fixed0_16(md->primaries[2][1]),
.white.x = pl_fixed0_16(md->white_point[0]),
.white.y = pl_fixed0_16(md->white_point[1]),
};
}
if (picture->frame_hdr->film_grain.present) {
const Dav1dFilmGrainData *fg = &picture->frame_hdr->film_grain.data;
out->film_grain = (struct pl_film_grain_data) {
.type = PL_FILM_GRAIN_AV1,
.seed = fg->seed,
.params.av1 = {
.num_points_y = fg->num_y_points,
.chroma_scaling_from_luma = fg->chroma_scaling_from_luma,
.num_points_uv = { fg->num_uv_points[0], fg->num_uv_points[1] },
.scaling_shift = fg->scaling_shift,
.ar_coeff_lag = fg->ar_coeff_lag,
.ar_coeff_shift = (int) fg->ar_coeff_shift,
.grain_scale_shift = fg->grain_scale_shift,
.uv_mult = { fg->uv_mult[0], fg->uv_mult[1] },
.uv_mult_luma = { fg->uv_luma_mult[0], fg->uv_luma_mult[1] },
.uv_offset = { fg->uv_offset[0], fg->uv_offset[1] },
.overlap = fg->overlap_flag,
},
};
struct pl_av1_grain_data *av1 = &out->film_grain.params.av1;
memcpy(av1->points_y, fg->y_points, sizeof(av1->points_y));
memcpy(av1->points_uv, fg->uv_points, sizeof(av1->points_uv));
memcpy(av1->ar_coeffs_y, fg->ar_coeffs_y, sizeof(av1->ar_coeffs_y));
memcpy(av1->ar_coeffs_uv[0], fg->ar_coeffs_uv[0], sizeof(av1->ar_coeffs_uv[0]));
memcpy(av1->ar_coeffs_uv[1], fg->ar_coeffs_uv[1], sizeof(av1->ar_coeffs_uv[1]));
}
switch (picture->p.layout) {
case DAV1D_PIXEL_LAYOUT_I400:
case DAV1D_PIXEL_LAYOUT_I444:
break;
case DAV1D_PIXEL_LAYOUT_I420:
case DAV1D_PIXEL_LAYOUT_I422:
// Only set the chroma location for definitely subsampled images
pl_frame_set_chroma_location(out, pl_chroma_from_dav1d(seq_hdr->chr));
break;
}
}
PL_DAV1D_API void pl_swapchain_colors_from_dav1dpicture(struct pl_swapchain_colors *out_colors,
const Dav1dPicture *picture)
{
struct pl_frame frame;
pl_frame_from_dav1dpicture(&frame, picture);
*out_colors = (struct pl_swapchain_colors) {
.primaries = frame.color.primaries,
.transfer = frame.color.transfer,
};
const Dav1dContentLightLevel *cll = picture->content_light;
if (cll) {
out_colors->hdr.max_cll = cll->max_content_light_level;
out_colors->hdr.max_fall = cll->max_frame_average_light_level;
}
const Dav1dMasteringDisplay *md = picture->mastering_display;
if (md) {
out_colors->hdr.min_luma = pl_fixed18_14(md->min_luminance);
out_colors->hdr.max_luma = pl_fixed24_8(md->max_luminance);
out_colors->hdr.prim.red.x = pl_fixed0_16(md->primaries[0][0]);
out_colors->hdr.prim.red.y = pl_fixed0_16(md->primaries[0][1]);
out_colors->hdr.prim.green.x = pl_fixed0_16(md->primaries[1][0]);
out_colors->hdr.prim.green.y = pl_fixed0_16(md->primaries[1][1]);
out_colors->hdr.prim.blue.x = pl_fixed0_16(md->primaries[2][0]);
out_colors->hdr.prim.blue.y = pl_fixed0_16(md->primaries[2][1]);
out_colors->hdr.prim.white.x = pl_fixed0_16(md->white_point[0]);
out_colors->hdr.prim.white.y = pl_fixed0_16(md->white_point[1]);
}
}
#define PL_MAGIC0 0x2c2a1269
#define PL_MAGIC1 0xc6d02577
struct pl_dav1dalloc {
uint32_t magic[2];
pl_gpu gpu;
pl_buf buf;
};
struct pl_dav1dref {
Dav1dPicture pic;
uint8_t count;
};
static void pl_dav1dpicture_unref(void *priv)
{
struct pl_dav1dref *ref = priv;
if (--ref->count == 0) {
dav1d_picture_unref(&ref->pic);
free(ref);
}
}
PL_DAV1D_API bool pl_upload_dav1dpicture(pl_gpu gpu,
struct pl_frame *out,
pl_tex tex[3],
const struct pl_dav1d_upload_params *params)
{
Dav1dPicture *pic = params->picture;
pl_frame_from_dav1dpicture(out, pic);
if (!params->film_grain)
out->film_grain.type = PL_FILM_GRAIN_NONE;
const int bytes = (pic->p.bpc + 7) / 8; // rounded up
int sub_x = 0, sub_y = 0;
switch (pic->p.layout) {
case DAV1D_PIXEL_LAYOUT_I400:
case DAV1D_PIXEL_LAYOUT_I444:
break;
case DAV1D_PIXEL_LAYOUT_I420:
sub_x = sub_y = 1;
break;
case DAV1D_PIXEL_LAYOUT_I422:
sub_x = 1;
break;
}
struct pl_plane_data data[3] = {
{
// Y plane
.type = PL_FMT_UNORM,
.width = pic->p.w,
.height = pic->p.h,
.pixel_stride = bytes,
.component_size = {bytes * 8},
.component_map = {0},
}, {
// U plane
.type = PL_FMT_UNORM,
.width = pic->p.w >> sub_x,
.height = pic->p.h >> sub_y,
.pixel_stride = bytes,
.component_size = {bytes * 8},
.component_map = {1},
}, {
// V plane
.type = PL_FMT_UNORM,
.width = pic->p.w >> sub_x,
.height = pic->p.h >> sub_y,
.pixel_stride = bytes,
.component_size = {bytes * 8},
.component_map = {2},
},
};
pl_buf buf = NULL;
struct pl_dav1dalloc *alloc = params->gpu_allocated ? pic->allocator_data : NULL;
struct pl_dav1dref *ref = NULL;
if (alloc && alloc->magic[0] == PL_MAGIC0 && alloc->magic[1] == PL_MAGIC1) {
// Re-use pre-allocated buffers directly
assert(alloc->gpu == gpu);
buf = alloc->buf;
} else if (params->asynchronous && gpu->limits.callbacks) {
ref = malloc(sizeof(*ref));
if (!ref)
return false;
memcpy(&ref->pic, pic, sizeof(Dav1dPicture));
ref->count = out->num_planes;
}
for (int p = 0; p < out->num_planes; p++) {
ptrdiff_t stride = p > 0 ? pic->stride[1] : pic->stride[0];
if (stride < 0) {
data[p].pixels = (uint8_t *) pic->data[p] + stride * (data[p].height - 1);
data[p].row_stride = -stride;
out->planes[p].flipped = true;
} else {
data[p].pixels = pic->data[p];
data[p].row_stride = stride;
}
if (buf) {
data[p].buf = buf;
data[p].buf_offset = (uintptr_t) data[p].pixels - (uintptr_t) buf->data;
data[p].pixels = NULL;
} else if (ref) {
data[p].priv = ref;
data[p].callback = pl_dav1dpicture_unref;
}
if (!pl_upload_plane(gpu, &out->planes[p], &tex[p], &data[p])) {
free(ref);
return false;
}
}
if (params->asynchronous) {
if (ref) {
*pic = (Dav1dPicture) {0};
} else {
dav1d_picture_unref(pic);
}
}
return true;
}
PL_DAV1D_API int pl_allocate_dav1dpicture(Dav1dPicture *p, void *cookie)
{
pl_gpu gpu = cookie;
if (!gpu->limits.max_mapped_size || !gpu->limits.host_cached ||
!gpu->limits.buf_transfer)
{
return DAV1D_ERR(ENOTSUP);
}
// Copied from dav1d_default_picture_alloc
const int hbd = p->p.bpc > 8;
const int aligned_w = PL_ALIGN2(p->p.w, 128);
const int aligned_h = PL_ALIGN2(p->p.h, 128);
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;
// Align strides up to multiples of the GPU performance hints
p->stride[0] = PL_ALIGN2(p->stride[0], gpu->limits.align_tex_xfer_pitch);
p->stride[1] = PL_ALIGN2(p->stride[1], gpu->limits.align_tex_xfer_pitch);
// Aligning offsets to 4 also implicitly aligns to the texel alignment (1 or 2)
size_t off_align = PL_ALIGN2(gpu->limits.align_tex_xfer_offset, 4);
const size_t y_sz = PL_ALIGN2(p->stride[0] * aligned_h, off_align);
const size_t uv_sz = PL_ALIGN2(p->stride[1] * (aligned_h >> ss_ver), off_align);
// The extra DAV1D_PICTURE_ALIGNMENTs are to brute force plane alignment,
// even in the case that the driver gives us insane alignments
const size_t pic_size = y_sz + 2 * uv_sz;
const size_t total_size = pic_size + DAV1D_PICTURE_ALIGNMENT * 4;
// Validate size limitations
if (total_size > gpu->limits.max_mapped_size)
return DAV1D_ERR(ENOMEM);
pl_buf buf = pl_buf_create(gpu, pl_buf_params(
.size = total_size,
.host_mapped = true,
.memory_type = PL_BUF_MEM_HOST,
));
if (!buf)
return DAV1D_ERR(ENOMEM);
struct pl_dav1dalloc *alloc = malloc(sizeof(struct pl_dav1dalloc));
if (!alloc) {
pl_buf_destroy(gpu, &buf);
return DAV1D_ERR(ENOMEM);
}
*alloc = (struct pl_dav1dalloc) {
.magic = { PL_MAGIC0, PL_MAGIC1 },
.gpu = gpu,
.buf = buf,
};
assert(buf->data);
uintptr_t base = (uintptr_t) buf->data, data[3];
data[0] = PL_ALIGN2(base, DAV1D_PICTURE_ALIGNMENT);
data[1] = PL_ALIGN2(data[0] + y_sz, DAV1D_PICTURE_ALIGNMENT);
data[2] = PL_ALIGN2(data[1] + uv_sz, DAV1D_PICTURE_ALIGNMENT);
p->allocator_data = alloc;
p->data[0] = (void *) data[0];
p->data[1] = (void *) data[1];
p->data[2] = (void *) data[2];
return 0;
}
PL_DAV1D_API void pl_release_dav1dpicture(Dav1dPicture *p, void *cookie)
{
struct pl_dav1dalloc *alloc = p->allocator_data;
if (!alloc)
return;
assert(alloc->magic[0] == PL_MAGIC0);
assert(alloc->magic[1] == PL_MAGIC1);
assert(alloc->gpu == cookie);
pl_buf_destroy(alloc->gpu, &alloc->buf);
free(alloc);
p->data[0] = p->data[1] = p->data[2] = p->allocator_data = NULL;
}
#undef PL_ALIGN2
#undef PL_MAGIC0
#undef PL_MAGIC1
#endif // LIBPLACEBO_DAV1D_H_