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

fonts: Instantiate system fonts using system font loaders #33747

Merged
merged 1 commit into from
Oct 11, 2024

Conversation

mrobinson
Copy link
Member

@mrobinson mrobinson commented Oct 9, 2024

System fonts used to be instantiated using the system font loader and
this change restores that behavior. In addition, on macOS and FreeType
platforms font data for system fonts is loaded using memory mapping. The
benefit is that system font loaders typically are able to cache fonts in
system memory (using memory mapping, for instance) and we'd like to load
them in a the way most compatible with other applications.

On my Linux system, this manages to get the overhead of loading a very
large font down from 10ms to approximately 1ms. Subsequent runs show
even less overhead. We've measured similar gains on macOS systems.

Currently, system font data must be loaded into memory manually for
canvas and this is unlikely to change even with a switch to vello. The
use of explicit memmory mapping should help in this case -- though it
probably won't be possible to use this properly on macOS and Windows if
we ever want to load fonts from TTCs properly.

Signed-off-by: Martin Robinson [email protected]
Co-authored-by: Mukilan Thiyagarajan [email protected]


  • ./mach build -d does not report any errors
  • ./mach test-tidy does not report any errors
  • These changes do not require tests because it should only lead to a performance improvement.

@mrobinson mrobinson force-pushed the use-native-font-descriptors branch from 61a561c to af626c8 Compare October 9, 2024 11:37
@mrobinson mrobinson requested a review from mukilan October 9, 2024 12:06
@atouchet
Copy link
Contributor

atouchet commented Oct 9, 2024

dwrote was updated in #33766.

pub template: FontTemplateRef,
pub metrics: FontMetrics,
pub descriptor: FontDescriptor,

/// The data for this font. This might be unitialized for system fonts.
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
/// The data for this font. This might be unitialized for system fonts.
/// The data for this font. This might be uninitialized for system fonts.

