1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-02-20 19:54:19 +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
&& testMode.h == currMode.h
&& testMode.refresh_rate == currMode.refresh_rate;
&& 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);
}
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 = DXGI_MODE_SCALING_CENTERED;
modes.push_back(mode);
}
}

View File

@ -5,11 +5,8 @@
namespace dxvk {
DxgiPresenter::DxgiPresenter(
const Rc<DxvkDevice>& device,
HWND window,
uint32_t bufferWidth,
uint32_t bufferHeight,
DXGI_FORMAT bufferFormat)
const Rc<DxvkDevice>& device,
HWND window)
: m_device (device),
m_context (device->createContext()) {
@ -19,15 +16,11 @@ namespace dxvk {
m_surface = m_device->adapter()->createSurface(instance, window);
// Create swap chain for the surface
DxvkSwapchainProperties swapchainProperties;
swapchainProperties.preferredSurfaceFormat = this->pickFormat(bufferFormat);
swapchainProperties.preferredPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
swapchainProperties.preferredBufferSize.width = bufferWidth;
swapchainProperties.preferredBufferSize.height = bufferHeight;
m_swapchain = m_device->createSwapchain(
m_surface, swapchainProperties);
// Reset options for the swap chain itself. We will
// create a swap chain object before presentation.
m_options.preferredSurfaceFormat = { VK_FORMAT_UNDEFINED, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
m_options.preferredPresentMode = VK_PRESENT_MODE_FIFO_KHR;
m_options.preferredBufferSize = { 0u, 0u };
// Sampler for presentation
DxvkSamplerCreateInfo samplerInfo;
@ -46,8 +39,11 @@ namespace dxvk {
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
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
// constant state objects will never be modified.
@ -153,6 +149,10 @@ namespace dxvk {
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_device->createCommandList());
@ -191,8 +191,10 @@ namespace dxvk {
m_context->setViewports(1, &viewport, &scissor);
m_context->bindResourceSampler(BindingIds::Sampler, m_sampler);
m_context->bindResourceImage (BindingIds::Texture, m_backBufferView);
m_context->bindResourceSampler(BindingIds::Sampler,
fitSize ? m_samplerFitting : m_samplerScaling);
m_context->bindResourceImage(BindingIds::Texture, m_backBufferView);
m_context->draw(4, 1, 0, 0);
m_device->submitCommandList(
@ -256,21 +258,34 @@ namespace dxvk {
}
void DxgiPresenter::recreateSwapchain(
uint32_t bufferWidth,
uint32_t bufferHeight,
DXGI_FORMAT bufferFormat) {
DxvkSwapchainProperties swapchainProperties;
swapchainProperties.preferredSurfaceFormat = this->pickFormat(bufferFormat);
swapchainProperties.preferredPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
swapchainProperties.preferredBufferSize.width = bufferWidth;
swapchainProperties.preferredBufferSize.height = bufferHeight;
void DxgiPresenter::recreateSwapchain(const DxvkSwapchainProperties& options) {
const bool doRecreate =
options.preferredSurfaceFormat.format != m_options.preferredSurfaceFormat.format
|| options.preferredSurfaceFormat.colorSpace != m_options.preferredSurfaceFormat.colorSpace
|| options.preferredPresentMode != m_options.preferredPresentMode
|| options.preferredBufferSize.width != m_options.preferredBufferSize.width
|| options.preferredBufferSize.height != m_options.preferredBufferSize.height;
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;
switch (fmt) {
@ -295,6 +310,11 @@ namespace dxvk {
}
VkPresentModeKHR DxgiPresenter::pickPresentMode(VkPresentModeKHR preferred) const {
return m_surface->pickPresentMode(1, &preferred);
}
Rc<DxvkShader> DxgiPresenter::createVertexShader() {
SpirvModule module;

View File

@ -22,11 +22,8 @@ namespace dxvk {
public:
DxgiPresenter(
const Rc<DxvkDevice>& device,
HWND window,
uint32_t bufferWidth,
uint32_t bufferHeight,
DXGI_FORMAT bufferFormat);
const Rc<DxvkDevice>& device,
HWND window);
~DxgiPresenter();
@ -50,16 +47,36 @@ namespace dxvk {
* \param [in] image Back buffer image
*/
void updateBackBuffer(
const Rc<DxvkImage>& image);
const Rc<DxvkImage>& image);
/**
* \brief Renders image to the screen
* \param [in] view Source image view
* \brief Recreats Vulkan swap chain
*
* 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(
uint32_t bufferWidth,
uint32_t bufferHeight,
DXGI_FORMAT bufferFormat);
const DxvkSwapchainProperties& options);
/**
* \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:
@ -74,13 +91,14 @@ namespace dxvk {
Rc<DxvkSurface> m_surface;
Rc<DxvkSwapchain> m_swapchain;
Rc<DxvkSampler> m_sampler;
Rc<DxvkSampler> m_samplerFitting;
Rc<DxvkSampler> m_samplerScaling;
Rc<DxvkImage> m_backBuffer;
Rc<DxvkImage> m_backBufferResolve;
Rc<DxvkImageView> m_backBufferView;
VkSurfaceFormatKHR pickFormat(DXGI_FORMAT fmt) const;
DxvkSwapchainProperties m_options;
Rc<DxvkShader> createVertexShader();
Rc<DxvkShader> createFragmentShader();

View File

@ -36,36 +36,24 @@ namespace dxvk {
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()));
}
// Adjust initial back buffer size. If zero, these
// 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.Height == 0) m_desc.BufferDesc.Height = windowSize.height;
// Set initial window mode and fullscreen state
if (FAILED(this->SetFullscreenState(!pDesc->Windowed, nullptr)))
throw DxvkError("DxgiSwapChain::DxgiSwapChain: Failed to set initial fullscreen state");
// if (FAILED(this->SetFullscreenState(!pDesc->Windowed, nullptr)))
// throw DxvkError("DxgiSwapChain: Failed to set initial fullscreen state");
this->createPresenter();
this->createBackBuffer();
TRACE(this);
if (FAILED(CreatePresenter()) || FAILED(CreateBackBuffer()))
throw DxvkError("DxgiSwapChain: Failed to create presenter or back buffer");
}
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)
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<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);
Logger::err("DxgiSwapChain::GetContainingOutput: Not implemented");
return E_NOTIMPL;
}
@ -175,10 +153,23 @@ namespace dxvk {
// Submit pending rendering commands
// before recording the present code.
m_presentDevice->FlushRenderingCommands();
// TODO implement sync interval
// TODO implement flags
m_presenter->presentImage();
// Update swap chain properties. This will not only set
// up vertical synchronization properly, but also apply
// 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;
} catch (const DxvkError& err) {
Logger::err(err.message());
@ -195,9 +186,9 @@ namespace dxvk {
UINT SwapChainFlags) {
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.Flags = SwapChainFlags;
@ -208,17 +199,7 @@ namespace dxvk {
if (NewFormat != DXGI_FORMAT_UNKNOWN)
m_desc.BufferDesc.Format = NewFormat;
try {
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;
}
return CreateBackBuffer();
}
@ -228,39 +209,21 @@ namespace dxvk {
std::lock_guard<std::mutex> lock(m_mutex);
// Applies to windowed mode
SDL_SetWindowSize(m_window,
pNewTargetParameters->Width,
pNewTargetParameters->Height);
// TODO support fullscreen mode
RECT newRect = { 0, 0, 0, 0 };
RECT oldRect = { 0, 0, 0, 0 };
// 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;
::GetWindowRect(m_desc.OutputWindow, &oldRect);
::SetRect(&newRect, 0, 0, pNewTargetParameters->Width, pNewTargetParameters->Height);
::AdjustWindowRectEx(&newRect,
::GetWindowLongW(m_desc.OutputWindow, GWL_STYLE), FALSE,
::GetWindowLongW(m_desc.OutputWindow, GWL_EXSTYLE));
::SetRect(&newRect, 0, 0, newRect.right - newRect.left, newRect.bottom - newRect.top);
::OffsetRect(&newRect, oldRect.left, oldRect.top);
::MoveWindow(m_desc.OutputWindow, newRect.left, newRect.top,
newRect.right - newRect.left, newRect.bottom - newRect.top, TRUE);
// TODO test mode change flag
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;
}
return S_OK;
}
@ -269,81 +232,54 @@ namespace dxvk {
IDXGIOutput* pTarget) {
std::lock_guard<std::mutex> 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;
Logger::err("DxgiSwapChain::SetFullscreenState: Not implemented");
return E_NOTIMPL;
}
void DxgiSwapChain::createPresenter() {
m_presenter = new DxgiPresenter(
m_device->GetDXVKDevice(),
m_desc.OutputWindow,
m_desc.BufferDesc.Width,
m_desc.BufferDesc.Height,
m_desc.BufferDesc.Format);
HRESULT DxgiSwapChain::CreatePresenter() {
try {
m_presenter = new DxgiPresenter(
m_device->GetDXVKDevice(),
m_desc.OutputWindow);
return S_OK;
} catch (const DxvkError& e) {
Logger::err(e.message());
return E_FAIL;
}
}
void DxgiSwapChain::createBackBuffer() {
HRESULT DxgiSwapChain::CreateBackBuffer() {
VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT;
if (FAILED(GetSampleCount(m_desc.SampleDesc.Count, &sampleCount)))
throw DxvkError("DxgiSwapChain: Invalid sample count");
if (FAILED(GetSampleCount(m_desc.SampleDesc.Count, &sampleCount))) {
Logger::err("DxgiSwapChain: Invalid sample count");
return E_INVALIDARG;
}
if (FAILED(m_presentDevice->CreateSwapChainBackBuffer(&m_desc, &m_backBuffer)))
throw DxvkError("DxgiSwapChain: Failed to create back buffer");
if (FAILED(m_presentDevice->CreateSwapChainBackBuffer(&m_desc, &m_backBuffer))) {
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 {
int winWidth = 0;
int winHeight = 0;
SDL_GetWindowSize(m_window, &winWidth, &winHeight);
VkExtent2D DxgiSwapChain::GetWindowSize() const {
RECT windowRect;
::GetClientRect(m_desc.OutputWindow, &windowRect);
VkExtent2D result;
result.width = winWidth;
result.height = winHeight;
result.width = windowRect.right;
result.height = windowRect.bottom;
return result;
}

View File

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