eglfs: Add support for raster content rotation

Running QWidget or other raster window-based apps with
QT_QPA_EGLFS_ROTATION=180 will now flip the screen as expected.

In addition, 90 and -90 are supported too. These will affect the
reported screen geometry as well.

The OpenGL mouse cursor is repositioned and rotated accordingly. For
true OpenGL content the rotation is ignored. Hardware mouse cursors
(DRM) ignore it as well for the time being.

[ChangeLog][Platform Specific Changes] QWidget-based applications
running on the eglfs platform plugin can now request 180 or 90 degrees
rotated output by setting the QT_QPA_EGLFS_ROTATION environment
variable.

Task-number: QTBUG-39959
Change-Id: I3570b515d0233c6c6a0e813899b935ee222eab62
Reviewed-by: Andy Nichols <andy.nichols@qt.io>
This commit is contained in:
Laszlo Agocs 2016-07-28 11:59:06 +02:00
parent 97c00e66c8
commit c4b4eba188
7 changed files with 75 additions and 17 deletions

View File

@ -40,7 +40,6 @@
#include <QtGui/QOpenGLContext>
#include <QtGui/QOpenGLFramebufferObject>
#include <QtGui/QWindow>
#include <QtGui/QMatrix4x4>
#include <qpa/qplatformbackingstore.h>
#include "qopenglcompositor_p.h"
@ -78,7 +77,8 @@ static QOpenGLCompositor *compositor = 0;
QOpenGLCompositor::QOpenGLCompositor()
: m_context(0),
m_targetWindow(0)
m_targetWindow(0),
m_rotation(0)
{
Q_ASSERT(!compositor);
m_updateTimer.setSingleShot(true);
@ -93,10 +93,19 @@ QOpenGLCompositor::~QOpenGLCompositor()
compositor = 0;
}
void QOpenGLCompositor::setTarget(QOpenGLContext *context, QWindow *targetWindow)
void QOpenGLCompositor::setTarget(QOpenGLContext *context, QWindow *targetWindow,
const QRect &nativeTargetGeometry)
{
m_context = context;
m_targetWindow = targetWindow;
m_nativeTargetGeometry = nativeTargetGeometry;
}
void QOpenGLCompositor::setRotation(int degrees)
{
m_rotation = degrees;
m_rotationMatrix.setToIdentity();
m_rotationMatrix.rotate(degrees, 0, 0, 1);
}
void QOpenGLCompositor::update()
@ -109,7 +118,7 @@ QImage QOpenGLCompositor::grab()
{
Q_ASSERT(m_context && m_targetWindow);
m_context->makeCurrent(m_targetWindow);
QScopedPointer<QOpenGLFramebufferObject> fbo(new QOpenGLFramebufferObject(m_targetWindow->geometry().size()));
QScopedPointer<QOpenGLFramebufferObject> fbo(new QOpenGLFramebufferObject(m_nativeTargetGeometry.size()));
renderAll(fbo.data());
return fbo->toImage();
}
@ -127,9 +136,7 @@ void QOpenGLCompositor::renderAll(QOpenGLFramebufferObject *fbo)
fbo->bind();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
const QRect targetWindowRect(QPoint(0, 0), m_targetWindow->geometry().size());
glViewport(0, 0, targetWindowRect.width(), targetWindowRect.height());
glViewport(0, 0, m_nativeTargetGeometry.width(), m_nativeTargetGeometry.height());
if (!m_blitter.isCreated())
m_blitter.create();
@ -181,7 +188,8 @@ static inline QRect toBottomLeftRect(const QRect &topLeftRect, int windowHeight)
topLeftRect.width(), topLeftRect.height());
}
static void clippedBlit(const QPlatformTextureList *textures, int idx, const QRect &targetWindowRect, QOpenGLTextureBlitter *blitter)
static void clippedBlit(const QPlatformTextureList *textures, int idx, const QRect &targetWindowRect,
QOpenGLTextureBlitter *blitter, QMatrix4x4 *rotationMatrix)
{
const QRect clipRect = textures->clipRect(idx);
if (clipRect.isEmpty())
@ -191,7 +199,10 @@ static void clippedBlit(const QPlatformTextureList *textures, int idx, const QRe
const QRect clippedRectInWindow = rectInWindow & clipRect.translated(rectInWindow.topLeft());
const QRect srcRect = toBottomLeftRect(clipRect, rectInWindow.height());
const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(clippedRectInWindow, targetWindowRect);
QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(clippedRectInWindow, targetWindowRect);
if (rotationMatrix)
target = *rotationMatrix * target;
const QMatrix3x3 source = QOpenGLTextureBlitter::sourceTransform(srcRect, rectInWindow.size(),
QOpenGLTextureBlitter::OriginBottomLeft);
@ -219,25 +230,29 @@ void QOpenGLCompositor::render(QOpenGLCompositorWindow *window)
if (textures->count() > 1 && i == textures->count() - 1) {
// Backingstore for a widget with QOpenGLWidget subwidgets
blend.set(true);
const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect);
QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect);
if (m_rotation)
target = m_rotationMatrix * target;
m_blitter.blit(textureId, target, QOpenGLTextureBlitter::OriginTopLeft);
} else if (textures->count() == 1) {
// A regular QWidget window
const bool translucent = window->sourceWindow()->requestedFormat().alphaBufferSize() > 0;
blend.set(translucent);
const QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect);
QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect);
if (m_rotation)
target = m_rotationMatrix * target;
m_blitter.blit(textureId, target, QOpenGLTextureBlitter::OriginTopLeft);
} else if (!textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) {
// Texture from an FBO belonging to a QOpenGLWidget
blend.set(false);
clippedBlit(textures, i, targetWindowRect, &m_blitter);
clippedBlit(textures, i, targetWindowRect, &m_blitter, m_rotation ? &m_rotationMatrix : nullptr);
}
}
for (int i = 0; i < textures->count(); ++i) {
if (textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) {
blend.set(true);
clippedBlit(textures, i, targetWindowRect, &m_blitter);
clippedBlit(textures, i, targetWindowRect, &m_blitter, m_rotation ? &m_rotationMatrix : nullptr);
}
}

