#include #include "dxgi_factory.h" #include "dxgi_surface.h" #include "dxgi_swapchain.h" #include "dxgi_swapchain_dispatcher.h" #include "../util/util_singleton.h" namespace dxvk { Singleton g_dxvkInstance; std::mutex s_globalHDRStateMutex; DXVK_VK_GLOBAL_HDR_STATE s_globalHDRState{}; DxgiVkFactory::DxgiVkFactory(DxgiFactory* pFactory) : m_factory(pFactory) { } ULONG STDMETHODCALLTYPE DxgiVkFactory::AddRef() { return m_factory->AddRef(); } ULONG STDMETHODCALLTYPE DxgiVkFactory::Release() { return m_factory->Release(); } HRESULT STDMETHODCALLTYPE DxgiVkFactory::QueryInterface( REFIID riid, void** ppvObject) { return m_factory->QueryInterface(riid, ppvObject); } void STDMETHODCALLTYPE DxgiVkFactory::GetVulkanInstance( VkInstance* pInstance, PFN_vkGetInstanceProcAddr* ppfnVkGetInstanceProcAddr) { auto instance = m_factory->GetDXVKInstance(); if (pInstance) *pInstance = instance->handle(); if (ppfnVkGetInstanceProcAddr) *ppfnVkGetInstanceProcAddr = instance->vki()->getLoaderProc(); } HRESULT STDMETHODCALLTYPE DxgiVkFactory::GetGlobalHDRState( DXGI_COLOR_SPACE_TYPE *pOutColorSpace, DXGI_HDR_METADATA_HDR10 *pOutMetadata) { std::unique_lock lock(s_globalHDRStateMutex); if (!s_globalHDRState.Serial) return S_FALSE; *pOutColorSpace = s_globalHDRState.ColorSpace; *pOutMetadata = s_globalHDRState.Metadata.HDR10; return S_OK; } HRESULT STDMETHODCALLTYPE DxgiVkFactory::SetGlobalHDRState( DXGI_COLOR_SPACE_TYPE ColorSpace, const DXGI_HDR_METADATA_HDR10 *pMetadata) { std::unique_lock lock(s_globalHDRStateMutex); static uint32_t s_GlobalHDRStateSerial = 0; s_globalHDRState.Serial = ++s_GlobalHDRStateSerial; s_globalHDRState.ColorSpace = ColorSpace; s_globalHDRState.Metadata.Type = DXGI_HDR_METADATA_TYPE_HDR10; s_globalHDRState.Metadata.HDR10 = *pMetadata; return S_OK; } DxgiFactory::DxgiFactory(UINT Flags) : m_instance (g_dxvkInstance.acquire(0)), m_interop (this), m_options (m_instance->config()), m_monitorInfo (this, m_options), m_flags (Flags), m_monitorFallback (false) { // Be robust against situations where some monitors are not // associated with any adapter. This can happen if device // filter options are used. std::vector monitors; for (uint32_t i = 0; ; i++) { HMONITOR hmon = wsi::enumMonitors(i); if (!hmon) break; monitors.push_back(hmon); } for (uint32_t i = 0; m_instance->enumAdapters(i) != nullptr; i++) { auto adapter = m_instance->enumAdapters(i); adapter->logAdapterInfo(); // Remove all monitors that are associated // with the current adapter from the list. const auto& vk11 = adapter->devicePropertiesExt().vk11; if (vk11.deviceLUIDValid) { auto luid = reinterpret_cast(&vk11.deviceLUID); for (uint32_t j = 0; ; j++) { HMONITOR hmon = wsi::enumMonitors(&luid, 1, j); if (!hmon) break; auto entry = std::find(monitors.begin(), monitors.end(), hmon); if (entry != monitors.end()) monitors.erase(entry); } } } // If any monitors are left on the list, enable the // fallback to always enumerate all monitors. if ((m_monitorFallback = !monitors.empty())) Logger::warn("DXGI: Found monitors not associated with any adapter, using fallback"); } DxgiFactory::~DxgiFactory() { g_dxvkInstance.release(); } HRESULT STDMETHODCALLTYPE DxgiFactory::QueryInterface(REFIID riid, void** ppvObject) { if (ppvObject == nullptr) return E_POINTER; *ppvObject = nullptr; if (riid == __uuidof(IUnknown) || riid == __uuidof(IDXGIObject) || riid == __uuidof(IDXGIFactory) || riid == __uuidof(IDXGIFactory1) || riid == __uuidof(IDXGIFactory2) || riid == __uuidof(IDXGIFactory3) || riid == __uuidof(IDXGIFactory4) || riid == __uuidof(IDXGIFactory5) || riid == __uuidof(IDXGIFactory6) || riid == __uuidof(IDXGIFactory7)) { *ppvObject = ref(this); return S_OK; } if (riid == __uuidof(IDXGIVkInteropFactory) || riid == __uuidof(IDXGIVkInteropFactory1)) { *ppvObject = ref(&m_interop); return S_OK; } if (riid == __uuidof(IDXGIVkMonitorInfo)) { *ppvObject = ref(&m_monitorInfo); return S_OK; } if (logQueryInterfaceError(__uuidof(IDXGIFactory), riid)) { Logger::warn("DxgiFactory::QueryInterface: Unknown interface query"); Logger::warn(str::format(riid)); } return E_NOINTERFACE; } HRESULT STDMETHODCALLTYPE DxgiFactory::GetParent(REFIID riid, void** ppParent) { InitReturnPtr(ppParent); Logger::warn("DxgiFactory::GetParent: Unknown interface query"); return E_NOINTERFACE; } BOOL STDMETHODCALLTYPE DxgiFactory::IsWindowedStereoEnabled() { // We don't support Stereo 3D at the moment return FALSE; } HRESULT STDMETHODCALLTYPE DxgiFactory::CreateSoftwareAdapter( HMODULE Module, IDXGIAdapter** ppAdapter) { InitReturnPtr(ppAdapter); if (ppAdapter == nullptr) return DXGI_ERROR_INVALID_CALL; Logger::err("DXGI: CreateSoftwareAdapter: Software adapters not supported"); return DXGI_ERROR_UNSUPPORTED; } HRESULT STDMETHODCALLTYPE DxgiFactory::CreateSwapChain( IUnknown* pDevice, DXGI_SWAP_CHAIN_DESC* pDesc, IDXGISwapChain** ppSwapChain) { if (ppSwapChain == nullptr || pDesc == nullptr || pDevice == nullptr) return DXGI_ERROR_INVALID_CALL; DXGI_SWAP_CHAIN_DESC1 desc; desc.Width = pDesc->BufferDesc.Width; desc.Height = pDesc->BufferDesc.Height; desc.Format = pDesc->BufferDesc.Format; desc.Stereo = FALSE; desc.SampleDesc = pDesc->SampleDesc; desc.BufferUsage = pDesc->BufferUsage; desc.BufferCount = pDesc->BufferCount; desc.Scaling = DXGI_SCALING_STRETCH; desc.SwapEffect = pDesc->SwapEffect; desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; desc.Flags = pDesc->Flags; DXGI_SWAP_CHAIN_FULLSCREEN_DESC descFs; descFs.RefreshRate = pDesc->BufferDesc.RefreshRate; descFs.ScanlineOrdering = pDesc->BufferDesc.ScanlineOrdering; descFs.Scaling = pDesc->BufferDesc.Scaling; descFs.Windowed = pDesc->Windowed; IDXGISwapChain1* swapChain = nullptr; HRESULT hr = CreateSwapChainForHwnd( pDevice, pDesc->OutputWindow, &desc, &descFs, nullptr, &swapChain); *ppSwapChain = swapChain; return hr; } HRESULT STDMETHODCALLTYPE DxgiFactory::CreateSwapChainForHwnd( IUnknown* pDevice, HWND hWnd, const DXGI_SWAP_CHAIN_DESC1* pDesc, const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc, IDXGIOutput* pRestrictToOutput, IDXGISwapChain1** ppSwapChain) { InitReturnPtr(ppSwapChain); if (!ppSwapChain || !pDesc || !hWnd || !pDevice) return DXGI_ERROR_INVALID_CALL; // Make sure the back buffer size is not zero DXGI_SWAP_CHAIN_DESC1 desc = *pDesc; wsi::getWindowSize(hWnd, desc.Width ? nullptr : &desc.Width, desc.Height ? nullptr : &desc.Height); // If necessary, set up a default set of // fullscreen parameters for the swap chain DXGI_SWAP_CHAIN_FULLSCREEN_DESC fsDesc; if (pFullscreenDesc) { fsDesc = *pFullscreenDesc; } else { fsDesc.RefreshRate = { 0, 0 }; fsDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; fsDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; fsDesc.Windowed = TRUE; } // Probe various modes to create the swap chain object Com frontendSwapChain; Com dxvkFactory; if (SUCCEEDED(pDevice->QueryInterface(IID_PPV_ARGS(&dxvkFactory)))) { Com surfaceFactory = new DxgiSurfaceFactory( m_instance->vki()->getLoaderProc(), hWnd); Com presenter; HRESULT hr = dxvkFactory->CreateSwapChain(surfaceFactory.ptr(), &desc, &presenter); if (FAILED(hr)) { Logger::err(str::format("DXGI: CreateSwapChainForHwnd: Failed to create swap chain, hr ", hr)); return hr; } frontendSwapChain = new DxgiSwapChain(this, presenter.ptr(), hWnd, &desc, &fsDesc); } else { Logger::err("DXGI: CreateSwapChainForHwnd: Unsupported device type"); return DXGI_ERROR_UNSUPPORTED; } // Wrap object in swap chain dispatcher *ppSwapChain = new DxgiSwapChainDispatcher(frontendSwapChain.ref(), pDevice); return S_OK; } HRESULT STDMETHODCALLTYPE DxgiFactory::CreateSwapChainForCoreWindow( IUnknown* pDevice, IUnknown* pWindow, const DXGI_SWAP_CHAIN_DESC1* pDesc, IDXGIOutput* pRestrictToOutput, IDXGISwapChain1** ppSwapChain) { InitReturnPtr(ppSwapChain); Logger::err("DxgiFactory::CreateSwapChainForCoreWindow: Not implemented"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE DxgiFactory::CreateSwapChainForComposition( IUnknown* pDevice, const DXGI_SWAP_CHAIN_DESC1* pDesc, IDXGIOutput* pRestrictToOutput, IDXGISwapChain1** ppSwapChain) { InitReturnPtr(ppSwapChain); Logger::err("DxgiFactory::CreateSwapChainForComposition: Not implemented"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE DxgiFactory::EnumAdapters( UINT Adapter, IDXGIAdapter** ppAdapter) { InitReturnPtr(ppAdapter); if (ppAdapter == nullptr) return DXGI_ERROR_INVALID_CALL; IDXGIAdapter1* handle = nullptr; HRESULT hr = this->EnumAdapters1(Adapter, &handle); *ppAdapter = handle; return hr; } HRESULT STDMETHODCALLTYPE DxgiFactory::EnumAdapters1( UINT Adapter, IDXGIAdapter1** ppAdapter) { InitReturnPtr(ppAdapter); if (ppAdapter == nullptr) return DXGI_ERROR_INVALID_CALL; Rc dxvkAdapter = m_instance->enumAdapters(Adapter); if (dxvkAdapter == nullptr) return DXGI_ERROR_NOT_FOUND; *ppAdapter = ref(new DxgiAdapter(this, dxvkAdapter, Adapter)); return S_OK; } HRESULT STDMETHODCALLTYPE DxgiFactory::EnumAdapterByLuid( LUID AdapterLuid, REFIID riid, void** ppvAdapter) { InitReturnPtr(ppvAdapter); uint32_t adapterId = 0; while (true) { Com adapter; HRESULT hr = EnumAdapters(adapterId++, &adapter); if (FAILED(hr)) return hr; DXGI_ADAPTER_DESC desc; adapter->GetDesc(&desc); if (!std::memcmp(&AdapterLuid, &desc.AdapterLuid, sizeof(LUID))) return adapter->QueryInterface(riid, ppvAdapter); } // This should be unreachable return DXGI_ERROR_NOT_FOUND; } HRESULT STDMETHODCALLTYPE DxgiFactory::EnumAdapterByGpuPreference( UINT Adapter, DXGI_GPU_PREFERENCE GpuPreference, REFIID riid, void** ppvAdapter) { InitReturnPtr(ppvAdapter); uint32_t adapterCount = m_instance->adapterCount(); if (Adapter >= adapterCount) return DXGI_ERROR_NOT_FOUND; // We know that the backend lists dedicated GPUs before // any integrated ones, so just list adapters in reverse // order. We have no other way to estimate performance. if (GpuPreference == DXGI_GPU_PREFERENCE_MINIMUM_POWER) Adapter = adapterCount - Adapter - 1; Com adapter; HRESULT hr = this->EnumAdapters(Adapter, &adapter); if (FAILED(hr)) return hr; return adapter->QueryInterface(riid, ppvAdapter); } HRESULT STDMETHODCALLTYPE DxgiFactory::EnumWarpAdapter( REFIID riid, void** ppvAdapter) { InitReturnPtr(ppvAdapter); static bool s_errorShown = false; if (!std::exchange(s_errorShown, true)) Logger::warn("DxgiFactory::EnumWarpAdapter: WARP not supported, returning first hardware adapter"); Com adapter; HRESULT hr = EnumAdapters1(0, &adapter); if (FAILED(hr)) return hr; return adapter->QueryInterface(riid, ppvAdapter); } HRESULT STDMETHODCALLTYPE DxgiFactory::GetWindowAssociation(HWND *pWindowHandle) { if (pWindowHandle == nullptr) return DXGI_ERROR_INVALID_CALL; // Wine tests show that this is always null for whatever reason *pWindowHandle = nullptr; return S_OK; } HRESULT STDMETHODCALLTYPE DxgiFactory::GetSharedResourceAdapterLuid( HANDLE hResource, LUID* pLuid) { Logger::err("DxgiFactory::GetSharedResourceAdapterLuid: Not implemented"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE DxgiFactory::MakeWindowAssociation(HWND WindowHandle, UINT Flags) { Logger::warn("DXGI: MakeWindowAssociation: Ignoring flags"); return S_OK; } BOOL STDMETHODCALLTYPE DxgiFactory::IsCurrent() { return TRUE; } HRESULT STDMETHODCALLTYPE DxgiFactory::RegisterOcclusionStatusWindow( HWND WindowHandle, UINT wMsg, DWORD* pdwCookie) { Logger::err("DxgiFactory::RegisterOcclusionStatusWindow: Not implemented"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE DxgiFactory::RegisterStereoStatusEvent( HANDLE hEvent, DWORD* pdwCookie) { Logger::err("DxgiFactory::RegisterStereoStatusEvent: Not implemented"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE DxgiFactory::RegisterStereoStatusWindow( HWND WindowHandle, UINT wMsg, DWORD* pdwCookie) { Logger::err("DxgiFactory::RegisterStereoStatusWindow: Not implemented"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE DxgiFactory::RegisterOcclusionStatusEvent( HANDLE hEvent, DWORD* pdwCookie) { Logger::err("DxgiFactory::RegisterOcclusionStatusEvent: Not implemented"); return E_NOTIMPL; } void STDMETHODCALLTYPE DxgiFactory::UnregisterStereoStatus( DWORD dwCookie) { Logger::err("DxgiFactory::UnregisterStereoStatus: Not implemented"); } void STDMETHODCALLTYPE DxgiFactory::UnregisterOcclusionStatus( DWORD dwCookie) { Logger::err("DxgiFactory::UnregisterOcclusionStatus: Not implemented"); } UINT STDMETHODCALLTYPE DxgiFactory::GetCreationFlags() { return m_flags; } HRESULT STDMETHODCALLTYPE DxgiFactory::CheckFeatureSupport( DXGI_FEATURE Feature, void* pFeatureSupportData, UINT FeatureSupportDataSize) { switch (Feature) { case DXGI_FEATURE_PRESENT_ALLOW_TEARING: { auto info = static_cast(pFeatureSupportData); if (FeatureSupportDataSize != sizeof(*info)) return E_INVALIDARG; *info = TRUE; } return S_OK; default: Logger::err(str::format("DxgiFactory: CheckFeatureSupport: Unknown feature: ", uint32_t(Feature))); return E_INVALIDARG; } } HRESULT STDMETHODCALLTYPE DxgiFactory::RegisterAdaptersChangedEvent( HANDLE hEvent, DWORD* pdwCookie) { Logger::err("DxgiFactory: RegisterAdaptersChangedEvent: Stub"); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE DxgiFactory::UnregisterAdaptersChangedEvent( DWORD Cookie) { Logger::err("DxgiFactory: UnregisterAdaptersChangedEvent: Stub"); return E_NOTIMPL; } DXVK_VK_GLOBAL_HDR_STATE DxgiFactory::GlobalHDRState() { std::unique_lock lock(s_globalHDRStateMutex); return s_globalHDRState; } }