From bed6d23e7f0f106b42f883ce4c4dde07280b1634 Mon Sep 17 00:00:00 2001
From: Philip Rebohle <philip.rebohle@tu-dortmund.de>
Date: Wed, 11 Oct 2017 03:09:04 +0200
Subject: [PATCH] [dxgi] Implemented some DXGI classes

---
 src/dxgi/dxgi_adapter.cpp      | 105 ++++++++++++++
 src/dxgi/dxgi_adapter.h        |  50 +++++++
 src/dxgi/dxgi_factory.cpp      |  88 ++++++++++++
 src/dxgi/dxgi_factory.h        |  55 ++++++++
 src/dxgi/dxgi_include.h        |  18 +++
 src/dxgi/dxgi_main.cpp         |  48 +++++++
 src/dxgi/dxgi_object.h         |  41 ++++++
 src/dxgi/dxgi_output.cpp       | 244 +++++++++++++++++++++++++++++++++
 src/dxgi/dxgi_output.h         |  74 ++++++++++
 src/dxgi/dxgi_private_data.cpp | 149 ++++++++++++++++++++
 src/dxgi/dxgi_private_data.h   | 105 ++++++++++++++
 src/dxgi/dxgi_swapchain.cpp    |   0
 src/dxgi/dxgi_swapchain.h      |   0
 src/dxgi/meson.build           |  17 +++
 src/meson.build                |   4 +-
 15 files changed, 997 insertions(+), 1 deletion(-)
 create mode 100644 src/dxgi/dxgi_adapter.cpp
 create mode 100644 src/dxgi/dxgi_adapter.h
 create mode 100644 src/dxgi/dxgi_factory.cpp
 create mode 100644 src/dxgi/dxgi_factory.h
 create mode 100644 src/dxgi/dxgi_include.h
 create mode 100644 src/dxgi/dxgi_main.cpp
 create mode 100644 src/dxgi/dxgi_object.h
 create mode 100644 src/dxgi/dxgi_output.cpp
 create mode 100644 src/dxgi/dxgi_output.h
 create mode 100644 src/dxgi/dxgi_private_data.cpp
 create mode 100644 src/dxgi/dxgi_private_data.h
 create mode 100644 src/dxgi/dxgi_swapchain.cpp
 create mode 100644 src/dxgi/dxgi_swapchain.h
 create mode 100644 src/dxgi/meson.build

