code:
#include "d3d9_sampler.h"

namespace dxvk {

  size_t D3D9SamplerKeyHash::operator () (const D3D9SamplerKey& key) const {
    DxvkHashState state;

    std::hash<DWORD>                dhash;
    std::hash<D3DTEXTUREADDRESS>    tahash;
    std::hash<D3DTEXTUREFILTERTYPE> tfhash;
    std::hash<float>                fhash;
    std::hash<bool>                 bhash;

    state.add(tahash(key.AddressU));
    state.add(tahash(key.AddressV));
    state.add(tahash(key.AddressW));
    state.add(tfhash(key.MagFilter));
    state.add(tfhash(key.MinFilter));
    state.add(tfhash(key.MipFilter));
    state.add(dhash (key.MaxAnisotropy));
    state.add(fhash (key.MipmapLodBias));
    state.add(dhash (key.MaxMipLevel));
    state.add(dhash (key.BorderColor));
    state.add(bhash (key.Depth));

    return state;
  }

  bool D3D9SamplerKeyEq::operator () (const D3D9SamplerKey& a, const D3D9SamplerKey& b) const {
    return a.AddressU       == b.AddressU
        && a.AddressV       == b.AddressV
        && a.AddressW       == b.AddressW
        && a.MagFilter      == b.MagFilter
        && a.MinFilter      == b.MinFilter
        && a.MipFilter      == b.MipFilter
        && a.MaxAnisotropy  == b.MaxAnisotropy
        && a.MipmapLodBias  == b.MipmapLodBias
        && a.MaxMipLevel    == b.MaxMipLevel
        && a.BorderColor    == b.BorderColor
        && a.Depth          == b.Depth;
  }

}


code:
#pragma once

#include "d3d9_include.h"

#include "d3d9_util.h"

#include "../dxvk/dxvk_hash.h"

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

namespace dxvk {

  struct D3D9SamplerKey {
    D3DTEXTUREADDRESS AddressU;
    D3DTEXTUREADDRESS AddressV;
    D3DTEXTUREADDRESS AddressW;
    D3DTEXTUREFILTERTYPE MagFilter;
    D3DTEXTUREFILTERTYPE MinFilter;
    D3DTEXTUREFILTERTYPE MipFilter;
    DWORD MaxAnisotropy;
    float MipmapLodBias;
    DWORD MaxMipLevel;
    D3DCOLOR BorderColor;
    bool Depth;
  };

  struct D3D9SamplerKeyHash {
    size_t operator () (const D3D9SamplerKey& key) const;
  };

  struct D3D9SamplerKeyEq {
    bool operator () (const D3D9SamplerKey& a, const D3D9SamplerKey& b) const;
  };

  inline void NormalizeSamplerKey(D3D9SamplerKey& key) {
    key.AddressU = std::clamp(key.AddressU, D3DTADDRESS_WRAP, D3DTADDRESS_MIRRORONCE);
    key.AddressV = std::clamp(key.AddressV, D3DTADDRESS_WRAP, D3DTADDRESS_MIRRORONCE);
    key.AddressW = std::clamp(key.AddressW, D3DTADDRESS_WRAP, D3DTADDRESS_MIRRORONCE);

    bool hasAnisotropy = IsAnisotropic(key.MagFilter) || IsAnisotropic(key.MinFilter);

    key.MagFilter = std::clamp(key.MagFilter, D3DTEXF_NONE, D3DTEXF_LINEAR);
    key.MinFilter = std::clamp(key.MinFilter, D3DTEXF_NONE, D3DTEXF_LINEAR);
    key.MipFilter = std::clamp(key.MipFilter, D3DTEXF_NONE, D3DTEXF_LINEAR);

    key.MaxAnisotropy = hasAnisotropy
      ? std::clamp<DWORD>(key.MaxAnisotropy, 1, 16)
      : 1;

    if (key.MipFilter == D3DTEXF_NONE) {
      // May as well try and keep slots down.
      key.MipmapLodBias = 0;
    }
    else {
      // Games also pass NAN here, this accounts for that.
      if (unlikely(key.MipmapLodBias != key.MipmapLodBias))
        key.MipmapLodBias = 0.0f;

      // Clamp between -15.0f and 15.0f, matching mip limits of d3d9.
      key.MipmapLodBias = std::clamp(key.MipmapLodBias, -15.0f, 15.0f);

      // Round to the nearest .5
      // Fixes sampler leaks in UE3 games w/ mip streaming
      // eg. Borderlands 2
      key.MipmapLodBias = std::round(key.MipmapLodBias * 2.0f) / 2.0f;
    }

    if (key.AddressU != D3DTADDRESS_BORDER
     && key.AddressV != D3DTADDRESS_BORDER
     && key.AddressW != D3DTADDRESS_BORDER) {
      key.BorderColor = 0;
    }
  }

}

repo_name: orochi/dxvk


code:
#include "d3d9_shader.h"

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

namespace dxvk {

  D3D9CommonShader::D3D9CommonShader() {}

  D3D9CommonShader::D3D9CommonShader(
            D3D9DeviceEx*         pDevice,
            VkShaderStageFlagBits ShaderStage,
      const DxvkShaderKey&        Key,
      const DxsoModuleInfo*       pDxsoModuleInfo,
      const void*                 pShaderBytecode,
      const DxsoAnalysisInfo&     AnalysisInfo,
            DxsoModule*           pModule) {
    const uint32_t bytecodeLength = AnalysisInfo.bytecodeByteLength;
    m_bytecode.resize(bytecodeLength);
    std::memcpy(m_bytecode.data(), pShaderBytecode, bytecodeLength);

    const std::string name = Key.toString();
    Logger::debug(str::format("Compiling shader ", name));

    // If requested by the user, dump both the raw DXBC
    // shader and the compiled SPIR-V module to a file.
    const std::string dumpPath = env::getEnvVar("DXVK_SHADER_DUMP_PATH");

    if (dumpPath.size() != 0) {
      DxsoReader reader(
        reinterpret_cast<const char*>(pShaderBytecode));

      reader.store(std::ofstream(str::tows(str::format(dumpPath, "/", name, ".dxso").c_str()).c_str(),
        std::ios_base::binary | std::ios_base::trunc), bytecodeLength);

      char comment[2048];
      Com<ID3DBlob> blob;
      HRESULT hr = DisassembleShader(
        pShaderBytecode,
        TRUE,
        comment,
        &blob);

      if (SUCCEEDED(hr)) {
        std::ofstream disassembledOut(str::tows(str::format(dumpPath, "/", name, ".dxso.dis").c_str()).c_str(), std::ios_base::binary | std::ios_base::trunc);
        disassembledOut.write(
          reinterpret_cast<const char*>(blob->GetBufferPointer()),
          blob->GetBufferSize());
      }
    }

    // Decide whether we need to create a pass-through
    // geometry shader for vertex shader stream output

    const D3D9ConstantLayout& constantLayout = ShaderStage == VK_SHADER_STAGE_VERTEX_BIT
      ? pDevice->GetVertexConstantLayout()
      : pDevice->GetPixelConstantLayout();
    m_shaders      = pModule->compile(*pDxsoModuleInfo, name, AnalysisInfo, constantLayout);
    m_isgn         = pModule->isgn();
    m_usedSamplers = pModule->usedSamplers();

    // Shift up these sampler bits so we can just
    // do an or per-draw in the device.
    // We shift by 17 because 16 ps samplers + 1 dmap (tess)
    if (ShaderStage == VK_SHADER_STAGE_VERTEX_BIT)
      m_usedSamplers <<= caps::MaxTexturesPS + 1;

    m_usedRTs      = pModule->usedRTs();

    m_info      = pModule->info();
    m_meta      = pModule->meta();
    m_constants = pModule->constants();
    m_maxDefinedConst = pModule->maxDefinedConstant();

    m_shaders[0]->setShaderKey(Key);

    if (m_shaders[1] != nullptr) {
      // Lets lie about the shader key type for the state cache.
      m_shaders[1]->setShaderKey({ VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, Key.sha1() });
    }

    if (dumpPath.size() != 0) {
      std::ofstream dumpStream(
        str::tows(str::format(dumpPath, "/", name, ".spv").c_str()).c_str(),
        std::ios_base::binary | std::ios_base::trunc);

      m_shaders[0]->dump(dumpStream);
    }

    pDevice->GetDXVKDevice()->registerShader(m_shaders[0]);

    if (m_shaders[1] != nullptr)
      pDevice->GetDXVKDevice()->registerShader(m_shaders[1]);
  }

  void D3D9ShaderModuleSet::GetShaderModule(
            D3D9DeviceEx*         pDevice,
            D3D9CommonShader*     pShaderModule,
            VkShaderStageFlagBits ShaderStage,
      const DxsoModuleInfo*       pDxbcModuleInfo,
      const void*                 pShaderBytecode) {
    DxsoReader reader(
      reinterpret_cast<const char*>(pShaderBytecode));

    DxsoModule module(reader);

    if (module.info().majorVersion() > pDxbcModuleInfo->options.shaderModel)
      throw DxvkError("GetShaderModule: Out of range of supported shader model");

    if (module.info().shaderStage() != ShaderStage)
      throw DxvkError("GetShaderModule: Bytecode does not match shader stage");

    DxsoAnalysisInfo info = module.analyze();

    DxvkShaderKey lookupKey = DxvkShaderKey(
      ShaderStage,
      Sha1Hash::compute(pShaderBytecode, info.bytecodeByteLength));

    // Use the shader's unique key for the lookup
    { std::unique_lock<dxvk::mutex> lock(m_mutex);

      auto entry = m_modules.find(lookupKey);
      if (entry != m_modules.end()) {
        *pShaderModule = entry->second;
        return;
      }
    }

    // This shader has not been compiled yet, so we have to create a
    // new module. This takes a while, so we won't lock the structure.
    *pShaderModule = D3D9CommonShader(
      pDevice, ShaderStage, lookupKey,
      pDxbcModuleInfo, pShaderBytecode,
      info, &module);

    // Insert the new module into the lookup table. If another thread
    // has compiled the same shader in the meantime, we should return
    // that object instead and discard the newly created module.
    { std::unique_lock<dxvk::mutex> lock(m_mutex);

      auto status = m_modules.insert({ lookupKey, *pShaderModule });
      if (!status.second) {
        *pShaderModule = status.first->second;
        return;
      }
    }
  }

}