View File

@ -53,6 +53,7 @@
#include <QtCore/QTimer>
#include <QtGui/QOpenGLTextureBlitter>
#include <QtGui/QMatrix4x4>
QT_BEGIN_NAMESPACE
@ -78,7 +79,8 @@ public:
static QOpenGLCompositor *instance();
static void destroy();
void setTarget(QOpenGLContext *context, QWindow *window);
void setTarget(QOpenGLContext *context, QWindow *window, const QRect &nativeTargetGeometry);
void setRotation(int degrees);
QOpenGLContext *context() const { return m_context; }
QWindow *targetWindow() const { return m_targetWindow; }
@ -106,6 +108,9 @@ private:
QOpenGLContext *m_context;
QWindow *m_targetWindow;
QRect m_nativeTargetGeometry;
int m_rotation;
QMatrix4x4 m_rotationMatrix;
QTimer m_updateTimer;
QOpenGLTextureBlitter m_blitter;
QList<QOpenGLCompositorWindow *> m_windows;

View File

@ -72,6 +72,10 @@ QEglFSCursor::QEglFSCursor(QPlatformScreen *screen)
if (!m_visible)
return;
int rotation = qEnvironmentVariableIntValue("QT_QPA_EGLFS_ROTATION");
if (rotation)
m_rotationMatrix.rotate(rotation, 0, 0, 1);
// Try to load the cursor atlas. If this fails, m_visible is set to false and
// paintOnScreen() and setCurrentCursor() become no-ops.
initCursorAtlas();
@ -129,9 +133,10 @@ void QEglFSCursor::createShaderPrograms()
"attribute highp vec2 vertexCoordEntry;\n"
"attribute highp vec2 textureCoordEntry;\n"
"varying highp vec2 textureCoord;\n"
"uniform highp mat4 mat;\n"
"void main() {\n"
" textureCoord = textureCoordEntry;\n"
" gl_Position = vec4(vertexCoordEntry, 1.0, 1.0);\n"
" gl_Position = mat * vec4(vertexCoordEntry, 1.0, 1.0);\n"
"}\n";
static const char *textureFragmentProgram =
@ -149,6 +154,7 @@ void QEglFSCursor::createShaderPrograms()
m_program->link();
m_textureEntry = m_program->uniformLocation("texture");
m_matEntry = m_program->uniformLocation("mat");
}
void QEglFSCursor::createCursorTexture(uint *texture, const QImage &image)
@ -492,6 +498,7 @@ void QEglFSCursor::draw(const QRectF &r)
m_program->setAttributeArray(1, textureCoordinates, 2);
m_program->setUniformValue(m_textureEntry, 0);
m_program->setUniformValue(m_matEntry, m_rotationMatrix);
glDisable(GL_CULL_FACE);
glFrontFace(GL_CCW);

