code:
#pragma once

#include "d3d9_include.h"

namespace dxvk::caps {

  constexpr uint32_t MaxClipPlanes                = 6;
  constexpr uint32_t MaxSamplers                  = 16;
  constexpr uint32_t MaxStreams                   = 16;
  constexpr uint32_t MaxSimultaneousTextures      = 8;
  constexpr uint32_t MaxTextureBlendStages        = MaxSimultaneousTextures;
  constexpr uint32_t MaxSimultaneousRenderTargets = D3D_MAX_SIMULTANEOUS_RENDERTARGETS;

  constexpr uint32_t MaxFloatConstantsVS          = 256;
  constexpr uint32_t MaxFloatConstantsPS          = 224;
  constexpr uint32_t MaxOtherConstants            = 16;
  constexpr uint32_t MaxFloatConstantsSoftware    = 8192;
  constexpr uint32_t MaxOtherConstantsSoftware    = 2048;

  constexpr uint32_t InputRegisterCount           = 16;

  constexpr uint32_t MaxTextureDimension          = 16384;
  constexpr uint32_t MaxMipLevels                 = 15;
  constexpr uint32_t MaxSubresources              = 15 * 6;

  constexpr uint32_t MaxTransforms                = 10 + 256;

  constexpr uint32_t TextureStageCount           = MaxSimultaneousTextures;

  constexpr uint32_t MaxEnabledLights             = 8;

  constexpr uint32_t MaxTexturesVS                = 4;
  constexpr uint32_t MaxTexturesPS                = 16;

}


code:
#include "d3d9_common_buffer.h"

#include "d3d9_device.h"
#include "d3d9_util.h"

namespace dxvk {

  D3D9CommonBuffer::D3D9CommonBuffer(
          D3D9DeviceEx*      pDevice,
    const D3D9_BUFFER_DESC*  pDesc)
    : m_parent ( pDevice ), m_desc ( *pDesc ),
      m_mapMode(DetermineMapMode(pDevice->GetOptions())) {
    m_buffer = CreateBuffer();
    if (m_mapMode == D3D9_COMMON_BUFFER_MAP_MODE_BUFFER)
      m_stagingBuffer = CreateStagingBuffer();

    m_sliceHandle = GetMapBuffer()->getSliceHandle();

    if (m_desc.Pool != D3DPOOL_DEFAULT)
      m_dirtyRange = D3D9Range(0, m_desc.Size);
  }

  HRESULT D3D9CommonBuffer::Lock(
          UINT   OffsetToLock,
          UINT   SizeToLock,
          void** ppbData,
          DWORD  Flags) {
    return m_parent->LockBuffer(
      this,
      OffsetToLock,
      SizeToLock,
      ppbData,
      Flags);
  }

  HRESULT D3D9CommonBuffer::Unlock() {
    return m_parent->UnlockBuffer(this);
  }

  HRESULT D3D9CommonBuffer::ValidateBufferProperties(const D3D9_BUFFER_DESC* pDesc) {
    if (pDesc->Size == 0)
      return D3DERR_INVALIDCALL;

    return D3D_OK;
  }

  void D3D9CommonBuffer::PreLoad() {
    if (IsPoolManaged(m_desc.Pool)) {
      auto lock = m_parent->LockDevice();

      if (NeedsUpload())
        m_parent->FlushBuffer(this);
    }
  }

  Rc<DxvkBuffer> D3D9CommonBuffer::CreateBuffer() const {
    DxvkBufferCreateInfo  info;
    info.size   = m_desc.Size;
    info.usage  = 0;
    info.stages = 0;
    info.access = 0;

    VkMemoryPropertyFlags memoryFlags = 0;

    if (m_desc.Type == D3DRTYPE_VERTEXBUFFER) {
      info.usage  |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
      info.stages |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
      info.access |= VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT;

      if (m_parent->SupportsSWVP()) {
        info.usage  |= VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
        info.stages |= VK_PIPELINE_STAGE_GEOMETRY_SHADER_BIT;
        info.access |= VK_ACCESS_SHADER_WRITE_BIT;
      }
    }
    else if (m_desc.Type == D3DRTYPE_INDEXBUFFER) {
      info.usage  |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
      info.stages |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
      info.access |= VK_ACCESS_INDEX_READ_BIT;
    }

    if (m_mapMode == D3D9_COMMON_BUFFER_MAP_MODE_DIRECT) {
      info.stages |= VK_PIPELINE_STAGE_HOST_BIT;
      info.access |= VK_ACCESS_HOST_WRITE_BIT;

      if (!(m_desc.Usage & D3DUSAGE_WRITEONLY))
        info.access |= VK_ACCESS_HOST_READ_BIT;

      memoryFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
                  |  VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
                  |  VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
    }
    else {
      info.stages |= VK_PIPELINE_STAGE_TRANSFER_BIT;
      info.usage  |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
      info.access |= VK_ACCESS_TRANSFER_WRITE_BIT;

      memoryFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
    }

    if (memoryFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT && m_parent->GetOptions()->apitraceMode) {
      memoryFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
                  |  VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
    }

    return m_parent->GetDXVKDevice()->createBuffer(info, memoryFlags);
  }

  Rc<DxvkBuffer> D3D9CommonBuffer::CreateStagingBuffer() const {
    DxvkBufferCreateInfo  info;
    info.size   = m_desc.Size;
    info.stages = VK_PIPELINE_STAGE_HOST_BIT
                | VK_PIPELINE_STAGE_TRANSFER_BIT;

    info.usage  = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;

    info.access = VK_ACCESS_HOST_WRITE_BIT
                | VK_ACCESS_TRANSFER_READ_BIT;

    if (!(m_desc.Usage & D3DUSAGE_WRITEONLY))
      info.access |= VK_ACCESS_HOST_READ_BIT;

    VkMemoryPropertyFlags memoryFlags =
      VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
    | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
    | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;

    return m_parent->GetDXVKDevice()->createBuffer(info, memoryFlags);
  }

}

repo_name: orochi/dxvk


code:
#pragma once

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

#include "d3d9_device_child.h"
#include "d3d9_format.h"

namespace dxvk {

  /**
   * \brief Buffer map mode
   */
  enum D3D9_COMMON_BUFFER_MAP_MODE {
    D3D9_COMMON_BUFFER_MAP_MODE_BUFFER,
    D3D9_COMMON_BUFFER_MAP_MODE_DIRECT
  };

  /**
   * \brief Common buffer descriptor
   */
  struct D3D9_BUFFER_DESC {
    D3DRESOURCETYPE Type;
    UINT            Size;
    DWORD           Usage;
    D3D9Format      Format;
    D3DPOOL         Pool;
    DWORD           FVF;
  };

  /**
   * \brief The type of buffer you want to use
   */
  enum D3D9_COMMON_BUFFER_TYPE {
    D3D9_COMMON_BUFFER_TYPE_MAPPING,
    D3D9_COMMON_BUFFER_TYPE_STAGING,
    D3D9_COMMON_BUFFER_TYPE_REAL
  };

