Skip to content

Reproduce and suggest fix for desktop app move performance issues#9113

Draft
ben-polinsky wants to merge 1 commit into
masterfrom
bdp/fix-dynamics-perf-1659
Draft

Reproduce and suggest fix for desktop app move performance issues#9113
ben-polinsky wants to merge 1 commit into
masterfrom
bdp/fix-dynamics-perf-1659

Conversation

@ben-polinsky
Copy link
Copy Markdown
Contributor

This PR proposes two fixes to improve viewer rendering performance related to moving elements, surfaced in a desktop application. User noted performance was not great in 4.11 while moving elements, but that iTwin.js 5.2 exacerbated the issue.

Decouple dynamics and decoration invalidations

We believe the invalidateDecorations() call inside of Viewport.changeDynamics() is the root cause of these performance issues. This forces a full decoration rebuild on every mouse move during dynamics.

While dynamics and decorations are related concepts, and I'm ignorant of much of the history here, it seems like they are rendered separately. Maybe it's idealistic (or ignorant), I don't think the two should invalidate each others' calls.

Assuming the idea behind this fix is on the right track, the next question is "are our consumers relying on this behavior"? In discussions with Mark and Erin, we don't think we can guarantee they are not. Thus, if we move forward, should probably make this behavior opt-in.

Issue exacerbated by #7604

A change in the above PR corresponds to our user's complaint that the performance became extremely noticeable in 5.2. Looking here, screenViewport.addDecorations() iterates getTileTreeRefs(), which yields view model refs + displayStyle refs + map refs + tiled graphics provider refs. But this.view is already decorated on the line above, so the view/displayStyle refs are seemingly redundant?

I've added a DTA reproduction with instructions in display-test-app/src/frontend/ReproIssue1659.ts. DTA repro tool - adds decorators matching the desktop app's patterns (uncached connector decorators, cached annotation overlays), then you use the real dta interactive move tool. dta repro 1659 toggle flips the actual code paths between fixed and regressed behavior so you can compare live. The first issue above seems to be the cause of most of the performance issues, but it is dependent on the complexity of the setup (num of models/elements/decorators).

@markschlosseratbentley @eringram have been involved in discussions.
@bbastings We're eager to know your thoughts when you have a chance.

@bbastings
Copy link
Copy Markdown
Contributor

bbastings commented Mar 20, 2026

If your decorations are not changing every dynamics frame, are you creating them as cached decorations? If all the decorations are cached, how is the performance?

/** Interface for drawing [[Decorations]] into, or on top of, a [[ScreenViewport]].
 * @public
 */
export interface ViewportDecorator {
  /** Override to enable cached decorations for this decorator.
   * By default, a decorator is asked to recreate its decorations from scratch via its [[decorate]] method whenever the viewport's decorations are invalidated.
   * Decorations become invalidated for a variety of reasons, including when the scene changes and when the mouse moves.
   * Most decorators care only about when the scene changes, and may create decorations that are too expensive to recreate on every mouse motion.
   * If `useCachedDecorations` is true, then the viewport will cache the most-recently-created decorations for this decorator, and only invoke its [[decorate]] method if it has no cached decorations for it.
   * The cached decorations are discarded:
   *  - Whenever the scene changes; and
   *  - When the decorator explicitly requests it via [[Viewport.invalidateCachedDecorations]] or [[ViewManager.invalidateCachedDecorationsAllViews]].
   * The decorator should invoke the latter when the criteria governing its decorations change.
   */
  readonly useCachedDecorations?: true;

@bbastings
Copy link
Copy Markdown
Contributor

We believe the invalidateDecorations() call inside of Viewport.changeDynamics() is the root cause of these performance issues. This forces a full decoration rebuild on every mouse move during dynamics.

People probably are relying on this even if they aren't aware...otherwise a lot of "tools" with decorations that follow the cursor would likely need to start explicitly invaliding decorations onMotion...

@ben-polinsky
Copy link
Copy Markdown
Contributor Author

If all the decorations are cached, how is the performance?

In the reproduction, it's very good.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants