text: /* Copyright (c) 2015-2021 The Khronos Group Inc.
   * Copyright (c) 2015-2021 Valve Corporation
   * Copyright (c) 2015-2021 LunarG, Inc.
   * Copyright (C) 2015-2021 Google Inc.
   * Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
   *
   * 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.
   *
   * Author: Courtney Goeltzenleuchter <courtneygo@google.com>
   * Author: Tobin Ehlis <tobine@google.com>
   * Author: Chris Forbes <chrisf@ijw.co.nz>
   * Author: Mark Lobodzinski <mark@lunarg.com>
   * Author: Dave Houlton <daveh@lunarg.com>
   * Author: John Zulauf <jzulauf@lunarg.com>
   * Author: Tobias Hector <tobias.hector@amd.com>
   */
  #pragma once
  #include "vk_layer_data.h"
  #include "hash_util.h"
  
  // Types to store queue family ownership (QFO) Transfers
  class COMMAND_POOL_STATE;
  
  template <typename Barrier>
  inline bool IsTransferOp(const Barrier &barrier) {
      return barrier.srcQueueFamilyIndex != barrier.dstQueueFamilyIndex;
  }
  
  // specializations for barriers that cannot do queue family ownership transfers
  template <>
  constexpr bool IsTransferOp(const VkMemoryBarrier &barrier) {
      return false;
  }
  template <>
  constexpr bool IsTransferOp(const VkMemoryBarrier2KHR &barrier) {
      return false;
  }
  template <>
  constexpr bool IsTransferOp(const VkSubpassDependency2 &barrier) {
      return false;
  }
  
  static inline bool QueueFamilyIsExternal(const uint32_t queue_family_index) {
      return (queue_family_index == VK_QUEUE_FAMILY_EXTERNAL) || (queue_family_index == VK_QUEUE_FAMILY_FOREIGN_EXT);
  }
  
  // Caution: Section 7.7.4 states that "If the values of srcQueueFamilyIndex and dstQueueFamilyIndex are equal, no ownership transfer
  // is performed, and the barrier operates as if they were both set to VK_QUEUE_FAMILY_IGNORED."; this does not handle that case.
  static inline bool QueueFamilyIsIgnored(uint32_t queue_family_index) { return queue_family_index == VK_QUEUE_FAMILY_IGNORED; }
  
  // Common to image and buffer memory barriers
  template <typename Handle>
  struct QFOTransferBarrierBase {
      using HandleType = Handle;
      Handle handle = VK_NULL_HANDLE;
      uint32_t srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
      uint32_t dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
  
      QFOTransferBarrierBase() = default;
      QFOTransferBarrierBase(const Handle &resource_handle, uint32_t src, uint32_t dst)
          : handle(resource_handle), srcQueueFamilyIndex(src), dstQueueFamilyIndex(dst) {}
  
      hash_util::HashCombiner base_hash_combiner() const {
          hash_util::HashCombiner hc;
          hc << srcQueueFamilyIndex << dstQueueFamilyIndex << handle;
          return hc;
      }
  
      bool operator==(const QFOTransferBarrierBase<Handle> &rhs) const {
          return (srcQueueFamilyIndex == rhs.srcQueueFamilyIndex) && (dstQueueFamilyIndex == rhs.dstQueueFamilyIndex) &&
                 (handle == rhs.handle);
      }
  };
  
  // Image barrier specific implementation
  struct QFOImageTransferBarrier : public QFOTransferBarrierBase<VkImage> {
      using BaseType = QFOTransferBarrierBase<VkImage>;
      VkImageLayout oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
      VkImageLayout newLayout = VK_IMAGE_LAYOUT_UNDEFINED;
      VkImageSubresourceRange subresourceRange;
  
      QFOImageTransferBarrier() = default;
      QFOImageTransferBarrier(const VkImageMemoryBarrier &barrier)
          : BaseType(barrier.image, barrier.srcQueueFamilyIndex, barrier.dstQueueFamilyIndex),
            oldLayout(barrier.oldLayout),
            newLayout(barrier.newLayout),
            subresourceRange(barrier.subresourceRange) {}
      QFOImageTransferBarrier(const VkImageMemoryBarrier2KHR &barrier)
          : BaseType(barrier.image, barrier.srcQueueFamilyIndex, barrier.dstQueueFamilyIndex),
            oldLayout(barrier.oldLayout),
            newLayout(barrier.newLayout),
            subresourceRange(barrier.subresourceRange) {}
      size_t hash() const {
          // Ignoring the layout information for the purpose of the hash, as we're interested in QFO release/acquisition w.r.t.
          // the subresource affected, an layout transitions are current validated on another path
          auto hc = base_hash_combiner() << subresourceRange;
          return hc.Value();
      }
      bool operator==(const QFOImageTransferBarrier &rhs) const {
          // Ignoring layout w.r.t. equality. See comment in hash above.
          return (static_cast<BaseType>(*this) == static_cast<BaseType>(rhs)) && (subresourceRange == rhs.subresourceRange);
      }
      // TODO: codegen a comprehensive complie time type -> string (and or other traits) template family
      static const char *BarrierName() { return "VkImageMemoryBarrier"; }
      static const char *HandleName() { return "VkImage"; }
      // UNASSIGNED-VkImageMemoryBarrier-image-00001 QFO transfer image barrier must not duplicate QFO recorded in command buffer
      static const char *ErrMsgDuplicateQFOInCB() { return "UNASSIGNED-VkImageMemoryBarrier-image-00001"; }
      // UNASSIGNED-VkImageMemoryBarrier-image-00002 QFO transfer image barrier must not duplicate QFO submitted in batch
      static const char *ErrMsgDuplicateQFOInSubmit() { return "UNASSIGNED-VkImageMemoryBarrier-image-00002"; }
      // UNASSIGNED-VkImageMemoryBarrier-image-00003 QFO transfer image barrier must not duplicate QFO submitted previously
      static const char *ErrMsgDuplicateQFOSubmitted() { return "UNASSIGNED-VkImageMemoryBarrier-image-00003"; }
      // UNASSIGNED-VkImageMemoryBarrier-image-00004 QFO acquire image barrier must have matching QFO release submitted previously
      static const char *ErrMsgMissingQFOReleaseInSubmit() { return "UNASSIGNED-VkImageMemoryBarrier-image-00004"; }
  };
  
  // Buffer barrier specific implementation
  struct QFOBufferTransferBarrier : public QFOTransferBarrierBase<VkBuffer> {
      using BaseType = QFOTransferBarrierBase<VkBuffer>;
      VkDeviceSize offset = 0;
      VkDeviceSize size = 0;
      QFOBufferTransferBarrier() = default;
      QFOBufferTransferBarrier(const VkBufferMemoryBarrier &barrier)
          : BaseType(barrier.buffer, barrier.srcQueueFamilyIndex, barrier.dstQueueFamilyIndex),
            offset(barrier.offset),
            size(barrier.size) {}
      QFOBufferTransferBarrier(const VkBufferMemoryBarrier2KHR &barrier)
          : BaseType(barrier.buffer, barrier.srcQueueFamilyIndex, barrier.dstQueueFamilyIndex),
            offset(barrier.offset),
            size(barrier.size) {}
      size_t hash() const {
          auto hc = base_hash_combiner() << offset << size;
          return hc.Value();
      }
      bool operator==(const QFOBufferTransferBarrier &rhs) const {
          return (static_cast<BaseType>(*this) == static_cast<BaseType>(rhs)) && (offset == rhs.offset) && (size == rhs.size);
      }
      static const char *BarrierName() { return "VkBufferMemoryBarrier"; }
      static const char *HandleName() { return "VkBuffer"; }
      // UNASSIGNED-VkImageMemoryBarrier-buffer-00001 QFO transfer buffer barrier must not duplicate QFO recorded in command buffer
      static const char *ErrMsgDuplicateQFOInCB() { return "UNASSIGNED-VkBufferMemoryBarrier-buffer-00001"; }
      // UNASSIGNED-VkBufferMemoryBarrier-buffer-00002 QFO transfer buffer barrier must not duplicate QFO submitted in batch
      static const char *ErrMsgDuplicateQFOInSubmit() { return "UNASSIGNED-VkBufferMemoryBarrier-buffer-00002"; }
      // UNASSIGNED-VkBufferMemoryBarrier-buffer-00003 QFO transfer buffer barrier must not duplicate QFO submitted previously
      static const char *ErrMsgDuplicateQFOSubmitted() { return "UNASSIGNED-VkBufferMemoryBarrier-buffer-00003"; }
      // UNASSIGNED-VkBufferMemoryBarrier-buffer-00004 QFO acquire buffer barrier must have matching QFO release submitted previously
      static const char *ErrMsgMissingQFOReleaseInSubmit() { return "UNASSIGNED-VkBufferMemoryBarrier-buffer-00004"; }
  };
  
  template <typename TransferBarrier>
  using QFOTransferBarrierHash = hash_util::HasHashMember<TransferBarrier>;
  
  // Command buffers store the set of barriers recorded
  template <typename TransferBarrier>
  using QFOTransferBarrierSet = layer_data::unordered_set<TransferBarrier, QFOTransferBarrierHash<TransferBarrier>>;
  
  template <typename TransferBarrier>
  struct QFOTransferBarrierSets {
      QFOTransferBarrierSet<TransferBarrier> release;
      QFOTransferBarrierSet<TransferBarrier> acquire;
      void Reset() {
          acquire.clear();
          release.clear();
      }
  };
  
  // The layer_data stores the map of pending release barriers
  template <typename TransferBarrier>
  using GlobalQFOTransferBarrierMap =
      layer_data::unordered_map<typename TransferBarrier::HandleType, QFOTransferBarrierSet<TransferBarrier>>;
  
  // Submit queue uses the Scoreboard to track all release/acquire operations in a batch.
  template <typename TransferBarrier>
  using QFOTransferCBScoreboard =
      layer_data::unordered_map<TransferBarrier, const CMD_BUFFER_STATE *, QFOTransferBarrierHash<TransferBarrier>>;
  
  template <typename TransferBarrier>
  struct QFOTransferCBScoreboards {
      QFOTransferCBScoreboard<TransferBarrier> acquire;
      QFOTransferCBScoreboard<TransferBarrier> release;
  };
  