2011-04-27 10:05:43 +00:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
2017-08-22 14:29:29 +00:00
|
|
|
** Copyright (C) 2017 The Qt Company Ltd.
|
2016-01-15 07:08:27 +00:00
|
|
|
** Contact: https://www.qt.io/licensing/
|
2011-04-27 10:05:43 +00:00
|
|
|
**
|
|
|
|
** This file is part of the plugins of the Qt Toolkit.
|
|
|
|
**
|
2016-01-15 07:08:27 +00:00
|
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
2012-09-19 12:28:29 +00:00
|
|
|
** 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
|
2015-01-28 08:44:43 +00:00
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
2016-01-15 07:08:27 +00:00
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
2012-09-19 12:28:29 +00:00
|
|
|
**
|
2011-04-27 10:05:43 +00:00
|
|
|
** GNU Lesser General Public License Usage
|
2012-09-19 12:28:29 +00:00
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
2016-01-15 07:08:27 +00:00
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
** ensure the GNU Lesser General Public License version 3 requirements
|
|
|
|
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
2011-04-27 10:05:43 +00:00
|
|
|
**
|
2016-01-15 07:08:27 +00:00
|
|
|
** GNU General Public License Usage
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
** General Public License version 2.0 or (at your option) the GNU General
|
|
|
|
** Public license version 3 or any later version approved by the KDE Free
|
|
|
|
** Qt Foundation. The licenses are as published by the Free Software
|
|
|
|
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
|
|
|
** https://www.gnu.org/licenses/gpl-3.0.html.
|
2011-04-27 10:05:43 +00:00
|
|
|
**
|
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
2014-01-06 13:51:37 +00:00
|
|
|
#include <QtGui/QOpenGLContext>
|
2014-11-19 13:58:43 +00:00
|
|
|
#include <QtGui/QWindow>
|
2014-06-24 13:05:49 +00:00
|
|
|
#include <QtGui/QPainter>
|
2016-01-26 14:48:19 +00:00
|
|
|
#include <QtGui/QOffscreenSurface>
|
2014-11-19 13:58:43 +00:00
|
|
|
#include <qpa/qplatformbackingstore.h>
|
2015-12-01 11:31:14 +00:00
|
|
|
#include <private/qwindow_p.h>
|
2014-01-06 13:51:37 +00:00
|
|
|
|
2014-11-19 13:58:43 +00:00
|
|
|
#include "qopenglcompositorbackingstore_p.h"
|
|
|
|
#include "qopenglcompositor_p.h"
|
2012-04-16 19:24:41 +00:00
|
|
|
|
2015-09-15 13:51:43 +00:00
|
|
|
#ifndef GL_UNPACK_ROW_LENGTH
|
|
|
|
#define GL_UNPACK_ROW_LENGTH 0x0CF2
|
|
|
|
#endif
|
|
|
|
|
2011-04-27 10:05:43 +00:00
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
2014-01-06 13:51:37 +00:00
|
|
|
/*!
|
2014-11-19 13:58:43 +00:00
|
|
|
\class QOpenGLCompositorBackingStore
|
|
|
|
\brief A backing store implementation for OpenGL
|
|
|
|
\since 5.4
|
2014-01-06 13:51:37 +00:00
|
|
|
\internal
|
|
|
|
\ingroup qpa
|
|
|
|
|
|
|
|
This implementation uploads raster-rendered widget windows into
|
2014-11-19 13:58:43 +00:00
|
|
|
textures. It is meant to be used with QOpenGLCompositor that
|
|
|
|
composites the textures onto a single native window using OpenGL.
|
|
|
|
This means that multiple top-level widgets are supported without
|
|
|
|
creating actual native windows for each of them.
|
|
|
|
|
|
|
|
\note It is important to call notifyComposited() from the
|
|
|
|
corresponding platform window's endCompositing() callback
|
|
|
|
(inherited from QOpenGLCompositorWindow).
|
|
|
|
|
|
|
|
\note When implementing QOpenGLCompositorWindow::textures() for
|
|
|
|
windows of type RasterSurface or RasterGLSurface, simply return
|
|
|
|
the list provided by this class' textures().
|
2014-01-06 13:51:37 +00:00
|
|
|
*/
|
|
|
|
|
2014-11-19 13:58:43 +00:00
|
|
|
QOpenGLCompositorBackingStore::QOpenGLCompositorBackingStore(QWindow *window)
|
2013-10-08 12:55:28 +00:00
|
|
|
: QPlatformBackingStore(window),
|
2014-11-19 13:58:43 +00:00
|
|
|
m_window(window),
|
2014-02-11 13:59:30 +00:00
|
|
|
m_bsTexture(0),
|
2015-11-12 16:26:21 +00:00
|
|
|
m_bsTextureContext(0),
|
2014-02-11 13:59:30 +00:00
|
|
|
m_textures(new QPlatformTextureList),
|
|
|
|
m_lockedWidgetTextures(0)
|
2013-09-13 11:02:20 +00:00
|
|
|
{
|
|
|
|
}
|
2012-04-16 19:24:41 +00:00
|
|
|
|
2014-11-19 13:58:43 +00:00
|
|
|
QOpenGLCompositorBackingStore::~QOpenGLCompositorBackingStore()
|
2014-02-11 13:59:30 +00:00
|
|
|
{
|
2015-11-12 16:26:21 +00:00
|
|
|
if (m_bsTexture) {
|
|
|
|
QOpenGLContext *ctx = QOpenGLContext::currentContext();
|
2016-01-26 14:48:19 +00:00
|
|
|
// With render-to-texture-widgets QWidget makes sure the TLW's shareContext() is
|
|
|
|
// made current before destroying backingstores. That is however not the case for
|
|
|
|
// windows with regular widgets only.
|
|
|
|
QScopedPointer<QOffscreenSurface> tempSurface;
|
|
|
|
if (!ctx) {
|
|
|
|
ctx = QOpenGLCompositor::instance()->context();
|
2022-01-20 17:29:35 +00:00
|
|
|
if (ctx) {
|
|
|
|
tempSurface.reset(new QOffscreenSurface);
|
|
|
|
tempSurface->setFormat(ctx->format());
|
|
|
|
tempSurface->create();
|
|
|
|
ctx->makeCurrent(tempSurface.data());
|
|
|
|
}
|
2016-01-26 14:48:19 +00:00
|
|
|
}
|
|
|
|
|
2022-01-20 17:29:35 +00:00
|
|
|
if (m_bsTextureContext && ctx && ctx->shareGroup() == m_bsTextureContext->shareGroup())
|
2015-11-12 16:26:21 +00:00
|
|
|
glDeleteTextures(1, &m_bsTexture);
|
|
|
|
else
|
|
|
|
qWarning("QOpenGLCompositorBackingStore: Texture is not valid in the current context");
|
2016-01-26 14:48:19 +00:00
|
|
|
|
2022-01-20 17:29:35 +00:00
|
|
|
if (tempSurface && ctx)
|
2016-01-26 14:48:19 +00:00
|
|
|
ctx->doneCurrent();
|
2015-11-12 16:26:21 +00:00
|
|
|
}
|
|
|
|
|
2016-01-26 14:48:19 +00:00
|
|
|
delete m_textures; // this does not actually own any GL resources
|
2014-02-11 13:59:30 +00:00
|
|
|
}
|
|
|
|
|
2014-11-19 13:58:43 +00:00
|
|
|
QPaintDevice *QOpenGLCompositorBackingStore::paintDevice()
|
2013-09-13 11:02:20 +00:00
|
|
|
{
|
|
|
|
return &m_image;
|
|
|
|
}
|
2012-04-16 19:24:41 +00:00
|
|
|
|
2014-11-19 13:58:43 +00:00
|
|
|
void QOpenGLCompositorBackingStore::updateTexture()
|
2013-09-13 11:02:20 +00:00
|
|
|
{
|
2014-02-11 13:59:30 +00:00
|
|
|
if (!m_bsTexture) {
|
2015-11-12 16:26:21 +00:00
|
|
|
m_bsTextureContext = QOpenGLContext::currentContext();
|
|
|
|
Q_ASSERT(m_bsTextureContext);
|
2014-02-11 13:59:30 +00:00
|
|
|
glGenTextures(1, &m_bsTexture);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, m_bsTexture);
|
|
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
2014-05-08 14:36:51 +00:00
|
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
2014-02-11 13:59:30 +00:00
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_image.width(), m_image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
|
|
|
|
} else {
|
|
|
|
glBindTexture(GL_TEXTURE_2D, m_bsTexture);
|
|
|
|
}
|
2012-04-16 19:24:41 +00:00
|
|
|
|
2012-04-17 06:46:13 +00:00
|
|
|
if (!m_dirty.isNull()) {
|
2013-09-13 11:02:20 +00:00
|
|
|
QRegion fixed;
|
2012-04-17 06:46:13 +00:00
|
|
|
QRect imageRect = m_image.rect();
|
|
|
|
|
2015-09-15 13:51:43 +00:00
|
|
|
QOpenGLContext *ctx = QOpenGLContext::currentContext();
|
|
|
|
if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) {
|
2016-02-27 13:34:46 +00:00
|
|
|
for (const QRect &rect : m_dirty) {
|
2015-09-15 13:51:43 +00:00
|
|
|
QRect r = imageRect & rect;
|
|
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, m_image.width());
|
|
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, r.x(), r.y(), r.width(), r.height(), GL_RGBA, GL_UNSIGNED_BYTE,
|
|
|
|
m_image.constScanLine(r.y()) + r.x() * 4);
|
|
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
2012-04-17 06:46:13 +00:00
|
|
|
}
|
2015-09-15 13:51:43 +00:00
|
|
|
} else {
|
2016-02-27 13:34:46 +00:00
|
|
|
for (const QRect &rect : m_dirty) {
|
2015-09-15 13:51:43 +00:00
|
|
|
// intersect with image rect to be sure
|
|
|
|
QRect r = imageRect & rect;
|
|
|
|
|
|
|
|
// if the rect is wide enough it's cheaper to just
|
|
|
|
// extend it instead of doing an image copy
|
|
|
|
if (r.width() >= imageRect.width() / 2) {
|
|
|
|
r.setX(0);
|
|
|
|
r.setWidth(imageRect.width());
|
|
|
|
}
|
|
|
|
|
|
|
|
fixed |= r;
|
|
|
|
}
|
2016-02-27 13:34:46 +00:00
|
|
|
for (const QRect &rect : fixed) {
|
2015-09-15 13:51:43 +00:00
|
|
|
// if the sub-rect is full-width we can pass the image data directly to
|
|
|
|
// OpenGL instead of copying, since there's no gap between scanlines
|
|
|
|
if (rect.width() == imageRect.width()) {
|
|
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, rect.y(), rect.width(), rect.height(), GL_RGBA, GL_UNSIGNED_BYTE,
|
|
|
|
m_image.constScanLine(rect.y()));
|
|
|
|
} else {
|
|
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.width(), rect.height(), GL_RGBA, GL_UNSIGNED_BYTE,
|
|
|
|
m_image.copy(rect).constBits());
|
|
|
|
}
|
2012-04-17 06:46:13 +00:00
|
|
|
}
|
2012-04-16 19:24:41 +00:00
|
|
|
}
|
|
|
|
|
2012-04-17 06:46:13 +00:00
|
|
|
m_dirty = QRegion();
|
|
|
|
}
|
2013-09-13 11:02:20 +00:00
|
|
|
}
|
2012-04-16 19:24:41 +00:00
|
|
|
|
2014-11-19 13:58:43 +00:00
|
|
|
void QOpenGLCompositorBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoint &offset)
|
2013-09-13 11:02:20 +00:00
|
|
|
{
|
2016-01-27 10:20:45 +00:00
|
|
|
// Called for ordinary raster windows.
|
2014-02-11 13:59:30 +00:00
|
|
|
|
2013-09-13 11:02:20 +00:00
|
|
|
Q_UNUSED(region);
|
|
|
|
Q_UNUSED(offset);
|
2012-04-16 19:24:41 +00:00
|
|
|
|
2014-11-19 13:58:43 +00:00
|
|
|
QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
|
|
|
|
QOpenGLContext *dstCtx = compositor->context();
|
2016-01-27 10:53:25 +00:00
|
|
|
Q_ASSERT(dstCtx);
|
|
|
|
|
2014-11-19 13:58:43 +00:00
|
|
|
QWindow *dstWin = compositor->targetWindow();
|
|
|
|
if (!dstWin)
|
2014-02-11 13:59:30 +00:00
|
|
|
return;
|
|
|
|
|
2014-11-19 13:58:43 +00:00
|
|
|
dstCtx->makeCurrent(dstWin);
|
2014-02-11 13:59:30 +00:00
|
|
|
updateTexture();
|
|
|
|
m_textures->clear();
|
2017-09-18 09:49:52 +00:00
|
|
|
m_textures->appendTexture(nullptr, m_bsTexture, window->geometry());
|
2014-11-19 13:58:43 +00:00
|
|
|
|
|
|
|
compositor->update();
|
2014-02-11 13:59:30 +00:00
|
|
|
}
|
|
|
|
|
2014-11-19 13:58:43 +00:00
|
|
|
void QOpenGLCompositorBackingStore::composeAndFlush(QWindow *window, const QRegion ®ion, const QPoint &offset,
|
2017-10-03 17:24:50 +00:00
|
|
|
QPlatformTextureList *textures,
|
2014-08-13 08:05:52 +00:00
|
|
|
bool translucentBackground)
|
2014-02-11 13:59:30 +00:00
|
|
|
{
|
2016-01-27 10:20:45 +00:00
|
|
|
// QOpenGLWidget/QQuickWidget content provided as textures. The raster content goes on top.
|
2014-02-11 13:59:30 +00:00
|
|
|
|
|
|
|
Q_UNUSED(region);
|
|
|
|
Q_UNUSED(offset);
|
2014-08-13 08:05:52 +00:00
|
|
|
Q_UNUSED(translucentBackground);
|
2012-04-16 19:24:41 +00:00
|
|
|
|
2014-11-19 13:58:43 +00:00
|
|
|
QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
|
|
|
|
QOpenGLContext *dstCtx = compositor->context();
|
2016-01-27 10:53:25 +00:00
|
|
|
Q_ASSERT(dstCtx); // setTarget() must have been called before, e.g. from QEGLFSWindow
|
|
|
|
|
|
|
|
// The compositor's context and the context to which QOpenGLWidget/QQuickWidget
|
|
|
|
// textures belong are not the same. They share resources, though.
|
2017-10-03 17:24:50 +00:00
|
|
|
Q_ASSERT(qt_window_private(window)->shareContext()->shareGroup() == dstCtx->shareGroup());
|
2016-01-27 10:53:25 +00:00
|
|
|
|
2014-11-19 13:58:43 +00:00
|
|
|
QWindow *dstWin = compositor->targetWindow();
|
|
|
|
if (!dstWin)
|
2013-10-08 12:55:28 +00:00
|
|
|
return;
|
2012-05-27 05:34:29 +00:00
|
|
|
|
2014-11-19 13:58:43 +00:00
|
|
|
dstCtx->makeCurrent(dstWin);
|
2014-02-11 13:59:30 +00:00
|
|
|
|
2015-12-01 11:31:14 +00:00
|
|
|
QWindowPrivate::get(window)->lastComposeTime.start();
|
|
|
|
|
2014-02-11 13:59:30 +00:00
|
|
|
m_textures->clear();
|
2014-06-20 09:58:34 +00:00
|
|
|
for (int i = 0; i < textures->count(); ++i)
|
2015-06-01 13:23:19 +00:00
|
|
|
m_textures->appendTexture(textures->source(i), textures->textureId(i), textures->geometry(i),
|
2015-06-01 09:44:37 +00:00
|
|
|
textures->clipRect(i), textures->flags(i));
|
2014-02-11 13:59:30 +00:00
|
|
|
|
2013-10-08 12:55:28 +00:00
|
|
|
updateTexture();
|
2017-09-18 09:49:52 +00:00
|
|
|
m_textures->appendTexture(nullptr, m_bsTexture, window->geometry());
|
2014-02-11 13:59:30 +00:00
|
|
|
|
|
|
|
textures->lock(true);
|
|
|
|
m_lockedWidgetTextures = textures;
|
|
|
|
|
2014-11-19 13:58:43 +00:00
|
|
|
compositor->update();
|
2012-04-16 19:24:41 +00:00
|
|
|
}
|
|
|
|
|
2014-11-19 13:58:43 +00:00
|
|
|
void QOpenGLCompositorBackingStore::notifyComposited()
|
2014-02-11 13:59:30 +00:00
|
|
|
{
|
|
|
|
if (m_lockedWidgetTextures) {
|
|
|
|
QPlatformTextureList *textureList = m_lockedWidgetTextures;
|
|
|
|
m_lockedWidgetTextures = 0; // may reenter so null before unlocking
|
|
|
|
textureList->lock(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-19 13:58:43 +00:00
|
|
|
void QOpenGLCompositorBackingStore::beginPaint(const QRegion ®ion)
|
2012-04-16 19:24:41 +00:00
|
|
|
{
|
2014-06-24 13:05:49 +00:00
|
|
|
m_dirty |= region;
|
|
|
|
|
|
|
|
if (m_image.hasAlphaChannel()) {
|
|
|
|
QPainter p(&m_image);
|
|
|
|
p.setCompositionMode(QPainter::CompositionMode_Source);
|
2016-02-27 13:34:46 +00:00
|
|
|
for (const QRect &r : region)
|
2014-06-24 13:05:49 +00:00
|
|
|
p.fillRect(r, Qt::transparent);
|
|
|
|
}
|
2012-03-23 07:51:15 +00:00
|
|
|
}
|
|
|
|
|
2014-11-19 13:58:43 +00:00
|
|
|
void QOpenGLCompositorBackingStore::resize(const QSize &size, const QRegion &staticContents)
|
2011-04-27 10:05:43 +00:00
|
|
|
{
|
2011-07-25 12:55:45 +00:00
|
|
|
Q_UNUSED(staticContents);
|
2012-04-16 19:24:41 +00:00
|
|
|
|
2014-11-19 13:58:43 +00:00
|
|
|
QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
|
|
|
|
QOpenGLContext *dstCtx = compositor->context();
|
|
|
|
QWindow *dstWin = compositor->targetWindow();
|
|
|
|
if (!dstWin)
|
2013-10-08 12:55:28 +00:00
|
|
|
return;
|
|
|
|
|
2014-07-08 16:29:29 +00:00
|
|
|
m_image = QImage(size, QImage::Format_RGBA8888);
|
2014-06-24 13:05:49 +00:00
|
|
|
|
2013-09-13 11:02:20 +00:00
|
|
|
m_window->create();
|
2013-10-08 12:55:28 +00:00
|
|
|
|
2014-11-19 13:58:43 +00:00
|
|
|
dstCtx->makeCurrent(dstWin);
|
2014-02-11 13:59:30 +00:00
|
|
|
if (m_bsTexture) {
|
|
|
|
glDeleteTextures(1, &m_bsTexture);
|
|
|
|
m_bsTexture = 0;
|
2017-09-18 09:49:52 +00:00
|
|
|
m_bsTextureContext = nullptr;
|
2014-02-11 13:59:30 +00:00
|
|
|
}
|
|
|
|
}
|
2013-09-13 11:02:20 +00:00
|
|
|
|
2014-11-19 13:58:43 +00:00
|
|
|
QImage QOpenGLCompositorBackingStore::toImage() const
|
2014-02-11 13:59:30 +00:00
|
|
|
{
|
|
|
|
return m_image;
|
2011-04-27 10:05:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QT_END_NAMESPACE
|