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