code:
#include "d3d11_device.h"
#include "d3d11_gdi.h"
#include "d3d11_texture.h"

#include "../util/util_shared_res.h"

namespace dxvk {

  D3D11CommonTexture::D3D11CommonTexture(
          ID3D11Resource*             pInterface,
          D3D11Device*                pDevice,
    const D3D11_COMMON_TEXTURE_DESC*  pDesc,
          D3D11_RESOURCE_DIMENSION    Dimension,
          DXGI_USAGE                  DxgiUsage,
          VkImage                     vkImage,
          HANDLE                      hSharedHandle)
  : m_interface(pInterface), m_device(pDevice), m_dimension(Dimension), m_desc(*pDesc), m_dxgiUsage(DxgiUsage) {
    DXGI_VK_FORMAT_MODE   formatMode   = GetFormatMode();
    DXGI_VK_FORMAT_INFO   formatInfo   = m_device->LookupFormat(m_desc.Format, formatMode);
    DXGI_VK_FORMAT_FAMILY formatFamily = m_device->LookupFamily(m_desc.Format, formatMode);
    DXGI_VK_FORMAT_INFO   formatPacked = m_device->LookupPackedFormat(m_desc.Format, formatMode);
    m_packedFormat = formatPacked.Format;

    DxvkImageCreateInfo imageInfo;
    imageInfo.type            = GetVkImageType();
    imageInfo.format          = formatInfo.Format;
    imageInfo.flags           = 0;
    imageInfo.sampleCount     = VK_SAMPLE_COUNT_1_BIT;
    imageInfo.extent.width    = m_desc.Width;
    imageInfo.extent.height   = m_desc.Height;
    imageInfo.extent.depth    = m_desc.Depth;
    imageInfo.numLayers       = m_desc.ArraySize;
    imageInfo.mipLevels       = m_desc.MipLevels;
    imageInfo.usage           = VK_IMAGE_USAGE_TRANSFER_SRC_BIT
                              | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
    imageInfo.stages          = VK_PIPELINE_STAGE_TRANSFER_BIT;
    imageInfo.access          = VK_ACCESS_TRANSFER_READ_BIT
                              | VK_ACCESS_TRANSFER_WRITE_BIT;
    imageInfo.tiling          = VK_IMAGE_TILING_OPTIMAL;
    imageInfo.layout          = VK_IMAGE_LAYOUT_GENERAL;
    imageInfo.initialLayout   = VK_IMAGE_LAYOUT_UNDEFINED;
    imageInfo.shared          = vkImage != VK_NULL_HANDLE;

    // Normalise hSharedhandle to INVALID_HANDLE_VALUE to allow passing in nullptr
    if (hSharedHandle == nullptr)
      hSharedHandle = INVALID_HANDLE_VALUE;

    if (m_desc.MiscFlags & (D3D11_RESOURCE_MISC_SHARED|D3D11_RESOURCE_MISC_SHARED_NTHANDLE)) {
      if (m_desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX)
        Logger::warn("D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX: not supported.");

      imageInfo.shared = true;
      imageInfo.sharing.mode = hSharedHandle == INVALID_HANDLE_VALUE ? DxvkSharedHandleMode::Export : DxvkSharedHandleMode::Import;
      imageInfo.sharing.type = m_desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED_NTHANDLE
        ? VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT
        : VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT;
      imageInfo.sharing.handle = hSharedHandle;
    }

    if (!pDevice->GetOptions()->disableMsaa)
      DecodeSampleCount(m_desc.SampleDesc.Count, &imageInfo.sampleCount);

    if ((m_desc.BindFlags & D3D11_BIND_UNORDERED_ACCESS) && IsR32UavCompatibleFormat(m_desc.Format)) {
      formatFamily.Add(formatInfo.Format);
      formatFamily.Add(VK_FORMAT_R32_SFLOAT);
      formatFamily.Add(VK_FORMAT_R32_UINT);
      formatFamily.Add(VK_FORMAT_R32_SINT);
    }

    // The image must be marked as mutable if it can be reinterpreted
    // by a view with a different format. Depth-stencil formats cannot
    // be reinterpreted in Vulkan, so we'll ignore those.
    auto formatProperties = imageFormatInfo(formatInfo.Format);

    bool isMutable = formatFamily.FormatCount > 1;
    bool isMultiPlane = (formatProperties->aspectMask & VK_IMAGE_ASPECT_PLANE_0_BIT) != 0;
    bool isColorFormat = (formatProperties->aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0;

    if (isMutable && (isColorFormat || isMultiPlane)) {
      imageInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
      imageInfo.viewFormatCount = formatFamily.FormatCount;
      imageInfo.viewFormats     = formatFamily.Formats;
    }

    // Adjust image flags based on the corresponding D3D flags
    if (m_desc.BindFlags & D3D11_BIND_SHADER_RESOURCE) {
      imageInfo.usage  |= VK_IMAGE_USAGE_SAMPLED_BIT;
      imageInfo.stages |= pDevice->GetEnabledShaderStages();
      imageInfo.access |= VK_ACCESS_SHADER_READ_BIT;
    }

    if (m_desc.BindFlags & D3D11_BIND_RENDER_TARGET) {
      imageInfo.usage  |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
      imageInfo.stages |= VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
      imageInfo.access |= VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
                       |  VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
    }

    if (m_desc.BindFlags & D3D11_BIND_DEPTH_STENCIL) {
      imageInfo.usage  |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
      imageInfo.stages |= VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT
                       |  VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
      imageInfo.access |= VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT
                       |  VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
    }

    if (m_desc.BindFlags & D3D11_BIND_UNORDERED_ACCESS) {
      imageInfo.usage  |= VK_IMAGE_USAGE_STORAGE_BIT;
      imageInfo.stages |= pDevice->GetEnabledShaderStages();
      imageInfo.access |= VK_ACCESS_SHADER_READ_BIT
                       |  VK_ACCESS_SHADER_WRITE_BIT;

      // UAVs are not supported for sRGB formats on most drivers,
      // but we can still create linear views for the image
      if (formatProperties->flags.test(DxvkFormatFlag::ColorSpaceSrgb))
        imageInfo.flags |= VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;
    }

    // Multi-plane formats need views to be created with color formats, and
    // may not report all relevant usage flags as supported on their own.
    // Also, enable sampled bit to enable use with video processor APIs.
    if (isMultiPlane) {
      imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
      imageInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT
                      |  VK_IMAGE_CREATE_EXTENDED_USAGE_BIT;
    }

    // Access pattern for meta-resolve operations
    if (imageInfo.sampleCount != VK_SAMPLE_COUNT_1_BIT && isColorFormat) {
      imageInfo.usage  |= VK_IMAGE_USAGE_SAMPLED_BIT;
      imageInfo.stages |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
      imageInfo.access |= VK_ACCESS_SHADER_READ_BIT;
    }

    if (m_desc.MiscFlags & D3D11_RESOURCE_MISC_TEXTURECUBE)
      imageInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;

    if (Dimension == D3D11_RESOURCE_DIMENSION_TEXTURE3D &&
        (m_desc.BindFlags & D3D11_BIND_RENDER_TARGET))
      imageInfo.flags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;

    // Swap chain back buffers need to be shader readable
    if (DxgiUsage & DXGI_USAGE_BACK_BUFFER) {
      imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
      imageInfo.stages |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
      imageInfo.access |= VK_ACCESS_SHADER_READ_BIT;
      imageInfo.shared = VK_TRUE;
    }

    // Some image formats (i.e. the R32G32B32 ones) are
    // only supported with linear tiling on most GPUs
    if (!CheckImageSupport(&imageInfo, VK_IMAGE_TILING_OPTIMAL))
      imageInfo.tiling = VK_IMAGE_TILING_LINEAR;

    // Determine map mode based on our findings
    m_mapMode = DetermineMapMode(&imageInfo);

    // If the image is mapped directly to host memory, we need
    // to enable linear tiling, and DXVK needs to be aware that
    // the image can be accessed by the host.
    if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT) {
      imageInfo.tiling        = VK_IMAGE_TILING_LINEAR;
      imageInfo.initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED;
    }

    // If necessary, create the mapped linear buffer
    if (m_mapMode != D3D11_COMMON_TEXTURE_MAP_MODE_NONE) {
      for (uint32_t i = 0; i < m_desc.ArraySize; i++) {
        for (uint32_t j = 0; j < m_desc.MipLevels; j++) {
          if (m_mapMode != D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT)
            m_buffers.push_back(CreateMappedBuffer(j));

          m_mapInfo.push_back({ D3D11_MAP(~0u), 0ull });
        }
      }
    }

    // Skip image creation if possible
    if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_STAGING)
      return;

    // We must keep LINEAR images in GENERAL layout, but we
    // can choose a better layout for the image based on how
    // it is going to be used by the game.
    if (imageInfo.tiling == VK_IMAGE_TILING_OPTIMAL && !isMultiPlane && imageInfo.sharing.mode == DxvkSharedHandleMode::None)
      imageInfo.layout = OptimizeLayout(imageInfo.usage);

    // For some formats, we need to enable sampled and/or
    // render target capabilities if available, but these
    // should in no way affect the default image layout
    imageInfo.usage |= EnableMetaCopyUsage(imageInfo.format, imageInfo.tiling);
    imageInfo.usage |= EnableMetaPackUsage(imageInfo.format, m_desc.CPUAccessFlags);

    // Check if we can actually create the image
    if (!CheckImageSupport(&imageInfo, imageInfo.tiling)) {
      throw DxvkError(str::format(
        "D3D11: Cannot create texture:",
        "\n  Format:  ", m_desc.Format,
        "\n  Extent:  ", m_desc.Width,
                    "x", m_desc.Height,
                    "x", m_desc.Depth,
        "\n  Samples: ", m_desc.SampleDesc.Count,
        "\n  Layers:  ", m_desc.ArraySize,
        "\n  Levels:  ", m_desc.MipLevels,
        "\n  Usage:   ", std::hex, m_desc.BindFlags,
        "\n  Flags:   ", std::hex, m_desc.MiscFlags));
    }

    // Create the image on a host-visible memory type
    // in case it is going to be mapped directly.
    VkMemoryPropertyFlags memoryProperties = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;

