diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp index 0e9ec0ec8..152d37f12 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.cpp @@ -25,16 +25,23 @@ void BackBufferVulkan::Setup(GPUSwapChainVulkan* window, VkImage backbuffer, Pix ImageAcquiredSemaphore = New(Device); } +void BackBufferVulkan::WaitForSubmit() +{ + if (SubmitCmdBuffer) + { + if (SubmitCmdBufferFenceCounter == SubmitCmdBuffer->GetFenceSignaledCounter()) + SubmitCmdBuffer->Wait(); + SubmitCmdBuffer = nullptr; + SubmitCmdBufferFenceCounter = 0; + } +} + void BackBufferVulkan::Release() { + WaitForSubmit(); Handle.Release(); Delete(RenderingDoneSemaphore); Delete(ImageAcquiredSemaphore); - if (SubmitCmdBuffer) - { - SubmitCmdBuffer->Wait(); - SubmitCmdBuffer = nullptr; - } Device = nullptr; } @@ -121,7 +128,14 @@ GPUTextureView* GPUSwapChainVulkan::GetBackBufferView() ASSERT(_acquiredImageIndex != -1); auto context = _device->MainContext; - const auto backBuffer = &_backBuffers[_acquiredImageIndex].Handle; + + // Wait for prior GPU work that used this acquired image before recording + // commands against it again. Waiting before acquire can target a different image + // and unnecessarily serialize frames when the swapchain has multiple images. + auto& acquiredBackBuffer = _backBuffers[_acquiredImageIndex]; + acquiredBackBuffer.WaitForSubmit(); + + const auto backBuffer = &acquiredBackBuffer.Handle; auto cmdBufferManager = context->GetCmdBufferManager(); auto cmdBuffer = cmdBufferManager->GetCmdBuffer(); @@ -142,17 +156,6 @@ GPUTextureView* GPUSwapChainVulkan::GetBackBufferView() void GPUSwapChainVulkan::Begin(RenderTask* task) { GPUSwapChain::Begin(task); - - // Wait for the backbuffer to be available - if (_currentImageIndex != -1) - { - auto& backBuffer = _backBuffers[_currentImageIndex]; - if (backBuffer.SubmitCmdBuffer) - { - backBuffer.SubmitCmdBuffer->Wait(); - backBuffer.SubmitCmdBuffer = nullptr; - } - } } bool GPUSwapChainVulkan::Resize(int32 width, int32 height) @@ -589,6 +592,7 @@ void GPUSwapChainVulkan::Present(bool vsync) acquiredBackBuffer.SubmitCmdBuffer = context->GetCmdBufferManager()->GetActiveCmdBuffer(); context->GetCmdBufferManager()->SubmitActiveCmdBuffer(_backBuffers[_acquiredImageIndex].RenderingDoneSemaphore); + acquiredBackBuffer.SubmitCmdBufferFenceCounter = acquiredBackBuffer.SubmitCmdBuffer->GetSubmittedFenceCounter(); // Present the back buffer to the viewport window const auto result = TryPresent(DoPresent, _device->PresentQueue, true); diff --git a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.h b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.h index 638c16bea..87ef6fb07 100644 --- a/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.h +++ b/Source/Engine/GraphicsDevice/Vulkan/GPUSwapChainVulkan.h @@ -34,6 +34,11 @@ public: /// CmdBufferVulkan* SubmitCmdBuffer = nullptr; + /// + /// The fence counter value for SubmitCmdBuffer at the time it was submitted. + /// + uint64 SubmitCmdBufferFenceCounter = 0; + /// /// The render target surface handle. /// @@ -41,6 +46,7 @@ public: public: void Setup(GPUSwapChainVulkan* window, VkImage backbuffer, PixelFormat format, VkExtent3D extent); + void WaitForSubmit(); void Release(); public: diff --git a/Source/Engine/Platform/iOS/iOSPlatform.cpp b/Source/Engine/Platform/iOS/iOSPlatform.cpp index a6c57c587..6eb1a3fe2 100644 --- a/Source/Engine/Platform/iOS/iOSPlatform.cpp +++ b/Source/Engine/Platform/iOS/iOSPlatform.cpp @@ -350,7 +350,13 @@ MessagePipeline MainThreadPipeline; // Create UI thread update callback self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(UIThreadMain)]; - self.displayLink.preferredFramesPerSecond = 60; + const int32 targetFrameRate = 60; +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 150000 + if (@available(iOS 15.0, *)) + self.displayLink.preferredFrameRateRange = CAFrameRateRangeMake(targetFrameRate, targetFrameRate, targetFrameRate); + else +#endif + self.displayLink.preferredFramesPerSecond = targetFrameRate; [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; // Run engine on a separate main thread diff --git a/Source/Platforms/iOS/Binaries/Project/FlaxGame.xcodeproj/project.pbxproj b/Source/Platforms/iOS/Binaries/Project/FlaxGame.xcodeproj/project.pbxproj index 047dde664..e5644554b 100644 --- a/Source/Platforms/iOS/Binaries/Project/FlaxGame.xcodeproj/project.pbxproj +++ b/Source/Platforms/iOS/Binaries/Project/FlaxGame.xcodeproj/project.pbxproj @@ -295,6 +295,7 @@ ${PBXResourcesGroup} GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = FlaxGame/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "${ProjectName}"; + INFOPLIST_KEY_CADisableMinimumFrameDurationOnPhone = YES; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.games"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; @@ -336,6 +337,7 @@ ${PBXResourcesGroup} GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = FlaxGame/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "${ProjectName}"; + INFOPLIST_KEY_CADisableMinimumFrameDurationOnPhone = YES; INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.games"; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; diff --git a/Source/Platforms/iOS/Binaries/Project/FlaxGame/Info.plist b/Source/Platforms/iOS/Binaries/Project/FlaxGame/Info.plist index 99eb6f55c..1095d1e5a 100644 --- a/Source/Platforms/iOS/Binaries/Project/FlaxGame/Info.plist +++ b/Source/Platforms/iOS/Binaries/Project/FlaxGame/Info.plist @@ -2,6 +2,8 @@ + CADisableMinimumFrameDurationOnPhone + UIApplicationSceneManifest UIApplicationSupportsMultipleScenes