code:
#pragma once

#include "../dxvk/dxvk_include.h"

#include "dxgi_format.h"
#include "dxgi_include.h"

namespace dxvk {
  class DxgiAdapter;
  class DxgiSwapChain;
  class DxvkAdapter;
  class DxvkBuffer;
  class DxvkDevice;
  class DxvkImage;
}

struct IDXGIVkInteropDevice;

/**
 * \brief Per-monitor data
 */
struct DXGI_VK_MONITOR_DATA {
  dxvk::DxgiSwapChain*  pSwapChain;
  DXGI_FRAME_STATISTICS FrameStats;
  DXGI_GAMMA_CONTROL    GammaCurve;
};

/**
 * \brief Private DXGI presenter
 *
 * Presenter interface that allows the DXGI swap
 * chain implementation to remain API-agnostic,
 * so that common code can stay in one class.
 */
MIDL_INTERFACE("104001a6-7f36-4957-b932-86ade9567d91")
IDXGIVkSwapChain : public IUnknown {
  virtual HRESULT STDMETHODCALLTYPE GetDesc(
          DXGI_SWAP_CHAIN_DESC1*    pDesc) = 0;

  virtual HRESULT STDMETHODCALLTYPE GetAdapter(
          REFIID                    riid,
          void**                    ppvObject) = 0;

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

  virtual HRESULT STDMETHODCALLTYPE GetImage(
          UINT                      BufferId,
          REFIID                    riid,
          void**                    ppBuffer) = 0;

  virtual UINT STDMETHODCALLTYPE GetImageIndex() = 0;

  virtual UINT STDMETHODCALLTYPE GetFrameLatency() = 0;

  virtual HANDLE STDMETHODCALLTYPE GetFrameLatencyEvent() = 0;

  virtual HRESULT STDMETHODCALLTYPE ChangeProperties(
    const DXGI_SWAP_CHAIN_DESC1*    pDesc) = 0;

  virtual HRESULT STDMETHODCALLTYPE SetPresentRegion(
    const RECT*                     pRegion) = 0;

  virtual HRESULT STDMETHODCALLTYPE SetGammaControl(
          UINT                      NumControlPoints,
    const DXGI_RGB*                 pControlPoints) = 0;

  virtual HRESULT STDMETHODCALLTYPE SetFrameLatency(
          UINT                      MaxLatency) = 0;

  virtual HRESULT STDMETHODCALLTYPE Present(
          UINT                      SyncInterval,
          UINT                      PresentFlags,
    const DXGI_PRESENT_PARAMETERS*  pPresentParameters) = 0;

  virtual void STDMETHODCALLTYPE NotifyModeChange(
          BOOL                      Windowed,
    const DXGI_MODE_DESC*           pDisplayMode) = 0;
};

/**
 * \brief Private DXGI adapter interface
 *
 * The implementation of \c IDXGIAdapter holds a
 * \ref DxvkAdapter which can be retrieved using
 * this interface.
 */
MIDL_INTERFACE("907bf281-ea3c-43b4-a8e4-9f231107b4ff")
IDXGIDXVKAdapter : public IDXGIAdapter4 {
  virtual dxvk::Rc<dxvk::DxvkAdapter> STDMETHODCALLTYPE GetDXVKAdapter() = 0;

  virtual dxvk::Rc<dxvk::DxvkInstance> STDMETHODCALLTYPE GetDXVKInstance() = 0;

};

/**
 * \brief Private DXGI device interface
 */
MIDL_INTERFACE("92a5d77b-b6e1-420a-b260-fdd701272827")
IDXGIDXVKDevice : public IUnknown {
  virtual void STDMETHODCALLTYPE SetAPIVersion(
            UINT                    Version) = 0;

  virtual UINT STDMETHODCALLTYPE GetAPIVersion() = 0;

};

/**
 * \brief Private DXGI monitor info interface
 *
 * Can be queried from the DXGI factory to store monitor
 * info globally, with a lifetime that exceeds that of
 * the \c IDXGIOutput or \c IDXGIAdapter objects.
 */
MIDL_INTERFACE("c06a236f-5be3-448a-8943-89c611c0c2c1")
IDXGIVkMonitorInfo : public IUnknown {
  /**
   * \brief Initializes monitor data
   *
   * Fails if data for the given monitor already exists.
   * \param [in] hMonitor The monitor handle
   * \param [in] pData Initial data
   */
  virtual HRESULT STDMETHODCALLTYPE InitMonitorData(
          HMONITOR                hMonitor,
    const DXGI_VK_MONITOR_DATA*   pData) = 0;

  /**
   * \brief Retrieves and locks monitor data
   *
   * Fails if no data for the given monitor exists.
   * \param [in] hMonitor The monitor handle
   * \param [out] Pointer to monitor data
   * \returns S_OK on success
   */
  virtual HRESULT STDMETHODCALLTYPE AcquireMonitorData(
          HMONITOR                hMonitor,
          DXGI_VK_MONITOR_DATA**  ppData) = 0;

  /**
   * \brief Unlocks monitor data
   *
   * Must be called after each successful
   * call to \ref AcquireMonitorData.
   * \param [in] hMonitor The monitor handle
   */
  virtual void STDMETHODCALLTYPE ReleaseMonitorData() = 0;

};

/**
 * \brief DXGI surface interface for Vulkan interop
 *
 * Provides access to the backing resource of a
 * DXGI surface, which is typically a D3D texture.
 */
MIDL_INTERFACE("5546cf8c-77e7-4341-b05d-8d4d5000e77d")
IDXGIVkInteropSurface : public IUnknown {
  /**
   * \brief Retrieves device interop interfaceSlots
   *
   * Queries the device that owns the surface for
   * the \ref IDXGIVkInteropDevice interface.
   * \param [out] ppDevice The device interface
   * \returns \c S_OK on success
   */
  virtual HRESULT STDMETHODCALLTYPE GetDevice(
          IDXGIVkInteropDevice**  ppDevice) = 0;

  /**
   * \brief Retrieves Vulkan image info
   *
   * Retrieves both the image handle as well as the image's
   * properties. Any of the given pointers may be \c nullptr.
   *
   * If \c pInfo is not \c nullptr, the following rules apply:
   * - \c pInfo->sType \e must be \c VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO
   * - \c pInfo->pNext \e must be \c nullptr or point to a supported
   *   extension-specific structure (currently none)
   * - \c pInfo->queueFamilyIndexCount must be the length of the
   *   \c pInfo->pQueueFamilyIndices array, in \c uint32_t units.
   * - \c pInfo->pQueueFamilyIndices must point to a pre-allocated
   *   array of \c uint32_t of size \c pInfo->pQueueFamilyIndices.
   *
   * \note As of now, the sharing mode will always be
   *       \c VK_SHARING_MODE_EXCLUSIVE and no queue
   *       family indices will be written to the array.
   *
   * After the call, the structure pointed to by \c pInfo can
   * be used to create an image with identical properties.
   *
   * If \c pLayout is not \c nullptr, it will receive the
   * layout that the image will be in after flushing any
   * outstanding commands on the device.
   * \param [out] pHandle The image handle
   * \param [out] pLayout Image layout
   * \param [out] pInfo Image properties
   * \returns \c S_OK on success, or \c E_INVALIDARG
   */
  virtual HRESULT STDMETHODCALLTYPE GetVulkanImageInfo(
          VkImage*              pHandle,
          VkImageLayout*        pLayout,
          VkImageCreateInfo*    pInfo) = 0;
};

/**
 * \brief DXGI device interface for Vulkan interop
 *
 * Provides access to the device and instance handles
 * as well as the queue that is used for rendering.
 */
MIDL_INTERFACE("e2ef5fa5-dc21-4af7-90c4-f67ef6a09323")
IDXGIVkInteropDevice : public IUnknown {
  /**
   * \brief Queries Vulkan handles used by DXVK
   *
   * \param [out] pInstance The Vulkan instance
   * \param [out] pPhysDev The physical device
   * \param [out] pDevide The device handle
   */
  virtual void STDMETHODCALLTYPE GetVulkanHandles(
          VkInstance*           pInstance,
          VkPhysicalDevice*     pPhysDev,
          VkDevice*             pDevice) = 0;

  /**
   * \brief Queries the rendering queue used by DXVK
   *
   * \param [out] pQueue The Vulkan queue handle
   * \param [out] pQueueFamilyIndex Queue family index
   */
  virtual void STDMETHODCALLTYPE GetSubmissionQueue(
          VkQueue*              pQueue,
          uint32_t*             pQueueFamilyIndex) = 0;

  /**
   * \brief Transitions a surface to a given layout
   *
   * Executes an explicit image layout transition on the
   * D3D device. Note that the image subresources \e must
   * be transitioned back to its original layout before
   * using it again from D3D11.
   * \param [in] pSurface The image to transform
   * \param [in] pSubresources Subresources to transform
   * \param [in] OldLayout Current image layout
   * \param [in] NewLayout Desired image layout
   */
  virtual void STDMETHODCALLTYPE TransitionSurfaceLayout(
          IDXGIVkInteropSurface*    pSurface,
    const VkImageSubresourceRange*  pSubresources,
          VkImageLayout             OldLayout,
          VkImageLayout             NewLayout) = 0;

  /**
   * \brief Flushes outstanding D3D rendering commands
   *
   * Must be called before submitting Vulkan commands
   * to the rendering queue if those commands use the
   * backing resource of a D3D11 object.
   */
  virtual void STDMETHODCALLTYPE FlushRenderingCommands() = 0;

  /**
   * \brief Locks submission queue
   *
   * Should be called immediately before submitting
   * Vulkan commands to the rendering queue in order
   * to prevent DXVK from using the queue.
   *
   * While the submission queue is locked, no D3D11
   * methods must be called from the locking thread,
   * or otherwise a deadlock might occur.
   */
  virtual void STDMETHODCALLTYPE LockSubmissionQueue() = 0;

  /**
   * \brief Releases submission queue
   *
   * Should be called immediately after submitting
   * Vulkan commands to the rendering queue in order
   * to allow DXVK to submit new commands.
   */
  virtual void STDMETHODCALLTYPE ReleaseSubmissionQueue() = 0;
};

struct D3D11_TEXTURE2D_DESC1;
struct ID3D11Texture2D;

/**
 * \brief See IDXGIVkInteropDevice.
 */
MIDL_INTERFACE("e2ef5fa5-dc21-4af7-90c4-f67ef6a09324")
IDXGIVkInteropDevice1 : public IDXGIVkInteropDevice {
  /**
   * \brief Queries the rendering queue used by DXVK
   *
   * \param [out] pQueue The Vulkan queue handle
   * \param [out] pQueueIndex Queue index
   * \param [out] pQueueFamilyIndex Queue family index
   */
  virtual void STDMETHODCALLTYPE GetSubmissionQueue1(
          VkQueue*              pQueue,
          uint32_t*             pQueueIndex,
          uint32_t*             pQueueFamilyIndex) = 0;

  virtual HRESULT STDMETHODCALLTYPE CreateTexture2DFromVkImage(
          const D3D11_TEXTURE2D_DESC1 *pDesc,
          VkImage vkImage,
          ID3D11Texture2D **ppTexture2D) = 0;
};

/**
 * \brief DXGI adapter interface for Vulkan interop
 *
 * Provides access to the physical device and
 * instance handles for the given DXGI adapter.
 */
MIDL_INTERFACE("3a6d8f2c-b0e8-4ab4-b4dc-4fd24891bfa5")
IDXGIVkInteropAdapter : public IUnknown {
  /**
   * \brief Queries Vulkan handles used by DXVK
   *
   * \param [out] pInstance The Vulkan instance
   * \param [out] pPhysDev The physical device
   */
  virtual void STDMETHODCALLTYPE GetVulkanHandles(
          VkInstance*           pInstance,
          VkPhysicalDevice*     pPhysDev) = 0;
};

/**
 * \brief IWineDXGISwapChainFactory device interface
 *
 * Allows a swap chain to be created from a device.
 * See include/wine/winedxgi.idl for definition.
 */
MIDL_INTERFACE("53cb4ff0-c25a-4164-a891-0e83db0a7aac")
IWineDXGISwapChainFactory : public IUnknown {
    virtual HRESULT STDMETHODCALLTYPE CreateSwapChainForHwnd(
            IDXGIFactory*           pFactory,
            HWND                    hWnd,
      const DXGI_SWAP_CHAIN_DESC1*  pDesc,
      const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc,
            IDXGIOutput*            pRestrictToOutput,
            IDXGISwapChain1**       ppSwapChain) = 0;
};

