Bug 1143575. Android's screenshotting code should invalidate the LayerManagerComposite to ensure composition will actually happen. r=nical
There is some ambiguity about whether ScheduleComposite will necessarily
trigger a composite all the way to nsWindow::DrawWindowUnderlay. Android
robocop tests assume it will, because they rely on DrawWindowOverlay
being called so they can take a screenshot and make progress,
but this is a very fragile assumption. They also rely on the entire
window being painted, which is also a fragile assumption.
This patch improves the situation by explicitly invalidating the current
window area when Android Java code needs to trigger a composite. This avoids
regressions from future patches in this series which make composition bail
out when there is nothing invalid.
The resulting setup is still a bit fragile for my taste but I'm not sure
what the ideal solution would be.
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -973,17 +973,17 @@ already_AddRefed<AndroidSurfaceTexture>
nsCOMPtr<nsIRunnable> frameCallback = NS_NewRunnableMethod(this, &nsNPAPIPluginInstance::OnSurfaceTextureFrameAvailable);
surface->SetFrameAvailableCallback(frameCallback);
return surface.forget();
}
void nsNPAPIPluginInstance::OnSurfaceTextureFrameAvailable()
{
if (mRunning == RUNNING && mOwner)
- AndroidBridge::Bridge()->ScheduleComposite();
+ AndroidBridge::Bridge()->InvalidateAndScheduleComposite();
}
void* nsNPAPIPluginInstance::AcquireContentWindow()
{
if (!mContentSurface) {
mContentSurface = CreateSurfaceTexture();
if (!mContentSurface)
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -838,16 +838,25 @@ bool
CompositorParent::RecvNotifyRegionInvalidated(const nsIntRegion& aRegion)
{
if (mLayerManager) {
mLayerManager->AddInvalidRegion(aRegion);
}
return true;
}
+void
+CompositorParent::Invalidate()
+{
+ if (mLayerManager && mLayerManager->GetRoot()) {
+ mLayerManager->AddInvalidRegion(
+ mLayerManager->GetRoot()->GetVisibleRegion().GetBounds());
+ }
+}
+
bool
CompositorParent::RecvStartFrameTimeRecording(const int32_t& aBufferSize, uint32_t* aOutStartIndex)
{
if (mLayerManager) {
*aOutStartIndex = mLayerManager->StartFrameTimeRecording(aBufferSize);
} else {
*aOutStartIndex = 0;
}
@@ -892,16 +901,24 @@ void
CompositorParent::ScheduleRenderOnCompositorThread()
{
CancelableTask *renderTask = NewRunnableMethod(this, &CompositorParent::ScheduleComposition);
MOZ_ASSERT(CompositorLoop());
CompositorLoop()->PostTask(FROM_HERE, renderTask);
}
void
+CompositorParent::InvalidateOnCompositorThread()
+{
+ CancelableTask *renderTask = NewRunnableMethod(this, &CompositorParent::Invalidate);
+ MOZ_ASSERT(CompositorLoop());
+ CompositorLoop()->PostTask(FROM_HERE, renderTask);
+}
+
+void
CompositorParent::PauseComposition()
{
MOZ_ASSERT(IsInCompositorThread(),
"PauseComposition() can only be called on the compositor thread");
MonitorAutoLock lock(mPauseCompositionMonitor);
if (!mPaused) {
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -292,16 +292,17 @@ public:
void NotifyChildCreated(const uint64_t& aChild);
void AsyncRender();
// Can be called from any thread
void ScheduleRenderOnCompositorThread();
void SchedulePauseOnCompositorThread();
+ void InvalidateOnCompositorThread();
/**
* Returns true if a surface was obtained and the resume succeeded; false
* otherwise.
*/
bool ScheduleResumeOnCompositorThread();
bool ScheduleResumeOnCompositorThread(int width, int height);
virtual void ScheduleComposition();
@@ -446,16 +447,17 @@ protected:
void SetEGLSurfaceSize(int width, int height);
void InitializeLayerManager(const nsTArray<LayersBackend>& aBackendHints);
void PauseComposition();
void ResumeComposition();
void ResumeCompositionAndResize(int width, int height);
void ForceComposition();
void CancelCurrentCompositeTask();
+ void Invalidate();
/**
* Add a compositor to the global compositor map.
*/
static void AddCompositor(CompositorParent* compositor, uint64_t* id);
/**
* Remove a compositor from the global compositor map.
*/
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -276,17 +276,17 @@ public class GeckoAppShell
public static void notifyUriVisited(String uri) {
sendEventToGecko(GeckoEvent.createVisitedEvent(uri));
}
public static native void processNextNativeEvent(boolean mayWait);
public static native void notifyBatteryChange(double aLevel, boolean aCharging, double aRemainingTime);
- public static native void scheduleComposite();
+ public static native void invalidateAndScheduleComposite();
// Resuming the compositor is a synchronous request, so be
// careful of possible deadlock. Resuming the compositor will also cause
// a composition, so there is no need to schedule a composition after
// resuming.
public static native void scheduleResumeComposition(int width, int height);
public static native float computeRenderIntegrity();
--- a/mobile/android/base/PresentationMediaPlayerManager.java
+++ b/mobile/android/base/PresentationMediaPlayerManager.java
@@ -95,23 +95,23 @@ public class PresentationMediaPlayerMana
}
}
private static class SurfaceListener implements SurfaceHolder.Callback {
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
// Surface changed so force a composite
- GeckoAppShell.scheduleComposite();
+ GeckoAppShell.invalidateAndScheduleComposite();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
GeckoAppShell.addPresentationSurface(holder.getSurface());
- GeckoAppShell.scheduleComposite();
+ GeckoAppShell.invalidateAndScheduleComposite();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
GeckoAppShell.removePresentationSurface(holder.getSurface());
}
}
}
--- a/mobile/android/base/gfx/GeckoLayerClient.java
+++ b/mobile/android/base/gfx/GeckoLayerClient.java
@@ -774,17 +774,17 @@ class GeckoLayerClient implements LayerV
adjustViewport(displayPort);
}
}
/** Implementation of LayerView.Listener */
@Override
public void renderRequested() {
try {
- GeckoAppShell.scheduleComposite();
+ GeckoAppShell.invalidateAndScheduleComposite();
} catch (UnsupportedOperationException uoe) {
// In some very rare cases this gets called before libxul is loaded,
// so catch and ignore the exception that will throw. See bug 837821
Log.d(LOGTAG, "Dropping renderRequested call before libxul load.");
}
}
/** Implementation of LayerView.Listener */
--- a/mozglue/android/jni-stubs.inc
+++ b/mozglue/android/jni-stubs.inc
@@ -205,31 +205,31 @@ Java_org_mozilla_gecko_GeckoAppShell_not
#endif
#ifdef JNI_BINDINGS
xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_notifyBatteryChange", &f_Java_org_mozilla_gecko_GeckoAppShell_notifyBatteryChange);
#endif
#ifdef JNI_STUBS
-typedef void (*Java_org_mozilla_gecko_GeckoAppShell_scheduleComposite_t)(JNIEnv *, jclass);
-static Java_org_mozilla_gecko_GeckoAppShell_scheduleComposite_t f_Java_org_mozilla_gecko_GeckoAppShell_scheduleComposite;
+typedef void (*Java_org_mozilla_gecko_GeckoAppShell_invalidateAndScheduleComposite_t)(JNIEnv *, jclass);
+static Java_org_mozilla_gecko_GeckoAppShell_invalidateAndScheduleComposite_t f_Java_org_mozilla_gecko_GeckoAppShell_invalidateAndScheduleComposite;
extern "C" NS_EXPORT void JNICALL
-Java_org_mozilla_gecko_GeckoAppShell_scheduleComposite(JNIEnv * arg0, jclass arg1) {
- if (!f_Java_org_mozilla_gecko_GeckoAppShell_scheduleComposite) {
+Java_org_mozilla_gecko_GeckoAppShell_invalidateAndScheduleComposite(JNIEnv * arg0, jclass arg1) {
+ if (!f_Java_org_mozilla_gecko_GeckoAppShell_invalidateAndScheduleComposite) {
arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"),
"JNI Function called before it was loaded");
return ;
}
- f_Java_org_mozilla_gecko_GeckoAppShell_scheduleComposite(arg0, arg1);
+ f_Java_org_mozilla_gecko_GeckoAppShell_invalidateAndScheduleComposite(arg0, arg1);
}
#endif
#ifdef JNI_BINDINGS
- xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_scheduleComposite", &f_Java_org_mozilla_gecko_GeckoAppShell_scheduleComposite);
+ xul_dlsym("Java_org_mozilla_gecko_GeckoAppShell_invalidateAndScheduleComposite", &f_Java_org_mozilla_gecko_GeckoAppShell_invalidateAndScheduleComposite);
#endif
#ifdef JNI_STUBS
typedef void (*Java_org_mozilla_gecko_GeckoAppShell_scheduleResumeComposition_t)(JNIEnv *, jclass, jint, jint);
static Java_org_mozilla_gecko_GeckoAppShell_scheduleResumeComposition_t f_Java_org_mozilla_gecko_GeckoAppShell_scheduleResumeComposition;
extern "C" NS_EXPORT void JNICALL
Java_org_mozilla_gecko_GeckoAppShell_scheduleResumeComposition(JNIEnv * arg0, jclass arg1, jint arg2, jint arg3) {
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -1646,19 +1646,19 @@ AndroidBridge::GetScreenOrientation()
if (!orientation)
return dom::eScreenOrientation_None;
return static_cast<dom::ScreenOrientation>(orientation);
}
void
-AndroidBridge::ScheduleComposite()
+AndroidBridge::InvalidateAndScheduleComposite()
{
- nsWindow::ScheduleComposite();
+ nsWindow::InvalidateAndScheduleComposite();
}
nsresult
AndroidBridge::GetProxyForURI(const nsACString & aSpec,
const nsACString & aScheme,
const nsACString & aHost,
const int32_t aPort,
nsACString & aResult)
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -303,17 +303,17 @@ public:
// enum and that would require including the header which requires
// include IPC headers which requires including basictypes.h which
// requires a lot of changes...
uint32_t GetScreenOrientation();
int GetAPIVersion() { return mAPIVersion; }
bool IsHoneycomb() { return mAPIVersion >= 11 && mAPIVersion <= 13; }
- void ScheduleComposite();
+ void InvalidateAndScheduleComposite();
nsresult GetProxyForURI(const nsACString & aSpec,
const nsACString & aScheme,
const nsACString & aHost,
const int32_t aPort,
nsACString & aResult);
bool PumpMessageLoop();
--- a/widget/android/AndroidJNI.cpp
+++ b/widget/android/AndroidJNI.cpp
@@ -660,19 +660,19 @@ Java_org_mozilla_gecko_GeckoSmsManager_n
nsCOMPtr<nsIRunnable> runnable =
new NotifyReadListFailedRunnable(aError, aRequestId);
NS_DispatchToMainThread(runnable);
}
#endif // MOZ_WEBSMS_BACKEND
NS_EXPORT void JNICALL
-Java_org_mozilla_gecko_GeckoAppShell_scheduleComposite(JNIEnv*, jclass)
+Java_org_mozilla_gecko_GeckoAppShell_invalidateAndScheduleComposite(JNIEnv*, jclass)
{
- nsWindow::ScheduleComposite();
+ nsWindow::InvalidateAndScheduleComposite();
}
NS_EXPORT void JNICALL
Java_org_mozilla_gecko_GeckoAppShell_scheduleResumeComposition(JNIEnv*, jclass, jint width, jint height)
{
nsWindow::ScheduleResumeComposition(width, height);
}
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -2396,19 +2396,20 @@ nsWindow::SetCompositor(mozilla::layers:
mozilla::layers::CompositorChild* aCompositorChild)
{
sLayerManager = aLayerManager;
sCompositorParent = aCompositorParent;
sCompositorChild = aCompositorChild;
}
void
-nsWindow::ScheduleComposite()
+nsWindow::InvalidateAndScheduleComposite()
{
if (sCompositorParent) {
+ sCompositorParent->InvalidateOnCompositorThread();
sCompositorParent->ScheduleRenderOnCompositorThread();
}
}
bool
nsWindow::IsCompositionPaused()
{
return sCompositorPaused;
--- a/widget/android/nsWindow.h
+++ b/widget/android/nsWindow.h
@@ -150,17 +150,17 @@ public:
virtual void DrawWindowOverlay(LayerManagerComposite* aManager, nsIntRect aRect);
virtual mozilla::layers::CompositorParent* NewCompositorParent(int aSurfaceWidth, int aSurfaceHeight) override;
static void SetCompositor(mozilla::layers::LayerManager* aLayerManager,
mozilla::layers::CompositorParent* aCompositorParent,
mozilla::layers::CompositorChild* aCompositorChild);
static bool IsCompositionPaused();
- static void ScheduleComposite();
+ static void InvalidateAndScheduleComposite();
static void SchedulePauseComposition();
static void ScheduleResumeComposition();
static void ScheduleResumeComposition(int width, int height);
static void ForceIsFirstPaint();
static float ComputeRenderIntegrity();
static mozilla::layers::APZCTreeManager* GetAPZCTreeManager();
/* RootLayerTreeId() can only be called when GetAPZCTreeManager() returns non-null */
static uint64_t RootLayerTreeId();