b=573181; clean up render path on Android and prepare for GL layers rendering; r=mwu

This commit is contained in:
Vladimir Vukicevic 2010-06-25 17:52:37 -07:00
parent b2dc7394dc
commit 5f25830c9f
9 changed files with 243 additions and 743 deletions

View File

@ -45,15 +45,6 @@ import java.util.concurrent.atomic.*;
import java.util.zip.*;
import java.nio.*;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGL11;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL;
import javax.microedition.khronos.opengles.GL10;
import android.os.*;
import android.app.*;
import android.text.*;
@ -85,9 +76,6 @@ class GeckoSurfaceView
setFocusable(true);
setFocusableInTouchMode(true);
if (!GeckoApp.useSoftwareDrawing)
startEgl();
mWidth = 0;
mHeight = 0;
mBufferWidth = 0;
@ -98,284 +86,6 @@ class GeckoSurfaceView
protected void finalize() throws Throwable {
super.finalize();
if (!GeckoApp.useSoftwareDrawing)
finishEgl();
}
private static final int EGL_OPENGL_ES2_BIT = 4;
private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
private void printConfig(EGL10 egl, EGLDisplay display,
EGLConfig config) {
int[] attributes = {
EGL10.EGL_BUFFER_SIZE,
EGL10.EGL_ALPHA_SIZE,
EGL10.EGL_BLUE_SIZE,
EGL10.EGL_GREEN_SIZE,
EGL10.EGL_RED_SIZE,
EGL10.EGL_DEPTH_SIZE,
EGL10.EGL_STENCIL_SIZE,
EGL10.EGL_CONFIG_CAVEAT,
EGL10.EGL_CONFIG_ID,
EGL10.EGL_LEVEL,
EGL10.EGL_MAX_PBUFFER_HEIGHT,
EGL10.EGL_MAX_PBUFFER_PIXELS,
EGL10.EGL_MAX_PBUFFER_WIDTH,
EGL10.EGL_NATIVE_RENDERABLE,
EGL10.EGL_NATIVE_VISUAL_ID,
EGL10.EGL_NATIVE_VISUAL_TYPE,
0x3030, // EGL10.EGL_PRESERVED_RESOURCES,
EGL10.EGL_SAMPLES,
EGL10.EGL_SAMPLE_BUFFERS,
EGL10.EGL_SURFACE_TYPE,
EGL10.EGL_TRANSPARENT_TYPE,
EGL10.EGL_TRANSPARENT_RED_VALUE,
EGL10.EGL_TRANSPARENT_GREEN_VALUE,
EGL10.EGL_TRANSPARENT_BLUE_VALUE,
0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB,
0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA,
0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL,
0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL,
EGL10.EGL_LUMINANCE_SIZE,
EGL10.EGL_ALPHA_MASK_SIZE,
EGL10.EGL_COLOR_BUFFER_TYPE,
EGL10.EGL_RENDERABLE_TYPE,
0x3042 // EGL10.EGL_CONFORMANT
};
String[] names = {
"EGL_BUFFER_SIZE",
"EGL_ALPHA_SIZE",
"EGL_BLUE_SIZE",
"EGL_GREEN_SIZE",
"EGL_RED_SIZE",
"EGL_DEPTH_SIZE",
"EGL_STENCIL_SIZE",
"EGL_CONFIG_CAVEAT",
"EGL_CONFIG_ID",
"EGL_LEVEL",
"EGL_MAX_PBUFFER_HEIGHT",
"EGL_MAX_PBUFFER_PIXELS",
"EGL_MAX_PBUFFER_WIDTH",
"EGL_NATIVE_RENDERABLE",
"EGL_NATIVE_VISUAL_ID",
"EGL_NATIVE_VISUAL_TYPE",
"EGL_PRESERVED_RESOURCES",
"EGL_SAMPLES",
"EGL_SAMPLE_BUFFERS",
"EGL_SURFACE_TYPE",
"EGL_TRANSPARENT_TYPE",
"EGL_TRANSPARENT_RED_VALUE",
"EGL_TRANSPARENT_GREEN_VALUE",
"EGL_TRANSPARENT_BLUE_VALUE",
"EGL_BIND_TO_TEXTURE_RGB",
"EGL_BIND_TO_TEXTURE_RGBA",
"EGL_MIN_SWAP_INTERVAL",
"EGL_MAX_SWAP_INTERVAL",
"EGL_LUMINANCE_SIZE",
"EGL_ALPHA_MASK_SIZE",
"EGL_COLOR_BUFFER_TYPE",
"EGL_RENDERABLE_TYPE",
"EGL_CONFORMANT"
};
int[] value = new int[1];
for (int i = 0; i < attributes.length; i++) {
int attribute = attributes[i];
String name = names[i];
if ( egl.eglGetConfigAttrib(display, config, attribute, value)) {
Log.w("GeckoAppJava", String.format(" %s: %d\n", name, value[0]));
} else {
Log.w("GeckoAppJava", String.format(" %s: failed\n", name));
// while (egl.eglGetError() != EGL10.EGL_SUCCESS);
}
}
}
public void startEgl() {
if (mEgl != null)
return;
mEgl = (EGL10) EGLContext.getEGL();
mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
// initialize egl
int[] version = new int[2];
mEgl.eglInitialize(mEglDisplay, version);
// flip this to true to dump all the EGL configs
if (false) {
int[] cs = { EGL10.EGL_NONE };
int[] ptrnum = new int[1];
mEgl.eglChooseConfig(mEglDisplay, cs, null, 0, ptrnum);
int num = ptrnum[0];
EGLConfig[] confs = new EGLConfig[num];
mEgl.eglChooseConfig(mEglDisplay, cs, confs, num, ptrnum);
for (int i = 0; i < num; ++i) {
Log.w("GeckoAppJava", "=== EGL config " + i + " ===");
printConfig(mEgl, mEglDisplay, confs[i]);
}
}
}
public void finishEgl() {
if (mEglDisplay != null) {
mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
}
if (mEglContext != null) {
mEgl.eglDestroyContext(mEglDisplay, mEglContext);
mEglContext = null;
}
if (mEglSurface != null) {
mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
mEglSurface = null;
}
if (mEglDisplay != null) {
mEgl.eglTerminate(mEglDisplay);
mEglDisplay = null;
}
mEglConfig = null;
mEgl = null;
mSurfaceChanged = false;
}
public void chooseEglConfig() {
int redBits, greenBits, blueBits, alphaBits;
// There are some shenanigans here.
// We're required to choose an exact EGL config to match
// the surface format, or we get an error. However,
// on Droid at least, the format is -1, which is PixelFormat.OPAQUE.
// That's not a valid format to be reported; that should just be a
// valid value for *setFormat*. We have a catch-all case that
// assumes 565 for those cases, but it's pretty ugly.
Log.i("GeckoAppJava", "GeckoView PixelFormat format is " + mFormat);
if (mFormat == PixelFormat.RGB_565) {
redBits = 5;
greenBits = 6;
blueBits = 5;
alphaBits = 0;
} else if (mFormat == PixelFormat.RGB_888 ||
mFormat == PixelFormat.RGBX_8888)
{
redBits = 8;
greenBits = 8;
blueBits = 8;
alphaBits = 0;
} else if (mFormat == PixelFormat.RGBA_8888) {
redBits = 8;
greenBits = 8;
blueBits = 8;
alphaBits = 8;
} else {
Log.w("GeckoAppJava", "Unknown PixelFormat for surface (format is " + mFormat + "), assuming 5650!");
redBits = 5;
greenBits = 6;
blueBits = 5;
alphaBits = 0;
}
// PowerVR SGX (Droid) seems to really want depth == 24 for
// performance, even 0 is slower. However, other platforms,
// like Tegra, don't -have- a 24 bit depth config (have 16).
// So that's not good. We'll try with 24 first, and if
// nothing, then we'll go to 0. I'm not sure what the nexus
// one chip wants.
int[] confSpec = new int[] {
// DEPTH_SIZE must be the first pair
EGL10.EGL_DEPTH_SIZE, 24,
EGL10.EGL_RED_SIZE, redBits,
EGL10.EGL_GREEN_SIZE, greenBits,
EGL10.EGL_BLUE_SIZE, blueBits,
EGL10.EGL_ALPHA_SIZE, alphaBits,
EGL10.EGL_STENCIL_SIZE, 0,
EGL10.EGL_CONFIG_CAVEAT, EGL10.EGL_NONE,
EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL10.EGL_NONE };
// so tortured to pass an int as an out param...
int[] ptrNumConfigs = new int[1];
mEgl.eglChooseConfig(mEglDisplay, confSpec, null, 0, ptrNumConfigs);
int numConfigs = ptrNumConfigs[0];
if (numConfigs == 0) {
// twiddle the DEPTH_SIZE value
confSpec[1] = 0;
Log.i("GeckoAppJava", "Couldn't find any valid EGL configs with 24 bit depth, trying 0.");
mEgl.eglChooseConfig(mEglDisplay, confSpec, null, 0, ptrNumConfigs);
numConfigs = ptrNumConfigs[0];
}
if (numConfigs <= 0) {
// couldn't find a config?
Log.w("GeckoAppJava", "Couldn't find any valid EGL configs, blindly trying them all!");
int[] fallbackConfSpec = new int[] {
EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL10.EGL_NONE };
confSpec = fallbackConfSpec;
mEgl.eglChooseConfig(mEglDisplay, confSpec, null, 0, ptrNumConfigs);
numConfigs = ptrNumConfigs[0];
if (numConfigs == 0) {
Log.e("GeckoAppJava", "There aren't any EGL configs available on this system.");
finishEgl();
mSurfaceValid = false;
return;
}
}
EGLConfig[] configs = new EGLConfig[numConfigs];
mEgl.eglChooseConfig(mEglDisplay, confSpec, configs, numConfigs, ptrNumConfigs);
// Find the first config that has the exact RGB sizes that we
// need for our window surface.
int[] ptrVal = new int[1];
for (int i = 0; i < configs.length; ++i) {
int confRed = -1, confGreen = -1, confBlue = -1, confAlpha = -1;
if (mEgl.eglGetConfigAttrib(mEglDisplay, configs[i], EGL10.EGL_RED_SIZE, ptrVal))
confRed = ptrVal[0];
if (mEgl.eglGetConfigAttrib(mEglDisplay, configs[i], EGL10.EGL_GREEN_SIZE, ptrVal))
confGreen = ptrVal[0];
if (mEgl.eglGetConfigAttrib(mEglDisplay, configs[i], EGL10.EGL_BLUE_SIZE, ptrVal))
confBlue = ptrVal[0];
if (mEgl.eglGetConfigAttrib(mEglDisplay, configs[i], EGL10.EGL_ALPHA_SIZE, ptrVal))
confAlpha = ptrVal[0];
if (confRed == redBits &&
confGreen == greenBits &&
confBlue == blueBits &&
confAlpha == alphaBits)
{
mEglConfig = configs[i];
break;
}
}
if (mEglConfig == null) {
Log.w("GeckoAppJava", "Couldn't find EGL config matching colors; using first, hope it works!");
mEglConfig = configs[0];
}
Log.i("GeckoAppJava", "====== Chosen config: ======");
printConfig(mEgl, mEglDisplay, mEglConfig);
mEglContext = null;
mEglSurface = null;
}
/*
@ -397,10 +107,6 @@ class GeckoSurfaceView
Log.i("GeckoAppJava", "surfaceChanged: fmt: " + format + " dim: " + width + " " + height);
if (!GeckoApp.useSoftwareDrawing) {
chooseEglConfig();
}
// XXX This code doesn't seem to actually get hit
if (!GeckoAppShell.sGeckoRunning) {
GeckoAppShell.setInitialSize(width, height);
@ -478,72 +184,6 @@ class GeckoSurfaceView
return DRAW_SOFTWARE;
}
/*
* GL rendering
*/
if (mEgl == null || mEglDisplay == null || mEglConfig == null) {
Log.e("GeckoAppJava", "beginDrawing called, but EGL was never initialized!");
mSurfaceLock.unlock();
return DRAW_ERROR;
}
/*
* If the surface doesn't exist, or if its dimensions or something else changed,
* recreate it.
*/
if (mEglSurface == null ||
mWidth != mBufferWidth ||
mHeight != mBufferHeight ||
mSurfaceChanged)
{
if (mEglContext != null) {
mEgl.eglDestroyContext(mEglDisplay, mEglContext);
mEglContext = null;
}
if (mEglSurface != null)
mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
mEglSurface = mEgl.eglCreateWindowSurface(mEglDisplay, mEglConfig, getHolder(), null);
if (mEglSurface == EGL10.EGL_NO_SURFACE)
{
Log.e("GeckoAppJava", "eglCreateWindowSurface failed!");
mSurfaceValid = false;
return DRAW_ERROR;
}
mBufferWidth = mWidth;
mBufferHeight = mHeight;
mSurfaceChanged = false;
}
if (mEglContext == null) {
int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2,
EGL10.EGL_NONE };
mEglContext = mEgl.eglCreateContext(mEglDisplay, mEglConfig, EGL10.EGL_NO_CONTEXT, attrib_list);
if (mEglContext == EGL10.EGL_NO_CONTEXT)
{
Log.e("GeckoAppJava", "eglCreateContext failed! " + mEgl.eglGetError());
mSurfaceValid = false;
return DRAW_ERROR;
}
Log.i("GeckoAppJava", "EglContext created");
}
// Hmm, should we issue this makecurrent for each frame?
// We'll likely have to, as other bits might switch the context (e.g.
// WebGL, video, etc.).
if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
int err = mEgl.eglGetError();
Log.e("GeckoAppJava", "eglMakeCurrent failed! " + err);
return DRAW_ERROR;
}
return DRAW_GLES_2;
}
@ -608,17 +248,6 @@ class GeckoSurfaceView
getHolder().unlockCanvasAndPost(mSoftwareCanvas);
mSoftwareCanvas = null;
}
} else {
// If the surface was changed while we were drawing;
// don't even bother trying to swap, it won't work,
// and may cause further problems. Note that
// eglSwapBuffers might also throw an
// IllegalArgumentException; we catch that as well.
if (!mSurfaceChanged) {
mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
if (mEgl.eglGetError() == EGL11.EGL_CONTEXT_LOST)
mSurfaceChanged = true;
}
}
} catch (java.lang.IllegalArgumentException ex) {
mSurfaceChanged = true;
@ -719,13 +348,6 @@ class GeckoSurfaceView
ByteBuffer mSoftwareBuffer;
Bitmap mSoftwareBitmap;
Canvas mSoftwareCanvas;
// GL rendering
EGL10 mEgl;
EGLDisplay mEglDisplay;
EGLSurface mEglSurface;
EGLConfig mEglConfig;
EGLContext mEglContext;
}
class GeckoInputConnection

