code:
#include <cstring>
#include <unordered_set>

#include "dxvk_adapter.h"
#include "dxvk_device.h"
#include "dxvk_instance.h"

namespace dxvk {

  DxvkAdapter::DxvkAdapter(
    const Rc<vk::InstanceFn>& vki,
          VkPhysicalDevice    handle)
  : m_vki           (vki),
    m_handle        (handle) {
    this->initHeapAllocInfo();
    this->queryExtensions();
    this->queryDeviceInfo();
    this->queryDeviceFeatures();
    this->queryDeviceQueues();

    m_hasMemoryBudget = m_deviceExtensions.supports(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME);
  }

  DxvkAdapter::~DxvkAdapter() {

  }

  DxvkAdapterMemoryInfo DxvkAdapter::getMemoryHeapInfo() const {
    VkPhysicalDeviceMemoryBudgetPropertiesEXT memBudget = { };
    memBudget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT;
    memBudget.pNext = nullptr;

    VkPhysicalDeviceMemoryProperties2 memProps = { };
    memProps.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2;
    memProps.pNext = m_hasMemoryBudget ? &memBudget : nullptr;

    m_vki->vkGetPhysicalDeviceMemoryProperties2(m_handle, &memProps);

    DxvkAdapterMemoryInfo info = { };
    info.heapCount = memProps.memoryProperties.memoryHeapCount;

    for (uint32_t i = 0; i < info.heapCount; i++) {
      info.heaps[i].heapFlags = memProps.memoryProperties.memoryHeaps[i].flags;

      if (m_hasMemoryBudget) {
        info.heaps[i].memoryBudget    = memBudget.heapBudget[i];
        info.heaps[i].memoryAllocated = memBudget.heapUsage[i];
      } else {
        info.heaps[i].memoryBudget    = memProps.memoryProperties.memoryHeaps[i].size;
        info.heaps[i].memoryAllocated = m_heapAlloc[i].load();
      }
    }

    return info;
  }

  VkPhysicalDeviceMemoryProperties DxvkAdapter::memoryProperties() const {
    VkPhysicalDeviceMemoryProperties memoryProperties;
    m_vki->vkGetPhysicalDeviceMemoryProperties(m_handle, &memoryProperties);
    return memoryProperties;
  }

  VkFormatProperties DxvkAdapter::formatProperties(VkFormat format) const {
    VkFormatProperties formatProperties;
    m_vki->vkGetPhysicalDeviceFormatProperties(m_handle, format, &formatProperties);
    return formatProperties;
  }

  VkResult DxvkAdapter::imageFormatProperties(
    VkFormat                  format,
    VkImageType               type,
    VkImageTiling             tiling,
    VkImageUsageFlags         usage,
    VkImageCreateFlags        flags,
    VkImageFormatProperties&  properties) const {
    return m_vki->vkGetPhysicalDeviceImageFormatProperties(
      m_handle, format, type, tiling, usage, flags, &properties);
  }

  DxvkAdapterQueueIndices DxvkAdapter::findQueueFamilies() const {
    uint32_t graphicsQueue = findQueueFamily(
      VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT,
      VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT);

    uint32_t computeQueue = findQueueFamily(
      VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT,
      VK_QUEUE_COMPUTE_BIT);

    if (computeQueue == VK_QUEUE_FAMILY_IGNORED)
      computeQueue = graphicsQueue;

    uint32_t transferQueue = findQueueFamily(
      VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT,
      VK_QUEUE_TRANSFER_BIT);

    if (transferQueue == VK_QUEUE_FAMILY_IGNORED)
      transferQueue = computeQueue;

    DxvkAdapterQueueIndices queues;
    queues.graphics = graphicsQueue;
    queues.transfer = transferQueue;
    return queues;
  }

  bool DxvkAdapter::checkFeatureSupport(const DxvkDeviceFeatures& required) const {
    return (m_deviceFeatures.core.features.robustBufferAccess
                || !required.core.features.robustBufferAccess)
        && (m_deviceFeatures.core.features.fullDrawIndexUint32
                || !required.core.features.fullDrawIndexUint32)
        && (m_deviceFeatures.core.features.imageCubeArray
                || !required.core.features.imageCubeArray)
        && (m_deviceFeatures.core.features.independentBlend
                || !required.core.features.independentBlend)
        && (m_deviceFeatures.core.features.geometryShader
                || !required.core.features.geometryShader)
        && (m_deviceFeatures.core.features.tessellationShader
                || !required.core.features.tessellationShader)
        && (m_deviceFeatures.core.features.sampleRateShading
                || !required.core.features.sampleRateShading)
        && (m_deviceFeatures.core.features.dualSrcBlend
                || !required.core.features.dualSrcBlend)
        && (m_deviceFeatures.core.features.logicOp
                || !required.core.features.logicOp)
        && (m_deviceFeatures.core.features.multiDrawIndirect
                || !required.core.features.multiDrawIndirect)
        && (m_deviceFeatures.core.features.drawIndirectFirstInstance
                || !required.core.features.drawIndirectFirstInstance)
        && (m_deviceFeatures.core.features.depthClamp
                || !required.core.features.depthClamp)
        && (m_deviceFeatures.core.features.depthBiasClamp
                || !required.core.features.depthBiasClamp)
        && (m_deviceFeatures.core.features.fillModeNonSolid
                || !required.core.features.fillModeNonSolid)
        && (m_deviceFeatures.core.features.depthBounds
                || !required.core.features.depthBounds)
        && (m_deviceFeatures.core.features.wideLines
                || !required.core.features.wideLines)
        && (m_deviceFeatures.core.features.largePoints
                || !required.core.features.largePoints)
        && (m_deviceFeatures.core.features.alphaToOne
                || !required.core.features.alphaToOne)
        && (m_deviceFeatures.core.features.multiViewport
                || !required.core.features.multiViewport)
        && (m_deviceFeatures.core.features.samplerAnisotropy
                || !required.core.features.samplerAnisotropy)
        && (m_deviceFeatures.core.features.textureCompressionETC2
                || !required.core.features.textureCompressionETC2)
        && (m_deviceFeatures.core.features.textureCompressionASTC_LDR
                || !required.core.features.textureCompressionASTC_LDR)
        && (m_deviceFeatures.core.features.textureCompressionBC
                || !required.core.features.textureCompressionBC)
        && (m_deviceFeatures.core.features.occlusionQueryPrecise
                || !required.core.features.occlusionQueryPrecise)
        && (m_deviceFeatures.core.features.pipelineStatisticsQuery
                || !required.core.features.pipelineStatisticsQuery)
        && (m_deviceFeatures.core.features.vertexPipelineStoresAndAtomics
                || !required.core.features.vertexPipelineStoresAndAtomics)
        && (m_deviceFeatures.core.features.fragmentStoresAndAtomics
                || !required.core.features.fragmentStoresAndAtomics)
        && (m_deviceFeatures.core.features.shaderTessellationAndGeometryPointSize
                || !required.core.features.shaderTessellationAndGeometryPointSize)
        && (m_deviceFeatures.core.features.shaderImageGatherExtended
                || !required.core.features.shaderImageGatherExtended)
        && (m_deviceFeatures.core.features.shaderStorageImageExtendedFormats
                || !required.core.features.shaderStorageImageExtendedFormats)
        && (m_deviceFeatures.core.features.shaderStorageImageMultisample
                || !required.core.features.shaderStorageImageMultisample)
        && (m_deviceFeatures.core.features.shaderStorageImageReadWithoutFormat
                || !required.core.features.shaderStorageImageReadWithoutFormat)
        && (m_deviceFeatures.core.features.shaderStorageImageWriteWithoutFormat
                || !required.core.features.shaderStorageImageWriteWithoutFormat)
        && (m_deviceFeatures.core.features.shaderUniformBufferArrayDynamicIndexing
                || !required.core.features.shaderUniformBufferArrayDynamicIndexing)
        && (m_deviceFeatures.core.features.shaderSampledImageArrayDynamicIndexing
                || !required.core.features.shaderSampledImageArrayDynamicIndexing)
        && (m_deviceFeatures.core.features.shaderStorageBufferArrayDynamicIndexing
                || !required.core.features.shaderStorageBufferArrayDynamicIndexing)
        && (m_deviceFeatures.core.features.shaderStorageImageArrayDynamicIndexing
                || !required.core.features.shaderStorageImageArrayDynamicIndexing)
        && (m_deviceFeatures.core.features.shaderClipDistance
                || !required.core.features.shaderClipDistance)
        && (m_deviceFeatures.core.features.shaderCullDistance
                || !required.core.features.shaderCullDistance)
        && (m_deviceFeatures.core.features.shaderFloat64
                || !required.core.features.shaderFloat64)
        && (m_deviceFeatures.core.features.shaderInt64
                || !required.core.features.shaderInt64)
        && (m_deviceFeatures.core.features.shaderInt16
                || !required.core.features.shaderInt16)
        && (m_deviceFeatures.core.features.shaderResourceResidency
                || !required.core.features.shaderResourceResidency)
        && (m_deviceFeatures.core.features.shaderResourceMinLod
                || !required.core.features.shaderResourceMinLod)
        && (m_deviceFeatures.core.features.sparseBinding
                || !required.core.features.sparseBinding)
        && (m_deviceFeatures.core.features.sparseResidencyBuffer
                || !required.core.features.sparseResidencyBuffer)
        && (m_deviceFeatures.core.features.sparseResidencyImage2D
                || !required.core.features.sparseResidencyImage2D)
        && (m_deviceFeatures.core.features.sparseResidencyImage3D
                || !required.core.features.sparseResidencyImage3D)
        && (m_deviceFeatures.core.features.sparseResidency2Samples
                || !required.core.features.sparseResidency2Samples)
        && (m_deviceFeatures.core.features.sparseResidency4Samples
                || !required.core.features.sparseResidency4Samples)
        && (m_deviceFeatures.core.features.sparseResidency8Samples
                || !required.core.features.sparseResidency8Samples)
        && (m_deviceFeatures.core.features.sparseResidency16Samples
                || !required.core.features.sparseResidency16Samples)
        && (m_deviceFeatures.core.features.sparseResidencyAliased
                || !required.core.features.sparseResidencyAliased)
        && (m_deviceFeatures.core.features.variableMultisampleRate
                || !required.core.features.variableMultisampleRate)
        && (m_deviceFeatures.core.features.inheritedQueries
                || !required.core.features.inheritedQueries)
        && (m_deviceFeatures.shaderDrawParameters.shaderDrawParameters
                || !required.shaderDrawParameters.shaderDrawParameters)
        && (m_deviceFeatures.ext4444Formats.formatA4R4G4B4
                || !required.ext4444Formats.formatA4R4G4B4)
        && (m_deviceFeatures.ext4444Formats.formatA4B4G4R4
                || !required.ext4444Formats.formatA4B4G4R4)
        && (m_deviceFeatures.extCustomBorderColor.customBorderColors
                || !required.extCustomBorderColor.customBorderColors)
        && (m_deviceFeatures.extCustomBorderColor.customBorderColorWithoutFormat
                || !required.extCustomBorderColor.customBorderColorWithoutFormat)
        && (m_deviceFeatures.extDepthClipEnable.depthClipEnable
                || !required.extDepthClipEnable.depthClipEnable)
        && (m_deviceFeatures.extExtendedDynamicState.extendedDynamicState
                || !required.extExtendedDynamicState.extendedDynamicState)
        && (m_deviceFeatures.extHostQueryReset.hostQueryReset
                || !required.extHostQueryReset.hostQueryReset)
        && (m_deviceFeatures.extMemoryPriority.memoryPriority
                || !required.extMemoryPriority.memoryPriority)
        && (m_deviceFeatures.extNonSeamlessCubeMap.nonSeamlessCubeMap
                || !required.extNonSeamlessCubeMap.nonSeamlessCubeMap)
        && (m_deviceFeatures.extRobustness2.robustBufferAccess2
                || !required.extRobustness2.robustBufferAccess2)
        && (m_deviceFeatures.extRobustness2.robustImageAccess2
                || !required.extRobustness2.robustImageAccess2)
        && (m_deviceFeatures.extRobustness2.nullDescriptor
                || !required.extRobustness2.nullDescriptor)
        && (m_deviceFeatures.extTransformFeedback.transformFeedback
                || !required.extTransformFeedback.transformFeedback)
        && (m_deviceFeatures.extVertexAttributeDivisor.vertexAttributeInstanceRateDivisor
                || !required.extVertexAttributeDivisor.vertexAttributeInstanceRateDivisor)
        && (m_deviceFeatures.extVertexAttributeDivisor.vertexAttributeInstanceRateZeroDivisor
                || !required.extVertexAttributeDivisor.vertexAttributeInstanceRateZeroDivisor)
        && (m_deviceFeatures.khrTimelineSemaphore.timelineSemaphore
                || !required.khrTimelineSemaphore.timelineSemaphore);
  }

  void DxvkAdapter::enableExtensions(const DxvkNameSet& extensions) {
    m_extraExtensions.merge(extensions);
  }

  Rc<DxvkDevice> DxvkAdapter::createDevice(
    const Rc<DxvkInstance>&   instance,
          DxvkDeviceFeatures  enabledFeatures) {
    DxvkDeviceExtensions devExtensions;

    std::array<DxvkExt*, 32> devExtensionList = {{
      &devExtensions.amdMemoryOverallocationBehaviour,
      &devExtensions.amdShaderFragmentMask,
      &devExtensions.ext4444Formats,
      &devExtensions.extConservativeRasterization,
      &devExtensions.extCustomBorderColor,
      &devExtensions.extDepthClipEnable,
      &devExtensions.extExtendedDynamicState,
      &devExtensions.extFullScreenExclusive,
      &devExtensions.extHostQueryReset,
      &devExtensions.extMemoryBudget,
      &devExtensions.extMemoryPriority,
      &devExtensions.extNonSeamlessCubeMap,
      &devExtensions.extRobustness2,
      &devExtensions.extShaderDemoteToHelperInvocation,
      &devExtensions.extShaderStencilExport,
      &devExtensions.extShaderViewportIndexLayer,
      &devExtensions.extTransformFeedback,
      &devExtensions.extVertexAttributeDivisor,
      &devExtensions.khrBufferDeviceAddress,
      &devExtensions.khrCreateRenderPass2,
      &devExtensions.khrDepthStencilResolve,
      &devExtensions.khrDrawIndirectCount,
      &devExtensions.khrDriverProperties,
      &devExtensions.khrExternalMemoryWin32,
      &devExtensions.khrExternalSemaphoreWin32,
      &devExtensions.khrImageFormatList,
      &devExtensions.khrSamplerMirrorClampToEdge,
      &devExtensions.khrShaderFloatControls,
      &devExtensions.khrSwapchain,
      &devExtensions.khrTimelineSemaphore,
      &devExtensions.nvxBinaryImport,
      &devExtensions.nvxImageViewHandle,
    }};

    // Only enable Cuda interop extensions in 64-bit builds in
    // order to avoid potential driver or address space issues.
    // VK_KHR_buffer_device_address is expensive on some drivers.
    bool enableCudaInterop = !env::is32BitHostPlatform() &&
      m_deviceExtensions.supports(devExtensions.nvxBinaryImport.name()) &&
      m_deviceExtensions.supports(devExtensions.nvxImageViewHandle.name()) &&
      m_deviceFeatures.khrBufferDeviceAddress.bufferDeviceAddress;

    if (enableCudaInterop) {
      devExtensions.nvxBinaryImport.setMode(DxvkExtMode::Optional);
      devExtensions.nvxImageViewHandle.setMode(DxvkExtMode::Optional);
      devExtensions.khrBufferDeviceAddress.setMode(DxvkExtMode::Optional);

      enabledFeatures.khrBufferDeviceAddress.bufferDeviceAddress = VK_TRUE;
    }

    DxvkNameSet extensionsEnabled;

    if (!m_deviceExtensions.enableExtensions(
          devExtensionList.size(),
          devExtensionList.data(),
          extensionsEnabled))
      throw DxvkError("DxvkAdapter: Failed to create device");

    // Enable additional extensions if necessary
    extensionsEnabled.merge(m_extraExtensions);
    DxvkNameList extensionNameList = extensionsEnabled.toNameList();

    // Enable additional device features if supported
    enabledFeatures.extExtendedDynamicState.extendedDynamicState = m_deviceFeatures.extExtendedDynamicState.extendedDynamicState;

    enabledFeatures.ext4444Formats.formatA4B4G4R4 = m_deviceFeatures.ext4444Formats.formatA4B4G4R4;
    enabledFeatures.ext4444Formats.formatA4R4G4B4 = m_deviceFeatures.ext4444Formats.formatA4R4G4B4;

    Logger::info(str::format("Device properties:"
      "\n  Device name:     : ", m_deviceInfo.core.properties.deviceName,
      "\n  Driver version   : ",
        VK_VERSION_MAJOR(m_deviceInfo.core.properties.driverVersion), ".",
        VK_VERSION_MINOR(m_deviceInfo.core.properties.driverVersion), ".",
        VK_VERSION_PATCH(m_deviceInfo.core.properties.driverVersion)));

    Logger::info("Enabled device extensions:");
    this->logNameList(extensionNameList);
    this->logFeatures(enabledFeatures);

    // Create pNext chain for additional device features
    enabledFeatures.core.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR;
    enabledFeatures.core.pNext = nullptr;

    enabledFeatures.shaderDrawParameters.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES;
    enabledFeatures.shaderDrawParameters.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.shaderDrawParameters);

    if (devExtensions.ext4444Formats) {
      enabledFeatures.ext4444Formats.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_4444_FORMATS_FEATURES_EXT;
      enabledFeatures.ext4444Formats.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.ext4444Formats);
    }

    if (devExtensions.extCustomBorderColor) {
      enabledFeatures.extCustomBorderColor.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT;
      enabledFeatures.extCustomBorderColor.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.extCustomBorderColor);
    }

    if (devExtensions.extDepthClipEnable) {
      enabledFeatures.extDepthClipEnable.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT;
      enabledFeatures.extDepthClipEnable.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.extDepthClipEnable);
    }

    if (devExtensions.extExtendedDynamicState) {
      enabledFeatures.extExtendedDynamicState.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
      enabledFeatures.extExtendedDynamicState.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.extExtendedDynamicState);
    }

    if (devExtensions.extHostQueryReset) {
      enabledFeatures.extHostQueryReset.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT;
      enabledFeatures.extHostQueryReset.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.extHostQueryReset);
    }

    if (devExtensions.extMemoryPriority) {
      enabledFeatures.extMemoryPriority.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PRIORITY_FEATURES_EXT;
      enabledFeatures.extMemoryPriority.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.extMemoryPriority);
    }

    if (devExtensions.extNonSeamlessCubeMap) {
      enabledFeatures.extNonSeamlessCubeMap.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_NON_SEAMLESS_CUBE_MAP_FEATURES_EXT;
      enabledFeatures.extNonSeamlessCubeMap.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.extNonSeamlessCubeMap);
    }

    if (devExtensions.extShaderDemoteToHelperInvocation) {
      enabledFeatures.extShaderDemoteToHelperInvocation.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT;
      enabledFeatures.extShaderDemoteToHelperInvocation.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.extShaderDemoteToHelperInvocation);
    }

    if (devExtensions.extRobustness2) {
      enabledFeatures.extRobustness2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT;
      enabledFeatures.extRobustness2.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.extRobustness2);
    }

    if (devExtensions.extTransformFeedback) {
      enabledFeatures.extTransformFeedback.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT;
      enabledFeatures.extTransformFeedback.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.extTransformFeedback);
    }

    if (devExtensions.extVertexAttributeDivisor.revision() >= 3) {
      enabledFeatures.extVertexAttributeDivisor.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT;
      enabledFeatures.extVertexAttributeDivisor.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.extVertexAttributeDivisor);
    }

    if (devExtensions.khrBufferDeviceAddress) {
      enabledFeatures.khrBufferDeviceAddress.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_KHR;
      enabledFeatures.khrBufferDeviceAddress.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.khrBufferDeviceAddress);
    }

    if (devExtensions.khrTimelineSemaphore) {
      enabledFeatures.khrTimelineSemaphore.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR;
      enabledFeatures.khrTimelineSemaphore.pNext = std::exchange(enabledFeatures.core.pNext, &enabledFeatures.khrTimelineSemaphore);
    }

    // Report the desired overallocation behaviour to the driver
    VkDeviceMemoryOverallocationCreateInfoAMD overallocInfo;
    overallocInfo.sType = VK_STRUCTURE_TYPE_DEVICE_MEMORY_OVERALLOCATION_CREATE_INFO_AMD;
    overallocInfo.pNext = nullptr;
    overallocInfo.overallocationBehavior = VK_MEMORY_OVERALLOCATION_BEHAVIOR_ALLOWED_AMD;

    // Create the requested queues
    float queuePriority = 1.0f;
    std::vector<VkDeviceQueueCreateInfo> queueInfos;

    std::unordered_set<uint32_t> queueFamiliySet;

    DxvkAdapterQueueIndices queueFamilies = findQueueFamilies();
    queueFamiliySet.insert(queueFamilies.graphics);
    queueFamiliySet.insert(queueFamilies.transfer);
    this->logQueueFamilies(queueFamilies);

    for (uint32_t family : queueFamiliySet) {
      VkDeviceQueueCreateInfo graphicsQueue;
      graphicsQueue.sType             = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
      graphicsQueue.pNext             = nullptr;
      graphicsQueue.flags             = 0;
      graphicsQueue.queueFamilyIndex  = family;
      graphicsQueue.queueCount        = 1;
      graphicsQueue.pQueuePriorities  = &queuePriority;
      queueInfos.push_back(graphicsQueue);
    }

    VkDeviceCreateInfo info;
    info.sType                      = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
    info.pNext                      = enabledFeatures.core.pNext;
    info.flags                      = 0;
    info.queueCreateInfoCount       = queueInfos.size();
    info.pQueueCreateInfos          = queueInfos.data();
    info.enabledLayerCount          = 0;
    info.ppEnabledLayerNames        = nullptr;
    info.enabledExtensionCount      = extensionNameList.count();
    info.ppEnabledExtensionNames    = extensionNameList.names();
    info.pEnabledFeatures           = &enabledFeatures.core.features;

    if (devExtensions.amdMemoryOverallocationBehaviour)
      overallocInfo.pNext = std::exchange(info.pNext, &overallocInfo);

    VkDevice device = VK_NULL_HANDLE;
    VkResult vr = m_vki->vkCreateDevice(m_handle, &info, nullptr, &device);

    if (vr != VK_SUCCESS && enableCudaInterop) {
      // Enabling certain Vulkan extensions can cause device creation to fail on
      // Nvidia drivers if a certain kernel module isn't loaded, but we cannot know
      // that in advance since the extensions are reported as supported anyway.
      Logger::err("DxvkAdapter: Failed to create device, retrying without CUDA interop extensions");

      extensionsEnabled.disableExtension(devExtensions.khrBufferDeviceAddress);
      extensionsEnabled.disableExtension(devExtensions.nvxBinaryImport);
      extensionsEnabled.disableExtension(devExtensions.nvxImageViewHandle);

      enabledFeatures.khrBufferDeviceAddress.bufferDeviceAddress = VK_FALSE;

      vk::removeStructFromPNextChain(&enabledFeatures.core.pNext,
        VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_KHR);

      extensionNameList = extensionsEnabled.toNameList();
      info.enabledExtensionCount      = extensionNameList.count();
      info.ppEnabledExtensionNames    = extensionNameList.names();

      vr = m_vki->vkCreateDevice(m_handle, &info, nullptr, &device);
    }

    if (vr != VK_SUCCESS)
      throw DxvkError("DxvkAdapter: Failed to create device");

    Rc<DxvkDevice> result = new DxvkDevice(instance, this,
      new vk::DeviceFn(true, m_vki->instance(), device),
      devExtensions, enabledFeatures);
    result->initResources();
    return result;
  }

  void DxvkAdapter::notifyHeapMemoryAlloc(
          uint32_t            heap,
          VkDeviceSize        bytes) {
    if (!m_hasMemoryBudget)
      m_heapAlloc[heap] += bytes;
  }

  void DxvkAdapter::notifyHeapMemoryFree(
          uint32_t            heap,
          VkDeviceSize        bytes) {
    if (!m_hasMemoryBudget)
      m_heapAlloc[heap] -= bytes;
  }

  bool DxvkAdapter::matchesDriver(
          DxvkGpuVendor       vendor,
          VkDriverIdKHR       driver,
          uint32_t            minVer,
          uint32_t            maxVer) const {
    bool driverMatches = m_deviceInfo.khrDeviceDriverProperties.driverID
      ? driver == m_deviceInfo.khrDeviceDriverProperties.driverID
      : vendor == DxvkGpuVendor(m_deviceInfo.core.properties.vendorID);

    if (minVer) driverMatches &= m_deviceInfo.core.properties.driverVersion >= minVer;
    if (maxVer) driverMatches &= m_deviceInfo.core.properties.driverVersion <  maxVer;

    return driverMatches;
  }

  void DxvkAdapter::logAdapterInfo() const {
    VkPhysicalDeviceProperties deviceInfo = this->deviceProperties();
    VkPhysicalDeviceMemoryProperties memoryInfo = this->memoryProperties();

    Logger::info(str::format(deviceInfo.deviceName, ":"));
    Logger::info(str::format("  Driver: ",
      VK_VERSION_MAJOR(deviceInfo.driverVersion), ".",
      VK_VERSION_MINOR(deviceInfo.driverVersion), ".",
      VK_VERSION_PATCH(deviceInfo.driverVersion)));
    Logger::info(str::format("  Vulkan: ",
      VK_VERSION_MAJOR(deviceInfo.apiVersion), ".",
      VK_VERSION_MINOR(deviceInfo.apiVersion), ".",
      VK_VERSION_PATCH(deviceInfo.apiVersion)));

    for (uint32_t i = 0; i < memoryInfo.memoryHeapCount; i++) {
      constexpr VkDeviceSize mib = 1024 * 1024;

      Logger::info(str::format("  Memory Heap[", i, "]: "));
      Logger::info(str::format("    Size: ", memoryInfo.memoryHeaps[i].size / mib, " MiB"));
      Logger::info(str::format("    Flags: ", "0x", std::hex, memoryInfo.memoryHeaps[i].flags));

      for (uint32_t j = 0; j < memoryInfo.memoryTypeCount; j++) {
        if (memoryInfo.memoryTypes[j].heapIndex == i) {
          Logger::info(str::format(
            "    Memory Type[", j, "]: ",
            "Property Flags = ", "0x", std::hex, memoryInfo.memoryTypes[j].propertyFlags));
        }
      }
    }
  }

  bool DxvkAdapter::isUnifiedMemoryArchitecture() const {
    auto memory = this->memoryProperties();
    bool result = true;

    for (uint32_t i = 0; i < memory.memoryHeapCount; i++)
      result &= memory.memoryHeaps[i].flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT;

    return result;
  }

  void DxvkAdapter::initHeapAllocInfo() {
    for (uint32_t i = 0; i < m_heapAlloc.size(); i++)
      m_heapAlloc[i] = 0;
  }

  void DxvkAdapter::queryExtensions() {
    m_deviceExtensions = DxvkNameSet::enumDeviceExtensions(m_vki, m_handle);
  }

  void DxvkAdapter::queryDeviceInfo() {
    m_deviceInfo = DxvkDeviceInfo();
    m_deviceInfo.core.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
    m_deviceInfo.core.pNext = nullptr;

    // Query info now so that we have basic device properties available
    m_vki->vkGetPhysicalDeviceProperties2(m_handle, &m_deviceInfo.core);

    m_deviceInfo.coreDeviceId.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES;
    m_deviceInfo.coreDeviceId.pNext = std::exchange(m_deviceInfo.core.pNext, &m_deviceInfo.coreDeviceId);

    m_deviceInfo.coreSubgroup.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES;
    m_deviceInfo.coreSubgroup.pNext = std::exchange(m_deviceInfo.core.pNext, &m_deviceInfo.coreSubgroup);

    if (m_deviceExtensions.supports(VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME)) {
      m_deviceInfo.extConservativeRasterization.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CONSERVATIVE_RASTERIZATION_PROPERTIES_EXT;
      m_deviceInfo.extConservativeRasterization.pNext = std::exchange(m_deviceInfo.core.pNext, &m_deviceInfo.extConservativeRasterization);
    }

    if (m_deviceExtensions.supports(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME)) {
      m_deviceInfo.extCustomBorderColor.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_PROPERTIES_EXT;
      m_deviceInfo.extCustomBorderColor.pNext = std::exchange(m_deviceInfo.core.pNext, &m_deviceInfo.extCustomBorderColor);
    }

    if (m_deviceExtensions.supports(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME)) {
      m_deviceInfo.extRobustness2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_PROPERTIES_EXT;
      m_deviceInfo.extRobustness2.pNext = std::exchange(m_deviceInfo.core.pNext, &m_deviceInfo.extRobustness2);
    }

    if (m_deviceExtensions.supports(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME)) {
      m_deviceInfo.extTransformFeedback.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_PROPERTIES_EXT;
      m_deviceInfo.extTransformFeedback.pNext = std::exchange(m_deviceInfo.core.pNext, &m_deviceInfo.extTransformFeedback);
    }

    if (m_deviceExtensions.supports(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME)) {
      m_deviceInfo.extVertexAttributeDivisor.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT;
      m_deviceInfo.extVertexAttributeDivisor.pNext = std::exchange(m_deviceInfo.core.pNext, &m_deviceInfo.extVertexAttributeDivisor);
    }

    if (m_deviceExtensions.supports(VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME)) {
      m_deviceInfo.khrDepthStencilResolve.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_STENCIL_RESOLVE_PROPERTIES_KHR;
      m_deviceInfo.khrDepthStencilResolve.pNext = std::exchange(m_deviceInfo.core.pNext, &m_deviceInfo.khrDepthStencilResolve);
    }

    if (m_deviceExtensions.supports(VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME)) {
      m_deviceInfo.khrDeviceDriverProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR;
      m_deviceInfo.khrDeviceDriverProperties.pNext = std::exchange(m_deviceInfo.core.pNext, &m_deviceInfo.khrDeviceDriverProperties);
    }

    if (m_deviceExtensions.supports(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME)) {
      m_deviceInfo.khrShaderFloatControls.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT_CONTROLS_PROPERTIES_KHR;
      m_deviceInfo.khrShaderFloatControls.pNext = std::exchange(m_deviceInfo.core.pNext, &m_deviceInfo.khrShaderFloatControls);
    }

    if (m_deviceExtensions.supports(VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME)) {
      m_deviceInfo.khrTimelineSemaphore.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_PROPERTIES_KHR;
      m_deviceInfo.khrTimelineSemaphore.pNext = std::exchange(m_deviceInfo.core.pNext, &m_deviceInfo.khrTimelineSemaphore);
    }

    // Query full device properties for all enabled extensions
    m_vki->vkGetPhysicalDeviceProperties2(m_handle, &m_deviceInfo.core);

    // Some drivers reports the driver version in a slightly different format
    switch (m_deviceInfo.khrDeviceDriverProperties.driverID) {
      case VK_DRIVER_ID_NVIDIA_PROPRIETARY:
        m_deviceInfo.core.properties.driverVersion = VK_MAKE_VERSION(
          (m_deviceInfo.core.properties.driverVersion >> 22) & 0x3ff,
          (m_deviceInfo.core.properties.driverVersion >> 14) & 0x0ff,
          (m_deviceInfo.core.properties.driverVersion >>  6) & 0x0ff);
        break;

      case VK_DRIVER_ID_INTEL_PROPRIETARY_WINDOWS:
        m_deviceInfo.core.properties.driverVersion = VK_MAKE_VERSION(
          m_deviceInfo.core.properties.driverVersion >> 14,
          m_deviceInfo.core.properties.driverVersion & 0x3fff, 0);
        break;

      default:;
    }
  }

  void DxvkAdapter::queryDeviceFeatures() {
    m_deviceFeatures = DxvkDeviceFeatures();
    m_deviceFeatures.core.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
    m_deviceFeatures.core.pNext = nullptr;

    m_deviceFeatures.shaderDrawParameters.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DRAW_PARAMETERS_FEATURES;
    m_deviceFeatures.shaderDrawParameters.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.shaderDrawParameters);

    if (m_deviceExtensions.supports(VK_EXT_4444_FORMATS_EXTENSION_NAME)) {
      m_deviceFeatures.ext4444Formats.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_4444_FORMATS_FEATURES_EXT;
      m_deviceFeatures.ext4444Formats.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.ext4444Formats);
    }

    if (m_deviceExtensions.supports(VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME)) {
      m_deviceFeatures.extCustomBorderColor.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_CUSTOM_BORDER_COLOR_FEATURES_EXT;
      m_deviceFeatures.extCustomBorderColor.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.extCustomBorderColor);
    }

    if (m_deviceExtensions.supports(VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME)) {
      m_deviceFeatures.extDepthClipEnable.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT;
      m_deviceFeatures.extDepthClipEnable.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.extDepthClipEnable);
    }

    if (m_deviceExtensions.supports(VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME)) {
      m_deviceFeatures.extExtendedDynamicState.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTENDED_DYNAMIC_STATE_FEATURES_EXT;
      m_deviceFeatures.extExtendedDynamicState.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.extExtendedDynamicState);
    }

    if (m_deviceExtensions.supports(VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME)) {
      m_deviceFeatures.extHostQueryReset.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT;
      m_deviceFeatures.extHostQueryReset.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.extHostQueryReset);
    }

    if (m_deviceExtensions.supports(VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME)) {
      m_deviceFeatures.extMemoryPriority.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PRIORITY_FEATURES_EXT;
      m_deviceFeatures.extMemoryPriority.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.extMemoryPriority);
    }

    if (m_deviceExtensions.supports(VK_EXT_NON_SEAMLESS_CUBE_MAP_EXTENSION_NAME)) {
      m_deviceFeatures.extNonSeamlessCubeMap.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_NON_SEAMLESS_CUBE_MAP_FEATURES_EXT;
      m_deviceFeatures.extNonSeamlessCubeMap.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.extNonSeamlessCubeMap);
    }

    if (m_deviceExtensions.supports(VK_EXT_ROBUSTNESS_2_EXTENSION_NAME)) {
      m_deviceFeatures.extRobustness2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT;
      m_deviceFeatures.extRobustness2.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.extRobustness2);
    }

    if (m_deviceExtensions.supports(VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME)) {
      m_deviceFeatures.extShaderDemoteToHelperInvocation.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_DEMOTE_TO_HELPER_INVOCATION_FEATURES_EXT;
      m_deviceFeatures.extShaderDemoteToHelperInvocation.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.extShaderDemoteToHelperInvocation);
    }

    if (m_deviceExtensions.supports(VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME)) {
      m_deviceFeatures.extTransformFeedback.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TRANSFORM_FEEDBACK_FEATURES_EXT;
      m_deviceFeatures.extTransformFeedback.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.extTransformFeedback);
    }

    if (m_deviceExtensions.supports(VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME) >= 3) {
      m_deviceFeatures.extVertexAttributeDivisor.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT;
      m_deviceFeatures.extVertexAttributeDivisor.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.extVertexAttributeDivisor);
    }

    if (m_deviceExtensions.supports(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME)) {
      m_deviceFeatures.khrBufferDeviceAddress.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_KHR;
      m_deviceFeatures.khrBufferDeviceAddress.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.khrBufferDeviceAddress);
    }

    if (m_deviceExtensions.supports(VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME)) {
      m_deviceFeatures.khrTimelineSemaphore.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR;
      m_deviceFeatures.khrTimelineSemaphore.pNext = std::exchange(m_deviceFeatures.core.pNext, &m_deviceFeatures.khrTimelineSemaphore);
    }

    m_vki->vkGetPhysicalDeviceFeatures2(m_handle, &m_deviceFeatures.core);
  }

  void DxvkAdapter::queryDeviceQueues() {
    uint32_t numQueueFamilies = 0;
    m_vki->vkGetPhysicalDeviceQueueFamilyProperties(
      m_handle, &numQueueFamilies, nullptr);

    m_queueFamilies.resize(numQueueFamilies);
    m_vki->vkGetPhysicalDeviceQueueFamilyProperties(
      m_handle, &numQueueFamilies, m_queueFamilies.data());
  }

  uint32_t DxvkAdapter::findQueueFamily(
          VkQueueFlags          mask,
          VkQueueFlags          flags) const {
    for (uint32_t i = 0; i < m_queueFamilies.size(); i++) {
      if ((m_queueFamilies[i].queueFlags & mask) == flags)
        return i;
    }

    return VK_QUEUE_FAMILY_IGNORED;
  }

  void DxvkAdapter::logNameList(const DxvkNameList& names) {
    for (uint32_t i = 0; i < names.count(); i++)
      Logger::info(str::format("  ", names.name(i)));
  }

  void DxvkAdapter::logFeatures(const DxvkDeviceFeatures& features) {
    Logger::info(str::format("Device features:",
      "\n  robustBufferAccess                     : ", features.core.features.robustBufferAccess ? "1" : "0",
      "\n  fullDrawIndexUint32                    : ", features.core.features.fullDrawIndexUint32 ? "1" : "0",
      "\n  imageCubeArray                         : ", features.core.features.imageCubeArray ? "1" : "0",
      "\n  independentBlend                       : ", features.core.features.independentBlend ? "1" : "0",
      "\n  geometryShader                         : ", features.core.features.geometryShader ? "1" : "0",
      "\n  tessellationShader                     : ", features.core.features.tessellationShader ? "1" : "0",
      "\n  sampleRateShading                      : ", features.core.features.sampleRateShading ? "1" : "0",
      "\n  dualSrcBlend                           : ", features.core.features.dualSrcBlend ? "1" : "0",
      "\n  logicOp                                : ", features.core.features.logicOp ? "1" : "0",
      "\n  multiDrawIndirect                      : ", features.core.features.multiDrawIndirect ? "1" : "0",
      "\n  drawIndirectFirstInstance              : ", features.core.features.drawIndirectFirstInstance ? "1" : "0",
      "\n  depthClamp                             : ", features.core.features.depthClamp ? "1" : "0",
      "\n  depthBiasClamp                         : ", features.core.features.depthBiasClamp ? "1" : "0",
      "\n  fillModeNonSolid                       : ", features.core.features.fillModeNonSolid ? "1" : "0",
      "\n  depthBounds                            : ", features.core.features.depthBounds ? "1" : "0",
      "\n  multiViewport                          : ", features.core.features.multiViewport ? "1" : "0",
      "\n  samplerAnisotropy                      : ", features.core.features.samplerAnisotropy ? "1" : "0",
      "\n  textureCompressionBC                   : ", features.core.features.textureCompressionBC ? "1" : "0",
      "\n  occlusionQueryPrecise                  : ", features.core.features.occlusionQueryPrecise ? "1" : "0",
      "\n  pipelineStatisticsQuery                : ", features.core.features.pipelineStatisticsQuery ? "1" : "0",
      "\n  vertexPipelineStoresAndAtomics         : ", features.core.features.vertexPipelineStoresAndAtomics ? "1" : "0",
      "\n  fragmentStoresAndAtomics               : ", features.core.features.fragmentStoresAndAtomics ? "1" : "0",
      "\n  shaderImageGatherExtended              : ", features.core.features.shaderImageGatherExtended ? "1" : "0",
      "\n  shaderStorageImageExtendedFormats      : ", features.core.features.shaderStorageImageExtendedFormats ? "1" : "0",
      "\n  shaderStorageImageReadWithoutFormat    : ", features.core.features.shaderStorageImageReadWithoutFormat ? "1" : "0",
      "\n  shaderStorageImageWriteWithoutFormat   : ", features.core.features.shaderStorageImageWriteWithoutFormat ? "1" : "0",
      "\n  shaderClipDistance                     : ", features.core.features.shaderClipDistance ? "1" : "0",
      "\n  shaderCullDistance                     : ", features.core.features.shaderCullDistance ? "1" : "0",
      "\n  shaderFloat64                          : ", features.core.features.shaderFloat64 ? "1" : "0",
      "\n  shaderInt64                            : ", features.core.features.shaderInt64 ? "1" : "0",
      "\n  variableMultisampleRate                : ", features.core.features.variableMultisampleRate ? "1" : "0",
      "\n", VK_EXT_4444_FORMATS_EXTENSION_NAME,
      "\n  formatA4R4G4B4                         : ", features.ext4444Formats.formatA4R4G4B4 ? "1" : "0",
      "\n  formatA4B4G4R4                         : ", features.ext4444Formats.formatA4B4G4R4 ? "1" : "0",
      "\n", VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME,
      "\n  customBorderColors                     : ", features.extCustomBorderColor.customBorderColors ? "1" : "0",
      "\n  customBorderColorWithoutFormat         : ", features.extCustomBorderColor.customBorderColorWithoutFormat ? "1" : "0",
      "\n", VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME,
      "\n  depthClipEnable                        : ", features.extDepthClipEnable.depthClipEnable ? "1" : "0",
      "\n", VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME,
      "\n  extendedDynamicState                   : ", features.extExtendedDynamicState.extendedDynamicState ? "1" : "0",
      "\n", VK_EXT_HOST_QUERY_RESET_EXTENSION_NAME,
      "\n  hostQueryReset                         : ", features.extHostQueryReset.hostQueryReset ? "1" : "0",
      "\n", VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME,
      "\n  memoryPriority                         : ", features.extMemoryPriority.memoryPriority ? "1" : "0",
      "\n", VK_EXT_NON_SEAMLESS_CUBE_MAP_EXTENSION_NAME,
      "\n  nonSeamlessCubeMap                     : ", features.extNonSeamlessCubeMap.nonSeamlessCubeMap ? "1" : "0",
      "\n", VK_EXT_ROBUSTNESS_2_EXTENSION_NAME,
      "\n  robustBufferAccess2                    : ", features.extRobustness2.robustBufferAccess2 ? "1" : "0",
      "\n  robustImageAccess2                     : ", features.extRobustness2.robustImageAccess2 ? "1" : "0",
      "\n  nullDescriptor                         : ", features.extRobustness2.nullDescriptor ? "1" : "0",
      "\n", VK_EXT_SHADER_DEMOTE_TO_HELPER_INVOCATION_EXTENSION_NAME,
      "\n  shaderDemoteToHelperInvocation         : ", features.extShaderDemoteToHelperInvocation.shaderDemoteToHelperInvocation ? "1" : "0",
      "\n", VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME,
      "\n  transformFeedback                      : ", features.extTransformFeedback.transformFeedback ? "1" : "0",
      "\n  geometryStreams                        : ", features.extTransformFeedback.geometryStreams ? "1" : "0",
      "\n", VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME,
      "\n  vertexAttributeInstanceRateDivisor     : ", features.extVertexAttributeDivisor.vertexAttributeInstanceRateDivisor ? "1" : "0",
      "\n  vertexAttributeInstanceRateZeroDivisor : ", features.extVertexAttributeDivisor.vertexAttributeInstanceRateZeroDivisor ? "1" : "0",
      "\n", VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME,
      "\n  bufferDeviceAddress                    : ", features.khrBufferDeviceAddress.bufferDeviceAddress,
      "\n", VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME,
      "\n  timelineSemaphore                      : ", features.khrTimelineSemaphore.timelineSemaphore));
  }

  void DxvkAdapter::logQueueFamilies(const DxvkAdapterQueueIndices& queues) {
    Logger::info(str::format("Queue families:",
      "\n  Graphics : ", queues.graphics,
      "\n  Transfer : ", queues.transfer));
  }

}


