/// Copyright (C) 2019 The Android Open Source Project
// Copyright (C) 2019 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "AndroidHardwareBuffer.h"

#include "../OpenglSystemCommon/HostConnection.h"

#include "vk_format_info.h"
#include "vk_util.h"

namespace goldfish_vk {

// From Intel ANV implementation.
/* Construct ahw usage mask from image usage bits, see
 * 'AHardwareBuffer Usage Equivalence' in Vulkan spec.
 */
uint64_t
getAndroidHardwareBufferUsageFromVkUsage(const VkImageCreateFlags vk_create,
                                 const VkImageUsageFlags vk_usage)
{
   uint64_t ahw_usage = 0;

   if (vk_usage & VK_IMAGE_USAGE_SAMPLED_BIT)
      ahw_usage |= AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;

   if (vk_usage & VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT)
      ahw_usage |= AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;

   if (vk_usage & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
      ahw_usage |= AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT;

   if (vk_create & VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT)
      ahw_usage |= AHARDWAREBUFFER_USAGE_GPU_CUBE_MAP;

   if (vk_create & VK_IMAGE_CREATE_PROTECTED_BIT)
      ahw_usage |= AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT;

   /* No usage bits set - set at least one GPU usage. */
   if (ahw_usage == 0)
      ahw_usage = AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE;

   return ahw_usage;
}

VkResult getAndroidHardwareBufferPropertiesANDROID(
    Gralloc* grallocHelper,
    const HostVisibleMemoryVirtualizationInfo* hostMemVirtInfo,
    VkDevice,
    const AHardwareBuffer* buffer,
    VkAndroidHardwareBufferPropertiesANDROID* pProperties) {

    VkAndroidHardwareBufferFormatPropertiesANDROID* ahbFormatProps =
        vk_find_struct<VkAndroidHardwareBufferFormatPropertiesANDROID>(pProperties);

    if (ahbFormatProps) {
        AHardwareBuffer_Desc desc;
        AHardwareBuffer_describe(buffer, &desc);

       const uint64_t gpu_usage =
          AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
          AHARDWAREBUFFER_USAGE_GPU_COLOR_OUTPUT |
          AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER;

        if (!(desc.usage & (gpu_usage))) {
            return VK_ERROR_INVALID_EXTERNAL_HANDLE;
        }
        switch(desc.format) {
            case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
                  ahbFormatProps->format = VK_FORMAT_R8G8B8A8_UNORM;
                  break;
            case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
                  ahbFormatProps->format = VK_FORMAT_R8G8B8A8_UNORM;
                  break;
            case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
                  ahbFormatProps->format = VK_FORMAT_R8G8B8_UNORM;
                  break;
            case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
                  ahbFormatProps->format = VK_FORMAT_R5G6B5_UNORM_PACK16;
                  break;
            case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
                  ahbFormatProps->format = VK_FORMAT_R16G16B16A16_SFLOAT;
                  break;
            case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
                  ahbFormatProps->format = VK_FORMAT_A2B10G10R10_UNORM_PACK32;
                  break;
            case AHARDWAREBUFFER_FORMAT_D16_UNORM:
                  ahbFormatProps->format = VK_FORMAT_D16_UNORM;
                  break;
            case AHARDWAREBUFFER_FORMAT_D24_UNORM:
                  ahbFormatProps->format = VK_FORMAT_X8_D24_UNORM_PACK32;
                  break;
            case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT:
                  ahbFormatProps->format = VK_FORMAT_D24_UNORM_S8_UINT;
                  break;
            case AHARDWAREBUFFER_FORMAT_D32_FLOAT:
                  ahbFormatProps->format = VK_FORMAT_D32_SFLOAT;
                  break;
            case AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT:
                  ahbFormatProps->format = VK_FORMAT_D32_SFLOAT_S8_UINT;
                  break;
            case AHARDWAREBUFFER_FORMAT_S8_UINT:
                  ahbFormatProps->format = VK_FORMAT_S8_UINT;
                  break;
            default:
                  ahbFormatProps->format = VK_FORMAT_UNDEFINED;
        }
        ahbFormatProps->externalFormat = desc.format;

        // The formatFeatures member must include
        // VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT and at least one of
        // VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT or
        // VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT, and should include
        // VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT and
        // VK_FORMAT_FEATURE_SAMPLED_IMAGE_YCBCR_CONVERSION_LINEAR_FILTER_BIT.

        // org.skia.skqp.SkQPRunner#UnitTest_VulkanHardwareBuffer* requires the following:
        // VK_FORMAT_FEATURE_TRANSFER_SRC_BIT
        // VK_FORMAT_FEATURE_TRANSFER_DST_BIT
        // VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT
        ahbFormatProps->formatFeatures =
            VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
            VK_FORMAT_FEATURE_MIDPOINT_CHROMA_SAMPLES_BIT |
            VK_FORMAT_FEATURE_TRANSFER_SRC_BIT |
            VK_FORMAT_FEATURE_TRANSFER_DST_BIT |
            VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;

        // "Implementations may not always be able to determine the color model,
        // numerical range, or chroma offsets of the image contents, so the values in
        // VkAndroidHardwareBufferFormatPropertiesANDROID are only suggestions.
        // Applications should treat these values as sensible defaults to use in the
        // absence of more reliable information obtained through some other means."

        ahbFormatProps->samplerYcbcrConversionComponents.r = VK_COMPONENT_SWIZZLE_IDENTITY;
        ahbFormatProps->samplerYcbcrConversionComponents.g = VK_COMPONENT_SWIZZLE_IDENTITY;
        ahbFormatProps->samplerYcbcrConversionComponents.b = VK_COMPONENT_SWIZZLE_IDENTITY;
        ahbFormatProps->samplerYcbcrConversionComponents.a = VK_COMPONENT_SWIZZLE_IDENTITY;

        ahbFormatProps->suggestedYcbcrModel =
            android_format_is_yuv(desc.format) ?
                VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601 :
                VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY;
        ahbFormatProps->suggestedYcbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_FULL;

        ahbFormatProps->suggestedXChromaOffset = VK_CHROMA_LOCATION_MIDPOINT;
        ahbFormatProps->suggestedYChromaOffset = VK_CHROMA_LOCATION_MIDPOINT;
    }

    const native_handle_t *handle =
       AHardwareBuffer_getNativeHandle(buffer);
    uint32_t colorBufferHandle =
        grallocHelper->getHostHandle(handle);
    if (!colorBufferHandle) {
        return VK_ERROR_INVALID_EXTERNAL_HANDLE;
    }

    // Disallow host visible and noflags heaps for now
    // (hard to make actual dedicated allocs)
    uint32_t memoryTypeBits = 0;
    for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i) {
        if (isNoFlagsMemoryTypeIndexForGuest(hostMemVirtInfo, i)) continue;
        if (isHostVisibleMemoryTypeIndexForGuest(hostMemVirtInfo, i)) continue;
        memoryTypeBits |= (1 << i);
    }

    pProperties->memoryTypeBits = memoryTypeBits;
    pProperties->allocationSize =
        grallocHelper->getAllocatedSize(handle);

    return VK_SUCCESS;
}

// Based on Intel ANV implementation.
VkResult getMemoryAndroidHardwareBufferANDROID(struct AHardwareBuffer **pBuffer) {

   /* Some quotes from Vulkan spec:
    *
    * "If the device memory was created by importing an Android hardware
    * buffer, vkGetMemoryAndroidHardwareBufferANDROID must return that same
    * Android hardware buffer object."
    *
    * "VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID must
    * have been included in VkExportMemoryAllocateInfo::handleTypes when
    * memory was created."
    */

    if (!pBuffer) return VK_ERROR_OUT_OF_HOST_MEMORY;
    if (!(*pBuffer)) return VK_ERROR_OUT_OF_HOST_MEMORY;

    AHardwareBuffer_acquire(*pBuffer);
    return VK_SUCCESS;
}

VkResult importAndroidHardwareBuffer(
    Gralloc* grallocHelper,
    const VkImportAndroidHardwareBufferInfoANDROID* info,
    struct AHardwareBuffer **importOut) {

