Cairo text rendering cannot be multithreaded after commit 305ebda
Description / Steps to reproduce the issue
On rnote, we use cairo for text rendering (text -> image).
To speed up performance a threadpool queue is used with rayon to do this in a multithreaded manner.
When this happens, a fresh new cairo context is created for each thread run.
Note: we are using piet and piet-cairo for the cairo calls.
But this crashes post https://github.com/msys2/MINGW-packages/commit/305ebda98c3041d9986d6fae498b45d2b2b9f4e8
Issue on rnote's side : https://github.com/flxzt/rnote/issues/1536 Building with the same PKGBUILD as latest on msys minus the D2D patch 0001-DWrite-Get-glyph-bitmap-with-D2D-in-selected-cases.patch works (see https://github.com/flxzt/rnote/pull/1561)
I believe creating multiple threads that all
- create a cairo context
- draw text using the piet
draw_textwill exhibit the issue.
Expected behavior
No crash
Actual behavior
Crashes
Stacktrace :
# Child-SP RetAddr Call Site
00 000000dd`f8bfa868 00007ffa`47e8d008 ntdll!RtlpBreakPointHeap+0x16
01 000000dd`f8bfa870 00007ffa`47ec9980 ntdll!RtlpValidateHeapEntry+0x5d7e8
02 000000dd`f8bfa8b0 00007ffa`47df1d2f ntdll!RtlDebugReAllocateHeap+0x120
03 000000dd`f8bfa930 00007ffa`47df38e3 ntdll!RtlpReAllocateHeap+0x34b
04 000000dd`f8bfaab0 00007ffa`47df36bd ntdll!RtlpReAllocateHeapInternal+0x1c3
05 000000dd`f8bfac10 00007ffa`472b9e07 ntdll!RtlReAllocateHeap+0x7d
06 000000dd`f8bfac50 00007ffa`3f602ce7 msvcrt_7ffa472a0000!realloc+0x57
07 000000dd`f8bfac80 00007ffa`3f603b11 d2d1!CArray<CachedGlyphOutline,CPodTraits<CachedGlyphOutline>,CDefaultAllocator>::EnsureLargerCapacity+0x57
08 000000dd`f8bfacb0 00007ffa`3f603a17 d2d1!TextOutlineCache::AddCachedGlyphInternal+0x91
09 000000dd`f8bface0 00007ffa`3f6040fa d2d1!TextOutlineCacheSubEntry::AddCachedGlyph+0x6f
0a 000000dd`f8bfad20 00007ffa`3f604f68 d2d1!<lambda_655c9741aced175d50f7ec4a5b30606b>::operator()+0xda
0b 000000dd`f8bfada0 00007ffa`3f60477e d2d1!GlyphOutlineRenderer::RenderSingleGlyphs+0x1a4
0c 000000dd`f8bfaec0 00007ffa`3f5c1dab d2d1!GlyphOutlineRenderer::RenderGlyphRun+0x1de
0d 000000dd`f8bfaf50 00007ffa`3f6fd51e d2d1!CHwSurfaceRenderTarget::DrawGlyphRun+0x69b
0e 000000dd`f8bfb1d0 00007ffa`3f5c560e d2d1!WarpRenderTarget::DrawGlyphRun+0x5ae
0f 000000dd`f8bfb420 00007ffa`3f5b3db5 d2d1!BrushRedirectionCompatibleCommand<CCommand_DrawGlyphRun,0>::Execute+0xee
10 000000dd`f8bfb530 00007ffa`3f5f3adf d2d1!CHwSurfaceRenderTarget::ProcessBatch+0x65
11 000000dd`f8bfb580 00007ffa`3f5b4b58 d2d1!CBatchSerializer::FlushInternal+0xcf
12 000000dd`f8bfb610 00007ffa`3f6dec80 d2d1!DrawingContext::Flush+0x88
13 000000dd`f8bfb670 00007ffa`3f6ca16d d2d1!DrawingContext::EndDraw+0x4c
14 000000dd`f8bfb6c0 00007ff9`f5de8bbe d2d1!D2DDeviceContextBase<ID2D1BitmapRenderTarget,ID2D1BitmapRenderTarget,ID2D1DeviceContext6>::EndDraw+0xad
15 000000dd`f8bfb710 00007ff9`f5dea81b libcairo_2!cairo_win32_scaled_font_get_device_to_logical+0xbee
16 000000dd`f8bfb900 00007ff9`f5d8cb78 libcairo_2!cairo_win32_scaled_font_get_device_to_logical+0x284b
17 000000dd`f8bfba30 00007ff9`f5d51849 libcairo_2!cairo_scaled_font_extents+0xcc8
18 000000dd`f8bfbad0 00007ff9`f5da5539 libcairo_2!cairo_font_options_get_custom_palette_color+0x6cd9
19 000000dd`f8bfc440 00007ff9`f5da59a5 libcairo_2!cairo_toy_font_face_get_weight+0x20c9
1a 000000dd`f8bfc530 00007ff9`f5d44ae7 libcairo_2!cairo_toy_font_face_get_weight+0x2535
1b 000000dd`f8bfc5d0 00007ff9`f5d591e2 libcairo_2!cairo_rectangle_list_destroy+0x1d77
1c 000000dd`f8bfc950 00007ff9`f5d9ea89 libcairo_2!cairo_font_options_get_custom_palette_color+0xe672
1d 000000dd`f8bfc9a0 00007ff9`f5d4f5a5 libcairo_2!cairo_surface_has_show_text_glyphs+0x469
1e 000000dd`f8bfca40 00007ff9`f5dace69 libcairo_2!cairo_font_options_get_custom_palette_color+0x4a35
1f 000000dd`f8bfde60 00007ffa`13005b74 libcairo_2!cairo_show_glyphs+0x29
20 000000dd`f8bfdea0 00007ffa`13005dad libpangocairo_1_0_0!pango_cairo_font_map_get_font_type+0x1e54
21 000000dd`f8bfe780 00007ffa`11ff2c0c libpangocairo_1_0_0!pango_cairo_font_map_get_font_type+0x208d
22 000000dd`f8bff090 00007ffa`11ff330c libpango_1_0_0!pango_renderer_draw_glyph_item+0x3c
23 000000dd`f8bff0f0 00007ffa`11ff3abf libpango_1_0_0!pango_renderer_draw_layout_line+0x65c
24 000000dd`f8bff260 00007ffa`13006594 libpango_1_0_0!pango_renderer_draw_layout+0xef
25 000000dd`f8bff340 00007ff6`307bd955 libpangocairo_1_0_0!pango_cairo_show_layout+0xa4
26 (Inline Function) --------`-------- rnote!pangocairo::auto::functions::show_layout+0x8 [C:/testernotebisect/rnote/_mesonbuild/cargo-home/registry/src/index.crates.io-1949cf8c6b5b557f/pangocairo-0.20.7/src/auto/functions.rs @ 142]
27 (Inline Function) --------`-------- rnote!piet_cairo::{impl#0}::draw_text<kurbo::point::Point>+0x1f [C:/testernotebisect/rnote/_mesonbuild/cargo-home/registry/src/index.crates.io-1949cf8c6b5b557f/piet-cairo-0.7.0/src/lib.rs @ 178]
28 000000dd`f8bff380 00007ff6`30842bc7 rnote!rnote_engine::strokes::textstroke::{impl#13}::draw<piet_cairo::CairoRenderContext>+0x1e5 [C:/testernotebisect/rnote/crates/rnote-engine/src/strokes/textstroke.rs @ 534]
29 (Inline Function) --------`-------- rnote!rnote_engine::strokes::content::Content::gen_images::{closure#1}<rnote_engine::strokes::textstroke::TextStroke>+0x8 [C:/testernotebisect/rnote/crates/rnote-engine/src/strokes/content.rs @ 58]
2a (Inline Function) --------`-------- rnote!rnote_engine::render::{impl#9}::gen_with_piet::{closure#0}<rnote_engine::strokes::content::Content::gen_images::{closure_env#1}<rnote_engine::strokes::textstroke::TextStroke>>+0x00000001`4000001b [C:/testernotebisect/rnote/crates/rnote-engine/src/render.rs @ 391]
2b (Inline Function) --------`-------- rnote!rnote_engine::render::Image::gen_with_cairo<rnote_engine::render::{impl#9}::gen_with_piet::{closure_env#0}<rnote_engine::strokes::content::Content::gen_images::{closure_env#1}<rnote_engine::strokes::textstroke::TextStroke>>>+0x00000001`400005f9 [C:/testernotebisect/rnote/crates/rnote-engine/src/render.rs @ 361]
2c (Inline Function) --------`-------- rnote!rnote_engine::render::Image::gen_with_piet<rnote_engine::strokes::content::Content::gen_images::{closure_env#1}<rnote_engine::strokes::textstroke::TextStroke>>+0x00000001`400005f9 [C:/testernotebisect/rnote/crates/rnote-engine/src/render.rs @ 398]
2d (Inline Function) --------`-------- rnote!rnote_engine::strokes::content::Content::gen_images<rnote_engine::strokes::textstroke::TextStroke>+0x00000001`40000a25 [C:/testernotebisect/rnote/crates/rnote-engine/src/strokes/content.rs @ 57]
2e 000000dd`f8bff4c0 00007ff6`30841c2b rnote!rnote_engine::strokes::stroke::{impl#0}::gen_images+0xb27 [C:/testernotebisect/rnote/crates/rnote-engine/src/strokes/stroke.rs @ 57]
2f (Inline Function) --------`-------- rnote!rnote_engine::store::render_comp::{impl#2}::regenerate_rendering_in_viewport_threaded::{closure#0}+0x00000001`40000025 [C:/testernotebisect/rnote/crates/rnote-engine/src/store/render_comp.rs @ 300]
30 (Inline Function) --------`-------- rnote!core::panic::unwind_safe::{impl#25}::call_once<(), rnote_engine::store::render_comp::{impl#2}::regenerate_rendering_in_viewport_threaded::{closure_env#0}>+0x00000001`40000035 [/rustc/1159e78c4747b02ef996e55082b704c09b970588/library/core/src/panic/unwind_safe.rs @ 272]
31 (Inline Function) --------`-------- rnote!std::panicking::catch_unwind::do_call<core::panic::unwind_safe::AssertUnwindSafe<rnote_engine::store::render_comp::{impl#2}::regenerate_rendering_in_viewport_threaded::{closure_env#0}>, ()>+0x00000001`40000035 [/rustc/1159e78c4747b02ef996e55082b704c09b970588/library/std/src/panicking.rs @ 589]
32 (Inline Function) --------`-------- rnote!std::panicking::catch_unwind<(), core::panic::unwind_safe::AssertUnwindSafe<rnote_engine::store::render_comp::{impl#2}::regenerate_rendering_in_viewport_threaded::{closure_env#0}>>+0x00000001`40000035 [/rustc/1159e78c4747b02ef996e55082b704c09b970588/library/std/src/panicking.rs @ 552]
33 (Inline Function) --------`-------- rnote!std::panic::catch_unwind<core::panic::unwind_safe::AssertUnwindSafe<rnote_engine::store::render_comp::{impl#2}::regenerate_rendering_in_viewport_threaded::{closure_env#0}>, ()>+0x00000001`40000035 [/rustc/1159e78c4747b02ef996e55082b704c09b970588/library/std/src/panic.rs @ 359]
34 (Inline Function) --------`-------- rnote!rayon_core::unwind::halt_unwinding<rnote_engine::store::render_comp::{impl#2}::regenerate_rendering_in_viewport_threaded::{closure_env#0}, ()>+0x00000001`40000047 [C:/testernotebisect/rnote/_mesonbuild/cargo-home/registry/src/index.crates.io-1949cf8c6b5b557f/rayon-core-1.12.1/src/unwind.rs @ 17]
35 (Inline Function) --------`-------- rnote!rayon_core::registry::Registry::catch_unwind<rnote_engine::store::render_comp::{impl#2}::regenerate_rendering_in_viewport_threaded::{closure_env#0}>+0x00000001`40000047 [C:/testernotebisect/rnote/_mesonbuild/cargo-home/registry/src/index.crates.io-1949cf8c6b5b557f/rayon-core-1.12.1/src/registry.rs @ 367]
36 (Inline Function) --------`-------- rnote!rayon_core::spawn::spawn_job::{closure#0}<rnote_engine::store::render_comp::{impl#2}::regenerate_rendering_in_viewport_threaded::{closure_env#0}>+0x00000001`40000083 [C:/testernotebisect/rnote/_mesonbuild/cargo-home/registry/src/index.crates.io-1949cf8c6b5b557f/rayon-core-1.12.1/src/spawn/mod.rs @ 97]
37 000000dd`f8bff6b0 00007ff6`3062a6db rnote!rayon_core::job::{impl#6}::execute<rayon_core::spawn::spawn_job::{closure_env#0}<rnote_engine::store::render_comp::{impl#2}::regenerate_rendering_in_viewport_threaded::{closure_env#0}>>+0xab [C:/testernotebisect/rnote/_mesonbuild/cargo-home/registry/src/index.crates.io-1949cf8c6b5b557f/rayon-core-1.12.1/src/job.rs @ 169]
38 (Inline Function) --------`-------- rnote!rayon_core::job::JobRef::execute+0x5 [C:/testernotebisect/rnote/_mesonbuild/cargo-home/registry/src/index.crates.io-1949cf8c6b5b557f/rayon-core-1.12.1/src/job.rs @ 64]
39 (Inline Function) --------`-------- rnote!rayon_core::registry::WorkerThread::execute+0x5 [C:/testernotebisect/rnote/_mesonbuild/cargo-home/registry/src/index.crates.io-1949cf8c6b5b557f/rayon-core-1.12.1/src/registry.rs @ 860]
3a 000000dd`f8bff8d0 00007ff6`30629989 rnote!rayon_core::registry::WorkerThread::wait_until_cold+0x53b [C:/testernotebisect/rnote/_mesonbuild/cargo-home/registry/src/index.crates.io-1949cf8c6b5b557f/rayon-core-1.12.1/src/registry.rs @ 782]
3b (Inline Function) --------`-------- rnote!rayon_core::registry::WorkerThread::wait_until<rayon_core::latch::OnceLatch>+0x00000001`40000098 [C:/testernotebisect/rnote/_mesonbuild/cargo-home/registry/src/index.crates.io-1949cf8c6b5b557f/rayon-core-1.12.1/src/registry.rs @ 769]
3c (Inline Function) --------`-------- rnote!rayon_core::registry::WorkerThread::wait_until_out_of_work+0x00000001`400000c3 [C:/testernotebisect/rnote/_mesonbuild/cargo-home/registry/src/index.crates.io-1949cf8c6b5b557f/rayon-core-1.12.1/src/registry.rs @ 818]
3d (Inline Function) --------`-------- rnote!rayon_core::registry::main_loop+0x00000001`40000133 [C:/testernotebisect/rnote/_mesonbuild/cargo-home/registry/src/index.crates.io-1949cf8c6b5b557f/rayon-core-1.12.1/src/registry.rs @ 923]
3e (Inline Function) --------`-------- rnote!rayon_core::registry::ThreadBuilder::run+0x00000001`40000133 [C:/testernotebisect/rnote/_mesonbuild/cargo-home/registry/src/index.crates.io-1949cf8c6b5b557f/rayon-core-1.12.1/src/registry.rs @ 53]
3f (Inline Function) --------`-------- rnote!rayon_core::registry::{impl#2}::spawn::{closure#0}+0x00000001`40000133 [C:/testernotebisect/rnote/_mesonbuild/cargo-home/registry/src/index.crates.io-1949cf8c6b5b557f/rayon-core-1.12.1/src/registry.rs @ 98]
40 000000dd`f8bff980 00007ff6`3062c5b5 rnote!std::sys::backtrace::__rust_begin_short_backtrace<rayon_core::registry::{impl#2}::spawn::{closure_env#0}, ()>+0x159 [/rustc/1159e78c4747b02ef996e55082b704c09b970588/library/std/src/sys/backtrace.rs @ 158]
41 (Inline Function) --------`-------- rnote!std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure#0}<rayon_core::registry::{impl#2}::spawn::{closure_env#0}, ()>+0x00000001`40000017 [/rustc/1159e78c4747b02ef996e55082b704c09b970588/library/std/src/thread/mod.rs @ 559]
42 (Inline Function) --------`-------- rnote!core::panic::unwind_safe::{impl#25}::call_once<(), std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure_env#0}<rayon_core::registry::{impl#2}::spawn::{closure_env#0}, ()>>+0x00000001`40000017 [/rustc/1159e78c4747b02ef996e55082b704c09b970588/library/core/src/panic/unwind_safe.rs @ 272]
43 (Inline Function) --------`-------- rnote!std::panicking::catch_unwind::do_call<core::panic::unwind_safe::AssertUnwindSafe<std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure_env#0}<rayon_core::registry::{impl#2}::spawn::{closure_env#0}, ()>>, ()>+0x00000001`4000001c [/rustc/1159e78c4747b02ef996e55082b704c09b970588/library/std/src/panicking.rs @ 589]
44 (Inline Function) --------`-------- rnote!std::panicking::catch_unwind<(), core::panic::unwind_safe::AssertUnwindSafe<std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure_env#0}<rayon_core::registry::{impl#2}::spawn::{closure_env#0}, ()>>>+0x00000001`4000001c [/rustc/1159e78c4747b02ef996e55082b704c09b970588/library/std/src/panicking.rs @ 552]
45 (Inline Function) --------`-------- rnote!std::panic::catch_unwind<core::panic::unwind_safe::AssertUnwindSafe<std::thread::{impl#0}::spawn_unchecked_::{closure#1}::{closure_env#0}<rayon_core::registry::{impl#2}::spawn::{closure_env#0}, ()>>, ()>+0x00000001`4000001c [/rustc/1159e78c4747b02ef996e55082b704c09b970588/library/std/src/panic.rs @ 359]
46 (Inline Function) --------`-------- rnote!std::thread::{impl#0}::spawn_unchecked_::{closure#1}<rayon_core::registry::{impl#2}::spawn::{closure_env#0}, ()>+0x00000001`40000117 [/rustc/1159e78c4747b02ef996e55082b704c09b970588/library/std/src/thread/mod.rs @ 557]
47 000000dd`f8bffc20 00007ff6`30b9ce0d rnote!core::ops::function::FnOnce::call_once<std::thread::{impl#0}::spawn_unchecked_::{closure_env#1}<rayon_core::registry::{impl#2}::spawn::{closure_env#0}, ()>, ()>+0x135 [/rustc/1159e78c4747b02ef996e55082b704c09b970588/library/core/src/ops/function.rs @ 253]
48 (Inline Function) --------`-------- rnote!alloc::boxed::{impl#28}::call_once<(), dyn core::ops::function::FnOnce<(), Output=()>, alloc::alloc::Global>+0x00000001`40000006 [/rustc/1159e78c4747b02ef996e55082b704c09b970588/library/alloc/src/boxed.rs @ 1971]
49 (Inline Function) --------`-------- rnote!alloc::boxed::{impl#28}::call_once<(), alloc::boxed::Box<dyn core::ops::function::FnOnce<(), Output=()>, alloc::alloc::Global>, alloc::alloc::Global>+0x00000001`4000000d [/rustc/1159e78c4747b02ef996e55082b704c09b970588/library/alloc/src/boxed.rs @ 1971]
4a 000000dd`f8bffdb0 00007ffa`47507374 rnote!std::sys::pal::windows::thread::{impl#0}::new::thread_start+0x2d [/rustc/1159e78c4747b02ef996e55082b704c09b970588/library/std/src/sys/pal/windows/thread.rs @ 60]
4b 000000dd`f8bffe00 00007ffa`47e1cc91 KERNEL32!BaseThreadInitThunk+0x14
4c 000000dd`f8bffe30 00000000`00000000 ntdll!RtlUserThreadStart+0x21
There are also some occasional errors popping
IWICBitmapSource::CopyPixels() failed: There is already a read or write lock pending.
Verification
- [x] I have verified that my MSYS2 is up-to-date before submitting the report (see https://www.msys2.org/docs/updating/)
Windows Version
MINGW64_NT-10.0-26100
MINGW environments affected
- [x] MINGW64
- [ ] MINGW32
- [ ] UCRT64
- [ ] CLANG64
- [ ] CLANGARM64
Are you willing to submit a PR?
No response
Hello @Doublonmousse!
The underlying issue is https://gitlab.freedesktop.org/cairo/cairo/-/issues/886. DWrite support for Cairo was imported from Firefox, which presumably used Cairo from a single thread only, so it uses the single-threaded D2D1 factory.
Technically Cairo was unsafe even before the patch, but now uses D2D even more...
I'll open a PR to fix this issue!
I'll open a PR to fix this issue!
Thanks.
For reference, that's the corresponding PR: https://gitlab.freedesktop.org/cairo/cairo/-/merge_requests/641
Can the DWrite backend be deactivated through an environment variable in the meantime ?
I know it can be excluded from the build with a meson configure but it'd be a little simpler not to have to compile cairo to workaround the issue
Can the DWrite backend be deactivated through an environment variable in the meantime ?
Looks like piet uses PangoCairo, so you might set the environment variable PANGOCAIRO_BACKEND=fc, where "fc" stands for "fontconfig". Note however that this affects GTK as well, and the naming of fonts might change. I don't know if font family / face names are hardcoded in CSS or present in user configuration files.
@striezel yes, that would help but there are other things which are not thread safe. I should open a final MR next week ;)
Can the DWrite backend be deactivated through an environment variable in the meantime ?
Looks like
pietusesPangoCairo, so you might set the environment variablePANGOCAIRO_BACKEND=fc, where "fc" stands for "fontconfig". Note however that this affects GTK as well, and the naming of fonts might change. I don't know if font family / face names are hardcoded in CSS or present in user configuration files.
Thanks !
Setting the env variable in powershell with
$env:PANGOCAIRO_BACKEND="fc"
& 'C:\Program Files\Rnote\bin\rnote.exe'
works (no crash). The only side effect seems to be on rendered emojis in rnote.
| without env variable | with fc |
|---|---|