blob: f633901a068f86141561dc32c6febea5bc846b58 [file] [log] [blame]
/*
* Copyright (c) 2022-2025 The Khronos Group Inc.
* Copyright (c) 2022-2025 RasterGrid Kft.
* Modifications Copyright (C) 2024 Advanced Micro Devices, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*/
#pragma once
#include "layer_validation_tests.h"
#include <vulkan/utility/vk_safe_struct.hpp>
#include <vk_video/vulkan_video_codecs_common.h>
#include <vk_video/vulkan_video_codec_h264std.h>
#include <vk_video/vulkan_video_codec_h264std_decode.h>
#include <vk_video/vulkan_video_codec_h264std_encode.h>
#include <vk_video/vulkan_video_codec_h265std.h>
#include <vk_video/vulkan_video_codec_h265std_decode.h>
#include <vk_video/vulkan_video_codec_h265std_encode.h>
#include <vk_video/vulkan_video_codec_av1std.h>
#include <vk_video/vulkan_video_codec_av1std_decode.h>
#include <vk_video/vulkan_video_codec_av1std_encode.h>
#include <vector>
#include <unordered_set>
#include <cmath>
class VideoConfig {
public:
VideoConfig() { session_create_info_.queueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; }
operator bool() const { return profile_.videoCodecOperation != VK_VIDEO_CODEC_OPERATION_NONE_KHR; }
uint32_t QueueFamilyIndex() const { return session_create_info_.queueFamilyIndex; }
const VkVideoProfileInfoKHR* Profile() const { return profile_.ptr(); }
VkVideoProfileInfoKHR* Profile() { return profile_.ptr(); }
const VkExtensionProperties* StdVersion() const { return &caps_.stdHeaderVersion; }
const VkVideoSessionCreateInfoKHR* SessionCreateInfo() const { return session_create_info_.ptr(); }
VkVideoSessionCreateInfoKHR* SessionCreateInfo() { return session_create_info_.ptr(); }
const VkVideoSessionParametersCreateInfoKHR* SessionParamsCreateInfo() const { return session_params_create_info_.ptr(); }
VkVideoSessionParametersCreateInfoKHR* SessionParamsCreateInfo() { return session_params_create_info_.ptr(); }
const VkVideoFormatPropertiesKHR* PictureFormatProps() const { return picture_format_props_.data(); }
const VkVideoFormatPropertiesKHR* DpbFormatProps() const { return dpb_format_props_.data(); }
const VkVideoFormatPropertiesKHR* QuantDeltaMapProps() const {
return quant_delta_map_format_props_.size() > 0 ? quant_delta_map_format_props_[0].ptr() : nullptr;
}
const VkVideoFormatPropertiesKHR* EmphasisMapProps() const {
return emphasis_map_format_props_.size() > 0 ? emphasis_map_format_props_[0].ptr() : nullptr;
}
const std::vector<VkVideoFormatPropertiesKHR>& SupportedPictureFormatProps() const { return picture_format_props_; }
const std::vector<VkVideoFormatPropertiesKHR>& SupportedDpbFormatProps() const { return dpb_format_props_; }
const std::vector<vku::safe_VkVideoFormatPropertiesKHR>& SupportedQuantDeltaMapProps() const {
return quant_delta_map_format_props_;
}
const std::vector<vku::safe_VkVideoFormatPropertiesKHR>& SupportedEmphasisMapProps() const {
return emphasis_map_format_props_;
}
static VkExtent2D GetQuantMapTexelSize(const VkVideoFormatPropertiesKHR& format_props) {
auto quant_map_props = vku::FindStructInPNextChain<VkVideoFormatQuantizationMapPropertiesKHR>(format_props.pNext);
assert(quant_map_props != nullptr);
return quant_map_props->quantizationMapTexelSize;
}
static VkExtent2D GetQuantMapTexelSize(const vku::safe_VkVideoFormatPropertiesKHR& format_props) {
return GetQuantMapTexelSize(*format_props.ptr());
}
VkExtent2D QuantDeltaMapTexelSize() const {
assert(QuantDeltaMapProps() != nullptr);
return GetQuantMapTexelSize(*QuantDeltaMapProps());
}
VkExtent2D EmphasisMapTexelSize() const {
assert(EmphasisMapProps() != nullptr);
return GetQuantMapTexelSize(*EmphasisMapProps());
}
uint32_t DpbSlotCount() const { return session_create_info_.maxDpbSlots; }
VkExtent2D MaxCodedExtent() const { return session_create_info_.maxCodedExtent; }
uint32_t MaxPartitionCount() const {
switch (profile_.videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
return EncodeCapsH264()->maxSliceCount;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
return EncodeCapsH265()->maxSliceSegmentCount;
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:
return EncodeCapsAV1()->maxTiles.width * EncodeCapsAV1()->maxTiles.height;
default:
assert(false);
return 0;
}
}
void SetQueueFamilyIndex(uint32_t queue_family_index) { session_create_info_.queueFamilyIndex = queue_family_index; }
void SetCodecProfile(void* profile) {
auto codec_specific = reinterpret_cast<VkBaseInStructure*>(profile);
auto pNext = reinterpret_cast<const VkBaseInStructure*>(profile_.pNext);
profile_.pNext = codec_specific;
codec_specific->pNext = pNext;
}
VkVideoCapabilitiesKHR* Caps() { return caps_.ptr(); }
const VkVideoCapabilitiesKHR* Caps() const { return caps_.ptr(); }
const VkVideoDecodeCapabilitiesKHR* DecodeCaps() const { return vku::FindStructInPNextChain<VkVideoDecodeCapabilitiesKHR>(caps_.pNext); }
const VkVideoDecodeH264CapabilitiesKHR* DecodeCapsH264() const {
assert(profile_.videoCodecOperation == VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR);
return vku::FindStructInPNextChain<VkVideoDecodeH264CapabilitiesKHR>(caps_.pNext);
}
const VkVideoDecodeH265CapabilitiesKHR* DecodeCapsH265() const {
assert(profile_.videoCodecOperation == VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR);
return vku::FindStructInPNextChain<VkVideoDecodeH265CapabilitiesKHR>(caps_.pNext);
}
const VkVideoEncodeCapabilitiesKHR* EncodeCaps() const {
return vku::FindStructInPNextChain<VkVideoEncodeCapabilitiesKHR>(caps_.pNext);
}
const VkVideoDecodeAV1CapabilitiesKHR* DecodeCapsAV1() const {
assert(profile_.videoCodecOperation == VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR);
return vku::FindStructInPNextChain<VkVideoDecodeAV1CapabilitiesKHR>(caps_.pNext);
}
const VkVideoDecodeAV1CapabilitiesKHR* DecodeCapsVP9() const {
assert(profile_.videoCodecOperation == VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR);
return vku::FindStructInPNextChain<VkVideoDecodeAV1CapabilitiesKHR>(caps_.pNext);
}
const VkVideoEncodeH264CapabilitiesKHR* EncodeCapsH264() const {
assert(profile_.videoCodecOperation == VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR);
return vku::FindStructInPNextChain<VkVideoEncodeH264CapabilitiesKHR>(caps_.pNext);
}
const VkVideoEncodeH265CapabilitiesKHR* EncodeCapsH265() const {
assert(profile_.videoCodecOperation == VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR);
return vku::FindStructInPNextChain<VkVideoEncodeH265CapabilitiesKHR>(caps_.pNext);
}
const VkVideoEncodeQuantizationMapCapabilitiesKHR* EncodeQuantizationMapCaps() const {
return vku::FindStructInPNextChain<VkVideoEncodeQuantizationMapCapabilitiesKHR>(caps_.pNext);
}
const VkVideoEncodeIntraRefreshCapabilitiesKHR* EncodeIntraRefreshCaps() const {
return vku::FindStructInPNextChain<VkVideoEncodeIntraRefreshCapabilitiesKHR>(caps_.pNext);
}
const VkVideoEncodeAV1CapabilitiesKHR* EncodeCapsAV1() const {
assert(profile_.videoCodecOperation == VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR);
return vku::FindStructInPNextChain<VkVideoEncodeAV1CapabilitiesKHR>(caps_.pNext);
}
VkVideoEncodeQualityLevelPropertiesKHR* EncodeQualityLevelProps() { return encode_quality_level_props_.ptr(); }
bool SupportsDecodeOutputDistinct() const {
return DecodeCaps()->flags & VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR;
}
bool SupportsDecodeOutputCoincide() const {
return DecodeCaps()->flags & VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR;
}
VkVideoEncodeRateControlModeFlagBitsKHR GetAnySupportedRateControlMode() const {
// Return a "real" rate control mode here, excluding the "DISABLED" mode
assert(EncodeCaps()->rateControlModes > VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DISABLED_BIT_KHR);
return static_cast<VkVideoEncodeRateControlModeFlagBitsKHR>(1
<< static_cast<uint32_t>(log2(EncodeCaps()->rateControlModes)));
}
bool SupportsRateControlMode(VkVideoEncodeRateControlModeFlagBitsKHR rate_control_mode) const {
return rate_control_mode == VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DEFAULT_KHR ||
(EncodeCaps()->rateControlModes & rate_control_mode) != 0;
}
uint32_t MaxEncodeH264MBCount() const {
assert(profile_.videoCodecOperation == VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR);
const uint32_t mb_size = 16;
return ((MaxCodedExtent().width + mb_size - 1) / mb_size) * ((MaxCodedExtent().height + mb_size - 1) / mb_size);
}
uint32_t MaxEncodeH264MBRowCount() const {
assert(profile_.videoCodecOperation == VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR);
const uint32_t mb_size = 16;
return (MaxCodedExtent().height + mb_size - 1) / mb_size;
}
uint32_t MaxEncodeH265CTBSize() const {
assert(profile_.videoCodecOperation == VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR);
return 16 * static_cast<uint32_t>(log2(EncodeCapsH265()->ctbSizes));
}
uint32_t MaxEncodeH265CTBCount() const {
assert(profile_.videoCodecOperation == VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR);
const uint32_t max_ctb_size = MaxEncodeH265CTBSize();
return ((MaxCodedExtent().width + max_ctb_size - 1) / max_ctb_size) *
((MaxCodedExtent().height + max_ctb_size - 1) / max_ctb_size);
}
uint32_t MaxEncodeH265CTBRowCount() const {
assert(profile_.videoCodecOperation == VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR);
const uint32_t max_ctb_size = MaxEncodeH265CTBSize();
return (MaxCodedExtent().height + max_ctb_size - 1) / max_ctb_size;
}
int32_t ClampH264Qp(int32_t qp) const {
auto caps = EncodeCapsH264();
return std::clamp(qp, caps->minQp, caps->maxQp);
}
int32_t ClampH265Qp(int32_t qp) const {
auto caps = EncodeCapsH265();
return std::clamp(qp, caps->minQp, caps->maxQp);
}
uint32_t ClampAV1QIndex(uint32_t qindex) const {
auto caps = EncodeCapsAV1();
return std::clamp(qindex, caps->minQIndex, caps->maxQIndex);
}
VkVideoEncodeAV1PredictionModeKHR GetAnySupportedAV1PredictionMode() const {
auto caps = EncodeCapsAV1();
if (caps->maxBidirectionalCompoundReferenceCount > 0) {
return VK_VIDEO_ENCODE_AV1_PREDICTION_MODE_BIDIRECTIONAL_COMPOUND_KHR;
} else if (caps->maxUnidirectionalCompoundReferenceCount > 0) {
return VK_VIDEO_ENCODE_AV1_PREDICTION_MODE_UNIDIRECTIONAL_COMPOUND_KHR;
} else if (caps->maxSingleReferenceCount > 0) {
return VK_VIDEO_ENCODE_AV1_PREDICTION_MODE_SINGLE_REFERENCE_KHR;
} else {
assert(false);
return VK_VIDEO_ENCODE_AV1_PREDICTION_MODE_INTRA_ONLY_KHR;
}
}
bool IsDecode() const { return is_decode_; }
bool IsEncode() const { return is_encode_; }
void SetDecode() { is_decode_ = true; }
void SetEncode() { is_encode_ = true; }
void EnableProfileIndependentResources() { use_profile_independent_resources_ = true; }
bool UseProfileIndependentResources() const { return use_profile_independent_resources_; }
VkVideoEncodeIntraRefreshModeFlagBitsKHR GetAnySupportedIntraRefreshMode() const {
assert(EncodeIntraRefreshCaps()->intraRefreshModes != 0);
return static_cast<VkVideoEncodeIntraRefreshModeFlagBitsKHR>(
1 << static_cast<uint32_t>(log2(EncodeIntraRefreshCaps()->intraRefreshModes)));
}
void SetVideoEncodeIntraRefreshMode(VkVideoEncodeIntraRefreshModeFlagBitsKHR mode) {
auto intra_refresh_info =
vku::FindStructInPNextChain<VkVideoEncodeSessionIntraRefreshCreateInfoKHR>(session_create_info_.pNext);
if (intra_refresh_info) {
const_cast<VkVideoEncodeSessionIntraRefreshCreateInfoKHR*>(intra_refresh_info)->intraRefreshMode = mode;
} else {
auto safe_intra_refresh_info = new vku::safe_VkVideoEncodeSessionIntraRefreshCreateInfoKHR();
safe_intra_refresh_info->intraRefreshMode = mode;
safe_intra_refresh_info->pNext = session_create_info_.pNext;
session_create_info_.pNext = safe_intra_refresh_info;
}
intra_refresh_mode_ = mode;
}
VkVideoEncodeIntraRefreshModeFlagBitsKHR GetVideoEncodeIntraRefreshMode() const { return intra_refresh_mode_; }
void SetCodecCapsChain(void* codec_chain) {
assert(caps_.pNext == nullptr);
caps_.pNext = codec_chain;
}
void SetCodecEncodeQualityLevelPropsChain(void* codec_chain) {
assert(encode_quality_level_props_.pNext == nullptr);
encode_quality_level_props_.pNext = codec_chain;
}
bool NeedsSessionParams() const { return session_params_create_info_.pNext != nullptr; }
void SetCodecSessionParamsInfo(const void* paramsInfo) {
assert(session_params_create_info_.pNext == nullptr);
session_params_create_info_.pNext = paramsInfo;
}
void SetFormatProps(const std::vector<VkVideoFormatPropertiesKHR>& picture_format_props,
const std::vector<VkVideoFormatPropertiesKHR>& dpb_format_props) {
picture_format_props_.clear();
dpb_format_props_.clear();
std::copy_if(picture_format_props.begin(), picture_format_props.end(), std::back_inserter(picture_format_props_),
IsTestableVideoFormat);
std::copy_if(dpb_format_props.begin(), dpb_format_props.end(), std::back_inserter(dpb_format_props_),
IsTestableVideoFormat);
session_create_info_.pictureFormat = picture_format_props[0].format;
session_create_info_.referencePictureFormat = dpb_format_props[0].format;
}
void SetQuantizationMapFormatProps(const std::vector<VkVideoFormatPropertiesKHR>& delta_map_format_props,
const std::vector<VkVideoFormatPropertiesKHR>& emphasis_map_format_props) {
quant_delta_map_format_props_.clear();
emphasis_map_format_props_.clear();
for (const auto& delta_map_format_prop : delta_map_format_props) {
if (IsTestableVideoFormat(delta_map_format_prop)) {
quant_delta_map_format_props_.emplace_back(&delta_map_format_prop);
}
}
for (const auto& emphasis_map_format_prop : emphasis_map_format_props) {
if (IsTestableVideoFormat(emphasis_map_format_prop)) {
emphasis_map_format_props_.emplace_back(&emphasis_map_format_prop);
}
}
}
StdVideoH264SequenceParameterSet CreateH264SPS(uint8_t sps_id) const {
StdVideoH264SequenceParameterSet sps{};
sps.seq_parameter_set_id = sps_id;
if (is_decode_) {
auto profile = vku::FindStructInPNextChain<VkVideoDecodeH264ProfileInfoKHR>(profile_.pNext);
assert(profile != nullptr);
sps.profile_idc = profile->stdProfileIdc;
} else if (is_encode_) {
auto profile = vku::FindStructInPNextChain<VkVideoEncodeH264ProfileInfoKHR>(profile_.pNext);
assert(profile != nullptr);
sps.profile_idc = profile->stdProfileIdc;
}
sps.flags.frame_mbs_only_flag = 1;
sps.chroma_format_idc = static_cast<StdVideoH264ChromaFormatIdc>(log2(profile_.chromaSubsampling));
sps.bit_depth_luma_minus8 = static_cast<uint8_t>(log2(profile_.lumaBitDepth));
sps.bit_depth_chroma_minus8 = static_cast<uint8_t>(log2(profile_.chromaBitDepth));
sps.pic_width_in_mbs_minus1 = (caps_.minCodedExtent.width + 15) / 16 - 1;
sps.pic_height_in_map_units_minus1 = (caps_.minCodedExtent.height + 15) / 16 - 1;
return sps;
}
StdVideoH264PictureParameterSet CreateH264PPS(uint8_t sps_id, uint8_t pps_id) const {
StdVideoH264PictureParameterSet pps{};
pps.seq_parameter_set_id = sps_id;
pps.pic_parameter_set_id = pps_id;
return pps;
}
StdVideoH265VideoParameterSet CreateH265VPS(uint8_t vps_id) const {
StdVideoH265VideoParameterSet vps{};
vps.vps_video_parameter_set_id = vps_id;
return vps;
}
StdVideoH265SequenceParameterSet CreateH265SPS(uint8_t vps_id, uint8_t sps_id) const {
StdVideoH265SequenceParameterSet sps{};
sps.sps_video_parameter_set_id = vps_id;
sps.sps_seq_parameter_set_id = sps_id;
return sps;
}
StdVideoH265PictureParameterSet CreateH265PPS(uint8_t vps_id, uint8_t sps_id, uint8_t pps_id) const {
StdVideoH265PictureParameterSet pps{};
pps.sps_video_parameter_set_id = vps_id;
pps.pps_seq_parameter_set_id = sps_id;
pps.pps_pic_parameter_set_id = pps_id;
return pps;
}
StdVideoAV1SequenceHeader CreateAV1SequenceHeader() const {
StdVideoAV1SequenceHeader seq_header{};
if (is_decode_) {
auto profile = vku::FindStructInPNextChain<VkVideoDecodeAV1ProfileInfoKHR>(profile_.pNext);
assert(profile != nullptr);
seq_header.seq_profile = profile->stdProfile;
} else if (is_encode_) {
auto profile = vku::FindStructInPNextChain<VkVideoEncodeAV1ProfileInfoKHR>(profile_.pNext);
assert(profile != nullptr);
seq_header.seq_profile = profile->stdProfile;
}
seq_header.frame_width_bits_minus_1 = static_cast<uint8_t>(log2(caps_.minCodedExtent.width));
seq_header.frame_height_bits_minus_1 = static_cast<uint8_t>(log2(caps_.minCodedExtent.height));
seq_header.max_frame_width_minus_1 = static_cast<uint16_t>(caps_.minCodedExtent.width - 1);
seq_header.max_frame_height_minus_1 = static_cast<uint16_t>(caps_.minCodedExtent.height - 1);
return seq_header;
}
StdVideoH264SequenceParameterSet* DecodeH264SPS() {
auto create_info =
vku::FindStructInPNextChain<VkVideoDecodeH264SessionParametersCreateInfoKHR>(session_params_create_info_.pNext);
return const_cast<StdVideoH264SequenceParameterSet*>(create_info->pParametersAddInfo->pStdSPSs);
}
StdVideoH264PictureParameterSet* DecodeH264PPS() {
auto create_info =
vku::FindStructInPNextChain<VkVideoDecodeH264SessionParametersCreateInfoKHR>(session_params_create_info_.pNext);
return const_cast<StdVideoH264PictureParameterSet*>(create_info->pParametersAddInfo->pStdPPSs);
}
StdVideoH265VideoParameterSet* DecodeH265VPS() {
auto create_info =
vku::FindStructInPNextChain<VkVideoDecodeH265SessionParametersCreateInfoKHR>(session_params_create_info_.pNext);
return const_cast<StdVideoH265VideoParameterSet*>(create_info->pParametersAddInfo->pStdVPSs);
}
StdVideoH265SequenceParameterSet* DecodeH265SPS() {
auto create_info =
vku::FindStructInPNextChain<VkVideoDecodeH265SessionParametersCreateInfoKHR>(session_params_create_info_.pNext);
return const_cast<StdVideoH265SequenceParameterSet*>(create_info->pParametersAddInfo->pStdSPSs);
}
StdVideoH265PictureParameterSet* DecodeH265PPS() {
auto create_info =
vku::FindStructInPNextChain<VkVideoDecodeH265SessionParametersCreateInfoKHR>(session_params_create_info_.pNext);
return const_cast<StdVideoH265PictureParameterSet*>(create_info->pParametersAddInfo->pStdPPSs);
}
StdVideoAV1SequenceHeader* DecodeAV1SequenceHeader() {
auto create_info =
vku::FindStructInPNextChain<VkVideoDecodeAV1SessionParametersCreateInfoKHR>(session_params_create_info_.pNext);
return const_cast<StdVideoAV1SequenceHeader*>(create_info->pStdSequenceHeader);
}
StdVideoH264SequenceParameterSet* EncodeH264SPS() {
auto create_info =
vku::FindStructInPNextChain<VkVideoEncodeH264SessionParametersCreateInfoKHR>(session_params_create_info_.pNext);
return const_cast<StdVideoH264SequenceParameterSet*>(create_info->pParametersAddInfo->pStdSPSs);
}
StdVideoH264PictureParameterSet* EncodeH264PPS() {
auto create_info =
vku::FindStructInPNextChain<VkVideoEncodeH264SessionParametersCreateInfoKHR>(session_params_create_info_.pNext);
return const_cast<StdVideoH264PictureParameterSet*>(create_info->pParametersAddInfo->pStdPPSs);
}
StdVideoH265VideoParameterSet* EncodeH265VPS() {
auto create_info =
vku::FindStructInPNextChain<VkVideoEncodeH265SessionParametersCreateInfoKHR>(session_params_create_info_.pNext);
return const_cast<StdVideoH265VideoParameterSet*>(create_info->pParametersAddInfo->pStdVPSs);
}
StdVideoH265SequenceParameterSet* EncodeH265SPS() {
auto create_info =
vku::FindStructInPNextChain<VkVideoEncodeH265SessionParametersCreateInfoKHR>(session_params_create_info_.pNext);
return const_cast<StdVideoH265SequenceParameterSet*>(create_info->pParametersAddInfo->pStdSPSs);
}
StdVideoH265PictureParameterSet* EncodeH265PPS() {
auto create_info =
vku::FindStructInPNextChain<VkVideoEncodeH265SessionParametersCreateInfoKHR>(session_params_create_info_.pNext);
return const_cast<StdVideoH265PictureParameterSet*>(create_info->pParametersAddInfo->pStdPPSs);
}
StdVideoAV1SequenceHeader* EncodeAV1SequenceHeader() {
auto create_info =
vku::FindStructInPNextChain<VkVideoEncodeAV1SessionParametersCreateInfoKHR>(session_params_create_info_.pNext);
return const_cast<StdVideoAV1SequenceHeader*>(create_info->pStdSequenceHeader);
}
void UpdateMaxCodedExtent(const VkExtent2D& max_coded_extent) {
SessionCreateInfo()->maxCodedExtent = max_coded_extent;
switch (profile_.videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
[[fallthrough]];
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR: {
auto sps = IsDecode() ? DecodeH264SPS() : EncodeH264SPS();
sps->pic_width_in_mbs_minus1 = (max_coded_extent.width + 15) / 16 - 1;
sps->pic_height_in_map_units_minus1 = (max_coded_extent.height + 15) / 16 - 1;
break;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
[[fallthrough]];
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR: {
break;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR:
[[fallthrough]];
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR: {
auto seq_header = IsDecode() ? DecodeAV1SequenceHeader() : EncodeAV1SequenceHeader();
seq_header->frame_width_bits_minus_1 = static_cast<uint8_t>(log2(max_coded_extent.width));
seq_header->frame_height_bits_minus_1 = static_cast<uint8_t>(log2(max_coded_extent.height));
seq_header->max_frame_width_minus_1 = static_cast<uint16_t>(max_coded_extent.width - 1);
seq_header->max_frame_height_minus_1 = static_cast<uint16_t>(max_coded_extent.height - 1);
break;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR:
// VP9 needs no action here
break;
default:
assert(false);
}
}
private:
bool is_decode_{false};
bool is_encode_{false};
bool use_profile_independent_resources_{false};
vku::safe_VkVideoProfileInfoKHR profile_{};
vku::safe_VkVideoCapabilitiesKHR caps_{};
vku::safe_VkVideoEncodeQualityLevelPropertiesKHR encode_quality_level_props_{};
vku::safe_VkVideoSessionCreateInfoKHR session_create_info_{};
vku::safe_VkVideoSessionParametersCreateInfoKHR session_params_create_info_{};
std::vector<VkVideoFormatPropertiesKHR> picture_format_props_{};
std::vector<VkVideoFormatPropertiesKHR> dpb_format_props_{};
std::vector<vku::safe_VkVideoFormatPropertiesKHR> quant_delta_map_format_props_{};
std::vector<vku::safe_VkVideoFormatPropertiesKHR> emphasis_map_format_props_{};
VkVideoEncodeIntraRefreshModeFlagBitsKHR intra_refresh_mode_{VK_VIDEO_ENCODE_INTRA_REFRESH_MODE_NONE_KHR};
static bool IsTestableVideoFormat(const VkVideoFormatPropertiesKHR& format) {
return format.imageTiling == VK_IMAGE_TILING_LINEAR || format.imageTiling == VK_IMAGE_TILING_OPTIMAL;
}
};
class BitstreamBuffer {
public:
BitstreamBuffer(vkt::Device* device, const VideoConfig& config, VkDeviceSize size, bool is_protected = false)
: device_(device),
size_(size),
memory_(VK_NULL_HANDLE),
buffer_(VK_NULL_HANDLE),
dep_info_(vku::InitStruct<VkDependencyInfo>()),
barrier_(vku::InitStruct<VkBufferMemoryBarrier2>()) {
if (config.UseProfileIndependentResources()) {
Init(nullptr, size, config.Profile()->videoCodecOperation, is_protected);
} else {
VkVideoProfileListInfoKHR profile_list = vku::InitStructHelper();
profile_list.profileCount = 1;
profile_list.pProfiles = config.Profile();
Init(&profile_list, size, config.Profile()->videoCodecOperation, is_protected);
}
}
~BitstreamBuffer() { Destroy(); }
VkDeviceSize Size() const { return size_; }
VkBuffer Buffer() const { return buffer_; }
const VkDependencyInfo* MemoryBarrier() { return &dep_info_; }
private:
void Init(const VkVideoProfileListInfoKHR* profile_list, VkDeviceSize size, VkVideoCodecOperationFlagBitsKHR codec_op,
bool is_protected) {
VkBufferUsageFlags usage = 0;
switch (codec_op) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
case VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR:
case VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR:
usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VIDEO_DECODE_SRC_BIT_KHR;
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:
usage |= VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_VIDEO_ENCODE_DST_BIT_KHR;
break;
default:
break;
}
{
VkBufferCreateInfo create_info = vku::InitStructHelper();
create_info.flags = is_protected ? VK_BUFFER_CREATE_PROTECTED_BIT : 0;
if (profile_list != nullptr) {
create_info.pNext = profile_list;
} else {
create_info.flags |= VK_BUFFER_CREATE_VIDEO_PROFILE_INDEPENDENT_BIT_KHR;
}
create_info.size = size;
create_info.usage = usage;
create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
ASSERT_EQ(VK_SUCCESS, vk::CreateBuffer(device_->handle(), &create_info, nullptr, &buffer_));
}
{
VkMemoryRequirements mem_req;
vk::GetBufferMemoryRequirements(device_->handle(), buffer_, &mem_req);
VkMemoryAllocateInfo alloc_info = vku::InitStructHelper();
VkMemoryPropertyFlags mem_props = is_protected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0;
ASSERT_TRUE(device_->Physical().SetMemoryType(mem_req.memoryTypeBits, &alloc_info, mem_props));
alloc_info.allocationSize = mem_req.size;
ASSERT_EQ(VK_SUCCESS, vk::AllocateMemory(device_->handle(), &alloc_info, nullptr, &memory_));
ASSERT_EQ(VK_SUCCESS, vk::BindBufferMemory(device_->handle(), buffer_, memory_, 0));
}
{
dep_info_.bufferMemoryBarrierCount = 1;
dep_info_.pBufferMemoryBarriers = &barrier_;
barrier_.srcStageMask = 0;
barrier_.srcAccessMask = 0;
barrier_.dstStageMask = 0;
barrier_.dstAccessMask = 0;
if (usage & VK_BUFFER_USAGE_VIDEO_ENCODE_DST_BIT_KHR) {
barrier_.srcStageMask |= VK_PIPELINE_STAGE_2_VIDEO_ENCODE_BIT_KHR;
barrier_.srcAccessMask |= VK_ACCESS_2_VIDEO_ENCODE_WRITE_BIT_KHR;
barrier_.dstStageMask |= VK_PIPELINE_STAGE_2_VIDEO_ENCODE_BIT_KHR;
barrier_.dstAccessMask |= VK_ACCESS_2_VIDEO_ENCODE_WRITE_BIT_KHR;
}
barrier_.buffer = Buffer();
barrier_.offset = 0;
barrier_.size = Size();
}
}
void Destroy() {
vk::DestroyBuffer(device_->handle(), buffer_, nullptr);
vk::FreeMemory(device_->handle(), memory_, nullptr);
}
vkt::Device* device_{};
VkDeviceSize size_{};
VkDeviceMemory memory_{};
VkBuffer buffer_{};
VkDependencyInfo dep_info_{};
VkBufferMemoryBarrier2 barrier_{};
};
class VideoPictureResource {
public:
~VideoPictureResource() { Destroy(); }
VkImage Image() const { return image_; }
VkImageView ImageView() const { return image_view_; }
const VkDependencyInfo* LayoutTransition(VkImageLayout new_layout, uint32_t first_layer = 0,
uint32_t layer_count = VK_REMAINING_ARRAY_LAYERS) {
barrier_.newLayout = new_layout;
barrier_.subresourceRange.baseArrayLayer = first_layer;
barrier_.subresourceRange.layerCount = layer_count;
return &dep_info_;
}
const VkDependencyInfo* MemoryBarrier(uint32_t first_layer = 0, uint32_t layer_count = VK_REMAINING_ARRAY_LAYERS) {
barrier_.newLayout = barrier_.oldLayout;
barrier_.subresourceRange.baseArrayLayer = first_layer;
barrier_.subresourceRange.layerCount = layer_count;
return &dep_info_;
}
protected:
VideoPictureResource(vkt::Device* device)
: device_(device),
memory_(VK_NULL_HANDLE),
image_(VK_NULL_HANDLE),
image_view_(VK_NULL_HANDLE),
dep_info_(vku::InitStruct<VkDependencyInfo>()),
barrier_(vku::InitStruct<VkImageMemoryBarrier2>()) {}
void Init(const VkVideoProfileListInfoKHR* profile_list, VkExtent2D extent, uint32_t layers,
const VkVideoFormatPropertiesKHR& format_props, bool is_protected) {
{
VkImageCreateInfo create_info = vku::InitStructHelper();
create_info.flags = is_protected ? VK_IMAGE_CREATE_PROTECTED_BIT : 0;
if (profile_list != nullptr) {
create_info.pNext = profile_list;
} else {
create_info.flags |= VK_IMAGE_CREATE_VIDEO_PROFILE_INDEPENDENT_BIT_KHR;
}
create_info.imageType = format_props.imageType;
create_info.format = format_props.format;
create_info.extent = {extent.width, extent.height, 1};
create_info.mipLevels = 1;
create_info.arrayLayers = layers;
create_info.samples = VK_SAMPLE_COUNT_1_BIT;
create_info.tiling = format_props.imageTiling;
create_info.usage = format_props.imageUsageFlags;
create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
ASSERT_EQ(VK_SUCCESS, vk::CreateImage(device_->handle(), &create_info, nullptr, &image_));
}
{
VkMemoryRequirements mem_req;
vk::GetImageMemoryRequirements(device_->handle(), image_, &mem_req);
VkMemoryAllocateInfo alloc_info = vku::InitStructHelper();
VkMemoryPropertyFlags mem_props = is_protected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0;
ASSERT_TRUE(device_->Physical().SetMemoryType(mem_req.memoryTypeBits, &alloc_info, mem_props));
alloc_info.allocationSize = mem_req.size;
ASSERT_EQ(VK_SUCCESS, vk::AllocateMemory(device_->handle(), &alloc_info, nullptr, &memory_));
ASSERT_EQ(VK_SUCCESS, vk::BindImageMemory(device_->handle(), image_, memory_, 0));
}
{
VkImageViewCreateInfo create_info = vku::InitStructHelper();
create_info.image = image_;
create_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
create_info.format = format_props.format;
create_info.components = format_props.componentMapping;
create_info.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, layers};
ASSERT_EQ(VK_SUCCESS, vk::CreateImageView(device_->handle(), &create_info, nullptr, &image_view_));
}
{
dep_info_.imageMemoryBarrierCount = 1;
dep_info_.pImageMemoryBarriers = &barrier_;
barrier_.srcStageMask = 0;
barrier_.srcAccessMask = 0;
barrier_.dstStageMask = 0;
barrier_.dstAccessMask = 0;
barrier_.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
if (format_props.imageUsageFlags &
(VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR | VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR)) {
barrier_.srcStageMask |= VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR;
barrier_.srcAccessMask |= VK_ACCESS_2_VIDEO_DECODE_READ_BIT_KHR | VK_ACCESS_2_VIDEO_DECODE_WRITE_BIT_KHR;
barrier_.dstStageMask |= VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR;
barrier_.dstAccessMask |= VK_ACCESS_2_VIDEO_DECODE_READ_BIT_KHR | VK_ACCESS_2_VIDEO_DECODE_WRITE_BIT_KHR;
}
if (format_props.imageUsageFlags &
(VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR | VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR)) {
barrier_.srcStageMask |= VK_PIPELINE_STAGE_2_VIDEO_ENCODE_BIT_KHR;
barrier_.srcAccessMask |= VK_ACCESS_2_VIDEO_ENCODE_READ_BIT_KHR | VK_ACCESS_2_VIDEO_ENCODE_WRITE_BIT_KHR;
barrier_.dstStageMask |= VK_PIPELINE_STAGE_2_VIDEO_ENCODE_BIT_KHR;
barrier_.dstAccessMask |= VK_ACCESS_2_VIDEO_ENCODE_READ_BIT_KHR | VK_ACCESS_2_VIDEO_ENCODE_WRITE_BIT_KHR;
}
if (format_props.imageUsageFlags &
(VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR | VK_IMAGE_USAGE_VIDEO_ENCODE_EMPHASIS_MAP_BIT_KHR)) {
barrier_.srcStageMask |= VK_PIPELINE_STAGE_2_VIDEO_ENCODE_BIT_KHR;
barrier_.srcAccessMask |= VK_ACCESS_2_VIDEO_ENCODE_READ_BIT_KHR;
barrier_.dstStageMask |= VK_PIPELINE_STAGE_2_VIDEO_ENCODE_BIT_KHR;
barrier_.dstAccessMask |= VK_ACCESS_2_VIDEO_ENCODE_READ_BIT_KHR;
}
barrier_.image = Image();
barrier_.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier_.subresourceRange.baseMipLevel = 0;
barrier_.subresourceRange.levelCount = 1;
}
}
void Destroy() {
vk::DestroyImageView(device_->handle(), image_view_, nullptr);
vk::DestroyImage(device_->handle(), image_, nullptr);
vk::FreeMemory(device_->handle(), memory_, nullptr);
}
private:
vkt::Device* device_{};
VkDeviceMemory memory_{};
VkImage image_{};
VkImageView image_view_{};
VkDependencyInfo dep_info_{};
VkImageMemoryBarrier2 barrier_{};
};
class VideoDecodeOutput : public VideoPictureResource {
public:
VideoDecodeOutput(vkt::Device* device, const VideoConfig& config, bool is_protected = false)
: VideoPictureResource(device), picture_(vku::InitStruct<VkVideoPictureResourceInfoKHR>()) {
if (config.UseProfileIndependentResources()) {
Init(nullptr, config.MaxCodedExtent(), 1, *config.PictureFormatProps(), is_protected);
} else {
VkVideoProfileListInfoKHR profile_list = vku::InitStructHelper();
profile_list.profileCount = 1;
profile_list.pProfiles = config.Profile();
Init(&profile_list, config.MaxCodedExtent(), 1, *config.PictureFormatProps(), is_protected);
}
picture_.codedOffset = {0, 0};
picture_.codedExtent = config.MaxCodedExtent();
picture_.baseArrayLayer = 0;
picture_.imageViewBinding = ImageView();
}
const VkVideoPictureResourceInfoKHR& Picture() const { return picture_; }
private:
VkVideoPictureResourceInfoKHR picture_{};
};
class VideoEncodeInput : public VideoPictureResource {
public:
VideoEncodeInput(vkt::Device* device, const VideoConfig& config, bool is_protected = false)
: VideoPictureResource(device), picture_(vku::InitStruct<VkVideoPictureResourceInfoKHR>()) {
if (config.UseProfileIndependentResources()) {
// For profile independent encode input pictures make sure to exclude DPB usage
// even if the implementation supports the same format for both input and DPB
auto format_props = *config.PictureFormatProps();
format_props.imageUsageFlags &= ~VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR;
Init(nullptr, config.MaxCodedExtent(), 1, format_props, is_protected);
} else {
VkVideoProfileListInfoKHR profile_list = vku::InitStructHelper();
profile_list.profileCount = 1;
profile_list.pProfiles = config.Profile();
Init(&profile_list, config.MaxCodedExtent(), 1, *config.PictureFormatProps(), is_protected);
}
picture_.codedOffset = {0, 0};
picture_.codedExtent = config.MaxCodedExtent();
picture_.baseArrayLayer = 0;
picture_.imageViewBinding = ImageView();
}
const VkVideoPictureResourceInfoKHR& Picture() const { return picture_; }
private:
VkVideoPictureResourceInfoKHR picture_{};
};
class VideoDPB : public VideoPictureResource {
public:
VideoDPB(vkt::Device* device, const VideoConfig& config, bool is_protected = false) : VideoPictureResource(device) {
VkVideoProfileListInfoKHR profile_list = vku::InitStructHelper();
profile_list.profileCount = 1;
profile_list.pProfiles = config.Profile();
Init(&profile_list, config.MaxCodedExtent(), config.DpbSlotCount(), *config.DpbFormatProps(), is_protected);
reference_pictures_.resize(config.DpbSlotCount());
for (uint32_t i = 0; i < config.DpbSlotCount(); ++i) {
reference_pictures_[i] = vku::InitStructHelper();
reference_pictures_[i].codedOffset = {0, 0};
reference_pictures_[i].codedExtent = config.MaxCodedExtent();
reference_pictures_[i].baseArrayLayer = i;
reference_pictures_[i].imageViewBinding = ImageView();
}
}
size_t PictureCount() const { return reference_pictures_.size(); }
const VkVideoPictureResourceInfoKHR& Picture(int32_t index) const { return reference_pictures_[index]; }
private:
std::vector<VkVideoPictureResourceInfoKHR> reference_pictures_{};
};
class VideoEncodeQuantizationMap : public VideoPictureResource {
public:
VideoEncodeQuantizationMap(vkt::Device* device, const VideoConfig& config, const VkVideoFormatPropertiesKHR& format_props,
bool is_protected = false)
: VideoPictureResource(device) {
assert(format_props.imageUsageFlags &
(VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR | VK_IMAGE_USAGE_VIDEO_ENCODE_EMPHASIS_MAP_BIT_KHR));
VkVideoProfileListInfoKHR profile_list = vku::InitStructHelper();
profile_list.profileCount = 1;
profile_list.pProfiles = config.Profile();
const auto texel_size = config.GetQuantMapTexelSize(format_props);
const auto map_extent = VkExtent2D{(config.MaxCodedExtent().width + texel_size.width - 1) / texel_size.width,
(config.MaxCodedExtent().height + texel_size.height - 1) / texel_size.height};
Init(&profile_list, map_extent, 1, format_props, is_protected);
}
};
template <typename INFOTYPE>
class VideoOpParams {
public:
VideoOpParams(const VideoConfig& config) : config_(&config), info_(vku::InitStruct<INFOTYPE>()) {}
VideoOpParams(VideoOpParams<INFOTYPE> const& other) : config_(other.config_) {
info_ = other.info_;
info_.pNext = nullptr;
}
VideoOpParams<INFOTYPE>& operator=(VideoOpParams<INFOTYPE> const& other) {
config_ = other.config_;
info_ = other.info_;
info_.pNext = nullptr;
return *this;
}
operator const INFOTYPE&() const { return info_; }
INFOTYPE* operator->() { return &info_; }
INFOTYPE& Ref() { return info_; }
protected:
template <typename T>
void ChainInfo(T& info, bool condition = true) {
assert(info.sType == vku::GetSType<T>());
if (condition) {
assert(vku::FindStructInPNextChain<T>(info_.pNext) == nullptr);
auto p = reinterpret_cast<VkBaseOutStructure*>(&info_);
while (p->pNext != nullptr) {
p = p->pNext;
}
p->pNext = reinterpret_cast<VkBaseOutStructure*>(&info);
info.pNext = nullptr;
}
}
template <typename T>
void ChainEntireChain(T& info, bool condition = true) {
assert(info.sType == vku::GetSType<T>());
if (condition) {
assert(vku::FindStructInPNextChain<T>(info_.pNext) == nullptr);
auto p = reinterpret_cast<VkBaseOutStructure*>(&info_);
while (p->pNext != nullptr) {
p = p->pNext;
}
p->pNext = reinterpret_cast<VkBaseOutStructure*>(&info);
}
}
const VideoConfig* config_{};
INFOTYPE info_{};
};
class VideoEncodeRateControlLayerInfo : public VideoOpParams<VkVideoEncodeRateControlLayerInfoKHR> {
public:
struct CodecInfoType {
VkVideoEncodeH264RateControlLayerInfoKHR encode_h264{};
VkVideoEncodeH265RateControlLayerInfoKHR encode_h265{};
VkVideoEncodeAV1RateControlLayerInfoKHR encode_av1{};
};
VideoEncodeRateControlLayerInfo(const VideoConfig& config, bool include_codec_info = false)
: VideoOpParams<VkVideoEncodeRateControlLayerInfoKHR>(config), include_codec_info_(include_codec_info), codec_info_() {
info_.averageBitrate = std::min((uint64_t)32000, config.EncodeCaps()->maxBitrate);
info_.maxBitrate = std::min((uint64_t)32000, config.EncodeCaps()->maxBitrate);
info_.frameRateNumerator = 15;
info_.frameRateDenominator = 1;
switch (config_->Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
codec_info_.encode_h264 = vku::InitStructHelper();
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
codec_info_.encode_h265 = vku::InitStructHelper();
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:
codec_info_.encode_av1 = vku::InitStructHelper();
break;
default:
break;
}
IncludeCodecInfo();
}
VideoEncodeRateControlLayerInfo(VideoEncodeRateControlLayerInfo const& other)
: VideoOpParams<VkVideoEncodeRateControlLayerInfoKHR>(other) {
CopyData(other);
}
VideoEncodeRateControlLayerInfo& operator=(VideoEncodeRateControlLayerInfo const& other) {
VideoOpParams<VkVideoEncodeRateControlLayerInfoKHR>::operator=(other);
CopyData(other);
return *this;
}
CodecInfoType& CodecInfo() { return codec_info_; }
private:
void IncludeCodecInfo() {
switch (config_->Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
ChainInfo(codec_info_.encode_h264, include_codec_info_);
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
ChainInfo(codec_info_.encode_h265, include_codec_info_);
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:
ChainInfo(codec_info_.encode_av1, include_codec_info_);
break;
default:
break;
}
}
void CopyData(VideoEncodeRateControlLayerInfo const& other) {
include_codec_info_ = other.include_codec_info_;
codec_info_ = other.codec_info_;
IncludeCodecInfo();
}
bool include_codec_info_{};
CodecInfoType codec_info_{};
};
class VideoEncodeRateControlInfo : public VideoOpParams<VkVideoEncodeRateControlInfoKHR> {
public:
struct CodecInfoType {
VkVideoEncodeH264RateControlInfoKHR encode_h264{};
VkVideoEncodeH265RateControlInfoKHR encode_h265{};
VkVideoEncodeAV1RateControlInfoKHR encode_av1{};
};
VideoEncodeRateControlInfo(const VideoConfig& config, bool include_codec_info = false)
: VideoOpParams<VkVideoEncodeRateControlInfoKHR>(config),
layers_(),
layer_info_(),
include_codec_info_(include_codec_info),
codec_info_() {
info_.virtualBufferSizeInMs = 1000;
info_.initialVirtualBufferSizeInMs = 0;
switch (config_->Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
codec_info_.encode_h264 = vku::InitStructHelper();
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
codec_info_.encode_h265 = vku::InitStructHelper();
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:
codec_info_.encode_av1 = vku::InitStructHelper();
break;
default:
break;
}
IncludeCodecInfo();
}
VideoEncodeRateControlInfo(VideoEncodeRateControlInfo const& other) : VideoOpParams<VkVideoEncodeRateControlInfoKHR>(other) {
CopyData(other);
}
VideoEncodeRateControlInfo& operator=(VideoEncodeRateControlInfo const& other) {
VideoOpParams<VkVideoEncodeRateControlInfoKHR>::operator=(other);
CopyData(other);
return *this;
}
VideoEncodeRateControlInfo& SetAnyMode() {
info_.rateControlMode = config_->GetAnySupportedRateControlMode();
return *this;
}
VideoEncodeRateControlInfo& AddLayer(const VideoEncodeRateControlLayerInfo& layer_info) {
layer_info_.push_back(layer_info);
layers_.push_back(layer_info_.back());
// push_back may have caused a reallocation of vector storage, hence we need to update pNext pointers
for (size_t i = 0; i < layer_info_.size(); ++i) {
layers_[i].pNext = layer_info_[i]->pNext;
}
info_.layerCount = (uint32_t)layers_.size();
info_.pLayers = layers_.data();
return *this;
}
CodecInfoType& CodecInfo() { return codec_info_; }
// This helper proxy is for allowing the individual configured layers to be modified and then propagated
// from the VideoEncodeRateControlLayerInfo object stored in the layer_info_ vector to the flattened
// array of VkVideoEncodeRateControlLayerInfoKHR structures stored in the layers_ vector.
class LayerModifierProxy {
public:
LayerModifierProxy(VideoEncodeRateControlInfo& parent, uint32_t layer_index, VideoEncodeRateControlLayerInfo& info)
: parent_(parent), layer_index_(layer_index), info_(info) {}
LayerModifierProxy(const LayerModifierProxy&) = delete;
LayerModifierProxy& operator=(const LayerModifierProxy&) = delete;
~LayerModifierProxy() { parent_.UpdateLayer(layer_index_); }
VkVideoEncodeRateControlLayerInfoKHR* operator->() { return info_.operator->(); }
VideoEncodeRateControlLayerInfo::CodecInfoType& CodecInfo() { return info_.CodecInfo(); }
private:
VideoEncodeRateControlInfo& parent_;
uint32_t layer_index_;
VideoEncodeRateControlLayerInfo& info_;
};
LayerModifierProxy Layer(uint32_t index) { return LayerModifierProxy(*this, index, layer_info_[index]); }
protected:
void UpdateLayer(uint32_t layer_index) {
assert(layer_index < layers_.size());
assert(layer_index < layer_info_.size());
layers_[layer_index] = layer_info_[layer_index];
}
private:
void IncludeCodecInfo() {
switch (config_->Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
ChainInfo(codec_info_.encode_h264, include_codec_info_);
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
ChainInfo(codec_info_.encode_h265, include_codec_info_);
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:
ChainInfo(codec_info_.encode_av1, include_codec_info_);
break;
default:
break;
}
}
void CopyData(VideoEncodeRateControlInfo const& other) {
layer_info_ = other.layer_info_;
include_codec_info_ = other.include_codec_info_;
codec_info_ = other.codec_info_;
layers_.resize(layer_info_.size());
for (size_t i = 0; i < layer_info_.size(); ++i) {
layers_[i] = layer_info_[i];
assert(layers_[i].pNext == layer_info_[i]->pNext);
}
info_.pLayers = layers_.data();
IncludeCodecInfo();
}
std::vector<VkVideoEncodeRateControlLayerInfoKHR> layers_{};
std::vector<VideoEncodeRateControlLayerInfo> layer_info_{};
bool include_codec_info_{};
CodecInfoType codec_info_{};
};
class VideoBeginCodingInfo : public VideoOpParams<VkVideoBeginCodingInfoKHR> {
public:
struct VideoEncodeGopRemainingFrameInfo {
VkVideoEncodeH264GopRemainingFrameInfoKHR encode_h264{};
VkVideoEncodeH265GopRemainingFrameInfoKHR encode_h265{};
VkVideoEncodeAV1GopRemainingFrameInfoKHR encode_av1{};
};
VideoBeginCodingInfo(const VideoConfig& config, VideoDPB* dpb, VkVideoSessionKHR session,
VkVideoSessionParametersKHR session_params)
: VideoOpParams<VkVideoBeginCodingInfoKHR>(config),
dpb_(dpb),
rc_info_(config),
slot_resources_(),
gop_remaining_frame_info_() {
info_.videoSession = session;
info_.videoSessionParameters = session_params;
}
VideoBeginCodingInfo(VideoBeginCodingInfo const& other)
: VideoOpParams<VkVideoBeginCodingInfoKHR>(other), rc_info_(*other.config_) {
CopyData(other);
}
VideoBeginCodingInfo& operator=(VideoBeginCodingInfo const& other) {
VideoOpParams<VkVideoBeginCodingInfoKHR>::operator=(other);
CopyData(other);
return *this;
}
VideoBeginCodingInfo& AddResource(int32_t slot_index, const VkVideoPictureResourceInfoKHR& resource) {
slot_resources_.push_back(vku::InitStruct<VkVideoReferenceSlotInfoKHR>());
auto& res = slot_resources_[info_.referenceSlotCount++];
res.slotIndex = slot_index;
res.pPictureResource = &resource;
info_.pReferenceSlots = slot_resources_.data();
return *this;
}
VideoBeginCodingInfo& AddResource(int32_t slot_index, int32_t resource_index) {
return AddResource(slot_index, dpb_->Picture(resource_index));
}
VideoBeginCodingInfo& InvalidateSlot(int32_t slot_index) {
slot_resources_.push_back(vku::InitStruct<VkVideoReferenceSlotInfoKHR>());
auto& res = slot_resources_[info_.referenceSlotCount++];
res.slotIndex = slot_index;
res.pPictureResource = nullptr;
info_.pReferenceSlots = slot_resources_.data();
return *this;
}
VideoBeginCodingInfo& RateControl(const VideoEncodeRateControlInfo& rc_info) {
rc_info_ = rc_info;
ChainGopRemainingFramesIfNeeded();
ChainEntireChain(rc_info_.Ref());
return *this;
}
VideoBeginCodingInfo& SetSessionParams(VkVideoSessionParametersKHR session_params) {
info_.videoSessionParameters = session_params;
return *this;
}
private:
void ChainGopRemainingFramesIfNeeded() {
if (rc_info_->rateControlMode != VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DEFAULT_KHR &&
rc_info_->rateControlMode != VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DISABLED_BIT_KHR) {
switch (config_->Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
if (config_->EncodeCapsH264()->requiresGopRemainingFrames) {
gop_remaining_frame_info_.encode_h264 = vku::InitStructHelper();
gop_remaining_frame_info_.encode_h264.useGopRemainingFrames = VK_TRUE;
gop_remaining_frame_info_.encode_h264.gopRemainingI = 5;
ChainInfo(gop_remaining_frame_info_.encode_h264);
}
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
if (config_->EncodeCapsH265()->requiresGopRemainingFrames) {
gop_remaining_frame_info_.encode_h265 = vku::InitStructHelper();
gop_remaining_frame_info_.encode_h265.useGopRemainingFrames = VK_TRUE;
gop_remaining_frame_info_.encode_h265.gopRemainingI = 5;
ChainInfo(gop_remaining_frame_info_.encode_h265);
}
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:
if (config_->EncodeCapsAV1()->requiresGopRemainingFrames) {
gop_remaining_frame_info_.encode_av1 = vku::InitStructHelper();
gop_remaining_frame_info_.encode_av1.useGopRemainingFrames = VK_TRUE;
gop_remaining_frame_info_.encode_av1.gopRemainingIntra = 5;
ChainInfo(gop_remaining_frame_info_.encode_av1);
}
break;
default:
break;
}
}
}
void CopyData(VideoBeginCodingInfo const& other) {
dpb_ = other.dpb_;
rc_info_ = other.rc_info_;
slot_resources_ = other.slot_resources_;
gop_remaining_frame_info_ = other.gop_remaining_frame_info_;
info_.pReferenceSlots = slot_resources_.data();
if (rc_info_->rateControlMode != VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DEFAULT_KHR) {
ChainGopRemainingFramesIfNeeded();
ChainEntireChain(rc_info_.Ref());
}
}
VideoDPB* dpb_{};
VideoEncodeRateControlInfo rc_info_;
std::vector<VkVideoReferenceSlotInfoKHR> slot_resources_{};
VideoEncodeGopRemainingFrameInfo gop_remaining_frame_info_{};
};
class VideoCodingControlInfo : public VideoOpParams<VkVideoCodingControlInfoKHR> {
public:
VideoCodingControlInfo(const VideoConfig& config)
: VideoOpParams<VkVideoCodingControlInfoKHR>(config),
encode_rate_control_info_(config),
encode_quality_level_info_(vku::InitStruct<VkVideoEncodeQualityLevelInfoKHR>()) {}
VideoCodingControlInfo(VideoCodingControlInfo const& other)
: VideoOpParams<VkVideoCodingControlInfoKHR>(other), encode_rate_control_info_(*other.config_) {
CopyData(other);
}
VideoCodingControlInfo& operator=(VideoCodingControlInfo const& other) {
VideoOpParams<VkVideoCodingControlInfoKHR>::operator=(other);
CopyData(other);
return *this;
}
VideoCodingControlInfo& Reset() {
info_.flags |= VK_VIDEO_CODING_CONTROL_RESET_BIT_KHR;
return *this;
}
VideoCodingControlInfo& RateControl(const VideoEncodeRateControlInfo& rc_info) {
info_.flags |= VK_VIDEO_CODING_CONTROL_ENCODE_RATE_CONTROL_BIT_KHR;
encode_rate_control_info_ = rc_info;
ChainEntireChain(encode_rate_control_info_.Ref());
return *this;
}
VideoCodingControlInfo& EncodeQualityLevel(uint32_t quality_level) {
info_.flags |= VK_VIDEO_CODING_CONTROL_ENCODE_QUALITY_LEVEL_BIT_KHR;
encode_quality_level_info_.qualityLevel = quality_level;
ChainInfo(encode_quality_level_info_);
return *this;
}
private:
void CopyData(VideoCodingControlInfo const& other) {
encode_rate_control_info_ = other.encode_rate_control_info_;
ChainEntireChain(encode_rate_control_info_.Ref(), info_.flags & VK_VIDEO_CODING_CONTROL_ENCODE_RATE_CONTROL_BIT_KHR);
encode_quality_level_info_ = other.encode_quality_level_info_;
ChainInfo(encode_quality_level_info_, info_.flags & VK_VIDEO_CODING_CONTROL_ENCODE_QUALITY_LEVEL_BIT_KHR);
}
VideoEncodeRateControlInfo encode_rate_control_info_;
VkVideoEncodeQualityLevelInfoKHR encode_quality_level_info_{};
};
class VideoEndCodingInfo : public VideoOpParams<VkVideoEndCodingInfoKHR> {
public:
VideoEndCodingInfo(const VideoConfig& config) : VideoOpParams<VkVideoEndCodingInfoKHR>(config) {}
};
class VideoDecodeInfo : public VideoOpParams<VkVideoDecodeInfoKHR> {
public:
struct CodecInfoType {
struct {
VkVideoDecodeH264PictureInfoKHR picture_info{};
StdVideoDecodeH264PictureInfo std_picture_info{};
std::vector<uint32_t> slice_offsets{};
VkVideoDecodeH264DpbSlotInfoKHR setup_slot_info{};
StdVideoDecodeH264ReferenceInfo std_setup_reference_info{};
std::vector<VkVideoDecodeH264DpbSlotInfoKHR> dpb_slot_info{};
std::vector<StdVideoDecodeH264ReferenceInfo> std_reference_info{};
VkVideoDecodeH264InlineSessionParametersInfoKHR inline_params_info{};
StdVideoH264SequenceParameterSet std_inline_sps{};
StdVideoH264PictureParameterSet std_inline_pps{};
} decode_h264{};
struct {
VkVideoDecodeH265PictureInfoKHR picture_info{};
StdVideoDecodeH265PictureInfo std_picture_info{};
std::vector<uint32_t> slice_segment_offsets{};
VkVideoDecodeH265DpbSlotInfoKHR setup_slot_info{};
StdVideoDecodeH265ReferenceInfo std_setup_reference_info{};
std::vector<VkVideoDecodeH265DpbSlotInfoKHR> dpb_slot_info{};
std::vector<StdVideoDecodeH265ReferenceInfo> std_reference_info{};
VkVideoDecodeH265InlineSessionParametersInfoKHR inline_params_info{};
StdVideoH265VideoParameterSet std_inline_vps{};
StdVideoH265SequenceParameterSet std_inline_sps{};
StdVideoH265PictureParameterSet std_inline_pps{};
} decode_h265{};
struct {
VkVideoDecodeAV1PictureInfoKHR picture_info{};
StdVideoDecodeAV1PictureInfo std_picture_info{};
std::vector<uint32_t> tile_offsets{};
std::vector<uint32_t> tile_sizes{};
VkVideoDecodeAV1DpbSlotInfoKHR setup_slot_info{};
StdVideoDecodeAV1ReferenceInfo std_setup_reference_info{};
std::vector<VkVideoDecodeAV1DpbSlotInfoKHR> dpb_slot_info{};
std::vector<StdVideoDecodeAV1ReferenceInfo> std_reference_info{};
VkVideoDecodeAV1InlineSessionParametersInfoKHR inline_params_info{};
StdVideoAV1SequenceHeader std_inline_seq_header{};
} decode_av1{};
struct {
VkVideoDecodeVP9PictureInfoKHR picture_info{};
StdVideoDecodeVP9PictureInfo std_picture_info{};
std::vector<uint32_t> tile_offsets{};
std::vector<uint32_t> tile_sizes{};
} decode_vp9{};
};
VideoDecodeInfo(const VideoConfig& config, BitstreamBuffer& bitstream, VideoDPB* dpb, const VideoDecodeOutput* output,
int32_t slot_index, const VkVideoPictureResourceInfoKHR* resource, bool reference = false)
: VideoOpParams<VkVideoDecodeInfoKHR>(config),
output_distinct_(config.SupportsDecodeOutputDistinct()),
distinct_output_picture_(output->Picture()),
dpb_(dpb),
reconstructed_(vku::InitStruct<VkVideoReferenceSlotInfoKHR>()),
references_(dpb ? dpb->PictureCount() : 0, vku::InitStruct<VkVideoReferenceSlotInfoKHR>()),
codec_info_(),
inline_query_info_() {
assert(config_->IsDecode());
assert(output != nullptr);
info_.srcBuffer = bitstream.Buffer();
info_.srcBufferOffset = 0;
info_.srcBufferRange = bitstream.Size();
info_.dstPictureResource = output->Picture();
assert(slot_index >= 0 || config.SessionCreateInfo()->maxDpbSlots == 0);
if (slot_index >= 0) {
if (resource == nullptr) {
resource = &dpb->Picture(slot_index);
}
reconstructed_.slotIndex = slot_index;
reconstructed_.pPictureResource = resource;
info_.pSetupReferenceSlot = &reconstructed_;
if (!output_distinct_) {
info_.dstPictureResource = *reconstructed_.pPictureResource;
}
}
switch (config_->Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
codec_info_.decode_h264.slice_offsets = {0};
codec_info_.decode_h264.picture_info = vku::InitStructHelper();
codec_info_.decode_h264.picture_info.pStdPictureInfo = &codec_info_.decode_h264.std_picture_info;
codec_info_.decode_h264.picture_info.sliceCount = (uint32_t)codec_info_.decode_h264.slice_offsets.size();
codec_info_.decode_h264.picture_info.pSliceOffsets = codec_info_.decode_h264.slice_offsets.data();
codec_info_.decode_h264.std_picture_info = {};
codec_info_.decode_h264.std_picture_info.flags.is_reference = reference ? 1 : 0;
reconstructed_.pNext = &codec_info_.decode_h264.setup_slot_info;
codec_info_.decode_h264.setup_slot_info = vku::InitStructHelper();
codec_info_.decode_h264.setup_slot_info.pStdReferenceInfo = &codec_info_.decode_h264.std_setup_reference_info;
codec_info_.decode_h264.std_setup_reference_info = {};
codec_info_.decode_h264.dpb_slot_info.resize(references_.size(), vku::InitStruct<VkVideoDecodeH264DpbSlotInfoKHR>());
codec_info_.decode_h264.std_reference_info.resize(references_.size(), {});
for (size_t i = 0; i < references_.size(); ++i) {
references_[i].pNext = &codec_info_.decode_h264.dpb_slot_info[i];
codec_info_.decode_h264.dpb_slot_info[i].pStdReferenceInfo = &codec_info_.decode_h264.std_reference_info[i];
}
ChainInfo(codec_info_.decode_h264.picture_info);
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
codec_info_.decode_h265.slice_segment_offsets = {0};
codec_info_.decode_h265.picture_info = vku::InitStructHelper();
codec_info_.decode_h265.picture_info.pStdPictureInfo = &codec_info_.decode_h265.std_picture_info;
codec_info_.decode_h265.picture_info.sliceSegmentCount =
(uint32_t)codec_info_.decode_h265.slice_segment_offsets.size();
codec_info_.decode_h265.picture_info.pSliceSegmentOffsets = codec_info_.decode_h265.slice_segment_offsets.data();
codec_info_.decode_h265.std_picture_info = {};
codec_info_.decode_h265.std_picture_info.flags.IsReference = reference ? 1 : 0;
reconstructed_.pNext = &codec_info_.decode_h265.setup_slot_info;
codec_info_.decode_h265.setup_slot_info = vku::InitStructHelper();
codec_info_.decode_h265.setup_slot_info.pStdReferenceInfo = &codec_info_.decode_h265.std_setup_reference_info;
codec_info_.decode_h265.std_setup_reference_info = {};
codec_info_.decode_h265.dpb_slot_info.resize(references_.size(), vku::InitStruct<VkVideoDecodeH265DpbSlotInfoKHR>());
codec_info_.decode_h265.std_reference_info.resize(references_.size(), {});
for (size_t i = 0; i < references_.size(); ++i) {
references_[i].pNext = &codec_info_.decode_h265.dpb_slot_info[i];
codec_info_.decode_h265.dpb_slot_info[i].pStdReferenceInfo = &codec_info_.decode_h265.std_reference_info[i];
}
ChainInfo(codec_info_.decode_h265.picture_info);
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR:
codec_info_.decode_av1.tile_offsets = {0};
codec_info_.decode_av1.tile_sizes = {256};
codec_info_.decode_av1.picture_info = vku::InitStructHelper();
codec_info_.decode_av1.picture_info.pStdPictureInfo = &codec_info_.decode_av1.std_picture_info;
for (uint32_t i = 0; i < VK_MAX_VIDEO_AV1_REFERENCES_PER_FRAME_KHR; ++i) {
codec_info_.decode_av1.picture_info.referenceNameSlotIndices[i] = -1;
}
codec_info_.decode_av1.picture_info.frameHeaderOffset = 0;
codec_info_.decode_av1.picture_info.tileCount = (uint32_t)codec_info_.decode_av1.tile_offsets.size();
codec_info_.decode_av1.picture_info.pTileOffsets = codec_info_.decode_av1.tile_offsets.data();
codec_info_.decode_av1.picture_info.pTileSizes = codec_info_.decode_av1.tile_sizes.data();
codec_info_.decode_av1.std_picture_info = {};
// We will simply use the DPB slot index as the VBI slot index for the purposes of testing
codec_info_.decode_av1.std_picture_info.refresh_frame_flags =
reference ? (1 << VK_MAX_VIDEO_AV1_REFERENCES_PER_FRAME_KHR) - 1 : 0;
reconstructed_.pNext = &codec_info_.decode_av1.setup_slot_info;
codec_info_.decode_av1.setup_slot_info = vku::InitStructHelper();
codec_info_.decode_av1.setup_slot_info.pStdReferenceInfo = &codec_info_.decode_av1.std_setup_reference_info;
codec_info_.decode_av1.std_setup_reference_info = {};
codec_info_.decode_av1.dpb_slot_info.resize(references_.size(), vku::InitStruct<VkVideoDecodeAV1DpbSlotInfoKHR>());
codec_info_.decode_av1.std_reference_info.resize(references_.size(), {});
for (size_t i = 0; i < references_.size(); ++i) {
references_[i].pNext = &codec_info_.decode_av1.dpb_slot_info[i];
codec_info_.decode_av1.dpb_slot_info[i].pStdReferenceInfo = &codec_info_.decode_av1.std_reference_info[i];
}
ChainInfo(codec_info_.decode_av1.picture_info);
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR:
codec_info_.decode_vp9.tile_offsets = {0};
codec_info_.decode_vp9.tile_sizes = {256};
codec_info_.decode_vp9.picture_info = vku::InitStructHelper();
codec_info_.decode_vp9.picture_info.pStdPictureInfo = &codec_info_.decode_vp9.std_picture_info;
for (uint32_t i = 0; i < VK_MAX_VIDEO_VP9_REFERENCES_PER_FRAME_KHR; ++i) {
codec_info_.decode_vp9.picture_info.referenceNameSlotIndices[i] = -1;
}
codec_info_.decode_vp9.picture_info.compressedHeaderOffset = 0;
codec_info_.decode_vp9.picture_info.uncompressedHeaderOffset = 0;
codec_info_.decode_vp9.picture_info.tilesOffset = 0;
codec_info_.decode_vp9.std_picture_info = {};
// We will simply use the DPB slot index as the reference slot index for the purposes of testing
codec_info_.decode_vp9.std_picture_info.refresh_frame_flags =
reference ? (1 << VK_MAX_VIDEO_VP9_REFERENCES_PER_FRAME_KHR) - 1 : 0;
ChainInfo(codec_info_.decode_vp9.picture_info);
break;
default:
break;
}
}
VideoDecodeInfo(VideoDecodeInfo const& other) : VideoOpParams<VkVideoDecodeInfoKHR>(other) { CopyData(other); }
VideoDecodeInfo& operator=(VideoDecodeInfo const& other) {
VideoOpParams<VkVideoDecodeInfoKHR>::operator=(other);
CopyData(other);
return *this;
}
VideoDecodeInfo& SetFrame(bool picture_only = false) {
switch (config_->Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
codec_info_.decode_h264.std_picture_info.flags.field_pic_flag = 0;
if (!picture_only) {
auto& setup_info = codec_info_.decode_h264.std_setup_reference_info;
setup_info.flags.top_field_flag = 0;
setup_info.flags.bottom_field_flag = 0;
}
return *this;
default:
return *this;
}
}
VideoDecodeInfo& SetTopField(bool picture_only = false) {
switch (config_->Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
codec_info_.decode_h264.std_picture_info.flags.field_pic_flag = 1;
codec_info_.decode_h264.std_picture_info.flags.bottom_field_flag = 0;
if (!picture_only) {
auto& setup_info = codec_info_.decode_h264.std_setup_reference_info;
setup_info.flags.top_field_flag = 1;
setup_info.flags.bottom_field_flag = 0;
}
return *this;
default:
assert(false);
return *this;
}
}
VideoDecodeInfo& SetBottomField(bool picture_only = false) {
switch (config_->Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
codec_info_.decode_h264.std_picture_info.flags.field_pic_flag = 1;
codec_info_.decode_h264.std_picture_info.flags.bottom_field_flag = 1;
if (!picture_only) {
auto& setup_info = codec_info_.decode_h264.std_setup_reference_info;
setup_info.flags.top_field_flag = 0;
setup_info.flags.bottom_field_flag = 1;
}
return *this;
default:
assert(false);
return *this;
}
}
VideoDecodeInfo& ApplyFilmGrain() {
switch (config_->Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR:
codec_info_.decode_av1.std_picture_info.flags.apply_grain = 1;
// We have to force using distinct output in this case
info_.dstPictureResource = distinct_output_picture_;
return *this;
default:
assert(false);
return *this;
}
}
VideoDecodeInfo& SetBitstream(const BitstreamBuffer& bitstream) {
info_.srcBuffer = bitstream.Buffer();
info_.srcBufferOffset = 0;
info_.srcBufferRange = bitstream.Size();
return *this;
}
VideoDecodeInfo& SetBitstreamBuffer(VkBuffer bitstream, VkDeviceSize offset, VkDeviceSize range) {
info_.srcBuffer = bitstream;
info_.srcBufferOffset = offset;
info_.srcBufferRange = range;
return *this;
}
VideoDecodeInfo& SetDecodeOutput(const VideoDecodeOutput* output) {
assert(output != nullptr);
info_.dstPictureResource = output->Picture();
return *this;
}
VideoDecodeInfo& SetDecodeOutput(const VkVideoPictureResourceInfoKHR& resource) {
info_.dstPictureResource = resource;
return *this;
}
VideoDecodeInfo& AddReferenceFrame(int32_t slot_index, const VkVideoPictureResourceInfoKHR* resource) {
assert(dpb_ != nullptr);
size_t index = info_.referenceSlotCount++;
references_[index].slotIndex = slot_index;
references_[index].pPictureResource = resource;
info_.pReferenceSlots = references_.data();
switch (config_->Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR: {
auto& info = codec_info_.decode_h264.std_reference_info[index];
info.flags.top_field_flag = 0;
info.flags.bottom_field_flag = 0;
return *this;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR: {
auto& info = codec_info_.decode_av1.picture_info;
const StdVideoAV1ReferenceName def_ref_name = STD_VIDEO_AV1_REFERENCE_NAME_GOLDEN_FRAME;
if (info.referenceNameSlotIndices[def_ref_name - STD_VIDEO_AV1_REFERENCE_NAME_LAST_FRAME] < 0) {
info.referenceNameSlotIndices[def_ref_name - STD_VIDEO_AV1_REFERENCE_NAME_LAST_FRAME] = slot_index;
} else {
for (uint32_t i = 0; i < VK_MAX_VIDEO_AV1_REFERENCES_PER_FRAME_KHR; ++i) {
if (info.referenceNameSlotIndices[i] < 0) {
info.referenceNameSlotIndices[i] = slot_index;
}
}
}
return *this;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR: {
auto& info = codec_info_.decode_vp9.picture_info;
const StdVideoVP9ReferenceName def_ref_name = STD_VIDEO_VP9_REFERENCE_NAME_GOLDEN_FRAME;
if (info.referenceNameSlotIndices[def_ref_name - STD_VIDEO_VP9_REFERENCE_NAME_LAST_FRAME] < 0) {
info.referenceNameSlotIndices[def_ref_name - STD_VIDEO_VP9_REFERENCE_NAME_LAST_FRAME] = slot_index;
} else {
for (uint32_t i = 0; i < VK_MAX_VIDEO_VP9_REFERENCES_PER_FRAME_KHR; ++i) {
if (info.referenceNameSlotIndices[i] < 0) {
info.referenceNameSlotIndices[i] = slot_index;
}
}
}
return *this;
}
default:
return *this;
}
}
VideoDecodeInfo& AddReferenceFrame(int32_t slot_index, int32_t resource_index) {
return AddReferenceFrame(slot_index, &dpb_->Picture(resource_index));
}
VideoDecodeInfo& AddReferenceFrame(int32_t slot_index) { return AddReferenceFrame(slot_index, slot_index); }
VideoDecodeInfo& AddReferenceTopField(int32_t slot_index, const VkVideoPictureResourceInfoKHR* resource) {
assert(dpb_ != nullptr);
size_t index = info_.referenceSlotCount++;
references_[index].slotIndex = slot_index;
references_[index].pPictureResource = resource;
info_.pReferenceSlots = references_.data();
switch (config_->Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR: {
auto& info = codec_info_.decode_h264.std_reference_info[index];
info.flags.top_field_flag = 1;
info.flags.bottom_field_flag = 0;
return *this;
}
default:
assert(false);
return *this;
}
}
VideoDecodeInfo& AddReferenceTopField(int32_t slot_index, int32_t resource_index) {
return AddReferenceTopField(slot_index, &dpb_->Picture(resource_index));
}
VideoDecodeInfo& AddReferenceTopField(int32_t slot_index) { return AddReferenceTopField(slot_index, slot_index); }
VideoDecodeInfo& AddReferenceBottomField(int32_t slot_index, const VkVideoPictureResourceInfoKHR* resource) {
assert(dpb_ != nullptr);
size_t index = info_.referenceSlotCount++;
references_[index].slotIndex = slot_index;
references_[index].pPictureResource = resource;
info_.pReferenceSlots = references_.data();
switch (config_->Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR: {
auto& info = codec_info_.decode_h264.std_reference_info[index];
info.flags.top_field_flag = 0;
info.flags.bottom_field_flag = 1;
return *this;
}
default:
assert(false);
return *this;
}
}
VideoDecodeInfo& AddReferenceBottomField(int32_t slot_index, int32_t resource_index) {
return AddReferenceBottomField(slot_index, &dpb_->Picture(resource_index));
}
VideoDecodeInfo& AddReferenceBottomField(int32_t slot_index) { return AddReferenceBottomField(slot_index, slot_index); }
VideoDecodeInfo& AddReferenceBothFields(int32_t slot_index, const VkVideoPictureResourceInfoKHR* resource) {
assert(dpb_ != nullptr);
size_t index = info_.referenceSlotCount++;
references_[index].slotIndex = slot_index;
references_[index].pPictureResource = resource;
info_.pReferenceSlots = references_.data();
switch (config_->Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR: {
auto& info = codec_info_.decode_h264.std_reference_info[index];
info.flags.top_field_flag = 1;
info.flags.bottom_field_flag = 1;
return *this;
}
default:
assert(false);
return *this;
}
}
VideoDecodeInfo& AddReferenceBothFields(int32_t slot_index, int32_t resource_index) {
return AddReferenceBothFields(slot_index, &dpb_->Picture(resource_index));
}
VideoDecodeInfo& AddReferenceBothFields(int32_t slot_index) { return AddReferenceBothFields(slot_index, slot_index); }
VideoDecodeInfo& InlineQuery(VkQueryPool pool, uint32_t first = 0, uint32_t count = 1) {
inline_query_info_ = vku::InitStructHelper();
inline_query_info_.queryPool = pool;
inline_query_info_.firstQuery = first;
inline_query_info_.queryCount = count;
ChainInfo(inline_query_info_);
return *this;
}
VideoDecodeInfo& InlineParamsH264(const StdVideoH264SequenceParameterSet* sps, const StdVideoH264PictureParameterSet* pps) {
assert(config_->Profile()->videoCodecOperation == VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR);
assert(codec_info_.decode_h264.inline_params_info.sType !=
VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_INLINE_SESSION_PARAMETERS_INFO_KHR);
codec_info_.decode_h264.inline_params_info = vku::InitStructHelper();
if (sps) {
codec_info_.decode_h264.std_inline_sps = *sps;
codec_info_.decode_h264.inline_params_info.pStdSPS = &codec_info_.decode_h264.std_inline_sps;
}
if (pps) {
codec_info_.decode_h264.std_inline_pps = *pps;
codec_info_.decode_h264.inline_params_info.pStdPPS = &codec_info_.decode_h264.std_inline_pps;
}
ChainInfo(codec_info_.decode_h264.inline_params_info);
return *this;
}
VideoDecodeInfo& InlineParamsH265(const StdVideoH265VideoParameterSet* vps, const StdVideoH265SequenceParameterSet* sps,
const StdVideoH265PictureParameterSet* pps) {
assert(config_->Profile()->videoCodecOperation == VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR);
assert(codec_info_.decode_h265.inline_params_info.sType !=
VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_INLINE_SESSION_PARAMETERS_INFO_KHR);
codec_info_.decode_h265.inline_params_info = vku::InitStructHelper();
if (vps) {
codec_info_.decode_h265.std_inline_vps = *vps;
codec_info_.decode_h265.inline_params_info.pStdVPS = &codec_info_.decode_h265.std_inline_vps;
}
if (sps) {
codec_info_.decode_h265.std_inline_sps = *sps;
codec_info_.decode_h265.inline_params_info.pStdSPS = &codec_info_.decode_h265.std_inline_sps;
}
if (pps) {
codec_info_.decode_h265.std_inline_pps = *pps;
codec_info_.decode_h265.inline_params_info.pStdPPS = &codec_info_.decode_h265.std_inline_pps;
}
ChainInfo(codec_info_.decode_h265.inline_params_info);
return *this;
}
VideoDecodeInfo& InlineParamsAV1(const StdVideoAV1SequenceHeader* seq_header) {
assert(config_->Profile()->videoCodecOperation == VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR);
assert(codec_info_.decode_av1.inline_params_info.sType !=
VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_INLINE_SESSION_PARAMETERS_INFO_KHR);
codec_info_.decode_av1.inline_params_info = vku::InitStructHelper();
if (seq_header) {
codec_info_.decode_av1.std_inline_seq_header = *seq_header;
codec_info_.decode_av1.inline_params_info.pStdSequenceHeader = &codec_info_.decode_av1.std_inline_seq_header;
}
ChainInfo(codec_info_.decode_av1.inline_params_info);
return *this;
}
CodecInfoType& CodecInfo() { return codec_info_; }
private:
void CopyData(VideoDecodeInfo const& other) {
output_distinct_ = other.output_distinct_;
distinct_output_picture_ = other.distinct_output_picture_;
dpb_ = other.dpb_;
reconstructed_ = other.reconstructed_;
references_ = other.references_;
if (other.info_.pSetupReferenceSlot != nullptr) {
info_.pSetupReferenceSlot = &reconstructed_;
}
if (other.info_.pReferenceSlots != nullptr) {
info_.pReferenceSlots = references_.data();
}
switch (config_->Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
codec_info_.decode_h264.slice_offsets = other.codec_info_.decode_h264.slice_offsets;
codec_info_.decode_h264.picture_info = other.codec_info_.decode_h264.picture_info;
codec_info_.decode_h264.picture_info.pStdPictureInfo = &codec_info_.decode_h264.std_picture_info;
codec_info_.decode_h264.picture_info.sliceCount = (uint32_t)codec_info_.decode_h264.slice_offsets.size();
codec_info_.decode_h264.picture_info.pSliceOffsets = codec_info_.decode_h264.slice_offsets.data();
codec_info_.decode_h264.std_picture_info = other.codec_info_.decode_h264.std_picture_info;
reconstructed_.pNext = &codec_info_.decode_h264.setup_slot_info;
codec_info_.decode_h264.setup_slot_info = other.codec_info_.decode_h264.setup_slot_info;
codec_info_.decode_h264.setup_slot_info.pStdReferenceInfo = &codec_info_.decode_h264.std_setup_reference_info;
codec_info_.decode_h264.std_setup_reference_info = other.codec_info_.decode_h264.std_setup_reference_info;
codec_info_.decode_h264.dpb_slot_info = other.codec_info_.decode_h264.dpb_slot_info;
codec_info_.decode_h264.std_reference_info = other.codec_info_.decode_h264.std_reference_info;
for (size_t i = 0; i < references_.size(); ++i) {
references_[i].pNext = &codec_info_.decode_h264.dpb_slot_info[i];
codec_info_.decode_h264.dpb_slot_info[i].pStdReferenceInfo = &codec_info_.decode_h264.std_reference_info[i];
}
ChainInfo(codec_info_.decode_h264.picture_info);
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
codec_info_.decode_h265.slice_segment_offsets = other.codec_info_.decode_h265.slice_segment_offsets;
codec_info_.decode_h265.picture_info = other.codec_info_.decode_h265.picture_info;
codec_info_.decode_h265.picture_info.pStdPictureInfo = &codec_info_.decode_h265.std_picture_info;
codec_info_.decode_h265.picture_info.sliceSegmentCount =
(uint32_t)codec_info_.decode_h265.slice_segment_offsets.size();
codec_info_.decode_h265.picture_info.pSliceSegmentOffsets = codec_info_.decode_h265.slice_segment_offsets.data();
codec_info_.decode_h265.std_picture_info = other.codec_info_.decode_h265.std_picture_info;
reconstructed_.pNext = &codec_info_.decode_h265.setup_slot_info;
codec_info_.decode_h265.setup_slot_info = other.codec_info_.decode_h265.setup_slot_info;
codec_info_.decode_h265.setup_slot_info.pStdReferenceInfo = &codec_info_.decode_h265.std_setup_reference_info;
codec_info_.decode_h265.std_setup_reference_info = other.codec_info_.decode_h265.std_setup_reference_info;
codec_info_.decode_h265.dpb_slot_info = other.codec_info_.decode_h265.dpb_slot_info;
codec_info_.decode_h265.std_reference_info = other.codec_info_.decode_h265.std_reference_info;
for (size_t i = 0; i < references_.size(); ++i) {
references_[i].pNext = &codec_info_.decode_h265.dpb_slot_info[i];
codec_info_.decode_h265.dpb_slot_info[i].pStdReferenceInfo = &codec_info_.decode_h265.std_reference_info[i];
}
ChainInfo(codec_info_.decode_h265.picture_info);
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR:
codec_info_.decode_av1.tile_offsets = other.codec_info_.decode_av1.tile_offsets;
codec_info_.decode_av1.tile_sizes = other.codec_info_.decode_av1.tile_sizes;
codec_info_.decode_av1.picture_info = other.codec_info_.decode_av1.picture_info;
codec_info_.decode_av1.picture_info.pStdPictureInfo = &codec_info_.decode_av1.std_picture_info;
codec_info_.decode_av1.picture_info.frameHeaderOffset = other.codec_info_.decode_av1.picture_info.frameHeaderOffset;
codec_info_.decode_av1.picture_info.tileCount = (uint32_t)codec_info_.decode_av1.tile_offsets.size();
codec_info_.decode_av1.picture_info.pTileOffsets = codec_info_.decode_av1.tile_offsets.data();
codec_info_.decode_av1.picture_info.pTileSizes = codec_info_.decode_av1.tile_sizes.data();
codec_info_.decode_av1.std_picture_info = other.codec_info_.decode_av1.std_picture_info;
reconstructed_.pNext = &codec_info_.decode_av1.setup_slot_info;
codec_info_.decode_av1.setup_slot_info = other.codec_info_.decode_av1.setup_slot_info;
codec_info_.decode_av1.setup_slot_info.pStdReferenceInfo = &codec_info_.decode_av1.std_setup_reference_info;
codec_info_.decode_av1.std_setup_reference_info = other.codec_info_.decode_av1.std_setup_reference_info;
codec_info_.decode_av1.dpb_slot_info = other.codec_info_.decode_av1.dpb_slot_info;
codec_info_.decode_av1.std_reference_info = other.codec_info_.decode_av1.std_reference_info;
for (size_t i = 0; i < references_.size(); ++i) {
references_[i].pNext = &codec_info_.decode_av1.dpb_slot_info[i];
codec_info_.decode_av1.dpb_slot_info[i].pStdReferenceInfo = &codec_info_.decode_av1.std_reference_info[i];
}
ChainInfo(codec_info_.decode_av1.picture_info);
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR:
codec_info_.decode_vp9.tile_offsets = other.codec_info_.decode_vp9.tile_offsets;
codec_info_.decode_vp9.tile_sizes = other.codec_info_.decode_vp9.tile_sizes;
codec_info_.decode_vp9.picture_info = other.codec_info_.decode_vp9.picture_info;
codec_info_.decode_vp9.picture_info.pStdPictureInfo = &codec_info_.decode_vp9.std_picture_info;
for (uint32_t i = 0; i < VK_MAX_VIDEO_VP9_REFERENCES_PER_FRAME_KHR; ++i) {
codec_info_.decode_vp9.picture_info.referenceNameSlotIndices[i] =
other.codec_info_.decode_vp9.picture_info.referenceNameSlotIndices[i];
}
codec_info_.decode_vp9.picture_info.compressedHeaderOffset =
other.codec_info_.decode_vp9.picture_info.compressedHeaderOffset;
codec_info_.decode_vp9.picture_info.uncompressedHeaderOffset =
other.codec_info_.decode_vp9.picture_info.uncompressedHeaderOffset;
codec_info_.decode_vp9.picture_info.tilesOffset = other.codec_info_.decode_vp9.picture_info.tilesOffset;
codec_info_.decode_vp9.std_picture_info = other.codec_info_.decode_vp9.std_picture_info;
ChainInfo(codec_info_.decode_vp9.picture_info);
break;
default:
break;
}
if (other.inline_query_info_.sType == VK_STRUCTURE_TYPE_VIDEO_INLINE_QUERY_INFO_KHR) {
inline_query_info_ = other.inline_query_info_;
ChainInfo(inline_query_info_);
}
if (other.codec_info_.decode_h264.inline_params_info.sType ==
VK_STRUCTURE_TYPE_VIDEO_DECODE_H264_INLINE_SESSION_PARAMETERS_INFO_KHR) {
codec_info_.decode_h264.inline_params_info = other.codec_info_.decode_h264.inline_params_info;
if (other.codec_info_.decode_h264.inline_params_info.pStdSPS) {
codec_info_.decode_h264.std_inline_sps = other.codec_info_.decode_h264.std_inline_sps;
codec_info_.decode_h264.inline_params_info.pStdSPS = &codec_info_.decode_h264.std_inline_sps;
}
if (other.codec_info_.decode_h264.inline_params_info.pStdPPS) {
codec_info_.decode_h264.std_inline_pps = other.codec_info_.decode_h264.std_inline_pps;
codec_info_.decode_h264.inline_params_info.pStdPPS = &codec_info_.decode_h264.std_inline_pps;
}
ChainInfo(codec_info_.decode_h264.inline_params_info);
}
if (other.codec_info_.decode_h265.inline_params_info.sType ==
VK_STRUCTURE_TYPE_VIDEO_DECODE_H265_INLINE_SESSION_PARAMETERS_INFO_KHR) {
codec_info_.decode_h265.inline_params_info = other.codec_info_.decode_h265.inline_params_info;
if (other.codec_info_.decode_h265.inline_params_info.pStdVPS) {
codec_info_.decode_h265.std_inline_vps = other.codec_info_.decode_h265.std_inline_vps;
codec_info_.decode_h265.inline_params_info.pStdVPS = &codec_info_.decode_h265.std_inline_vps;
}
if (other.codec_info_.decode_h265.inline_params_info.pStdSPS) {
codec_info_.decode_h265.std_inline_sps = other.codec_info_.decode_h265.std_inline_sps;
codec_info_.decode_h265.inline_params_info.pStdSPS = &codec_info_.decode_h265.std_inline_sps;
}
if (other.codec_info_.decode_h265.inline_params_info.pStdPPS) {
codec_info_.decode_h265.std_inline_pps = other.codec_info_.decode_h265.std_inline_pps;
codec_info_.decode_h265.inline_params_info.pStdPPS = &codec_info_.decode_h265.std_inline_pps;
}
ChainInfo(codec_info_.decode_h265.inline_params_info);
}
if (other.codec_info_.decode_av1.inline_params_info.sType ==
VK_STRUCTURE_TYPE_VIDEO_DECODE_AV1_INLINE_SESSION_PARAMETERS_INFO_KHR) {
codec_info_.decode_av1.inline_params_info = other.codec_info_.decode_av1.inline_params_info;
if (other.codec_info_.decode_av1.inline_params_info.pStdSequenceHeader) {
codec_info_.decode_av1.std_inline_seq_header = other.codec_info_.decode_av1.std_inline_seq_header;
codec_info_.decode_av1.inline_params_info.pStdSequenceHeader = &codec_info_.decode_av1.std_inline_seq_header;
}
ChainInfo(codec_info_.decode_av1.inline_params_info);
}
}
bool output_distinct_{};
VkVideoPictureResourceInfoKHR distinct_output_picture_{};
VideoDPB* dpb_{};
VkVideoReferenceSlotInfoKHR reconstructed_{};
std::vector<VkVideoReferenceSlotInfoKHR> references_{};
CodecInfoType codec_info_{};
VkVideoInlineQueryInfoKHR inline_query_info_{};
};
class VideoEncodeInfo : public VideoOpParams<VkVideoEncodeInfoKHR> {
public:
struct CodecInfoType {
struct {
VkVideoEncodeH264PictureInfoKHR picture_info{};
StdVideoEncodeH264PictureInfo std_picture_info{};
StdVideoEncodeH264ReferenceListsInfo std_ref_lists_info{};
std::vector<VkVideoEncodeH264NaluSliceInfoKHR> slice_info{};
std::vector<StdVideoEncodeH264SliceHeader> std_slice_headers{};
VkVideoEncodeH264DpbSlotInfoKHR setup_slot_info{};
StdVideoEncodeH264ReferenceInfo std_setup_reference_info{};
std::vector<VkVideoEncodeH264DpbSlotInfoKHR> dpb_slot_info{};
std::vector<StdVideoEncodeH264ReferenceInfo> std_reference_info{};
} encode_h264{};
struct {
VkVideoEncodeH265PictureInfoKHR picture_info{};
StdVideoEncodeH265PictureInfo std_picture_info{};
StdVideoEncodeH265ReferenceListsInfo std_ref_lists_info{};
std::vector<VkVideoEncodeH265NaluSliceSegmentInfoKHR> slice_segment_info{};
std::vector<StdVideoEncodeH265SliceSegmentHeader> std_slice_segment_headers{};
VkVideoEncodeH265DpbSlotInfoKHR setup_slot_info{};
StdVideoEncodeH265ReferenceInfo std_setup_reference_info{};
std::vector<VkVideoEncodeH265DpbSlotInfoKHR> dpb_slot_info{};
std::vector<StdVideoEncodeH265ReferenceInfo> std_reference_info{};
} encode_h265{};
struct {
VkVideoEncodeAV1PictureInfoKHR picture_info{};
StdVideoEncodeAV1PictureInfo std_picture_info{};
VkVideoEncodeAV1DpbSlotInfoKHR setup_slot_info{};
StdVideoEncodeAV1ReferenceInfo std_setup_reference_info{};
std::vector<VkVideoEncodeAV1DpbSlotInfoKHR> dpb_slot_info{};
std::vector<StdVideoEncodeAV1ReferenceInfo> std_reference_info{};
} encode_av1{};
};
VideoEncodeInfo(const VideoConfig& config, BitstreamBuffer& bitstream, VideoDPB* dpb, const VideoEncodeInput* input,
int32_t slot_index, const VkVideoPictureResourceInfoKHR* resource, bool reference = false)
: VideoOpParams<VkVideoEncodeInfoKHR>(config),
dpb_(dpb),
reconstructed_(vku::InitStruct<VkVideoReferenceSlotInfoKHR>()),
references_(dpb ? dpb->PictureCount() : 0, vku::InitStruct<VkVideoReferenceSlotInfoKHR>()),
codec_info_(),
inline_query_info_(),
quantization_map_info_(),
intra_refresh_info_(),
intra_refreshed_references_(dpb ? dpb->PictureCount() : 0) {
assert(config_->IsEncode());
assert(input != nullptr);
info_.dstBuffer = bitstream.Buffer();
info_.dstBufferOffset = 0;
info_.dstBufferRange = bitstream.Size();
info_.srcPictureResource = input->Picture();
assert(slot_index >= 0 || config.SessionCreateInfo()->maxDpbSlots == 0);
if (slot_index >= 0) {
if (resource == nullptr) {
resource = &dpb->Picture(slot_index);
}
reconstructed_.slotIndex = slot_index;
reconstructed_.pPictureResource = resource;
info_.pSetupReferenceSlot = &reconstructed_;
}
switch (config_->Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
codec_info_.encode_h264.picture_info = vku::InitStructHelper();
codec_info_.encode_h264.picture_info.pStdPictureInfo = &codec_info_.encode_h264.std_picture_info;
codec_info_.encode_h264.std_picture_info = {};
codec_info_.encode_h264.std_picture_info.flags.is_reference = reference ? 1 : 0;
codec_info_.encode_h264.std_picture_info.primary_pic_type = STD_VIDEO_H264_PICTURE_TYPE_I;
codec_info_.encode_h264.std_picture_info.pRefLists = &codec_info_.encode_h264.std_ref_lists_info;
codec_info_.encode_h264.std_ref_lists_info = {};
for (uint32_t i = 0; i < STD_VIDEO_H264_MAX_NUM_LIST_REF; ++i) {
codec_info_.encode_h264.std_ref_lists_info.RefPicList0[i] = STD_VIDEO_H264_NO_REFERENCE_PICTURE;
codec_info_.encode_h264.std_ref_lists_info.RefPicList1[i] = STD_VIDEO_H264_NO_REFERENCE_PICTURE;
}
reconstructed_.pNext = &codec_info_.encode_h264.setup_slot_info;
codec_info_.encode_h264.setup_slot_info = vku::InitStructHelper();
codec_info_.encode_h264.setup_slot_info.pStdReferenceInfo = &codec_info_.encode_h264.std_setup_reference_info;
codec_info_.encode_h264.std_setup_reference_info = {};
codec_info_.encode_h264.dpb_slot_info.resize(references_.size(),
vku::InitStruct<VkVideoEncodeH264DpbSlotInfoKHR>());
codec_info_.encode_h264.std_reference_info.resize(references_.size(), {});
for (size_t i = 0; i < references_.size(); ++i) {
references_[i].pNext = &codec_info_.encode_h264.dpb_slot_info[i];
codec_info_.encode_h264.dpb_slot_info[i].pStdReferenceInfo = &codec_info_.encode_h264.std_reference_info[i];
codec_info_.encode_h264.std_reference_info[i].primary_pic_type = STD_VIDEO_H264_PICTURE_TYPE_I;
}
ChainInfo(codec_info_.encode_h264.picture_info);
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
codec_info_.encode_h265.picture_info = vku::InitStructHelper();
codec_info_.encode_h265.picture_info.pStdPictureInfo = &codec_info_.encode_h265.std_picture_info;
codec_info_.encode_h265.std_picture_info = {};
codec_info_.encode_h265.std_picture_info.flags.is_reference = reference ? 1 : 0;
codec_info_.encode_h265.std_picture_info.pic_type = STD_VIDEO_H265_PICTURE_TYPE_I;
codec_info_.encode_h265.std_picture_info.pRefLists = &codec_info_.encode_h265.std_ref_lists_info;
codec_info_.encode_h265.std_ref_lists_info = {};
for (uint32_t i = 0; i < STD_VIDEO_H265_MAX_NUM_LIST_REF; ++i) {
codec_info_.encode_h265.std_ref_lists_info.RefPicList0[i] = STD_VIDEO_H265_NO_REFERENCE_PICTURE;
codec_info_.encode_h265.std_ref_lists_info.RefPicList1[i] = STD_VIDEO_H265_NO_REFERENCE_PICTURE;
}
reconstructed_.pNext = &codec_info_.encode_h265.setup_slot_info;
codec_info_.encode_h265.setup_slot_info = vku::InitStructHelper();
codec_info_.encode_h265.setup_slot_info.pStdReferenceInfo = &codec_info_.encode_h265.std_setup_reference_info;
codec_info_.encode_h265.std_setup_reference_info = {};
codec_info_.encode_h265.dpb_slot_info.resize(references_.size(),
vku::InitStruct<VkVideoEncodeH265DpbSlotInfoKHR>());
codec_info_.encode_h265.std_reference_info.resize(references_.size(), {});
for (size_t i = 0; i < references_.size(); ++i) {
references_[i].pNext = &codec_info_.encode_h265.dpb_slot_info[i];
codec_info_.encode_h265.dpb_slot_info[i].pStdReferenceInfo = &codec_info_.encode_h265.std_reference_info[i];
codec_info_.encode_h265.std_reference_info[i].pic_type = STD_VIDEO_H265_PICTURE_TYPE_I;
}
ChainInfo(codec_info_.encode_h265.picture_info);
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:
codec_info_.encode_av1.picture_info = vku::InitStructHelper();
codec_info_.encode_av1.picture_info.predictionMode = VK_VIDEO_ENCODE_AV1_PREDICTION_MODE_INTRA_ONLY_KHR;
codec_info_.encode_av1.picture_info.rateControlGroup = VK_VIDEO_ENCODE_AV1_RATE_CONTROL_GROUP_INTRA_KHR;
codec_info_.encode_av1.picture_info.pStdPictureInfo = &codec_info_.encode_av1.std_picture_info;
for (uint32_t i = 0; i < VK_MAX_VIDEO_AV1_REFERENCES_PER_FRAME_KHR; ++i) {
codec_info_.encode_av1.picture_info.referenceNameSlotIndices[i] = -1;
}
codec_info_.encode_av1.std_picture_info = {};
codec_info_.encode_av1.std_picture_info.frame_type = STD_VIDEO_AV1_FRAME_TYPE_KEY;
codec_info_.encode_av1.std_picture_info.primary_ref_frame = STD_VIDEO_AV1_PRIMARY_REF_NONE;
codec_info_.encode_av1.std_picture_info.refresh_frame_flags =
reference ? (1 << VK_MAX_VIDEO_AV1_REFERENCES_PER_FRAME_KHR) - 1 : 0;
reconstructed_.pNext = &codec_info_.encode_av1.setup_slot_info;
codec_info_.encode_av1.setup_slot_info = vku::InitStructHelper();
codec_info_.encode_av1.setup_slot_info.pStdReferenceInfo = &codec_info_.encode_av1.std_setup_reference_info;
codec_info_.encode_av1.std_setup_reference_info = {};
codec_info_.encode_av1.dpb_slot_info.resize(references_.size(), vku::InitStruct<VkVideoEncodeAV1DpbSlotInfoKHR>());
codec_info_.encode_av1.std_reference_info.resize(references_.size(), {});
for (size_t i = 0; i < references_.size(); ++i) {
references_[i].pNext = &codec_info_.encode_av1.dpb_slot_info[i];
codec_info_.encode_av1.dpb_slot_info[i].pStdReferenceInfo = &codec_info_.encode_av1.std_reference_info[i];
codec_info_.encode_av1.std_reference_info[i].frame_type = STD_VIDEO_AV1_FRAME_TYPE_KEY;
}
ChainInfo(codec_info_.encode_av1.picture_info);
break;
default:
break;
}
SetPartitionCount(1);
}
VideoEncodeInfo(VideoEncodeInfo const& other) : VideoOpParams<VkVideoEncodeInfoKHR>(other) { CopyData(other); }
VideoEncodeInfo& operator=(VideoEncodeInfo const& other) {
VideoOpParams<VkVideoEncodeInfoKHR>::operator=(other);
CopyData(other);
return *this;
}
VideoEncodeInfo& SetBitstream(const BitstreamBuffer& bitstream) {
info_.dstBuffer = bitstream.Buffer();
info_.dstBufferOffset = 0;
info_.dstBufferRange = bitstream.Size();
return *this;
}
VideoEncodeInfo& SetBitstreamBuffer(VkBuffer bitstream, VkDeviceSize offset, VkDeviceSize range) {
info_.dstBuffer = bitstream;
info_.dstBufferOffset = offset;
info_.dstBufferRange = range;
return *this;
}
VideoEncodeInfo& SetEncodeInput(const VideoEncodeInput* input) {
assert(input != nullptr);
info_.srcPictureResource = input->Picture();
return *this;
}
VideoEncodeInfo& SetEncodeInput(const VkVideoPictureResourceInfoKHR& resource) {
info_.srcPictureResource = resource;
return *this;
}
VideoEncodeInfo& SetPartitionCount(size_t count) {
switch (config_->Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
codec_info_.encode_h264.slice_info.resize(count, vku::InitStructHelper());
codec_info_.encode_h264.std_slice_headers.resize(count);
for (size_t i = 0; i < codec_info_.encode_h264.slice_info.size(); ++i) {
codec_info_.encode_h264.std_slice_headers[i].slice_type = STD_VIDEO_H264_SLICE_TYPE_I;
codec_info_.encode_h264.slice_info[i].pStdSliceHeader = &codec_info_.encode_h264.std_slice_headers[i];
}
codec_info_.encode_h264.picture_info.naluSliceEntryCount = (uint32_t)codec_info_.encode_h264.slice_info.size();
codec_info_.encode_h264.picture_info.pNaluSliceEntries = codec_info_.encode_h264.slice_info.data();
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
codec_info_.encode_h265.slice_segment_info.resize(count, vku::InitStructHelper());
codec_info_.encode_h265.std_slice_segment_headers.resize(count);
for (size_t i = 0; i < codec_info_.encode_h265.slice_segment_info.size(); ++i) {
codec_info_.encode_h265.std_slice_segment_headers[i].slice_type = STD_VIDEO_H265_SLICE_TYPE_I;
codec_info_.encode_h265.slice_segment_info[i].pStdSliceSegmentHeader =
&codec_info_.encode_h265.std_slice_segment_headers[i];
}
codec_info_.encode_h265.picture_info.naluSliceSegmentEntryCount =
(uint32_t)codec_info_.encode_h265.slice_segment_info.size();
codec_info_.encode_h265.picture_info.pNaluSliceSegmentEntries = codec_info_.encode_h265.slice_segment_info.data();
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:
// Nothing to do, AV1 encode does not guarantee tile count
break;
default:
assert(false);
break;
}
return *this;
}
VideoEncodeInfo& AddReferenceFrame(int32_t slot_index, const VkVideoPictureResourceInfoKHR* resource) {
return AddReferenceFrameInternal(slot_index, resource, false);
}
VideoEncodeInfo& AddReferenceFrame(int32_t slot_index, int32_t resource_index) {
return AddReferenceFrame(slot_index, &dpb_->Picture(resource_index));
}
VideoEncodeInfo& AddReferenceFrame(int32_t slot_index) { return AddReferenceFrame(slot_index, slot_index); }
VideoEncodeInfo& AddBackReferenceFrame(int32_t slot_index, const VkVideoPictureResourceInfoKHR* resource) {
return AddReferenceFrameInternal(slot_index, resource, true);
}
VideoEncodeInfo& AddBackReferenceFrame(int32_t slot_index, int32_t resource_index) {
return AddBackReferenceFrame(slot_index, &dpb_->Picture(resource_index));
}
VideoEncodeInfo& AddBackReferenceFrame(int32_t slot_index) { return AddBackReferenceFrame(slot_index, slot_index); }
VideoEncodeInfo& InlineQuery(VkQueryPool pool, uint32_t first = 0, uint32_t count = 1) {
inline_query_info_ = vku::InitStructHelper();
inline_query_info_.queryPool = pool;
inline_query_info_.firstQuery = first;
inline_query_info_.queryCount = count;
ChainInfo(inline_query_info_);
return *this;
}
VideoEncodeInfo& QuantizationMap(VkVideoEncodeFlagBitsKHR flag, VkExtent2D texel_size, VkImageView image_view) {
info_.flags |= flag;
quantization_map_info_ = vku::InitStructHelper();
quantization_map_info_.quantizationMap = image_view;
quantization_map_info_.quantizationMapExtent.width =
(info_.srcPictureResource.codedExtent.width + texel_size.width - 1) / texel_size.width;
quantization_map_info_.quantizationMapExtent.height =
(info_.srcPictureResource.codedExtent.height + texel_size.height - 1) / texel_size.height;
ChainInfo(quantization_map_info_);
return *this;
}
VideoEncodeInfo& IntraRefresh(uint32_t intra_refresh_cycle_duration, uint32_t intra_refresh_index) {
assert(intra_refresh_info_.sType != VK_STRUCTURE_TYPE_VIDEO_ENCODE_INTRA_REFRESH_INFO_KHR);
intra_refresh_info_ = vku::InitStructHelper();
intra_refresh_info_.intraRefreshCycleDuration = intra_refresh_cycle_duration;
// This frame will be encoded with intra refresh
info_.flags |= VK_VIDEO_ENCODE_INTRA_REFRESH_BIT_KHR;
intra_refresh_info_.intraRefreshIndex = intra_refresh_index;
if (config_->GetVideoEncodeIntraRefreshMode() == VK_VIDEO_ENCODE_INTRA_REFRESH_MODE_PER_PICTURE_PARTITION_BIT_KHR) {
SetPartitionCount(intra_refresh_cycle_duration);
switch (config_->Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
// Set all slice types that are current I to P, only keep the intra-refreshed slice I
for (size_t i = 0; i < codec_info_.encode_h264.std_slice_headers.size(); ++i) {
if (codec_info_.encode_h264.std_slice_headers[i].slice_type == STD_VIDEO_H264_SLICE_TYPE_I) {
codec_info_.encode_h264.std_slice_headers[i].slice_type = STD_VIDEO_H264_SLICE_TYPE_P;
}
}
codec_info_.encode_h264.std_slice_headers[intra_refresh_index].slice_type = STD_VIDEO_H264_SLICE_TYPE_I;
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
// Set all slice segment types that are current I to P, only keep the intra-refreshed slice segment I
for (size_t i = 0; i < codec_info_.encode_h265.std_slice_segment_headers.size(); ++i) {
if (codec_info_.encode_h265.std_slice_segment_headers[i].slice_type == STD_VIDEO_H265_SLICE_TYPE_I) {
codec_info_.encode_h265.std_slice_segment_headers[i].slice_type = STD_VIDEO_H265_SLICE_TYPE_P;
}
}
codec_info_.encode_h265.std_slice_segment_headers[intra_refresh_index].slice_type = STD_VIDEO_H265_SLICE_TYPE_I;
break;
default:
break;
}
}
ChainInfo(intra_refresh_info_);
return *this;
}
VideoEncodeInfo& AddReferenceFrameWithDirtyRegions(int32_t slot_index, const VkVideoPictureResourceInfoKHR* resource,
uint32_t dirty_intra_refresh_regions) {
// IntraRefresh must have been called either because this is an intra refreshed frame or because it uses
// intra refreshed references and hence the cycle duration needed to be specified
assert(intra_refresh_info_.sType == VK_STRUCTURE_TYPE_VIDEO_ENCODE_INTRA_REFRESH_INFO_KHR);
AddReferenceFrameInternal(slot_index, resource, false);
auto index = info_.referenceSlotCount - 1;
assert(intra_refreshed_references_[index].sType != VK_STRUCTURE_TYPE_VIDEO_REFERENCE_INTRA_REFRESH_INFO_KHR);
intra_refreshed_references_[index] = vku::InitStruct<VkVideoReferenceIntraRefreshInfoKHR>();
intra_refreshed_references_[index].dirtyIntraRefreshRegions = dirty_intra_refresh_regions;
// Chain to reference info
intra_refreshed_references_[index].pNext = references_[index].pNext;
references_[index].pNext = &intra_refreshed_references_[index];
return *this;
}
VideoEncodeInfo& AddReferenceFrameWithDirtyRegions(int32_t slot_index, int32_t resource_index,
uint32_t dirty_intra_refresh_regions) {
return AddReferenceFrameWithDirtyRegions(slot_index, &dpb_->Picture(resource_index), dirty_intra_refresh_regions);
}
VideoEncodeInfo& AddReferenceFrameWithDirtyRegions(int32_t slot_index, uint32_t dirty_intra_refresh_regions) {
return AddReferenceFrameWithDirtyRegions(slot_index, slot_index, dirty_intra_refresh_regions);
}
VideoEncodeInfo& QuantizationMap(VkVideoEncodeFlagBitsKHR flag, VkExtent2D texel_size,
VideoEncodeQuantizationMap& quantization_map) {
return QuantizationMap(flag, texel_size, quantization_map.ImageView());
}
CodecInfoType& CodecInfo() { return codec_info_; }
VkVideoEncodeQuantizationMapInfoKHR& QuantizationMapInfo() {
assert(quantization_map_info_.sType == VK_STRUCTURE_TYPE_VIDEO_ENCODE_QUANTIZATION_MAP_INFO_KHR);
return quantization_map_info_;
}
private:
void CopyData(VideoEncodeInfo const& other) {
dpb_ = other.dpb_;
reconstructed_ = other.reconstructed_;
references_ = other.references_;
if (other.info_.pSetupReferenceSlot != nullptr) {
info_.pSetupReferenceSlot = &reconstructed_;
}
if (other.info_.pReferenceSlots != nullptr) {
info_.pReferenceSlots = references_.data();
}
switch (config_->Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
codec_info_.encode_h264.slice_info = other.codec_info_.encode_h264.slice_info;
codec_info_.encode_h264.std_slice_headers = other.codec_info_.encode_h264.std_slice_headers;
for (size_t i = 0; i < codec_info_.encode_h264.slice_info.size(); ++i) {
codec_info_.encode_h264.slice_info[i].pStdSliceHeader = &codec_info_.encode_h264.std_slice_headers[i];
}
codec_info_.encode_h264.picture_info = other.codec_info_.encode_h264.picture_info;
codec_info_.encode_h264.picture_info.pNaluSliceEntries = codec_info_.encode_h264.slice_info.data();
codec_info_.encode_h264.picture_info.pStdPictureInfo = &codec_info_.encode_h264.std_picture_info;
codec_info_.encode_h264.std_picture_info = other.codec_info_.encode_h264.std_picture_info;
codec_info_.encode_h264.std_picture_info.pRefLists = &codec_info_.encode_h264.std_ref_lists_info;
codec_info_.encode_h264.std_ref_lists_info = other.codec_info_.encode_h264.std_ref_lists_info;
reconstructed_.pNext = &codec_info_.encode_h264.setup_slot_info;
codec_info_.encode_h264.setup_slot_info = other.codec_info_.encode_h264.setup_slot_info;
codec_info_.encode_h264.setup_slot_info.pStdReferenceInfo = &codec_info_.encode_h264.std_setup_reference_info;
codec_info_.encode_h264.std_setup_reference_info = other.codec_info_.encode_h264.std_setup_reference_info;
codec_info_.encode_h264.dpb_slot_info = other.codec_info_.encode_h264.dpb_slot_info;
codec_info_.encode_h264.std_reference_info = other.codec_info_.encode_h264.std_reference_info;
for (size_t i = 0; i < references_.size(); ++i) {
references_[i].pNext = &codec_info_.encode_h264.dpb_slot_info[i];
codec_info_.encode_h264.dpb_slot_info[i].pStdReferenceInfo = &codec_info_.encode_h264.std_reference_info[i];
}
ChainInfo(codec_info_.encode_h264.picture_info);
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
codec_info_.encode_h265.slice_segment_info = other.codec_info_.encode_h265.slice_segment_info;
codec_info_.encode_h265.std_slice_segment_headers = other.codec_info_.encode_h265.std_slice_segment_headers;
for (size_t i = 0; i < codec_info_.encode_h265.slice_segment_info.size(); ++i) {
codec_info_.encode_h265.slice_segment_info[i].pStdSliceSegmentHeader =
&codec_info_.encode_h265.std_slice_segment_headers[i];
}
codec_info_.encode_h265.picture_info = other.codec_info_.encode_h265.picture_info;
codec_info_.encode_h265.picture_info.pNaluSliceSegmentEntries = codec_info_.encode_h265.slice_segment_info.data();
codec_info_.encode_h265.picture_info.pStdPictureInfo = &codec_info_.encode_h265.std_picture_info;
codec_info_.encode_h265.std_picture_info = other.codec_info_.encode_h265.std_picture_info;
codec_info_.encode_h265.std_picture_info.pRefLists = &codec_info_.encode_h265.std_ref_lists_info;
codec_info_.encode_h265.std_ref_lists_info = other.codec_info_.encode_h265.std_ref_lists_info;
reconstructed_.pNext = &codec_info_.encode_h265.setup_slot_info;
codec_info_.encode_h265.setup_slot_info = other.codec_info_.encode_h265.setup_slot_info;
codec_info_.encode_h265.setup_slot_info.pStdReferenceInfo = &codec_info_.encode_h265.std_setup_reference_info;
codec_info_.encode_h265.std_setup_reference_info = other.codec_info_.encode_h265.std_setup_reference_info;
codec_info_.encode_h265.dpb_slot_info = other.codec_info_.encode_h265.dpb_slot_info;
codec_info_.encode_h265.std_reference_info = other.codec_info_.encode_h265.std_reference_info;
for (size_t i = 0; i < references_.size(); ++i) {
references_[i].pNext = &codec_info_.encode_h265.dpb_slot_info[i];
codec_info_.encode_h265.dpb_slot_info[i].pStdReferenceInfo = &codec_info_.encode_h265.std_reference_info[i];
}
ChainInfo(codec_info_.encode_h265.picture_info);
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:
codec_info_.encode_av1.picture_info = other.codec_info_.encode_av1.picture_info;
codec_info_.encode_av1.picture_info.pStdPictureInfo = &codec_info_.encode_av1.std_picture_info;
codec_info_.encode_av1.std_picture_info = other.codec_info_.encode_av1.std_picture_info;
reconstructed_.pNext = &codec_info_.encode_av1.setup_slot_info;
codec_info_.encode_av1.setup_slot_info = other.codec_info_.encode_av1.setup_slot_info;
codec_info_.encode_av1.setup_slot_info.pStdReferenceInfo = &codec_info_.encode_av1.std_setup_reference_info;
codec_info_.encode_av1.std_setup_reference_info = other.codec_info_.encode_av1.std_setup_reference_info;
codec_info_.encode_av1.dpb_slot_info = other.codec_info_.encode_av1.dpb_slot_info;
codec_info_.encode_av1.std_reference_info = other.codec_info_.encode_av1.std_reference_info;
for (size_t i = 0; i < references_.size(); ++i) {
references_[i].pNext = &codec_info_.encode_av1.dpb_slot_info[i];
codec_info_.encode_av1.dpb_slot_info[i].pStdReferenceInfo = &codec_info_.encode_av1.std_reference_info[i];
}
ChainInfo(codec_info_.encode_av1.picture_info);
break;
default:
break;
}
if (other.inline_query_info_.sType == VK_STRUCTURE_TYPE_VIDEO_INLINE_QUERY_INFO_KHR) {
inline_query_info_ = other.inline_query_info_;
ChainInfo(inline_query_info_);
}
if (other.quantization_map_info_.sType == VK_STRUCTURE_TYPE_VIDEO_ENCODE_QUANTIZATION_MAP_INFO_KHR) {
quantization_map_info_ = other.quantization_map_info_;
ChainInfo(quantization_map_info_);
}
if (other.intra_refresh_info_.sType == VK_STRUCTURE_TYPE_VIDEO_ENCODE_INTRA_REFRESH_INFO_KHR) {
intra_refresh_info_ = other.intra_refresh_info_;
ChainInfo(intra_refresh_info_);
intra_refreshed_references_.resize(other.intra_refreshed_references_.size());
for (size_t i = 0; i < other.intra_refreshed_references_.size(); ++i) {
if (other.intra_refreshed_references_[i].sType == VK_STRUCTURE_TYPE_VIDEO_REFERENCE_INTRA_REFRESH_INFO_KHR) {
intra_refreshed_references_[i] = other.intra_refreshed_references_[i];
intra_refreshed_references_[i].pNext = references_[i].pNext;
references_[i].pNext = &intra_refreshed_references_[i];
}
}
}
}
VideoEncodeInfo& AddReferenceFrameInternal(int32_t slot_index, const VkVideoPictureResourceInfoKHR* resource,
bool back_reference) {
assert(dpb_ != nullptr);
size_t index = info_.referenceSlotCount++;
references_[index].slotIndex = slot_index;
references_[index].pPictureResource = resource;
info_.pReferenceSlots = references_.data();
// We have to skip overriding the slice (segment) type of the intra-refreshed slice (segment) if using
// per-picture-partition intra refresh
const uint32_t intra_refresh_index =
(config_->GetVideoEncodeIntraRefreshMode() == VK_VIDEO_ENCODE_INTRA_REFRESH_MODE_PER_PICTURE_PARTITION_BIT_KHR) &&
(intra_refresh_info_.sType == VK_STRUCTURE_TYPE_VIDEO_ENCODE_INTRA_REFRESH_INFO_KHR)
? intra_refresh_info_.intraRefreshIndex
: UINT32_MAX;
switch (config_->Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
if (codec_info_.encode_h264.std_picture_info.primary_pic_type != STD_VIDEO_H264_PICTURE_TYPE_B) {
bool use_b_pic = back_reference || config_->EncodeCapsH264()->maxPPictureL0ReferenceCount == 0;
codec_info_.encode_h264.std_picture_info.primary_pic_type =
use_b_pic ? STD_VIDEO_H264_PICTURE_TYPE_B : STD_VIDEO_H264_PICTURE_TYPE_P;
for (size_t i = 0; i < codec_info_.encode_h264.std_slice_headers.size(); ++i) {
if (i != intra_refresh_index) {
codec_info_.encode_h264.std_slice_headers[i].slice_type =
use_b_pic ? STD_VIDEO_H264_SLICE_TYPE_B : STD_VIDEO_H264_SLICE_TYPE_P;
}
}
}
AddReferenceInfoH264(slot_index, !back_reference);
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
if (codec_info_.encode_h265.std_picture_info.pic_type != STD_VIDEO_H265_PICTURE_TYPE_B) {
bool use_b_pic = back_reference || config_->EncodeCapsH265()->maxPPictureL0ReferenceCount == 0;
codec_info_.encode_h265.std_picture_info.pic_type =
use_b_pic ? STD_VIDEO_H265_PICTURE_TYPE_B : STD_VIDEO_H265_PICTURE_TYPE_P;
for (size_t i = 0; i < codec_info_.encode_h265.std_slice_segment_headers.size(); ++i) {
if (i != intra_refresh_index) {
codec_info_.encode_h265.std_slice_segment_headers[i].slice_type =
use_b_pic ? STD_VIDEO_H265_SLICE_TYPE_B : STD_VIDEO_H265_SLICE_TYPE_P;
}
}
}
AddReferenceInfoH265(slot_index, !back_reference);
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:
codec_info_.encode_av1.std_picture_info.frame_type = STD_VIDEO_AV1_FRAME_TYPE_INTER;
AddReferenceInfoAV1(slot_index, back_reference);
break;
default:
break;
}
return *this;
}
void AddReferenceInfoH264(int32_t slot_index, bool L0 = true) {
uint8_t& active_minus1 = L0 ? codec_info_.encode_h264.std_ref_lists_info.num_ref_idx_l0_active_minus1
: codec_info_.encode_h264.std_ref_lists_info.num_ref_idx_l1_active_minus1;
uint8_t* ref_pic_list = L0 ? &codec_info_.encode_h264.std_ref_lists_info.RefPicList0[0]
: &codec_info_.encode_h264.std_ref_lists_info.RefPicList1[0];
if (ref_pic_list[0] == STD_VIDEO_H264_NO_REFERENCE_PICTURE) {
ref_pic_list[0] = (uint8_t)slot_index;
} else {
ref_pic_list[++active_minus1] = (uint8_t)slot_index;
}
}
void AddReferenceInfoH265(int32_t slot_index, bool L0 = true) {
uint8_t& active_minus1 = L0 ? codec_info_.encode_h265.std_ref_lists_info.num_ref_idx_l0_active_minus1
: codec_info_.encode_h265.std_ref_lists_info.num_ref_idx_l1_active_minus1;
uint8_t* ref_pic_list = L0 ? &codec_info_.encode_h265.std_ref_lists_info.RefPicList0[0]
: &codec_info_.encode_h265.std_ref_lists_info.RefPicList1[0];
if (ref_pic_list[0] == STD_VIDEO_H265_NO_REFERENCE_PICTURE) {
ref_pic_list[0] = (uint8_t)slot_index;
} else {
ref_pic_list[++active_minus1] = (uint8_t)slot_index;
}
}
void AddReferenceInfoAV1(int32_t slot_index, bool back_reference) {
codec_info_.encode_av1.picture_info.predictionMode = config_->GetAnySupportedAV1PredictionMode();
codec_info_.encode_av1.picture_info.rateControlGroup = back_reference
? VK_VIDEO_ENCODE_AV1_RATE_CONTROL_GROUP_BIPREDICTIVE_KHR
: VK_VIDEO_ENCODE_AV1_RATE_CONTROL_GROUP_PREDICTIVE_KHR;
if (info_.referenceSlotCount == 1) {
codec_info_.encode_av1.std_picture_info.primary_ref_frame = 0;
codec_info_.encode_av1.std_picture_info.frame_type = STD_VIDEO_AV1_FRAME_TYPE_INTER;
}
for (uint32_t i = info_.referenceSlotCount - 1; i < VK_MAX_VIDEO_AV1_REFERENCES_PER_FRAME_KHR; ++i) {
codec_info_.encode_av1.picture_info.referenceNameSlotIndices[i] = slot_index;
}
}
VideoDPB* dpb_{};
VkVideoReferenceSlotInfoKHR reconstructed_{};
std::vector<VkVideoReferenceSlotInfoKHR> references_{};
CodecInfoType codec_info_{};
VkVideoInlineQueryInfoKHR inline_query_info_{};
VkVideoEncodeQuantizationMapInfoKHR quantization_map_info_{};
VkVideoEncodeIntraRefreshInfoKHR intra_refresh_info_{};
std::vector<VkVideoReferenceIntraRefreshInfoKHR> intra_refreshed_references_{};
};
class VideoContext {
public:
class Params {
public:
Params(VkDevice device = VK_NULL_HANDLE, VkVideoSessionParametersKHR params = VK_NULL_HANDLE)
: device_(device), params_(params) {}
~Params() {
if (params_ != VK_NULL_HANDLE) {
vk::DestroyVideoSessionParametersKHR(device_, params_, nullptr);
params_ = VK_NULL_HANDLE;
}
}
Params(const Params&) = delete;
Params& operator=(const Params&) = delete;
Params(Params&& params) { MoveFrom(std::move(params)); }
Params& operator=(Params&& params) { return MoveFrom(std::move(params)); }
operator bool() const { return params_ != VK_NULL_HANDLE; }
operator VkVideoSessionParametersKHR() const { return params_; }
private:
Params& MoveFrom(Params&& params) {
device_ = params.device_;
params_ = params.params_;
params.params_ = VK_NULL_HANDLE;
return *this;
}
VkDevice device_;
VkVideoSessionParametersKHR params_;
};
explicit VideoContext(vkt::Device* device, const VideoConfig& config, bool protected_content = false)
: config_(config),
device_(device),
queue_(GetQueue(device, config)),
cmd_pool_(
*device, queue_.family_index,
VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT | (protected_content ? VK_COMMAND_POOL_CREATE_PROTECTED_BIT : 0)),
cmd_buffer_(*device, cmd_pool_, VK_COMMAND_BUFFER_LEVEL_PRIMARY),
session_(VK_NULL_HANDLE),
session_memory_(),
session_params_(),
status_query_pool_(VK_NULL_HANDLE),
encode_feedback_query_pool_(VK_NULL_HANDLE),
bitstream_(nullptr),
dpb_(nullptr),
decode_output_(nullptr),
encode_input_(nullptr),
dep_info_(vku::InitStruct<VkDependencyInfo>()),
barrier_(vku::InitStruct<VkMemoryBarrier2>()) {
Init(protected_content);
}
~VideoContext() { Destroy(); }
const VideoConfig& Config() const { return config_; }
void CreateAndBindSessionMemory() {
ASSERT_TRUE(session_ != VK_NULL_HANDLE);
uint32_t mem_req_count = 0;
ASSERT_EQ(VK_SUCCESS, vk::GetVideoSessionMemoryRequirementsKHR(device_->handle(), session_, &mem_req_count, nullptr));
if (mem_req_count == 0) return;
std::vector<VkVideoSessionMemoryRequirementsKHR> mem_reqs(mem_req_count,
vku::InitStruct<VkVideoSessionMemoryRequirementsKHR>());
ASSERT_EQ(VK_SUCCESS,
vk::GetVideoSessionMemoryRequirementsKHR(device_->handle(), session_, &mem_req_count, mem_reqs.data()));
std::vector<VkBindVideoSessionMemoryInfoKHR> bind_info(mem_req_count, vku::InitStruct<VkBindVideoSessionMemoryInfoKHR>());
for (uint32_t i = 0; i < mem_req_count; ++i) {
VkMemoryAllocateInfo alloc_info = vku::InitStructHelper();
ASSERT_TRUE(device_->Physical().SetMemoryType(mem_reqs[i].memoryRequirements.memoryTypeBits, &alloc_info, 0));
alloc_info.allocationSize = mem_reqs[i].memoryRequirements.size;
VkDeviceMemory memory = VK_NULL_HANDLE;
ASSERT_EQ(VK_SUCCESS, vk::AllocateMemory(device_->handle(), &alloc_info, nullptr, &memory));
session_memory_.push_back(memory);
bind_info[i].memoryBindIndex = mem_reqs[i].memoryBindIndex;
bind_info[i].memory = memory;
bind_info[i].memoryOffset = 0;
bind_info[i].memorySize = mem_reqs[i].memoryRequirements.size;
}
ASSERT_EQ(VK_SUCCESS,
vk::BindVideoSessionMemoryKHR(device_->handle(), session_, (uint32_t)bind_info.size(), bind_info.data()));
}
void CreateResources(bool protected_bitstream = false, bool protected_dpb = false, bool protected_image = false) {
VkDeviceSize buffer_size = 0;
if (config_.IsDecode()) {
// A small placeholder buffer should be enough as the input bitstream buffer for decode
buffer_size = std::max((VkDeviceSize)4096, config_.Caps()->minBitstreamBufferSizeAlignment * 2);
}
if (config_.IsEncode()) {
// For encode we should be conservative and request a large bitstream buffer just in case
buffer_size = 4 * 1024 * 1024;
}
bitstream_ = std::unique_ptr<BitstreamBuffer>(new BitstreamBuffer(device_, config_, buffer_size, protected_bitstream));
if (config_.SessionCreateInfo()->maxDpbSlots > 0) {
dpb_ = std::unique_ptr<VideoDPB>(new VideoDPB(device_, config_, protected_dpb));
}
if (config_.IsDecode()) {
decode_output_ = std::unique_ptr<VideoDecodeOutput>(new VideoDecodeOutput(device_, config_, protected_image));
}
if (config_.IsEncode()) {
encode_input_ = std::unique_ptr<VideoEncodeInput>(new VideoEncodeInput(device_, config_, protected_image));
}
}
void CreateStatusQueryPool(uint32_t query_count = 1) {
VkQueryPoolCreateInfo create_info = vku::InitStructHelper();
create_info.pNext = config_.Profile();
create_info.queryType = VK_QUERY_TYPE_RESULT_STATUS_ONLY_KHR;
create_info.queryCount = query_count;
ASSERT_EQ(VK_SUCCESS, vk::CreateQueryPool(device_->handle(), &create_info, nullptr, &status_query_pool_));
}
void CreateEncodeFeedbackQueryPool(uint32_t query_count = 1, VkVideoEncodeFeedbackFlagsKHR encode_feedback_flags =
VK_VIDEO_ENCODE_FEEDBACK_BITSTREAM_BUFFER_OFFSET_BIT_KHR |
VK_VIDEO_ENCODE_FEEDBACK_BITSTREAM_BYTES_WRITTEN_BIT_KHR) {
assert(config_.IsEncode());
auto encode_feedback_info = vku::InitStruct<VkQueryPoolVideoEncodeFeedbackCreateInfoKHR>();
encode_feedback_info.pNext = config_.Profile();
encode_feedback_info.encodeFeedbackFlags = encode_feedback_flags;
auto create_info = vku::InitStruct<VkQueryPoolCreateInfo>(&encode_feedback_info);
create_info.queryType = VK_QUERY_TYPE_VIDEO_ENCODE_FEEDBACK_KHR;
create_info.queryCount = query_count;
ASSERT_EQ(VK_SUCCESS, vk::CreateQueryPool(device_->handle(), &create_info, nullptr, &encode_feedback_query_pool_));
}
Params CreateSessionParams(VkVideoSessionParametersCreateFlagsKHR flags = 0, void* pNext = nullptr) {
VkVideoSessionParametersCreateInfoKHR create_info = *config_.SessionParamsCreateInfo();
create_info.flags = flags;
create_info.videoSession = session_;
if (pNext != nullptr) {
auto last_struct = reinterpret_cast<VkBaseInStructure*>(vku::FindLastStructInPNextChain(pNext));
last_struct->pNext = reinterpret_cast<const VkBaseInStructure*>(create_info.pNext);
create_info.pNext = pNext;
}
VkVideoSessionParametersKHR params = VK_NULL_HANDLE;
vk::CreateVideoSessionParametersKHR(device_->handle(), &create_info, nullptr, &params);
return Params(device_->handle(), params);
}
Params CreateSessionParamsWithQuantMapTexelSize(VkExtent2D texel_size) {
auto quant_map_info = vku::InitStruct<VkVideoEncodeQuantizationMapSessionParametersCreateInfoKHR>();
quant_map_info.quantizationMapTexelSize = texel_size;
return CreateSessionParams(VK_VIDEO_SESSION_PARAMETERS_CREATE_QUANTIZATION_MAP_COMPATIBLE_BIT_KHR, &quant_map_info);
}
StdVideoH264SequenceParameterSet CreateH264SPS(uint8_t sps_id) const { return config_.CreateH264SPS(sps_id); }
StdVideoH264PictureParameterSet CreateH264PPS(uint8_t sps_id, uint8_t pps_id) const {
return config_.CreateH264PPS(sps_id, pps_id);
}
StdVideoH265VideoParameterSet CreateH265VPS(uint8_t vps_id) const { return config_.CreateH265VPS(vps_id); }
StdVideoH265SequenceParameterSet CreateH265SPS(uint8_t vps_id, uint8_t sps_id) const {
return config_.CreateH265SPS(vps_id, sps_id);
}
StdVideoH265PictureParameterSet CreateH265PPS(uint8_t vps_id, uint8_t sps_id, uint8_t pps_id) const {
return config_.CreateH265PPS(vps_id, sps_id, pps_id);
}
VkVideoSessionKHR Session() { return session_; }
VkVideoSessionParametersKHR SessionParams() { return session_params_; }
VkQueryPool StatusQueryPool() { return status_query_pool_; }
VkQueryPool EncodeFeedbackQueryPool() { return encode_feedback_query_pool_; }
vkt::Queue& Queue() { return queue_; }
vkt::CommandBuffer& CmdBuffer() { return cmd_buffer_; }
BitstreamBuffer& Bitstream() { return *bitstream_; }
VideoDPB* Dpb() { return dpb_.get(); }
VideoDecodeOutput* DecodeOutput() { return decode_output_.get(); }
VideoEncodeInput* EncodeInput() { return encode_input_.get(); }
VideoBeginCodingInfo Begin() const { return VideoBeginCodingInfo(config_, dpb_.get(), session_, session_params_); }
VideoCodingControlInfo Control() const { return VideoCodingControlInfo(config_); }
VideoEndCodingInfo End() const { return VideoEndCodingInfo(config_); }
VideoDecodeInfo DecodeFrame(int32_t slot_index = -1, const VkVideoPictureResourceInfoKHR* resource = nullptr) {
return Decode(slot_index, resource);
}
VideoDecodeInfo DecodeFrame(int32_t slot_index, int32_t resource_index) {
return Decode(slot_index, &Dpb()->Picture(resource_index));
}
VideoDecodeInfo DecodeTopField(int32_t slot_index = -1, const VkVideoPictureResourceInfoKHR* resource = nullptr) {
return Decode(slot_index, resource).SetTopField();
}
VideoDecodeInfo DecodeTopField(int32_t slot_index, int32_t resource_index) {
return Decode(slot_index, &Dpb()->Picture(resource_index)).SetTopField();
}
VideoDecodeInfo DecodeBottomField(int32_t slot_index = -1, const VkVideoPictureResourceInfoKHR* resource = nullptr) {
return Decode(slot_index, resource).SetBottomField();
}
VideoDecodeInfo DecodeBottomField(int32_t slot_index, int32_t resource_index) {
return Decode(slot_index, &Dpb()->Picture(resource_index)).SetBottomField();
}
VideoDecodeInfo DecodeReferenceFrame(int32_t slot_index = -1, const VkVideoPictureResourceInfoKHR* resource = nullptr) {
return Decode(slot_index, resource, true);
}
VideoDecodeInfo DecodeReferenceFrame(int32_t slot_index, int32_t resource_index) {
return Decode(slot_index, &Dpb()->Picture(resource_index), true);
}
VideoDecodeInfo DecodeReferenceTopField(int32_t slot_index = -1, const VkVideoPictureResourceInfoKHR* resource = nullptr) {
return Decode(slot_index, resource, true).SetTopField();
}
VideoDecodeInfo DecodeReferenceTopField(int32_t slot_index, int32_t resource_index) {
return Decode(slot_index, &Dpb()->Picture(resource_index), true).SetTopField();
}
VideoDecodeInfo DecodeReferenceBottomField(int32_t slot_index = -1, const VkVideoPictureResourceInfoKHR* resource = nullptr) {
return Decode(slot_index, resource, true).SetBottomField();
}
VideoDecodeInfo DecodeReferenceBottomField(int32_t slot_index, int32_t resource_index) {
return Decode(slot_index, &Dpb()->Picture(resource_index), true).SetBottomField();
}
VideoEncodeInfo EncodeFrame(int32_t slot_index = -1, const VkVideoPictureResourceInfoKHR* resource = nullptr) {
return Encode(slot_index, resource);
}
VideoEncodeInfo EncodeFrame(int32_t slot_index, int32_t resource_index) {
return Encode(slot_index, &Dpb()->Picture(resource_index));
}
VideoEncodeInfo EncodeReferenceFrame(int32_t slot_index = -1, const VkVideoPictureResourceInfoKHR* resource = nullptr) {
return Encode(slot_index, resource, true);
}
VideoEncodeInfo EncodeReferenceFrame(int32_t slot_index, int32_t resource_index) {
return Encode(slot_index, &Dpb()->Picture(resource_index), true);
}
const VkDependencyInfo* MemoryBarrier() {
if (config_.IsDecode()) {
return MemoryBarrier(VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR,
VK_ACCESS_2_VIDEO_DECODE_READ_BIT_KHR | VK_ACCESS_2_VIDEO_DECODE_WRITE_BIT_KHR,
VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR,
VK_ACCESS_2_VIDEO_DECODE_READ_BIT_KHR | VK_ACCESS_2_VIDEO_DECODE_WRITE_BIT_KHR);
}
if (config_.IsEncode()) {
return MemoryBarrier(VK_PIPELINE_STAGE_2_VIDEO_ENCODE_BIT_KHR,
VK_ACCESS_2_VIDEO_ENCODE_READ_BIT_KHR | VK_ACCESS_2_VIDEO_ENCODE_WRITE_BIT_KHR,
VK_PIPELINE_STAGE_2_VIDEO_ENCODE_BIT_KHR,
VK_ACCESS_2_VIDEO_ENCODE_READ_BIT_KHR | VK_ACCESS_2_VIDEO_ENCODE_WRITE_BIT_KHR);
}
assert(false);
dep_info_ = vku::InitStructHelper();
return &dep_info_;
}
const VkDependencyInfo* MemoryBarrier(VkPipelineStageFlags2 src_stages, VkAccessFlagBits2 src_access,
VkPipelineStageFlags2 dst_stages, VkAccessFlagBits2 dst_access) {
dep_info_ = vku::InitStructHelper();
dep_info_.memoryBarrierCount = 1;
dep_info_.pMemoryBarriers = &barrier_;
barrier_ = vku::InitStructHelper();
barrier_.srcStageMask = src_stages;
barrier_.srcAccessMask = src_access;
barrier_.dstStageMask = dst_stages;
barrier_.dstAccessMask = dst_access;
return &dep_info_;
}
private:
VideoDecodeInfo Decode(int32_t slot_index, const VkVideoPictureResourceInfoKHR* resource, bool reference = false) {
return VideoDecodeInfo(config_, *bitstream_, dpb_.get(), decode_output_.get(), slot_index, resource, reference);
}
VideoEncodeInfo Encode(int32_t slot_index, const VkVideoPictureResourceInfoKHR* resource, bool reference = false) {
return VideoEncodeInfo(config_, *bitstream_, dpb_.get(), encode_input_.get(), slot_index, resource, reference);
}
vkt::Queue GetQueue(vkt::Device* device, const VideoConfig& config) const {
VkQueue queue = VK_NULL_HANDLE;
assert(config.QueueFamilyIndex() != VK_QUEUE_FAMILY_IGNORED);
vk::GetDeviceQueue(device->handle(), config.QueueFamilyIndex(), 0, &queue);
return vkt::Queue(queue, config.QueueFamilyIndex());
}
void Init(bool protected_content) {
ASSERT_TRUE(queue_ != VK_NULL_HANDLE);
ASSERT_TRUE(cmd_pool_ != VK_NULL_HANDLE);
ASSERT_TRUE(cmd_buffer_ != VK_NULL_HANDLE);
{
VkVideoSessionCreateInfoKHR create_info = *config_.SessionCreateInfo();
if (protected_content) {
create_info.flags |= VK_VIDEO_SESSION_CREATE_PROTECTED_CONTENT_BIT_KHR;
}
create_info.pVideoProfile = config_.Profile();
create_info.pStdHeaderVersion = config_.StdVersion();
ASSERT_EQ(VK_SUCCESS, vk::CreateVideoSessionKHR(device_->handle(), &create_info, nullptr, &session_));
}
if (config_.NeedsSessionParams()) {
session_params_ = CreateSessionParams();
ASSERT_TRUE(session_params_);
}
}
void Destroy() {
vk::DestroyVideoSessionKHR(device_->handle(), session_, nullptr);
vk::DestroyQueryPool(device_->handle(), status_query_pool_, nullptr);
vk::DestroyQueryPool(device_->handle(), encode_feedback_query_pool_, nullptr);
for (auto session_memory : session_memory_) {
vk::FreeMemory(device_->handle(), session_memory, nullptr);
}
}
const VideoConfig config_{};
vkt::Device* device_{};
vkt::Queue queue_;
vkt::CommandPool cmd_pool_{};
vkt::CommandBuffer cmd_buffer_{};
VkVideoSessionKHR session_{};
std::vector<VkDeviceMemory> session_memory_{};
Params session_params_{};
VkQueryPool status_query_pool_{};
VkQueryPool encode_feedback_query_pool_{};
std::unique_ptr<BitstreamBuffer> bitstream_{};
std::unique_ptr<VideoDPB> dpb_{};
std::unique_ptr<VideoDecodeOutput> decode_output_{};
std::unique_ptr<VideoEncodeInput> encode_input_{};
VkDependencyInfo dep_info_{};
VkMemoryBarrier2 barrier_{};
};
class VideoEncodeRateControlTestUtils {
public:
VideoEncodeRateControlTestUtils(VkLayerTest* test, VideoContext& context) : test_(test), context_(context) {}
VideoEncodeRateControlLayerInfo CreateRateControlLayerWithMinMaxQp() const {
const auto& config = context_.Config();
auto rc_layer = VideoEncodeRateControlLayerInfo(config, true);
switch (config.Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
rc_layer.CodecInfo().encode_h264.useMinQp = VK_TRUE;
rc_layer.CodecInfo().encode_h264.minQp.qpI = config.EncodeCapsH264()->minQp;
rc_layer.CodecInfo().encode_h264.minQp.qpP = rc_layer.CodecInfo().encode_h264.minQp.qpI;
rc_layer.CodecInfo().encode_h264.minQp.qpB = rc_layer.CodecInfo().encode_h264.minQp.qpI;
rc_layer.CodecInfo().encode_h264.useMaxQp = VK_TRUE;
rc_layer.CodecInfo().encode_h264.maxQp.qpI = config.EncodeCapsH264()->maxQp;
rc_layer.CodecInfo().encode_h264.maxQp.qpP = rc_layer.CodecInfo().encode_h264.maxQp.qpI;
rc_layer.CodecInfo().encode_h264.maxQp.qpB = rc_layer.CodecInfo().encode_h264.maxQp.qpI;
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
rc_layer.CodecInfo().encode_h265.useMinQp = VK_TRUE;
rc_layer.CodecInfo().encode_h265.minQp.qpI = config.EncodeCapsH265()->minQp;
rc_layer.CodecInfo().encode_h265.minQp.qpP = rc_layer.CodecInfo().encode_h265.minQp.qpI;
rc_layer.CodecInfo().encode_h265.minQp.qpB = rc_layer.CodecInfo().encode_h265.minQp.qpI;
rc_layer.CodecInfo().encode_h265.useMaxQp = VK_TRUE;
rc_layer.CodecInfo().encode_h265.maxQp.qpI = config.EncodeCapsH265()->maxQp;
rc_layer.CodecInfo().encode_h265.maxQp.qpP = rc_layer.CodecInfo().encode_h265.maxQp.qpI;
rc_layer.CodecInfo().encode_h265.maxQp.qpB = rc_layer.CodecInfo().encode_h265.maxQp.qpI;
break;
default:
assert(false);
break;
}
return rc_layer;
}
VideoEncodeRateControlLayerInfo CreateRateControlLayerWithMinMaxQIndex() const {
const auto& config = context_.Config();
auto rc_layer = VideoEncodeRateControlLayerInfo(config, true);
switch (config.Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:
rc_layer.CodecInfo().encode_av1.useMinQIndex = VK_TRUE;
rc_layer.CodecInfo().encode_av1.minQIndex.intraQIndex = config.EncodeCapsAV1()->minQIndex;
rc_layer.CodecInfo().encode_av1.minQIndex.predictiveQIndex = rc_layer.CodecInfo().encode_av1.minQIndex.intraQIndex;
rc_layer.CodecInfo().encode_av1.minQIndex.bipredictiveQIndex =
rc_layer.CodecInfo().encode_av1.minQIndex.intraQIndex;
rc_layer.CodecInfo().encode_av1.useMaxQIndex = VK_TRUE;
rc_layer.CodecInfo().encode_av1.maxQIndex.intraQIndex = config.EncodeCapsAV1()->maxQIndex;
rc_layer.CodecInfo().encode_av1.maxQIndex.predictiveQIndex = rc_layer.CodecInfo().encode_av1.maxQIndex.intraQIndex;
rc_layer.CodecInfo().encode_av1.maxQIndex.bipredictiveQIndex =
rc_layer.CodecInfo().encode_av1.maxQIndex.intraQIndex;
break;
default:
assert(false);
break;
}
return rc_layer;
}
void TestRateControlInfo(const VideoEncodeRateControlInfo& rc_info, const char* expected_vuid = nullptr,
const std::vector<const char*>& allowed_vuids = {}) {
vkt::CommandBuffer& cb = context_.CmdBuffer();
cb.Begin();
if (expected_vuid != nullptr) {
for (auto allowed_vuid : allowed_vuids) {
test_->Monitor().SetAllowedFailureMsg(allowed_vuid);
}
test_->Monitor().SetDesiredError(expected_vuid);
cb.BeginVideoCoding(context_.Begin().RateControl(rc_info));
test_->Monitor().VerifyFound();
cb.BeginVideoCoding(context_.Begin());
} else {
cb.BeginVideoCoding(context_.Begin().RateControl(rc_info));
}
if (expected_vuid != nullptr) {
for (auto allowed_vuid : allowed_vuids) {
test_->Monitor().SetAllowedFailureMsg(allowed_vuid);
}
test_->Monitor().SetDesiredError(expected_vuid);
cb.ControlVideoCoding(context_.Control().RateControl(rc_info));
test_->Monitor().VerifyFound();
} else {
cb.ControlVideoCoding(context_.Control().RateControl(rc_info));
}
cb.EndVideoCoding(context_.End());
cb.End();
};
void TestRateControlLayerInfo(const VideoEncodeRateControlLayerInfo& rc_layer_info, const char* expected_vuid = nullptr,
const std::vector<const char*>& allowed_vuids = {}) {
auto rc_info = VideoEncodeRateControlInfo(context_.Config()).SetAnyMode();
rc_info.AddLayer(rc_layer_info);
TestRateControlInfo(rc_info, expected_vuid, allowed_vuids);
}
void TestRateControlStateMismatch(const VideoEncodeRateControlInfo& rc_info) {
vkt::CommandBuffer& cb = context_.CmdBuffer();
cb.Begin();
cb.BeginVideoCoding(context_.Begin().RateControl(rc_info));
cb.EndVideoCoding(context_.End());
cb.End();
test_->Monitor().SetDesiredError("VUID-vkCmdBeginVideoCodingKHR-pBeginInfo-08254");
context_.Queue().Submit(cb);
test_->Monitor().VerifyFound();
test_->DeviceObj()->Wait();
}
private:
VkLayerTest* test_;
VideoContext& context_;
};
class VkVideoLayerTest : public VkLayerTest {
protected:
void SetTargetApiVersion(APIVersion target_api_version) {
assert(!initialized_);
VkLayerTest::SetTargetApiVersion(target_api_version);
custom_target_api_version_ = true;
}
void AddRequiredFeature(vkt::Feature feature) {
assert(!initialized_);
required_features_.insert(feature);
}
void AddOptionalFeature(vkt::Feature feature) {
assert(!initialized_);
optional_features_.insert(feature);
}
void ForceDisableFeature(vkt::Feature feature) {
assert(!initialized_);
required_features_.erase(feature);
optional_features_.erase(feature);
}
void Init() {
// Video requires at least Vulkan 1.1
if (!custom_target_api_version_) {
SetTargetApiVersion(VK_API_VERSION_1_1);
}
AddRequiredExtensions(VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME);
AddRequiredExtensions(VK_KHR_VIDEO_QUEUE_EXTENSION_NAME);
AddOptionalExtensions(VK_KHR_VIDEO_DECODE_QUEUE_EXTENSION_NAME);
AddOptionalExtensions(VK_KHR_VIDEO_ENCODE_QUEUE_EXTENSION_NAME);
// NOTE: this appears to be required for the format that is chosen in
// VkVideoLayerTest.BeginCodingIncompatRefPicProfile
AddOptionalExtensions(VK_EXT_YCBCR_2PLANE_444_FORMATS_EXTENSION_NAME);
AddOptionalExtensions(VK_KHR_VIDEO_DECODE_H264_EXTENSION_NAME);
AddOptionalExtensions(VK_KHR_VIDEO_DECODE_H265_EXTENSION_NAME);
AddOptionalExtensions(VK_KHR_VIDEO_DECODE_AV1_EXTENSION_NAME);
AddOptionalExtensions(VK_KHR_VIDEO_DECODE_VP9_EXTENSION_NAME);
AddOptionalExtensions(VK_KHR_VIDEO_ENCODE_H264_EXTENSION_NAME);
AddOptionalExtensions(VK_KHR_VIDEO_ENCODE_H265_EXTENSION_NAME);
AddOptionalExtensions(VK_KHR_VIDEO_ENCODE_AV1_EXTENSION_NAME);
for (auto feature : required_features_) {
VkLayerTest::AddRequiredFeature(feature);
}
for (auto feature : optional_features_) {
VkLayerTest::AddOptionalFeature(feature);
}
RETURN_IF_SKIP(VkLayerTest::InitFramework(instance_pnext_));
VkPhysicalDeviceProtectedMemoryProperties prot_mem_props = vku::InitStructHelper();
VkPhysicalDeviceProperties2 props = vku::InitStructHelper(&prot_mem_props);
vk::GetPhysicalDeviceProperties2(Gpu(), &props);
protected_no_fault_supported_ = (prot_mem_props.protectedNoFault == VK_TRUE);
// TODO: these tests use features and capabilities structures for extensions that
// aren't enabled on all platforms.
m_errorMonitor->SetAllowedFailureMsg("VUID-VkDeviceCreateInfo-pNext-pNext");
m_errorMonitor->SetAllowedFailureMsg("VUID-VkVideoCapabilitiesKHR-pNext-pNext");
RETURN_IF_SKIP(VkLayerTest::InitState());
uint32_t qf_count;
vk::GetPhysicalDeviceQueueFamilyProperties2(Gpu(), &qf_count, nullptr);
queue_family_props_.resize(qf_count, vku::InitStruct<VkQueueFamilyProperties2>());
queue_family_video_props_.resize(qf_count, vku::InitStruct<VkQueueFamilyVideoPropertiesKHR>());
queue_family_query_result_status_props_.resize(qf_count, vku::InitStruct<VkQueueFamilyQueryResultStatusPropertiesKHR>());
for (uint32_t i = 0; i < qf_count; ++i) {
queue_family_props_[i].pNext = &queue_family_video_props_[i];
queue_family_video_props_[i].pNext = &queue_family_query_result_status_props_[i];
}
vk::GetPhysicalDeviceQueueFamilyProperties2(Gpu(), &qf_count, queue_family_props_.data());
InitConfigs();
initialized_ = true;
}
const std::vector<VideoConfig>& GetConfigs() const { return configs_; }
const VideoConfig& GetConfig() const { return GetConfig(configs_); }
const VideoConfig& GetConfigInvalid() const { return config_invalid_; }
const std::vector<VideoConfig>& GetConfigsDecode() const { return configs_decode_; }
const VideoConfig& GetConfigDecode() const { return GetConfig(configs_decode_); }
const VideoConfig& GetConfigDecodeInvalid() const { return config_decode_invalid_; }
const std::vector<VideoConfig>& GetConfigsDecodeH264() const { return configs_decode_h264_; }
const VideoConfig& GetConfigDecodeH264() const { return GetConfig(configs_decode_h264_); }
const std::vector<VideoConfig>& GetConfigsDecodeH264Interlaced() const { return configs_decode_h264_interlaced_; }
const VideoConfig& GetConfigDecodeH264Interlaced() const { return GetConfig(configs_decode_h264_interlaced_); }
const std::vector<VideoConfig>& GetConfigsDecodeH265() const { return configs_decode_h265_; }
const VideoConfig& GetConfigDecodeH265() const { return GetConfig(configs_decode_h265_); }
const std::vector<VideoConfig>& GetConfigsDecodeAV1() const { return configs_decode_av1_; }
const VideoConfig& GetConfigDecodeAV1() const { return GetConfig(configs_decode_av1_); }
const std::vector<VideoConfig>& GetConfigsDecodeAV1FilmGrain() const { return configs_decode_av1_film_grain_; }
const VideoConfig& GetConfigDecodeAV1FilmGrain() const { return GetConfig(configs_decode_av1_film_grain_); }
const std::vector<VideoConfig>& GetConfigsDecodeVP9() const { return configs_decode_vp9_; }
const VideoConfig& GetConfigDecodeVP9() const { return GetConfig(configs_decode_vp9_); }
const std::vector<VideoConfig>& GetConfigsEncode() const { return configs_encode_; }
const VideoConfig& GetConfigEncode() const { return GetConfig(configs_encode_); }
const VideoConfig& GetConfigEncodeInvalid() const { return config_encode_invalid_; }
const std::vector<VideoConfig>& GetConfigsEncodeH264() const { return configs_encode_h264_; }
const VideoConfig& GetConfigEncodeH264() const { return GetConfig(configs_encode_h264_); }
const std::vector<VideoConfig>& GetConfigsEncodeH265() const { return configs_encode_h265_; }
const VideoConfig& GetConfigEncodeH265() const { return GetConfig(configs_encode_h265_); }
const std::vector<VideoConfig>& GetConfigsEncodeAV1() const { return configs_encode_av1_; }
const VideoConfig& GetConfigEncodeAV1() const { return GetConfig(configs_encode_av1_); }
const VideoConfig& GetConfig(const std::vector<VideoConfig>& configs) const {
return configs.empty() ? default_config_ : configs[0];
}
const std::vector<VideoConfig> FilterConfigs(const std::vector<VideoConfig>& configs,
std::function<bool(const VideoConfig&)> filter) const {
std::vector<VideoConfig> filtered_configs;
for (const auto& config : configs) {
if (filter(config)) {
filtered_configs.push_back(config);
}
}
return filtered_configs;
}
const std::vector<VideoConfig> GetConfigsWithDpbSlots(const std::vector<VideoConfig>& configs, uint32_t count = 1) const {
return FilterConfigs(configs, [count](const VideoConfig& config) { return config.Caps()->maxDpbSlots >= count; });
}
const std::vector<VideoConfig> GetConfigsWithReferences(const std::vector<VideoConfig>& configs, uint32_t count = 1) const {
return FilterConfigs(configs,
[count](const VideoConfig& config) { return config.Caps()->maxActiveReferencePictures >= count; });
}
const std::vector<VideoConfig> GetConfigsWithRateControl(
const std::vector<VideoConfig>& configs,
VkVideoEncodeRateControlModeFlagsKHR oneOfModes = VK_VIDEO_ENCODE_RATE_CONTROL_MODE_CBR_BIT_KHR |
VK_VIDEO_ENCODE_RATE_CONTROL_MODE_VBR_BIT_KHR) const {
return FilterConfigs(
configs, [oneOfModes](const VideoConfig& config) { return (config.EncodeCaps()->rateControlModes & oneOfModes) != 0; });
}
const std::vector<VideoConfig> GetConfigsWithMultiLayerRateControl(const std::vector<VideoConfig>& configs) const {
return FilterConfigs(GetConfigsWithRateControl(configs),
[](const VideoConfig& config) { return config.EncodeCaps()->maxRateControlLayers > 1; });
}
const VideoConfig& GetConfigWithParams(const std::vector<VideoConfig>& configs) const {
for (const auto& config : configs) {
if (config.NeedsSessionParams()) {
return config;
}
}
return default_config_;
}
const VideoConfig& GetConfigWithMultiEncodeQualityLevelParams(const std::vector<VideoConfig>& configs) const {
for (const auto& config : configs) {
assert(config.IsEncode());
if (config.NeedsSessionParams() && config.EncodeCaps()->maxQualityLevels > 1) {
return config;
}
}
return default_config_;
}
const VideoConfig& GetConfigWithoutProtectedContent(const std::vector<VideoConfig>& configs) const {
for (const auto& config : configs) {
if ((config.Caps()->flags & VK_VIDEO_CAPABILITY_PROTECTED_CONTENT_BIT_KHR) == 0) {
return config;
}
}
return default_config_;
}
const VideoConfig& GetConfigWithProtectedContent(const std::vector<VideoConfig>& configs) const {
for (const auto& config : configs) {
if (config.Caps()->flags & VK_VIDEO_CAPABILITY_PROTECTED_CONTENT_BIT_KHR) {
return config;
}
}
return default_config_;
}
const VideoConfig& GetConfigWithQuantDeltaMap(const std::vector<VideoConfig>& configs) const {
for (const auto& config : configs) {
assert(config.IsEncode());
if (config.EncodeCaps()->flags & VK_VIDEO_ENCODE_CAPABILITY_QUANTIZATION_DELTA_MAP_BIT_KHR &&
config.SupportedQuantDeltaMapProps().size() > 0) {
return config;
}
}
return default_config_;
}
const VideoConfig& GetConfigWithEmphasisMap(const std::vector<VideoConfig>& configs) const {
for (const auto& config : configs) {
assert(config.IsEncode());
if (config.EncodeCaps()->flags & VK_VIDEO_ENCODE_CAPABILITY_EMPHASIS_MAP_BIT_KHR &&
config.SupportedEmphasisMapProps().size() > 0) {
// Emphasis map assumes support for some non-default and non-disabled rate control mode
assert(config.EncodeCaps()->rateControlModes > VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DISABLED_BIT_KHR);
return config;
}
}
return default_config_;
}
const std::vector<VideoConfig> GetConfigsWithIntraRefresh(const std::vector<VideoConfig>& configs, uint32_t dpb_slot_count = 2,
uint32_t active_reference_count = 1) {
return FilterConfigs(GetConfigsWithReferences(GetConfigsWithDpbSlots(configs, dpb_slot_count), active_reference_count),
[](const VideoConfig& config) { return config.EncodeIntraRefreshCaps()->intraRefreshModes != 0; });
}
const std::vector<VideoConfig> GetConfigsWithPerPartitionIntraRefresh(const std::vector<VideoConfig>& configs,
uint32_t dpb_slot_count = 2,
uint32_t active_reference_count = 1) {
return FilterConfigs(GetConfigsWithReferences(GetConfigsWithDpbSlots(configs, dpb_slot_count), active_reference_count),
[](const VideoConfig& config) {
return (config.EncodeIntraRefreshCaps()->intraRefreshModes &
VK_VIDEO_ENCODE_INTRA_REFRESH_MODE_PER_PICTURE_PARTITION_BIT_KHR) != 0;
});
}
const std::vector<VideoConfig> GetConfigsWithBlockBasedIntraRefresh(
const std::vector<VideoConfig>& configs,
VkVideoEncodeIntraRefreshModeFlagBitsKHR mode = VK_VIDEO_ENCODE_INTRA_REFRESH_MODE_BLOCK_BASED_BIT_KHR,
uint32_t dpb_slot_count = 2, uint32_t active_reference_count = 1) {
return FilterConfigs(
GetConfigsWithReferences(GetConfigsWithDpbSlots(configs, dpb_slot_count), active_reference_count),
[mode](const VideoConfig& config) { return (config.EncodeIntraRefreshCaps()->intraRefreshModes & mode) != 0; });
}
uint32_t QueueFamilyCount() const { return (uint32_t)queue_family_video_props_.size(); }
VkQueueFlags QueueFamilyFlags(uint32_t qfi) const { return queue_family_props_[qfi].queueFamilyProperties.queueFlags; }
VkVideoCodecOperationFlagsKHR QueueFamilyVideoCodecOps(uint32_t qfi) const {
return queue_family_video_props_[qfi].videoCodecOperations;
}
bool QueueFamilySupportsResultStatusOnlyQueries(uint32_t qfi) const {
return queue_family_query_result_status_props_[qfi].queryResultStatusSupport;
}
bool HasQueueFamilySupportsResultStatusOnlyQueries() const {
for (size_t qfi = 0; qfi < queue_family_video_props_.size(); qfi++) {
if (queue_family_query_result_status_props_[qfi].queryResultStatusSupport) {
return true;
}
}
return false;
}
bool IsProtectedNoFaultSupported() const { return protected_no_fault_supported_; }
void SetInstancePNext(void* pNext) { instance_pnext_ = pNext; }
private:
// InitFramework and InitState is explicitly hidden because there is a custom Init that should be used instead
void InitFramework(void*) { assert(false); }
void InitState(VkPhysicalDeviceFeatures*, void*, const VkCommandPoolCreateFlags) { assert(false); }
uint32_t FindQueueFamilySupportingCodecOp(VkVideoCodecOperationFlagBitsKHR codec_op) {
uint32_t qfi = VK_QUEUE_FAMILY_IGNORED;
for (size_t i = 0; i < queue_family_video_props_.size(); ++i) {
if (queue_family_video_props_[i].videoCodecOperations & codec_op) {
qfi = i;
break;
}
}
return qfi;
}
bool GetCodecCapabilities(VideoConfig& config) {
if (vk::GetPhysicalDeviceVideoCapabilitiesKHR(Gpu(), config.Profile(), config.Caps()) != VK_SUCCESS) {
return false;
}
config.SessionCreateInfo()->maxCodedExtent = config.Caps()->minCodedExtent;
return true;
}
bool GetCodecFormats(VideoConfig& config) {
VkVideoProfileListInfoKHR video_profiles = vku::InitStructHelper();
video_profiles.profileCount = 1;
video_profiles.pProfiles = config.Profile();
VkPhysicalDeviceVideoFormatInfoKHR info = vku::InitStructHelper();
info.pNext = &video_profiles;
uint32_t count = 0;
VkImageUsageFlags allowed_usages = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
if (config.IsDecode()) {
allowed_usages |= VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR | VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR;
auto decode_caps = config.DecodeCaps();
if (decode_caps == nullptr) {
return false;
}
if (decode_caps->flags & VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR) {
info.imageUsage = VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR;
VkResult result = vk::GetPhysicalDeviceVideoFormatPropertiesKHR(Gpu(), &info, &count, nullptr);
if (result != VK_SUCCESS || count == 0) {
return false;
}
std::vector<VkVideoFormatPropertiesKHR> pic_props(count, vku::InitStruct<VkVideoFormatPropertiesKHR>());
result = vk::GetPhysicalDeviceVideoFormatPropertiesKHR(Gpu(), &info, &count, pic_props.data());
if (result != VK_SUCCESS) {
return false;
}
for (uint32_t i = 0; i < count; ++i) {
pic_props[i].imageUsageFlags &= allowed_usages;
}
info.imageUsage = VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR;
result = vk::GetPhysicalDeviceVideoFormatPropertiesKHR(Gpu(), &info, &count, nullptr);
if (result != VK_SUCCESS || count == 0) {
return false;
}
std::vector<VkVideoFormatPropertiesKHR> dpb_props(count, vku::InitStruct<VkVideoFormatPropertiesKHR>());
result = vk::GetPhysicalDeviceVideoFormatPropertiesKHR(Gpu(), &info, &count, dpb_props.data());
if (result != VK_SUCCESS) {
return false;
}
for (uint32_t i = 0; i < count; ++i) {
dpb_props[i].imageUsageFlags &= allowed_usages;
}
config.SetFormatProps(pic_props, dpb_props);
return true;
} else if (decode_caps->flags & VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR) {
info.imageUsage = VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR | VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR;
VkResult result = vk::GetPhysicalDeviceVideoFormatPropertiesKHR(Gpu(), &info, &count, nullptr);
if (result != VK_SUCCESS || count == 0) {
return false;
}
std::vector<VkVideoFormatPropertiesKHR> dpb_props(count, vku::InitStruct<VkVideoFormatPropertiesKHR>());
result = vk::GetPhysicalDeviceVideoFormatPropertiesKHR(Gpu(), &info, &count, dpb_props.data());
if (result != VK_SUCCESS) {
return false;
}
for (uint32_t i = 0; i < count; ++i) {
dpb_props[i].imageUsageFlags &= allowed_usages;
}
config.SetFormatProps(dpb_props, dpb_props);
return true;
} else {
return false;
}
} else if (config.IsEncode()) {
allowed_usages |= VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR | VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR;
info.imageUsage = VK_IMAGE_USAGE_VIDEO_ENCODE_SRC_BIT_KHR;
VkResult result = vk::GetPhysicalDeviceVideoFormatPropertiesKHR(Gpu(), &info, &count, nullptr);
if (result != VK_SUCCESS || count == 0) {
return false;
}
std::vector<VkVideoFormatPropertiesKHR> pic_props(count, vku::InitStruct<VkVideoFormatPropertiesKHR>());
result = vk::GetPhysicalDeviceVideoFormatPropertiesKHR(Gpu(), &info, &count, pic_props.data());
if (result != VK_SUCCESS) {
return false;
}
for (uint32_t i = 0; i < count; ++i) {
pic_props[i].imageUsageFlags &= allowed_usages;
}
info.imageUsage = VK_IMAGE_USAGE_VIDEO_ENCODE_DPB_BIT_KHR;
result = vk::GetPhysicalDeviceVideoFormatPropertiesKHR(Gpu(), &info, &count, nullptr);
if (result != VK_SUCCESS || count == 0) {
return false;
}
std::vector<VkVideoFormatPropertiesKHR> dpb_props(count, vku::InitStruct<VkVideoFormatPropertiesKHR>());
result = vk::GetPhysicalDeviceVideoFormatPropertiesKHR(Gpu(), &info, &count, dpb_props.data());
if (result != VK_SUCCESS) {
return false;
}
for (uint32_t i = 0; i < count; ++i) {
dpb_props[i].imageUsageFlags &= allowed_usages;
}
config.SetFormatProps(pic_props, dpb_props);
struct QuantMapTypeInfo {
VkVideoEncodeCapabilityFlagBitsKHR encode_cap;
VkImageUsageFlagBits image_usage;
std::vector<VkVideoFormatPropertiesKHR> format_props{};
std::vector<VkVideoFormatQuantizationMapPropertiesKHR> quant_map_props{};
std::vector<VkVideoFormatH265QuantizationMapPropertiesKHR> h265_quant_map_props{};
std::vector<VkVideoFormatAV1QuantizationMapPropertiesKHR> av1_quant_map_props{};
};
std::vector<QuantMapTypeInfo> quant_map_types = {
QuantMapTypeInfo{VK_VIDEO_ENCODE_CAPABILITY_QUANTIZATION_DELTA_MAP_BIT_KHR,
VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR},
QuantMapTypeInfo{VK_VIDEO_ENCODE_CAPABILITY_EMPHASIS_MAP_BIT_KHR, VK_IMAGE_USAGE_VIDEO_ENCODE_EMPHASIS_MAP_BIT_KHR},
};
for (auto& quant_map_type : quant_map_types) {
// Get quantization map related formats for this profile
if (config.EncodeCaps()->flags & quant_map_type.encode_cap) {
allowed_usages = quant_map_type.image_usage | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
info.imageUsage = quant_map_type.image_usage;
result = vk::GetPhysicalDeviceVideoFormatPropertiesKHR(Gpu(), &info, &count, nullptr);
if (result != VK_SUCCESS || count == 0) {
return false;
}
quant_map_type.format_props.resize(count, vku::InitStruct<VkVideoFormatPropertiesKHR>());
quant_map_type.quant_map_props.resize(count, vku::InitStruct<VkVideoFormatQuantizationMapPropertiesKHR>());
for (uint32_t i = 0; i < count; ++i) {
quant_map_type.format_props[i].pNext = &quant_map_type.quant_map_props[i];
}
switch (config.Profile()->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
quant_map_type.h265_quant_map_props.resize(
count, vku::InitStruct<VkVideoFormatH265QuantizationMapPropertiesKHR>());
for (uint32_t i = 0; i < count; ++i) {
quant_map_type.quant_map_props[i].pNext = &quant_map_type.h265_quant_map_props[i];
}
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR: {
quant_map_type.av1_quant_map_props.resize(
count, vku::InitStruct<VkVideoFormatAV1QuantizationMapPropertiesKHR>());
for (uint32_t i = 0; i < count; ++i) {
quant_map_type.quant_map_props[i].pNext = &quant_map_type.av1_quant_map_props[i];
}
break;
}
default:
assert(false);
return false;
}
result =
vk::GetPhysicalDeviceVideoFormatPropertiesKHR(Gpu(), &info, &count, quant_map_type.format_props.data());
// This should never happen since the initial call to get count was fine.
if (result != VK_SUCCESS) {
return false;
}
for (uint32_t i = 0; i < count; ++i) {
quant_map_type.format_props[i].imageUsageFlags &= allowed_usages;
}
}
}
config.SetQuantizationMapFormatProps(quant_map_types[0].format_props, quant_map_types[1].format_props);
return true;
} else {
return false;
}
}
void CollectCodecProfileCapsFormats(VideoConfig& config, std::vector<VideoConfig>& config_list) {
VkVideoChromaSubsamplingFlagsKHR subsamplings[] = {
VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR, VK_VIDEO_CHROMA_SUBSAMPLING_422_BIT_KHR,
VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR, VK_VIDEO_CHROMA_SUBSAMPLING_MONOCHROME_BIT_KHR};
VkVideoComponentBitDepthFlagsKHR bit_depths[] = {VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR,
VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR,
VK_VIDEO_COMPONENT_BIT_DEPTH_12_BIT_KHR};
for (size_t bd_idx = 0; bd_idx < sizeof(bit_depths) / sizeof(bit_depths[0]); ++bd_idx) {
for (size_t ss_idx = 0; ss_idx < sizeof(subsamplings) / sizeof(subsamplings[0]); ++ss_idx) {
config.Profile()->chromaSubsampling = subsamplings[ss_idx];
config.Profile()->lumaBitDepth = bit_depths[bd_idx];
config.Profile()->chromaBitDepth = bit_depths[bd_idx];
if (GetCodecCapabilities(config) && GetCodecFormats(config)) {
config_list.push_back(config);
}
}
}
}
void ChainAdditionalEncodeCaps(vku::safe_VkVideoEncodeCapabilitiesKHR* encode_caps) {
auto encode_quantization_map_caps = new vku::safe_VkVideoEncodeQuantizationMapCapabilitiesKHR();
encode_quantization_map_caps->pNext = encode_caps->pNext;
encode_caps->pNext = encode_quantization_map_caps;
auto encode_intra_refresh_caps = new vku::safe_VkVideoEncodeIntraRefreshCapabilitiesKHR();
encode_intra_refresh_caps->pNext = encode_caps->pNext;
encode_caps->pNext = encode_intra_refresh_caps;
}
void InitDecodeH264Configs(uint32_t queueFamilyIndex) {
const VkVideoDecodeH264PictureLayoutFlagBitsKHR picture_layouts[] = {
VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_PROGRESSIVE_KHR,
VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_INTERLACED_INTERLEAVED_LINES_BIT_KHR};
for (size_t i = 0; i < sizeof(picture_layouts) / sizeof(picture_layouts[0]); ++i) {
VideoConfig config;
auto& configs = (picture_layouts[i] == VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_PROGRESSIVE_KHR)
? configs_decode_h264_
: configs_decode_h264_interlaced_;
config.SetDecode();
config.SetQueueFamilyIndex(queueFamilyIndex);
config.Profile()->videoCodecOperation = VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR;
auto codec_profile = new vku::safe_VkVideoDecodeH264ProfileInfoKHR();
codec_profile->pictureLayout = picture_layouts[i];
config.SetCodecProfile(codec_profile);
auto decode_caps_h264 = new vku::safe_VkVideoDecodeH264CapabilitiesKHR();
auto decode_caps = new vku::safe_VkVideoDecodeCapabilitiesKHR();
decode_caps->pNext = decode_caps_h264;
config.SetCodecCapsChain(decode_caps);
StdVideoH264ProfileIdc profile_idc_list[] = {
STD_VIDEO_H264_PROFILE_IDC_BASELINE,
STD_VIDEO_H264_PROFILE_IDC_MAIN,
STD_VIDEO_H264_PROFILE_IDC_HIGH,
STD_VIDEO_H264_PROFILE_IDC_HIGH_444_PREDICTIVE,
};
for (size_t j = 0; j < sizeof(profile_idc_list) / sizeof(profile_idc_list[0]); ++j) {
codec_profile->stdProfileIdc = profile_idc_list[j];
CollectCodecProfileCapsFormats(config, configs);
}
for (auto& added_config : configs) {
auto sps = new StdVideoH264SequenceParameterSet[1]();
*sps = added_config.CreateH264SPS(0);
auto pps = new StdVideoH264PictureParameterSet[1]();
*pps = added_config.CreateH264PPS(0, 0);
auto add_info = new vku::safe_VkVideoDecodeH264SessionParametersAddInfoKHR();
add_info->stdSPSCount = 1;
add_info->pStdSPSs = sps;
add_info->stdPPSCount = 1;
add_info->pStdPPSs = pps;
auto params_info = new vku::safe_VkVideoDecodeH264SessionParametersCreateInfoKHR();
params_info->maxStdSPSCount = 1;
params_info->maxStdPPSCount = 1;
params_info->pParametersAddInfo = add_info;
added_config.SetCodecSessionParamsInfo(params_info);
}
configs_decode_.insert(configs_decode_.end(), configs.begin(), configs.end());
configs_.insert(configs_.end(), configs.begin(), configs.end());
}
}
void InitDecodeH265Configs(uint32_t queueFamilyIndex) {
VideoConfig config;
config.SetDecode();
config.SetQueueFamilyIndex(queueFamilyIndex);
config.Profile()->videoCodecOperation = VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR;
auto codec_profile = new vku::safe_VkVideoDecodeH265ProfileInfoKHR();
codec_profile->stdProfileIdc = STD_VIDEO_H265_PROFILE_IDC_MAIN;
config.SetCodecProfile(codec_profile);
auto decode_caps_h265 = new vku::safe_VkVideoDecodeH265CapabilitiesKHR();
auto decode_caps = new vku::safe_VkVideoDecodeCapabilitiesKHR();
decode_caps->pNext = decode_caps_h265;
config.SetCodecCapsChain(decode_caps);
StdVideoH265ProfileIdc profile_idc_list[] = {
STD_VIDEO_H265_PROFILE_IDC_MAIN,
STD_VIDEO_H265_PROFILE_IDC_MAIN_10,
STD_VIDEO_H265_PROFILE_IDC_MAIN_STILL_PICTURE,
STD_VIDEO_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSIONS,
STD_VIDEO_H265_PROFILE_IDC_SCC_EXTENSIONS,
};
for (size_t i = 0; i < sizeof(profile_idc_list) / sizeof(profile_idc_list[0]); ++i) {
codec_profile->stdProfileIdc = profile_idc_list[i];
CollectCodecProfileCapsFormats(config, configs_decode_h265_);
}
for (auto& added_config : configs_decode_h265_) {
auto vps = new StdVideoH265VideoParameterSet[1]();
*vps = added_config.CreateH265VPS(0);
auto sps = new StdVideoH265SequenceParameterSet[1]();
*sps = added_config.CreateH265SPS(0, 0);
auto pps = new StdVideoH265PictureParameterSet[1]();
*pps = added_config.CreateH265PPS(0, 0, 0);
auto add_info = new vku::safe_VkVideoDecodeH265SessionParametersAddInfoKHR();
add_info->stdVPSCount = 1;
add_info->pStdVPSs = vps;
add_info->stdSPSCount = 1;
add_info->pStdSPSs = sps;
add_info->stdPPSCount = 1;
add_info->pStdPPSs = pps;
auto params_info = new vku::safe_VkVideoDecodeH265SessionParametersCreateInfoKHR();
params_info->maxStdVPSCount = 1;
params_info->maxStdSPSCount = 1;
params_info->maxStdPPSCount = 1;
params_info->pParametersAddInfo = add_info;
added_config.SetCodecSessionParamsInfo(params_info);
}
configs_decode_.insert(configs_decode_.end(), configs_decode_h265_.begin(), configs_decode_h265_.end());
configs_.insert(configs_.end(), configs_decode_h265_.begin(), configs_decode_h265_.end());
}
void InitDecodeAV1Configs(uint32_t queueFamilyIndex) {
for (size_t i = 0; i < 2; ++i) {
VideoConfig config;
auto& configs = (i == 0) ? configs_decode_av1_ : configs_decode_av1_film_grain_;
config.SetDecode();
config.SetQueueFamilyIndex(queueFamilyIndex);
config.Profile()->videoCodecOperation = VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR;
auto codec_profile = new vku::safe_VkVideoDecodeAV1ProfileInfoKHR();
codec_profile->filmGrainSupport = (i == 0) ? VK_FALSE : VK_TRUE;
config.SetCodecProfile(codec_profile);
auto decode_caps_av1 = new vku::safe_VkVideoDecodeAV1CapabilitiesKHR();
auto decode_caps = new vku::safe_VkVideoDecodeCapabilitiesKHR();
decode_caps->pNext = decode_caps_av1;
config.SetCodecCapsChain(decode_caps);
StdVideoAV1Profile profile_list[] = {
STD_VIDEO_AV1_PROFILE_MAIN,
STD_VIDEO_AV1_PROFILE_HIGH,
STD_VIDEO_AV1_PROFILE_PROFESSIONAL,
};
for (size_t j = 0; j < sizeof(profile_list) / sizeof(profile_list[0]); ++j) {
codec_profile->stdProfile = profile_list[j];
CollectCodecProfileCapsFormats(config, configs);
}
for (auto& added_config : configs) {
auto seq_header = new StdVideoAV1SequenceHeader();
auto params_info = new vku::safe_VkVideoDecodeAV1SessionParametersCreateInfoKHR();
params_info->pStdSequenceHeader = seq_header;
added_config.SetCodecSessionParamsInfo(params_info);
}
configs_decode_.insert(configs_decode_.end(), configs.begin(), configs.end());
configs_.insert(configs_.end(), configs.begin(), configs.end());
}
}
void InitDecodeVP9Configs(uint32_t queueFamilyIndex) {
VideoConfig config;
config.SetDecode();
config.SetQueueFamilyIndex(queueFamilyIndex);
config.Profile()->videoCodecOperation = VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR;
auto codec_profile = new vku::safe_VkVideoDecodeVP9ProfileInfoKHR();
config.SetCodecProfile(codec_profile);
auto decode_caps_vp9 = new vku::safe_VkVideoDecodeVP9CapabilitiesKHR();
auto decode_caps = new vku::safe_VkVideoDecodeCapabilitiesKHR();
decode_caps->pNext = decode_caps_vp9;
config.SetCodecCapsChain(decode_caps);
StdVideoVP9Profile profile_list[] = {STD_VIDEO_VP9_PROFILE_0, STD_VIDEO_VP9_PROFILE_1, STD_VIDEO_VP9_PROFILE_2,
STD_VIDEO_VP9_PROFILE_3};
for (size_t j = 0; j < sizeof(profile_list) / sizeof(profile_list[0]); ++j) {
codec_profile->stdProfile = profile_list[j];
CollectCodecProfileCapsFormats(config, configs_decode_vp9_);
}
configs_decode_.insert(configs_decode_.end(), configs_decode_vp9_.begin(), configs_decode_vp9_.end());
configs_.insert(configs_.end(), configs_decode_vp9_.begin(), configs_decode_vp9_.end());
}
void InitEncodeH264Configs(uint32_t queueFamilyIndex) {
VideoConfig config;
config.SetEncode();
config.SetQueueFamilyIndex(queueFamilyIndex);
config.Profile()->videoCodecOperation = VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR;
auto codec_profile = new vku::safe_VkVideoEncodeH264ProfileInfoKHR();
codec_profile->stdProfileIdc = STD_VIDEO_H264_PROFILE_IDC_BASELINE;
config.SetCodecProfile(codec_profile);
auto encode_caps_h264 = new vku::safe_VkVideoEncodeH264CapabilitiesKHR();
auto encode_caps = new vku::safe_VkVideoEncodeCapabilitiesKHR();
encode_caps->pNext = encode_caps_h264;
ChainAdditionalEncodeCaps(encode_caps);
config.SetCodecCapsChain(encode_caps);
config.SetCodecEncodeQualityLevelPropsChain(new vku::safe_VkVideoEncodeH264QualityLevelPropertiesKHR());
StdVideoH264ProfileIdc profile_idc_list[] = {
STD_VIDEO_H264_PROFILE_IDC_BASELINE,
STD_VIDEO_H264_PROFILE_IDC_MAIN,
STD_VIDEO_H264_PROFILE_IDC_HIGH,
STD_VIDEO_H264_PROFILE_IDC_HIGH_444_PREDICTIVE,
};
for (size_t i = 0; i < sizeof(profile_idc_list) / sizeof(profile_idc_list[0]); ++i) {
codec_profile->stdProfileIdc = profile_idc_list[i];
CollectCodecProfileCapsFormats(config, configs_encode_h264_);
}
for (auto& added_config : configs_encode_h264_) {
auto sps = new StdVideoH264SequenceParameterSet[1]();
*sps = added_config.CreateH264SPS(0);
auto pps = new StdVideoH264PictureParameterSet[1]();
*pps = added_config.CreateH264PPS(0, 0);
auto add_info = new vku::safe_VkVideoEncodeH264SessionParametersAddInfoKHR();
add_info->stdSPSCount = 1;
add_info->pStdSPSs = sps;
add_info->stdPPSCount = 1;
add_info->pStdPPSs = pps;
auto params_info = new vku::safe_VkVideoEncodeH264SessionParametersCreateInfoKHR();
params_info->maxStdSPSCount = 1;
params_info->maxStdPPSCount = 1;
params_info->pParametersAddInfo = add_info;
added_config.SetCodecSessionParamsInfo(params_info);
}
configs_encode_.insert(configs_encode_.end(), configs_encode_h264_.begin(), configs_encode_h264_.end());
configs_.insert(configs_.end(), configs_encode_h264_.begin(), configs_encode_h264_.end());
}
void InitEncodeH265Configs(uint32_t queueFamilyIndex) {
VideoConfig config;
config.SetEncode();
config.SetQueueFamilyIndex(queueFamilyIndex);
config.Profile()->videoCodecOperation = VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR;
auto codec_profile = new vku::safe_VkVideoEncodeH265ProfileInfoKHR();
codec_profile->stdProfileIdc = STD_VIDEO_H265_PROFILE_IDC_MAIN;
config.SetCodecProfile(codec_profile);
auto encode_caps_h265 = new vku::safe_VkVideoEncodeH265CapabilitiesKHR();
auto encode_caps = new vku::safe_VkVideoEncodeCapabilitiesKHR();
encode_caps->pNext = encode_caps_h265;
ChainAdditionalEncodeCaps(encode_caps);
config.SetCodecCapsChain(encode_caps);
config.SetCodecEncodeQualityLevelPropsChain(new vku::safe_VkVideoEncodeH265QualityLevelPropertiesKHR());
StdVideoH265ProfileIdc profile_idc_list[] = {
STD_VIDEO_H265_PROFILE_IDC_MAIN,
STD_VIDEO_H265_PROFILE_IDC_MAIN_10,
STD_VIDEO_H265_PROFILE_IDC_MAIN_STILL_PICTURE,
STD_VIDEO_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSIONS,
STD_VIDEO_H265_PROFILE_IDC_SCC_EXTENSIONS,
};
for (size_t i = 0; i < sizeof(profile_idc_list) / sizeof(profile_idc_list[0]); ++i) {
codec_profile->stdProfileIdc = profile_idc_list[i];
CollectCodecProfileCapsFormats(config, configs_encode_h265_);
}
for (auto& added_config : configs_encode_h265_) {
auto vps = new StdVideoH265VideoParameterSet[1]();
*vps = added_config.CreateH265VPS(0);
auto sps = new StdVideoH265SequenceParameterSet[1]();
*sps = added_config.CreateH265SPS(0, 0);
auto pps = new StdVideoH265PictureParameterSet[1]();
*pps = added_config.CreateH265PPS(0, 0, 0);
auto add_info = new vku::safe_VkVideoEncodeH265SessionParametersAddInfoKHR();
add_info->stdVPSCount = 1;
add_info->pStdVPSs = vps;
add_info->stdSPSCount = 1;
add_info->pStdSPSs = sps;
add_info->stdPPSCount = 1;
add_info->pStdPPSs = pps;
auto params_info = new vku::safe_VkVideoEncodeH265SessionParametersCreateInfoKHR();
params_info->maxStdVPSCount = 1;
params_info->maxStdSPSCount = 1;
params_info->maxStdPPSCount = 1;
params_info->pParametersAddInfo = add_info;
added_config.SetCodecSessionParamsInfo(params_info);
}
configs_encode_.insert(configs_encode_.end(), configs_encode_h265_.begin(), configs_encode_h265_.end());
configs_.insert(configs_.end(), configs_encode_h265_.begin(), configs_encode_h265_.end());
}
void InitEncodeAV1Configs(uint32_t queueFamilyIndex) {
VideoConfig config;
config.SetEncode();
config.SetQueueFamilyIndex(queueFamilyIndex);
config.Profile()->videoCodecOperation = VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR;
auto codec_profile = new vku::safe_VkVideoEncodeAV1ProfileInfoKHR();
codec_profile->stdProfile = STD_VIDEO_AV1_PROFILE_MAIN;
config.SetCodecProfile(codec_profile);
auto encode_caps_av1 = new vku::safe_VkVideoEncodeAV1CapabilitiesKHR();
auto encode_caps = new vku::safe_VkVideoEncodeCapabilitiesKHR();
encode_caps->pNext = encode_caps_av1;
ChainAdditionalEncodeCaps(encode_caps);
config.SetCodecCapsChain(encode_caps);
config.SetCodecEncodeQualityLevelPropsChain(new vku::safe_VkVideoEncodeAV1QualityLevelPropertiesKHR());
StdVideoAV1Profile profile_list[] = {STD_VIDEO_AV1_PROFILE_MAIN, STD_VIDEO_AV1_PROFILE_HIGH,
STD_VIDEO_AV1_PROFILE_PROFESSIONAL};
for (size_t i = 0; i < sizeof(profile_list) / sizeof(profile_list[0]); ++i) {
codec_profile->stdProfile = profile_list[i];
CollectCodecProfileCapsFormats(config, configs_encode_av1_);
}
for (auto& added_config : configs_encode_av1_) {
auto seq_header = new StdVideoAV1SequenceHeader();
*seq_header = added_config.CreateAV1SequenceHeader();
auto params_info = new vku::safe_VkVideoEncodeAV1SessionParametersCreateInfoKHR();
params_info->pStdSequenceHeader = seq_header;
added_config.SetCodecSessionParamsInfo(params_info);
}
configs_encode_.insert(configs_encode_.end(), configs_encode_av1_.begin(), configs_encode_av1_.end());
configs_.insert(configs_.end(), configs_encode_av1_.begin(), configs_encode_av1_.end());
}
void InitInvalidConfigs() {
if (IsExtensionsEnabled(VK_KHR_VIDEO_DECODE_QUEUE_EXTENSION_NAME)) {
auto codec_profile = new vku::safe_VkVideoDecodeH264ProfileInfoKHR();
codec_profile->stdProfileIdc = STD_VIDEO_H264_PROFILE_IDC_INVALID;
config_decode_invalid_.SetDecode();
config_decode_invalid_.SetQueueFamilyIndex(0);
config_decode_invalid_.Profile()->videoCodecOperation = VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR;
config_decode_invalid_.Profile()->chromaSubsampling = VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR;
config_decode_invalid_.Profile()->lumaBitDepth = VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR;
config_decode_invalid_.Profile()->chromaBitDepth = VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR;
config_decode_invalid_.SetCodecProfile(codec_profile);
config_invalid_ = config_decode_invalid_;
}
if (IsExtensionsEnabled(VK_KHR_VIDEO_ENCODE_QUEUE_EXTENSION_NAME)) {
auto codec_profile = new vku::safe_VkVideoEncodeH264ProfileInfoKHR();
codec_profile->stdProfileIdc = STD_VIDEO_H264_PROFILE_IDC_INVALID;
config_encode_invalid_.SetEncode();
config_encode_invalid_.SetQueueFamilyIndex(0);
config_encode_invalid_.Profile()->videoCodecOperation = VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR;
config_encode_invalid_.Profile()->chromaSubsampling = VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR;
config_encode_invalid_.Profile()->lumaBitDepth = VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR;
config_encode_invalid_.Profile()->chromaBitDepth = VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR;
config_encode_invalid_.SetCodecProfile(codec_profile);
config_encode_invalid_.SetCodecEncodeQualityLevelPropsChain(new vku::safe_VkVideoEncodeH264QualityLevelPropertiesKHR());
config_invalid_ = config_encode_invalid_;
}
}
void InitConfigs() {
default_config_ = VideoConfig();
uint32_t qfi = VK_QUEUE_FAMILY_IGNORED;
qfi = FindQueueFamilySupportingCodecOp(VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR);
if (qfi != VK_QUEUE_FAMILY_IGNORED) {
InitDecodeH264Configs(qfi);
}
qfi = FindQueueFamilySupportingCodecOp(VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR);
if (qfi != VK_QUEUE_FAMILY_IGNORED) {
InitDecodeH265Configs(qfi);
}
qfi = FindQueueFamilySupportingCodecOp(VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR);
if (qfi != VK_QUEUE_FAMILY_IGNORED) {
InitDecodeAV1Configs(qfi);
}
qfi = FindQueueFamilySupportingCodecOp(VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR);
if (qfi != VK_QUEUE_FAMILY_IGNORED) {
InitDecodeVP9Configs(qfi);
}
qfi = FindQueueFamilySupportingCodecOp(VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR);
if (qfi != VK_QUEUE_FAMILY_IGNORED) {
InitEncodeH264Configs(qfi);
}
qfi = FindQueueFamilySupportingCodecOp(VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR);
if (qfi != VK_QUEUE_FAMILY_IGNORED) {
InitEncodeH265Configs(qfi);
}
qfi = FindQueueFamilySupportingCodecOp(VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR);
if (qfi != VK_QUEUE_FAMILY_IGNORED) {
InitEncodeAV1Configs(qfi);
}
InitInvalidConfigs();
}
void* instance_pnext_ = nullptr;
std::vector<VkQueueFamilyProperties2> queue_family_props_{};
std::vector<VkQueueFamilyVideoPropertiesKHR> queue_family_video_props_{};
std::vector<VkQueueFamilyQueryResultStatusPropertiesKHR> queue_family_query_result_status_props_{};
bool custom_target_api_version_{false};
bool initialized_{false};
bool protected_no_fault_supported_{false};
std::unordered_set<vkt::Feature> required_features_{vkt::Feature::synchronization2};
std::unordered_set<vkt::Feature> optional_features_{vkt::Feature::videoEncodeAV1, vkt::Feature::videoDecodeVP9};
VideoConfig default_config_{};
VideoConfig config_invalid_{};
VideoConfig config_decode_invalid_{};
VideoConfig config_encode_invalid_{};
std::vector<VideoConfig> configs_{};
std::vector<VideoConfig> configs_decode_{};
std::vector<VideoConfig> configs_decode_h264_{};
std::vector<VideoConfig> configs_decode_h264_interlaced_{};
std::vector<VideoConfig> configs_decode_h265_{};
std::vector<VideoConfig> configs_decode_av1_{};
std::vector<VideoConfig> configs_decode_av1_film_grain_{};
std::vector<VideoConfig> configs_decode_vp9_{};
std::vector<VideoConfig> configs_encode_{};
std::vector<VideoConfig> configs_encode_h264_{};
std::vector<VideoConfig> configs_encode_h265_{};
std::vector<VideoConfig> configs_encode_av1_{};
};
class VkVideoSyncLayerTest : public VkVideoLayerTest {
public:
VkVideoSyncLayerTest() {
SetInstancePNext(&features_);
AddRequiredExtensions(VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME);
}
private:
const VkValidationFeatureEnableEXT enables_[1] = {VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT};
const VkValidationFeatureDisableEXT disables_[4] = {
VK_VALIDATION_FEATURE_DISABLE_THREAD_SAFETY_EXT, VK_VALIDATION_FEATURE_DISABLE_API_PARAMETERS_EXT,
VK_VALIDATION_FEATURE_DISABLE_OBJECT_LIFETIMES_EXT, VK_VALIDATION_FEATURE_DISABLE_CORE_CHECKS_EXT};
VkValidationFeaturesEXT features_ = {VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT, nullptr, 1u, enables_, 4, disables_};
};
class VkVideoBestPracticesLayerTest : public VkVideoLayerTest {
public:
VkVideoBestPracticesLayerTest() {
SetInstancePNext(&features_);
AddRequiredExtensions(VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME);
}
private:
VkValidationFeatureEnableEXT enables_[1] = {VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT};
VkValidationFeatureDisableEXT disables_[2] = {VK_VALIDATION_FEATURE_DISABLE_THREAD_SAFETY_EXT,
VK_VALIDATION_FEATURE_DISABLE_CORE_CHECKS_EXT};
VkValidationFeaturesEXT features_ = {VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT, nullptr, 1, enables_, 2, disables_};
};
class NegativeVideoBestPractices : public VkVideoBestPracticesLayerTest {};