blob: d07395528f451b62b005c78844ad6ff0998be5b3 [file] [edit]
/* 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.
*/
#pragma once
#include "state_tracker/state_object.h"
#include "utils/hash_util.h"
#include <vulkan/utility/vk_safe_struct.hpp>
#include <cmath>
#include <memory>
#include <mutex>
#include <vector>
#include <functional>
namespace vvl {
class DeviceState;
class DeviceProxy;
class Image;
class ImageView;
class VideoSession;
struct QuantizationMapTexelSize {
struct compare {
bool operator()(VkExtent2D const &lhs, VkExtent2D const &rhs) const {
return lhs.width == rhs.width && lhs.height == rhs.height;
}
};
struct hash {
std::size_t operator()(VkExtent2D const &texel_size) const {
hash_util::HashCombiner hc;
hc << texel_size.width << texel_size.height;
return hc.Value();
}
};
};
using SupportedQuantizationMapTexelSizes =
unordered_set<VkExtent2D, QuantizationMapTexelSize::hash, QuantizationMapTexelSize::compare>;
using SupportedVideoProfiles = unordered_set<std::shared_ptr<const class VideoProfileDesc>>;
// The VideoProfileDesc contains the entire video profile description, which includes all
// parameters specified in VkVideoProfileInfoKHR and its pNext chain. This includes any
// parameters specific to the coding operation type (e.g. decode/encode usage hints/modes)
// and the codec. Accordingly, hashing and comparison takes into consideration all the
// relevant parameters.
class VideoProfileDesc : public std::enable_shared_from_this<VideoProfileDesc> {
public:
struct Profile {
bool valid;
bool is_decode;
bool is_encode;
VkVideoProfileInfoKHR base;
union {
VkVideoDecodeUsageInfoKHR decode_usage;
VkVideoEncodeUsageInfoKHR encode_usage;
};
union {
VkVideoDecodeH264ProfileInfoKHR decode_h264;
VkVideoDecodeH265ProfileInfoKHR decode_h265;
VkVideoDecodeAV1ProfileInfoKHR decode_av1;
VkVideoDecodeVP9ProfileInfoKHR decode_vp9;
VkVideoEncodeH264ProfileInfoKHR encode_h264;
VkVideoEncodeH265ProfileInfoKHR encode_h265;
VkVideoEncodeAV1ProfileInfoKHR encode_av1;
};
};
struct Capabilities {
bool supported;
VkVideoCapabilitiesKHR base;
union {
VkVideoDecodeCapabilitiesKHR decode;
VkVideoEncodeCapabilitiesKHR encode;
};
union {
VkVideoDecodeH264CapabilitiesKHR decode_h264;
VkVideoDecodeH265CapabilitiesKHR decode_h265;
VkVideoDecodeAV1CapabilitiesKHR decode_av1;
VkVideoDecodeVP9CapabilitiesKHR decode_vp9;
VkVideoEncodeH264CapabilitiesKHR encode_h264;
VkVideoEncodeH265CapabilitiesKHR encode_h265;
VkVideoEncodeAV1CapabilitiesKHR encode_av1;
};
union {
struct {
} decode_ext;
struct {
VkVideoEncodeQuantizationMapCapabilitiesKHR quantization_map;
VkVideoEncodeIntraRefreshCapabilitiesKHR intra_refresh;
} encode_ext;
};
};
VideoProfileDesc(VkPhysicalDevice physical_device, VkVideoProfileInfoKHR const *profile);
~VideoProfileDesc();
const Profile &GetProfile() const { return profile_; }
const Capabilities &GetCapabilities() const { return capabilities_; }
const SupportedQuantizationMapTexelSizes &GetSupportedQuantDeltaMapTexelSizes() const { return quant_delta_map_texel_sizes_; }
const SupportedQuantizationMapTexelSizes &GetSupportedEmphasisMapTexelSizes() const { return emphasis_map_texel_sizes_; }
bool IsDecode() const { return profile_.is_decode; }
bool IsEncode() const { return profile_.is_encode; }
VkVideoCodecOperationFlagBitsKHR GetCodecOp() const { return profile_.base.videoCodecOperation; }
VkVideoDecodeH264PictureLayoutFlagBitsKHR GetH264PictureLayout() const { return profile_.decode_h264.pictureLayout; }
bool HasAV1FilmGrainSupport() const { return profile_.decode_av1.filmGrainSupport; }
VkExtent2D GetMaxCodingBlockSize() const {
switch (profile_.base.videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR: {
const uint32_t mb_size = 16;
return {mb_size, mb_size};
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR: {
uint32_t max_ctb_size = 16 * static_cast<uint32_t>(log2(capabilities_.encode_h265.ctbSizes));
return {max_ctb_size, max_ctb_size};
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR: {
uint32_t max_sb_size = 128 * static_cast<uint32_t>(log2(capabilities_.encode_av1.superblockSizes));
return {max_sb_size, max_sb_size};
}
default:
assert(false);
return {0, 0};
}
}
struct compare {
public:
bool operator()(VideoProfileDesc const *lhs, VideoProfileDesc const *rhs) const {
bool match = lhs->profile_.base.videoCodecOperation == rhs->profile_.base.videoCodecOperation &&
lhs->profile_.base.chromaSubsampling == rhs->profile_.base.chromaSubsampling &&
lhs->profile_.base.lumaBitDepth == rhs->profile_.base.lumaBitDepth &&
lhs->profile_.base.chromaBitDepth == rhs->profile_.base.chromaBitDepth;
if (match && lhs->profile_.is_decode) {
match = match && lhs->profile_.decode_usage.videoUsageHints == rhs->profile_.decode_usage.videoUsageHints;
}
if (match && lhs->profile_.is_encode) {
match = match && lhs->profile_.encode_usage.videoUsageHints == rhs->profile_.encode_usage.videoUsageHints &&
lhs->profile_.encode_usage.videoContentHints == rhs->profile_.encode_usage.videoContentHints &&
lhs->profile_.encode_usage.tuningMode == rhs->profile_.encode_usage.tuningMode;
}
if (match) {
switch (lhs->profile_.base.videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
match = match && lhs->profile_.decode_h264.stdProfileIdc == rhs->profile_.decode_h264.stdProfileIdc &&
lhs->profile_.decode_h264.pictureLayout == rhs->profile_.decode_h264.pictureLayout;
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
match = match && lhs->profile_.decode_h265.stdProfileIdc == rhs->profile_.decode_h265.stdProfileIdc;
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR:
match = match && lhs->profile_.decode_av1.stdProfile == rhs->profile_.decode_av1.stdProfile &&
lhs->profile_.decode_av1.filmGrainSupport == rhs->profile_.decode_av1.filmGrainSupport;
break;
case VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR:
match = match && lhs->profile_.decode_vp9.stdProfile == rhs->profile_.decode_vp9.stdProfile;
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
match = match && lhs->profile_.encode_h264.stdProfileIdc == rhs->profile_.encode_h264.stdProfileIdc;
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
match = match && lhs->profile_.encode_h265.stdProfileIdc == rhs->profile_.encode_h265.stdProfileIdc;
break;
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR:
match = match && lhs->profile_.encode_av1.stdProfile == rhs->profile_.encode_av1.stdProfile;
break;
default:
break;
}
}
return match;
}
};
struct hash {
public:
std::size_t operator()(VideoProfileDesc const *desc) const {
hash_util::HashCombiner hc;
hc << desc->profile_.base.videoCodecOperation << desc->profile_.base.chromaSubsampling
<< desc->profile_.base.lumaBitDepth << desc->profile_.base.chromaBitDepth;
if (desc->profile_.is_decode) {
hc << desc->profile_.decode_usage.videoUsageHints;
}
if (desc->profile_.is_encode) {
hc << desc->profile_.encode_usage.videoUsageHints << desc->profile_.encode_usage.videoContentHints
<< desc->profile_.encode_usage.tuningMode;
}
switch (desc->profile_.base.videoCodecOperation) {
case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR: {
hc << desc->profile_.decode_h264.stdProfileIdc << desc->profile_.decode_h264.pictureLayout;
break;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR: {
hc << desc->profile_.decode_h265.stdProfileIdc;
break;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR: {
hc << desc->profile_.decode_av1.stdProfile << desc->profile_.decode_av1.filmGrainSupport;
break;
}
case VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR: {
hc << desc->profile_.decode_vp9.stdProfile;
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR: {
hc << desc->profile_.encode_h264.stdProfileIdc;
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR: {
hc << desc->profile_.encode_h265.stdProfileIdc;
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR: {
hc << desc->profile_.encode_av1.stdProfile;
break;
}
default:
break;
}
return hc.Value();
}
};
// The cache maintains non-owning references to all VideoProfileDesc objects that are referred to by shared
// pointers of VideoProfileDesc owned by any object.
// This ensured that every VideoProfileDesc object has only a unique instance acquired from the cache using the
// Get() method and thus enable us to compare video profiles through a single pointer comparison instead of a
// deep compare of the structure chains.
// Once all shared pointers to a VideoProfileDesc go away the destructor will call Release() to remove the
// non-owning reference to the to-be-deleted object.
class Cache {
public:
std::shared_ptr<const VideoProfileDesc> Get(VkPhysicalDevice physical_device, VkVideoProfileInfoKHR const *profile);
SupportedVideoProfiles Get(VkPhysicalDevice physical_device, VkVideoProfileListInfoKHR const *profile_list);
void Release(VideoProfileDesc const *desc);
private:
using PerDeviceVideoProfileDescSet =
unordered_set<VideoProfileDesc const *, VideoProfileDesc::hash, VideoProfileDesc::compare>;
std::mutex mutex_;
unordered_map<VkPhysicalDevice, PerDeviceVideoProfileDescSet> entries_;
std::shared_ptr<const VideoProfileDesc> GetOrCreate(VkPhysicalDevice physical_device, VkVideoProfileInfoKHR const *profile);
};
private:
VkPhysicalDevice physical_device_;
Profile profile_;
Capabilities capabilities_;
SupportedQuantizationMapTexelSizes quant_delta_map_texel_sizes_;
SupportedQuantizationMapTexelSizes emphasis_map_texel_sizes_;
Cache *cache_;
bool InitProfile(VkVideoProfileInfoKHR const *profile);
void InitCapabilities(VkPhysicalDevice physical_device);
void InitQuantizationMapFormats(VkPhysicalDevice physical_device);
};
class VideoPictureResource {
public:
std::shared_ptr<const ImageView> image_view_state;
std::shared_ptr<const Image> image_state;
uint32_t base_array_layer;
VkImageSubresourceRange range;
VkOffset2D coded_offset;
VkExtent2D coded_extent;
VideoPictureResource();
VideoPictureResource(const DeviceState &dev_data, VkVideoPictureResourceInfoKHR const &res);
operator bool() const { return image_view_state != nullptr; }
bool operator==(VideoPictureResource const &rhs) const {
return image_state == rhs.image_state && range.baseMipLevel == rhs.range.baseMipLevel &&
range.baseArrayLayer == rhs.range.baseArrayLayer && coded_offset.x == rhs.coded_offset.x &&
coded_offset.y == rhs.coded_offset.y && coded_extent.width == rhs.coded_extent.width &&
coded_extent.height == rhs.coded_extent.height;
}
bool operator!=(VideoPictureResource const &rhs) const {
return image_state != rhs.image_state || range.baseMipLevel != rhs.range.baseMipLevel ||
range.baseArrayLayer != rhs.range.baseArrayLayer || coded_offset.x != rhs.coded_offset.x ||
coded_offset.y != rhs.coded_offset.y || coded_extent.width != rhs.coded_extent.width ||
coded_extent.height != rhs.coded_extent.height;
}
struct hash {
public:
std::size_t operator()(VideoPictureResource const &res) const {
hash_util::HashCombiner hc;
hc << res.image_state.get() << res.range.baseMipLevel << res.range.baseArrayLayer << res.coded_offset.x
<< res.coded_offset.y << res.coded_extent.width << res.coded_extent.height;
return hc.Value();
}
};
VkOffset3D GetEffectiveImageOffset(const vvl::VideoSession &vs_state) const;
VkExtent3D GetEffectiveImageExtent(const vvl::VideoSession &vs_state) const;
private:
VkImageSubresourceRange GetImageSubresourceRange(ImageView const *image_view_state, uint32_t layer);
};
using VideoPictureResources = unordered_set<VideoPictureResource, VideoPictureResource::hash>;
using BoundVideoPictureResources = unordered_map<VideoPictureResource, int32_t, VideoPictureResource::hash>;
struct VideoPictureID {
// Used by H.264 to indicate it's a top field picture
bool top_field = false;
// Used by H.264 to indicate it's a bottom field picture
bool bottom_field = false;
VideoPictureID() {}
VideoPictureID(VideoProfileDesc const &profile, VkVideoReferenceSlotInfoKHR const &slot);
static VideoPictureID Frame() {
VideoPictureID id{};
return id;
}
static VideoPictureID TopField() {
VideoPictureID id{};
id.top_field = true;
return id;
}
static VideoPictureID BottomField() {
VideoPictureID id{};
id.bottom_field = true;
return id;
}
bool IsFrame() const { return !top_field && !bottom_field; }
bool IsTopField() const { return top_field && !bottom_field; }
bool IsBottomField() const { return !top_field && bottom_field; }
bool IsBothFields() const { return top_field && bottom_field; }
bool ContainsTopField() const { return top_field; }
bool ContainsBottomField() const { return bottom_field; }
bool operator==(VideoPictureID const &rhs) const { return top_field == rhs.top_field && bottom_field == rhs.bottom_field; }
struct hash {
public:
std::size_t operator()(VideoPictureID const &id) const {
hash_util::HashCombiner hc;
hc << id.top_field << id.bottom_field;
return hc.Value();
}
};
};
struct VideoReferenceSlot {
int32_t index;
VideoPictureID picture_id;
VideoPictureResource resource;
VideoReferenceSlot() : index(-1), picture_id(), resource() {}
VideoReferenceSlot(const DeviceState &dev_data, VideoProfileDesc const &profile, VkVideoReferenceSlotInfoKHR const &slot,
bool has_picture_id = true)
: index(slot.slotIndex),
picture_id(has_picture_id ? VideoPictureID(profile, slot) : VideoPictureID()),
resource(slot.pPictureResource ? VideoPictureResource(dev_data, *slot.pPictureResource) : VideoPictureResource()) {}
// The reference is only valid if it refers to a valid DPB index and resource
operator bool() const { return index >= 0 && resource; }
};
struct VideoEncodeRateControlLayerState {
VkVideoEncodeRateControlLayerInfoKHR base;
union {
VkVideoEncodeH264RateControlLayerInfoKHR h264;
VkVideoEncodeH265RateControlLayerInfoKHR h265;
VkVideoEncodeAV1RateControlLayerInfoKHR av1;
};
VideoEncodeRateControlLayerState(VkVideoCodecOperationFlagBitsKHR op = VK_VIDEO_CODEC_OPERATION_NONE_KHR,
const VkVideoEncodeRateControlLayerInfoKHR *info = nullptr) {
base = info ? *info : vku::InitStructHelper();
switch (op) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR: {
auto h264_info = vku::FindStructInPNextChain<VkVideoEncodeH264RateControlLayerInfoKHR>(info);
h264 = h264_info ? *h264_info : vku::InitStructHelper();
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR: {
auto h265_info = vku::FindStructInPNextChain<VkVideoEncodeH265RateControlLayerInfoKHR>(info);
h265 = h265_info ? *h265_info : vku::InitStructHelper();
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR: {
auto av1_info = vku::FindStructInPNextChain<VkVideoEncodeAV1RateControlLayerInfoKHR>(info);
av1 = av1_info ? *av1_info : vku::InitStructHelper();
break;
}
default:
assert(false);
break;
}
}
};
struct VideoEncodeRateControlState {
bool default_state{true};
VkVideoEncodeRateControlInfoKHR base;
union {
VkVideoEncodeH264RateControlInfoKHR h264;
VkVideoEncodeH265RateControlInfoKHR h265;
VkVideoEncodeAV1RateControlInfoKHR av1;
};
std::vector<VideoEncodeRateControlLayerState> layers;
VideoEncodeRateControlState(VkVideoCodecOperationFlagBitsKHR op = VK_VIDEO_CODEC_OPERATION_NONE_KHR,
const void *info = nullptr) {
auto base_info = vku::FindStructInPNextChain<VkVideoEncodeRateControlInfoKHR>(info);
if (base_info != nullptr) {
default_state = false;
base = *base_info;
} else {
default_state = true;
base = vku::InitStructHelper();
}
switch (op) {
case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR: {
auto h264_info = vku::FindStructInPNextChain<VkVideoEncodeH264RateControlInfoKHR>(info);
h264 = h264_info ? *h264_info : vku::InitStructHelper();
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR: {
auto h265_info = vku::FindStructInPNextChain<VkVideoEncodeH265RateControlInfoKHR>(info);
h265 = h265_info ? *h265_info : vku::InitStructHelper();
break;
}
case VK_VIDEO_CODEC_OPERATION_ENCODE_AV1_BIT_KHR: {
auto av1_info = vku::FindStructInPNextChain<VkVideoEncodeAV1RateControlInfoKHR>(info);
av1 = av1_info ? *av1_info : vku::InitStructHelper();
break;
}
default:
break;
}
layers.reserve(base.layerCount);
for (uint32_t i = 0; i < base.layerCount; ++i) {
layers.emplace_back(op, &base.pLayers[i]);
}
}
operator bool() const { return !default_state; }
};
class VideoSessionDeviceState {
public:
VideoSessionDeviceState(uint32_t reference_slot_count = 0)
: initialized_(false),
is_active_(reference_slot_count, false),
all_pictures_(reference_slot_count),
pictures_per_id_(reference_slot_count),
encode_() {}
bool IsInitialized() const { return initialized_; }
bool IsSlotActive(int32_t slot_index) const {
if (slot_index < 0 || static_cast<uint32_t>(slot_index) >= is_active_.size()) {
// Out-of-bounds slot index
return false;
}
return is_active_[slot_index];
}
bool IsSlotPicture(int32_t slot_index, const VideoPictureResource &res) const {
if (slot_index < 0 || static_cast<uint32_t>(slot_index) >= all_pictures_.size()) {
// Out-of-bounds slot index
return false;
}
return all_pictures_[slot_index].find(res) != all_pictures_[slot_index].end();
}
bool IsSlotPicture(int32_t slot_index, const VideoPictureID &picture_id, const VideoPictureResource &res) const {
if (slot_index < 0 || static_cast<uint32_t>(slot_index) >= pictures_per_id_.size()) {
// Out-of-bounds slot index
return false;
}
auto it = pictures_per_id_[slot_index].find(picture_id);
return it != pictures_per_id_[slot_index].end() && it->second == res;
}
uint32_t GetEncodeQualityLevel() const { return encode_.quality_level; }
const VideoEncodeRateControlState &GetRateControlState() const { return encode_.rate_control_state; }
void Reset();
void Activate(int32_t slot_index, const VideoPictureID &picture_id, const VideoPictureResource &res);
void Invalidate(int32_t slot_index, const VideoPictureID &picture_id);
void Deactivate(int32_t slot_index);
void SetEncodeQualityLevel(uint32_t quality_level) { encode_.quality_level = quality_level; }
void SetRateControlState(const VideoEncodeRateControlState &rate_control_state) {
encode_.rate_control_state = rate_control_state;
}
bool ValidateRateControlState(const Logger &log, const VideoSession *vs_state,
const vku::safe_VkVideoBeginCodingInfoKHR &begin_info, const Location &loc) const;
private:
bool initialized_;
std::vector<bool> is_active_;
std::vector<VideoPictureResources> all_pictures_;
std::vector<unordered_map<VideoPictureID, VideoPictureResource, VideoPictureID::hash>> pictures_per_id_;
struct {
uint32_t quality_level{0};
VideoEncodeRateControlState rate_control_state{};
} encode_;
};
class VideoSession : public StateObject {
public:
struct MemoryBindingInfo {
VkMemoryRequirements requirements;
bool bound;
};
using MemoryBindingMap = unordered_map<uint32_t, MemoryBindingInfo>;
const vku::safe_VkVideoSessionCreateInfoKHR safe_create_info;
const VkVideoSessionCreateInfoKHR &create_info;
std::shared_ptr<const VideoProfileDesc> profile;
bool memory_binding_count_queried;
uint32_t memory_bindings_queried;
class DeviceStateWriter {
public:
DeviceStateWriter(VideoSession *state) : lock_(state->device_state_mutex_), state_(state->device_state_) {}
VideoSessionDeviceState &operator*() { return state_; }
private:
std::unique_lock<std::mutex> lock_;
VideoSessionDeviceState &state_;
};
VideoSession(const DeviceState &dev_data, VkVideoSessionKHR handle, VkVideoSessionCreateInfoKHR const *pCreateInfo,
std::shared_ptr<const VideoProfileDesc> &&profile_desc);
VkVideoSessionKHR VkHandle() const { return handle_.Cast<VkVideoSessionKHR>(); }
bool IsDecode() const { return profile->IsDecode(); }
bool IsEncode() const { return profile->IsEncode(); }
VkVideoCodecOperationFlagBitsKHR GetCodecOp() const { return profile->GetCodecOp(); }
VkVideoDecodeH264PictureLayoutFlagBitsKHR GetH264PictureLayout() const { return profile->GetH264PictureLayout(); }
VideoSessionDeviceState DeviceStateCopy() const {
std::unique_lock<std::mutex> lock(device_state_mutex_);
return device_state_;
}
DeviceStateWriter DeviceStateWrite() { return DeviceStateWriter(this); }
const MemoryBindingInfo *GetMemoryBindingInfo(uint32_t index) const {
auto it = memory_bindings_.find(index);
if (it != memory_bindings_.end()) {
return &it->second;
} else {
return nullptr;
}
}
uint32_t GetMemoryBindingCount() const { return (uint32_t)memory_bindings_.size(); }
uint32_t GetUnboundMemoryBindingCount() const { return unbound_memory_binding_count_; }
void BindMemoryBindingIndex(uint32_t index) {
auto it = memory_bindings_.find(index);
if (it != memory_bindings_.end() && !it->second.bound) {
it->second.bound = true;
--unbound_memory_binding_count_;
}
}
uint32_t GetVideoDecodeOperationCount(VkVideoDecodeInfoKHR const *) const { return 1; }
uint32_t GetVideoEncodeOperationCount(VkVideoEncodeInfoKHR const *) const { return 1; }
bool ReferenceSetupRequested(VkVideoDecodeInfoKHR const &decode_info) const;
bool ReferenceSetupRequested(VkVideoEncodeInfoKHR const &encode_info) const;
VkVideoEncodeIntraRefreshModeFlagBitsKHR GetIntraRefreshMode() const { return intra_refresh_mode_; }
private:
MemoryBindingMap GetMemoryBindings(const DeviceState &dev_data, VkVideoSessionKHR vs);
MemoryBindingMap memory_bindings_;
uint32_t unbound_memory_binding_count_;
const VkVideoEncodeIntraRefreshModeFlagBitsKHR intra_refresh_mode_;
mutable std::mutex device_state_mutex_;
VideoSessionDeviceState device_state_;
};
class VideoSessionParameters : public StateObject {
public:
using H264SPSKey = uint8_t;
using H264PPSKey = uint16_t;
using H265VPSKey = uint8_t;
using H265SPSKey = uint16_t;
using H265PPSKey = uint32_t;
using ParameterKey = uint32_t;
struct H264Parameters {
unordered_map<H264SPSKey, StdVideoH264SequenceParameterSet> sps;
unordered_map<H264PPSKey, StdVideoH264PictureParameterSet> pps;
uint32_t sps_capacity;
uint32_t pps_capacity;
};
struct H265Parameters {
unordered_map<H265VPSKey, StdVideoH265VideoParameterSet> vps;
unordered_map<H265SPSKey, StdVideoH265SequenceParameterSet> sps;
unordered_map<H265PPSKey, StdVideoH265PictureParameterSet> pps;
uint32_t vps_capacity;
uint32_t sps_capacity;
uint32_t pps_capacity;
};
struct AV1Parameters {
std::unique_ptr<StdVideoAV1SequenceHeader> seq_header;
};
struct Data {
uint32_t update_sequence_counter;
H264Parameters h264;
H265Parameters h265;
AV1Parameters av1;
};
struct EncodeConfig {
uint32_t quality_level{0};
VkExtent2D quantization_map_texel_size{0, 0};
};
struct Config {
EncodeConfig encode;
};
static H264SPSKey GetH264SPSKey(const StdVideoH264SequenceParameterSet &sps) { return sps.seq_parameter_set_id; }
static H264PPSKey GetH264PPSKey(const StdVideoH264PictureParameterSet &pps) {
return GetKeyFor2xID8(pps.seq_parameter_set_id, pps.pic_parameter_set_id);
}
static H265VPSKey GetH265VPSKey(const StdVideoH265VideoParameterSet &vps) { return vps.vps_video_parameter_set_id; }
static H265SPSKey GetH265SPSKey(const StdVideoH265SequenceParameterSet &sps) {
return GetKeyFor2xID8(sps.sps_video_parameter_set_id, sps.sps_seq_parameter_set_id);
}
static H265PPSKey GetH265PPSKey(const StdVideoH265PictureParameterSet &pps) {
return GetKeyFor3xID8(pps.sps_video_parameter_set_id, pps.pps_seq_parameter_set_id, pps.pps_pic_parameter_set_id);
}
class ReadOnlyAccessor {
public:
ReadOnlyAccessor() : lock_(), data_(nullptr) {}
ReadOnlyAccessor(const VideoSessionParameters *state) : lock_(state->mutex_), data_(&state->data_) {}
operator bool() const { return data_ != nullptr; }
const Data *operator->() const { return data_; }
const StdVideoH264SequenceParameterSet *GetH264SPS(uint32_t sps_id) const {
if (sps_id > 0xFF) return nullptr;
auto it = data_->h264.sps.find(sps_id & 0xFF);
if (it != data_->h264.sps.end()) {
return &it->second;
} else {
return nullptr;
}
}
const StdVideoH264PictureParameterSet *GetH264PPS(uint32_t sps_id, uint32_t pps_id) const {
if (sps_id > 0xFF) return nullptr;
if (pps_id > 0xFF) return nullptr;
auto it = data_->h264.pps.find(GetKeyFor2xID8(sps_id & 0xFF, pps_id & 0xFF));
if (it != data_->h264.pps.end()) {
return &it->second;
} else {
return nullptr;
}
}
const StdVideoH265VideoParameterSet *GetH265VPS(uint32_t vps_id) const {
if (vps_id > 0xFF) return nullptr;
auto it = data_->h265.vps.find(vps_id & 0xFF);
if (it != data_->h265.vps.end()) {
return &it->second;
} else {
return nullptr;
}
}
const StdVideoH265SequenceParameterSet *GetH265SPS(uint32_t vps_id, uint32_t sps_id) const {
if (vps_id > 0xFF) return nullptr;
if (sps_id > 0xFF) return nullptr;
auto it = data_->h265.sps.find(GetKeyFor2xID8(vps_id & 0xFF, sps_id & 0xFF));
if (it != data_->h265.sps.end()) {
return &it->second;
} else {
return nullptr;
}
}
const StdVideoH265PictureParameterSet *GetH265PPS(uint32_t vps_id, uint32_t sps_id, uint32_t pps_id) const {
if (vps_id > 0xFF) return nullptr;
if (sps_id > 0xFF) return nullptr;
if (pps_id > 0xFF) return nullptr;
auto it = data_->h265.pps.find(GetKeyFor3xID8(vps_id & 0xFF, sps_id & 0xFF, pps_id & 0xFF));
if (it != data_->h265.pps.end()) {
return &it->second;
} else {
return nullptr;
}
}
const StdVideoAV1SequenceHeader *GetAV1SequenceHeader() const { return data_->av1.seq_header.get(); }
private:
std::unique_lock<std::mutex> lock_;
const Data *data_;
};
const vku::safe_VkVideoSessionParametersCreateInfoKHR safe_create_info;
const VkVideoSessionParametersCreateInfoKHR &create_info;
std::shared_ptr<const VideoSession> vs_state;
VideoSessionParameters(VkVideoSessionParametersKHR handle, VkVideoSessionParametersCreateInfoKHR const *pCreateInfo,
std::shared_ptr<VideoSession> &&vsstate, std::shared_ptr<VideoSessionParameters> &&vsp_template);
VkVideoSessionParametersKHR VkHandle() const { return handle_.Cast<VkVideoSessionParametersKHR>(); }
bool IsDecode() const { return vs_state->IsDecode(); }
bool IsEncode() const { return vs_state->IsEncode(); }
VkVideoCodecOperationFlagBitsKHR GetCodecOp() const { return vs_state->GetCodecOp(); }
uint32_t GetEncodeQualityLevel() const { return config_.encode.quality_level; }
VkExtent2D GetEncodeQuantizationMapTexelSize() const { return config_.encode.quantization_map_texel_size; }
void Update(VkVideoSessionParametersUpdateInfoKHR const *info);
ReadOnlyAccessor Lock() const { return ReadOnlyAccessor(this); }
private:
mutable std::mutex mutex_;
Data data_;
const Config config_;
Config InitConfig(VkVideoSessionParametersCreateInfoKHR const *pCreateInfo);
static uint16_t GetKeyFor2xID8(uint8_t id1, uint8_t id2) { return (id1 << 8) | id2; }
static uint32_t GetKeyFor3xID8(uint8_t id1, uint8_t id2, uint8_t id3) { return (id1 << 16) | (id2 << 8) | id3; }
void AddDecodeH264(VkVideoDecodeH264SessionParametersAddInfoKHR const *info);
void AddDecodeH265(VkVideoDecodeH265SessionParametersAddInfoKHR const *info);
void AddEncodeH264(VkVideoEncodeH264SessionParametersAddInfoKHR const *info);
void AddEncodeH265(VkVideoEncodeH265SessionParametersAddInfoKHR const *info);
};
using VideoSessionUpdateList =
std::vector<std::function<bool(const VideoSession *vs_state, VideoSessionDeviceState &dev_state, bool do_validate)>>;
using VideoSessionUpdateMap = unordered_map<VkVideoSessionKHR, VideoSessionUpdateList>;
std::string string_VideoProfileDesc(const vvl::VideoProfileDesc &profile);
std::string string_SupportedVideoProfiles(const SupportedVideoProfiles &profiles);
} // namespace vvl