blob: b582cb6b2440f14de7e06101d84d5318f2917b23 [file] [log] [blame]
/* Copyright (c) 2022-2024 The Khronos Group Inc.
* Copyright (c) 2022-2024 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
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "state_tracker/video_session_state.h"
#include "state_tracker/image_state.h"
#include "state_tracker/state_tracker.h"
#include "generated/dispatch_functions.h"
#include <sstream>
namespace vvl {
VideoProfileDesc::VideoProfileDesc(VkPhysicalDevice physical_device, VkVideoProfileInfoKHR const *profile)
: std::enable_shared_from_this<VideoProfileDesc>(),
physical_device_(physical_device),
profile_(),
capabilities_(),
cache_(nullptr) {
if (InitProfile(profile)) {
InitCapabilities(physical_device);
InitQuantizationMapFormats(physical_device);
}
}
VideoProfileDesc::~VideoProfileDesc() {
if (cache_) {
cache_->Release(this);
}
}
bool VideoProfileDesc::InitProfile(VkVideoProfileInfoKHR const *profile) {
if (profile) {
profile_.base = *profile;
profile_.base.pNext = nullptr;
if (profile_.base.chromaSubsampling == VK_VIDEO_CHROMA_SUBSAMPLING_MONOCHROME_BIT_KHR) {
// If monochrome, then chromaBitDepth is ignored, so let's set it to INVALID
// to avoid special-casing the comparison and hash functions.
profile_.base.chromaBitDepth = VK_VIDEO_COMPONENT_BIT_DEPTH_INVALID_KHR;
}
switch (profile->videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR: {
auto decode_h264 = vku::FindStructInPNextChain<VkVideoDecodeH264ProfileInfoKHR>(profile->pNext);
if (decode_h264) {
profile_.valid = true;
profile_.decode_h264 = *decode_h264;
profile_.decode_h264.pNext = nullptr;
} else {
profile_.valid = false;
profile_.decode_h264 = vku::InitStructHelper();
}
profile_.is_decode = true;
profile_.base.pNext = &profile_.decode_h264;
break;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR: {
auto decode_h265 = vku::FindStructInPNextChain<VkVideoDecodeH265ProfileInfoKHR>(profile->pNext);
if (decode_h265) {
profile_.valid = true;
profile_.decode_h265 = *decode_h265;
profile_.decode_h265.pNext = nullptr;
} else {
profile_.valid = false;
profile_.decode_h265 = vku::InitStructHelper();
}
profile_.is_decode = true;
profile_.base.pNext = &profile_.decode_h265;
break;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR: {
auto decode_av1 = vku::FindStructInPNextChain<VkVideoDecodeAV1ProfileInfoKHR>(profile->pNext);
if (decode_av1) {
profile_.valid = true;
profile_.decode_av1 = *decode_av1;
profile_.decode_av1.pNext = nullptr;
} else {
profile_.valid = false;
profile_.decode_av1 = vku::InitStructHelper();
}
profile_.is_decode = true;
profile_.base.pNext = &profile_.decode_av1;
break;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR: {
auto decode_vp9 = vku::FindStructInPNextChain<VkVideoDecodeVP9ProfileInfoKHR>(profile->pNext);
if (decode_vp9) {
profile_.valid = true;
profile_.decode_vp9 = *decode_vp9;
profile_.decode_vp9.pNext = nullptr;
} else {
profile_.valid = false;
profile_.decode_vp9 = vku::InitStructHelper();
}
profile_.is_decode = true;
profile_.base.pNext = &profile_.decode_vp9;
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR: {
auto encode_h264 = vku::FindStructInPNextChain<VkVideoEncodeH264ProfileInfoKHR>(profile->pNext);
if (encode_h264) {
profile_.valid = true;
profile_.encode_h264 = *encode_h264;
profile_.encode_h264.pNext = nullptr;
} else {
profile_.valid = false;
profile_.encode_h264 = vku::InitStructHelper();
}
profile_.is_encode = true;
profile_.base.pNext = &profile_.encode_h264;
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR: {
auto encode_h265 = vku::FindStructInPNextChain<VkVideoEncodeH265ProfileInfoKHR>(profile->pNext);
if (encode_h265) {
profile_.valid = true;
profile_.encode_h265 = *encode_h265;
profile_.encode_h265.pNext = nullptr;
} else {
profile_.valid = false;
profile_.encode_h265 = vku::InitStructHelper();
}
profile_.is_encode = true;
profile_.base.pNext = &profile_.encode_h265;
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR: {
auto encode_av1 = vku::FindStructInPNextChain<VkVideoEncodeAV1ProfileInfoKHR>(profile->pNext);
if (encode_av1) {
profile_.valid = true;
profile_.encode_av1 = *encode_av1;
profile_.encode_av1.pNext = nullptr;
} else {
profile_.valid = false;
profile_.encode_av1 = vku::InitStructHelper();
}
profile_.is_encode = true;
profile_.base.pNext = &profile_.encode_av1;
break;
}
default:
profile_.valid = false;
break;
}
if (profile_.is_decode) {
auto usage = vku::FindStructInPNextChain<VkVideoDecodeUsageInfoKHR>(profile->pNext);
if (usage) {
profile_.decode_usage = *usage;
profile_.decode_usage.pNext = profile_.base.pNext;
profile_.base.pNext = &profile_.decode_usage;
} else {
profile_.decode_usage = vku::InitStructHelper();
}
}
if (profile_.is_encode) {
auto usage = vku::FindStructInPNextChain<VkVideoEncodeUsageInfoKHR>(profile->pNext);
if (usage) {
profile_.encode_usage = *usage;
profile_.encode_usage.pNext = profile_.base.pNext;
profile_.base.pNext = &profile_.encode_usage;
} else {
profile_.encode_usage = vku::InitStructHelper();
}
}
} else {
profile_.valid = false;
profile_.base = vku::InitStructHelper();
}
return profile_.valid;
}
void VideoProfileDesc::InitCapabilities(VkPhysicalDevice physical_device) {
capabilities_.base = vku::InitStructHelper();
switch (profile_.base.videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
capabilities_.base.pNext = &capabilities_.decode;
capabilities_.decode = vku::InitStructHelper();
capabilities_.decode.pNext = &capabilities_.decode_h264;
capabilities_.decode_h264 = vku::InitStructHelper();
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
capabilities_.base.pNext = &capabilities_.decode;
capabilities_.decode = vku::InitStructHelper();
capabilities_.decode.pNext = &capabilities_.decode_h265;
capabilities_.decode_h265 = vku::InitStructHelper();
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR:
capabilities_.base.pNext = &capabilities_.decode;
capabilities_.decode = vku::InitStructHelper();
capabilities_.decode.pNext = &capabilities_.decode_av1;
capabilities_.decode_av1 = vku::InitStructHelper();
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR:
capabilities_.base.pNext = &capabilities_.decode;
capabilities_.decode = vku::InitStructHelper();
capabilities_.decode.pNext = &capabilities_.decode_vp9;
capabilities_.decode_vp9 = vku::InitStructHelper();
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
capabilities_.base.pNext = &capabilities_.encode;
capabilities_.encode = vku::InitStructHelper();
capabilities_.encode.pNext = &capabilities_.encode_h264;
capabilities_.encode_h264 = vku::InitStructHelper();
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
capabilities_.base.pNext = &capabilities_.encode;
capabilities_.encode = vku::InitStructHelper();
capabilities_.encode.pNext = &capabilities_.encode_h265;
capabilities_.encode_h265 = vku::InitStructHelper();
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:
capabilities_.base.pNext = &capabilities_.encode;
capabilities_.encode = vku::InitStructHelper();
capabilities_.encode.pNext = &capabilities_.encode_av1;
capabilities_.encode_av1 = vku::InitStructHelper();
break;
default:
return;
}
if (IsEncode()) {
capabilities_.encode_ext.quantization_map = vku::InitStructHelper(capabilities_.base.pNext);
capabilities_.base.pNext = &capabilities_.encode_ext.quantization_map;
capabilities_.encode_ext.intra_refresh = vku::InitStructHelper(capabilities_.base.pNext);
capabilities_.base.pNext = &capabilities_.encode_ext.intra_refresh;
}
VkResult result = DispatchGetPhysicalDeviceVideoCapabilitiesKHR(physical_device, &profile_.base, &capabilities_.base);
if (result == VK_SUCCESS) {
capabilities_.supported = true;
}
}
void VideoProfileDesc::InitQuantizationMapFormats(VkPhysicalDevice physical_device) {
VkVideoProfileListInfoKHR list = vku::InitStructHelper();
list.pProfiles = &profile_.base;
list.profileCount = 1;
struct QuantMapTypeInfo {
VkImageUsageFlagBits usage;
VkVideoEncodeCapabilityFlagBitsKHR cap;
SupportedQuantizationMapTexelSizes *supported_texel_sizes;
};
const std::vector<QuantMapTypeInfo> map_types{
{VK_IMAGE_USAGE_VIDEO_ENCODE_QUANTIZATION_DELTA_MAP_BIT_KHR, VK_VIDEO_ENCODE_CAPABILITY_QUANTIZATION_DELTA_MAP_BIT_KHR,
&quant_delta_map_texel_sizes_},
{VK_IMAGE_USAGE_VIDEO_ENCODE_EMPHASIS_MAP_BIT_KHR, VK_VIDEO_ENCODE_CAPABILITY_EMPHASIS_MAP_BIT_KHR,
&emphasis_map_texel_sizes_},
};
for (const auto &map_type : map_types) {
if ((capabilities_.encode.flags & map_type.cap) == 0) continue;
VkPhysicalDeviceVideoFormatInfoKHR info = vku::InitStructHelper();
info.imageUsage = map_type.usage;
info.pNext = &list;
uint32_t count;
VkResult result = DispatchGetPhysicalDeviceVideoFormatPropertiesKHR(physical_device, &info, &count, nullptr);
if (result != VK_SUCCESS) {
continue;
}
std::vector<VkVideoFormatPropertiesKHR> formats(count, vku::InitStruct<VkVideoFormatPropertiesKHR>());
std::vector<VkVideoFormatQuantizationMapPropertiesKHR> map_props(
count, vku::InitStruct<VkVideoFormatQuantizationMapPropertiesKHR>());
for (uint32_t i = 0; i < count; i++) {
formats[i].pNext = &map_props[i];
}
result = DispatchGetPhysicalDeviceVideoFormatPropertiesKHR(physical_device, &info, &count, formats.data());
if (result != VK_SUCCESS) {
formats.resize(0);
continue;
}
for (const auto &props : map_props) {
map_type.supported_texel_sizes->insert(props.quantizationMapTexelSize);
}
}
}
std::shared_ptr<const VideoProfileDesc> VideoProfileDesc::Cache::GetOrCreate(VkPhysicalDevice physical_device,
VkVideoProfileInfoKHR const *profile) {
VideoProfileDesc desc(physical_device, profile);
if (desc.GetProfile().valid) {
auto &set = entries_[physical_device];
auto it = set.find(&desc);
if (it != set.end()) {
return (*it)->shared_from_this();
} else {
auto desc_ptr = std::make_shared<VideoProfileDesc>(desc);
desc_ptr->cache_ = this;
set.emplace(desc_ptr.get());
return desc_ptr;
}
} else {
return nullptr;
}
}
std::shared_ptr<const VideoProfileDesc> VideoProfileDesc::Cache::Get(VkPhysicalDevice physical_device,
VkVideoProfileInfoKHR const *profile) {
if (profile) {
std::unique_lock<std::mutex> lock(mutex_);
return GetOrCreate(physical_device, profile);
} else {
return nullptr;
}
}
SupportedVideoProfiles VideoProfileDesc::Cache::Get(VkPhysicalDevice physical_device,
VkVideoProfileListInfoKHR const *profile_list) {
SupportedVideoProfiles supported_profiles{};
if (profile_list) {
std::unique_lock<std::mutex> lock(mutex_);
for (uint32_t i = 0; i < profile_list->profileCount; ++i) {
auto profile_desc = GetOrCreate(physical_device, &profile_list->pProfiles[i]);
if (profile_desc) {
supported_profiles.insert(std::move(profile_desc));
}
}
}
return supported_profiles;
}
void VideoProfileDesc::Cache::Release(VideoProfileDesc const *desc) {
std::unique_lock<std::mutex> lock(mutex_);
entries_[desc->physical_device_].erase(desc);
}
VideoPictureResource::VideoPictureResource()
: image_view_state(nullptr), image_state(nullptr), base_array_layer(0), range(), coded_offset(), coded_extent() {}
VideoPictureResource::VideoPictureResource(const DeviceState &dev_data, VkVideoPictureResourceInfoKHR const &res)
: image_view_state(dev_data.Get<ImageView>(res.imageViewBinding)),
image_state(image_view_state ? image_view_state->image_state : nullptr),
base_array_layer(res.baseArrayLayer),
range(GetImageSubresourceRange(image_view_state.get(), res.baseArrayLayer)),
coded_offset(res.codedOffset),
coded_extent(res.codedExtent) {}
VkImageSubresourceRange VideoPictureResource::GetImageSubresourceRange(ImageView const *image_view_state, uint32_t layer) {
VkImageSubresourceRange range{};
if (image_view_state) {
range = image_view_state->normalized_subresource_range;
range.baseArrayLayer += layer;
range.layerCount = 1;
}
return range;
}
VkOffset3D VideoPictureResource::GetEffectiveImageOffset(const vvl::VideoSession &vs_state) const {
VkOffset3D offset{coded_offset.x, coded_offset.y, 0};
// Round to picture access granularity
const auto gran = vs_state.profile->GetCapabilities().base.pictureAccessGranularity;
offset.x = (offset.x / gran.width) * gran.width;
offset.y = (offset.y / gran.height) * gran.height;
return offset;
}
VkExtent3D VideoPictureResource::GetEffectiveImageExtent(const vvl::VideoSession &vs_state) const {
VkExtent3D extent{coded_extent.width, coded_extent.height, 1};
// H.264 decode interlacing with separate planes only accesses half of the coded height
if (vs_state.GetCodecOp() == VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR &&
vs_state.GetH264PictureLayout() == VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_INTERLACED_SEPARATE_PLANES_BIT_KHR) {
extent.height /= 2;
}
// Round to picture access granularity
const auto gran = vs_state.profile->GetCapabilities().base.pictureAccessGranularity;
extent.width = ((extent.width + gran.width - 1) / gran.width) * gran.width;
extent.height = ((extent.height + gran.height - 1) / gran.height) * gran.height;
// Clamp to mip level dimensions
extent.width = std::min(extent.width, image_state->create_info.extent.width >> range.baseMipLevel);
extent.height = std::min(extent.height, image_state->create_info.extent.height >> range.baseMipLevel);
return extent;
}
VideoPictureID::VideoPictureID(VideoProfileDesc const &profile, VkVideoReferenceSlotInfoKHR const &slot) {
switch (profile.GetCodecOp()) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR: {
auto slot_info = vku::FindStructInPNextChain<VkVideoDecodeH264DpbSlotInfoKHR>(slot.pNext);
if (slot_info && slot_info->pStdReferenceInfo) {
top_field = slot_info->pStdReferenceInfo->flags.top_field_flag;
bottom_field = slot_info->pStdReferenceInfo->flags.bottom_field_flag;
}
break;
}
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:
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:
break;
default:
break;
}
}
void VideoSessionDeviceState::Reset() {
initialized_ = true;
for (size_t i = 0; i < is_active_.size(); ++i) {
is_active_[i] = false;
all_pictures_[i].clear();
pictures_per_id_[i].clear();
}
encode_.quality_level = 0;
encode_.rate_control_state = VideoEncodeRateControlState();
}
void VideoSessionDeviceState::Activate(int32_t slot_index, const VideoPictureID &picture_id, const VideoPictureResource &res) {
assert(!picture_id.IsBothFields());
if (slot_index < 0 || static_cast<uint32_t>(slot_index) >= is_active_.size()) {
// Out-of-bounds slot index
return;
}
is_active_[slot_index] = true;
if (picture_id.IsFrame()) {
// If slot is activated with a frame then it overrides all previous pictures
all_pictures_[slot_index].clear();
pictures_per_id_[slot_index].clear();
}
auto prev_res_it = pictures_per_id_[slot_index].find(picture_id);
if (prev_res_it != pictures_per_id_[slot_index].end()) {
// If we replace an existing picture then remove it
all_pictures_[slot_index].erase(prev_res_it->second);
}
all_pictures_[slot_index].insert(res);
pictures_per_id_[slot_index][picture_id] = res;
}
void VideoSessionDeviceState::Invalidate(int32_t slot_index, const VideoPictureID &picture_id) {
assert(!picture_id.IsBothFields());
if (slot_index < 0 || static_cast<uint32_t>(slot_index) >= is_active_.size()) {
// Out-of-bounds slot index
return;
}
bool previous_is_frame = pictures_per_id_[slot_index].find(VideoPictureID::Frame()) != pictures_per_id_[slot_index].end();
if (picture_id.IsFrame() || previous_is_frame) {
// If invalidation happens due to a non-reference setup frame then it invalidates all previous pictures
// Also invalidate all if the previous picture reference was a frame (e.g. a field invalidates a previous frame)
all_pictures_[slot_index].clear();
pictures_per_id_[slot_index].clear();
} else {
// Invalidate any existing picture reference with the specified id by removing it
auto prev_res_it = pictures_per_id_[slot_index].find(picture_id);
if (prev_res_it != pictures_per_id_[slot_index].end()) {
VideoPictureResource res = prev_res_it->second;
pictures_per_id_[slot_index].erase(picture_id);
// Check if there are any more references to the previous resource
auto other_ref_it = std::find_if(std::begin(pictures_per_id_[slot_index]), std::end(pictures_per_id_[slot_index]),
[&res](const auto &it) { return it.second == res; });
if (other_ref_it == pictures_per_id_[slot_index].end()) {
// If there are no remaining references to the resource, remove it
all_pictures_[slot_index].erase(res);
}
}
}
// If there are no remaining picture references then deactivate the slot
if (pictures_per_id_[slot_index].empty()) {
is_active_[slot_index] = false;
}
}
void VideoSessionDeviceState::Deactivate(int32_t slot_index) {
if (slot_index < 0 || static_cast<uint32_t>(slot_index) >= is_active_.size()) {
// Out-of-bounds slot index
return;
}
is_active_[slot_index] = false;
all_pictures_[slot_index].clear();
pictures_per_id_[slot_index].clear();
}
class RateControlStateMismatchRecorder {
public:
RateControlStateMismatchRecorder() : has_mismatch_{false}, stream_{}, string_{} {}
template <typename T>
void Record(const char *where, T actual, T expected) {
has_mismatch_ = true;
stream_ << where << " (" << actual << ") does not match current device state (" << expected << ")." << std::endl;
}
template <typename T>
void RecordDefault(const char *missing_struct, const char *member, T expected) {
has_mismatch_ = true;
stream_ << missing_struct << " is not in the pNext chain but the current device state for its " << member
<< " member is set (" << expected << ")." << std::endl;
}
template <typename T>
void RecordLayer(uint32_t layer_idx, const char *where, T actual, T expected) {
has_mismatch_ = true;
stream_ << where << " (" << actual << ") in VkVideoEncodeRateControlLayerInfoKHR::pLayers[" << layer_idx
<< "] does not match current device state (" << expected << ")." << std::endl;
}
template <typename T>
void RecordLayerDefault(uint32_t layer_idx, const char *missing_struct, const char *member, T expected) {
has_mismatch_ = true;
stream_ << missing_struct << " is not in the pNext chain of VkVideoEncodeRateControlLayerInfoKHR::pLayers[" << layer_idx
<< "] but the current device state for its " << member << " member is set (" << expected << ")." << std::endl;
}
bool HasMismatch() const { return has_mismatch_; }
const char *c_str() const {
string_ = stream_.str();
return string_.c_str();
}
private:
bool has_mismatch_{};
std::ostringstream stream_{};
mutable std::string string_{};
};
bool VideoSessionDeviceState::ValidateRateControlState(const Logger &log, const VideoSession *vs_state,
const vku::safe_VkVideoBeginCodingInfoKHR &begin_info,
const Location &loc) const {
bool skip = false;
assert(vs_state->IsEncode());
auto rc_base = vku::FindStructInPNextChain<VkVideoEncodeRateControlInfoKHR>(begin_info.pNext);
if (rc_base != nullptr) {
const auto &ref_base = encode_.rate_control_state.base;
RateControlStateMismatchRecorder mismatch_recorder{};
auto string_bool = [](VkBool32 value) { return value ? "VK_TRUE" : "VK_FALSE"; };
#define CHECK_RC_INFO(SCOPE, STRUCT_NAME, MEMBER, CONVERTER) \
if (rc_##SCOPE != nullptr) { \
if (rc_##SCOPE->MEMBER != ref_##SCOPE.MEMBER) { \
mismatch_recorder.Record(#STRUCT_NAME "::" #MEMBER, CONVERTER(rc_##SCOPE->MEMBER), CONVERTER(ref_##SCOPE.MEMBER)); \
} \
} else { \
if (ref_##SCOPE.MEMBER != decltype(ref_##SCOPE.MEMBER)()) { \
mismatch_recorder.RecordDefault(#STRUCT_NAME, #MEMBER, CONVERTER(ref_##SCOPE.MEMBER)); \
} \
}
#define CHECK_RC_LAYER_INFO(SCOPE, STRUCT_NAME, MEMBER, CONVERTER) \
if (rc_layer_##SCOPE != nullptr) { \
if (rc_layer_##SCOPE->MEMBER != ref_layer_##SCOPE.MEMBER) { \
mismatch_recorder.RecordLayer(layer_idx, #STRUCT_NAME "::" #MEMBER, CONVERTER(rc_layer_##SCOPE->MEMBER), \
CONVERTER(ref_layer_##SCOPE.MEMBER)); \
} \
} else { \
if (ref_layer_##SCOPE.MEMBER != decltype(ref_layer_##SCOPE.MEMBER)()) { \
mismatch_recorder.RecordLayerDefault(layer_idx, #STRUCT_NAME, #MEMBER, CONVERTER(ref_layer_##SCOPE.MEMBER)); \
} \
}
CHECK_RC_INFO(base, VkVideoEncodeRateControlInfoKHR, rateControlMode, string_VkVideoEncodeRateControlModeFlagBitsKHR);
CHECK_RC_INFO(base, VkVideoEncodeRateControlInfoKHR, layerCount, uint32_t);
CHECK_RC_INFO(base, VkVideoEncodeRateControlInfoKHR, virtualBufferSizeInMs, uint32_t);
CHECK_RC_INFO(base, VkVideoEncodeRateControlInfoKHR, initialVirtualBufferSizeInMs, uint32_t);
switch (vs_state->GetCodecOp()) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR: {
auto rc_h264 = vku::FindStructInPNextChain<VkVideoEncodeH264RateControlInfoKHR>(begin_info.pNext);
const auto &ref_h264 = encode_.rate_control_state.h264;
CHECK_RC_INFO(h264, VkVideoEncodeH264RateControlInfoKHR, flags, string_VkVideoEncodeH264RateControlFlagsKHR);
CHECK_RC_INFO(h264, VkVideoEncodeH264RateControlInfoKHR, gopFrameCount, uint32_t);
CHECK_RC_INFO(h264, VkVideoEncodeH264RateControlInfoKHR, idrPeriod, uint32_t);
CHECK_RC_INFO(h264, VkVideoEncodeH264RateControlInfoKHR, consecutiveBFrameCount, uint32_t);
CHECK_RC_INFO(h264, VkVideoEncodeH264RateControlInfoKHR, temporalLayerCount, uint32_t);
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR: {
auto rc_h265 = vku::FindStructInPNextChain<VkVideoEncodeH265RateControlInfoKHR>(begin_info.pNext);
const auto &ref_h265 = encode_.rate_control_state.h265;
CHECK_RC_INFO(h265, VkVideoEncodeH265RateControlInfoKHR, flags, string_VkVideoEncodeH265RateControlFlagsKHR);
CHECK_RC_INFO(h265, VkVideoEncodeH265RateControlInfoKHR, gopFrameCount, uint32_t);
CHECK_RC_INFO(h265, VkVideoEncodeH265RateControlInfoKHR, idrPeriod, uint32_t);
CHECK_RC_INFO(h265, VkVideoEncodeH265RateControlInfoKHR, consecutiveBFrameCount, uint32_t);
CHECK_RC_INFO(h265, VkVideoEncodeH265RateControlInfoKHR, subLayerCount, uint32_t);
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR: {
auto rc_av1 = vku::FindStructInPNextChain<VkVideoEncodeAV1RateControlInfoKHR>(begin_info.pNext);
const auto &ref_av1 = encode_.rate_control_state.av1;
CHECK_RC_INFO(av1, VkVideoEncodeAV1RateControlInfoKHR, flags, string_VkVideoEncodeAV1RateControlFlagsKHR);
CHECK_RC_INFO(av1, VkVideoEncodeAV1RateControlInfoKHR, gopFrameCount, uint32_t);
CHECK_RC_INFO(av1, VkVideoEncodeAV1RateControlInfoKHR, keyFramePeriod, uint32_t);
CHECK_RC_INFO(av1, VkVideoEncodeAV1RateControlInfoKHR, consecutiveBipredictiveFrameCount, uint32_t);
CHECK_RC_INFO(av1, VkVideoEncodeAV1RateControlInfoKHR, temporalLayerCount, uint32_t);
break;
}
default:
assert(false);
break;
}
for (uint32_t layer_idx = 0; layer_idx < std::min(rc_base->layerCount, ref_base.layerCount); ++layer_idx) {
const auto rc_layer_base = &rc_base->pLayers[layer_idx];
const auto &ref_layer_base = encode_.rate_control_state.layers[layer_idx].base;
CHECK_RC_LAYER_INFO(base, VkVideoEncodeRateControlLayerInfoKHR, averageBitrate, uint64_t);
CHECK_RC_LAYER_INFO(base, VkVideoEncodeRateControlLayerInfoKHR, maxBitrate, uint64_t);
CHECK_RC_LAYER_INFO(base, VkVideoEncodeRateControlLayerInfoKHR, frameRateNumerator, uint32_t);
CHECK_RC_LAYER_INFO(base, VkVideoEncodeRateControlLayerInfoKHR, frameRateDenominator, uint32_t);
switch (vs_state->GetCodecOp()) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR: {
auto rc_layer_h264 =
vku::FindStructInPNextChain<VkVideoEncodeH264RateControlLayerInfoKHR>(rc_layer_base->pNext);
const auto &ref_layer_h264 = encode_.rate_control_state.layers[layer_idx].h264;
CHECK_RC_LAYER_INFO(h264, VkVideoEncodeH264RateControlLayerInfoKHR, useMinQp, string_bool);
CHECK_RC_LAYER_INFO(h264, VkVideoEncodeH264RateControlLayerInfoKHR, minQp.qpI, int32_t);
CHECK_RC_LAYER_INFO(h264, VkVideoEncodeH264RateControlLayerInfoKHR, minQp.qpP, int32_t);
CHECK_RC_LAYER_INFO(h264, VkVideoEncodeH264RateControlLayerInfoKHR, minQp.qpB, int32_t);
CHECK_RC_LAYER_INFO(h264, VkVideoEncodeH264RateControlLayerInfoKHR, useMaxQp, string_bool);
CHECK_RC_LAYER_INFO(h264, VkVideoEncodeH264RateControlLayerInfoKHR, maxQp.qpI, int32_t);
CHECK_RC_LAYER_INFO(h264, VkVideoEncodeH264RateControlLayerInfoKHR, maxQp.qpP, int32_t);
CHECK_RC_LAYER_INFO(h264, VkVideoEncodeH264RateControlLayerInfoKHR, maxQp.qpB, int32_t);
CHECK_RC_LAYER_INFO(h264, VkVideoEncodeH264RateControlLayerInfoKHR, useMaxFrameSize, string_bool);
CHECK_RC_LAYER_INFO(h264, VkVideoEncodeH264RateControlLayerInfoKHR, maxFrameSize.frameISize, uint32_t);
CHECK_RC_LAYER_INFO(h264, VkVideoEncodeH264RateControlLayerInfoKHR, maxFrameSize.framePSize, uint32_t);
CHECK_RC_LAYER_INFO(h264, VkVideoEncodeH264RateControlLayerInfoKHR, maxFrameSize.frameBSize, uint32_t);
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR: {
auto rc_layer_h265 =
vku::FindStructInPNextChain<VkVideoEncodeH265RateControlLayerInfoKHR>(rc_layer_base->pNext);
const auto &ref_layer_h265 = encode_.rate_control_state.layers[layer_idx].h265;
CHECK_RC_LAYER_INFO(h265, VkVideoEncodeH265RateControlLayerInfoKHR, useMinQp, string_bool);
CHECK_RC_LAYER_INFO(h265, VkVideoEncodeH265RateControlLayerInfoKHR, minQp.qpI, int32_t);
CHECK_RC_LAYER_INFO(h265, VkVideoEncodeH265RateControlLayerInfoKHR, minQp.qpP, int32_t);
CHECK_RC_LAYER_INFO(h265, VkVideoEncodeH265RateControlLayerInfoKHR, minQp.qpB, int32_t);
CHECK_RC_LAYER_INFO(h265, VkVideoEncodeH265RateControlLayerInfoKHR, useMaxQp, string_bool);
CHECK_RC_LAYER_INFO(h265, VkVideoEncodeH265RateControlLayerInfoKHR, maxQp.qpI, int32_t);
CHECK_RC_LAYER_INFO(h265, VkVideoEncodeH265RateControlLayerInfoKHR, maxQp.qpP, int32_t);
CHECK_RC_LAYER_INFO(h265, VkVideoEncodeH265RateControlLayerInfoKHR, maxQp.qpB, int32_t);
CHECK_RC_LAYER_INFO(h265, VkVideoEncodeH265RateControlLayerInfoKHR, useMaxFrameSize, string_bool);
CHECK_RC_LAYER_INFO(h265, VkVideoEncodeH265RateControlLayerInfoKHR, maxFrameSize.frameISize, uint32_t);
CHECK_RC_LAYER_INFO(h265, VkVideoEncodeH265RateControlLayerInfoKHR, maxFrameSize.framePSize, uint32_t);
CHECK_RC_LAYER_INFO(h265, VkVideoEncodeH265RateControlLayerInfoKHR, maxFrameSize.frameBSize, uint32_t);
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR: {
auto rc_layer_av1 = vku::FindStructInPNextChain<VkVideoEncodeAV1RateControlLayerInfoKHR>(rc_layer_base->pNext);
const auto &ref_layer_av1 = encode_.rate_control_state.layers[layer_idx].av1;
CHECK_RC_LAYER_INFO(av1, VkVideoEncodeAV1RateControlLayerInfoKHR, useMinQIndex, string_bool);
CHECK_RC_LAYER_INFO(av1, VkVideoEncodeAV1RateControlLayerInfoKHR, minQIndex.intraQIndex, uint32_t);
CHECK_RC_LAYER_INFO(av1, VkVideoEncodeAV1RateControlLayerInfoKHR, minQIndex.predictiveQIndex, uint32_t);
CHECK_RC_LAYER_INFO(av1, VkVideoEncodeAV1RateControlLayerInfoKHR, minQIndex.bipredictiveQIndex, uint32_t);
CHECK_RC_LAYER_INFO(av1, VkVideoEncodeAV1RateControlLayerInfoKHR, useMaxQIndex, string_bool);
CHECK_RC_LAYER_INFO(av1, VkVideoEncodeAV1RateControlLayerInfoKHR, maxQIndex.intraQIndex, uint32_t);
CHECK_RC_LAYER_INFO(av1, VkVideoEncodeAV1RateControlLayerInfoKHR, maxQIndex.predictiveQIndex, uint32_t);
CHECK_RC_LAYER_INFO(av1, VkVideoEncodeAV1RateControlLayerInfoKHR, maxQIndex.bipredictiveQIndex, uint32_t);
CHECK_RC_LAYER_INFO(av1, VkVideoEncodeAV1RateControlLayerInfoKHR, useMaxFrameSize, string_bool);
CHECK_RC_LAYER_INFO(av1, VkVideoEncodeAV1RateControlLayerInfoKHR, maxFrameSize.intraFrameSize, uint32_t);
CHECK_RC_LAYER_INFO(av1, VkVideoEncodeAV1RateControlLayerInfoKHR, maxFrameSize.predictiveFrameSize, uint32_t);
CHECK_RC_LAYER_INFO(av1, VkVideoEncodeAV1RateControlLayerInfoKHR, maxFrameSize.bipredictiveFrameSize, uint32_t);
break;
}
default:
assert(false);
break;
}
}
#undef CHECK_RC_INFO
#undef CHECK_RC_LAYER_INFO
if (mismatch_recorder.HasMismatch()) {
skip |= log.LogError("VUID-vkCmdBeginVideoCodingKHR-pBeginInfo-08254", vs_state->Handle(), loc,
"The video encode rate control information specified when beginning the video coding scope "
"does not match the currently configured video encode rate control state for %s:\n%s",
log.FormatHandle(vs_state->Handle()).c_str(), mismatch_recorder.c_str());
}
} else {
if (encode_.rate_control_state.base.rateControlMode != VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DEFAULT_KHR) {
skip |= log.LogError("VUID-vkCmdBeginVideoCodingKHR-pBeginInfo-08253", vs_state->Handle(), loc,
"No VkVideoEncodeRateControlInfoKHR structure was specified when beginning the "
"video coding scope but the currently set video encode rate control mode for %s is %s.",
log.FormatHandle(vs_state->Handle()).c_str(),
string_VkVideoEncodeRateControlModeFlagBitsKHR(encode_.rate_control_state.base.rateControlMode));
}
}
return skip;
}
static VkVideoEncodeIntraRefreshModeFlagBitsKHR InitIntraRefreshMode(const VkVideoSessionCreateInfoKHR *ci) {
auto intra_refresh_info = vku::FindStructInPNextChain<VkVideoEncodeSessionIntraRefreshCreateInfoKHR>(ci->pNext);
return (intra_refresh_info) ? intra_refresh_info->intraRefreshMode : VK_VIDEO_ENCODE_INTRA_REFRESH_MODE_NONE_KHR;
}
VideoSession::VideoSession(const DeviceState &dev_data, VkVideoSessionKHR handle, VkVideoSessionCreateInfoKHR const *pCreateInfo,
std::shared_ptr<const VideoProfileDesc> &&profile_desc)
: StateObject(handle, kVulkanObjectTypeVideoSessionKHR),
safe_create_info(pCreateInfo),
create_info(*safe_create_info.ptr()),
profile(std::move(profile_desc)),
memory_binding_count_queried(false),
memory_bindings_queried(0),
memory_bindings_(GetMemoryBindings(dev_data, handle)),
unbound_memory_binding_count_(static_cast<uint32_t>(memory_bindings_.size())),
intra_refresh_mode_(InitIntraRefreshMode(pCreateInfo)),
device_state_mutex_(),
device_state_(pCreateInfo->maxDpbSlots) {}
VideoSession::MemoryBindingMap VideoSession::GetMemoryBindings(const DeviceState &dev_data, VkVideoSessionKHR vs) {
uint32_t memory_requirement_count;
DispatchGetVideoSessionMemoryRequirementsKHR(dev_data.device, vs, &memory_requirement_count, nullptr);
std::vector<VkVideoSessionMemoryRequirementsKHR> memory_requirements(memory_requirement_count,
vku::InitStruct<VkVideoSessionMemoryRequirementsKHR>());
DispatchGetVideoSessionMemoryRequirementsKHR(dev_data.device, vs, &memory_requirement_count, memory_requirements.data());
MemoryBindingMap memory_bindings;
for (uint32_t i = 0; i < memory_requirement_count; ++i) {
memory_bindings[memory_requirements[i].memoryBindIndex].requirements = memory_requirements[i].memoryRequirements;
}
return memory_bindings;
}
bool VideoSession::ReferenceSetupRequested(VkVideoDecodeInfoKHR const &decode_info) const {
switch (GetCodecOp()) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR: {
auto pic_info = vku::FindStructInPNextChain<VkVideoDecodeH264PictureInfoKHR>(decode_info.pNext);
return pic_info != nullptr && pic_info->pStdPictureInfo != nullptr && pic_info->pStdPictureInfo->flags.is_reference;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR: {
auto pic_info = vku::FindStructInPNextChain<VkVideoDecodeH265PictureInfoKHR>(decode_info.pNext);
return pic_info != nullptr && pic_info->pStdPictureInfo != nullptr && pic_info->pStdPictureInfo->flags.IsReference;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR: {
auto pic_info = vku::FindStructInPNextChain<VkVideoDecodeAV1PictureInfoKHR>(decode_info.pNext);
return pic_info != nullptr && pic_info->pStdPictureInfo != nullptr &&
pic_info->pStdPictureInfo->refresh_frame_flags != 0;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR: {
auto pic_info = vku::FindStructInPNextChain<VkVideoDecodeVP9PictureInfoKHR>(decode_info.pNext);
return pic_info != nullptr && pic_info->pStdPictureInfo != nullptr &&
pic_info->pStdPictureInfo->refresh_frame_flags != 0;
}
default:
return false;
}
}
bool VideoSession::ReferenceSetupRequested(VkVideoEncodeInfoKHR const &encode_info) const {
switch (GetCodecOp()) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR: {
auto pic_info = vku::FindStructInPNextChain<VkVideoEncodeH264PictureInfoKHR>(encode_info.pNext);
return pic_info != nullptr && pic_info->pStdPictureInfo != nullptr && pic_info->pStdPictureInfo->flags.is_reference;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR: {
auto pic_info = vku::FindStructInPNextChain<VkVideoEncodeH265PictureInfoKHR>(encode_info.pNext);
return pic_info != nullptr && pic_info->pStdPictureInfo != nullptr && pic_info->pStdPictureInfo->flags.is_reference;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR: {
auto pic_info = vku::FindStructInPNextChain<VkVideoEncodeAV1PictureInfoKHR>(encode_info.pNext);
return pic_info != nullptr && pic_info->pStdPictureInfo != nullptr &&
pic_info->pStdPictureInfo->refresh_frame_flags != 0;
}
default:
return false;
}
}
VideoSessionParameters::VideoSessionParameters(VkVideoSessionParametersKHR handle,
VkVideoSessionParametersCreateInfoKHR const *pCreateInfo,
std::shared_ptr<VideoSession> &&vsstate,
std::shared_ptr<VideoSessionParameters> &&vsp_template)
: StateObject(handle, kVulkanObjectTypeVideoSessionParametersKHR),
safe_create_info(pCreateInfo),
create_info(*safe_create_info.ptr()),
vs_state(vsstate),
mutex_(),
data_(),
config_(InitConfig(pCreateInfo)) {
data_.update_sequence_counter = 0;
switch (vs_state->GetCodecOp()) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR: {
const auto decode_h264 =
vku::FindStructInPNextChain<VkVideoDecodeH264SessionParametersCreateInfoKHR>(create_info.pNext);
if (vsp_template) {
auto template_data = vsp_template->Lock();
data_.h264.sps = template_data->h264.sps;
data_.h264.pps = template_data->h264.pps;
}
if (decode_h264->pParametersAddInfo) {
AddDecodeH264(decode_h264->pParametersAddInfo);
}
data_.h264.sps_capacity = decode_h264->maxStdSPSCount;
data_.h264.pps_capacity = decode_h264->maxStdPPSCount;
break;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR: {
const auto decode_h265 =
vku::FindStructInPNextChain<VkVideoDecodeH265SessionParametersCreateInfoKHR>(create_info.pNext);
if (vsp_template) {
auto template_data = vsp_template->Lock();
data_.h265.vps = template_data->h265.vps;
data_.h265.sps = template_data->h265.sps;
data_.h265.pps = template_data->h265.pps;
}
if (decode_h265->pParametersAddInfo) {
AddDecodeH265(decode_h265->pParametersAddInfo);
}
data_.h265.vps_capacity = decode_h265->maxStdVPSCount;
data_.h265.sps_capacity = decode_h265->maxStdSPSCount;
data_.h265.pps_capacity = decode_h265->maxStdPPSCount;
break;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR: {
const auto decode_av1 = vku::FindStructInPNextChain<VkVideoDecodeAV1SessionParametersCreateInfoKHR>(create_info.pNext);
if (decode_av1->pStdSequenceHeader) {
data_.av1.seq_header = std::make_unique<StdVideoAV1SequenceHeader>(*decode_av1->pStdSequenceHeader);
}
break;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR: {
// VP9 does not use video session parameters
assert(false);
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR: {
const auto encode_h264 =
vku::FindStructInPNextChain<VkVideoEncodeH264SessionParametersCreateInfoKHR>(create_info.pNext);
if (vsp_template) {
auto template_data = vsp_template->Lock();
data_.h264.sps = template_data->h264.sps;
data_.h264.pps = template_data->h264.pps;
}
if (encode_h264->pParametersAddInfo) {
AddEncodeH264(encode_h264->pParametersAddInfo);
}
data_.h264.sps_capacity = encode_h264->maxStdSPSCount;
data_.h264.pps_capacity = encode_h264->maxStdPPSCount;
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR: {
const auto encode_h265 =
vku::FindStructInPNextChain<VkVideoEncodeH265SessionParametersCreateInfoKHR>(create_info.pNext);
if (vsp_template) {
auto template_data = vsp_template->Lock();
data_.h265.vps = template_data->h265.vps;
data_.h265.sps = template_data->h265.sps;
data_.h265.pps = template_data->h265.pps;
}
if (encode_h265->pParametersAddInfo) {
AddEncodeH265(encode_h265->pParametersAddInfo);
}
data_.h265.vps_capacity = encode_h265->maxStdVPSCount;
data_.h265.sps_capacity = encode_h265->maxStdSPSCount;
data_.h265.pps_capacity = encode_h265->maxStdPPSCount;
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR: {
const auto encode_av1 = vku::FindStructInPNextChain<VkVideoEncodeAV1SessionParametersCreateInfoKHR>(create_info.pNext);
if (encode_av1->pStdSequenceHeader) {
data_.av1.seq_header = std::make_unique<StdVideoAV1SequenceHeader>(*encode_av1->pStdSequenceHeader);
}
break;
}
default:
break;
}
}
VideoSessionParameters::Config VideoSessionParameters::InitConfig(VkVideoSessionParametersCreateInfoKHR const *pCreateInfo) {
Config config{};
if (vs_state->IsEncode()) {
auto quality_level_info = vku::FindStructInPNextChain<VkVideoEncodeQualityLevelInfoKHR>(pCreateInfo->pNext);
if (quality_level_info != nullptr) {
config.encode.quality_level = quality_level_info->qualityLevel;
}
auto quantization_map_info =
vku::FindStructInPNextChain<VkVideoEncodeQuantizationMapSessionParametersCreateInfoKHR>(pCreateInfo->pNext);
if (quantization_map_info != nullptr) {
config.encode.quantization_map_texel_size = quantization_map_info->quantizationMapTexelSize;
}
}
return config;
}
void VideoSessionParameters::Update(VkVideoSessionParametersUpdateInfoKHR const *info) {
auto lock = Lock();
data_.update_sequence_counter = info->updateSequenceCount;
switch (vs_state->GetCodecOp()) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR: {
auto add_info = vku::FindStructInPNextChain<VkVideoDecodeH264SessionParametersAddInfoKHR>(info->pNext);
if (add_info) {
AddDecodeH264(add_info);
}
break;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR: {
auto add_info = vku::FindStructInPNextChain<VkVideoDecodeH265SessionParametersAddInfoKHR>(info->pNext);
if (add_info) {
AddDecodeH265(add_info);
}
break;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR: {
// AV1 decode session parameters cannot be updated
assert(false);
break;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR: {
// VP9 does not use video session parameters
assert(false);
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR: {
auto add_info = vku::FindStructInPNextChain<VkVideoEncodeH264SessionParametersAddInfoKHR>(info->pNext);
if (add_info) {
AddEncodeH264(add_info);
}
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR: {
auto add_info = vku::FindStructInPNextChain<VkVideoEncodeH265SessionParametersAddInfoKHR>(info->pNext);
if (add_info) {
AddEncodeH265(add_info);
}
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR: {
// AV1 encode session parameters cannot be updated
assert(false);
break;
}
default:
break;
}
}
void VideoSessionParameters::AddDecodeH264(VkVideoDecodeH264SessionParametersAddInfoKHR const *info) {
for (uint32_t i = 0; i < info->stdSPSCount; ++i) {
const auto &entry = info->pStdSPSs[i];
data_.h264.sps[GetH264SPSKey(entry)] = entry;
}
for (uint32_t i = 0; i < info->stdPPSCount; ++i) {
const auto &entry = info->pStdPPSs[i];
data_.h264.pps[GetH264PPSKey(entry)] = entry;
}
}
void VideoSessionParameters::AddDecodeH265(VkVideoDecodeH265SessionParametersAddInfoKHR const *info) {
for (uint32_t i = 0; i < info->stdVPSCount; ++i) {
const auto &entry = info->pStdVPSs[i];
data_.h265.vps[GetH265VPSKey(entry)] = entry;
}
for (uint32_t i = 0; i < info->stdSPSCount; ++i) {
const auto &entry = info->pStdSPSs[i];
data_.h265.sps[GetH265SPSKey(entry)] = entry;
}
for (uint32_t i = 0; i < info->stdPPSCount; ++i) {
const auto &entry = info->pStdPPSs[i];
data_.h265.pps[GetH265PPSKey(entry)] = entry;
}
}
void VideoSessionParameters::AddEncodeH264(VkVideoEncodeH264SessionParametersAddInfoKHR const *info) {
for (uint32_t i = 0; i < info->stdSPSCount; ++i) {
const auto &entry = info->pStdSPSs[i];
data_.h264.sps[GetH264SPSKey(entry)] = entry;
}
for (uint32_t i = 0; i < info->stdPPSCount; ++i) {
const auto &entry = info->pStdPPSs[i];
data_.h264.pps[GetH264PPSKey(entry)] = entry;
}
}
void VideoSessionParameters::AddEncodeH265(VkVideoEncodeH265SessionParametersAddInfoKHR const *info) {
for (uint32_t i = 0; i < info->stdVPSCount; ++i) {
const auto &entry = info->pStdVPSs[i];
data_.h265.vps[GetH265VPSKey(entry)] = entry;
}
for (uint32_t i = 0; i < info->stdSPSCount; ++i) {
const auto &entry = info->pStdSPSs[i];
data_.h265.sps[GetH265SPSKey(entry)] = entry;
}
for (uint32_t i = 0; i < info->stdPPSCount; ++i) {
const auto &entry = info->pStdPPSs[i];
data_.h265.pps[GetH265PPSKey(entry)] = entry;
}
}
std::string string_VideoProfileDesc(const vvl::VideoProfileDesc &profile) {
std::ostringstream ss;
const vvl::VideoProfileDesc::Profile &internal_profile = profile.GetProfile();
switch (internal_profile.base.videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
ss << "H.264 Decode";
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
ss << "H.265 Decode";
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR:
ss << "AV1 Decode";
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR:
ss << "VP9 Decode";
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
ss << "H.264 Encode";
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
ss << "H.265 Encode";
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:
ss << "AV1 Encode";
break;
default:
assert(false);
ss << "<unknown video codec>";
break;
}
ss << " (";
switch (internal_profile.base.chromaSubsampling) {
case VK_VIDEO_CHROMA_SUBSAMPLING_MONOCHROME_BIT_KHR:
ss << "monochrome";
break;
case VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR:
ss << "4:2:0";
break;
case VK_VIDEO_CHROMA_SUBSAMPLING_422_BIT_KHR:
ss << "4:2:2";
break;
case VK_VIDEO_CHROMA_SUBSAMPLING_444_BIT_KHR:
ss << "4:4:4";
break;
default:
assert(false);
ss << "<unknown chroma subsampling>";
break;
}
ss << " ";
auto string_video_component_bit_depth = [](const VkVideoComponentBitDepthFlagsKHR bit_depth) {
switch (bit_depth) {
case VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR:
return "8";
case VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR:
return "10";
case VK_VIDEO_COMPONENT_BIT_DEPTH_12_BIT_KHR:
return "12";
default:
assert(false);
return "<unknown bit depth>";
}
};
ss << string_video_component_bit_depth(internal_profile.base.lumaBitDepth);
if (internal_profile.base.chromaSubsampling != VK_VIDEO_CHROMA_SUBSAMPLING_MONOCHROME_BIT_KHR &&
internal_profile.base.lumaBitDepth != internal_profile.base.chromaBitDepth) {
ss << ":" << string_video_component_bit_depth(internal_profile.base.chromaBitDepth);
}
ss << "-bit) ";
auto string_std_video_h264_profile_idc = [](StdVideoH264ProfileIdc stdProfileIdc) {
switch (stdProfileIdc) {
case STD_VIDEO_H264_PROFILE_IDC_BASELINE:
return "Baseline";
case STD_VIDEO_H264_PROFILE_IDC_MAIN:
return "Main";
case STD_VIDEO_H264_PROFILE_IDC_HIGH:
return "High";
case STD_VIDEO_H264_PROFILE_IDC_HIGH_444_PREDICTIVE:
return "High 4:4:4 Predictive";
default:
assert(false);
return "<unknown H.264 profile indicator>";
}
};
auto string_video_decode_h264_picture_layout = [](VkVideoDecodeH264PictureLayoutFlagBitsKHR pictureLayout) {
switch (pictureLayout) {
case VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_PROGRESSIVE_KHR:
return "progressive";
case VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_INTERLACED_INTERLEAVED_LINES_BIT_KHR:
return "interlaced (interleaved lines)";
case VK_VIDEO_DECODE_H264_PICTURE_LAYOUT_INTERLACED_SEPARATE_PLANES_BIT_KHR:
return "interlaced (separate planes)";
default:
assert(false);
return "<unknown H.264 decode picture layout>";
}
};
auto string_std_video_h265_profile_idc = [](StdVideoH265ProfileIdc stdProfileIdc) {
switch (stdProfileIdc) {
case STD_VIDEO_H265_PROFILE_IDC_MAIN:
return "Main";
case STD_VIDEO_H265_PROFILE_IDC_MAIN_10:
return "Main 10";
case STD_VIDEO_H265_PROFILE_IDC_MAIN_STILL_PICTURE:
return "Main Still Picture";
case STD_VIDEO_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSIONS:
return "Format range extensions";
case STD_VIDEO_H265_PROFILE_IDC_SCC_EXTENSIONS:
return "Screen content coding extensions";
default:
assert(false);
return "<unknown H.265 profile indicator>";
}
};
auto string_std_video_av1_profile_idc = [](StdVideoAV1Profile stdProfileIdc) {
switch (stdProfileIdc) {
case STD_VIDEO_AV1_PROFILE_MAIN:
return "Main";
case STD_VIDEO_AV1_PROFILE_HIGH:
return "High";
case STD_VIDEO_AV1_PROFILE_PROFESSIONAL:
return "Professional";
default:
assert(false);
return "<unknown AV1 profile>";
}
};
auto string_std_video_vp9_profile = [](StdVideoVP9Profile stdProfile) {
switch (stdProfile) {
case STD_VIDEO_VP9_PROFILE_0:
return "Profile 0";
case STD_VIDEO_VP9_PROFILE_1:
return "Profile 1";
case STD_VIDEO_VP9_PROFILE_2:
return "Profile 2";
case STD_VIDEO_VP9_PROFILE_3:
return "Profile 3";
default:
assert(false);
return "<unknown VP9 profile>";
}
};
switch (internal_profile.base.videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
ss << string_std_video_h264_profile_idc(internal_profile.decode_h264.stdProfileIdc) << " "
<< string_video_decode_h264_picture_layout(internal_profile.decode_h264.pictureLayout);
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
ss << string_std_video_h264_profile_idc(internal_profile.encode_h264.stdProfileIdc);
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
ss << string_std_video_h265_profile_idc(internal_profile.decode_h265.stdProfileIdc);
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
ss << string_std_video_h265_profile_idc(internal_profile.encode_h265.stdProfileIdc);
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR:
ss << string_std_video_av1_profile_idc(internal_profile.decode_av1.stdProfile)
<< (internal_profile.decode_av1.filmGrainSupport ? " with film grain support" : " without film grain support");
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:
ss << string_std_video_av1_profile_idc(internal_profile.decode_av1.stdProfile);
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR:
ss << string_std_video_vp9_profile(internal_profile.decode_vp9.stdProfile);
break;
default:
assert(false);
ss << "<unknown video profile>";
break;
}
return ss.str();
}
std::string string_SupportedVideoProfiles(const SupportedVideoProfiles &profiles) {
std::ostringstream ss;
if (!profiles.empty()) {
for (const auto &profile : profiles) {
ss << "\t" << string_VideoProfileDesc(*profile) << "\n";
}
} else {
ss << "\tNone\n";
}
return ss.str();
}
} // namespace vvl