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