Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Vulkan swapchain extent and buffer count issues, Implement Vulkan mailbox mode option #787

Merged
merged 3 commits into from
Jul 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion neo/sys/DeviceManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ struct DeviceCreationParameters
uint32_t backBufferHeight = 720;
uint32_t backBufferSampleCount = 1; // optional HDR Framebuffer MSAA
uint32_t refreshRate = 0;
uint32_t swapChainBufferCount = 3; // SRS - hardcode to 3 for Vsync modes and linux surfaceCaps.minImageCount = 3
uint32_t swapChainBufferCount = NUM_FRAME_DATA; // SRS - default matches GPU frames, can be overridden by renderer
nvrhi::Format swapChainFormat = nvrhi::Format::RGBA8_UNORM; // RB: don't do the sRGB gamma ramp with the swapchain
uint32_t swapChainSampleCount = 1;
uint32_t swapChainSampleQuality = 0;
Expand Down
55 changes: 40 additions & 15 deletions neo/sys/DeviceManager_VK.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@
idCVar r_vmaDeviceLocalMemoryMB( "r_vmaDeviceLocalMemoryMB", "256", CVAR_INTEGER | CVAR_INIT, "Size of VMA allocation block for gpu memory." );
#endif

idCVar r_preferFastSync( "r_preferFastSync", "1", CVAR_RENDERER | CVAR_ARCHIVE | CVAR_BOOL, "Prefer Fast Sync/no-tearing in place of VSync off/tearing (Vulkan only)" );

// Define the Vulkan dynamic dispatcher - this needs to occur in exactly one cpp file in the program.
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE

Expand Down Expand Up @@ -280,8 +282,10 @@ class DeviceManager_VK : public DeviceManager

nvrhi::EventQueryHandle m_FrameWaitQuery;

// SRS - flag indicating support for eFifoRelaxed surface presentation (r_swapInterval = 1) mode
bool enablePModeFifoRelaxed = false;
// SRS - flags indicating support for various Vulkan surface presentation modes
bool enablePModeMailbox = false; // r_swapInterval = 0 (defaults to eImmediate if not available)
bool enablePModeImmediate = false; // r_swapInterval = 0 (defaults to eFifo if not available)
bool enablePModeFifoRelaxed = false; // r_swapInterval = 1 (defaults to eFifo if not available)


private:
Expand Down Expand Up @@ -559,14 +563,12 @@ bool DeviceManager_VK::pickPhysicalDevice()
auto surfaceFmts = dev.getSurfaceFormatsKHR( m_WindowSurface );
auto surfacePModes = dev.getSurfacePresentModesKHR( m_WindowSurface );

if( surfaceCaps.minImageCount > m_DeviceParams.swapChainBufferCount ||
( surfaceCaps.maxImageCount < m_DeviceParams.swapChainBufferCount && surfaceCaps.maxImageCount > 0 ) )
{
errorStream << std::endl << " - cannot support the requested swap chain image count:";
errorStream << " requested " << m_DeviceParams.swapChainBufferCount << ", available " << surfaceCaps.minImageCount << " - " << surfaceCaps.maxImageCount;
deviceIsGood = false;
}
// SRS/Ricardo Garcia rg3 - clamp swapChainBufferCount to the min/max capabilities of the surface
m_DeviceParams.swapChainBufferCount = Max( surfaceCaps.minImageCount, m_DeviceParams.swapChainBufferCount );
m_DeviceParams.swapChainBufferCount = surfaceCaps.maxImageCount > 0 ? Min( m_DeviceParams.swapChainBufferCount, surfaceCaps.maxImageCount ) : m_DeviceParams.swapChainBufferCount;

/* SRS - Don't check extent here since window manager surfaceCaps may restrict extent to something smaller than requested
- Instead, check and clamp extent to window manager surfaceCaps during swap chain creation inside createSwapChain()
if( surfaceCaps.minImageExtent.width > requestedExtent.width ||
surfaceCaps.minImageExtent.height > requestedExtent.height ||
surfaceCaps.maxImageExtent.width < requestedExtent.width ||
Expand All @@ -578,6 +580,7 @@ bool DeviceManager_VK::pickPhysicalDevice()
errorStream << " - " << surfaceCaps.maxImageExtent.width << "x" << surfaceCaps.maxImageExtent.height;
deviceIsGood = false;
}
*/