code:
#pragma once

#include "d3d9_resource.h"
#include "../dxso/dxso_module.h"
#include "d3d9_shader_permutations.h"
#include "d3d9_util.h"

#include <array>

namespace dxvk {

  /**
   * \brief Common shader object
   *
   * Stores the compiled SPIR-V shader and the SHA-1
   * hash of the original DXBC shader, which can be
   * used to identify the shader.
   */
  class D3D9CommonShader {

  public:

    D3D9CommonShader();

    D3D9CommonShader(
            D3D9DeviceEx*         pDevice,
            VkShaderStageFlagBits ShaderStage,
      const DxvkShaderKey&        Key,
      const DxsoModuleInfo*       pDxbcModuleInfo,
      const void*                 pShaderBytecode,
      const DxsoAnalysisInfo&     AnalysisInfo,
            DxsoModule*           pModule);

    Rc<DxvkShader> GetShader(D3D9ShaderPermutation Permutation) const {
      return m_shaders[Permutation];
    }

    std::string GetName() const {
      return m_shaders[D3D9ShaderPermutations::None]->debugName();
    }

    const std::vector<uint8_t>& GetBytecode() const {
      return m_bytecode;
    }

    const DxsoIsgn& GetIsgn() const {
      return m_isgn;
    }

    const DxsoShaderMetaInfo& GetMeta() const { return m_meta; }
    const DxsoDefinedConstants& GetConstants() const { return m_constants; }

    D3D9ShaderMasks GetShaderMask() const { return D3D9ShaderMasks{ m_usedSamplers, m_usedRTs }; }

    const DxsoProgramInfo& GetInfo() const { return m_info; }

    uint32_t GetMaxDefinedConstant() const { return m_maxDefinedConst; }

  private:

    DxsoIsgn              m_isgn;
    uint32_t              m_usedSamplers;
    uint32_t              m_usedRTs;

    DxsoProgramInfo       m_info;
    DxsoShaderMetaInfo    m_meta;
    DxsoDefinedConstants  m_constants;
    uint32_t              m_maxDefinedConst;

    DxsoPermutations      m_shaders;

    std::vector<uint8_t>  m_bytecode;

  };

  /**
   * \brief Common shader interface
   *
   * Implements methods for all D3D11*Shader
   * interfaces and stores the actual shader
   * module object.
   */
  template <typename Base>
  class D3D9Shader : public D3D9DeviceChild<Base> {

  public:

    D3D9Shader(
            D3D9DeviceEx*      pDevice,
      const D3D9CommonShader&  CommonShader)
      : D3D9DeviceChild<Base>( pDevice )
      , m_shader             ( CommonShader ) { }

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

      *ppvObject = nullptr;

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

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

    HRESULT STDMETHODCALLTYPE GetFunction(void* pOut, UINT* pSizeOfData) {
      if (pSizeOfData == nullptr)
        return D3DERR_INVALIDCALL;

      const auto& bytecode = m_shader.GetBytecode();

      if (pOut == nullptr) {
        *pSizeOfData = bytecode.size();
        return D3D_OK;
      }

      size_t copyAmount = std::min(size_t(*pSizeOfData), bytecode.size());
      std::memcpy(pOut, bytecode.data(), copyAmount);

      return D3D_OK;
    }

    const D3D9CommonShader* GetCommonShader() const {
      return &m_shader;
    }

  private:

    D3D9CommonShader m_shader;

  };

  // Needs their own classes and not usings for forward decl.

  class D3D9VertexShader final : public D3D9Shader<IDirect3DVertexShader9> {

  public:

    D3D9VertexShader(
            D3D9DeviceEx*      pDevice,
      const D3D9CommonShader&  CommonShader)
      : D3D9Shader<IDirect3DVertexShader9>( pDevice, CommonShader ) { }

  };

  class D3D9PixelShader final : public D3D9Shader<IDirect3DPixelShader9> {

  public:

    D3D9PixelShader(
            D3D9DeviceEx*      pDevice,
      const D3D9CommonShader&  CommonShader)
      : D3D9Shader<IDirect3DPixelShader9>( pDevice, CommonShader ) { }

  };

  /**
   * \brief Shader module set
   *
   * Some applications may compile the same shader multiple
   * times, so we should cache the resulting shader modules
   * and reuse them rather than creating new ones. This
   * class is thread-safe.
   */
  class D3D9ShaderModuleSet : public RcObject {

  public:

    void GetShaderModule(
            D3D9DeviceEx*         pDevice,
            D3D9CommonShader*     pShaderModule,
            VkShaderStageFlagBits ShaderStage,
      const DxsoModuleInfo*       pDxbcModuleInfo,
      const void*                 pShaderBytecode);

  private:

    dxvk::mutex m_mutex;

    std::unordered_map<
      DxvkShaderKey,
      D3D9CommonShader,
      DxvkHash, DxvkEq> m_modules;

  };

  template<typename T>
  const D3D9CommonShader* GetCommonShader(const T& pShader) {
    return pShader != nullptr ? pShader->GetCommonShader() : nullptr;
  }

}


code:
#pragma once

#include "d3d9_include.h"

namespace dxvk {

  class DxvkShader;

  namespace D3D9ShaderPermutations {
    enum D3D9ShaderPermutation {
      None,
      FlatShade,
      Count
    };
  }
  using D3D9ShaderPermutation = D3D9ShaderPermutations::D3D9ShaderPermutation;

  using DxsoPermutations = std::array<Rc<DxvkShader>, D3D9ShaderPermutations::Count>;

}


code:
#pragma once

#include "d3d9_include.h"

namespace dxvk {

  class IDirect3DShaderValidator9 : public IUnknown {

  public:

    virtual HRESULT STDMETHODCALLTYPE Begin(
            void* pCallback,
            void* pUserParam,
            DWORD Unknown) = 0;

    virtual HRESULT STDMETHODCALLTYPE Instruction(
      const char*  pUnknown1,
            UINT   Unknown2,
      const DWORD* pInstruction,
            DWORD  InstructionLength) = 0;

    virtual HRESULT STDMETHODCALLTYPE End() = 0;

  };

  class D3D9ShaderValidator final : public ComObjectClamp<IDirect3DShaderValidator9> {

  public:

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

      *ppvObject = ref(this);
      return S_OK;
    }

    HRESULT STDMETHODCALLTYPE Begin(
            void* pCallback,
            void* pUserParam,
            DWORD Unknown) {
      Logger::debug("D3D9ShaderValidator::Begin: Stub");

      return D3D_OK;
    }

    HRESULT STDMETHODCALLTYPE Instruction(
      const char*  pUnknown1,
            UINT   Unknown2,
      const DWORD* pInstruction,
            DWORD  InstructionLength) {
      Logger::debug("D3D9ShaderValidator::Instruction: Stub");

      return D3D_OK;
    }

    HRESULT STDMETHODCALLTYPE End() {
      Logger::debug("D3D9ShaderValidator::End: Stub");

      return D3D_OK;
    }

  };

}


code:
#pragma once

#include <cstdint>

namespace dxvk {

  enum D3D9SpecConstantId : uint32_t {
    AlphaCompareOp  = 0,
    SamplerType     = 1,
    FogEnabled      = 2,
    VertexFogMode   = 3,
    PixelFogMode    = 4,

    PointMode       = 5,
    ProjectionType  = 6,

    VertexShaderBools = 7,
    PixelShaderBools  = 8,
    Fetch4            = 9,

    SamplerDepthMode  = 10,
  };

}


code:
#include "d3d9_state.h"

#include "d3d9_texture.h"

namespace dxvk {

  D3D9CapturableState::D3D9CapturableState() {
    for (uint32_t i = 0; i < streamFreq.size(); i++)
      streamFreq[i] = 1;

    for (uint32_t i = 0; i < enabledLightIndices.size(); i++)
      enabledLightIndices[i] = UINT32_MAX;
  }

  D3D9CapturableState::~D3D9CapturableState() {
    for (uint32_t i = 0; i < textures.size(); i++)
      TextureChangePrivate(textures[i], nullptr);
  }

}


code:
#pragma once

#include "d3d9_caps.h"
#include "d3d9_constant_set.h"
#include "../dxso/dxso_common.h"
#include "../util/util_matrix.h"

#include "d3d9_surface.h"
#include "d3d9_shader.h"
#include "d3d9_vertex_declaration.h"
#include "d3d9_buffer.h"

#include <array>
#include <bitset>
#include <optional>

namespace dxvk {

  static constexpr uint32_t RenderStateCount  = 256;
  static constexpr uint32_t SamplerStateCount = D3DSAMP_DMAPOFFSET + 1;
  static constexpr uint32_t SamplerCount      = caps::MaxTexturesPS + caps::MaxTexturesVS + 1;
  static constexpr uint32_t TextureStageStateCount = DXVK_TSS_COUNT;

  namespace hacks::PointSize {
    static constexpr DWORD AlphaToCoverageDisabled = MAKEFOURCC('A', '2', 'M', '0');
    static constexpr DWORD AlphaToCoverageEnabled  = MAKEFOURCC('A', '2', 'M', '1');
  }

  struct D3D9ClipPlane {
    float coeff[4] = {};
  };

