Crash when adding and removing entities in Qt3D - qt

I have entities that should be created and deleted at runtime. Entity have only material and mesh component. Material is QDiffuseMapMaterial with QTextureImage.
When entities begin to add and delete from scene, sometimes application crashes with this assert:
qt_assert: ASSERT: "img != nullptr" in file
../../include/Qt3DRender/5.8.0/Qt3DRender/private/../../../../../src/render/texture/apitexturemanager_p.h,
line 286
But unfortunately I can’t figure out why it happenes. I’m adding the entity this way:
QEntity * visibleObject = new QEntity();
QDiffuseMapMaterial * material = new QDiffuseMapMaterial();
QTextureImage * diffuseTextureImage = new QTextureImage();
diffuseTextureImage->setSource(QUrl("qrc:/resources/tile.png"));
material->diffuse()->addTextureImage(diffuseTextureImage);
visibleObject->addComponent(material);
// set mesh
QPlaneMesh * mesh = new QPlaneMesh();
mesh->setWidth(1.0 / 2);
mesh->setHeight(1.0 / 2);
visibleObject->addComponent(mesh);
visibleObject->setParent(_rootEntity);
And delete just like this:
delete visibleObject;
_rootEntity is scene root entity defined in cpp code. Scene defined in QML file using Scene3D:
Scene3D {
id: scene
anchors.fill: parent
aspects: ["render", "logic", "input"]
entity: rootEntity
}
And _rootEntity passed like this:
context->setContextProperty("rootEntity", _rootEntity);
If it is necessary I can prepare simple example that reproduce issue.
Thanks.

The line visibleObject->setParent(_rootEntity); means that _rootEntity takes ownership of visibleObject. If you delete visibleObject; you create a dangling pointer in _rootEntity. See http://doc.qt.io/qt-5/objecttrees.html. So don't delete visibleObject;

It seems your trouble is that you've defining how to delete things incorrectly, The best solution to any problem is to eliminate the problem entirely.
Have you attempted to use smart pointers? Specifically Shared pointers? Using smart pointers eliminates the issue of hanging references entirely, and there is little if any overhead associated with their use. Qt even has it's own dedicated implementation of smart pointers.
If you eliminate the necessity to explicitly specify the deletion of your objects, you wouldn't have issues specifying the deletion.

Related

Adding a high number of QML objects in a QWidget application using QQuickView or QQuickWidget poses performance problem

I'm developping a Qt application in which the user can add QML objects inside a QGraphicsScene. The available QML objects are listed and the user can add as many as he wants.
Until now, I used QQuickWidgets. The QGraphicsScene contains a top-level widget which is the parent of all the QQuickWidgets I create. It works fine, but I have a performance problem. With a high number of objects, the application starts to slow down, and takes too much space in RAM (more than 1.5 GB with the first example I created, containing 400 objects).
I thought it comes from the way QQuickWidgets are handled by Qt, and wanted to try another way, with QQuickViews. To do so I created a root view, converted in a QWidget so I can embed it in my view, which is a QWidget. Then I add a new QQuickView in the root view for each created object.
The creation of the root view, its container and the engine:
_rootView = new QQuickWindow();
_rootView->resize(1024, 720);
_rootView->show();
QWidget *container = QWidget::createWindowContainer(_rootView, this);
container->resize(_rootView->size());
container->setObjectName("TopLevelQmlViewWidget");
_layout->addWidget(container);
_engine = new QQmlEngine(_rootView);
The creation of the QQuickViews representing the objects:
QQuickView *view = new QQuickView(_engine, _rootView);
view->setSource(url);
view->setResizeMode(QQuickView::SizeViewToRootObject);
view->show();
It works, but the problem is that each QQuickView creates its own thread, which doesn't change the way I handle it but takes place in memory. I don't understand why, because I reparent them to the root view.
So my questions are the following :
1 - Is there a way to prevent the QQuickViews to create their own threads ?
2 - Is using QQuickViews, indeed, less memory-consuming than using QQuickWidgets ?
3 - If no, how can I handle adding a big number of QML objects in a QWidget view without consuming too much memory ?
I think using multiple QQuickViews is a bad idea. An application usually only needs one. I would take a look at QQmlComponent instead. Here is an example:
QQmlComponent component(_engine, QUrl::fromLocalFile("MyItem.qml"));
QQuickItem *childItem = qobject_cast<QQuickItem*>(component.create());
childItem->setParentItem(_rootView);
I'm by no means a QML expert. However, here are some pointers I can think of
Avoid mixing and matching QQuick: widget/View.
Consider creating objects dynamically
You can also make use of Loaders, but they have a small amount of extra overhead.
Consider using something like a stack/swipe view to minimize amount of loaded objects.
For best ROI, I'd first try implementing something like stack view and see how much it may help with the RAM. Then go on to create other objects dynamically as needed.
Finally I think QT has a tool that lets you see during runtime the amount of memory of the QML tree. You can look at that and see where your biggest memory hogs are.

How to debug error "Cannot read property of null"

In a big QML project, sometimes I get the following error :
MyItem.qml:8: TypeError: Cannot read property of null
When I look at the concerned line, it is always in a sub-Item, with a property binding to a parent property like
Item
{
id: myItem
ASubItem
{
id: subItem
width: parent.width
}
}
So it seems like the parent became null and the inner item is trying to update its property after that.
We sometimes delete the items from C++ because we want to create new ones, it seems to be the cause of the error messages. We also create some items from C++ (using QQmlComponent) and set the parent and parent-item to the QQuickItem that contains the items, but it seems that I get this error on other items as well.
But I don't understand why an inner item would try to update itself while the parent is null, shouldn't it be deleted at the same time ?
Is there any way to debug this to see when the parent is deleted and when its child-item is trying to update ?
a workaround that worked for me, however my case is a little bit different since I'm creating the objects dynamically from Javascript not c++ but I'm sure you can figure it out on how to do it from c++.
I passed the QGuiApplication instance to the root QML Context
engine.rootContext()->setContextProperty("KApp",&app);
and then made this connection
Connections{
target: KApp;
onAboutToQuit:{
dynamicallyCreatedObject.destroy();
}
}
so the point is, you have to destroy the dynamically created QML objects when the aboutToQuit() signal is emitted

