| @@ -319,16 +319,60 @@ static int vaapi_encode_issue(AVCodecContext *avctx, · 318 unmodified lines |
| 319 | 319 | } |
| 320 | 320 | } |
| 321 | 321 | |
| 322 | + if (pic->nb_slices == 0) |
| 323 | + pic->nb_slices = ctx->nb_slices; |
| 322 | 324 | if (pic->nb_slices > 0) { |
| 325 | + int rounding; |
| 326 | + |
| 323 | 327 | pic->slices = av_mallocz_array(pic->nb_slices, sizeof(*pic->slices)); |
| 324 | 328 | if (!pic->slices) { |
| 325 | 329 | err = AVERROR(ENOMEM); |
| 326 | 330 | goto fail; |
| 327 | 331 | } |
| 332 | + |
| 333 | + for (i = 0; i < pic->nb_slices; i++) |
| 334 | + pic->slices[i].row_size = ctx->slice_size; |
| 335 | + |
| 336 | + rounding = ctx->slice_block_rows - ctx->nb_slices * ctx->slice_size; |
| 337 | + if (rounding > 0) { |
| 338 | + // Place rounding error at top and bottom of frame. |
| 339 | + av_assert0(rounding < pic->nb_slices); |
| 340 | + // Some Intel drivers contain a bug where the encoder will fail |
| 341 | + // if the last slice is smaller than the one before it. Since |
| 342 | + // that's straightforward to avoid here, just do so. |
| 343 | + if (rounding <= 2) { |
| 344 | + for (i = 0; i < rounding; i++) |
| 345 | + ++pic->slices[i].row_size; |
| 346 | + } else { |
| 347 | + for (i = 0; i < (rounding + 1) / 2; i++) |
| 348 | + ++pic->slices[pic->nb_slices - i - 1].row_size; |
| 349 | + for (i = 0; i < rounding / 2; i++) |
| 350 | + ++pic->slices[i].row_size; |
| 351 | + } |
| 352 | + } else if (rounding < 0) { |
| 353 | + // Remove rounding error from last slice only. |
| 354 | + av_assert0(rounding < ctx->slice_size); |
| 355 | + pic->slices[pic->nb_slices - 1].row_size += rounding; |
| 356 | + } |
| 328 | 357 | } |
| 329 | 358 | for (i = 0; i < pic->nb_slices; i++) { |
| 330 | 359 | slice = &pic->slices[i]; |
| 331 | 360 | slice->index = i; |
| 361 | + if (i == 0) { |
| 362 | + slice->row_start = 0; |
| 363 | + slice->block_start = 0; |
| 364 | + } else { |
| 365 | + const VAAPIEncodeSlice *prev = &pic->slices[i - 1]; |
| 366 | + slice->row_start = prev->row_start + prev->row_size; |
| 367 | + slice->block_start = prev->block_start + prev->block_size; |
| 368 | + } |
| 369 | + slice->block_size = slice->row_size * ctx->slice_block_cols; |
| 370 | + |
| 371 | + av_log(avctx, AV_LOG_DEBUG, "Slice %d: %d-%d (%d rows), " |
| 372 | + "%d-%d (%d blocks).\n", i, slice->row_start, |
| 373 | + slice->row_start + slice->row_size - 1, slice->row_size, |
| 374 | + slice->block_start, slice->block_start + slice->block_size - 1, |
| 375 | + slice->block_size); |
| 332 | 376 | |
| 333 | 377 | if (ctx->codec->slice_params_size > 0) { |
| 334 | 378 | slice->codec_slice_params = av_mallocz(ctx->codec->slice_params_size); |
| @@ -1444,6 +1488,106 @@ static av_cold int vaapi_encode_init_gop_structure(AVCodecContext *avctx) · 1109 unmodified lines |
| 1444 | 1488 | return 0; |
| 1445 | 1489 | } |
| 1446 | 1490 | |
| 1491 | +static av_cold int vaapi_encode_init_slice_structure(AVCodecContext *avctx) |
| 1492 | +{ |
| 1493 | + VAAPIEncodeContext *ctx = avctx->priv_data; |
| 1494 | + VAConfigAttrib attr[2] = { { VAConfigAttribEncMaxSlices }, |
| 1495 | + { VAConfigAttribEncSliceStructure } }; |
| 1496 | + VAStatus vas; |
| 1497 | + uint32_t max_slices, slice_structure; |
| 1498 | + int req_slices; |
| 1499 | + |
| 1500 | + if (!(ctx->codec->flags & FLAG_SLICE_CONTROL)) { |
| 1501 | + if (avctx->slices > 0) { |
| 1502 | + av_log(avctx, AV_LOG_WARNING, "Multiple slices were requested " |
| 1503 | + "but this codec does not support controlling slices.\n"); |
| 1504 | + } |
| 1505 | + return 0; |
| 1506 | + } |
| 1507 | + |
| 1508 | + ctx->slice_block_rows = (avctx->height + ctx->slice_block_height - 1) / |
| 1509 | + ctx->slice_block_height; |
| 1510 | + ctx->slice_block_cols = (avctx->width + ctx->slice_block_width - 1) / |
| 1511 | + ctx->slice_block_width; |
| 1512 | + |
| 1513 | + if (avctx->slices <= 1) { |
| 1514 | + ctx->nb_slices = 1; |
| 1515 | + ctx->slice_size = ctx->slice_block_rows; |
| 1516 | + return 0; |
| 1517 | + } |
| 1518 | + |
| 1519 | + vas = vaGetConfigAttributes(ctx->hwctx->display, |
| 1520 | + ctx->va_profile, |
| 1521 | + ctx->va_entrypoint, |
| 1522 | + attr, FF_ARRAY_ELEMS(attr)); |
| 1523 | + if (vas != VA_STATUS_SUCCESS) { |
| 1524 | + av_log(avctx, AV_LOG_ERROR, "Failed to query slice " |
| 1525 | + "attributes: %d (%s).\n", vas, vaErrorStr(vas)); |
| 1526 | + return AVERROR_EXTERNAL; |
| 1527 | + } |
| 1528 | + max_slices = attr[0].value; |
| 1529 | + slice_structure = attr[1].value; |
| 1530 | + if (max_slices == VA_ATTRIB_NOT_SUPPORTED || |
| 1531 | + slice_structure == VA_ATTRIB_NOT_SUPPORTED) { |
| 1532 | + av_log(avctx, AV_LOG_ERROR, "Driver does not support encoding " |
| 1533 | + "pictures as multiple slices.\n."); |
| 1534 | + return AVERROR(EINVAL); |
| 1535 | + } |
| 1536 | + |
| 1537 | + // For fixed-size slices currently we only support whole rows, making |
| 1538 | + // rectangular slices. This could be extended to arbitrary runs of |
| 1539 | + // blocks, but since slices tend to be a conformance requirement and |
| 1540 | + // most cases (such as broadcast or bluray) want rectangular slices |
| 1541 | + // only it would need to be gated behind another option. |
| 1542 | + if (avctx->slices > ctx->slice_block_rows) { |
| 1543 | + av_log(avctx, AV_LOG_WARNING, "Not enough rows to use " |
| 1544 | + "configured number of slices (%d < %d); using " |
| 1545 | + "maximum.\n", ctx->slice_block_rows, avctx->slices); |
| 1546 | + req_slices = ctx->slice_block_rows; |
| 1547 | + } else { |
| 1548 | + req_slices = avctx->slices; |
| 1549 | + } |
| 1550 | + if (slice_structure & VA_ENC_SLICE_STRUCTURE_ARBITRARY_ROWS || |
| 1551 | + slice_structure & VA_ENC_SLICE_STRUCTURE_ARBITRARY_MACROBLOCKS) { |
| 1552 | + ctx->nb_slices = req_slices; |
| 1553 | + ctx->slice_size = ctx->slice_block_rows / ctx->nb_slices; |
| 1554 | + } else if (slice_structure & VA_ENC_SLICE_STRUCTURE_POWER_OF_TWO_ROWS) { |
| 1555 | + int k; |
| 1556 | + for (k = 1;; k *= 2) { |
| 1557 | + if (2 * k * (req_slices - 1) + 1 >= ctx->slice_block_rows) |
| 1558 | + break; |
| 1559 | + } |
| 1560 | + ctx->nb_slices = (ctx->slice_block_rows + k - 1) / k; |
| 1561 | + ctx->slice_size = k; |
| 1562 | +#if VA_CHECK_VERSION(1, 0, 0) |
| 1563 | + } else if (slice_structure & VA_ENC_SLICE_STRUCTURE_EQUAL_ROWS) { |
| 1564 | + ctx->nb_slices = ctx->slice_block_rows; |
| 1565 | + ctx->slice_size = 1; |
| 1566 | +#endif |
| 1567 | + } else { |
| 1568 | + av_log(avctx, AV_LOG_ERROR, "Driver does not support any usable " |
| 1569 | + "slice structure modes (%#x).\n", slice_structure); |
| 1570 | + return AVERROR(EINVAL); |
| 1571 | + } |
| 1572 | + |
| 1573 | + if (ctx->nb_slices > avctx->slices) { |
| 1574 | + av_log(avctx, AV_LOG_WARNING, "Slice count rounded up to " |
| 1575 | + "%d (from %d) due to driver constraints on slice " |
| 1576 | + "structure.\n", ctx->nb_slices, avctx->slices); |
| 1577 | + } |
| 1578 | + if (ctx->nb_slices > max_slices) { |
| 1579 | + av_log(avctx, AV_LOG_ERROR, "Driver does not support " |
| 1580 | + "encoding with %d slices (max %"PRIu32").\n", |
| 1581 | + ctx->nb_slices, max_slices); |
| 1582 | + return AVERROR(EINVAL); |
| 1583 | + } |
| 1584 | + |
| 1585 | + av_log(avctx, AV_LOG_VERBOSE, "Encoding pictures with %d slices " |
| 1586 | + "(default size %d block rows).\n", |
| 1587 | + ctx->nb_slices, ctx->slice_size); |
| 1588 | + return 0; |
| 1589 | +} |
| 1590 | + |
| 1447 | 1591 | static av_cold int vaapi_encode_init_packed_headers(AVCodecContext *avctx) |
| 1448 | 1592 | { |
| 1449 | 1593 | VAAPIEncodeContext *ctx = avctx->priv_data; |
| @@ -1734,6 +1878,10 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx) · 284 unmodified lines |
| 1734 | 1878 | if (err < 0) |
| 1735 | 1879 | goto fail; |
| 1736 | 1880 | |
| 1881 | + err = vaapi_encode_init_slice_structure(avctx); |
| 1882 | + if (err < 0) |
| 1883 | + goto fail; |
| 1884 | + |
| 1737 | 1885 | err = vaapi_encode_init_packed_headers(avctx); |
| 1738 | 1886 | if (err < 0) |
| 1739 | 1887 | goto fail; |