1/* 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 "base/RTCLogging.h" 22 23#include "absl/types/optional.h" 24 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. 76 absl::optional<RTCVideoRotation> _currentRotation; 77 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) { 147 _currentRotation = absl::optional<RTCVideoRotation>(rotation); 148 RTCSetVertexData(*_currentRotation); 149 } 150 return YES; 151} 152 153- (void)applyShadingForFrameWithWidth:(int)width 154 height:(int)height 155 rotation:(RTCVideoRotation)rotation 156 yPlane:(GLuint)yPlane 157 uPlane:(GLuint)uPlane 158 vPlane:(GLuint)vPlane { 159 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 182- (void)applyShadingForFrameWithWidth:(int)width 183 height:(int)height 184 rotation:(RTCVideoRotation)rotation 185 yPlane:(GLuint)yPlane 186 uvPlane:(GLuint)uvPlane { 187 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 208