1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQuick module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qsgopenglatlastexture_p.h"
41
42#include <QtCore/QVarLengthArray>
43#include <QtCore/QElapsedTimer>
44#include <QtCore/QtMath>
45
46#include <QtGui/QOpenGLContext>
47#include <QtGui/QOpenGLTexture>
48#include <QtGui/QOpenGLFunctions>
49#include <QtGui/QGuiApplication>
50#include <QtGui/QScreen>
51#include <QtGui/QSurface>
52#include <QtGui/QWindow>
53#include <QtGui/qpa/qplatformnativeinterface.h>
54
55#include <private/qqmlglobal_p.h>
56#include <private/qsgtexture_p.h>
57#include <private/qsgcompressedtexture_p.h>
58#include <private/qsgcompressedatlastexture_p.h>
59
60#include <private/qquickprofiler_p.h>
61
62#include <qtquick_tracepoints_p.h>
63
64QT_BEGIN_NAMESPACE
65
66#ifndef GL_BGRA
67#define GL_BGRA 0x80E1
68#endif
69
70int qt_sg_envInt(const char *name, int defaultValue);
71
72static QElapsedTimer qsg_renderer_timer;
73
74DEFINE_BOOL_CONFIG_OPTION(qsgEnableCompressedAtlas, QSG_ENABLE_COMPRESSED_ATLAS)
75
76namespace QSGOpenGLAtlasTexture
77{
78
79Manager::Manager(const QSize &surfacePixelSize)
80 : m_atlas(nullptr)
81{
82 QOpenGLContext *gl = QOpenGLContext::currentContext();
83 Q_ASSERT(gl);
84 int max;
85 gl->functions()->glGetIntegerv(GL_MAX_TEXTURE_SIZE, params: &max);
86
87 int w = qMin(a: max, b: qt_sg_envInt(name: "QSG_ATLAS_WIDTH", defaultValue: qMax(a: 512U, b: qNextPowerOfTwo(v: surfacePixelSize.width() - 1))));
88 int h = qMin(a: max, b: qt_sg_envInt(name: "QSG_ATLAS_HEIGHT", defaultValue: qMax(a: 512U, b: qNextPowerOfTwo(v: surfacePixelSize.height() - 1))));
89
90 if (gl->surface()->surfaceClass() == QSurface::Window) {
91 QWindow *window = static_cast<QWindow *>(gl->surface());
92 // Coverwindows, optimize for memory rather than speed
93 if ((window->type() & Qt::CoverWindow) == Qt::CoverWindow) {
94 w /= 2;
95 h /= 2;
96 }
97 }
98
99 m_atlas_size_limit = qt_sg_envInt(name: "QSG_ATLAS_SIZE_LIMIT", defaultValue: qMax(a: w, b: h) / 2);
100 m_atlas_size = QSize(w, h);
101
102 qCDebug(QSG_LOG_INFO, "opengl texture atlas dimensions: %dx%d", w, h);
103}
104
105Manager::~Manager()
106{
107 Q_ASSERT(m_atlas == nullptr);
108 Q_ASSERT(m_atlases.isEmpty());
109}
110
111void Manager::invalidate()
112{
113 if (m_atlas) {
114 m_atlas->invalidate();
115 m_atlas->deleteLater();
116 m_atlas = nullptr;
117 }
118
119 QHash<unsigned int, QSGCompressedAtlasTexture::Atlas*>::iterator i = m_atlases.begin();
120 while (i != m_atlases.end()) {
121 i.value()->invalidate();
122 i.value()->deleteLater();
123 ++i;
124 }
125 m_atlases.clear();
126}
127
128QSGTexture *Manager::create(const QImage &image, bool hasAlphaChannel)
129{
130 Texture *t = nullptr;
131 if (image.width() < m_atlas_size_limit && image.height() < m_atlas_size_limit) {
132 if (!m_atlas)
133 m_atlas = new Atlas(m_atlas_size);
134 // t may be null for atlas allocation failure
135 t = m_atlas->create(image);
136 if (t && !hasAlphaChannel && t->hasAlphaChannel())
137 t->setHasAlphaChannel(false);
138 }
139 return t;
140}
141
142QSGTexture *Manager::create(const QSGCompressedTextureFactory *factory)
143{
144 QSGTexture *t = nullptr;
145 if (!qsgEnableCompressedAtlas() || !factory->m_textureData.isValid())
146 return t;
147
148 // TODO: further abstract the atlas and remove this restriction
149 unsigned int format = factory->m_textureData.glInternalFormat();
150 switch (format) {
151 case QOpenGLTexture::RGB8_ETC1:
152 case QOpenGLTexture::RGB8_ETC2:
153 case QOpenGLTexture::RGBA8_ETC2_EAC:
154 case QOpenGLTexture::RGB8_PunchThrough_Alpha1_ETC2:
155 case QOpenGLTexture::RGB_DXT1:
156 case QOpenGLTexture::RGBA_DXT1:
157 case QOpenGLTexture::RGBA_DXT3:
158 case QOpenGLTexture::RGBA_DXT5:
159 break;
160 default:
161 return t;
162 }
163
164 QSize size = factory->m_textureData.size();
165 if (size.width() < m_atlas_size_limit && size.height() < m_atlas_size_limit) {
166 QHash<unsigned int, QSGCompressedAtlasTexture::Atlas*>::iterator i = m_atlases.find(key: format);
167 if (i == m_atlases.end()) {
168 // must be multiple of 4
169 QSize paddedSize(((m_atlas_size.width() + 3) / 4) * 4, ((m_atlas_size.height() + 3) / 4) * 4);
170 i = m_atlases.insert(key: format, value: new QSGCompressedAtlasTexture::Atlas(paddedSize, format));
171 }
172
173 // must be multiple of 4
174 QSize paddedSize(((size.width() + 3) / 4) * 4, ((size.height() + 3) / 4) * 4);
175 QByteArray data = factory->m_textureData.data();
176 t = i.value()->create(data, dataLength: factory->m_textureData.dataLength(), dataOffset: factory->m_textureData.dataOffset(), size, paddedSize);
177 }
178 return t;
179}
180
181AtlasBase::AtlasBase(const QSize &size)
182 : m_allocator(size)
183 , m_texture_id(0)
184 , m_size(size)
185 , m_allocated(false)
186{
187}
188
189AtlasBase::~AtlasBase()
190{
191 Q_ASSERT(!m_texture_id);
192}
193
194void AtlasBase::invalidate()
195{
196 if (m_texture_id && QOpenGLContext::currentContext())
197 QOpenGLContext::currentContext()->functions()->glDeleteTextures(n: 1, textures: &m_texture_id);
198 m_texture_id = 0;
199}
200
201int AtlasBase::textureId() const
202{
203 if (!m_texture_id) {
204 Q_ASSERT(QOpenGLContext::currentContext());
205 QOpenGLContext::currentContext()->functions()->glGenTextures(n: 1, textures: &const_cast<AtlasBase *>(this)->m_texture_id);
206 }
207
208 return m_texture_id;
209}
210
211void AtlasBase::bind(QSGTexture::Filtering filtering)
212{
213 QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
214 if (!m_allocated) {
215 m_allocated = true;
216
217 while (funcs->glGetError() != GL_NO_ERROR) ;
218
219 funcs->glGenTextures(n: 1, textures: &m_texture_id);
220 funcs->glBindTexture(GL_TEXTURE_2D, texture: m_texture_id);
221 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
222 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
223 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
224 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
225#if !defined(QT_OPENGL_ES_2)
226 if (!QOpenGLContext::currentContext()->isOpenGLES())
227 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, param: 0);
228#endif
229 generateTexture();
230
231 GLenum errorCode = funcs->glGetError();
232 if (errorCode == GL_OUT_OF_MEMORY) {
233 qDebug(msg: "QSGTextureAtlas: texture atlas allocation failed, out of memory");
234 funcs->glDeleteTextures(n: 1, textures: &m_texture_id);
235 m_texture_id = 0;
236 } else if (errorCode != GL_NO_ERROR) {
237 qDebug(msg: "QSGTextureAtlas: texture atlas allocation failed, code=%x", errorCode);
238 funcs->glDeleteTextures(n: 1, textures: &m_texture_id);
239 m_texture_id = 0;
240 }
241 } else {
242 funcs->glBindTexture(GL_TEXTURE_2D, texture: m_texture_id);
243 }
244
245 if (m_texture_id == 0)
246 return;
247
248 // Upload all pending images..
249 for (int i=0; i<m_pending_uploads.size(); ++i) {
250
251 bool profileFrames = QSG_LOG_TIME_TEXTURE().isDebugEnabled();
252 if (profileFrames)
253 qsg_renderer_timer.start();
254
255 Q_TRACE_SCOPE(QSG_texture_prepare);
256 Q_QUICK_SG_PROFILE_START(QQuickProfiler::SceneGraphTexturePrepare);
257
258 // Skip bind, convert, swizzle; they're irrelevant
259 Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphTexturePrepare,
260 QQuickProfiler::SceneGraphTexturePrepareStart, 3);
261 Q_TRACE(QSG_texture_upload_entry);
262
263 uploadPendingTexture(i);
264
265 Q_TRACE(QSG_texture_upload_exit);
266 Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphTexturePrepare,
267 QQuickProfiler::SceneGraphTexturePrepareUpload);
268
269 // Skip mipmap; unused
270 Q_QUICK_SG_PROFILE_SKIP(QQuickProfiler::SceneGraphTexturePrepare,
271 QQuickProfiler::SceneGraphTexturePrepareUpload, 1);
272 Q_QUICK_SG_PROFILE_REPORT(QQuickProfiler::SceneGraphTexturePrepare,
273 QQuickProfiler::SceneGraphTexturePrepareMipmap);
274 }
275
276 GLenum f = filtering == QSGTexture::Nearest ? GL_NEAREST : GL_LINEAR;
277 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, param: f);
278 funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, param: f);
279
280 m_pending_uploads.clear();
281}
282
283void AtlasBase::remove(TextureBase *t)
284{
285 QRect atlasRect = t->atlasSubRect();
286 m_allocator.deallocate(rect: atlasRect);
287 m_pending_uploads.removeOne(t);
288}
289
290Atlas::Atlas(const QSize &size)
291 : AtlasBase(size)
292 , m_atlas_transient_image_threshold(0)
293{
294
295 m_internalFormat = GL_RGBA;
296 m_externalFormat = GL_BGRA;
297
298#ifndef QT_OPENGL_ES
299 if (QOpenGLContext::currentContext()->isOpenGLES()) {
300#endif
301
302#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
303 QString *deviceName =
304 static_cast<QString *>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("AndroidDeviceName"));
305 static bool wrongfullyReportsBgra8888Support = deviceName != 0
306 && (deviceName->compare(QLatin1String("samsung SM-T211"), Qt::CaseInsensitive) == 0
307 || deviceName->compare(QLatin1String("samsung SM-T210"), Qt::CaseInsensitive) == 0
308 || deviceName->compare(QLatin1String("samsung SM-T215"), Qt::CaseInsensitive) == 0);
309#else
310 static bool wrongfullyReportsBgra8888Support = false;
311 // The Raspberry Pi (both 1 and 2) GPU refuses framebuffers with BGRA color attachments.
312 const GLubyte *renderer = QOpenGLContext::currentContext()->functions()->glGetString(GL_RENDERER);
313 if (renderer && strstr(haystack: (const char *) renderer, needle: "VideoCore IV"))
314 wrongfullyReportsBgra8888Support = true;
315#endif // ANDROID
316
317 if (qEnvironmentVariableIsSet(varName: "QSG_ATLAS_NO_BGRA_WORKAROUNDS"))
318 wrongfullyReportsBgra8888Support = false;
319
320 const char *ext = (const char *) QOpenGLContext::currentContext()->functions()->glGetString(GL_EXTENSIONS);
321 if (ext && !wrongfullyReportsBgra8888Support
322 && (strstr(haystack: ext, needle: "GL_EXT_bgra")
323 || strstr(haystack: ext, needle: "GL_EXT_texture_format_BGRA8888")
324 || strstr(haystack: ext, needle: "GL_IMG_texture_format_BGRA8888"))) {
325 m_internalFormat = m_externalFormat = GL_BGRA;
326#if defined(Q_OS_DARWIN) && !defined(Q_OS_OSX)
327 } else if (ext && strstr(ext, "GL_APPLE_texture_format_BGRA8888")) {
328 m_internalFormat = GL_RGBA;
329 m_externalFormat = GL_BGRA;
330#endif // IOS || TVOS
331 } else {
332 m_internalFormat = m_externalFormat = GL_RGBA;
333 }
334
335#ifndef QT_OPENGL_ES
336 }
337#endif
338
339 m_use_bgra_fallback = qEnvironmentVariableIsSet(varName: "QSG_ATLAS_USE_BGRA_FALLBACK");
340 m_debug_overlay = qEnvironmentVariableIsSet(varName: "QSG_ATLAS_OVERLAY");
341
342 // images smaller than this will retain their QImage.
343 // by default no images are retained (favoring memory)
344 // set to a very large value to retain all images (allowing quick removal from the atlas)
345 m_atlas_transient_image_threshold = qt_sg_envInt(name: "QSG_ATLAS_TRANSIENT_IMAGE_THRESHOLD", defaultValue: 0);
346}
347
348Atlas::~Atlas()
349{
350}
351
352Texture *Atlas::create(const QImage &image)
353{
354 // No need to lock, as manager already locked it.
355 QRect rect = m_allocator.allocate(size: QSize(image.width() + 2, image.height() + 2));
356 if (rect.width() > 0 && rect.height() > 0) {
357 Texture *t = new Texture(this, rect, image);
358 m_pending_uploads << t;
359 return t;
360 }
361 return nullptr;
362}
363
364void Atlas::upload(Texture *texture)
365{
366 const QImage &image = texture->image();
367 const QRect &r = texture->atlasSubRect();
368
369 QImage tmp(r.width(), r.height(), QImage::Format_ARGB32_Premultiplied);
370 {
371 QPainter p(&tmp);
372 p.setCompositionMode(QPainter::CompositionMode_Source);
373
374 int w = r.width();
375 int h = r.height();
376 int iw = image.width();
377 int ih = image.height();
378
379 p.drawImage(x: 1, y: 1, image);
380 p.drawImage(x: 1, y: 0, image, sx: 0, sy: 0, sw: iw, sh: 1);
381 p.drawImage(x: 1, y: h - 1, image, sx: 0, sy: ih - 1, sw: iw, sh: 1);
382 p.drawImage(x: 0, y: 1, image, sx: 0, sy: 0, sw: 1, sh: ih);
383 p.drawImage(x: w - 1, y: 1, image, sx: iw - 1, sy: 0, sw: 1, sh: ih);
384 p.drawImage(x: 0, y: 0, image, sx: 0, sy: 0, sw: 1, sh: 1);
385 p.drawImage(x: 0, y: h - 1, image, sx: 0, sy: ih - 1, sw: 1, sh: 1);
386 p.drawImage(x: w - 1, y: 0, image, sx: iw - 1, sy: 0, sw: 1, sh: 1);
387 p.drawImage(x: w - 1, y: h - 1, image, sx: iw - 1, sy: ih - 1, sw: 1, sh: 1);
388 if (m_debug_overlay) {
389 p.setCompositionMode(QPainter::CompositionMode_SourceAtop);
390 p.fillRect(x: 0, y: 0, w: iw, h: ih, b: QBrush(QColor::fromRgbF(r: 1, g: 0, b: 1, a: 0.5)));
391 }
392 }
393
394 if (m_externalFormat == GL_RGBA)
395 tmp = std::move(tmp).convertToFormat(f: QImage::Format_RGBA8888_Premultiplied);
396 QOpenGLContext::currentContext()->functions()->glTexSubImage2D(GL_TEXTURE_2D, level: 0,
397 xoffset: r.x(), yoffset: r.y(), width: r.width(), height: r.height(),
398 format: m_externalFormat, GL_UNSIGNED_BYTE, pixels: tmp.constBits());
399}
400
401void Atlas::uploadBgra(Texture *texture)
402{
403 QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
404 const QRect &r = texture->atlasSubRect();
405 QImage image = texture->image();
406
407 if (image.isNull())
408 return;
409
410 if (image.format() != QImage::Format_ARGB32_Premultiplied
411 && image.format() != QImage::Format_RGB32) {
412 image = image.convertToFormat(f: QImage::Format_ARGB32_Premultiplied);
413 }
414
415 if (m_debug_overlay) {
416 QPainter p(&image);
417 p.setCompositionMode(QPainter::CompositionMode_SourceAtop);
418 p.fillRect(x: 0, y: 0, w: image.width(), h: image.height(), b: QBrush(QColor::fromRgbF(r: 0, g: 1, b: 1, a: 0.5)));
419 }
420
421 QVarLengthArray<quint32, 512> tmpBits(qMax(a: image.width() + 2, b: image.height() + 2));
422 int iw = image.width();
423 int ih = image.height();
424 int bpl = image.bytesPerLine() / 4;
425 const quint32 *src = (const quint32 *) image.constBits();
426 quint32 *dst = tmpBits.data();
427
428 // top row, padding corners
429 dst[0] = src[0];
430 memcpy(dest: dst + 1, src: src, n: iw * sizeof(quint32));
431 dst[1 + iw] = src[iw-1];
432 funcs->glTexSubImage2D(GL_TEXTURE_2D, level: 0, xoffset: r.x(), yoffset: r.y(), width: iw + 2, height: 1, format: m_externalFormat, GL_UNSIGNED_BYTE, pixels: dst);
433
434 // bottom row, padded corners
435 const quint32 *lastRow = src + bpl * (ih - 1);
436 dst[0] = lastRow[0];
437 memcpy(dest: dst + 1, src: lastRow, n: iw * sizeof(quint32));
438 dst[1 + iw] = lastRow[iw-1];
439 funcs->glTexSubImage2D(GL_TEXTURE_2D, level: 0, xoffset: r.x(), yoffset: r.y() + ih + 1, width: iw + 2, height: 1, format: m_externalFormat, GL_UNSIGNED_BYTE, pixels: dst);
440
441 // left column
442 for (int i=0; i<ih; ++i)
443 dst[i] = src[i * bpl];
444 funcs->glTexSubImage2D(GL_TEXTURE_2D, level: 0, xoffset: r.x(), yoffset: r.y() + 1, width: 1, height: ih, format: m_externalFormat, GL_UNSIGNED_BYTE, pixels: dst);
445
446 // right column
447 for (int i=0; i<ih; ++i)
448 dst[i] = src[i * bpl + iw - 1];
449 funcs->glTexSubImage2D(GL_TEXTURE_2D, level: 0, xoffset: r.x() + iw + 1, yoffset: r.y() + 1, width: 1, height: ih, format: m_externalFormat, GL_UNSIGNED_BYTE, pixels: dst);
450
451 // Inner part of the image....
452 if (bpl != iw) {
453 int sy = r.y() + 1;
454 int ey = sy + r.height() - 2;
455 for (int y = sy; y < ey; ++y) {
456 funcs->glTexSubImage2D(GL_TEXTURE_2D, level: 0, xoffset: r.x() + 1, yoffset: y, width: r.width() - 2, height: 1, format: m_externalFormat, GL_UNSIGNED_BYTE, pixels: src);
457 src += bpl;
458 }
459 } else {
460 funcs->glTexSubImage2D(GL_TEXTURE_2D, level: 0, xoffset: r.x() + 1, yoffset: r.y() + 1, width: r.width() - 2, height: r.height() - 2, format: m_externalFormat, GL_UNSIGNED_BYTE, pixels: src);
461 }
462}
463
464void Atlas::generateTexture()
465{
466 QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
467 funcs->glTexImage2D(GL_TEXTURE_2D, level: 0, internalformat: m_internalFormat, width: m_size.width(), height: m_size.height(), border: 0, format: m_externalFormat, GL_UNSIGNED_BYTE, pixels: nullptr);
468
469#if 0
470 QImage pink(m_size.width(), m_size.height(), QImage::Format_ARGB32_Premultiplied);
471 pink.fill(0);
472 QPainter p(&pink);
473 QLinearGradient redGrad(0, 0, m_size.width(), 0);
474 redGrad.setColorAt(0, Qt::black);
475 redGrad.setColorAt(1, Qt::red);
476 p.fillRect(0, 0, m_size.width(), m_size.height(), redGrad);
477 p.setCompositionMode(QPainter::CompositionMode_Plus);
478 QLinearGradient blueGrad(0, 0, 0, m_size.height());
479 blueGrad.setColorAt(0, Qt::black);
480 blueGrad.setColorAt(1, Qt::blue);
481 p.fillRect(0, 0, m_size.width(), m_size.height(), blueGrad);
482 p.end();
483
484 funcs->glTexImage2D(GL_TEXTURE_2D, 0, m_internalFormat, m_size.width(), m_size.height(), 0, m_externalFormat, GL_UNSIGNED_BYTE, pink.constBits());
485#endif
486}
487
488void Atlas::uploadPendingTexture(int i)
489{
490 Texture *t = static_cast<Texture*>(m_pending_uploads.at(i));
491 if (m_externalFormat == GL_BGRA &&
492 !m_use_bgra_fallback) {
493 uploadBgra(texture: t);
494 } else {
495 upload(texture: t);
496 }
497 const QSize textureSize = t->textureSize();
498 if (textureSize.width() > m_atlas_transient_image_threshold ||
499 textureSize.height() > m_atlas_transient_image_threshold)
500 t->releaseImage();
501
502 qCDebug(QSG_LOG_TIME_TEXTURE, "atlastexture uploaded in: %lldms (%dx%d)",
503 qsg_renderer_timer.elapsed(),
504 t->textureSize().width(),
505 t->textureSize().height());
506}
507
508TextureBase::TextureBase(AtlasBase *atlas, const QRect &textureRect)
509 : m_allocated_rect(textureRect)
510 , m_atlas(atlas)
511{
512}
513
514TextureBase::~TextureBase()
515{
516 m_atlas->remove(t: this);
517}
518
519void TextureBase::bind()
520{
521 m_atlas->bind(filtering: filtering());
522}
523
524Texture::Texture(Atlas *atlas, const QRect &textureRect, const QImage &image)
525 : TextureBase(atlas, textureRect)
526 , m_image(image)
527 , m_nonatlas_texture(nullptr)
528 , m_has_alpha(image.hasAlphaChannel())
529{
530 qreal w = atlas->size().width();
531 qreal h = atlas->size().height();
532 QRect nopad = atlasSubRectWithoutPadding();
533 m_texture_coords_rect = QRectF(nopad.x() / w,
534 nopad.y() / h,
535 nopad.width() / w,
536 nopad.height() / h);
537}
538
539Texture::~Texture()
540{
541 if (m_nonatlas_texture)
542 delete m_nonatlas_texture;
543}
544
545QSGTexture *Texture::removedFromAtlas() const
546{
547 if (m_nonatlas_texture) {
548 m_nonatlas_texture->setMipmapFiltering(mipmapFiltering());
549 m_nonatlas_texture->setFiltering(filtering());
550 return m_nonatlas_texture;
551 }
552
553 if (!m_image.isNull()) {
554 m_nonatlas_texture = new QSGPlainTexture();
555 m_nonatlas_texture->setImage(m_image);
556 m_nonatlas_texture->setFiltering(filtering());
557
558 } else {
559 QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
560 // bind the atlas texture as an fbo and extract the texture..
561
562 // First extract the currently bound fbo so we can restore it later.
563 GLint currentFbo;
564 f->glGetIntegerv(GL_FRAMEBUFFER_BINDING, params: &currentFbo);
565
566 // Create an FBO and bind the atlas texture into it.
567 GLuint fbo;
568 f->glGenFramebuffers(n: 1, framebuffers: &fbo);
569 f->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: fbo);
570 f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture: m_atlas->textureId(), level: 0);
571
572 // Create the target texture, QSGPlainTexture below will deal with the texparams, so we don't
573 // need to worry about those here.
574 GLuint texture;
575 f->glGenTextures(n: 1, textures: &texture);
576 f->glBindTexture(GL_TEXTURE_2D, texture);
577 QRect r = atlasSubRectWithoutPadding();
578 // and copy atlas into our texture.
579 while (f->glGetError() != GL_NO_ERROR) ;
580 f->glCopyTexImage2D(GL_TEXTURE_2D, level: 0, internalformat: static_cast<Atlas*>(m_atlas)->internalFormat(), x: r.x(), y: r.y(), width: r.width(), height: r.height(), border: 0);
581 // BGRA may have been rejected by some GLES implementations
582 if (f->glGetError() != GL_NO_ERROR)
583 f->glCopyTexImage2D(GL_TEXTURE_2D, level: 0, GL_RGBA, x: r.x(), y: r.y(), width: r.width(), height: r.height(), border: 0);
584
585 m_nonatlas_texture = new QSGPlainTexture();
586 m_nonatlas_texture->setTextureId(texture);
587 m_nonatlas_texture->setOwnsTexture(true);
588 m_nonatlas_texture->setHasAlphaChannel(m_has_alpha);
589 m_nonatlas_texture->setTextureSize(r.size());
590
591 // cleanup: unbind our atlas from the fbo, rebind the old default and delete the fbo.
592 f->glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture: 0, level: 0);
593 f->glBindFramebuffer(GL_FRAMEBUFFER, framebuffer: (GLuint) currentFbo);
594 f->glDeleteFramebuffers(n: 1, framebuffers: &fbo);
595 }
596
597 m_nonatlas_texture->setMipmapFiltering(mipmapFiltering());
598 m_nonatlas_texture->setFiltering(filtering());
599 return m_nonatlas_texture;
600}
601
602}
603
604QT_END_NAMESPACE
605
606#include "moc_qsgopenglatlastexture_p.cpp"
607

source code of qtdeclarative/src/quick/scenegraph/util/qsgopenglatlastexture.cpp