OLD | NEW |
| (Empty) |
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 "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 tex
ture that is already | |
19 // in use. | |
20 static const GLsizei kNumTextureSets = 2; | |
21 static const GLsizei kNumTexturesPerSet = 3; | |
22 static 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 fra
mes. | |
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 | |
OLD | NEW |