Where does QtQuick attach the buffers to the FBO? - qt

When using QQuickFramebufferObject, the QQuickFramebufferObject::Renderer is asked to create a FBO and also to output in this FBO (the content of the FBO being merged with the overall GUI by QtQuick behind the hood).
Now the Renderer is only asked to create the FBO (QOpenGLFramebufferObject), not the buffers themselves (Textures or RenderBuffers).
Where are these buffers attached to the FBO?

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.

QQuickWindow:renderTargetId() replacement in Qt 6.0?

QQuickWindow::renderTargetId() is removed in Qt 6.0, because underlying graphics API now may be different from OpenGL.
There is now QQuickRenderTarget which is abstract from graphics API, but I can't obtain FBO ID from it when I use OpenGL (I call QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL) in main()).
So I render my scene in my offscreen FBO, but when I want to render a result into the FBO, created by a QQuickWindow, which is not the default FBO with ID = 0 (QQuickWindow creates an FBO with ID = 1 right now), because my QQuickWindow is used by a QQuickWidget embedded into a QMainWindow (so I put Qt Quick and QML into a QWidget-based UI).
I may query the current set FBO ID via OpenGL API, of course, before I call glBindFramebuffer() to render into my own FBO, but I would like to read QQuickWindow's FBO ID using it's API, is there any way?

Efficiently getting from a QPaintDevice to a QSGTexture in QQuickItem

My custom QQuickItem currently does the following
Create a QSGNode that subclasses QSGSimpleTextureNode
In the nodes preprocess function, create a QOpenGLFramebufferObject to draw to
Draw on the QOpenGLFramebufferObject using a QPainter
Display the contents of the QOpenGLFramebufferObject as the node contents
The process I have for converting the FBO to a QSGTexture that I can set on the QSGSimpleTextureNode is the following.
QImage img = m_fbo->toImage();
QSGTexture* tex = m_window->createTextureFromImage(img, QQuickWindow::TextureCanUseAtlas);
setTexture(tex);
This seems very inefficient, and the app starts to get real framey even with relatively reasonable sized FBOs.
My questions are the following.
Is there a simpler way of getting an FBO into a QSGTexture?
Is there a better QPaintDevice compatible item that I should be using rather than a QOpenGLFramebufferObject?
Is there a better subclass I should be extending than QSGSimpleTextureNode to do what I am wanting to do?
Thanks!
1) For non multisample framebuffer objects a texture with the specified texture target is created. You can get the texture id for the texture attached to framebuffer object, using QOpenGLFramebufferObject::takeTexture(). And then
create a new QSGTexture object from an existing GL texture id:
QSize textureSize = m_fbo.size();
GLuint textureId = m_fbo.takeTexture();
QSGTexture* texture = window()->createTextureFromId(textureId, textureSize);
2, 3) The QQuickPaintedItem class provides a way to use the QPainter API in the QML Scene Graph.
The QQuickFramebufferObject class is a convenience class for integrating rendering using a framebuffer object (FBO) with Qt Quick.

Is there any easy way to copy QPainter state?

I want to implment push_group/pop_group of cairo with QPainter, but QPainter resets all its state while begin() with a new painterDevice, so I have to save/revert all state manually.
Yes, just check out QPainter::save() and QPainter::restore().
If you want to save/restore between the lifespan of multiple QPainters, you have to do it manually. You could just create a class PainterState that encapsulates the painter state (pen, brush, transform, etc.), and then store a QStack<PainterState>.
There is a QPainterState class, but it is for internal use only, and I think it's only for use with a single QPainter. See the source ("qpainter_p.h") if you're interested in the QPainterState members (too many to copy here).
When constructing the QPainter object, you can draw it to a QPicture. Then it can be reloaded when needed and painted out to the real QPaintDevice.
QPicture picture;
QPainter painterQueued;
painterQueued.begin(&picture); // paint in picture
painterQueued.drawEllipse(10,20, 80,70); // draw an ellipse
painterQueued.end(); // painting done
QImage myImage;
QPainter painterTarget;
painterTarget.begin(&myImage); // paint in myImage
painterTarget.drawPicture(0, 0, picture); // draw the picture at (0,0)
painterTarget.end(); // painting done
You could queue up many QPicture objects in a list, stack, etc, and replay them when needed.

How to associate a MFC DC with QPrinter?

I was just wondering if it is possible to use my existing MFC created DC to associate with a QPrinter so that it directly uses the existing DC rather creates its own with QPrintDialog?
Does QPrinter make use of Qt's backingstore as QWidget does, i.e., does it create an off-screen image before printing?
First thing:
QPrintDialog has nothing to do with your question.
You can set-up a QPrinter without using QPrintDialog.
Anyway: You paint into a QPainter where QPrinter is "only" the printing device. Printing only starts when you end the QPainter. So there is a backing store when using QPrinter.
You can't convert a DC to a QPainter so QPrinter can't do anything with a DC. The closest thing would be to get a Bitmap from the DC and print it to a fresh QPainter.

Resources