diff --git a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfig.cpp b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfig.cpp index 9918d573..97feee85 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfig.cpp +++ b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfig.cpp @@ -813,26 +813,6 @@ VkResult EncoderConfig::CreateCodecConfig(int argc, const char *argv[], return VK_ERROR_INITIALIZATION_FAILED; } -void EncoderConfig::InitVideoProfile() -{ - if (encodeBitDepthLuma == 0) { - encodeBitDepthLuma = input.bpp; - } - - if (encodeBitDepthChroma == 0) { - encodeBitDepthChroma = encodeBitDepthLuma; - } - - // update the video profile - videoCoreProfile = VkVideoCoreProfile::CreateEncodeProfile( - codec, encodeChromaSubsampling, - GetComponentBitDepthFlagBits(encodeBitDepthLuma), - GetComponentBitDepthFlagBits(encodeBitDepthChroma), - (videoProfileIdc != (uint32_t)-1) ? videoProfileIdc : - GetDefaultVideoProfileIdc(), - tuningMode); -} - bool EncoderConfig::InitRateControl() { uint32_t levelBitRate = ((rateControlMode != VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DISABLED_BIT_KHR) && hrdBitrate == 0) diff --git a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfig.h b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfig.h index 8ad4b161..a4939ecc 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) @@ -915,8 +913,6 @@ struct EncoderConfig : public VkVideoRefCountBase { // Factory Function static VkResult CreateCodecConfig(int argc, const char *argv[], VkSharedBaseObj& encoderConfig); - void InitVideoProfile(); - int ParseArguments(int argc, const char *argv[]); virtual int DoParseArguments(int argc, const char *argv[]) { @@ -959,9 +955,12 @@ 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 VkResult InitVideoProfileCapabilities(const VulkanDeviceContext* vkDevCtx) { return VK_ERROR_INITIALIZATION_FAILED; }; + + // Returns the codec-specific profile identifier (must be set by InitCodecProfile first) + virtual uint32_t GetCodecProfile() = 0; - virtual uint32_t GetDefaultVideoProfileIdc() { return 0; }; + virtual bool DetermineLevelTier() = 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..fade4076 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigAV1.cpp +++ b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigAV1.cpp @@ -187,8 +187,36 @@ bool EncoderConfigAV1::InitSequenceHeader(StdVideoAV1SequenceHeader *seqHdr, return true; } -VkResult EncoderConfigAV1::InitDeviceCapabilities(const VulkanDeviceContext* vkDevCtx) +VkResult EncoderConfigAV1::InitVideoProfileCapabilities(const VulkanDeviceContext* vkDevCtx) { + if (encodeBitDepthLuma == 0) + encodeBitDepthLuma = input.bpp; + if (encodeBitDepthChroma == 0) + encodeBitDepthChroma = encodeBitDepthLuma; + + // If profile hasn't been specified, determine it based on bit depth and chroma subsampling + 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; + } + } + + videoCoreProfile = VkVideoCoreProfile::CreateEncodeProfile( + codec, encodeChromaSubsampling, + GetComponentBitDepthFlagBits(encodeBitDepthLuma), + GetComponentBitDepthFlagBits(encodeBitDepthChroma), + GetCodecProfile(), tuningMode); + VkResult result = VulkanVideoCapabilities::GetVideoEncodeCapabilities (vkDevCtx, videoCoreProfile, @@ -342,17 +370,14 @@ bool EncoderConfigAV1::DetermineLevelTier() } } if (lvl > STD_VIDEO_AV1_LEVEL_7_3) { - level = STD_VIDEO_AV1_LEVEL_7_3; - tier = 0; + return false; } - return true; } bool EncoderConfigAV1::InitRateControl() { - DetermineLevelTier(); - + // Level and tier are already initialized by DetermineLevelTier() // 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..01f8b383 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigAV1.h +++ b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigAV1.h @@ -101,17 +101,20 @@ struct EncoderConfigAV1 : public EncoderConfig { pic_width_in_sbs = DivUp(encodeWidth, 64); pic_height_in_sbs = DivUp(encodeHeight, 16); - if ((pic_width_in_sbs > 0) && (pic_height_in_sbs > 0)) { - return VK_SUCCESS; + if (pic_width_in_sbs == 0 || pic_height_in_sbs == 0) { + assert(!"Invalid pic_width_in_sbs and pic_height_in_sbs"); + return VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR; } - assert(!"Invalid pic_width_in_sbs and pic_height_in_sbs"); - return VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR; + return VK_SUCCESS; } - virtual VkResult InitDeviceCapabilities(const VulkanDeviceContext* vkDevCtx) override; + virtual VkResult InitVideoProfileCapabilities(const VulkanDeviceContext* vkDevCtx) override; - virtual uint32_t GetDefaultVideoProfileIdc() override { return STD_VIDEO_AV1_PROFILE_MAIN; } + virtual uint32_t GetCodecProfile() override { + assert(profile != STD_VIDEO_AV1_PROFILE_INVALID); + return static_cast(profile); + } virtual int8_t InitDpbCount() override; @@ -138,7 +141,7 @@ struct EncoderConfigAV1 : public EncoderConfig { bool ValidateLevel(uint32_t lLevel, uint32_t lTier); - bool DetermineLevelTier(); + virtual bool DetermineLevelTier() override; uint32_t GetLevelMaxBitrate(uint32_t lLevel, uint32_t lTier) { if (lLevel < STD_VIDEO_AV1_LEVEL_4_0) { @@ -185,8 +188,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..ad463762 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; + + // Set cpbBrNalFactor depending on whether the High (or greater) profile is + // being used. See table A-2 in the H.264 specification. + 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,29 +317,12 @@ 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) || - (profileIdc == STD_VIDEO_H264_PROFILE_IDC_MAIN)) { - fprintf(stderr, "The profile selected does not support transform_8x8_mode_flag, disabling it.\n"); - pps->flags.transform_8x8_mode_flag = false; - } - } else if (adaptiveTransformMode == ADAPTIVE_TRANSFORM_DISABLE) { - pps->flags.transform_8x8_mode_flag = false; + pps->flags.transform_8x8_mode_flag = (profileIdc >= STD_VIDEO_H264_PROFILE_IDC_HIGH); + if (!pps->flags.transform_8x8_mode_flag) + fprintf(stderr, "The transform_8x8_mode_flag has been disabled because the selected profile selected does not support it.\n"); } 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; - } - } + pps->flags.transform_8x8_mode_flag = false; } if (entropyCodingMode == ENTROPY_CODING_MODE_CABAC) { @@ -348,28 +338,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; @@ -390,10 +358,72 @@ bool EncoderConfigH264::InitSpsPpsParameters(StdVideoH264SequenceParameterSet *s return true; } -VkResult EncoderConfigH264::InitDeviceCapabilities(const VulkanDeviceContext* vkDevCtx) +StdVideoH264ProfileIdc EncoderConfigH264::GetNextLowerProfile(StdVideoH264ProfileIdc profile) +{ + switch (profile) { + case STD_VIDEO_H264_PROFILE_IDC_HIGH_444_PREDICTIVE: return STD_VIDEO_H264_PROFILE_IDC_HIGH; + case STD_VIDEO_H264_PROFILE_IDC_HIGH: return STD_VIDEO_H264_PROFILE_IDC_MAIN; + case STD_VIDEO_H264_PROFILE_IDC_MAIN: return STD_VIDEO_H264_PROFILE_IDC_BASELINE; + default: return STD_VIDEO_H264_PROFILE_IDC_INVALID; + } +} + +VkResult EncoderConfigH264::InitVideoProfileCapabilities(const VulkanDeviceContext* vkDevCtx) { - VkResult result = VulkanVideoCapabilities::GetVideoEncodeCapabilities + if (encodeBitDepthLuma == 0) + encodeBitDepthLuma = input.bpp; + if (encodeBitDepthChroma == 0) + encodeBitDepthChroma = encodeBitDepthLuma; + + // Track whether the profile was user-specified or auto-selected. + const bool autoSelected = (profileIdc == STD_VIDEO_H264_PROFILE_IDC_INVALID); + + if (autoSelected) { + // Select the highest profile required for the requested features. + // 8x8 transform support is hardware-dependent; it is verified after + // the capability query and the mode is adjusted accordingly. + bool use8x8Transform = (adaptiveTransformMode != ADAPTIVE_TRANSFORM_DISABLE); + + 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; + } + } + + // Determine the minimum profile required by the content format. + StdVideoH264ProfileIdc minProfile = STD_VIDEO_H264_PROFILE_IDC_BASELINE; + if ((tuningMode == VK_VIDEO_ENCODE_TUNING_MODE_LOSSLESS_KHR) || + (input.chromaSubsampling == VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR)) { + minProfile = STD_VIDEO_H264_PROFILE_IDC_HIGH_444_PREDICTIVE; + } else if (gopStructure.GetConsecutiveBFrameCount() > 0) { + minProfile = STD_VIDEO_H264_PROFILE_IDC_MAIN; + } + + // Probe from the desired profile down to minProfile, stopping at the first + // profile the hardware accepts. If the profile was user-specified, a single + // probe is made and failure is reported without fallback. + VkResult result = VK_SUCCESS; + + for (;;) { + videoCoreProfile = VkVideoCoreProfile::CreateEncodeProfile( + codec, encodeChromaSubsampling, + GetComponentBitDepthFlagBits(encodeBitDepthLuma), + GetComponentBitDepthFlagBits(encodeBitDepthChroma), + GetCodecProfile(), tuningMode); + + result = VulkanVideoCapabilities::GetVideoEncodeCapabilities (vkDevCtx, videoCoreProfile, videoCapabilities, videoEncodeCapabilities, @@ -401,17 +431,36 @@ VkResult EncoderConfigH264::InitDeviceCapabilities(const VulkanDeviceContext* vk quantizationMapCapabilities, h264QuantizationMapCapabilities, intraRefreshCapabilities); - if (result != VK_SUCCESS) { - // Distinguish between "not supported" and "actual error" - if (IsVideoUnsupportedResult(result)) { - // Not supported by hardware/driver - return VK_ERROR_INCOMPATIBLE_DRIVER - std::cerr << "*** Video encode capabilities not supported by hardware/driver (" - << VKVS_STRINGIFY(result) << ") ***" << std::endl; + if (result == VK_SUCCESS) { + break; + } + + if (!IsVideoUnsupportedResult(result)) { + std::cerr << "*** Error getting video encode capabilities: " << VKVS_STRINGIFY(result) << " ***" << std::endl; + return result; + } + + if (!autoSelected || (profileIdc == minProfile)) { + std::cerr << "*** H.264 profile " << profileIdc << " is not supported by the hardware/driver ***" << std::endl; return VK_ERROR_INCOMPATIBLE_DRIVER; } - // Actual error (e.g., out of memory) - std::cerr << "*** Error getting video capabilities: " << VKVS_STRINGIFY(result) << " ***" << std::endl; - return result; + + StdVideoH264ProfileIdc nextProfile = GetNextLowerProfile(profileIdc); + if (verbose) { + std::cout << "H.264 profile " << profileIdc << " not supported, retrying with profile " << nextProfile << std::endl; + } + profileIdc = nextProfile; + } + + // If adaptiveTransformMode is AUTOSELECT, set it to either ENABLE or DISABLE + // based on hardware capabilities and the chosen profile, so that subsequent + // code using this parameter can take a simple binary decision. + if (adaptiveTransformMode == ADAPTIVE_TRANSFORM_AUTOSELECT) { + bool hwSupports8x8 = (h264EncodeCapabilities.stdSyntaxFlags & + VK_VIDEO_ENCODE_H264_STD_TRANSFORM_8X8_MODE_FLAG_SET_BIT_KHR) != 0; + adaptiveTransformMode = (hwSupports8x8 && (profileIdc >= STD_VIDEO_H264_PROFILE_IDC_HIGH)) + ? ADAPTIVE_TRANSFORM_ENABLE + : ADAPTIVE_TRANSFORM_DISABLE; } if (verboseMsg) { @@ -491,42 +540,35 @@ VkResult EncoderConfigH264::InitDeviceCapabilities(const VulkanDeviceContext* vk return VK_SUCCESS; } -int8_t EncoderConfigH264::InitDpbCount() +bool EncoderConfigH264::DetermineLevelTier() { - dpbCount = 0; // TODO: What is the need for this? - - // 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); + if (levelIdc == STD_VIDEO_H264_LEVEL_IDC_INVALID) { + return false; } + return (levelIdc <= h264EncodeCapabilities.maxLevelIdc); +} + +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..ed41c527 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; @@ -163,6 +165,9 @@ struct EncoderConfigH264 : public EncoderConfig { uint32_t vbvBufferSize, double frameRate); + // Returns the next lower profile in the H.264 hierarchy, or INVALID at the bottom. + static StdVideoH264ProfileIdc GetNextLowerProfile(StdVideoH264ProfileIdc profile); + static void SetAspectRatio(StdVideoH264SequenceParameterSetVui *vui, int32_t width, int32_t height, int32_t darWidth, int32_t darHeight); @@ -177,17 +182,22 @@ struct EncoderConfigH264 : public EncoderConfig { pic_width_in_mbs = DivUp(encodeWidth, 16); pic_height_in_map_units = DivUp(encodeHeight, 16); - if ((pic_width_in_mbs > 0) && (pic_height_in_map_units > 0)) { - return VK_SUCCESS; + if (pic_width_in_mbs == 0 || pic_height_in_map_units == 0) { + assert(!"Invalid pic_width_in_mbs and pic_height_in_map_units"); + return VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR; } - assert(!"Invalid pic_width_in_mbs and pic_height_in_map_units"); - return VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR; + return VK_SUCCESS; } - virtual VkResult InitDeviceCapabilities(const VulkanDeviceContext* vkDevCtx) override; + virtual bool DetermineLevelTier() override; + + virtual VkResult InitVideoProfileCapabilities(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..38e688f6 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.cpp +++ b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.cpp @@ -111,8 +111,35 @@ int EncoderConfigH265::DoParseArguments(int argc, const char* argv[]) return 0; } -VkResult EncoderConfigH265::InitDeviceCapabilities(const VulkanDeviceContext* vkDevCtx) +VkResult EncoderConfigH265::InitVideoProfileCapabilities(const VulkanDeviceContext* vkDevCtx) { + if (encodeBitDepthLuma == 0) + encodeBitDepthLuma = input.bpp; + if (encodeBitDepthChroma == 0) + encodeBitDepthChroma = encodeBitDepthLuma; + + // If profile hasn't been specified, determine it based on bit depth and chroma subsampling + 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; + } + } + + videoCoreProfile = VkVideoCoreProfile::CreateEncodeProfile( + codec, encodeChromaSubsampling, + GetComponentBitDepthFlagBits(encodeBitDepthLuma), + GetComponentBitDepthFlagBits(encodeBitDepthChroma), + GetCodecProfile(), tuningMode); + VkResult result = VulkanVideoCapabilities::GetVideoEncodeCapabilities (vkDevCtx, videoCoreProfile, @@ -483,45 +510,39 @@ 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() +bool 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; } } if (levelIdx >= levelLimitsTblSize) { - assert(!"No suitable level selected"); + return false; } - - return profileTierLevel; + return (levelIdc <= h265EncodeCapabilities.maxLevelIdc); } + 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 DetermineLevelTier() + if (levelIdc >= levelLimitsTblSize) { assert(!"The h.265 level index is invalid"); return false; } @@ -529,7 +550,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 +574,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 +647,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 InitCodecProfile() / DetermineLevelTier() + 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 +659,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..8d6a3c7d 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,17 @@ struct EncoderConfigH265 : public EncoderConfig { if (result != VK_SUCCESS) { return result; } + // TODO: more h.265 parameters init ... return VK_SUCCESS; } - virtual VkResult InitDeviceCapabilities(const VulkanDeviceContext* vkDevCtx) override; + virtual VkResult InitVideoProfileCapabilities(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 +195,7 @@ struct EncoderConfigH265 : public EncoderConfig { StdVideoH265SequenceParameterSetVui* vui = nullptr); bool IsSuitableLevel(uint32_t levelIdx, bool highTier); - StdVideoH265ProfileTierLevel GetLevelTier(); + virtual bool DetermineLevelTier() override; void InitializeSpsRefPicSet(SpsH265 *pSps); }; diff --git a/vk_video_encoder/libs/VkVideoEncoder/VkVideoEncoder.cpp b/vk_video_encoder/libs/VkVideoEncoder/VkVideoEncoder.cpp index 93209af9..cfeabc4c 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkVideoEncoder.cpp +++ b/vk_video_encoder/libs/VkVideoEncoder/VkVideoEncoder.cpp @@ -779,15 +779,17 @@ VkResult VkVideoEncoder::InitEncoder(VkSharedBaseObj& encoderConf m_encoderConfig = encoderConfig; - // Update the video profile - encoderConfig->InitVideoProfile(); - - result = encoderConfig->InitDeviceCapabilities(m_vkDevCtx); + result = encoderConfig->InitVideoProfileCapabilities(m_vkDevCtx); if (result != VK_SUCCESS) { - std::cerr << "InitDeviceCapabilties failed" << std::endl; + std::cerr << "InitVideoProfileCapabilities failed" << std::endl; return result; } + if (!encoderConfig->DetermineLevelTier()) { + std::cerr << "Failed to determine a suitable level for the given encoding parameters" << std::endl; + return VK_ERROR_INITIALIZATION_FAILED; + } + if (encoderConfig->verbose) { const VkVideoCapabilitiesKHR& vidCaps = encoderConfig->videoCapabilities; const VkVideoEncodeCapabilitiesKHR& encCaps = encoderConfig->videoEncodeCapabilities; @@ -889,7 +891,7 @@ VkResult VkVideoEncoder::InitEncoder(VkSharedBaseObj& encoderConf // Reconfigure the gopStructure structure because the device may not support // specific GOP structure. For example it may not support B-frames. - // gopStructure.Init() should be called after encoderConfig->InitDeviceCapabilities(). + // gopStructure.Init() should be called after encoderConfig->InitVideoProfileCapabilities(). m_encoderConfig->gopStructure.Init(m_encoderConfig->numFrames); if (encoderConfig->GetMaxBFrameCount() < m_encoderConfig->gopStructure.GetConsecutiveBFrameCount()) { if (m_encoderConfig->verbose) {