Index: webrtc/sdk/objc/Framework/Classes/RTCI420Shader.mm |
diff --git a/webrtc/sdk/objc/Framework/Classes/RTCI420Shader.mm b/webrtc/sdk/objc/Framework/Classes/RTCI420Shader.mm |
index 3c3ffa72f003c5a9267584bed12cb85f718b34b8..83ecbe4d677009d0ee567562e6ee7fb9c0b1d804 100644 |
--- a/webrtc/sdk/objc/Framework/Classes/RTCI420Shader.mm |
+++ b/webrtc/sdk/objc/Framework/Classes/RTCI420Shader.mm |
@@ -10,22 +10,13 @@ |
#import "RTCShader.h" |
-#include <vector> |
- |
+#import "RTCI420TextureCache.h" |
#import "RTCShader+Private.h" |
#import "WebRTC/RTCLogging.h" |
#import "WebRTC/RTCVideoFrame.h" |
#include "webrtc/base/optional.h" |
-// |kNumTextures| must not exceed 8, which is the limit in OpenGLES2. Two sets |
-// of 3 textures are used here, one for each of the Y, U and V planes. Having |
-// two sets alleviates CPU blockage in the event that the GPU is asked to render |
-// to a texture that is already in use. |
-static const GLsizei kNumTextureSets = 2; |
-static const GLsizei kNumTexturesPerSet = 3; |
-static const GLsizei kNumTextures = kNumTexturesPerSet * kNumTextureSets; |
- |
// Fragment shader converts YUV values from input textures into a final RGB |
// pixel. The conversion formula is from http://www.fourcc.org/fccyvrgb.php. |
static const char kI420FragmentShaderSource[] = |
@@ -50,10 +41,8 @@ static const char kI420FragmentShaderSource[] = |
" }\n"; |
@implementation RTCI420Shader { |
- BOOL _hasUnpackRowLength; |
- GLint _currentTextureSet; |
+ RTCI420TextureCache* textureCache; |
// Handles for OpenGL constructs. |
- GLuint _textures[kNumTextures]; |
GLuint _i420Program; |
GLuint _vertexArray; |
GLuint _vertexBuffer; |
@@ -63,20 +52,13 @@ static const char kI420FragmentShaderSource[] = |
// Store current rotation and only upload new vertex data when rotation |
// changes. |
rtc::Optional<RTCVideoRotation> _currentRotation; |
- // Used to create a non-padded plane for GPU upload when we receive padded |
- // frames. |
- std::vector<uint8_t> _planeBuffer; |
} |
- (instancetype)initWithContext:(GlContextType *)context { |
if (self = [super init]) { |
-#if TARGET_OS_IPHONE |
- _hasUnpackRowLength = (context.API == kEAGLRenderingAPIOpenGLES3); |
-#else |
- _hasUnpackRowLength = YES; |
-#endif |
+ textureCache = [[RTCI420TextureCache alloc] initWithContext:context]; |
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); |
- if (![self setupI420Program] || ![self setupTextures] || |
+ if (![self setupI420Program] || |
!RTCSetupVerticesForProgram(_i420Program, &_vertexBuffer, &_vertexArray)) { |
RTCLog(@"Failed to initialize RTCI420Shader."); |
self = nil; |
@@ -87,7 +69,6 @@ static const char kI420FragmentShaderSource[] = |
- (void)dealloc { |
glDeleteProgram(_i420Program); |
- glDeleteTextures(kNumTextures, _textures); |
glDeleteBuffers(1, &_vertexBuffer); |
glDeleteVertexArrays(1, &_vertexArray); |
} |
@@ -104,27 +85,27 @@ static const char kI420FragmentShaderSource[] = |
return (_ySampler >= 0 && _uSampler >= 0 && _vSampler >= 0); |
} |
-- (BOOL)setupTextures { |
- glGenTextures(kNumTextures, _textures); |
- // Set parameters for each of the textures we created. |
- for (GLsizei i = 0; i < kNumTextures; i++) { |
- glBindTexture(GL_TEXTURE_2D, _textures[i]); |
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
- } |
- return YES; |
-} |
- |
- (BOOL)drawFrame:(RTCVideoFrame*)frame { |
glUseProgram(_i420Program); |
- if (![self updateTextureDataForFrame:frame]) { |
- return NO; |
- } |
+ |
+ [textureCache uploadFrameToTextures:frame]; |
+ |
#if !TARGET_OS_IPHONE |
glBindVertexArray(_vertexArray); |
#endif |
+ |
+ glActiveTexture(GL_TEXTURE0); |
+ glBindTexture(GL_TEXTURE_2D, textureCache.yTexture); |
+ glUniform1i(_ySampler, 0); |
+ |
+ glActiveTexture(GL_TEXTURE1); |
+ glBindTexture(GL_TEXTURE_2D, textureCache.uTexture); |
+ glUniform1i(_uSampler, 1); |
+ |
+ glActiveTexture(GL_TEXTURE2); |
+ glBindTexture(GL_TEXTURE_2D, textureCache.vTexture); |
+ glUniform1i(_vSampler, 2); |
+ |
glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); |
if (!_currentRotation || frame.rotation != *_currentRotation) { |
_currentRotation = rtc::Optional<RTCVideoRotation>(frame.rotation); |
@@ -135,90 +116,4 @@ static const char kI420FragmentShaderSource[] = |
return YES; |
} |
-- (void)uploadPlane:(const uint8_t *)plane |
- sampler:(GLint)sampler |
- offset:(GLint)offset |
- width:(size_t)width |
- height:(size_t)height |
- stride:(int32_t)stride { |
- glActiveTexture(static_cast<GLenum>(GL_TEXTURE0 + offset)); |
- glBindTexture(GL_TEXTURE_2D, _textures[offset]); |
- |
- // When setting texture sampler uniforms, the texture index is used not |
- // the texture handle. |
- glUniform1i(sampler, offset); |
- const uint8_t *uploadPlane = plane; |
- if ((size_t)stride != width) { |
- if (_hasUnpackRowLength) { |
- // GLES3 allows us to specify stride. |
- glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); |
- glTexImage2D(GL_TEXTURE_2D, |
- 0, |
- RTC_PIXEL_FORMAT, |
- static_cast<GLsizei>(width), |
- static_cast<GLsizei>(height), |
- 0, |
- RTC_PIXEL_FORMAT, |
- GL_UNSIGNED_BYTE, |
- uploadPlane); |
- glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); |
- return; |
- } else { |
- // Make an unpadded copy and upload that instead. Quick profiling showed |
- // that this is faster than uploading row by row using glTexSubImage2D. |
- uint8_t *unpaddedPlane = _planeBuffer.data(); |
- for (size_t y = 0; y < height; ++y) { |
- memcpy(unpaddedPlane + y * width, plane + y * stride, width); |
- } |
- uploadPlane = unpaddedPlane; |
- } |
- } |
- glTexImage2D(GL_TEXTURE_2D, |
- 0, |
- RTC_PIXEL_FORMAT, |
- static_cast<GLsizei>(width), |
- static_cast<GLsizei>(height), |
- 0, |
- RTC_PIXEL_FORMAT, |
- GL_UNSIGNED_BYTE, |
- uploadPlane); |
-} |
- |
-- (BOOL)updateTextureDataForFrame:(RTCVideoFrame *)frame { |
- GLint textureOffset = _currentTextureSet * 3; |
- NSAssert(textureOffset + 3 <= kNumTextures, @"invalid offset"); |
- |
- const int chromaWidth = (frame.width + 1) / 2; |
- const int chromaHeight = (frame.height + 1) / 2; |
- if (frame.strideY != frame.width || |
- frame.strideU != chromaWidth || |
- frame.strideV != chromaWidth) { |
- _planeBuffer.resize(frame.width * frame.height); |
- } |
- |
- [self uploadPlane:frame.dataY |
- sampler:_ySampler |
- offset:textureOffset |
- width:frame.width |
- height:frame.height |
- stride:frame.strideY]; |
- |
- [self uploadPlane:frame.dataU |
- sampler:_uSampler |
- offset:textureOffset + 1 |
- width:chromaWidth |
- height:chromaHeight |
- stride:frame.strideU]; |
- |
- [self uploadPlane:frame.dataV |
- sampler:_vSampler |
- offset:textureOffset + 2 |
- width:chromaWidth |
- height:chromaHeight |
- stride:frame.strideV]; |
- |
- _currentTextureSet = (_currentTextureSet + 1) % kNumTextureSets; |
- return YES; |
-} |
- |
@end |