diff --git a/neo/sys/DeviceManager.h b/neo/sys/DeviceManager.h index 0cc238cd..7cfd8e34 100644 --- a/neo/sys/DeviceManager.h +++ b/neo/sys/DeviceManager.h @@ -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; diff --git a/neo/sys/DeviceManager_VK.cpp b/neo/sys/DeviceManager_VK.cpp index 5d084155..b46cb80c 100644 --- a/neo/sys/DeviceManager_VK.cpp +++ b/neo/sys/DeviceManager_VK.cpp @@ -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 @@ -280,8 +282,10 @@ private: 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: @@ -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 || @@ -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 ) @@ -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; } @@ -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 @@ -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 uniqueQueues = @@ -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 ) @@ -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 ); diff --git a/neo/sys/sdl/sdl_vkimp.cpp b/neo/sys/sdl/sdl_vkimp.cpp index 64586a75..b89aa28c 100755 --- a/neo/sys/sdl/sdl_vkimp.cpp +++ b/neo/sys/sdl/sdl_vkimp.cpp @@ -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 @@ -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; @@ -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; @@ -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 ) ) @@ -280,7 +293,7 @@ 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() ); @@ -288,16 +301,28 @@ bool VKimp_Init( glimpParms_t parms ) } 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; @@ -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 @@ -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 @@ -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; diff --git a/neo/sys/win32/win_glimp.cpp b/neo/sys/win32/win_glimp.cpp index d5275caa..e5c7ce6d 100755 --- a/neo/sys/win32/win_glimp.cpp +++ b/neo/sys/win32/win_glimp.cpp @@ -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 @@ -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; @@ -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 @@ -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 ); @@ -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 ) + // SRS - Get window's client area dimensions to set new render size + RECT rect; + if( !GetClientRect( win32.hWnd, &rect ) ) { - glConfig.nativeScreenWidth = w; - glConfig.nativeScreenHeight = h; - } - // otherwise use parms - else - { - 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;