Sign up for the KDAB Newsletter
Stay on top of the latest news, publications, events and more.
Go to Sign-up


Find what you need - explore our website and developer resources
Frames skipped counter in application
How long does it take for an Item that you’ve just loaded to become visible? Answering this question allows for a way to detect what some users would perceive as "frame drops". I write that in quotes because Qt Quick only draws frames when needed, meaning it doesn't drop frames; but it can show them later than one would expect. That is what we would like to identify: When components are drawn late, and by how many milliseconds or frames are they late?
I've come up with a simple solution - code below the article - on how to measure this. Items being measured must inherit a class based of QQuickItem that has a connection on QQuickItem::visibleChanged. Its visible property should be false by default. When visible becomes true, a slot will start measuring elapsed time and create a direct connection to QQuickWindow::afterFrameEnd. Once the scene graph has submitted a frame, the slot will take the measurement and disconnect the connection that triggered it to prevent further frames from triggering this event.
That alone isn't enough, however. If there were other elements on the scene being animated (say from the render thread via an Animator), those would trigger a frame swap before our item would have had a chance to be drawn, causing our measurement to be taken prematurely.
We need a way of knowing when the frame that draws our component is the one that got swapped into view. Enter QQuickItem::ensurePolished. Calling this function ensures that QQuickItem::updatePolish will be called when the scene graph is ready to render our item. We override QQuickItem::updatePolish and use it to set a flag that’ll tell us that the next frame to come be displayed will correspond to the component we’re measuring. Lastly, we read this flag during the next call to QQuickWindow::afterFrameEnd, effectively using it to trigger the elapsed time measurement only when our item is swapped onto the screen.
There is a variable amount of time between the last user interaction and the moment a frame can be rendered; because of that, a measurement in milliseconds is only accurate to the average time that it takes for one frame to be rendered immediately after the previous frame. That turns out to be 1 second divided by the display's refresh rate. We can use Qscreen::refreshRate, which gives us this value in hertz. For a 60hz display, a frame's time (T) would be 1000 ms / 60 hz ≃ 16 ms. Any time measured that is between zero and T (16 ms) would mean an instant frame swap. If we divide the measured time by T, and apply a floor function to the result, we get the number of frames dropped while making the component visible, which is a more consistent measurement than the number of milliseconds passed. For a well optimized program the output would be zero, one, or a positive integer very close to that. For more information about the rendering process, you can read scene graph and rendering from Qt's documentation.
Make C++ items visible during their instantiation, or they won’t show up on screen. This QQuickItem subclass is different from its parent in that the Item is not visible by default. We set visible to false from the C++ constructor because the order in which initial properties are evaluated and assigned in QML differs depending on the approach used to instantiate the Item and assign its initial properties. You may set the initial visible property of an item in QML to false, then make it true during its instantiation as a delegate somewhere, only for the QML Engine to optimally evaluate its initial value solely to true, causing the visibleChanged signal to never be emitted because there was, effectively, no change to the visible property. Setting the visibility to false from the constructor in C++ is a simple way to guarantee that visibleChanged will be triggered upon any initialization of the visible property in QML.
The code for the QQuickItem subclass described in this article is documented below. Hope you find it useful. Reach out to us if you need help profiling software, would like to receive our training courses, or need help developing tools such as this.
Best regards, Javier
#include <QQuickItem>
#include <QQuickWindow>
class TimedItem : public QQuickItem
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(qint64 timeToDisplay READ timeToDisplay NOTIFY timeToDisplayChanged FINAL)
public:
TimedItem(QQuickItem* parent = nullptr) : QQuickItem(parent),
m_elapsedTimer(new QElapsedTimer())
{
setVisible(false);
// When made visible, measure time to display
QObject::connect(this, &QQuickItem::visibleChanged, this, &TimedItem::startMeasuringTimeToDisplay, Qt::DirectConnection);
};
qint64 timeToDisplay() {
return m_timeToDisplay;
};
signals:
void timeToDisplayChanged();
private:
void startMeasuringTimeToDisplay()
{
if (isVisible())
{
// Reset
m_frameReady = false;
// Attempt to take measurement after frame swaps
QObject::connect(window(), &QQuickWindow::afterFrameEnd, this, &TimedItem::measure,
static_cast<Qt::ConnectionType>(Qt::DirectConnection | Qt::UniqueConnection));
// Force polish, ensuring elapsed measurement is taken on the right frame
ensurePolished();
// Take initial measurement
m_elapsedTimer->start();
}
}
void updatePolish()
{
// The frame for this component will be rendered after this
m_frameReady = true;
}
void measure()
{
// This will be called for every frame until the right frame has been rendered
if (m_frameReady)
{
// Measure elapsed time
m_timeToDisplay = m_elapsedTimer->elapsed();
// Prevent measuring further frame
QObject::disconnect(window(), &QQuickWindow::afterFrameEnd, this, &TimedItem::measure);
// Propagate measured time
emit timeToDisplayChanged();
}
}
private:
qint64 m_timeToDisplay = 0;
QElapsedTimer *m_elapsedTimer;
bool m_frameReady = false;
}; About KDAB
The KDAB Group is a globally recognized provider for software consulting, development and training, specializing in embedded devices and complex cross-platform desktop applications. In addition to being leading experts in Qt, C++ and 3D technologies for over two decades, KDAB provides deep expertise across the stack, including Linux, Rust and modern UI frameworks. With 100+ employees from 20 countries and offices in Sweden, Germany, USA, France and UK, we serve clients around the world.
Stay on top of the latest news, publications, events and more.
Go to Sign-up
Upgrade your applications from Qt 5 to Qt 6 with KDAB’s migration services. Get a free migration assessment and join a hands-on workshop to prepare your team for a successful transition!
Learn more
Learn Modern C++
Our hands-on Modern C++ training courses are designed to quickly familiarize newcomers with the language. They also update professional C++ developers on the latest changes in the language and standard library introduced in recent C++ editions.
Learn more
Need help with performance issues?
Let the KDAB experts solve concrete performance problems, improve your software architecture or teach your team how to apply debugging and profiling tools to your developement process in the best way.
Get in touch
2 Comments
30 - Jun - 2026
David Boosalis
Javier
Thank you for providing this. At first blush I am not sure how one would use it for QML files. Perhaps one would make this the parent or wrapper class that encloses another page that is done in QML. What would be nice is to have an example of how to use it in a git repository.
Regards, David
30 - Jun - 2026
Javier Cordero
Hi David,
Thank you for writing! You could either make this the parent of a page or a part of its topmost component. I've done both in practice. Which approach works best may depend on whether this is added early to the software architecture or later on.
I'd prefer to show you an example of this being used to show the time to display pages, but I haven't had to do that in open source code yet... What I do have is a similar application, where I expanded the class with a method to measure frame skips during an animation: https://github.com/Cuperino/QPrompt-Teleprompter/compare/main...frame-drops-counter
This works there by placing the TimedItem next to an item that contains things that animate, thus producing a scenegraph update that refreshes the TimedItem. The order of placement and the z property play an important roll in ensuring updatePolish gets called when the new frames are produced.
However, this application is flawed because the animation might not need to produce a new frame every 1/60th of a second, which is the case for slow animations of scrolling text that uses Text.NativeRendering. The time in between frames, when no frame needed to be drawn, would then be counted, unfairly, as frame skips.
My goal with that code was to produce an image for this blog post and, in the process, make something useful. The code works fine for internal tests, since I can speed-up the animation and still get a useful measurement (because of the what this app is meant to do), or use Text.QtRendering for the text (tho I'd technically be measuring a different thing there).
If/when I make use of this in open source again, I'll be sure to leave a comment linking to that application of it.
Cheers, Javier