/*
 * 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 "av1_tables.h"
#include "vp9_tables.h"
#include "vk_video/vulkan_video_codecs_common.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;

   vk_object_base_finish(&vid->vk.base);
   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);
   ANV_FROM_HANDLE(anv_video_session, vid, pCreateInfo->videoSession);
   ANV_FROM_HANDLE(anv_video_session_params, templ, pCreateInfo->videoSessionParametersTemplate);
   struct anv_video_session_params *params =
      vk_alloc2(&device->vk.alloc, pAllocator, sizeof(*params), 8, VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
   if (!params)
      return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);

   VkResult result = vk_video_session_parameters_init(&device->vk,
                                                      &params->vk,
                                                      &vid->vk,
                                                      templ ? &templ->vk : NULL,
                                                      pCreateInfo);
   if (result != VK_SUCCESS) {
      vk_free2(&device->vk.alloc, pAllocator, params);
      return result;
   }

   *pVideoSessionParameters = anv_video_session_params_to_handle(params);
   return VK_SUCCESS;
}

void
anv_DestroyVideoSessionParametersKHR(VkDevice _device,
                                      VkVideoSessionParametersKHR _params,
                                      const VkAllocationCallbacks *pAllocator)
{
   ANV_FROM_HANDLE(anv_device, device, _device);
   ANV_FROM_HANDLE(anv_video_session_params, params, _params);
   if (!_params)
      return;
   vk_video_session_parameters_finish(&device->vk, &params->vk);
   vk_free2(&device->vk.alloc, pAllocator, params);
}

VkResult
anv_GetPhysicalDeviceVideoCapabilitiesKHR(VkPhysicalDevice physicalDevice,
                                           const VkVideoProfileInfoKHR *pVideoProfile,
                                           VkVideoCapabilitiesKHR *pCapabilities)
{
   ANV_FROM_HANDLE(anv_physical_device, pdevice, physicalDevice);

   pCapabilities->minBitstreamBufferOffsetAlignment = 32;
   pCapabilities->minBitstreamBufferSizeAlignment = 1;
   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;

   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;

   /* H264 allows different luma and chroma bit depths */
   if (pVideoProfile->lumaBitDepth != pVideoProfile->chromaBitDepth)
      return VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR;

   if (pVideoProfile->chromaSubsampling != VK_VIDEO_CHROMA_SUBSAMPLING_420_BIT_KHR)
      return VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR;

   switch (pVideoProfile->videoCodecOperation) {
   case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR: {
      struct VkVideoDecodeH264CapabilitiesKHR *ext = (struct VkVideoDecodeH264CapabilitiesKHR *)
         vk_find_struct(pCapabilities->pNext, VIDEO_DECODE_H264_CAPABILITIES_KHR);

      if (pVideoProfile->lumaBitDepth != VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR)
         return VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR;

      pCapabilities->maxDpbSlots = ANV_VIDEO_H264_MAX_DPB_SLOTS;
      pCapabilities->maxActiveReferencePictures = ANV_VIDEO_H264_MAX_NUM_REF_FRAME;
      pCapabilities->pictureAccessGranularity.width = ANV_MB_WIDTH;
      pCapabilities->pictureAccessGranularity.height = ANV_MB_HEIGHT;
      pCapabilities->minCodedExtent.width = ANV_MB_WIDTH;
      pCapabilities->minCodedExtent.height = ANV_MB_HEIGHT;

      ext->fieldOffsetGranularity.x = 0;
      ext->fieldOffsetGranularity.y = 0;
      ext->maxLevelIdc = STD_VIDEO_H264_LEVEL_IDC_5_1;
      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;
      break;
   }
   case VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR: {

      const struct VkVideoDecodeAV1ProfileInfoKHR *av1_profile =
         vk_find_struct_const(pVideoProfile->pNext, VIDEO_DECODE_AV1_PROFILE_INFO_KHR);

      if (av1_profile->stdProfile != STD_VIDEO_AV1_PROFILE_MAIN)
         return VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR;

      if (pVideoProfile->lumaBitDepth != VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR &&
          pVideoProfile->lumaBitDepth != VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR)
         return VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR;

      struct VkVideoDecodeAV1CapabilitiesKHR *ext = (struct VkVideoDecodeAV1CapabilitiesKHR *)
         vk_find_struct(pCapabilities->pNext, VIDEO_DECODE_AV1_CAPABILITIES_KHR);

      ext->maxLevel = STD_VIDEO_AV1_LEVEL_6_0;

      pCapabilities->maxDpbSlots = STD_VIDEO_AV1_NUM_REF_FRAMES + 1;
      pCapabilities->maxActiveReferencePictures = STD_VIDEO_AV1_NUM_REF_FRAMES;
      dec_caps->flags |= VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR;

      strcpy(pCapabilities->stdHeaderVersion.extensionName, VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_EXTENSION_NAME);
      pCapabilities->stdHeaderVersion.specVersion = VK_STD_VULKAN_VIDEO_CODEC_AV1_DECODE_SPEC_VERSION;
      break;
   }
   case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR: {
      struct VkVideoDecodeH265CapabilitiesKHR *ext = (struct VkVideoDecodeH265CapabilitiesKHR *)
         vk_find_struct(pCapabilities->pNext, VIDEO_DECODE_H265_CAPABILITIES_KHR);

      const struct VkVideoDecodeH265ProfileInfoKHR *h265_profile =
         vk_find_struct_const(pVideoProfile->pNext,
                              VIDEO_DECODE_H265_PROFILE_INFO_KHR);

      /* No hardware supports the scc extension profile */
      if (h265_profile->stdProfileIdc != STD_VIDEO_H265_PROFILE_IDC_MAIN &&
          h265_profile->stdProfileIdc != STD_VIDEO_H265_PROFILE_IDC_MAIN_10 &&
          h265_profile->stdProfileIdc != STD_VIDEO_H265_PROFILE_IDC_MAIN_STILL_PICTURE &&
          h265_profile->stdProfileIdc != STD_VIDEO_H265_PROFILE_IDC_FORMAT_RANGE_EXTENSIONS)
         return VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR;

      /* Skylake only supports the main profile */
      if (h265_profile->stdProfileIdc != STD_VIDEO_H265_PROFILE_IDC_MAIN &&
          h265_profile->stdProfileIdc != STD_VIDEO_H265_PROFILE_IDC_MAIN_STILL_PICTURE &&
          pdevice->info.platform <= INTEL_PLATFORM_SKL)
         return VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR;

      /* Gfx10 and under don't support the range extension profile */
      if (h265_profile->stdProfileIdc != STD_VIDEO_H265_PROFILE_IDC_MAIN &&
          h265_profile->stdProfileIdc != STD_VIDEO_H265_PROFILE_IDC_MAIN_10 &&
          h265_profile->stdProfileIdc != STD_VIDEO_H265_PROFILE_IDC_MAIN_STILL_PICTURE &&
          pdevice->info.ver <= 10)
         return VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR;

      if (pVideoProfile->lumaBitDepth != VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR &&
          pVideoProfile->lumaBitDepth != VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR)
         return VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR;

      pCapabilities->pictureAccessGranularity.width = ANV_MAX_H265_CTB_SIZE;
      pCapabilities->pictureAccessGranularity.height = ANV_MAX_H265_CTB_SIZE;
      pCapabilities->minCodedExtent.width = ANV_MAX_H265_CTB_SIZE;
      pCapabilities->minCodedExtent.height = ANV_MAX_H265_CTB_SIZE;
      pCapabilities->maxDpbSlots = ANV_VIDEO_H265_MAX_NUM_REF_FRAME;
      pCapabilities->maxActiveReferencePictures = ANV_VIDEO_H265_HCP_NUM_REF_FRAME;

      ext->maxLevelIdc = STD_VIDEO_H265_LEVEL_IDC_6_2;

      strcpy(pCapabilities->stdHeaderVersion.extensionName, VK_STD_VULKAN_VIDEO_CODEC_H265_DECODE_EXTENSION_NAME);
      pCapabilities->stdHeaderVersion.specVersion = VK_STD_VULKAN_VIDEO_CODEC_H265_DECODE_SPEC_VERSION;
      break;
   }
   case VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR: {
      struct VkVideoDecodeVP9CapabilitiesKHR *ext = (struct VkVideoDecodeVP9CapabilitiesKHR *)
         vk_find_struct(pCapabilities->pNext, VIDEO_DECODE_VP9_CAPABILITIES_KHR);

      if (pVideoProfile->lumaBitDepth != VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR &&
          pVideoProfile->lumaBitDepth != VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR)
         return VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR;

      pCapabilities->maxDpbSlots = STD_VIDEO_VP9_NUM_REF_FRAMES + 4;
      pCapabilities->maxActiveReferencePictures = STD_VIDEO_VP9_REFS_PER_FRAME;
      pCapabilities->pictureAccessGranularity.width = 8;
      pCapabilities->pictureAccessGranularity.height = 8;
      pCapabilities->minCodedExtent.width = 8;
      pCapabilities->minCodedExtent.height = 8;

      ext->maxLevel = 4;

      strcpy(pCapabilities->stdHeaderVersion.extensionName, VK_STD_VULKAN_VIDEO_CODEC_VP9_DECODE_EXTENSION_NAME);
      pCapabilities->stdHeaderVersion.specVersion = VK_STD_VULKAN_VIDEO_CODEC_VP9_DECODE_SPEC_VERSION;
      break;
   }
   default:
      break;
   }

   struct VkVideoEncodeCapabilitiesKHR *enc_caps = (struct VkVideoEncodeCapabilitiesKHR *)
      vk_find_struct(pCapabilities->pNext, VIDEO_ENCODE_CAPABILITIES_KHR);

   if (enc_caps) {
      enc_caps->flags = 0;
      enc_caps->rateControlModes = VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DEFAULT_KHR |
                                   VK_VIDEO_ENCODE_RATE_CONTROL_MODE_DISABLED_BIT_KHR;
      enc_caps->maxRateControlLayers = 1;
      enc_caps->maxQualityLevels = 1;
      enc_caps->encodeInputPictureGranularity.width = 32;
      enc_caps->encodeInputPictureGranularity.height = 32;
      enc_caps->supportedEncodeFeedbackFlags =
         VK_VIDEO_ENCODE_FEEDBACK_BITSTREAM_BUFFER_OFFSET_BIT_KHR |
         VK_VIDEO_ENCODE_FEEDBACK_BITSTREAM_BYTES_WRITTEN_BIT_KHR;
   }

   switch (pVideoProfile->videoCodecOperation) {
   case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR: {
      struct VkVideoEncodeH264CapabilitiesKHR *ext = (struct VkVideoEncodeH264CapabilitiesKHR *)
         vk_find_struct(pCapabilities->pNext, VIDEO_ENCODE_H264_CAPABILITIES_KHR);

      if (ext) {
         ext->flags = VK_VIDEO_ENCODE_H264_CAPABILITY_HRD_COMPLIANCE_BIT_KHR |
                      VK_VIDEO_ENCODE_H264_CAPABILITY_PER_PICTURE_TYPE_MIN_MAX_QP_BIT_KHR;
         ext->maxLevelIdc = STD_VIDEO_H264_LEVEL_IDC_5_1;
         ext->maxSliceCount = 1;
         ext->maxPPictureL0ReferenceCount = 8;
         ext->maxBPictureL0ReferenceCount = 8;
         ext->maxL1ReferenceCount = 0;
         ext->maxTemporalLayerCount = 0;
         ext->expectDyadicTemporalLayerPattern = false;
         ext->prefersGopRemainingFrames = 0;
         ext->requiresGopRemainingFrames = 0;
         ext->minQp = 10;
         ext->maxQp = 51;
         ext->stdSyntaxFlags = VK_VIDEO_ENCODE_H264_STD_CONSTRAINED_INTRA_PRED_FLAG_SET_BIT_KHR |
                               VK_VIDEO_ENCODE_H264_STD_ENTROPY_CODING_MODE_FLAG_UNSET_BIT_KHR |
                               VK_VIDEO_ENCODE_H264_STD_ENTROPY_CODING_MODE_FLAG_SET_BIT_KHR |
                               VK_VIDEO_ENCODE_H264_STD_DEBLOCKING_FILTER_DISABLED_BIT_KHR |
                               VK_VIDEO_ENCODE_H264_STD_DEBLOCKING_FILTER_ENABLED_BIT_KHR |
                               VK_VIDEO_ENCODE_H264_STD_DEBLOCKING_FILTER_PARTIAL_BIT_KHR |
                               VK_VIDEO_ENCODE_H264_STD_TRANSFORM_8X8_MODE_FLAG_SET_BIT_KHR |
                               VK_VIDEO_ENCODE_H264_STD_CHROMA_QP_INDEX_OFFSET_BIT_KHR |
                               VK_VIDEO_ENCODE_H264_STD_SECOND_CHROMA_QP_INDEX_OFFSET_BIT_KHR;
      }


      pCapabilities->minBitstreamBufferOffsetAlignment = 32;
      pCapabilities->minBitstreamBufferSizeAlignment = 4096;

      pCapabilities->maxDpbSlots = ANV_VIDEO_H264_MAX_NUM_REF_FRAME;
      pCapabilities->maxActiveReferencePictures = ANV_VIDEO_H264_MAX_NUM_REF_FRAME;
      pCapabilities->pictureAccessGranularity.width = ANV_MB_WIDTH;
      pCapabilities->pictureAccessGranularity.height = ANV_MB_HEIGHT;
      pCapabilities->minCodedExtent.width = ANV_MB_WIDTH;
      pCapabilities->minCodedExtent.height = ANV_MB_HEIGHT;

      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;
      break;
   }
   case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR: {
      struct VkVideoEncodeH265CapabilitiesKHR *ext = (struct VkVideoEncodeH265CapabilitiesKHR *)
         vk_find_struct(pCapabilities->pNext, VIDEO_ENCODE_H265_CAPABILITIES_KHR);

      if (ext) {
         ext->flags = VK_VIDEO_ENCODE_H265_CAPABILITY_PER_PICTURE_TYPE_MIN_MAX_QP_BIT_KHR;
         ext->maxLevelIdc = STD_VIDEO_H265_LEVEL_IDC_5_1;
         ext->ctbSizes = VK_VIDEO_ENCODE_H265_CTB_SIZE_64_BIT_KHR;
         ext->transformBlockSizes = VK_VIDEO_ENCODE_H265_TRANSFORM_BLOCK_SIZE_4_BIT_KHR |
                                    VK_VIDEO_ENCODE_H265_TRANSFORM_BLOCK_SIZE_8_BIT_KHR |
                                    VK_VIDEO_ENCODE_H265_TRANSFORM_BLOCK_SIZE_16_BIT_KHR |
                                    VK_VIDEO_ENCODE_H265_TRANSFORM_BLOCK_SIZE_32_BIT_KHR;
         ext->maxPPictureL0ReferenceCount = 8;
         ext->maxBPictureL0ReferenceCount = 8;
         ext->maxL1ReferenceCount = 1;
         ext->minQp = 10;
         ext->maxQp = 51;
         ext->maxSliceSegmentCount = 128;
         ext->maxTiles.width = 1;
         ext->maxTiles.height = 1;
         ext->maxSubLayerCount = 1;
         ext->expectDyadicTemporalSubLayerPattern = false;
         ext->prefersGopRemainingFrames = 0;
         ext->requiresGopRemainingFrames = 0;
         ext->stdSyntaxFlags = VK_VIDEO_ENCODE_H265_STD_SAMPLE_ADAPTIVE_OFFSET_ENABLED_FLAG_SET_BIT_KHR |
                               VK_VIDEO_ENCODE_H265_STD_PCM_ENABLED_FLAG_SET_BIT_KHR |
                               VK_VIDEO_ENCODE_H265_STD_TRANSFORM_SKIP_ENABLED_FLAG_SET_BIT_KHR |
                               VK_VIDEO_ENCODE_H265_STD_CONSTRAINED_INTRA_PRED_FLAG_SET_BIT_KHR;
      }

      pCapabilities->minBitstreamBufferOffsetAlignment = 4096;
      pCapabilities->minBitstreamBufferSizeAlignment = 4096;

      pCapabilities->maxDpbSlots = ANV_VIDEO_H265_MAX_NUM_REF_FRAME;
      pCapabilities->maxActiveReferencePictures = ANV_VIDEO_H265_MAX_NUM_REF_FRAME;
      pCapabilities->pictureAccessGranularity.width = ANV_MAX_H265_CTB_SIZE;
      pCapabilities->pictureAccessGranularity.height = ANV_MAX_H265_CTB_SIZE;
      pCapabilities->minCodedExtent.width = ANV_MAX_H265_CTB_SIZE;
      pCapabilities->minCodedExtent.height = ANV_MAX_H265_CTB_SIZE;

      strcpy(pCapabilities->stdHeaderVersion.extensionName, VK_STD_VULKAN_VIDEO_CODEC_H265_ENCODE_EXTENSION_NAME);
      pCapabilities->stdHeaderVersion.specVersion = VK_STD_VULKAN_VIDEO_CODEC_H265_ENCODE_SPEC_VERSION;
      break;
   }
   default:
      break;
   }

   return VK_SUCCESS;
}