  struct D3D9RenderStateInfo {
    std::array<float, 3> fogColor = { };
    float fogScale   = 0.0f;
    float fogEnd     = 1.0f;
    float fogDensity = 1.0f;

    float alphaRef   = 0.0f;

    float pointSize    = 1.0f;
    float pointSizeMin = 1.0f;
    float pointSizeMax = 64.0f;
    float pointScaleA  = 1.0f;
    float pointScaleB  = 0.0f;
    float pointScaleC  = 0.0f;
  };

  enum class D3D9RenderStateItem {
    FogColor   = 0,
    FogScale   = 1,
    FogEnd,
    FogDensity,
    AlphaRef,

    PointSize,
    PointSizeMin,
    PointSizeMax,
    PointScaleA,
    PointScaleB,
    PointScaleC,

    Count
  };

  // This is needed in fixed function for POSITION_T support.
  // These are constants we need to * and add to move
  // Window Coords -> Real Coords w/ respect to the viewport.
  struct D3D9ViewportInfo {
    Vector4 inverseOffset;
    Vector4 inverseExtent;
  };

  struct D3D9Light {
    D3D9Light(const D3DLIGHT9& light, Matrix4 viewMtx) {
      Diffuse  = Vector4(light.Diffuse.r,  light.Diffuse.g,  light.Diffuse.b,  light.Diffuse.a);
      Specular = Vector4(light.Specular.r, light.Specular.g, light.Specular.b, light.Specular.a);
      Ambient  = Vector4(light.Ambient.r,  light.Ambient.g,  light.Ambient.b,  light.Ambient.a);

      Position  = viewMtx * Vector4(light.Position.x,  light.Position.y,  light.Position.z,  1.0f);
      Direction = Vector4(light.Direction.x, light.Direction.y, light.Direction.z, 0.0f);
      Direction = normalize(viewMtx * Direction);

      Type         = light.Type;
      Range        = light.Range;
      Falloff      = light.Falloff;
      Attenuation0 = light.Attenuation0;
      Attenuation1 = light.Attenuation1;
      Attenuation2 = light.Attenuation2;
      Theta        = cosf(light.Theta / 2.0f);
      Phi          = cosf(light.Phi / 2.0f);
    }

    Vector4 Diffuse;
    Vector4 Specular;
    Vector4 Ambient;

    Vector4 Position;
    Vector4 Direction;

    D3DLIGHTTYPE Type;
    float Range;
    float Falloff;
    float Attenuation0;
    float Attenuation1;
    float Attenuation2;
    float Theta;
    float Phi;
  };

  struct D3D9FixedFunctionVS {
    Matrix4 WorldView;
    Matrix4 NormalMatrix;
    Matrix4 InverseView;
    Matrix4 Projection;

    std::array<Matrix4, 8> TexcoordMatrices;

    D3D9ViewportInfo ViewportInfo;

    Vector4 GlobalAmbient;
    std::array<D3D9Light, caps::MaxEnabledLights> Lights;
    D3DMATERIAL9 Material;
    float TweenFactor;
  };

  struct D3D9FixedFunctionVertexBlendDataHW {
    Matrix4 WorldView[8];
  };

  struct D3D9FixedFunctionVertexBlendDataSW {
    Matrix4 WorldView[256];
  };

  struct D3D9FixedFunctionPS {
    Vector4 textureFactor;
  };

  enum D3D9SharedPSStages {
    D3D9SharedPSStages_Constant,
    D3D9SharedPSStages_BumpEnvMat0,
    D3D9SharedPSStages_BumpEnvMat1,
    D3D9SharedPSStages_BumpEnvLScale,
    D3D9SharedPSStages_BumpEnvLOffset,
    D3D9SharedPSStages_Count,
  };

  struct D3D9SharedPS {
    struct Stage {
      float Constant[4];
      float BumpEnvMat[2][2];
      float BumpEnvLScale;
      float BumpEnvLOffset;
      float Padding[2];
    } Stages[8];
  };

  struct D3D9VBO {
    Com<D3D9VertexBuffer, false> vertexBuffer;

    UINT              offset = 0;
    UINT              stride = 0;
  };

  constexpr D3DLIGHT9 DefaultLight = {
    D3DLIGHT_DIRECTIONAL,     // Type
    {1.0f, 1.0f, 1.0f, 1.0f}, // Diffuse
    {0.0f, 0.0f, 0.0f, 0.0f}, // Specular
    {0.0f, 0.0f, 0.0f, 0.0f}, // Ambient
    {0.0f, 0.0f, 0.0f},       // Position
    {0.0f, 0.0f, 0.0f},       // Direction
    0.0f,                     // Range
    0.0f,                     // Falloff
    0.0f, 0.0f, 0.0f,         // Attenuations [constant, linear, quadratic]
    0.0f,                     // Theta
    0.0f                      // Phi
  };

  struct D3D9CapturableState {
    D3D9CapturableState();

    ~D3D9CapturableState();

    Com<D3D9VertexDecl,  false>                       vertexDecl;
    Com<D3D9IndexBuffer, false>                       indices;

    std::array<DWORD, RenderStateCount>              renderStates = {};

    std::array<
      std::array<DWORD, SamplerStateCount>,
      SamplerCount>                                  samplerStates = {};

    std::array<D3D9VBO, caps::MaxStreams>            vertexBuffers = {};

    std::array<
      IDirect3DBaseTexture9*,
      SamplerCount>                                  textures = {};

    Com<D3D9VertexShader, false>                     vertexShader;
    Com<D3D9PixelShader,  false>                     pixelShader;

    D3DVIEWPORT9                                     viewport = {};
    RECT                                             scissorRect = {};

    std::array<
      D3D9ClipPlane,
      caps::MaxClipPlanes>                           clipPlanes = {};

    std::array<
      std::array<DWORD, TextureStageStateCount>,
      caps::TextureStageCount>                       textureStages = {};

    D3D9ShaderConstantsVSSoftware                    vsConsts;
    D3D9ShaderConstantsPS                            psConsts;

    std::array<UINT, caps::MaxStreams>               streamFreq = {};

    std::array<Matrix4, caps::MaxTransforms>         transforms = {};

    D3DMATERIAL9                                     material = {};

    std::vector<std::optional<D3DLIGHT9>>            lights;
    std::array<DWORD, caps::MaxEnabledLights>        enabledLightIndices;

    bool IsLightEnabled(DWORD Index) {
      const auto& indices = enabledLightIndices;
      return std::find(indices.begin(), indices.end(), Index) != indices.end();
    }
  };

  template <
    DxsoProgramType  ProgramType,
    D3D9ConstantType ConstantType,
    typename         T>
  HRESULT UpdateStateConstants(
          D3D9CapturableState* pState,
          UINT                 StartRegister,
    const T*                   pConstantData,
          UINT                 Count,
          bool                 FloatEmu) {
    auto UpdateHelper = [&] (auto& set) {
      if constexpr (ConstantType == D3D9ConstantType::Float) {

        if (!FloatEmu) {
          size_t size = Count * sizeof(Vector4);

          std::memcpy(set.fConsts[StartRegister].data, pConstantData, size);
        }
        else {
          for (UINT i = 0; i < Count; i++)
            set.fConsts[StartRegister + i] = replaceNaN(pConstantData + (i * 4));
        }
      }
      else if constexpr (ConstantType == D3D9ConstantType::Int) {
        size_t size = Count * sizeof(Vector4i);

        std::memcpy(set.iConsts[StartRegister].data, pConstantData, size);
      }
      else {
        for (uint32_t i = 0; i < Count; i++) {
          const uint32_t constantIdx = StartRegister + i;
          const uint32_t arrayIdx    = constantIdx / 32;
          const uint32_t bitIdx      = constantIdx % 32;

          const uint32_t bit = 1u << bitIdx;

          set.bConsts[arrayIdx] &= ~bit;
          if (pConstantData[i])
            set.bConsts[arrayIdx] |= bit;
        }
      }

      return D3D_OK;
    };

    return ProgramType == DxsoProgramTypes::VertexShader
      ? UpdateHelper(pState->vsConsts)
      : UpdateHelper(pState->psConsts);
  }

  struct Direct3DState9 : public D3D9CapturableState {

    std::array<Com<D3D9Surface, false>, caps::MaxSimultaneousRenderTargets> renderTargets;
    Com<D3D9Surface, false> depthStencil;

  };

  struct D3D9InputAssemblyState {
    D3DPRIMITIVETYPE primitiveType = D3DPRIMITIVETYPE(0);
    uint32_t streamsInstanced = 0;
    uint32_t streamsUsed      = 0;
  };

}


code:
#include "d3d9_stateblock.h"

#include "d3d9_caps.h"
#include "d3d9_device.h"
#include "d3d9_vertex_declaration.h"
#include "d3d9_buffer.h"
#include "d3d9_shader.h"
#include "d3d9_texture.h"

#include "d3d9_util.h"

namespace dxvk {

  D3D9StateBlock::D3D9StateBlock(D3D9DeviceEx* pDevice, D3D9StateBlockType Type)
    : D3D9StateBlockBase(pDevice)
    , m_deviceState     (pDevice->GetRawState()) {
    CaptureType(Type);
  }

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

    *ppvObject = nullptr;

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

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

  HRESULT STDMETHODCALLTYPE D3D9StateBlock::Capture() {
    if (m_captures.flags.test(D3D9CapturedStateFlag::VertexDecl))
      SetVertexDeclaration(m_deviceState->vertexDecl.ptr());

    ApplyOrCapture<D3D9StateFunction::Capture>();

    return D3D_OK;
  }

  HRESULT STDMETHODCALLTYPE D3D9StateBlock::Apply() {
    m_applying = true;

    if (m_captures.flags.test(D3D9CapturedStateFlag::VertexDecl) && m_state.vertexDecl != nullptr)
      m_parent->SetVertexDeclaration(m_state.vertexDecl.ptr());

    ApplyOrCapture<D3D9StateFunction::Apply>();
    m_applying = false;

    return D3D_OK;
  }