    if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT)
      memoryProperties = GetMemoryFlags();

    if (vkImage == VK_NULL_HANDLE)
      m_image = m_device->GetDXVKDevice()->createImage(imageInfo, memoryProperties);
    else
      m_image = m_device->GetDXVKDevice()->createImageFromVkImage(imageInfo, vkImage);

    if (imageInfo.sharing.mode == DxvkSharedHandleMode::Export)
      ExportImageInfo();
  }

  D3D11CommonTexture::~D3D11CommonTexture() {

  }

  VkDeviceSize D3D11CommonTexture::ComputeMappedOffset(UINT Subresource, UINT Plane, VkOffset3D Offset) const {
    auto packedFormatInfo = imageFormatInfo(m_packedFormat);

    VkImageAspectFlags aspectMask = packedFormatInfo->aspectMask;
    VkDeviceSize elementSize = packedFormatInfo->elementSize;

    if (packedFormatInfo->flags.test(DxvkFormatFlag::MultiPlane)) {
      auto plane = &packedFormatInfo->planes[Plane];
      elementSize = plane->elementSize;
      Offset.x /= plane->blockSize.width;
      Offset.y /= plane->blockSize.height;
      aspectMask = vk::getPlaneAspect(Plane);
    }

    auto layout = GetSubresourceLayout(aspectMask, Subresource);
    auto blockOffset = util::computeBlockOffset(Offset, packedFormatInfo->blockSize);

    return VkDeviceSize(blockOffset.z) * layout.DepthPitch
         + VkDeviceSize(blockOffset.y) * layout.RowPitch
         + VkDeviceSize(blockOffset.x) * elementSize
         + VkDeviceSize(layout.Offset);
  }

  VkImageSubresource D3D11CommonTexture::GetSubresourceFromIndex(
          VkImageAspectFlags    Aspect,
          UINT                  Subresource) const {
    VkImageSubresource result;
    result.aspectMask     = Aspect;
    result.mipLevel       = Subresource % m_desc.MipLevels;
    result.arrayLayer     = Subresource / m_desc.MipLevels;
    return result;
  }

  D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT D3D11CommonTexture::GetSubresourceLayout(
          VkImageAspectFlags    AspectMask,
          UINT                  Subresource) const {
    VkImageSubresource subresource = GetSubresourceFromIndex(AspectMask, Subresource);
    D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT layout = { };

    switch (m_mapMode) {
      case D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT: {
        auto vkLayout = m_image->querySubresourceLayout(subresource);
        layout.Offset     = vkLayout.offset;
        layout.Size       = vkLayout.size;
        layout.RowPitch   = vkLayout.rowPitch;
        layout.DepthPitch = vkLayout.depthPitch;
      } break;

      case D3D11_COMMON_TEXTURE_MAP_MODE_NONE:
      case D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER:
      case D3D11_COMMON_TEXTURE_MAP_MODE_STAGING: {
        auto packedFormatInfo = imageFormatInfo(m_packedFormat);

        VkImageAspectFlags aspects = packedFormatInfo->aspectMask;
        VkExtent3D mipExtent = MipLevelExtent(subresource.mipLevel);

        while (aspects) {
          auto aspect = vk::getNextAspect(aspects);
          auto extent = mipExtent;
          auto elementSize = packedFormatInfo->elementSize;

          if (packedFormatInfo->flags.test(DxvkFormatFlag::MultiPlane)) {
            auto plane = &packedFormatInfo->planes[vk::getPlaneIndex(aspect)];
            extent.width  /= plane->blockSize.width;
            extent.height /= plane->blockSize.height;
            elementSize = plane->elementSize;
          }

          auto blockCount = util::computeBlockCount(extent, packedFormatInfo->blockSize);

          if (!layout.RowPitch) {
            layout.RowPitch   = elementSize * blockCount.width;
            layout.DepthPitch = elementSize * blockCount.width * blockCount.height;
          }

          VkDeviceSize size = elementSize * blockCount.width * blockCount.height * blockCount.depth;

          if (aspect & AspectMask)
            layout.Size += size;
          else if (!layout.Size)
            layout.Offset += size;
        }
      } break;
    }

    // D3D wants us to return the total subresource size in some instances
    if (m_dimension < D3D11_RESOURCE_DIMENSION_TEXTURE2D) layout.RowPitch = layout.Size;
    if (m_dimension < D3D11_RESOURCE_DIMENSION_TEXTURE3D) layout.DepthPitch = layout.Size;
    return layout;
  }

  DXGI_VK_FORMAT_MODE D3D11CommonTexture::GetFormatMode() const {
    if (m_desc.BindFlags & D3D11_BIND_RENDER_TARGET)
      return DXGI_VK_FORMAT_MODE_COLOR;

    if (m_desc.BindFlags & D3D11_BIND_DEPTH_STENCIL)
      return DXGI_VK_FORMAT_MODE_DEPTH;

    return DXGI_VK_FORMAT_MODE_ANY;
  }

  uint32_t D3D11CommonTexture::GetPlaneCount() const {
    return vk::getPlaneCount(m_image->formatInfo()->aspectMask);
  }

  bool D3D11CommonTexture::CheckViewCompatibility(UINT BindFlags, DXGI_FORMAT Format, UINT Plane) const {
    const DxvkImageCreateInfo& imageInfo = m_image->info();

    // Check whether the given bind flags are supported
    if ((m_desc.BindFlags & BindFlags) != BindFlags)
      return false;

    // Check whether the view format is compatible
    DXGI_VK_FORMAT_MODE formatMode = GetFormatMode();
    DXGI_VK_FORMAT_INFO viewFormat = m_device->LookupFormat(Format,        formatMode);
    DXGI_VK_FORMAT_INFO baseFormat = m_device->LookupFormat(m_desc.Format, formatMode);

    // Check whether the plane index is valid for the given format
    uint32_t planeCount = GetPlaneCount();

    if (Plane >= planeCount)
      return false;

    if (imageInfo.flags & VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT) {
      // Check whether the given combination of image
      // view type and view format is actually supported
      VkFormatFeatureFlags features = GetImageFormatFeatures(BindFlags);

      if (!CheckFormatFeatureSupport(viewFormat.Format, features))
        return false;

      // Using the image format itself is supported for non-planar formats
      if (viewFormat.Format == baseFormat.Format && planeCount == 1)
        return true;

      // If there is a list of compatible formats, the view format must be
      // included in that list. For planar formats, the list is laid out in
      // such a way that the n-th format is supported for the n-th plane.
      for (size_t i = Plane; i < imageInfo.viewFormatCount; i += planeCount) {
        if (imageInfo.viewFormats[i] == viewFormat.Format) {
          return true;
        }
      }

      // Otherwise, all bit-compatible formats can be used.
      if (imageInfo.viewFormatCount == 0 && planeCount == 1) {
        auto baseFormatInfo = imageFormatInfo(baseFormat.Format);
        auto viewFormatInfo = imageFormatInfo(viewFormat.Format);

        return baseFormatInfo->aspectMask  == viewFormatInfo->aspectMask
            && baseFormatInfo->elementSize == viewFormatInfo->elementSize;
      }

      return false;
    } else {
      // For non-mutable images, the view format
      // must be identical to the image format.
      return viewFormat.Format == baseFormat.Format && planeCount == 1;
    }
  }

  HRESULT D3D11CommonTexture::NormalizeTextureProperties(D3D11_COMMON_TEXTURE_DESC* pDesc) {
    if (pDesc->Width == 0 || pDesc->Height == 0 || pDesc->Depth == 0 || pDesc->ArraySize == 0)
      return E_INVALIDARG;

    if (FAILED(DecodeSampleCount(pDesc->SampleDesc.Count, nullptr)))
      return E_INVALIDARG;

    if ((pDesc->MiscFlags & D3D11_RESOURCE_MISC_GDI_COMPATIBLE)
     && (pDesc->Usage == D3D11_USAGE_STAGING
      || (pDesc->Format != DXGI_FORMAT_B8G8R8A8_TYPELESS
       && pDesc->Format != DXGI_FORMAT_B8G8R8A8_UNORM
       && pDesc->Format != DXGI_FORMAT_B8G8R8A8_UNORM_SRGB)))
      return E_INVALIDARG;

    if ((pDesc->MiscFlags & D3D11_RESOURCE_MISC_GENERATE_MIPS)
     && (pDesc->BindFlags & (D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET))
                         != (D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET))
      return E_INVALIDARG;

    // TILE_POOL is invalid, but we don't support TILED either
    if (pDesc->MiscFlags & (D3D11_RESOURCE_MISC_TILE_POOL | D3D11_RESOURCE_MISC_TILED))
      return E_INVALIDARG;

    // Use the maximum possible mip level count if the supplied
    // mip level count is either unspecified (0) or invalid
    const uint32_t maxMipLevelCount = (pDesc->SampleDesc.Count <= 1)
      ? util::computeMipLevelCount({ pDesc->Width, pDesc->Height, pDesc->Depth })
      : 1u;

    if (pDesc->MipLevels == 0 || pDesc->MipLevels > maxMipLevelCount)
      pDesc->MipLevels = maxMipLevelCount;

    // Row-major is only supported for textures with one single
    // subresource and one sample and cannot have bind flags.
    if (pDesc->TextureLayout == D3D11_TEXTURE_LAYOUT_ROW_MAJOR
     && (pDesc->MipLevels != 1 || pDesc->SampleDesc.Count != 1 || pDesc->BindFlags))
      return E_INVALIDARG;

    // Standard swizzle is unsupported
    if (pDesc->TextureLayout == D3D11_TEXTURE_LAYOUT_64K_STANDARD_SWIZZLE)
      return E_INVALIDARG;

    return S_OK;
  }

  BOOL D3D11CommonTexture::CheckImageSupport(
    const DxvkImageCreateInfo*  pImageInfo,
          VkImageTiling         Tiling) const {
    const Rc<DxvkAdapter> adapter = m_device->GetDXVKDevice()->adapter();

    VkImageUsageFlags usage = pImageInfo->usage;

    if (pImageInfo->flags & VK_IMAGE_CREATE_EXTENDED_USAGE_BIT)
      usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;

    VkImageFormatProperties formatProps = { };
    VkResult status = adapter->imageFormatProperties(
      pImageInfo->format, pImageInfo->type, Tiling,
      usage, pImageInfo->flags, formatProps);

    if (status != VK_SUCCESS)
      return FALSE;

    return (pImageInfo->extent.width  <= formatProps.maxExtent.width)
        && (pImageInfo->extent.height <= formatProps.maxExtent.height)
        && (pImageInfo->extent.depth  <= formatProps.maxExtent.depth)
        && (pImageInfo->numLayers     <= formatProps.maxArrayLayers)
        && (pImageInfo->mipLevels     <= formatProps.maxMipLevels)
        && (pImageInfo->sampleCount    & formatProps.sampleCounts);
  }

  BOOL D3D11CommonTexture::CheckFormatFeatureSupport(
          VkFormat              Format,
          VkFormatFeatureFlags  Features) const {
    VkFormatProperties properties = m_device->GetDXVKDevice()->adapter()->formatProperties(Format);

    return (properties.linearTilingFeatures  & Features) == Features
        || (properties.optimalTilingFeatures & Features) == Features;
  }

  VkImageUsageFlags D3D11CommonTexture::EnableMetaCopyUsage(
          VkFormat              Format,
          VkImageTiling         Tiling) const {
    VkFormatFeatureFlags requestedFeatures = 0;

    if (Format == VK_FORMAT_D16_UNORM || Format == VK_FORMAT_D32_SFLOAT) {
      requestedFeatures |= VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT
                        |  VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
    }

    if (Format == VK_FORMAT_R16_UNORM || Format == VK_FORMAT_R32_SFLOAT) {
      requestedFeatures |= VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT
                        |  VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
    }

    if (Format == VK_FORMAT_D32_SFLOAT_S8_UINT || Format == VK_FORMAT_D24_UNORM_S8_UINT)
      requestedFeatures |= VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;

    if (requestedFeatures == 0)
      return 0;

    // Enable usage flags for all supported and requested features
    VkFormatProperties properties = m_device->GetDXVKDevice()->adapter()->formatProperties(Format);

    requestedFeatures &= Tiling == VK_IMAGE_TILING_OPTIMAL
      ? properties.optimalTilingFeatures
      : properties.linearTilingFeatures;

    VkImageUsageFlags requestedUsage = 0;

    if (requestedFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)
      requestedUsage |= VK_IMAGE_USAGE_SAMPLED_BIT;

    if (requestedFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
      requestedUsage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;

    if (requestedFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT)
      requestedUsage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;

    return requestedUsage;
  }

  VkImageUsageFlags D3D11CommonTexture::EnableMetaPackUsage(
          VkFormat              Format,
          UINT                  CpuAccess) const {
    if ((CpuAccess & D3D11_CPU_ACCESS_READ) == 0)
      return 0;

    const auto dsMask = VK_IMAGE_ASPECT_DEPTH_BIT
                      | VK_IMAGE_ASPECT_STENCIL_BIT;

    auto formatInfo = imageFormatInfo(Format);

    return formatInfo->aspectMask == dsMask
      ? VK_IMAGE_USAGE_SAMPLED_BIT
      : 0;
  }

  VkMemoryPropertyFlags D3D11CommonTexture::GetMemoryFlags() const {
    VkMemoryPropertyFlags memoryFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
                                      | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;

    bool useCached = (m_device->GetOptions()->cachedDynamicResources == ~0u)
                  || (m_device->GetOptions()->cachedDynamicResources & m_desc.BindFlags);

    if (m_desc.Usage == D3D11_USAGE_STAGING || useCached)
      memoryFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
    else if (m_desc.Usage == D3D11_USAGE_DEFAULT || m_desc.BindFlags)
      memoryFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;

    return memoryFlags;
  }

  D3D11_COMMON_TEXTURE_MAP_MODE D3D11CommonTexture::DetermineMapMode(
    const DxvkImageCreateInfo*  pImageInfo) const {
    // Don't map an image unless the application requests it
    if (!m_desc.CPUAccessFlags)
      return D3D11_COMMON_TEXTURE_MAP_MODE_NONE;

    // If the resource cannot be used in the actual rendering pipeline, we
    // do not need to create an actual image and can instead implement copy
    // functions as buffer-to-image and image-to-buffer copies.
    if (!m_desc.BindFlags && m_desc.Usage != D3D11_USAGE_DEFAULT)
      return D3D11_COMMON_TEXTURE_MAP_MODE_STAGING;

    // Depth-stencil formats in D3D11 can be mapped and follow special
    // packing rules, so we need to copy that data into a buffer first
    if (GetPackedDepthStencilFormat(m_desc.Format))
      return D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER;

    // Multi-plane images have a special memory layout in D3D11
    if (imageFormatInfo(pImageInfo->format)->flags.test(DxvkFormatFlag::MultiPlane))
      return D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER;

    // If we can't use linear tiling for this image, we have to use a buffer
    if (!this->CheckImageSupport(pImageInfo, VK_IMAGE_TILING_LINEAR))
      return D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER;

    // If supported and requested, create a linear image. Default images
    // can be used for resolves and other operations regardless of bind
    // flags, so we need to use a proper image for those.
    if (m_desc.TextureLayout == D3D11_TEXTURE_LAYOUT_ROW_MAJOR
     || m_desc.Usage         == D3D11_USAGE_DEFAULT)
      return D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT;

    // The overhead of frequently uploading large dynamic images may outweigh
    // the benefit of linear tiling, so use a linear image in those cases.
    VkDeviceSize threshold = m_device->GetOptions()->maxDynamicImageBufferSize;
    VkDeviceSize size = util::computeImageDataSize(pImageInfo->format, pImageInfo->extent);

    if (size > threshold)
      return D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT;

    // Dynamic images that can be sampled by a shader should generally go
    // through a buffer to allow optimal tiling and to avoid running into
    // bugs where games ignore the pitch when mapping the image.
    return D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER;
  }

  void D3D11CommonTexture::ExportImageInfo() {
    HANDLE hSharedHandle;

    if (m_desc.MiscFlags & D3D11_RESOURCE_MISC_SHARED)
      hSharedHandle = openKmtHandle( m_image->sharedHandle() );
    else
      hSharedHandle = m_image->sharedHandle();

    DxvkSharedTextureMetadata metadata;

    metadata.Width          = m_desc.Width;
    metadata.Height         = m_desc.Height;
    metadata.MipLevels      = m_desc.MipLevels;
    metadata.ArraySize      = m_desc.ArraySize;
    metadata.Format         = m_desc.Format;
    metadata.SampleDesc     = m_desc.SampleDesc;
    metadata.Usage          = m_desc.Usage;
    metadata.BindFlags      = m_desc.BindFlags;
    metadata.CPUAccessFlags = m_desc.CPUAccessFlags;
    metadata.MiscFlags      = m_desc.MiscFlags;
    metadata.TextureLayout  = m_desc.TextureLayout;

    if (hSharedHandle == INVALID_HANDLE_VALUE || !setSharedMetadata(hSharedHandle, &metadata, sizeof(metadata))) {
      Logger::warn("D3D11: Failed to write shared resource info for a texture");
    }

    if (hSharedHandle != INVALID_HANDLE_VALUE)
      CloseHandle(hSharedHandle);
  }

  BOOL D3D11CommonTexture::IsR32UavCompatibleFormat(
          DXGI_FORMAT           Format) {
    return Format == DXGI_FORMAT_R8G8B8A8_TYPELESS
        || Format == DXGI_FORMAT_B8G8R8A8_TYPELESS
        || Format == DXGI_FORMAT_B8G8R8X8_TYPELESS
        || Format == DXGI_FORMAT_R10G10B10A2_TYPELESS
        || Format == DXGI_FORMAT_R16G16_TYPELESS
        || Format == DXGI_FORMAT_R32_TYPELESS;
  }

  D3D11CommonTexture::MappedBuffer D3D11CommonTexture::CreateMappedBuffer(UINT MipLevel) const {
    const DxvkFormatInfo* formatInfo = imageFormatInfo(
      m_device->LookupPackedFormat(m_desc.Format, GetFormatMode()).Format);

    DxvkBufferCreateInfo info;
    info.size   = GetSubresourceLayout(formatInfo->aspectMask, MipLevel).Size;
    info.usage  = VK_BUFFER_USAGE_TRANSFER_SRC_BIT
                | VK_BUFFER_USAGE_TRANSFER_DST_BIT
                | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT
                | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT
                | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
    info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT
                | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
    info.access = VK_ACCESS_TRANSFER_READ_BIT
                | VK_ACCESS_TRANSFER_WRITE_BIT
                | VK_ACCESS_SHADER_READ_BIT
                | VK_ACCESS_SHADER_WRITE_BIT;

    VkMemoryPropertyFlags memType = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
                                  | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;

    bool useCached = m_device->GetOptions()->cachedDynamicResources == ~0u;

    if (m_desc.Usage == D3D11_USAGE_STAGING || useCached)
      memType |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;

    MappedBuffer result;
    result.buffer = m_device->GetDXVKDevice()->createBuffer(info, memType);
    result.slice = result.buffer->getSliceHandle();
    return result;
  }

  VkImageType D3D11CommonTexture::GetImageTypeFromResourceDim(D3D11_RESOURCE_DIMENSION Dimension) {
    switch (Dimension) {
      case D3D11_RESOURCE_DIMENSION_TEXTURE1D: return VK_IMAGE_TYPE_1D;
      case D3D11_RESOURCE_DIMENSION_TEXTURE2D: return VK_IMAGE_TYPE_2D;
      case D3D11_RESOURCE_DIMENSION_TEXTURE3D: return VK_IMAGE_TYPE_3D;
      default: throw DxvkError("D3D11CommonTexture: Unhandled resource dimension");
    }
  }

  VkImageLayout D3D11CommonTexture::OptimizeLayout(VkImageUsageFlags Usage) {
    const VkImageUsageFlags usageFlags = Usage;

    // Filter out unnecessary flags. Transfer operations
    // are handled by the backend in a transparent manner.
    Usage &= ~(VK_IMAGE_USAGE_TRANSFER_DST_BIT
             | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);

    // If the image is used only as an attachment, we never
    // have to transform the image back to a different layout
    if (Usage == VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
      return VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

    if (Usage == VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
      return VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

    Usage &= ~(VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
             | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);

    // If the image is used for reading but not as a storage
    // image, we can optimize the image for texture access
    if (Usage == VK_IMAGE_USAGE_SAMPLED_BIT) {
      return usageFlags & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT
        ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL
        : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
    }

    // Otherwise, we have to stick with the default layout
    return VK_IMAGE_LAYOUT_GENERAL;
  }

  D3D11DXGISurface::D3D11DXGISurface(
          ID3D11Resource*     pResource,
          D3D11CommonTexture* pTexture)
  : m_resource  (pResource),
    m_texture   (pTexture),
    m_gdiSurface(nullptr) {
    if (pTexture->Desc()->MiscFlags & D3D11_RESOURCE_MISC_GDI_COMPATIBLE)
      m_gdiSurface = new D3D11GDISurface(m_resource, 0);
  }

  D3D11DXGISurface::~D3D11DXGISurface() {
    if (m_gdiSurface)
      delete m_gdiSurface;
  }

  ULONG STDMETHODCALLTYPE D3D11DXGISurface::AddRef() {
    return m_resource->AddRef();
  }

  ULONG STDMETHODCALLTYPE D3D11DXGISurface::Release() {
    return m_resource->Release();
  }

  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::QueryInterface(
          REFIID                  riid,
          void**                  ppvObject) {
    return m_resource->QueryInterface(riid, ppvObject);
  }

  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::GetPrivateData(
          REFGUID                 Name,
          UINT*                   pDataSize,
          void*                   pData) {
    return m_resource->GetPrivateData(Name, pDataSize, pData);
  }

  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::SetPrivateData(
          REFGUID                 Name,
          UINT                    DataSize,
    const void*                   pData) {
    return m_resource->SetPrivateData(Name, DataSize, pData);
  }

  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::SetPrivateDataInterface(
          REFGUID                 Name,
    const IUnknown*               pUnknown) {
    return m_resource->SetPrivateDataInterface(Name, pUnknown);
  }

  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::GetParent(
          REFIID                  riid,
          void**                  ppParent) {
    return GetDevice(riid, ppParent);
  }

  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::GetDevice(
          REFIID                  riid,
          void**                  ppDevice) {
    Com<ID3D11Device> device;
    m_resource->GetDevice(&device);
    return device->QueryInterface(riid, ppDevice);
  }

  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::GetDesc(
          DXGI_SURFACE_DESC*      pDesc) {
    if (!pDesc)
      return DXGI_ERROR_INVALID_CALL;

    auto desc = m_texture->Desc();
    pDesc->Width      = desc->Width;
    pDesc->Height     = desc->Height;
    pDesc->Format     = desc->Format;
    pDesc->SampleDesc = desc->SampleDesc;
    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::Map(
            DXGI_MAPPED_RECT*       pLockedRect,
            UINT                    MapFlags) {
    Com<ID3D11Device>        device;
    Com<ID3D11DeviceContext> context;

    m_resource->GetDevice(&device);
    device->GetImmediateContext(&context);

    if (pLockedRect) {
      pLockedRect->Pitch = 0;
      pLockedRect->pBits = nullptr;
    }

    D3D11_MAP mapType;

    if (MapFlags & (DXGI_MAP_READ | DXGI_MAP_WRITE))
      mapType = D3D11_MAP_READ_WRITE;
    else if (MapFlags & DXGI_MAP_READ)
      mapType = D3D11_MAP_READ;
    else if (MapFlags & (DXGI_MAP_WRITE | DXGI_MAP_DISCARD))
      mapType = D3D11_MAP_WRITE_DISCARD;
    else if (MapFlags & DXGI_MAP_WRITE)
      mapType = D3D11_MAP_WRITE;
    else
      return DXGI_ERROR_INVALID_CALL;

    D3D11_MAPPED_SUBRESOURCE sr;
    HRESULT hr = context->Map(m_resource, 0,
      mapType, 0, pLockedRect ? &sr : nullptr);

    if (hr != S_OK)
      return hr;

    pLockedRect->Pitch = sr.RowPitch;
    pLockedRect->pBits = reinterpret_cast<unsigned char*>(sr.pData);
    return hr;
  }

  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::Unmap() {
    Com<ID3D11Device>        device;
    Com<ID3D11DeviceContext> context;

    m_resource->GetDevice(&device);
    device->GetImmediateContext(&context);

    context->Unmap(m_resource, 0);
    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::GetDC(
          BOOL                    Discard,
          HDC*                    phdc) {
    if (!m_gdiSurface)
      return DXGI_ERROR_INVALID_CALL;

    return m_gdiSurface->Acquire(Discard, phdc);
  }

  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::ReleaseDC(
          RECT*                   pDirtyRect) {
    if (!m_gdiSurface)
      return DXGI_ERROR_INVALID_CALL;

    return m_gdiSurface->Release(pDirtyRect);
  }

  HRESULT STDMETHODCALLTYPE D3D11DXGISurface::GetResource(
          REFIID                  riid,
          void**                  ppParentResource,
          UINT*                   pSubresourceIndex) {
    HRESULT hr = m_resource->QueryInterface(riid, ppParentResource);
    if (pSubresourceIndex)
      *pSubresourceIndex = 0;
    return hr;
  }

  bool D3D11DXGISurface::isSurfaceCompatible() const {
    auto desc = m_texture->Desc();

    return desc->ArraySize == 1
        && desc->MipLevels == 1;
  }

  D3D11VkInteropSurface::D3D11VkInteropSurface(
          ID3D11Resource*     pResource,
          D3D11CommonTexture* pTexture)
  : m_resource(pResource),
    m_texture (pTexture) {

  }

  D3D11VkInteropSurface::~D3D11VkInteropSurface() {

  }

  ULONG STDMETHODCALLTYPE D3D11VkInteropSurface::AddRef() {
    return m_resource->AddRef();
  }

  ULONG STDMETHODCALLTYPE D3D11VkInteropSurface::Release() {
    return m_resource->Release();
  }

  HRESULT STDMETHODCALLTYPE D3D11VkInteropSurface::QueryInterface(
          REFIID                  riid,
          void**                  ppvObject) {
    return m_resource->QueryInterface(riid, ppvObject);
  }

  HRESULT STDMETHODCALLTYPE D3D11VkInteropSurface::GetDevice(
          IDXGIVkInteropDevice**  ppDevice) {
    Com<ID3D11Device> device;
    m_resource->GetDevice(&device);

    return device->QueryInterface(
      __uuidof(IDXGIVkInteropDevice),
      reinterpret_cast<void**>(ppDevice));
  }

  HRESULT STDMETHODCALLTYPE D3D11VkInteropSurface::GetVulkanImageInfo(
          VkImage*              pHandle,
          VkImageLayout*        pLayout,
          VkImageCreateInfo*    pInfo) {
    const Rc<DxvkImage> image = m_texture->GetImage();
    const DxvkImageCreateInfo& info = image->info();

    if (pHandle != nullptr)
      *pHandle = image->handle();

    if (pLayout != nullptr)
      *pLayout = info.layout;

    if (pInfo != nullptr) {
      // We currently don't support any extended structures
      if (pInfo->sType != VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO
       || pInfo->pNext != nullptr)
        return E_INVALIDARG;

      pInfo->flags          = 0;
      pInfo->imageType      = info.type;
      pInfo->format         = info.format;
      pInfo->extent         = info.extent;
      pInfo->mipLevels      = info.mipLevels;
      pInfo->arrayLayers    = info.numLayers;
      pInfo->samples        = info.sampleCount;
      pInfo->tiling         = info.tiling;
      pInfo->usage          = info.usage;
      pInfo->sharingMode    = VK_SHARING_MODE_EXCLUSIVE;
      pInfo->queueFamilyIndexCount = 0;
      pInfo->initialLayout  = VK_IMAGE_LAYOUT_UNDEFINED;
    }

    return S_OK;
  }

  ///////////////////////////////////////////
  //      D 3 D 1 1 T E X T U R E 1 D
  D3D11Texture1D::D3D11Texture1D(
          D3D11Device*                pDevice,
    const D3D11_COMMON_TEXTURE_DESC*  pDesc)
  : D3D11DeviceChild<ID3D11Texture1D>(pDevice),
    m_texture (this, pDevice, pDesc, D3D11_RESOURCE_DIMENSION_TEXTURE1D, 0, VK_NULL_HANDLE, nullptr),
    m_interop (this, &m_texture),
    m_surface (this, &m_texture),
    m_resource(this),
    m_d3d10   (this) {

  }

  D3D11Texture1D::~D3D11Texture1D() {

  }

  HRESULT STDMETHODCALLTYPE D3D11Texture1D::QueryInterface(REFIID riid, void** ppvObject) {
    if (ppvObject == nullptr)
      return E_POINTER;

    *ppvObject = nullptr;

    if (riid == __uuidof(IUnknown)
     || riid == __uuidof(ID3D11DeviceChild)
     || riid == __uuidof(ID3D11Resource)
     || riid == __uuidof(ID3D11Texture1D)) {
      *ppvObject = ref(this);
      return S_OK;
    }

    if (riid == __uuidof(ID3D10DeviceChild)
     || riid == __uuidof(ID3D10Resource)
     || riid == __uuidof(ID3D10Texture1D)) {
      *ppvObject = ref(&m_d3d10);
      return S_OK;
    }

    if (m_surface.isSurfaceCompatible()
     && (riid == __uuidof(IDXGISurface)
      || riid == __uuidof(IDXGISurface1)
      || riid == __uuidof(IDXGISurface2))) {
      *ppvObject = ref(&m_surface);
      return S_OK;
    }

    if (riid == __uuidof(IDXGIObject)
     || riid == __uuidof(IDXGIDeviceSubObject)
     || riid == __uuidof(IDXGIResource)
     || riid == __uuidof(IDXGIResource1)) {
       *ppvObject = ref(&m_resource);
       return S_OK;
    }

    if (riid == __uuidof(IDXGIVkInteropSurface)) {
      *ppvObject = ref(&m_interop);
      return S_OK;
    }

    Logger::warn("D3D11Texture1D::QueryInterface: Unknown interface query");
    Logger::warn(str::format(riid));
    return E_NOINTERFACE;
  }

  void STDMETHODCALLTYPE D3D11Texture1D::GetType(D3D11_RESOURCE_DIMENSION *pResourceDimension) {
    *pResourceDimension = D3D11_RESOURCE_DIMENSION_TEXTURE1D;
  }

  UINT STDMETHODCALLTYPE D3D11Texture1D::GetEvictionPriority() {
    return DXGI_RESOURCE_PRIORITY_NORMAL;
  }

  void STDMETHODCALLTYPE D3D11Texture1D::SetEvictionPriority(UINT EvictionPriority) {
    static bool s_errorShown = false;

    if (!std::exchange(s_errorShown, true))
      Logger::warn("D3D11Texture1D::SetEvictionPriority: Stub");
  }

  void STDMETHODCALLTYPE D3D11Texture1D::GetDesc(D3D11_TEXTURE1D_DESC *pDesc) {
    pDesc->Width          = m_texture.Desc()->Width;
    pDesc->MipLevels      = m_texture.Desc()->MipLevels;
    pDesc->ArraySize      = m_texture.Desc()->ArraySize;
    pDesc->Format         = m_texture.Desc()->Format;
    pDesc->Usage          = m_texture.Desc()->Usage;
    pDesc->BindFlags      = m_texture.Desc()->BindFlags;
    pDesc->CPUAccessFlags = m_texture.Desc()->CPUAccessFlags;
    pDesc->MiscFlags      = m_texture.Desc()->MiscFlags;
  }

  ///////////////////////////////////////////
  //      D 3 D 1 1 T E X T U R E 2 D
  D3D11Texture2D::D3D11Texture2D(
          D3D11Device*                pDevice,
    const D3D11_COMMON_TEXTURE_DESC*  pDesc,
          HANDLE                      hSharedHandle)
  : D3D11DeviceChild<ID3D11Texture2D1>(pDevice),
    m_texture   (this, pDevice, pDesc, D3D11_RESOURCE_DIMENSION_TEXTURE2D, 0, VK_NULL_HANDLE, hSharedHandle),
    m_interop   (this, &m_texture),
    m_surface   (this, &m_texture),
    m_resource  (this),
    m_d3d10     (this),
    m_swapChain (nullptr) {
  }

  D3D11Texture2D::D3D11Texture2D(
          D3D11Device*                pDevice,
    const D3D11_COMMON_TEXTURE_DESC*  pDesc,
          DXGI_USAGE                  DxgiUsage,
          VkImage                     vkImage)
  : D3D11DeviceChild<ID3D11Texture2D1>(pDevice),
    m_texture   (this, pDevice, pDesc, D3D11_RESOURCE_DIMENSION_TEXTURE2D, DxgiUsage, vkImage, nullptr),
    m_interop   (this, &m_texture),
    m_surface   (this, &m_texture),
    m_resource  (this),
    m_d3d10     (this),
    m_swapChain (nullptr) {

  }

  D3D11Texture2D::D3D11Texture2D(
          D3D11Device*                pDevice,
          IUnknown*                   pSwapChain,
    const D3D11_COMMON_TEXTURE_DESC*  pDesc,
          DXGI_USAGE                  DxgiUsage)
  : D3D11DeviceChild<ID3D11Texture2D1>(pDevice),
    m_texture   (this, pDevice, pDesc, D3D11_RESOURCE_DIMENSION_TEXTURE2D, DxgiUsage, VK_NULL_HANDLE, nullptr),
    m_interop   (this, &m_texture),
    m_surface   (this, &m_texture),
    m_resource  (this),
    m_d3d10     (this),
    m_swapChain (pSwapChain) {

  }

  D3D11Texture2D::~D3D11Texture2D() {

  }

  ULONG STDMETHODCALLTYPE D3D11Texture2D::AddRef() {
    uint32_t refCount = m_refCount++;

    if (unlikely(!refCount)) {
      if (m_swapChain)
        m_swapChain->AddRef();

      AddRefPrivate();
    }

    return refCount + 1;
  }

  ULONG STDMETHODCALLTYPE D3D11Texture2D::Release() {
    uint32_t refCount = --m_refCount;

    if (unlikely(!refCount)) {
      if (m_swapChain)
        m_swapChain->Release();

      ReleasePrivate();
    }

    return refCount;
  }

  HRESULT STDMETHODCALLTYPE D3D11Texture2D::QueryInterface(REFIID riid, void** ppvObject) {
    if (ppvObject == nullptr)
      return E_POINTER;

    *ppvObject = nullptr;

    if (riid == __uuidof(IUnknown)
     || riid == __uuidof(ID3D11DeviceChild)
     || riid == __uuidof(ID3D11Resource)
     || riid == __uuidof(ID3D11Texture2D)
     || riid == __uuidof(ID3D11Texture2D1)) {
      *ppvObject = ref(this);
      return S_OK;
    }

    if (riid == __uuidof(ID3D10DeviceChild)
     || riid == __uuidof(ID3D10Resource)
     || riid == __uuidof(ID3D10Texture2D)) {
      *ppvObject = ref(&m_d3d10);
      return S_OK;
    }

    if (m_surface.isSurfaceCompatible()
     && (riid == __uuidof(IDXGISurface)
      || riid == __uuidof(IDXGISurface1)
      || riid == __uuidof(IDXGISurface2))) {
      *ppvObject = ref(&m_surface);
      return S_OK;
    }

    if (riid == __uuidof(IDXGIObject)
     || riid == __uuidof(IDXGIDeviceSubObject)
     || riid == __uuidof(IDXGIResource)
     || riid == __uuidof(IDXGIResource1)) {
       *ppvObject = ref(&m_resource);
       return S_OK;
    }

    if (riid == __uuidof(IDXGIVkInteropSurface)) {
      *ppvObject = ref(&m_interop);
      return S_OK;
    }

    Logger::warn("D3D11Texture2D::QueryInterface: Unknown interface query");
    Logger::warn(str::format(riid));
    return E_NOINTERFACE;
  }

  void STDMETHODCALLTYPE D3D11Texture2D::GetType(D3D11_RESOURCE_DIMENSION *pResourceDimension) {
    *pResourceDimension = D3D11_RESOURCE_DIMENSION_TEXTURE2D;
  }

  UINT STDMETHODCALLTYPE D3D11Texture2D::GetEvictionPriority() {
    return DXGI_RESOURCE_PRIORITY_NORMAL;
  }

  void STDMETHODCALLTYPE D3D11Texture2D::SetEvictionPriority(UINT EvictionPriority) {
    static bool s_errorShown = false;

    if (!std::exchange(s_errorShown, true))
      Logger::warn("D3D11Texture2D::SetEvictionPriority: Stub");
  }

  void STDMETHODCALLTYPE D3D11Texture2D::GetDesc(D3D11_TEXTURE2D_DESC* pDesc) {
    pDesc->Width          = m_texture.Desc()->Width;
    pDesc->Height         = m_texture.Desc()->Height;
    pDesc->MipLevels      = m_texture.Desc()->MipLevels;
    pDesc->ArraySize      = m_texture.Desc()->ArraySize;
    pDesc->Format         = m_texture.Desc()->Format;
    pDesc->SampleDesc     = m_texture.Desc()->SampleDesc;
    pDesc->Usage          = m_texture.Desc()->Usage;
    pDesc->BindFlags      = m_texture.Desc()->BindFlags;
    pDesc->CPUAccessFlags = m_texture.Desc()->CPUAccessFlags;
    pDesc->MiscFlags      = m_texture.Desc()->MiscFlags;
  }

  void STDMETHODCALLTYPE D3D11Texture2D::GetDesc1(D3D11_TEXTURE2D_DESC1* pDesc) {
    pDesc->Width          = m_texture.Desc()->Width;
    pDesc->Height         = m_texture.Desc()->Height;
    pDesc->MipLevels      = m_texture.Desc()->MipLevels;
    pDesc->ArraySize      = m_texture.Desc()->ArraySize;
    pDesc->Format         = m_texture.Desc()->Format;
    pDesc->SampleDesc     = m_texture.Desc()->SampleDesc;
    pDesc->Usage          = m_texture.Desc()->Usage;
    pDesc->BindFlags      = m_texture.Desc()->BindFlags;
    pDesc->CPUAccessFlags = m_texture.Desc()->CPUAccessFlags;
    pDesc->MiscFlags      = m_texture.Desc()->MiscFlags;
    pDesc->TextureLayout  = m_texture.Desc()->TextureLayout;
  }

  ///////////////////////////////////////////
  //      D 3 D 1 1 T E X T U R E 3 D
  D3D11Texture3D::D3D11Texture3D(
          D3D11Device*                pDevice,
    const D3D11_COMMON_TEXTURE_DESC*  pDesc)
  : D3D11DeviceChild<ID3D11Texture3D1>(pDevice),
    m_texture (this, pDevice, pDesc, D3D11_RESOURCE_DIMENSION_TEXTURE3D, 0, VK_NULL_HANDLE, nullptr),
    m_interop (this, &m_texture),
    m_resource(this),
    m_d3d10   (this) {

  }

  D3D11Texture3D::~D3D11Texture3D() {

  }

  HRESULT STDMETHODCALLTYPE D3D11Texture3D::QueryInterface(REFIID riid, void** ppvObject) {
    if (ppvObject == nullptr)
      return E_POINTER;

    *ppvObject = nullptr;

    if (riid == __uuidof(IUnknown)
     || riid == __uuidof(ID3D11DeviceChild)
     || riid == __uuidof(ID3D11Resource)
     || riid == __uuidof(ID3D11Texture3D)
     || riid == __uuidof(ID3D11Texture3D1)) {
      *ppvObject = ref(this);
      return S_OK;
    }

    if (riid == __uuidof(ID3D10DeviceChild)
     || riid == __uuidof(ID3D10Resource)
     || riid == __uuidof(ID3D10Texture3D)) {
      *ppvObject = ref(&m_d3d10);
      return S_OK;
    }

    if (riid == __uuidof(IDXGIObject)
     || riid == __uuidof(IDXGIDeviceSubObject)
     || riid == __uuidof(IDXGIResource)
     || riid == __uuidof(IDXGIResource1)) {
       *ppvObject = ref(&m_resource);
       return S_OK;
    }

    if (riid == __uuidof(IDXGIVkInteropSurface)) {
      *ppvObject = ref(&m_interop);
      return S_OK;
    }

    Logger::warn("D3D11Texture3D::QueryInterface: Unknown interface query");
    Logger::warn(str::format(riid));
    return E_NOINTERFACE;
  }

  void STDMETHODCALLTYPE D3D11Texture3D::GetType(D3D11_RESOURCE_DIMENSION *pResourceDimension) {
    *pResourceDimension = D3D11_RESOURCE_DIMENSION_TEXTURE3D;
  }

  UINT STDMETHODCALLTYPE D3D11Texture3D::GetEvictionPriority() {
    return DXGI_RESOURCE_PRIORITY_NORMAL;
  }

  void STDMETHODCALLTYPE D3D11Texture3D::SetEvictionPriority(UINT EvictionPriority) {
    static bool s_errorShown = false;

    if (!std::exchange(s_errorShown, true))
      Logger::warn("D3D11Texture3D::SetEvictionPriority: Stub");
  }

  void STDMETHODCALLTYPE D3D11Texture3D::GetDesc(D3D11_TEXTURE3D_DESC* pDesc) {
    pDesc->Width          = m_texture.Desc()->Width;
    pDesc->Height         = m_texture.Desc()->Height;
    pDesc->Depth          = m_texture.Desc()->Depth;
    pDesc->MipLevels      = m_texture.Desc()->MipLevels;
    pDesc->Format         = m_texture.Desc()->Format;
    pDesc->Usage          = m_texture.Desc()->Usage;
    pDesc->BindFlags      = m_texture.Desc()->BindFlags;
    pDesc->CPUAccessFlags = m_texture.Desc()->CPUAccessFlags;
    pDesc->MiscFlags      = m_texture.Desc()->MiscFlags;
  }

  void STDMETHODCALLTYPE D3D11Texture3D::GetDesc1(D3D11_TEXTURE3D_DESC1* pDesc) {
    pDesc->Width          = m_texture.Desc()->Width;
    pDesc->Height         = m_texture.Desc()->Height;
    pDesc->Depth          = m_texture.Desc()->Depth;
    pDesc->MipLevels      = m_texture.Desc()->MipLevels;
    pDesc->Format         = m_texture.Desc()->Format;
    pDesc->Usage          = m_texture.Desc()->Usage;
    pDesc->BindFlags      = m_texture.Desc()->BindFlags;
    pDesc->CPUAccessFlags = m_texture.Desc()->CPUAccessFlags;
    pDesc->MiscFlags      = m_texture.Desc()->MiscFlags;
  }

  D3D11CommonTexture* GetCommonTexture(ID3D11Resource* pResource) {
    D3D11_RESOURCE_DIMENSION dimension = D3D11_RESOURCE_DIMENSION_UNKNOWN;
    pResource->GetType(&dimension);

    switch (dimension) {
      case D3D11_RESOURCE_DIMENSION_TEXTURE1D:
        return static_cast<D3D11Texture1D*>(pResource)->GetCommonTexture();

      case D3D11_RESOURCE_DIMENSION_TEXTURE2D:
        return static_cast<D3D11Texture2D*>(pResource)->GetCommonTexture();

      case D3D11_RESOURCE_DIMENSION_TEXTURE3D:
        return static_cast<D3D11Texture3D*>(pResource)->GetCommonTexture();

      default:
        return nullptr;
    }
  }

}


code:
#pragma once

#include "../dxvk/dxvk_cs.h"
#include "../dxvk/dxvk_device.h"

#include "../d3d10/d3d10_texture.h"

#include "d3d11_device_child.h"
#include "d3d11_interfaces.h"
#include "d3d11_resource.h"

namespace dxvk {

  class D3D11Device;
  class D3D11GDISurface;

  /**
   * \brief Image memory mapping mode
   *
   * Determines how exactly \c Map will
   * behave when mapping an image.
   */
  enum D3D11_COMMON_TEXTURE_MAP_MODE {
    D3D11_COMMON_TEXTURE_MAP_MODE_NONE,     ///< Not mapped
    D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER,   ///< Mapped through buffer
    D3D11_COMMON_TEXTURE_MAP_MODE_DIRECT,   ///< Directly mapped to host mem
    D3D11_COMMON_TEXTURE_MAP_MODE_STAGING,  ///< Buffer only, no image
  };

  /**
   * \brief Common texture description
   *
   * Contains all members that can be
   * defined for 1D, 2D and 3D textures.
   */
  struct D3D11_COMMON_TEXTURE_DESC {
    UINT             Width;
    UINT             Height;
    UINT             Depth;
    UINT             MipLevels;
    UINT             ArraySize;
    DXGI_FORMAT      Format;
    DXGI_SAMPLE_DESC SampleDesc;
    D3D11_USAGE      Usage;
    UINT             BindFlags;
    UINT             CPUAccessFlags;
    UINT             MiscFlags;
    D3D11_TEXTURE_LAYOUT TextureLayout;
  };

  /**
   * \brief Packed subresource layout
   */
  struct D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT {
    UINT64          Offset;
    UINT64          Size;
    UINT            RowPitch;
    UINT            DepthPitch;
  };

  /**
   * \brief D3D11 common texture object
   *
   * This class implements common texture methods and
   * aims to work around the issue that there are three
   * different interfaces for basically the same thing.
   */
  class D3D11CommonTexture {

  public:

    D3D11CommonTexture(
            ID3D11Resource*             pInterface,
            D3D11Device*                pDevice,
      const D3D11_COMMON_TEXTURE_DESC*  pDesc,
            D3D11_RESOURCE_DIMENSION    Dimension,
            DXGI_USAGE                  DxgiUsage,
            VkImage                     vkImage,
            HANDLE                      hSharedHandle);

    ~D3D11CommonTexture();

    /**
     * \brief Retrieves resource interface
     * \returns Resource interface
     */
    ID3D11Resource* GetInterface() const {
      return m_interface;
    }

    /**
     * \brief Texture properties
     *
     * The returned data can be used to fill in
     * \c D3D11_TEXTURE2D_DESC and similar structs.
     * \returns Pointer to texture description
     */
    const D3D11_COMMON_TEXTURE_DESC* Desc() const {
      return &m_desc;
    }

    /**
     * \brief Retrieves D3D11 texture type
     * \returns D3D11 resource dimension
     */
    D3D11_RESOURCE_DIMENSION GetDimension() const {
      return m_dimension;
    }

    /**
     * \brief Retrieves Vulkan image type
     *
     * Returns the image type based on the D3D11 resource
     * dimension. Also works if there is no actual image.
     * \returns Vulkan image type
     */
    VkImageType GetVkImageType() const {
      return GetImageTypeFromResourceDim(m_dimension);
    }

    /**
     * \brief Computes extent of a given mip level
     *
     * This also works for staging resources that have no image.
     * \param [in] Level Mip level to compute the size of
     */
    VkExtent3D MipLevelExtent(uint32_t Level) const {
      return util::computeMipLevelExtent(
        VkExtent3D { m_desc.Width, m_desc.Height, m_desc.Depth }, Level);
    }

    /**
     * \brief Special DXGI usage flags
     *
     * Flags that are set in addition to the bind
     * flags. Zero for application-created textures.
     * \returns DXGI usage flags
     */
    DXGI_USAGE GetDxgiUsage() const {
      return m_dxgiUsage;
    }

    /**
     * \brief Counts number of subresources
     * \returns Number of subresources
     */
    UINT CountSubresources() const {
      return m_desc.ArraySize * m_desc.MipLevels;
    }

    /**
     * \brief Map mode
     * \returns Map mode
     */
    D3D11_COMMON_TEXTURE_MAP_MODE GetMapMode() const {
      return m_mapMode;
    }

    /**
     * \brief Map type of a given subresource
     *
     * \param [in] Subresource Subresource index
     * \returns Current map mode of that subresource
     */
    D3D11_MAP GetMapType(UINT Subresource) const {
      return Subresource < m_mapInfo.size()
        ? D3D11_MAP(m_mapInfo[Subresource].mapType)
        : D3D11_MAP(~0u);
    }

    /**
     * \brief Sets map type for a given subresource
     *
     * \param [in] Subresource The subresource
     * \param [in] MapType The map type
     */
    void SetMapType(UINT Subresource, D3D11_MAP MapType) {
      if (Subresource < m_mapInfo.size())
        m_mapInfo[Subresource].mapType = MapType;
    }

    /**
     * \brief The DXVK image
     * \returns The DXVK image
     */
    Rc<DxvkImage> GetImage() const {
      return m_image;
    }

    /**
     * \brief Mapped subresource buffer
     *
     * \param [in] Subresource Subresource index
     * \returns Mapped subresource buffer
     */
    Rc<DxvkBuffer> GetMappedBuffer(UINT Subresource) const {
      return Subresource < m_buffers.size()
        ? m_buffers[Subresource].buffer
        : Rc<DxvkBuffer>();
    }

    /**
     * \brief Discards mapped buffer slice for a given subresource
     *
     * \param [in] Subresource Subresource to discard
     * \returns Newly allocated mapped buffer slice
     */
    DxvkBufferSliceHandle DiscardSlice(UINT Subresource) {
      if (Subresource < m_buffers.size()) {
        DxvkBufferSliceHandle slice = m_buffers[Subresource].buffer->allocSlice();
        m_buffers[Subresource].slice = slice;
        return slice;
      } else {
        return DxvkBufferSliceHandle();
      }
    }

    /**
     * \brief Retrieves mapped buffer slice for a given subresource
     *
     * \param [in] Subresource Subresource index to query
     * \returns Currently mapped buffer slice
     */
    DxvkBufferSliceHandle GetMappedSlice(UINT Subresource) const {
      return Subresource < m_buffers.size()
        ? m_buffers[Subresource].slice
        : DxvkBufferSliceHandle();
    }

    /**
     * \brief Returns underlying packed Vulkan format
     *
     * This works even for staging resources that have no image.
     * Note that for depth-stencil resources, the returned format
     * may be different from the image format on some systems.
     * \returns Packed Vulkan format
     */
    VkFormat GetPackedFormat() const {
      return m_packedFormat;
    }

    /**
     * \brief Checks whether the resource is eligible for tracking
     *
     * Mapped resources with no bind flags can be tracked so that
     * mapping them will not necessarily cause a CS thread sync.
     * \returns \c true if tracking is supported for this resource
     */
    bool HasSequenceNumber() const {
      if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_NONE)
        return false;

      // For buffer-mapped images we only need to track copies to
      // and from that buffer, so we can safely ignore bind flags
      if (m_mapMode == D3D11_COMMON_TEXTURE_MAP_MODE_BUFFER)
        return true;

      // Otherwise we can only do accurate tracking if the
      // image cannot be used in the rendering pipeline.
      return m_desc.BindFlags == 0;
    }

    /**
     * \brief Tracks sequence number for a given subresource
     *
     * Stores which CS chunk the resource was last used on.
     * \param [in] Subresource Subresource index
     * \param [in] Seq Sequence number
     */
    void TrackSequenceNumber(UINT Subresource, uint64_t Seq) {
      if (Subresource < m_mapInfo.size())
        m_mapInfo[Subresource].seq = Seq;
    }

    /**
     * \brief Queries sequence number for a given subresource
     *
     * Returns which CS chunk the resource was last used on.
     * \param [in] Subresource Subresource index
     * \returns Sequence number for the given subresource
     */
    uint64_t GetSequenceNumber(UINT Subresource) {
      if (HasSequenceNumber()) {
        return Subresource < m_buffers.size()
          ? m_mapInfo[Subresource].seq
          : 0ull;
      } else {
        return DxvkCsThread::SynchronizeAll;
      }
    }

    /**
     * \brief Computes pixel offset into mapped buffer
     *
     * \param [in] Subresource Subresource index
     * \param [in] Subresource Plane index
     * \param [in] Offset Pixel coordinate to compute offset for
     * \returns Offset into mapped subresource buffer, in pixels
     */
    VkDeviceSize ComputeMappedOffset(UINT Subresource, UINT Plane, VkOffset3D Offset) const;

    /**
     * \brief Computes subresource from the subresource index
     *
     * Used by some functions that operate on only
     * one subresource, such as \c UpdateSubresource.
     * \param [in] Aspect The image aspect
     * \param [in] Subresource Subresource index
     * \returns The Vulkan image subresource
     */
    VkImageSubresource GetSubresourceFromIndex(
            VkImageAspectFlags    Aspect,
            UINT                  Subresource) const;

    /**
     * \brief Computes subresource layout for the given subresource
     *
     * \param [in] AspectMask The image aspect
     * \param [in] Subresource Subresource index
     * \returns Memory layout of the mapped subresource
     */
    D3D11_COMMON_TEXTURE_SUBRESOURCE_LAYOUT GetSubresourceLayout(
            VkImageAspectFlags    AspectMask,
            UINT                  Subresource) const;

    /**
     * \brief Format mode
     *
     * Determines whether the image is going to
     * be used as a color image or a depth image.
     * \returns Format mode
     */
    DXGI_VK_FORMAT_MODE GetFormatMode() const;

    /**
     * \brief Computes plane count
     *
     * For non-planar formats, the plane count will be 1.
     * \returns Image plane count
     */
    uint32_t GetPlaneCount() const;

    /**
     * \brief Checks whether a view can be created for this textue
     *
     * View formats are only compatible if they are either identical
     * or from the same family of typeless formats, where the resource
     * format must be typeless and the view format must be typed. This
     * will also check whether the required bind flags are supported.
     * \param [in] BindFlags Bind flags for the view
     * \param [in] Format The desired view format
     * \param [in] Plane Plane slice for planar formats
     * \returns \c true if the format is compatible
     */
    bool CheckViewCompatibility(
            UINT                BindFlags,
            DXGI_FORMAT         Format,
            UINT                Plane) const;

    /**
     * \brief Normalizes and validates texture description
     *
     * Fills in undefined values and validates the texture
     * parameters. Any error returned by this method should
     * be forwarded to the application.
     * \param [in,out] pDesc Texture description
     * \returns \c S_OK if the parameters are valid
     */
    static HRESULT NormalizeTextureProperties(
            D3D11_COMMON_TEXTURE_DESC* pDesc);

  private:

    struct MappedBuffer {
      Rc<DxvkBuffer>        buffer;
      DxvkBufferSliceHandle slice;
    };

    struct MappedInfo {
      D3D11_MAP             mapType;
      uint64_t              seq;
    };

    ID3D11Resource*               m_interface;
    D3D11Device*                  m_device;
    D3D11_RESOURCE_DIMENSION      m_dimension;
    D3D11_COMMON_TEXTURE_DESC     m_desc;
    D3D11_COMMON_TEXTURE_MAP_MODE m_mapMode;
    DXGI_USAGE                    m_dxgiUsage;
    VkFormat                      m_packedFormat;

    Rc<DxvkImage>                 m_image;
    std::vector<MappedBuffer>     m_buffers;
    std::vector<MappedInfo>       m_mapInfo;

    MappedBuffer CreateMappedBuffer(
            UINT                  MipLevel) const;

    BOOL CheckImageSupport(
      const DxvkImageCreateInfo*  pImageInfo,
            VkImageTiling         Tiling) const;

    BOOL CheckFormatFeatureSupport(
            VkFormat              Format,
            VkFormatFeatureFlags  Features) const;

    VkImageUsageFlags EnableMetaCopyUsage(
            VkFormat              Format,
            VkImageTiling         Tiling) const;

    VkImageUsageFlags EnableMetaPackUsage(
            VkFormat              Format,
            UINT                  CpuAccess) const;

    VkMemoryPropertyFlags GetMemoryFlags() const;

    D3D11_COMMON_TEXTURE_MAP_MODE DetermineMapMode(
      const DxvkImageCreateInfo*  pImageInfo) const;

    void ExportImageInfo();

    static BOOL IsR32UavCompatibleFormat(
            DXGI_FORMAT           Format);

    static VkImageType GetImageTypeFromResourceDim(
            D3D11_RESOURCE_DIMENSION  Dimension);

    static VkImageLayout OptimizeLayout(
            VkImageUsageFlags         Usage);
  };

  /**
   * \brief IDXGISurface implementation for D3D11 textures
   *
   * Provides an implementation for 2D textures that
   * have only one array layer and one mip level.
   */
  class D3D11DXGISurface : public IDXGISurface2 {

  public:

    D3D11DXGISurface(
            ID3D11Resource*     pResource,
            D3D11CommonTexture* pTexture);

    ~D3D11DXGISurface();

    ULONG STDMETHODCALLTYPE AddRef();

    ULONG STDMETHODCALLTYPE Release();

    HRESULT STDMETHODCALLTYPE QueryInterface(
            REFIID                  riid,
            void**                  ppvObject);

    HRESULT STDMETHODCALLTYPE GetPrivateData(
            REFGUID                 Name,
            UINT*                   pDataSize,
            void*                   pData);

    HRESULT STDMETHODCALLTYPE SetPrivateData(
            REFGUID                 Name,
            UINT                    DataSize,
      const void*                   pData);

    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(
            REFGUID                 Name,
      const IUnknown*               pUnknown);

    HRESULT STDMETHODCALLTYPE GetParent(
            REFIID                  riid,
            void**                  ppParent);

    HRESULT STDMETHODCALLTYPE GetDevice(
            REFIID                  riid,
            void**                  ppDevice);

    HRESULT STDMETHODCALLTYPE GetDesc(
            DXGI_SURFACE_DESC*      pDesc);

    HRESULT STDMETHODCALLTYPE Map(
            DXGI_MAPPED_RECT*       pLockedRect,
            UINT                    MapFlags);

    HRESULT STDMETHODCALLTYPE Unmap();

    HRESULT STDMETHODCALLTYPE GetDC(
            BOOL                    Discard,
            HDC*                    phdc);

    HRESULT STDMETHODCALLTYPE ReleaseDC(
            RECT*                   pDirtyRect);

    HRESULT STDMETHODCALLTYPE GetResource(
            REFIID                  riid,
            void**                  ppParentResource,
            UINT*                   pSubresourceIndex);

    bool isSurfaceCompatible() const;

  private:

    ID3D11Resource*     m_resource;
    D3D11CommonTexture* m_texture;
    D3D11GDISurface*    m_gdiSurface;

  };

  /**
   * \brief Common texture interop class
   *
   * Provides access to the underlying Vulkan
   * texture to external Vulkan libraries.
   */
  class D3D11VkInteropSurface : public IDXGIVkInteropSurface {

  public:

    D3D11VkInteropSurface(
            ID3D11Resource*     pResource,
            D3D11CommonTexture* pTexture);

    ~D3D11VkInteropSurface();

    ULONG STDMETHODCALLTYPE AddRef();

    ULONG STDMETHODCALLTYPE Release();

    HRESULT STDMETHODCALLTYPE QueryInterface(
            REFIID                  riid,
            void**                  ppvObject);

    HRESULT STDMETHODCALLTYPE GetDevice(
            IDXGIVkInteropDevice**  ppDevice);

    HRESULT STDMETHODCALLTYPE GetVulkanImageInfo(
            VkImage*              pHandle,
            VkImageLayout*        pLayout,
            VkImageCreateInfo*    pInfo);

  private:

    ID3D11Resource*     m_resource;
    D3D11CommonTexture* m_texture;

  };

  ///////////////////////////////////////////
  //      D 3 D 1 1 T E X T U R E 1 D
  class D3D11Texture1D : public D3D11DeviceChild<ID3D11Texture1D> {

  public:

    D3D11Texture1D(
            D3D11Device*                pDevice,
      const D3D11_COMMON_TEXTURE_DESC*  pDesc);

    ~D3D11Texture1D();

    HRESULT STDMETHODCALLTYPE QueryInterface(
            REFIID  riid,
            void**  ppvObject) final;

    void STDMETHODCALLTYPE GetType(
            D3D11_RESOURCE_DIMENSION *pResourceDimension) final;

    UINT STDMETHODCALLTYPE GetEvictionPriority() final;

    void STDMETHODCALLTYPE SetEvictionPriority(UINT EvictionPriority) final;

    void STDMETHODCALLTYPE GetDesc(
            D3D11_TEXTURE1D_DESC *pDesc) final;

    D3D11CommonTexture* GetCommonTexture() {
      return &m_texture;
    }

    D3D10Texture1D* GetD3D10Iface() {
      return &m_d3d10;
    }

  private:

    D3D11CommonTexture    m_texture;
    D3D11VkInteropSurface m_interop;
    D3D11DXGISurface      m_surface;
    D3D11DXGIResource     m_resource;
    D3D10Texture1D        m_d3d10;

  };

  ///////////////////////////////////////////
  //      D 3 D 1 1 T E X T U R E 2 D
  class D3D11Texture2D : public D3D11DeviceChild<ID3D11Texture2D1> {

  public:

    D3D11Texture2D(
            D3D11Device*                pDevice,
      const D3D11_COMMON_TEXTURE_DESC*  pDesc,
            HANDLE                      hSharedHandle);

    D3D11Texture2D(
            D3D11Device*                pDevice,
      const D3D11_COMMON_TEXTURE_DESC*  pDesc,
            DXGI_USAGE                  DxgiUsage,
            VkImage                     vkImage);

    D3D11Texture2D(
            D3D11Device*                pDevice,
            IUnknown*                   pSwapChain,
      const D3D11_COMMON_TEXTURE_DESC*  pDesc,
            DXGI_USAGE                  DxgiUsage);

    ~D3D11Texture2D();

    ULONG STDMETHODCALLTYPE AddRef();

    ULONG STDMETHODCALLTYPE Release();

    HRESULT STDMETHODCALLTYPE QueryInterface(
            REFIID  riid,
            void**  ppvObject) final;

    void STDMETHODCALLTYPE GetType(
            D3D11_RESOURCE_DIMENSION *pResourceDimension) final;

    UINT STDMETHODCALLTYPE GetEvictionPriority() final;

    void STDMETHODCALLTYPE SetEvictionPriority(UINT EvictionPriority) final;

    void STDMETHODCALLTYPE GetDesc(
            D3D11_TEXTURE2D_DESC* pDesc) final;

    void STDMETHODCALLTYPE GetDesc1(
            D3D11_TEXTURE2D_DESC1* pDesc) final;

    D3D11CommonTexture* GetCommonTexture() {
      return &m_texture;
    }

    D3D10Texture2D* GetD3D10Iface() {
      return &m_d3d10;
    }

  private:

    D3D11CommonTexture    m_texture;
    D3D11VkInteropSurface m_interop;
    D3D11DXGISurface      m_surface;
    D3D11DXGIResource     m_resource;
    D3D10Texture2D        m_d3d10;
    IUnknown*             m_swapChain;

  };

  ///////////////////////////////////////////
  //      D 3 D 1 1 T E X T U R E 3 D
  class D3D11Texture3D : public D3D11DeviceChild<ID3D11Texture3D1> {

  public:

    D3D11Texture3D(
            D3D11Device*                pDevice,
      const D3D11_COMMON_TEXTURE_DESC*  pDesc);

    ~D3D11Texture3D();

    HRESULT STDMETHODCALLTYPE QueryInterface(
            REFIID  riid,
            void**  ppvObject) final;

    void STDMETHODCALLTYPE GetType(
            D3D11_RESOURCE_DIMENSION *pResourceDimension) final;

    UINT STDMETHODCALLTYPE GetEvictionPriority() final;

    void STDMETHODCALLTYPE SetEvictionPriority(UINT EvictionPriority) final;

    void STDMETHODCALLTYPE GetDesc(
            D3D11_TEXTURE3D_DESC* pDesc) final;

    void STDMETHODCALLTYPE GetDesc1(
            D3D11_TEXTURE3D_DESC1* pDesc) final;

    D3D11CommonTexture* GetCommonTexture() {
      return &m_texture;
    }

    D3D10Texture3D* GetD3D10Iface() {
      return &m_d3d10;
    }

  private:

    D3D11CommonTexture    m_texture;
    D3D11VkInteropSurface m_interop;
    D3D11DXGIResource     m_resource;
    D3D10Texture3D        m_d3d10;

  };

  /**
   * \brief Retrieves texture from resource pointer
   *
   * \param [in] pResource The resource to query
   * \returns Pointer to texture info, or \c nullptr
   */
  D3D11CommonTexture* GetCommonTexture(
          ID3D11Resource*       pResource);

}

repo_name: orochi/dxvk


code:
#include "d3d11_util.h"

namespace dxvk {

  HRESULT DecodeSampleCount(UINT Count, VkSampleCountFlagBits* pCount) {
    VkSampleCountFlagBits flag;

    switch (Count) {
      case  1: flag = VK_SAMPLE_COUNT_1_BIT;  break;
      case  2: flag = VK_SAMPLE_COUNT_2_BIT;  break;
      case  4: flag = VK_SAMPLE_COUNT_4_BIT;  break;
      case  8: flag = VK_SAMPLE_COUNT_8_BIT;  break;
      case 16: flag = VK_SAMPLE_COUNT_16_BIT; break;
      default: return E_INVALIDARG;
    }

    if (pCount != nullptr) {
      *pCount = flag;
      return S_OK;
    } return S_FALSE;
  }

  VkSamplerAddressMode DecodeAddressMode(
          D3D11_TEXTURE_ADDRESS_MODE  mode) {
    switch (mode) {
      case D3D11_TEXTURE_ADDRESS_WRAP:
        return VK_SAMPLER_ADDRESS_MODE_REPEAT;

      case D3D11_TEXTURE_ADDRESS_MIRROR:
        return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;

      case D3D11_TEXTURE_ADDRESS_CLAMP:
        return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;

      case D3D11_TEXTURE_ADDRESS_BORDER:
        return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER;

      case D3D11_TEXTURE_ADDRESS_MIRROR_ONCE:
        return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;

      default:
        Logger::err(str::format("D3D11: Unsupported address mode: ", mode));
        return VK_SAMPLER_ADDRESS_MODE_REPEAT;
    }
  }

  VkCompareOp DecodeCompareOp(D3D11_COMPARISON_FUNC Mode) {
    switch (Mode) {
      case D3D11_COMPARISON_NEVER:          return VK_COMPARE_OP_NEVER;
      case D3D11_COMPARISON_LESS:           return VK_COMPARE_OP_LESS;
      case D3D11_COMPARISON_EQUAL:          return VK_COMPARE_OP_EQUAL;
      case D3D11_COMPARISON_LESS_EQUAL:     return VK_COMPARE_OP_LESS_OR_EQUAL;
      case D3D11_COMPARISON_GREATER:        return VK_COMPARE_OP_GREATER;
      case D3D11_COMPARISON_NOT_EQUAL:      return VK_COMPARE_OP_NOT_EQUAL;
      case D3D11_COMPARISON_GREATER_EQUAL:  return VK_COMPARE_OP_GREATER_OR_EQUAL;
      case D3D11_COMPARISON_ALWAYS:         return VK_COMPARE_OP_ALWAYS;
    }

    if (Mode != 0)  // prevent log spamming when apps use ZeroMemory
      Logger::err(str::format("D3D11: Unsupported compare op: ", Mode));
    return VK_COMPARE_OP_NEVER;
  }

  VkConservativeRasterizationModeEXT DecodeConservativeRasterizationMode(
          D3D11_CONSERVATIVE_RASTERIZATION_MODE Mode) {
    switch (Mode) {
      case D3D11_CONSERVATIVE_RASTERIZATION_MODE_OFF:
        return VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT;
      case D3D11_CONSERVATIVE_RASTERIZATION_MODE_ON:
        return VK_CONSERVATIVE_RASTERIZATION_MODE_OVERESTIMATE_EXT;
    }

    Logger::err(str::format("D3D11: Unsupported conservative raster mode: ", Mode));
    return VK_CONSERVATIVE_RASTERIZATION_MODE_DISABLED_EXT;
  }

  VkShaderStageFlagBits GetShaderStage(DxbcProgramType ProgramType) {
    switch (ProgramType) {
      case DxbcProgramType::VertexShader:   return VK_SHADER_STAGE_VERTEX_BIT;
      case DxbcProgramType::HullShader:     return VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
      case DxbcProgramType::DomainShader:   return VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
      case DxbcProgramType::GeometryShader: return VK_SHADER_STAGE_GEOMETRY_BIT;
      case DxbcProgramType::PixelShader:    return VK_SHADER_STAGE_FRAGMENT_BIT;
      case DxbcProgramType::ComputeShader:  return VK_SHADER_STAGE_COMPUTE_BIT;
      default:                              return VkShaderStageFlagBits(0);
    }
  }

  VkFormatFeatureFlags GetBufferFormatFeatures(UINT BindFlags) {
    VkFormatFeatureFlags features = 0;

    if (BindFlags & D3D11_BIND_SHADER_RESOURCE)
      features |= VK_FORMAT_FEATURE_UNIFORM_TEXEL_BUFFER_BIT;
    if (BindFlags & D3D11_BIND_UNORDERED_ACCESS)
      features |= VK_FORMAT_FEATURE_STORAGE_TEXEL_BUFFER_BIT;

    return features;
  }

  VkFormatFeatureFlags GetImageFormatFeatures(UINT BindFlags) {
    VkFormatFeatureFlags features = 0;

    if (BindFlags & D3D11_BIND_DEPTH_STENCIL)
      features |= VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
    if (BindFlags & D3D11_BIND_RENDER_TARGET)
      features |= VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
    if (BindFlags & D3D11_BIND_SHADER_RESOURCE)
      features |= VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
    if (BindFlags & D3D11_BIND_UNORDERED_ACCESS)
      features |= VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT;

    return features;
  }

  VkFormat GetPackedDepthStencilFormat(DXGI_FORMAT Format) {
    switch (Format) {
      case DXGI_FORMAT_R24G8_TYPELESS:
      case DXGI_FORMAT_R24_UNORM_X8_TYPELESS:
      case DXGI_FORMAT_X24_TYPELESS_G8_UINT:
      case DXGI_FORMAT_D24_UNORM_S8_UINT:
        return VK_FORMAT_D24_UNORM_S8_UINT;

      case DXGI_FORMAT_R32G8X24_TYPELESS:
      case DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS:
      case DXGI_FORMAT_X32_TYPELESS_G8X24_UINT:
      case DXGI_FORMAT_D32_FLOAT_S8X24_UINT:
        return VK_FORMAT_D32_SFLOAT_S8_UINT;

      default:
        return VK_FORMAT_UNDEFINED;
    }
  }

}


code:
#pragma once

#include "../dxvk/dxvk_device.h"

#include "../dxbc/dxbc_util.h"

#include "d3d11_include.h"

namespace dxvk {

  template<typename T>
  UINT CompactSparseList(T* pData, UINT Mask) {
    uint32_t count = 0;

    for (uint32_t id : bit::BitMask(Mask))
      pData[count++] = pData[id];

    return count;
  }

  HRESULT DecodeSampleCount(
          UINT                      Count,
          VkSampleCountFlagBits*    pCount);

  VkSamplerAddressMode DecodeAddressMode(
          D3D11_TEXTURE_ADDRESS_MODE  mode);

  VkCompareOp DecodeCompareOp(
          D3D11_COMPARISON_FUNC     Mode);

  VkConservativeRasterizationModeEXT DecodeConservativeRasterizationMode(
          D3D11_CONSERVATIVE_RASTERIZATION_MODE Mode);

  VkShaderStageFlagBits GetShaderStage(
          DxbcProgramType           ProgramType);

  VkFormatFeatureFlags GetBufferFormatFeatures(
          UINT                      BindFlags);

  VkFormatFeatureFlags GetImageFormatFeatures(
          UINT                      BindFlags);

  VkFormat GetPackedDepthStencilFormat(
          DXGI_FORMAT               Format);

}