Skip to content

Build the dust masks per view, not once for the app - #3413

Open
FrayxRulez wants to merge 1 commit into
developfrom
dust-masks-per-compositor
Open

Build the dust masks per view, not once for the app#3413
FrayxRulez wants to merge 1 commit into
developfrom
dust-masks-per-compositor

Conversation

@FrayxRulez

Copy link
Copy Markdown
Collaborator

Reported by crash telemetry on 12.10.2.0.

ArgumentException
The parameter is incorrect. Invalid argument to parameter value. Unsupported source brush type.

Windows::UI::Composition::CompositionMaskBrush::Validate_Mask
Windows::UI::Composition::CompositionMaskBrushT<...>::Api::put_Mask
ABI.Windows.UI.Composition.ICompositionMaskBrushMethods.set_Mask
Telegram.Composition.CompositionDustLayers.Build      CompositionDustLayers.cs:110
Telegram.Composition.CompositionDustVisual.Play       CompositionDustVisual.cs:153
Telegram.Views.ChatView.Captured                      ChatView.xaml.cs:588
Telegram.Collections.SynchronizedList`1.Flush         SynchronizedList.cs:154

Cause

CompositionDustLayers._masks was static, so one set of CompositionSurfaceBrush served the
whole app. They are built in PrepareAsync from BootStrapper.Current.Compositor, which is
Window.Current.Compositor — a per-view object. The first ChatView to be constructed decides
which view's compositor the masks belong to, and that is the main window.

Open a chat in its own window (createNewWindow, which goes through
CoreApplication.CreateNewView) and its ChatView gets a compositor of its own. Build then
creates the mask brush on that compositor and assigns a mask that belongs to another one, and
composition rejects it. Deleting a message there — or deleting the chat, which flushes the whole
history through SynchronizedList — kills the window every time.

The validation message does not name the compositor, but a wrong type is not reachable: the
only thing ever assigned is compositor.CreateSurfaceBrush(...), which Mask accepts, and the
array is fully populated before it is published, so no element can be null. What is left is the
object's origin.

Fix

Split the two halves that were sharing a lifetime.

The expensive part — the scatter pass over the noise and the PNG encode — has nothing to do with
any compositor, so it stays shared: EncodeAsync(count) produces the streams once per session
(behind a lock, since views are threads and two can ask at once) and hands the same array to
every caller.

The brushes are now [ThreadStatic], which under UWP is one set per view, and each view loads
them from a CloneStream() of the shared stream so two views loading at the same time do not
move each other's cursor.

Not built or run; the file parses clean under Roslyn.

Adjacent and left alone: CompositionDustVisual._random is a shared Random read from Build
on every view's thread. It could not be reached from a second view before this change; now it
can, and concurrent bursts in two windows could scramble it. The failure is cosmetic rather than
a crash, so it is not folded in here.

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

Labels

None yet

1 participant