Bug 1669239 - Call Tick to paint in PuppetWidget::Paint instead of using WillPaintWindow r=emilio,mattwoodrow

The `WillPaintWindow` call is problematic for FirstContentfulPaint
algorithm because it causes paints that outside of ticks, which would
revoke the viewer flush for the next tick, hence no firstContentful
paint entries are fired.

However, this `WillPaintWindow` call is also useful because it allows
the parent to aware that the layers are updated, so that the parent
can fire corresponding events based on the status. If we don't do it,
parent may end up not knowing that the layers are updated, so some
tests may stall because of the missing events.

The proposed solution here is simply using Tick here to satisfy both
requirements.

It's okay to remove mDirtyRegion in this patch because the
mDirtyRegion usage was for BASIC_LAYERS. BASIC_LAYERS means
BasicLayerManager which does in-process drawing which is nonsense
from the content process.

Differential Revision: https://phabricator.services.mozilla.com/D92445
This commit is contained in:
Sean Feng 2020-10-15 17:51:36 +00:00
Родитель 2f3eeff1f7
Коммит 40945e8a39
2 изменённых файлов: 22 добавлений и 55 удалений

Просмотреть файл

@ -9,6 +9,7 @@
#include "ClientLayerManager.h"
#include "gfxPlatform.h"
#include "nsRefreshDriver.h"
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/Hal.h"
@ -268,10 +269,8 @@ void PuppetWidget::Invalidate(const LayoutDeviceIntRect& aRect) {
return;
}
mDirtyRegion.Or(mDirtyRegion, aRect);
if (mBrowserChild && !mDirtyRegion.IsEmpty() && !mPaintTask.IsPending()) {
mPaintTask = new PaintTask(this);
if (mBrowserChild && !aRect.IsEmpty() && !mPaintTask.IsPending()) {
mPaintTask = new PaintTask(this, false);
nsCOMPtr<nsIRunnable> event(mPaintTask.get());
SchedulerGroup::Dispatch(TaskCategory::Other, event.forget());
return;
@ -980,61 +979,27 @@ void PuppetWidget::ClearCachedCursor() {
mCustomCursor = nullptr;
}
nsresult PuppetWidget::Paint() {
MOZ_ASSERT(!mDirtyRegion.IsEmpty(), "paint event logic messed up");
if (!GetCurrentWidgetListener()) return NS_OK;
LayoutDeviceIntRegion region = mDirtyRegion;
nsresult PuppetWidget::Paint(bool aDoTick) {
if (!GetCurrentWidgetListener()) {
return NS_OK;
}
// reset repaint tracking
mDirtyRegion.SetEmpty();
mPaintTask.Revoke();
RefPtr<PuppetWidget> strongThis(this);
GetCurrentWidgetListener()->WillPaintWindow(this);
if (GetCurrentWidgetListener()) {
#ifdef DEBUG
debug_DumpPaintEvent(stderr, this, region.ToUnknownRegion(), "PuppetWidget",
0);
#endif
if (mLayerManager->GetBackendType() ==
mozilla::layers::LayersBackend::LAYERS_CLIENT ||
mLayerManager->GetBackendType() ==
mozilla::layers::LayersBackend::LAYERS_WR ||
(mozilla::layers::LayersBackend::LAYERS_BASIC ==
mLayerManager->GetBackendType() &&
mBrowserChild && mBrowserChild->IsLayersConnected().isSome())) {
// Do nothing, the compositor will handle drawing
if (mBrowserChild) {
mBrowserChild->NotifyPainted();
}
} else if (mozilla::layers::LayersBackend::LAYERS_BASIC ==
mLayerManager->GetBackendType()) {
RefPtr<gfxContext> ctx = gfxContext::CreateOrNull(mDrawTarget);
if (!ctx) {
gfxDevCrash(LogReason::InvalidContext)
<< "PuppetWidget context problem " << gfx::hexa(mDrawTarget);
return NS_ERROR_FAILURE;
}
ctx->Rectangle(gfxRect(0, 0, 0, 0));
ctx->Clip();
AutoLayerManagerSetup setupLayerManager(this, ctx,
BufferMode::BUFFER_NONE);
GetCurrentWidgetListener()->PaintWindow(this, region);
if (mBrowserChild) {
mBrowserChild->NotifyPainted();
if (PresShell* presShell = mBrowserChild->GetTopLevelPresShell()) {
if (RefPtr<nsRefreshDriver> refreshDriver = presShell->GetRefreshDriver()) {
refreshDriver->ScheduleViewManagerFlush();
// The Tick here is mainly for optimization purpose; Ticking
// here allows us to notify layer updates faster.
if (aDoTick) {
refreshDriver->DoTick();
}
}
}
if (GetCurrentWidgetListener()) {
GetCurrentWidgetListener()->DidPaintWindow();
}
return NS_OK;
}
@ -1049,14 +1014,14 @@ void PuppetWidget::SetChild(PuppetWidget* aChild) {
NS_IMETHODIMP
PuppetWidget::PaintTask::Run() {
if (mWidget) {
mWidget->Paint();
mWidget->Paint(mDoTick);
}
return NS_OK;
}
void PuppetWidget::PaintNowIfNeeded() {
if (IsVisible() && mPaintTask.IsPending()) {
Paint();
Paint(true);
}
}

Просмотреть файл

@ -326,7 +326,7 @@ class PuppetWidget : public nsBaseWidget,
virtual void OnMemoryPressure(layers::MemoryPressureReason aWhy) override;
private:
nsresult Paint();
nsresult Paint(bool aDoTick);
void SetChild(PuppetWidget* aChild);
@ -355,12 +355,15 @@ class PuppetWidget : public nsBaseWidget,
class PaintTask : public Runnable {
public:
NS_DECL_NSIRUNNABLE
explicit PaintTask(PuppetWidget* widget)
: Runnable("PuppetWidget::PaintTask"), mWidget(widget) {}
explicit PaintTask(PuppetWidget* widget, bool aDoTick)
: Runnable("PuppetWidget::PaintTask"),
mWidget(widget),
mDoTick(aDoTick) {}
void Revoke() { mWidget = nullptr; }
private:
PuppetWidget* mWidget;
bool mDoTick;
};
// BrowserChild normally holds a strong reference to this PuppetWidget
@ -373,7 +376,6 @@ class PuppetWidget : public nsBaseWidget,
// The "widget" to which we delegate events if we don't have an
// event handler.
RefPtr<PuppetWidget> mChild;
LayoutDeviceIntRegion mDirtyRegion;
nsRevocableEventPtr<PaintTask> mPaintTask;
RefPtr<layers::MemoryPressureObserver> mMemoryPressureObserver;
// XXX/cjones: keeping this around until we teach LayerManager to do