Remove "All rights reserved" line from license headers.
[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 QtDeclarative 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/qdeclarativedebugtrace_p.h>
49
50 #if !defined(QT_NO_DEBUG) && (defined(Q_OS_LINUX) || defined(Q_OS_MAC))
51 #include <execinfo.h>
52 #include <QHash>
53 #endif
54
55 QT_BEGIN_NAMESPACE
56
57 inline static bool isPowerOfTwo(int x)
58 {
59     // Assumption: x >= 1
60     return x == (x & -x);
61 }
62
63 QSGTexturePrivate::QSGTexturePrivate()
64     : wrapChanged(false)
65     , filteringChanged(false)
66     , horizontalWrap(QSGTexture::ClampToEdge)
67     , verticalWrap(QSGTexture::ClampToEdge)
68     , mipmapMode(QSGTexture::None)
69     , filterMode(QSGTexture::Nearest)
70 {
71 }
72
73 #ifndef QT_NO_DEBUG
74
75 static int qt_debug_texture_count = 0;
76
77 #if defined(Q_OS_LINUX) || defined (Q_OS_MAC)
78 DEFINE_BOOL_CONFIG_OPTION(qmlDebugLeakBacktrace, QML_DEBUG_LEAK_BACKTRACE)
79
80 #define BACKTRACE_SIZE 20
81 class SGTextureTraceItem
82 {
83 public:
84     void *backTrace[BACKTRACE_SIZE];
85     size_t backTraceSize;
86 };
87
88 static QHash<QSGTexture*, SGTextureTraceItem*> qt_debug_allocated_textures;
89 #endif
90
91 inline static void qt_debug_print_texture_count()
92 {
93     qDebug("Number of leaked textures: %i", qt_debug_texture_count);
94     qt_debug_texture_count = -1;
95
96 #if defined(Q_OS_LINUX) || defined (Q_OS_MAC)
97     if (qmlDebugLeakBacktrace()) {
98         while (!qt_debug_allocated_textures.isEmpty()) {
99             QHash<QSGTexture*, SGTextureTraceItem*>::Iterator it = qt_debug_allocated_textures.begin();
100             QSGTexture* texture = it.key();
101             SGTextureTraceItem* item = it.value();
102
103             qt_debug_allocated_textures.erase(it);
104
105             qDebug() << "------";
106             qDebug() << "Leaked" << texture << "backtrace:";
107
108             char** symbols = backtrace_symbols(item->backTrace, item->backTraceSize);
109
110             if (symbols) {
111                 for (int i=0; i<(int) item->backTraceSize; i++)
112                     qDebug("Backtrace <%02d>: %s", i, symbols[i]);
113                 free(symbols);
114             }
115
116             qDebug() << "------";
117
118             delete item;
119         }
120     }
121 #endif
122 }
123
124 inline static void qt_debug_add_texture(QSGTexture* texture)
125 {
126 #if defined(Q_OS_LINUX) || defined (Q_OS_MAC)
127     if (qmlDebugLeakBacktrace()) {
128         SGTextureTraceItem* item = new SGTextureTraceItem;
129         item->backTraceSize = backtrace(item->backTrace, BACKTRACE_SIZE);
130         qt_debug_allocated_textures.insert(texture, item);
131     }
132 #else
133     Q_UNUSED(texture);
134 #endif // Q_OS_LINUX
135
136     ++qt_debug_texture_count;
137
138     static bool atexit_registered = false;
139     if (!atexit_registered) {
140         atexit(qt_debug_print_texture_count);
141         atexit_registered = true;
142     }
143 }
144
145 static void qt_debug_remove_texture(QSGTexture* texture)
146 {
147 #if defined(Q_OS_LINUX) || defined (Q_OS_MAC)
148     if (qmlDebugLeakBacktrace()) {
149         SGTextureTraceItem* item = qt_debug_allocated_textures.value(texture, 0);
150         if (item) {
151             qt_debug_allocated_textures.remove(texture);
152             delete item;
153         }
154     }
155 #else
156     Q_UNUSED(texture)
157 #endif
158
159     --qt_debug_texture_count;
160
161     if (qt_debug_texture_count < 0)
162         qDebug("Material destroyed after qt_debug_print_texture_count() was called.");
163 }
164
165 #endif // QT_NO_DEBUG
166
167
168 QSGTexture::QSGTexture()
169     : QObject(*(new QSGTexturePrivate))
170 {
171 #ifndef QT_NO_DEBUG
172     qt_debug_add_texture(this);
173 #endif
174 }
175
176 QSGTexture::~QSGTexture()
177 {
178 #ifndef QT_NO_DEBUG
179     qt_debug_remove_texture(this);
180 #endif
181 }
182
183
184 /*!
185     \fn void QSGTexture::bind()
186
187     Call this function to bind this texture to the current texture
188     target.
189
190     Binding a texture may also include uploading the texture data from
191     a previously set QImage.
192
193     \warning This function can only be called from the rendering thread.
194  */
195
196 /*!
197     This function returns a copy of the current texture which is removed
198     from its atlas.
199
200     The current texture remains unchanged, so texture coordinates do not
201     need to be updated.
202
203     Removing a texture from an atlas is primarily useful when passing
204     it to a shader that operates on the texture coordinates 0-1 instead
205     of the texture subrect inside the atlas.
206
207     If the texture is not part of a texture atlas, this function returns 0.
208
209     Implementations of this function are recommended to return the same instance
210     for multiple calls to limit memory usage.
211
212     \warning This function can only be called from the rendering thread.
213  */
214
215 QSGTexture *QSGTexture::removedFromAtlas() const
216 {
217     Q_ASSERT_X(!isAtlasTexture(), "QSGTexture::removedFromAtlas()", "Called on a non-atlas texture");
218     return 0;
219 }
220
221 /*!
222     Returns weither this texture is part of an atlas or not.
223
224     The default implementation returns false.
225  */
226 bool QSGTexture::isAtlasTexture() const
227 {
228     return false;
229 }
230
231 /*!
232     \fn int QSGTexture::textureId() const
233
234     Returns the OpenGL texture id for this texture.
235
236     The default value is 0, indicating that it is an invalid texture id.
237
238     The function should at all times return the correct texture id.
239
240     \warning This function can only be called from the rendering thread.
241  */
242
243
244
245 /*!
246     Returns the rectangle inside textureSize() that this texture
247     represents in normalized coordinates.
248
249     The default implementation returns a rect at position (0, 0) with
250     width and height of 1.
251  */
252 QRectF QSGTexture::normalizedTextureSubRect() const
253 {
254     return QRectF(0, 0, 1, 1);
255 }
256
257 /*!
258     \fn bool QSGTexture::hasMipmaps() const
259
260     Returns true if the texture data contains mipmap levels.
261  */
262
263
264 /*!
265     Sets the mipmap sampling mode to be used for the upcoming bind() call to \a filter.
266
267     Setting the mipmap filtering has no effect it the texture does not have mipmaps.
268
269     \sa hasMipmaps()
270  */
271 void QSGTexture::setMipmapFiltering(Filtering filter)
272 {
273     Q_D(QSGTexture);
274     if (d->mipmapMode != (uint) filter) {
275         d->mipmapMode = filter;
276         d->filteringChanged = true;
277     }
278 }
279
280 /*!
281     Returns whether mipmapping should be used when sampling from this texture.
282  */
283 QSGTexture::Filtering QSGTexture::mipmapFiltering() const
284 {
285     return (QSGTexture::Filtering) d_func()->mipmapMode;
286 }
287
288
289 /*!
290     Sets the sampling mode to be used for the upcoming bind() call to \a filter.
291  */
292 void QSGTexture::setFiltering(QSGTexture::Filtering filter)
293 {
294     Q_D(QSGTexture);
295     if (d->filterMode != (uint) filter) {
296         d->filterMode = filter;
297         d->filteringChanged = true;
298     }
299 }
300
301 QSGTexture::Filtering QSGTexture::filtering() const
302 {
303     return (QSGTexture::Filtering) d_func()->filterMode;
304 }
305
306
307
308 /*!
309     Sets the horizontal wrap mode to be used for the upcoming bind() call to \a hwrap
310  */
311
312 void QSGTexture::setHorizontalWrapMode(WrapMode hwrap)
313 {
314     Q_D(QSGTexture);
315     if ((uint) hwrap != d->horizontalWrap) {
316         d->horizontalWrap = hwrap;
317         d->wrapChanged = true;
318     }
319 }
320
321 QSGTexture::WrapMode QSGTexture::horizontalWrapMode() const
322 {
323     return (QSGTexture::WrapMode) d_func()->horizontalWrap;
324 }
325
326
327
328 void QSGTexture::setVerticalWrapMode(WrapMode vwrap)
329 {
330     Q_D(QSGTexture);
331     if ((uint) vwrap != d->verticalWrap) {
332         d->verticalWrap = vwrap;
333         d->wrapChanged = true;
334     }
335 }
336
337 QSGTexture::WrapMode QSGTexture::verticalWrapMode() const
338 {
339     return (QSGTexture::WrapMode) d_func()->verticalWrap;
340 }
341
342
343 /*!
344     Update the texture state to match the filtering, mipmap and wrap options
345     currently set.
346
347     If \a force is true, all properties will be updated regardless of weither
348     they have changed or not.
349  */
350 void QSGTexture::updateBindOptions(bool force)
351 {
352     Q_D(QSGTexture);
353     if (force || d->filteringChanged) {
354         bool linear = d->filterMode == Linear;
355         GLint minFilter = linear ? GL_LINEAR : GL_NEAREST;
356         GLint magFilter = linear ? GL_LINEAR : GL_NEAREST;
357
358         if (hasMipmaps()) {
359             if (d->mipmapMode == Nearest)
360                 minFilter = linear ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_NEAREST;
361             else if (d->mipmapMode == Linear)
362                 minFilter = linear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_LINEAR;
363         }
364         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
365         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
366         d->filteringChanged = false;
367     }
368
369     if (force || d->wrapChanged) {
370 #if !defined(QT_NO_DEBUG) && defined(QT_OPENGL_ES_2)
371         if (d->horizontalWrap == Repeat || d->verticalWrap == Repeat) {
372             bool npotSupported = QOpenGLFunctions(QOpenGLContext::currentContext()).hasOpenGLFeature(QOpenGLFunctions::NPOTTextures);
373             QSize size = textureSize();
374             bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
375             if (!npotSupported && isNpot)
376                 qWarning("Scene Graph: This system does not support the REPEAT wrap mode for non-power-of-two textures.");
377         }
378 #endif
379         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, d->horizontalWrap == Repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
380         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, d->verticalWrap == Repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
381         d->wrapChanged = false;
382     }
383 }
384
385 QSGPlainTexture::QSGPlainTexture()
386     : QSGTexture()
387     , m_texture_id(0)
388     , m_has_alpha(false)
389     , m_has_mipmaps(false)
390     , m_dirty_bind_options(false)
391     , m_owns_texture(true)
392     , m_mipmaps_generated(false)
393 {
394 }
395
396
397 QSGPlainTexture::~QSGPlainTexture()
398 {
399     if (m_texture_id && m_owns_texture)
400         glDeleteTextures(1, &m_texture_id);
401 }
402
403 #ifdef QT_OPENGL_ES
404 static void swizzleBGRAToRGBA(QImage *image)
405 {
406     const int width = image->width();
407     const int height = image->height();
408     for (int i = 0; i < height; ++i) {
409         uint *p = (uint *) image->scanLine(i);
410         for (int x = 0; x < width; ++x)
411             p[x] = ((p[x] << 16) & 0xff0000) | ((p[x] >> 16) & 0xff) | (p[x] & 0xff00ff00);
412     }
413 }
414 #endif
415
416 void QSGPlainTexture::setImage(const QImage &image)
417 {
418     m_image = image;
419     m_texture_size = image.size();
420     m_has_alpha = image.hasAlphaChannel();
421     m_dirty_texture = true;
422     m_dirty_bind_options = true;
423  }
424
425 int QSGPlainTexture::textureId() const
426 {
427     if (m_dirty_texture) {
428         if (m_image.isNull()) {
429             // The actual texture and id will be updated/deleted in a later bind()
430             // or ~QSGPlainTexture so just keep it minimal here.
431             return 0;
432         } else {
433             // Generate a texture id for use later and return it.
434             glGenTextures(1, &const_cast<QSGPlainTexture *>(this)->m_texture_id);
435             return m_texture_id;
436         }
437     }
438     return m_texture_id;
439 }
440
441 void QSGPlainTexture::setTextureId(int id)
442 {
443     if (m_texture_id && m_owns_texture)
444         glDeleteTextures(1, &m_texture_id);
445
446     m_texture_id = id;
447     m_dirty_texture = false;
448     m_dirty_bind_options = true;
449     m_image = QImage();
450     m_mipmaps_generated = false;
451 }
452
453 void QSGPlainTexture::setHasMipmaps(bool mm)
454 {
455     m_has_mipmaps = mm;
456     m_mipmaps_generated = false;
457 }
458
459
460 void QSGPlainTexture::bind()
461 {
462     if (!m_dirty_texture) {
463         glBindTexture(GL_TEXTURE_2D, m_texture_id);
464         if (m_has_mipmaps && !m_mipmaps_generated) {
465             QOpenGLContext *ctx = QOpenGLContext::currentContext();
466             ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D);
467             m_mipmaps_generated = true;
468         }
469         updateBindOptions(m_dirty_bind_options);
470         m_dirty_bind_options = false;
471         return;
472     }
473
474     m_dirty_texture = false;
475
476
477     if (m_image.isNull()) {
478         if (m_texture_id && m_owns_texture)
479             glDeleteTextures(1, &m_texture_id);
480         m_texture_id = 0;
481         m_texture_size = QSize();
482         m_has_mipmaps = false;
483         m_has_alpha = false;
484         return;
485     }
486
487     if (m_texture_id == 0)
488         glGenTextures(1, &m_texture_id);
489     glBindTexture(GL_TEXTURE_2D, m_texture_id);
490
491     // ### TODO: check for out-of-memory situations...
492     int w = m_image.width();
493     int h = m_image.height();
494
495     QImage tmp = (m_image.format() == QImage::Format_RGB32 || m_image.format() == QImage::Format_ARGB32_Premultiplied)
496                  ? m_image
497                  : m_image.convertToFormat(QImage::Format_ARGB32_Premultiplied);
498
499 #ifdef QT_OPENGL_ES
500         swizzleBGRAToRGBA(&tmp);
501         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, tmp.constBits());
502 #else
503         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, tmp.constBits());
504 #endif
505
506     if (m_has_mipmaps) {
507         QOpenGLContext *ctx = QOpenGLContext::currentContext();
508         ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D);
509         m_mipmaps_generated = true;
510     }
511
512     m_texture_size = QSize(w, h);
513     m_texture_rect = QRectF(0, 0, 1, 1);
514
515     updateBindOptions(m_dirty_bind_options);
516     m_dirty_bind_options = false;
517 }
518
519
520 /*!
521     \class QSGDynamicTexture
522     \brief The QSGDynamicTexture class serves as a baseclass for dynamically changing textures,
523     such as content that is rendered to FBO's.
524
525     To update the content of the texture, call updateTexture() explicitly. Simply calling bind()
526     will not update the texture.
527  */
528
529
530 /*!
531     \fn bool QSGDynamicTexture::updateTexture()
532
533     Call this function to explicitely update the dynamic texture. Calling bind() will bind
534     the content that was previously updated.
535
536     The function returns true if the texture was changed as a resul of the update; otherwise
537     returns false.
538  */
539
540
541
542 QT_END_NAMESPACE