View File

@ -81,7 +81,7 @@ DumpLayerAndChildren(LayerOGL *l, int advance = 0)
/**
* LayerManagerOGL
*/
LayerManagerOGL::LayerManagerOGL(nsIWidget *aWidget)
LayerManagerOGL::LayerManagerOGL(nsIWidget *aWidget)
: mWidget(aWidget)
, mBackBufferFBO(0)
, mBackBufferTexture(0)
@ -102,13 +102,17 @@ LayerManagerOGL::~LayerManagerOGL()
}
PRBool
LayerManagerOGL::Initialize()
LayerManagerOGL::Initialize(GLContext *aExistingContext)
{
mGLContext = sGLContextProvider.CreateForWindow(mWidget);
if (aExistingContext) {
mGLContext = aExistingContext;
} else {
mGLContext = sGLContextProvider.CreateForWindow(mWidget);
if (!mGLContext) {
NS_WARNING("Failed to create LayerManagerOGL context");
return PR_FALSE;
if (!mGLContext) {
NS_WARNING("Failed to create LayerManagerOGL context");
return PR_FALSE;
}
}
MakeCurrent();

View File

@ -75,6 +75,8 @@ class LayerOGL;
* the main thread.
*/
class THEBES_API LayerManagerOGL : public LayerManager {
typedef mozilla::gl::GLContext GLContext;
public:
LayerManagerOGL(nsIWidget *aWidget);
virtual ~LayerManagerOGL();
@ -85,9 +87,12 @@ public:
* to draw to the window. If this method fails the device cannot be used.
* This function is not threadsafe.
*
* \param aExistingContext an existing GL context to use, instead of creating
* our own for the widget.
*
* \return True is initialization was succesful, false when it was not.
*/
PRBool Initialize();
PRBool Initialize(GLContext *aExistingContext = nsnull);
/**
* Sets the clipping region for this layer manager. This is important on
@ -167,8 +172,6 @@ public:
return static_cast<ColorTextureLayerProgram*>(mPrograms[RGBALayerProgramType]);
}
typedef mozilla::gl::GLContext GLContext;
GLContext *gl() const { return mGLContext; }
/*

View File

@ -65,7 +65,11 @@ typedef Window EGLNativeWindowType;
#elif defined(ANDROID)
#define GET_NATIVE_WINDOW(aWidget) (nsnull)
#include <android/log.h>
#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko" , ## args)
/* from widget */
#include "AndroidBridge.h"
typedef void *EGLNativeDisplayType;
typedef void *EGLNativePixmapType;
@ -148,6 +152,11 @@ public:
pfnWaitNative fWaitNative;
typedef EGLCastToRelevantPtr (*pfnGetProcAddress)(const char *procname);
pfnGetProcAddress fGetProcAddress;
typedef EGLBoolean (*pfnSwapBuffers)(EGLDisplay dpy, EGLSurface surface);
pfnSwapBuffers fSwapBuffers;
typedef EGLBoolean (*pfnCopyBuffers)(EGLDisplay dpy, EGLSurface surface,
EGLNativePixmapType target);
pfnCopyBuffers fCopyBuffers;
typedef const GLubyte* (*pfnQueryString)(EGLDisplay, EGLint name);
pfnQueryString fQueryString;
typedef EGLBoolean (*pfnBindTexImage)(EGLDisplay, EGLSurface surface, EGLint buffer);
@ -197,6 +206,8 @@ public:
SYMBOL(GetConfigAttrib),
SYMBOL(WaitNative),
SYMBOL(GetProcAddress),
SYMBOL(SwapBuffers),
SYMBOL(CopyBuffers),
SYMBOL(QueryString),
SYMBOL(BindTexImage),
SYMBOL(ReleaseTexImage),
@ -316,9 +327,9 @@ public:
#else
succeeded = PR_FALSE;
#endif
}
else
} else {
succeeded = sEGLLibrary.fMakeCurrent(mDisplay, mSurface, mSurface, mContext);
}
NS_ASSERTION(succeeded, "Failed to make GL context current!");
}
@ -345,6 +356,11 @@ public:
}
}
PRBool SwapBuffers()
{
return sEGLLibrary.fSwapBuffers(mDisplay, mSurface);
}
private:
EGLDisplay mDisplay;
EGLConfig mConfig;
@ -401,13 +417,17 @@ GLContextProvider::CreateForWindow(nsIWidget *aWidget)
return nsnull;
}
if (!sEGLLibrary.fBindAPI(LOCAL_EGL_OPENGL_ES_API)) {
return nsnull;
}
EGLint attribs[] = {
LOCAL_EGL_SURFACE_TYPE, LOCAL_EGL_WINDOW_BIT,
LOCAL_EGL_RENDERABLE_TYPE, LOCAL_EGL_OPENGL_ES2_BIT,
#ifdef MOZ_GFX_OPTIMIZE_MOBILE
LOCAL_EGL_RED_SIZE, 5,
LOCAL_EGL_GREEN_SIZE, 6,
LOCAL_EGL_BLUE_SIZE, 5,
LOCAL_EGL_ALPHA_SIZE, 0,
#endif
LOCAL_EGL_NONE
};
@ -418,11 +438,28 @@ GLContextProvider::CreateForWindow(nsIWidget *aWidget)
return nsnull;
}
#ifdef ANDROID
// On Android, we have to ask Java to make the eglCreateWindowSurface
// call for us. See GLHelpers.java for a description of why.
//
// We also only have one true "window", so we just use it directly and ignore
// what was passed in.
surface = mozilla::AndroidBridge::Bridge()->
CallEglCreateWindowSurface(display, config,
mozilla::AndroidBridge::Bridge()->SurfaceView());
#else
surface = sEGLLibrary.fCreateWindowSurface(display, config, GET_NATIVE_WINDOW(aWidget), 0);
#endif
if (!surface) {
return nsnull;
}
if (!sEGLLibrary.fBindAPI(LOCAL_EGL_OPENGL_ES_API)) {
sEGLLibrary.fDestroySurface(display, surface);
return nsnull;
}
EGLint cxattribs[] = {
LOCAL_EGL_CONTEXT_CLIENT_VERSION, 2,
LOCAL_EGL_NONE

View File

@ -101,6 +101,15 @@ AndroidBridge::Init(JNIEnv *jEnv,
jOpenUriExternal = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "openUriExternal", "(Ljava/lang/String;Ljava/lang/String;)Z");
jGetMimeTypeFromExtension = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getMimeTypeFromExtension", "(Ljava/lang/String;)Ljava/lang/String;");
jMoveTaskToBack = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "moveTaskToBack", "()V");
jEGLContextClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("javax/microedition/khronos/egl/EGLContext"));
jEGL10Class = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("javax/microedition/khronos/egl/EGL10"));
jEGLSurfaceImplClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("com/google/android/gles_jni/EGLSurfaceImpl"));
jEGLContextImplClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("com/google/android/gles_jni/EGLContextImpl"));
jEGLConfigImplClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("com/google/android/gles_jni/EGLConfigImpl"));
jEGLDisplayImplClass = (jclass) jEnv->NewGlobalRef(jEnv->FindClass("com/google/android/gles_jni/EGLDisplayImpl"));
InitAndroidJavaWrappers(jEnv);
// jEnv should NOT be cached here by anything -- the jEnv here
@ -273,6 +282,50 @@ AndroidBridge::SetSurfaceView(jobject obj)
mSurfaceView.Init(obj);
}
void *
AndroidBridge::CallEglCreateWindowSurface(void *dpy, void *config, AndroidGeckoSurfaceView &sview)
{
AutoLocalJNIFrame jniFrame;
/*
* This is basically:
*
* s = EGLContext.getEGL().eglCreateWindowSurface(new EGLDisplayImpl(dpy),
* new EGLConfigImpl(config),
* view.getHolder(), null);
* return s.mEGLSurface;
*
* We can't do it from java, because the EGLConfigImpl constructor is private.
*/
jobject surfaceHolder = sview.GetSurfaceHolder();
if (!surfaceHolder)
return nsnull;
// grab some fields and methods we'll need
jmethodID constructConfig = mJNIEnv->GetMethodID(jEGLConfigImplClass, "<init>", "(I)V");
jmethodID constructDisplay = mJNIEnv->GetMethodID(jEGLDisplayImplClass, "<init>", "(I)V");
jmethodID getEgl = mJNIEnv->GetStaticMethodID(jEGLContextClass, "getEGL", "()Ljavax/microedition/khronos/egl/EGL;");
jmethodID createWindowSurface = mJNIEnv->GetMethodID(jEGL10Class, "eglCreateWindowSurface", "(Ljavax/microedition/khronos/egl/EGLDisplay;Ljavax/microedition/khronos/egl/EGLConfig;Ljava/lang/Object;[I)Ljavax/microedition/khronos/egl/EGLSurface;");
jobject egl = mJNIEnv->CallStaticObjectMethod(jEGLContextClass, getEgl);
jobject jdpy = mJNIEnv->NewObject(jEGLDisplayImplClass, constructDisplay, (int) dpy);
jobject jconf = mJNIEnv->NewObject(jEGLConfigImplClass, constructConfig, (int) config);
// make the call
jobject surf = mJNIEnv->CallObjectMethod(egl, createWindowSurface, jdpy, jconf, surfaceHolder, NULL);
if (!surf)
return nsnull;
jfieldID sfield = mJNIEnv->GetFieldID(jEGLSurfaceImplClass, "mEGLSurface", "I");
jint realSurface = mJNIEnv->GetIntField(surf, sfield);
return (void*) realSurface;
}
// Available for places elsewhere in the code to link to.
PRBool
mozilla_AndroidBridge_SetMainThread(void *thr)
@ -296,4 +349,3 @@ extern "C" JNIEnv * GetJNIForThread()
{
return mozilla::AndroidBridge::JNIForThread();
}

View File

@ -129,6 +129,9 @@ public:
int mEntries;
};
/* See GLHelpers.java as to why this is needed */
void *CallEglCreateWindowSurface(void *dpy, void *config, AndroidGeckoSurfaceView& surfaceView);
protected:
static AndroidBridge *sBridge;
@ -162,6 +165,14 @@ protected:
jmethodID jOpenUriExternal;
jmethodID jGetMimeTypeFromExtension;
jmethodID jMoveTaskToBack;
// stuff we need for CallEglCreateWindowSurface
jclass jEGLSurfaceImplClass;
jclass jEGLContextImplClass;
jclass jEGLConfigImplClass;
jclass jEGLDisplayImplClass;
jclass jEGLContextClass;
jclass jEGL10Class;
};
}

View File

@ -84,6 +84,7 @@ jclass AndroidGeckoSurfaceView::jGeckoSurfaceViewClass = 0;
jmethodID AndroidGeckoSurfaceView::jBeginDrawingMethod = 0;
jmethodID AndroidGeckoSurfaceView::jEndDrawingMethod = 0;
jmethodID AndroidGeckoSurfaceView::jGetSoftwareDrawBufferMethod = 0;
jmethodID AndroidGeckoSurfaceView::jGetHolderMethod = 0;
#define JNI() (AndroidBridge::JNI())
@ -146,6 +147,7 @@ AndroidGeckoSurfaceView::InitGeckoSurfaceViewClass(JNIEnv *jEnv)
jBeginDrawingMethod = getMethod("beginDrawing", "()I");
jGetSoftwareDrawBufferMethod = getMethod("getSoftwareDrawBuffer", "()Ljava/nio/ByteBuffer;");
jEndDrawingMethod = getMethod("endDrawing", "()V");
jGetHolderMethod = getMethod("getHolder", "()Landroid/view/SurfaceHolder;");
}
void
@ -378,6 +380,12 @@ AndroidGeckoSurfaceView::GetSoftwareDrawBuffer(int *cap)
return (unsigned char*) bp;
}
jobject
AndroidGeckoSurfaceView::GetSurfaceHolder()
{
return JNI()->CallObjectMethod(wrapped_obj, jGetHolderMethod);
}
void
AndroidPoint::Init(JNIEnv *jenv, jobject jobj)
{

View File

@ -46,8 +46,10 @@
#include "nsRect.h"
#include "nsString.h"
//#define FORCE_ALOG 1
#ifndef ALOG
#ifdef DEBUG
#if defined(DEBUG) || defined(FORCE_ALOG)
#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "Gecko" , ## args)
#else
#define ALOG(args...)
@ -168,11 +170,17 @@ public:
int BeginDrawing();
unsigned char *GetSoftwareDrawBuffer(int *cap);
void EndDrawing();
// must have a JNI local frame when calling this,
// and you'd better know what you're doing
jobject GetSurfaceHolder();
protected:
static jclass jGeckoSurfaceViewClass;
static jmethodID jBeginDrawingMethod;
static jmethodID jEndDrawingMethod;
static jmethodID jGetSoftwareDrawBufferMethod;
static jmethodID jGetHolderMethod;
};
class AndroidKeyEvent

