Closed Bug 1192919 Opened 10 years ago Closed 10 years ago

Need to schedule paints for scrolling inside inactive layers

Categories

(Core :: Panning and Zooming, defect)

defect
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla46
Tracking Status
firefox43 --- affected
firefox46 --- fixed

People

(Reporter: mstange, Assigned: dvander)

References

Details

(Keywords: regression)

Attachments

(8 files, 3 obsolete files)

7.75 KB, patch
kats
: review+
Details | Diff | Splinter Review
4.06 KB, patch
kats
: review+
Details | Diff | Splinter Review
3.31 KB, patch
kats
: review+
Details | Diff | Splinter Review
9.70 KB, patch
kats
: review+
Details | Diff | Splinter Review
12.08 KB, patch
kats
: review+
Details | Diff | Splinter Review
37.33 KB, patch
kats
: review+
Details | Diff | Splinter Review
9.66 KB, patch
kats
: review+
Details | Diff | Splinter Review
4.89 KB, patch
kats
: review+
Details | Diff | Splinter Review
STR: 1. Put lots and lots of text into your URL bar. 2. Using your touchpad that supports horizontal scrolling, scroll to the center of the URL bar textbox and wait for the parent process refresh driver to idle. 3. Scroll left and right. Expected results: Good scrolling. Actual results: Bad scrolling. The URL bar textbox has an SVG mask applied to it, which we don't yet support on the compositor, so scrolling happens by APZ "scrolling" the ScrollInfoLayer APZC and syncing the scroll position to the main thread.
Isn't what you described in comment 0 the desired behavior? I'm not sure what you mean by the text in the bug summary and how that would improve things.
Flags: needinfo?(mstange)
Oh I guess this is one of the scenarios referred to in bug 1187432 comment 7 that was made worse?
Exactly. I didn't do a very good job of describing the problem, did I? The problem is that we don't support SVG masks and filters in the compositor, so APZ can't async scroll anything. We need a main thread paint in order to reflect any scrolling to the screen. And with bug 1187432, we only do main thread paints when the display port changes.
Flags: needinfo?(mstange)
Assignee: nobody → dvander
Status: NEW → ASSIGNED
This puts a bit on FrameMetrics telling us whether or not it's for a ScrollInfoLayer.
Attachment #8695702 - Flags: review?(bugmail.mozilla)
Attached patch part 2, minimize displayports (obsolete) — Splinter Review
This patch forces us to have empty margins for frames with ScrollInfoLayers, so we don't waste time painting extra stuff that will never be scrolled asynchronously. GetDisplayPortFromMarginsData does some alignment that can expand the displayport further - this patch bypasses that as well.
Attachment #8695706 - Flags: review?(bugmail.mozilla)
Attached patch part 3, fix throttling (obsolete) — Splinter Review
Normally when scrolling we throttle paint requests, which is okay since while layout is idle, APZ can still scroll content in the displayport. With scrollinfo layers, APZ has no content to scroll, but it is still responsible for driving animation. In this scenario, we need the paint requests to arrive as soon as possible. Currently it looks like these paint requests are getting throttled, so animation is extremely janky. Normally the first paint request would change the displayport, but not the scroll position, and this would cause a repaint. With empty margins and no scroll position change, we won't get any layers update from content, and the throttler keeps cancelling and accepting new requests without ever dispatching any. This patch just stops throttling when using scrollinfo layers, and the scrolling quality on bhphotovideo.com and loop.ableton.com is *much* better. I'm not sure if this is the right approach though. It's possible we just need to unthrottle a few requests, or synchronize it with the compositor - or, defer the animation to the main thread entirely (but this wouldn't work with apz-only animations).
Attachment #8695761 - Flags: review?(bugmail.mozilla)
Comment on attachment 8695702 [details] [diff] [review] part 1, annotate FrameMetrics Review of attachment 8695702 [details] [diff] [review]: ----------------------------------------------------------------- ::: gfx/layers/apz/src/AsyncPanZoomController.cpp @@ +3216,1 @@ > mFrameMetrics.SetLineScrollAmount(aLayerMetrics.GetLineScrollAmount()); Move this down to the end of the pile so that it's in roughly the same order as everywhere else
Attachment #8695702 - Flags: review?(bugmail.mozilla) → review+
Comment on attachment 8695702 [details] [diff] [review] part 1, annotate FrameMetrics Review of attachment 8695702 [details] [diff] [review]: ----------------------------------------------------------------- Actually this would probably be more accurate as mIsScrollInfoLayer than mHasScrollInfoLayer.
Comment on attachment 8695706 [details] [diff] [review] part 2, minimize displayports Review of attachment 8695706 [details] [diff] [review]: ----------------------------------------------------------------- Minusing until we agree on the nsLayoutUtils change. ::: gfx/layers/apz/util/APZCCallbackHelper.cpp @@ +42,5 @@ > { > + if (aFrameMetrics.HasScrollInfoLayer()) { > + // Don't perform any adjustment - we don't want a large displayport > + // if the scroll cannot be performed asynchronously at all. > + return; So the code in this function doesn't actually increase the *size* of the displayport, but it does adjust it to account for any residual difference between the APZ scroll offset and the content scroll offset. There are two main reasons this residual difference is nonzero: 1) If the APZ scroll offset is not perfectly on an app-unit boundary, then it will be slightly different from the content scroll offset which is always an integer number of app units 2) In the case of overflow:hidden but zoomable top-level frames, we allow the user to zoom and then pan around in the APZ, without changing the scroll offset in content. (This was added in bug 975962). There are probably some other conditions where this might happen as well, but in all cases it's the difference between the APZ scroll offset and the content scroll offset. The important thing here is that for scrollinfo layers, the APZ scroll offset doesn't really matter because it's not user-visible, so we want the displayport margins to apply to the content scroll offset. All this to say that the code you're adding here is correct, but the comment is wrong. Please reword it to say: // In cases where the APZ scroll offset is different from the // content scroll offset, we want to interpret the margins // as relative to the APZ scroll offset except when the frame // is not scrollable by APZ. Therefore, if the layer is a scroll // info layer, we leave the margins as-is and they will be // interpreted as relative to the content scroll offset. Also, ironic that you fixed the indent in the existing code but not in the new code you added :p ::: layout/base/nsLayoutUtils.cpp @@ +1013,5 @@ > > + // If we're not tiling and our margins are empty, we probably don't want > + // extra displayport at all so don't inflate the screen rect. > + if (gfxPrefs::LayersTilesEnabled() || aMarginsData->mMargins != ScreenMargin()) { > + // Round-out the display port to the nearest alignment (tiles) I'm not sure if I agree with this. In bug 1204136 BenWa specifically made the displayport align to "virtual" 128x128 tiles even if tiling was disabled, so that the perf characteristics would be consistent. I think this change undoes some of that work, and for a negligible benefit. If we keep this code as it was originally, then at worst we'll pad out the displayport to the next tile which shouldn't be that much extra painting. The bulk of the displayport area was eliminated in the change you made to AsyncPanZoomController.cpp in this patch. And as the user scrolls around on this scrollinfo layer, *not* having this tile alignment will cause a lot of actual repaints, but having the tile aligned displayport should only cause an actual repaint as the user rolls onto another tile. This will give us perf characteristics similar to everything else, and I think we should keep it as-is.
Attachment #8695706 - Flags: review?(bugmail.mozilla) → review-
Comment on attachment 8695761 [details] [diff] [review] part 3, fix throttling Review of attachment 8695761 [details] [diff] [review]: ----------------------------------------------------------------- ::: gfx/layers/apz/src/AsyncPanZoomController.cpp @@ +2831,1 @@ > mPaintThrottler->PostTask( So on the one hand I see that we want to be sending these repaint requests more frequently. But I'm not sure this is the right place to do this. The PaintThrottler throttles the paint requests so that there's only one inflight at a time. If we bypass this, we could flood the content process with repaint requests (which don't get coalesced in IPDL) which is going to create a ton of work for the content process, much more than it needs to do. It could end up lagging behind, doing stale paints when the compositor is already far ahead. I imagine that with this patch it *looks* smoother because the main thread is painting every single frame, but the entire animation will run slower (for the same reason). That might be right tradeoff but I'm not convinced, particularly since it means a lot of extra work for the content process. What I'd like you to try first is instead of removing this throttling, drop the APZPanRepaintInterval, APZFlingRepaintInterval, and APZSmoothScrollRepaintInterval prefs to 0 (or comment out the code with the TimeSinceLastRequest calls) and see if that also results in better scrolling on bhphotovideo and loop.ableton.com. If it does, we can make that repaint interval conditional on !HasScrollInfoLayer and keep this throttling. Note that you'll need the SchedulePaint you added to APZCCallbackHelper still. ::: gfx/layers/apz/util/APZCCallbackHelper.cpp @@ +148,5 @@ > > + // If APZ cannot scroll this frame, force a paint. > + if (aMetrics.HasScrollInfoLayer()) { > + if (nsIFrame* frame = aContent->GetPrimaryFrame()) { > + frame->SchedulePaint(); I think this belongs inside the |if (scrollUpdated)| condition above, after the call to AdjustDisplayPortForScrollDelta. In fact, you could combine this with the part 2 patch, so that the code looks like this: if (scrollUpdated) { if (aMetrics.HasScrollInfoLayer()) { // the code comment from comment 9 if (nsIFrame* frame = aContent->GetPrimaryFrame()) { frame->SchedulePaint(); } } else { AdjustDisplayPortForScrollDelta(...) } } else { ... } (Ideally we could handle this by just passing a RepaintMode::Repaint as the last argument at [1] but it looks like the nsLayoutUtils function will early-exit if the margins didn't change, so that won't work.) [1] https://dxr.mozilla.org/mozilla-central/rev/528fc18a86d3f6e6bec37190522a46853a846665/gfx/layers/apz/util/APZCCallbackHelper.cpp#167 ::: layout/generic/nsGfxScrollFrame.cpp @@ +2526,5 @@ > + // If we don't have displayport margins, assume we're trying to minimize > + // the displayport, in which case we should always repaint since APZ has > + // nothing to asynchronously scroll. > + if (!displayPort.IsEqualEdges(oldDisplayPort) || > + nsLayoutUtils::HasZeroDisplayPortMargins(mOuter->GetContent())) I'm not sure why this would be needed if we are already calling SchedulePaint from APZCCallbackHelper.cpp ::: layout/xul/nsSliderFrame.cpp @@ +766,1 @@ > SchedulePaint(); Ditto, not sure why this is needed
Attachment #8695761 - Flags: review?(bugmail.mozilla)
I was looking at this code for another bug and realized that there's another reason we can't just not throttle, at least not the way you did in the patch. It looks like when we send an unthrottled request (which we do from only one place currently, in FlushRepaintForNewInputBlock) we have to do a bunch of other things as well, such as cancel the pending tasks in the throttler and update mLastPaintRequestMetrics. Otherwise things will get badly out of sync. This code should probably be moved into RequestContentRepaint and made conditional on !throttled, so that we don't have to do it at call sites but I just wanted to document that here.
Attachment #8695706 - Attachment is obsolete: true
Attachment #8696770 - Flags: review?(bugmail.mozilla)
Comment on attachment 8696770 [details] [diff] [review] part 2, minimize displayports Review of attachment 8696770 [details] [diff] [review]: ----------------------------------------------------------------- This looks good, thanks
Attachment #8696770 - Flags: review?(bugmail.mozilla) → review+
Attached patch part 3, fix throttling v2 (obsolete) — Splinter Review
Drop the repaint interval, and dispatch throttled paint tasks on each vsync. We reasoned this is okay since (1) content is repainting on vsync anyway, and (2) content is also eliding paints that don't change the displayport.
Attachment #8695761 - Attachment is obsolete: true
Attachment #8697383 - Flags: feedback?(bugmail.mozilla)
Always schedule paints on the thread main when we're updating a scroll info layer. With all of these patches, scrolling loop.abletoon is very smooth.
Attachment #8697385 - Flags: review?(bugmail.mozilla)
Comment on attachment 8697383 [details] [diff] [review] part 3, fix throttling v2 Review of attachment 8697383 [details] [diff] [review]: ----------------------------------------------------------------- This looks like it's on the right track. After thinking it over some more and talking it over to Botond we came to the conclusion that it should be ok to rip out the TaskThrottler entirely. The repaint requests can get sent unthrottled always to the content process and shouldn't significantly increase the work on the main thread, because the repaints themselves are still batched up based on vsync. If our repaint requests are triggering flushes or any other significant work on the main thread then we should stop that. The other main job of the TaskThrottler is to compute the estimated paint duration to use as part of the displayport heuristic. Botond and I talked about it and we think it makes sense to just disable this heuristic for now, and reimplement it later using a different method (passing around repaint request sequence numbers) if it will help. (We will need to land the checkerboard recording stuff first, to evaluate how much this would actually help). So I'm ok with flipping apz.use_paint_duration to false everywhere and just using the hard-coded alternate 50ms time in CalculatePendingDisplayPort.
Attachment #8697383 - Flags: feedback?(bugmail.mozilla) → feedback+
Attachment #8697385 - Flags: review?(bugmail.mozilla) → review+
Attachment #8697385 - Attachment description: part 4, schedule paints → part 3, schedule paints
This removes code related to estimating the APZ paint duration, and turns off the pref that enabled it.
Attachment #8697383 - Attachment is obsolete: true
Attachment #8697648 - Flags: review?(bugmail.mozilla)
Comment on attachment 8697648 [details] [diff] [review] part 4, disable paint duration code Review of attachment 8697648 [details] [diff] [review]: ----------------------------------------------------------------- ::: modules/libpref/init/all.js @@ -595,5 @@ > pref("apz.smooth_scroll_repaint_interval", 16); > pref("apz.test.logging_enabled", false); > pref("apz.touch_start_tolerance", "0.1"); > pref("apz.touch_move_tolerance", "0.03"); > -pref("apz.use_paint_duration", true); If we're ripping this out entirely, remove it from gfxPrefs.h and the documentation at the top of AsyncPanZoomController.cpp as well.
Attachment #8697648 - Flags: review?(bugmail.mozilla) → review+
This patch doesn't remove the throttler - but it removes usage in AsyncPanZoomController. Instead paints are dispatched immediately. I think this also obsoletes mLastPaintRequestMetrics. Except for an edge case where there is no GeckoContentController and mLastDispatchedPaintMetrics won't be updated, it is otherwise superceded. I'll do the bulk of the TaskThrottler code removal in another patch so it's easier to review.
Attachment #8697652 - Flags: review?(bugmail.mozilla)
Comment on attachment 8697652 [details] [diff] [review] part 5, don't throttle Review of attachment 8697652 [details] [diff] [review]: ----------------------------------------------------------------- This looks good. Two questions: 1) Does this still result in good scrolling on loop.ableton? 2) Does this cause test failures? Please do a try push with at least linux,b2g,fennec. There's a pretty high risk for failures with this patch, particularly in some of the gtests.
Attachment #8697652 - Flags: review?(bugmail.mozilla) → review+
Yup, scrolling still looks quite good on loop.ableton - well, as good as it's going to get without compositor help. Will post a try run shortly since this does seem risky.
assuming part 5 passes (or can be made to pass) try...
Attachment #8697674 - Flags: review?(bugmail.mozilla)
Attachment #8697674 - Flags: review?(bugmail.mozilla) → review+
Comment on attachment 8697652 [details] [diff] [review] part 5, don't throttle Review of attachment 8697652 [details] [diff] [review]: ----------------------------------------------------------------- ::: gfx/layers/apz/src/AsyncPanZoomController.cpp @@ +2798,5 @@ > > // If we're trying to paint what we already think is painted, discard this > // request since it's a pointless paint. > + const FrameMetrics& previous = mLastDispatchedPaintMetrics; > + ScreenMargin marginDelta = (previous.GetDisplayPortMargins() Actually I just realized that using mLastDispatchedPaintMetrics here as the compare-to metrics might result in badness. Consider the case where: 1) APZ sends a repaint request with metrics A 2) APZ sends a repaint request with metrics B 3) NotifyLayersUpdated gets called with metrics A 4) Content processes the metrics from (1) and (2), leave it with metrics B 5) APZ tries to send a repaint request with metrics A In this sequence, at step 3, mLastDispatchedPaintMetrics could get overwritten from B back to A, which would make step 5 short-circuit and not send it. Content would have B as the latest metrics and APZ would have A as the latest metrics, and that would leave them out of sync. I'm not sure if this will actually happen in practice, but I think it could - we've seen similar issues in the past. I'd recommend renaming mLastDispatchedPaintMetrics to something more representative like mExpectedGeckoMetrics (as in, what APZ expects the gecko metrics to be), and bring back mLastPaintRequestMetrics just to track the last metrics that actually got sent in RequestContentRepaint. The DispatchRepaintRequest would update both mExpectedGeckoMetrics and mLastPaintRequestMetrics. I'm ok with doing this in an extra patch at the end of the queue so you don't have to rebase.
Hrm, yeah. This is a partial revert of part 5, with the renaming suggested. I'll fold this in when landing.
Attachment #8697770 - Flags: review?(bugmail.mozilla)
Comment on attachment 8697770 [details] [diff] [review] part 5.1, partial revert Review of attachment 8697770 [details] [diff] [review]: ----------------------------------------------------------------- ::: gfx/layers/apz/src/AsyncPanZoomController.cpp @@ +2782,5 @@ > aFrameMetrics.SetUseDisplayPortMargins(); > > // If we're trying to paint what we already think is painted, discard this > // request since it's a pointless paint. > + const FrameMetrics& mLastPaintRequestMetrics = mExpectedGeckoMetrics; This line ain't right, it creates a local mLastPaintRequestMetrics which shadows the instance variable. I think this line just needs to be removed, and the rest of the code can use the real mLastPaintRequestMetrics.
Attachment #8697770 - Flags: review?(bugmail.mozilla) → review+
Fixes for gtest failures on try. Since we no longer have "floating" paint requests, some call counts changed, and tests checking the async transform had to be changed too. If you think we need any additional tests for new behavior let me know.
Attachment #8697890 - Flags: review?(bugmail.mozilla)
Comment on attachment 8697890 [details] [diff] [review] part 5.2, fix gtests Review of attachment 8697890 [details] [diff] [review]: ----------------------------------------------------------------- This looks fine. Although part of me thinks it would be nice to still have a test to exercise async transform stuff, the other part of me says it's not really needed if we don't run into that scenario any more because we're aggressively pushing out paint requests. So I don't think we need to do anything else here. ::: gfx/layers/apz/test/gtest/TestAsyncPanZoomController.cpp @@ +2426,5 @@ > EXPECT_EQ(apzcroot, hit.get()); > // transformToApzc doesn't unapply the root's own async transform > EXPECT_EQ(ParentLayerPoint(75, 75), transformToApzc * ScreenPoint(75, 75)); > + // transformToGecko unapplies the full async transform of -100 pixels > + EXPECT_EQ(ScreenPoint(75, 75), transformToGecko * ParentLayerPoint(75, 75)); The comment here isn't right, I think. Because of the repaint request, the async transform should be empty at this point, which is why the transformToGecko doesn't have any effect. If it were really unapplying an async transform of -100 pixels then the expected output would be ScreenPoint(75, 175). So update the comment to be: // transformToGecko should be empty because we sent the repaint request already @@ +2434,5 @@ > EXPECT_EQ(apzcroot, hit.get()); > // transformToApzc doesn't unapply the root's own async transform > EXPECT_EQ(ParentLayerPoint(25, 25), transformToApzc * ScreenPoint(25, 25)); > + // transformToGecko unapplies the full async transform of -100 pixels > + EXPECT_EQ(ScreenPoint(25, 25), transformToGecko * ParentLayerPoint(25, 25)); Ditto here
Attachment #8697890 - Flags: review?(bugmail.mozilla) → review+
For the record, we're waiting on this until bug 1209970 lands, because otherwise the extra repaint requests from this patch will trigger more scroll events to web content, because of the code path at [1], invoked from [2]. [1] https://dxr.mozilla.org/mozilla-central/rev/e6463ae7eda2775bc84593bb4a0742940bb87379/layout/base/nsPresContext.cpp#3279 [2] https://dxr.mozilla.org/mozilla-central/rev/e6463ae7eda2775bc84593bb4a0742940bb87379/layout/generic/nsGfxScrollFrame.cpp#4256
Depends on: 1209970
Updated try push with dvander's github branch rebased combined with the patch from bug 1209970: https://treeherder.mozilla.org/#/jobs?repo=try&revision=a82f9955e6e2
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: