Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hide the window from DWM until we're finished with initialization #12979

Merged
15 commits merged into from
May 6, 2022

Conversation

zadjii-msft
Copy link
Member

@zadjii-msft zadjii-msft commented Apr 25, 2022

When we start up, our window is initially just a frame with a transparent content area. We're gonna do all this startup init on the UI thread, so the UI won't actually paint till it's all done. This results in a few frames where the frame is visible, before the page paints for the first time, before any tabs appears, etc.

To mitigate this, we're gonna wait for the UI thread to finish everything it's gotta do for the initial init, and then fire our Initialized event. By waiting for everything else to finish (CoreDispatcherPriority::Low), we let all the tabs and panes actually get created. In the window layer, we're gonna cloak just not show the window till this event is fired, so we don't actually see this frame until we're actually all ready to go. This will result in the window seemingly not loading as fast, but it will actually take exactly the same amount of time before it's usable.

I also experimented with drawing a solid BG color before the initialization is finished. However, there are still a few frames after the frame is displayed before the XAML content first draws, so that didn't actually resolve any issues.

@ghost ghost added Area-UserInterface Issues pertaining to the user interface of the Console or Terminal Area-Windowing Window frame, quake mode, tearout Issue-Bug It either shouldn't be doing this or needs an investigation. Priority-3 A description (P3) Product-Terminal The new Windows Terminal. labels Apr 25, 2022
@github-actions

This comment was marked as resolved.

@github-actions

This comment was marked as outdated.

// so that didn't actually resolve any issues.

// Capture calling context.
winrt::apartment_context ui_thread;
Copy link
Member

Choose a reason for hiding this comment

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

Unused?

Copy link
Member Author

Choose a reason for hiding this comment

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

yea - I wanted to just co_await ui_thread, but that doesn't let me await it with low priority 🤷

Copy link
Member

@lhecker lhecker Apr 26, 2022

Choose a reason for hiding this comment

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

We should try to avoid using winrt::apartment_context as it's inherently flawed unfortunately. 😥
As Raymond Chen wrote:

Unfortunately, it's true. co_await'ing an apartment_context is blocking [the background thread]. That's because it's built out of COM's IContextCallback, which supports only synchronous transfer.
[...]
Note that it's even worse: Even after you get to the other side, the side you came *from* is still blocked until you finish doing your other-side work (which could take the form of another co_await).
[...]
If the UI thread is hung, this will lead to threadpool starvation and eventually a deadlock.

_GetWindowLayoutAsync() in particular is quite dangerous because it uses an apartment_context to switch back to a background thread and blocks the UI thread for that duration...

@zadjii-msft
Copy link
Member Author

Okay, after discussing in team sync - we totally can just defer the actual first call to ShowWindow, and it'll just do the right thing:

now-with-less-cloaking

and now we get the little shwoop animation back too. Thanks for the idea @lhecker!

