c3f15cc5687f13e00b9ec1456ed5cedb67c01767
[profile/ivi/qtdeclarative.git] / src / quick / scenegraph / util / qsgtexture.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtQml module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #define GL_GLEXT_PROTOTYPES
43
44 #include "qsgtexture_p.h"
45 #include <qopenglfunctions.h>
46 #include <QtQuick/private/qsgcontext_p.h>
47 #include <qthread.h>
48 #include <private/qqmlprofilerservice_p.h>
49 #include <private/qqmlglobal_p.h>
50
51 #if defined(Q_OS_LINUX) && !defined(Q_OS_LINUX_ANDROID)
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 QT_BEGIN_NAMESPACE
69
70 inline static bool isPowerOfTwo(int x)
71 {
72     // Assumption: x >= 1
73     return x == (x & -x);
74 }
75
76 QSGTexturePrivate::QSGTexturePrivate()
77     : wrapChanged(false)
78     , filteringChanged(false)
79     , horizontalWrap(QSGTexture::ClampToEdge)
80     , verticalWrap(QSGTexture::ClampToEdge)
81     , mipmapMode(QSGTexture::None)
82     , filterMode(QSGTexture::Nearest)
83 {
84 }
85
86 #ifndef QT_NO_DEBUG
87
88 static int qt_debug_texture_count = 0;
89
90 #if defined(Q_OS_LINUX) || defined (Q_OS_MAC)
91 DEFINE_BOOL_CONFIG_OPTION(qmlDebugLeakBacktrace, QML_DEBUG_LEAK_BACKTRACE)
92
93 #define BACKTRACE_SIZE 20
94 class SGTextureTraceItem
95 {
96 public:
97     void *backTrace[BACKTRACE_SIZE];
98     size_t backTraceSize;
99 };
100
101 static QHash<QSGTexture*, SGTextureTraceItem*> qt_debug_allocated_textures;
102 #endif
103
104 inline static void qt_debug_print_texture_count()
105 {
106     qDebug("Number of leaked textures: %i", qt_debug_texture_count);
107     qt_debug_texture_count = -1;
108
109 #if defined(CAN_BACKTRACE_EXECINFO)
110     if (qmlDebugLeakBacktrace()) {
111         while (!qt_debug_allocated_textures.isEmpty()) {
112             QHash<QSGTexture*, SGTextureTraceItem*>::Iterator it = qt_debug_allocated_textures.begin();
113             QSGTexture* texture = it.key();
114             SGTextureTraceItem* item = it.value();
115
116             qt_debug_allocated_textures.erase(it);
117
118             qDebug() << "------";
119             qDebug() << "Leaked" << texture << "backtrace:";
120
121             char** symbols = backtrace_symbols(item->backTrace, item->backTraceSize);
122
123             if (symbols) {
124                 for (int i=0; i<(int) item->backTraceSize; i++)
125                     qDebug("Backtrace <%02d>: %s", i, symbols[i]);
126                 free(symbols);
127             }
128
129             qDebug() << "------";
130
131             delete item;
132         }
133     }
134 #endif
135 }
136
137 inline static void qt_debug_add_texture(QSGTexture* texture)
138 {
139 #if defined(CAN_BACKTRACE_EXECINFO)
140     if (qmlDebugLeakBacktrace()) {
141         SGTextureTraceItem* item = new SGTextureTraceItem;
142         item->backTraceSize = backtrace(item->backTrace, BACKTRACE_SIZE);
143         qt_debug_allocated_textures.insert(texture, item);
144     }
145 #else
146     Q_UNUSED(texture);
147 #endif // Q_OS_LINUX
148
149     ++qt_debug_texture_count;
150
151     static bool atexit_registered = false;
152     if (!atexit_registered) {
153         atexit(qt_debug_print_texture_count);
154         atexit_registered = true;
155     }
156 }
157
158 static void qt_debug_remove_texture(QSGTexture* texture)
159 {
160 #if defined(CAN_BACKTRACE_EXECINFO)
161     if (qmlDebugLeakBacktrace()) {
162         SGTextureTraceItem* item = qt_debug_allocated_textures.value(texture, 0);
163         if (item) {
164             qt_debug_allocated_textures.remove(texture);
165             delete item;
166         }
167     }
168 #else
169     Q_UNUSED(texture)
170 #endif
171
172     --qt_debug_texture_count;
173
174     if (qt_debug_texture_count < 0)
175         qDebug("Material destroyed after qt_debug_print_texture_count() was called.");
176 }
177
178 #endif // QT_NO_DEBUG
179
180 /*!
181     \class QSGTexture
182
183     \inmodule QtQuick
184
185     \brief The QSGTexture class is a baseclass for textures used in
186     the scene graph.
187
188
189     Users can freely implement their own texture classes to support
190     arbitrary input textures, such as YUV video frames or 8 bit alpha
191     masks. The scene graph backend provides a default implementation
192     of normal color textures. As the implementation of these may be
193     hardware specific, they are are constructed via the factory
194     function QQuickCanvas::createTextureFromImage().
195
196     The texture is a wrapper around an OpenGL texture, which texture
197     id is given by textureId() and which size in pixels is given by
198     textureSize(). hasAlphaChannel() reports if the texture contains
199     opacity values and hasMipmaps() reports if the texture contains
200     mipmap levels.
201
202     To use a texture, call the bind() function. The texture parameters
203     specifying how the texture is bound, can be specified with
204     setMipmapFiltering(), setFiltering(), setHorizontalWrapMode() and
205     setVerticalWrapMode(). The texture will internally try to store
206     these values to minimize the OpenGL state changes when the texture
207     is bound.
208
209     \section1 Texture Atlasses
210
211     Some scene graph backens use texture atlasses, grouping multiple
212     small textures into one large texture. If this is the case, the
213     function isAtlasTexture() will return true. Atlasses are used to
214     aid the rendering algorithm to do better sorting which increases
215     performance. The location of the texture inside the atlas is
216     given with the normalizedTextureSubRect() function.
217
218     If the texture is used in such a way that atlas is not preferable,
219     the function removedFromAtlas() can be used to extract a
220     non-atlassed copy.
221  */
222
223 /*!
224     \enum QSGTexture::WrapMode
225
226     Specifies how the texture should treat texture coordinates.
227
228     \value Repeat Only the factional part of the texture coordiante is
229     used, causing values above 1 and below 0 to repeat.
230
231     \value ClampToEdge Values above 1 are clamped to 1 and values
232     below 0 are clamped to 0.
233  */
234
235 /*!
236     \enum QSGTexture::Filtering
237
238     Specifies how sampling of texels should filter when texture
239     coordinates are not pixel aligned.
240
241     \value None No filtering should occur. This value is only used
242     together with setMipmapFiltering().
243
244     \value Nearest Sampling returns the nearest texel.
245
246     \value Linear Sampling returns a linear interpolation of the
247     neighboring texels.
248 */
249
250 QSGTexture::QSGTexture()
251     : QObject(*(new QSGTexturePrivate))
252 {
253 #ifndef QT_NO_DEBUG
254     qt_debug_add_texture(this);
255 #endif
256 }
257
258 QSGTexture::~QSGTexture()
259 {
260 #ifndef QT_NO_DEBUG
261     qt_debug_remove_texture(this);
262 #endif
263 }
264
265
266 /*!
267     \fn void QSGTexture::bind()
268
269     Call this function to bind this texture to the current texture
270     target.
271
272     Binding a texture may also include uploading the texture data from
273     a previously set QImage.
274
275     \warning This function can only be called from the rendering thread.
276  */
277
278 /*!
279     This function returns a copy of the current texture which is removed
280     from its atlas.
281
282     The current texture remains unchanged, so texture coordinates do not
283     need to be updated.
284
285     Removing a texture from an atlas is primarily useful when passing
286     it to a shader that operates on the texture coordinates 0-1 instead
287     of the texture subrect inside the atlas.
288
289     If the texture is not part of a texture atlas, this function returns 0.
290
291     Implementations of this function are recommended to return the same instance
292     for multiple calls to limit memory usage.
293
294     \warning This function can only be called from the rendering thread.
295  */
296
297 QSGTexture *QSGTexture::removedFromAtlas() const
298 {
299     Q_ASSERT_X(!isAtlasTexture(), "QSGTexture::removedFromAtlas()", "Called on a non-atlas texture");
300     return 0;
301 }
302
303 /*!
304     Returns weither this texture is part of an atlas or not.
305
306     The default implementation returns false.
307  */
308 bool QSGTexture::isAtlasTexture() const
309 {
310     return false;
311 }
312
313 /*!
314     \fn int QSGTexture::textureId() const
315
316     Returns the OpenGL texture id for this texture.
317
318     The default value is 0, indicating that it is an invalid texture id.
319
320     The function should at all times return the correct texture id.
321
322     \warning This function can only be called from the rendering thread.
323  */
324
325
326
327 /*!
328     Returns the rectangle inside textureSize() that this texture
329     represents in normalized coordinates.
330
331     The default implementation returns a rect at position (0, 0) with
332     width and height of 1.
333  */
334 QRectF QSGTexture::normalizedTextureSubRect() const
335 {
336     return QRectF(0, 0, 1, 1);
337 }
338
339 /*!
340     \fn bool QSGTexture::hasMipmaps() const
341
342     Returns true if the texture data contains mipmap levels.
343  */
344
345
346 /*!
347     Sets the mipmap sampling mode to be used for the upcoming bind() call to \a filter.
348
349     Setting the mipmap filtering has no effect it the texture does not have mipmaps.
350
351     \sa hasMipmaps()
352  */
353 void QSGTexture::setMipmapFiltering(Filtering filter)
354 {
355     Q_D(QSGTexture);
356     if (d->mipmapMode != (uint) filter) {
357         d->mipmapMode = filter;
358         d->filteringChanged = true;
359     }
360 }
361
362 /*!
363     Returns whether mipmapping should be used when sampling from this texture.
364  */
365 QSGTexture::Filtering QSGTexture::mipmapFiltering() const
366 {
367     return (QSGTexture::Filtering) d_func()->mipmapMode;
368 }
369
370
371 /*!
372     Sets the sampling mode to be used for the upcoming bind() call to \a filter.
373  */
374 void QSGTexture::setFiltering(QSGTexture::Filtering filter)
375 {
376     Q_D(QSGTexture);
377     if (d->filterMode != (uint) filter) {
378         d->filterMode = filter;
379         d->filteringChanged = true;
380     }
381 }
382
383 QSGTexture::Filtering QSGTexture::filtering() const
384 {
385     return (QSGTexture::Filtering) d_func()->filterMode;
386 }
387
388
389
390 /*!
391     Sets the horizontal wrap mode to be used for the upcoming bind() call to \a hwrap
392  */
393
394 void QSGTexture::setHorizontalWrapMode(WrapMode hwrap)
395 {
396     Q_D(QSGTexture);
397     if ((uint) hwrap != d->horizontalWrap) {
398         d->horizontalWrap = hwrap;
399         d->wrapChanged = true;
400     }
401 }
402
403 QSGTexture::WrapMode QSGTexture::horizontalWrapMode() const
404 {
405     return (QSGTexture::WrapMode) d_func()->horizontalWrap;
406 }
407
408
409
410 void QSGTexture::setVerticalWrapMode(WrapMode vwrap)
411 {
412     Q_D(QSGTexture);
413     if ((uint) vwrap != d->verticalWrap) {
414         d->verticalWrap = vwrap;
415         d->wrapChanged = true;
416     }
417 }
418
419 QSGTexture::WrapMode QSGTexture::verticalWrapMode() const
420 {
421     return (QSGTexture::WrapMode) d_func()->verticalWrap;
422 }
423
424
425 /*!
426     Update the texture state to match the filtering, mipmap and wrap options
427     currently set.
428
429     If \a force is true, all properties will be updated regardless of weither
430     they have changed or not.
431  */
432 void QSGTexture::updateBindOptions(bool force)
433 {
434     Q_D(QSGTexture);
435     if (force || d->filteringChanged) {
436         bool linear = d->filterMode == Linear;
437         GLint minFilter = linear ? GL_LINEAR : GL_NEAREST;
438         GLint magFilter = linear ? GL_LINEAR : GL_NEAREST;
439
440         if (hasMipmaps()) {
441             if (d->mipmapMode == Nearest)
442                 minFilter = linear ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_NEAREST;
443             else if (d->mipmapMode == Linear)
444                 minFilter = linear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_LINEAR;
445         }
446         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
447         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
448         d->filteringChanged = false;
449     }
450
451     if (force || d->wrapChanged) {
452 #if !defined(QT_NO_DEBUG) && defined(QT_OPENGL_ES_2)
453         if (d->horizontalWrap == Repeat || d->verticalWrap == Repeat) {
454             bool npotSupported = QOpenGLFunctions(QOpenGLContext::currentContext()).hasOpenGLFeature(QOpenGLFunctions::NPOTTextures);
455             QSize size = textureSize();
456             bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
457             if (!npotSupported && isNpot)
458                 qWarning("Scene Graph: This system does not support the REPEAT wrap mode for non-power-of-two textures.");
459         }
460 #endif
461         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, d->horizontalWrap == Repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
462         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, d->verticalWrap == Repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
463         d->wrapChanged = false;
464     }
465 }
466
467 QSGPlainTexture::QSGPlainTexture()
468     : QSGTexture()
469     , m_texture_id(0)
470     , m_has_alpha(false)
471     , m_has_mipmaps(false)
472     , m_dirty_bind_options(false)
473     , m_owns_texture(true)
474     , m_mipmaps_generated(false)
475 {
476 }
477
478
479 QSGPlainTexture::~QSGPlainTexture()
480 {
481     if (m_texture_id && m_owns_texture)
482         glDeleteTextures(1, &m_texture_id);
483 }
484
485 #ifdef QT_OPENGL_ES
486 void qsg_swizzleBGRAToRGBA(QImage *image)
487 {
488     const int width = image->width();
489     const int height = image->height();
490     for (int i = 0; i < height; ++i) {
491         uint *p = (uint *) image->scanLine(i);
492         for (int x = 0; x < width; ++x)
493             p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00);
494     }
495 }
496 #endif
497
498 void QSGPlainTexture::setImage(const QImage &image)
499 {
500     m_image = image;
501     m_texture_size = image.size();
502     m_has_alpha = image.hasAlphaChannel();
503     m_dirty_texture = true;
504     m_dirty_bind_options = true;
505  }
506
507 int QSGPlainTexture::textureId() const
508 {
509     if (m_dirty_texture) {
510         if (m_image.isNull()) {
511             // The actual texture and id will be updated/deleted in a later bind()
512             // or ~QSGPlainTexture so just keep it minimal here.
513             return 0;
514         } else if (m_texture_id == 0){
515             // Generate a texture id for use later and return it.
516             glGenTextures(1, &const_cast<QSGPlainTexture *>(this)->m_texture_id);
517             return m_texture_id;
518         }
519     }
520     return m_texture_id;
521 }
522
523 void QSGPlainTexture::setTextureId(int id)
524 {
525     if (m_texture_id && m_owns_texture)
526         glDeleteTextures(1, &m_texture_id);
527
528     m_texture_id = id;
529     m_dirty_texture = false;
530     m_dirty_bind_options = true;
531     m_image = QImage();
532     m_mipmaps_generated = false;
533 }
534
535 void QSGPlainTexture::setHasMipmaps(bool mm)
536 {
537     m_has_mipmaps = mm;
538     m_mipmaps_generated = false;
539 }
540
541
542 void QSGPlainTexture::bind()
543 {
544     if (!m_dirty_texture) {
545         glBindTexture(GL_TEXTURE_2D, m_texture_id);
546         if (m_has_mipmaps && !m_mipmaps_generated) {
547             QOpenGLContext *ctx = QOpenGLContext::currentContext();
548             ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D);
549             m_mipmaps_generated = true;
550         }
551         updateBindOptions(m_dirty_bind_options);
552         m_dirty_bind_options = false;
553         return;
554     }
555
556     m_dirty_texture = false;
557
558
559     if (m_image.isNull()) {
560         if (m_texture_id && m_owns_texture)
561             glDeleteTextures(1, &m_texture_id);
562         m_texture_id = 0;
563         m_texture_size = QSize();
564         m_has_mipmaps = false;
565         m_has_alpha = false;
566         return;
567     }
568
569     if (m_texture_id == 0)
570         glGenTextures(1, &m_texture_id);
571     glBindTexture(GL_TEXTURE_2D, m_texture_id);
572
573     // ### TODO: check for out-of-memory situations...
574     int w = m_image.width();
575     int h = m_image.height();
576
577     QImage tmp = (m_image.format() == QImage::Format_RGB32 || m_image.format() == QImage::Format_ARGB32_Premultiplied)
578                  ? m_image
579                  : m_image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
580
581     updateBindOptions(m_dirty_bind_options);
582
583 #ifdef QT_OPENGL_ES
584         qsg_swizzleBGRAToRGBA(&tmp);
585         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, tmp.constBits());
586 #else
587         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, tmp.constBits());
588 #endif
589
590     if (m_has_mipmaps) {
591         QOpenGLContext *ctx = QOpenGLContext::currentContext();
592         ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D);
593         m_mipmaps_generated = true;
594     }
595
596     m_texture_size = QSize(w, h);
597     m_texture_rect = QRectF(0, 0, 1, 1);
598
599     m_dirty_bind_options = false;
600 }
601
602
603 /*!
604     \class QSGDynamicTexture
605     \brief The QSGDynamicTexture class serves as a baseclass for dynamically changing textures,
606     such as content that is rendered to FBO's.
607     \inmodule QtQuick
608
609     To update the content of the texture, call updateTexture() explicitly. Simply calling bind()
610     will not update the texture.
611  */
612
613
614 /*!
615     \fn bool QSGDynamicTexture::updateTexture()
616
617     Call this function to explicitely update the dynamic texture. Calling bind() will bind
618     the content that was previously updated.
619
620     The function returns true if the texture was changed as a resul of the update; otherwise
621     returns false.
622  */
623
624
625
626 QT_END_NAMESPACE