VkResult
anv_GetPhysicalDeviceVideoFormatPropertiesKHR(VkPhysicalDevice physicalDevice,
                                               const VkPhysicalDeviceVideoFormatInfoKHR *pVideoFormatInfo,
                                               uint32_t *pVideoFormatPropertyCount,
                                               VkVideoFormatPropertiesKHR *pVideoFormatProperties)
{
   VK_OUTARRAY_MAKE_TYPED(VkVideoFormatPropertiesKHR, out,
                          pVideoFormatProperties,
                          pVideoFormatPropertyCount);

   const struct VkVideoProfileListInfoKHR *prof_list = (struct VkVideoProfileListInfoKHR *)
      vk_find_struct_const(pVideoFormatInfo->pNext, VIDEO_PROFILE_LIST_INFO_KHR);

   /* We only support VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT with
    * Y-tiling/Tile4, as supported by the hardware for video decoding.
    * However, we are unable to determine the tiling without modifiers here.
    * So just disable them all.
    */
   const bool decode_dst = !!(pVideoFormatInfo->imageUsage &
                              VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR);

   if (prof_list) {
      for (unsigned i = 0; i < prof_list->profileCount; i++) {
         const VkVideoProfileInfoKHR *profile = &prof_list->pProfiles[i];

         if (profile->lumaBitDepth & VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR ||
             profile->chromaBitDepth & VK_VIDEO_COMPONENT_BIT_DEPTH_8_BIT_KHR) {
            vk_outarray_append_typed(VkVideoFormatPropertiesKHR, &out, p) {
               p->format = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
               p->imageCreateFlags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
               p->imageType = VK_IMAGE_TYPE_2D;
               p->imageTiling = VK_IMAGE_TILING_OPTIMAL;
               p->imageUsageFlags = pVideoFormatInfo->imageUsage;
            }

            if (!decode_dst) {
               vk_outarray_append_typed(VkVideoFormatPropertiesKHR, &out, p) {
                  p->format = VK_FORMAT_G8_B8R8_2PLANE_420_UNORM;
                  p->imageCreateFlags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
                  p->imageType = VK_IMAGE_TYPE_2D;
                  p->imageTiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
                  p->imageUsageFlags = pVideoFormatInfo->imageUsage;
               }
            }
         }

         if (profile->lumaBitDepth & VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR ||
             profile->chromaBitDepth & VK_VIDEO_COMPONENT_BIT_DEPTH_10_BIT_KHR) {
            vk_outarray_append_typed(VkVideoFormatPropertiesKHR, &out, p) {
               p->format = VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16;
               p->imageCreateFlags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
               p->imageType = VK_IMAGE_TYPE_2D;
               p->imageTiling = VK_IMAGE_TILING_OPTIMAL;
               p->imageUsageFlags = pVideoFormatInfo->imageUsage;
            }
            if (!decode_dst) {
               vk_outarray_append_typed(VkVideoFormatPropertiesKHR, &out, p) {
                  p->format = VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16;
                  p->imageCreateFlags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
                  p->imageType = VK_IMAGE_TYPE_2D;
                  p->imageTiling = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
                  p->imageUsageFlags = pVideoFormatInfo->imageUsage;
               }
            }
         }
      }
   }

   if (*pVideoFormatPropertyCount == 0)
      return VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR;

   return vk_outarray_status(&out);
}

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;

   switch (mem_idx) {
   case ANV_VID_MEM_H264_INTRA_ROW_STORE:
      return width_in_mb * 64;
   case ANV_VID_MEM_H264_DEBLOCK_FILTER_ROW_STORE:
      return width_in_mb * 64 * 4;
   case ANV_VID_MEM_H264_BSD_MPC_ROW_SCRATCH:
      return width_in_mb * 64 * 2;
   case ANV_VID_MEM_H264_MPR_ROW_SCRATCH:
      return width_in_mb * 64 * 2;
   default:
      UNREACHABLE("unknown memory");
   }
}