  HRESULT D3D9StateBlock::SetVertexDeclaration(D3D9VertexDecl* pDecl) {
    m_state.vertexDecl = pDecl;

    m_captures.flags.set(D3D9CapturedStateFlag::VertexDecl);
    return D3D_OK;
  }

  HRESULT D3D9StateBlock::SetIndices(D3D9IndexBuffer* pIndexData) {
    m_state.indices = pIndexData;

    m_captures.flags.set(D3D9CapturedStateFlag::Indices);
    return D3D_OK;
  }

  HRESULT D3D9StateBlock::SetRenderState(D3DRENDERSTATETYPE State, DWORD Value) {
    m_state.renderStates[State] = Value;

    m_captures.flags.set(D3D9CapturedStateFlag::RenderStates);
    m_captures.renderStates.set(State, true);
    return D3D_OK;
  }

  HRESULT D3D9StateBlock::SetStateSamplerState(
          DWORD               StateSampler,
          D3DSAMPLERSTATETYPE Type,
          DWORD               Value) {
    m_state.samplerStates[StateSampler][Type] = Value;

    m_captures.flags.set(D3D9CapturedStateFlag::SamplerStates);
    m_captures.samplers.set(StateSampler, true);
    m_captures.samplerStates[StateSampler].set(Type, true);
    return D3D_OK;
  }

  HRESULT D3D9StateBlock::SetStreamSource(
          UINT                StreamNumber,
          D3D9VertexBuffer*   pStreamData,
          UINT                OffsetInBytes,
          UINT                Stride) {
    m_state.vertexBuffers[StreamNumber].vertexBuffer = pStreamData;

    m_state.vertexBuffers[StreamNumber].offset = OffsetInBytes;
    m_state.vertexBuffers[StreamNumber].stride = Stride;

    m_captures.flags.set(D3D9CapturedStateFlag::VertexBuffers);
    m_captures.vertexBuffers.set(StreamNumber, true);
    return D3D_OK;
  }

  HRESULT D3D9StateBlock::SetStreamSourceFreq(UINT StreamNumber, UINT Setting) {
    m_state.streamFreq[StreamNumber] = Setting;

    m_captures.flags.set(D3D9CapturedStateFlag::StreamFreq);
    m_captures.streamFreq.set(StreamNumber, true);
    return D3D_OK;
  }

  HRESULT D3D9StateBlock::SetStateTexture(DWORD StateSampler, IDirect3DBaseTexture9* pTexture) {
    TextureChangePrivate(m_state.textures[StateSampler], pTexture);

    m_captures.flags.set(D3D9CapturedStateFlag::Textures);
    m_captures.textures.set(StateSampler, true);
    return D3D_OK;
  }

  HRESULT D3D9StateBlock::SetVertexShader(D3D9VertexShader* pShader) {
    m_state.vertexShader = pShader;

    m_captures.flags.set(D3D9CapturedStateFlag::VertexShader);
    return D3D_OK;
  }

  HRESULT D3D9StateBlock::SetPixelShader(D3D9PixelShader* pShader) {
    m_state.pixelShader = pShader;

    m_captures.flags.set(D3D9CapturedStateFlag::PixelShader);
    return D3D_OK;
  }

  HRESULT D3D9StateBlock::SetMaterial(const D3DMATERIAL9* pMaterial) {
    m_state.material = *pMaterial;

    m_captures.flags.set(D3D9CapturedStateFlag::Material);
    return D3D_OK;
  }

  HRESULT D3D9StateBlock::SetStateTransform(uint32_t idx, const D3DMATRIX* pMatrix) {
    m_state.transforms[idx] = ConvertMatrix(pMatrix);

    m_captures.flags.set(D3D9CapturedStateFlag::Transforms);
    m_captures.transforms.set(idx, true);
    return D3D_OK;
  }

  HRESULT D3D9StateBlock::SetStateTextureStageState(
          DWORD                      Stage,
          D3D9TextureStageStateTypes Type,
          DWORD                      Value) {
    m_state.textureStages[Stage][Type] = Value;

    m_captures.flags.set(D3D9CapturedStateFlag::TextureStages);
    m_captures.textureStages.set(Stage, true);
    m_captures.textureStageStates[Stage].set(Type, true);
    return D3D_OK;
  }

  HRESULT D3D9StateBlock::MultiplyStateTransform(uint32_t idx, const D3DMATRIX* pMatrix) {
    m_state.transforms[idx] = m_state.transforms[idx] * ConvertMatrix(pMatrix);

    m_captures.flags.set(D3D9CapturedStateFlag::Transforms);
    m_captures.transforms.set(idx, true);
    return D3D_OK;
  }

  HRESULT D3D9StateBlock::SetViewport(const D3DVIEWPORT9* pViewport) {
    m_state.viewport = *pViewport;

    m_captures.flags.set(D3D9CapturedStateFlag::Viewport);
    return D3D_OK;
  }

  HRESULT D3D9StateBlock::SetScissorRect(const RECT* pRect) {
    m_state.scissorRect = *pRect;

    m_captures.flags.set(D3D9CapturedStateFlag::ScissorRect);
    return D3D_OK;
  }

  HRESULT D3D9StateBlock::SetClipPlane(DWORD Index, const float* pPlane) {
    for (uint32_t i = 0; i < 4; i++)
      m_state.clipPlanes[Index].coeff[i] = pPlane[i];

    m_captures.flags.set(D3D9CapturedStateFlag::ClipPlanes);
    m_captures.clipPlanes.set(Index, true);
    return D3D_OK;
  }

  HRESULT D3D9StateBlock::SetVertexShaderConstantF(
          UINT   StartRegister,
    const float* pConstantData,
          UINT   Vector4fCount) {
    return SetShaderConstants<
      DxsoProgramTypes::VertexShader,
      D3D9ConstantType::Float>(
        StartRegister,
        pConstantData,
        Vector4fCount);
  }

  HRESULT D3D9StateBlock::SetVertexShaderConstantI(
          UINT StartRegister,
    const int* pConstantData,
          UINT Vector4iCount) {
    return SetShaderConstants<
      DxsoProgramTypes::VertexShader,
      D3D9ConstantType::Int>(
        StartRegister,
        pConstantData,
        Vector4iCount);
  }

  HRESULT D3D9StateBlock::SetVertexShaderConstantB(
          UINT  StartRegister,
    const BOOL* pConstantData,
          UINT  BoolCount) {
    return SetShaderConstants<
      DxsoProgramTypes::VertexShader,
      D3D9ConstantType::Bool>(
        StartRegister,
        pConstantData,
        BoolCount);
  }

  HRESULT D3D9StateBlock::SetPixelShaderConstantF(
          UINT   StartRegister,
    const float* pConstantData,
          UINT   Vector4fCount) {
    return SetShaderConstants<
      DxsoProgramTypes::PixelShader,
      D3D9ConstantType::Float>(
        StartRegister,
        pConstantData,
        Vector4fCount);
  }

  HRESULT D3D9StateBlock::SetPixelShaderConstantI(
          UINT StartRegister,
    const int* pConstantData,
          UINT Vector4iCount) {
    return SetShaderConstants<
      DxsoProgramTypes::PixelShader,
      D3D9ConstantType::Int>(
        StartRegister,
        pConstantData,
        Vector4iCount);
  }

  HRESULT D3D9StateBlock::SetPixelShaderConstantB(
          UINT  StartRegister,
    const BOOL* pConstantData,
          UINT  BoolCount) {
    return SetShaderConstants<
      DxsoProgramTypes::PixelShader,
      D3D9ConstantType::Bool>(
        StartRegister,
        pConstantData,
        BoolCount);
  }

  HRESULT D3D9StateBlock::SetVertexBoolBitfield(uint32_t idx, uint32_t mask, uint32_t bits) {
    m_state.vsConsts.bConsts[idx] &= ~mask;
    m_state.vsConsts.bConsts[idx] |= bits & mask;
    return D3D_OK;
  }

  HRESULT D3D9StateBlock::SetPixelBoolBitfield(uint32_t idx, uint32_t mask, uint32_t bits) {
    m_state.psConsts.bConsts[idx] &= ~mask;
    m_state.psConsts.bConsts[idx] |= bits & mask;
    return D3D_OK;
  }