#ifdef _MSC_VER
struct __declspec(uuid("907bf281-ea3c-43b4-a8e4-9f231107b4ff")) IDXGIDXVKAdapter;
struct __declspec(uuid("92a5d77b-b6e1-420a-b260-fdd701272827")) IDXGIDXVKDevice;
struct __declspec(uuid("c06a236f-5be3-448a-8943-89c611c0c2c1")) IDXGIVkMonitorInfo;
struct __declspec(uuid("3a6d8f2c-b0e8-4ab4-b4dc-4fd24891bfa5")) IDXGIVkInteropAdapter;
struct __declspec(uuid("e2ef5fa5-dc21-4af7-90c4-f67ef6a09323")) IDXGIVkInteropDevice;
struct __declspec(uuid("e2ef5fa5-dc21-4af7-90c4-f67ef6a09324")) IDXGIVkInteropDevice1;
struct __declspec(uuid("5546cf8c-77e7-4341-b05d-8d4d5000e77d")) IDXGIVkInteropSurface;
struct __declspec(uuid("104001a6-7f36-4957-b932-86ade9567d91")) IDXGIVkSwapChain;
struct __declspec(uuid("53cb4ff0-c25a-4164-a891-0e83db0a7aac")) IWineDXGISwapChainFactory;
#else
__CRT_UUID_DECL(IDXGIDXVKAdapter,          0x907bf281,0xea3c,0x43b4,0xa8,0xe4,0x9f,0x23,0x11,0x07,0xb4,0xff);
__CRT_UUID_DECL(IDXGIDXVKDevice,           0x92a5d77b,0xb6e1,0x420a,0xb2,0x60,0xfd,0xf7,0x01,0x27,0x28,0x27);
__CRT_UUID_DECL(IDXGIVkMonitorInfo,        0xc06a236f,0x5be3,0x448a,0x89,0x43,0x89,0xc6,0x11,0xc0,0xc2,0xc1);
__CRT_UUID_DECL(IDXGIVkInteropAdapter,     0x3a6d8f2c,0xb0e8,0x4ab4,0xb4,0xdc,0x4f,0xd2,0x48,0x91,0xbf,0xa5);
__CRT_UUID_DECL(IDXGIVkInteropDevice,      0xe2ef5fa5,0xdc21,0x4af7,0x90,0xc4,0xf6,0x7e,0xf6,0xa0,0x93,0x23);
__CRT_UUID_DECL(IDXGIVkInteropDevice1,     0xe2ef5fa5,0xdc21,0x4af7,0x90,0xc4,0xf6,0x7e,0xf6,0xa0,0x93,0x24);
__CRT_UUID_DECL(IDXGIVkInteropSurface,     0x5546cf8c,0x77e7,0x4341,0xb0,0x5d,0x8d,0x4d,0x50,0x00,0xe7,0x7d);
__CRT_UUID_DECL(IDXGIVkSwapChain,          0x104001a6,0x7f36,0x4957,0xb9,0x32,0x86,0xad,0xe9,0x56,0x7d,0x91);
__CRT_UUID_DECL(IWineDXGISwapChainFactory, 0x53cb4ff0,0xc25a,0x4164,0xa8,0x91,0x0e,0x83,0xdb,0x0a,0x7a,0xac);
#endif


code:
#include "dxgi_factory.h"
#include "dxgi_include.h"

namespace dxvk {

  Logger Logger::s_instance("dxgi.log");

  HRESULT createDxgiFactory(UINT Flags, REFIID riid, void **ppFactory) {
    try {
      Com<DxgiFactory> factory = new DxgiFactory(Flags);
      HRESULT hr = factory->QueryInterface(riid, ppFactory);

      if (FAILED(hr))
        return hr;

      return S_OK;
    } catch (const DxvkError& e) {
      Logger::err(e.message());
      return E_FAIL;
    }
  }
}

extern "C" {
  DLLEXPORT HRESULT __stdcall CreateDXGIFactory2(UINT Flags, REFIID riid, void **ppFactory) {
    dxvk::Logger::warn("CreateDXGIFactory2: Ignoring flags");
    return dxvk::createDxgiFactory(Flags, riid, ppFactory);
  }

  DLLEXPORT HRESULT __stdcall CreateDXGIFactory1(REFIID riid, void **ppFactory) {
    return dxvk::createDxgiFactory(0, riid, ppFactory);
  }

  DLLEXPORT HRESULT __stdcall CreateDXGIFactory(REFIID riid, void **ppFactory) {
    return dxvk::createDxgiFactory(0, riid, ppFactory);
  }

  DLLEXPORT HRESULT __stdcall DXGIDeclareAdapterRemovalSupport() {
    static bool enabled = false;

    if (std::exchange(enabled, true))
      return 0x887a0036; // DXGI_ERROR_ALREADY_EXISTS;

    dxvk::Logger::warn("DXGIDeclareAdapterRemovalSupport: Stub");
    return S_OK;
  }

  DLLEXPORT HRESULT __stdcall DXGIGetDebugInterface1(UINT Flags, REFIID riid, void **ppDebug) {
    static bool errorShown = false;

    if (!std::exchange(errorShown, true))
      dxvk::Logger::warn("DXGIGetDebugInterface1: Stub");

    return E_NOINTERFACE;
  }

}

repo_name: orochi/dxvk


code:
#include "dxgi_monitor.h"

namespace dxvk {

  DxgiMonitorInfo::DxgiMonitorInfo(IUnknown* pParent)
  : m_parent(pParent) {

  }

  DxgiMonitorInfo::~DxgiMonitorInfo() {

  }

  ULONG STDMETHODCALLTYPE DxgiMonitorInfo::AddRef() {
    return m_parent->AddRef();
  }

  ULONG STDMETHODCALLTYPE DxgiMonitorInfo::Release() {
    return m_parent->Release();
  }

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

  HRESULT STDMETHODCALLTYPE DxgiMonitorInfo::InitMonitorData(
          HMONITOR                hMonitor,
    const DXGI_VK_MONITOR_DATA*   pData) {
    if (!hMonitor || !pData)
      return E_INVALIDARG;

    std::lock_guard<dxvk::mutex> lock(m_monitorMutex);
    auto result = m_monitorData.insert({ hMonitor, *pData });

    return result.second ? S_OK : E_INVALIDARG;
  }

  HRESULT STDMETHODCALLTYPE DxgiMonitorInfo::AcquireMonitorData(
          HMONITOR                hMonitor,
          DXGI_VK_MONITOR_DATA**  ppData) {
    InitReturnPtr(ppData);

    if (!hMonitor || !ppData)
      return E_INVALIDARG;

    m_monitorMutex.lock();

    auto entry = m_monitorData.find(hMonitor);
    if (entry == m_monitorData.end()) {
      m_monitorMutex.unlock();
      return DXGI_ERROR_NOT_FOUND;
    }

    *ppData = &entry->second;
    return S_OK;
  }

  void STDMETHODCALLTYPE DxgiMonitorInfo::ReleaseMonitorData() {
    m_monitorMutex.unlock();
  }

  uint32_t GetMonitorFormatBpp(DXGI_FORMAT Format) {
    switch (Format) {
      case DXGI_FORMAT_R8G8B8A8_UNORM:
      case DXGI_FORMAT_B8G8R8A8_UNORM:
      case DXGI_FORMAT_B8G8R8X8_UNORM:
      case DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
      case DXGI_FORMAT_B8G8R8A8_UNORM_SRGB:
      case DXGI_FORMAT_B8G8R8X8_UNORM_SRGB:
      case DXGI_FORMAT_R10G10B10A2_UNORM:
        return 32;

      case DXGI_FORMAT_R16G16B16A16_FLOAT:
        return 64;

      default:
        Logger::warn(str::format(
          "GetMonitorFormatBpp: Unknown format: ",
          Format));
        return 32;
    }
  }

}


code:
#pragma once

#include <mutex>
#include <unordered_map>

#include "dxgi_interfaces.h"

namespace dxvk {

  class DxgiSwapChain;

  class DxgiMonitorInfo : public IDXGIVkMonitorInfo {

  public:

    DxgiMonitorInfo(IUnknown* pParent);

    ~DxgiMonitorInfo();

    ULONG STDMETHODCALLTYPE AddRef();

    ULONG STDMETHODCALLTYPE Release();

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

    HRESULT STDMETHODCALLTYPE InitMonitorData(
            HMONITOR                hMonitor,
      const DXGI_VK_MONITOR_DATA*   pData);

    HRESULT STDMETHODCALLTYPE AcquireMonitorData(
            HMONITOR                hMonitor,
            DXGI_VK_MONITOR_DATA**  ppData);

    void STDMETHODCALLTYPE ReleaseMonitorData();

  private:

    IUnknown* m_parent;

    dxvk::mutex                                        m_monitorMutex;
    std::unordered_map<HMONITOR, DXGI_VK_MONITOR_DATA> m_monitorData;

  };

  /**
   * \brief Queries bits per pixel for a format
   *
   * The format must be a valid swap chain format.
   * \param [in] Format The DXGI format to query
   * \returns Bits per pixel for this format
   */
  uint32_t GetMonitorFormatBpp(
          DXGI_FORMAT             Format);

}


code:
#pragma once

#include "dxgi_include.h"

#include "../util/com/com_private_data.h"

namespace dxvk {

  template<typename Base>
  class DxgiObject : public ComObject<Base> {

  public:

    HRESULT STDMETHODCALLTYPE GetPrivateData(
            REFGUID       Name,
            UINT*         pDataSize,
            void*         pData) final {
      return m_privateData.getData(
        Name, pDataSize, pData);
    }

    HRESULT STDMETHODCALLTYPE SetPrivateData(
            REFGUID       Name,
            UINT          DataSize,
      const void*         pData) final {
      return m_privateData.setData(
        Name, DataSize, pData);
    }

    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(
            REFGUID       Name,
      const IUnknown*     pUnknown) final {
      return m_privateData.setInterface(
        Name, pUnknown);
    }

  private:

    ComPrivateData m_privateData;

  };

}


code:
#include "dxgi_options.h"

#include <unordered_map>

namespace dxvk {

  static int32_t parsePciId(const std::string& str) {
    if (str.size() != 4)
      return -1;

    int32_t id = 0;

    for (size_t i = 0; i < str.size(); i++) {
      id *= 16;

      if (str[i] >= '0' && str[i] <= '9')
        id += str[i] - '0';
      else if (str[i] >= 'A' && str[i] <= 'F')
        id += str[i] - 'A' + 10;
      else if (str[i] >= 'a' && str[i] <= 'f')
        id += str[i] - 'a' + 10;
      else
        return -1;
    }

    return id;
  }

  DxgiOptions::DxgiOptions(const Config& config) {
    // Fetch these as a string representing a hexadecimal number and parse it.
    this->customVendorId = parsePciId(config.getOption<std::string>("dxgi.customVendorId"));
    this->customDeviceId = parsePciId(config.getOption<std::string>("dxgi.customDeviceId"));
    this->customDeviceDesc = config.getOption<std::string>("dxgi.customDeviceDesc", "");

    // Emulate a UMA device
    this->emulateUMA = config.getOption<bool>("dxgi.emulateUMA", false);

    // Interpret the memory limits as Megabytes
    this->maxDeviceMemory = VkDeviceSize(config.getOption<int32_t>("dxgi.maxDeviceMemory", 0)) << 20;
    this->maxSharedMemory = VkDeviceSize(config.getOption<int32_t>("dxgi.maxSharedMemory", 0)) << 20;

    // Force nvapiHack to be disabled if NvAPI is enabled in environment
    if (env::getEnvVar("DXVK_ENABLE_NVAPI") == "1")
      this->nvapiHack = false;
    else
      this->nvapiHack = config.getOption<bool>("dxgi.nvapiHack", true);
  }

}


code:
#pragma once

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

#include "../dxvk/dxvk_include.h"

#include "dxgi_include.h"

namespace dxvk {

  /**
   * \brief DXGI options
   *
   * Per-app options that control the
   * behaviour of some DXGI classes.
   */
  struct DxgiOptions {
    DxgiOptions(const Config& config);

    /// Override PCI vendor and device IDs reported to the
    /// application. This may make apps think they are running
    /// on a different GPU than they do and behave differently.
    int32_t customVendorId;
    int32_t customDeviceId;
    std::string customDeviceDesc;

    /// Override maximum reported VRAM size. This may be
    /// useful for some 64-bit games which do not support
    /// more than 4 GiB of VRAM.
    VkDeviceSize maxDeviceMemory;
    VkDeviceSize maxSharedMemory;

    /// Emulate UMA
    bool emulateUMA;

    /// Enables nvapi workaround
    bool nvapiHack;
  };

}


code:
#include <algorithm>
#include <numeric>

#include <cstdlib>
#include <cstring>

#include <sstream>
#include <string>

#include "dxgi_adapter.h"
#include "dxgi_factory.h"
#include "dxgi_output.h"
#include "dxgi_swapchain.h"

#include "../dxvk/dxvk_format.h"

namespace dxvk {

  DxgiOutput::DxgiOutput(
    const Com<DxgiFactory>& factory,
    const Com<DxgiAdapter>& adapter,
              HMONITOR      monitor)
  : m_monitorInfo(factory->GetMonitorInfo()),
    m_adapter(adapter),
    m_monitor(monitor) {
    // Init monitor info if necessary
    DXGI_VK_MONITOR_DATA monitorData;
    monitorData.pSwapChain = nullptr;
    monitorData.FrameStats = DXGI_FRAME_STATISTICS();
    monitorData.GammaCurve.Scale  = { 1.0f, 1.0f, 1.0f };
    monitorData.GammaCurve.Offset = { 0.0f, 0.0f, 0.0f };

    for (uint32_t i = 0; i < DXGI_VK_GAMMA_CP_COUNT; i++) {
      const float value = GammaControlPointLocation(i);
      monitorData.GammaCurve.GammaCurve[i] = { value, value, value };
    }

    m_monitorInfo->InitMonitorData(monitor, &monitorData);
  }

  DxgiOutput::~DxgiOutput() {

  }

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

    *ppvObject = nullptr;