static uint64_t
get_h265_video_mem_size(struct anv_video_session *vid, uint32_t mem_idx)
{
   uint32_t bit_shift =
      vid->vk.h265.profile_idc == STD_VIDEO_H265_PROFILE_IDC_MAIN_10 ? 2 : 3;

   /* TODO. these sizes can be determined dynamically depending on ctb sizes of each slice. */
   uint32_t width_in_ctb =
      align(vid->vk.max_coded.width, ANV_MAX_H265_CTB_SIZE) / ANV_MAX_H265_CTB_SIZE;
   uint32_t height_in_ctb =
      align(vid->vk.max_coded.height, ANV_MAX_H265_CTB_SIZE) / ANV_MAX_H265_CTB_SIZE;
   uint64_t size;

   switch (mem_idx) {
   case ANV_VID_MEM_H265_DEBLOCK_FILTER_ROW_STORE_LINE:
   case ANV_VID_MEM_H265_DEBLOCK_FILTER_ROW_STORE_TILE_LINE:
      size = align(vid->vk.max_coded.width, 32) >> bit_shift;
      break;
   case ANV_VID_MEM_H265_DEBLOCK_FILTER_ROW_STORE_TILE_COLUMN:
      size = align(vid->vk.max_coded.height + 6 * height_in_ctb, 32) >> bit_shift;
      break;
   case ANV_VID_MEM_H265_METADATA_LINE:
      size = (((vid->vk.max_coded.width + 15) >> 4) * 188 + width_in_ctb * 9 + 1023) >> 9;
      break;
   case ANV_VID_MEM_H265_METADATA_TILE_LINE:
      size = (((vid->vk.max_coded.width + 15) >> 4) * 172 + width_in_ctb * 9 + 1023) >> 9;
      break;
   case ANV_VID_MEM_H265_METADATA_TILE_COLUMN:
      size = (((vid->vk.max_coded.height + 15) >> 4) * 176 + height_in_ctb * 89 + 1023) >> 9;
      break;
   case ANV_VID_MEM_H265_SAO_LINE:
      size = align((vid->vk.max_coded.width >> 1) + width_in_ctb * 3, 16) >> bit_shift;
      break;
   case ANV_VID_MEM_H265_SAO_TILE_LINE:
      size = align((vid->vk.max_coded.width >> 1) + width_in_ctb * 6, 16) >> bit_shift;
      break;
   case ANV_VID_MEM_H265_SAO_TILE_COLUMN:
      size = align((vid->vk.max_coded.height >> 1) + height_in_ctb * 6, 16) >> bit_shift;
      break;
   case ANV_VID_MEM_H265_SSE_SRC_PIX_ROW_STORE: {
      /* Take the formula from media-driver */
#define CACHELINE_SIZE 64
#define HEVC_MIN_TILE_SIZE 128
      uint32_t max_tile_cols = DIV_ROUND_UP(vid->vk.max_coded.width, HEVC_MIN_TILE_SIZE);
      size = 2 * ((CACHELINE_SIZE * (4 + 4)) << 1) * (width_in_ctb + 3 * max_tile_cols);
      return size;
   }
   default:
      UNREACHABLE("unknown memory");
   }

   return size << 6;
}