bool surfaceFormatPresent = false;
for( const vk::SurfaceFormatKHR& surfaceFmt : surfaceFmts )
Expand All @@ -596,11 +599,10 @@ bool DeviceManager_VK::pickPhysicalDevice()
deviceIsGood = false;
}

if( ( find( surfacePModes.begin(), surfacePModes.end(), vk::PresentModeKHR::eImmediate ) == surfacePModes.end() ) ||
( find( surfacePModes.begin(), surfacePModes.end(), vk::PresentModeKHR::eFifo ) == surfacePModes.end() ) )
if( find( surfacePModes.begin(), surfacePModes.end(), vk::PresentModeKHR::eFifo ) == surfacePModes.end() )
{
// can't find the required surface present modes
errorStream << std::endl << " - does not support the requested surface present modes";
// this should never happen since eFifo is mandatory according to the Vulkan spec
errorStream << std::endl << " - does not support the required surface present modes";
deviceIsGood = false;
}

Expand Down Expand Up @@ -908,8 +910,10 @@ bool DeviceManager_VK::createDevice()
&imageFormatProperties );
m_DeviceParams.enableImageFormatD24S8 = ( ret == vk::Result::eSuccess );

// SRS - Determine if "smart" (r_swapInterval = 1) vsync mode eFifoRelaxed is supported by device and surface
// SRS/rg3 - Determine which Vulkan surface present modes are supported by device and surface
auto surfacePModes = m_VulkanPhysicalDevice.getSurfacePresentModesKHR( m_WindowSurface );
enablePModeMailbox = find( surfacePModes.begin(), surfacePModes.end(), vk::PresentModeKHR::eMailbox ) != surfacePModes.end();
enablePModeImmediate = find( surfacePModes.begin(), surfacePModes.end(), vk::PresentModeKHR::eImmediate ) != surfacePModes.end();
enablePModeFifoRelaxed = find( surfacePModes.begin(), surfacePModes.end(), vk::PresentModeKHR::eFifoRelaxed ) != surfacePModes.end();

// stash the renderer string
Expand Down Expand Up @@ -998,6 +1002,11 @@ bool DeviceManager_VK::createSwapChain()
vk::ColorSpaceKHR::eSrgbNonlinear
};

// SRS - Clamp swap chain extent within the range supported by the device / window surface
auto surfaceCaps = m_VulkanPhysicalDevice.getSurfaceCapabilitiesKHR( m_WindowSurface );
m_DeviceParams.backBufferWidth = idMath::ClampInt( surfaceCaps.minImageExtent.width, surfaceCaps.maxImageExtent.width, m_DeviceParams.backBufferWidth );
m_DeviceParams.backBufferHeight = idMath::ClampInt( surfaceCaps.minImageExtent.height, surfaceCaps.maxImageExtent.height, m_DeviceParams.backBufferHeight );

vk::Extent2D extent = vk::Extent2D( m_DeviceParams.backBufferWidth, m_DeviceParams.backBufferHeight );

std::unordered_set<uint32_t> uniqueQueues =
Expand All @@ -1010,6 +1019,22 @@ bool DeviceManager_VK::createSwapChain()

const bool enableSwapChainSharing = queues.size() > 1;

// SRS/rg3 - set up Vulkan present mode based on vsync setting and available surface features
vk::PresentModeKHR presentMode;
switch( m_DeviceParams.vsyncEnabled )
{
case 0:
presentMode = enablePModeMailbox && r_preferFastSync.GetBool() ? vk::PresentModeKHR::eMailbox :
( enablePModeImmediate ? vk::PresentModeKHR::eImmediate : vk::PresentModeKHR::eFifo );
break;
case 1:
presentMode = enablePModeFifoRelaxed ? vk::PresentModeKHR::eFifoRelaxed : vk::PresentModeKHR::eFifo;
break;
case 2:
default:
presentMode = vk::PresentModeKHR::eFifo; // eFifo always supported according to Vulkan spec
}

