blob: 9dc3b6489daf134146f62e3f8189ea33336eac58 [file] [log] [blame]
/**************************************************************************
*
* Copyright 2017 Advanced Micro Devices, Inc.
* Copyright 2023 Red Hat Inc.
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sub license, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial portions
* of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
* IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
**************************************************************************/
#include "radv_buffer.h"
#include "radv_cs.h"
#include "radv_debug.h"
#include "radv_device_memory.h"
#include "radv_entrypoints.h"
#include "radv_image_view.h"
#include "radv_physical_device.h"
#include "radv_query.h"
#include "radv_video.h"
#include "ac_vcn_enc.h"
#include "ac_vcn_enc_av1_default_cdf.h"
#define ENC_ALIGNMENT 256
#define RENCODE_V5_FW_INTERFACE_MAJOR_VERSION 1
#define RENCODE_V5_FW_INTERFACE_MINOR_VERSION 3
#define RENCODE_V4_FW_INTERFACE_MAJOR_VERSION 1
#define RENCODE_V4_FW_INTERFACE_MINOR_VERSION 11
#define RENCODE_V3_FW_INTERFACE_MAJOR_VERSION 1
#define RENCODE_V3_FW_INTERFACE_MINOR_VERSION 27
#define RENCODE_V2_FW_INTERFACE_MAJOR_VERSION 1
#define RENCODE_V2_FW_INTERFACE_MINOR_VERSION 20
#define RENCODE_FW_INTERFACE_MAJOR_VERSION 1
#define RENCODE_FW_INTERFACE_MINOR_VERSION 15
#define ENC_ALIGNMENT 256
void
radv_probe_video_encode(struct radv_physical_device *pdev)
{
pdev->video_encode_enabled = false;
if (pdev->info.vcn_ip_version >= VCN_5_0_0) {
pdev->video_encode_enabled = true;
return;
} else if (pdev->info.vcn_ip_version >= VCN_4_0_0) {
if (pdev->info.vcn_enc_major_version != RENCODE_V4_FW_INTERFACE_MAJOR_VERSION)
return;
if (pdev->info.vcn_enc_minor_version < RENCODE_V4_FW_INTERFACE_MINOR_VERSION)
return;
/* VCN 4 FW 1.22 has all the necessary pieces to pass CTS */
if (pdev->info.vcn_enc_minor_version >= 22) {
pdev->video_encode_enabled = true;
return;
}
} else if (pdev->info.vcn_ip_version >= VCN_3_0_0) {
if (pdev->info.vcn_enc_major_version != RENCODE_V3_FW_INTERFACE_MAJOR_VERSION)
return;
if (pdev->info.vcn_enc_minor_version < RENCODE_V3_FW_INTERFACE_MINOR_VERSION)
return;
/* VCN 3 FW 1.33 has all the necessary pieces to pass CTS */
if (pdev->info.vcn_enc_minor_version >= 33) {
pdev->video_encode_enabled = true;
return;
}
} else if (pdev->info.vcn_ip_version >= VCN_2_0_0) {
if (pdev->info.vcn_enc_major_version != RENCODE_V2_FW_INTERFACE_MAJOR_VERSION)
return;
if (pdev->info.vcn_enc_minor_version < RENCODE_V2_FW_INTERFACE_MINOR_VERSION)
return;
/* VCN 2 FW 1.24 has all the necessary pieces to pass CTS */
if (pdev->info.vcn_enc_minor_version >= 24) {
pdev->video_encode_enabled = true;
return;
}
} else {
if (pdev->info.vcn_enc_major_version != RENCODE_FW_INTERFACE_MAJOR_VERSION)
return;
if (pdev->info.vcn_enc_minor_version < RENCODE_FW_INTERFACE_MINOR_VERSION)
return;
}
struct radv_instance *instance = radv_physical_device_instance(pdev);
pdev->video_encode_enabled = !!(instance->perftest_flags & RADV_PERFTEST_VIDEO_ENCODE);
}
void
radv_init_physical_device_encoder(struct radv_physical_device *pdev)
{
if (pdev->info.vcn_ip_version >= VCN_5_0_0) {
pdev->enc_hw_ver = RADV_VIDEO_ENC_HW_5;
pdev->encoder_interface_version = ((RENCODE_V5_FW_INTERFACE_MAJOR_VERSION << RENCODE_IF_MAJOR_VERSION_SHIFT) |
(RENCODE_V5_FW_INTERFACE_MINOR_VERSION << RENCODE_IF_MINOR_VERSION_SHIFT));
} else if (pdev->info.vcn_ip_version >= VCN_4_0_0) {
pdev->enc_hw_ver = RADV_VIDEO_ENC_HW_4;
pdev->encoder_interface_version = ((RENCODE_V4_FW_INTERFACE_MAJOR_VERSION << RENCODE_IF_MAJOR_VERSION_SHIFT) |
(RENCODE_V4_FW_INTERFACE_MINOR_VERSION << RENCODE_IF_MINOR_VERSION_SHIFT));
} else if (pdev->info.vcn_ip_version >= VCN_3_0_0) {
pdev->enc_hw_ver = RADV_VIDEO_ENC_HW_3;
pdev->encoder_interface_version = ((RENCODE_V3_FW_INTERFACE_MAJOR_VERSION << RENCODE_IF_MAJOR_VERSION_SHIFT) |
(RENCODE_V3_FW_INTERFACE_MINOR_VERSION << RENCODE_IF_MINOR_VERSION_SHIFT));
} else if (pdev->info.vcn_ip_version >= VCN_2_0_0) {
pdev->enc_hw_ver = RADV_VIDEO_ENC_HW_2;
pdev->encoder_interface_version = ((RENCODE_V2_FW_INTERFACE_MAJOR_VERSION << RENCODE_IF_MAJOR_VERSION_SHIFT) |
(RENCODE_V2_FW_INTERFACE_MINOR_VERSION << RENCODE_IF_MINOR_VERSION_SHIFT));
} else {
pdev->enc_hw_ver = RADV_VIDEO_ENC_HW_1_2;
pdev->encoder_interface_version = ((RENCODE_FW_INTERFACE_MAJOR_VERSION << RENCODE_IF_MAJOR_VERSION_SHIFT) |
(RENCODE_FW_INTERFACE_MINOR_VERSION << RENCODE_IF_MINOR_VERSION_SHIFT));
}
ac_vcn_enc_init_cmds(&pdev->vcn_enc_cmds, pdev->info.vcn_ip_version);
}
/* to process invalid frame rate */
static void
radv_vcn_enc_invalid_frame_rate(uint32_t *den, uint32_t *num)
{
if (*den == 0 || *num == 0) {
*den = 1;
*num = 30;
}
}
static uint32_t
radv_vcn_per_frame_integer(uint32_t bitrate, uint32_t den, uint32_t num)
{
uint64_t rate_den = (uint64_t)bitrate * (uint64_t)den;
return (uint32_t)(rate_den / num);
}
static uint32_t
radv_vcn_per_frame_frac(uint32_t bitrate, uint32_t den, uint32_t num)
{
uint64_t rate_den = (uint64_t)bitrate * (uint64_t)den;
uint64_t remainder = rate_den % num;
return (uint32_t)((remainder << 32) / num);
}
static void
radv_enc_set_emulation_prevention(struct radv_cmd_buffer *cmd_buffer, bool set)
{
struct radv_enc_state *enc = &cmd_buffer->video.enc;
if (set != enc->emulation_prevention) {
enc->emulation_prevention = set;
enc->num_zeros = 0;
}
}
static uint32_t
radv_enc_value_bits(uint32_t value)
{
uint32_t i = 1;
while (value > 1) {
i++;
value >>= 1;
}
return i;
}
static const unsigned index_to_shifts[4] = {24, 16, 8, 0};
static void
radv_enc_output_one_byte(struct radv_cmd_buffer *cmd_buffer, unsigned char byte)
{
struct radeon_cmdbuf *cs = cmd_buffer->cs;
struct radv_enc_state *enc = &cmd_buffer->video.enc;
if (enc->byte_index == 0)
cs->buf[cs->cdw] = 0;
cs->buf[cs->cdw] |= ((unsigned int)(byte) << index_to_shifts[enc->byte_index]);
enc->byte_index++;
if (enc->byte_index >= 4) {
enc->byte_index = 0;
cs->cdw++;
}
}
static void
radv_enc_emulation_prevention(struct radv_cmd_buffer *cmd_buffer, unsigned char byte)
{
struct radv_enc_state *enc = &cmd_buffer->video.enc;
if (enc->emulation_prevention) {
if ((enc->num_zeros >= 2) && ((byte == 0x00) || (byte == 0x01) || (byte == 0x02) || (byte == 0x03))) {
radv_enc_output_one_byte(cmd_buffer, 0x03);
enc->bits_output += 8;
enc->num_zeros = 0;
}
enc->num_zeros = (byte == 0 ? (enc->num_zeros + 1) : 0);
}
}
static void
radv_enc_code_fixed_bits(struct radv_cmd_buffer *cmd_buffer, unsigned int value, unsigned int num_bits)
{
struct radv_enc_state *enc = &cmd_buffer->video.enc;
unsigned int bits_to_pack = 0;
enc->bits_size += num_bits;
while (num_bits > 0) {
unsigned int value_to_pack = value & (0xffffffff >> (32 - num_bits));
bits_to_pack = num_bits > (32 - enc->bits_in_shifter) ? (32 - enc->bits_in_shifter) : num_bits;
if (bits_to_pack < num_bits)
value_to_pack = value_to_pack >> (num_bits - bits_to_pack);
enc->shifter |= value_to_pack << (32 - enc->bits_in_shifter - bits_to_pack);
num_bits -= bits_to_pack;
enc->bits_in_shifter += bits_to_pack;
while (enc->bits_in_shifter >= 8) {
unsigned char output_byte = (unsigned char)(enc->shifter >> 24);
enc->shifter <<= 8;
radv_enc_emulation_prevention(cmd_buffer, output_byte);
radv_enc_output_one_byte(cmd_buffer, output_byte);
enc->bits_in_shifter -= 8;
enc->bits_output += 8;
}
}
}
static void
radv_enc_reset(struct radv_cmd_buffer *cmd_buffer)
{
struct radv_enc_state *enc = &cmd_buffer->video.enc;
enc->emulation_prevention = false;
enc->shifter = 0;
enc->bits_in_shifter = 0;
enc->bits_output = 0;
enc->num_zeros = 0;
enc->byte_index = 0;
enc->bits_size = 0;
}
static void
radv_enc_byte_align(struct radv_cmd_buffer *cmd_buffer)
{
struct radv_enc_state *enc = &cmd_buffer->video.enc;
unsigned int num_padding_zeros = (32 - enc->bits_in_shifter) % 8;
if (num_padding_zeros > 0)
radv_enc_code_fixed_bits(cmd_buffer, 0, num_padding_zeros);
}
static void
radv_enc_flush_headers(struct radv_cmd_buffer *cmd_buffer)
{
struct radv_enc_state *enc = &cmd_buffer->video.enc;
struct radeon_cmdbuf *cs = cmd_buffer->cs;
if (enc->bits_in_shifter != 0) {
unsigned char output_byte = (unsigned char)(enc->shifter >> 24);
radv_enc_emulation_prevention(cmd_buffer, output_byte);
radv_enc_output_one_byte(cmd_buffer, output_byte);
enc->bits_output += enc->bits_in_shifter;
enc->shifter = 0;
enc->bits_in_shifter = 0;
enc->num_zeros = 0;
}
if (enc->byte_index > 0) {
cs->cdw++;
enc->byte_index = 0;
}
}
static void
radv_enc_code_ue(struct radv_cmd_buffer *cmd_buffer, unsigned int value)
{
unsigned int x = 0;
unsigned int ue_code = value + 1;
value += 1;
while (value) {
value = (value >> 1);
x += 1;
}
if (x > 1)
radv_enc_code_fixed_bits(cmd_buffer, 0, x - 1);
radv_enc_code_fixed_bits(cmd_buffer, ue_code, x);
}
static void
radv_enc_code_se(struct radv_cmd_buffer *cmd_buffer, int value)
{
unsigned int v = 0;
if (value != 0)
v = (value < 0 ? ((unsigned int)(0 - value) << 1) : (((unsigned int)(value) << 1) - 1));
radv_enc_code_ue(cmd_buffer, v);
}
static void
radv_enc_code_ns(struct radv_cmd_buffer *cmd_buffer, uint32_t value, uint32_t max)
{
uint32_t w = 0;
uint32_t m;
uint32_t max_num = max;
assert(value < max);
while (max_num) {
max_num >>= 1;
w++;
}
m = (1 << w) - max;
if (value < m) {
radv_enc_code_fixed_bits(cmd_buffer, value, w - 1);
} else {
uint32_t diff = value - m;
uint32_t out = (((diff >> 1) + m) << 1) | (diff & 0x1);
radv_enc_code_fixed_bits(cmd_buffer, out, w);
}
}
static uint32_t
radv_enc_h264_pic_type(enum StdVideoH264PictureType type)
{
switch (type) {
case STD_VIDEO_H264_PICTURE_TYPE_P:
return RENCODE_PICTURE_TYPE_P;
case STD_VIDEO_H264_PICTURE_TYPE_B:
return RENCODE_PICTURE_TYPE_B;
case STD_VIDEO_H264_PICTURE_TYPE_I:
case STD_VIDEO_H264_PICTURE_TYPE_IDR:
default:
return RENCODE_PICTURE_TYPE_I;
}
}
static uint32_t
radv_enc_h265_pic_type(enum StdVideoH265PictureType type)
{
switch (type) {
case STD_VIDEO_H265_PICTURE_TYPE_P:
return RENCODE_PICTURE_TYPE_P;
case STD_VIDEO_H265_PICTURE_TYPE_B:
return RENCODE_PICTURE_TYPE_B;
case STD_VIDEO_H265_PICTURE_TYPE_I:
case STD_VIDEO_H265_PICTURE_TYPE_IDR:
default:
return RENCODE_PICTURE_TYPE_I;
}
}
#define RADEON_ENC_CS(value) (cmd_buffer->cs->buf[cmd_buffer->cs->cdw++] = (value))
#define RADEON_ENC_BEGIN(cmd) \
{ \
uint32_t *begin = &cmd_buffer->cs->buf[cmd_buffer->cs->cdw++]; \
RADEON_ENC_CS(cmd)
#define RADEON_ENC_END() \
*begin = (&cmd_buffer->cs->buf[cmd_buffer->cs->cdw] - begin) * 4; \
cmd_buffer->video.enc.total_task_size += *begin; \
}
/* this function has to be in pair with AV1 header copy instruction type at the end */
static void
radv_enc_av1_bs_copy_end(struct radv_cmd_buffer *cmd_buffer, uint32_t bits)
{
struct radv_enc_state *enc = &cmd_buffer->video.enc;
assert(bits > 0);
/* it must be dword aligned at the end */
*enc->copy_start = DIV_ROUND_UP(bits, 32) * 4 + 12;
*(enc->copy_start + 2) = bits;
}
/* av1 bitstream instruction type */
static void
radv_enc_av1_bs_instruction_type(struct radv_cmd_buffer *cmd_buffer, uint32_t inst, uint32_t obu_type)
{
struct radeon_cmdbuf *cs = cmd_buffer->cs;
struct radv_enc_state *enc = &cmd_buffer->video.enc;
radv_enc_flush_headers(cmd_buffer);
if (enc->bits_output)
radv_enc_av1_bs_copy_end(cmd_buffer, enc->bits_output);
enc->copy_start = &cs->buf[cs->cdw++];
RADEON_ENC_CS(inst);
if (inst != RENCODE_HEADER_INSTRUCTION_COPY) {
*enc->copy_start = 8;
if (inst == RENCODE_AV1_BITSTREAM_INSTRUCTION_OBU_START) {
*enc->copy_start += 4;
RADEON_ENC_CS(obu_type);
}
} else
RADEON_ENC_CS(0); /* allocate a dword for number of bits */
radv_enc_reset(cmd_buffer);
}
static void
radv_enc_session_info(struct radv_cmd_buffer *cmd_buffer)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
struct radeon_cmdbuf *cs = cmd_buffer->cs;
radv_cs_add_buffer(device->ws, cs, cmd_buffer->video.vid->sessionctx.mem->bo);
uint64_t va = radv_buffer_get_va(cmd_buffer->video.vid->sessionctx.mem->bo);
va += cmd_buffer->video.vid->sessionctx.offset;
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.session_info);
RADEON_ENC_CS(pdev->encoder_interface_version);
RADEON_ENC_CS(va >> 32);
RADEON_ENC_CS(va & 0xffffffff);
if (pdev->enc_hw_ver < RADV_VIDEO_ENC_HW_3)
RADEON_ENC_CS(RENCODE_ENGINE_TYPE_ENCODE);
else
RADEON_ENC_CS(0);
RADEON_ENC_END();
}
static void
radv_enc_task_info(struct radv_cmd_buffer *cmd_buffer, bool feedback)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
struct radeon_cmdbuf *cs = cmd_buffer->cs;
struct radv_enc_state *enc = &cmd_buffer->video.enc;
enc->task_id++;
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.task_info);
enc->p_task_size = &cs->buf[cs->cdw++];
RADEON_ENC_CS(enc->task_id);
RADEON_ENC_CS(feedback ? 1 : 0);
RADEON_ENC_END();
}
static void
radv_enc_session_init(struct radv_cmd_buffer *cmd_buffer, const struct VkVideoEncodeInfoKHR *enc_info)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
struct radv_video_session *vid = cmd_buffer->video.vid;
unsigned alignment_w = 16;
unsigned alignment_h = 16;
if (vid->vk.op == VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR) {
alignment_w = 64;
} else if (vid->vk.op == VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR) {
if (pdev->enc_hw_ver == RADV_VIDEO_ENC_HW_4) {
alignment_w = 64;
} else if (pdev->enc_hw_ver == RADV_VIDEO_ENC_HW_5) {
alignment_w = 8;
alignment_h = 2;
}
}
if (pdev->info.vcn_ip_version == VCN_4_0_2 || pdev->info.vcn_ip_version == VCN_4_0_5 ||
pdev->info.vcn_ip_version == VCN_4_0_6)
vid->enc_session.WA_flags = 1;
uint32_t w = enc_info->srcPictureResource.codedExtent.width;
uint32_t h = enc_info->srcPictureResource.codedExtent.height;
vid->enc_session.aligned_picture_width = align(w, alignment_w);
vid->enc_session.aligned_picture_height = align(h, alignment_h);
vid->enc_session.padding_width = vid->enc_session.aligned_picture_width - w;
vid->enc_session.padding_height = vid->enc_session.aligned_picture_height - h;
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.session_init);
RADEON_ENC_CS(vid->enc_session.encode_standard);
RADEON_ENC_CS(vid->enc_session.aligned_picture_width);
RADEON_ENC_CS(vid->enc_session.aligned_picture_height);
RADEON_ENC_CS(vid->enc_session.padding_width);
RADEON_ENC_CS(vid->enc_session.padding_height);
RADEON_ENC_CS(vid->enc_session.pre_encode_mode);
RADEON_ENC_CS(vid->enc_session.pre_encode_chroma_enabled);
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_3)
RADEON_ENC_CS(vid->enc_session.slice_output_enabled);
RADEON_ENC_CS(vid->enc_session.display_remote);
if (pdev->enc_hw_ver == RADV_VIDEO_ENC_HW_4) {
RADEON_ENC_CS(vid->enc_session.WA_flags);
RADEON_ENC_CS(0);
}
RADEON_ENC_END();
}
static void
radv_enc_layer_control(struct radv_cmd_buffer *cmd_buffer, const rvcn_enc_layer_control_t *rc_layer_control)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.layer_control);
RADEON_ENC_CS(rc_layer_control->max_num_temporal_layers); // max num temporal layesr
RADEON_ENC_CS(rc_layer_control->num_temporal_layers); // num temporal layers
RADEON_ENC_END();
}
static void
radv_enc_layer_select(struct radv_cmd_buffer *cmd_buffer, int tl_idx)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.layer_select);
RADEON_ENC_CS(tl_idx); // temporal layer index
RADEON_ENC_END();
}
static void
radv_enc_slice_control(struct radv_cmd_buffer *cmd_buffer, const struct VkVideoEncodeInfoKHR *enc_info)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
const struct VkVideoEncodeH264PictureInfoKHR *h264_picture_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_H264_PICTURE_INFO_KHR);
uint32_t num_mbs_in_slice;
uint32_t width_in_mbs = DIV_ROUND_UP(enc_info->srcPictureResource.codedExtent.width, 16);
uint32_t height_in_mbs = DIV_ROUND_UP(enc_info->srcPictureResource.codedExtent.height, 16);
num_mbs_in_slice = DIV_ROUND_UP(width_in_mbs * height_in_mbs, h264_picture_info->naluSliceEntryCount);
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.slice_control_h264);
RADEON_ENC_CS(RENCODE_H264_SLICE_CONTROL_MODE_FIXED_MBS); // slice control mode
RADEON_ENC_CS(num_mbs_in_slice); // num mbs per slice
RADEON_ENC_END();
}
static void
radv_enc_spec_misc_h264(struct radv_cmd_buffer *cmd_buffer, const struct VkVideoEncodeInfoKHR *enc_info)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
const struct VkVideoEncodeH264PictureInfoKHR *h264_picture_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_H264_PICTURE_INFO_KHR);
const StdVideoEncodeH264PictureInfo *pic = h264_picture_info->pStdPictureInfo;
const StdVideoH264SequenceParameterSet *sps =
vk_video_find_h264_enc_std_sps(&cmd_buffer->video.params->vk, pic->seq_parameter_set_id);
const StdVideoH264PictureParameterSet *pps =
vk_video_find_h264_enc_std_pps(&cmd_buffer->video.params->vk, pic->pic_parameter_set_id);
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.spec_misc_h264);
RADEON_ENC_CS(pps->flags.constrained_intra_pred_flag); // constrained_intra_pred_flag
RADEON_ENC_CS(pps->flags.entropy_coding_mode_flag); // cabac enable
RADEON_ENC_CS(0); // cabac init idc
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_5)
RADEON_ENC_CS(pps->flags.transform_8x8_mode_flag);
RADEON_ENC_CS(1); // half pel enabled
RADEON_ENC_CS(1); // quarter pel enabled
RADEON_ENC_CS(cmd_buffer->video.vid->vk.h264.profile_idc); // profile_idc
RADEON_ENC_CS(vk_video_get_h264_level(sps->level_idc));
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_3) {
RADEON_ENC_CS(1); // v3 b_picture_enabled
RADEON_ENC_CS(pps->weighted_bipred_idc); // v3 weighted bipred idc
}
RADEON_ENC_END();
}
static void
radv_enc_spec_misc_hevc(struct radv_cmd_buffer *cmd_buffer, const struct VkVideoEncodeInfoKHR *enc_info)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
const struct VkVideoEncodeH265PictureInfoKHR *h265_picture_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_H265_PICTURE_INFO_KHR);
const StdVideoEncodeH265PictureInfo *pic = h265_picture_info->pStdPictureInfo;
const VkVideoEncodeH265NaluSliceSegmentInfoKHR *h265_slice = &h265_picture_info->pNaluSliceSegmentEntries[0];
const StdVideoEncodeH265SliceSegmentHeader *slice = h265_slice->pStdSliceSegmentHeader;
const StdVideoH265SequenceParameterSet *sps =
vk_video_find_h265_enc_std_sps(&cmd_buffer->video.params->vk, pic->pps_seq_parameter_set_id);
const StdVideoH265PictureParameterSet *pps =
vk_video_find_h265_enc_std_pps(&cmd_buffer->video.params->vk, pic->pps_pic_parameter_set_id);
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.spec_misc_hevc);
RADEON_ENC_CS(sps->log2_min_luma_coding_block_size_minus3);
RADEON_ENC_CS(!sps->flags.amp_enabled_flag);
RADEON_ENC_CS(sps->flags.strong_intra_smoothing_enabled_flag);
RADEON_ENC_CS(pps->flags.constrained_intra_pred_flag);
RADEON_ENC_CS(slice->flags.cabac_init_flag);
RADEON_ENC_CS(1); // enc->enc_pic.hevc_spec_misc.half_pel_enabled
RADEON_ENC_CS(1); // enc->enc_pic.hevc_spec_misc.quarter_pel_enabled
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_3) {
RADEON_ENC_CS(!pps->flags.transform_skip_enabled_flag);
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_5)
RADEON_ENC_CS(0);
}
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_2)
RADEON_ENC_CS(pps->flags.cu_qp_delta_enabled_flag);
RADEON_ENC_END();
}
static void
radv_enc_slice_control_hevc(struct radv_cmd_buffer *cmd_buffer, const struct VkVideoEncodeInfoKHR *enc_info)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
const struct VkVideoEncodeH265PictureInfoKHR *h265_picture_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_H265_PICTURE_INFO_KHR);
uint32_t width_in_ctb, height_in_ctb, num_ctbs_in_slice;
width_in_ctb = DIV_ROUND_UP(enc_info->srcPictureResource.codedExtent.width, 64);
height_in_ctb = DIV_ROUND_UP(enc_info->srcPictureResource.codedExtent.height, 64);
num_ctbs_in_slice = DIV_ROUND_UP(width_in_ctb * height_in_ctb, h265_picture_info->naluSliceSegmentEntryCount);
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.slice_control_hevc);
RADEON_ENC_CS(RENCODE_HEVC_SLICE_CONTROL_MODE_FIXED_CTBS);
RADEON_ENC_CS(num_ctbs_in_slice); // num_ctbs_in_slice
RADEON_ENC_CS(num_ctbs_in_slice); // num_ctbs_in_slice_segment
RADEON_ENC_END();
}
static int32_t
radv_enc_av1_get_relative_dist(uint32_t order_hint_bits_minus_1, uint32_t a, uint32_t b)
{
uint32_t diff = a - b;
uint32_t m = 1 << order_hint_bits_minus_1;
diff = (diff & (m - 1)) - (diff & m);
return diff;
}
static bool
radv_enc_av1_skip_mode_allowed(uint32_t order_hint_bits, uint32_t *ref_order_hint, uint32_t curr_order_hint,
uint32_t frames[2])
{
int32_t forward_idx = -1, backward_idx = -1;
uint32_t forward_hint = 0, backward_hint = 0;
for (uint32_t i = 0; i < STD_VIDEO_AV1_REFS_PER_FRAME; i++) {
uint32_t ref_hint = ref_order_hint[i];
int32_t dist = radv_enc_av1_get_relative_dist(order_hint_bits, ref_hint, curr_order_hint);
if (dist < 0) {
if (forward_idx < 0 || radv_enc_av1_get_relative_dist(order_hint_bits, ref_hint, forward_hint) > 0) {
forward_idx = i;
forward_hint = ref_hint;
}
} else if (dist > 0) {
if (backward_idx < 0 || radv_enc_av1_get_relative_dist(order_hint_bits, ref_hint, backward_hint) < 0) {
backward_idx = i;
backward_hint = ref_hint;
}
}
}
if (forward_idx < 0)
return false;
if (backward_idx >= 0) {
frames[0] = MIN2(forward_idx, backward_idx);
frames[1] = MAX2(forward_idx, backward_idx);
return true;
}
int32_t second_forward_idx = -1;
uint32_t second_forward_hint;
for (uint32_t i = 0; i < STD_VIDEO_AV1_REFS_PER_FRAME; i++) {
uint32_t ref_hint = ref_order_hint[i];
if (radv_enc_av1_get_relative_dist(order_hint_bits, ref_hint, forward_hint) < 0) {
if (second_forward_idx < 0 ||
radv_enc_av1_get_relative_dist(order_hint_bits, ref_hint, second_forward_hint) > 0) {
second_forward_idx = i;
second_forward_hint = ref_hint;
}
}
}
if (second_forward_idx < 0)
return false;
frames[0] = MIN2(forward_idx, second_forward_idx);
frames[1] = MAX2(forward_idx, second_forward_idx);
return true;
}
static void
radv_enc_spec_misc_av1(struct radv_cmd_buffer *cmd_buffer, const struct VkVideoEncodeInfoKHR *enc_info)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
struct radv_video_session *vid = cmd_buffer->video.vid;
struct radv_video_session_params *params = cmd_buffer->video.params;
const struct VkVideoEncodeAV1PictureInfoKHR *av1_picture_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_AV1_PICTURE_INFO_KHR);
const StdVideoEncodeAV1PictureInfo *pic = av1_picture_info->pStdPictureInfo;
const StdVideoAV1SequenceHeader *seq = &params->vk.av1_enc.seq_hdr.base;
uint32_t precision = 0;
if (!pic->flags.allow_high_precision_mv)
precision = RENCODE_AV1_MV_PRECISION_DISALLOW_HIGH_PRECISION;
if (pic->flags.force_integer_mv)
precision = RENCODE_AV1_MV_PRECISION_FORCE_INTEGER_MV;
vid->skip_mode_allowed =
seq->flags.enable_order_hint &&
av1_picture_info->predictionMode >= VK_VIDEO_ENCODE_AV1_PREDICTION_MODE_UNIDIRECTIONAL_COMPOUND_KHR;
if (vid->skip_mode_allowed) {
uint32_t skip_frames[2];
uint32_t ref_order_hint[STD_VIDEO_AV1_REFS_PER_FRAME];
for (unsigned i = 0; i < STD_VIDEO_AV1_REFS_PER_FRAME; i++)
ref_order_hint[i] = pic->ref_order_hint[pic->ref_frame_idx[i]];
vid->skip_mode_allowed =
radv_enc_av1_skip_mode_allowed(seq->order_hint_bits_minus_1, ref_order_hint, pic->order_hint, skip_frames);
vid->disallow_skip_mode = !vid->skip_mode_allowed;
/* Skip mode frames must match reference frames */
if (vid->skip_mode_allowed) {
vid->disallow_skip_mode = !pic->flags.skip_mode_present || skip_frames[0] != 0 ||
av1_picture_info->referenceNameSlotIndices[skip_frames[1]] == -1;
}
}
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.spec_misc_av1);
RADEON_ENC_CS(pic->flags.allow_screen_content_tools);
RADEON_ENC_CS(precision);
RADEON_ENC_CS(seq->flags.enable_cdef);
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_5) {
if (seq->flags.enable_cdef) {
RADEON_ENC_CS(pic->pCDEF->cdef_bits);
RADEON_ENC_CS(pic->pCDEF->cdef_damping_minus_3);
for (int i = 0; i < RENCODE_AV1_CDEF_MAX_NUM; i++)
RADEON_ENC_CS(pic->pCDEF->cdef_y_pri_strength[i]);
for (int i = 0; i < RENCODE_AV1_CDEF_MAX_NUM; i++)
RADEON_ENC_CS(pic->pCDEF->cdef_y_sec_strength[i]);
for (int i = 0; i < RENCODE_AV1_CDEF_MAX_NUM; i++)
RADEON_ENC_CS(pic->pCDEF->cdef_uv_pri_strength[i]);
for (int i = 0; i < RENCODE_AV1_CDEF_MAX_NUM; i++)
RADEON_ENC_CS(pic->pCDEF->cdef_uv_sec_strength[i]);
} else {
for (int i = 0; i < (2 + 4 * RENCODE_AV1_CDEF_MAX_NUM); i++)
RADEON_ENC_CS(0);
}
RADEON_ENC_CS(0); // allow intrabc
}
RADEON_ENC_CS(pic->flags.disable_cdf_update);
RADEON_ENC_CS(pic->flags.disable_frame_end_update_cdf);
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_5) {
RADEON_ENC_CS(vid->disallow_skip_mode);
RADEON_ENC_CS(pic->pQuantization ? pic->pQuantization->DeltaQYDc : 0);
RADEON_ENC_CS(pic->pQuantization ? pic->pQuantization->DeltaQUDc : 0);
RADEON_ENC_CS(pic->pQuantization ? pic->pQuantization->DeltaQUAc : 0);
RADEON_ENC_CS(pic->pQuantization ? pic->pQuantization->DeltaQVDc : 0);
RADEON_ENC_CS(pic->pQuantization ? pic->pQuantization->DeltaQVAc : 0);
} else {
RADEON_ENC_CS(vid->tile_config.num_tile_cols * vid->tile_config.num_tile_rows);
}
RADEON_ENC_CS(0); // enable screen content auto detection
RADEON_ENC_CS(0); // screen content frame percentage threshold
if (pdev->enc_hw_ver < RADV_VIDEO_ENC_HW_5) {
RADEON_ENC_CS(0xffffffff);
RADEON_ENC_CS(0xffffffff);
}
RADEON_ENC_END();
}
static void
radv_enc_rc_session_init(struct radv_cmd_buffer *cmd_buffer)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
struct radv_video_session *vid = cmd_buffer->video.vid;
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.rc_session_init);
RADEON_ENC_CS(vid->enc_rate_control_method); // rate_control_method);
RADEON_ENC_CS(vid->enc_vbv_buffer_level); // vbv_buffer_level);
RADEON_ENC_END();
}
static void
radv_enc_rc_layer_init(struct radv_cmd_buffer *cmd_buffer, rvcn_enc_rate_ctl_layer_init_t *layer_init)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.rc_layer_init);
RADEON_ENC_CS(layer_init->target_bit_rate); // target bit rate
RADEON_ENC_CS(layer_init->peak_bit_rate); // peak bit rate
RADEON_ENC_CS(layer_init->frame_rate_num); // frame rate num
RADEON_ENC_CS(layer_init->frame_rate_den); // frame rate dem
RADEON_ENC_CS(layer_init->vbv_buffer_size); // vbv buffer size
RADEON_ENC_CS(layer_init->avg_target_bits_per_picture); // avg target bits per picture
RADEON_ENC_CS(layer_init->peak_bits_per_picture_integer); // peak bit per picture int
RADEON_ENC_CS(layer_init->peak_bits_per_picture_fractional); // peak bit per picture fract
RADEON_ENC_END();
}
static void
radv_enc_deblocking_filter_h264(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeInfoKHR *enc_info)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
const struct VkVideoEncodeH264PictureInfoKHR *h264_picture_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_H264_PICTURE_INFO_KHR);
const VkVideoEncodeH264NaluSliceInfoKHR *h264_slice = &h264_picture_info->pNaluSliceEntries[0];
const StdVideoEncodeH264SliceHeader *slice = h264_slice->pStdSliceHeader;
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.deblocking_filter_h264);
RADEON_ENC_CS(slice->disable_deblocking_filter_idc);
RADEON_ENC_CS(slice->slice_alpha_c0_offset_div2);
RADEON_ENC_CS(slice->slice_beta_offset_div2);
RADEON_ENC_CS(0); // cb qp offset
RADEON_ENC_CS(0); // cr qp offset
RADEON_ENC_END();
}
static void
radv_enc_deblocking_filter_hevc(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeInfoKHR *enc_info)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
const struct VkVideoEncodeH265PictureInfoKHR *h265_picture_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_H265_PICTURE_INFO_KHR);
const StdVideoEncodeH265PictureInfo *pic = h265_picture_info->pStdPictureInfo;
const VkVideoEncodeH265NaluSliceSegmentInfoKHR *h265_slice = &h265_picture_info->pNaluSliceSegmentEntries[0];
const StdVideoEncodeH265SliceSegmentHeader *slice = h265_slice->pStdSliceSegmentHeader;
const StdVideoH265SequenceParameterSet *sps =
vk_video_find_h265_enc_std_sps(&cmd_buffer->video.params->vk, pic->pps_seq_parameter_set_id);
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.deblocking_filter_hevc);
RADEON_ENC_CS(slice->flags.slice_loop_filter_across_slices_enabled_flag);
RADEON_ENC_CS(slice->flags.slice_deblocking_filter_disabled_flag);
RADEON_ENC_CS(slice->slice_beta_offset_div2);
RADEON_ENC_CS(slice->slice_tc_offset_div2);
RADEON_ENC_CS(slice->slice_cb_qp_offset);
RADEON_ENC_CS(slice->slice_cr_qp_offset);
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_2)
RADEON_ENC_CS(!sps->flags.sample_adaptive_offset_enabled_flag);
RADEON_ENC_END();
}
static void
radv_enc_quality_params(struct radv_cmd_buffer *cmd_buffer)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.quality_params);
RADEON_ENC_CS(0);
RADEON_ENC_CS(0);
RADEON_ENC_CS(0);
RADEON_ENC_CS(0);
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_2)
RADEON_ENC_CS(0);
RADEON_ENC_END();
}
static void
radv_enc_latency(struct radv_cmd_buffer *cmd_buffer, VkVideoEncodeTuningModeKHR tuning_mode)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
if (tuning_mode == VK_VIDEO_ENCODE_TUNING_MODE_LOW_LATENCY_KHR ||
tuning_mode == VK_VIDEO_ENCODE_TUNING_MODE_ULTRA_LOW_LATENCY_KHR) {
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.enc_latency);
RADEON_ENC_CS(1000);
RADEON_ENC_END();
}
}
static void
radv_enc_slice_header(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeInfoKHR *enc_info)
{
struct radv_enc_state *enc = &cmd_buffer->video.enc;
uint32_t instruction[RENCODE_SLICE_HEADER_TEMPLATE_MAX_NUM_INSTRUCTIONS] = {0};
uint32_t num_bits[RENCODE_SLICE_HEADER_TEMPLATE_MAX_NUM_INSTRUCTIONS] = {0};
const struct VkVideoEncodeH264PictureInfoKHR *h264_picture_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_H264_PICTURE_INFO_KHR);
int slice_count = h264_picture_info->naluSliceEntryCount;
const StdVideoEncodeH264PictureInfo *pic = h264_picture_info->pStdPictureInfo;
const StdVideoH264SequenceParameterSet *sps =
vk_video_find_h264_enc_std_sps(&cmd_buffer->video.params->vk, pic->seq_parameter_set_id);
const StdVideoH264PictureParameterSet *pps =
vk_video_find_h264_enc_std_pps(&cmd_buffer->video.params->vk, pic->pic_parameter_set_id);
const VkVideoEncodeH264NaluSliceInfoKHR *slice_info = &h264_picture_info->pNaluSliceEntries[0];
unsigned int inst_index = 0;
unsigned int cdw_start = 0;
unsigned int cdw_filled = 0;
unsigned int bits_copied = 0;
assert(slice_count <= 1);
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
struct radeon_cmdbuf *cs = cmd_buffer->cs;
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.slice_header);
radv_enc_reset(cmd_buffer);
radv_enc_set_emulation_prevention(cmd_buffer, false);
cdw_start = cs->cdw;
if (pic->flags.IdrPicFlag)
radv_enc_code_fixed_bits(cmd_buffer, 0x65, 8);
else if (!pic->flags.is_reference)
radv_enc_code_fixed_bits(cmd_buffer, 0x01, 8);
else
radv_enc_code_fixed_bits(cmd_buffer, 0x41, 8);
radv_enc_flush_headers(cmd_buffer);
instruction[inst_index] = RENCODE_HEADER_INSTRUCTION_COPY;
num_bits[inst_index] = enc->bits_output - bits_copied;
bits_copied = enc->bits_output;
inst_index++;
instruction[inst_index] = RENCODE_H264_HEADER_INSTRUCTION_FIRST_MB;
inst_index++;
switch (pic->primary_pic_type) {
case STD_VIDEO_H264_PICTURE_TYPE_I:
case STD_VIDEO_H264_PICTURE_TYPE_IDR:
default:
radv_enc_code_ue(cmd_buffer, 7);
break;
case STD_VIDEO_H264_PICTURE_TYPE_P:
radv_enc_code_ue(cmd_buffer, 5);
break;
case STD_VIDEO_H264_PICTURE_TYPE_B:
radv_enc_code_ue(cmd_buffer, 6);
break;
}
radv_enc_code_ue(cmd_buffer, 0x0);
unsigned int max_frame_num_bits = sps->log2_max_frame_num_minus4 + 4;
radv_enc_code_fixed_bits(cmd_buffer, pic->frame_num % (1 << max_frame_num_bits), max_frame_num_bits);
#if 0
if (enc->enc_pic.h264_enc_params.input_picture_structure !=
RENCODE_H264_PICTURE_STRUCTURE_FRAME) {
radv_enc_code_fixed_bits(cmd_buffer, 0x1, 1);
radv_enc_code_fixed_bits(cmd_buffer,
enc->enc_pic.h264_enc_params.input_picture_structure ==
RENCODE_H264_PICTURE_STRUCTURE_BOTTOM_FIELD
? 1
: 0,
1);
}
#endif
if (pic->flags.IdrPicFlag)
radv_enc_code_ue(cmd_buffer, pic->idr_pic_id);
if (sps->pic_order_cnt_type == STD_VIDEO_H264_POC_TYPE_0) {
unsigned int max_poc_bits = sps->log2_max_pic_order_cnt_lsb_minus4 + 4;
radv_enc_code_fixed_bits(cmd_buffer, pic->PicOrderCnt % (1 << max_poc_bits), max_poc_bits);
}
if (pps->flags.redundant_pic_cnt_present_flag)
radv_enc_code_ue(cmd_buffer, 0);
if (pic->primary_pic_type == STD_VIDEO_H264_PICTURE_TYPE_B) {
radv_enc_code_fixed_bits(cmd_buffer, slice_info->pStdSliceHeader->flags.direct_spatial_mv_pred_flag, 1);
}
const StdVideoEncodeH264ReferenceListsInfo *ref_lists = pic->pRefLists;
/* ref_pic_list_modification() */
if (pic->primary_pic_type != STD_VIDEO_H264_PICTURE_TYPE_IDR &&
pic->primary_pic_type != STD_VIDEO_H264_PICTURE_TYPE_I) {
/* num ref idx active override flag */
radv_enc_code_fixed_bits(cmd_buffer, slice_info->pStdSliceHeader->flags.num_ref_idx_active_override_flag, 1);
if (slice_info->pStdSliceHeader->flags.num_ref_idx_active_override_flag) {
radv_enc_code_ue(cmd_buffer, ref_lists->num_ref_idx_l0_active_minus1);
if (pic->primary_pic_type == STD_VIDEO_H264_PICTURE_TYPE_B)
radv_enc_code_ue(cmd_buffer, ref_lists->num_ref_idx_l1_active_minus1);
}
radv_enc_code_fixed_bits(cmd_buffer, ref_lists->flags.ref_pic_list_modification_flag_l0, 1);
if (ref_lists->flags.ref_pic_list_modification_flag_l0) {
for (unsigned op = 0; op < ref_lists->refList0ModOpCount; op++) {
const StdVideoEncodeH264RefListModEntry *entry = &ref_lists->pRefList0ModOperations[op];
radv_enc_code_ue(cmd_buffer, entry->modification_of_pic_nums_idc);
if (entry->modification_of_pic_nums_idc ==
STD_VIDEO_H264_MODIFICATION_OF_PIC_NUMS_IDC_SHORT_TERM_SUBTRACT ||
entry->modification_of_pic_nums_idc == STD_VIDEO_H264_MODIFICATION_OF_PIC_NUMS_IDC_SHORT_TERM_ADD)
radv_enc_code_ue(cmd_buffer, entry->abs_diff_pic_num_minus1);
else if (entry->modification_of_pic_nums_idc == STD_VIDEO_H264_MODIFICATION_OF_PIC_NUMS_IDC_LONG_TERM)
radv_enc_code_ue(cmd_buffer, entry->long_term_pic_num);
}
}
if (pic->primary_pic_type == STD_VIDEO_H264_PICTURE_TYPE_B) {
radv_enc_code_fixed_bits(cmd_buffer, ref_lists->flags.ref_pic_list_modification_flag_l1, 1);
if (ref_lists->flags.ref_pic_list_modification_flag_l1) {
for (unsigned op = 0; op < ref_lists->refList1ModOpCount; op++) {
const StdVideoEncodeH264RefListModEntry *entry = &ref_lists->pRefList1ModOperations[op];
radv_enc_code_ue(cmd_buffer, entry->modification_of_pic_nums_idc);
if (entry->modification_of_pic_nums_idc ==
STD_VIDEO_H264_MODIFICATION_OF_PIC_NUMS_IDC_SHORT_TERM_SUBTRACT ||
entry->modification_of_pic_nums_idc == STD_VIDEO_H264_MODIFICATION_OF_PIC_NUMS_IDC_SHORT_TERM_ADD)
radv_enc_code_ue(cmd_buffer, entry->abs_diff_pic_num_minus1);
else if (entry->modification_of_pic_nums_idc == STD_VIDEO_H264_MODIFICATION_OF_PIC_NUMS_IDC_LONG_TERM)
radv_enc_code_ue(cmd_buffer, entry->long_term_pic_num);
}
}
}
}
if (pic->flags.IdrPicFlag) {
radv_enc_code_fixed_bits(cmd_buffer, 0x0, 1);
radv_enc_code_fixed_bits(cmd_buffer, pic->flags.long_term_reference_flag, 1); /* long_term_reference_flag */
} else if (pic->flags.is_reference) {
radv_enc_code_fixed_bits(cmd_buffer, ref_lists->refPicMarkingOpCount > 0 ? 1 : 0, 1);
for (unsigned op = 0; op < ref_lists->refPicMarkingOpCount; op++) {
const StdVideoEncodeH264RefPicMarkingEntry *entry = &ref_lists->pRefPicMarkingOperations[op];
radv_enc_code_ue(cmd_buffer, entry->memory_management_control_operation);
if (entry->memory_management_control_operation == STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_UNMARK_SHORT_TERM ||
entry->memory_management_control_operation == STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_MARK_LONG_TERM)
radv_enc_code_ue(cmd_buffer, entry->difference_of_pic_nums_minus1);
if (entry->memory_management_control_operation == STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_UNMARK_LONG_TERM)
radv_enc_code_ue(cmd_buffer, entry->long_term_pic_num);
if (entry->memory_management_control_operation == STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_MARK_LONG_TERM ||
entry->memory_management_control_operation == STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_MARK_CURRENT_AS_LONG_TERM)
radv_enc_code_ue(cmd_buffer, entry->long_term_frame_idx);
if (entry->memory_management_control_operation == STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_SET_MAX_LONG_TERM_INDEX)
radv_enc_code_ue(cmd_buffer, entry->max_long_term_frame_idx_plus1);
if (entry->memory_management_control_operation == STD_VIDEO_H264_MEM_MGMT_CONTROL_OP_END)
break;
}
}
if (pic->primary_pic_type != STD_VIDEO_H264_PICTURE_TYPE_IDR &&
pic->primary_pic_type != STD_VIDEO_H264_PICTURE_TYPE_I && pps->flags.entropy_coding_mode_flag)
radv_enc_code_ue(cmd_buffer, slice_info->pStdSliceHeader->cabac_init_idc);
radv_enc_flush_headers(cmd_buffer);
instruction[inst_index] = RENCODE_HEADER_INSTRUCTION_COPY;
num_bits[inst_index] = enc->bits_output - bits_copied;
bits_copied = enc->bits_output;
inst_index++;
instruction[inst_index] = RENCODE_H264_HEADER_INSTRUCTION_SLICE_QP_DELTA;
inst_index++;
if (pps->flags.deblocking_filter_control_present_flag) {
radv_enc_code_ue(cmd_buffer, slice_info->pStdSliceHeader->disable_deblocking_filter_idc);
if (!slice_info->pStdSliceHeader->disable_deblocking_filter_idc) {
radv_enc_code_se(cmd_buffer, slice_info->pStdSliceHeader->slice_alpha_c0_offset_div2);
radv_enc_code_se(cmd_buffer, slice_info->pStdSliceHeader->slice_beta_offset_div2);
}
}
radv_enc_flush_headers(cmd_buffer);
instruction[inst_index] = RENCODE_HEADER_INSTRUCTION_COPY;
num_bits[inst_index] = enc->bits_output - bits_copied;
bits_copied = enc->bits_output;
inst_index++;
instruction[inst_index] = RENCODE_HEADER_INSTRUCTION_END;
cdw_filled = cs->cdw - cdw_start;
for (int i = 0; i < RENCODE_SLICE_HEADER_TEMPLATE_MAX_TEMPLATE_SIZE_IN_DWORDS - cdw_filled; i++)
RADEON_ENC_CS(0x00000000);
for (int j = 0; j < RENCODE_SLICE_HEADER_TEMPLATE_MAX_NUM_INSTRUCTIONS; j++) {
RADEON_ENC_CS(instruction[j]);
RADEON_ENC_CS(num_bits[j]);
}
RADEON_ENC_END();
}
static unsigned int
radv_enc_hevc_st_ref_pic_set(struct radv_cmd_buffer *cmd_buffer, const StdVideoH265SequenceParameterSet *sps,
const StdVideoH265ShortTermRefPicSet *rps)
{
const StdVideoH265ShortTermRefPicSet *ref_rps;
unsigned num_pic_total_curr = 0;
unsigned int num_short_term_ref_pic_sets = sps->num_short_term_ref_pic_sets;
unsigned int index = num_short_term_ref_pic_sets;
if (index != 0)
radv_enc_code_fixed_bits(cmd_buffer, rps->flags.inter_ref_pic_set_prediction_flag, 0x1);
if (rps->flags.inter_ref_pic_set_prediction_flag) {
/* in the slice case this is always true, but leave here to make spec alignment easier */
if (index == num_short_term_ref_pic_sets)
radv_enc_code_ue(cmd_buffer, rps->delta_idx_minus1);
radv_enc_code_fixed_bits(cmd_buffer, rps->flags.delta_rps_sign, 0x1);
radv_enc_code_ue(cmd_buffer, rps->abs_delta_rps_minus1);
unsigned ref_rps_idx = index - (rps->delta_idx_minus1 + 1);
if (ref_rps_idx == num_short_term_ref_pic_sets) {
ref_rps = rps;
} else {
ref_rps = &sps->pShortTermRefPicSet[ref_rps_idx];
}
for (unsigned i = 0; i <= (ref_rps->num_negative_pics + ref_rps->num_positive_pics); i++) {
radv_enc_code_fixed_bits(cmd_buffer, !!(rps->used_by_curr_pic_flag & (1 << i)), 0x1);
if (!(rps->used_by_curr_pic_flag & (1 << i))) {
radv_enc_code_fixed_bits(cmd_buffer, !!(rps->use_delta_flag & (1 << i)), 0x1);
}
}
} else {
radv_enc_code_ue(cmd_buffer, rps->num_negative_pics);
radv_enc_code_ue(cmd_buffer, rps->num_positive_pics);
for (int i = 0; i < rps->num_negative_pics; i++) {
radv_enc_code_ue(cmd_buffer, rps->delta_poc_s0_minus1[i]);
radv_enc_code_fixed_bits(cmd_buffer, !!(rps->used_by_curr_pic_s0_flag & (1 << i)), 0x1);
if (rps->used_by_curr_pic_s0_flag & (1 << i))
num_pic_total_curr++;
}
for (int i = 0; i < rps->num_positive_pics; i++) {
radv_enc_code_ue(cmd_buffer, rps->delta_poc_s1_minus1[i]);
radv_enc_code_fixed_bits(cmd_buffer, !!(rps->used_by_curr_pic_s1_flag & (1 << i)), 0x1);
if (rps->used_by_curr_pic_s1_flag & (1 << i))
num_pic_total_curr++;
}
}
return num_pic_total_curr;
}
static void
radv_enc_slice_header_hevc(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeInfoKHR *enc_info)
{
struct radv_enc_state *enc = &cmd_buffer->video.enc;
uint32_t instruction[RENCODE_SLICE_HEADER_TEMPLATE_MAX_NUM_INSTRUCTIONS] = {0};
uint32_t num_bits[RENCODE_SLICE_HEADER_TEMPLATE_MAX_NUM_INSTRUCTIONS] = {0};
const struct VkVideoEncodeH265PictureInfoKHR *h265_picture_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_H265_PICTURE_INFO_KHR);
const StdVideoEncodeH265PictureInfo *pic = h265_picture_info->pStdPictureInfo;
const VkVideoEncodeH265NaluSliceSegmentInfoKHR *h265_slice = &h265_picture_info->pNaluSliceSegmentEntries[0];
const StdVideoEncodeH265SliceSegmentHeader *slice = h265_slice->pStdSliceSegmentHeader;
const StdVideoH265SequenceParameterSet *sps =
vk_video_find_h265_enc_std_sps(&cmd_buffer->video.params->vk, pic->pps_seq_parameter_set_id);
const StdVideoH265PictureParameterSet *pps =
vk_video_find_h265_enc_std_pps(&cmd_buffer->video.params->vk, pic->pps_pic_parameter_set_id);
unsigned int inst_index = 0;
unsigned int cdw_start = 0;
unsigned int cdw_filled = 0;
unsigned int bits_copied = 0;
unsigned int num_pic_total_curr = 0;
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
struct radeon_cmdbuf *cs = cmd_buffer->cs;
unsigned nal_unit_type = vk_video_get_h265_nal_unit(pic);
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.slice_header);
radv_enc_reset(cmd_buffer);
radv_enc_set_emulation_prevention(cmd_buffer, false);
cdw_start = cs->cdw;
radv_enc_code_fixed_bits(cmd_buffer, 0x0, 1);
radv_enc_code_fixed_bits(cmd_buffer, nal_unit_type, 6);
radv_enc_code_fixed_bits(cmd_buffer, 0x0, 6);
radv_enc_code_fixed_bits(cmd_buffer, pic->TemporalId + 1, 3);
radv_enc_flush_headers(cmd_buffer);
instruction[inst_index] = RENCODE_HEADER_INSTRUCTION_COPY;
num_bits[inst_index] = enc->bits_output - bits_copied;
bits_copied = enc->bits_output;
inst_index++;
instruction[inst_index] = RENCODE_HEVC_HEADER_INSTRUCTION_FIRST_SLICE;
inst_index++;
if ((nal_unit_type >= 16) && (nal_unit_type <= 23))
radv_enc_code_fixed_bits(cmd_buffer, pic->flags.no_output_of_prior_pics_flag, 1);
radv_enc_code_ue(cmd_buffer, pic->pps_pic_parameter_set_id);
radv_enc_flush_headers(cmd_buffer);
instruction[inst_index] = RENCODE_HEADER_INSTRUCTION_COPY;
num_bits[inst_index] = enc->bits_output - bits_copied;
bits_copied = enc->bits_output;
inst_index++;
instruction[inst_index] = RENCODE_HEVC_HEADER_INSTRUCTION_SLICE_SEGMENT;
inst_index++;
instruction[inst_index] = RENCODE_HEVC_HEADER_INSTRUCTION_DEPENDENT_SLICE_END;
inst_index++;
/* slice_type */
switch (pic->pic_type) {
case STD_VIDEO_H265_PICTURE_TYPE_I:
case STD_VIDEO_H265_PICTURE_TYPE_IDR:
radv_enc_code_ue(cmd_buffer, 0x2);
break;
case STD_VIDEO_H265_PICTURE_TYPE_P:
radv_enc_code_ue(cmd_buffer, 0x1);
break;
case STD_VIDEO_H265_PICTURE_TYPE_B:
radv_enc_code_ue(cmd_buffer, 0x0);
break;
default:
radv_enc_code_ue(cmd_buffer, 0x1);
}
if (pps->flags.output_flag_present_flag)
radv_enc_code_fixed_bits(cmd_buffer, pic->flags.pic_output_flag, 1);
if ((nal_unit_type != 19) && nal_unit_type != 20) {
/* slice_pic_order_cnt_lsb */
unsigned int max_poc_bits = sps->log2_max_pic_order_cnt_lsb_minus4 + 4;
radv_enc_code_fixed_bits(cmd_buffer, pic->PicOrderCntVal % (1 << max_poc_bits), max_poc_bits);
radv_enc_code_fixed_bits(cmd_buffer, pic->flags.short_term_ref_pic_set_sps_flag, 0x1);
if (!pic->flags.short_term_ref_pic_set_sps_flag) {
num_pic_total_curr = radv_enc_hevc_st_ref_pic_set(cmd_buffer, sps, pic->pShortTermRefPicSet);
} else if (sps->num_short_term_ref_pic_sets > 1) {
radv_enc_code_fixed_bits(cmd_buffer, pic->short_term_ref_pic_set_idx,
util_logbase2_ceil(sps->num_short_term_ref_pic_sets));
}
if (sps->flags.long_term_ref_pics_present_flag) {
const StdVideoEncodeH265LongTermRefPics *lt = pic->pLongTermRefPics;
if (sps->num_long_term_ref_pics_sps > 0)
radv_enc_code_ue(cmd_buffer, lt->num_long_term_sps);
radv_enc_code_ue(cmd_buffer, lt->num_long_term_pics);
for (unsigned i = 0; i < lt->num_long_term_sps + lt->num_long_term_pics; i++) {
if (i < lt->num_long_term_sps) {
if (sps->num_long_term_ref_pics_sps > 1)
radv_enc_code_fixed_bits(cmd_buffer, lt->lt_idx_sps[i],
util_logbase2_ceil(sps->num_long_term_ref_pics_sps));
} else {
radv_enc_code_fixed_bits(cmd_buffer, lt->poc_lsb_lt[i], sps->log2_max_pic_order_cnt_lsb_minus4 + 4);
radv_enc_code_fixed_bits(cmd_buffer, lt->used_by_curr_pic_lt_flag & (1 << i), 1);
if (lt->used_by_curr_pic_lt_flag & (1 << i))
num_pic_total_curr++;
}
radv_enc_code_fixed_bits(cmd_buffer, lt->delta_poc_msb_present_flag[i], 1);
if (lt->delta_poc_msb_present_flag[i])
radv_enc_code_ue(cmd_buffer, lt->delta_poc_msb_cycle_lt[i]);
}
}
if (sps->flags.sps_temporal_mvp_enabled_flag)
radv_enc_code_fixed_bits(cmd_buffer, pic->flags.slice_temporal_mvp_enabled_flag, 1);
}
if (sps->flags.sample_adaptive_offset_enabled_flag) {
radv_enc_flush_headers(cmd_buffer);
instruction[inst_index] = RENCODE_HEADER_INSTRUCTION_COPY;
num_bits[inst_index] = enc->bits_output - bits_copied;
bits_copied = enc->bits_output;
inst_index++;
instruction[inst_index] = RENCODE_HEVC_HEADER_INSTRUCTION_SAO_ENABLE;
inst_index++;
}
if ((pic->pic_type == STD_VIDEO_H265_PICTURE_TYPE_P) || (pic->pic_type == STD_VIDEO_H265_PICTURE_TYPE_B)) {
radv_enc_code_fixed_bits(cmd_buffer, slice->flags.num_ref_idx_active_override_flag, 1);
if (slice->flags.num_ref_idx_active_override_flag) {
radv_enc_code_ue(cmd_buffer, pic->pRefLists->num_ref_idx_l0_active_minus1);
if (pic->pic_type == STD_VIDEO_H265_PICTURE_TYPE_B)
radv_enc_code_ue(cmd_buffer, pic->pRefLists->num_ref_idx_l1_active_minus1);
}
if (pps->flags.lists_modification_present_flag && num_pic_total_curr > 1) {
const StdVideoEncodeH265ReferenceListsInfo *rl = pic->pRefLists;
unsigned num_pic_bits = util_logbase2_ceil(num_pic_total_curr);
unsigned num_ref_l0_minus1 = slice->flags.num_ref_idx_active_override_flag
? rl->num_ref_idx_l0_active_minus1
: pps->num_ref_idx_l0_default_active_minus1;
radv_enc_code_fixed_bits(cmd_buffer, rl->flags.ref_pic_list_modification_flag_l0, 1);
for (unsigned i = 0; i <= num_ref_l0_minus1; i++)
radv_enc_code_fixed_bits(cmd_buffer, rl->list_entry_l0[i], num_pic_bits);
if (pic->pic_type == STD_VIDEO_H265_PICTURE_TYPE_B) {
unsigned num_ref_l1_minus1 = slice->flags.num_ref_idx_active_override_flag
? rl->num_ref_idx_l1_active_minus1
: pps->num_ref_idx_l1_default_active_minus1;
radv_enc_code_fixed_bits(cmd_buffer, rl->flags.ref_pic_list_modification_flag_l1, 1);
for (unsigned i = 0; i <= num_ref_l1_minus1; i++)
radv_enc_code_fixed_bits(cmd_buffer, rl->list_entry_l1[i], num_pic_bits);
}
}
if (pic->pic_type == STD_VIDEO_H265_PICTURE_TYPE_B)
radv_enc_code_fixed_bits(cmd_buffer, slice->flags.mvd_l1_zero_flag, 1);
if (pps->flags.cabac_init_present_flag)
radv_enc_code_fixed_bits(cmd_buffer, slice->flags.cabac_init_flag, 1);
if (pic->flags.slice_temporal_mvp_enabled_flag) {
if (pic->pic_type == STD_VIDEO_H265_PICTURE_TYPE_B)
radv_enc_code_fixed_bits(cmd_buffer, slice->flags.collocated_from_l0_flag, 1);
}
radv_enc_code_ue(cmd_buffer, 5 - slice->MaxNumMergeCand);
}
radv_enc_flush_headers(cmd_buffer);
instruction[inst_index] = RENCODE_HEADER_INSTRUCTION_COPY;
num_bits[inst_index] = enc->bits_output - bits_copied;
bits_copied = enc->bits_output;
inst_index++;
instruction[inst_index] = RENCODE_HEVC_HEADER_INSTRUCTION_SLICE_QP_DELTA;
inst_index++;
if (pps->flags.pps_slice_chroma_qp_offsets_present_flag) {
radv_enc_code_se(cmd_buffer, slice->slice_cb_qp_offset);
radv_enc_code_se(cmd_buffer, slice->slice_cr_qp_offset);
}
if (pps->flags.pps_slice_act_qp_offsets_present_flag) {
radv_enc_code_se(cmd_buffer, slice->slice_act_y_qp_offset);
radv_enc_code_se(cmd_buffer, slice->slice_act_cb_qp_offset);
radv_enc_code_se(cmd_buffer, slice->slice_act_cr_qp_offset);
}
if (pps->flags.chroma_qp_offset_list_enabled_flag) {
radv_enc_code_fixed_bits(cmd_buffer, slice->flags.cu_chroma_qp_offset_enabled_flag, 1);
}
if (pps->flags.deblocking_filter_override_enabled_flag) {
radv_enc_code_fixed_bits(cmd_buffer, slice->flags.deblocking_filter_override_flag, 1);
if (slice->flags.deblocking_filter_override_flag) {
radv_enc_code_fixed_bits(cmd_buffer, slice->flags.slice_deblocking_filter_disabled_flag, 1);
if (!slice->flags.slice_deblocking_filter_disabled_flag) {
radv_enc_code_se(cmd_buffer, slice->slice_beta_offset_div2);
radv_enc_code_se(cmd_buffer, slice->slice_tc_offset_div2);
}
}
}
if ((pps->flags.pps_loop_filter_across_slices_enabled_flag) &&
(!slice->flags.slice_deblocking_filter_disabled_flag || slice->flags.slice_sao_luma_flag ||
slice->flags.slice_sao_chroma_flag)) {
if (slice->flags.slice_sao_luma_flag || slice->flags.slice_sao_chroma_flag) {
instruction[inst_index] = RENCODE_HEVC_HEADER_INSTRUCTION_LOOP_FILTER_ACROSS_SLICES_ENABLE;
inst_index++;
} else {
radv_enc_code_fixed_bits(cmd_buffer, slice->flags.slice_loop_filter_across_slices_enabled_flag, 1);
radv_enc_flush_headers(cmd_buffer);
instruction[inst_index] = RENCODE_HEADER_INSTRUCTION_COPY;
num_bits[inst_index] = enc->bits_output - bits_copied;
bits_copied = enc->bits_output;
inst_index++;
}
}
instruction[inst_index] = RENCODE_HEADER_INSTRUCTION_END;
cdw_filled = cs->cdw - cdw_start;
for (int i = 0; i < RENCODE_SLICE_HEADER_TEMPLATE_MAX_TEMPLATE_SIZE_IN_DWORDS - cdw_filled; i++)
RADEON_ENC_CS(0x00000000);
for (int j = 0; j < RENCODE_SLICE_HEADER_TEMPLATE_MAX_NUM_INSTRUCTIONS; j++) {
RADEON_ENC_CS(instruction[j]);
RADEON_ENC_CS(num_bits[j]);
}
RADEON_ENC_END();
}
static void
dpb_image_sizes(struct radv_image *image, uint32_t *luma_pitch, uint32_t *luma_size, uint32_t *chroma_size,
uint32_t *colloc_bytes)
{
uint32_t rec_alignment = 64;
uint32_t aligned_width = align(image->vk.extent.width, rec_alignment);
uint32_t aligned_height = align(image->vk.extent.height, rec_alignment);
uint32_t pitch = align(aligned_width, ENC_ALIGNMENT);
uint32_t aligned_dpb_height = MAX2(256, aligned_height);
*luma_pitch = pitch;
*luma_size = align(pitch * aligned_dpb_height, ENC_ALIGNMENT);
*chroma_size = align(*luma_size / 2, ENC_ALIGNMENT);
if (image->vk.format == VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16 ||
image->vk.format == VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16) {
*luma_size *= 2;
*chroma_size *= 2;
}
*colloc_bytes = (align((aligned_width / 16), 64) / 2) * (aligned_height / 16);
}
static void
radv_enc_ctx(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeInfoKHR *info)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
struct radv_video_session *vid = cmd_buffer->video.vid;
struct radeon_cmdbuf *cs = cmd_buffer->cs;
struct radv_image_view *dpb_iv = NULL;
struct radv_image *dpb = NULL;
uint64_t va = 0;
uint32_t luma_pitch = 0;
int max_ref_slot_idx = 0;
bool is_av1 = vid->vk.op == VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR;
if (info->pSetupReferenceSlot) {
dpb_iv = radv_image_view_from_handle(info->pSetupReferenceSlot->pPictureResource->imageViewBinding);
if (info->pSetupReferenceSlot->slotIndex > max_ref_slot_idx)
max_ref_slot_idx = info->pSetupReferenceSlot->slotIndex;
}
if (info->referenceSlotCount > 0) {
dpb_iv = radv_image_view_from_handle(info->pReferenceSlots[0].pPictureResource->imageViewBinding);
for (unsigned i = 0; i < info->referenceSlotCount; i++) {
if (info->pReferenceSlots[i].slotIndex > max_ref_slot_idx)
max_ref_slot_idx = info->pReferenceSlots[i].slotIndex;
}
}
uint32_t luma_size = 0, chroma_size = 0, colloc_bytes = 0;
if (dpb_iv) {
dpb = dpb_iv->image;
dpb_image_sizes(dpb, &luma_pitch, &luma_size, &chroma_size, &colloc_bytes);
radv_cs_add_buffer(device->ws, cs, dpb->bindings[0].bo);
va = dpb->bindings[0].addr;
}
uint32_t swizzle_mode = 0;
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_4)
swizzle_mode = RENCODE_REC_SWIZZLE_MODE_256B_D;
else if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_2)
swizzle_mode = RENCODE_REC_SWIZZLE_MODE_256B_S;
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.ctx);
RADEON_ENC_CS(va >> 32);
RADEON_ENC_CS(va & 0xffffffff);
RADEON_ENC_CS(swizzle_mode);
RADEON_ENC_CS(luma_pitch); // rec_luma_pitch
RADEON_ENC_CS(luma_pitch); // rec_luma_pitch0); //rec_chromma_pitch
RADEON_ENC_CS(max_ref_slot_idx + 1); // num_reconstructed_pictures
int i;
unsigned offset = 0;
unsigned colloc_buffer_offset = 0;
uint32_t sdb_frame_offset = offset;
if (vid->vk.op == VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR && pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_3) {
colloc_buffer_offset = offset;
offset += colloc_bytes;
} else if (is_av1)
offset += RENCODE_AV1_SDB_FRAME_CONTEXT_SIZE;
for (i = 0; i < max_ref_slot_idx + 1; i++) {
RADEON_ENC_CS(offset);
offset += luma_size;
RADEON_ENC_CS(offset);
offset += chroma_size;
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_4) {
if (is_av1) {
RADEON_ENC_CS(offset); /* unused offset 1 */
offset += RENCODE_AV1_FRAME_CONTEXT_CDF_TABLE_SIZE;
RADEON_ENC_CS(offset); /* unused offset 2 */
offset += RENCODE_AV1_CDEF_ALGORITHM_FRAME_CONTEXT_SIZE;
} else {
RADEON_ENC_CS(0); /* unused offset 1 */
RADEON_ENC_CS(0); /* unused offset 2 */
}
}
}
for (; i < RENCODE_MAX_NUM_RECONSTRUCTED_PICTURES; i++) {
RADEON_ENC_CS(0);
RADEON_ENC_CS(0);
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_4) {
RADEON_ENC_CS(0); /* unused offset 1 */
RADEON_ENC_CS(0); /* unused offset 2 */
}
}
if (pdev->enc_hw_ver == RADV_VIDEO_ENC_HW_3) {
RADEON_ENC_CS(colloc_buffer_offset);
}
RADEON_ENC_CS(0); // enc pic pre encode luma pitch
RADEON_ENC_CS(0); // enc pic pre encode chroma pitch
for (i = 0; i < RENCODE_MAX_NUM_RECONSTRUCTED_PICTURES; i++) {
RADEON_ENC_CS(0); // pre encode luma offset
RADEON_ENC_CS(0); // pre encode chroma offset
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_4) {
RADEON_ENC_CS(0); /* unused offset 1 */
RADEON_ENC_CS(0); /* unused offset 2 */
}
}
if (pdev->enc_hw_ver == RADV_VIDEO_ENC_HW_2) {
RADEON_ENC_CS(0); // enc pic yuv luma offset
RADEON_ENC_CS(0); // enc pic yuv chroma offset
RADEON_ENC_CS(0); // two pass search center map offset
// rgboffsets
RADEON_ENC_CS(0); // red
RADEON_ENC_CS(0); // green
RADEON_ENC_CS(0); // blue
} else if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_3) {
RADEON_ENC_CS(0); // red
RADEON_ENC_CS(0); // green
RADEON_ENC_CS(0); // blue
RADEON_ENC_CS(0); // v3 two pass search center map offset
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_4) {
if (is_av1) {
RADEON_ENC_CS(sdb_frame_offset);
} else {
RADEON_ENC_CS(colloc_buffer_offset);
}
} else {
RADEON_ENC_CS(0);
}
if (pdev->enc_hw_ver == RADV_VIDEO_ENC_HW_3) {
RADEON_ENC_CS(0);
}
}
RADEON_ENC_END();
}
static void
radv_enc_ctx2(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeInfoKHR *info)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
struct radv_video_session *vid = cmd_buffer->video.vid;
uint32_t luma_pitch = 0, luma_size = 0, chroma_size = 0, colloc_bytes = 0;
int max_ref_slot_idx = 0;
const VkVideoPictureResourceInfoKHR *slots[RENCODE_MAX_NUM_RECONSTRUCTED_PICTURES] = {NULL};
if (info->pSetupReferenceSlot) {
max_ref_slot_idx = info->pSetupReferenceSlot->slotIndex;
slots[info->pSetupReferenceSlot->slotIndex] = info->pSetupReferenceSlot->pPictureResource;
} else {
slots[0] = info->pReferenceSlots[0].pPictureResource;
}
for (unsigned i = 0; i < info->referenceSlotCount; i++) {
if (info->pReferenceSlots[i].slotIndex > max_ref_slot_idx)
max_ref_slot_idx = info->pReferenceSlots[i].slotIndex;
slots[info->pReferenceSlots[i].slotIndex] = info->pReferenceSlots[i].pPictureResource;
}
uint64_t va = 0;
if (cmd_buffer->video.vid->ctx.mem) {
va = radv_buffer_get_va(cmd_buffer->video.vid->ctx.mem->bo);
va += cmd_buffer->video.vid->ctx.offset + VCN_ENC_AV1_DEFAULT_CDF_SIZE;
}
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.ctx);
RADEON_ENC_CS(va >> 32);
RADEON_ENC_CS(va & 0xffffffff);
RADEON_ENC_CS(max_ref_slot_idx + 1); // num_reconstructed_pictures
for (int i = 0; i < RENCODE_MAX_NUM_RECONSTRUCTED_PICTURES; i++) {
const VkVideoPictureResourceInfoKHR *res = slots[i];
if (!res) {
for (int j = 0; j < 15; j++)
RADEON_ENC_CS(0);
continue;
}
struct radv_image_view *dpb_iv = radv_image_view_from_handle(res->imageViewBinding);
assert(dpb_iv != NULL);
struct radv_image *dpb_img = dpb_iv->image;
radv_cs_add_buffer(device->ws, cmd_buffer->cs, dpb_img->bindings[0].bo);
dpb_image_sizes(dpb_iv->image, &luma_pitch, &luma_size, &chroma_size, &colloc_bytes);
uint32_t metadata_size = RENCODE_MAX_METADATA_BUFFER_SIZE_PER_FRAME;
if (vid->vk.op == VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR) {
metadata_size += colloc_bytes;
} else if (vid->vk.op == VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR) {
metadata_size += RENCODE_AV1_FRAME_CONTEXT_CDF_TABLE_SIZE;
metadata_size += RENCODE_AV1_CDEF_ALGORITHM_FRAME_CONTEXT_SIZE;
}
uint32_t dpb_array_idx = res->baseArrayLayer + dpb_iv->vk.base_array_layer;
uint64_t luma_va = dpb_img->bindings[0].addr + dpb_array_idx * (luma_size + chroma_size + metadata_size);
uint64_t chroma_va = luma_va + luma_size;
uint64_t fcb_va = chroma_va + chroma_size;
RADEON_ENC_CS(luma_va >> 32);
RADEON_ENC_CS(luma_va & 0xffffffff);
RADEON_ENC_CS(luma_pitch);
RADEON_ENC_CS(chroma_va >> 32);
RADEON_ENC_CS(chroma_va & 0xffffffff);
RADEON_ENC_CS(luma_pitch / 2);
RADEON_ENC_CS(0);
RADEON_ENC_CS(0);
RADEON_ENC_CS(0);
RADEON_ENC_CS(RENCODE_REC_SWIZZLE_MODE_256B_D_VCN5);
RADEON_ENC_CS(fcb_va >> 32);
RADEON_ENC_CS(fcb_va & 0xffffffff);
RADEON_ENC_CS(RENCODE_MAX_METADATA_BUFFER_SIZE_PER_FRAME); // colloc/cdf offset
RADEON_ENC_CS(RENCODE_MAX_METADATA_BUFFER_SIZE_PER_FRAME +
RENCODE_AV1_FRAME_CONTEXT_CDF_TABLE_SIZE); // cdef offset
RADEON_ENC_CS(0); // metadata offset
}
// pre-encode
for (int i = 0; i < RENCODE_MAX_NUM_RECONSTRUCTED_PICTURES * 15; i++)
RADEON_ENC_CS(0);
RADEON_ENC_CS(0);
RADEON_ENC_CS(0);
RADEON_ENC_CS(0);
RADEON_ENC_CS(0);
RADEON_ENC_CS(0);
RADEON_ENC_CS(0);
RADEON_ENC_END();
}
static void
radv_enc_bitstream(struct radv_cmd_buffer *cmd_buffer, struct radv_buffer *buffer, VkDeviceSize offset)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
struct radeon_cmdbuf *cs = cmd_buffer->cs;
uint64_t va = vk_buffer_address(&buffer->vk, offset);
radv_cs_add_buffer(device->ws, cs, buffer->bo);
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.bitstream);
RADEON_ENC_CS(RENCODE_REC_SWIZZLE_MODE_LINEAR);
RADEON_ENC_CS(va >> 32);
RADEON_ENC_CS(va & 0xffffffff);
RADEON_ENC_CS(buffer->vk.size);
RADEON_ENC_CS(0);
RADEON_ENC_END();
}
static void
radv_enc_feedback(struct radv_cmd_buffer *cmd_buffer, uint64_t feedback_query_va)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.feedback);
RADEON_ENC_CS(RENCODE_FEEDBACK_BUFFER_MODE_LINEAR);
RADEON_ENC_CS(feedback_query_va >> 32);
RADEON_ENC_CS(feedback_query_va & 0xffffffff);
RADEON_ENC_CS(16); // buffer_size
RADEON_ENC_CS(40); // data_size
RADEON_ENC_END();
}
static void
radv_enc_intra_refresh(struct radv_cmd_buffer *cmd_buffer)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.intra_refresh);
RADEON_ENC_CS(0); // intra refresh mode
RADEON_ENC_CS(0); // intra ref offset
RADEON_ENC_CS(0); // intra region size
RADEON_ENC_END();
}
static void
radv_enc_rc_per_pic(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeInfoKHR *enc_info,
rvcn_enc_rate_ctl_per_picture_t *per_pic)
{
struct radv_video_session *vid = cmd_buffer->video.vid;
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
unsigned qp = per_pic->qp_i;
if (vid->enc_rate_control_method == RENCODE_RATE_CONTROL_METHOD_NONE && !vid->enc_rate_control_default) {
switch (vid->vk.op) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR: {
const struct VkVideoEncodeH264PictureInfoKHR *h264_picture_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_H264_PICTURE_INFO_KHR);
const VkVideoEncodeH264NaluSliceInfoKHR *h264_slice = &h264_picture_info->pNaluSliceEntries[0];
qp = h264_slice->constantQp;
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR: {
const struct VkVideoEncodeH265PictureInfoKHR *h265_picture_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_H265_PICTURE_INFO_KHR);
const VkVideoEncodeH265NaluSliceSegmentInfoKHR *h265_slice = &h265_picture_info->pNaluSliceSegmentEntries[0];
qp = h265_slice->constantQp;
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR: {
const struct VkVideoEncodeAV1PictureInfoKHR *av1_picture_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_AV1_PICTURE_INFO_KHR);
qp = av1_picture_info->constantQIndex;
break;
}
default:
break;
}
}
uint32_t cmd =
pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_5 ? pdev->vcn_enc_cmds.rc_per_pic : pdev->vcn_enc_cmds.rc_per_pic_ex;
RADEON_ENC_BEGIN(cmd);
RADEON_ENC_CS(qp); // qp_i
RADEON_ENC_CS(qp); // qp_p
RADEON_ENC_CS(qp); // qp_b
RADEON_ENC_CS(per_pic->min_qp_i);
RADEON_ENC_CS(per_pic->max_qp_i);
RADEON_ENC_CS(per_pic->min_qp_p);
RADEON_ENC_CS(per_pic->max_qp_p);
RADEON_ENC_CS(per_pic->min_qp_b);
RADEON_ENC_CS(per_pic->max_qp_b);
RADEON_ENC_CS(per_pic->max_au_size_i);
RADEON_ENC_CS(per_pic->max_au_size_p);
RADEON_ENC_CS(per_pic->max_au_size_b);
RADEON_ENC_CS(per_pic->enabled_filler_data);
RADEON_ENC_CS(per_pic->skip_frame_enable);
RADEON_ENC_CS(per_pic->enforce_hrd);
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_3)
RADEON_ENC_CS(0xFFFFFFFF); // qvbr_quality_level
RADEON_ENC_END();
}
static void
radv_enc_params(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeInfoKHR *enc_info)
{
const struct VkVideoEncodeH264PictureInfoKHR *h264_picture_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_H264_PICTURE_INFO_KHR);
const struct VkVideoEncodeH265PictureInfoKHR *h265_picture_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_H265_PICTURE_INFO_KHR);
const struct VkVideoEncodeAV1PictureInfoKHR *av1_picture_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_AV1_PICTURE_INFO_KHR);
const StdVideoEncodeH264PictureInfo *h264_pic = h264_picture_info ? h264_picture_info->pStdPictureInfo : NULL;
const StdVideoEncodeH265PictureInfo *h265_pic = h265_picture_info ? h265_picture_info->pStdPictureInfo : NULL;
const StdVideoEncodeAV1PictureInfo *av1_pic = av1_picture_info ? av1_picture_info->pStdPictureInfo : NULL;
struct radv_image_view *src_iv = radv_image_view_from_handle(enc_info->srcPictureResource.imageViewBinding);
struct radv_image *src_img = src_iv->image;
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
struct radeon_cmdbuf *cs = cmd_buffer->cs;
uint32_t array_idx = enc_info->srcPictureResource.baseArrayLayer + src_iv->vk.base_array_layer;
uint64_t va = src_img->bindings[0].addr;
uint64_t luma_va = va + src_img->planes[0].surface.u.gfx9.surf_offset +
array_idx * src_img->planes[0].surface.u.gfx9.surf_slice_size;
uint64_t chroma_va = va + src_img->planes[1].surface.u.gfx9.surf_offset +
array_idx * src_img->planes[1].surface.u.gfx9.surf_slice_size;
uint32_t pic_type;
unsigned int slot_idx = 0xffffffff;
unsigned int max_layers = cmd_buffer->video.vid->rc_layer_control.max_num_temporal_layers;
radv_cs_add_buffer(device->ws, cs, src_img->bindings[0].bo);
if (h264_pic) {
switch (h264_pic->primary_pic_type) {
case STD_VIDEO_H264_PICTURE_TYPE_P:
case STD_VIDEO_H264_PICTURE_TYPE_B:
slot_idx = enc_info->pReferenceSlots[0].slotIndex;
break;
default:
break;
}
pic_type = radv_enc_h264_pic_type(h264_pic->primary_pic_type);
radv_enc_layer_select(cmd_buffer, MIN2(h264_pic->temporal_id, max_layers));
} else if (h265_pic) {
switch (h265_pic->pic_type) {
case STD_VIDEO_H265_PICTURE_TYPE_P:
case STD_VIDEO_H265_PICTURE_TYPE_B:
slot_idx = enc_info->pReferenceSlots[0].slotIndex;
break;
default:
break;
}
pic_type = radv_enc_h265_pic_type(h265_pic->pic_type);
radv_enc_layer_select(cmd_buffer, MIN2(h265_pic->TemporalId, max_layers));
} else if (av1_pic) {
switch (av1_pic->frame_type) {
case STD_VIDEO_AV1_FRAME_TYPE_KEY:
case STD_VIDEO_AV1_FRAME_TYPE_INTRA_ONLY:
pic_type = RENCODE_PICTURE_TYPE_I;
break;
default:
if (av1_picture_info->predictionMode >= VK_VIDEO_ENCODE_AV1_PREDICTION_MODE_UNIDIRECTIONAL_COMPOUND_KHR)
pic_type = RENCODE_PICTURE_TYPE_B;
else
pic_type = RENCODE_PICTURE_TYPE_P;
slot_idx = av1_picture_info->referenceNameSlotIndices[0];
break;
}
radv_enc_layer_select(cmd_buffer,
MIN2(av1_pic->pExtensionHeader ? av1_pic->pExtensionHeader->temporal_id : 0, max_layers));
} else {
assert(0);
return;
}
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.enc_params);
RADEON_ENC_CS(pic_type); // pic type
RADEON_ENC_CS(enc_info->dstBufferRange); // allowed max bitstream size
RADEON_ENC_CS(luma_va >> 32);
RADEON_ENC_CS(luma_va & 0xffffffff);
RADEON_ENC_CS(chroma_va >> 32);
RADEON_ENC_CS(chroma_va & 0xffffffff);
RADEON_ENC_CS(src_img->planes[0].surface.u.gfx9.surf_pitch); // luma pitch
RADEON_ENC_CS(src_img->planes[1].surface.u.gfx9.surf_pitch); // chroma pitch
RADEON_ENC_CS(src_img->planes[0].surface.u.gfx9.swizzle_mode); // swizzle mode
if (pdev->enc_hw_ver < RADV_VIDEO_ENC_HW_5)
RADEON_ENC_CS(slot_idx); // ref0_idx
if (enc_info->pSetupReferenceSlot)
RADEON_ENC_CS(enc_info->pSetupReferenceSlot->slotIndex); // reconstructed picture index
else
RADEON_ENC_CS(0);
RADEON_ENC_END();
}
static void
radv_enc_params_h264(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeInfoKHR *enc_info)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
const struct VkVideoEncodeH264PictureInfoKHR *h264_picture_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_H264_PICTURE_INFO_KHR);
assert(h264_picture_info);
const StdVideoEncodeH264PictureInfo *h264_pic = h264_picture_info->pStdPictureInfo;
unsigned slot_idx_0 = 0xffffffff;
unsigned slot_idx_1 = 0xffffffff;
const VkVideoEncodeH264DpbSlotInfoKHR *slot_info_0 = NULL;
const VkVideoEncodeH264DpbSlotInfoKHR *slot_info_1 = NULL;
switch (h264_pic->primary_pic_type) {
case STD_VIDEO_H264_PICTURE_TYPE_P:
slot_idx_0 = enc_info->pReferenceSlots[0].slotIndex;
slot_info_0 = vk_find_struct_const(enc_info->pReferenceSlots[0].pNext, VIDEO_ENCODE_H264_DPB_SLOT_INFO_KHR);
break;
case STD_VIDEO_H264_PICTURE_TYPE_B:
slot_idx_0 = enc_info->pReferenceSlots[0].slotIndex;
slot_idx_1 = enc_info->pReferenceSlots[1].slotIndex;
slot_info_0 = vk_find_struct_const(enc_info->pReferenceSlots[0].pNext, VIDEO_ENCODE_H264_DPB_SLOT_INFO_KHR);
slot_info_1 = vk_find_struct_const(enc_info->pReferenceSlots[1].pNext, VIDEO_ENCODE_H264_DPB_SLOT_INFO_KHR);
break;
default:
break;
}
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.enc_params_h264);
if (pdev->enc_hw_ver < RADV_VIDEO_ENC_HW_3) {
RADEON_ENC_CS(RENCODE_H264_PICTURE_STRUCTURE_FRAME);
RADEON_ENC_CS(RENCODE_H264_INTERLACING_MODE_PROGRESSIVE);
RADEON_ENC_CS(RENCODE_H264_PICTURE_STRUCTURE_FRAME);
RADEON_ENC_CS(0xffffffff); // reference_picture1_index
} else if (pdev->enc_hw_ver < RADV_VIDEO_ENC_HW_5) {
RADEON_ENC_CS(RENCODE_H264_PICTURE_STRUCTURE_FRAME);
RADEON_ENC_CS(h264_pic->PicOrderCnt);
RADEON_ENC_CS(RENCODE_H264_INTERLACING_MODE_PROGRESSIVE);
if (slot_info_0) {
RADEON_ENC_CS(radv_enc_h264_pic_type(slot_info_0->pStdReferenceInfo->primary_pic_type));
RADEON_ENC_CS(slot_info_0->pStdReferenceInfo->flags.used_for_long_term_reference);
RADEON_ENC_CS(RENCODE_H264_PICTURE_STRUCTURE_FRAME);
RADEON_ENC_CS(slot_info_0->pStdReferenceInfo->PicOrderCnt);
} else {
RADEON_ENC_CS(0); // l0 ref pic0 pic_type
RADEON_ENC_CS(0); // l0 ref pic0 is long term
RADEON_ENC_CS(0); // l0 ref pic0 picture structure
RADEON_ENC_CS(0); // l0 ref pic0 pic order cnt
}
RADEON_ENC_CS(0xffffffff); // l0 ref pic1 index
RADEON_ENC_CS(0); // l0 ref pic1 pic_type
RADEON_ENC_CS(0); // l0 ref pic1 is long term
RADEON_ENC_CS(0); // l0 ref pic1 picture structure
RADEON_ENC_CS(0); // l0 ref pic1 pic order cnt
RADEON_ENC_CS(slot_idx_1); // l1 ref pic0 index
if (slot_info_1) {
RADEON_ENC_CS(radv_enc_h264_pic_type(slot_info_1->pStdReferenceInfo->primary_pic_type));
RADEON_ENC_CS(slot_info_1->pStdReferenceInfo->flags.used_for_long_term_reference);
RADEON_ENC_CS(RENCODE_H264_PICTURE_STRUCTURE_FRAME);
RADEON_ENC_CS(slot_info_1->pStdReferenceInfo->PicOrderCnt);
} else {
RADEON_ENC_CS(0); // l1 ref pic0 pic_type
RADEON_ENC_CS(0); // l1 ref pic0 is long term
RADEON_ENC_CS(0); // l1 ref pic0 picture structure
RADEON_ENC_CS(0); // l1 ref pic0 pic order cnt
}
RADEON_ENC_CS(h264_pic->flags.is_reference); // is reference
} else {
// V5
RADEON_ENC_CS(RENCODE_H264_PICTURE_STRUCTURE_FRAME);
RADEON_ENC_CS(h264_pic->PicOrderCnt);
RADEON_ENC_CS(h264_pic->flags.is_reference);
RADEON_ENC_CS(h264_pic->flags.long_term_reference_flag);
RADEON_ENC_CS(RENCODE_H264_INTERLACING_MODE_PROGRESSIVE);
RADEON_ENC_CS(slot_idx_0); // ref_list0[0]
for (int i = 1; i < RENCODE_H264_MAX_REFERENCE_LIST_SIZE; i++)
RADEON_ENC_CS(0);
RADEON_ENC_CS(slot_idx_0 != 0xffffffff ? 1 : 0); // num_active_references_l0
RADEON_ENC_CS(slot_idx_1); // ref_list1[0]
for (int i = 1; i < RENCODE_H264_MAX_REFERENCE_LIST_SIZE; i++)
RADEON_ENC_CS(0);
RADEON_ENC_CS(slot_idx_1 != 0xffffffff ? 1 : 0); // num_active_references_l1
RADEON_ENC_CS(0); // lsm_reference_pictures[0].list
RADEON_ENC_CS(0); // lsm_reference_pictures[0].list_index
RADEON_ENC_CS(1); // lsm_reference_pictures[1].list
RADEON_ENC_CS(0); // lsm_reference_pictures[0].list_index
}
RADEON_ENC_END();
}
static void
radv_enc_params_hevc(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeInfoKHR *enc_info)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
if (pdev->enc_hw_ver < RADV_VIDEO_ENC_HW_5)
return;
const struct VkVideoEncodeH265PictureInfoKHR *h265_picture_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_H265_PICTURE_INFO_KHR);
assert(h265_picture_info);
const StdVideoEncodeH265PictureInfo *h265_pic = h265_picture_info->pStdPictureInfo;
unsigned slot_idx_0 = 0xffffffff;
switch (h265_pic->pic_type) {
case STD_VIDEO_H265_PICTURE_TYPE_P:
slot_idx_0 = enc_info->pReferenceSlots[0].slotIndex;
break;
default:
break;
}
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.enc_params_hevc);
RADEON_ENC_CS(slot_idx_0);
for (int i = 1; i < RENCODE_HEVC_MAX_REFERENCE_LIST_SIZE; i++)
RADEON_ENC_CS(0);
RADEON_ENC_CS(slot_idx_0 != 0xffffffff ? 1 : 0); // num_active_references_l0
RADEON_ENC_CS(0);
RADEON_ENC_END();
}
static void
radv_enc_op_init(struct radv_cmd_buffer *cmd_buffer)
{
RADEON_ENC_BEGIN(RENCODE_IB_OP_INITIALIZE);
RADEON_ENC_END();
}
static void
radv_enc_op_enc(struct radv_cmd_buffer *cmd_buffer)
{
RADEON_ENC_BEGIN(RENCODE_IB_OP_ENCODE);
RADEON_ENC_END();
}
static void
radv_enc_op_init_rc(struct radv_cmd_buffer *cmd_buffer)
{
RADEON_ENC_BEGIN(RENCODE_IB_OP_INIT_RC);
RADEON_ENC_END();
}
static void
radv_enc_op_init_rc_vbv(struct radv_cmd_buffer *cmd_buffer)
{
RADEON_ENC_BEGIN(RENCODE_IB_OP_INIT_RC_VBV_BUFFER_LEVEL);
RADEON_ENC_END();
}
static void
radv_enc_op_preset(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeInfoKHR *enc_info)
{
struct radv_video_session *vid = cmd_buffer->video.vid;
uint32_t preset_mode;
if (vid->enc_preset_mode == RENCODE_PRESET_MODE_QUALITY)
preset_mode = RENCODE_IB_OP_SET_QUALITY_ENCODING_MODE;
else if (vid->enc_preset_mode == RENCODE_PRESET_MODE_BALANCE)
preset_mode = RENCODE_IB_OP_SET_BALANCE_ENCODING_MODE;
else
preset_mode = RENCODE_IB_OP_SET_SPEED_ENCODING_MODE;
switch (vid->vk.op) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR: {
const struct VkVideoEncodeH265PictureInfoKHR *h265_picture_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_H265_PICTURE_INFO_KHR);
const StdVideoEncodeH265PictureInfo *pic = h265_picture_info->pStdPictureInfo;
const StdVideoH265SequenceParameterSet *sps =
vk_video_find_h265_enc_std_sps(&cmd_buffer->video.params->vk, pic->pps_seq_parameter_set_id);
if (sps->flags.sample_adaptive_offset_enabled_flag && vid->enc_preset_mode == RENCODE_PRESET_MODE_SPEED)
preset_mode = RENCODE_IB_OP_SET_BALANCE_ENCODING_MODE;
break;
}
default:
break;
}
RADEON_ENC_BEGIN(preset_mode);
RADEON_ENC_END();
}
static void
radv_enc_input_format(struct radv_cmd_buffer *cmd_buffer)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
struct radv_video_session *vid = cmd_buffer->video.vid;
uint32_t color_bit_depth;
uint32_t color_packing_format;
switch (vid->vk.picture_format) {
case VK_FORMAT_G8_B8R8_2PLANE_420_UNORM:
color_bit_depth = RENCODE_COLOR_BIT_DEPTH_8_BIT;
color_packing_format = RENCODE_COLOR_PACKING_FORMAT_NV12;
break;
case VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16:
color_bit_depth = RENCODE_COLOR_BIT_DEPTH_10_BIT;
color_packing_format = RENCODE_COLOR_PACKING_FORMAT_P010;
break;
default:
assert(0);
return;
}
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.input_format);
RADEON_ENC_CS(0); // input color volume
RADEON_ENC_CS(0); // input color space
RADEON_ENC_CS(RENCODE_COLOR_RANGE_STUDIO); // input color range
RADEON_ENC_CS(0); // input chroma subsampling
RADEON_ENC_CS(0); // input chroma location
RADEON_ENC_CS(color_bit_depth); // input color bit depth
RADEON_ENC_CS(color_packing_format); // input color packing format
RADEON_ENC_END();
}
static void
radv_enc_output_format(struct radv_cmd_buffer *cmd_buffer)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
struct radv_video_session *vid = cmd_buffer->video.vid;
uint32_t color_bit_depth;
switch (vid->vk.op) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
color_bit_depth = RENCODE_COLOR_BIT_DEPTH_8_BIT;
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
if (vid->vk.h265.profile_idc == STD_VIDEO_H265_PROFILE_IDC_MAIN_10)
color_bit_depth = RENCODE_COLOR_BIT_DEPTH_10_BIT;
else
color_bit_depth = RENCODE_COLOR_BIT_DEPTH_8_BIT;
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:
if (cmd_buffer->video.params->vk.av1_enc.seq_hdr.color_config.BitDepth == 10)
color_bit_depth = RENCODE_COLOR_BIT_DEPTH_10_BIT;
else
color_bit_depth = RENCODE_COLOR_BIT_DEPTH_8_BIT;
break;
default:
assert(0);
return;
}
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.output_format);
RADEON_ENC_CS(0); // output color volume
RADEON_ENC_CS(RENCODE_COLOR_RANGE_STUDIO); // output color range
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_5)
RADEON_ENC_CS(0); // output chroma subsampling
RADEON_ENC_CS(0); // output chroma location
RADEON_ENC_CS(color_bit_depth); // output color bit depth
RADEON_ENC_END();
}
static void
radv_enc_headers_h264(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeInfoKHR *enc_info)
{
radv_enc_slice_header(cmd_buffer, enc_info);
radv_enc_params(cmd_buffer, enc_info);
radv_enc_params_h264(cmd_buffer, enc_info);
}
static void
radv_enc_headers_hevc(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeInfoKHR *enc_info)
{
radv_enc_slice_header_hevc(cmd_buffer, enc_info);
radv_enc_params(cmd_buffer, enc_info);
radv_enc_params_hevc(cmd_buffer, enc_info);
}
static void
radv_enc_cdf_default_table(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeInfoKHR *enc_info)
{
struct radeon_cmdbuf *cs = cmd_buffer->cs;
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
const struct VkVideoEncodeAV1PictureInfoKHR *av1_picture_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_AV1_PICTURE_INFO_KHR);
const StdVideoEncodeAV1PictureInfo *av1_pic = av1_picture_info->pStdPictureInfo;
radv_cs_add_buffer(device->ws, cs, cmd_buffer->video.vid->ctx.mem->bo);
uint64_t va = radv_buffer_get_va(cmd_buffer->video.vid->ctx.mem->bo);
va += cmd_buffer->video.vid->ctx.offset;
uint32_t use_cdf_default = (av1_pic->frame_type == STD_VIDEO_AV1_FRAME_TYPE_KEY ||
av1_pic->frame_type == STD_VIDEO_AV1_FRAME_TYPE_INTRA_ONLY ||
av1_pic->frame_type == STD_VIDEO_AV1_FRAME_TYPE_SWITCH ||
av1_pic->primary_ref_frame == STD_VIDEO_AV1_PRIMARY_REF_NONE);
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.cdf_default_table_av1);
RADEON_ENC_CS(use_cdf_default);
if (pdev->enc_hw_ver == RADV_VIDEO_ENC_HW_4) {
RADEON_ENC_CS(va & 0xffffffff);
RADEON_ENC_CS(va >> 32);
} else {
RADEON_ENC_CS(va >> 32);
RADEON_ENC_CS(va & 0xffffffff);
}
RADEON_ENC_END();
}
static void
radv_enc_params_av1(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeInfoKHR *enc_info)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
if (pdev->enc_hw_ver < RADV_VIDEO_ENC_HW_5)
return;
const struct VkVideoEncodeAV1PictureInfoKHR *av1_picture_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_AV1_PICTURE_INFO_KHR);
unsigned slot_idx_0 = 0xffffffff;
unsigned slot_idx_1 = 0xffffffff;
switch (av1_picture_info->predictionMode) {
case VK_VIDEO_ENCODE_AV1_PREDICTION_MODE_SINGLE_REFERENCE_KHR:
slot_idx_0 = 0; /* LAST_FRAME */
break;
case VK_VIDEO_ENCODE_AV1_PREDICTION_MODE_UNIDIRECTIONAL_COMPOUND_KHR:
slot_idx_0 = 0; /* LAST_FRAME */
slot_idx_1 = 3; /* GOLDEN_FRAME */
break;
case VK_VIDEO_ENCODE_AV1_PREDICTION_MODE_BIDIRECTIONAL_COMPOUND_KHR:
slot_idx_0 = 0; /* LAST_FRAME */
slot_idx_1 = 6; /* ALTREF_FRAME */
break;
default:
break;
}
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.enc_params_av1);
for (int i = 0; i < RENCODE_AV1_REFS_PER_FRAME; i++)
RADEON_ENC_CS(av1_picture_info->referenceNameSlotIndices[i]);
RADEON_ENC_CS(slot_idx_0);
RADEON_ENC_CS(slot_idx_1);
RADEON_ENC_END();
}
static void
radv_enc_av1_tile_config(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeInfoKHR *enc_info)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
const struct VkVideoEncodeAV1PictureInfoKHR *av1_picture_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_AV1_PICTURE_INFO_KHR);
const StdVideoEncodeAV1PictureInfo *av1_pic = av1_picture_info->pStdPictureInfo;
struct radv_video_session *vid = cmd_buffer->video.vid;
uint32_t w = vid->enc_session.aligned_picture_width;
uint32_t h = vid->enc_session.aligned_picture_height;
uint32_t sb_w = DIV_ROUND_UP(w, 64);
uint32_t sb_h = DIV_ROUND_UP(h, 64);
vid->tile_config.tile_widths[0] = 0;
vid->tile_config.tile_height[0] = 0;
vid->tile_config.tile_size_bytes_minus_1 = 3;
if (av1_pic->pTileInfo) {
/* 2 cols only supported for width > 4096. */
if (w <= 4096 && av1_pic->pTileInfo->TileCols > 1) {
vid->tile_config.num_tile_cols = 1;
vid->tile_config.num_tile_rows = MIN2(av1_pic->pTileInfo->TileRows * av1_pic->pTileInfo->TileCols, sb_h);
vid->tile_config.uniform_tile_spacing = util_is_power_of_two_or_zero(vid->tile_config.num_tile_rows);
} else {
vid->tile_config.uniform_tile_spacing = av1_pic->pTileInfo->flags.uniform_tile_spacing_flag;
vid->tile_config.num_tile_cols = av1_pic->pTileInfo->TileCols;
vid->tile_config.num_tile_rows = av1_pic->pTileInfo->TileRows;
if (av1_pic->pTileInfo->pWidthInSbsMinus1) {
for (unsigned i = 0; i < av1_pic->pTileInfo->TileCols; i++)
vid->tile_config.tile_widths[i] = av1_pic->pTileInfo->pWidthInSbsMinus1[i] + 1;
}
if (av1_pic->pTileInfo->pHeightInSbsMinus1) {
for (unsigned i = 0; i < av1_pic->pTileInfo->TileRows; i++)
vid->tile_config.tile_height[i] = av1_pic->pTileInfo->pHeightInSbsMinus1[i] + 1;
}
}
vid->tile_config.context_update_tile_id = av1_pic->pTileInfo->context_update_tile_id;
vid->tile_config.context_update_tile_id_mode = vid->tile_config.context_update_tile_id == 0
? RENCODE_AV1_CONTEXT_UPDATE_TILE_ID_MODE_DEFAULT
: RENCODE_AV1_CONTEXT_UPDATE_TILE_ID_MODE_CUSTOMIZED;
} else {
vid->tile_config.num_tile_cols = w > 4096 ? 2 : 1;
uint32_t max_tile_width = DIV_ROUND_UP(w, vid->tile_config.num_tile_cols);
uint32_t max_tile_height = (4096 * 2304) / max_tile_width;
vid->tile_config.num_tile_rows = DIV_ROUND_UP(h, max_tile_height);
vid->tile_config.uniform_tile_spacing = util_is_power_of_two_or_zero(vid->tile_config.num_tile_rows);
vid->tile_config.context_update_tile_id = 0;
vid->tile_config.context_update_tile_id_mode = RENCODE_AV1_CONTEXT_UPDATE_TILE_ID_MODE_DEFAULT;
}
if (vid->tile_config.tile_widths[0] == 0) {
uint32_t tile_w = DIV_ROUND_UP(sb_w, vid->tile_config.num_tile_cols);
if (tile_w * (vid->tile_config.num_tile_cols - 1) >= sb_w) {
tile_w = sb_w / vid->tile_config.num_tile_cols;
vid->tile_config.uniform_tile_spacing = false;
}
for (unsigned i = 0; i < vid->tile_config.num_tile_cols; i++) {
if (i == vid->tile_config.num_tile_cols - 1)
tile_w = sb_w - (i * tile_w);
vid->tile_config.tile_widths[i] = tile_w;
}
}
if (vid->tile_config.tile_height[0] == 0) {
uint32_t tile_h = DIV_ROUND_UP(sb_h, vid->tile_config.num_tile_rows);
if (tile_h * (vid->tile_config.num_tile_rows - 1) >= sb_h) {
tile_h = sb_h / vid->tile_config.num_tile_rows;
vid->tile_config.uniform_tile_spacing = false;
}
for (unsigned i = 0; i < vid->tile_config.num_tile_rows; i++) {
if (i == vid->tile_config.num_tile_rows - 1)
tile_h = sb_h - (i * tile_h);
vid->tile_config.tile_height[i] = tile_h;
}
}
vid->tile_config.num_tile_groups = vid->tile_config.num_tile_cols * vid->tile_config.num_tile_rows;
for (unsigned i = 0; i < vid->tile_config.num_tile_groups; i++) {
vid->tile_config.tile_groups[i].start = i;
vid->tile_config.tile_groups[i].end = i;
}
if (pdev->enc_hw_ver < RADV_VIDEO_ENC_HW_5)
return;
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.tile_config_av1);
RADEON_ENC_CS(vid->tile_config.num_tile_cols);
RADEON_ENC_CS(vid->tile_config.num_tile_rows);
for (int i = 0; i < RENCODE_AV1_TILE_CONFIG_MAX_NUM_COLS; i++)
RADEON_ENC_CS(vid->tile_config.tile_widths[i]);
for (int i = 0; i < RENCODE_AV1_TILE_CONFIG_MAX_NUM_ROWS; i++)
RADEON_ENC_CS(vid->tile_config.tile_height[i]);
RADEON_ENC_CS(vid->tile_config.num_tile_groups);
for (int i = 0; i < RENCODE_AV1_TILE_CONFIG_MAX_NUM_COLS * RENCODE_AV1_TILE_CONFIG_MAX_NUM_ROWS; i++) {
RADEON_ENC_CS(vid->tile_config.tile_groups[i].start);
RADEON_ENC_CS(vid->tile_config.tile_groups[i].end);
}
RADEON_ENC_CS(vid->tile_config.context_update_tile_id_mode);
RADEON_ENC_CS(vid->tile_config.context_update_tile_id);
RADEON_ENC_CS(vid->tile_config.tile_size_bytes_minus_1);
RADEON_ENC_END();
}
static void
radv_enc_av1_obu_header(struct radv_cmd_buffer *cmd_buffer, uint32_t obu_type,
const StdVideoEncodeAV1ExtensionHeader *ext_header)
{
/* obu header () */
/* obu_forbidden_bit */
radv_enc_code_fixed_bits(cmd_buffer, 0, 1);
/* obu_type */
radv_enc_code_fixed_bits(cmd_buffer, obu_type, 4);
/* obu_extension_flag */
radv_enc_code_fixed_bits(cmd_buffer, ext_header ? 1 : 0, 1);
/* obu_has_size_field */
radv_enc_code_fixed_bits(cmd_buffer, 1, 1);
/* obu_reserved_1bit */
radv_enc_code_fixed_bits(cmd_buffer, 0, 1);
if (ext_header) {
radv_enc_code_fixed_bits(cmd_buffer, ext_header->temporal_id, 3);
radv_enc_code_fixed_bits(cmd_buffer, ext_header->spatial_id, 2);
radv_enc_code_fixed_bits(cmd_buffer, 0, 3); /* reserved 3 bits */
}
}
static void
radv_enc_av1_write_delta_q(struct radv_cmd_buffer *cmd_buffer, int32_t q)
{
radv_enc_code_fixed_bits(cmd_buffer, !!q, 1);
if (q)
radv_enc_code_fixed_bits(cmd_buffer, q, 7);
}
static unsigned
radv_enc_av1_tile_log2(unsigned blk_size, unsigned target)
{
unsigned k;
for (k = 0; (blk_size << k) < target; k++)
;
return k;
}
static void
radv_enc_av1_obu_instruction(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeInfoKHR *enc_info)
{
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
struct radv_video_session *vid = cmd_buffer->video.vid;
struct radv_video_session_params *params = cmd_buffer->video.params;
const struct VkVideoEncodeAV1PictureInfoKHR *av1_picture_info =
vk_find_struct_const(enc_info->pNext, VIDEO_ENCODE_AV1_PICTURE_INFO_KHR);
const StdVideoEncodeAV1PictureInfo *av1_pic = av1_picture_info->pStdPictureInfo;
const StdVideoAV1SequenceHeader *seq = &params->vk.av1_enc.seq_hdr.base;
const StdVideoEncodeAV1ExtensionHeader *ext_header =
av1_picture_info->generateObuExtensionHeader ? av1_pic->pExtensionHeader : NULL;
bool frame_is_intra =
av1_pic->frame_type == STD_VIDEO_AV1_FRAME_TYPE_KEY || av1_pic->frame_type == STD_VIDEO_AV1_FRAME_TYPE_INTRA_ONLY;
bool error_resilient_mode = false;
radv_enc_reset(cmd_buffer);
RADEON_ENC_BEGIN(pdev->vcn_enc_cmds.bitstream_instruction_av1);
/* OBU_FRAME_HEADER */
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_OBU_START,
RENCODE_OBU_START_TYPE_FRAME_HEADER);
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_COPY, 0);
radv_enc_av1_obu_header(cmd_buffer, RENCODE_OBU_TYPE_FRAME_HEADER, ext_header);
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_OBU_SIZE, 0);
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_COPY, 0);
/* uncompressed_header() */
if (!seq->flags.reduced_still_picture_header) {
/* show_existing_frame */
radv_enc_code_fixed_bits(cmd_buffer, 0, 1);
/* frame_type */
radv_enc_code_fixed_bits(cmd_buffer, av1_pic->frame_type, 2);
/* show_frame */
radv_enc_code_fixed_bits(cmd_buffer, av1_pic->flags.show_frame, 1);
if (!av1_pic->flags.show_frame)
/* showable_frame */
radv_enc_code_fixed_bits(cmd_buffer, av1_pic->flags.showable_frame, 1);
if ((av1_pic->frame_type == STD_VIDEO_AV1_FRAME_TYPE_SWITCH) ||
(av1_pic->frame_type == STD_VIDEO_AV1_FRAME_TYPE_KEY && av1_pic->flags.show_frame))
error_resilient_mode = true;
else {
/* error_resilient_mode */
radv_enc_code_fixed_bits(cmd_buffer, av1_pic->flags.error_resilient_mode, 1);
error_resilient_mode = av1_pic->flags.error_resilient_mode;
}
}
/* disable_cdf_update */
radv_enc_code_fixed_bits(cmd_buffer, av1_pic->flags.disable_cdf_update, 1);
bool allow_screen_content_tools = false;
if (seq->flags.reduced_still_picture_header || av1_pic->flags.allow_screen_content_tools) {
/* allow_screen_content_tools */
allow_screen_content_tools = /*av1_pic->av1_spec_misc.palette_mode_enable ||*/
av1_pic->flags.force_integer_mv;
radv_enc_code_fixed_bits(cmd_buffer, allow_screen_content_tools ? 1 : 0, 1);
}
if (allow_screen_content_tools)
/* force_integer_mv */
radv_enc_code_fixed_bits(cmd_buffer, av1_pic->flags.force_integer_mv, 1);
if (seq->flags.frame_id_numbers_present_flag)
/* current_frame_id */
radv_enc_code_fixed_bits(cmd_buffer, av1_pic->current_frame_id,
seq->delta_frame_id_length_minus_2 + 2 + seq->additional_frame_id_length_minus_1 + 1);
bool frame_size_override = false;
if (av1_pic->frame_type == STD_VIDEO_AV1_FRAME_TYPE_SWITCH)
frame_size_override = true;
else if (!seq->flags.reduced_still_picture_header) {
/* frame_size_override_flag */
frame_size_override = false;
radv_enc_code_fixed_bits(cmd_buffer, 0, 1);
}
if (seq->flags.enable_order_hint)
radv_enc_code_fixed_bits(cmd_buffer, av1_pic->order_hint, seq->order_hint_bits_minus_1 + 1);
if (!frame_is_intra && !error_resilient_mode) {
/* primary_ref_frame - VCN4 can either use NONE (7) or LAST (0) */
radv_enc_code_fixed_bits(cmd_buffer, av1_pic->primary_ref_frame != 7 ? 0 : 7, 3);
}
if ((av1_pic->frame_type != STD_VIDEO_AV1_FRAME_TYPE_SWITCH) &&
(av1_pic->frame_type != STD_VIDEO_AV1_FRAME_TYPE_KEY || !av1_pic->flags.show_frame))
/* refresh_frame_flags */
radv_enc_code_fixed_bits(cmd_buffer, av1_pic->refresh_frame_flags, 8);
if ((!frame_is_intra || av1_pic->refresh_frame_flags != 0xff) && error_resilient_mode &&
seq->flags.enable_order_hint) {
for (unsigned i = 0; i < STD_VIDEO_AV1_NUM_REF_FRAMES; i++)
/* ref_order_hint */
radv_enc_code_fixed_bits(cmd_buffer, av1_pic->ref_order_hint[i], seq->order_hint_bits_minus_1 + 1);
}
if (frame_is_intra) {
/* render_and_frame_size_different */
radv_enc_code_fixed_bits(cmd_buffer, av1_pic->flags.render_and_frame_size_different, 1);
if (av1_pic->flags.render_and_frame_size_different) {
/* render_width_minus_1 */
radv_enc_code_fixed_bits(cmd_buffer, av1_pic->render_width_minus_1, 16);
/* render_height_minus_1 */
radv_enc_code_fixed_bits(cmd_buffer, av1_pic->render_height_minus_1, 16);
}
if (av1_pic->flags.allow_screen_content_tools && av1_pic->flags.force_integer_mv)
/* allow_intrabc */
radv_enc_code_fixed_bits(cmd_buffer, 0, 1);
} else {
if (seq->flags.enable_order_hint)
/* frame_refs_short_signaling */
radv_enc_code_fixed_bits(cmd_buffer, 0, 1);
for (unsigned i = 0; i < STD_VIDEO_AV1_REFS_PER_FRAME; i++) {
/* ref_frame_idx */
radv_enc_code_fixed_bits(cmd_buffer, av1_pic->ref_frame_idx[i], 3);
if (seq->flags.frame_id_numbers_present_flag)
radv_enc_code_fixed_bits(cmd_buffer, av1_pic->delta_frame_id_minus_1[i],
seq->delta_frame_id_length_minus_2 + 2);
}
if (frame_size_override && !error_resilient_mode)
/* found_ref */
radv_enc_code_fixed_bits(cmd_buffer, 1, 1);
else {
if (frame_size_override) {
/* frame_width_minus_1 */
uint32_t val = vid->enc_session.aligned_picture_width - 1;
uint32_t used_bits = radv_enc_value_bits(val);
radv_enc_code_fixed_bits(cmd_buffer, val, used_bits);
/* frame_height_minus_1 */
val = vid->enc_session.aligned_picture_height - 1;
used_bits = radv_enc_value_bits(val);
radv_enc_code_fixed_bits(cmd_buffer, val, used_bits);
}
/* render_and_frame_size_different */
radv_enc_code_fixed_bits(cmd_buffer, av1_pic->flags.render_and_frame_size_different, 1);
if (av1_pic->flags.render_and_frame_size_different) {
/* render_width_minus_1 */
radv_enc_code_fixed_bits(cmd_buffer, av1_pic->render_width_minus_1, 16);
/* render_height_minus_1 */
radv_enc_code_fixed_bits(cmd_buffer, av1_pic->render_height_minus_1, 16);
}
}
if (!av1_pic->flags.allow_screen_content_tools || !av1_pic->flags.force_integer_mv)
/* allow_high_precision_mv */
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_ALLOW_HIGH_PRECISION_MV, 0);
/* read_interpolation_filter */
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_READ_INTERPOLATION_FILTER, 0);
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_COPY, 0);
/* is_motion_mode_switchable */
radv_enc_code_fixed_bits(cmd_buffer, 0, 1);
}
if (!seq->flags.reduced_still_picture_header && !av1_pic->flags.disable_cdf_update)
/* disable_frame_end_update_cdf */
radv_enc_code_fixed_bits(cmd_buffer, av1_pic->flags.disable_frame_end_update_cdf, 1);
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_5) {
/* tile_info */
uint32_t sb_cols = DIV_ROUND_UP(vid->enc_session.aligned_picture_width, 64);
uint32_t sb_rows = DIV_ROUND_UP(vid->enc_session.aligned_picture_height, 64);
uint32_t min_log2_tile_cols = radv_enc_av1_tile_log2(64, sb_cols);
uint32_t min_log2_tiles = MAX2(min_log2_tile_cols, radv_enc_av1_tile_log2(64 * 36, sb_rows * sb_cols));
uint32_t tile_cols_log2 = radv_enc_av1_tile_log2(1, vid->tile_config.num_tile_cols);
uint32_t tile_rows_log2 = radv_enc_av1_tile_log2(1, vid->tile_config.num_tile_rows);
radv_enc_code_fixed_bits(cmd_buffer, vid->tile_config.uniform_tile_spacing, 1);
if (vid->tile_config.uniform_tile_spacing) {
for (unsigned i = min_log2_tile_cols; i < tile_cols_log2; i++)
radv_enc_code_fixed_bits(cmd_buffer, 1, 1);
radv_enc_code_fixed_bits(cmd_buffer, 0, 1);
for (unsigned i = min_log2_tiles - tile_cols_log2; i < tile_rows_log2; i++)
radv_enc_code_fixed_bits(cmd_buffer, 1, 1);
radv_enc_code_fixed_bits(cmd_buffer, 0, 1);
} else {
uint32_t widest_tile_sb = 0;
uint32_t start_sb = 0;
for (unsigned i = 0; i < vid->tile_config.num_tile_cols; i++) {
uint32_t max_width = MIN2(sb_cols - start_sb, 64);
radv_enc_code_ns(cmd_buffer, vid->tile_config.tile_widths[i] - 1, max_width);
widest_tile_sb = MAX2(vid->tile_config.tile_widths[i], widest_tile_sb);
start_sb += vid->tile_config.tile_widths[i];
}
uint32_t max_tile_area_sb;
if (min_log2_tiles > 0)
max_tile_area_sb = (sb_rows * sb_cols) >> (min_log2_tiles + 1);
else
max_tile_area_sb = sb_rows * sb_cols;
uint32_t max_tile_height_sb = MAX2(max_tile_area_sb / widest_tile_sb, 1);
start_sb = 0;
for (unsigned i = 0; i < vid->tile_config.num_tile_rows; i++) {
uint32_t max_height = MIN2(sb_rows - start_sb, max_tile_height_sb);
radv_enc_code_ns(cmd_buffer, vid->tile_config.tile_height[i] - 1, max_height);
start_sb += vid->tile_config.tile_height[i];
}
}
if (tile_cols_log2 || tile_rows_log2) {
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_V5_AV1_BITSTREAM_INSTRUCTION_CONTEXT_UPDATE_TILE_ID, 0);
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_COPY, 0);
radv_enc_code_fixed_bits(cmd_buffer, vid->tile_config.tile_size_bytes_minus_1, 2);
}
/* quantization_params */
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_V5_AV1_BITSTREAM_INSTRUCTION_BASE_Q_IDX, 0);
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_COPY, 0);
radv_enc_av1_write_delta_q(cmd_buffer, av1_pic->pQuantization ? av1_pic->pQuantization->DeltaQYDc : 0);
if (seq->pColorConfig && seq->pColorConfig->flags.separate_uv_delta_q)
radv_enc_code_fixed_bits(cmd_buffer, 1, 1);
radv_enc_av1_write_delta_q(cmd_buffer, av1_pic->pQuantization ? av1_pic->pQuantization->DeltaQUDc : 0);
radv_enc_av1_write_delta_q(cmd_buffer, av1_pic->pQuantization ? av1_pic->pQuantization->DeltaQUAc : 0);
if (seq->pColorConfig && seq->pColorConfig->flags.separate_uv_delta_q) {
radv_enc_av1_write_delta_q(cmd_buffer, av1_pic->pQuantization ? av1_pic->pQuantization->DeltaQVDc : 0);
radv_enc_av1_write_delta_q(cmd_buffer, av1_pic->pQuantization ? av1_pic->pQuantization->DeltaQVAc : 0);
}
/* using qmatrix */
radv_enc_code_fixed_bits(cmd_buffer, 0, 1);
} else {
/* tile_info */
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_V4_AV1_BITSTREAM_INSTRUCTION_TILE_INFO, 0);
/* quantization_params */
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_V4_AV1_BITSTREAM_INSTRUCTION_QUANTIZATION_PARAMS, 0);
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_COPY, 0);
}
/* segmentation_enable */
radv_enc_code_fixed_bits(cmd_buffer, 0, 1);
/* delta_q_params */
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_DELTA_Q_PARAMS, 0);
/* delta_lf_params */
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_DELTA_LF_PARAMS, 0);
/* loop_filter_params */
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_LOOP_FILTER_PARAMS, 0);
/* cdef_params */
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_CDEF_PARAMS, 0);
/* lr_params */
/* read_tx_mode */
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_READ_TX_MODE, 0);
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_COPY, 0);
if (!frame_is_intra) {
/* reference_select */
bool compound =
av1_picture_info->predictionMode >= VK_VIDEO_ENCODE_AV1_PREDICTION_MODE_UNIDIRECTIONAL_COMPOUND_KHR;
radv_enc_code_fixed_bits(cmd_buffer, compound, 1);
}
if (vid->skip_mode_allowed)
/* skip_mode_present */
radv_enc_code_fixed_bits(cmd_buffer, !vid->disallow_skip_mode, 1);
/* reduced_tx_set */
radv_enc_code_fixed_bits(cmd_buffer, 0, 1);
if (!frame_is_intra)
for (uint32_t ref = STD_VIDEO_AV1_REFERENCE_NAME_LAST_FRAME; ref <= STD_VIDEO_AV1_REFERENCE_NAME_ALTREF_FRAME;
ref++)
/* is_global */
radv_enc_code_fixed_bits(cmd_buffer, 0, 1);
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_OBU_END, 0);
/* OBU_TILE_GROUP */
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_OBU_START,
RENCODE_OBU_START_TYPE_TILE_GROUP);
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_COPY, 0);
radv_enc_av1_obu_header(cmd_buffer, RENCODE_OBU_TYPE_TILE_GROUP, ext_header);
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_OBU_SIZE, 0);
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_TILE_GROUP_OBU, 0);
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_OBU_END, 0);
radv_enc_av1_bs_instruction_type(cmd_buffer, RENCODE_AV1_BITSTREAM_INSTRUCTION_END, 0);
RADEON_ENC_END();
}
static void
radv_enc_headers_av1(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeInfoKHR *enc_info)
{
radv_enc_av1_obu_instruction(cmd_buffer, enc_info);
radv_enc_params(cmd_buffer, enc_info);
radv_enc_params_av1(cmd_buffer, enc_info);
radv_enc_cdf_default_table(cmd_buffer, enc_info);
}
static void
begin(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeInfoKHR *enc_info)
{
struct radv_video_session *vid = cmd_buffer->video.vid;
radv_enc_op_init(cmd_buffer);
radv_enc_session_init(cmd_buffer, enc_info);
radv_enc_layer_control(cmd_buffer, &vid->rc_layer_control);
radv_enc_rc_session_init(cmd_buffer);
radv_enc_quality_params(cmd_buffer);
radv_enc_latency(cmd_buffer, vid->vk.enc_usage.tuning_mode);
// temporal layers init
unsigned i = 0;
do {
radv_enc_layer_select(cmd_buffer, i);
radv_enc_rc_layer_init(cmd_buffer, &vid->rc_layer_init[i]);
radv_enc_layer_select(cmd_buffer, i);
radv_enc_rc_per_pic(cmd_buffer, enc_info, &vid->rc_per_pic[i]);
} while (++i < vid->rc_layer_control.num_temporal_layers);
radv_enc_op_init_rc(cmd_buffer);
radv_enc_op_init_rc_vbv(cmd_buffer);
}
static void
radv_vcn_encode_video(struct radv_cmd_buffer *cmd_buffer, const VkVideoEncodeInfoKHR *enc_info)
{
VK_FROM_HANDLE(radv_buffer, dst_buffer, enc_info->dstBuffer);
struct radv_video_session *vid = cmd_buffer->video.vid;
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
const struct radv_physical_device *pdev = radv_device_physical(device);
struct radv_enc_state *enc = &cmd_buffer->video.enc;
uint64_t feedback_query_va;
switch (vid->vk.op) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:
break;
default:
assert(0);
return;
}
radeon_check_space(device->ws, cmd_buffer->cs, 1600);
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_4)
radv_vcn_sq_header(cmd_buffer->cs, &cmd_buffer->video.sq, RADEON_VCN_ENGINE_TYPE_ENCODE, false);
const struct VkVideoInlineQueryInfoKHR *inline_queries = NULL;
if (vid->vk.flags & VK_VIDEO_SESSION_CREATE_INLINE_QUERIES_BIT_KHR) {
inline_queries = vk_find_struct_const(enc_info->pNext, VIDEO_INLINE_QUERY_INFO_KHR);
if (inline_queries) {
VK_FROM_HANDLE(radv_query_pool, pool, inline_queries->queryPool);
radv_cs_add_buffer(device->ws, cmd_buffer->cs, pool->bo);
feedback_query_va = radv_buffer_get_va(pool->bo);
feedback_query_va += pool->stride * inline_queries->firstQuery;
}
}
if (!inline_queries)
feedback_query_va = cmd_buffer->video.feedback_query_va;
// before encode
// session info
radv_enc_session_info(cmd_buffer);
cmd_buffer->video.enc.total_task_size = 0;
// task info
radv_enc_task_info(cmd_buffer, true);
if (vid->enc_need_begin) {
begin(cmd_buffer, enc_info);
vid->enc_need_begin = false;
} else {
// temporal layers init
unsigned i = 0;
do {
if (vid->enc_need_rate_control) {
radv_enc_layer_select(cmd_buffer, i);
radv_enc_rc_layer_init(cmd_buffer, &vid->rc_layer_init[i]);
vid->enc_need_rate_control = false;
}
if (vid->session_initialized) {
radv_enc_layer_select(cmd_buffer, i);
radv_enc_rc_per_pic(cmd_buffer, enc_info, &vid->rc_per_pic[i]);
}
} while (++i < vid->rc_layer_control.num_temporal_layers);
}
if (vid->vk.op == VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR) {
radv_enc_slice_control(cmd_buffer, enc_info);
radv_enc_spec_misc_h264(cmd_buffer, enc_info);
radv_enc_deblocking_filter_h264(cmd_buffer, enc_info);
radv_enc_headers_h264(cmd_buffer, enc_info);
} else if (vid->vk.op == VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR) {
radv_enc_slice_control_hevc(cmd_buffer, enc_info);
radv_enc_spec_misc_hevc(cmd_buffer, enc_info);
radv_enc_deblocking_filter_hevc(cmd_buffer, enc_info);
radv_enc_headers_hevc(cmd_buffer, enc_info);
} else if (vid->vk.op == VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR) {
radv_enc_av1_tile_config(cmd_buffer, enc_info);
radv_enc_spec_misc_av1(cmd_buffer, enc_info);
radv_enc_headers_av1(cmd_buffer, enc_info);
}
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_5)
radv_enc_ctx2(cmd_buffer, enc_info);
else
radv_enc_ctx(cmd_buffer, enc_info);
// bitstream
radv_enc_bitstream(cmd_buffer, dst_buffer, enc_info->dstBufferOffset);
// feedback
radv_enc_feedback(cmd_buffer, feedback_query_va);
// v2 encode statistics
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_2) {
}
// intra_refresh
radv_enc_intra_refresh(cmd_buffer);
// v2 input format
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_2) {
radv_enc_input_format(cmd_buffer);
radv_enc_output_format(cmd_buffer);
}
// v2 output format
// op_preset
radv_enc_op_preset(cmd_buffer, enc_info);
// op_enc
radv_enc_op_enc(cmd_buffer);
*enc->p_task_size = enc->total_task_size;
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_4)
radv_vcn_sq_tail(cmd_buffer->cs, &cmd_buffer->video.sq);
}
static void
set_rate_control_defaults(struct radv_video_session *vid)
{
uint32_t frame_rate_den = 1, frame_rate_num = 30;
vid->enc_rate_control_default = true;
vid->enc_rate_control_method = RENCODE_RATE_CONTROL_METHOD_NONE;
vid->enc_vbv_buffer_level = 64;
vid->rc_layer_control.num_temporal_layers = 1;
vid->rc_layer_control.max_num_temporal_layers = 1;
vid->rc_per_pic[0].qp_i = 26;
vid->rc_per_pic[0].qp_p = 26;
vid->rc_per_pic[0].qp_b = 26;
vid->rc_per_pic[0].min_qp_i = 0;
vid->rc_per_pic[0].max_qp_i = 51;
vid->rc_per_pic[0].min_qp_p = 0;
vid->rc_per_pic[0].max_qp_p = 51;
vid->rc_per_pic[0].min_qp_b = 0;
vid->rc_per_pic[0].max_qp_b = 51;
vid->rc_per_pic[0].max_au_size_i = 0;
vid->rc_per_pic[0].max_au_size_p = 0;
vid->rc_per_pic[0].max_au_size_b = 0;
vid->rc_per_pic[0].enabled_filler_data = 1;
vid->rc_per_pic[0].skip_frame_enable = 0;
vid->rc_per_pic[0].enforce_hrd = 1;
vid->rc_layer_init[0].frame_rate_den = frame_rate_den;
vid->rc_layer_init[0].frame_rate_num = frame_rate_num;
vid->rc_layer_init[0].vbv_buffer_size = 20000000; // rate_control->virtualBufferSizeInMs;
vid->rc_layer_init[0].target_bit_rate = 16000;
vid->rc_layer_init[0].peak_bit_rate = 32000;
vid->rc_layer_init[0].avg_target_bits_per_picture =
radv_vcn_per_frame_integer(16000, frame_rate_den, frame_rate_num);
vid->rc_layer_init[0].peak_bits_per_picture_integer =
radv_vcn_per_frame_integer(32000, frame_rate_den, frame_rate_num);
vid->rc_layer_init[0].peak_bits_per_picture_fractional =
radv_vcn_per_frame_frac(32000, frame_rate_den, frame_rate_num);
return;
}
void
radv_video_enc_control_video_coding(struct radv_cmd_buffer *cmd_buffer, const VkVideoCodingControlInfoKHR *control_info)
{
struct radv_video_session *vid = cmd_buffer->video.vid;
switch (vid->vk.op) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:
if (control_info->flags & VK_VIDEO_CODING_CONTROL_RESET_BIT_KHR) {
struct radv_device *device = radv_cmd_buffer_device(cmd_buffer);
uint8_t *cdfptr = radv_buffer_map(device->ws, vid->ctx.mem->bo);
cdfptr += vid->ctx.offset;
memcpy(cdfptr, rvcn_av1_cdf_default_table, VCN_ENC_AV1_DEFAULT_CDF_SIZE);
device->ws->buffer_unmap(device->ws, vid->ctx.mem->bo, false);
}
break;
default:
unreachable("Unsupported\n");
}
if (control_info->flags & VK_VIDEO_CODING_CONTROL_RESET_BIT_KHR) {
set_rate_control_defaults(vid);
vid->enc_need_begin = true;
vid->session_initialized = true;
}
if (control_info->flags & VK_VIDEO_CODING_CONTROL_ENCODE_RATE_CONTROL_BIT_KHR) {
const VkVideoEncodeRateControlInfoKHR *rate_control = (VkVideoEncodeRateControlInfoKHR *)vk_find_struct_const(
control_info->pNext, VIDEO_ENCODE_RATE_CONTROL_INFO_KHR);
assert(rate_control);
const VkVideoEncodeH264RateControlInfoKHR *h264_rate_control =
(VkVideoEncodeH264RateControlInfoKHR *)vk_find_struct_const(rate_control->pNext,
VIDEO_ENCODE_H264_RATE_CONTROL_INFO_KHR);
const VkVideoEncodeH265RateControlInfoKHR *h265_rate_control =
(VkVideoEncodeH265RateControlInfoKHR *)vk_find_struct_const(rate_control->pNext,
VIDEO_ENCODE_H265_RATE_CONTROL_INFO_KHR);
const VkVideoEncodeAV1RateControlInfoKHR *av1_rate_control =
(VkVideoEncodeAV1RateControlInfoKHR *)vk_find_struct_const(rate_control->pNext,
VIDEO_ENCODE_AV1_RATE_CONTROL_INFO_KHR);
uint32_t rate_control_method = RENCODE_RATE_CONTROL_METHOD_NONE;
vid->enc_rate_control_default = false;
if (rate_control->rateControlMode == VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DEFAULT_KHR)
set_rate_control_defaults(vid);
else if (rate_control->rateControlMode == VK_VIDEO_ENCODE_RATE_CONTROL_MODE_CBR_BIT_KHR)
rate_control_method = RENCODE_RATE_CONTROL_METHOD_CBR;
else if (rate_control->rateControlMode == VK_VIDEO_ENCODE_RATE_CONTROL_MODE_VBR_BIT_KHR)
rate_control_method = RENCODE_RATE_CONTROL_METHOD_PEAK_CONSTRAINED_VBR;
vid->enc_need_rate_control = true;
if (vid->enc_rate_control_method != rate_control_method)
vid->enc_need_begin = true;
vid->enc_rate_control_method = rate_control_method;
if (rate_control->rateControlMode == VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DEFAULT_KHR)
return;
if (h264_rate_control) {
vid->rc_layer_control.max_num_temporal_layers = h264_rate_control->temporalLayerCount;
vid->rc_layer_control.num_temporal_layers = h264_rate_control->temporalLayerCount;
} else if (h265_rate_control) {
vid->rc_layer_control.max_num_temporal_layers = h265_rate_control->subLayerCount;
vid->rc_layer_control.num_temporal_layers = h265_rate_control->subLayerCount;
} else if (av1_rate_control) {
vid->rc_layer_control.max_num_temporal_layers = av1_rate_control->temporalLayerCount;
vid->rc_layer_control.num_temporal_layers = av1_rate_control->temporalLayerCount;
}
for (unsigned l = 0; l < rate_control->layerCount; l++) {
const VkVideoEncodeRateControlLayerInfoKHR *layer = &rate_control->pLayers[l];
const VkVideoEncodeH264RateControlLayerInfoKHR *h264_layer =
(VkVideoEncodeH264RateControlLayerInfoKHR *)vk_find_struct_const(
layer->pNext, VIDEO_ENCODE_H264_RATE_CONTROL_LAYER_INFO_KHR);
const VkVideoEncodeH265RateControlLayerInfoKHR *h265_layer =
(VkVideoEncodeH265RateControlLayerInfoKHR *)vk_find_struct_const(
layer->pNext, VIDEO_ENCODE_H265_RATE_CONTROL_LAYER_INFO_KHR);
const VkVideoEncodeAV1RateControlLayerInfoKHR *av1_layer =
(VkVideoEncodeAV1RateControlLayerInfoKHR *)vk_find_struct_const(
layer->pNext, VIDEO_ENCODE_AV1_RATE_CONTROL_LAYER_INFO_KHR);
uint32_t frame_rate_den, frame_rate_num;
vid->rc_layer_init[l].target_bit_rate = layer->averageBitrate;
vid->rc_layer_init[l].peak_bit_rate = layer->maxBitrate;
frame_rate_den = layer->frameRateDenominator;
frame_rate_num = layer->frameRateNumerator;
radv_vcn_enc_invalid_frame_rate(&frame_rate_den, &frame_rate_num);
vid->rc_layer_init[l].frame_rate_den = frame_rate_den;
vid->rc_layer_init[l].frame_rate_num = frame_rate_num;
vid->rc_layer_init[l].vbv_buffer_size = (rate_control->virtualBufferSizeInMs / 1000.) * layer->averageBitrate;
vid->rc_layer_init[l].avg_target_bits_per_picture =
radv_vcn_per_frame_integer(layer->averageBitrate, frame_rate_den, frame_rate_num);
vid->rc_layer_init[l].peak_bits_per_picture_integer =
radv_vcn_per_frame_integer(layer->maxBitrate, frame_rate_den, frame_rate_num);
vid->rc_layer_init[l].peak_bits_per_picture_fractional =
radv_vcn_per_frame_frac(layer->maxBitrate, frame_rate_den, frame_rate_num);
if (h264_layer) {
vid->rc_per_pic[l].min_qp_i = h264_layer->useMinQp ? h264_layer->minQp.qpI : 0;
vid->rc_per_pic[l].min_qp_p = h264_layer->useMinQp ? h264_layer->minQp.qpP : 0;
vid->rc_per_pic[l].min_qp_b = h264_layer->useMinQp ? h264_layer->minQp.qpB : 0;
vid->rc_per_pic[l].max_qp_i = h264_layer->useMaxQp ? h264_layer->maxQp.qpI : 51;
vid->rc_per_pic[l].max_qp_p = h264_layer->useMaxQp ? h264_layer->maxQp.qpP : 51;
vid->rc_per_pic[l].max_qp_b = h264_layer->useMaxQp ? h264_layer->maxQp.qpB : 51;
vid->rc_per_pic[l].max_au_size_i = h264_layer->useMaxFrameSize ? h264_layer->maxFrameSize.frameISize : 0;
vid->rc_per_pic[l].max_au_size_p = h264_layer->useMaxFrameSize ? h264_layer->maxFrameSize.framePSize : 0;
vid->rc_per_pic[l].max_au_size_b = h264_layer->useMaxFrameSize ? h264_layer->maxFrameSize.frameBSize : 0;
} else if (h265_layer) {
vid->rc_per_pic[l].min_qp_i = h265_layer->useMinQp ? h265_layer->minQp.qpI : 0;
vid->rc_per_pic[l].min_qp_p = h265_layer->useMinQp ? h265_layer->minQp.qpP : 0;
vid->rc_per_pic[l].min_qp_b = h265_layer->useMinQp ? h265_layer->minQp.qpB : 0;
vid->rc_per_pic[l].max_qp_i = h265_layer->useMaxQp ? h265_layer->maxQp.qpI : 51;
vid->rc_per_pic[l].max_qp_p = h265_layer->useMaxQp ? h265_layer->maxQp.qpP : 51;
vid->rc_per_pic[l].max_qp_b = h265_layer->useMaxQp ? h265_layer->maxQp.qpB : 51;
vid->rc_per_pic[l].max_au_size_i = h265_layer->useMaxFrameSize ? h265_layer->maxFrameSize.frameISize : 0;
vid->rc_per_pic[l].max_au_size_p = h265_layer->useMaxFrameSize ? h265_layer->maxFrameSize.framePSize : 0;
vid->rc_per_pic[l].max_au_size_b = h265_layer->useMaxFrameSize ? h265_layer->maxFrameSize.frameBSize : 0;
} else if (av1_layer) {
vid->rc_per_pic[l].min_qp_i = av1_layer->useMinQIndex ? av1_layer->minQIndex.intraQIndex : 0;
vid->rc_per_pic[l].min_qp_p = av1_layer->useMinQIndex ? av1_layer->minQIndex.predictiveQIndex : 0;
vid->rc_per_pic[l].min_qp_b = av1_layer->useMinQIndex ? av1_layer->minQIndex.bipredictiveQIndex : 0;
vid->rc_per_pic[l].max_qp_i = av1_layer->useMaxQIndex ? av1_layer->maxQIndex.intraQIndex : 0;
vid->rc_per_pic[l].max_qp_p = av1_layer->useMaxQIndex ? av1_layer->maxQIndex.predictiveQIndex : 0;
vid->rc_per_pic[l].max_qp_b = av1_layer->useMaxQIndex ? av1_layer->maxQIndex.bipredictiveQIndex : 0;
vid->rc_per_pic[l].max_au_size_i = av1_layer->useMaxFrameSize ? av1_layer->maxFrameSize.intraFrameSize : 0;
vid->rc_per_pic[l].max_au_size_p =
av1_layer->useMaxFrameSize ? av1_layer->maxFrameSize.predictiveFrameSize : 0;
vid->rc_per_pic[l].max_au_size_b =
av1_layer->useMaxFrameSize ? av1_layer->maxFrameSize.bipredictiveFrameSize : 0;
}
vid->rc_per_pic[l].enabled_filler_data = 1;
vid->rc_per_pic[l].skip_frame_enable = 0;
vid->rc_per_pic[l].enforce_hrd = 1;
}
if (rate_control->virtualBufferSizeInMs > 0)
vid->enc_vbv_buffer_level =
lroundf((float)rate_control->initialVirtualBufferSizeInMs / rate_control->virtualBufferSizeInMs * 64);
}
}
VKAPI_ATTR void VKAPI_CALL
radv_CmdEncodeVideoKHR(VkCommandBuffer commandBuffer, const VkVideoEncodeInfoKHR *pEncodeInfo)
{
VK_FROM_HANDLE(radv_cmd_buffer, cmd_buffer, commandBuffer);
radv_vcn_encode_video(cmd_buffer, pEncodeInfo);
}
VKAPI_ATTR VkResult VKAPI_CALL
radv_GetPhysicalDeviceVideoEncodeQualityLevelPropertiesKHR(
VkPhysicalDevice physicalDevice, const VkPhysicalDeviceVideoEncodeQualityLevelInfoKHR *pQualityLevelInfo,
VkVideoEncodeQualityLevelPropertiesKHR *pQualityLevelProperties)
{
VK_FROM_HANDLE(radv_physical_device, pdev, physicalDevice);
pQualityLevelProperties->preferredRateControlMode = VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DEFAULT_KHR;
pQualityLevelProperties->preferredRateControlLayerCount = 0;
switch (pQualityLevelInfo->pVideoProfile->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR: {
struct VkVideoEncodeH264QualityLevelPropertiesKHR *ext =
(struct VkVideoEncodeH264QualityLevelPropertiesKHR *)vk_find_struct(
pQualityLevelProperties->pNext, VIDEO_ENCODE_H264_QUALITY_LEVEL_PROPERTIES_KHR);
if (ext) {
ext->preferredRateControlFlags = VK_VIDEO_ENCODE_H264_RATE_CONTROL_ATTEMPT_HRD_COMPLIANCE_BIT_KHR;
ext->preferredGopFrameCount = 60;
ext->preferredIdrPeriod = 60;
ext->preferredConsecutiveBFrameCount = 0;
ext->preferredTemporalLayerCount = 1;
ext->preferredConstantQp.qpI = 26;
ext->preferredConstantQp.qpP = 26;
ext->preferredConstantQp.qpB = 26;
ext->preferredMaxL0ReferenceCount = 1;
ext->preferredMaxL1ReferenceCount = 0;
ext->preferredStdEntropyCodingModeFlag = 1;
}
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR: {
struct VkVideoEncodeH265QualityLevelPropertiesKHR *ext =
(struct VkVideoEncodeH265QualityLevelPropertiesKHR *)vk_find_struct(
pQualityLevelProperties->pNext, VIDEO_ENCODE_H265_QUALITY_LEVEL_PROPERTIES_KHR);
if (ext) {
ext->preferredRateControlFlags = VK_VIDEO_ENCODE_H265_RATE_CONTROL_ATTEMPT_HRD_COMPLIANCE_BIT_KHR;
ext->preferredGopFrameCount = 60;
ext->preferredIdrPeriod = 60;
ext->preferredConsecutiveBFrameCount = 0;
ext->preferredSubLayerCount = 1;
ext->preferredConstantQp.qpI = 26;
ext->preferredConstantQp.qpP = 26;
ext->preferredConstantQp.qpB = 26;
ext->preferredMaxL0ReferenceCount = 1;
ext->preferredMaxL1ReferenceCount = 0;
}
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR: {
struct VkVideoEncodeAV1QualityLevelPropertiesKHR *ext =
(struct VkVideoEncodeAV1QualityLevelPropertiesKHR *)vk_find_struct(
pQualityLevelProperties->pNext, VIDEO_ENCODE_AV1_QUALITY_LEVEL_PROPERTIES_KHR);
if (ext) {
ext->preferredRateControlFlags =
0; // https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/35767#note_2979437
ext->preferredGopFrameCount = 60;
ext->preferredKeyFramePeriod = 60;
ext->preferredConsecutiveBipredictiveFrameCount = 0;
ext->preferredTemporalLayerCount = 1;
ext->preferredConstantQIndex.intraQIndex = 128;
ext->preferredConstantQIndex.predictiveQIndex = 128;
ext->preferredConstantQIndex.bipredictiveQIndex = 128;
ext->preferredMaxSingleReferenceCount = 1;
ext->preferredSingleReferenceNameMask =
(1 << (STD_VIDEO_AV1_REFERENCE_NAME_LAST_FRAME - STD_VIDEO_AV1_REFERENCE_NAME_LAST_FRAME));
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_5) {
ext->preferredMaxUnidirectionalCompoundReferenceCount = 2;
ext->preferredMaxUnidirectionalCompoundGroup1ReferenceCount = 2;
ext->preferredUnidirectionalCompoundReferenceNameMask =
(1 << (STD_VIDEO_AV1_REFERENCE_NAME_LAST_FRAME - STD_VIDEO_AV1_REFERENCE_NAME_LAST_FRAME)) |
(1 << (STD_VIDEO_AV1_REFERENCE_NAME_GOLDEN_FRAME - STD_VIDEO_AV1_REFERENCE_NAME_LAST_FRAME));
ext->preferredMaxBidirectionalCompoundReferenceCount = 2;
ext->preferredMaxBidirectionalCompoundGroup1ReferenceCount = 1;
ext->preferredMaxBidirectionalCompoundGroup2ReferenceCount = 1;
ext->preferredBidirectionalCompoundReferenceNameMask =
(1 << (STD_VIDEO_AV1_REFERENCE_NAME_LAST_FRAME - STD_VIDEO_AV1_REFERENCE_NAME_LAST_FRAME)) |
(1 << (STD_VIDEO_AV1_REFERENCE_NAME_ALTREF_FRAME - STD_VIDEO_AV1_REFERENCE_NAME_LAST_FRAME));
} else {
ext->preferredMaxUnidirectionalCompoundReferenceCount = 0;
ext->preferredMaxUnidirectionalCompoundGroup1ReferenceCount = 0;
ext->preferredUnidirectionalCompoundReferenceNameMask = 0;
ext->preferredMaxBidirectionalCompoundReferenceCount = 0;
ext->preferredMaxBidirectionalCompoundGroup1ReferenceCount = 0;
ext->preferredMaxBidirectionalCompoundGroup2ReferenceCount = 0;
ext->preferredBidirectionalCompoundReferenceNameMask = 0;
}
}
break;
}
default:
break;
}
return VK_SUCCESS;
}
void
radv_video_patch_encode_session_parameters(struct radv_device *device, struct vk_video_session_parameters *params)
{
struct radv_physical_device *pdev = radv_device_physical(device);
switch (params->op) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
for (unsigned i = 0; i < params->h264_enc.h264_pps_count; i++) {
params->h264_enc.h264_pps[i].base.pic_init_qp_minus26 = 0;
params->h264_enc.h264_pps[i].base.pic_init_qs_minus26 = 0;
if (pdev->enc_hw_ver < RADV_VIDEO_ENC_HW_5 ||
!params->h264_enc.h264_pps[i].base.flags.entropy_coding_mode_flag)
params->h264_enc.h264_pps[i].base.flags.transform_8x8_mode_flag = 0;
}
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR: {
/*
* AMD firmware requires these flags to be set in h265 with RC modes,
* VCN 3 need 1.27 and VCN 4 needs 1.7 or newer to pass the CTS tests,
* dEQP-VK.video.encode.h265_rc_*.
*/
for (unsigned i = 0; i < params->h265_enc.h265_pps_count; i++) {
params->h265_enc.h265_pps[i].base.flags.cu_qp_delta_enabled_flag = 1;
params->h265_enc.h265_pps[i].base.diff_cu_qp_delta_depth = 0;
params->h265_enc.h265_pps[i].base.init_qp_minus26 = 0;
params->h265_enc.h265_pps[i].base.flags.dependent_slice_segments_enabled_flag = 1;
if (pdev->enc_hw_ver < RADV_VIDEO_ENC_HW_3)
params->h265_enc.h265_pps[i].base.flags.transform_skip_enabled_flag = 0;
}
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR: {
/* If the resolution isn't aligned, we need to override it. */
uint16_t frame_width = params->av1_enc.seq_hdr.base.max_frame_width_minus_1 + 1;
uint16_t frame_height = params->av1_enc.seq_hdr.base.max_frame_height_minus_1 + 1;
if (pdev->enc_hw_ver == RADV_VIDEO_ENC_HW_4) {
frame_width = align(frame_width, 64);
frame_height = align(frame_height, 16);
} else if (pdev->enc_hw_ver == RADV_VIDEO_ENC_HW_5) {
frame_width = align(frame_width, 8);
frame_height = align(frame_height, 2);
}
params->av1_enc.seq_hdr.base.max_frame_width_minus_1 = frame_width - 1;
params->av1_enc.seq_hdr.base.max_frame_height_minus_1 = frame_height - 1;
/* Also override the bit length if they're too small now */
if (frame_width >= (1 << (params->av1_enc.seq_hdr.base.frame_width_bits_minus_1 + 1)))
params->av1_enc.seq_hdr.base.frame_width_bits_minus_1++;
if (frame_height >= (1 << (params->av1_enc.seq_hdr.base.frame_height_bits_minus_1 + 1)))
params->av1_enc.seq_hdr.base.frame_height_bits_minus_1++;
/* AMD does not support loop restoration */
params->av1_enc.seq_hdr.base.flags.enable_restoration = 0;
/* If pColorConfig is NULL we need to force 10 bit here. */
params->av1_enc.seq_hdr.color_config.BitDepth =
params->luma_bit_depth == VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR ? 10 : 8;
break;
}
default:
break;
}
}
VKAPI_ATTR VkResult VKAPI_CALL
radv_GetEncodedVideoSessionParametersKHR(VkDevice device,
const VkVideoEncodeSessionParametersGetInfoKHR *pVideoSessionParametersInfo,
VkVideoEncodeSessionParametersFeedbackInfoKHR *pFeedbackInfo,
size_t *pDataSize, void *pData)
{
VK_FROM_HANDLE(radv_video_session_params, templ, pVideoSessionParametersInfo->videoSessionParameters);
size_t total_size = 0;
size_t size_limit = 0;
if (pData)
size_limit = *pDataSize;
switch (templ->vk.op) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR: {
const struct VkVideoEncodeH264SessionParametersGetInfoKHR *h264_get_info =
vk_find_struct_const(pVideoSessionParametersInfo->pNext, VIDEO_ENCODE_H264_SESSION_PARAMETERS_GET_INFO_KHR);
size_t sps_size = 0, pps_size = 0;
if (h264_get_info->writeStdSPS) {
const StdVideoH264SequenceParameterSet *sps =
vk_video_find_h264_enc_std_sps(&templ->vk, h264_get_info->stdSPSId);
assert(sps);
vk_video_encode_h264_sps(sps, size_limit, &sps_size, pData);
}
if (h264_get_info->writeStdPPS) {
const StdVideoH264PictureParameterSet *pps =
vk_video_find_h264_enc_std_pps(&templ->vk, h264_get_info->stdPPSId);
assert(pps);
char *data_ptr = pData ? (char *)pData + sps_size : NULL;
vk_video_encode_h264_pps(pps, templ->vk.h264_enc.profile_idc == STD_VIDEO_H264_PROFILE_IDC_HIGH, size_limit,
&pps_size, data_ptr);
if (pFeedbackInfo) {
struct VkVideoEncodeH264SessionParametersFeedbackInfoKHR *h264_feedback_info =
vk_find_struct(pFeedbackInfo->pNext, VIDEO_ENCODE_H264_SESSION_PARAMETERS_FEEDBACK_INFO_KHR);
pFeedbackInfo->hasOverrides = VK_TRUE;
if (h264_feedback_info)
h264_feedback_info->hasStdPPSOverrides = VK_TRUE;
}
}
total_size = sps_size + pps_size;
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR: {
const struct VkVideoEncodeH265SessionParametersGetInfoKHR *h265_get_info =
vk_find_struct_const(pVideoSessionParametersInfo->pNext, VIDEO_ENCODE_H265_SESSION_PARAMETERS_GET_INFO_KHR);
size_t sps_size = 0, pps_size = 0, vps_size = 0;
if (h265_get_info->writeStdVPS) {
const StdVideoH265VideoParameterSet *vps = vk_video_find_h265_enc_std_vps(&templ->vk, h265_get_info->stdVPSId);
assert(vps);
vk_video_encode_h265_vps(vps, size_limit, &vps_size, pData);
}
if (h265_get_info->writeStdSPS) {
const StdVideoH265SequenceParameterSet *sps =
vk_video_find_h265_enc_std_sps(&templ->vk, h265_get_info->stdSPSId);
assert(sps);
char *data_ptr = pData ? (char *)pData + vps_size : NULL;
vk_video_encode_h265_sps(sps, size_limit, &sps_size, data_ptr);
}
if (h265_get_info->writeStdPPS) {
const StdVideoH265PictureParameterSet *pps =
vk_video_find_h265_enc_std_pps(&templ->vk, h265_get_info->stdPPSId);
assert(pps);
char *data_ptr = pData ? (char *)pData + vps_size + sps_size : NULL;
vk_video_encode_h265_pps(pps, size_limit, &pps_size, data_ptr);
if (pFeedbackInfo) {
struct VkVideoEncodeH265SessionParametersFeedbackInfoKHR *h265_feedback_info =
vk_find_struct(pFeedbackInfo->pNext, VIDEO_ENCODE_H265_SESSION_PARAMETERS_FEEDBACK_INFO_KHR);
pFeedbackInfo->hasOverrides = VK_TRUE;
if (h265_feedback_info)
h265_feedback_info->hasStdPPSOverrides = VK_TRUE;
}
}
total_size = sps_size + pps_size + vps_size;
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR: {
struct vk_video_av1_seq_hdr *seq_hdr = &templ->vk.av1_enc.seq_hdr;
if (!seq_hdr)
return VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR;
vk_video_encode_av1_seq_hdr(&templ->vk, size_limit, &total_size, pData);
break;
}
default:
break;
}
*pDataSize = total_size;
return VK_SUCCESS;
}
#define VCN_ENC_SESSION_SIZE 128 * 1024
VkResult
radv_video_get_encode_session_memory_requirements(struct radv_device *device, struct radv_video_session *vid,
uint32_t *pMemoryRequirementsCount,
VkVideoSessionMemoryRequirementsKHR *pMemoryRequirements)
{
struct radv_physical_device *pdev = radv_device_physical(device);
uint32_t memory_type_bits = (1u << pdev->memory_properties.memoryTypeCount) - 1;
VK_OUTARRAY_MAKE_TYPED(VkVideoSessionMemoryRequirementsKHR, out, pMemoryRequirements, pMemoryRequirementsCount);
vk_outarray_append_typed(VkVideoSessionMemoryRequirementsKHR, &out, m)
{
m->memoryBindIndex = 0;
m->memoryRequirements.size = VCN_ENC_SESSION_SIZE;
m->memoryRequirements.alignment = 0;
m->memoryRequirements.memoryTypeBits = memory_type_bits;
}
if (vid->vk.op == VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR) {
vk_outarray_append_typed(VkVideoSessionMemoryRequirementsKHR, &out, m)
{
m->memoryBindIndex = RADV_BIND_ENCODE_AV1_CDF_STORE;
m->memoryRequirements.size = VCN_ENC_AV1_DEFAULT_CDF_SIZE;
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_5)
m->memoryRequirements.size += RENCODE_AV1_SDB_FRAME_CONTEXT_SIZE;
m->memoryRequirements.alignment = 0;
m->memoryRequirements.memoryTypeBits = 0;
for (unsigned i = 0; i < pdev->memory_properties.memoryTypeCount; i++)
if (pdev->memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT)
m->memoryRequirements.memoryTypeBits |= (1 << i);
}
}
return vk_outarray_status(&out);
}
void
radv_video_get_enc_dpb_image(struct radv_device *device, const struct VkVideoProfileListInfoKHR *profile_list,
struct radv_image *image, struct radv_image_create_info *create_info)
{
const struct radv_physical_device *pdev = radv_device_physical(device);
uint32_t luma_pitch, luma_size, chroma_size, colloc_bytes;
uint32_t num_reconstructed_pictures = image->vk.array_layers;
bool has_h264_b_support = false;
bool is_av1 = false;
for (unsigned i = 0; i < profile_list->profileCount; i++) {
if (profile_list->pProfiles[i].videoCodecOperation == VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR) {
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_3) {
has_h264_b_support = true;
}
}
if (profile_list->pProfiles[i].videoCodecOperation == VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR) {
is_av1 = true;
}
}
dpb_image_sizes(image, &luma_pitch, &luma_size, &chroma_size, &colloc_bytes);
image->size = 0;
if (pdev->enc_hw_ver < RADV_VIDEO_ENC_HW_5) {
if (has_h264_b_support)
image->size += colloc_bytes;
if (is_av1)
image->size += RENCODE_AV1_SDB_FRAME_CONTEXT_SIZE;
}
for (unsigned i = 0; i < num_reconstructed_pictures; i++) {
image->size += luma_size;
image->size += chroma_size;
if (is_av1) {
image->size += RENCODE_AV1_FRAME_CONTEXT_CDF_TABLE_SIZE;
image->size += RENCODE_AV1_CDEF_ALGORITHM_FRAME_CONTEXT_SIZE;
}
if (pdev->enc_hw_ver >= RADV_VIDEO_ENC_HW_5) {
image->size += RENCODE_MAX_METADATA_BUFFER_SIZE_PER_FRAME;
if (has_h264_b_support)
image->size += colloc_bytes;
}
}
image->alignment = ENC_ALIGNMENT;
}
bool
radv_video_encode_av1_supported(const struct radv_physical_device *pdev)
{
if (pdev->info.vcn_ip_version >= VCN_5_0_0) {
return true;
} else if (pdev->info.vcn_ip_version >= VCN_4_0_0) {
return pdev->info.vcn_ip_version != VCN_4_0_3 && pdev->info.vcn_enc_minor_version >= 20;
} else {
return false;
}
}