From ed4224fbdaca23a9ba593d07d6809a0943f73528 Mon Sep 17 00:00:00 2001 From: Magnus Jedvert Date: Wed, 2 Sep 2015 12:52:03 +0200 Subject: [PATCH] Android GlRectDrawer: Add fragment shader for RGB(A) textures Add third shader type for RGB(A) and refactor according to the Rule of three. BUG=webrtc:4742 R=hbos@webrtc.org Review URL: https://codereview.webrtc.org/1311093005 . Cr-Commit-Position: refs/heads/master@{#9843} --- .../java/android/org/webrtc/GlRectDrawer.java | 130 ++++++++++-------- 1 file changed, 74 insertions(+), 56 deletions(-) diff --git a/talk/app/webrtc/java/android/org/webrtc/GlRectDrawer.java b/talk/app/webrtc/java/android/org/webrtc/GlRectDrawer.java index abd0ddf08a..c14abb0219 100644 --- a/talk/app/webrtc/java/android/org/webrtc/GlRectDrawer.java +++ b/talk/app/webrtc/java/android/org/webrtc/GlRectDrawer.java @@ -36,6 +36,8 @@ import org.webrtc.GlUtil; import java.nio.ByteBuffer; import java.nio.FloatBuffer; import java.util.Arrays; +import java.util.IdentityHashMap; +import java.util.Map; /** * Helper class to draw a quad that covers the entire viewport. Rotation, mirror, and cropping is @@ -47,7 +49,7 @@ import java.util.Arrays; public class GlRectDrawer { // Simple vertex shader, used for both YUV and OES. private static final String VERTEX_SHADER_STRING = - "varying vec2 interp_tc;\n" + "varying vec2 interp_tc;\n" + "attribute vec4 in_pos;\n" + "attribute vec4 in_tc;\n" + "\n" @@ -59,7 +61,7 @@ public class GlRectDrawer { + "}\n"; private static final String YUV_FRAGMENT_SHADER_STRING = - "precision mediump float;\n" + "precision mediump float;\n" + "varying vec2 interp_tc;\n" + "\n" + "uniform sampler2D y_tex;\n" @@ -76,8 +78,18 @@ public class GlRectDrawer { + " y + 1.77 * u, 1);\n" + "}\n"; + private static final String RGB_FRAGMENT_SHADER_STRING = + "precision mediump float;\n" + + "varying vec2 interp_tc;\n" + + "\n" + + "uniform sampler2D rgb_tex;\n" + + "\n" + + "void main() {\n" + + " gl_FragColor = texture2D(rgb_tex, interp_tc);\n" + + "}\n"; + private static final String OES_FRAGMENT_SHADER_STRING = - "#extension GL_OES_EGL_image_external : require\n" + "#extension GL_OES_EGL_image_external : require\n" + "precision mediump float;\n" + "varying vec2 interp_tc;\n" + "\n" @@ -103,38 +115,18 @@ public class GlRectDrawer { 1.0f, 1.0f // Top right. }); - private GlShader oesShader; - private GlShader yuvShader; + // The keys are one of the fragments shaders above. + private final Map shaders = new IdentityHashMap(); private GlShader currentShader; private float[] currentTexMatrix; private int texMatrixLocation; - private void initGeometry(GlShader shader) { - shader.setVertexAttribArray("in_pos", 2, FULL_RECTANGLE_BUF); - shader.setVertexAttribArray("in_tc", 2, FULL_RECTANGLE_TEX_BUF); - } - /** * 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) { - // Lazy allocation. - if (oesShader == null) { - oesShader = new GlShader(VERTEX_SHADER_STRING, OES_FRAGMENT_SHADER_STRING); - oesShader.useProgram(); - initGeometry(oesShader); - } - - // Set GLES state to OES. - if (currentShader != oesShader) { - currentShader = oesShader; - oesShader.useProgram(); - GLES20.glActiveTexture(GLES20.GL_TEXTURE0); - currentTexMatrix = null; - texMatrixLocation = oesShader.getUniformLocation("texMatrix"); - } - + prepareShader(OES_FRAGMENT_SHADER_STRING); // 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); @@ -142,40 +134,35 @@ public class GlRectDrawer { GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0); } + /** + * 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); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId); + drawRectangle(texMatrix); + // Unbind the texture as a precaution. + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); + } + /** * Draw a YUV frame with specified texture transformation matrix. Required resources are * allocated at the first call to this function. */ public void drawYuv(int width, int height, int[] yuvTextures, float[] texMatrix) { - // Lazy allocation. - if (yuvShader == null) { - yuvShader = new GlShader(VERTEX_SHADER_STRING, YUV_FRAGMENT_SHADER_STRING); - yuvShader.useProgram(); - - // Set texture samplers. - GLES20.glUniform1i(yuvShader.getUniformLocation("y_tex"), 0); - GLES20.glUniform1i(yuvShader.getUniformLocation("u_tex"), 1); - GLES20.glUniform1i(yuvShader.getUniformLocation("v_tex"), 2); - GlUtil.checkNoGLES2Error("y/u/v_tex glGetUniformLocation"); - - initGeometry(yuvShader); - } - - // Set GLES state to YUV. - if (currentShader != yuvShader) { - currentShader = yuvShader; - yuvShader.useProgram(); - currentTexMatrix = null; - texMatrixLocation = yuvShader.getUniformLocation("texMatrix"); - } - + prepareShader(YUV_FRAGMENT_SHADER_STRING); // 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); + // Unbind the textures as a precaution.. + for (int i = 0; i < 3; ++i) { + GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i); + GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); + } } private void drawRectangle(float[] texMatrix) { @@ -189,17 +176,48 @@ public class GlRectDrawer { 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); + shaders.put(fragmentShader, shader); + shader.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); + } else if (fragmentShader == RGB_FRAGMENT_SHADER_STRING) { + GLES20.glUniform1i(shader.getUniformLocation("rgb_tex"), 0); + } else if (fragmentShader == OES_FRAGMENT_SHADER_STRING) { + GLES20.glUniform1i(shader.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"); + } + } + /** * Release all GLES resources. This needs to be done manually, otherwise the resources are leaked. */ public void release() { - if (oesShader != null) { - oesShader.release(); - oesShader = null; - } - if (yuvShader != null) { - yuvShader.release(); - yuvShader = null; + for (GlShader shader : shaders.values()) { + shader.release(); } + shaders.clear(); } }