diff --git a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfig.cpp b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfig.cpp index 9918d573..56d4d804 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfig.cpp +++ b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfig.cpp @@ -823,13 +823,15 @@ void EncoderConfig::InitVideoProfile() encodeBitDepthChroma = encodeBitDepthLuma; } + // Get the codec-specific profile (already set by InitProfileLevel) + uint32_t codecProfile = GetCodecProfile(); + // update the video profile videoCoreProfile = VkVideoCoreProfile::CreateEncodeProfile( codec, encodeChromaSubsampling, GetComponentBitDepthFlagBits(encodeBitDepthLuma), GetComponentBitDepthFlagBits(encodeBitDepthChroma), - (videoProfileIdc != (uint32_t)-1) ? videoProfileIdc : - GetDefaultVideoProfileIdc(), + codecProfile, tuningMode); } diff --git a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfig.h b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfig.h index 8ad4b161..35e0727f 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfig.h +++ b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfig.h @@ -685,7 +685,6 @@ struct EncoderConfig : public VkVideoRefCountBase { bool noDeviceFallback; VkVideoCodecOperationFlagBitsKHR codec; bool useDpbArray; - uint32_t videoProfileIdc; uint32_t numInputImages; EncoderInputImageParameters input; uint8_t encodeBitDepthLuma; @@ -793,7 +792,6 @@ struct EncoderConfig : public VkVideoRefCountBase { , noDeviceFallback(false) , codec(VK_VIDEO_CODEC_OPERATION_NONE_KHR) , useDpbArray(false) - , videoProfileIdc((uint32_t)-1) , numInputImages(DEFAULT_NUM_INPUT_IMAGES) , input() , encodeBitDepthLuma(0) @@ -961,7 +959,8 @@ struct EncoderConfig : public VkVideoRefCountBase { // These functions should be overwritten from the codec-specific classes virtual VkResult InitDeviceCapabilities(const VulkanDeviceContext* vkDevCtx) { return VK_ERROR_INITIALIZATION_FAILED; }; - virtual uint32_t GetDefaultVideoProfileIdc() { return 0; }; + // Returns the codec-specific profile identifier (must be set by InitProfileLevel first) + virtual uint32_t GetCodecProfile() = 0; virtual int8_t InitDpbCount() { return 16; }; diff --git a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigAV1.cpp b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigAV1.cpp index c3ba67c1..24cee2ef 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigAV1.cpp +++ b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigAV1.cpp @@ -285,6 +285,29 @@ VkResult EncoderConfigAV1::InitDeviceCapabilities(const VulkanDeviceContext* vkD return VK_SUCCESS; } +void EncoderConfigAV1::InitProfileLevel() +{ + // If profile hasn't been specified, determine it based on bit depth and chroma + if (profile == STD_VIDEO_AV1_PROFILE_INVALID) { + // PROFESSIONAL is required for 12-bit or 422 + if ((input.bpp > 10) || + (encodeChromaSubsampling == VK_VIDEO_CHROMA_SUBSAMPLING_422_BIT_KHR)) { + profile = STD_VIDEO_AV1_PROFILE_PROFESSIONAL; + } + // HIGH is required for 444 chroma + else if (encodeChromaSubsampling == VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR) { + profile = STD_VIDEO_AV1_PROFILE_HIGH; + } + // MAIN supports 8-bit and 10-bit with 420 + else { + profile = STD_VIDEO_AV1_PROFILE_MAIN; + } + } + + // Determine level and tier based on encoder configuration + DetermineLevelTier(); +} + int8_t EncoderConfigAV1::InitDpbCount() { dpbCount = STD_VIDEO_AV1_NUM_REF_FRAMES + 1; // BUFFER_POOL_MAX_SIZE = Number of frames in buffer pool = 10 @@ -351,8 +374,7 @@ bool EncoderConfigAV1::DetermineLevelTier() bool EncoderConfigAV1::InitRateControl() { - DetermineLevelTier(); - + // Level and tier are already initialized by InitProfileLevel() // use level max values for now. Limit it to 120Mbits/sec uint32_t levelBitrate = std::min(GetLevelBitrate(level, tier), 120000000u); diff --git a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigAV1.h b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigAV1.h index 622977d6..58168822 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigAV1.h +++ b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigAV1.h @@ -102,6 +102,8 @@ struct EncoderConfigAV1 : public EncoderConfig { pic_height_in_sbs = DivUp(encodeHeight, 16); if ((pic_width_in_sbs > 0) && (pic_height_in_sbs > 0)) { + // Initialize profile, level, and tier based on encoder configuration + InitProfileLevel(); return VK_SUCCESS; } @@ -111,7 +113,12 @@ struct EncoderConfigAV1 : public EncoderConfig { virtual VkResult InitDeviceCapabilities(const VulkanDeviceContext* vkDevCtx) override; - virtual uint32_t GetDefaultVideoProfileIdc() override { return STD_VIDEO_AV1_PROFILE_MAIN; } + void InitProfileLevel(); + + virtual uint32_t GetCodecProfile() override { + assert(profile != STD_VIDEO_AV1_PROFILE_INVALID); + return static_cast(profile); + } virtual int8_t InitDpbCount() override; @@ -185,8 +192,8 @@ struct EncoderConfigAV1 : public EncoderConfig { return ((encodeWidth * encodeHeight * picSizeProfileFactor) >> 3); } - StdVideoAV1Profile profile{ STD_VIDEO_AV1_PROFILE_MAIN }; - StdVideoAV1Level level{ STD_VIDEO_AV1_LEVEL_5_0 }; + StdVideoAV1Profile profile{ STD_VIDEO_AV1_PROFILE_INVALID }; + StdVideoAV1Level level{ STD_VIDEO_AV1_LEVEL_INVALID }; uint8_t tier{}; VkVideoEncodeAV1CapabilitiesKHR av1EncodeCapabilities{ VK_STRUCTURE_TYPE_VIDEO_ENCODE_AV1_CAPABILITIES_KHR }; VkVideoEncodeAV1QualityLevelPropertiesKHR av1QualityLevelProperties{ VK_STRUCTURE_TYPE_VIDEO_ENCODE_AV1_QUALITY_LEVEL_PROPERTIES_KHR }; diff --git a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH264.cpp b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH264.cpp index e9c94bed..07a623e7 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH264.cpp +++ b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH264.cpp @@ -63,6 +63,13 @@ StdVideoH264LevelIdc EncoderConfigH264::DetermineLevel(uint8_t dpbSize, uint32_t _vbvBufferSize, double frameRate) { + uint32_t cpbBrNalFactor = 1200; + + // Adjust cpbBrNalFactor depending on whether the High (or greater) profile + // is being used + if (profileIdc >= STD_VIDEO_H264_PROFILE_IDC_HIGH) { + cpbBrNalFactor = (profileIdc >= STD_VIDEO_H264_PROFILE_IDC_HIGH_444_PREDICTIVE) ? 4800 : 1500; + } uint32_t frameSizeInMbs = pic_width_in_mbs * pic_height_in_map_units; for (uint32_t idx = 0; idx < levelLimitsSize; idx++) { @@ -71,8 +78,8 @@ StdVideoH264LevelIdc EncoderConfigH264::DetermineLevel(uint8_t dpbSize, if ((frameSizeInMbs) > ((uint32_t)levelLimits[idx].maxFS)) continue; if ((frameSizeInMbs * numRefFrames * 384) > levelLimits[idx].maxDPB * 1024) continue; - if ((bitrate != 0) && (bitrate > ((uint32_t)levelLimits[idx].maxBR * 1200))) continue; - if ((_vbvBufferSize != 0) && (_vbvBufferSize > ((uint32_t)levelLimits[idx].maxCPB * 1200))) continue; + if ((bitrate != 0) && (bitrate > ((uint32_t)levelLimits[idx].maxBR * cpbBrNalFactor))) continue; + if ((_vbvBufferSize != 0) && (_vbvBufferSize > ((uint32_t)levelLimits[idx].maxCPB * cpbBrNalFactor))) continue; return levelLimits[idx].level; } @@ -310,11 +317,6 @@ bool EncoderConfigH264::InitSpsPpsParameters(StdVideoH264SequenceParameterSet *s sps->pic_order_cnt_type = STD_VIDEO_H264_POC_TYPE_2; } - // FIXME: Check if the HW supports transform_8x8_mode_is_supported - // based on capabilities or profiles supported - const bool transform_8x8_mode_is_supported = true; - const bool bIsFastestPreset = false; - if (adaptiveTransformMode == ADAPTIVE_TRANSFORM_ENABLE) { pps->flags.transform_8x8_mode_flag = true; if ((profileIdc == STD_VIDEO_H264_PROFILE_IDC_BASELINE) || @@ -326,12 +328,9 @@ bool EncoderConfigH264::InitSpsPpsParameters(StdVideoH264SequenceParameterSet *s pps->flags.transform_8x8_mode_flag = false; } else { // Autoselect - if (!bIsFastestPreset || transform_8x8_mode_is_supported) { - if ((profileIdc == STD_VIDEO_H264_PROFILE_IDC_INVALID) || - (profileIdc >= STD_VIDEO_H264_PROFILE_IDC_HIGH)) { - // Unconditionally enable 8x8 transform - pps->flags.transform_8x8_mode_flag = true; - } + if (profileIdc >= STD_VIDEO_H264_PROFILE_IDC_HIGH) { + // Unconditionally enable 8x8 transform + pps->flags.transform_8x8_mode_flag = true; } } @@ -348,28 +347,6 @@ bool EncoderConfigH264::InitSpsPpsParameters(StdVideoH264SequenceParameterSet *s pps->flags.constrained_intra_pred_flag = true; } - // If the profileIdc hasn't been specified, force set it now. - if (profileIdc == STD_VIDEO_H264_PROFILE_IDC_INVALID) { - profileIdc = STD_VIDEO_H264_PROFILE_IDC_BASELINE; - - if (entropyCodingMode == ENTROPY_CODING_MODE_CABAC) { - profileIdc = STD_VIDEO_H264_PROFILE_IDC_MAIN; - } - - if ((gopStructure.GetConsecutiveBFrameCount() > 0) || pps->flags.entropy_coding_mode_flag || !sps->flags.frame_mbs_only_flag) - profileIdc = STD_VIDEO_H264_PROFILE_IDC_MAIN; - - if (pps->flags.transform_8x8_mode_flag) { - profileIdc = STD_VIDEO_H264_PROFILE_IDC_HIGH; - } - - if ((sps->flags.qpprime_y_zero_transform_bypass_flag && - (rateControlMode == VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DISABLED_BIT_KHR)) || - (sps->chroma_format_idc == STD_VIDEO_H264_CHROMA_FORMAT_IDC_444)) { - profileIdc = STD_VIDEO_H264_PROFILE_IDC_HIGH_444_PREDICTIVE; - } - } - sps->profile_idc = profileIdc; sps->level_idc = levelIdc; @@ -491,42 +468,67 @@ VkResult EncoderConfigH264::InitDeviceCapabilities(const VulkanDeviceContext* vk return VK_SUCCESS; } -int8_t EncoderConfigH264::InitDpbCount() +void EncoderConfigH264::InitProfileLevel() { - dpbCount = 0; // TODO: What is the need for this? + // FIXME: Check if the HW supports transform_8x8_mode_is_supported + // based on capabilities or profiles supported + bool use8x8Transform = true; + + if (adaptiveTransformMode == ADAPTIVE_TRANSFORM_ENABLE) { + use8x8Transform = true; + } else if (adaptiveTransformMode == ADAPTIVE_TRANSFORM_DISABLE) { + use8x8Transform = false; + } else { + // Autoselect + if ((profileIdc == STD_VIDEO_H264_PROFILE_IDC_INVALID) || + (profileIdc >= STD_VIDEO_H264_PROFILE_IDC_HIGH)) { + // Unconditionally enable 8x8 transform + use8x8Transform = true; + } + } + + // If the profileIdc hasn't been specified, force set it now. + if (profileIdc == STD_VIDEO_H264_PROFILE_IDC_INVALID) { + profileIdc = STD_VIDEO_H264_PROFILE_IDC_BASELINE; + + // Upgrade to MAIN profile if using B-frames or CABAC entropy coding + if ((gopStructure.GetConsecutiveBFrameCount() > 0) || (entropyCodingMode == ENTROPY_CODING_MODE_CABAC)) + profileIdc = STD_VIDEO_H264_PROFILE_IDC_MAIN; + + if (use8x8Transform) { + profileIdc = STD_VIDEO_H264_PROFILE_IDC_HIGH; + } + + // Upgrade to HIGH_444_PREDICTIVE for lossless encoding or 4:4:4 chroma + if ((tuningMode == VK_VIDEO_ENCODE_TUNING_MODE_LOSSLESS_KHR) || + (input.chromaSubsampling == VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR)) { + profileIdc = STD_VIDEO_H264_PROFILE_IDC_HIGH_444_PREDICTIVE; + } + } - // spsInfo->level represents the smallest level that we require for the - // given stream. This level constrains the maximum size (in terms of - // number of frames) that the DPB can have. levelDpbSize is this maximum - // value. uint32_t levelBitRate = ((rateControlMode != VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DISABLED_BIT_KHR) && hrdBitrate == 0) ? averageBitrate // constrained by avg bitrate : hrdBitrate; // constrained by max bitrate assert(pic_width_in_mbs > 0); assert(pic_height_in_map_units > 0); - uint32_t frameSizeInMbs = pic_width_in_mbs * pic_height_in_map_units; double frameRate = ((frameRateNumerator > 0) && (frameRateDenominator > 0)) ? (double)frameRateNumerator / frameRateDenominator : (double)FRAME_RATE_NUM_DEFAULT / (double)FRAME_RATE_DEN_DEFAULT; - // WAR for super HD resolution (bypass H264 level check for frame mode and use level 5.2) - if ((frameSizeInMbs > ((uint32_t)levelLimits[levelLimitsSize - 1].maxFS) || - ((frameSizeInMbs * frameRate) > ((uint32_t)levelLimits[levelLimitsSize - 1].maxMBPS)))) { - levelIdc = STD_VIDEO_H264_LEVEL_IDC_5_2; - } else { - // find lowest possible level - levelIdc = DetermineLevel(dpbCount, levelBitRate, vbvBufferSize, frameRate); - } + // find lowest possible level + levelIdc = DetermineLevel(dpbCount, levelBitRate, vbvBufferSize, frameRate); +} + +int8_t EncoderConfigH264::InitDpbCount() +{ + dpbCount = 0; // TODO: What is the need for this? uint8_t levelDpbSize = (uint8_t)(((1024 * levelLimits[levelIdc].maxDPB)) / ((pic_width_in_mbs * pic_height_in_map_units) * 384)); - // XXX: If the level is 5.2, it is highly likely that we forced it to that - // value as a WAR for super HD. In that case, force the DPB size to - // DEFAULT_MAX_NUM_REF_FRAMES. Otherwise, clamp the computed DPB size to DEFAULT_MAX_NUM_REF_FRAMES. - levelDpbSize = (levelIdc == STD_VIDEO_H264_LEVEL_IDC_5_2) ? (uint8_t)DEFAULT_MAX_NUM_REF_FRAMES : std::min(uint8_t(DEFAULT_MAX_NUM_REF_FRAMES), levelDpbSize); + levelDpbSize = std::min(uint8_t(DEFAULT_MAX_NUM_REF_FRAMES), levelDpbSize); uint8_t dpbSize = (uint8_t)((dpbCount < 1) ? levelDpbSize : (uint8_t)(std::min((uint8_t)dpbCount, levelDpbSize))) + 1; diff --git a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH264.h b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH264.h index 6d8865a5..32546503 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH264.h +++ b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH264.h @@ -54,7 +54,6 @@ struct EncoderConfigH264 : public EncoderConfig { uint32_t maxBR; // 1200 bits/s uint32_t maxCPB; // 1200 bits uint32_t maxVmvR; // [-MaxVmvR..+MaxVmvR-0.25] - uint32_t prog; // frame_mbs_only_flag = 1 StdVideoH264LevelIdc level; }; @@ -90,23 +89,26 @@ struct EncoderConfigH264 : public EncoderConfig { { // Level limits (Table A-1) static const LevelLimits levelLimitsTbl[] = { - // level_idc, maxMBPS, maxFS, maxDPB, maxBR, maxCPB, maxVmvR, prog, level - {10, 1485, 99, 148.5, 64, 175, 64, 1, STD_VIDEO_H264_LEVEL_IDC_1_0}, - {11, 3000, 396, 337.5, 192, 500, 128, 1, STD_VIDEO_H264_LEVEL_IDC_1_1}, - {12, 6000, 396, 891.0, 384, 1000, 128, 1, STD_VIDEO_H264_LEVEL_IDC_1_2}, - {13, 11880, 396, 891.0, 768, 2000, 128, 1, STD_VIDEO_H264_LEVEL_IDC_1_3}, - {20, 11880, 396, 891.0, 2000, 2000, 128, 1, STD_VIDEO_H264_LEVEL_IDC_2_0}, - {21, 19800, 792, 1782.0, 4000, 4000, 256, 0, STD_VIDEO_H264_LEVEL_IDC_2_1}, - {22, 20250, 1620, 3037.5, 4000, 4000, 256, 0, STD_VIDEO_H264_LEVEL_IDC_2_2}, - {30, 40500, 1620, 3037.5, 10000, 10000, 256, 0, STD_VIDEO_H264_LEVEL_IDC_3_0}, - {31, 108000, 3600, 6750.0, 14000, 14000, 512, 0, STD_VIDEO_H264_LEVEL_IDC_3_1}, - {32, 216000, 5120, 7680.0, 20000, 20000, 512, 0, STD_VIDEO_H264_LEVEL_IDC_3_2}, - {40, 245760, 8192, 12288.0, 20000, 25000, 512, 0, STD_VIDEO_H264_LEVEL_IDC_4_0}, - {41, 245760, 8192, 12288.0, 50000, 62500, 512, 0, STD_VIDEO_H264_LEVEL_IDC_4_1}, - {42, 522240, 8704, 13056.0, 50000, 62500, 512, 0, STD_VIDEO_H264_LEVEL_IDC_4_2}, - {50, 589824, 22080, 41400.0, 135000, 135000, 512, 0, STD_VIDEO_H264_LEVEL_IDC_5_0}, - {51, 983040, 36864, 69120.0, 240000, 240000, 512, 0, STD_VIDEO_H264_LEVEL_IDC_5_1}, - {52, 2073600, 36864, 69120.0, 240000, 240000, 512, 0, STD_VIDEO_H264_LEVEL_IDC_5_2}, + // level_idc, maxMBPS, maxFS, maxDPB, maxBR, maxCPB, maxVmvR, level + {10, 1485, 99, 148.5, 64, 175, 64, STD_VIDEO_H264_LEVEL_IDC_1_0}, + {11, 3000, 396, 337.5, 192, 500, 128, STD_VIDEO_H264_LEVEL_IDC_1_1}, + {12, 6000, 396, 891.0, 384, 1000, 128, STD_VIDEO_H264_LEVEL_IDC_1_2}, + {13, 11880, 396, 891.0, 768, 2000, 128, STD_VIDEO_H264_LEVEL_IDC_1_3}, + {20, 11880, 396, 891.0, 2000, 2000, 128, STD_VIDEO_H264_LEVEL_IDC_2_0}, + {21, 19800, 792, 1782.0, 4000, 4000, 256, STD_VIDEO_H264_LEVEL_IDC_2_1}, + {22, 20250, 1620, 3037.5, 4000, 4000, 256, STD_VIDEO_H264_LEVEL_IDC_2_2}, + {30, 40500, 1620, 3037.5, 10000, 10000, 256, STD_VIDEO_H264_LEVEL_IDC_3_0}, + {31, 108000, 3600, 6750.0, 14000, 14000, 512, STD_VIDEO_H264_LEVEL_IDC_3_1}, + {32, 216000, 5120, 7680.0, 20000, 20000, 512, STD_VIDEO_H264_LEVEL_IDC_3_2}, + {40, 245760, 8192, 12288.0, 20000, 25000, 512, STD_VIDEO_H264_LEVEL_IDC_4_0}, + {41, 245760, 8192, 12288.0, 50000, 62500, 512, STD_VIDEO_H264_LEVEL_IDC_4_1}, + {42, 522240, 8704, 13056.0, 50000, 62500, 512, STD_VIDEO_H264_LEVEL_IDC_4_2}, + {50, 589824, 22080, 41400.0, 135000, 135000, 512, STD_VIDEO_H264_LEVEL_IDC_5_0}, + {51, 983040, 36864, 69120.0, 240000, 240000, 512, STD_VIDEO_H264_LEVEL_IDC_5_1}, + {52, 2073600, 36864, 69120.0, 240000, 240000, 512, STD_VIDEO_H264_LEVEL_IDC_5_2}, + {60, 4177920, 139264, 261120.0, 240000, 240000, 8192, STD_VIDEO_H264_LEVEL_IDC_6_0}, + {61, 8355840, 139264, 261120.0, 480000, 480000, 8192, STD_VIDEO_H264_LEVEL_IDC_6_1}, + {62, 16711680, 139264, 261120.0, 800000, 800000, 8192, STD_VIDEO_H264_LEVEL_IDC_6_2}, }; levelLimits = levelLimitsTbl; @@ -178,6 +180,8 @@ struct EncoderConfigH264 : public EncoderConfig { pic_height_in_map_units = DivUp(encodeHeight, 16); if ((pic_width_in_mbs > 0) && (pic_height_in_map_units > 0)) { + // Initialize codec profile and level based on encoder configuration + InitProfileLevel(); return VK_SUCCESS; } @@ -185,9 +189,14 @@ struct EncoderConfigH264 : public EncoderConfig { return VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR; } + void InitProfileLevel(); + virtual VkResult InitDeviceCapabilities(const VulkanDeviceContext* vkDevCtx) override; - virtual uint32_t GetDefaultVideoProfileIdc() override { return STD_VIDEO_H264_PROFILE_IDC_HIGH; }; + virtual uint32_t GetCodecProfile() override { + assert(profileIdc != STD_VIDEO_H264_PROFILE_IDC_INVALID); + return static_cast(profileIdc); + } // 1. First h.264 determine the number of the Dpb buffers required virtual int8_t InitDpbCount() override; diff --git a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.cpp b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.cpp index 33bcc53e..8b041b9d 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.cpp +++ b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.cpp @@ -483,25 +483,24 @@ void EncoderConfigH265::InitializeSpsRefPicSet(SpsH265 *pSps) memset(&pSps->longTermRefPicsSps.lt_ref_pic_poc_lsb_sps, 0, sizeof(pSps->longTermRefPicsSps.lt_ref_pic_poc_lsb_sps)); } -StdVideoH265ProfileTierLevel EncoderConfigH265::GetLevelTier() +void EncoderConfigH265::DetermineLevelTier() { - StdVideoH265ProfileTierLevel profileTierLevel{}; - profileTierLevel.general_profile_idc = STD_VIDEO_H265_PROFILE_IDC_INVALID; - profileTierLevel.general_level_idc = STD_VIDEO_H265_LEVEL_IDC_INVALID; + levelIdc = STD_VIDEO_H265_LEVEL_IDC_INVALID; + general_tier_flag = 0; uint32_t levelIdx = 0; for (; levelIdx < levelLimitsTblSize; levelIdx++) { if (IsSuitableLevel(levelIdx, 0)) { - profileTierLevel.general_level_idc = levelLimits[levelIdx].stdLevel; - profileTierLevel.flags.general_tier_flag = 0; // Main tier + levelIdc = levelLimits[levelIdx].stdLevel; + general_tier_flag = 0; // Main tier break; } if ((levelLimits[levelIdx].levelIdc >= 120) && // level 4.0 and above IsSuitableLevel(levelIdx, 1)) { - profileTierLevel.general_level_idc = levelLimits[levelIdx].stdLevel; - profileTierLevel.flags.general_tier_flag = 1; // Main tier + levelIdc = levelLimits[levelIdx].stdLevel; + general_tier_flag = 1; // High tier break; } } @@ -509,19 +508,34 @@ StdVideoH265ProfileTierLevel EncoderConfigH265::GetLevelTier() if (levelIdx >= levelLimitsTblSize) { assert(!"No suitable level selected"); } +} + +void EncoderConfigH265::InitProfileLevel() +{ + // If profile hasn't been specified, determine it based on bit depth and chroma + if (profile == STD_VIDEO_H265_PROFILE_IDC_INVALID) { + if (encodeChromaSubsampling == VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR) { + if (input.bpp == 8) { + profile = STD_VIDEO_H265_PROFILE_IDC_MAIN; + } else if (input.bpp <= 10) { + profile = STD_VIDEO_H265_PROFILE_IDC_MAIN_10; + } else { + profile = STD_VIDEO_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSIONS; + } + } else { + // 4:2:2 or 4:4:4 requires Range Extensions profile + profile = STD_VIDEO_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSIONS; + } + } - return profileTierLevel; + // Determine level and tier based on resolution, bitrate, etc. + DetermineLevelTier(); } bool EncoderConfigH265::InitRateControl() { - StdVideoH265ProfileTierLevel profileTierLevel = GetLevelTier(); - // Assigning default main profile if invalid. - if (profileTierLevel.general_profile_idc == STD_VIDEO_H265_PROFILE_IDC_INVALID) { - profileTierLevel.general_profile_idc = STD_VIDEO_H265_PROFILE_IDC_MAIN; - } - uint32_t level = profileTierLevel.general_level_idc; - if (level >= levelLimitsTblSize) { + // Level is already initialized by InitProfileLevel() + if (levelIdc >= levelLimitsTblSize) { assert(!"The h.265 level index is invalid"); return false; } @@ -529,7 +543,7 @@ bool EncoderConfigH265::InitRateControl() // Safe default maximum bitrate uint32_t levelBitRate = std::max(averageBitrate, hrdBitrate); - levelBitRate = std::max(levelBitRate, std::min(levelLimits[level].maxBitRateMainTier * 800u, 120000000u)); + levelBitRate = std::max(levelBitRate, std::min(levelLimits[levelIdc].maxBitRateMainTier * 800u, 120000000u)); // If no bitrate is specified, use the level limit if (averageBitrate == 0) { @@ -553,7 +567,7 @@ bool EncoderConfigH265::InitRateControl() // Use the main tier level limit for the max VBV buffer size, and no more than 8 seconds at peak rate if (vbvBufferSize == 0) { - vbvBufferSize = std::min(levelLimits[level].maxCPBSizeMainTier * cpbVclFactor, 100000000u); + vbvBufferSize = std::min(levelLimits[levelIdc].maxCPBSizeMainTier * cpbVclFactor, 100000000u); if (rateControlMode != VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DISABLED_BIT_KHR) { if ((vbvBufferSize >> 3) > hrdBitrate) { vbvBufferSize = hrdBitrate << 3; @@ -626,7 +640,9 @@ bool EncoderConfigH265::InitParamameters(VpsH265 *vpsInfo, SpsH265 *spsInfo, spsInfo->decPicBufMgr.max_num_reorder_pics[i] = gopStructure.GetConsecutiveBFrameCount() ? 1 : 0; } - spsInfo->profileTierLevel = GetLevelTier(); + // Use pre-initialized profile, level, and tier from InitProfileLevel() + spsInfo->profileTierLevel.general_profile_idc = profile; + spsInfo->profileTierLevel.general_level_idc = levelIdc; spsInfo->profileTierLevel.flags.general_tier_flag = general_tier_flag; // Always insert profile tier flags assuming frame mode @@ -636,21 +652,6 @@ bool EncoderConfigH265::InitParamameters(VpsH265 *vpsInfo, SpsH265 *spsInfo, spsInfo->profileTierLevel.flags.general_non_packed_constraint_flag = 0; spsInfo->profileTierLevel.flags.general_frame_only_constraint_flag = 1; - // Assigning default main profile if invalid. - if (spsInfo->profileTierLevel.general_profile_idc == STD_VIDEO_H265_PROFILE_IDC_INVALID) { - if (encodeChromaSubsampling == VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR) { - if (input.bpp == 8) { - spsInfo->profileTierLevel.general_profile_idc = STD_VIDEO_H265_PROFILE_IDC_MAIN; - } else if (input.bpp == 10) { - spsInfo->profileTierLevel.general_profile_idc = STD_VIDEO_H265_PROFILE_IDC_MAIN_10; - } else { - spsInfo->profileTierLevel.general_profile_idc = STD_VIDEO_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSIONS; - } - } else { - spsInfo->profileTierLevel.general_profile_idc = STD_VIDEO_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSIONS; - } - } - const uint32_t ctbLog2SizeY = cuSize + 3; const uint32_t minCbLog2SizeY = cuMinSize + 3; const uint32_t log2MinTransformBlockSize = std::min(minCbLog2SizeY, minTransformUnitSize + 2U); diff --git a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.h b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.h index 774bf1a9..39fcf019 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.h +++ b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.h @@ -83,8 +83,8 @@ struct EncoderConfigH265 : public EncoderConfig { size_t levelLimitsTblSize; EncoderConfigH265() - : profile(STD_VIDEO_H265_PROFILE_IDC_MAIN) - , levelIdc(STD_VIDEO_H265_LEVEL_IDC_5_2) + : profile(STD_VIDEO_H265_PROFILE_IDC_INVALID) + , levelIdc(STD_VIDEO_H265_LEVEL_IDC_INVALID) , h265EncodeCapabilities() , general_tier_flag(false) , numRefL0(1) @@ -147,13 +147,22 @@ struct EncoderConfigH265 : public EncoderConfig { if (result != VK_SUCCESS) { return result; } + + // Initialize profile, level, and tier based on encoder configuration + InitProfileLevel(); + // TODO: more h.265 parameters init ... return VK_SUCCESS; } + void InitProfileLevel(); + virtual VkResult InitDeviceCapabilities(const VulkanDeviceContext* vkDevCtx) override; - virtual uint32_t GetDefaultVideoProfileIdc() override { return STD_VIDEO_H265_PROFILE_IDC_MAIN; }; + virtual uint32_t GetCodecProfile() override { + assert(profile != STD_VIDEO_H265_PROFILE_IDC_INVALID); + return static_cast(profile); + } // 1. First h.265 determine the number of the Dpb buffers required virtual int8_t InitDpbCount() override; @@ -191,7 +200,7 @@ struct EncoderConfigH265 : public EncoderConfig { StdVideoH265SequenceParameterSetVui* vui = nullptr); bool IsSuitableLevel(uint32_t levelIdx, bool highTier); - StdVideoH265ProfileTierLevel GetLevelTier(); + void DetermineLevelTier(); void InitializeSpsRefPicSet(SpsH265 *pSps); };