Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance with no QNANO_USE_RENDERNODE #33

Open
miguelangelorenes opened this issue Oct 5, 2018 · 11 comments
Open

Performance with no QNANO_USE_RENDERNODE #33

miguelangelorenes opened this issue Oct 5, 2018 · 11 comments

Comments

@miguelangelorenes
Copy link

miguelangelorenes commented Oct 5, 2018

Hi,

We are making an application that is generating dinamically QNanoQuickItem Custom Items, all the items created are sharing the same parent.
The behavoiur is nice but we are getting some "issues" when overloading the number of items created.

We are adding qputenv("QSG_RENDERER_DEBUG", "render"); to check how is the render behaviour and we can see the number of "batches" is incrising each time we create a new QNanoQuickItem. And that is doing our render effect degrade the performance because of that.
As additional point, we have checked the same app but based on QQuickItem Custom Items, and in this case the number of "batches" is not incrised and not degrading the performance behaviour.

Is this the common behaviour and I have to assume it, or is there a way to change this behaviour?

@QUItCoding
Copy link
Owner

Qt Quick scene graph is able to "batch" some Qt Quick items into same draw calls. This only works for certain items and increases performance only when number of visible Qt Quick items grows bigger. QNanoQuickItems (QQuickFramebufferObject) likely can't be batched by scene graph. Just to make sure, tested QNanoPainter vs. QQuickPaintedItem and for both the amount of batches increases with the amount of items. So yes, this is expected behavior.

I don't know your exact use-case but if the amount of visible items is hundreds I think you should consider custom rendering those inside a single QNanoQuickItem instead, like e.g. in mouse_events example.

@miguelangelorenes
Copy link
Author

Thanks for the quick response.
The use-case is I need to create my items as separated items because of they can be modified one by one:

  • Change position
  • Rotate
  • Scale

I also was checking the possibility to use QNANO_USE_RENDERNODE but I stucked on rotate and scale actions.

Any thoughs?

@QUItCoding
Copy link
Owner

Sure custom items in QNanoQuickItem can have own position, rotation, scale etc. Just as an example here is small patch which would add those into mouse_events example. Looks like this with ~1000 items (gif doesn't do justice to quality & performance):

qnano_custom_items_test
qnano_mouse_events_rotate_and_scale_diff.txt

@miguelangelorenes
Copy link
Author

Hi again,
That looks like really promissing, I will try to check as soon as possible.
I will feedback to you, I do not want to make premature comments before seeing the patch in action ;).

@miguelangelorenes
Copy link
Author

Hi Again,
We are checking "mouse_event" example and our only one concern is the peformance when too many items on the m_items.
We can see all the mouse actions (press, move and release) are calling to update() that will provoke a call to EventItemPainter::paint where all the items are iterated and drawing each time.
Does that not degrade the performace when a lot of items on m_items? for example: lines, images, paths, shapes, texts, ...
On any case, we are going to explore this solution to see how it works.

@QUItCoding
Copy link
Owner

Would need to understand your requirements better... But if you have thousands of quite complicated items which all are not changing/animating then yes, you probably should not redraw all from scratch. Like you should not paint items which are outside current viewport. And you could for example cache all static items into FBO which is fast to render and just repaint changing items. Or you could use lod to reduce details when zoomed out and amount of items grows. But you should think these optimizations no matter are you using QNanoPainter, Qt Quick items or raw OpenGL. =)

@miguelangelorenes
Copy link
Author