    if (riid == __uuidof(IUnknown)
     || riid == __uuidof(IDXGIObject)
     || riid == __uuidof(IDXGIOutput)
     || riid == __uuidof(IDXGIOutput1)
     || riid == __uuidof(IDXGIOutput2)
     || riid == __uuidof(IDXGIOutput3)
     || riid == __uuidof(IDXGIOutput4)
     || riid == __uuidof(IDXGIOutput5)
     || riid == __uuidof(IDXGIOutput6)) {
      *ppvObject = ref(this);
      return S_OK;
    }

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

  HRESULT STDMETHODCALLTYPE DxgiOutput::GetParent(REFIID riid, void **ppParent) {
    return m_adapter->QueryInterface(riid, ppParent);
  }

  HRESULT STDMETHODCALLTYPE DxgiOutput::FindClosestMatchingMode(
    const DXGI_MODE_DESC *pModeToMatch,
          DXGI_MODE_DESC *pClosestMatch,
          IUnknown       *pConcernedDevice) {
    if (!pModeToMatch || !pClosestMatch)
      return DXGI_ERROR_INVALID_CALL;

    DXGI_MODE_DESC1 modeToMatch;
    modeToMatch.Width            = pModeToMatch->Width;
    modeToMatch.Height           = pModeToMatch->Height;
    modeToMatch.RefreshRate      = pModeToMatch->RefreshRate;
    modeToMatch.Format           = pModeToMatch->Format;
    modeToMatch.ScanlineOrdering = pModeToMatch->ScanlineOrdering;
    modeToMatch.Scaling          = pModeToMatch->Scaling;
    modeToMatch.Stereo           = FALSE;

    DXGI_MODE_DESC1 closestMatch = { };

    HRESULT hr = FindClosestMatchingMode1(
      &modeToMatch, &closestMatch, pConcernedDevice);

    if (FAILED(hr))
      return hr;

    pClosestMatch->Width            = closestMatch.Width;
    pClosestMatch->Height           = closestMatch.Height;
    pClosestMatch->RefreshRate      = closestMatch.RefreshRate;
    pClosestMatch->Format           = closestMatch.Format;
    pClosestMatch->ScanlineOrdering = closestMatch.ScanlineOrdering;
    pClosestMatch->Scaling          = closestMatch.Scaling;
    return hr;
  }

  HRESULT STDMETHODCALLTYPE DxgiOutput::FindClosestMatchingMode1(
    const DXGI_MODE_DESC1*      pModeToMatch,
          DXGI_MODE_DESC1*      pClosestMatch,
          IUnknown*             pConcernedDevice) {
    if (!pModeToMatch || !pClosestMatch)
      return DXGI_ERROR_INVALID_CALL;

    if (pModeToMatch->Format == DXGI_FORMAT_UNKNOWN && !pConcernedDevice)
      return DXGI_ERROR_INVALID_CALL;

    // Both or neither must be zero
    if ((pModeToMatch->Width == 0) ^ (pModeToMatch->Height == 0))
      return DXGI_ERROR_INVALID_CALL;

    DEVMODEW devMode;
    devMode.dmSize = sizeof(devMode);

    if (!GetMonitorDisplayMode(m_monitor, ENUM_CURRENT_SETTINGS, &devMode))
      return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE;

    DXGI_MODE_DESC activeMode = { };
    activeMode.Width            = devMode.dmPelsWidth;
    activeMode.Height           = devMode.dmPelsHeight;
    activeMode.RefreshRate      = { devMode.dmDisplayFrequency, 1 };
    activeMode.Format           = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; // FIXME
    activeMode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE;
    activeMode.Scaling          = DXGI_MODE_SCALING_UNSPECIFIED;

    DXGI_MODE_DESC1 defaultMode;
    defaultMode.Width            = 0;
    defaultMode.Height           = 0;
    defaultMode.RefreshRate      = { 0, 0 };
    defaultMode.Format           = DXGI_FORMAT_UNKNOWN;
    defaultMode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
    defaultMode.Scaling          = DXGI_MODE_SCALING_UNSPECIFIED;
    defaultMode.Stereo           = pModeToMatch->Stereo;

    if (pModeToMatch->ScanlineOrdering == DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED)
      defaultMode.ScanlineOrdering = activeMode.ScanlineOrdering;

    if (pModeToMatch->Scaling == DXGI_MODE_SCALING_UNSPECIFIED)
      defaultMode.Scaling = activeMode.Scaling;

    DXGI_FORMAT targetFormat = pModeToMatch->Format;

    if (pModeToMatch->Format == DXGI_FORMAT_UNKNOWN) {
      defaultMode.Format = activeMode.Format;
      targetFormat       = activeMode.Format;
    }

    if (!pModeToMatch->Width) {
      defaultMode.Width  = activeMode.Width;
      defaultMode.Height = activeMode.Height;
    }

    if (!pModeToMatch->RefreshRate.Numerator || !pModeToMatch->RefreshRate.Denominator) {
      defaultMode.RefreshRate.Numerator   = activeMode.RefreshRate.Numerator;
      defaultMode.RefreshRate.Denominator = activeMode.RefreshRate.Denominator;
    }

    UINT modeCount = 0;
    GetDisplayModeList1(targetFormat, DXGI_ENUM_MODES_SCALING, &modeCount, nullptr);

    if (modeCount == 0) {
      Logger::err("DXGI: FindClosestMatchingMode: No modes found");
      return DXGI_ERROR_NOT_FOUND;
    }

    std::vector<DXGI_MODE_DESC1> modes(modeCount);
    GetDisplayModeList1(targetFormat, DXGI_ENUM_MODES_SCALING, &modeCount, modes.data());

    FilterModesByDesc(modes, *pModeToMatch);
    FilterModesByDesc(modes, defaultMode);

    if (modes.empty())
      return DXGI_ERROR_NOT_FOUND;

    *pClosestMatch = modes[0];

    Logger::debug(str::format(
      "DXGI: For mode ",
        pModeToMatch->Width, "x", pModeToMatch->Height, "@",
        pModeToMatch->RefreshRate.Denominator ? (pModeToMatch->RefreshRate.Numerator / pModeToMatch->RefreshRate.Denominator) : 0,
      " found closest mode ",
        pClosestMatch->Width, "x", pClosestMatch->Height, "@",
        pClosestMatch->RefreshRate.Denominator ? (pClosestMatch->RefreshRate.Numerator / pClosestMatch->RefreshRate.Denominator) : 0));
    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE DxgiOutput::GetDesc(DXGI_OUTPUT_DESC *pDesc) {
    if (pDesc == nullptr)
      return DXGI_ERROR_INVALID_CALL;

    DXGI_OUTPUT_DESC1 desc;
    HRESULT hr = GetDesc1(&desc);

    if (SUCCEEDED(hr)) {
      std::memcpy(pDesc->DeviceName, desc.DeviceName, sizeof(pDesc->DeviceName));
      pDesc->DesktopCoordinates = desc.DesktopCoordinates;
      pDesc->AttachedToDesktop  = desc.AttachedToDesktop;
      pDesc->Rotation           = desc.Rotation;
      pDesc->Monitor            = desc.Monitor;
    }

    return hr;
  }

  HRESULT STDMETHODCALLTYPE DxgiOutput::GetDesc1(
          DXGI_OUTPUT_DESC1*    pDesc) {
    if (pDesc == nullptr)
      return DXGI_ERROR_INVALID_CALL;

    ::MONITORINFOEXW monInfo;
    monInfo.cbSize = sizeof(monInfo);

    if (!::GetMonitorInfoW(m_monitor, reinterpret_cast<MONITORINFO*>(&monInfo))) {
      Logger::err("DXGI: Failed to query monitor info");
      return E_FAIL;
    }

    std::memcpy(pDesc->DeviceName, monInfo.szDevice, std::size(pDesc->DeviceName));

    pDesc->DesktopCoordinates = monInfo.rcMonitor;
    pDesc->AttachedToDesktop  = 1;
    pDesc->Rotation           = DXGI_MODE_ROTATION_UNSPECIFIED;
    pDesc->Monitor            = m_monitor;
    pDesc->BitsPerColor       = 8;
    pDesc->ColorSpace         = DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709;

    // We don't really have a way to get these
    for (uint32_t i = 0; i < 2; i++) {
      pDesc->RedPrimary[i]    = 0.0f;
      pDesc->GreenPrimary[i]  = 0.0f;
      pDesc->BluePrimary[i]   = 0.0f;
      pDesc->WhitePoint[i]    = 0.0f;
    }

    pDesc->MinLuminance       = 0.0f;
    pDesc->MaxLuminance       = 0.0f;
    pDesc->MaxFullFrameLuminance = 0.0f;
    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE DxgiOutput::GetDisplayModeList(
          DXGI_FORMAT    EnumFormat,
          UINT           Flags,
          UINT*          pNumModes,
          DXGI_MODE_DESC* pDesc) {
    if (pNumModes == nullptr)
      return DXGI_ERROR_INVALID_CALL;

    std::vector<DXGI_MODE_DESC1> modes;

    if (pDesc)
      modes.resize(std::max(1u, *pNumModes));

    HRESULT hr = GetDisplayModeList1(
      EnumFormat, Flags, pNumModes,
      pDesc ? modes.data() : nullptr);

    for (uint32_t i = 0; i < *pNumModes && i < modes.size(); i++) {
      pDesc[i].Width            = modes[i].Width;
      pDesc[i].Height           = modes[i].Height;
      pDesc[i].RefreshRate      = modes[i].RefreshRate;
      pDesc[i].Format           = modes[i].Format;
      pDesc[i].ScanlineOrdering = modes[i].ScanlineOrdering;
      pDesc[i].Scaling          = modes[i].Scaling;
    }

    return hr;
  }

  HRESULT STDMETHODCALLTYPE DxgiOutput::GetDisplayModeList1(
          DXGI_FORMAT           EnumFormat,
          UINT                  Flags,
          UINT*                 pNumModes,
          DXGI_MODE_DESC1*      pDesc) {
    if (pNumModes == nullptr)
      return DXGI_ERROR_INVALID_CALL;

    // Special case, just return zero modes
    if (EnumFormat == DXGI_FORMAT_UNKNOWN) {
      *pNumModes = 0;
      return S_OK;
    }

    // Walk over all modes that the display supports and
    // return those that match the requested format etc.
    DEVMODEW devMode = { };
    devMode.dmSize = sizeof(DEVMODEW);

    uint32_t srcModeId = 0;
    uint32_t dstModeId = 0;

    std::vector<DXGI_MODE_DESC1> modeList;

    while (GetMonitorDisplayMode(m_monitor, srcModeId++, &devMode)) {
      // Skip interlaced modes altogether
      if (devMode.dmDisplayFlags & DM_INTERLACED)
        continue;

      // Skip modes with incompatible formats
      if (devMode.dmBitsPerPel != GetMonitorFormatBpp(EnumFormat))
        continue;

      if (pDesc != nullptr) {
        DXGI_MODE_DESC1 mode;
        mode.Width            = devMode.dmPelsWidth;
        mode.Height           = devMode.dmPelsHeight;
        mode.RefreshRate      = { devMode.dmDisplayFrequency * 1000, 1000 };
        mode.Format           = EnumFormat;
        mode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE;
        mode.Scaling          = DXGI_MODE_SCALING_UNSPECIFIED;
        mode.Stereo           = FALSE;
        modeList.push_back(mode);
      }

      dstModeId += 1;
    }

    // Sort display modes by width, height and refresh rate,
    // in that order. Some games rely on correct ordering.
    std::sort(modeList.begin(), modeList.end(),
      [] (const DXGI_MODE_DESC1& a, const DXGI_MODE_DESC1& b) {
        if (a.Width < b.Width) return true;
        if (a.Width > b.Width) return false;

        if (a.Height < b.Height) return true;
        if (a.Height > b.Height) return false;

        return (a.RefreshRate.Numerator / a.RefreshRate.Denominator)
             < (b.RefreshRate.Numerator / b.RefreshRate.Denominator);
      });

    // If requested, write out the first set of display
    // modes to the destination array.
    if (pDesc != nullptr) {
      for (uint32_t i = 0; i < *pNumModes && i < dstModeId; i++)
        pDesc[i] = modeList[i];

      if (dstModeId > *pNumModes)
        return DXGI_ERROR_MORE_DATA;
    }

    *pNumModes = dstModeId;
    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE DxgiOutput::GetDisplaySurfaceData(IDXGISurface* pDestination) {
    Logger::err("DxgiOutput::GetDisplaySurfaceData: Not implemented");
    return E_NOTIMPL;
  }

  HRESULT STDMETHODCALLTYPE DxgiOutput::GetFrameStatistics(DXGI_FRAME_STATISTICS* pStats) {
    DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;
    HRESULT hr = m_monitorInfo->AcquireMonitorData(m_monitor, &monitorInfo);

    if (FAILED(hr))
      return hr;

    static bool s_errorShown = false;

    if (!std::exchange(s_errorShown, true))
      Logger::warn("DxgiOutput::GetFrameStatistics: Stub");

    *pStats = monitorInfo->FrameStats;
    m_monitorInfo->ReleaseMonitorData();
    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE DxgiOutput::GetGammaControl(DXGI_GAMMA_CONTROL* pArray) {
    DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;
    HRESULT hr = m_monitorInfo->AcquireMonitorData(m_monitor, &monitorInfo);

    if (FAILED(hr))
      return hr;

    *pArray = monitorInfo->GammaCurve;
    m_monitorInfo->ReleaseMonitorData();
    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE DxgiOutput::GetGammaControlCapabilities(DXGI_GAMMA_CONTROL_CAPABILITIES* pGammaCaps) {
    pGammaCaps->ScaleAndOffsetSupported = FALSE;
    pGammaCaps->MaxConvertedValue       = 1.0f;
    pGammaCaps->MinConvertedValue       = 0.0f;
    pGammaCaps->NumGammaControlPoints   = DXGI_VK_GAMMA_CP_COUNT;

    for (uint32_t i = 0; i < pGammaCaps->NumGammaControlPoints; i++)
      pGammaCaps->ControlPointPositions[i] = GammaControlPointLocation(i);
    return S_OK;
  }

  void STDMETHODCALLTYPE DxgiOutput::ReleaseOwnership() {
    Logger::warn("DxgiOutput::ReleaseOwnership: Stub");
  }

  HRESULT STDMETHODCALLTYPE DxgiOutput::SetDisplaySurface(IDXGISurface* pScanoutSurface) {
    Logger::err("DxgiOutput::SetDisplaySurface: Not implemented");
    return E_NOTIMPL;
  }

  HRESULT STDMETHODCALLTYPE DxgiOutput::GetDisplaySurfaceData1(IDXGIResource* pDestination) {
    Logger::err("DxgiOutput::SetDisplaySurface1: Not implemented");
    return E_NOTIMPL;
  }

  HRESULT STDMETHODCALLTYPE DxgiOutput::SetGammaControl(const DXGI_GAMMA_CONTROL* pArray) {
    DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;
    HRESULT hr = m_monitorInfo->AcquireMonitorData(m_monitor, &monitorInfo);

    if (FAILED(hr))
      return hr;

    monitorInfo->GammaCurve = *pArray;

    if (monitorInfo->pSwapChain) {
      hr = monitorInfo->pSwapChain->SetGammaControl(
        DXGI_VK_GAMMA_CP_COUNT, pArray->GammaCurve);
    }

    m_monitorInfo->ReleaseMonitorData();
    return hr;
  }

  HRESULT STDMETHODCALLTYPE DxgiOutput::TakeOwnership(
          IUnknown *pDevice,
          BOOL     Exclusive) {
    Logger::warn("DxgiOutput::TakeOwnership: Stub");
    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE DxgiOutput::WaitForVBlank() {
    static bool s_errorShown = false;

    if (!std::exchange(s_errorShown, true))
      Logger::warn("DxgiOutput::WaitForVBlank: Stub");
    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE DxgiOutput::DuplicateOutput(
          IUnknown*                 pDevice,
          IDXGIOutputDuplication**  ppOutputDuplication) {
    return DuplicateOutput1(pDevice, 0, 0, nullptr, ppOutputDuplication);
  }

  HRESULT STDMETHODCALLTYPE DxgiOutput::DuplicateOutput1(
          IUnknown*                 pDevice,
          UINT                      Flags,
          UINT                      SupportedFormatsCount,
    const DXGI_FORMAT*              pSupportedFormats,
          IDXGIOutputDuplication**  ppOutputDuplication) {
    InitReturnPtr(ppOutputDuplication);

    if (!pDevice)
      return E_INVALIDARG;

    static bool s_errorShown = false;

    if (!std::exchange(s_errorShown, true))
      Logger::err("DxgiOutput::DuplicateOutput1: Not implemented");

    // At least return a valid error code
    return DXGI_ERROR_UNSUPPORTED;
  }

  BOOL DxgiOutput::SupportsOverlays() {
    return FALSE;
  }

  HRESULT STDMETHODCALLTYPE DxgiOutput::CheckOverlaySupport(
          DXGI_FORMAT EnumFormat,
          IUnknown*   pConcernedDevice,
          UINT*       pFlags) {
    Logger::warn("DxgiOutput: CheckOverlaySupport: Stub");
    return DXGI_ERROR_UNSUPPORTED;
  }

  HRESULT STDMETHODCALLTYPE DxgiOutput::CheckOverlayColorSpaceSupport(
          DXGI_FORMAT           Format,
          DXGI_COLOR_SPACE_TYPE ColorSpace,
          IUnknown*             pConcernedDevice,
          UINT*                 pFlags) {
    Logger::warn("DxgiOutput: CheckOverlayColorSpaceSupport: Stub");
    return DXGI_ERROR_UNSUPPORTED;
  }

  HRESULT STDMETHODCALLTYPE DxgiOutput::CheckHardwareCompositionSupport(
          UINT*                 pFlags) {
    Logger::warn("DxgiOutput: CheckHardwareCompositionSupport: Stub");

    *pFlags = 0;
    return S_OK;
  }

  void DxgiOutput::FilterModesByDesc(
          std::vector<DXGI_MODE_DESC1>& Modes,
    const DXGI_MODE_DESC1&              TargetMode) {
    // Filter modes based on format properties
    bool testScanlineOrder = false;
    bool testScaling       = false;
    bool testFormat        = false;

    for (const auto& mode : Modes) {
      testScanlineOrder |= TargetMode.ScanlineOrdering != DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED
                        && TargetMode.ScanlineOrdering == mode.ScanlineOrdering;
      testScaling       |= TargetMode.Scaling != DXGI_MODE_SCALING_UNSPECIFIED
                        && TargetMode.Scaling == mode.Scaling;
      testFormat        |= TargetMode.Format != DXGI_FORMAT_UNKNOWN
                        && TargetMode.Format == mode.Format;
    }

    for (auto it = Modes.begin(); it != Modes.end(); ) {
      bool skipMode = it->Stereo != TargetMode.Stereo;

      if (testScanlineOrder)
        skipMode |= it->ScanlineOrdering != TargetMode.ScanlineOrdering;

      if (testScaling)
        skipMode |= it->Scaling != TargetMode.Scaling;

      if (testFormat)
        skipMode |= it->Format != TargetMode.Format;

      it = skipMode ? Modes.erase(it) : ++it;
    }

    // Filter by closest resolution
    uint32_t minDiffResolution  = 0;

    if (TargetMode.Width) {
      minDiffResolution = std::accumulate(
        Modes.begin(), Modes.end(), std::numeric_limits<uint32_t>::max(),
        [&TargetMode] (uint32_t current, const DXGI_MODE_DESC1& mode) {
          uint32_t diff = std::abs(int32_t(TargetMode.Width  - mode.Width))
                        + std::abs(int32_t(TargetMode.Height - mode.Height));
          return std::min(current, diff);
        });

      for (auto it = Modes.begin(); it != Modes.end(); ) {
        uint32_t diff = std::abs(int32_t(TargetMode.Width  - it->Width))
                      + std::abs(int32_t(TargetMode.Height - it->Height));

        bool skipMode = diff != minDiffResolution;
        it = skipMode ? Modes.erase(it) : ++it;
      }
    }

    // Filter by closest refresh rate
    uint32_t minDiffRefreshRate = 0;

    if (TargetMode.RefreshRate.Numerator && TargetMode.RefreshRate.Denominator) {
      minDiffRefreshRate = std::accumulate(
        Modes.begin(), Modes.end(), std::numeric_limits<uint64_t>::max(),
        [&TargetMode] (uint64_t current, const DXGI_MODE_DESC1& mode) {
          uint64_t rate = uint64_t(mode.RefreshRate.Numerator)
                        * uint64_t(TargetMode.RefreshRate.Denominator)
                        / uint64_t(mode.RefreshRate.Denominator);
          uint64_t diff = std::abs(int64_t(rate - uint64_t(TargetMode.RefreshRate.Numerator)));
          return std::min(current, diff);
        });

      for (auto it = Modes.begin(); it != Modes.end(); ) {
        uint64_t rate = uint64_t(it->RefreshRate.Numerator)
                      * uint64_t(TargetMode.RefreshRate.Denominator)
                      / uint64_t(it->RefreshRate.Denominator);
        uint64_t diff = std::abs(int64_t(rate - uint64_t(TargetMode.RefreshRate.Numerator)));

        bool skipMode = diff != minDiffRefreshRate;
        it = skipMode ? Modes.erase(it) : ++it;
      }
    }
  }

}


code:
#pragma once

#include "dxgi_monitor.h"
#include "dxgi_object.h"

namespace dxvk {

  class DxgiAdapter;
  class DxgiFactory;

  /**
   * \brief Number of gamma control points
   */
  constexpr uint32_t DXGI_VK_GAMMA_CP_COUNT = 1024;

  /**
   * \brief Computes gamma control point location
   *
   * \param [in] CpIndex Control point ID
   * \returns Location of the control point
   */
  inline float GammaControlPointLocation(uint32_t CpIndex) {
    return float(CpIndex) / float(DXGI_VK_GAMMA_CP_COUNT - 1);
  }

  class DxgiOutput : public DxgiObject<IDXGIOutput6> {

  public:

    DxgiOutput(
      const Com<DxgiFactory>& factory,
      const Com<DxgiAdapter>& adapter,
            HMONITOR          monitor);

    ~DxgiOutput();

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

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

    HRESULT STDMETHODCALLTYPE FindClosestMatchingMode(
      const DXGI_MODE_DESC*       pModeToMatch,
            DXGI_MODE_DESC*       pClosestMatch,
            IUnknown*             pConcernedDevice) final;

    HRESULT STDMETHODCALLTYPE FindClosestMatchingMode1(
      const DXGI_MODE_DESC1*      pModeToMatch,
            DXGI_MODE_DESC1*      pClosestMatch,
            IUnknown*             pConcernedDevice) final;

    HRESULT STDMETHODCALLTYPE GetDesc(
            DXGI_OUTPUT_DESC*     pDesc) final;

    HRESULT STDMETHODCALLTYPE GetDesc1(
            DXGI_OUTPUT_DESC1*    pDesc) final;

    HRESULT STDMETHODCALLTYPE GetDisplayModeList(
            DXGI_FORMAT           EnumFormat,
            UINT                  Flags,
            UINT*                 pNumModes,
            DXGI_MODE_DESC*       pDesc) final;

    HRESULT STDMETHODCALLTYPE GetDisplayModeList1(
            DXGI_FORMAT           EnumFormat,
            UINT                  Flags,
            UINT*                 pNumModes,
            DXGI_MODE_DESC1*      pDesc) final;

    HRESULT STDMETHODCALLTYPE GetDisplaySurfaceData(
            IDXGISurface*         pDestination) final;

    HRESULT STDMETHODCALLTYPE GetDisplaySurfaceData1(
            IDXGIResource*        pDestination) final;

    HRESULT STDMETHODCALLTYPE GetFrameStatistics(
            DXGI_FRAME_STATISTICS* pStats) final;

    HRESULT STDMETHODCALLTYPE GetGammaControl(
            DXGI_GAMMA_CONTROL*   pArray) final;

    HRESULT STDMETHODCALLTYPE GetGammaControlCapabilities(
            DXGI_GAMMA_CONTROL_CAPABILITIES *pGammaCaps) final;

    void STDMETHODCALLTYPE ReleaseOwnership() final;

    HRESULT STDMETHODCALLTYPE SetDisplaySurface(
            IDXGISurface*         pScanoutSurface) final;

    HRESULT STDMETHODCALLTYPE SetGammaControl(
      const DXGI_GAMMA_CONTROL*   pArray) final;

    HRESULT STDMETHODCALLTYPE TakeOwnership(
            IUnknown*             pDevice,
            BOOL                  Exclusive) final;

    HRESULT STDMETHODCALLTYPE WaitForVBlank() final;

    HRESULT STDMETHODCALLTYPE DuplicateOutput(
            IUnknown*                 pDevice,
            IDXGIOutputDuplication**  ppOutputDuplication) final;

    HRESULT STDMETHODCALLTYPE DuplicateOutput1(
            IUnknown*                 pDevice,
            UINT                      Flags,
            UINT                      SupportedFormatsCount,
      const DXGI_FORMAT*              pSupportedFormats,
            IDXGIOutputDuplication**  ppOutputDuplication) final;

    BOOL STDMETHODCALLTYPE SupportsOverlays() final;

    HRESULT STDMETHODCALLTYPE CheckOverlaySupport(
            DXGI_FORMAT           EnumFormat,
            IUnknown*             pConcernedDevice,
            UINT*                 pFlags) final;

    HRESULT STDMETHODCALLTYPE CheckOverlayColorSpaceSupport(
            DXGI_FORMAT           Format,
            DXGI_COLOR_SPACE_TYPE ColorSpace,
            IUnknown*             pConcernedDevice,
            UINT*                 pFlags) final;

    HRESULT STDMETHODCALLTYPE CheckHardwareCompositionSupport(
            UINT*                 pFlags) final;

  private:

    DxgiMonitorInfo* m_monitorInfo = nullptr;
    Com<DxgiAdapter> m_adapter = nullptr;
    HMONITOR         m_monitor = nullptr;

    static void FilterModesByDesc(
            std::vector<DXGI_MODE_DESC1>& Modes,
      const DXGI_MODE_DESC1&              TargetMode);

  };

}


code:
#include "dxgi_factory.h"
#include "dxgi_output.h"
#include "dxgi_swapchain.h"

namespace dxvk {

  DxgiSwapChain::DxgiSwapChain(
          IDXGIFactory*               pFactory,
          IDXGIVkSwapChain*           pPresenter,
          HWND                        hWnd,
    const DXGI_SWAP_CHAIN_DESC1*      pDesc,
    const DXGI_SWAP_CHAIN_FULLSCREEN_DESC*  pFullscreenDesc)
  : m_factory   (pFactory),
    m_window    (hWnd),
    m_desc      (*pDesc),
    m_descFs    (*pFullscreenDesc),
    m_presentCount(0u),
    m_presenter (pPresenter),
    m_monitor   (nullptr) {
    if (FAILED(m_presenter->GetAdapter(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&m_adapter))))
      throw DxvkError("DXGI: Failed to get adapter for present device");

    // Query monitor info form DXVK's DXGI factory, if available
    m_factory->QueryInterface(__uuidof(IDXGIVkMonitorInfo), reinterpret_cast<void**>(&m_monitorInfo));

    // Apply initial window mode and fullscreen state
    if (!m_descFs.Windowed && FAILED(EnterFullscreenMode(nullptr)))
      throw DxvkError("DXGI: Failed to set initial fullscreen state");
  }

  DxgiSwapChain::~DxgiSwapChain() {
    RestoreDisplayMode(m_monitor);

    // Decouple swap chain from monitor if necessary
    DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;

    if (SUCCEEDED(AcquireMonitorData(m_monitor, &monitorInfo))) {
      if (monitorInfo->pSwapChain == this)
        monitorInfo->pSwapChain = nullptr;

      ReleaseMonitorData();
    }
  }

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

    *ppvObject = nullptr;

    if (riid == __uuidof(IUnknown)
     || riid == __uuidof(IDXGIObject)
     || riid == __uuidof(IDXGIDeviceSubObject)
     || riid == __uuidof(IDXGISwapChain)
     || riid == __uuidof(IDXGISwapChain1)
     || riid == __uuidof(IDXGISwapChain2)
     || riid == __uuidof(IDXGISwapChain3)
     || riid == __uuidof(IDXGISwapChain4)) {
      *ppvObject = ref(this);
      return S_OK;
    }

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

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetParent(REFIID riid, void** ppParent) {
    return m_factory->QueryInterface(riid, ppParent);
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetDevice(REFIID riid, void** ppDevice) {
    return m_presenter->GetDevice(riid, ppDevice);
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetBuffer(UINT Buffer, REFIID riid, void** ppSurface) {
    return m_presenter->GetImage(Buffer, riid, ppSurface);
  }

  UINT STDMETHODCALLTYPE DxgiSwapChain::GetCurrentBackBufferIndex() {
    return m_presenter->GetImageIndex();
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetContainingOutput(IDXGIOutput** ppOutput) {
    InitReturnPtr(ppOutput);

    if (!IsWindow(m_window))
      return DXGI_ERROR_INVALID_CALL;

    if (m_target != nullptr) {
      *ppOutput = m_target.ref();
      return S_OK;
    }

    RECT windowRect = { 0, 0, 0, 0 };
    ::GetWindowRect(m_window, &windowRect);

    HMONITOR monitor = ::MonitorFromPoint(
      { (windowRect.left + windowRect.right) / 2,
        (windowRect.top + windowRect.bottom) / 2 },
      MONITOR_DEFAULTTOPRIMARY);

    return GetOutputFromMonitor(monitor, ppOutput);
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetDesc(DXGI_SWAP_CHAIN_DESC* pDesc) {
    if (!pDesc)
      return E_INVALIDARG;

    pDesc->BufferDesc.Width     = m_desc.Width;
    pDesc->BufferDesc.Height    = m_desc.Height;
    pDesc->BufferDesc.RefreshRate = m_descFs.RefreshRate;
    pDesc->BufferDesc.Format    = m_desc.Format;
    pDesc->BufferDesc.ScanlineOrdering = m_descFs.ScanlineOrdering;
    pDesc->BufferDesc.Scaling   = m_descFs.Scaling;
    pDesc->SampleDesc           = m_desc.SampleDesc;
    pDesc->BufferUsage          = m_desc.BufferUsage;
    pDesc->BufferCount          = m_desc.BufferCount;
    pDesc->OutputWindow         = m_window;
    pDesc->Windowed             = m_descFs.Windowed;
    pDesc->SwapEffect           = m_desc.SwapEffect;
    pDesc->Flags                = m_desc.Flags;
    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetDesc1(DXGI_SWAP_CHAIN_DESC1* pDesc) {
    if (pDesc == nullptr)
      return E_INVALIDARG;

    *pDesc = m_desc;
    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetBackgroundColor(
          DXGI_RGBA*                pColor) {
    Logger::err("DxgiSwapChain::GetBackgroundColor: Not implemented");
    return E_NOTIMPL;
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetRotation(
          DXGI_MODE_ROTATION*       pRotation) {
    Logger::err("DxgiSwapChain::GetRotation: Not implemented");
    return E_NOTIMPL;
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetRestrictToOutput(
          IDXGIOutput**             ppRestrictToOutput) {
    InitReturnPtr(ppRestrictToOutput);

    Logger::err("DxgiSwapChain::GetRestrictToOutput: Not implemented");
    return E_NOTIMPL;
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetFrameStatistics(DXGI_FRAME_STATISTICS* pStats) {
    std::lock_guard<dxvk::recursive_mutex> lock(m_lockWindow);

    if (!pStats)
      return E_INVALIDARG;

    static bool s_errorShown = false;

    if (!std::exchange(s_errorShown, true))
      Logger::warn("DxgiSwapChain::GetFrameStatistics: Semi-stub");

    // TODO deal with the refresh counts at some point
    pStats->PresentCount = m_presentCount;
    pStats->PresentRefreshCount = 0;
    pStats->SyncRefreshCount = 0;
    QueryPerformanceCounter(&pStats->SyncQPCTime);
    pStats->SyncGPUTime.QuadPart = 0;
    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetFullscreenState(
          BOOL*         pFullscreen,
          IDXGIOutput** ppTarget) {
    HRESULT hr = S_OK;

    if (pFullscreen != nullptr)
      *pFullscreen = !m_descFs.Windowed;

    if (ppTarget != nullptr)
      *ppTarget = m_target.ref();

    return hr;
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetFullscreenDesc(
          DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pDesc) {
    if (pDesc == nullptr)
      return E_INVALIDARG;

    *pDesc = m_descFs;
    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetHwnd(
          HWND*                     pHwnd) {
    if (pHwnd == nullptr)
      return E_INVALIDARG;

    *pHwnd = m_window;
    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetCoreWindow(
          REFIID                    refiid,
          void**                    ppUnk) {
    InitReturnPtr(ppUnk);

    Logger::err("DxgiSwapChain::GetCoreWindow: Not implemented");
    return E_NOTIMPL;
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetLastPresentCount(UINT* pLastPresentCount) {
    if (pLastPresentCount == nullptr)
      return E_INVALIDARG;

    *pLastPresentCount = m_presentCount;
    return S_OK;
  }

  BOOL STDMETHODCALLTYPE DxgiSwapChain::IsTemporaryMonoSupported() {
    // This seems to be related to stereo 3D display
    // modes, which we don't support at the moment
    return FALSE;
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::Present(UINT SyncInterval, UINT Flags) {
    return Present1(SyncInterval, Flags, nullptr);
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::Present1(
          UINT                      SyncInterval,
          UINT                      PresentFlags,
    const DXGI_PRESENT_PARAMETERS*  pPresentParameters) {

    if (!IsWindow(m_window))
      return S_OK;

    if (SyncInterval > 4)
      return DXGI_ERROR_INVALID_CALL;

    std::lock_guard<dxvk::recursive_mutex> lockWin(m_lockWindow);
    std::lock_guard<dxvk::mutex> lockBuf(m_lockBuffer);

    try {
      HRESULT hr = m_presenter->Present(SyncInterval, PresentFlags, nullptr);
      if (hr == S_OK && !(PresentFlags & DXGI_PRESENT_TEST))
        m_presentCount++;
      return hr;
    } catch (const DxvkError& err) {
      Logger::err(err.message());
      return DXGI_ERROR_DRIVER_INTERNAL_ERROR;
    }
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::ResizeBuffers(
          UINT        BufferCount,
          UINT        Width,
          UINT        Height,
          DXGI_FORMAT NewFormat,
          UINT        SwapChainFlags) {
    if (!IsWindow(m_window))
      return DXGI_ERROR_INVALID_CALL;

    constexpr UINT PreserveFlags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;

    if ((m_desc.Flags & PreserveFlags) != (SwapChainFlags & PreserveFlags))
      return DXGI_ERROR_INVALID_CALL;

    std::lock_guard<dxvk::mutex> lock(m_lockBuffer);
    m_desc.Width  = Width;
    m_desc.Height = Height;

    GetWindowClientSize(m_window,
      m_desc.Width  ? nullptr : &m_desc.Width,
      m_desc.Height ? nullptr : &m_desc.Height);

    if (BufferCount != 0)
      m_desc.BufferCount = BufferCount;

    if (NewFormat != DXGI_FORMAT_UNKNOWN)
      m_desc.Format = NewFormat;

    return m_presenter->ChangeProperties(&m_desc);
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::ResizeBuffers1(
          UINT                      BufferCount,
          UINT                      Width,
          UINT                      Height,
          DXGI_FORMAT               Format,
          UINT                      SwapChainFlags,
    const UINT*                     pCreationNodeMask,
          IUnknown* const*          ppPresentQueue) {
    static bool s_errorShown = false;

    if (!std::exchange(s_errorShown, true))
      Logger::warn("DxgiSwapChain::ResizeBuffers1: Stub");

    return ResizeBuffers(BufferCount,
      Width, Height, Format, SwapChainFlags);
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::ResizeTarget(const DXGI_MODE_DESC* pNewTargetParameters) {
    std::lock_guard<dxvk::recursive_mutex> lock(m_lockWindow);

    if (pNewTargetParameters == nullptr)
      return DXGI_ERROR_INVALID_CALL;

    if (!IsWindow(m_window))
      return DXGI_ERROR_INVALID_CALL;

    // Update the swap chain description
    if (pNewTargetParameters->RefreshRate.Numerator != 0)
      m_descFs.RefreshRate = pNewTargetParameters->RefreshRate;

    m_descFs.ScanlineOrdering = pNewTargetParameters->ScanlineOrdering;
    m_descFs.Scaling          = pNewTargetParameters->Scaling;

    if (m_descFs.Windowed) {
      // Adjust window position and size
      RECT newRect = { 0, 0, 0, 0 };
      RECT oldRect = { 0, 0, 0, 0 };

      ::GetWindowRect(m_window, &oldRect);
      ::SetRect(&newRect, 0, 0, pNewTargetParameters->Width, pNewTargetParameters->Height);
      ::AdjustWindowRectEx(&newRect,
        ::GetWindowLongW(m_window, GWL_STYLE), FALSE,
        ::GetWindowLongW(m_window, GWL_EXSTYLE));
      ::SetRect(&newRect, 0, 0, newRect.right - newRect.left, newRect.bottom - newRect.top);
      ::OffsetRect(&newRect, oldRect.left, oldRect.top);
      ::MoveWindow(m_window, newRect.left, newRect.top,
          newRect.right - newRect.left, newRect.bottom - newRect.top, TRUE);
    } else {
      Com<IDXGIOutput> output;

      if (FAILED(GetOutputFromMonitor(m_monitor, &output))) {
        Logger::err("DXGI: ResizeTarget: Failed to query containing output");
        return E_FAIL;
      }

      // If the swap chain allows it, change the display mode
      if (m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH) {
        ChangeDisplayMode(output.ptr(), pNewTargetParameters);
        NotifyModeChange(m_monitor, FALSE);
      }

      // Resize and reposition the window to
      DXGI_OUTPUT_DESC desc;
      output->GetDesc(&desc);

      RECT newRect = desc.DesktopCoordinates;

      ::MoveWindow(m_window, newRect.left, newRect.top,
          newRect.right - newRect.left, newRect.bottom - newRect.top, TRUE);
    }

    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetFullscreenState(
          BOOL          Fullscreen,
          IDXGIOutput*  pTarget) {
    std::lock_guard<dxvk::recursive_mutex> lock(m_lockWindow);

    if (!Fullscreen && pTarget)
      return DXGI_ERROR_INVALID_CALL;

    if (m_descFs.Windowed && Fullscreen)
      return this->EnterFullscreenMode(pTarget);
    else if (!m_descFs.Windowed && !Fullscreen)
      return this->LeaveFullscreenMode();

    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetBackgroundColor(
    const DXGI_RGBA*                pColor) {
    Logger::err("DxgiSwapChain::SetBackgroundColor: Not implemented");
    return E_NOTIMPL;
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetRotation(
          DXGI_MODE_ROTATION        Rotation) {
    Logger::err("DxgiSwapChain::SetRotation: Not implemented");
    return E_NOTIMPL;
  }

  HANDLE STDMETHODCALLTYPE DxgiSwapChain::GetFrameLatencyWaitableObject() {
    if (!(m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT))
      return nullptr;

    return m_presenter->GetFrameLatencyEvent();
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetMatrixTransform(
          DXGI_MATRIX_3X2_F*        pMatrix) {
    // We don't support composition swap chains
    Logger::err("DxgiSwapChain::GetMatrixTransform: Not supported");
    return DXGI_ERROR_INVALID_CALL;
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetMaximumFrameLatency(
          UINT*                     pMaxLatency) {
    if (!(m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT))
      return DXGI_ERROR_INVALID_CALL;

    std::lock_guard<dxvk::recursive_mutex> lock(m_lockWindow);
    *pMaxLatency = m_presenter->GetFrameLatency();
    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetSourceSize(
          UINT*                     pWidth,
          UINT*                     pHeight) {
    // TODO implement properly once supported
    if (pWidth)  *pWidth  = m_desc.Width;
    if (pHeight) *pHeight = m_desc.Height;
    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetMatrixTransform(
    const DXGI_MATRIX_3X2_F*        pMatrix) {
    // We don't support composition swap chains
    Logger::err("DxgiSwapChain::SetMatrixTransform: Not supported");
    return DXGI_ERROR_INVALID_CALL;
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetMaximumFrameLatency(
          UINT                      MaxLatency) {
    if (!(m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT))
      return DXGI_ERROR_INVALID_CALL;

    std::lock_guard<dxvk::recursive_mutex> lock(m_lockWindow);
    return m_presenter->SetFrameLatency(MaxLatency);
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetSourceSize(
          UINT                      Width,
          UINT                      Height) {
    if (Width  == 0 || Width  > m_desc.Width
     || Height == 0 || Height > m_desc.Height)
      return E_INVALIDARG;

    RECT region;
    region.left   = 0;
    region.top    = 0;
    region.right  = Width;
    region.bottom = Height;
    return m_presenter->SetPresentRegion(&region);
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::CheckColorSpaceSupport(
          DXGI_COLOR_SPACE_TYPE           ColorSpace,
          UINT*                           pColorSpaceSupport) {
    if (!pColorSpaceSupport)
      return E_INVALIDARG;

    UINT supportFlags = 0;

    if (ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709)
      supportFlags |= DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT;

    *pColorSpaceSupport = supportFlags;
    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetColorSpace1(DXGI_COLOR_SPACE_TYPE ColorSpace) {
    UINT support = 0;

    HRESULT hr = CheckColorSpaceSupport(ColorSpace, &support);

    if (FAILED(hr))
      return hr;

    if (!support)
      return E_INVALIDARG;

    return S_OK;
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetHDRMetaData(
          DXGI_HDR_METADATA_TYPE    Type,
          UINT                      Size,
          void*                     pMetaData) {
    if (Size && !pMetaData)
      return E_INVALIDARG;

    switch (Type) {
      case DXGI_HDR_METADATA_TYPE_NONE:
        return S_OK;

      case DXGI_HDR_METADATA_TYPE_HDR10:
        if (Size != sizeof(DXGI_HDR_METADATA_HDR10))
          return E_INVALIDARG;

        // For some reason this always seems to succeed on Windows
        Logger::warn("DXGI: HDR not supported");
        return S_OK;

      default:
        Logger::err(str::format("DXGI: Invalid HDR metadata type: ", Type));
        return E_INVALIDARG;
    }
  }

  HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetGammaControl(
          UINT                      NumPoints,
    const DXGI_RGB*                 pGammaCurve) {
    std::lock_guard<dxvk::mutex> lockBuf(m_lockBuffer);
    return m_presenter->SetGammaControl(NumPoints, pGammaCurve);
  }

  HRESULT DxgiSwapChain::EnterFullscreenMode(IDXGIOutput* pTarget) {
    Com<IDXGIOutput> output = pTarget;

    if (!IsWindow(m_window))
      return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE;

    if (output == nullptr) {
      if (FAILED(GetContainingOutput(&output))) {
        Logger::err("DXGI: EnterFullscreenMode: Cannot query containing output");
        return E_FAIL;
      }
    }

    // Find a display mode that matches what we need
    ::GetWindowRect(m_window, &m_windowState.rect);

    if (m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH) {
      DXGI_MODE_DESC displayMode;
      displayMode.Width            = m_desc.Width;
      displayMode.Height           = m_desc.Height;
      displayMode.RefreshRate      = m_descFs.RefreshRate;
      displayMode.Format           = m_desc.Format;
      // Ignore these two, games usually use them wrong and we don't
      // support any scaling modes except UNSPECIFIED anyway.
      displayMode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
      displayMode.Scaling          = DXGI_MODE_SCALING_UNSPECIFIED;

      if (FAILED(ChangeDisplayMode(output.ptr(), &displayMode))) {
        Logger::err("DXGI: EnterFullscreenMode: Failed to change display mode");
        return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE;
      }
    }

    // Update swap chain description
    m_descFs.Windowed = FALSE;

    // Change the window flags to remove the decoration etc.
    LONG style   = ::GetWindowLongW(m_window, GWL_STYLE);
    LONG exstyle = ::GetWindowLongW(m_window, GWL_EXSTYLE);

    m_windowState.style = style;
    m_windowState.exstyle = exstyle;

    style   &= ~WS_OVERLAPPEDWINDOW;
    exstyle &= ~WS_EX_OVERLAPPEDWINDOW;

    ::SetWindowLongW(m_window, GWL_STYLE, style);
    ::SetWindowLongW(m_window, GWL_EXSTYLE, exstyle);

    // Move the window so that it covers the entire output
    DXGI_OUTPUT_DESC desc;
    output->GetDesc(&desc);

    const RECT rect = desc.DesktopCoordinates;

    ::SetWindowPos(m_window, HWND_TOPMOST,
      rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
      SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOACTIVATE);

    m_monitor = desc.Monitor;
    m_target  = std::move(output);

    // Apply current gamma curve of the output
    DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;

    if (SUCCEEDED(AcquireMonitorData(m_monitor, &monitorInfo))) {
      if (!monitorInfo->pSwapChain)
        monitorInfo->pSwapChain = this;

      SetGammaControl(DXGI_VK_GAMMA_CP_COUNT, monitorInfo->GammaCurve.GammaCurve);
      ReleaseMonitorData();
    }

    NotifyModeChange(m_monitor, FALSE);
    return S_OK;
  }

  HRESULT DxgiSwapChain::LeaveFullscreenMode() {
    if (FAILED(RestoreDisplayMode(m_monitor)))
      Logger::warn("DXGI: LeaveFullscreenMode: Failed to restore display mode");

    // Reset gamma control and decouple swap chain from monitor
    DXGI_VK_MONITOR_DATA* monitorInfo = nullptr;

    if (SUCCEEDED(AcquireMonitorData(m_monitor, &monitorInfo))) {
      if (monitorInfo->pSwapChain == this)
        monitorInfo->pSwapChain = nullptr;

      SetGammaControl(0, nullptr);
      ReleaseMonitorData();
    }

    // Restore internal state
    HMONITOR monitor = m_monitor;

    m_descFs.Windowed = TRUE;
    m_monitor = nullptr;
    m_target  = nullptr;

    if (!IsWindow(m_window))
      return S_OK;

    // Only restore the window style if the application hasn't
    // changed them. This is in line with what native DXGI does.
    LONG curStyle   = ::GetWindowLongW(m_window, GWL_STYLE) & ~WS_VISIBLE;
    LONG curExstyle = ::GetWindowLongW(m_window, GWL_EXSTYLE) & ~WS_EX_TOPMOST;

    if (curStyle == (m_windowState.style & ~(WS_VISIBLE | WS_OVERLAPPEDWINDOW))
     && curExstyle == (m_windowState.exstyle & ~(WS_EX_TOPMOST | WS_EX_OVERLAPPEDWINDOW))) {
      ::SetWindowLongW(m_window, GWL_STYLE,   m_windowState.style);
      ::SetWindowLongW(m_window, GWL_EXSTYLE, m_windowState.exstyle);
    }

    // Restore window position and apply the style
    const RECT rect = m_windowState.rect;

    ::SetWindowPos(m_window, (m_windowState.exstyle & WS_EX_TOPMOST) ? HWND_TOPMOST : HWND_NOTOPMOST,
      rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
      SWP_FRAMECHANGED | SWP_NOACTIVATE);

    NotifyModeChange(monitor, TRUE);
    return S_OK;
  }

  HRESULT DxgiSwapChain::ChangeDisplayMode(
          IDXGIOutput*            pOutput,
    const DXGI_MODE_DESC*         pDisplayMode) {
    if (!pOutput)
      return DXGI_ERROR_INVALID_CALL;

    // Find a mode that the output supports
    DXGI_OUTPUT_DESC outputDesc;
    pOutput->GetDesc(&outputDesc);

    DXGI_MODE_DESC preferredMode = *pDisplayMode;
    DXGI_MODE_DESC selectedMode;

    if (preferredMode.Format == DXGI_FORMAT_UNKNOWN)
      preferredMode.Format = m_desc.Format;

    HRESULT hr = pOutput->FindClosestMatchingMode(
      &preferredMode, &selectedMode, nullptr);

    if (FAILED(hr)) {
      Logger::err(str::format(
        "DXGI: Failed to query closest mode:",
        "\n  Format: ", preferredMode.Format,
        "\n  Mode:   ", preferredMode.Width, "x", preferredMode.Height,
          "@", preferredMode.RefreshRate.Numerator / preferredMode.RefreshRate.Denominator));
      return hr;
    }

    DEVMODEW devMode = { };
    devMode.dmSize       = sizeof(devMode);
    devMode.dmFields     = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL;
    devMode.dmPelsWidth  = selectedMode.Width;
    devMode.dmPelsHeight = selectedMode.Height;
    devMode.dmBitsPerPel = GetMonitorFormatBpp(selectedMode.Format);

    if (selectedMode.RefreshRate.Numerator != 0)  {
      devMode.dmFields |= DM_DISPLAYFREQUENCY;
      devMode.dmDisplayFrequency = selectedMode.RefreshRate.Numerator
                                 / selectedMode.RefreshRate.Denominator;
    }

    return SetMonitorDisplayMode(outputDesc.Monitor, &devMode)
      ? S_OK
      : DXGI_ERROR_NOT_CURRENTLY_AVAILABLE;
  }

  HRESULT DxgiSwapChain::RestoreDisplayMode(HMONITOR hMonitor) {
    if (!hMonitor)
      return DXGI_ERROR_INVALID_CALL;

    return RestoreMonitorDisplayMode()
      ? S_OK
      : DXGI_ERROR_NOT_CURRENTLY_AVAILABLE;
  }

  HRESULT DxgiSwapChain::GetSampleCount(UINT Count, VkSampleCountFlagBits* pCount) const {
    switch (Count) {
      case  1: *pCount = VK_SAMPLE_COUNT_1_BIT;  return S_OK;
      case  2: *pCount = VK_SAMPLE_COUNT_2_BIT;  return S_OK;
      case  4: *pCount = VK_SAMPLE_COUNT_4_BIT;  return S_OK;
      case  8: *pCount = VK_SAMPLE_COUNT_8_BIT;  return S_OK;
      case 16: *pCount = VK_SAMPLE_COUNT_16_BIT; return S_OK;
    }

    return E_INVALIDARG;
  }

  HRESULT DxgiSwapChain::GetOutputFromMonitor(
          HMONITOR                  Monitor,
          IDXGIOutput**             ppOutput) {
    if (!ppOutput)
      return DXGI_ERROR_INVALID_CALL;

    for (uint32_t i = 0; SUCCEEDED(m_adapter->EnumOutputs(i, ppOutput)); i++) {
      DXGI_OUTPUT_DESC outputDesc;
      (*ppOutput)->GetDesc(&outputDesc);

      if (outputDesc.Monitor == Monitor)
        return S_OK;

      (*ppOutput)->Release();
      (*ppOutput) = nullptr;
    }

    return DXGI_ERROR_NOT_FOUND;
  }

  HRESULT DxgiSwapChain::AcquireMonitorData(
          HMONITOR                hMonitor,
          DXGI_VK_MONITOR_DATA**  ppData) {
    return m_monitorInfo != nullptr
      ? m_monitorInfo->AcquireMonitorData(hMonitor, ppData)
      : E_NOINTERFACE;
  }

  void DxgiSwapChain::ReleaseMonitorData() {
    if (m_monitorInfo != nullptr)
      m_monitorInfo->ReleaseMonitorData();
  }

  void DxgiSwapChain::NotifyModeChange(
          HMONITOR                hMonitor,
          BOOL                    Windowed) {
    DEVMODEW devMode = { };
    devMode.dmSize       = sizeof(devMode);
    devMode.dmFields     = DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY;

    if (GetMonitorDisplayMode(hMonitor, ENUM_CURRENT_SETTINGS, &devMode)) {
      DXGI_MODE_DESC displayMode = { };
      displayMode.Width            = devMode.dmPelsWidth;
      displayMode.Height           = devMode.dmPelsHeight;
      displayMode.RefreshRate      = { devMode.dmDisplayFrequency, 1 };
      displayMode.Format           = m_desc.Format;
      displayMode.ScanlineOrdering = m_descFs.ScanlineOrdering;
      displayMode.Scaling          = m_descFs.Scaling;
      m_presenter->NotifyModeChange(Windowed, &displayMode);
    } else {
      Logger::warn("Failed to query current display mode");
      m_presenter->NotifyModeChange(Windowed, nullptr);
    }
  }

}


code:
#pragma once

#include <memory>
#include <mutex>

#include "dxgi_interfaces.h"
#include "dxgi_monitor.h"
#include "dxgi_object.h"

#include "../d3d11/d3d11_interfaces.h"

#include "../spirv/spirv_module.h"

#include "../util/util_time.h"

namespace dxvk {

  class DxgiDevice;
  class DxgiFactory;
  class DxgiOutput;

  class DxgiSwapChain : public DxgiObject<IDXGISwapChain4> {

  public:

    DxgiSwapChain(
            IDXGIFactory*               pFactory,
            IDXGIVkSwapChain*           pPresenter,
            HWND                        hWnd,
      const DXGI_SWAP_CHAIN_DESC1*      pDesc,
      const DXGI_SWAP_CHAIN_FULLSCREEN_DESC*  pFullscreenDesc);

    ~DxgiSwapChain();

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

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

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

    HRESULT STDMETHODCALLTYPE GetBuffer(
            UINT                      Buffer,
            REFIID                    riid,
            void**                    ppSurface) final;

    UINT STDMETHODCALLTYPE GetCurrentBackBufferIndex() final;

    HRESULT STDMETHODCALLTYPE GetContainingOutput(
            IDXGIOutput**             ppOutput) final;

    HRESULT STDMETHODCALLTYPE GetDesc(
            DXGI_SWAP_CHAIN_DESC*     pDesc) final;

    HRESULT STDMETHODCALLTYPE GetDesc1(
            DXGI_SWAP_CHAIN_DESC1*    pDesc) final;

    HRESULT STDMETHODCALLTYPE GetFullscreenState(
            BOOL*                     pFullscreen,
            IDXGIOutput**             ppTarget) final;

    HRESULT STDMETHODCALLTYPE GetFullscreenDesc(
            DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pDesc) final;

    HRESULT STDMETHODCALLTYPE GetHwnd(
            HWND*                     pHwnd) final;

    HRESULT STDMETHODCALLTYPE GetCoreWindow(
            REFIID                    refiid,
            void**                    ppUnk) final;

    HRESULT STDMETHODCALLTYPE GetBackgroundColor(
            DXGI_RGBA*                pColor) final;

    HRESULT STDMETHODCALLTYPE GetRotation(
            DXGI_MODE_ROTATION*       pRotation) final;

    HRESULT STDMETHODCALLTYPE GetRestrictToOutput(
            IDXGIOutput**             ppRestrictToOutput) final;

    HRESULT STDMETHODCALLTYPE GetFrameStatistics(
            DXGI_FRAME_STATISTICS*    pStats) final;

    HRESULT STDMETHODCALLTYPE GetLastPresentCount(
            UINT*                     pLastPresentCount) final;

    BOOL STDMETHODCALLTYPE IsTemporaryMonoSupported() final;

    HRESULT STDMETHODCALLTYPE Present(
            UINT                      SyncInterval,
            UINT                      Flags) final;

    HRESULT STDMETHODCALLTYPE Present1(
            UINT                      SyncInterval,
            UINT                      PresentFlags,
      const DXGI_PRESENT_PARAMETERS*  pPresentParameters) final;

    HRESULT STDMETHODCALLTYPE ResizeBuffers(
            UINT                      BufferCount,
            UINT                      Width,
            UINT                      Height,
            DXGI_FORMAT               NewFormat,
            UINT                      SwapChainFlags) final;

    HRESULT STDMETHODCALLTYPE ResizeBuffers1(
            UINT                      BufferCount,
            UINT                      Width,
            UINT                      Height,
            DXGI_FORMAT               Format,
            UINT                      SwapChainFlags,
      const UINT*                     pCreationNodeMask,
            IUnknown* const*          ppPresentQueue) final;

    HRESULT STDMETHODCALLTYPE ResizeTarget(
      const DXGI_MODE_DESC*           pNewTargetParameters) final;

    HRESULT STDMETHODCALLTYPE SetFullscreenState(
            BOOL                      Fullscreen,
            IDXGIOutput*              pTarget) final;

    HRESULT STDMETHODCALLTYPE SetBackgroundColor(
      const DXGI_RGBA*                pColor) final;

    HRESULT STDMETHODCALLTYPE SetRotation(
            DXGI_MODE_ROTATION        Rotation) final;

    HANDLE STDMETHODCALLTYPE GetFrameLatencyWaitableObject() final;

    HRESULT STDMETHODCALLTYPE GetMatrixTransform(
            DXGI_MATRIX_3X2_F*        pMatrix) final;

    HRESULT STDMETHODCALLTYPE GetMaximumFrameLatency(
            UINT*                     pMaxLatency) final;

    HRESULT STDMETHODCALLTYPE GetSourceSize(
            UINT*                     pWidth,
            UINT*                     pHeight) final;

    HRESULT STDMETHODCALLTYPE SetMatrixTransform(
      const DXGI_MATRIX_3X2_F*        pMatrix) final;

    HRESULT STDMETHODCALLTYPE SetMaximumFrameLatency(
            UINT                      MaxLatency) final;

    HRESULT STDMETHODCALLTYPE SetSourceSize(
            UINT                      Width,
            UINT                      Height) final;

    HRESULT STDMETHODCALLTYPE CheckColorSpaceSupport(
            DXGI_COLOR_SPACE_TYPE     ColorSpace,
            UINT*                     pColorSpaceSupport) final;

    HRESULT STDMETHODCALLTYPE SetColorSpace1(
            DXGI_COLOR_SPACE_TYPE     ColorSpace) final;

    HRESULT STDMETHODCALLTYPE SetHDRMetaData(
            DXGI_HDR_METADATA_TYPE    Type,
            UINT                      Size,
            void*                     pMetaData) final;

    HRESULT STDMETHODCALLTYPE SetGammaControl(
            UINT                      NumPoints,
      const DXGI_RGB*                 pGammaCurve);

  private:

    struct WindowState {
      LONG style   = 0;
      LONG exstyle = 0;
      RECT rect    = { 0, 0, 0, 0 };
    };

    dxvk::recursive_mutex           m_lockWindow;
    dxvk::mutex                     m_lockBuffer;

    Com<IDXGIFactory>               m_factory;
    Com<IDXGIAdapter>               m_adapter;
    Com<IDXGIOutput>                m_target;
    Com<IDXGIVkMonitorInfo>         m_monitorInfo;

    HWND                            m_window;
    DXGI_SWAP_CHAIN_DESC1           m_desc;
    DXGI_SWAP_CHAIN_FULLSCREEN_DESC m_descFs;
    UINT                            m_presentCount;

    Com<IDXGIVkSwapChain>           m_presenter;

    HMONITOR                        m_monitor;
    WindowState                     m_windowState;

    HRESULT EnterFullscreenMode(
            IDXGIOutput             *pTarget);

    HRESULT LeaveFullscreenMode();

    HRESULT ChangeDisplayMode(
            IDXGIOutput*            pOutput,
      const DXGI_MODE_DESC*         pDisplayMode);

    HRESULT RestoreDisplayMode(
            HMONITOR                hMonitor);

    HRESULT GetSampleCount(
            UINT                    Count,
            VkSampleCountFlagBits*  pCount) const;

    HRESULT GetOutputFromMonitor(
            HMONITOR                Monitor,
            IDXGIOutput**           ppOutput);

    HRESULT AcquireMonitorData(
            HMONITOR                hMonitor,
            DXGI_VK_MONITOR_DATA**  ppData);

    void ReleaseMonitorData();

    void NotifyModeChange(
            HMONITOR                hMonitor,
            BOOL                    Windowed);

  };

}


code:
#pragma once

#include "dxgi_swapchain.h"

namespace dxvk {

  class DxgiSwapChainDispatcher : public IDXGISwapChain4 {

  public:

    DxgiSwapChainDispatcher(IDXGISwapChain4* dispatch)
      : m_dispatch(dispatch) {
    }

    virtual ~DxgiSwapChainDispatcher() {
    }

    ULONG STDMETHODCALLTYPE AddRef() {
      if (likely(m_dispatch != nullptr))
        return m_dispatch->AddRef();

      return 0;
    }

    ULONG STDMETHODCALLTYPE Release() {
      if (likely(m_dispatch != nullptr)) {
        ULONG refCount = m_dispatch->Release();

        if (unlikely(!refCount)) {
          // Clear the underlying swap chain pointer. Helps in
          // use-after-free situations encountered in Divinity
          // Original Sin Enhanced Edition.
          m_dispatch = nullptr;
          delete this;
        }

        return refCount;
      }

      return ~0u;
    }

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

      *ppvObject = nullptr;

      if (riid == __uuidof(IUnknown)
       || riid == __uuidof(IDXGIObject)
       || riid == __uuidof(IDXGIDeviceSubObject)
       || riid == __uuidof(IDXGISwapChain)
       || riid == __uuidof(IDXGISwapChain1)
       || riid == __uuidof(IDXGISwapChain2)
       || riid == __uuidof(IDXGISwapChain3)
       || riid == __uuidof(IDXGISwapChain4)) {
        *ppvObject = ref(this);
        return S_OK;
      }

      Logger::warn("DxgiSwapChainDispatcher::QueryInterface: Unknown interface query");
      Logger::warn(str::format(riid));
      return m_dispatch->QueryInterface(riid, ppvObject);
    }

    HRESULT STDMETHODCALLTYPE GetPrivateData(
            REFGUID       Name,
            UINT*         pDataSize,
            void*         pData) final {
      return m_dispatch->GetPrivateData(Name, pDataSize, pData);
    }

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

    HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(
            REFGUID       Name,
      const IUnknown*     pUnknown) final {
      return m_dispatch->SetPrivateDataInterface(Name, pUnknown);
    }

    HRESULT STDMETHODCALLTYPE GetParent(
            REFIID                    riid,
            void**                    ppParent) final {
      return m_dispatch->GetParent(riid, ppParent);
    }

    HRESULT STDMETHODCALLTYPE GetDevice(
            REFIID                    riid,
            void**                    ppDevice) final {
      return m_dispatch->GetDevice(riid, ppDevice);
    }

    HRESULT STDMETHODCALLTYPE GetBuffer(
            UINT                      Buffer,
            REFIID                    riid,
            void**                    ppSurface) final {
      return m_dispatch->GetBuffer(Buffer, riid, ppSurface);
    }

    UINT STDMETHODCALLTYPE GetCurrentBackBufferIndex() final {
      return m_dispatch->GetCurrentBackBufferIndex();
    }

    HRESULT STDMETHODCALLTYPE GetContainingOutput(
            IDXGIOutput**             ppOutput) final {
      return m_dispatch->GetContainingOutput(ppOutput);
    }

    HRESULT STDMETHODCALLTYPE GetDesc(
            DXGI_SWAP_CHAIN_DESC*     pDesc) final {
      return m_dispatch->GetDesc(pDesc);
    }

    HRESULT STDMETHODCALLTYPE GetDesc1(
            DXGI_SWAP_CHAIN_DESC1*    pDesc) final {
      return m_dispatch->GetDesc1(pDesc);
    }

    HRESULT STDMETHODCALLTYPE GetFullscreenState(
            BOOL*                     pFullscreen,
            IDXGIOutput**             ppTarget) final {
      if (likely(m_dispatch != nullptr))
        return m_dispatch->GetFullscreenState(pFullscreen, ppTarget);

      if (pFullscreen)
        *pFullscreen = FALSE;

      if (ppTarget)
        *ppTarget = nullptr;

      return S_OK;
    }

    HRESULT STDMETHODCALLTYPE GetFullscreenDesc(
            DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pDesc) final {
      return m_dispatch->GetFullscreenDesc(pDesc);
    }

    HRESULT STDMETHODCALLTYPE GetHwnd(
            HWND*                     pHwnd) final {
      return m_dispatch->GetHwnd(pHwnd);
    }

    HRESULT STDMETHODCALLTYPE GetCoreWindow(
            REFIID                    refiid,
            void**                    ppUnk) final {
      return m_dispatch->GetCoreWindow(refiid, ppUnk);
    }

    HRESULT STDMETHODCALLTYPE GetBackgroundColor(
            DXGI_RGBA*                pColor) final {
      return m_dispatch->GetBackgroundColor(pColor);
    }

    HRESULT STDMETHODCALLTYPE GetRotation(
            DXGI_MODE_ROTATION*       pRotation) final {
      return m_dispatch->GetRotation(pRotation);
    }

    HRESULT STDMETHODCALLTYPE GetRestrictToOutput(
            IDXGIOutput**             ppRestrictToOutput) final {
      return m_dispatch->GetRestrictToOutput(ppRestrictToOutput);
    }

    HRESULT STDMETHODCALLTYPE GetFrameStatistics(
            DXGI_FRAME_STATISTICS*    pStats) final {
      return m_dispatch->GetFrameStatistics(pStats);
    }

    HRESULT STDMETHODCALLTYPE GetLastPresentCount(
            UINT*                     pLastPresentCount) final {
      return m_dispatch->GetLastPresentCount(pLastPresentCount);
    }

    BOOL STDMETHODCALLTYPE IsTemporaryMonoSupported() final {
      return m_dispatch->IsTemporaryMonoSupported();
    }

    HRESULT STDMETHODCALLTYPE Present(
            UINT                      SyncInterval,
            UINT                      Flags) final {
      return m_dispatch->Present(SyncInterval, Flags);
    }

    HRESULT STDMETHODCALLTYPE Present1(
            UINT                      SyncInterval,
            UINT                      PresentFlags,
      const DXGI_PRESENT_PARAMETERS*  pPresentParameters) final {
      return m_dispatch->Present1(SyncInterval, PresentFlags, pPresentParameters);
    }

    HRESULT STDMETHODCALLTYPE ResizeBuffers(
            UINT                      BufferCount,
            UINT                      Width,
            UINT                      Height,
            DXGI_FORMAT               NewFormat,
            UINT                      SwapChainFlags) final {
      return m_dispatch->ResizeBuffers(BufferCount, Width, Height, NewFormat, SwapChainFlags);
    }

    HRESULT STDMETHODCALLTYPE ResizeBuffers1(
            UINT                      BufferCount,
            UINT                      Width,
            UINT                      Height,
            DXGI_FORMAT               Format,
            UINT                      SwapChainFlags,
      const UINT*                     pCreationNodeMask,
            IUnknown* const*          ppPresentQueue) final {
      return m_dispatch->ResizeBuffers1(BufferCount, Width, Height, Format, SwapChainFlags, pCreationNodeMask, ppPresentQueue);
    }

    HRESULT STDMETHODCALLTYPE ResizeTarget(
      const DXGI_MODE_DESC*           pNewTargetParameters) final {
      return m_dispatch->ResizeTarget(pNewTargetParameters);
    }

    HRESULT STDMETHODCALLTYPE SetFullscreenState(
            BOOL                      Fullscreen,
            IDXGIOutput*              pTarget) final {
      if (likely(m_dispatch != nullptr))
        return m_dispatch->SetFullscreenState(Fullscreen, pTarget);
      return S_OK;
    }

    HRESULT STDMETHODCALLTYPE SetBackgroundColor(
      const DXGI_RGBA*                pColor) final {
      return m_dispatch->SetBackgroundColor(pColor);
    }

    HRESULT STDMETHODCALLTYPE SetRotation(
            DXGI_MODE_ROTATION        Rotation) final {
      return m_dispatch->SetRotation(Rotation);
    }

    HANDLE STDMETHODCALLTYPE GetFrameLatencyWaitableObject() final {
      return m_dispatch->GetFrameLatencyWaitableObject();
    }

    HRESULT STDMETHODCALLTYPE GetMatrixTransform(
            DXGI_MATRIX_3X2_F*        pMatrix) final {
      return m_dispatch->GetMatrixTransform(pMatrix);
    }

    HRESULT STDMETHODCALLTYPE GetMaximumFrameLatency(
            UINT*                     pMaxLatency) final {
      return m_dispatch->GetMaximumFrameLatency(pMaxLatency);
    }

    HRESULT STDMETHODCALLTYPE GetSourceSize(
            UINT*                     pWidth,
            UINT*                     pHeight) final {
      return m_dispatch->GetSourceSize(pWidth, pHeight);
    }

    HRESULT STDMETHODCALLTYPE SetMatrixTransform(
      const DXGI_MATRIX_3X2_F*        pMatrix) final {
      return m_dispatch->SetMatrixTransform(pMatrix);
    }

    HRESULT STDMETHODCALLTYPE SetMaximumFrameLatency(
            UINT                      MaxLatency) final {
      return m_dispatch->SetMaximumFrameLatency(MaxLatency);
    }

    HRESULT STDMETHODCALLTYPE SetSourceSize(
            UINT                      Width,
            UINT                      Height) final {
      return m_dispatch->SetSourceSize(Width, Height);
    }

    HRESULT STDMETHODCALLTYPE CheckColorSpaceSupport(
            DXGI_COLOR_SPACE_TYPE     ColorSpace,
            UINT*                     pColorSpaceSupport) final {
      return m_dispatch->CheckColorSpaceSupport(ColorSpace, pColorSpaceSupport);
    }

    HRESULT STDMETHODCALLTYPE SetColorSpace1(
            DXGI_COLOR_SPACE_TYPE     ColorSpace) final {
      return m_dispatch->SetColorSpace1(ColorSpace);
    }

    HRESULT STDMETHODCALLTYPE SetHDRMetaData(
            DXGI_HDR_METADATA_TYPE    Type,
            UINT                      Size,
            void*                     pMetaData) final {
      return m_dispatch->SetHDRMetaData(Type, Size, pMetaData);
    }

  private:

    IDXGISwapChain4* m_dispatch;

  };

}


code:
dxgi_res = wrc_generator.process('version.rc')

dxgi_src = [
  'dxgi_adapter.cpp',
  'dxgi_enums.cpp',
  'dxgi_factory.cpp',
  'dxgi_format.cpp',
  'dxgi_main.cpp',
  'dxgi_monitor.cpp',
  'dxgi_options.cpp',
  'dxgi_output.cpp',
  'dxgi_swapchain.cpp',
]

dxgi_dll = shared_library('dxgi'+dll_ext, dxgi_src, dxgi_res,
  name_prefix         : '',
  dependencies        : [ dxvk_dep ],
  include_directories : dxvk_include_path,
  install             : true,
  vs_module_defs      : 'dxgi'+def_spec_ext,
)

dxgi_dep = declare_dependency(
  link_with           : [ dxgi_dll ],
  include_directories : [ dxvk_include_path ],
)


code:
#include <windows.h>

// DLL version information.
VS_VERSION_INFO    VERSIONINFO
FILEVERSION        10,0,17763,1
PRODUCTVERSION     10,0,17763,1
FILEFLAGSMASK      VS_FFI_FILEFLAGSMASK
FILEFLAGS          0
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
  BLOCK "StringFileInfo"
  BEGIN
    BLOCK "080904b0"
    BEGIN
      VALUE "CompanyName",      "DXVK"
      VALUE "FileDescription",  "DirectX Graphics Infrastructure"
      VALUE "FileVersion",      "10.0.17763.1 (WinBuild.160101.0800)"
      VALUE "InternalName",     "dxgi.dll"
      VALUE "LegalCopyright",   "zlib/libpng license"
      VALUE "OriginalFilename", "dxgi.dll"
      VALUE "ProductName",      "DXVK"
      VALUE "ProductVersion",   "10.0.17763.1"
    END
  END
  BLOCK "VarFileInfo"
  BEGIN
    VALUE "Translation", 0x0809, 1200
  END
END


code:
#include "dxso_analysis.h"

namespace dxvk {

  DxsoAnalyzer::DxsoAnalyzer(
    DxsoAnalysisInfo& analysis)
    : m_analysis(&analysis) { }

  void DxsoAnalyzer::processInstruction(
    const DxsoInstructionContext& ctx) {
    DxsoOpcode opcode = ctx.instruction.opcode;

    // Co-issued CNDs are issued before their parents,
    // except when the parent is a CND.
    if (opcode == DxsoOpcode::Cnd &&
        m_parentOpcode != DxsoOpcode::Cnd &&
        ctx.instruction.coissue) {
      m_analysis->coissues.push_back(ctx);
    }

    if (opcode == DxsoOpcode::TexKill)
      m_analysis->usesKill = true;

    if (opcode == DxsoOpcode::DsX
     || opcode == DxsoOpcode::DsY

     || opcode == DxsoOpcode::Tex
     || opcode == DxsoOpcode::TexCoord
     || opcode == DxsoOpcode::TexBem
     || opcode == DxsoOpcode::TexBemL
     || opcode == DxsoOpcode::TexReg2Ar
     || opcode == DxsoOpcode::TexReg2Gb
     || opcode == DxsoOpcode::TexM3x2Pad
     || opcode == DxsoOpcode::TexM3x2Tex
     || opcode == DxsoOpcode::TexM3x3Pad
     || opcode == DxsoOpcode::TexM3x3Tex
     || opcode == DxsoOpcode::TexM3x3Spec
     || opcode == DxsoOpcode::TexM3x3VSpec
     || opcode == DxsoOpcode::TexReg2Rgb
     || opcode == DxsoOpcode::TexDp3Tex
     || opcode == DxsoOpcode::TexM3x2Depth
     || opcode == DxsoOpcode::TexDp3
     || opcode == DxsoOpcode::TexM3x3
     //  Explicit LOD.
     //|| opcode == DxsoOpcode::TexLdd
     //|| opcode == DxsoOpcode::TexLdl
     || opcode == DxsoOpcode::TexDepth)
      m_analysis->usesDerivatives = true;

    m_parentOpcode = ctx.instruction.opcode;
  }

  void DxsoAnalyzer::finalize(size_t tokenCount) {
    m_analysis->bytecodeByteLength = tokenCount * sizeof(uint32_t);
  }

}


code:
#pragma once

#include "dxso_modinfo.h"
#include "dxso_decoder.h"

namespace dxvk {

  struct DxsoAnalysisInfo {
    uint32_t bytecodeByteLength;

    bool usesDerivatives = false;
    bool usesKill        = false;

    std::vector<DxsoInstructionContext> coissues;
  };

  class DxsoAnalyzer {

  public:

    DxsoAnalyzer(
            DxsoAnalysisInfo& analysis);

    /**
     * \brief Processes a single instruction
     * \param [in] ins The instruction
     */
    void processInstruction(
      const DxsoInstructionContext& ctx);

    void finalize(size_t tokenCount);

  private:

    DxsoAnalysisInfo* m_analysis = nullptr;

    DxsoOpcode m_parentOpcode;

  };

}


code:
#include "dxso_code.h"

namespace dxvk {

  DxsoCode::DxsoCode(DxsoReader& reader) {
    m_code =
      reinterpret_cast<const uint32_t*>(reader.currentPtr());
  }

  const uint32_t* DxsoCodeIter::ptrAt(uint32_t id) const {
    return m_ptr + id;
  }

  uint32_t DxsoCodeIter::at(uint32_t id) const {
    return m_ptr[id];
  }

  uint32_t DxsoCodeIter::read() {
    return *(m_ptr++);
  }

  DxsoCodeIter DxsoCodeIter::skip(uint32_t n) const {
    return DxsoCodeIter(m_ptr + n);
  }

}


code:
#pragma once

#include "dxso_include.h"
#include "dxso_reader.h"

#include <vector>
#include <cstdint>

namespace dxvk {

  /**
   * \brief DXBC code iterator
   *
   * Convenient pointer wrapper that allows
   * reading the code token stream.
   */
  class DxsoCodeIter {

  public:

    DxsoCodeIter(
      const uint32_t* ptr)
    : m_ptr(ptr) { }

    const uint32_t* ptrAt(uint32_t id) const;

    uint32_t at(uint32_t id) const;
    uint32_t read();

    DxsoCodeIter skip(uint32_t n) const;

  private:

    const uint32_t* m_ptr = nullptr;

  };

  class DxsoCode {

  public:

    DxsoCode(DxsoReader& reader);

    DxsoCodeIter iter() const {
      return DxsoCodeIter(m_code);
    }

  private:

    const uint32_t* m_code;

  };

}


code:
#include "dxso_common.h"

namespace dxvk {

  VkShaderStageFlagBits DxsoProgramInfo::shaderStage() const {
    switch (m_type) {
      case DxsoProgramTypes::PixelShader:  return VK_SHADER_STAGE_FRAGMENT_BIT;
      case DxsoProgramTypes::VertexShader: return VK_SHADER_STAGE_VERTEX_BIT;
      default:                             break;
    }

    throw DxvkError("DxsoProgramInfo::shaderStage: Unsupported program type");
  }

  spv::ExecutionModel DxsoProgramInfo::executionModel() const {
    switch (m_type) {
      case DxsoProgramTypes::PixelShader:  return spv::ExecutionModelFragment;
      case DxsoProgramTypes::VertexShader: return spv::ExecutionModelVertex;
      default:                             break;
    }

    throw DxvkError("DxsoProgramInfo::executionModel: Unsupported program type");
  }

}


code:
#pragma once

#include "dxso_include.h"

#include <cstdint>

namespace dxvk {

 /**
 * \brief DXSO Program type
 *
 * Defines the shader stage that a DXSO
 * module has been compiled for.
 */
  namespace DxsoProgramTypes {
    enum DxsoProgramType : uint16_t {
      VertexShader = 0,
      PixelShader  = 1,
      Count        = 2,
    };
  }
  using DxsoProgramType = DxsoProgramTypes::DxsoProgramType;

  class DxsoProgramInfo {

  public:

    DxsoProgramInfo() { }
    DxsoProgramInfo(
            DxsoProgramType type,
            uint32_t        minorVersion,
            uint32_t        majorVersion)
      : m_type{ type }
      , m_minorVersion{ minorVersion }
      , m_majorVersion{ majorVersion } {}

    /**
     * \brief Program type
     * \returns Program type
     */
    DxsoProgramType type() const {
      return m_type;
    }

    /**
     * \brief Vulkan shader stage
     *
     * The \c VkShaderStageFlagBits constant
     * that corresponds to the program type.
     * \returns Vulkan shader stage
     */
    VkShaderStageFlagBits shaderStage() const;

    /**
     * \brief SPIR-V execution model
     *
     * The execution model that corresponds
     * to the Vulkan shader stage.
     * \returns SPIR-V execution model
     */
    spv::ExecutionModel executionModel() const;

    /**
     * \brief Minor version
     * \returns The minor version of the shader model.
     */
    uint32_t minorVersion() const {
      return m_minorVersion;
    }

    /**
     * \brief Major version
     * \returns The major version of the shader model.
     */
    uint32_t majorVersion() const {
      return m_majorVersion;
    }

  private:

    DxsoProgramType m_type;

    uint32_t        m_minorVersion;
    uint32_t        m_majorVersion;

  };

}