Say hello to QtQuick module
[profile/ivi/qtdeclarative.git] / src / quick / scenegraph / util / qsgtexture.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
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::textureSubRect() 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.convertToFormat(QImage::Format_ARGB32_Premultiplied);
419 #ifdef QT_OPENGL_ES
420     swizzleBGRAToRGBA(&m_image);
421 #endif
422
423     m_texture_size = image.size();
424     m_has_alpha = image.hasAlphaChannel();
425     m_dirty_texture = true;
426     m_dirty_bind_options = true;
427  }
428
429 int QSGPlainTexture::textureId() const
430 {
431     if (m_dirty_texture) {
432         if (m_image.isNull()) {
433             // The actual texture and id will be updated/deleted in a later bind()
434             // or ~QSGPlainTexture so just keep it minimal here.
435             return 0;
436         } else {
437             // Generate a texture id for use later and return it.
438             glGenTextures(1, &const_cast<QSGPlainTexture *>(this)->m_texture_id);
439             return m_texture_id;
440         }
441     }
442     return m_texture_id;
443 }
444
445 void QSGPlainTexture::setTextureId(int id)
446 {
447     if (m_texture_id && m_owns_texture)
448         glDeleteTextures(1, &m_texture_id);
449
450     m_texture_id = id;
451     m_dirty_texture = false;
452     m_dirty_bind_options = true;
453     m_image = QImage();
454     m_mipmaps_generated = false;
455 }
456
457 void QSGPlainTexture::setHasMipmaps(bool mm)
458 {
459     m_has_mipmaps = mm;
460     m_mipmaps_generated = false;
461 }
462
463
464 void QSGPlainTexture::bind()
465 {
466     if (!m_dirty_texture) {
467         glBindTexture(GL_TEXTURE_2D, m_texture_id);
468         if (m_has_mipmaps && !m_mipmaps_generated) {
469             QOpenGLContext *ctx = QOpenGLContext::currentContext();
470             ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D);
471             m_mipmaps_generated = true;
472         }
473         updateBindOptions(m_dirty_bind_options);
474         m_dirty_bind_options = false;
475         return;
476     }
477
478     m_dirty_texture = false;
479
480
481     if (m_image.isNull()) {
482         if (m_texture_id && m_owns_texture)
483             glDeleteTextures(1, &m_texture_id);
484         m_texture_id = 0;
485         m_texture_size = QSize();
486         m_has_mipmaps = false;
487         m_has_alpha = false;
488         return;
489     }
490
491     if (m_texture_id == 0)
492         glGenTextures(1, &m_texture_id);
493     glBindTexture(GL_TEXTURE_2D, m_texture_id);
494
495     // ### TODO: check for out-of-memory situations...
496     int w = m_image.width();
497     int h = m_image.height();
498
499 #ifdef QT_OPENGL_ES
500     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_image.constBits());
501 #else
502     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, m_image.constBits());
503 #endif
504
505     if (m_has_mipmaps) {
506         QOpenGLContext *ctx = QOpenGLContext::currentContext();
507         ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D);
508         m_mipmaps_generated = true;
509     }
510
511     m_texture_size = QSize(w, h);
512     m_texture_rect = QRectF(0, 0, 1, 1);
513
514     updateBindOptions(m_dirty_bind_options);
515     m_dirty_bind_options = false;
516 }
517
518
519 /*!
520     \class QSGDynamicTexture
521     \brief The QSGDynamicTexture class serves as a baseclass for dynamically changing textures,
522     such as content that is rendered to FBO's.
523
524     To update the content of the texture, call updateTexture() explicitly. Simply calling bind()
525     will not update the texture.
526  */
527
528
529 /*!
530     \fn bool QSGDynamicTexture::updateTexture()
531
532     Call this function to explicitely update the dynamic texture. Calling bind() will bind
533     the content that was previously updated.
534
535     The function returns true if the texture was changed as a resul of the update; otherwise
536     returns false.
537  */
538
539
540
541 QT_END_NAMESPACE