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