Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Free ShaderEffectSource FBOs when no longer needed.
This commit also fixes handling of texture provider deletion in
ShaderEffect.

Change-Id: Ib22a9308a35325972bc545cf29de11bd625b22b2
Reviewed-by: Gunnar Sletta <gunnar.sletta@nokia.com>
  • Loading branch information
Kim Motoyoshi Kalland authored and Qt by Nokia committed Mar 21, 2012
1 parent 9405264 commit 63f1fb2
Show file tree
Hide file tree
Showing 10 changed files with 230 additions and 83 deletions.
6 changes: 1 addition & 5 deletions src/quick/items/qquickcanvas.cpp
Expand Up @@ -327,6 +327,7 @@ void QQuickCanvasPrivate::init(QQuickCanvas *c)
rootItem = new QQuickRootItem;
QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(rootItem);
rootItemPrivate->canvas = q;
rootItemPrivate->canvasRefCount = 1;
rootItemPrivate->flags |= QQuickItem::ItemIsFocusScope;

// In the absence of a focus in event on some platforms assume the window will
Expand Down Expand Up @@ -800,11 +801,6 @@ QQuickCanvas::~QQuickCanvas()

d->windowManager->canvasDestroyed(this);

// ### should we change ~QQuickItem to handle this better?
// manually cleanup for the root item (item destructor only handles these when an item is parented)
QQuickItemPrivate *rootItemPrivate = QQuickItemPrivate::get(d->rootItem);
rootItemPrivate->removeFromDirtyList();

QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
delete d->incubationController; d->incubationController = 0;

Expand Down
148 changes: 108 additions & 40 deletions src/quick/items/qquickitem.cpp
Expand Up @@ -1824,10 +1824,13 @@ QQuickItem::~QQuickItem()

Q_D(QQuickItem);

if (d->canvasRefCount > 1)
d->canvasRefCount = 1; // Make sure canvas is set to null in next call to derefCanvas().
if (d->parentItem)
setParentItem(0);
else if (d->canvas && d->itemNodeInstance)
QQuickCanvasPrivate::get(d->canvas)->cleanup(d->itemNodeInstance); // cleanup root
else if (d->canvas)
d->derefCanvas();

// XXX todo - optimize
while (!d->childItems.isEmpty())
d->childItems.first()->setParentItem(0);
Expand Down Expand Up @@ -1950,19 +1953,25 @@ void QQuickItem::setParentItem(QQuickItem *parentItem)
QQuickCanvasPrivate::get(d->canvas)->parentlessItems.remove(this);
}

d->parentItem = parentItem;

