1/* 2 * Copyright 2016 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11#import "RTCShader.h" 12 13#if TARGET_OS_IPHONE 14#import <OpenGLES/ES3/gl.h> 15#else 16#import <OpenGL/gl3.h> 17#endif 18 19#include <algorithm> 20#include <array> 21#include <memory> 22 23#import "RTCOpenGLDefines.h" 24 25#include "rtc_base/checks.h" 26#include "rtc_base/logging.h" 27 28// Vertex shader doesn't do anything except pass coordinates through. 29const char kRTCVertexShaderSource[] = 30 SHADER_VERSION 31 VERTEX_SHADER_IN " vec2 position;\n" 32 VERTEX_SHADER_IN " vec2 texcoord;\n" 33 VERTEX_SHADER_OUT " vec2 v_texcoord;\n" 34 "void main() {\n" 35 " gl_Position = vec4(position.x, position.y, 0.0, 1.0);\n" 36 " v_texcoord = texcoord;\n" 37 "}\n"; 38 39// Compiles a shader of the given |type| with GLSL source |source| and returns 40// the shader handle or 0 on error. 41GLuint RTCCreateShader(GLenum type, const GLchar *source) { 42 GLuint shader = glCreateShader(type); 43 if (!shader) { 44 return 0; 45 } 46 glShaderSource(shader, 1, &source, NULL); 47 glCompileShader(shader); 48 GLint compileStatus = GL_FALSE; 49 glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus); 50 if (compileStatus == GL_FALSE) { 51 GLint logLength = 0; 52 // The null termination character is included in the returned log length. 53 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength); 54 if (logLength > 0) { 55 std::unique_ptr<char[]> compileLog(new char[logLength]); 56 // The returned string is null terminated. 57 glGetShaderInfoLog(shader, logLength, NULL, compileLog.get()); 58 RTC_LOG(LS_ERROR) << "Shader compile error: " << compileLog.get(); 59 } 60 glDeleteShader(shader); 61 shader = 0; 62 } 63 return shader; 64} 65 66// Links a shader program with the given vertex and fragment shaders and 67// returns the program handle or 0 on error. 68GLuint RTCCreateProgram(GLuint vertexShader, GLuint fragmentShader) { 69 if (vertexShader == 0 || fragmentShader == 0) { 70 return 0; 71 } 72 GLuint program = glCreateProgram(); 73 if (!program) { 74 return 0; 75 } 76 glAttachShader(program, vertexShader); 77 glAttachShader(program, fragmentShader); 78 glLinkProgram(program); 79 GLint linkStatus = GL_FALSE; 80 glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); 81 if (linkStatus == GL_FALSE) { 82 glDeleteProgram(program); 83 program = 0; 84 } 85 return program; 86} 87 88// Creates and links a shader program with the given fragment shader source and 89// a plain vertex shader. Returns the program handle or 0 on error. 90GLuint RTCCreateProgramFromFragmentSource(const char fragmentShaderSource[]) { 91 GLuint vertexShader = RTCCreateShader(GL_VERTEX_SHADER, kRTCVertexShaderSource); 92 RTC_CHECK(vertexShader) << "failed to create vertex shader"; 93 GLuint fragmentShader = 94 RTCCreateShader(GL_FRAGMENT_SHADER, fragmentShaderSource); 95 RTC_CHECK(fragmentShader) << "failed to create fragment shader"; 96 GLuint program = RTCCreateProgram(vertexShader, fragmentShader); 97 // Shaders are created only to generate program. 98 if (vertexShader) { 99 glDeleteShader(vertexShader); 100 } 101 if (fragmentShader) { 102 glDeleteShader(fragmentShader); 103 } 104 105 // Set vertex shader variables 'position' and 'texcoord' in program. 106 GLint position = glGetAttribLocation(program, "position"); 107 GLint texcoord = glGetAttribLocation(program, "texcoord"); 108 if (position < 0 || texcoord < 0) { 109 glDeleteProgram(program); 110 return 0; 111 } 112 113 // Read position attribute with size of 2 and stride of 4 beginning at the start of the array. The 114 // last argument indicates offset of data within the vertex buffer. 115 glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void *)0); 116 glEnableVertexAttribArray(position); 117 118 // Read texcoord attribute with size of 2 and stride of 4 beginning at the first texcoord in the 119 // array. The last argument indicates offset of data within the vertex buffer. 120 glVertexAttribPointer( 121 texcoord, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (void *)(2 * sizeof(GLfloat))); 122 glEnableVertexAttribArray(texcoord); 123 124 return program; 125} 126 127BOOL RTCCreateVertexBuffer(GLuint *vertexBuffer, GLuint *vertexArray) { 128#if !TARGET_OS_IPHONE 129 glGenVertexArrays(1, vertexArray); 130 if (*vertexArray == 0) { 131 return NO; 132 } 133 glBindVertexArray(*vertexArray); 134#endif 135 glGenBuffers(1, vertexBuffer); 136 if (*vertexBuffer == 0) { 137 glDeleteVertexArrays(1, vertexArray); 138 return NO; 139 } 140 glBindBuffer(GL_ARRAY_BUFFER, *vertexBuffer); 141 glBufferData(GL_ARRAY_BUFFER, 4 * 4 * sizeof(GLfloat), NULL, GL_DYNAMIC_DRAW); 142 return YES; 143} 144 145// Set vertex data to the currently bound vertex buffer. 146void RTCSetVertexData(RTCVideoRotation rotation) { 147 // When modelview and projection matrices are identity (default) the world is 148 // contained in the square around origin with unit size 2. Drawing to these 149 // coordinates is equivalent to drawing to the entire screen. The texture is 150 // stretched over that square using texture coordinates (u, v) that range 151 // from (0, 0) to (1, 1) inclusive. Texture coordinates are flipped vertically 152 // here because the incoming frame has origin in upper left hand corner but 153 // OpenGL expects origin in bottom left corner. 154 std::array<std::array<GLfloat, 2>, 4> UVCoords = {{ 155 {{0, 1}}, // Lower left. 156 {{1, 1}}, // Lower right. 157 {{1, 0}}, // Upper right. 158 {{0, 0}}, // Upper left. 159 }}; 160 161 // Rotate the UV coordinates. 162 int rotation_offset; 163 switch (rotation) { 164 case RTCVideoRotation_0: 165 rotation_offset = 0; 166 break; 167 case RTCVideoRotation_90: 168 rotation_offset = 1; 169 break; 170 case RTCVideoRotation_180: 171 rotation_offset = 2; 172 break; 173 case RTCVideoRotation_270: 174 rotation_offset = 3; 175 break; 176 } 177 std::rotate(UVCoords.begin(), UVCoords.begin() + rotation_offset, 178 UVCoords.end()); 179 180 const GLfloat gVertices[] = { 181 // X, Y, U, V. 182 -1, -1, UVCoords[0][0], UVCoords[0][1], 183 1, -1, UVCoords[1][0], UVCoords[1][1], 184 1, 1, UVCoords[2][0], UVCoords[2][1], 185 -1, 1, UVCoords[3][0], UVCoords[3][1], 186 }; 187 188 glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(gVertices), gVertices); 189} 190