code:
#pragma once

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

namespace dxvk {

  class DxvkDevice;
  class DxvkInstance;

  /**
   * \brief GPU vendors
   * Based on PCIe IDs.
   */
  enum class DxvkGpuVendor : uint16_t {
    Amd    = 0x1002,
    Nvidia = 0x10de,
    Intel  = 0x8086,
  };

  /**
   * \brief Adapter memory heap info
   *
   * Stores info about a heap, and the amount
   * of memory allocated from it by the app.
   */
  struct DxvkAdapterMemoryHeapInfo {
    VkMemoryHeapFlags heapFlags;
    VkDeviceSize memoryBudget;
    VkDeviceSize memoryAllocated;
  };

  /**
   * \brief Adapter memory info
   *
   * Stores properties and allocation
   * info of each available heap.
   */
  struct DxvkAdapterMemoryInfo {
    uint32_t                  heapCount;
    DxvkAdapterMemoryHeapInfo heaps[VK_MAX_MEMORY_HEAPS];
  };

  /**
   * \brief Retrieves queue indices
   */
  struct DxvkAdapterQueueIndices {
    uint32_t graphics;
    uint32_t transfer;
  };

  /**
   * \brief DXVK adapter
   *
   * Corresponds to a physical device in Vulkan. Provides
   * all kinds of information about the device itself and
   * the supported feature set.
   */
  class DxvkAdapter : public RcObject {

  public:

    DxvkAdapter(
      const Rc<vk::InstanceFn>& vki,
            VkPhysicalDevice    handle);
    ~DxvkAdapter();

    /**
     * \brief Vulkan instance functions
     * \returns Vulkan instance functions
     */
    Rc<vk::InstanceFn> vki() const {
      return m_vki;
    }

    /**
     * \brief Physical device handle
     * \returns The adapter handle
     */
    VkPhysicalDevice handle() const {
      return m_handle;
    }

    /**
     * \brief Physical device properties
     *
     * Returns a read-only reference to the core
     * properties of the Vulkan physical device.
     * \returns Physical device core properties
     */
    const VkPhysicalDeviceProperties& deviceProperties() const {
      return m_deviceInfo.core.properties;
    }

    /**
     * \brief Device info
     *
     * Returns a read-only reference to the full
     * device info structure, including extended
     * properties.
     * \returns Device info struct
     */
    const DxvkDeviceInfo& devicePropertiesExt() const {
      return m_deviceInfo;
    }

    /**
     * \brief Supportred device features
     *
     * Queries the supported device features.
     * \returns Device features
     */
    const DxvkDeviceFeatures& features() const {
      return m_deviceFeatures;
    }

    /**
     * \brief Retrieves memory heap info
     *
     * Returns properties of all available memory heaps,
     * both device-local and non-local heaps, and the
     * amount of memory allocated from those heaps by
     * logical devices.
     * \returns Memory heap info
     */
    DxvkAdapterMemoryInfo getMemoryHeapInfo() const;

