code:
#include "dxvk_instance.h"
#include "dxvk_openvr.h"

#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
#endif

#include <openvr/openvr.hpp>

using VR_InitInternalProc        = vr::IVRSystem* (VR_CALLTYPE *)(vr::EVRInitError*, vr::EVRApplicationType);
using VR_ShutdownInternalProc    = void  (VR_CALLTYPE *)();
using VR_GetGenericInterfaceProc = void* (VR_CALLTYPE *)(const char*, vr::EVRInitError*);

namespace dxvk {

  struct VrFunctions {
    VR_InitInternalProc        initInternal        = nullptr;
    VR_ShutdownInternalProc    shutdownInternal    = nullptr;
    VR_GetGenericInterfaceProc getGenericInterface = nullptr;
  };

  VrFunctions g_vrFunctions;
  VrInstance VrInstance::s_instance;

  VrInstance:: VrInstance() {
    m_no_vr = env::getEnvVar("DXVK_NO_VR") == "1";
  }
  VrInstance::~VrInstance() { }

  std::string_view VrInstance::getName() {
    return "OpenVR";
  }

  DxvkNameSet VrInstance::getInstanceExtensions() {
    std::lock_guard<dxvk::mutex> lock(m_mutex);
    return m_insExtensions;
  }

  DxvkNameSet VrInstance::getDeviceExtensions(uint32_t adapterId) {
    std::lock_guard<dxvk::mutex> lock(m_mutex);

    if (adapterId < m_devExtensions.size())
      return m_devExtensions[adapterId];

    return DxvkNameSet();
  }

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

    if (m_no_vr || m_initializedDevExt)
        return;

    if (!m_vr_key)
    {
        LSTATUS status;

        if ((status = RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Wine\\VR", 0, KEY_READ, &m_vr_key)))
            Logger::info(str::format("OpenVR: could not open registry key, status ", status));
    }

    if (!m_vr_key && !m_compositor)
      m_compositor = this->getCompositor();

    if (!m_vr_key && !m_compositor)
      return;

    m_insExtensions = this->queryInstanceExtensions();
    m_initializedInsExt = true;
  }

  void VrInstance::initDeviceExtensions(const DxvkInstance* instance) {
    std::lock_guard<dxvk::mutex> lock(m_mutex);

    if (m_no_vr || (!m_vr_key && !m_compositor) || m_initializedDevExt)
      return;

    for (uint32_t i = 0; instance->enumAdapters(i) != nullptr; i++) {
      m_devExtensions.push_back(this->queryDeviceExtensions(
        instance->enumAdapters(i)));
    }

    m_initializedDevExt = true;
    this->shutdown();
  }

  bool VrInstance::waitVrKeyReady() const {
    DWORD type, value, wait_status, size;
    LSTATUS status;
    HANDLE event;

    size = sizeof(value);
    if ((status = RegQueryValueExA(m_vr_key, "state", nullptr, &type, reinterpret_cast<BYTE*>(&value), &size)))
    {
        Logger::err(str::format("OpenVR: could not query value, status ", status));
        return false;
    }
    if (type != REG_DWORD)
    {
        Logger::err(str::format("OpenVR: unexpected value type ", type));
        return false;
    }

    if (value)
        return value == 1;

    event = CreateEventA(nullptr, FALSE, FALSE, nullptr);
    while (1)
    {
        if (RegNotifyChangeKeyValue(m_vr_key, FALSE, REG_NOTIFY_CHANGE_LAST_SET, event, TRUE))
        {
            Logger::err("Error registering registry change notification");
            goto done;
        }
        size = sizeof(value);
        if ((status = RegQueryValueExA(m_vr_key, "state", nullptr, &type, reinterpret_cast<BYTE*>(&value), &size)))
        {
            Logger::err(str::format("OpenVR: could not query value, status ", status));
            goto done;
        }
        if (value)
            break;
        while ((wait_status = WaitForSingleObject(event, 1000)) == WAIT_TIMEOUT)
            Logger::warn("VR state wait timeout (retrying)");

        if (wait_status != WAIT_OBJECT_0)
        {
            Logger::err(str::format("Got unexpected wait status ", wait_status));
            break;
        }
    }

  done:
    CloseHandle(event);
    return value == 1;
  }

  DxvkNameSet VrInstance::queryInstanceExtensions() const {
    std::vector<char> extensionList;
    DWORD len;

    if (m_vr_key)
    {
        LSTATUS status;
        DWORD type;

        if (!this->waitVrKeyReady())
            return DxvkNameSet();

        len = 0;
        if ((status = RegQueryValueExA(m_vr_key, "openvr_vulkan_instance_extensions", nullptr, &type, nullptr, &len)))
        {
            Logger::err(str::format("OpenVR: could not query value, status ", status));
            return DxvkNameSet();
        }
        extensionList.resize(len);
        if ((status = RegQueryValueExA(m_vr_key, "openvr_vulkan_instance_extensions", nullptr, &type, reinterpret_cast<BYTE*>(extensionList.data()), &len)))
        {
            Logger::err(str::format("OpenVR: could not query value, status ", status));
            return DxvkNameSet();
        }
    }
    else
    {
        len = m_compositor->GetVulkanInstanceExtensionsRequired(nullptr, 0);
        extensionList.resize(len);
        len = m_compositor->GetVulkanInstanceExtensionsRequired(extensionList.data(), len);
    }
    return parseExtensionList(std::string(extensionList.data(), len));
  }

  DxvkNameSet VrInstance::queryDeviceExtensions(Rc<DxvkAdapter> adapter) const {
    std::vector<char> extensionList;
    DWORD len;

    if (m_vr_key)
    {
        LSTATUS status;
        char name[256];
        DWORD type;

        if (!this->waitVrKeyReady())
            return DxvkNameSet();

        sprintf(name, "PCIID:%04x:%04x", adapter->deviceProperties().vendorID, adapter->deviceProperties().deviceID);
        len = 0;
        if ((status = RegQueryValueExA(m_vr_key, name, nullptr, &type, nullptr, &len)))
        {
            Logger::err(str::format("OpenVR: could not query value, status ", status));
            return DxvkNameSet();
        }
        extensionList.resize(len);
        if ((status = RegQueryValueExA(m_vr_key, name, nullptr, &type, reinterpret_cast<BYTE*>(extensionList.data()), &len)))
        {
            Logger::err(str::format("OpenVR: could not query value, status ", status));
            return DxvkNameSet();
        }
    }
    else
    {
        len = m_compositor->GetVulkanDeviceExtensionsRequired(adapter->handle(), nullptr, 0);
        extensionList.resize(len);
        len = m_compositor->GetVulkanDeviceExtensionsRequired(adapter->handle(), extensionList.data(), len);
    }
    return parseExtensionList(std::string(extensionList.data(), len));
  }

  DxvkNameSet VrInstance::parseExtensionList(const std::string& str) const {
    DxvkNameSet result;

    std::stringstream strstream(str);
    std::string       section;

    while (std::getline(strstream, section, ' '))
      result.add(section.c_str());

    return result;
  }

  vr::IVRCompositor* VrInstance::getCompositor() {
    // Skip OpenVR initialization if requested

    // Locate the OpenVR DLL if loaded by the process. Some
    // applications may not have OpenVR loaded at the time
    // they create the DXGI instance, so we try our own DLL.
    m_ovrApi = this->loadLibrary();

    if (!m_ovrApi) {
      Logger::info("OpenVR: Failed to locate module");
      return nullptr;
    }

    // Load method used to retrieve the IVRCompositor interface
    g_vrFunctions.initInternal        = reinterpret_cast<VR_InitInternalProc>       (this->getSym("VR_InitInternal"));
    g_vrFunctions.shutdownInternal    = reinterpret_cast<VR_ShutdownInternalProc>   (this->getSym("VR_ShutdownInternal"));
    g_vrFunctions.getGenericInterface = reinterpret_cast<VR_GetGenericInterfaceProc>(this->getSym("VR_GetGenericInterface"));

    if (!g_vrFunctions.getGenericInterface) {
      Logger::warn("OpenVR: VR_GetGenericInterface not found");
      return nullptr;
    }

    // Retrieve the compositor interface
    vr::EVRInitError error = vr::VRInitError_None;

    vr::IVRCompositor* compositor = reinterpret_cast<vr::IVRCompositor*>(
      g_vrFunctions.getGenericInterface(vr::IVRCompositor_Version, &error));

    if (error != vr::VRInitError_None || !compositor) {
      if (!g_vrFunctions.initInternal
       || !g_vrFunctions.shutdownInternal) {
        Logger::warn("OpenVR: VR_InitInternal or VR_ShutdownInternal not found");
        return nullptr;
      }

      // If the app has not initialized OpenVR yet, we need
      // to do it now in order to grab a compositor instance
      g_vrFunctions.initInternal(&error, vr::VRApplication_Background);
      m_initializedOpenVr = error == vr::VRInitError_None;

      if (error != vr::VRInitError_None) {
        Logger::warn("OpenVR: Failed to initialize OpenVR");
        return nullptr;
      }

      compositor = reinterpret_cast<vr::IVRCompositor*>(
        g_vrFunctions.getGenericInterface(vr::IVRCompositor_Version, &error));

      if (error != vr::VRInitError_None || !compositor) {
        Logger::warn("OpenVR: Failed to query compositor interface");
        this->shutdown();
        return nullptr;
      }
    }

    Logger::info("OpenVR: Compositor interface found");
    return compositor;
  }

  void VrInstance::shutdown() {
    if (m_vr_key)
    {
        RegCloseKey(m_vr_key);
        m_vr_key = nullptr;
    }

    if (m_initializedOpenVr)
      g_vrFunctions.shutdownInternal();

    if (m_loadedOvrApi)
      this->freeLibrary();

    m_initializedOpenVr = false;
    m_loadedOvrApi      = false;
  }

  HMODULE VrInstance::loadLibrary() {
    HMODULE handle = nullptr;
    if (!(handle = ::GetModuleHandle("openvr_api.dll"))) {
      handle = ::LoadLibrary("openvr_api_dxvk.dll");
      m_loadedOvrApi = handle != nullptr;
    }
    return handle;
  }

  void VrInstance::freeLibrary() {
    ::FreeLibrary(m_ovrApi);
  }

  void* VrInstance::getSym(const char* sym) {
    return reinterpret_cast<void*>(
      ::GetProcAddress(m_ovrApi, sym));
  }

}


code:
#pragma once

#include <mutex>
#include <vector>

#include "dxvk_extension_provider.h"

namespace vr {
  class IVRCompositor;
  class IVRSystem;
}

namespace dxvk {

  class DxvkInstance;

  /**
   * \brief OpenVR instance
   *
   * Loads Initializes OpenVR to provide
   * access to Vulkan extension queries.
   */
  class VrInstance : public DxvkExtensionProvider {

  public:

    VrInstance();
    ~VrInstance();

    std::string_view getName();

    DxvkNameSet getInstanceExtensions();

    DxvkNameSet getDeviceExtensions(
            uint32_t      adapterId);

    void initInstanceExtensions();

    void initDeviceExtensions(
      const DxvkInstance* instance);

    static VrInstance s_instance;

  private:

    dxvk::mutex           m_mutex;
    HKEY                  m_vr_key     = nullptr;
    vr::IVRCompositor*    m_compositor = nullptr;
    HMODULE               m_ovrApi     = nullptr;

    bool m_no_vr;
    bool m_loadedOvrApi      = false;
    bool m_initializedOpenVr = false;
    bool m_initializedInsExt = false;
    bool m_initializedDevExt = false;

    DxvkNameSet              m_insExtensions;
    std::vector<DxvkNameSet> m_devExtensions;

    DxvkNameSet queryInstanceExtensions() const;

    DxvkNameSet queryDeviceExtensions(
            Rc<DxvkAdapter>           adapter) const;

    DxvkNameSet parseExtensionList(
      const std::string&              str) const;

    vr::IVRCompositor* getCompositor();

    void shutdown();

    HMODULE loadLibrary();

    void freeLibrary();

    void* getSym(const char* sym);

    bool waitVrKeyReady() const;
  };

  extern VrInstance g_vrInstance;

}

repo_name: orochi/dxvk


code:
#include "dxvk_instance.h"
#include "dxvk_openxr.h"

#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
#endif

using PFN___wineopenxr_GetVulkanInstanceExtensions = int (WINAPI *)(uint32_t, uint32_t *, char *);
using PFN___wineopenxr_GetVulkanDeviceExtensions = int (WINAPI *)(uint32_t, uint32_t *, char *);

namespace dxvk {

  struct WineXrFunctions {
    PFN___wineopenxr_GetVulkanInstanceExtensions __wineopenxr_GetVulkanInstanceExtensions = nullptr;
    PFN___wineopenxr_GetVulkanDeviceExtensions __wineopenxr_GetVulkanDeviceExtensions = nullptr;
  };

  WineXrFunctions g_winexrFunctions;
  DxvkXrProvider DxvkXrProvider::s_instance;

  DxvkXrProvider:: DxvkXrProvider() { }

  DxvkXrProvider::~DxvkXrProvider() { }

  std::string_view DxvkXrProvider::getName() {
    return "OpenXR";
  }

  DxvkNameSet DxvkXrProvider::getInstanceExtensions() {
    std::lock_guard<dxvk::mutex> lock(m_mutex);
    return m_insExtensions;
  }

  DxvkNameSet DxvkXrProvider::getDeviceExtensions(uint32_t adapterId) {
    std::lock_guard<dxvk::mutex> lock(m_mutex);
    return m_devExtensions;
  }

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

    if (!m_wineOxr)
      m_wineOxr = this->loadLibrary();

    if (!m_wineOxr || m_initializedInsExt)
      return;

    if (!this->loadFunctions()) {
      this->shutdown();
      return;
    }

    m_insExtensions = this->queryInstanceExtensions();
    m_initializedInsExt = true;
  }

  bool DxvkXrProvider::loadFunctions() {
    g_winexrFunctions.__wineopenxr_GetVulkanInstanceExtensions =
        reinterpret_cast<PFN___wineopenxr_GetVulkanInstanceExtensions>(this->getSym("__wineopenxr_GetVulkanInstanceExtensions"));
    g_winexrFunctions.__wineopenxr_GetVulkanDeviceExtensions =
        reinterpret_cast<PFN___wineopenxr_GetVulkanDeviceExtensions>(this->getSym("__wineopenxr_GetVulkanDeviceExtensions"));
    return g_winexrFunctions.__wineopenxr_GetVulkanInstanceExtensions != nullptr
      && g_winexrFunctions.__wineopenxr_GetVulkanDeviceExtensions != nullptr;
  }

  void DxvkXrProvider::initDeviceExtensions(const DxvkInstance* instance) {
    std::lock_guard<dxvk::mutex> lock(m_mutex);

    if (!m_wineOxr || m_initializedDevExt)
      return;

    m_devExtensions = this->queryDeviceExtensions();
    m_initializedDevExt = true;

    this->shutdown();
  }

  DxvkNameSet DxvkXrProvider::queryInstanceExtensions() const {
    int res;
    uint32_t len;

    res = g_winexrFunctions.__wineopenxr_GetVulkanInstanceExtensions(0, &len, nullptr);
    if (res != 0) {
      Logger::warn("OpenXR: Unable to get required Vulkan instance extensions size");
      return DxvkNameSet();
    }

    std::vector<char> extensionList(len);
    res = g_winexrFunctions.__wineopenxr_GetVulkanInstanceExtensions(len, &len, &extensionList[0]);
    if (res != 0) {
      Logger::warn("OpenXR: Unable to get required Vulkan instance extensions");
      return DxvkNameSet();
    }

    return parseExtensionList(std::string(extensionList.data(), len));
  }

  DxvkNameSet DxvkXrProvider::queryDeviceExtensions() const {
    int res;

    uint32_t len;
    res = g_winexrFunctions.__wineopenxr_GetVulkanDeviceExtensions(0, &len, nullptr);
    if (res != 0) {
      Logger::warn("OpenXR: Unable to get required Vulkan Device extensions size");
      return DxvkNameSet();
    }

    std::vector<char> extensionList(len);
    res = g_winexrFunctions.__wineopenxr_GetVulkanDeviceExtensions(len, &len, &extensionList[0]);
    if (res != 0) {
      Logger::warn("OpenXR: Unable to get required Vulkan Device extensions");
      return DxvkNameSet();
    }

    return parseExtensionList(std::string(extensionList.data(), len));
  }

  DxvkNameSet DxvkXrProvider::parseExtensionList(const std::string& str) const {
    DxvkNameSet result;

    std::stringstream strstream(str);
    std::string       section;

    while (std::getline(strstream, section, ' '))
      result.add(section.c_str());

    return result;
  }

  void DxvkXrProvider::shutdown() {
    if (m_loadedOxrApi)
      this->freeLibrary();

    m_loadedOxrApi      = false;
    m_wineOxr = nullptr;
  }

  HMODULE DxvkXrProvider::loadLibrary() {
    HMODULE handle = nullptr;
    if (!(handle = ::GetModuleHandle("wineopenxr.dll"))) {
      handle = ::LoadLibrary("wineopenxr.dll");
      m_loadedOxrApi = handle != nullptr;
    }
    return handle;
  }

  void DxvkXrProvider::freeLibrary() {
    ::FreeLibrary(m_wineOxr);
  }

  void* DxvkXrProvider::getSym(const char* sym) {
    return reinterpret_cast<void*>(
      ::GetProcAddress(m_wineOxr, sym));
  }
}


code:
#pragma once

#include <mutex>
#include <vector>

#include "dxvk_extension_provider.h"

namespace dxvk {

  class DxvkInstance;

  /**
   * \brief OpenXR instance
   *
   * Loads OpenXR to provide access to Vulkan extension queries.
   */
  class DxvkXrProvider : public DxvkExtensionProvider {

  public:

    DxvkXrProvider();
    ~DxvkXrProvider();

    std::string_view getName();

    DxvkNameSet getInstanceExtensions();

    DxvkNameSet getDeviceExtensions(
            uint32_t      adapterId);

    void initInstanceExtensions();

    void initDeviceExtensions(
      const DxvkInstance* instance);

    static DxvkXrProvider s_instance;

  private:

    dxvk::mutex           m_mutex;
    HMODULE               m_wineOxr     = nullptr;

    bool m_loadedOxrApi      = false;
    bool m_initializedInsExt = false;
    bool m_initializedDevExt = false;

    DxvkNameSet              m_insExtensions;
    DxvkNameSet              m_devExtensions;

    DxvkNameSet queryInstanceExtensions() const;

    DxvkNameSet queryDeviceExtensions() const;

    DxvkNameSet parseExtensionList(
      const std::string&              str) const;

    bool loadFunctions();

    void shutdown();

    HMODULE loadLibrary();

    void freeLibrary();

    void* getSym(const char* sym);

  };

}


code:
#include "dxvk_options.h"

namespace dxvk {

  DxvkOptions::DxvkOptions(const Config& config) {
    enableDebugUtils      = config.getOption<bool>    ("dxvk.enableDebugUtils",       false);
    enableStateCache      = config.getOption<bool>    ("dxvk.enableStateCache",       true);
    numCompilerThreads    = config.getOption<int32_t> ("dxvk.numCompilerThreads",     0);
    useRawSsbo            = config.getOption<Tristate>("dxvk.useRawSsbo",             Tristate::Auto);
    shrinkNvidiaHvvHeap   = config.getOption<Tristate>("dxvk.shrinkNvidiaHvvHeap",    Tristate::Auto);
    hud                   = config.getOption<std::string>("dxvk.hud", "");
  }

}


code:
#pragma once

#include "../util/config/config.h"

namespace dxvk {

  struct DxvkOptions {
    DxvkOptions() { }
    DxvkOptions(const Config& config);

    /// Enable debug utils (alternative to DXVK_PERF_EVENTS=1)
    bool enableDebugUtils;

    /// Enable state cache
    bool enableStateCache;

    /// Number of compiler threads
    /// when using the state cache
    int32_t numCompilerThreads;

    /// Shader-related options
    Tristate useRawSsbo;

    /// Workaround for NVIDIA driver bug 3114283
    Tristate shrinkNvidiaHvvHeap;

    /// HUD elements
    std::string hud;
  };

}


code:
#include "dxvk_pipecache.h"

namespace dxvk {

}


code:
#pragma once

#include <atomic>
#include <condition_variable>
#include <fstream>

#include "dxvk_include.h"

#include "../util/sha1/sha1_util.h"
#include "../util/util_env.h"
#include "../util/util_time.h"

namespace dxvk {

  /**
   * \brief Pipeline cache
   *
   * Allows the Vulkan implementation to
   * re-use previously compiled pipelines.
   */
  class DxvkPipelineCache : public RcObject {

  public:

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

    /**
     * \brief Pipeline cache handle
     * \returns Pipeline cache handle
     */
    VkPipelineCache handle() const {
      return VK_NULL_HANDLE;
    }

  };

}


code:
#include <cstring>

#include "dxvk_descriptor.h"
#include "dxvk_limits.h"
#include "dxvk_pipelayout.h"

namespace dxvk {

  DxvkDescriptorSlotMapping:: DxvkDescriptorSlotMapping() { }
  DxvkDescriptorSlotMapping::~DxvkDescriptorSlotMapping() { }

  void DxvkDescriptorSlotMapping::defineSlot(
          VkShaderStageFlagBits stage,
    const DxvkResourceSlot&     desc) {
    uint32_t bindingId = this->getBindingId(desc.slot);

    if (bindingId != InvalidBinding) {
      m_descriptorSlots[bindingId].stages |= stage;
      m_descriptorSlots[bindingId].access |= desc.access;
    } else {
      DxvkDescriptorSlot slotInfo;
      slotInfo.slot   = desc.slot;
      slotInfo.type   = desc.type;
      slotInfo.view   = desc.view;
      slotInfo.stages = stage;
      slotInfo.access = desc.access;
      m_descriptorSlots.push_back(slotInfo);
    }
  }

  void DxvkDescriptorSlotMapping::definePushConstRange(
          VkShaderStageFlagBits stage,
          uint32_t              offset,
          uint32_t              size) {
    m_pushConstRange.stageFlags |= stage;
    m_pushConstRange.size = std::max(
      m_pushConstRange.size, offset + size);
  }

  uint32_t DxvkDescriptorSlotMapping::getBindingId(uint32_t slot) const {
    // This won't win a performance competition, but the number
    // of bindings used by a shader is usually much smaller than
    // the number of resource slots available to the system.
    for (uint32_t i = 0; i < m_descriptorSlots.size(); i++) {
      if (m_descriptorSlots[i].slot == slot)
        return i;
    }

    return InvalidBinding;
  }

  void DxvkDescriptorSlotMapping::makeDescriptorsDynamic(
          uint32_t              uniformBuffers,
          uint32_t              storageBuffers) {
    if (this->countDescriptors(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER) <= uniformBuffers)
      this->replaceDescriptors(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC);
  }

  uint32_t DxvkDescriptorSlotMapping::countDescriptors(
          VkDescriptorType      type) const {
    uint32_t count = 0;

    for (const auto& slot : m_descriptorSlots)
      count += slot.type == type ? 1 : 0;

    return count;
  }

  void DxvkDescriptorSlotMapping::replaceDescriptors(
          VkDescriptorType      oldType,
          VkDescriptorType      newType) {
    for (auto& slot : m_descriptorSlots) {
      if (slot.type == oldType)
        slot.type = newType;
    }
  }

