code:
#pragma once

#include "dxvk_include.h"
#include "dxvk_extensions.h"

#include <vector>
#include <string>

namespace dxvk {

  class DxvkInstance;
  class DxvkExtensionProvider;

  /**
   * \brief Extension provider base
   *
   * Abstract interface for extension
   * providers
   */
  class DxvkExtensionProvider {

  public:

    /**
     * \brief Extension provider name
     * \returns The extension provider's name
     */
    virtual std::string_view getName() = 0;

    /**
     * \brief Query instance extensions
     * \returns Instance extensions
     */
    virtual DxvkNameSet getInstanceExtensions() = 0;

    /**
     * \brief Query device extensions
     *
     * Retrieves the extensions required for a specific
     * physical device. The adapter index should remain
     * the same across multiple Vulkan instances.
     * \param [in] adapterId Adapter index
     */
    virtual DxvkNameSet getDeviceExtensions(
            uint32_t      adapterId) = 0;

    /**
     * \brief Initializes instance extension set
     *
     * Should be called before creating
     * the first Vulkan instance.
     */
    virtual void initInstanceExtensions() = 0;

    /**
     * \brief Initializes device extension sets
     *
     * Should be called after setting
     * up the Vulkan physical devices.
     * \param [in] instance DXVK instance
     */
    virtual void initDeviceExtensions(
      const DxvkInstance* instance) = 0;

  };

}


code:
#include "dxvk_extensions.h"

namespace dxvk {

  DxvkNameSet::DxvkNameSet() { }
  DxvkNameSet::~DxvkNameSet() { }

  void DxvkNameSet::add(const char* pName) {
    m_names.insert({ pName, 1u });
  }

  void DxvkNameSet::merge(const DxvkNameSet& names) {
    for (const auto& pair : names.m_names)
      m_names.insert(pair);
  }

  uint32_t DxvkNameSet::supports(const char* pName) const {
    auto entry = m_names.find(pName);

    if (entry == m_names.end())
      return 0;

    return entry->second != 0
      ? entry->second
      : 1;
  }

  bool DxvkNameSet::enableExtensions(
          uint32_t          numExtensions,
          DxvkExt**         ppExtensions,
          DxvkNameSet&       nameSet) const {
    bool allRequiredEnabled = true;

    for (uint32_t i = 0; i < numExtensions; i++) {
      DxvkExt* ext = ppExtensions[i];

      if (ext->mode() == DxvkExtMode::Disabled)
        continue;

      uint32_t revision = supports(ext->name());

      if (revision) {
        if (ext->mode() != DxvkExtMode::Passive)
          nameSet.add(ext->name());

        ext->enable(revision);
      } else if (ext->mode() == DxvkExtMode::Required) {
        Logger::info(str::format("Required Vulkan extension ", ext->name(), " not supported"));
        allRequiredEnabled = false;
        continue;
      }
    }

    return allRequiredEnabled;
  }

  void DxvkNameSet::disableExtension(
          DxvkExt&          ext) {
    m_names.erase(ext.name());
    ext.disable();
  }

  DxvkNameList DxvkNameSet::toNameList() const {
    DxvkNameList nameList;
    for (const auto& pair : m_names)
      nameList.add(pair.first.c_str());
    return nameList;
  }

  DxvkNameSet DxvkNameSet::enumInstanceLayers(const Rc<vk::LibraryFn>& vkl) {
    uint32_t entryCount = 0;
    if (vkl->vkEnumerateInstanceLayerProperties(
          &entryCount, nullptr) != VK_SUCCESS)
      return DxvkNameSet();

    std::vector<VkLayerProperties> entries(entryCount);
    if (vkl->vkEnumerateInstanceLayerProperties(
          &entryCount, entries.data()) != VK_SUCCESS)
      return DxvkNameSet();

    DxvkNameSet set;
    for (uint32_t i = 0; i < entryCount; i++)
      set.m_names.insert({ entries[i].layerName, entries[i].specVersion });
    return set;
  }

  DxvkNameSet DxvkNameSet::enumInstanceExtensions(const Rc<vk::LibraryFn>& vkl) {
    uint32_t entryCount = 0;
    if (vkl->vkEnumerateInstanceExtensionProperties(
          nullptr, &entryCount, nullptr) != VK_SUCCESS)
      return DxvkNameSet();

    std::vector<VkExtensionProperties> entries(entryCount);
    if (vkl->vkEnumerateInstanceExtensionProperties(
          nullptr, &entryCount, entries.data()) != VK_SUCCESS)
      return DxvkNameSet();

    DxvkNameSet set;
    for (uint32_t i = 0; i < entryCount; i++)
      set.m_names.insert({ entries[i].extensionName, entries[i].specVersion });
    return set;
  }

  DxvkNameSet DxvkNameSet::enumDeviceExtensions(
    const Rc<vk::InstanceFn>& vki,
          VkPhysicalDevice    device) {
    uint32_t entryCount = 0;
    if (vki->vkEnumerateDeviceExtensionProperties(
          device, nullptr, &entryCount, nullptr) != VK_SUCCESS)
      return DxvkNameSet();

    std::vector<VkExtensionProperties> entries(entryCount);
    if (vki->vkEnumerateDeviceExtensionProperties(
          device, nullptr, &entryCount, entries.data()) != VK_SUCCESS)
      return DxvkNameSet();

    DxvkNameSet set;
    for (uint32_t i = 0; i < entryCount; i++)
      set.m_names.insert({ entries[i].extensionName, entries[i].specVersion });
    return set;
  }

}

repo_name: orochi/dxvk


code:
#pragma once

#include <algorithm>
#include <map>
#include <vector>

#include "dxvk_include.h"

namespace dxvk {

  /**
   * \brief Vulkan extension mode
   *
   * Defines whether an extension is
   * optional, required, or disabled.
   */
  enum class DxvkExtMode {
    Disabled,
    Optional,
    Required,
    Passive,
  };

  /**
   * \brief Vulkan extension info
   *
   * Stores information for a single extension.
   * The renderer can use this information to
   * find out which extensions are enabled.
   */
  class DxvkExt {

  public:

    DxvkExt(
      const char*       pName,
            DxvkExtMode mode)
    : m_name(pName), m_mode(mode) { }

    /**
     * \brief Extension name
     * \returns Extension name
     */
    const char* name() const {
      return m_name;
    }

    /**
     * \brief Extension mode
     * \returns Extension mode
     */
    DxvkExtMode mode() const {
      return m_mode;
    }

    /**
     * \brief Checks whether the extension is enabled
     *
     * If an extension is enabled, the features
     * provided by the extension can be used.
     * \returns \c true if the extension is enabled
     */
    operator bool () const {
      return m_revision != 0;
    }

    /**
     * \brief Supported revision
     * \returns Supported revision
     */
    uint32_t revision() const {
      return m_revision;
    }

    /**
     * \brief Changes extension mode
     *
     * In some cases, it may be useful to change the
     * default mode dynamically after initialization.
     */
    void setMode(DxvkExtMode mode) {
      m_mode = mode;
    }

    /**
     * \brief Enables the extension
     */
    void enable(uint32_t revision) {
      m_revision = revision;
    }

    /**
     * \brief Disables the extension
     */
    void disable() {
      m_revision = 0;
    }

  private:

    const char* m_name     = nullptr;
    DxvkExtMode m_mode     = DxvkExtMode::Disabled;
    uint32_t    m_revision = 0;

  };

  /**
   * \brief Vulkan name list
   *
   * A simple \c vector wrapper that can
   * be used to build a list of layer and
   * extension names.
   */
  class DxvkNameList {

  public:

    /**
     * \brief Adds a name
     * \param [in] pName The name
     */
    void add(const char* pName) {
      m_names.push_back(pName);
    }

    /**
     * \brief Number of names
     * \returns Name count
     */
    uint32_t count() const {
      return m_names.size();
    }

    /**
     * \brief Name list
     * \returns Name list
     */
    const char* const* names() const {
      return m_names.data();
    }

    /**
     * \brief Retrieves a single name
     *
     * \param [in] index Name index
     * \returns The given name
     */
    const char* name(uint32_t index) const {
      return m_names.at(index);
    }

  private:

    std::vector<const char*> m_names;

  };

  /**
   * \brief Vulkan extension set
   *
   * Stores a set of extensions or layers
   * supported by the Vulkan implementation.
   */
  class DxvkNameSet {

  public:

    DxvkNameSet();
    ~DxvkNameSet();

    /**
     * \brief Adds a name to the set
     * \param [in] pName Extension name
     */
    void add(
      const char*             pName);

    /**
     * \brief Merges two name sets
     *
     * Adds all names from the given name set to
     * this name set, avoiding duplicate entries.
     * \param [in] names Name set to merge
     */
    void merge(
      const DxvkNameSet&      names);

    /**
     * \brief Checks whether an extension is supported
     *
     * \param [in] pName Extension name
     * \returns Supported revision, or zero
     */
    uint32_t supports(
      const char*             pName) const;

    /**
     * \brief Enables requested extensions
     *
     * Walks over a set of extensions and enables all
     * extensions that are supported and not disabled.
     * This also checks whether all required extensions
     * could be enabled, and returns \c false otherwise.
     * \param [in] numExtensions Number of extensions
     * \param [in] ppExtensions List of extensions
     * \param [out] nameSet Extension name set
     * \returns \c true on success
     */
    bool enableExtensions(
            uint32_t          numExtensions,
            DxvkExt**         ppExtensions,
            DxvkNameSet&      nameSet) const;

    /**
     * \brief Disables given extension
     *
     * Removes the given extension from the set
     * and sets its revision to 0 (i.e. disabled).
     * \param [in,out] ext Extension to disable
     */
    void disableExtension(
            DxvkExt&          ext);

    /**
     * \brief Creates name list from name set
     *
     * Adds all names contained in the name set
     * to a name list, which can then be passed
     * to Vulkan functions.
     * \returns Name list
     */
    DxvkNameList toNameList() const;

    /**
     * \brief Enumerates instance layers
     *
     * \param [in] vkl Vulkan library functions
     * \returns Set of available instance layers
     */
    static DxvkNameSet enumInstanceLayers(
      const Rc<vk::LibraryFn>&  vkl);

    /**
     * \brief Enumerates instance extensions
     *
     * \param [in] vkl Vulkan library functions
     * \returns Set of available instance extensions
     */
    static DxvkNameSet enumInstanceExtensions(
      const Rc<vk::LibraryFn>&  vkl);

    /**
     * \brief Enumerates device extensions
     *
     * \param [in] vki Vulkan instance functions
     * \param [in] device The device to query
     * \returns Set of available device extensions
     */
    static DxvkNameSet enumDeviceExtensions(
      const Rc<vk::InstanceFn>& vki,
            VkPhysicalDevice    device);

