diff mbox

[FFmpeg-devel,v4,29/38] vaapi_encode: Support configurable slices

Message ID 20180918223116.14322-30-sw@jkqxz.net
State Accepted
Headers show

Commit Message

Mark Thompson Sept. 18, 2018, 10:31 p.m. UTC
This adds common code to query driver support and set appropriate
address/size information for each slice.  It only supports rectangular
slices for now, since that is the most common use-case.

Also immediately use that to replace the ad-hoc code doing the same
thing for MPEG-2.
---
 libavcodec/vaapi_encode.c       | 149 +++++++++++++++++++++++++++++++-
 libavcodec/vaapi_encode.h       |  22 +++++
 libavcodec/vaapi_encode_mpeg2.c |  18 ++--
 3 files changed, 177 insertions(+), 12 deletions(-)

Comments

Michael Niedermayer Sept. 19, 2018, 8:24 a.m. UTC | #1
On Tue, Sep 18, 2018 at 11:31:07PM +0100, Mark Thompson wrote:
> This adds common code to query driver support and set appropriate
> address/size information for each slice.  It only supports rectangular
> slices for now, since that is the most common use-case.
> 
> Also immediately use that to replace the ad-hoc code doing the same
> thing for MPEG-2.
> ---
>  libavcodec/vaapi_encode.c       | 149 +++++++++++++++++++++++++++++++-
>  libavcodec/vaapi_encode.h       |  22 +++++
>  libavcodec/vaapi_encode_mpeg2.c |  18 ++--
>  3 files changed, 177 insertions(+), 12 deletions(-)

breaks build (on ubuntu)

