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 |