summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKim Motoyoshi Kalland <kim.kalland@nokia.com>2010-11-23 13:20:20 +0100
committerKim Motoyoshi Kalland <kim.kalland@nokia.com>2010-11-25 13:12:42 +0100
commit9d33e7f2a093c87acbf2cb1d87971eebd1702b3c (patch)
tree89b8a06a5d71517843cc9d6ecabfc6829cf36fe4
parentc9d0b05de7688906009587f1a14a21f9026c6ca8 (diff)
Rewrote the shader effect item.
Sources are now rendered into FBOs in its own coordinate system instead of the effect's coordinate system. We can therefore have one FBO per source instead of one per source-effect pair.
-rw-r--r--src/effects/shadereffectitem.cpp675
-rw-r--r--src/effects/shadereffectitem.h182
-rw-r--r--src/graphicsitems/qxitem.cpp14
-rw-r--r--src/graphicsitems/qxitem_p.h1
-rw-r--r--src/qmlscene_global.cpp1
-rw-r--r--src/scenegraph/coreapi/renderer.cpp13
-rw-r--r--src/scenegraph/coreapi/renderer.h1
7 files changed, 673 insertions, 214 deletions
diff --git a/src/effects/shadereffectitem.cpp b/src/effects/shadereffectitem.cpp
index 8d802df..28a48db 100644
--- a/src/effects/shadereffectitem.cpp
+++ b/src/effects/shadereffectitem.cpp
@@ -48,7 +48,8 @@
#include "adaptationlayer.h"
#include <QtCore/qsignalmapper.h>
-
+#include <QtOpenGL/qglframebufferobject.h>
+#include <QtGui/qimagereader.h>
static const char qt_default_vertex_code[] =
"uniform highp mat4 qt_Matrix; \n"
@@ -109,18 +110,43 @@ void CustomShaderMaterialData::updateEffectState(Renderer *r, AbstractEffect *ne
Q_UNUSED(oldEffect);
Q_UNUSED(newEffect);
- GLint filtering = q->m_linear_filtering ? GL_LINEAR : GL_NEAREST;
for (int i = q->m_sources.size() - 1; i >= 0; --i) {
+ const ShaderEffectItem::SourceData &source = q->m_sources.at(i);
+ if (!source.source)
+ continue;
+
+ bool linear = source.source->filtering() == ShaderEffectSource::Linear;
+ ShaderEffectSource::FilterMode mipmap = source.source->mipmap();
+
+ GLint filtering = GL_NEAREST;
+ switch (mipmap) {
+ case ShaderEffectSource::Nearest:
+ filtering = linear ? GL_LINEAR_MIPMAP_NEAREST : GL_NEAREST_MIPMAP_NEAREST;
+ break;
+ case ShaderEffectSource::Linear:
+ filtering = linear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST_MIPMAP_LINEAR;
+ break;
+ default:
+ filtering = linear ? GL_LINEAR : GL_NEAREST;
+ break;
+ }
+
+ GLuint hwrap = source.source->horizontalWrap() == ShaderEffectSource::Repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE;
+ GLuint vwrap = source.source->verticalWrap() == ShaderEffectSource::Repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE;
+
r->glActiveTexture(GL_TEXTURE0 + i);
#if !defined(QT_OPENGL_ES_2)
glEnable(GL_TEXTURE_2D);
#endif
- if (q->m_sources.at(i).texture.isNull())
+ if (source.source->texture().isNull())
glBindTexture(GL_TEXTURE_2D, 0);
else
- q->m_sources.at(i).texture->bind();
+ source.source->texture()->bind();
+
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, hwrap);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, vwrap);
}
if (q->m_respects_opacity)
@@ -163,9 +189,6 @@ void CustomShaderMaterialData::updateEffectState(Renderer *r, AbstractEffect *ne
q->m_program.setUniformValue(name.constData(), qvariant_cast<QVector3D>(v));
break;
default:
- // QObjects are probably mapped to sampler2Ds which are handled above.
- if (!qVariantCanConvert<QObject *>(v))
- qWarning("ShaderEffectItem: unhandled uniform value for '%s': type=%d, typeName=%s\n", name.constData(), v.type(), v.typeName());
break;
}
}
@@ -179,28 +202,389 @@ const QGL::VertexAttribute *CustomShaderMaterialData::requiredFields() const
}
+ShaderEffectSource::ShaderEffectSource(QObject *parent)
+ : QObject(parent)
+ , m_sourceItem(0)
+ , m_mipmap(None)
+ , m_filtering(Nearest)
+ , m_horizontalWrap(ClampToEdge)
+ , m_verticalWrap(ClampToEdge)
+ , m_margins(0, 0)
+ , m_size(0, 0)
+ , m_static(false)
+ , m_fbo(0)
+ , m_renderer(0)
+ , m_refs(0)
+ , m_dirtyTexture(true)
+ , m_dirtySceneGraph(true)
+{
+}
+
+ShaderEffectSource::~ShaderEffectSource()
+{
+ if (m_refs && m_sourceItem)
+ QxItemPrivate::get(m_sourceItem)->derefFromEffectItem();
+ delete m_fbo;
+ delete m_renderer;
+}
+
+void ShaderEffectSource::setSourceItem(QxItem *item)
+{
+ if (item == m_sourceItem)
+ return;
+
+ if (m_sourceItem) {
+ disconnect(m_sourceItem, SIGNAL(widthChanged()), this, SLOT(markSourceSizeDirty()));
+ disconnect(m_sourceItem, SIGNAL(heightChanged()), this, SLOT(markSourceSizeDirty()));
+ if (m_refs) {
+ Q_ASSERT(m_renderer);
+ QxItemPrivate *d = QxItemPrivate::get(m_sourceItem);
+ d->derefFromEffectItem();
+ m_renderer->setRootNode(0);
+ }
+ }
+
+ m_sourceItem = item;
+
+ if (m_sourceItem && !m_renderer) {
+ m_renderer = qt_adaptation_layer()->createRenderer();
+ connect(m_renderer, SIGNAL(sceneGraphChanged()), this, SLOT(markSceneGraphDirty()));
+ }
+
+ if (m_sourceItem) {
+ if (m_refs) {
+ Q_ASSERT(m_renderer);
+ QxItemPrivate *d = QxItemPrivate::get(m_sourceItem);
+ d->refFromEffectItem();
+ m_renderer->setRootNode(d->rootNode);
+ }
+ connect(m_sourceItem, SIGNAL(widthChanged()), this, SLOT(markSourceSizeDirty()));
+ connect(m_sourceItem, SIGNAL(heightChanged()), this, SLOT(markSourceSizeDirty()));
+ }
+
+ updateSizeAndTexture();
+ emit sourceItemChanged();
+ emit repaintRequired();
+}
+
+void ShaderEffectSource::setSourceImage(const QUrl &url)
+{
+ if (url == m_sourceImage)
+ return;
+ m_sourceImage = url;
+ updateSizeAndTexture();
+ emit sourceImageChanged();
+ emit repaintRequired();
+}
+
+void ShaderEffectSource::setMipmap(FilterMode mode)
+{
+ if (mode == m_mipmap)
+ return;
+ m_mipmap = mode;
+ emit mipmapChanged();
+ emit repaintRequired();
+}
+
+void ShaderEffectSource::setFiltering(FilterMode mode)
+{
+ if (mode == m_filtering)
+ return;
+ m_filtering = mode;
+ emit filteringChanged();
+ emit repaintRequired();
+}
+
+void ShaderEffectSource::setHorizontalWrap(WrapMode mode)
+{
+ if (mode == m_horizontalWrap)
+ return;
+ m_horizontalWrap = mode;
+ emit horizontalWrapChanged();
+ emit repaintRequired();
+}
+
+void ShaderEffectSource::setVerticalWrap(WrapMode mode)
+{
+ if (mode == m_verticalWrap)
+ return;
+ m_verticalWrap = mode;
+ emit verticalWrapChanged();
+ emit repaintRequired();
+}
+
+void ShaderEffectSource::setMargins(const QSize &size)
+{
+ if (size == m_margins)
+ return;
+ m_margins = size;
+ updateSizeAndTexture();
+ emit marginsChanged();
+ emit repaintRequired();
+}
+
+void ShaderEffectSource::setTextureSize(const QSize &size)
+{
+ if (size == m_textureSize)
+ return;
+ m_textureSize = size;
+ updateSizeAndTexture();
+ emit textureSizeChanged();
+ emit repaintRequired();
+}
+
+void ShaderEffectSource::setStatic(bool s)
+{
+ if (s == m_static)
+ return;
+ m_static = s;
+ emit staticChanged();
+ emit repaintRequired();
+}
+
+void ShaderEffectSource::refFromEffectItem()
+{
+ if (m_refs++ == 0) {
+ if (m_sourceItem) {
+ Q_ASSERT(m_renderer);
+ QxItemPrivate *d = QxItemPrivate::get(m_sourceItem);
+ d->refFromEffectItem();
+ m_renderer->setRootNode(d->rootNode);
+ }
+ emit activeChanged();
+ }
+}
+
+void ShaderEffectSource::derefFromEffectItem()
+{
+ if (--m_refs == 0) {
+ if (m_sourceItem) {
+ Q_ASSERT(m_renderer);
+ m_renderer->setRootNode(0);
+ QxItemPrivate *d = QxItemPrivate::get(m_sourceItem);
+ d->derefFromEffectItem();
+ }
+ emit activeChanged();
+ }
+ Q_ASSERT(m_refs >= 0);
+}
+
+void ShaderEffectSource::update()
+{
+ Q_ASSERT(m_refs);
+ if (!m_dirtyTexture && (!m_dirtySceneGraph || m_static))
+ return;
+ if (m_sourceItem) {
+ QxItemPrivate *src = QxItemPrivate::get(m_sourceItem);
+ Q_ASSERT(src->rootNode && src->rootNode == m_renderer->rootNode());
+ Q_ASSERT(m_renderer);
+
+ if (!m_fbo) {
+ // TODO: Implement support for multisampling.
+ m_fbo = new QGLFramebufferObject(m_size, QGLFramebufferObject::CombinedDepthStencil);
+ m_texture = QGLTexture2DPtr(QGLTexture2D::fromTextureId(m_fbo->texture(), m_fbo->size()));
+ }
+ Q_ASSERT(m_size == m_fbo->size());
+
+ QRectF r(0, 0, m_sourceItem->width(), m_sourceItem->height());
+ r.adjust(-m_margins.width(), -m_margins.height(), m_margins.width(), m_margins.height());
+ m_renderer->setDeviceRect(m_size);
+ m_renderer->setProjectMatrixToRect(r);
+ m_renderer->setClearColor(Qt::transparent);
+
+ m_renderer->renderScene(BindableFbo(const_cast<QGLContext *>(QGLContext::currentContext()), m_fbo));
+ m_dirtySceneGraph = false;
+ }
+ m_dirtyTexture = false;
+ // TODO: Update mipmap.
+}
+
+void ShaderEffectSource::markSceneGraphDirty()
+{
+ m_dirtySceneGraph = true;
+ emit repaintRequired();
+}
+
+void ShaderEffectSource::markSourceSizeDirty()
+{
+ Q_ASSERT(m_sourceItem);
+ if (m_textureSize.isEmpty())
+ updateSizeAndTexture();
+ if (m_refs)
+ emit repaintRequired();
+}
+
+void ShaderEffectSource::updateSizeAndTexture()
+{
+ if (m_sourceItem) {
+ QSize size = m_textureSize;
+ if (size.isEmpty())
+ size = m_sourceItem->size().toSize() + 2 * m_margins;
+ if (size.width() < 1)
+ size.setWidth(1);
+ if (size.height() < 1)
+ size.setHeight(1);
+ if (m_fbo && m_fbo->size() != size) {
+ delete m_fbo;
+ m_fbo = 0;
+ m_texture.clear();
+ }
+ if (m_size.width() != size.width()) {
+ m_size.setWidth(size.width());
+ emit widthChanged();
+ }
+ if (m_size.height() != size.height()) {
+ m_size.setHeight(size.height());
+ emit heightChanged();
+ }
+ m_dirtyTexture = true;
+ } else {
+ if (m_fbo) {
+ delete m_fbo;
+ m_fbo = 0;
+ m_texture.clear();
+ }
+ if (!m_sourceImage.isEmpty()) {
+ m_texture = QGLTexture2DPtr(new QGLTexture2D);
+ m_texture->setBindOptions(QGLContext::InternalBindOption);
+
+ // TODO: Implement async loading and loading over network.
+ QImageReader reader(m_sourceImage.toLocalFile());
+ if (!m_textureSize.isEmpty())
+ reader.setScaledSize(m_textureSize);
+ QImage image = reader.read();
+ if (image.isNull())
+ qWarning() << reader.errorString();
+ m_texture->setImage(image.mirrored());
+ if (m_size.width() != image.width()) {
+ m_size.setWidth(image.width());
+ emit widthChanged();
+ }
+ if (m_size.height() != image.height()) {
+ m_size.setHeight(image.height());
+ emit heightChanged();
+ }
+ } else {
+ if (m_size.width() != 0) {
+ m_size.setWidth(0);
+ emit widthChanged();
+ }
+ if (m_size.height() != 0) {
+ m_size.setHeight(0);
+ emit heightChanged();
+ }
+ }
+ }
+}
+
+ShaderEffectNode::ShaderEffectNode()
+ : m_meshResolution(1, 1)
+{
+ setFlag(UsePreprocess);
+ setGeometry(Utilities::createTexturedRectGeometry(QRectF(0, 0, 1, 1), QSize(1, 1), QRectF(0, 1, 1, -1)));
+}
+
+void ShaderEffectNode::setRect(const QRectF &rect)
+{
+ setUpdateFlag(UpdateGeometry);
+ setBoundingRect(rect);
+}
+
+QRectF ShaderEffectNode::rect() const
+{
+ return boundingRect();
+}
+
+void ShaderEffectNode::setResolution(const QSize &res)
+{
+ setUpdateFlag(UpdateGeometry);
+ m_meshResolution = res;
+}
+
+QSize ShaderEffectNode::resolution() const
+{
+ return m_meshResolution;
+}
+
+void ShaderEffectNode::update(uint updateFlags)
+{
+ if (updateFlags & UpdateGeometry)
+ updateGeometry();
+}
+
+void ShaderEffectNode::updateGeometry()
+{
+ int vmesh = m_meshResolution.height();
+ int hmesh = m_meshResolution.width();
+
+ Geometry *g = geometry();
+ g->setVertexCount((vmesh + 1) * (hmesh + 1));
+ g->setIndexCount(vmesh * 2 * (hmesh + 2));
+
+ struct V { float x, y, tx, ty; };
+
+ V *vdata = (V *) g->vertexData();
+
+ QRectF dstRect = boundingRect();
+ QRectF srcRect(0, 1, 1, -1);
+ for (int iy = 0; iy <= vmesh; ++iy) {
+ float fy = iy / float(vmesh);
+ float y = float(dstRect.top()) + fy * float(dstRect.height());
+ float ty = float(srcRect.top()) + fy * float(srcRect.height());
+ for (int ix = 0; ix <= hmesh; ++ix) {
+ float fx = ix / float(hmesh);
+ vdata->x = float(dstRect.left()) + fx * float(dstRect.width());
+ vdata->y = y;
+ vdata->tx = float(srcRect.left()) + fx * float(srcRect.width());
+ vdata->ty = ty;
+ ++vdata;
+ }
+ }
+
+ quint16 *indices = (quint16 *)g->ushortIndexData();
+ int i = 0;
+ for (int iy = 0; iy < vmesh; ++iy) {
+ *(indices++) = i;
+ for (int ix = 0; ix <= hmesh; ++ix) {
+ *(indices++) = i++;
+ *(indices++) = i + hmesh;
+ }
+ *(indices++) = i + hmesh;
+ }
+ Q_ASSERT(indices == g->ushortIndexData() + g->indexCount());
+
+ markDirty(Node::DirtyGeometry);
+}
+
+void ShaderEffectNode::preprocess()
+{
+ ShaderEffectItem *item = static_cast<ShaderEffectItem *>(material());
+ Q_ASSERT(item);
+ item->preprocess();
+}
+
+
ShaderEffectItem::ShaderEffectItem(QxItem *parent)
: QxItem(parent)
- , m_data(0)
, m_mesh_resolution(1, 1)
- , m_linear_filtering(false)
, m_program_dirty(true)
- , m_geometry_dirty(true)
, m_active(true)
, m_respects_matrix(false)
, m_respects_opacity(false)
{
setFlags(Blending);
m_node.setMaterial(this);
- m_node.updateGeometryDescription(Utilities::getTexturedRectGeometryDescription(), GL_UNSIGNED_SHORT);
- Utilities::setupRectGeometry(m_node.geometry(), QRectF(0, 0, 1, 1), QSize(1, 1), QRectF(0, 1, 1, -1));
setPaintNode(&m_node);
- connect(&m_node, SIGNAL(textureCreated(uint, const QSize &, int)), this, SLOT(textureCreated(uint, const QSize &, int)));
- connect(&m_node, SIGNAL(aboutToRender()), this, SLOT(effectNodeAboutToRender()));
+}
+
+ShaderEffectItem::~ShaderEffectItem()
+{
+ reset();
}
void ShaderEffectItem::componentComplete()
{
+ m_node.setRect(QRectF(QPointF(), size()));
updateProperties();
QxItem::componentComplete();
}
@@ -221,7 +605,7 @@ void ShaderEffectItem::setFragmentShader(const QString &code)
return;
m_fragment_code = code;
if (isComponentComplete()) {
- markShaderProgramAsDirty();
+ reset();
updateProperties();
}
emit fragmentShaderChanged();
@@ -233,7 +617,7 @@ void ShaderEffectItem::setVertexShader(const QString &code)
return;
m_vertex_code = code;
if (isComponentComplete()) {
- markShaderProgramAsDirty();
+ reset();
updateProperties();
}
emit vertexShaderChanged();
@@ -255,11 +639,11 @@ void ShaderEffectItem::setActive(bool enable)
if (m_active) {
for (int i = 0; i < m_sources.size(); ++i) {
- if (!m_sources.at(i).item)
+ ShaderEffectSource *source = m_sources.at(i).source;
+ if (!source)
continue;
- QxItemPrivate *src = QxItemPrivate::get(m_sources.at(i).item);
- m_node.setSubtree(0, i);
- src->derefFromEffectItem();
+ disconnect(source, SIGNAL(repaintRequired()), this, SLOT(markDirty()));
+ source->derefFromEffectItem();
}
setPaintNode(0);
}
@@ -269,125 +653,123 @@ void ShaderEffectItem::setActive(bool enable)
if (m_active) {
setPaintNode(&m_node);
for (int i = 0; i < m_sources.size(); ++i) {
- if (!m_sources.at(i).item)
+ ShaderEffectSource *source = m_sources.at(i).source;
+ if (!source)
continue;
- QxItemPrivate *src = QxItemPrivate::get(m_sources.at(i).item);
- src->refFromEffectItem();
- m_node.setSubtree(src->rootNode, i);
+ source->refFromEffectItem();
+ connect(source, SIGNAL(repaintRequired()), this, SLOT(markDirty()));
}
}
emit activeChanged();
}
-void ShaderEffectItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
-{
- if (newGeometry.size() != oldGeometry.size()) {
- m_node.setSize(newGeometry.size().toSize());
- m_geometry_dirty = true;
- }
- QxItem::geometryChanged(newGeometry, oldGeometry);
-}
-
-void ShaderEffectItem::smoothChange(bool newSmooth, bool oldSmooth)
+void ShaderEffectItem::setMeshResolution(const QSize &size)
{
- m_linear_filtering = newSmooth;
- m_node.markDirty(Node::DirtyMaterial);
- QxItem::smoothChange(newSmooth, oldSmooth);
-}
-
-void ShaderEffectItem::transformChanged(const QTransform &newTransform, const QTransform &oldTransform) {
- updateTransform();
- QxItem::transformChanged(newTransform, oldTransform);
+ if (size == m_mesh_resolution)
+ return;
+ m_node.setResolution(size);
}
-void ShaderEffectItem::updateTransform()
+void ShaderEffectItem::preprocess()
{
for (int i = 0; i < m_sources.size(); ++i) {
- int depth = m_sources.at(i).depth;
- if (depth) {
- QTransform t;
- QxItem *current = this;
- for (int j = 0; current && j < depth; ++j) {
- t *= current->combinedTransform();
- current = current->parentItem();
- }
- m_node.setSubtreeMatrix(t.inverted(), i);
- } else {
- m_node.setSubtreeMatrix(QMatrix4x4(), i);
- }
+ ShaderEffectSource *source = m_sources.at(i).source;
+ if (source)
+ source->update();
}
}
-void ShaderEffectItem::textureCreated(uint id, const QSize &size, int index)
+void ShaderEffectItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
{
- Q_ASSERT(index >= 0 && index < m_sources.size());
- m_sources[index].texture = QGLTexture2DConstPtr(QGLTexture2D::fromTextureId(id, size));
- m_node.markDirty(Node::DirtyMaterial);
+ if (newGeometry.size() != oldGeometry.size())
+ m_node.setRect(QRectF(QPointF(), newGeometry.size()));
+ QxItem::geometryChanged(newGeometry, oldGeometry);
}
void ShaderEffectItem::changeSource(int index)
{
Q_ASSERT(index >= 0 && index < m_sources.size());
QVariant v = property(m_sources.at(index).name.constData());
- if (v.isValid()) {
- QxItem *item = qobject_cast<QxItem *>(qVariantValue<QObject *>(v));
- setSource(item, index);
- }
+ setSource(v, index);
+}
+
+void ShaderEffectItem::markDirty()
+{
+ m_node.markDirty(Node::DirtyMaterial);
}
-void ShaderEffectItem::setSource(QxItem *item, int index)
+void ShaderEffectItem::setSource(QVariant var, int index)
{
Q_ASSERT(index >= 0 && index < m_sources.size());
SourceData &source = m_sources[index];
- if (source.item == item)
- return;
-
- if (m_active && source.item) {
- QxItemPrivate *src = QxItemPrivate::get(source.item);
- m_node.setSubtree(0, index);
- src->derefFromEffectItem();
+ if (m_active && source.source) {
+ disconnect(source.source, SIGNAL(repaintRequired()), this, SLOT(markDirty()));
+ source.source->derefFromEffectItem();
}
- if (item) {
- source.depth = 0;
-
- // There are two supported relations between effects and sources:
- // - source's parent is effect's ancestor, but source itself is not:
- // - Effect { source: foo }; Item { id: foo }
- // - Item { Effect { source: foo } }; Item { id: foo }
- // - effect is parent of source:
- // - Effect { source: Item { } }
- // - Effect { source: foo; Item { id: foo } }
-
- if (item->parentItem() == 0)
- item->setParentItem(this);
+ enum SourceType { Url, Item, Source, Other };
+ SourceType sourceType = Other;
+ QObject *obj = 0;
+
+ if (!var.isValid()) {
+ sourceType = Source; // Causes source to be set to null.
+ } else if (var.type() == QVariant::Url || var.type() == QVariant::String) {
+ sourceType = Url;
+ } else if (var.type() == QMetaType::QObjectStar) {
+ obj = qVariantValue<QObject *>(var);
+ if (qobject_cast<QxItem *>(obj))
+ sourceType = Item;
+ else if (!obj || qobject_cast<ShaderEffectSource *>(obj)) // Interpret null as ShaderEffectSource.
+ sourceType = Source;
+ }
- QxItem *parent = this;
- while (parent && parent != item->parentItem()) {
- parent = parent->parentItem();
- ++source.depth;
- }
- if (!parent) {
- qWarning("ShaderEffectItem: The source needs to be a sibling or child of the effect.");
- item = 0;
+ switch (sourceType) {
+ case Url:
+ {
+ QUrl url = var.type() == QVariant::Url ? var.toUrl() : QUrl(var.toString());
+ if (source.ownedByEffect && !url.isEmpty() && source.source->sourceImage() == url)
+ break;
+ if (source.ownedByEffect)
+ delete source.source;
+ source.source = new ShaderEffectSource;
+ source.ownedByEffect = true;
+ source.source->setSourceImage(url);
}
+ break;
+ case Item:
+ if (source.ownedByEffect && source.source->sourceItem() == obj)
+ break;
+ if (source.ownedByEffect)
+ delete source.source;
+ source.source = new ShaderEffectSource;
+ source.ownedByEffect = true;
+ source.source->setSourceItem(static_cast<QxItem *>(obj));
+ break;
+ case Source:
+ if (obj == source.source)
+ break;
+ if (source.ownedByEffect)
+ delete source.source;
+ source.source = static_cast<ShaderEffectSource *>(obj);
+ source.ownedByEffect = false;
+ break;
+ default:
+ qWarning("Could not assign source of type '%s' to property '%s'.", var.typeName(), source.name.constData());
+ break;
}
- source.item = item;
-
- if (m_active && source.item) {
- QxItemPrivate *src = QxItemPrivate::get(source.item);
- src->refFromEffectItem();
- m_node.setSubtree(src->rootNode, index);
+ if (m_active && source.source) {
+ source.source->refFromEffectItem();
+ connect(source.source, SIGNAL(repaintRequired()), this, SLOT(markDirty()));
}
}
void ShaderEffectItem::disconnectPropertySignals()
{
- disconnect(this, 0, &m_node, SLOT(sceneGraphChanged()));
+ disconnect(this, 0, this, SLOT(markDirty()));
for (int i = 0; i < m_sources.size(); ++i) {
SourceData &source = m_sources[i];
disconnect(this, 0, source.mapper, 0);
@@ -406,7 +788,7 @@ void ShaderEffectItem::connectPropertySignals()
qWarning("ShaderEffectItem: property '%s' does not have notification method!", it->constData());
QByteArray signalName("2");
signalName.append(mp.notifySignal().signature());
- connect(this, signalName, &m_node, SLOT(sceneGraphChanged()));
+ connect(this, signalName, this, SLOT(markDirty()));
} else {
qWarning("ShaderEffectItem: '%s' does not have a matching property!", it->constData());
}
@@ -427,7 +809,7 @@ void ShaderEffectItem::connectPropertySignals()
}
}
-void ShaderEffectItem::markShaderProgramAsDirty()
+void ShaderEffectItem::reset()
{
disconnectPropertySignals();
@@ -435,8 +817,15 @@ void ShaderEffectItem::markShaderProgramAsDirty()
m_attributeNames.clear();
m_attributes.clear();
m_uniformNames.clear();
- for (int i = 0; i < m_sources.size(); ++i)
- delete m_sources.at(i).mapper;
+ for (int i = 0; i < m_sources.size(); ++i) {
+ const SourceData &source = m_sources.at(i);
+ if (m_active && source.source)
+ source.source->derefFromEffectItem();
+ delete source.mapper;
+ if (source.ownedByEffect)
+ delete source.source;
+ }
+
m_sources.clear();
m_node.markDirty(Node::DirtyMaterial);
@@ -454,15 +843,10 @@ void ShaderEffectItem::updateProperties()
lookThroughShaderCode(vertexCode);
lookThroughShaderCode(fragmentCode);
- m_node.setSubtreeCount(m_sources.size());
for (int i = 0; i < m_sources.size(); ++i) {
QVariant v = property(m_sources.at(i).name);
- if (v.isValid()) {
- // Property exists.
- QxItem *item = qobject_cast<QxItem *>(qVariantValue<QObject *>(v));
- setSource(item, i);
- }
+ setSource(v, i); // Property exists.
}
// Append an 'end of array' marker so that m_attributes.constData() can be returned in requiredFields().
@@ -544,81 +928,12 @@ void ShaderEffectItem::lookThroughShaderCode(const QString &code)
if (type == QLatin1String("sampler2D")) {
SourceData d;
d.mapper = new QSignalMapper;
- d.item = 0;
+ d.source = 0;
d.name = name.toLatin1();
- d.depth = 0;
+ d.ownedByEffect = false;
m_sources.append(d);
}
}
}
}
}
-
-void ShaderEffectItem::effectNodeAboutToRender()
-{
- updateTransform();
- if (m_geometry_dirty) {
- buildGeometry();
- m_geometry_dirty = false;
- }
-}
-
-void ShaderEffectItem::setMeshResolution(const QSize &size)
-{
- if (size == m_mesh_resolution)
- return;
- m_mesh_resolution = size;
- m_geometry_dirty = true;
-}
-
-void ShaderEffectItem::buildGeometry() {
-
- int vmesh = m_mesh_resolution.height() + 1;
- int hmesh = m_mesh_resolution.width() + 1;
-
- Geometry *g = m_node.geometry();
- g->setVertexCount(vmesh * hmesh);
- g->setIndexCount(m_mesh_resolution.height() * 2 * hmesh + 2 * m_mesh_resolution.height());
-
- struct VertexData {
- float x, y, tx, ty;
- };
- VertexData *vdata = (VertexData *) g->vertexData();
-
- float w = width();
- float h = height();
-
- QRectF srcRect = m_node.sourceRect();
- for (int iy = 0; iy < vmesh; ++iy) {
- float fy = iy / float(m_mesh_resolution.height());
- float ty = float(srcRect.top()) + fy * float(srcRect.height());
- float y = h * fy;
- for (int ix = 0; ix < hmesh; ++ix) {
- float fx = ix / float(m_mesh_resolution.width());
- vdata->tx = float(srcRect.left()) + fx * float(srcRect.width());
- vdata->x = w * fx;
- vdata->y = y;
- vdata->ty = ty;
- ++vdata;
- }
- }
-
- quint16 *indices = (quint16 *) g->ushortIndexData();
- int i = 0;
- for (int iy=0; iy<m_mesh_resolution.height(); ++iy) {
- quint16 i1 = iy * hmesh;
- quint16 i2 = i1 + hmesh;
- indices[i++] = i1;
- for (int ix=0; ix<hmesh; ++ix) {
- indices[i++] = i1 + ix;
- indices[i++] = i2 + ix;
- }
- indices[i] = indices[i-1];
- ++i;
- }
- Q_ASSERT(i == g->indexCount());
-
- m_node.markDirty(Node::DirtyGeometry);
-}
-
-
diff --git a/src/effects/shadereffectitem.h b/src/effects/shadereffectitem.h
index 37b944c..591bc0b 100644
--- a/src/effects/shadereffectitem.h
+++ b/src/effects/shadereffectitem.h
@@ -47,8 +47,153 @@
#include "material.h"
#include "adaptationinterfaces.h"
+#include <QtCore/qpointer.h>
+
class QSignalMapper;
class CustomShaderMaterialData;
+class QGLFramebufferObject;
+
+// TODO: Implement async loading and loading over network.
+// TODO: Implement support for multisampling.
+class ShaderEffectSource : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QxItem *sourceItem READ sourceItem WRITE setSourceItem NOTIFY sourceItemChanged);
+ Q_PROPERTY(QUrl sourceImage READ sourceImage WRITE setSourceImage NOTIFY sourceImageChanged);
+ Q_PROPERTY(FilterMode mipmap READ mipmap WRITE setMipmap NOTIFY mipmapChanged);
+ Q_PROPERTY(FilterMode filtering READ filtering WRITE setFiltering NOTIFY filteringChanged);
+ Q_PROPERTY(WrapMode horizontalWrap READ horizontalWrap WRITE setHorizontalWrap NOTIFY horizontalWrapChanged);
+ Q_PROPERTY(WrapMode verticalWrap READ verticalWrap WRITE setVerticalWrap NOTIFY verticalWrapChanged);
+ Q_PROPERTY(QSize margins READ margins WRITE setMargins NOTIFY marginsChanged);
+ Q_PROPERTY(QSize textureSize READ textureSize WRITE setTextureSize NOTIFY textureSizeChanged);
+ Q_PROPERTY(int width READ width NOTIFY widthChanged);
+ Q_PROPERTY(int height READ height NOTIFY heightChanged);
+ Q_PROPERTY(bool static READ isStatic WRITE setStatic NOTIFY staticChanged);
+ Q_PROPERTY(bool active READ isActive NOTIFY activeChanged);
+ Q_ENUMS(FilterMode);
+ Q_ENUMS(WrapMode);
+
+public:
+ enum FilterMode
+ {
+ None,
+ Nearest,
+ Linear
+ };
+
+ enum WrapMode
+ {
+ Repeat,
+ ClampToEdge
+ };
+
+ ShaderEffectSource(QObject *parent = 0);
+ virtual ~ShaderEffectSource();
+
+ QxItem *sourceItem() const { return m_sourceItem.data(); }
+ void setSourceItem(QxItem *item);
+
+ QUrl sourceImage() const { return m_sourceImage; }
+ void setSourceImage(const QUrl &url);
+
+ FilterMode mipmap() const { return m_mipmap; }
+ void setMipmap(FilterMode mode);
+
+ FilterMode filtering() const { return m_filtering; }
+ void setFiltering(FilterMode mode);
+
+ WrapMode horizontalWrap() const { return m_horizontalWrap; }
+ void setHorizontalWrap(WrapMode mode);
+
+ WrapMode verticalWrap() const { return m_verticalWrap; }
+ void setVerticalWrap(WrapMode mode);
+
+ QSize margins() const { return m_margins; }
+ void setMargins(const QSize &size);
+
+ QSize textureSize() const { return m_textureSize; }
+ void setTextureSize(const QSize &size);
+
+ int width() const { return m_size.width(); }
+ int height() const { return m_size.height(); }
+
+ bool isStatic() const { return m_static; }
+ void setStatic(bool s);
+
+ bool isActive() const { return m_refs; }
+
+ QGLTexture2DConstPtr texture() const { return qSharedPointerConstCast<const QGLTexture2D>(m_texture); }
+
+ void refFromEffectItem();
+ void derefFromEffectItem();
+ void update();
+
+Q_SIGNALS:
+ void sourceItemChanged();
+ void sourceImageChanged();
+ void mipmapChanged();
+ void filteringChanged();
+ void horizontalWrapChanged();
+ void verticalWrapChanged();
+ void marginsChanged();
+ void textureSizeChanged();
+ void widthChanged();
+ void heightChanged();
+ void staticChanged();
+ void activeChanged();
+
+ void repaintRequired();
+
+private Q_SLOTS:
+ void markSceneGraphDirty();
+ void markSourceSizeDirty();
+
+private:
+ void updateSizeAndTexture();
+
+ QPointer<QxItem> m_sourceItem;
+ QUrl m_sourceImage;
+ FilterMode m_mipmap;
+ FilterMode m_filtering;
+ WrapMode m_horizontalWrap;
+ WrapMode m_verticalWrap;
+ QSize m_margins;
+ QSize m_textureSize;
+ QSize m_size;
+ bool m_static;
+
+ QGLTexture2DPtr m_texture;
+ QGLFramebufferObject *m_fbo;
+ Renderer *m_renderer;
+ int m_refs;
+ uint m_dirtyTexture : 1; // Causes update no matter what.
+ uint m_dirtySceneGraph : 1; // Causes update if not static.
+};
+
+class ShaderEffectNode : public GeometryNode
+{
+public:
+ ShaderEffectNode();
+
+ void setRect(const QRectF &rect);
+ QRectF rect() const;
+
+ void setResolution(const QSize &res);
+ QSize resolution() const;
+
+ virtual void preprocess();
+ virtual void update(uint updateFlags);
+
+private:
+ enum UpdateFlag
+ {
+ UpdateGeometry = 0x01
+ };
+
+ void updateGeometry();
+
+ QSize m_meshResolution;
+};
class ShaderEffectItem : public QxItem, public AbstractEffect
{
@@ -57,11 +202,12 @@ class ShaderEffectItem : public QxItem, public AbstractEffect
Q_PROPERTY(QString vertexShader READ vertexShader WRITE setVertexShader NOTIFY vertexShaderChanged);
Q_PROPERTY(bool blending READ blending WRITE setBlending NOTIFY blendingChanged);
Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged);
- Q_PROPERTY(QSize margins READ margins WRITE setMargins NOTIFY marginsChanged);
Q_PROPERTY(QSize meshResolution READ meshResolution WRITE setMeshResolution NOTIFY meshResolutionChanged);
public:
ShaderEffectItem(QxItem *parent = 0);
+ ~ShaderEffectItem();
+
virtual void componentComplete();
virtual AbstractEffectType *type() const;
@@ -79,13 +225,12 @@ public:
bool active() const { return m_active; }
void setActive(bool enable);
- QSize margins() const { return m_node.margins(); }
- void setMargins(const QSize &margins) { m_node.setMargins(margins); }
-
- QSize meshResolution() const { return m_mesh_resolution; }
+ QSize meshResolution() const { return m_node.resolution(); }
void setMeshResolution(const QSize &size);
-signals:
+ void preprocess();
+
+Q_SIGNALS:
void fragmentShaderChanged();
void vertexShaderChanged();
void blendingChanged();
@@ -96,32 +241,24 @@ signals:
protected:
virtual void geometryChanged(const QRectF &newGeometry,
const QRectF &oldGeometry);
- virtual void smoothChange(bool newSmooth, bool oldSmooth);
- virtual void transformChanged(const QTransform &newTransform, const QTransform &oldTransform);
-private slots:
- void textureCreated(uint id, const QSize &size, int index);
- void effectNodeAboutToRender();
+private Q_SLOTS:
void changeSource(int index);
+ void markDirty();
private:
friend class CustomShaderMaterialData;
- void setSource(QxItem *item, int index);
+ void setSource(QVariant var, int index);
void disconnectPropertySignals();
void connectPropertySignals();
- void markShaderProgramAsDirty();
+ void reset();
void updateProperties();
void updateShaderProgram();
void lookThroughShaderCode(const QString &code);
- void buildGeometry();
- void updateTransform();
-
- EffectSubtreeNode m_node;
- CustomShaderMaterialData *m_data;
+ ShaderEffectNode m_node;
AbstractEffectType m_type;
-
QGLShaderProgram m_program;
QString m_fragment_code;
QString m_vertex_code;
@@ -133,17 +270,14 @@ private:
struct SourceData
{
QSignalMapper *mapper;
- QxItem *item;
+ QPointer<ShaderEffectSource> source;
QByteArray name;
- QGLTexture2DConstPtr texture;
- int depth;
+ bool ownedByEffect;
};
QVector<SourceData> m_sources;
- uint m_linear_filtering : 1;
uint m_program_dirty : 1;
- uint m_geometry_dirty : 1;
uint m_active : 1;
uint m_respects_matrix : 1;
diff --git a/src/graphicsitems/qxitem.cpp b/src/graphicsitems/qxitem.cpp
index eb05b0c..7d6ca35 100644
--- a/src/graphicsitems/qxitem.cpp
+++ b/src/graphicsitems/qxitem.cpp
@@ -1244,7 +1244,7 @@ void QxItem::setTransform(const QTransform &)
QTransform QxItem::combinedTransform() const
{
Q_D(const QxItem);
- return d->transformNode.matrix().toTransform();
+ return d->transform.toTransform();
}
void QxItem::stackBefore(QxItem *)
@@ -1517,12 +1517,12 @@ void QxItemPrivate::updateGeometry()
{
Q_Q(QxItem);
- const QMatrix4x4 old = transformNode.matrix();
-
QMatrix4x4 m = computeMatrixFromProperties();
- if (m != old) {
- transformNode.setMatrix(m);
- q->transformChanged(m.toTransform(), old.toTransform());
+ if (m != transform) {
+ transform = m;
+ if (!effectRefCount)
+ transformNode.setMatrix(m);
+ q->transformChanged(m.toTransform(), transform.toTransform());
}
}
@@ -1859,6 +1859,7 @@ void QxItemPrivate::refFromEffectItem()
rootNode = new RootNode;
rootNode->appendChildNode(&transformNode);
+ transformNode.setMatrix(QMatrix4x4());
}
++effectRefCount;
}
@@ -1873,6 +1874,7 @@ void QxItemPrivate::derefFromEffectItem()
rootNode->removeChildNode(&transformNode);
delete rootNode;
rootNode = 0;
+ transformNode.setMatrix(transform);
if (opacity != 0 && visible)
attachTransformNode();
diff --git a/src/graphicsitems/qxitem_p.h b/src/graphicsitems/qxitem_p.h
index e54a6f7..ead8ca0 100644
--- a/src/graphicsitems/qxitem_p.h
+++ b/src/graphicsitems/qxitem_p.h
@@ -258,6 +258,7 @@ public:
void updateGeometry();
QxClipNode *clipNode;
TransformNode transformNode;
+ QMatrix4x4 transform;
Node groupNode;
Node *localNode;
diff --git a/src/qmlscene_global.cpp b/src/qmlscene_global.cpp
index 54b6d97..dea3cd9 100644
--- a/src/qmlscene_global.cpp
+++ b/src/qmlscene_global.cpp
@@ -177,6 +177,7 @@ void qt_scenegraph_register_types()
qmlRegisterType<QxParticleField>("Qt.particles",1,0,"ParticleField");
qmlRegisterType<ShaderEffectItem>("QtQuick", 2, 0, "ShaderEffectItem");
+ qmlRegisterType<ShaderEffectSource>("QtQuick", 2, 0, "ShaderEffectSource");
}
void qt_scenegraph_configure_engine(QDeclarativeEngine *engine)
diff --git a/src/scenegraph/coreapi/renderer.cpp b/src/scenegraph/coreapi/renderer.cpp
index 39818d4..f57fb11 100644
--- a/src/scenegraph/coreapi/renderer.cpp
+++ b/src/scenegraph/coreapi/renderer.cpp
@@ -122,11 +122,16 @@ void Renderer::renderScene(const Bindable &bindable)
void Renderer::setProjectMatrixToDeviceRect()
{
+ setProjectMatrixToRect(m_device_rect);
+}
+
+void Renderer::setProjectMatrixToRect(const QRectF &rect)
+{
QMatrix4x4 matrix;
- matrix.ortho(m_device_rect.x(),
- m_device_rect.x() + m_device_rect.width(),
- m_device_rect.y() + m_device_rect.height(),
- m_device_rect.y(),
+ matrix.ortho(rect.x(),
+ rect.x() + rect.width(),
+ rect.y() + rect.height(),
+ rect.y(),
qreal(0.01),
-1);
setProjectMatrix(matrix);
diff --git a/src/scenegraph/coreapi/renderer.h b/src/scenegraph/coreapi/renderer.h
index 78ee5cf..80fa771 100644
--- a/src/scenegraph/coreapi/renderer.h
+++ b/src/scenegraph/coreapi/renderer.h
@@ -114,6 +114,7 @@ public:
QMatrix4x4Stack &modelViewMatrix() { return m_modelViewMatrix; }
void setProjectMatrixToDeviceRect();
+ void setProjectMatrixToRect(const QRectF &rect);
void setProjectMatrix(const QMatrix4x4 &matrix) { m_projection_matrix = matrix; }
QMatrix4x4 projectMatrix() const { return m_projection_matrix; }