Open Bug 1936605 Opened 2 months ago Updated 7 days ago

Very slow performance when zooming and scrolling PDF in Fenix

Categories

(GeckoView :: PDF Viewer, defect)

defect

Tracking

(Performance Impact:medium)

Performance Impact medium

People

(Reporter: denispal, Unassigned)

References

(Blocks 2 open bugs)

Details

Attach (recommended) or Link to PDF file here:

Steps to reproduce the problem:

  1. Load https://turtlejacks.com/wp-content/uploads/2021/05/TJS-11514-Nutritional-Menu-Guide-Update-2024.pdf in Fenix
  2. Zoom in and scroll around. The content takes a very long time to update and refresh.

Profile: https://share.firefox.dev/3OMqTGC

Blocks: perf-android
Performance Impact: --- → medium

I can reproduce this using other PDF's as well after zooming in. Here is another example PDF https://assets.metrolinx.com/image/upload/v1729508831/Documents/GO/full-schedules/FS03112024/TABLE01.pdf Performance is very bad after zooming in enough.

Another profile using the gecko profiler this time: https://share.firefox.dev/3VAOcqu

A lot of CPU-side copies and texture upload. Lee, can you take a look?

Component: PDF Viewer → Graphics: Canvas2D
Flags: needinfo?(lsalzman)
Product: Firefox → Core
Version: unspecified → Trunk

Part of the problem seems to be the sheer amount of repaints being triggered. This causes a lot of shared memory traffic to shuttle things to the compositor every time, and I don't think we ever found a great way to make this better yet.

Marco/Calixte, do you know why pdf.js is triggering so many repaints in the middle of drawing this PDF?

Flags: needinfo?(mcastelluccio)
Flags: needinfo?(lsalzman)
Flags: needinfo?(cdenizet)
Severity: -- → S3

How is the performance on Chrome?

Chrome just opens the default PDF viewer app. Actually even after downloading the file in Firefox, after I click open Firefox just ends up opening it again inside the browser which is also a problem.

We discussed this bug in our android performance meeting. Considering the performance impact here is quite severe and will likely affect anyone reading a PDF (via reading a menu at a restaurant, or train schedule, etc) we thought it's best to bump this up to S2.

Severity: S3 → S2

The performance is significantly better when opening the PDF through https://mozilla.github.io/pdf.js/web/viewer.html link directly instead of opening the PDF through the browser. I took a profile of each situation:

Open PDF in App: https://share.firefox.dev/3P1myzp
Open PDF through link: https://share.firefox.dev/3ZJEweu

Jeff helped look at the profiles and indicated that we might not be using the GPU canvas when opening the PDF in the app.

From my perspective, the pdf seems to be normal.
While zooming, the canvas is scaled in using CSS and once zooming is done, after a small delay, the canvas is redrawn.
The viewer in Fenix is disabling hardware acceleration (i.e. we pass willReadFrequently: true when getting the canvas context) when the viewer in the link isn't.
We decided to disable hardware acceleration in bug 1902012 because of wrong rendering or poor performances issues.
Right now it's disabled on every platform, :jrmuizel, do you think we should enable it for Fenix only ? or could we meet the same kind of issues we already saw on mac (perf issues with filters) or on linux (bad rendering) ?

Flags: needinfo?(mcastelluccio)
Flags: needinfo?(jmuizelaar)
Flags: needinfo?(cdenizet)
Blocks: 1894804

I think we need to better understand the performance problem that's happening in software mode before we consider changing to gpu acceleration.

Performance is going to be a lot more unpredictable with gpu acceleration so my preference is to make sure we're doing things right with gpu acceleration disabled before we consider turning it back on.

Flags: needinfo?(jmuizelaar)

30% of the main thread time appears to be in this stack:

_memcpy_aarch64_simd [libc.so]
SkBitmap::writePixels(SkPixmap const&, int, int) [gfx/skia/skia/src/core/SkBitmap.cpp]
SkBitmapDevice::onWritePixels(SkPixmap const&, int, int) [gfx/skia/skia/src/core/SkBitmapDevice.cpp]
SkDevice::writePixels(SkPixmap const&, int, int) [gfx/skia/skia/src/core/SkDevice.h]
SkCanvas::writePixels(SkImageInfo const&, void const*, unsigned long, int, int) [gfx/skia/skia/src/core/SkCanvas.cpp]
mozilla::gfx::DrawTargetSkia::CopySurface(mozilla::gfx::SourceSurface*, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&, mozilla::gfx::IntPointTyped<mozilla::gfx::UnknownUnits> const&) [gfx/2d/DrawTargetSkia.cpp]
mozilla::layers::TextureClient::CopyToTextureClient(mozilla::layers::TextureClient*, mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const*, mozilla::gfx::IntPointTyped<mozilla::gfx::UnknownUnits> const*) [gfx/layers/client/TextureClient.cpp]
mozilla::layers::PersistentBufferProviderShared::BorrowDrawTarget(mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&) [gfx/layers/PersistentBufferProvider.cpp]
mozilla::dom::CanvasRenderingContext2D::BorrowTarget(mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits> const&, bool) [dom/canvas/CanvasRenderingContext2D.cpp]
mozilla::dom::CanvasRenderingContext2D::EnsureTarget(mozilla::ErrorResult&, mozilla::gfx::RectTyped<mozilla::gfx::UnknownUnits, float> const*, bool, bool) [dom/canvas/CanvasRenderingContext2D.cpp]
mozilla::dom::CanvasRenderingContext2D::EnsureTarget(mozilla::gfx::RectTyped<mozilla::gfx::UnknownUnits, float> const*, bool, bool) [dom/canvas/CanvasRenderingContext2D.h]
mozilla::dom::CanvasRenderingContext2D::Save() [dom/canvas/CanvasRenderingContext2D.cpp]
mozilla::dom::CanvasRenderingContext2D_Binding::save(JSContext*, JS::Handle<JSObject*>, void*, JSJitMethodCallArgs const&) [dom/bindings/CanvasRenderingContext2DBinding.cpp]

