/* * Copyright © 2021 Red Hat * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "anv_private.h" #include #include #include "vk_common_entrypoints.h" #include "vk_video/vulkan_video_codecs_common.h" #include "vk_video/vulkan_video_codec_h264std.h" #include "vk_video/vulkan_video_codec_h264std_encode.h" VkResult anv_CreateVideoSessionKHR(VkDevice _device, const VkVideoSessionCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkVideoSessionKHR *pVideoSession) { ANV_FROM_HANDLE(anv_device, device, _device); struct anv_video_session *vid = vk_alloc2(&device->vk.alloc, pAllocator, sizeof(*vid), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); if (!vid) return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); memset(vid, 0, sizeof(struct anv_video_session)); VkResult result = vk_video_session_init(&device->vk, &vid->vk, pCreateInfo); if (result != VK_SUCCESS) { vk_free2(&device->vk.alloc, pAllocator, vid); return result; } *pVideoSession = anv_video_session_to_handle(vid); return VK_SUCCESS; } void anv_DestroyVideoSessionKHR(VkDevice _device, VkVideoSessionKHR _session, const VkAllocationCallbacks *pAllocator) { ANV_FROM_HANDLE(anv_device, device, _device); ANV_FROM_HANDLE(anv_video_session, vid, _session); if (!_session) return; /* Otherwise we hit VK_ERROR_DEVICE_LOST for unknown reasons. */ vk_common_DeviceWaitIdle(_device); vk_video_session_finish(&vid->vk); vk_free2(&device->vk.alloc, pAllocator, vid); } VkResult anv_CreateVideoSessionParametersKHR(VkDevice _device, const VkVideoSessionParametersCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkVideoSessionParametersKHR *pVideoSessionParameters) { ANV_FROM_HANDLE(anv_device, device, _device); struct vk_video_session_parameters *params = vk_video_session_parameters_create(&device->vk, pCreateInfo, pAllocator, sizeof(struct anv_video_session_params)); if (!params) return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY); *pVideoSessionParameters = vk_video_session_parameters_to_handle(params); return VK_SUCCESS; } void anv_DestroyVideoSessionParametersKHR(VkDevice _device, VkVideoSessionParametersKHR _params, const VkAllocationCallbacks *pAllocator) { ANV_FROM_HANDLE(anv_device, device, _device); VK_FROM_HANDLE(vk_video_session_parameters, params, _params); vk_video_session_parameters_destroy(&device->vk, pAllocator, params); } VkResult anv_GetPhysicalDeviceVideoCapabilitiesKHR(VkPhysicalDevice physicalDevice, const VkVideoProfileInfoKHR *pVideoProfile, VkVideoCapabilitiesKHR *pCapabilities) { const bool is_encode = pVideoProfile->videoCodecOperation == VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR; if (pVideoProfile->videoCodecOperation != VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR && !is_encode) { return VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR; } if (pVideoProfile->chromaSubsampling != VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR || pVideoProfile->lumaBitDepth != VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR || pVideoProfile->chromaBitDepth != VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR) return VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR; if (!is_encode) { const VkVideoDecodeH264ProfileInfoKHR *h264_profile = vk_find_struct_const(pVideoProfile->pNext, VIDEO_DECODE_H264_PROFILE_INFO_KHR); if (h264_profile) { switch (h264_profile->stdProfileIdc) { case STD_VIDEO_H264_PROFILE_IDC_BASELINE: case STD_VIDEO_H264_PROFILE_IDC_MAIN: case STD_VIDEO_H264_PROFILE_IDC_HIGH: break; default: return VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR; } } } else { /* Baseline and Main are supported. Main is accepted so that CABAC * (entropy_coding_mode_flag == 1, forbidden in Baseline) can be selected; * the PAK performs the entropy coding. Main's inter-prediction features * (B slices) are still not emitted — only I frames are produced. High is * rejected (8x8 transform / scaling lists not emitted). */ const VkVideoEncodeH264ProfileInfoKHR *h264_profile = vk_find_struct_const(pVideoProfile->pNext, VIDEO_ENCODE_H264_PROFILE_INFO_KHR); if (h264_profile) { switch (h264_profile->stdProfileIdc) { case STD_VIDEO_H264_PROFILE_IDC_BASELINE: case STD_VIDEO_H264_PROFILE_IDC_MAIN: break; default: return VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR; } } } if (is_encode) { pCapabilities->minBitstreamBufferOffsetAlignment = 4096; pCapabilities->minBitstreamBufferSizeAlignment = 4096; } else { pCapabilities->minBitstreamBufferOffsetAlignment = 32; pCapabilities->minBitstreamBufferSizeAlignment = 32; } pCapabilities->pictureAccessGranularity.width = ANV_MB_WIDTH; pCapabilities->pictureAccessGranularity.height = ANV_MB_HEIGHT; pCapabilities->minCodedExtent.width = ANV_MB_WIDTH; pCapabilities->minCodedExtent.height = ANV_MB_HEIGHT; pCapabilities->maxCodedExtent.width = 4096; pCapabilities->maxCodedExtent.height = 4096; pCapabilities->flags = VK_VIDEO_CAPABILITY_SEPARATE_REFERENCE_IMAGES_BIT_KHR; if (!is_encode) { struct VkVideoDecodeCapabilitiesKHR *dec_caps = (struct VkVideoDecodeCapabilitiesKHR *) vk_find_struct(pCapabilities->pNext, VIDEO_DECODE_CAPABILITIES_KHR); if (dec_caps) dec_caps->flags = VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR; struct VkVideoDecodeH264CapabilitiesKHR *ext = (struct VkVideoDecodeH264CapabilitiesKHR *) vk_find_struct(pCapabilities->pNext, VIDEO_DECODE_H264_CAPABILITIES_KHR); pCapabilities->maxDpbSlots = 16; pCapabilities->maxActiveReferencePictures = 16; if (ext) { ext->fieldOffsetGranularity.x = 0; ext->fieldOffsetGranularity.y = 0; ext->maxLevelIdc = 51; } strcpy(pCapabilities->stdHeaderVersion.extensionName, VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_EXTENSION_NAME); pCapabilities->stdHeaderVersion.specVersion = VK_STD_VULKAN_VIDEO_CODEC_H264_DECODE_SPEC_VERSION; } else { /* Limits reflecting what the encoder emits: I and P frames, CQP. A DPB * slot maps onto a frame store of the same index and there are 16 of * those, so a long GOP can keep every picture it likes around. Only one * of them can be predicted from, though: each macroblock is coded against * reference index 0, so multi-reference and B frames are still out. */ pCapabilities->maxDpbSlots = ANV_VIDEO_MAX_DPB_SLOTS; pCapabilities->maxActiveReferencePictures = 1; struct VkVideoEncodeCapabilitiesKHR *enc_caps = (struct VkVideoEncodeCapabilitiesKHR *) vk_find_struct(pCapabilities->pNext, VIDEO_ENCODE_CAPABILITIES_KHR); if (enc_caps) { enc_caps->flags = 0; /* CBR and VBR are open loop: the quantizer is derived from the * bitrate target once per picture and nothing measures the result, * so both behave the same and neither tracks the target closely. * See anv_video_rate_control_qp(). */ enc_caps->rateControlModes = VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DISABLED_BIT_KHR | VK_VIDEO_ENCODE_RATE_CONTROL_MODE_CBR_BIT_KHR | VK_VIDEO_ENCODE_RATE_CONTROL_MODE_VBR_BIT_KHR; enc_caps->maxRateControlLayers = 1; /* Level 4.1's ceiling, the highest level the encoder reports. */ enc_caps->maxBitrate = 62500000; enc_caps->maxQualityLevels = 1; enc_caps->encodeInputPictureGranularity.width = ANV_MB_WIDTH; enc_caps->encodeInputPictureGranularity.height = ANV_MB_HEIGHT; /* Bytes written comes from the MFC bitstream byte counter; see the * feedback query write in anv_h264_encode_video(). */ enc_caps->supportedEncodeFeedbackFlags = VK_VIDEO_ENCODE_FEEDBACK_BITSTREAM_BUFFER_OFFSET_BIT_KHR | VK_VIDEO_ENCODE_FEEDBACK_BITSTREAM_BYTES_WRITTEN_BIT_KHR; } struct VkVideoEncodeIntraRefreshCapabilitiesKHR *ir_caps = (struct VkVideoEncodeIntraRefreshCapabilitiesKHR *) vk_find_struct(pCapabilities->pNext, VIDEO_ENCODE_INTRA_REFRESH_CAPABILITIES_KHR); if (ir_caps) { /* Each macroblock's intra/inter decision is made on its own when the * PAK objects are emitted, so the picture can be split along any of * these axes. Per-picture-partition mode needs nothing extra: the * refreshed partition is a slice the client marks as an I slice, and * slices are already coded according to their own slice_type. */ ir_caps->intraRefreshModes = VK_VIDEO_ENCODE_INTRA_REFRESH_MODE_PER_PICTURE_PARTITION_BIT_KHR | VK_VIDEO_ENCODE_INTRA_REFRESH_MODE_BLOCK_BASED_BIT_KHR | VK_VIDEO_ENCODE_INTRA_REFRESH_MODE_BLOCK_ROW_BASED_BIT_KHR | VK_VIDEO_ENCODE_INTRA_REFRESH_MODE_BLOCK_COLUMN_BASED_BIT_KHR; /* Down to one region per macroblock at the largest coded extent. */ ir_caps->maxIntraRefreshCycleDuration = (4096 / ANV_MB_WIDTH) * (4096 / ANV_MB_HEIGHT); ir_caps->maxIntraRefreshActiveReferencePictures = 1; /* Regions are macroblock ranges that owe nothing to slice boundaries, * and a block-based one is not rectangular. */ ir_caps->partitionIndependentIntraRefreshRegions = VK_TRUE; ir_caps->nonRectangularIntraRefreshRegions = VK_TRUE; } struct VkVideoEncodeH264CapabilitiesKHR *h264_enc_ext = (struct VkVideoEncodeH264CapabilitiesKHR *) vk_find_struct(pCapabilities->pNext, VIDEO_ENCODE_H264_CAPABILITIES_KHR); if (h264_enc_ext) { h264_enc_ext->flags = VK_VIDEO_ENCODE_H264_CAPABILITY_PER_SLICE_CONSTANT_QP_BIT_KHR; h264_enc_ext->maxLevelIdc = STD_VIDEO_H264_LEVEL_IDC_4_1; /* One slice per MB row at most (matches the encoder's row-granular * slice splitting). */ h264_enc_ext->maxSliceCount = 68; /* 1088/16, covers up to 1080p */ h264_enc_ext->maxPPictureL0ReferenceCount = 1; h264_enc_ext->maxBPictureL0ReferenceCount = 0; h264_enc_ext->maxL1ReferenceCount = 0; h264_enc_ext->maxTemporalLayerCount = 1; h264_enc_ext->expectDyadicTemporalLayerPattern = VK_FALSE; h264_enc_ext->minQp = 0; h264_enc_ext->maxQp = 51; h264_enc_ext->prefersGopRemainingFrames = VK_FALSE; h264_enc_ext->requiresGopRemainingFrames = VK_FALSE; h264_enc_ext->stdSyntaxFlags = 0; } strcpy(pCapabilities->stdHeaderVersion.extensionName, VK_STD_VULKAN_VIDEO_CODEC_H264_ENCODE_EXTENSION_NAME); pCapabilities->stdHeaderVersion.specVersion = VK_STD_VULKAN_VIDEO_CODEC_H264_ENCODE_SPEC_VERSION; } return VK_SUCCESS; } VkResult anv_GetPhysicalDeviceVideoFormatPropertiesKHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceVideoFormatInfoKHR *pVideoFormatInfo, uint32_t *pVideoFormatPropertyCount, VkVideoFormatPropertiesKHR *pVideoFormatProperties) { *pVideoFormatPropertyCount = 1; if (!pVideoFormatProperties) return VK_SUCCESS; VkImageUsageFlags usage_flags = pVideoFormatInfo->imageUsage; const VkImageUsageFlags dpb_output_coincide_usage = VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR | VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR; if ((usage_flags & dpb_output_coincide_usage) == VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR || (usage_flags & dpb_output_coincide_usage) == VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR) usage_flags |= dpb_output_coincide_usage; if (usage_flags & VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR) usage_flags |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; pVideoFormatProperties[0].sType = VK_STRUCTURE_TYPE_VIDEO_FORMAT_PROPERTIES_KHR; pVideoFormatProperties[0].pNext = NULL; pVideoFormatProperties[0].format = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM; pVideoFormatProperties[0].componentMapping.r = VK_COMPONENT_SWIZZLE_IDENTITY; pVideoFormatProperties[0].componentMapping.g = VK_COMPONENT_SWIZZLE_IDENTITY; pVideoFormatProperties[0].componentMapping.b = VK_COMPONENT_SWIZZLE_IDENTITY; pVideoFormatProperties[0].componentMapping.a = VK_COMPONENT_SWIZZLE_IDENTITY; pVideoFormatProperties[0].imageCreateFlags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT | VK_IMAGE_CREATE_EXTENDED_USAGE_BIT | VK_IMAGE_CREATE_ALIAS_BIT; pVideoFormatProperties[0].imageType = VK_IMAGE_TYPE_2D; pVideoFormatProperties[0].imageTiling = VK_IMAGE_TILING_OPTIMAL; pVideoFormatProperties[0].imageUsageFlags = usage_flags; return VK_SUCCESS; } static uint64_t get_h264_video_mem_size(struct anv_video_session *vid, uint32_t mem_idx) { uint32_t width_in_mb = align(vid->vk.max_coded.width, ANV_MB_WIDTH) / ANV_MB_WIDTH; uint32_t height_in_mb = align(vid->vk.max_coded.height, ANV_MB_HEIGHT) / ANV_MB_HEIGHT; switch (mem_idx) { case ANV_VID_MEM_H264_INTRA_ROW_STORE: /* intra row store is width in macroblocks * 64 */ return width_in_mb * 64; case ANV_VID_MEM_H264_DEBLOCK_FILTER_ROW_STORE: /* deblocking filter row store is width in macroblocks * 64 * 4 */ return width_in_mb * 64 * 4; case ANV_VID_MEM_H264_BSD_MPC_ROW_SCRATCH: /* bsd mpc row scratch is width in macroblocks * 64 * 2 */ return width_in_mb * 64 * 2; case ANV_VID_MEM_H264_MPR_ROW_SCRATCH: /* mpr row scratch is width in macroblocks * 64 * 2 */ return width_in_mb * 64 * 2; /* Encode scratch buffer sizes, matching gen6_mfc_init (VA-API). */ case ANV_VID_MEM_H264_ENC_MB_STATUS: return width_in_mb * height_in_mb * 16; case ANV_VID_MEM_H264_ENC_DIRECT_MV_TOP: case ANV_VID_MEM_H264_ENC_DIRECT_MV_BOTTOM: return 68ull * width_in_mb * height_in_mb; case ANV_VID_MEM_H264_ENC_INPUT_MV: /* 16 4x4 motion-vector pairs (32MV) per MB, 4 bytes each = 64 bytes. */ return 64ull * width_in_mb * height_in_mb; default: UNREACHABLE("unknown memory"); } } static void get_h264_video_session_mem_reqs(struct anv_video_session *vid, VkVideoSessionMemoryRequirementsKHR *mem_reqs, uint32_t *pVideoSessionMemoryRequirementsCount, uint32_t memory_types, uint32_t first_mem_idx, uint32_t count) { VK_OUTARRAY_MAKE_TYPED(VkVideoSessionMemoryRequirementsKHR, out, mem_reqs, pVideoSessionMemoryRequirementsCount); for (unsigned i = 0; i < count; i++) { uint32_t bind_index = first_mem_idx + i; uint64_t size = get_h264_video_mem_size(vid, bind_index); /* The row-store and direct-MV scratch buffers reach the hardware through * MFX_PIPE_BUF_ADDR_STATE / MFX_BSP_BUF_BASE_ADDR_STATE, whose address * fields are bits 31:6, so 64 bytes is enough. The encode input-MV buffer * instead feeds MFX_IND_OBJ_BASE_ADDR_STATE, which stores addresses in * bits 31:12; a base that is not 4KiB aligned would be truncated and the * MFC would fetch its per-MB motion vectors from the wrong address. */ uint64_t alignment = bind_index == ANV_VID_MEM_H264_ENC_INPUT_MV ? 4096 : 64; vk_outarray_append_typed(VkVideoSessionMemoryRequirementsKHR, &out, p) { p->memoryBindIndex = bind_index; p->memoryRequirements.size = size; p->memoryRequirements.alignment = alignment; p->memoryRequirements.memoryTypeBits = memory_types; } } } VkResult anv_GetVideoSessionMemoryRequirementsKHR(VkDevice _device, VkVideoSessionKHR videoSession, uint32_t *pVideoSessionMemoryRequirementsCount, VkVideoSessionMemoryRequirementsKHR *mem_reqs) { ANV_FROM_HANDLE(anv_device, device, _device); ANV_FROM_HANDLE(anv_video_session, vid, videoSession); uint32_t memory_types = (1ull << device->physical->memory.type_count) - 1; switch (vid->vk.op) { case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR: get_h264_video_session_mem_reqs(vid, mem_reqs, pVideoSessionMemoryRequirementsCount, memory_types, ANV_VID_MEM_H264_INTRA_ROW_STORE, ANV_VID_MEM_H264_MPR_ROW_SCRATCH - ANV_VID_MEM_H264_INTRA_ROW_STORE + 1); break; case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR: /* A single call over the whole contiguous enum range. * VK_OUTARRAY_MAKE_TYPED resets *count to 0 and writes from index 0 on * every call, so two separate calls would make the second clobber the * first, leaving some scratch buffers unbound. MPR_ROW_SCRATCH falls in * the range and is included even though encode does not use it (a small * unused allocation). */ get_h264_video_session_mem_reqs(vid, mem_reqs, pVideoSessionMemoryRequirementsCount, memory_types, ANV_VID_MEM_H264_INTRA_ROW_STORE, ANV_VID_MEM_H264_ENC_INPUT_MV - ANV_VID_MEM_H264_INTRA_ROW_STORE + 1); break; default: UNREACHABLE("unknown codec"); } return VK_SUCCESS; } VkResult anv_UpdateVideoSessionParametersKHR(VkDevice _device, VkVideoSessionParametersKHR _params, const VkVideoSessionParametersUpdateInfoKHR *pUpdateInfo) { ANV_FROM_HANDLE(anv_video_session_params, params, _params); return vk_video_session_parameters_update(¶ms->vk, pUpdateInfo); } /* Required entry point for VK_KHR_video_encode_queue. Emits the SPS/PPS RBSP * (start code and NAL header included) using the common * vk_video_encode_h264_sps/pps() helpers; the H.264-only reduction of the * mainline anv implementation. */ VkResult anv_GetEncodedVideoSessionParametersKHR(VkDevice _device, const VkVideoEncodeSessionParametersGetInfoKHR *pVideoSessionParametersInfo, VkVideoEncodeSessionParametersFeedbackInfoKHR *pFeedbackInfo, size_t *pDataSize, void *pData) { ANV_FROM_HANDLE(anv_video_session_params, params, pVideoSessionParametersInfo->videoSessionParameters); size_t total_size = 0; size_t size_limit = pData ? *pDataSize : 0; if (params->vk.op == VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR) { const struct VkVideoEncodeH264SessionParametersGetInfoKHR *h264_get_info = vk_find_struct_const(pVideoSessionParametersInfo->pNext, VIDEO_ENCODE_H264_SESSION_PARAMETERS_GET_INFO_KHR); size_t sps_size = 0, pps_size = 0; if (h264_get_info->writeStdSPS) { for (unsigned i = 0; i < params->vk.h264_enc.h264_sps_count; i++) if (params->vk.h264_enc.h264_sps[i].base.seq_parameter_set_id == h264_get_info->stdSPSId) vk_video_encode_h264_sps(¶ms->vk.h264_enc.h264_sps[i].base, size_limit, &sps_size, pData); } if (h264_get_info->writeStdPPS) { char *data_ptr = pData ? (char *)pData + sps_size : NULL; for (unsigned i = 0; i < params->vk.h264_enc.h264_pps_count; i++) if (params->vk.h264_enc.h264_pps[i].base.pic_parameter_set_id == h264_get_info->stdPPSId) vk_video_encode_h264_pps(¶ms->vk.h264_enc.h264_pps[i].base, params->vk.h264_enc.profile_idc == STD_VIDEO_H264_PROFILE_IDC_HIGH, size_limit, &pps_size, data_ptr); } total_size = sps_size + pps_size; } /* The size check is done here against *pDataSize (mainline anv pattern). */ if (pData && *pDataSize < total_size) { *pDataSize = 0; return VK_INCOMPLETE; } *pDataSize = total_size; return VK_SUCCESS; } /* Required entry point for VK_KHR_video_encode_queue; the H.264-only reduction * of the mainline anv implementation. The reported values are preferred * defaults and do not constrain what the application may request. */ VkResult anv_GetPhysicalDeviceVideoEncodeQualityLevelPropertiesKHR(VkPhysicalDevice physicalDevice, const VkPhysicalDeviceVideoEncodeQualityLevelInfoKHR *pQualityLevelInfo, VkVideoEncodeQualityLevelPropertiesKHR *pQualityLevelProperties) { const VkVideoProfileInfoKHR *profile = pQualityLevelInfo->pVideoProfile; pQualityLevelProperties->preferredRateControlMode = VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DEFAULT_KHR; switch (profile->videoCodecOperation) { case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR: { VkVideoEncodeH264QualityLevelPropertiesKHR *ext = vk_find_struct(pQualityLevelProperties->pNext, VIDEO_ENCODE_H264_QUALITY_LEVEL_PROPERTIES_KHR); if (ext) { ext->preferredIdrPeriod = 60; ext->preferredGopFrameCount = 60; ext->preferredConstantQp = (VkVideoEncodeH264QpKHR) { 26, 26, 26 }; ext->preferredMaxL0ReferenceCount = 0; /* no references */ ext->preferredMaxL1ReferenceCount = 0; ext->preferredStdEntropyCodingModeFlag = false; /* Baseline: CAVLC */ } break; } default: return VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR; } return VK_SUCCESS; } static void copy_bind(struct anv_vid_mem *dst, const VkBindVideoSessionMemoryInfoKHR *src) { dst->mem = anv_device_memory_from_handle(src->memory); dst->offset = src->memoryOffset; dst->size = src->memorySize; } VkResult anv_BindVideoSessionMemoryKHR(VkDevice _device, VkVideoSessionKHR videoSession, uint32_t bind_mem_count, const VkBindVideoSessionMemoryInfoKHR *bind_mem) { ANV_FROM_HANDLE(anv_video_session, vid, videoSession); switch (vid->vk.op) { case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR: /* Validate bind_mem_count and memoryBindIndex values to prevent * out-of-bounds access to vid->vid_mem array. */ for (unsigned i = 0; i < bind_mem_count; i++) { if (bind_mem[i].memoryBindIndex >= ANV_VID_MEM_H264_MAX) { continue; } copy_bind(&vid->vid_mem[bind_mem[i].memoryBindIndex], &bind_mem[i]); } break; default: UNREACHABLE("unknown codec!"); } return VK_SUCCESS; }