* [FFmpeg-devel] [PATCH v2 1/4] libavcodec/vc2enc: Split out common functions between software and hardware encoders
@ 2025-03-08 15:33 IndecisiveTurtle
2025-03-08 15:33 ` [FFmpeg-devel] [PATCH v2 2/4] libavcodec/vulkan: Add modifications to common shader for VC2 vulkan encoder IndecisiveTurtle
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: IndecisiveTurtle @ 2025-03-08 15:33 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: IndecisiveTurtle
From: IndecisiveTurtle <geoster3d@gmail.com>
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 499f826635..a96c700745 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -768,7 +768,7 @@ OBJS-$(CONFIG_VC1_CUVID_DECODER) += cuviddec.o
OBJS-$(CONFIG_VC1_MMAL_DECODER) += mmaldec.o
OBJS-$(CONFIG_VC1_QSV_DECODER) += qsvdec.o
OBJS-$(CONFIG_VC1_V4L2M2M_DECODER) += v4l2_m2m_dec.o
-OBJS-$(CONFIG_VC2_ENCODER) += vc2enc.o vc2enc_dwt.o diractab.o
+OBJS-$(CONFIG_VC2_ENCODER) += vc2enc.o vc2enc_dwt.o vc2enc_common.o diractab.o
OBJS-$(CONFIG_VCR1_DECODER) += vcr1.o
OBJS-$(CONFIG_VMDAUDIO_DECODER) += vmdaudio.o
OBJS-$(CONFIG_VMDVIDEO_DECODER) += vmdvideo.o
diff --git a/libavcodec/vc2enc.c b/libavcodec/vc2enc.c
index b82370a753..712d0cf68a 100644
--- a/libavcodec/vc2enc.c
+++ b/libavcodec/vc2enc.c
@@ -29,506 +29,7 @@
#include "put_bits.h"
#include "version.h"
-#include "vc2enc_dwt.h"
-#include "diractab.h"
-
-/* The limited size resolution of each slice forces us to do this */
-#define SSIZE_ROUND(b) (FFALIGN((b), s->size_scaler) + 4 + s->prefix_bytes)
-
-/* Decides the cutoff point in # of slices to distribute the leftover bytes */
-#define SLICE_REDIST_TOTAL 150
-
-typedef struct VC2BaseVideoFormat {
- enum AVPixelFormat pix_fmt;
- AVRational time_base;
- int width, height;
- uint8_t interlaced, level;
- char name[13];
-} VC2BaseVideoFormat;
-
-static const VC2BaseVideoFormat base_video_fmts[] = {
- { 0 }, /* Custom format, here just to make indexing equal to base_vf */
- { AV_PIX_FMT_YUV420P, { 1001, 15000 }, 176, 120, 0, 1, "QSIF525" },
- { AV_PIX_FMT_YUV420P, { 2, 25 }, 176, 144, 0, 1, "QCIF" },
- { AV_PIX_FMT_YUV420P, { 1001, 15000 }, 352, 240, 0, 1, "SIF525" },
- { AV_PIX_FMT_YUV420P, { 2, 25 }, 352, 288, 0, 1, "CIF" },
- { AV_PIX_FMT_YUV420P, { 1001, 15000 }, 704, 480, 0, 1, "4SIF525" },
- { AV_PIX_FMT_YUV420P, { 2, 25 }, 704, 576, 0, 1, "4CIF" },
-
- { AV_PIX_FMT_YUV422P10, { 1001, 30000 }, 720, 480, 1, 2, "SD480I-60" },
- { AV_PIX_FMT_YUV422P10, { 1, 25 }, 720, 576, 1, 2, "SD576I-50" },
-
- { AV_PIX_FMT_YUV422P10, { 1001, 60000 }, 1280, 720, 0, 3, "HD720P-60" },
- { AV_PIX_FMT_YUV422P10, { 1, 50 }, 1280, 720, 0, 3, "HD720P-50" },
- { AV_PIX_FMT_YUV422P10, { 1001, 30000 }, 1920, 1080, 1, 3, "HD1080I-60" },
- { AV_PIX_FMT_YUV422P10, { 1, 25 }, 1920, 1080, 1, 3, "HD1080I-50" },
- { AV_PIX_FMT_YUV422P10, { 1001, 60000 }, 1920, 1080, 0, 3, "HD1080P-60" },
- { AV_PIX_FMT_YUV422P10, { 1, 50 }, 1920, 1080, 0, 3, "HD1080P-50" },
-
- { AV_PIX_FMT_YUV444P12, { 1, 24 }, 2048, 1080, 0, 4, "DC2K" },
- { AV_PIX_FMT_YUV444P12, { 1, 24 }, 4096, 2160, 0, 5, "DC4K" },
-
- { AV_PIX_FMT_YUV422P10, { 1001, 60000 }, 3840, 2160, 0, 6, "UHDTV 4K-60" },
- { AV_PIX_FMT_YUV422P10, { 1, 50 }, 3840, 2160, 0, 6, "UHDTV 4K-50" },
-
- { AV_PIX_FMT_YUV422P10, { 1001, 60000 }, 7680, 4320, 0, 7, "UHDTV 8K-60" },
- { AV_PIX_FMT_YUV422P10, { 1, 50 }, 7680, 4320, 0, 7, "UHDTV 8K-50" },
-
- { AV_PIX_FMT_YUV422P10, { 1001, 24000 }, 1920, 1080, 0, 3, "HD1080P-24" },
- { AV_PIX_FMT_YUV422P10, { 1001, 30000 }, 720, 486, 1, 2, "SD Pro486" },
-};
-static const int base_video_fmts_len = FF_ARRAY_ELEMS(base_video_fmts);
-
-enum VC2_QM {
- VC2_QM_DEF = 0,
- VC2_QM_COL,
- VC2_QM_FLAT,
-
- VC2_QM_NB
-};
-
-typedef struct SubBand {
- dwtcoef *buf;
- ptrdiff_t stride;
- int width;
- int height;
-} SubBand;
-
-typedef struct Plane {
- SubBand band[MAX_DWT_LEVELS][4];
- dwtcoef *coef_buf;
- int width;
- int height;
- int dwt_width;
- int dwt_height;
- ptrdiff_t coef_stride;
-} Plane;
-
-typedef struct SliceArgs {
- const struct VC2EncContext *ctx;
- union {
- int cache[DIRAC_MAX_QUANT_INDEX];
- uint8_t *buf;
- };
- int x;
- int y;
- int quant_idx;
- int bits_ceil;
- int bits_floor;
- int bytes;
-} SliceArgs;
-
-typedef struct TransformArgs {
- const struct VC2EncContext *ctx;
- Plane *plane;
- const void *idata;
- ptrdiff_t istride;
- int field;
- VC2TransformContext t;
-} TransformArgs;
-
-typedef struct VC2EncContext {
- AVClass *av_class;
- PutBitContext pb;
- Plane plane[3];
- AVCodecContext *avctx;
- DiracVersionInfo ver;
-
- SliceArgs *slice_args;
- TransformArgs transform_args[3];
-
- /* For conversion from unsigned pixel values to signed */
- int diff_offset;
- int bpp;
- int bpp_idx;
-
- /* Picture number */
- uint32_t picture_number;
-
- /* Base video format */
- int base_vf;
- int level;
- int profile;
-
- /* Quantization matrix */
- uint8_t quant[MAX_DWT_LEVELS][4];
- int custom_quant_matrix;
-
- /* Division LUT */
- uint32_t qmagic_lut[116][2];
-
- int num_x; /* #slices horizontally */
- int num_y; /* #slices vertically */
- int prefix_bytes;
- int size_scaler;
- int chroma_x_shift;
- int chroma_y_shift;
-
- /* Rate control stuff */
- int frame_max_bytes;
- int slice_max_bytes;
- int slice_min_bytes;
- int q_ceil;
- int q_avg;
-
- /* Options */
- double tolerance;
- int wavelet_idx;
- int wavelet_depth;
- int strict_compliance;
- int slice_height;
- int slice_width;
- int interlaced;
- enum VC2_QM quant_matrix;
-
- /* Parse code state */
- uint32_t next_parse_offset;
- enum DiracParseCodes last_parse_code;
-} VC2EncContext;
-
-static av_always_inline void put_vc2_ue_uint(PutBitContext *pb, uint32_t val)
-{
- int i;
- int bits = 0;
- unsigned topbit = 1, maxval = 1;
- uint64_t pbits = 0;
-
- if (!val++) {
- put_bits(pb, 1, 1);
- return;
- }
-
- while (val > maxval) {
- topbit <<= 1;
- maxval <<= 1;
- maxval |= 1;
- }
-
- bits = ff_log2(topbit);
-
- for (i = 0; i < bits; i++) {
- topbit >>= 1;
- av_assert2(pbits <= UINT64_MAX>>3);
- pbits <<= 2;
- if (val & topbit)
- pbits |= 0x1;
- }
-
- put_bits64(pb, bits*2 + 1, (pbits << 1) | 1);
-}
-
-static av_always_inline int count_vc2_ue_uint(uint32_t val)
-{
- int topbit = 1, maxval = 1;
-
- if (!val++)
- return 1;
-
- while (val > maxval) {
- topbit <<= 1;
- maxval <<= 1;
- maxval |= 1;
- }
-
- return ff_log2(topbit)*2 + 1;
-}
-
-/* VC-2 10.4 - parse_info() */
-static void encode_parse_info(VC2EncContext *s, enum DiracParseCodes pcode)
-{
- uint32_t cur_pos, dist;
-
- align_put_bits(&s->pb);
-
- cur_pos = put_bytes_count(&s->pb, 0);
-
- /* Magic string */
- ff_put_string(&s->pb, "BBCD", 0);
-
- /* Parse code */
- put_bits(&s->pb, 8, pcode);
-
- /* Next parse offset */
- dist = cur_pos - s->next_parse_offset;
- AV_WB32(s->pb.buf + s->next_parse_offset + 5, dist);
- s->next_parse_offset = cur_pos;
- put_bits32(&s->pb, pcode == DIRAC_PCODE_END_SEQ ? 13 : 0);
-
- /* Last parse offset */
- put_bits32(&s->pb, s->last_parse_code == DIRAC_PCODE_END_SEQ ? 13 : dist);
-
- s->last_parse_code = pcode;
-}
-
-/* VC-2 11.1 - parse_parameters()
- * The level dictates what the decoder should expect in terms of resolution
- * and allows it to quickly reject whatever it can't support. Remember,
- * this codec kinda targets cheapo FPGAs without much memory. Unfortunately
- * it also limits us greatly in our choice of formats, hence the flag to disable
- * strict_compliance */
-static void encode_parse_params(VC2EncContext *s)
-{
- put_vc2_ue_uint(&s->pb, s->ver.major); /* VC-2 demands this to be 2 */
- put_vc2_ue_uint(&s->pb, s->ver.minor); /* ^^ and this to be 0 */
- put_vc2_ue_uint(&s->pb, s->profile); /* 3 to signal HQ profile */
- put_vc2_ue_uint(&s->pb, s->level); /* 3 - 1080/720, 6 - 4K */
-}
-
-/* VC-2 11.3 - frame_size() */
-static void encode_frame_size(VC2EncContext *s)
-{
- put_bits(&s->pb, 1, !s->strict_compliance);
- if (!s->strict_compliance) {
- AVCodecContext *avctx = s->avctx;
- put_vc2_ue_uint(&s->pb, avctx->width);
- put_vc2_ue_uint(&s->pb, avctx->height);
- }
-}
-
-/* VC-2 11.3.3 - color_diff_sampling_format() */
-static void encode_sample_fmt(VC2EncContext *s)
-{
- put_bits(&s->pb, 1, !s->strict_compliance);
- if (!s->strict_compliance) {
- int idx;
- if (s->chroma_x_shift == 1 && s->chroma_y_shift == 0)
- idx = 1; /* 422 */
- else if (s->chroma_x_shift == 1 && s->chroma_y_shift == 1)
- idx = 2; /* 420 */
- else
- idx = 0; /* 444 */
- put_vc2_ue_uint(&s->pb, idx);
- }
-}
-
-/* VC-2 11.3.4 - scan_format() */
-static void encode_scan_format(VC2EncContext *s)
-{
- put_bits(&s->pb, 1, !s->strict_compliance);
- if (!s->strict_compliance)
- put_vc2_ue_uint(&s->pb, s->interlaced);
-}
-
-/* VC-2 11.3.5 - frame_rate() */
-static void encode_frame_rate(VC2EncContext *s)
-{
- put_bits(&s->pb, 1, !s->strict_compliance);
- if (!s->strict_compliance) {
- AVCodecContext *avctx = s->avctx;
- put_vc2_ue_uint(&s->pb, 0);
- put_vc2_ue_uint(&s->pb, avctx->time_base.den);
- put_vc2_ue_uint(&s->pb, avctx->time_base.num);
- }
-}
-
-/* VC-2 11.3.6 - aspect_ratio() */
-static void encode_aspect_ratio(VC2EncContext *s)
-{
- put_bits(&s->pb, 1, !s->strict_compliance);
- if (!s->strict_compliance) {
- AVCodecContext *avctx = s->avctx;
- put_vc2_ue_uint(&s->pb, 0);
- put_vc2_ue_uint(&s->pb, avctx->sample_aspect_ratio.num);
- put_vc2_ue_uint(&s->pb, avctx->sample_aspect_ratio.den);
- }
-}
-
-/* VC-2 11.3.7 - clean_area() */
-static void encode_clean_area(VC2EncContext *s)
-{
- put_bits(&s->pb, 1, 0);
-}
-
-/* VC-2 11.3.8 - signal_range() */
-static void encode_signal_range(VC2EncContext *s)
-{
- put_bits(&s->pb, 1, !s->strict_compliance);
- if (!s->strict_compliance)
- put_vc2_ue_uint(&s->pb, s->bpp_idx);
-}
-
-/* VC-2 11.3.9 - color_spec() */
-static void encode_color_spec(VC2EncContext *s)
-{
- AVCodecContext *avctx = s->avctx;
- put_bits(&s->pb, 1, !s->strict_compliance);
- if (!s->strict_compliance) {
- int val;
- put_vc2_ue_uint(&s->pb, 0);
-
- /* primaries */
- put_bits(&s->pb, 1, 1);
- if (avctx->color_primaries == AVCOL_PRI_BT470BG)
- val = 2;
- else if (avctx->color_primaries == AVCOL_PRI_SMPTE170M)
- val = 1;
- else if (avctx->color_primaries == AVCOL_PRI_SMPTE240M)
- val = 1;
- else
- val = 0;
- put_vc2_ue_uint(&s->pb, val);
-
- /* color matrix */
- put_bits(&s->pb, 1, 1);
- if (avctx->colorspace == AVCOL_SPC_RGB)
- val = 3;
- else if (avctx->colorspace == AVCOL_SPC_YCOCG)
- val = 2;
- else if (avctx->colorspace == AVCOL_SPC_BT470BG)
- val = 1;
- else
- val = 0;
- put_vc2_ue_uint(&s->pb, val);
-
- /* transfer function */
- put_bits(&s->pb, 1, 1);
- if (avctx->color_trc == AVCOL_TRC_LINEAR)
- val = 2;
- else if (avctx->color_trc == AVCOL_TRC_BT1361_ECG)
- val = 1;
- else
- val = 0;
- put_vc2_ue_uint(&s->pb, val);
- }
-}
-
-/* VC-2 11.3 - source_parameters() */
-static void encode_source_params(VC2EncContext *s)
-{
- encode_frame_size(s);
- encode_sample_fmt(s);
- encode_scan_format(s);
- encode_frame_rate(s);
- encode_aspect_ratio(s);
- encode_clean_area(s);
- encode_signal_range(s);
- encode_color_spec(s);
-}
-
-/* VC-2 11 - sequence_header() */
-static void encode_seq_header(VC2EncContext *s)
-{
- align_put_bits(&s->pb);
- encode_parse_params(s);
- put_vc2_ue_uint(&s->pb, s->base_vf);
- encode_source_params(s);
- put_vc2_ue_uint(&s->pb, s->interlaced); /* Frames or fields coding */
-}
-
-/* VC-2 12.1 - picture_header() */
-static void encode_picture_header(VC2EncContext *s)
-{
- align_put_bits(&s->pb);
- put_bits32(&s->pb, s->picture_number++);
-}
-
-/* VC-2 12.3.4.1 - slice_parameters() */
-static void encode_slice_params(VC2EncContext *s)
-{
- put_vc2_ue_uint(&s->pb, s->num_x);
- put_vc2_ue_uint(&s->pb, s->num_y);
- put_vc2_ue_uint(&s->pb, s->prefix_bytes);
- put_vc2_ue_uint(&s->pb, s->size_scaler);
-}
-
-/* 1st idx = LL, second - vertical, third - horizontal, fourth - total */
-static const uint8_t vc2_qm_col_tab[][4] = {
- {20, 9, 15, 4},
- { 0, 6, 6, 4},
- { 0, 3, 3, 5},
- { 0, 3, 5, 1},
- { 0, 11, 10, 11}
-};
-
-static const uint8_t vc2_qm_flat_tab[][4] = {
- { 0, 0, 0, 0},
- { 0, 0, 0, 0},
- { 0, 0, 0, 0},
- { 0, 0, 0, 0},
- { 0, 0, 0, 0}
-};
-
-static void init_quant_matrix(VC2EncContext *s)
-{
- int level, orientation;
-
- if (s->wavelet_depth <= 4 && s->quant_matrix == VC2_QM_DEF) {
- s->custom_quant_matrix = 0;
- for (level = 0; level < s->wavelet_depth; level++) {
- s->quant[level][0] = ff_dirac_default_qmat[s->wavelet_idx][level][0];
- s->quant[level][1] = ff_dirac_default_qmat[s->wavelet_idx][level][1];
- s->quant[level][2] = ff_dirac_default_qmat[s->wavelet_idx][level][2];
- s->quant[level][3] = ff_dirac_default_qmat[s->wavelet_idx][level][3];
- }
- return;
- }
-
- s->custom_quant_matrix = 1;
-
- if (s->quant_matrix == VC2_QM_DEF) {
- for (level = 0; level < s->wavelet_depth; level++) {
- for (orientation = 0; orientation < 4; orientation++) {
- if (level <= 3)
- s->quant[level][orientation] = ff_dirac_default_qmat[s->wavelet_idx][level][orientation];
- else
- s->quant[level][orientation] = vc2_qm_col_tab[level][orientation];
- }
- }
- } else if (s->quant_matrix == VC2_QM_COL) {
- for (level = 0; level < s->wavelet_depth; level++) {
- for (orientation = 0; orientation < 4; orientation++) {
- s->quant[level][orientation] = vc2_qm_col_tab[level][orientation];
- }
- }
- } else {
- for (level = 0; level < s->wavelet_depth; level++) {
- for (orientation = 0; orientation < 4; orientation++) {
- s->quant[level][orientation] = vc2_qm_flat_tab[level][orientation];
- }
- }
- }
-}
-
-/* VC-2 12.3.4.2 - quant_matrix() */
-static void encode_quant_matrix(VC2EncContext *s)
-{
- int level;
- put_bits(&s->pb, 1, s->custom_quant_matrix);
- if (s->custom_quant_matrix) {
- put_vc2_ue_uint(&s->pb, s->quant[0][0]);
- for (level = 0; level < s->wavelet_depth; level++) {
- put_vc2_ue_uint(&s->pb, s->quant[level][1]);
- put_vc2_ue_uint(&s->pb, s->quant[level][2]);
- put_vc2_ue_uint(&s->pb, s->quant[level][3]);
- }
- }
-}
-
-/* VC-2 12.3 - transform_parameters() */
-static void encode_transform_params(VC2EncContext *s)
-{
- put_vc2_ue_uint(&s->pb, s->wavelet_idx);
- put_vc2_ue_uint(&s->pb, s->wavelet_depth);
-
- encode_slice_params(s);
- encode_quant_matrix(s);
-}
-
-/* VC-2 12.2 - wavelet_transform() */
-static void encode_wavelet_transform(VC2EncContext *s)
-{
- encode_transform_params(s);
- align_put_bits(&s->pb);
-}
-
-/* VC-2 12 - picture_parse() */
-static void encode_picture_start(VC2EncContext *s)
-{
- align_put_bits(&s->pb);
- encode_picture_header(s);
- align_put_bits(&s->pb);
- encode_wavelet_transform(s);
-}
+#include "vc2enc_common.h"
#define QUANT(c, mul, add, shift) (((mul) * (c) + (add)) >> (shift))
@@ -658,7 +159,7 @@ static int calc_slice_sizes(VC2EncContext *s)
SliceArgs *enc_args = s->slice_args;
SliceArgs *top_loc[SLICE_REDIST_TOTAL] = {NULL};
- init_quant_matrix(s);
+ ff_vc2_init_quant_matrix(s);
for (slice_y = 0; slice_y < s->num_y; slice_y++) {
for (slice_x = 0; slice_x < s->num_x; slice_x++) {
@@ -931,24 +432,24 @@ static int encode_frame(VC2EncContext *s, AVPacket *avpkt, const AVFrame *frame,
}
/* Sequence header */
- encode_parse_info(s, DIRAC_PCODE_SEQ_HEADER);
- encode_seq_header(s);
+ ff_vc2_encode_parse_info(s, DIRAC_PCODE_SEQ_HEADER);
+ ff_vc2_encode_seq_header(s);
/* Encoder version */
if (aux_data) {
- encode_parse_info(s, DIRAC_PCODE_AUX);
+ ff_vc2_encode_parse_info(s, DIRAC_PCODE_AUX);
ff_put_string(&s->pb, aux_data, 1);
}
/* Picture header */
- encode_parse_info(s, DIRAC_PCODE_PICTURE_HQ);
- encode_picture_start(s);
+ ff_vc2_encode_parse_info(s, DIRAC_PCODE_PICTURE_HQ);
+ ff_vc2_encode_picture_start(s);
/* Encode slices */
encode_slices(s);
/* End sequence */
- encode_parse_info(s, DIRAC_PCODE_END_SEQ);
+ ff_vc2_encode_parse_info(s, DIRAC_PCODE_END_SEQ);
return 0;
}
diff --git a/libavcodec/vc2enc_common.c b/libavcodec/vc2enc_common.c
new file mode 100644
index 0000000000..e90c71a130
--- /dev/null
+++ b/libavcodec/vc2enc_common.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 2016 Open Broadcast Systems Ltd.
+ * Author 2016 Rostislav Pehlivanov <atomnuker@gmail.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "vc2enc_common.h"
+
+const VC2BaseVideoFormat base_video_fmts[] = {
+ { 0 }, /* Custom format, here just to make indexing equal to base_vf */
+ { AV_PIX_FMT_YUV420P, { 1001, 15000 }, 176, 120, 0, 1, "QSIF525" },
+ { AV_PIX_FMT_YUV420P, { 2, 25 }, 176, 144, 0, 1, "QCIF" },
+ { AV_PIX_FMT_YUV420P, { 1001, 15000 }, 352, 240, 0, 1, "SIF525" },
+ { AV_PIX_FMT_YUV420P, { 2, 25 }, 352, 288, 0, 1, "CIF" },
+ { AV_PIX_FMT_YUV420P, { 1001, 15000 }, 704, 480, 0, 1, "4SIF525" },
+ { AV_PIX_FMT_YUV420P, { 2, 25 }, 704, 576, 0, 1, "4CIF" },
+
+ { AV_PIX_FMT_YUV422P10, { 1001, 30000 }, 720, 480, 1, 2, "SD480I-60" },
+ { AV_PIX_FMT_YUV422P10, { 1, 25 }, 720, 576, 1, 2, "SD576I-50" },
+
+ { AV_PIX_FMT_YUV422P10, { 1001, 60000 }, 1280, 720, 0, 3, "HD720P-60" },
+ { AV_PIX_FMT_YUV422P10, { 1, 50 }, 1280, 720, 0, 3, "HD720P-50" },
+ { AV_PIX_FMT_YUV422P10, { 1001, 30000 }, 1920, 1080, 1, 3, "HD1080I-60" },
+ { AV_PIX_FMT_YUV422P10, { 1, 25 }, 1920, 1080, 1, 3, "HD1080I-50" },
+ { AV_PIX_FMT_YUV422P10, { 1001, 60000 }, 1920, 1080, 0, 3, "HD1080P-60" },
+ { AV_PIX_FMT_YUV422P10, { 1, 50 }, 1920, 1080, 0, 3, "HD1080P-50" },
+
+ { AV_PIX_FMT_YUV444P12, { 1, 24 }, 2048, 1080, 0, 4, "DC2K" },
+ { AV_PIX_FMT_YUV444P12, { 1, 24 }, 4096, 2160, 0, 5, "DC4K" },
+
+ { AV_PIX_FMT_YUV422P10, { 1001, 60000 }, 3840, 2160, 0, 6, "UHDTV 4K-60" },
+ { AV_PIX_FMT_YUV422P10, { 1, 50 }, 3840, 2160, 0, 6, "UHDTV 4K-50" },
+
+ { AV_PIX_FMT_YUV422P10, { 1001, 60000 }, 7680, 4320, 0, 7, "UHDTV 8K-60" },
+ { AV_PIX_FMT_YUV422P10, { 1, 50 }, 7680, 4320, 0, 7, "UHDTV 8K-50" },
+
+ { AV_PIX_FMT_YUV422P10, { 1001, 24000 }, 1920, 1080, 0, 3, "HD1080P-24" },
+ { AV_PIX_FMT_YUV422P10, { 1001, 30000 }, 720, 486, 1, 2, "SD Pro486" },
+};
+const int base_video_fmts_len = FF_ARRAY_ELEMS(base_video_fmts);
+
+/* VC-2 10.4 - parse_info() */
+void ff_vc2_encode_parse_info(VC2EncContext *s, enum DiracParseCodes pcode)
+{
+ uint32_t cur_pos, dist;
+
+ align_put_bits(&s->pb);
+
+ cur_pos = put_bytes_count(&s->pb, 0);
+
+ /* Magic string */
+ ff_put_string(&s->pb, "BBCD", 0);
+
+ /* Parse code */
+ put_bits(&s->pb, 8, pcode);
+
+ /* Next parse offset */
+ dist = cur_pos - s->next_parse_offset;
+ AV_WB32(s->pb.buf + s->next_parse_offset + 5, dist);
+ s->next_parse_offset = cur_pos;
+ put_bits32(&s->pb, pcode == DIRAC_PCODE_END_SEQ ? 13 : 0);
+
+ cur_pos = put_bytes_count(&s->pb, 0);
+
+ /* Last parse offset */
+ put_bits32(&s->pb, s->last_parse_code == DIRAC_PCODE_END_SEQ ? 13 : dist);
+
+ s->last_parse_code = pcode;
+}
+
+/* VC-2 11.1 - parse_parameters()
+ * The level dictates what the decoder should expect in terms of resolution
+ * and allows it to quickly reject whatever it can't support. Remember,
+ * this codec kinda targets cheapo FPGAs without much memory. Unfortunately
+ * it also limits us greatly in our choice of formats, hence the flag to disable
+ * strict_compliance */
+static void encode_parse_params(VC2EncContext *s)
+{
+ put_vc2_ue_uint(&s->pb, s->ver.major); /* VC-2 demands this to be 2 */
+ put_vc2_ue_uint(&s->pb, s->ver.minor); /* ^^ and this to be 0 */
+ put_vc2_ue_uint(&s->pb, s->profile); /* 3 to signal HQ profile */
+ put_vc2_ue_uint(&s->pb, s->level); /* 3 - 1080/720, 6 - 4K */
+}
+
+/* VC-2 11.3 - frame_size() */
+static void encode_frame_size(VC2EncContext *s)
+{
+ put_bits(&s->pb, 1, !s->strict_compliance);
+ if (!s->strict_compliance) {
+ AVCodecContext *avctx = s->avctx;
+ put_vc2_ue_uint(&s->pb, avctx->width);
+ put_vc2_ue_uint(&s->pb, avctx->height);
+ }
+}
+
+/* VC-2 11.3.3 - color_diff_sampling_format() */
+static void encode_sample_fmt(VC2EncContext *s)
+{
+ put_bits(&s->pb, 1, !s->strict_compliance);
+ if (!s->strict_compliance) {
+ int idx;
+ if (s->chroma_x_shift == 1 && s->chroma_y_shift == 0)
+ idx = 1; /* 422 */
+ else if (s->chroma_x_shift == 1 && s->chroma_y_shift == 1)
+ idx = 2; /* 420 */
+ else
+ idx = 0; /* 444 */
+ put_vc2_ue_uint(&s->pb, idx);
+ }
+}
+
+/* VC-2 11.3.4 - scan_format() */
+static void encode_scan_format(VC2EncContext *s)
+{
+ put_bits(&s->pb, 1, !s->strict_compliance);
+ if (!s->strict_compliance)
+ put_vc2_ue_uint(&s->pb, s->interlaced);
+}
+
+/* VC-2 11.3.5 - frame_rate() */
+static void encode_frame_rate(VC2EncContext *s)
+{
+ put_bits(&s->pb, 1, !s->strict_compliance);
+ if (!s->strict_compliance) {
+ AVCodecContext *avctx = s->avctx;
+ put_vc2_ue_uint(&s->pb, 0);
+ put_vc2_ue_uint(&s->pb, avctx->time_base.den);
+ put_vc2_ue_uint(&s->pb, avctx->time_base.num);
+ }
+}
+
+/* VC-2 11.3.6 - aspect_ratio() */
+static void encode_aspect_ratio(VC2EncContext *s)
+{
+ put_bits(&s->pb, 1, !s->strict_compliance);
+ if (!s->strict_compliance) {
+ AVCodecContext *avctx = s->avctx;
+ put_vc2_ue_uint(&s->pb, 0);
+ put_vc2_ue_uint(&s->pb, avctx->sample_aspect_ratio.num);
+ put_vc2_ue_uint(&s->pb, avctx->sample_aspect_ratio.den);
+ }
+}
+
+/* VC-2 11.3.7 - clean_area() */
+static void encode_clean_area(VC2EncContext *s)
+{
+ put_bits(&s->pb, 1, 0);
+}
+
+/* VC-2 11.3.8 - signal_range() */
+static void encode_signal_range(VC2EncContext *s)
+{
+ put_bits(&s->pb, 1, !s->strict_compliance);
+ if (!s->strict_compliance)
+ put_vc2_ue_uint(&s->pb, s->bpp_idx);
+}
+
+/* VC-2 11.3.9 - color_spec() */
+static void encode_color_spec(VC2EncContext *s)
+{
+ AVCodecContext *avctx = s->avctx;
+ put_bits(&s->pb, 1, !s->strict_compliance);
+ if (!s->strict_compliance) {
+ int val;
+ put_vc2_ue_uint(&s->pb, 0);
+
+ /* primaries */
+ put_bits(&s->pb, 1, 1);
+ if (avctx->color_primaries == AVCOL_PRI_BT470BG)
+ val = 2;
+ else if (avctx->color_primaries == AVCOL_PRI_SMPTE170M)
+ val = 1;
+ else if (avctx->color_primaries == AVCOL_PRI_SMPTE240M)
+ val = 1;
+ else
+ val = 0;
+ put_vc2_ue_uint(&s->pb, val);
+
+ /* color matrix */
+ put_bits(&s->pb, 1, 1);
+ if (avctx->colorspace == AVCOL_SPC_RGB)
+ val = 3;
+ else if (avctx->colorspace == AVCOL_SPC_YCOCG)
+ val = 2;
+ else if (avctx->colorspace == AVCOL_SPC_BT470BG)
+ val = 1;
+ else
+ val = 0;
+ put_vc2_ue_uint(&s->pb, val);
+
+ /* transfer function */
+ put_bits(&s->pb, 1, 1);
+ if (avctx->color_trc == AVCOL_TRC_LINEAR)
+ val = 2;
+ else if (avctx->color_trc == AVCOL_TRC_BT1361_ECG)
+ val = 1;
+ else
+ val = 0;
+ put_vc2_ue_uint(&s->pb, val);
+ }
+}
+
+/* VC-2 11.3 - source_parameters() */
+static void encode_source_params(VC2EncContext *s)
+{
+ encode_frame_size(s);
+ encode_sample_fmt(s);
+ encode_scan_format(s);
+ encode_frame_rate(s);
+ encode_aspect_ratio(s);
+ encode_clean_area(s);
+ encode_signal_range(s);
+ encode_color_spec(s);
+}
+
+/* VC-2 11 - sequence_header() */
+void ff_vc2_encode_seq_header(VC2EncContext *s)
+{
+ align_put_bits(&s->pb);
+ encode_parse_params(s);
+ put_vc2_ue_uint(&s->pb, s->base_vf);
+ encode_source_params(s);
+ put_vc2_ue_uint(&s->pb, s->interlaced); /* Frames or fields coding */
+}
+
+/* VC-2 12.1 - picture_header() */
+static void encode_picture_header(VC2EncContext *s)
+{
+ align_put_bits(&s->pb);
+ put_bits32(&s->pb, s->picture_number++);
+}
+
+/* VC-2 12.3.4.1 - slice_parameters() */
+static void encode_slice_params(VC2EncContext *s)
+{
+ put_vc2_ue_uint(&s->pb, s->num_x);
+ put_vc2_ue_uint(&s->pb, s->num_y);
+ put_vc2_ue_uint(&s->pb, s->prefix_bytes);
+ put_vc2_ue_uint(&s->pb, s->size_scaler);
+}
+
+/* 1st idx = LL, second - vertical, third - horizontal, fourth - total */
+static const uint8_t vc2_qm_col_tab[][4] = {
+ {20, 9, 15, 4},
+ { 0, 6, 6, 4},
+ { 0, 3, 3, 5},
+ { 0, 3, 5, 1},
+ { 0, 11, 10, 11}
+};
+
+static const uint8_t vc2_qm_flat_tab[][4] = {
+ { 0, 0, 0, 0},
+ { 0, 0, 0, 0},
+ { 0, 0, 0, 0},
+ { 0, 0, 0, 0},
+ { 0, 0, 0, 0}
+};
+
+void ff_vc2_init_quant_matrix(VC2EncContext *s)
+{
+ int level, orientation;
+
+ if (s->wavelet_depth <= 4 && s->quant_matrix == VC2_QM_DEF) {
+ s->custom_quant_matrix = 0;
+ for (level = 0; level < s->wavelet_depth; level++) {
+ s->quant[level][0] = ff_dirac_default_qmat[s->wavelet_idx][level][0];
+ s->quant[level][1] = ff_dirac_default_qmat[s->wavelet_idx][level][1];
+ s->quant[level][2] = ff_dirac_default_qmat[s->wavelet_idx][level][2];
+ s->quant[level][3] = ff_dirac_default_qmat[s->wavelet_idx][level][3];
+ }
+ return;
+ }
+
+ s->custom_quant_matrix = 1;
+
+ if (s->quant_matrix == VC2_QM_DEF) {
+ for (level = 0; level < s->wavelet_depth; level++) {
+ for (orientation = 0; orientation < 4; orientation++) {
+ if (level <= 3)
+ s->quant[level][orientation] = ff_dirac_default_qmat[s->wavelet_idx][level][orientation];
+ else
+ s->quant[level][orientation] = vc2_qm_col_tab[level][orientation];
+ }
+ }
+ } else if (s->quant_matrix == VC2_QM_COL) {
+ for (level = 0; level < s->wavelet_depth; level++) {
+ for (orientation = 0; orientation < 4; orientation++) {
+ s->quant[level][orientation] = vc2_qm_col_tab[level][orientation];
+ }
+ }
+ } else {
+ for (level = 0; level < s->wavelet_depth; level++) {
+ for (orientation = 0; orientation < 4; orientation++) {
+ s->quant[level][orientation] = vc2_qm_flat_tab[level][orientation];
+ }
+ }
+ }
+}
+
+/* VC-2 12.3.4.2 - quant_matrix() */
+static void encode_quant_matrix(VC2EncContext *s)
+{
+ int level;
+ put_bits(&s->pb, 1, s->custom_quant_matrix);
+ if (s->custom_quant_matrix) {
+ put_vc2_ue_uint(&s->pb, s->quant[0][0]);
+ for (level = 0; level < s->wavelet_depth; level++) {
+ put_vc2_ue_uint(&s->pb, s->quant[level][1]);
+ put_vc2_ue_uint(&s->pb, s->quant[level][2]);
+ put_vc2_ue_uint(&s->pb, s->quant[level][3]);
+ }
+ }
+}
+
+/* VC-2 12.3 - transform_parameters() */
+static void encode_transform_params(VC2EncContext *s)
+{
+ put_vc2_ue_uint(&s->pb, s->wavelet_idx);
+ put_vc2_ue_uint(&s->pb, s->wavelet_depth);
+
+ encode_slice_params(s);
+ encode_quant_matrix(s);
+}
+
+/* VC-2 12.2 - wavelet_transform() */
+static void encode_wavelet_transform(VC2EncContext *s)
+{
+ encode_transform_params(s);
+ align_put_bits(&s->pb);
+}
+
+/* VC-2 12 - picture_parse() */
+void ff_vc2_encode_picture_start(VC2EncContext *s)
+{
+ align_put_bits(&s->pb);
+ encode_picture_header(s);
+ align_put_bits(&s->pb);
+ encode_wavelet_transform(s);
+}
diff --git a/libavcodec/vc2enc_common.h b/libavcodec/vc2enc_common.h
new file mode 100644
index 0000000000..097d1850e2
--- /dev/null
+++ b/libavcodec/vc2enc_common.h
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2016 Open Broadcast Systems Ltd.
+ * Author 2016 Rostislav Pehlivanov <atomnuker@gmail.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef AVCODEC_VC2ENC_COMMON_H
+#define AVCODEC_VC2ENC_COMMON_H
+
+#include "avcodec.h"
+#include "dirac.h"
+#include "put_bits.h"
+
+#include "vc2enc_dwt.h"
+#include "diractab.h"
+
+/* The limited size resolution of each slice forces us to do this */
+#define SSIZE_ROUND(b) (FFALIGN((b), s->size_scaler) + 4 + s->prefix_bytes)
+
+/* Decides the cutoff point in # of slices to distribute the leftover bytes */
+#define SLICE_REDIST_TOTAL 150
+
+typedef struct VC2BaseVideoFormat {
+ enum AVPixelFormat pix_fmt;
+ AVRational time_base;
+ int width, height;
+ uint8_t interlaced, level;
+ char name[13];
+} VC2BaseVideoFormat;
+
+extern const VC2BaseVideoFormat base_video_fmts[];
+extern const int base_video_fmts_len;
+
+enum VC2_QM {
+ VC2_QM_DEF = 0,
+ VC2_QM_COL,
+ VC2_QM_FLAT,
+
+ VC2_QM_NB
+};
+
+typedef struct SubBand {
+ dwtcoef *buf;
+ ptrdiff_t stride;
+ int width;
+ int height;
+ int shift;
+} SubBand;
+
+typedef struct Plane {
+ SubBand band[MAX_DWT_LEVELS][4];
+ dwtcoef *coef_buf;
+ int width;
+ int height;
+ int dwt_width;
+ int dwt_height;
+ ptrdiff_t coef_stride;
+} Plane;
+
+typedef struct SliceArgs {
+ const struct VC2EncContext *ctx;
+ union {
+ int cache[DIRAC_MAX_QUANT_INDEX];
+ uint8_t *buf;
+ };
+ int x;
+ int y;
+ int quant_idx;
+ int bits_ceil;
+ int bits_floor;
+ int bytes;
+} SliceArgs;
+
+typedef struct TransformArgs {
+ struct VC2EncContext *ctx;
+ Plane *plane;
+ const void *idata;
+ ptrdiff_t istride;
+ int field;
+ VC2TransformContext t;
+} TransformArgs;
+
+typedef struct VC2EncContext {
+ AVClass *av_class;
+ PutBitContext pb;
+ Plane plane[3];
+ AVCodecContext *avctx;
+ DiracVersionInfo ver;
+
+ SliceArgs *slice_args;
+ TransformArgs transform_args[3];
+
+ /* For conversion from unsigned pixel values to signed */
+ int diff_offset;
+ int bpp;
+ int bpp_idx;
+
+ /* Picture number */
+ uint32_t picture_number;
+
+ /* Base video format */
+ int base_vf;
+ int level;
+ int profile;
+
+ /* Quantization matrix */
+ uint8_t quant[MAX_DWT_LEVELS][4];
+ int custom_quant_matrix;
+
+ /* Division LUT */
+ uint32_t qmagic_lut[116][2];
+
+ int num_x; /* #slices horizontally */
+ int num_y; /* #slices vertically */
+ int group_x;
+ int group_y;
+ int prefix_bytes;
+ int size_scaler;
+ int chroma_x_shift;
+ int chroma_y_shift;
+
+ /* Rate control stuff */
+ int frame_max_bytes;
+ int slice_max_bytes;
+ int slice_min_bytes;
+ int q_ceil;
+ int q_avg;
+
+ /* Options */
+ double tolerance;
+ int wavelet_idx;
+ int wavelet_depth;
+ int strict_compliance;
+ int slice_height;
+ int slice_width;
+ int interlaced;
+ enum VC2_QM quant_matrix;
+
+ /* Parse code state */
+ uint32_t next_parse_offset;
+ enum DiracParseCodes last_parse_code;
+} VC2EncContext;
+
+static inline void put_vc2_ue_uint(PutBitContext *pb, uint32_t val)
+{
+ int i;
+ int bits = 0;
+ unsigned topbit = 1, maxval = 1;
+ uint64_t pbits = 0;
+
+ if (!val++) {
+ put_bits(pb, 1, 1);
+ return;
+ }
+
+ while (val > maxval) {
+ topbit <<= 1;
+ maxval <<= 1;
+ maxval |= 1;
+ }
+
+ bits = ff_log2(topbit);
+
+ for (i = 0; i < bits; i++) {
+ topbit >>= 1;
+ av_assert2(pbits <= UINT64_MAX>>3);
+ pbits <<= 2;
+ if (val & topbit)
+ pbits |= 0x1;
+ }
+
+ put_bits64(pb, bits*2 + 1, (pbits << 1) | 1);
+}
+
+static inline int count_vc2_ue_uint(uint32_t val)
+{
+ int topbit = 1, maxval = 1;
+
+ if (!val++)
+ return 1;
+
+ while (val > maxval) {
+ topbit <<= 1;
+ maxval <<= 1;
+ maxval |= 1;
+ }
+
+ return ff_log2(topbit)*2 + 1;
+}
+
+void ff_vc2_init_quant_matrix(VC2EncContext *s);
+
+void ff_vc2_encode_parse_info(VC2EncContext *s, enum DiracParseCodes pcode);
+
+void ff_vc2_encode_seq_header(VC2EncContext *s);
+
+void ff_vc2_encode_picture_start(VC2EncContext *s);
+
+#endif
--
2.48.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 4+ messages in thread
* [FFmpeg-devel] [PATCH v2 2/4] libavcodec/vulkan: Add modifications to common shader for VC2 vulkan encoder
2025-03-08 15:33 [FFmpeg-devel] [PATCH v2 1/4] libavcodec/vc2enc: Split out common functions between software and hardware encoders IndecisiveTurtle
@ 2025-03-08 15:33 ` IndecisiveTurtle
2025-03-08 15:33 ` [FFmpeg-devel] [PATCH v2 3/4] libavcodec/vulkan: Add vulkan vc2 shaders IndecisiveTurtle
2025-03-08 15:33 ` [FFmpeg-devel] [PATCH v2 4/4] avcodec/vc2enc: Initial vulkan VC2 encoder Implements a Vulkan based dirac encoder. Supports Haar and Legall wavelets and should work with all wavelet depths IndecisiveTurtle
2 siblings, 0 replies; 4+ messages in thread
From: IndecisiveTurtle @ 2025-03-08 15:33 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: IndecisiveTurtle
---
libavcodec/vulkan/common.comp | 58 +++++++++++++++++++++++++++--------
1 file changed, 46 insertions(+), 12 deletions(-)
diff --git a/libavcodec/vulkan/common.comp b/libavcodec/vulkan/common.comp
index e4e983b3e2..3dc1527529 100644
--- a/libavcodec/vulkan/common.comp
+++ b/libavcodec/vulkan/common.comp
@@ -18,6 +18,9 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#extension GL_EXT_buffer_reference : require
+#extension GL_EXT_buffer_reference2 : require
+
layout(buffer_reference, buffer_reference_align = 1) buffer u8buf {
uint8_t v;
};
@@ -57,22 +60,20 @@ layout(buffer_reference, buffer_reference_align = 8) buffer u64buf {
#define mid_pred(a, b, c) \
max(min((a), (b)), min(max((a), (b)), (c)))
-/* TODO: optimize */
+
uint align(uint src, uint a)
{
- uint res = src % a;
- if (res == 0)
- return src;
- return src + a - res;
+ return (src + a - 1) & ~(a - 1);
+}
+
+int align(int src, int a)
+{
+ return (src + a - 1) & ~(a - 1);
}
-/* TODO: optimize */
uint64_t align64(uint64_t src, uint64_t a)
{
- uint64_t res = src % a;
- if (res == 0)
- return src;
- return src + a - res;
+ return (src + a - 1) & ~(a - 1);
}
#define reverse4(src) \
@@ -163,6 +164,39 @@ uint32_t flush_put_bits(inout PutBitContext pb)
return uint32_t(pb.buf - pb.buf_start);
}
+void skip_put_bytes(inout PutBitContext pb, int n)
+{
+ int bytes_left = pb.bit_left >> 3;
+ if (n < bytes_left)
+ {
+ int n_bits = n << 3;
+ int mask = (1 << n_bits) - 1;
+ pb.bit_buf <<= n_bits;
+ pb.bit_buf |= mask;
+ pb.bit_left -= uint8_t(n_bits);
+ return;
+ }
+ if (pb.bit_left < BUF_BITS)
+ {
+ int mask = (1 << pb.bit_left) - 1;
+ pb.bit_buf <<= pb.bit_left;
+ pb.bit_buf |= mask;
+ u32vec2buf(pb.buf).v = BUF_REVERSE(pb.bit_buf);
+ pb.buf += BUF_BYTES;
+ n -= pb.bit_left >> 3;
+ }
+ int skip_dwords = n >> 2;
+ while (skip_dwords > 0)
+ {
+ u32buf(pb.buf).v = 0xFFFFFFFF;
+ pb.buf += 4;
+ skip_dwords--;
+ }
+ int skip_bits = (n & 3) << 3;
+ pb.bit_buf = (1 << skip_bits) - 1;
+ pb.bit_left = uint8_t(BUF_BITS - skip_bits);
+}
+
void init_put_bits(out PutBitContext pb, u8buf data, uint64_t len)
{
pb.buf_start = uint64_t(data);
@@ -177,8 +211,8 @@ uint64_t put_bits_count(in PutBitContext pb)
return (pb.buf - pb.buf_start)*8 + BUF_BITS - pb.bit_left;
}
-uint32_t put_bytes_count(in PutBitContext pb)
+int32_t put_bytes_count(in PutBitContext pb)
{
uint64_t num_bytes = (pb.buf - pb.buf_start) + ((BUF_BITS - pb.bit_left) >> 3);
- return uint32_t(num_bytes);
+ return int32_t(num_bytes);
}
--
2.48.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 4+ messages in thread
* [FFmpeg-devel] [PATCH v2 3/4] libavcodec/vulkan: Add vulkan vc2 shaders
2025-03-08 15:33 [FFmpeg-devel] [PATCH v2 1/4] libavcodec/vc2enc: Split out common functions between software and hardware encoders IndecisiveTurtle
2025-03-08 15:33 ` [FFmpeg-devel] [PATCH v2 2/4] libavcodec/vulkan: Add modifications to common shader for VC2 vulkan encoder IndecisiveTurtle
@ 2025-03-08 15:33 ` IndecisiveTurtle
2025-03-08 15:33 ` [FFmpeg-devel] [PATCH v2 4/4] avcodec/vc2enc: Initial vulkan VC2 encoder Implements a Vulkan based dirac encoder. Supports Haar and Legall wavelets and should work with all wavelet depths IndecisiveTurtle
2 siblings, 0 replies; 4+ messages in thread
From: IndecisiveTurtle @ 2025-03-08 15:33 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: IndecisiveTurtle
---
libavcodec/vulkan/vc2_dwt_haar.comp | 66 +++++++
libavcodec/vulkan/vc2_dwt_haar_subgroup.comp | 89 +++++++++
libavcodec/vulkan/vc2_dwt_hor_legall.comp | 66 +++++++
libavcodec/vulkan/vc2_dwt_upload.comp | 29 +++
libavcodec/vulkan/vc2_dwt_ver_legall.comp | 62 +++++++
libavcodec/vulkan/vc2_encode.comp | 173 ++++++++++++++++++
libavcodec/vulkan/vc2_slice_sizes.comp | 183 +++++++++++++++++++
7 files changed, 668 insertions(+)
create mode 100644 libavcodec/vulkan/vc2_dwt_haar.comp
create mode 100644 libavcodec/vulkan/vc2_dwt_haar_subgroup.comp
create mode 100644 libavcodec/vulkan/vc2_dwt_hor_legall.comp
create mode 100644 libavcodec/vulkan/vc2_dwt_upload.comp
create mode 100644 libavcodec/vulkan/vc2_dwt_ver_legall.comp
create mode 100644 libavcodec/vulkan/vc2_encode.comp
create mode 100644 libavcodec/vulkan/vc2_slice_sizes.comp
diff --git a/libavcodec/vulkan/vc2_dwt_haar.comp b/libavcodec/vulkan/vc2_dwt_haar.comp
new file mode 100644
index 0000000000..793053b6c1
--- /dev/null
+++ b/libavcodec/vulkan/vc2_dwt_haar.comp
@@ -0,0 +1,66 @@
+#extension GL_EXT_scalar_block_layout : require
+#extension GL_EXT_buffer_reference : require
+
+#define LOCAL_X 256
+
+struct Plane {
+ ivec2 dim;
+ ivec2 dwt_dim;
+};
+
+layout(push_constant, scalar) uniform ComputeInfo {
+ int s;
+ int plane_idx;
+ int wavelet_depth;
+ Plane planes[3];
+};
+
+shared int local_coef[LOCAL_X];
+
+void main()
+{
+ ivec2 coord = ivec2(gl_GlobalInvocationID.xy);
+ ivec2 dwt_dim = planes[plane_idx].dwt_dim;
+ int value = imageLoad(planes0[plane_idx], coord).x;
+
+ /* Perform Haar wavelet on the 16x16 local workgroup with shared memory */
+ for (int i = 0; i < wavelet_depth; i++)
+ {
+ ivec2 mask = ivec2((1 << i) - 1);
+ if (any(notEqual(coord & mask, ivec2(0))))
+ break;
+
+ /* Offset between valid hor pixels for each level, +1, +2, +4 etc */
+ int dist = (1 << i);
+
+ local_coef[gl_LocalInvocationIndex] = value;
+ barrier();
+
+ /* Horizontal haar wavelet */
+ uint other_id = gl_LocalInvocationIndex ^ dist;
+ int other = local_coef[other_id];
+ int a = gl_LocalInvocationIndex < other_id ? value : other;
+ int b = gl_LocalInvocationIndex < other_id ? other : value;
+ int dst_b = (b - a) * (1 << s);
+ int dst_a = a * (1 << s) + ((dst_b + 1) >> 1);
+ value = gl_LocalInvocationIndex < other_id ? dst_a : dst_b;
+
+ /* Offset between valid ver pixels for each level, +1, +2, +4 etc */
+ dist <<= 4;
+
+ local_coef[gl_LocalInvocationIndex] = value;
+ barrier();
+
+ /* Vertical haar wavelet */
+ other_id = gl_LocalInvocationIndex ^ dist;
+ other = local_coef[other_id];
+ a = gl_LocalInvocationIndex < other_id ? value : other;
+ b = gl_LocalInvocationIndex < other_id ? other : value;
+ dst_b = b - a;
+ dst_a = a + ((dst_b + 1) >> 1);
+ value = gl_LocalInvocationIndex < other_id ? dst_a : dst_b;
+ }
+
+ /* Store value */
+ imageStore(planes0[plane_idx], coord, ivec4(value));
+}
diff --git a/libavcodec/vulkan/vc2_dwt_haar_subgroup.comp b/libavcodec/vulkan/vc2_dwt_haar_subgroup.comp
new file mode 100644
index 0000000000..da0b9f72ca
--- /dev/null
+++ b/libavcodec/vulkan/vc2_dwt_haar_subgroup.comp
@@ -0,0 +1,89 @@
+#extension GL_EXT_scalar_block_layout : require
+#extension GL_KHR_shader_subgroup_basic : require
+#extension GL_KHR_shader_subgroup_shuffle : require
+#extension GL_EXT_buffer_reference : require
+
+#define TILE_DIM 8
+
+layout(scalar, buffer_reference, buffer_reference_align = 4) buffer DwtCoef {
+ int coef_buf[];
+};
+
+struct Plane {
+ ivec2 dim;
+ ivec2 dwt_dim;
+};
+
+layout(push_constant, scalar) uniform ComputeInfo {
+ int s;
+ int plane_idx;
+ int wavelet_depth;
+ Plane planes[3];
+ DwtCoef pbuf[3];
+};
+
+int dwt_haar_subgroup(int value, int i)
+{
+ /* Offset between valid hor pixels for each level, +1, +2, +4 etc */
+ int dist = (1 << i);
+
+ /* Horizontal haar wavelet */
+ uint other_sub_id = gl_SubgroupInvocationID ^ dist;
+ int other = subgroupShuffle(value, other_sub_id);
+ int a = gl_SubgroupInvocationID < other_sub_id ? value : other;
+ int b = gl_SubgroupInvocationID < other_sub_id ? other : value;
+ int dst_b = (b - a) * (1 << s);
+ int dst_a = a * (1 << s) + ((dst_b + 1) >> 1);
+ value = gl_SubgroupInvocationID < other_sub_id ? dst_a : dst_b;
+
+ /* Offset between valid ver pixels for each level, +1, +2, +4 etc */
+ dist <<= 3;
+
+ /* Vertical haar wavelet */
+ other_sub_id = gl_SubgroupInvocationID ^ dist;
+ other = subgroupShuffle(value, other_sub_id);
+ a = gl_SubgroupInvocationID < other_sub_id ? value : other;
+ b = gl_SubgroupInvocationID < other_sub_id ? other : value;
+ dst_b = b - a;
+ dst_a = a + ((dst_b + 1) >> 1);
+ return gl_SubgroupInvocationID < other_sub_id ? dst_a : dst_b;
+}
+
+bool is_thread_active(int i, ivec2 coord)
+{
+ if (i > wavelet_depth - 1)
+ return false;
+ ivec2 mask = ivec2((1 << i) - 1);
+ if (any(notEqual(coord & mask, ivec2(0))))
+ return false;
+ return true;
+}
+
+void main() {
+ ivec2 tile_coord = ivec2(gl_WorkGroupID.xy);
+ ivec2 local_coord = ivec2(gl_LocalInvocationIndex & 7, gl_LocalInvocationIndex >> 3);
+ ivec2 coord = tile_coord * ivec2(TILE_DIM) + local_coord;
+ ivec2 dwt_dim = planes[plane_idx].dwt_dim;
+ if (any(greaterThanEqual(coord, dwt_dim))) {
+ return;
+ }
+ int index = dwt_dim.x * coord.y + coord.x;
+ int value = pbuf[plane_idx].coef_buf[index];
+
+ if (gl_SubgroupSize == 64) {
+ for (int i = 0; i < 3; i++) {
+ if (!is_thread_active(i, local_coord))
+ break;
+ value = dwt_haar_subgroup(value, i);
+ }
+ } else {
+ for (int i = 0; i < 2; i++) {
+ if (!is_thread_active(i, local_coord))
+ break;
+ value = dwt_haar_subgroup(value, i);
+ }
+ }
+
+ // Store value
+ pbuf[plane_idx].coef_buf[index] = value;
+}
diff --git a/libavcodec/vulkan/vc2_dwt_hor_legall.comp b/libavcodec/vulkan/vc2_dwt_hor_legall.comp
new file mode 100644
index 0000000000..3eece4ab48
--- /dev/null
+++ b/libavcodec/vulkan/vc2_dwt_hor_legall.comp
@@ -0,0 +1,66 @@
+#extension GL_EXT_scalar_block_layout : require
+#extension GL_EXT_buffer_reference : require
+
+struct Plane {
+ ivec2 dim;
+ ivec2 dwt_dim;
+};
+
+layout(push_constant, scalar) uniform ComputeInfo {
+ int s;
+ int diff_offset;
+ int level;
+ Plane planes[3];
+};
+
+int image_load(int coord_x)
+{
+ int coord_y = int(gl_GlobalInvocationID.x);
+ return imageLoad(planes0[gl_GlobalInvocationID.z], ivec2(coord_x, coord_y)).x;
+}
+
+void image_store(int coord_x, int value)
+{
+ int coord_y = int(gl_GlobalInvocationID.x);
+ imageStore(planes0[gl_GlobalInvocationID.z], ivec2(coord_x, coord_y), ivec4(value));
+}
+
+void main()
+{
+ int coord_y = int(gl_GlobalInvocationID.x);
+ uint plane_idx = gl_GlobalInvocationID.z;
+ ivec2 work_area = planes[plane_idx].dwt_dim;
+ int dist = 1 << level;
+ if (coord_y >= work_area.y || (coord_y & (dist - 1)) != 0)
+ return;
+
+ // Shift in one bit that is used for additional precision
+ for (int x = 0; x < work_area.x; x += dist)
+ image_store(x, image_load(x) << 1);
+
+ // Lifting stage 2
+ for (int x = 0; x < work_area.x - 2 * dist; x += 2 * dist) {
+ int lhs = image_load(x);
+ int rhs = image_load(x + 2 * dist);
+ int value = image_load(x + dist);
+ value -= (lhs + rhs + 1) >> 1;
+ image_store(x + dist, value);
+ }
+ int lhs = image_load(work_area.x - 2 * dist);
+ int value = image_load(work_area.x - dist);
+ value -= (2 * lhs + 1) >> 1;
+ image_store(work_area.x - dist, value);
+
+ // Lifting stage 1
+ lhs = image_load(dist);
+ value = image_load(0);
+ value += (2 * lhs + 2) >> 2;
+ image_store(0, value);
+ for (int x = 2 * dist; x <= work_area.x - 2 * dist; x += 2 * dist) {
+ int lhs = image_load(x - dist);
+ int rhs = image_load(x + dist);
+ int value = image_load(x);
+ value += (lhs + rhs + 2) >> 2;
+ image_store(x, value);
+ }
+}
diff --git a/libavcodec/vulkan/vc2_dwt_upload.comp b/libavcodec/vulkan/vc2_dwt_upload.comp
new file mode 100644
index 0000000000..6de3721d3b
--- /dev/null
+++ b/libavcodec/vulkan/vc2_dwt_upload.comp
@@ -0,0 +1,29 @@
+#extension GL_EXT_scalar_block_layout : require
+#extension GL_EXT_shader_explicit_arithmetic_types : require
+#extension GL_EXT_buffer_reference : require
+
+layout(scalar, buffer_reference, buffer_reference_align = 1) buffer PlaneBuf {
+ uint8_t data[];
+};
+
+struct Plane {
+ ivec2 dim;
+ ivec2 dwt_dim;
+};
+
+layout(push_constant, scalar) uniform ComputeInfo {
+ int s;
+ int diff_offset;
+ int level;
+ Plane planes[3];
+};
+
+void main()
+{
+ ivec2 coord = ivec2(gl_GlobalInvocationID.xy);
+ uint plane_idx = gl_GlobalInvocationID.z;
+ ivec2 coord_i = clamp(coord, ivec2(0), planes[plane_idx].dim);
+ uint texel = imageLoad(planes1[plane_idx], coord_i).x;
+ int result = int(texel - diff_offset);
+ imageStore(planes0[plane_idx], coord, ivec4(result));
+}
diff --git a/libavcodec/vulkan/vc2_dwt_ver_legall.comp b/libavcodec/vulkan/vc2_dwt_ver_legall.comp
new file mode 100644
index 0000000000..28cfb97a7a
--- /dev/null
+++ b/libavcodec/vulkan/vc2_dwt_ver_legall.comp
@@ -0,0 +1,62 @@
+#extension GL_EXT_scalar_block_layout : require
+#extension GL_EXT_buffer_reference : require
+
+struct Plane {
+ ivec2 dim;
+ ivec2 dwt_dim;
+};
+
+layout(push_constant, scalar) uniform ComputeInfo {
+ int s;
+ int diff_offset;
+ int level;
+ Plane planes[3];
+};
+
+int image_load(int coord_y)
+{
+ int coord_x = int(gl_GlobalInvocationID.x);
+ return imageLoad(planes0[gl_GlobalInvocationID.z], ivec2(coord_x, coord_y)).x;
+}
+
+void image_store(int coord_y, int value)
+{
+ int coord_x = int(gl_GlobalInvocationID.x);
+ imageStore(planes0[gl_GlobalInvocationID.z], ivec2(coord_x, coord_y), ivec4(value));
+}
+
+void main()
+{
+ int coord_x = int(gl_GlobalInvocationID.x);
+ uint plane_idx = gl_GlobalInvocationID.z;
+ ivec2 work_area = planes[plane_idx].dwt_dim;
+ int dist = 1 << level;
+ if (coord_x >= work_area.x || (coord_x & (dist - 1)) != 0)
+ return;
+
+ // Lifting stage 2
+ for (int y = dist; y < work_area.y - 2 * dist; y += 2 * dist) {
+ int lhs = image_load(y - dist);
+ int rhs = image_load(y + dist);
+ int value = image_load(y);
+ value -= (lhs + rhs + 1) >> 1;
+ image_store(y, value);
+ }
+ int lhs = image_load(work_area.y - 2 * dist);
+ int value = image_load(work_area.y - dist);
+ value -= (2 * lhs + 1) >> 1;
+ image_store(work_area.y - dist, value);
+
+ // Lifting stage 1
+ lhs = image_load(dist);
+ value = image_load(0);
+ value += (2 * lhs + 2) >> 2;
+ image_store(0, value);
+ for (int y = 2 * dist; y <= work_area.y - 2 * dist; y += 2 * dist) {
+ int lhs = image_load(y + dist);
+ int rhs = image_load(y - dist);
+ int value = image_load(y);
+ value += (lhs + rhs + 2) >> 2;
+ image_store(y, value);
+ }
+}
diff --git a/libavcodec/vulkan/vc2_encode.comp b/libavcodec/vulkan/vc2_encode.comp
new file mode 100644
index 0000000000..da64d9c6d8
--- /dev/null
+++ b/libavcodec/vulkan/vc2_encode.comp
@@ -0,0 +1,173 @@
+#extension GL_EXT_shader_explicit_arithmetic_types : require
+#extension GL_EXT_scalar_block_layout : require
+#extension GL_EXT_buffer_reference : require
+#extension GL_EXT_debug_printf : require
+
+#define MAX_DWT_LEVELS (5)
+
+struct SliceArgs {
+ int quant_idx;
+ int bytes;
+ int pb_start;
+ int pad;
+};
+
+struct Plane {
+ ivec2 dim;
+ ivec2 dwt_dim;
+};
+
+layout(std430, buffer_reference, buffer_reference_align = 16) buffer SliceArgBuf {
+ SliceArgs args[];
+};
+layout(scalar, buffer_reference, buffer_reference_align = 1) buffer BitBuf {
+ uint data[];
+};
+layout(scalar, buffer_reference, buffer_reference_align = 4) buffer QuantLuts {
+ int quant[5][4];
+ int ff_dirac_qscale_tab[116];
+};
+
+layout(push_constant, scalar) uniform ComputeInfo {
+ BitBuf bytestream;
+ QuantLuts luts;
+ SliceArgBuf slice;
+ ivec2 num_slices;
+ Plane planes[3];
+ int wavelet_depth;
+ int size_scaler;
+ int prefix_bytes;
+};
+
+void put_vc2_ue_uint(inout PutBitContext pb, uint val)
+{
+ int pbits = 0, topbit = 1, maxval = 1, bits = 0;
+ if (val == 0)
+ {
+ put_bits(pb, 1, 1);
+ return;
+ }
+ val++;
+
+ while (val > maxval)
+ {
+ topbit <<= 1;
+ bits++;
+ maxval <<= 1;
+ maxval |= 1;
+ }
+
+ for (int i = 0; i < bits; i++)
+ {
+ topbit >>= 1;
+ pbits <<= 2;
+ if ((val & topbit) != 0)
+ pbits |= 1;
+ }
+
+ put_bits(pb, bits * 2 + 1, (pbits << 1) | 1);
+}
+
+int quants[MAX_DWT_LEVELS][4];
+
+int subband_coord(int index, int h, int lvl)
+{
+ int coord = index;
+ coord <<= 1;
+ coord |= h;
+ coord <<= (wavelet_depth-lvl-1);
+ return coord;
+}
+
+void main()
+{
+ int slice_index = int(gl_GlobalInvocationID.x);
+ int max_index = num_slices.x * num_slices.y;
+ if (slice_index >= max_index)
+ return;
+
+ /* Step 2. Quantize and encode */
+ int pb_start = slice.args[slice_index].pb_start;
+ int workgroup_x = int(gl_WorkGroupSize.x);
+ for (int i = 0, index = workgroup_x - 1; i < gl_WorkGroupID.x; i++) {
+ pb_start += slice.args[index].pb_start + slice.args[index].bytes;
+ index += workgroup_x;
+ }
+ ivec2 slice_coord = ivec2(slice_index % num_slices.x, slice_index / num_slices.x);
+ int slice_bytes_max = slice.args[slice_index].bytes;
+ int quant_index = slice.args[slice_index].quant_idx;
+
+ PutBitContext pb;
+ init_put_bits(pb, OFFBUF(u8buf, bytestream, pb_start), slice_bytes_max);
+
+ for (int level = 0; level < wavelet_depth; level++)
+ for (int orientation = int(level > 0); orientation < 4; orientation++)
+ quants[level][orientation] = max(quant_index - luts.quant[level][orientation], 0);
+
+ /* Write quant index for this slice */
+ put_bits(pb, 8, quant_index);
+
+ /* Luma + 2 Chroma planes */
+ for (int p = 0; p < 3; p++)
+ {
+ int pad_s, pad_c;
+ int bytes_start = put_bytes_count(pb);
+
+ /* Save current location and write a zero value */
+ uint64_t write_ptr_start = pb.buf;
+ int bit_left_start = pb.bit_left;
+ put_bits(pb, 8, 0);
+
+ int stride = align(planes[p].dwt_dim.x, 32);
+ for (int level = 0; level < wavelet_depth; level++)
+ {
+ ivec2 band_size = planes[p].dwt_dim >> (wavelet_depth - level);
+ for (int o = int(level > 0); o < 4; o++)
+ {
+ /* Encode subband */
+ int left = band_size.x * (slice_coord.x) / num_slices.x;
+ int right = band_size.x * (slice_coord.x+1) / num_slices.x;
+ int top = band_size.y * (slice_coord.y) / num_slices.y;
+ int bottom = band_size.y * (slice_coord.y+1) / num_slices.y;
+
+ const int q_idx = quants[level][o];
+ const int qfactor = luts.ff_dirac_qscale_tab[q_idx];
+
+ const int yh = o >> 1;
+ const int xh = o & 1;
+
+ for (int y = top; y < bottom; y++)
+ {
+ for (int x = left; x < right; x++)
+ {
+ int sx = subband_coord(x, xh, level);
+ int sy = subband_coord(y, yh, level);
+ int coef = imageLoad(planes0[p], ivec2(sx, sy)).x;
+ uint c_abs = uint(abs(coef));
+ c_abs = (c_abs << 2) / qfactor;
+ put_vc2_ue_uint(pb, c_abs);
+ if (c_abs != 0)
+ put_bits(pb, 1, int(coef < 0));
+ }
+ }
+ }
+ }
+ flush_put_bits(pb);
+ int bytes_len = put_bytes_count(pb) - bytes_start - 1;
+ if (p == 2)
+ {
+ int len_diff = slice_bytes_max - put_bytes_count(pb);
+ pad_s = align((bytes_len + len_diff), size_scaler)/size_scaler;
+ pad_c = (pad_s*size_scaler) - bytes_len;
+ }
+ else
+ {
+ pad_s = align(bytes_len, size_scaler)/size_scaler;
+ pad_c = (pad_s*size_scaler) - bytes_len;
+ }
+ uint64_t start_ptr = write_ptr_start + ((BUF_BITS - bit_left_start) >> 3);
+ u8buf(start_ptr).v = uint8_t(pad_s);
+ /* vc2-reference uses that padding that decodes to '0' coeffs */
+ skip_put_bytes(pb, pad_c);
+ }
+}
diff --git a/libavcodec/vulkan/vc2_slice_sizes.comp b/libavcodec/vulkan/vc2_slice_sizes.comp
new file mode 100644
index 0000000000..9c048f3664
--- /dev/null
+++ b/libavcodec/vulkan/vc2_slice_sizes.comp
@@ -0,0 +1,183 @@
+#extension GL_EXT_shader_explicit_arithmetic_types : require
+#extension GL_EXT_scalar_block_layout : require
+#extension GL_EXT_buffer_reference : require
+
+#define DIRAC_MAX_QUANT_INDEX 116
+#define MAX_DWT_LEVELS 5
+
+struct SliceArgs {
+ int quant_idx;
+ int bytes;
+ int pb_start;
+ int pad;
+};
+
+struct Plane {
+ ivec2 dim;
+ ivec2 dwt_dim;
+};
+
+layout(std430, buffer_reference) buffer SliceArgBuf {
+ SliceArgs args[];
+};
+layout(scalar, buffer_reference, buffer_reference_align = 4) buffer QuantLuts {
+ int quant[5][4];
+ int ff_dirac_qscale_tab[116];
+};
+
+layout(push_constant, scalar) uniform ComputeInfo {
+ QuantLuts luts;
+ SliceArgBuf slice;
+ ivec2 num_slices;
+ Plane planes[3];
+ int wavelet_depth;
+ int size_scaler;
+ int prefix_bytes;
+ int bits_ceil;
+ int bits_floor;
+};
+
+int count_vc2_ue_uint(uint val)
+{
+ uint topbit = 1, maxval = 1;
+ int bits = 0;
+ if (val == 0)
+ return 1;
+ val++;
+ while (val > maxval)
+ {
+ bits++;
+ topbit <<= 1;
+ maxval <<= 1;
+ maxval |= 1;
+ }
+ return bits * 2 + 1;
+}
+
+int cache[DIRAC_MAX_QUANT_INDEX];
+int quants[MAX_DWT_LEVELS][4];
+shared int slice_sizes[gl_WorkGroupSize.x];
+
+int subband_coord(int index, int h, int lvl)
+{
+ int coord = index;
+ coord <<= 1;
+ coord |= h;
+ coord <<= (wavelet_depth-lvl-1);
+ return coord;
+}
+
+int count_hq_slice(int quant_index)
+{
+ int bits = 0;
+ if (cache[quant_index] != 0)
+ return cache[quant_index];
+
+ bits += 8*prefix_bytes;
+ bits += 8; /* quant_idx */
+
+ for (int level = 0; level < wavelet_depth; level++)
+ for (int orientation = int(level > 0); orientation < 4; orientation++)
+ quants[level][orientation] = max(quant_index - luts.quant[level][orientation], 0);
+
+ int slice_index = int(gl_GlobalInvocationID.x);
+ ivec2 slice_coord = ivec2(slice_index % num_slices.x, slice_index / num_slices.x);
+ for (int p = 0; p < 3; p++)
+ {
+ int bytes_start = bits >> 3;
+ bits += 8;
+
+ const int stride = align(planes[p].dwt_dim.x, 32);
+ for (int level = 0; level < wavelet_depth; level++)
+ {
+ ivec2 band_dim = planes[p].dwt_dim >> (wavelet_depth - level);
+ for (int o = int(level > 0); o < 4; o++)
+ {
+ const int left = band_dim.x * slice_coord.x / num_slices.x;
+ const int right = band_dim.x * (slice_coord.x+1) / num_slices.x;
+ const int top = band_dim.y * slice_coord.y / num_slices.y;
+ const int bottom = band_dim.y * (slice_coord.y+1) / num_slices.y;
+
+ const int q_idx = quants[level][o];
+ const int qfactor = luts.ff_dirac_qscale_tab[q_idx];
+
+ const int yh = o >> 1;
+ const int xh = o & 1;
+
+ for (int y = top; y < bottom; y++)
+ {
+ for (int x = left; x < right; x++)
+ {
+ int sx = subband_coord(x, xh, level);
+ int sy = subband_coord(y, yh, level);
+ int coef = imageLoad(planes0[p], ivec2(sx, sy)).x;
+ uint c_abs = uint(abs(coef));
+ c_abs = (c_abs << 2) / qfactor;
+ bits += count_vc2_ue_uint(c_abs);
+ bits += int(c_abs > 0);
+ }
+ }
+ }
+ }
+ bits += align(bits, 8) - bits;
+ int bytes_len = (bits >> 3) - bytes_start - 1;
+ int pad_s = align(bytes_len, size_scaler) / size_scaler;
+ int pad_c = (pad_s * size_scaler) - bytes_len;
+ bits += pad_c * 8;
+ }
+
+ cache[quant_index] = bits;
+ return bits;
+}
+
+int ssize_round(int b)
+{
+ return align(b, size_scaler) + 4 + prefix_bytes;
+}
+
+void main()
+{
+ int slice_index = int(gl_GlobalInvocationID.x);
+ int max_index = num_slices.x * num_slices.y;
+ if (slice_index >= max_index)
+ return;
+
+ for (int i = 0; i < DIRAC_MAX_QUANT_INDEX; i++)
+ cache[i] = 0;
+
+ const int q_ceil = DIRAC_MAX_QUANT_INDEX;
+ const int top = bits_ceil;
+ const int bottom = bits_floor;
+ int quant_buf[2] = int[2](-1, -1);
+ int quant = slice.args[slice_index].quant_idx;
+ int step = 1;
+ int bits_last = 0;
+ int bits = count_hq_slice(quant);
+ while ((bits > top) || (bits < bottom))
+ {
+ const int signed_step = bits > top ? +step : -step;
+ quant = clamp(quant + signed_step, 0, q_ceil-1);
+ bits = count_hq_slice(quant);
+ if (quant_buf[1] == quant)
+ {
+ quant = max(quant_buf[0], quant);
+ bits = quant == quant_buf[0] ? bits_last : bits;
+ break;
+ }
+ step = clamp(step / 2, 1, (q_ceil - 1) / 2);
+ quant_buf[1] = quant_buf[0];
+ quant_buf[0] = quant;
+ bits_last = bits;
+ }
+ int bytes = ssize_round(bits >> 3);
+ slice.args[slice_index].quant_idx = clamp(quant, 0, q_ceil-1);
+ slice.args[slice_index].bytes = bytes;
+ slice_sizes[gl_LocalInvocationIndex] = bytes;
+ barrier();
+
+ /* Prefix sum for all slices in current workgroup */
+ int total_bytes = 0;
+ for (int i = 0; i < gl_LocalInvocationIndex; i++)
+ total_bytes += slice_sizes[i];
+ slice.args[slice_index].pb_start = total_bytes;
+}
--
2.48.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 4+ messages in thread
* [FFmpeg-devel] [PATCH v2 4/4] avcodec/vc2enc: Initial vulkan VC2 encoder Implements a Vulkan based dirac encoder. Supports Haar and Legall wavelets and should work with all wavelet depths.
2025-03-08 15:33 [FFmpeg-devel] [PATCH v2 1/4] libavcodec/vc2enc: Split out common functions between software and hardware encoders IndecisiveTurtle
2025-03-08 15:33 ` [FFmpeg-devel] [PATCH v2 2/4] libavcodec/vulkan: Add modifications to common shader for VC2 vulkan encoder IndecisiveTurtle
2025-03-08 15:33 ` [FFmpeg-devel] [PATCH v2 3/4] libavcodec/vulkan: Add vulkan vc2 shaders IndecisiveTurtle
@ 2025-03-08 15:33 ` IndecisiveTurtle
2 siblings, 0 replies; 4+ messages in thread
From: IndecisiveTurtle @ 2025-03-08 15:33 UTC (permalink / raw)
To: ffmpeg-devel; +Cc: IndecisiveTurtle
Performance wise, encoding a 1080p 1-minute video is performed in about 2.5 minutes with the cpu encoder running on my Ryzen 5 4600H, while it takes about 30 seconds on my NVIDIA GTX 1650
Haar shader has a subgroup optimized variant that applies when configured wavelet depth allows it
lavapipe seems to be bugged for some reason, after a bunch of debugging I'm not quite sure if it's a bug here or in lavapipe. But people probably dont want to use this with a software implementation anyway.
---
configure | 1 +
libavcodec/Makefile | 3 +
libavcodec/allcodecs.c | 1 +
libavcodec/vc2enc_vulkan.c | 953 +++++++++++++++++++++++++++++++++++++
4 files changed, 958 insertions(+)
create mode 100644 libavcodec/vc2enc_vulkan.c
diff --git a/configure b/configure
index 2c08901adc..f9a44c0ba6 100755
--- a/configure
+++ b/configure
@@ -3122,6 +3122,7 @@ utvideo_encoder_select="bswapdsp huffman llvidencdsp"
vble_decoder_select="llviddsp"
vbn_decoder_select="texturedsp"
vbn_encoder_select="texturedspenc"
+vc2_vulkan_encoder_select="vulkan spirv_compiler"
vmix_decoder_select="idctdsp"
vc1_decoder_select="blockdsp h264qpel intrax8 mpegvideodec qpeldsp vc1dsp"
vc1image_decoder_select="vc1_decoder"
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index a96c700745..7a61b42376 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -769,6 +769,9 @@ OBJS-$(CONFIG_VC1_MMAL_DECODER) += mmaldec.o
OBJS-$(CONFIG_VC1_QSV_DECODER) += qsvdec.o
OBJS-$(CONFIG_VC1_V4L2M2M_DECODER) += v4l2_m2m_dec.o
OBJS-$(CONFIG_VC2_ENCODER) += vc2enc.o vc2enc_dwt.o vc2enc_common.o diractab.o
+OBJS-$(CONFIG_VC2_VULKAN_ENCODER) += vc2enc_vulkan.o vulkan/vc2_encode.o vulkan/vc2_slice_sizes.o \
+ vulkan/vc2_dwt_hor_legall.o vulkan/vc2_dwt_ver_legall.o \
+ vulkan/vc2_dwt_upload.o vulkan/vc2_dwt_haar.o vulkan/vc2_dwt_haar_subgroup.o
OBJS-$(CONFIG_VCR1_DECODER) += vcr1.o
OBJS-$(CONFIG_VMDAUDIO_DECODER) += vmdaudio.o
OBJS-$(CONFIG_VMDVIDEO_DECODER) += vmdvideo.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index 3be33f5cc4..b77a7d44c9 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -365,6 +365,7 @@ extern const FFCodec ff_vc1image_decoder;
extern const FFCodec ff_vc1_mmal_decoder;
extern const FFCodec ff_vc1_qsv_decoder;
extern const FFCodec ff_vc1_v4l2m2m_decoder;
+extern const FFCodec ff_vc2_vulkan_encoder;
extern const FFCodec ff_vc2_encoder;
extern const FFCodec ff_vcr1_decoder;
extern const FFCodec ff_vmdvideo_decoder;
diff --git a/libavcodec/vc2enc_vulkan.c b/libavcodec/vc2enc_vulkan.c
new file mode 100644
index 0000000000..87a7da99de
--- /dev/null
+++ b/libavcodec/vc2enc_vulkan.c
@@ -0,0 +1,953 @@
+/*
+ * Copyright (C) 2016 Open Broadcast Systems Ltd.
+ * Author 2016 Rostislav Pehlivanov <atomnuker@gmail.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/avassert.h"
+#include "libavutil/mem.h"
+#include "libavutil/pixdesc.h"
+#include "libavutil/opt.h"
+#include "libavutil/version.h"
+#include "libavutil/vulkan_spirv.h"
+#include "libavutil/hwcontext_vulkan.h"
+#include "libavutil/vulkan_loader.h"
+#include "libavutil/vulkan.h"
+#include "codec_internal.h"
+#include "internal.h"
+#include "encode.h"
+#include "version.h"
+#include "vc2enc_common.h"
+#include "hwconfig.h"
+
+#define LEGALL_TILE_DIM 16
+#define LEGALL_WORKGROUP_X 64
+#define SLICE_WORKGROUP_X 128
+
+extern const char *ff_source_common_comp;
+extern const char *ff_source_vc2_encode_comp;
+extern const char *ff_source_vc2_dwt_hor_legall_comp;
+extern const char *ff_source_vc2_dwt_ver_legall_comp;
+extern const char *ff_source_vc2_slice_sizes_comp;
+extern const char *ff_source_vc2_dwt_upload_comp;
+extern const char *ff_source_vc2_dwt_haar_comp;
+extern const char *ff_source_vc2_dwt_haar_subgroup_comp;
+
+typedef struct VC2DwtPlane {
+ int width;
+ int height;
+ int dwt_width;
+ int dwt_height;
+} VC2DwtPlane;
+
+typedef struct VC2DwtPushData {
+ int s;
+ union {
+ int diff_offset;
+ int plane_idx;
+ };
+ int level;
+ VC2DwtPlane planes[3];
+} VC2DwtPushData;
+
+typedef struct VC2EncAuxData {
+ uint32_t quant[MAX_DWT_LEVELS][4];
+ int ff_dirac_qscale_tab[116];
+} VC2EncAuxData;
+
+typedef struct VC2EncPushData {
+ VkDeviceAddress pb;
+ VkDeviceAddress luts;
+ VkDeviceAddress slice;
+ int num_x;
+ int num_y;
+ VC2DwtPlane planes[3];
+ int wavelet_depth;
+ int size_scaler;
+ int prefix_bytes;
+} VC2EncPushData;
+
+typedef struct VC2EncSliceArgs {
+ int quant_idx;
+ int bytes;
+ int pb_start;
+ int pad;
+} VC2EncSliceArgs;
+
+typedef struct VC2EncSliceCalcPushData {
+ VkDeviceAddress luts;
+ VkDeviceAddress slice;
+ int num_x;
+ int num_y;
+ VC2DwtPlane planes[3];
+ int wavelet_depth;
+ int size_scaler;
+ int prefix_bytes;
+ int bits_ceil;
+ int bits_floor;
+} VC2EncSliceCalcPushData;
+
+typedef struct VC2EncVulkanContext {
+ VC2EncContext base;
+ VC2EncSliceArgs* vk_slice_args;
+
+ /* Vulkan state */
+ FFVulkanContext vkctx;
+ AVVulkanDeviceQueueFamily *qf;
+ FFVkExecPool e;
+
+ FFVulkanShader dwt_haar_shd;
+ FFVulkanShader dwt_upload_shd;
+ FFVulkanShader dwt_hor_shd, dwt_ver_shd;
+ FFVulkanShader slice_shd;
+ FFVulkanShader enc_shd;
+ AVBufferPool* dwt_buf_pool;
+ int haar_subgroup;
+
+ VkBuffer plane_buf, slice_buf;
+ VC2EncPushData enc_consts;
+ VC2DwtPushData dwt_consts;
+ VC2EncSliceCalcPushData calc_consts;
+
+ /* Intermediate frame pool */
+ AVBufferRef *intermediate_frames_ref[3];
+ AVFrame *intermediate_frame[AV_NUM_DATA_POINTERS];
+ VkImageView intermediate_views[AV_NUM_DATA_POINTERS];
+} VC2EncVulkanContext;
+
+static int init_vulkan_pipeline(VC2EncVulkanContext* s, FFVkSPIRVCompiler *spv,
+ FFVulkanShader* shd, int push_size,
+ int lg_x, int lg_y, int lg_z,
+ const char* pl_name, const char* pl_source,
+ int num_desc)
+{
+ int err = 0;
+ uint8_t *spv_data;
+ size_t spv_len;
+ void *spv_opaque = NULL;
+ FFVulkanContext *vkctx = &s->vkctx;
+ FFVulkanDescriptorSetBinding *desc;
+
+ ff_vk_shader_init(vkctx, shd, pl_name, VK_SHADER_STAGE_COMPUTE_BIT,
+ NULL, 0, lg_x, lg_y, lg_z, 0);
+
+ desc = (FFVulkanDescriptorSetBinding []) {
+ {
+ .name = "planes0",
+ .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+ .mem_layout = "r32i",
+ .dimensions = 2,
+ .elems = 3,
+ .stages = VK_SHADER_STAGE_COMPUTE_BIT,
+ },
+ {
+ .name = "planes1",
+ .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
+ .mem_layout = ff_vk_shader_rep_fmt(vkctx->frames->sw_format, FF_VK_REP_INT),
+ .dimensions = 2,
+ .elems = 3,
+ .stages = VK_SHADER_STAGE_COMPUTE_BIT,
+ },
+ };
+ RET(ff_vk_shader_add_descriptor_set(vkctx, shd, desc, num_desc, 0, 0));
+
+ ff_vk_shader_add_push_const(shd, 0, push_size, VK_SHADER_STAGE_COMPUTE_BIT);
+ av_bprintf(&shd->src, "#define PB_UNALIGNED\n" );
+ GLSLD(ff_source_common_comp);
+ GLSLD(pl_source);
+
+ /* Compile Haar shader */
+ RET(spv->compile_shader(vkctx, spv, shd, &spv_data, &spv_len, "main", &spv_opaque));
+ RET(ff_vk_shader_link(vkctx, shd, spv_data, spv_len, "main"));
+ RET(ff_vk_shader_register_exec(vkctx, &s->e, shd));
+
+fail:
+ return err;
+}
+
+static int init_frame_pools(AVCodecContext *avctx)
+{
+ int i, err = 0;
+ VC2EncVulkanContext *vk_s = avctx->priv_data;
+ AVHWFramesContext *frames_ctx;
+ AVVulkanFramesContext *vk_frames;
+ enum AVPixelFormat sw_format = AV_PIX_FMT_GRAY32;
+
+ for (i = 0; i < 3; i++) {
+ vk_s->intermediate_frames_ref[i] = av_hwframe_ctx_alloc(vk_s->vkctx.device_ref);
+ if (!vk_s->intermediate_frames_ref[i])
+ return AVERROR(ENOMEM);
+
+ frames_ctx = (AVHWFramesContext *)vk_s->intermediate_frames_ref[i]->data;
+ frames_ctx->format = AV_PIX_FMT_VULKAN;
+ frames_ctx->sw_format = sw_format;
+ frames_ctx->width = vk_s->base.plane[i].dwt_width;
+ frames_ctx->height = vk_s->base.plane[i].dwt_height;
+
+ vk_frames = frames_ctx->hwctx;
+ vk_frames->tiling = VK_IMAGE_TILING_OPTIMAL;
+ vk_frames->usage = VK_IMAGE_USAGE_STORAGE_BIT;
+ vk_frames->img_flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
+
+ err = av_hwframe_ctx_init(vk_s->intermediate_frames_ref[i]);
+ if (err < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Unable to initialize frame pool with format %s: %s\n",
+ av_get_pix_fmt_name(sw_format), av_err2str(err));
+ av_buffer_unref(&vk_s->intermediate_frames_ref[i]);
+ return err;
+ }
+ }
+
+ return err;
+}
+
+static int init_vulkan(AVCodecContext *avctx)
+{
+ VC2EncVulkanContext *vk_s = avctx->priv_data;
+ VC2EncContext *s = &vk_s->base;
+ FFVulkanContext *vkctx = &vk_s->vkctx;
+ FFVkSPIRVCompiler *spv;
+ AVBufferRef* dwt_buf = NULL;
+ VC2EncAuxData* ad = NULL;
+ FFVkBuffer* vk_buf = NULL;
+ Plane *p;
+ VC2DwtPlane vk_plane;
+ int i, level, err = 0;
+ unsigned int subgroup_size = vkctx->subgroup_props.maxSubgroupSize;
+
+ /* Initialize spirv compiler */
+ spv = ff_vk_spirv_init();
+ if (!spv) {
+ av_log(avctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n");
+ return -1;
+ }
+
+ ff_vk_exec_pool_init(vkctx, vk_s->qf, &vk_s->e, 1, 0, 0, 0, NULL);
+
+ for (i = 0; i < 3; i++) {
+ p = &s->plane[i];
+ vk_plane.dwt_width = p->dwt_width;
+ vk_plane.dwt_height = p->dwt_height;
+ vk_plane.width = p->width;
+ vk_plane.height = p->height;
+ memcpy(&vk_s->calc_consts.planes[i], &vk_plane, sizeof(vk_plane));
+ memcpy(&vk_s->dwt_consts.planes[i], &vk_plane, sizeof(vk_plane));
+ memcpy(&vk_s->enc_consts.planes[i], &vk_plane, sizeof(vk_plane));
+ }
+
+ /* Initialize Haar push data */
+ vk_s->dwt_consts.diff_offset = s->diff_offset;
+ vk_s->dwt_consts.s = s->wavelet_idx == VC2_TRANSFORM_HAAR_S ? 1 : 0;
+ vk_s->dwt_consts.level = 0;
+
+ /* Initializer slice calculation push data */
+ vk_s->calc_consts.num_x = s->num_x;
+ vk_s->calc_consts.num_y = s->num_y;
+ vk_s->calc_consts.wavelet_depth = s->wavelet_depth;
+ vk_s->calc_consts.prefix_bytes = s->prefix_bytes;
+
+ /* Initialize encoder push data */
+ vk_s->enc_consts.wavelet_depth = s->wavelet_depth;
+ vk_s->enc_consts.num_x = s->num_x;
+ vk_s->enc_consts.num_y = s->num_y;
+
+ /* Create buffer for encoder auxilary data. */
+ RET(ff_vk_get_pooled_buffer(vkctx, &vk_s->dwt_buf_pool, &dwt_buf,
+ VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
+ NULL,
+ sizeof(VC2EncAuxData),
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
+ vk_buf = (FFVkBuffer*)dwt_buf->data;
+ vk_s->calc_consts.luts = vk_buf->address;
+ vk_s->enc_consts.luts = vk_buf->address;
+ ad = (VC2EncAuxData*)vk_buf->mapped_mem;
+ if (s->wavelet_depth <= 4 && s->quant_matrix == VC2_QM_DEF) {
+ s->custom_quant_matrix = 0;
+ for (level = 0; level < s->wavelet_depth; level++) {
+ ad->quant[level][0] = ff_dirac_default_qmat[s->wavelet_idx][level][0];
+ ad->quant[level][1] = ff_dirac_default_qmat[s->wavelet_idx][level][1];
+ ad->quant[level][2] = ff_dirac_default_qmat[s->wavelet_idx][level][2];
+ ad->quant[level][3] = ff_dirac_default_qmat[s->wavelet_idx][level][3];
+ }
+ }
+ memcpy(ad->ff_dirac_qscale_tab, ff_dirac_qscale_tab, sizeof(ff_dirac_qscale_tab));
+
+ /* Create buffer for slice arguments */
+ RET(ff_vk_get_pooled_buffer(vkctx, &vk_s->dwt_buf_pool, &dwt_buf,
+ VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
+ NULL,
+ sizeof(VC2EncSliceArgs) * s->num_x * s->num_y,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT));
+ vk_buf = (FFVkBuffer*)dwt_buf->data;
+ vk_s->slice_buf = vk_buf->buf;
+ vk_s->vk_slice_args = (VC2EncSliceArgs*)vk_buf->mapped_mem;
+ vk_s->calc_consts.slice = vk_buf->address;
+ vk_s->enc_consts.slice = vk_buf->address;
+
+ vk_s->haar_subgroup = 0;
+
+ /* Initialize intermediate frame pool. */
+ RET(init_frame_pools(avctx));
+
+ /* Initialize encoding pipelines */
+ init_vulkan_pipeline(vk_s, spv, &vk_s->dwt_upload_shd, sizeof(VC2DwtPushData),
+ 8, 8, 1, "dwt_upload_pl", ff_source_vc2_dwt_upload_comp, 2);
+ init_vulkan_pipeline(vk_s, spv, &vk_s->slice_shd, sizeof(VC2EncPushData),
+ SLICE_WORKGROUP_X, 1, 1, "slice_pl", ff_source_vc2_slice_sizes_comp, 1);
+ init_vulkan_pipeline(vk_s, spv, &vk_s->enc_shd, sizeof(VC2EncPushData),
+ SLICE_WORKGROUP_X, 1, 1, "enc_pl", ff_source_vc2_encode_comp, 1);
+
+ if (s->wavelet_idx == VC2_TRANSFORM_HAAR || s->wavelet_idx == VC2_TRANSFORM_HAAR_S) {
+ if (subgroup_size == 32 && s->wavelet_depth < 3) {
+ init_vulkan_pipeline(vk_s, spv, &vk_s->dwt_haar_shd, sizeof(VC2DwtPushData),
+ 64, 1, 1, "dwt_haar_pl", ff_source_vc2_dwt_haar_subgroup_comp, 1);
+ vk_s->haar_subgroup = 1;
+ } else if (subgroup_size == 64 && s->wavelet_depth < 4) {
+ init_vulkan_pipeline(vk_s, spv, &vk_s->dwt_haar_shd, sizeof(VC2DwtPushData),
+ 64, 1, 1, "dwt_haar_pl", ff_source_vc2_dwt_haar_subgroup_comp, 1);
+ vk_s->haar_subgroup = 1;
+ } else {
+ init_vulkan_pipeline(vk_s, spv, &vk_s->dwt_haar_shd, sizeof(VC2DwtPushData),
+ 16, 16, 1, "dwt_haar_pl", ff_source_vc2_dwt_haar_comp, 1);
+ }
+ } else if (s->wavelet_idx == VC2_TRANSFORM_5_3) {
+ init_vulkan_pipeline(vk_s, spv, &vk_s->dwt_hor_shd, sizeof(VC2DwtPushData),
+ LEGALL_WORKGROUP_X, 1, 1, "dwt_hor_pl", ff_source_vc2_dwt_hor_legall_comp, 1);
+ init_vulkan_pipeline(vk_s, spv, &vk_s->dwt_ver_shd, sizeof(VC2DwtPushData),
+ LEGALL_WORKGROUP_X, 1, 1, "dwt_ver_pl", ff_source_vc2_dwt_ver_legall_comp, 1);
+ }
+
+ s->group_x = s->plane[0].dwt_width >> 3;
+ s->group_y = s->plane[0].dwt_height >> 3;
+
+fail:
+ return err;
+}
+
+static void vulkan_bind_img_planes(FFVulkanContext *s, FFVkExecContext *e,
+ FFVulkanShader *shd, VkImageView *views,
+ int set, int binding)
+{
+ for (int i = 0; i < 3; i++)
+ ff_vk_set_descriptor_image(s, shd, e, set, binding, i,
+ views[i], VK_IMAGE_LAYOUT_GENERAL,
+ VK_NULL_HANDLE);
+}
+
+static void dwt_plane_haar(VC2EncVulkanContext *s, FFVkExecContext *exec,
+ VkImageMemoryBarrier2* img_bar, int nb_img_bar)
+{
+ int p, group_x, group_y;
+ FFVulkanContext *vkctx = &s->vkctx;
+ FFVulkanFunctions *vk = &vkctx->vkfn;
+ Plane* plane;
+
+ s->dwt_consts.level = s->base.wavelet_depth;
+ vulkan_bind_img_planes(vkctx, exec, &s->dwt_haar_shd, s->intermediate_views, 0, 0);
+ ff_vk_exec_bind_shader(vkctx, exec, &s->dwt_haar_shd);
+
+ /* Haar pass */
+ for (p = 0; p < 3; p++) {
+ plane = &s->base.plane[p];
+ s->dwt_consts.plane_idx = p;
+ if (s->haar_subgroup) {
+ group_x = FFALIGN(plane->dwt_width, 8) >> 3;
+ group_y = FFALIGN(plane->dwt_height, 8) >> 3;
+ } else {
+ group_x = FFALIGN(plane->dwt_width, 16) >> 4;
+ group_y = FFALIGN(plane->dwt_height, 16) >> 4;
+ }
+
+ ff_vk_shader_update_push_const(vkctx, exec, &s->dwt_haar_shd, VK_SHADER_STAGE_COMPUTE_BIT,
+ 0, sizeof(VC2DwtPushData), &s->dwt_consts);
+ vk->CmdDispatch(exec->buf, group_x, group_y, 1);
+ }
+
+ /* Wait for haar dispatches to complete */
+ vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
+ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
+ .pImageMemoryBarriers = img_bar,
+ .imageMemoryBarrierCount = nb_img_bar,
+ });
+}
+
+static void dwt_plane_legall(VC2EncVulkanContext *s, FFVkExecContext *exec,
+ VkImageMemoryBarrier2* img_bar, int nb_img_bar)
+{
+ FFVulkanContext *vkctx = &s->vkctx;
+ FFVulkanFunctions *vk = &vkctx->vkfn;
+ int legall_group_x = (s->base.plane[0].dwt_height + LEGALL_WORKGROUP_X - 1) >> 6;
+ int legall_group_y = (s->base.plane[0].dwt_width + LEGALL_WORKGROUP_X - 1) >> 6;
+ int i;
+
+ /* Perform Haar wavelet trasform */
+ for (i = 0; i < s->base.wavelet_depth; i++) {
+ s->dwt_consts.level = i;
+
+ /* Horizontal Haar pass */
+ vulkan_bind_img_planes(vkctx, exec, &s->dwt_hor_shd, s->intermediate_views, 0, 0);
+ ff_vk_exec_bind_shader(vkctx, exec, &s->dwt_hor_shd);
+ ff_vk_shader_update_push_const(vkctx, exec, &s->dwt_hor_shd, VK_SHADER_STAGE_COMPUTE_BIT,
+ 0, sizeof(VC2DwtPushData), &s->dwt_consts);
+ vk->CmdDispatch(exec->buf, legall_group_x, 1, 3);
+ vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
+ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
+ .pImageMemoryBarriers = img_bar,
+ .imageMemoryBarrierCount = nb_img_bar,
+ });
+
+ /* Vertical Haar pass */
+ vulkan_bind_img_planes(vkctx, exec, &s->dwt_ver_shd, s->intermediate_views, 0, 0);
+ ff_vk_exec_bind_shader(vkctx, exec, &s->dwt_ver_shd);
+ ff_vk_shader_update_push_const(vkctx, exec, &s->dwt_ver_shd, VK_SHADER_STAGE_COMPUTE_BIT,
+ 0, sizeof(VC2DwtPushData), &s->dwt_consts);
+ vk->CmdDispatch(exec->buf, legall_group_y, 1, 3);
+ vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
+ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
+ .pImageMemoryBarriers = img_bar,
+ .imageMemoryBarrierCount = nb_img_bar,
+ });
+ }
+}
+
+static int dwt_plane(VC2EncVulkanContext *s, FFVkExecContext *exec, AVFrame *frame)
+{
+ int i, err = 0, nb_img_bar = 0;
+ int wavelet_idx = s->base.wavelet_idx;
+ int num_slice_groups = (s->base.num_x * s->base.num_y + SLICE_WORKGROUP_X - 1) >> 7;
+ FFVulkanContext *vkctx = &s->vkctx;
+ FFVulkanFunctions *vk = &vkctx->vkfn;
+ VkImageView views[AV_NUM_DATA_POINTERS];
+ VkImageMemoryBarrier2 img_bar[AV_NUM_DATA_POINTERS];
+
+ /* Generate barriers and image views for frame images. */
+ RET(ff_vk_exec_add_dep_frame(vkctx, exec, frame,
+ VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
+ VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT));
+ RET(ff_vk_create_imageviews(vkctx, exec, views, frame, FF_VK_REP_UINT));
+ ff_vk_frame_barrier(vkctx, exec, frame, img_bar, &nb_img_bar,
+ VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
+ VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
+ VK_ACCESS_SHADER_READ_BIT,
+ VK_IMAGE_LAYOUT_GENERAL,
+ VK_QUEUE_FAMILY_IGNORED);
+
+ /* Submit the image barriers. */
+ vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
+ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
+ .pImageMemoryBarriers = img_bar,
+ .imageMemoryBarrierCount = nb_img_bar,
+ });
+
+ /* Create a temporaty frames */
+ nb_img_bar = 0;
+ for (i = 0; i < 3; i++) {
+ s->intermediate_frame[i] = av_frame_alloc();
+ if (!s->intermediate_frame[i])
+ return AVERROR(ENOMEM);
+
+ RET(av_hwframe_get_buffer(s->intermediate_frames_ref[i],
+ s->intermediate_frame[i], 0));
+ RET(ff_vk_exec_add_dep_frame(vkctx, exec, s->intermediate_frame[i],
+ VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
+ VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT));
+ RET(ff_vk_create_imageviews(vkctx, exec, &s->intermediate_views[i],
+ s->intermediate_frame[i], FF_VK_REP_UINT));
+ ff_vk_frame_barrier(vkctx, exec, s->intermediate_frame[i], img_bar, &nb_img_bar,
+ VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
+ VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
+ VK_ACCESS_SHADER_READ_BIT,
+ VK_IMAGE_LAYOUT_GENERAL,
+ VK_QUEUE_FAMILY_IGNORED);
+ }
+
+ /* Submit the image barriers. */
+ vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
+ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
+ .pImageMemoryBarriers = img_bar,
+ .imageMemoryBarrierCount = nb_img_bar,
+ });
+
+ /* Bind input images to the shader. */
+ vulkan_bind_img_planes(vkctx, exec, &s->dwt_upload_shd, s->intermediate_views, 0, 0);
+ ff_vk_shader_update_img_array(vkctx, exec, &s->dwt_upload_shd, frame, views, 0, 1,
+ VK_IMAGE_LAYOUT_GENERAL, VK_NULL_HANDLE);
+
+ /* Upload coefficients from planes to the buffer. */
+ s->dwt_consts.diff_offset = s->base.diff_offset;
+ ff_vk_exec_bind_shader(vkctx, exec, &s->dwt_upload_shd);
+ ff_vk_shader_update_push_const(vkctx, exec, &s->dwt_upload_shd, VK_SHADER_STAGE_COMPUTE_BIT,
+ 0, sizeof(VC2DwtPushData), &s->dwt_consts);
+ vk->CmdDispatch(exec->buf, s->base.group_x, s->base.group_y, 3);
+ vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
+ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
+ .pImageMemoryBarriers = img_bar,
+ .imageMemoryBarrierCount = nb_img_bar,
+ });
+
+ /* Perform wavelet trasform. */
+ if (wavelet_idx == VC2_TRANSFORM_HAAR || wavelet_idx == VC2_TRANSFORM_HAAR_S) {
+ dwt_plane_haar(s, exec, img_bar, nb_img_bar);
+ } else if (wavelet_idx == VC2_TRANSFORM_5_3) {
+ dwt_plane_legall(s, exec, img_bar, nb_img_bar);
+ }
+
+ /* Calculate slice sizes. */
+ ff_vk_exec_bind_shader(vkctx, exec, &s->slice_shd);
+ vulkan_bind_img_planes(vkctx, exec, &s->slice_shd, s->intermediate_views, 0, 0);
+ ff_vk_shader_update_push_const(vkctx, exec, &s->slice_shd, VK_SHADER_STAGE_COMPUTE_BIT,
+ 0, sizeof(VC2EncSliceCalcPushData), &s->calc_consts);
+ vk->CmdDispatch(exec->buf, num_slice_groups, 1, 1);
+
+fail:
+ return err;
+}
+
+static void vulkan_encode_slices(VC2EncVulkanContext *s, FFVkExecContext *exec)
+{
+ int num_slices = s->base.num_x * s->base.num_y;
+ int i, skip = 0, num_slice_groups = (num_slices + SLICE_WORKGROUP_X - 1) >> 7;
+ FFVulkanContext *vkctx = &s->vkctx;
+ FFVulkanFunctions *vk = &vkctx->vkfn;
+
+ VkBufferMemoryBarrier2 slice_buf_bar = (VkBufferMemoryBarrier2) {
+ .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2,
+ .srcStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
+ .dstStageMask = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT,
+ .srcAccessMask = VK_ACCESS_2_SHADER_WRITE_BIT,
+ .dstAccessMask = VK_ACCESS_2_SHADER_READ_BIT,
+ .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
+ .buffer = s->slice_buf,
+ .size = sizeof(VC2EncSliceArgs) * num_slices,
+ .offset = 0,
+ };
+
+ flush_put_bits(&s->base.pb);
+ s->enc_consts.pb += put_bytes_output(&s->base.pb);
+
+ /* Wait for slice sizes to be written. */
+ vk->CmdPipelineBarrier2(exec->buf, &(VkDependencyInfo) {
+ .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO,
+ .pBufferMemoryBarriers = &slice_buf_bar,
+ .bufferMemoryBarrierCount = 1U,
+ });
+
+ /* Bind encoding shader. */
+ vulkan_bind_img_planes(vkctx, exec, &s->enc_shd, s->intermediate_views, 0, 0);
+ ff_vk_exec_bind_shader(vkctx, exec, &s->enc_shd);
+ ff_vk_shader_update_push_const(vkctx, exec, &s->enc_shd, VK_SHADER_STAGE_COMPUTE_BIT,
+ 0, sizeof(VC2EncPushData), &s->enc_consts);
+
+ vk->CmdDispatch(exec->buf, num_slice_groups, 1, 1);
+
+ ff_vk_exec_submit(vkctx, exec);
+ ff_vk_exec_wait(vkctx, exec);
+
+ for (int slice_y = 0; slice_y < s->base.num_y; slice_y++) {
+ for (int slice_x = 0; slice_x < s->base.num_x; slice_x++) {
+ VC2EncSliceArgs *args = &s->vk_slice_args[s->base.num_x * slice_y + slice_x];
+ skip += args->bytes;
+ }
+ }
+
+ /* Skip forward to write end header */
+ skip_put_bytes(&s->base.pb, skip);
+
+ /* Free allocated intermediate frames */
+ for (i = 0; i < 3; i++)
+ av_frame_free(&s->intermediate_frame[i]);
+}
+
+static int encode_frame(VC2EncVulkanContext *vk_s, AVPacket *avpkt, const AVFrame *frame,
+ const char *aux_data, const int header_size, int field)
+{
+ int ret;
+ int64_t max_frame_bytes;
+ AVBufferRef *avpkt_buf = NULL;
+ FFVkBuffer* buf_vk = NULL;
+ VC2EncContext* s = &vk_s->base;
+ FFVulkanContext *vkctx = &vk_s->vkctx;
+ FFVkExecContext *exec = ff_vk_exec_get(vkctx, &vk_s->e);
+
+ ff_vk_exec_start(vkctx, exec);
+
+ /* Perform wavelet pass on the inpute frame. */
+ dwt_plane(vk_s, exec, (AVFrame*)frame);
+
+ /* Allocate a buffer that can fit at all all 3 planes of data */
+ max_frame_bytes = header_size + s->avctx->width * s->avctx->height * sizeof(dwtcoef);
+ s->custom_quant_matrix = 0;
+
+ /* Get a pooled device local host visible buffer for writing output data */
+ if (field < 2) {
+ ret = ff_vk_get_pooled_buffer(vkctx, &vk_s->dwt_buf_pool, &avpkt_buf,
+ VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
+ VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, NULL,
+ max_frame_bytes << s->interlaced,
+ VK_MEMORY_PROPERTY_HOST_CACHED_BIT |
+ VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
+ VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
+ avpkt->buf = avpkt_buf;
+ buf_vk = (FFVkBuffer *)avpkt_buf->data;
+ avpkt->data = buf_vk->mapped_mem;
+ avpkt->size = max_frame_bytes << s->interlaced;
+ vk_s->enc_consts.pb = buf_vk->address;
+
+ if (ret < 0) {
+ return ret;
+ }
+ init_put_bits(&s->pb, avpkt->data, avpkt->size);
+ }
+
+ /* Sequence header */
+ ff_vc2_encode_parse_info(s, DIRAC_PCODE_SEQ_HEADER);
+ ff_vc2_encode_seq_header(s);
+
+ /* Encoder version */
+ if (aux_data) {
+ ff_vc2_encode_parse_info(s, DIRAC_PCODE_AUX);
+ ff_put_string(&s->pb, aux_data, 1);
+ }
+
+ /* Picture header */
+ ff_vc2_encode_parse_info(s, DIRAC_PCODE_PICTURE_HQ);
+ ff_vc2_encode_picture_start(s);
+
+ /* Encode slices */
+ vulkan_encode_slices(vk_s, exec);
+
+ /* End sequence */
+ ff_vc2_encode_parse_info(s, DIRAC_PCODE_END_SEQ);
+
+ return 0;
+}
+
+static av_cold int vc2_encode_frame(AVCodecContext *avctx, AVPacket *avpkt,
+ const AVFrame *frame, int *got_packet)
+{
+ int ret = 0;
+ int slice_ceil, sig_size = 256;
+ VC2EncVulkanContext *vk_s = avctx->priv_data;
+ VC2EncContext* s = &vk_s->base;
+ const int bitexact = avctx->flags & AV_CODEC_FLAG_BITEXACT;
+ const char *aux_data = bitexact ? "Lavc" : LIBAVCODEC_IDENT;
+ const int aux_data_size = bitexact ? sizeof("Lavc") : sizeof(LIBAVCODEC_IDENT);
+ const int header_size = 100 + aux_data_size;
+ int64_t r_bitrate = avctx->bit_rate >> (s->interlaced);
+
+ s->avctx = avctx;
+ s->size_scaler = 2;
+ s->prefix_bytes = 0;
+ s->last_parse_code = 0;
+ s->next_parse_offset = 0;
+
+ /* Rate control */
+ s->frame_max_bytes = (av_rescale(r_bitrate, s->avctx->time_base.num,
+ s->avctx->time_base.den) >> 3) - header_size;
+ s->slice_max_bytes = slice_ceil = av_rescale(s->frame_max_bytes, 1, s->num_x * s->num_y);
+
+ /* Find an appropriate size scaler */
+ while (sig_size > 255) {
+ int r_size = SSIZE_ROUND(s->slice_max_bytes);
+ if (r_size > slice_ceil) {
+ s->slice_max_bytes -= r_size - slice_ceil;
+ r_size = SSIZE_ROUND(s->slice_max_bytes);
+ }
+ sig_size = r_size/s->size_scaler; /* Signalled slize size */
+ s->size_scaler <<= 1;
+ }
+
+ s->slice_min_bytes = s->slice_max_bytes - s->slice_max_bytes*(s->tolerance/100.0f);
+ if (s->slice_min_bytes < 0)
+ return AVERROR(EINVAL);
+
+ /* Update slice calc push data */
+ vk_s->calc_consts.size_scaler = s->size_scaler;
+ vk_s->calc_consts.bits_ceil = s->slice_max_bytes << 3;
+ vk_s->calc_consts.bits_floor = s->slice_min_bytes << 3;
+ vk_s->enc_consts.prefix_bytes = 0;
+ vk_s->enc_consts.size_scaler = s->size_scaler;
+
+ ret = encode_frame(vk_s, avpkt, frame, aux_data, header_size, s->interlaced);
+ if (ret)
+ return ret;
+ if (s->interlaced) {
+ ret = encode_frame(vk_s, avpkt, frame, aux_data, header_size, 2);
+ if (ret)
+ return ret;
+ }
+
+ flush_put_bits(&s->pb);
+ av_shrink_packet(avpkt, put_bytes_output(&s->pb));
+ avpkt->flags |= AV_PKT_FLAG_KEY;
+ *got_packet = 1;
+
+ return 0;
+}
+
+static av_cold int vc2_encode_end(AVCodecContext *avctx)
+{
+ int i;
+ VC2EncVulkanContext *vk_s = avctx->priv_data;
+
+ av_log(avctx, AV_LOG_INFO, "Qavg: %i\n", vk_s->base.q_avg);
+
+ for (i = 0; i < 3; i++) {
+ ff_vc2enc_free_transforms(&vk_s->base.transform_args[i].t);
+ av_freep(&vk_s->base.plane[i].coef_buf);
+ av_buffer_unref(&vk_s->intermediate_frames_ref[i]);
+ }
+
+ return 0;
+}
+
+static av_cold int vc2_encode_init(AVCodecContext *avctx)
+{
+ Plane *p;
+ SubBand *b;
+ int i, level, o, ret, depth;
+ const AVPixFmtDescriptor *fmt;
+ VC2EncVulkanContext *vk_s = avctx->priv_data;
+ VC2EncContext *s = &vk_s->base;
+ FFVulkanContext *vkctx = &vk_s->vkctx;
+
+ /* Init Vulkan */
+ ret = ff_vk_init(&vk_s->vkctx, avctx, NULL, avctx->hw_frames_ctx);
+ if (ret < 0)
+ return ret;
+
+ vk_s->qf = ff_vk_qf_find(vkctx, VK_QUEUE_COMPUTE_BIT, 0);
+ if (!vk_s->qf) {
+ av_log(avctx, AV_LOG_ERROR, "Device has no compute queues!\n");
+ return ret;
+ }
+
+ s->picture_number = 0;
+
+ /* Total allowed quantization range */
+ s->q_ceil = DIRAC_MAX_QUANT_INDEX;
+
+ s->ver.major = 2;
+ s->ver.minor = 0;
+ s->profile = 3;
+ s->level = 3;
+
+ s->base_vf = -1;
+ s->strict_compliance = 1;
+
+ s->q_avg = 0;
+ s->slice_max_bytes = 0;
+ s->slice_min_bytes = 0;
+
+ /* Mark unknown as progressive */
+ s->interlaced = !((avctx->field_order == AV_FIELD_UNKNOWN) ||
+ (avctx->field_order == AV_FIELD_PROGRESSIVE));
+
+ for (i = 0; i < base_video_fmts_len; i++) {
+ const VC2BaseVideoFormat *fmt = &base_video_fmts[i];
+ if (avctx->pix_fmt != fmt->pix_fmt)
+ continue;
+ if (avctx->time_base.num != fmt->time_base.num)
+ continue;
+ if (avctx->time_base.den != fmt->time_base.den)
+ continue;
+ if (avctx->width != fmt->width)
+ continue;
+ if (avctx->height != fmt->height)
+ continue;
+ if (s->interlaced != fmt->interlaced)
+ continue;
+ s->base_vf = i;
+ s->level = base_video_fmts[i].level;
+ break;
+ }
+
+ if (s->interlaced)
+ av_log(avctx, AV_LOG_WARNING, "Interlacing enabled!\n");
+
+ if ((s->slice_width & (s->slice_width - 1)) ||
+ (s->slice_height & (s->slice_height - 1))) {
+ av_log(avctx, AV_LOG_ERROR, "Slice size is not a power of two!\n");
+ return AVERROR_UNKNOWN;
+ }
+
+ if ((s->slice_width > avctx->width) ||
+ (s->slice_height > avctx->height)) {
+ av_log(avctx, AV_LOG_ERROR, "Slice size is bigger than the image!\n");
+ return AVERROR_UNKNOWN;
+ }
+
+ if (s->base_vf <= 0) {
+ if (avctx->strict_std_compliance < FF_COMPLIANCE_STRICT) {
+ s->strict_compliance = s->base_vf = 0;
+ av_log(avctx, AV_LOG_WARNING, "Format does not strictly comply with VC2 specs\n");
+ } else {
+ av_log(avctx, AV_LOG_WARNING, "Given format does not strictly comply with "
+ "the specifications, decrease strictness to use it.\n");
+ return AVERROR_UNKNOWN;
+ }
+ } else {
+ av_log(avctx, AV_LOG_INFO, "Selected base video format = %i (%s)\n",
+ s->base_vf, base_video_fmts[s->base_vf].name);
+ }
+
+ /* Chroma subsampling */
+ ret = av_pix_fmt_get_chroma_sub_sample(vkctx->frames->sw_format, &s->chroma_x_shift, &s->chroma_y_shift);
+ if (ret)
+ return ret;
+
+ /* Bit depth and color range index */
+ fmt = av_pix_fmt_desc_get(vkctx->frames->sw_format);
+ depth = fmt->comp[0].depth;
+ if (depth == 8 && avctx->color_range == AVCOL_RANGE_JPEG) {
+ s->bpp = 1;
+ s->bpp_idx = 1;
+ s->diff_offset = 128;
+ } else if (depth == 8 && (avctx->color_range == AVCOL_RANGE_MPEG ||
+ avctx->color_range == AVCOL_RANGE_UNSPECIFIED)) {
+ s->bpp = 1;
+ s->bpp_idx = 2;
+ s->diff_offset = 128;
+ } else if (depth == 10) {
+ s->bpp = 2;
+ s->bpp_idx = 3;
+ s->diff_offset = 512;
+ } else {
+ s->bpp = 2;
+ s->bpp_idx = 4;
+ s->diff_offset = 2048;
+ }
+
+ /* Planes initialization */
+ for (i = 0; i < 3; i++) {
+ int w, h;
+ p = &s->plane[i];
+ p->width = avctx->width >> (i ? s->chroma_x_shift : 0);
+ p->height = avctx->height >> (i ? s->chroma_y_shift : 0);
+ if (s->interlaced)
+ p->height >>= 1;
+ p->dwt_width = w = FFALIGN(p->width, (1 << s->wavelet_depth));
+ p->dwt_height = h = FFALIGN(p->height, (1 << s->wavelet_depth));
+ p->coef_stride = FFALIGN(p->dwt_width, 32);
+ for (level = s->wavelet_depth-1; level >= 0; level--) {
+ w = w >> 1;
+ h = h >> 1;
+ for (o = 0; o < 4; o++) {
+ b = &p->band[level][o];
+ b->width = w;
+ b->height = h;
+ b->stride = p->coef_stride;
+ b->shift = (o > 1)*b->height*b->stride + (o & 1)*b->width;
+ }
+ }
+
+ /* DWT init */
+ if (ff_vc2enc_init_transforms(&s->transform_args[i].t,
+ s->plane[i].coef_stride,
+ s->plane[i].dwt_height,
+ s->slice_width, s->slice_height))
+ return AVERROR(ENOMEM);
+ }
+
+ /* Slices */
+ s->num_x = s->plane[0].dwt_width/s->slice_width;
+ s->num_y = s->plane[0].dwt_height/s->slice_height;
+
+ s->slice_args = av_calloc(s->num_x*s->num_y, sizeof(SliceArgs));
+ if (!s->slice_args)
+ return AVERROR(ENOMEM);
+
+ for (i = 0; i < 116; i++) {
+ const uint64_t qf = ff_dirac_qscale_tab[i];
+ const uint32_t m = av_log2(qf);
+ const uint32_t t = (1ULL << (m + 32)) / qf;
+ const uint32_t r = (t*qf + qf) & UINT32_MAX;
+ if (!(qf & (qf - 1))) {
+ s->qmagic_lut[i][0] = 0xFFFFFFFF;
+ s->qmagic_lut[i][1] = 0xFFFFFFFF;
+ } else if (r <= 1 << m) {
+ s->qmagic_lut[i][0] = t + 1;
+ s->qmagic_lut[i][1] = 0;
+ } else {
+ s->qmagic_lut[i][0] = t;
+ s->qmagic_lut[i][1] = t;
+ }
+ }
+ init_vulkan(avctx);
+
+ return 0;
+}
+
+#define VC2ENC_FLAGS (AV_OPT_FLAG_ENCODING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
+static const AVOption vc2enc_options[] = {
+ {"tolerance", "Max undershoot in percent", offsetof(VC2EncContext, tolerance), AV_OPT_TYPE_DOUBLE, {.dbl = 5.0f}, 0.0f, 45.0f, VC2ENC_FLAGS, .unit = "tolerance"},
+ {"slice_width", "Slice width", offsetof(VC2EncContext, slice_width), AV_OPT_TYPE_INT, {.i64 = 32}, 32, 1024, VC2ENC_FLAGS, .unit = "slice_width"},
+ {"slice_height", "Slice height", offsetof(VC2EncContext, slice_height), AV_OPT_TYPE_INT, {.i64 = 16}, 8, 1024, VC2ENC_FLAGS, .unit = "slice_height"},
+ {"wavelet_depth", "Transform depth", offsetof(VC2EncContext, wavelet_depth), AV_OPT_TYPE_INT, {.i64 = 4}, 1, 5, VC2ENC_FLAGS, .unit = "wavelet_depth"},
+ {"wavelet_type", "Transform type", offsetof(VC2EncContext, wavelet_idx), AV_OPT_TYPE_INT, {.i64 = VC2_TRANSFORM_HAAR_S}, 0, VC2_TRANSFORMS_NB, VC2ENC_FLAGS, .unit = "wavelet_idx"},
+ {"5_3", "LeGall (5,3)", 0, AV_OPT_TYPE_CONST, {.i64 = VC2_TRANSFORM_5_3}, INT_MIN, INT_MAX, VC2ENC_FLAGS, .unit = "wavelet_idx"},
+ {"haar", "Haar (with shift)", 0, AV_OPT_TYPE_CONST, {.i64 = VC2_TRANSFORM_HAAR_S}, INT_MIN, INT_MAX, VC2ENC_FLAGS, .unit = "wavelet_idx"},
+ {"haar_noshift", "Haar (without shift)", 0, AV_OPT_TYPE_CONST, {.i64 = VC2_TRANSFORM_HAAR}, INT_MIN, INT_MAX, VC2ENC_FLAGS, .unit = "wavelet_idx"},
+ {"qm", "Custom quantization matrix", offsetof(VC2EncContext, quant_matrix), AV_OPT_TYPE_INT, {.i64 = VC2_QM_DEF}, 0, VC2_QM_NB, VC2ENC_FLAGS, .unit = "quant_matrix"},
+ {"default", "Default from the specifications", 0, AV_OPT_TYPE_CONST, {.i64 = VC2_QM_DEF}, INT_MIN, INT_MAX, VC2ENC_FLAGS, .unit = "quant_matrix"},
+ {"color", "Prevents low bitrate discoloration", 0, AV_OPT_TYPE_CONST, {.i64 = VC2_QM_COL}, INT_MIN, INT_MAX, VC2ENC_FLAGS, .unit = "quant_matrix"},
+ {"flat", "Optimize for PSNR", 0, AV_OPT_TYPE_CONST, {.i64 = VC2_QM_FLAT}, INT_MIN, INT_MAX, VC2ENC_FLAGS, .unit = "quant_matrix"},
+ {NULL}
+};
+
+static const AVClass vc2enc_class = {
+ .class_name = "vc2_vulkan_encoder",
+ .category = AV_CLASS_CATEGORY_ENCODER,
+ .option = vc2enc_options,
+ .item_name = av_default_item_name,
+ .version = LIBAVUTIL_VERSION_INT
+};
+
+static const FFCodecDefault vc2enc_defaults[] = {
+ { "b", "600000000" },
+ { NULL },
+};
+
+const AVCodecHWConfigInternal *const ff_vc2_hw_configs[] = {
+ HW_CONFIG_ENCODER_FRAMES(VULKAN, VULKAN),
+ HW_CONFIG_ENCODER_DEVICE(NONE, VULKAN),
+ NULL,
+};
+
+const FFCodec ff_vc2_vulkan_encoder = {
+ .p.name = "vc2_vulkan",
+ CODEC_LONG_NAME("SMPTE VC-2"),
+ .p.type = AVMEDIA_TYPE_VIDEO,
+ .p.id = AV_CODEC_ID_DIRAC,
+ .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_HARDWARE,
+ .caps_internal = FF_CODEC_CAP_INIT_CLEANUP,
+ .priv_data_size = sizeof(VC2EncVulkanContext),
+ .init = vc2_encode_init,
+ .close = vc2_encode_end,
+ FF_CODEC_ENCODE_CB(vc2_encode_frame),
+ .p.priv_class = &vc2enc_class,
+ .defaults = vc2enc_defaults,
+ .p.pix_fmts = (const enum AVPixelFormat[]) {
+ AV_PIX_FMT_VULKAN,
+ AV_PIX_FMT_NONE,
+ },
+ .hw_configs = ff_vc2_hw_configs,
+};
--
2.48.1
_______________________________________________
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
To unsubscribe, visit link above, or email
ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2025-03-08 15:35 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-03-08 15:33 [FFmpeg-devel] [PATCH v2 1/4] libavcodec/vc2enc: Split out common functions between software and hardware encoders IndecisiveTurtle
2025-03-08 15:33 ` [FFmpeg-devel] [PATCH v2 2/4] libavcodec/vulkan: Add modifications to common shader for VC2 vulkan encoder IndecisiveTurtle
2025-03-08 15:33 ` [FFmpeg-devel] [PATCH v2 3/4] libavcodec/vulkan: Add vulkan vc2 shaders IndecisiveTurtle
2025-03-08 15:33 ` [FFmpeg-devel] [PATCH v2 4/4] avcodec/vc2enc: Initial vulkan VC2 encoder Implements a Vulkan based dirac encoder. Supports Haar and Legall wavelets and should work with all wavelet depths IndecisiveTurtle
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git