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; |
} |
} |