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
4 June 2026
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 has 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 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 can 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.
Remember to make instantiated 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