From: Pyry Haulos Date: Wed, 1 Oct 2014 22:49:17 +0000 (-0700) Subject: Make Android integration more robust for API >= 11 X-Git-Tag: upstream/0.1.0~2130 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=217426c2d19d5247e094e363615d7a0b84daf3a7;p=platform%2Fupstream%2FVK-GL-CTS.git Make Android integration more robust for API >= 11 Recent targetSdkLevel bump exposed robustness issues in the Android port which were hidden by pre-honeycomb workarounds in android.app.ActivityThread. For honeycomb and up when launching activity with lockscreen on or screen off the system for some reason first starts the activity once, puts it back to stopped state and then resumes again. Original Android port assumed that stop event signaled that the test execution should always stop and process should kill itself. This might have been necessary workaround on some older Android versions where onDestroy was never called even if app called finish(). This commit improves Android integration in multiple ways: * Android port can continue test execution even across stop-start boundary in the Activity life-cycle. * eglu::GLRenderContext can recover from window loss by recreating the window if NativeWindow::postIterate() throws WindowDestroyedError. * eglu::GLRenderContext refreshes window surface size in postIterate() call. Note that most test cases might not like if surface is resized during execution though. * Temporary window loss (recreation) no longer leads to test run abort, but rather Android NativeWindow correctly signals that event allowing eglu::GLRenderContext to recover. Change-Id: Iaa240f16a2ae6cea7fb894c5772d1e7959a1cfac --- diff --git a/android/package/AndroidManifest.xml b/android/package/AndroidManifest.xml index 12e3681..33380c2 100644 --- a/android/package/AndroidManifest.xml +++ b/android/package/AndroidManifest.xml @@ -5,7 +5,6 @@ android:versionName="1.0"> - - - - - - + + + + diff --git a/framework/egl/egluGLContextFactory.cpp b/framework/egl/egluGLContextFactory.cpp index b6f79c4..358edff 100644 --- a/framework/egl/egluGLContextFactory.cpp +++ b/framework/egl/egluGLContextFactory.cpp @@ -133,19 +133,20 @@ public: RenderContext (const NativeDisplayFactory* displayFactory, const NativeWindowFactory* windowFactory, const NativePixmapFactory* pixmapFactory, const glu::RenderConfig& config); virtual ~RenderContext (void); - virtual glu::ContextType getType (void) const { return m_glContextType; } - virtual const glw::Functions& getFunctions (void) const { return m_glFunctions; } - virtual const tcu::RenderTarget& getRenderTarget (void) const { return m_glRenderTarget; } + virtual glu::ContextType getType (void) const { return m_renderConfig.type; } + virtual const glw::Functions& getFunctions (void) const { return m_glFunctions; } + virtual const tcu::RenderTarget& getRenderTarget (void) const { return m_glRenderTarget; } virtual void postIterate (void); - virtual EGLDisplay getEGLDisplay (void) const { return m_eglDisplay; } - virtual EGLContext getEGLContext (void) const { return m_eglContext; } + virtual EGLDisplay getEGLDisplay (void) const { return m_eglDisplay; } + virtual EGLContext getEGLContext (void) const { return m_eglContext; } private: void create (const NativeDisplayFactory* displayFactory, const NativeWindowFactory* windowFactory, const NativePixmapFactory* pixmapFactory, const glu::RenderConfig& config); void destroy (void); - const glu::ContextType m_glContextType; + const glu::RenderConfig m_renderConfig; + const NativeWindowFactory* const m_nativeWindowFactory; // Stored in case window must be re-created NativeDisplay* m_display; NativeWindow* m_window; @@ -162,7 +163,8 @@ private: }; RenderContext::RenderContext (const NativeDisplayFactory* displayFactory, const NativeWindowFactory* windowFactory, const NativePixmapFactory* pixmapFactory, const glu::RenderConfig& config) - : m_glContextType (config.type) + : m_renderConfig (config) + , m_nativeWindowFactory (windowFactory) , m_display (DE_NULL) , m_window (DE_NULL) , m_pixmap (DE_NULL) @@ -639,8 +641,89 @@ void RenderContext::postIterate (void) { if (m_window) { - EGLU_CHECK_CALL(eglSwapBuffers(m_eglDisplay, m_eglSurface)); - m_window->processEvents(); + EGLBoolean swapOk = eglSwapBuffers(m_eglDisplay, m_eglSurface); + EGLint error = eglGetError(); + + if (!swapOk && error != EGL_BAD_SURFACE) + { + if (error == EGL_BAD_ALLOC) + throw BadAllocError("eglSwapBuffers()"); + else if (error == EGL_CONTEXT_LOST) + throw tcu::ResourceError("eglSwapBuffers() failed, context lost"); + else + throw Error(error, "eglSwapBuffers()"); + } + + try + { + m_window->processEvents(); + } + catch (const WindowDestroyedError&) + { + tcu::print("Warning: Window destroyed, recreating...\n"); + + EGLU_CHECK_CALL(eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)); + EGLU_CHECK_CALL(eglDestroySurface(m_eglDisplay, m_eglSurface)); + m_eglSurface = EGL_NO_SURFACE; + + delete m_window; + m_window = DE_NULL; + + try + { + WindowSurfacePair windowSurface = createWindow(m_display, m_nativeWindowFactory, m_eglDisplay, m_eglConfig, m_renderConfig); + m_window = windowSurface.first; + m_eglSurface = windowSurface.second; + + EGLU_CHECK_CALL(eglMakeCurrent(m_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext)); + + swapOk = EGL_TRUE; + error = EGL_SUCCESS; + } + catch (const std::exception& e) + { + if (m_eglSurface) + { + eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglDestroySurface(m_eglDisplay, m_eglSurface); + m_eglSurface = EGL_NO_SURFACE; + } + + delete m_window; + m_window = DE_NULL; + + throw tcu::ResourceError(string("Failed to re-create window: ") + e.what()); + } + } + + if (!swapOk) + { + DE_ASSERT(error == EGL_BAD_SURFACE); + throw Error(error, "eglSwapBuffers()"); + } + + // Refresh dimensions + { + int newWidth = 0; + int newHeight = 0; + + eglQuerySurface(m_eglDisplay, m_eglSurface, EGL_WIDTH, &newWidth); + eglQuerySurface(m_eglDisplay, m_eglSurface, EGL_HEIGHT, &newHeight); + EGLU_CHECK_MSG("Failed to query window size"); + + if (newWidth != m_glRenderTarget.getWidth() || + newHeight != m_glRenderTarget.getHeight()) + { + tcu::print("Warning: Window size changed (%dx%d -> %dx%d), test results might be invalid!\n", + m_glRenderTarget.getWidth(), m_glRenderTarget.getHeight(), newWidth, newHeight); + + m_glRenderTarget = tcu::RenderTarget(newWidth, newHeight, + m_glRenderTarget.getPixelFormat(), + m_glRenderTarget.getDepthBits(), + m_glRenderTarget.getStencilBits(), + m_glRenderTarget.getNumSamples()); + } + } } else { diff --git a/framework/egl/egluNativeWindow.hpp b/framework/egl/egluNativeWindow.hpp index 616fe02..f404013 100644 --- a/framework/egl/egluNativeWindow.hpp +++ b/framework/egl/egluNativeWindow.hpp @@ -64,6 +64,12 @@ struct WindowParams WindowParams (int width_, int height_, Visibility visibility_) : width(width_), height(height_), visibility(visibility_) {} }; +class WindowDestroyedError : public tcu::ResourceError +{ +public: + WindowDestroyedError (const std::string& message) : tcu::ResourceError(message) {} +}; + class NativeWindow { public: diff --git a/framework/platform/android/tcuAndroidPlatform.cpp b/framework/platform/android/tcuAndroidPlatform.cpp index a137ea3..d2cf29e 100644 --- a/framework/platform/android/tcuAndroidPlatform.cpp +++ b/framework/platform/android/tcuAndroidPlatform.cpp @@ -50,7 +50,7 @@ public: class NativeDisplayFactory : public eglu::NativeDisplayFactory { public: - NativeDisplayFactory (Window& window); + NativeDisplayFactory (WindowRegistry& windowRegistry); ~NativeDisplayFactory (void) {} virtual eglu::NativeDisplay* createDisplay (const EGLAttrib* attribList) const; @@ -59,70 +59,70 @@ public: class NativeWindow : public eglu::NativeWindow { public: - NativeWindow (Window& window, int width, int height, int32_t format); + NativeWindow (Window* window, int width, int height, int32_t format); virtual ~NativeWindow (void); - virtual EGLNativeWindowType getLegacyNative (void) { return m_window.getNativeWindow(); } - IVec2 getScreenSize (void) const { return m_window.getSize(); } + virtual EGLNativeWindowType getLegacyNative (void) { return m_window->getNativeWindow(); } + IVec2 getScreenSize (void) const { return m_window->getSize(); } void setSurfaceSize (IVec2 size); virtual void processEvents (void); private: - Window& m_window; + Window* m_window; int32_t m_format; }; class NativeWindowFactory : public eglu::NativeWindowFactory { public: - NativeWindowFactory (Window& window); + NativeWindowFactory (WindowRegistry& windowRegistry); ~NativeWindowFactory (void); virtual eglu::NativeWindow* createWindow (eglu::NativeDisplay* nativeDisplay, const eglu::WindowParams& params) const; virtual eglu::NativeWindow* createWindow (eglu::NativeDisplay* nativeDisplay, EGLDisplay display, EGLConfig config, const EGLAttrib* attribList, const eglu::WindowParams& params) const; private: - Window& m_window; + virtual eglu::NativeWindow* createWindow (const eglu::WindowParams& params, int32_t format) const; + + WindowRegistry& m_windowRegistry; }; // NativeWindow -NativeWindow::NativeWindow (Window& window, int width, int height, int32_t format) +NativeWindow::NativeWindow (Window* window, int width, int height, int32_t format) : eglu::NativeWindow (WINDOW_CAPABILITIES) , m_window (window) , m_format (format) { - // Try to acquire window. - if (!m_window.tryAcquire()) - throw ResourceError("Native window is not available", "", __FILE__, __LINE__); - // Set up buffers. setSurfaceSize(IVec2(width, height)); } NativeWindow::~NativeWindow (void) { - m_window.release(); + m_window->release(); } void NativeWindow::processEvents (void) { + if (m_window->isPendingDestroy()) + throw eglu::WindowDestroyedError("Window has been destroyed"); } void NativeWindow::setSurfaceSize (tcu::IVec2 size) { - m_window.setBuffersGeometry(size.x() != eglu::WindowParams::SIZE_DONT_CARE ? size.x() : 0, - size.y() != eglu::WindowParams::SIZE_DONT_CARE ? size.y() : 0, - m_format); + m_window->setBuffersGeometry(size.x() != eglu::WindowParams::SIZE_DONT_CARE ? size.x() : 0, + size.y() != eglu::WindowParams::SIZE_DONT_CARE ? size.y() : 0, + m_format); } // NativeWindowFactory -NativeWindowFactory::NativeWindowFactory (Window& window) +NativeWindowFactory::NativeWindowFactory (WindowRegistry& windowRegistry) : eglu::NativeWindowFactory ("default", "Default display", WINDOW_CAPABILITIES) - , m_window (window) + , m_windowRegistry (windowRegistry) { } @@ -133,22 +133,33 @@ NativeWindowFactory::~NativeWindowFactory (void) eglu::NativeWindow* NativeWindowFactory::createWindow (eglu::NativeDisplay* nativeDisplay, const eglu::WindowParams& params) const { DE_UNREF(nativeDisplay); - return new NativeWindow(m_window, params.width, params.height, WINDOW_FORMAT_RGBA_8888); + return createWindow(params, WINDOW_FORMAT_RGBA_8888); } eglu::NativeWindow* NativeWindowFactory::createWindow (eglu::NativeDisplay* nativeDisplay, EGLDisplay display, EGLConfig config, const EGLAttrib* attribList, const eglu::WindowParams& params) const { const int32_t format = (int32_t)eglu::getConfigAttribInt(display, config, EGL_NATIVE_VISUAL_ID); DE_UNREF(nativeDisplay && attribList); - return new NativeWindow(m_window, params.width, params.height, format); + return createWindow(params, format); +} + + +eglu::NativeWindow* NativeWindowFactory::createWindow (const eglu::WindowParams& params, int32_t format) const +{ + Window* window = m_windowRegistry.tryAcquireWindow(); + + if (!window) + throw ResourceError("Native window is not available", DE_NULL, __FILE__, __LINE__); + + return new NativeWindow(window, params.width, params.height, format); } // NativeDisplayFactory -NativeDisplayFactory::NativeDisplayFactory (Window& window) +NativeDisplayFactory::NativeDisplayFactory (WindowRegistry& windowRegistry) : eglu::NativeDisplayFactory("default", "Default display", DISPLAY_CAPABILITIES) { - m_nativeWindowRegistry.registerFactory(new NativeWindowFactory(window)); + m_nativeWindowRegistry.registerFactory(new NativeWindowFactory(windowRegistry)); } eglu::NativeDisplay* NativeDisplayFactory::createDisplay (const EGLAttrib* attribList) const @@ -159,10 +170,9 @@ eglu::NativeDisplay* NativeDisplayFactory::createDisplay (const EGLAttrib* attri // Platform -Platform::Platform (ANativeWindow* window) - : m_window(window) +Platform::Platform (void) { - m_nativeDisplayFactoryRegistry.registerFactory(new NativeDisplayFactory(m_window)); + m_nativeDisplayFactoryRegistry.registerFactory(new NativeDisplayFactory(m_windowRegistry)); m_contextFactoryRegistry.registerFactory(new eglu::GLContextFactory(m_nativeDisplayFactoryRegistry)); } @@ -170,5 +180,11 @@ Platform::~Platform (void) { } +bool Platform::processEvents (void) +{ + m_windowRegistry.garbageCollect(); + return true; +} + } // Android } // tcu diff --git a/framework/platform/android/tcuAndroidPlatform.hpp b/framework/platform/android/tcuAndroidPlatform.hpp index d6df153..d8463c3 100644 --- a/framework/platform/android/tcuAndroidPlatform.hpp +++ b/framework/platform/android/tcuAndroidPlatform.hpp @@ -37,14 +37,18 @@ namespace Android class Platform : public tcu::Platform, private eglu::Platform, private glu::Platform { public: - Platform (ANativeWindow* window); + Platform (void); virtual ~Platform (void); + virtual bool processEvents (void); + virtual const glu::Platform& getGLPlatform (void) const { return static_cast(*this); } virtual const eglu::Platform& getEGLPlatform (void) const { return static_cast(*this); } + WindowRegistry& getWindowRegistry (void) { return m_windowRegistry; } + private: - Window m_window; + WindowRegistry m_windowRegistry; }; } // Android diff --git a/framework/platform/android/tcuAndroidRenderActivity.cpp b/framework/platform/android/tcuAndroidRenderActivity.cpp index 075f30c..142435e 100644 --- a/framework/platform/android/tcuAndroidRenderActivity.cpp +++ b/framework/platform/android/tcuAndroidRenderActivity.cpp @@ -47,6 +47,26 @@ enum MESSAGE_QUEUE_SIZE = 8 //!< Length of RenderThread message queue. }; +#if defined(DE_DEBUG) +static const char* getMessageTypeName (MessageType type) +{ + static const char* s_names[] = + { + "RESUME", + "PAUSE", + "FINISH", + "WINDOW_CREATED", + "WINDOW_RESIZED", + "WINDOW_DESTROYED", + "INPUT_QUEUE_CREATED", + "INPUT_QUEUE_DESTROYED", + "SYNC" + }; + DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(s_names) == MESSAGETYPE_LAST); + return s_names[type]; +} +#endif + // RenderThread RenderThread::RenderThread (NativeActivity& activity) @@ -71,7 +91,7 @@ void RenderThread::start (void) Thread::start(); } -void RenderThread::destroy (void) +void RenderThread::stop (void) { // Queue finish command enqueue(Message(MESSAGE_FINISH)); @@ -108,21 +128,29 @@ void RenderThread::sync (void) void RenderThread::processMessage (const Message& message) { + DBG_PRINT(("RenderThread::processMessage(): message = { %s, %p }\n", getMessageTypeName(message.type), message.payload.window)); + switch (message.type) { case MESSAGE_RESUME: m_paused = false; break; case MESSAGE_PAUSE: m_paused = true; break; case MESSAGE_FINISH: m_finish = true; break; + // \note While Platform / WindowRegistry are currently multi-window -capable, + // the fact that platform gives us windows too late / at unexpected times + // forces us to do some sanity checking and limit system to one window here. case MESSAGE_WINDOW_CREATED: if (m_windowState != WINDOWSTATE_NOT_CREATED && m_windowState != WINDOWSTATE_DESTROYED) throw InternalError("Got unexpected onNativeWindowCreated() event from system"); + m_windowState = WINDOWSTATE_NOT_INITIALIZED; m_window = message.payload.window; break; case MESSAGE_WINDOW_RESIZED: - DE_ASSERT(m_window == message.payload.window); + if (m_window != message.payload.window) + throw InternalError("Got onNativeWindowResized() event targeting different window"); + if (m_windowState == WINDOWSTATE_NOT_INITIALIZED) { // Got first resize event, window is ready for use. @@ -133,14 +161,19 @@ void RenderThread::processMessage (const Message& message) onWindowResized(message.payload.window); else throw InternalError("Got unexpected onNativeWindowResized() event from system"); + break; case MESSAGE_WINDOW_DESTROYED: - DE_ASSERT(m_window == message.payload.window); + if (m_window != message.payload.window) + throw InternalError("Got onNativeWindowDestroyed() event targeting different window"); + if (m_windowState != WINDOWSTATE_NOT_INITIALIZED && m_windowState != WINDOWSTATE_READY) throw InternalError("Got unexpected onNativeWindowDestroyed() event from system"); - onWindowDestroyed(message.payload.window); + if (m_windowState == WINDOWSTATE_READY) + onWindowDestroyed(message.payload.window); + m_windowState = WINDOWSTATE_DESTROYED; m_window = DE_NULL; break; @@ -207,15 +240,10 @@ void RenderThread::run (void) // Everything set up - safe to render. if (!render()) + { + DBG_PRINT(("RenderThread::run(): render\n")); break; - } - - if (m_windowState == WINDOWSTATE_READY) - { - // Clean up window use - onWindowDestroyed(m_window); - m_windowState = WINDOWSTATE_DESTROYED; - m_window = DE_NULL; + } } } catch (const std::exception& e) @@ -270,9 +298,6 @@ void RenderActivity::setThread (RenderThread* thread) void RenderActivity::onStart (void) { DBG_PRINT(("RenderActivity::onStart()")); - - // Launch tester thread when activity is created. It will remain in paused state until resume() is called. - m_thread->start(); } void RenderActivity::onResume (void) @@ -294,7 +319,6 @@ void RenderActivity::onPause (void) void RenderActivity::onStop (void) { DBG_PRINT(("RenderActivity::onStop()")); - m_thread->destroy(); } void RenderActivity::onDestroy (void) diff --git a/framework/platform/android/tcuAndroidRenderActivity.hpp b/framework/platform/android/tcuAndroidRenderActivity.hpp index 676c295..e2e7331 100644 --- a/framework/platform/android/tcuAndroidRenderActivity.hpp +++ b/framework/platform/android/tcuAndroidRenderActivity.hpp @@ -126,7 +126,8 @@ public: void start (void); void resume (void); void pause (void); - void destroy (void); + void stop (void); + void enqueue (const Message& message); void sync (void); diff --git a/framework/platform/android/tcuAndroidTestActivity.cpp b/framework/platform/android/tcuAndroidTestActivity.cpp index d2a7cd1..25d0b7f 100644 --- a/framework/platform/android/tcuAndroidTestActivity.cpp +++ b/framework/platform/android/tcuAndroidTestActivity.cpp @@ -36,33 +36,15 @@ namespace tcu namespace Android { -// TestApp - -TestApp::TestApp (NativeActivity& activity, ANativeWindow* window, const CommandLine& cmdLine) - : m_cmdLine (cmdLine) - , m_platform (window) - , m_archive (activity.getNativeActivity()->assetManager) - , m_log (m_cmdLine.getLogFileName()) - , m_app (m_platform, m_archive, m_log, m_cmdLine) -{ -} - -TestApp::~TestApp (void) -{ -} - -bool TestApp::iterate (void) -{ - return m_app.iterate(); -} - // TestThread TestThread::TestThread (NativeActivity& activity, const CommandLine& cmdLine) : RenderThread (activity) , m_cmdLine (cmdLine) - , m_testApp (DE_NULL) - , m_done (false) + , m_archive (activity.getNativeActivity()->assetManager) + , m_log (m_cmdLine.getLogFileName()) + , m_app (m_platform, m_archive, m_log, m_cmdLine) + , m_finished (false) { } @@ -74,45 +56,29 @@ TestThread::~TestThread (void) void TestThread::run (void) { RenderThread::run(); - - delete m_testApp; - m_testApp = DE_NULL; } void TestThread::onWindowCreated (ANativeWindow* window) { - DE_ASSERT(!m_testApp); - m_testApp = new TestApp(getNativeActivity(), window, m_cmdLine); + m_platform.getWindowRegistry().addWindow(window); } void TestThread::onWindowDestroyed (ANativeWindow* window) { - DE_UNREF(window); - DE_ASSERT(m_testApp); - delete m_testApp; - m_testApp = DE_NULL; - - if (!m_done) - { - // \note We could just throw exception here and RenderThread would gracefully terminate. - // However, native window is often destroyed when app is closed and android may not - // end up calling onStop(). - die("Window was destroyed during execution"); - } + m_platform.getWindowRegistry().destroyWindow(window); } void TestThread::onWindowResized (ANativeWindow* window) { - // \todo [2013-05-12 pyry] Handle this in some sane way. DE_UNREF(window); print("Warning: Native window was resized, results may be undefined"); } bool TestThread::render (void) { - DE_ASSERT(m_testApp); - m_done = !m_testApp->iterate(); - return !m_done; + if (!m_finished) + m_finished = !m_app.iterate(); + return !m_finished; } // TestActivity @@ -121,10 +87,8 @@ TestActivity::TestActivity (ANativeActivity* activity) : RenderActivity (activity) , m_cmdLine (getIntentStringExtra(activity, "cmdLine")) , m_testThread (*this, m_cmdLine) + , m_started (false) { - // Provide RenderThread - setThread(&m_testThread); - // Set initial orientation. setRequestedOrientation(getNativeActivity(), mapScreenRotation(m_cmdLine.getScreenRotation())); @@ -139,9 +103,28 @@ TestActivity::~TestActivity (void) { } -void TestActivity::onStop (void) +void TestActivity::onStart (void) { - RenderActivity::onStop(); + if (!m_started) + { + setThread(&m_testThread); + m_testThread.start(); + m_started = true; + } + + RenderActivity::onStart(); +} + +void TestActivity::onDestroy (void) +{ + if (m_started) + { + setThread(DE_NULL); + m_testThread.stop(); + m_started = false; + } + + RenderActivity::onDestroy(); // Kill this process. print("Done, killing process"); diff --git a/framework/platform/android/tcuAndroidTestActivity.hpp b/framework/platform/android/tcuAndroidTestActivity.hpp index 9effc35..58c28e6 100644 --- a/framework/platform/android/tcuAndroidTestActivity.hpp +++ b/framework/platform/android/tcuAndroidTestActivity.hpp @@ -36,22 +36,6 @@ namespace tcu namespace Android { -class TestApp -{ -public: - TestApp (NativeActivity& activity, ANativeWindow* window, const CommandLine& cmdLine); - ~TestApp (void); - - bool iterate (void); - -private: - const CommandLine& m_cmdLine; - Platform m_platform; - AssetArchive m_archive; - TestLog m_log; - App m_app; -}; - class TestThread : public RenderThread { public: @@ -67,8 +51,11 @@ private: virtual bool render (void); const CommandLine& m_cmdLine; - TestApp* m_testApp; - bool m_done; //!< Is execution finished. + Platform m_platform; + AssetArchive m_archive; + TestLog m_log; + App m_app; + bool m_finished; //!< Is execution finished. }; class TestActivity : public RenderActivity @@ -77,12 +64,14 @@ public: TestActivity (ANativeActivity* nativeActivity); ~TestActivity (void); - virtual void onStop (void); + virtual void onStart (void); + virtual void onDestroy (void); virtual void onConfigurationChanged (void); private: CommandLine m_cmdLine; TestThread m_testThread; + bool m_started; }; } // Android diff --git a/framework/platform/android/tcuAndroidWindow.cpp b/framework/platform/android/tcuAndroidWindow.cpp index 147583b..aad607e 100644 --- a/framework/platform/android/tcuAndroidWindow.cpp +++ b/framework/platform/android/tcuAndroidWindow.cpp @@ -28,9 +28,13 @@ namespace tcu namespace Android { +using std::vector; + +// Window + Window::Window (ANativeWindow* window) - : m_window (window) - , m_semaphore (1) + : m_window (window) + , m_state (STATE_AVAILABLE) { } @@ -50,5 +54,143 @@ IVec2 Window::getSize (void) const return IVec2(width, height); } +bool Window::tryAcquire (void) +{ + de::ScopedLock lock(m_stateLock); + + if (m_state == STATE_AVAILABLE) + { + m_state = STATE_IN_USE; + return true; + } + else + return false; +} + +void Window::release (void) +{ + de::ScopedLock lock(m_stateLock); + + if (m_state == STATE_IN_USE) + m_state = STATE_AVAILABLE; + else if (m_state == STATE_PENDING_DESTROY) + m_state = STATE_READY_FOR_DESTROY; + else + DE_ASSERT(!"Invalid window state"); +} + +void Window::markForDestroy (void) +{ + de::ScopedLock lock(m_stateLock); + + if (m_state == STATE_AVAILABLE) + m_state = STATE_READY_FOR_DESTROY; + else if (m_state == STATE_IN_USE) + m_state = STATE_PENDING_DESTROY; + else + DE_ASSERT(!"Invalid window state"); +} + +bool Window::isPendingDestroy (void) const +{ + de::ScopedLock lock(m_stateLock); + return m_state == STATE_PENDING_DESTROY; +} + +bool Window::tryAcquireForDestroy (bool onlyMarked) +{ + de::ScopedLock lock(m_stateLock); + + if (m_state == STATE_READY_FOR_DESTROY || + (!onlyMarked && m_state == STATE_AVAILABLE)) + { + m_state = STATE_ACQUIRED_FOR_DESTROY; + return true; + } + else + return false; +} + +// WindowRegistry + +WindowRegistry::WindowRegistry (void) +{ +} + +WindowRegistry::~WindowRegistry (void) +{ + for (vector::const_iterator winIter = m_windows.begin(); winIter != m_windows.end(); winIter++) + { + Window* const window = *winIter; + + if (window->tryAcquireForDestroy(false)) + delete window; + else + { + print("ERROR: Window was not available for deletion, leaked tcu::Android::Window!\n"); + DE_ASSERT(!"Window leaked"); + } + } +} + +void WindowRegistry::addWindow (ANativeWindow* window) +{ + m_windows.reserve(m_windows.size()+1); + m_windows.push_back(new Window(window)); +} + +void WindowRegistry::destroyWindow (ANativeWindow* rawHandle) +{ + for (int ndx = 0; ndx < (int)m_windows.size(); ++ndx) + { + Window* const window = m_windows[ndx]; + + if (window->getNativeWindow() == rawHandle) + { + if (window->tryAcquireForDestroy(false)) + { + delete window; + m_windows[ndx] = m_windows.back(); + m_windows.pop_back(); + } + else + window->markForDestroy(); + + return; + } + } + + throw tcu::InternalError("Window not registered", DE_NULL, __FILE__, __LINE__); +} + +Window* WindowRegistry::tryAcquireWindow (void) +{ + for (int ndx = 0; ndx < (int)m_windows.size(); ++ndx) + { + Window* const window = m_windows[ndx]; + + if (window->tryAcquire()) + return window; + } + + return DE_NULL; +} + +void WindowRegistry::garbageCollect (void) +{ + for (int ndx = 0; ndx < (int)m_windows.size(); ++ndx) + { + Window* const window = m_windows[ndx]; + + if (window->tryAcquireForDestroy(true)) + { + delete window; + m_windows[ndx] = m_windows.back(); + m_windows.pop_back(); + ndx -= 1; + } + } +} + } // Android } // tcu diff --git a/framework/platform/android/tcuAndroidWindow.hpp b/framework/platform/android/tcuAndroidWindow.hpp index 171318e..dd6bc35 100644 --- a/framework/platform/android/tcuAndroidWindow.hpp +++ b/framework/platform/android/tcuAndroidWindow.hpp @@ -26,6 +26,9 @@ #include "tcuDefs.hpp" #include "tcuVector.hpp" #include "deSemaphore.hpp" +#include "deMutex.hpp" + +#include #include @@ -34,28 +37,62 @@ namespace tcu namespace Android { +// \note Window is thread-safe, WindowRegistry is not + class Window { public: - Window (ANativeWindow* window); - ~Window (void); + enum State + { + STATE_AVAILABLE = 0, + STATE_IN_USE, + STATE_PENDING_DESTROY, + STATE_READY_FOR_DESTROY, + STATE_ACQUIRED_FOR_DESTROY, + + STATE_LAST + }; + + Window (ANativeWindow* window); + ~Window (void); - void acquire (void) { m_semaphore.decrement(); } - bool tryAcquire (void) { return m_semaphore.tryDecrement(); } - void release (void) { m_semaphore.increment(); } + bool tryAcquire (void); + void release (void); - ANativeWindow* getNativeWindow (void) { return m_window; } + void markForDestroy (void); + bool isPendingDestroy (void) const; + bool tryAcquireForDestroy(bool onlyMarked); - void setBuffersGeometry (int width, int height, int32_t format); + ANativeWindow* getNativeWindow (void) { return m_window; } - IVec2 getSize (void) const; + void setBuffersGeometry (int width, int height, int32_t format); + + IVec2 getSize (void) const; private: - Window (const Window& other); - Window& operator= (const Window& other); + Window (const Window& other); + Window& operator= (const Window& other); + + ANativeWindow* m_window; + mutable de::Mutex m_stateLock; + State m_state; +}; - ANativeWindow* m_window; - de::Semaphore m_semaphore; +class WindowRegistry +{ +public: + WindowRegistry (void); + ~WindowRegistry (void); + + void addWindow (ANativeWindow* window); + void destroyWindow (ANativeWindow* window); + + Window* tryAcquireWindow (void); + + void garbageCollect (void); + +private: + std::vector m_windows; }; } // Android