QNX: Enable rendering on different displays using OpenGL

Change-Id: Ife9c090cff732aba42a5cbc04640721e8fdde69d
Reviewed-by: Bernd Weimer <bweimer@blackberry.com>
Reviewed-by: Sean Harmer <sean.harmer@kdab.com>
This commit is contained in:
Fabian Bumberger 2013-07-08 11:12:47 +02:00 committed by The Qt Project
parent 167a444742
commit f5841521a4
6 changed files with 180 additions and 148 deletions

View File

@ -60,36 +60,10 @@ QT_BEGIN_NAMESPACE
EGLDisplay QQnxGLContext::ms_eglDisplay = EGL_NO_DISPLAY;
static EGLenum checkEGLError(const char *msg)
{
static const char *errmsg[] =
{
"EGL function succeeded",
"EGL is not initialized, or could not be initialized, for the specified display",
"EGL cannot access a requested resource",
"EGL failed to allocate resources for the requested operation",
"EGL fail to access an unrecognized attribute or attribute value was passed in an attribute list",
"EGLConfig argument does not name a valid EGLConfig",
"EGLContext argument does not name a valid EGLContext",
"EGL current surface of the calling thread is no longer valid",
"EGLDisplay argument does not name a valid EGLDisplay",
"EGL arguments are inconsistent",
"EGLNativePixmapType argument does not refer to a valid native pixmap",
"EGLNativeWindowType argument does not refer to a valid native window",
"EGL one or more argument values are invalid",
"EGLSurface argument does not name a valid surface configured for rendering",
"EGL power management event has occurred",
};
EGLenum error = eglGetError();
fprintf(stderr, "%s: %s\n", msg, errmsg[error - EGL_SUCCESS]);
return error;
}
QQnxGLContext::QQnxGLContext(QOpenGLContext *glContext)
: QPlatformOpenGLContext(),
m_glContext(glContext),
m_eglSurface(EGL_NO_SURFACE),
m_newSurfaceRequested(true) // Create a surface the first time makeCurrent() is called
m_currentEglSurface(EGL_NO_SURFACE)
{
qGLContextDebug() << Q_FUNC_INFO;
QSurfaceFormat format = m_glContext->format();
@ -168,9 +142,31 @@ QQnxGLContext::~QQnxGLContext()
// Cleanup EGL context if it exists
if (m_eglContext != EGL_NO_CONTEXT)
eglDestroyContext(ms_eglDisplay, m_eglContext);
}
// Cleanup EGL surface if it exists
destroySurface();
EGLenum QQnxGLContext::checkEGLError(const char *msg)
{
static const char *errmsg[] =
{
"EGL function succeeded",
"EGL is not initialized, or could not be initialized, for the specified display",
"EGL cannot access a requested resource",
"EGL failed to allocate resources for the requested operation",
"EGL fail to access an unrecognized attribute or attribute value was passed in an attribute list",
"EGLConfig argument does not name a valid EGLConfig",
"EGLContext argument does not name a valid EGLContext",
"EGL current surface of the calling thread is no longer valid",
"EGLDisplay argument does not name a valid EGLDisplay",
"EGL arguments are inconsistent",
"EGLNativePixmapType argument does not refer to a valid native pixmap",
"EGLNativeWindowType argument does not refer to a valid native window",
"EGL one or more argument values are invalid",
"EGLSurface argument does not name a valid surface configured for rendering",
"EGL power management event has occurred",
};
EGLenum error = eglGetError();
fprintf(stderr, "%s: %s\n", msg, errmsg[error - EGL_SUCCESS]);
return error;
}
void QQnxGLContext::initialize()
@ -199,12 +195,6 @@ void QQnxGLContext::shutdown()
eglTerminate(ms_eglDisplay);
}
void QQnxGLContext::requestSurfaceChange()
{
qGLContextDebug() << Q_FUNC_INFO;
m_newSurfaceRequested.testAndSetRelease(false, true);
}
bool QQnxGLContext::makeCurrent(QPlatformSurface *surface)
{
qGLContextDebug() << Q_FUNC_INFO;
@ -216,14 +206,18 @@ bool QQnxGLContext::makeCurrent(QPlatformSurface *surface)
if (eglResult != EGL_TRUE)
qFatal("QQnxGLContext: failed to set EGL API, err=%d", eglGetError());
if (m_newSurfaceRequested.testAndSetOrdered(true, false)) {
qGLContextDebug() << "New EGL surface requested";
QQnxWindow *platformWindow = dynamic_cast<QQnxWindow*>(surface);
if (!platformWindow)
return false;
platformWindow->setPlatformOpenGLContext(this);
if (m_currentEglSurface == EGL_NO_SURFACE || m_currentEglSurface != platformWindow->getSurface()) {
m_currentEglSurface = platformWindow->getSurface();
doneCurrent();
destroySurface();
createSurface(surface);
}
eglResult = eglMakeCurrent(ms_eglDisplay, m_eglSurface, m_eglSurface, m_eglContext);
eglResult = eglMakeCurrent(ms_eglDisplay, m_currentEglSurface, m_currentEglSurface, m_eglContext);
if (eglResult != EGL_TRUE) {
checkEGLError("eglMakeCurrent");
qFatal("QQNX: failed to set current EGL context, err=%d", eglGetError());
@ -248,18 +242,12 @@ void QQnxGLContext::doneCurrent()
void QQnxGLContext::swapBuffers(QPlatformSurface *surface)
{
Q_UNUSED(surface);
qGLContextDebug() << Q_FUNC_INFO;
QQnxWindow *platformWindow = dynamic_cast<QQnxWindow*>(surface);
if (!platformWindow)
return;
// Set current rendering API
EGLBoolean eglResult = eglBindAPI(EGL_OPENGL_ES_API);
if (eglResult != EGL_TRUE)
qFatal("QQNX: failed to set EGL API, err=%d", eglGetError());
// Post EGL surface to window
eglResult = eglSwapBuffers(ms_eglDisplay, m_eglSurface);
if (eglResult != EGL_TRUE)
qFatal("QQNX: failed to swap EGL buffers, err=%d", eglGetError());
platformWindow->swapEGLBuffers();
}
QFunctionPointer QQnxGLContext::getProcAddress(const QByteArray &procName)
@ -275,6 +263,10 @@ QFunctionPointer QQnxGLContext::getProcAddress(const QByteArray &procName)
return static_cast<QFunctionPointer>(eglGetProcAddress(procName.constData()));
}
EGLDisplay QQnxGLContext::getEglDisplay() {
return ms_eglDisplay;
}
EGLint *QQnxGLContext::contextAttrs()
{
qGLContextDebug() << Q_FUNC_INFO;
@ -288,66 +280,4 @@ EGLint *QQnxGLContext::contextAttrs()
#endif
}
bool QQnxGLContext::isCurrent() const
{
qGLContextDebug() << Q_FUNC_INFO;
return (eglGetCurrentContext() == m_eglContext);
}
void QQnxGLContext::createSurface(QPlatformSurface *surface)
{
qGLContextDebug() << Q_FUNC_INFO;
// Get a pointer to the corresponding platform window
QQnxWindow *platformWindow = dynamic_cast<QQnxWindow*>(surface);
if (!platformWindow)
qFatal("QQNX: unable to create EGLSurface without a QQnxWindow");
// Link the window and context
platformWindow->setPlatformOpenGLContext(this);
// Fetch the surface size from the window and update
// the window's buffers before we create the EGL surface
const QSize surfaceSize = platformWindow->requestedBufferSize();
if (!surfaceSize.isValid()) {
qFatal("QQNX: Trying to create 0 size EGL surface. "
"Please set a valid window size before calling QOpenGLContext::makeCurrent()");
}
platformWindow->setBufferSize(surfaceSize);
// Post root window, in case it hasn't been posted yet, to make it appear.
platformWindow->screen()->onWindowPost(platformWindow);
// Obtain the native handle for our window
screen_window_t handle = platformWindow->nativeHandle();
const EGLint eglSurfaceAttrs[] =
{
EGL_RENDER_BUFFER, EGL_BACK_BUFFER,
EGL_NONE
};
// Create EGL surface
m_eglSurface = eglCreateWindowSurface(ms_eglDisplay, m_eglConfig, (EGLNativeWindowType) handle, eglSurfaceAttrs);
if (m_eglSurface == EGL_NO_SURFACE) {
checkEGLError("eglCreateWindowSurface");
qFatal("QQNX: failed to create EGL surface, err=%d", eglGetError());
}
}
void QQnxGLContext::destroySurface()
{
qGLContextDebug() << Q_FUNC_INFO;
// Destroy EGL surface if it exists
if (m_eglSurface != EGL_NO_SURFACE) {
EGLBoolean eglResult = eglDestroySurface(ms_eglDisplay, m_eglSurface);
if (eglResult != EGL_TRUE) {
qFatal("QQNX: failed to destroy EGL surface, err=%d", eglGetError());
}
}
m_eglSurface = EGL_NO_SURFACE;
}
QT_END_NAMESPACE

View File

@ -59,6 +59,8 @@ public:
QQnxGLContext(QOpenGLContext *glContext);
virtual ~QQnxGLContext();
static EGLenum checkEGLError(const char *msg);
static void initialize();
static void shutdown();
@ -71,13 +73,10 @@ public:
virtual QSurfaceFormat format() const { return m_windowFormat; }
bool isCurrent() const;
void createSurface(QPlatformSurface *surface);
void destroySurface();
static EGLDisplay getEglDisplay();
EGLConfig getEglConfig() const { return m_eglConfig;}
private:
/** \todo Should this be non-static so we can use additional displays? */
//Can be static because different displays returne the same handle
static EGLDisplay ms_eglDisplay;
QSurfaceFormat m_windowFormat;
@ -85,9 +84,7 @@ private:
EGLConfig m_eglConfig;
EGLContext m_eglContext;
EGLSurface m_eglSurface;
QAtomicInt m_newSurfaceRequested;
EGLSurface m_currentEglSurface;
static EGLint *contextAttrs();
};

View File

@ -126,7 +126,6 @@ QQnxScreen::QQnxScreen(screen_context_t screenContext, screen_display_t display,
m_posted(false),
m_keyboardHeight(0),
m_nativeOrientation(Qt::PrimaryOrientation),
m_platformContext(0),
m_cursor(new QQnxCursor())
{
qScreenDebug() << Q_FUNC_INFO;
@ -482,9 +481,14 @@ void QQnxScreen::updateHierarchy()
int topZorder;
errno = 0;
result = screen_get_window_property_iv(rootWindow()->nativeHandle(), SCREEN_PROPERTY_ZORDER, &topZorder);
if (result != 0)
qFatal("QQnxScreen: failed to query root window z-order, errno=%d", errno);
if (isPrimaryScreen()) {
result = screen_get_window_property_iv(rootWindow()->nativeHandle(), SCREEN_PROPERTY_ZORDER, &topZorder);
if (result != 0)
qFatal("QQnxScreen: failed to query root window z-order, errno=%d", errno);
} else {
topZorder = 0; //We do not need z ordering on the secondary screen, because only one window
//is supported there
}
topZorder++; // root window has the lowest z-order in the windowgroup

View File

@ -133,7 +133,6 @@ private:
Qt::ScreenOrientation m_nativeOrientation;
QRect m_initialGeometry;
QRect m_currentGeometry;
QPlatformOpenGLContext *m_platformContext;
QList<QQnxWindow *> m_childWindows;
QList<screen_window_t> m_overlays;

View File

@ -72,13 +72,15 @@ QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context)
m_window(0),
m_currentBufferIndex(-1),
m_previousBufferIndex(-1),
#if !defined(QT_NO_OPENGL)
m_platformOpenGLContext(0),
#endif
m_screen(0),
m_parentWindow(0),
m_visible(false),
m_windowState(Qt::WindowNoState),
#if !defined(QT_NO_OPENGL)
m_platformOpenGLContext(0),
m_newSurfaceRequested(true),
m_eglSurface(EGL_NO_SURFACE),
#endif
m_requestedBufferSize(window->geometry().size())
{
qWindowDebug() << Q_FUNC_INFO << "window =" << window << ", size =" << window->size();
@ -86,7 +88,11 @@ QQnxWindow::QQnxWindow(QWindow *window, screen_context_t context)
// Create child QNX window
errno = 0;
result = screen_create_window_type(&m_window, m_screenContext, SCREEN_CHILD_WINDOW);
if (static_cast<QQnxScreen *>(window->screen()->handle())->isPrimaryScreen()) {
result = screen_create_window_type(&m_window, m_screenContext, SCREEN_CHILD_WINDOW);
} else {
result = screen_create_window(&m_window, m_screenContext);
}
if (result != 0)
qFatal("QQnxWindow: failed to create window, errno=%d", errno);
@ -172,6 +178,11 @@ QQnxWindow::~QQnxWindow()
// Cleanup QNX window and its buffers
screen_destroy_window(m_window);
#if !defined(QT_NO_OPENGL)
// Cleanup EGL surface if it exists
destroyEGLSurface();
#endif
}
void QQnxWindow::setGeometry(const QRect &rect)
@ -180,16 +191,16 @@ void QQnxWindow::setGeometry(const QRect &rect)
#if !defined(QT_NO_OPENGL)
// If this is an OpenGL window we need to request that the GL context updates
// the EGLsurface on which it is rendering. The surface will be recreated the
// next time QQnxGLContext::makeCurrent() is called.
// the EGLsurface on which it is rendering.
{
// We want the setting of the atomic bool in the GL context to be atomic with
// setting m_requestedBufferSize and therefore extended the scope to include
// that test.
const QMutexLocker locker(&m_mutex);
m_requestedBufferSize = rect.size();
if (m_platformOpenGLContext != 0 && bufferSize() != rect.size())
m_platformOpenGLContext->requestSurfaceChange();
if (m_platformOpenGLContext != 0 && bufferSize() != rect.size()) {
m_newSurfaceRequested.testAndSetRelease(false, true);
}
}
#endif
@ -517,8 +528,12 @@ void QQnxWindow::setScreen(QQnxScreen *platformScreen)
if (m_screen == platformScreen)
return;
if (m_screen)
if (m_screen) {
qWindowDebug() << Q_FUNC_INFO << "Moving window to different screen";
m_screen->removeWindow(this);
screen_leave_window_group(m_window);
}
platformScreen->addWindow(this);
m_screen = platformScreen;
@ -529,17 +544,20 @@ void QQnxWindow::setScreen(QQnxScreen *platformScreen)
if (result != 0)
qFatal("QQnxWindow: failed to set window display, errno=%d", errno);
// Add window to display's window group
errno = 0;
result = screen_join_window_group(m_window, platformScreen->windowGroupName());
if (result != 0)
qFatal("QQnxWindow: failed to join window group, errno=%d", errno);
Q_FOREACH (QQnxWindow *childWindow, m_childWindows) {
// Only subwindows and tooltips need necessarily be moved to another display with the window.
if ((window()->type() & Qt::WindowType_Mask) == Qt::SubWindow ||
(window()->type() & Qt::WindowType_Mask) == Qt::ToolTip)
childWindow->setScreen(platformScreen);
if (m_screen->isPrimaryScreen()) {
// Add window to display's window group
errno = 0;
result = screen_join_window_group(m_window, platformScreen->windowGroupName());
if (result != 0)
qFatal("QQnxWindow: failed to join window group, errno=%d", errno);
Q_FOREACH (QQnxWindow *childWindow, m_childWindows) {
// Only subwindows and tooltips need necessarily be moved to another display with the window.
if ((window()->type() & Qt::WindowType_Mask) == Qt::SubWindow ||
(window()->type() & Qt::WindowType_Mask) == Qt::ToolTip)
childWindow->setScreen(platformScreen);
}
}
m_screen->updateHierarchy();
@ -718,6 +736,79 @@ void QQnxWindow::minimize()
#endif
}
#if !defined(QT_NO_OPENGL)
void QQnxWindow::createEGLSurface()
{
// Fetch the surface size from the window and update
// the window's buffers before we create the EGL surface
const QSize surfaceSize = requestedBufferSize();
if (!surfaceSize.isValid()) {
qFatal("QQNX: Trying to create 0 size EGL surface. "
"Please set a valid window size before calling QOpenGLContext::makeCurrent()");
}
setBufferSize(surfaceSize);
// Post root window, in case it hasn't been posted yet, to make it appear.
screen()->onWindowPost(0);
const EGLint eglSurfaceAttrs[] =
{
EGL_RENDER_BUFFER, EGL_BACK_BUFFER,
EGL_NONE
};
qWindowDebug() << "Creating EGL surface" << platformOpenGLContext()->getEglDisplay()
<< platformOpenGLContext()->getEglConfig();
// Create EGL surface
m_eglSurface = eglCreateWindowSurface(platformOpenGLContext()->getEglDisplay()
, platformOpenGLContext()->getEglConfig(),
(EGLNativeWindowType) m_window, eglSurfaceAttrs);
if (m_eglSurface == EGL_NO_SURFACE) {
QQnxGLContext::checkEGLError("eglCreateWindowSurface");
qFatal("QQNX: failed to create EGL surface, err=%d", eglGetError());
}
}
void QQnxWindow::destroyEGLSurface()
{
// Destroy EGL surface if it exists
if (m_eglSurface != EGL_NO_SURFACE) {
EGLBoolean eglResult = eglDestroySurface(platformOpenGLContext()->getEglDisplay(), m_eglSurface);
if (eglResult != EGL_TRUE)
qFatal("QQNX: failed to destroy EGL surface, err=%d", eglGetError());
}
m_eglSurface = EGL_NO_SURFACE;
}
void QQnxWindow::swapEGLBuffers()
{
qWindowDebug() << Q_FUNC_INFO;
// Set current rendering API
EGLBoolean eglResult = eglBindAPI(EGL_OPENGL_ES_API);
if (eglResult != EGL_TRUE)
qFatal("QQNX: failed to set EGL API, err=%d", eglGetError());
// Post EGL surface to window
eglResult = eglSwapBuffers(m_platformOpenGLContext->getEglDisplay(), m_eglSurface);
if (eglResult != EGL_TRUE)
qFatal("QQNX: failed to swap EGL buffers, err=%d", eglGetError());
}
EGLSurface QQnxWindow::getSurface()
{
if (m_newSurfaceRequested.testAndSetOrdered(true, false)) {
if (m_eglSurface != EGL_NO_SURFACE) {
platformOpenGLContext()->doneCurrent();
destroyEGLSurface();
}
createEGLSurface();
}
return m_eglSurface;
}
#endif
void QQnxWindow::updateZorder(int &topZorder)
{
errno = 0;

View File

@ -120,6 +120,13 @@ public:
void blitFrom(QQnxWindow *sourceWindow, const QPoint &sourceOffset, const QRegion &targetRegion);
void minimize();
#if !defined(QT_NO_OPENGL)
void createEGLSurface();
void destroyEGLSurface();
void swapEGLBuffers();
EGLSurface getSurface();
#endif
private:
QRect setGeometryHelper(const QRect &rect);
void removeFromParent();
@ -147,9 +154,6 @@ private:
QRegion m_previousDirty;
QRegion m_scrolled;
#if !defined(QT_NO_OPENGL)
QQnxGLContext *m_platformOpenGLContext;
#endif
QQnxScreen *m_screen;
QList<QQnxWindow*> m_childWindows;
QQnxWindow *m_parentWindow;
@ -164,6 +168,13 @@ private:
// EGL surface. All of this has to be done from the thread that is calling
// QQnxGLContext::makeCurrent()
mutable QMutex m_mutex;
#if !defined(QT_NO_OPENGL)
QQnxGLContext *m_platformOpenGLContext;
QAtomicInt m_newSurfaceRequested;
EGLSurface m_eglSurface;
#endif
QSize m_requestedBufferSize;
};