Initial import from qtquick2.
[profile/ivi/qtdeclarative.git] / src / declarative / items / qsgshadereffectsource.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 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qsgshadereffectsource_p.h"
43
44 #include "qsgitem_p.h"
45 #include "qsgcanvas_p.h"
46 #include <private/qsgadaptationlayer_p.h>
47 #include <private/qsgrenderer_p.h>
48
49 #include "qglframebufferobject.h"
50 #include "qmath.h"
51 #include <private/qsgtexture_p.h>
52
53 QT_BEGIN_NAMESPACE
54
55 DEFINE_BOOL_CONFIG_OPTION(qmlFboOverlay, QML_FBO_OVERLAY)
56
57 QSGShaderEffectTexture::QSGShaderEffectTexture(QSGItem *shaderSource)
58     : QSGDynamicTexture()
59     , m_item(0)
60     , m_format(GL_RGBA)
61     , m_shaderSource(shaderSource)
62     , m_renderer(0)
63     , m_fbo(0)
64     , m_multisampledFbo(0)
65 #ifdef QSG_DEBUG_FBO_OVERLAY
66     , m_debugOverlay(0)
67 #endif
68     , m_live(true)
69     , m_dirtyTexture(true)
70     , m_multisamplingSupportChecked(false)
71     , m_multisampling(false)
72 {
73 }
74
75 QSGShaderEffectTexture::~QSGShaderEffectTexture()
76 {
77     delete m_renderer;
78     delete m_fbo;
79     delete m_multisampledFbo;
80 #ifdef QSG_DEBUG_FBO_OVERLAY
81     delete m_debugOverlay;
82 #endif
83 }
84
85
86 int QSGShaderEffectTexture::textureId() const
87 {
88     return m_fbo->texture();
89 }
90
91 bool QSGShaderEffectTexture::hasAlphaChannel() const
92 {
93     return m_format != GL_RGB;
94 }
95
96 bool QSGShaderEffectTexture::hasMipmaps() const
97 {
98     return m_mipmapFiltering;
99 }
100
101
102 void QSGShaderEffectTexture::bind()
103 {
104     glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
105     updateBindOptions();
106 }
107
108 bool QSGShaderEffectTexture::updateTexture()
109 {
110     if (m_dirtyTexture) {
111         grab();
112         return true;
113     }
114     return false;
115 }
116
117 void QSGShaderEffectTexture::setHasMipmaps(QSGTexture::Filtering filtering)
118 {
119     if (filtering == m_mipmapFiltering)
120         return;
121     m_mipmapFiltering = filtering;
122     if (filtering != None && m_fbo && !m_fbo->format().mipmap())
123         markDirtyTexture();
124 }
125
126
127 void QSGShaderEffectTexture::setItem(QSGNode *item)
128 {
129     if (item == m_item)
130         return;
131     m_item = item;
132     markDirtyTexture();
133 }
134
135 void QSGShaderEffectTexture::setRect(const QRectF &rect)
136 {
137     if (rect == m_rect)
138         return;
139     m_rect = rect;
140     markDirtyTexture();
141 }
142
143 void QSGShaderEffectTexture::setSize(const QSize &size)
144 {
145     if (size == m_size)
146         return;
147     m_size = size;
148     markDirtyTexture();
149 }
150
151 void QSGShaderEffectTexture::setFormat(GLenum format)
152 {
153     if (format == m_format)
154         return;
155     m_format = format;
156     markDirtyTexture();
157 }
158
159 void QSGShaderEffectTexture::setLive(bool live)
160 {
161     if (live == m_live)
162         return;
163     m_live = live;
164     markDirtyTexture();
165 }
166
167 void QSGShaderEffectTexture::markDirtyTexture()
168 {
169     if (m_live) {
170         m_dirtyTexture = true;
171         emit textureChanged();
172     }
173 }
174
175 void QSGShaderEffectTexture::grab()
176 {
177     Q_ASSERT(m_item);
178     QSGNode *root = m_item;
179     while (root->childCount() && root->type() != QSGNode::RootNodeType)
180         root = root->childAtIndex(0);
181     if (root->type() != QSGNode::RootNodeType)
182         return;
183
184     if (m_size.isEmpty()) {
185         delete m_fbo;
186         delete m_multisampledFbo;
187         m_multisampledFbo = m_fbo = 0;
188         return;
189     }
190
191     QSGContext *context = QSGItemPrivate::get(m_shaderSource)->sceneGraphContext();
192
193     if (!m_renderer) {
194         m_renderer = context->createRenderer();
195         connect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markDirtyTexture()));
196     }
197     m_renderer->setRootNode(static_cast<QSGRootNode *>(root));
198
199     bool mipmap = m_mipmapFiltering != None;
200     if (!m_fbo || m_fbo->size() != m_size || m_fbo->format().internalTextureFormat() != m_format
201         || (!m_fbo->format().mipmap() && mipmap))
202     {
203         if (!m_multisamplingSupportChecked) {
204             QList<QByteArray> extensions = QByteArray((const char *)glGetString(GL_EXTENSIONS)).split(' ');
205             m_multisampling = extensions.contains("GL_EXT_framebuffer_multisample")
206                             && extensions.contains("GL_EXT_framebuffer_blit");
207             m_multisamplingSupportChecked = true;
208         }
209         if (m_multisampling) {
210             delete m_fbo;
211             delete m_multisampledFbo;
212             QGLFramebufferObjectFormat format;
213
214             format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
215             format.setInternalTextureFormat(m_format);
216             format.setSamples(8);
217             m_multisampledFbo = new QGLFramebufferObject(m_size, format);
218
219             format.setAttachment(QGLFramebufferObject::NoAttachment);
220             format.setMipmap(m_mipmapFiltering);
221             format.setSamples(0);
222             m_fbo = new QGLFramebufferObject(m_size, format);
223
224         } else {
225             delete m_fbo;
226             QGLFramebufferObjectFormat format;
227             format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
228             format.setInternalTextureFormat(m_format);
229             format.setMipmap(m_mipmapFiltering);
230             m_fbo = new QGLFramebufferObject(m_size, format);
231         }
232     }
233
234     // Render texture.
235     QSGNode::DirtyFlags dirty = root->dirtyFlags();
236     root->markDirty(QSGNode::DirtyNodeAdded); // Force matrix and clip update.
237     m_renderer->nodeChanged(root, QSGNode::DirtyNodeAdded); // Force render list update.
238
239 #ifdef QSG_DEBUG_FBO_OVERLAY
240     if (qmlFboOverlay()) {
241         if (!m_debugOverlay)
242             m_debugOverlay = context->createRectangleNode();
243         m_debugOverlay->setRect(QRectF(0, 0, m_size.width(), m_size.height()));
244         m_debugOverlay->setColor(QColor(0xff, 0x00, 0x80, 0x40));
245         m_debugOverlay->setPenColor(QColor());
246         m_debugOverlay->setPenWidth(0);
247         m_debugOverlay->setRadius(0);
248         m_debugOverlay->update();
249         root->appendChildNode(m_debugOverlay);
250     }
251 #endif
252
253     m_dirtyTexture = false;
254
255     const QGLContext *ctx = QGLContext::currentContext();
256     m_renderer->setDeviceRect(m_size);
257     m_renderer->setViewportRect(m_size);
258     m_renderer->setProjectMatrixToRect(m_rect);
259     m_renderer->setClearColor(Qt::transparent);
260
261     if (m_multisampling) {
262         m_renderer->renderScene(BindableFbo(m_multisampledFbo));
263         QRect r(0, 0, m_fbo->width(), m_fbo->height());
264         QGLFramebufferObject::blitFramebuffer(m_fbo, r, m_multisampledFbo, r);
265     } else {
266         m_renderer->renderScene(BindableFbo(m_fbo));
267     }
268
269     if (mipmap) {
270         glBindTexture(GL_TEXTURE_2D, textureId());
271         ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D);
272     }
273
274     root->markDirty(dirty | QSGNode::DirtyNodeAdded); // Force matrix, clip and render list update.
275
276 #ifdef QSG_DEBUG_FBO_OVERLAY
277     if (qmlFboOverlay())
278         root->removeChildNode(m_debugOverlay);
279 #endif
280 }
281
282
283 QSGShaderEffectSource::QSGShaderEffectSource(QSGItem *parent)
284     : QSGItem(parent)
285     , m_wrapMode(ClampToEdge)
286     , m_sourceItem(0)
287     , m_textureSize(0, 0)
288     , m_format(RGBA)
289     , m_live(true)
290     , m_hideSource(false)
291     , m_mipmap(false)
292 {
293     setFlag(ItemHasContents);
294     m_texture = new QSGShaderEffectTexture(this);
295 }
296
297 QSGShaderEffectSource::~QSGShaderEffectSource()
298 {
299     delete m_texture;
300     if (m_sourceItem)
301         QSGItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource);
302 }
303
304 QSGShaderEffectSource::WrapMode QSGShaderEffectSource::wrapMode() const
305 {
306     return m_wrapMode;
307 }
308
309 void QSGShaderEffectSource::setWrapMode(WrapMode mode)
310 {
311     if (mode == m_wrapMode)
312         return;
313     m_wrapMode = mode;
314     update();
315     emit wrapModeChanged();
316 }
317
318 QSGItem *QSGShaderEffectSource::sourceItem() const
319 {
320     return m_sourceItem;
321 }
322
323 void QSGShaderEffectSource::setSourceItem(QSGItem *item)
324 {
325     if (item == m_sourceItem)
326         return;
327     if (m_sourceItem)
328         QSGItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource);
329     m_sourceItem = item;
330     if (m_sourceItem) {
331         // TODO: Find better solution.
332         // 'm_sourceItem' needs a canvas to get a scenegraph node.
333         // The easiest way to make sure it gets a canvas is to
334         // make it a part of the same item tree as 'this'.
335         if (m_sourceItem->parentItem() == 0) {
336             m_sourceItem->setParentItem(this);
337             m_sourceItem->setVisible(false);
338         }
339         QSGItemPrivate::get(m_sourceItem)->refFromEffectItem(m_hideSource);
340     }
341     update();
342     emit sourceItemChanged();
343 }
344
345 QRectF QSGShaderEffectSource::sourceRect() const
346 {
347     return m_sourceRect;
348 }
349
350 void QSGShaderEffectSource::setSourceRect(const QRectF &rect)
351 {
352     if (rect == m_sourceRect)
353         return;
354     m_sourceRect = rect;
355     update();
356     emit sourceRectChanged();
357 }
358
359 QSize QSGShaderEffectSource::textureSize() const
360 {
361     return m_textureSize;
362 }
363
364 void QSGShaderEffectSource::setTextureSize(const QSize &size)
365 {
366     if (size == m_textureSize)
367         return;
368     m_textureSize = size;
369     update();
370     emit textureSizeChanged();
371 }
372
373 QSGShaderEffectSource::Format QSGShaderEffectSource::format() const
374 {
375     return m_format;
376 }
377
378 void QSGShaderEffectSource::setFormat(QSGShaderEffectSource::Format format)
379 {
380     if (format == m_format)
381         return;
382     m_format = format;
383     update();
384     emit formatChanged();
385 }
386
387 bool QSGShaderEffectSource::live() const
388 {
389     return m_live;
390 }
391
392 void QSGShaderEffectSource::setLive(bool live)
393 {
394     if (live == m_live)
395         return;
396     m_live = live;
397     update();
398     emit liveChanged();
399 }
400
401 bool QSGShaderEffectSource::hideSource() const
402 {
403     return m_hideSource;
404 }
405
406 void QSGShaderEffectSource::setHideSource(bool hide)
407 {
408     if (hide == m_hideSource)
409         return;
410     if (m_sourceItem) {
411         QSGItemPrivate::get(m_sourceItem)->refFromEffectItem(hide);
412         QSGItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource);
413     }
414     m_hideSource = hide;
415     update();
416     emit hideSourceChanged();
417 }
418
419 bool QSGShaderEffectSource::mipmap() const
420 {
421     return m_mipmap;
422 }
423
424 void QSGShaderEffectSource::setMipmap(bool enabled)
425 {
426     if (enabled == m_mipmap)
427         return;
428     printf("setting mipmap to: %d\n", enabled);
429     m_mipmap = enabled;
430     update();
431     emit mipmapChanged();
432 }
433
434 void QSGShaderEffectSource::grab()
435 {
436     if (!m_sourceItem)
437         return;
438     QSGCanvas *canvas = m_sourceItem->canvas();
439     if (!canvas)
440         return;
441     QSGCanvasPrivate::get(canvas)->updateDirtyNodes();
442     QGLContext *glctx = const_cast<QGLContext *>(canvas->context());
443     glctx->makeCurrent();
444     qobject_cast<QSGShaderEffectTexture *>(m_texture)->grab();
445 }
446
447 static void get_wrap_mode(QSGShaderEffectSource::WrapMode mode, QSGTexture::WrapMode *hWrap, QSGTexture::WrapMode *vWrap)
448 {
449     switch (mode) {
450     case QSGShaderEffectSource::RepeatHorizontally:
451         *hWrap = QSGTexture::Repeat;
452         *vWrap = QSGTexture::ClampToEdge;
453         break;
454     case QSGShaderEffectSource::RepeatVertically:
455         *vWrap = QSGTexture::Repeat;
456         *hWrap = QSGTexture::ClampToEdge;
457         break;
458     case QSGShaderEffectSource::Repeat:
459         *hWrap = *vWrap = QSGTexture::Repeat;
460         break;
461     default:
462         break;
463     }
464 }
465
466
467 QSGTexture *QSGShaderEffectSource::texture() const
468 {
469     m_texture->setMipmapFiltering(m_mipmap ? QSGTexture::Linear : QSGTexture::None);
470     m_texture->setFiltering(QSGItemPrivate::get(this)->smooth ? QSGTexture::Linear : QSGTexture::Nearest);
471     QSGTexture::WrapMode h, v;
472     get_wrap_mode(m_wrapMode, &h, &v);
473     m_texture->setHorizontalWrapMode(h);
474     m_texture->setVerticalWrapMode(v);
475     return m_texture;
476 }
477
478 QSGNode *QSGShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
479 {
480     if (!m_sourceItem) {
481         delete oldNode;
482         return 0;
483     }
484
485     QSGImageNode *node = static_cast<QSGImageNode *>(oldNode);
486     if (!node) {
487         node = QSGItemPrivate::get(this)->sceneGraphContext()->createImageNode();
488         node->setFlag(QSGNode::UsePreprocess, true);
489         node->setTexture(m_texture);
490     }
491
492     QSGShaderEffectTexture *tex = qobject_cast<QSGShaderEffectTexture *>(m_texture);
493
494     tex->setItem(QSGItemPrivate::get(m_sourceItem)->itemNode());
495     QRectF sourceRect = m_sourceRect.isEmpty()
496                       ? QRectF(0, 0, m_sourceItem->width(), m_sourceItem->height())
497                       : m_sourceRect;
498     tex->setRect(sourceRect);
499     QSize textureSize = m_textureSize.isEmpty()
500                       ? QSize(qCeil(sourceRect.width()), qCeil(sourceRect.height()))
501                       : m_textureSize;
502     tex->setSize(textureSize);
503     tex->setLive(m_live);
504     tex->setFormat(GLenum(m_format));
505
506     QSGTexture::Filtering filtering = QSGItemPrivate::get(this)->smooth
507                                             ? QSGTexture::Linear
508                                             : QSGTexture::Nearest;
509     QSGTexture::Filtering mmFiltering = m_mipmap ? filtering : QSGTexture::None;
510     tex->setHasMipmaps(mmFiltering);
511     node->setMipmapFiltering(mmFiltering);
512     node->setFiltering(filtering);
513
514     QSGTexture::WrapMode hWrap, vWrap;
515     get_wrap_mode(m_wrapMode, &hWrap, &vWrap);
516
517     node->setHorizontalWrapMode(hWrap);
518     node->setVerticalWrapMode(vWrap);
519     node->setTargetRect(QRectF(0, 0, width(), height()));
520     node->setSourceRect(QRectF(0, 1, 1, -1));
521     node->update();
522
523     return node;
524 }
525
526 QT_END_NAMESPACE