Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(960)

Side by Side Diff: webrtc/sdk/objc/Framework/Classes/RTCI420Shader.mm

Issue 2202823004: iOS: Add support for rendering native CVPixelBuffers directly (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 #include <vector>
14
15 #import "RTCShader+Private.h"
16 #import "WebRTC/RTCVideoFrame.h"
17
18 // |kNumTextures| must not exceed 8, which is the limit in OpenGLES2. Two sets
19 // of 3 textures are used here, one for each of the Y, U and V planes. Having
20 // two sets alleviates CPU blockage in the event that the GPU is asked to render
21 // to a texture that is already in use.
22 static const GLsizei kNumTextureSets = 2;
23 static const GLsizei kNumTextures = 3 * kNumTextureSets;
sakal 2016/08/03 10:52:59 I would prefer 3 be a constant like kNumTexturesPe
tkchin_webrtc 2016/08/03 17:53:19 Done.
24
25 // Fragment shader converts YUV values from input textures into a final RGB
26 // pixel. The conversion formula is from http://www.fourcc.org/fccyvrgb.php.
27 static const char kI420FragmentShaderSource[] =
28 SHADER_VERSION
29 "precision highp float;"
30 FRAGMENT_SHADER_IN " vec2 v_texcoord;\n"
31 "uniform lowp sampler2D s_textureY;\n"
32 "uniform lowp sampler2D s_textureU;\n"
33 "uniform lowp sampler2D s_textureV;\n"
34 FRAGMENT_SHADER_OUT
35 "void main() {\n"
36 " float y, u, v, r, g, b;\n"
37 " y = " FRAGMENT_SHADER_TEXTURE "(s_textureY, v_texcoord).r;\n"
38 " u = " FRAGMENT_SHADER_TEXTURE "(s_textureU, v_texcoord).r;\n"
39 " v = " FRAGMENT_SHADER_TEXTURE "(s_textureV, v_texcoord).r;\n"
40 " u = u - 0.5;\n"
41 " v = v - 0.5;\n"
42 " r = y + 1.403 * v;\n"
43 " g = y - 0.344 * u - 0.714 * v;\n"
44 " b = y + 1.770 * u;\n"
45 " " FRAGMENT_SHADER_COLOR " = vec4(r, g, b, 1.0);\n"
46 " }\n";
47
48 @implementation RTCI420Shader {
49 BOOL _hasUnpackRowLength;
50 GLint _currentTextureSet;
51 // Handles for OpenGL constructs.
52 GLuint _textures[kNumTextures];
53 GLuint _i420Program;
54 GLuint _vertexArray;
55 GLuint _vertexBuffer;
56 GLint _ySampler;
57 GLint _uSampler;
58 GLint _vSampler;
59 // Used to create a non-padded plane for GPU upload when we receive padded
60 // frames.
61 std::vector<uint8_t> _planeBuffer;
62 }
63
64 - (instancetype)initWithContext:(GlContextType *)context {
65 if (self = [super init]) {
66 #if TARGET_OS_IPHONE
67 _hasUnpackRowLength = (context.API == kEAGLRenderingAPIOpenGLES3);
68 #else
69 _hasUnpackRowLength = YES;
70 #endif
71 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
72 if (![self setupI420Program] || ![self setupTextures] ||
73 !RTCSetupVerticesForProgram(_i420Program, &_vertexBuffer, &_vertexArray) ) {
74 self = nil;
75 }
76 }
77 return self;
78 }
79
80 - (void)dealloc {
81 if (_i420Program != 0) {
sakal 2016/08/03 10:52:59 These if statements are unnecessary. OpenGL silent
tkchin_webrtc 2016/08/03 17:53:19 Done.
82 glDeleteProgram(_i420Program);
83 }
84 if (_textures[0] != 0) {
85 glDeleteTextures(kNumTextures, _textures);
86 }
87 if (_vertexBuffer != 0) {
88 glDeleteBuffers(1, &_vertexBuffer);
89 }
90 if (_vertexArray != 0) {
91 glDeleteVertexArrays(1, &_vertexArray);
92 }
93 }
94
95 - (BOOL)setupI420Program {
96 _i420Program = RTCCreateProgramFromFragmentSource(kI420FragmentShaderSource);
97 if (!_i420Program) {
98 return NO;
99 }
100 _ySampler = glGetUniformLocation(_i420Program, "s_textureY");
101 _uSampler = glGetUniformLocation(_i420Program, "s_textureU");
102 _vSampler = glGetUniformLocation(_i420Program, "s_textureV");
103
104 return (_ySampler >= 0 && _uSampler >= 0 && _vSampler >= 0);
105 }
106
107 - (BOOL)setupTextures {
108 glGenTextures(kNumTextures, _textures);
109 // Set parameters for each of the textures we created.
110 for (GLsizei i = 0; i < kNumTextures; i++) {
111 glActiveTexture(GL_TEXTURE0 + i);
sakal 2016/08/03 10:52:59 I don't think this is needed. We can just set up t
tkchin_webrtc 2016/08/03 17:53:19 Done.
112 glBindTexture(GL_TEXTURE_2D, _textures[i]);
113 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
114 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
115 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
116 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
117 }
118 return YES;
119 }
120
121 - (BOOL)drawFrame:(RTCVideoFrame*)frame {
122 glUseProgram(_i420Program);
123 if (![self updateTextureDataForFrame:frame]) {
124 return NO;
125 }
126 #if !TARGET_OS_IPHONE
127 glBindVertexArray(_vertexArray);
128 #endif
129 glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
130 glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
131
132 return YES;
133 }
134
135 - (void)uploadPlane:(const uint8_t *)plane
136 sampler:(GLint)sampler
137 offset:(GLint)offset
138 width:(size_t)width
139 height:(size_t)height
140 stride:(int32_t)stride {
141 glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + offset));
sakal 2016/08/03 10:52:59 Is it not possible to just always use GL_TEXTURE0-
tkchin_webrtc 2016/08/03 17:53:19 See comment at top of file.
142 glBindTexture(GL_TEXTURE_2D, _textures[offset]);
143
144 // When setting texture sampler uniforms, the texture index is used not
145 // the texture handle.
146 glUniform1i(sampler, offset);
147 const uint8_t *uploadPlane = plane;
148 if ((size_t)stride != width) {
149 if (_hasUnpackRowLength) {
150 // GLES3 allows us to specify stride.
151 glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
152 glTexImage2D(GL_TEXTURE_2D,
153 0,
154 RTC_PIXEL_FORMAT,
155 static_cast<GLsizei>(width),
156 static_cast<GLsizei>(height),
157 0,
158 RTC_PIXEL_FORMAT,
159 GL_UNSIGNED_BYTE,
160 uploadPlane);
161 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
162 return;
163 } else {
164 // Make an unpadded copy and upload that instead. Quick profiling showed
165 // that this is faster than uploading row by row using glTexSubImage2D.
166 uint8_t *unpaddedPlane = _planeBuffer.data();
167 for (size_t y = 0; y < height; ++y) {
168 memcpy(unpaddedPlane + y * width, plane + y * stride, width);
169 }
170 uploadPlane = unpaddedPlane;
171 }
172 }
173 glTexImage2D(GL_TEXTURE_2D,
174 0,
175 RTC_PIXEL_FORMAT,
176 static_cast<GLsizei>(width),
177 static_cast<GLsizei>(height),
178 0,
179 RTC_PIXEL_FORMAT,
180 GL_UNSIGNED_BYTE,
181 uploadPlane);
182 }
183
184 - (BOOL)updateTextureDataForFrame:(RTCVideoFrame *)frame {
185 GLint textureOffset = _currentTextureSet * 3;
186 NSAssert(textureOffset + 3 <= kNumTextures, @"invalid offset");
187
188 if (frame.yPitch != static_cast<int32_t>(frame.width) ||
189 frame.uPitch != static_cast<int32_t>(frame.chromaWidth) ||
190 frame.vPitch != static_cast<int32_t>(frame.chromaWidth)) {
191 _planeBuffer.resize(frame.width * frame.height);
192 }
193
194 [self uploadPlane:frame.yPlane
195 sampler:_ySampler
196 offset:textureOffset
197 width:frame.width
198 height:frame.height
199 stride:frame.yPitch];
200
201 [self uploadPlane:frame.uPlane
202 sampler:_uSampler
203 offset:textureOffset + 1
204 width:frame.chromaWidth
205 height:frame.chromaHeight
206 stride:frame.uPitch];
207
208 [self uploadPlane:frame.vPlane
209 sampler:_vSampler
210 offset:textureOffset + 2
211 width:frame.chromaWidth
212 height:frame.chromaHeight
213 stride:frame.vPitch];
214
215 _currentTextureSet = (_currentTextureSet + 1) % kNumTextureSets;
216 return YES;
217 }
218
219 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698