Chromium Code Reviews

Side by Side Diff: talk/app/webrtc/objc/RTCEAGLVideoView.m

Issue 1347013002: RTCEAGLVideoView: Fix GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT error. (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Add isDirty flag Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff |
« no previous file with comments | « AUTHORS ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 /* 1 /*
2 * libjingle 2 * libjingle
3 * Copyright 2014 Google Inc. 3 * Copyright 2014 Google Inc.
4 * 4 *
5 * Redistribution and use in source and binary forms, with or without 5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met: 6 * modification, are permitted provided that the following conditions are met:
7 * 7 *
8 * 1. Redistributions of source code must retain the above copyright notice, 8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer. 9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice, 10 * 2. Redistributions in binary form must reproduce the above copyright notice,
(...skipping 74 matching lines...)
85 - (void)invalidate { 85 - (void)invalidate {
86 [_displayLink invalidate]; 86 [_displayLink invalidate];
87 } 87 }
88 88
89 - (void)displayLinkDidFire:(CADisplayLink*)displayLink { 89 - (void)displayLinkDidFire:(CADisplayLink*)displayLink {
90 _timerHandler(); 90 _timerHandler();
91 } 91 }
92 92
93 @end 93 @end
94 94
95 // RTCEAGLVideoView wraps a GLKView which is setup with
96 // enableSetNeedsDisplay = NO for the purpose of gaining control of
97 // exactly when to call -[GLKView display]. This need for extra
98 // control is required to avoid triggering method calls on GLKView
99 // that results in attempting to bind the underlying render buffer
100 // when the drawable size would be empty which would result in the
101 // error GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT. -[GLKView display] is
102 // the method that will trigger the binding of the render
103 // buffer. Because the standard behaviour of -[UIView setNeedsDisplay]
104 // is disabled for the reasons above, the RTCEAGLVideoView maintains
105 // its own |isDirty| flag.
106
95 @interface RTCEAGLVideoView () <GLKViewDelegate> 107 @interface RTCEAGLVideoView () <GLKViewDelegate>
96 // |i420Frame| is set when we receive a frame from a worker thread and is read 108 // |i420Frame| is set when we receive a frame from a worker thread and is read
97 // from the display link callback so atomicity is required. 109 // from the display link callback so atomicity is required.
98 @property(atomic, strong) RTCI420Frame* i420Frame; 110 @property(atomic, strong) RTCI420Frame* i420Frame;
99 @property(nonatomic, readonly) GLKView* glkView; 111 @property(nonatomic, readonly) GLKView* glkView;
100 @property(nonatomic, readonly) RTCOpenGLVideoRenderer* glRenderer; 112 @property(nonatomic, readonly) RTCOpenGLVideoRenderer* glRenderer;
101 @end 113 @end
102 114
103 @implementation RTCEAGLVideoView { 115 @implementation RTCEAGLVideoView {
104 RTCDisplayLinkTimer* _timer; 116 RTCDisplayLinkTimer* _timer;
105 GLKView* _glkView; 117 GLKView* _glkView;
106 RTCOpenGLVideoRenderer* _glRenderer; 118 RTCOpenGLVideoRenderer* _glRenderer;
119 BOOL _isDirty; // this flag should only be set and read on the main thread (e. g. by
magjed_webrtc 2015/09/21 11:44:10 Put this comment above the variable instead. Also
120 // setNeedsDisplay)
107 } 121 }
108 122
109 - (instancetype)initWithFrame:(CGRect)frame { 123 - (instancetype)initWithFrame:(CGRect)frame {
110 if (self = [super initWithFrame:frame]) { 124 if (self = [super initWithFrame:frame]) {
111 [self configure]; 125 [self configure];
112 } 126 }
113 return self; 127 return self;
114 } 128 }
115 129
116 - (instancetype)initWithCoder:(NSCoder *)aDecoder { 130 - (instancetype)initWithCoder:(NSCoder *)aDecoder {
117 if (self = [super initWithCoder:aDecoder]) { 131 if (self = [super initWithCoder:aDecoder]) {
118 [self configure]; 132 [self configure];
119 } 133 }
120 return self; 134 return self;
121 } 135 }
122 136
123 - (void)configure { 137 - (void)configure {
124 EAGLContext* glContext = 138 EAGLContext* glContext =
125 [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; 139 [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
126 if (!glContext) { 140 if (!glContext) {
127 glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; 141 glContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
128 } 142 }
129 _glRenderer = [[RTCOpenGLVideoRenderer alloc] initWithContext:glContext]; 143 _glRenderer = [[RTCOpenGLVideoRenderer alloc] initWithContext:glContext];
130 144
131 // GLKView manages a framebuffer for us. 145 // GLKView manages a framebuffer for us.
132 _glkView = [[GLKView alloc] initWithFrame:CGRectZero 146 _glkView = [[GLKView alloc] initWithFrame:CGRectZero
magjed_webrtc 2015/09/21 11:44:10 Would it solve all problems to simply replace CGRe
133 context:glContext]; 147 context:glContext];
134 _glkView.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; 148 _glkView.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
135 _glkView.drawableDepthFormat = GLKViewDrawableDepthFormatNone; 149 _glkView.drawableDepthFormat = GLKViewDrawableDepthFormatNone;
136 _glkView.drawableStencilFormat = GLKViewDrawableStencilFormatNone; 150 _glkView.drawableStencilFormat = GLKViewDrawableStencilFormatNone;
137 _glkView.drawableMultisample = GLKViewDrawableMultisampleNone; 151 _glkView.drawableMultisample = GLKViewDrawableMultisampleNone;
138 _glkView.delegate = self; 152 _glkView.delegate = self;
139 _glkView.layer.masksToBounds = YES; 153 _glkView.layer.masksToBounds = YES;
154 _glkView.enableSetNeedsDisplay = NO;
140 [self addSubview:_glkView]; 155 [self addSubview:_glkView];
141 156
142 // Listen to application state in order to clean up OpenGL before app goes 157 // Listen to application state in order to clean up OpenGL before app goes
143 // away. 158 // away.
144 NSNotificationCenter* notificationCenter = 159 NSNotificationCenter* notificationCenter =
145 [NSNotificationCenter defaultCenter]; 160 [NSNotificationCenter defaultCenter];
146 [notificationCenter addObserver:self 161 [notificationCenter addObserver:self
147 selector:@selector(willResignActive) 162 selector:@selector(willResignActive)
148 name:UIApplicationWillResignActiveNotification 163 name:UIApplicationWillResignActiveNotification
149 object:nil]; 164 object:nil];
150 [notificationCenter addObserver:self 165 [notificationCenter addObserver:self
151 selector:@selector(didBecomeActive) 166 selector:@selector(didBecomeActive)
152 name:UIApplicationDidBecomeActiveNotification 167 name:UIApplicationDidBecomeActiveNotification
153 object:nil]; 168 object:nil];
154 169
155 // Frames are received on a separate thread, so we poll for current frame 170 // Frames are received on a separate thread, so we poll for current frame
156 // using a refresh rate proportional to screen refresh frequency. This 171 // using a refresh rate proportional to screen refresh frequency. This
157 // occurs on the main thread. 172 // occurs on the main thread.
158 __weak RTCEAGLVideoView* weakSelf = self; 173 __weak RTCEAGLVideoView* weakSelf = self;
159 _timer = [[RTCDisplayLinkTimer alloc] initWithTimerHandler:^{ 174 _timer = [[RTCDisplayLinkTimer alloc] initWithTimerHandler:^{
160 RTCEAGLVideoView* strongSelf = weakSelf; 175 RTCEAGLVideoView* strongSelf = weakSelf;
161 // Don't render if frame hasn't changed. 176 [strongSelf displayLinkTimerDidFire];
magjed_webrtc 2015/09/21 11:44:10 What if you keep this code unchanged, remove the v
162 if (strongSelf.glRenderer.lastDrawnFrame == strongSelf.i420Frame) {
163 return;
164 }
165 // This tells the GLKView that it's dirty, which will then call the
166 // GLKViewDelegate method implemented below.
167 [strongSelf.glkView setNeedsDisplay];
168 }]; 177 }];
169 [self setupGL]; 178 [self setupGL];
170 } 179 }
171 180
172 - (void)dealloc { 181 - (void)dealloc {
173 [[NSNotificationCenter defaultCenter] removeObserver:self]; 182 [[NSNotificationCenter defaultCenter] removeObserver:self];
174 UIApplicationState appState = 183 UIApplicationState appState =
175 [UIApplication sharedApplication].applicationState; 184 [UIApplication sharedApplication].applicationState;
176 if (appState == UIApplicationStateActive) { 185 if (appState == UIApplicationStateActive) {
177 [self teardownGL]; 186 [self teardownGL];
178 } 187 }
179 [_timer invalidate]; 188 [_timer invalidate];
180 } 189 }
181 190
182 #pragma mark - UIView 191 #pragma mark - UIView
183 192
193 - (void)setNeedsDisplay {
194 [super setNeedsDisplay];
195 _isDirty = YES;
magjed_webrtc 2015/09/21 11:44:10 You still need to execute the code in displayLinkT
196 }
197
198 - (void)setNeedsDisplayInRect:(CGRect)rect {
199 [super setNeedsDisplayInRect:rect];
200 _isDirty = YES;
201 }
202
184 - (void)layoutSubviews { 203 - (void)layoutSubviews {
185 [super layoutSubviews]; 204 [super layoutSubviews];
186 _glkView.frame = self.bounds; 205 _glkView.frame = self.bounds;
187 } 206 }
188 207
189 #pragma mark - GLKViewDelegate 208 #pragma mark - GLKViewDelegate
190 209
191 // This method is called when the GLKView's content is dirty and needs to be 210 // This method is called when the GLKView's content is dirty and needs to be
192 // redrawn. This occurs on main thread. 211 // redrawn. This occurs on main thread.
193 - (void)glkView:(GLKView*)view drawInRect:(CGRect)rect { 212 - (void)glkView:(GLKView*)view drawInRect:(CGRect)rect {
(...skipping 12 matching lines...)
206 [strongSelf.delegate videoView:strongSelf didChangeVideoSize:size]; 225 [strongSelf.delegate videoView:strongSelf didChangeVideoSize:size];
207 }); 226 });
208 } 227 }
209 228
210 - (void)renderFrame:(RTCI420Frame*)frame { 229 - (void)renderFrame:(RTCI420Frame*)frame {
211 self.i420Frame = frame; 230 self.i420Frame = frame;
212 } 231 }
213 232
214 #pragma mark - Private 233 #pragma mark - Private
215 234
235 - (void)displayLinkTimerDidFire {
236 // Don't render unless video frame have changed or the view content
237 // has explicitly been marked dirty.
238 if (!_isDirty && _glRenderer.lastDrawnFrame == self.i420Frame) {
239 return;
240 }
241
242 // Always reset isDirty at this point, even if -[GLKView display]
243 // won't be called in the case the drawable size is empty.
244 _isDirty = NO;
245
246 // Only call -[GLKView display] if the drawable size is
247 // non-empty. Calling display will make the GLKView setup its
248 // render buffer if necessary, but that will fail with error
249 // GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT if size is empty.
250 if (self.bounds.size.width > 0 && self.bounds.size.height > 0) {
251 [_glkView display];
magjed_webrtc 2015/09/21 11:44:10 Is there any way to override the display call and
252 }
253 }
254
216 - (void)setupGL { 255 - (void)setupGL {
217 self.i420Frame = nil; 256 self.i420Frame = nil;
218 [_glRenderer setupGL]; 257 [_glRenderer setupGL];
219 _timer.isPaused = NO; 258 _timer.isPaused = NO;
220 } 259 }
221 260
222 - (void)teardownGL { 261 - (void)teardownGL {
223 self.i420Frame = nil; 262 self.i420Frame = nil;
224 _timer.isPaused = YES; 263 _timer.isPaused = YES;
225 [_glkView deleteDrawable]; 264 [_glkView deleteDrawable];
226 [_glRenderer teardownGL]; 265 [_glRenderer teardownGL];
227 } 266 }
228 267
229 - (void)didBecomeActive { 268 - (void)didBecomeActive {
230 [self setupGL]; 269 [self setupGL];
231 } 270 }
232 271
233 - (void)willResignActive { 272 - (void)willResignActive {
234 [self teardownGL]; 273 [self teardownGL];
235 } 274 }
236 275
237 @end 276 @end
OLDNEW
« no previous file with comments | « AUTHORS ('k') | no next file » | no next file with comments »

Powered by Google App Engine