  private:

    std::map<std::string, uint32_t> m_names;

  };

  /**
   * \brief Device extensions
   *
   * Lists all Vulkan extensions that are potentially
   * used by DXVK if supported by the implementation.
   */
  struct DxvkDeviceExtensions {
    DxvkExt amdMemoryOverallocationBehaviour  = { VK_AMD_MEMORY_OVERALLOCATION_BEHAVIOR_EXTENSION_NAME,     DxvkExtMode::Optional };
    DxvkExt amdShaderFragmentMask             = { VK_AMD_SHADER_FRAGMENT_MASK_EXTENSION_NAME,               DxvkExtMode::Optional };
    DxvkExt ext4444Formats                    = { VK_EXT_4444_FORMATS_EXTENSION_NAME,                       DxvkExtMode::Optional };
    DxvkExt extConservativeRasterization      = { VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME,         DxvkExtMode::Optional };
    DxvkExt extCustomBorderColor              = { VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME,                DxvkExtMode::Optional };
    DxvkExt extDepthClipEnable                = { VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME,                  DxvkExtMode::Optional };
    DxvkExt extExtendedDynamicState           = { VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME,             DxvkExtMode::Optional };
    DxvkExt extFullScreenExclusive            = { VK_EXT_FULL_SCREEN_EXCLUSIVE_EXTENSION_NAME,              DxvkExtMode::Optional };
    DxvkExt extHostQueryReset                 = { VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME,                   DxvkExtMode::Optional };
    DxvkExt extMemoryBudget                   = { VK_EXT_MEMORY_BUDGET_EXTENSION_NAME,                      DxvkExtMode::Passive  };
    DxvkExt extMemoryPriority                 = { VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME,                    DxvkExtMode::Optional };
    DxvkExt extNonSeamlessCubeMap             = { VK_EXT_NON_SEAMLESS_CUBE_MAP_EXTENSION_NAME,              DxvkExtMode::Optional };
    DxvkExt extRobustness2                    = { VK_EXT_ROBUSTNESS_2_EXTENSION_NAME,                       DxvkExtMode::Optional };
    DxvkExt extShaderDemoteToHelperInvocation = { VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME, DxvkExtMode::Optional };
    DxvkExt extShaderStencilExport            = { VK_EXT_SHADER_STENCIL_EXPORT_EXTENSION_NAME,              DxvkExtMode::Optional };
    DxvkExt extShaderViewportIndexLayer       = { VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME,        DxvkExtMode::Optional };
    DxvkExt extTransformFeedback              = { VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME,                 DxvkExtMode::Optional };
    DxvkExt extVertexAttributeDivisor         = { VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME,           DxvkExtMode::Optional };
    DxvkExt khrBufferDeviceAddress            = { VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME,              DxvkExtMode::Disabled };
    DxvkExt khrCreateRenderPass2              = { VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME,                DxvkExtMode::Optional };
    DxvkExt khrDepthStencilResolve            = { VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME,              DxvkExtMode::Optional };
    DxvkExt khrDrawIndirectCount              = { VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME,                DxvkExtMode::Optional };
    DxvkExt khrDriverProperties               = { VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME,                  DxvkExtMode::Optional };
    DxvkExt khrExternalMemoryWin32            = { VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME,              DxvkExtMode::Optional };
    DxvkExt khrExternalSemaphoreWin32         = { VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME,           DxvkExtMode::Optional };
    DxvkExt khrImageFormatList                = { VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME,                  DxvkExtMode::Required };
    DxvkExt khrSamplerMirrorClampToEdge       = { VK_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE_EXTENSION_NAME,       DxvkExtMode::Optional };
    DxvkExt khrShaderFloatControls            = { VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME,              DxvkExtMode::Optional };
    DxvkExt khrSwapchain                      = { VK_KHR_SWAPCHAIN_EXTENSION_NAME,                          DxvkExtMode::Required };
    DxvkExt khrTimelineSemaphore              = { VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME,                 DxvkExtMode::Optional };
    DxvkExt nvxBinaryImport                   = { VK_NVX_BINARY_IMPORT_EXTENSION_NAME,                      DxvkExtMode::Disabled };
    DxvkExt nvxImageViewHandle                = { VK_NVX_IMAGE_VIEW_HANDLE_EXTENSION_NAME,                  DxvkExtMode::Disabled };
  };

  /**
   * \brief Instance extensions
   *
   * Lists all Vulkan extensions that are potentially
   * used by DXVK if supported by the implementation.
   */
  struct DxvkInstanceExtensions {
    DxvkExt extDebugUtils                   = { VK_EXT_DEBUG_UTILS_EXTENSION_NAME,                      DxvkExtMode::Optional };
    DxvkExt khrGetSurfaceCapabilities2      = { VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME,       DxvkExtMode::Optional };
    DxvkExt khrSurface                      = { VK_KHR_SURFACE_EXTENSION_NAME,                          DxvkExtMode::Required };
  };

}


code:
#include "dxvk_device.h"
#include "dxvk_fence.h"

namespace dxvk {

  DxvkFence::DxvkFence(
          DxvkDevice*           device,
    const DxvkFenceCreateInfo&  info)
  : m_vkd(device->vkd()), m_info(info) {
    if (!device->features().khrTimelineSemaphore.timelineSemaphore)
      throw DxvkError("Timeline semaphores not supported");

    VkSemaphoreTypeCreateInfoKHR typeInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO_KHR };
    typeInfo.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE_KHR;
    typeInfo.initialValue = info.initialValue;

    VkExportSemaphoreCreateInfo exportInfo = { VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO };
    exportInfo.handleTypes = info.sharedType;

    VkExternalSemaphoreFeatureFlags externalFeatures = 0;

    if (info.sharedType != VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FLAG_BITS_MAX_ENUM) {
      auto vki = device->adapter()->vki();

      VkPhysicalDeviceExternalSemaphoreInfo externalInfo = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_SEMAPHORE_INFO, &typeInfo };
      externalInfo.handleType = info.sharedType;

      VkExternalSemaphoreProperties externalProperties = { };
      vki->vkGetPhysicalDeviceExternalSemaphoreProperties(
        device->adapter()->handle(), &externalInfo, &externalProperties);

      externalFeatures = externalProperties.externalSemaphoreFeatures;

      if (externalFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_EXPORTABLE_BIT)
        typeInfo.pNext = &exportInfo;
      else
        Logger::warn(str::format("Exporting semaphores of type ", info.sharedType, " not supported by device"));
    }

    VkSemaphoreCreateInfo semaphoreInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, &typeInfo };

    VkResult vr = m_vkd->vkCreateSemaphore(m_vkd->device(),
      &semaphoreInfo, nullptr, &m_semaphore);

    if (vr != VK_SUCCESS)
      throw DxvkError("Failed to create timeline semaphore");

    if (info.sharedHandle != INVALID_HANDLE_VALUE) {
      if (externalFeatures & VK_EXTERNAL_SEMAPHORE_FEATURE_IMPORTABLE_BIT) {
        VkImportSemaphoreWin32HandleInfoKHR importInfo = { VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_WIN32_HANDLE_INFO_KHR };
        importInfo.semaphore = m_semaphore;
        importInfo.handleType = info.sharedType;
        importInfo.handle = info.sharedHandle;

        vr = m_vkd->vkImportSemaphoreWin32HandleKHR(m_vkd->device(), &importInfo);
        if (vr != VK_SUCCESS)
          throw DxvkError("Failed to import timeline semaphore");
      } else {
        Logger::warn(str::format("Importing semaphores of type ", info.sharedType, " not supported by device"));
      }
    }

    m_thread = dxvk::thread([this] { run(); });
  }

  DxvkFence::~DxvkFence() {
    m_stop.store(true);
    m_thread.join();

    m_vkd->vkDestroySemaphore(m_vkd->device(), m_semaphore, nullptr);
  }

  void DxvkFence::enqueueWait(uint64_t value, DxvkFenceEvent&& event) {
    std::unique_lock<dxvk::mutex> lock(m_mutex);

    if (value > m_lastValue.load())
      m_queue.emplace(value, std::move(event));
    else
      event();
  }

  void DxvkFence::run() {
    uint64_t value = 0ull;

    VkSemaphoreWaitInfoKHR waitInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO_KHR };
    waitInfo.semaphoreCount = 1;
    waitInfo.pSemaphores = &m_semaphore;
    waitInfo.pValues = &value;

    while (!m_stop.load()) {
      std::unique_lock<dxvk::mutex> lock(m_mutex);

      // Query actual semaphore value and start from there, so that
      // we can skip over large increments in the semaphore value
      VkResult vr = m_vkd->vkGetSemaphoreCounterValueKHR(m_vkd->device(), m_semaphore, &value);

      if (vr != VK_SUCCESS) {
        Logger::err(str::format("Failed to query semaphore value: ", vr));
        return;
      }

      m_lastValue.store(value);

      // Signal all enqueued events whose value is not greater than
      // the current semaphore value
      while (!m_queue.empty() && m_queue.top().value <= value) {
        m_queue.top().event();
        m_queue.pop();
      }

      if (m_stop)
        return;

      lock.unlock();

      // Wait for the semaphore to be singaled again and update state.
      // The timeout is unfortunate, but we can't always know when a
      // signal operation has been recorded, and the alternative would
      // be to create a teardown semaphore and use WAIT_ANY, which may
      // be fall back to a busy-wait loop on some drivers.
      value += 1;

      vr = m_vkd->vkWaitSemaphoresKHR(
        m_vkd->device(), &waitInfo, 10'000'000ull);

      if (vr != VK_SUCCESS && vr != VK_TIMEOUT) {
        Logger::err(str::format("Failed to wait for semaphore: ", vr));
        return;
      }
    }
  }

  HANDLE DxvkFence::sharedHandle() const {
    if (m_info.sharedType == VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FLAG_BITS_MAX_ENUM)
      return INVALID_HANDLE_VALUE;

    VkSemaphoreGetWin32HandleInfoKHR win32HandleInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_GET_WIN32_HANDLE_INFO_KHR };
    win32HandleInfo.semaphore = m_semaphore;
    win32HandleInfo.handleType = m_info.sharedType;

    HANDLE sharedHandle = INVALID_HANDLE_VALUE;
    VkResult vr = m_vkd->vkGetSemaphoreWin32HandleKHR(m_vkd->device(), &win32HandleInfo, &sharedHandle);

    if (vr != VK_SUCCESS)
      Logger::err(str::format("Failed to get semaphore handle: ", vr));

    return sharedHandle;
  }
}


