$npx -y skills add ZhangHanDong/makepad-skills --skill makepad-2.0-performanceCRITICAL: Use for Makepad 2.0 performance optimization and debugging. Triggers on: makepad performance, makepad debug, makepad profiling, makepad gc, new_batch, texture_caching, render optimization, draw batching, mod.gc, garbage collection, memory, debug logging, troubleshoot, V
| 1 | # Makepad 2.0 Performance & Debugging Skill |
| 2 | |
| 3 | ## 1. Overview |
| 4 | |
| 5 | Makepad 2.0 uses a unique rendering pipeline combined with the Splash script VM. Performance depends on understanding three critical subsystems: |
| 6 | |
| 7 | 1. **Draw Batching** - How Makepad groups GPU draw calls and why `new_batch: true` matters |
| 8 | 2. **Garbage Collection** - The Splash VM's mark-sweep GC with per-type-bucket thresholds |
| 9 | 3. **Render Triggers** - The `on_render` / `.render()` system that controls when sub-trees rebuild |
| 10 | |
| 11 | Unlike traditional retained-mode UI frameworks, Makepad uses an immediate-mode-inspired draw pipeline where widgets emit draw commands into a sorted batch list. Understanding this pipeline is essential for diagnosing invisible text, flickering, and performance regressions. |
| 12 | |
| 13 | --- |
| 14 | |
| 15 | ## 2. Draw Batching System |
| 16 | |
| 17 | ### How It Works |
| 18 | |
| 19 | Makepad automatically batches consecutive draw calls that use the **same shader** into a single GPU draw call. This is a major performance optimization, but it has a critical side effect: draw order can be surprising. |
| 20 | |
| 21 | ``` |
| 22 | Draw pipeline (simplified): |
| 23 | |
| 24 | Widget tree: GPU batches (default): |
| 25 | |
| 26 | View (bg shader) Batch 1: all bg shaders |
| 27 | Label (text) --> Batch 2: all text shaders |
| 28 | View (bg shader) |
| 29 | Label (text) Result: ALL backgrounds draw first, |
| 30 | then ALL text draws second |
| 31 | ``` |
| 32 | |
| 33 | When a View has `show_bg: true` AND contains text children, the text can end up **behind** the background because both text draws get batched together into a single draw call that executes before (or after) the background draw calls. |
| 34 | |
| 35 | ### `new_batch: true` |
| 36 | |
| 37 | Setting `new_batch: true` on a View forces Makepad to start a **new draw batch** at that point. This creates a `ViewOptimize::DrawList` internally, which ensures proper draw ordering within that View's subtree. |
| 38 | |
| 39 | ``` |
| 40 | // PROBLEM: Label text is invisible - batched behind the background |
| 41 | RoundedView{ |
| 42 | width: Fill height: Fit |
| 43 | draw_bg.color: #1e1e2e |
| 44 | Label{text: "This text is INVISIBLE"} |
| 45 | } |
| 46 | |
| 47 | // FIX: new_batch ensures background draws before text |
| 48 | RoundedView{ |
| 49 | width: Fill height: Fit |
| 50 | new_batch: true |
| 51 | draw_bg.color: #1e1e2e |
| 52 | Label{text: "This text is VISIBLE"} |
| 53 | } |
| 54 | ``` |
| 55 | |
| 56 | ### When `new_batch: true` Is Required |
| 57 | |
| 58 | | Scenario | Required? | Why | |
| 59 | |----------|-----------|-----| |
| 60 | | View with `show_bg: true` containing Labels | YES | Text batches behind background | |
| 61 | | View with hover animator + text children | YES | Hover bg covers text on activation | |
| 62 | | Container of repeated items with backgrounds | YES | Each item and the container need it | |
| 63 | | Transparent View (no `show_bg`) with Labels | NO | No background to overlap | |
| 64 | | View with only non-text children (e.g., icons) | NO | Same shader type - no overlap issue | |
| 65 | | Deeply nested Views each with backgrounds | YES on each | Each background layer needs its own batch | |
| 66 | |
| 67 | ### Hover Effects and `new_batch` |
| 68 | |
| 69 | This is the **number one mistake** with hoverable list items. When a View has `show_bg: true` with a hover animator that transitions from transparent (`#0000`) to opaque on hover, the text disappears on hover because the newly-opaque background covers the batched text. |
| 70 | |
| 71 | ``` |
| 72 | // CORRECT: Hoverable item with new_batch |
| 73 | let HoverItem = View{ |
| 74 | width: Fill height: Fit |
| 75 | new_batch: true |
| 76 | show_bg: true |
| 77 | draw_bg +: { |
| 78 | color: uniform(#0000) |
| 79 | color_hover: uniform(#fff2) |
| 80 | hover: instance(0.0) |
| 81 | } |
| 82 | animator: Animator{ |
| 83 | hover: { |
| 84 | default: { |
| 85 | from: {all: Forward{duration: 0.1}} |
| 86 | apply: {draw_bg: {hover: 0.0}} |
| 87 | } |
| 88 | on: { |
| 89 | from: {all: Forward{duration: 0.1}} |
| 90 | apply: {draw_bg: {hover: 1.0}} |
| 91 | } |
| 92 | } |
| 93 | } |
| 94 | label := Label{text: "item" draw_text.color: #fff} |
| 95 | } |
| 96 | |
| 97 | // Parent container of hover items also needs new_batch |
| 98 | RoundedView{ |
| 99 | flow: Down height: Fit new_batch: true |
| 100 | draw_bg.color: #2a2a3d |
| 101 | draw_bg.border_radius: 8.0 |
| 102 | HoverItem{label.text: "First item"} |
| 103 | HoverItem{label.text: "Second item"} |
| 104 | } |
| 105 | ``` |
| 106 | |
| 107 | ### ViewOptimize Internals |
| 108 | |
| 109 | The `new_batch` and `texture_caching` properties map to a `ViewOptimize` enum: |
| 110 | |
| 111 | ``` |
| 112 | ViewOptimize::None - Default. No special draw ordering. |
| 113 | ViewOptimize::DrawList - Created by new_batch: true. Starts a new DrawList2d. |
| 114 | ViewOptimize::Texture - Created by texture_caching: true. Renders to offscreen texture. |
| 115 | ``` |
| 116 | |
| 117 | Priority: `texture_caching` takes precedence over `new_batch` if both are set. |
| 118 | |
| 119 | --- |
| 120 | |
| 121 | ## 3. Texture Caching |
| 122 | |
| 123 | ### How |