    /**
     * \brief Memory properties
     *
     * Queries the memory types and memory heaps of
     * the device. This is useful for memory allocators.
     * \returns Device memory properties
     */
    VkPhysicalDeviceMemoryProperties memoryProperties() const;

    /**
     * \brief Queries format support
     *
     * \param [in] format The format to query
     * \returns Format support info
     */
    VkFormatProperties formatProperties(
      VkFormat format) const;

    /**
     * \brief Queries image format support
     *
     * \param [in] format Format to query
     * \param [in] type Image type
     * \param [in] tiling Image tiling
     * \param [in] usage Image usage flags
     * \param [in] flags Image create flags
     * \param [out] properties Format properties
     * \returns \c VK_SUCCESS or \c VK_ERROR_FORMAT_NOT_SUPPORTED
     */
    VkResult imageFormatProperties(
      VkFormat                  format,
      VkImageType               type,
      VkImageTiling             tiling,
      VkImageUsageFlags         usage,
      VkImageCreateFlags        flags,
      VkImageFormatProperties&  properties) const;

    /**
     * \brief Retrieves queue family indices
     * \returns Indices for all queue families
     */
    DxvkAdapterQueueIndices findQueueFamilies() const;

    /**
     * \brief Tests whether all required features are supported
     *
     * \param [in] features Required device features
     * \returns \c true if all features are supported
     */
    bool checkFeatureSupport(
      const DxvkDeviceFeatures& required) const;

    /**
     * \brief Enables extensions for this adapter
     *
     * When creating a device, all extensions that
     * are added using this method will be enabled
     * in addition to the ones required by DXVK.
     * This is used for OpenVR support.
     */
    void enableExtensions(
      const DxvkNameSet&        extensions);

    /**
     * \brief Creates a DXVK device
     *
     * Creates a logical device for this adapter.
     * \param [in] instance Parent instance
     * \param [in] enabledFeatures Device features
     * \returns Device handle
     */
    Rc<DxvkDevice> createDevice(
      const Rc<DxvkInstance>&   instance,
            DxvkDeviceFeatures  enabledFeatures);

    /**
     * \brief Registers memory allocation
     *
     * Updates memory alloc info accordingly.
     * \param [in] heap Memory heap index
     * \param [in] bytes Allocation size
     */
    void notifyHeapMemoryAlloc(
            uint32_t            heap,
            VkDeviceSize        bytes);

    /**
     * \brief Registers memory deallocation
     *
     * Updates memory alloc info accordingly.
     * \param [in] heap Memory heap index
     * \param [in] bytes Allocation size
     */
    void notifyHeapMemoryFree(
            uint32_t            heap,
            VkDeviceSize        bytes);

    /**
     * \brief Tests if the driver matches certain criteria
     *
     * \param [in] vendor GPU vendor
     * \param [in] driver Driver. Ignored when the
     *    driver properties extension is not supported.
     * \param [in] minVer Match versions starting with this one
     * \param [in] maxVer Match versions lower than this one
     * \returns \c True if the driver matches these criteria
     */
    bool matchesDriver(
            DxvkGpuVendor       vendor,
            VkDriverIdKHR       driver,
            uint32_t            minVer,
            uint32_t            maxVer) const;

    /**
     * \brief Logs DXVK adapter info
     *
     * May be useful for bug reports
     * and general troubleshooting.
     */
    void logAdapterInfo() const;

    /**
     * \brief Checks whether this is a UMA system
     *
     * Basically tests whether all heaps are device-local.
     * Can be used for various optimizations in client APIs.
     * \returns \c true if the system has unified memory.
     */
    bool isUnifiedMemoryArchitecture() const;

  private:

    Rc<vk::InstanceFn>  m_vki;
    VkPhysicalDevice    m_handle;

    DxvkNameSet         m_extraExtensions;
    DxvkNameSet         m_deviceExtensions;
    DxvkDeviceInfo      m_deviceInfo;
    DxvkDeviceFeatures  m_deviceFeatures;

    bool                m_hasMemoryBudget;

    std::vector<VkQueueFamilyProperties> m_queueFamilies;

    std::array<std::atomic<VkDeviceSize>, VK_MAX_MEMORY_HEAPS> m_heapAlloc;

    void initHeapAllocInfo();
    void queryExtensions();
    void queryDeviceInfo();
    void queryDeviceFeatures();
    void queryDeviceQueues();

    uint32_t findQueueFamily(
            VkQueueFlags          mask,
            VkQueueFlags          flags) const;

    static void logNameList(const DxvkNameList& names);
    static void logFeatures(const DxvkDeviceFeatures& features);
    static void logQueueFamilies(const DxvkAdapterQueueIndices& queues);

  };

}

repo_name: orochi/dxvk


code:
#pragma once

#include <d3d11_1.h>
#include <d3d9.h>

MIDL_INTERFACE("7f2c2f72-1cc8-4979-8d9c-7e3faeddecde")
IDXVKUserDefinedAnnotation : public ID3DUserDefinedAnnotation {

public:

  INT STDMETHODCALLTYPE BeginEvent(
          LPCWSTR                 Name) final {
    return this->BeginEvent(0, Name);
  }

  void STDMETHODCALLTYPE SetMarker(
          LPCWSTR                 Name) final {
    this->SetMarker(0, Name);
  }

  virtual INT STDMETHODCALLTYPE BeginEvent(
          D3DCOLOR                Color,
          LPCWSTR                 Name) = 0;

  virtual void STDMETHODCALLTYPE SetMarker(
          D3DCOLOR                Color,
          LPCWSTR                 Name) = 0;

};

#ifdef _MSC_VER
struct __declspec(uuid("7f2c2f72-1cc8-4979-8d9c-7e3faeddecde")) IDXVKUserDefinedAnnotation;
#else
__CRT_UUID_DECL(IDXVKUserDefinedAnnotation, 0x7f2c2f72,0x1cc8,0x4979,0x8d,0x9c,0x7e,0x3f,0xae,0xdd,0xec,0xde);
#endif


code:
#include "dxvk_barrier.h"

namespace dxvk {

  constexpr static VkAccessFlags AccessReadMask
    = VK_ACCESS_INDIRECT_COMMAND_READ_BIT
    | VK_ACCESS_INDEX_READ_BIT
    | VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT
    | VK_ACCESS_UNIFORM_READ_BIT
    | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT
    | VK_ACCESS_SHADER_READ_BIT
    | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
    | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
    | VK_ACCESS_TRANSFER_READ_BIT
    | VK_ACCESS_HOST_READ_BIT
    | VK_ACCESS_MEMORY_READ_BIT
    | VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_READ_BIT_EXT;

  constexpr static VkAccessFlags AccessWriteMask
    = VK_ACCESS_SHADER_WRITE_BIT
    | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT
    | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT
    | VK_ACCESS_TRANSFER_WRITE_BIT
    | VK_ACCESS_HOST_WRITE_BIT
    | VK_ACCESS_MEMORY_WRITE_BIT
    | VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT
    | VK_ACCESS_TRANSFORM_FEEDBACK_COUNTER_WRITE_BIT_EXT;

  DxvkBarrierSet:: DxvkBarrierSet(DxvkCmdBuffer cmdBuffer)
  : m_cmdBuffer(cmdBuffer) {

  }

  DxvkBarrierSet::~DxvkBarrierSet() {

  }

  void DxvkBarrierSet::accessMemory(
          VkPipelineStageFlags      srcStages,
          VkAccessFlags             srcAccess,
          VkPipelineStageFlags      dstStages,
          VkAccessFlags             dstAccess) {
    DxvkAccessFlags access = this->getAccessTypes(srcAccess);

    m_srcStages |= srcStages;
    m_dstStages |= dstStages;

    m_srcAccess |= srcAccess & AccessWriteMask;

    if (access.test(DxvkAccess::Write))
      m_dstAccess |= dstAccess;
  }

  void DxvkBarrierSet::accessBuffer(
    const DxvkBufferSliceHandle&    bufSlice,
          VkPipelineStageFlags      srcStages,
          VkAccessFlags             srcAccess,
          VkPipelineStageFlags      dstStages,
          VkAccessFlags             dstAccess) {
    DxvkAccessFlags access = this->getAccessTypes(srcAccess);

    if (srcStages == VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT
     || dstStages == VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT)
      access.set(DxvkAccess::Write);

    m_srcStages |= srcStages;
    m_dstStages |= dstStages;

    m_srcAccess |= srcAccess & AccessWriteMask;

    if (access.test(DxvkAccess::Write))
      m_dstAccess |= dstAccess;

    m_bufSlices.insert(bufSlice.handle,
      DxvkBarrierBufferSlice(bufSlice.offset, bufSlice.length, access));
  }

  void DxvkBarrierSet::accessImage(
    const Rc<DxvkImage>&            image,
    const VkImageSubresourceRange&  subresources,
          VkImageLayout             srcLayout,
          VkPipelineStageFlags      srcStages,
          VkAccessFlags             srcAccess,
          VkImageLayout             dstLayout,
          VkPipelineStageFlags      dstStages,
          VkAccessFlags             dstAccess) {
    DxvkAccessFlags access = this->getAccessTypes(srcAccess);

    if (srcStages == VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT
     || dstStages == VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT
     || srcLayout != dstLayout)
      access.set(DxvkAccess::Write);

    m_srcStages |= srcStages;
    m_dstStages |= dstStages;

    if (srcLayout == dstLayout) {
      m_srcAccess |= srcAccess & AccessWriteMask;

      if (access.test(DxvkAccess::Write))
        m_dstAccess |= dstAccess;
    } else {
      VkImageMemoryBarrier barrier;
      barrier.sType                       = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
      barrier.pNext                       = nullptr;
      barrier.srcAccessMask               = srcAccess & AccessWriteMask;
      barrier.dstAccessMask               = dstAccess;
      barrier.oldLayout                   = srcLayout;
      barrier.newLayout                   = dstLayout;
      barrier.srcQueueFamilyIndex         = VK_QUEUE_FAMILY_IGNORED;
      barrier.dstQueueFamilyIndex         = VK_QUEUE_FAMILY_IGNORED;
      barrier.image                       = image->handle();
      barrier.subresourceRange            = subresources;
      barrier.subresourceRange.aspectMask = image->formatInfo()->aspectMask;
      m_imgBarriers.push_back(barrier);
    }

    m_imgSlices.insert(image->handle(),
      DxvkBarrierImageSlice(subresources, access));
  }

  void DxvkBarrierSet::releaseBuffer(
          DxvkBarrierSet&           acquire,
    const DxvkBufferSliceHandle&    bufSlice,
          uint32_t                  srcQueue,
          VkPipelineStageFlags      srcStages,
          VkAccessFlags             srcAccess,
          uint32_t                  dstQueue,
          VkPipelineStageFlags      dstStages,
          VkAccessFlags             dstAccess) {
    auto& release = *this;

    release.m_srcStages |= srcStages;
    acquire.m_dstStages |= dstStages;

    VkBufferMemoryBarrier barrier;
    barrier.sType                       = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
    barrier.pNext                       = nullptr;
    barrier.srcAccessMask               = srcAccess & AccessWriteMask;
    barrier.dstAccessMask               = 0;
    barrier.srcQueueFamilyIndex         = srcQueue;
    barrier.dstQueueFamilyIndex         = dstQueue;
    barrier.buffer                      = bufSlice.handle;
    barrier.offset                      = bufSlice.offset;
    barrier.size                        = bufSlice.length;
    release.m_bufBarriers.push_back(barrier);

    barrier.srcAccessMask               = 0;
    barrier.dstAccessMask               = dstAccess;
    acquire.m_bufBarriers.push_back(barrier);

    DxvkAccessFlags access(DxvkAccess::Read, DxvkAccess::Write);
    release.m_bufSlices.insert(bufSlice.handle,
      DxvkBarrierBufferSlice(bufSlice.offset, bufSlice.length, access));
    acquire.m_bufSlices.insert(bufSlice.handle,
      DxvkBarrierBufferSlice(bufSlice.offset, bufSlice.length, access));
  }

