QMLBestPractices.qml 15.6 KB
Newer Older
Michael Winkelmann's avatar
Michael Winkelmann committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
import QtQuick 2.0

import "../Slides" 1.0
import "../common" 1.0

QtPresentation {
    id: root

    TitleSlide {
        SlideBackground {
            anchors.fill: parent
            z: -10
        }

        author: "Michael Winkelmann"
        email: "michael.winkelmann@qt.io"
        subtitle: "October 2018 - Based on Qt 5.11"
        title: "QML Optimizations"
        readonly property bool hideMaster: true
    }

    ContentSlide {
        title: "Contents"
        items: ["What can be optimized?",
            "Memory optimizations",
            "Load time optimizations",
            "Rendering optimization",
            "Debugging and profiling tools and techniques",
            "Dos and don'ts"
        ]
    }

    ContentSlide {
        title: "What can be optimized?"
        items: ["Optimizations for a QML application can be divided in 3 categories",
            "*Memory",
            "*Rendering",
            "*Loading time",
            "Optimizations affect eachother:",
            "*E.g. a loading time optimization might affect memory consumption or vice versa",
            "Most important thing for optimizing is: <b>Take measurements!</b>"]
    }

    TopicSlide {
        title: "Memory optimizations"
    }

    ContentSlide {
        title: "Memory optimizations"
        items: ["QML Engine uses Garbage Collector (GC)",
            "C++ objects exposed to QML can be GC'ed or mem-managed from C++ side",
            "Graphic assets can be compressed or lazy loaded",
            "*Images often consume most memory!"]
    }

    ContentSlide {
        title: "Memory management"
        items: [
            "Be aware of object ownership",
            "QObjects owned by QML garbage-collected",
            "QObjects owned by C++ deleted by parent or manually",
            "QObject handed from C++ to QML determined heuristically",
            "Use QQmlEngine::setObjectOwnership() for explicit control"]
    }

    ContentSlide {
        title: "Garbage collector"
        items: ["GC runs in the main thread, blocks application",
            "Implementation can be found in QV4::MemoryManager",
            "*Tracing GC, two phases: mark & sweep",
            "",
            "GC is triggered either through",
            "*an allocation, depending on GC heuristic and usage metrics",
            "*manually (from JS or C++)"]
    }

    ContentSlide {
        title: "Garbage collection of QObjects"
        items: ["A QObject is garbage-collected if:",
            "*it has QQmlEngine::JavaScriptOwnership",
            "*it has no parent",
            "*it has no remaining JavaScript references",
            "*it is not visible as QQuickItem"]
    }

    ContentSlide {
        title: "Manual garbage collection"
        items: [
            "gc() should not be called in general",
            "With a few exceptions:",
            "*The application is idle (and no one is looking)",
            "*after unloading a large/complex QML component",
            "",
            "Ensure to pass through the eventloop once before calling gc()",
            "Try to run malloc_trim(0) to encourage malloc to give memory back to the OS"]
    }

    TopicSlide {
        title: "Rendering optimizations"
    }

    ContentSlide {
        title: "Rendering optimizations"
        items: ["Hide unvisible elements",
            "Optimize render batches",
            "Write custom optimized scenegraph elements",
            "Optimize property bindings",
            "Font rendering"]
    }

    ContentSlide {
        title: "Optimizing Graphical Assets"
        items: [
            "Use <b>tinypng</b> to optimize size PNG image",
            "*This will only affect start up time performance!",
            "Combine several layers into one layer if possible",
            "Compress textures with ETC1, DXT1 or other algorithms if graphic hardware supports it",
            "This will reduce the overall memory consumption and most likely also decrease loading time",
            "*But it will affect visual quality",
            "*Use it where possible, disable it when it affect visual quality too much"]
    }

    ContentSlide {
        title: "Scene Graph Rendering Optimizations"
        itemFontSize: 16
        items: [
            "Scene graph tree is rendered by SG Renderer",
            "Capable of making full scene optimizations",
            "Separates opaque geometry from translucent geometry and renders opaque geometry first (to color and depth buffers) from front to back",
            "Translucent geometry is not rendered at all, if covered by opaque geometry (depth testing enabled)",
            "*Note! Renderer still takes these nodes into account and the vertex shader is run for each vertex => use visible/opacity = 0", "After opaque primitives, alpha blended primitives are drawn in back-to-front manner",
            "*May not be batchable, if overlaps with different material states"]
    }

    ContentSlide {
        title: "Renderer Optimizations - Batches"
        itemFontSize: 16
        items: [
            "Renderer tries to batch paint operations",
            "*To minimize the data sent to the GPU for each frame",
            "",
            "Batches are uploaded and stored in GPU memory using vertex buffer objects",
            "*Each QQuickItem inserts a transform node into the SG to manage its x, y, scale, rotation",
            "*A node, which changes between the frames and has a fairly complex sub-tree is a good candidate for the batch root",
            "*Geometry nodes in the sub-tree are pre-transformed in the CPU and uploaded and retained in the GPU",
            "**Fast list, grid scrolling, no need to upload unchanged parts of the UI",
            "*Beneath a batch root, one batch is created for each unique set of material state and geometry type",
            "",
            "The number of batches should be < 10, at least 3-4 opaque to minimize state changes",
            "*Use environment variable <b>QSG_RENDERER_DEBUG=render</b> to get statistics about batches", "*Use QSG_VISUALIZE=batches/clip/overdraw/changes to visualize the batches, clipping, and overdraws"]
    }

    ContentSlide {
        title: "Renderer Optimizations - Batches"
        itemFontSize: 16
        items: [
            "Clip node as a batch root node",
            "Qt Quick supports only rectangle clip, but the renderer supports any shape using the stencil buffer",
            "Clip sub-tree needs to be renderer with a unique GL state",
            "Batching of an item limited to its children",
            "Anti-aliasing",
            "*Vertex anti-aliasing requires each primitive to be blended",
            "*Possibly less batching, possibly heavy blending, if interior if large rectangle requires blending",
            "*Multisampling anti-aliasing may not be supported by the GPU, performance is HW dependent"]
    }

    ContentSlide {
        title: "More Rendering optimizations"
        items: [
            "Minimize batches",
            "Overlapping compound items cannot be batched",
            "QSGMaterial::RequiresFullMatrix prevents all batching",
            "Make items not visible, if they are not shown",
            "Use texture atlas",
            "Use opaque primitives",
            "Do not use clipping per-item basis",
            "Mipmapped images are not placed in global atlas, not batched",
            "QSG_RENDER_TIMING=1 outputs useful timing parameters"]
    }

    TopicSlide {
        title: "Load time optimizations"
    }

    ContentSlide {
        title: "Loading optimizations"
        items: [
            "Prioritizing Views: What does the user see first?",
            "Cheating by pre-loading: Start loading when car door is unlocked",
            "Use QML Loaders for complex Items and daisy chain them",
            "Use static linking if possible",
            "Optimize graphical assets"
        ]
    }

    ContentSlide {
        title: "QML File Selector"
        itemFontSize: 16
        items: [
            "QFileSelector for QML file loading",
            "Load specific files for custom selectors, env vars, target platform, locale name",
            "*+myselector/+linux/+en_GB",
            "*QQmlEngine engine;",
            "*QQmlFileSelector* selector = new QQmlFileSelector(&engine);",
            "Files:",
            "*main.qml",
            "*Component.qml",
            "*asset.png",
            "*+unix/Component.qml",
            "*+mac/asset.png"]
    }


    ContentSlide {
        title: "Qt Quick Compiler"
        items: [
            "Dev addon for Qt Quick Applications",
            "Compiles QML for resource files into intermediate C++",
            "instead of generating machine code on the fly (JIT)",
            "Speeds up the startup time, as there’s no need to compile QML files to byte code",
            "Speeds up the JS execution in the platforms, which do not use JIT (iOS, WinRT)",
            "QML files do not need to be deployed and shipped",
            "*Note that QML debugging does not work with QML compiler – so recommended to be used in release builds"
        ]
    }

    ContentSlide {
        title: "Qt Quick Compiler"
        items: [
            "Usage is simple. Enable by adding:",
            "*<b>CONFIG += qtquickcompiler</b> in qmake",
            "*or <b>find_package(Qt5QuickCompiler)</b> in cmake",
            "Add QML files to .qrc file and add .qrc to RESOURCES in .pro file",
            "For release builds, verify that application works without QML files",
            "Only use qtquickcompiler in release builds",
            "QML Cache not usable for readonly file systems",
            "Strictly tied to Qt version: Upgrade of Qt version requires update of compiler",
            "*Compiled code may not be compatible between Qt versions",
        ]
    }


    TopicSlide {
        title: "Debugging and profiling tools"
    }

    ContentSlide {
        title: "Benchmarking and profiling"
        itemFontSize: 18
        items: [
            "<b>Rule #1: Measure performance!</b>",
            "Find the worst consumers of time and try to optimize them first.",
            "You can waste a lot of time if you start optimizing minor spots first",
            "Write benchmarks using QTestLib and Q_BENCHMARK macro",
            "Track regressions using VCS and CI.",
            "Measure GPU limitations and take those into account when designing the UI.",
            "For pre-Qml and kernel related stuff, use serial connection and http://elinux.org/Grabserial",
            "Parsing for kernel log for timing is also very useful. Or systemd-analyze.",
            "Use profilers: strace, perf and QML profiler"]
    }

    ContentSlide {
        title: "QML Profiler"
        items: [
            "Profile different parts of the application",
            "*Compiling QML components",
            "*Creating objects",
            "*Binding properties",
            "*Painting",
            "*Rendering",
            "*Memory usage",
            "Visualization tools also help finding performance bottlenecks"]
    }

    ContentSlide {
        title: "Defines for debugging"
        items: [
            "Preprocessor macros:",
            "*V4_USE_VALGRIND",
            "*V4_USE_HEAPTRACK",
            "Environment variables:",
            "*QV4_MM_STATS"]
    }

    ContentSlide {
        title: "Useful environment variables"
        items: [
            "QSG_RENDER_TIMING=1",
            "QSG_VISUALIZE=[batches,clip,changes,overdraw]",
            "QV4_FORCE_INTERPRETER=1 better backtraces",
            "QML_IMPORT_TRACE=1 to check import problems",
            "QML_DISABLE_DISK_CACHE=1",
            "QML_LEAK_CHECK=1",
            "QML_FBO_OVERLAY",
            "grep DEFINE_BOOL_CONFIG_OPTION and getenv"]
    }

    ContentSlide {
        title: "valgrind/massif/massif-visualizer"
        items: [
            "Call:",
            "*<code>valgrind --tool=massif –pages-as-heap=yes</code>",
            "Shows heap allocations of a process",
            "QML objects are visible but not QML source itself",
            "Objects on JS heap are not visible",
            "Massif shows only what triggered the initial allocation, not what is currently stored!"]
    }

    TopicSlide {
        title: "Dos and Don'ts"
    }

    ContentSlide {
        title: "Dos"
        itemFontSize: 18
        items: [
            "Measure and analyze where time is spent",
            "Design your application to start fast from the beginning",
            "Set target goal and scope of the software as early as possible",
            "Try to reach the goal early in the dev stage and then keep the level throughout development",
            "When designing your software architecture take into account the startup targets",
            "Optimize most time consuming and simple parts first, then continue with the details",
            "Consider static linking if that provides better result in your SW&HW configuration",
            "Measure hardware limitations and take those into account when designing the UI",
        ]
    }

    ContentSlide {
        title: "Dos"
        itemFontSize: 18
        items: [
            "Use QML profiler",
            "Make startup animations to allow parallel loading",
            "Use chain loading. Run only as many loaders as you have cores in you CPU (two cores: two loaders running at the same time)",
            "First loaders should not be asynchronous. Trigger the rest of the loaders",
            "Create QML plugins that load when required",
            "Let the QML plugins start up non-critical services and close those down when not required anymore",
            "Optimize png/jpg images",
            "Optimize your 3d models by reducing the amount of vertices and removing parts that are not visible",
            "Render only of what is needed, avoid Rectangle"
        ]
    }

    ContentSlide {
        title: "Dos"
        itemFontSize: 18
        items: [
            "Optimize the 3D model loading by using glTF",
            "Use Quick Controls 2.0. They have been designed for embedded use.",
            "Limit the usage of clip, opacity and Rectangles",
            "Use Qt Quick Compiler to pre-compile QML files",
            "Investigate if static linking is possible for your architecture and license model",
            "Keep property bindings and QML code simple",
            "Avoid using JS when possible",
            "Use QQmlFileSelectors when targeting multiple platforms"]
    }

    ContentSlide {
        title: "Don'ts"
        itemFontSize: 18
        items: [
            "Overuse QML",
            "Initialize everything in your main.cpp",
            "Create big singletons that contain all the required interfaces",
            "Create complex delegates for Listviews",
            "Use Qt Quick Controls 1.0 for embedded",
            "Clip should be avoided altogether if possible. (98% of the use cases this should be possible).",
            "Overuse Loader",
            "Overuse JS: bad performance, bad maintainability",
            "Overdo re-use. Maximum code re-use often leads to more bindings, more complexity, and less performance."]
    }
    ContentSlide {
        title: "Don'ts"
        itemFontSize: 18
        items: [
            "Overestimate performance of your hardware",
            "Complicate your software architecture. Simpler architecture runs faster",
            "Load unnecessary things",
            "Use prebuilt images in the final product",
            "Leave optimization at the end of the project",
            "Underestimate the effort required for opimizing the very last milliseconds",
            "Layer QML on top of C++. From C++ never reach into QML",
            "Don’t store state in QML",
            "Don’t access id from other files"]
    }
}