blob: d263c75e939713e0ffbd41d1924fbf6087a18976 [file] [log] [blame]
magjed8245a852017-04-26 02:02:10 -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 "RTCI420TextureCache.h"
12
13#import "RTCShader+Private.h"
14
15#include <vector>
16
17// Two sets of 3 textures are used here, one for each of the Y, U and V planes. Having two sets
18// alleviates CPU blockage in the event that the GPU is asked to render to a texture that is already
19// in use.
20static const GLsizei kNumTextureSets = 2;
21static const GLsizei kNumTexturesPerSet = 3;
22static const GLsizei kNumTextures = kNumTexturesPerSet * kNumTextureSets;
23
24@implementation RTCI420TextureCache {
25 BOOL _hasUnpackRowLength;
26 GLint _currentTextureSet;
27 // Handles for OpenGL constructs.
28 GLuint _textures[kNumTextures];
29 // Used to create a non-padded plane for GPU upload when we receive padded frames.
30 std::vector<uint8_t> _planeBuffer;
31}
32
33- (GLuint)yTexture {
34 return _textures[_currentTextureSet * kNumTexturesPerSet];
35}
36
37- (GLuint)uTexture {
38 return _textures[_currentTextureSet * kNumTexturesPerSet + 1];
39}
40
41- (GLuint)vTexture {
42 return _textures[_currentTextureSet * kNumTexturesPerSet + 2];
43}
44
45- (instancetype)initWithContext:(GlContextType *)context {
46 if (self = [super init]) {
47#if TARGET_OS_IPHONE
48 _hasUnpackRowLength = (context.API == kEAGLRenderingAPIOpenGLES3);
49#else
50 _hasUnpackRowLength = YES;
51#endif
52 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
53
54 [self setupTextures];
55 }
56 return self;
57}
58
59- (void)dealloc {
60 glDeleteTextures(kNumTextures, _textures);
61}
62
63- (void)setupTextures {
64 glGenTextures(kNumTextures, _textures);
65 // Set parameters for each of the textures we created.
66 for (GLsizei i = 0; i < kNumTextures; i++) {
67 glBindTexture(GL_TEXTURE_2D, _textures[i]);
68 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
69 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
70 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
71 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
72 }
73}
74
75- (void)uploadPlane:(const uint8_t *)plane
76 texture:(GLuint)texture
77 width:(size_t)width
78 height:(size_t)height
79 stride:(int32_t)stride {
80 glBindTexture(GL_TEXTURE_2D, texture);
81
82 const uint8_t *uploadPlane = plane;
83 if ((size_t)stride != width) {
84 if (_hasUnpackRowLength) {
85 // GLES3 allows us to specify stride.
86 glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
87 glTexImage2D(GL_TEXTURE_2D,
88 0,
89 RTC_PIXEL_FORMAT,
90 static_cast<GLsizei>(width),
91 static_cast<GLsizei>(height),
92 0,
93 RTC_PIXEL_FORMAT,
94 GL_UNSIGNED_BYTE,
95 uploadPlane);
96 glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
97 return;
98 } else {
99 // Make an unpadded copy and upload that instead. Quick profiling showed
100 // that this is faster than uploading row by row using glTexSubImage2D.
101 uint8_t *unpaddedPlane = _planeBuffer.data();
102 for (size_t y = 0; y < height; ++y) {
103 memcpy(unpaddedPlane + y * width, plane + y * stride, width);
104 }
105 uploadPlane = unpaddedPlane;
106 }
107 }
108 glTexImage2D(GL_TEXTURE_2D,
109 0,
110 RTC_PIXEL_FORMAT,
111 static_cast<GLsizei>(width),
112 static_cast<GLsizei>(height),
113 0,
114 RTC_PIXEL_FORMAT,
115 GL_UNSIGNED_BYTE,
116 uploadPlane);
117}
118
119- (void)uploadFrameToTextures:(RTCVideoFrame *)frame {
120 _currentTextureSet = (_currentTextureSet + 1) % kNumTextureSets;
121
122 const int chromaWidth = (frame.width + 1) / 2;
123 const int chromaHeight = (frame.height + 1) / 2;
124 if (frame.strideY != frame.width ||
125 frame.strideU != chromaWidth ||
126 frame.strideV != chromaWidth) {
127 _planeBuffer.resize(frame.width * frame.height);
128 }
129
130 [self uploadPlane:frame.dataY
131 texture:self.yTexture
132 width:frame.width
133 height:frame.height
134 stride:frame.strideY];
135
136 [self uploadPlane:frame.dataU
137 texture:self.uTexture
138 width:chromaWidth
139 height:chromaHeight
140 stride:frame.strideU];
141
142 [self uploadPlane:frame.dataV
143 texture:self.vTexture
144 width:chromaWidth
145 height:chromaHeight
146 stride:frame.strideV];
147}
148
149@end