  void DxvkBarrierSet::releaseImage(
          DxvkBarrierSet&           acquire,
    const Rc<DxvkImage>&            image,
    const VkImageSubresourceRange&  subresources,
          uint32_t                  srcQueue,
          VkImageLayout             srcLayout,
          VkPipelineStageFlags      srcStages,
          VkAccessFlags             srcAccess,
          uint32_t                  dstQueue,
          VkImageLayout             dstLayout,
          VkPipelineStageFlags      dstStages,
          VkAccessFlags             dstAccess) {
    auto& release = *this;

    release.m_srcStages |= srcStages;
    acquire.m_dstStages |= dstStages;

    VkImageMemoryBarrier barrier;
    barrier.sType                       = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
    barrier.pNext                       = nullptr;
    barrier.srcAccessMask               = srcAccess & AccessWriteMask;
    barrier.dstAccessMask               = 0;
    barrier.oldLayout                   = srcLayout;
    barrier.newLayout                   = dstLayout;
    barrier.srcQueueFamilyIndex         = srcQueue;
    barrier.dstQueueFamilyIndex         = dstQueue;
    barrier.image                       = image->handle();
    barrier.subresourceRange            = subresources;
    barrier.subresourceRange.aspectMask = image->formatInfo()->aspectMask;
    release.m_imgBarriers.push_back(barrier);

    if (srcQueue == dstQueue)
      barrier.oldLayout = dstLayout;

    barrier.srcAccessMask               = 0;
    barrier.dstAccessMask               = dstAccess;
    acquire.m_imgBarriers.push_back(barrier);

    DxvkAccessFlags access(DxvkAccess::Read, DxvkAccess::Write);
    release.m_imgSlices.insert(image->handle(),
      DxvkBarrierImageSlice(subresources, access));
    acquire.m_imgSlices.insert(image->handle(),
      DxvkBarrierImageSlice(subresources, access));
  }

  bool DxvkBarrierSet::isBufferDirty(
    const DxvkBufferSliceHandle&    bufSlice,
          DxvkAccessFlags           bufAccess) {
    return m_bufSlices.isDirty(bufSlice.handle,
      DxvkBarrierBufferSlice(bufSlice.offset, bufSlice.length, bufAccess));
  }

  bool DxvkBarrierSet::isImageDirty(
    const Rc<DxvkImage>&            image,
    const VkImageSubresourceRange&  imgSubres,
          DxvkAccessFlags           imgAccess) {
    return m_imgSlices.isDirty(image->handle(),
      DxvkBarrierImageSlice(imgSubres, imgAccess));
  }

  DxvkAccessFlags DxvkBarrierSet::getBufferAccess(
    const DxvkBufferSliceHandle&    bufSlice) {
    return m_bufSlices.getAccess(bufSlice.handle,
      DxvkBarrierBufferSlice(bufSlice.offset, bufSlice.length, 0));
  }

  DxvkAccessFlags DxvkBarrierSet::getImageAccess(
    const Rc<DxvkImage>&            image,
    const VkImageSubresourceRange&  imgSubres) {
    return m_imgSlices.getAccess(image->handle(),
      DxvkBarrierImageSlice(imgSubres, 0));
  }

  void DxvkBarrierSet::recordCommands(const Rc<DxvkCommandList>& commandList) {
    if (m_srcStages | m_dstStages) {
      VkPipelineStageFlags srcFlags = m_srcStages;
      VkPipelineStageFlags dstFlags = m_dstStages;

      if (!srcFlags) srcFlags = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
      if (!dstFlags) dstFlags = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;

      VkMemoryBarrier memBarrier;
      memBarrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
      memBarrier.pNext = nullptr;
      memBarrier.srcAccessMask = m_srcAccess;
      memBarrier.dstAccessMask = m_dstAccess;

      VkMemoryBarrier* pMemBarrier = nullptr;
      if (m_srcAccess | m_dstAccess)
        pMemBarrier = &memBarrier;

      commandList->cmdPipelineBarrier(
        m_cmdBuffer, srcFlags, dstFlags, 0,
        pMemBarrier ? 1 : 0, pMemBarrier,
        m_bufBarriers.size(),
        m_bufBarriers.data(),
        m_imgBarriers.size(),
        m_imgBarriers.data());

      commandList->addStatCtr(DxvkStatCounter::CmdBarrierCount, 1);

      this->reset();
    }
  }

  void DxvkBarrierSet::reset() {
    m_srcStages = 0;
    m_dstStages = 0;

    m_srcAccess = 0;
    m_dstAccess = 0;

    m_bufBarriers.resize(0);
    m_imgBarriers.resize(0);

    m_bufSlices.clear();
    m_imgSlices.clear();
  }

  DxvkAccessFlags DxvkBarrierSet::getAccessTypes(VkAccessFlags flags) {
    DxvkAccessFlags result;
    if (flags & AccessReadMask)  result.set(DxvkAccess::Read);
    if (flags & AccessWriteMask) result.set(DxvkAccess::Write);
    return result;
  }

}


code:
#pragma once

#include <utility>
#include <vector>

#include "dxvk_buffer.h"
#include "dxvk_cmdlist.h"
#include "dxvk_image.h"

namespace dxvk {

  /**
   * \brief Buffer slice for barrier tracking
   *
   * Stores the offset and length of a buffer slice,
   * as well as access flags for the given range.
   */
  class DxvkBarrierBufferSlice {

  public:

    DxvkBarrierBufferSlice()
    : m_offset(0), m_length(0), m_access(0) { }

    DxvkBarrierBufferSlice(VkDeviceSize offset, VkDeviceSize length, DxvkAccessFlags access)
    : m_offset(offset), m_length(length), m_access(access) { }

    /**
     * \brief Checks whether two slices overlap
     *
     * \param [in] slice The other buffer slice to check
     * \returns \c true if the two slices overlap
     */
    bool overlaps(const DxvkBarrierBufferSlice& slice) const {
      return m_offset +       m_length > slice.m_offset
          && m_offset < slice.m_offset + slice.m_length;
    }

    /**
     * \brief Checks whether a given slice is dirty
     *
     * \param [in] slice The buffer slice to check
     * \returns \c true if the two slices overlap, and if
     *    at least one of the two slices have write access.
     */
    bool isDirty(const DxvkBarrierBufferSlice& slice) const {
      return (slice.m_access | m_access).test(DxvkAccess::Write) && overlaps(slice);
    }

    /**
     * \brief Checks whether two slices can be merged
     *
     * Two buffer slices can be merged if they overlap or are adjacent
     * and if the access flags are the same, or alternatively, if the
     * offset and size are the same and only the access flags differ.
     * \param [in] slice The other buffer slice to check
     * \returns \c true if the slices can be merged.
     */
    bool canMerge(const DxvkBarrierBufferSlice& slice) const {
      if (m_access == slice.m_access) {
        return m_offset +        m_length >= slice.m_offset
            && m_offset <= slice.m_offset +  slice.m_length;
      } else {
        return m_offset == slice.m_offset
            && m_length == slice.m_length;
      }
    }

    /**
     * \brief Merges two buffer slices
     *
     * The resulting slice is guaranteed to fully contain both slices,
     * including their access flags. If called when \c canMerge would
     * return \c false, this will be a strict superset of both slices.
     * \param [in] slice The slice to merge
     */
    void merge(const DxvkBarrierBufferSlice& slice) {
      VkDeviceSize end = std::max(m_offset + m_length, slice.m_offset + slice.m_length);

      m_offset = std::min(m_offset, slice.m_offset);
      m_length = end - m_offset;
      m_access.set(slice.m_access);
    }

    /**
     * \brief Queries access flags
     * \returns Access flags
     */
    DxvkAccessFlags getAccess() const {
      return m_access;
    }

  private:

    VkDeviceSize    m_offset;
    VkDeviceSize    m_length;
    DxvkAccessFlags m_access;

  };

  /**
   * \brief Image slice for barrier tracking
   *
   * Stores an image subresource range, as well as
   * access flags for the given image subresources.
   */
  class DxvkBarrierImageSlice {

  public:

    DxvkBarrierImageSlice()
    : m_range(VkImageSubresourceRange()), m_access(0) { }

    DxvkBarrierImageSlice(VkImageSubresourceRange range, DxvkAccessFlags access)
    : m_range(range), m_access(access) { }

    /**
     * \brief Checks whether two slices overlap
     *
     * \param [in] slice The other image slice to check
     * \returns \c true if the two slices overlap
     */
    bool overlaps(const DxvkBarrierImageSlice& slice) const {
      return (m_range.aspectMask     & slice.m_range.aspectMask)
          && (m_range.baseArrayLayer < slice.m_range.baseArrayLayer + slice.m_range.layerCount)
          && (m_range.baseArrayLayer +       m_range.layerCount     > slice.m_range.baseArrayLayer)
          && (m_range.baseMipLevel   < slice.m_range.baseMipLevel   + slice.m_range.levelCount)
          && (m_range.baseMipLevel   +       m_range.levelCount     > slice.m_range.baseMipLevel);
    }

    /**
     * \brief Checks whether a given slice is dirty
     *
     * \param [in] slice The image slice to check
     * \returns \c true if the two slices overlap, and if
     *    at least one of the two slices have write access.
     */
    bool isDirty(const DxvkBarrierImageSlice& slice) const {
      return (slice.m_access | m_access).test(DxvkAccess::Write) && overlaps(slice);
    }

    /**
     * \brief Checks whether two slices can be merged
     *
     * This is a simplified implementation that does not check for
     * adjacent or overlapping layers or levels, and instead only
     * returns \c true if both slices contain the same mip levels
     * and array layers. Access flags and image aspects may differ.
     * \param [in] slice The other image slice to check
     * \returns \c true if the slices can be merged.
     */
    bool canMerge(const DxvkBarrierImageSlice& slice) const {
      return m_range.baseMipLevel   == slice.m_range.baseMipLevel
          && m_range.levelCount     == slice.m_range.levelCount
          && m_range.baseArrayLayer == slice.m_range.baseArrayLayer
          && m_range.layerCount     == slice.m_range.layerCount;
    }

    /**
     * \brief Merges two image slices
     *
     * The resulting slice is guaranteed to fully contain both slices,
     * including their access flags. If called when \c canMerge would
     * return \c false, this will be a strict superset of both slices.
     * \param [in] slice The slice to merge
     */
    void merge(const DxvkBarrierImageSlice& slice) {
      uint32_t maxMipLevel = std::max(m_range.baseMipLevel + m_range.levelCount,
        slice.m_range.baseMipLevel + slice.m_range.levelCount);
      uint32_t maxArrayLayer = std::max(m_range.baseArrayLayer + m_range.layerCount,
        slice.m_range.baseArrayLayer + slice.m_range.layerCount);
      m_range.aspectMask     |= slice.m_range.aspectMask;
      m_range.baseMipLevel    = std::min(m_range.baseMipLevel, slice.m_range.baseMipLevel);
      m_range.levelCount      = maxMipLevel - m_range.baseMipLevel;
      m_range.baseArrayLayer  = std::min(m_range.baseMipLevel, slice.m_range.baseArrayLayer);
      m_range.layerCount      = maxArrayLayer - m_range.baseArrayLayer;
      m_access.set(slice.m_access);
    }

    /**
     * \brief Queries access flags
     * \returns Access flags
     */
    DxvkAccessFlags getAccess() const {
      return m_access;
    }

  private:

    VkImageSubresourceRange m_range;
    DxvkAccessFlags         m_access;

  };