QQuickCanvas *parentCanvas = parentItem?QQuickItemPrivate::get(parentItem)->canvas:0;
if (d->canvas != parentCanvas) {
QQuickItemPrivate::InitializationState initState;
initState.clear();
d->initCanvas(&initState, parentCanvas);
QQuickCanvas *oldParentCanvas = oldParentItem ? QQuickItemPrivate::get(oldParentItem)->canvas : 0;
QQuickCanvas *parentCanvas = parentItem ? QQuickItemPrivate::get(parentItem)->canvas : 0;
if (oldParentCanvas == parentCanvas) {
// Avoid freeing and reallocating resources if the canvas stays the same.
d->parentItem = parentItem;
} else {
if (oldParentCanvas)
d->derefCanvas();
d->parentItem = parentItem;
if (parentCanvas)
d->refCanvas(parentCanvas);
}

d->dirty(QQuickItemPrivate::ParentChanged);

if (d->parentItem)
QQuickItemPrivate::get(d->parentItem)->addChild(this);
else if (d->canvas)
QQuickCanvasPrivate::get(d->canvas)->parentlessItems.insert(this);

d->setEffectiveVisibleRecur(d->calcEffectiveVisible());
d->setEffectiveEnableRecur(0, d->calcEffectiveEnable());
Expand Down Expand Up @@ -2175,30 +2184,82 @@ QQuickItem *QQuickItemPrivate::InitializationState::getFocusScope(QQuickItem *it
return focusScope;
}

void QQuickItemPrivate::initCanvas(InitializationState *state, QQuickCanvas *c)
void QQuickItemPrivate::refCanvas(InitializationState *state, QQuickCanvas *c)
{
Q_Q(QQuickItem);
// An item needs a canvas if it is referenced by another item which has a canvas.
// Typically the item is referenced by a parent, but can also be referenced by a
// ShaderEffect or ShaderEffectSource. 'canvasRefCount' counts how many items with
// a canvas is referencing this item. When the reference count goes from zero to one,
// or one to zero, the canvas of this item is updated and propagated to the children.
// As long as the reference count stays above zero, the canvas is unchanged.
// refCanvas() increments the reference count.
// derefCanvas() decrements the reference count.

if (canvas) {
removeFromDirtyList();
QQuickCanvasPrivate *c = QQuickCanvasPrivate::get(canvas);
if (polishScheduled)
c->itemsToPolish.remove(q);
if (c->mouseGrabberItem == q)
c->mouseGrabberItem = 0;
if ( hoverEnabled )
c->hoverItems.removeAll(q);
if (itemNodeInstance)
c->cleanup(itemNodeInstance);
if (!parentItem)
c->parentlessItems.remove(q);
Q_Q(QQuickItem);
Q_ASSERT((canvas != 0) == (canvasRefCount > 0));
Q_ASSERT(c);
if (++canvasRefCount > 1) {
if (c != canvas)
qWarning("QQuickItem: Cannot use same item on different canvases at the same time.");
return; // Canvas already set.
}

Q_ASSERT(canvas == 0);
canvas = c;

if (canvas && polishScheduled)
if (polishScheduled)
QQuickCanvasPrivate::get(canvas)->itemsToPolish.insert(q);

InitializationState _dummy;
InitializationState *childState = state;

if (q->isFocusScope()) {
_dummy.clear(q);
childState = &_dummy;
}

if (!parentItem)
QQuickCanvasPrivate::get(canvas)->parentlessItems.insert(q);

for (int ii = 0; ii < childItems.count(); ++ii) {
QQuickItem *child = childItems.at(ii);
QQuickItemPrivate::get(child)->refCanvas(childState, c);
}

dirty(Canvas);

if (extra.isAllocated() && extra->screenAttached)
extra->screenAttached->canvasChanged(c);
itemChange(QQuickItem::ItemSceneChange, c);
}

void QQuickItemPrivate::derefCanvas()
{
Q_Q(QQuickItem);
Q_ASSERT((canvas != 0) == (canvasRefCount > 0));

if (!canvas)
return; // This can happen when destroying recursive shader effect sources.

if (--canvasRefCount > 0)
return; // There are still other references, so don't set canvas to null yet.

q->releaseResources();
removeFromDirtyList();
QQuickCanvasPrivate *c = QQuickCanvasPrivate::get(canvas);
if (polishScheduled)
c->itemsToPolish.remove(q);
if (c->mouseGrabberItem == q)
c->mouseGrabberItem = 0;
if ( hoverEnabled )
c->hoverItems.removeAll(q);
if (itemNodeInstance)
c->cleanup(itemNodeInstance);
if (!parentItem)
c->parentlessItems.remove(q);

canvas = 0;

itemNodeInstance = 0;

if (extra.isAllocated()) {
Expand All @@ -2211,29 +2272,19 @@ void QQuickItemPrivate::initCanvas(InitializationState *state, QQuickCanvas *c)
groupNode = 0;
paintNode = 0;

InitializationState _dummy;
InitializationState *childState = state;

if (c && q->isFocusScope()) {
_dummy.clear(q);
childState = &_dummy;
}

if (!parentItem && canvas)
QQuickCanvasPrivate::get(canvas)->parentlessItems.insert(q);

for (int ii = 0; ii < childItems.count(); ++ii) {
QQuickItem *child = childItems.at(ii);
QQuickItemPrivate::get(child)->initCanvas(childState, c);
QQuickItemPrivate::get(child)->derefCanvas();
}

dirty(Canvas);

if (extra.isAllocated() && extra->screenAttached)
extra->screenAttached->canvasChanged(c);
itemChange(QQuickItem::ItemSceneChange, c);
extra->screenAttached->canvasChanged(0);
itemChange(QQuickItem::ItemSceneChange, (QQuickCanvas *)0);
}


/*!
Returns a transform that maps points from canvas space into item space.
*/
Expand Down Expand Up @@ -2346,7 +2397,7 @@ QQuickItemPrivate::QQuickItemPrivate()

dirtyAttributes(0), nextDirtyItem(0), prevDirtyItem(0),

canvas(0), parentItem(0), sortedChildItems(&childItems),
canvas(0), canvasRefCount(0), parentItem(0), sortedChildItems(&childItems),

subFocusItem(0),

Expand Down Expand Up @@ -2990,6 +3041,23 @@ QSGNode *QQuickItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
return 0;
}

/*!
This function is called when the item's scene graph resources are no longer needed.
It allows items to free its resources, for instance textures, that are not owned by scene graph
nodes. Note that scene graph nodes are managed by QQuickCanvas and should not be deleted by
this function. Scene graph resources are no longer needed when the parent is set to null and
the item is not used by any \l ShaderEffect or \l ShaderEffectSource.
This function is called from the main thread. Therefore, resources used by the scene graph
should not be deleted directly, but by calling \l QObject::deleteLater().
\note The item destructor still needs to free its scene graph resources if not already done.
*/

void QQuickItem::releaseResources()
{
}

QSGTransformNode *QQuickItemPrivate::createTransformNode()
{
return new QSGTransformNode;
Expand Down
1 change: 1 addition & 0 deletions src/quick/items/qquickitem.h
Expand Up @@ -395,6 +395,7 @@ public Q_SLOTS:
const QRectF &oldGeometry);

virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
virtual void releaseResources();
virtual void updatePolish();

protected Q_SLOTS:
Expand Down
13 changes: 12 additions & 1 deletion src/quick/items/qquickitem_p.h
Expand Up @@ -450,6 +450,7 @@ class Q_QUICK_EXPORT QQuickItemPrivate : public QObjectPrivate
QQuickItem**prevDirtyItem;

QQuickCanvas *canvas;
int canvasRefCount;
inline QSGContext *sceneGraphContext() const;

QQuickItem *parentItem;
Expand All @@ -472,7 +473,10 @@ class Q_QUICK_EXPORT QQuickItemPrivate : public QObjectPrivate
private:
QQuickItem *focusScope;
};
void initCanvas(InitializationState *, QQuickCanvas *);

void refCanvas(QQuickCanvas *);
void refCanvas(InitializationState *, QQuickCanvas *);
void derefCanvas();

QQuickItem *subFocusItem;
void updateSubFocusItem(QQuickItem *scope, bool focus);
Expand Down Expand Up @@ -857,6 +861,13 @@ QQuickItem::TransformOrigin QQuickItemPrivate::origin() const
return extra.isAllocated()?extra->origin:QQuickItem::Center;
}

