Add Map::pause/resume
Fixed bug after switching apps
This commit is contained in:
@@ -221,6 +221,20 @@ void JNICALL nativeStop(JNIEnv* env, jobject obj, jlong native_map_view_ptr) {
|
||||
native_map_view->stop();
|
||||
}
|
||||
|
||||
void JNICALL nativePause(JNIEnv* env, jobject obj, jlong native_map_view_ptr) {
|
||||
LOG_VERBOSE("nativePause");
|
||||
LOG_ASSERT(native_map_view_ptr != 0);
|
||||
NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr);
|
||||
native_map_view->pause();
|
||||
}
|
||||
|
||||
void JNICALL nativeResume(JNIEnv* env, jobject obj, jlong native_map_view_ptr) {
|
||||
LOG_VERBOSE("nativeResume");
|
||||
LOG_ASSERT(native_map_view_ptr != 0);
|
||||
NativeMapView* native_map_view = reinterpret_cast<NativeMapView*>(native_map_view_ptr);
|
||||
native_map_view->resume();
|
||||
}
|
||||
|
||||
void JNICALL nativeRun(JNIEnv* env, jobject obj, jlong native_map_view_ptr) {
|
||||
LOG_VERBOSE("nativeRun");
|
||||
LOG_ASSERT(native_map_view_ptr != 0);
|
||||
@@ -739,7 +753,7 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
|
||||
}
|
||||
|
||||
// NOTE: if you get java.lang.UnsatisfiedLinkError you likely forgot to set the size of the array correctly (too large)
|
||||
std::array<JNINativeMethod, 58> methods = {{ // Can remove the extra brace in C++14
|
||||
std::array<JNINativeMethod, 60> methods = {{ // Can remove the extra brace in C++14
|
||||
{ "nativeCreate", "(Ljava/lang/String;)J", reinterpret_cast<void*>(&nativeCreate) },
|
||||
{ "nativeDestroy", "(J)V", reinterpret_cast<void*>(&nativeDestroy) },
|
||||
{ "nativeInitializeDisplay", "(J)V", reinterpret_cast<void*>(&nativeInitializeDisplay) },
|
||||
@@ -750,6 +764,8 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
|
||||
{ "nativeDestroySurface", "(J)V", reinterpret_cast<void*>(&nativeDestroySurface) },
|
||||
{ "nativeStart", "(J)V", reinterpret_cast<void*>(&nativeStart) },
|
||||
{ "nativeStop", "(J)V", reinterpret_cast<void*>(&nativeStop) },
|
||||
{ "nativePause", "(J)V", reinterpret_cast<void*>(&nativePause) },
|
||||
{ "nativeResume", "(J)V", reinterpret_cast<void*>(&nativeResume) },
|
||||
{ "nativeRun", "(J)V", reinterpret_cast<void*>(&nativeRun) },
|
||||
{ "nativeRerender", "(J)V", reinterpret_cast<void*>(&nativeRerender) },
|
||||
{ "nativeUpdate", "(J)V", reinterpret_cast<void*>(&nativeUpdate) },
|
||||
|
||||
@@ -149,12 +149,6 @@ bool NativeMapView::initializeDisplay() {
|
||||
|
||||
// Now try 8888
|
||||
use565 = false;
|
||||
// TODO removed these due to different indices
|
||||
//config_attribs[9] = 32; // EGL_BUFFER_SIZE // Ensure we get 32bit color buffer on Tegra, 24 bit will be sorted first without it (slow software mode)
|
||||
//config_attribs[11] = 8; // EGL_RED_SIZE
|
||||
//config_attribs[13] = 8; // EGL_GREEN_SIZE
|
||||
//config_attribs[15] = 8; // EGL_BLUE_SIZE
|
||||
|
||||
config_attribs[5] = 32; // EGL_BUFFER_SIZE // Ensure we get 32bit color buffer on Tegra, 24 bit will be sorted first without it (slow software mode)
|
||||
config_attribs[7] = 8; // EGL_RED_SIZE
|
||||
config_attribs[9] = 8; // EGL_GREEN_SIZE
|
||||
@@ -296,7 +290,25 @@ bool NativeMapView::createSurface(ANativeWindow* window_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
start();
|
||||
if (!eglMakeCurrent(display, surface, surface, context)) {
|
||||
LOG_ERROR("eglMakeCurrent() returned error %d", eglGetError());
|
||||
}
|
||||
|
||||
log_gl_string(GL_VENDOR, "Vendor");
|
||||
log_gl_string(GL_RENDERER, "Renderer");
|
||||
log_gl_string(GL_VERSION, "Version");
|
||||
log_gl_string(GL_SHADING_LANGUAGE_VERSION, "SL Version"); // In the emulator this returns NULL with error code 0? https://code.google.com/p/android/issues/detail?id=78977
|
||||
log_gl_string(GL_EXTENSIONS, "Extensions");
|
||||
|
||||
loadExtensions();
|
||||
|
||||
if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
||||
EGL_NO_CONTEXT)) {
|
||||
LOG_ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d",
|
||||
eglGetError());
|
||||
}
|
||||
|
||||
resume();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -304,7 +316,7 @@ bool NativeMapView::createSurface(ANativeWindow* window_) {
|
||||
void NativeMapView::destroySurface() {
|
||||
LOG_VERBOSE("NativeMapView::destroySurface");
|
||||
|
||||
map->terminate();
|
||||
pause();
|
||||
|
||||
if (surface != EGL_NO_SURFACE) {
|
||||
if (!eglDestroySurface(display, surface)) {
|
||||
@@ -464,31 +476,10 @@ void NativeMapView::start() {
|
||||
initializeContext();
|
||||
}
|
||||
|
||||
if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)
|
||||
&& (context != EGL_NO_CONTEXT)) {
|
||||
|
||||
if (!eglMakeCurrent(display, surface, surface, context)) {
|
||||
LOG_ERROR("eglMakeCurrent() returned error %d", eglGetError());
|
||||
}
|
||||
|
||||
log_gl_string(GL_VENDOR, "Vendor");
|
||||
log_gl_string(GL_RENDERER, "Renderer");
|
||||
log_gl_string(GL_VERSION, "Version");
|
||||
log_gl_string(GL_SHADING_LANGUAGE_VERSION, "SL Version"); // In the emulator this returns NULL with error code 0? https://code.google.com/p/android/issues/detail?id=78977
|
||||
log_gl_string(GL_EXTENSIONS, "Extensions");
|
||||
|
||||
loadExtensions();
|
||||
|
||||
if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE,
|
||||
EGL_NO_CONTEXT)) {
|
||||
LOG_ERROR("eglMakeCurrent(EGL_NO_CONTEXT) returned error %d",
|
||||
eglGetError());
|
||||
}
|
||||
|
||||
map->start();
|
||||
} else {
|
||||
LOG_DEBUG("Not starting because we are not ready");
|
||||
}
|
||||
LOG_ASSERT(display != EGL_NO_DISPLAY);
|
||||
LOG_ASSERT(context != EGL_NO_CONTEXT);
|
||||
|
||||
map->start();
|
||||
}
|
||||
|
||||
void NativeMapView::loadExtensions() {
|
||||
@@ -519,9 +510,33 @@ void NativeMapView::loadExtensions() {
|
||||
|
||||
void NativeMapView::stop() {
|
||||
LOG_VERBOSE("NativeMapView::stop");
|
||||
if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)
|
||||
&& (context != EGL_NO_CONTEXT)) {
|
||||
map->stop();
|
||||
|
||||
LOG_ASSERT(display != EGL_NO_DISPLAY);
|
||||
LOG_ASSERT(context != EGL_NO_CONTEXT);
|
||||
|
||||
map->stop();
|
||||
}
|
||||
|
||||
|
||||
void NativeMapView::pause() {
|
||||
LOG_VERBOSE("NativeMapView::pause");
|
||||
|
||||
LOG_ASSERT(display != EGL_NO_DISPLAY);
|
||||
LOG_ASSERT(context != EGL_NO_CONTEXT);
|
||||
|
||||
map->pause();
|
||||
}
|
||||
|
||||
void NativeMapView::resume() {
|
||||
LOG_VERBOSE("NativeMapView::resume");
|
||||
|
||||
LOG_ASSERT(display != EGL_NO_DISPLAY);
|
||||
LOG_ASSERT(context != EGL_NO_CONTEXT);
|
||||
|
||||
if (surface != EGL_NO_SURFACE) {
|
||||
map->resume();
|
||||
} else {
|
||||
LOG_DEBUG("Not resuming because we are not ready");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,9 @@ public:
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
void resume();
|
||||
void pause();
|
||||
|
||||
void notifyMapChange();
|
||||
|
||||
private:
|
||||
|
||||
@@ -373,6 +373,7 @@ public class MapView extends SurfaceView {
|
||||
}
|
||||
|
||||
mNativeMapView.initializeDisplay();
|
||||
mNativeMapView.initializeContext();
|
||||
}
|
||||
|
||||
// Called when we need to save instance state
|
||||
@@ -392,6 +393,7 @@ public class MapView extends SurfaceView {
|
||||
// Must be called from Activity onDestroy
|
||||
public void onDestroy() {
|
||||
Log.v(TAG, "onDestroy");
|
||||
mNativeMapView.terminateContext();
|
||||
mNativeMapView.terminateDisplay();
|
||||
}
|
||||
|
||||
@@ -399,21 +401,22 @@ public class MapView extends SurfaceView {
|
||||
// Must be called from Activity onStart
|
||||
public void onStart() {
|
||||
Log.v(TAG, "onStart");
|
||||
mNativeMapView.initializeContext();
|
||||
mNativeMapView.pause();
|
||||
mNativeMapView.start();
|
||||
}
|
||||
|
||||
// Called when we need to terminate the GL context
|
||||
// Must be called from Activity onPause
|
||||
public void onStop() {
|
||||
Log.v(TAG, "onStop");
|
||||
mNativeMapView.terminateContext();
|
||||
mNativeMapView.stop();
|
||||
}
|
||||
|
||||
// Called when we need to stop the render thread
|
||||
// Must be called from Activity onPause
|
||||
public void onPause() {
|
||||
Log.v(TAG, "onPause");
|
||||
mNativeMapView.stop();
|
||||
mNativeMapView.pause();
|
||||
}
|
||||
|
||||
// Called when we need to start the render thread
|
||||
@@ -421,7 +424,7 @@ public class MapView extends SurfaceView {
|
||||
|
||||
public void onResume() {
|
||||
Log.v(TAG, "onResume");
|
||||
mNativeMapView.start();
|
||||
mNativeMapView.resume();
|
||||
}
|
||||
|
||||
// This class handles SurfaceHolder callbacks
|
||||
@@ -464,6 +467,8 @@ public class MapView extends SurfaceView {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO examine how GLSurvaceView hadles attach/detach from window
|
||||
|
||||
// Called when view is no longer connected
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
|
||||
@@ -80,6 +80,14 @@ class NativeMapView {
|
||||
nativeStop(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void pause() {
|
||||
nativePause(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void resume() {
|
||||
nativeResume(mNativeMapViewPtr);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
nativeRun(mNativeMapViewPtr);
|
||||
}
|
||||
@@ -420,6 +428,10 @@ class NativeMapView {
|
||||
|
||||
private native void nativeStop(long nativeMapViewPtr);
|
||||
|
||||
private native void nativePause(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeResume(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeRun(long nativeMapViewPtr);
|
||||
|
||||
private native void nativeRerender(long nativeMapViewPtr);
|
||||
|
||||
@@ -45,6 +45,12 @@ public:
|
||||
// function.
|
||||
void stop(stop_callback cb = nullptr, void *data = nullptr);
|
||||
|
||||
// Pauses the render thread. The render thread will stop running but will not be terminated and will not lose state until resumed.
|
||||
void pause();
|
||||
|
||||
// Resumes a paused render thread
|
||||
void resume();
|
||||
|
||||
// Runs the map event loop. ONLY run this function when you want to get render a single frame
|
||||
// with this map object. It will *not* spawn a separate thread and instead block until the
|
||||
// frame is completely rendered.
|
||||
@@ -145,6 +151,9 @@ private:
|
||||
static void terminate(uv_async_t *async);
|
||||
static void cleanup(uv_async_t *async);
|
||||
|
||||
// Checks if render thread needs to pause
|
||||
void check_for_pause();
|
||||
|
||||
// Setup
|
||||
void setup();
|
||||
|
||||
@@ -167,6 +176,12 @@ private:
|
||||
std::unique_ptr<uv_async_t> async_render;
|
||||
std::unique_ptr<uv_async_t> async_cleanup;
|
||||
|
||||
private:
|
||||
bool terminating = false;
|
||||
bool is_paused = false;
|
||||
std::unique_ptr<uv::mutex> mutex_pause;
|
||||
std::unique_ptr<uv::cond> cond_resume;
|
||||
|
||||
private:
|
||||
// If cleared, the next time the render thread attempts to render the map, it will *actually*
|
||||
// render the map.
|
||||
|
||||
@@ -21,6 +21,8 @@ class thread;
|
||||
class rwlock;
|
||||
class loop;
|
||||
class worker;
|
||||
class mutex;
|
||||
class cond;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ private:
|
||||
};
|
||||
|
||||
class mutex {
|
||||
friend class cond;
|
||||
public:
|
||||
inline mutex() {
|
||||
if (uv_mutex_init(&mtx) != 0) {
|
||||
@@ -66,6 +67,23 @@ private:
|
||||
uv_mutex_t mtx;
|
||||
};
|
||||
|
||||
class cond {
|
||||
public:
|
||||
inline cond() {
|
||||
if (uv_cond_init(&cnd) != 0) {
|
||||
throw std::runtime_error("failed to initialize condition variable");
|
||||
}
|
||||
}
|
||||
inline ~cond() { uv_cond_destroy(&cnd); }
|
||||
inline void signal() { uv_cond_signal(&cnd); }
|
||||
inline void broadcast() { uv_cond_broadcast(&cnd); }
|
||||
inline void wait(mutex &mtx) { uv_cond_wait(&cnd, &mtx.mtx); }
|
||||
inline void timedwait(mutex &mtx, uint64_t timeout) { uv_cond_timedwait(&cnd, &mtx.mtx, timeout); }
|
||||
|
||||
private:
|
||||
uv_cond_t cnd;
|
||||
};
|
||||
|
||||
class lock {
|
||||
public:
|
||||
lock(mutex &mtx_) : mtx(mtx_) { mtx.lock(); }
|
||||
|
||||
@@ -36,6 +36,8 @@ Map::Map(View& view_)
|
||||
async_terminate(new uv_async_t()),
|
||||
async_render(new uv_async_t()),
|
||||
async_cleanup(new uv_async_t()),
|
||||
mutex_pause(std::make_unique<uv::mutex>()),
|
||||
cond_resume(std::make_unique<uv::cond>()),
|
||||
view(view_),
|
||||
#ifndef NDEBUG
|
||||
main_thread(uv_thread_self()),
|
||||
@@ -76,7 +78,7 @@ uv::worker &Map::getWorker() {
|
||||
return *workers;
|
||||
}
|
||||
|
||||
void Map::start() {
|
||||
void Map::start() { // TODO add pause arg
|
||||
assert(uv_thread_self() == main_thread);
|
||||
assert(!async);
|
||||
|
||||
@@ -136,6 +138,8 @@ void Map::stop(stop_callback cb, void *data) {
|
||||
|
||||
uv_async_send(async_terminate.get());
|
||||
|
||||
resume();
|
||||
|
||||
if (cb) {
|
||||
// Wait until the render thread stopped. We are using this construct instead of plainly
|
||||
// relying on the thread_join because the system might need to run things in the current
|
||||
@@ -155,6 +159,45 @@ void Map::stop(stop_callback cb, void *data) {
|
||||
async = false;
|
||||
}
|
||||
|
||||
void Map::pause() {
|
||||
assert(uv_thread_self() == main_thread);
|
||||
Log::Info(Event::General, "Map::pause() start");
|
||||
|
||||
// TODO wait until paused before return
|
||||
|
||||
|
||||
Log::Info(Event::General, "Map::pause() locking");
|
||||
mutex_pause->lock();
|
||||
Log::Info(Event::General, "Map::pause() locked");
|
||||
is_paused = true;
|
||||
Log::Info(Event::General, "Map::pause() set is_paused = true");
|
||||
Log::Info(Event::General, "Map::pause() unlocking");
|
||||
mutex_pause->unlock();
|
||||
Log::Info(Event::General, "Map::pause() unlocked");
|
||||
|
||||
Log::Info(Event::General, "Map::pause() stopping loop");
|
||||
uv_stop(**loop);
|
||||
Log::Info(Event::General, "Map::pause() stopping done");
|
||||
}
|
||||
|
||||
void Map::resume() {
|
||||
assert(uv_thread_self() == main_thread);
|
||||
Log::Info(Event::General, "Map::resume() start");
|
||||
|
||||
Log::Info(Event::General, "Map::resume() locking");
|
||||
mutex_pause->lock();
|
||||
Log::Info(Event::General, "Map::resume() locked");
|
||||
is_paused = false;
|
||||
Log::Info(Event::General, "Map::resume() set is_paused = false");
|
||||
Log::Info(Event::General, "Map::resume() broadcasting");
|
||||
cond_resume->broadcast();
|
||||
Log::Info(Event::General, "Map::resume() broadcasted");
|
||||
Log::Info(Event::General, "Map::resume() unlocking");
|
||||
mutex_pause->unlock();
|
||||
Log::Info(Event::General, "Map::resume() unlocked");
|
||||
Log::Info(Event::General, "Map::resume() done");
|
||||
}
|
||||
|
||||
void Map::run() {
|
||||
#ifndef NDEBUG
|
||||
if (!async) {
|
||||
@@ -163,9 +206,28 @@ void Map::run() {
|
||||
#endif
|
||||
assert(uv_thread_self() == map_thread);
|
||||
|
||||
check_for_pause();
|
||||
|
||||
setup();
|
||||
prepare();
|
||||
uv_run(**loop, UV_RUN_DEFAULT);
|
||||
|
||||
Log::Info(Event::General, "Map::run() start");
|
||||
terminating = false;
|
||||
Log::Info(Event::General, "Map::run() set terminating = false");
|
||||
|
||||
Log::Info(Event::General, "Map::run() entering run loop");
|
||||
while(!terminating) {
|
||||
Log::Info(Event::General, "Map::run() next run loop, terminating = %s", terminating ? "true" : "false");
|
||||
Log::Info(Event::General, "Map::run() uv_run enter");
|
||||
uv_run(**loop, UV_RUN_DEFAULT);
|
||||
Log::Info(Event::General, "Map::run() exit");
|
||||
|
||||
check_for_pause();
|
||||
|
||||
//uv_run(**loop, UV_RUN_DEFAULT);
|
||||
}
|
||||
|
||||
Log::Info(Event::General, "Map::run() run loop exit");
|
||||
|
||||
// Run the event loop once more to make sure our async delete handlers are called.
|
||||
uv_run(**loop, UV_RUN_ONCE);
|
||||
@@ -180,6 +242,23 @@ void Map::run() {
|
||||
}
|
||||
}
|
||||
|
||||
void Map::check_for_pause() {
|
||||
Log::Info(Event::General, "Map::check_for_pause() locking");
|
||||
mutex_pause->lock();
|
||||
Log::Info(Event::General, "Map::check_for_pause() locked");
|
||||
Log::Info(Event::General, "Map::check_for_pause() entering pause loop");
|
||||
while (is_paused) {
|
||||
Log::Info(Event::General, "Map::check_for_pause() next pause loop, is_paused = %s", is_paused ? "true" : "false");
|
||||
Log::Info(Event::General, "Map::check_for_pause() waiting");
|
||||
cond_resume->wait(*mutex_pause);
|
||||
Log::Info(Event::General, "Map::check_for_pause() waited");
|
||||
}
|
||||
Log::Info(Event::General, "Map::check_for_pause() exit pause loop");
|
||||
Log::Info(Event::General, "Map::check_for_pause() unlocking");
|
||||
mutex_pause->unlock();
|
||||
Log::Info(Event::General, "Map::check_for_pause() unlocked");
|
||||
}
|
||||
|
||||
void Map::rerender() {
|
||||
// We only send render events if we want to continuously update the map
|
||||
// (== async rendering).
|
||||
@@ -250,6 +329,7 @@ void Map::render(uv_async_t *async) {
|
||||
}
|
||||
|
||||
void Map::terminate(uv_async_t *async) {
|
||||
Log::Info(Event::General, "Map::terminate() start");
|
||||
// Closes all open handles on the loop. This means that the loop will automatically terminate.
|
||||
Map *map = static_cast<Map *>(async->data);
|
||||
assert(uv_thread_self() == map->map_thread);
|
||||
@@ -263,9 +343,14 @@ void Map::terminate(uv_async_t *async) {
|
||||
|
||||
map->view.make_inactive();
|
||||
|
||||
map->terminating = true;
|
||||
Log::Info(Event::General, "Map::terminate() set terminating = true");
|
||||
|
||||
uv_close((uv_handle_t *)map->async_cleanup.get(), nullptr);
|
||||
uv_close((uv_handle_t *)map->async_render.get(), nullptr);
|
||||
uv_close((uv_handle_t *)map->async_terminate.get(), nullptr);
|
||||
|
||||
Log::Info(Event::General, "Map::terminate() done");
|
||||
}
|
||||
|
||||
#pragma mark - Setup
|
||||
|
||||
Reference in New Issue
Block a user