magjed | 8245a85 | 2017-04-26 02:02:10 -0700 | [diff] [blame] | 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" |
Anders Carlsson | e5960ce | 2017-06-22 15:26:30 +0200 | [diff] [blame] | 12 | #import "WebRTC/RTCVideoFrameBuffer.h" |
magjed | 8245a85 | 2017-04-26 02:02:10 -0700 | [diff] [blame] | 13 | |
magjed | 1394191 | 2017-05-30 06:11:58 -0700 | [diff] [blame] | 14 | #if TARGET_OS_IPHONE |
| 15 | #import <OpenGLES/ES3/gl.h> |
| 16 | #else |
| 17 | #import <OpenGL/gl3.h> |
| 18 | #endif |
magjed | 8245a85 | 2017-04-26 02:02:10 -0700 | [diff] [blame] | 19 | |
| 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. |
| 25 | static const GLsizei kNumTextureSets = 2; |
| 26 | static const GLsizei kNumTexturesPerSet = 3; |
| 27 | static 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 Carlsson | e5960ce | 2017-06-22 15:26:30 +0200 | [diff] [blame] | 127 | 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); |
magjed | 8245a85 | 2017-04-26 02:02:10 -0700 | [diff] [blame] | 134 | } |
| 135 | |
Anders Carlsson | e5960ce | 2017-06-22 15:26:30 +0200 | [diff] [blame] | 136 | [self uploadPlane:buffer.dataY |
magjed | 8245a85 | 2017-04-26 02:02:10 -0700 | [diff] [blame] | 137 | texture:self.yTexture |
Anders Carlsson | e5960ce | 2017-06-22 15:26:30 +0200 | [diff] [blame] | 138 | width:buffer.width |
| 139 | height:buffer.height |
| 140 | stride:buffer.strideY]; |
magjed | 8245a85 | 2017-04-26 02:02:10 -0700 | [diff] [blame] | 141 | |
Anders Carlsson | e5960ce | 2017-06-22 15:26:30 +0200 | [diff] [blame] | 142 | [self uploadPlane:buffer.dataU |
magjed | 8245a85 | 2017-04-26 02:02:10 -0700 | [diff] [blame] | 143 | texture:self.uTexture |
| 144 | width:chromaWidth |
| 145 | height:chromaHeight |
Anders Carlsson | e5960ce | 2017-06-22 15:26:30 +0200 | [diff] [blame] | 146 | stride:buffer.strideU]; |
magjed | 8245a85 | 2017-04-26 02:02:10 -0700 | [diff] [blame] | 147 | |
Anders Carlsson | e5960ce | 2017-06-22 15:26:30 +0200 | [diff] [blame] | 148 | [self uploadPlane:buffer.dataV |
magjed | 8245a85 | 2017-04-26 02:02:10 -0700 | [diff] [blame] | 149 | texture:self.vTexture |
| 150 | width:chromaWidth |
| 151 | height:chromaHeight |
Anders Carlsson | e5960ce | 2017-06-22 15:26:30 +0200 | [diff] [blame] | 152 | stride:buffer.strideV]; |
magjed | 8245a85 | 2017-04-26 02:02:10 -0700 | [diff] [blame] | 153 | } |
| 154 | |
| 155 | @end |