if (auto self{ weak.get() })
{
// Then enqueue the rest of this function for after the UI thread settles.
co_await wil::resume_foreground(self->Dispatcher(), CoreDispatcherPriority::Low);
Copy link
Member

Choose a reason for hiding this comment

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

I'd suggest just storing the dispatcher in the beginning as

const auto dispatcher = Dispatcher();

Seems like a more hassle free solution to me.
Also: Do you mind removing the winrt::apartment_context instantiation?

Finally there's the option to drop all of the coroutine stuff and write this:

Dispatcher().RunAsync(CoreDispatcherPriority::Low, [weak = get_weak()]() {
    if (auto self = weak.get())
    {
        self->_InitializedHandlers(*self, nullptr);
    }
});

Copy link
Member Author

Choose a reason for hiding this comment

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

Also: Do you mind removing the winrt::apartment_context instantiation?

frick deleted locally, forgot to save

I'd suggest just storing the dispatcher in the beginning

I thought it best to not stash an owning reference to the dispatcher, as to decrease the chances that the coroutine kept the Dispatcher alive despite the Page getting deref'ed.

@zadjii-msft zadjii-msft changed the title Cloak the window from DWM until we're finished with initialization Hide the window from DWM until we're finished with initialization Apr 26, 2022
@zadjii-msft zadjii-msft mentioned this pull request Apr 27, 2022
@ghost ghost added the Priority-2 A description (P2) label Apr 27, 2022
@zadjii-msft zadjii-msft added this to the Terminal v1.14 milestone Apr 28, 2022
@ghost
Copy link

ghost commented Apr 29, 2022

CLA assistant check
Thank you for your submission, we really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.

❌ zadjii-msft sign now
You have signed the CLA already but the status is still pending? Let us recheck it.

// visible.

LaunchMode launchMode{};
_initialResizeAndRepositionWindow(_window->GetHandle(), _proposedRect, launchMode);
Copy link
Member

Choose a reason for hiding this comment

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

Worried a bit about the resize - how often will we start up at the wrong size?

Copy link
Member Author

Choose a reason for hiding this comment

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

I never saw it?

Copy link
Member Author

Choose a reason for hiding this comment

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

lmao well this was the issue now 😑

Copy link
Member

@DHowett DHowett left a comment

Choose a reason for hiding this comment

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

Does this work well enough with session restoration?

What happens if the hosted application exits cleanly before finishing startup?

wt -w -1 -- cmd /c exit 0

How does it work for DefTerm?

How about DefTerm when the command exits instantly?

@zadjii-msft
Copy link
Member Author

Does this work well enough with session restoration?

Yep, pretty slick honestly

What happens if the hosted application exits cleanly before finishing startup?

The foreground window changes for a second, but the new Terminal window never actually gets drawn.

How does it work for DefTerm?

Seems to work well enough

How about DefTerm when the command exits instantly?

You got me, in that specific case, a frame flashes and immediately goes away.

@DHowett DHowett added the AutoMerge Marked for automatic merge by the bot when requirements are met label May 6, 2022
@ghost
Copy link

ghost commented May 6, 2022

Hello @DHowett!

Because this pull request has the AutoMerge label, I will be glad to assist with helping to merge this pull request once all check-in policies pass.

p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (@msftbot) and give me an instruction to get started! Learn more here.

@ghost ghost merged commit 14098d7 into main May 6, 2022
@ghost ghost deleted the dev/migrie/b/11561-CLOAK branch May 6, 2022 20:08
carlos-zamora pushed a commit that referenced this pull request May 6, 2022
…2979)

When we start up, our window is initially just a frame with a transparent content area. We're gonna do all this startup init on the UI thread, so the UI won't actually paint till it's all done. This results in a few frames where the frame is visible, before the page paints for the first time, before any tabs appears, etc.

To mitigate this, we're gonna wait for the UI thread to finish everything it's gotta do for the initial init, and _then_ fire our Initialized event. By waiting for everything else to finish (`CoreDispatcherPriority::Low`), we let all the tabs and panes actually get created. In the window layer, we're gonna ~cloak~ just not show the window till this event is fired, so we don't actually see this frame until we're actually all ready to go. **This will result in the window seemingly not loading as fast**, but it will actually take exactly the same amount of time before it's usable.

I also experimented with drawing a solid BG color before the initialization is finished. However, there are still a few frames after the frame is displayed before the XAML content first draws, so that didn't actually resolve any issues.

* [x] Closes #11561
* [x] Tested manually
* [x] I work here.
* [x] Accidentally also closes #9053. By switching the initial call from `ShowWindow(SW_SHOW)` to `ShowWindow(SW_SHOWDEFAULT)`, we actually obey the startup info now.

(cherry picked from commit 14098d7)
Service-Card-Id: 81599073
Service-Version: 1.14
zadjii-msft added a commit that referenced this pull request May 10, 2022
ghost pushed a commit that referenced this pull request May 12, 2022
## Summary of the Pull Request
For some reason, the PGO tests (specifically the `RunMakeKillTabs` test) started to fail after #12979 merged. After closer inspection, the test was actually improperly written. We should be using <kbd>ctrl+shift+t</kbd> to open new tabs, not <kbd>alt+shift+t</kbd>. Presumably, the <kbd>alt</kbd> was copied over from the previous test, because they look _very_ similar.

So I went ahead and fixed the test, and it now (1) tests what it's intended to test and (2) doesn't fail. Why did #12979 cause the tests to fail? idk, but it works now.

## References
#10071 - Introduce PGO Tests

## Validation Steps Performed
Ran PGO tests locally and confirmed that it works.
Ran PGO pipeline and confirmed that it works.
carlos-zamora added a commit that referenced this pull request May 12, 2022
## Summary of the Pull Request
For some reason, the PGO tests (specifically the `RunMakeKillTabs` test) started to fail after #12979 merged. After closer inspection, the test was actually improperly written. We should be using <kbd>ctrl+shift+t</kbd> to open new tabs, not <kbd>alt+shift+t</kbd>. Presumably, the <kbd>alt</kbd> was copied over from the previous test, because they look _very_ similar.

