diff --git a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfig.cpp b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfig.cpp index 9ab2ff79..bddbb4df 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfig.cpp +++ b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfig.cpp @@ -822,24 +822,14 @@ VkResult EncoderConfig::CreateCodecConfig(int argc, const char *argv[], return VK_ERROR_INITIALIZATION_FAILED; } -void EncoderConfig::InitVideoProfile() +VkVideoCoreProfile EncoderConfig::MakeVideoProfile(uint32_t codecProfile) { - 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); + return VkVideoCoreProfile::CreateEncodeProfile( + codec, encodeChromaSubsampling, + GetComponentBitDepthFlagBits(encodeBitDepthLuma), + GetComponentBitDepthFlagBits(encodeBitDepthChroma), + codecProfile, + tuningMode); } bool EncoderConfig::InitRateControl() diff --git a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfig.h b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfig.h index d1ad322a..56e996dc 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfig.h +++ b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfig.h @@ -695,7 +695,6 @@ struct EncoderConfig : public VkVideoRefCountBase { bool noDeviceFallback; VkVideoCodecOperationFlagBitsKHR codec; bool useDpbArray; - uint32_t videoProfileIdc; uint32_t numInputImages; EncoderInputImageParameters input; uint8_t encodeBitDepthLuma; @@ -803,7 +802,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) @@ -925,7 +923,7 @@ struct EncoderConfig : public VkVideoRefCountBase { // Factory Function static VkResult CreateCodecConfig(int argc, const char *argv[], VkSharedBaseObj& encoderConfig); - void InitVideoProfile(); + VkVideoCoreProfile MakeVideoProfile(uint32_t codecProfile); int ParseArguments(int argc, const char *argv[]); @@ -965,13 +963,21 @@ struct EncoderConfig : public VkVideoRefCountBase { return VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR; } + if (encodeBitDepthLuma == 0) { + encodeBitDepthLuma = input.bpp; + } + + if (encodeBitDepthChroma == 0) { + encodeBitDepthChroma = encodeBitDepthLuma; + } + return VK_SUCCESS; } // 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; }; - 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 85552f85..d79ef46f 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigAV1.cpp +++ b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigAV1.cpp @@ -199,8 +199,28 @@ bool EncoderConfigAV1::InitSequenceHeader(StdVideoAV1SequenceHeader *seqHdr, return true; } -VkResult EncoderConfigAV1::InitDeviceCapabilities(const VulkanDeviceContext* vkDevCtx) +VkResult EncoderConfigAV1::InitVideoProfileCapabilities(const VulkanDeviceContext* vkDevCtx) { + // 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 ((encodeBitDepthLuma > 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; + } + } + + assert(profile != STD_VIDEO_AV1_PROFILE_INVALID); + videoCoreProfile = MakeVideoProfile(static_cast(profile)); + const bool feedback2Requested = enablePictureFeedback || enablePixelCountFeedback || enableSkippedPixelCountFeedback || enablePerPartitionFeedback; videoEncodeFeedback2Capabilities = VkVideoEncodeFeedback2CapabilitiesKHR @@ -376,17 +396,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 68c794da..8f88e485 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigAV1.h +++ b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigAV1.h @@ -109,9 +109,7 @@ struct EncoderConfigAV1 : public EncoderConfig { return VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR; } - virtual VkResult InitDeviceCapabilities(const VulkanDeviceContext* vkDevCtx) override; - - virtual uint32_t GetDefaultVideoProfileIdc() override { return STD_VIDEO_AV1_PROFILE_MAIN; } + virtual VkResult InitVideoProfileCapabilities(const VulkanDeviceContext* vkDevCtx) override; virtual int8_t InitDpbCount() override; @@ -138,7 +136,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 +183,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 19d3d072..ed0002b2 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH264.cpp +++ b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH264.cpp @@ -64,6 +64,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++) { @@ -72,8 +79,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; } @@ -311,29 +318,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 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) { @@ -349,28 +339,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; @@ -391,10 +359,64 @@ 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 + // 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) || + (encodeChromaSubsampling == 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) || + (encodeChromaSubsampling == 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 (;;) { + assert(profileIdc != STD_VIDEO_H264_PROFILE_IDC_INVALID); + videoCoreProfile = MakeVideoProfile(static_cast(profileIdc)); + + result = VulkanVideoCapabilities::GetVideoEncodeCapabilities (vkDevCtx, videoCoreProfile, videoCapabilities, videoEncodeCapabilities, @@ -402,17 +424,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 (verbose) { @@ -503,42 +544,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..e07da2d1 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); @@ -185,9 +190,9 @@ struct EncoderConfigH264 : public EncoderConfig { return VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR; } - virtual VkResult InitDeviceCapabilities(const VulkanDeviceContext* vkDevCtx) override; + virtual bool DetermineLevelTier() override; - virtual uint32_t GetDefaultVideoProfileIdc() override { return STD_VIDEO_H264_PROFILE_IDC_HIGH; }; + virtual VkResult InitVideoProfileCapabilities(const VulkanDeviceContext* vkDevCtx) override; // 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 db42884a..87dad47b 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.cpp +++ b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.cpp @@ -112,8 +112,27 @@ int EncoderConfigH265::DoParseArguments(int argc, const char* argv[]) return 0; } -VkResult EncoderConfigH265::InitDeviceCapabilities(const VulkanDeviceContext* vkDevCtx) +VkResult EncoderConfigH265::InitVideoProfileCapabilities(const VulkanDeviceContext* vkDevCtx) { + // 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 (encodeBitDepthLuma == 8) { + profile = STD_VIDEO_H265_PROFILE_IDC_MAIN; + } else if (encodeBitDepthLuma <= 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; + } + } + + assert(profile != STD_VIDEO_H265_PROFILE_IDC_INVALID); + videoCoreProfile = MakeVideoProfile(static_cast(profile)); + VkResult result = VulkanVideoCapabilities::GetVideoEncodeCapabilities (vkDevCtx, videoCoreProfile, @@ -440,6 +459,17 @@ bool EncoderConfigH265::IsSuitableLevel(uint32_t levelIdx, bool highTier) return false; } + if (frameRateNumerator > 0 && frameRateDenominator > 0) { + double frameRate = (double)frameRateNumerator / frameRateDenominator; + if (picSizeInSamples * frameRate > levelLimits[levelIdx].maxLumaSr) { + return false; + } + } + + // XXX: We currently don't check the DPB size as per Annex A.4.2 (max DPB pictures as a function of + // picture size relative to MaxLumaPS), as it is handled separately in VerifyDpbSize(). + // This is fine because we don't allow the user to set the DPB size today. + if (widthCtbAligned > (uint32_t)sqrt(levelLimits[levelIdx].maxLumaPS * 8.0)) { return false; } @@ -498,45 +528,38 @@ 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; } @@ -544,7 +567,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) { @@ -568,7 +591,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; @@ -641,7 +664,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 InitVideoProfileCapabilities() / 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 @@ -651,21 +676,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 56dfca98..9440ef59 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.h +++ b/vk_video_encoder/libs/VkVideoEncoder/VkEncoderConfigH265.h @@ -85,8 +85,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) @@ -149,13 +149,12 @@ 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 uint32_t GetDefaultVideoProfileIdc() override { return STD_VIDEO_H265_PROFILE_IDC_MAIN; }; + virtual VkResult InitVideoProfileCapabilities(const VulkanDeviceContext* vkDevCtx) override; // 1. First h.265 determine the number of the Dpb buffers required virtual int8_t InitDpbCount() override; @@ -193,7 +192,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 5a9bfc61..47d471f9 100644 --- a/vk_video_encoder/libs/VkVideoEncoder/VkVideoEncoder.cpp +++ b/vk_video_encoder/libs/VkVideoEncoder/VkVideoEncoder.cpp @@ -807,15 +807,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; @@ -969,7 +971,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) {