1 /****************************************************************************
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtDeclarative module of the Qt Toolkit.
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
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.
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.
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
40 ****************************************************************************/
42 #include "qsgshadereffectsource_p.h"
44 #include "qsgitem_p.h"
45 #include "qsgcanvas_p.h"
46 #include <private/qsgadaptationlayer_p.h>
47 #include <private/qsgrenderer_p.h>
49 #include "qglframebufferobject.h"
51 #include <private/qsgtexture_p.h>
55 DEFINE_BOOL_CONFIG_OPTION(qmlFboOverlay, QML_FBO_OVERLAY)
57 QSGShaderEffectTexture::QSGShaderEffectTexture(QSGItem *shaderSource)
61 , m_shaderSource(shaderSource)
64 , m_multisampledFbo(0)
65 #ifdef QSG_DEBUG_FBO_OVERLAY
69 , m_dirtyTexture(true)
70 , m_multisamplingSupportChecked(false)
71 , m_multisampling(false)
75 QSGShaderEffectTexture::~QSGShaderEffectTexture()
79 delete m_multisampledFbo;
80 #ifdef QSG_DEBUG_FBO_OVERLAY
81 delete m_debugOverlay;
86 int QSGShaderEffectTexture::textureId() const
88 return m_fbo->texture();
91 bool QSGShaderEffectTexture::hasAlphaChannel() const
93 return m_format != GL_RGB;
96 bool QSGShaderEffectTexture::hasMipmaps() const
98 return m_mipmapFiltering;
102 void QSGShaderEffectTexture::bind()
104 glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
108 bool QSGShaderEffectTexture::updateTexture()
110 if (m_dirtyTexture) {
117 void QSGShaderEffectTexture::setHasMipmaps(QSGTexture::Filtering filtering)
119 if (filtering == m_mipmapFiltering)
121 m_mipmapFiltering = filtering;
122 if (filtering != None && m_fbo && !m_fbo->format().mipmap())
127 void QSGShaderEffectTexture::setItem(QSGNode *item)
135 void QSGShaderEffectTexture::setRect(const QRectF &rect)
143 void QSGShaderEffectTexture::setSize(const QSize &size)
151 void QSGShaderEffectTexture::setFormat(GLenum format)
153 if (format == m_format)
159 void QSGShaderEffectTexture::setLive(bool live)
167 void QSGShaderEffectTexture::markDirtyTexture()
170 m_dirtyTexture = true;
171 emit textureChanged();
175 void QSGShaderEffectTexture::grab()
178 QSGNode *root = m_item;
179 while (root->childCount() && root->type() != QSGNode::RootNodeType)
180 root = root->childAtIndex(0);
181 if (root->type() != QSGNode::RootNodeType)
184 if (m_size.isEmpty()) {
186 delete m_multisampledFbo;
187 m_multisampledFbo = m_fbo = 0;
191 QSGContext *context = QSGItemPrivate::get(m_shaderSource)->sceneGraphContext();
194 m_renderer = context->createRenderer();
195 connect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markDirtyTexture()));
197 m_renderer->setRootNode(static_cast<QSGRootNode *>(root));
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))
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;
209 if (m_multisampling) {
211 delete m_multisampledFbo;
212 QGLFramebufferObjectFormat format;
214 format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
215 format.setInternalTextureFormat(m_format);
216 format.setSamples(8);
217 m_multisampledFbo = new QGLFramebufferObject(m_size, format);
219 format.setAttachment(QGLFramebufferObject::NoAttachment);
220 format.setMipmap(m_mipmapFiltering);
221 format.setSamples(0);
222 m_fbo = new QGLFramebufferObject(m_size, format);
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);
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.
239 #ifdef QSG_DEBUG_FBO_OVERLAY
240 if (qmlFboOverlay()) {
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);
253 m_dirtyTexture = false;
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);
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);
266 m_renderer->renderScene(BindableFbo(m_fbo));
270 glBindTexture(GL_TEXTURE_2D, textureId());
271 ctx->functions()->glGenerateMipmap(GL_TEXTURE_2D);
274 root->markDirty(dirty | QSGNode::DirtyNodeAdded); // Force matrix, clip and render list update.
276 #ifdef QSG_DEBUG_FBO_OVERLAY
278 root->removeChildNode(m_debugOverlay);
283 QSGShaderEffectSource::QSGShaderEffectSource(QSGItem *parent)
285 , m_wrapMode(ClampToEdge)
287 , m_textureSize(0, 0)
290 , m_hideSource(false)
293 setFlag(ItemHasContents);
294 m_texture = new QSGShaderEffectTexture(this);
297 QSGShaderEffectSource::~QSGShaderEffectSource()
301 QSGItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource);
304 QSGShaderEffectSource::WrapMode QSGShaderEffectSource::wrapMode() const
309 void QSGShaderEffectSource::setWrapMode(WrapMode mode)
311 if (mode == m_wrapMode)
315 emit wrapModeChanged();
318 QSGItem *QSGShaderEffectSource::sourceItem() const
323 void QSGShaderEffectSource::setSourceItem(QSGItem *item)
325 if (item == m_sourceItem)
328 QSGItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource);
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);
339 QSGItemPrivate::get(m_sourceItem)->refFromEffectItem(m_hideSource);
342 emit sourceItemChanged();
345 QRectF QSGShaderEffectSource::sourceRect() const
350 void QSGShaderEffectSource::setSourceRect(const QRectF &rect)
352 if (rect == m_sourceRect)
356 emit sourceRectChanged();
359 QSize QSGShaderEffectSource::textureSize() const
361 return m_textureSize;
364 void QSGShaderEffectSource::setTextureSize(const QSize &size)
366 if (size == m_textureSize)
368 m_textureSize = size;
370 emit textureSizeChanged();
373 QSGShaderEffectSource::Format QSGShaderEffectSource::format() const
378 void QSGShaderEffectSource::setFormat(QSGShaderEffectSource::Format format)
380 if (format == m_format)
384 emit formatChanged();
387 bool QSGShaderEffectSource::live() const
392 void QSGShaderEffectSource::setLive(bool live)
401 bool QSGShaderEffectSource::hideSource() const
406 void QSGShaderEffectSource::setHideSource(bool hide)
408 if (hide == m_hideSource)
411 QSGItemPrivate::get(m_sourceItem)->refFromEffectItem(hide);
412 QSGItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource);
416 emit hideSourceChanged();
419 bool QSGShaderEffectSource::mipmap() const
424 void QSGShaderEffectSource::setMipmap(bool enabled)
426 if (enabled == m_mipmap)
428 printf("setting mipmap to: %d\n", enabled);
431 emit mipmapChanged();
434 void QSGShaderEffectSource::grab()
438 QSGCanvas *canvas = m_sourceItem->canvas();
441 QSGCanvasPrivate::get(canvas)->updateDirtyNodes();
442 QGLContext *glctx = const_cast<QGLContext *>(canvas->context());
443 glctx->makeCurrent();
444 qobject_cast<QSGShaderEffectTexture *>(m_texture)->grab();
447 static void get_wrap_mode(QSGShaderEffectSource::WrapMode mode, QSGTexture::WrapMode *hWrap, QSGTexture::WrapMode *vWrap)
450 case QSGShaderEffectSource::RepeatHorizontally:
451 *hWrap = QSGTexture::Repeat;
452 *vWrap = QSGTexture::ClampToEdge;
454 case QSGShaderEffectSource::RepeatVertically:
455 *vWrap = QSGTexture::Repeat;
456 *hWrap = QSGTexture::ClampToEdge;
458 case QSGShaderEffectSource::Repeat:
459 *hWrap = *vWrap = QSGTexture::Repeat;
467 QSGTexture *QSGShaderEffectSource::texture() const
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);
478 QSGNode *QSGShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
485 QSGImageNode *node = static_cast<QSGImageNode *>(oldNode);
487 node = QSGItemPrivate::get(this)->sceneGraphContext()->createImageNode();
488 node->setFlag(QSGNode::UsePreprocess, true);
489 node->setTexture(m_texture);
492 QSGShaderEffectTexture *tex = qobject_cast<QSGShaderEffectTexture *>(m_texture);
494 tex->setItem(QSGItemPrivate::get(m_sourceItem)->itemNode());
495 QRectF sourceRect = m_sourceRect.isEmpty()
496 ? QRectF(0, 0, m_sourceItem->width(), m_sourceItem->height())
498 tex->setRect(sourceRect);
499 QSize textureSize = m_textureSize.isEmpty()
500 ? QSize(qCeil(sourceRect.width()), qCeil(sourceRect.height()))
502 tex->setSize(textureSize);
503 tex->setLive(m_live);
504 tex->setFormat(GLenum(m_format));
506 QSGTexture::Filtering filtering = QSGItemPrivate::get(this)->smooth
508 : QSGTexture::Nearest;
509 QSGTexture::Filtering mmFiltering = m_mipmap ? filtering : QSGTexture::None;
510 tex->setHasMipmaps(mmFiltering);
511 node->setMipmapFiltering(mmFiltering);
512 node->setFiltering(filtering);
514 QSGTexture::WrapMode hWrap, vWrap;
515 get_wrap_mode(m_wrapMode, &hWrap, &vWrap);
517 node->setHorizontalWrapMode(hWrap);
518 node->setVerticalWrapMode(vWrap);
519 node->setTargetRect(QRectF(0, 0, width(), height()));
520 node->setSourceRect(QRectF(0, 1, 1, -1));