debug_fix_screen_change #2
@@ -8,7 +8,10 @@
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
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<std::string, ThrottleSlot> g_throttle;
|
||||
constexpr int64_t kThrottleWindowMs = 2000; // 2 seconds
|
||||
|
||||
pid_t currentTid() {
|
||||
return static_cast<pid_t>(syscall(SYS_gettid));
|
||||
}
|
||||
|
||||
int64_t nowMs() {
|
||||
timespec ts{};
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return static_cast<int64_t>(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<std::mutex> 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<std::mutex> 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() {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
+32
-23
@@ -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::milliseconds>(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();
|
||||
}
|
||||
+115
-4
@@ -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!");
|
||||
}
|
||||
|
||||
|
||||
@@ -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<unsigned char>& image_data, size_t image_size, int w, int h, Texture& tex, bool srgb, VkCommandPool pool, std::string path);
|
||||
|
||||
+71
-1
@@ -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<std::mutex> lk_point(mtx_point);
|
||||
std::unique_lock<std::mutex> lk_motion(changeMotionMtx);
|
||||
std::unique_lock<std::mutex> 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<std::mutex> lk_point(mtx_point);
|
||||
std::unique_lock<std::mutex> lk_motion(changeMotionMtx);
|
||||
std::unique_lock<std::mutex> 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<string> 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<string> 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user