  DxvkPipelineLayout::DxvkPipelineLayout(
    const Rc<vk::DeviceFn>&   vkd,
    const DxvkDescriptorSlotMapping& slotMapping,
          VkPipelineBindPoint pipelineBindPoint)
  : m_vkd           (vkd),
    m_pushConstRange(slotMapping.pushConstRange()),
    m_bindingSlots  (slotMapping.bindingCount()) {

    auto bindingCount = slotMapping.bindingCount();
    auto bindingInfos = slotMapping.bindingInfos();

    if (bindingCount > MaxNumActiveBindings)
      throw DxvkError(str::format("Too many active bindings in pipeline layout (", bindingCount, ")"));

    for (uint32_t i = 0; i < bindingCount; i++)
      m_bindingSlots[i] = bindingInfos[i];

    std::vector<VkDescriptorSetLayoutBinding>    bindings(bindingCount);
    std::vector<VkDescriptorUpdateTemplateEntry> tEntries(bindingCount);

    for (uint32_t i = 0; i < bindingCount; i++) {
      bindings[i].binding            = i;
      bindings[i].descriptorType     = bindingInfos[i].type;
      bindings[i].descriptorCount    = 1;
      bindings[i].stageFlags         = bindingInfos[i].stages;
      bindings[i].pImmutableSamplers = nullptr;

      tEntries[i].dstBinding      = i;
      tEntries[i].dstArrayElement = 0;
      tEntries[i].descriptorCount = 1;
      tEntries[i].descriptorType  = bindingInfos[i].type;
      tEntries[i].offset          = sizeof(DxvkDescriptorInfo) * i;
      tEntries[i].stride          = 0;

      if (bindingInfos[i].type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC)
        m_dynamicSlots.push_back(i);

      m_descriptorTypes.set(bindingInfos[i].type);
    }

    // Create descriptor set layout. We do not need to
    // create one if there are no active resource bindings.
    if (bindingCount > 0) {
      VkDescriptorSetLayoutCreateInfo dsetInfo;
      dsetInfo.sType        = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
      dsetInfo.pNext        = nullptr;
      dsetInfo.flags        = 0;
      dsetInfo.bindingCount = bindings.size();
      dsetInfo.pBindings    = bindings.data();

      if (m_vkd->vkCreateDescriptorSetLayout(m_vkd->device(),
            &dsetInfo, nullptr, &m_descriptorSetLayout) != VK_SUCCESS)
        throw DxvkError("DxvkPipelineLayout: Failed to create descriptor set layout");
    }

    // Create pipeline layout with the given descriptor set layout
    VkPipelineLayoutCreateInfo pipeInfo;
    pipeInfo.sType                  = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
    pipeInfo.pNext                  = nullptr;
    pipeInfo.flags                  = 0;
    pipeInfo.setLayoutCount         = bindingCount > 0 ? 1 : 0;
    pipeInfo.pSetLayouts            = &m_descriptorSetLayout;
    pipeInfo.pushConstantRangeCount = 0;
    pipeInfo.pPushConstantRanges    = nullptr;

    if (m_pushConstRange.size) {
      pipeInfo.pushConstantRangeCount = 1;
      pipeInfo.pPushConstantRanges    = &m_pushConstRange;
    }

    if (m_vkd->vkCreatePipelineLayout(m_vkd->device(),
        &pipeInfo, nullptr, &m_pipelineLayout) != VK_SUCCESS) {
      m_vkd->vkDestroyDescriptorSetLayout(m_vkd->device(), m_descriptorSetLayout, nullptr);
      throw DxvkError("DxvkPipelineLayout: Failed to create pipeline layout");
    }

    // Create descriptor update template. If there are no active
    // resource bindings, there won't be any descriptors to update.
    if (bindingCount > 0) {
      VkDescriptorUpdateTemplateCreateInfo templateInfo;
      templateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO;
      templateInfo.pNext = nullptr;
      templateInfo.flags = 0;
      templateInfo.descriptorUpdateEntryCount = tEntries.size();
      templateInfo.pDescriptorUpdateEntries   = tEntries.data();
      templateInfo.templateType               = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET;
      templateInfo.descriptorSetLayout        = m_descriptorSetLayout;
      templateInfo.pipelineBindPoint          = pipelineBindPoint;
      templateInfo.pipelineLayout             = m_pipelineLayout;
      templateInfo.set                        = 0;

      if (m_vkd->vkCreateDescriptorUpdateTemplate(
          m_vkd->device(), &templateInfo, nullptr, &m_descriptorTemplate) != VK_SUCCESS) {
        m_vkd->vkDestroyDescriptorSetLayout(m_vkd->device(), m_descriptorSetLayout, nullptr);
        m_vkd->vkDestroyPipelineLayout(m_vkd->device(), m_pipelineLayout, nullptr);
        throw DxvkError("DxvkPipelineLayout: Failed to create descriptor update template");
      }
    }
  }

  DxvkPipelineLayout::~DxvkPipelineLayout() {
    m_vkd->vkDestroyDescriptorUpdateTemplate(
      m_vkd->device(), m_descriptorTemplate, nullptr);

    m_vkd->vkDestroyPipelineLayout(
      m_vkd->device(), m_pipelineLayout, nullptr);

    m_vkd->vkDestroyDescriptorSetLayout(
      m_vkd->device(), m_descriptorSetLayout, nullptr);
  }

}


code:
#pragma once

#include <vector>

#include "dxvk_include.h"

namespace dxvk {

  /**
   * \brief Resource slot
   *
   * Describes the type of a single resource
   * binding that a shader can access.
   */
  struct DxvkResourceSlot {
    uint32_t           slot;
    VkDescriptorType   type;
    VkImageViewType    view;
    VkAccessFlags      access;
  };

  /**
   * \brief Shader interface binding
   *
   * Corresponds to a single descriptor binding in
   * Vulkan. DXVK does not use descriptor arrays.
   * Instead, each binding stores one descriptor.
   */
  struct DxvkDescriptorSlot {
    uint32_t           slot;    ///< Resource slot index for the context
    VkDescriptorType   type;    ///< Descriptor type (aka resource type)
    VkImageViewType    view;    ///< Compatible image view type
    VkShaderStageFlags stages;  ///< Stages that can use the resource
    VkAccessFlags      access;  ///< Access flags
  };

  /**
   * \brief Descriptor slot mapping
   *
   * Convenience class that generates descriptor slot
   * index to binding index mappings. This is required
   * when generating Vulkan pipeline and descriptor set
   * layouts.
   */
  class DxvkDescriptorSlotMapping {
    constexpr static uint32_t InvalidBinding = 0xFFFFFFFFu;
  public:

    DxvkDescriptorSlotMapping();
    ~DxvkDescriptorSlotMapping();

    /**
     * \brief Number of descriptor bindings
     * \returns Descriptor binding count
     */
    uint32_t bindingCount() const {
      return m_descriptorSlots.size();
    }

    /**
     * \brief Descriptor binding infos
     * \returns Descriptor binding infos
     */
    const DxvkDescriptorSlot* bindingInfos() const {
      return m_descriptorSlots.data();
    }

    /**
     * \brief Push constant range
     * \returns Push constant range
     */
    VkPushConstantRange pushConstRange() const {
      return m_pushConstRange;
    }

    /**
     * \brief Defines a new slot
     *
     * Adds a slot to the mapping. If the slot is already
     * defined by another shader stage, this will extend
     * the stage mask by the given stage. Otherwise, an
     * entirely new binding is added.
     * \param [in] stage Shader stage
     * \param [in] desc Slot description
     */
    void defineSlot(
            VkShaderStageFlagBits stage,
      const DxvkResourceSlot&     desc);

    /**
     * \brief Defines new push constant range
     *
     * \param [in] stage Shader stage
     * \param [in] offset Range offset
     * \param [in] size Range size
     */
    void definePushConstRange(
            VkShaderStageFlagBits stage,
            uint32_t              offset,
            uint32_t              size);

    /**
     * \brief Gets binding ID for a slot
     *
     * \param [in] slot Resource slot
     * \returns Binding index, or \c InvalidBinding
     */
    uint32_t getBindingId(
            uint32_t              slot) const;

    /**
     * \brief Makes static descriptors dynamic
     *
     * Replaces static uniform and storage buffer bindings by
     * their dynamic equivalent if the number of bindings of
     * the respective type lies within supported device limits.
     * Using dynamic descriptor types may improve performance.
     * \param [in] uniformBuffers Max number of uniform buffers
     * \param [in] storageBuffers Max number of storage buffers
     */
    void makeDescriptorsDynamic(
            uint32_t              uniformBuffers,
            uint32_t              storageBuffers);

  private:

    std::vector<DxvkDescriptorSlot> m_descriptorSlots;
    VkPushConstantRange             m_pushConstRange = { };

    uint32_t countDescriptors(
            VkDescriptorType      type) const;

    void replaceDescriptors(
            VkDescriptorType      oldType,
            VkDescriptorType      newType);

  };

  /**
   * \brief Shader interface
   *
   * Describes shader resource bindings
   * for a graphics or compute pipeline.
   */
  class DxvkPipelineLayout : public RcObject {

  public:

    DxvkPipelineLayout(
      const Rc<vk::DeviceFn>&   vkd,
      const DxvkDescriptorSlotMapping& slotMapping,
            VkPipelineBindPoint pipelineBindPoint);

    ~DxvkPipelineLayout();

    /**
     * \brief Number of resource bindings
     * \returns Resource binding count
     */
    uint32_t bindingCount() const {
      return m_bindingSlots.size();
    }

    /**
     * \brief Resource binding info
     *
     * \param [in] id Binding index
     * \returns Resource binding info
     */
    const DxvkDescriptorSlot& binding(uint32_t id) const {
      return m_bindingSlots[id];
    }

    /**
     * \brief Resource binding info
     * \returns Resource binding info
     */
    const DxvkDescriptorSlot* bindings() const {
      return m_bindingSlots.data();
    }

    /**
     * \brief Push constant range
     * \returns Push constant range
     */
    const VkPushConstantRange& pushConstRange() const {
      return m_pushConstRange;
    }

    /**
     * \brief Descriptor set layout handle
     * \returns Descriptor set layout handle
     */
    VkDescriptorSetLayout descriptorSetLayout() const {
      return m_descriptorSetLayout;
    }

    /**
     * \brief Pipeline layout handle
     * \returns Pipeline layout handle
     */
    VkPipelineLayout pipelineLayout() const {
      return m_pipelineLayout;
    }

    /**
     * \brief Descriptor update template
     * \returns Descriptor update template
     */
    VkDescriptorUpdateTemplateKHR descriptorTemplate() const {
      return m_descriptorTemplate;
    }

    /**
     * \brief Number of dynamic bindings
     * \returns Dynamic binding count
     */
    uint32_t dynamicBindingCount() const {
      return m_dynamicSlots.size();
    }

    /**
     * \brief Returns a dynamic binding
     *
     * \param [in] id Dynamic binding ID
     * \returns Reference to that binding
     */
    const DxvkDescriptorSlot& dynamicBinding(uint32_t id) const {
      return this->binding(m_dynamicSlots[id]);
    }

    /**
     * \brief Checks for static buffer bindings
     *
     * Returns \c true if there is at least one
     * descriptor of the static uniform buffer
     * type.
     */
    bool hasStaticBufferBindings() const {
      return m_descriptorTypes.test(
        VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
    }

    /**
     * \brief Checks whether buffers or images are written to
     *
     * It is assumed that storage images and buffers
     * will be written to if they are present. Used
     * for synchronization purposes.
     * \param [in] stages Shader stages to check
     */
    VkShaderStageFlags getStorageDescriptorStages() const {
      VkShaderStageFlags stages = 0;

      for (const auto& slot : m_bindingSlots) {
        if (slot.access & VK_ACCESS_SHADER_WRITE_BIT)
          stages |= slot.stages;
      }

      return stages;
    }

  private:

    Rc<vk::DeviceFn> m_vkd;

    VkPushConstantRange             m_pushConstRange      = { };
    VkDescriptorSetLayout           m_descriptorSetLayout = VK_NULL_HANDLE;
    VkPipelineLayout                m_pipelineLayout      = VK_NULL_HANDLE;
    VkDescriptorUpdateTemplateKHR   m_descriptorTemplate  = VK_NULL_HANDLE;

    std::vector<DxvkDescriptorSlot> m_bindingSlots;
    std::vector<uint32_t>           m_dynamicSlots;

    Flags<VkDescriptorType>         m_descriptorTypes;

  };

}


code:
#include "dxvk_device.h"
#include "dxvk_pipemanager.h"
#include "dxvk_state_cache.h"

namespace dxvk {

  DxvkPipelineManager::DxvkPipelineManager(
          DxvkDevice*         device,
          DxvkRenderPassPool* passManager)
  : m_device    (device),
    m_cache     (new DxvkPipelineCache(device->vkd())) {
    std::string useStateCache = env::getEnvVar("DXVK_STATE_CACHE");

    if (useStateCache != "0" && device->config().enableStateCache)
      m_stateCache = new DxvkStateCache(device, this, passManager);
  }

  DxvkPipelineManager::~DxvkPipelineManager() {

  }

  DxvkComputePipeline* DxvkPipelineManager::createComputePipeline(
    const DxvkComputePipelineShaders& shaders) {
    if (shaders.cs == nullptr)
      return nullptr;

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

    auto pair = m_computePipelines.find(shaders);
    if (pair != m_computePipelines.end())
      return &pair->second;

    auto iter = m_computePipelines.emplace(
      std::piecewise_construct,
      std::tuple(shaders),
      std::tuple(this, shaders));
    return &iter.first->second;
  }

  DxvkGraphicsPipeline* DxvkPipelineManager::createGraphicsPipeline(
    const DxvkGraphicsPipelineShaders& shaders) {
    if (shaders.vs == nullptr)
      return nullptr;

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

    auto pair = m_graphicsPipelines.find(shaders);
    if (pair != m_graphicsPipelines.end())
      return &pair->second;

    auto iter = m_graphicsPipelines.emplace(
      std::piecewise_construct,
      std::tuple(shaders),
      std::tuple(this, shaders));
    return &iter.first->second;
  }

  void DxvkPipelineManager::registerShader(
    const Rc<DxvkShader>&         shader) {
    if (m_stateCache != nullptr)
      m_stateCache->registerShader(shader);
  }

  DxvkPipelineCount DxvkPipelineManager::getPipelineCount() const {
    DxvkPipelineCount result;
    result.numComputePipelines  = m_numComputePipelines.load();
    result.numGraphicsPipelines = m_numGraphicsPipelines.load();
    return result;
  }

  bool DxvkPipelineManager::isCompilingShaders() const {
    return m_stateCache != nullptr
        && m_stateCache->isCompilingShaders();
  }

  void DxvkPipelineManager::stopWorkerThreads() const {
    if (m_stateCache != nullptr)
      m_stateCache->stopWorkerThreads();
  }

}


code:
#pragma once

#include <mutex>
#include <unordered_map>

#include "dxvk_compute.h"
#include "dxvk_graphics.h"

namespace dxvk {

  class DxvkStateCache;

  /**
   * \brief Pipeline count
   *
   * Stores number of graphics and
   * compute pipelines, individually.
   */
  struct DxvkPipelineCount {
    uint32_t numGraphicsPipelines;
    uint32_t numComputePipelines;
  };