  struct D3D9Range {
    D3D9Range() { Clear(); }

    D3D9Range(uint32_t min, uint32_t max)
    : min(min),
      max(max) {

    }

    inline bool IsDegenerate() { return min == max; }

    inline void Conjoin(D3D9Range range) {
      if (IsDegenerate())
        *this = range;
      else {
        min = std::min(range.min, min);
        max = std::max(range.max, max);
      }
    }

    inline bool Overlaps(D3D9Range range) {
      if (IsDegenerate())
        return false;

      return range.max > min && range.min < max;
    }

    inline void Clear() { min = 0; max = 0; }

    uint32_t min = 0;
    uint32_t max = 0;
  };

  class D3D9CommonBuffer {
    static constexpr VkDeviceSize BufferSliceAlignment = 64;
  public:

    D3D9CommonBuffer(
            D3D9DeviceEx*      pDevice,
      const D3D9_BUFFER_DESC*  pDesc);

    HRESULT Lock(
            UINT   OffsetToLock,
            UINT   SizeToLock,
            void** ppbData,
            DWORD  Flags);

    HRESULT Unlock();

    /**
    * \brief Determine the mapping mode of the buffer, (ie. direct mapping or backed)
    */
    inline D3D9_COMMON_BUFFER_MAP_MODE DetermineMapMode(const D3D9Options* options) const {
      return (m_desc.Pool == D3DPOOL_DEFAULT && (m_desc.Usage & (D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY)) && options->allowDirectBufferMapping)
        ? D3D9_COMMON_BUFFER_MAP_MODE_DIRECT
        : D3D9_COMMON_BUFFER_MAP_MODE_BUFFER;
    }

    /**
    * \brief Get the mapping mode of the buffer, (ie. direct mapping or backed)
    */
    inline D3D9_COMMON_BUFFER_MAP_MODE GetMapMode() const {
      return m_mapMode;
    }

    /**
    * \brief Abstraction for getting a type of buffer (mapping/staging/the real buffer) across mapping modes.
    */
    template <D3D9_COMMON_BUFFER_TYPE Type>
    inline const Rc<DxvkBuffer>& GetBuffer() const {
      if constexpr (Type == D3D9_COMMON_BUFFER_TYPE_MAPPING)
        return GetMapBuffer();
      else if constexpr (Type == D3D9_COMMON_BUFFER_TYPE_STAGING)
        return GetStagingBuffer();
      else //if constexpr (Type == D3D9_COMMON_BUFFER_TYPE_REAL)
        return GetRealBuffer();
    }

    template <D3D9_COMMON_BUFFER_TYPE Type>
    inline DxvkBufferSlice GetBufferSlice() const {
      return GetBufferSlice<Type>(0, m_desc.Size);
    }

    template <D3D9_COMMON_BUFFER_TYPE Type>
    inline DxvkBufferSlice GetBufferSlice(VkDeviceSize offset) const {
      return GetBufferSlice<Type>(offset, m_desc.Size - offset);
    }

    template <D3D9_COMMON_BUFFER_TYPE Type>
    inline DxvkBufferSlice GetBufferSlice(VkDeviceSize offset, VkDeviceSize length) const {
      return DxvkBufferSlice(GetBuffer<Type>(), offset, length);
    }

    inline DxvkBufferSliceHandle AllocMapSlice() {
      return GetMapBuffer()->allocSlice();
    }

    inline DxvkBufferSliceHandle DiscardMapSlice() {
      m_sliceHandle = GetMapBuffer()->allocSlice();
      return m_sliceHandle;
    }

    inline DxvkBufferSliceHandle GetMappedSlice() const {
      return m_sliceHandle;
    }

    inline DWORD GetMapFlags() const      { return m_mapFlags; }
    inline void SetMapFlags(DWORD Flags)  { m_mapFlags = Flags; }

    inline const D3D9_BUFFER_DESC* Desc() const { return &m_desc; }

    static HRESULT ValidateBufferProperties(const D3D9_BUFFER_DESC* pDesc);

    /**
     * \brief The range of the buffer that was changed using Lock calls
     */
    inline D3D9Range& DirtyRange()  { return m_dirtyRange; }

    /**
     * \brief The range of the buffer that might currently be read by the GPU
     */
    inline D3D9Range& GPUReadingRange() { return m_gpuReadingRange; }

    /**
    * \brief Whether or not the buffer was written to by the GPU (in IDirect3DDevice9::ProcessVertices)
    */
    inline bool NeedsReadback() const     { return m_needsReadback; }

    /**
    * \brief Sets whether or not the buffer was written to by the GPU
    */
    inline void SetNeedsReadback(bool state) { m_needsReadback = state; }

    inline uint32_t IncrementLockCount() { return ++m_lockCount; }
    inline uint32_t DecrementLockCount() {
      if (m_lockCount == 0)
        return 0;

      return --m_lockCount;
    }
    inline uint32_t GetLockCount() const { return m_lockCount; }

    /**
     * \brief Whether or not the staging buffer needs to be copied to the actual buffer
     */
    inline bool NeedsUpload() { return m_desc.Pool != D3DPOOL_DEFAULT && !m_dirtyRange.IsDegenerate(); }

    inline bool DoesStagingBufferUploads() const { return m_uploadUsingStaging; }

    inline void EnableStagingBufferUploads() {
      if (GetMapMode() != D3D9_COMMON_BUFFER_MAP_MODE_BUFFER)
        return;

      m_uploadUsingStaging = true;
    }

    void PreLoad();

    bool HasSequenceNumber() const {
      return m_mapMode != D3D9_COMMON_BUFFER_MAP_MODE_DIRECT;
    }

     /**
     * \brief Tracks sequence number
     *
     * Stores which CS chunk the resource was last used on.
     * \param [in] Seq Sequence number
     */
    void TrackMappingBufferSequenceNumber(uint64_t Seq) {
      m_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 GetMappingBufferSequenceNumber() const {
      return HasSequenceNumber() ? m_seq
        : DxvkCsThread::SynchronizeAll;
    }

  private:

    Rc<DxvkBuffer> CreateBuffer() const;
    Rc<DxvkBuffer> CreateStagingBuffer() const;

    inline const Rc<DxvkBuffer>& GetMapBuffer() const {
      return m_stagingBuffer != nullptr ? m_stagingBuffer : m_buffer;
    }

    inline const Rc<DxvkBuffer>& GetStagingBuffer() const {
      return m_stagingBuffer;
    }

    inline const Rc<DxvkBuffer>& GetRealBuffer() const {
      return m_buffer;
    }

    D3D9DeviceEx*               m_parent;
    const D3D9_BUFFER_DESC      m_desc;
    DWORD                       m_mapFlags;
    bool                        m_needsReadback = false;
    bool                        m_uploadUsingStaging = false;
    D3D9_COMMON_BUFFER_MAP_MODE m_mapMode;

    Rc<DxvkBuffer>              m_buffer;
    Rc<DxvkBuffer>              m_stagingBuffer;

    DxvkBufferSliceHandle       m_sliceHandle;

    D3D9Range                   m_dirtyRange;
    D3D9Range                   m_gpuReadingRange;

    uint32_t                    m_lockCount = 0;

    uint64_t                    m_seq = 0ull;

  };

}


code:
#include "d3d9_common_texture.h"

#include "d3d9_util.h"
#include "d3d9_device.h"

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

#include <algorithm>

namespace dxvk {

  D3D9CommonTexture::D3D9CommonTexture(
          D3D9DeviceEx*             pDevice,
    const D3D9_COMMON_TEXTURE_DESC* pDesc,
          D3DRESOURCETYPE           ResourceType,
          HANDLE*                   pSharedHandle)
    : m_device(pDevice), m_desc(*pDesc), m_type(ResourceType) {
    if (m_desc.Format == D3D9Format::Unknown)
      m_desc.Format = (m_desc.Usage & D3DUSAGE_DEPTHSTENCIL)
                    ? D3D9Format::D32
                    : D3D9Format::X8R8G8B8;

    for (uint32_t i = 0; i < m_dirtyBoxes.size(); i++) {
      AddDirtyBox(nullptr, i);
    }

    if (m_desc.Pool != D3DPOOL_DEFAULT) {
      const uint32_t subresources = CountSubresources();
      for (uint32_t i = 0; i < subresources; i++) {
        SetNeedsUpload(i, true);
      }
      if (pSharedHandle) {
        throw DxvkError("D3D9: Incompatible pool type for texture sharing.");
      }
    }

    m_mapping = pDevice->LookupFormat(m_desc.Format);

    m_mapMode        = DetermineMapMode();
    m_shadow         = DetermineShadowState();
    m_supportsFetch4 = DetermineFetch4Compatibility();

    if (m_mapMode == D3D9_COMMON_TEXTURE_MAP_MODE_BACKED) {
      bool plainSurface = m_type == D3DRTYPE_SURFACE &&
                          !(m_desc.Usage & (D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL));

      try {
        m_image = CreatePrimaryImage(ResourceType, plainSurface, pSharedHandle);
      }
      catch (const DxvkError& e) {
        // D3DUSAGE_AUTOGENMIPMAP and offscreen plain is mutually exclusive
        // so we can combine their retry this way.
        if (m_desc.Usage & D3DUSAGE_AUTOGENMIPMAP || plainSurface) {
          m_desc.Usage &= ~D3DUSAGE_AUTOGENMIPMAP;
          m_desc.MipLevels = 1;
          m_image = CreatePrimaryImage(ResourceType, false, pSharedHandle);
        }
        else
          throw e;
      }

      if (pSharedHandle && *pSharedHandle == nullptr) {
        *pSharedHandle = m_image->sharedHandle();
        ExportImageInfo();
      }

      CreateSampleView(0);

      if (!IsManaged()) {
        m_size = m_image->memSize();
        if (!m_device->ChangeReportedMemory(-m_size))
          throw DxvkError("D3D9: Reporting out of memory from tracking.");
      }
    }

    if (m_mapMode == D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM)
      CreateBuffers();

    m_exposedMipLevels = m_desc.MipLevels;

    if (m_desc.Usage & D3DUSAGE_AUTOGENMIPMAP)
      m_exposedMipLevels = 1;
  }

  D3D9CommonTexture::~D3D9CommonTexture() {
    if (m_size != 0)
      m_device->ChangeReportedMemory(m_size);
  }

  VkImageSubresource D3D9CommonTexture::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;
  }

  HRESULT D3D9CommonTexture::NormalizeTextureProperties(
          D3D9DeviceEx*             pDevice,
          D3D9_COMMON_TEXTURE_DESC* pDesc) {
    auto* options = pDevice->GetOptions();

    //////////////////////
    // Mapping Validation
    auto mapping = pDevice->LookupFormat(pDesc->Format);

    // Handle DisableA8RT hack for The Sims 2
    if (pDesc->Format == D3D9Format::A8       &&
       (pDesc->Usage & D3DUSAGE_RENDERTARGET) &&
        options->disableA8RT)
      return D3DERR_INVALIDCALL;

    // If the mapping is invalid then lets return invalid
    // Some edge cases:
    // NULL format does not map to anything, but should succeed
    // SCRATCH textures can still be made if the device does not support
    // the format at all.

    if (!mapping.IsValid() && pDesc->Format != D3D9Format::NULL_FORMAT) {
      auto info = pDevice->UnsupportedFormatInfo(pDesc->Format);

      if (pDesc->Pool != D3DPOOL_SCRATCH || info->elementSize == 0)
        return D3DERR_INVALIDCALL;
    }

    ///////////////////
    // Desc Validation

    if (pDesc->Width == 0 || pDesc->Height == 0 || pDesc->Depth == 0)
      return D3DERR_INVALIDCALL;

    if (FAILED(DecodeMultiSampleType(pDevice->GetDXVKDevice(), pDesc->MultiSample, pDesc->MultisampleQuality, nullptr)))
      return D3DERR_INVALIDCALL;

    // Using MANAGED pool with DYNAMIC usage is illegal
    if (IsPoolManaged(pDesc->Pool) && (pDesc->Usage & D3DUSAGE_DYNAMIC))
      return D3DERR_INVALIDCALL;

    // D3DUSAGE_WRITEONLY doesn't apply to textures.
    if (pDesc->Usage & D3DUSAGE_WRITEONLY)
      return D3DERR_INVALIDCALL;

    // RENDERTARGET and DEPTHSTENCIL must be default pool
    constexpr DWORD incompatibleUsages = D3DUSAGE_RENDERTARGET | D3DUSAGE_DEPTHSTENCIL;
    if (pDesc->Pool != D3DPOOL_DEFAULT && (pDesc->Usage & incompatibleUsages))
      return D3DERR_INVALIDCALL;

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

    if (pDesc->Usage & D3DUSAGE_AUTOGENMIPMAP)
      pDesc->MipLevels = 0;

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

    return D3D_OK;
  }

  bool D3D9CommonTexture::CreateBufferSubresource(UINT Subresource) {
    if (m_buffers[Subresource] != nullptr)
      return false;

    DxvkBufferCreateInfo info;
    info.size   = GetMipSize(Subresource);
    info.usage  = VK_BUFFER_USAGE_TRANSFER_SRC_BIT
                | VK_BUFFER_USAGE_TRANSFER_DST_BIT
                | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
    info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT;
    info.access = VK_ACCESS_TRANSFER_READ_BIT
                | VK_ACCESS_TRANSFER_WRITE_BIT;

    if (m_mapping.ConversionFormatInfo.FormatType != D3D9ConversionFormat_None) {
      info.usage  |= VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT;
      info.stages |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
    }

    VkMemoryPropertyFlags memType = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
                                  | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
                                  | VK_MEMORY_PROPERTY_HOST_CACHED_BIT;

    m_buffers[Subresource] = m_device->GetDXVKDevice()->createBuffer(info, memType);
    m_mappedSlices[Subresource] = m_buffers[Subresource]->getSliceHandle();

    return true;
  }

  VkDeviceSize D3D9CommonTexture::GetMipSize(UINT Subresource) const {
    const UINT MipLevel = Subresource % m_desc.MipLevels;

    const DxvkFormatInfo* formatInfo = m_mapping.FormatColor != VK_FORMAT_UNDEFINED
      ? imageFormatInfo(m_mapping.FormatColor)
      : m_device->UnsupportedFormatInfo(m_desc.Format);

    const VkExtent3D mipExtent = util::computeMipLevelExtent(
      GetExtent(), MipLevel);

    const VkExtent3D blockCount = util::computeBlockCount(
      mipExtent, formatInfo->blockSize);

    const uint32_t planeCount = m_mapping.ConversionFormatInfo.PlaneCount;

    return std::min(planeCount, 2u)
         * align(formatInfo->elementSize * blockCount.width, 4)
         * blockCount.height
         * blockCount.depth;
  }

  Rc<DxvkImage> D3D9CommonTexture::CreatePrimaryImage(D3DRESOURCETYPE ResourceType, bool TryOffscreenRT, HANDLE* pSharedHandle) const {
    DxvkImageCreateInfo imageInfo;
    imageInfo.type            = GetImageTypeFromResourceType(ResourceType);
    imageInfo.format          = m_mapping.ConversionFormatInfo.FormatColor != VK_FORMAT_UNDEFINED
                              ? m_mapping.ConversionFormatInfo.FormatColor
                              : m_mapping.FormatColor;
    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
                              | VK_IMAGE_USAGE_SAMPLED_BIT;
    imageInfo.stages          = VK_PIPELINE_STAGE_TRANSFER_BIT
                              | m_device->GetEnabledShaderStages();
    imageInfo.access          = VK_ACCESS_TRANSFER_READ_BIT
                              | VK_ACCESS_TRANSFER_WRITE_BIT
                              | VK_ACCESS_SHADER_READ_BIT;
    imageInfo.tiling          = VK_IMAGE_TILING_OPTIMAL;
    imageInfo.layout          = VK_IMAGE_LAYOUT_GENERAL;
    imageInfo.shared          = m_desc.IsBackBuffer;

    if (pSharedHandle) {
      imageInfo.sharing.type = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT;
      imageInfo.sharing.mode = (*pSharedHandle == INVALID_HANDLE_VALUE || *pSharedHandle == nullptr)
        ? DxvkSharedHandleMode::Export
        : DxvkSharedHandleMode::Import;
      imageInfo.sharing.type = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT;
      imageInfo.sharing.handle = *pSharedHandle;
      imageInfo.shared = true;
      // TODO: validate metadata?
    }

    if (m_mapping.ConversionFormatInfo.FormatType != D3D9ConversionFormat_None) {
      imageInfo.usage  |= VK_IMAGE_USAGE_STORAGE_BIT;
      imageInfo.stages |= VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT;
    }

    DecodeMultiSampleType(m_device->GetDXVKDevice(), m_desc.MultiSample, m_desc.MultisampleQuality, &imageInfo.sampleCount);

    // 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(m_mapping.FormatColor);

    bool isMutable     = m_mapping.FormatSrgb != VK_FORMAT_UNDEFINED;
    bool isColorFormat = (formatProperties->aspectMask & VK_IMAGE_ASPECT_COLOR_BIT) != 0;

    if (isMutable && isColorFormat) {
      imageInfo.flags |= VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;

      imageInfo.viewFormatCount = 2;
      imageInfo.viewFormats     = m_mapping.Formats;
    }

    // Are we an RT, need to gen mips or an offscreen plain surface?
    if (m_desc.Usage & (D3DUSAGE_RENDERTARGET | D3DUSAGE_AUTOGENMIPMAP) || TryOffscreenRT) {
      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.Usage & D3DUSAGE_DEPTHSTENCIL) {
      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 (ResourceType == D3DRTYPE_CUBETEXTURE)
      imageInfo.flags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;

    // 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;

    // 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 && imageInfo.sharing.mode == DxvkSharedHandleMode::None)
      imageInfo.layout = OptimizeLayout(imageInfo.usage);

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

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

    return m_device->GetDXVKDevice()->createImage(imageInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
  }

  Rc<DxvkImage> D3D9CommonTexture::CreateResolveImage() const {
    DxvkImageCreateInfo imageInfo = m_image->info();
    imageInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;

    return m_device->GetDXVKDevice()->createImage(imageInfo, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
  }

  BOOL D3D9CommonTexture::DetermineShadowState() const {
    constexpr std::array<D3D9Format, 3> blacklist = {
      D3D9Format::INTZ, D3D9Format::DF16, D3D9Format::DF24
    };

    return IsDepthFormat(m_desc.Format)
        && std::find(blacklist.begin(), blacklist.end(), m_desc.Format) == blacklist.end();
  }

  BOOL D3D9CommonTexture::DetermineFetch4Compatibility() const {
    constexpr std::array<D3D9Format, 8> singleChannelFormats = {
      D3D9Format::INTZ, D3D9Format::DF16, D3D9Format::DF24,
      D3D9Format::R16F, D3D9Format::R32F, D3D9Format::A8,
      D3D9Format::L8, D3D9Format::L16
    };

    return std::find(singleChannelFormats.begin(), singleChannelFormats.end(), m_desc.Format) != singleChannelFormats.end();
  }

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

    VkImageFormatProperties formatProps = { };

    VkResult status = adapter->imageFormatProperties(
      pImageInfo->format, pImageInfo->type, Tiling,
      pImageInfo->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);
  }

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

    if (Format == VK_FORMAT_D16_UNORM || Format == VK_FORMAT_D32_SFLOAT)
      requestedFeatures |= VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;

    if (Format == VK_FORMAT_R16_UNORM || Format == VK_FORMAT_R32_SFLOAT)
      requestedFeatures |=  VK_FORMAT_FEATURE_COLOR_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_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;
  }

  VkImageType D3D9CommonTexture::GetImageTypeFromResourceType(D3DRESOURCETYPE Type) {
    switch (Type) {
      case D3DRTYPE_SURFACE:
      case D3DRTYPE_TEXTURE:       return VK_IMAGE_TYPE_2D;
      case D3DRTYPE_VOLUMETEXTURE: return VK_IMAGE_TYPE_3D;
      case D3DRTYPE_CUBETEXTURE:   return VK_IMAGE_TYPE_2D;
      default: throw DxvkError("D3D9CommonTexture: Unhandled resource type");
    }
  }

  VkImageViewType D3D9CommonTexture::GetImageViewTypeFromResourceType(
          D3DRESOURCETYPE  Dimension,
          UINT             Layer) {
    switch (Dimension) {
      case D3DRTYPE_SURFACE:
      case D3DRTYPE_TEXTURE:       return VK_IMAGE_VIEW_TYPE_2D;
      case D3DRTYPE_VOLUMETEXTURE: return VK_IMAGE_VIEW_TYPE_3D;
      case D3DRTYPE_CUBETEXTURE:   return Layer == AllLayers
                                        ? VK_IMAGE_VIEW_TYPE_CUBE
                                        : VK_IMAGE_VIEW_TYPE_2D;
      default: throw DxvkError("D3D9CommonTexture: Unhandled resource type");
    }
  }

  VkImageLayout D3D9CommonTexture::OptimizeLayout(VkImageUsageFlags Usage) const {
    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);

    // Ignore sampled bit in case the image was created with
    // an image flag that only allows attachment usage
    if (m_desc.IsAttachmentOnly)
      Usage &= ~VK_IMAGE_USAGE_SAMPLED_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;
  }

  void D3D9CommonTexture::ExportImageInfo() {
    /* From MSDN:
      Textures being shared from D3D9 to D3D11 have the following restrictions.

      - Textures must be 2D
      - Only 1 mip level is allowed
      - Texture must have default usage
      - Texture must be write only
      - MSAA textures are not allowed
      - Bind flags must have SHADER_RESOURCE and RENDER_TARGET set
      - Only R10G10B10A2_UNORM, R16G16B16A16_FLOAT and R8G8B8A8_UNORM formats are allowed
    */
    DXGI_FORMAT dxgiFormat = DXGI_FORMAT_UNKNOWN;

    switch (m_desc.Format) {
      case D3D9Format::A2B10G10R10: dxgiFormat = DXGI_FORMAT_R10G10B10A2_UNORM; break;
      case D3D9Format::A16B16G16R16F: dxgiFormat = DXGI_FORMAT_R16G16B16A16_FLOAT; break;
      case D3D9Format::A8B8G8R8: dxgiFormat = DXGI_FORMAT_R8G8B8A8_UNORM; break;
      case D3D9Format::X8B8G8R8: dxgiFormat = DXGI_FORMAT_R8G8B8A8_UNORM; break; /* No RGBX in DXGI */
      case D3D9Format::A8R8G8B8: dxgiFormat = DXGI_FORMAT_B8G8R8A8_UNORM; break;
      case D3D9Format::X8R8G8B8: dxgiFormat = DXGI_FORMAT_B8G8R8X8_UNORM; break;
      default:
        Logger::warn(str::format("D3D9: Unsupported format for shared textures: ", m_desc.Format));
        return;
    }

    if (m_desc.Depth == 1 && m_desc.MipLevels == 1 && m_desc.MultiSample == D3DMULTISAMPLE_NONE &&
        m_desc.Usage & D3DUSAGE_RENDERTARGET && dxgiFormat != DXGI_FORMAT_UNKNOWN) {
      HANDLE ntHandle = openKmtHandle(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             = dxgiFormat;
      metadata.SampleDesc.Count   = 1;
      metadata.SampleDesc.Quality = 0;
      metadata.Usage              = D3D11_USAGE_DEFAULT;
      metadata.BindFlags          = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
      metadata.CPUAccessFlags     = 0;
      metadata.MiscFlags          = D3D11_RESOURCE_MISC_SHARED;
      metadata.TextureLayout      = D3D11_TEXTURE_LAYOUT_UNDEFINED;

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

      if (ntHandle != INVALID_HANDLE_VALUE)
        ::CloseHandle(ntHandle);
    }
  }

  Rc<DxvkImageView> D3D9CommonTexture::CreateView(
          UINT                   Layer,
          UINT                   Lod,
          VkImageUsageFlags      UsageFlags,
          bool                   Srgb) {
    DxvkImageViewCreateInfo viewInfo;
    viewInfo.format    = m_mapping.ConversionFormatInfo.FormatColor != VK_FORMAT_UNDEFINED
                       ? PickSRGB(m_mapping.ConversionFormatInfo.FormatColor, m_mapping.ConversionFormatInfo.FormatSrgb, Srgb)
                       : PickSRGB(m_mapping.FormatColor, m_mapping.FormatSrgb, Srgb);
    viewInfo.aspect    = imageFormatInfo(viewInfo.format)->aspectMask;
    viewInfo.swizzle   = m_mapping.Swizzle;
    viewInfo.usage     = UsageFlags;
    viewInfo.type      = GetImageViewTypeFromResourceType(m_type, Layer);
    viewInfo.minLevel  = Lod;
    viewInfo.numLevels = m_desc.MipLevels - Lod;
    viewInfo.minLayer  = Layer == AllLayers ? 0                : Layer;
    viewInfo.numLayers = Layer == AllLayers ? m_desc.ArraySize : 1;

    // Remove the stencil aspect if we are trying to create a regular image
    // view of a depth stencil format
    if (UsageFlags != VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
      viewInfo.aspect &= ~VK_IMAGE_ASPECT_STENCIL_BIT;

    if (UsageFlags == VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT ||
        UsageFlags == VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
      viewInfo.numLevels = 1;

    // Remove swizzle on depth views.
    if (UsageFlags == VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)
      viewInfo.swizzle = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY,
                           VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY };

    // Create the underlying image view object
    return m_device->GetDXVKDevice()->createImageView(GetImage(), viewInfo);
  }

  void D3D9CommonTexture::PreLoadAll() {
    if (!IsManaged())
      return;

    auto lock = m_device->LockDevice();
    m_device->UploadManagedTexture(this);
    m_device->MarkTextureUploaded(this);
  }

  void D3D9CommonTexture::PreLoadSubresource(UINT Subresource) {
    if (IsManaged()) {
      auto lock = m_device->LockDevice();

      if (NeedsUpload(Subresource)) {
        m_device->FlushImage(this, Subresource);
        SetNeedsUpload(Subresource, false);

        if (!NeedsAnyUpload())
          m_device->MarkTextureUploaded(this);
      }
    }
  }

  void D3D9CommonTexture::CreateSampleView(UINT Lod) {
    // This will be a no-op for SYSTEMMEM types given we
    // don't expose the cap to allow texturing with them.
    if (unlikely(m_mapMode == D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM))
      return;

    m_sampleView.Color = CreateView(AllLayers, Lod, VK_IMAGE_USAGE_SAMPLED_BIT, false);

    if (IsSrgbCompatible())
      m_sampleView.Srgb = CreateView(AllLayers, Lod, VK_IMAGE_USAGE_SAMPLED_BIT, true);
  }

}


code:
#pragma once

#include "d3d9_format.h"
#include "d3d9_util.h"
#include "d3d9_caps.h"

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

#include "../util/util_bit.h"

namespace dxvk {

  class D3D9DeviceEx;

  /**
   * \brief Image memory mapping mode
   *
   * Determines how exactly \c LockBox will
   * behave when mapping an image.
   */
  enum D3D9_COMMON_TEXTURE_MAP_MODE {
    D3D9_COMMON_TEXTURE_MAP_MODE_NONE,      ///< No mapping available
    D3D9_COMMON_TEXTURE_MAP_MODE_BACKED,    ///< Mapped image through buffer
    D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM, ///< Only a buffer - no image
  };

  /**
   * \brief Common texture description
   *
   * Contains all members that can be
   * defined for 2D, Cube and 3D textures.
   */
  struct D3D9_COMMON_TEXTURE_DESC {
    UINT                Width;
    UINT                Height;
    UINT                Depth;
    UINT                ArraySize;
    UINT                MipLevels;
    DWORD               Usage;
    D3D9Format          Format;
    D3DPOOL             Pool;
    D3DMULTISAMPLE_TYPE MultiSample;
    DWORD               MultisampleQuality;
    bool                Discard;
    bool                IsBackBuffer;
    bool                IsAttachmentOnly;
  };

  struct D3D9ColorView {
    inline Rc<DxvkImageView>& Pick(bool Srgb) {
      return Srgb ? this->Srgb : this->Color;
    }

    inline const Rc<DxvkImageView>& Pick(bool Srgb) const {
      return Srgb ? this->Srgb : this->Color;
    }

    Rc<DxvkImageView> Color;
    Rc<DxvkImageView> Srgb;
  };

  template <typename T>
  using D3D9SubresourceArray = std::array<T, caps::MaxSubresources>;

  using D3D9SubresourceBitset = bit::bitset<caps::MaxSubresources>;

  class D3D9CommonTexture {

  public:

    D3D9CommonTexture(
            D3D9DeviceEx*             pDevice,
      const D3D9_COMMON_TEXTURE_DESC* pDesc,
            D3DRESOURCETYPE           ResourceType,
            HANDLE*                   pSharedHandle);

    ~D3D9CommonTexture();

    /**
     * \brief Device
     * \returns The parent device
     */
    D3D9DeviceEx* Device() const {
      return m_device;
    }

    /**
      * \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 D3D9_COMMON_TEXTURE_DESC* Desc() const {
      return &m_desc;
    }

    /**
     * \brief Vulkan Format
     * \returns The Vulkan format of the resource
     */
    const D3D9_VK_FORMAT_MAPPING& GetFormatMapping() const {
      return m_mapping;
    }

    /**
     * \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
     */
    D3D9_COMMON_TEXTURE_MAP_MODE GetMapMode() const {
      return m_mapMode;
    }

    /**
     * \brief The DXVK image
     * Note, this will be nullptr if the map mode is D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM
     * \returns The DXVK image
     */
    const Rc<DxvkImage>& GetImage() const {
      return m_image;
    }

    /**
     * \brief Get a copy of the main image, but with a single sample
     * This function will allocate/reuse an image with the same info
     * as the main image
     * \returns An image with identical info, but 1 sample
     */
    const Rc<DxvkImage>& GetResolveImage() {
      if (unlikely(m_resolveImage == nullptr))
        m_resolveImage = CreateResolveImage();

      return m_resolveImage;
    }

    const Rc<DxvkBuffer>& GetBuffer(UINT Subresource) {
      return m_buffers[Subresource];
    }

    DxvkBufferSliceHandle GetMappedSlice(UINT Subresource) {
      return m_mappedSlices[Subresource];
    }

    DxvkBufferSliceHandle DiscardMapSlice(UINT Subresource) {
      DxvkBufferSliceHandle handle = m_buffers[Subresource]->allocSlice();
      m_mappedSlices[Subresource] = handle;
      return handle;
    }

    /**
     * \brief Computes subresource from the subresource index
     *
     * Used by some functions that operate on only
     * one subresource, such as \c UpdateSurface.
     * \param [in] Aspect The image aspect
     * \param [in] Subresource Subresource index
     * \returns The Vulkan image subresource
     */
    VkImageSubresource GetSubresourceFromIndex(
            VkImageAspectFlags    Aspect,
            UINT                  Subresource) 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(
            D3D9DeviceEx*              pDevice,
            D3D9_COMMON_TEXTURE_DESC*  pDesc);

    /**
     * \brief Shadow
     * \returns Whether the texture is to be depth compared
     */
    bool IsShadow() const {
      return m_shadow;
    }

    /**
     * \brief FETCH4 compatibility
     * \returns Whether the format of the texture supports the FETCH4 hack
     */
    bool SupportsFetch4() const {
      return m_supportsFetch4;
    }

    /**
     * \brief Null
     * \returns Whether the texture is D3DFMT_NULL or not
     */
    bool IsNull() const {
      return m_desc.Format == D3D9Format::NULL_FORMAT;
    }

    /**
     * \brief Subresource
     * \returns The subresource idx of a given face and mip level
     */
    UINT CalcSubresource(UINT Face, UINT MipLevel) const {
      return Face * m_desc.MipLevels + MipLevel;
    }

    /**
     * \brief Creates buffers
     * Creates mapping and staging buffers for all subresources
     * allocates new buffers if necessary
     */
    void CreateBuffers() {
      const uint32_t count = CountSubresources();
      for (uint32_t i = 0; i < count; i++)
        CreateBufferSubresource(i);
    }

    /**
     * \brief Creates a buffer
     * Creates mapping and staging buffers for a given subresource
     * allocates new buffers if necessary
     * \returns Whether an allocation happened
     */
    bool CreateBufferSubresource(UINT Subresource);

    /**
     * \brief Destroys a buffer
     * Destroys mapping and staging buffers for a given subresource
     */
    void DestroyBufferSubresource(UINT Subresource) {
      m_buffers[Subresource] = nullptr;
      SetNeedsReadback(Subresource, true);
    }

    bool IsDynamic() const {
      return m_desc.Usage & D3DUSAGE_DYNAMIC;
    }

    /**
     * \brief Managed
     * \returns Whether a resource is managed (pool) or not
     */
    bool IsManaged() const {
      return IsPoolManaged(m_desc.Pool);
    }

    /**
     * \brief Render Target
     * \returns Whether a resource is a render target or not
     */
    bool IsRenderTarget() const {
      return m_desc.Usage & D3DUSAGE_RENDERTARGET;
    }

    /**
     * \brief Depth stencil
     * \returns Whether a resource is a depth stencil or not
     */
    bool IsDepthStencil() const {
      return m_desc.Usage & D3DUSAGE_DEPTHSTENCIL;
    }

    /**
     * \brief Autogen Mipmap
     * \returns Whether the texture is to have automatic mip generation
     */
    bool IsAutomaticMip() const {
      return m_desc.Usage & D3DUSAGE_AUTOGENMIPMAP;
    }

    /**
     * \brief Checks whether sRGB views can be created
     * \returns Whether the format is sRGB compatible.
     */
    bool IsSrgbCompatible() const {
      return m_mapping.FormatSrgb;
    }

    /**
     * \brief Recreate main image view
     * Recreates the main view of the sampler w/ a specific LOD.
     * SetLOD only works on MANAGED textures so this is A-okay.
     */
    void CreateSampleView(UINT Lod);

    /**
     * \brief Extent
     * \returns The extent of the top-level mip
     */
    VkExtent3D GetExtent() const {
      return VkExtent3D{ m_desc.Width, m_desc.Height, m_desc.Depth };
    }

    /**
     * \brief Mip Extent
     * \returns The extent of a mip or subresource
     */
    VkExtent3D GetExtentMip(UINT Subresource) const {
      UINT MipLevel = Subresource % m_desc.MipLevels;
      return util::computeMipLevelExtent(GetExtent(), MipLevel);
    }

    bool MarkHazardous() {
      return std::exchange(m_hazardous, true);
    }

    D3DRESOURCETYPE GetType() {
      return m_type;
    }

    const D3D9_VK_FORMAT_MAPPING& GetMapping() { return m_mapping; }

    void SetLocked(UINT Subresource, bool value) { m_locked.set(Subresource, value); }

    bool GetLocked(UINT Subresource) const { return m_locked.get(Subresource); }

    bool IsAnySubresourceLocked() const { return m_locked.any(); }

    void SetNeedsReadback(UINT Subresource, bool value) { m_needsReadback.set(Subresource, value); }

    bool NeedsReachback(UINT Subresource) const { return m_needsReadback.get(Subresource); }

    void MarkAllNeedReadback() { m_needsReadback.setAll(); }

    void SetReadOnlyLocked(UINT Subresource, bool readOnly) { return m_readOnly.set(Subresource, readOnly); }

    bool GetReadOnlyLocked(UINT Subresource) const { return m_readOnly.get(Subresource); }

    const Rc<DxvkImageView>& GetSampleView(bool srgb) const {
      return m_sampleView.Pick(srgb && IsSrgbCompatible());
    }

    VkImageLayout DetermineRenderTargetLayout() const {
      return m_image != nullptr &&
             m_image->info().tiling == VK_IMAGE_TILING_OPTIMAL &&
            !m_hazardous
        ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
        : VK_IMAGE_LAYOUT_GENERAL;
    }

    VkImageLayout DetermineDepthStencilLayout(bool write, bool hazardous) const {
      VkImageLayout layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

      if (unlikely(hazardous)) {
        layout = write
          ? VK_IMAGE_LAYOUT_GENERAL
          : VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL;
      }

      if (unlikely(m_image->info().tiling != VK_IMAGE_TILING_OPTIMAL))
        layout = VK_IMAGE_LAYOUT_GENERAL;

      return layout;
    }

    Rc<DxvkImageView> CreateView(
            UINT                   Layer,
            UINT                   Lod,
            VkImageUsageFlags      UsageFlags,
            bool                   Srgb);
    D3D9SubresourceBitset& GetUploadBitmask() { return m_needsUpload; }

    void SetAllNeedUpload() {
      m_needsUpload.setAll();
    }
    void SetNeedsUpload(UINT Subresource, bool upload) { m_needsUpload.set(Subresource, upload); }
    bool NeedsUpload(UINT Subresource) const { return m_needsUpload.get(Subresource); }
    bool NeedsAnyUpload() { return m_needsUpload.any(); }
    void ClearNeedsUpload() { return m_needsUpload.clearAll();  }
    bool DoesStagingBufferUploads(UINT Subresource) const { return m_uploadUsingStaging.get(Subresource); }

    void EnableStagingBufferUploads(UINT Subresource) {
      m_uploadUsingStaging.set(Subresource, true);
    }

    void SetNeedsMipGen(bool value) { m_needsMipGen = value; }
    bool NeedsMipGen() const { return m_needsMipGen; }

    DWORD ExposedMipLevels() { return m_exposedMipLevels; }

    void SetMipFilter(D3DTEXTUREFILTERTYPE filter) { m_mipFilter = filter; }
    D3DTEXTUREFILTERTYPE GetMipFilter() const { return m_mipFilter; }

    void PreLoadAll();
    void PreLoadSubresource(UINT Subresource);

    void AddDirtyBox(CONST D3DBOX* pDirtyBox, uint32_t layer) {
      if (pDirtyBox) {
        D3DBOX box = *pDirtyBox;
        if (box.Right <= box.Left
          || box.Bottom <= box.Top
          || box.Back <= box.Front)
          return;

        box.Right = std::min(box.Right, m_desc.Width);
        box.Bottom = std::min(box.Bottom, m_desc.Height);
        box.Back = std::min(box.Back, m_desc.Depth);

        D3DBOX& dirtyBox = m_dirtyBoxes[layer];
        if (dirtyBox.Left == dirtyBox.Right) {
          dirtyBox = box;
        } else {
          dirtyBox.Left    = std::min(dirtyBox.Left,   box.Left);
          dirtyBox.Right   = std::max(dirtyBox.Right,  box.Right);
          dirtyBox.Top     = std::min(dirtyBox.Top,    box.Top);
          dirtyBox.Bottom  = std::max(dirtyBox.Bottom, box.Bottom);
          dirtyBox.Front   = std::min(dirtyBox.Front,  box.Front);
          dirtyBox.Back    = std::max(dirtyBox.Back,   box.Back);
        }
      } else {
        m_dirtyBoxes[layer] = { 0, 0, m_desc.Width, m_desc.Height, 0, m_desc.Depth };
      }
    }

    void ClearDirtyBoxes() {
      for (uint32_t i = 0; i < m_dirtyBoxes.size(); i++) {
        m_dirtyBoxes[i] = { 0, 0, 0, 0, 0, 0 };
      }
    }

    const D3DBOX& GetDirtyBox(uint32_t layer) const {
      return m_dirtyBoxes[layer];
    }

    static VkImageType GetImageTypeFromResourceType(
            D3DRESOURCETYPE  Dimension);

     /**
     * \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 TrackMappingBufferSequenceNumber(UINT Subresource, uint64_t Seq) {
      if (Subresource < m_seqs.size())
        m_seqs[Subresource] = 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 GetMappingBufferSequenceNumber(UINT Subresource) {
      return Subresource < m_seqs.size()
        ? m_seqs[Subresource]
        : 0ull;
    }

  private:

    D3D9DeviceEx*                 m_device;
    D3D9_COMMON_TEXTURE_DESC      m_desc;
    D3DRESOURCETYPE               m_type;
    D3D9_COMMON_TEXTURE_MAP_MODE  m_mapMode;

    Rc<DxvkImage>                 m_image;
    Rc<DxvkImage>                 m_resolveImage;
    D3D9SubresourceArray<
      Rc<DxvkBuffer>>             m_buffers;
    D3D9SubresourceArray<
      DxvkBufferSliceHandle>      m_mappedSlices;
    D3D9SubresourceArray<
      uint64_t>                   m_seqs = { };

    D3D9_VK_FORMAT_MAPPING        m_mapping;

    bool                          m_shadow; //< Depth Compare-ness
    bool                          m_supportsFetch4;

    int64_t                       m_size = 0;

    bool                          m_systemmemModified = false;

    bool                          m_hazardous = false;

    D3D9ColorView                 m_sampleView;

    D3D9SubresourceBitset         m_locked = { };

    D3D9SubresourceBitset         m_readOnly = { };

    D3D9SubresourceBitset         m_needsReadback = { };

    D3D9SubresourceBitset         m_needsUpload = { };

    D3D9SubresourceBitset         m_uploadUsingStaging = { };

    DWORD                         m_exposedMipLevels = 0;

    bool                          m_needsMipGen = false;

    D3DTEXTUREFILTERTYPE          m_mipFilter = D3DTEXF_LINEAR;

    std::array<D3DBOX, 6>         m_dirtyBoxes;

    /**
     * \brief Mip level
     * \returns Size of packed mip level in bytes
     */
    VkDeviceSize GetMipSize(UINT Subresource) const;

    Rc<DxvkImage> CreatePrimaryImage(D3DRESOURCETYPE ResourceType, bool TryOffscreenRT, HANDLE* pSharedHandle) const;

    Rc<DxvkImage> CreateResolveImage() const;

    BOOL DetermineShadowState() const;

    BOOL DetermineFetch4Compatibility() const;

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

    VkImageUsageFlags EnableMetaCopyUsage(
            VkFormat              Format,
            VkImageTiling         Tiling) const;

    D3D9_COMMON_TEXTURE_MAP_MODE DetermineMapMode() const {
      if (m_desc.Format == D3D9Format::NULL_FORMAT)
        return D3D9_COMMON_TEXTURE_MAP_MODE_NONE;

      if (m_desc.Pool == D3DPOOL_SYSTEMMEM || m_desc.Pool == D3DPOOL_SCRATCH)
        return D3D9_COMMON_TEXTURE_MAP_MODE_SYSTEMMEM;

      return D3D9_COMMON_TEXTURE_MAP_MODE_BACKED;
    }

    VkImageLayout OptimizeLayout(
            VkImageUsageFlags         Usage) const;

    void ExportImageInfo();

    static VkImageViewType GetImageViewTypeFromResourceType(
            D3DRESOURCETYPE  Dimension,
            UINT             Layer);

    static constexpr UINT AllLayers = UINT32_MAX;

  };

}


code:
#pragma once

#include <cstdint>

#include "d3d9_caps.h"

namespace dxvk {

  struct D3D9ConstantLayout {
    uint32_t floatCount;
    uint32_t intCount;
    uint32_t boolCount;
    uint32_t bitmaskCount;

    uint32_t floatSize()     const { return floatCount   * 4 * sizeof(float); }
    uint32_t intSize()       const { return intCount     * 4 * sizeof(int); }
    uint32_t bitmaskSize()   const {
      // Account for SWVP (non SWVP uses a spec constant)
      return bitmaskCount != 1
        ? bitmaskCount * 1 * sizeof(uint32_t)
        : 0;
    }

    uint32_t intOffset()     const { return 0; }
    uint32_t floatOffset()   const { return intOffset() + intSize(); }
    uint32_t bitmaskOffset() const { return floatOffset() + floatSize(); }

    uint32_t totalSize()     const { return floatSize() + intSize() + bitmaskSize(); }
  };

}


code:
#pragma once

#include "d3d9_caps.h"

#include "../dxvk/dxvk_buffer.h"

#include "../dxso/dxso_isgn.h"

#include "../util/util_math.h"
#include "../util/util_vector.h"

#include <cstdint>

namespace dxvk {

  enum class D3D9ConstantType {
    Float,
    Int,
    Bool
  };

  // We make an assumption later based on the packing of this struct for copying.
  struct D3D9ShaderConstantsVSSoftware {
    Vector4i iConsts[caps::MaxOtherConstantsSoftware];
    Vector4  fConsts[caps::MaxFloatConstantsSoftware];
    uint32_t bConsts[caps::MaxOtherConstantsSoftware / 32];
  };

  struct D3D9ShaderConstantsVSHardware {
    Vector4i iConsts[caps::MaxOtherConstants];
    Vector4  fConsts[caps::MaxFloatConstantsVS];
    uint32_t bConsts[1];
  };

  struct D3D9ShaderConstantsPS {
    Vector4i iConsts[caps::MaxOtherConstants];
    Vector4  fConsts[caps::MaxFloatConstantsPS];
    uint32_t bConsts[1];
  };

  struct D3D9SwvpConstantBuffers {
    Rc<DxvkBuffer>        floatBuffer;
    Rc<DxvkBuffer>        intBuffer;
    Rc<DxvkBuffer>        boolBuffer;
  };

  struct D3D9ConstantSets {
    D3D9SwvpConstantBuffers   swvpBuffers;
    Rc<DxvkBuffer>            buffer;
    DxsoShaderMetaInfo        meta  = {};
    bool                      dirty = true;
  };

}


code:
#include "d3d9_cursor.h"
#include "d3d9_util.h"

#include <utility>

namespace dxvk {

  void D3D9Cursor::UpdateCursor(int X, int Y) {
    POINT currentPos = { };
    if (::GetCursorPos(&currentPos) && currentPos == POINT{ X, Y })
        return;

    ::SetCursorPos(X, Y);
  }

  BOOL D3D9Cursor::ShowCursor(BOOL bShow) {
    ::SetCursor(bShow ? m_hCursor : nullptr);
    return std::exchange(m_visible, bShow);
  }

  HRESULT D3D9Cursor::SetHardwareCursor(UINT XHotSpot, UINT YHotSpot, const CursorBitmap& bitmap) {
    DWORD mask[32];
    std::memset(mask, ~0, sizeof(mask));

    ICONINFO info;
    info.fIcon    = FALSE;
    info.xHotspot = XHotSpot;
    info.yHotspot = YHotSpot;
    info.hbmMask  = ::CreateBitmap(HardwareCursorWidth, HardwareCursorHeight, 1, 1,  mask);
    info.hbmColor = ::CreateBitmap(HardwareCursorWidth, HardwareCursorHeight, 1, 32, &bitmap[0]);

    if (m_hCursor != nullptr)
      ::DestroyCursor(m_hCursor);

    m_hCursor = ::CreateIconIndirect(&info);

    ::DeleteObject(info.hbmMask);
    ::DeleteObject(info.hbmColor);

    ShowCursor(m_visible);

    return D3D_OK;
  }

}


code:
#pragma once

#include "d3d9_include.h"

namespace dxvk {

  constexpr uint32_t HardwareCursorWidth     = 32u;
  constexpr uint32_t HardwareCursorHeight    = 32u;
  constexpr uint32_t HardwareCursorFormatSize = 4u;
  constexpr uint32_t HardwareCursorPitch      = HardwareCursorWidth * HardwareCursorFormatSize;

  // Format Size of 4 bytes (ARGB)
  using CursorBitmap = uint8_t[HardwareCursorHeight * HardwareCursorPitch];

  class D3D9Cursor {

  public:

    void UpdateCursor(int X, int Y);

    BOOL ShowCursor(BOOL bShow);

    HRESULT SetHardwareCursor(UINT XHotSpot, UINT YHotSpot, const CursorBitmap& bitmap);

  private:

    BOOL    m_visible       = FALSE;

    HCURSOR m_hCursor       = nullptr;

  };

}