Skip to content

fix: SG-45066: Load annotation font list without blocking first activation - #1444

Open
cedrik-fuoco-adsk wants to merge 1 commit into
AcademySoftwareFoundation:mainfrom
cedrik-fuoco-adsk:SG-45066-annotation-tools-first-activation-delay
Open

cedrik-fuoco-adsk wants to merge 1 commit into
AcademySoftwareFoundation:mainfrom
cedrik-fuoco-adsk:SG-45066-annotation-tools-first-activation-delay

Conversation

@cedrik-fuoco-adsk

@cedrik-fuoco-adsk cedrik-fuoco-adsk commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

fix: SG-45066: Load annotation font list without blocking first activation

Linked issues

none

Summarize your change.

The beta annotate tool no longer freezes RV while it builds the font list for the Text tool. The dock opens right away, and the font combo then fills in time-boxed batches (about 1 ms each) on the GUI thread.

Describe the reason for the change.

_TextOptionsPanel.__init__ filled the font combo by calling QFontDatabase.isSmoothlyScalable() on every installed family. Each call resolves every style of the family, so the first activation of the tool froze RV: 650 to 870 ms measured on macOS with a warm font cache, and about 2 seconds from what I can see on my Macbook.

Describe what you have tested and on which operating system.

macOS (debug build), F10 sent through RV's event table from a startup script:

Before After
Time until the dock is built (UI frozen) 650 to 870 ms 85 to 96 ms
Font list complete inside the freeze 355 to 520 ms after the dock is built, 321 families
Longest event-loop gap while loading n/a 17 to 18 ms (one slow isSmoothlyScalable() call)

Also checked in RV on macOS:

  • Saved font family (font_family setting) is selected after loading and the setting is unchanged.
  • A text annotation created before the list has loaded uses the saved family.
  • A font picked mid-load is emitted once, saved, and kept for the rest of the load and after a restart.
  • Deactivating and reactivating the tool mid-load, and again after, doesn't enumerate fonts again or add duplicates.

Add a list of changes, and note any that might need special attention during the review.

  • _TextOptionsPanel no longer touches QFontDatabase in __init__. start_loading_fonts() (runs once) queues the families() names. A 0 ms QTimer then runs _load_font_batch(), which adds smoothly scalable families until about 1 ms have passed and then returns to the event loop.
  • AnnotateSecondaryPanel.showEvent schedules start_loading_fonts() with a single-shot timer, so the dock paints first. The text page itself can't trigger it, because it's a hidden stacked page unless the Text tool is active.
  • The combo now has setPlaceholderText("Aa"). Without it, QComboBox auto-selects the first family added. Re-picking that item during the load wouldn't change the index, so no signal would fire and the user's pick would be lost. The closed combo already paints "Aa", so this changes nothing visible.
  • set_font_family() always updates the "Aa" preview font, even before the family is in the list. If the saved family is no longer installed, Qt spends about 68 ms resolving aliases. Only users whose saved font was removed pay that.

Why time-boxed batches instead of a worker thread: I prototyped a QThread that builds the list and measured both in RV:

Load time Longest main-thread stall 99th percentile stall
Worker QThread 351 to 431 ms 16 to 21 ms 16 to 21 ms
Time-boxed batches (this PR) 423 to 437 ms 11 to 17 ms 8 to 11 ms

The thread doesn't stall the UI less, because PySide6 keeps the GIL while isSmoothlyScalable() runs.

A standalone test confirmed this:

During the slow calls the main thread's Python was blocked for up to 18.5 ms, more than the 5 ms GIL switch interval, while a time.sleep() control blocked it for 0.1 ms. Both approaches are therefore bounded by one slow call.

The thread finishes up to about 70 ms sooner, but it brings shutdown handling (a QThread destroyed while running aborts) and depends on QFontDatabase thread safety on every platform's font back end. The batched version stalls less often (see the 99th percentile column), with deterministic behavior that's easier to test.

@cedrik-fuoco-adsk
cedrik-fuoco-adsk force-pushed the SG-45066-annotation-tools-first-activation-delay branch from 5907ea6 to bd674ba Compare October 7, 2026 17:04
@cedrik-fuoco-adsk
cedrik-fuoco-adsk marked this pull request as ready for review October 7, 2026 17:07
…ation

The Text tool's font combo was filled in _TextOptionsPanel.__init__ by
calling QFontDatabase.isSmoothlyScalable() on every installed family.
Each call resolves all styles of the family, so building the dock froze
RV the first time the beta annotate tool was opened: 650 to 870 ms
measured on macOS with a warm font cache, and about 2 s reported. The
cost grows with the number of installed fonts.

The dock now builds without touching the font list. Once the secondary
panel is shown, the list is filled in batches of about 1 ms from a
QTimer, so the event loop keeps running between batches. The saved
font family is selected when it is added, and a placeholder keeps the
combo from auto-selecting the first font, so loading never emits
font_family_changed or overwrites the saved setting.

Signed-off-by: Cédrik Fuoco <cedrik.fuoco@autodesk.com>
@cedrik-fuoco-adsk
cedrik-fuoco-adsk force-pushed the SG-45066-annotation-tools-first-activation-delay branch from bd674ba to b66f7bf Compare October 7, 2026 17:23

@bernie-laberge bernie-laberge left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

DEFAULT_FONT_FAMILY = "Helvetica"

# Time spent filling the font list per event loop pass.
FONT_BATCH_SECONDS = 0.001

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like the variable name should be descriptive enough to not need a comment above it here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants