Merge branch 'sync-changes' into 750-better-vsync

This commit is contained in:
Robert Beckebans 2023-03-06 19:58:14 +01:00
commit 9b67e2a91a
10 changed files with 63 additions and 75 deletions

View file

@ -494,7 +494,7 @@ Recommended in this case is `cmake-vs2019-64bit.bat` or `cmake-vs2019-64bit-no-f
You don't need FFmpeg to be installed. You can turn it off by adding -DFFMPEG=OFF and -DBINKDEC=ON to the CMake options. For debug builds FFmpeg is enabled by default because the bundled libbinkdec is slow during development if compiled for Debug mode. For release, retail and universal builds FFmpeg is disabled and libbinkdec is enabled by default.
The Vulkan SDK must be installed and can be obtained from https://vulkan.lunarg.com/sdk/home#mac
The Vulkan SDK 1.3.231.1 or later must be installed and can be obtained from https://vulkan.lunarg.com/sdk/home#mac
3. Generate the Makefiles using CMake:

View file

@ -5,6 +5,7 @@ cd xcode-debug
# note 1: remove or set -DCMAKE_SUPPRESS_REGENERATION=OFF to reenable ZERO_CHECK target which checks for CMakeLists.txt changes and re-runs CMake before builds
# however, if ZERO_CHECK is reenabled **must** add VULKAN_SDK location to Xcode Custom Paths (under Prefs/Locations) otherwise build failures may occur
# note 2: policy CMAKE_POLICY_DEFAULT_CMP0142=NEW suppresses non-existant per-config suffixes on Xcode library search paths, works for cmake version 3.25 and later
#note 3: env variable MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE=1 enables MoltenVK's image view swizzle which may be required on older macOS versions or hardware (see vulkaninfo)
# note 4: env variable MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=2 enables MoltenVK's use of Metal argument buffers only if VK_EXT_descriptor_indexing is enabled
cmake -G Xcode -DCMAKE_BUILD_TYPE=Debug -DCMAKE_XCODE_GENERATE_SCHEME=ON -DCMAKE_XCODE_SCHEME_ENVIRONMENT="MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE=1;MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=2" -DCMAKE_SUPPRESS_REGENERATION=ON -DOPENAL_LIBRARY=/usr/local/opt/openal-soft/lib/libopenal.dylib -DOPENAL_INCLUDE_DIR=/usr/local/opt/openal-soft/include ../neo -DCMAKE_POLICY_DEFAULT_CMP0142=NEW -Wno-dev
# note 3: env variable MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE=1 enables MoltenVK's image view swizzle which may be required on older macOS versions or hardware (see vulkaninfo)
# note 4: env variable MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS=1 enforces synchronous queue submits which is required for the synchronization method used by the game
# note 5: env variable MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=2 enables MoltenVK's use of Metal argument buffers only if VK_EXT_descriptor_indexing is enabled
cmake -G Xcode -DCMAKE_BUILD_TYPE=Debug -DCMAKE_XCODE_GENERATE_SCHEME=ON -DCMAKE_XCODE_SCHEME_ENVIRONMENT="MVK_CONFIG_FULL_IMAGE_VIEW_SWIZZLE=1;MVK_CONFIG_SYNCHRONOUS_QUEUE_SUBMITS=1;MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=2" -DCMAKE_SUPPRESS_REGENERATION=ON -DOPENAL_LIBRARY=/usr/local/opt/openal-soft/lib/libopenal.dylib -DOPENAL_INCLUDE_DIR=/usr/local/opt/openal-soft/include ../neo -DCMAKE_POLICY_DEFAULT_CMP0142=NEW -Wno-dev

View file

@ -106,12 +106,7 @@ function(compile_shaders)
endif()
if (NOT params_CFLAGS)
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
# SRS - MoltenVK currently supports vulkan 1.1 (1.2 coming in next SDK release)
set(CFLAGS "$<IF:$<CONFIG:Debug>,-Zi,> -fspv-target-env=vulkan1.1 -O3 -WX -Zpr")
else()
set(CFLAGS "$<IF:$<CONFIG:Debug>,-Zi,> -fspv-target-env=vulkan1.2 -O3 -WX -Zpr")
endif()
set(CFLAGS "$<IF:$<CONFIG:Debug>,-Zi,> -fspv-target-env=vulkan1.2 -O3 -WX -Zpr")
else()
set(CFLAGS ${params_CFLAGS})
endif()

View file

@ -90,8 +90,9 @@ const int MAX_EXPRESSION_REGISTERS = 4096;
// everything that is needed by the backend needs
// to be double buffered to allow it to run in
// parallel on a dual cpu machine
#if defined(__APPLE__) && ( defined( USE_VULKAN ) || defined( USE_NVRHI ) )
#if ( defined(__APPLE__) && defined( USE_VULKAN ) ) || defined( USE_NVRHI )
// SRS - macOS MoltenVK/Metal needs triple buffering for full screen to work properly
// SRS - use triple buffering for NVRHI with command queue event query sync method
const uint32 NUM_FRAME_DATA = 3;
#else
const uint32 NUM_FRAME_DATA = 2;

View file

@ -1603,14 +1603,16 @@ void idRenderBackend::GL_BlockingSwapBuffers()
OPTICK_EVENT( "BlockingSwapBuffers" );
// Make sure that all frames have finished rendering
deviceManager->GetDevice()->waitForIdle();
// Release all in-flight references to the render targets
deviceManager->GetDevice()->runGarbageCollection();
// SRS - device-level sync kills perf by serializing command queue processing (CPU) and rendering (GPU)
// - instead, use alternative sync method (based on command queue event queries) inside Present()
//deviceManager->GetDevice()->waitForIdle();
// Present to the swap chain.
deviceManager->Present();
// Release all in-flight references to the render targets
deviceManager->GetDevice()->runGarbageCollection();
renderLog.EndFrame();
if( deviceManager->GetGraphicsAPI() == nvrhi::GraphicsAPI::VULKAN )

View file

@ -890,7 +890,12 @@ bool R_ReadPixelsRGB8( nvrhi::IDevice* device, CommonRenderPasses* pPasses, nvrh
data[ i * 4 + 3 ] = 0xff;
}
// SRS - Save screen shots to fs_savepath on macOS (i.e. don't save into an app bundle's basepath)
#if defined(__APPLE__)
R_WritePNG( fullname, static_cast<byte*>( pData ), 4, desc.width, desc.height, true, "fs_savepath" );
#else
R_WritePNG( fullname, static_cast<byte*>( pData ), 4, desc.width, desc.height, true, "fs_basepath" );
#endif
if( newData )
{

View file

@ -54,7 +54,6 @@ struct DeviceCreationParameters
nvrhi::Format swapChainFormat = nvrhi::Format::RGBA8_UNORM; // RB: don't do the sRGB gamma ramp with the swapchain
uint32_t swapChainSampleCount = 1;
uint32_t swapChainSampleQuality = 0;
uint32_t maxFramesInFlight = 2;
bool enableDebugRuntime = false;
bool enableNvrhiValidationLayer = false;
bool vsyncEnabled = false;

View file

@ -61,6 +61,7 @@ class DeviceManager_DX12 : public DeviceManager
std::vector<nvrhi::TextureHandle> m_RhiSwapChainBuffers;
RefCountPtr<ID3D12Fence> m_FrameFence;
std::vector<HANDLE> m_FrameFenceEvents;
nvrhi::EventQueryHandle m_FrameWaitQuery;
UINT64 m_FrameCount = 1;
@ -447,6 +448,9 @@ bool DeviceManager_DX12::CreateDeviceAndSwapChain()
m_FrameFenceEvents.push_back( CreateEvent( nullptr, false, true, NULL ) );
}
m_FrameWaitQuery = nvrhiDevice->createEventQuery();
nvrhiDevice->setEventQuery( m_FrameWaitQuery, nvrhi::CommandQueue::Graphics );
return true;
}
@ -459,6 +463,8 @@ void DeviceManager_DX12::DestroyDeviceAndSwapChain()
m_NvrhiDevice = nullptr;
m_FrameWaitQuery = nullptr;
for( auto fenceEvent : m_FrameFenceEvents )
{
WaitForSingleObject( fenceEvent, INFINITE );
@ -630,6 +636,11 @@ void DeviceManager_DX12::EndFrame()
void DeviceManager_DX12::Present()
{
// SRS - Sync on previous frame's command queue completion vs. waitForIdle() on whole device
m_NvrhiDevice->waitEventQuery( m_FrameWaitQuery );
m_NvrhiDevice->resetEventQuery( m_FrameWaitQuery );
m_NvrhiDevice->setEventQuery( m_FrameWaitQuery, nvrhi::CommandQueue::Graphics );
if( !m_windowVisible )
{
return;

View file

@ -272,8 +272,7 @@ private:
nvrhi::CommandListHandle m_BarrierCommandList;
vk::Semaphore m_PresentSemaphore;
std::queue<nvrhi::EventQueryHandle> m_FramesInFlight;
std::vector<nvrhi::EventQueryHandle> m_QueryPool;
nvrhi::EventQueryHandle m_FrameWaitQuery;
// SRS - flag indicating support for eFifoRelaxed surface presentation (r_swapInterval = 1) mode
bool enablePModeFifoRelaxed = false;
@ -1124,6 +1123,10 @@ bool DeviceManager_VK::CreateDeviceAndSwapChain()
vkGetMoltenVKConfigurationMVK( m_VulkanInstance, &pConfig, &pConfigSize );
// SRS - Enforce synchronous queue submission for vkQueueSubmit() & vkQueuePresentKHR()
pConfig.synchronousQueueSubmits = VK_TRUE;
vkSetMoltenVKConfigurationMVK( m_VulkanInstance, &pConfig, &pConfigSize );
// SRS - If we don't have native image view swizzle, enable MoltenVK's image view swizzle feature
if( portabilityFeatures.imageViewFormatSwizzle == VK_FALSE )
{
@ -1182,6 +1185,9 @@ bool DeviceManager_VK::CreateDeviceAndSwapChain()
m_PresentSemaphore = m_VulkanDevice.createSemaphore( vk::SemaphoreCreateInfo() );
m_FrameWaitQuery = m_NvrhiDevice->createEventQuery();
m_NvrhiDevice->setEventQuery( m_FrameWaitQuery, nvrhi::CommandQueue::Graphics );
#undef CHECK
return true;
@ -1191,25 +1197,13 @@ void DeviceManager_VK::DestroyDeviceAndSwapChain()
{
destroySwapChain();
m_FrameWaitQuery = nullptr;
m_VulkanDevice.destroySemaphore( m_PresentSemaphore );
m_PresentSemaphore = vk::Semaphore();
m_BarrierCommandList = nullptr;
while( m_FramesInFlight.size() > 0 )
{
auto query = m_FramesInFlight.front();
m_FramesInFlight.pop();
query = nullptr;
}
if( !m_QueryPool.empty() )
{
auto query = m_QueryPool.back();
m_QueryPool.pop_back();
query = nullptr;
}
m_NvrhiDevice = nullptr;
m_ValidationLayer = nullptr;
m_RendererString.clear();
@ -1276,6 +1270,11 @@ void DeviceManager_VK::EndFrame()
void DeviceManager_VK::Present()
{
// SRS - Sync on previous frame's command queue completion vs. waitForIdle() on whole device
m_NvrhiDevice->waitEventQuery( m_FrameWaitQuery );
m_NvrhiDevice->resetEventQuery( m_FrameWaitQuery );
m_NvrhiDevice->setEventQuery( m_FrameWaitQuery, nvrhi::CommandQueue::Graphics );
vk::PresentInfoKHR info = vk::PresentInfoKHR()
.setWaitSemaphoreCount( 1 )
.setPWaitSemaphores( &m_PresentSemaphore )
@ -1286,46 +1285,12 @@ void DeviceManager_VK::Present()
const vk::Result res = m_PresentQueue.presentKHR( &info );
assert( res == vk::Result::eSuccess || res == vk::Result::eErrorOutOfDateKHR || res == vk::Result::eSuboptimalKHR );
if( m_DeviceParams.enableDebugRuntime )
if( m_DeviceParms.enableDebugRuntime || deviceParms.vsyncEnabled )
{
// 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( m_DeviceParams.vsyncEnabled )
{
m_PresentQueue.waitIdle();
}
#endif
while( m_FramesInFlight.size() > m_DeviceParams.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()

View file

@ -470,15 +470,24 @@ 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.w = parms.width;
m.h = parms.height;
// set that displaymode
if( SDL_SetWindowDisplayMode( window, &m ) < 0 )
if( m.w != parms.width || m.h != parms.height || m.refresh_rate != parms.displayHz )
{
common->Warning( "Couldn't set window mode for fullscreen, reason: %s", SDL_GetError() );
return false;
m.w = parms.width;
m.h = parms.height;
m.refresh_rate = parms.displayHz;
// if we're already in fullscreen mode, disable it first so resizing works properly
if( glConfig.isFullscreen )
{
SDL_SetWindowFullscreen( window, SDL_FALSE );
}
// set the new displaymode
if( SDL_SetWindowDisplayMode( window, &m ) < 0 )
{
common->Warning( "Couldn't set window mode for fullscreen, reason: %s", SDL_GetError() );
return false;
}
}
// if we're currently not in fullscreen mode, we need to switch to fullscreen