auto desc = vk::SwapchainCreateInfoKHR()
.setSurface( m_WindowSurface )
.setMinImageCount( m_DeviceParams.swapChainBufferCount )
Expand All @@ -1023,7 +1048,7 @@ bool DeviceManager_VK::createSwapChain()
.setPQueueFamilyIndices( enableSwapChainSharing ? queues.data() : nullptr )
.setPreTransform( vk::SurfaceTransformFlagBitsKHR::eIdentity )
.setCompositeAlpha( vk::CompositeAlphaFlagBitsKHR::eOpaque )
.setPresentMode( m_DeviceParams.vsyncEnabled > 0 ? ( m_DeviceParams.vsyncEnabled == 2 || !enablePModeFifoRelaxed ? vk::PresentModeKHR::eFifo : vk::PresentModeKHR::eFifoRelaxed ) : vk::PresentModeKHR::eImmediate )
.setPresentMode( presentMode )
.setClipped( true )
.setOldSwapchain( nullptr );

Expand Down
61 changes: 45 additions & 16 deletions neo/sys/sdl/sdl_vkimp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@ void DeviceManager::UpdateWindowSize( const glimpParms_t& parms )
m_DeviceParams.vsyncEnabled = m_RequestedVSync;

ResizeSwapChain();

// SRS - Get actual swapchain dimensions to set new render size
deviceManager->GetWindowDimensions( glConfig.nativeScreenWidth, glConfig.nativeScreenHeight );

BackBufferResized();
}
else
Expand Down Expand Up @@ -195,7 +199,8 @@ static int GetDisplayIndex( glimpParms_t parms )
{
SDL_Rect rect;
SDL_GetDisplayBounds( i, &rect );
if( windowPosX >= rect.x && windowPosX < ( rect.x + rect.w ) && windowPosY >= rect.y && windowPosY < ( rect.y + rect.h ) )
if( ( windowPosX >= rect.x && windowPosX < ( rect.x + rect.w ) && windowPosY >= rect.y && windowPosY < ( rect.y + rect.h ) ) ||
( parms.x == SDL_WINDOWPOS_CENTERED_DISPLAY( i ) && parms.y == SDL_WINDOWPOS_CENTERED_DISPLAY( i ) ) )
{
displayIdx = i;
break;
Expand All @@ -206,11 +211,18 @@ static int GetDisplayIndex( glimpParms_t parms )
return displayIdx;
}

// SRS - Function to get display frequency of monitor hosting the current window
// SRS - Function to get display frequency of monitor corresponding to the window position
static int GetDisplayFrequency( glimpParms_t parms )
{
int displayIndex = GetDisplayIndex( parms );
if( displayIndex < 0 )
{
// SRS - window is out of bounds for desktop, fall back to primary display
displayIndex = 0;
}

SDL_DisplayMode m = {0};
if( SDL_GetWindowDisplayMode( window, &m ) < 0 )
if( SDL_GetCurrentDisplayMode( displayIndex, &m ) )
{
common->Warning( "Couldn't get display refresh rate, reason: %s", SDL_GetError() );
return parms.displayHz;
Expand Down Expand Up @@ -255,8 +267,9 @@ bool VKimp_Init( glimpParms_t parms )
}
else if( GetDisplayIndex( parms ) < 0 ) // verify window position for -1 and -2 borderless modes
{
// SRS - window is out of bounds for desktop, startup on default display instead
createParms.x = createParms.y = SDL_WINDOWPOS_UNDEFINED;
// SRS - window is out of bounds for desktop, startup on primary display instead
createParms.x = createParms.y = SDL_WINDOWPOS_CENTERED;
common->Warning( "Window position out of bounds, falling back to primary display" );
}

if( !deviceManager->CreateWindowDeviceAndSwapChain( createParms, GAME_NAME ) )
Expand All @@ -280,24 +293,36 @@ bool VKimp_Init( glimpParms_t parms )
}
}

