// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_PIPELINE_CACHE_DATA_VK_H_
#define FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_PIPELINE_CACHE_DATA_VK_H_

#include "flutter/fml/mapping.h"
#include "flutter/fml/unique_fd.h"
#include "impeller/renderer/backend/vulkan/vk.h"

namespace impeller {

//------------------------------------------------------------------------------
/// @brief      An Impeller specific header prepended to all pipeline cache
///             information that is persisted on disk. This information is used
///             to perform additional integrity checks that may have been missed
///             by the Vulkan driver.
///
///             Inspired by
///             https://medium.com/@zeuxcg/creating-a-robust-pipeline-cache-with-vulkan-961d09416cda.
///
struct PipelineCacheHeaderVK {
  // This can be used by Impeller to manually invalidate all old caches.
  uint32_t magic = 0xC0DEF00D;
  // Notably, this field is missing from checks the Vulkan driver performs. For
  // drivers that don't correctly check the UUID, explicitly disregarding caches
  // generated by previous driver versions sidesteps some landmines.
  uint32_t driver_version = 0;
  uint32_t vendor_id = 0;
  uint32_t device_id = 0;
  // If applications are published as 32-bit and updated via the app store to be
  // 64-bits, this check comes in handy to disregard previous caches.
  uint32_t abi = sizeof(void*);
  uint8_t uuid[VK_UUID_SIZE] = {};
  uint64_t data_size = 0;

  //----------------------------------------------------------------------------
  /// @brief      Constructs a new empty instance.
  ///
  PipelineCacheHeaderVK();

  //----------------------------------------------------------------------------
  /// @brief      Constructs a new instance that will be compatible with the
  ///             given physical device properties.
  ///
  /// @param[in]  props        The properties.
  /// @param[in]  p_data_size  The data size.
  ///
  explicit PipelineCacheHeaderVK(const VkPhysicalDeviceProperties& props,
                                 uint64_t p_data_size);

  //----------------------------------------------------------------------------
  /// @brief      Determines whether the specified o is compatible with.
  ///
  ///             The size of the data following the header may be different and
  ///             is not part of compatibility checks.
  ///
  /// @param[in]  other     The other header.
  ///
  /// @return     True if the specified header is compatible with this one,
  ///             False otherwise. The size of the data following the header may
  ///             be different.
  ///
  bool IsCompatibleWith(const PipelineCacheHeaderVK& other) const;
};

//------------------------------------------------------------------------------
/// @brief      Persist the pipeline cache to a file in the given cache
///             directory. This function performs integrity checks the Vulkan
///             driver may have missed.
///
/// @warning    The pipeline cache must be externally synchronized for most
///             complete results. If additional pipelines are being created
///             while this function is executing, this function may fail to
///             persist data.
///
/// @param[in]  cache_directory  The cache directory
/// @param[in]  props            The physical device properties
/// @param[in]  cache            The cache
///
/// @return     If the cache data could be persisted to disk.
///
bool PipelineCacheDataPersist(const fml::UniqueFD& cache_directory,
                              const VkPhysicalDeviceProperties& props,
                              const vk::UniquePipelineCache& cache);

//------------------------------------------------------------------------------
/// @brief      Retrieve the previously persisted pipeline cache data. This
///             function provides integrity checks the Vulkan driver may have
///             missed.
///
///             The data is stripped of any additional headers that perform
///             integrity checks. It can be used directly to construct a
///             pre-initialized Vulkan pipeline cache.
///
/// @param[in]  cache_directory  The cache directory
/// @param[in]  props            The properties
///
/// @return     The cache data if it was found and checked to have passed
///             additional integrity checks.
///
std::unique_ptr<fml::Mapping> PipelineCacheDataRetrieve(
    const fml::UniqueFD& cache_directory,
    const VkPhysicalDeviceProperties& props);

}  // namespace impeller

#endif  // FLUTTER_IMPELLER_RENDERER_BACKEND_VULKAN_PIPELINE_CACHE_DATA_VK_H_
