2 Commits
2 changed files with 81 additions and 9 deletions
+31 -1
View File
@@ -191,8 +191,38 @@ void android_main(struct android_app *pApp) {
}
} while (!pApp->destroyRequested);
DebugLog::log("android_main: destroyRequested, running cleanup");
// ⚠️ 不要在这里调 cleanupSecondInit()。
//
// 在 Android 上,Activity 销毁(APP_CMD_DESTROY) 之前已经先收到了
// APP_CMD_TERM_WINDOWhandle_cmd 里走的是 onWindowLost()
// → Application::cleanupForWindowLost():销毁 swapChain / surface /
// framebuffers / imageViews / commandBuffers / 同步对象,但故意保留
// renderPass / pipelineLayout / graphicsPipeline,以便下一次进入
// Activity 时 reinitForNewWindow() 在 extent/format 不变的情况下
// 直接复用,省下重建 RenderPass / 重新编译 Pipeline 的开销。
//
// 而 cleanupSecondInit() 是 Windows 路径
// (mainLoop → cleanupSecondInit → initVulkan → mainLoop) 的对偶——
// 它会把 renderPass / pipelineLayout / graphicsPipeline 全部销毁。
// 又因为 g_Application 是一个跨 Activity 存活的全局 FaceApp 实例
// (android_main 入口处只在 g_Application == nullptr 时才 new)
// _applicationInited 也不会被复位;
// 下一次同一进程内进入另一个继承自 FaceActivity 的 Activity 时,
// onWindowInit() 看到 _applicationInited=1 走恢复分支调用
// reinitForNewWindow()extent/format 与上次相同(480x480 / 同 format)
// 时**不会**重建 renderPass / pipelineLayout / graphicsPipeline——
// 用着已经被销毁的 renderPass 句柄继续 createFramebuffers() →
// recordCommandBuffer() → vkCmdBeginRenderPass()
// Mali 驱动 (libGLES_mali) 在校验层后解引用已释放的 RenderPass 内部
// 数据,触发 SIGSEGV / si_addr=0x4
// (recordCommandBuffer + 0xBC,正好是 vkCmdBeginRenderPass)。
//
// 解决办法:android_main 退出时只信任 cleanupForWindowLost 已经做的
// 那部分清理,不再额外调 cleanupSecondInit(),让 renderPass /
// pipelineLayout / graphicsPipeline / FaceApp 的 m_graphicsPipeline*
// 都能存活到下一次 android_main 重新进来被复用。
//application.cleanup();
g_Application->cleanupSecondInit();
//g_Application->cleanupSecondInit();
g_Application->clearnSecondFaceApp();
DebugLog::log("android_main: exit");
}
+42
View File
@@ -136,14 +136,55 @@ void Application::initVulkan()
void Application::cleanupSecondInit()
{
// ★ 幂等:销毁后立刻把句柄置 VK_NULL_HANDLE / 清空 vector。
//
// 这个函数过去在 Android 路径 (android_main 退出) 上被错误地调用过;
// 由于销毁后没有清零句柄,紧接着重新进入的 reinitForNewWindow() 在
// extent/format 不变时会跳过重建,让 framebuffer / drawFrame 在
// 已经销毁的 renderPass 上继续工作,最终在
// vkCmdBeginRenderPass 内部 (libGLES_mali) 触发 SIGSEGV。
//
// 现在 android_main 退出路径已经不再调本函数;本函数只剩 Windows
// 路径 (mainLoop → cleanupSecondInit → initVulkan 的"二次 session"
// 复用) 在用。把它写成幂等的可以兜住将来任何对它的误用。
FACE_DBG_LOG("cleanupSecondInit: begin (swapChain=%s surface=%s renderPass=%s pipelineLayout=%s graphicsPipeline=%s imageViews=%zu framebuffers=%zu)",
swapChain == VK_NULL_HANDLE ? "null" : "live",
surface == VK_NULL_HANDLE ? "null" : "live",
renderPass == VK_NULL_HANDLE ? "null" : "live",
pipelineLayout == VK_NULL_HANDLE ? "null" : "live",
graphicsPipeline == VK_NULL_HANDLE ? "null" : "live",
swapChainImageViews.size(), swapChainFramebuffers.size());
if (device != VK_NULL_HANDLE) {
vkDeviceWaitIdle(device);
}
if (swapChain != VK_NULL_HANDLE) {
vkDestroySwapchainKHR(device, swapChain, nullptr);
swapChain = VK_NULL_HANDLE;
}
if (surface != VK_NULL_HANDLE) {
vkDestroySurfaceKHR(instance, surface, nullptr);
surface = VK_NULL_HANDLE;
}
if (graphicsPipeline != VK_NULL_HANDLE) {
vkDestroyPipeline(device, graphicsPipeline, nullptr);
graphicsPipeline = VK_NULL_HANDLE;
}
if (pipelineLayout != VK_NULL_HANDLE) {
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
pipelineLayout = VK_NULL_HANDLE;
}
if (renderPass != VK_NULL_HANDLE) {
vkDestroyRenderPass(device, renderPass, nullptr);
renderPass = VK_NULL_HANDLE;
}
for (auto imageView : swapChainImageViews) {
if (imageView != VK_NULL_HANDLE) {
vkDestroyImageView(device, imageView, nullptr);
}
}
swapChainImageViews.clear();
for (auto framebuffer : swapChainFramebuffers)
{
if (framebuffer != VK_NULL_HANDLE)
@@ -152,6 +193,7 @@ void Application::cleanupSecondInit()
}
}
swapChainFramebuffers.clear();
swapChainImages.clear();
_sceondInited = false;
}