static uint64_t
get_vp9_video_mem_size(struct anv_video_session *vid, uint32_t mem_idx)
{
   uint32_t width_in_ctb =
      DIV_ROUND_UP(vid->vk.max_coded.width, ANV_MAX_VP9_CTB_SIZE);
   uint32_t height_in_ctb =
      DIV_ROUND_UP(vid->vk.max_coded.height, ANV_MAX_VP9_CTB_SIZE);
   uint64_t size;

   switch (mem_idx) {
   case ANV_VID_MEM_VP9_DEBLOCK_FILTER_ROW_STORE_LINE:
   case ANV_VID_MEM_VP9_DEBLOCK_FILTER_ROW_STORE_TILE_LINE:
      /* if profile <= 1: multiply 18, if profile > 1: multiply 36
       * But we don't know the profile here, so use 36.
       */
      size = width_in_ctb * 36;
      break;
   case ANV_VID_MEM_VP9_DEBLOCK_FILTER_ROW_STORE_TILE_COLUMN:
      size = height_in_ctb * 34;
      break;
   case ANV_VID_MEM_VP9_METADATA_LINE:
   case ANV_VID_MEM_VP9_METADATA_TILE_LINE:
      size = width_in_ctb * 5;
      break;
   case ANV_VID_MEM_VP9_METADATA_TILE_COLUMN:
      size = height_in_ctb * 5;
      break;
   case ANV_VID_MEM_VP9_PROBABILITY_0:
   case ANV_VID_MEM_VP9_PROBABILITY_1:
   case ANV_VID_MEM_VP9_PROBABILITY_2:
   case ANV_VID_MEM_VP9_PROBABILITY_3:
      size = 32;
      break;
   case ANV_VID_MEM_VP9_SEGMENT_ID:
      size = width_in_ctb * height_in_ctb;
      break;
   case ANV_VID_MEM_VP9_HVD_LINE_ROW_STORE:
   case ANV_VID_MEM_VP9_HVD_TILE_ROW_STORE:
      size = width_in_ctb;
      break;
   case ANV_VID_MEM_VP9_MV_1:
   case ANV_VID_MEM_VP9_MV_2:
      size = (width_in_ctb * height_in_ctb * 9);
      break;
   default:
      UNREACHABLE("unknown memory");
   }

   return size << 6;
}

static void
get_h264_video_session_mem_reqs(struct anv_video_session *vid,
                                VkVideoSessionMemoryRequirementsKHR *mem_reqs,
                                uint32_t *pVideoSessionMemoryRequirementsCount,
                                uint32_t memory_types)
{
   VK_OUTARRAY_MAKE_TYPED(VkVideoSessionMemoryRequirementsKHR,
                          out,
                          mem_reqs,
                          pVideoSessionMemoryRequirementsCount);

   for (unsigned i = 0; i < ANV_VID_MEM_H264_MAX; i++) {
      uint32_t bind_index = ANV_VID_MEM_H264_INTRA_ROW_STORE + i;
      uint64_t size = get_h264_video_mem_size(vid, i);

      vk_outarray_append_typed(VkVideoSessionMemoryRequirementsKHR, &out, p) {
         p->memoryBindIndex = bind_index;
         p->memoryRequirements.size = size;
         p->memoryRequirements.alignment = 4096;
         p->memoryRequirements.memoryTypeBits = memory_types;
      }
   }
}

static void
get_h265_video_session_mem_reqs(struct anv_video_session *vid,
                                VkVideoSessionMemoryRequirementsKHR *mem_reqs,
                                uint32_t *pVideoSessionMemoryRequirementsCount,
                                uint32_t memory_types)
{
   VK_OUTARRAY_MAKE_TYPED(VkVideoSessionMemoryRequirementsKHR,
                          out,
                          mem_reqs,
                          pVideoSessionMemoryRequirementsCount);

   uint32_t mem_cnt = (vid->vk.op & VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR) ?
                       ANV_VID_MEM_H265_DEC_MAX : ANV_VID_MEM_H265_ENC_MAX;

   for (unsigned i = 0; i < mem_cnt; i++) {
      uint32_t bind_index =
         ANV_VID_MEM_H265_DEBLOCK_FILTER_ROW_STORE_LINE + i;
      uint64_t size = get_h265_video_mem_size(vid, i);

      vk_outarray_append_typed(VkVideoSessionMemoryRequirementsKHR, &out, p) {
         p->memoryBindIndex = bind_index;
         p->memoryRequirements.size = size;
         p->memoryRequirements.alignment = 4096;
         p->memoryRequirements.memoryTypeBits = memory_types;
      }
   }
}

static void
get_vp9_video_session_mem_reqs(struct anv_video_session *vid,
                                VkVideoSessionMemoryRequirementsKHR *mem_reqs,
                                uint32_t *pVideoSessionMemoryRequirementsCount,
                                uint32_t memory_types)
{
   VK_OUTARRAY_MAKE_TYPED(VkVideoSessionMemoryRequirementsKHR,
                          out,
                          mem_reqs,
                          pVideoSessionMemoryRequirementsCount);

   for (unsigned i = 0; i < ANV_VID_MEM_VP9_DEC_MAX; i++) {
      uint32_t bind_index =
         ANV_VID_MEM_VP9_DEBLOCK_FILTER_ROW_STORE_LINE + i;
      uint64_t size = get_vp9_video_mem_size(vid, i);

      vk_outarray_append_typed(VkVideoSessionMemoryRequirementsKHR, &out, p) {
         p->memoryBindIndex = bind_index;
         p->memoryRequirements.size = size;
         p->memoryRequirements.alignment = 4096;
         p->memoryRequirements.memoryTypeBits = memory_types;
      }
   }
}

