Frames getting lost using InvalidatingCoreAnimation with CAOpenGLLayer from NPPlugin
Reported by
duncante...@turbulenz.biz,
Oct 31 2011
|
||||||||
Issue descriptionChrome Version : 15.0.874.106 (Official Build 107270) OS Version: OS X 10.6 URLs (if applicable) : Other browsers tested: Add OK or FAIL after other browsers where you have tested this issue: Safari 5: OK Firefox 7: OK (better) We are developing an NPAPI plugin that, under Chrome and Firefox7 uses the InvalidatingCoreAnimation model with a CAOpenGLLayer to render using OpenGL. Within CFTimer callbacks we render to a framebuffer, call NPN_InvalidateRect, and then blit the framewbuffer to the CAOpenGLLayer during the CAOpenGLLayer::drawInContext() callback. The trouble we are seeing is that it is very easy for frames to get 'lost' when the GPU or CPU is under high load - that is, over several frames the data blitted into the CALayer is not transferred to the browser window and the animation appears to pause and then skip ahead several frames. By making invalidate calls at a lower frequency we can avoid frameskipping, but it is extremely difficult to determine the optimal frequency at runtime. . We cannot measure the full time taken to render a frame with any accuracy because most frames just queue a set of GPU commands up, and so to the plugin code they appear to finish very quickly. We don't need a precise frequency - just how many 1/60 second intervals we should wait between frames, but I haven't found a way of measuring the true cost of a single frame even to this granularity. To accurately judge the correct rate at which to call NPN_InvalidateRect and make sure that frames don't get missed we would ideally need to know when a given frame has been blitted into the browsers window, or when that process has finished with the data. However, I cannot find a way to determine this. Currently, we are forced to call glFinish() at the end of each frame - this seems to give the system enough time to make most frames visible in the browser, and avoid any noticeable frameskip. Of course this has a bad impact on performance. I assume this problem is related to the fact that pixel data must be made available across multiple processes, incurring some kind of latency between a frame being rendered into the CALayer and that data being consumed by the process that renders the browser window. Under Safari we use CoreAnimation and set the asynchronous flag on the CALayer, driving our frame rate from the CALayer::canDraw callbacks. This seems to eliminate any skipping in Safari without the need to call glFinish(). Safari must also be using something like IOSurfaces to make pixel data available across processes, so I'm guessing similar performance must be achievable in Chrome. We have tried using many different ways of controlling the frequency of invalidate calls, including regular CFRunLoopTimers, CFRunLoop idle observers, measuring the CPU cost over a series of frames, etc but with no success. The NPN_InvalidateRect calls are enclosed in "[CATransaction begin];" and "[CATransaction commit];" to make sure that CAOpenGLLayer::drawInContext() happens synchronously, but this also has no noticeable effect. Is there some way of determining a reasonably optimal frequency of calls to NPN_InvalidateRect such that frames don't get lost? Or something else I might have missed that can help solve this problem? Many thanks in advance.
,
Nov 7 2011
> Safari must also be using something like IOSurfaces to make pixel data available across > processes, so I'm guessing similar performance must be achievable in Chrome. Safari uses private SPI to share surfaces, which has different behaviors than IOSurface, so things that Safari can do aren't necessarily feasible in other browsers (which is why Invalidate Core Animation exists in the first place). kbr, thakis, any thoughts? How is the GPU process handling this?
,
Nov 7 2011
(I think Safari uses CARemoteLayerClient / CARemoteLayerServer which as of 10.7 is no longer SPI. But we can't use it because we don't use CA in the renderer, and that's because our sandbox is more strict than Safari's amongst other reasons.)
,
Nov 7 2011
Neither end of NPAPI drawing is in the renderer, so if this exists (undocumented) in 10.6 we could presumably use it. But did it really, or is that a 10.7 formalization/replacement of whatever internal thing they used in 10.6?
,
Nov 9 2011
Currently, theoretically, if we could share a Core Animation layer tree across processes, then we could use that mechanism to render CA plugins. The plugin vends a CA layer to the plugin process, and the plugin process would share that layer with the browser process for display onscreen. Currently Chromium uses a CARenderer in the plugin process to render the plugin's output into an IOSurface, and that IOSurface is drawn to the screen using OpenGL in the browser process. However, this is all changing in Issue 38967 , where CA plugins will be drawn to the screen using the compositor. Given that this will be a major change, further investigation into this issue (or any advice) should be deferred until those changes are in. Under the new code path there would not be much point in sharing the layer tree across processes. Chromium's compositor implementation, unlike WebKit's, does not use Core Animation, so it could not import the plugin's layer into its layer tree. To determine how quickly to render, pure HTML5 web applications use requestAnimationFrame. Perhaps it would be possible to drive your plugin's animation loop using that plus scriptability of your plugin. Note that there are reports that http://box2dflash.sourceforge.net/ drops frames while the plugin thinks it is rendering at 30-60 FPS. This may be the same underlying issue. I'm hoping the behavior improves with the new rendering path for CA plugins.
,
Nov 9 2011
The box2dflash problem sounds exactly like what we are seeing. I'll check out the use of requestAnimationFrame and wait for the compositor. Thanks for the information.
,
Nov 24 2011
Just wanted to update after trying the requestAnimationFrame suggestion, in case other people are in a similar situation. Using requestAnimationFrame to drive the framerate allows events from the browser to arrive at the plugin much quicker (previously when the system was under load we would sometimes see a perceptible lag between keys being pressed and events reaching the plugin), however it only has a marginal effect on the issue of frames being dropped. (Not sure if it's of any help but there was a more noticeable improvement under Firefox 8, both in terms of frames being skipped and events reaching the plugin.)
,
Nov 30 2011
The fix for Issue 38967 (rendering Core Animation plugins through the compositor) has landed. Unfortunately it doesn't appear that it has magically solved the problem. I think that the behavior with box2dflash looks a little better, but the improvement, if any, is definitely incremental. It's certainly technically possible for the entire browser window to update at a solid 60 FPS, so there must be some solution. Perhaps we need frame rate throttling between the plugin and GPU process, to avoid overloading the GPU with more frames than can be rendered.
,
Mar 10 2013
,
Apr 6 2013
,
Apr 6 2013
,
Jun 5 2015
Closing due to NPAPI deprecation.
,
Jun 5 2015
|
||||||||
►
Sign in to add a comment |
||||||||
Comment 1 by rsesek@chromium.org
, Oct 31 2011Labels: -Area-Undefined Area-Internals Feature-Plugins