1
0
mirror of https://github.com/EduApps-CDG/OpenDX synced 2024-12-30 09:45:37 +01:00

[dxgi] Reimplemented parts of DxgiSwapchain with win32 APIs

This solves issues in applications that create more than one
swap chain for a given window, and helps applications that
don't like SDL interference in general. Fullscreen support
is currently missing entirely.
This commit is contained in:
Philip Rebohle 2017-12-31 00:23:34 +01:00
parent 588ceba1d1
commit 4412f2f5ff
5 changed files with 171 additions and 208 deletions

View File

@ -137,30 +137,23 @@ namespace dxvk {
} }
hasMode = testMode.w == currMode.w hasMode = testMode.w == currMode.w
&& testMode.h == currMode.h && testMode.h == currMode.h
&& testMode.refresh_rate == currMode.refresh_rate; && testMode.refresh_rate == currMode.refresh_rate;
} }
// Convert the SDL display mode to a DXGI display mode info // Convert the SDL display mode to a DXGI display mode info
// structure and filter out any unwanted modes based on the // structure and filter out any unwanted modes based on the
// supplied flags. // supplied flags.
if (!hasMode) { if (!hasMode) {
bool isNativeMode = (currMode.w == desktopMode.w) DXGI_MODE_DESC mode;
&& (currMode.h == desktopMode.h); mode.Width = currMode.w;
mode.Height = currMode.h;
if (isNativeMode || (Flags & DXGI_ENUM_MODES_SCALING)) { mode.RefreshRate.Numerator = currMode.refresh_rate;
DXGI_MODE_DESC mode; mode.RefreshRate.Denominator = 1;
mode.Width = currMode.w; mode.Format = EnumFormat;
mode.Height = currMode.h; mode.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE;
mode.RefreshRate.Numerator = currMode.refresh_rate; mode.Scaling = DXGI_MODE_SCALING_CENTERED;
mode.RefreshRate.Denominator = 1; modes.push_back(mode);
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);
}
} }
} }

View File

@ -5,11 +5,8 @@
namespace dxvk { namespace dxvk {
DxgiPresenter::DxgiPresenter( DxgiPresenter::DxgiPresenter(
const Rc<DxvkDevice>& device, const Rc<DxvkDevice>& device,
HWND window, HWND window)
uint32_t bufferWidth,
uint32_t bufferHeight,
DXGI_FORMAT bufferFormat)
: m_device (device), : m_device (device),
m_context (device->createContext()) { m_context (device->createContext()) {
@ -19,15 +16,11 @@ namespace dxvk {
m_surface = m_device->adapter()->createSurface(instance, window); m_surface = m_device->adapter()->createSurface(instance, window);
// Create swap chain for the surface // Reset options for the swap chain itself. We will
DxvkSwapchainProperties swapchainProperties; // create a swap chain object before presentation.
swapchainProperties.preferredSurfaceFormat = this->pickFormat(bufferFormat); m_options.preferredSurfaceFormat = { VK_FORMAT_UNDEFINED, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
swapchainProperties.preferredPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; m_options.preferredPresentMode = VK_PRESENT_MODE_FIFO_KHR;
swapchainProperties.preferredBufferSize.width = bufferWidth; m_options.preferredBufferSize = { 0u, 0u };
swapchainProperties.preferredBufferSize.height = bufferHeight;
m_swapchain = m_device->createSwapchain(
m_surface, swapchainProperties);
// Sampler for presentation // Sampler for presentation
DxvkSamplerCreateInfo samplerInfo; DxvkSamplerCreateInfo samplerInfo;
@ -46,8 +39,11 @@ namespace dxvk {
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK; samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
samplerInfo.usePixelCoord = VK_FALSE; samplerInfo.usePixelCoord = VK_FALSE;
m_samplerFitting = m_device->createSampler(samplerInfo);
m_sampler = m_device->createSampler(samplerInfo); samplerInfo.magFilter = VK_FILTER_LINEAR;
samplerInfo.minFilter = VK_FILTER_LINEAR;
m_samplerScaling = m_device->createSampler(samplerInfo);
// Set up context state. The shader bindings and the // Set up context state. The shader bindings and the
// constant state objects will never be modified. // constant state objects will never be modified.
@ -153,6 +149,10 @@ namespace dxvk {
void DxgiPresenter::presentImage() { void DxgiPresenter::presentImage() {
const bool fitSize =
m_backBuffer->info().extent.width == m_options.preferredBufferSize.width
&& m_backBuffer->info().extent.height == m_options.preferredBufferSize.height;
m_context->beginRecording( m_context->beginRecording(
m_device->createCommandList()); m_device->createCommandList());
@ -191,8 +191,10 @@ namespace dxvk {
m_context->setViewports(1, &viewport, &scissor); m_context->setViewports(1, &viewport, &scissor);
m_context->bindResourceSampler(BindingIds::Sampler, m_sampler); m_context->bindResourceSampler(BindingIds::Sampler,
m_context->bindResourceImage (BindingIds::Texture, m_backBufferView); fitSize ? m_samplerFitting : m_samplerScaling);
m_context->bindResourceImage(BindingIds::Texture, m_backBufferView);
m_context->draw(4, 1, 0, 0); m_context->draw(4, 1, 0, 0);
m_device->submitCommandList( m_device->submitCommandList(
@ -256,21 +258,34 @@ namespace dxvk {
} }
void DxgiPresenter::recreateSwapchain( void DxgiPresenter::recreateSwapchain(const DxvkSwapchainProperties& options) {
uint32_t bufferWidth, const bool doRecreate =
uint32_t bufferHeight, options.preferredSurfaceFormat.format != m_options.preferredSurfaceFormat.format
DXGI_FORMAT bufferFormat) { || options.preferredSurfaceFormat.colorSpace != m_options.preferredSurfaceFormat.colorSpace
DxvkSwapchainProperties swapchainProperties; || options.preferredPresentMode != m_options.preferredPresentMode
swapchainProperties.preferredSurfaceFormat = this->pickFormat(bufferFormat); || options.preferredBufferSize.width != m_options.preferredBufferSize.width
swapchainProperties.preferredPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; || options.preferredBufferSize.height != m_options.preferredBufferSize.height;
swapchainProperties.preferredBufferSize.width = bufferWidth;
swapchainProperties.preferredBufferSize.height = bufferHeight;
m_swapchain->changeProperties(swapchainProperties); if (doRecreate) {
Logger::info(str::format(
"DxgiPresenter: Recreating swap chain: ",
"\n Format: ", options.preferredSurfaceFormat.format,
"\n Present mode: ", options.preferredPresentMode,
"\n Buffer size: ", options.preferredBufferSize.width, "x", options.preferredBufferSize.height));
m_options = options;
if (m_swapchain == nullptr) {
m_swapchain = m_device->createSwapchain(
m_surface, options);
} else {
m_swapchain->changeProperties(options);
}
}
} }
VkSurfaceFormatKHR DxgiPresenter::pickFormat(DXGI_FORMAT fmt) const { VkSurfaceFormatKHR DxgiPresenter::pickSurfaceFormat(DXGI_FORMAT fmt) const {
std::vector<VkSurfaceFormatKHR> formats; std::vector<VkSurfaceFormatKHR> formats;
switch (fmt) { switch (fmt) {
@ -295,6 +310,11 @@ namespace dxvk {
} }
VkPresentModeKHR DxgiPresenter::pickPresentMode(VkPresentModeKHR preferred) const {
return m_surface->pickPresentMode(1, &preferred);
}
Rc<DxvkShader> DxgiPresenter::createVertexShader() { Rc<DxvkShader> DxgiPresenter::createVertexShader() {
SpirvModule module; SpirvModule module;

View File

@ -22,11 +22,8 @@ namespace dxvk {
public: public:
DxgiPresenter( DxgiPresenter(
const Rc<DxvkDevice>& device, const Rc<DxvkDevice>& device,
HWND window, HWND window);
uint32_t bufferWidth,
uint32_t bufferHeight,
DXGI_FORMAT bufferFormat);
~DxgiPresenter(); ~DxgiPresenter();
@ -50,16 +47,36 @@ namespace dxvk {
* \param [in] image Back buffer image * \param [in] image Back buffer image
*/ */
void updateBackBuffer( void updateBackBuffer(
const Rc<DxvkImage>& image); const Rc<DxvkImage>& image);
/** /**
* \brief Renders image to the screen * \brief Recreats Vulkan swap chain
* \param [in] view Source image view *
* Only actually recreates the swap chain object
* if any of the properties have changed. If no
* properties have changed, this is a no-op.
* \param [in] options New swap chain options
*/ */
void recreateSwapchain( void recreateSwapchain(
uint32_t bufferWidth, const DxvkSwapchainProperties& options);
uint32_t bufferHeight,
DXGI_FORMAT bufferFormat); /**
* \brief Picks a surface format based on a DXGI format
*
* This will return a supported format that, if possible,
* has properties similar to those of the DXGI format.
* \param [in] fmt The DXGI format
* \returns The Vulkan format
*/
VkSurfaceFormatKHR pickSurfaceFormat(DXGI_FORMAT fmt) const;
/**
* \brief Picks a supported present mode
*
* \param [in] preferred Preferred present mode
* \returns An actually supported present mode
*/
VkPresentModeKHR pickPresentMode(VkPresentModeKHR preferred) const;
private: private:
@ -74,13 +91,14 @@ namespace dxvk {
Rc<DxvkSurface> m_surface; Rc<DxvkSurface> m_surface;
Rc<DxvkSwapchain> m_swapchain; Rc<DxvkSwapchain> m_swapchain;
Rc<DxvkSampler> m_sampler; Rc<DxvkSampler> m_samplerFitting;
Rc<DxvkSampler> m_samplerScaling;
Rc<DxvkImage> m_backBuffer; Rc<DxvkImage> m_backBuffer;
Rc<DxvkImage> m_backBufferResolve; Rc<DxvkImage> m_backBufferResolve;
Rc<DxvkImageView> m_backBufferView; Rc<DxvkImageView> m_backBufferView;
VkSurfaceFormatKHR pickFormat(DXGI_FORMAT fmt) const; DxvkSwapchainProperties m_options;
Rc<DxvkShader> createVertexShader(); Rc<DxvkShader> createVertexShader();
Rc<DxvkShader> createFragmentShader(); Rc<DxvkShader> createFragmentShader();

View File

@ -36,36 +36,24 @@ namespace dxvk {
m_stats.SyncQPCTime.QuadPart = 0; m_stats.SyncQPCTime.QuadPart = 0;
m_stats.SyncGPUTime.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()));
}
// Adjust initial back buffer size. If zero, these // Adjust initial back buffer size. If zero, these
// shall be set to the current window size. // shall be set to the current window size.
VkExtent2D windowSize = this->getWindowSize(); const VkExtent2D windowSize = GetWindowSize();
if (m_desc.BufferDesc.Width == 0) m_desc.BufferDesc.Width = windowSize.width; if (m_desc.BufferDesc.Width == 0) m_desc.BufferDesc.Width = windowSize.width;
if (m_desc.BufferDesc.Height == 0) m_desc.BufferDesc.Height = windowSize.height; if (m_desc.BufferDesc.Height == 0) m_desc.BufferDesc.Height = windowSize.height;
// Set initial window mode and fullscreen state // Set initial window mode and fullscreen state
if (FAILED(this->SetFullscreenState(!pDesc->Windowed, nullptr))) // if (FAILED(this->SetFullscreenState(!pDesc->Windowed, nullptr)))
throw DxvkError("DxgiSwapChain::DxgiSwapChain: Failed to set initial fullscreen state"); // throw DxvkError("DxgiSwapChain: Failed to set initial fullscreen state");
this->createPresenter(); if (FAILED(CreatePresenter()) || FAILED(CreateBackBuffer()))
this->createBackBuffer(); throw DxvkError("DxgiSwapChain: Failed to create presenter or back buffer");
TRACE(this);
} }
DxgiSwapChain::~DxgiSwapChain() { DxgiSwapChain::~DxgiSwapChain() {
TRACE(this);
// We do not release the SDL window handle here since
// that would destroy the underlying window as well.
} }
@ -106,18 +94,8 @@ namespace dxvk {
if (ppOutput != nullptr) if (ppOutput != nullptr)
return DXGI_ERROR_INVALID_CALL; return DXGI_ERROR_INVALID_CALL;
// We can use the display index returned by SDL to query the Logger::err("DxgiSwapChain::GetContainingOutput: Not implemented");
// containing output, since DxgiAdapter::EnumOutputs uses the return E_NOTIMPL;
// same output IDs.
std::lock_guard<std::mutex> 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;
}
return m_adapter->EnumOutputs(displayId, ppOutput);
} }
@ -175,10 +153,23 @@ namespace dxvk {
// Submit pending rendering commands // Submit pending rendering commands
// before recording the present code. // before recording the present code.
m_presentDevice->FlushRenderingCommands(); m_presentDevice->FlushRenderingCommands();
// TODO implement sync interval // Update swap chain properties. This will not only set
// TODO implement flags // up vertical synchronization properly, but also apply
m_presenter->presentImage(); // changes that were made to the window size even if the
// Vulkan swap chain itself remains valid.
DxvkSwapchainProperties swapchainProps;
swapchainProps.preferredSurfaceFormat
= m_presenter->pickSurfaceFormat(m_desc.BufferDesc.Format);
swapchainProps.preferredPresentMode = SyncInterval == 0
? m_presenter->pickPresentMode(VK_PRESENT_MODE_IMMEDIATE_KHR)
: m_presenter->pickPresentMode(VK_PRESENT_MODE_MAILBOX_KHR);
swapchainProps.preferredBufferSize = GetWindowSize();
m_presenter->recreateSwapchain(swapchainProps);
for (uint32_t i = 0; i < SyncInterval || i < 1; i++)
m_presenter->presentImage();
return S_OK; return S_OK;
} catch (const DxvkError& err) { } catch (const DxvkError& err) {
Logger::err(err.message()); Logger::err(err.message());
@ -195,9 +186,9 @@ namespace dxvk {
UINT SwapChainFlags) { UINT SwapChainFlags) {
std::lock_guard<std::mutex> lock(m_mutex); std::lock_guard<std::mutex> lock(m_mutex);
VkExtent2D windowSize = this->getWindowSize(); const VkExtent2D windowSize = GetWindowSize();
m_desc.BufferDesc.Width = Width != 0 ? Width : windowSize.width; m_desc.BufferDesc.Width = Width != 0 ? Width : windowSize.width;
m_desc.BufferDesc.Height = Height != 0 ? Height : windowSize.height; m_desc.BufferDesc.Height = Height != 0 ? Height : windowSize.height;
m_desc.Flags = SwapChainFlags; m_desc.Flags = SwapChainFlags;
@ -208,17 +199,7 @@ namespace dxvk {
if (NewFormat != DXGI_FORMAT_UNKNOWN) if (NewFormat != DXGI_FORMAT_UNKNOWN)
m_desc.BufferDesc.Format = NewFormat; m_desc.BufferDesc.Format = NewFormat;
try { return CreateBackBuffer();
m_presenter->recreateSwapchain(
m_desc.BufferDesc.Width,
m_desc.BufferDesc.Height,
m_desc.BufferDesc.Format);
this->createBackBuffer();
return S_OK;
} catch (const DxvkError& err) {
Logger::err(err.message());
return DXGI_ERROR_DRIVER_INTERNAL_ERROR;
}
} }
@ -228,39 +209,21 @@ namespace dxvk {
std::lock_guard<std::mutex> lock(m_mutex); std::lock_guard<std::mutex> lock(m_mutex);
// Applies to windowed mode // TODO support fullscreen mode
SDL_SetWindowSize(m_window, RECT newRect = { 0, 0, 0, 0 };
pNewTargetParameters->Width, RECT oldRect = { 0, 0, 0, 0 };
pNewTargetParameters->Height);
// Applies to fullscreen mode ::GetWindowRect(m_desc.OutputWindow, &oldRect);
SDL_DisplayMode displayMode; ::SetRect(&newRect, 0, 0, pNewTargetParameters->Width, pNewTargetParameters->Height);
displayMode.format = SDL_PIXELFORMAT_RGBA32; ::AdjustWindowRectEx(&newRect,
displayMode.w = pNewTargetParameters->Width; ::GetWindowLongW(m_desc.OutputWindow, GWL_STYLE), FALSE,
displayMode.h = pNewTargetParameters->Height; ::GetWindowLongW(m_desc.OutputWindow, GWL_EXSTYLE));
displayMode.refresh_rate = pNewTargetParameters->RefreshRate.Numerator ::SetRect(&newRect, 0, 0, newRect.right - newRect.left, newRect.bottom - newRect.top);
/ pNewTargetParameters->RefreshRate.Denominator; ::OffsetRect(&newRect, oldRect.left, oldRect.top);
displayMode.driverdata = nullptr; ::MoveWindow(m_desc.OutputWindow, newRect.left, newRect.top,
newRect.right - newRect.left, newRect.bottom - newRect.top, TRUE);
// TODO test mode change flag return S_OK;
if (SDL_SetWindowDisplayMode(m_window, &displayMode)) {
Logger::err(str::format(
"DxgiSwapChain::ResizeTarget: Failed to set display mode:\n",
SDL_GetError()));
return DXGI_ERROR_DRIVER_INTERNAL_ERROR;
}
try {
m_presenter->recreateSwapchain(
m_desc.BufferDesc.Width,
m_desc.BufferDesc.Height,
m_desc.BufferDesc.Format);
return S_OK;
} catch (const DxvkError& err) {
Logger::err(err.message());
return DXGI_ERROR_DRIVER_INTERNAL_ERROR;
}
} }
@ -269,81 +232,54 @@ namespace dxvk {
IDXGIOutput* pTarget) { IDXGIOutput* pTarget) {
std::lock_guard<std::mutex> lock(m_mutex); std::lock_guard<std::mutex> lock(m_mutex);
// Unconditionally reset the swap chain to windowed mode first. Logger::err("DxgiSwapChain::SetFullscreenState: Not implemented");
// This required if the application wants to move the window to return E_NOTIMPL;
// 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;
} }
void DxgiSwapChain::createPresenter() { HRESULT DxgiSwapChain::CreatePresenter() {
m_presenter = new DxgiPresenter( try {
m_device->GetDXVKDevice(), m_presenter = new DxgiPresenter(
m_desc.OutputWindow, m_device->GetDXVKDevice(),
m_desc.BufferDesc.Width, m_desc.OutputWindow);
m_desc.BufferDesc.Height, return S_OK;
m_desc.BufferDesc.Format); } catch (const DxvkError& e) {
Logger::err(e.message());
return E_FAIL;
}
} }
void DxgiSwapChain::createBackBuffer() { HRESULT DxgiSwapChain::CreateBackBuffer() {
VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT; VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT;
if (FAILED(GetSampleCount(m_desc.SampleDesc.Count, &sampleCount))) if (FAILED(GetSampleCount(m_desc.SampleDesc.Count, &sampleCount))) {
throw DxvkError("DxgiSwapChain: Invalid sample count"); Logger::err("DxgiSwapChain: Invalid sample count");
return E_INVALIDARG;
}
if (FAILED(m_presentDevice->CreateSwapChainBackBuffer(&m_desc, &m_backBuffer))) if (FAILED(m_presentDevice->CreateSwapChainBackBuffer(&m_desc, &m_backBuffer))) {
throw DxvkError("DxgiSwapChain: Failed to create back buffer"); Logger::err("DxgiSwapChain: Failed to create back buffer");
return E_FAIL;
}
m_presenter->updateBackBuffer(m_backBuffer->GetDXVKImage()); try {
m_presenter->updateBackBuffer(m_backBuffer->GetDXVKImage());
return S_OK;
} catch (const DxvkError& e) {
Logger::err(e.message());
return E_FAIL;
}
} }
VkExtent2D DxgiSwapChain::getWindowSize() const { VkExtent2D DxgiSwapChain::GetWindowSize() const {
int winWidth = 0; RECT windowRect;
int winHeight = 0; ::GetClientRect(m_desc.OutputWindow, &windowRect);
SDL_GetWindowSize(m_window, &winWidth, &winHeight);
VkExtent2D result; VkExtent2D result;
result.width = winWidth; result.width = windowRect.right;
result.height = winHeight; result.height = windowRect.bottom;
return result; return result;
} }

View File

@ -88,20 +88,16 @@ namespace dxvk {
Com<IDXGIDevicePrivate> m_device; Com<IDXGIDevicePrivate> m_device;
Com<IDXGIPresentDevicePrivate> m_presentDevice; Com<IDXGIPresentDevicePrivate> m_presentDevice;
DXGI_SWAP_CHAIN_DESC m_desc; DXGI_SWAP_CHAIN_DESC m_desc;
DXGI_FRAME_STATISTICS m_stats; DXGI_FRAME_STATISTICS m_stats;
SDL_Window* m_window = nullptr; Rc<DxgiPresenter> m_presenter;
Com<IDXGIPresentBackBuffer> m_backBuffer;
Rc<DxgiPresenter> m_presenter; HRESULT CreatePresenter();
Com<IDXGIPresentBackBuffer> m_backBuffer; HRESULT CreateBackBuffer();
void createPresenter(); VkExtent2D GetWindowSize() const;
void createBackBuffer();
void createContext();
VkExtent2D getWindowSize() const;
HRESULT GetSampleCount( HRESULT GetSampleCount(
UINT Count, UINT Count,