static const uint8_t av1_buffer_size[ANV_VID_MEM_AV1_MAX][4] = {
   { 2 ,   4   ,   2   ,    4 },  /* bsdLineBuf, */
   { 2 ,   4   ,   2   ,    4 },  /* bsdTileLineBuf, */
   { 2 ,   4   ,   4   ,    8 },  /* intraPredLine, */
   { 2 ,   4   ,   4   ,    8 },  /* intraPredTileLine, */
   { 4 ,   8   ,   4   ,    8 },  /* spatialMvLineBuf, */
   { 4 ,   8   ,   4   ,    8 },  /* spatialMvTileLineBuf, */
   { 1 ,   1   ,   1   ,    1 },  /* lrMetaTileCol, */
   { 7 ,   7   ,   7   ,    7 },  /* lrTileLineY, */
   { 5 ,   5   ,   5   ,    5 },  /* lrTileLineU, */
   { 5 ,   5   ,   5   ,    5 },  /* lrTileLineV, */
   { 9 ,   17  ,   11  ,    21 }, /* deblockLineYBuf, */
   { 3 ,   4   ,   3   ,    5 },  /* deblockLineUBuf, */
   { 3 ,   4   ,   3   ,    5 },  /* deblockLineVBuf, */
   { 9 ,   17  ,   11  ,    21 }, /* deblockTileLineYBuf, */
   { 3 ,   4   ,   3   ,    5 },  /* deblockTileLineVBuf, */
   { 3 ,   4   ,   3   ,    5 },  /* deblockTileLineUBuf, */
   { 8 ,   16  ,   10  ,    20 }, /* deblockTileColYBuf, */
   { 2 ,   4   ,   3   ,    5 },  /* deblockTileColUBuf, */
   { 2 ,   4   ,   3   ,    5 },  /* deblockTileColVBuf, */
   { 8 ,   16  ,   10  ,    20 }, /* cdefLineBuf, */
   { 8 ,   16  ,   10  ,    20 }, /* cdefTileLineBuf, */
   { 8 ,   16  ,   10  ,    20 }, /* cdefTileColBuf, */
   { 1 ,   1   ,   1   ,    1 },  /* cdefMetaTileLine, */
   { 1 ,   1   ,   1   ,    1 },  /* cdefMetaTileCol, */
   { 1 ,   1   ,   1   ,    1 },  /* cdefTopLeftCornerBuf, */
   { 22,   44  ,   29  ,    58 }, /* superResTileColYBuf, */
   { 8 ,   16  ,   10  ,    20 }, /* superResTileColUBuf, */
   { 8 ,   16  ,   10  ,    20 }, /* superResTileColVBuf, */
   { 9 ,   17  ,   11  ,    22 }, /* lrTileColYBuf, */
   { 5 ,   9   ,   6   ,    12 }, /* lrTileColUBuf, */
   { 5 ,   9   ,   6   ,    12 }, /* lrTileColVBuf, */
   { 4,    8   ,   5   ,    10 }, /* lrTileColAlignBuffer, */
};

static const uint8_t av1_buffer_size_ext[ANV_VID_MEM_AV1_MAX][4] = {
   { 0 ,    0    ,    0    ,    0 },  /* bsdLineBuf, */
   { 0 ,    0    ,    0    ,    0 },  /* bsdTileLineBuf, */
   { 0 ,    0    ,    0    ,    0 },  /* intraPredLine, */
   { 0 ,    0    ,    0    ,    0 },  /* intraPredTileLine, */
   { 0 ,    0    ,    0    ,    0 },  /* spatialMvLineBuf, */
   { 0 ,    0    ,    0    ,    0 },  /* spatialMvTileLineBuf, */
   { 1 ,    1    ,    1    ,    1 },  /* lrMetaTileCol, */
   { 0 ,    0    ,    0    ,    0 },  /* lrTileLineY, */
   { 0 ,    0    ,    0    ,    0 },  /* lrTileLineU, */
   { 0 ,    0    ,    0    ,    0 },  /* lrTileLineV, */
   { 0 ,    0    ,    0    ,    0 },  /* deblockLineYBuf, */
   { 0 ,    0    ,    0    ,    0 },  /* deblockLineUBuf, */
   { 0 ,    0    ,    0    ,    0 },  /* deblockLineVBuf, */
   { 0 ,    0    ,    0    ,    0 },  /* deblockTileLineYBuf, */
   { 0 ,    0    ,    0    ,    0 },  /* deblockTileLineVBuf, */
   { 0 ,    0    ,    0    ,    0 },  /* deblockTileLineUBuf, */
   { 0 ,    0    ,    0    ,    0 },  /* deblockTileColYBuf, */
   { 0 ,    0    ,    0    ,    0 },  /* deblockTileColUBuf, */
   { 0 ,    0    ,    0    ,    0 },  /* deblockTileColVBuf, */
   { 1 ,    1    ,    2    ,    2 },  /* cdefLineBuf, */
   { 1 ,    1    ,    2    ,    2 },  /* cdefTileLineBuf, */
   { 1 ,    1    ,    2    ,    2 },  /* cdefTileColBuf, */
   { 0 ,    0    ,    0    ,    0 },  /* cdefMetaTileLine, */
   { 1 ,    1    ,    1    ,    1 },  /* cdefMetaTileCol, */
   { 0 ,    0    ,    0    ,    0 },  /* cdefTopLeftCornerBuf, */
   { 22,    44   ,    29   ,    58 }, /* superResTileColYBuf, */
   { 8 ,    16   ,    10   ,    20 }, /* superResTileColUBuf, */
   { 8 ,    16   ,    10   ,    20 }, /* superResTileColVBuf, */
   { 2 ,    2    ,    2    ,    2 },  /* lrTileColYBuf, */
   { 1 ,    1    ,    1    ,    1 },  /* lrTileColUBuf, */
   { 1 ,    1    ,    1    ,    1 },  /* lrTileColVBuf, */
   { 1,     1    ,    1    ,    1 },  /* lrTileColAlignBuffer, */
};

const uint32_t av1_mi_size_log2         = 2;
const uint32_t av1_max_mib_size_log2    = 5;

static void
get_av1_sb_size(uint32_t *w_in_sb, uint32_t *h_in_sb)
{
   const uint32_t width = 4096;
   const uint32_t height = 4096;

   uint32_t mi_cols = width  >> av1_mi_size_log2;
   uint32_t mi_rows = height >> av1_mi_size_log2;

   uint32_t width_in_sb = align(mi_cols, (1 << av1_mi_size_log2)) >> av1_mi_size_log2;
   uint32_t height_in_sb = align(mi_rows, (1 << av1_mi_size_log2)) >> av1_mi_size_log2;

   *w_in_sb = width_in_sb;
   *h_in_sb = height_in_sb;

   return;
}

