| OLD | NEW |
| 1 /* | 1 /* |
| 2 * libjingle | 2 * libjingle |
| 3 * Copyright 2015 Google Inc. | 3 * Copyright 2015 Google Inc. |
| 4 * | 4 * |
| 5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions are met: | 6 * modification, are permitted provided that the following conditions are met: |
| 7 * | 7 * |
| 8 * 1. Redistributions of source code must retain the above copyright notice, | 8 * 1. Redistributions of source code must retain the above copyright notice, |
| 9 * this list of conditions and the following disclaimer. | 9 * this list of conditions and the following disclaimer. |
| 10 * 2. Redistributions in binary form must reproduce the above copyright notice, | 10 * 2. Redistributions in binary form must reproduce the above copyright notice, |
| (...skipping 22 matching lines...) Expand all Loading... |
| 33 import org.webrtc.GlShader; | 33 import org.webrtc.GlShader; |
| 34 import org.webrtc.GlUtil; | 34 import org.webrtc.GlUtil; |
| 35 | 35 |
| 36 import java.nio.ByteBuffer; | 36 import java.nio.ByteBuffer; |
| 37 import java.nio.FloatBuffer; | 37 import java.nio.FloatBuffer; |
| 38 import java.util.Arrays; | 38 import java.util.Arrays; |
| 39 import java.util.IdentityHashMap; | 39 import java.util.IdentityHashMap; |
| 40 import java.util.Map; | 40 import java.util.Map; |
| 41 | 41 |
| 42 /** | 42 /** |
| 43 * Helper class to draw a quad that covers the entire viewport. Rotation, mirror
, and cropping is | 43 * Helper class to draw an opaque quad on the target viewport location. Rotation
, mirror, and |
| 44 * specified using a 4x4 texture coordinate transform matrix. The frame input ca
n either be an OES | 44 * cropping is specified using a 4x4 texture coordinate transform matrix. The fr
ame input can either |
| 45 * texture or YUV textures in I420 format. The GL state must be preserved betwee
n draw calls, this | 45 * be an OES texture or YUV textures in I420 format. The GL state must be preser
ved between draw |
| 46 * is intentional to maximize performance. The function release() must be called
manually to free | 46 * calls, this is intentional to maximize performance. The function release() mu
st be called |
| 47 * the resources held by this object. | 47 * manually to free the resources held by this object. |
| 48 */ | 48 */ |
| 49 public class GlRectDrawer { | 49 public class GlRectDrawer implements RendererCommon.GlDrawer { |
| 50 // Simple vertex shader, used for both YUV and OES. | 50 // Simple vertex shader, used for both YUV and OES. |
| 51 private static final String VERTEX_SHADER_STRING = | 51 private static final String VERTEX_SHADER_STRING = |
| 52 "varying vec2 interp_tc;\n" | 52 "varying vec2 interp_tc;\n" |
| 53 + "attribute vec4 in_pos;\n" | 53 + "attribute vec4 in_pos;\n" |
| 54 + "attribute vec4 in_tc;\n" | 54 + "attribute vec4 in_tc;\n" |
| 55 + "\n" | 55 + "\n" |
| 56 + "uniform mat4 texMatrix;\n" | 56 + "uniform mat4 texMatrix;\n" |
| 57 + "\n" | 57 + "\n" |
| 58 + "void main() {\n" | 58 + "void main() {\n" |
| 59 + " gl_Position = in_pos;\n" | 59 + " gl_Position = in_pos;\n" |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 111 | 111 |
| 112 // Texture coordinates - (0, 0) is bottom-left and (1, 1) is top-right. | 112 // Texture coordinates - (0, 0) is bottom-left and (1, 1) is top-right. |
| 113 private static final FloatBuffer FULL_RECTANGLE_TEX_BUF = | 113 private static final FloatBuffer FULL_RECTANGLE_TEX_BUF = |
| 114 GlUtil.createFloatBuffer(new float[] { | 114 GlUtil.createFloatBuffer(new float[] { |
| 115 0.0f, 0.0f, // Bottom left. | 115 0.0f, 0.0f, // Bottom left. |
| 116 1.0f, 0.0f, // Bottom right. | 116 1.0f, 0.0f, // Bottom right. |
| 117 0.0f, 1.0f, // Top left. | 117 0.0f, 1.0f, // Top left. |
| 118 1.0f, 1.0f // Top right. | 118 1.0f, 1.0f // Top right. |
| 119 }); | 119 }); |
| 120 | 120 |
| 121 // The keys are one of the fragments shaders above. | 121 private static class Shader { |
| 122 private final Map<String, GlShader> shaders = new IdentityHashMap<String, GlSh
ader>(); | 122 public final GlShader glShader; |
| 123 private GlShader currentShader; | 123 public final int texMatrixLocation; |
| 124 private float[] currentTexMatrix; | |
| 125 private int texMatrixLocation; | |
| 126 // Intermediate copy buffer for uploading yuv frames that are not packed, i.e.
stride > width. | |
| 127 // TODO(magjed): Investigate when GL_UNPACK_ROW_LENGTH is available, or make a
custom shader that | |
| 128 // handles stride and compare performance with intermediate copy. | |
| 129 private ByteBuffer copyBuffer; | |
| 130 | 124 |
| 131 /** | 125 public Shader(String fragmentShader) { |
| 132 * Upload |planes| into |outputYuvTextures|, taking stride into consideration.
|outputYuvTextures| | 126 this.glShader = new GlShader(VERTEX_SHADER_STRING, fragmentShader); |
| 133 * must have been generated in advance. | 127 this.texMatrixLocation = glShader.getUniformLocation("texMatrix"); |
| 134 */ | |
| 135 public void uploadYuvData( | |
| 136 int[] outputYuvTextures, int width, int height, int[] strides, ByteBuffer[
] planes) { | |
| 137 // Make a first pass to see if we need a temporary copy buffer. | |
| 138 int copyCapacityNeeded = 0; | |
| 139 for (int i = 0; i < 3; ++i) { | |
| 140 final int planeWidth = (i == 0) ? width : width / 2; | |
| 141 final int planeHeight = (i == 0) ? height : height / 2; | |
| 142 if (strides[i] > planeWidth) { | |
| 143 copyCapacityNeeded = Math.max(copyCapacityNeeded, planeWidth * planeHeig
ht); | |
| 144 } | |
| 145 } | |
| 146 // Allocate copy buffer if necessary. | |
| 147 if (copyCapacityNeeded > 0 | |
| 148 && (copyBuffer == null || copyBuffer.capacity() < copyCapacityNeeded)) { | |
| 149 copyBuffer = ByteBuffer.allocateDirect(copyCapacityNeeded); | |
| 150 } | |
| 151 // Upload each plane. | |
| 152 for (int i = 0; i < 3; ++i) { | |
| 153 GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i); | |
| 154 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, outputYuvTextures[i]); | |
| 155 final int planeWidth = (i == 0) ? width : width / 2; | |
| 156 final int planeHeight = (i == 0) ? height : height / 2; | |
| 157 // GLES only accepts packed data, i.e. stride == planeWidth. | |
| 158 final ByteBuffer packedByteBuffer; | |
| 159 if (strides[i] == planeWidth) { | |
| 160 // Input is packed already. | |
| 161 packedByteBuffer = planes[i]; | |
| 162 } else { | |
| 163 VideoRenderer.nativeCopyPlane( | |
| 164 planes[i], planeWidth, planeHeight, strides[i], copyBuffer, planeWid
th); | |
| 165 packedByteBuffer = copyBuffer; | |
| 166 } | |
| 167 GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, planeWid
th, planeHeight, 0, | |
| 168 GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, packedByteBuffer); | |
| 169 } | 128 } |
| 170 } | 129 } |
| 171 | 130 |
| 131 // The keys are one of the fragments shaders above. |
| 132 private final Map<String, Shader> shaders = new IdentityHashMap<String, Shader
>(); |
| 133 |
| 172 /** | 134 /** |
| 173 * Draw an OES texture frame with specified texture transformation matrix. Req
uired resources are | 135 * Draw an OES texture frame with specified texture transformation matrix. Req
uired resources are |
| 174 * allocated at the first call to this function. | 136 * allocated at the first call to this function. |
| 175 */ | 137 */ |
| 176 public void drawOes(int oesTextureId, float[] texMatrix) { | 138 @Override |
| 177 prepareShader(OES_FRAGMENT_SHADER_STRING); | 139 public void drawOes(int oesTextureId, float[] texMatrix, int x, int y, int wid
th, int height) { |
| 140 prepareShader(OES_FRAGMENT_SHADER_STRING, texMatrix); |
| 141 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); |
| 178 // updateTexImage() may be called from another thread in another EGL context
, so we need to | 142 // updateTexImage() may be called from another thread in another EGL context
, so we need to |
| 179 // bind/unbind the texture in each draw call so that GLES understads it's a
new texture. | 143 // bind/unbind the texture in each draw call so that GLES understads it's a
new texture. |
| 180 GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, oesTextureId); | 144 GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, oesTextureId); |
| 181 drawRectangle(texMatrix); | 145 drawRectangle(x, y, width, height); |
| 182 GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0); | 146 GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0); |
| 183 } | 147 } |
| 184 | 148 |
| 185 /** | 149 /** |
| 186 * Draw a RGB(A) texture frame with specified texture transformation matrix. R
equired resources | 150 * Draw a RGB(A) texture frame with specified texture transformation matrix. R
equired resources |
| 187 * are allocated at the first call to this function. | 151 * are allocated at the first call to this function. |
| 188 */ | 152 */ |
| 189 public void drawRgb(int textureId, float[] texMatrix) { | 153 @Override |
| 190 prepareShader(RGB_FRAGMENT_SHADER_STRING); | 154 public void drawRgb(int textureId, float[] texMatrix, int x, int y, int width,
int height) { |
| 155 prepareShader(RGB_FRAGMENT_SHADER_STRING, texMatrix); |
| 156 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); |
| 191 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId); | 157 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId); |
| 192 drawRectangle(texMatrix); | 158 drawRectangle(x, y, width, height); |
| 193 // Unbind the texture as a precaution. | 159 // Unbind the texture as a precaution. |
| 194 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); | 160 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); |
| 195 } | 161 } |
| 196 | 162 |
| 197 /** | 163 /** |
| 198 * Draw a YUV frame with specified texture transformation matrix. Required res
ources are | 164 * Draw a YUV frame with specified texture transformation matrix. Required res
ources are |
| 199 * allocated at the first call to this function. | 165 * allocated at the first call to this function. |
| 200 */ | 166 */ |
| 201 public void drawYuv(int[] yuvTextures, float[] texMatrix) { | 167 @Override |
| 202 prepareShader(YUV_FRAGMENT_SHADER_STRING); | 168 public void drawYuv(int[] yuvTextures, float[] texMatrix, int x, int y, int wi
dth, int height) { |
| 169 prepareShader(YUV_FRAGMENT_SHADER_STRING, texMatrix); |
| 203 // Bind the textures. | 170 // Bind the textures. |
| 204 for (int i = 0; i < 3; ++i) { | 171 for (int i = 0; i < 3; ++i) { |
| 205 GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i); | 172 GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i); |
| 206 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, yuvTextures[i]); | 173 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, yuvTextures[i]); |
| 207 } | 174 } |
| 208 drawRectangle(texMatrix); | 175 drawRectangle(x, y, width, height); |
| 209 // Unbind the textures as a precaution.. | 176 // Unbind the textures as a precaution.. |
| 210 for (int i = 0; i < 3; ++i) { | 177 for (int i = 0; i < 3; ++i) { |
| 211 GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i); | 178 GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i); |
| 212 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); | 179 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); |
| 213 } | 180 } |
| 214 } | 181 } |
| 215 | 182 |
| 216 private void drawRectangle(float[] texMatrix) { | 183 private void drawRectangle(int x, int y, int width, int height) { |
| 217 // Try avoid uploading the texture if possible. | |
| 218 if (!Arrays.equals(currentTexMatrix, texMatrix)) { | |
| 219 currentTexMatrix = texMatrix.clone(); | |
| 220 // Copy the texture transformation matrix over. | |
| 221 GLES20.glUniformMatrix4fv(texMatrixLocation, 1, false, texMatrix, 0); | |
| 222 } | |
| 223 // Draw quad. | 184 // Draw quad. |
| 185 GLES20.glViewport(x, y, width, height); |
| 224 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); | 186 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); |
| 225 } | 187 } |
| 226 | 188 |
| 227 private void prepareShader(String fragmentShader) { | 189 private void prepareShader(String fragmentShader, float[] texMatrix) { |
| 228 // Lazy allocation. | 190 final Shader shader; |
| 229 if (!shaders.containsKey(fragmentShader)) { | 191 if (shaders.containsKey(fragmentShader)) { |
| 230 final GlShader shader = new GlShader(VERTEX_SHADER_STRING, fragmentShader)
; | 192 shader = shaders.get(fragmentShader); |
| 193 } else { |
| 194 // Lazy allocation. |
| 195 shader = new Shader(fragmentShader); |
| 231 shaders.put(fragmentShader, shader); | 196 shaders.put(fragmentShader, shader); |
| 232 shader.useProgram(); | 197 shader.glShader.useProgram(); |
| 233 // Initialize fragment shader uniform values. | 198 // Initialize fragment shader uniform values. |
| 234 if (fragmentShader == YUV_FRAGMENT_SHADER_STRING) { | 199 if (fragmentShader == YUV_FRAGMENT_SHADER_STRING) { |
| 235 GLES20.glUniform1i(shader.getUniformLocation("y_tex"), 0); | 200 GLES20.glUniform1i(shader.glShader.getUniformLocation("y_tex"), 0); |
| 236 GLES20.glUniform1i(shader.getUniformLocation("u_tex"), 1); | 201 GLES20.glUniform1i(shader.glShader.getUniformLocation("u_tex"), 1); |
| 237 GLES20.glUniform1i(shader.getUniformLocation("v_tex"), 2); | 202 GLES20.glUniform1i(shader.glShader.getUniformLocation("v_tex"), 2); |
| 238 } else if (fragmentShader == RGB_FRAGMENT_SHADER_STRING) { | 203 } else if (fragmentShader == RGB_FRAGMENT_SHADER_STRING) { |
| 239 GLES20.glUniform1i(shader.getUniformLocation("rgb_tex"), 0); | 204 GLES20.glUniform1i(shader.glShader.getUniformLocation("rgb_tex"), 0); |
| 240 } else if (fragmentShader == OES_FRAGMENT_SHADER_STRING) { | 205 } else if (fragmentShader == OES_FRAGMENT_SHADER_STRING) { |
| 241 GLES20.glUniform1i(shader.getUniformLocation("oes_tex"), 0); | 206 GLES20.glUniform1i(shader.glShader.getUniformLocation("oes_tex"), 0); |
| 242 } else { | 207 } else { |
| 243 throw new IllegalStateException("Unknown fragment shader: " + fragmentSh
ader); | 208 throw new IllegalStateException("Unknown fragment shader: " + fragmentSh
ader); |
| 244 } | 209 } |
| 245 GlUtil.checkNoGLES2Error("Initialize fragment shader uniform values."); | 210 GlUtil.checkNoGLES2Error("Initialize fragment shader uniform values."); |
| 246 // Initialize vertex shader attributes. | 211 // Initialize vertex shader attributes. |
| 247 shader.setVertexAttribArray("in_pos", 2, FULL_RECTANGLE_BUF); | 212 shader.glShader.setVertexAttribArray("in_pos", 2, FULL_RECTANGLE_BUF); |
| 248 shader.setVertexAttribArray("in_tc", 2, FULL_RECTANGLE_TEX_BUF); | 213 shader.glShader.setVertexAttribArray("in_tc", 2, FULL_RECTANGLE_TEX_BUF); |
| 249 } | 214 } |
| 250 | 215 shader.glShader.useProgram(); |
| 251 // Update GLES state if shader is not already current. | 216 // Copy the texture transformation matrix over. |
| 252 final GlShader shader = shaders.get(fragmentShader); | 217 GLES20.glUniformMatrix4fv(shader.texMatrixLocation, 1, false, texMatrix, 0); |
| 253 if (currentShader != shader) { | |
| 254 currentShader = shader; | |
| 255 shader.useProgram(); | |
| 256 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); | |
| 257 currentTexMatrix = null; | |
| 258 texMatrixLocation = shader.getUniformLocation("texMatrix"); | |
| 259 } | |
| 260 } | 218 } |
| 261 | 219 |
| 262 /** | 220 /** |
| 263 * Release all GLES resources. This needs to be done manually, otherwise the r
esources are leaked. | 221 * Release all GLES resources. This needs to be done manually, otherwise the r
esources are leaked. |
| 264 */ | 222 */ |
| 223 @Override |
| 265 public void release() { | 224 public void release() { |
| 266 for (GlShader shader : shaders.values()) { | 225 for (Shader shader : shaders.values()) { |
| 267 shader.release(); | 226 shader.glShader.release(); |
| 268 } | 227 } |
| 269 shaders.clear(); | 228 shaders.clear(); |
| 270 copyBuffer = null; | |
| 271 } | 229 } |
| 272 } | 230 } |
| OLD | NEW |