blob: ea4228eb13047ae9b9a3c72b4456ed81e7446478 [file] [log] [blame]
tkchin04dbb342016-08-08 03:10:07 -07001/*
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
magjed13941912017-05-30 06:11:58 -070013#if TARGET_OS_IPHONE
14#import <OpenGLES/ES3/gl.h>
15#else
16#import <OpenGL/gl3.h>
17#endif
18
magjedfb372f02016-08-10 07:58:29 -070019#include <algorithm>
20#include <array>
tkchin04dbb342016-08-08 03:10:07 -070021#include <memory>
22
magjed13941912017-05-30 06:11:58 -070023#import "RTCOpenGLDefines.h"
tkchin04dbb342016-08-08 03:10:07 -070024
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020025#include "rtc_base/checks.h"
26#include "rtc_base/logging.h"
tkchin04dbb342016-08-08 03:10:07 -070027
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
tkchin04dbb342016-08-08 03:10:07 -070039// 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());
Mirko Bonadei675513b2017-11-09 11:09:25 +010058 RTC_LOG(LS_ERROR) << "Shader compile error: " << compileLog.get();
tkchin04dbb342016-08-08 03:10:07 -070059 }
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 }
tkchin04dbb342016-08-08 03:10:07 -0700104
magjed13941912017-05-30 06:11:58 -0700105 // Set vertex shader variables 'position' and 'texcoord' in program.
tkchin04dbb342016-08-08 03:10:07 -0700106 GLint position = glGetAttribLocation(program, "position");
107 GLint texcoord = glGetAttribLocation(program, "texcoord");
108 if (position < 0 || texcoord < 0) {
magjed13941912017-05-30 06:11:58 -0700109 glDeleteProgram(program);
110 return 0;
tkchin04dbb342016-08-08 03:10:07 -0700111 }
magjed13941912017-05-30 06:11:58 -0700112
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) {
tkchin04dbb342016-08-08 03:10:07 -0700128#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) {
magjed13941912017-05-30 06:11:58 -0700137 glDeleteVertexArrays(1, vertexArray);
tkchin04dbb342016-08-08 03:10:07 -0700138 return NO;
139 }
140 glBindBuffer(GL_ARRAY_BUFFER, *vertexBuffer);
magjedfb372f02016-08-10 07:58:29 -0700141 glBufferData(GL_ARRAY_BUFFER, 4 * 4 * sizeof(GLfloat), NULL, GL_DYNAMIC_DRAW);
tkchin04dbb342016-08-08 03:10:07 -0700142 return YES;
143}
magjedfb372f02016-08-10 07:58:29 -0700144
145// Set vertex data to the currently bound vertex buffer.
magjed7ee51252017-02-21 04:19:46 -0800146void RTCSetVertexData(RTCVideoRotation rotation) {
magjedfb372f02016-08-10 07:58:29 -0700147 // 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) {
magjed7ee51252017-02-21 04:19:46 -0800164 case RTCVideoRotation_0:
magjedfb372f02016-08-10 07:58:29 -0700165 rotation_offset = 0;
166 break;
magjed7ee51252017-02-21 04:19:46 -0800167 case RTCVideoRotation_90:
magjedfb372f02016-08-10 07:58:29 -0700168 rotation_offset = 1;
169 break;
magjed7ee51252017-02-21 04:19:46 -0800170 case RTCVideoRotation_180:
magjedfb372f02016-08-10 07:58:29 -0700171 rotation_offset = 2;
172 break;
magjed7ee51252017-02-21 04:19:46 -0800173 case RTCVideoRotation_270:
magjedfb372f02016-08-10 07:58:29 -0700174 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}