// SRS - Move to fullscreen mode after window creation to avoid SDL platform differences
// SRS - Switch into fullscreen mode after window creation to avoid SDL platform differences
if( SDL_SetWindowFullscreen( window, SDL_WINDOW_FULLSCREEN ) < 0 )
{
common->Warning( "Couldn't switch to fullscreen mode, reason: %s", SDL_GetError() );
}
}
else if( parms.fullScreen == -2 )
{
// SRS - Move to borderless fullscreen mode after window creation
// SRS - Switch into borderless fullscreen mode after window creation
if( SDL_SetWindowFullscreen( window, SDL_WINDOW_FULLSCREEN_DESKTOP ) < 0 )
{
common->Warning( "Couldn't switch to borderless fullscreen mode, reason: %s", SDL_GetError() );
}
}
else if( parms.fullScreen == -1 )
{
// SRS - Make sure custom borderless window is in position after window creation
SDL_SetWindowPosition( window, createParms.x, createParms.y );
}

// RB begin
SDL_GetWindowSize( window, &glConfig.nativeScreenWidth, &glConfig.nativeScreenHeight );
// RB end
if( parms.fullScreen )
{
// SRS - Get window's client area dimensions to set initial render size for fullscreen modes
SDL_GetWindowSize( window, &glConfig.nativeScreenWidth, &glConfig.nativeScreenHeight );
}
else
{
// SRS - Get actual swapchain dimensions to set initial render size for windowed mode
deviceManager->GetWindowDimensions( glConfig.nativeScreenWidth, glConfig.nativeScreenHeight );
}

// SRS - Detect and save actual fullscreen state supporting all modes (-2, -1, 0, 1, ...)
glConfig.isFullscreen = ( SDL_GetWindowFlags( window ) & SDL_WINDOW_FULLSCREEN ) || ( parms.fullScreen == -1 ) ? parms.fullScreen : 0;
Expand Down Expand Up @@ -371,9 +396,10 @@ static int ScreenParmsHandleDisplayIndex( glimpParms_t parms )
int windowPosX = parms.x, windowPosY = parms.y;
if( displayIdx < 0 )
{
// SRS - window is out of bounds for desktop, reposition onto default display
windowPosX = windowPosY = SDL_WINDOWPOS_UNDEFINED;
// SRS - window is out of bounds for desktop, reposition onto primary display
displayIdx = 0;
windowPosX = windowPosY = SDL_WINDOWPOS_CENTERED;
common->Warning( "Window position out of bounds, falling back to primary display" );
}

// move window to the specified desktop position
Expand Down Expand Up @@ -434,8 +460,9 @@ static bool SetScreenParmsWindowed( glimpParms_t parms )
int windowPosX = parms.x, windowPosY = parms.y;
if( GetDisplayIndex( parms ) < 0 )
{
// SRS - window is out of bounds for desktop, reposition onto default display
windowPosX = windowPosY = SDL_WINDOWPOS_UNDEFINED;
// SRS - window is out of bounds for desktop, reposition onto primary display
windowPosX = windowPosY = SDL_WINDOWPOS_CENTERED;
common->Warning( "Window position out of bounds, falling back to primary display" );
}

// SRS - handle differences in WM behaviour: for macOS set position first, for linux set it last
Expand Down Expand Up @@ -496,8 +523,10 @@ bool VKimp_SetScreenParms( glimpParms_t parms )

glConfig.isFullscreen = parms.fullScreen;
glConfig.isStereoPixelFormat = parms.stereo;
glConfig.nativeScreenWidth = parms.width;
glConfig.nativeScreenHeight = parms.height;

// SRS - Get window's client area dimensions to set new render size
SDL_GetWindowSize( window, &glConfig.nativeScreenWidth, &glConfig.nativeScreenHeight );

glConfig.displayFrequency = GetDisplayFrequency( parms );
glConfig.multisamples = parms.multiSamples;