diff --git a/src/dxgi/dxgi_adapter.cpp b/src/dxgi/dxgi_adapter.cpp
new file mode 100644
index 000000000..e067ad050
--- /dev/null
+++ b/src/dxgi/dxgi_adapter.cpp
@@ -0,0 +1,105 @@
+#include <cstdlib>
+#include <cstring>
+
+#include "dxgi_adapter.h"
+#include "dxgi_factory.h"
+#include "dxgi_output.h"
+
+namespace dxvk {
+
+  DxgiAdapter::DxgiAdapter(
+          DxgiFactory*      factory,
+    const Rc<DxvkAdapter>&  adapter)
+  : m_factory (factory),
+    m_adapter (adapter) {
+    TRACE(this, factory, adapter);
+  }
+  
+  
+  DxgiAdapter::~DxgiAdapter() {
+    TRACE(this);
+  }
+  
+  
+  HRESULT DxgiAdapter::QueryInterface(
+          REFIID riid,
+          void **ppvObject) {
+    COM_QUERY_IFACE(riid, ppvObject, IDXGIAdapter);
+    
+    Logger::warn("DxgiAdapter::QueryInterface: Unknown interface query");
+    return E_NOINTERFACE;
+  }
+  
+  
+  HRESULT DxgiAdapter::GetParent(
+          REFIID riid,
+          void   **ppParent) {
+    return m_factory->QueryInterface(riid, ppParent);
+  }
+  
+  
+  HRESULT DxgiAdapter::CheckInterfaceSupport(
+          REFGUID       InterfaceName,
+          LARGE_INTEGER *pUMDVersion) {
+    Logger::err("DxgiAdapter::CheckInterfaceSupport: No D3D10 support");
+    return DXGI_ERROR_UNSUPPORTED;
+  }
+  
+  
+  HRESULT DxgiAdapter::EnumOutputs(
+          UINT        Output,
+          IDXGIOutput **ppOutput) {
+    TRACE(this, Output, ppOutput);
+    
+    if (ppOutput == nullptr)
+      return DXGI_ERROR_INVALID_CALL;
+    
+    int numDisplays = SDL_GetNumVideoDisplays();
+    
+    if (numDisplays < 0) {
+      Logger::err("DxgiAdapter::EnumOutputs: Failed to query display count");
+      return DXGI_ERROR_DRIVER_INTERNAL_ERROR;
+    }
+    
+    if (Output >= static_cast<uint32_t>(numDisplays))
+      return DXGI_ERROR_NOT_FOUND;
+    
+    *ppOutput = ref(new DxgiOutput(this, Output));
+    return S_OK;
+  }
+  
+  
+  HRESULT DxgiAdapter::GetDesc(DXGI_ADAPTER_DESC* pDesc) {
+    if (pDesc == nullptr)
+      return DXGI_ERROR_INVALID_CALL;
+    
+    const auto deviceProp = m_adapter->deviceProperties();
+    const auto memoryProp = m_adapter->memoryProperties();
+    
+    std::memset(pDesc->Description, 0, sizeof(pDesc->Description));
+    std::mbstowcs(pDesc->Description, deviceProp.deviceName, _countof(pDesc->Description) - 1);
+    
+    VkDeviceSize deviceMemory = 0;
+    VkDeviceSize sharedMemory = 0;
+    
+    for (uint32_t i = 0; i < memoryProp.memoryHeapCount; i++) {
+      VkMemoryHeap heap = memoryProp.memoryHeaps[i];
+      
+      if (heap.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT)
+        deviceMemory += heap.size;
+      else
+        sharedMemory += heap.size;
+    }
+    
+    pDesc->VendorId               = deviceProp.vendorID;
+    pDesc->DeviceId               = deviceProp.deviceID;
+    pDesc->SubSysId               = 0;
+    pDesc->Revision               = 0;
+    pDesc->DedicatedVideoMemory   = deviceMemory;
+    pDesc->DedicatedSystemMemory  = 0;
+    pDesc->SharedSystemMemory     = sharedMemory;
+    pDesc->AdapterLuid            = LUID { 0, 0 };  // TODO implement
+    return S_OK;
+  }
+  
+}
diff --git a/src/dxgi/dxgi_adapter.h b/src/dxgi/dxgi_adapter.h
new file mode 100644
index 000000000..972ecd6f3
--- /dev/null
+++ b/src/dxgi/dxgi_adapter.h
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <memory>
+#include <vector>
+
+#include <dxvk_adapter.h>
+
+#include "dxgi_object.h"
+
+namespace dxvk {
+  
+  class DxgiFactory;
+  class DxgiOutput;
+  
+  class DxgiAdapter : public DxgiObject<IDXGIAdapter> {
+    
+  public:
+    
+    DxgiAdapter(
+            DxgiFactory*      factory,
+      const Rc<DxvkAdapter>&  adapter);
+    ~DxgiAdapter();
+    
+    HRESULT QueryInterface(
+            REFIID riid,
+            void **ppvObject) final;
+    
+    HRESULT GetParent(
+            REFIID riid,
+            void   **ppParent) final;
+    
+    HRESULT CheckInterfaceSupport(
+            REFGUID       InterfaceName,
+            LARGE_INTEGER *pUMDVersion) final;
+    
+    HRESULT EnumOutputs(
+            UINT        Output,
+            IDXGIOutput **ppOutput) final;
+    
+    HRESULT GetDesc(
+            DXGI_ADAPTER_DESC *pDesc) final;
+    
+  private:
+    
+    DxgiFactory*    m_factory;
+    Rc<DxvkAdapter> m_adapter;
+    
+  };
+
+}
diff --git a/src/dxgi/dxgi_factory.cpp b/src/dxgi/dxgi_factory.cpp
new file mode 100644
index 000000000..82d92de57
--- /dev/null
+++ b/src/dxgi/dxgi_factory.cpp
@@ -0,0 +1,88 @@
+#include "dxgi_factory.h"
+#include "dxgi_swapchain.h"
+
+namespace dxvk {
+  
+  DxgiFactory::DxgiFactory()
+  : m_instance(new DxvkInstance()) {
+    TRACE(this);
+    
+    auto adapters = m_instance->enumAdapters();
+    for (auto a : adapters)
+      m_adapters.push_back(new DxgiAdapter(this, a));
+  }
+  
+  
+  DxgiFactory::~DxgiFactory() {
+    TRACE(this);
+  }
+  
+  
+  HRESULT DxgiFactory::QueryInterface(
+          REFIID  riid,
+          void**  ppvObject) {
+    COM_QUERY_IFACE(riid, ppvObject, IDXGIFactory);
+    
+    Logger::warn("DxgiFactory::QueryInterface: Unknown interface query");
+    return E_NOINTERFACE;
+  }
+  
+  
+  HRESULT DxgiFactory::GetParent(
+          REFIID  riid,
+          void**  ppParent) {
+    Logger::warn("DxgiFactory::GetParent: Unknown interface query");
+    return E_NOINTERFACE;
+  }
+  
+  
+  HRESULT DxgiFactory::CreateSoftwareAdapter(
+          HMODULE         Module,
+          IDXGIAdapter**  ppAdapter) {
+    Logger::err("DxgiFactory::CreateSoftwareAdapter: Software adapters not supported");
+    return DXGI_ERROR_UNSUPPORTED;
+  }
+  
+  
+  HRESULT DxgiFactory::CreateSwapChain(
+          IUnknown*             pDevice,
+          DXGI_SWAP_CHAIN_DESC* pDesc,
+          IDXGISwapChain**      ppSwapChain) {
+    TRACE(this, pDevice, pDesc, ppSwapChain);
+    return DXGI_ERROR_UNSUPPORTED;
+  }
+  
+  
+  HRESULT DxgiFactory::EnumAdapters(
+          UINT            Adapter,
+          IDXGIAdapter**  ppAdapter) {
+    TRACE(this, Adapter, ppAdapter);
+    
+    if (ppAdapter == nullptr)
+      return DXGI_ERROR_INVALID_CALL;
+    
+    if (Adapter >= m_adapters.size())
+      return DXGI_ERROR_NOT_FOUND;
+    
+    *ppAdapter = m_adapters.at(Adapter).ref();
+    return S_OK;
+  }
+  
+  
+  HRESULT DxgiFactory::GetWindowAssociation(HWND *pWindowHandle) {
+    if (pWindowHandle == nullptr)
+      return DXGI_ERROR_INVALID_CALL;
+    
+    *pWindowHandle = m_associatedWindow;
+    return S_OK;
+  }
+  
+  
+  HRESULT DxgiFactory::MakeWindowAssociation(HWND WindowHandle, UINT Flags) {
+    TRACE(this, WindowHandle, Flags);
+    Logger::warn("DxgiFactory::MakeWindowAssociation: Ignoring flags");
+    m_associatedWindow = WindowHandle;
+    return S_OK;
+  }
+  
+}
diff --git a/src/dxgi/dxgi_factory.h b/src/dxgi/dxgi_factory.h
new file mode 100644
index 000000000..6903dc45b
--- /dev/null
+++ b/src/dxgi/dxgi_factory.h
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <vector>
+
+#include <dxvk_instance.h>
+
+#include "dxgi_adapter.h"
+
+namespace dxvk {
+    
+  class DxgiFactory : public DxgiObject<IDXGIFactory> {
+    
+  public:
+    
+    DxgiFactory();
+    ~DxgiFactory();
+    
+    HRESULT QueryInterface(
+            REFIID  riid,
+            void**  ppvObject) final;
+    
+    HRESULT GetParent(
+            REFIID  riid,
+            void**  ppParent) final;
+    
+    HRESULT CreateSoftwareAdapter(
+            HMODULE         Module,
+            IDXGIAdapter**  ppAdapter) final;
+    
+    HRESULT CreateSwapChain(
+            IUnknown*             pDevice,
+            DXGI_SWAP_CHAIN_DESC* pDesc,
+            IDXGISwapChain**      ppSwapChain) final;
+    
+    HRESULT EnumAdapters(
+            UINT            Adapter,
+            IDXGIAdapter**  ppAdapter) final;
+    
+    HRESULT GetWindowAssociation(
+            HWND *pWindowHandle) final;
+    
+    HRESULT MakeWindowAssociation(
+            HWND WindowHandle,
+            UINT Flags) final;
+    
+  private:
+    
+    Rc<DxvkInstance>              m_instance;
+    std::vector<Com<DxgiAdapter>> m_adapters;
+    
+    HWND m_associatedWindow = nullptr;
+    
+  };
+  
+}
diff --git a/src/dxgi/dxgi_include.h b/src/dxgi/dxgi_include.h
new file mode 100644
index 000000000..5fab09b35
--- /dev/null
+++ b/src/dxgi/dxgi_include.h
@@ -0,0 +1,18 @@
+#pragma once
+
+#define DLLEXPORT __declspec(dllexport)
+
+#include "../util/com/com_guid.h"
+#include "../util/com/com_object.h"
+#include "../util/com/com_pointer.h"
+
+#include "../util/log/log.h"
+#include "../util/log/log_debug.h"
+
+#include "../util/util_enum.h"
+#include "../util/util_error.h"
+#include "../util/util_string.h"
+
+#include <dxgi1_2.h>
+
+#include <SDL2/SDL.h>
diff --git a/src/dxgi/dxgi_main.cpp b/src/dxgi/dxgi_main.cpp
new file mode 100644
index 000000000..5fe9fa435
--- /dev/null
+++ b/src/dxgi/dxgi_main.cpp
@@ -0,0 +1,48 @@
+#include "dxgi_factory.h"
+#include "dxgi_include.h"
+
+namespace dxvk {
+  
+  struct SdlInstance {
+    SdlInstance() {
+      TRACE(this);
+      
+      if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE))
+        Logger::err("Instance::init: Failed to initialize SDL");
+    }
+    
+    ~SdlInstance() {
+      TRACE(this);
+      SDL_Quit();
+    }
+  };
+  
+  SdlInstance sdl;
+  
+  HRESULT createDxgiFactory(REFIID riid, void **ppFactory) {
+    TRACE(riid, ppFactory);
+    
+    if (riid != __uuidof(IDXGIFactory)) {
+      Logger::err("CreateDXGIFactory: Requested version of IDXGIFactory not supported");
+      return DXGI_ERROR_UNSUPPORTED;
+    }
+    
+    try {
+      *ppFactory = ref(new DxgiFactory());
+      return S_OK;
+    } catch (const DxvkError& err) {
+      Logger::err(err.message());
+      return DXGI_ERROR_UNSUPPORTED;
+    }
+  }
+}
+
+extern "C" {
+  DLLEXPORT HRESULT __stdcall CreateDXGIFactory1(REFIID riid, void **ppFactory) {
+    return dxvk::createDxgiFactory(riid, ppFactory);
+  }
+  
+  DLLEXPORT HRESULT __stdcall CreateDXGIFactory(REFIID riid, void **ppFactory) {
+    return dxvk::createDxgiFactory(riid, ppFactory);
+  }
+}
\ No newline at end of file
diff --git a/src/dxgi/dxgi_object.h b/src/dxgi/dxgi_object.h
new file mode 100644
index 000000000..004b6dbb9
--- /dev/null
+++ b/src/dxgi/dxgi_object.h
@@ -0,0 +1,41 @@
+#pragma once
+
+#include "dxgi_private_data.h"
+
+namespace dxvk {
+  
+  template<typename Base>
+  class DxgiObject : public ComObject<Base> {
+    
+  public:
+    
+    HRESULT GetPrivateData(
+            REFGUID Name,
+            UINT    *pDataSize,
+            void    *pData) final {
+      return m_privateData.getData(
+        Name, pDataSize, pData);
+    }
+    
+    HRESULT SetPrivateData(
+            REFGUID Name,
+            UINT    DataSize,
+      const void    *pData) final {
+      return m_privateData.setData(
+        Name, DataSize, pData);
+    }
+    
+    HRESULT SetPrivateDataInterface(
+            REFGUID  Name,
+      const IUnknown *pUnknown) final {
+      return m_privateData.setInterface(
+        Name, pUnknown);
+    }
+    
+  private:
+    
+    DxgiPrivateData m_privateData;
+    
+  };
+  
+}
diff --git a/src/dxgi/dxgi_output.cpp b/src/dxgi/dxgi_output.cpp
new file mode 100644
index 000000000..4f0273728
--- /dev/null
+++ b/src/dxgi/dxgi_output.cpp
@@ -0,0 +1,244 @@
+#include <cstdlib>
+#include <cstring>
+
+#include <sstream>
+#include <string>
+
+#include "dxgi_adapter.h"
+#include "dxgi_output.h"
+
+namespace dxvk {
+  
+  DxgiOutput::DxgiOutput(
+              DxgiAdapter*  adapter,
+              UINT          display)
+  : m_adapter (adapter),
+    m_display (display) {
+    TRACE(this, adapter);
+  }
+  
+  
+  DxgiOutput::~DxgiOutput() {
+    TRACE(this);
+  }
+  
+  
+  HRESULT DxgiOutput::QueryInterface(
+          REFIID riid,
+          void **ppvObject) {
+    COM_QUERY_IFACE(riid, ppvObject, IDXGIOutput);
+    
+    Logger::warn("DxgiOutput::QueryInterface: Unknown interface query");
+    return E_NOINTERFACE;
+  }
+  
+  
+  HRESULT DxgiOutput::GetParent(
+          REFIID riid,
+          void   **ppParent) {
+    return m_adapter->QueryInterface(riid, ppParent);
+  }
+  
+  
+  HRESULT DxgiOutput::FindClosestMatchingMode(
+    const DXGI_MODE_DESC *pModeToMatch,
+          DXGI_MODE_DESC *pClosestMatch,
+          IUnknown       *pConcernedDevice) {
+    Logger::err("DxgiOutput::FindClosestMatchingMode: Not implemented");
+    return E_NOTIMPL;
+  }
+  
+  
+  HRESULT DxgiOutput::GetDesc(DXGI_OUTPUT_DESC *pDesc) {
+    if (pDesc == nullptr)
+      return DXGI_ERROR_INVALID_CALL;
+    
+    // Display name, Windows requires wide chars
+    const char* displayName = SDL_GetDisplayName(m_display);
+    
+    if (displayName == nullptr) {
+      Logger::err("DxgiOutput::GetDesc: Failed to get display name");
+      return DXGI_ERROR_DRIVER_INTERNAL_ERROR;
+    }
+    
+    std::memset(pDesc->DeviceName, 0, sizeof(pDesc->DeviceName));
+    std::mbstowcs(pDesc->DeviceName, displayName, _countof(pDesc->DeviceName) - 1);
+    
+    // Current desktop rect of the display
+    SDL_Rect rect;
+    
+    if (SDL_GetDisplayBounds(m_display, &rect)) {
+      Logger::err("DxgiOutput::GetDesc: Failed to get display bounds");
+      return DXGI_ERROR_DRIVER_INTERNAL_ERROR;
+    }
+    
+    pDesc->DesktopCoordinates.left    = rect.x;
+    pDesc->DesktopCoordinates.top     = rect.y;
+    pDesc->DesktopCoordinates.right   = rect.x + rect.w;
+    pDesc->DesktopCoordinates.bottom  = rect.y + rect.h;
+    
+    // We don't have any info for these
+    pDesc->AttachedToDesktop  = 1;
+    pDesc->Rotation           = DXGI_MODE_ROTATION_UNSPECIFIED;
+    pDesc->Monitor            = nullptr;
+    return S_OK;
+  }
+  
+  
+  HRESULT DxgiOutput::GetDisplayModeList(
+          DXGI_FORMAT    EnumFormat,
+          UINT           Flags,
+          UINT           *pNumModes,
+          DXGI_MODE_DESC *pDesc) {
+    TRACE(this, EnumFormat, Flags, pNumModes, pDesc);
+    
+    if (pNumModes == nullptr)
+      return DXGI_ERROR_INVALID_CALL;
+      
+    // In order to check whether a display mode is 'centered' or
+    // 'streched' in DXGI terms, we compare its size to the desktop
+    // 'mode. If they are the same, we consider the mode to be
+    // 'centered', which most games will prefer over 'streched'. 
+    SDL_DisplayMode desktopMode;
+    
+    if (SDL_GetDesktopDisplayMode(m_display, &desktopMode)) {
+      Logger::err("DxgiOutput::GetDisplayModeList: Failed to list display modes");
+      return DXGI_ERROR_DRIVER_INTERNAL_ERROR;
+    }
+    
+    // Create a list of suitable display modes. Because of the way DXGI
+    // swapchains are handled by DXVK, we can ignore the format constraints
+    // here and just pick whatever modes SDL returns for the current display.
+    std::vector<DXGI_MODE_DESC> modes;
+    
+    int numDisplayModes = SDL_GetNumDisplayModes(m_display);
+    
+    if (numDisplayModes < 0) {
+      Logger::err("DxgiOutput::GetDisplayModeList: Failed to list display modes");
+      return DXGI_ERROR_DRIVER_INTERNAL_ERROR;
+    }
+    
+    for (int i = 0; i < numDisplayModes; i++) {
+      SDL_DisplayMode currMode;
+      
+      if (SDL_GetDisplayMode(m_display, i, &currMode)) {
+        Logger::err("DxgiOutput::GetDisplayModeList: Failed to list display modes");
+        return DXGI_ERROR_DRIVER_INTERNAL_ERROR;
+      }
+      
+      // We don't want duplicates, so we'll filter out modes
+      // with matching resolution and refresh rate.
+      bool hasMode = false;
+      
+      for (int j = 0; j < i && !hasMode; j++) {
+        SDL_DisplayMode testMode;
+        
+        if (SDL_GetDisplayMode(m_display, j, &testMode)) {
+          Logger::err("DxgiOutput::GetDisplayModeList: Failed to list display modes");
+          return DXGI_ERROR_DRIVER_INTERNAL_ERROR;
+        }
+        
+        hasMode = testMode.w            == currMode.w
+                && testMode.h            == currMode.h
+                && testMode.refresh_rate == currMode.refresh_rate;
+      }
+      
+      // Convert the SDL display mode to a DXGI display mode info
+      // structure and filter out any unwanted modes based on the
+      // supplied flags.
+      if (!hasMode) {
+        bool isNativeMode = (currMode.w == desktopMode.w)
+                          && (currMode.h == desktopMode.h);
+        
+        if (isNativeMode || (Flags & DXGI_ENUM_MODES_SCALING)) {
+          DXGI_MODE_DESC mode;
+          mode.Width                      = currMode.w;
+          mode.Height                     = currMode.h;
+          mode.RefreshRate.Numerator      = currMode.refresh_rate;
+          mode.RefreshRate.Denominator    = 1;
+          mode.Format                     = EnumFormat;
+          mode.ScanlineOrdering           = DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE;
+          mode.Scaling                    = isNativeMode
+            ? DXGI_MODE_SCALING_CENTERED
+            : DXGI_MODE_SCALING_STRETCHED;
+          modes.push_back(mode);
+        }
+      }
+    }
+    
+    // Copy list of display modes to the application-provided
+    // destination buffer. The buffer may not be appropriately
+    // sized by the time this is called.
+    if (pDesc != nullptr) {
+      for (uint32_t i = 0; i < modes.size() && i < *pNumModes; i++)
+        pDesc[i] = modes.at(i);
+    }
+    
+    // If the buffer is too small, we shall ask the application
+    // to query the display mode list again by returning the
+    // appropriate DXGI error code.
+    if ((pDesc == nullptr) || (modes.size() <= *pNumModes)) {
+      *pNumModes = modes.size();
+      return S_OK;
+    } else {
+      return DXGI_ERROR_MORE_DATA;
+    }
+      
+  }
+  
+  
+  HRESULT DxgiOutput::GetDisplaySurfaceData(IDXGISurface *pDestination) {
+    Logger::err("DxgiOutput::GetDisplaySurfaceData: Not implemented");
+    return E_NOTIMPL;
+  }
+  
+  
+  HRESULT DxgiOutput::GetFrameStatistics(DXGI_FRAME_STATISTICS *pStats) {
+    Logger::err("DxgiOutput::GetFrameStatistics: Not implemented");
+    return E_NOTIMPL;
+  }
+  
+  
+  HRESULT DxgiOutput::GetGammaControl(DXGI_GAMMA_CONTROL *pArray) {
+    Logger::err("DxgiOutput::GetGammaControl: Not implemented");
+    return E_NOTIMPL;
+  }
+  
+  
+  HRESULT DxgiOutput::GetGammaControlCapabilities(DXGI_GAMMA_CONTROL_CAPABILITIES *pGammaCaps) {
+    Logger::err("DxgiOutput::GetGammaControlCapabilities: Not implemented");
+    return E_NOTIMPL;
+  }
+  
+  
+  void DxgiOutput::ReleaseOwnership() {
+    Logger::warn("DxgiOutput::ReleaseOwnership: Stub");
+  }
+  
+  
+  HRESULT DxgiOutput::SetDisplaySurface(IDXGISurface *pScanoutSurface) {
+    Logger::err("DxgiOutput::SetDisplaySurface: Not implemented");
+    return E_NOTIMPL;
+  }
+  
+  
+  HRESULT DxgiOutput::SetGammaControl(const DXGI_GAMMA_CONTROL *pArray) {
+    Logger::err("DxgiOutput::SetGammaControl: Not implemented");
+    return E_NOTIMPL;
+  }
+  
+  
+  HRESULT DxgiOutput::TakeOwnership(
+          IUnknown *pDevice,
+          BOOL     Exclusive) {
+    Logger::warn("DxgiOutput::TakeOwnership: Stub");
+    return S_OK;
+  }
+  
+  
+  HRESULT DxgiOutput::WaitForVBlank() {
+    Logger::warn("DxgiOutput::WaitForVBlank: Stub");
+    return S_OK;
+  }
+  
+}
diff --git a/src/dxgi/dxgi_output.h b/src/dxgi/dxgi_output.h
new file mode 100644
index 000000000..0abf323a4
--- /dev/null
+++ b/src/dxgi/dxgi_output.h
@@ -0,0 +1,74 @@
+#pragma once
+
+#include "dxgi_object.h"
+
+namespace dxvk {
+  
+  class DxgiAdapter;
+  
+  class DxgiOutput : public DxgiObject<IDXGIOutput> {
+    
+  public:
+    
+    DxgiOutput(
+            DxgiAdapter*  adapter,
+            UINT          display);
+    
+    ~DxgiOutput();
+    
+    HRESULT QueryInterface(
+            REFIID riid,
+            void **ppvObject) final;
+    
+    HRESULT GetParent(
+            REFIID riid,
+            void   **ppParent) final;
+    
+    HRESULT FindClosestMatchingMode(
+      const DXGI_MODE_DESC *pModeToMatch,
+            DXGI_MODE_DESC *pClosestMatch,
+            IUnknown       *pConcernedDevice) final;
+    
+    HRESULT GetDesc(
+            DXGI_OUTPUT_DESC *pDesc) final;
+    
+    HRESULT GetDisplayModeList(
+            DXGI_FORMAT    EnumFormat,
+            UINT           Flags,
+            UINT           *pNumModes,
+            DXGI_MODE_DESC *pDesc) final;
+    
+    HRESULT GetDisplaySurfaceData(
+            IDXGISurface *pDestination) final;
+    
+    HRESULT GetFrameStatistics(
+            DXGI_FRAME_STATISTICS *pStats) final;
+    
+    HRESULT GetGammaControl(
+            DXGI_GAMMA_CONTROL *pArray) final;    
+    
+    HRESULT GetGammaControlCapabilities(
+            DXGI_GAMMA_CONTROL_CAPABILITIES *pGammaCaps) final;
+    
+    void ReleaseOwnership() final;
+    
+    HRESULT SetDisplaySurface(
+            IDXGISurface *pScanoutSurface) final;
+    
+    HRESULT SetGammaControl(
+      const DXGI_GAMMA_CONTROL *pArray) final;
+    
+    HRESULT TakeOwnership(
+            IUnknown *pDevice,
+            BOOL     Exclusive) final;
+    
+    HRESULT WaitForVBlank() final;
+    
+  private:
+    
+    DxgiAdapter*  m_adapter;
+    UINT          m_display;
+    
+  };
+
+}
diff --git a/src/dxgi/dxgi_private_data.cpp b/src/dxgi/dxgi_private_data.cpp
new file mode 100644
index 000000000..b900a616b
--- /dev/null
+++ b/src/dxgi/dxgi_private_data.cpp
@@ -0,0 +1,149 @@
+#include <cmath>
+#include <cstring>
+#include <cstdlib>
+
+#include "dxgi_private_data.h"
+
+namespace dxvk {
+  
+  DxgiPrivateDataEntry::DxgiPrivateDataEntry() { }
+  DxgiPrivateDataEntry::DxgiPrivateDataEntry(
+          REFGUID   guid,
+          UINT      size,
+    const void*     data)
+  : m_guid(guid),
+    m_size(size),
+    m_data(std::malloc(size)) {
+    std::memcpy(m_data, data, size);
+  }
+  
+  
+  DxgiPrivateDataEntry::DxgiPrivateDataEntry(
+          REFGUID   guid,
+    const IUnknown* iface)
+  : m_guid  (guid),
+    m_iface (const_cast<IUnknown*>(iface)) {
+    m_iface->AddRef();
+  }
+  
+  
+  DxgiPrivateDataEntry::~DxgiPrivateDataEntry() {
+    this->destroy();
+  }
+  
+  
+  DxgiPrivateDataEntry::DxgiPrivateDataEntry(DxgiPrivateDataEntry&& other)
+  : m_guid  (other.m_guid),
+    m_size  (other.m_size),
+    m_data  (other.m_data),
+    m_iface (other.m_iface) {
+    other.m_guid  = __uuidof(IUnknown);
+    other.m_size  = 0;
+    other.m_data  = nullptr;
+    other.m_iface = nullptr;
+  }
+  
+  
+  DxgiPrivateDataEntry& DxgiPrivateDataEntry::operator = (DxgiPrivateDataEntry&& other) {
+    this->destroy();
+    this->m_guid  = other.m_guid;
+    this->m_size  = other.m_size;
+    this->m_data  = other.m_data;
+    this->m_iface = other.m_iface;
+    
+    other.m_guid  = __uuidof(IUnknown);
+    other.m_size  = 0;
+    other.m_data  = nullptr;
+    other.m_iface = nullptr;
+    return *this;
+  }
+  
+  
+  HRESULT DxgiPrivateDataEntry::get(UINT& size, void* data) const {
+    if (size != 0 && data == nullptr)
+      return DXGI_ERROR_INVALID_CALL;
+    
+    const UINT minSize = m_iface != nullptr
+      ? sizeof(IUnknown*)
+      : m_size;
+    
+    const HRESULT result = size < minSize
+      ? DXGI_ERROR_MORE_DATA
+      : S_OK;
+    
+    if (size >= minSize) {
+      if (m_iface != nullptr) {
+        m_iface->AddRef();
+        std::memcpy(data, &m_iface, minSize);
+      } else {
+        std::memcpy(data, m_data, minSize);
+      }
+    }
+    
+    size = minSize;
+    return result;
+  }
+  
+  
+  void DxgiPrivateDataEntry::destroy() {
+    if (m_data != nullptr)
+      std::free(m_data);
+    if (m_iface != nullptr)
+      m_iface->Release();
+  }
+  
+  
+  HRESULT DxgiPrivateData::setData(
+          REFGUID   guid,
+          UINT      size,
+    const void*     data) {
+    this->insertEntry(DxgiPrivateDataEntry(guid, size, data));
+    return S_OK;
+  }
+  
+  
+  HRESULT DxgiPrivateData::setInterface(
+          REFGUID   guid,
+    const IUnknown* iface) {
+    this->insertEntry(DxgiPrivateDataEntry(guid, iface));
+    return S_OK;
+  }
+  
+  
+  HRESULT DxgiPrivateData::getData(
+          REFGUID   guid,
+          UINT*     size,
+          void*     data) {
+    if (size == nullptr)
+      return DXGI_ERROR_INVALID_CALL;
+    
+    auto entry = this->findEntry(guid);
+    
+    if (entry == nullptr)
+      return DXGI_ERROR_NOT_FOUND;
+    
+    return entry->get(*size, data);
+  }
+  
+  
+  DxgiPrivateDataEntry* DxgiPrivateData::findEntry(REFGUID guid) {
+    for (DxgiPrivateDataEntry& e : m_entries) {
+      if (e.hasGuid(guid))
+        return &e;
+    }
+    
+    return nullptr;
+  }
+  
+  
+  void DxgiPrivateData::insertEntry(DxgiPrivateDataEntry&& entry) {
+    DxgiPrivateDataEntry  srcEntry = std::move(entry);
+    DxgiPrivateDataEntry* dstEntry = this->findEntry(srcEntry.guid());
+    
+    if (dstEntry != nullptr)
+      *dstEntry = std::move(srcEntry);
+    else
+      m_entries.push_back(std::move(srcEntry));
+  }
+  
+}
diff --git a/src/dxgi/dxgi_private_data.h b/src/dxgi/dxgi_private_data.h
new file mode 100644
index 000000000..26bab34de
--- /dev/null
+++ b/src/dxgi/dxgi_private_data.h
@@ -0,0 +1,105 @@
+#pragma once
+
+#include <vector>
+
+#include "dxgi_include.h"
+
+namespace dxvk {
+  
+  /**
+   * \brief Data entry for private storage
+   * Stores a single private storage item.
+   */
+  class DxgiPrivateDataEntry {
+    
+  public:
+    
+    DxgiPrivateDataEntry();
+    DxgiPrivateDataEntry(
+            REFGUID   guid,
+            UINT      size,
+      const void*     data);
+    DxgiPrivateDataEntry(
+            REFGUID   guid,
+      const IUnknown* iface);
+    ~DxgiPrivateDataEntry();
+    
+    DxgiPrivateDataEntry             (DxgiPrivateDataEntry&& other);
+    DxgiPrivateDataEntry& operator = (DxgiPrivateDataEntry&& other);
+    
+    /**
+     * \brief The entry's GUID
+     * \returns The GUID
+     */
+    REFGUID guid() const {
+      return m_guid;
+    }
+    
+    /**
+     * \brief Checks whether the GUID matches another one
+     * 
+     * GUIDs are used to identify private data entries.
+     * \param [in] guid The GUID to compare to
+     * \returns \c true if this entry holds the same GUID
+     */
+    bool hasGuid(REFGUID guid) const {
+      return m_guid == guid;
+    }
+    
+    /**
+     * \brief Retrieves stored data
+     * 
+     * \param [in,out] size Destination buffer size
+     * \param [in] data Appliaction-provided buffer
+     * \returns \c S_OK on success, or \c DXGI_ERROR_MORE_DATA
+     *          if the destination buffer is too small
+     */
+    HRESULT get(UINT& size, void* data) const;
+    
+  private:
+    
+    GUID      m_guid  = __uuidof(IUnknown);
+    UINT      m_size  = 0;
+    void*     m_data  = nullptr;
+    IUnknown* m_iface = nullptr;
+    
+    void destroy();
+    
+  };
+  
+  
+  /**
+   * \brief Private storage for DXGI objects
+   * 
+   * Provides storage for application-defined
+   * byte arrays or COM interfaces that can be
+   * retrieved using GUIDs.
+   */
+  class DxgiPrivateData {
+    
+  public:
+    
+    HRESULT setData(
+            REFGUID   guid,
+            UINT      size,
+      const void*     data);
+    
+    HRESULT setInterface(
+            REFGUID   guid,
+      const IUnknown* iface);
+    
+    HRESULT getData(
+            REFGUID   guid,
+            UINT*     size,
+            void*     data);
+    
+  private:
+    
+    std::vector<DxgiPrivateDataEntry> m_entries;
+    
+    DxgiPrivateDataEntry* findEntry(REFGUID guid);
+    void insertEntry(DxgiPrivateDataEntry&& entry);
+    
+  };
+  
+}
diff --git a/src/dxgi/dxgi_swapchain.cpp b/src/dxgi/dxgi_swapchain.cpp
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/dxgi/dxgi_swapchain.h b/src/dxgi/dxgi_swapchain.h
new file mode 100644
index 000000000..e69de29bb
diff --git a/src/dxgi/meson.build b/src/dxgi/meson.build
new file mode 100644
index 000000000..57c101ecc
--- /dev/null
+++ b/src/dxgi/meson.build
@@ -0,0 +1,17 @@
+dxgi_src = [
+  'dxgi_adapter.cpp',
+  'dxgi_factory.cpp',
+  'dxgi_main.cpp',
+  'dxgi_output.cpp',
+  'dxgi_private_data.cpp',
+  'dxgi_swapchain.cpp',
+]
+
+dxgi_dll = shared_library('dxgi', dxgi_src,
+  link_with           : [ util_lib ],
+  dependencies        : [ dxvk_dep ],
+  include_directories : dxvk_include_path)
+
+dxgi_dep = declare_dependency(
+  link_with           : [ dxgi_dll ],
+  include_directories : [ dxvk_include_path, include_directories('.') ])
diff --git a/src/meson.build b/src/meson.build
index 185ea80b7..5938df467 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1,2 +1,4 @@
 subdir('util')
-subdir('dxvk')
\ No newline at end of file
+subdir('dxvk')
+subdir('dxgi')
+subdir('d3d11')
\ No newline at end of file