  /**
   * \brief Resource slice set for barrier tracking
   *
   * Implements a versioned hash table for fast resource
   * lookup, with a single-linked list accurately storing
   * each accessed slice if necessary.
   * \tparam K Resource handle type
   * \tparam T Resource slice type
   */
  template<typename K, typename T>
  class DxvkBarrierSubresourceSet {
    constexpr static uint32_t NoEntry = ~0u;
  public:

    /**
     * \brief Queries access flags of a given resource slice
     *
     * \param [in] resource Resource handle
     * \param [in] slice Resource slice
     * \returns Or'd access flags of all known slices
     *    that overlap with the given slice.
     */
    DxvkAccessFlags getAccess(K resource, const T& slice) {
      HashEntry* entry = findHashEntry(resource);

      if (!entry)
        return DxvkAccessFlags();

      // Exit early if we know that there are no overlapping
      // slices, or if there is only one slice to check anyway.
      if (!entry->data.overlaps(slice))
        return DxvkAccessFlags();

      ListEntry* list = getListEntry(entry->next);

      if (!list)
        return entry->data.getAccess();

      // The early out condition just checks whether there are
      // any access flags left that may potentially get added
      DxvkAccessFlags access;

      while (list && access != entry->data.getAccess()) {
        if (list->data.overlaps(slice))
          access.set(list->data.getAccess());

        list = getListEntry(list->next);
      }

      return access;
    }

    /**
     * \brief Checks whether a given resource slice is dirty
     *
     * \param [in] resource Resourece handle
     * \param [in] slice Resource slice
     * \returns \c true if there is at least one slice that
     *    overlaps with the given slice, and either slice has
     *    the \c DxvkAccess::Write flag set.
     */
    bool isDirty(K resource, const T& slice) {
      HashEntry* entry = findHashEntry(resource);

      if (!entry)
        return false;

      // Exit early if there are no overlapping slices, or
      // if none of the slices have the write flag set.
      if (!entry->data.isDirty(slice))
        return false;

      // We know that some subresources are dirty, so if
      // there is no list, the given slice must be dirty.
      ListEntry* list = getListEntry(entry->next);

      if (!list)
        return true;

      // Exit earlier if we find one dirty slice
      bool dirty = false;

      while (list && !dirty) {
        dirty = list->data.isDirty(slice);
        list = getListEntry(list->next);
      }

      return dirty;
    }

    /**
     * \brief Inserts a given resource slice
     *
     * This will attempt to deduplicate and merge entries if
     * possible, so that lookup and further insertions remain
     * reasonably fast.
     * \param [in] resource Resource handle
     * \param [in] slice Resource slice
     */
    void insert(K resource, const T& slice) {
      HashEntry* hashEntry = insertHashEntry(resource, slice);

      if (hashEntry) {
        ListEntry* listEntry = getListEntry(hashEntry->next);

        // Only create the linear list if absolutely necessary
        if (!listEntry && !hashEntry->data.canMerge(slice))
          listEntry = insertListEntry(hashEntry->data, hashEntry);

        if (listEntry) {
          while (listEntry) {
            // Avoid adding new list entries if possible
            if (listEntry->data.canMerge(slice)) {
              listEntry->data.merge(slice);
              break;
            }

            listEntry = getListEntry(listEntry->next);
          }

          if (!listEntry)
            insertListEntry(slice, hashEntry);
        }

        // Merge hash entry data so that it stores
        // a superset of all slices in the list.
        hashEntry->data.merge(slice);
      }
    }

    /**
     * \brief Removes all resources from the set
     */
    void clear() {
      m_used = 0;
      m_version += 1;
      m_list.clear();
    }

  private:

    struct ListEntry {
      T         data;
      uint32_t  next;
    };

    struct HashEntry {
      uint64_t  version;
      K         key;
      T         data;
      uint32_t  next;
    };

    uint64_t m_version = 1ull;
    uint64_t m_used    = 0ull;

    std::vector<ListEntry> m_list;
    std::vector<HashEntry> m_hashMap;

    static size_t computeHash(K key) {
      return size_t(uint64_t(key));
    }

    size_t computeIndex(K key) const {
      return computeHash(key) % m_hashMap.size();
    }

    size_t advanceIndex(size_t index) const {
      size_t size = m_hashMap.size();
      size_t next = index + 1;
      return next < size ? next : 0;
    }

    HashEntry* findHashEntry(K key) {
      if (!m_used)
        return nullptr;

      size_t index = computeIndex(key);

      while (m_hashMap[index].version == m_version) {
        if (m_hashMap[index].key == key)
          return &m_hashMap[index];

        index = advanceIndex(index);
      }

      return nullptr;
    }

    HashEntry* insertHashEntry(K key, const T& data) {
      growHashMapBeforeInsert();

      // If we already have an entry for the given key, return
      // the old one and let the caller deal with it
      size_t index = computeIndex(key);

      while (m_hashMap[index].version == m_version) {
        if (m_hashMap[index].key == key)
          return &m_hashMap[index];

        index = advanceIndex(index);
      }

      HashEntry* entry = &m_hashMap[index];
      entry->version = m_version;
      entry->key     = key;
      entry->data    = data;
      entry->next    = NoEntry;

      m_used += 1;
      return nullptr;
    }

    void growHashMap(size_t newSize) {
      size_t oldSize = m_hashMap.size();
      m_hashMap.resize(newSize);

      // Relocate hash entries in place
      for (size_t i = 0; i < oldSize; i++) {
        HashEntry entry = m_hashMap[i];
        m_hashMap[i].version = 0;

        while (entry.version == m_version) {
          size_t index = computeIndex(entry.key);
          entry.version = m_version + 1;

          while (m_hashMap[index].version > m_version)
            index = advanceIndex(index);

          std::swap(entry, m_hashMap[index]);
        }
      }

      m_version += 1;
    }

    void growHashMapBeforeInsert() {
      // Allow a load factor of 0.7 for performance reasons
      size_t oldSize = m_hashMap.size();

      if (10 * m_used >= 7 * oldSize) {
        size_t newSize = oldSize ? (oldSize * 2 + 5) : 37;
        growHashMap(newSize);
      }
    }

    ListEntry* getListEntry(uint32_t index) {
      return index < NoEntry ? &m_list[index] : nullptr;
    }

    ListEntry* insertListEntry(const T& subresource, HashEntry* head) {
      uint32_t newIndex = uint32_t(m_list.size());
      m_list.push_back({ subresource, head->next });
      head->next = newIndex;
      return &m_list[newIndex];
    }

  };

  /**
   * \brief Barrier set
   *
   * Accumulates memory barriers and provides a
   * method to record all those barriers into a
   * command buffer at once.
   */
  class DxvkBarrierSet {

  public:

    DxvkBarrierSet(DxvkCmdBuffer cmdBuffer);
    ~DxvkBarrierSet();

    void accessMemory(
            VkPipelineStageFlags      srcStages,
            VkAccessFlags             srcAccess,
            VkPipelineStageFlags      dstStages,
            VkAccessFlags             dstAccess);

    void accessBuffer(
      const DxvkBufferSliceHandle&    bufSlice,
            VkPipelineStageFlags      srcStages,
            VkAccessFlags             srcAccess,
            VkPipelineStageFlags      dstStages,
            VkAccessFlags             dstAccess);

    void accessImage(
      const Rc<DxvkImage>&            image,
      const VkImageSubresourceRange&  subresources,
            VkImageLayout             srcLayout,
            VkPipelineStageFlags      srcStages,
            VkAccessFlags             srcAccess,
            VkImageLayout             dstLayout,
            VkPipelineStageFlags      dstStages,
            VkAccessFlags             dstAccess);

    void releaseBuffer(
            DxvkBarrierSet&           acquire,
      const DxvkBufferSliceHandle&    bufSlice,
            uint32_t                  srcQueue,
            VkPipelineStageFlags      srcStages,
            VkAccessFlags             srcAccess,
            uint32_t                  dstQueue,
            VkPipelineStageFlags      dstStages,
            VkAccessFlags             dstAccess);

    void releaseImage(
            DxvkBarrierSet&           acquire,
      const Rc<DxvkImage>&            image,
      const VkImageSubresourceRange&  subresources,
            uint32_t                  srcQueue,
            VkImageLayout             srcLayout,
            VkPipelineStageFlags      srcStages,
            VkAccessFlags             srcAccess,
            uint32_t                  dstQueue,
            VkImageLayout             dstLayout,
            VkPipelineStageFlags      dstStages,
            VkAccessFlags             dstAccess);

    bool isBufferDirty(
      const DxvkBufferSliceHandle&    bufSlice,
            DxvkAccessFlags           bufAccess);

    bool isImageDirty(
      const Rc<DxvkImage>&            image,
      const VkImageSubresourceRange&  imgSubres,
            DxvkAccessFlags           imgAccess);

    DxvkAccessFlags getBufferAccess(
      const DxvkBufferSliceHandle&    bufSlice);

    DxvkAccessFlags getImageAccess(
      const Rc<DxvkImage>&            image,
      const VkImageSubresourceRange&  imgSubres);

    VkPipelineStageFlags getSrcStages() {
      return m_srcStages;
    }

    void recordCommands(
      const Rc<DxvkCommandList>&      commandList);

    void reset();

    static DxvkAccessFlags getAccessTypes(VkAccessFlags flags);

  private:

    struct BufSlice {
      DxvkBufferSliceHandle   slice;
      DxvkAccessFlags         access;
    };

    struct ImgSlice {
      VkImage                 image;
      VkImageSubresourceRange subres;
      DxvkAccessFlags         access;
    };

    DxvkCmdBuffer m_cmdBuffer;

    VkPipelineStageFlags m_srcStages = 0;
    VkPipelineStageFlags m_dstStages = 0;

    VkAccessFlags m_srcAccess = 0;
    VkAccessFlags m_dstAccess = 0;

    std::vector<VkBufferMemoryBarrier> m_bufBarriers;
    std::vector<VkImageMemoryBarrier>  m_imgBarriers;

    DxvkBarrierSubresourceSet<VkBuffer, DxvkBarrierBufferSlice> m_bufSlices;
    DxvkBarrierSubresourceSet<VkImage,  DxvkBarrierImageSlice>  m_imgSlices;

  };

}


code:
#pragma once

#include "dxvk_buffer.h"
#include "dxvk_descriptor.h"
#include "dxvk_image.h"
#include "dxvk_limits.h"
#include "dxvk_sampler.h"

namespace dxvk {

  /**
   * \brief Binding mask
   *
   * Used to track which resource slots have a compatible
   * binding and which ones don't. This is used to set up
   * binding-related specialization constants in shaders.
   * \tparam N Number of binding slots
   */
  template<uint32_t BindingCount>
  class DxvkBindingSet {
    constexpr static uint32_t BitCount = 32;
    constexpr static uint32_t IntCount = (BindingCount + BitCount - 1) / BitCount;
  public:

    /**
     * \brief Tests whether a binding is active
     *
     * \param [in] slot The binding ID
     * \returns \c true if the binding is active
     */
    bool test(uint32_t slot) const {
      const uint32_t intId = slot / BitCount;
      const uint32_t bitId = slot % BitCount;
      const uint32_t bitMask = 1u << bitId;
      return (m_slots[intId] & bitMask) != 0;
    }

    /**
     * \brief Changes a single binding
     *
     * \param [in] slot The binding ID
     * \param [in] value New binding state
     * \returns \c true if the state has changed
     */
    bool set(uint32_t slot, bool value) {
      const uint32_t intId = slot / BitCount;
      const uint32_t bitId = slot % BitCount;
      const uint32_t bitMask = 1u << bitId;

      const uint32_t prev = m_slots[intId];
      const uint32_t next = value
        ? prev |  bitMask
        : prev & ~bitMask;
      m_slots[intId] = next;
      return prev != next;
    }

    /**
     * \brief Marks a binding as active
     *
     * \param [in] slot The binding ID
     * \returns \c true if the state has changed
     */
    bool set(uint32_t slot) {
      return set(slot, true);
    }

