blob: c5fbde12c82b1b71a8c98fe2d91e6fbd19833e4b [file] [log] [blame]
magjed13941912017-05-30 06:11:58 -07001/*
2 * Copyright 2017 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 "RTCDefaultShader.h"
12
13#if TARGET_OS_IPHONE
14#import <OpenGLES/ES3/gl.h>
15#else
16#import <OpenGL/gl3.h>
17#endif
18
19#import "RTCOpenGLDefines.h"
20#import "RTCShader.h"
21#import "WebRTC/RTCLogging.h"
22
Danil Chapovalov196100e2018-06-21 10:17:24 +020023#include "absl/types/optional.h"
magjed13941912017-05-30 06:11:58 -070024
25static const int kYTextureUnit = 0;
26static const int kUTextureUnit = 1;
27static const int kVTextureUnit = 2;
28static const int kUvTextureUnit = 1;
29
30// Fragment shader converts YUV values from input textures into a final RGB
31// pixel. The conversion formula is from http://www.fourcc.org/fccyvrgb.php.
32static const char kI420FragmentShaderSource[] =
33 SHADER_VERSION
34 "precision highp float;"
35 FRAGMENT_SHADER_IN " vec2 v_texcoord;\n"
36 "uniform lowp sampler2D s_textureY;\n"
37 "uniform lowp sampler2D s_textureU;\n"
38 "uniform lowp sampler2D s_textureV;\n"
39 FRAGMENT_SHADER_OUT
40 "void main() {\n"
41 " float y, u, v, r, g, b;\n"
42 " y = " FRAGMENT_SHADER_TEXTURE "(s_textureY, v_texcoord).r;\n"
43 " u = " FRAGMENT_SHADER_TEXTURE "(s_textureU, v_texcoord).r;\n"
44 " v = " FRAGMENT_SHADER_TEXTURE "(s_textureV, v_texcoord).r;\n"
45 " u = u - 0.5;\n"
46 " v = v - 0.5;\n"
47 " r = y + 1.403 * v;\n"
48 " g = y - 0.344 * u - 0.714 * v;\n"
49 " b = y + 1.770 * u;\n"
50 " " FRAGMENT_SHADER_COLOR " = vec4(r, g, b, 1.0);\n"
51 " }\n";
52
53static const char kNV12FragmentShaderSource[] =
54 SHADER_VERSION
55 "precision mediump float;"
56 FRAGMENT_SHADER_IN " vec2 v_texcoord;\n"
57 "uniform lowp sampler2D s_textureY;\n"
58 "uniform lowp sampler2D s_textureUV;\n"
59 FRAGMENT_SHADER_OUT
60 "void main() {\n"
61 " mediump float y;\n"
62 " mediump vec2 uv;\n"
63 " y = " FRAGMENT_SHADER_TEXTURE "(s_textureY, v_texcoord).r;\n"
64 " uv = " FRAGMENT_SHADER_TEXTURE "(s_textureUV, v_texcoord).ra -\n"
65 " vec2(0.5, 0.5);\n"
66 " " FRAGMENT_SHADER_COLOR " = vec4(y + 1.403 * uv.y,\n"
67 " y - 0.344 * uv.x - 0.714 * uv.y,\n"
68 " y + 1.770 * uv.x,\n"
69 " 1.0);\n"
70 " }\n";
71
72@implementation RTCDefaultShader {
73 GLuint _vertexBuffer;
74 GLuint _vertexArray;
75 // Store current rotation and only upload new vertex data when rotation changes.
Danil Chapovalov196100e2018-06-21 10:17:24 +020076 absl::optional<RTCVideoRotation> _currentRotation;
magjed13941912017-05-30 06:11:58 -070077
78 GLuint _i420Program;
79 GLuint _nv12Program;
80}
81
82- (void)dealloc {
83 glDeleteProgram(_i420Program);
84 glDeleteProgram(_nv12Program);
85 glDeleteBuffers(1, &_vertexBuffer);
86 glDeleteVertexArrays(1, &_vertexArray);
87}
88
89- (BOOL)createAndSetupI420Program {
90 NSAssert(!_i420Program, @"I420 program already created");
91 _i420Program = RTCCreateProgramFromFragmentSource(kI420FragmentShaderSource);
92 if (!_i420Program) {
93 return NO;
94 }
95 GLint ySampler = glGetUniformLocation(_i420Program, "s_textureY");
96 GLint uSampler = glGetUniformLocation(_i420Program, "s_textureU");
97 GLint vSampler = glGetUniformLocation(_i420Program, "s_textureV");
98
99 if (ySampler < 0 || uSampler < 0 || vSampler < 0) {
100 RTCLog(@"Failed to get uniform variable locations in I420 shader");
101 glDeleteProgram(_i420Program);
102 _i420Program = 0;
103 return NO;
104 }
105
106 glUseProgram(_i420Program);
107 glUniform1i(ySampler, kYTextureUnit);
108 glUniform1i(uSampler, kUTextureUnit);
109 glUniform1i(vSampler, kVTextureUnit);
110
111 return YES;
112}
113
114- (BOOL)createAndSetupNV12Program {
115 NSAssert(!_nv12Program, @"NV12 program already created");
116 _nv12Program = RTCCreateProgramFromFragmentSource(kNV12FragmentShaderSource);
117 if (!_nv12Program) {
118 return NO;
119 }
120 GLint ySampler = glGetUniformLocation(_nv12Program, "s_textureY");
121 GLint uvSampler = glGetUniformLocation(_nv12Program, "s_textureUV");
122
123 if (ySampler < 0 || uvSampler < 0) {
124 RTCLog(@"Failed to get uniform variable locations in NV12 shader");
125 glDeleteProgram(_nv12Program);
126 _nv12Program = 0;
127 return NO;
128 }
129
130 glUseProgram(_nv12Program);
131 glUniform1i(ySampler, kYTextureUnit);
132 glUniform1i(uvSampler, kUvTextureUnit);
133
134 return YES;
135}
136
137- (BOOL)prepareVertexBufferWithRotation:(RTCVideoRotation)rotation {
138 if (!_vertexBuffer && !RTCCreateVertexBuffer(&_vertexBuffer, &_vertexArray)) {
139 RTCLog(@"Failed to setup vertex buffer");
140 return NO;
141 }
142#if !TARGET_OS_IPHONE
143 glBindVertexArray(_vertexArray);
144#endif
145 glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
146 if (!_currentRotation || rotation != *_currentRotation) {
Danil Chapovalov196100e2018-06-21 10:17:24 +0200147 _currentRotation = absl::optional<RTCVideoRotation>(rotation);
magjed13941912017-05-30 06:11:58 -0700148 RTCSetVertexData(*_currentRotation);
149 }
150 return YES;
151}
152
Magnus Jedvert6b9653e2017-06-05 17:58:34 +0200153- (void)applyShadingForFrameWithWidth:(int)width
154 height:(int)height
155 rotation:(RTCVideoRotation)rotation
156 yPlane:(GLuint)yPlane
157 uPlane:(GLuint)uPlane
158 vPlane:(GLuint)vPlane {
magjed13941912017-05-30 06:11:58 -0700159 if (![self prepareVertexBufferWithRotation:rotation]) {
160 return;
161 }
162
163 if (!_i420Program && ![self createAndSetupI420Program]) {
164 RTCLog(@"Failed to setup I420 program");
165 return;
166 }
167
168 glUseProgram(_i420Program);
169
170 glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + kYTextureUnit));
171 glBindTexture(GL_TEXTURE_2D, yPlane);
172
173 glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + kUTextureUnit));
174 glBindTexture(GL_TEXTURE_2D, uPlane);
175
176 glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + kVTextureUnit));
177 glBindTexture(GL_TEXTURE_2D, vPlane);
178
179 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
180}
181
Magnus Jedvert6b9653e2017-06-05 17:58:34 +0200182- (void)applyShadingForFrameWithWidth:(int)width
183 height:(int)height
184 rotation:(RTCVideoRotation)rotation
185 yPlane:(GLuint)yPlane
186 uvPlane:(GLuint)uvPlane {
magjed13941912017-05-30 06:11:58 -0700187 if (![self prepareVertexBufferWithRotation:rotation]) {
188 return;
189 }
190
191 if (!_nv12Program && ![self createAndSetupNV12Program]) {
192 RTCLog(@"Failed to setup NV12 shader");
193 return;
194 }
195
196 glUseProgram(_nv12Program);
197
198 glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + kYTextureUnit));
199 glBindTexture(GL_TEXTURE_2D, yPlane);
200
201 glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + kUvTextureUnit));
202 glBindTexture(GL_TEXTURE_2D, uvPlane);
203
204 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
205}
206
207@end