diff --git a/neo/CMakeLists.txt b/neo/CMakeLists.txt index d67e8b80..6f25d914 100644 --- a/neo/CMakeLists.txt +++ b/neo/CMakeLists.txt @@ -852,6 +852,16 @@ file(GLOB RAPIDJSON_INCLUDES libs/rapidjson/include/rapidjson/*.h) file(GLOB SYS_INCLUDES sys/*.h) file(GLOB SYS_SOURCES sys/*.cpp) +if(NOT USE_DX12) + get_filename_component(devicemanager_dx12_cpp_full_path ${CMAKE_CURRENT_SOURCE_DIR}/sys/DeviceManager_DX12.cpp ABSOLUTE) + list(REMOVE_ITEM SYS_SOURCES "${devicemanager_dx12_cpp_full_path}") +endif() + +if(NOT USE_NVRHI_VULKAN) + get_filename_component(devicemanager_vk_cpp_full_path ${CMAKE_CURRENT_SOURCE_DIR}/sys/DeviceManager_VK.cpp ABSOLUTE) + list(REMOVE_ITEM SYS_SOURCES "${devicemanager_vk_cpp_full_path}") +endif() + file(GLOB UI_INCLUDES ui/*.h) file(GLOB UI_SOURCES ui/*.cpp) @@ -1104,14 +1114,6 @@ set(WIN32_SOURCES sys/win32/win_taskkeyhook.cpp sys/win32/win_wndproc.cpp) -if(USE_DX12) - list(APPEND WIN32_SOURCES sys/win32/DeviceManager_DX12.cpp) -endif() - -if(USE_NVRHI_VULKAN) - list(APPEND WIN32_SOURCES sys/win32/DeviceManager_VK.cpp) -endif() - if(MSVC) list(APPEND WIN32_SOURCES sys/win32/win_cpu.cpp) endif() @@ -1206,8 +1208,6 @@ if(UNIX) list(REMOVE_ITEM SYS_INCLUDES "${devicemanager_h_full_path}") get_filename_component(devicemanager_cpp_full_path ${CMAKE_CURRENT_SOURCE_DIR}/sys/DeviceManager.cpp ABSOLUTE) list(REMOVE_ITEM SYS_SOURCES "${devicemanager_cpp_full_path}") - get_filename_component(devicemanager_vk_cpp_full_path ${CMAKE_CURRENT_SOURCE_DIR}/sys/sdl/DeviceManager_VK.cpp ABSOLUTE) - list(REMOVE_ITEM SDL_SOURCES "${devicemanager_vk_cpp_full_path}") endif() endif() @@ -1881,7 +1881,7 @@ else() list(APPEND Vulkan_LIBRARIES glslang-default-resource-limits) endif() endif() - elseif(USE_NVRHI) + elseif(USE_NVRHI_VULKAN) list(APPEND RBDOOM3_INCLUDES ${RENDERER_NVRHI_INCLUDES}) list(APPEND RBDOOM3_SOURCES ${RENDERER_NVRHI_SOURCES}) diff --git a/neo/framework/Console.cpp b/neo/framework/Console.cpp index 1e856cbf..6a39ce64 100644 --- a/neo/framework/Console.cpp +++ b/neo/framework/Console.cpp @@ -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(); } diff --git a/neo/renderer/NVRHI/Image_NVRHI.cpp b/neo/renderer/NVRHI/Image_NVRHI.cpp index 075de7ef..0c739692 100644 --- a/neo/renderer/NVRHI/Image_NVRHI.cpp +++ b/neo/renderer/NVRHI/Image_NVRHI.cpp @@ -286,7 +286,15 @@ void idImage::AllocImage() break; case FMT_DEPTH_STENCIL: - format = nvrhi::Format::D24S8; + // SRS - Check if D24S8 is supported, otherwise fall back to D32S8 + if( deviceManager->deviceParms.enableImageFormatD24S8 ) + { + format = nvrhi::Format::D24S8; + } + else + { + format = nvrhi::Format::D32S8; + } break; case FMT_SHADOW_ARRAY: diff --git a/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp b/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp index 24d5d122..e265f7a5 100644 --- a/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp +++ b/neo/renderer/NVRHI/RenderBackend_NVRHI.cpp @@ -192,7 +192,6 @@ void idRenderBackend::Init() // RB: FIXME but for now disable it to avoid validation errors if( deviceManager->GetGraphicsAPI() == nvrhi::GraphicsAPI::VULKAN ) { - glConfig.timerQueryAvailable = false; r_useSSAO.SetBool( false ); } } @@ -1711,6 +1710,13 @@ void idRenderBackend::CheckCVars() R_SetColorMappings(); } + // SRS - support dynamic changes to vsync setting + if( r_swapInterval.IsModified() ) + { + r_swapInterval.ClearModified(); + deviceManager->SetVsyncEnabled( r_swapInterval.GetBool() ); + } + // filtering /*if( r_maxAnisotropicFiltering.IsModified() || r_useTrilinearFiltering.IsModified() || r_lodBias.IsModified() ) { @@ -2145,6 +2151,13 @@ idRenderBackend::ResizeImages */ void idRenderBackend::ResizeImages() { + glimpParms_t parms; + + parms.width = glConfig.nativeScreenWidth; + parms.height = glConfig.nativeScreenHeight; + parms.multiSamples = glConfig.multisamples; + + deviceManager->UpdateWindowSize( parms ); } void idRenderBackend::SetCurrentImage( idImage* image ) diff --git a/neo/renderer/RenderBackend.cpp b/neo/renderer/RenderBackend.cpp index 856115e1..d7daffb4 100644 --- a/neo/renderer/RenderBackend.cpp +++ b/neo/renderer/RenderBackend.cpp @@ -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 { diff --git a/neo/renderer/RenderLog.cpp b/neo/renderer/RenderLog.cpp index daf4be0c..41f5d5a6 100644 --- a/neo/renderer/RenderLog.cpp +++ b/neo/renderer/RenderLog.cpp @@ -324,7 +324,7 @@ idRenderLog::idRenderLog() void idRenderLog::Init() { #if defined( USE_NVRHI ) - for( int i = 0; i < MRB_TOTAL_QUERIES; i++ ) + for( int i = 0; i < MRB_TOTAL * NUM_FRAME_DATA; i++ ) { timerQueries.Append( deviceManager->GetDevice()->createTimerQuery() ); timerUsed.Append( false ); @@ -335,12 +335,7 @@ void idRenderLog::Init() void idRenderLog::Shutdown() { #if defined( USE_NVRHI ) - if( commandList ) - { - commandList.Reset(); - } - - for( int i = 0; i < MRB_TOTAL_QUERIES; i++ ) + for( int i = 0; i < MRB_TOTAL * NUM_FRAME_DATA; i++ ) { timerQueries[i].Reset(); } @@ -356,6 +351,9 @@ void idRenderLog::StartFrame( nvrhi::ICommandList* _commandList ) void idRenderLog::EndFrame() { +#if defined( USE_NVRHI ) + commandList = nullptr; +#endif } @@ -376,8 +374,11 @@ void idRenderLog::OpenMainBlock( renderLogMainBlock_t block ) int timerIndex = mainBlock + frameParity * MRB_TOTAL; - commandList->beginTimerQuery( timerQueries[ timerIndex ] ); - timerUsed[ timerIndex ] = true; + // SRS - Only issue a new start timer query if timer slot unused + if( !timerUsed[ timerIndex ] ) + { + commandList->beginTimerQuery( timerQueries[ timerIndex ] ); + } #elif defined( USE_VULKAN ) if( vkcontext.queryIndex[ vkcontext.frameParity ] >= ( NUM_TIMESTAMP_QUERIES - 1 ) ) @@ -437,7 +438,12 @@ void idRenderLog::CloseMainBlock( int _block ) int timerIndex = block + frameParity * MRB_TOTAL; - commandList->endTimerQuery( timerQueries[ timerIndex ] ); + // SRS - Only issue a new end timer query if timer slot unused + if( !timerUsed[ timerIndex ] ) + { + commandList->endTimerQuery( timerQueries[ timerIndex ] ); + timerUsed[ timerIndex ] = true; + } #elif defined( USE_VULKAN ) if( vkcontext.queryIndex[ vkcontext.frameParity ] >= ( NUM_TIMESTAMP_QUERIES - 1 ) ) @@ -448,7 +454,7 @@ void idRenderLog::CloseMainBlock( int _block ) VkCommandBuffer commandBuffer = vkcontext.commandBuffer[ vkcontext.frameParity ]; VkQueryPool queryPool = vkcontext.queryPools[ vkcontext.frameParity ]; - uint32 queryIndex = vkcontext.queryAssignedIndex[ vkcontext.frameParity ][ mainBlock * 2 + 1 ] = vkcontext.queryIndex[ vkcontext.frameParity ]++; + uint32 queryIndex = vkcontext.queryAssignedIndex[ vkcontext.frameParity ][ block * 2 + 1 ] = vkcontext.queryIndex[ vkcontext.frameParity ]++; vkCmdWriteTimestamp( commandBuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, queryPool, queryIndex ); #elif defined(__APPLE__) @@ -457,12 +463,12 @@ void idRenderLog::CloseMainBlock( int _block ) if( !r_useShadowMapping.GetBool() || glConfig.vendor != VENDOR_AMD || r_skipAMDWorkarounds.GetBool() ) { glEndQuery( GL_TIME_ELAPSED_EXT ); - glcontext.renderLogMainBlockTimeQueryIssued[ glcontext.frameParity ][ mainBlock * 2 + 1 ]++; + glcontext.renderLogMainBlockTimeQueryIssued[ glcontext.frameParity ][ block * 2 + 1 ]++; } #else - glQueryCounter( glcontext.renderLogMainBlockTimeQueryIds[ glcontext.frameParity ][ mainBlock * 2 + 1 ], GL_TIMESTAMP ); - glcontext.renderLogMainBlockTimeQueryIssued[ glcontext.frameParity ][ mainBlock * 2 + 1 ]++; + glQueryCounter( glcontext.renderLogMainBlockTimeQueryIds[ glcontext.frameParity ][ block * 2 + 1 ], GL_TIMESTAMP ); + glcontext.renderLogMainBlockTimeQueryIssued[ glcontext.frameParity ][ block * 2 + 1 ]++; #endif } } @@ -475,7 +481,7 @@ idRenderLog::FetchGPUTimers void idRenderLog::FetchGPUTimers( backEndCounters_t& pc ) { frameCounter++; - frameParity ^= 1; + frameParity = ( frameParity + 1 ) % NUM_FRAME_DATA; #if defined( USE_NVRHI ) diff --git a/neo/renderer/RenderLog.h b/neo/renderer/RenderLog.h index 5d6b81da..b8345a0e 100644 --- a/neo/renderer/RenderLog.h +++ b/neo/renderer/RenderLog.h @@ -82,8 +82,8 @@ private: uint64 frameCounter; uint32 frameParity; - idStaticList timerQueries; - idStaticList timerUsed; + idStaticList timerQueries; + idStaticList timerUsed; #endif public: diff --git a/neo/renderer/RenderProgs.cpp b/neo/renderer/RenderProgs.cpp index 2f8cedde..21676f56 100644 --- a/neo/renderer/RenderProgs.cpp +++ b/neo/renderer/RenderProgs.cpp @@ -107,7 +107,7 @@ void idRenderProgManager::Init( nvrhi::IDevice* device ) // RB: FIXME this is ugly - DOUBLECHECK this constantBuffer = device->createBuffer( nvrhi::utils::CreateVolatileConstantBufferDesc( uniforms.Allocated(), - "RenderParams", 1024 ) ); + "RenderParams", 4096 ) ); } else { diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp index 7db1f552..a2d9287c 100644 --- a/neo/renderer/RenderSystem_init.cpp +++ b/neo/renderer/RenderSystem_init.cpp @@ -2245,6 +2245,8 @@ idRenderSystemLocal::Shutdown */ void idRenderSystemLocal::Shutdown() { + extern idCVar com_smp; + common->Printf( "idRenderSystem::Shutdown()\n" ); fonts.DeleteContents(); @@ -2270,7 +2272,11 @@ void idRenderSystemLocal::Shutdown() UnbindBufferObjects(); // SRS - wait for fence to hit before freeing any resources the GPU may be using, otherwise get Vulkan validation layer errors on shutdown - backend.GL_BlockingSwapBuffers(); + // SRS - skip this step if we are in Doom 3 mode (com_smp = -1) which has already finished and presented + if( com_smp.GetInteger() != -1 ) + { + backend.GL_BlockingSwapBuffers(); + } // free the vertex cache, which should have nothing allocated now vertexCache.Shutdown(); diff --git a/neo/sys/DeviceManager.h b/neo/sys/DeviceManager.h index 09c48979..4a4be2ff 100644 --- a/neo/sys/DeviceManager.h +++ b/neo/sys/DeviceManager.h @@ -96,6 +96,9 @@ struct DeviceCreationParameters std::vector optionalVulkanLayers; std::vector ignoredVulkanValidationMessageLocations; #endif + + // SRS - Used by idImage::AllocImage() to determine if format D24S8 is supported by device (default = true) + bool enableImageFormatD24S8 = true; }; struct DefaultMessageCallback : public nvrhi::IMessageCallback @@ -131,6 +134,7 @@ public: protected: friend class idRenderBackend; + friend class idImage; void* windowInstance; void* windowHandle; @@ -216,4 +220,4 @@ private: std::string m_WindowTitle; }; -#endif \ No newline at end of file +#endif diff --git a/neo/sys/win32/DeviceManager_DX12.cpp b/neo/sys/DeviceManager_DX12.cpp similarity index 95% rename from neo/sys/win32/DeviceManager_DX12.cpp rename to neo/sys/DeviceManager_DX12.cpp index 88ccb7d3..9438d3cd 100644 --- a/neo/sys/win32/DeviceManager_DX12.cpp +++ b/neo/sys/DeviceManager_DX12.cpp @@ -24,7 +24,7 @@ #include "renderer/RenderCommon.h" #include "renderer/RenderSystem.h" -#include "../DeviceManager.h" +#include #include #include @@ -204,15 +204,6 @@ void DeviceManager_DX12::ReportLiveObjects() bool DeviceManager_DX12::CreateDeviceAndSwapChain() { - UINT windowStyle = deviceParms.startFullscreen - ? ( WS_POPUP | WS_SYSMENU | WS_VISIBLE ) - : deviceParms.startMaximized - ? ( WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_MAXIMIZE ) - : ( WS_OVERLAPPEDWINDOW | WS_VISIBLE ); - - RECT rect = { 0, 0, LONG( deviceParms.backBufferWidth ), LONG( deviceParms.backBufferHeight ) }; - AdjustWindowRect( &rect, windowStyle, FALSE ); - RefCountPtr targetAdapter; if( deviceParms.adapter ) @@ -248,12 +239,25 @@ bool DeviceManager_DX12::CreateDeviceAndSwapChain() isNvidia = IsNvDeviceID( aDesc.VendorId ); } +/* + // SRS - Don't center window here for DX12 only, instead use portable initialization in CreateWindowDeviceAndSwapChain() within win_glimp.cpp + // - Also, calling SetWindowPos() triggers a window mgr event that overwrites r_windowX / r_windowY, which may be undesirable to the user + + UINT windowStyle = deviceParms.startFullscreen + ? ( WS_POPUP | WS_SYSMENU | WS_VISIBLE ) + : deviceParms.startMaximized + ? ( WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_MAXIMIZE ) + : ( WS_OVERLAPPEDWINDOW | WS_VISIBLE ); + + RECT rect = { 0, 0, LONG( deviceParms.backBufferWidth ), LONG( deviceParms.backBufferHeight ) }; + AdjustWindowRect( &rect, windowStyle, FALSE ); if( MoveWindowOntoAdapter( targetAdapter, rect ) ) { SetWindowPos( ( HWND )windowHandle, deviceParms.startFullscreen ? HWND_TOPMOST : HWND_NOTOPMOST, rect.left, rect.top, 0, 0, SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE ); } +*/ HRESULT hr = E_FAIL; RECT clientRect; @@ -539,6 +543,8 @@ void DeviceManager_DX12::ResizeSwapChain() void DeviceManager_DX12::BeginFrame() { +/* SRS - This code not needed: framebuffer/swapchain resizing & fullscreen are handled by idRenderBackend::ResizeImages() and DeviceManager::UpdateWindowSize() + DXGI_SWAP_CHAIN_DESC1 newSwapChainDesc; DXGI_SWAP_CHAIN_FULLSCREEN_DESC newFullScreenDesc; if( SUCCEEDED( m_SwapChain->GetDesc1( &newSwapChainDesc ) ) && SUCCEEDED( m_SwapChain->GetFullscreenDesc( &newFullScreenDesc ) ) ) @@ -561,7 +567,7 @@ void DeviceManager_DX12::BeginFrame() BackBufferResized(); } } - +*/ auto bufferIndex = m_SwapChain->GetCurrentBackBufferIndex(); WaitForSingleObject( m_FrameFenceEvents[bufferIndex], INFINITE ); @@ -607,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 ); @@ -631,4 +630,4 @@ void DeviceManager_DX12::Present() DeviceManager* DeviceManager::CreateD3D12() { return new DeviceManager_DX12(); -} \ No newline at end of file +} diff --git a/neo/sys/sdl/DeviceManager_VK.cpp b/neo/sys/DeviceManager_VK.cpp similarity index 92% rename from neo/sys/sdl/DeviceManager_VK.cpp rename to neo/sys/DeviceManager_VK.cpp index f42d04e0..43961f4f 100644 --- a/neo/sys/sdl/DeviceManager_VK.cpp +++ b/neo/sys/DeviceManager_VK.cpp @@ -164,22 +164,17 @@ private: VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME }, // layers - { -#if defined(__APPLE__) && !defined( USE_MoltenVK ) - //"VK_LAYER_KHRONOS_synchronization2" // sync2 not supported natively on MoltenVK, use layer implementation instead -#endif - }, + { }, // device { VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_KHR_MAINTENANCE1_EXTENSION_NAME, #if defined(__APPLE__) +#if defined( VK_KHR_portability_subset ) VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME, +#endif VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME, VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME, -#if !defined( USE_MoltenVK ) - //VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME -#endif #endif }, }; @@ -197,14 +192,20 @@ private: VK_EXT_DEBUG_UTILS_EXTENSION_NAME }, // layers - { }, + { +#if defined(__APPLE__) + // SRS - synchronization2 not supported natively on MoltenVK, use layer implementation instead + "VK_LAYER_KHRONOS_synchronization2" +#endif + }, // device { VK_EXT_DEBUG_MARKER_EXTENSION_NAME, VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME, VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME, VK_NV_MESH_SHADER_EXTENSION_NAME, - VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME + VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME, + VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME }, }; @@ -257,6 +258,9 @@ private: std::queue m_FramesInFlight; std::vector 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( @@ -570,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 @@ -706,6 +718,7 @@ bool DeviceManager_VK::createDevice() bool rayQuerySupported = false; bool meshletsSupported = false; bool vrsSupported = false; + bool sync2Supported = false; common->Printf( "Enabled Vulkan device extensions:\n" ); for( const auto& ext : enabledExtensions.device ) @@ -737,6 +750,10 @@ bool DeviceManager_VK::createDevice() { vrsSupported = true; } + else if( ext == VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME ) + { + sync2Supported = true; + } } std::unordered_set uniqueQueueFamilies = @@ -782,7 +799,17 @@ bool DeviceManager_VK::createDevice() .setPrimitiveFragmentShadingRate( true ) .setAttachmentFragmentShadingRate( true ); + auto sync2Features = vk::PhysicalDeviceSynchronization2FeaturesKHR() + .setSynchronization2( true ); + +#if defined(__APPLE__) && defined( VK_KHR_portability_subset ) + auto portabilityFeatures = vk::PhysicalDevicePortabilitySubsetFeaturesKHR() + .setImageViewFormatSwizzle( true ); + + void* pNext = &portabilityFeatures; +#else void* pNext = nullptr; +#endif #define APPEND_EXTENSION(condition, desc) if (condition) { (desc).pNext = pNext; pNext = &(desc); } // NOLINT(cppcoreguidelines-macro-usage) APPEND_EXTENSION( accelStructSupported, accelStructFeatures ) APPEND_EXTENSION( bufferAddressSupported, bufferAddressFeatures ) @@ -790,6 +817,7 @@ bool DeviceManager_VK::createDevice() APPEND_EXTENSION( rayQuerySupported, rayQueryFeatures ) APPEND_EXTENSION( meshletsSupported, meshletFeatures ) APPEND_EXTENSION( vrsSupported, vrsFeatures ) + APPEND_EXTENSION( sync2Supported, sync2Features ) #undef APPEND_EXTENSION auto deviceFeatures = vk::PhysicalDeviceFeatures() @@ -800,6 +828,7 @@ bool DeviceManager_VK::createDevice() #if !defined(__APPLE__) .setGeometryShader( true ) #endif + .setFillModeNonSolid( true ) .setImageCubeArray( true ) .setDualSrcBlend( true ); @@ -844,7 +873,21 @@ bool DeviceManager_VK::createDevice() m_VulkanDevice.getQueue( m_PresentQueueFamily, 0, &m_PresentQueue ); VULKAN_HPP_DEFAULT_DISPATCHER.init( m_VulkanDevice ); + + // 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::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(); m_RendererString = std::string( prop.deviceName.data() ); @@ -896,19 +939,22 @@ void DeviceManager_VK::destroySwapChain() m_VulkanDevice.waitIdle(); } + while( !m_SwapChainImages.empty() ) + { + auto sci = m_SwapChainImages.back(); + m_SwapChainImages.pop_back(); + sci.rhiHandle = nullptr; + } + if( m_SwapChain ) { m_VulkanDevice.destroySwapchainKHR( m_SwapChain ); m_SwapChain = nullptr; } - - m_SwapChainImages.clear(); } bool DeviceManager_VK::createSwapChain() { - destroySwapChain(); - m_SwapChainFormat = { vk::Format( nvrhi::vulkan::convertFormat( deviceParms.swapChainFormat ) ), @@ -940,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 ); @@ -1134,7 +1180,7 @@ void DeviceManager_VK::BeginFrame() vk::Fence(), &m_SwapChainIndex ); - assert( res == vk::Result::eSuccess ); + assert( res == vk::Result::eSuccess || res == vk::Result::eSuboptimalKHR ); m_NvrhiDevice->queueWaitForSemaphore( nvrhi::CommandQueue::Graphics, m_PresentSemaphore, 0 ); } @@ -1158,7 +1204,7 @@ void DeviceManager_VK::Present() .setPImageIndices( &m_SwapChainIndex ); const vk::Result res = m_PresentQueue.presentKHR( &info ); - assert( res == vk::Result::eSuccess || res == vk::Result::eErrorOutOfDateKHR ); + assert( res == vk::Result::eSuccess || res == vk::Result::eErrorOutOfDateKHR || res == vk::Result::eSuboptimalKHR ); if( deviceParms.enableDebugRuntime ) { diff --git a/neo/sys/sdl/sdl_events.cpp b/neo/sys/sdl/sdl_events.cpp index a894309b..5451c7dd 100644 --- a/neo/sys/sdl/sdl_events.cpp +++ b/neo/sys/sdl/sdl_events.cpp @@ -970,11 +970,19 @@ sysEvent_t Sys_GetEvent() { int w = ev.window.data1; int h = ev.window.data2; - r_windowWidth.SetInteger( w ); - r_windowHeight.SetInteger( h ); + + // SRS - Only save window resized events when in windowed modes + if( !renderSystem->IsFullScreen() ) + { + r_windowWidth.SetInteger( w ); + r_windowHeight.SetInteger( h ); + } glConfig.nativeScreenWidth = w; glConfig.nativeScreenHeight = h; + + // SRS - Make sure ImGui gets new window boundaries + ImGuiHook::NotifyDisplaySizeChanged( glConfig.nativeScreenWidth, glConfig.nativeScreenHeight ); break; } @@ -982,8 +990,13 @@ sysEvent_t Sys_GetEvent() { int x = ev.window.data1; int y = ev.window.data2; - r_windowX.SetInteger( x ); - r_windowY.SetInteger( y ); + + // SRS - Only save window moved events when in windowed modes + if( !renderSystem->IsFullScreen() ) + { + r_windowX.SetInteger( x ); + r_windowY.SetInteger( y ); + } break; } } @@ -1049,7 +1062,7 @@ sysEvent_t Sys_GetEvent() case SDL_KEYDOWN: if( ev.key.keysym.sym == SDLK_RETURN && ( ev.key.keysym.mod & KMOD_ALT ) > 0 ) { - // DG: go to fullscreen on current display, instead of always first display + /* DG: go to fullscreen on current display, instead of always first display int fullscreen = 0; if( ! renderSystem->IsFullScreen() ) { @@ -1058,7 +1071,10 @@ sysEvent_t Sys_GetEvent() fullscreen = -2; } cvarSystem->SetCVarInteger( "r_fullscreen", fullscreen ); - // DG end + // DG end */ + // SRS - Until Borderless Fullscreen is implemented properly, use same implementation as on Windows + cvarSystem->SetCVarBool( "r_fullscreen", !renderSystem->IsFullScreen() ); + // SRS end PushConsoleEvent( "vid_restart" ); continue; // handle next event } diff --git a/neo/sys/sdl/sdl_vkimp.cpp b/neo/sys/sdl/sdl_vkimp.cpp index 9f295188..cf76f874 100644 --- a/neo/sys/sdl/sdl_vkimp.cpp +++ b/neo/sys/sdl/sdl_vkimp.cpp @@ -135,21 +135,17 @@ bool DeviceManager::CreateWindowDeviceAndSwapChain( const glimpParms_t& parms, c return false; } - glConfig.isFullscreen = parms.fullScreen; - - UpdateWindowSize( parms ); - return true; } -void DeviceManager::UpdateWindowSize( const glimpParms_t& params ) +void DeviceManager::UpdateWindowSize( const glimpParms_t& parms ) { windowVisible = true; - if( int( deviceParms.backBufferWidth ) != params.width || - int( deviceParms.backBufferHeight ) != params.height || + if( int( deviceParms.backBufferWidth ) != parms.width || + int( deviceParms.backBufferHeight ) != parms.height || #if ID_MSAA - int( deviceParms.backBufferSampleCount ) != params.multiSamples || + int( deviceParms.backBufferSampleCount ) != parms.multiSamples || #endif ( deviceParms.vsyncEnabled != requestedVSync && GetGraphicsAPI() == nvrhi::GraphicsAPI::VULKAN ) ) { @@ -157,16 +153,18 @@ void DeviceManager::UpdateWindowSize( const glimpParms_t& params ) BackBufferResizing(); - deviceParms.backBufferWidth = params.width; - deviceParms.backBufferHeight = params.height; - deviceParms.backBufferSampleCount = params.multiSamples; + deviceParms.backBufferWidth = parms.width; + deviceParms.backBufferHeight = parms.height; + deviceParms.backBufferSampleCount = parms.multiSamples; deviceParms.vsyncEnabled = requestedVSync; ResizeSwapChain(); BackBufferResized(); } - - deviceParms.vsyncEnabled = requestedVSync; + else + { + deviceParms.vsyncEnabled = requestedVSync; + } } #endif @@ -190,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? =================== @@ -346,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 @@ -362,7 +385,7 @@ bool VKimp_Init( glimpParms_t parms ) #endif // RB begin - glConfig.displayFrequency = 60; + glConfig.displayFrequency = GetDisplayFrequency( parms ); glConfig.isStereoPixelFormat = parms.stereo; glConfig.multisamples = parms.multiSamples; @@ -451,7 +474,7 @@ static bool SetScreenParmsFullscreen( glimpParms_t parms ) // change settings in that display mode according to parms // FIXME: check if refreshrate, width and height are supported? - // m.refresh_rate = parms.displayHz; + m.refresh_rate = parms.displayHz; m.w = parms.width; m.h = parms.height; @@ -476,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 ) @@ -488,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(__APPLE__) + 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; } @@ -524,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; diff --git a/neo/sys/win32/DeviceManager_VK.cpp b/neo/sys/win32/DeviceManager_VK.cpp deleted file mode 100644 index 5f456b0d..00000000 --- a/neo/sys/win32/DeviceManager_VK.cpp +++ /dev/null @@ -1,1225 +0,0 @@ -/* -* Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. -* -* Permission is hereby granted, free of charge, to any person obtaining a -* copy of this software and associated documentation files (the "Software"), -* to deal in the Software without restriction, including without limitation -* the rights to use, copy, modify, merge, publish, distribute, sublicense, -* and/or sell copies of the Software, and to permit persons to whom the -* Software is furnished to do so, subject to the following conditions: -* -* The above copyright notice and this permission notice shall be included in -* all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -* DEALINGS IN THE SOFTWARE. -*/ - -#include -#pragma hdrstop - -#include -#include -#include - -#include - -#include -#include - -// Define the Vulkan dynamic dispatcher - this needs to occur in exactly one cpp file in the program. -VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE - -class DeviceManager_VK : public DeviceManager -{ -public: - [[nodiscard]] nvrhi::IDevice* GetDevice() const override - { - if( m_ValidationLayer ) - { - return m_ValidationLayer; - } - - return m_NvrhiDevice; - } - - [[nodiscard]] nvrhi::GraphicsAPI GetGraphicsAPI() const override - { - return nvrhi::GraphicsAPI::VULKAN; - } - -protected: - bool CreateDeviceAndSwapChain() override; - void DestroyDeviceAndSwapChain() override; - - void ResizeSwapChain() override - { - if( m_VulkanDevice ) - { - destroySwapChain(); - createSwapChain(); - } - } - - nvrhi::ITexture* GetCurrentBackBuffer() override - { - return m_SwapChainImages[m_SwapChainIndex].rhiHandle; - } - nvrhi::ITexture* GetBackBuffer( uint32_t index ) override - { - if( index < m_SwapChainImages.size() ) - { - return m_SwapChainImages[index].rhiHandle; - } - return nullptr; - } - uint32_t GetCurrentBackBufferIndex() override - { - return m_SwapChainIndex; - } - uint32_t GetBackBufferCount() override - { - return uint32_t( m_SwapChainImages.size() ); - } - - void BeginFrame() override; - void EndFrame() override; - void Present() override; - - const char* GetRendererString() const override - { - return m_RendererString.c_str(); - } - - bool IsVulkanInstanceExtensionEnabled( const char* extensionName ) const override - { - return enabledExtensions.instance.find( extensionName ) != enabledExtensions.instance.end(); - } - - bool IsVulkanDeviceExtensionEnabled( const char* extensionName ) const override - { - return enabledExtensions.device.find( extensionName ) != enabledExtensions.device.end(); - } - - bool IsVulkanLayerEnabled( const char* layerName ) const override - { - return enabledExtensions.layers.find( layerName ) != enabledExtensions.layers.end(); - } - - void GetEnabledVulkanInstanceExtensions( std::vector& extensions ) const override - { - for( const auto& ext : enabledExtensions.instance ) - { - extensions.push_back( ext ); - } - } - - void GetEnabledVulkanDeviceExtensions( std::vector& extensions ) const override - { - for( const auto& ext : enabledExtensions.device ) - { - extensions.push_back( ext ); - } - } - - void GetEnabledVulkanLayers( std::vector& layers ) const override - { - for( const auto& ext : enabledExtensions.layers ) - { - layers.push_back( ext ); - } - } - -private: - bool createInstance(); - bool createWindowSurface(); - void installDebugCallback(); - bool pickPhysicalDevice(); - bool findQueueFamilies( vk::PhysicalDevice physicalDevice ); - bool createDevice(); - bool createSwapChain(); - void destroySwapChain(); - - struct VulkanExtensionSet - { - std::unordered_set instance; - std::unordered_set layers; - std::unordered_set device; - }; - - // minimal set of required extensions - VulkanExtensionSet enabledExtensions = - { - // instance - { - VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME - }, - // layers - { }, - // device - { - VK_KHR_SWAPCHAIN_EXTENSION_NAME, - VK_KHR_MAINTENANCE1_EXTENSION_NAME, - }, - }; - - // optional extensions - VulkanExtensionSet optionalExtensions = - { - // instance - { - VK_EXT_SAMPLER_FILTER_MINMAX_EXTENSION_NAME, - VK_EXT_DEBUG_UTILS_EXTENSION_NAME - }, - // layers - { }, - // device - { - VK_EXT_DEBUG_MARKER_EXTENSION_NAME, - VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME, - VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME, - VK_NV_MESH_SHADER_EXTENSION_NAME, - VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME - }, - }; - - std::unordered_set m_RayTracingExtensions = - { - VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME, - VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME, - VK_KHR_PIPELINE_LIBRARY_EXTENSION_NAME, - VK_KHR_RAY_QUERY_EXTENSION_NAME, - VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME - }; - - std::string m_RendererString; - - vk::Instance m_VulkanInstance; - vk::DebugReportCallbackEXT m_DebugReportCallback; - - vk::PhysicalDevice m_VulkanPhysicalDevice; - int m_GraphicsQueueFamily = -1; - int m_ComputeQueueFamily = -1; - int m_TransferQueueFamily = -1; - int m_PresentQueueFamily = -1; - - vk::Device m_VulkanDevice; - vk::Queue m_GraphicsQueue; - vk::Queue m_ComputeQueue; - vk::Queue m_TransferQueue; - vk::Queue m_PresentQueue; - - vk::SurfaceKHR m_WindowSurface; - - vk::SurfaceFormatKHR m_SwapChainFormat; - vk::SwapchainKHR m_SwapChain; - - struct SwapChainImage - { - vk::Image image; - nvrhi::TextureHandle rhiHandle; - }; - - std::vector m_SwapChainImages; - uint32_t m_SwapChainIndex = uint32_t( -1 ); - - nvrhi::vulkan::DeviceHandle m_NvrhiDevice; - nvrhi::DeviceHandle m_ValidationLayer; - - nvrhi::CommandListHandle m_BarrierCommandList; - vk::Semaphore m_PresentSemaphore; - - std::queue m_FramesInFlight; - std::vector m_QueryPool; - - -private: - static VKAPI_ATTR VkBool32 VKAPI_CALL vulkanDebugCallback( - VkDebugReportFlagsEXT flags, - VkDebugReportObjectTypeEXT objType, - uint64_t obj, - size_t location, - int32_t code, - const char* layerPrefix, - const char* msg, - void* userData ) - { - const DeviceManager_VK* manager = ( const DeviceManager_VK* )userData; - - if( manager ) - { - const auto& ignored = manager->deviceParms.ignoredVulkanValidationMessageLocations; - const auto found = std::find( ignored.begin(), ignored.end(), location ); - if( found != ignored.end() ) - { - return VK_FALSE; - } - } - - if( flags & VK_DEBUG_REPORT_ERROR_BIT_EXT ) - { - idLib::Printf( "[Vulkan] ERROR location=0x%zx code=%d, layerPrefix='%s'] %s\n", location, code, layerPrefix, msg ); - } - else if( flags & VK_DEBUG_REPORT_WARNING_BIT_EXT ) - { - idLib::Printf( "[Vulkan] WARNING location=0x%zx code=%d, layerPrefix='%s'] %s\n", location, code, layerPrefix, msg ); - } - else if( flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT ) - { - idLib::Printf( "[Vulkan] PERFORMANCE WARNING location=0x%zx code=%d, layerPrefix='%s'] %s\n", location, code, layerPrefix, msg ); - } - else if( flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT ) - { - idLib::Printf( "[Vulkan] INFO location=0x%zx code=%d, layerPrefix='%s'] %s\n", location, code, layerPrefix, msg ); - } - else if( flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT ) - { - idLib::Printf( "[Vulkan] DEBUG location=0x%zx code=%d, layerPrefix='%s'] %s\n", location, code, layerPrefix, msg ); - } - - return VK_FALSE; - } -}; - -static std::vector stringSetToVector( const std::unordered_set& set ) -{ - std::vector ret; - for( const auto& s : set ) - { - ret.push_back( s.c_str() ); - } - - return ret; -} - -template -static std::vector setToVector( const std::unordered_set& set ) -{ - std::vector ret; - for( const auto& s : set ) - { - ret.push_back( s ); - } - - return ret; -} - -bool DeviceManager_VK::createInstance() -{ - enabledExtensions.instance.insert( VK_KHR_SURFACE_EXTENSION_NAME ); - -#if defined(_WIN32) - enabledExtensions.instance.insert( VK_KHR_WIN32_SURFACE_EXTENSION_NAME ); -#elif defined(VK_USE_PLATFORM_ANDROID_KHR) - enabledExtensions.instance.insert( VK_KHR_ANDROID_SURFACE_EXTENSION_NAME ); -#elif defined(_DIRECT2DISPLAY) - enabledExtensions.instance.insert( VK_KHR_DISPLAY_EXTENSION_NAME ); -#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT) - enabledExtensions.instance.insert( VK_EXT_DIRECTFB_SURFACE_EXTENSION_NAME ); -#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) - enabledExtensions.instance.insert( VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME ); -#elif defined(VK_USE_PLATFORM_XCB_KHR) - enabledExtensions.instance.insert( VK_KHR_XCB_SURFACE_EXTENSION_NAME ); -#elif defined(VK_USE_PLATFORM_IOS_MVK) - enabledExtensions.instance.insert( VK_MVK_IOS_SURFACE_EXTENSION_NAME ); -#elif defined(VK_USE_PLATFORM_MACOS_MVK) - enabledExtensions.instance.insert( VK_MVK_MACOS_SURFACE_EXTENSION_NAME ); -#elif defined(VK_USE_PLATFORM_HEADLESS_EXT) - enabledExtensions.instance.insert( VK_EXT_HEADLESS_SURFACE_EXTENSION_NAME ); -#endif - - // Get extensions supported by the instance and store for later use - uint32_t extCount = 0; - vk::enumerateInstanceExtensionProperties( nullptr, &extCount, nullptr ); - if( extCount > 0 ) - { - idList extensions; - extensions.SetNum( extCount ); - - if( vk::enumerateInstanceExtensionProperties( nullptr, &extCount, &extensions[0] ) == vk::Result::eSuccess ) - { - for( VkExtensionProperties extension : extensions ) - { - enabledExtensions.instance.insert( extension.extensionName ); - } - } - } - - // add instance extensions requested by the user - for( const std::string& name : deviceParms.requiredVulkanInstanceExtensions ) - { - enabledExtensions.instance.insert( name ); - } - for( const std::string& name : deviceParms.optionalVulkanInstanceExtensions ) - { - optionalExtensions.instance.insert( name ); - } - - // add layers requested by the user - for( const std::string& name : deviceParms.requiredVulkanLayers ) - { - enabledExtensions.layers.insert( name ); - } - for( const std::string& name : deviceParms.optionalVulkanLayers ) - { - optionalExtensions.layers.insert( name ); - } - - std::unordered_set requiredExtensions = enabledExtensions.instance; - - // figure out which optional extensions are supported - for( const auto& instanceExt : vk::enumerateInstanceExtensionProperties() ) - { - const std::string name = instanceExt.extensionName; - if( optionalExtensions.instance.find( name ) != optionalExtensions.instance.end() ) - { - enabledExtensions.instance.insert( name ); - } - - requiredExtensions.erase( name ); - } - - if( !requiredExtensions.empty() ) - { - std::stringstream ss; - ss << "Cannot create a Vulkan instance because the following required extension(s) are not supported:"; - for( const auto& ext : requiredExtensions ) - { - ss << std::endl << " - " << ext; - } - - common->FatalError( "%s", ss.str().c_str() ); - return false; - } - - common->Printf( "Enabled Vulkan instance extensions:\n" ); - for( const auto& ext : enabledExtensions.instance ) - { - common->Printf( " %s\n", ext.c_str() ); - } - - std::unordered_set requiredLayers = enabledExtensions.layers; - - for( const auto& layer : vk::enumerateInstanceLayerProperties() ) - { - const std::string name = layer.layerName; - if( optionalExtensions.layers.find( name ) != optionalExtensions.layers.end() ) - { - enabledExtensions.layers.insert( name ); - } - - requiredLayers.erase( name ); - } - - if( !requiredLayers.empty() ) - { - std::stringstream ss; - ss << "Cannot create a Vulkan instance because the following required layer(s) are not supported:"; - for( const auto& ext : requiredLayers ) - { - ss << std::endl << " - " << ext; - } - - common->FatalError( "%s", ss.str().c_str() ); - return false; - } - - common->Printf( "Enabled Vulkan layers:\n" ); - for( const auto& layer : enabledExtensions.layers ) - { - common->Printf( " %s\n", layer.c_str() ); - } - - auto instanceExtVec = stringSetToVector( enabledExtensions.instance ); - auto layerVec = stringSetToVector( enabledExtensions.layers ); - - auto applicationInfo = vk::ApplicationInfo() - .setApiVersion( VK_MAKE_VERSION( 1, 2, 0 ) ); - - // create the vulkan instance - vk::InstanceCreateInfo info = vk::InstanceCreateInfo() - .setEnabledLayerCount( uint32_t( layerVec.size() ) ) - .setPpEnabledLayerNames( layerVec.data() ) - .setEnabledExtensionCount( uint32_t( instanceExtVec.size() ) ) - .setPpEnabledExtensionNames( instanceExtVec.data() ) - .setPApplicationInfo( &applicationInfo ); - - const vk::Result res = vk::createInstance( &info, nullptr, &m_VulkanInstance ); - if( res != vk::Result::eSuccess ) - { - common->FatalError( "Failed to create a Vulkan instance, error code = %s", nvrhi::vulkan::resultToString( res ) ); - return false; - } - - VULKAN_HPP_DEFAULT_DISPATCHER.init( m_VulkanInstance ); - - return true; -} - -void DeviceManager_VK::installDebugCallback() -{ - auto info = vk::DebugReportCallbackCreateInfoEXT() - .setFlags( vk::DebugReportFlagBitsEXT::eError | - vk::DebugReportFlagBitsEXT::eWarning | - // vk::DebugReportFlagBitsEXT::eInformation | - vk::DebugReportFlagBitsEXT::ePerformanceWarning ) - .setPfnCallback( vulkanDebugCallback ) - .setPUserData( this ); - - vk::Result res = m_VulkanInstance.createDebugReportCallbackEXT( &info, nullptr, &m_DebugReportCallback ); - assert( res == vk::Result::eSuccess ); -} - -bool DeviceManager_VK::pickPhysicalDevice() -{ - vk::Format requestedFormat = nvrhi::vulkan::convertFormat( deviceParms.swapChainFormat ); - vk::Extent2D requestedExtent( deviceParms.backBufferWidth, deviceParms.backBufferHeight ); - - auto devices = m_VulkanInstance.enumeratePhysicalDevices(); - - // Start building an error message in case we cannot find a device. - std::stringstream errorStream; - errorStream << "Cannot find a Vulkan device that supports all the required extensions and properties."; - - // build a list of GPUs - std::vector discreteGPUs; - std::vector otherGPUs; - for( const auto& dev : devices ) - { - auto prop = dev.getProperties(); - - errorStream << std::endl << prop.deviceName.data() << ":"; - - // check that all required device extensions are present - std::unordered_set requiredExtensions = enabledExtensions.device; - auto deviceExtensions = dev.enumerateDeviceExtensionProperties(); - for( const auto& ext : deviceExtensions ) - { - requiredExtensions.erase( std::string( ext.extensionName.data() ) ); - } - - bool deviceIsGood = true; - - if( !requiredExtensions.empty() ) - { - // device is missing one or more required extensions - for( const auto& ext : requiredExtensions ) - { - errorStream << std::endl << " - missing " << ext; - } - deviceIsGood = false; - } - - auto deviceFeatures = dev.getFeatures(); - if( !deviceFeatures.samplerAnisotropy ) - { - // device is a toaster oven - errorStream << std::endl << " - does not support samplerAnisotropy"; - deviceIsGood = false; - } - if( !deviceFeatures.textureCompressionBC ) - { - errorStream << std::endl << " - does not support textureCompressionBC"; - deviceIsGood = false; - } - - // check that this device supports our intended swap chain creation parameters - auto surfaceCaps = dev.getSurfaceCapabilitiesKHR( m_WindowSurface ); - auto surfaceFmts = dev.getSurfaceFormatsKHR( m_WindowSurface ); - auto surfacePModes = dev.getSurfacePresentModesKHR( m_WindowSurface ); - - if( surfaceCaps.minImageCount > deviceParms.swapChainBufferCount || - ( surfaceCaps.maxImageCount < deviceParms.swapChainBufferCount && surfaceCaps.maxImageCount > 0 ) ) - { - errorStream << std::endl << " - cannot support the requested swap chain image count:"; - errorStream << " requested " << deviceParms.swapChainBufferCount << ", available " << surfaceCaps.minImageCount << " - " << surfaceCaps.maxImageCount; - deviceIsGood = false; - } - - if( surfaceCaps.minImageExtent.width > requestedExtent.width || - surfaceCaps.minImageExtent.height > requestedExtent.height || - surfaceCaps.maxImageExtent.width < requestedExtent.width || - surfaceCaps.maxImageExtent.height < requestedExtent.height ) - { - errorStream << std::endl << " - cannot support the requested swap chain size:"; - errorStream << " requested " << requestedExtent.width << "x" << requestedExtent.height << ", "; - errorStream << " available " << surfaceCaps.minImageExtent.width << "x" << surfaceCaps.minImageExtent.height; - errorStream << " - " << surfaceCaps.maxImageExtent.width << "x" << surfaceCaps.maxImageExtent.height; - deviceIsGood = false; - } - - bool surfaceFormatPresent = false; - for( const vk::SurfaceFormatKHR& surfaceFmt : surfaceFmts ) - { - if( surfaceFmt.format == requestedFormat ) - { - surfaceFormatPresent = true; - break; - } - } - - if( !surfaceFormatPresent ) - { - // can't create a swap chain using the format requested - errorStream << std::endl << " - does not support the requested swap chain format"; - deviceIsGood = false; - } - - if( !findQueueFamilies( dev ) ) - { - // device doesn't have all the queue families we need - errorStream << std::endl << " - does not support the necessary queue types"; - deviceIsGood = false; - } - - // check that we can present from the graphics queue - uint32_t canPresent = dev.getSurfaceSupportKHR( m_GraphicsQueueFamily, m_WindowSurface ); - if( !canPresent ) - { - errorStream << std::endl << " - cannot present"; - deviceIsGood = false; - } - - if( !deviceIsGood ) - { - continue; - } - - if( prop.deviceType == vk::PhysicalDeviceType::eDiscreteGpu ) - { - discreteGPUs.push_back( dev ); - } - else - { - otherGPUs.push_back( dev ); - } - } - - // pick the first discrete GPU if it exists, otherwise the first integrated GPU - if( !discreteGPUs.empty() ) - { - m_VulkanPhysicalDevice = discreteGPUs[0]; - return true; - } - - if( !otherGPUs.empty() ) - { - m_VulkanPhysicalDevice = otherGPUs[0]; - return true; - } - - common->FatalError( "%s", errorStream.str().c_str() ); - - return false; -} - -bool DeviceManager_VK::findQueueFamilies( vk::PhysicalDevice physicalDevice ) -{ - auto props = physicalDevice.getQueueFamilyProperties(); - - for( int i = 0; i < int( props.size() ); i++ ) - { - const auto& queueFamily = props[i]; - - if( m_GraphicsQueueFamily == -1 ) - { - if( queueFamily.queueCount > 0 && - ( queueFamily.queueFlags & vk::QueueFlagBits::eGraphics ) ) - { - m_GraphicsQueueFamily = i; - } - } - - if( m_ComputeQueueFamily == -1 ) - { - if( queueFamily.queueCount > 0 && - ( queueFamily.queueFlags & vk::QueueFlagBits::eCompute ) && - !( queueFamily.queueFlags & vk::QueueFlagBits::eGraphics ) ) - { - m_ComputeQueueFamily = i; - } - } - - if( m_TransferQueueFamily == -1 ) - { - if( queueFamily.queueCount > 0 && - ( queueFamily.queueFlags & vk::QueueFlagBits::eTransfer ) && - !( queueFamily.queueFlags & vk::QueueFlagBits::eCompute ) && - !( queueFamily.queueFlags & vk::QueueFlagBits::eGraphics ) ) - { - m_TransferQueueFamily = i; - } - } - - if( m_PresentQueueFamily == -1 ) - { - if( queueFamily.queueCount > 0 && - vkGetPhysicalDeviceWin32PresentationSupportKHR( physicalDevice, i ) ) - { - m_PresentQueueFamily = i; - } - } - } - - if( m_GraphicsQueueFamily == -1 || - m_PresentQueueFamily == -1 || - ( m_ComputeQueueFamily == -1 && deviceParms.enableComputeQueue ) || - ( m_TransferQueueFamily == -1 && deviceParms.enableCopyQueue ) ) - { - return false; - } - - return true; -} - -bool DeviceManager_VK::createDevice() -{ - // figure out which optional extensions are supported - auto deviceExtensions = m_VulkanPhysicalDevice.enumerateDeviceExtensionProperties(); - for( const auto& ext : deviceExtensions ) - { - const std::string name = ext.extensionName; - if( optionalExtensions.device.find( name ) != optionalExtensions.device.end() ) - { - enabledExtensions.device.insert( name ); - } - - if( deviceParms.enableRayTracingExtensions && m_RayTracingExtensions.find( name ) != m_RayTracingExtensions.end() ) - { - enabledExtensions.device.insert( name ); - } - } - - bool accelStructSupported = false; - bool bufferAddressSupported = false; - bool rayPipelineSupported = false; - bool rayQuerySupported = false; - bool meshletsSupported = false; - bool vrsSupported = false; - - common->Printf( "Enabled Vulkan device extensions:" ); - for( const auto& ext : enabledExtensions.device ) - { - common->Printf( " %s\n", ext.c_str() ); - - if( ext == VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME ) - { - accelStructSupported = true; - } - else if( ext == VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME ) - { - // RB: only makes problems at the moment - bufferAddressSupported = true; - } - else if( ext == VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME ) - { - rayPipelineSupported = true; - } - else if( ext == VK_KHR_RAY_QUERY_EXTENSION_NAME ) - { - rayQuerySupported = true; - } - else if( ext == VK_NV_MESH_SHADER_EXTENSION_NAME ) - { - meshletsSupported = true; - } - else if( ext == VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME ) - { - vrsSupported = true; - } - } - - std::unordered_set uniqueQueueFamilies = - { - m_GraphicsQueueFamily, - m_PresentQueueFamily - }; - - if( deviceParms.enableComputeQueue ) - { - uniqueQueueFamilies.insert( m_ComputeQueueFamily ); - } - - if( deviceParms.enableCopyQueue ) - { - uniqueQueueFamilies.insert( m_TransferQueueFamily ); - } - - float priority = 1.f; - std::vector queueDesc; - for( int queueFamily : uniqueQueueFamilies ) - { - queueDesc.push_back( vk::DeviceQueueCreateInfo() - .setQueueFamilyIndex( queueFamily ) - .setQueueCount( 1 ) - .setPQueuePriorities( &priority ) ); - } - - auto accelStructFeatures = vk::PhysicalDeviceAccelerationStructureFeaturesKHR() - .setAccelerationStructure( true ); - auto bufferAddressFeatures = vk::PhysicalDeviceBufferAddressFeaturesEXT() - .setBufferDeviceAddress( true ); - auto rayPipelineFeatures = vk::PhysicalDeviceRayTracingPipelineFeaturesKHR() - .setRayTracingPipeline( true ) - .setRayTraversalPrimitiveCulling( true ); - auto rayQueryFeatures = vk::PhysicalDeviceRayQueryFeaturesKHR() - .setRayQuery( true ); - auto meshletFeatures = vk::PhysicalDeviceMeshShaderFeaturesNV() - .setTaskShader( true ) - .setMeshShader( true ); - auto vrsFeatures = vk::PhysicalDeviceFragmentShadingRateFeaturesKHR() - .setPipelineFragmentShadingRate( true ) - .setPrimitiveFragmentShadingRate( true ) - .setAttachmentFragmentShadingRate( true ); - - void* pNext = nullptr; -#define APPEND_EXTENSION(condition, desc) if (condition) { (desc).pNext = pNext; pNext = &(desc); } // NOLINT(cppcoreguidelines-macro-usage) - APPEND_EXTENSION( accelStructSupported, accelStructFeatures ) - APPEND_EXTENSION( bufferAddressSupported, bufferAddressFeatures ) - APPEND_EXTENSION( rayPipelineSupported, rayPipelineFeatures ) - APPEND_EXTENSION( rayQuerySupported, rayQueryFeatures ) - APPEND_EXTENSION( meshletsSupported, meshletFeatures ) - APPEND_EXTENSION( vrsSupported, vrsFeatures ) -#undef APPEND_EXTENSION - - auto deviceFeatures = vk::PhysicalDeviceFeatures() - .setShaderImageGatherExtended( true ) - .setSamplerAnisotropy( true ) - .setTessellationShader( true ) - .setTextureCompressionBC( true ) - .setGeometryShader( true ) - .setImageCubeArray( true ) - .setDualSrcBlend( true ); - - auto vulkan12features = vk::PhysicalDeviceVulkan12Features() - .setDescriptorIndexing( true ) - .setRuntimeDescriptorArray( true ) - .setDescriptorBindingPartiallyBound( true ) - .setDescriptorBindingVariableDescriptorCount( true ) - .setTimelineSemaphore( true ) - .setShaderSampledImageArrayNonUniformIndexing( true ) - .setPNext( pNext ); - - auto layerVec = stringSetToVector( enabledExtensions.layers ); - auto extVec = stringSetToVector( enabledExtensions.device ); - - auto deviceDesc = vk::DeviceCreateInfo() - .setPQueueCreateInfos( queueDesc.data() ) - .setQueueCreateInfoCount( uint32_t( queueDesc.size() ) ) - .setPEnabledFeatures( &deviceFeatures ) - .setEnabledExtensionCount( uint32_t( extVec.size() ) ) - .setPpEnabledExtensionNames( extVec.data() ) - .setEnabledLayerCount( uint32_t( layerVec.size() ) ) - .setPpEnabledLayerNames( layerVec.data() ) - .setPNext( &vulkan12features ); - - const vk::Result res = m_VulkanPhysicalDevice.createDevice( &deviceDesc, nullptr, &m_VulkanDevice ); - if( res != vk::Result::eSuccess ) - { - common->FatalError( "Failed to create a Vulkan physical device, error code = %s", nvrhi::vulkan::resultToString( res ) ); - return false; - } - - m_VulkanDevice.getQueue( m_GraphicsQueueFamily, 0, &m_GraphicsQueue ); - if( deviceParms.enableComputeQueue ) - { - m_VulkanDevice.getQueue( m_ComputeQueueFamily, 0, &m_ComputeQueue ); - } - if( deviceParms.enableCopyQueue ) - { - m_VulkanDevice.getQueue( m_TransferQueueFamily, 0, &m_TransferQueue ); - } - m_VulkanDevice.getQueue( m_PresentQueueFamily, 0, &m_PresentQueue ); - - VULKAN_HPP_DEFAULT_DISPATCHER.init( m_VulkanDevice ); - - // stash the renderer string - auto prop = m_VulkanPhysicalDevice.getProperties(); - m_RendererString = std::string( prop.deviceName.data() ); - - common->Printf( "Created Vulkan device: %s", m_RendererString.c_str() ); - - return true; -} - -/* -* Vulkan Example base class -* -* Copyright (C) by Sascha Willems - www.saschawillems.de -* -* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) -*/ -bool DeviceManager_VK::createWindowSurface() -{ - VkResult err = VK_SUCCESS; - - // Create the os-specific surface -#if defined(VK_USE_PLATFORM_WIN32_KHR) - VkWin32SurfaceCreateInfoKHR surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; - surfaceCreateInfo.hinstance = ( HINSTANCE )windowInstance; - surfaceCreateInfo.hwnd = ( HWND )windowHandle; - err = vkCreateWin32SurfaceKHR( m_VulkanInstance, &surfaceCreateInfo, nullptr, ( VkSurfaceKHR* )&m_WindowSurface ); -#elif defined(VK_USE_PLATFORM_ANDROID_KHR) - VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; - surfaceCreateInfo.window = window; - err = vkCreateAndroidSurfaceKHR( m_VulkanInstance, &surfaceCreateInfo, nullptr, ( VkSurfaceKHR* )&m_WindowSurface ); -#elif defined(VK_USE_PLATFORM_IOS_MVK) - VkIOSSurfaceCreateInfoMVK surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK; - surfaceCreateInfo.pNext = NULL; - surfaceCreateInfo.flags = 0; - surfaceCreateInfo.pView = view; - err = vkCreateIOSSurfaceMVK( m_VulkanInstance, &surfaceCreateInfo, nullptr, ( VkSurfaceKHR* )&m_WindowSurface ); -#elif defined(VK_USE_PLATFORM_MACOS_MVK) - VkMacOSSurfaceCreateInfoMVK surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK; - surfaceCreateInfo.pNext = NULL; - surfaceCreateInfo.flags = 0; - surfaceCreateInfo.pView = view; - err = vkCreateMacOSSurfaceMVK( m_VulkanInstance, &surfaceCreateInfo, nullptr, ( VkSurfaceKHR* )&m_WindowSurface ); -#elif defined(_DIRECT2DISPLAY) - createDirect2DisplaySurface( width, height ); -#elif defined(VK_USE_PLATFORM_DIRECTFB_EXT) - VkDirectFBSurfaceCreateInfoEXT surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_DIRECTFB_SURFACE_CREATE_INFO_EXT; - surfaceCreateInfo.dfb = dfb; - surfaceCreateInfo.surface = window; - err = vkCreateDirectFBSurfaceEXT( m_VulkanInstance, &surfaceCreateInfo, nullptr, ( VkSurfaceKHR* )&m_WindowSurface ); -#elif defined(VK_USE_PLATFORM_WAYLAND_KHR) - VkWaylandSurfaceCreateInfoKHR surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; - surfaceCreateInfo.display = display; - surfaceCreateInfo.surface = window; - err = vkCreateWaylandSurfaceKHR( m_VulkanInstance, &surfaceCreateInfo, nullptr, ( VkSurfaceKHR* )&m_WindowSurface ); -#elif defined(VK_USE_PLATFORM_XCB_KHR) - VkXcbSurfaceCreateInfoKHR surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; - surfaceCreateInfo.connection = connection; - surfaceCreateInfo.window = window; - err = vkCreateXcbSurfaceKHR( m_VulkanInstance, &surfaceCreateInfo, nullptr, ( VkSurfaceKHR* )&m_WindowSurface ); -#elif defined(VK_USE_PLATFORM_HEADLESS_EXT) - VkHeadlessSurfaceCreateInfoEXT surfaceCreateInfo = {}; - surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_HEADLESS_SURFACE_CREATE_INFO_EXT; - PFN_vkCreateHeadlessSurfaceEXT fpCreateHeadlessSurfaceEXT = ( PFN_vkCreateHeadlessSurfaceEXT )vkGetInstanceProcAddr( instance, "vkCreateHeadlessSurfaceEXT" ); - if( !fpCreateHeadlessSurfaceEXT ) - { - vks::tools::exitFatal( "Could not fetch function pointer for the headless extension!", -1 ); - } - err = fpCreateHeadlessSurfaceEXT( m_VulkanInstance, &surfaceCreateInfo, nullptr, ( VkSurfaceKHR* )&m_WindowSurface ); -#endif - - if( err != VK_SUCCESS ) - { - common->FatalError( "Failed to create a GLFW window surface, error code = %s", nvrhi::vulkan::resultToString( err ) ); - return false; - } - - return true; -} - -void DeviceManager_VK::destroySwapChain() -{ - if( m_VulkanDevice ) - { - m_VulkanDevice.waitIdle(); - } - - if( m_SwapChain ) - { - m_VulkanDevice.destroySwapchainKHR( m_SwapChain ); - m_SwapChain = nullptr; - } - - m_SwapChainImages.clear(); -} - -bool DeviceManager_VK::createSwapChain() -{ - destroySwapChain(); - - m_SwapChainFormat = - { - vk::Format( nvrhi::vulkan::convertFormat( deviceParms.swapChainFormat ) ), - vk::ColorSpaceKHR::eSrgbNonlinear - }; - - vk::Extent2D extent = vk::Extent2D( deviceParms.backBufferWidth, deviceParms.backBufferHeight ); - - std::unordered_set uniqueQueues = - { - uint32_t( m_GraphicsQueueFamily ), - uint32_t( m_PresentQueueFamily ) - }; - - std::vector queues = setToVector( uniqueQueues ); - - const bool enableSwapChainSharing = queues.size() > 1; - - auto desc = vk::SwapchainCreateInfoKHR() - .setSurface( m_WindowSurface ) - .setMinImageCount( deviceParms.swapChainBufferCount ) - .setImageFormat( m_SwapChainFormat.format ) - .setImageColorSpace( m_SwapChainFormat.colorSpace ) - .setImageExtent( extent ) - .setImageArrayLayers( 1 ) - .setImageUsage( vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eTransferDst | vk::ImageUsageFlagBits::eSampled ) - .setImageSharingMode( enableSwapChainSharing ? vk::SharingMode::eConcurrent : vk::SharingMode::eExclusive ) - .setQueueFamilyIndexCount( enableSwapChainSharing ? uint32_t( queues.size() ) : 0 ) - .setPQueueFamilyIndices( enableSwapChainSharing ? queues.data() : nullptr ) - .setPreTransform( vk::SurfaceTransformFlagBitsKHR::eIdentity ) - .setCompositeAlpha( vk::CompositeAlphaFlagBitsKHR::eOpaque ) - .setPresentMode( deviceParms.vsyncEnabled ? vk::PresentModeKHR::eFifo : vk::PresentModeKHR::eImmediate ) - .setClipped( true ) - .setOldSwapchain( nullptr ); - - const vk::Result res = m_VulkanDevice.createSwapchainKHR( &desc, nullptr, &m_SwapChain ); - if( res != vk::Result::eSuccess ) - { - common->FatalError( "Failed to create a Vulkan swap chain, error code = %s", nvrhi::vulkan::resultToString( res ) ); - return false; - } - - // retrieve swap chain images - auto images = m_VulkanDevice.getSwapchainImagesKHR( m_SwapChain ); - for( auto image : images ) - { - SwapChainImage sci; - sci.image = image; - - nvrhi::TextureDesc textureDesc; - textureDesc.width = deviceParms.backBufferWidth; - textureDesc.height = deviceParms.backBufferHeight; - textureDesc.format = deviceParms.swapChainFormat; - textureDesc.debugName = "Swap chain image"; - textureDesc.initialState = nvrhi::ResourceStates::Present; - textureDesc.keepInitialState = true; - textureDesc.isRenderTarget = true; - - sci.rhiHandle = m_NvrhiDevice->createHandleForNativeTexture( nvrhi::ObjectTypes::VK_Image, nvrhi::Object( sci.image ), textureDesc ); - m_SwapChainImages.push_back( sci ); - } - - m_SwapChainIndex = 0; - - return true; -} - -bool DeviceManager_VK::CreateDeviceAndSwapChain() -{ - // RB: control these through the cmdline - deviceParms.enableNvrhiValidationLayer = r_useValidationLayers.GetInteger() > 0; - deviceParms.enableDebugRuntime = r_useValidationLayers.GetInteger() > 1; - - if( deviceParms.enableDebugRuntime ) - { - enabledExtensions.instance.insert( "VK_EXT_debug_report" ); - enabledExtensions.layers.insert( "VK_LAYER_KHRONOS_validation" ); - } - - const vk::DynamicLoader dl; - const PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = // NOLINT(misc-misplaced-const) - dl.getProcAddress( "vkGetInstanceProcAddr" ); - VULKAN_HPP_DEFAULT_DISPATCHER.init( vkGetInstanceProcAddr ); - -#define CHECK(a) if (!(a)) { return false; } - - CHECK( createInstance() ); - - if( deviceParms.enableDebugRuntime ) - { - installDebugCallback(); - } - - if( deviceParms.swapChainFormat == nvrhi::Format::SRGBA8_UNORM ) - { - deviceParms.swapChainFormat = nvrhi::Format::SBGRA8_UNORM; - } - else if( deviceParms.swapChainFormat == nvrhi::Format::RGBA8_UNORM ) - { - deviceParms.swapChainFormat = nvrhi::Format::BGRA8_UNORM; - } - - // add device extensions requested by the user - for( const std::string& name : deviceParms.requiredVulkanDeviceExtensions ) - { - enabledExtensions.device.insert( name ); - } - for( const std::string& name : deviceParms.optionalVulkanDeviceExtensions ) - { - optionalExtensions.device.insert( name ); - } - - CHECK( createWindowSurface() ); - CHECK( pickPhysicalDevice() ); - CHECK( findQueueFamilies( m_VulkanPhysicalDevice ) ); - CHECK( createDevice() ); - - auto vecInstanceExt = stringSetToVector( enabledExtensions.instance ); - auto vecLayers = stringSetToVector( enabledExtensions.layers ); - auto vecDeviceExt = stringSetToVector( enabledExtensions.device ); - - nvrhi::vulkan::DeviceDesc deviceDesc; - deviceDesc.errorCB = &DefaultMessageCallback::GetInstance(); - deviceDesc.instance = m_VulkanInstance; - deviceDesc.physicalDevice = m_VulkanPhysicalDevice; - deviceDesc.device = m_VulkanDevice; - deviceDesc.graphicsQueue = m_GraphicsQueue; - deviceDesc.graphicsQueueIndex = m_GraphicsQueueFamily; - if( deviceParms.enableComputeQueue ) - { - deviceDesc.computeQueue = m_ComputeQueue; - deviceDesc.computeQueueIndex = m_ComputeQueueFamily; - } - if( deviceParms.enableCopyQueue ) - { - deviceDesc.transferQueue = m_TransferQueue; - deviceDesc.transferQueueIndex = m_TransferQueueFamily; - } - deviceDesc.instanceExtensions = vecInstanceExt.data(); - deviceDesc.numInstanceExtensions = vecInstanceExt.size(); - deviceDesc.deviceExtensions = vecDeviceExt.data(); - deviceDesc.numDeviceExtensions = vecDeviceExt.size(); - - m_NvrhiDevice = nvrhi::vulkan::createDevice( deviceDesc ); - - if( deviceParms.enableNvrhiValidationLayer ) - { - m_ValidationLayer = nvrhi::validation::createValidationLayer( m_NvrhiDevice ); - } - - CHECK( createSwapChain() ); - - m_BarrierCommandList = m_NvrhiDevice->createCommandList(); - - m_PresentSemaphore = m_VulkanDevice.createSemaphore( vk::SemaphoreCreateInfo() ); - -#undef CHECK - - return true; -} - -void DeviceManager_VK::DestroyDeviceAndSwapChain() -{ - destroySwapChain(); - - m_VulkanDevice.destroySemaphore( m_PresentSemaphore ); - m_PresentSemaphore = vk::Semaphore(); - - m_BarrierCommandList = nullptr; - - m_NvrhiDevice = nullptr; - m_ValidationLayer = nullptr; - m_RendererString.clear(); - - if( m_DebugReportCallback ) - { - m_VulkanInstance.destroyDebugReportCallbackEXT( m_DebugReportCallback ); - } - - if( m_VulkanDevice ) - { - m_VulkanDevice.destroy(); - m_VulkanDevice = nullptr; - } - - if( m_WindowSurface ) - { - assert( m_VulkanInstance ); - m_VulkanInstance.destroySurfaceKHR( m_WindowSurface ); - m_WindowSurface = nullptr; - } - - if( m_VulkanInstance ) - { - m_VulkanInstance.destroy(); - m_VulkanInstance = nullptr; - } -} - -void DeviceManager_VK::BeginFrame() -{ - const vk::Result res = m_VulkanDevice.acquireNextImageKHR( m_SwapChain, - std::numeric_limits::max(), // timeout - m_PresentSemaphore, - vk::Fence(), - &m_SwapChainIndex ); - - assert( res == vk::Result::eSuccess ); - - m_NvrhiDevice->queueWaitForSemaphore( nvrhi::CommandQueue::Graphics, m_PresentSemaphore, 0 ); -} - -void DeviceManager_VK::EndFrame() -{ - m_NvrhiDevice->queueSignalSemaphore( nvrhi::CommandQueue::Graphics, m_PresentSemaphore, 0 ); - - m_BarrierCommandList->open(); // umm... - m_BarrierCommandList->close(); - m_NvrhiDevice->executeCommandList( m_BarrierCommandList ); -} - -void DeviceManager_VK::Present() -{ - vk::PresentInfoKHR info = vk::PresentInfoKHR() - .setWaitSemaphoreCount( 1 ) - .setPWaitSemaphores( &m_PresentSemaphore ) - .setSwapchainCount( 1 ) - .setPSwapchains( &m_SwapChain ) - .setPImageIndices( &m_SwapChainIndex ); - - const vk::Result res = m_PresentQueue.presentKHR( &info ); - assert( res == vk::Result::eSuccess || res == vk::Result::eErrorOutOfDateKHR ); - - if( deviceParms.enableDebugRuntime ) - { - // according to vulkan-tutorial.com, "the validation layer implementation expects - // the application to explicitly synchronize with the GPU" - m_PresentQueue.waitIdle(); - } - else - { -#ifndef _WIN32 - if( deviceParms.vsyncEnabled ) - { - m_PresentQueue.waitIdle(); - } -#endif - - while( m_FramesInFlight.size() > deviceParms.maxFramesInFlight ) - { - auto query = m_FramesInFlight.front(); - m_FramesInFlight.pop(); - - m_NvrhiDevice->waitEventQuery( query ); - - m_QueryPool.push_back( query ); - } - - nvrhi::EventQueryHandle query; - if( !m_QueryPool.empty() ) - { - query = m_QueryPool.back(); - m_QueryPool.pop_back(); - } - else - { - query = m_NvrhiDevice->createEventQuery(); - } - - m_NvrhiDevice->resetEventQuery( query ); - m_NvrhiDevice->setEventQuery( query, nvrhi::CommandQueue::Graphics ); - m_FramesInFlight.push( query ); - } -} - -DeviceManager* DeviceManager::CreateVK() -{ - return new DeviceManager_VK(); -} diff --git a/neo/sys/win32/win_glimp.cpp b/neo/sys/win32/win_glimp.cpp index c3e69b65..6bd73b29 100644 --- a/neo/sys/win32/win_glimp.cpp +++ b/neo/sys/win32/win_glimp.cpp @@ -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; @@ -1065,6 +1070,29 @@ static bool GLW_GetWindowDimensions( const glimpParms_t parms, int& x, int& y, i return true; } +static bool GetCenteredWindowDimensions( int& x, int& y, int& w, int& h ) +{ + // get position and size of default display for windowed mode (parms.fullScreen = 0) + int displayX, displayY, displayW, displayH, displayHz = 0; + if( !GetDisplayCoordinates( 0, displayX, displayY, displayW, displayH, displayHz ) ) + { + return false; + } + + // find the centered position not exceeding display bounds + const int centreX = displayX + displayW / 2; + const int centreY = displayY + displayH / 2; + const int left = centreX - w / 2; + const int right = left + w; + const int top = centreY - h / 2; + const int bottom = top + h; + x = std::max( left, displayX ); + y = std::max( top, displayY ); + w = std::min( right - left, displayW ); + h = std::min( bottom - top, displayH ); + + return true; +} bool DeviceManager::CreateWindowDeviceAndSwapChain( const glimpParms_t& parms, const char* windowTitle ) { @@ -1074,6 +1102,15 @@ bool DeviceManager::CreateWindowDeviceAndSwapChain( const glimpParms_t& parms, c return false; } + // SRS - if in windowed mode, start with centered windowed on default display, afterwards use r_windowX / r_windowY + if( parms.fullScreen == 0 ) + { + if( !GetCenteredWindowDimensions( x, y, w, h ) ) + { + return false; + } + } + int stylebits; int exstyle; @@ -1136,21 +1173,17 @@ bool DeviceManager::CreateWindowDeviceAndSwapChain( const glimpParms_t& parms, c SetForegroundWindow( win32.hWnd ); SetFocus( win32.hWnd ); - glConfig.isFullscreen = parms.fullScreen; - - UpdateWindowSize( parms ); - return true; } -void DeviceManager::UpdateWindowSize( const glimpParms_t& params ) +void DeviceManager::UpdateWindowSize( const glimpParms_t& parms ) { windowVisible = true; - if( int( deviceParms.backBufferWidth ) != params.width || - int( deviceParms.backBufferHeight ) != params.height || + if( int( deviceParms.backBufferWidth ) != parms.width || + int( deviceParms.backBufferHeight ) != parms.height || #if ID_MSAA - int( deviceParms.backBufferSampleCount ) != params.multiSamples || + int( deviceParms.backBufferSampleCount ) != parms.multiSamples || #endif ( deviceParms.vsyncEnabled != requestedVSync && GetGraphicsAPI() == nvrhi::GraphicsAPI::VULKAN ) ) { @@ -1158,16 +1191,18 @@ void DeviceManager::UpdateWindowSize( const glimpParms_t& params ) BackBufferResizing(); - deviceParms.backBufferWidth = params.width; - deviceParms.backBufferHeight = params.height; - deviceParms.backBufferSampleCount = params.multiSamples; + deviceParms.backBufferWidth = parms.width; + deviceParms.backBufferHeight = parms.height; + deviceParms.backBufferSampleCount = parms.multiSamples; deviceParms.vsyncEnabled = requestedVSync; ResizeSwapChain(); BackBufferResized(); } - - deviceParms.vsyncEnabled = requestedVSync; + else + { + deviceParms.vsyncEnabled = requestedVSync; + } } /* @@ -1375,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 @@ -1455,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 @@ -1538,11 +1596,11 @@ bool GLimp_SetScreenParms( glimpParms_t parms ) glConfig.isFullscreen = parms.fullScreen; glConfig.pixelAspect = 1.0f; // FIXME: some monitor modes may be distorted - glConfig.isFullscreen = parms.fullScreen; + glConfig.isStereoPixelFormat = parms.stereo; glConfig.nativeScreenWidth = parms.width; glConfig.nativeScreenHeight = parms.height; - - deviceManager->UpdateWindowSize( parms ); + glConfig.displayFrequency = GetDisplayFrequency( parms ); + glConfig.multisamples = parms.multiSamples; return true; } diff --git a/neo/sys/win32/win_wndproc.cpp b/neo/sys/win32/win_wndproc.cpp index 31ebaa75..e24abb9a 100644 --- a/neo/sys/win32/win_wndproc.cpp +++ b/neo/sys/win32/win_wndproc.cpp @@ -179,31 +179,32 @@ LONG WINAPI MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) switch( uMsg ) { case WM_WINDOWPOSCHANGED: - // RB: FIXME this messes with with the window size in a really bad way -#if 0 - if( renderSystem->IsInitialized() )//&& win32.hDC != NULL ) + // SRS - Needed by ResizeImages() to resize before the start of a frame + // SRS - Aspect ratio constraints are controlled by WIN_Sizing() above + if( renderSystem->IsInitialized() && win32.hDC != NULL ) { RECT rect; if( ::GetClientRect( win32.hWnd, &rect ) ) { - auto originalWidth = glConfig.nativeScreenWidth; - auto originalHeight = glConfig.nativeScreenHeight; if( rect.right > rect.left && rect.bottom > rect.top ) { glConfig.nativeScreenWidth = rect.right - rect.left; glConfig.nativeScreenHeight = rect.bottom - rect.top; // save the window size in cvars if we aren't fullscreen + // SRS - also check renderSystem state to make sure WM doesn't fool us when exiting fullscreen int style = GetWindowLong( hWnd, GWL_STYLE ); - if( ( style & WS_POPUP ) == 0 ) + if( ( style & WS_POPUP ) == 0 && !renderSystem->IsFullScreen() ) { r_windowWidth.SetInteger( glConfig.nativeScreenWidth ); r_windowHeight.SetInteger( glConfig.nativeScreenHeight ); } + + // SRS - Inform ImGui that the window size has changed + ImGuiHook::NotifyDisplaySizeChanged( glConfig.nativeScreenWidth, glConfig.nativeScreenHeight ); } } } -#endif break; case WM_MOVE: { @@ -211,8 +212,9 @@ LONG WINAPI MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) RECT r; // save the window origin in cvars if we aren't fullscreen + // SRS - also check renderSystem state to make sure WM doesn't fool us when exiting fullscreen int style = GetWindowLong( hWnd, GWL_STYLE ); - if( ( style & WS_POPUP ) == 0 ) + if( ( style & WS_POPUP ) == 0 && !renderSystem->IsFullScreen() ) { xPos = ( short ) LOWORD( lParam ); // horizontal position yPos = ( short ) HIWORD( lParam ); // vertical position