android:versionName="1.0">
<application android:label="dEQP Tests"
android:icon="@drawable/deqp_app">
-
<activity android:name="com.drawelements.deqp.execserver.ExecServerActivity"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:label="dEQP ExecServer"
android:label="dEQP ExecServer Service"
android:exported="true"
android:process=":execserver" />
-
- <!-- Tester process -->
<activity android:name="android.app.NativeActivity"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:label="dEQP Test Process"
- android:configChanges="orientation|screenSize|keyboardHidden"
+ android:configChanges="keyboardHidden|orientation|screenSize"
android:exported="true"
android:process=":testercore">
<meta-data android:name="android.app.lib_name"
<meta-data android:name="android.app.func_name"
android:value="createTestActivity" />
</activity>
- </application>
-
- <uses-sdk android:minSdkVersion="13"/>
+ </application>
+
+ <uses-sdk android:minSdkVersion="13"/>
+ <uses-sdk android:targetSdkVersion="19"/>
<uses-feature android:glEsVersion="0x00020000"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.GET_TASKS" />
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;
};
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)
{
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
{
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:
class NativeDisplayFactory : public eglu::NativeDisplayFactory
{
public:
- NativeDisplayFactory (Window& window);
+ NativeDisplayFactory (WindowRegistry& windowRegistry);
~NativeDisplayFactory (void) {}
virtual eglu::NativeDisplay* createDisplay (const EGLAttrib* attribList) const;
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)
{
}
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
// 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));
}
{
}
+bool Platform::processEvents (void)
+{
+ m_windowRegistry.garbageCollect();
+ return true;
+}
+
} // Android
} // tcu
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<const glu::Platform&>(*this); }
virtual const eglu::Platform& getEGLPlatform (void) const { return static_cast<const eglu::Platform&>(*this); }
+ WindowRegistry& getWindowRegistry (void) { return m_windowRegistry; }
+
private:
- Window m_window;
+ WindowRegistry m_windowRegistry;
};
} // Android
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)
Thread::start();
}
-void RenderThread::destroy (void)
+void RenderThread::stop (void)
{
// Queue finish command
enqueue(Message(MESSAGE_FINISH));
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.
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;
// 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)
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)
void RenderActivity::onStop (void)
{
DBG_PRINT(("RenderActivity::onStop()"));
- m_thread->destroy();
}
void RenderActivity::onDestroy (void)
void start (void);
void resume (void);
void pause (void);
- void destroy (void);
+ void stop (void);
+
void enqueue (const Message& message);
void sync (void);
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)
{
}
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
: 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()));
{
}
-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");
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:
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
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
namespace Android
{
+using std::vector;
+
+// Window
+
Window::Window (ANativeWindow* window)
- : m_window (window)
- , m_semaphore (1)
+ : m_window (window)
+ , m_state (STATE_AVAILABLE)
{
}
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<Window*>::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
#include "tcuDefs.hpp"
#include "tcuVector.hpp"
#include "deSemaphore.hpp"
+#include "deMutex.hpp"
+
+#include <vector>
#include <android/native_window.h>
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<Window*> m_windows;
};
} // Android