code:
#pragma once

#include <functional>
#include <queue>
#include <utility>
#include <vector>

#include "dxvk_resource.h"

#include "../util/thread.h"

namespace dxvk {

  class DxvkDevice;
  class DxvkFence;

  using DxvkFenceEvent = std::function<void ()>;

  /**
   * \brief Fence create info
   */
  struct DxvkFenceCreateInfo {
    uint64_t        initialValue;
    VkExternalSemaphoreHandleTypeFlagBits sharedType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_FLAG_BITS_MAX_ENUM;
    union {
#ifdef _WIN32
      HANDLE          sharedHandle = INVALID_HANDLE_VALUE;
#else
      // Placeholder for other handle types, such as FD
      void *dummy;
#endif
    };
  };

  /**
   * \brief Fence-value pair
   */
  struct DxvkFenceValuePair {
    DxvkFenceValuePair() { }
    DxvkFenceValuePair(Rc<DxvkFence>&& fence_, uint64_t value_)
    : fence(std::move(fence_)), value(value_) { }
    DxvkFenceValuePair(const Rc<DxvkFence>& fence_, uint64_t value_)
    : fence(fence_), value(value_) { }

    Rc<DxvkFence> fence;
    uint64_t value;
  };

  /**
   * \brief Fence
   *
   * Wrapper around Vulkan timeline semaphores that
   * can signal an event when the value changes.
   */
  class DxvkFence : public RcObject {

  public:

    DxvkFence(
            DxvkDevice*           device,
      const DxvkFenceCreateInfo&  info);

    ~DxvkFence();

    /**
     * \brief Semaphore handle
     */
    VkSemaphore handle() const {
      return m_semaphore;
    }

    /**
     * \brief Retrieves current semaphore value
     * \returns Current semaphore value
     */
    uint64_t getValue() {
      return m_lastValue.load();
    }

    /**
     * \brief Enqueues semaphore wait
     *
     * Signals the given event when the
     * semaphore reaches the given value.
     * \param [in] value Enqueue value
     * \param [in] event Callback
     */
    void enqueueWait(uint64_t value, DxvkFenceEvent&& event);

    /**
     * \brief Create a new shared handle to timeline semaphore backing the fence
     * \returns The shared handle with the type given by DxvkFenceCreateInfo::sharedType
     */
    HANDLE sharedHandle() const;

  private:

    struct QueueItem {
      QueueItem() { }
      QueueItem(uint64_t v, DxvkFenceEvent&& e)
      : value(v), event(std::move(e)) { }

      uint64_t        value;
      DxvkFenceEvent  event;

      bool operator == (const QueueItem& item) const { return value == item.value; }
      bool operator != (const QueueItem& item) const { return value != item.value; }
      bool operator <  (const QueueItem& item) const { return value <  item.value; }
      bool operator <= (const QueueItem& item) const { return value <= item.value; }
      bool operator >  (const QueueItem& item) const { return value >  item.value; }
      bool operator >= (const QueueItem& item) const { return value >= item.value; }
    };

    Rc<vk::DeviceFn>                m_vkd;
    DxvkFenceCreateInfo             m_info;
    VkSemaphore                     m_semaphore;

    std::priority_queue<QueueItem>  m_queue;
    std::atomic<uint64_t>           m_lastValue = { 0ull  };
    std::atomic<bool>               m_stop      = { false };

    dxvk::mutex                     m_mutex;
    dxvk::thread                    m_thread;

    void run();

  };

}


code:
#include "dxvk_format.h"

namespace dxvk {

  constexpr VkColorComponentFlags RGBA = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
  constexpr VkColorComponentFlags RGB  = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT;
  constexpr VkColorComponentFlags RG   = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT;
  constexpr VkColorComponentFlags R    = VK_COLOR_COMPONENT_R_BIT;