CC	libavcodec/vaapi_encode.o
libavcodec/vaapi_encode.c: In function ‘vaapi_encode_init_slice_structure’:
libavcodec/vaapi_encode.c:1561:34: error: ‘VA_ENC_SLICE_STRUCTURE_EQUAL_ROWS’ undeclared (first use in this function)
     } else if (slice_structure & VA_ENC_SLICE_STRUCTURE_EQUAL_ROWS) {
                                  ^
libavcodec/vaapi_encode.c:1561:34: note: each undeclared identifier is reported only once for each function it appears in
make: *** [libavcodec/vaapi_encode.o] Error 1
make: Target `all' not remade because of errors.

[...]
diff mbox

Patch

diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index 2c34cdce2c..601d74b5c4 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -319,16 +319,60 @@  static int vaapi_encode_issue(AVCodecContext *avctx,
         }
     }
 
+    if (pic->nb_slices == 0)
+        pic->nb_slices = ctx->nb_slices;
     if (pic->nb_slices > 0) {
+        int rounding;
+
         pic->slices = av_mallocz_array(pic->nb_slices, sizeof(*pic->slices));
         if (!pic->slices) {
             err = AVERROR(ENOMEM);
             goto fail;
         }
+
+        for (i = 0; i < pic->nb_slices; i++)
+            pic->slices[i].row_size = ctx->slice_size;
+
+        rounding = ctx->slice_block_rows - ctx->nb_slices * ctx->slice_size;
+        if (rounding > 0) {
+            // Place rounding error at top and bottom of frame.
+            av_assert0(rounding < pic->nb_slices);
+            // Some Intel hardware contains a bug where the encoder will fail
+            // if the last slice is smaller than the one before it.  Since
+            // that's straightforward to avoid here, just do so.
+            if (rounding <= 2) {
+                for (i = 0; i < rounding; i++)
+                    ++pic->slices[i].row_size;
+            } else {
+                for (i = 0; i < (rounding + 1) / 2; i++)
+                    ++pic->slices[pic->nb_slices - i - 1].row_size;
+                for (i = 0; i < rounding / 2; i++)
+                    ++pic->slices[i].row_size;
+            }
+        } else if (rounding < 0) {
+            // Remove rounding error from last slice only.
+            av_assert0(rounding < ctx->slice_size);
+            pic->slices[pic->nb_slices - 1].row_size += rounding;
+        }
     }
     for (i = 0; i < pic->nb_slices; i++) {
         slice = &pic->slices[i];
         slice->index = i;
+        if (i == 0) {
+            slice->row_start   = 0;
+            slice->block_start = 0;
+        } else {
+            const VAAPIEncodeSlice *prev = &pic->slices[i - 1];
+            slice->row_start   = prev->row_start   + prev->row_size;
+            slice->block_start = prev->block_start + prev->block_size;
+        }
+        slice->block_size  = slice->row_size * ctx->slice_block_cols;
+
+        av_log(avctx, AV_LOG_DEBUG, "Slice %d: %d-%d (%d rows), "
+               "%d-%d (%d blocks).\n", i, slice->row_start,
+               slice->row_start + slice->row_size - 1, slice->row_size,
+               slice->block_start, slice->block_start + slice->block_size - 1,
+               slice->block_size);
 
         if (ctx->codec->slice_params_size > 0) {
             slice->codec_slice_params = av_mallocz(ctx->codec->slice_params_size);
@@ -1014,8 +1058,7 @@  static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
     VAEntrypoint *va_entrypoints = NULL;
     VAStatus vas;
     const VAEntrypoint *usable_entrypoints;
-    const VAAPIEncodeProfile *profile;
-    const AVPixFmtDescriptor *desc;
+    const VAAPIEncodeProfile *profile;    const AVPixFmtDescriptor *desc;
     VAConfigAttrib rt_format_attr;
     const VAAPIEncodeRTFormat *rt_format;
     const char *profile_string, *entrypoint_string;
@@ -1444,6 +1487,104 @@  static av_cold int vaapi_encode_init_gop_structure(AVCodecContext *avctx)
     return 0;
 }
 
+static av_cold int vaapi_encode_init_slice_structure(AVCodecContext *avctx)
+{
+    VAAPIEncodeContext *ctx = avctx->priv_data;
+    VAConfigAttrib attr[2] = { { VAConfigAttribEncMaxSlices },
+                               { VAConfigAttribEncSliceStructure } };
+    VAStatus vas;
+    uint32_t max_slices, slice_structure;
+    int req_slices;
+
+    if (!(ctx->codec->flags & FLAG_SLICE_CONTROL)) {
+        if (avctx->slices > 0) {
+            av_log(avctx, AV_LOG_WARNING, "Multiple slices were requested "
+                   "but this codec does not support controlling slices.\n");
+        }
+        return 0;
+    }
+
+    ctx->slice_block_rows = (avctx->height + ctx->slice_block_height - 1) /
+                             ctx->slice_block_height;
+    ctx->slice_block_cols = (avctx->width  + ctx->slice_block_width  - 1) /
+                             ctx->slice_block_width;
+
+    if (avctx->slices <= 1) {
+        ctx->nb_slices  = 1;
+        ctx->slice_size = ctx->slice_block_rows;
+        return 0;
+    }
+
+    vas = vaGetConfigAttributes(ctx->hwctx->display,
+                                ctx->va_profile,
+                                ctx->va_entrypoint,
+                                attr, FF_ARRAY_ELEMS(attr));
+    if (vas != VA_STATUS_SUCCESS) {
+        av_log(avctx, AV_LOG_ERROR, "Failed to query slice "
+               "attributes: %d (%s).\n", vas, vaErrorStr(vas));
+        return AVERROR_EXTERNAL;
+    }
+    max_slices      = attr[0].value;
+    slice_structure = attr[1].value;
+    if (max_slices      == VA_ATTRIB_NOT_SUPPORTED ||
+        slice_structure == VA_ATTRIB_NOT_SUPPORTED) {
+        av_log(avctx, AV_LOG_ERROR, "Driver does not support encoding "
+               "pictures as multiple slices.\n.");
+        return AVERROR(EINVAL);
+    }
+
+    // For fixed-size slices currently we only support whole rows, making
+    // rectangular slices.  This could be extended to arbitrary runs of
+    // blocks, but since slices tend to be a conformance requirement and
+    // most cases (such as broadcast or bluray) want rectangular slices
+    // only it would need to be gated behind another option.
+    if (avctx->slices > ctx->slice_block_rows) {
+        av_log(avctx, AV_LOG_WARNING, "Not enough rows to use "
+               "configured number of slices (%d < %d); using "
+               "maximum.\n", ctx->slice_block_rows, avctx->slices);
+        req_slices = ctx->slice_block_rows;
+    } else {
+        req_slices = avctx->slices;
+    }
+    if (slice_structure & VA_ENC_SLICE_STRUCTURE_ARBITRARY_ROWS ||
+        slice_structure & VA_ENC_SLICE_STRUCTURE_ARBITRARY_MACROBLOCKS) {
+        ctx->nb_slices  = req_slices;
+        ctx->slice_size = ctx->slice_block_rows / ctx->nb_slices;
+    } else if (slice_structure & VA_ENC_SLICE_STRUCTURE_POWER_OF_TWO_ROWS) {
+        int k;
+        for (k = 1;; k *= 2) {
+            if (2 * k * (req_slices - 1) + 1 >= ctx->slice_block_rows)
+                break;
+        }
+        ctx->nb_slices  = (ctx->slice_block_rows + k - 1) / k;
+        ctx->slice_size = k;
+    } else if (slice_structure & VA_ENC_SLICE_STRUCTURE_EQUAL_ROWS) {
+        ctx->nb_slices  = ctx->slice_block_rows;
+        ctx->slice_size = 1;
+    } else {
+        av_log(avctx, AV_LOG_ERROR, "Driver does not support any usable "
+               "slice structure modes (%#x).\n", slice_structure);
+        return AVERROR(EINVAL);
+    }
+
+    if (ctx->nb_slices > avctx->slices) {
+        av_log(avctx, AV_LOG_WARNING, "Slice count rounded up to "
+               "%d (from %d) due to driver constraints on slice "
+               "structure.\n", ctx->nb_slices, avctx->slices);
+    }
+    if (ctx->nb_slices > max_slices) {
+        av_log(avctx, AV_LOG_ERROR, "Driver does not support "
+               "encoding with %d slices (max %"PRIu32").\n",
+               ctx->nb_slices, max_slices);
+        return AVERROR(EINVAL);
+    }
+
+    av_log(avctx, AV_LOG_VERBOSE, "Encoding pictures with %d slices "
+           "(default size %d block rows).\n",
+           ctx->nb_slices, ctx->slice_size);
+    return 0;
+}
+
 static av_cold int vaapi_encode_init_packed_headers(AVCodecContext *avctx)
 {
     VAAPIEncodeContext *ctx = avctx->priv_data;
@@ -1734,6 +1875,10 @@  av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
     if (err < 0)
         goto fail;
 
+    err = vaapi_encode_init_slice_structure(avctx);
+    if (err < 0)
+        goto fail;
+
     err = vaapi_encode_init_packed_headers(avctx);
     if (err < 0)
         goto fail;
diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h
index 091889f9ae..271d4ef518 100644
--- a/libavcodec/vaapi_encode.h
+++ b/libavcodec/vaapi_encode.h
@@ -52,6 +52,10 @@  enum {
 
 typedef struct VAAPIEncodeSlice {
     int             index;
+    int             row_start;
+    int             row_size;
+    int             block_start;
+    int             block_size;
     void           *priv_data;
     void           *codec_slice_params;
 } VAAPIEncodeSlice;
@@ -125,6 +129,10 @@  typedef struct VAAPIEncodeContext {
     int             surface_width;
     int             surface_height;
 
+    // The block size for slice calculations.
+    int             slice_block_width;
+    int             slice_block_height;
+
     // Everything above this point must be set before calling
     // ff_vaapi_encode_init().
 
@@ -224,6 +232,12 @@  typedef struct VAAPIEncodeContext {
     int64_t         dts_pts_diff;
     int64_t         ts_ring[MAX_REORDER_DELAY * 3];
 
+    // Slice structure.
+    int slice_block_rows;
+    int slice_block_cols;
+    int nb_slices;
+    int slice_size;
+
     // Frame type decision.
     int gop_size;
     int p_per_i;
@@ -234,11 +248,19 @@  typedef struct VAAPIEncodeContext {
     int end_of_stream;
 } VAAPIEncodeContext;
 
+enum {
+    // Codec supports controlling the subdivision of pictures into slices.
+    FLAG_SLICE_CONTROL         = 1 << 0,
+};
+
 typedef struct VAAPIEncodeType {
     // List of supported profiles and corresponding VAAPI profiles.
     // (Must end with FF_PROFILE_UNKNOWN.)
     const VAAPIEncodeProfile *profiles;
 
+    // Codec feature flags.
+    int flags;
+
     // Perform any extra codec-specific configuration after the
     // codec context is initialised (set up the private data and
     // add any necessary global parameters).
diff --git a/libavcodec/vaapi_encode_mpeg2.c b/libavcodec/vaapi_encode_mpeg2.c
index 1377eeb9bb..99a8b43237 100644
--- a/libavcodec/vaapi_encode_mpeg2.c
+++ b/libavcodec/vaapi_encode_mpeg2.c
@@ -35,9 +35,6 @@  typedef struct VAAPIEncodeMPEG2Context {
     int level;
 
     // Derived settings.
-    int mb_width;
-    int mb_height;
-
     int quant_i;
     int quant_p;
     int quant_b;
@@ -477,8 +474,6 @@  static int vaapi_encode_mpeg2_init_picture_params(AVCodecContext *avctx,
     vpic->f_code[1][0]       = pce->f_code[1][0];
     vpic->f_code[1][1]       = pce->f_code[1][1];
 
-    pic->nb_slices = priv->mb_height;
-
     return 0;
 }
 
@@ -490,8 +485,8 @@  static int vaapi_encode_mpeg2_init_slice_params(AVCodecContext *avctx,
     VAEncSliceParameterBufferMPEG2   *vslice = slice->codec_slice_params;
     int qp;
 
-    vslice->macroblock_address = priv->mb_width * slice->index;
-    vslice->num_macroblocks    = priv->mb_width;
+    vslice->macroblock_address = slice->block_start;
+    vslice->num_macroblocks    = slice->block_size;
 
     switch (pic->type) {
     case PICTURE_TYPE_IDR:
@@ -525,9 +520,6 @@  static av_cold int vaapi_encode_mpeg2_configure(AVCodecContext *avctx)
     if (err < 0)
         return err;
 
-    priv->mb_width  = FFALIGN(avctx->width,  16) / 16;
-    priv->mb_height = FFALIGN(avctx->height, 16) / 16;
-
     if (ctx->va_rc_mode == VA_RC_CQP) {
         priv->quant_p = av_clip(avctx->global_quality, 1, 31);
         if (avctx->i_quant_factor > 0.0)
@@ -553,6 +545,12 @@  static av_cold int vaapi_encode_mpeg2_configure(AVCodecContext *avctx)
         av_assert0(0 && "Invalid RC mode.");
     }
 
+    ctx->slice_block_rows = FFALIGN(avctx->width,  16) / 16;
+    ctx->slice_block_cols = FFALIGN(avctx->height, 16) / 16;
+
+    ctx->nb_slices  = ctx->slice_block_rows;
+    ctx->slice_size = 1;
+
     return 0;
 }