Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(8100)

Unified Diff: chrome/browser/renderer_host/render_widget_host_view_mac.mm

Issue 3010054: Mac: Well-behaved accelerated plugins, preparation (Closed)
Patch Set: '' Created 10 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/renderer_host/render_widget_host_view_mac.mm
diff --git a/chrome/browser/renderer_host/render_widget_host_view_mac.mm b/chrome/browser/renderer_host/render_widget_host_view_mac.mm
index 647eba676c6e772100cb3936adf101a5cc3d581a..e4ad82cd310ceb7b702df2044033bc33e8aa63d1 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_mac.mm
+++ b/chrome/browser/renderer_host/render_widget_host_view_mac.mm
@@ -11,6 +11,7 @@
#include "base/command_line.h"
#include "base/histogram.h"
#include "base/logging.h"
+#import "base/scoped_nsautorelease_pool.h"
#import "base/scoped_nsobject.h"
#include "base/string_util.h"
#include "base/sys_string_conversions.h"
@@ -127,51 +128,152 @@ void DisablePasswordInput() {
} // namespace
-// AcceleratedPluginLayer ------------------------------------------------------
+// AcceleratedPluginView ------------------------------------------------------
-// This subclass of CAOpenGLLayer hosts the output of accelerated plugins on
+// This subclass of NSView hosts the output of accelerated plugins on
// the page.
-@interface AcceleratedPluginLayer : CAOpenGLLayer {
+@interface AcceleratedPluginView : NSView {
+ scoped_nsobject<NSOpenGLPixelFormat> glPixelFormat_;
+ CGLPixelFormatObj cglPixelFormat_; // weak, backed by |glPixelFormat_|.
+ scoped_nsobject<NSOpenGLContext> glContext_;
+ CGLContextObj cglContext_; // weak, backed by |glContext_|.
+
+ CVDisplayLinkRef displayLink_; // Owned by us.
+
RenderWidgetHostViewMac* renderWidgetHostView_; // weak
+ gfx::PluginWindowHandle pluginHandle_; // weak
+
+ // True if the backing IO surface was updated since we last painted.
+ BOOL surfaceWasSwapped_;
}
-- (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r;
+- (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r
+ pluginHandle:(gfx::PluginWindowHandle)pluginHandle;
+- (void)drawView;
+
+// This _must_ be atomic, since it's accessed from several threads.
+@property BOOL surfaceWasSwapped;
@end
-@implementation AcceleratedPluginLayer
-- (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r {
- self = [super init];
- if (self != nil) {
+@implementation AcceleratedPluginView : NSView
+@synthesize surfaceWasSwapped = surfaceWasSwapped_;
+
+- (CVReturn)getFrameForTime:(const CVTimeStamp*)outputTime {
+ // There is no autorelease pool when this method is called because it will be
+ // called from a background thread.
+ base::ScopedNSAutoreleasePool pool;
+
+ if (![self surfaceWasSwapped])
+ return kCVReturnSuccess;
+
+ [self drawView];
+ [self setSurfaceWasSwapped:NO];
+ return kCVReturnSuccess;
+}
+
+// This is the renderer output callback function
+static CVReturn MyDisplayLinkCallback(
stuartmorgan 2010/08/11 16:40:44 Can we give this a better name please?
Ken Russell (switch to Gerrit) 2010/08/14 02:12:11 Will change to "DrawOneAcceleratedPluginCallback".
+ CVDisplayLinkRef displayLink,
+ const CVTimeStamp* now,
+ const CVTimeStamp* outputTime,
+ CVOptionFlags flagsIn,
+ CVOptionFlags* flagsOut,
+ void* displayLinkContext) {
+ CVReturn result =
+ [(AcceleratedPluginView*)displayLinkContext getFrameForTime:outputTime];
+ return result;
+}
+
+- (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r
+ pluginHandle:(gfx::PluginWindowHandle)pluginHandle {
+ if ((self = [super initWithFrame:NSZeroRect])) {
renderWidgetHostView_ = r;
+ pluginHandle_ = pluginHandle;
+
+ [self setAutoresizingMask:NSViewMaxXMargin|NSViewMinYMargin];
+
+ NSOpenGLPixelFormatAttribute attributes[] =
+ { NSOpenGLPFAAccelerated, NSOpenGLPFADoubleBuffer, 0};
+
+ glPixelFormat_.reset([[NSOpenGLPixelFormat alloc]
+ initWithAttributes:attributes]);
+ glContext_.reset([[NSOpenGLContext alloc] initWithFormat:glPixelFormat_
+ shareContext:nil]);
+
+ cglContext_ = (CGLContextObj)[glContext_ CGLContextObj];
+ cglPixelFormat_ = (CGLPixelFormatObj)[glPixelFormat_ CGLPixelFormatObj];
+
+ // Draw at beam vsync.
+ GLint swapInterval = 1;
+ [glContext_ setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval];
+
+
+ // Set up a display link to do OpenGL rendering on a background thread.
+ CVDisplayLinkCreateWithActiveCGDisplays(&displayLink_);
+ CVDisplayLinkSetOutputCallback(displayLink_, &MyDisplayLinkCallback, self);
+ CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext(
+ displayLink_, cglContext_, cglPixelFormat_);
+ CVDisplayLinkStart(displayLink_);
}
return self;
}
-- (void)drawInCGLContext:(CGLContextObj)glContext
- pixelFormat:(CGLPixelFormatObj)pixelFormat
- forLayerTime:(CFTimeInterval)timeInterval
- displayTime:(const CVTimeStamp *)timeStamp {
- renderWidgetHostView_->DrawAcceleratedSurfaceInstances(glContext);
- [super drawInCGLContext:glContext
- pixelFormat:pixelFormat
- forLayerTime:timeInterval
- displayTime:timeStamp];
-}
-
-- (void)setFrame:(CGRect)rect {
- // The frame we get when the superlayer resizes doesn't make sense, so ignore
- // it and just match the superlayer's size. See the email thread referenced in
- // ensureAcceleratedPluginLayer for an explanation of why the superlayer
- // isn't trustworthy.
- [CATransaction begin];
- [CATransaction setValue:[NSNumber numberWithInt:0]
- forKey:kCATransactionAnimationDuration];
- if ([self superlayer])
- [super setFrame:[[self superlayer] bounds]];
- else
- [super setFrame:rect];
- [CATransaction commit];
+- (void)drawView {
+ // Called on a background thread. Synchronized via the CGL context lock.
+ CGLLockContext(cglContext_);
+
+ renderWidgetHostView_->DrawAcceleratedSurfaceInstance(
+ cglContext_, pluginHandle_);
+
+ CGLFlushDrawable(cglContext_);
+ CGLUnlockContext(cglContext_);
+}
+
+- (void)drawRect:(NSRect)rect {
+ [self drawView];
+}
+
+- (void)globalFrameDidChange:(NSNotification*)notification {
+ CGLLockContext(cglContext_);
+ [glContext_ update];
+
+ // You would think that -update updates the viewport. You would be wrong.
+ CGLSetCurrentContext(cglContext_);
+ NSSize size = [self frame].size;
+ glViewport(0, 0, size.width, size.height);
+
+ CGLUnlockContext(cglContext_);
+}
+
+- (void)renewGState {
+ // Synchronize with window server to avoid flashes or corrupt drawing.
+ [[self window] disableScreenUpdatesUntilFlush];
+ [self globalFrameDidChange:nil];
+ [super renewGState];
+}
+
+- (void)lockFocus {
+ [super lockFocus];
+
+ // If we're using OpenGL, make sure it is connected and that the display link
+ // is running.
+ if ([glContext_ view] != self) {
+ [glContext_ setView:self];
+
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(globalFrameDidChange:)
+ name:NSViewGlobalFrameDidChangeNotification
+ object:self];
+ }
+ [glContext_ makeCurrentContext];
+}
+
+- (void)dealloc {
+ CVDisplayLinkRelease(displayLink_);
+ [[NSNotificationCenter defaultCenter] removeObserver:self];
+ [super dealloc];
}
@end
@@ -316,12 +418,27 @@ void RenderWidgetHostViewMac::MovePluginWindows(
webkit_glue::WebPluginGeometry geom = *iter;
// Ignore bogus moves which claim to move the plugin to (0, 0)
// with width and height (0, 0)
- if (geom.window_rect.x() != 0 ||
- geom.window_rect.y() != 0 ||
- geom.window_rect.width() != 0 ||
- geom.window_rect.height() != 0) {
- plugin_container_manager_.MovePluginContainer(geom);
+ if (geom.window_rect.x() == 0 &&
+ geom.window_rect.y() == 0 &&
+ geom.window_rect.IsEmpty()) {
+ continue;
+ }
+
+ gfx::Rect rect = geom.window_rect;
+ if (geom.visible) {
+ rect.set_x(rect.x() + geom.clip_rect.x());
+ rect.set_y(rect.y() + geom.clip_rect.y());
+ rect.set_width(geom.clip_rect.width());
+ rect.set_height(geom.clip_rect.height());
}
+
+ PluginViewMap::iterator it = plugin_views_.find(geom.window);
+ CHECK(plugin_views_.end() != it);
stuartmorgan 2010/08/11 16:40:44 CHECK? Really? Crashing end-users doesn't seem lik
Ken Russell (switch to Gerrit) 2010/08/14 02:12:11 Will change these to DCHECKs and add if tests for
+ NSRect new_rect([cocoa_view_ RectToNSRect:rect]);
+ [it->second setFrame:new_rect];
+ [it->second setNeedsDisplay:YES];
+
+ plugin_container_manager_.MovePluginContainer(geom);
}
}
}
@@ -470,8 +587,12 @@ void RenderWidgetHostViewMac::Destroy() {
if (!is_popup_menu_) {
// Depth-first destroy all popups. Use ShutdownHost() to enforce
// deepest-first ordering.
- for (RenderWidgetHostViewCocoa* subview in [cocoa_view_ subviews]) {
- [subview renderWidgetHostViewMac]->ShutdownHost();
+ for (NSView* subview in [cocoa_view_ subviews]) {
+ if (![subview isKindOfClass:[RenderWidgetHostViewCocoa class]])
+ continue; // Skip plugin views.
stuartmorgan 2010/08/11 16:40:44 s/plugin/accelerated/
Ken Russell (switch to Gerrit) 2010/08/14 02:12:11 Will fix.
+
+ [static_cast<RenderWidgetHostViewCocoa*>(subview)
+ renderWidgetHostViewMac]->ShutdownHost();
}
// We've been told to destroy.
@@ -615,14 +736,28 @@ void RenderWidgetHostViewMac::KillSelf() {
gfx::PluginWindowHandle
RenderWidgetHostViewMac::AllocateFakePluginWindowHandle(bool opaque,
bool root) {
- // Make sure we have a layer for the plugin to draw into.
- [cocoa_view_ ensureAcceleratedPluginLayer];
+ // Create an NSView to host the plugin's/compositor's pixels.
+ gfx::PluginWindowHandle handle =
+ plugin_container_manager_.AllocateFakePluginWindowHandle(opaque, root);
+
+ scoped_nsobject<NSView> plugin_view(
+ [[AcceleratedPluginView alloc] initWithRenderWidgetHostViewMac:this
+ pluginHandle:handle]);
+ [plugin_view setHidden:YES];
+
+ [cocoa_view_ addSubview:plugin_view];
+ plugin_views_[handle] = plugin_view;
- return plugin_container_manager_.AllocateFakePluginWindowHandle(opaque, root);
+ return handle;
}
void RenderWidgetHostViewMac::DestroyFakePluginWindowHandle(
gfx::PluginWindowHandle window) {
+ PluginViewMap::iterator it = plugin_views_.find(window);
+ CHECK(plugin_views_.end() != it);
+ [it->second removeFromSuperview];
+ plugin_views_.erase(it);
+
plugin_container_manager_.DestroyFakePluginWindowHandle(window);
}
@@ -631,10 +766,26 @@ void RenderWidgetHostViewMac::AcceleratedSurfaceSetIOSurface(
int32 width,
int32 height,
uint64 io_surface_identifier) {
+ PluginViewMap::iterator it = plugin_views_.find(window);
+ CHECK(plugin_views_.end() != it);
+
plugin_container_manager_.SetSizeAndIOSurface(window,
width,
height,
io_surface_identifier);
+
+ if (plugin_container_manager_.IsRootContainer(window)) {
+ // Fake up a WebPluginGeometry for the root window to set the
+ // container's size; we will never get a notification from the
+ // browser about the root window, only plugins.
+ webkit_glue::WebPluginGeometry geom;
+ gfx::Rect rect(0, 0, width, height);
+ geom.window = window;
+ geom.window_rect = rect;
+ geom.clip_rect = rect;
+ geom.visible = true;
+ MovePluginWindows(std::vector<webkit_glue::WebPluginGeometry>(1, geom));
+ }
}
void RenderWidgetHostViewMac::AcceleratedSurfaceSetTransportDIB(
@@ -642,6 +793,9 @@ void RenderWidgetHostViewMac::AcceleratedSurfaceSetTransportDIB(
int32 width,
int32 height,
TransportDIB::Handle transport_dib) {
+ PluginViewMap::iterator it = plugin_views_.find(window);
+ CHECK(plugin_views_.end() != it);
stuartmorgan 2010/08/11 16:40:44 Again, why CHECK?
Ken Russell (switch to Gerrit) 2010/08/14 02:12:11 Will change.
+
plugin_container_manager_.SetSizeAndTransportDIB(window,
width,
height,
@@ -650,32 +804,38 @@ void RenderWidgetHostViewMac::AcceleratedSurfaceSetTransportDIB(
void RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped(
gfx::PluginWindowHandle window) {
- [cocoa_view_ drawAcceleratedPluginLayer];
- if (GetRenderWidgetHost()->is_gpu_rendering_active()) {
- // Additionally dirty the entire region of the view to make AppKit
- // and Core Animation think that our CALayer needs to repaint
- // itself.
- [cocoa_view_ setNeedsDisplayInRect:[cocoa_view_ frame]];
- }
+ PluginViewMap::iterator it = plugin_views_.find(window);
+ CHECK(plugin_views_.end() != it);
stuartmorgan 2010/08/11 16:40:44 And here, and below, and in DrawAcceleratedSurface
Ken Russell (switch to Gerrit) 2010/08/14 02:12:11 Will change.
+ CHECK([it->second isKindOfClass:[AcceleratedPluginView class]]);
+
+ AcceleratedPluginView* view = static_cast<AcceleratedPluginView*>(it->second);
+ [view setHidden:NO];
+ [view setSurfaceWasSwapped:YES];
}
-void RenderWidgetHostViewMac::DrawAcceleratedSurfaceInstances(
- CGLContextObj context) {
+void RenderWidgetHostViewMac::DrawAcceleratedSurfaceInstance(
+ CGLContextObj context, gfx::PluginWindowHandle plugin_handle) {
+ // Called on the display link thread.
+ PluginViewMap::iterator it = plugin_views_.find(plugin_handle);
+ CHECK(plugin_views_.end() != it);
+
CGLSetCurrentContext(context);
- gfx::Rect rect = GetWindowRect();
+ // TODO(thakis): Pixel or view coordinates?
+ NSSize size = [it->second frame].size;
+
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Note that we place the origin at the upper left corner with +y
// going down
- glOrtho(0, rect.width(), rect.height(), 0, -1, 1);
+ glOrtho(0, size.width, size.height, 0, -1, 1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
plugin_container_manager_.Draw(
- context, GetRenderWidgetHost()->is_gpu_rendering_active());
+ context, plugin_handle, GetRenderWidgetHost()->is_gpu_rendering_active());
}
-void RenderWidgetHostViewMac::AcceleratedSurfaceContextChanged() {
+void RenderWidgetHostViewMac::ForceTextureReload() {
plugin_container_manager_.ForceTextureReload();
}
@@ -925,7 +1085,7 @@ void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
[self keyEvent:theEvent wasKeyEquivalent:NO];
}
-- (void)keyEvent:(NSEvent *)theEvent wasKeyEquivalent:(BOOL)equiv {
+- (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv {
DCHECK([theEvent type] != NSKeyDown ||
!equiv == !([theEvent modifierFlags] & NSCommandKeyMask));
@@ -1127,8 +1287,12 @@ void RenderWidgetHostViewMac::SetTextInputActive(bool active) {
// If this view can be the key view, it is not a popup. Therefore, if it has
// any children, they are popups that need to be canceled.
if (canBeKeyView_) {
- for (RenderWidgetHostViewCocoa* subview in [self subviews]) {
- subview->renderWidgetHostView_->KillSelf();
+ for (NSView* subview in [self subviews]) {
+ if (![subview isKindOfClass:[RenderWidgetHostViewCocoa class]])
+ continue; // Skip plugin views.
+
+ [static_cast<RenderWidgetHostViewCocoa*>(subview)
+ renderWidgetHostViewMac]->KillSelf();
}
}
}
@@ -1981,9 +2145,12 @@ extern NSString *NSTextInputReplacementRangeAttributeName;
NSWindow* newWindow = [self window];
// Pointer comparison only, since we don't know if lastWindow_ is still
// valid.
- if (newWindow && (newWindow != lastWindow_)) {
- lastWindow_ = newWindow;
- renderWidgetHostView_->WindowFrameChanged();
+ if (newWindow) {
+ if (newWindow != lastWindow_) {
+ lastWindow_ = newWindow;
+ renderWidgetHostView_->WindowFrameChanged();
+ }
+ renderWidgetHostView_->ForceTextureReload();
}
}
@@ -2049,48 +2216,6 @@ extern NSString *NSTextInputReplacementRangeAttributeName;
}
}
-- (void)ensureAcceleratedPluginLayer {
- if (acceleratedPluginLayer_.get())
- return;
-
- AcceleratedPluginLayer* pluginLayer = [[AcceleratedPluginLayer alloc]
- initWithRenderWidgetHostViewMac:renderWidgetHostView_.get()];
- acceleratedPluginLayer_.reset(pluginLayer);
- // Make our layer resize to fit the superlayer
- pluginLayer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
- // Make the view layer-backed so that there will be a layer to hang the
- // |layer| off of. This is not the "right" way to host a sublayer in a view,
- // but the right way would require making the whole view's drawing system
- // layer-based (using setLayer:). We don't want to do that (at least not
- // yet) so instead we override setLayer: and re-bind our plugin layer each
- // time the view's layer changes. For discussion see:
- // http://lists.apple.com/archives/Cocoa-dev/2009/Feb/msg01132.html
- [self setWantsLayer:YES];
- [self attachPluginLayer];
-}
-
-- (void)attachPluginLayer {
- CALayer* pluginLayer = acceleratedPluginLayer_.get();
- if (!pluginLayer)
- return;
-
- CALayer* rootLayer = [self layer];
- DCHECK(rootLayer != nil);
- [pluginLayer setFrame:NSRectToCGRect([self bounds])];
- [rootLayer addSublayer:pluginLayer];
- renderWidgetHostView_->AcceleratedSurfaceContextChanged();
-}
-
-- (void)setLayer:(CALayer *)newLayer {
- CALayer* pluginLayer = acceleratedPluginLayer_.get();
- if (!newLayer && [pluginLayer superlayer])
- [pluginLayer removeFromSuperlayer];
-
- [super setLayer:newLayer];
- if ([self layer])
- [self attachPluginLayer];
-}
-
- (void)drawAcceleratedPluginLayer {
[acceleratedPluginLayer_.get() setNeedsDisplay];
}

Powered by Google App Engine
This is Rietveld 408576698