static void
get_av1_video_session_mem_reqs(struct anv_video_session *vid,
                               VkVideoSessionMemoryRequirementsKHR *mem_reqs,
                               uint32_t *pVideoSessionMemoryRequirementsCount,
                               uint32_t memory_types)
{
   VK_OUTARRAY_MAKE_TYPED(VkVideoSessionMemoryRequirementsKHR,
                          out,
                          mem_reqs,
                          pVideoSessionMemoryRequirementsCount);

   uint32_t width_in_sb, height_in_sb;
   get_av1_sb_size(&width_in_sb, &height_in_sb);

   uint32_t max_tile_width_sb = DIV_ROUND_UP(4096, 1 << (av1_max_mib_size_log2 + av1_mi_size_log2));
   uint32_t max_tile_cols = 16; /* TODO. get the profile to work this out */

   /* Assume 8-bit 128x128 sb is true, can't know at this point */
   int buf_size_idx = 1;

   for (enum anv_vid_mem_av1_types mem = ANV_VID_MEM_AV1_BITSTREAM_LINE_ROWSTORE;
        mem < ANV_VID_MEM_AV1_MAX; mem++) {
      VkDeviceSize buffer_size = 0;

      switch (mem) {
      case ANV_VID_MEM_AV1_BITSTREAM_LINE_ROWSTORE:
      case ANV_VID_MEM_AV1_INTRA_PREDICTION_LINE_ROWSTORE:
      case ANV_VID_MEM_AV1_SPATIAL_MOTION_VECTOR_LINE:
      case ANV_VID_MEM_AV1_DEBLOCKER_FILTER_LINE_Y:
      case ANV_VID_MEM_AV1_DEBLOCKER_FILTER_LINE_U:
      case ANV_VID_MEM_AV1_DEBLOCKER_FILTER_LINE_V:
         buffer_size = max_tile_width_sb * av1_buffer_size[mem][buf_size_idx];
         break;
      case ANV_VID_MEM_AV1_CDEF_FILTER_LINE:
         buffer_size = max_tile_width_sb * av1_buffer_size[mem][buf_size_idx] +
            av1_buffer_size_ext[mem][buf_size_idx];
         break;
      case ANV_VID_MEM_AV1_BITSTREAM_TILE_LINE_ROWSTORE:
      case ANV_VID_MEM_AV1_SPATIAL_MOTION_VECTOR_TILE_LINE:
      case ANV_VID_MEM_AV1_INTRA_PREDICTION_TILE_LINE_ROWSTORE:
      case ANV_VID_MEM_AV1_DEBLOCKER_FILTER_TILE_LINE_Y:
      case ANV_VID_MEM_AV1_DEBLOCKER_FILTER_TILE_LINE_U:
      case ANV_VID_MEM_AV1_DEBLOCKER_FILTER_TILE_LINE_V:
         buffer_size = width_in_sb * av1_buffer_size[mem][buf_size_idx];
         break;

      case ANV_VID_MEM_AV1_LOOP_RESTORATION_FILTER_TILE_LINE_Y:
         buffer_size = max_tile_cols * 7;
         break;
      case ANV_VID_MEM_AV1_LOOP_RESTORATION_FILTER_TILE_LINE_U:
      case ANV_VID_MEM_AV1_LOOP_RESTORATION_FILTER_TILE_LINE_V:
         buffer_size = max_tile_cols * 5;
         break;

      case ANV_VID_MEM_AV1_DEBLOCKER_FILTER_TILE_COLUMN_Y:
      case ANV_VID_MEM_AV1_DEBLOCKER_FILTER_TILE_COLUMN_U:
      case ANV_VID_MEM_AV1_DEBLOCKER_FILTER_TILE_COLUMN_V:
         buffer_size = height_in_sb * av1_buffer_size[mem][buf_size_idx];
         break;
      case ANV_VID_MEM_AV1_CDEF_FILTER_TILE_LINE:
         buffer_size = width_in_sb * av1_buffer_size[mem][buf_size_idx] +
            av1_buffer_size_ext[mem][buf_size_idx];
         break;
      case ANV_VID_MEM_AV1_CDEF_FILTER_META_TILE_LINE:
         buffer_size = max_tile_cols;
         break;
      case ANV_VID_MEM_AV1_CDEF_FILTER_TOP_LEFT_CORNER:
         buffer_size = max_tile_cols * 8; /* TODO. take from profile */
         break;
      case ANV_VID_MEM_AV1_CDEF_FILTER_TILE_COLUMN:
      case ANV_VID_MEM_AV1_CDEF_FILTER_META_TILE_COLUMN:
      case ANV_VID_MEM_AV1_SUPER_RES_TILE_COLUMN_Y:
      case ANV_VID_MEM_AV1_SUPER_RES_TILE_COLUMN_U:
      case ANV_VID_MEM_AV1_SUPER_RES_TILE_COLUMN_V:
      case ANV_VID_MEM_AV1_LOOP_RESTORATION_FILTER_TILE_COLUMN_Y:
      case ANV_VID_MEM_AV1_LOOP_RESTORATION_FILTER_TILE_COLUMN_U:
      case ANV_VID_MEM_AV1_LOOP_RESTORATION_FILTER_TILE_COLUMN_V:
      case ANV_VID_MEM_AV1_LOOP_RESTORATION_FILTER_TILE_COLUMN_ALIGNMENT_RW:
      case ANV_VID_MEM_AV1_LOOP_RESTORATION_META_TILE_COLUMN:
         buffer_size = height_in_sb * av1_buffer_size[mem][buf_size_idx] +
            av1_buffer_size_ext[mem][buf_size_idx];
         break;
      case ANV_VID_MEM_AV1_CDF_DEFAULTS_0:
      case ANV_VID_MEM_AV1_CDF_DEFAULTS_1:
      case ANV_VID_MEM_AV1_CDF_DEFAULTS_2:
      case ANV_VID_MEM_AV1_CDF_DEFAULTS_3:
         buffer_size = av1_cdf_max_num_bytes;
         break;
      case ANV_VID_MEM_AV1_DBD_BUFFER:
         buffer_size = 1;
         break;
      default:
         assert(0);
         break;
      }

      switch (mem) {
      case ANV_VID_MEM_AV1_CDF_DEFAULTS_0:
      case ANV_VID_MEM_AV1_CDF_DEFAULTS_1:
      case ANV_VID_MEM_AV1_CDF_DEFAULTS_2:
      case ANV_VID_MEM_AV1_CDF_DEFAULTS_3:
         break;
      default:
         buffer_size *= 64;
         break;
      }
      vk_outarray_append_typed(VkVideoSessionMemoryRequirementsKHR, &out, p) {
         p->memoryBindIndex = mem;
         p->memoryRequirements.size = buffer_size;
         p->memoryRequirements.alignment = 4096;
         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 =
      (vid->vk.flags & VK_VIDEO_SESSION_CREATE_PROTECTED_CONTENT_BIT_KHR) ?
      device->physical->memory.protected_mem_types :
      device->physical->memory.default_buffer_mem_types;

   switch (vid->vk.op) {
   case VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR:
   case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
      get_h264_video_session_mem_reqs(vid,
                                      mem_reqs,
                                      pVideoSessionMemoryRequirementsCount,
                                      memory_types);
      break;
   case VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR:
   case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
      get_h265_video_session_mem_reqs(vid,
                                      mem_reqs,
                                      pVideoSessionMemoryRequirementsCount,
                                      memory_types);
      break;
   case VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR:
      get_av1_video_session_mem_reqs(vid,
                                     mem_reqs,
                                     pVideoSessionMemoryRequirementsCount,
                                     memory_types);
      break;
   case VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR:
      get_vp9_video_session_mem_reqs(vid,
                                     mem_reqs,
                                     pVideoSessionMemoryRequirementsCount,
                                     memory_types);
      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(&params->vk, pUpdateInfo);
}

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:
   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:
      for (unsigned i = 0; i < bind_mem_count; i++) {
         copy_bind(&vid->vid_mem[bind_mem[i].memoryBindIndex], &bind_mem[i]);
      }
      break;
   case VK_VIDEO_CODEC_OPERATION_ENCODE_H264_BIT_KHR:
   case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR:
      for (unsigned i = 0; i < bind_mem_count; i++) {
         copy_bind(&vid->vid_mem[bind_mem[i].memoryBindIndex], &bind_mem[i]);
      }
      break;
   default:
      UNREACHABLE("unknown codec");
   }
   return VK_SUCCESS;
}

VKAPI_ATTR VkResult VKAPI_CALL
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 = 0;

   if (pData)
      size_limit = *pDataSize;

   switch (params->vk.op) {
   case 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(&params->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(&params->vk.h264_enc.h264_pps[i].base, false, size_limit, &pps_size, data_ptr);
            }
      }
      total_size = sps_size + pps_size;
      break;
   }
   case VK_VIDEO_CODEC_OPERATION_ENCODE_H265_BIT_KHR: {
      const struct VkVideoEncodeH265SessionParametersGetInfoKHR *h265_get_info =
         vk_find_struct_const(pVideoSessionParametersInfo->pNext, VIDEO_ENCODE_H265_SESSION_PARAMETERS_GET_INFO_KHR);
      size_t sps_size = 0, pps_size = 0, vps_size = 0;
      if (h265_get_info->writeStdVPS) {
         for (unsigned i = 0; i < params->vk.h265_enc.h265_vps_count; i++)
            if (params->vk.h265_enc.h265_vps[i].base.vps_video_parameter_set_id == h265_get_info->stdVPSId)
               vk_video_encode_h265_vps(&params->vk.h265_enc.h265_vps[i].base, size_limit, &vps_size, pData);
      }
      if (h265_get_info->writeStdSPS) {
         char *data_ptr = pData ? (char *)pData + vps_size : NULL;
         for (unsigned i = 0; i < params->vk.h265_enc.h265_sps_count; i++)
            if (params->vk.h265_enc.h265_sps[i].base.sps_seq_parameter_set_id == h265_get_info->stdSPSId) {
               vk_video_encode_h265_sps(&params->vk.h265_enc.h265_sps[i].base, size_limit, &sps_size, data_ptr);
            }
      }
      if (h265_get_info->writeStdPPS) {
         char *data_ptr = pData ? (char *)pData + vps_size + sps_size : NULL;
         for (unsigned i = 0; i < params->vk.h265_enc.h265_pps_count; i++)
            if (params->vk.h265_enc.h265_pps[i].base.pps_seq_parameter_set_id == h265_get_info->stdPPSId) {
               params->vk.h265_enc.h265_pps[i].base.flags.cu_qp_delta_enabled_flag = 0;
               vk_video_encode_h265_pps(&params->vk.h265_enc.h265_pps[i].base, size_limit, &pps_size, data_ptr);
            }
      }
      total_size = sps_size + pps_size + vps_size;
      break;
   }
   default:
      break;
   }

   /* vk_video_encode_h26x functions support to be safe even if size_limit is not enough,
    * so we could just confirm whether pDataSize is valid afterwards.
    */
   if (pData && *pDataSize < total_size) {
      *pDataSize = 0;
      return VK_INCOMPLETE;
   }

   *pDataSize = total_size;
   return VK_SUCCESS;
}

