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 |