  void D3D9StateBlock::CapturePixelRenderStates() {
    m_captures.flags.set(D3D9CapturedStateFlag::RenderStates);

    m_captures.renderStates.set(D3DRS_ZENABLE, true);
    m_captures.renderStates.set(D3DRS_FILLMODE, true);
    m_captures.renderStates.set(D3DRS_SHADEMODE, true);
    m_captures.renderStates.set(D3DRS_ZWRITEENABLE, true);
    m_captures.renderStates.set(D3DRS_ALPHATESTENABLE, true);
    m_captures.renderStates.set(D3DRS_LASTPIXEL, true);
    m_captures.renderStates.set(D3DRS_SRCBLEND, true);
    m_captures.renderStates.set(D3DRS_DESTBLEND, true);
    m_captures.renderStates.set(D3DRS_ZFUNC, true);
    m_captures.renderStates.set(D3DRS_ALPHAREF, true);
    m_captures.renderStates.set(D3DRS_ALPHAFUNC, true);
    m_captures.renderStates.set(D3DRS_DITHERENABLE, true);
    m_captures.renderStates.set(D3DRS_FOGSTART, true);
    m_captures.renderStates.set(D3DRS_FOGEND, true);
    m_captures.renderStates.set(D3DRS_FOGDENSITY, true);
    m_captures.renderStates.set(D3DRS_ALPHABLENDENABLE, true);
    m_captures.renderStates.set(D3DRS_DEPTHBIAS, true);
    m_captures.renderStates.set(D3DRS_STENCILENABLE, true);
    m_captures.renderStates.set(D3DRS_STENCILFAIL, true);
    m_captures.renderStates.set(D3DRS_STENCILZFAIL, true);
    m_captures.renderStates.set(D3DRS_STENCILPASS, true);
    m_captures.renderStates.set(D3DRS_STENCILFUNC, true);
    m_captures.renderStates.set(D3DRS_STENCILREF, true);
    m_captures.renderStates.set(D3DRS_STENCILMASK, true);
    m_captures.renderStates.set(D3DRS_STENCILWRITEMASK, true);
    m_captures.renderStates.set(D3DRS_TEXTUREFACTOR, true);
    m_captures.renderStates.set(D3DRS_WRAP0, true);
    m_captures.renderStates.set(D3DRS_WRAP1, true);
    m_captures.renderStates.set(D3DRS_WRAP2, true);
    m_captures.renderStates.set(D3DRS_WRAP3, true);
    m_captures.renderStates.set(D3DRS_WRAP4, true);
    m_captures.renderStates.set(D3DRS_WRAP5, true);
    m_captures.renderStates.set(D3DRS_WRAP6, true);
    m_captures.renderStates.set(D3DRS_WRAP7, true);
    m_captures.renderStates.set(D3DRS_WRAP8, true);
    m_captures.renderStates.set(D3DRS_WRAP9, true);
    m_captures.renderStates.set(D3DRS_WRAP10, true);
    m_captures.renderStates.set(D3DRS_WRAP11, true);
    m_captures.renderStates.set(D3DRS_WRAP12, true);
    m_captures.renderStates.set(D3DRS_WRAP13, true);
    m_captures.renderStates.set(D3DRS_WRAP14, true);
    m_captures.renderStates.set(D3DRS_WRAP15, true);
    m_captures.renderStates.set(D3DRS_COLORWRITEENABLE, true);
    m_captures.renderStates.set(D3DRS_BLENDOP, true);
    m_captures.renderStates.set(D3DRS_SCISSORTESTENABLE, true);
    m_captures.renderStates.set(D3DRS_SLOPESCALEDEPTHBIAS, true);
    m_captures.renderStates.set(D3DRS_ANTIALIASEDLINEENABLE, true);
    m_captures.renderStates.set(D3DRS_TWOSIDEDSTENCILMODE, true);
    m_captures.renderStates.set(D3DRS_CCW_STENCILFAIL, true);
    m_captures.renderStates.set(D3DRS_CCW_STENCILZFAIL, true);
    m_captures.renderStates.set(D3DRS_CCW_STENCILPASS, true);
    m_captures.renderStates.set(D3DRS_CCW_STENCILFUNC, true);
    m_captures.renderStates.set(D3DRS_COLORWRITEENABLE1, true);
    m_captures.renderStates.set(D3DRS_COLORWRITEENABLE2, true);
    m_captures.renderStates.set(D3DRS_COLORWRITEENABLE3, true);
    m_captures.renderStates.set(D3DRS_BLENDFACTOR, true);
    m_captures.renderStates.set(D3DRS_SRGBWRITEENABLE, true);
    m_captures.renderStates.set(D3DRS_SEPARATEALPHABLENDENABLE, true);
    m_captures.renderStates.set(D3DRS_SRCBLENDALPHA, true);
    m_captures.renderStates.set(D3DRS_DESTBLENDALPHA, true);
    m_captures.renderStates.set(D3DRS_BLENDOPALPHA, true);
  }

  void D3D9StateBlock::CapturePixelSamplerStates() {
    m_captures.flags.set(D3D9CapturedStateFlag::SamplerStates);

    for (uint32_t i = 0; i < caps::MaxTexturesPS + 1; i++) {
      m_captures.samplers.set(i, true);

      m_captures.samplerStates[i].set(D3DSAMP_ADDRESSU, true);
      m_captures.samplerStates[i].set(D3DSAMP_ADDRESSV, true);
      m_captures.samplerStates[i].set(D3DSAMP_ADDRESSW, true);
      m_captures.samplerStates[i].set(D3DSAMP_BORDERCOLOR, true);
      m_captures.samplerStates[i].set(D3DSAMP_MAGFILTER, true);
      m_captures.samplerStates[i].set(D3DSAMP_MINFILTER, true);
      m_captures.samplerStates[i].set(D3DSAMP_MIPFILTER, true);
      m_captures.samplerStates[i].set(D3DSAMP_MIPMAPLODBIAS, true);
      m_captures.samplerStates[i].set(D3DSAMP_MAXMIPLEVEL, true);
      m_captures.samplerStates[i].set(D3DSAMP_MAXANISOTROPY, true);
      m_captures.samplerStates[i].set(D3DSAMP_SRGBTEXTURE, true);
      m_captures.samplerStates[i].set(D3DSAMP_ELEMENTINDEX, true);
    }
  }

  void D3D9StateBlock::CapturePixelShaderStates() {
    m_captures.flags.set(D3D9CapturedStateFlag::PixelShader);
    m_captures.flags.set(D3D9CapturedStateFlag::PsConstants);

    m_captures.psConsts.fConsts.setAll();
    m_captures.psConsts.iConsts.setAll();
    m_captures.psConsts.bConsts.setAll();
  }

  void D3D9StateBlock::CaptureVertexRenderStates() {
    m_captures.flags.set(D3D9CapturedStateFlag::RenderStates);

    m_captures.renderStates.set(D3DRS_CULLMODE, true);
    m_captures.renderStates.set(D3DRS_FOGENABLE, true);
    m_captures.renderStates.set(D3DRS_FOGCOLOR, true);
    m_captures.renderStates.set(D3DRS_FOGTABLEMODE, true);
    m_captures.renderStates.set(D3DRS_FOGSTART, true);
    m_captures.renderStates.set(D3DRS_FOGEND, true);
    m_captures.renderStates.set(D3DRS_FOGDENSITY, true);
    m_captures.renderStates.set(D3DRS_RANGEFOGENABLE, true);
    m_captures.renderStates.set(D3DRS_AMBIENT, true);
    m_captures.renderStates.set(D3DRS_COLORVERTEX, true);
    m_captures.renderStates.set(D3DRS_FOGVERTEXMODE, true);
    m_captures.renderStates.set(D3DRS_CLIPPING, true);
    m_captures.renderStates.set(D3DRS_LIGHTING, true);
    m_captures.renderStates.set(D3DRS_LOCALVIEWER, true);
    m_captures.renderStates.set(D3DRS_EMISSIVEMATERIALSOURCE, true);
    m_captures.renderStates.set(D3DRS_AMBIENTMATERIALSOURCE, true);
    m_captures.renderStates.set(D3DRS_DIFFUSEMATERIALSOURCE, true);
    m_captures.renderStates.set(D3DRS_SPECULARMATERIALSOURCE, true);
    m_captures.renderStates.set(D3DRS_VERTEXBLEND, true);
    m_captures.renderStates.set(D3DRS_CLIPPLANEENABLE, true);
    m_captures.renderStates.set(D3DRS_POINTSIZE, true);
    m_captures.renderStates.set(D3DRS_POINTSIZE_MIN, true);
    m_captures.renderStates.set(D3DRS_POINTSPRITEENABLE, true);
    m_captures.renderStates.set(D3DRS_POINTSCALEENABLE, true);
    m_captures.renderStates.set(D3DRS_POINTSCALE_A, true);
    m_captures.renderStates.set(D3DRS_POINTSCALE_B, true);
    m_captures.renderStates.set(D3DRS_POINTSCALE_C, true);
    m_captures.renderStates.set(D3DRS_MULTISAMPLEANTIALIAS, true);
    m_captures.renderStates.set(D3DRS_MULTISAMPLEMASK, true);
    m_captures.renderStates.set(D3DRS_PATCHEDGESTYLE, true);
    m_captures.renderStates.set(D3DRS_POINTSIZE_MAX, true);
    m_captures.renderStates.set(D3DRS_INDEXEDVERTEXBLENDENABLE, true);
    m_captures.renderStates.set(D3DRS_TWEENFACTOR, true);
    m_captures.renderStates.set(D3DRS_POSITIONDEGREE, true);
    m_captures.renderStates.set(D3DRS_NORMALDEGREE, true);
    m_captures.renderStates.set(D3DRS_MINTESSELLATIONLEVEL, true);
    m_captures.renderStates.set(D3DRS_MAXTESSELLATIONLEVEL, true);
    m_captures.renderStates.set(D3DRS_ADAPTIVETESS_X, true);
    m_captures.renderStates.set(D3DRS_ADAPTIVETESS_Y, true);
    m_captures.renderStates.set(D3DRS_ADAPTIVETESS_Z, true);
    m_captures.renderStates.set(D3DRS_ADAPTIVETESS_W, true);
    m_captures.renderStates.set(D3DRS_ENABLEADAPTIVETESSELLATION, true);
    m_captures.renderStates.set(D3DRS_NORMALIZENORMALS, true);
    m_captures.renderStates.set(D3DRS_SPECULARENABLE, true);
    m_captures.renderStates.set(D3DRS_SHADEMODE, true);
  }

  void D3D9StateBlock::CaptureVertexSamplerStates() {
    m_captures.flags.set(D3D9CapturedStateFlag::SamplerStates);

    for (uint32_t i = caps::MaxTexturesPS + 1; i < SamplerCount; i++) {
      m_captures.samplers.set(i, true);
      m_captures.samplerStates[i].set(D3DSAMP_DMAPOFFSET, true);
    }
  }