VkResult
anv_GetPhysicalDeviceVideoEncodeQualityLevelPropertiesKHR(VkPhysicalDevice physicalDevice,
                                                          const VkPhysicalDeviceVideoEncodeQualityLevelInfoKHR* pQualityLevelInfo,
                                                          VkVideoEncodeQualityLevelPropertiesKHR* pQualityLevelProperties)
{
   /* TODO. */
   return VK_SUCCESS;
}

static void
init_single_av1_entry(const struct syntax_element_cdf_table_layout *entry, uint16_t *dst_ptr)
{
   uint16_t entry_count_per_cl = entry->entry_count_per_cl;
   uint16_t entry_count_total = entry->entry_count_total;
   uint16_t start_cl = entry->start_cl;

   const uint16_t *src = entry->init_data;
   uint16_t *dst = dst_ptr + start_cl * 32;
   uint16_t entry_count_left = entry_count_total;

   while (entry_count_left >= entry_count_per_cl) {
      memcpy(dst, src, entry_count_per_cl * sizeof(uint16_t));
      entry_count_left -= entry_count_per_cl;

      src += entry_count_per_cl;
      dst += 32;
   }

   if (entry_count_left > 0)
      memcpy(dst, src, entry_count_left * sizeof(uint16_t));
}

#define INIT_TABLE(x) do {\
   for (unsigned i = 0; i < ARRAY_SIZE((x)); i++) \
      init_single_av1_entry(&(x)[i], dst_ptr); \
   } while (0)

static void
init_all_av1_entry(uint16_t *dst_ptr, int index)
{
   INIT_TABLE(av1_cdf_intra_part1);

   switch (index) {
   case 0:
      INIT_TABLE(av1_cdf_intra_coeffs_0);
      break;
   case 1:
      INIT_TABLE(av1_cdf_intra_coeffs_1);
      break;
   case 2:
      INIT_TABLE(av1_cdf_intra_coeffs_2);
      break;
   case 3:
      INIT_TABLE(av1_cdf_intra_coeffs_3);
      break;
   default:
      UNREACHABLE("illegal av1 entry\n");
   }
   INIT_TABLE(av1_cdf_intra_part2);
   INIT_TABLE(av1_cdf_inter);
}

void
anv_init_av1_cdf_tables(struct anv_cmd_buffer *cmd,
                        struct anv_video_session *vid)
{
   void *ptr;

   for (unsigned i = 0; i < 4; i++) {
      VkResult result =
         anv_device_map_bo(cmd->device,
                           vid->vid_mem[ANV_VID_MEM_AV1_CDF_DEFAULTS_0 + i].mem->bo,
                           vid->vid_mem[ANV_VID_MEM_AV1_CDF_DEFAULTS_0 + i].offset,
                           vid->vid_mem[ANV_VID_MEM_AV1_CDF_DEFAULTS_0 + i].size,
                           NULL,
                           &ptr);

      if (result != VK_SUCCESS) {
         anv_batch_set_error(&cmd->batch, result);
         return;
      }

      init_all_av1_entry(ptr, i);
      anv_device_unmap_bo(cmd->device, vid->vid_mem[ANV_VID_MEM_AV1_CDF_DEFAULTS_0 + i].mem->bo, ptr,
                          vid->vid_mem[ANV_VID_MEM_AV1_CDF_DEFAULTS_0 + i].size, NULL);

   }
}