View File

@ -54,6 +54,7 @@
#include "qeglfsglobal.h"
#include <qpa/qplatformcursor.h>
#include <qpa/qplatformscreen.h>
#include <QtGui/QMatrix4x4>
#include <QtGui/QOpenGLFunctions>
#include <QtGui/private/qinputdevicemanager_p.h>
@ -138,8 +139,10 @@ private:
QEglFSScreen *m_screen;
QOpenGLShaderProgram *m_program;
int m_textureEntry;
int m_matEntry;
QEglFSCursorDeviceListener *m_deviceListener;
bool m_updateRequested;
QMatrix4x4 m_rotationMatrix;
};
QT_END_NAMESPACE

View File

@ -64,6 +64,31 @@ QEglFSScreen::~QEglFSScreen()
}
QRect QEglFSScreen::geometry() const
{
QRect r = geometryForSurface();
static int rotation = qEnvironmentVariableIntValue("QT_QPA_EGLFS_ROTATION");
switch (rotation) {
case 0:
case 180:
case -180:
break;
case 90:
case -90: {
int h = r.height();
r.setHeight(r.width());
r.setWidth(h);
break;
}
default:
qWarning("Invalid rotation %d specified in QT_QPA_EGLFS_ROTATION", rotation);
break;
}
return r;
}
QRect QEglFSScreen::geometryForSurface() const
{
return QRect(QPoint(0, 0), qt_egl_device_integration()->screenSize());
}

View File

@ -68,6 +68,7 @@ public:
~QEglFSScreen();
QRect geometry() const Q_DECL_OVERRIDE;
QRect geometryForSurface() const;
int depth() const Q_DECL_OVERRIDE;
QImage::Format format() const Q_DECL_OVERRIDE;

View File

@ -142,7 +142,8 @@ void QEglFSWindow::create()
context->setScreen(window()->screen());
if (Q_UNLIKELY(!context->create()))
qFatal("EGLFS: Failed to create compositing context");
compositor->setTarget(context, window());
compositor->setTarget(context, window(), screen->geometryForSurface());
compositor->setRotation(qEnvironmentVariableIntValue("QT_QPA_EGLFS_ROTATION"));
// If there is a "root" window into which raster and QOpenGLWidget content is
// composited, all other contexts must share with its context.
if (!qt_gl_global_share_context()) {
@ -189,7 +190,8 @@ void QEglFSWindow::resetSurface()
m_config = QEglFSDeviceIntegration::chooseConfig(display, platformFormat);
m_format = q_glFormatFromConfig(display, m_config, platformFormat);
m_window = qt_egl_device_integration()->createNativeWindow(this, screen()->geometry().size(), m_format);
const QSize surfaceSize = screen()->geometryForSurface().size();
m_window = qt_egl_device_integration()->createNativeWindow(this, surfaceSize, m_format);
m_surface = eglCreateWindowSurface(display, m_config, m_window, NULL);
}