  const std::array<DxvkFormatInfo, 152> g_formatInfos = {{
    // VK_FORMAT_UNDEFINED
    { },

    // VK_FORMAT_R4G4_UNORM_PACK8
    { 1, RG, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R4G4B4A4_UNORM_PACK16
    { 2, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_B4G4R4A4_UNORM_PACK16
    { 2, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R5G6B5_UNORM_PACK16
    { 2, RGB, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_B5G6R5_UNORM_PACK16
    { 2, RGB, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R5G5B5A1_UNORM_PACK16
    { 2, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_B5G5R5A1_UNORM_PACK16
    { 2, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_A1R5G5B5_UNORM_PACK16
    { 2, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R8_UNORM
    { 1, R, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R8_SNORM
    { 1, R, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R8_USCALED
    { 1, R, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R8_SSCALED
    { 1, R, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R8_UINT
    { 1, R, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledUInt },

    // VK_FORMAT_R8_SINT
    { 1, R, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledSInt },

    // VK_FORMAT_R8_SRGB
    { 1, R, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::ColorSpaceSrgb },

    // VK_FORMAT_R8G8_UNORM
    { 2, RG, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R8G8_SNORM
    { 2, RG, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R8G8_USCALED
    { 2, RG, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R8G8_SSCALED
    { 2, RG, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R8G8_UINT
    { 2, RG, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledUInt },

    // VK_FORMAT_R8G8_SINT
    { 2, RG, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledSInt },

    // VK_FORMAT_R8G8_SRGB
    { 2, RG, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::ColorSpaceSrgb },

    // VK_FORMAT_R8G8B8_UNORM
    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R8G8B8_SNORM
    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R8G8B8_USCALED
    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R8G8B8_SSCALED
    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R8G8B8_UINT
    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledUInt },

    // VK_FORMAT_R8G8B8_SINT
    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledSInt },

    // VK_FORMAT_R8G8B8_SRGB
    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::ColorSpaceSrgb },

    // VK_FORMAT_B8G8R8_UNORM
    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_B8G8R8_SNORM
    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_B8G8R8_USCALED
    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_B8G8R8_SSCALED
    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_B8G8R8_UINT
    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledUInt },

    // VK_FORMAT_B8G8R8_SINT
    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledSInt },

    // VK_FORMAT_B8G8R8_SRGB
    { 3, RGB, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::ColorSpaceSrgb },

    // VK_FORMAT_R8G8B8A8_UNORM
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R8G8B8A8_SNORM
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R8G8B8A8_USCALED
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R8G8B8A8_SSCALED
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R8G8B8A8_UINT
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledUInt },

    // VK_FORMAT_R8G8B8A8_SINT
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledSInt },

    // VK_FORMAT_R8G8B8A8_SRGB
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::ColorSpaceSrgb },

    // VK_FORMAT_B8G8R8A8_UNORM
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_B8G8R8A8_SNORM
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_B8G8R8A8_USCALED
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_B8G8R8A8_SSCALED
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_B8G8R8A8_UINT
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledUInt },

    // VK_FORMAT_B8G8R8A8_SINT
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledSInt },

    // VK_FORMAT_B8G8R8A8_SRGB
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::ColorSpaceSrgb },

    // VK_FORMAT_A8B8G8R8_UNORM_PACK32
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_A8B8G8R8_SNORM_PACK32
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_A8B8G8R8_USCALED_PACK32
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_A8B8G8R8_SSCALED_PACK32
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_A8B8G8R8_UINT_PACK32
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledUInt },

    // VK_FORMAT_A8B8G8R8_SINT_PACK32
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledSInt },

    // VK_FORMAT_A8B8G8R8_SRGB_PACK32
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::ColorSpaceSrgb },

    // VK_FORMAT_A2R10G10B10_UNORM_PACK32
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_A2R10G10B10_SNORM_PACK32
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_A2R10G10B10_USCALED_PACK32
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_A2R10G10B10_SSCALED_PACK32
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_A2R10G10B10_UINT_PACK32
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledUInt },

    // VK_FORMAT_A2R10G10B10_SINT_PACK32
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledSInt },

    // VK_FORMAT_A2B10G10R10_UNORM_PACK32
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_A2B10G10R10_SNORM_PACK32
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_A2B10G10R10_USCALED_PACK32
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_A2B10G10R10_SSCALED_PACK32
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_A2B10G10R10_UINT_PACK32
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledUInt },

    // VK_FORMAT_A2B10G10R10_SINT_PACK32
    { 4, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledSInt },

    // VK_FORMAT_R16_UNORM
    { 2, R, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R16_SNORM
    { 2, R, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R16_USCALED
    { 2, R, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R16_SSCALED
    { 2, R, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R16_UINT
    { 2, R, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledUInt },

    // VK_FORMAT_R16_SINT
    { 2, R, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledSInt },

    // VK_FORMAT_R16_SFLOAT
    { 2, R, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R16G16_UNORM
    { 4, RG, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R16G16_SNORM
    { 4, RG, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R16G16_USCALED
    { 4, RG, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R16G16_SSCALED
    { 4, RG, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R16G16_UINT
    { 4, RG, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledUInt },

    // VK_FORMAT_R16G16_SINT
    { 4, RG, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledSInt },

    // VK_FORMAT_R16G16_SFLOAT
    { 4, RG, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R16G16B16_UNORM
    { 6, RGB, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R16G16B16_SNORM
    { 6, RGB, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R16G16B16_USCALED
    { 6, RGB, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R16G16B16_SSCALED
    { 6, RGB, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R16G16B16_UINT
    { 6, RGB, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledUInt },

    // VK_FORMAT_R16G16B16_SINT
    { 6, RGB, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledSInt },

    // VK_FORMAT_R16G16B16_SFLOAT
    { 6, RGB, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R16G16B16A16_UNORM
    { 8, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R16G16B16A16_SNORM
    { 8, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R16G16B16A16_USCALED
    { 8, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R16G16B16A16_SSCALED
    { 8, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R16G16B16A16_UINT
    { 8, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledUInt },

    // VK_FORMAT_R16G16B16A16_SINT
    { 8, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledSInt },

    // VK_FORMAT_R16G16B16A16_SFLOAT
    { 8, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R32_UINT
    { 4, R, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledUInt },

    // VK_FORMAT_R32_SINT
    { 4, R, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledSInt },

    // VK_FORMAT_R32_SFLOAT
    { 4, R, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R32G32_UINT
    { 8, RG, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledUInt },

    // VK_FORMAT_R32G32_SINT
    { 8, RG, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledSInt },

    // VK_FORMAT_R32G32_SFLOAT
    { 8, RG, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R32G32B32_UINT
    { 12, RGB, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledUInt },

    // VK_FORMAT_R32G32B32_SINT
    { 12, RGB, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledSInt },

    // VK_FORMAT_R32G32B32_SFLOAT
    { 12, RGB, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R32G32B32A32_UINT
    { 16, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledUInt },

    // VK_FORMAT_R32G32B32A32_SINT
    { 16, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledSInt },

    // VK_FORMAT_R32G32B32A32_SFLOAT
    { 16, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R64_UINT
    { 8, R, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledUInt },

    // VK_FORMAT_R64_SINT
    { 8, R, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledSInt },

    // VK_FORMAT_R64_SFLOAT
    { 8, R, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R64G64_UINT
    { 16, RG, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledUInt },

    // VK_FORMAT_R64G64_SINT
    { 16, RG, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledSInt },

    // VK_FORMAT_R64G64_SFLOAT
    { 16, RG, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R64G64B64_UINT
    { 24, RGB, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledUInt },

    // VK_FORMAT_R64G64B64_SINT
    { 24, RGB, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledSInt },

    // VK_FORMAT_R64G64B64_SFLOAT
    { 24, RGB, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_R64G64B64A64_UINT
    { 32, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledUInt },

    // VK_FORMAT_R64G64B64A64_SINT
    { 32, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::SampledSInt },

    // VK_FORMAT_R64G64B64A64_SFLOAT
    { 32, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_B10G11R11_UFLOAT_PACK32
    { 4, RGB, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_E5B9G9R9_UFLOAT_PACK32
    { 4, RGB, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_D16_UNORM
    { 2, 0, VK_IMAGE_ASPECT_DEPTH_BIT },

    // VK_FORMAT_X8_D24_UNORM_PACK32
    { 4, 0, VK_IMAGE_ASPECT_DEPTH_BIT },

    // VK_FORMAT_D32_SFLOAT
    { 4, 0, VK_IMAGE_ASPECT_DEPTH_BIT },

    // VK_FORMAT_S8_UINT
    { 1, 0, VK_IMAGE_ASPECT_STENCIL_BIT },

    // VK_FORMAT_D16_UNORM_S8_UINT
    { 4, 0, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT },

    // VK_FORMAT_D24_UNORM_S8_UINT
    { 4, 0, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT },

    // VK_FORMAT_D32_SFLOAT_S8_UINT
    { 8, 0, VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT },

    // VK_FORMAT_BC1_RGB_UNORM_BLOCK
    { 8, RGB, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::BlockCompressed,
      VkExtent3D { 4, 4, 1 } },

    // VK_FORMAT_BC1_RGB_SRGB_BLOCK
    { 8, RGB, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlags(
        DxvkFormatFlag::BlockCompressed,
        DxvkFormatFlag::ColorSpaceSrgb),
      VkExtent3D { 4, 4, 1 } },

    // VK_FORMAT_BC1_RGBA_UNORM_BLOCK
    { 8, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::BlockCompressed,
      VkExtent3D { 4, 4, 1 } },

    // VK_FORMAT_BC1_RGBA_SRGB_BLOCK
    { 8, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlags(
        DxvkFormatFlag::BlockCompressed,
        DxvkFormatFlag::ColorSpaceSrgb),
      VkExtent3D { 4, 4, 1 } },

    // VK_FORMAT_BC2_UNORM_BLOCK
    { 16, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::BlockCompressed,
      VkExtent3D { 4, 4, 1 } },

    // VK_FORMAT_BC2_SRGB_BLOCK
    { 16, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlags(
        DxvkFormatFlag::BlockCompressed,
        DxvkFormatFlag::ColorSpaceSrgb),
      VkExtent3D { 4, 4, 1 } },

    // VK_FORMAT_BC3_UNORM_BLOCK
    { 16, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::BlockCompressed,
      VkExtent3D { 4, 4, 1 } },

    // VK_FORMAT_BC3_SRGB_BLOCK
    { 16, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlags(
        DxvkFormatFlag::BlockCompressed,
        DxvkFormatFlag::ColorSpaceSrgb),
      VkExtent3D { 4, 4, 1 } },

    // VK_FORMAT_BC4_UNORM_BLOCK
    { 8, R, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::BlockCompressed,
      VkExtent3D { 4, 4, 1 } },

    // VK_FORMAT_BC4_SNORM_BLOCK
    { 8, R, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::BlockCompressed,
      VkExtent3D { 4, 4, 1 } },

    // VK_FORMAT_BC5_UNORM_BLOCK
    { 16, RG, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::BlockCompressed,
      VkExtent3D { 4, 4, 1 } },

    // VK_FORMAT_BC5_SNORM_BLOCK
    { 16, RG, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::BlockCompressed,
      VkExtent3D { 4, 4, 1 } },

    // VK_FORMAT_BC6H_UFLOAT_BLOCK
    { 16, RGB, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::BlockCompressed,
      VkExtent3D { 4, 4, 1 } },

    // VK_FORMAT_BC6H_SFLOAT_BLOCK
    { 16, RGB, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::BlockCompressed,
      VkExtent3D { 4, 4, 1 } },

    // VK_FORMAT_BC7_UNORM_BLOCK
    { 16, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::BlockCompressed,
      VkExtent3D { 4, 4, 1 } },

    // VK_FORMAT_BC7_SRGB_BLOCK
    { 16, RGBA, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlags(
        DxvkFormatFlag::BlockCompressed,
        DxvkFormatFlag::ColorSpaceSrgb),
      VkExtent3D { 4, 4, 1 } },

    // VK_FORMAT_G8B8G8R8_422_UNORM_KHR
    { 4, RGB, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::BlockCompressed,
      VkExtent3D { 2, 1, 1 } },

    // VK_FORMAT_B8G8R8G8_422_UNORM_KHR
    { 4, RGB, VK_IMAGE_ASPECT_COLOR_BIT,
      DxvkFormatFlag::BlockCompressed,
      VkExtent3D { 2, 1, 1 } },

    // VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT
    { 2, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT
    { 2, RGBA, VK_IMAGE_ASPECT_COLOR_BIT },

    // VK_FORMAT_G8_B8R8_2PLANE_420_UNORM
    { 6, RGB, VK_IMAGE_ASPECT_PLANE_0_BIT | VK_IMAGE_ASPECT_PLANE_1_BIT,
      DxvkFormatFlag::MultiPlane, VkExtent3D { 1, 1, 1 },
      { DxvkPlaneFormatInfo { 1, { 1, 1 } },
        DxvkPlaneFormatInfo { 2, { 2, 2 } } } },
  }};

  const std::array<std::pair<VkFormat, VkFormat>, 4> g_formatGroups = {{
    { VK_FORMAT_UNDEFINED,                  VK_FORMAT_BC7_SRGB_BLOCK            },
    { VK_FORMAT_G8B8G8R8_422_UNORM_KHR,     VK_FORMAT_B8G8R8G8_422_UNORM_KHR    },
    { VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT,  VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT },
    { VK_FORMAT_G8_B8R8_2PLANE_420_UNORM,   VK_FORMAT_G8_B8R8_2PLANE_420_UNORM  },
  }};

  const DxvkFormatInfo* imageFormatInfo(VkFormat format) {
    uint32_t indexOffset = 0;

    for (const auto& group : g_formatGroups) {
      if (format >= group.first && format <= group.second) {
        uint32_t index = uint32_t(format) - uint32_t(group.first);
        return &g_formatInfos[indexOffset + index];
      } else {
        indexOffset += uint32_t(group.second)
                     - uint32_t(group.first) + 1;
      }
    }

    return nullptr;
  }

}


code:
#pragma once

#include "dxvk_include.h"

namespace dxvk {

  enum class DxvkFormatFlag {
    BlockCompressed = 0,  ///< Image format is block compressed
    SampledUInt     = 1,  ///< Sampled type is an unsigned integer type
    SampledSInt     = 2,  ///< Sampled type is a signed integer type
    ColorSpaceSrgb  = 3,  ///< Non-linear SRGB color format
    MultiPlane      = 4,  ///< Multi-plane format
  };

  using DxvkFormatFlags = Flags<DxvkFormatFlag>;

  /**
   * \brief Planar format info
   */
  struct DxvkPlaneFormatInfo {
    /// Byte size of a pixel in the current plane
    VkDeviceSize elementSize = 0;
    /// Number of image pixels covered by a
    /// single pixel in the current plane
    VkExtent2D blockSize = { 1, 1 };
  };

  /**
   * \brief Format info structure
   *
   * Provides some useful information
   * about a Vulkan image format.
   */
  struct DxvkFormatInfo {
    /// Size of an element in this format. For compressed
    /// formats, this is the size of a block, in bytes.
    VkDeviceSize elementSize = 0;

    /// Available component mask
    VkColorComponentFlags componentMask = 0;

    /// Available image aspect flags
    VkImageAspectFlags aspectMask = 0;

    /// Some other format info flags
    DxvkFormatFlags flags = 0;

    /// Size, in pixels, of a compressed block. For
    /// non-block formats, all these values are 1.
    VkExtent3D blockSize = { 1, 1, 1 };

    /// Plane info for multi-planar formats
    std::array<DxvkPlaneFormatInfo, 3> planes;
  };

  const DxvkFormatInfo* imageFormatInfo(VkFormat format);

}


code:
#include "dxvk_framebuffer.h"

namespace dxvk {

  DxvkFramebufferInfo::DxvkFramebufferInfo() {

  }

  DxvkFramebufferInfo::DxvkFramebufferInfo(
    const DxvkRenderTargets&      renderTargets,
    const DxvkFramebufferSize&    defaultSize,
          DxvkRenderPass*         renderPass)
  : m_renderTargets (renderTargets),
    m_renderSize    (computeRenderSize(defaultSize)),
    m_renderPass    (renderPass) {

    for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {
      if (m_renderTargets.color[i].view != nullptr) {
        m_attachments[m_attachmentCount++] = i;
        m_sampleCount = m_renderTargets.color[i].view->imageInfo().sampleCount;
      }
    }

    if (m_renderTargets.depth.view != nullptr) {
      m_attachments[m_attachmentCount++] = -1;
      m_sampleCount = m_renderTargets.depth.view->imageInfo().sampleCount;
    }
  }

  DxvkFramebufferInfo::~DxvkFramebufferInfo() {

  }

  int32_t DxvkFramebufferInfo::findAttachment(const Rc<DxvkImageView>& view) const {
    for (uint32_t i = 0; i < m_attachmentCount; i++) {
      if (getAttachment(i).view->matchesView(view))
        return int32_t(i);
    }

    return -1;
  }

  bool DxvkFramebufferInfo::hasTargets(const DxvkRenderTargets& renderTargets) {
    bool eq = m_renderTargets.depth.view   == renderTargets.depth.view
           && m_renderTargets.depth.layout == renderTargets.depth.layout;

    for (uint32_t i = 0; i < MaxNumRenderTargets && eq; i++) {
      eq &= m_renderTargets.color[i].view   == renderTargets.color[i].view
         && m_renderTargets.color[i].layout == renderTargets.color[i].layout;
    }

    return eq;
  }

  bool DxvkFramebufferInfo::isFullSize(const Rc<DxvkImageView>& view) const {
    return m_renderSize.width  == view->mipLevelExtent(0).width
        && m_renderSize.height == view->mipLevelExtent(0).height
        && m_renderSize.layers == view->info().numLayers;
  }

  bool DxvkFramebufferInfo::isWritable(uint32_t attachmentIndex, VkImageAspectFlags aspects) const {
    VkImageAspectFlags writableAspects = vk::getWritableAspectsForLayout(getAttachment(attachmentIndex).layout);
    return (writableAspects & aspects) == aspects;
  }

  DxvkFramebufferKey DxvkFramebufferInfo::key() const {
    DxvkFramebufferKey result = { };

    for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {
      if (m_renderTargets.color[i].view != nullptr)
        result.colorViews[i] = m_renderTargets.color[i].view->cookie();
    }

    if (m_renderTargets.depth.view != nullptr)
      result.depthView = m_renderTargets.depth.view->cookie();

    if (result.renderPass)
      result.renderPass = m_renderPass->getDefaultHandle();

    return result;
  }

  DxvkRenderPassFormat DxvkFramebufferInfo::getRenderPassFormat(const DxvkRenderTargets& renderTargets) {
    DxvkRenderPassFormat format;

    for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {
      if (renderTargets.color[i].view != nullptr) {
        format.sampleCount     = renderTargets.color[i].view->imageInfo().sampleCount;
        format.color[i].format = renderTargets.color[i].view->info().format;
        format.color[i].layout = renderTargets.color[i].layout;
      }
    }

    if (renderTargets.depth.view != nullptr) {
      format.sampleCount  = renderTargets.depth.view->imageInfo().sampleCount;
      format.depth.format = renderTargets.depth.view->info().format;
      format.depth.layout = renderTargets.depth.layout;
    }

    return format;
  }

  DxvkFramebufferSize DxvkFramebufferInfo::computeRenderSize(
    const DxvkFramebufferSize& defaultSize) const {
    // Some games bind render targets of a different size and
    // expect it to work, so we'll compute the minimum size
    DxvkFramebufferSize minSize = defaultSize;

    if (m_renderTargets.depth.view != nullptr) {
      DxvkFramebufferSize depthSize = this->computeRenderTargetSize(m_renderTargets.depth.view);
      minSize.width  = std::min(minSize.width,  depthSize.width);
      minSize.height = std::min(minSize.height, depthSize.height);
      minSize.layers = std::min(minSize.layers, depthSize.layers);
    }

    for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {
      if (m_renderTargets.color[i].view != nullptr) {
        DxvkFramebufferSize colorSize = this->computeRenderTargetSize(m_renderTargets.color[i].view);
        minSize.width  = std::min(minSize.width,  colorSize.width);
        minSize.height = std::min(minSize.height, colorSize.height);
        minSize.layers = std::min(minSize.layers, colorSize.layers);
      }
    }

    return minSize;
  }

  DxvkFramebufferSize DxvkFramebufferInfo::computeRenderTargetSize(
    const Rc<DxvkImageView>& renderTarget) const {
    auto extent = renderTarget->mipLevelExtent(0);
    auto layers = renderTarget->info().numLayers;
    return DxvkFramebufferSize { extent.width, extent.height, layers };
  }

  DxvkFramebuffer::DxvkFramebuffer(
    const Rc<vk::DeviceFn>&       vkd,
    const DxvkFramebufferInfo&    info)
  : m_vkd(vkd), m_key(info.key()) {
    std::array<VkImageView, MaxNumRenderTargets + 1> views;
    uint32_t attachmentCount = 0;

    for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {
      if (info.getColorTarget(i).view != nullptr)
        views[attachmentCount++] = info.getColorTarget(i).view->handle();
    }

    if (info.getDepthTarget().view != nullptr)
      views[attachmentCount++] = info.getDepthTarget().view->handle();

    VkFramebufferCreateInfo fbInfo;
    fbInfo.sType                = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
    fbInfo.pNext                = nullptr;
    fbInfo.flags                = 0;
    fbInfo.renderPass           = info.renderPass()->getDefaultHandle();
    fbInfo.attachmentCount      = attachmentCount;
    fbInfo.pAttachments         = views.data();
    fbInfo.width                = info.size().width;
    fbInfo.height               = info.size().height;
    fbInfo.layers               = info.size().layers;

    if (m_vkd->vkCreateFramebuffer(m_vkd->device(), &fbInfo, nullptr, &m_handle) != VK_SUCCESS)
      Logger::err("DxvkFramebuffer: Failed to create framebuffer object");
  }

  DxvkFramebuffer::~DxvkFramebuffer() {
    m_vkd->vkDestroyFramebuffer(m_vkd->device(), m_handle, nullptr);
  }

}


code:
#pragma once

#include "dxvk_image.h"
#include "dxvk_renderpass.h"

namespace dxvk {

  /**
   * \brief Framebuffer size
   *
   * Stores the width, height and number of layers
   * of a framebuffer. This can be used in case a
   * framebuffer does not have any attachments.
   */
  struct DxvkFramebufferSize {
    uint32_t width;
    uint32_t height;
    uint32_t layers;
  };

  /**
   * \brief Framebuffer attachment
   *
   * Stores an attachment, as well as the image layout
   * that will be used for rendering to the attachment.
   */
  struct DxvkAttachment {
    Rc<DxvkImageView> view    = nullptr;
    VkImageLayout     layout  = VK_IMAGE_LAYOUT_UNDEFINED;
  };

  /**
   * \brief Render targets
   *
   * Stores all depth-stencil and color
   * attachments attached to a framebuffer.
   */
  struct DxvkRenderTargets {
    DxvkAttachment depth;
    DxvkAttachment color[MaxNumRenderTargets];
  };

  /**
   * \brief Render target layouts
   */
  struct DxvkRenderTargetLayouts {
    VkImageLayout color[MaxNumRenderTargets];
    VkImageLayout depth;
  };

  /**
   * \brief Framebuffer key
   */
  struct DxvkFramebufferKey {
    uint64_t            colorViews[MaxNumRenderTargets];
    uint64_t            depthView;
    VkRenderPass        renderPass;

    size_t hash() const {
      DxvkHashState state;
      state.add(depthView);
      for (uint32_t i = 0; i < MaxNumRenderTargets; i++)
        state.add(colorViews[i]);
      state.add(uint64_t(renderPass));
      return state;
    }

    bool eq(const DxvkFramebufferKey& other) const {
      bool eq = depthView   == other.depthView
             && renderPass  == other.renderPass;
      for (uint32_t i = 0; i < MaxNumRenderTargets; i++)
        eq &= colorViews[i] == other.colorViews[i];
      return eq;
    }
  };

  /**
   * \brief Framebuffer info
   *
   * Stores metadata about the current framebuffer,
   * without actually creating a framebuffer object.
   */
  class DxvkFramebufferInfo {

  public:

    DxvkFramebufferInfo();

    DxvkFramebufferInfo(
      const DxvkRenderTargets&      renderTargets,
      const DxvkFramebufferSize&    defaultSize,
            DxvkRenderPass*         renderPass);

    ~DxvkFramebufferInfo();

    /**
     * \brief Retrieves all attachments
     * \returns Render targets
     */
    const DxvkRenderTargets& attachments() const {
      return m_renderTargets;
    }

    /**
     * \brief Framebuffer size
     * \returns Framebuffer size
     */
    DxvkFramebufferSize size() const {
      return m_renderSize;
    }

    /**
     * \brief Render pass
     * \returns Render pass
     */
    DxvkRenderPass* renderPass() const {
      return m_renderPass;
    }

    /**
     * \brief Framebuffer sample count
     *
     * Returns the sample count of the color
     * and depth-stencil attachments, or 0 if
     * there are no attachments.
     * \returns Sample count
     */
    VkSampleCountFlags getSampleCount() const {
      return m_sampleCount;
    }

    /**
     * \brief Depth-stencil target
     * \returns Depth-stencil target
     */
    const DxvkAttachment& getDepthTarget() const {
      return m_renderTargets.depth;
    }

    /**
     * \brief Color target
     *
     * \param [in] id Target Index
     * \returns The color target
     */
    const DxvkAttachment& getColorTarget(uint32_t id) const {
      return m_renderTargets.color[id];
    }

    /**
     * \brief Number of framebuffer attachment
     * \returns Total attachment count
     */
    uint32_t numAttachments() const {
      return m_attachmentCount;
    }

    /**
     * \brief Queries color attachment index of a given attachment
     * \returns The index, or -1 if the given attachment is the depth attachment
     */
    const int32_t getColorAttachmentIndex(uint32_t id) const {
      return m_attachments[id];
    }

    /**
     * \brief Retrieves attachment by index
     *
     * \param [in] id Framebuffer attachment ID
     * \returns The framebuffer attachment
     */
    const DxvkAttachment& getAttachment(uint32_t id) const {
      int32_t idx = getColorAttachmentIndex(id);
      return idx < 0 ? m_renderTargets.depth : m_renderTargets.color[idx];
    }

    /**
     * \brief Finds attachment index by view
     *
     * Color attachments start at 0
     * \param [in] view Image view
     * \returns Attachment index
     */
    int32_t findAttachment(const Rc<DxvkImageView>& view) const;

    /**
     * \brief Checks whether the framebuffer's targets match
     *
     * \param [in] renderTargets Render targets to check
     * \returns \c true if the render targets are the same
     *          as the ones used for this framebuffer object.
     */
    bool hasTargets(const DxvkRenderTargets& renderTargets);

    /**
     * \brief Checks whether view and framebuffer sizes match
     *
     * Tests whether the size of the framebuffer is the same
     * as the size of one of its views. This may be \c false
     * when mixing attachments with mismatched dimensions.
     * \param [in] view Image view to test
     * \returns \c true if \c view has the same size as
     *          the framebuffer.
     */
    bool isFullSize(const Rc<DxvkImageView>& view) const;

    /**
     * \brief Checks whether an attachment is writable
     *
     * Needed for certain clear optimizations.
     * \param [in] attachmentIndex Attachment to check
     * \param [in] aspects Aspect mask to check
     * \returns \c true if all aspects can be written for the given attachment
     */
    bool isWritable(uint32_t attachmentIndex, VkImageAspectFlags aspects) const;

    /**
     * \brief Generates framebuffer key
     * \returns Framebuffer key
     */
    DxvkFramebufferKey key() const;

    /**
     * \brief Generatess render pass format
     *
     * This render pass format can be used to
     * look up a compatible render pass.
     * \param [in] renderTargets Render targets
     * \returns The render pass format
     */
    static DxvkRenderPassFormat getRenderPassFormat(
      const DxvkRenderTargets&  renderTargets);

  private:

    DxvkRenderTargets   m_renderTargets;
    DxvkFramebufferSize m_renderSize  = { 0u, 0u, 0u };
    VkSampleCountFlags  m_sampleCount = 0;
    DxvkRenderPass*     m_renderPass;

    uint32_t                                     m_attachmentCount = 0;
    std::array<int32_t, MaxNumRenderTargets + 1> m_attachments;

    DxvkFramebufferSize computeRenderSize(
      const DxvkFramebufferSize& defaultSize) const;

    DxvkFramebufferSize computeRenderTargetSize(
      const Rc<DxvkImageView>& renderTarget) const;

  };

  /**
   * \brief Framebuffer
   *
   * A framebuffer either stores a set of image views
   * that will be used as render targets, or in case
   * no render targets are attached, fixed dimensions.
   */
  class DxvkFramebuffer : public DxvkResource {

  public:

    DxvkFramebuffer(
      const Rc<vk::DeviceFn>&       vkd,
      const DxvkFramebufferInfo&    info);

    ~DxvkFramebuffer();

    /**
     * \brief Framebuffer handle
     * \returns Framebuffer handle
     */
    VkFramebuffer handle() const {
      return m_handle;
    }

    /**
     * \brief Framebuffer key
     */
    const DxvkFramebufferKey& key() const {
      return m_key;
    }

  private:

    Rc<vk::DeviceFn>    m_vkd;
    VkFramebuffer       m_handle;
    DxvkFramebufferKey  m_key;

  };

}


code:
#include "dxvk_gpu_event.h"
#include "dxvk_device.h"

namespace dxvk {

  DxvkGpuEvent::DxvkGpuEvent(const Rc<vk::DeviceFn>& vkd)
  : m_vkd(vkd) { }

  DxvkGpuEvent::~DxvkGpuEvent() {
    if (m_handle.pool && m_handle.event)
      m_handle.pool->freeEvent(m_handle.event);
  }

  DxvkGpuEventStatus DxvkGpuEvent::test() const {
    if (!m_handle.event)
      return DxvkGpuEventStatus::Invalid;

    VkResult status = m_vkd->vkGetEventStatus(
      m_vkd->device(), m_handle.event);

    switch (status) {
      case VK_EVENT_SET:    return DxvkGpuEventStatus::Signaled;
      case VK_EVENT_RESET:  return DxvkGpuEventStatus::Pending;
      default:              return DxvkGpuEventStatus::Invalid;
    }
  }

  DxvkGpuEventHandle DxvkGpuEvent::reset(DxvkGpuEventHandle handle) {
    m_vkd->vkResetEvent(m_vkd->device(), handle.event);
    return std::exchange(m_handle, handle);
  }

  DxvkGpuEventPool::DxvkGpuEventPool(const DxvkDevice* device)
  : m_vkd(device->vkd()) { }

  DxvkGpuEventPool::~DxvkGpuEventPool() {
    for (VkEvent ev : m_events)
      m_vkd->vkDestroyEvent(m_vkd->device(), ev, nullptr);
  }

  DxvkGpuEventHandle DxvkGpuEventPool::allocEvent() {
    VkEvent event = VK_NULL_HANDLE;

    { std::lock_guard<dxvk::mutex> lock(m_mutex);

      if (m_events.size() > 0) {
        event = m_events.back();
        m_events.pop_back();
      }
    }

    if (!event) {
      VkEventCreateInfo info;
      info.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
      info.pNext = nullptr;
      info.flags = 0;

      VkResult status = m_vkd->vkCreateEvent(
        m_vkd->device(), &info, nullptr, &event);

      if (status != VK_SUCCESS) {
        Logger::err("DXVK: Failed to create GPU event");
        return DxvkGpuEventHandle();
      }
    }

    return { this, event };
  }

  void DxvkGpuEventPool::freeEvent(VkEvent event) {
    std::lock_guard<dxvk::mutex> lock(m_mutex);
    m_events.push_back(event);
  }

  DxvkGpuEventTracker::DxvkGpuEventTracker() { }
  DxvkGpuEventTracker::~DxvkGpuEventTracker() { }

  void DxvkGpuEventTracker::trackEvent(DxvkGpuEventHandle handle) {
    if (handle.pool && handle.event)
      m_handles.push_back(handle);
  }

  void DxvkGpuEventTracker::reset() {
    for (const auto& h : m_handles)
      h.pool->freeEvent(h.event);

    m_handles.clear();
  }

}


code:
#pragma once

#include <vector>

#include "dxvk_resource.h"

namespace dxvk {

  class DxvkDevice;
  class DxvkGpuEventPool;

  /**
   * \brief Event status
   *
   * Reports whether the event is in
   * a signaled or unsignaled state.
   */
  enum class DxvkGpuEventStatus : uint32_t {
    Invalid   = 0,
    Pending   = 1,
    Signaled  = 2,
  };

  /**
   * \brief Event handle
   *
   * Stores the event handle itself as well
   * as a pointer to the pool that the event
   * was allocated from.
   */
  struct DxvkGpuEventHandle {
    DxvkGpuEventPool* pool  = nullptr;
    VkEvent           event = VK_NULL_HANDLE;
  };

  /**
   * \brief GPU event
   *
   * An event managed by the GPU which allows
   * the application to check whether a specific
   * command has completed execution.
   */
  class DxvkGpuEvent : public DxvkResource {

  public:

    DxvkGpuEvent(const Rc<vk::DeviceFn>& vkd);
    ~DxvkGpuEvent();

    /**
     * \brief Retrieves event status
     *
     * Only valid after the event has been
     * recorded intro a command buffer.
     * \returns Event status
     */
    DxvkGpuEventStatus test() const;

    /**
     * \brief Resets event
     *
     * Assigns a new Vulkan event to this event
     * object and replaces the old one. The old
     * event should be freed as soon as the GPU
     * stops using it.
     * \param [in] handle New GPU event handle
     * \returns Old GPU event handle
     */
    DxvkGpuEventHandle reset(DxvkGpuEventHandle handle);

  private:

    Rc<vk::DeviceFn>   m_vkd;
    DxvkGpuEventHandle m_handle;

  };

  /**
   * \brief Event pool
   *
   * Thread-safe event allocator that provides
   * a way to create and recycle Vulkan events.
   */
  class DxvkGpuEventPool {

  public:

    DxvkGpuEventPool(const DxvkDevice* device);
    ~DxvkGpuEventPool();

    /**
     * \brief Allocates an event
     *
     * Either returns a recycled event, or
     * creates a new one if necessary. The
     * state of the event is undefined.
     * \returns An event handle
     */
    DxvkGpuEventHandle allocEvent();

    /**
     * \brief Recycles an event
     *
     * \param [in] handle Event to free
     */
    void freeEvent(VkEvent event);

  private:

    Rc<vk::DeviceFn>     m_vkd;
    dxvk::mutex          m_mutex;
    std::vector<VkEvent> m_events;

  };

  /**
   * \brief GPU event tracker
   *
   * Stores events currently accessed by the
   * GPU, and returns them to the event pool
   * once they are no longer in use.
   */
  class DxvkGpuEventTracker {

  public:

    DxvkGpuEventTracker();
    ~DxvkGpuEventTracker();

    /**
     * \brief Tracks an event
     * \param [in] handle Event to track
     */
    void trackEvent(DxvkGpuEventHandle handle);

    /**
     * \brief Resets event tracker
     *
     * Releases all tracked events back
     * to the respective event pool
     */
    void reset();

  private:

    std::vector<DxvkGpuEventHandle> m_handles;

  };

}


code:
#include <algorithm>

#include "dxvk_cmdlist.h"
#include "dxvk_device.h"
#include "dxvk_gpu_query.h"

namespace dxvk {

  DxvkGpuQuery::DxvkGpuQuery(
    const Rc<vk::DeviceFn>&   vkd,
          VkQueryType         type,
          VkQueryControlFlags flags,
          uint32_t            index)
  : m_vkd(vkd), m_type(type), m_flags(flags),
    m_index(index), m_ended(false) {

  }

  DxvkGpuQuery::~DxvkGpuQuery() {
    if (m_handle.queryPool)
      m_handle.allocator->freeQuery(m_handle);

    for (DxvkGpuQueryHandle handle : m_handles)
      handle.allocator->freeQuery(handle);
  }

  bool DxvkGpuQuery::isIndexed() const {
    return m_type == VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT;
  }

  DxvkGpuQueryStatus DxvkGpuQuery::getData(DxvkQueryData& queryData) const {
    queryData = DxvkQueryData();

    if (!m_ended)
      return DxvkGpuQueryStatus::Invalid;

    // Empty begin/end pair
    if (!m_handle.queryPool)
      return DxvkGpuQueryStatus::Available;

    // Get query data from all associated handles
    DxvkGpuQueryStatus status = getDataForHandle(queryData, m_handle);

    for (size_t i = 0; i < m_handles.size()
        && status == DxvkGpuQueryStatus::Available; i++)
      status = getDataForHandle(queryData, m_handles[i]);

    // Treat non-precise occlusion queries as available
    // if we already know the result will be non-zero
    if ((status == DxvkGpuQueryStatus::Pending)
     && (m_type == VK_QUERY_TYPE_OCCLUSION)
     && !(m_flags & VK_QUERY_CONTROL_PRECISE_BIT)
     && (queryData.occlusion.samplesPassed))
      status = DxvkGpuQueryStatus::Available;

    return status;
  }

  void DxvkGpuQuery::begin(const Rc<DxvkCommandList>& cmd) {
    m_ended = false;

    cmd->trackGpuQuery(m_handle);
    m_handle = DxvkGpuQueryHandle();

    for (const auto& handle : m_handles)
      cmd->trackGpuQuery(handle);
    m_handles.clear();
  }

  void DxvkGpuQuery::end() {
    m_ended = true;
  }

  void DxvkGpuQuery::addQueryHandle(const DxvkGpuQueryHandle& handle) {
    if (m_handle.queryPool)
      m_handles.push_back(m_handle);

    m_handle = handle;
  }

  DxvkGpuQueryStatus DxvkGpuQuery::getDataForHandle(
          DxvkQueryData&      queryData,
    const DxvkGpuQueryHandle& handle) const {
    DxvkQueryData tmpData;

    // Try to copy query data to temporary structure
    VkResult result = m_vkd->vkGetQueryPoolResults(m_vkd->device(),
      handle.queryPool, handle.queryId, 1,
      sizeof(DxvkQueryData), &tmpData,
      sizeof(DxvkQueryData), VK_QUERY_RESULT_64_BIT);

    if (result == VK_NOT_READY)
      return DxvkGpuQueryStatus::Pending;
    else if (result != VK_SUCCESS)
      return DxvkGpuQueryStatus::Failed;

    // Add numbers to the destination structure
    switch (m_type) {
      case VK_QUERY_TYPE_OCCLUSION:
        queryData.occlusion.samplesPassed += tmpData.occlusion.samplesPassed;
        break;

      case VK_QUERY_TYPE_TIMESTAMP:
        queryData.timestamp.time = tmpData.timestamp.time;
        break;

      case VK_QUERY_TYPE_PIPELINE_STATISTICS:
        queryData.statistic.iaVertices       += tmpData.statistic.iaVertices;
        queryData.statistic.iaPrimitives     += tmpData.statistic.iaPrimitives;
        queryData.statistic.vsInvocations    += tmpData.statistic.vsInvocations;
        queryData.statistic.gsInvocations    += tmpData.statistic.gsInvocations;
        queryData.statistic.gsPrimitives     += tmpData.statistic.gsPrimitives;
        queryData.statistic.clipInvocations  += tmpData.statistic.clipInvocations;
        queryData.statistic.clipPrimitives   += tmpData.statistic.clipPrimitives;
        queryData.statistic.fsInvocations    += tmpData.statistic.fsInvocations;
        queryData.statistic.tcsPatches       += tmpData.statistic.tcsPatches;
        queryData.statistic.tesInvocations   += tmpData.statistic.tesInvocations;
        queryData.statistic.csInvocations    += tmpData.statistic.csInvocations;
        break;

      case VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT:
        queryData.xfbStream.primitivesWritten += tmpData.xfbStream.primitivesWritten;
        queryData.xfbStream.primitivesNeeded  += tmpData.xfbStream.primitivesNeeded;
        break;

      default:
        Logger::err(str::format("DXVK: Unhandled query type: ", m_type));
        return DxvkGpuQueryStatus::Invalid;
    }

    return DxvkGpuQueryStatus::Available;
  }

  DxvkGpuQueryAllocator::DxvkGpuQueryAllocator(
          DxvkDevice*         device,
          VkQueryType         queryType,
          uint32_t            queryPoolSize)
  : m_device        (device),
    m_vkd           (device->vkd()),
    m_queryType     (queryType),
    m_queryPoolSize (queryPoolSize) {

  }

  DxvkGpuQueryAllocator::~DxvkGpuQueryAllocator() {
    for (VkQueryPool pool : m_pools) {
      m_vkd->vkDestroyQueryPool(
        m_vkd->device(), pool, nullptr);
    }
  }

  DxvkGpuQueryHandle DxvkGpuQueryAllocator::allocQuery() {
    std::lock_guard<dxvk::mutex> lock(m_mutex);

    if (m_handles.size() == 0)
      this->createQueryPool();

    if (m_handles.size() == 0)
      return DxvkGpuQueryHandle();

    DxvkGpuQueryHandle result = m_handles.back();
    m_handles.pop_back();
    return result;
  }

  void DxvkGpuQueryAllocator::freeQuery(DxvkGpuQueryHandle handle) {
    std::lock_guard<dxvk::mutex> lock(m_mutex);
    m_handles.push_back(handle);
  }

  void DxvkGpuQueryAllocator::createQueryPool() {
    VkQueryPoolCreateInfo info;
    info.sType      = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
    info.pNext      = nullptr;
    info.flags      = 0;
    info.queryType  = m_queryType;
    info.queryCount = m_queryPoolSize;
    info.pipelineStatistics = 0;

    if (m_queryType == VK_QUERY_TYPE_PIPELINE_STATISTICS) {
      info.pipelineStatistics
        = VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_VERTICES_BIT
        | VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT
        | VK_QUERY_PIPELINE_STATISTIC_VERTEX_SHADER_INVOCATIONS_BIT
        | VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_INVOCATIONS_BIT
        | VK_QUERY_PIPELINE_STATISTIC_GEOMETRY_SHADER_PRIMITIVES_BIT
        | VK_QUERY_PIPELINE_STATISTIC_CLIPPING_INVOCATIONS_BIT
        | VK_QUERY_PIPELINE_STATISTIC_CLIPPING_PRIMITIVES_BIT
        | VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT
        | VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_CONTROL_SHADER_PATCHES_BIT
        | VK_QUERY_PIPELINE_STATISTIC_TESSELLATION_EVALUATION_SHADER_INVOCATIONS_BIT
        | VK_QUERY_PIPELINE_STATISTIC_COMPUTE_SHADER_INVOCATIONS_BIT;
    }

    VkQueryPool queryPool = VK_NULL_HANDLE;

    if (m_vkd->vkCreateQueryPool(m_vkd->device(), &info, nullptr, &queryPool)) {
      Logger::err(str::format("DXVK: Failed to create query pool (", m_queryType, "; ", m_queryPoolSize, ")"));
      return;
    }

    m_pools.push_back(queryPool);

    for (uint32_t i = 0; i < m_queryPoolSize; i++)
      m_handles.push_back({ this, queryPool, i });
  }

  DxvkGpuQueryPool::DxvkGpuQueryPool(DxvkDevice* device)
  : m_occlusion(device, VK_QUERY_TYPE_OCCLUSION,                     16384),
    m_statistic(device, VK_QUERY_TYPE_PIPELINE_STATISTICS,           1024),
    m_timestamp(device, VK_QUERY_TYPE_TIMESTAMP,                     1024),
    m_xfbStream(device, VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT, 1024) {

  }

  DxvkGpuQueryPool::~DxvkGpuQueryPool() {

  }

  DxvkGpuQueryHandle DxvkGpuQueryPool::allocQuery(VkQueryType type) {
    switch (type) {
      case VK_QUERY_TYPE_OCCLUSION:
        return m_occlusion.allocQuery();
      case VK_QUERY_TYPE_PIPELINE_STATISTICS:
        return m_statistic.allocQuery();
      case VK_QUERY_TYPE_TIMESTAMP:
        return m_timestamp.allocQuery();
      case VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT:
        return m_xfbStream.allocQuery();
      default:
        Logger::err(str::format("DXVK: Unhandled query type: ", type));
        return DxvkGpuQueryHandle();
    }
  }

  DxvkGpuQueryManager::DxvkGpuQueryManager(DxvkGpuQueryPool& pool)
  : m_pool(&pool), m_activeTypes(0) {

  }

  DxvkGpuQueryManager::~DxvkGpuQueryManager() {

  }

  void DxvkGpuQueryManager::enableQuery(
    const Rc<DxvkCommandList>&  cmd,
    const Rc<DxvkGpuQuery>&     query) {
    query->begin(cmd);

    m_activeQueries.push_back(query);

    if (m_activeTypes & getQueryTypeBit(query->type()))
      beginSingleQuery(cmd, query);
  }

  void DxvkGpuQueryManager::disableQuery(
    const Rc<DxvkCommandList>&  cmd,
    const Rc<DxvkGpuQuery>&     query) {
    auto iter = std::find(
      m_activeQueries.begin(),
      m_activeQueries.end(),
      query);

    if (iter != m_activeQueries.end()) {
      if (m_activeTypes & getQueryTypeBit((*iter)->type()))
        endSingleQuery(cmd, query);
      m_activeQueries.erase(iter);

      query->end();
    }
  }

  void DxvkGpuQueryManager::writeTimestamp(
    const Rc<DxvkCommandList>&  cmd,
    const Rc<DxvkGpuQuery>&     query) {
    DxvkGpuQueryHandle handle = m_pool->allocQuery(query->type());

    query->begin(cmd);
    query->addQueryHandle(handle);
    query->end();

    cmd->resetQuery(
      handle.queryPool,
      handle.queryId);

    cmd->cmdWriteTimestamp(
      VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
      handle.queryPool,
      handle.queryId);

    cmd->trackResource<DxvkAccess::None>(query);
  }

  void DxvkGpuQueryManager::beginQueries(
    const Rc<DxvkCommandList>&  cmd,
          VkQueryType           type) {
    m_activeTypes |= getQueryTypeBit(type);

    for (size_t i = 0; i < m_activeQueries.size(); i++) {
      if (m_activeQueries[i]->type() == type)
        beginSingleQuery(cmd, m_activeQueries[i]);
    }
  }

  void DxvkGpuQueryManager::endQueries(
    const Rc<DxvkCommandList>&  cmd,
          VkQueryType           type) {
    m_activeTypes &= ~getQueryTypeBit(type);

    for (size_t i = 0; i < m_activeQueries.size(); i++) {
      if (m_activeQueries[i]->type() == type)
        endSingleQuery(cmd, m_activeQueries[i]);
    }
  }

  void DxvkGpuQueryManager::beginSingleQuery(
    const Rc<DxvkCommandList>&  cmd,
    const Rc<DxvkGpuQuery>&     query) {
    DxvkGpuQueryHandle handle = m_pool->allocQuery(query->type());

    cmd->resetQuery(
      handle.queryPool,
      handle.queryId);

    if (query->isIndexed()) {
      cmd->cmdBeginQueryIndexed(
        handle.queryPool,
        handle.queryId,
        query->flags(),
        query->index());
    } else {
      cmd->cmdBeginQuery(
        handle.queryPool,
        handle.queryId,
        query->flags());
    }

    query->addQueryHandle(handle);
  }

  void DxvkGpuQueryManager::endSingleQuery(
    const Rc<DxvkCommandList>&  cmd,
    const Rc<DxvkGpuQuery>&     query) {
    DxvkGpuQueryHandle handle = query->handle();

    if (query->isIndexed()) {
      cmd->cmdEndQueryIndexed(
        handle.queryPool,
        handle.queryId,
        query->index());
    } else {
      cmd->cmdEndQuery(
        handle.queryPool,
        handle.queryId);
    }

    cmd->trackResource<DxvkAccess::None>(query);
  }

  uint32_t DxvkGpuQueryManager::getQueryTypeBit(
          VkQueryType           type) {
    switch (type) {
      case VK_QUERY_TYPE_OCCLUSION:                     return 0x01;
      case VK_QUERY_TYPE_PIPELINE_STATISTICS:           return 0x02;
      case VK_QUERY_TYPE_TIMESTAMP:                     return 0x04;
      case VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT: return 0x08;
      default:                                          return 0;
    }
  }

  DxvkGpuQueryTracker::DxvkGpuQueryTracker() { }
  DxvkGpuQueryTracker::~DxvkGpuQueryTracker() { }

  void DxvkGpuQueryTracker::trackQuery(DxvkGpuQueryHandle handle) {
    if (handle.queryPool)
      m_handles.push_back(handle);
  }

  void DxvkGpuQueryTracker::reset() {
    for (DxvkGpuQueryHandle handle : m_handles)
      handle.allocator->freeQuery(handle);

    m_handles.clear();
  }

}


code:
#pragma once

#include <mutex>
#include <vector>

#include "dxvk_resource.h"

namespace dxvk {

  class DxvkCommandList;

  class DxvkGpuQueryPool;
  class DxvkGpuQueryAllocator;

  /**
   * \brief Query status
   *
   * Reports whether a query is in
   * signaled or unsignaled state.
   */
  enum class DxvkGpuQueryStatus : uint32_t {
    Invalid   = 0,
    Pending   = 1,
    Available = 2,
    Failed    = 3,
  };

  /**
   * \brief Occlusion query data
   *
   * Stores the number of samples
   * that passes fragment tests.
   */
  struct DxvkQueryOcclusionData {
    uint64_t samplesPassed;
  };

  /**
   * \brief Timestamp data
   *
   * Stores a GPU time stamp.
   */
  struct DxvkQueryTimestampData {
    uint64_t time;
  };

  /**
   * \brief Pipeline statistics
   *
   * Stores the counters for
   * pipeline statistics queries.
   */
  struct DxvkQueryStatisticData {
    uint64_t iaVertices;
    uint64_t iaPrimitives;
    uint64_t vsInvocations;
    uint64_t gsInvocations;
    uint64_t gsPrimitives;
    uint64_t clipInvocations;
    uint64_t clipPrimitives;
    uint64_t fsInvocations;
    uint64_t tcsPatches;
    uint64_t tesInvocations;
    uint64_t csInvocations;
  };

  /**
   * \brief Transform feedback stream query
   *
   * Stores the number of primitives written to the
   * buffer, as well as the number of primitives
   * generated. The latter can be used to check for
   * overflow.
   */
  struct DxvkQueryXfbStreamData {
    uint64_t primitivesWritten;
    uint64_t primitivesNeeded;
  };

  /**
   * \brief Query data
   *
   * A union that stores query data. Select an
   * appropriate member based on the query type.
   */
  union DxvkQueryData {
    DxvkQueryOcclusionData occlusion;
    DxvkQueryTimestampData timestamp;
    DxvkQueryStatisticData statistic;
    DxvkQueryXfbStreamData xfbStream;
  };

  /**
   * \brief Query handle
   *
   * Stores the query allocator, as well as
   * the actual pool and query index. Since
   * query pools have to be reset on the GPU,
   * this also comes with a reset event.
   */
  struct DxvkGpuQueryHandle {
    DxvkGpuQueryAllocator* allocator  = nullptr;
    VkQueryPool            queryPool  = VK_NULL_HANDLE;
    uint32_t               queryId    = 0;
  };

  /**
   * \brief Query object
   *
   * Manages Vulkan queries that are sub-allocated
   * from larger query pools
   */
  class DxvkGpuQuery : public DxvkResource {

  public:

    DxvkGpuQuery(
      const Rc<vk::DeviceFn>&   vkd,
            VkQueryType         type,
            VkQueryControlFlags flags,
            uint32_t            index);

    ~DxvkGpuQuery();

    /**
     * \brief Query type
     * \returns Query type
     */
    VkQueryType type() const {
      return m_type;
    }

    /**
     * \brief Query control flags
     * \returns Query control flags
     */
    VkQueryControlFlags flags() const {
      return m_flags;
    }

    /**
     * \brief Retrieves current handle
     *
     * Note that the query handle will change
     * when calling \ref addQueryHandle.
     * \returns Current query handle
     */
    DxvkGpuQueryHandle handle() const {
      return m_handle;
    }

    /**
     * \brief Query index
     *
     * Only valid for indexed query types.
     * For non-zero values, indexed query
     * functions must be used.
     * \returns Query index
     */
    uint32_t index() const {
      return m_index;
    }

    /**
     * \brief Checks whether query is indexed
     * \returns \c true for indexed query types
     */
    bool isIndexed() const;

    /**
     * \brief Retrieves query data
     *
     * If all query data is available, this will
     * return \c DxvkGpuQueryStatus::Signaled, and
     * the destination structure will be filled
     * with the data retrieved from all associated
     * query handles.
     * \param [out] queryData Query data
     * \returns Current query status
     */
    DxvkGpuQueryStatus getData(
            DxvkQueryData&      queryData) const;

    /**
     * \brief Begins query
     *
     * Moves all current query handles to the given
     * command list and sets the query into active
     * state. No data can be retrieved while the
     * query is active.
     * \param [in] cmd Command list
     */
    void begin(
      const Rc<DxvkCommandList>& cmd);

    /**
     * \brief Ends query
     *
     * Sets query into pending state. Calling
     * \c getData is legal after calling this.
     */
    void end();

    /**
     * \brief Adds a query handle to the query
     *
     * The given query handle shall be used when
     * retrieving query data. A query can have
     * multiple handles attached.
     * \param [in] handle The query handle
     */
    void addQueryHandle(
      const DxvkGpuQueryHandle& handle);

  private:

    Rc<vk::DeviceFn>    m_vkd;

    VkQueryType         m_type;
    VkQueryControlFlags m_flags;
    uint32_t            m_index;
    bool                m_ended;

    DxvkGpuQueryHandle  m_handle;

    std::vector<DxvkGpuQueryHandle> m_handles;

    DxvkGpuQueryStatus getDataForHandle(
            DxvkQueryData&      queryData,
      const DxvkGpuQueryHandle& handle) const;

  };

  /**
   * \brief Query allocator
   *
   * Creates query pools and allocates
   * queries for a single query type.
   */
  class DxvkGpuQueryAllocator {

  public:

    DxvkGpuQueryAllocator(
            DxvkDevice*         device,
            VkQueryType         queryType,
            uint32_t            queryPoolSize);

    ~DxvkGpuQueryAllocator();

    /**
     * \brief Allocates a query
     *
     * If possible, this returns a free query
     * from an existing query pool. Otherwise,
     * a new query pool will be created.
     * \returns Query handle
     */
    DxvkGpuQueryHandle allocQuery();

    /**
     * \brief Recycles a query
     *
     * Returns a query back to the allocator
     * so that it can be reused. The query
     * must not be in pending state.
     * \param [in] handle Query to reset
     */
    void freeQuery(DxvkGpuQueryHandle handle);

  private:

    DxvkDevice*       m_device;
    Rc<vk::DeviceFn>  m_vkd;
    VkQueryType       m_queryType;
    uint32_t          m_queryPoolSize;

    dxvk::mutex                     m_mutex;
    std::vector<DxvkGpuQueryHandle> m_handles;
    std::vector<VkQueryPool>        m_pools;

    void createQueryPool();

  };

  /**
   * \brief Query pool
   *
   * Small wrapper class that manages query
   * allocators for all supported query types,
   */
  class DxvkGpuQueryPool {

  public:

    DxvkGpuQueryPool(DxvkDevice* device);

    ~DxvkGpuQueryPool();

    /**
     * \brief Allocates a single query
     *
     * \param [in] type Query type
     * \returns Handle to the allocated query
     */
    DxvkGpuQueryHandle allocQuery(VkQueryType type);

  private:

    DxvkGpuQueryAllocator m_occlusion;
    DxvkGpuQueryAllocator m_statistic;
    DxvkGpuQueryAllocator m_timestamp;
    DxvkGpuQueryAllocator m_xfbStream;

  };

  /**
   * \brief Query manager
   *
   * Keeps track of enabled and disabled queries
   * and assigns Vulkan queries to them as needed.
   */
  class DxvkGpuQueryManager {

  public:

    DxvkGpuQueryManager(DxvkGpuQueryPool& pool);

    ~DxvkGpuQueryManager();

    /**
     * \brief Enables a query
     *
     * This will also immediately begin the
     * query in case the query type is active.
     * \param [in] cmd Command list
     * \param [in] query Query to allocate
     */
    void enableQuery(
      const Rc<DxvkCommandList>&  cmd,
      const Rc<DxvkGpuQuery>&     query);

    /**
     * \brief Disables a query
     *
     * This will also immediately end the
     * query in case the query type is active.
     * \param [in] cmd Command list
     * \param [in] query Query to allocate
     */
    void disableQuery(
      const Rc<DxvkCommandList>&  cmd,
      const Rc<DxvkGpuQuery>&     query);

    /**
     * \brief Signals a time stamp query
     *
     * Timestamp queries are not scoped.
     * \param [in] cmd Command list
     * \param [in] query Query to allocate
     */
    void writeTimestamp(
      const Rc<DxvkCommandList>&  cmd,
      const Rc<DxvkGpuQuery>&     query);

    /**
     * \brief Begins queries of a given type
     *
     * Makes a query type \e active. Begins
     * all enabled queries of this type.
     * \param [in] cmd Command list
     * \param [in] type Query type
     */
    void beginQueries(
      const Rc<DxvkCommandList>&  cmd,
            VkQueryType           type);

    /**
     * \brief Ends queries of a given type
     *
     * Makes a query type \e inactive. Ends
     * all enabled queries of this type.
     * \param [in] cmd Command list
     * \param [in] type Query type
     */
    void endQueries(
      const Rc<DxvkCommandList>&  cmd,
            VkQueryType           type);

  private:

    DxvkGpuQueryPool*             m_pool;
    uint32_t                      m_activeTypes;
    std::vector<Rc<DxvkGpuQuery>> m_activeQueries;

    void beginSingleQuery(
      const Rc<DxvkCommandList>&  cmd,
      const Rc<DxvkGpuQuery>&     query);

    void endSingleQuery(
      const Rc<DxvkCommandList>&  cmd,
      const Rc<DxvkGpuQuery>&     query);

    static uint32_t getQueryTypeBit(
            VkQueryType           type);

  };

  /**
   * \brief Query tracker
   *
   * Returns queries to their allocators after
   * the command buffer has finished executing.
   */
  class DxvkGpuQueryTracker {

  public:

    DxvkGpuQueryTracker();
    ~DxvkGpuQueryTracker();

    /**
     * \param Tracks a query
     * \param [in] handle Query handle
     */
    void trackQuery(DxvkGpuQueryHandle handle);

    /**
     * \brief Recycles all tracked handles
     *
     * Releases all tracked query handles
     * to their respective query allocator.
     */
    void reset();

  private:

    std::vector<DxvkGpuQueryHandle> m_handles;

  };
}