Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qopenglcompositor.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <QtOpenGL/QOpenGLFramebufferObject>
5#include <QtGui/QOpenGLContext>
6#include <QtGui/QWindow>
7#include <rhi/qrhi.h>
8#include <qpa/qplatformbackingstore.h>
9
10#include "qopenglcompositor_p.h"
11
13
42
43QOpenGLCompositor::QOpenGLCompositor()
44 : m_context(0),
45 m_targetWindow(0),
46 m_rotation(0)
47{
49 m_updateTimer.setSingleShot(true);
50 m_updateTimer.setInterval(0);
51 connect(&m_updateTimer, SIGNAL(timeout()), SLOT(handleRenderAllRequest()));
52}
53
54QOpenGLCompositor::~QOpenGLCompositor()
55{
56 Q_ASSERT(compositor == this);
57 m_blitter.destroy();
58 compositor = 0;
59}
60
61void QOpenGLCompositor::setTargetWindow(QWindow *targetWindow, const QRect &nativeTargetGeometry)
62{
63 m_targetWindow = targetWindow;
64 m_nativeTargetGeometry = nativeTargetGeometry;
65}
66
71
73{
74 m_rotation = degrees;
75 m_rotationMatrix.setToIdentity();
76 m_rotationMatrix.rotate(degrees, 0, 0, 1);
77}
78
80{
81 if (!m_updateTimer.isActive())
82 m_updateTimer.start();
83}
84
86{
87 Q_ASSERT(m_context && m_targetWindow);
88 QOpenGLFramebufferObject fbo(m_nativeTargetGeometry.size());
90 return fbo.toImage();
91}
92
94{
95 Q_ASSERT(fbo);
96 if (fbo->size() != m_nativeTargetGeometry.size()
97 || fbo->format().textureTarget() != GL_TEXTURE_2D)
98 return false;
99
100 m_context->makeCurrent(m_targetWindow);
101 renderAll(fbo,
104 return true;
105}
106
107void QOpenGLCompositor::handleRenderAllRequest()
108{
109 Q_ASSERT(m_context && m_targetWindow);
110 m_context->makeCurrent(m_targetWindow);
111 renderAll(0);
112}
113
114void QOpenGLCompositor::renderAll(QOpenGLFramebufferObject *fbo, QOpenGLTextureBlitter::Origin origin)
115{
116 if (fbo)
117 fbo->bind();
118
119 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
120 glViewport(0, 0, m_nativeTargetGeometry.width(), m_nativeTargetGeometry.height());
121
122 if (!m_blitter.isCreated())
123 m_blitter.create();
124
125 m_blitter.bind();
126
127 for (int i = 0; i < m_windows.size(); ++i)
128 m_windows.at(i)->beginCompositing();
129
130 for (int i = 0; i < m_windows.size(); ++i)
131 render(m_windows.at(i), origin);
132
133 m_blitter.release();
134 if (!fbo)
135 m_context->swapBuffers(m_targetWindow);
136 else
137 fbo->release();
138
139 for (int i = 0; i < m_windows.size(); ++i)
140 m_windows.at(i)->endCompositing();
141}
142
144{
146 glDisable(GL_BLEND);
147 }
148 void set(bool blend) {
149 if (blend != m_blend) {
150 if (blend) {
151 glEnable(GL_BLEND);
152 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
153 } else {
154 glDisable(GL_BLEND);
155 }
156 m_blend = blend;
157 }
158 }
160 if (m_blend)
161 glDisable(GL_BLEND);
162 }
164};
165
166static inline QRect toBottomLeftRect(const QRect &topLeftRect, int windowHeight)
167{
168 return QRect(topLeftRect.x(), windowHeight - topLeftRect.bottomRight().y() - 1,
169 topLeftRect.width(), topLeftRect.height());
170}
171
172static void clippedBlit(const QPlatformTextureList *textures, int idx,
173 const QRect &sourceWindowRect, const QRect &targetWindowRect,
174 QOpenGLTextureBlitter *blitter, QMatrix4x4 *rotationMatrix,
176{
177 const QRect clipRect = textures->clipRect(idx);
178 if (clipRect.isEmpty())
179 return;
180
181 const QRect rectInWindow = textures->geometry(idx).translated(sourceWindowRect.topLeft());
182 const QRect clippedRectInWindow = rectInWindow & clipRect.translated(rectInWindow.topLeft());
183 const QRect srcRect = toBottomLeftRect(clipRect, rectInWindow.height());
184
185 QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(clippedRectInWindow, targetWindowRect);
186 if (rotationMatrix)
187 target = *rotationMatrix * target;
188
189 const QMatrix3x3 source = QOpenGLTextureBlitter::sourceTransform(srcRect, rectInWindow.size(),
190 sourceOrigin);
191
192 const uint textureId = textures->texture(idx)->nativeTexture().object;
193 blitter->blit(textureId, target, source);
194}
195
196void QOpenGLCompositor::render(QOpenGLCompositorWindow *window, QOpenGLTextureBlitter::Origin origin)
197{
198 const QPlatformTextureList *textures = window->textures();
199 if (!textures)
200 return;
201
202 const QRect targetWindowRect(QPoint(0, 0), m_targetWindow->geometry().size());
203 float currentOpacity = 1.0f;
204 BlendStateBinder blend;
205 const QRect sourceWindowRect = window->sourceWindow()->geometry();
206 auto clippedBlitSourceOrigin = origin == QOpenGLTextureBlitter::OriginTopLeft
209 for (int i = 0; i < textures->count(); ++i) {
210 const uint textureId = textures->texture(i)->nativeTexture().object;
211 const float opacity = window->sourceWindow()->opacity();
212 if (opacity != currentOpacity) {
213 currentOpacity = opacity;
214 m_blitter.setOpacity(currentOpacity);
215 }
216
217 if (textures->count() > 1 && i == textures->count() - 1) {
218 // Backingstore for a widget with QOpenGLWidget subwidgets
219 blend.set(true);
220 QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect);
221 if (m_rotation)
222 target = m_rotationMatrix * target;
223 m_blitter.blit(textureId, target, origin);
224 } else if (textures->count() == 1) {
225 // A regular QWidget window
226 const bool translucent = window->sourceWindow()->requestedFormat().alphaBufferSize() > 0;
227 blend.set(translucent);
228 QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect);
229 if (m_rotation)
230 target = m_rotationMatrix * target;
231 m_blitter.blit(textureId, target, origin);
232 } else if (!textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) {
233 // Texture from an FBO belonging to a QOpenGLWidget or QQuickWidget
234 blend.set(false);
235 clippedBlit(textures, i, sourceWindowRect, targetWindowRect, &m_blitter,
236 m_rotation ? &m_rotationMatrix : nullptr, clippedBlitSourceOrigin);
237 }
238 }
239
240 for (int i = 0; i < textures->count(); ++i) {
241 if (textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) {
242 blend.set(true);
243 clippedBlit(textures, i, sourceWindowRect, targetWindowRect, &m_blitter,
244 m_rotation ? &m_rotationMatrix : nullptr, clippedBlitSourceOrigin);
245 }
246 }
247
248 m_blitter.setOpacity(1.0f);
249}
250
257
259{
260 delete compositor;
261 compositor = 0;
262}
263
265{
266 if (!m_windows.contains(window)) {
267 m_windows.append(window);
268 ensureCorrectZOrder();
269 if (window == m_windows.constLast())
271 }
272}
273
275{
276 m_windows.removeOne(window);
277 if (!m_windows.isEmpty())
278 emit topWindowChanged(m_windows.last());
279}
280
282{
283 if (!m_windows.isEmpty() && window == m_windows.constLast()) {
284 // Already on top
285 return;
286 }
287
288 m_windows.removeOne(window);
289 m_windows.append(window);
290 ensureCorrectZOrder();
291
292 if (window == m_windows.constLast())
294}
295
297{
298 int idx = m_windows.indexOf(window);
299 if (idx != -1 && idx != newIdx) {
300 m_windows.move(idx, newIdx);
301 ensureCorrectZOrder();
302 if (window == m_windows.constLast())
303 emit topWindowChanged(m_windows.last());
304 }
305}
306
307void QOpenGLCompositor::ensureCorrectZOrder()
308{
309 const auto originalOrder = m_windows;
310
311 std::sort(m_windows.begin(), m_windows.end(),
312 [this, &originalOrder](QOpenGLCompositorWindow *cw1, QOpenGLCompositorWindow *cw2) {
313 QWindow *w1 = cw1->sourceWindow();
314 QWindow *w2 = cw2->sourceWindow();
315
316 // Case #1: The main window needs to have less z-order. It can never be in
317 // front of our tool windows, popups etc, because it's fullscreen!
318 if (w1 == m_targetWindow || w2 == m_targetWindow)
319 return w1 == m_targetWindow;
320
321 // Case #2:
322 if (w2->isAncestorOf(w1)) {
323 // w1 is transient child of w2. W1 goes in front then.
324 return false;
325 }
326
327 if (w1->isAncestorOf(w2)) {
328 // Or the other way around
329 return true;
330 }
331
332 // Case #3: Modality gets higher Z
333 if (w1->modality() != Qt::NonModal && w2->modality() == Qt::NonModal)
334 return false;
335
336 if (w2->modality() != Qt::NonModal && w1->modality() == Qt::NonModal)
337 return true;
338
339 const bool isTool1 = (w1->flags() & Qt::Tool) == Qt::Tool;
340 const bool isTool2 = (w2->flags() & Qt::Tool) == Qt::Tool;
341 const bool isPurePopup1 = !isTool1 && (w1->flags() & Qt::Popup) == Qt::Popup;
342 const bool isPurePopup2 = !isTool2 && (w2->flags() & Qt::Popup) == Qt::Popup;
343
344 // Case #4: By pure-popup we mean menus and tooltips. Qt::Tool implies Qt::Popup
345 // and we don't want to catch QDockWidget and other tool windows just yet
346 if (isPurePopup1 != isPurePopup2)
347 return !isPurePopup1;
348
349 // Case #5: One of the window is a Tool, that goes to front, as done in other QPAs
350 if (isTool1 != isTool2)
351 return !isTool1;
352
353 // Case #6: Just preserve original sorting:
354 return originalOrder.indexOf(cw1) < originalOrder.indexOf(cw2);
355 });
356}
357
359
360#include "moc_qopenglcompositor_p.cpp"
\inmodule QtGui
Definition qimage.h:37
qsizetype size() const noexcept
Definition qlist.h:397
bool isEmpty() const noexcept
Definition qlist.h:401
T & last()
Definition qlist.h:648
const T & constLast() const noexcept
Definition qlist.h:650
bool removeOne(const AT &t)
Definition qlist.h:598
iterator end()
Definition qlist.h:626
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
void move(qsizetype from, qsizetype to)
Definition qlist.h:610
iterator begin()
Definition qlist.h:625
void append(parameter_type t)
Definition qlist.h:458
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
void rotate(float angle, const QVector3D &vector)
Multiples this matrix by another that rotates coordinates through angle degrees about vector.
void setToIdentity()
Sets this matrix to the identity.
Definition qmatrix4x4.h:316
A generic OpenGL-based compositor.
QRect nativeTargetGeometry() const
void setTargetContext(QOpenGLContext *context)
void removeWindow(QOpenGLCompositorWindow *window)
static QOpenGLCompositor * instance()
void setTargetWindow(QWindow *window, const QRect &nativeTargetGeometry)
void moveToTop(QOpenGLCompositorWindow *window)
void setRotation(int degrees)
QOpenGLContext * context() const
QWindow * targetWindow() const
void topWindowChanged(QOpenGLCompositorWindow *window)
void addWindow(QOpenGLCompositorWindow *window)
bool grabToFrameBufferObject(QOpenGLFramebufferObject *fbo, GrabOrientation orientation=Flipped)
void changeWindowIndex(QOpenGLCompositorWindow *window, int newIdx)
\inmodule QtGui
bool makeCurrent(QSurface *surface)
Makes the context current in the current thread, against the given surface.
void swapBuffers(QSurface *surface)
Swap the back and front buffers of surface.
GLenum textureTarget() const
Returns the texture target of the texture attached to a framebuffer object.
The QOpenGLFramebufferObject class encapsulates an OpenGL framebuffer object.
QOpenGLFramebufferObjectFormat format() const
Returns the format of this framebuffer object.
bool release()
Switches rendering back to the default, windowing system provided framebuffer.
QImage toImage(bool flipped=true) const
Returns the contents of this framebuffer object as a QImage.
bool bind()
Switches rendering from the default, windowing system provided framebuffer to this framebuffer object...
The QOpenGLTextureBlitter class provides a convenient way to draw textured quads via OpenGL.
bool create()
Initializes the graphics resources used by the blitter.
void destroy()
Frees all graphics resources held by the blitter.
Origin
\value OriginBottomLeft Indicates that the data in the texture follows the OpenGL convention of coord...
void setOpacity(float opacity)
Changes the opacity to opacity.
void blit(GLuint texture, const QMatrix4x4 &targetTransform, Origin sourceOrigin)
Performs the blit with the source texture texture.
static QMatrix3x3 sourceTransform(const QRectF &subTexture, const QSize &textureSize, Origin origin)
Calculates a 3x3 matrix suitable as the input to blit().
void release()
Unbinds the graphics resources used by the blitter.
static QMatrix4x4 targetTransform(const QRectF &target, const QRect &viewport)
Calculates a target transform suitable for blit().
void bind(GLenum target=GL_TEXTURE_2D)
Binds the graphics resources used by the blitter.
\inmodule QtCore\reentrant
Definition qpoint.h:25
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr bool isEmpty() const noexcept
Returns true if the rectangle is empty, otherwise returns false.
Definition qrect.h:167
constexpr int height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:239
constexpr QSize size() const noexcept
Returns the size of the rectangle.
Definition qrect.h:242
constexpr int width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:236
constexpr QRect translated(int dx, int dy) const noexcept
Returns a copy of the rectangle that is translated dx along the x axis and dy along the y axis,...
Definition qrect.h:261
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
Definition qtimer.cpp:241
bool isActive() const
Returns true if the timer is running (pending); otherwise returns false.
Definition qtimer.cpp:167
\inmodule QtGui
Definition qwindow.h:63
Combined button and popup list for selecting options.
@ NonModal
@ Popup
Definition qnamespace.h:211
@ Tool
Definition qnamespace.h:212
static void * context
#define SLOT(a)
Definition qobjectdefs.h:52
#define SIGNAL(a)
Definition qobjectdefs.h:53
static QOpenGLCompositor * compositor
static QRect toBottomLeftRect(const QRect &topLeftRect, int windowHeight)
static void clippedBlit(const QPlatformTextureList *textures, int idx, const QRect &sourceWindowRect, const QRect &targetWindowRect, QOpenGLTextureBlitter *blitter, QMatrix4x4 *rotationMatrix, QOpenGLTextureBlitter::Origin sourceOrigin)
GLuint const GLuint GLuint const GLuint * textures
GLbitfield GLuint64 timeout
[4]
GLenum target
GLsizei GLsizei GLchar * source
GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint GLdouble GLdouble w2
GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint GLdouble w1
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
unsigned int uint
Definition qtypes.h:34
if(qFloatDistance(a, b)<(1<< 7))
[0]
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
QObject::connect nullptr
aWidget window() -> setWindowTitle("New Window Title")
[2]
void set(bool blend)
qsizetype indexOf(const AT &t, qsizetype from=0) const noexcept
Definition qlist.h:962
bool contains(const AT &t) const noexcept
Definition qlist.h:45