// JavaEGL extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayRender(JNIEnv *env, jobject obj) { static bool hasSetThreadName = false; if (!hasSetThreadName) { hasSetThreadName = true; setCurrentThreadName("AndroidRender"); } if (renderer_inited) { NativeUpdate(); NativeRender(graphicsContext); time_update(); } else { ELOG("BAD: Ended up in nativeRender even though app has quit.%s", ""); // Shouldn't really get here. Let's draw magenta. // TODO: Should we have GL here? glDepthMask(GL_TRUE); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glClearColor(1.0, 0.0, 1.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } std::lock_guard<std::mutex> guard(frameCommandLock); if (!nativeActivity) { while (!frameCommands.empty()) frameCommands.pop(); return; } // Still under lock here. ProcessFrameCommands(env); }
void UpdateRunLoopAndroid(JNIEnv *env) { NativeUpdate(); NativeRender(graphicsContext); time_update(); std::lock_guard<std::mutex> guard(frameCommandLock); if (!nativeActivity) { while (!frameCommands.empty()) frameCommands.pop(); return; } // Still under lock here. ProcessFrameCommands(env); }
// JavaEGL extern "C" void Java_org_ppsspp_ppsspp_NativeRenderer_displayRender(JNIEnv *env, jobject obj) { static bool hasSetThreadName = false; if (!hasSetThreadName) { hasSetThreadName = true; setCurrentThreadName("AndroidRender"); } if (renderer_inited) { // TODO: Look into if these locks are a perf loss { lock_guard guard(input_state.lock); input_state.pad_lstick_x = left_joystick_x_async; input_state.pad_lstick_y = left_joystick_y_async; input_state.pad_rstick_x = right_joystick_x_async; input_state.pad_rstick_y = right_joystick_y_async; UpdateInputState(&input_state); } NativeUpdate(input_state); { lock_guard guard(input_state.lock); EndInputState(&input_state); } NativeRender(graphicsContext); time_update(); } else { ELOG("BAD: Ended up in nativeRender even though app has quit.%s", ""); // Shouldn't really get here. Let's draw magenta. // TODO: Should we have GL here? glDepthMask(GL_TRUE); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glClearColor(1.0, 0.0, 1.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); } lock_guard guard(frameCommandLock); if (!nativeActivity) { while (!frameCommands.empty()) frameCommands.pop(); return; } ProcessFrameCommands(env); }
extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(JNIEnv *env, jobject obj, jobject _surf) { // Needed for Vulkan, even if we're not using the old EGL path. exitRenderLoop = false; // This is up here to prevent race conditions, in case we pause during init. renderLoopRunning = true; ANativeWindow *wnd = _surf ? ANativeWindow_fromSurface(env, _surf) : nullptr; WLOG("runEGLRenderLoop. display_xres=%d display_yres=%d", display_xres, display_yres); if (wnd == nullptr) { ELOG("Error: Surface is null."); renderLoopRunning = false; return false; } retry: bool vulkan = g_Config.iGPUBackend == (int)GPUBackend::VULKAN; int tries = 0; if (!graphicsContext->InitFromRenderThread(wnd, desiredBackbufferSizeX, desiredBackbufferSizeY, backbuffer_format, androidVersion)) { ELOG("Failed to initialize graphics context."); if (!exitRenderLoop && (vulkan && tries < 2)) { ILOG("Trying again, this time with OpenGL."); g_Config.iGPUBackend = (int)GPUBackend::OPENGL; SetGPUBackend((GPUBackend)g_Config.iGPUBackend); tries++; goto retry; } delete graphicsContext; graphicsContext = nullptr; renderLoopRunning = false; return false; } if (!exitRenderLoop) { if (!useCPUThread) { NativeInitGraphics(graphicsContext); } graphicsContext->ThreadStart(); renderer_inited = true; } if (!exitRenderLoop) { static bool hasSetThreadName = false; if (!hasSetThreadName) { hasSetThreadName = true; setCurrentThreadName("AndroidRender"); } } if (useCPUThread) { ELOG("Running graphics loop"); while (!exitRenderLoop) { // This is the "GPU thread". graphicsContext->ThreadFrame(); graphicsContext->SwapBuffers(); } } else { while (!exitRenderLoop) { NativeUpdate(); NativeRender(graphicsContext); time_update(); graphicsContext->SwapBuffers(); ProcessFrameCommands(env); } } ILOG("Leaving EGL/Vulkan render loop."); if (useCPUThread) { EmuThreadStop("exitrenderloop"); while (graphicsContext->ThreadFrame()) { continue; } EmuThreadJoin(); } else { NativeShutdownGraphics(); } renderer_inited = false; graphicsContext->ThreadEnd(); // Shut the graphics context down to the same state it was in when we entered the render thread. ILOG("Shutting down graphics context from render thread..."); graphicsContext->ShutdownFromRenderThread(); renderLoopRunning = false; WLOG("Render loop function exited."); return true; }
extern "C" void Java_org_ppsspp_ppsspp_NativeApp_init (JNIEnv *env, jclass, jstring jmodel, jint jdeviceType, jstring jlangRegion, jstring japkpath, jstring jdataDir, jstring jexternalDir, jstring jlibraryDir, jstring jcacheDir, jstring jshortcutParam, jint jAndroidVersion, jstring jboard) { setCurrentThreadName("androidInit"); // Makes sure we get early permission grants. ProcessFrameCommands(env); ILOG("NativeApp.init() -- begin"); PROFILE_INIT(); renderer_inited = false; androidVersion = jAndroidVersion; deviceType = jdeviceType; left_joystick_x_async = 0; left_joystick_y_async = 0; right_joystick_x_async = 0; right_joystick_y_async = 0; hat_joystick_x_async = 0; hat_joystick_y_async = 0; std::string apkPath = GetJavaString(env, japkpath); VFSRegister("", new ZipAssetReader(apkPath.c_str(), "assets/")); systemName = GetJavaString(env, jmodel); langRegion = GetJavaString(env, jlangRegion); std::string externalDir = GetJavaString(env, jexternalDir); std::string user_data_path = GetJavaString(env, jdataDir); if (user_data_path.size() > 0) user_data_path += "/"; library_path = GetJavaString(env, jlibraryDir) + "/"; std::string shortcut_param = GetJavaString(env, jshortcutParam); std::string cacheDir = GetJavaString(env, jcacheDir); std::string buildBoard = GetJavaString(env, jboard); boardName = buildBoard; ILOG("NativeApp.init(): External storage path: %s", externalDir.c_str()); ILOG("NativeApp.init(): Launch shortcut parameter: %s", shortcut_param.c_str()); std::string app_name; std::string app_nice_name; std::string version; bool landscape; // Unfortunately, on the Samsung Galaxy S7, this isn't in /proc/cpuinfo. // We also can't read it from __system_property_get. if (buildBoard == "universal8890") { cpu_info.sQuirks.bExynos8890DifferingCachelineSizes = true; } NativeGetAppInfo(&app_name, &app_nice_name, &landscape, &version); // If shortcut_param is not empty, pass it as additional varargs argument to NativeInit() method. // NativeInit() is expected to treat extra argument as boot_filename, which in turn will start game immediately. // NOTE: Will only work if ppsspp started from Activity.onCreate(). Won't work if ppsspp app start from onResume(). if (shortcut_param.empty()) { const char *argv[2] = {app_name.c_str(), 0}; NativeInit(1, argv, user_data_path.c_str(), externalDir.c_str(), cacheDir.c_str()); } else { const char *argv[3] = {app_name.c_str(), shortcut_param.c_str(), 0}; NativeInit(2, argv, user_data_path.c_str(), externalDir.c_str(), cacheDir.c_str()); } retry: // Now that we've loaded config, set javaGL. javaGL = NativeQueryConfig("androidJavaGL") == "true"; switch (g_Config.iGPUBackend) { case (int)GPUBackend::OPENGL: useCPUThread = true; if (javaGL) { ILOG("NativeApp.init() -- creating OpenGL context (JavaGL)"); graphicsContext = new AndroidJavaEGLGraphicsContext(); } else { graphicsContext = new AndroidEGLGraphicsContext(); } break; case (int)GPUBackend::VULKAN: { ILOG("NativeApp.init() -- creating Vulkan context"); useCPUThread = false; // The Vulkan render manager manages its own thread. // We create and destroy the Vulkan graphics context in the "EGL" thread. AndroidVulkanContext *ctx = new AndroidVulkanContext(); if (!ctx->InitAPI()) { ILOG("Failed to initialize Vulkan, switching to OpenGL"); g_Config.iGPUBackend = (int)GPUBackend::OPENGL; SetGPUBackend(GPUBackend::OPENGL); goto retry; } else { graphicsContext = ctx; } break; } default: ELOG("NativeApp.init(): iGPUBackend %d not supported. Switching to OpenGL.", (int)g_Config.iGPUBackend); g_Config.iGPUBackend = (int)GPUBackend::OPENGL; goto retry; // Crash(); } if (useCPUThread) { ILOG("NativeApp.init() - launching emu thread"); EmuThreadStart(); } }
extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(JNIEnv *env, jobject obj, jobject _surf) { ANativeWindow *wnd = ANativeWindow_fromSurface(env, _surf); WLOG("runEGLRenderLoop. display_xres=%d display_yres=%d", display_xres, display_yres); if (wnd == nullptr) { ELOG("Error: Surface is null."); return false; } AndroidEGLGraphicsContext *graphicsContext = new AndroidEGLGraphicsContext(); if (!graphicsContext->Init(wnd, desiredBackbufferSizeX, desiredBackbufferSizeY, backbuffer_format)) { ELOG("Failed to initialize graphics context."); delete graphicsContext; return false; } if (!renderer_inited) { NativeInitGraphics(graphicsContext); renderer_inited = true; } exitRenderLoop = false; renderLoopRunning = true; while (!exitRenderLoop) { static bool hasSetThreadName = false; if (!hasSetThreadName) { hasSetThreadName = true; setCurrentThreadName("AndroidRender"); } // TODO: Look into if these locks are a perf loss { lock_guard guard(input_state.lock); input_state.pad_lstick_x = left_joystick_x_async; input_state.pad_lstick_y = left_joystick_y_async; input_state.pad_rstick_x = right_joystick_x_async; input_state.pad_rstick_y = right_joystick_y_async; UpdateInputState(&input_state); } NativeUpdate(input_state); { lock_guard guard(input_state.lock); EndInputState(&input_state); } NativeRender(graphicsContext); time_update(); graphicsContext->SwapBuffers(); ProcessFrameCommands(env); } ILOG("After render loop."); g_gameInfoCache->WorkQueue()->Flush(); NativeDeviceLost(); ILOG("NativeDeviceLost completed."); NativeShutdownGraphics(); renderer_inited = false; graphicsContext->Shutdown(); renderLoopRunning = false; WLOG("Render loop function exited."); return true; }
extern "C" bool JNICALL Java_org_ppsspp_ppsspp_NativeActivity_runEGLRenderLoop(JNIEnv *env, jobject obj, jobject _surf) { ANativeWindow *wnd = ANativeWindow_fromSurface(env, _surf); // Need to get the local JNI env for the graphics thread. Used later in draw_text_android. int res = javaVM->GetEnv((void **)&jniEnvGraphics, JNI_VERSION_1_6); if (res != JNI_OK) { ELOG("GetEnv failed: %d", res); } WLOG("runEGLRenderLoop. display_xres=%d display_yres=%d", display_xres, display_yres); if (wnd == nullptr) { ELOG("Error: Surface is null."); return false; } retry: bool vulkan = g_Config.iGPUBackend == GPU_BACKEND_VULKAN; int tries = 0; AndroidGraphicsContext *graphicsContext; if (vulkan) { graphicsContext = new AndroidVulkanContext(); } else { graphicsContext = new AndroidEGLGraphicsContext(); } if (!graphicsContext->Init(wnd, desiredBackbufferSizeX, desiredBackbufferSizeY, backbuffer_format, androidVersion)) { ELOG("Failed to initialize graphics context."); if (vulkan && tries < 2) { ILOG("Trying again, this time with OpenGL."); g_Config.iGPUBackend = GPU_BACKEND_OPENGL; SetGPUBackend((GPUBackend)g_Config.iGPUBackend); // Wait, why do we need a separate enum here? tries++; goto retry; } delete graphicsContext; return false; } if (!renderer_inited) { NativeInitGraphics(graphicsContext); if (renderer_ever_inited) { NativeDeviceRestore(); } renderer_inited = true; renderer_ever_inited = true; } exitRenderLoop = false; renderLoopRunning = true; while (!exitRenderLoop) { static bool hasSetThreadName = false; if (!hasSetThreadName) { hasSetThreadName = true; setCurrentThreadName("AndroidRender"); } NativeUpdate(); NativeRender(graphicsContext); time_update(); graphicsContext->SwapBuffers(); ProcessFrameCommands(env); } ILOG("After render loop."); g_gameInfoCache->WorkQueue()->Flush(); NativeDeviceLost(); renderer_inited = false; ILOG("Shutting down graphics context."); graphicsContext->Shutdown(); delete graphicsContext; graphicsContext = nullptr; renderLoopRunning = false; WLOG("Render loop function exited."); jniEnvGraphics = nullptr; return true; }