  void D3D9StateBlock::CaptureVertexShaderStates() {
    m_captures.flags.set(D3D9CapturedStateFlag::VertexShader);
    m_captures.flags.set(D3D9CapturedStateFlag::VsConstants);

    for (uint32_t i = 0; i < m_parent->GetVertexConstantLayout().floatCount / 32; i++)
      m_captures.vsConsts.fConsts.dword(i) = std::numeric_limits<uint32_t>::max();

    for (uint32_t i = 0; i < m_parent->GetVertexConstantLayout().intCount / 32; i++)
      m_captures.vsConsts.iConsts.dword(i) = std::numeric_limits<uint32_t>::max();

    for (uint32_t i = 0; i < m_parent->GetVertexConstantLayout().bitmaskCount; i++)
      m_captures.vsConsts.bConsts.dword(i) = std::numeric_limits<uint32_t>::max();
  }

  void D3D9StateBlock::CaptureType(D3D9StateBlockType Type) {
    if (Type == D3D9StateBlockType::PixelState || Type == D3D9StateBlockType::All) {
      CapturePixelRenderStates();
      CapturePixelSamplerStates();
      CapturePixelShaderStates();

      m_captures.flags.set(D3D9CapturedStateFlag::TextureStages);
      m_captures.textureStages.setAll();
      for (auto& stage : m_captures.textureStageStates)
        stage.setAll();
    }

    if (Type == D3D9StateBlockType::VertexState || Type == D3D9StateBlockType::All) {
      CaptureVertexRenderStates();
      CaptureVertexSamplerStates();
      CaptureVertexShaderStates();

      m_captures.flags.set(D3D9CapturedStateFlag::VertexDecl);
      m_captures.flags.set(D3D9CapturedStateFlag::StreamFreq);

      for (uint32_t i = 0; i < caps::MaxStreams; i++)
        m_captures.streamFreq.set(i, true);
    }

    if (Type == D3D9StateBlockType::All) {
      m_captures.flags.set(D3D9CapturedStateFlag::Textures);
      m_captures.textures.setAll();

      m_captures.flags.set(D3D9CapturedStateFlag::VertexBuffers);
      m_captures.vertexBuffers.setAll();

      m_captures.flags.set(D3D9CapturedStateFlag::Indices);
      m_captures.flags.set(D3D9CapturedStateFlag::Viewport);
      m_captures.flags.set(D3D9CapturedStateFlag::ScissorRect);

      m_captures.flags.set(D3D9CapturedStateFlag::ClipPlanes);
      m_captures.clipPlanes.setAll();

      m_captures.flags.set(D3D9CapturedStateFlag::Transforms);
      m_captures.transforms.setAll();

      m_captures.flags.set(D3D9CapturedStateFlag::Material);
    }

    if (Type != D3D9StateBlockType::None)
      this->Capture();
  }

}


code:
#pragma once

#include "d3d9_device_child.h"
#include "d3d9_device.h"
#include "d3d9_state.h"

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

namespace dxvk {

  enum class D3D9CapturedStateFlag : uint32_t {
    VertexDecl,
    Indices,
    RenderStates,
    SamplerStates,
    VertexBuffers,
    Textures,
    VertexShader,
    PixelShader,
    Viewport,
    ScissorRect,
    ClipPlanes,
    VsConstants,
    PsConstants,
    StreamFreq,
    Transforms,
    TextureStages,
    Material
  };

  using D3D9CapturedStateFlags = Flags<D3D9CapturedStateFlag>;

  struct D3D9StateCaptures {
    D3D9CapturedStateFlags flags;

    bit::bitset<RenderStateCount>                       renderStates;

    bit::bitset<SamplerCount>                           samplers;
    std::array<
      bit::bitset<SamplerStateCount>,
      SamplerCount>                                     samplerStates;

    bit::bitset<caps::MaxStreams>                       vertexBuffers;
    bit::bitset<SamplerCount>                           textures;
    bit::bitset<caps::MaxClipPlanes>                    clipPlanes;
    bit::bitset<caps::MaxStreams>                       streamFreq;
    bit::bitset<caps::MaxTransforms>                    transforms;
    bit::bitset<caps::TextureStageCount>                textureStages;
    std::array<
      bit::bitset<TextureStageStateCount>,
      caps::TextureStageCount>                          textureStageStates;

    struct {
      bit::bitset<caps::MaxFloatConstantsSoftware>      fConsts;
      bit::bitset<caps::MaxOtherConstantsSoftware>      iConsts;
      bit::bitset<caps::MaxOtherConstantsSoftware>      bConsts;
    } vsConsts;

    struct {
      bit::bitset<caps::MaxFloatConstantsPS>            fConsts;
      bit::bitset<caps::MaxOtherConstants>              iConsts;
      bit::bitset<caps::MaxOtherConstants>              bConsts;
    } psConsts;
  };

  enum class D3D9StateBlockType :uint32_t {
    None,
    VertexState,
    PixelState,
    All
  };

  inline D3D9StateBlockType ConvertStateBlockType(D3DSTATEBLOCKTYPE type) {
    switch (type) {
      case D3DSBT_PIXELSTATE:  return D3D9StateBlockType::PixelState;
      case D3DSBT_VERTEXSTATE: return D3D9StateBlockType::VertexState;
      default:
      case D3DSBT_ALL:         return D3D9StateBlockType::All;
    }
  }

  using D3D9StateBlockBase = D3D9DeviceChild<IDirect3DStateBlock9>;
  class D3D9StateBlock : public D3D9StateBlockBase {

  public:

    D3D9StateBlock(D3D9DeviceEx* pDevice, D3D9StateBlockType Type);

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

    HRESULT STDMETHODCALLTYPE Capture() final;
    HRESULT STDMETHODCALLTYPE Apply() final;

    HRESULT SetVertexDeclaration(D3D9VertexDecl* pDecl);

    HRESULT SetIndices(D3D9IndexBuffer* pIndexData);

    HRESULT SetRenderState(D3DRENDERSTATETYPE State, DWORD Value);

    HRESULT SetStateSamplerState(
            DWORD               StateSampler,
            D3DSAMPLERSTATETYPE Type,
            DWORD               Value);

    HRESULT SetStreamSource(
            UINT               StreamNumber,
            D3D9VertexBuffer*  pStreamData,
            UINT               OffsetInBytes,
            UINT               Stride);

    HRESULT SetStreamSourceFreq(UINT StreamNumber, UINT Setting);

    HRESULT SetStateTexture(DWORD StateSampler, IDirect3DBaseTexture9* pTexture);

    HRESULT SetVertexShader(D3D9VertexShader* pShader);

    HRESULT SetPixelShader(D3D9PixelShader* pShader);

    HRESULT SetMaterial(const D3DMATERIAL9* pMaterial);

    HRESULT SetStateTransform(uint32_t idx, const D3DMATRIX* pMatrix);

    HRESULT SetStateTextureStageState(
            DWORD                      Stage,
            D3D9TextureStageStateTypes Type,
            DWORD                      Value);

    HRESULT MultiplyStateTransform(uint32_t idx, const D3DMATRIX* pMatrix);

    HRESULT SetViewport(const D3DVIEWPORT9* pViewport);

    HRESULT SetScissorRect(const RECT* pRect);

    HRESULT SetClipPlane(DWORD Index, const float* pPlane);

    HRESULT SetVertexShaderConstantF(
            UINT   StartRegister,
      const float* pConstantData,
            UINT   Vector4fCount);

    HRESULT SetVertexShaderConstantI(
            UINT StartRegister,
      const int* pConstantData,
            UINT Vector4iCount);

    HRESULT SetVertexShaderConstantB(
            UINT  StartRegister,
      const BOOL* pConstantData,
            UINT  BoolCount);

    HRESULT SetPixelShaderConstantF(
            UINT   StartRegister,
      const float* pConstantData,
            UINT   Vector4fCount);

    HRESULT SetPixelShaderConstantI(
            UINT StartRegister,
      const int* pConstantData,
            UINT Vector4iCount);

    HRESULT SetPixelShaderConstantB(
            UINT  StartRegister,
      const BOOL* pConstantData,
            UINT  BoolCount);

    enum class D3D9StateFunction {
      Apply,
      Capture
    };