Get pointer to registered field in the wizard

Can I get access to the widget of registred field in the QWizardPage?
QLineEdit* fld = new QLineEdit;
// register
registerField( "test", fld );
....
// Somewhere in wizard
QLineEdit* field = ?
You can assign a unique name to the widget, then find it in the wizard's children. Recall that widgets are QObjects. This technique applies to all QObjects and is not specific to the wizard, or even to widgets.
char const fieldName[] = "MyCoolField";
fld->setObjectName(fieldName);
...
QLineEdit * field = wizardPage->findChild<QLineEdit*>(fieldName);
if (field) {
...
}
It looks like QWizardPage isn't really designed to do this — which is why the field function just returns the widget's contents, not a pointer to it.
Looking at the source code for QWizardPage::registerField, getting to the fields is going to be nasty: they're stored in the QWizardPrivate class, which is used to isolate the private implementation of the QWizard class from its public interface (see Qt's D-Pointer write-up for more information about this approach).
I don't recommend trying to figure out how to actually expose the internals of QWizardPrivate to your own code. That's because the whole reason QWizardPrivate exists is to allow Qt to completely change the private data and implementation in a minor release, without affecting compiled code — it's a moving target.
The simplest solution is to store your own pointer to the QLineEdit externally, if at all possible. For example, if you're subclassing QWizardPage, try creating a "shadow" field map of your own.

crash after QGraphicsProxyWidget is removed from a scene

I have a simple scene with a QGraphicsProxyWidget added to it ( and some widget set on it ).
If I try removing it from the scene, like so:
if ( widget )
{
parentScene->removeItem( m_widget ); // m_widget is a QGraphicsProxyWidget
delete m_widget; // I also tried m_widget->deleteLater() here - same result
m_widget = NULL;
}
I get an instant crash - no descriptive callstack though that would tell me what's wrong ( some windows related calls in the callstack, and that's all ).
I'm using version 5.1.1 of the Qt library, I've searched the manual and the net for an instruction how to actually remove such a widget, and it's not documented at all, so I'm not sure if I'm doing something wrong there, or is there a bug in the library.
I'd appreciate any help.
Cheers,
Piotr
Check your if statement: It says widget when you probably meant m_widget. If widget is non-null and m_widget is null, your program will crash, since the calls to removeItem/delete will attempt to reference a null pointer.
I found the problem - seemingly unrelated call to 'prepareGeometryChange' was to blame here.
My widgets were changing size ( I didn't mention it, 'cause it seemed irrelevant at the time ). However I was calling the aforementioned method AFTER I made the change to the geometry, not before, as the manual instructs.
Apparently that method is very important in order to keep the scene's internal data in order, and due to my calling it incorrectly, it was leaving some invalid references to the deleted items.

QML and QtGStreamer within plugin

I'm trying to prepare QML plugin to play video on embedded device in a way that other developers can use it without much hassle. However the way it is currently proposed requires almost always writing some C++ wrapper around your QML application. I'm refering to this example:
http://gstreamer.freedesktop.org/data/doc/gstreamer/head/qt-gstreamer/html/examples_2qmlplayer_2main_8cpp-example.html
I would like to be able to have plugin and simply write:
import AwesomeVideoPlugin 1.0
Rect
{
AwesomeVideo
{
width: 320
height: 240
url: "./myvideo.avi"
// ... some minor stuff like mouse click handling, controls, etc.
}
}
Currently QtGStreamer requires to provide videoSurface property to VideoItem. The only way to do this is to create and set context for additional property in rootContext(). And to create GraphicsVideoSurface I need QGraphicsView (QDeclarativeView fills this role).
Is it possible to:
Get QDeclarativeView from within QDeclarativeItem (to which I have only access from QML plugin) in a way that it can be later used to feed GraphicsVideoSurface? My guess is no - however I've found path QFraphicsItem::scene() ==> QGraphScene ==> QGraphScene::views() ==> QList of QGraphicsView - it looks like VERY bad programming but maybe somebody got it to work (I'm getting segfault)
Is there other way to provide video sink for QtGStreamer from within QDeclarativeItem ?
Greetz
Yatsa
I had the same question, but haven't come up with an elegant solution.
However, one thought would be to make the videosurface available via an accessor function from a sub-classed QApplication object.
This would, of course, mean that your plug-in depends on the application subclass having a getVideoSurface method, but it does remove the ugliness from the QML code.
class MyApp : public QApplication
{
....
QGst::Ui::GraphicsVideoSurface *getVideoSurface() { return m_videosurface; }
}
...
int MyApp::init()
{
m_viewer = new QDeclarativeView();
m_viewer->setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers)));
m_videosurface = new QGst::Ui::GraphicsVideoSurface(m_viewer);
}
MyVideoPlugin::MyVideoPlugin(QDeclarativeItem *parent) : QDeclarativeItem(parent)
{
QGst::Ui::GraphicsVideoSurface *surface = ((MyApp*)qApp)->getVideoSurface();
}
...
Now the MyVideoPlugin element may be used without referencing an exported videosurface context item.

Resources