iOS render: Handle frame rotation in OpenGL

This CL handles frame rotation by updating the OpenGL vertex data in
RTCOpenGLVideoRenderer, instead of calling the expensive
libyuv::I420Rotate that will rotate the actual memory. Also, we can
handle rotated native frames instead of falling back to
NativeToI420Buffer.

Review-Url: https://codereview.webrtc.org/2176623002
Cr-Commit-Position: refs/heads/master@{#13715}
diff --git a/webrtc/sdk/objc/Framework/Classes/RTCShader.mm b/webrtc/sdk/objc/Framework/Classes/RTCShader.mm
index 8808206..26dc64f 100644
--- a/webrtc/sdk/objc/Framework/Classes/RTCShader.mm
+++ b/webrtc/sdk/objc/Framework/Classes/RTCShader.mm
@@ -10,6 +10,8 @@
 
 #import "RTCShader.h"
 
+#include <algorithm>
+#include <array>
 #include <memory>
 
 #import "RTCShader+Private.h"
@@ -28,21 +30,6 @@
   "    v_texcoord = texcoord;\n"
   "}\n";
 
-// When modelview and projection matrices are identity (default) the world is
-// contained in the square around origin with unit size 2. Drawing to these
-// coordinates is equivalent to drawing to the entire screen. The texture is
-// stretched over that square using texture coordinates (u, v) that range
-// from (0, 0) to (1, 1) inclusive. Texture coordinates are flipped vertically
-// here because the incoming frame has origin in upper left hand corner but
-// OpenGL expects origin in bottom left corner.
-static const GLfloat gVertices[] = {
-  // X, Y, U, V.
-  -1, -1, 0, 1,  // Bottom left.
-   1, -1, 1, 1,  // Bottom right.
-   1,  1, 1, 0,  // Top right.
-  -1,  1, 0, 0,  // Top left.
-};
-
 // Compiles a shader of the given |type| with GLSL source |source| and returns
 // the shader handle or 0 on error.
 GLuint RTCCreateShader(GLenum type, const GLchar *source) {
@@ -111,9 +98,8 @@
   return program;
 }
 
-// Set vertex shader variables 'position' and 'texcoord' in |program| to the
-// |gVertices| array above. It will use |vertexBuffer| and |vertexArray| to
-// store the vertex data.
+// Set vertex shader variables 'position' and 'texcoord' in |program| to use
+// |vertexBuffer| and |vertexArray| to store the vertex data.
 BOOL RTCSetupVerticesForProgram(GLuint program, GLuint* vertexBuffer, GLuint* vertexArray) {
   GLint position = glGetAttribLocation(program, "position");
   GLint texcoord = glGetAttribLocation(program, "texcoord");
@@ -132,11 +118,11 @@
     return NO;
   }
   glBindBuffer(GL_ARRAY_BUFFER, *vertexBuffer);
-  glBufferData(GL_ARRAY_BUFFER, sizeof(gVertices), gVertices, GL_DYNAMIC_DRAW);
+  glBufferData(GL_ARRAY_BUFFER, 4 * 4 * sizeof(GLfloat), NULL, GL_DYNAMIC_DRAW);
 
-  // Read position attribute from |gVertices| with size of 2 and stride of 4
-  // beginning at the start of the array. The last argument indicates offset
-  // of data within |gVertices| as supplied to the vertex buffer.
+  // Read position attribute with size of 2 and stride of 4 beginning at the
+  // start of the array. The last argument indicates offset of data within the
+  // vertex buffer.
   glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat),
                         (void *)0);
   glEnableVertexAttribArray(position);
@@ -150,3 +136,49 @@
 
   return YES;
 }
+
+// Set vertex data to the currently bound vertex buffer.
+void RTCSetVertexData(webrtc::VideoRotation rotation) {
+  // When modelview and projection matrices are identity (default) the world is
+  // contained in the square around origin with unit size 2. Drawing to these
+  // coordinates is equivalent to drawing to the entire screen. The texture is
+  // stretched over that square using texture coordinates (u, v) that range
+  // from (0, 0) to (1, 1) inclusive. Texture coordinates are flipped vertically
+  // here because the incoming frame has origin in upper left hand corner but
+  // OpenGL expects origin in bottom left corner.
+  std::array<std::array<GLfloat, 2>, 4> UVCoords = {{
+      {{0, 1}},  // Lower left.
+      {{1, 1}},  // Lower right.
+      {{1, 0}},  // Upper right.
+      {{0, 0}},  // Upper left.
+  }};
+
+  // Rotate the UV coordinates.
+  int rotation_offset;
+  switch (rotation) {
+    case webrtc::kVideoRotation_0:
+      rotation_offset = 0;
+      break;
+    case webrtc::kVideoRotation_90:
+      rotation_offset = 1;
+      break;
+    case webrtc::kVideoRotation_180:
+      rotation_offset = 2;
+      break;
+    case webrtc::kVideoRotation_270:
+      rotation_offset = 3;
+      break;
+  }
+  std::rotate(UVCoords.begin(), UVCoords.begin() + rotation_offset,
+              UVCoords.end());
+
+  const GLfloat gVertices[] = {
+      // X, Y, U, V.
+      -1, -1, UVCoords[0][0], UVCoords[0][1],
+       1, -1, UVCoords[1][0], UVCoords[1][1],
+       1,  1, UVCoords[2][0], UVCoords[2][1],
+      -1,  1, UVCoords[3][0], UVCoords[3][1],
+  };
+
+  glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(gVertices), gVertices);
+}