yeah, you tips have been being really useful.
We were able to make the solution with QQuickItem more or less easily but QNano is more powerful and we are trying our best with the library because of QNano is offering us a way to no fight with OpenGL directives and graphic(we have not enough experience with them) stuffs.
Our goal is to have a Whiteboard 4K and offer to users an almost paper experience, as for example Ink is doing on Windows systems. We almost have it with QNano but our main problem is, with our current solution, the performance is degraded when the amount of items is big, and as I mentioned at the beginning of the thread, we could see the the number of Qt batches&node is increasing with each item.
We got that for Android by QQuickItem( after an strong work by playing with Qt paths, batches and nodes) on 1080p devices but that solution, although the performance is great, does not offer us stuffs like real-time anti-aliasing and smooth drawings. We discarded QQuickPaintedItem and QML Canvas for memory and performace matters.
So, at the moments, the scenario we are managing:

  1. An item for a drawing
  • Pro: speed when the number of items is low.
  • Contras: General degrade when the number of items is big, increase use of memory.
  1. Global item to manage all drawings
  • Pro: Speed when low charge of paths to draw, low use of memory.
  • Contras: too much time on "renderloop" when too much paths to draw.
  1. Next step, based on your suggestions:
  • 2 "layers", one for active/in-progress items, and other layer for persisted items. This could be a solution because of we do not need to be fast on movements, scale nor rotation actions.
  1. Next-next step:
  • We also found the cost to draw big paths are really expensive in terms of performance(checking "renderloop" timing) when the path is really big --> split current path on several paths, it was useful for QQuickItem solution. To be honest, the set of tools that Qt provide for Scene Graphic profiling is great.

Sorry for the extension of my answer, I hope now it is more clear, at least clear enough :)

@QUItCoding
Copy link
Owner

Thanks for explaining your needs! Yes, I think layering could be best approach for that sort of whiteboard/painting application. Please inform how you get it working and good luck!!

@NielsMayer
Copy link
Contributor

How can the "2 layers" implementation be done easily to handle background vs "active/in-progress items"? Got any examples? (Likewise examples or suggestions on use of QNanoPainter::save() and QNanoPainter::restore()).

I have a lot of background items that I cache as QRects instead of redrawing each time but since there can be hundreds-thousands of these, it is certainly a performance hit, especially if the number of "foreground" items become large.

Take, for example, what happens when I zoom-in on audio "waveforms" which "thins" progressively less data (or when zooming out the waveform is thinned). Note the glitching on the scrolling text carousel when that happens. Which is already somewhat "at the limit" since it's pulsing to the beat of the playback music :-)

https://www.youtube.com/watch?v=qEn-YUXBERc (have to jump around alot in the demos or you get a copyright strike from youtube :-) finally succeeded... )

@NielsMayer
Copy link
Contributor

Also, as a separate request @QUItCoding would it be possible to commit to 'master' those cool changes you did to the gallery app above in https://github.com/QUItCoding/qnanopainter/files/2452024/qnano_mouse_events_rotate_and_scale_diff.txt

The only change is to update to the current suppress-warnings "style" of last batch of updates is the following:
from examples/gallery/mouse_events/src/eventitem.cpp:

void EventItem::generateRandomItems()
{
    m_items.clear();
    int margin = int(width()*0.10f);
    int items = 2 + qrand()%1000;
    for (int i=0; i<items ; i++) {
        double w = 20 + qrand() % int(width()*0.05f);
        double h = 20 + qrand() % int(height()*0.05f);
        double x = qrand() % int(width()-w-margin*2) + margin;
        double y = qrand() % int(height()-h-margin*2) + margin;
        MyItem item;
        item.rect = QRectF(x, y, w, h);
        item.rotation = (float)(qrand()%360) * (3.14159/180);
        item.scale = 0.2 + (float)(qrand()%100) / 50.0;
        m_items << item;
    }
    update();
}

@QUItCoding
Copy link
Owner

@NielsMayer Nice! Quite a bit rectangles there so no wonder it needs to be tuned to perform!

For this specific case "2 layers" could mean making bottom graph a separate QNanoQuickItem as it seems to be static? Just take the seeking arrow to separate QML item on top of it. This item could have "layer.enabled: true" so it will be cached and not repainted until it changes.

For upper zoomable view it is important to not paint things which are out of currently visible area, probably you are doing that already. There also seems to be some LOD already in use (when zooming out enought lines graph disappears). Bars in graph currently have slightly rounded corners which increases vertices count, you could check how removing rounding & borders affects performance. Are the scrolling text and elapsed / duration on same QNanoPainter item or separate QML items? You could try separating them also.

Please keep us informed how your application progresses!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants