summaryrefslogtreecommitdiffstats
path: root/src/include/libplacebo/utils/dav1d_internal.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/include/libplacebo/utils/dav1d_internal.h')
-rw-r--r--src/include/libplacebo/utils/dav1d_internal.h613
1 files changed, 613 insertions, 0 deletions
diff --git a/src/include/libplacebo/utils/dav1d_internal.h b/src/include/libplacebo/utils/dav1d_internal.h
new file mode 100644
index 0000000..2e0512a
--- /dev/null
+++ b/src/include/libplacebo/utils/dav1d_internal.h
@@ -0,0 +1,613 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIBPLACEBO_DAV1D_H_
+#error This header should be included as part of <libplacebo/utils/dav1d.h>
+#elif defined(__cplusplus)
+#error This header cannot be included from C++ define PL_DAV1D_IMPLEMENTATION appropriately
+#else
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+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_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_