| /* 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 |