Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qopenglcompositorbackingstore.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 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// Qt-Security score:significant reason:default
4
5#include <QtGui/QOpenGLContext>
6#include <QtGui/QWindow>
7#include <QtGui/QPainter>
8#include <qpa/qplatformbackingstore.h>
9#include <private/qwindow_p.h>
10#include <rhi/qrhi.h>
11
14
15#ifndef GL_UNPACK_ROW_LENGTH
16#define GL_UNPACK_ROW_LENGTH 0x0CF2
17#endif
18
20
21/*!
22 \class QOpenGLCompositorBackingStore
23 \brief A backing store implementation for OpenGL
24 \since 5.4
25 \internal
26 \ingroup qpa
27
28 This implementation uploads raster-rendered widget windows into
29 textures. It is meant to be used with QOpenGLCompositor that
30 composites the textures onto a single native window using OpenGL.
31 This means that multiple top-level widgets are supported without
32 creating actual native windows for each of them.
33
34 \note It is important to call notifyComposited() from the
35 corresponding platform window's endCompositing() callback
36 (inherited from QOpenGLCompositorWindow).
37
38 \note When implementing QOpenGLCompositorWindow::textures() for
39 windows of type RasterSurface, simply return
40 the list provided by this class' textures().
41*/
42
43QOpenGLCompositorBackingStore::QOpenGLCompositorBackingStore(QWindow *window)
44 : QPlatformBackingStore(window),
45 m_window(window),
46 m_bsTexture(0),
47 m_bsTextureWrapper(nullptr),
48 m_bsTextureContext(0),
49 m_textures(new QPlatformTextureList),
50 m_lockedWidgetTextures(0),
51 m_rhi(nullptr)
52{
53}
54
55QOpenGLCompositorBackingStore::~QOpenGLCompositorBackingStore()
56{
57 if (m_bsTexture && m_rhi) {
58 delete m_bsTextureWrapper;
59 // Contexts are sharing resources, won't matter which one is
60 // current here, use the rhi's shortcut.
61 m_rhi->makeThreadLocalNativeContextCurrent();
62 glDeleteTextures(1, &m_bsTexture);
63 }
64
65 delete m_textures; // this does not actually own any GL resources
66}
67
68QPaintDevice *QOpenGLCompositorBackingStore::paintDevice()
69{
70 return &m_image;
71}
72
73void QOpenGLCompositorBackingStore::updateTexture()
74{
75 if (!m_bsTexture) {
76 m_bsTextureContext = QOpenGLContext::currentContext();
77 Q_ASSERT(m_bsTextureContext);
78 glGenTextures(1, &m_bsTexture);
79 glBindTexture(GL_TEXTURE_2D, m_bsTexture);
80 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
81 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
82 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
83 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
84 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_image.width(), m_image.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
85 } else {
86 glBindTexture(GL_TEXTURE_2D, m_bsTexture);
87 }
88
89 if (!m_dirty.isNull()) {
90 QRegion fixed;
91 QRect imageRect = m_image.rect();
92
93 QOpenGLContext *ctx = QOpenGLContext::currentContext();
94 if (!ctx->isOpenGLES() || ctx->format().majorVersion() >= 3) {
95 for (const QRect &rect : m_dirty) {
96 QRect r = imageRect & rect;
97 glPixelStorei(GL_UNPACK_ROW_LENGTH, m_image.width());
98 glTexSubImage2D(GL_TEXTURE_2D, 0, r.x(), r.y(), r.width(), r.height(), GL_RGBA, GL_UNSIGNED_BYTE,
99 m_image.constScanLine(r.y()) + r.x() * 4);
100 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
101 }
102 } else {
103 for (const QRect &rect : m_dirty) {
104 // intersect with image rect to be sure
105 QRect r = imageRect & rect;
106
107 // if the rect is wide enough it's cheaper to just
108 // extend it instead of doing an image copy
109 if (r.width() >= imageRect.width() / 2) {
110 r.setX(0);
111 r.setWidth(imageRect.width());
112 }
113
114 fixed |= r;
115 }
116 for (const QRect &rect : fixed) {
117 // if the sub-rect is full-width we can pass the image data directly to
118 // OpenGL instead of copying, since there's no gap between scanlines
119 if (rect.width() == imageRect.width()) {
120 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, rect.y(), rect.width(), rect.height(), GL_RGBA, GL_UNSIGNED_BYTE,
121 m_image.constScanLine(rect.y()));
122 } else {
123 glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x(), rect.y(), rect.width(), rect.height(), GL_RGBA, GL_UNSIGNED_BYTE,
124 m_image.copy(rect).constBits());
125 }
126 }
127 }
128
129 m_dirty = QRegion();
130 }
131
132 if (!m_bsTextureWrapper) {
133 m_bsTextureWrapper = m_rhi->newTexture(QRhiTexture::RGBA8, m_image.size());
134 m_bsTextureWrapper->createFrom({m_bsTexture, 0});
135 }
136}
137
138void QOpenGLCompositorBackingStore::flush(QWindow *flushedWindow, const QRegion &region, const QPoint &offset)
139{
140 // Called for ordinary raster windows.
141
142 Q_UNUSED(region);
143 Q_UNUSED(offset);
144
145 QOpenGLCompositorWindow *handle = dynamic_cast<QOpenGLCompositorWindow *>(flushedWindow->handle());
146 if (handle && !handle->backingStore())
147 handle->setBackingStore(this);
148
149 if (!rhi(flushedWindow)) {
150 QPlatformBackingStoreRhiConfig rhiConfig;
151 rhiConfig.setApi(QPlatformBackingStoreRhiConfig::OpenGL);
152 rhiConfig.setEnabled(true);
153 createRhi(flushedWindow, rhiConfig);
154 }
155
156 static QPlatformTextureList emptyTextureList;
157 bool translucentBackground = m_image.hasAlphaChannel();
158 rhiFlush(flushedWindow, flushedWindow->devicePixelRatio(),
159 region, offset, &emptyTextureList, translucentBackground);
160}
161
162QPlatformBackingStore::FlushResult QOpenGLCompositorBackingStore::rhiFlush(QWindow *window,
163 qreal sourceDevicePixelRatio,
164 const QRegion &region,
165 const QPoint &offset,
166 QPlatformTextureList *textures,
167 bool translucentBackground,
168 qreal sourceTransformFactor)
169{
170 // QOpenGLWidget/QQuickWidget content provided as textures. The raster content goes on top.
171
172 Q_UNUSED(region);
173 Q_UNUSED(offset);
174 Q_UNUSED(translucentBackground);
175 Q_UNUSED(sourceDevicePixelRatio);
176 Q_UNUSED(sourceTransformFactor);
177
178 m_rhi = rhi(window);
179 Q_ASSERT(m_rhi);
180
181 QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
182 QOpenGLContext *dstCtx = compositor->context();
183 if (!dstCtx)
184 return FlushFailed;
185
186 QWindow *dstWin = compositor->targetWindow();
187 if (!dstWin)
188 return FlushFailed;
189
190 if (!dstCtx->makeCurrent(dstWin))
191 return FlushFailed;
192
193 QWindowPrivate::get(window)->lastComposeTime.start();
194
195 m_textures->clear();
196 for (int i = 0; i < textures->count(); ++i) {
197 m_textures->appendTexture(textures->source(i), textures->texture(i), textures->geometry(i),
198 textures->clipRect(i), textures->flags(i));
199 }
200
201 updateTexture();
202 m_textures->appendTexture(nullptr, m_bsTextureWrapper, window->geometry());
203
204 textures->lock(true);
205 m_lockedWidgetTextures = textures;
206
207 compositor->update();
208
209 return FlushSuccess;
210}
211
212void QOpenGLCompositorBackingStore::notifyComposited()
213{
214 if (m_lockedWidgetTextures) {
215 QPlatformTextureList *textureList = m_lockedWidgetTextures;
216 m_lockedWidgetTextures = 0; // may reenter so null before unlocking
217 textureList->lock(false);
218 }
219}
220
221void QOpenGLCompositorBackingStore::beginPaint(const QRegion &region)
222{
223 m_dirty |= region;
224
225 if (m_image.hasAlphaChannel()) {
226 QPainter p(&m_image);
227 p.setCompositionMode(QPainter::CompositionMode_Source);
228 for (const QRect &r : region)
229 p.fillRect(r, Qt::transparent);
230 }
231}
232
233void QOpenGLCompositorBackingStore::resize(const QSize &size, const QRegion &staticContents)
234{
235 Q_UNUSED(staticContents);
236
237 QOpenGLCompositor *compositor = QOpenGLCompositor::instance();
238 QOpenGLContext *dstCtx = compositor->context();
239 if (!dstCtx)
240 return;
241 QWindow *dstWin = compositor->targetWindow();
242 if (!dstWin)
243 return;
244
245 m_image = QImage(size, QImage::Format_RGBA8888);
246
247 m_window->create();
248
249 dstCtx->makeCurrent(dstWin);
250 if (m_bsTexture) {
251 delete m_bsTextureWrapper;
252 m_bsTextureWrapper = nullptr;
253 glDeleteTextures(1, &m_bsTexture);
254 m_bsTexture = 0;
255 m_bsTextureContext = nullptr;
256 }
257}
258
259QImage QOpenGLCompositorBackingStore::toImage() const
260{
261 return m_image;
262}
263
264QT_END_NAMESPACE
Combined button and popup list for selecting options.
#define GL_UNPACK_ROW_LENGTH