Perhaps we can avoid copying the previous buffer's contents somehow. Calixte, do you know where this canvas.save() call is coming from in pdf.js?

Flags: needinfo?(cdenizet)

According to the profile, the ctx.save are called from showText:
https://github.com/mozilla/pdf.js/blob/877f69886c3e000fb7be2d82d536476032f2b8e4/src/display/canvas.js#L2168
(there are few other save in this function but there aren't used for this pdf).
It's called 1801 times.

So as said before, this pdf has nothing special except the page dimension (11 x 31.5 in).
I don't see any perf problems on desktop, so it seems to be specific to Android, or am I missing something ?

That said, in reading the docs here:
https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/save
I'm not sure to understand (I'm not a gfx expert though...) why we need to do some pixel stuff when we just want to save some basic properties.

Flags: needinfo?(cdenizet)
See Also: → 1898391
See Also: → 1896219
See Also: → 1890537
See Also: → 1838865

(In reply to Denis Palmeiro [:denispal] from comment #8)

The performance is significantly better when opening the PDF through https://mozilla.github.io/pdf.js/web/viewer.html link directly instead of opening the PDF through the browser. I took a profile of each situation:

Open PDF in App: https://share.firefox.dev/3P1myzp
Open PDF through link: https://share.firefox.dev/3ZJEweu

Jeff helped look at the profiles and indicated that we might not be using the GPU canvas when opening the PDF in the app.

Do you still see differences between opening the PDF directly through the browser and opening it in https://mozilla.github.io/pdf.js/web/viewer.html?
What does the performance look like in Chrome when opening the PDF in https://mozilla.github.io/pdf.js/web/viewer.html?

Flags: needinfo?(dpalmeiro)

If you look at https://share.firefox.dev/3VAOcqu it looks like pdf.js is painting every requestAnimationFrame. This causes us to do an upload of what might be a large canvas every requestAnimationFrame. The solution here is likely that pdf.js needs to break the pdf into tiles of one canvas each and then paint them less aggressively.

Component: Graphics: Canvas2D → PDF Viewer
Product: Core → Firefox

Ah yes, during zooming you just want the zooming itself to be responsive, you don't want it to be rendered "sharp" at every zoom level.
I agree, when the zoom level changes during pinch zooming, the existing canvas should be scaled. The actual repainting into that canvas should happen at a much lower rate.

And tiling the canvas would remove the cost from uploading texture bytes for the parts of the canvas that are outside the screen.

Scaling the existing content during zooming should also fix the main thread performance, where the bulk of the time is spent allocating and copy data into new backbuffers due to the current frontbuffer still be read locked. Not having to make a new backbuffer every frame during zooming would be massive.

We have a similar thing in webrender called "low quality pinch zoom". We scale the existing content whilst a pinch zoom is active, then render again at the new scale when the zoom ends. Additionally we render at the new scale during an active pinch zoom if the zoom level doubles or halves -- when zooming in this prevents the content getting too blurry. Importantly, when zooming out this prevents having a huge number of small tiles on screen at once, which caused serious performance issues. Something to look out for.

As a secondary thought, in the layers days BufferTextureHost used to be able to drop its read lock once it had uploaded to a GL texture. I don't believe we do that for webrender. If we still see a lot of backbuffer allocations occuring after implementing tiling and scaling-zoom, we could perhaps look in to that too.

Product: Firefox → GeckoView

Thanks for the suggestions, we'll see if we can implement the slow refresh first. Tiling (https://github.com/mozilla/pdf.js/issues/6419) will require quite some work though, so we need to weigh it against other priorities.

(In reply to Marco Castelluccio [:marco] from comment #13)

Do you still see differences between opening the PDF directly through the browser and opening it in https://mozilla.github.io/pdf.js/web/viewer.html?
What does the performance look like in Chrome when opening the PDF in https://mozilla.github.io/pdf.js/web/viewer.html?

This feels better now than when I first opened this bug, but still a lot of jank and stuttering when scrolling quickly. Performance seems similar between the viewer and in-app however. Chrome does feel smoother when opening the PDF in the viewer, however.

Flags: needinfo?(dpalmeiro)
You need to log in before you can comment on or make changes to this bug.