diff options
author | Laszlo Agocs <laszlo.agocs@digia.com> | 2014-11-19 14:58:43 +0100 |
---|---|---|
committer | Laszlo Agocs <laszlo.agocs@theqtcompany.com> | 2014-11-26 09:31:16 +0100 |
commit | 37cce4cadace48a22cbc5252810237341a371b10 (patch) | |
tree | 8cbe588c8ee3ad7e06428736bedeac94e7f49d3e /src/platformsupport/platformcompositor/qopenglcompositor.cpp | |
parent | e41713b21dad247aca1af493aa7e06f0822dc4ea (diff) |
Add generic OpenGL compositor and backingstore bits
By removing the EGL(FS) dependencies we get classes that can be used
by any platform plugin that runs without a windowing system.
Change-Id: If99b42de5a4da02bbef80863609b6d92c6734613
Reviewed-by: Jørgen Lind <jorgen.lind@theqtcompany.com>
Diffstat (limited to 'src/platformsupport/platformcompositor/qopenglcompositor.cpp')
-rw-r--r-- | src/platformsupport/platformcompositor/qopenglcompositor.cpp | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/src/platformsupport/platformcompositor/qopenglcompositor.cpp b/src/platformsupport/platformcompositor/qopenglcompositor.cpp new file mode 100644 index 0000000000..3fefdc4935 --- /dev/null +++ b/src/platformsupport/platformcompositor/qopenglcompositor.cpp @@ -0,0 +1,243 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtGui/QOpenGLContext> +#include <QtGui/QWindow> +#include <QtGui/QMatrix4x4> +#include <qpa/qplatformbackingstore.h> + +#include "qopenglcompositor_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QOpenGLCompositor + \brief A generic OpenGL-based compositor + \since 5.4 + \internal + \ingroup qpa + + This class provides a lightweight compositor that maintains the + basic stacking order of windows and composites them by drawing + textured quads via OpenGL. + + It it meant to be used by platform plugins that run without a + windowing system. + + It is up to the platform plugin to manage the lifetime of the + compositor (instance(), destroy()), set the correct destination + context and window as early as possible (setTargetWindow()), + register the composited windows as they are shown, activated, + raised and lowered (addWindow(), moveToTop(), etc.), and to + schedule repaints (update()). + + \note To get support for QWidget-based windows, just use + QOpenGLCompositorBackingStore. It will automatically create + textures from the raster-rendered content and trigger the + necessary repaints. + */ + +static QOpenGLCompositor *compositor = 0; + +QOpenGLCompositor::QOpenGLCompositor() + : m_context(0), + m_targetWindow(0) +{ + Q_ASSERT(!compositor); + m_updateTimer.setSingleShot(true); + m_updateTimer.setInterval(0); + connect(&m_updateTimer, SIGNAL(timeout()), SLOT(renderAll())); +} + +QOpenGLCompositor::~QOpenGLCompositor() +{ + Q_ASSERT(compositor == this); + m_blitter.destroy(); + compositor = 0; +} + +void QOpenGLCompositor::setTarget(QOpenGLContext *context, QWindow *targetWindow) +{ + m_context = context; + m_targetWindow = targetWindow; +} + +void QOpenGLCompositor::update() +{ + if (!m_updateTimer.isActive()) + m_updateTimer.start(); +} + +void QOpenGLCompositor::renderAll() +{ + Q_ASSERT(m_context && m_targetWindow); + m_context->makeCurrent(m_targetWindow); + + 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()); + + if (!m_blitter.isCreated()) + m_blitter.create(); + + m_blitter.bind(); + + for (int i = 0; i < m_windows.size(); ++i) + m_windows.at(i)->beginCompositing(); + + for (int i = 0; i < m_windows.size(); ++i) + render(m_windows.at(i)); + + m_blitter.release(); + m_context->swapBuffers(m_targetWindow); + + for (int i = 0; i < m_windows.size(); ++i) + m_windows.at(i)->endCompositing(); +} + +struct BlendStateBinder +{ + BlendStateBinder() : m_blend(false) { + glDisable(GL_BLEND); + } + void set(bool blend) { + if (blend != m_blend) { + if (blend) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } else { + glDisable(GL_BLEND); + } + m_blend = blend; + } + } + ~BlendStateBinder() { + if (m_blend) + glDisable(GL_BLEND); + } + bool m_blend; +}; + +void QOpenGLCompositor::render(QOpenGLCompositorWindow *window) +{ + const QPlatformTextureList *textures = window->textures(); + if (!textures) + return; + + const QRect targetWindowRect(QPoint(0, 0), m_targetWindow->geometry().size()); + float currentOpacity = 1.0f; + BlendStateBinder blend; + + for (int i = 0; i < textures->count(); ++i) { + uint textureId = textures->textureId(i); + QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect); + const float opacity = window->sourceWindow()->opacity(); + if (opacity != currentOpacity) { + currentOpacity = opacity; + m_blitter.setOpacity(currentOpacity); + } + + if (textures->count() > 1 && i == textures->count() - 1) { + // Backingstore for a widget with QOpenGLWidget subwidgets + blend.set(true); + 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); + m_blitter.blit(textureId, target, QOpenGLTextureBlitter::OriginTopLeft); + } else if (!textures->stacksOnTop(i)) { + // Texture from an FBO belonging to a QOpenGLWidget + blend.set(false); + m_blitter.blit(textureId, target, QOpenGLTextureBlitter::OriginBottomLeft); + } + } + + for (int i = 0; i < textures->count(); ++i) { + if (textures->stacksOnTop(i)) { + QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect); + blend.set(true); + m_blitter.blit(textures->textureId(i), target, QOpenGLTextureBlitter::OriginBottomLeft); + } + } + + m_blitter.setOpacity(1.0f); +} + +QOpenGLCompositor *QOpenGLCompositor::instance() +{ + if (!compositor) + compositor = new QOpenGLCompositor; + return compositor; +} + +void QOpenGLCompositor::destroy() +{ + delete compositor; + compositor = 0; +} + +void QOpenGLCompositor::addWindow(QOpenGLCompositorWindow *window) +{ + if (!m_windows.contains(window)) { + m_windows.append(window); + emit topWindowChanged(window); + } +} + +void QOpenGLCompositor::removeWindow(QOpenGLCompositorWindow *window) +{ + m_windows.removeOne(window); + if (!m_windows.isEmpty()) + emit topWindowChanged(m_windows.last()); +} + +void QOpenGLCompositor::moveToTop(QOpenGLCompositorWindow *window) +{ + m_windows.removeOne(window); + m_windows.append(window); + emit topWindowChanged(window); +} + +void QOpenGLCompositor::changeWindowIndex(QOpenGLCompositorWindow *window, int newIdx) +{ + int idx = m_windows.indexOf(window); + if (idx != -1 && idx != newIdx) { + m_windows.move(idx, newIdx); + if (newIdx == m_windows.size() - 1) + emit topWindowChanged(m_windows.last()); + } +} + +QT_END_NAMESPACE |