Improve timer stats, get / set displayFrequency, implement Vulkan vsync mode 1, fix DX12 vsync mode 0 for fullscreen

This commit is contained in:
Stephen Saunders 2022-11-17 23:42:06 -05:00
parent 8d57d5399b
commit afacd8014c
6 changed files with 131 additions and 55 deletions

View file

@ -220,7 +220,7 @@ extern bool R_UseTemporalAA();
#define FPS_FRAMES_HISTORY 90
float idConsoleLocal::DrawFPS( float y )
{
extern idCVar com_smp;
extern idCVar r_swapInterval;
static float previousTimes[FPS_FRAMES];
static float previousTimesNormalized[FPS_FRAMES_HISTORY];
@ -288,8 +288,6 @@ float idConsoleLocal::DrawFPS( float y )
const uint64 rendererBackEndTime = commonLocal.GetRendererBackEndMicroseconds();
const uint64 rendererShadowsTime = commonLocal.GetRendererShadowsMicroseconds();
// SRS - GPU idle time calculation depends on whether game is operating in smp mode or not
const uint64 rendererGPUIdleTime = commonLocal.GetRendererIdleMicroseconds() - ( com_smp.GetInteger() > 0 && com_editors == 0 ? 0 : gameThreadTotalTime );
const uint64 rendererGPUTime = commonLocal.GetRendererGPUMicroseconds();
const uint64 rendererGPUEarlyZTime = commonLocal.GetRendererGpuEarlyZMicroseconds();
const uint64 rendererGPU_SSAOTime = commonLocal.GetRendererGpuSSAOMicroseconds();
@ -300,15 +298,20 @@ float idConsoleLocal::DrawFPS( float y )
const uint64 rendererGPUShaderPassesTime = commonLocal.GetRendererGpuShaderPassMicroseconds();
const uint64 rendererGPU_TAATime = commonLocal.GetRendererGpuTAAMicroseconds();
const uint64 rendererGPUPostProcessingTime = commonLocal.GetRendererGpuPostProcessingMicroseconds();
const int maxTime = int( 1000 / com_engineHz_latched ) * 1000;
// SRS - Get GPU sync time at the start of a frame (com_smp = 1 or 0) and at the end of a frame (com_smp = -1)
const uint64 rendererStartFrameSyncTime = commonLocal.GetRendererStartFrameSyncMicroseconds();
const uint64 rendererEndFrameSyncTime = commonLocal.GetRendererEndFrameSyncMicroseconds();
// SRS - Calculate max fps and max frame time based on glConfig.displayFrequency if vsync enabled and lower than engine Hz, otherwise use com_engineHz_latched
const int max_FPS = ( r_swapInterval.GetInteger() > 0 && glConfig.displayFrequency > 0 ? std::min( glConfig.displayFrequency, int( com_engineHz_latched ) ) : com_engineHz_latched );
const int maxTime = 1000.0 / max_FPS * 1000;
// SRS - Total CPU and Frame time calculations depend on whether game is operating in smp mode or not
const uint64 totalCPUTime = ( com_smp.GetInteger() > 0 && com_editors == 0 ? std::max( gameThreadTotalTime, rendererBackEndTime ) : gameThreadTotalTime + rendererBackEndTime );
const uint64 totalFrameTime = ( com_smp.GetInteger() > 0 && com_editors == 0 ? std::max( gameThreadTotalTime, rendererEndFrameSyncTime ) : gameThreadTotalTime + rendererEndFrameSyncTime ) + rendererStartFrameSyncTime;
// SRS - Frame idle and busy time calculations are based on direct frame-over-frame measurement relative to finishSyncTime
const uint64 frameIdleTime = commonLocal.mainFrameTiming.startGameTime - commonLocal.mainFrameTiming.finishSyncTime;
const uint64 frameBusyTime = commonLocal.frameTiming.finishSyncTime - commonLocal.mainFrameTiming.startGameTime;
// SRS - Frame sync time represents swap buffer synchronization + game thread wait + other time spent outside of rendering
const uint64 frameSyncTime = commonLocal.frameTiming.finishSyncTime - commonLocal.mainFrameTiming.finishRenderTime;
// SRS - GPU idle time is simply the difference between measured frame-over-frame time and GPU busy time (directly from GPU timers)
const uint64 rendererGPUIdleTime = frameBusyTime + frameIdleTime - rendererGPUTime;
#if 1
@ -476,7 +479,7 @@ float idConsoleLocal::DrawFPS( float y )
}
else
{
ImGui::TextColored( fps < com_engineHz_latched ? colorRed : colorYellow, "Average FPS %i", fps );
ImGui::TextColored( fps < max_FPS ? colorRed : colorYellow, "Average FPS %i", fps );
}
ImGui::Spacing();
@ -486,14 +489,13 @@ float idConsoleLocal::DrawFPS( float y )
ImGui::TextColored( gameThreadGameTime > maxTime ? colorRed : colorWhite, "Game: %5llu us SSAO: %5llu us", gameThreadGameTime, rendererGPU_SSAOTime );
ImGui::TextColored( gameThreadRenderTime > maxTime ? colorRed : colorWhite, "RF: %5llu us SSR: %5llu us", gameThreadRenderTime, rendererGPU_SSRTime );
ImGui::TextColored( rendererBackEndTime > maxTime ? colorRed : colorWhite, "RB: %5llu us Ambient Pass: %5llu us", rendererBackEndTime, rendererGPUAmbientPassTime );
ImGui::TextColored( rendererGPUShadowAtlasTime > maxTime ? colorRed : colorWhite, " Shadow Atlas: %5llu us", rendererGPUShadowAtlasTime );
ImGui::TextColored( rendererShadowsTime > maxTime ? colorRed : colorWhite, "Shadows: %5llu us Interactions: %5llu us", rendererShadowsTime, rendererGPUInteractionsTime );
ImGui::TextColored( rendererGPUShadowAtlasTime > maxTime ? colorRed : colorWhite, "Shadows: %5llu us Shadow Atlas: %5llu us", rendererShadowsTime, rendererGPUShadowAtlasTime );
ImGui::TextColored( rendererGPUInteractionsTime > maxTime ? colorRed : colorWhite, "Sync: %5llu us Interactions: %5llu us", frameSyncTime, rendererGPUInteractionsTime );
ImGui::TextColored( rendererGPUShaderPassesTime > maxTime ? colorRed : colorWhite, " Shader Pass: %5llu us", rendererGPUShaderPassesTime );
ImGui::TextColored( rendererGPU_TAATime > maxTime ? colorRed : colorWhite, " TAA: %5llu us", rendererGPU_TAATime );
ImGui::TextColored( rendererGPUPostProcessingTime > maxTime ? colorRed : colorWhite, " PostFX: %5llu us", rendererGPUPostProcessingTime );
ImGui::TextColored( totalCPUTime > maxTime || rendererGPUTime > maxTime ? colorRed : colorWhite,
"Total: %5llu us Total: %5llu us", totalCPUTime, rendererGPUTime );
ImGui::TextColored( totalFrameTime > maxTime ? colorRed : colorWhite, "Frame: %5llu us Idle: %5llu us", totalFrameTime, rendererGPUIdleTime );
ImGui::TextColored( frameBusyTime > maxTime || rendererGPUTime > maxTime ? colorRed : colorWhite, "Total: %5llu us Total: %5llu us", frameBusyTime, rendererGPUTime );
ImGui::TextColored( colorWhite, "Idle: %5llu us Idle: %5llu us", frameIdleTime, rendererGPUIdleTime );
ImGui::End();
}