  /**
   * \brief Pipeline manager
   *
   * Creates and stores graphics pipelines and compute
   * pipelines for each combination of shaders that is
   * used within the application. This is necessary
   * because DXVK does not expose the concept of shader
   * pipeline objects to the client API.
   */
  class DxvkPipelineManager {
    friend class DxvkComputePipeline;
    friend class DxvkGraphicsPipeline;
  public:

    DxvkPipelineManager(
            DxvkDevice*         device,
            DxvkRenderPassPool* passManager);

    ~DxvkPipelineManager();

    /**
     * \brief Retrieves a compute pipeline object
     *
     * If a pipeline for the given shader stage object
     * already exists, it will be returned. Otherwise,
     * a new pipeline will be created.
     * \param [in] shaders Shaders for the pipeline
     * \returns Compute pipeline object
     */
    DxvkComputePipeline* createComputePipeline(
      const DxvkComputePipelineShaders& shaders);

    /**
     * \brief Retrieves a graphics pipeline object
     *
     * If a pipeline for the given shader stage objects
     * already exists, it will be returned. Otherwise,
     * a new pipeline will be created.
     * \param [in] shaders Shaders for the pipeline
     * \returns Graphics pipeline object
     */
    DxvkGraphicsPipeline* createGraphicsPipeline(
      const DxvkGraphicsPipelineShaders& shaders);

    /*
     * \brief Registers a shader
     *
     * Starts compiling pipelines asynchronously
     * in case the state cache contains state
     * vectors for this shader.
     * \param [in] shader Newly compiled shader
     */
    void registerShader(
      const Rc<DxvkShader>&         shader);

    /**
     * \brief Retrieves total pipeline count
     * \returns Number of compute/graphics pipelines
     */
    DxvkPipelineCount getPipelineCount() const;

    /**
     * \brief Checks whether async compiler is busy
     * \returns \c true if shaders are being compiled
     */
    bool isCompilingShaders() const;

    /**
     * \brief Stops async compiler threads
     */
    void stopWorkerThreads() const;

  private:

    DxvkDevice*               m_device;
    Rc<DxvkPipelineCache>     m_cache;
    Rc<DxvkStateCache>        m_stateCache;

    std::atomic<uint32_t>     m_numComputePipelines  = { 0 };
    std::atomic<uint32_t>     m_numGraphicsPipelines = { 0 };

    dxvk::mutex m_mutex;

    std::unordered_map<
      DxvkComputePipelineShaders,
      DxvkComputePipeline,
      DxvkHash, DxvkEq> m_computePipelines;

    std::unordered_map<
      DxvkGraphicsPipelineShaders,
      DxvkGraphicsPipeline,
      DxvkHash, DxvkEq> m_graphicsPipelines;

  };

}


code:
#pragma once

#include "dxvk_extension_provider.h"

namespace dxvk {

  class DxvkPlatformExts : public DxvkExtensionProvider {

  public:

    std::string_view getName();

    DxvkNameSet getInstanceExtensions();

    DxvkNameSet getDeviceExtensions(
            uint32_t      adapterId);

    void initInstanceExtensions();

    void initDeviceExtensions(
      const DxvkInstance* instance);

    static DxvkPlatformExts s_instance;
  };

}


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

namespace dxvk {

  DxvkSubmissionQueue::DxvkSubmissionQueue(DxvkDevice* device)
  : m_device(device),
    m_submitThread([this] () { submitCmdLists(); }),
    m_finishThread([this] () { finishCmdLists(); }) {

  }

  DxvkSubmissionQueue::~DxvkSubmissionQueue() {
    { std::unique_lock<dxvk::mutex> lock(m_mutex);
      m_stopped.store(true);
    }

    m_appendCond.notify_all();
    m_submitCond.notify_all();

    m_submitThread.join();
    m_finishThread.join();
  }

  void DxvkSubmissionQueue::submit(DxvkSubmitInfo submitInfo) {
    std::unique_lock<dxvk::mutex> lock(m_mutex);

    m_finishCond.wait(lock, [this] {
      return m_submitQueue.size() + m_finishQueue.size() <= MaxNumQueuedCommandBuffers;
    });

    DxvkSubmitEntry entry = { };
    entry.submit = std::move(submitInfo);

    m_pending += 1;
    m_submitQueue.push(std::move(entry));
    m_appendCond.notify_all();
  }

  void DxvkSubmissionQueue::present(DxvkPresentInfo presentInfo, DxvkSubmitStatus* status) {
    std::unique_lock<dxvk::mutex> lock(m_mutex);

    DxvkSubmitEntry entry = { };
    entry.status  = status;
    entry.present = std::move(presentInfo);

    m_submitQueue.push(std::move(entry));
    m_appendCond.notify_all();
  }

  void DxvkSubmissionQueue::synchronizeSubmission(
          DxvkSubmitStatus*   status) {
    std::unique_lock<dxvk::mutex> lock(m_mutex);

    m_submitCond.wait(lock, [status] {
      return status->result.load() != VK_NOT_READY;
    });
  }

  void DxvkSubmissionQueue::synchronize() {
    std::unique_lock<dxvk::mutex> lock(m_mutex);

    m_submitCond.wait(lock, [this] {
      return m_submitQueue.empty();
    });
  }

  void DxvkSubmissionQueue::lockDeviceQueue() {
    m_mutexQueue.lock();
  }

  void DxvkSubmissionQueue::unlockDeviceQueue() {
    m_mutexQueue.unlock();
  }

  void DxvkSubmissionQueue::submitCmdLists() {
    env::setThreadName("dxvk-submit");

    std::unique_lock<dxvk::mutex> lock(m_mutex);

    while (!m_stopped.load()) {
      m_appendCond.wait(lock, [this] {
        return m_stopped.load() || !m_submitQueue.empty();
      });

      if (m_stopped.load())
        return;

      DxvkSubmitEntry entry = std::move(m_submitQueue.front());
      lock.unlock();

      // Submit command buffer to device
      VkResult status = VK_NOT_READY;

      if (m_lastError != VK_ERROR_DEVICE_LOST) {
        std::lock_guard<dxvk::mutex> lock(m_mutexQueue);

        if (entry.submit.cmdList != nullptr) {
          status = entry.submit.cmdList->submit(
            entry.submit.waitSync,
            entry.submit.wakeSync);
        } else if (entry.present.presenter != nullptr) {
          status = entry.present.presenter->presentImage();
        }
      } else {
        // Don't submit anything after device loss
        // so that drivers get a chance to recover
        status = VK_ERROR_DEVICE_LOST;
      }

      if (entry.status)
        entry.status->result = status;

      // On success, pass it on to the queue thread
      lock = std::unique_lock<dxvk::mutex>(m_mutex);

      if (status == VK_SUCCESS) {
        if (entry.submit.cmdList != nullptr)
          m_finishQueue.push(std::move(entry));
      } else if (status == VK_ERROR_DEVICE_LOST || entry.submit.cmdList != nullptr) {
        Logger::err(str::format("DxvkSubmissionQueue: Command submission failed: ", status));
        m_lastError = status;
        m_device->waitForIdle();
      }

      m_submitQueue.pop();
      m_submitCond.notify_all();
    }
  }

  void DxvkSubmissionQueue::finishCmdLists() {
    env::setThreadName("dxvk-queue");

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

      if (m_finishQueue.empty()) {
        auto t0 = dxvk::high_resolution_clock::now();

        m_submitCond.wait(lock, [this] {
          return m_stopped.load() || !m_finishQueue.empty();
        });

        auto t1 = dxvk::high_resolution_clock::now();
        m_gpuIdle += std::chrono::duration_cast<std::chrono::microseconds>(t1 - t0).count();
      }

      if (m_stopped.load())
        return;

      DxvkSubmitEntry entry = std::move(m_finishQueue.front());
      lock.unlock();

      VkResult status = m_lastError.load();

      if (status != VK_ERROR_DEVICE_LOST)
        status = entry.submit.cmdList->synchronize();

      if (status != VK_SUCCESS) {
        Logger::err(str::format("DxvkSubmissionQueue: Failed to sync fence: ", status));
        m_lastError = status;
        m_device->waitForIdle();
      }

      // Release resources and signal events, then immediately wake
      // up any thread that's currently waiting on a resource in
      // order to reduce delays as much as possible.
      entry.submit.cmdList->notifyObjects();

      lock.lock();
      m_pending -= 1;

      m_finishQueue.pop();
      m_finishCond.notify_all();
      lock.unlock();

      // Free the command list and associated objects now
      entry.submit.cmdList->reset();
      m_device->recycleCommandList(entry.submit.cmdList);
    }
  }

}


code:
#pragma once

#include <condition_variable>
#include <mutex>
#include <queue>

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

#include "../vulkan/vulkan_presenter.h"

#include "dxvk_cmdlist.h"

namespace dxvk {

  class DxvkDevice;

  /**
   * \brief Submission status
   *
   * Stores the result of a queue
   * submission or a present call.
   */
  struct DxvkSubmitStatus {
    std::atomic<VkResult> result = { VK_SUCCESS };
  };

  /**
   * \brief Queue submission info
   *
   * Stores parameters used to submit
   * a command buffer to the device.
   */
  struct DxvkSubmitInfo {
    Rc<DxvkCommandList> cmdList;
    VkSemaphore         waitSync;
    VkSemaphore         wakeSync;
  };

  /**
   * \brief Present info
   *
   * Stores parameters used to present
   * a swap chain image on the device.
   */
  struct DxvkPresentInfo {
    Rc<vk::Presenter>   presenter;
  };

  /**
   * \brief Submission queue entry
   */
  struct DxvkSubmitEntry {
    DxvkSubmitStatus*   status;
    DxvkSubmitInfo      submit;
    DxvkPresentInfo     present;
  };

  /**
   * \brief Submission queue
   */
  class DxvkSubmissionQueue {

  public:

    DxvkSubmissionQueue(DxvkDevice* device);
    ~DxvkSubmissionQueue();

    /**
     * \brief Number of pending submissions
     *
     * A return value of 0 indicates
     * that the GPU is currently idle.
     * \returns Pending submission count
     */
    uint32_t pendingSubmissions() const {
      return m_pending.load();
    }

    /**
     * \brief Retrieves estimated GPU idle time
     *
     * This is a monotonically increasing counter
     * which can be evaluated periodically in order
     * to calculate the GPU load.
     * \returns Accumulated GPU idle time, in us
     */
    uint64_t gpuIdleTicks() const {
      return m_gpuIdle.load();
    }

    /**
     * \brief Retrieves last submission error
     *
     * In case an error occured during asynchronous command
     * submission, it will be returned by this function.
     * \returns Last error from command submission
     */
    VkResult getLastError() const {
      return m_lastError.load();
    }

    /**
     * \brief Submits a command list asynchronously
     *
     * Queues a command list for submission on the
     * dedicated submission thread. Use this to take
     * the submission overhead off the calling thread.
     * \param [in] submitInfo Submission parameters
     */
    void submit(
            DxvkSubmitInfo      submitInfo);

    /**
     * \brief Presents an image synchronously
     *
     * Waits for queued command lists to be submitted
     * and then presents the current swap chain image
     * of the presenter. May stall the calling thread.
     * \param [in] present Present parameters
     * \returns Status of the operation
     */
    void present(
            DxvkPresentInfo     presentInfo,
            DxvkSubmitStatus*   status);

    /**
     * \brief Synchronizes with one queue submission
     *
     * Waits for the result of the given submission
     * or present operation to become available.
     * \param [in,out] status Submission status
     */
    void synchronizeSubmission(
            DxvkSubmitStatus*   status);

    /**
     * \brief Synchronizes with queue submissions
     *
     * Waits for all pending command lists to be
     * submitted to the GPU before returning.
     */
    void synchronize();

    /**
     * \brief Synchronizes until a given condition becomes true
     *
     * Useful to wait for the GPU without busy-waiting.
     * \param [in] pred Predicate to check
     */
    template<typename Pred>
    void synchronizeUntil(const Pred& pred) {
      std::unique_lock<dxvk::mutex> lock(m_mutex);
      m_finishCond.wait(lock, pred);
    }

    /**
     * \brief Locks device queue
     *
     * Locks the mutex that protects the Vulkan queue
     * that DXVK uses for command buffer submission.
     * This is needed when the app submits its own
     * command buffers to the queue.
     */
    void lockDeviceQueue();

    /**
     * \brief Unlocks device queue
     *
     * Unlocks the mutex that protects the Vulkan
     * queue used for command buffer submission.
     */
    void unlockDeviceQueue();

  private:

    DxvkDevice*             m_device;

    std::atomic<VkResult>   m_lastError = { VK_SUCCESS };

    std::atomic<bool>       m_stopped = { false };
    std::atomic<uint32_t>   m_pending = { 0u };
    std::atomic<uint64_t>   m_gpuIdle = { 0ull };

    dxvk::mutex                 m_mutex;
    dxvk::mutex                 m_mutexQueue;

