Add Map::pause/resume

Fixed bug after switching apps
This commit is contained in:
Leith Bade
2014-11-09 22:36:37 +11:00
parent b3e3c21129
commit 28d66bb6f8
9 changed files with 214 additions and 43 deletions

View File

@@ -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) },

View File

@@ -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");
}
}

View File

@@ -62,6 +62,9 @@ public:
void start();
void stop();
void resume();
void pause();
void notifyMapChange();
private:

View File

@@ -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() {

View File

@@ -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);

View File

@@ -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.

View File

@@ -21,6 +21,8 @@ class thread;
class rwlock;
class loop;
class worker;
class mutex;
class cond;
}

View File

@@ -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(); }

View File

@@ -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