Qt 6.x
The Qt SDK
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
68{
69 m_context = context;
70}
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 m_context->makeCurrent(m_targetWindow);
90 renderAll(fbo.data());
91 return fbo->toImage();
92}
93
94void QOpenGLCompositor::handleRenderAllRequest()
95{
96 Q_ASSERT(m_context && m_targetWindow);
97 m_context->makeCurrent(m_targetWindow);
98 renderAll(0);
99}
100
101void QOpenGLCompositor::renderAll(QOpenGLFramebufferObject *fbo)
102{
103 if (fbo)
104 fbo->bind();
105
106 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
107 glViewport(0, 0, m_nativeTargetGeometry.width(), m_nativeTargetGeometry.height());
108
109 if (!m_blitter.isCreated())
110 m_blitter.create();
111
112 m_blitter.bind();
113
114 for (int i = 0; i < m_windows.size(); ++i)
115 m_windows.at(i)->beginCompositing();
116
117 for (int i = 0; i < m_windows.size(); ++i)
118 render(m_windows.at(i));
119
120 m_blitter.release();
121 if (!fbo)
122 m_context->swapBuffers(m_targetWindow);
123 else
124 fbo->release();
125
126 for (int i = 0; i < m_windows.size(); ++i)
127 m_windows.at(i)->endCompositing();
128}
129
131{
133 glDisable(GL_BLEND);
134 }
135 void set(bool blend) {
136 if (blend != m_blend) {
137 if (blend) {
138 glEnable(GL_BLEND);
139 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
140 } else {
141 glDisable(GL_BLEND);
142 }
143 m_blend = blend;
144 }
145 }
147 if (m_blend)
148 glDisable(GL_BLEND);
149 }
151};
152
153static inline QRect toBottomLeftRect(const QRect &topLeftRect, int windowHeight)
154{
155 return QRect(topLeftRect.x(), windowHeight - topLeftRect.bottomRight().y() - 1,
156 topLeftRect.width(), topLeftRect.height());
157}
158
159static void clippedBlit(const QPlatformTextureList *textures, int idx, const QRect &sourceWindowRect,
160 const QRect &targetWindowRect,
161 QOpenGLTextureBlitter *blitter, QMatrix4x4 *rotationMatrix)
162{
163 const QRect clipRect = textures->clipRect(idx);
164 if (clipRect.isEmpty())
165 return;
166
167 const QRect rectInWindow = textures->geometry(idx).translated(sourceWindowRect.topLeft());
168 const QRect clippedRectInWindow = rectInWindow & clipRect.translated(rectInWindow.topLeft());
169 const QRect srcRect = toBottomLeftRect(clipRect, rectInWindow.height());
170
171 QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(clippedRectInWindow, targetWindowRect);
172 if (rotationMatrix)
173 target = *rotationMatrix * target;
174
175 const QMatrix3x3 source = QOpenGLTextureBlitter::sourceTransform(srcRect, rectInWindow.size(),
177
178 const uint textureId = textures->texture(idx)->nativeTexture().object;
179 blitter->blit(textureId, target, source);
180}
181
182void QOpenGLCompositor::render(QOpenGLCompositorWindow *window)
183{
184 const QPlatformTextureList *textures = window->textures();
185 if (!textures)
186 return;
187
188 const QRect targetWindowRect(QPoint(0, 0), m_targetWindow->geometry().size());
189 float currentOpacity = 1.0f;
190 BlendStateBinder blend;
191 const QRect sourceWindowRect = window->sourceWindow()->geometry();
192 for (int i = 0; i < textures->count(); ++i) {
193 const uint textureId = textures->texture(i)->nativeTexture().object;
194 const float opacity = window->sourceWindow()->opacity();
195 if (opacity != currentOpacity) {
196 currentOpacity = opacity;
197 m_blitter.setOpacity(currentOpacity);
198 }
199
200 if (textures->count() > 1 && i == textures->count() - 1) {
201 // Backingstore for a widget with QOpenGLWidget subwidgets
202 blend.set(true);
203 QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect);
204 if (m_rotation)
205 target = m_rotationMatrix * target;
206 m_blitter.blit(textureId, target, QOpenGLTextureBlitter::OriginTopLeft);
207 } else if (textures->count() == 1) {
208 // A regular QWidget window
209 const bool translucent = window->sourceWindow()->requestedFormat().alphaBufferSize() > 0;
210 blend.set(translucent);
211 QMatrix4x4 target = QOpenGLTextureBlitter::targetTransform(textures->geometry(i), targetWindowRect);
212 if (m_rotation)
213 target = m_rotationMatrix * target;
214 m_blitter.blit(textureId, target, QOpenGLTextureBlitter::OriginTopLeft);
215 } else if (!textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) {
216 // Texture from an FBO belonging to a QOpenGLWidget or QQuickWidget
217 blend.set(false);
218 clippedBlit(textures, i, sourceWindowRect, targetWindowRect, &m_blitter, m_rotation ? &m_rotationMatrix : nullptr);
219 }
220 }
221
222 for (int i = 0; i < textures->count(); ++i) {
223 if (textures->flags(i).testFlag(QPlatformTextureList::StacksOnTop)) {
224 blend.set(true);
225 clippedBlit(textures, i, sourceWindowRect, targetWindowRect, &m_blitter, m_rotation ? &m_rotationMatrix : nullptr);
226 }
227 }
228
229 m_blitter.setOpacity(1.0f);
230}
231
233{
234 if (!compositor)
236 return compositor;
237}
238
240{
241 delete compositor;
242 compositor = 0;
243}
244
246{
247 if (!m_windows.contains(window)) {
248 m_windows.append(window);
249 ensureCorrectZOrder();
250 if (window == m_windows.constLast())
252 }
253}
254
256{
257 m_windows.removeOne(window);
258 if (!m_windows.isEmpty())
259 emit topWindowChanged(m_windows.last());
260}
261
263{
264 if (!m_windows.isEmpty() && window == m_windows.constLast()) {
265 // Already on top
266 return;
267 }
268
269 m_windows.removeOne(window);
270 m_windows.append(window);
271 ensureCorrectZOrder();
272
273 if (window == m_windows.constLast())
275}
276
278{
279 int idx = m_windows.indexOf(window);
280 if (idx != -1 && idx != newIdx) {
281 m_windows.move(idx, newIdx);
282 ensureCorrectZOrder();
283 if (window == m_windows.constLast())
284 emit topWindowChanged(m_windows.last());
285 }
286}
287
288void QOpenGLCompositor::ensureCorrectZOrder()
289{
290 const auto originalOrder = m_windows;
291
292 std::sort(m_windows.begin(), m_windows.end(),
293 [this, &originalOrder](QOpenGLCompositorWindow *cw1, QOpenGLCompositorWindow *cw2) {
294 QWindow *w1 = cw1->sourceWindow();
295 QWindow *w2 = cw2->sourceWindow();
296
297 // Case #1: The main window needs to have less z-order. It can never be in
298 // front of our tool windows, popups etc, because it's fullscreen!
299 if (w1 == m_targetWindow || w2 == m_targetWindow)
300 return w1 == m_targetWindow;
301
302 // Case #2:
303 if (w2->isAncestorOf(w1)) {
304 // w1 is transient child of w2. W1 goes in front then.
305 return false;
306 }
307
308 if (w1->isAncestorOf(w2)) {
309 // Or the other way around
310 return true;
311 }
312
313 // Case #3: Modality gets higher Z
314 if (w1->modality() != Qt::NonModal && w2->modality() == Qt::NonModal)
315 return false;
316
317 if (w2->modality() != Qt::NonModal && w1->modality() == Qt::NonModal)
318 return true;
319
320 const bool isTool1 = (w1->flags() & Qt::Tool) == Qt::Tool;
321 const bool isTool2 = (w2->flags() & Qt::Tool) == Qt::Tool;
322 const bool isPurePopup1 = !isTool1 && (w1->flags() & Qt::Popup) == Qt::Popup;
323 const bool isPurePopup2 = !isTool2 && (w2->flags() & Qt::Popup) == Qt::Popup;
324
325 // Case #4: By pure-popup we mean menus and tooltips. Qt::Tool implies Qt::Popup
326 // and we don't want to catch QDockWidget and other tool windows just yet
327 if (isPurePopup1 != isPurePopup2)
328 return !isPurePopup1;
329
330 // Case #5: One of the window is a Tool, that goes to front, as done in other QPAs
331 if (isTool1 != isTool2)
332 return !isTool1;
333
334 // Case #6: Just preserve original sorting:
335 return originalOrder.indexOf(cw1) < originalOrder.indexOf(cw2);
336 });
337}
338
340
341#include "moc_qopenglcompositor_p.cpp"
\inmodule QtGui
Definition qimage.h:37
qsizetype size() const noexcept
Definition qlist.h:386
bool isEmpty() const noexcept
Definition qlist.h:390
T & last()
Definition qlist.h:631
const T & constLast() const noexcept
Definition qlist.h:633
bool removeOne(const AT &t)
Definition qlist.h:581
iterator end()
Definition qlist.h:609
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
void move(qsizetype from, qsizetype to)
Definition qlist.h:593
iterator begin()
Definition qlist.h:608
void append(parameter_type t)
Definition qlist.h:441
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.
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)
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.
The QOpenGLFramebufferObject class encapsulates an OpenGL framebuffer object.
bool release()
Switches rendering back to the default, windowing system provided framebuffer.
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.
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:23
constexpr int y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:132
\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:166
constexpr int height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:238
constexpr QPoint topLeft() const noexcept
Returns the position of the rectangle's top-left corner.
Definition qrect.h:220
constexpr int x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:184
constexpr QSize size() const noexcept
Returns the size of the rectangle.
Definition qrect.h:241
constexpr QPoint bottomRight() const noexcept
Returns the position of the rectangle's bottom-right corner.
Definition qrect.h:223
constexpr int width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:235
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:260
\inmodule QtCore
T * data() const noexcept
Returns the value of the pointer referenced by this object.
void start(int msec)
Starts or restarts the timer with a timeout interval of msec milliseconds.
Definition qtimer.cpp:208
bool isActive() const
Returns true if the timer is running (pending); otherwise returns false.
Definition qtimer.cpp:156
\inmodule QtGui
Definition qwindow.h:63
Combined button and popup list for selecting options.
@ NonModal
@ Popup
Definition qnamespace.h:210
@ Tool
Definition qnamespace.h:211
static void * context
#define SLOT(a)
Definition qobjectdefs.h:51
#define SIGNAL(a)
Definition qobjectdefs.h:52
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)
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:29
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:955
bool contains(const AT &t) const noexcept
Definition qlist.h:44