    if (!info || !info->buffer) {
        return VK_ERROR_INVALID_EXTERNAL_HANDLE;
    }

    uint32_t colorBufferHandle =
        grallocHelper->getHostHandle(
            AHardwareBuffer_getNativeHandle(info->buffer));
    if (!colorBufferHandle) {
        return VK_ERROR_INVALID_EXTERNAL_HANDLE;
    }

    auto ahb = info->buffer;

    AHardwareBuffer_acquire(ahb);

    if (importOut) *importOut = ahb;

    return VK_SUCCESS;
}

VkResult createAndroidHardwareBuffer(
    bool hasDedicatedImage,
    bool hasDedicatedBuffer,
    const VkExtent3D& imageExtent,
    uint32_t imageLayers,
    VkFormat imageFormat,
    VkImageUsageFlags imageUsage,
    VkImageCreateFlags imageCreateFlags,
    VkDeviceSize bufferSize,
    VkDeviceSize allocationInfoAllocSize,
    struct AHardwareBuffer **out) {

    uint32_t w = 0;
    uint32_t h = 1;
    uint32_t layers = 1;
    uint32_t format = 0;
    uint64_t usage = 0;

    /* If caller passed dedicated information. */
    if (hasDedicatedImage) {
       w = imageExtent.width;
       h = imageExtent.height;
       layers = imageLayers;
       format = android_format_from_vk(imageFormat);
       usage = getAndroidHardwareBufferUsageFromVkUsage(imageCreateFlags, imageUsage);
    } else if (hasDedicatedBuffer) {
       w = bufferSize;
       format = AHARDWAREBUFFER_FORMAT_BLOB;
       usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
               AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
               AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER;
    } else {
       w = allocationInfoAllocSize;
       format = AHARDWAREBUFFER_FORMAT_BLOB;
       usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN |
               AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN |
               AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER;
    }

    struct AHardwareBuffer *ahw = NULL;
    struct AHardwareBuffer_Desc desc = {
        .width = w,
        .height = h,
        .layers = layers,
        .format = format,
        .usage = usage,
    };

    if (AHardwareBuffer_allocate(&desc, &ahw) != 0) {
        return VK_ERROR_OUT_OF_HOST_MEMORY;
    }

    *out = ahw;

    return VK_SUCCESS;
}

} // namespace goldfish_vk