forked from mirrors/gecko-dev
We see a fair number of crashes caused by failing to create an EGL surface when resuming the compositor on Android. We believe that in the vast majority of these cases the Surface we have been provided by the OS is in an invalid state, and therefore we will never succeed in creating an EGL surface from it. Currently when creating the EGL surface fails we raise a NEW_SURFACE webrender error. This causes us to fall back through webrender configurations, reinitialize the compositors, and eventually crash when we are still unable to resume. None of this will help when the Android Surface we have been provided is in this invalid state. This patch therefore avoids raising the webrender error initially, and instead gives the widget an opportunity to handle the failure. The widget uses the new GeckoView API added in the previous patch in this series to request a new Surface from the application. This will cause another resume event immediately afterwards with a new - and hopefully valid - surface, allowing the EGL surface to be created and the compositor to be successfully resumed. If we are still unable to create an EGL surface after this, then we will raise the webrender error as before, likely eventually resulting in a crash. Differential Revision: https://phabricator.services.mozilla.com/D176721
296 lines
9.2 KiB
C++
296 lines
9.2 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
#include "UiCompositorControllerParent.h"
|
|
|
|
#if defined(MOZ_WIDGET_ANDROID)
|
|
# include "apz/src/APZCTreeManager.h"
|
|
# include "mozilla/widget/AndroidCompositorWidget.h"
|
|
#endif
|
|
#include <utility>
|
|
|
|
#include "FrameMetrics.h"
|
|
#include "SynchronousTask.h"
|
|
#include "mozilla/Unused.h"
|
|
#include "mozilla/gfx/Types.h"
|
|
#include "mozilla/ipc/Endpoint.h"
|
|
#include "mozilla/layers/Compositor.h"
|
|
#include "mozilla/layers/CompositorBridgeParent.h"
|
|
#include "mozilla/layers/CompositorThread.h"
|
|
#include "mozilla/layers/UiCompositorControllerMessageTypes.h"
|
|
#include "mozilla/layers/WebRenderBridgeParent.h"
|
|
|
|
namespace mozilla {
|
|
namespace layers {
|
|
|
|
typedef CompositorBridgeParent::LayerTreeState LayerTreeState;
|
|
|
|
/* static */
|
|
RefPtr<UiCompositorControllerParent>
|
|
UiCompositorControllerParent::GetFromRootLayerTreeId(
|
|
const LayersId& aRootLayerTreeId) {
|
|
RefPtr<UiCompositorControllerParent> controller;
|
|
CompositorBridgeParent::CallWithIndirectShadowTree(
|
|
aRootLayerTreeId, [&](LayerTreeState& aState) -> void {
|
|
controller = aState.mUiControllerParent;
|
|
});
|
|
return controller;
|
|
}
|
|
|
|
/* static */
|
|
RefPtr<UiCompositorControllerParent> UiCompositorControllerParent::Start(
|
|
const LayersId& aRootLayerTreeId,
|
|
Endpoint<PUiCompositorControllerParent>&& aEndpoint) {
|
|
RefPtr<UiCompositorControllerParent> parent =
|
|
new UiCompositorControllerParent(aRootLayerTreeId);
|
|
|
|
RefPtr<Runnable> task =
|
|
NewRunnableMethod<Endpoint<PUiCompositorControllerParent>&&>(
|
|
"layers::UiCompositorControllerParent::Open", parent,
|
|
&UiCompositorControllerParent::Open, std::move(aEndpoint));
|
|
CompositorThread()->Dispatch(task.forget());
|
|
|
|
return parent;
|
|
}
|
|
|
|
mozilla::ipc::IPCResult UiCompositorControllerParent::RecvPause() {
|
|
CompositorBridgeParent* parent =
|
|
CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(
|
|
mRootLayerTreeId);
|
|
if (parent) {
|
|
parent->PauseComposition();
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult UiCompositorControllerParent::RecvResume(
|
|
bool* aOutResumed) {
|
|
*aOutResumed = false;
|
|
CompositorBridgeParent* parent =
|
|
CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(
|
|
mRootLayerTreeId);
|
|
if (parent) {
|
|
*aOutResumed = parent->ResumeComposition();
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult UiCompositorControllerParent::RecvResumeAndResize(
|
|
const int32_t& aX, const int32_t& aY, const int32_t& aWidth,
|
|
const int32_t& aHeight, bool* aOutResumed) {
|
|
*aOutResumed = false;
|
|
CompositorBridgeParent* parent =
|
|
CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(
|
|
mRootLayerTreeId);
|
|
if (parent) {
|
|
// Front-end expects a first paint callback upon resume/resize.
|
|
parent->ForceIsFirstPaint();
|
|
#if defined(MOZ_WIDGET_ANDROID)
|
|
parent->GetWidget()->AsAndroid()->NotifyClientSizeChanged(
|
|
LayoutDeviceIntSize(aWidth, aHeight));
|
|
#endif
|
|
*aOutResumed = parent->ResumeCompositionAndResize(aX, aY, aWidth, aHeight);
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
UiCompositorControllerParent::RecvInvalidateAndRender() {
|
|
CompositorBridgeParent* parent =
|
|
CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(
|
|
mRootLayerTreeId);
|
|
if (parent) {
|
|
parent->ScheduleComposition(wr::RenderReasons::OTHER);
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult UiCompositorControllerParent::RecvMaxToolbarHeight(
|
|
const int32_t& aHeight) {
|
|
mMaxToolbarHeight = aHeight;
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult UiCompositorControllerParent::RecvFixedBottomOffset(
|
|
const int32_t& aOffset) {
|
|
#if defined(MOZ_WIDGET_ANDROID)
|
|
CompositorBridgeParent* parent =
|
|
CompositorBridgeParent::GetCompositorBridgeParentFromLayersId(
|
|
mRootLayerTreeId);
|
|
if (parent) {
|
|
parent->SetFixedLayerMargins(0, aOffset);
|
|
}
|
|
#endif // defined(MOZ_WIDGET_ANDROID)
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult UiCompositorControllerParent::RecvDefaultClearColor(
|
|
const uint32_t& aColor) {
|
|
LayerTreeState* state =
|
|
CompositorBridgeParent::GetIndirectShadowTree(mRootLayerTreeId);
|
|
|
|
if (state && state->mWrBridge) {
|
|
state->mWrBridge->SetClearColor(gfx::DeviceColor::UnusualFromARGB(aColor));
|
|
}
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
UiCompositorControllerParent::RecvRequestScreenPixels() {
|
|
#if defined(MOZ_WIDGET_ANDROID)
|
|
LayerTreeState* state =
|
|
CompositorBridgeParent::GetIndirectShadowTree(mRootLayerTreeId);
|
|
|
|
if (state && state->mWrBridge) {
|
|
state->mWrBridge->RequestScreenPixels(this);
|
|
state->mWrBridge->ScheduleForcedGenerateFrame(wr::RenderReasons::OTHER);
|
|
}
|
|
#endif // defined(MOZ_WIDGET_ANDROID)
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
UiCompositorControllerParent::RecvEnableLayerUpdateNotifications(
|
|
const bool& aEnable) {
|
|
#if defined(MOZ_WIDGET_ANDROID)
|
|
// Layers updates are need by Robocop test which enables them
|
|
mCompositorLayersUpdateEnabled = aEnable;
|
|
#endif // defined(MOZ_WIDGET_ANDROID)
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
void UiCompositorControllerParent::ActorDestroy(ActorDestroyReason aWhy) {
|
|
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
|
Shutdown();
|
|
}
|
|
|
|
void UiCompositorControllerParent::ToolbarAnimatorMessageFromCompositor(
|
|
int32_t aMessage) {
|
|
// This function can be call from ether compositor or controller thread.
|
|
if (!CompositorThreadHolder::IsInCompositorThread()) {
|
|
CompositorThread()->Dispatch(NewRunnableMethod<int32_t>(
|
|
"layers::UiCompositorControllerParent::"
|
|
"ToolbarAnimatorMessageFromCompositor",
|
|
this,
|
|
&UiCompositorControllerParent::ToolbarAnimatorMessageFromCompositor,
|
|
aMessage));
|
|
return;
|
|
}
|
|
|
|
Unused << SendToolbarAnimatorMessageFromCompositor(aMessage);
|
|
}
|
|
|
|
bool UiCompositorControllerParent::AllocPixelBuffer(const int32_t aSize,
|
|
ipc::Shmem* aMem) {
|
|
MOZ_ASSERT(aSize > 0);
|
|
return AllocShmem(aSize, aMem);
|
|
}
|
|
|
|
void UiCompositorControllerParent::NotifyLayersUpdated() {
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
if (mCompositorLayersUpdateEnabled) {
|
|
ToolbarAnimatorMessageFromCompositor(LAYERS_UPDATED);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void UiCompositorControllerParent::NotifyFirstPaint() {
|
|
ToolbarAnimatorMessageFromCompositor(FIRST_PAINT);
|
|
}
|
|
|
|
void UiCompositorControllerParent::NotifyUpdateScreenMetrics(
|
|
const GeckoViewMetrics& aMetrics) {
|
|
#if defined(MOZ_WIDGET_ANDROID)
|
|
// TODO: Need to handle different x-and y-scales.
|
|
CSSToScreenScale scale = ViewTargetAs<ScreenPixel>(
|
|
aMetrics.mZoom, PixelCastJustification::ScreenIsParentLayerForRoot);
|
|
ScreenPoint scrollOffset = aMetrics.mVisualScrollOffset * scale;
|
|
CompositorThread()->Dispatch(NewRunnableMethod<ScreenPoint, CSSToScreenScale>(
|
|
"UiCompositorControllerParent::SendRootFrameMetrics", this,
|
|
&UiCompositorControllerParent::SendRootFrameMetrics, scrollOffset,
|
|
scale));
|
|
#endif
|
|
}
|
|
|
|
UiCompositorControllerParent::UiCompositorControllerParent(
|
|
const LayersId& aRootLayerTreeId)
|
|
: mRootLayerTreeId(aRootLayerTreeId)
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
,
|
|
mCompositorLayersUpdateEnabled(false)
|
|
#endif
|
|
,
|
|
mMaxToolbarHeight(0) {
|
|
MOZ_COUNT_CTOR(UiCompositorControllerParent);
|
|
}
|
|
|
|
UiCompositorControllerParent::~UiCompositorControllerParent() {
|
|
MOZ_COUNT_DTOR(UiCompositorControllerParent);
|
|
}
|
|
|
|
void UiCompositorControllerParent::InitializeForSameProcess() {
|
|
// This function is called by UiCompositorControllerChild in the main thread.
|
|
// So dispatch to the compositor thread to Initialize.
|
|
if (!CompositorThreadHolder::IsInCompositorThread()) {
|
|
SetOtherProcessId(base::GetCurrentProcId());
|
|
SynchronousTask task(
|
|
"UiCompositorControllerParent::InitializeForSameProcess");
|
|
|
|
CompositorThread()->Dispatch(NS_NewRunnableFunction(
|
|
"UiCompositorControllerParent::InitializeForSameProcess", [&]() {
|
|
AutoCompleteTask complete(&task);
|
|
InitializeForSameProcess();
|
|
}));
|
|
|
|
task.Wait();
|
|
return;
|
|
}
|
|
|
|
Initialize();
|
|
}
|
|
|
|
void UiCompositorControllerParent::InitializeForOutOfProcess() {
|
|
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
|
Initialize();
|
|
}
|
|
|
|
void UiCompositorControllerParent::Initialize() {
|
|
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
|
LayerTreeState* state =
|
|
CompositorBridgeParent::GetIndirectShadowTree(mRootLayerTreeId);
|
|
MOZ_ASSERT(state);
|
|
MOZ_ASSERT(state->mParent);
|
|
if (!state || !state->mParent) {
|
|
return;
|
|
}
|
|
state->mUiControllerParent = this;
|
|
}
|
|
|
|
void UiCompositorControllerParent::Open(
|
|
Endpoint<PUiCompositorControllerParent>&& aEndpoint) {
|
|
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
|
if (!aEndpoint.Bind(this)) {
|
|
// We can't recover from this.
|
|
MOZ_CRASH("Failed to bind UiCompositorControllerParent to endpoint");
|
|
}
|
|
InitializeForOutOfProcess();
|
|
}
|
|
|
|
void UiCompositorControllerParent::Shutdown() {
|
|
MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
|
|
LayerTreeState* state =
|
|
CompositorBridgeParent::GetIndirectShadowTree(mRootLayerTreeId);
|
|
if (state) {
|
|
state->mUiControllerParent = nullptr;
|
|
}
|
|
}
|
|
|
|
} // namespace layers
|
|
} // namespace mozilla
|