{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreidomtfjsezzvimu3rsbtasuc6jj7ylj6ikniphh5u36zb6dhn2rdu",
"uri": "at://did:plc:6dmfe46c76jjenq3kaxc5eds/app.bsky.feed.post/3mniplam42ji2"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreihv4wy6xydeg4pocujtu4g24as54evv5c3pi456qcd2mcfixwcqku"
},
"mimeType": "image/jpeg",
"size": 49372
},
"path": "/how-long-does-it-take-for-an-item-to-become-visible/",
"publishedAt": "2026-06-04T14:18:30.000Z",
"site": "https://www.kdab.com",
"tags": [
"QQuickItem::visibleChanged",
"QQuickWindow::afterFrameEnd",
"Animator",
"QQuickItem::ensurePolished",
"QQuickItem::updatePolish",
"Qscreen::refreshRate",
"scene graph and rendering",
"How long does it take for an Item to become visible?",
"KDAB"
],
"textContent": "# How long does it take for an Item to become visible?\n\nFrames skipped counter in application\n\nHow 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?\n\nI'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.\n\nThat 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.\nWe 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.\n\nThere 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.\n\nMake C++ items visible during their instantiation, or they won’t show up on screen. This `QQuickItem` derivative 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.\n\nThe code for the `QQuickItem` derivative 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.\n\nBest regards, Javier\n\n## Code\n\n\n #include <QQuickItem>\n #include <QQuickWindow>\n class TimedItem : public QQuickItem\n {\n Q_OBJECT\n QML_ELEMENT\n Q_PROPERTY(qint64 timeToDisplay READ timeToDisplay NOTIFY timeToDisplayChanged FINAL)\n public:\n TimedItem(QQuickItem* parent = nullptr) : QQuickItem(parent),\n m_elapsedTimer(new QElapsedTimer())\n {\n setVisible(false);\n // When made visible, measure time to display\n QObject::connect(this, &QQuickItem::visibleChanged, this, &TimedItem::startMeasuringTimeToDisplay, Qt::DirectConnection);\n };\n qint64 timeToDisplay() {\n return m_timeToDisplay;\n };\n signals:\n void timeToDisplayChanged();\n private:\n void startMeasuringTimeToDisplay()\n {\n if (isVisible())\n {\n // Reset\n m_frameReady = false;\n // Attempt to take measurement after frame swaps\n QObject::connect(window(), &QQuickWindow::afterFrameEnd, this, &TimedItem::measure,\n static_cast<Qt::ConnectionType>(Qt::DirectConnection | Qt::UniqueConnection));\n // Force polish, ensuring elapsed measurement is taken on the right frame\n ensurePolished();\n // Take initial measurement\n m_elapsedTimer->start();\n }\n }\n void updatePolish()\n {\n // The frame for this component will be rendered after this\n m_frameReady = true;\n }\n void measure()\n {\n // This will be called for every frame until the right frame has been rendered\n if (m_frameReady)\n {\n // Measure elapsed time\n m_timeToDisplay = m_elapsedTimer->elapsed();\n // Prevent measuring further frame\n QObject::disconnect(window(), &QQuickWindow::afterFrameEnd, this, &TimedItem::measure);\n // Propagate measured time\n emit timeToDisplayChanged();\n }\n }\n private:\n qint64 m_timeToDisplay = 0;\n QElapsedTimer *m_elapsedTimer;\n bool m_frameReady = false;\n };\n\nThe post How long does it take for an Item to become visible? appeared first on KDAB.",
"title": "How long does it take for an Item to become visible?"
}