From 26e662e12cc9764899139b539abae661d5640e35 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sun, 26 Nov 2017 15:29:57 +0100 Subject: [PATCH] [dxgi] Implemented SDL-backed DxgiSwapChain methods --- src/dxgi/dxgi_swapchain.cpp | 166 ++++++++++++++++++++++++++++--- src/dxgi/dxgi_swapchain.h | 7 +- tests/dxgi/test_dxgi_factory.cpp | 4 +- 3 files changed, 159 insertions(+), 18 deletions(-) diff --git a/src/dxgi/dxgi_swapchain.cpp b/src/dxgi/dxgi_swapchain.cpp index fcf4d544..57e4b070 100644 --- a/src/dxgi/dxgi_swapchain.cpp +++ b/src/dxgi/dxgi_swapchain.cpp @@ -12,15 +12,40 @@ namespace dxvk { : m_factory (factory), m_desc (*pDesc) { + // Retrieve a device pointer that allows us to + // communicate with the underlying D3D device if (FAILED(pDevice->QueryInterface(__uuidof(IDXVKDevice), reinterpret_cast(&m_device)))) throw DxvkError("DxgiSwapChain::DxgiSwapChain: Invalid device"); + // Initialize frame statistics + m_stats.PresentCount = 0; + m_stats.PresentRefreshCount = 0; + m_stats.SyncRefreshCount = 0; + m_stats.SyncQPCTime.QuadPart = 0; + m_stats.SyncGPUTime.QuadPart = 0; + + // Create SDL window handle + m_window = SDL_CreateWindowFrom(m_desc.OutputWindow); + + if (m_window == nullptr) { + throw DxvkError(str::format( + "DxgiSwapChain::DxgiSwapChain: Failed to create window:\n", + SDL_GetError())); + } + + // Set initial window mode and fullscreen state + if (FAILED(this->ResizeTarget(&pDesc->BufferDesc))) + throw DxvkError("DxgiSwapChain::DxgiSwapChain: Failed to set initial display mode"); + + if (FAILED(this->SetFullscreenState(!pDesc->Windowed, nullptr))) + throw DxvkError("DxgiSwapChain::DxgiSwapChain: Failed to set initial fullscreen state"); } DxgiSwapChain::~DxgiSwapChain() { - + // We do not release the SDL window handle here since + // that would destroy the underlying window as well. } @@ -46,44 +71,87 @@ namespace dxvk { HRESULT DxgiSwapChain::GetBuffer(UINT Buffer, REFIID riid, void** ppSurface) { + std::lock_guard lock(m_mutex); + Logger::err("DxgiSwapChain::GetBuffer: Not implemented"); return E_NOTIMPL; } HRESULT DxgiSwapChain::GetContainingOutput(IDXGIOutput** ppOutput) { - Logger::err("DxgiSwapChain::GetContainingOutput: Not implemented"); - return E_NOTIMPL; + if (ppOutput != nullptr) + return DXGI_ERROR_INVALID_CALL; + + // We can use the display index returned by SDL to query the + // containing output, since DxgiAdapter::EnumOutputs uses the + // same output IDs. + std::lock_guard lock(m_mutex); + int32_t displayId = SDL_GetWindowDisplayIndex(m_window); + + if (displayId < 0) { + Logger::err("DxgiSwapChain::GetContainingOutput: Failed to query window display index"); + return DXGI_ERROR_DRIVER_INTERNAL_ERROR; + } + + Com adapter; + + if (FAILED(m_device->GetAdapter(&adapter))) + return DXGI_ERROR_DRIVER_INTERNAL_ERROR; + + return adapter->EnumOutputs(displayId, ppOutput); } HRESULT DxgiSwapChain::GetDesc(DXGI_SWAP_CHAIN_DESC* pDesc) { - Logger::err("DxgiSwapChain::GetDesc: Not implemented"); - return E_NOTIMPL; + if (pDesc == nullptr) + return DXGI_ERROR_INVALID_CALL; + + std::lock_guard lock(m_mutex); + *pDesc = m_desc; + return S_OK; } HRESULT DxgiSwapChain::GetFrameStatistics(DXGI_FRAME_STATISTICS* pStats) { - Logger::err("DxgiSwapChain::GetFrameStatistics: Not implemented"); - return E_NOTIMPL; + if (pStats == nullptr) + return DXGI_ERROR_INVALID_CALL; + + std::lock_guard lock(m_mutex); + *pStats = m_stats; + return S_OK; } HRESULT DxgiSwapChain::GetFullscreenState( BOOL* pFullscreen, IDXGIOutput** ppTarget) { - Logger::err("DxgiSwapChain::GetFullscreenState: Not implemented"); - return E_NOTIMPL; + std::lock_guard lock(m_mutex); + + HRESULT hr = S_OK; + + if (pFullscreen != nullptr) + *pFullscreen = !m_desc.Windowed; + + if ((ppTarget != nullptr) && !m_desc.Windowed) + hr = this->GetContainingOutput(ppTarget); + + return hr; } HRESULT DxgiSwapChain::GetLastPresentCount(UINT* pLastPresentCount) { - Logger::err("DxgiSwapChain::GetLastPresentCount: Not implemented"); - return E_NOTIMPL; + if (pLastPresentCount == nullptr) + return DXGI_ERROR_INVALID_CALL; + + std::lock_guard lock(m_mutex); + *pLastPresentCount = m_stats.PresentCount; + return S_OK; } HRESULT DxgiSwapChain::Present(UINT SyncInterval, UINT Flags) { + std::lock_guard lock(m_mutex); + Logger::err("DxgiSwapChain::Present: Not implemented"); return E_NOTIMPL; } @@ -95,22 +163,90 @@ namespace dxvk { UINT Height, DXGI_FORMAT NewFormat, UINT SwapChainFlags) { + std::lock_guard lock(m_mutex); + Logger::err("DxgiSwapChain::ResizeBuffers: Not implemented"); return E_NOTIMPL; } HRESULT DxgiSwapChain::ResizeTarget(const DXGI_MODE_DESC* pNewTargetParameters) { - Logger::err("DxgiSwapChain::ResizeTarget: Not implemented"); - return E_NOTIMPL; + if (pNewTargetParameters == nullptr) + return DXGI_ERROR_INVALID_CALL; + + std::lock_guard lock(m_mutex); + m_desc.BufferDesc = *pNewTargetParameters; + + // Applies to windowed mode + SDL_SetWindowSize(m_window, + pNewTargetParameters->Width, + pNewTargetParameters->Height); + + // Applies to fullscreen mode + SDL_DisplayMode displayMode; + displayMode.format = SDL_PIXELFORMAT_RGBA32; + displayMode.w = pNewTargetParameters->Width; + displayMode.h = pNewTargetParameters->Height; + displayMode.refresh_rate = pNewTargetParameters->RefreshRate.Numerator + / pNewTargetParameters->RefreshRate.Denominator; + displayMode.driverdata = nullptr; + + if (SDL_SetWindowDisplayMode(m_window, &displayMode)) { + throw DxvkError(str::format( + "DxgiSwapChain::ResizeTarget: Failed to set display mode:\n", + SDL_GetError())); + return DXGI_ERROR_DRIVER_INTERNAL_ERROR; + } + + return S_OK; } HRESULT DxgiSwapChain::SetFullscreenState( BOOL Fullscreen, IDXGIOutput* pTarget) { - Logger::err("DxgiSwapChain::SetFullscreenState: Not implemented"); - return E_NOTIMPL; + std::lock_guard lock(m_mutex); + + // Unconditionally reset the swap chain to windowed mode first. + // This required if the application wants to move the window to + // a different display while remaining in fullscreen mode. + if (SDL_SetWindowFullscreen(m_window, 0)) { + Logger::err(str::format( + "DxgiSwapChain::SetFullscreenState: Failed to set windowed mode:\n", + SDL_GetError())); + return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE; + } + + m_desc.Windowed = !Fullscreen; + + if (Fullscreen) { + // If a target output is specified, we need to move the + // window to that output first while in windowed mode. + if (pTarget != nullptr) { + DXGI_OUTPUT_DESC outputDesc; + + if (FAILED(pTarget->GetDesc(&outputDesc))) { + Logger::err("DxgiSwapChain::SetFullscreenState: Failed to query output properties"); + return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE; + } + + SDL_SetWindowPosition(m_window, + outputDesc.DesktopCoordinates.left, + outputDesc.DesktopCoordinates.top); + } + + // Now that the window is located at the target location, + // SDL should fullscreen it on the requested display. We + // only use borderless fullscreen for now, may be changed. + if (SDL_SetWindowFullscreen(m_window, SDL_WINDOW_FULLSCREEN_DESKTOP)) { + Logger::err(str::format( + "DxgiSwapChain::SetFullscreenState: Failed to set fullscreen mode:\n", + SDL_GetError())); + return DXGI_ERROR_NOT_CURRENTLY_AVAILABLE; + } + } + + return S_OK; } } diff --git a/src/dxgi/dxgi_swapchain.h b/src/dxgi/dxgi_swapchain.h index 132caf51..0e240865 100644 --- a/src/dxgi/dxgi_swapchain.h +++ b/src/dxgi/dxgi_swapchain.h @@ -73,10 +73,15 @@ namespace dxvk { private: + std::mutex m_mutex; + Com m_factory; Com m_device; - DXGI_SWAP_CHAIN_DESC m_desc; + DXGI_SWAP_CHAIN_DESC m_desc; + DXGI_FRAME_STATISTICS m_stats; + + SDL_Window* m_window = nullptr; }; diff --git a/tests/dxgi/test_dxgi_factory.cpp b/tests/dxgi/test_dxgi_factory.cpp index d9d96127..e5eaf0ea 100644 --- a/tests/dxgi/test_dxgi_factory.cpp +++ b/tests/dxgi/test_dxgi_factory.cpp @@ -11,7 +11,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { - Com factory = nullptr; + Com factory; if (CreateDXGIFactory(__uuidof(IDXGIFactory), reinterpret_cast(&factory)) != S_OK) { @@ -19,7 +19,7 @@ int WINAPI WinMain(HINSTANCE hInstance, return 1; } - Com adapter = nullptr; + Com adapter; for (UINT i = 0; factory->EnumAdapters(i, &adapter) == S_OK; i++) { DXGI_ADAPTER_DESC adapterDesc;