#define VP9_CTX_DEFAULT(field) {                                \
   assert(sizeof(ctx.field) == sizeof(default_##field));        \
   memcpy(ctx.field, default_##field, sizeof(default_##field)); \
}

static void
vp9_prob_buf_update(struct anv_video_session *vid,
                    void *ptr,
                    bool key_frame,
                    const StdVideoVP9Segmentation *seg)
{
   vp9_frame_context ctx = { 0, };

   /* Reset all */
   if (BITSET_TEST(vid->prob_tbl_set, 0)) {
      ctx.tx_probs = default_tx_probs;
      VP9_CTX_DEFAULT(coef_probs_4x4);
      VP9_CTX_DEFAULT(coef_probs_8x8);
      VP9_CTX_DEFAULT(coef_probs_16x16);
      VP9_CTX_DEFAULT(coef_probs_32x32);

      VP9_CTX_DEFAULT(skip_probs);

      if (key_frame) {
         memcpy(ctx.partition_probs, vp9_kf_partition_probs,
                sizeof(vp9_kf_partition_probs));
         memcpy(ctx.uv_mode_probs, vp9_kf_uv_mode_probs,
                sizeof(vp9_kf_uv_mode_probs));
      } else {
         VP9_CTX_DEFAULT(inter_mode_probs);
         VP9_CTX_DEFAULT(switchable_interp_prob);
         VP9_CTX_DEFAULT(intra_inter_prob);
         VP9_CTX_DEFAULT(comp_inter_prob);
         VP9_CTX_DEFAULT(single_ref_prob);
         VP9_CTX_DEFAULT(comp_ref_prob);
         VP9_CTX_DEFAULT(y_mode_prob);
         VP9_CTX_DEFAULT(partition_probs);
         ctx.nmvc = default_nmv_context;
         VP9_CTX_DEFAULT(uv_mode_probs);
      }

      memcpy(ptr, &ctx, sizeof(vp9_frame_context));
   }

   /* Reset partially */
   if (BITSET_TEST(vid->prob_tbl_set, 1)) {
      if (key_frame) {
         memcpy(ctx.partition_probs, vp9_kf_partition_probs,
                sizeof(vp9_kf_partition_probs));
         memcpy(ctx.uv_mode_probs, vp9_kf_uv_mode_probs,
                sizeof(vp9_kf_uv_mode_probs));
      } else {
         VP9_CTX_DEFAULT(inter_mode_probs);
         VP9_CTX_DEFAULT(switchable_interp_prob);
         VP9_CTX_DEFAULT(intra_inter_prob);
         VP9_CTX_DEFAULT(comp_inter_prob);
         VP9_CTX_DEFAULT(single_ref_prob);
         VP9_CTX_DEFAULT(comp_ref_prob);
         VP9_CTX_DEFAULT(y_mode_prob);
         VP9_CTX_DEFAULT(partition_probs);
         ctx.nmvc = default_nmv_context;
         VP9_CTX_DEFAULT(uv_mode_probs);
      }

      memcpy(ptr + INTER_MODE_PROBS_OFFSET, &ctx.inter_mode_probs, INTER_MODE_PROBS_SIZE);
   }

   /* Copy seg probs */
   if (BITSET_TEST(vid->prob_tbl_set, 2)) {
      memcpy(ctx.seg_tree_probs, seg->segmentation_tree_probs,
             sizeof(ctx.seg_tree_probs));
      memcpy(ctx.seg_pred_probs, seg->segmentation_pred_prob,
             sizeof(ctx.seg_pred_probs));
      memcpy(ptr + SEG_PROBS_OFFSET, &ctx.seg_tree_probs,
             SEG_TREE_PROBS + PREDICTION_PROBS);
   } else if (BITSET_TEST(vid->prob_tbl_set, 3)) {
      VP9_CTX_DEFAULT(seg_tree_probs);
      VP9_CTX_DEFAULT(seg_pred_probs);
      memcpy(ptr + SEG_PROBS_OFFSET, &ctx,
             SEG_TREE_PROBS + PREDICTION_PROBS);
   }

   /* TODO for 4, 5 */
}

void
anv_update_vp9_tables(struct anv_cmd_buffer *cmd,
                      struct anv_video_session *vid,
                      uint32_t prob_id,
                      bool key_frame,
                      const StdVideoVP9Segmentation *seg)
{
   void *prob_map;

   VkResult result =
      anv_device_map_bo(cmd->device,
                        vid->vid_mem[prob_id].mem->bo,
                        vid->vid_mem[prob_id].offset,
                        vid->vid_mem[prob_id].size,
                        NULL /* placed_addr */,
                        &prob_map);

   if (result != VK_SUCCESS) {
      anv_batch_set_error(&cmd->batch, result);
      return;
   }

   vp9_prob_buf_update(vid, prob_map, key_frame, seg);

   /* Clear probability setting table */
   for (int i = 0; i < 6; i++)
      BITSET_CLEAR(vid->prob_tbl_set, i);

   anv_device_unmap_bo(cmd->device,
                       vid->vid_mem[prob_id].mem->bo,
                       prob_map,
                       vid->vid_mem[prob_id].size, false);
}

void
anv_calculate_qmul(const struct VkVideoDecodeVP9PictureInfoKHR *vp9_pic,
                   uint32_t seg_id,
                   int16_t *ptr)
{
   const StdVideoDecodeVP9PictureInfo *std_pic = vp9_pic->pStdPictureInfo;
   const StdVideoVP9Segmentation *segmentation = std_pic->pSegmentation;

   uint32_t bpp_index = std_pic->pColorConfig->BitDepth > 8 ? 1 : 0;

   uint32_t qyac;

   if (std_pic->flags.segmentation_enabled && segmentation->FeatureEnabled[seg_id]) {
      if (segmentation->flags.segmentation_abs_or_delta_update) {
         /* FIXME. which lvl needs to be picked */
         qyac = segmentation->FeatureData[seg_id][0] & 0xff;
      } else {
         qyac = (std_pic->base_q_idx + segmentation->FeatureData[seg_id][0]) & 0xff;
      }
   } else {
      qyac = std_pic->base_q_idx & 0xff;
   }

   uint32_t qydc = (qyac + std_pic->delta_q_y_dc) & 0xff;
   uint32_t quvdc = (qyac + std_pic->delta_q_uv_dc) & 0xff;
   uint32_t quvac = (qyac + std_pic->delta_q_uv_ac) & 0xff;

   int16_t qmul[2][2] = { 0, };

   qmul[0][0] = vp9_dc_qlookup[bpp_index][qydc];
   qmul[0][1] = vp9_ac_qlookup[bpp_index][qyac];
   qmul[1][0] = vp9_dc_qlookup[bpp_index][quvdc];
   qmul[1][1] = vp9_ac_qlookup[bpp_index][quvac];

   memcpy(ptr, qmul, sizeof(qmul));
}

void
anv_vp9_reset_segment_id(struct anv_cmd_buffer *cmd, struct anv_video_session *vid)
{
   void *map;

   VkResult result =
      anv_device_map_bo(cmd->device,
                        vid->vid_mem[ANV_VID_MEM_VP9_SEGMENT_ID].mem->bo,
                        vid->vid_mem[ANV_VID_MEM_VP9_SEGMENT_ID].offset,
                        vid->vid_mem[ANV_VID_MEM_VP9_SEGMENT_ID].size,
                        NULL,
                        &map);

   if (result != VK_SUCCESS) {
      anv_batch_set_error(&cmd->batch, result);
      return;
   }

   memset(map, 0, vid->vid_mem[ANV_VID_MEM_VP9_SEGMENT_ID].size);
   anv_device_unmap_bo(cmd->device,
                       vid->vid_mem[ANV_VID_MEM_VP9_SEGMENT_ID].mem->bo,
                       map,
                       vid->vid_mem[ANV_VID_MEM_VP9_SEGMENT_ID].size, NULL);
}

uint32_t
anv_video_get_image_mv_size(struct anv_device *device,
                            struct anv_image *image,
                            const struct VkVideoProfileListInfoKHR *profile_list)
{
   uint32_t size = 0;

   for (unsigned i = 0; i < profile_list->profileCount; i++) {
      if (profile_list->pProfiles[i].videoCodecOperation == VK_VIDEO_CODEC_OPERATION_DECODE_H264_BIT_KHR) {
         unsigned w_mb = DIV_ROUND_UP(image->vk.extent.width, ANV_MB_WIDTH);
         unsigned h_mb = DIV_ROUND_UP(image->vk.extent.height, ANV_MB_HEIGHT);
         size = w_mb * h_mb * 128;
      } else if (profile_list->pProfiles[i].videoCodecOperation == VK_VIDEO_CODEC_OPERATION_DECODE_H265_BIT_KHR) {
         unsigned w_mb = DIV_ROUND_UP(image->vk.extent.width, 32);
         unsigned h_mb = DIV_ROUND_UP(image->vk.extent.height, 32);
         size = ALIGN(w_mb * h_mb, 2) << 6;
      } else if (profile_list->pProfiles[i].videoCodecOperation == VK_VIDEO_CODEC_OPERATION_DECODE_VP9_BIT_KHR) {
         unsigned w_ctb = DIV_ROUND_UP(image->vk.extent.width, ANV_MAX_VP9_CTB_SIZE);
         unsigned h_ctb = DIV_ROUND_UP(image->vk.extent.height, ANV_MAX_VP9_CTB_SIZE);

         size = (w_ctb * h_ctb * 9) << 6;
      } else if (profile_list->pProfiles[i].videoCodecOperation == VK_VIDEO_CODEC_OPERATION_DECODE_AV1_BIT_KHR) {
         uint32_t width_in_sb, height_in_sb;
         get_av1_sb_size(&width_in_sb, &height_in_sb);
         uint32_t sb_total = width_in_sb * height_in_sb;

         size = sb_total * 16;
      }
   }
   return size;
}