View File

@ -51,25 +51,16 @@
#include "gfxImageSurface.h"
#include "gfxContext.h"
#include "Layers.h"
#include "BasicLayers.h"
#include "LayerManagerOGL.h"
#include "GLContext.h"
#include "GLContextProvider.h"
#include "nsTArray.h"
#include "AndroidBridge.h"
/* OpenGL */
#define USE_GLES2
#ifdef USE_GLES2
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#else
#include <GLES/gl.h>
#include <GLES/glext.h>
#endif
#ifndef GL_BGRA_EXT
#define GL_BGRA_EXT 0x80E1
#endif
using namespace mozilla;
NS_IMPL_ISUPPORTS_INHERITED0(nsWindow, nsBaseWidget)
@ -90,6 +81,9 @@ static nsTArray<nsWindow*> gTopLevelWindows;
static nsWindow* gFocusedWindow = nsnull;
static PRUint32 gIMEState;
static nsRefPtr<gl::GLContext> sGLContext;
static PRBool sFailedToCreateGLContext = PR_FALSE;
static nsWindow*
TopWindow()
{
@ -119,7 +113,7 @@ nsWindow::DumpWindows()
void
nsWindow::DumpWindows(const nsTArray<nsWindow*>& wins, int indent)
{
for (int i = 0; i < wins.Length(); ++i) {
for (PRUint32 i = 0; i < wins.Length(); ++i) {
nsWindow *w = wins[i];
LogWindow(w, i, indent);
DumpWindows(w->mChildren, indent+1);
@ -671,38 +665,92 @@ nsWindow::DrawTo(gfxASurface *targetSurface)
if (!mIsVisible)
return PR_FALSE;
nsRefPtr<gfxContext> ctx = new gfxContext(targetSurface);
nsEventStatus status;
nsIntRect boundsRect(0, 0, mBounds.width, mBounds.height);
nsPaintEvent event(PR_TRUE, NS_PAINT, this);
event.region = boundsRect;
{
AutoLayerManagerSetup setupLayerManager(this, ctx);
nsEventStatus status = DispatchEvent(&event);
// Figure out if any of our children cover this widget completely
PRInt32 coveringChildIndex = -1;
for (PRUint32 i = 0; i < mChildren.Length(); ++i) {
if (mChildren[i]->mBounds.IsEmpty())
continue;
if (mChildren[i]->mBounds.Contains(boundsRect)) {
coveringChildIndex = PRInt32(i);
}
}
// XXX uhh.. we can't just ignore this because we no longer have
// what we needed before, but let's keep drawing the children anyway?
// If we have no covering child, then we need to render this.
if (coveringChildIndex == -1) {
ALOG("nsWindow[%p]::DrawTo no covering child, drawing this", (void*) this);
switch (GetLayerManager()->GetBackendType()) {
case LayerManager::LAYERS_BASIC: {
nsRefPtr<gfxContext> ctx = new gfxContext(targetSurface);
nsPaintEvent event(PR_TRUE, NS_PAINT, this);
event.region = boundsRect;
{
AutoLayerManagerSetup setupLayerManager(this, ctx);
status = DispatchEvent(&event);
}
// XXX uhh.. we can't just ignore this because we no longer have
// what we needed before, but let's keep drawing the children anyway?
#if 0
if (status == nsEventStatus_eIgnore)
return PR_FALSE;
if (status == nsEventStatus_eIgnore)
return PR_FALSE;
#endif
// XXX if we got an ignore for the parent, do we still want to draw the children?
// We don't really have a good way not to...
// XXX if we got an ignore for the parent, do we still want to draw the children?
// We don't really have a good way not to...
gfxPoint offset = targetSurface->GetDeviceOffset();
}
break;
for (PRUint32 i = 0; i < mChildren.Length(); ++i) {
targetSurface->SetDeviceOffset(offset + gfxPoint(mChildren[i]->mBounds.x,
mChildren[i]->mBounds.y));
if (!mChildren[i]->DrawTo(targetSurface)) {
case LayerManager::LAYERS_OPENGL: {
static_cast<mozilla::layers::LayerManagerOGL*>(GetLayerManager())->
SetClippingRegion(nsIntRegion(boundsRect));
nsPaintEvent event(PR_TRUE, NS_PAINT, this);
event.region = boundsRect;
status = DispatchEvent(&event);
}
break;
default:
NS_ERROR("Invalid layer manager");
}
// We had no covering child, so make sure we draw all the children,
// starting from index 0.
coveringChildIndex = 0;
}
gfxPoint offset;
if (targetSurface)
offset = targetSurface->GetDeviceOffset();
for (PRUint32 i = coveringChildIndex; i < mChildren.Length(); ++i) {
if (mChildren[i]->mBounds.IsEmpty() ||
!mChildren[i]->mBounds.Intersects(boundsRect))
{
continue;
}
if (targetSurface)
targetSurface->SetDeviceOffset(offset + gfxPoint(mChildren[i]->mBounds.x,
mChildren[i]->mBounds.y));
PRBool ok = mChildren[i]->DrawTo(targetSurface);
if (!ok) {
ALOG("nsWindow[%p]::DrawTo child %d[%p] returned FALSE!", (void*) this, i, (void*)mChildren[i]);
}
}
targetSurface->SetDeviceOffset(offset);
if (targetSurface)
targetSurface->SetDeviceOffset(offset);
return PR_TRUE;
}
@ -753,7 +801,11 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae)
return;
}
nsRefPtr<gfxImageSurface> targetSurface;
if (GetLayerManager()->GetBackendType() == LayerManager::LAYERS_BASIC) {
drawType = AndroidGeckoSurfaceView::DRAW_SOFTWARE;
} else {
drawType = AndroidGeckoSurfaceView::DRAW_GLES_2;
}
if (drawType == AndroidGeckoSurfaceView::DRAW_SOFTWARE) {
int bufCap;
@ -763,7 +815,8 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae)
sview.EndDrawing();
return;
}
targetSurface =
nsRefPtr<gfxImageSurface> targetSurface =
new gfxImageSurface(buf,
gfxIntSize(mBounds.width, mBounds.height),
mBounds.width * 4,
@ -777,319 +830,17 @@ nsWindow::OnDraw(AndroidGeckoEvent *ae)
while (ibuf < ibufMax) {
*ibuf++ = (*ibuf & 0xff00ff00) | ((*ibuf & 0x00ff0000) >> 16) | ((*ibuf & 0x000000ff) << 16);
}
} else if (drawType == AndroidGeckoSurfaceView::DRAW_GLES_2) {
NS_ASSERTION(sGLContext, "Drawing with GLES without a GL context?");
sview.EndDrawing();
return;
sGLContext->fClear(LOCAL_GL_COLOR_BUFFER_BIT | LOCAL_GL_DEPTH_BUFFER_BIT);
DrawTo(nsnull);
if (sGLContext)
sGLContext->SwapBuffers();
}
targetSurface =
new gfxImageSurface(gfxIntSize(mBounds.width, mBounds.height), gfxASurface::ImageFormatARGB32);
if (!DrawTo(targetSurface)) {
sview.EndDrawing();
return;
}
#ifdef USE_GLES2
if (drawType != AndroidGeckoSurfaceView::DRAW_GLES_2) {
ALOG("#### GL drawing path, but beingDrawing wanted something else!");
sview.EndDrawing();
return;
}
static bool hasBGRA = false;
if (firstDraw) {
const char *ext = (const char *) glGetString(GL_EXTENSIONS);
ALOG("GL extensions: %s", ext);
if (strstr(ext, "GL_EXT_bgra") ||
strstr(ext, "GL_IMG_texture_format_BGRA8888") ||
strstr(ext, "GL_EXT_texture_format_BGRA8888"))
hasBGRA = true;
firstDraw = false;
}
static GLuint textureId = GLuint(-1);
static GLuint programId = GLuint(-1);
static GLint positionLoc, texCoordLoc, textureLoc;
if (textureId == GLuint(-1) || !glIsTexture(textureId)) {
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
/* Set required NPOT texture params, for baseline ES2 NPOT support */
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
const char *vsString =
"attribute vec3 aPosition; \n"
"attribute vec2 aTexCoord; \n"
"varying vec2 vTexCoord; \n"
"void main() { \n"
" gl_Position = vec4(aPosition, 1.0); \n"
" vTexCoord = aTexCoord; \n"
"}";
/* Note that while the swizzle is, afaik, "free", the texture upload
* can potentially be faster if the native hardware format is BGRA. So
* we use BGRA if it's available.
*/
const char *fsStringNoBGRA =
"precision mediump float; \n"
"varying vec2 vTexCoord; \n"
"uniform sampler2D sTexture; \n"
"void main() { \n"
" gl_FragColor = vec4(texture2D(sTexture, vTexCoord).bgr, 1.0); \n"
"}";
const char *fsStringBGRA =
"precision mediump float; \n"
"varying vec2 vTexCoord; \n"
"uniform sampler2D sTexture; \n"
"void main() { \n"
" gl_FragColor = vec4(texture2D(sTexture, vTexCoord).rgb, 1.0); \n"
"}";
GLint status;
GLuint vsh = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vsh, 1, &vsString, NULL);
glCompileShader(vsh);
glGetShaderiv(vsh, GL_COMPILE_STATUS, &status);
if (!status) {
ALOG("Failed to compile vertex shader");
return;
}
GLuint fsh = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fsh, 1, hasBGRA ? &fsStringBGRA : &fsStringNoBGRA, NULL);
glCompileShader(fsh);
glGetShaderiv(fsh, GL_COMPILE_STATUS, &status);
if (!status) {
ALOG("Failed to compile fragment shader");
return;
}
programId = glCreateProgram();
glAttachShader(programId, vsh);
glAttachShader(programId, fsh);
glLinkProgram(programId);
glGetProgramiv(programId, GL_LINK_STATUS, &status);
if (!status) {
ALOG("Failed to link program");
return;
}
positionLoc = glGetAttribLocation(programId, "aPosition");
texCoordLoc = glGetAttribLocation(programId, "aTexCoord");
textureLoc = glGetUniformLocation(programId, "sTexture");
}
int texDimWidth = targetSurface->Width();
int texDimHeight = targetSurface->Height();
glClearColor(1.0f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glFrontFace(GL_CCW);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glBindTexture(GL_TEXTURE_2D, textureId);
glTexImage2D(GL_TEXTURE_2D,
0,
hasBGRA ? GL_BGRA_EXT : GL_RGBA,
texDimWidth,
texDimHeight,
0,
hasBGRA ? GL_BGRA_EXT : GL_RGBA,
GL_UNSIGNED_BYTE,
targetSurface->Data());
GLfloat texCoords[] = { 0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 1.0f,
1.0f, 0.0f };
GLfloat vCoords[] = { -1.0f, -1.0f, 0.0f,
-1.0f, 1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f };
glUseProgram(programId);
glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, vCoords);
glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
glEnableVertexAttribArray(positionLoc);
glEnableVertexAttribArray(texCoordLoc);
glUniform1i(textureLoc, 0);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
int err = glGetError();
if (err)
ALOG("GL error: %d", err);
#else
/* GLES 1.0[1?] code.
*
* Two things:
* NPOT textures are not supported by default, unlike with GLES 2
* BGRA texture support is generally available, and might have
* one of three different extension names.
*/
static bool hasBGRA = false;
static bool hasNPOT = false;
static bool hasDrawTex = false;
if (firstDraw) {
const char *ext = (const char *) glGetString(GL_EXTENSIONS);
ALOG("GL extensions: %s", ext);
if (strstr(ext, "GL_EXT_bgra") ||
strstr(ext, "GL_IMG_texture_format_BGRA8888") ||
strstr(ext, "GL_EXT_texture_format_BGRA8888"))
hasBGRA = true;
if (strstr(ext, "GL_ARB_texture_non_power_of_two"))
hasNPOT = true;
if (strstr(ext, "GL_OES_draw_texture"))
hasDrawTex = true;
if (!hasBGRA)
ALOG("No BGRA extension found -- colors will be weird! XXX FIXME");
firstDraw = false;
}
glClearColor(1.0f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// This is all the way a hack and will need to be changed with
// real code to handle lost/reset context issues (e.g. when
// rotating.
static int lastTexDimWidth = -1;
static int lastTexDimHeight = -1;
static GLuint textureId = GLuint(-1);
if (textureId == GLuint(-1) || !glIsTexture(textureId)) {
glGenTextures(1, &textureId);
// Reset these to ensure that the non-NPOT path
// calls TexImage2D first.
lastTexDimWidth = -1;
lastTexDimHeight = -1;
}
glBindTexture(GL_TEXTURE_2D, textureId);
int texDimWidth = targetSurface->Width();
int texDimHeight = targetSurface->Height();
// Not sure if it would be faster to just call TexImage2D
// if we have NPOT textures -- I'd hope that the driver
// can turn that SubImage2D to an equivalent operation,
// given that the dimensions are going to cover the full
// size of the texture.
// There seems to be a Tegra bug here, where we can't
// do TexSubImage2D with GL_BGRA_EXT. We have NPOT there,
// but it means that we have to have a separate codepath
// for when we have NPOT to avoid the update with SubImage2D.
if (hasNPOT) {
glTexImage2D(GL_TEXTURE_2D,
0,
hasBGRA ? GL_BGRA_EXT : GL_RGBA,
texDimWidth,
texDimHeight,
0,
hasBGRA ? GL_BGRA_EXT : GL_RGBA,
GL_UNSIGNED_BYTE,
targetSurface->Data());
} else {
texDimWidth = next_power_of_two(targetSurface->Width());
texDimHeight = next_power_of_two(targetSurface->Height());
if (lastTexDimWidth != texDimWidth ||
lastTexDimHeight != texDimHeight)
{
// Set the texture size, but don't load
// data.
glTexImage2D(GL_TEXTURE_2D,
0,
hasBGRA ? GL_BGRA_EXT : GL_RGBA,
texDimWidth,
texDimHeight,
0,
hasBGRA ? GL_BGRA_EXT : GL_RGBA,
GL_UNSIGNED_BYTE,
NULL);
lastTexDimWidth = texDimWidth;
lastTexDimHeight = texDimHeight;
}
// then actually load the data in the sub-rectangle
glTexSubImage2D(GL_TEXTURE_2D,
0,
0, 0,
targetSurface->Width(),
targetSurface->Height(),
hasBGRA ? GL_BGRA_EXT : GL_RGBA,
GL_UNSIGNED_BYTE,
targetSurface->Data());
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glEnable(GL_TEXTURE_2D);
glFrontFace(GL_CCW);
glDisable(GL_CULL_FACE);
glDisable(GL_ALPHA_TEST);
glDisable(GL_DEPTH_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
GLfloat texCoordW = GLfloat(targetSurface->Width()) / GLfloat(texDimWidth);
GLfloat texCoordH = GLfloat(targetSurface->Height()) / GLfloat(texDimHeight);
GLfloat texCoords[] = { 0.0f, texCoordH,
0.0f, 0.0f,
texCoordW, texCoordH,
texCoordW, 0.0f };
GLfloat vCoords[] = { -1.0f, -1.0f, 0.0f,
-1.0f, 1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f };
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, vCoords);
glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
#endif
sview.EndDrawing();
}
@ -1149,6 +900,10 @@ void *
nsWindow::GetNativeData(PRUint32 aDataType)
{
switch (aDataType) {
// used by GLContextProviderEGL, NULL is EGL_DEFAULT_DISPLAY
case NS_NATIVE_DISPLAY:
return NULL;
case NS_NATIVE_WIDGET:
return (void *) this;
}