From 27b70758189d6932f86dca4b5b6953f0fdcfdf2a Mon Sep 17 00:00:00 2001 From: xiangsilian Date: Thu, 23 Apr 2026 22:18:22 +0800 Subject: [PATCH] fix bug and add log --- app/src/main/cpp/DebugLog.cpp | 91 ++++++++++++++++++++++---- app/src/main/cpp/DebugLog.h | 8 +++ app/src/main/cpp/main.cpp | 55 +++++++++------- vulkan/Application.cpp | 119 ++++++++++++++++++++++++++++++++-- vulkan/Application.h | 11 ++++ vulkan/FaceApp.cpp | 72 +++++++++++++++++++- vulkan/FaceApp.h | 11 ++++ 7 files changed, 327 insertions(+), 40 deletions(-) diff --git a/app/src/main/cpp/DebugLog.cpp b/app/src/main/cpp/DebugLog.cpp index 0815aac..c24881f 100644 --- a/app/src/main/cpp/DebugLog.cpp +++ b/app/src/main/cpp/DebugLog.cpp @@ -8,7 +8,10 @@ #include #include #include +#include #include +#include +#include namespace { @@ -17,10 +20,43 @@ FILE* g_fp = nullptr; std::string g_path; constexpr size_t kMaxSize = 2 * 1024 * 1024; // 2 MB before rotation +// Throttle bookkeeping: one slot per distinct key passed to log_throttled(). +struct ThrottleSlot { + int64_t last_emit_ms = 0; // when we last actually wrote a line for this key + uint32_t suppressed = 0; // number of calls swallowed since last_emit_ms +}; +std::unordered_map g_throttle; +constexpr int64_t kThrottleWindowMs = 2000; // 2 seconds + pid_t currentTid() { return static_cast(syscall(SYS_gettid)); } +int64_t nowMs() { + timespec ts{}; + clock_gettime(CLOCK_MONOTONIC, &ts); + return static_cast(ts.tv_sec) * 1000 + ts.tv_nsec / 1000000; +} + +// Assumes g_mtx is held. Writes a single pre-formatted line to disk + logcat. +void writeLineLocked(const char* body) { + pid_t tid = currentTid(); + + timespec ts{}; + clock_gettime(CLOCK_REALTIME, &ts); + struct tm tm_info{}; + localtime_r(&ts.tv_sec, &tm_info); + char timebuf[32]; + strftime(timebuf, sizeof(timebuf), "%H:%M:%S", &tm_info); + + if (g_fp) { + std::fprintf(g_fp, "[%s.%03ld][tid=%d] %s\n", + timebuf, ts.tv_nsec / 1000000, tid, body); + std::fflush(g_fp); + } + __android_log_print(ANDROID_LOG_INFO, "FACE_DBG", "[tid=%d] %s", tid, body); +} + } // namespace namespace DebugLog { @@ -67,22 +103,53 @@ void log(const char* fmt, ...) { va_end(ap); if (n < 0) return; - pid_t tid = currentTid(); + std::lock_guard lk(g_mtx); + writeLineLocked(buf); +} - timespec ts{}; - clock_gettime(CLOCK_REALTIME, &ts); - struct tm tm_info{}; - localtime_r(&ts.tv_sec, &tm_info); - char timebuf[32]; - strftime(timebuf, sizeof(timebuf), "%H:%M:%S", &tm_info); +void log_throttled(const char* key, const char* fmt, ...) { + char buf[1024]; + va_list ap; + va_start(ap, fmt); + int n = std::vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + if (n < 0) return; + + const char* k = key ? key : ""; + int64_t now = nowMs(); std::lock_guard lk(g_mtx); - if (g_fp) { - std::fprintf(g_fp, "[%s.%03ld][tid=%d] %s\n", - timebuf, ts.tv_nsec / 1000000, tid, buf); - std::fflush(g_fp); + auto it = g_throttle.find(k); + if (it == g_throttle.end()) { + // First time we see this key -> always log. + ThrottleSlot slot; + slot.last_emit_ms = now; + slot.suppressed = 0; + g_throttle.emplace(k, slot); + writeLineLocked(buf); + return; } - __android_log_print(ANDROID_LOG_INFO, "FACE_DBG", "[tid=%d] %s", tid, buf); + + ThrottleSlot& slot = it->second; + int64_t elapsed = now - slot.last_emit_ms; + if (elapsed < kThrottleWindowMs) { + // Still inside the 2s window -> swallow. + ++slot.suppressed; + return; + } + + // Window elapsed: emit the line, annotated with how many we swallowed. + if (slot.suppressed > 0) { + char annotated[1200]; + std::snprintf(annotated, sizeof(annotated), + "%s (repeated %u times in last %lldms, key=%s)", + buf, slot.suppressed, (long long)elapsed, k); + writeLineLocked(annotated); + } else { + writeLineLocked(buf); + } + slot.last_emit_ms = now; + slot.suppressed = 0; } void flush() { diff --git a/app/src/main/cpp/DebugLog.h b/app/src/main/cpp/DebugLog.h index 49ce238..6691588 100644 --- a/app/src/main/cpp/DebugLog.h +++ b/app/src/main/cpp/DebugLog.h @@ -14,6 +14,14 @@ void init(const char* internalDataPath); // so even a hard crash will preserve the tail. void log(const char* fmt, ...) __attribute__((format(printf, 1, 2))); +// Throttled variant keyed by a caller-supplied string. +// - The first call with a given key is always written. +// - Subsequent calls with the same key within 2 seconds are suppressed. +// - When the throttle window elapses, the next matching call is written with a +// "(repeated N times in last Xms)" suffix describing the suppressed ones. +// Use stable, short keys (e.g. "drawFrame.acquire", "drawFrame.present"). +void log_throttled(const char* key, const char* fmt, ...) __attribute__((format(printf, 2, 3))); + // Force flush to disk. void flush(); diff --git a/app/src/main/cpp/main.cpp b/app/src/main/cpp/main.cpp index 88ce0cd..7a0bc36 100644 --- a/app/src/main/cpp/main.cpp +++ b/app/src/main/cpp/main.cpp @@ -20,7 +20,6 @@ static inline pid_t dbg_tid() { static const char* appCmdName(int32_t cmd) { switch (cmd) { - case APP_CMD_INPUT_CHANGED: return "APP_CMD_INPUT_CHANGED"; case APP_CMD_INIT_WINDOW: return "APP_CMD_INIT_WINDOW"; case APP_CMD_TERM_WINDOW: return "APP_CMD_TERM_WINDOW"; case APP_CMD_WINDOW_RESIZED: return "APP_CMD_WINDOW_RESIZED"; @@ -54,14 +53,18 @@ void handle_cmd(android_app *pApp, int32_t cmd) { switch (cmd) { case APP_CMD_INIT_WINDOW: aout << "APP_CMD_INIT_WINDOW" << std::endl; - DebugLog::log("handle_cmd APP_CMD_INIT_WINDOW: calling initVulkan()"); - g_Application->initVulkan(); - DebugLog::log("handle_cmd APP_CMD_INIT_WINDOW: initVulkan() returned, isInited=%d", + DebugLog::log("handle_cmd APP_CMD_INIT_WINDOW: calling onWindowInit()"); + g_Application->onWindowInit(); + DebugLog::log("handle_cmd APP_CMD_INIT_WINDOW: onWindowInit() returned, isInited=%d", (int)g_Application->isInited()); break; case APP_CMD_TERM_WINDOW: aout << "APP_CMD_TERM_WINDOW" << std::endl; - DebugLog::log("handle_cmd APP_CMD_TERM_WINDOW: (no handler yet, swapChain/surface still point to dead ANativeWindow)"); + DebugLog::log("handle_cmd APP_CMD_TERM_WINDOW: calling onWindowLost()"); + if (g_Application != nullptr) { + g_Application->onWindowLost(); + } + DebugLog::log("handle_cmd APP_CMD_TERM_WINDOW: onWindowLost() returned"); break; default: break; @@ -116,6 +119,11 @@ void android_main(struct android_app *pApp) { } } } + // isInited() checks _applicationInited && _faceAppInited && _sceondInited, + // so while the window is gone (between TERM_WINDOW and the next + // INIT_WINDOW) this is false and we skip drawFrame entirely — no + // exceptions, no log spam. The outer sleep at the bottom already + // caps the loop frequency. if(g_Application->isInited()) { auto frameTime = (start_time - _lastDrawFrameTime); @@ -123,7 +131,6 @@ void android_main(struct android_app *pApp) { static uint64_t s_frameIdx = 0; static uint64_t s_excCount = 0; - static uint64_t s_lastLoggedExc = 0; ++s_frameIdx; if (s_frameIdx % 600 == 0) { DebugLog::log("heartbeat: frame=%llu exceptions_so_far=%llu", @@ -135,22 +142,17 @@ void android_main(struct android_app *pApp) { g_Application->drawFrame(frameTime); } catch (const std::exception& e) { ++s_excCount; - // Log first 10 exceptions verbosely, then at most one per 120 swallowed. - if (s_excCount <= 10 || (s_excCount - s_lastLoggedExc) >= 120) { - DebugLog::log("!!! drawFrame std::exception (frame=%llu count=%llu): %s", - (unsigned long long)s_frameIdx, - (unsigned long long)s_excCount, - e.what()); - s_lastLoggedExc = s_excCount; - } + DebugLog::log_throttled("main.drawFrame.stdexc", + "!!! drawFrame std::exception (frame=%llu count=%llu): %s", + (unsigned long long)s_frameIdx, + (unsigned long long)s_excCount, + e.what()); } catch (...) { ++s_excCount; - if (s_excCount <= 10 || (s_excCount - s_lastLoggedExc) >= 120) { - DebugLog::log("!!! drawFrame unknown exception (frame=%llu count=%llu)", - (unsigned long long)s_frameIdx, - (unsigned long long)s_excCount); - s_lastLoggedExc = s_excCount; - } + DebugLog::log_throttled("main.drawFrame.unknown", + "!!! drawFrame unknown exception (frame=%llu count=%llu)", + (unsigned long long)s_frameIdx, + (unsigned long long)s_excCount); } } auto end_time = chrono::duration_cast(chrono::system_clock::now().time_since_epoch()).count(); @@ -163,9 +165,11 @@ void android_main(struct android_app *pApp) { this_thread::sleep_for(chrono::milliseconds((35-function_time))); } } while (!pApp->destroyRequested); + DebugLog::log("android_main: destroyRequested, running cleanup"); //application.cleanup(); g_Application->cleanupSecondInit(); g_Application->clearnSecondFaceApp(); + DebugLog::log("android_main: exit"); } } @@ -228,9 +232,9 @@ Java_com_hmwl_face_1sdk_FaceActivity_passDataToNative(JNIEnv *env, jobject thiz, extern "C" JNIEXPORT void JNICALL Java_com_hmwl_face_1sdk_FaceActivity_SetCppInitArg(JNIEnv *env, jobject thiz, jstring json) { - // TODO: implement SetCppInitArg() const char *nativeString = env->GetStringUTFChars(json, nullptr); jsize len = env->GetStringUTFLength(json); + DebugLog::log("JNI SetCppInitArg tid=%d len=%d", dbg_tid(), (int)len); if(len > ArgLen) { aout << "ArgLen to long:" << len << std::endl; @@ -242,6 +246,7 @@ Java_com_hmwl_face_1sdk_FaceActivity_SetCppInitArg(JNIEnv *env, jobject thiz, js extern "C" JNIEXPORT void JNICALL Java_com_hmwl_face_1sdk_FaceActivity_StopRunning(JNIEnv *env, jobject thiz) { + DebugLog::log("JNI StopRunning tid=%d", dbg_tid()); g_Application->Stop(); } @@ -377,6 +382,7 @@ extern "C" JNIEXPORT jstring JNICALL Java_com_hmwl_face_1sdk_FaceActivity_PreReadAction(JNIEnv *env, jobject thiz, jstring motion, jobject callback) { + DebugLog::log("JNI PreReadAction tid=%d", dbg_tid()); // 清理旧的全局引用 if (g_callback != nullptr) { env->DeleteGlobalRef(g_callback); @@ -425,9 +431,11 @@ extern "C" JNIEXPORT void JNICALL Java_com_hmwl_face_1sdk_FaceActivity_ChangeMotionCpp(JNIEnv *env, jobject thiz, jstring json, jobject callback, jboolean loop) { - // TODO: implement ChangeMotionCpp() const char *nativeString = env->GetStringUTFChars(json, nullptr); jsize len = env->GetStringUTFLength(json); + DebugLog::log_throttled("JNI.ChangeMotionCpp", + "JNI ChangeMotionCpp tid=%d len=%d loop=%d", + dbg_tid(), (int)len, (int)loop); if(len > ArgLen) { aout << "ArgLen to long:" << len << std::endl; @@ -450,11 +458,12 @@ Java_com_hmwl_face_1sdk_FaceActivity_ChangeMotionCpp(JNIEnv *env, jobject thiz, extern "C" JNIEXPORT void JNICALL Java_com_hmwl_face_1sdk_FaceActivity_StopMotionNative(JNIEnv *env, jobject thiz) { + DebugLog::log("JNI StopMotionNative tid=%d", dbg_tid()); g_Application->StopMotion(); } extern "C" JNIEXPORT void JNICALL Java_com_hmwl_face_1sdk_FaceActivity_ResumeMotionNative(JNIEnv *env, jobject thiz) { - // TODO: implement ResumeMotionNative() + DebugLog::log("JNI ResumeMotionNative tid=%d", dbg_tid()); g_Application->ResumeMotion(); } \ No newline at end of file diff --git a/vulkan/Application.cpp b/vulkan/Application.cpp index f9d6170..61f4bc9 100644 --- a/vulkan/Application.cpp +++ b/vulkan/Application.cpp @@ -148,6 +148,105 @@ void Application::cleanupSecondInit() _sceondInited = false; } +void Application::cleanupForWindowLost() +{ + if (!_applicationInited || !_sceondInited) { + FACE_DBG_LOG("cleanupForWindowLost: skip (_applicationInited=%d _sceondInited=%d)", + (int)_applicationInited, (int)_sceondInited); + return; + } + FACE_DBG_LOG("cleanupForWindowLost: begin (imageCount=%zu MAX_FRAMES_IN_FLIGHT=%d)", + swapChainImages.size(), MAX_FRAMES_IN_FLIGHT); + + // Block until GPU is no longer using any of the resources we are about + // to destroy. Anything weaker than this risks hitting VK_ERROR_DEVICE_LOST + // on drivers that don't tolerate destroying in-flight resources. + vkDeviceWaitIdle(device); + + for (auto framebuffer : swapChainFramebuffers) { + if (framebuffer != VK_NULL_HANDLE) { + vkDestroyFramebuffer(device, framebuffer, nullptr); + } + } + swapChainFramebuffers.clear(); + + for (auto imageView : swapChainImageViews) { + if (imageView != VK_NULL_HANDLE) { + vkDestroyImageView(device, imageView, nullptr); + } + } + swapChainImageViews.clear(); + + // Free the command buffers allocated from commandPool. The pool itself is + // kept alive so textures/staging uploads that run concurrently off the + // render thread (via commandPool_ex) aren't disrupted. + if (!commandBuffers.empty()) { + vkFreeCommandBuffers(device, commandPool, + (uint32_t)commandBuffers.size(), commandBuffers.data()); + commandBuffers.clear(); + } + + for (size_t i = 0; i < imageAvailableSemaphores.size(); ++i) { + if (imageAvailableSemaphores[i] != VK_NULL_HANDLE) { + vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr); + } + } + imageAvailableSemaphores.clear(); + for (size_t i = 0; i < renderFinishedSemaphores.size(); ++i) { + if (renderFinishedSemaphores[i] != VK_NULL_HANDLE) { + vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr); + } + } + renderFinishedSemaphores.clear(); + for (size_t i = 0; i < inFlightFences.size(); ++i) { + if (inFlightFences[i] != VK_NULL_HANDLE) { + vkDestroyFence(device, inFlightFences[i], nullptr); + } + } + inFlightFences.clear(); + imagesInFlight.clear(); + + if (swapChain != VK_NULL_HANDLE) { + vkDestroySwapchainKHR(device, swapChain, nullptr); + swapChain = VK_NULL_HANDLE; + } + swapChainImages.clear(); + + if (surface != VK_NULL_HANDLE) { + vkDestroySurfaceKHR(instance, surface, nullptr); + surface = VK_NULL_HANDLE; + } + + currentFrame = 0; + _sceondInited = false; + FACE_DBG_LOG("cleanupForWindowLost: done"); +} + +void Application::reinitForNewWindow() +{ + if (!_applicationInited) { + FACE_DBG_LOG("reinitForNewWindow: skip, _applicationInited=0 (must go through initVulkan first)"); + return; + } + if (_sceondInited) { + FACE_DBG_LOG("reinitForNewWindow: skip, already _sceondInited=1"); + return; + } + FACE_DBG_LOG("reinitForNewWindow: begin"); + + createSurface(); + createSwapChain(); + createImageViews(); + createFramebuffers(); + createCommandBuffer(); + createSyncObjects(); + + _sceondInited = true; + FACE_DBG_LOG("reinitForNewWindow: done (imageCount=%zu extent=%ux%u MAX_FRAMES_IN_FLIGHT=%d)", + swapChainImages.size(), swapChainExtent.width, swapChainExtent.height, + MAX_FRAMES_IN_FLIGHT); +} + void Application::createImageViews() { @@ -590,18 +689,24 @@ void Application::drawFrame(long long frameTime) // 1. 等待前一帧完成 VkResult waitRes = vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX); if (waitRes != VK_SUCCESS) { - FACE_DBG_LOG("drawFrame[%llu] vkWaitForFences -> %d (%s) currentFrame=%u", +#ifndef _WIN32 + DebugLog::log_throttled("drawFrame.waitFences", + "drawFrame[%llu] vkWaitForFences -> %d (%s) currentFrame=%u", (unsigned long long)s_drawFrameCount, (int)waitRes, VkResultStr(waitRes), currentFrame); +#endif } // 2. 获取交换链图像 uint32_t imageIndex; VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex); if (result != VK_SUCCESS) { - FACE_DBG_LOG("drawFrame[%llu] vkAcquireNextImageKHR -> %d (%s) currentFrame=%u", +#ifndef _WIN32 + DebugLog::log_throttled("drawFrame.acquire", + "drawFrame[%llu] vkAcquireNextImageKHR -> %d (%s) currentFrame=%u", (unsigned long long)s_drawFrameCount, (int)result, VkResultStr(result), currentFrame); +#endif throw std::runtime_error("failed to acquire swap chain image!"); } @@ -634,10 +739,13 @@ void Application::drawFrame(long long frameTime) VkResult submitRes = vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]); if (submitRes != VK_SUCCESS) { - FACE_DBG_LOG("drawFrame[%llu] vkQueueSubmit -> %d (%s) currentFrame=%u imageIndex=%u", +#ifndef _WIN32 + DebugLog::log_throttled("drawFrame.submit", + "drawFrame[%llu] vkQueueSubmit -> %d (%s) currentFrame=%u imageIndex=%u", (unsigned long long)s_drawFrameCount, (int)submitRes, VkResultStr(submitRes), currentFrame, imageIndex); +#endif throw std::runtime_error("failed to submit draw command buffer!"); } // 7. 呈现图像 @@ -652,10 +760,13 @@ void Application::drawFrame(long long frameTime) result = vkQueuePresentKHR(presentQueue, &presentInfo); if (result != VK_SUCCESS) { - FACE_DBG_LOG("drawFrame[%llu] vkQueuePresentKHR -> %d (%s) currentFrame=%u imageIndex=%u", +#ifndef _WIN32 + DebugLog::log_throttled("drawFrame.present", + "drawFrame[%llu] vkQueuePresentKHR -> %d (%s) currentFrame=%u imageIndex=%u", (unsigned long long)s_drawFrameCount, (int)result, VkResultStr(result), currentFrame, imageIndex); +#endif throw std::runtime_error("failed to present swap chain image!"); } diff --git a/vulkan/Application.h b/vulkan/Application.h index 7ea5d2e..ad7d62e 100644 --- a/vulkan/Application.h +++ b/vulkan/Application.h @@ -81,6 +81,17 @@ public: bool _applicationInited = false; void cleanupSecondInit(); + // Tear down only the window-dependent Vulkan objects so we can survive an + // Android APP_CMD_TERM_WINDOW (screen off / background / rotate). + // Keeps renderPass / pipelines / VMA / textures / FaceApp resources intact, + // so pipelines created by FaceApp remain valid for the new swapchain. + // Caller MUST hold any app-level mutexes that serialize JNI -> Vulkan access. + void cleanupForWindowLost(); + + // Rebuild the window-dependent Vulkan objects torn down above. + // Safe to call only after cleanupForWindowLost(). + void reinitForNewWindow(); + protected: void loadTexture(std::string path, Texture& tex, bool srgb, VkCommandPool pool); void loadTexture(std::vector& image_data, size_t image_size, int w, int h, Texture& tex, bool srgb, VkCommandPool pool, std::string path); diff --git a/vulkan/FaceApp.cpp b/vulkan/FaceApp.cpp index 56e45eb..459d384 100644 --- a/vulkan/FaceApp.cpp +++ b/vulkan/FaceApp.cpp @@ -32,11 +32,14 @@ FaceApp::~FaceApp() void FaceApp::Stop() { + FACE_DBG_LOG("FaceApp::Stop called (_running=%d worker_joinable=%d)", + (int)_running, (int)worker_.joinable()); _running = false; if(worker_.joinable()) { worker_.join(); } + FACE_DBG_LOG("FaceApp::Stop done"); } void ReceiveFacePoint(float* pos, int pointCount, int width, int height) @@ -858,14 +861,70 @@ void FaceApp::initVulkan() void FaceApp::clearnSecondFaceApp() { + FACE_DBG_LOG("FaceApp::clearnSecondFaceApp: _secondfaceAppInited %d -> 0", + (int)_secondfaceAppInited); _secondfaceAppInited = false; } void FaceApp::Start() { + FACE_DBG_LOG("FaceApp::Start: _running %d -> 1", (int)_running); _running = true; } +void FaceApp::onWindowLost() +{ + FACE_DBG_LOG("FaceApp::onWindowLost enter _applicationInited=%d _faceAppInited=%d _sceondInited=%d _secondfaceAppInited=%d _running=%d", + (int)_applicationInited, (int)_faceAppInited, (int)_sceondInited, + (int)_secondfaceAppInited, (int)_running); + + // Stop the render loop from touching Vulkan while we tear down. + _running = false; + + // Serialize against JNI callbacks that may concurrently submit GPU work + // via commandPool / commandPool_ex (processImageNative -> update texture, + // passDataToNative -> update vertex buffer, changeMotionList). + std::unique_lock lk_point(mtx_point); + std::unique_lock lk_motion(changeMotionMtx); + std::unique_lock lk_tex(createTextureMtx); + + Application::cleanupForWindowLost(); + _secondfaceAppInited = false; + + FACE_DBG_LOG("FaceApp::onWindowLost done"); +} + +void FaceApp::onWindowInit() +{ + FACE_DBG_LOG("FaceApp::onWindowInit enter _applicationInited=%d _faceAppInited=%d _sceondInited=%d _secondfaceAppInited=%d", + (int)_applicationInited, (int)_faceAppInited, (int)_sceondInited, + (int)_secondfaceAppInited); + + if (!_applicationInited) { + // First-time path: go through the full initVulkan pipeline. + initVulkan(); + } else { + // Recovery path after an earlier onWindowLost. Rebuild only the + // window-dependent Vulkan objects; keep renderPass / pipelines / + // FaceApp GPU resources intact. + std::unique_lock lk_point(mtx_point); + std::unique_lock lk_motion(changeMotionMtx); + std::unique_lock lk_tex(createTextureMtx); + + Application::reinitForNewWindow(); + + if (!_secondfaceAppInited) { + _secondfaceAppInited = true; + _playMotion = true; + _running = true; + } + } + + FACE_DBG_LOG("FaceApp::onWindowInit done _applicationInited=%d _faceAppInited=%d _sceondInited=%d _secondfaceAppInited=%d _running=%d", + (int)_applicationInited, (int)_faceAppInited, (int)_sceondInited, + (int)_secondfaceAppInited, (int)_running); +} + void FaceApp::update_uniform_buffers() { uint32_t width = 480; @@ -1340,6 +1399,7 @@ void FaceApp::cleanupResources(VkDevice device, VmaAllocator allocator) { void FaceApp::cleanup() { + FACE_DBG_LOG("FaceApp::cleanup enter"); vkDeviceWaitIdle(device); if (uniform_buffer_mapped != nullptr) @@ -1375,7 +1435,7 @@ void FaceApp::cleanup() // allocator = VK_NULL_HANDLE; //} - + FACE_DBG_LOG("FaceApp::cleanup done"); } @@ -1481,6 +1541,8 @@ void FaceApp::drawFrame(long long frameTime) string FaceApp::preLoadMotionList(string motion_list_str, Callback callback) { + FACE_DBG_LOG("FaceApp::preLoadMotionList called str_len=%zu _isLoadMotion=%d", + motion_list_str.size(), (int)_isLoadMotion); if (_isLoadMotion) { return "failue load not finished"; @@ -1496,11 +1558,13 @@ string FaceApp::preLoadMotionList(string motion_list_str, Callback callback) worker_.join(); } worker_ = std::thread(&FaceApp::loadMotionThread, this); + FACE_DBG_LOG("FaceApp::preLoadMotionList worker_ started, todo_count=%zu", _curLoadMotionList.size()); return "ok"; } void FaceApp::loadMotionThread() { + FACE_DBG_LOG("FaceApp::loadMotionThread enter, todo_count=%zu", _curLoadMotionList.size()); while (!isInited()) { std::this_thread::sleep_for(std::chrono::milliseconds(50)); @@ -1561,6 +1625,7 @@ void FaceApp::loadMotionThread() // } update_descriptor_set(m_texs_left, m_descriptor_sets_left); _isLoadMotion = false; + FACE_DBG_LOG("FaceApp::loadMotionThread done, loaded_total=%zu", motion_list_map.size()); _callback_loadfinish(); } @@ -1572,6 +1637,9 @@ Motion FaceApp::getMotionByName(string name) void FaceApp::changeMotionList(vector motions, AnimationFinishedCallback callback, bool loop) { + DebugLog::log_throttled("FaceApp.changeMotionList", + "FaceApp::changeMotionList called count=%zu loop=%d _isLoadMotion=%d", + motions.size(), (int)loop, (int)_isLoadMotion); if (_isLoadMotion) { return; @@ -1623,10 +1691,12 @@ void FaceApp::changeMotionList(vector motions, AnimationFinishedCallback } void FaceApp::StopMotion() { + FACE_DBG_LOG("FaceApp::StopMotion: _playMotion %d -> 0", (int)_playMotion); _playMotion = false; } void FaceApp::ResumeMotion() { + FACE_DBG_LOG("FaceApp::ResumeMotion: _playMotion %d -> 1", (int)_playMotion); _playMotion = true; } diff --git a/vulkan/FaceApp.h b/vulkan/FaceApp.h index f6df3c7..244d903 100644 --- a/vulkan/FaceApp.h +++ b/vulkan/FaceApp.h @@ -182,6 +182,17 @@ public: void clearnSecondFaceApp(); void Start(); void Stop(); + + // Called from main thread in response to APP_CMD_TERM_WINDOW. + // Serializes against JNI callbacks (processImageNative / passDataToNative / + // changeMotionList) by taking all three FaceApp mutexes + createTextureMtx, + // then tears down window-dependent Vulkan objects via Application. + void onWindowLost(); + + // Called from main thread in response to APP_CMD_INIT_WINDOW. + // Does the full first-time initVulkan() on the very first call, and a + // lightweight swapchain/surface rebuild on subsequent calls. + void onWindowInit(); void loadMotionThread(); std::thread worker_; bool _isLoadMotion = false;