View file

@ -6664,7 +6664,7 @@ void idRenderBackend::ExecuteBackEndCommands( const emptyCommand_t* cmds )
// SRS - Restore timestamp capture state after overlay GUI rendering is finished
glConfig.timerQueryAvailable = timerQueryAvailable;
renderLog.CloseBlock();
renderLog.CloseMainBlock();
renderLog.CloseMainBlock( MRB_DRAW_GUI );
}
else
{

View file

@ -613,21 +613,14 @@ void DeviceManager_DX12::Present()
UINT presentFlags = 0;
if( r_swapInterval.GetInteger() == 1 )
{
SetVsyncEnabled( false );
}
else if( r_swapInterval.GetInteger() == 2 )
{
SetVsyncEnabled( true );
}
if( !deviceParms.vsyncEnabled && !glConfig.isFullscreen && m_TearingSupported && r_swapInterval.GetInteger() == 0 )
// SRS - DXGI docs say fullscreen must be disabled for unlocked fps/tear, but this does not seem to be true
if( !deviceParms.vsyncEnabled && m_TearingSupported ) //&& !glConfig.isFullscreen )
{
presentFlags |= DXGI_PRESENT_ALLOW_TEARING;
}
m_SwapChain->Present( deviceParms.vsyncEnabled ? 1 : 0, presentFlags );
// SRS - Don't change deviceParms.vsyncEnabled here, simply test for vsync mode 2 to set DXGI SyncInterval
m_SwapChain->Present( deviceParms.vsyncEnabled && r_swapInterval.GetInteger() == 2 ? 1 : 0, presentFlags );
m_FrameFence->SetEventOnCompletion( m_FrameCount, m_FrameFenceEvents[bufferIndex] );
m_GraphicsQueue->Signal( m_FrameFence, m_FrameCount );

View file

@ -258,6 +258,9 @@ private:
std::queue<nvrhi::EventQueryHandle> m_FramesInFlight;
std::vector<nvrhi::EventQueryHandle> m_QueryPool;
// SRS - flag indicating support for eFifoRelaxed surface presentation (r_swapInterval = 1) mode
bool enablePModeFifoRelaxed = false;
private:
static VKAPI_ATTR VkBool32 VKAPI_CALL vulkanDebugCallback(
@ -571,6 +574,14 @@ 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() ) )
{
// can't find the required surface present modes
errorStream << std::endl << " - does not support the requested surface present modes";
deviceIsGood = false;
}
if( !findQueueFamilies( dev, m_WindowSurface ) )
{
// device doesn't have all the queue families we need
@ -865,13 +876,17 @@ bool DeviceManager_VK::createDevice()
// SRS - Determine if preferred image depth/stencil format D24S8 is supported (issue with Vulkan on AMD GPUs)
vk::ImageFormatProperties imageFormatProperties;
const vk::Result ret = m_VulkanPhysicalDevice.getImageFormatProperties( vk::Format( VK_FORMAT_D24_UNORM_S8_UINT ),
vk::ImageType( VK_IMAGE_TYPE_2D ),
vk::ImageTiling( VK_IMAGE_TILING_OPTIMAL ),
vk::ImageUsageFlags( VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT ),
const vk::Result ret = m_VulkanPhysicalDevice.getImageFormatProperties( vk::Format::eD24UnormS8Uint,
vk::ImageType::e2D,
vk::ImageTiling::eOptimal,
vk::ImageUsageFlags( vk::ImageUsageFlagBits::eDepthStencilAttachment ),
vk::ImageCreateFlags( 0 ),
&imageFormatProperties );
deviceParms.enableImageFormatD24S8 = ( ret == vk::Result::eSuccess );
// SRS - Determine if "smart" (r_swapInterval = 1) vsync mode eFifoRelaxed is supported by device and surface
auto surfacePModes = m_VulkanPhysicalDevice.getSurfacePresentModesKHR( m_WindowSurface );
enablePModeFifoRelaxed = find( surfacePModes.begin(), surfacePModes.end(), vk::PresentModeKHR::eFifoRelaxed ) != surfacePModes.end();
// stash the renderer string
auto prop = m_VulkanPhysicalDevice.getProperties();
@ -971,7 +986,7 @@ bool DeviceManager_VK::createSwapChain()
.setPQueueFamilyIndices( enableSwapChainSharing ? queues.data() : nullptr )
.setPreTransform( vk::SurfaceTransformFlagBitsKHR::eIdentity )
.setCompositeAlpha( vk::CompositeAlphaFlagBitsKHR::eOpaque )
.setPresentMode( deviceParms.vsyncEnabled ? vk::PresentModeKHR::eFifo : vk::PresentModeKHR::eImmediate )
.setPresentMode( deviceParms.vsyncEnabled ? ( r_swapInterval.GetInteger() == 2 || !enablePModeFifoRelaxed ? vk::PresentModeKHR::eFifo : vk::PresentModeKHR::eFifoRelaxed ) : vk::PresentModeKHR::eImmediate )
.setClipped( true )
.setOldSwapchain( nullptr );

View file

@ -188,6 +188,18 @@ void VKimp_PreInit() // DG: added this function for SDL compatibility
}
}
// SRS - Function to get display frequency of monitor hosting the current window
static int GetDisplayFrequency( glimpParms_t parms )
{
SDL_DisplayMode m = {0};
if( SDL_GetWindowDisplayMode( window, &m ) < 0 )
{
common->Warning( "Couldn't get display refresh rate, reason: %s", SDL_GetError() );
return parms.displayHz;
}
return m.refresh_rate;
}
/* Eric: Is the majority of this function not needed since switching from GL to Vulkan?
===================
@ -344,6 +356,19 @@ bool VKimp_Init( glimpParms_t parms )
continue;
}
// SRS - Make sure display is set to requested refresh rate from the start
if( parms.displayHz > 0 && parms.displayHz != GetDisplayFrequency( parms ) )
{
SDL_DisplayMode m = {0};
SDL_GetWindowDisplayMode( window, &m );
m.refresh_rate = parms.displayHz;
if( SDL_SetWindowDisplayMode( window, &m ) < 0 )
{
common->Warning( "Couldn't set display refresh rate to %i Hz", parms.displayHz );
}
}
// RB begin
SDL_GetWindowSize( window, &glConfig.nativeScreenWidth, &glConfig.nativeScreenHeight );
// RB end
@ -360,7 +385,7 @@ bool VKimp_Init( glimpParms_t parms )
#endif
// RB begin
glConfig.displayFrequency = 60; // FIXME: should use parms.displayHz and set mode correctly from start
glConfig.displayFrequency = GetDisplayFrequency( parms );
glConfig.isStereoPixelFormat = parms.stereo;
glConfig.multisamples = parms.multiSamples;
@ -474,8 +499,10 @@ static bool SetScreenParmsFullscreen( glimpParms_t parms )
static bool SetScreenParmsWindowed( glimpParms_t parms )
{
SDL_SetWindowSize( window, parms.width, parms.height );
// SRS - handle differences in WM behaviour: for macOS set position first, for linux set it last
#if defined(__APPLE__)
SDL_SetWindowPosition( window, parms.x, parms.y );
#endif
// if we're currently in fullscreen mode, we need to disable that
if( SDL_GetWindowFlags( window ) & SDL_WINDOW_FULLSCREEN )
@ -486,6 +513,17 @@ static bool SetScreenParmsWindowed( glimpParms_t parms )
return false;
}
}
SDL_SetWindowSize( window, parms.width, parms.height );
// SRS - this logic prevents window position drift on linux when coming in and out of fullscreen
#if defined(__linux__)
SDL_bool borderState = SDL_GetWindowFlags( window ) & SDL_WINDOW_BORDERLESS ? SDL_FALSE : SDL_TRUE;
SDL_SetWindowBordered( window, SDL_FALSE );
SDL_SetWindowPosition( window, parms.x, parms.y );
SDL_SetWindowBordered( window, borderState );
#endif
return true;
}
@ -522,7 +560,7 @@ bool VKimp_SetScreenParms( glimpParms_t parms )
glConfig.isStereoPixelFormat = parms.stereo;
glConfig.nativeScreenWidth = parms.width;
glConfig.nativeScreenHeight = parms.height;
glConfig.displayFrequency = parms.displayHz;
glConfig.displayFrequency = GetDisplayFrequency( parms );
glConfig.multisamples = parms.multiSamples;
return true;

View file

@ -694,6 +694,8 @@ GetDisplayCoordinates
*/
static bool GetDisplayCoordinates( const int deviceNum, int& x, int& y, int& width, int& height, int& displayHz )
{
bool verbose = false;
idStr deviceName = GetDeviceName( deviceNum );
if( deviceName.Length() == 0 )
{
@ -729,24 +731,27 @@ static bool GetDisplayCoordinates( const int deviceNum, int& x, int& y, int& wid
return false;
}
common->Printf( "display device: %i\n", deviceNum );
common->Printf( " DeviceName : %s\n", device.DeviceName );
common->Printf( " DeviceString: %s\n", device.DeviceString );
common->Printf( " StateFlags : 0x%x\n", device.StateFlags );
common->Printf( " DeviceID : %s\n", device.DeviceID );
common->Printf( " DeviceKey : %s\n", device.DeviceKey );
common->Printf( " DeviceName : %s\n", monitor.DeviceName );
common->Printf( " DeviceString: %s\n", monitor.DeviceString );
common->Printf( " StateFlags : 0x%x\n", monitor.StateFlags );
common->Printf( " DeviceID : %s\n", monitor.DeviceID );
common->Printf( " DeviceKey : %s\n", monitor.DeviceKey );
common->Printf( " dmPosition.x : %i\n", devmode.dmPosition.x );
common->Printf( " dmPosition.y : %i\n", devmode.dmPosition.y );
common->Printf( " dmBitsPerPel : %i\n", devmode.dmBitsPerPel );
common->Printf( " dmPelsWidth : %i\n", devmode.dmPelsWidth );
common->Printf( " dmPelsHeight : %i\n", devmode.dmPelsHeight );
common->Printf( " dmDisplayFlags : 0x%x\n", devmode.dmDisplayFlags );
common->Printf( " dmDisplayFrequency: %i\n", devmode.dmDisplayFrequency );
if( verbose )
{
common->Printf("display device: %i\n", deviceNum);
common->Printf(" DeviceName : %s\n", device.DeviceName);
common->Printf(" DeviceString: %s\n", device.DeviceString);
common->Printf(" StateFlags : 0x%x\n", device.StateFlags);
common->Printf(" DeviceID : %s\n", device.DeviceID);
common->Printf(" DeviceKey : %s\n", device.DeviceKey);
common->Printf(" DeviceName : %s\n", monitor.DeviceName);
common->Printf(" DeviceString: %s\n", monitor.DeviceString);
common->Printf(" StateFlags : 0x%x\n", monitor.StateFlags);
common->Printf(" DeviceID : %s\n", monitor.DeviceID);
common->Printf(" DeviceKey : %s\n", monitor.DeviceKey);
common->Printf(" dmPosition.x : %i\n", devmode.dmPosition.x);
common->Printf(" dmPosition.y : %i\n", devmode.dmPosition.y);
common->Printf(" dmBitsPerPel : %i\n", devmode.dmBitsPerPel);
common->Printf(" dmPelsWidth : %i\n", devmode.dmPelsWidth);
common->Printf(" dmPelsHeight : %i\n", devmode.dmPelsHeight);
common->Printf(" dmDisplayFlags : 0x%x\n", devmode.dmDisplayFlags);
common->Printf(" dmDisplayFrequency: %i\n", devmode.dmDisplayFrequency);
}
x = devmode.dmPosition.x;
y = devmode.dmPosition.y;
@ -1405,6 +1410,28 @@ void GLimp_PreInit()
// DG: not needed on this platform, so just do nothing
}
// SRS - Function to get display frequency of monitor hosting the current window
static int GetDisplayFrequency( glimpParms_t parms )
{
HMONITOR hMonitor = MonitorFromWindow( win32.hWnd, MONITOR_DEFAULTTOPRIMARY );
MONITORINFOEX minfo;
minfo.cbSize = sizeof( minfo );
if( !GetMonitorInfo( hMonitor, &minfo ) )
{
return parms.displayHz;
}
DEVMODE devmode;
devmode.dmSize = sizeof( devmode );
if( !EnumDisplaySettings( minfo.szDevice, ENUM_CURRENT_SETTINGS, &devmode ) )
{
return parms.displayHz;
}
return devmode.dmDisplayFrequency;
}
/*
===================
GLimp_Init
@ -1485,6 +1512,7 @@ bool GLimp_Init( glimpParms_t parms )
glConfig.isStereoPixelFormat = parms.stereo;
glConfig.nativeScreenWidth = parms.width;
glConfig.nativeScreenHeight = parms.height;
glConfig.displayFrequency = GetDisplayFrequency( parms );
glConfig.multisamples = parms.multiSamples;
glConfig.pixelAspect = 1.0f; // FIXME: some monitor modes may be distorted
@ -1571,7 +1599,7 @@ bool GLimp_SetScreenParms( glimpParms_t parms )
glConfig.isStereoPixelFormat = parms.stereo;
glConfig.nativeScreenWidth = parms.width;
glConfig.nativeScreenHeight = parms.height;
glConfig.displayFrequency = parms.displayHz;
glConfig.displayFrequency = GetDisplayFrequency( parms );
glConfig.multisamples = parms.multiSamples;
return true;