    dxvk::condition_variable    m_appendCond;
    dxvk::condition_variable    m_submitCond;
    dxvk::condition_variable    m_finishCond;

    std::queue<DxvkSubmitEntry> m_submitQueue;
    std::queue<DxvkSubmitEntry> m_finishQueue;

    dxvk::thread                m_submitThread;
    dxvk::thread                m_finishThread;

    VkResult submitToQueue(
      const DxvkSubmitInfo& submission);

    void submitCmdLists();

    void finishCmdLists();

  };

}


code:
#pragma once

#include <mutex>
#include <vector>

namespace dxvk {

  /**
   * \brief Object recycler
   *
   * Implements a thread-safe buffer that can store up to
   * a given number of objects of a certain type. This way,
   * DXVK can efficiently reuse and reset objects instead
   * of destroying them and creating them anew.
   * \tparam T Type of the objects to store
   * \tparam N Maximum number of objects to store
   */
  template<typename T, size_t N>
  class DxvkRecycler {

  public:

    /**
     * \brief Retrieves an object if possible
     *
     * Returns an object that was returned to the recycler
     * earier. In case no objects are available, this will
     * return \c nullptr and a new object has to be created.
     * \return An object, or \c nullptr
     */
    Rc<T> retrieveObject() {
      std::lock_guard<dxvk::mutex> lock(m_mutex);

      if (m_objectId == 0)
        return nullptr;

      return m_objects.at(--m_objectId);
    }

    /**
     * \brief Returns an object to the recycler
     *
     * If the buffer is full, the object will be destroyed
     * once the last reference runs out of scope. No further
     * action needs to be taken in this case.
     * \param [in] object The object to return
     */
    void returnObject(const Rc<T>& object) {
      std::lock_guard<dxvk::mutex> lock(m_mutex);

      if (m_objectId < N)
        m_objects.at(m_objectId++) = object;
    }

  private:

    dxvk::mutex          m_mutex;
    std::array<Rc<T>, N> m_objects;
    size_t               m_objectId = 0;

  };

}


code:
#include <algorithm>

#include "dxvk_device.h"
#include "dxvk_renderpass.h"

namespace dxvk {

  bool DxvkRenderPassFormat::eq(const DxvkRenderPassFormat& fmt) const {
    bool eq = sampleCount == fmt.sampleCount;

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

    eq &= depth.format == fmt.depth.format
       && depth.layout == fmt.depth.layout;

    return eq;
  }

  size_t DxvkRenderPassFormat::hash() const {
    DxvkHashState state;
    state.add(uint32_t(sampleCount));

    for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {
      state.add(uint32_t(color[i].format));
      state.add(uint32_t(color[i].layout));
    }

    state.add(uint32_t(depth.format));
    state.add(uint32_t(depth.layout));
    return state;
  }

  DxvkRenderPass::DxvkRenderPass(
    const Rc<vk::DeviceFn>&       vkd,
    const DxvkRenderPassFormat&   fmt)
  : m_vkd(vkd), m_format(fmt),
    m_default(createRenderPass(DxvkRenderPassOps())) {

  }

  DxvkRenderPass::~DxvkRenderPass() {
    m_vkd->vkDestroyRenderPass(m_vkd->device(), m_default, nullptr);

    for (const auto& i : m_instances) {
      m_vkd->vkDestroyRenderPass(
        m_vkd->device(), i.handle, nullptr);
    }
  }

  bool DxvkRenderPass::hasCompatibleFormat(const DxvkRenderPassFormat& fmt) const {
    return m_format.eq(fmt);
  }

  VkRenderPass DxvkRenderPass::getHandle(const DxvkRenderPassOps& ops) {
    VkRenderPass handle = this->findHandle(ops);

    if (unlikely(!handle)) {
      std::lock_guard<dxvk::mutex> lock(m_mutex);
      handle = this->findHandle(ops);

      if (!handle) {
        handle = this->createRenderPass(ops);
        m_instances.insert({ ops, handle });
      }
    }

    return handle;
  }

  VkRenderPass DxvkRenderPass::findHandle(const DxvkRenderPassOps& ops) {
    for (const auto& i : m_instances) {
      if (compareOps(i.ops, ops))
        return i.handle;
    }

    return VK_NULL_HANDLE;
  }

  VkRenderPass DxvkRenderPass::createRenderPass(const DxvkRenderPassOps& ops) {
    std::vector<VkAttachmentDescription> attachments;

    VkAttachmentReference                                  depthRef;
    std::array<VkAttachmentReference, MaxNumRenderTargets> colorRef;

    // Render passes may not require the previous
    // contents of the attachments to be preserved.
    for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {
      if (m_format.color[i].format != VK_FORMAT_UNDEFINED) {
        VkAttachmentDescription desc;
        desc.flags            = 0;
        desc.format           = m_format.color[i].format;
        desc.samples          = m_format.sampleCount;
        desc.loadOp           = ops.colorOps[i].loadOp;
        desc.storeOp          = VK_ATTACHMENT_STORE_OP_STORE;
        desc.stencilLoadOp    = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
        desc.stencilStoreOp   = VK_ATTACHMENT_STORE_OP_DONT_CARE;
        desc.initialLayout    = ops.colorOps[i].loadLayout;
        desc.finalLayout      = ops.colorOps[i].storeLayout;

        colorRef[i].attachment = attachments.size();
        colorRef[i].layout     = m_format.color[i].layout;

        attachments.push_back(desc);
      } else {
        colorRef[i].attachment = VK_ATTACHMENT_UNUSED;
        colorRef[i].layout     = VK_IMAGE_LAYOUT_UNDEFINED;
      }
    }

    if (m_format.depth.format != VK_FORMAT_UNDEFINED) {
      VkAttachmentDescription desc;
      desc.flags          = 0;
      desc.format         = m_format.depth.format;
      desc.samples        = m_format.sampleCount;
      desc.loadOp         = ops.depthOps.loadOpD;
      desc.storeOp        = VK_ATTACHMENT_STORE_OP_STORE;
      desc.stencilLoadOp  = ops.depthOps.loadOpS;
      desc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;
      desc.initialLayout  = ops.depthOps.loadLayout;
      desc.finalLayout    = ops.depthOps.storeLayout;

      depthRef.attachment = attachments.size();
      depthRef.layout     = m_format.depth.layout;

      attachments.push_back(desc);
    } else {
      depthRef.attachment = VK_ATTACHMENT_UNUSED;
      depthRef.layout     = VK_IMAGE_LAYOUT_UNDEFINED;
    }

    VkSubpassDescription subpass;
    subpass.flags                     = 0;
    subpass.pipelineBindPoint         = VK_PIPELINE_BIND_POINT_GRAPHICS;
    subpass.inputAttachmentCount      = 0;
    subpass.pInputAttachments         = nullptr;
    subpass.colorAttachmentCount      = colorRef.size();
    subpass.pColorAttachments         = colorRef.data();
    subpass.pResolveAttachments       = nullptr;
    subpass.pDepthStencilAttachment   = &depthRef;
    subpass.preserveAttachmentCount   = 0;
    subpass.pPreserveAttachments      = nullptr;

    if (m_format.depth.format == VK_FORMAT_UNDEFINED)
      subpass.pDepthStencilAttachment = nullptr;

    std::array<VkSubpassDependency, 3> subpassDeps;
    uint32_t                           subpassDepCount = 0;

    VkPipelineStageFlags renderStages = 0;
    VkAccessFlags        renderAccess = 0;

    if (m_format.depth.format) {
      renderStages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
                   |  VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;

      VkImageAspectFlags loadAspects = 0;

      if (ops.depthOps.loadOpD == VK_ATTACHMENT_LOAD_OP_LOAD)
        loadAspects |= VK_IMAGE_ASPECT_DEPTH_BIT;
      if (ops.depthOps.loadOpS == VK_ATTACHMENT_LOAD_OP_LOAD)
        loadAspects |= VK_IMAGE_ASPECT_STENCIL_BIT;

      if (loadAspects & imageFormatInfo(m_format.depth.format)->aspectMask)
        renderAccess |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT;

      if (m_format.depth.layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL)
        renderAccess |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;

      if (m_format.depth.layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) {
        renderStages |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
        renderAccess |= VK_ACCESS_SHADER_READ_BIT;
      }
    }

    for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {
      if (!m_format.color[i].format)
        continue;

      renderStages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
      renderAccess |= VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;

      if (ops.colorOps[i].loadOp == VK_ATTACHMENT_LOAD_OP_LOAD)
        renderAccess |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;
    }

    if (renderStages) {
      subpassDeps[subpassDepCount++] = {
        VK_SUBPASS_EXTERNAL, 0,
        renderStages, renderStages,
        0, renderAccess };
    }

    if (ops.barrier.srcStages & (
          VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
          VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT |
          VK_PIPELINE_STAGE_ALL_COMMANDS_BIT)) {
      subpassDeps[subpassDepCount++] = { 0, 0,
        VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
        VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
        VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
        VK_ACCESS_SHADER_READ_BIT,
        VK_DEPENDENCY_BY_REGION_BIT };
    }

    if (ops.barrier.srcStages && ops.barrier.dstStages) {
      subpassDeps[subpassDepCount++] = {
        0, VK_SUBPASS_EXTERNAL,
        ops.barrier.srcStages,
        ops.barrier.dstStages,
        ops.barrier.srcAccess,
        ops.barrier.dstAccess, 0 };
    }

    VkRenderPassCreateInfo info;
    info.sType                        = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
    info.pNext                        = nullptr;
    info.flags                        = 0;
    info.attachmentCount              = attachments.size();
    info.pAttachments                 = attachments.data();
    info.subpassCount                 = 1;
    info.pSubpasses                   = &subpass;
    info.dependencyCount              = subpassDepCount;
    info.pDependencies                = subpassDepCount ? subpassDeps.data() : nullptr;

    VkRenderPass renderPass = VK_NULL_HANDLE;

    if (m_vkd->vkCreateRenderPass(m_vkd->device(), &info, nullptr, &renderPass) != VK_SUCCESS) {
      Logger::err("DxvkRenderPass: Failed to create render pass object");
      return VK_NULL_HANDLE;
    }

    return renderPass;
  }

  bool DxvkRenderPass::compareOps(
    const DxvkRenderPassOps& a,
    const DxvkRenderPassOps& b) {
    bool eq = a.barrier.srcStages == b.barrier.srcStages
           && a.barrier.srcAccess == b.barrier.srcAccess
           && a.barrier.dstStages == b.barrier.dstStages
           && a.barrier.dstAccess == b.barrier.dstAccess;

    if (eq) {
      eq &= a.depthOps.loadOpD     == b.depthOps.loadOpD
         && a.depthOps.loadOpS     == b.depthOps.loadOpS
         && a.depthOps.loadLayout  == b.depthOps.loadLayout
         && a.depthOps.storeLayout == b.depthOps.storeLayout;
    }

    for (uint32_t i = 0; i < MaxNumRenderTargets && eq; i++) {
      eq &= a.colorOps[i].loadOp      == b.colorOps[i].loadOp
         && a.colorOps[i].loadLayout  == b.colorOps[i].loadLayout
         && a.colorOps[i].storeLayout == b.colorOps[i].storeLayout;
    }

    return eq;
  }

  DxvkRenderPassPool::DxvkRenderPassPool(const DxvkDevice* device)
  : m_device(device) {

  }

  DxvkRenderPassPool::~DxvkRenderPassPool() {

  }

  DxvkRenderPass* DxvkRenderPassPool::getRenderPass(const DxvkRenderPassFormat& fmt) {
    std::lock_guard<dxvk::mutex> lock(m_mutex);

    auto entry = m_renderPasses.find(fmt);
    if (entry != m_renderPasses.end())
      return &entry->second;

    auto result = m_renderPasses.emplace(std::piecewise_construct,
      std::tuple(fmt),
      std::tuple(m_device->vkd(), fmt));
    return &result.first->second;
  }

  bool DxvkRenderPassPool::validateRenderPassFormat(
    const DxvkRenderPassFormat&  fmt) {
    Rc<DxvkAdapter> adapter = m_device->adapter();

    if (fmt.depth.format) {
      VkFormatProperties depthInfo = adapter->formatProperties(fmt.depth.format);
      VkFormatFeatureFlags depthFlags = depthInfo.linearTilingFeatures | depthInfo.optimalTilingFeatures;

      if (!(depthFlags & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT))
        return false;

      if (fmt.depth.layout != VK_IMAGE_LAYOUT_GENERAL
       && fmt.depth.layout != VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL
       && fmt.depth.layout != VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL
       && fmt.depth.layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL
       && fmt.depth.layout != VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL)
        return false;
    }

    for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {
      if (fmt.color[i].format) {
        VkFormatProperties colorInfo = adapter->formatProperties(fmt.color[i].format);
        VkFormatFeatureFlags colorFlags = colorInfo.linearTilingFeatures | colorInfo.optimalTilingFeatures;

        if (!(colorFlags & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT))
          return false;

        if (fmt.color[i].layout != VK_IMAGE_LAYOUT_GENERAL
         && fmt.color[i].layout != VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL)
          return false;
      }
    }

    return true;
  }

}


code:
#pragma once

#include <mutex>
#include <vector>
#include <unordered_map>

#include "../util/sync/sync_list.h"

#include "dxvk_hash.h"
#include "dxvk_include.h"
#include "dxvk_limits.h"

namespace dxvk {

  class DxvkDevice;

  /**
   * \brief Format and layout for a render target
   *
   * Stores the image format of the attachment and
   * the image layout that is used while rendering.
   */
  struct DxvkAttachmentFormat {
    VkFormat      format = VK_FORMAT_UNDEFINED;
    VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;
  };

  /**
   * \brief Render pass format
   *
   * Stores the attachment formats for all depth and
   * color attachments, as well as the sample count.
   */
  struct DxvkRenderPassFormat {
    VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT;
    DxvkAttachmentFormat  depth;
    DxvkAttachmentFormat  color[MaxNumRenderTargets];

    bool eq(const DxvkRenderPassFormat& fmt) const;

    size_t hash() const;
  };

  /**
   * \brief Color attachment transitions
   *
   * Stores the load/store ops and the initial
   * and final layout of a single attachment.
   */
  struct DxvkColorAttachmentOps {
    VkAttachmentLoadOp  loadOp      = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    VkImageLayout       loadLayout  = VK_IMAGE_LAYOUT_UNDEFINED;
    VkImageLayout       storeLayout = VK_IMAGE_LAYOUT_GENERAL;
  };

  /**
   * \brief Depth attachment transitions
   *
   * Stores the load/store ops and the initial and
   * final layout of the depth-stencil attachment.
   */
  struct DxvkDepthAttachmentOps {
    VkAttachmentLoadOp  loadOpD     = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    VkAttachmentLoadOp  loadOpS     = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
    VkImageLayout       loadLayout  = VK_IMAGE_LAYOUT_UNDEFINED;
    VkImageLayout       storeLayout = VK_IMAGE_LAYOUT_GENERAL;
  };

  /**
   * \brief Render pass barrier
   *
   * External subpass dependency that is to be
   * executed after a render pass has completed.
   */
  struct DxvkRenderPassBarrier {
    VkPipelineStageFlags  srcStages = 0;
    VkAccessFlags         srcAccess = 0;
    VkPipelineStageFlags  dstStages = 0;
    VkAccessFlags         dstAccess = 0;
  };

  /**
   * \brief Render pass transitions
   *
   * Stores transitions for all depth and color attachments.
   * This is used to select a specific render pass object
   * from a group of render passes with the same format.
   */
  struct DxvkRenderPassOps {
    DxvkRenderPassBarrier  barrier;
    DxvkDepthAttachmentOps depthOps;
    DxvkColorAttachmentOps colorOps[MaxNumRenderTargets];
  };

  /**
   * \brief Render pass object
   *
   * Manages a set of compatible render passes, i.e.
   * render passes which share the same format but
   * may differ in their attachment operations.
   */
  class DxvkRenderPass {

  public:

    DxvkRenderPass(
      const Rc<vk::DeviceFn>&       vkd,
      const DxvkRenderPassFormat&   fmt);

    ~DxvkRenderPass();

    /**
     * \brief Retrieves render pass format
     * \returns The render pass format
     */
    DxvkRenderPassFormat format() const {
      return m_format;
    }

    /**
     * \brief Checks whether a format is compatible
     *
     * Two render pass formats are considered compatible
     * if all the relevant attachment formats match.
     * \param [in] fmt The render pass format to check
     * \returns \c true if this render pass is compatible.
     */
    bool hasCompatibleFormat(
      const DxvkRenderPassFormat&  fmt) const;

    /**
     * \brief Retrieves sample count
     *
     * If no sample count has been explicitly specitied,
     * this will return \c VK_SAMPLE_COUNT_1_BIT.
     * \returns Sample count
     */
    VkSampleCountFlagBits getSampleCount() const {
      return m_format.sampleCount;
    }

    /**
     * \brief Returns handle of default render pass
     *
     * The default render pass handle should be used to
     * create pipelines and framebuffer objects. It can
     * \e not be used for \c vkCmdBeginRenderPass calls.
     * \returns The default render pass handle
     */
    VkRenderPass getDefaultHandle() const {
      return m_default;
    }

    /**
     * \brief Returns handle to a specialized render pass
     *
     * Returns a handle to a render pass with the given
     * set of parameters. This should be used for calls
     * to \c vkCmdBeginRenderPass.
     * \param [in] ops Attachment ops
     * \returns Render pass handle
     */
    VkRenderPass getHandle(
      const DxvkRenderPassOps& ops);

  private:

    struct Instance {
      DxvkRenderPassOps ops;
      VkRenderPass      handle;
    };

    Rc<vk::DeviceFn>        m_vkd;
    DxvkRenderPassFormat    m_format;
    VkRenderPass            m_default;

    dxvk::mutex             m_mutex;
    sync::List<Instance>    m_instances;

    VkRenderPass findHandle(
      const DxvkRenderPassOps& ops);

    VkRenderPass createRenderPass(
      const DxvkRenderPassOps& ops);

    static bool compareOps(
      const DxvkRenderPassOps& a,
      const DxvkRenderPassOps& b);

  };

  /**
   * \brief Render pass pool
   *
   * Manages render pass objects. For each render
   * pass format, a new render pass object will
   * be created, but no two render pass objects
   * will have the same format.
   */
  class DxvkRenderPassPool {

  public:

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

    /**
     * \brief Retrieves a render pass object
     *
     * \param [in] fmt The render pass format
     * \returns Matching render pass object
     */
    DxvkRenderPass* getRenderPass(
      const DxvkRenderPassFormat&  fmt);

    /**
     * \brief Validates render pass format
     *
     * \param [in] fmt The render pass format
     * \returns \c true if the format is supported
     */
    bool validateRenderPassFormat(
      const DxvkRenderPassFormat&  fmt);

  private:

    const DxvkDevice*               m_device;

    dxvk::mutex                     m_mutex;
    std::unordered_map<
      DxvkRenderPassFormat,
      DxvkRenderPass,
      DxvkHash, DxvkEq>             m_renderPasses;

  };

}


code:
#include "dxvk_resource.h"

namespace dxvk {

  DxvkResource::~DxvkResource() {

  }

}


code:
#pragma once

#include "dxvk_include.h"

namespace dxvk {

  enum class DxvkAccess {
    Read    = 0,
    Write   = 1,
    None    = 2,
  };

  using DxvkAccessFlags = Flags<DxvkAccess>;

  /**
   * \brief DXVK resource
   *
   * Keeps track of whether the resource is currently in use
   * by the GPU. As soon as a command that uses the resource
   * is recorded, it will be marked as 'in use'.
   */
  class DxvkResource : public RcObject {

  public:

    virtual ~DxvkResource();

    /**
     * \brief Checks whether resource is in use
     *
     * Returns \c true if there are pending accesses to
     * the resource by the GPU matching the given access
     * type. Note that checking for reads will also return
     * \c true if the resource is being written to.
     * \param [in] access Access type to check for
     * \returns \c true if the resource is in use
     */
    bool isInUse(DxvkAccess access = DxvkAccess::Read) const {
      bool result = m_useCountW.load() != 0;
      if (access == DxvkAccess::Read)
        result |= m_useCountR.load() != 0;
      return result;
    }

    /**
     * \brief Acquires resource
     *
     * Increments use count for the given access type.
     * \param Access Resource access type
     */
    void acquire(DxvkAccess access) {
      if (access != DxvkAccess::None) {
        (access == DxvkAccess::Read
          ? m_useCountR
          : m_useCountW) += 1;
      }
    }

    /**
     * \brief Releases resource
     *
     * Decrements use count for the given access type.
     * \param Access Resource access type
     */
    void release(DxvkAccess access) {
      if (access != DxvkAccess::None) {
        (access == DxvkAccess::Read
          ? m_useCountR
          : m_useCountW) -= 1;
      }
    }

    /**
     * \brief Waits for resource to become unused
     *
     * Blocks calling thread until the GPU finishes
     * using the resource with the given access type.
     * \param [in] access Access type to check for
     */
    void waitIdle(DxvkAccess access = DxvkAccess::Read) const {
      sync::spin(50000, [this, access] {
        return !isInUse(access);
      });
    }

  private:

    std::atomic<uint32_t> m_useCountR = { 0u };
    std::atomic<uint32_t> m_useCountW = { 0u };

  };

}


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

namespace dxvk {

  DxvkSampler::DxvkSampler(
          DxvkDevice*             device,
    const DxvkSamplerCreateInfo&  info)
  : m_vkd(device->vkd()) {
    VkSamplerCustomBorderColorCreateInfoEXT borderColorInfo;
    borderColorInfo.sType               = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT;
    borderColorInfo.pNext               = nullptr;
    borderColorInfo.customBorderColor   = info.borderColor;
    borderColorInfo.format              = VK_FORMAT_UNDEFINED;

    VkSamplerCreateInfo samplerInfo;
    samplerInfo.sType                   = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
    samplerInfo.pNext                   = nullptr;
    samplerInfo.flags                   = info.nonSeamless ? VK_SAMPLER_CREATE_NON_SEAMLESS_CUBE_MAP_BIT_EXT : 0;
    samplerInfo.magFilter               = info.magFilter;
    samplerInfo.minFilter               = info.minFilter;
    samplerInfo.mipmapMode              = info.mipmapMode;
    samplerInfo.addressModeU            = info.addressModeU;
    samplerInfo.addressModeV            = info.addressModeV;
    samplerInfo.addressModeW            = info.addressModeW;
    samplerInfo.mipLodBias              = info.mipmapLodBias;
    samplerInfo.anisotropyEnable        = info.useAnisotropy;
    samplerInfo.maxAnisotropy           = info.maxAnisotropy;
    samplerInfo.compareEnable           = info.compareToDepth;
    samplerInfo.compareOp               = info.compareOp;
    samplerInfo.minLod                  = info.mipmapLodMin;
    samplerInfo.maxLod                  = info.mipmapLodMax;
    samplerInfo.borderColor             = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
    samplerInfo.unnormalizedCoordinates = info.usePixelCoord;

    if (!device->features().core.features.samplerAnisotropy)
      samplerInfo.anisotropyEnable = VK_FALSE;

    if (samplerInfo.addressModeU == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER
     || samplerInfo.addressModeV == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER
     || samplerInfo.addressModeW == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER)
      samplerInfo.borderColor = getBorderColor(device, info);

    if (samplerInfo.borderColor == VK_BORDER_COLOR_FLOAT_CUSTOM_EXT)
      samplerInfo.pNext = &borderColorInfo;

    if (m_vkd->vkCreateSampler(m_vkd->device(),
        &samplerInfo, nullptr, &m_sampler) != VK_SUCCESS)
      throw DxvkError("DxvkSampler::DxvkSampler: Failed to create sampler");
  }

  DxvkSampler::~DxvkSampler() {
    m_vkd->vkDestroySampler(
      m_vkd->device(), m_sampler, nullptr);
  }

  VkBorderColor DxvkSampler::getBorderColor(const Rc<DxvkDevice>& device, const DxvkSamplerCreateInfo& info) {
    static const std::array<std::pair<VkClearColorValue, VkBorderColor>, 3> s_borderColors = {{
      { { { 0.0f, 0.0f, 0.0f, 0.0f } }, VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK },
      { { { 0.0f, 0.0f, 0.0f, 1.0f } }, VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK },
      { { { 1.0f, 1.0f, 1.0f, 1.0f } }, VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE },
    }};

    // Ignore G/B/A components for shadow samplers
    size_t size = !info.compareToDepth
      ? sizeof(VkClearColorValue)
      : sizeof(float);

    for (const auto& e : s_borderColors) {
      if (!std::memcmp(&e.first, &info.borderColor, size))
        return e.second;
    }

    if (!device->features().extCustomBorderColor.customBorderColorWithoutFormat) {
      Logger::warn("DXVK: Custom border colors not supported");
      return VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
    }

    return VK_BORDER_COLOR_FLOAT_CUSTOM_EXT;
  }

}


code:
#pragma once

#include "dxvk_resource.h"

namespace dxvk {

  class DxvkDevice;

  /**
   * \brief Sampler properties
   */
  struct DxvkSamplerCreateInfo {
    /// Texture filter propertoes
    VkFilter magFilter;
    VkFilter minFilter;

    /// Mipmapping properties
    VkSamplerMipmapMode mipmapMode;
    float               mipmapLodBias;
    float               mipmapLodMin;
    float               mipmapLodMax;

    /// Anisotropic filtering
    VkBool32 useAnisotropy;
    float    maxAnisotropy;

    /// Address modes
    VkSamplerAddressMode addressModeU;
    VkSamplerAddressMode addressModeV;
    VkSamplerAddressMode addressModeW;

    /// Compare op for shadow textures
    VkBool32    compareToDepth;
    VkCompareOp compareOp;

    /// Texture border color
    VkClearColorValue borderColor;

    /// Enables unnormalized coordinates
    VkBool32 usePixelCoord;

    /// Enables non seamless cube map filtering
    VkBool32 nonSeamless;
  };

  /**
   * \brief Sampler
   *
   * Manages a sampler object that can be bound to
   * a pipeline. Sampler objects provide parameters
   * for texture lookups within a shader.
   */
  class DxvkSampler : public DxvkResource {

  public:

    DxvkSampler(
            DxvkDevice*             device,
      const DxvkSamplerCreateInfo&  info);
    ~DxvkSampler();

    /**
     * \brief Sampler handle
     * \returns Sampler handle
     */
    VkSampler handle() const {
      return m_sampler;
    }

  private:

    Rc<vk::DeviceFn>      m_vkd;
    VkSampler             m_sampler = VK_NULL_HANDLE;

    static VkBorderColor getBorderColor(
      const Rc<DxvkDevice>&         device,
      const DxvkSamplerCreateInfo&  info);

  };

}


code:
#include "dxvk_shader.h"

#include <algorithm>
#include <unordered_map>
#include <unordered_set>

namespace dxvk {

  DxvkShaderModule::DxvkShaderModule()
  : m_vkd(nullptr), m_stage() {

  }

  DxvkShaderModule::DxvkShaderModule(DxvkShaderModule&& other)
  : m_vkd(std::move(other.m_vkd)) {
    this->m_stage = other.m_stage;
    other.m_stage = VkPipelineShaderStageCreateInfo();
  }

  DxvkShaderModule::DxvkShaderModule(
    const Rc<vk::DeviceFn>&     vkd,
    const Rc<DxvkShader>&       shader,
    const SpirvCodeBuffer&      code)
  : m_vkd(vkd), m_stage() {
    m_stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
    m_stage.pNext = nullptr;
    m_stage.flags = 0;
    m_stage.stage = shader->info().stage;
    m_stage.module = VK_NULL_HANDLE;
    m_stage.pName = "main";
    m_stage.pSpecializationInfo = nullptr;

    VkShaderModuleCreateInfo info;
    info.sType    = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
    info.pNext    = nullptr;
    info.flags    = 0;
    info.codeSize = code.size();
    info.pCode    = code.data();

    if (m_vkd->vkCreateShaderModule(m_vkd->device(), &info, nullptr, &m_stage.module) != VK_SUCCESS)
      throw DxvkError("DxvkComputePipeline::DxvkComputePipeline: Failed to create shader module");
  }

  DxvkShaderModule::~DxvkShaderModule() {
    if (m_vkd != nullptr) {
      m_vkd->vkDestroyShaderModule(
        m_vkd->device(), m_stage.module, nullptr);
    }
  }

  DxvkShaderModule& DxvkShaderModule::operator = (DxvkShaderModule&& other) {
    this->m_vkd   = std::move(other.m_vkd);
    this->m_stage = other.m_stage;
    other.m_stage = VkPipelineShaderStageCreateInfo();
    return *this;
  }

  DxvkShader::DxvkShader(
    const DxvkShaderCreateInfo&   info,
          SpirvCodeBuffer&&       spirv)
  : m_info(info), m_code(spirv) {
    m_info.resourceSlots = nullptr;
    m_info.uniformData = nullptr;

    // Copy resource binding slot infos
    if (info.resourceSlotCount) {
      m_slots.resize(info.resourceSlotCount);
      for (uint32_t i = 0; i < info.resourceSlotCount; i++)
        m_slots[i] = info.resourceSlots[i];
      m_info.resourceSlots = m_slots.data();
    }

    // Copy uniform buffer data
    if (info.uniformSize) {
      m_uniformData.resize(info.uniformSize);
      std::memcpy(m_uniformData.data(), info.uniformData, info.uniformSize);
      m_info.uniformData = m_uniformData.data();
    }

    // Run an analysis pass over the SPIR-V code to gather some
    // info that we may need during pipeline compilation.
    SpirvCodeBuffer code = std::move(spirv);
    uint32_t o1VarId = 0;

    for (auto ins : code) {
      if (ins.opCode() == spv::OpDecorate) {
        if (ins.arg(2) == spv::DecorationBinding
         || ins.arg(2) == spv::DecorationSpecId)
          m_idOffsets.push_back(ins.offset() + 3);

        if (ins.arg(2) == spv::DecorationLocation && ins.arg(3) == 1) {
          m_o1LocOffset = ins.offset() + 3;
          o1VarId = ins.arg(1);
        }

        if (ins.arg(2) == spv::DecorationIndex && ins.arg(1) == o1VarId)
          m_o1IdxOffset = ins.offset() + 3;
      }

      if (ins.opCode() == spv::OpExecutionMode) {
        if (ins.arg(2) == spv::ExecutionModeStencilRefReplacingEXT)
          m_flags.set(DxvkShaderFlag::ExportsStencilRef);

        if (ins.arg(2) == spv::ExecutionModeXfb)
          m_flags.set(DxvkShaderFlag::HasTransformFeedback);
      }

      if (ins.opCode() == spv::OpCapability) {
        if (ins.arg(1) == spv::CapabilitySampleRateShading)
          m_flags.set(DxvkShaderFlag::HasSampleRateShading);

        if (ins.arg(1) == spv::CapabilityShaderViewportIndexLayerEXT)
          m_flags.set(DxvkShaderFlag::ExportsViewportIndexLayerFromVertexStage);
      }
    }
  }

  DxvkShader::~DxvkShader() {

  }

  void DxvkShader::defineResourceSlots(
          DxvkDescriptorSlotMapping& mapping) const {
    for (const auto& slot : m_slots)
      mapping.defineSlot(m_info.stage, slot);

    if (m_info.pushConstSize) {
      mapping.definePushConstRange(m_info.stage,
        m_info.pushConstOffset,
        m_info.pushConstSize);
    }
  }

  DxvkShaderModule DxvkShader::createShaderModule(
    const Rc<vk::DeviceFn>&          vkd,
    const DxvkDescriptorSlotMapping& mapping,
    const DxvkShaderModuleCreateInfo& info) {
    SpirvCodeBuffer spirvCode = m_code.decompress();
    uint32_t* code = spirvCode.data();

    // Remap resource binding IDs
    for (uint32_t ofs : m_idOffsets) {
      if (code[ofs] < MaxNumResourceSlots)
        code[ofs] = mapping.getBindingId(code[ofs]);
    }

    // For dual-source blending we need to re-map
    // location 1, index 0 to location 0, index 1
    if (info.fsDualSrcBlend && m_o1IdxOffset && m_o1LocOffset)
      std::swap(code[m_o1IdxOffset], code[m_o1LocOffset]);

    // Replace undefined input variables with zero
    for (uint32_t u : bit::BitMask(info.undefinedInputs))
      eliminateInput(spirvCode, u);

    return DxvkShaderModule(vkd, this, spirvCode);
  }

  void DxvkShader::dump(std::ostream& outputStream) const {
    m_code.decompress().store(outputStream);
  }

  void DxvkShader::eliminateInput(SpirvCodeBuffer& code, uint32_t location) {
    struct SpirvTypeInfo {
      spv::Op           op            = spv::OpNop;
      uint32_t          baseTypeId    = 0;
      uint32_t          compositeSize = 0;
      spv::StorageClass storageClass  = spv::StorageClassMax;
    };

    std::unordered_map<uint32_t, SpirvTypeInfo> types;
    std::unordered_map<uint32_t, uint32_t>      constants;
    std::unordered_set<uint32_t>                candidates;

    // Find the input variable in question
    size_t   inputVarOffset = 0;
    uint32_t inputVarTypeId = 0;
    uint32_t inputVarId     = 0;

    for (auto ins : code) {
      if (ins.opCode() == spv::OpDecorate) {
        if (ins.arg(2) == spv::DecorationLocation
         && ins.arg(3) == location)
          candidates.insert(ins.arg(1));
      }

      if (ins.opCode() == spv::OpConstant)
        constants.insert({ ins.arg(2), ins.arg(3) });

      if (ins.opCode() == spv::OpTypeFloat || ins.opCode() == spv::OpTypeInt)
        types.insert({ ins.arg(1), { ins.opCode(), 0, ins.arg(2), spv::StorageClassMax }});

      if (ins.opCode() == spv::OpTypeVector)
        types.insert({ ins.arg(1), { ins.opCode(), ins.arg(2), ins.arg(3), spv::StorageClassMax }});

      if (ins.opCode() == spv::OpTypeArray) {
        auto constant = constants.find(ins.arg(3));
        if (constant == constants.end())
          continue;
        types.insert({ ins.arg(1), { ins.opCode(), ins.arg(2), constant->second, spv::StorageClassMax }});
      }

      if (ins.opCode() == spv::OpTypePointer)
        types.insert({ ins.arg(1), { ins.opCode(), ins.arg(3), 0, spv::StorageClass(ins.arg(2)) }});

      if (ins.opCode() == spv::OpVariable && spv::StorageClass(ins.arg(3)) == spv::StorageClassInput) {
        if (candidates.find(ins.arg(2)) != candidates.end()) {
          inputVarOffset = ins.offset();
          inputVarTypeId = ins.arg(1);
          inputVarId     = ins.arg(2);
          break;
        }
      }
    }

    if (!inputVarId)
      return;

    // Declare private pointer types
    auto pointerType = types.find(inputVarTypeId);
    if (pointerType == types.end())
      return;

    code.beginInsertion(inputVarOffset);
    std::vector<std::pair<uint32_t, SpirvTypeInfo>> privateTypes;

    for (auto p  = types.find(pointerType->second.baseTypeId);
              p != types.end();
              p  = types.find(p->second.baseTypeId)) {
      std::pair<uint32_t, SpirvTypeInfo> info = *p;
      info.first = 0;
      info.second.baseTypeId = p->first;
      info.second.storageClass = spv::StorageClassPrivate;

      for (auto t : types) {
        if (t.second.op           == info.second.op
         && t.second.baseTypeId   == info.second.baseTypeId
         && t.second.storageClass == info.second.storageClass)
          info.first = t.first;
      }

      if (!info.first) {
        info.first = code.allocId();

        code.putIns(spv::OpTypePointer, 4);
        code.putWord(info.first);
        code.putWord(info.second.storageClass);
        code.putWord(info.second.baseTypeId);
      }

      privateTypes.push_back(info);
    }

    // Define zero constants
    uint32_t constantId = 0;

    for (auto i = privateTypes.rbegin(); i != privateTypes.rend(); i++) {
      if (constantId) {
        uint32_t compositeSize = i->second.compositeSize;
        uint32_t compositeId   = code.allocId();

        code.putIns(spv::OpConstantComposite, 3 + compositeSize);
        code.putWord(i->second.baseTypeId);
        code.putWord(compositeId);

        for (uint32_t i = 0; i < compositeSize; i++)
          code.putWord(constantId);

        constantId = compositeId;
      } else {
        constantId = code.allocId();

        code.putIns(spv::OpConstant, 4);
        code.putWord(i->second.baseTypeId);
        code.putWord(constantId);
        code.putWord(0);
      }
    }

    // Erase and re-declare variable
    code.erase(4);

    code.putIns(spv::OpVariable, 5);
    code.putWord(privateTypes[0].first);
    code.putWord(inputVarId);
    code.putWord(spv::StorageClassPrivate);
    code.putWord(constantId);

    code.endInsertion();

    // Remove variable from interface list
    for (auto ins : code) {
      if (ins.opCode() == spv::OpEntryPoint) {
        uint32_t argIdx = 2 + code.strLen(ins.chr(2));

        while (argIdx < ins.length()) {
          if (ins.arg(argIdx) == inputVarId) {
            ins.setArg(0, spv::OpEntryPoint | ((ins.length() - 1) << spv::WordCountShift));

            code.beginInsertion(ins.offset() + argIdx);
            code.erase(1);
            code.endInsertion();
            break;
          }

          argIdx += 1;
        }
      }
    }

    // Remove location and other declarations
    for (auto iter = code.begin(); iter != code.end(); ) {
      auto ins = *(iter++);

      if (ins.opCode() == spv::OpDecorate && ins.arg(1) == inputVarId) {
        uint32_t numWords;

        switch (ins.arg(2)) {
          case spv::DecorationLocation:
          case spv::DecorationFlat:
          case spv::DecorationNoPerspective:
          case spv::DecorationCentroid:
          case spv::DecorationPatch:
          case spv::DecorationSample:
            numWords = ins.length();
            break;

          default:
            numWords = 0;
        }

        if (numWords) {
          code.beginInsertion(ins.offset());
          code.erase(numWords);

          iter = SpirvInstructionIterator(code.data(), code.endInsertion(), code.dwords());
        }
      }

      if (ins.opCode() == spv::OpFunction)
        break;
    }

    // Fix up pointer types used in access chain instructions
    std::unordered_map<uint32_t, uint32_t> accessChainIds;

    for (auto ins : code) {
      if (ins.opCode() == spv::OpAccessChain
       || ins.opCode() == spv::OpInBoundsAccessChain) {
        uint32_t depth = ins.length() - 4;

        if (ins.arg(3) == inputVarId) {
          // Access chains accessing the variable directly
          ins.setArg(1, privateTypes.at(depth).first);
          accessChainIds.insert({ ins.arg(2), depth });
        } else {
          // Access chains derived from the variable
          auto entry = accessChainIds.find(ins.arg(2));
          if (entry != accessChainIds.end()) {
            depth += entry->second;
            ins.setArg(1, privateTypes.at(depth).first);
            accessChainIds.insert({ ins.arg(2), depth });
          }
        }
      }
    }
  }

}


code:
#pragma once

#include <vector>

#include "dxvk_include.h"
#include "dxvk_limits.h"
#include "dxvk_pipelayout.h"
#include "dxvk_shader_key.h"

#include "../spirv/spirv_code_buffer.h"
#include "../spirv/spirv_compression.h"

namespace dxvk {

  class DxvkShader;
  class DxvkShaderModule;

  /**
   * \brief Built-in specialization constants
   *
   * These specialization constants allow the SPIR-V
   * shaders to access some pipeline state like D3D
   * shaders do. They need to be filled in by the
   * implementation at pipeline compilation time.
   */
  enum class DxvkSpecConstantId : uint32_t {
    /// Special constant ranges that do not count
    /// towards the spec constant min/max values
    ColorComponentMappings      = MaxNumResourceSlots,

    // Specialization constants for pipeline state
    SpecConstantRangeStart      = ColorComponentMappings + MaxNumRenderTargets,
    RasterizerSampleCount       = SpecConstantRangeStart + 0,
    FirstPipelineConstant
  };

  /**
   * \brief Shader flags
   *
   * Provides extra information about the features
   * used by a shader.
   */
  enum DxvkShaderFlag : uint64_t {
    HasSampleRateShading,
    HasTransformFeedback,
    ExportsStencilRef,
    ExportsViewportIndexLayerFromVertexStage,
  };

  using DxvkShaderFlags = Flags<DxvkShaderFlag>;

  /**
   * \brief Shader info
   */
  struct DxvkShaderCreateInfo {
    /// Shader stage
    VkShaderStageFlagBits stage;
    /// Descriptor info
    uint32_t resourceSlotCount = 0;
    const DxvkResourceSlot* resourceSlots = nullptr;
    /// Input and output register mask
    uint32_t inputMask = 0;
    uint32_t outputMask = 0;
    /// Push constant range
    uint32_t pushConstOffset = 0;
    uint32_t pushConstSize = 0;
    /// Uniform buffer data
    uint32_t uniformSize = 0;
    const char* uniformData = nullptr;
    /// Rasterized stream, or -1
    int32_t xfbRasterizedStream = 0;
    /// Transform feedback vertex strides
    uint32_t xfbStrides[MaxNumXfbBuffers] = { };
  };

  /**
   * \brief Shader module create info
   */
  struct DxvkShaderModuleCreateInfo {
    bool      fsDualSrcBlend  = false;
    uint32_t  undefinedInputs = 0;
  };

  /**
   * \brief Shader object
   *
   * Stores a SPIR-V shader and information on the
   * bindings that the shader uses. In order to use
   * the shader with a pipeline, a shader module
   * needs to be created from he shader object.
   */
  class DxvkShader : public RcObject {

  public:

    DxvkShader(
      const DxvkShaderCreateInfo&   info,
            SpirvCodeBuffer&&       spirv);

    ~DxvkShader();

    /**
     * \brief Shader info
     * \returns Shader info
     */
    const DxvkShaderCreateInfo& info() const {
      return m_info;
    }

    /**
     * \brief Retrieves shader flags
     * \returns Shader flags
     */
    DxvkShaderFlags flags() const {
      return m_flags;
    }

    /**
     * \brief Adds resource slots definitions to a mapping
     *
     * Used to generate the exact descriptor set layout when
     * compiling a graphics or compute pipeline. Slot indices
     * have to be mapped to actual binding numbers.
     */
    void defineResourceSlots(
            DxvkDescriptorSlotMapping& mapping) const;

    /**
     * \brief Creates a shader module
     *
     * Maps the binding slot numbers
     * \param [in] vkd Vulkan device functions
     * \param [in] mapping Resource slot mapping
     * \param [in] info Module create info
     * \returns The shader module
     */
    DxvkShaderModule createShaderModule(
      const Rc<vk::DeviceFn>&          vkd,
      const DxvkDescriptorSlotMapping& mapping,
      const DxvkShaderModuleCreateInfo& info);

    /**
     * \brief Dumps SPIR-V shader
     *
     * Can be used to store the SPIR-V code in a file.
     * \param [in] outputStream Stream to write to
     */
    void dump(std::ostream& outputStream) const;

    /**
     * \brief Sets the shader key
     * \param [in] key Unique key
     */
    void setShaderKey(const DxvkShaderKey& key) {
      m_key = key;
      m_hash = key.hash();
    }

    /**
     * \brief Retrieves shader key
     * \returns The unique shader key
     */
    DxvkShaderKey getShaderKey() const {
      return m_key;
    }

    /**
     * \brief Get lookup hash
     *
     * Retrieves a non-unique hash value derived from the
     * shader key which can be used to perform lookups.
     * This is better than relying on the pointer value.
     * \returns Hash value for map lookups
     */
    size_t getHash() const {
      return m_hash;
    }

    /**
     * \brief Retrieves debug name
     * \returns The shader's name
     */
    std::string debugName() const {
      return m_key.toString();
    }

    /**
     * \brief Get lookup hash for a shader
     *
     * Convenience method that returns \c 0 for a null
     * pointer, and the shader's lookup hash otherwise.
     * \param [in] shader The shader
     * \returns The shader's lookup hash, or 0
     */
    static size_t getHash(const Rc<DxvkShader>& shader) {
      return shader != nullptr ? shader->getHash() : 0;
    }

  private:

    DxvkShaderCreateInfo          m_info;
    SpirvCompressedBuffer         m_code;

    DxvkShaderFlags               m_flags;
    DxvkShaderKey                 m_key;
    size_t                        m_hash = 0;

    size_t                        m_o1IdxOffset = 0;
    size_t                        m_o1LocOffset = 0;

    std::vector<DxvkResourceSlot> m_slots;
    std::vector<char>             m_uniformData;
    std::vector<size_t>           m_idOffsets;

    static void eliminateInput(SpirvCodeBuffer& code, uint32_t location);

  };

  /**
   * \brief Shader module object
   *
   * Manages a Vulkan shader module. This will not
   * perform any shader compilation. Instead, the
   * context will create pipeline objects on the
   * fly when executing draw calls.
   */
  class DxvkShaderModule {

  public:

    DxvkShaderModule();

    DxvkShaderModule(DxvkShaderModule&& other);

    DxvkShaderModule(
      const Rc<vk::DeviceFn>&     vkd,
      const Rc<DxvkShader>&       shader,
      const SpirvCodeBuffer&      code);

    ~DxvkShaderModule();

    DxvkShaderModule& operator = (DxvkShaderModule&& other);

    /**
     * \brief Shader stage creation info
     *
     * \param [in] specInfo Specialization info
     * \returns Shader stage create info
     */
    VkPipelineShaderStageCreateInfo stageInfo(
      const VkSpecializationInfo* specInfo) const {
      VkPipelineShaderStageCreateInfo stage = m_stage;
      stage.pSpecializationInfo = specInfo;
      return stage;
    }

    /**
     * \brief Checks whether module is valid
     * \returns \c true if module is valid
     */
    operator bool () const {
      return m_stage.module != VK_NULL_HANDLE;
    }

  private:

    Rc<vk::DeviceFn>                m_vkd;
    VkPipelineShaderStageCreateInfo m_stage;

  };

}


code:
#include "dxvk_shader_key.h"

namespace dxvk {

  DxvkShaderKey::DxvkShaderKey()
  : m_type(0),
    m_sha1(Sha1Hash::compute(nullptr, 0)) { }

  std::string DxvkShaderKey::toString() const {
    const char* prefix = nullptr;

    switch (m_type) {
      case VK_SHADER_STAGE_VERTEX_BIT:                  prefix = "VS_";  break;
      case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:    prefix = "TCS_"; break;
      case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: prefix = "TES_"; break;
      case VK_SHADER_STAGE_GEOMETRY_BIT:                prefix = "GS_";  break;
      case VK_SHADER_STAGE_FRAGMENT_BIT:                prefix = "FS_";  break;
      case VK_SHADER_STAGE_COMPUTE_BIT:                 prefix = "CS_";  break;
      default:                                          prefix = "";
    }

    return str::format(prefix, m_sha1.toString());
  }

  size_t DxvkShaderKey::hash() const {
    DxvkHashState result;
    result.add(uint32_t(m_type));

    for (uint32_t i = 0; i < 5; i++)
      result.add(m_sha1.dword(i));

    return result;
  }

  bool DxvkShaderKey::eq(const DxvkShaderKey& key) const {
    return m_type == key.m_type
        && m_sha1 == key.m_sha1;
  }

}


code:
#pragma once

#include "dxvk_hash.h"
#include "dxvk_include.h"

namespace dxvk {

  /**
   * \brief Shader key
   *
   * Provides a unique key that can be used
   * to look up a specific shader within a
   * structure. This consists of the shader
   * stage and the source hash, which should
   * be generated from the original code.
   */
  class DxvkShaderKey {

  public:

    /**
     * \brief Creates default shader key
     */
    DxvkShaderKey();

    /**
     * \brief Creates shader key
     *
     * \param [in] stage Shader stage
     * \param [in] hash Shader hash
     */
    DxvkShaderKey(
            VkShaderStageFlagBits stage,
            Sha1Hash              hash)
    : m_type(stage), m_sha1(hash) { }

    /**
     * \brief Generates string from shader key
     * \returns String representation of the key
     */
    std::string toString() const;

    /**
     * \brief Computes lookup hash
     * \returns Lookup hash
     */
    size_t hash() const;

    /**
     * \brief Shader type
     * \returns Shader type
     */
    VkShaderStageFlags type() const { return m_type; }

    /**
     * \brief Shader SHA1
     * \returns Shader SHA1
     */
    const Sha1Hash& sha1() const { return m_sha1; }

    /**
     * \brief Checks whether two keys are equal
     *
     * \param [in] key The shader key to compare to
     * \returns \c true if the two keys are equal
     */
    bool eq(const DxvkShaderKey& key) const;

  private:

    VkShaderStageFlags  m_type;
    Sha1Hash            m_sha1;

  };

}


code:
#include "dxvk_signal.h"

namespace dxvk {

  DxvkSignalTracker::DxvkSignalTracker() {

  }

  DxvkSignalTracker::~DxvkSignalTracker() {

  }

  void DxvkSignalTracker::add(const Rc<sync::Signal>& signal, uint64_t value) {
    m_signals.push_back({ signal, value });
  }

  void DxvkSignalTracker::notify() {
    for (const auto& pair : m_signals)
      pair.first->signal(pair.second);
  }

  void DxvkSignalTracker::reset() {
    m_signals.clear();
  }

}


code:
#pragma once

#include "dxvk_include.h"

namespace dxvk {

  /**
   * \brief Signal tracker
   */
  class DxvkSignalTracker {

  public:

    DxvkSignalTracker();
    ~DxvkSignalTracker();

    /**
     * \brief Adds a signal to track
     *
     * \param [in] signal The signal
     * \param [in] value Target value
     */
    void add(const Rc<sync::Signal>& signal, uint64_t value);

    /**
     * \brief Notifies tracked signals
     */
    void notify();

    /**
     * \brief Resets signal tracker
     */
    void reset();

  private:

    std::vector<std::pair<Rc<sync::Signal>, uint64_t>> m_signals;

  };

}


code:
#include "dxvk_spec_const.h"

namespace dxvk {

  DxvkSpecConstants::DxvkSpecConstants() {

  }

  DxvkSpecConstants::~DxvkSpecConstants() {

  }

  VkSpecializationInfo DxvkSpecConstants::getSpecInfo() const {
    VkSpecializationInfo specInfo;
    specInfo.mapEntryCount = m_map.size();
    specInfo.pMapEntries   = m_map.data();
    specInfo.dataSize      = m_data.size() * sizeof(uint32_t);
    specInfo.pData         = m_data.data();
    return specInfo;
  }

  void DxvkSpecConstants::setAsUint32(uint32_t specId, uint32_t value) {
    uint32_t index = m_data.size();
    m_data.push_back(value);

    VkSpecializationMapEntry mapEntry;
    mapEntry.constantID = specId;
    mapEntry.offset     = sizeof(uint32_t) * index;
    mapEntry.size       = sizeof(uint32_t);
    m_map.push_back(mapEntry);
  }

}