You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
394 lines
9.9 KiB
394 lines
9.9 KiB
/*-------------------------------------------------------------------------
|
|
* drawElements Quality Program Tester Core
|
|
* ----------------------------------------
|
|
*
|
|
* Copyright 2014 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
*//*!
|
|
* \file
|
|
* \brief RenderActivity base class.
|
|
*//*--------------------------------------------------------------------*/
|
|
|
|
#include "tcuAndroidRenderActivity.hpp"
|
|
#include "deSemaphore.hpp"
|
|
|
|
#include <android/window.h>
|
|
|
|
#include <string>
|
|
#include <stdlib.h>
|
|
|
|
using std::string;
|
|
|
|
#if defined(DE_DEBUG)
|
|
# define DBG_PRINT(X) print X
|
|
#else
|
|
# define DBG_PRINT(X)
|
|
#endif
|
|
|
|
namespace tcu
|
|
{
|
|
namespace Android
|
|
{
|
|
|
|
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)
|
|
: m_activity (activity)
|
|
, m_msgQueue (MESSAGE_QUEUE_SIZE)
|
|
, m_threadRunning (false)
|
|
, m_inputQueue (DE_NULL)
|
|
, m_windowState (WINDOWSTATE_NOT_CREATED)
|
|
, m_window (DE_NULL)
|
|
, m_paused (false)
|
|
, m_finish (false)
|
|
, m_receivedFirstResize(false)
|
|
{
|
|
}
|
|
|
|
RenderThread::~RenderThread (void)
|
|
{
|
|
}
|
|
|
|
void RenderThread::start (void)
|
|
{
|
|
m_threadRunning = true;
|
|
Thread::start();
|
|
}
|
|
|
|
void RenderThread::stop (void)
|
|
{
|
|
// Queue finish command
|
|
enqueue(Message(MESSAGE_FINISH));
|
|
|
|
// Wait for thread to terminate
|
|
join();
|
|
|
|
m_threadRunning = false;
|
|
}
|
|
|
|
void RenderThread::enqueue (const Message& message)
|
|
{
|
|
// \note Thread must be running or otherwise nobody is going to drain the queue.
|
|
DE_ASSERT(m_threadRunning);
|
|
m_msgQueue.pushFront(message);
|
|
}
|
|
|
|
void RenderThread::pause (void)
|
|
{
|
|
enqueue(Message(MESSAGE_PAUSE));
|
|
}
|
|
|
|
void RenderThread::resume (void)
|
|
{
|
|
enqueue(Message(MESSAGE_RESUME));
|
|
}
|
|
|
|
void RenderThread::sync (void)
|
|
{
|
|
de::Semaphore waitSem(0);
|
|
enqueue(Message(MESSAGE_SYNC, &waitSem));
|
|
waitSem.decrement();
|
|
}
|
|
|
|
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");
|
|
|
|
// The documented behavior for the callbacks is that the native activity
|
|
// will get a call to onNativeWindowCreated(), at which point it should have
|
|
// a surface to render to, and can then start immediately.
|
|
//
|
|
// The actual creation process has the framework making calls to both
|
|
// onNativeWindowCreated() and then onNativeWindowResized(). The test
|
|
// waits for that first resize before it considers the window ready for
|
|
// rendering.
|
|
//
|
|
// However subsequent events in the framework may cause the window to be
|
|
// recreated at a new position without a size change, which sends on
|
|
// onNativeWindowDestroyed(), and then on onNativeWindowCreated() without
|
|
// a follow-up onNativeWindowResized(). If this happens, the test will
|
|
// stop rendering as it is no longer in the ready state, and a watchdog
|
|
// thread will eventually kill the test, causing it to fail. We therefore
|
|
// set the window state back to READY and process the window creation here
|
|
// if we have already observed that first resize call.
|
|
if (!m_receivedFirstResize) {
|
|
m_windowState = WINDOWSTATE_NOT_INITIALIZED;
|
|
} else {
|
|
m_windowState = WINDOWSTATE_READY;
|
|
onWindowCreated(message.payload.window);
|
|
}
|
|
m_window = message.payload.window;
|
|
break;
|
|
|
|
case MESSAGE_WINDOW_RESIZED:
|
|
if (m_window != message.payload.window)
|
|
throw InternalError("Got onNativeWindowResized() event targeting different window");
|
|
|
|
// Record that we've the first resize event, in case the window is
|
|
// recreated later without a resize.
|
|
m_receivedFirstResize = true;
|
|
|
|
if (m_windowState == WINDOWSTATE_NOT_INITIALIZED)
|
|
{
|
|
// Got first resize event, window is ready for use.
|
|
m_windowState = WINDOWSTATE_READY;
|
|
onWindowCreated(message.payload.window);
|
|
}
|
|
else if (m_windowState == WINDOWSTATE_READY)
|
|
onWindowResized(message.payload.window);
|
|
else
|
|
throw InternalError("Got unexpected onNativeWindowResized() event from system");
|
|
|
|
break;
|
|
|
|
case MESSAGE_WINDOW_DESTROYED:
|
|
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");
|
|
|
|
if (m_windowState == WINDOWSTATE_READY)
|
|
onWindowDestroyed(message.payload.window);
|
|
|
|
m_windowState = WINDOWSTATE_DESTROYED;
|
|
m_window = DE_NULL;
|
|
break;
|
|
|
|
case MESSAGE_INPUT_QUEUE_CREATED:
|
|
m_inputQueue = message.payload.inputQueue;
|
|
break;
|
|
|
|
case MESSAGE_INPUT_QUEUE_DESTROYED:
|
|
m_inputQueue = message.payload.inputQueue;
|
|
break;
|
|
|
|
case MESSAGE_SYNC:
|
|
message.payload.semaphore->increment();
|
|
break;
|
|
|
|
default:
|
|
throw std::runtime_error("Unknown message type");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void RenderThread::run (void)
|
|
{
|
|
// Init state
|
|
m_windowState = WINDOWSTATE_NOT_CREATED;
|
|
m_paused = true;
|
|
m_finish = false;
|
|
|
|
try
|
|
{
|
|
while (!m_finish)
|
|
{
|
|
if (m_paused || m_windowState != WINDOWSTATE_READY)
|
|
{
|
|
// Block until we are not paused and window is ready.
|
|
Message msg = m_msgQueue.popBack();
|
|
processMessage(msg);
|
|
continue;
|
|
}
|
|
|
|
// Process available commands
|
|
{
|
|
Message msg;
|
|
if (m_msgQueue.tryPopBack(msg))
|
|
{
|
|
processMessage(msg);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
DE_ASSERT(m_windowState == WINDOWSTATE_READY);
|
|
|
|
// Process input events.
|
|
// \todo [2013-05-08 pyry] What if system fills up the input queue before we have window ready?
|
|
while (m_inputQueue &&
|
|
AInputQueue_hasEvents(m_inputQueue) > 0)
|
|
{
|
|
AInputEvent* event;
|
|
TCU_CHECK(AInputQueue_getEvent(m_inputQueue, &event) >= 0);
|
|
onInputEvent(event);
|
|
AInputQueue_finishEvent(m_inputQueue, event, 1);
|
|
}
|
|
|
|
// Everything set up - safe to render.
|
|
if (!render())
|
|
{
|
|
DBG_PRINT(("RenderThread::run(): render\n"));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
print("RenderThread: %s\n", e.what());
|
|
}
|
|
|
|
// Tell activity to finish.
|
|
DBG_PRINT(("RenderThread::run(): done, waiting for FINISH\n"));
|
|
m_activity.finish();
|
|
|
|
// Thread must keep draining message queue until FINISH message is encountered.
|
|
try
|
|
{
|
|
while (!m_finish)
|
|
{
|
|
Message msg = m_msgQueue.popBack();
|
|
|
|
// Ignore all but SYNC and FINISH messages.
|
|
if (msg.type == MESSAGE_SYNC || msg.type == MESSAGE_FINISH)
|
|
processMessage(msg);
|
|
}
|
|
}
|
|
catch (const std::exception& e)
|
|
{
|
|
die("RenderThread: %s\n", e.what());
|
|
}
|
|
|
|
DBG_PRINT(("RenderThread::run(): exiting...\n"));
|
|
}
|
|
|
|
// RenderActivity
|
|
|
|
RenderActivity::RenderActivity (ANativeActivity* activity)
|
|
: NativeActivity(activity)
|
|
, m_thread (DE_NULL)
|
|
{
|
|
DBG_PRINT(("RenderActivity::RenderActivity()"));
|
|
}
|
|
|
|
RenderActivity::~RenderActivity (void)
|
|
{
|
|
DBG_PRINT(("RenderActivity::~RenderActivity()"));
|
|
}
|
|
|
|
void RenderActivity::setThread (RenderThread* thread)
|
|
{
|
|
m_thread = thread;
|
|
}
|
|
|
|
void RenderActivity::onStart (void)
|
|
{
|
|
DBG_PRINT(("RenderActivity::onStart()"));
|
|
}
|
|
|
|
void RenderActivity::onResume (void)
|
|
{
|
|
DBG_PRINT(("RenderActivity::onResume()"));
|
|
|
|
// Resume (or start) test execution
|
|
m_thread->resume();
|
|
}
|
|
|
|
void RenderActivity::onPause (void)
|
|
{
|
|
DBG_PRINT(("RenderActivity::onPause()"));
|
|
|
|
// Pause test execution
|
|
m_thread->pause();
|
|
}
|
|
|
|
void RenderActivity::onStop (void)
|
|
{
|
|
DBG_PRINT(("RenderActivity::onStop()"));
|
|
}
|
|
|
|
void RenderActivity::onDestroy (void)
|
|
{
|
|
DBG_PRINT(("RenderActivity::onDestroy()"));
|
|
}
|
|
|
|
void RenderActivity::onNativeWindowCreated (ANativeWindow* window)
|
|
{
|
|
DBG_PRINT(("RenderActivity::onNativeWindowCreated()"));
|
|
m_thread->enqueue(Message(MESSAGE_WINDOW_CREATED, window));
|
|
}
|
|
|
|
void RenderActivity::onNativeWindowResized (ANativeWindow* window)
|
|
{
|
|
DBG_PRINT(("RenderActivity::onNativeWindowResized()"));
|
|
m_thread->enqueue(Message(MESSAGE_WINDOW_RESIZED, window));
|
|
}
|
|
|
|
void RenderActivity::onNativeWindowRedrawNeeded (ANativeWindow* window)
|
|
{
|
|
DE_UNREF(window);
|
|
}
|
|
|
|
void RenderActivity::onNativeWindowDestroyed (ANativeWindow* window)
|
|
{
|
|
DBG_PRINT(("RenderActivity::onNativeWindowDestroyed()"));
|
|
m_thread->enqueue(Message(MESSAGE_WINDOW_DESTROYED, window));
|
|
m_thread->sync(); // Block until thread has processed all messages.
|
|
}
|
|
|
|
void RenderActivity::onInputQueueCreated (AInputQueue* queue)
|
|
{
|
|
DBG_PRINT(("RenderActivity::onInputQueueCreated()"));
|
|
m_thread->enqueue(Message(MESSAGE_INPUT_QUEUE_CREATED, queue));
|
|
}
|
|
|
|
void RenderActivity::onInputQueueDestroyed (AInputQueue* queue)
|
|
{
|
|
DBG_PRINT(("RenderActivity::onInputQueueDestroyed()"));
|
|
m_thread->enqueue(Message(MESSAGE_INPUT_QUEUE_DESTROYED, queue));
|
|
m_thread->sync();
|
|
}
|
|
|
|
} // Android
|
|
} // tcu
|