@@ -121,66 +77,32 @@ impl FontStore {
/// load was canceled (for instance, if the stylesheet was removed), then do nothing and return
/// false.
///
/// `data` will be `None` When loading fonts via the `local()` directive in CSS, in which case
Copy link
Member

Choose a reason for hiding this comment

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

This method doesn't have a data parameter.

pub use font::*;
pub use font_context::*;
pub use font_store::*;
pub use font_template::*;
pub use glyph::*;
use ipc_channel::ipc::IpcSharedMemory;
pub use shaper::*;
pub use system_font_service::*;
use unicode_properties::{emoji, EmojiStatus, UnicodeEmoji};

Copy link
Member

Choose a reason for hiding this comment

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

This documentation seems outdated as we can no longer send FontData over IPC channels directly and we don't support the as_arc method.

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've updated this documentation to reflect the fact the handle is what is sent across the IPC channel.

@@ -43,14 +44,29 @@ impl LocalFontIdentifier {
.map_or(0, |font| font.create_font_face().get_index())
}

pub(crate) fn read_data_from_file(&self) -> Vec<u8> {
let font = FontCollection::system()
pub fn native_font_handle(&self) -> NativeFontHandle {
Copy link
Member

Choose a reason for hiding this comment

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

Should this be pub(crate)? It is not pub in other platforms.

system_font_service: Arc<MockSystemFontService>,
system_font_service_proxy: SystemFontServiceProxy,
}
// This currently only works on FreeType platforms as it requires being able ot create
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
// This currently only works on FreeType platforms as it requires being able ot create
// This currently only works on FreeType platforms as it requires being able to create

@mrobinson mrobinson force-pushed the use-native-font-descriptors branch from af626c8 to e422131 Compare October 10, 2024 10:57
@mrobinson
Copy link
Member Author

Thanks for the review @mukilan. I believe I have addressed all of your comments.

@mrobinson mrobinson force-pushed the use-native-font-descriptors branch from e422131 to 66fea8a Compare October 10, 2024 11:05
@mrobinson mrobinson force-pushed the use-native-font-descriptors branch from 66fea8a to b02fc1e Compare October 10, 2024 13:10
@mrobinson mrobinson added the T-linux-wpt-2020 Do a try run of the WPT label Oct 10, 2024
@github-actions github-actions bot removed the T-linux-wpt-2020 Do a try run of the WPT label Oct 10, 2024
Copy link

🔨 Triggering try run (#11274731178) for Linux WPT

Copy link

Test results for linux-wpt-layout-2020 from try job (#11274731178):

Flaky unexpected result (20)
  • FAIL [expected PASS] /_mozilla/css/dirty_viewport.html (#13731)
  • TIMEOUT [expected OK] /_webgl/conformance/glsl/misc/shader-uniform-packing-restrictions.html (#28103)
  • TIMEOUT [expected OK] /_webgl/conformance/uniforms/out-of-bounds-uniform-array-access.html (#26225)
    • NOTRUN [expected PASS] subtest: Overall test
  • CRASH [expected OK] /encoding/legacy-mb-japanese/euc-jp/eucjp-decode-x-euc-jp.html?3001-4000
  • CRASH [expected OK] /encoding/legacy-mb-tchinese/big5/big5-decode.html?14001-last
  • TIMEOUT /fetch/metadata/generated/css-images.sub.tentative.html (#29047)
    • PASS [expected TIMEOUT] subtest: background-image sec-fetch-site - HTTPS downgrade (header not sent)
  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-cross-origin.sub.window.html (#29056)
    • FAIL [expected PASS] subtest: Cross-origin navigation started from unload handler must be ignored

      promise_test: Unhandled rejection with value: object "SecurityError: The operation is insecure."
      

  • OK /html/browsers/the-window-object/open-close/creating_browsing_context_test_01.html (#29046)
    • PASS [expected FAIL] subtest: first argument: absolute url
  • TIMEOUT [expected OK] /html/infrastructure/urls/base-url/document-base-url-window-initiator-is-not-opener.https.window.html (#30970)
  • OK [expected TIMEOUT] /html/semantics/embedded-content/media-elements/playing-the-media-resource/loop-from-ended.tentative.html (#33778)
    • FAIL [expected TIMEOUT] subtest: play() with loop set to true after playback ended

      this argument is not a finite floating-point value
      

  • OK /html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-nav-location-assign.html (#32863)
    • FAIL [expected PASS] subtest: Navigating iframe loading='lazy' before it is loaded: location.assign

      uncaught exception: Error: assert_equals: expected "http://web-platform.test:8000/html/semantics/embedded-content/the-iframe-element/support/blank.htm?nav" but got "http://web-platform.test:8000/html/semantics/embedded-content/the-iframe-element/support/blank.htm?src"
      

  • OK /html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-nav-location-replace-set-src.html (#32697)
    • PASS [expected FAIL] subtest: Navigating iframe loading='lazy' and then setting src: location.replace
  • OK /html/semantics/forms/form-submission-0/urlencoded2.window.html (#28687)
    • PASS [expected FAIL] subtest: application/x-www-form-urlencoded: 0x00 in name (formdata event)
    • PASS [expected FAIL] subtest: application/x-www-form-urlencoded: 0x00 in value (normal form)
  • OK /html/semantics/forms/historical.html (#28568)
    • PASS [expected FAIL] subtest: &lt;input name=isindex&gt; should not be supported
  • CRASH [expected OK] /html/semantics/forms/the-fieldset-element/disabled-003.html (#31730)
  • OK /html/webappapis/dynamic-markup-insertion/document-write/module-tla-delayed.html (#29137)
    • FAIL [expected PASS] subtest: document.write in an imported module

      assert_true: onload must be called expected true got false
      

  • TIMEOUT [expected OK] /webmessaging/with-ports/017.html (#24486)
    • TIMEOUT [expected PASS] subtest: origin of the script that invoked the method, about:blank

      Test timed out
      

  • TIMEOUT [expected OK] /webmessaging/with-ports/018.html (#24485)
    • TIMEOUT [expected PASS] subtest: origin of the script that invoked the method, javascript:

      Test timed out
      

  • TIMEOUT [expected OK] /webmessaging/without-ports/018.html (#24485)
    • TIMEOUT [expected PASS] subtest: origin of the script that invoked the method, javascript:

      Test timed out
      

  • OK /xhr/open-url-multi-window-5.htm (#23360)
    • FAIL [expected PASS] subtest: XMLHttpRequest: open() resolving URLs (multi-Window; 5)

      assert_throws_dom: function "function() {client.open("GET", "...") }" did not throw
      

Stable unexpected results that are known to be intermittent (18)
  • FAIL [expected PASS] /_mozilla/css/iframe/hide_and_show.html (#15265)
  • OK /css/cssom-view/MediaQueryList-addListener-removeListener.html (#24569)
    • PASS [expected FAIL] subtest: listener that was added twice is called only once
  • OK /css/cssom-view/MediaQueryList-extends-EventTarget-interop.html (#25285)
    • PASS [expected FAIL] subtest: capturing event listener fires before non-capturing listener at target
  • OK /css/cssom-view/MediaQueryList-extends-EventTarget.html (#25269)
    • PASS [expected FAIL] subtest: onchange adds listener
  • TIMEOUT /fetch/metadata/generated/element-img-environment-change.sub.html (#30111)
    • PASS [expected FAIL] subtest: sec-fetch-site - Not sent to non-trustworthy same-origin destination, no attributes
  • OK /html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-window-open.html (#28691)
    • PASS [expected FAIL] subtest: load event does not fire on window.open('about:blank')
  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin.window.html (#29049)
    • PASS [expected FAIL] subtest: Same-origin navigation started from unload handler must be ignored
  • ERROR [expected TIMEOUT] /html/canvas/element/manual/imagebitmap/createImageBitmap-invalid-args.html (#32745)
  • OK [expected TIMEOUT] /html/canvas/element/manual/imagebitmap/createImageBitmap-origin.sub.html (#31931)
  • TIMEOUT /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-1.html (#22647)
    • TIMEOUT [expected FAIL] subtest: Check that popups from a sandboxed iframe escape the sandbox if allow-popups-to-escape-sandbox is used

      Test timed out
      

  • TIMEOUT [expected CRASH] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-2.html (#22667)
  • CRASH [expected TIMEOUT] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-2.html (#22154)
  • OK [expected TIMEOUT] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-3.html (#24066)
    • FAIL [expected NOTRUN] subtest: Check that popups from a sandboxed iframe do not escape the sandbox

      assert_equals: It came from a sandboxed iframe expected "null" but got "http://web-platform.test:8000"
      

  • TIMEOUT [expected OK] /resource-timing/nested-context-navigations-iframe.html (#24311)
    • TIMEOUT [expected PASS] subtest: Test that iframe navigations are not observable by the parent, even after history navigations by the parent

      Test timed out
      

    • NOTRUN [expected PASS] subtest: Test that crossorigin iframe navigations are not observable by the parent, even after history navigations by the parent
    • NOTRUN [expected PASS] subtest: Test that cross-site iframe navigations are not observable by the parent, even after history navigations by the parent
    • NOTRUN [expected PASS] subtest: Test that iframe navigations are not observable by the parent
    • NOTRUN [expected PASS] subtest: Test that crossorigin iframe navigations are not observable by the parent
    • NOTRUN [expected PASS] subtest: Test that cross-site iframe navigations are not observable by the parent
    • NOTRUN [expected PASS] subtest: Test that iframe refreshes are not observable by the parent
    • NOTRUN [expected PASS] subtest: Test that crossorigin iframe refreshes are not observable by the parent
    • NOTRUN [expected PASS] subtest: Test that cross-site iframe refreshes are not observable by the parent
  • TIMEOUT [expected OK] /webstorage/localstorage-about-blank-3P-iframe-opens-3P-window.partitioned.html (#29053)
    • TIMEOUT [expected PASS] subtest: StorageKey: test 3P about:blank window opened from a 3P iframe

      Test timed out
      

  • OK /webxr/light-estimation/xrWebGLBinding_getReflectionCubeMap.https.html (#33760)
    • FAIL [expected PASS] subtest: Test that getReflectionCubeMap returns or throws appropriately without a reflection map. - webgl2

      promise_test: Unhandled rejection with value: object "TypeError: session.requestLightProbe is not a function"
      

  • ERROR [expected OK] /webxr/render_state_update.https.html (#27535)
  • ERROR [expected OK] /workers/constructors/Worker/Worker-constructor.html (#22991)

Copy link

✨ Try run (#11274731178) succeeded.

@mrobinson mrobinson force-pushed the use-native-font-descriptors branch from b02fc1e to c831ea7 Compare October 10, 2024 15:50
@mrobinson mrobinson enabled auto-merge October 10, 2024 15:50
@mrobinson mrobinson added this pull request to the merge queue Oct 10, 2024
@github-merge-queue github-merge-queue bot removed this pull request from the merge queue due to failed status checks Oct 10, 2024
@mrobinson mrobinson force-pushed the use-native-font-descriptors branch from c831ea7 to effe36e Compare October 10, 2024 19:47
@mrobinson mrobinson added T-android Do a try run on Android T-ohos Do a try run on OpenHarmony labels Oct 10, 2024
@github-actions github-actions bot removed T-android Do a try run on Android T-ohos Do a try run on OpenHarmony labels Oct 10, 2024
Copy link

🔨 Triggering try run (#11280960555) for Android, OpenHarmony

Copy link

⚠️ Try run (#11280960555) failed.

System fonts used to be instantiated using the system font loader and
this change restores that behavior. In addition, on macOS and FreeType
platforms font data for system fonts is loaded using memory mapping. The
benefit is that system font loaders typically are able to cache fonts in
system memory (using memory mapping, for instance) and we'd like to load
them in a the way most compatible with other applications.

On my Linux system, this manages to get the overhead of loading a very
large font down from 10ms to approximately 1ms. Subsequent runs show
even less overhead. We've measured similar gains on macOS systems.

Currently, system font data must be loaded into memory manually for
canvas and this is unlikely to change even with a switch to `vello`. The
use of explicit memmory mapping should help in this case -- though it
probably won't be possible to use this properly on macOS and Windows if
we ever want to load fonts from TTCs properly.

Signed-off-by: Martin Robinson <[email protected]>
Co-authored-by: Mukilan Thiyagarajan <[email protected]>
@mrobinson mrobinson force-pushed the use-native-font-descriptors branch from effe36e to 7695f40 Compare October 10, 2024 22:36
@mrobinson mrobinson enabled auto-merge October 10, 2024 22:36
@mrobinson mrobinson added this pull request to the merge queue Oct 10, 2024
Merged via the queue into servo:main with commit 0553789 Oct 11, 2024
14 checks passed
@mrobinson mrobinson deleted the use-native-font-descriptors branch October 11, 2024 00:08
@nicoburns
Copy link
Contributor

The benefit is that system font loaders typically are able to cache fonts in system memory (using memory mapping, for instance) and we'd like to load them in a the way most compatible with other applications.

I can't speak to compatibility with other applications, but my understanding is that the caching benefit applies to any non-system font loaders that use memory mapping too. System fonts are just files on the system, and OS page caches will share memory mapped pages across processes. So a font file memory mapped in a Rust process ought to reuse the same physical memory that the system loader (and other processes using fonts) are using. fontique and fontdb both memory map all the fonts they load.

@mrobinson
Copy link
Member Author

Yes, I suspect that system fonts are usually memory mapped. This change ensures that when system fonts are loaded they are memory mapped with the same configuration as other applications as much as possible.

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.

4 participants