Expand Down
61 changes: 31 additions & 30 deletions neo/sys/win32/win_glimp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -659,7 +659,7 @@ static bool GLW_GetWindowDimensions( const glimpParms_t parms, int& x, int& y, i
{
displayNotFound = true;
displayNum = DisplayPrimary();
idLib::Printf( "Can't find display for specified window position, falling back to display %i\n", displayNum + 1 );
common->Warning( "Window position out of bounds, falling back to primary display" );
}

// get the current monitor position and size on the desktop, assuming
Expand Down Expand Up @@ -802,19 +802,17 @@ bool DeviceManager::CreateWindowDeviceAndSwapChain( const glimpParms_t& parms, c
return false;
}

// SRS - For fullscreen borderless windowed mode == -2 need to use actual display dimensions
if( parms.fullScreen == -2 )
// SRS - Get window's client area dimensions to set initial swapchain size
RECT rect;
if( !GetClientRect( win32.hWnd, &rect ) )
{
m_DeviceParams.backBufferWidth = w;
m_DeviceParams.backBufferHeight = h;
}
// otherwise use parms
else
{
m_DeviceParams.backBufferWidth = parms.width;
m_DeviceParams.backBufferHeight = parms.height;
common->Printf( "^3GLW_CreateWindow() - GetClientRect() failed^0\n" );
return false;
}

m_DeviceParams.backBufferWidth = rect.right - rect.left;
m_DeviceParams.backBufferHeight = rect.bottom - rect.top;

// RB
m_DeviceParams.backBufferSampleCount = parms.multiSamples;
m_DeviceParams.vsyncEnabled = m_RequestedVSync;
Expand Down Expand Up @@ -851,6 +849,10 @@ void DeviceManager::UpdateWindowSize( const glimpParms_t& parms )
m_DeviceParams.vsyncEnabled = m_RequestedVSync;

ResizeSwapChain();

// SRS - Get actual swapchain dimensions to set new render size
deviceManager->GetWindowDimensions( glConfig.nativeScreenWidth, glConfig.nativeScreenHeight );

BackBufferResized();
}
else
Expand Down Expand Up @@ -1129,22 +1131,23 @@ bool GLimp_Init( glimpParms_t parms )
glConfig.isFullscreen = parms.fullScreen;
glConfig.isStereoPixelFormat = parms.stereo;

// SRS - For fullscreen borderless windowed mode == -2 need to use actual display dimensions
if( parms.fullScreen == -2 )
if( parms.fullScreen )
{
int x, y, w, h;
if( !GLW_GetWindowDimensions( parms, x, y, w, h ) )
// SRS - Get window's client area dimensions to set initial render size for fullscreen modes
RECT rect;
if( !GetClientRect( win32.hWnd, &rect ) )
{
common->Printf( "^3GLimp_Init() - GetClientRect() failed^0\n" );
return false;
}
glConfig.nativeScreenWidth = w;
glConfig.nativeScreenHeight = h;

glConfig.nativeScreenWidth = rect.right - rect.left;
glConfig.nativeScreenHeight = rect.bottom - rect.top;
}
// otherwise use parms
else
{
glConfig.nativeScreenWidth = parms.width;
glConfig.nativeScreenHeight = parms.height;
// SRS - Get actual swapchain dimensions to set initial render size for windowed mode
deviceManager->GetWindowDimensions( glConfig.nativeScreenWidth, glConfig.nativeScreenHeight );
}

glConfig.displayFrequency = GetDisplayFrequency( parms );
Expand Down Expand Up @@ -1219,19 +1222,17 @@ bool GLimp_SetScreenParms( glimpParms_t parms )

glConfig.isStereoPixelFormat = parms.stereo;

// SRS - For fullscreen borderless windowed mode == -2 need to use actual display dimensions
if( parms.fullScreen == -2 )
{
glConfig.nativeScreenWidth = w;
glConfig.nativeScreenHeight = h;
}
// otherwise use parms
else
// SRS - Get window's client area dimensions to set new render size
RECT rect;
if( !GetClientRect( win32.hWnd, &rect ) )
{
glConfig.nativeScreenWidth = parms.width;
glConfig.nativeScreenHeight = parms.height;
common->Printf( "^3GLimp_SetScreenParms() - GetClientRect() failed^0\n" );
return false;
}

glConfig.nativeScreenWidth = rect.right - rect.left;
glConfig.nativeScreenHeight = rect.bottom - rect.top;

glConfig.displayFrequency = GetDisplayFrequency( parms );
glConfig.multisamples = parms.multiSamples;

Expand Down