Bug 728371 - Make Flash work with GLES 2.0 and GL layers r=pcwalton

--HG--
extra : rebase_source : f7a9eb9dab84cb35ba17fb1551051dae2e744d00
This commit is contained in:
James Willcox 2012-03-12 13:03:54 -04:00
parent df2f0361f7
commit 02313468a0
8 changed files with 241 additions and 131 deletions

View File

@ -2917,8 +2917,14 @@ void nsPluginInstanceOwner::Paint(gfxContext* aContext,
PRInt32 model = mInstance->GetANPDrawingModel();
float xResolution = mObjectFrame->PresContext()->GetRootPresContext()->PresShell()->GetXResolution();
float yResolution = mObjectFrame->PresContext()->GetRootPresContext()->PresShell()->GetYResolution();
gfxRect scaledFrameRect = aFrameRect;
scaledFrameRect.Scale(xResolution, yResolution);
if (model == kSurface_ANPDrawingModel) {
if (!AddPluginView(aFrameRect)) {
if (!AddPluginView(scaledFrameRect)) {
Invalidate();
}
return;
@ -2928,11 +2934,9 @@ void nsPluginInstanceOwner::Paint(gfxContext* aContext,
if (!mLayer)
mLayer = new AndroidMediaLayer();
// FIXME: this is gross
float zoomLevel = aFrameRect.width / (float)mPluginWindow->width;
mLayer->UpdatePosition(aFrameRect, zoomLevel);
mLayer->UpdatePosition(scaledFrameRect, xResolution);
SendSize((int)aFrameRect.width, (int)aFrameRect.height);
SendSize((int)scaledFrameRect.width, (int)scaledFrameRect.height);
return;
}

View File

@ -1635,26 +1635,14 @@ nsObjectFrame::PaintPlugin(nsDisplayListBuilder* aBuilder,
{
#if defined(MOZ_WIDGET_ANDROID)
if (mInstanceOwner) {
NPWindow *window;
mInstanceOwner->GetWindow(window);
gfxRect frameGfxRect =
PresContext()->AppUnitsToGfxUnits(aPluginRect);
gfxRect dirtyGfxRect =
PresContext()->AppUnitsToGfxUnits(aDirtyRect);
gfxContext* ctx = aRenderingContext.ThebesContext();
gfx3DMatrix matrix3d = nsLayoutUtils::GetTransformToAncestor(this, nsnull);
gfxMatrix matrix2d;
if (!matrix3d.Is2D(&matrix2d))
return;
// The matrix includes the frame's position, so we need to transform
// from 0,0 to get the correct coordinates.
frameGfxRect.MoveTo(0, 0);
mInstanceOwner->Paint(ctx, matrix2d.Transform(frameGfxRect), dirtyGfxRect);
mInstanceOwner->Paint(ctx, frameGfxRect, dirtyGfxRect);
return;
}
#endif

View File

@ -110,6 +110,7 @@ public class GeckoLayerClient implements GeckoEventResponder,
view.setListener(this);
mLayerRenderer = new LayerRenderer(view);
view.setLayerRenderer(mLayerRenderer);
}
/** This function is invoked by Gecko via JNI; be careful when modifying signature. */

View File

@ -226,8 +226,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
GLES20.glUniform1i(mSampleHandle, 0);
TextureGenerator.get().fill();
// TODO: Move these calls into a separate deactivate() call that is called after the
// underlay and overlay are rendered.
}
@ -614,8 +612,17 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
LayerController controller = mView.getController();
/* Draw any extra layers that were added (likely plugins) */
for (Layer layer : mExtraLayers)
layer.draw(mPageContext);
if (mExtraLayers.size() > 0) {
// This is a hack. SurfaceTextureLayer draws with its own program, so disable ours here
// and re-enable when done. If we end up adding other types of Layer here we'll need
// to do something different.
deactivateDefaultProgram();
for (Layer layer : mExtraLayers)
layer.draw(mPageContext);
activateDefaultProgram();
}
/* Draw the vertical scrollbar. */
IntSize screenSize = new IntSize(controller.getViewportSize());

View File

@ -226,5 +226,14 @@ public class LayerView extends FlexibleGLSurfaceView {
public IntBuffer getPixels() {
return mRenderer.getPixels();
}
public void setLayerRenderer(LayerRenderer renderer) {
mRenderer = renderer;
setRenderer(mRenderer);
}
public LayerRenderer getLayerRenderer() {
return mRenderer;
}
}

View File

@ -43,20 +43,14 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.opengl.GLES11;
import android.opengl.GLES11Ext;
import android.opengl.Matrix;
import android.opengl.GLES20;
import android.util.Log;
import android.view.Surface;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11Ext;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import android.hardware.Camera;
// TODO: Port this to GLES 2.0.
public class SurfaceTextureLayer extends Layer implements SurfaceTexture.OnFrameAvailableListener {
private static final String LOGTAG = "SurfaceTextureLayer";
private static final int LOCAL_GL_TEXTURE_EXTERNAL_OES = 0x00008d65; // This is only defined in API level 15 for some reason (Android 4.0.3)
@ -65,16 +59,63 @@ public class SurfaceTextureLayer extends Layer implements SurfaceTexture.OnFrame
private final Surface mSurface;
private int mTextureId;
private boolean mHaveFrame;
private float[] mTextureTransform = new float[16];
private boolean mInverted;
private boolean mNewInverted;
private boolean mBlend;
private boolean mNewBlend;
private FloatBuffer textureBuffer;
private FloatBuffer textureBufferInverted;
private static int mProgram;
private static int mPositionHandle;
private static int mTextureHandle;
private static int mSampleHandle;
private static int mProjectionMatrixHandle;
private static int mTextureMatrixHandle;
public SurfaceTextureLayer(int textureId) {
private static final float[] PROJECTION_MATRIX = {
2.0f, 0.0f, 0.0f, 0.0f,
0.0f, 2.0f, 0.0f, 0.0f,
0.0f, 0.0f, 2.0f, 0.0f,
-1.0f, -1.0f, 0.0f, 1.0f
};
private static final String VERTEX_SHADER =
"uniform mat4 projectionMatrix;\n" +
"uniform mat4 textureMatrix;\n" +
"attribute vec4 vPosition;\n" +
"attribute vec4 aTexCoord;\n" +
"varying vec2 vTexCoord;\n" +
"void main() {\n" +
" gl_Position = projectionMatrix * vPosition;\n" +
" vTexCoord = (textureMatrix * vec4(aTexCoord.x, aTexCoord.y, 0.0, 1.0)).xy;\n" +
"}\n";
private static String FRAGMENT_SHADER_OES =
"#extension GL_OES_EGL_image_external : require\n" +
"precision mediump float;\n" +
"varying vec2 vTexCoord; \n" +
"uniform samplerExternalOES sTexture; \n" +
"void main() {\n" +
" gl_FragColor = texture2D(sTexture, vTexCoord); \n" +
"}\n";
private static final float TEXTURE_MAP[] = {
0.0f, 1.0f, // top left
0.0f, 0.0f, // bottom left
1.0f, 1.0f, // top right
1.0f, 0.0f, // bottom right
};
private static final float TEXTURE_MAP_INVERTED[] = {
0.0f, 0.0f, // bottom left
0.0f, 1.0f, // top left
1.0f, 0.0f, // bottom right
1.0f, 1.0f, // top right
};
private SurfaceTextureLayer(int textureId) {
mTextureId = textureId;
mHaveFrame = true;
mInverted = false;
@ -90,24 +131,6 @@ public class SurfaceTextureLayer extends Layer implements SurfaceTexture.OnFrame
}
mSurface = tmp;
float textureMap[] = {
0.0f, 1.0f, // top left
0.0f, 0.0f, // bottom left
1.0f, 1.0f, // top right
1.0f, 0.0f, // bottom right
};
textureBuffer = createBuffer(textureMap);
float textureMapInverted[] = {
0.0f, 0.0f, // bottom left
0.0f, 1.0f, // top left
1.0f, 0.0f, // bottom right
1.0f, 1.0f, // top right
};
textureBufferInverted = createBuffer(textureMapInverted);
}
public static SurfaceTextureLayer create() {
@ -120,23 +143,10 @@ public class SurfaceTextureLayer extends Layer implements SurfaceTexture.OnFrame
// For SurfaceTexture.OnFrameAvailableListener
public void onFrameAvailable(SurfaceTexture texture) {
// FIXME: for some reason this doesn't get called
mHaveFrame = true;
GeckoApp.mAppContext.requestRender();
}
private FloatBuffer createBuffer(float[] input) {
// a float has 4 bytes so we allocate for each coordinate 4 bytes
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(input.length * 4);
byteBuffer.order(ByteOrder.nativeOrder());
FloatBuffer floatBuffer = byteBuffer.asFloatBuffer();
floatBuffer.put(input);
floatBuffer.position(0);
return floatBuffer;
}
public void update(Rect position, float resolution, boolean inverted, boolean blend) {
beginTransaction(); // this is called on the Gecko thread
@ -173,78 +183,123 @@ public class SurfaceTextureLayer extends Layer implements SurfaceTexture.OnFrame
mInverted = mNewInverted;
mBlend = mNewBlend;
GLES11.glEnable(LOCAL_GL_TEXTURE_EXTERNAL_OES);
GLES11.glBindTexture(LOCAL_GL_TEXTURE_EXTERNAL_OES, mTextureId);
mSurfaceTexture.updateTexImage();
GLES11.glDisable(LOCAL_GL_TEXTURE_EXTERNAL_OES);
// FIXME: we should return true and rely on onFrameAvailable, but
// that isn't working for some reason
return false;
return true;
}
private float mapToGLCoords(float input, float viewport, boolean flip) {
if (flip) input = viewport - input;
return ((input / viewport) * 2.0f) - 1.0f;
private static boolean ensureProgram() {
if (mProgram != 0)
return true;
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_OES);
mProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(mProgram, vertexShader);
GLES20.glAttachShader(mProgram, fragmentShader);
GLES20.glLinkProgram(mProgram);
mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
mTextureHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoord");
mSampleHandle = GLES20.glGetUniformLocation(mProgram, "sTexture");
mProjectionMatrixHandle = GLES20.glGetUniformLocation(mProgram, "projectionMatrix");
mTextureMatrixHandle = GLES20.glGetUniformLocation(mProgram, "textureMatrix");
return mProgram != 0;
}
private static int loadShader(int type, String shaderCode) {
int shader = GLES20.glCreateShader(type);
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
private static void activateProgram() {
GLES20.glUseProgram(mProgram);
}
public static void deactivateProgram() {
GLES20.glDisableVertexAttribArray(mTextureHandle);
GLES20.glDisableVertexAttribArray(mPositionHandle);
GLES20.glUseProgram(0);
}
@Override
public void draw(RenderContext context) {
if (!ensureProgram() || !mHaveFrame)
return;
// Enable GL_TEXTURE_EXTERNAL_OES and bind our texture
GLES11.glEnable(LOCAL_GL_TEXTURE_EXTERNAL_OES);
GLES11.glBindTexture(LOCAL_GL_TEXTURE_EXTERNAL_OES, mTextureId);
// Enable vertex and texture coordinate buffers
GLES11.glEnableClientState(GL10.GL_VERTEX_ARRAY);
GLES11.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
// Load whatever texture transform the SurfaceMatrix needs
float[] matrix = new float[16];
mSurfaceTexture.getTransformMatrix(matrix);
GLES11.glMatrixMode(GLES11.GL_TEXTURE);
GLES11.glLoadMatrixf(matrix, 0);
// Figure out vertices to put the texture in the right spot on the screen
RectF bounds = getBounds(context);
RectF rect = getBounds(context);
RectF viewport = context.viewport;
bounds.offset(-viewport.left, -viewport.top);
rect.offset(-viewport.left, -viewport.top);
float vertices[] = new float[8];
float viewWidth = viewport.width();
float viewHeight = viewport.height();
// Bottom left
vertices[0] = mapToGLCoords(bounds.left, viewport.width(), false);
vertices[1] = mapToGLCoords(bounds.bottom, viewport.height(), true);
float top = viewHeight - rect.top;
float bot = viewHeight - rect.bottom;
// Top left
vertices[2] = mapToGLCoords(bounds.left, viewport.width(), false);
vertices[3] = mapToGLCoords(bounds.top, viewport.height(), true);
float[] textureCoords = mInverted ? TEXTURE_MAP_INVERTED : TEXTURE_MAP;
// Bottom right
vertices[4] = mapToGLCoords(bounds.right, viewport.width(), false);
vertices[5] = mapToGLCoords(bounds.bottom, viewport.height(), true);
// Coordinates for the scrollbar's body combined with the texture coordinates
float[] coords = {
// x, y, z, texture_x, texture_y
rect.left/viewWidth, bot/viewHeight, 0,
textureCoords[0], textureCoords[1],
// Top right
vertices[6] = mapToGLCoords(bounds.right, viewport.width(), false);
vertices[7] = mapToGLCoords(bounds.top, viewport.height(), true);
rect.left/viewWidth, (bot+rect.height())/viewHeight, 0,
textureCoords[2], textureCoords[3],
// Set texture and vertex buffers
GLES11.glVertexPointer(2, GL10.GL_FLOAT, 0, createBuffer(vertices));
GLES11.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mInverted ? textureBufferInverted : textureBuffer);
(rect.left+rect.width())/viewWidth, bot/viewHeight, 0,
textureCoords[4], textureCoords[5],
(rect.left+rect.width())/viewWidth, (bot+rect.height())/viewHeight, 0,
textureCoords[6], textureCoords[7]
};
FloatBuffer coordBuffer = context.coordBuffer;
coordBuffer.position(0);
coordBuffer.put(coords);
activateProgram();
// Set the transformation matrix
GLES20.glUniformMatrix4fv(mProjectionMatrixHandle, 1, false, PROJECTION_MATRIX, 0);
// Enable the arrays from which we get the vertex and texture coordinates
GLES20.glEnableVertexAttribArray(mPositionHandle);
GLES20.glEnableVertexAttribArray(mTextureHandle);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glUniform1i(mSampleHandle, 0);
GLES20.glBindTexture(LOCAL_GL_TEXTURE_EXTERNAL_OES, mTextureId);
mSurfaceTexture.updateTexImage();
mSurfaceTexture.getTransformMatrix(mTextureTransform);
GLES20.glUniformMatrix4fv(mTextureMatrixHandle, 1, false, mTextureTransform, 0);
// Vertex coordinates are x,y,z starting at position 0 into the buffer.
coordBuffer.position(0);
GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, 20,
coordBuffer);
// Texture coordinates are texture_x, texture_y starting at position 3 into the buffer.
coordBuffer.position(3);
GLES20.glVertexAttribPointer(mTextureHandle, 3, GLES20.GL_FLOAT, false, 20,
coordBuffer);
if (mBlend) {
GLES11.glEnable(GL10.GL_BLEND);
GLES11.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA);
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
}
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
// Draw the vertices as triangle strip
GLES11.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 2);
// Clean up
GLES11.glDisableClientState(GL10.GL_VERTEX_ARRAY);
GLES11.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
GLES11.glDisable(LOCAL_GL_TEXTURE_EXTERNAL_OES);
GLES11.glLoadIdentity();
if (mBlend)
GLES20.glDisable(GLES20.GL_BLEND);
deactivateProgram();
}
public SurfaceTexture getSurfaceTexture() {

View File

@ -37,16 +37,22 @@
package org.mozilla.gecko.gfx;
import android.util.Log;
import android.opengl.GLES20;
import java.util.Stack;
import java.util.concurrent.ArrayBlockingQueue;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLContext;
public class TextureGenerator {
private static final int MIN_TEXTURES = 5;
private static final String LOGTAG = "TextureGenerator";
private static final int POOL_SIZE = 5;
private static TextureGenerator sSharedInstance;
private Stack<Integer> mTextureIds;
private TextureGenerator() { mTextureIds = new Stack<Integer>(); }
private ArrayBlockingQueue<Integer> mTextureIds;
private EGLContext mContext;
private TextureGenerator() { mTextureIds = new ArrayBlockingQueue<Integer>(POOL_SIZE); }
public static TextureGenerator get() {
if (sSharedInstance == null)
@ -55,17 +61,56 @@ public class TextureGenerator {
}
public synchronized int take() {
if (mTextureIds.empty())
try {
// Will block until one becomes available
return (int)mTextureIds.take();
} catch (InterruptedException e) {
return 0;
}
}
return (int)mTextureIds.pop();
private void evictTextures() {
int[] textures = new int[1];
Integer texture;
while ((texture = mTextureIds.poll()) != null) {
textures[0] = texture;
GLES20.glDeleteTextures(1, textures, 0);
}
}
public synchronized void fill() {
int[] textures = new int[1];
while (mTextureIds.size() < MIN_TEXTURES) {
GLES20.glGenTextures(1, textures, 0);
mTextureIds.push(textures[0]);
EGL10 egl = (EGL10)EGLContext.getEGL();
EGLContext context = egl.eglGetCurrentContext();
if (mContext != null && mContext != context) {
evictTextures();
}
mContext = context;
int numNeeded = mTextureIds.remainingCapacity();
if (numNeeded == 0)
return;
// Clear existing GL errors
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
Log.w(LOGTAG, String.format("Clearing GL error: %#x", error));
}
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
int[] textures = new int[numNeeded];
GLES20.glGenTextures(numNeeded, textures, 0);
error = GLES20.glGetError();
if (error != GLES20.GL_NO_ERROR) {
Log.e(LOGTAG, String.format("Failed to generate textures: %#x", error), new Exception());
return;
}
for (int i = 0; i < numNeeded; i++) {
mTextureIds.offer(textures[i]);
}
}
}

View File

@ -137,9 +137,6 @@ void AndroidMediaLayer::UpdatePosition(const gfxRect& aRect, float aZoomLevel) {
std::map<void*, SurfaceData*>::iterator it;
if (EnsureContentSurface())
AndroidBridge::Bridge()->ShowSurface(mContentData.surface, aRect, mInverted, true);
for (it = mVideoSurfaces.begin(); it != mVideoSurfaces.end(); it++) {
SurfaceData* data = it->second;
@ -152,6 +149,10 @@ void AndroidMediaLayer::UpdatePosition(const gfxRect& aRect, float aZoomLevel) {
scaledDimensions.width, scaledDimensions.height);
AndroidBridge::Bridge()->ShowSurface(data->surface, videoRect, mInverted, false);
}
if (EnsureContentSurface()) {
AndroidBridge::Bridge()->ShowSurface(mContentData.surface, aRect, mInverted, true);
}
}
void AndroidMediaLayer::SetVisible(bool aVisible) {