mirror of
https://github.com/elwrcl/intel-hard-graphics.git
synced 2026-10-31 10:58:03 +03:00
Only constant QP was offered. Add the two bitrate modes, with the quantizer derived once per picture from the requested bitrate and frame rate. There is no closed loop and there cannot be one here: a picture's size is known only after the GPU encodes it, and vkCmdEncodeVideoKHR merely records commands. The VA-API encoder for this hardware runs its bit rate control after each frame completes, which that API's synchronisation allows and this one does not, and gen7 has no macroblock-level rate control the hardware drives on its own -- the VA-API driver leaves those MFX_AVC_IMG_STATE fields zero on this generation. Vulkan defines rate control as best effort, and this is the effort. The quantizer comes from a measured curve rather than a guess: sweeping QP 14 to 50 over a 176x144 clip gives 361 bits per macroblock at QP 26 for an I picture and 166 for a P one, halving every 12.5 steps of the quantizer for I and every 10.3 for P. Measured against the target across a 15x range of bitrates, I pictures land between 0.82x and 1.30x, P pictures between 0.59x and 1.56x -- the spread on P is content dependent and nothing open loop can narrow it. A target below about 90 bits per macroblock cannot be met at all, since that is where the curve flattens. dEQP-VK.video.encode.h264 goes from 84 to 92 of 120 with no failures. The remaining 28 are quantization maps, which gen7 has no hardware path for, and B frames. Co-Authored-By: Claude Opus 5 <[email protected]>
583 lines
25 KiB
C
583 lines
25 KiB
C
/*
|
|
* 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 <stdlib.h>
|
|
#include <string.h>
|
|
#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;
|
|
}
|