Avoid direct GL calls in Quick
[qt:qtdeclarative.git] / src / quick / scenegraph / util / qsgtexture.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
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 Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qsgtexture_p.h"
43 #include <qopenglfunctions.h>
44 #include <QtQuick/private/qsgcontext_p.h>
45 #include <qthread.h>
46 #include <private/qquickprofiler_p.h>
47 #include <private/qqmlglobal_p.h>
48 #include <QtGui/qguiapplication.h>
49 #include <QtGui/qpa/qplatformnativeinterface.h>
50
51 #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) && !defined(__UCLIBC__)
52 #define CAN_BACKTRACE_EXECINFO
53 #endif
54
55 #if defined(Q_OS_MAC)
56 #define CAN_BACKTRACE_EXECINFO
57 #endif
58
59 #if defined(QT_NO_DEBUG)
60 #undef CAN_BACKTRACE_EXECINFO
61 #endif
62
63 #if defined(CAN_BACKTRACE_EXECINFO)
64 #include <execinfo.h>
65 #include <QHash>
66 #endif
67
68 #ifndef QT_NO_DEBUG
69 static bool qsg_leak_check = !qgetenv("QML_LEAK_CHECK").isEmpty();
70 #endif
71
72 #ifndef QSG_NO_RENDER_TIMING
73 static bool qsg_render_timing = !qgetenv("QSG_RENDER_TIMING").isEmpty();
74 static QElapsedTimer qsg_renderer_timer;
75 #endif
76
77 #ifndef GL_BGRA
78 #define GL_BGRA 0x80E1
79 #endif
80
81
82 QT_BEGIN_NAMESPACE
83
84 #ifndef QT_NO_DEBUG
85 inline static bool isPowerOfTwo(int x)
86 {
87     // Assumption: x >= 1
88     return x == (x & -x);
89 }
90 #endif
91
92 QSGTexturePrivate::QSGTexturePrivate()
93     : wrapChanged(false)
94     , filteringChanged(false)
95     , horizontalWrap(QSGTexture::ClampToEdge)
96     , verticalWrap(QSGTexture::ClampToEdge)
97     , mipmapMode(QSGTexture::None)
98     , filterMode(QSGTexture::Nearest)
99 {
100 }
101
102 #ifndef QT_NO_DEBUG
103
104 static int qt_debug_texture_count = 0;
105
106 #if (defined(Q_OS_LINUX) || defined (Q_OS_MAC)) && !defined(Q_OS_ANDROID)
107 DEFINE_BOOL_CONFIG_OPTION(qmlDebugLeakBacktrace, QML_DEBUG_LEAK_BACKTRACE)
108
109 #define BACKTRACE_SIZE 20
110 class SGTextureTraceItem
111 {
112 public:
113     void *backTrace[BACKTRACE_SIZE];
114     size_t backTraceSize;
115 };
116
117 static QHash<QSGTexture*, SGTextureTraceItem*> qt_debug_allocated_textures;
118 #endif
119
120 inline static void qt_debug_print_texture_count()
121 {
122     qDebug("Number of leaked textures: %i", qt_debug_texture_count);
123     qt_debug_texture_count = -1;
124
125 #if defined(CAN_BACKTRACE_EXECINFO)
126     if (qmlDebugLeakBacktrace()) {
127         while (!qt_debug_allocated_textures.isEmpty()) {
128             QHash<QSGTexture*, SGTextureTraceItem*>::Iterator it = qt_debug_allocated_textures.begin();
129             QSGTexture* texture = it.key();
130             SGTextureTraceItem* item = it.value();
131
132             qt_debug_allocated_textures.erase(it);
133
134             qDebug() << "------";
135             qDebug() << "Leaked" << texture << "backtrace:";
136
137             char** symbols = backtrace_symbols(item->backTrace, item->backTraceSize);
138
139             if (symbols) {
140                 for (int i=0; i<(int) item->backTraceSize; i++)
141                     qDebug("Backtrace <%02d>: %s", i, symbols[i]);
142                 free(symbols);
143             }
144
145             qDebug() << "------";
146
147             delete item;
148         }
149     }
150 #endif
151 }
152
153 inline static void qt_debug_add_texture(QSGTexture* texture)
154 {
155 #if defined(CAN_BACKTRACE_EXECINFO)
156     if (qmlDebugLeakBacktrace()) {
157         SGTextureTraceItem* item = new SGTextureTraceItem;
158         item->backTraceSize = backtrace(item->backTrace, BACKTRACE_SIZE);
159         qt_debug_allocated_textures.insert(texture, item);
160     }
161 #else
162     Q_UNUSED(texture);
163 #endif // Q_OS_LINUX
164
165     ++qt_debug_texture_count;
166
167     static bool atexit_registered = false;
168     if (!atexit_registered) {
169         atexit(qt_debug_print_texture_count);
170         atexit_registered = true;
171     }
172 }
173
174 static void qt_debug_remove_texture(QSGTexture* texture)
175 {
176 #if defined(CAN_BACKTRACE_EXECINFO)
177     if (qmlDebugLeakBacktrace()) {
178         SGTextureTraceItem* item = qt_debug_allocated_textures.value(texture, 0);
179         if (item) {
180             qt_debug_allocated_textures.remove(texture);
181             delete item;
182         }
183     }
184 #else
185     Q_UNUSED(texture)
186 #endif
187
188     --qt_debug_texture_count;
189
190     if (qt_debug_texture_count < 0)
191         qDebug("Texture destroyed after qt_debug_print_texture_count() was called.");
192 }
193
194 #endif // QT_NO_DEBUG
195
196 /*!
197     \class QSGTexture
198
199     \inmodule QtQuick
200
201     \brief The QSGTexture class is a baseclass for textures used in
202     the scene graph.
203
204
205     Users can freely implement their own texture classes to support
206     arbitrary input textures, such as YUV video frames or 8 bit alpha
207     masks. The scene graph backend provides a default implementation
208     of normal color textures. As the implementation of these may be
209     hardware specific, they are are constructed via the factory
210     function QQuickWindow::createTextureFromImage().
211
212     The texture is a wrapper around an OpenGL texture, which texture
213     id is given by textureId() and which size in pixels is given by
214     textureSize(). hasAlphaChannel() reports if the texture contains
215     opacity values and hasMipmaps() reports if the texture contains
216     mipmap levels.
217
218     To use a texture, call the bind() function. The texture parameters
219     specifying how the texture is bound, can be specified with
220     setMipmapFiltering(), setFiltering(), setHorizontalWrapMode() and
221     setVerticalWrapMode(). The texture will internally try to store
222     these values to minimize the OpenGL state changes when the texture
223     is bound.
224
225     \section1 Texture Atlasses
226
227     Some scene graph backends use texture atlasses, grouping multiple
228     small textures into one large texture. If this is the case, the
229     function isAtlasTexture() will return true. Atlasses are used to
230     aid the rendering algorithm to do better sorting which increases
231     performance. The location of the texture inside the atlas is
232     given with the normalizedTextureSubRect() function.
233
234     If the texture is used in such a way that atlas is not preferable,
235     the function removedFromAtlas() can be used to extract a
236     non-atlassed copy.
237
238     \note All classes with QSG prefix should be used solely on the scene graph's
239     rendering thread. See \l {Scene Graph and Rendering} for more information.
240
241     \sa {Scene Graph - Rendering FBOs}, {Scene Graph - Rendering FBOs in a thread}
242  */
243
244 /*!
245     \enum QSGTexture::WrapMode
246
247     Specifies how the texture should treat texture coordinates.
248
249     \value Repeat Only the factional part of the texture coordiante is
250     used, causing values above 1 and below 0 to repeat.
251
252     \value ClampToEdge Values above 1 are clamped to 1 and values
253     below 0 are clamped to 0.
254  */
255
256 /*!
257     \enum QSGTexture::Filtering
258
259     Specifies how sampling of texels should filter when texture
260     coordinates are not pixel aligned.
261
262     \value None No filtering should occur. This value is only used
263     together with setMipmapFiltering().
264
265     \value Nearest Sampling returns the nearest texel.
266
267     \value Linear Sampling returns a linear interpolation of the
268     neighboring texels.
269 */
270
271 /*!
272     \fn QSGTexture::QSGTexture(QSGTexturePrivate &dd)
273     \internal
274  */
275
276 /*!
277     Constructs the QSGTexture base class.
278  */
279 QSGTexture::QSGTexture()
280     : QObject(*(new QSGTexturePrivate))
281 {
282 #ifndef QT_NO_DEBUG
283     if (qsg_leak_check)
284         qt_debug_add_texture(this);
285 #endif
286 }
287
288 /*!
289     Destroys the QSGTexture.
290  */
291 QSGTexture::~QSGTexture()
292 {
293 #ifndef QT_NO_DEBUG
294     if (qsg_leak_check)
295         qt_debug_remove_texture(this);
296 #endif
297 }
298
299
300 /*!
301     \fn void QSGTexture::bind()
302
303     Call this function to bind this texture to the current texture
304     target.
305
306     Binding a texture may also include uploading the texture data from
307     a previously set QImage.
308
309     \warning This function can only be called from the rendering thread.
310  */
311
312 /*!
313     \fn QRectF QSGTexture::convertToNormalizedSourceRect(const QRectF &rect) const
314
315     Returns \a rect converted to normalized coordinates.
316
317     \sa normalizedTextureSubRect()
318  */
319
320 /*!
321     This function returns a copy of the current texture which is removed
322     from its atlas.
323
324     The current texture remains unchanged, so texture coordinates do not
325     need to be updated.
326
327     Removing a texture from an atlas is primarily useful when passing
328     it to a shader that operates on the texture coordinates 0-1 instead
329     of the texture subrect inside the atlas.
330
331     If the texture is not part of a texture atlas, this function returns 0.
332
333     Implementations of this function are recommended to return the same instance
334     for multiple calls to limit memory usage.
335
336     \warning This function can only be called from the rendering thread.
337  */
338
339 QSGTexture *QSGTexture::removedFromAtlas() const
340 {
341     Q_ASSERT_X(!isAtlasTexture(), "QSGTexture::removedFromAtlas()", "Called on a non-atlas texture");
342     return 0;
343 }
344
345 /*!
346     Returns weither this texture is part of an atlas or not.
347
348     The default implementation returns false.
349  */
350 bool QSGTexture::isAtlasTexture() const
351 {
352     return false;
353 }
354
355 /*!
356     \fn int QSGTexture::textureId() const
357
358     Returns the OpenGL texture id for this texture.
359
360     The default value is 0, indicating that it is an invalid texture id.
361
362     The function should at all times return the correct texture id.
363
364     \warning This function can only be called from the rendering thread.
365  */
366
367 /*!
368     \fn QSize QSGTexture::textureSize() const
369
370     Returns the size of the texture.
371  */
372
373 /*!
374     Returns the rectangle inside textureSize() that this texture
375     represents in normalized coordinates.
376
377     The default implementation returns a rect at position (0, 0) with
378     width and height of 1.
379  */
380 QRectF QSGTexture::normalizedTextureSubRect() const
381 {
382     return QRectF(0, 0, 1, 1);
383 }
384
385 /*!
386     \fn bool QSGTexture::hasAlphaChannel() const
387
388     Returns true if the texture data contains an alpha channel.
389  */
390
391 /*!
392     \fn bool QSGTexture::hasMipmaps() const
393
394     Returns true if the texture data contains mipmap levels.
395  */
396
397
398 /*!
399     Sets the mipmap sampling mode to be used for the upcoming bind() call to \a filter.
400
401     Setting the mipmap filtering has no effect it the texture does not have mipmaps.
402
403     \sa hasMipmaps()
404  */
405 void QSGTexture::setMipmapFiltering(Filtering filter)
406 {
407     Q_D(QSGTexture);
408     if (d->mipmapMode != (uint) filter) {
409         d->mipmapMode = filter;
410         d->filteringChanged = true;
411     }
412 }
413
414 /*!
415     Returns whether mipmapping should be used when sampling from this texture.
416  */
417 QSGTexture::Filtering QSGTexture::mipmapFiltering() const
418 {
419     return (QSGTexture::Filtering) d_func()->mipmapMode;
420 }
421
422
423 /*!
424     Sets the sampling mode to be used for the upcoming bind() call to \a filter.
425  */
426 void QSGTexture::setFiltering(QSGTexture::Filtering filter)
427 {
428     Q_D(QSGTexture);
429     if (d->filterMode != (uint) filter) {
430         d->filterMode = filter;
431         d->filteringChanged = true;
432     }
433 }
434
435 /*!
436     Returns the sampling mode to be used for this texture.
437  */
438 QSGTexture::Filtering QSGTexture::filtering() const
439 {
440     return (QSGTexture::Filtering) d_func()->filterMode;
441 }
442
443
444
445 /*!
446     Sets the horizontal wrap mode to be used for the upcoming bind() call to \a hwrap
447  */
448
449 void QSGTexture::setHorizontalWrapMode(WrapMode hwrap)
450 {
451     Q_D(QSGTexture);
452     if ((uint) hwrap != d->horizontalWrap) {
453         d->horizontalWrap = hwrap;
454         d->wrapChanged = true;
455     }
456 }
457
458 /*!
459     Returns the horizontal wrap mode to be used for this texture.
460  */
461 QSGTexture::WrapMode QSGTexture::horizontalWrapMode() const
462 {
463     return (QSGTexture::WrapMode) d_func()->horizontalWrap;
464 }
465
466
467
468 /*!
469     Sets the vertical wrap mode to be used for the upcoming bind() call to \a vwrap
470  */
471 void QSGTexture::setVerticalWrapMode(WrapMode vwrap)
472 {
473     Q_D(QSGTexture);
474     if ((uint) vwrap != d->verticalWrap) {
475         d->verticalWrap = vwrap;
476         d->wrapChanged = true;
477     }
478 }
479
480 /*!
481     Returns the vertical wrap mode to be used for this texture.
482  */
483 QSGTexture::WrapMode QSGTexture::verticalWrapMode() const
484 {
485     return (QSGTexture::WrapMode) d_func()->verticalWrap;
486 }
487
488
489 /*!
490     Update the texture state to match the filtering, mipmap and wrap options
491     currently set.
492
493     If \a force is true, all properties will be updated regardless of weither
494     they have changed or not.
495  */
496 void QSGTexture::updateBindOptions(bool force)
497 {
498     Q_D(QSGTexture);
499     QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions();
500     force |= isAtlasTexture();
501
502     if (force || d->filteringChanged) {
503         bool linear = d->filterMode == Linear;
504         GLint minFilter = linear ? GL_LINEAR : GL_NEAREST;
505         GLint magFilter = linear ? GL_LINEAR : GL_NEAREST;
506
507         if (hasMipmaps()) {
508             if (d->mipmapMode == Nearest)
509                 minFilter = linear ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_NEAREST;
510             else if (d->mipmapMode == Linear)
511                 minFilter = linear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_LINEAR;
512         }
513         funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
514         funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
515         d->filteringChanged = false;
516     }
517
518     if (force || d->wrapChanged) {
519 #ifndef QT_NO_DEBUG
520         if (d->horizontalWrap == Repeat || d->verticalWrap == Repeat) {
521             bool npotSupported = QOpenGLFunctions(QOpenGLContext::currentContext()).hasOpenGLFeature(QOpenGLFunctions::NPOTTextures);
522             QSize size = textureSize();
523             bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
524             if (!npotSupported && isNpot)
525                 qWarning("Scene Graph: This system does not support the REPEAT wrap mode for non-power-of-two textures.");
526         }
527 #endif
528         funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, d->horizontalWrap == Repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
529         funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, d->verticalWrap == Repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
530         d->wrapChanged = false;
531     }
532 }
533
534 QSGPlainTexture::QSGPlainTexture()
535     : QSGTexture()
536     , m_texture_id(0)
537     , m_has_alpha(false)
538     , m_dirty_texture(false)
539     , m_dirty_bind_options(false)
540     , m_owns_texture(true)
541     , m_mipmaps_generated(false)
542     , m_retain_image(false)
543 {
544 }
545
546
547 QSGPlainTexture::~QSGPlainTexture()
548 {
549     if (m_texture_id && m_owns_texture)
550         QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_texture_id);
551 }
552
553 void qsg_swizzleBGRAToRGBA(QImage *image)
554 {
555     const int width = image->width();
556     const int height = image->height();
557     for (int i = 0; i < height; ++i) {
558         uint *p = (uint *) image->scanLine(i);
559         for (int x = 0; x < width; ++x)
560             p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00);
561     }
562 }
563
564 void QSGPlainTexture::setImage(const QImage &image)
565 {
566     m_image = image;
567     m_texture_size = image.size();
568     m_has_alpha = image.hasAlphaChannel();
569     m_dirty_texture = true;
570     m_dirty_bind_options = true;
571     m_mipmaps_generated = false;
572  }
573
574 int QSGPlainTexture::textureId() const
575 {
576     if (m_dirty_texture) {
577         if (m_image.isNull()) {
578             // The actual texture and id will be updated/deleted in a later bind()
579             // or ~QSGPlainTexture so just keep it minimal here.
580             return 0;
581         } else if (m_texture_id == 0){
582             // Generate a texture id for use later and return it.
583             QOpenGLContext::currentContext()->functions()->glGenTextures(1, &const_cast<QSGPlainTexture *>(this)->m_texture_id);
584             return m_texture_id;
585         }
586     }
587     return m_texture_id;
588 }
589
590 void QSGPlainTexture::setTextureId(int id)
591 {
592     if (m_texture_id && m_owns_texture)
593         QOpenGLContext::currentContext()->functions()->glDeleteTextures(1, &m_texture_id);
594
595     m_texture_id = id;
596     m_dirty_texture = false;
597     m_dirty_bind_options = true;
598     m_image = QImage();
599     m_mipmaps_generated = false;
600 }
601
602 void QSGPlainTexture::bind()
603 {
604     QOpenGLContext *context = QOpenGLContext::currentContext();
605     QOpenGLFunctions *funcs = context->functions();
606     if (!m_dirty_texture) {
607         funcs->glBindTexture(GL_TEXTURE_2D, m_texture_id);
608         if (mipmapFiltering() != QSGTexture::None && !m_mipmaps_generated) {
609             funcs->glGenerateMipmap(GL_TEXTURE_2D);
610             m_mipmaps_generated = true;
611         }
612         updateBindOptions(m_dirty_bind_options);
613         m_dirty_bind_options = false;
614         return;
615     }
616
617     m_dirty_texture = false;
618
619 #ifndef QSG_NO_RENDER_TIMING
620     bool profileFrames = qsg_render_timing || QQuickProfiler::enabled;
621     if (profileFrames)
622         qsg_renderer_timer.start();
623 #endif
624
625     if (m_image.isNull()) {
626         if (m_texture_id && m_owns_texture) {
627             funcs->glDeleteTextures(1, &m_texture_id);
628 #ifndef QSG_NO_RENDER_TIMING
629             if (qsg_render_timing) {
630                 qDebug("   - texture deleted in %dms (size: %dx%d)",
631                        (int) qsg_renderer_timer.elapsed(),
632                        m_texture_size.width(),
633                        m_texture_size.height());
634             }
635             Q_QUICK_SG_PROFILE1(QQuickProfiler::SceneGraphTextureDeletion, (
636                     qsg_renderer_timer.nsecsElapsed()));
637 #endif
638         }
639         m_texture_id = 0;
640         m_texture_size = QSize();
641         m_has_alpha = false;
642
643         return;
644     }
645
646     if (m_texture_id == 0)
647         funcs->glGenTextures(1, &m_texture_id);
648     funcs->glBindTexture(GL_TEXTURE_2D, m_texture_id);
649
650 #ifndef QSG_NO_RENDER_TIMING
651     qint64 bindTime = 0;
652     if (profileFrames)
653         bindTime = qsg_renderer_timer.nsecsElapsed();
654 #endif
655
656     // ### TODO: check for out-of-memory situations...
657     int w = m_image.width();
658     int h = m_image.height();
659
660     QImage tmp = (m_image.format() == QImage::Format_RGB32 || m_image.format() == QImage::Format_ARGB32_Premultiplied)
661                  ? m_image
662                  : m_image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
663     if (tmp.width() * 4 != tmp.bytesPerLine())
664         tmp = tmp.copy();
665
666 #ifndef QSG_NO_RENDER_TIMING
667     qint64 convertTime = 0;
668     if (profileFrames)
669         convertTime = qsg_renderer_timer.nsecsElapsed();
670 #endif
671
672     updateBindOptions(m_dirty_bind_options);
673
674     GLenum externalFormat = GL_RGBA;
675     GLenum internalFormat = GL_RGBA;
676
677 #if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_NO_SDK)
678     QString *deviceName =
679             static_cast<QString *>(QGuiApplication::platformNativeInterface()->nativeResourceForIntegration("AndroidDeviceName"));
680     static bool wrongfullyReportsBgra8888Support = deviceName != 0
681                                                     && (deviceName->compare(QStringLiteral("samsung SM-T211"), Qt::CaseInsensitive) == 0
682                                                         || deviceName->compare(QStringLiteral("samsung SM-T210"), Qt::CaseInsensitive) == 0
683                                                         || deviceName->compare(QStringLiteral("samsung SM-T215"), Qt::CaseInsensitive) == 0);
684 #else
685     static bool wrongfullyReportsBgra8888Support = false;
686 #endif
687
688     if (context->hasExtension(QByteArrayLiteral("GL_EXT_bgra"))) {
689         externalFormat = GL_BGRA;
690 #ifdef QT_OPENGL_ES
691         internalFormat = GL_BGRA;
692 #else
693         if (context->isOpenGLES())
694             internalFormat = GL_BGRA;
695 #endif // QT_OPENGL_ES
696     } else if (!wrongfullyReportsBgra8888Support
697                && (context->hasExtension(QByteArrayLiteral("GL_EXT_texture_format_BGRA8888"))
698                    || context->hasExtension(QByteArrayLiteral("GL_IMG_texture_format_BGRA8888")))) {
699         externalFormat = GL_BGRA;
700         internalFormat = GL_BGRA;
701 #ifdef Q_OS_IOS
702     } else if (context->hasExtension(QByteArrayLiteral("GL_APPLE_texture_format_BGRA8888"))) {
703         externalFormat = GL_BGRA;
704         internalFormat = GL_RGBA;
705 #endif
706     } else {
707         qsg_swizzleBGRAToRGBA(&tmp);
708     }
709
710 #ifndef QSG_NO_RENDER_TIMING
711     qint64 swizzleTime = 0;
712     if (profileFrames)
713         swizzleTime = qsg_renderer_timer.nsecsElapsed();
714 #endif
715     funcs->glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, w, h, 0, externalFormat, GL_UNSIGNED_BYTE, tmp.constBits());
716
717 #ifndef QSG_NO_RENDER_TIMING
718     qint64 uploadTime = 0;
719     if (profileFrames)
720         uploadTime = qsg_renderer_timer.nsecsElapsed();
721 #endif
722
723
724     if (mipmapFiltering() != QSGTexture::None) {
725         funcs->glGenerateMipmap(GL_TEXTURE_2D);
726         m_mipmaps_generated = true;
727     }
728
729 #ifndef QSG_NO_RENDER_TIMING
730     qint64 mipmapTime = 0;
731     if (qsg_render_timing) {
732         mipmapTime = qsg_renderer_timer.nsecsElapsed();
733
734         qDebug("   - plaintexture(%dx%d) bind=%d, convert=%d, swizzle=%d (%s->%s), upload=%d, mipmap=%d, total=%d",
735                m_texture_size.width(), m_texture_size.height(),
736                int(bindTime/1000000),
737                int((convertTime - bindTime)/1000000),
738                int((swizzleTime - convertTime)/1000000),
739                externalFormat == GL_BGRA ? "BGRA" : "RGBA",
740                internalFormat == GL_BGRA ? "BGRA" : "RGBA",
741                int((uploadTime - swizzleTime)/1000000),
742                int((mipmapTime - uploadTime)/1000000),
743                (int) qsg_renderer_timer.elapsed());
744
745     }
746
747     Q_QUICK_SG_PROFILE1(QQuickProfiler::SceneGraphTexturePrepare, (
748             bindTime,
749             convertTime - bindTime,
750             swizzleTime - convertTime,
751             uploadTime - swizzleTime,
752             qsg_renderer_timer.nsecsElapsed() - uploadTime));
753
754 #endif
755
756
757     m_texture_size = QSize(w, h);
758     m_texture_rect = QRectF(0, 0, 1, 1);
759
760     m_dirty_bind_options = false;
761     if (!m_retain_image)
762         m_image = QImage();
763 }
764
765
766 /*!
767     \class QSGDynamicTexture
768     \brief The QSGDynamicTexture class serves as a baseclass for dynamically changing textures,
769     such as content that is rendered to FBO's.
770     \inmodule QtQuick
771
772     To update the content of the texture, call updateTexture() explicitly. Simply calling bind()
773     will not update the texture.
774
775     \note All classes with QSG prefix should be used solely on the scene graph's
776     rendering thread. See \l {Scene Graph and Rendering} for more information.
777  */
778
779
780 /*!
781     \fn bool QSGDynamicTexture::updateTexture()
782
783     Call this function to explicitly update the dynamic texture. Calling bind() will bind
784     the content that was previously updated.
785
786     The function returns true if the texture was changed as a resul of the update; otherwise
787     returns false.
788  */
789
790
791
792 QT_END_NAMESPACE