/*
 * Copyright © 2021 Collabora Ltd.
 * SPDX-License-Identifier: MIT
 */

#include "genxml/gen_macros.h"

#include "panvk_device.h"
#include "panvk_entrypoints.h"
#include "panvk_sampler.h"

#include "pan_encoder.h"
#include "pan_format.h"

#include "vk_format.h"
#include "vk_log.h"

static enum mali_mipmap_mode
panvk_translate_sampler_mipmap_mode(VkSamplerMipmapMode mode)
{
   switch (mode) {
   case VK_SAMPLER_MIPMAP_MODE_NEAREST:
      return MALI_MIPMAP_MODE_NEAREST;
   case VK_SAMPLER_MIPMAP_MODE_LINEAR:
      return MALI_MIPMAP_MODE_TRILINEAR;
   default:
      unreachable("Invalid mipmap mode");
   }
}

static unsigned
panvk_translate_sampler_address_mode(VkSamplerAddressMode mode)
{
   switch (mode) {
   case VK_SAMPLER_ADDRESS_MODE_REPEAT:
      return MALI_WRAP_MODE_REPEAT;
   case VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT:
      return MALI_WRAP_MODE_MIRRORED_REPEAT;
   case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE:
      return MALI_WRAP_MODE_CLAMP_TO_EDGE;
   case VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER:
      return MALI_WRAP_MODE_CLAMP_TO_BORDER;
   case VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE:
      return MALI_WRAP_MODE_MIRRORED_CLAMP_TO_EDGE;
   default:
      unreachable("Invalid wrap");
   }
}

static enum mali_func
panvk_translate_sampler_compare_func(const VkSamplerCreateInfo *pCreateInfo)
{
   if (!pCreateInfo->compareEnable)
      return MALI_FUNC_NEVER;

   return panfrost_flip_compare_func((enum mali_func)pCreateInfo->compareOp);
}

static void
swizzle_border_color(VkClearColorValue *border_color, VkFormat fmt)
{
   if (PAN_ARCH != 7)
      return;

   enum pipe_format pfmt = vk_format_to_pipe_format(fmt);
   if (panfrost_format_is_yuv(pfmt) || util_format_is_depth_or_stencil(pfmt))
      return;

   const struct util_format_description *fdesc = util_format_description(pfmt);
   if (fdesc->swizzle[0] == PIPE_SWIZZLE_Z &&
       fdesc->swizzle[2] == PIPE_SWIZZLE_X) {
      uint32_t red = border_color->uint32[0];

      border_color->uint32[0] = border_color->uint32[2];
      border_color->uint32[2] = red;
   }
}

VKAPI_ATTR VkResult VKAPI_CALL
panvk_per_arch(CreateSampler)(VkDevice _device,
                              const VkSamplerCreateInfo *pCreateInfo,
                              const VkAllocationCallbacks *pAllocator,
                              VkSampler *pSampler)
{
   VK_FROM_HANDLE(panvk_device, device, _device);
   struct panvk_sampler *sampler;

   assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO);

   sampler =
      vk_sampler_create(&device->vk, pCreateInfo, pAllocator, sizeof(*sampler));
   if (!sampler)
      return vk_error(device, VK_ERROR_OUT_OF_HOST_MEMORY);

   STATIC_ASSERT(sizeof(sampler->desc) >= pan_size(SAMPLER));

   VkFormat fmt;
   VkClearColorValue border_color =
      vk_sampler_border_color_value(pCreateInfo, &fmt);

   swizzle_border_color(&border_color, fmt);

   pan_pack(sampler->desc.opaque, SAMPLER, cfg) {
      cfg.magnify_nearest = pCreateInfo->magFilter == VK_FILTER_NEAREST;
      cfg.minify_nearest = pCreateInfo->minFilter == VK_FILTER_NEAREST;
      cfg.mipmap_mode =
         panvk_translate_sampler_mipmap_mode(pCreateInfo->mipmapMode);
      cfg.normalized_coordinates = !pCreateInfo->unnormalizedCoordinates;

      cfg.lod_bias = pCreateInfo->mipLodBias;
      cfg.minimum_lod = pCreateInfo->minLod;
      cfg.maximum_lod = pCreateInfo->maxLod;
      cfg.wrap_mode_s =
         panvk_translate_sampler_address_mode(pCreateInfo->addressModeU);
      cfg.wrap_mode_t =
         panvk_translate_sampler_address_mode(pCreateInfo->addressModeV);

      /* "
       * When unnormalizedCoordinates is VK_TRUE, images the sampler is used
       * with in the shader have the following requirements:
       * - The viewType must be either VK_IMAGE_VIEW_TYPE_1D or
       *   VK_IMAGE_VIEW_TYPE_2D.
       * - The image view must have a single layer and a single mip level.
       * "
       *
       * This means addressModeW should be ignored. We pick a default value
       * that works for normalized_coordinates=false.
       */
      cfg.wrap_mode_r =
         pCreateInfo->unnormalizedCoordinates
            ? MALI_WRAP_MODE_CLAMP_TO_EDGE
            : panvk_translate_sampler_address_mode(pCreateInfo->addressModeW);
      cfg.compare_function = panvk_translate_sampler_compare_func(pCreateInfo);
      cfg.border_color_r = border_color.uint32[0];
      cfg.border_color_g = border_color.uint32[1];
      cfg.border_color_b = border_color.uint32[2];
      cfg.border_color_a = border_color.uint32[3];

      if (pCreateInfo->anisotropyEnable && pCreateInfo->maxAnisotropy > 1) {
         cfg.maximum_anisotropy = pCreateInfo->maxAnisotropy;
         cfg.lod_algorithm = MALI_LOD_ALGORITHM_ANISOTROPIC;
      }
   }

   *pSampler = panvk_sampler_to_handle(sampler);
   return VK_SUCCESS;
}

VKAPI_ATTR void VKAPI_CALL
panvk_per_arch(DestroySampler)(VkDevice _device, VkSampler _sampler,
                               const VkAllocationCallbacks *pAllocator)
{
   VK_FROM_HANDLE(panvk_device, device, _device);
   VK_FROM_HANDLE(panvk_sampler, sampler, _sampler);

   if (!sampler)
      return;

   vk_sampler_destroy(&device->vk, pAllocator, &sampler->vk);
}
