-
Notifications
You must be signed in to change notification settings - Fork 8.3k
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
Fix Windows 10 support for nearby font loading #12554
Conversation
void AtlasEngine::UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept | ||
{ | ||
_api.hyperlinkHoveredId = hoveredId; | ||
} | ||
|
||
#pragma endregion | ||
|
||
void AtlasEngine::_resolveAntialiasingMode() noexcept | ||
{ | ||
// If the user asks for ClearType, but also for a transparent background | ||
// (which our ClearType shader doesn't simultaneously support) | ||
// then we need to sneakily force the renderer to grayscale AA. | ||
const auto forceGrayscaleAA = _api.antialiasingMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && !_api.backgroundOpaqueMixin; | ||
_api.realizedAntialiasingMode = forceGrayscaleAA ? D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE : _api.antialiasingMode; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code isn't new. It's just moved up from down below to group the code diff better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this makes it difficult to review the diff -- can you point out the part that fixed the fallback issue you mentioned?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, saying that I moved it might've been an overstatement.
I didn't actually move the code. The _updateFont
function is new. It's just git diffing it poorly and no builtin algorithm improves this diff.
c6b873f
to
9aa6246
Compare
void AtlasEngine::UpdateHyperlinkHoveredId(const uint16_t hoveredId) noexcept | ||
{ | ||
_api.hyperlinkHoveredId = hoveredId; | ||
} | ||
|
||
#pragma endregion | ||
|
||
void AtlasEngine::_resolveAntialiasingMode() noexcept | ||
{ | ||
// If the user asks for ClearType, but also for a transparent background | ||
// (which our ClearType shader doesn't simultaneously support) | ||
// then we need to sneakily force the renderer to grayscale AA. | ||
const auto forceGrayscaleAA = _api.antialiasingMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && !_api.backgroundOpaqueMixin; | ||
_api.realizedAntialiasingMode = forceGrayscaleAA ? D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE : _api.antialiasingMode; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this makes it difficult to review the diff -- can you point out the part that fixed the fallback issue you mentioned?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if the font exists in the system set, but is unreadable due to a permission error? Would we find a "nearby" version of the same font and load it? That is the core of the issue with Cascadia on upgrade.
With the Windows Font Cache Service enabled I can't get my system in a state where Jetbrains Mono isn't available since it remains cached even after the .ttf file had its ACLs changed. If I disable that service, DirectWrite properly won't support the font at all. In short: I can't get my system to replicate the issue even after changing the ACLs. In theory this approach should fix the issue, because we append the "nearby fonts" last to the collection, due to which they should take priority over the system fonts. I suspect that the people for whom this doesn't work, mostly used Windows 10, which this PR will fix. |
dbf37a7
to
418ef40
Compare
418ef40
to
fe55404
Compare
FYI I made the following changes to the PR:
|
_weight = DWRITE_FONT_WEIGHT_NORMAL; | ||
_stretch = DWRITE_FONT_STRETCH_NORMAL; | ||
_style = DWRITE_FONT_STYLE_NORMAL; | ||
face = _FindFontFace(dwriteFactory, localeName, false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
_FindFontFace
doesn't depend on any of the 3 members above, so this code is redundant and could be removed.
static constexpr std::array fallbackFaceNames{ static_cast<const wchar_t*>(nullptr), L"Consolas", L"Lucida Console", L"Courier New" }; | ||
auto it = fallbackFaceNames.begin(); | ||
const auto end = fallbackFaceNames.end(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Windows Terminal ignores the HRESULT this function returns and will simply crash with a division by 0 error if it wasn't for this new code. This implements font fallback in a similar way as DxEngine does.
In the future Terminal shouldn't ignore the HRESULT and call UpdateFont() with fallback fonts itself until this method succeeds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the future
should we track this work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a short note in #9999.
@@ -122,8 +114,7 @@ void DxFontInfo::SetFromEngine(const std::wstring_view familyName, | |||
// - localeName - Locale to search for appropriate fonts | |||
// Return Value: | |||
// - Smart pointer holding interface reference for queryable font data. | |||
[[nodiscard]] Microsoft::WRL::ComPtr<IDWriteFontFace1> DxFontInfo::ResolveFontFaceWithFallback(gsl::not_null<IDWriteFactory1*> dwriteFactory, | |||
std::wstring& localeName) | |||
[[nodiscard]] Microsoft::WRL::ComPtr<IDWriteFontFace1> DxFontInfo::ResolveFontFaceWithFallback(IDWriteFontCollection* fontCollection, std::wstring& localeName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved ownership of the nearby font collection over to DxFontRenderData
. That way we don't instantiate 4 font collections for the 4 font styles (italic & bold).
fontCollection = nearbyCollection; | ||
} | ||
} | ||
THROW_IF_FAILED(fontCollection->FindFamilyName(_familyName.data(), &familyIndex, &familyExists)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DxEngine will now always search for nearby fonts. The previous approach is inherently flawed, because we first checked if a font exists in the system collection, but those were corrupted. So the only fix is to eagerly use nearby fonts and not rely on MSIX anymore.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
k I'm happy the spooky action at a distance is gone
return fontFiles; | ||
} | ||
|
||
inline wil::com_ptr<IDWriteFontCollection> getFontCollection(bool forceUpdate) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm kinda just trusting that this bit of the code will work as expected. I don't totally know the mechanical differences between a IDWriteFactory6
and a IDWriteFactory
, or a IDWriteFontSetBuilder1
and a IDWriteFontSetBuilder2
, but I'm trusting it works
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW the new code is what IDWriteFontSetBuilder2::AddFontFile
does internally. It's implemented in terms of IDWriteFontSetBuilder1::AddFontFile
.
static constexpr std::array fallbackFaceNames{ static_cast<const wchar_t*>(nullptr), L"Consolas", L"Lucida Console", L"Courier New" }; | ||
auto it = fallbackFaceNames.begin(); | ||
const auto end = fallbackFaceNames.end(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the future
should we track this work?
if (til::ends_with(p.path().native(), L".ttf")) | ||
{ | ||
wil::com_ptr<IDWriteFontFile> fontFile; | ||
if (SUCCEEDED_LOG(factory5->CreateFontFileReference(p.path().c_str(), nullptr, fontFile.addressof()))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so it's safe to use these objects on different threads because it is a SHARED-type factory? neato
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DWRITE_FACTORY_TYPE_SHARED
only enables the use of the Windows Font Cache service for DirectWrite. It's unspecified whether DirectWrite is thread-safe, but it should be, since Direct2D can be used across threads. I'm gonna ask one of the dwrite devs about this...
Hello @zadjii-msft! Because this pull request has the 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 (
|
By replacing `IDWriteFontSetBuilder2::AddFontFile` with `IDWriteFactory5::CreateFontFileReference` and `IDWriteFontSetBuilder1::AddFontFile` we add nearby font loading support for Windows 10, build 15021. This commit also fixes font fallback for AtlasEngine, which was crashing during testing. Finally it fixes a bug in DxEngine, where we only created a "nearby" font collection if we couldn't find the font in the system collection. This doesn't fix the bug, if the font is locked or broken in the system collection. This is related to #11648. ## PR Checklist * [x] Closes #12420 * [x] I work here * [x] Tests added/passed ## Validation Steps Performed * Build a Debug version of Windows Terminal * Put Jetbrains Mono into the writeable AppX directory * Jetbrains Mono is present in the settings UI ✅ * DxEngine works with Jetbrains Mono ✅ * AtlasEngine works with Jetbrains Mono ✅ (cherry picked from commit f84ccad)
By replacing `IDWriteFontSetBuilder2::AddFontFile` with `IDWriteFactory5::CreateFontFileReference` and `IDWriteFontSetBuilder1::AddFontFile` we add nearby font loading support for Windows 10, build 15021. This commit also fixes font fallback for AtlasEngine, which was crashing during testing. Finally it fixes a bug in DxEngine, where we only created a "nearby" font collection if we couldn't find the font in the system collection. This doesn't fix the bug, if the font is locked or broken in the system collection. This is related to #11648. * [x] Closes #12420 * [x] I work here * [x] Tests added/passed * Build a Debug version of Windows Terminal * Put Jetbrains Mono into the writeable AppX directory * Jetbrains Mono is present in the settings UI ✅ * DxEngine works with Jetbrains Mono ✅ * AtlasEngine works with Jetbrains Mono ✅ (cherry picked from commit f84ccad)
🎉 Handy links: |
The original research for a solution all the way back in #11032 contained an unfortunate flaw. The nearby font loading code was written under the assumption that Cascadia is missing in the system font collection, leading to our issues. Adding nearby fonts last into the collection would thus ensure that we use the system fonts whenever possible, but only have nearby fonts as a fallback. This didn't work and we figured that we'd have to always prefer loading nearby fonts over system fonts. #12554 tried to achieve this, but failed to change the order in which the font set is built. In order to prefer nearby fonts over system ones, we have to add the system font collection last. ## PR Checklist * [x] Closes #11648 * [x] I work here * [x] Tests added/passed * [x] Embarrassment for my incompetence ## Validation Steps Performed * Put Jetbrains Mono into the AppX directory of the Debug build * Jetbrains Mono shows up in the font selector and is useable Additionally a more complex mini-test was built: Using FontForge I've cloned arial.ttf and removed all characters except for the letter "0". Afterwards I've build a custom font collection the same way we do it in Terminal, extracted a `FontFace` named "Arial" and called `IDWriteFont::HasCharacter` for the letter "1". Loading the system font collection first results in `TRUE` and loading it last results in `FALSE` (since my custom arial.ttf doesn't have the letter "1"). This confirms that we need to load the system font collection last.
The original research for a solution all the way back in #11032 contained an unfortunate flaw. The nearby font loading code was written under the assumption that Cascadia is missing in the system font collection, leading to our issues. Adding nearby fonts last into the collection would thus ensure that we use the system fonts whenever possible, but only have nearby fonts as a fallback. This didn't work and we figured that we'd have to always prefer loading nearby fonts over system fonts. #12554 tried to achieve this, but failed to change the order in which the font set is built. In order to prefer nearby fonts over system ones, we have to add the system font collection last. ## PR Checklist * [x] Closes #11648 * [x] I work here * [x] Tests added/passed * [x] Embarrassment for my incompetence ## Validation Steps Performed * Put Jetbrains Mono into the AppX directory of the Debug build * Jetbrains Mono shows up in the font selector and is useable Additionally a more complex mini-test was built: Using FontForge I've cloned arial.ttf and removed all characters except for the letter "0". Afterwards I've build a custom font collection the same way we do it in Terminal, extracted a `FontFace` named "Arial" and called `IDWriteFont::HasCharacter` for the letter "1". Loading the system font collection first results in `TRUE` and loading it last results in `FALSE` (since my custom arial.ttf doesn't have the letter "1"). This confirms that we need to load the system font collection last. (cherry picked from commit eeb8970) Service-Card-Id: 80641214 Service-Version: 1.13
The original research for a solution all the way back in #11032 contained an unfortunate flaw. The nearby font loading code was written under the assumption that Cascadia is missing in the system font collection, leading to our issues. Adding nearby fonts last into the collection would thus ensure that we use the system fonts whenever possible, but only have nearby fonts as a fallback. This didn't work and we figured that we'd have to always prefer loading nearby fonts over system fonts. #12554 tried to achieve this, but failed to change the order in which the font set is built. In order to prefer nearby fonts over system ones, we have to add the system font collection last. ## PR Checklist * [x] Closes #11648 * [x] I work here * [x] Tests added/passed * [x] Embarrassment for my incompetence ## Validation Steps Performed * Put Jetbrains Mono into the AppX directory of the Debug build * Jetbrains Mono shows up in the font selector and is useable Additionally a more complex mini-test was built: Using FontForge I've cloned arial.ttf and removed all characters except for the letter "0". Afterwards I've build a custom font collection the same way we do it in Terminal, extracted a `FontFace` named "Arial" and called `IDWriteFont::HasCharacter` for the letter "1". Loading the system font collection first results in `TRUE` and loading it last results in `FALSE` (since my custom arial.ttf doesn't have the letter "1"). This confirms that we need to load the system font collection last. (cherry picked from commit eeb8970) Service-Card-Id: 80641213 Service-Version: 1.12
By replacing
IDWriteFontSetBuilder2::AddFontFile
withIDWriteFactory5::CreateFontFileReference
andIDWriteFontSetBuilder1::AddFontFile
we add nearbyfont loading support for Windows 10, build 15021.
This commit also fixes font fallback for AtlasEngine,
which was crashing during testing.
Finally it fixes a bug in DxEngine, where we only created a "nearby" font
collection if we couldn't find the font in the system collection. This doesn't
fix the bug, if the font is locked or broken in the system collection.
This is related to #11648.
PR Checklist
Validation Steps Performed