    /**
     * \brief Marks a binding as inactive
     *
     * \param [in] slot The binding ID
     * \returns \c true if the state has changed
     */
    bool clr(uint32_t slot) {
      return set(slot, false);
    }

    /**
     * \brief Clears binding state
     *
     * Useful to zero out any bindings
     * that are not used by a pipeline.
     */
    void clear() {
      for (uint32_t i = 0; i < IntCount; i++)
        m_slots[i] = 0;
    }

    /**
     * \brief Enables multiple bindings
     * \param [in] n Number of bindings
     */
    void setFirst(uint32_t n) {
      for (uint32_t i = 0; i < IntCount; i++) {
        m_slots[i] = n >= BitCount ? ~0u : ~(~0u << n);
        n = n >= BitCount ? n - BitCount : 0;
      }
    }

    /**
     * \brief Finds next set binding
     *
     * \param [in] first Fist bit to consider
     * \returns Binding ID, or -1 if none was found
     */
    int32_t findNext(uint32_t first) const {
      if (unlikely(first >= BindingCount))
        return -1;

      uint32_t intId = first / BitCount;
      uint32_t bitId = first % BitCount;

      auto mask = m_slots[intId] & ~((1 << bitId) - 1);

      while (!mask && ++intId < IntCount)
        mask = m_slots[intId];

      if (!mask)
        return -1;

      return BitCount * intId + bit::tzcnt(mask);
    }

    bool operator == (const DxvkBindingSet& other) const {
      bool eq = true;
      for (uint32_t i = 0; i < IntCount; i++)
        eq &= m_slots[i] == other.m_slots[i];
      return eq;
    }

    bool operator != (const DxvkBindingSet& other) const {
      return !this->operator == (other);
    }

  private:

    uint32_t m_slots[IntCount];

  };

  using DxvkBindingMask = DxvkBindingSet<MaxNumActiveBindings>;

  /**
   * \brief Bound shader resources
   *
   * Stores the resources bound to a binding
   * slot in DXVK. These are used to create
   * descriptor sets.
   */
  struct DxvkShaderResourceSlot {
    Rc<DxvkSampler>    sampler;
    Rc<DxvkImageView>  imageView;
    Rc<DxvkBufferView> bufferView;
    DxvkBufferSlice    bufferSlice;
  };

}


code:
#include "dxvk_barrier.h"
#include "dxvk_buffer.h"
#include "dxvk_device.h"

#include <algorithm>

namespace dxvk {

  DxvkBuffer::DxvkBuffer(
          DxvkDevice*           device,
    const DxvkBufferCreateInfo& createInfo,
          DxvkMemoryAllocator&  memAlloc,
          VkMemoryPropertyFlags memFlags)
  : m_device        (device),
    m_info          (createInfo),
    m_memAlloc      (&memAlloc),
    m_memFlags      (memFlags) {
    // Align slices so that we don't violate any alignment
    // requirements imposed by the Vulkan device/driver
    VkDeviceSize sliceAlignment = computeSliceAlignment();
    m_physSliceLength = createInfo.size;
    m_physSliceStride = align(createInfo.size, sliceAlignment);
    m_physSliceCount  = std::max<VkDeviceSize>(1, 256 / m_physSliceStride);

    // Limit size of multi-slice buffers to reduce fragmentation
    constexpr VkDeviceSize MaxBufferSize = 256 << 10;

    m_physSliceMaxCount = MaxBufferSize >= m_physSliceStride
      ? MaxBufferSize / m_physSliceStride
      : 1;

    // Allocate the initial set of buffer slices. Only clear
    // buffer memory if there is more than one slice, since
    // we expect the client api to initialize the first slice.
    m_buffer = allocBuffer(m_physSliceCount, m_physSliceCount > 1);

    DxvkBufferSliceHandle slice;
    slice.handle = m_buffer.buffer;
    slice.offset = 0;
    slice.length = m_physSliceLength;
    slice.mapPtr = m_buffer.memory.mapPtr(0);

    m_physSlice = slice;
    m_lazyAlloc = m_physSliceCount > 1;
  }

  DxvkBuffer::~DxvkBuffer() {
    auto vkd = m_device->vkd();

    for (const auto& buffer : m_buffers)
      vkd->vkDestroyBuffer(vkd->device(), buffer.buffer, nullptr);
    vkd->vkDestroyBuffer(vkd->device(), m_buffer.buffer, nullptr);
  }

  DxvkBufferHandle DxvkBuffer::allocBuffer(VkDeviceSize sliceCount, bool clear) const {
    auto vkd = m_device->vkd();

    VkBufferCreateInfo info;
    info.sType                 = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
    info.pNext                 = nullptr;
    info.flags                 = 0;
    info.size                  = m_physSliceStride * sliceCount;
    info.usage                 = m_info.usage;
    info.sharingMode           = VK_SHARING_MODE_EXCLUSIVE;
    info.queueFamilyIndexCount = 0;
    info.pQueueFamilyIndices   = nullptr;

    DxvkBufferHandle handle;

    if (vkd->vkCreateBuffer(vkd->device(),
          &info, nullptr, &handle.buffer) != VK_SUCCESS) {
      throw DxvkError(str::format(
        "DxvkBuffer: Failed to create buffer:"
        "\n  size:  ", info.size,
        "\n  usage: ", info.usage));
    }

    VkMemoryDedicatedRequirements dedicatedRequirements;
    dedicatedRequirements.sType                       = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS;
    dedicatedRequirements.pNext                       = VK_NULL_HANDLE;
    dedicatedRequirements.prefersDedicatedAllocation  = VK_FALSE;
    dedicatedRequirements.requiresDedicatedAllocation = VK_FALSE;

    VkMemoryRequirements2 memReq;
    memReq.sType = VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2;
    memReq.pNext = &dedicatedRequirements;

    VkBufferMemoryRequirementsInfo2 memReqInfo;
    memReqInfo.sType  = VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2;
    memReqInfo.buffer = handle.buffer;
    memReqInfo.pNext  = VK_NULL_HANDLE;

    VkMemoryDedicatedAllocateInfo dedMemoryAllocInfo;
    dedMemoryAllocInfo.sType  = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO;
    dedMemoryAllocInfo.pNext  = VK_NULL_HANDLE;
    dedMemoryAllocInfo.buffer = handle.buffer;
    dedMemoryAllocInfo.image  = VK_NULL_HANDLE;

    vkd->vkGetBufferMemoryRequirements2(
       vkd->device(), &memReqInfo, &memReq);

    // Use high memory priority for GPU-writable resources
    bool isGpuWritable = (m_info.access & (
      VK_ACCESS_SHADER_WRITE_BIT |
      VK_ACCESS_TRANSFORM_FEEDBACK_WRITE_BIT_EXT)) != 0;

    DxvkMemoryFlags hints(DxvkMemoryFlag::GpuReadable);

    if (isGpuWritable)
      hints.set(DxvkMemoryFlag::GpuWritable);

    // Staging buffers that can't even be used as a transfer destinations
    // are likely short-lived, so we should put them on a separate memory
    // pool in order to avoid fragmentation
    if ((DxvkBarrierSet::getAccessTypes(m_info.access) == DxvkAccess::Read)
     && (m_info.usage & VK_BUFFER_USAGE_TRANSFER_SRC_BIT))
      hints.set(DxvkMemoryFlag::Transient);

    // Ask driver whether we should be using a dedicated allocation
    handle.memory = m_memAlloc->alloc(&memReq.memoryRequirements,
      dedicatedRequirements, dedMemoryAllocInfo, m_memFlags, hints);

    if (vkd->vkBindBufferMemory(vkd->device(), handle.buffer,
        handle.memory.memory(), handle.memory.offset()) != VK_SUCCESS)
      throw DxvkError("DxvkBuffer: Failed to bind device memory");

    if (clear && (m_memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT))
      std::memset(handle.memory.mapPtr(0), 0, info.size);

    return handle;
  }

  VkDeviceSize DxvkBuffer::computeSliceAlignment() const {
    const auto& devInfo = m_device->properties().core.properties;

    VkDeviceSize result = sizeof(uint32_t);

    if (m_info.usage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT)
      result = std::max(result, devInfo.limits.minUniformBufferOffsetAlignment);

    if (m_info.usage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT)
      result = std::max(result, devInfo.limits.minStorageBufferOffsetAlignment);

    if (m_info.usage & (VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT)) {
      result = std::max(result, devInfo.limits.minTexelBufferOffsetAlignment);
      result = std::max(result, VkDeviceSize(16));
    }

    if (m_info.usage & (VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT)
     && m_info.size > (devInfo.limits.optimalBufferCopyOffsetAlignment / 2))
      result = std::max(result, devInfo.limits.optimalBufferCopyOffsetAlignment);

    // For some reason, Warhammer Chaosbane breaks otherwise
    if (m_info.usage & (VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT))
      result = std::max(result, VkDeviceSize(256));

    if (m_memFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
      result = std::max(result, devInfo.limits.nonCoherentAtomSize);
      result = std::max(result, VkDeviceSize(64));
    }

    return result;
  }

  DxvkBufferView::DxvkBufferView(
    const Rc<vk::DeviceFn>&         vkd,
    const Rc<DxvkBuffer>&           buffer,
    const DxvkBufferViewCreateInfo& info)
  : m_vkd(vkd), m_info(info), m_buffer(buffer),
    m_bufferSlice (getSliceHandle()),
    m_bufferView  (createBufferView(m_bufferSlice)) {

  }

  DxvkBufferView::~DxvkBufferView() {
    if (m_views.empty()) {
      m_vkd->vkDestroyBufferView(
        m_vkd->device(), m_bufferView, nullptr);
    } else {
      for (const auto& pair : m_views) {
        m_vkd->vkDestroyBufferView(
          m_vkd->device(), pair.second, nullptr);
      }
    }
  }

  VkBufferView DxvkBufferView::createBufferView(
    const DxvkBufferSliceHandle& slice) {
    VkBufferViewCreateInfo viewInfo;
    viewInfo.sType  = VK_STRUCTURE_TYPE_BUFFER_VIEW_CREATE_INFO;
    viewInfo.pNext  = nullptr;
    viewInfo.flags  = 0;
    viewInfo.buffer = slice.handle;
    viewInfo.format = m_info.format;
    viewInfo.offset = slice.offset;
    viewInfo.range  = slice.length;

    VkBufferView result = VK_NULL_HANDLE;

    if (m_vkd->vkCreateBufferView(m_vkd->device(),
          &viewInfo, nullptr, &result) != VK_SUCCESS) {
      throw DxvkError(str::format(
        "DxvkBufferView: Failed to create buffer view:",
        "\n  Offset: ", viewInfo.offset,
        "\n  Range:  ", viewInfo.range,
        "\n  Format: ", viewInfo.format));
    }

    return result;
  }

  void DxvkBufferView::updateBufferView(
    const DxvkBufferSliceHandle& slice) {
    if (m_views.empty())
      m_views.insert({ m_bufferSlice, m_bufferView });

    m_bufferSlice = slice;

    auto entry = m_views.find(slice);
    if (entry != m_views.end()) {
      m_bufferView = entry->second;
    } else {
      m_bufferView = createBufferView(m_bufferSlice);
      m_views.insert({ m_bufferSlice, m_bufferView });
    }
  }

  DxvkBufferTracker:: DxvkBufferTracker() { }
  DxvkBufferTracker::~DxvkBufferTracker() { }

  void DxvkBufferTracker::reset() {
    std::sort(m_entries.begin(), m_entries.end(),
      [] (const Entry& a, const Entry& b) {
        return a.slice.handle < b.slice.handle;
      });

    for (const auto& e : m_entries)
      e.buffer->freeSlice(e.slice);

    m_entries.clear();
  }

}