mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-11 21:58:31 +09:00
[Fix] (DirectVulkan): suspend presentation while the window is zero-area - a minimized window's out-of-date swapchain used to keep Present submitting on a signaled fence and presenting never-acquired images (adversarial review); also drop logging from the process-detach abandon path
This commit is contained in:
+3
-2
@@ -95,15 +95,16 @@ namespace MobileGL {
|
|||||||
// inside a dying process - other threads have already been terminated,
|
// inside a dying process - other threads have already been terminated,
|
||||||
// so Vulkan/GLES cleanup crashes with the process half-dead. The process
|
// so Vulkan/GLES cleanup crashes with the process half-dead. The process
|
||||||
// is exiting; the OS reclaims everything.
|
// is exiting; the OS reclaims everything.
|
||||||
|
// No logging here: this runs under the loader lock after every other
|
||||||
|
// thread was terminated, where the log mutex/heap/stdio may be in any
|
||||||
|
// state. Plain pointer stores only.
|
||||||
void AbandonAtProcessExit() {
|
void AbandonAtProcessExit() {
|
||||||
MGLOG_I("MobileGL: process detach, abandoning global state");
|
|
||||||
MG_Backend::DirectVulkan::pVulkanRenderer.release();
|
MG_Backend::DirectVulkan::pVulkanRenderer.release();
|
||||||
MG_Backend::pActiveBackendObject.release();
|
MG_Backend::pActiveBackendObject.release();
|
||||||
MG_State::pGLContext.release();
|
MG_State::pGLContext.release();
|
||||||
MG_State::pEGLContext.release();
|
MG_State::pEGLContext.release();
|
||||||
MG_Impl::GLImpl::TextureImpl::pProxyTextureManager.release();
|
MG_Impl::GLImpl::TextureImpl::pProxyTextureManager.release();
|
||||||
MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo.release();
|
MG_Impl::GLImpl::FramebufferImpl::pDefaultFramebufferInfo.release();
|
||||||
MGLOG_I("MobileGL: global state abandoned");
|
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -6865,18 +6865,32 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::Present() {
|
void VulkanRenderer::Present() {
|
||||||
if (m_swapchainObject.GetHandle() == VK_NULL_HANDLE) {
|
if (m_swapchainObject.GetHandle() == VK_NULL_HANDLE || m_presentSuspended) {
|
||||||
// No swapchain yet (the window had a zero-area surface at initialization).
|
// No usable swapchain: the window was zero-area at initialization, or
|
||||||
// Try to bring one up now that the window may have a real size; if it is
|
// presentation was suspended when the window minimized. Try to bring a
|
||||||
// still zero-area there is nothing to present to.
|
// swapchain up now that the window may have a real size; until then, drop
|
||||||
RecreateSwapchain();
|
// this frame's recording instead of submitting - a submit would wait on a
|
||||||
if (m_swapchainObject.GetHandle() == VK_NULL_HANDLE) {
|
// never-signaled acquire semaphore and reuse a still-signaled fence.
|
||||||
MGLOG_D("Present skipped: still no swapchain (zero-area window)");
|
if (!RecreateSwapchain()) {
|
||||||
|
auto& suspendedFrame = m_frameContext.GetCurrent();
|
||||||
|
if (VkRenderPassManager::GetActiveRenderPass()) {
|
||||||
|
VkRenderPassManager::EndRenderPass(suspendedFrame.commandBuffer);
|
||||||
|
}
|
||||||
|
if (suspendedFrame.isCommandRecording) {
|
||||||
|
m_frameContext.EndCommandRecording();
|
||||||
|
}
|
||||||
|
suspendedFrame.isCommandRecording = false;
|
||||||
|
suspendedFrame.hasCommandBufferRecorded = false;
|
||||||
|
m_lastPipelineValid = false;
|
||||||
|
MGLOG_D("Present skipped: no usable swapchain (zero-area window)");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
m_presentSuspended = false;
|
||||||
const VkResult acquireResult =
|
const VkResult acquireResult =
|
||||||
m_frameContext.WaitAndAcquireNextImage(m_device, m_swapchainObject.GetHandle(), m_imageIndexAcquired);
|
m_frameContext.WaitAndAcquireNextImage(m_device, m_swapchainObject.GetHandle(), m_imageIndexAcquired);
|
||||||
VK_VERIFY(acquireResult, "Present, deferred first WaitAndAcquireNextImage");
|
if (acquireResult != VK_SUBOPTIMAL_KHR) {
|
||||||
|
VK_VERIFY(acquireResult, "Present, deferred first WaitAndAcquireNextImage");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
MOBILEGL_ASSERT(m_imageIndexAcquired < m_swapchainObject.GetImageCount(),
|
MOBILEGL_ASSERT(m_imageIndexAcquired < m_swapchainObject.GetImageCount(),
|
||||||
"Present, acquired image index out of range");
|
"Present, acquired image index out of range");
|
||||||
@@ -6911,14 +6925,26 @@ void main() {
|
|||||||
auto result = vkQueuePresentKHR(m_presentQueue, &presentPacket.presentInfo);
|
auto result = vkQueuePresentKHR(m_presentQueue, &presentPacket.presentInfo);
|
||||||
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
|
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
|
||||||
MGLOG_D("Present, vkQueuePresentKHR got %d, recreating swapchain", result);
|
MGLOG_D("Present, vkQueuePresentKHR got %d, recreating swapchain", result);
|
||||||
RecreateSwapchain();
|
if (!RecreateSwapchain()) {
|
||||||
|
// Window went zero-area (minimize) with the swapchain out of date:
|
||||||
|
// stop submitting/acquiring until it has a size again.
|
||||||
|
m_presentSuspended = true;
|
||||||
|
m_swapchainResizeRequested = false;
|
||||||
|
MGLOG_D("Present, zero-area window with out-of-date swapchain; suspending presentation");
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_swapchainResizeRequested = false;
|
m_swapchainResizeRequested = false;
|
||||||
result = VK_SUCCESS;
|
result = VK_SUCCESS;
|
||||||
}
|
}
|
||||||
VK_VERIFY(result, "Present, vkQueuePresentKHR");
|
VK_VERIFY(result, "Present, vkQueuePresentKHR");
|
||||||
if (m_swapchainResizeRequested) {
|
if (m_swapchainResizeRequested) {
|
||||||
MGLOG_D("Present, processing requested swapchain resize");
|
MGLOG_D("Present, processing requested swapchain resize");
|
||||||
RecreateSwapchain();
|
if (!RecreateSwapchain()) {
|
||||||
|
m_presentSuspended = true;
|
||||||
|
m_swapchainResizeRequested = false;
|
||||||
|
MGLOG_D("Present, zero-area window on requested resize; suspending presentation");
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_swapchainResizeRequested = false;
|
m_swapchainResizeRequested = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -6929,7 +6955,12 @@ void main() {
|
|||||||
result = m_frameContext.WaitAndAcquireNextImage(m_device, m_swapchainObject.GetHandle(), m_imageIndexAcquired);
|
result = m_frameContext.WaitAndAcquireNextImage(m_device, m_swapchainObject.GetHandle(), m_imageIndexAcquired);
|
||||||
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
|
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
|
||||||
MGLOG_D("Present, vkAcquireNextImageKHR got %d, recreating swapchain", result);
|
MGLOG_D("Present, vkAcquireNextImageKHR got %d, recreating swapchain", result);
|
||||||
RecreateSwapchain();
|
if (!RecreateSwapchain()) {
|
||||||
|
m_presentSuspended = true;
|
||||||
|
m_swapchainResizeRequested = false;
|
||||||
|
MGLOG_D("Present, zero-area window on next-frame acquire; suspending presentation");
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_swapchainResizeRequested = false;
|
m_swapchainResizeRequested = false;
|
||||||
result =
|
result =
|
||||||
m_frameContext.WaitAndAcquireNextImage(m_device, m_swapchainObject.GetHandle(), m_imageIndexAcquired);
|
m_frameContext.WaitAndAcquireNextImage(m_device, m_swapchainObject.GetHandle(), m_imageIndexAcquired);
|
||||||
@@ -7743,13 +7774,13 @@ void main() {
|
|||||||
m_swapchainObject.Shutdown(m_device);
|
m_swapchainObject.Shutdown(m_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanRenderer::RecreateSwapchain() {
|
Bool VulkanRenderer::RecreateSwapchain() {
|
||||||
// Handle cases like minimize on Windows, where swapchain could return a 0x0 extent
|
// Handle cases like minimize on Windows, where swapchain could return a 0x0 extent
|
||||||
const auto swapchainCapabilities =
|
const auto swapchainCapabilities =
|
||||||
SwapchainObject::GetSwapchainCapabilities(m_physicalDevice.handle, m_surface);
|
SwapchainObject::GetSwapchainCapabilities(m_physicalDevice.handle, m_surface);
|
||||||
if (swapchainCapabilities.capabilities.currentExtent.width == 0 ||
|
if (swapchainCapabilities.capabilities.currentExtent.width == 0 ||
|
||||||
swapchainCapabilities.capabilities.currentExtent.height == 0) {
|
swapchainCapabilities.capabilities.currentExtent.height == 0) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
vkDeviceWaitIdle(m_device);
|
vkDeviceWaitIdle(m_device);
|
||||||
@@ -7793,6 +7824,7 @@ void main() {
|
|||||||
m_bufferManager.BeginFrame(m_frameContext.GetCurrentFrameIndex());
|
m_bufferManager.BeginFrame(m_frameContext.GetCurrentFrameIndex());
|
||||||
m_convertedVertexStreams.clear();
|
m_convertedVertexStreams.clear();
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PhysicalDevice& VulkanRenderer::GetPhysicalDevice() const {
|
const PhysicalDevice& VulkanRenderer::GetPhysicalDevice() const {
|
||||||
|
|||||||
@@ -257,7 +257,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
Uint64 GetTimerQueryTimestampNs(const VkTimerQueryManager::TimestampRecord& record) const;
|
Uint64 GetTimerQueryTimestampNs(const VkTimerQueryManager::TimestampRecord& record) const;
|
||||||
|
|
||||||
void RequestSwapchainResize(Uint32 width, Uint32 height);
|
void RequestSwapchainResize(Uint32 width, Uint32 height);
|
||||||
void RecreateSwapchain();
|
// Returns false when the surface is zero-area (minimized/hidden window):
|
||||||
|
// no new swapchain is installed and presentation must stay suspended.
|
||||||
|
Bool RecreateSwapchain();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct BlitUniformData {
|
struct BlitUniformData {
|
||||||
@@ -355,6 +357,10 @@ namespace MobileGL::MG_Backend::DirectVulkan {
|
|||||||
void* m_platformCloseDisplay = nullptr;
|
void* m_platformCloseDisplay = nullptr;
|
||||||
VulkanRendererConfig m_config;
|
VulkanRendererConfig m_config;
|
||||||
Bool m_swapchainResizeRequested = false;
|
Bool m_swapchainResizeRequested = false;
|
||||||
|
// Presentation is suspended while the window is zero-area (minimized): the
|
||||||
|
// swapchain is unusable/out of date, so Present drops frames instead of
|
||||||
|
// submitting on a signaled fence / presenting never-acquired images.
|
||||||
|
Bool m_presentSuspended = false;
|
||||||
|
|
||||||
// Vulkan objects
|
// Vulkan objects
|
||||||
Bool m_validationLayersEnabled = false;
|
Bool m_validationLayersEnabled = false;
|
||||||
|
|||||||
Reference in New Issue
Block a user