So I went ahead and fixed the test, and it now (1) tests what it's intended to test and (2) doesn't fail. Why did #12979 cause the tests to fail? idk, but it works now.

## References
#10071 - Introduce PGO Tests

## Validation Steps Performed
Ran PGO tests locally and confirmed that it works.
Ran PGO pipeline and confirmed that it works.

(cherry picked from commit 17d1e24)
Service-Card-Id: 81863976
Service-Version: 1.14
@zadjii-msft
Copy link
Member Author

we definitely need to back this one out, I think it's causing my persisted windows (on my secondary monitor) to shrink a little each time.

carlos-zamora added a commit that referenced this pull request May 13, 2022
DHowett pushed a commit that referenced this pull request May 13, 2022
…tion (#12979)" (#13098)

This reverts commit 14098d7.

## Summary of the Pull Request
@zadjii-msft found that this is causing persisted windows on a secondary monitor to shrink a little each time. We're choosing to revert this commit until that gets resolved.

## References
#12979
carlos-zamora added a commit that referenced this pull request May 13, 2022
…tion (#12979)" (#13098)

This reverts commit 14098d7.

## Summary of the Pull Request
@zadjii-msft found that this is causing persisted windows on a secondary monitor to shrink a little each time. We're choosing to revert this commit until that gets resolved.

## References
#12979

(cherry picked from commit 6ffc3dc)
Service-Card-Id: 81910920
Service-Version: 1.14
zadjii-msft added a commit that referenced this pull request Aug 19, 2022
zadjii-msft added a commit that referenced this pull request Apr 6, 2023
…alization" (#13811)

This is a revert of the revert of #12979. We mainly reverted that PR
because of an issue where restored windows would grow/shrink slightly on
external displays. It was too close to the ship date for that release,
so we backed it out wholesale (in #13098). I think I've found the real
root of the problem, and fixed it here.

The money diff here from the original PR:
4c08b9a...c34495d.
Basically, I had put the part where we actually handle the creation of
the window into `_AppInitializedHandler`, when we should have left the
window to be created in `_HandleCreateWindow` We create it there,
_hidden_, and then should only _show_ it in `_AppInitializedHandler`.

I'm _NOT_ incorporating the change for #9053. I reverted that bit in
1fac403. I am too worried about that messing with the phwnd that I
wanted to get that reviewed and committed atomically, separately.

* fixes  #11561
* tested manually
* I work here
microsoft-github-policy-service bot pushed a commit that referenced this pull request Apr 18, 2023
…ute (#13838)

Original description, pre-process model v3:

> This is just the `SHOWDEFAULT` bit from #12979. This seems to also
work now, but I'm PR'ing it separately so it can be a separate revert
from #13811, if it is problematic.

More accurately: 
This PR enables terminal windows to use the `wShowCmd` from the
STARTUPINFO passed to `windowsterminal.exe` to set the initial
visibility of the window. We can't just use `SW_SHOWDEFAULT`, because
all the windows are running in the initial process! After the first
window, the subsequent ones would ignore any params passed to their
originating `windowsterminal.exe` processes. To mitigate, we pass that
`wShowCmd` info from the source process, to the actual running terminal
process. That accounts for most of the delta here.

Closes #9053


This doesn't do the same for defterm-initiated connections. This is
because we don't need to! Defterm very explicitly rejects handoff for
minimized console apps. This is probably for the best! I put an attempt
in 66f8b25 before I forgot that it was filtered long before the
Terminal. NOT doing this for /min saves us all sorts of "what happens if
`start /min cmd` tries to glom?" or "what if someone does `start /min
cmd && start /max cmd` and they glom together?".

<hr>

Also closes #15193, which was introduced as a part of this.

---------

Co-authored-by: Dustin L. Howett <[email protected]>
This pull request was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-UserInterface Issues pertaining to the user interface of the Console or Terminal Area-Windowing Window frame, quake mode, tearout AutoMerge Marked for automatic merge by the bot when requirements are met Issue-Bug It either shouldn't be doing this or needs an investigation. Priority-2 A description (P2) Priority-3 A description (P3) Product-Terminal The new Windows Terminal.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Starting WT momentarily shows a black titlebar START /MIN WT?
3 participants