    template <typename Dst, typename Src>
    void ApplyOrCapture(Dst* dst, const Src* src) {
      if (m_captures.flags.test(D3D9CapturedStateFlag::StreamFreq)) {
        for (uint32_t idx : bit::BitMask(m_captures.streamFreq.dword(0)))
          dst->SetStreamSourceFreq(idx, src->streamFreq[idx]);
      }

      if (m_captures.flags.test(D3D9CapturedStateFlag::Indices))
        dst->SetIndices(src->indices.ptr());

      if (m_captures.flags.test(D3D9CapturedStateFlag::RenderStates)) {
        for (uint32_t i = 0; i < m_captures.renderStates.dwordCount(); i++) {
          for (uint32_t rs : bit::BitMask(m_captures.renderStates.dword(i))) {
            uint32_t idx = i * 32 + rs;

            dst->SetRenderState(D3DRENDERSTATETYPE(idx), src->renderStates[idx]);
          }
        }
      }

      if (m_captures.flags.test(D3D9CapturedStateFlag::SamplerStates)) {
        for (uint32_t samplerIdx : bit::BitMask(m_captures.samplers.dword(0))) {
          for (uint32_t stateIdx : bit::BitMask(m_captures.samplerStates[samplerIdx].dword(0)))
            dst->SetStateSamplerState(samplerIdx, D3DSAMPLERSTATETYPE(stateIdx), src->samplerStates[samplerIdx][stateIdx]);
        }
      }

      if (m_captures.flags.test(D3D9CapturedStateFlag::VertexBuffers)) {
        for (uint32_t idx : bit::BitMask(m_captures.vertexBuffers.dword(0))) {
          const auto& vbo = src->vertexBuffers[idx];
          dst->SetStreamSource(
            idx,
            vbo.vertexBuffer.ptr(),
            vbo.offset,
            vbo.stride);
        }
      }

      if (m_captures.flags.test(D3D9CapturedStateFlag::Material))
        dst->SetMaterial(&src->material);

      if (m_captures.flags.test(D3D9CapturedStateFlag::Textures)) {
        for (uint32_t idx : bit::BitMask(m_captures.textures.dword(0)))
          dst->SetStateTexture(idx, src->textures[idx]);
      }

      if (m_captures.flags.test(D3D9CapturedStateFlag::VertexShader))
        dst->SetVertexShader(src->vertexShader.ptr());

      if (m_captures.flags.test(D3D9CapturedStateFlag::PixelShader))
        dst->SetPixelShader(src->pixelShader.ptr());

      if (m_captures.flags.test(D3D9CapturedStateFlag::Transforms)) {
        for (uint32_t i = 0; i < m_captures.transforms.dwordCount(); i++) {
          for (uint32_t trans : bit::BitMask(m_captures.transforms.dword(i))) {
            uint32_t idx = i * 32 + trans;

            dst->SetStateTransform(idx, reinterpret_cast<const D3DMATRIX*>(&src->transforms[idx]));
          }
        }
      }

      if (m_captures.flags.test(D3D9CapturedStateFlag::TextureStages)) {
        for (uint32_t stageIdx : bit::BitMask(m_captures.textureStages.dword(0))) {
          for (uint32_t stateIdx : bit::BitMask(m_captures.textureStageStates[stageIdx].dword(0)))
            dst->SetStateTextureStageState(stageIdx, D3D9TextureStageStateTypes(stateIdx), src->textureStages[stageIdx][stateIdx]);
        }
      }

      if (m_captures.flags.test(D3D9CapturedStateFlag::Viewport))
        dst->SetViewport(&src->viewport);

      if (m_captures.flags.test(D3D9CapturedStateFlag::ScissorRect))
        dst->SetScissorRect(&src->scissorRect);

      if (m_captures.flags.test(D3D9CapturedStateFlag::ClipPlanes)) {
        for (uint32_t idx : bit::BitMask(m_captures.clipPlanes.dword(0)))
          dst->SetClipPlane(idx, src->clipPlanes[idx].coeff);
      }

      if (m_captures.flags.test(D3D9CapturedStateFlag::VsConstants)) {
        for (uint32_t i = 0; i < m_captures.vsConsts.fConsts.dwordCount(); i++) {
          for (uint32_t consts : bit::BitMask(m_captures.vsConsts.fConsts.dword(i))) {
            uint32_t idx = i * 32 + consts;

            dst->SetVertexShaderConstantF(idx, (float*)&src->vsConsts.fConsts[idx], 1);
          }
        }

        for (uint32_t i = 0; i < m_captures.vsConsts.iConsts.dwordCount(); i++) {
          for (uint32_t consts : bit::BitMask(m_captures.vsConsts.iConsts.dword(i))) {
            uint32_t idx = i * 32 + consts;

            dst->SetVertexShaderConstantI(idx, (int*)&src->vsConsts.iConsts[idx], 1);
          }
        }

        if (m_captures.vsConsts.bConsts.any()) {
          for (uint32_t i = 0; i < m_captures.vsConsts.bConsts.dwordCount(); i++)
            dst->SetVertexBoolBitfield(i, m_captures.vsConsts.bConsts.dword(i), src->vsConsts.bConsts[i]);
        }
      }

      if (m_captures.flags.test(D3D9CapturedStateFlag::PsConstants)) {
        for (uint32_t i = 0; i < m_captures.psConsts.fConsts.dwordCount(); i++) {
          for (uint32_t consts : bit::BitMask(m_captures.psConsts.fConsts.dword(i))) {
            uint32_t idx = i * 32 + consts;

            dst->SetPixelShaderConstantF(idx, (float*)&src->psConsts.fConsts[idx], 1);
          }
        }

        for (uint32_t i = 0; i < m_captures.psConsts.iConsts.dwordCount(); i++) {
          for (uint32_t consts : bit::BitMask(m_captures.psConsts.iConsts.dword(i))) {
            uint32_t idx = i * 32 + consts;

            dst->SetPixelShaderConstantI(idx, (int*)&src->psConsts.iConsts[idx], 1);
          }
        }

        if (m_captures.psConsts.bConsts.any()) {
          for (uint32_t i = 0; i < m_captures.psConsts.bConsts.dwordCount(); i++)
            dst->SetPixelBoolBitfield(i, m_captures.psConsts.bConsts.dword(i), src->psConsts.bConsts[i]);
        }
      }
    }

    template <D3D9StateFunction Func>
    void ApplyOrCapture() {
      if      constexpr (Func == D3D9StateFunction::Apply)
        ApplyOrCapture(m_parent, &m_state);
      else if constexpr (Func == D3D9StateFunction::Capture)
        ApplyOrCapture(this, m_deviceState);
    }

    template <
      DxsoProgramType  ProgramType,
      D3D9ConstantType ConstantType,
      typename         T>
    HRESULT SetShaderConstants(
            UINT  StartRegister,
      const T*    pConstantData,
            UINT  Count) {
      auto SetHelper = [&](auto& setCaptures) {
        if constexpr (ProgramType == DxsoProgramTypes::VertexShader)
          m_captures.flags.set(D3D9CapturedStateFlag::VsConstants);
        else
          m_captures.flags.set(D3D9CapturedStateFlag::PsConstants);

        for (uint32_t i = 0; i < Count; i++) {
          uint32_t reg = StartRegister + i;
          if      constexpr (ConstantType == D3D9ConstantType::Float)
            setCaptures.fConsts.set(reg, true);
          else if constexpr (ConstantType == D3D9ConstantType::Int)
            setCaptures.iConsts.set(reg, true);
          else if constexpr (ConstantType == D3D9ConstantType::Bool)
            setCaptures.bConsts.set(reg, true);
        }

        UpdateStateConstants<
          ProgramType,
          ConstantType,
          T>(
            &m_state,
            StartRegister,
            pConstantData,
            Count,
            false);

        return D3D_OK;
      };

      return ProgramType == DxsoProgramTypes::VertexShader
        ? SetHelper(m_captures.vsConsts)
        : SetHelper(m_captures.psConsts);
    }

    HRESULT SetVertexBoolBitfield(uint32_t idx, uint32_t mask, uint32_t bits);
    HRESULT SetPixelBoolBitfield (uint32_t idx, uint32_t mask, uint32_t bits);

    inline bool IsApplying() {
      return m_applying;
    }

  private:

    void CapturePixelRenderStates();
    void CapturePixelSamplerStates();
    void CapturePixelShaderStates();

    void CaptureVertexRenderStates();
    void CaptureVertexSamplerStates();
    void CaptureVertexShaderStates();

    void CaptureType(D3D9StateBlockType State);

    D3D9CapturableState  m_state;
    D3D9StateCaptures    m_captures;

    D3D9CapturableState* m_deviceState;

    bool                 m_applying = false;

  };

}


code:
#pragma once

#include "d3d9_resource.h"
#include "d3d9_common_texture.h"

namespace dxvk {

  template <typename... Type>
  class D3D9Subresource : public D3D9Resource<Type...> {

  public:

    D3D9Subresource(
            D3D9DeviceEx*           pDevice,
            D3D9CommonTexture*      pTexture,
            UINT                    Face,
            UINT                    MipLevel,
            IDirect3DBaseTexture9*  pBaseTexture,
            IUnknown*               pContainer)
    : D3D9Resource<Type...>(pDevice),
      m_container          (pContainer),
      m_baseTexture        (pBaseTexture),
      m_texture            (pTexture),
      m_face               (Face),
      m_mipLevel           (MipLevel),
      m_isSrgbCompatible   (pTexture->IsSrgbCompatible()),
      m_isNull             (pTexture->IsNull()) {

    }

    ~D3D9Subresource() {
      // We own the texture!
      if (m_baseTexture == nullptr)
        delete m_texture;
    }

    ULONG STDMETHODCALLTYPE AddRef() final {
      if (m_baseTexture != nullptr)
        return m_baseTexture->AddRef();

      return D3D9Resource<Type...>::AddRef();
    }

    ULONG STDMETHODCALLTYPE Release() final {
      if (m_baseTexture != nullptr)
        return m_baseTexture->Release();

      return D3D9Resource<Type...>::Release();
    }

    HRESULT STDMETHODCALLTYPE GetContainer(REFIID riid, void** ppContainer) final {
      if (m_container != nullptr)
        return m_container->QueryInterface(riid, ppContainer);

      return this->GetDevice()->QueryInterface(riid, ppContainer);
    }

    void STDMETHODCALLTYPE PreLoad() {
      m_texture->PreLoadSubresource(GetSubresource());
    }

    inline D3D9CommonTexture* GetCommonTexture() {
      return m_texture;
    }

    inline UINT GetFace() const {
      return m_face;
    }

    inline UINT GetMipLevel() const {
      return m_mipLevel;
    }

    inline UINT GetSubresource() const {
      return m_texture->CalcSubresource(m_face, m_mipLevel);
    }

    inline const Rc<DxvkImageView>& GetImageView(bool Srgb) {
      Srgb &= m_isSrgbCompatible;
      Rc<DxvkImageView>& view = m_sampleView.Pick(Srgb);

      if (unlikely(view == nullptr && !IsNull()))
        view = m_texture->CreateView(m_face, m_mipLevel, VK_IMAGE_USAGE_SAMPLED_BIT, Srgb);

      return view;
    }

    inline const Rc<DxvkImageView>& GetRenderTargetView(bool Srgb) {
      Srgb &= m_isSrgbCompatible;
      Rc<DxvkImageView>& view = m_renderTargetView.Pick(Srgb);

      if (unlikely(view == nullptr && !IsNull()))
        view = m_texture->CreateView(m_face, m_mipLevel, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, Srgb);

      return view;
    }

    inline VkImageLayout GetRenderTargetLayout() const {
      return m_texture->DetermineRenderTargetLayout();
    }

    inline const Rc<DxvkImageView>& GetDepthStencilView() {
      Rc<DxvkImageView>& view = m_depthStencilView;

      if (unlikely(view == nullptr))
        view = m_texture->CreateView(m_face, m_mipLevel, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, false);

      return view;
    }

    inline VkImageLayout GetDepthStencilLayout(bool write, bool hazardous) const {
      return m_texture->DetermineDepthStencilLayout(write, hazardous);
    }

    inline bool IsNull() {
      return m_isNull;
    }

    inline IDirect3DBaseTexture9* GetBaseTexture() {
      return m_baseTexture;
    }

    inline void Swap(D3D9Subresource* Other) {
      // Only used for swap chain back buffers that don't
      // have a container and all have identical properties
      std::swap(m_texture,          Other->m_texture);
      std::swap(m_sampleView,       Other->m_sampleView);
      std::swap(m_renderTargetView, Other->m_renderTargetView);
    }

  protected:

    IUnknown*               m_container;
    IDirect3DBaseTexture9*  m_baseTexture;

    D3D9CommonTexture*      m_texture;

    UINT                    m_face             : 8;
    UINT                    m_mipLevel         : 16;
    UINT                    m_isSrgbCompatible : 1;
    UINT                    m_isNull           : 1;

    D3D9ColorView           m_sampleView;
    D3D9ColorView           m_renderTargetView;
    Rc<DxvkImageView>       m_depthStencilView;

  };

}


code:
#include "d3d9_surface.h"
#include "d3d9_texture.h"
#include "d3d9_swapchain.h"

#include "d3d9_device.h"

namespace dxvk {

  D3D9Surface::D3D9Surface(
          D3D9DeviceEx*             pDevice,
    const D3D9_COMMON_TEXTURE_DESC* pDesc,
          IUnknown*                 pContainer,
          HANDLE*                   pSharedHandle)
    : D3D9SurfaceBase(
        pDevice,
        new D3D9CommonTexture( pDevice, pDesc, D3DRTYPE_SURFACE, pSharedHandle),
        0, 0,
        nullptr,
        pContainer) { }

  D3D9Surface::D3D9Surface(
          D3D9DeviceEx*             pDevice,
          D3D9CommonTexture*        pTexture,
          UINT                      Face,
          UINT                      MipLevel,
          IDirect3DBaseTexture9*    pBaseTexture)
    : D3D9SurfaceBase(
        pDevice,
        pTexture,
        Face, MipLevel,
        pBaseTexture,
        pBaseTexture) { }

  void D3D9Surface::AddRefPrivate() {
    if (m_baseTexture != nullptr) {
      D3DRESOURCETYPE type = m_baseTexture->GetType();
      if (type == D3DRTYPE_TEXTURE)
        static_cast<D3D9Texture2D*>  (m_baseTexture)->AddRefPrivate();
      else //if (type == D3DRTYPE_CUBETEXTURE)
        static_cast<D3D9TextureCube*>(m_baseTexture)->AddRefPrivate();

      return;
    }

    D3D9SurfaceBase::AddRefPrivate();
  }

  void D3D9Surface::ReleasePrivate() {
    if (m_baseTexture != nullptr) {
      D3DRESOURCETYPE type = m_baseTexture->GetType();
      if (type == D3DRTYPE_TEXTURE)
        static_cast<D3D9Texture2D*>  (m_baseTexture)->ReleasePrivate();
      else //if (type == D3DRTYPE_CUBETEXTURE)
        static_cast<D3D9TextureCube*>(m_baseTexture)->ReleasePrivate();

      return;
    }

    D3D9SurfaceBase::ReleasePrivate();
  }

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

    *ppvObject = nullptr;

    if (riid == __uuidof(IUnknown)
     || riid == __uuidof(IDirect3DResource9)
     || riid == __uuidof(IDirect3DSurface9)) {
      *ppvObject = ref(this);
      return S_OK;
    }

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

  D3DRESOURCETYPE STDMETHODCALLTYPE D3D9Surface::GetType() {
    return D3DRTYPE_SURFACE;
  }

  HRESULT STDMETHODCALLTYPE D3D9Surface::GetDesc(D3DSURFACE_DESC *pDesc) {
    if (pDesc == nullptr)
      return D3DERR_INVALIDCALL;

    auto& desc = *(m_texture->Desc());

    pDesc->Format             = static_cast<D3DFORMAT>(desc.Format);
    pDesc->Type               = D3DRTYPE_SURFACE;
    pDesc->Usage              = desc.Usage;
    pDesc->Pool               = desc.Pool;

    pDesc->MultiSampleType    = desc.MultiSample;
    pDesc->MultiSampleQuality = desc.MultisampleQuality;
    pDesc->Width              = std::max(1u, desc.Width >> m_mipLevel);
    pDesc->Height             = std::max(1u, desc.Height >> m_mipLevel);

    return D3D_OK;
  }

  HRESULT STDMETHODCALLTYPE D3D9Surface::LockRect(D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) {
    if (unlikely(pLockedRect == nullptr))
      return D3DERR_INVALIDCALL;

    D3DBOX box;
    if (pRect != nullptr) {
      box.Left   = pRect->left;
      box.Right  = pRect->right;
      box.Top    = pRect->top;
      box.Bottom = pRect->bottom;
      box.Front  = 0;
      box.Back   = 1;
    }

    D3DLOCKED_BOX lockedBox;

    HRESULT hr = m_parent->LockImage(
      m_texture,
      m_face, m_mipLevel,
      &lockedBox,
      pRect != nullptr ? &box : nullptr,
      Flags);

    pLockedRect->pBits = lockedBox.pBits;
    pLockedRect->Pitch = lockedBox.RowPitch;

    return hr;
  }

  HRESULT STDMETHODCALLTYPE D3D9Surface::UnlockRect() {
    return m_parent->UnlockImage(
      m_texture,
      m_face, m_mipLevel);
  }

  HRESULT STDMETHODCALLTYPE D3D9Surface::GetDC(HDC *phDC) {
    if (phDC == nullptr)
      return D3DERR_INVALIDCALL;

    const D3D9_COMMON_TEXTURE_DESC& desc = *m_texture->Desc();

    D3DLOCKED_RECT lockedRect;
    HRESULT hr = LockRect(&lockedRect, nullptr, 0);
    if (FAILED(hr))
      return hr;

    D3DKMT_CREATEDCFROMMEMORY createInfo;
    // In...
    createInfo.pMemory     = lockedRect.pBits;
    createInfo.Format      = static_cast<D3DFORMAT>(desc.Format);
    createInfo.Width       = desc.Width;
    createInfo.Height      = desc.Height;
    createInfo.Pitch       = lockedRect.Pitch;
    createInfo.hDeviceDc   = CreateCompatibleDC(NULL);
    createInfo.pColorTable = nullptr;

    // Out...
    createInfo.hBitmap     = nullptr;
    createInfo.hDc         = nullptr;

    D3DKMTCreateDCFromMemory(&createInfo);
    DeleteDC(createInfo.hDeviceDc);

    // These should now be set...
    m_dcDesc.hDC     = createInfo.hDc;
    m_dcDesc.hBitmap = createInfo.hBitmap;

    *phDC = m_dcDesc.hDC;
    return D3D_OK;
  }

  HRESULT STDMETHODCALLTYPE D3D9Surface::ReleaseDC(HDC hDC) {
    if (m_dcDesc.hDC == nullptr || m_dcDesc.hDC != hDC)
      return D3DERR_INVALIDCALL;

    D3DKMTDestroyDCFromMemory(&m_dcDesc);

    HRESULT hr = UnlockRect();
    if (FAILED(hr))
      return hr;

    return D3D_OK;
  }

  void D3D9Surface::ClearContainer() {
    m_container = nullptr;
  }

}


code:
#pragma once

#include "d3d9_subresource.h"

#include "d3d9_common_texture.h"

#include "../util/util_gdi.h"

#include <algorithm>

namespace dxvk {

  using D3D9GDIDesc = D3DKMT_DESTROYDCFROMMEMORY;

  using D3D9SurfaceBase = D3D9Subresource<IDirect3DSurface9>;
  class D3D9Surface final : public D3D9SurfaceBase {

  public:

    D3D9Surface(
            D3D9DeviceEx*             pDevice,
      const D3D9_COMMON_TEXTURE_DESC* pDesc,
            IUnknown*                 pContainer,
            HANDLE*                   pSharedHandle);

    D3D9Surface(
            D3D9DeviceEx*             pDevice,
            D3D9CommonTexture*        pTexture,
            UINT                      Face,
            UINT                      MipLevel,
            IDirect3DBaseTexture9*    pBaseTexture);

    void AddRefPrivate();

    void ReleasePrivate();

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

    D3DRESOURCETYPE STDMETHODCALLTYPE GetType() final;

    HRESULT STDMETHODCALLTYPE GetDesc(D3DSURFACE_DESC *pDesc) final;

    HRESULT STDMETHODCALLTYPE LockRect(D3DLOCKED_RECT* pLockedRect, CONST RECT* pRect, DWORD Flags) final;

    HRESULT STDMETHODCALLTYPE UnlockRect() final;

    HRESULT STDMETHODCALLTYPE GetDC(HDC *phDC) final;

    HRESULT STDMETHODCALLTYPE ReleaseDC(HDC hDC) final;

    inline VkExtent2D GetSurfaceExtent() const {
      const auto* desc = m_texture->Desc();

      return VkExtent2D {
        std::max(1u, desc->Width  >> GetMipLevel()),
        std::max(1u, desc->Height >> GetMipLevel())
      };
    }

    void ClearContainer();

  private:

    D3D9GDIDesc m_dcDesc;

  };
}