inline void QQuickItemPrivate::refCanvas(QQuickCanvas *c)
{
QQuickItemPrivate::InitializationState initState;
initState.clear();
refCanvas(&initState, c);
}

QSGTransformNode *QQuickItemPrivate::itemNode()
{
if (!itemNodeInstance) {
Expand Down
57 changes: 42 additions & 15 deletions src/quick/items/qquickshadereffect.cpp
Expand Up @@ -398,12 +398,27 @@ void QQuickShaderEffect::updateLogAndStatus(const QString &log, int status)
emit statusChanged();
}

void QQuickShaderEffect::sourceDestroyed(QObject *object)
{
for (int i = 0; i < m_sources.size(); ++i) {
SourceData &source = m_sources[i];
if (object == source.sourceObject)
source.sourceObject = 0;
}
}

void QQuickShaderEffect::setSource(const QVariant &var, int index)
{
Q_ASSERT(index >= 0 && index < m_sources.size());

SourceData &source = m_sources[index];

if (source.sourceObject) {
if (canvas())
QQuickItemPrivate::get(source.sourceObject)->derefCanvas();
disconnect(source.sourceObject, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*)));
}

source.sourceObject = 0;
if (var.isNull()) {
return;
Expand All @@ -425,16 +440,13 @@ void QQuickShaderEffect::setSource(const QVariant &var, int index)
source.sourceObject = item;

if (item) {
QQuickItemPrivate *d = QQuickItemPrivate::get(item);
// 'item' needs a canvas to get a scene graph node. It usually gets one through its
// parent, but if the source item is "inline" rather than a reference -- i.e.
// "property variant source: Image { }" instead of "property variant source: foo" -- it
// will not get a parent. In those cases, 'item' should get the canvas from 'this'.
if (!d->parentItem && canvas() && !d->canvas) {
QQuickItemPrivate::InitializationState initState;
initState.clear();
d->initCanvas(&initState, canvas());
}
if (canvas())
QQuickItemPrivate::get(item)->refCanvas(canvas());
connect(item, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*)));
}
}

