| Index: talk/app/webrtc/java/android/org/webrtc/GlRectDrawer.java
|
| diff --git a/talk/app/webrtc/java/android/org/webrtc/GlRectDrawer.java b/talk/app/webrtc/java/android/org/webrtc/GlRectDrawer.java
|
| index 2cb8af754d324ddb71667c640a5cdfd9fb629c52..6d3d5d256399876cb9b1c93957f3b41a03d29f2d 100644
|
| --- a/talk/app/webrtc/java/android/org/webrtc/GlRectDrawer.java
|
| +++ b/talk/app/webrtc/java/android/org/webrtc/GlRectDrawer.java
|
| @@ -40,13 +40,13 @@ import java.util.IdentityHashMap;
|
| import java.util.Map;
|
|
|
| /**
|
| - * Helper class to draw a quad that covers the entire viewport. Rotation, mirror, and cropping is
|
| - * specified using a 4x4 texture coordinate transform matrix. The frame input can either be an OES
|
| - * texture or YUV textures in I420 format. The GL state must be preserved between draw calls, this
|
| - * is intentional to maximize performance. The function release() must be called manually to free
|
| - * the resources held by this object.
|
| + * Helper class to draw an opaque quad on the target viewport location. Rotation, mirror, and
|
| + * cropping is specified using a 4x4 texture coordinate transform matrix. The frame input can either
|
| + * be an OES texture or YUV textures in I420 format. The GL state must be preserved between draw
|
| + * calls, this is intentional to maximize performance. The function release() must be called
|
| + * manually to free the resources held by this object.
|
| */
|
| -public class GlRectDrawer {
|
| +public class GlRectDrawer implements RendererCommon.GlDrawer {
|
| // Simple vertex shader, used for both YUV and OES.
|
| private static final String VERTEX_SHADER_STRING =
|
| "varying vec2 interp_tc;\n"
|
| @@ -118,67 +118,31 @@ public class GlRectDrawer {
|
| 1.0f, 1.0f // Top right.
|
| });
|
|
|
| - // The keys are one of the fragments shaders above.
|
| - private final Map<String, GlShader> shaders = new IdentityHashMap<String, GlShader>();
|
| - private GlShader currentShader;
|
| - private float[] currentTexMatrix;
|
| - private int texMatrixLocation;
|
| - // Intermediate copy buffer for uploading yuv frames that are not packed, i.e. stride > width.
|
| - // TODO(magjed): Investigate when GL_UNPACK_ROW_LENGTH is available, or make a custom shader that
|
| - // handles stride and compare performance with intermediate copy.
|
| - private ByteBuffer copyBuffer;
|
| + private static class Shader {
|
| + public final GlShader glShader;
|
| + public final int texMatrixLocation;
|
|
|
| - /**
|
| - * Upload |planes| into |outputYuvTextures|, taking stride into consideration. |outputYuvTextures|
|
| - * must have been generated in advance.
|
| - */
|
| - public void uploadYuvData(
|
| - int[] outputYuvTextures, int width, int height, int[] strides, ByteBuffer[] planes) {
|
| - // Make a first pass to see if we need a temporary copy buffer.
|
| - int copyCapacityNeeded = 0;
|
| - for (int i = 0; i < 3; ++i) {
|
| - final int planeWidth = (i == 0) ? width : width / 2;
|
| - final int planeHeight = (i == 0) ? height : height / 2;
|
| - if (strides[i] > planeWidth) {
|
| - copyCapacityNeeded = Math.max(copyCapacityNeeded, planeWidth * planeHeight);
|
| - }
|
| - }
|
| - // Allocate copy buffer if necessary.
|
| - if (copyCapacityNeeded > 0
|
| - && (copyBuffer == null || copyBuffer.capacity() < copyCapacityNeeded)) {
|
| - copyBuffer = ByteBuffer.allocateDirect(copyCapacityNeeded);
|
| - }
|
| - // Upload each plane.
|
| - for (int i = 0; i < 3; ++i) {
|
| - GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i);
|
| - GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, outputYuvTextures[i]);
|
| - final int planeWidth = (i == 0) ? width : width / 2;
|
| - final int planeHeight = (i == 0) ? height : height / 2;
|
| - // GLES only accepts packed data, i.e. stride == planeWidth.
|
| - final ByteBuffer packedByteBuffer;
|
| - if (strides[i] == planeWidth) {
|
| - // Input is packed already.
|
| - packedByteBuffer = planes[i];
|
| - } else {
|
| - VideoRenderer.nativeCopyPlane(
|
| - planes[i], planeWidth, planeHeight, strides[i], copyBuffer, planeWidth);
|
| - packedByteBuffer = copyBuffer;
|
| - }
|
| - GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, planeWidth, planeHeight, 0,
|
| - GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, packedByteBuffer);
|
| + public Shader(String fragmentShader) {
|
| + this.glShader = new GlShader(VERTEX_SHADER_STRING, fragmentShader);
|
| + this.texMatrixLocation = glShader.getUniformLocation("texMatrix");
|
| }
|
| }
|
|
|
| + // The keys are one of the fragments shaders above.
|
| + private final Map<String, Shader> shaders = new IdentityHashMap<String, Shader>();
|
| +
|
| /**
|
| * Draw an OES texture frame with specified texture transformation matrix. Required resources are
|
| * allocated at the first call to this function.
|
| */
|
| - public void drawOes(int oesTextureId, float[] texMatrix) {
|
| - prepareShader(OES_FRAGMENT_SHADER_STRING);
|
| + @Override
|
| + public void drawOes(int oesTextureId, float[] texMatrix, int x, int y, int width, int height) {
|
| + prepareShader(OES_FRAGMENT_SHADER_STRING, texMatrix);
|
| + GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
|
| // updateTexImage() may be called from another thread in another EGL context, so we need to
|
| // bind/unbind the texture in each draw call so that GLES understads it's a new texture.
|
| GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, oesTextureId);
|
| - drawRectangle(texMatrix);
|
| + drawRectangle(x, y, width, height);
|
| GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
|
| }
|
|
|
| @@ -186,10 +150,12 @@ public class GlRectDrawer {
|
| * Draw a RGB(A) texture frame with specified texture transformation matrix. Required resources
|
| * are allocated at the first call to this function.
|
| */
|
| - public void drawRgb(int textureId, float[] texMatrix) {
|
| - prepareShader(RGB_FRAGMENT_SHADER_STRING);
|
| + @Override
|
| + public void drawRgb(int textureId, float[] texMatrix, int x, int y, int width, int height) {
|
| + prepareShader(RGB_FRAGMENT_SHADER_STRING, texMatrix);
|
| + GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
|
| GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
|
| - drawRectangle(texMatrix);
|
| + drawRectangle(x, y, width, height);
|
| // Unbind the texture as a precaution.
|
| GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
|
| }
|
| @@ -198,14 +164,15 @@ public class GlRectDrawer {
|
| * Draw a YUV frame with specified texture transformation matrix. Required resources are
|
| * allocated at the first call to this function.
|
| */
|
| - public void drawYuv(int[] yuvTextures, float[] texMatrix) {
|
| - prepareShader(YUV_FRAGMENT_SHADER_STRING);
|
| + @Override
|
| + public void drawYuv(int[] yuvTextures, float[] texMatrix, int x, int y, int width, int height) {
|
| + prepareShader(YUV_FRAGMENT_SHADER_STRING, texMatrix);
|
| // Bind the textures.
|
| for (int i = 0; i < 3; ++i) {
|
| GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i);
|
| GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, yuvTextures[i]);
|
| }
|
| - drawRectangle(texMatrix);
|
| + drawRectangle(x, y, width, height);
|
| // Unbind the textures as a precaution..
|
| for (int i = 0; i < 3; ++i) {
|
| GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i);
|
| @@ -213,60 +180,51 @@ public class GlRectDrawer {
|
| }
|
| }
|
|
|
| - private void drawRectangle(float[] texMatrix) {
|
| - // Try avoid uploading the texture if possible.
|
| - if (!Arrays.equals(currentTexMatrix, texMatrix)) {
|
| - currentTexMatrix = texMatrix.clone();
|
| - // Copy the texture transformation matrix over.
|
| - GLES20.glUniformMatrix4fv(texMatrixLocation, 1, false, texMatrix, 0);
|
| - }
|
| + private void drawRectangle(int x, int y, int width, int height) {
|
| // Draw quad.
|
| + GLES20.glViewport(x, y, width, height);
|
| GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
|
| }
|
|
|
| - private void prepareShader(String fragmentShader) {
|
| - // Lazy allocation.
|
| - if (!shaders.containsKey(fragmentShader)) {
|
| - final GlShader shader = new GlShader(VERTEX_SHADER_STRING, fragmentShader);
|
| + private void prepareShader(String fragmentShader, float[] texMatrix) {
|
| + final Shader shader;
|
| + if (shaders.containsKey(fragmentShader)) {
|
| + shader = shaders.get(fragmentShader);
|
| + } else {
|
| + // Lazy allocation.
|
| + shader = new Shader(fragmentShader);
|
| shaders.put(fragmentShader, shader);
|
| - shader.useProgram();
|
| + shader.glShader.useProgram();
|
| // Initialize fragment shader uniform values.
|
| if (fragmentShader == YUV_FRAGMENT_SHADER_STRING) {
|
| - GLES20.glUniform1i(shader.getUniformLocation("y_tex"), 0);
|
| - GLES20.glUniform1i(shader.getUniformLocation("u_tex"), 1);
|
| - GLES20.glUniform1i(shader.getUniformLocation("v_tex"), 2);
|
| + GLES20.glUniform1i(shader.glShader.getUniformLocation("y_tex"), 0);
|
| + GLES20.glUniform1i(shader.glShader.getUniformLocation("u_tex"), 1);
|
| + GLES20.glUniform1i(shader.glShader.getUniformLocation("v_tex"), 2);
|
| } else if (fragmentShader == RGB_FRAGMENT_SHADER_STRING) {
|
| - GLES20.glUniform1i(shader.getUniformLocation("rgb_tex"), 0);
|
| + GLES20.glUniform1i(shader.glShader.getUniformLocation("rgb_tex"), 0);
|
| } else if (fragmentShader == OES_FRAGMENT_SHADER_STRING) {
|
| - GLES20.glUniform1i(shader.getUniformLocation("oes_tex"), 0);
|
| + GLES20.glUniform1i(shader.glShader.getUniformLocation("oes_tex"), 0);
|
| } else {
|
| throw new IllegalStateException("Unknown fragment shader: " + fragmentShader);
|
| }
|
| GlUtil.checkNoGLES2Error("Initialize fragment shader uniform values.");
|
| // Initialize vertex shader attributes.
|
| - shader.setVertexAttribArray("in_pos", 2, FULL_RECTANGLE_BUF);
|
| - shader.setVertexAttribArray("in_tc", 2, FULL_RECTANGLE_TEX_BUF);
|
| - }
|
| -
|
| - // Update GLES state if shader is not already current.
|
| - final GlShader shader = shaders.get(fragmentShader);
|
| - if (currentShader != shader) {
|
| - currentShader = shader;
|
| - shader.useProgram();
|
| - GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
|
| - currentTexMatrix = null;
|
| - texMatrixLocation = shader.getUniformLocation("texMatrix");
|
| + shader.glShader.setVertexAttribArray("in_pos", 2, FULL_RECTANGLE_BUF);
|
| + shader.glShader.setVertexAttribArray("in_tc", 2, FULL_RECTANGLE_TEX_BUF);
|
| }
|
| + shader.glShader.useProgram();
|
| + // Copy the texture transformation matrix over.
|
| + GLES20.glUniformMatrix4fv(shader.texMatrixLocation, 1, false, texMatrix, 0);
|
| }
|
|
|
| /**
|
| * Release all GLES resources. This needs to be done manually, otherwise the resources are leaked.
|
| */
|
| + @Override
|
| public void release() {
|
| - for (GlShader shader : shaders.values()) {
|
| - shader.release();
|
| + for (Shader shader : shaders.values()) {
|
| + shader.glShader.release();
|
| }
|
| shaders.clear();
|
| - copyBuffer = null;
|
| }
|
| }
|
|
|