Bug 844275 - Drive the layer manager creation from the GLController rather than GetLayerManager. r=Cwiiis

This commit is contained in:
Kartikaya Gupta 2013-02-28 13:28:23 -05:00
parent b7560520fb
commit bd341fe084
9 changed files with 121 additions and 74 deletions

View File

@ -551,7 +551,7 @@ public:
EGL_NO_CONTEXT);
if (!mSurface) {
#ifdef MOZ_ANDROID_OMTC
mSurface = mozilla::AndroidBridge::Bridge()->ProvideEGLSurface(false);
mSurface = mozilla::AndroidBridge::Bridge()->ProvideEGLSurface();
if (!mSurface) {
return false;
}
@ -2077,7 +2077,7 @@ GLContextProviderEGL::CreateForWindow(nsIWidget *aWidget)
#ifdef MOZ_ANDROID_OMTC
mozilla::AndroidBridge::Bridge()->RegisterCompositor();
EGLSurface surface = mozilla::AndroidBridge::Bridge()->ProvideEGLSurface(true);
EGLSurface surface = mozilla::AndroidBridge::Bridge()->ProvideEGLSurface();
#else
EGLSurface surface = CreateSurfaceForWindow(aWidget, config);
#endif

View File

@ -5,8 +5,12 @@
package org.mozilla.gecko.gfx;
import org.mozilla.gecko.GeckoApp;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.GeckoThread;
import android.util.Log;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
@ -41,6 +45,7 @@ public class GLController {
private EGL10 mEGL;
private EGLDisplay mEGLDisplay;
private EGLConfig mEGLConfig;
private EGLSurface mEGLSurface;
private static final int LOCAL_EGL_OPENGL_ES2_BIT = 4;
@ -64,22 +69,11 @@ public class GLController {
return sInstance;
}
/* Wait until we are allowed to use EGL functions on the Surface backing
* this window.
* This function is invoked by JNI */
synchronized void waitForValidSurface() {
while (!mSurfaceValid) {
try {
wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
synchronized void surfaceDestroyed() {
GeckoApp.assertOnUiThread();
mSurfaceValid = false;
notifyAll();
mEGLSurface = null;
// We need to coordinate with Gecko when pausing composition, to ensure
// that Gecko never executes a draw event while the compositor is paused.
@ -95,13 +89,85 @@ public class GLController {
}
synchronized void surfaceChanged(int newWidth, int newHeight) {
GeckoApp.assertOnUiThread();
mWidth = newWidth;
mHeight = newHeight;
if (mSurfaceValid) {
// We need to make this call even when the compositor isn't currently
// paused (e.g. during an orientation change), to make the compositor
// aware of the changed surface.
resumeCompositor(mWidth, mHeight);
return;
}
mSurfaceValid = true;
notifyAll();
// If we get here, we supposedly have a valid surface where previously we
// did not. So we're going to create the window surface and hold on to it
// until the compositor comes asking for it. However, we can't call
// eglCreateWindowSurface right away because the UI thread isn't *actually*
// done setting up - for some reason Android will send us a surfaceChanged
// notification before the surface is actually ready. So, we need to do the
// call to eglCreateWindowSurface in a runnable posted back to the UI thread
// that will run once this call unwinds all the way out and Android finishes
// doing its thing.
mView.post(new Runnable() {
public void run() {
try {
// Re-check mSurfaceValid in case the surface was destroyed between
// where we set it to true above and this runnable getting run.
// If mSurfaceValid is still true, try to create mEGLSurface. If
// mSurfaceValid is false, leave mEGLSurface as null. So at the end
// of this block mEGLSurface will be null (or EGL_NO_SURFACE) if
// eglCreateWindowSurface failed or if mSurfaceValid changed to false.
if (mSurfaceValid) {
if (mEGL == null) {
initEGL();
}
mEGLSurface = mEGL.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, mView.getNativeWindow(), null);
}
} catch (Exception e) {
Log.e(LOGTAG, "Unable to create window surface", e);
}
if (mEGLSurface == null || mEGLSurface == EGL10.EGL_NO_SURFACE) {
mSurfaceValid = false;
mEGLSurface = null; // normalize EGL_NO_SURFACE to null to simplify later checks
Log.e(LOGTAG, "EGL window surface could not be created: " + getEGLError());
return;
}
// At this point mSurfaceValid is true and mEGLSurface is a valid surface. Try
// to create the compositor if it hasn't been created already.
createCompositor();
}
});
}
void createCompositor() {
GeckoApp.assertOnUiThread();
if (mCompositorCreated) {
// If the compositor has already been created, just resume it instead. We don't need
// to block here because if the surface is destroyed before the compositor grabs it,
// we can handle that gracefully (i.e. the compositor will remain paused).
resumeCompositor(mWidth, mHeight);
return;
}
// Only try to create the compositor if we have a valid surface and gecko is up. When these
// two conditions are satisfied, we can be relatively sure that the compositor creation will
// happen without needing to block anyhwere. Do it with a sync gecko event so that the
// android doesn't have a chance to destroy our surface in between.
if (mEGLSurface != null && GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) {
GeckoAppShell.sendEventToGeckoSync(GeckoEvent.createCompositorResumeEvent());
}
}
void compositorCreated() {
// This is invoked on the compositor thread, while the java UI thread
// is blocked on the gecko sync event in createCompositor() above
mCompositorCreated = true;
}
@ -148,30 +214,9 @@ public class GLController {
throw new GLControllerException("No suitable EGL configuration found");
}
/**
* Provides an EGLSurface without assuming ownership of this surface.
* This class does not keep a reference to the provided EGL surface; the
* caller assumes ownership of the surface once it is returned.
* This function is invoked by JNI */
/* This function is invoked by JNI on the compositor thread */
private EGLSurface provideEGLSurface() {
synchronized (this) {
if (!mSurfaceValid) {
return null;
}
}
if (mEGL == null) {
initEGL();
}
Object window = mView.getNativeWindow();
EGLSurface surface = mEGL.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, window, null);
if (surface == null || surface == EGL10.EGL_NO_SURFACE) {
throw new GLControllerException("EGL window surface could not be created! " +
getEGLError());
}
return surface;
return mEGLSurface;
}
private String getEGLError() {

View File

@ -126,6 +126,17 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
sendResizeEventIfNecessary(true);
DisplayPortCalculator.initPrefs();
// Gecko being ready is one of the two conditions (along with having an available
// surface) that cause us to create the compositor. So here, now that we know gecko
// is ready, call createCompositor() to see if we can actually do the creation.
// This needs to run on the UI thread so that the surface validity can't change on
// us while we're in the middle of creating the compositor.
mView.post(new Runnable() {
public void run() {
mView.getGLController().createCompositor();
}
});
}
public void destroy() {
@ -604,11 +615,6 @@ public class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
@Override
public void surfaceChanged(int width, int height) {
setViewportSize(width, height);
// We need to make this call even when the compositor isn't currently
// paused (e.g. during an orientation change), to make the compositor
// aware of the changed surface.
mView.getGLController().resumeCompositor(width, height);
}
/** Implementation of PanZoomTarget */

View File

@ -1142,11 +1142,8 @@ AndroidBridge::RegisterCompositor(JNIEnv *env)
}
EGLSurface
AndroidBridge::ProvideEGLSurface(bool waitUntilValid)
AndroidBridge::ProvideEGLSurface()
{
if (waitUntilValid) {
sController.WaitForValidSurface();
}
return sController.ProvideEGLSurface();
}

View File

@ -256,7 +256,7 @@ public:
// Switch Java to composite with the Gecko Compositor thread
void RegisterCompositor(JNIEnv* env = NULL);
EGLSurface ProvideEGLSurface(bool waitUntilValid);
EGLSurface ProvideEGLSurface();
bool GetStaticStringField(const char *classID, const char *field, nsAString &result, JNIEnv* env = nullptr);

View File

@ -26,7 +26,6 @@ void AndroidEGLObject::Init(JNIEnv* aJEnv) {
jEGLSurfacePointerField = aJEnv->GetFieldID(jClass, "mEGLSurface", "I");
}
jmethodID AndroidGLController::jWaitForValidSurfaceMethod = 0;
jmethodID AndroidGLController::jProvideEGLSurfaceMethod = 0;
void
@ -36,7 +35,6 @@ AndroidGLController::Init(JNIEnv* aJEnv)
jProvideEGLSurfaceMethod = aJEnv->GetMethodID(jClass, "provideEGLSurface",
"()Ljavax/microedition/khronos/egl/EGLSurface;");
jWaitForValidSurfaceMethod = aJEnv->GetMethodID(jClass, "waitForValidSurface", "()V");
}
void
@ -62,11 +60,3 @@ AndroidGLController::ProvideEGLSurface()
return reinterpret_cast<EGLSurface>(mJEnv->GetIntField(jObj, jEGLSurfacePointerField));
}
void
AndroidGLController::WaitForValidSurface()
{
ASSERT_THREAD();
AutoLocalJNIFrame jniFrame(mJEnv, 0);
mJEnv->CallVoidMethod(mJObj, jWaitForValidSurfaceMethod);
}

View File

@ -22,10 +22,8 @@ public:
void Acquire(JNIEnv* aJEnv, jobject aJObj);
EGLSurface ProvideEGLSurface();
void WaitForValidSurface();
private:
static jmethodID jWaitForValidSurfaceMethod;
static jmethodID jProvideEGLSurfaceMethod;
// the JNIEnv for the compositor thread

View File

@ -688,41 +688,49 @@ nsWindow::GetLayerManager(PLayersChild*, LayersBackend, LayerManagerPersistence,
if (mLayerManager) {
return mLayerManager;
}
// for OMTC allow use of the single layer manager/compositor
// shared across all windows
if (UseOffMainThreadCompositing()) {
return sLayerManager;
}
return nullptr;
}
void
nsWindow::CreateLayerManager()
{
if (mLayerManager) {
return;
}
nsWindow *topLevelWindow = FindTopLevel();
if (!topLevelWindow || topLevelWindow->mWindowType == eWindowType_invisible) {
// don't create a layer manager for an invisible top-level window
return nullptr;
return;
}
mUseLayersAcceleration = ComputeShouldAccelerate(mUseLayersAcceleration);
bool useCompositor = UseOffMainThreadCompositing();
if (useCompositor) {
if (UseOffMainThreadCompositing()) {
if (sLayerManager) {
return sLayerManager;
return;
}
CreateCompositor();
if (mLayerManager) {
// for OMTC create a single layer manager and compositor that will be
// used for all windows.
SetCompositor(mLayerManager, mCompositorParent, mCompositorChild);
return mLayerManager;
return;
}
// If we get here, then off main thread compositing failed to initialize.
sFailedToCreateGLContext = true;
}
if (!mUseLayersAcceleration ||
sFailedToCreateGLContext)
{
if (!mUseLayersAcceleration || sFailedToCreateGLContext) {
printf_stderr(" -- creating basic, not accelerated\n");
mLayerManager = CreateBasicLayerManager();
}
return mLayerManager;
}
void
@ -886,6 +894,8 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae)
break;
case AndroidGeckoEvent::COMPOSITOR_RESUME:
win->CreateLayerManager();
// When we receive this, the compositor has already been told to
// resume. (It turns out that waiting till we reach here to tell
// the compositor to resume takes too long, resulting in a black
@ -2231,7 +2241,7 @@ nsWindow::DrawWindowOverlay(LayerManager* aManager, nsIntRect aRect)
nsRefPtr<mozilla::layers::LayerManager> nsWindow::sLayerManager = 0;
nsRefPtr<mozilla::layers::CompositorParent> nsWindow::sCompositorParent = 0;
nsRefPtr<mozilla::layers::CompositorChild> nsWindow::sCompositorChild = 0;
bool nsWindow::sCompositorPaused = false;
bool nsWindow::sCompositorPaused = true;
void
nsWindow::SetCompositor(mozilla::layers::LayerManager* aLayerManager,

View File

@ -139,6 +139,7 @@ public:
LayersBackend aBackendHint = mozilla::layers::LAYERS_NONE,
LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT,
bool* aAllowRetaining = nullptr);
void CreateLayerManager();
NS_IMETHOD ReparentNativeWidget(nsIWidget* aNewParent);