Expand Down Expand Up @@ -512,6 +524,11 @@ void QQuickShaderEffect::reset()
for (int i = 0; i < m_sources.size(); ++i) {
const SourceData &source = m_sources.at(i);
delete source.mapper;
if (source.sourceObject) {
if (canvas())
QQuickItemPrivate::get(source.sourceObject)->derefCanvas();
disconnect(source.sourceObject, SIGNAL(destroyed(QObject*)), this, SLOT(sourceDestroyed(QObject*)));
}
}
m_sources.clear();
m_log.clear();
Expand Down Expand Up @@ -817,15 +834,22 @@ QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDa
}
for (int i = 0; i < oldTextures.size(); ++i) {
QSGTextureProvider *t = oldTextures.at(i).second;
if (t)
if (t) {
disconnect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
disconnect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
}
}
for (int i = 0; i < m_sources.size(); ++i) {
const SourceData &source = m_sources.at(i);
QSGTextureProvider *t = source.sourceObject ? source.sourceObject->textureProvider() : 0;
textures.append(qMakePair(source.name, t));
if (t)
connect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()), Qt::DirectConnection);
if (t) {
Q_ASSERT_X(t->thread() == QThread::currentThread(),
"QQuickShaderEffect::updatePaintNode",
"Texture provider must belong to the rendering thread");
connect(t, SIGNAL(textureChanged()), node, SLOT(markDirtyTexture()));
connect(t, SIGNAL(destroyed(QObject*)), node, SLOT(textureProviderDestroyed(QObject*)));
}
}
material->setUniforms(values);
material->setTextureProviders(textures);
Expand All @@ -840,12 +864,15 @@ void QQuickShaderEffect::itemChange(ItemChange change, const ItemChangeData &val
{
if (change == QQuickItem::ItemSceneChange) {
// See comment in QQuickShaderEffect::setSource().
for (int i = 0; i < m_sources.size(); ++i) {
QQuickItemPrivate *d = QQuickItemPrivate::get(m_sources.at(i).sourceObject);
if (!d->parentItem && value.canvas != d->canvas) {
QQuickItemPrivate::InitializationState initState;
initState.clear();
d->initCanvas(&initState, value.canvas);
if (value.canvas) {
for (int i = 0; i < m_sources.size(); ++i) {
if (m_sources.at(i).sourceObject)
QQuickItemPrivate::get(m_sources.at(i).sourceObject)->refCanvas(value.canvas);
}
} else {
for (int i = 0; i < m_sources.size(); ++i) {
if (m_sources.at(i).sourceObject)
QQuickItemPrivate::get(m_sources.at(i).sourceObject)->derefCanvas();
}
}
}
Expand Down

0 comments on commit 63f1fb2

Please sign in to comment.